diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..8926f7673208 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root=true + +[*] +indent_style=space +indent_size=4 +trim_trailing_whitespace=true +insert_final_newline=true diff --git a/.gitattributes b/.gitattributes index 31335b861e1d..8c535f62e19d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -# Auto detect text files and perform LF normalization +# Auto detect text files * text=crlf # Custom for Visual Studio @@ -9,6 +9,22 @@ *.fsproj merge=union *.dbproj merge=union +# Project files +*.bat eol=crlf +*.c eol=crlf +*.cs eol=crlf +*.config eol=crlf +*.cmd eol=crlf +*.csproj eol=crlf +*.def eol=crlf +*.h eol=crlf +*.manifest eol=crlf +*.resx eol=crlf +*.rc eol=crlf +*.sln eol=crlf +*.txt eol=crlf +*.vcxproj eol=crlf + # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain diff --git a/.gitignore b/.gitignore index f29e5c0572b9..346a563a6704 100644 --- a/.gitignore +++ b/.gitignore @@ -2,25 +2,24 @@ ## Visual Studio ################# -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - # User-specific files *.suo *.user -*.sln.docstates *.key # Build results -*_i.c -*_p.c +*.appx +*.appxbundle +*.cer *.ilk *.meta *.obj *.pch *.pdb +*.pfx *.pgc *.pgd +*.pvk *.rsp *.sbr *.tlb @@ -28,11 +27,10 @@ *.tlh *.tmp *.vspscc -.builds -*.dotCover +*_i.c +*_p.c # Visual C++ cache files -ipch/ *.aps *.ncb *.opendb @@ -40,6 +38,9 @@ ipch/ *.sdf *.db *.dll + +# Visual C++ cache folders +ipch/ .vs/ # Visual Studio profiler @@ -49,68 +50,40 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper* -# 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 # Others [Bb]in [Oo]bj -sql -TestResults *.Cache -ClientBin -stylecop.* ~$* -*.dbmdl -Generated_Code #added for RIA/Silverlight projects -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) +# Backup & report files _UpgradeReport_Files/ #Backup*/ UpgradeLog*.XML -############ -## Windows -############ - # Windows image file caches Thumbs.db # Folder config file Desktop.ini -# Mac crap -.DS_Store - ########################## ## Project specific rules ########################## -build/installer/*.exe -ProcessHacker/include/phapprev.h -ProcessHacker/sdk/phapppub.h +build/output/ plugins-extra/ -/sdk/ +sdk/ -KProcessHacker/*.log -KProcessHacker/*.err -KProcessHacker/*.wrn -KProcessHacker/*/objchk_* -KProcessHacker/*/objfre_* +!KProcessHacker/bin/ +KProcessHacker/bin/Debug32/ +KProcessHacker/bin/Debug64/ +KProcessHacker/bin/Release32/ +KProcessHacker/bin/Release64/ +KProcessHacker/bin/* !plugins/SamplePlugin/bin/ plugins/SamplePlugin/bin/Debug32/ @@ -121,6 +94,13 @@ plugins/SamplePlugin/bin/Release64/* !plugins/SamplePlugin/bin/Release64/SamplePlugin.dll plugins/OnlineChecks/virustotal.h +!tools/CustomBuildTool/bin/ +tools/CustomBuildTool/bin/Debug*/ +tools/CustomBuildTool/bin/Release*/* +!tools/CustomBuildTool/bin/Release/CustomBuildTool.exe +!tools/CustomBuildTool/bin/Release/CustomBuildTool.exe.config +!tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb + !tools/CustomSignTool/bin/ tools/CustomSignTool/bin/Debug*/ tools/CustomSignTool/bin/Release*/* @@ -136,3 +116,5 @@ tools/fixlib/bin/Release/* tools/GenerateZw/GenerateZw/bin/Debug/ tools/GenerateZw/GenerateZw/bin/Release/* !tools/GenerateZw/GenerateZw/bin/Release/GenerateZw.exe + +!tools/delaylib/bin/ diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 6860af3f2ae8..a2a8cbddb6e7 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,661 +1,668 @@ -Process Hacker - -3.0 - * HIGHLIGHTS: - * OTHER CHANGES: - * NOTE: - * Support for Windows XP and Vista has been dropped. For those platforms, use Process Hacker 2.38. - * This release has significant internal code changes. Please make sure all plugins are up-to-date. - -2.39 - * HIGHLIGHTS: - * Improved compatibility with security and anti-cheat software - * Added ability to edit process environment variables - * Fixed .NET process detection - * OTHER CHANGES: - * Improved tooltip information for dllhost.exe - * Removed Terminator - * Updated DotNetTools plugin: - * Fixed .NET assembly tab performance issues - * Added extra .NET memory counters to the .NET performance tab - * Added "Show sizes in bytes" checkbox to the .NET performance tab - * Added right-click menu to the .NET assembly tab - * Updated ExtendedTools plugin: - * Fixed "No process" disk event bug - * Updated HardwareDevices plugin: - * Fixed incorrect drive letters - * Fixed drive letter and panel clipping issue - -2.38 - * HIGHLIGHTS: - * Added labels to indicate the maximum data point in each I/O graph - * Graph grids now scale correctly when resized - * Improved high DPI scaling - * Added exploit mitigation policy information to process properties (Windows 8 and above) - * Added File modified time and File size columns for processes and modules - * Added Key modified time column for services - * Clicking a tray icon now shows the pop-up UI (useful for touch-enabled devices) - * The NetAdapters plugin has been renamed to HardwareDevices - * This plugin shows network adapter and disk drive graphs - * If you are manually upgrading, please delete NetAdapters.dll from the plugins folder - * Updated UserNotes plugin: - * Added "Collapse by default" option for processes - * OTHER CHANGES: - * Added "Start when I log on" option - * Added "Not responding" text to tray icon rich pop-up for programs that are hung - * Added right-click menu and double-click action for environment variables - * Added dialog box to show long command line strings - * Added Time stamp column for processes - * Added -sysinfo command line parameter for opening System Information at startup - * Added 32x32 icons for high DPI displays - * Digital signature verification is now performed with very low I/O priority - * Improved performance when handling a large number of threads, modules or handles - * The pop-up UI no longer displays when double-clicking the tray icon - * Fixed ASLR state being shown as N/A in process properties - * Fixed multi monitor window placement bug - * Fixed handle enumeration bug affecting processes with PID >= 65536 - * Fixed Interrupts being missing from the max CPU usage history - * Updated ToolStatus plugin: - * Added 32x32 icons for high DPI displays - * Fixed status bar crash - * NOTE: - * This release has significant internal code changes. Please make sure all plugins are up-to-date. - -2.37 - * HIGHLIGHTS: - * Updated for Windows 10 - * The "Include CPU (and other) usage of children in collapsed processes" option now aggregates memory and I/O statistics - * Added regex search to "Find Handles or DLLs" - * Added process exit codes to log - * Fixed crash that occurred under some conditions when processes terminated - * OTHER CHANGES: - * Added warning when trying to search for handles when the system has too many handles open - * Upgraded to PCRE2 - * Updated DotNetTools plugin: - * Rewrite of .NET Performance statistics and AppDomain enumeration - * Updated OnlineChecks plugin: - * Fixed virusscan.jotti.org uploader - * Updated NetAdapters plugin: - * Added adapter details window - * Updated ToolStatus plugin: - * Added CPU, Memory and I/O graphs to the toolbar (not enabled by default) - * Added toolbar and status bar customization, as well as a new theme - * Added option to auto-hide the main menu - * Updated UserNotes plugin: - * Added individual process highlighting support - -2.36 - * HIGHLIGHTS: - * New rich pop-up UI when hovering the cursor over a tray icon, showing the most active processes - * Completely new Memory tab for processes, with heap, stack and working set usage - * Process Hacker now takes 32-bit dumps of 32-bit processes on 64-bit Windows - * NOTE: When using the portable (.zip) release, the entire archive must be extracted - * Updated DotNetTools plugin: - * Process Hacker now displays managed stack traces for 32-bit .NET processes on 64-bit Windows - * Fixed inaccurate stack traces when clicking Refresh - * Added AppDomain column for threads in .NET programs - * OTHER CHANGES: - * Added customizable bytes per row setting for memory editor - * Dramatically faster handle listing and search when running without administrative privileges - * Improved accuracy and speed of symbol resolution, especially when new modules are loaded - * Added trigger and delayed start information to service list - * Added file information to service list tooltips - * Balloon tips for process/service notifications are now clickable - * Added handle names for unnamed File objects - * Added I/O Priority to tray icon process menu - * Added warning for users who attempt to start the 32-bit version on 64-bit Windows - * Updated ExtendedServices plugin: - * Added service protection and SID information - * Added auto-elevation when saving recovery information, triggers and other service settings - * Updated ExtendedTools plugin: - * Added tray icon mini info window support - * Improved automatic GPU node selection - * Updated UserNotes plugin: - * Added tray icon mini info window support - * Fixed a bug in phsvc that caused hangs when automatically elevating actions - * Fixed hang when viewing handle security for certain File objects - * Fixed lack of information on startup when using slower refresh intervals - * Fixed Read/Write Address crash - * Fixed service non-polling mode on Windows 8 and above - * Fixed file dialog crash in Windows PE environments - * Fixed string scanning false positive case - * Fixed process window detection for Modern UI apps - * Fixed handle list selection bug when disabling "Hide unnamed handles" - * NOTE: - * This release has significant internal code changes. Please make sure all plugins are up-to-date. - -2.35 - * NEW/IMPROVED: - * Added Load Time and Load Reason columns for modules (Windows 8 and above) - * Added handle names for Job and Section objects - * Added Read/Write Memory for Section objects (in process Handles tab) - * Added CF Guard (Control Flow Guard) column for processes and modules - * Added highlighting for AppContainer DLLs - * Added AppContainer and CF Guard image characteristics to peview - * Added Open Key and Open File Location menu items for services - * Set priority and I/O priority for multiple processes at once - * Support for up to 64 processors when setting process/thread affinity - * Updated ExtendedTools plugin: - * Added Disk and Network graphs for all processes - * Updated UserNotes plugin: - * Added ability to save I/O priority - * FIXED: - * Fixed memory editor copy bug - -2.34 - * NEW/IMPROVED: - * Proper Unicode support - * CPU and GPU graphs are displayed in a grid now (thanks pavel_kv!) - * Start Task Manager now elevates when necessary - * Better names for memory regions in Memory tab (for PEBs, TEBs, thread stacks) - * Added tooltip information for user-mode driver framework (UMDF) host processes - * Added option to reduce row height (set ThinRows to 1 in settings.xml) - * Added NetAdapters plugin: adds graphs for selected network adapters to the System Information window - * Updated ExtendedTools plugin: - * Added GPU graphs for all processes - * Can now use the search box in the Disk tab - * Improved kernel logger handling - * FIXED: - * Fixed touch scrolling - * Fixed EtwRegistration object names for 64-bit Windows 8.1 - * Fixed tray icons being clipped in high DPI environments - * Fixed crash in memory editor - * Fixed multi monitor window placement bug - -2.33 - * NEW/IMPROVED: - * View digital signature information from process properties and peview - * Signatures for Windows 8 apps are now detected - * Improved file, key, process and thread handle properties - * Added DPI Awareness column - * Added new Windows 8.1 process protection information - * KProcessHacker is no longer needed for highlighting of GUI threads - * Added suspend count for threads on Windows 8.1 - * Updated DotNetTools plugin: - * Improved .NET assembly enumeration timeout handling - * FIXED: - * Service start type and error control are never updated if modified outside of Process Hacker - -2.32 - * NOTE: - * All executable files are now signed. - * NEW/IMPROVED: - * Updated for Windows 8.1 - * Added progress display for thread stacks - * Updated ExtendedServices plugin: - * Added new trigger data types - * Updated NetworkTools plugin: - * Updated UI - * Updated OnlineChecks plugin: - * Added file analyzed prompt - * FIXED: - * Fixed handling of long symbol names - * Fixed Run As preventing Windows 8 apps from starting - * Fixed console host information for Windows 8.1 - * Fixed reflected processes not terminating on Windows 8.1 - * Fixed CPU frequency on Windows 8.1 - -2.31 - * NEW/IMPROVED: - * Updated ExtendedServices plugin: - * Fixed some bugs relating to Windows 8 - * Updated OnlineChecks plugin: - * Added upload progress - * Updated UserNotes plugin: - * Fixed bug where process priorities were not actually saved - * FIXED: - * Fixed module list not updating properly - * DLL enumeration crash - -2.30 - * NEW/IMPROVED: - * Added "Icon click toggles visibility" option - * Re-enabled powerful process termination on 32-bit Windows 8 - * Updated UserNotes plugin: - * Added ability to save process priority - * Added "Only for processes with the same command line" option for process comments - * FIXED: - * Fixed crash on CPUs without SSE2 - -2.29 - * NEW/IMPROVED: - * Added App ID column for processes - * Added new ASLR information for Windows 8 - * Added Restart to Boot Options and Hybrid Shutdown menu items for Windows 8 - * Added ability to specify processes by their names and inject and unload DLLs in command line - * Removed 512 character limit when copying text - * Moved Terminator to Miscellaneous menu - * Updated default dbghelp.dll path for Windows SDK v8 - * Updated ExtendedServices plugin: - * Added new triggers for Windows 8 - * Fixed bug when restarting services - * Updated ExtendedTools plugin: - * Improved support for multiple GPUs (again) - * GPU column now respects "Include CPU usage of children" option - * Updated ToolStatus plugin: - * Fixed search box fonts - * Fixed controls not being properly hidden/removed from the window when disabled - * Updated WindowExplorer plugin: - * Fixed window list not displaying Modern UI windows - * FIXED: - * Fixed Load Count column sorting bug - * Fixed signature verification on Windows 8 - * Fixed task scheduler information on Windows 8 - * Fixed drag bug in tree list - * Fixed KProcessHacker bug affecting TmTx objects - * Fixed Run As feature on Windows 8 - * Fixed bug where -settings parameter is not propagated - * Fixed tab key behavior on main window - * Fixed recognition of Modern UI windows - -2.28 - * NEW/IMPROVED: - * peview now resolves .lnk targets - * Fixed Ctrl+A for processes, services and network connections and added Ctrl+A for other windows - * Changed confirmation prompts to select the destructive action by default - * Updated DotNetTools plugin: - * Fixed inaccurate stack traces for certain .NET programs - * Updated ExtendedTools plugin: - * Fixed network graph scaling - * Updated ToolStatus plugin: - * Added search box - * Updated Updater plugin - * FIXED: - * Fixed Verification Status column sorting bug in module list - * Fixed rare System Information crash - * Fixed bug in opening process handles - * Fixed freezing when viewing stack traces of certain system threads - -2.27 - * NEW/IMPROVED: - * Updated OnlineChecks plugin: - * 2012-01-16: Updated VirusTotal uploader and added hash checking - * FIXED: - * Fixed Description column sorting bug - * Fixed notification icon bug - -2.26 - * NEW/IMPROVED: - * Added option to show Commit Charge in system information summary view - * Added -priority and -selectpid command line options - * Updated ExtendedTools plugin: - * Improved support for multiple GPUs - * FIXED: - * Fixed 100% CPU when starting on some machines - -2.25 - * NEW/IMPROVED: - * Improved CPU frequency calculation - * Updated ExtendedTools plugin: - * Added GPU node selection - * Fixed incorrect GPU usage calculation - * FIXED: - * Graph tooltip position with large cursors - * Fixed .NET process detection - * Fixed incorrect values in Bits column - -2.24 - * NOTE: - * This release has significant internal code changes. Please make sure all plugins are up-to-date. - * NEW/IMPROVED: - * Completely new system information window - * Added option to scroll to new processes - * Added option to hide driver services - * Added menu item to copy individual cells - * Improved module scanning - * Added Start Task Manager menu item - * Added Image base to peview - * Updated ExtendedTools plugin: - * Added support for new system information window - * Added Disk, Network and GPU tray icons - * Added support for custom fonts in the Disk tab - * Updated Updater plugin: - * Added download speed - * Added remaining time - * FIXED: - * Fixed retrieval of version information for certain files - * Fixed driver file names on Windows XP - * Fixed Run As Administrator when used with complex commands - -2.23 - * NEW/IMPROVED: - * Added display of token capabilities, user/device claims and security attributes - * Added ability to change token integrity levels - * Added Description column to service list - * Added option to reset all settings - * Made grid color darker - * Enabled multi-selection in the hidden processes window - * Added UserNotes plugin - * Updated ExtendedNotifications plugin: - * Added Growl support - * Updated ExtendedTools plugin: - * Added GPU monitoring - * Added rate columns for disk and network I/O - * FIXED: - * Fixed copying lists when plugin columns are enabled - * Freezing when viewing the tooltip for a process with a very long command line - * Disabled Hidden Processes feature on 64-bit systems - -2.22 - * NEW/IMPROVED: - * Added highlighting for metro style apps - * Added Package Name column - * Added package name to process tooltip - * Improved .NET process detection - * Updated OS Context column for Windows 8 - * Updated ExtendedTools plugin: - * Updated disk monitoring for Windows 8 - * Updated memory list information for Windows 8 - * Updated WindowExplorer plugin: - * Fixed hook support for low integrity processes - * FIXED: - * Fixed memory leaks - * Fixed bug preventing Interrupts/DPCs from being shown as the max. CPU process on 64-bit systems - * Fixed DEP Status column on 64-bit systems - -2.21 - * NEW/IMPROVED: - * Added Private Bytes Delta, ASLR and Subsystem columns - * Added ASLR and Time Stamp columns to modules list - * Added check for debugger in Terminator - * FIXED: - * Fixed Show CPU Below 0.01 not respecting locale - * Fixed copying from network list - -2.20 - * NEW/IMPROVED: - * Added support for managed thread stacks on x64 - * Added column selection for handle list - * Added CPU column to threads list - * Improved module detection - * Added Ideal Processor to Threads tab - * Added pool usage and minimum/maximum working set columns - * Implemented Properties button for Thread handles - * Set descending sort as the default for most numeric columns - * Extended header context menu - * Removed tooltip text truncation - * Improved cycle-based CPU usage calculation - * Set default KProcessHacker security level to only allow connections when Process Hacker is running as administrator. - See README.txt for instructions on how to restore the old behavior. - * Added Updater plugin - * Updated DotNetTools plugin: - * Added managed symbol resolution for thread stacks - * Updated ExtendedTools plugin: - * Added Disk tab - * Added Hard Faults, Hard Faults Delta and Peak Threads columns to process tree list - * Added Firewall Status column - * FIXED: - * Fixed file name resolution bug - * Save settings on shutdown/logoff - * Fixed state highlighting bug - * Fixed command line propagation for -elevate - * Fixed tree list mouse wheel handling - * Fixed saving network list - -2.19 - * NEW/IMPROVED: - * Added cycle-based CPU usage for Windows 7 - * Added Show CPU Below 0.01 - * Added OS Context column - * Rewrote graph drawing code for improved performance - * Optimized retrieval of cycle time and private working set information for Windows 7 - * Added Open File Location to process context menu and reorganized some items - * Added checkboxes to Terminator - * FIXED: - * Crash when sorting by Time Stamp - * GDI handle leak in drag selection - -2.18 - * NEW/IMPROVED: - * Completely rewritten tree list control: - * Process Name column is now fixed to the left - * Tooltips for column headers - * Improved performance - * Bug fixes - * Added more process tree list columns - * Added Time stamp column to network list - * Date/time display is now swapped (so time is shown before date) - * Added W3 terminator test - * Added DotNetTools plugin - * Updated ExtendedServices plugin: - * Disabled editing of required privileges for drivers - * Updated ExtendedTools plugin: - * Added ETW columns for processes and network connections - * Updated OnlineChecks plugin: - * Added Comodo Instant Malware Analysis - * Updated WindowExplorer plugin: - * Fixed hook bugs - * FIXED: - * Fixed Run As This User - * Verification Status sorting - -2.17 - * NEW/IMPROVED: - * Added support for setting page priority - * Added elevation support for setting priority - * Added support for automatically using a settings file in the program directory (e.g. ProcessHacker.exe.settings.xml) - * Improved Run As mechanism - * Updated ExtendedServices plugin: - * Added support for editing triggers - * Added support for editing preshutdown time-out - * Added support for editing required privileges - * Added elevation support for restarting services - * Updated WindowExplorer plugin: - * Added more window properties - * FIXED: - * Handle leak - -2.16 - * NEW/IMPROVED: - * Updated WindowExplorer plugin - * PE viewer: Added version string to CLR tab - * PE viewer: Added display of delay imports - * PE viewer: Added Load Config tab - * Improved wait analysis - * Added arrows to the service list to indicate whether a service is running - * FIXED: - * Fixed the IPv6-related workaround causing crashes - * Incorrect handling of window positions - -2.15 - * NEW/IMPROVED: - * Updated ExtendedServices plugin - * Updated ToolStatus plugin - * Added DEP Status column - * Improved User Name column - * FIXED: - * Image file versions - * Workaround for an IPv6-related bug in Windows XP - * DPCs and Interrupts in System Information tooltips - * File dialog crash on Windows XP - * ExtendedTools plugin: WS Watch refresh bug - -2.14 - * NEW/IMPROVED: - * ExtendedServices plugin: Option to add a Services menu for processes - * Command line support for setting process priority and I/O priority - * Improved termination of explorer.exe - * FIXED: - * Icon should restore the main window if it is minimized - * System Information window crashes - * Hide Processes From Other Users and Hide Signed Processes settings are now saved - * Font selection on Windows XP - * ToolStatus plugin: Always on Top status being reset by Find Window - * Service-related crashes - * WindowExplorer plugin: sorting in tree list - * Process minidump creation with old versions of dbghelp.dll - -2.13 - * NEW/IMPROVED: - * Added copy support to PE viewer - * Added Connect Time, Disconnect Time and Last Input Time to session properties - * Added more working set counters to the Statistics tab - * FIXED: - * Column sort arrows - * CPU usage calculations - -2.12 - * NEW/IMPROVED: - * Updated KProcessHacker for Windows 7 SP1 - * Added elevation support for more actions - * Added ability to disable plugins - * Updated ToolStatus plugin - * Added Remote Control for sessions - * More command line options - * FIXED: - * Memory leaks - * Run As issues with different sessions - -2.11 - * NEW/IMPROVED: - * Added WS Watch and other features to ExtendedTools plugin - * Added WindowExplorer plugin - * Properties for hidden processes - * Improved menus - * Debug console can now be closed without affecting the entire program - * FIXED: - * Always on Top issues - * Hang when setting DEP status of a terminating process - * Encoding bug in NetworkTools plugin - * LSA interfacing issues - * Creating dumps of self - -2.10 - * NEW/IMPROVED: - * KProcessHacker is now signed, so it works on 64-bit systems. Thank you to the ReactOS Foundation. - * Added Run As Limited User - * Added CPU, private bytes and I/O history columns - * Added font selection - * Slightly improved highlighting configuration - * FIXED: - * High DPI support - * Multi-monitor support in graph tooltips - * DEP status retrieval - * ExtendedTools plugin crash - * Notification icon menu crash - * Memory leaks - * Other small bug fixes - -2.9 - * NEW/IMPROVED: - * Added column selection for modules list - * Added wait analysis for 64-bit systems - * Added signature verification for modules - * Added ExtendedTools plugin (Vista and above only) with Disk and Network information - * Updated ExtendedNotifications plugin: added ability to log events to a file - * Updated ExtendedServices plugin: new tab on Vista and above - * Updated ToolStatus plugin: resolves ghost windows to hung windows - * Environment variables and current directory are now correctly shown for WOW64 processes - * I/O priority names are now used instead of numbers - * FIXED: - * Network list bug - * Memory leaks - -2.8 - * NEW/IMPROVED: - * Better service list (including column selection) - * Added Peak Handles - * Process tree sorting is now preserved - * Save works for services and network connections - * Pausing now works correctly with the Network tab - * Added option to display inclusive CPU usages for collapsed processes - * Added CLR tab to peview - * Added ability to destroy heaps - * Improved process tree list appearance - * Certain command line parameters are now propagated - * FIXED: - * Icon handling bugs - * Memory leaks - * Extended tooltips for WOW64 processes - -2.7 - * NEW/IMPROVED: - * Vastly improved startup time and lower memory usage - * Added Cycles and Cycles Delta columns - * Added option to disable address resolution for network connections - * Added Logon Time to session properties - * Added time stamp display to peview - * FIXED: - * ToolStatus layout problems - * .NET highlighting crashes - * Run As on Windows XP - -2.6 - * NEW/IMPROVED: - * Sorting for most lists is now much faster - * Hide Signed Processes option - * Added plugin for uploading files to online virus scanners - * Added Network tools plugin - * Updated ExtendedServices plugin - * PE viewer now verifies checksums - * Performance improvements - * FIXED: - * Fixed service handle leak - -2.5 - * NEW/IMPROVED: - * Unmap section views in Memory tab - * Plugin for extended service information (including recovery information, dependencies and dependents) - * FIXED: - * Critical bug for file dialogs on Windows XP - * Esc couldn't close Service Properties on open - * Small bug fixes - -2.4 - * NEW/IMPROVED: - * Better Run As behaviour - * Show Processes From All Users option - * Can now unmap section views - * Control over thread affinity - * Window Title and Window Status columns - * Plugin for filtering notifications - * Plugin for toolbar and status bar - * Performance improvements - * FIXED: - * Memory leak - * SbieSupport plugin on 64-bit - * Crash when running under certain conditions - * Memory case-insensitive filter - * Process parent association bug - * REMOVED: - * Process database - -2.3 - * NEW/IMPROVED: - * Can add processes to jobs - * Double-clicking in the system information graphs now opens information for the relevant process - * Setting I/O priority doesn't need KProcessHacker anymore - * Elevation for certain actions - * FIXED: - * HKCU key name resolution - * Network connection host resolution - * Information window resizing - * Log clearing - -2.2 - * NEW/IMPROVED: - * Plugins support - * Can now unload 32-bit modules on 64-bit systems - * Tasks are shown in tooltips for taskeng.exe/taskhost.exe processes - * Run As can now start processes elevated - * Handle count by type - * Process priorities in notification icon menu - * CSV export - * Relative start times - * FIXED: - * Run and Run As shortcuts - * Command line handling - * Process tree selection - -2.1 - * NEW/IMPROVED: - * Add Pause key shortcut to pause/resume updates - * Added Ctrl+Tab and Ctrl+Shift+Tab shortcuts - * Grid is a bit darker - * Checks for digital signatures and packing is now off by default and optional - * FIXED: - * MD5 calculation code for files was wrong - * Process record bugs - -2.0 - * First release in the Process Hacker 2.x branch. +Process Hacker + +3.0 + * HIGHLIGHTS: + * New Process Hacker setup. + * New process properties handle search. + * Added F11 hotkey for fullscreen System Information window. + * OTHER CHANGES: +* Updated Updater plugin: + * New design and layout. +* Updated WindowExplorer plugin: + * Added Windows process properties page. + * NOTE: + * Support for Windows XP and Vista has been dropped. For those platforms, use Process Hacker 2.38. + * This release has significant internal code changes. Please make sure all plugins are up-to-date. + +2.39 + * HIGHLIGHTS: + * Improved compatibility with security and anti-cheat software + * Added ability to edit process environment variables + * Fixed .NET process detection + * OTHER CHANGES: + * Improved tooltip information for dllhost.exe + * Removed Terminator + * Updated DotNetTools plugin: + * Fixed .NET assembly tab performance issues + * Added extra .NET memory counters to the .NET performance tab + * Added "Show sizes in bytes" checkbox to the .NET performance tab + * Added right-click menu to the .NET assembly tab + * Updated ExtendedTools plugin: + * Fixed "No process" disk event bug + * Updated HardwareDevices plugin: + * Fixed incorrect drive letters + * Fixed drive letter and panel clipping issue + +2.38 + * HIGHLIGHTS: + * Added labels to indicate the maximum data point in each I/O graph + * Graph grids now scale correctly when resized + * Improved high DPI scaling + * Added exploit mitigation policy information to process properties (Windows 8 and above) + * Added File modified time and File size columns for processes and modules + * Added Key modified time column for services + * Clicking a tray icon now shows the pop-up UI (useful for touch-enabled devices) + * The NetAdapters plugin has been renamed to HardwareDevices + * This plugin shows network adapter and disk drive graphs + * If you are manually upgrading, please delete NetAdapters.dll from the plugins folder + * Updated UserNotes plugin: + * Added "Collapse by default" option for processes + * OTHER CHANGES: + * Added "Start when I log on" option + * Added "Not responding" text to tray icon rich pop-up for programs that are hung + * Added right-click menu and double-click action for environment variables + * Added dialog box to show long command line strings + * Added Time stamp column for processes + * Added -sysinfo command line parameter for opening System Information at startup + * Added 32x32 icons for high DPI displays + * Digital signature verification is now performed with very low I/O priority + * Improved performance when handling a large number of threads, modules or handles + * The pop-up UI no longer displays when double-clicking the tray icon + * Fixed ASLR state being shown as N/A in process properties + * Fixed multi monitor window placement bug + * Fixed handle enumeration bug affecting processes with PID >= 65536 + * Fixed Interrupts being missing from the max CPU usage history + * Updated ToolStatus plugin: + * Added 32x32 icons for high DPI displays + * Fixed status bar crash + * NOTE: + * This release has significant internal code changes. Please make sure all plugins are up-to-date. + +2.37 + * HIGHLIGHTS: + * Updated for Windows 10 + * The "Include CPU (and other) usage of children in collapsed processes" option now aggregates memory and I/O statistics + * Added regex search to "Find Handles or DLLs" + * Added process exit codes to log + * Fixed crash that occurred under some conditions when processes terminated + * OTHER CHANGES: + * Added warning when trying to search for handles when the system has too many handles open + * Upgraded to PCRE2 + * Updated DotNetTools plugin: + * Rewrite of .NET Performance statistics and AppDomain enumeration + * Updated OnlineChecks plugin: + * Fixed virusscan.jotti.org uploader + * Updated NetAdapters plugin: + * Added adapter details window + * Updated ToolStatus plugin: + * Added CPU, Memory and I/O graphs to the toolbar (not enabled by default) + * Added toolbar and status bar customization, as well as a new theme + * Added option to auto-hide the main menu + * Updated UserNotes plugin: + * Added individual process highlighting support + +2.36 + * HIGHLIGHTS: + * New rich pop-up UI when hovering the cursor over a tray icon, showing the most active processes + * Completely new Memory tab for processes, with heap, stack and working set usage + * Process Hacker now takes 32-bit dumps of 32-bit processes on 64-bit Windows + * NOTE: When using the portable (.zip) release, the entire archive must be extracted + * Updated DotNetTools plugin: + * Process Hacker now displays managed stack traces for 32-bit .NET processes on 64-bit Windows + * Fixed inaccurate stack traces when clicking Refresh + * Added AppDomain column for threads in .NET programs + * OTHER CHANGES: + * Added customizable bytes per row setting for memory editor + * Dramatically faster handle listing and search when running without administrative privileges + * Improved accuracy and speed of symbol resolution, especially when new modules are loaded + * Added trigger and delayed start information to service list + * Added file information to service list tooltips + * Balloon tips for process/service notifications are now clickable + * Added handle names for unnamed File objects + * Added I/O Priority to tray icon process menu + * Added warning for users who attempt to start the 32-bit version on 64-bit Windows + * Updated ExtendedServices plugin: + * Added service protection and SID information + * Added auto-elevation when saving recovery information, triggers and other service settings + * Updated ExtendedTools plugin: + * Added tray icon mini info window support + * Improved automatic GPU node selection + * Updated UserNotes plugin: + * Added tray icon mini info window support + * Fixed a bug in phsvc that caused hangs when automatically elevating actions + * Fixed hang when viewing handle security for certain File objects + * Fixed lack of information on startup when using slower refresh intervals + * Fixed Read/Write Address crash + * Fixed service non-polling mode on Windows 8 and above + * Fixed file dialog crash in Windows PE environments + * Fixed string scanning false positive case + * Fixed process window detection for Modern UI apps + * Fixed handle list selection bug when disabling "Hide unnamed handles" + * NOTE: + * This release has significant internal code changes. Please make sure all plugins are up-to-date. + +2.35 + * NEW/IMPROVED: + * Added Load Time and Load Reason columns for modules (Windows 8 and above) + * Added handle names for Job and Section objects + * Added Read/Write Memory for Section objects (in process Handles tab) + * Added CF Guard (Control Flow Guard) column for processes and modules + * Added highlighting for AppContainer DLLs + * Added AppContainer and CF Guard image characteristics to peview + * Added Open Key and Open File Location menu items for services + * Set priority and I/O priority for multiple processes at once + * Support for up to 64 processors when setting process/thread affinity + * Updated ExtendedTools plugin: + * Added Disk and Network graphs for all processes + * Updated UserNotes plugin: + * Added ability to save I/O priority + * FIXED: + * Fixed memory editor copy bug + +2.34 + * NEW/IMPROVED: + * Proper Unicode support + * CPU and GPU graphs are displayed in a grid now (thanks pavel_kv!) + * Start Task Manager now elevates when necessary + * Better names for memory regions in Memory tab (for PEBs, TEBs, thread stacks) + * Added tooltip information for user-mode driver framework (UMDF) host processes + * Added option to reduce row height (set ThinRows to 1 in settings.xml) + * Added NetAdapters plugin: adds graphs for selected network adapters to the System Information window + * Updated ExtendedTools plugin: + * Added GPU graphs for all processes + * Can now use the search box in the Disk tab + * Improved kernel logger handling + * FIXED: + * Fixed touch scrolling + * Fixed EtwRegistration object names for 64-bit Windows 8.1 + * Fixed tray icons being clipped in high DPI environments + * Fixed crash in memory editor + * Fixed multi monitor window placement bug + +2.33 + * NEW/IMPROVED: + * View digital signature information from process properties and peview + * Signatures for Windows 8 apps are now detected + * Improved file, key, process and thread handle properties + * Added DPI Awareness column + * Added new Windows 8.1 process protection information + * KProcessHacker is no longer needed for highlighting of GUI threads + * Added suspend count for threads on Windows 8.1 + * Updated DotNetTools plugin: + * Improved .NET assembly enumeration timeout handling + * FIXED: + * Service start type and error control are never updated if modified outside of Process Hacker + +2.32 + * NOTE: + * All executable files are now signed. + * NEW/IMPROVED: + * Updated for Windows 8.1 + * Added progress display for thread stacks + * Updated ExtendedServices plugin: + * Added new trigger data types + * Updated NetworkTools plugin: + * Updated UI + * Updated OnlineChecks plugin: + * Added file analyzed prompt + * FIXED: + * Fixed handling of long symbol names + * Fixed Run As preventing Windows 8 apps from starting + * Fixed console host information for Windows 8.1 + * Fixed reflected processes not terminating on Windows 8.1 + * Fixed CPU frequency on Windows 8.1 + +2.31 + * NEW/IMPROVED: + * Updated ExtendedServices plugin: + * Fixed some bugs relating to Windows 8 + * Updated OnlineChecks plugin: + * Added upload progress + * Updated UserNotes plugin: + * Fixed bug where process priorities were not actually saved + * FIXED: + * Fixed module list not updating properly + * DLL enumeration crash + +2.30 + * NEW/IMPROVED: + * Added "Icon click toggles visibility" option + * Re-enabled powerful process termination on 32-bit Windows 8 + * Updated UserNotes plugin: + * Added ability to save process priority + * Added "Only for processes with the same command line" option for process comments + * FIXED: + * Fixed crash on CPUs without SSE2 + +2.29 + * NEW/IMPROVED: + * Added App ID column for processes + * Added new ASLR information for Windows 8 + * Added Restart to Boot Options and Hybrid Shutdown menu items for Windows 8 + * Added ability to specify processes by their names and inject and unload DLLs in command line + * Removed 512 character limit when copying text + * Moved Terminator to Miscellaneous menu + * Updated default dbghelp.dll path for Windows SDK v8 + * Updated ExtendedServices plugin: + * Added new triggers for Windows 8 + * Fixed bug when restarting services + * Updated ExtendedTools plugin: + * Improved support for multiple GPUs (again) + * GPU column now respects "Include CPU usage of children" option + * Updated ToolStatus plugin: + * Fixed search box fonts + * Fixed controls not being properly hidden/removed from the window when disabled + * Updated WindowExplorer plugin: + * Fixed window list not displaying Modern UI windows + * FIXED: + * Fixed Load Count column sorting bug + * Fixed signature verification on Windows 8 + * Fixed task scheduler information on Windows 8 + * Fixed drag bug in tree list + * Fixed KProcessHacker bug affecting TmTx objects + * Fixed Run As feature on Windows 8 + * Fixed bug where -settings parameter is not propagated + * Fixed tab key behavior on main window + * Fixed recognition of Modern UI windows + +2.28 + * NEW/IMPROVED: + * peview now resolves .lnk targets + * Fixed Ctrl+A for processes, services and network connections and added Ctrl+A for other windows + * Changed confirmation prompts to select the destructive action by default + * Updated DotNetTools plugin: + * Fixed inaccurate stack traces for certain .NET programs + * Updated ExtendedTools plugin: + * Fixed network graph scaling + * Updated ToolStatus plugin: + * Added search box + * Updated Updater plugin + * FIXED: + * Fixed Verification Status column sorting bug in module list + * Fixed rare System Information crash + * Fixed bug in opening process handles + * Fixed freezing when viewing stack traces of certain system threads + +2.27 + * NEW/IMPROVED: + * Updated OnlineChecks plugin: + * 2012-01-16: Updated VirusTotal uploader and added hash checking + * FIXED: + * Fixed Description column sorting bug + * Fixed notification icon bug + +2.26 + * NEW/IMPROVED: + * Added option to show Commit Charge in system information summary view + * Added -priority and -selectpid command line options + * Updated ExtendedTools plugin: + * Improved support for multiple GPUs + * FIXED: + * Fixed 100% CPU when starting on some machines + +2.25 + * NEW/IMPROVED: + * Improved CPU frequency calculation + * Updated ExtendedTools plugin: + * Added GPU node selection + * Fixed incorrect GPU usage calculation + * FIXED: + * Graph tooltip position with large cursors + * Fixed .NET process detection + * Fixed incorrect values in Bits column + +2.24 + * NOTE: + * This release has significant internal code changes. Please make sure all plugins are up-to-date. + * NEW/IMPROVED: + * Completely new system information window + * Added option to scroll to new processes + * Added option to hide driver services + * Added menu item to copy individual cells + * Improved module scanning + * Added Start Task Manager menu item + * Added Image base to peview + * Updated ExtendedTools plugin: + * Added support for new system information window + * Added Disk, Network and GPU tray icons + * Added support for custom fonts in the Disk tab + * Updated Updater plugin: + * Added download speed + * Added remaining time + * FIXED: + * Fixed retrieval of version information for certain files + * Fixed driver file names on Windows XP + * Fixed Run As Administrator when used with complex commands + +2.23 + * NEW/IMPROVED: + * Added display of token capabilities, user/device claims and security attributes + * Added ability to change token integrity levels + * Added Description column to service list + * Added option to reset all settings + * Made grid color darker + * Enabled multi-selection in the hidden processes window + * Added UserNotes plugin + * Updated ExtendedNotifications plugin: + * Added Growl support + * Updated ExtendedTools plugin: + * Added GPU monitoring + * Added rate columns for disk and network I/O + * FIXED: + * Fixed copying lists when plugin columns are enabled + * Freezing when viewing the tooltip for a process with a very long command line + * Disabled Hidden Processes feature on 64-bit systems + +2.22 + * NEW/IMPROVED: + * Added highlighting for metro style apps + * Added Package Name column + * Added package name to process tooltip + * Improved .NET process detection + * Updated OS Context column for Windows 8 + * Updated ExtendedTools plugin: + * Updated disk monitoring for Windows 8 + * Updated memory list information for Windows 8 + * Updated WindowExplorer plugin: + * Fixed hook support for low integrity processes + * FIXED: + * Fixed memory leaks + * Fixed bug preventing Interrupts/DPCs from being shown as the max. CPU process on 64-bit systems + * Fixed DEP Status column on 64-bit systems + +2.21 + * NEW/IMPROVED: + * Added Private Bytes Delta, ASLR and Subsystem columns + * Added ASLR and Time Stamp columns to modules list + * Added check for debugger in Terminator + * FIXED: + * Fixed Show CPU Below 0.01 not respecting locale + * Fixed copying from network list + +2.20 + * NEW/IMPROVED: + * Added support for managed thread stacks on x64 + * Added column selection for handle list + * Added CPU column to threads list + * Improved module detection + * Added Ideal Processor to Threads tab + * Added pool usage and minimum/maximum working set columns + * Implemented Properties button for Thread handles + * Set descending sort as the default for most numeric columns + * Extended header context menu + * Removed tooltip text truncation + * Improved cycle-based CPU usage calculation + * Set default KProcessHacker security level to only allow connections when Process Hacker is running as administrator. + See README.txt for instructions on how to restore the old behavior. + * Added Updater plugin + * Updated DotNetTools plugin: + * Added managed symbol resolution for thread stacks + * Updated ExtendedTools plugin: + * Added Disk tab + * Added Hard Faults, Hard Faults Delta and Peak Threads columns to process tree list + * Added Firewall Status column + * FIXED: + * Fixed file name resolution bug + * Save settings on shutdown/logoff + * Fixed state highlighting bug + * Fixed command line propagation for -elevate + * Fixed tree list mouse wheel handling + * Fixed saving network list + +2.19 + * NEW/IMPROVED: + * Added cycle-based CPU usage for Windows 7 + * Added Show CPU Below 0.01 + * Added OS Context column + * Rewrote graph drawing code for improved performance + * Optimized retrieval of cycle time and private working set information for Windows 7 + * Added Open File Location to process context menu and reorganized some items + * Added checkboxes to Terminator + * FIXED: + * Crash when sorting by Time Stamp + * GDI handle leak in drag selection + +2.18 + * NEW/IMPROVED: + * Completely rewritten tree list control: + * Process Name column is now fixed to the left + * Tooltips for column headers + * Improved performance + * Bug fixes + * Added more process tree list columns + * Added Time stamp column to network list + * Date/time display is now swapped (so time is shown before date) + * Added W3 terminator test + * Added DotNetTools plugin + * Updated ExtendedServices plugin: + * Disabled editing of required privileges for drivers + * Updated ExtendedTools plugin: + * Added ETW columns for processes and network connections + * Updated OnlineChecks plugin: + * Added Comodo Instant Malware Analysis + * Updated WindowExplorer plugin: + * Fixed hook bugs + * FIXED: + * Fixed Run As This User + * Verification Status sorting + +2.17 + * NEW/IMPROVED: + * Added support for setting page priority + * Added elevation support for setting priority + * Added support for automatically using a settings file in the program directory (e.g. ProcessHacker.exe.settings.xml) + * Improved Run As mechanism + * Updated ExtendedServices plugin: + * Added support for editing triggers + * Added support for editing preshutdown time-out + * Added support for editing required privileges + * Added elevation support for restarting services + * Updated WindowExplorer plugin: + * Added more window properties + * FIXED: + * Handle leak + +2.16 + * NEW/IMPROVED: + * Updated WindowExplorer plugin + * PE viewer: Added version string to CLR tab + * PE viewer: Added display of delay imports + * PE viewer: Added Load Config tab + * Improved wait analysis + * Added arrows to the service list to indicate whether a service is running + * FIXED: + * Fixed the IPv6-related workaround causing crashes + * Incorrect handling of window positions + +2.15 + * NEW/IMPROVED: + * Updated ExtendedServices plugin + * Updated ToolStatus plugin + * Added DEP Status column + * Improved User Name column + * FIXED: + * Image file versions + * Workaround for an IPv6-related bug in Windows XP + * DPCs and Interrupts in System Information tooltips + * File dialog crash on Windows XP + * ExtendedTools plugin: WS Watch refresh bug + +2.14 + * NEW/IMPROVED: + * ExtendedServices plugin: Option to add a Services menu for processes + * Command line support for setting process priority and I/O priority + * Improved termination of explorer.exe + * FIXED: + * Icon should restore the main window if it is minimized + * System Information window crashes + * Hide Processes From Other Users and Hide Signed Processes settings are now saved + * Font selection on Windows XP + * ToolStatus plugin: Always on Top status being reset by Find Window + * Service-related crashes + * WindowExplorer plugin: sorting in tree list + * Process minidump creation with old versions of dbghelp.dll + +2.13 + * NEW/IMPROVED: + * Added copy support to PE viewer + * Added Connect Time, Disconnect Time and Last Input Time to session properties + * Added more working set counters to the Statistics tab + * FIXED: + * Column sort arrows + * CPU usage calculations + +2.12 + * NEW/IMPROVED: + * Updated KProcessHacker for Windows 7 SP1 + * Added elevation support for more actions + * Added ability to disable plugins + * Updated ToolStatus plugin + * Added Remote Control for sessions + * More command line options + * FIXED: + * Memory leaks + * Run As issues with different sessions + +2.11 + * NEW/IMPROVED: + * Added WS Watch and other features to ExtendedTools plugin + * Added WindowExplorer plugin + * Properties for hidden processes + * Improved menus + * Debug console can now be closed without affecting the entire program + * FIXED: + * Always on Top issues + * Hang when setting DEP status of a terminating process + * Encoding bug in NetworkTools plugin + * LSA interfacing issues + * Creating dumps of self + +2.10 + * NEW/IMPROVED: + * KProcessHacker is now signed, so it works on 64-bit systems. Thank you to the ReactOS Foundation. + * Added Run As Limited User + * Added CPU, private bytes and I/O history columns + * Added font selection + * Slightly improved highlighting configuration + * FIXED: + * High DPI support + * Multi-monitor support in graph tooltips + * DEP status retrieval + * ExtendedTools plugin crash + * Notification icon menu crash + * Memory leaks + * Other small bug fixes + +2.9 + * NEW/IMPROVED: + * Added column selection for modules list + * Added wait analysis for 64-bit systems + * Added signature verification for modules + * Added ExtendedTools plugin (Vista and above only) with Disk and Network information + * Updated ExtendedNotifications plugin: added ability to log events to a file + * Updated ExtendedServices plugin: new tab on Vista and above + * Updated ToolStatus plugin: resolves ghost windows to hung windows + * Environment variables and current directory are now correctly shown for WOW64 processes + * I/O priority names are now used instead of numbers + * FIXED: + * Network list bug + * Memory leaks + +2.8 + * NEW/IMPROVED: + * Better service list (including column selection) + * Added Peak Handles + * Process tree sorting is now preserved + * Save works for services and network connections + * Pausing now works correctly with the Network tab + * Added option to display inclusive CPU usages for collapsed processes + * Added CLR tab to peview + * Added ability to destroy heaps + * Improved process tree list appearance + * Certain command line parameters are now propagated + * FIXED: + * Icon handling bugs + * Memory leaks + * Extended tooltips for WOW64 processes + +2.7 + * NEW/IMPROVED: + * Vastly improved startup time and lower memory usage + * Added Cycles and Cycles Delta columns + * Added option to disable address resolution for network connections + * Added Logon Time to session properties + * Added time stamp display to peview + * FIXED: + * ToolStatus layout problems + * .NET highlighting crashes + * Run As on Windows XP + +2.6 + * NEW/IMPROVED: + * Sorting for most lists is now much faster + * Hide Signed Processes option + * Added plugin for uploading files to online virus scanners + * Added Network tools plugin + * Updated ExtendedServices plugin + * PE viewer now verifies checksums + * Performance improvements + * FIXED: + * Fixed service handle leak + +2.5 + * NEW/IMPROVED: + * Unmap section views in Memory tab + * Plugin for extended service information (including recovery information, dependencies and dependents) + * FIXED: + * Critical bug for file dialogs on Windows XP + * Esc couldn't close Service Properties on open + * Small bug fixes + +2.4 + * NEW/IMPROVED: + * Better Run As behaviour + * Show Processes From All Users option + * Can now unmap section views + * Control over thread affinity + * Window Title and Window Status columns + * Plugin for filtering notifications + * Plugin for toolbar and status bar + * Performance improvements + * FIXED: + * Memory leak + * SbieSupport plugin on 64-bit + * Crash when running under certain conditions + * Memory case-insensitive filter + * Process parent association bug + * REMOVED: + * Process database + +2.3 + * NEW/IMPROVED: + * Can add processes to jobs + * Double-clicking in the system information graphs now opens information for the relevant process + * Setting I/O priority doesn't need KProcessHacker anymore + * Elevation for certain actions + * FIXED: + * HKCU key name resolution + * Network connection host resolution + * Information window resizing + * Log clearing + +2.2 + * NEW/IMPROVED: + * Plugins support + * Can now unload 32-bit modules on 64-bit systems + * Tasks are shown in tooltips for taskeng.exe/taskhost.exe processes + * Run As can now start processes elevated + * Handle count by type + * Process priorities in notification icon menu + * CSV export + * Relative start times + * FIXED: + * Run and Run As shortcuts + * Command line handling + * Process tree selection + +2.1 + * NEW/IMPROVED: + * Add Pause key shortcut to pause/resume updates + * Added Ctrl+Tab and Ctrl+Shift+Tab shortcuts + * Grid is a bit darker + * Checks for digital signatures and packing is now off by default and optional + * FIXED: + * MD5 calculation code for files was wrong + * Process record bugs + +2.0 + * First release in the Process Hacker 2.x branch. diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 2fa97372f513..5469a3bafbe6 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,132 +1,132 @@ -== Process Hacker == -Process Hacker is licensed under the GNU GPL v3, with exceptions. A full -copy of the license is provided in LICENSE.txt. - - Copyright (C) 2009-2016 wj32 and various authors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -== Mini-XML == -Process Hacker uses Mini-XML licensed under the following terms: - - The Mini-XML library and included programs are provided under the - terms of the GNU Library General Public License (LGPL) with the - following exceptions: - - 1. Static linking of applications to the Mini-XML library - does not constitute a derivative work and does not require - the author to provide source code for the application, use - the shared Mini-XML libraries, or link their applications - against a user-supplied version of Mini-XML. - - If you link the application to a modified version of - Mini-XML, then the changes to Mini-XML must be provided - under the terms of the LGPL in sections 1, 2, and 4. - - 2. You do not have to provide a copy of the Mini-XML license - with programs that are linked to the Mini-XML library, nor - do you have to identify the Mini-XML license in your - program or documentation as required by section 6 of the - LGPL. - -== PCRE == -Process Hacker uses Perl-Compatible Regular Expressions licensed under the -following terms: - - PCRE is a library of functions to support regular expressions whose syntax - and semantics are as close as possible to those of the Perl 5 language. - - Release 8 of PCRE is distributed under the terms of the "BSD" licence, as - specified below. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the name of Google - Inc. nor the names of their contributors may be used to endorse or - promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -== MD5 == -Process Hacker uses a MD5 implementation licensed under the following terms: - - MD5 hash implementation and interface functions - Copyright (c) 2003-2005, Jouni Malinen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation. - -== SHA == -Process Hacker uses a SHA implementation licensed under the following terms: - - Copyright 2004 Filip Navara - Based on public domain SHA code by Steve Reid - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - -== Natural order string comparison == -Process Hacker uses "strnatcmp.c" licensed under the following terms: - - strnatcmp.c -- Perform 'natural order' comparisons of strings in C. - Copyright (C) 2000, 2004 by Martin Pool - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - This code has been modified for Process Hacker. +== Process Hacker == +Process Hacker is licensed under the GNU GPL v3, with exceptions. A full +copy of the license is provided in LICENSE.txt. + + Copyright (C) 2009-2016 wj32 and various authors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +== Mini-XML == +Process Hacker uses Mini-XML licensed under the following terms: + + The Mini-XML library and included programs are provided under the + terms of the GNU Library General Public License (LGPL) with the + following exceptions: + + 1. Static linking of applications to the Mini-XML library + does not constitute a derivative work and does not require + the author to provide source code for the application, use + the shared Mini-XML libraries, or link their applications + against a user-supplied version of Mini-XML. + + If you link the application to a modified version of + Mini-XML, then the changes to Mini-XML must be provided + under the terms of the LGPL in sections 1, 2, and 4. + + 2. You do not have to provide a copy of the Mini-XML license + with programs that are linked to the Mini-XML library, nor + do you have to identify the Mini-XML license in your + program or documentation as required by section 6 of the + LGPL. + +== PCRE == +Process Hacker uses Perl-Compatible Regular Expressions licensed under the +following terms: + + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +== MD5 == +Process Hacker uses a MD5 implementation licensed under the following terms: + + MD5 hash implementation and interface functions + Copyright (c) 2003-2005, Jouni Malinen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + +== SHA == +Process Hacker uses a SHA implementation licensed under the following terms: + + Copyright 2004 Filip Navara + Based on public domain SHA code by Steve Reid + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +== Natural order string comparison == +Process Hacker uses "strnatcmp.c" licensed under the following terms: + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This code has been modified for Process Hacker. diff --git a/KProcessHacker/KProcessHacker.sln b/KProcessHacker/KProcessHacker.sln new file mode 100644 index 000000000000..f10b7a09a817 --- /dev/null +++ b/KProcessHacker/KProcessHacker.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KProcessHacker", "KProcessHacker.vcxproj", "{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|Win32.Build.0 = Debug|Win32 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|Win32.Deploy.0 = Debug|Win32 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|x64.ActiveCfg = Debug|x64 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|x64.Build.0 = Debug|x64 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Debug|x64.Deploy.0 = Debug|x64 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|Win32.ActiveCfg = Release|Win32 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|Win32.Build.0 = Release|Win32 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|Win32.Deploy.0 = Release|Win32 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|x64.ActiveCfg = Release|x64 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|x64.Build.0 = Release|x64 + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4989C8A8-DCF1-48A9-9012-23369AB01403} + EndGlobalSection +EndGlobal diff --git a/KProcessHacker/KProcessHacker.vcxproj b/KProcessHacker/KProcessHacker.vcxproj index 98820bf88ad9..2523a02cadc5 100644 --- a/KProcessHacker/KProcessHacker.vcxproj +++ b/KProcessHacker/KProcessHacker.vcxproj @@ -1,139 +1,175 @@ - - - - - Win8 Debug - Win32 - - - Win8 Release - Win32 - - - Win8 Debug - x64 - - - Win8 Release - x64 - - - - {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF} - {dd38f7fc-d7bd-488b-9242-7d8754cde80d} - v4.5 - 11.0 - Win8 Debug - Win32 - - - KProcessHacker - $(VCTargetsPath11) - - - WindowsKernelModeDriver8.0 - Driver - WDM - - - - Windows8 - true - - - Windows8 - false - - - Windows8 - true - - - Windows8 - false - - - - - - - - - - DbgengKernelDebugger - - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ - kprocesshacker - - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ - kprocesshacker - - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ - kprocesshacker - - - $(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\ - kprocesshacker - - - - ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) - KPH_CONFIG_CLEAN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) - Level3 - - - - - ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) - KPH_CONFIG_CLEAN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) - Level3 - - - - - ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) - KPH_CONFIG_CLEAN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) - Level3 - - - - - ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) - KPH_CONFIG_CLEAN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) - Level3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF} + {dd38f7fc-d7bd-488b-9242-7d8754cde80d} + 11.0 + Win8 Debug + Win32 + + + KProcessHacker + $(VCTargetsPath11) + 10.0.18362.0 + + + WindowsKernelModeDriver10.0 + Driver + WDM + + + + Windows7 + true + Unicode + + + Windows7 + false + Unicode + + + Windows7 + true + Unicode + + + Windows7 + false + Unicode + + + + + + + + + + DbgengKernelDebugger + + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + kprocesshacker + + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + kprocesshacker + + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + kprocesshacker + + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + kprocesshacker + + + + ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) + KPH_CONFIG_CLEAN;POOL_NX_OPTIN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) + Level3 + true + true + false + + + ksecdd.lib;%(AdditionalDependencies) + true + + + + + ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) + KPH_CONFIG_CLEAN;POOL_NX_OPTIN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) + Level3 + true + true + + + ksecdd.lib;%(AdditionalDependencies) + true + + + + + ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) + KPH_CONFIG_CLEAN;POOL_NX_OPTIN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) + Level3 + true + true + false + + + ksecdd.lib;%(AdditionalDependencies) + true + + + + + ../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories) + KPH_CONFIG_CLEAN;POOL_NX_OPTIN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) + Level3 + true + true + + + ksecdd.lib;%(AdditionalDependencies) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/KProcessHacker/KProcessHacker.vcxproj.filters b/KProcessHacker/KProcessHacker.vcxproj.filters index 1ff8bd09f11c..87b584444426 100644 --- a/KProcessHacker/KProcessHacker.vcxproj.filters +++ b/KProcessHacker/KProcessHacker.vcxproj.filters @@ -46,6 +46,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/KProcessHacker/bin/amd64/kprocesshacker.pdb b/KProcessHacker/bin/amd64/kprocesshacker.pdb deleted file mode 100644 index 93dbdc215621..000000000000 Binary files a/KProcessHacker/bin/amd64/kprocesshacker.pdb and /dev/null differ diff --git a/KProcessHacker/bin/amd64/kprocesshacker.sys b/KProcessHacker/bin/amd64/kprocesshacker.sys deleted file mode 100644 index f6a94ff33aa2..000000000000 Binary files a/KProcessHacker/bin/amd64/kprocesshacker.sys and /dev/null differ diff --git a/KProcessHacker/bin/i386/kprocesshacker.pdb b/KProcessHacker/bin/i386/kprocesshacker.pdb deleted file mode 100644 index b82fe335059d..000000000000 Binary files a/KProcessHacker/bin/i386/kprocesshacker.pdb and /dev/null differ diff --git a/KProcessHacker/bin/i386/kprocesshacker.sys b/KProcessHacker/bin/i386/kprocesshacker.sys deleted file mode 100644 index 4f8f329cc11f..000000000000 Binary files a/KProcessHacker/bin/i386/kprocesshacker.sys and /dev/null differ diff --git a/KProcessHacker/clean/makefile b/KProcessHacker/clean/makefile deleted file mode 100644 index 05a507be45d7..000000000000 --- a/KProcessHacker/clean/makefile +++ /dev/null @@ -1 +0,0 @@ -!INCLUDE $(NTMAKEENV)\makefile.def \ No newline at end of file diff --git a/KProcessHacker/clean/sources b/KProcessHacker/clean/sources deleted file mode 100644 index e2834d805734..000000000000 --- a/KProcessHacker/clean/sources +++ /dev/null @@ -1,7 +0,0 @@ -!IF 0 - -This builds a clean version of KProcessHacker suitable for driver signing. - -!ENDIF - -!include ..\sources.inc diff --git a/KProcessHacker/devctrl.c b/KProcessHacker/devctrl.c index f6a048fae306..8276f50d983f 100644 --- a/KProcessHacker/devctrl.c +++ b/KProcessHacker/devctrl.c @@ -1,566 +1,566 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -NTSTATUS KphDispatchDeviceControl( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp - ) -{ - NTSTATUS status; - PIO_STACK_LOCATION stackLocation; - PFILE_OBJECT fileObject; - PKPH_CLIENT client; - PVOID originalInput; - ULONG inputLength; - ULONG ioControlCode; - KPROCESSOR_MODE accessMode; - UCHAR capturedInput[16 * sizeof(ULONG_PTR)]; - PVOID capturedInputPointer; - -#define VERIFY_INPUT_LENGTH \ - do { \ - /* Ensure at compile time that our local buffer fits this particular call. */ \ - C_ASSERT(sizeof(*input) <= sizeof(capturedInput)); \ - \ - if (inputLength != sizeof(*input)) \ - { \ - status = STATUS_INFO_LENGTH_MISMATCH; \ - goto ControlEnd; \ - } \ - } while (0) - - stackLocation = IoGetCurrentIrpStackLocation(Irp); - fileObject = stackLocation->FileObject; - client = fileObject->FsContext; - - originalInput = stackLocation->Parameters.DeviceIoControl.Type3InputBuffer; - inputLength = stackLocation->Parameters.DeviceIoControl.InputBufferLength; - ioControlCode = stackLocation->Parameters.DeviceIoControl.IoControlCode; - accessMode = Irp->RequestorMode; - - // Make sure we have a client object. - if (!client) - { - status = STATUS_INTERNAL_ERROR; - goto ControlEnd; - } - - // Enforce signature requirement if necessary. - if ((ioControlCode != KPH_GETFEATURES && ioControlCode != KPH_VERIFYCLIENT) && - (KphParameters.SecurityLevel == KphSecuritySignatureCheck || - KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck) && - !client->VerificationSucceeded) - { - status = STATUS_ACCESS_DENIED; - goto ControlEnd; - } - - // Make sure we actually have input if the input length is non-zero. - if (inputLength != 0 && !originalInput) - { - status = STATUS_INVALID_BUFFER_SIZE; - goto ControlEnd; - } - - // Make sure the caller isn't giving us a huge buffer. If they are, it can't be correct because - // we have a compile-time check that makes sure our buffer can store the arguments for all the - // calls. - if (inputLength > sizeof(capturedInput)) - { - status = STATUS_INVALID_BUFFER_SIZE; - goto ControlEnd; - } - - // Probe and capture the input buffer. - if (accessMode != KernelMode) - { - __try - { - ProbeForRead(originalInput, inputLength, sizeof(UCHAR)); - memcpy(capturedInput, originalInput, inputLength); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - goto ControlEnd; - } - } - else - { - memcpy(capturedInput, originalInput, inputLength); - } - - capturedInputPointer = capturedInput; // avoid casting below - - switch (ioControlCode) - { - case KPH_GETFEATURES: - { - struct - { - PULONG Features; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiGetFeatures( - input->Features, - accessMode - ); - } - break; - case KPH_VERIFYCLIENT: - { - struct - { - PVOID CodeAddress; - PVOID Signature; - ULONG SignatureSize; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - if (accessMode == UserMode) - { - status = KpiVerifyClient( - input->CodeAddress, - input->Signature, - input->SignatureSize, - client - ); - } - else - { - status = STATUS_UNSUCCESSFUL; - } - } - break; - case KPH_RETRIEVEKEY: - { - struct - { - KPH_KEY_LEVEL KeyLevel; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - if (accessMode == UserMode) - { - status = KphRetrieveKeyViaApc( - client, - input->KeyLevel, - Irp - ); - } - else - { - status = STATUS_UNSUCCESSFUL; - } - } - break; - case KPH_OPENPROCESS: - { - struct - { - PHANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - PCLIENT_ID ClientId; - KPH_KEY Key; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiOpenProcess( - input->ProcessHandle, - input->DesiredAccess, - input->ClientId, - input->Key, - client, - accessMode - ); - } - break; - case KPH_OPENPROCESSTOKEN: - { - struct - { - HANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - PHANDLE TokenHandle; - KPH_KEY Key; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiOpenProcessToken( - input->ProcessHandle, - input->DesiredAccess, - input->TokenHandle, - input->Key, - client, - accessMode - ); - } - break; - case KPH_OPENPROCESSJOB: - { - struct - { - HANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - PHANDLE JobHandle; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiOpenProcessJob( - input->ProcessHandle, - input->DesiredAccess, - input->JobHandle, - accessMode - ); - } - break; - case KPH_TERMINATEPROCESS: - { - struct - { - HANDLE ProcessHandle; - NTSTATUS ExitStatus; - KPH_KEY Key; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiTerminateProcess( - input->ProcessHandle, - input->ExitStatus, - input->Key, - client, - accessMode - ); - } - break; - case KPH_READVIRTUALMEMORYUNSAFE: - { - struct - { - HANDLE ProcessHandle; - PVOID BaseAddress; - PVOID Buffer; - SIZE_T BufferSize; - PSIZE_T NumberOfBytesRead; - KPH_KEY Key; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiReadVirtualMemoryUnsafe( - input->ProcessHandle, - input->BaseAddress, - input->Buffer, - input->BufferSize, - input->NumberOfBytesRead, - input->Key, - client, - accessMode - ); - } - break; - case KPH_QUERYINFORMATIONPROCESS: - { - struct - { - HANDLE ProcessHandle; - KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; - PVOID ProcessInformation; - ULONG ProcessInformationLength; - PULONG ReturnLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiQueryInformationProcess( - input->ProcessHandle, - input->ProcessInformationClass, - input->ProcessInformation, - input->ProcessInformationLength, - input->ReturnLength, - accessMode - ); - } - break; - case KPH_SETINFORMATIONPROCESS: - { - struct - { - HANDLE ProcessHandle; - KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; - PVOID ProcessInformation; - ULONG ProcessInformationLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiSetInformationProcess( - input->ProcessHandle, - input->ProcessInformationClass, - input->ProcessInformation, - input->ProcessInformationLength, - accessMode - ); - } - break; - case KPH_OPENTHREAD: - { - struct - { - PHANDLE ThreadHandle; - ACCESS_MASK DesiredAccess; - PCLIENT_ID ClientId; - KPH_KEY Key; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiOpenThread( - input->ThreadHandle, - input->DesiredAccess, - input->ClientId, - input->Key, - client, - accessMode - ); - } - break; - case KPH_OPENTHREADPROCESS: - { - struct - { - HANDLE ThreadHandle; - ACCESS_MASK DesiredAccess; - PHANDLE ProcessHandle; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiOpenThreadProcess( - input->ThreadHandle, - input->DesiredAccess, - input->ProcessHandle, - accessMode - ); - } - break; - case KPH_CAPTURESTACKBACKTRACETHREAD: - { - struct - { - HANDLE ThreadHandle; - ULONG FramesToSkip; - ULONG FramesToCapture; - PVOID *BackTrace; - PULONG CapturedFrames; - PULONG BackTraceHash; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiCaptureStackBackTraceThread( - input->ThreadHandle, - input->FramesToSkip, - input->FramesToCapture, - input->BackTrace, - input->CapturedFrames, - input->BackTraceHash, - accessMode - ); - } - break; - case KPH_QUERYINFORMATIONTHREAD: - { - struct - { - HANDLE ThreadHandle; - KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; - PVOID ThreadInformation; - ULONG ThreadInformationLength; - PULONG ReturnLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiQueryInformationThread( - input->ThreadHandle, - input->ThreadInformationClass, - input->ThreadInformation, - input->ThreadInformationLength, - input->ReturnLength, - accessMode - ); - } - break; - case KPH_SETINFORMATIONTHREAD: - { - struct - { - HANDLE ThreadHandle; - KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; - PVOID ThreadInformation; - ULONG ThreadInformationLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiSetInformationThread( - input->ThreadHandle, - input->ThreadInformationClass, - input->ThreadInformation, - input->ThreadInformationLength, - accessMode - ); - } - break; - case KPH_ENUMERATEPROCESSHANDLES: - { - struct - { - HANDLE ProcessHandle; - PVOID Buffer; - ULONG BufferLength; - PULONG ReturnLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiEnumerateProcessHandles( - input->ProcessHandle, - input->Buffer, - input->BufferLength, - input->ReturnLength, - accessMode - ); - } - break; - case KPH_QUERYINFORMATIONOBJECT: - { - struct - { - HANDLE ProcessHandle; - HANDLE Handle; - KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; - PVOID ObjectInformation; - ULONG ObjectInformationLength; - PULONG ReturnLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiQueryInformationObject( - input->ProcessHandle, - input->Handle, - input->ObjectInformationClass, - input->ObjectInformation, - input->ObjectInformationLength, - input->ReturnLength, - accessMode - ); - } - break; - case KPH_SETINFORMATIONOBJECT: - { - struct - { - HANDLE ProcessHandle; - HANDLE Handle; - KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; - PVOID ObjectInformation; - ULONG ObjectInformationLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiSetInformationObject( - input->ProcessHandle, - input->Handle, - input->ObjectInformationClass, - input->ObjectInformation, - input->ObjectInformationLength, - accessMode - ); - } - break; - case KPH_OPENDRIVER: - { - struct - { - PHANDLE DriverHandle; - ACCESS_MASK DesiredAccess; - POBJECT_ATTRIBUTES ObjectAttributes; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiOpenDriver( - input->DriverHandle, - input->DesiredAccess, - input->ObjectAttributes, - accessMode - ); - } - break; - case KPH_QUERYINFORMATIONDRIVER: - { - struct - { - HANDLE DriverHandle; - DRIVER_INFORMATION_CLASS DriverInformationClass; - PVOID DriverInformation; - ULONG DriverInformationLength; - PULONG ReturnLength; - } *input = capturedInputPointer; - - VERIFY_INPUT_LENGTH; - - status = KpiQueryInformationDriver( - input->DriverHandle, - input->DriverInformationClass, - input->DriverInformation, - input->DriverInformationLength, - input->ReturnLength, - accessMode - ); - } - break; - default: - status = STATUS_INVALID_DEVICE_REQUEST; - break; - } - -ControlEnd: - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +NTSTATUS KphDispatchDeviceControl( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +{ + NTSTATUS status; + PIO_STACK_LOCATION stackLocation; + PFILE_OBJECT fileObject; + PKPH_CLIENT client; + PVOID originalInput; + ULONG inputLength; + ULONG ioControlCode; + KPROCESSOR_MODE accessMode; + UCHAR capturedInput[16 * sizeof(ULONG_PTR)]; + PVOID capturedInputPointer; + +#define VERIFY_INPUT_LENGTH \ + do { \ + /* Ensure at compile time that our local buffer fits this particular call. */ \ + C_ASSERT(sizeof(*input) <= sizeof(capturedInput)); \ + \ + if (inputLength != sizeof(*input)) \ + { \ + status = STATUS_INFO_LENGTH_MISMATCH; \ + goto ControlEnd; \ + } \ + } while (0) + + stackLocation = IoGetCurrentIrpStackLocation(Irp); + fileObject = stackLocation->FileObject; + client = fileObject->FsContext; + + originalInput = stackLocation->Parameters.DeviceIoControl.Type3InputBuffer; + inputLength = stackLocation->Parameters.DeviceIoControl.InputBufferLength; + ioControlCode = stackLocation->Parameters.DeviceIoControl.IoControlCode; + accessMode = Irp->RequestorMode; + + // Make sure we have a client object. + if (!client) + { + status = STATUS_INTERNAL_ERROR; + goto ControlEnd; + } + + // Enforce signature requirement if necessary. + if ((ioControlCode != KPH_GETFEATURES && ioControlCode != KPH_VERIFYCLIENT) && + (KphParameters.SecurityLevel == KphSecuritySignatureCheck || + KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck) && + !client->VerificationSucceeded) + { + status = STATUS_ACCESS_DENIED; + goto ControlEnd; + } + + // Make sure we actually have input if the input length is non-zero. + if (inputLength != 0 && !originalInput) + { + status = STATUS_INVALID_BUFFER_SIZE; + goto ControlEnd; + } + + // Make sure the caller isn't giving us a huge buffer. If they are, it can't be correct because + // we have a compile-time check that makes sure our buffer can store the arguments for all the + // calls. + if (inputLength > sizeof(capturedInput)) + { + status = STATUS_INVALID_BUFFER_SIZE; + goto ControlEnd; + } + + // Probe and capture the input buffer. + if (accessMode != KernelMode) + { + __try + { + ProbeForRead(originalInput, inputLength, sizeof(UCHAR)); + memcpy(capturedInput, originalInput, inputLength); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + goto ControlEnd; + } + } + else + { + memcpy(capturedInput, originalInput, inputLength); + } + + capturedInputPointer = capturedInput; // avoid casting below + + switch (ioControlCode) + { + case KPH_GETFEATURES: + { + struct + { + PULONG Features; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiGetFeatures( + input->Features, + accessMode + ); + } + break; + case KPH_VERIFYCLIENT: + { + struct + { + PVOID CodeAddress; + PVOID Signature; + ULONG SignatureSize; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + if (accessMode == UserMode) + { + status = KpiVerifyClient( + input->CodeAddress, + input->Signature, + input->SignatureSize, + client + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + } + break; + case KPH_RETRIEVEKEY: + { + struct + { + KPH_KEY_LEVEL KeyLevel; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + if (accessMode == UserMode) + { + status = KphRetrieveKeyViaApc( + client, + input->KeyLevel, + Irp + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + } + break; + case KPH_OPENPROCESS: + { + struct + { + PHANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + PCLIENT_ID ClientId; + KPH_KEY Key; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiOpenProcess( + input->ProcessHandle, + input->DesiredAccess, + input->ClientId, + input->Key, + client, + accessMode + ); + } + break; + case KPH_OPENPROCESSTOKEN: + { + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + PHANDLE TokenHandle; + KPH_KEY Key; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiOpenProcessToken( + input->ProcessHandle, + input->DesiredAccess, + input->TokenHandle, + input->Key, + client, + accessMode + ); + } + break; + case KPH_OPENPROCESSJOB: + { + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + PHANDLE JobHandle; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiOpenProcessJob( + input->ProcessHandle, + input->DesiredAccess, + input->JobHandle, + accessMode + ); + } + break; + case KPH_TERMINATEPROCESS: + { + struct + { + HANDLE ProcessHandle; + NTSTATUS ExitStatus; + KPH_KEY Key; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiTerminateProcess( + input->ProcessHandle, + input->ExitStatus, + input->Key, + client, + accessMode + ); + } + break; + case KPH_READVIRTUALMEMORYUNSAFE: + { + struct + { + HANDLE ProcessHandle; + PVOID BaseAddress; + PVOID Buffer; + SIZE_T BufferSize; + PSIZE_T NumberOfBytesRead; + KPH_KEY Key; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiReadVirtualMemoryUnsafe( + input->ProcessHandle, + input->BaseAddress, + input->Buffer, + input->BufferSize, + input->NumberOfBytesRead, + input->Key, + client, + accessMode + ); + } + break; + case KPH_QUERYINFORMATIONPROCESS: + { + struct + { + HANDLE ProcessHandle; + KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; + PVOID ProcessInformation; + ULONG ProcessInformationLength; + PULONG ReturnLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiQueryInformationProcess( + input->ProcessHandle, + input->ProcessInformationClass, + input->ProcessInformation, + input->ProcessInformationLength, + input->ReturnLength, + accessMode + ); + } + break; + case KPH_SETINFORMATIONPROCESS: + { + struct + { + HANDLE ProcessHandle; + KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; + PVOID ProcessInformation; + ULONG ProcessInformationLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiSetInformationProcess( + input->ProcessHandle, + input->ProcessInformationClass, + input->ProcessInformation, + input->ProcessInformationLength, + accessMode + ); + } + break; + case KPH_OPENTHREAD: + { + struct + { + PHANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + PCLIENT_ID ClientId; + KPH_KEY Key; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiOpenThread( + input->ThreadHandle, + input->DesiredAccess, + input->ClientId, + input->Key, + client, + accessMode + ); + } + break; + case KPH_OPENTHREADPROCESS: + { + struct + { + HANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + PHANDLE ProcessHandle; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiOpenThreadProcess( + input->ThreadHandle, + input->DesiredAccess, + input->ProcessHandle, + accessMode + ); + } + break; + case KPH_CAPTURESTACKBACKTRACETHREAD: + { + struct + { + HANDLE ThreadHandle; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + PULONG CapturedFrames; + PULONG BackTraceHash; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiCaptureStackBackTraceThread( + input->ThreadHandle, + input->FramesToSkip, + input->FramesToCapture, + input->BackTrace, + input->CapturedFrames, + input->BackTraceHash, + accessMode + ); + } + break; + case KPH_QUERYINFORMATIONTHREAD: + { + struct + { + HANDLE ThreadHandle; + KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; + PVOID ThreadInformation; + ULONG ThreadInformationLength; + PULONG ReturnLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiQueryInformationThread( + input->ThreadHandle, + input->ThreadInformationClass, + input->ThreadInformation, + input->ThreadInformationLength, + input->ReturnLength, + accessMode + ); + } + break; + case KPH_SETINFORMATIONTHREAD: + { + struct + { + HANDLE ThreadHandle; + KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; + PVOID ThreadInformation; + ULONG ThreadInformationLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiSetInformationThread( + input->ThreadHandle, + input->ThreadInformationClass, + input->ThreadInformation, + input->ThreadInformationLength, + accessMode + ); + } + break; + case KPH_ENUMERATEPROCESSHANDLES: + { + struct + { + HANDLE ProcessHandle; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiEnumerateProcessHandles( + input->ProcessHandle, + input->Buffer, + input->BufferLength, + input->ReturnLength, + accessMode + ); + } + break; + case KPH_QUERYINFORMATIONOBJECT: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; + PVOID ObjectInformation; + ULONG ObjectInformationLength; + PULONG ReturnLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiQueryInformationObject( + input->ProcessHandle, + input->Handle, + input->ObjectInformationClass, + input->ObjectInformation, + input->ObjectInformationLength, + input->ReturnLength, + accessMode + ); + } + break; + case KPH_SETINFORMATIONOBJECT: + { + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; + PVOID ObjectInformation; + ULONG ObjectInformationLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiSetInformationObject( + input->ProcessHandle, + input->Handle, + input->ObjectInformationClass, + input->ObjectInformation, + input->ObjectInformationLength, + accessMode + ); + } + break; + case KPH_OPENDRIVER: + { + struct + { + PHANDLE DriverHandle; + ACCESS_MASK DesiredAccess; + POBJECT_ATTRIBUTES ObjectAttributes; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiOpenDriver( + input->DriverHandle, + input->DesiredAccess, + input->ObjectAttributes, + accessMode + ); + } + break; + case KPH_QUERYINFORMATIONDRIVER: + { + struct + { + HANDLE DriverHandle; + DRIVER_INFORMATION_CLASS DriverInformationClass; + PVOID DriverInformation; + ULONG DriverInformationLength; + PULONG ReturnLength; + } *input = capturedInputPointer; + + VERIFY_INPUT_LENGTH; + + status = KpiQueryInformationDriver( + input->DriverHandle, + input->DriverInformationClass, + input->DriverInformation, + input->DriverInformationLength, + input->ReturnLength, + accessMode + ); + } + break; + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + +ControlEnd: + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} diff --git a/KProcessHacker/dirs b/KProcessHacker/dirs deleted file mode 100644 index 1caf7fa5b055..000000000000 --- a/KProcessHacker/dirs +++ /dev/null @@ -1 +0,0 @@ -DIRS=clean diff --git a/KProcessHacker/dyndata.c b/KProcessHacker/dyndata.c index 616e3aa7125c..be90cda839b0 100644 --- a/KProcessHacker/dyndata.c +++ b/KProcessHacker/dyndata.c @@ -1,167 +1,165 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#define _DYNDATA_PRIVATE -#include - -#define C_2sTo4(x) ((unsigned int)(signed short)(x)) - -NTSTATUS KphpLoadDynamicConfiguration( - __in PVOID Buffer, - __in ULONG Length - ); - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KphDynamicDataInitialization) -#pragma alloc_text(PAGE, KphReadDynamicDataParameters) -#pragma alloc_text(PAGE, KphpLoadDynamicConfiguration) -#endif - -NTSTATUS KphDynamicDataInitialization( - VOID - ) -{ - NTSTATUS status = STATUS_SUCCESS; - - PAGED_CODE(); - - // Get Windows version information. - - KphDynOsVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - status = RtlGetVersion((PRTL_OSVERSIONINFOW)&KphDynOsVersionInfo); - - return status; -} - -NTSTATUS KphReadDynamicDataParameters( - __in_opt HANDLE KeyHandle - ) -{ - NTSTATUS status; - UNICODE_STRING valueName; - PKEY_VALUE_PARTIAL_INFORMATION info; - ULONG resultLength; - - PAGED_CODE(); - - if (!KeyHandle) - return STATUS_UNSUCCESSFUL; - - RtlInitUnicodeString(&valueName, L"DynamicConfiguration"); - - status = ZwQueryValueKey( - KeyHandle, - &valueName, - KeyValuePartialInformation, - NULL, - 0, - &resultLength - ); - - if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) - { - // Unexpected status; fail now. - return STATUS_UNSUCCESSFUL; - } - - info = ExAllocatePoolWithTag(PagedPool, resultLength, 'ThpK'); - - if (!info) - return STATUS_INSUFFICIENT_RESOURCES; - - status = ZwQueryValueKey( - KeyHandle, - &valueName, - KeyValuePartialInformation, - info, - resultLength, - &resultLength - ); - - if (NT_SUCCESS(status)) - { - if (info->Type == REG_BINARY) - status = KphpLoadDynamicConfiguration(info->Data, info->DataLength); - else - status = STATUS_OBJECT_TYPE_MISMATCH; - - if (!NT_SUCCESS(status)) - dprintf("Unable to load dynamic configuration: 0x%x\n", status); - } - - ExFreePoolWithTag(info, 'ThpK'); - - return status; -} - -NTSTATUS KphpLoadDynamicConfiguration( - __in PVOID Buffer, - __in ULONG Length - ) -{ - PKPH_DYN_CONFIGURATION config; - ULONG i; - PKPH_DYN_PACKAGE package; - - PAGED_CODE(); - - config = Buffer; - - if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages)) - return STATUS_INVALID_PARAMETER; - if (config->Version != KPH_DYN_CONFIGURATION_VERSION) - return STATUS_INVALID_PARAMETER; - if (config->NumberOfPackages > KPH_DYN_MAXIMUM_PACKAGES) - return STATUS_INVALID_PARAMETER; - if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages) + config->NumberOfPackages * sizeof(KPH_DYN_PACKAGE)) - return STATUS_INVALID_PARAMETER; - - dprintf("Loading dynamic configuration with %u package(s)\n", config->NumberOfPackages); - - for (i = 0; i < config->NumberOfPackages; i++) - { - package = &config->Packages[i]; - - if (package->MajorVersion == KphDynOsVersionInfo.dwMajorVersion && - package->MinorVersion == KphDynOsVersionInfo.dwMinorVersion && - (package->ServicePackMajor == (USHORT)-1 || package->ServicePackMajor == KphDynOsVersionInfo.wServicePackMajor) && - (package->BuildNumber == (USHORT)-1 || package->BuildNumber == KphDynOsVersionInfo.dwBuildNumber)) - { - dprintf("Found matching package at index %u for Windows %u.%u\n", i, package->MajorVersion, package->MinorVersion); - - KphDynNtVersion = package->ResultingNtVersion; - - KphDynEgeGuid = C_2sTo4(package->StructData.EgeGuid); - KphDynEpObjectTable = C_2sTo4(package->StructData.EpObjectTable); - KphDynEreGuidEntry = C_2sTo4(package->StructData.EreGuidEntry); - KphDynHtHandleContentionEvent = C_2sTo4(package->StructData.HtHandleContentionEvent); - KphDynOtName = C_2sTo4(package->StructData.OtName); - KphDynOtIndex = C_2sTo4(package->StructData.OtIndex); - KphDynObDecodeShift = C_2sTo4(package->StructData.ObDecodeShift); - KphDynObAttributesShift = C_2sTo4(package->StructData.ObAttributesShift); - - return STATUS_SUCCESS; - } - } - - return STATUS_NOT_FOUND; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#define _DYNDATA_PRIVATE +#include + +NTSTATUS KphpLoadDynamicConfiguration( + _In_ PVOID Buffer, + _In_ ULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphDynamicDataInitialization) +#pragma alloc_text(PAGE, KphReadDynamicDataParameters) +#pragma alloc_text(PAGE, KphpLoadDynamicConfiguration) +#endif + +NTSTATUS KphDynamicDataInitialization( + VOID + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // Get Windows version information. + + KphDynOsVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + status = RtlGetVersion((PRTL_OSVERSIONINFOW)&KphDynOsVersionInfo); + + return status; +} + +NTSTATUS KphReadDynamicDataParameters( + _In_opt_ HANDLE KeyHandle + ) +{ + NTSTATUS status; + UNICODE_STRING valueName; + PKEY_VALUE_PARTIAL_INFORMATION info; + ULONG resultLength; + + PAGED_CODE(); + + if (!KeyHandle) + return STATUS_UNSUCCESSFUL; + + RtlInitUnicodeString(&valueName, L"DynamicConfiguration"); + + status = ZwQueryValueKey( + KeyHandle, + &valueName, + KeyValuePartialInformation, + NULL, + 0, + &resultLength + ); + + if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) + { + // Unexpected status; fail now. + return STATUS_UNSUCCESSFUL; + } + + info = ExAllocatePoolWithTag(PagedPool, resultLength, 'ThpK'); + + if (!info) + return STATUS_INSUFFICIENT_RESOURCES; + + status = ZwQueryValueKey( + KeyHandle, + &valueName, + KeyValuePartialInformation, + info, + resultLength, + &resultLength + ); + + if (NT_SUCCESS(status)) + { + if (info->Type == REG_BINARY) + status = KphpLoadDynamicConfiguration(info->Data, info->DataLength); + else + status = STATUS_OBJECT_TYPE_MISMATCH; + + if (!NT_SUCCESS(status)) + dprintf("Unable to load dynamic configuration: 0x%x\n", status); + } + + ExFreePoolWithTag(info, 'ThpK'); + + return status; +} + +NTSTATUS KphpLoadDynamicConfiguration( + _In_ PVOID Buffer, + _In_ ULONG Length + ) +{ + PKPH_DYN_CONFIGURATION config; + ULONG i; + PKPH_DYN_PACKAGE package; + + PAGED_CODE(); + + config = Buffer; + + if (Length < UFIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages)) + return STATUS_INVALID_PARAMETER; + if (config->Version != KPH_DYN_CONFIGURATION_VERSION) + return STATUS_INVALID_PARAMETER; + if (config->NumberOfPackages > KPH_DYN_MAXIMUM_PACKAGES) + return STATUS_INVALID_PARAMETER; + if (Length < UFIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages) + config->NumberOfPackages * sizeof(KPH_DYN_PACKAGE)) + return STATUS_INVALID_PARAMETER; + + dprintf("Loading dynamic configuration with %u package(s)\n", config->NumberOfPackages); + + for (i = 0; i < config->NumberOfPackages; i++) + { + package = &config->Packages[i]; + + if (package->MajorVersion == KphDynOsVersionInfo.dwMajorVersion && + package->MinorVersion == KphDynOsVersionInfo.dwMinorVersion && + (package->ServicePackMajor == (USHORT)-1 || package->ServicePackMajor == KphDynOsVersionInfo.wServicePackMajor) && + (package->BuildNumber == (USHORT)-1 || package->BuildNumber == KphDynOsVersionInfo.dwBuildNumber)) + { + dprintf("Found matching package at index %u for Windows %u.%u\n", i, package->MajorVersion, package->MinorVersion); + + KphDynNtVersion = package->ResultingNtVersion; + + KphDynEgeGuid = C_2sTo4(package->StructData.EgeGuid); + KphDynEpObjectTable = C_2sTo4(package->StructData.EpObjectTable); + KphDynEreGuidEntry = C_2sTo4(package->StructData.EreGuidEntry); + KphDynHtHandleContentionEvent = C_2sTo4(package->StructData.HtHandleContentionEvent); + KphDynOtName = C_2sTo4(package->StructData.OtName); + KphDynOtIndex = C_2sTo4(package->StructData.OtIndex); + KphDynObDecodeShift = C_2sTo4(package->StructData.ObDecodeShift); + KphDynObAttributesShift = C_2sTo4(package->StructData.ObAttributesShift); + + return STATUS_SUCCESS; + } + } + + return STATUS_NOT_FOUND; +} diff --git a/KProcessHacker/dynimp.c b/KProcessHacker/dynimp.c index 979364464879..7b02b5b1841f 100644 --- a/KProcessHacker/dynimp.c +++ b/KProcessHacker/dynimp.c @@ -1,60 +1,60 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KphGetSystemRoutineAddress) -#pragma alloc_text(PAGE, KphDynamicImport) -#endif - -/** - * Dynamically imports routines. - */ -VOID KphDynamicImport( - VOID - ) -{ - PAGED_CODE(); - NOTHING; -} - -/** - * Retrieves the address of a function exported by NTOS or HAL. - * - * \param SystemRoutineName The name of the function. - * - * \return The address of the function, or NULL if the function could - * not be found. - */ -PVOID KphGetSystemRoutineAddress( - __in PWSTR SystemRoutineName - ) -{ - UNICODE_STRING systemRoutineName; - - PAGED_CODE(); - - RtlInitUnicodeString(&systemRoutineName, SystemRoutineName); - - return MmGetSystemRoutineAddress(&systemRoutineName); -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphGetSystemRoutineAddress) +#pragma alloc_text(PAGE, KphDynamicImport) +#endif + +/** + * Dynamically imports routines. + */ +VOID KphDynamicImport( + VOID + ) +{ + PAGED_CODE(); + NOTHING; +} + +/** + * Retrieves the address of a function exported by NTOS or HAL. + * + * \param SystemRoutineName The name of the function. + * + * \return The address of the function, or NULL if the function could + * not be found. + */ +PVOID KphGetSystemRoutineAddress( + _In_ PWSTR SystemRoutineName + ) +{ + UNICODE_STRING systemRoutineName; + + PAGED_CODE(); + + RtlInitUnicodeString(&systemRoutineName, SystemRoutineName); + + return MmGetSystemRoutineAddress(&systemRoutineName); +} diff --git a/KProcessHacker/include/dyndata.h b/KProcessHacker/include/dyndata.h index a9ba65dec015..ab36182a3fd9 100644 --- a/KProcessHacker/include/dyndata.h +++ b/KProcessHacker/include/dyndata.h @@ -1,47 +1,47 @@ -#ifndef DYNDATA_H -#define DYNDATA_H - -#ifdef EXT -#undef EXT -#endif - -#ifdef _DYNDATA_PRIVATE -#define EXT -#define OFFDEFAULT = -1 -#else -#define EXT extern -#define OFFDEFAULT -#endif - -EXT ULONG KphDynNtVersion; -EXT RTL_OSVERSIONINFOEXW KphDynOsVersionInfo; - -// Structures -// Ege: ETW_GUID_ENTRY -// Ep: EPROCESS -// Ere: ETW_REG_ENTRY -// Et: ETHREAD -// Ht: HANDLE_TABLE -// Oh: OBJECT_HEADER -// Ot: OBJECT_TYPE -// Oti: OBJECT_TYPE_INITIALIZER, offset measured from an OBJECT_TYPE -// ObDecodeShift: shift value in ObpDecodeObject -// ObAttributesShift: shift value in ObpGetHandleAttributes -EXT ULONG KphDynEgeGuid OFFDEFAULT; -EXT ULONG KphDynEpObjectTable OFFDEFAULT; -EXT ULONG KphDynEreGuidEntry OFFDEFAULT; -EXT ULONG KphDynHtHandleContentionEvent OFFDEFAULT; -EXT ULONG KphDynOtName OFFDEFAULT; -EXT ULONG KphDynOtIndex OFFDEFAULT; -EXT ULONG KphDynObDecodeShift OFFDEFAULT; -EXT ULONG KphDynObAttributesShift OFFDEFAULT; - -NTSTATUS KphDynamicDataInitialization( - VOID - ); - -NTSTATUS KphReadDynamicDataParameters( - __in_opt HANDLE KeyHandle - ); - -#endif +#ifndef DYNDATA_H +#define DYNDATA_H + +#ifdef EXT +#undef EXT +#endif + +#ifdef _DYNDATA_PRIVATE +#define EXT +#define OFFDEFAULT = -1 +#else +#define EXT extern +#define OFFDEFAULT +#endif + +EXT ULONG KphDynNtVersion; +EXT RTL_OSVERSIONINFOEXW KphDynOsVersionInfo; + +// Structures +// Ege: ETW_GUID_ENTRY +// Ep: EPROCESS +// Ere: ETW_REG_ENTRY +// Et: ETHREAD +// Ht: HANDLE_TABLE +// Oh: OBJECT_HEADER +// Ot: OBJECT_TYPE +// Oti: OBJECT_TYPE_INITIALIZER, offset measured from an OBJECT_TYPE +// ObDecodeShift: shift value in ObpDecodeObject +// ObAttributesShift: shift value in ObpGetHandleAttributes +EXT ULONG KphDynEgeGuid OFFDEFAULT; +EXT ULONG KphDynEpObjectTable OFFDEFAULT; +EXT ULONG KphDynEreGuidEntry OFFDEFAULT; +EXT ULONG KphDynHtHandleContentionEvent OFFDEFAULT; +EXT ULONG KphDynOtName OFFDEFAULT; +EXT ULONG KphDynOtIndex OFFDEFAULT; +EXT ULONG KphDynObDecodeShift OFFDEFAULT; +EXT ULONG KphDynObAttributesShift OFFDEFAULT; + +NTSTATUS KphDynamicDataInitialization( + VOID + ); + +NTSTATUS KphReadDynamicDataParameters( + _In_opt_ HANDLE KeyHandle + ); + +#endif diff --git a/KProcessHacker/include/kph.h b/KProcessHacker/include/kph.h index 1d196675dfd0..2afa4a4fc84a 100644 --- a/KProcessHacker/include/kph.h +++ b/KProcessHacker/include/kph.h @@ -1,365 +1,374 @@ -#ifndef KPH_H -#define KPH_H - -#include -#define PHNT_MODE PHNT_MODE_KERNEL -#include -#include -#include -#include - -// Debugging - -#ifdef DBG -#define dprintf(Format, ...) DbgPrint("KProcessHacker: " Format, __VA_ARGS__) -#else -#define dprintf -#endif - -typedef struct _KPH_CLIENT -{ - struct - { - ULONG VerificationPerformed : 1; - ULONG VerificationSucceeded : 1; - ULONG KeysGenerated : 1; - ULONG SpareBits : 29; - }; - FAST_MUTEX StateMutex; - NTSTATUS VerificationStatus; - PVOID VerifiedProcess; // EPROCESS (for equality checking only - do not access contents) - HANDLE VerifiedProcessId; - PVOID VerifiedRangeBase; - SIZE_T VerifiedRangeSize; - // Level 1 and 2 secret keys - FAST_MUTEX KeyBackoffMutex; - KPH_KEY L1Key; - KPH_KEY L2Key; -} KPH_CLIENT, *PKPH_CLIENT; - -typedef struct _KPH_PARAMETERS -{ - KPH_SECURITY_LEVEL SecurityLevel; -} KPH_PARAMETERS, *PKPH_PARAMETERS; - -// main - -extern ULONG KphFeatures; -extern KPH_PARAMETERS KphParameters; - -NTSTATUS KpiGetFeatures( - __out PULONG Features, - __in KPROCESSOR_MODE AccessMode - ); - -// devctrl - -__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH KphDispatchDeviceControl; - -NTSTATUS KphDispatchDeviceControl( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp - ); - -// dynimp - -VOID KphDynamicImport( - VOID - ); - -PVOID KphGetSystemRoutineAddress( - __in PWSTR SystemRoutineName - ); - -// object - -PHANDLE_TABLE KphReferenceProcessHandleTable( - __in PEPROCESS Process - ); - -VOID KphDereferenceProcessHandleTable( - __in PEPROCESS Process - ); - -VOID KphUnlockHandleTableEntry( - __in PHANDLE_TABLE HandleTable, - __in PHANDLE_TABLE_ENTRY HandleTableEntry - ); - -NTSTATUS KpiEnumerateProcessHandles( - __in HANDLE ProcessHandle, - __out_bcount(BufferLength) PVOID Buffer, - __in_opt ULONG BufferLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KphQueryNameObject( - __in PVOID Object, - __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength - ); - -NTSTATUS KphQueryNameFileObject( - __in PFILE_OBJECT FileObject, - __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength - ); - -NTSTATUS KpiQueryInformationObject( - __in HANDLE ProcessHandle, - __in HANDLE Handle, - __in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - __out_bcount(ObjectInformationLength) PVOID ObjectInformation, - __in ULONG ObjectInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiSetInformationObject( - __in HANDLE ProcessHandle, - __in HANDLE Handle, - __in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - __in_bcount(ObjectInformationLength) PVOID ObjectInformation, - __in ULONG ObjectInformationLength, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KphOpenNamedObject( - __out PHANDLE ObjectHandle, - __in ACCESS_MASK DesiredAccess, - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in POBJECT_TYPE ObjectType, - __in KPROCESSOR_MODE AccessMode - ); - -// process - -NTSTATUS KpiOpenProcess( - __out PHANDLE ProcessHandle, - __in ACCESS_MASK DesiredAccess, - __in PCLIENT_ID ClientId, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiOpenProcessToken( - __in HANDLE ProcessHandle, - __in ACCESS_MASK DesiredAccess, - __out PHANDLE TokenHandle, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiOpenProcessJob( - __in HANDLE ProcessHandle, - __in ACCESS_MASK DesiredAccess, - __out PHANDLE JobHandle, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiTerminateProcess( - __in HANDLE ProcessHandle, - __in NTSTATUS ExitStatus, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiQueryInformationProcess( - __in HANDLE ProcessHandle, - __in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - __out_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiSetInformationProcess( - __in HANDLE ProcessHandle, - __in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - __in_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength, - __in KPROCESSOR_MODE AccessMode - ); - -// qrydrv - -NTSTATUS KpiOpenDriver( - __out PHANDLE DriverHandle, - __in ACCESS_MASK DesiredAccess, - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiQueryInformationDriver( - __in HANDLE DriverHandle, - __in DRIVER_INFORMATION_CLASS DriverInformationClass, - __out_bcount(DriverInformationLength) PVOID DriverInformation, - __in ULONG DriverInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ); - -// thread - -NTSTATUS KpiOpenThread( - __out PHANDLE ThreadHandle, - __in ACCESS_MASK DesiredAccess, - __in PCLIENT_ID ClientId, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiOpenThreadProcess( - __in HANDLE ThreadHandle, - __in ACCESS_MASK DesiredAccess, - __out PHANDLE ProcessHandle, - __in KPROCESSOR_MODE AccessMode - ); - -ULONG KphCaptureStackBackTrace( - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __in_opt ULONG Flags, - __out_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG BackTraceHash - ); - -NTSTATUS KphCaptureStackBackTraceThread( - __in PETHREAD Thread, - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __out_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG CapturedFrames, - __out_opt PULONG BackTraceHash, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiCaptureStackBackTraceThread( - __in HANDLE ThreadHandle, - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __out_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG CapturedFrames, - __out_opt PULONG BackTraceHash, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiQueryInformationThread( - __in HANDLE ThreadHandle, - __in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - __out_bcount(ProcessInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ); - -NTSTATUS KpiSetInformationThread( - __in HANDLE ThreadHandle, - __in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - __in_bcount(ThreadInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __in KPROCESSOR_MODE AccessMode - ); - -// util - -VOID KphFreeCapturedUnicodeString( - __in PUNICODE_STRING CapturedUnicodeString - ); - -NTSTATUS KphCaptureUnicodeString( - __in PUNICODE_STRING UnicodeString, - __out PUNICODE_STRING CapturedUnicodeString - ); - -NTSTATUS KphEnumerateSystemModules( - __out PRTL_PROCESS_MODULES *Modules - ); - -NTSTATUS KphValidateAddressForSystemModules( - __in PVOID Address, - __in SIZE_T Length - ); - -NTSTATUS KphGetProcessMappedFileName( - __in HANDLE ProcessHandle, - __in PVOID BaseAddress, - __out PUNICODE_STRING *FileName - ); - -// verify - -NTSTATUS KphHashFile( - __in PUNICODE_STRING FileName, - __out PVOID *Hash, - __out PULONG HashSize - ); - -NTSTATUS KphVerifyFile( - __in PUNICODE_STRING FileName, - __in_bcount(SignatureSize) PUCHAR Signature, - __in ULONG SignatureSize - ); - -VOID KphVerifyClient( - __inout PKPH_CLIENT Client, - __in PVOID CodeAddress, - __in_bcount(SignatureSize) PUCHAR Signature, - __in ULONG SignatureSize - ); - -NTSTATUS KpiVerifyClient( - __in PVOID CodeAddress, - __in_bcount(SignatureSize) PUCHAR Signature, - __in ULONG SignatureSize, - __in PKPH_CLIENT Client - ); - -VOID KphGenerateKeysClient( - __inout PKPH_CLIENT Client - ); - -NTSTATUS KphRetrieveKeyViaApc( - __inout PKPH_CLIENT Client, - __in KPH_KEY_LEVEL KeyLevel, - __inout PIRP Irp - ); - -NTSTATUS KphValidateKey( - __in KPH_KEY_LEVEL RequiredKeyLevel, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ); - -// vm - -NTSTATUS KphCopyVirtualMemory( - __in PEPROCESS FromProcess, - __in PVOID FromAddress, - __in PEPROCESS ToProcess, - __in PVOID ToAddress, - __in SIZE_T BufferLength, - __in KPROCESSOR_MODE AccessMode, - __out PSIZE_T ReturnLength - ); - -NTSTATUS KpiReadVirtualMemoryUnsafe( - __in_opt HANDLE ProcessHandle, - __in PVOID BaseAddress, - __out_bcount(BufferSize) PVOID Buffer, - __in SIZE_T BufferSize, - __out_opt PSIZE_T NumberOfBytesRead, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ); - -#endif +#ifndef KPH_H +#define KPH_H + +#include +#define PHNT_MODE PHNT_MODE_KERNEL +#include +#include +#include +#include + +// Memory + +#define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) +#define PTR_SUB_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) - (ULONG_PTR)(Offset))) + +// Zero extension and sign extension macros + +#define C_2sTo4(x) ((unsigned int)(signed short)(x)) + +// Debugging + +#ifdef DBG +#define dprintf(Format, ...) DbgPrint("KProcessHacker: " Format, __VA_ARGS__) +#else +#define dprintf +#endif + +typedef struct _KPH_CLIENT +{ + struct + { + ULONG VerificationPerformed : 1; + ULONG VerificationSucceeded : 1; + ULONG KeysGenerated : 1; + ULONG SpareBits : 29; + }; + FAST_MUTEX StateMutex; + NTSTATUS VerificationStatus; + PVOID VerifiedProcess; // EPROCESS (for equality checking only - do not access contents) + HANDLE VerifiedProcessId; + PVOID VerifiedRangeBase; + SIZE_T VerifiedRangeSize; + // Level 1 and 2 secret keys + FAST_MUTEX KeyBackoffMutex; + KPH_KEY L1Key; + KPH_KEY L2Key; +} KPH_CLIENT, *PKPH_CLIENT; + +typedef struct _KPH_PARAMETERS +{ + KPH_SECURITY_LEVEL SecurityLevel; +} KPH_PARAMETERS, *PKPH_PARAMETERS; + +// main + +extern ULONG KphFeatures; +extern KPH_PARAMETERS KphParameters; + +NTSTATUS KpiGetFeatures( + _Out_ PULONG Features, + _In_ KPROCESSOR_MODE AccessMode + ); + +// devctrl + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH KphDispatchDeviceControl; + +NTSTATUS KphDispatchDeviceControl( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ); + +// dynimp + +VOID KphDynamicImport( + VOID + ); + +PVOID KphGetSystemRoutineAddress( + _In_ PWSTR SystemRoutineName + ); + +// object + +PHANDLE_TABLE KphReferenceProcessHandleTable( + _In_ PEPROCESS Process + ); + +VOID KphDereferenceProcessHandleTable( + _In_ PEPROCESS Process + ); + +VOID KphUnlockHandleTableEntry( + _In_ PHANDLE_TABLE HandleTable, + _In_ PHANDLE_TABLE_ENTRY HandleTableEntry + ); + +NTSTATUS KpiEnumerateProcessHandles( + _In_ HANDLE ProcessHandle, + _Out_writes_bytes_(BufferLength) PVOID Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphQueryNameObject( + _In_ PVOID Object, + _Out_writes_bytes_(BufferLength) POBJECT_NAME_INFORMATION Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS KphQueryNameFileObject( + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(BufferLength) POBJECT_NAME_INFORMATION Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS KpiQueryInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_writes_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiSetInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KphOpenNamedObject( + _Out_ PHANDLE ObjectHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ POBJECT_TYPE ObjectType, + _In_ KPROCESSOR_MODE AccessMode + ); + +// process + +NTSTATUS KpiOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiOpenProcessJob( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE JobHandle, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiTerminateProcess( + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiSetInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +// qrydrv + +NTSTATUS KpiOpenDriver( + _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiQueryInformationDriver( + _In_ HANDLE DriverHandle, + _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, + _Out_writes_bytes_(DriverInformationLength) PVOID DriverInformation, + _In_ ULONG DriverInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +// thread + +NTSTATUS KpiOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiOpenThreadProcess( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE ProcessHandle, + _In_ KPROCESSOR_MODE AccessMode + ); + +ULONG KphCaptureStackBackTrace( + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _In_opt_ ULONG Flags, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG BackTraceHash + ); + +NTSTATUS KphCaptureStackBackTraceThread( + _In_ PETHREAD Thread, + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG CapturedFrames, + _Out_opt_ PULONG BackTraceHash, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiCaptureStackBackTraceThread( + _In_ HANDLE ThreadHandle, + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG CapturedFrames, + _Out_opt_ PULONG BackTraceHash, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +NTSTATUS KpiSetInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _In_ KPROCESSOR_MODE AccessMode + ); + +// util + +VOID KphFreeCapturedUnicodeString( + _In_ PUNICODE_STRING CapturedUnicodeString + ); + +NTSTATUS KphCaptureUnicodeString( + _In_ PUNICODE_STRING UnicodeString, + _Out_ PUNICODE_STRING CapturedUnicodeString + ); + +NTSTATUS KphEnumerateSystemModules( + _Out_ PRTL_PROCESS_MODULES *Modules + ); + +NTSTATUS KphValidateAddressForSystemModules( + _In_ PVOID Address, + _In_ SIZE_T Length + ); + +NTSTATUS KphGetProcessMappedFileName( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_ PUNICODE_STRING *FileName + ); + +// verify + +NTSTATUS KphHashFile( + _In_ PUNICODE_STRING FileName, + _Out_ PVOID *Hash, + _Out_ PULONG HashSize + ); + +NTSTATUS KphVerifyFile( + _In_ PUNICODE_STRING FileName, + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize + ); + +VOID KphVerifyClient( + _Inout_ PKPH_CLIENT Client, + _In_ PVOID CodeAddress, + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize + ); + +NTSTATUS KpiVerifyClient( + _In_ PVOID CodeAddress, + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize, + _In_ PKPH_CLIENT Client + ); + +VOID KphGenerateKeysClient( + _Inout_ PKPH_CLIENT Client + ); + +NTSTATUS KphRetrieveKeyViaApc( + _Inout_ PKPH_CLIENT Client, + _In_ KPH_KEY_LEVEL KeyLevel, + _Inout_ PIRP Irp + ); + +NTSTATUS KphValidateKey( + _In_ KPH_KEY_LEVEL RequiredKeyLevel, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ); + +// vm + +NTSTATUS KphCopyVirtualMemory( + _In_ PEPROCESS FromProcess, + _In_ PVOID FromAddress, + _In_ PEPROCESS ToProcess, + _In_ PVOID ToAddress, + _In_ SIZE_T BufferLength, + _In_ KPROCESSOR_MODE AccessMode, + _Out_ PSIZE_T ReturnLength + ); + +NTSTATUS KpiReadVirtualMemoryUnsafe( + _In_opt_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_writes_bytes_(BufferSize) PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ); + +#endif diff --git a/KProcessHacker/include/ntfill.h b/KProcessHacker/include/ntfill.h index f430f49983e5..0cf0bcdc300c 100644 --- a/KProcessHacker/include/ntfill.h +++ b/KProcessHacker/include/ntfill.h @@ -1,351 +1,335 @@ -#ifndef NTFILL_H -#define NTFILL_H - -extern ULONG KphDynNtVersion; -extern ULONG KphDynObDecodeShift; -extern ULONG KphDynObAttributesShift; - -// EX - -typedef struct _EX_PUSH_LOCK_WAIT_BLOCK *PEX_PUSH_LOCK_WAIT_BLOCK; - -NTKERNELAPI -VOID -FASTCALL -ExfUnblockPushLock( - __inout PEX_PUSH_LOCK PushLock, - __inout_opt PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock - ); - -typedef struct _HANDLE_TABLE_ENTRY -{ - union - { - PVOID Object; - ULONG ObAttributes; - ULONG_PTR Value; - }; - union - { - ACCESS_MASK GrantedAccess; - LONG NextFreeTableEntry; - }; -} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; - -typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE; - -typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK_61)( - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context - ); - -// since WIN8 -typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK)( - __in PHANDLE_TABLE HandleTable, - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context - ); - -NTKERNELAPI -BOOLEAN -NTAPI -ExEnumHandleTable( - __in PHANDLE_TABLE HandleTable, - __in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, - __inout PVOID Context, - __out_opt PHANDLE Handle - ); - -NTSYSCALLAPI -NTSTATUS -NTAPI -ZwQuerySystemInformation( - __in SYSTEM_INFORMATION_CLASS SystemInformationClass, - __out_bcount_opt(SystemInformationLength) PVOID SystemInformation, - __in ULONG SystemInformationLength, - __out_opt PULONG ReturnLength - ); - -// IO - -extern POBJECT_TYPE *IoDriverObjectType; - -// KE - -typedef enum _KAPC_ENVIRONMENT -{ - OriginalApcEnvironment, - AttachedApcEnvironment, - CurrentApcEnvironment, - InsertApcEnvironment -} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT; - -typedef VOID (NTAPI *PKNORMAL_ROUTINE)( - __in PVOID NormalContext, - __in PVOID SystemArgument1, - __in PVOID SystemArgument2 - ); - -typedef VOID KKERNEL_ROUTINE( - __in PRKAPC Apc, - __inout PKNORMAL_ROUTINE *NormalRoutine, - __inout PVOID *NormalContext, - __inout PVOID *SystemArgument1, - __inout PVOID *SystemArgument2 - ); - -typedef KKERNEL_ROUTINE (NTAPI *PKKERNEL_ROUTINE); - -typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)( - __in PRKAPC Apc - ); - -NTKERNELAPI -VOID -NTAPI -KeInitializeApc( - __out PRKAPC Apc, - __in PRKTHREAD Thread, - __in KAPC_ENVIRONMENT Environment, - __in PKKERNEL_ROUTINE KernelRoutine, - __in_opt PKRUNDOWN_ROUTINE RundownRoutine, - __in_opt PKNORMAL_ROUTINE NormalRoutine, - __in_opt KPROCESSOR_MODE ProcessorMode, - __in_opt PVOID NormalContext - ); - -NTKERNELAPI -BOOLEAN -NTAPI -KeInsertQueueApc( - __inout PRKAPC Apc, - __in_opt PVOID SystemArgument1, - __in_opt PVOID SystemArgument2, - __in KPRIORITY Increment - ); - -// MM - -NTSYSCALLAPI -NTSTATUS -NTAPI -ZwQueryVirtualMemory( - __in HANDLE ProcessHandle, - __in PVOID BaseAddress, - __in MEMORY_INFORMATION_CLASS MemoryInformationClass, - __out_bcount(MemoryInformationLength) PVOID MemoryInformation, - __in SIZE_T MemoryInformationLength, - __out_opt PSIZE_T ReturnLength - ); - -// OB - -// These definitions are no longer correct, but they produce correct results. - -#define OBJ_PROTECT_CLOSE 0x00000001 -#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE) - -// This attribute is now stored in the GrantedAccess field. -#define ObpAccessProtectCloseBit 0x2000000 - -#define ObpDecodeGrantedAccess(Access) \ - ((Access) & ~ObpAccessProtectCloseBit) - -FORCEINLINE PVOID ObpDecodeObject(PVOID Object) -{ -#ifdef _M_X64 - if (KphDynNtVersion >= PHNT_WIN8) - { - if (KphDynObDecodeShift != -1) - return (PVOID)(((LONG_PTR)Object >> KphDynObDecodeShift) & ~(ULONG_PTR)0xf); - else - return NULL; - } - else - { - return (PVOID)((ULONG_PTR)Object & ~OBJ_HANDLE_ATTRIBUTES); - } -#else - return (PVOID)((ULONG_PTR)Object & ~OBJ_HANDLE_ATTRIBUTES); -#endif -} - -FORCEINLINE ULONG ObpGetHandleAttributes(PHANDLE_TABLE_ENTRY HandleTableEntry) -{ -#ifdef _M_X64 - if (KphDynNtVersion >= PHNT_WIN8) - { - if (KphDynObAttributesShift != -1) - return (ULONG)(HandleTableEntry->Value >> KphDynObAttributesShift) & 0x3; - else - return 0; - } - else - { - return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) | - ((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0); - } -#else - return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) | - ((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0); -#endif -} - -typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; - -// This is incorrect as of Windows 8.1, but the size of the structure is still correct. -typedef struct _OBJECT_HEADER -{ - LONG PointerCount; - union - { - LONG HandleCount; - PVOID NextToFree; - }; - POBJECT_TYPE Type; - UCHAR NameInfoOffset; - UCHAR HandleInfoOffset; - UCHAR QuotaInfoOffset; - UCHAR Flags; - union - { - POBJECT_CREATE_INFORMATION ObjectCreateInfo; - PVOID QuotaBlockCharged; - }; - PVOID SecurityDescriptor; - QUAD Body; -} OBJECT_HEADER, *POBJECT_HEADER; - -#define OBJECT_TO_OBJECT_HEADER(Object) CONTAINING_RECORD((Object), OBJECT_HEADER, Body) - -NTKERNELAPI -POBJECT_TYPE -NTAPI -ObGetObjectType( - __in PVOID Object - ); - -NTKERNELAPI -NTSTATUS -NTAPI -ObOpenObjectByName( - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in POBJECT_TYPE ObjectType, - __in KPROCESSOR_MODE PreviousMode, - __in_opt PACCESS_STATE AccessState, - __in_opt ACCESS_MASK DesiredAccess, - __in PVOID ParseContext, - __out PHANDLE Handle - ); - -NTKERNELAPI -NTSTATUS -NTAPI -ObSetHandleAttributes( - __in HANDLE Handle, - __in POBJECT_HANDLE_FLAG_INFORMATION HandleFlags, - __in KPROCESSOR_MODE PreviousMode - ); - -NTKERNELAPI -NTSTATUS -ObCloseHandle( - __in HANDLE Handle, - __in KPROCESSOR_MODE PreviousMode - ); - -// PS - -NTSYSCALLAPI -NTSTATUS -NTAPI -ZwQueryInformationProcess( - __in HANDLE ProcessHandle, - __in PROCESSINFOCLASS ProcessInformationClass, - __out_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength, - __out_opt PULONG ReturnLength - ); - -NTSYSCALLAPI -NTSTATUS -NTAPI -ZwSetInformationProcess( - __in HANDLE ProcessHandle, - __in PROCESSINFOCLASS ProcessInformationClass, - __in_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength - ); - -NTSYSCALLAPI -NTSTATUS -NTAPI -ZwQueryInformationThread( - __in HANDLE ThreadHandle, - __in THREADINFOCLASS ThreadInformationClass, - __out_bcount(ThreadInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __out_opt PULONG ReturnLength - ); - -NTKERNELAPI -NTSTATUS -NTAPI -PsLookupProcessThreadByCid( - __in PCLIENT_ID ClientId, - __out_opt PEPROCESS *Process, - __out PETHREAD *Thread - ); - -NTKERNELAPI -PVOID -NTAPI -PsGetThreadWin32Thread( - __in PETHREAD Thread - ); - -typedef struct _EJOB *PEJOB; - -extern POBJECT_TYPE *PsJobType; - -NTKERNELAPI -PEJOB -NTAPI -PsGetProcessJob( - __in PEPROCESS Process - ); - -NTKERNELAPI -NTSTATUS -NTAPI -PsAcquireProcessExitSynchronization( - __in PEPROCESS Process - ); - -NTKERNELAPI -VOID -NTAPI -PsReleaseProcessExitSynchronization( - __in PEPROCESS Process - ); - -// RTL - -// Sensible limit that may or may not correspond to the actual Windows value. -#define MAX_STACK_DEPTH 256 - -#define RTL_WALK_USER_MODE_STACK 0x00000001 -#define RTL_WALK_VALID_FLAGS 0x00000001 - -NTSYSAPI -ULONG -NTAPI -RtlWalkFrameChain( - __out PVOID *Callers, - __in ULONG Count, - __in ULONG Flags - ); - -#endif +#ifndef NTFILL_H +#define NTFILL_H + +extern ULONG KphDynNtVersion; +extern ULONG KphDynObDecodeShift; +extern ULONG KphDynObAttributesShift; + +// EX + +typedef struct _EX_PUSH_LOCK_WAIT_BLOCK *PEX_PUSH_LOCK_WAIT_BLOCK; + +NTKERNELAPI +VOID +FASTCALL +ExfUnblockPushLock( + _Inout_ PEX_PUSH_LOCK PushLock, + _Inout_opt_ PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock + ); + +typedef struct _HANDLE_TABLE_ENTRY +{ + union + { + PVOID Object; + ULONG ObAttributes; + ULONG_PTR Value; + }; + union + { + ACCESS_MASK GrantedAccess; + LONG NextFreeTableEntry; + }; +} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; + +typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE; + +typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK_61)( + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context + ); + +// since WIN8 +typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK)( + _In_ PHANDLE_TABLE HandleTable, + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context + ); + +NTKERNELAPI +BOOLEAN +NTAPI +ExEnumHandleTable( + _In_ PHANDLE_TABLE HandleTable, + _In_ PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, + _Inout_ PVOID Context, + _Out_opt_ PHANDLE Handle + ); + +NTSYSCALLAPI +NTSTATUS +NTAPI +ZwQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +// IO + +extern POBJECT_TYPE *IoDriverObjectType; + +// KE + +typedef enum _KAPC_ENVIRONMENT +{ + OriginalApcEnvironment, + AttachedApcEnvironment, + CurrentApcEnvironment, + InsertApcEnvironment +} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT; + +typedef VOID (NTAPI *PKNORMAL_ROUTINE)( + _In_ PVOID NormalContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2 + ); + +typedef VOID KKERNEL_ROUTINE( + _In_ PRKAPC Apc, + _Inout_ PKNORMAL_ROUTINE *NormalRoutine, + _Inout_ PVOID *NormalContext, + _Inout_ PVOID *SystemArgument1, + _Inout_ PVOID *SystemArgument2 + ); + +typedef KKERNEL_ROUTINE (NTAPI *PKKERNEL_ROUTINE); + +typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)( + _In_ PRKAPC Apc + ); + +NTKERNELAPI +VOID +NTAPI +KeInitializeApc( + _Out_ PRKAPC Apc, + _In_ PRKTHREAD Thread, + _In_ KAPC_ENVIRONMENT Environment, + _In_ PKKERNEL_ROUTINE KernelRoutine, + _In_opt_ PKRUNDOWN_ROUTINE RundownRoutine, + _In_opt_ PKNORMAL_ROUTINE NormalRoutine, + _In_opt_ KPROCESSOR_MODE ProcessorMode, + _In_opt_ PVOID NormalContext + ); + +NTKERNELAPI +BOOLEAN +NTAPI +KeInsertQueueApc( + _Inout_ PRKAPC Apc, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2, + _In_ KPRIORITY Increment + ); + +// OB + +// These definitions are no longer correct, but they produce correct results. + +#define OBJ_PROTECT_CLOSE 0x00000001 +#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE) + +// This attribute is now stored in the GrantedAccess field. +#define ObpAccessProtectCloseBit 0x2000000 + +#define ObpDecodeGrantedAccess(Access) \ + ((Access) & ~ObpAccessProtectCloseBit) + +FORCEINLINE PVOID ObpDecodeObject(PVOID Object) +{ +#ifdef _M_X64 + if (KphDynNtVersion >= PHNT_WIN8) + { + if (KphDynObDecodeShift != -1) + return (PVOID)(((LONG_PTR)Object >> KphDynObDecodeShift) & ~(ULONG_PTR)0xf); + else + return NULL; + } + else + { + return (PVOID)((ULONG_PTR)Object & ~OBJ_HANDLE_ATTRIBUTES); + } +#else + return (PVOID)((ULONG_PTR)Object & ~OBJ_HANDLE_ATTRIBUTES); +#endif +} + +FORCEINLINE ULONG ObpGetHandleAttributes(PHANDLE_TABLE_ENTRY HandleTableEntry) +{ +#ifdef _M_X64 + if (KphDynNtVersion >= PHNT_WIN8) + { + if (KphDynObAttributesShift != -1) + return (ULONG)(HandleTableEntry->Value >> KphDynObAttributesShift) & 0x3; + else + return 0; + } + else + { + return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) | + ((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0); + } +#else + return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) | + ((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0); +#endif +} + +typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; + +// This structure is not correct on Windows 7, but the offsets we need are still correct. +typedef struct _OBJECT_HEADER +{ + LONG PointerCount; + union + { + LONG HandleCount; + PVOID NextToFree; + }; + EX_PUSH_LOCK Lock; + UCHAR TypeIndex; + union + { + UCHAR TraceFlags; + struct + { + UCHAR DbgRefTrace : 1; + UCHAR DbgTracePermanent : 1; + UCHAR Reserved : 6; + }; + }; + UCHAR InfoMask; + union + { + UCHAR Flags; + struct + { + UCHAR NewObject : 1; + UCHAR KernelObject : 1; + UCHAR KernelOnlyAccess : 1; + UCHAR ExclusiveObject : 1; + UCHAR PermanentObject : 1; + UCHAR DefaultSecurityQuota : 1; + UCHAR SingleHandleEntry : 1; + UCHAR DeletedInline : 1; + }; + }; + union + { + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + PVOID QuotaBlockCharged; + }; + PVOID SecurityDescriptor; + QUAD Body; +} OBJECT_HEADER, *POBJECT_HEADER; + +#ifdef _M_X64 +C_ASSERT(FIELD_OFFSET(OBJECT_HEADER, Body) == 0x030); +C_ASSERT(sizeof(OBJECT_HEADER) == 0x038); +#else +C_ASSERT(FIELD_OFFSET(OBJECT_HEADER, Body) == 0x018); +C_ASSERT(sizeof(OBJECT_HEADER) == 0x020); +#endif + +#define OBJECT_TO_OBJECT_HEADER(Object) CONTAINING_RECORD((Object), OBJECT_HEADER, Body) + +NTKERNELAPI +POBJECT_TYPE +NTAPI +ObGetObjectType( + _In_ PVOID Object + ); + +NTKERNELAPI +NTSTATUS +NTAPI +ObOpenObjectByName( + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ POBJECT_TYPE ObjectType, + _In_ KPROCESSOR_MODE PreviousMode, + _In_opt_ PACCESS_STATE AccessState, + _In_opt_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID ParseContext, + _Out_ PHANDLE Handle + ); + +NTKERNELAPI +NTSTATUS +NTAPI +ObSetHandleAttributes( + _In_ HANDLE Handle, + _In_ POBJECT_HANDLE_FLAG_INFORMATION HandleFlags, + _In_ KPROCESSOR_MODE PreviousMode + ); + +// PS + +NTSYSCALLAPI +NTSTATUS +NTAPI +ZwQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSYSCALLAPI +NTSTATUS +NTAPI +ZwQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ THREADINFOCLASS ThreadInformationClass, + _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTKERNELAPI +NTSTATUS +NTAPI +PsLookupProcessThreadByCid( + _In_ PCLIENT_ID ClientId, + _Out_opt_ PEPROCESS *Process, + _Out_ PETHREAD *Thread + ); + +typedef struct _EJOB *PEJOB; + +extern POBJECT_TYPE *PsJobType; + +NTKERNELAPI +PEJOB +NTAPI +PsGetProcessJob( + _In_ PEPROCESS Process + ); + +NTKERNELAPI +NTSTATUS +NTAPI +PsAcquireProcessExitSynchronization( + _In_ PEPROCESS Process + ); + +NTKERNELAPI +VOID +NTAPI +PsReleaseProcessExitSynchronization( + _In_ PEPROCESS Process + ); + +// RTL + +// Sensible limit that may or may not correspond to the actual Windows value. +#define MAX_STACK_DEPTH 256 + +#define RTL_WALK_USER_MODE_STACK 0x00000001 +#define RTL_WALK_VALID_FLAGS 0x00000001 + +#endif diff --git a/KProcessHacker/main.c b/KProcessHacker/main.c index d4eef0fc45b1..c2696cdee21f 100644 --- a/KProcessHacker/main.c +++ b/KProcessHacker/main.c @@ -1,356 +1,358 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -DRIVER_INITIALIZE DriverEntry; -DRIVER_UNLOAD DriverUnload; -__drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH KphDispatchCreate; -__drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH KphDispatchClose; - -ULONG KphpReadIntegerParameter( - __in_opt HANDLE KeyHandle, - __in PUNICODE_STRING ValueName, - __in ULONG DefaultValue - ); - -NTSTATUS KphpReadDriverParameters( - __in PUNICODE_STRING RegistryPath - ); - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, DriverEntry) -#pragma alloc_text(PAGE, DriverUnload) -#pragma alloc_text(PAGE, KphpReadIntegerParameter) -#pragma alloc_text(PAGE, KphpReadDriverParameters) -#pragma alloc_text(PAGE, KpiGetFeatures) -#endif - -PDRIVER_OBJECT KphDriverObject; -PDEVICE_OBJECT KphDeviceObject; -ULONG KphFeatures; -KPH_PARAMETERS KphParameters; - -NTSTATUS DriverEntry( - __in PDRIVER_OBJECT DriverObject, - __in PUNICODE_STRING RegistryPath - ) -{ - NTSTATUS status; - UNICODE_STRING deviceName; - PDEVICE_OBJECT deviceObject; - - PAGED_CODE(); - - KphDriverObject = DriverObject; - - if (!NT_SUCCESS(status = KphDynamicDataInitialization())) - return status; - - KphDynamicImport(); - - if (!NT_SUCCESS(status = KphpReadDriverParameters(RegistryPath))) - return status; - - // Create the device. - - RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME); - - status = IoCreateDevice( - DriverObject, - 0, - &deviceName, - FILE_DEVICE_UNKNOWN, - FILE_DEVICE_SECURE_OPEN, - FALSE, - &deviceObject - ); - - if (!NT_SUCCESS(status)) - return status; - - KphDeviceObject = deviceObject; - - // Set up I/O. - - DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = KphDispatchClose; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl; - DriverObject->DriverUnload = DriverUnload; - - deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - - dprintf("Driver loaded\n"); - - return status; -} - -VOID DriverUnload( - __in PDRIVER_OBJECT DriverObject - ) -{ - PAGED_CODE(); - - IoDeleteDevice(KphDeviceObject); - - dprintf("Driver unloaded\n"); -} - -NTSTATUS KphDispatchCreate( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PIO_STACK_LOCATION stackLocation; - PFILE_OBJECT fileObject; - PIO_SECURITY_CONTEXT securityContext; - PKPH_CLIENT client; - - stackLocation = IoGetCurrentIrpStackLocation(Irp); - fileObject = stackLocation->FileObject; - securityContext = stackLocation->Parameters.Create.SecurityContext; - - dprintf("Client (PID %Iu) is connecting\n", PsGetCurrentProcessId()); - - if (KphParameters.SecurityLevel == KphSecurityPrivilegeCheck || - KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck) - { - UCHAR requiredPrivilegesBuffer[FIELD_OFFSET(PRIVILEGE_SET, Privilege) + sizeof(LUID_AND_ATTRIBUTES)]; - PPRIVILEGE_SET requiredPrivileges; - - // Check for SeDebugPrivilege. - - requiredPrivileges = (PPRIVILEGE_SET)requiredPrivilegesBuffer; - requiredPrivileges->PrivilegeCount = 1; - requiredPrivileges->Control = PRIVILEGE_SET_ALL_NECESSARY; - requiredPrivileges->Privilege[0].Luid.LowPart = SE_DEBUG_PRIVILEGE; - requiredPrivileges->Privilege[0].Luid.HighPart = 0; - requiredPrivileges->Privilege[0].Attributes = 0; - - if (!SePrivilegeCheck( - requiredPrivileges, - &securityContext->AccessState->SubjectSecurityContext, - Irp->RequestorMode - )) - { - status = STATUS_PRIVILEGE_NOT_HELD; - dprintf("Client (PID %Iu) was rejected\n", PsGetCurrentProcessId()); - } - } - - if (NT_SUCCESS(status)) - { - client = ExAllocatePoolWithTag(PagedPool, sizeof(KPH_CLIENT), 'ChpK'); - - if (client) - { - memset(client, 0, sizeof(KPH_CLIENT)); - - ExInitializeFastMutex(&client->StateMutex); - ExInitializeFastMutex(&client->KeyBackoffMutex); - - fileObject->FsContext = client; - } - else - { - dprintf("Unable to allocate memory for client (PID %Iu)\n", PsGetCurrentProcessId()); - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; -} - -NTSTATUS KphDispatchClose( - __in PDEVICE_OBJECT DeviceObject, - __in PIRP Irp - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PIO_STACK_LOCATION stackLocation; - PFILE_OBJECT fileObject; - PKPH_CLIENT client; - - stackLocation = IoGetCurrentIrpStackLocation(Irp); - fileObject = stackLocation->FileObject; - client = fileObject->FsContext; - - if (client) - { - ExFreePoolWithTag(client, 'ChpK'); - } - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; -} - -/** - * Reads an integer (REG_DWORD) parameter from the registry. - * - * \param KeyHandle A handle to the Parameters key. If NULL, the function - * fails immediately and returns \a DefaultValue. - * \param ValueName The name of the parameter. - * \param DefaultValue The value that is returned if the function fails - * to retrieve the parameter from the registry. - * - * \return The parameter value, or \a DefaultValue if the function failed. - */ -ULONG KphpReadIntegerParameter( - __in_opt HANDLE KeyHandle, - __in PUNICODE_STRING ValueName, - __in ULONG DefaultValue - ) -{ - NTSTATUS status; - UCHAR buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)]; - PKEY_VALUE_PARTIAL_INFORMATION info; - ULONG resultLength; - - PAGED_CODE(); - - if (!KeyHandle) - return DefaultValue; - - info = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; - - status = ZwQueryValueKey( - KeyHandle, - ValueName, - KeyValuePartialInformation, - info, - sizeof(buffer), - &resultLength - ); - - if (info->Type != REG_DWORD) - status = STATUS_OBJECT_TYPE_MISMATCH; - - if (!NT_SUCCESS(status)) - { - dprintf("Unable to query parameter %.*S: 0x%x\n", ValueName->Length / sizeof(WCHAR), ValueName->Buffer, status); - return DefaultValue; - } - - return *(PULONG)info->Data; -} - -/** - * Reads the driver parameters. - * - * \param RegistryPath The registry path of the driver. - */ -NTSTATUS KphpReadDriverParameters( - __in PUNICODE_STRING RegistryPath - ) -{ - NTSTATUS status; - HANDLE parametersKeyHandle; - UNICODE_STRING parametersString; - UNICODE_STRING parametersKeyName; - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING valueName; - - PAGED_CODE(); - - // Open the Parameters key. - - RtlInitUnicodeString(¶metersString, L"\\Parameters"); - - parametersKeyName.Length = RegistryPath->Length + parametersString.Length; - parametersKeyName.MaximumLength = parametersKeyName.Length; - parametersKeyName.Buffer = ExAllocatePoolWithTag(PagedPool, parametersKeyName.MaximumLength, 'ThpK'); - - if (!parametersKeyName.Buffer) - return STATUS_INSUFFICIENT_RESOURCES; - - memcpy(parametersKeyName.Buffer, RegistryPath->Buffer, RegistryPath->Length); - memcpy(¶metersKeyName.Buffer[RegistryPath->Length / sizeof(WCHAR)], parametersString.Buffer, parametersString.Length); - - InitializeObjectAttributes( - &objectAttributes, - ¶metersKeyName, - OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, - NULL, - NULL - ); - status = ZwOpenKey( - ¶metersKeyHandle, - KEY_READ, - &objectAttributes - ); - ExFreePoolWithTag(parametersKeyName.Buffer, 'ThpK'); - - if (!NT_SUCCESS(status)) - { - dprintf("Unable to open Parameters key: 0x%x\n", status); - status = STATUS_SUCCESS; - parametersKeyHandle = NULL; - // Continue so we can set up defaults. - } - - // Read in the parameters. - - RtlInitUnicodeString(&valueName, L"SecurityLevel"); - KphParameters.SecurityLevel = KphpReadIntegerParameter(parametersKeyHandle, &valueName, KphSecurityPrivilegeCheck); - - KphReadDynamicDataParameters(parametersKeyHandle); - - if (parametersKeyHandle) - ZwClose(parametersKeyHandle); - - return status; -} - -NTSTATUS KpiGetFeatures( - __out PULONG Features, - __in KPROCESSOR_MODE AccessMode - ) -{ - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(Features, sizeof(ULONG), sizeof(ULONG)); - *Features = KphFeatures; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else - { - *Features = KphFeatures; - } - - return STATUS_SUCCESS; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD DriverUnload; +_Dispatch_type_(IRP_MJ_CREATE) DRIVER_DISPATCH KphDispatchCreate; +_Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH KphDispatchClose; + +ULONG KphpReadIntegerParameter( + _In_opt_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ ULONG DefaultValue + ); + +NTSTATUS KphpReadDriverParameters( + _In_ PUNICODE_STRING RegistryPath + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, DriverEntry) +#pragma alloc_text(PAGE, DriverUnload) +#pragma alloc_text(PAGE, KphpReadIntegerParameter) +#pragma alloc_text(PAGE, KphpReadDriverParameters) +#pragma alloc_text(PAGE, KpiGetFeatures) +#endif + +PDRIVER_OBJECT KphDriverObject; +PDEVICE_OBJECT KphDeviceObject; +ULONG KphFeatures; +KPH_PARAMETERS KphParameters; + +NTSTATUS DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status; + UNICODE_STRING deviceName; + PDEVICE_OBJECT deviceObject; + + PAGED_CODE(); + + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + + KphDriverObject = DriverObject; + + if (!NT_SUCCESS(status = KphDynamicDataInitialization())) + return status; + + KphDynamicImport(); + + if (!NT_SUCCESS(status = KphpReadDriverParameters(RegistryPath))) + return status; + + // Create the device. + + RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME); + + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &deviceObject + ); + + if (!NT_SUCCESS(status)) + return status; + + KphDeviceObject = deviceObject; + + // Set up I/O. + + DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = KphDispatchClose; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl; + DriverObject->DriverUnload = DriverUnload; + + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + dprintf("Driver loaded\n"); + + return status; +} + +VOID DriverUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +{ + PAGED_CODE(); + + IoDeleteDevice(KphDeviceObject); + + dprintf("Driver unloaded\n"); +} + +NTSTATUS KphDispatchCreate( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION stackLocation; + PFILE_OBJECT fileObject; + PIO_SECURITY_CONTEXT securityContext; + PKPH_CLIENT client; + + stackLocation = IoGetCurrentIrpStackLocation(Irp); + fileObject = stackLocation->FileObject; + securityContext = stackLocation->Parameters.Create.SecurityContext; + + dprintf("Client (PID %Iu) is connecting\n", PsGetCurrentProcessId()); + + if (KphParameters.SecurityLevel == KphSecurityPrivilegeCheck || + KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck) + { + UCHAR requiredPrivilegesBuffer[FIELD_OFFSET(PRIVILEGE_SET, Privilege) + sizeof(LUID_AND_ATTRIBUTES)]; + PPRIVILEGE_SET requiredPrivileges; + + // Check for SeDebugPrivilege. + + requiredPrivileges = (PPRIVILEGE_SET)requiredPrivilegesBuffer; + requiredPrivileges->PrivilegeCount = 1; + requiredPrivileges->Control = PRIVILEGE_SET_ALL_NECESSARY; + requiredPrivileges->Privilege[0].Luid.LowPart = SE_DEBUG_PRIVILEGE; + requiredPrivileges->Privilege[0].Luid.HighPart = 0; + requiredPrivileges->Privilege[0].Attributes = 0; + + if (!SePrivilegeCheck( + requiredPrivileges, + &securityContext->AccessState->SubjectSecurityContext, + Irp->RequestorMode + )) + { + status = STATUS_PRIVILEGE_NOT_HELD; + dprintf("Client (PID %Iu) was rejected\n", PsGetCurrentProcessId()); + } + } + + if (NT_SUCCESS(status)) + { + client = ExAllocatePoolWithTag(PagedPool, sizeof(KPH_CLIENT), 'ChpK'); + + if (client) + { + memset(client, 0, sizeof(KPH_CLIENT)); + + ExInitializeFastMutex(&client->StateMutex); + ExInitializeFastMutex(&client->KeyBackoffMutex); + + fileObject->FsContext = client; + } + else + { + dprintf("Unable to allocate memory for client (PID %Iu)\n", PsGetCurrentProcessId()); + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS KphDispatchClose( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION stackLocation; + PFILE_OBJECT fileObject; + PKPH_CLIENT client; + + stackLocation = IoGetCurrentIrpStackLocation(Irp); + fileObject = stackLocation->FileObject; + client = fileObject->FsContext; + + if (client) + { + ExFreePoolWithTag(client, 'ChpK'); + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +/** + * Reads an integer (REG_DWORD) parameter from the registry. + * + * \param KeyHandle A handle to the Parameters key. If NULL, the function + * fails immediately and returns \a DefaultValue. + * \param ValueName The name of the parameter. + * \param DefaultValue The value that is returned if the function fails + * to retrieve the parameter from the registry. + * + * \return The parameter value, or \a DefaultValue if the function failed. + */ +ULONG KphpReadIntegerParameter( + _In_opt_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ ULONG DefaultValue + ) +{ + NTSTATUS status; + UCHAR buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION info; + ULONG resultLength; + + PAGED_CODE(); + + if (!KeyHandle) + return DefaultValue; + + info = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; + + status = ZwQueryValueKey( + KeyHandle, + ValueName, + KeyValuePartialInformation, + info, + sizeof(buffer), + &resultLength + ); + + if (info->Type != REG_DWORD) + status = STATUS_OBJECT_TYPE_MISMATCH; + + if (!NT_SUCCESS(status)) + { + dprintf("Unable to query parameter %.*S: 0x%x\n", ValueName->Length / sizeof(WCHAR), ValueName->Buffer, status); + return DefaultValue; + } + + return *(PULONG)info->Data; +} + +/** + * Reads the driver parameters. + * + * \param RegistryPath The registry path of the driver. + */ +NTSTATUS KphpReadDriverParameters( + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status; + HANDLE parametersKeyHandle; + UNICODE_STRING parametersString; + UNICODE_STRING parametersKeyName; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING valueName; + + PAGED_CODE(); + + // Open the Parameters key. + + RtlInitUnicodeString(¶metersString, L"\\Parameters"); + + parametersKeyName.Length = RegistryPath->Length + parametersString.Length; + parametersKeyName.MaximumLength = parametersKeyName.Length; + parametersKeyName.Buffer = ExAllocatePoolWithTag(PagedPool, parametersKeyName.MaximumLength, 'ThpK'); + + if (!parametersKeyName.Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + memcpy(parametersKeyName.Buffer, RegistryPath->Buffer, RegistryPath->Length); + memcpy(¶metersKeyName.Buffer[RegistryPath->Length / sizeof(WCHAR)], parametersString.Buffer, parametersString.Length); + + InitializeObjectAttributes( + &objectAttributes, + ¶metersKeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + status = ZwOpenKey( + ¶metersKeyHandle, + KEY_READ, + &objectAttributes + ); + ExFreePoolWithTag(parametersKeyName.Buffer, 'ThpK'); + + if (!NT_SUCCESS(status)) + { + dprintf("Unable to open Parameters key: 0x%x\n", status); + status = STATUS_SUCCESS; + parametersKeyHandle = NULL; + // Continue so we can set up defaults. + } + + // Read in the parameters. + + RtlInitUnicodeString(&valueName, L"SecurityLevel"); + KphParameters.SecurityLevel = KphpReadIntegerParameter(parametersKeyHandle, &valueName, KphSecurityPrivilegeCheck); + + KphReadDynamicDataParameters(parametersKeyHandle); + + if (parametersKeyHandle) + ZwClose(parametersKeyHandle); + + return status; +} + +NTSTATUS KpiGetFeatures( + _Out_ PULONG Features, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(Features, sizeof(ULONG), sizeof(ULONG)); + *Features = KphFeatures; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + *Features = KphFeatures; + } + + return STATUS_SUCCESS; +} diff --git a/KProcessHacker/object.c b/KProcessHacker/object.c index 65e30548cfb3..704fd90dea58 100644 --- a/KProcessHacker/object.c +++ b/KProcessHacker/object.c @@ -1,1293 +1,1293 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#ifdef _X86_ -#define KERNEL_HANDLE_BIT (0x80000000) -#else -#define KERNEL_HANDLE_BIT (0xffffffff80000000) -#endif - -#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0) -#define MakeKernelHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_BIT)) - -typedef struct _KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT -{ - PVOID Buffer; - PVOID BufferLimit; - PVOID CurrentEntry; - ULONG Count; - NTSTATUS Status; -} KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT, *PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT; - -BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context - ); - -BOOLEAN KphpEnumerateProcessHandlesEnumCallback( - __in PHANDLE_TABLE HandleTable, - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context - ); - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KphReferenceProcessHandleTable) -#pragma alloc_text(PAGE, KphDereferenceProcessHandleTable) -#pragma alloc_text(PAGE, KphUnlockHandleTableEntry) -#pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback61) -#pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback) -#pragma alloc_text(PAGE, KpiEnumerateProcessHandles) -#pragma alloc_text(PAGE, KphQueryNameObject) -#pragma alloc_text(PAGE, KphQueryNameFileObject) -#pragma alloc_text(PAGE, KpiQueryInformationObject) -#pragma alloc_text(PAGE, KpiSetInformationObject) -#pragma alloc_text(PAGE, KphOpenNamedObject) -#endif - -/** - * Gets a pointer to the handle table of a process. - * - * \param Process A process object. - * - * \return A pointer to the handle table, or NULL if the process is terminating or the request is - * not supported. You must call KphDereferenceProcessHandleTable() when the handle table is no - * longer needed. - */ -PHANDLE_TABLE KphReferenceProcessHandleTable( - __in PEPROCESS Process - ) -{ - PHANDLE_TABLE handleTable = NULL; - - PAGED_CODE(); - - // Fail if we don't have an offset. - if (KphDynEpObjectTable == -1) - return NULL; - - // Prevent the process from terminating and get its handle table. - if (NT_SUCCESS(PsAcquireProcessExitSynchronization(Process))) - { - handleTable = *(PHANDLE_TABLE *)((ULONG_PTR)Process + KphDynEpObjectTable); - - if (!handleTable) - PsReleaseProcessExitSynchronization(Process); - } - - return handleTable; -} - -/** - * Dereferences the handle table of a process. - * - * \param Process A process object. - */ -VOID KphDereferenceProcessHandleTable( - __in PEPROCESS Process - ) -{ - PAGED_CODE(); - - PsReleaseProcessExitSynchronization(Process); -} - -VOID KphUnlockHandleTableEntry( - __in PHANDLE_TABLE HandleTable, - __in PHANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - PEX_PUSH_LOCK handleContentionEvent; - - PAGED_CODE(); - - // Set the unlocked bit. - -#ifdef _M_X64 - InterlockedExchangeAdd64(&HandleTableEntry->Value, 1); -#else - InterlockedExchangeAdd(&HandleTableEntry->Value, 1); -#endif - - // Allow waiters to wake up. - - handleContentionEvent = (PEX_PUSH_LOCK)((ULONG_PTR)HandleTable + KphDynHtHandleContentionEvent); - - if (*(PULONG_PTR)handleContentionEvent != 0) - ExfUnblockPushLock(handleContentionEvent, NULL); -} - -BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context - ) -{ - PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context = Context; - KPH_PROCESS_HANDLE handleInfo; - POBJECT_HEADER objectHeader; - POBJECT_TYPE objectType; - PKPH_PROCESS_HANDLE entryInBuffer; - - PAGED_CODE(); - - objectHeader = ObpDecodeObject(HandleTableEntry->Object); - handleInfo.Handle = Handle; - handleInfo.Object = objectHeader ? &objectHeader->Body : NULL; - handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess); - handleInfo.ObjectTypeIndex = -1; - handleInfo.Reserved1 = 0; - handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry); - handleInfo.Reserved2 = 0; - - if (handleInfo.Object) - { - objectType = ObGetObjectType(handleInfo.Object); - - if (objectType && KphDynOtIndex != -1) - handleInfo.ObjectTypeIndex = (USHORT)*(PUCHAR)((ULONG_PTR)objectType + KphDynOtIndex); - } - - // Advance the current entry pointer regardless of whether the information will be written; this - // will allow the parent function to report the correct return length. - entryInBuffer = context->CurrentEntry; - context->CurrentEntry = (PVOID)((ULONG_PTR)context->CurrentEntry + sizeof(KPH_PROCESS_HANDLE)); - context->Count++; - - // Only write if we have not exceeded the buffer length. Also check for a potential overflow (if - // the process has an extremely large number of handles, the buffer pointer may wrap). - if ( - (ULONG_PTR)entryInBuffer >= (ULONG_PTR)context->Buffer && - (ULONG_PTR)entryInBuffer + sizeof(KPH_PROCESS_HANDLE) <= (ULONG_PTR)context->BufferLimit - ) - { - __try - { - *entryInBuffer = handleInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - // Report an error. - if (context->Status == STATUS_SUCCESS) - context->Status = GetExceptionCode(); - } - } - else - { - // Report that the buffer is too small. - if (context->Status == STATUS_SUCCESS) - context->Status = STATUS_BUFFER_TOO_SMALL; - } - - return FALSE; -} - -BOOLEAN KphpEnumerateProcessHandlesEnumCallback( - __in PHANDLE_TABLE HandleTable, - __inout PHANDLE_TABLE_ENTRY HandleTableEntry, - __in HANDLE Handle, - __in PVOID Context - ) -{ - BOOLEAN result; - - PAGED_CODE(); - - result = KphpEnumerateProcessHandlesEnumCallback61(HandleTableEntry, Handle, Context); - KphUnlockHandleTableEntry(HandleTable, HandleTableEntry); - - return result; -} - -/** - * Enumerates the handles of a process. - * - * \param ProcessHandle A handle to a process. - * \param Buffer The buffer in which the handle information will be stored. - * \param BufferLength The number of bytes available in \a Buffer. - * \param ReturnLength A variable which receives the number of bytes required to be available in - * \a Buffer. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiEnumerateProcessHandles( - __in HANDLE ProcessHandle, - __out_bcount(BufferLength) PVOID Buffer, - __in_opt ULONG BufferLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - BOOLEAN result; - PEPROCESS process; - PHANDLE_TABLE handleTable; - KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context; - - PAGED_CODE(); - - if (KphDynNtVersion >= PHNT_WIN8 && KphDynHtHandleContentionEvent == -1) - { - return STATUS_NOT_SUPPORTED; - } - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(Buffer, BufferLength, sizeof(ULONG)); - - if (ReturnLength) - ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - // Reference the process object. - status = ObReferenceObjectByHandle( - ProcessHandle, - 0, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Get its handle table. - handleTable = KphReferenceProcessHandleTable(process); - - if (!handleTable) - { - ObDereferenceObject(process); - return STATUS_UNSUCCESSFUL; - } - - // Initialize the enumeration context. - context.Buffer = Buffer; - context.BufferLimit = (PVOID)((ULONG_PTR)Buffer + BufferLength); - context.CurrentEntry = ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->Handles; - context.Count = 0; - context.Status = STATUS_SUCCESS; - - // Enumerate the handles. - - if (KphDynNtVersion >= PHNT_WIN8) - { - result = ExEnumHandleTable( - handleTable, - KphpEnumerateProcessHandlesEnumCallback, - &context, - NULL - ); - } - else - { - result = ExEnumHandleTable( - handleTable, - (PEX_ENUM_HANDLE_CALLBACK)KphpEnumerateProcessHandlesEnumCallback61, - &context, - NULL - ); - } - - KphDereferenceProcessHandleTable(process); - ObDereferenceObject(process); - - // Write the number of handles if we can. - if (BufferLength >= FIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles)) - { - if (AccessMode != KernelMode) - { - __try - { - ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else - { - ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count; - } - } - - // Supply the return length if the caller wanted it. - if (ReturnLength) - { - ULONG returnLength; - - // Note: if the CurrentEntry pointer wrapped, this will give the wrong return length. - returnLength = (ULONG)((ULONG_PTR)context.CurrentEntry - (ULONG_PTR)Buffer); - - if (AccessMode != KernelMode) - { - __try - { - *ReturnLength = returnLength; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else - { - *ReturnLength = returnLength; - } - } - - return context.Status; -} - -/** - * Queries the name of an object. - * - * \param Object A pointer to an object. - * \param Buffer The buffer in which the object name will be stored. - * \param BufferLength The number of bytes available in \a Buffer. - * \param ReturnLength A variable which receives the number of bytes required to be available in - * \a Buffer. - */ -NTSTATUS KphQueryNameObject( - __in PVOID Object, - __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength - ) -{ - NTSTATUS status; - POBJECT_TYPE objectType; - - PAGED_CODE(); - - objectType = ObGetObjectType(Object); - - // Check if we are going to hang when querying the object, and use - // the special file object query function if needed. - if (objectType == *IoFileObjectType && - (((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters)) - { - status = KphQueryNameFileObject(Object, Buffer, BufferLength, ReturnLength); - dprintf("KphQueryNameFileObject: status 0x%x\n", status); - } - else - { - status = ObQueryNameString(Object, Buffer, BufferLength, ReturnLength); - dprintf("ObQueryNameString: status 0x%x\n", status); - } - - // Make the error returns consistent. - if (status == STATUS_BUFFER_OVERFLOW) // returned by I/O subsystem - status = STATUS_BUFFER_TOO_SMALL; - if (status == STATUS_INFO_LENGTH_MISMATCH) // returned by ObQueryNameString - status = STATUS_BUFFER_TOO_SMALL; - - if (NT_SUCCESS(status)) - dprintf("KphQueryNameObject: %.*S\n", Buffer->Name.Length / sizeof(WCHAR), Buffer->Name.Buffer); - else - dprintf("KphQueryNameObject: status 0x%x\n", status); - - return status; -} - -/** - * Queries the name of a file object. - * - * \param FileObject A pointer to a file object. - * \param Buffer The buffer in which the object name will be stored. - * \param BufferLength The number of bytes available in \a Buffer. - * \param ReturnLength A variable which receives the number of bytes required to be available in - * \a Buffer. - */ -NTSTATUS KphQueryNameFileObject( - __in PFILE_OBJECT FileObject, - __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer, - __in ULONG BufferLength, - __out PULONG ReturnLength - ) -{ - NTSTATUS status = STATUS_SUCCESS; - ULONG returnLength; - PCHAR objectName; - ULONG usedLength; - ULONG subNameLength; - PFILE_OBJECT relatedFileObject; - - PAGED_CODE(); - - // We need at least the size of OBJECT_NAME_INFORMATION to continue. - if (BufferLength < sizeof(OBJECT_NAME_INFORMATION)) - { - *ReturnLength = sizeof(OBJECT_NAME_INFORMATION); - - return STATUS_BUFFER_TOO_SMALL; - } - - // Assume failure. - Buffer->Name.Length = 0; - // We will place the object name directly after the UNICODE_STRING structure in the buffer. - Buffer->Name.Buffer = (PWSTR)((ULONG_PTR)Buffer + sizeof(OBJECT_NAME_INFORMATION)); - // Retain a local pointer to the object name so we can manipulate the pointer. - objectName = (PCHAR)Buffer->Name.Buffer; - // A variable that keeps track of how much space we have used. - usedLength = sizeof(OBJECT_NAME_INFORMATION); - - // Check if the file object has an associated device (e.g. "\Device\NamedPipe", "\Device\Mup"). - // We can use the user-supplied buffer for this since if the buffer isn't big enough, we can't - // proceed anyway (we are going to use the name). - if (FileObject->DeviceObject) - { - status = ObQueryNameString( - FileObject->DeviceObject, - Buffer, - BufferLength, - &returnLength - ); - - if (!NT_SUCCESS(status)) - { - if (status == STATUS_INFO_LENGTH_MISMATCH) - status = STATUS_BUFFER_TOO_SMALL; - - *ReturnLength = returnLength; - - return status; - } - - // The UNICODE_STRING in the buffer is now filled in. We will append to the object name - // later, so we need to fix the object name pointer by adding the length, in bytes, of the - // device name string we just got. - objectName += Buffer->Name.Length; - usedLength += Buffer->Name.Length; - } - - // Check if the file object has a file name component. If not, we can't do anything else, so we - // just return the name we have already. - if (!FileObject->FileName.Buffer) - { - *ReturnLength = usedLength; - - return STATUS_SUCCESS; - } - - // The file object has a name. We need to walk up the file object chain and append the names of - // the related file objects in reverse order. This means we need to calculate the total length - // first. - - relatedFileObject = FileObject; - subNameLength = 0; - - do - { - subNameLength += relatedFileObject->FileName.Length; - - // Avoid infinite loops. - if (relatedFileObject == relatedFileObject->RelatedFileObject) - break; - - relatedFileObject = relatedFileObject->RelatedFileObject; - } while (relatedFileObject); - - usedLength += subNameLength; - - // Check if we have enough space to write the whole thing. - if (usedLength > BufferLength) - { - *ReturnLength = usedLength; - - return STATUS_BUFFER_TOO_SMALL; - } - - // We're ready to begin copying the names. - - // Add the name length because we're copying in reverse order. - objectName += subNameLength; - - relatedFileObject = FileObject; - - do - { - objectName -= relatedFileObject->FileName.Length; - memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length); - - // Avoid infinite loops. - if (relatedFileObject == relatedFileObject->RelatedFileObject) - break; - - relatedFileObject = relatedFileObject->RelatedFileObject; - } while (relatedFileObject); - - // Update the length. - Buffer->Name.Length += (USHORT)subNameLength; - - // Pass the return length back. - *ReturnLength = usedLength; - - return STATUS_SUCCESS; -} - -/** - * Queries object information. - * - * \param ProcessHandle A handle to a process. - * \param Handle A handle which is present in the process referenced by \a ProcessHandle. - * \param ObjectInformationClass The type of information to retrieve. - * \param ObjectInformation The buffer in which the information will be stored. - * \param ObjectInformationLength The number of bytes available in \a ObjectInformation. - * \param ReturnLength A variable which receives the number of bytes required to be available in - * \a ObjectInformation. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiQueryInformationObject( - __in HANDLE ProcessHandle, - __in HANDLE Handle, - __in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - __out_bcount(ObjectInformationLength) PVOID ObjectInformation, - __in ULONG ObjectInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - KPROCESSOR_MODE referenceMode; - KAPC_STATE apcState; - ULONG returnLength; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(ObjectInformation, ObjectInformationLength, sizeof(ULONG)); - - if (ReturnLength) - ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ProcessHandle, - 0, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - if (process == PsInitialSystemProcess) - { - // A check was added in Windows 7 - if we're attached to the System process, the handle must - // be a kernel handle. - Handle = MakeKernelHandle(Handle); - referenceMode = KernelMode; - } - else - { - // Make sure the handle isn't a kernel handle if we're not attached to the System process. - // This means we can avoid referencing then opening the objects later when calling - // ZwQueryObject, etc. - if (IsKernelHandle(Handle)) - { - ObDereferenceObject(process); - return STATUS_INVALID_HANDLE; - } - - referenceMode = AccessMode; - } - - switch (ObjectInformationClass) - { - case KphObjectBasicInformation: - { - OBJECT_BASIC_INFORMATION basicInfo; - - KeStackAttachProcess(process, &apcState); - status = ZwQueryObject( - Handle, - ObjectBasicInformation, - &basicInfo, - sizeof(OBJECT_BASIC_INFORMATION), - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - if (ObjectInformationLength == sizeof(OBJECT_BASIC_INFORMATION)) - { - __try - { - *(POBJECT_BASIC_INFORMATION)ObjectInformation = basicInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - - returnLength = sizeof(OBJECT_BASIC_INFORMATION); - } - break; - case KphObjectNameInformation: - { - PVOID object; - ULONG allocateSize; - POBJECT_NAME_INFORMATION nameInfo; - - returnLength = sizeof(OBJECT_NAME_INFORMATION); - - // Attach to the process a get a pointer to the object. - KeStackAttachProcess(process, &apcState); - status = ObReferenceObjectByHandle( - Handle, - 0, - NULL, - referenceMode, - &object, - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - allocateSize = ObjectInformationLength; - - if (allocateSize < sizeof(OBJECT_NAME_INFORMATION)) // make sure we never try to allocate 0 bytes - allocateSize = sizeof(OBJECT_NAME_INFORMATION); - - nameInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK'); - - if (nameInfo) - { - // Make sure we don't leak any data. - memset(nameInfo, 0, ObjectInformationLength); - - status = KphQueryNameObject( - object, - nameInfo, - ObjectInformationLength, - &returnLength - ); - dprintf("KpiQueryInformationObject: called KphQueryNameObject: Handle: 0x%Ix, ObjectInformationLength: %u, returnLength: %u\n", - Handle, ObjectInformationLength, returnLength); - - if (NT_SUCCESS(status)) - { - // Fix up the buffer pointer. - if (nameInfo->Name.Buffer) - nameInfo->Name.Buffer = (PVOID)((ULONG_PTR)nameInfo->Name.Buffer - (ULONG_PTR)nameInfo + (ULONG_PTR)ObjectInformation); - - __try - { - memcpy(ObjectInformation, nameInfo, ObjectInformationLength); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - - ExFreePoolWithTag(nameInfo, 'QhpK'); - } - else - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - - ObDereferenceObject(object); - } - } - break; - case KphObjectTypeInformation: - { - ULONG allocateSize; - POBJECT_TYPE_INFORMATION typeInfo; - - returnLength = sizeof(OBJECT_TYPE_INFORMATION); - allocateSize = ObjectInformationLength; - - if (allocateSize < sizeof(OBJECT_TYPE_INFORMATION)) - allocateSize = sizeof(OBJECT_TYPE_INFORMATION); - - // ObQueryTypeInfo uses ObjectType->Name.MaximumLength instead of - // ObjectType->Name.Length + sizeof(WCHAR) to calculate the required buffer size. In - // Windows 8, certain object types (e.g. TmTx) do NOT include the null terminator in - // MaximumLength, which causes ObQueryTypeInfo to overrun the given buffer. To work - // around this bug, we add some (generous) padding to our allocation. - allocateSize += sizeof(ULONGLONG); - - typeInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK'); - - if (typeInfo) - { - memset(typeInfo, 0, ObjectInformationLength); - - KeStackAttachProcess(process, &apcState); - status = ZwQueryObject( - Handle, - ObjectTypeInformation, - typeInfo, - ObjectInformationLength, - &returnLength - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - // Fix up the buffer pointer. - if (typeInfo->TypeName.Buffer) - typeInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)typeInfo->TypeName.Buffer - (ULONG_PTR)typeInfo + (ULONG_PTR)ObjectInformation); - - __try - { - memcpy(ObjectInformation, typeInfo, ObjectInformationLength); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - - ExFreePoolWithTag(typeInfo, 'QhpK'); - } - else - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - break; - case KphObjectHandleFlagInformation: - { - OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; - - KeStackAttachProcess(process, &apcState); - status = ZwQueryObject( - Handle, - ObjectHandleFlagInformation, - &handleFlagInfo, - sizeof(OBJECT_HANDLE_FLAG_INFORMATION), - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION)) - { - __try - { - *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation = handleFlagInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - - returnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION); - } - break; - case KphObjectProcessBasicInformation: - { - PROCESS_BASIC_INFORMATION basicInfo; - - KeStackAttachProcess(process, &apcState); - status = ZwQueryInformationProcess( - Handle, - ProcessBasicInformation, - &basicInfo, - sizeof(PROCESS_BASIC_INFORMATION), - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - if (ObjectInformationLength == sizeof(PROCESS_BASIC_INFORMATION)) - { - __try - { - *(PPROCESS_BASIC_INFORMATION)ObjectInformation = basicInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - - returnLength = sizeof(PROCESS_BASIC_INFORMATION); - } - break; - case KphObjectThreadBasicInformation: - { - THREAD_BASIC_INFORMATION basicInfo; - - KeStackAttachProcess(process, &apcState); - status = ZwQueryInformationThread( - Handle, - ThreadBasicInformation, - &basicInfo, - sizeof(THREAD_BASIC_INFORMATION), - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - if (ObjectInformationLength == sizeof(THREAD_BASIC_INFORMATION)) - { - __try - { - *(PTHREAD_BASIC_INFORMATION)ObjectInformation = basicInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - - returnLength = sizeof(THREAD_BASIC_INFORMATION); - } - break; - case KphObjectEtwRegBasicInformation: - { - PVOID etwReg; - PVOID objectType; - PUNICODE_STRING objectTypeName; - UNICODE_STRING etwRegistrationName; - PVOID guidEntry; - ETWREG_BASIC_INFORMATION basicInfo; - - // Check dynamic data requirements. - if (KphDynEgeGuid != -1 && - KphDynEreGuidEntry != -1 && - KphDynOtName != -1) - { - // Attach to the process and get a pointer to the object. We don't have a pointer to - // the EtwRegistration object type, so we'll just have to check the type name. - - KeStackAttachProcess(process, &apcState); - status = ObReferenceObjectByHandle( - Handle, - 0, - NULL, - referenceMode, - &etwReg, - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - // Check the type name. - - objectType = ObGetObjectType(etwReg); - - if (objectType) - { - objectTypeName = (PUNICODE_STRING)((ULONG_PTR)objectType + KphDynOtName); - RtlInitUnicodeString(&etwRegistrationName, L"EtwRegistration"); - - if (!RtlEqualUnicodeString(objectTypeName, &etwRegistrationName, FALSE)) - { - status = STATUS_OBJECT_TYPE_MISMATCH; - } - } - else - { - status = STATUS_NOT_SUPPORTED; - } - - if (NT_SUCCESS(status)) - { - guidEntry = *(PVOID *)((ULONG_PTR)etwReg + KphDynEreGuidEntry); - - if (guidEntry) - basicInfo.Guid = *(GUID *)((ULONG_PTR)guidEntry + KphDynEgeGuid); - else - memset(&basicInfo.Guid, 0, sizeof(GUID)); - - basicInfo.SessionId = 0; // not implemented - - if (ObjectInformationLength == sizeof(ETWREG_BASIC_INFORMATION)) - { - __try - { - *(PETWREG_BASIC_INFORMATION)ObjectInformation = basicInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - - ObDereferenceObject(etwReg); - } - } - else - { - status = STATUS_NOT_SUPPORTED; - } - - returnLength = sizeof(ETWREG_BASIC_INFORMATION); - } - break; - case KphObjectFileObjectInformation: - { - PFILE_OBJECT fileObject; - KPH_FILE_OBJECT_INFORMATION objectInfo; - - KeStackAttachProcess(process, &apcState); - status = ObReferenceObjectByHandle( - Handle, - 0, - *IoFileObjectType, - referenceMode, - &fileObject, - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - objectInfo.LockOperation = fileObject->LockOperation; - objectInfo.DeletePending = fileObject->DeletePending; - objectInfo.ReadAccess = fileObject->ReadAccess; - objectInfo.WriteAccess = fileObject->WriteAccess; - objectInfo.DeleteAccess = fileObject->DeleteAccess; - objectInfo.SharedRead = fileObject->SharedRead; - objectInfo.SharedWrite = fileObject->SharedWrite; - objectInfo.SharedDelete = fileObject->SharedDelete; - objectInfo.CurrentByteOffset = fileObject->CurrentByteOffset; - objectInfo.Flags = fileObject->Flags; - - if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_INFORMATION)) - { - __try - { - *(PKPH_FILE_OBJECT_INFORMATION)ObjectInformation = objectInfo; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - - ObDereferenceObject(fileObject); - } - - returnLength = sizeof(KPH_FILE_OBJECT_INFORMATION); - } - break; - case KphObjectFileObjectDriver: - { - PFILE_OBJECT fileObject; - HANDLE driverHandle; - - if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_DRIVER)) - { - KeStackAttachProcess(process, &apcState); - status = ObReferenceObjectByHandle( - Handle, - 0, - *IoFileObjectType, - referenceMode, - &fileObject, - NULL - ); - KeUnstackDetachProcess(&apcState); - - if (NT_SUCCESS(status)) - { - if (fileObject->DeviceObject && fileObject->DeviceObject->DriverObject) - { - status = ObOpenObjectByPointer( - fileObject->DeviceObject->DriverObject, - 0, - NULL, - SYNCHRONIZE, - *IoDriverObjectType, - AccessMode, - &driverHandle - ); - } - else - { - driverHandle = NULL; - } - - if (NT_SUCCESS(status)) - { - __try - { - ((PKPH_FILE_OBJECT_DRIVER)ObjectInformation)->DriverHandle = driverHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - - ObDereferenceObject(fileObject); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - break; - default: - status = STATUS_INVALID_INFO_CLASS; - returnLength = 0; - break; - } - - ObDereferenceObject(process); - - if (ReturnLength) - { - if (AccessMode != KernelMode) - { - __try - { - *ReturnLength = returnLength; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - NOTHING; - } - } - else - { - *ReturnLength = returnLength; - } - } - - return status; -} - -/** - * Sets object information. - * - * \param ProcessHandle A handle to a process. - * \param Handle A handle which is present in the process referenced by \a ProcessHandle. - * \param ObjectInformationClass The type of information to set. - * \param ObjectInformation A buffer which contains the information to set. - * \param ObjectInformationLength The number of bytes present in \a ObjectInformation. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiSetInformationObject( - __in HANDLE ProcessHandle, - __in HANDLE Handle, - __in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - __in_bcount(ObjectInformationLength) PVOID ObjectInformation, - __in ULONG ObjectInformationLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - KAPC_STATE apcState; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - ULONG alignment; - - switch (ObjectInformationClass) - { - case KphObjectHandleFlagInformation: - alignment = sizeof(BOOLEAN); - break; - default: - alignment = sizeof(ULONG); - break; - } - - __try - { - ProbeForRead(ObjectInformation, ObjectInformationLength, alignment); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ProcessHandle, - 0, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - if (process == PsInitialSystemProcess) - { - Handle = MakeKernelHandle(Handle); - } - else - { - if (IsKernelHandle(Handle)) - { - ObDereferenceObject(process); - return STATUS_INVALID_HANDLE; - } - } - - switch (ObjectInformationClass) - { - case KphObjectHandleFlagInformation: - { - OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; - - if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION)) - { - __try - { - handleFlagInfo = *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - - if (NT_SUCCESS(status)) - { - KeStackAttachProcess(process, &apcState); - status = ObSetHandleAttributes(Handle, &handleFlagInfo, KernelMode); - KeUnstackDetachProcess(&apcState); - } - } - break; - default: - status = STATUS_INVALID_INFO_CLASS; - break; - } - - ObDereferenceObject(process); - - return status; -} - -NTSTATUS KphOpenNamedObject( - __out PHANDLE ObjectHandle, - __in ACCESS_MASK DesiredAccess, - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in POBJECT_TYPE ObjectType, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - HANDLE objectHandle; - - PAGED_CODE(); - - // Open the object. - status = ObOpenObjectByName( - ObjectAttributes, - ObjectType, - AccessMode, - NULL, - DesiredAccess, - NULL, - &objectHandle - ); - - // Pass the handle back. - if (AccessMode != KernelMode) - { - __try - { - *ObjectHandle = objectHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *ObjectHandle = objectHandle; - } - - return status; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#ifdef _X86_ +#define KERNEL_HANDLE_BIT (0x80000000) +#else +#define KERNEL_HANDLE_BIT (0xffffffff80000000) +#endif + +#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0) +#define MakeKernelHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_BIT)) + +typedef struct _KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT +{ + PVOID Buffer; + PVOID BufferLimit; + PVOID CurrentEntry; + ULONG Count; + NTSTATUS Status; +} KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT, *PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT; + +BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context + ); + +BOOLEAN KphpEnumerateProcessHandlesEnumCallback( + _In_ PHANDLE_TABLE HandleTable, + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphReferenceProcessHandleTable) +#pragma alloc_text(PAGE, KphDereferenceProcessHandleTable) +#pragma alloc_text(PAGE, KphUnlockHandleTableEntry) +#pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback61) +#pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback) +#pragma alloc_text(PAGE, KpiEnumerateProcessHandles) +#pragma alloc_text(PAGE, KphQueryNameObject) +#pragma alloc_text(PAGE, KphQueryNameFileObject) +#pragma alloc_text(PAGE, KpiQueryInformationObject) +#pragma alloc_text(PAGE, KpiSetInformationObject) +#pragma alloc_text(PAGE, KphOpenNamedObject) +#endif + +/** + * Gets a pointer to the handle table of a process. + * + * \param Process A process object. + * + * \return A pointer to the handle table, or NULL if the process is terminating or the request is + * not supported. You must call KphDereferenceProcessHandleTable() when the handle table is no + * longer needed. + */ +PHANDLE_TABLE KphReferenceProcessHandleTable( + _In_ PEPROCESS Process + ) +{ + PHANDLE_TABLE handleTable = NULL; + + PAGED_CODE(); + + // Fail if we don't have an offset. + if (KphDynEpObjectTable == -1) + return NULL; + + // Prevent the process from terminating and get its handle table. + if (NT_SUCCESS(PsAcquireProcessExitSynchronization(Process))) + { + handleTable = *(PHANDLE_TABLE *)PTR_ADD_OFFSET(Process, KphDynEpObjectTable); + + if (!handleTable) + PsReleaseProcessExitSynchronization(Process); + } + + return handleTable; +} + +/** + * Dereferences the handle table of a process. + * + * \param Process A process object. + */ +VOID KphDereferenceProcessHandleTable( + _In_ PEPROCESS Process + ) +{ + PAGED_CODE(); + + PsReleaseProcessExitSynchronization(Process); +} + +VOID KphUnlockHandleTableEntry( + _In_ PHANDLE_TABLE HandleTable, + _In_ PHANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + PEX_PUSH_LOCK handleContentionEvent; + + PAGED_CODE(); + + // Set the unlocked bit. + +#ifdef _M_X64 + InterlockedExchangeAdd64(&HandleTableEntry->Value, 1); +#else + InterlockedExchangeAdd(&HandleTableEntry->Value, 1); +#endif + + // Allow waiters to wake up. + + handleContentionEvent = (PEX_PUSH_LOCK)PTR_ADD_OFFSET(HandleTable, KphDynHtHandleContentionEvent); + + if (*(PULONG_PTR)handleContentionEvent != 0) + ExfUnblockPushLock(handleContentionEvent, NULL); +} + +BOOLEAN KphpEnumerateProcessHandlesEnumCallback61( + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context + ) +{ + PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context = Context; + KPH_PROCESS_HANDLE handleInfo; + POBJECT_HEADER objectHeader; + POBJECT_TYPE objectType; + PKPH_PROCESS_HANDLE entryInBuffer; + + PAGED_CODE(); + + objectHeader = ObpDecodeObject(HandleTableEntry->Object); + handleInfo.Handle = Handle; + handleInfo.Object = objectHeader ? &objectHeader->Body : NULL; + handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess); + handleInfo.ObjectTypeIndex = -1; + handleInfo.Reserved1 = 0; + handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry); + handleInfo.Reserved2 = 0; + + if (handleInfo.Object) + { + objectType = ObGetObjectType(handleInfo.Object); + + if (objectType && KphDynOtIndex != -1) + handleInfo.ObjectTypeIndex = (USHORT)*(PUCHAR)PTR_ADD_OFFSET(objectType, KphDynOtIndex); + } + + // Advance the current entry pointer regardless of whether the information will be written; this + // will allow the parent function to report the correct return length. + entryInBuffer = context->CurrentEntry; + context->CurrentEntry = PTR_ADD_OFFSET(context->CurrentEntry, sizeof(KPH_PROCESS_HANDLE)); + context->Count++; + + // Only write if we have not exceeded the buffer length. Also check for a potential overflow (if + // the process has an extremely large number of handles, the buffer pointer may wrap). + if ( + (ULONG_PTR)entryInBuffer >= (ULONG_PTR)context->Buffer && + (ULONG_PTR)entryInBuffer + sizeof(KPH_PROCESS_HANDLE) <= (ULONG_PTR)context->BufferLimit + ) + { + __try + { + *entryInBuffer = handleInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // Report an error. + if (context->Status == STATUS_SUCCESS) + context->Status = GetExceptionCode(); + } + } + else + { + // Report that the buffer is too small. + if (context->Status == STATUS_SUCCESS) + context->Status = STATUS_BUFFER_TOO_SMALL; + } + + return FALSE; +} + +BOOLEAN KphpEnumerateProcessHandlesEnumCallback( + _In_ PHANDLE_TABLE HandleTable, + _Inout_ PHANDLE_TABLE_ENTRY HandleTableEntry, + _In_ HANDLE Handle, + _In_ PVOID Context + ) +{ + BOOLEAN result; + + PAGED_CODE(); + + result = KphpEnumerateProcessHandlesEnumCallback61(HandleTableEntry, Handle, Context); + KphUnlockHandleTableEntry(HandleTable, HandleTableEntry); + + return result; +} + +/** + * Enumerates the handles of a process. + * + * \param ProcessHandle A handle to a process. + * \param Buffer The buffer in which the handle information will be stored. + * \param BufferLength The number of bytes available in \a Buffer. + * \param ReturnLength A variable which receives the number of bytes required to be available in + * \a Buffer. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiEnumerateProcessHandles( + _In_ HANDLE ProcessHandle, + _Out_writes_bytes_(BufferLength) PVOID Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + BOOLEAN result; + PEPROCESS process; + PHANDLE_TABLE handleTable; + KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context; + + PAGED_CODE(); + + if (KphDynNtVersion >= PHNT_WIN8 && KphDynHtHandleContentionEvent == -1) + { + return STATUS_NOT_SUPPORTED; + } + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(Buffer, BufferLength, sizeof(ULONG)); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + // Reference the process object. + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Get its handle table. + handleTable = KphReferenceProcessHandleTable(process); + + if (!handleTable) + { + ObDereferenceObject(process); + return STATUS_UNSUCCESSFUL; + } + + // Initialize the enumeration context. + context.Buffer = Buffer; + context.BufferLimit = PTR_ADD_OFFSET(Buffer, BufferLength); + context.CurrentEntry = ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->Handles; + context.Count = 0; + context.Status = STATUS_SUCCESS; + + // Enumerate the handles. + + if (KphDynNtVersion >= PHNT_WIN8) + { + result = ExEnumHandleTable( + handleTable, + KphpEnumerateProcessHandlesEnumCallback, + &context, + NULL + ); + } + else + { + result = ExEnumHandleTable( + handleTable, + (PEX_ENUM_HANDLE_CALLBACK)KphpEnumerateProcessHandlesEnumCallback61, + &context, + NULL + ); + } + + KphDereferenceProcessHandleTable(process); + ObDereferenceObject(process); + + // Write the number of handles if we can. + if (BufferLength >= UFIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles)) + { + if (AccessMode != KernelMode) + { + __try + { + ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count; + } + } + + // Supply the return length if the caller wanted it. + if (ReturnLength) + { + ULONG returnLength; + + // Note: if the CurrentEntry pointer wrapped, this will give the wrong return length. + returnLength = (ULONG)((ULONG_PTR)context.CurrentEntry - (ULONG_PTR)Buffer); + + if (AccessMode != KernelMode) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + *ReturnLength = returnLength; + } + } + + return context.Status; +} + +/** + * Queries the name of an object. + * + * \param Object A pointer to an object. + * \param Buffer The buffer in which the object name will be stored. + * \param BufferLength The number of bytes available in \a Buffer. + * \param ReturnLength A variable which receives the number of bytes required to be available in + * \a Buffer. + */ +NTSTATUS KphQueryNameObject( + _In_ PVOID Object, + _Out_writes_bytes_(BufferLength) POBJECT_NAME_INFORMATION Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG ReturnLength + ) +{ + NTSTATUS status; + POBJECT_TYPE objectType; + + PAGED_CODE(); + + objectType = ObGetObjectType(Object); + + // Check if we are going to hang when querying the object, and use + // the special file object query function if needed. + if (objectType == *IoFileObjectType && + (((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters)) + { + status = KphQueryNameFileObject(Object, Buffer, BufferLength, ReturnLength); + dprintf("KphQueryNameFileObject: status 0x%x\n", status); + } + else + { + status = ObQueryNameString(Object, Buffer, BufferLength, ReturnLength); + dprintf("ObQueryNameString: status 0x%x\n", status); + } + + // Make the error returns consistent. + if (status == STATUS_BUFFER_OVERFLOW) // returned by I/O subsystem + status = STATUS_BUFFER_TOO_SMALL; + if (status == STATUS_INFO_LENGTH_MISMATCH) // returned by ObQueryNameString + status = STATUS_BUFFER_TOO_SMALL; + + if (NT_SUCCESS(status)) + dprintf("KphQueryNameObject: %.*S\n", Buffer->Name.Length / sizeof(WCHAR), Buffer->Name.Buffer); + else + dprintf("KphQueryNameObject: status 0x%x\n", status); + + return status; +} + +/** + * Queries the name of a file object. + * + * \param FileObject A pointer to a file object. + * \param Buffer The buffer in which the object name will be stored. + * \param BufferLength The number of bytes available in \a Buffer. + * \param ReturnLength A variable which receives the number of bytes required to be available in + * \a Buffer. + */ +NTSTATUS KphQueryNameFileObject( + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(BufferLength) POBJECT_NAME_INFORMATION Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength; + PCHAR objectName; + ULONG usedLength; + ULONG subNameLength; + PFILE_OBJECT relatedFileObject; + + PAGED_CODE(); + + // We need at least the size of OBJECT_NAME_INFORMATION to continue. + if (BufferLength < sizeof(OBJECT_NAME_INFORMATION)) + { + *ReturnLength = sizeof(OBJECT_NAME_INFORMATION); + + return STATUS_BUFFER_TOO_SMALL; + } + + // Assume failure. + Buffer->Name.Length = 0; + // We will place the object name directly after the UNICODE_STRING structure in the buffer. + Buffer->Name.Buffer = (PWSTR)PTR_ADD_OFFSET(Buffer, sizeof(OBJECT_NAME_INFORMATION)); + // Retain a local pointer to the object name so we can manipulate the pointer. + objectName = (PCHAR)Buffer->Name.Buffer; + // A variable that keeps track of how much space we have used. + usedLength = sizeof(OBJECT_NAME_INFORMATION); + + // Check if the file object has an associated device (e.g. "\Device\NamedPipe", "\Device\Mup"). + // We can use the user-supplied buffer for this since if the buffer isn't big enough, we can't + // proceed anyway (we are going to use the name). + if (FileObject->DeviceObject) + { + status = ObQueryNameString( + FileObject->DeviceObject, + Buffer, + BufferLength, + &returnLength + ); + + if (!NT_SUCCESS(status)) + { + if (status == STATUS_INFO_LENGTH_MISMATCH) + status = STATUS_BUFFER_TOO_SMALL; + + *ReturnLength = returnLength; + + return status; + } + + // The UNICODE_STRING in the buffer is now filled in. We will append to the object name + // later, so we need to fix the object name pointer by adding the length, in bytes, of the + // device name string we just got. + objectName += Buffer->Name.Length; + usedLength += Buffer->Name.Length; + } + + // Check if the file object has a file name component. If not, we can't do anything else, so we + // just return the name we have already. + if (!FileObject->FileName.Buffer) + { + *ReturnLength = usedLength; + + return STATUS_SUCCESS; + } + + // The file object has a name. We need to walk up the file object chain and append the names of + // the related file objects in reverse order. This means we need to calculate the total length + // first. + + relatedFileObject = FileObject; + subNameLength = 0; + + do + { + subNameLength += relatedFileObject->FileName.Length; + + // Avoid infinite loops. + if (relatedFileObject == relatedFileObject->RelatedFileObject) + break; + + relatedFileObject = relatedFileObject->RelatedFileObject; + } while (relatedFileObject); + + usedLength += subNameLength; + + // Check if we have enough space to write the whole thing. + if (usedLength > BufferLength) + { + *ReturnLength = usedLength; + + return STATUS_BUFFER_TOO_SMALL; + } + + // We're ready to begin copying the names. + + // Add the name length because we're copying in reverse order. + objectName += subNameLength; + + relatedFileObject = FileObject; + + do + { + objectName -= relatedFileObject->FileName.Length; + memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length); + + // Avoid infinite loops. + if (relatedFileObject == relatedFileObject->RelatedFileObject) + break; + + relatedFileObject = relatedFileObject->RelatedFileObject; + } while (relatedFileObject); + + // Update the length. + Buffer->Name.Length += (USHORT)subNameLength; + + // Pass the return length back. + *ReturnLength = usedLength; + + return STATUS_SUCCESS; +} + +/** + * Queries object information. + * + * \param ProcessHandle A handle to a process. + * \param Handle A handle which is present in the process referenced by \a ProcessHandle. + * \param ObjectInformationClass The type of information to retrieve. + * \param ObjectInformation The buffer in which the information will be stored. + * \param ObjectInformationLength The number of bytes available in \a ObjectInformation. + * \param ReturnLength A variable which receives the number of bytes required to be available in + * \a ObjectInformation. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiQueryInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_writes_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + KPROCESSOR_MODE referenceMode; + KAPC_STATE apcState; + ULONG returnLength; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ObjectInformation, ObjectInformationLength, sizeof(ULONG)); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (process == PsInitialSystemProcess) + { + // A check was added in Windows 7 - if we're attached to the System process, the handle must + // be a kernel handle. + Handle = MakeKernelHandle(Handle); + referenceMode = KernelMode; + } + else + { + // Make sure the handle isn't a kernel handle if we're not attached to the System process. + // This means we can avoid referencing then opening the objects later when calling + // ZwQueryObject, etc. + if (IsKernelHandle(Handle)) + { + ObDereferenceObject(process); + return STATUS_INVALID_HANDLE; + } + + referenceMode = AccessMode; + } + + switch (ObjectInformationClass) + { + case KphObjectBasicInformation: + { + OBJECT_BASIC_INFORMATION basicInfo; + + KeStackAttachProcess(process, &apcState); + status = ZwQueryObject( + Handle, + ObjectBasicInformation, + &basicInfo, + sizeof(OBJECT_BASIC_INFORMATION), + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + if (ObjectInformationLength == sizeof(OBJECT_BASIC_INFORMATION)) + { + __try + { + *(POBJECT_BASIC_INFORMATION)ObjectInformation = basicInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + returnLength = sizeof(OBJECT_BASIC_INFORMATION); + } + break; + case KphObjectNameInformation: + { + PVOID object; + ULONG allocateSize; + POBJECT_NAME_INFORMATION nameInfo; + + returnLength = sizeof(OBJECT_NAME_INFORMATION); + + // Attach to the process a get a pointer to the object. + KeStackAttachProcess(process, &apcState); + status = ObReferenceObjectByHandle( + Handle, + 0, + NULL, + referenceMode, + &object, + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + allocateSize = ObjectInformationLength; + + if (allocateSize < sizeof(OBJECT_NAME_INFORMATION)) // make sure we never try to allocate 0 bytes + allocateSize = sizeof(OBJECT_NAME_INFORMATION); + + nameInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK'); + + if (nameInfo) + { + // Make sure we don't leak any data. + memset(nameInfo, 0, ObjectInformationLength); + + status = KphQueryNameObject( + object, + nameInfo, + ObjectInformationLength, + &returnLength + ); + dprintf("KpiQueryInformationObject: called KphQueryNameObject: Handle: 0x%Ix, ObjectInformationLength: %u, returnLength: %u\n", + Handle, ObjectInformationLength, returnLength); + + if (NT_SUCCESS(status)) + { + // Fix up the buffer pointer. + if (nameInfo->Name.Buffer) + nameInfo->Name.Buffer = (PVOID)((ULONG_PTR)nameInfo->Name.Buffer - (ULONG_PTR)nameInfo + (ULONG_PTR)ObjectInformation); + + __try + { + memcpy(ObjectInformation, nameInfo, ObjectInformationLength); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + ExFreePoolWithTag(nameInfo, 'QhpK'); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + ObDereferenceObject(object); + } + } + break; + case KphObjectTypeInformation: + { + ULONG allocateSize; + POBJECT_TYPE_INFORMATION typeInfo; + + returnLength = sizeof(OBJECT_TYPE_INFORMATION); + allocateSize = ObjectInformationLength; + + if (allocateSize < sizeof(OBJECT_TYPE_INFORMATION)) + allocateSize = sizeof(OBJECT_TYPE_INFORMATION); + + // ObQueryTypeInfo uses ObjectType->Name.MaximumLength instead of + // ObjectType->Name.Length + sizeof(WCHAR) to calculate the required buffer size. In + // Windows 8, certain object types (e.g. TmTx) do NOT include the null terminator in + // MaximumLength, which causes ObQueryTypeInfo to overrun the given buffer. To work + // around this bug, we add some (generous) padding to our allocation. + allocateSize += sizeof(ULONGLONG); + + typeInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK'); + + if (typeInfo) + { + memset(typeInfo, 0, ObjectInformationLength); + + KeStackAttachProcess(process, &apcState); + status = ZwQueryObject( + Handle, + ObjectTypeInformation, + typeInfo, + ObjectInformationLength, + &returnLength + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + // Fix up the buffer pointer. + if (typeInfo->TypeName.Buffer) + typeInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)typeInfo->TypeName.Buffer - (ULONG_PTR)typeInfo + (ULONG_PTR)ObjectInformation); + + __try + { + memcpy(ObjectInformation, typeInfo, ObjectInformationLength); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + ExFreePoolWithTag(typeInfo, 'QhpK'); + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + break; + case KphObjectHandleFlagInformation: + { + OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; + + KeStackAttachProcess(process, &apcState); + status = ZwQueryObject( + Handle, + ObjectHandleFlagInformation, + &handleFlagInfo, + sizeof(OBJECT_HANDLE_FLAG_INFORMATION), + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION)) + { + __try + { + *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation = handleFlagInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + returnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION); + } + break; + case KphObjectProcessBasicInformation: + { + PROCESS_BASIC_INFORMATION basicInfo; + + KeStackAttachProcess(process, &apcState); + status = ZwQueryInformationProcess( + Handle, + ProcessBasicInformation, + &basicInfo, + sizeof(PROCESS_BASIC_INFORMATION), + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + if (ObjectInformationLength == sizeof(PROCESS_BASIC_INFORMATION)) + { + __try + { + *(PPROCESS_BASIC_INFORMATION)ObjectInformation = basicInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + returnLength = sizeof(PROCESS_BASIC_INFORMATION); + } + break; + case KphObjectThreadBasicInformation: + { + THREAD_BASIC_INFORMATION basicInfo; + + KeStackAttachProcess(process, &apcState); + status = ZwQueryInformationThread( + Handle, + ThreadBasicInformation, + &basicInfo, + sizeof(THREAD_BASIC_INFORMATION), + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + if (ObjectInformationLength == sizeof(THREAD_BASIC_INFORMATION)) + { + __try + { + *(PTHREAD_BASIC_INFORMATION)ObjectInformation = basicInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + returnLength = sizeof(THREAD_BASIC_INFORMATION); + } + break; + case KphObjectEtwRegBasicInformation: + { + PVOID etwReg; + PVOID objectType; + PUNICODE_STRING objectTypeName; + UNICODE_STRING etwRegistrationName; + PVOID guidEntry; + ETWREG_BASIC_INFORMATION basicInfo; + + // Check dynamic data requirements. + if (KphDynEgeGuid != -1 && + KphDynEreGuidEntry != -1 && + KphDynOtName != -1) + { + // Attach to the process and get a pointer to the object. We don't have a pointer to + // the EtwRegistration object type, so we'll just have to check the type name. + + KeStackAttachProcess(process, &apcState); + status = ObReferenceObjectByHandle( + Handle, + 0, + NULL, + referenceMode, + &etwReg, + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + // Check the type name. + + objectType = ObGetObjectType(etwReg); + + if (objectType) + { + objectTypeName = (PUNICODE_STRING)PTR_ADD_OFFSET(objectType, KphDynOtName); + RtlInitUnicodeString(&etwRegistrationName, L"EtwRegistration"); + + if (!RtlEqualUnicodeString(objectTypeName, &etwRegistrationName, FALSE)) + { + status = STATUS_OBJECT_TYPE_MISMATCH; + } + } + else + { + status = STATUS_NOT_SUPPORTED; + } + + if (NT_SUCCESS(status)) + { + guidEntry = *(PVOID *)PTR_ADD_OFFSET(etwReg, KphDynEreGuidEntry); + + if (guidEntry) + basicInfo.Guid = *(GUID *)PTR_ADD_OFFSET(guidEntry, KphDynEgeGuid); + else + memset(&basicInfo.Guid, 0, sizeof(GUID)); + + basicInfo.SessionId = 0; // not implemented + + if (ObjectInformationLength == sizeof(ETWREG_BASIC_INFORMATION)) + { + __try + { + *(PETWREG_BASIC_INFORMATION)ObjectInformation = basicInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + + ObDereferenceObject(etwReg); + } + } + else + { + status = STATUS_NOT_SUPPORTED; + } + + returnLength = sizeof(ETWREG_BASIC_INFORMATION); + } + break; + case KphObjectFileObjectInformation: + { + PFILE_OBJECT fileObject; + KPH_FILE_OBJECT_INFORMATION objectInfo; + + KeStackAttachProcess(process, &apcState); + status = ObReferenceObjectByHandle( + Handle, + 0, + *IoFileObjectType, + referenceMode, + &fileObject, + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + objectInfo.LockOperation = fileObject->LockOperation; + objectInfo.DeletePending = fileObject->DeletePending; + objectInfo.ReadAccess = fileObject->ReadAccess; + objectInfo.WriteAccess = fileObject->WriteAccess; + objectInfo.DeleteAccess = fileObject->DeleteAccess; + objectInfo.SharedRead = fileObject->SharedRead; + objectInfo.SharedWrite = fileObject->SharedWrite; + objectInfo.SharedDelete = fileObject->SharedDelete; + objectInfo.CurrentByteOffset = fileObject->CurrentByteOffset; + objectInfo.Flags = fileObject->Flags; + + if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_INFORMATION)) + { + __try + { + *(PKPH_FILE_OBJECT_INFORMATION)ObjectInformation = objectInfo; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + ObDereferenceObject(fileObject); + } + + returnLength = sizeof(KPH_FILE_OBJECT_INFORMATION); + } + break; + case KphObjectFileObjectDriver: + { + PFILE_OBJECT fileObject; + HANDLE driverHandle; + + if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_DRIVER)) + { + KeStackAttachProcess(process, &apcState); + status = ObReferenceObjectByHandle( + Handle, + 0, + *IoFileObjectType, + referenceMode, + &fileObject, + NULL + ); + KeUnstackDetachProcess(&apcState); + + if (NT_SUCCESS(status)) + { + if (fileObject->DeviceObject && fileObject->DeviceObject->DriverObject) + { + status = ObOpenObjectByPointer( + fileObject->DeviceObject->DriverObject, + 0, + NULL, + SYNCHRONIZE, + *IoDriverObjectType, + AccessMode, + &driverHandle + ); + } + else + { + driverHandle = NULL; + } + + if (NT_SUCCESS(status)) + { + __try + { + ((PKPH_FILE_OBJECT_DRIVER)ObjectInformation)->DriverHandle = driverHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + + ObDereferenceObject(fileObject); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + break; + default: + status = STATUS_INVALID_INFO_CLASS; + returnLength = 0; + break; + } + + ObDereferenceObject(process); + + if (ReturnLength) + { + if (AccessMode != KernelMode) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + NOTHING; + } + } + else + { + *ReturnLength = returnLength; + } + } + + return status; +} + +/** + * Sets object information. + * + * \param ProcessHandle A handle to a process. + * \param Handle A handle which is present in the process referenced by \a ProcessHandle. + * \param ObjectInformationClass The type of information to set. + * \param ObjectInformation A buffer which contains the information to set. + * \param ObjectInformationLength The number of bytes present in \a ObjectInformation. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiSetInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + KAPC_STATE apcState; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + ULONG alignment; + + switch (ObjectInformationClass) + { + case KphObjectHandleFlagInformation: + alignment = sizeof(BOOLEAN); + break; + default: + alignment = sizeof(ULONG); + break; + } + + __try + { + ProbeForRead(ObjectInformation, ObjectInformationLength, alignment); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (process == PsInitialSystemProcess) + { + Handle = MakeKernelHandle(Handle); + } + else + { + if (IsKernelHandle(Handle)) + { + ObDereferenceObject(process); + return STATUS_INVALID_HANDLE; + } + } + + switch (ObjectInformationClass) + { + case KphObjectHandleFlagInformation: + { + OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; + + if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION)) + { + __try + { + handleFlagInfo = *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + if (NT_SUCCESS(status)) + { + KeStackAttachProcess(process, &apcState); + status = ObSetHandleAttributes(Handle, &handleFlagInfo, KernelMode); + KeUnstackDetachProcess(&apcState); + } + } + break; + default: + status = STATUS_INVALID_INFO_CLASS; + break; + } + + ObDereferenceObject(process); + + return status; +} + +NTSTATUS KphOpenNamedObject( + _Out_ PHANDLE ObjectHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ POBJECT_TYPE ObjectType, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + HANDLE objectHandle; + + PAGED_CODE(); + + // Open the object. + status = ObOpenObjectByName( + ObjectAttributes, + ObjectType, + AccessMode, + NULL, + DesiredAccess, + NULL, + &objectHandle + ); + + // Pass the handle back. + if (AccessMode != KernelMode) + { + __try + { + *ObjectHandle = objectHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *ObjectHandle = objectHandle; + } + + return status; +} diff --git a/KProcessHacker/process.c b/KProcessHacker/process.c index bdb27261fa3b..17532e91dd5c 100644 --- a/KProcessHacker/process.c +++ b/KProcessHacker/process.c @@ -1,570 +1,570 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KpiOpenProcess) -#pragma alloc_text(PAGE, KpiOpenProcessToken) -#pragma alloc_text(PAGE, KpiOpenProcessJob) -#pragma alloc_text(PAGE, KpiTerminateProcess) -#pragma alloc_text(PAGE, KpiQueryInformationProcess) -#pragma alloc_text(PAGE, KpiSetInformationProcess) -#endif - -/** - * Opens a process. - * - * \param ProcessHandle A variable which receives the process handle. - * \param DesiredAccess The desired access to the process. - * \param ClientId The identifier of a process or thread. If \a UniqueThread is present, the process - * of the identified thread will be opened. If \a UniqueProcess is present, the identified process - * will be opened. - * \param Key An access key. - * \li If a L2 key is provided, no access checks are performed. - * \li If a L1 key is provided, only read access is permitted but no additional access checks are - * performed. - * \li If no valid key is provided, the function fails. - * \param Client The client that initiated the request. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiOpenProcess( - __out PHANDLE ProcessHandle, - __in ACCESS_MASK DesiredAccess, - __in PCLIENT_ID ClientId, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - CLIENT_ID clientId; - PEPROCESS process; - PETHREAD thread; - KPH_KEY_LEVEL requiredKeyLevel; - HANDLE processHandle; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(ProcessHandle, sizeof(HANDLE), sizeof(HANDLE)); - ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG)); - clientId = *ClientId; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else - { - clientId = *ClientId; - } - - // Use the thread ID if it was specified. - if (clientId.UniqueThread) - { - status = PsLookupProcessThreadByCid(&clientId, &process, &thread); - - if (NT_SUCCESS(status)) - { - // We don't actually need the thread. - ObDereferenceObject(thread); - } - } - else - { - status = PsLookupProcessByProcessId(clientId.UniqueProcess, &process); - } - - if (!NT_SUCCESS(status)) - return status; - - requiredKeyLevel = KphKeyLevel1; - - if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) != DesiredAccess) - requiredKeyLevel = KphKeyLevel2; - - if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode))) - { - // Always open in KernelMode to skip ordinary access checks. - status = ObOpenObjectByPointer( - process, - 0, - NULL, - DesiredAccess, - *PsProcessType, - KernelMode, - &processHandle - ); - } - - ObDereferenceObject(process); - - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) - { - __try - { - *ProcessHandle = processHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *ProcessHandle = processHandle; - } - } - - return status; -} - -/** - * Opens the token of a process. - * - * \param ProcessHandle A handle to a process. - * \param DesiredAccess The desired access to the token. - * \param TokenHandle A variable which receives the token handle. - * \param Key An access key. - * \li If a L2 key is provided, no access checks are performed. - * \li If a L1 key is provided, only read access is permitted but no additional access checks are - * performed. - * \li If no valid key is provided, the function fails. - * \param Client The client that initiated the request. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiOpenProcessToken( - __in HANDLE ProcessHandle, - __in ACCESS_MASK DesiredAccess, - __out PHANDLE TokenHandle, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - PACCESS_TOKEN primaryToken; - KPH_KEY_LEVEL requiredKeyLevel; - HANDLE tokenHandle; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(TokenHandle, sizeof(HANDLE), sizeof(HANDLE)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ProcessHandle, - 0, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - if (primaryToken = PsReferencePrimaryToken(process)) - { - requiredKeyLevel = KphKeyLevel1; - - if ((DesiredAccess & KPH_TOKEN_READ_ACCESS) != DesiredAccess) - requiredKeyLevel = KphKeyLevel2; - - if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode))) - { - status = ObOpenObjectByPointer( - primaryToken, - 0, - NULL, - DesiredAccess, - *SeTokenObjectType, - KernelMode, - &tokenHandle - ); - } - - PsDereferencePrimaryToken(primaryToken); - } - else - { - status = STATUS_NO_TOKEN; - } - - ObDereferenceObject(process); - - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) - { - __try - { - *TokenHandle = tokenHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *TokenHandle = tokenHandle; - } - } - - return status; -} - -/** - * Opens the job object of a process. - * - * \param ProcessHandle A handle to a process. - * \param DesiredAccess The desired access to the job. - * \param JobHandle A variable which receives the job object handle. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiOpenProcessJob( - __in HANDLE ProcessHandle, - __in ACCESS_MASK DesiredAccess, - __out PHANDLE JobHandle, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - PEJOB job; - HANDLE jobHandle = NULL; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(JobHandle, sizeof(HANDLE), sizeof(HANDLE)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ProcessHandle, - 0, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - job = PsGetProcessJob(process); - - if (job) - { - status = ObOpenObjectByPointer( - job, - 0, - NULL, - DesiredAccess, - *PsJobType, - AccessMode, - &jobHandle - ); - } - else - { - status = STATUS_NOT_FOUND; - } - - ObDereferenceObject(process); - - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) - { - __try - { - *JobHandle = jobHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *JobHandle = jobHandle; - } - } - - return status; -} - -/** - * Terminates a process. - * - * \param ProcessHandle A handle to a process. - * \param ExitStatus A status value which indicates why the process is being terminated. - * \param Key An access key. - * \li If a L2 key is provided, no access checks are performed. - * \li If no valid L2 key is provided, the function fails. - * \param Client The client that initiated the request. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiTerminateProcess( - __in HANDLE ProcessHandle, - __in NTSTATUS ExitStatus, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - - PAGED_CODE(); - - if (!NT_SUCCESS(status = KphValidateKey(KphKeyLevel2, Key, Client, AccessMode))) - return status; - - status = ObReferenceObjectByHandle( - ProcessHandle, - 0, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - if (process != PsGetCurrentProcess()) - { - HANDLE newProcessHandle; - - // Re-open the process to get a kernel handle. - if (NT_SUCCESS(status = ObOpenObjectByPointer( - process, - OBJ_KERNEL_HANDLE, - NULL, - PROCESS_TERMINATE, - *PsProcessType, - KernelMode, - &newProcessHandle - ))) - { - status = ZwTerminateProcess(newProcessHandle, ExitStatus); - ZwClose(newProcessHandle); - } - } - else - { - status = STATUS_CANT_TERMINATE_SELF; - } - - ObDereferenceObject(process); - - return status; -} - -/** - * Queries process information. - * - * \param ProcessHandle A handle to a process. - * \param ProcessInformationClass The type of information to query. - * \param ProcessInformation The buffer in which the information will be stored. - * \param ProcessInformationLength The number of bytes available in \a ProcessInformation. - * \param ReturnLength A variable which receives the number of bytes required to be available in - * \a ProcessInformation. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiQueryInformationProcess( - __in HANDLE ProcessHandle, - __in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - __out_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - ULONG returnLength; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - ULONG alignment; - - switch (ProcessInformationClass) - { - default: - alignment = sizeof(ULONG); - break; - } - - __try - { - ProbeForWrite(ProcessInformation, ProcessInformationLength, alignment); - - if (ReturnLength) - ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ProcessHandle, - PROCESS_QUERY_INFORMATION, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - switch (ProcessInformationClass) - { - default: - status = STATUS_INVALID_INFO_CLASS; - returnLength = 0; - break; - } - - ObDereferenceObject(process); - - if (ReturnLength) - { - if (AccessMode != KernelMode) - { - __try - { - *ReturnLength = returnLength; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - NOTHING; - } - } - else - { - *ReturnLength = returnLength; - } - } - - return status; -} - -/** - * Sets process information. - * - * \param ProcessHandle A handle to a process. - * \param ProcessInformationClass The type of information to set. - * \param ProcessInformation A buffer which contains the information to set. - * \param ProcessInformationLength The number of bytes present in \a ProcessInformation. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiSetInformationProcess( - __in HANDLE ProcessHandle, - __in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - __in_bcount(ProcessInformationLength) PVOID ProcessInformation, - __in ULONG ProcessInformationLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - ULONG alignment; - - switch (ProcessInformationClass) - { - default: - alignment = sizeof(ULONG); - break; - } - - __try - { - ProbeForRead(ProcessInformation, ProcessInformationLength, alignment); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ProcessHandle, - PROCESS_SET_INFORMATION, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - switch (ProcessInformationClass) - { - default: - status = STATUS_INVALID_INFO_CLASS; - break; - } - - ObDereferenceObject(process); - - return status; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KpiOpenProcess) +#pragma alloc_text(PAGE, KpiOpenProcessToken) +#pragma alloc_text(PAGE, KpiOpenProcessJob) +#pragma alloc_text(PAGE, KpiTerminateProcess) +#pragma alloc_text(PAGE, KpiQueryInformationProcess) +#pragma alloc_text(PAGE, KpiSetInformationProcess) +#endif + +/** + * Opens a process. + * + * \param ProcessHandle A variable which receives the process handle. + * \param DesiredAccess The desired access to the process. + * \param ClientId The identifier of a process or thread. If \a UniqueThread is present, the process + * of the identified thread will be opened. If \a UniqueProcess is present, the identified process + * will be opened. + * \param Key An access key. + * \li If a L2 key is provided, no access checks are performed. + * \li If a L1 key is provided, only read access is permitted but no additional access checks are + * performed. + * \li If no valid key is provided, the function fails. + * \param Client The client that initiated the request. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + CLIENT_ID clientId; + PEPROCESS process; + PETHREAD thread; + KPH_KEY_LEVEL requiredKeyLevel; + HANDLE processHandle; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ProcessHandle, sizeof(HANDLE), sizeof(HANDLE)); + ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG)); + clientId = *ClientId; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + clientId = *ClientId; + } + + // Use the thread ID if it was specified. + if (clientId.UniqueThread) + { + status = PsLookupProcessThreadByCid(&clientId, &process, &thread); + + if (NT_SUCCESS(status)) + { + // We don't actually need the thread. + ObDereferenceObject(thread); + } + } + else + { + status = PsLookupProcessByProcessId(clientId.UniqueProcess, &process); + } + + if (!NT_SUCCESS(status)) + return status; + + requiredKeyLevel = KphKeyLevel1; + + if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) != DesiredAccess) + requiredKeyLevel = KphKeyLevel2; + + if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode))) + { + // Always open in KernelMode to skip ordinary access checks. + status = ObOpenObjectByPointer( + process, + 0, + NULL, + DesiredAccess, + *PsProcessType, + KernelMode, + &processHandle + ); + + if (NT_SUCCESS(status)) + { + if (AccessMode != KernelMode) + { + __try + { + *ProcessHandle = processHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *ProcessHandle = processHandle; + } + } + } + + ObDereferenceObject(process); + + return status; +} + +/** + * Opens the token of a process. + * + * \param ProcessHandle A handle to a process. + * \param DesiredAccess The desired access to the token. + * \param TokenHandle A variable which receives the token handle. + * \param Key An access key. + * \li If a L2 key is provided, no access checks are performed. + * \li If a L1 key is provided, only read access is permitted but no additional access checks are + * performed. + * \li If no valid key is provided, the function fails. + * \param Client The client that initiated the request. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + PACCESS_TOKEN primaryToken; + KPH_KEY_LEVEL requiredKeyLevel; + HANDLE tokenHandle; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(TokenHandle, sizeof(HANDLE), sizeof(HANDLE)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (primaryToken = PsReferencePrimaryToken(process)) + { + requiredKeyLevel = KphKeyLevel1; + + if ((DesiredAccess & KPH_TOKEN_READ_ACCESS) != DesiredAccess) + requiredKeyLevel = KphKeyLevel2; + + if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode))) + { + status = ObOpenObjectByPointer( + primaryToken, + 0, + NULL, + DesiredAccess, + *SeTokenObjectType, + KernelMode, + &tokenHandle + ); + + if (NT_SUCCESS(status)) + { + if (AccessMode != KernelMode) + { + __try + { + *TokenHandle = tokenHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *TokenHandle = tokenHandle; + } + } + } + + PsDereferencePrimaryToken(primaryToken); + } + else + { + status = STATUS_NO_TOKEN; + } + + ObDereferenceObject(process); + + return status; +} + +/** + * Opens the job object of a process. + * + * \param ProcessHandle A handle to a process. + * \param DesiredAccess The desired access to the job. + * \param JobHandle A variable which receives the job object handle. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiOpenProcessJob( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE JobHandle, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + PEJOB job; + HANDLE jobHandle; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(JobHandle, sizeof(HANDLE), sizeof(HANDLE)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + job = PsGetProcessJob(process); + + if (job) + { + status = ObOpenObjectByPointer( + job, + 0, + NULL, + DesiredAccess, + *PsJobType, + AccessMode, + &jobHandle + ); + + if (NT_SUCCESS(status)) + { + if (AccessMode != KernelMode) + { + __try + { + *JobHandle = jobHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *JobHandle = jobHandle; + } + } + } + else + { + status = STATUS_NOT_FOUND; + } + + ObDereferenceObject(process); + + return status; +} + +/** + * Terminates a process. + * + * \param ProcessHandle A handle to a process. + * \param ExitStatus A status value which indicates why the process is being terminated. + * \param Key An access key. + * \li If a L2 key is provided, no access checks are performed. + * \li If no valid L2 key is provided, the function fails. + * \param Client The client that initiated the request. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiTerminateProcess( + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + + PAGED_CODE(); + + if (!NT_SUCCESS(status = KphValidateKey(KphKeyLevel2, Key, Client, AccessMode))) + return status; + + status = ObReferenceObjectByHandle( + ProcessHandle, + 0, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (process != PsGetCurrentProcess()) + { + HANDLE newProcessHandle; + + // Re-open the process to get a kernel handle. + if (NT_SUCCESS(status = ObOpenObjectByPointer( + process, + OBJ_KERNEL_HANDLE, + NULL, + PROCESS_TERMINATE, + *PsProcessType, + KernelMode, + &newProcessHandle + ))) + { + status = ZwTerminateProcess(newProcessHandle, ExitStatus); + ZwClose(newProcessHandle); + } + } + else + { + status = STATUS_CANT_TERMINATE_SELF; + } + + ObDereferenceObject(process); + + return status; +} + +/** + * Queries process information. + * + * \param ProcessHandle A handle to a process. + * \param ProcessInformationClass The type of information to query. + * \param ProcessInformation The buffer in which the information will be stored. + * \param ProcessInformationLength The number of bytes available in \a ProcessInformation. + * \param ReturnLength A variable which receives the number of bytes required to be available in + * \a ProcessInformation. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + ULONG returnLength; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + ULONG alignment; + + switch (ProcessInformationClass) + { + default: + alignment = sizeof(ULONG); + break; + } + + __try + { + ProbeForWrite(ProcessInformation, ProcessInformationLength, alignment); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_QUERY_INFORMATION, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + switch (ProcessInformationClass) + { + default: + status = STATUS_INVALID_INFO_CLASS; + returnLength = 0; + break; + } + + ObDereferenceObject(process); + + if (ReturnLength) + { + if (AccessMode != KernelMode) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + NOTHING; + } + } + else + { + *ReturnLength = returnLength; + } + } + + return status; +} + +/** + * Sets process information. + * + * \param ProcessHandle A handle to a process. + * \param ProcessInformationClass The type of information to set. + * \param ProcessInformation A buffer which contains the information to set. + * \param ProcessInformationLength The number of bytes present in \a ProcessInformation. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiSetInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + ULONG alignment; + + switch (ProcessInformationClass) + { + default: + alignment = sizeof(ULONG); + break; + } + + __try + { + ProbeForRead(ProcessInformation, ProcessInformationLength, alignment); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_SET_INFORMATION, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + switch (ProcessInformationClass) + { + default: + status = STATUS_INVALID_INFO_CLASS; + break; + } + + ObDereferenceObject(process); + + return status; +} diff --git a/KProcessHacker/qrydrv.c b/KProcessHacker/qrydrv.c index 3ecfb6197f00..cc3879e0e3fb 100644 --- a/KProcessHacker/qrydrv.c +++ b/KProcessHacker/qrydrv.c @@ -1,238 +1,238 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -VOID KphpCopyInfoUnicodeString( - __out PVOID Information, - __in_opt PUNICODE_STRING UnicodeString - ); - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KpiOpenDriver) -#pragma alloc_text(PAGE, KpiQueryInformationDriver) -#pragma alloc_text(PAGE, KphpCopyInfoUnicodeString) -#endif - -NTSTATUS KpiOpenDriver( - __out PHANDLE DriverHandle, - __in ACCESS_MASK DesiredAccess, - __in POBJECT_ATTRIBUTES ObjectAttributes, - __in KPROCESSOR_MODE AccessMode - ) -{ - PAGED_CODE(); - - return KphOpenNamedObject( - DriverHandle, - DesiredAccess, - ObjectAttributes, - *IoDriverObjectType, - AccessMode - ); -} - -NTSTATUS KpiQueryInformationDriver( - __in HANDLE DriverHandle, - __in DRIVER_INFORMATION_CLASS DriverInformationClass, - __out_bcount(DriverInformationLength) PVOID DriverInformation, - __in ULONG DriverInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PDRIVER_OBJECT driverObject; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(DriverInformation, DriverInformationLength, 1); - - if (ReturnLength) - ProbeForWrite(ReturnLength, sizeof(ULONG), 1); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - DriverHandle, - 0, - *IoDriverObjectType, - AccessMode, - &driverObject, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - __try - { - switch (DriverInformationClass) - { - // Basic information such as flags, driver base and driver size. - case DriverBasicInformation: - { - if (DriverInformationLength == sizeof(DRIVER_BASIC_INFORMATION)) - { - PDRIVER_BASIC_INFORMATION basicInfo; - - basicInfo = (PDRIVER_BASIC_INFORMATION)DriverInformation; - basicInfo->Flags = driverObject->Flags; - basicInfo->DriverStart = driverObject->DriverStart; - basicInfo->DriverSize = driverObject->DriverSize; - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - - if (ReturnLength) - *ReturnLength = sizeof(DRIVER_BASIC_INFORMATION); - } - break; - - // The name of the driver - e.g. \Driver\Null. - case DriverNameInformation: - { - if (DriverInformation) - { - /* Check buffer length. */ - if (sizeof(UNICODE_STRING) + driverObject->DriverName.Length <= - DriverInformationLength) - { - KphpCopyInfoUnicodeString( - DriverInformation, - &driverObject->DriverName - ); - } - else - { - status = STATUS_BUFFER_TOO_SMALL; - } - } - - if (ReturnLength) - *ReturnLength = sizeof(UNICODE_STRING) + driverObject->DriverName.Length; - } - break; - - // The name of the driver's service key - e.g. \REGISTRY\... - case DriverServiceKeyNameInformation: - { - if (driverObject->DriverExtension) - { - if (DriverInformation) - { - if (sizeof(UNICODE_STRING) + - driverObject->DriverExtension->ServiceKeyName.Length <= - DriverInformationLength) - { - KphpCopyInfoUnicodeString( - DriverInformation, - &driverObject->DriverExtension->ServiceKeyName - ); - } - else - { - status = STATUS_BUFFER_TOO_SMALL; - } - } - - if (ReturnLength) - { - *ReturnLength = sizeof(UNICODE_STRING) + - driverObject->DriverExtension->ServiceKeyName.Length; - } - } - else - { - if (DriverInformation) - { - if (sizeof(UNICODE_STRING) <= DriverInformationLength) - { - // Zero the information buffer. - KphpCopyInfoUnicodeString( - DriverInformation, - NULL - ); - } - else - { - status = STATUS_BUFFER_TOO_SMALL; - } - } - - if (ReturnLength) - *ReturnLength = sizeof(UNICODE_STRING); - } - } - break; - - default: - { - status = STATUS_INVALID_INFO_CLASS; - } - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - - ObDereferenceObject(driverObject); - - return status; -} - -VOID KphpCopyInfoUnicodeString( - __out PVOID Information, - __in_opt PUNICODE_STRING UnicodeString - ) -{ - PUNICODE_STRING targetUnicodeString = Information; - PWCHAR targetBuffer; - - PAGED_CODE(); - - if (UnicodeString) - { - targetBuffer = (PWCHAR)((PCHAR)Information + sizeof(UNICODE_STRING)); - - targetUnicodeString->Length = UnicodeString->Length; - targetUnicodeString->MaximumLength = UnicodeString->Length; - targetUnicodeString->Buffer = targetBuffer; - memcpy(targetBuffer, UnicodeString->Buffer, UnicodeString->Length); - } - else - { - targetUnicodeString->Length = 0; - targetUnicodeString->MaximumLength = 0; - targetUnicodeString->Buffer = NULL; - } -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID KphpCopyInfoUnicodeString( + _Out_ PVOID Information, + _In_opt_ PUNICODE_STRING UnicodeString + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KpiOpenDriver) +#pragma alloc_text(PAGE, KpiQueryInformationDriver) +#pragma alloc_text(PAGE, KphpCopyInfoUnicodeString) +#endif + +NTSTATUS KpiOpenDriver( + _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + PAGED_CODE(); + + return KphOpenNamedObject( + DriverHandle, + DesiredAccess, + ObjectAttributes, + *IoDriverObjectType, + AccessMode + ); +} + +NTSTATUS KpiQueryInformationDriver( + _In_ HANDLE DriverHandle, + _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, + _Out_writes_bytes_(DriverInformationLength) PVOID DriverInformation, + _In_ ULONG DriverInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PDRIVER_OBJECT driverObject; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(DriverInformation, DriverInformationLength, 1); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), 1); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + DriverHandle, + 0, + *IoDriverObjectType, + AccessMode, + &driverObject, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + __try + { + switch (DriverInformationClass) + { + // Basic information such as flags, driver base and driver size. + case DriverBasicInformation: + { + if (DriverInformationLength == sizeof(DRIVER_BASIC_INFORMATION)) + { + PDRIVER_BASIC_INFORMATION basicInfo; + + basicInfo = (PDRIVER_BASIC_INFORMATION)DriverInformation; + basicInfo->Flags = driverObject->Flags; + basicInfo->DriverStart = driverObject->DriverStart; + basicInfo->DriverSize = driverObject->DriverSize; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + if (ReturnLength) + *ReturnLength = sizeof(DRIVER_BASIC_INFORMATION); + } + break; + + // The name of the driver - e.g. \Driver\Null. + case DriverNameInformation: + { + if (DriverInformation) + { + /* Check buffer length. */ + if (sizeof(UNICODE_STRING) + driverObject->DriverName.Length <= + DriverInformationLength) + { + KphpCopyInfoUnicodeString( + DriverInformation, + &driverObject->DriverName + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING) + driverObject->DriverName.Length; + } + break; + + // The name of the driver's service key - e.g. \REGISTRY\... + case DriverServiceKeyNameInformation: + { + if (driverObject->DriverExtension) + { + if (DriverInformation) + { + if (sizeof(UNICODE_STRING) + + driverObject->DriverExtension->ServiceKeyName.Length <= + DriverInformationLength) + { + KphpCopyInfoUnicodeString( + DriverInformation, + &driverObject->DriverExtension->ServiceKeyName + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + { + *ReturnLength = sizeof(UNICODE_STRING) + + driverObject->DriverExtension->ServiceKeyName.Length; + } + } + else + { + if (DriverInformation) + { + if (sizeof(UNICODE_STRING) <= DriverInformationLength) + { + // Zero the information buffer. + KphpCopyInfoUnicodeString( + DriverInformation, + NULL + ); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnLength) + *ReturnLength = sizeof(UNICODE_STRING); + } + } + break; + + default: + { + status = STATUS_INVALID_INFO_CLASS; + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + ObDereferenceObject(driverObject); + + return status; +} + +VOID KphpCopyInfoUnicodeString( + _Out_ PVOID Information, + _In_opt_ PUNICODE_STRING UnicodeString + ) +{ + PUNICODE_STRING targetUnicodeString = Information; + PWCHAR targetBuffer; + + PAGED_CODE(); + + if (UnicodeString) + { + targetBuffer = (PWCHAR)PTR_ADD_OFFSET(Information, sizeof(UNICODE_STRING)); + + targetUnicodeString->Length = UnicodeString->Length; + targetUnicodeString->MaximumLength = UnicodeString->Length; + targetUnicodeString->Buffer = targetBuffer; + memcpy(targetBuffer, UnicodeString->Buffer, UnicodeString->Length); + } + else + { + targetUnicodeString->Length = 0; + targetUnicodeString->MaximumLength = 0; + targetUnicodeString->Buffer = NULL; + } +} diff --git a/KProcessHacker/resource.rc b/KProcessHacker/resource.rc index ca42f86d3631..ce831638b987 100644 --- a/KProcessHacker/resource.rc +++ b/KProcessHacker/resource.rc @@ -1,53 +1,53 @@ -#include - -#define VER_COMMA 3,1,0,0 -#define VER_STR "3.1\0" - -#define VER_FILEVERSION VER_COMMA -#define VER_FILEVERSION_STR VER_STR -#define VER_PRODUCTVERSION VER_COMMA -#define VER_PRODUCTVERSION_STR VER_STR - -#ifndef DEBUG -#define VER_DEBUG 0 -#else -#define VER_DEBUG VS_FF_DEBUG -#endif - -#define VER_PRIVATEBUILD 0 -#define VER_PRERELEASE 0 - -#define VER_COMPANYNAME_STR "wj32\0" -#define VER_FILEDESCRIPTION_STR "KProcessHacker\0" -#define VER_LEGALCOPYRIGHT_STR "Licensed under the GNU GPL, v3.\0" -#define VER_ORIGINALFILENAME_STR "kprocesshacker.sys\0" -#define VER_PRODUCTNAME_STR "KProcessHacker\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VER_FILEVERSION -PRODUCTVERSION VER_PRODUCTVERSION -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG) -FILEOS VOS__WINDOWS32 -FILETYPE VFT_DRV -FILESUBTYPE VFT2_DRV_SYSTEM -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", VER_COMPANYNAME_STR - VALUE "FileDescription", VER_FILEDESCRIPTION_STR - VALUE "FileVersion", VER_FILEVERSION_STR - VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR - VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR - VALUE "ProductName", VER_PRODUCTNAME_STR - VALUE "ProductVersion", VER_PRODUCTVERSION_STR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END +#include + +#define VER_COMMA 3,1,0,0 +#define VER_STR "3.1\0" + +#define VER_FILEVERSION VER_COMMA +#define VER_FILEVERSION_STR VER_STR +#define VER_PRODUCTVERSION VER_COMMA +#define VER_PRODUCTVERSION_STR VER_STR + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#define VER_PRIVATEBUILD 0 +#define VER_PRERELEASE 0 + +#define VER_COMPANYNAME_STR "wj32\0" +#define VER_FILEDESCRIPTION_STR "KProcessHacker\0" +#define VER_LEGALCOPYRIGHT_STR "Licensed under the GNU GPL, v3.\0" +#define VER_ORIGINALFILENAME_STR "kprocesshacker.sys\0" +#define VER_PRODUCTNAME_STR "KProcessHacker\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG) +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DRV +FILESUBTYPE VFT2_DRV_SYSTEM +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", VER_FILEDESCRIPTION_STR + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/KProcessHacker/sign.cmd b/KProcessHacker/sign.cmd deleted file mode 100644 index fe9c32599e2a..000000000000 --- a/KProcessHacker/sign.cmd +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -set PHBASE=.. -set SIGN_TIMESTAMP=1 -copy bin\i386\kprocesshacker.sys bin-signed\i386\kprocesshacker.sys -copy bin\amd64\kprocesshacker.sys bin-signed\amd64\kprocesshacker.sys -call ..\build\internal\sign.cmd bin-signed\i386\kprocesshacker.sys kmcs -call ..\build\internal\sign.cmd bin-signed\amd64\kprocesshacker.sys kmcs diff --git a/KProcessHacker/sources.inc b/KProcessHacker/sources.inc deleted file mode 100644 index d50fe7368fa1..000000000000 --- a/KProcessHacker/sources.inc +++ /dev/null @@ -1,28 +0,0 @@ -TARGETTYPE=DRIVER - -!IF !DEFINED(TARGETNAME) -TARGETNAME=kprocesshacker -!ENDIF - -!IF !DEFINED(TARGETPATH) -TARGETPATH=..\bin -!ENDIF - -TARGETLIBS=$(TARGETLIBS) $(DDK_LIB_PATH)\ksecdd.lib - -INCLUDES=$(DDK_INC_PATH);..\include;..\..\phnt\include;..\..\phlib\include -LIBS=%BUILD%\lib - -SOURCES= \ - ..\main.c \ - ..\devctrl.c \ - ..\dyndata.c \ - ..\dynimp.c \ - ..\object.c \ - ..\process.c \ - ..\qrydrv.c \ - ..\thread.c \ - ..\util.c \ - ..\verify.c \ - ..\vm.c \ - ..\resource.rc diff --git a/KProcessHacker/thread.c b/KProcessHacker/thread.c index 1ac2b750aab7..15ccd26705b8 100644 --- a/KProcessHacker/thread.c +++ b/KProcessHacker/thread.c @@ -1,714 +1,714 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT -{ - BOOLEAN Local; - KAPC Apc; - KEVENT CompletedEvent; - ULONG FramesToSkip; - ULONG FramesToCapture; - PVOID *BackTrace; - ULONG CapturedFrames; - ULONG BackTraceHash; -} CAPTURE_BACKTRACE_THREAD_CONTEXT, *PCAPTURE_BACKTRACE_THREAD_CONTEXT; - -KKERNEL_ROUTINE KphpCaptureStackBackTraceThreadSpecialApc; - -VOID KphpCaptureStackBackTraceThreadSpecialApc( - __in PRKAPC Apc, - __inout PKNORMAL_ROUTINE *NormalRoutine, - __inout PVOID *NormalContext, - __inout PVOID *SystemArgument1, - __inout PVOID *SystemArgument2 - ); - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KpiOpenThread) -#pragma alloc_text(PAGE, KpiOpenThreadProcess) -#pragma alloc_text(PAGE, KphCaptureStackBackTraceThread) -#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThreadSpecialApc) -#pragma alloc_text(PAGE, KpiCaptureStackBackTraceThread) -#pragma alloc_text(PAGE, KpiQueryInformationThread) -#pragma alloc_text(PAGE, KpiSetInformationThread) -#endif - -/** - * Opens a thread. - * - * \param ThreadHandle A variable which receives the thread handle. - * \param DesiredAccess The desired access to the thread. - * \param ClientId The identifier of a thread. \a UniqueThread must be present. If \a UniqueProcess - * is present, the process of the referenced thread will be checked against this identifier. - * \param Key An access key. - * \li If a L2 key is provided, no access checks are performed. - * \li If a L1 key is provided, only read access is permitted but no additional access checks are - * performed. - * \li If no valid key is provided, the function fails. - * \param Client The client that initiated the request. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiOpenThread( - __out PHANDLE ThreadHandle, - __in ACCESS_MASK DesiredAccess, - __in PCLIENT_ID ClientId, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - CLIENT_ID clientId; - PETHREAD thread; - KPH_KEY_LEVEL requiredKeyLevel; - HANDLE threadHandle; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(ThreadHandle, sizeof(HANDLE), sizeof(HANDLE)); - ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG)); - clientId = *ClientId; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else - { - clientId = *ClientId; - } - - // Use the process ID if it was specified. - if (clientId.UniqueProcess) - { - status = PsLookupProcessThreadByCid(&clientId, NULL, &thread); - } - else - { - status = PsLookupThreadByThreadId(clientId.UniqueThread, &thread); - } - - if (!NT_SUCCESS(status)) - return status; - - - requiredKeyLevel = KphKeyLevel1; - - if ((DesiredAccess & KPH_THREAD_READ_ACCESS) != DesiredAccess) - requiredKeyLevel = KphKeyLevel2; - - if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode))) - { - // Always open in KernelMode to skip access checks. - status = ObOpenObjectByPointer( - thread, - 0, - NULL, - DesiredAccess, - *PsThreadType, - KernelMode, - &threadHandle - ); - } - - ObDereferenceObject(thread); - - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) - { - __try - { - *ThreadHandle = threadHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *ThreadHandle = threadHandle; - } - } - - return status; -} - -/** - * Opens the process of a thread. - * - * \param ThreadHandle A handle to a thread. - * \param DesiredAccess The desired access to the process. - * \param ProcessHandle A variable which receives the process handle. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiOpenThreadProcess( - __in HANDLE ThreadHandle, - __in ACCESS_MASK DesiredAccess, - __out PHANDLE ProcessHandle, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PETHREAD thread; - PEPROCESS process; - HANDLE processHandle; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(ProcessHandle, sizeof(HANDLE), sizeof(HANDLE)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ThreadHandle, - 0, - *PsThreadType, - AccessMode, - &thread, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - process = IoThreadToProcess(thread); - - status = ObOpenObjectByPointer( - process, - 0, - NULL, - DesiredAccess, - *PsProcessType, - AccessMode, - &processHandle - ); - - ObDereferenceObject(thread); - - if (NT_SUCCESS(status)) - { - if (AccessMode != KernelMode) - { - __try - { - *ProcessHandle = processHandle; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - *ProcessHandle = processHandle; - } - } - - return status; -} - -/** - * Captures a stack trace of the current thread. - * - * \param FramesToSkip The number of frames to skip from the bottom of the stack. - * \param FramesToCapture The number of frames to capture. - * \param Flags A combination of the following: - * \li \c RTL_WALK_USER_MODE_STACK The user-mode stack will be retrieved instead of the kernel-mode - * stack. - * \param BackTrace An array in which the stack trace will be stored. - * \param BackTraceHash A variable which receives a hash of the stack trace. - * - * \return The number of frames captured. - */ -ULONG KphCaptureStackBackTrace( - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __in_opt ULONG Flags, - __out_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG BackTraceHash - ) -{ - PVOID backTrace[MAX_STACK_DEPTH]; - ULONG framesFound; - ULONG hash; - ULONG i; - - // Skip the current frame (for this function). - FramesToSkip++; - - // Ensure that we won't overrun the buffer. - if (FramesToCapture + FramesToSkip > MAX_STACK_DEPTH) - return 0; - // Validate the flags. - if ((Flags & RTL_WALK_VALID_FLAGS) != Flags) - return 0; - - // Walk the stack. - framesFound = RtlWalkFrameChain( - backTrace, - FramesToCapture + FramesToSkip, - Flags - ); - // Return nothing if we found fewer frames than we wanted to skip. - if (framesFound <= FramesToSkip) - return 0; - - // Copy over the stack trace. At the same time we calculate the stack trace hash by summing the - // addresses. - for (i = 0, hash = 0; i < FramesToCapture; i++) - { - if (FramesToSkip + i >= framesFound) - break; - - BackTrace[i] = backTrace[FramesToSkip + i]; - hash += PtrToUlong(BackTrace[i]); - } - - if (BackTraceHash) - *BackTraceHash = hash; - - return i; -} - -/** - * Captures the stack trace of a thread. - * - * \param Thread The thread to capture the stack trace of. - * \param FramesToSkip The number of frames to skip from the bottom of the stack. - * \param FramesToCapture The number of frames to capture. - * \param BackTrace An array in which the stack trace will be stored. - * \param CapturedFrames A variable which receives the number of frames captured. - * \param BackTraceHash A variable which receives a hash of the stack trace. - * \param AccessMode The mode in which to perform access checks. - * - * \return The number of frames captured. - */ -NTSTATUS KphCaptureStackBackTraceThread( - __in PETHREAD Thread, - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __out_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG CapturedFrames, - __out_opt PULONG BackTraceHash, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status = STATUS_SUCCESS; - CAPTURE_BACKTRACE_THREAD_CONTEXT context; - ULONG backTraceSize; - PVOID *backTrace; - - PAGED_CODE(); - - // Make sure the caller didn't request too many frames. This also restricts the amount of memory - // we will try to allocate later. - if (FramesToCapture > MAX_STACK_DEPTH) - return STATUS_INVALID_PARAMETER_3; - - backTraceSize = FramesToCapture * sizeof(PVOID); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(BackTrace, backTraceSize, sizeof(PVOID)); - - if (CapturedFrames) - ProbeForWrite(CapturedFrames, sizeof(ULONG), sizeof(ULONG)); - if (BackTraceHash) - ProbeForWrite(BackTraceHash, sizeof(ULONG), sizeof(ULONG)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - // If the caller doesn't want to capture anything, return immediately. - if (backTraceSize == 0) - { - if (AccessMode != KernelMode) - { - __try - { - if (CapturedFrames) - *CapturedFrames = 0; - if (BackTraceHash) - *BackTraceHash = 0; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - if (CapturedFrames) - *CapturedFrames = 0; - if (BackTraceHash) - *BackTraceHash = 0; - } - - return status; - } - - // Allocate storage for the stack trace. - backTrace = ExAllocatePoolWithTag(NonPagedPool, backTraceSize, 'bhpK'); - - if (!backTrace) - return STATUS_INSUFFICIENT_RESOURCES; - - // Initialize the context structure. - context.FramesToSkip = FramesToSkip; - context.FramesToCapture = FramesToCapture; - context.BackTrace = backTrace; - - // Check if we're trying to get a stack trace of the current thread. - // If so, we don't need to insert an APC. - if (Thread == PsGetCurrentThread()) - { - PCAPTURE_BACKTRACE_THREAD_CONTEXT contextPtr = &context; - PVOID dummy = NULL; - KIRQL oldIrql; - - // Raise the IRQL to APC_LEVEL to simulate an APC environment, - // and call the APC routine directly. - - context.Local = TRUE; - KeRaiseIrql(APC_LEVEL, &oldIrql); - KphpCaptureStackBackTraceThreadSpecialApc( - &context.Apc, - NULL, - NULL, - &contextPtr, - &dummy - ); - KeLowerIrql(oldIrql); - } - else - { - context.Local = FALSE; - KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE); - KeInitializeApc( - &context.Apc, - (PKTHREAD)Thread, - OriginalApcEnvironment, - KphpCaptureStackBackTraceThreadSpecialApc, - NULL, - NULL, - KernelMode, - NULL - ); - - if (KeInsertQueueApc(&context.Apc, &context, NULL, 2)) - { - // Wait for the APC to complete. - status = KeWaitForSingleObject( - &context.CompletedEvent, - Executive, - KernelMode, - FALSE, - NULL - ); - } - else - { - status = STATUS_UNSUCCESSFUL; - } - } - - if (NT_SUCCESS(status)) - { - ASSERT(context.CapturedFrames <= FramesToCapture); - - if (AccessMode != KernelMode) - { - __try - { - memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID)); - - if (CapturedFrames) - *CapturedFrames = context.CapturedFrames; - if (BackTraceHash) - *BackTraceHash = context.BackTraceHash; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - } - else - { - memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID)); - - if (CapturedFrames) - *CapturedFrames = context.CapturedFrames; - if (BackTraceHash) - *BackTraceHash = context.BackTraceHash; - } - } - - ExFreePoolWithTag(backTrace, 'bhpK'); - - return status; -} - -VOID KphpCaptureStackBackTraceThreadSpecialApc( - __in PRKAPC Apc, - __inout PKNORMAL_ROUTINE *NormalRoutine, - __inout PVOID *NormalContext, - __inout PVOID *SystemArgument1, - __inout PVOID *SystemArgument2 - ) -{ - PCAPTURE_BACKTRACE_THREAD_CONTEXT context = *SystemArgument1; - - PAGED_CODE(); - - context->CapturedFrames = KphCaptureStackBackTrace( - context->FramesToSkip, - context->FramesToCapture, - 0, - context->BackTrace, - &context->BackTraceHash - ); - - if (!context->Local) - { - // Notify the originating thread that we have completed. - KeSetEvent(&context->CompletedEvent, 0, FALSE); - } -} - -/** - * Captures the stack trace of a thread. - * - * \param ThreadHandle A handle to the thread to capture the stack trace of. - * \param FramesToSkip The number of frames to skip from the bottom of the stack. - * \param FramesToCapture The number of frames to capture. - * \param BackTrace An array in which the stack trace will be stored. - * \param CapturedFrames A variable which receives the number of frames captured. - * \param BackTraceHash A variable which receives a hash of the stack trace. - * \param AccessMode The mode in which to perform access checks. - * - * \return The number of frames captured. - */ -NTSTATUS KpiCaptureStackBackTraceThread( - __in HANDLE ThreadHandle, - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __out_ecount(FramesToCapture) PVOID *BackTrace, - __out_opt PULONG CapturedFrames, - __out_opt PULONG BackTraceHash, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PETHREAD thread; - - PAGED_CODE(); - - status = ObReferenceObjectByHandle( - ThreadHandle, - 0, - *PsThreadType, - AccessMode, - &thread, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - status = KphCaptureStackBackTraceThread( - thread, - FramesToSkip, - FramesToCapture, - BackTrace, - CapturedFrames, - BackTraceHash, - AccessMode - ); - ObDereferenceObject(thread); - - return status; -} - -/** - * Queries thread information. - * - * \param ThreadHandle A handle to a thread. - * \param ThreadInformationClass The type of information to query. - * \param ThreadInformation The buffer in which the information will be stored. - * \param ThreadInformationLength The number of bytes available in \a ThreadInformation. - * \param ReturnLength A variable which receives the number of bytes required to be available in - * \a ThreadInformation. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiQueryInformationThread( - __in HANDLE ThreadHandle, - __in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - __out_bcount(ProcessInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __out_opt PULONG ReturnLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PETHREAD thread; - ULONG returnLength; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForWrite(ThreadInformation, ThreadInformationLength, sizeof(ULONG)); - - if (ReturnLength) - ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ThreadHandle, - THREAD_QUERY_INFORMATION, - *PsThreadType, - AccessMode, - &thread, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - switch (ThreadInformationClass) - { - default: - status = STATUS_INVALID_INFO_CLASS; - returnLength = 0; - break; - } - - ObDereferenceObject(thread); - - if (ReturnLength) - { - if (AccessMode != KernelMode) - { - __try - { - *ReturnLength = returnLength; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - NOTHING; - } - } - else - { - *ReturnLength = returnLength; - } - } - - return status; -} - -/** - * Sets thread information. - * - * \param ThreadHandle A handle to a thread. - * \param ThreadInformationClass The type of information to set. - * \param ThreadInformation A buffer which contains the information to set. - * \param ThreadInformationLength The number of bytes present in \a ThreadInformation. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiSetInformationThread( - __in HANDLE ThreadHandle, - __in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - __in_bcount(ThreadInformationLength) PVOID ThreadInformation, - __in ULONG ThreadInformationLength, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PETHREAD thread; - - PAGED_CODE(); - - if (AccessMode != KernelMode) - { - __try - { - ProbeForRead(ThreadInformation, ThreadInformationLength, sizeof(ULONG)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - status = ObReferenceObjectByHandle( - ThreadHandle, - THREAD_SET_INFORMATION, - *PsThreadType, - AccessMode, - &thread, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - switch (ThreadInformationClass) - { - default: - status = STATUS_INVALID_INFO_CLASS; - break; - } - - ObDereferenceObject(thread); - - return status; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT +{ + BOOLEAN Local; + KAPC Apc; + KEVENT CompletedEvent; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + ULONG CapturedFrames; + ULONG BackTraceHash; +} CAPTURE_BACKTRACE_THREAD_CONTEXT, *PCAPTURE_BACKTRACE_THREAD_CONTEXT; + +KKERNEL_ROUTINE KphpCaptureStackBackTraceThreadSpecialApc; + +VOID KphpCaptureStackBackTraceThreadSpecialApc( + _In_ PRKAPC Apc, + _Inout_opt_ PKNORMAL_ROUTINE *NormalRoutine, + _Inout_opt_ PVOID *NormalContext, + _Inout_ PVOID *SystemArgument1, + _Inout_ PVOID *SystemArgument2 + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KpiOpenThread) +#pragma alloc_text(PAGE, KpiOpenThreadProcess) +#pragma alloc_text(PAGE, KphCaptureStackBackTraceThread) +#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThreadSpecialApc) +#pragma alloc_text(PAGE, KpiCaptureStackBackTraceThread) +#pragma alloc_text(PAGE, KpiQueryInformationThread) +#pragma alloc_text(PAGE, KpiSetInformationThread) +#endif + +/** + * Opens a thread. + * + * \param ThreadHandle A variable which receives the thread handle. + * \param DesiredAccess The desired access to the thread. + * \param ClientId The identifier of a thread. \a UniqueThread must be present. If \a UniqueProcess + * is present, the process of the referenced thread will be checked against this identifier. + * \param Key An access key. + * \li If a L2 key is provided, no access checks are performed. + * \li If a L1 key is provided, only read access is permitted but no additional access checks are + * performed. + * \li If no valid key is provided, the function fails. + * \param Client The client that initiated the request. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + CLIENT_ID clientId; + PETHREAD thread; + KPH_KEY_LEVEL requiredKeyLevel; + HANDLE threadHandle = NULL; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ThreadHandle, sizeof(HANDLE), sizeof(HANDLE)); + ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG)); + clientId = *ClientId; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + clientId = *ClientId; + } + + // Use the process ID if it was specified. + if (clientId.UniqueProcess) + { + status = PsLookupProcessThreadByCid(&clientId, NULL, &thread); + } + else + { + status = PsLookupThreadByThreadId(clientId.UniqueThread, &thread); + } + + if (!NT_SUCCESS(status)) + return status; + + + requiredKeyLevel = KphKeyLevel1; + + if ((DesiredAccess & KPH_THREAD_READ_ACCESS) != DesiredAccess) + requiredKeyLevel = KphKeyLevel2; + + if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode))) + { + // Always open in KernelMode to skip access checks. + status = ObOpenObjectByPointer( + thread, + 0, + NULL, + DesiredAccess, + *PsThreadType, + KernelMode, + &threadHandle + ); + } + + ObDereferenceObject(thread); + + if (NT_SUCCESS(status)) + { + if (AccessMode != KernelMode) + { + __try + { + *ThreadHandle = threadHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *ThreadHandle = threadHandle; + } + } + + return status; +} + +/** + * Opens the process of a thread. + * + * \param ThreadHandle A handle to a thread. + * \param DesiredAccess The desired access to the process. + * \param ProcessHandle A variable which receives the process handle. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiOpenThreadProcess( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE ProcessHandle, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PETHREAD thread; + PEPROCESS process; + HANDLE processHandle; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ProcessHandle, sizeof(HANDLE), sizeof(HANDLE)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ThreadHandle, + 0, + *PsThreadType, + AccessMode, + &thread, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + process = IoThreadToProcess(thread); + + status = ObOpenObjectByPointer( + process, + 0, + NULL, + DesiredAccess, + *PsProcessType, + AccessMode, + &processHandle + ); + + ObDereferenceObject(thread); + + if (NT_SUCCESS(status)) + { + if (AccessMode != KernelMode) + { + __try + { + *ProcessHandle = processHandle; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + *ProcessHandle = processHandle; + } + } + + return status; +} + +/** + * Captures a stack trace of the current thread. + * + * \param FramesToSkip The number of frames to skip from the bottom of the stack. + * \param FramesToCapture The number of frames to capture. + * \param Flags A combination of the following: + * \li \c RTL_WALK_USER_MODE_STACK The user-mode stack will be retrieved instead of the kernel-mode + * stack. + * \param BackTrace An array in which the stack trace will be stored. + * \param BackTraceHash A variable which receives a hash of the stack trace. + * + * \return The number of frames captured. + */ +ULONG KphCaptureStackBackTrace( + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _In_opt_ ULONG Flags, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG BackTraceHash + ) +{ + PVOID backTrace[MAX_STACK_DEPTH]; + ULONG framesFound; + ULONG hash; + ULONG i; + + // Skip the current frame (for this function). + FramesToSkip++; + + // Ensure that we won't overrun the buffer. + if (FramesToCapture + FramesToSkip > MAX_STACK_DEPTH) + return 0; + // Validate the flags. + if ((Flags & RTL_WALK_VALID_FLAGS) != Flags) + return 0; + + // Walk the stack. + framesFound = RtlWalkFrameChain( + backTrace, + FramesToCapture + FramesToSkip, + Flags + ); + // Return nothing if we found fewer frames than we wanted to skip. + if (framesFound <= FramesToSkip) + return 0; + + // Copy over the stack trace. At the same time we calculate the stack trace hash by summing the + // addresses. + for (i = 0, hash = 0; i < FramesToCapture; i++) + { + if (FramesToSkip + i >= framesFound) + break; + + BackTrace[i] = backTrace[FramesToSkip + i]; + hash += PtrToUlong(BackTrace[i]); + } + + if (BackTraceHash) + *BackTraceHash = hash; + + return i; +} + +/** + * Captures the stack trace of a thread. + * + * \param Thread The thread to capture the stack trace of. + * \param FramesToSkip The number of frames to skip from the bottom of the stack. + * \param FramesToCapture The number of frames to capture. + * \param BackTrace An array in which the stack trace will be stored. + * \param CapturedFrames A variable which receives the number of frames captured. + * \param BackTraceHash A variable which receives a hash of the stack trace. + * \param AccessMode The mode in which to perform access checks. + * + * \return The number of frames captured. + */ +NTSTATUS KphCaptureStackBackTraceThread( + _In_ PETHREAD Thread, + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG CapturedFrames, + _Out_opt_ PULONG BackTraceHash, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + CAPTURE_BACKTRACE_THREAD_CONTEXT context; + ULONG backTraceSize; + PVOID *backTrace; + + PAGED_CODE(); + + // Make sure the caller didn't request too many frames. This also restricts the amount of memory + // we will try to allocate later. + if (FramesToCapture > MAX_STACK_DEPTH) + return STATUS_INVALID_PARAMETER_3; + + backTraceSize = FramesToCapture * sizeof(PVOID); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(BackTrace, backTraceSize, sizeof(PVOID)); + + if (CapturedFrames) + ProbeForWrite(CapturedFrames, sizeof(ULONG), sizeof(ULONG)); + if (BackTraceHash) + ProbeForWrite(BackTraceHash, sizeof(ULONG), sizeof(ULONG)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + // If the caller doesn't want to capture anything, return immediately. + if (backTraceSize == 0) + { + if (AccessMode != KernelMode) + { + __try + { + if (CapturedFrames) + *CapturedFrames = 0; + if (BackTraceHash) + *BackTraceHash = 0; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + if (CapturedFrames) + *CapturedFrames = 0; + if (BackTraceHash) + *BackTraceHash = 0; + } + + return status; + } + + // Allocate storage for the stack trace. + backTrace = ExAllocatePoolWithTag(NonPagedPool, backTraceSize, 'bhpK'); + + if (!backTrace) + return STATUS_INSUFFICIENT_RESOURCES; + + // Initialize the context structure. + context.FramesToSkip = FramesToSkip; + context.FramesToCapture = FramesToCapture; + context.BackTrace = backTrace; + + // Check if we're trying to get a stack trace of the current thread. + // If so, we don't need to insert an APC. + if (Thread == PsGetCurrentThread()) + { + PCAPTURE_BACKTRACE_THREAD_CONTEXT contextPtr = &context; + PVOID dummy = NULL; + KIRQL oldIrql; + + // Raise the IRQL to APC_LEVEL to simulate an APC environment, + // and call the APC routine directly. + + context.Local = TRUE; + KeRaiseIrql(APC_LEVEL, &oldIrql); + KphpCaptureStackBackTraceThreadSpecialApc( + &context.Apc, + NULL, + NULL, + &contextPtr, + &dummy + ); + KeLowerIrql(oldIrql); + } + else + { + context.Local = FALSE; + KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE); + KeInitializeApc( + &context.Apc, + (PKTHREAD)Thread, + OriginalApcEnvironment, + KphpCaptureStackBackTraceThreadSpecialApc, + NULL, + NULL, + KernelMode, + NULL + ); + + if (KeInsertQueueApc(&context.Apc, &context, NULL, 2)) + { + // Wait for the APC to complete. + status = KeWaitForSingleObject( + &context.CompletedEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + } + + if (NT_SUCCESS(status)) + { + ASSERT(context.CapturedFrames <= FramesToCapture); + + if (AccessMode != KernelMode) + { + __try + { + memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID)); + + if (CapturedFrames) + *CapturedFrames = context.CapturedFrames; + if (BackTraceHash) + *BackTraceHash = context.BackTraceHash; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + } + else + { + memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID)); + + if (CapturedFrames) + *CapturedFrames = context.CapturedFrames; + if (BackTraceHash) + *BackTraceHash = context.BackTraceHash; + } + } + + ExFreePoolWithTag(backTrace, 'bhpK'); + + return status; +} + +VOID KphpCaptureStackBackTraceThreadSpecialApc( + _In_ PRKAPC Apc, + _Inout_opt_ PKNORMAL_ROUTINE *NormalRoutine, + _Inout_opt_ PVOID *NormalContext, + _Inout_ PVOID *SystemArgument1, + _Inout_ PVOID *SystemArgument2 + ) +{ + PCAPTURE_BACKTRACE_THREAD_CONTEXT context = *SystemArgument1; + + PAGED_CODE(); + + context->CapturedFrames = KphCaptureStackBackTrace( + context->FramesToSkip, + context->FramesToCapture, + 0, + context->BackTrace, + &context->BackTraceHash + ); + + if (!context->Local) + { + // Notify the originating thread that we have completed. + KeSetEvent(&context->CompletedEvent, 0, FALSE); + } +} + +/** + * Captures the stack trace of a thread. + * + * \param ThreadHandle A handle to the thread to capture the stack trace of. + * \param FramesToSkip The number of frames to skip from the bottom of the stack. + * \param FramesToCapture The number of frames to capture. + * \param BackTrace An array in which the stack trace will be stored. + * \param CapturedFrames A variable which receives the number of frames captured. + * \param BackTraceHash A variable which receives a hash of the stack trace. + * \param AccessMode The mode in which to perform access checks. + * + * \return The number of frames captured. + */ +NTSTATUS KpiCaptureStackBackTraceThread( + _In_ HANDLE ThreadHandle, + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG CapturedFrames, + _Out_opt_ PULONG BackTraceHash, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PETHREAD thread; + + PAGED_CODE(); + + status = ObReferenceObjectByHandle( + ThreadHandle, + 0, + *PsThreadType, + AccessMode, + &thread, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = KphCaptureStackBackTraceThread( + thread, + FramesToSkip, + FramesToCapture, + BackTrace, + CapturedFrames, + BackTraceHash, + AccessMode + ); + ObDereferenceObject(thread); + + return status; +} + +/** + * Queries thread information. + * + * \param ThreadHandle A handle to a thread. + * \param ThreadInformationClass The type of information to query. + * \param ThreadInformation The buffer in which the information will be stored. + * \param ThreadInformationLength The number of bytes available in \a ThreadInformation. + * \param ReturnLength A variable which receives the number of bytes required to be available in + * \a ThreadInformation. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PETHREAD thread; + ULONG returnLength; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForWrite(ThreadInformation, ThreadInformationLength, sizeof(ULONG)); + + if (ReturnLength) + ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_QUERY_INFORMATION, + *PsThreadType, + AccessMode, + &thread, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + switch (ThreadInformationClass) + { + default: + status = STATUS_INVALID_INFO_CLASS; + returnLength = 0; + break; + } + + ObDereferenceObject(thread); + + if (ReturnLength) + { + if (AccessMode != KernelMode) + { + __try + { + *ReturnLength = returnLength; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + NOTHING; + } + } + else + { + *ReturnLength = returnLength; + } + } + + return status; +} + +/** + * Sets thread information. + * + * \param ThreadHandle A handle to a thread. + * \param ThreadInformationClass The type of information to set. + * \param ThreadInformation A buffer which contains the information to set. + * \param ThreadInformationLength The number of bytes present in \a ThreadInformation. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiSetInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PETHREAD thread; + + PAGED_CODE(); + + if (AccessMode != KernelMode) + { + __try + { + ProbeForRead(ThreadInformation, ThreadInformationLength, sizeof(ULONG)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + status = ObReferenceObjectByHandle( + ThreadHandle, + THREAD_SET_INFORMATION, + *PsThreadType, + AccessMode, + &thread, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + switch (ThreadInformationClass) + { + default: + status = STATUS_INVALID_INFO_CLASS; + break; + } + + ObDereferenceObject(thread); + + return status; +} diff --git a/KProcessHacker/util.c b/KProcessHacker/util.c index b30ae7e6bcd6..d372284bda48 100644 --- a/KProcessHacker/util.c +++ b/KProcessHacker/util.c @@ -30,7 +30,7 @@ #endif VOID KphFreeCapturedUnicodeString( - __in PUNICODE_STRING CapturedUnicodeString + _In_ PUNICODE_STRING CapturedUnicodeString ) { PAGED_CODE(); @@ -40,8 +40,8 @@ VOID KphFreeCapturedUnicodeString( } NTSTATUS KphCaptureUnicodeString( - __in PUNICODE_STRING UnicodeString, - __out PUNICODE_STRING CapturedUnicodeString + _In_ PUNICODE_STRING UnicodeString, + _Out_ PUNICODE_STRING CapturedUnicodeString ) { UNICODE_STRING unicodeString; @@ -107,7 +107,7 @@ NTSTATUS KphCaptureUnicodeString( * the kernel modules. The structure must be freed with the tag 'ThpK'. */ NTSTATUS KphEnumerateSystemModules( - __out PRTL_PROCESS_MODULES *Modules + _Out_ PRTL_PROCESS_MODULES *Modules ) { NTSTATUS status; @@ -162,8 +162,8 @@ NTSTATUS KphEnumerateSystemModules( * \param Length The number of bytes in the address range. */ NTSTATUS KphValidateAddressForSystemModules( - __in PVOID Address, - __in SIZE_T Length + _In_ PVOID Address, + _In_ SIZE_T Length ) { NTSTATUS status; @@ -214,9 +214,9 @@ NTSTATUS KphValidateAddressForSystemModules( * section. The structure must be freed with the tag 'ThpK'. */ NTSTATUS KphGetProcessMappedFileName( - __in HANDLE ProcessHandle, - __in PVOID BaseAddress, - __out PUNICODE_STRING *FileName + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_ PUNICODE_STRING *FileName ) { NTSTATUS status; diff --git a/KProcessHacker/verify.c b/KProcessHacker/verify.c index 08ccbf15d82f..8756f17f2afb 100644 --- a/KProcessHacker/verify.c +++ b/KProcessHacker/verify.c @@ -25,7 +25,7 @@ #define FILE_MAX_SIZE (32 * 1024 * 1024) // 32 MB VOID KphpBackoffKey( - __in PKPH_CLIENT Client + _In_ PKPH_CLIENT Client ); static UCHAR KphpTrustedPublicKey[] = @@ -49,9 +49,9 @@ static UCHAR KphpTrustedPublicKey[] = #endif NTSTATUS KphHashFile( - __in PUNICODE_STRING FileName, - __out PVOID *Hash, - __out PULONG HashSize + _In_ PUNICODE_STRING FileName, + _Out_ PVOID *Hash, + _Out_ PULONG HashSize ) { NTSTATUS status; @@ -192,9 +192,9 @@ NTSTATUS KphHashFile( } NTSTATUS KphVerifyFile( - __in PUNICODE_STRING FileName, - __in_bcount(SignatureSize) PUCHAR Signature, - __in ULONG SignatureSize + _In_ PUNICODE_STRING FileName, + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize ) { NTSTATUS status; @@ -240,10 +240,10 @@ NTSTATUS KphVerifyFile( } VOID KphVerifyClient( - __inout PKPH_CLIENT Client, - __in PVOID CodeAddress, - __in_bcount(SignatureSize) PUCHAR Signature, - __in ULONG SignatureSize + _Inout_ PKPH_CLIENT Client, + _In_ PVOID CodeAddress, + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize ) { NTSTATUS status; @@ -311,10 +311,10 @@ VOID KphVerifyClient( } NTSTATUS KpiVerifyClient( - __in PVOID CodeAddress, - __in_bcount(SignatureSize) PUCHAR Signature, - __in ULONG SignatureSize, - __in PKPH_CLIENT Client + _In_ PVOID CodeAddress, + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize, + _In_ PKPH_CLIENT Client ) { PUCHAR signature; @@ -347,14 +347,14 @@ NTSTATUS KpiVerifyClient( return GetExceptionCode(); } - KphVerifyClient(Client, CodeAddress, Signature, SignatureSize); + KphVerifyClient(Client, CodeAddress, signature, SignatureSize); ExFreePoolWithTag(signature, 'ThpK'); return Client->VerificationStatus; } VOID KphGenerateKeysClient( - __inout PKPH_CLIENT Client + _Inout_ PKPH_CLIENT Client ) { ULONGLONG interruptTime; @@ -389,9 +389,9 @@ VOID KphGenerateKeysClient( } NTSTATUS KphRetrieveKeyViaApc( - __inout PKPH_CLIENT Client, - __in KPH_KEY_LEVEL KeyLevel, - __inout PIRP Irp + _Inout_ PKPH_CLIENT Client, + _In_ KPH_KEY_LEVEL KeyLevel, + _Inout_ PIRP Irp ) { PIO_APC_ROUTINE userApcRoutine; @@ -440,10 +440,10 @@ NTSTATUS KphRetrieveKeyViaApc( } NTSTATUS KphValidateKey( - __in KPH_KEY_LEVEL RequiredKeyLevel, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode + _In_ KPH_KEY_LEVEL RequiredKeyLevel, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode ) { PAGED_CODE(); @@ -478,7 +478,7 @@ NTSTATUS KphValidateKey( } VOID KphpBackoffKey( - __in PKPH_CLIENT Client + _In_ PKPH_CLIENT Client ) { LARGE_INTEGER backoffTime; diff --git a/KProcessHacker/vm.c b/KProcessHacker/vm.c index 0ffbb578181d..32f0057a4089 100644 --- a/KProcessHacker/vm.c +++ b/KProcessHacker/vm.c @@ -1,449 +1,449 @@ -/* - * KProcessHacker - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -ULONG KphpGetCopyExceptionInfo( - __in PEXCEPTION_POINTERS ExceptionInfo, - __out PBOOLEAN HaveBadAddress, - __out PULONG_PTR BadAddress - ); - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, KphCopyVirtualMemory) -#pragma alloc_text(PAGE, KpiReadVirtualMemoryUnsafe) -#endif - -#define KPH_STACK_COPY_BYTES 0x200 -#define KPH_POOL_COPY_BYTES 0x10000 -#define KPH_MAPPED_COPY_PAGES 14 -#define KPH_POOL_COPY_THRESHOLD 0x3ff - -ULONG KphpGetCopyExceptionInfo( - __in PEXCEPTION_POINTERS ExceptionInfo, - __out PBOOLEAN HaveBadAddress, - __out PULONG_PTR BadAddress - ) -{ - PEXCEPTION_RECORD exceptionRecord; - - *HaveBadAddress = FALSE; - exceptionRecord = ExceptionInfo->ExceptionRecord; - - if ((exceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || - (exceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || - (exceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) - { - if (exceptionRecord->NumberParameters > 1) - { - /* We have the address. */ - *HaveBadAddress = TRUE; - *BadAddress = exceptionRecord->ExceptionInformation[1]; - } - } - - return EXCEPTION_EXECUTE_HANDLER; -} - -/** - * Copies memory from one process to another. - * - * \param FromProcess The source process. - * \param FromAddress The source address. - * \param ToProcess The target process. - * \param ToAddress The target address. - * \param BufferLength The number of bytes to copy. - * \param AccessMode The mode in which to perform access checks. - * \param ReturnLength A variable which receives the number of bytes copied. - */ -NTSTATUS KphCopyVirtualMemory( - __in PEPROCESS FromProcess, - __in PVOID FromAddress, - __in PEPROCESS ToProcess, - __in PVOID ToAddress, - __in SIZE_T BufferLength, - __in KPROCESSOR_MODE AccessMode, - __out PSIZE_T ReturnLength - ) -{ - UCHAR stackBuffer[KPH_STACK_COPY_BYTES]; - PVOID buffer; - PFN_NUMBER mdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + KPH_MAPPED_COPY_PAGES + 1]; - PMDL mdl = (PMDL)mdlBuffer; - PVOID mappedAddress; - SIZE_T mappedTotalSize; - SIZE_T blockSize; - SIZE_T stillToCopy; - KAPC_STATE apcState; - PVOID sourceAddress; - PVOID targetAddress; - BOOLEAN doMappedCopy; - BOOLEAN pagesLocked; - BOOLEAN copyingToTarget = FALSE; - BOOLEAN probing = FALSE; - BOOLEAN mapping = FALSE; - BOOLEAN haveBadAddress; - ULONG_PTR badAddress; - - PAGED_CODE(); - - sourceAddress = FromAddress; - targetAddress = ToAddress; - - // We don't check if buffer == NULL when freeing. If buffer doesn't need to be freed, set to - // stackBuffer, not NULL. - buffer = stackBuffer; - - mappedTotalSize = (KPH_MAPPED_COPY_PAGES - 2) * PAGE_SIZE; - - if (mappedTotalSize > BufferLength) - mappedTotalSize = BufferLength; - - stillToCopy = BufferLength; - blockSize = mappedTotalSize; - - while (stillToCopy) - { - // If we're at the last copy block, copy the remaining bytes instead of the whole block - // size. - if (blockSize > stillToCopy) - blockSize = stillToCopy; - - // Choose the best method based on the number of bytes left to copy. - if (blockSize > KPH_POOL_COPY_THRESHOLD) - { - doMappedCopy = TRUE; - } - else - { - doMappedCopy = FALSE; - - if (blockSize <= KPH_STACK_COPY_BYTES) - { - if (buffer != stackBuffer) - ExFreePoolWithTag(buffer, 'ChpK'); - - buffer = stackBuffer; - } - else - { - // Don't allocate the buffer if we've done so already. Note that the block size - // never increases, so this allocation will always be OK. - if (buffer == stackBuffer) - { - // Keep trying to allocate a buffer. - - while (TRUE) - { - buffer = ExAllocatePoolWithTag(NonPagedPool, blockSize, 'ChpK'); - - // Stop trying if we got a buffer. - if (buffer) - break; - - blockSize /= 2; - - // Use the stack buffer if we can. - if (blockSize <= KPH_STACK_COPY_BYTES) - { - buffer = stackBuffer; - break; - } - } - } - } - } - - // Reset state. - mappedAddress = NULL; - pagesLocked = FALSE; - copyingToTarget = FALSE; - - KeStackAttachProcess(FromProcess, &apcState); - - __try - { - // Probe only if this is the first time. - if (sourceAddress == FromAddress && AccessMode != KernelMode) - { - probing = TRUE; - ProbeForRead(sourceAddress, BufferLength, sizeof(UCHAR)); - probing = FALSE; - } - - if (doMappedCopy) - { - // Initialize the MDL. - MmInitializeMdl(mdl, sourceAddress, blockSize); - MmProbeAndLockPages(mdl, AccessMode, IoReadAccess); - pagesLocked = TRUE; - - // Map the pages. - mappedAddress = MmMapLockedPagesSpecifyCache( - mdl, - KernelMode, - MmCached, - NULL, - FALSE, - HighPagePriority - ); - - if (!mappedAddress) - { - // Insufficient resources; exit. - mapping = TRUE; - ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); - } - } - else - { - memcpy(buffer, sourceAddress, blockSize); - } - - KeUnstackDetachProcess(&apcState); - - // Attach to the target process and copy the contents out. - KeStackAttachProcess(ToProcess, &apcState); - - // Probe only if this is the first time. - if (targetAddress == ToAddress && AccessMode != KernelMode) - { - probing = TRUE; - ProbeForWrite(targetAddress, BufferLength, sizeof(UCHAR)); - probing = FALSE; - } - - // Copy the data. - copyingToTarget = TRUE; - - if (doMappedCopy) - memcpy(targetAddress, mappedAddress, blockSize); - else - memcpy(targetAddress, buffer, blockSize); - } - __except (KphpGetCopyExceptionInfo( - GetExceptionInformation(), - &haveBadAddress, - &badAddress - )) - { - KeUnstackDetachProcess(&apcState); - - // If we mapped the pages, unmap them. - if (mappedAddress) - MmUnmapLockedPages(mappedAddress, mdl); - - // If we locked the pages, unlock them. - if (pagesLocked) - MmUnlockPages(mdl); - - // If we allocated pool storage, free it. - if (buffer != stackBuffer) - ExFreePoolWithTag(buffer, 'ChpK'); - - // If we failed when probing or mapping, return the error status. - if (probing || mapping) - return GetExceptionCode(); - - // Determine which copy failed. - if (copyingToTarget && haveBadAddress) - { - *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress); - } - else - { - *ReturnLength = BufferLength - stillToCopy; - } - - return STATUS_PARTIAL_COPY; - } - - KeUnstackDetachProcess(&apcState); - - if (doMappedCopy) - { - MmUnmapLockedPages(mappedAddress, mdl); - MmUnlockPages(mdl); - } - - stillToCopy -= blockSize; - sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize); - targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize); - } - - if (buffer != stackBuffer) - ExFreePoolWithTag(buffer, 'ChpK'); - - *ReturnLength = BufferLength; - - return STATUS_SUCCESS; -} - -/** - * Copies process or kernel memory into the current process. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_VM_READ access. This - * parameter may be NULL if \a BaseAddress lies above the user-mode range. - * \param BaseAddress The address from which memory is to be copied. - * \param Buffer A buffer which receives the copied memory. - * \param BufferSize The number of bytes to copy. - * \param NumberOfBytesRead A variable which receives the number of bytes copied to the buffer. - * \param Key An access key. If no valid L2 key is provided, the function fails. - * \param Client The client that initiated the request. - * \param AccessMode The mode in which to perform access checks. - */ -NTSTATUS KpiReadVirtualMemoryUnsafe( - __in_opt HANDLE ProcessHandle, - __in PVOID BaseAddress, - __out_bcount(BufferSize) PVOID Buffer, - __in SIZE_T BufferSize, - __out_opt PSIZE_T NumberOfBytesRead, - __in_opt KPH_KEY Key, - __in PKPH_CLIENT Client, - __in KPROCESSOR_MODE AccessMode - ) -{ - NTSTATUS status; - PEPROCESS process; - SIZE_T numberOfBytesRead; - - PAGED_CODE(); - - if (!NT_SUCCESS(status = KphValidateKey(KphKeyLevel2, Key, Client, AccessMode))) - return status; - - if (AccessMode != KernelMode) - { - if ( - (ULONG_PTR)BaseAddress + BufferSize < (ULONG_PTR)BaseAddress || - (ULONG_PTR)Buffer + BufferSize < (ULONG_PTR)Buffer || - (ULONG_PTR)Buffer + BufferSize > (ULONG_PTR)MmHighestUserAddress - ) - { - return STATUS_ACCESS_VIOLATION; - } - - if (NumberOfBytesRead) - { - __try - { - ProbeForWrite(NumberOfBytesRead, sizeof(SIZE_T), sizeof(SIZE_T)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - } - - if (BufferSize != 0) - { - // Select the appropriate copy method. - if ((ULONG_PTR)BaseAddress + BufferSize > (ULONG_PTR)MmHighestUserAddress) - { - ULONG_PTR page; - ULONG_PTR pageEnd; - - status = KphValidateAddressForSystemModules(BaseAddress, BufferSize); - - if (!NT_SUCCESS(status)) - return status; - - // Kernel memory copy (unsafe) - - page = (ULONG_PTR)BaseAddress & ~(PAGE_SIZE - 1); - pageEnd = ((ULONG_PTR)BaseAddress + BufferSize - 1) & ~(PAGE_SIZE - 1); - - __try - { - // This will obviously fail if any of the pages aren't resident. - for (; page <= pageEnd; page += PAGE_SIZE) - { - if (!MmIsAddressValid((PVOID)page)) - ExRaiseStatus(STATUS_ACCESS_VIOLATION); - } - - memcpy(Buffer, BaseAddress, BufferSize); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - numberOfBytesRead = BufferSize; - status = STATUS_SUCCESS; - } - else - { - // User memory copy (safe) - - status = ObReferenceObjectByHandle( - ProcessHandle, - PROCESS_VM_READ, - *PsProcessType, - AccessMode, - &process, - NULL - ); - - if (NT_SUCCESS(status)) - { - status = KphCopyVirtualMemory( - process, - BaseAddress, - PsGetCurrentProcess(), - Buffer, - BufferSize, - AccessMode, - &numberOfBytesRead - ); - ObDereferenceObject(process); - } - } - } - else - { - numberOfBytesRead = 0; - status = STATUS_SUCCESS; - } - - if (NumberOfBytesRead) - { - if (AccessMode != KernelMode) - { - __try - { - *NumberOfBytesRead = numberOfBytesRead; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - // Don't mess with the status. - NOTHING; - } - } - else - { - *NumberOfBytesRead = numberOfBytesRead; - } - } - - return status; -} +/* + * KProcessHacker + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +ULONG KphpGetCopyExceptionInfo( + _In_ PEXCEPTION_POINTERS ExceptionInfo, + _Out_ PBOOLEAN HaveBadAddress, + _Out_ PULONG_PTR BadAddress + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, KphCopyVirtualMemory) +#pragma alloc_text(PAGE, KpiReadVirtualMemoryUnsafe) +#endif + +#define KPH_STACK_COPY_BYTES 0x200 +#define KPH_POOL_COPY_BYTES 0x10000 +#define KPH_MAPPED_COPY_PAGES 14 +#define KPH_POOL_COPY_THRESHOLD 0x3ff + +ULONG KphpGetCopyExceptionInfo( + _In_ PEXCEPTION_POINTERS ExceptionInfo, + _Out_ PBOOLEAN HaveBadAddress, + _Out_ PULONG_PTR BadAddress + ) +{ + PEXCEPTION_RECORD exceptionRecord; + + *HaveBadAddress = FALSE; + exceptionRecord = ExceptionInfo->ExceptionRecord; + + if ((exceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || + (exceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || + (exceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) + { + if (exceptionRecord->NumberParameters > 1) + { + /* We have the address. */ + *HaveBadAddress = TRUE; + *BadAddress = exceptionRecord->ExceptionInformation[1]; + } + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +/** + * Copies memory from one process to another. + * + * \param FromProcess The source process. + * \param FromAddress The source address. + * \param ToProcess The target process. + * \param ToAddress The target address. + * \param BufferLength The number of bytes to copy. + * \param AccessMode The mode in which to perform access checks. + * \param ReturnLength A variable which receives the number of bytes copied. + */ +NTSTATUS KphCopyVirtualMemory( + _In_ PEPROCESS FromProcess, + _In_ PVOID FromAddress, + _In_ PEPROCESS ToProcess, + _In_ PVOID ToAddress, + _In_ SIZE_T BufferLength, + _In_ KPROCESSOR_MODE AccessMode, + _Out_ PSIZE_T ReturnLength + ) +{ + UCHAR stackBuffer[KPH_STACK_COPY_BYTES]; + PVOID buffer; + PFN_NUMBER mdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + KPH_MAPPED_COPY_PAGES + 1]; + PMDL mdl = (PMDL)mdlBuffer; + PVOID mappedAddress; + SIZE_T mappedTotalSize; + SIZE_T blockSize; + SIZE_T stillToCopy; + KAPC_STATE apcState; + PVOID sourceAddress; + PVOID targetAddress; + BOOLEAN doMappedCopy; + BOOLEAN pagesLocked; + BOOLEAN copyingToTarget = FALSE; + BOOLEAN probing = FALSE; + BOOLEAN mapping = FALSE; + BOOLEAN haveBadAddress; + ULONG_PTR badAddress; + + PAGED_CODE(); + + sourceAddress = FromAddress; + targetAddress = ToAddress; + + // We don't check if buffer == NULL when freeing. If buffer doesn't need to be freed, set to + // stackBuffer, not NULL. + buffer = stackBuffer; + + mappedTotalSize = (KPH_MAPPED_COPY_PAGES - 2) * PAGE_SIZE; + + if (mappedTotalSize > BufferLength) + mappedTotalSize = BufferLength; + + stillToCopy = BufferLength; + blockSize = mappedTotalSize; + + while (stillToCopy) + { + // If we're at the last copy block, copy the remaining bytes instead of the whole block + // size. + if (blockSize > stillToCopy) + blockSize = stillToCopy; + + // Choose the best method based on the number of bytes left to copy. + if (blockSize > KPH_POOL_COPY_THRESHOLD) + { + doMappedCopy = TRUE; + } + else + { + doMappedCopy = FALSE; + + if (blockSize <= KPH_STACK_COPY_BYTES) + { + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, 'ChpK'); + + buffer = stackBuffer; + } + else + { + // Don't allocate the buffer if we've done so already. Note that the block size + // never increases, so this allocation will always be OK. + if (buffer == stackBuffer) + { + // Keep trying to allocate a buffer. + + while (TRUE) + { + buffer = ExAllocatePoolWithTag(NonPagedPool, blockSize, 'ChpK'); + + // Stop trying if we got a buffer. + if (buffer) + break; + + blockSize /= 2; + + // Use the stack buffer if we can. + if (blockSize <= KPH_STACK_COPY_BYTES) + { + buffer = stackBuffer; + break; + } + } + } + } + } + + // Reset state. + mappedAddress = NULL; + pagesLocked = FALSE; + copyingToTarget = FALSE; + + KeStackAttachProcess(FromProcess, &apcState); + + __try + { + // Probe only if this is the first time. + if (sourceAddress == FromAddress && AccessMode != KernelMode) + { + probing = TRUE; + ProbeForRead(sourceAddress, BufferLength, sizeof(UCHAR)); + probing = FALSE; + } + + if (doMappedCopy) + { + // Initialize the MDL. + MmInitializeMdl(mdl, sourceAddress, blockSize); + MmProbeAndLockPages(mdl, AccessMode, IoReadAccess); + pagesLocked = TRUE; + + // Map the pages. + mappedAddress = MmMapLockedPagesSpecifyCache( + mdl, + KernelMode, + MmCached, + NULL, + FALSE, + HighPagePriority + ); + + if (!mappedAddress) + { + // Insufficient resources; exit. + mapping = TRUE; + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + } + } + else + { + memcpy(buffer, sourceAddress, blockSize); + } + + KeUnstackDetachProcess(&apcState); + + // Attach to the target process and copy the contents out. + KeStackAttachProcess(ToProcess, &apcState); + + // Probe only if this is the first time. + if (targetAddress == ToAddress && AccessMode != KernelMode) + { + probing = TRUE; + ProbeForWrite(targetAddress, BufferLength, sizeof(UCHAR)); + probing = FALSE; + } + + // Copy the data. + copyingToTarget = TRUE; + + if (doMappedCopy) + memcpy(targetAddress, mappedAddress, blockSize); + else + memcpy(targetAddress, buffer, blockSize); + } + __except (KphpGetCopyExceptionInfo( + GetExceptionInformation(), + &haveBadAddress, + &badAddress + )) + { + KeUnstackDetachProcess(&apcState); + + // If we mapped the pages, unmap them. + if (mappedAddress) + MmUnmapLockedPages(mappedAddress, mdl); + + // If we locked the pages, unlock them. + if (pagesLocked) + MmUnlockPages(mdl); + + // If we allocated pool storage, free it. + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, 'ChpK'); + + // If we failed when probing or mapping, return the error status. + if (probing || mapping) + return GetExceptionCode(); + + // Determine which copy failed. + if (copyingToTarget && haveBadAddress) + { + *ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress); + } + else + { + *ReturnLength = BufferLength - stillToCopy; + } + + return STATUS_PARTIAL_COPY; + } + + KeUnstackDetachProcess(&apcState); + + if (doMappedCopy) + { + MmUnmapLockedPages(mappedAddress, mdl); + MmUnlockPages(mdl); + } + + stillToCopy -= blockSize; + sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize); + targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize); + } + + if (buffer != stackBuffer) + ExFreePoolWithTag(buffer, 'ChpK'); + + *ReturnLength = BufferLength; + + return STATUS_SUCCESS; +} + +/** + * Copies process or kernel memory into the current process. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_VM_READ access. This + * parameter may be NULL if \a BaseAddress lies above the user-mode range. + * \param BaseAddress The address from which memory is to be copied. + * \param Buffer A buffer which receives the copied memory. + * \param BufferSize The number of bytes to copy. + * \param NumberOfBytesRead A variable which receives the number of bytes copied to the buffer. + * \param Key An access key. If no valid L2 key is provided, the function fails. + * \param Client The client that initiated the request. + * \param AccessMode The mode in which to perform access checks. + */ +NTSTATUS KpiReadVirtualMemoryUnsafe( + _In_opt_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_writes_bytes_(BufferSize) PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead, + _In_opt_ KPH_KEY Key, + _In_ PKPH_CLIENT Client, + _In_ KPROCESSOR_MODE AccessMode + ) +{ + NTSTATUS status; + PEPROCESS process; + SIZE_T numberOfBytesRead = 0; + + PAGED_CODE(); + + if (!NT_SUCCESS(status = KphValidateKey(KphKeyLevel2, Key, Client, AccessMode))) + return status; + + if (AccessMode != KernelMode) + { + if ( + (ULONG_PTR)BaseAddress + BufferSize < (ULONG_PTR)BaseAddress || + (ULONG_PTR)Buffer + BufferSize < (ULONG_PTR)Buffer || + (ULONG_PTR)Buffer + BufferSize > (ULONG_PTR)MmHighestUserAddress + ) + { + return STATUS_ACCESS_VIOLATION; + } + + if (NumberOfBytesRead) + { + __try + { + ProbeForWrite(NumberOfBytesRead, sizeof(SIZE_T), sizeof(SIZE_T)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + } + + if (BufferSize != 0) + { + // Select the appropriate copy method. + if ((ULONG_PTR)BaseAddress + BufferSize > (ULONG_PTR)MmHighestUserAddress) + { + ULONG_PTR page; + ULONG_PTR pageEnd; + + status = KphValidateAddressForSystemModules(BaseAddress, BufferSize); + + if (!NT_SUCCESS(status)) + return status; + + // Kernel memory copy (unsafe) + + page = (ULONG_PTR)BaseAddress & ~(PAGE_SIZE - 1); + pageEnd = ((ULONG_PTR)BaseAddress + BufferSize - 1) & ~(PAGE_SIZE - 1); + + __try + { + // This will obviously fail if any of the pages aren't resident. + for (; page <= pageEnd; page += PAGE_SIZE) + { + if (!MmIsAddressValid((PVOID)page)) + ExRaiseStatus(STATUS_ACCESS_VIOLATION); + } + + memcpy(Buffer, BaseAddress, BufferSize); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + numberOfBytesRead = BufferSize; + status = STATUS_SUCCESS; + } + else + { + // User memory copy (safe) + + status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_VM_READ, + *PsProcessType, + AccessMode, + &process, + NULL + ); + + if (NT_SUCCESS(status)) + { + status = KphCopyVirtualMemory( + process, + BaseAddress, + PsGetCurrentProcess(), + Buffer, + BufferSize, + AccessMode, + &numberOfBytesRead + ); + ObDereferenceObject(process); + } + } + } + else + { + numberOfBytesRead = 0; + status = STATUS_SUCCESS; + } + + if (NumberOfBytesRead) + { + if (AccessMode != KernelMode) + { + __try + { + *NumberOfBytesRead = numberOfBytesRead; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // Don't mess with the status. + NOTHING; + } + } + else + { + *NumberOfBytesRead = numberOfBytesRead; + } + } + + return status; +} diff --git a/LICENSE.txt b/LICENSE.txt index 4e402de2cfe4..201de86e9f7e 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,685 +1,685 @@ -Process Hacker is distributed under the GNU GPL version 3, with the -following exception: - - Permission is granted to dynamically (but not statically) link this - program with independent modules, regardless of the license terms of - these independent modules, provided that this program is not modified - in any way. An independent module is a module which is not derived - from or based on this program. If you modify this program, this - additional permission no longer applies unless authorized by the - copyright holders. - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +Process Hacker is distributed under the GNU GPL version 3, with the +following exception: + + Permission is granted to dynamically (but not statically) link this + program with independent modules, regardless of the license terms of + these independent modules, provided that this program is not modified + in any way. An independent module is a module which is not derived + from or based on this program. If you modify this program, this + additional permission no longer applies unless authorized by the + copyright holders. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ProcessHacker.sln b/ProcessHacker.sln index f9d474cc33d6..4e87a04af677 100644 --- a/ProcessHacker.sln +++ b/ProcessHacker.sln @@ -1,81 +1,51 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 -MinimumVisualStudioVersion = 15.0.26228.4 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{2758DC86-368B-430C-9D29-F1EF20032A71}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProcessHacker", "ProcessHacker\ProcessHacker.vcxproj", "{0271DD27-6707-4290-8DFE-285702B7115D}" - ProjectSection(ProjectDependencies) = postProject - {477D0215-F252-41A1-874B-F27E3EA1ED17} = {477D0215-F252-41A1-874B-F27E3EA1ED17} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phlib", "phlib\phlib.vcxproj", "{477D0215-F252-41A1-874B-F27E3EA1ED17}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fixlib", "tools\fixlib\fixlib.vcxproj", "{31F4AA06-7ED5-4A6D-B901-19AD4BD16175}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peview", "tools\peview\peview.vcxproj", "{72C124A2-3C80-41C6-ABA1-C4948B713204}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{FD3C278D-BD40-4551-AE67-4DE196F8D7F6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phlib-test", "tests\phlib-test\phlib-test.vcxproj", "{0C21014E-BC90-4AE5-AA32-398445C13B28}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomSignTool", "tools\CustomSignTool\CustomSignTool.vcxproj", "{E8CD0A41-1537-4EA6-98AC-E80CD59C478E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|Win32.ActiveCfg = Debug|Win32 - {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|Win32.Build.0 = Debug|Win32 - {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|x64.ActiveCfg = Debug|x64 - {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|x64.Build.0 = Debug|x64 - {0271DD27-6707-4290-8DFE-285702B7115D}.Release|Win32.ActiveCfg = Release|Win32 - {0271DD27-6707-4290-8DFE-285702B7115D}.Release|Win32.Build.0 = Release|Win32 - {0271DD27-6707-4290-8DFE-285702B7115D}.Release|x64.ActiveCfg = Release|x64 - {0271DD27-6707-4290-8DFE-285702B7115D}.Release|x64.Build.0 = Release|x64 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|Win32.ActiveCfg = Debug|Win32 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|Win32.Build.0 = Debug|Win32 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|x64.ActiveCfg = Debug|x64 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|x64.Build.0 = Debug|x64 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|Win32.ActiveCfg = Release|Win32 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|Win32.Build.0 = Release|Win32 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|x64.ActiveCfg = Release|x64 - {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|x64.Build.0 = Release|x64 - {31F4AA06-7ED5-4A6D-B901-19AD4BD16175}.Debug|Win32.ActiveCfg = Debug|Win32 - {31F4AA06-7ED5-4A6D-B901-19AD4BD16175}.Debug|x64.ActiveCfg = Debug|Win32 - {31F4AA06-7ED5-4A6D-B901-19AD4BD16175}.Release|Win32.ActiveCfg = Release|Win32 - {31F4AA06-7ED5-4A6D-B901-19AD4BD16175}.Release|x64.ActiveCfg = Release|Win32 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|Win32.ActiveCfg = Debug|Win32 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|Win32.Build.0 = Debug|Win32 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|x64.ActiveCfg = Debug|x64 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|x64.Build.0 = Debug|x64 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|Win32.ActiveCfg = Release|Win32 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|Win32.Build.0 = Release|Win32 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|x64.ActiveCfg = Release|x64 - {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|x64.Build.0 = Release|x64 - {0C21014E-BC90-4AE5-AA32-398445C13B28}.Debug|Win32.ActiveCfg = Debug|Win32 - {0C21014E-BC90-4AE5-AA32-398445C13B28}.Debug|Win32.Build.0 = Debug|Win32 - {0C21014E-BC90-4AE5-AA32-398445C13B28}.Debug|x64.ActiveCfg = Debug|Win32 - {0C21014E-BC90-4AE5-AA32-398445C13B28}.Release|Win32.ActiveCfg = Release|Win32 - {0C21014E-BC90-4AE5-AA32-398445C13B28}.Release|Win32.Build.0 = Release|Win32 - {0C21014E-BC90-4AE5-AA32-398445C13B28}.Release|x64.ActiveCfg = Release|Win32 - {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|Win32.ActiveCfg = Debug|Win32 - {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|x64.ActiveCfg = Debug|x64 - {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|Win32.ActiveCfg = Release|Win32 - {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|x64.ActiveCfg = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {31F4AA06-7ED5-4A6D-B901-19AD4BD16175} = {2758DC86-368B-430C-9D29-F1EF20032A71} - {72C124A2-3C80-41C6-ABA1-C4948B713204} = {2758DC86-368B-430C-9D29-F1EF20032A71} - {0C21014E-BC90-4AE5-AA32-398445C13B28} = {FD3C278D-BD40-4551-AE67-4DE196F8D7F6} - {E8CD0A41-1537-4EA6-98AC-E80CD59C478E} = {2758DC86-368B-430C-9D29-F1EF20032A71} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.6 +MinimumVisualStudioVersion = 15.0.26228.4 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProcessHacker", "ProcessHacker\ProcessHacker.vcxproj", "{0271DD27-6707-4290-8DFE-285702B7115D}" + ProjectSection(ProjectDependencies) = postProject + {477D0215-F252-41A1-874B-F27E3EA1ED17} = {477D0215-F252-41A1-874B-F27E3EA1ED17} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phlib", "phlib\phlib.vcxproj", "{477D0215-F252-41A1-874B-F27E3EA1ED17}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peview", "tools\peview\peview.vcxproj", "{72C124A2-3C80-41C6-ABA1-C4948B713204}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|Win32.ActiveCfg = Debug|Win32 + {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|Win32.Build.0 = Debug|Win32 + {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|x64.ActiveCfg = Debug|x64 + {0271DD27-6707-4290-8DFE-285702B7115D}.Debug|x64.Build.0 = Debug|x64 + {0271DD27-6707-4290-8DFE-285702B7115D}.Release|Win32.ActiveCfg = Release|Win32 + {0271DD27-6707-4290-8DFE-285702B7115D}.Release|Win32.Build.0 = Release|Win32 + {0271DD27-6707-4290-8DFE-285702B7115D}.Release|x64.ActiveCfg = Release|x64 + {0271DD27-6707-4290-8DFE-285702B7115D}.Release|x64.Build.0 = Release|x64 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|Win32.ActiveCfg = Debug|Win32 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|Win32.Build.0 = Debug|Win32 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|x64.ActiveCfg = Debug|x64 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Debug|x64.Build.0 = Debug|x64 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|Win32.ActiveCfg = Release|Win32 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|Win32.Build.0 = Release|Win32 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|x64.ActiveCfg = Release|x64 + {477D0215-F252-41A1-874B-F27E3EA1ED17}.Release|x64.Build.0 = Release|x64 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|Win32.ActiveCfg = Debug|Win32 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|Win32.Build.0 = Debug|Win32 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|x64.ActiveCfg = Debug|x64 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Debug|x64.Build.0 = Debug|x64 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|Win32.ActiveCfg = Release|Win32 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|Win32.Build.0 = Release|Win32 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|x64.ActiveCfg = Release|x64 + {72C124A2-3C80-41C6-ABA1-C4948B713204}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ProcessHacker/ProcessHacker.def b/ProcessHacker/ProcessHacker.def index fec35a00d451..820c7f55006c 100644 --- a/ProcessHacker/ProcessHacker.def +++ b/ProcessHacker/ProcessHacker.def @@ -5,574 +5,693 @@ EXPORTS ; ; ref - PhAutoDereferenceObject - PhCreateAlloc - PhCreateObject - PhCreateObjectType - PhCreateObjectTypeEx - PhDeleteAutoPool - PhDereferenceObject - PhDereferenceObjectDeferDelete - PhDereferenceObjectEx - PhDrainAutoPool - PhGetObjectType - PhGetObjectTypeInformation - PhInitializeAutoPool - PhReferenceObject - PhReferenceObjectEx - PhReferenceObjectSafe + PhAutoDereferenceObject + PhCreateAlloc + PhCreateObject + PhCreateObjectType + PhCreateObjectTypeEx + PhDeleteAutoPool + PhDereferenceObject + PhDereferenceObjectDeferDelete + PhDereferenceObjectEx + PhDrainAutoPool + PhGetObjectType + PhGetObjectTypeInformation + PhInitializeAutoPool + PhReferenceObject + PhReferenceObjectEx + PhReferenceObjectSafe ; queuedlock - PhfAcquireQueuedLockExclusive - PhfAcquireQueuedLockShared - PhfPulseAllCondition - PhfPulseCondition - PhfQueueWakeEvent - PhfReleaseQueuedLockExclusive - PhfReleaseQueuedLockShared - PhfSetWakeEvent - PhfWaitForCondition - PhfWaitForConditionEx - PhfWaitForWakeEvent - PhfWakeForReleaseQueuedLock + PhfAcquireQueuedLockExclusive + PhfAcquireQueuedLockShared + PhfPulseAllCondition + PhfPulseCondition + PhfQueueWakeEvent + PhfReleaseQueuedLockExclusive + PhfReleaseQueuedLockShared + PhfSetWakeEvent + PhfWaitForCondition + PhfWaitForConditionEx + PhfWaitForWakeEvent + PhfWakeForReleaseQueuedLock ; phconfig - PhGlobalDpi DATA - PhHeapHandle DATA - PhIsExecutingInWow64 - PhLibImageBase DATA - PhOsVersion DATA - PhSystemBasicInformation DATA - ProcessAllAccess DATA - ProcessQueryAccess DATA - ThreadAllAccess DATA - ThreadQueryAccess DATA - ThreadSetAccess DATA - WindowsVersion DATA + PhGlobalDpi DATA + PhIsExecutingInWow64 + PhInstanceHandle DATA + PhOsVersion DATA + PhSystemBasicInformation DATA + ProcessAllAccess DATA + ProcessQueryAccess DATA + ThreadAllAccess DATA + ThreadQueryAccess DATA + ThreadSetAccess DATA + WindowsVersion DATA ; phbasesup - PhAddElementAvlTree - PhAddEntryHashtable - PhAddEntryHashtableEx - PhAddItemArray - PhAddItemList - PhAddItemPointerList - PhAddItemsArray - PhAddItemSimpleHashtable - PhAddItemsList - PhAllocate - PhAllocateExSafe - PhAllocateFromFreeList - PhAllocatePage - PhAllocateSafe - PhAppendBytesBuilder - PhAppendBytesBuilder2 - PhAppendBytesBuilderEx - PhAppendCharStringBuilder - PhAppendCharStringBuilder2 - PhAppendFormatStringBuilder - PhAppendFormatStringBuilder_V - PhAppendStringBuilder - PhAppendStringBuilder2 - PhAppendStringBuilderEx - PhBufferToHexString - PhBufferToHexStringEx - PhClearArray - PhClearHashtable - PhClearList - PhCompareStringRef - PhCompareStringZNatural - PhConcatStringRef2 - PhConcatStringRef3 - PhConcatStrings - PhConcatStrings_V - PhConcatStrings2 - PhConvertMultiByteToUtf16 - PhConvertMultiByteToUtf16Ex - PhConvertUtf16ToAsciiEx - PhConvertUtf16ToMultiByte - PhConvertUtf16ToMultiByteEx - PhConvertUtf16ToUtf8 - PhConvertUtf16ToUtf8Buffer - PhConvertUtf16ToUtf8Ex - PhConvertUtf16ToUtf8Size - PhConvertUtf8ToUtf16 - PhConvertUtf8ToUtf16Buffer - PhConvertUtf8ToUtf16Ex - PhConvertUtf8ToUtf16Size - PhCopyBytesZ - PhCopyStringZ - PhCopyStringZFromBytes - PhCopyStringZFromMultiByte - PhCountStringZ - PhCreateBytes - PhCreateBytesEx - PhCreateHashtable - PhCreateList - PhCreatePointerList - PhCreateSimpleHashtable - PhCreateString - PhCreateStringEx - PhCreateThread - PhDecodeUnicodeDecoder - PhDeleteArray - PhDeleteBytesBuilder - PhDeleteCallback - PhDeleteFreeList - PhDeleteStringBuilder - PhDivideSinglesBySingle - PhDosErrorToNtStatus - PhDuplicateBytesZ - PhDuplicateBytesZSafe - PhDuplicateStringZ - PhEncodeUnicode - PhEnumAvlTree - PhEnumHashtable - PhEnumPointerListEx - PhEqualStringRef - PhExponentiate - PhExponentiate64 - PhfAcquireRundownProtection - PhfBeginInitOnce - PhfEndInitOnce - PhFillMemoryUlong - PhFinalArrayItems - PhFinalBytesBuilderBytes - PhFinalStringBuilderString - PhFindCharInStringRef - PhFindElementAvlTree - PhFindEntryHashtable - PhFindItemList - PhFindItemPointerList - PhFindItemSimpleHashtable - PhFindLastCharInStringRef - PhFindStringInStringRef - PhfInitializeBarrier - PhfInitializeEvent - PhfInitializeInitOnce - PhfInitializeRundownProtection - PhFormat - PhFormatString - PhFormatString_V - PhFormatToBuffer - PhFree - PhFreePage - PhFreeToFreeList - PhfReleaseRundownProtection - PhfResetEvent - PhfSetEvent - PhfWaitForBarrier - PhfWaitForEvent - PhfWaitForRundownProtection - PhGetPrimeNumber - PhHashBytes - PhHashStringRef - PhHexStringToBuffer - PhInitializeArray - PhInitializeAvlTree - PhInitializeBytesBuilder - PhInitializeCallback - PhInitializeFreeList - PhInitializeStringBuilder - PhInsertItemList - PhInsertItemsList - PhInsertStringBuilder - PhInsertStringBuilder2 - PhInsertStringBuilderEx - PhIntegerToString64 - PhInvokeCallback - PhLocalTimeToSystemTime - PhLowerBoundElementAvlTree - PhLowerDualBoundElementAvlTree - PhMaximumElementAvlTree - PhMinimumElementAvlTree - PhNtStatusFileNotFound - PhNtStatusToDosError - PhPredecessorElementAvlTree - PhPrintTimeSpan - PhQuerySystemTime - PhQueryTimeZoneBias - PhReAllocate - PhReAllocateSafe - PhReferenceEmptyString - PhRegisterCallback - PhRegisterCallbackEx - PhRemoveElementAvlTree - PhRemoveEntryHashtable - PhRemoveItemArray - PhRemoveItemList - PhRemoveItemPointerList - PhRemoveItemsArray - PhRemoveItemSimpleHashtable - PhRemoveItemsList - PhRemoveStringBuilder - PhResizeArray - PhResizeList - PhRoundUpToPowerOfTwo - PhSplitStringRefAtChar - PhSplitStringRefAtLastChar - PhSplitStringRefAtString - PhSplitStringRefEx - PhStringToDouble - PhStringToInteger64 - PhSuccessorElementAvlTree - PhSystemTimeToLocalTime - PhTrimStringRef - PhUnregisterCallback - PhUpperBoundElementAvlTree - PhUpperDualBoundElementAvlTree - PhWriteUnicodeDecoder - PhZeroExtendToUtf16Buffer - PhZeroExtendToUtf16Ex + PhAddElementAvlTree + PhAddEntryHashtable + PhAddEntryHashtableEx + PhAddItemArray + PhAddItemList + PhAddItemPointerList + PhAddItemsArray + PhAddItemSimpleHashtable + PhAddItemsList + PhAllocate + PhAllocateExSafe + PhAllocateFromFreeList + PhAllocatePage + PhAllocateSafe + PhAppendBytesBuilder + PhAppendBytesBuilder2 + PhAppendBytesBuilderEx + PhAppendCharStringBuilder + PhAppendCharStringBuilder2 + PhAppendFormatStringBuilder + PhAppendFormatStringBuilder_V + PhAppendStringBuilder + PhAppendStringBuilder2 + PhAppendStringBuilderEx + PhBufferToHexString + PhBufferToHexStringEx + PhClearArray + PhClearHashtable + PhClearList + PhCompareStringRef + PhCompareStringZNatural + PhConcatStringRef2 + PhConcatStringRef3 + PhConcatStrings + PhConcatStrings_V + PhConcatStrings2 + PhConvertMultiByteToUtf16 + PhConvertMultiByteToUtf16Ex + PhConvertUtf16ToAsciiEx + PhConvertUtf16ToMultiByte + PhConvertUtf16ToMultiByteEx + PhConvertUtf16ToUtf8 + PhConvertUtf16ToUtf8Buffer + PhConvertUtf16ToUtf8Ex + PhConvertUtf16ToUtf8Size + PhConvertUtf8ToUtf16 + PhConvertUtf8ToUtf16Buffer + PhConvertUtf8ToUtf16Ex + PhConvertUtf8ToUtf16Size + PhCopyBytesZ + PhCopyStringZ + PhCopyStringZFromBytes + PhCopyStringZFromMultiByte + PhCountStringZ + PhCreateBytes + PhCreateBytesEx + PhCreateHashtable + PhCreateList + PhCreatePointerList + PhCreateSimpleHashtable + PhCreateString + PhCreateStringEx + PhCreateThread + PhCreateThread2 + PhCreateThreadEx + PhDecodeUnicodeDecoder + PhDeleteArray + PhDeleteBytesBuilder + PhDeleteCallback + PhDeleteFreeList + PhDeleteStringBuilder + PhDivideSinglesBySingle + PhDosErrorToNtStatus + PhDuplicateBytesZ + PhDuplicateBytesZSafe + PhDuplicateStringZ + PhEncodeUnicode + PhEnumAvlTree + PhEnumHashtable + PhEnumPointerListEx + PhEqualStringRef + PhExponentiate + PhExponentiate64 + PhExtractIcon + PhExtractIconEx + PhfAcquireRundownProtection + PhfBeginInitOnce + PhfEndInitOnce + PhFillMemoryUlong + PhFinalArrayItems + PhFinalBytesBuilderBytes + PhFinalStringBuilderString + PhFindCharInStringRef + PhFindElementAvlTree + PhFindEntryHashtable + PhFindItemList + PhFindItemPointerList + PhFindItemSimpleHashtable + PhFindLastCharInStringRef + PhFindStringInStringRef + PhfInitializeBarrier + PhfInitializeEvent + PhfInitializeInitOnce + PhfInitializeRundownProtection + PhFormat + PhFormatString + PhFormatString_V + PhFormatToBuffer + PhFree + PhFreePage + PhFreeToFreeList + PhfReleaseRundownProtection + PhfResetEvent + PhfSetEvent + PhfWaitForBarrier + PhfWaitForEvent + PhfWaitForRundownProtection + PhGetPrimeNumber + PhHashBytes + PhHashStringRef + PhHexStringToBuffer + PhHexStringToBufferEx + PhInitializeArray + PhInitializeAvlTree + PhInitializeBytesBuilder + PhInitializeCallback + PhInitializeFreeList + PhInitializeStringBuilder + PhInsertItemList + PhInsertItemsList + PhInsertStringBuilder + PhInsertStringBuilder2 + PhInsertStringBuilderEx + PhIntegerToString64 + PhInvokeCallback + PhLoadIndirectString + PhLoadResource + PhLocalTimeToSystemTime + PhLowerBoundElementAvlTree + PhLowerDualBoundElementAvlTree + PhMaximumElementAvlTree + PhMinimumElementAvlTree + PhNtStatusFileNotFound + PhNtStatusToDosError + PhPredecessorElementAvlTree + PhPrintTimeSpan + PhQuerySystemTime + PhQueryTimeZoneBias + PhReAllocate + PhReAllocateSafe + PhReferenceEmptyString + PhRegisterCallback + PhRegisterCallbackEx + PhRemoveElementAvlTree + PhRemoveEntryHashtable + PhRemoveItemArray + PhRemoveItemList + PhRemoveItemPointerList + PhRemoveItemsArray + PhRemoveItemSimpleHashtable + PhRemoveItemsList + PhRemoveStringBuilder + PhResizeArray + PhResizeList + PhRoundUpToPowerOfTwo + PhSplitStringRefAtChar + PhSplitStringRefAtLastChar + PhSplitStringRefAtString + PhSplitStringRefEx + PhStringToDouble + PhStringToInteger64 + PhSuccessorElementAvlTree + PhSystemTimeToLocalTime + PhTrimStringRef + PhUnregisterCallback + PhUpperBoundElementAvlTree + PhUpperDualBoundElementAvlTree + PhWriteUnicodeDecoder + PhZeroExtendToUtf16Buffer + PhZeroExtendToUtf16Ex ; phnative - PhCreateFileWin32 - PhCreateFileWin32Ex - PhCreateKey - PhDeleteFileWin32 - PhDisconnectNamedPipe - PhEnumDirectoryFile - PhEnumDirectoryObjects - PhEnumFileStreams - PhEnumGenericModules - PhEnumHandles - PhEnumHandlesEx - PhEnumKernelModules - PhEnumPagefiles - PhEnumProcessEnvironmentVariables - PhEnumProcesses - PhEnumProcessesEx - PhEnumProcessesForSession - PhEnumProcessModules - PhEnumProcessModules32 - PhEnumProcessModules32Ex - PhEnumProcessModulesEx - PhFindProcessInformation - PhFindProcessInformationByImageName - PhGetFileName - PhGetFileSize - PhGetJobProcessIdList - PhGetKernelFileName - PhGetObjectSecurity - PhGetOwnTokenAttributes - PhGetProcedureAddressRemote - PhGetProcessCommandLine - PhGetProcessDepStatus - PhGetProcessEnvironment - PhGetProcessImageFileName - PhGetProcessImageFileNameByProcessId - PhGetProcessImageFileNameWin32 - PhGetProcessIsDotNet - PhGetProcessIsDotNetEx - PhGetProcessMappedFileName - PhGetProcessPebString - PhGetProcessWindowTitle - PhGetProcessWorkingSetInformation - PhGetProcessWsCounters - PhGetTokenGroups - PhGetTokenIntegrityLevel - PhGetTokenOwner - PhGetTokenPrimaryGroup - PhGetTokenPrivileges - PhGetTokenUser - PhImpersonateClientOfNamedPipe - PhInjectDllProcess - PhListenNamedPipe - PhOpenKey - PhOpenProcess = PhOpenProcessPublic - PhOpenThread = PhOpenThreadPublic - PhOpenThreadProcess - PhPeekNamedPipe - PhQueryFullAttributesFileWin32 - PhQueryKey - PhQueryValueKey - PhResolveDevicePrefix - PhSetFileSize - PhSetObjectSecurity - PhSetTokenIsVirtualizationEnabled - PhSetTokenPrivilege - PhSetTokenPrivilege2 - PhSetTokenSessionId - PhTerminateProcess = PhTerminateProcessPublic - PhTransceiveNamedPipe - PhUnloadDllProcess - PhUnloadDriver - PhUpdateDosDevicePrefixes - PhUpdateMupDevicePrefixes - PhWaitForNamedPipe + PhCreateDirectory + PhCreateFileWin32 + PhCreateFileWin32Ex + PhCreateKey + PhCreateNamedPipe + PhCreatePipe + PhConnectPipe + PhDeleteDirectory + PhDeleteFileWin32 + PhDoesFileExistsWin32 + PhDisconnectNamedPipe + PhEnumDirectoryFile + PhEnumDirectoryObjects + PhEnumFileStreams + PhEnumGenericModules + PhEnumHandles + PhEnumHandlesEx + PhEnumHandlesEx2 + PhEnumKernelModules + PhEnumPagefiles + PhEnumProcessEnvironmentVariables + PhEnumProcesses + PhEnumProcessesEx + PhEnumProcessesForSession + PhEnumProcessModules + PhEnumProcessModules32 + PhEnumProcessModules32Ex + PhEnumProcessModulesEx + PhFindProcessInformation + PhFindProcessInformationByImageName + PhGetFileName + PhGetFileSize + PhGetJobProcessIdList + PhGetKernelFileName + PhGetObjectSecurity + PhGetOwnTokenAttributes + PhGetDllHandle + PhGetModuleProcAddress + PhGetProcedureAddress + PhGetProcedureAddressRemote + PhGetProcessCommandLine + PhGetProcessDepStatus + PhGetProcessEnvironment + PhGetProcessImageFileName + PhGetProcessImageFileNameByProcessId + PhGetProcessImageFileNameWin32 + PhGetProcessIsDotNet + PhGetProcessIsDotNetEx + PhGetProcessMappedFileName + PhGetProcessPebString + PhGetProcessUnloadedDlls + PhGetProcessWindowTitle + PhGetProcessWorkingSetInformation + PhGetProcessWsCounters + PhGetTokenGroups + PhGetTokenIntegrityLevelRID + PhGetTokenIntegrityLevel + PhGetTokenOwner + PhGetTokenPrimaryGroup + PhGetTokenPrivileges + PhGetTokenUser + PhImpersonateClientOfNamedPipe + PhListenNamedPipe + PhOpenKey + PhLoadAppKey + PhOpenProcess = PhOpenProcessPublic + PhOpenThread = PhOpenThreadPublic + PhOpenThreadProcess + PhOpenProcessToken = PhOpenProcessTokenPublic + PhPeekNamedPipe + PhQueryFullAttributesFileWin32 + PhQueryKey + PhQueryValueKey + PhQueryTokenVariableSize + PhResolveDevicePrefix + PhSetFileSize + PhSetObjectSecurity + PhSetTokenIsVirtualizationEnabled + PhSetTokenPrivilege + PhSetTokenPrivilege2 + PhSetTokenSessionId + PhTerminateProcess = PhTerminateProcessPublic + PhTransceiveNamedPipe + PhUnloadDllProcess + PhUnloadDriver + PhUpdateDosDevicePrefixes + PhUpdateMupDevicePrefixes + PhWaitForNamedPipe ; phutil - PhAdjustRectangleToBounds - PhAdjustRectangleToWorkingArea - PhCenterRectangle - PhCenterWindow - PhCompareUnicodeStringZIgnoreMenuPrefix - PhCreateOpenFileDialog - PhCreateProcess - PhCreateProcessAsUser - PhCreateProcessWin32 - PhCreateProcessWin32Ex - PhCreateSaveFileDialog - PhDeleteImageVersionInfo - PhDereferenceObjects - PhEllipsisString - PhEllipsisStringPath - PhEscapeCommandLinePart - PhEscapeStringForMenuPrefix - PhExpandEnvironmentStrings - PhFinalHash - PhFindIntegerSiKeyValuePairs - PhFindLoaderEntry - PhFindStringSiKeyValuePairs - PhFormatDate - PhFormatDateTime - PhFormatDecimal - PhFormatGuid - PhFormatImageVersionInfo - PhFormatSize - PhFormatTime - PhFormatTimeSpan - PhFormatTimeSpanRelative - PhFormatUInt64 - PhFreeFileDialog - PhGenerateGuid - PhGenerateGuidFromName - PhGenerateRandomAlphaString - PhGetApplicationDirectory - PhGetApplicationFileName - PhGetBaseName - PhGetDllFileName - PhGetFileDialogFileName - PhGetFileDialogFilterIndex - PhGetFileDialogOptions - PhGetFileVersionInfo - PhGetFileVersionInfoLangCodePage - PhGetFileVersionInfoString - PhGetFileVersionInfoString2 - PhGetFullPath - PhGetKnownLocation - PhGetMessage - PhGetNtMessage - PhGetSystemDirectory - PhGetSystemRoot - PhGetWin32Message - PhInitializeHash - PhInitializeImageVersionInfo - PhIsExecutablePacked - PhMapFlags1 - PhMapFlags2 - PhMatchWildcards - PhParseCommandLine - PhParseCommandLineFuzzy - PhParseCommandLinePart - PhQueryRegistryString - PhReferenceObjects - PhSetFileDialogFileName - PhSetFileDialogFilter - PhSetFileDialogOptions - PhShellExecute - PhShellExecuteEx - PhShellExploreFile - PhShellOpenKey - PhShellProperties - PhShowConfirmMessage - PhShowContinueStatus - PhShowFileDialog - PhShowMessage - PhShowMessage_V - PhShowStatus - PhUpdateHash + PhAdjustRectangleToBounds + PhAdjustRectangleToWorkingArea + PhCenterRectangle + PhCenterWindow + PhCompareUnicodeStringZIgnoreMenuPrefix + PhCreateOpenFileDialog + PhCreateProcess + PhCreateProcessAsUser + PhCreateProcessWin32 + PhCreateProcessWin32Ex + PhCreateSaveFileDialog + PhDeleteImageVersionInfo + PhDereferenceObjects + PhEllipsisString + PhEllipsisStringPath + PhEscapeCommandLinePart + PhEscapeStringForMenuPrefix + PhExpandEnvironmentStrings + PhFinalHash + PhFindIntegerSiKeyValuePairs + PhFindLoaderEntry + PhFindStringSiKeyValuePairs + PhFormatDate + PhFormatDateTime + PhFormatDecimal + PhFormatGuid + PhFormatImageVersionInfo + PhFormatSize + PhFormatTime + PhFormatTimeSpan + PhFormatTimeSpanRelative + PhFormatUInt64 + PhFreeFileDialog + PhGenerateGuid + PhGenerateGuidFromName + PhGenerateRandomAlphaString + PhGetApplicationDirectory + PhGetApplicationFileName + PhGetBaseDirectory + PhGetBaseName + PhGetDllFileName + PhGetFileDialogFileName + PhGetFileDialogFilterIndex + PhGetFileDialogOptions + PhGetFileVersionInfo + PhGetFileVersionInfoLangCodePage + PhGetFileVersionInfoString + PhGetFileVersionInfoString2 + PhGetFullPath + PhGetKnownLocation + PhGetMessage + PhGetNtMessage + PhGetStatusMessage + PhGetSystemDirectory + PhGetSystemRoot + PhGetWin32Message + PhInitializeHash + PhInitializeImageVersionInfo + PhIsExecutablePacked + PhMapFlags1 + PhMapFlags2 + PhMatchWildcards + PhParseCommandLine + PhParseCommandLineFuzzy + PhParseCommandLinePart + PhQueryRegistryString + PhQueryRegistryUlong + PhQueryRegistryUlong64 + PhReferenceObjects + PhSetFileDialogFileName + PhSetFileDialogFilter + PhSetFileDialogOptions + PhShellExecute + PhShellExecuteEx + PhShellExploreFile + PhShellOpenKey + PhShellProperties + PhShowConfirmMessage + PhShowContinueStatus + PhShowFileDialog + PhShowMessage + PhShowMessage2 + PhShowStatus + PhUpdateHash ; circbuf - PhClearCircularBuffer_FLOAT - PhClearCircularBuffer_PVOID - PhClearCircularBuffer_ULONG - PhClearCircularBuffer_ULONG64 - PhCopyCircularBuffer_FLOAT - PhCopyCircularBuffer_PVOID - PhCopyCircularBuffer_ULONG - PhCopyCircularBuffer_ULONG64 - PhDeleteCircularBuffer_FLOAT - PhDeleteCircularBuffer_PVOID - PhDeleteCircularBuffer_ULONG - PhDeleteCircularBuffer_ULONG64 - PhInitializeCircularBuffer_FLOAT - PhInitializeCircularBuffer_PVOID - PhInitializeCircularBuffer_ULONG - PhInitializeCircularBuffer_ULONG64 - PhResizeCircularBuffer_FLOAT - PhResizeCircularBuffer_PVOID - PhResizeCircularBuffer_ULONG - PhResizeCircularBuffer_ULONG64 + PhClearCircularBuffer_FLOAT + PhClearCircularBuffer_PVOID + PhClearCircularBuffer_ULONG + PhClearCircularBuffer_ULONG64 + PhCopyCircularBuffer_FLOAT + PhCopyCircularBuffer_PVOID + PhCopyCircularBuffer_ULONG + PhCopyCircularBuffer_ULONG64 + PhDeleteCircularBuffer_FLOAT + PhDeleteCircularBuffer_PVOID + PhDeleteCircularBuffer_ULONG + PhDeleteCircularBuffer_ULONG64 + PhInitializeCircularBuffer_FLOAT + PhInitializeCircularBuffer_PVOID + PhInitializeCircularBuffer_ULONG + PhInitializeCircularBuffer_ULONG64 + PhResizeCircularBuffer_FLOAT + PhResizeCircularBuffer_PVOID + PhResizeCircularBuffer_ULONG + PhResizeCircularBuffer_ULONG64 ; cpysave - PhGetGenericTreeNewLines - PhGetTreeNewText + PhGetGenericTreeNewLines + PhGetTreeNewText ; emenu - PhCreateEMenu - PhCreateEMenuItem - PhDestroyEMenu - PhDestroyEMenuItem - PhFindEMenuItem - PhIndexOfEMenuItem - PhInsertEMenuItem - PhLoadResourceEMenuItem - PhModifyEMenuItem - PhRemoveAllEMenuItems - PhRemoveEMenuItem - PhSetFlagsAllEMenuItems - PhSetFlagsEMenuItem - PhShowEMenu + PhCreateEMenu + PhCreateEMenuItem + PhDestroyEMenu + PhDestroyEMenuItem + PhFindEMenuItem + PhIndexOfEMenuItem + PhInsertEMenuItem + PhLoadResourceEMenuItem + PhModifyEMenuItem + PhRemoveAllEMenuItems + PhRemoveEMenuItem + PhSetFlagsAllEMenuItems + PhSetFlagsEMenuItem + PhShowEMenu ; fastlock - PhDeleteFastLock - PhfAcquireFastLockExclusive - PhfAcquireFastLockShared - PhfReleaseFastLockExclusive - PhfReleaseFastLockShared - PhfTryAcquireFastLockExclusive - PhfTryAcquireFastLockShared - PhInitializeFastLock + PhDeleteFastLock + PhfAcquireFastLockExclusive + PhfAcquireFastLockShared + PhfReleaseFastLockExclusive + PhfReleaseFastLockShared + PhfTryAcquireFastLockExclusive + PhfTryAcquireFastLockShared + PhInitializeFastLock ; filestream - PhCreateFileStream - PhCreateFileStream2 - PhFlushFileStream - PhGetPositionFileStream - PhLockFileStream - PhReadFileStream - PhSeekFileStream - PhUnlockFileStream - PhVerifyFileStream - PhWriteFileStream - PhWriteStringAsUtf8FileStream - PhWriteStringAsUtf8FileStream2 - PhWriteStringAsUtf8FileStreamEx - PhWriteStringFormatAsUtf8FileStream - PhWriteStringFormatAsUtf8FileStream_V + PhCreateFileStream + PhCreateFileStream2 + PhFlushFileStream + PhGetPositionFileStream + PhLockFileStream + PhReadFileStream + PhSeekFileStream + PhUnlockFileStream + PhVerifyFileStream + PhWriteFileStream + PhWriteStringAsUtf8FileStream + PhWriteStringAsUtf8FileStream2 + PhWriteStringAsUtf8FileStreamEx + PhWriteStringFormatAsUtf8FileStream + PhWriteStringFormatAsUtf8FileStream_V ; graph - PhDeleteGraphState - PhDrawGraphDirect - PhGetDrawInfoGraphBuffers - PhGraphStateGetDrawInfo - PhInitializeGraphState - PhSetGraphText + PhDeleteGraphState + PhDrawGraphDirect + PhDrawTrayIconText + PhGetDrawInfoGraphBuffers + PhGraphStateGetDrawInfo + PhInitializeGraphState + PhSetGraphText ; guisup - PhAddComboBoxStrings - PhAddLayoutItem - PhAddLayoutItemEx - PhAddListViewColumn - PhAddListViewItem - PhAddTabControlTab - PhDeleteLayoutManager - PhFindListViewItemByFlags - PhFindListViewItemByParam - PhGetComboBoxString - PhGetFileShellIcon - PhGetListBoxString - PhGetListViewItemImageIndex - PhGetListViewItemParam - PhGetSelectedListViewItemParam - PhGetSelectedListViewItemParams - PhGetStockApplicationIcon - PhGetWindowText - PhGetWindowTextEx - PhIconToBitmap - PhInitializeLayoutManager - PhLayoutManagerLayout - PhLoadIcon - PhLoadListViewColumnSettings - PhModalPropertySheet - PhRemoveListViewItem - PhSaveListViewColumnSettings - PhSelectComboBoxString - PhSetClipboardString - PhSetControlTheme - PhSetExtendedListView - PhSetHeaderSortIcon - PhSetImageListBitmap - PhSetListViewItemImageIndex - PhSetListViewSubItem - PhSetStateAllListViewItems + PhAddComboBoxStrings + PhAddLayoutItem + PhAddLayoutItemEx + PhAddListViewColumn + PhAddListViewItem + PhAddTabControlTab + PhDeleteLayoutManager + PhFindListViewItemByFlags + PhFindListViewItemByParam + PhGetComboBoxString + PhGetFileShellIcon + PhGetListBoxString + PhGetListViewItemImageIndex + PhGetListViewItemParam + PhGetSelectedListViewItemParam + PhGetSelectedListViewItemParams + PhGetStockApplicationIcon + PhGetWindowText + PhGetWindowTextEx + PhIconToBitmap + PhInitializeLayoutManager + PhLayoutManagerLayout + PhLoadIcon + PhLoadListViewColumnSettings + PhModalPropertySheet + PhRemoveListViewItem + PhSaveListViewColumnSettings + PhSelectComboBoxString + PhSetClipboardString + PhSetControlTheme + PhSetExtendedListView + PhSetHeaderSortIcon + PhSetImageListBitmap + PhSetListViewItemImageIndex + PhSetListViewSubItem + PhAddListViewGroup + PhAddListViewGroupItem + PhSetStateAllListViewItems + PhGetWindowContext + PhSetWindowContext + PhRemoveWindowContext + PhGetDialogItemValue + PhSetDialogItemValue + PhSetDialogItemText + PhSetWindowText + PhApplicationFont + PhTreeWindowFont + PhRegisterWindowCallback + PhUnregisterWindowCallback + PhInitializeWindowTheme + PhReInitializeWindowTheme + PhInitializeWindowThemeStatusBar + PhGetGlobalTimerQueue ; hndlinfo - PhEnumObjectTypes - PhFormatNativeKeyName - PhGetHandleInformation - PhGetHandleInformationEx - PhStdGetClientIdName + PhEnumObjectTypes + PhFormatNativeKeyName + PhGetHandleInformation + PhGetHandleInformationEx + PhStdGetClientIdName ; lsasup - PhGetSidFullName - PhLookupName - PhLookupPrivilegeDisplayName - PhLookupPrivilegeName - PhLookupPrivilegeValue - PhLookupSid - PhOpenLsaPolicy - PhSidToStringSid + PhGetSidFullName + PhLookupName + PhLookupPrivilegeDisplayName + PhLookupPrivilegeName + PhLookupPrivilegeValue + PhLookupSid + PhOpenLsaPolicy + PhSidToStringSid ; mapimg - PhGetMappedImageCfg - PhGetMappedImageCfgEntry - PhGetMappedImageLoadConfig32 - PhGetMappedImageLoadConfig64 - PhInitializeMappedImage - PhLoadMappedImage + PhGetMappedImageCfg + PhGetMappedImageCfgEntry + PhGetMappedImageLoadConfig32 + PhGetMappedImageLoadConfig64 + PhGetMappedImageExports + PhGetMappedImageExportFunction + PhMappedImageRvaToVa + PhInitializeMappedImage + PhLoadMappedImage + PhLoadMappedImageEx + PhUnloadMappedImage + +; settings + PhAddSetting + PhAddSettings + PhClearIgnoredSettings + PhConvertIgnoredSettings + PhGetIntegerSetting + PhGetIntegerPairSetting + PhGetScalableIntegerPairSetting + PhGetStringSetting + PhLoadSettings + PhLoadListViewColumnSettings + PhLoadListViewColumnsFromSetting + PhLoadListViewSortColumnsFromSetting + PhLoadListViewGroupStatesFromSetting + PhLoadWindowPlacementFromSetting + PhResetSettings + PhSaveListViewColumnSettings + PhSaveListViewColumnsToSetting + PhSaveListViewSortColumnsToSetting + PhSaveListViewGroupStatesToSetting + PhSaveWindowPlacementToSetting + PhSettingsInitialization + PhSetIntegerSetting + PhSetIntegerPairSetting + PhSetScalableIntegerPairSetting + PhSetScalableIntegerPairSetting2 + PhSetStringSetting + PhSetStringSetting2 + PhSaveSettings + PhUpdateCachedSettings ; secedit - PhCreateSecurityPage - PhEditSecurity - PhGetAccessEntries - PhGetAccessString - PhGetSeObjectSecurity - PhSetSeObjectSecurity - PhStdGetObjectSecurity - PhStdSetObjectSecurity + PhCreateSecurityPage + PhEditSecurity + PhGetAccessEntries + PhGetAccessString + PhGetSeObjectSecurity + PhSetSeObjectSecurity + PhStdGetObjectSecurity + PhStdSetObjectSecurity ; svcsup - PhEnumServices - PhGetServiceConfig - PhGetServiceDelayedAutoStart - PhGetServiceDescription - PhGetServiceDllParameter - PhGetServiceErrorControlInteger - PhGetServiceErrorControlString - PhGetServiceNameFromTag - PhGetServiceStartTypeInteger - PhGetServiceStartTypeString - PhGetServiceStateString - PhGetServiceTypeInteger - PhGetServiceTypeString - PhGetThreadServiceTag - PhOpenService - PhQueryServiceVariableSize - PhSetServiceDelayedAutoStart + PhEnumServices + PhGetServiceConfig + PhGetServiceDelayedAutoStart + PhGetServiceDescription + PhGetServiceDllParameter + PhGetServiceErrorControlInteger + PhGetServiceErrorControlString + PhGetServiceNameFromTag + PhGetServiceStartTypeInteger + PhGetServiceStartTypeString + PhGetServiceStateString + PhGetServiceTypeInteger + PhGetServiceTypeString + PhGetThreadServiceTag + PhOpenService + PhQueryServiceVariableSize + PhSetServiceDelayedAutoStart ; symprv - PhCreateSymbolProvider - PhGetLineFromAddress - PhGetModuleFromAddress - PhGetSymbolFromAddress - PhGetSymbolFromName - PhLoadModuleSymbolProvider - PhSetOptionsSymbolProvider - PhSetSearchPathSymbolProvider - PhStackWalk - PhWalkThreadStack - PhWriteMiniDumpProcess + PhCreateSymbolProvider + PhGetLineFromAddress + PhGetModuleFromAddress + PhGetSymbolFromAddress + PhGetSymbolFromName + PhLoadModuleSymbolProvider + PhSetOptionsSymbolProvider + PhSetSearchPathSymbolProvider + PhStackWalk + PhWalkThreadStack + PhWriteMiniDumpProcess ; verify - PhVerifyFile + PhVerifyFile ; workqueue - PhDeleteWorkQueue - PhGetGlobalWorkQueue - PhInitializeWorkQueue - PhInitializeWorkQueueEnvironment - PhQueueItemWorkQueue - PhQueueItemWorkQueueEx - PhWaitForWorkQueue + PhDeleteWorkQueue + PhGetGlobalWorkQueue + PhInitializeWorkQueue + PhInitializeWorkQueueEnvironment + PhQueueItemWorkQueue + PhQueueItemWorkQueueEx + PhWaitForWorkQueue + +; mxml + mxmlDelete + mxmlElementGetAttrCount + mxmlElementGetAttrByIndex + mxmlElementSetAttr + mxmlGetFirstChild + mxmlGetNextSibling + mxmlGetOpaque + mxmlGetType + mxmlLoadFd + mxmlNewOpaque + mxmlNewElement + mxmlSaveFd + mxml_opaque_cb + +; json + PhCreateJsonParser + PhFreeJsonParser + PhGetJsonValueAsString + PhGetJsonValueAsLong64 + PhCreateJsonObject + PhGetJsonObject + PhGetJsonObjectLength + PhGetJsonObjectBool + PhAddJsonObject + PhGetJsonObjectAsArrayList + PhCreateJsonArray + PhAddJsonArrayObject + PhGetJsonArrayString + PhGetJsonArrayLong64 + PhGetJsonArrayLength + PhGetJsonArrayIndexObject + +; cache + PhCreateCacheFile + PhClearCacheDirectory + PhDeleteCacheFile + +; appresolver + PhAppResolverGetAppIdForWindow diff --git a/ProcessHacker/ProcessHacker.manifest b/ProcessHacker/ProcessHacker.manifest index 3bc4a2568dd8..1a214566dc7e 100644 --- a/ProcessHacker/ProcessHacker.manifest +++ b/ProcessHacker/ProcessHacker.manifest @@ -1,49 +1,49 @@ - - - - Process Hacker - - - - - - - - - - - - - - - - - - - - - - - - true - - - true - - - true - - + + + + Process Hacker + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + \ No newline at end of file diff --git a/ProcessHacker/ProcessHacker.rc b/ProcessHacker/ProcessHacker.rc index 1fdef831b78a..33a08b00949b 100644 --- a/ProcessHacker/ProcessHacker.rc +++ b/ProcessHacker/ProcessHacker.rc @@ -1,2607 +1,2371 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" -#include "include/phappres.h" -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "#include ""include/phappres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_PROCESSHACKER ICON "ProcessHacker.ico" - -IDI_PHAPPLICATION ICON "resources\\application.ico" - -IDI_COG ICON "resources\\cog.ico" - -IDI_PHAPPLICATIONGO ICON "resources\\application_go.ico" - -IDI_COGGO ICON "resources\\cog_go.ico" - -IDI_PIN ICON "resources\\pin.ico" - -IDI_FOLDER ICON "resources\\folder.ico" - -IDI_PENCIL ICON "resources\\pencil.ico" - -IDI_MAGNIFIER ICON "resources\\magnifier.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_MAINWND MENU -BEGIN - POPUP "&Hacker" - BEGIN - MENUITEM "&Run...\aCtrl+R", ID_HACKER_RUN - MENUITEM "Run as administrator...", ID_HACKER_RUNASADMINISTRATOR - MENUITEM "Run as &limited user...", ID_HACKER_RUNASLIMITEDUSER - MENUITEM "Run &as...\aCtrl+Shift+R", ID_HACKER_RUNAS - MENUITEM "Show &details for all processes", ID_HACKER_SHOWDETAILSFORALLPROCESSES - MENUITEM SEPARATOR - MENUITEM "&Save...\aCtrl+S", ID_HACKER_SAVE - MENUITEM "&Find handles or DLLs...\aCtrl+F", ID_HACKER_FINDHANDLESORDLLS - MENUITEM "&Options...", ID_HACKER_OPTIONS - MENUITEM "&Plugins...", ID_HACKER_PLUGINS - MENUITEM SEPARATOR - POPUP "&Computer" - BEGIN - MENUITEM "&Lock", ID_COMPUTER_LOCK - MENUITEM "Log o&ff", ID_COMPUTER_LOGOFF - MENUITEM SEPARATOR - MENUITEM "&Sleep", ID_COMPUTER_SLEEP - MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE - MENUITEM SEPARATOR - MENUITEM "R&estart", ID_COMPUTER_RESTART - MENUITEM "Restart to boot options", ID_COMPUTER_RESTARTBOOTOPTIONS - MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN - MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID - END - MENUITEM "E&xit", ID_HACKER_EXIT - END - POPUP "&View" - BEGIN - MENUITEM "System &information\aCtrl+I", ID_VIEW_SYSTEMINFORMATION - POPUP "&Tray icons" - BEGIN - MENUITEM "CPU history", ID_TRAYICONS_CPUHISTORY - MENUITEM "CPU usage", ID_TRAYICONS_CPUUSAGE - MENUITEM "I/O history", ID_TRAYICONS_IOHISTORY - MENUITEM "Commit charge history", ID_TRAYICONS_COMMITHISTORY - MENUITEM "Physical memory history", ID_TRAYICONS_PHYSICALMEMORYHISTORY - END - MENUITEM SEPARATOR - MENUITEM "
", ID_VIEW_SECTIONPLACEHOLDER - MENUITEM SEPARATOR - MENUITEM "&Always on top", ID_VIEW_ALWAYSONTOP - POPUP "&Opacity" - BEGIN - MENUITEM "10%", ID_OPACITY_10 - MENUITEM "20%", ID_OPACITY_20 - MENUITEM "30%", ID_OPACITY_30 - MENUITEM "40%", ID_OPACITY_40 - MENUITEM "50%", ID_OPACITY_50 - MENUITEM "60%", ID_OPACITY_60 - MENUITEM "70%", ID_OPACITY_70 - MENUITEM "80%", ID_OPACITY_80 - MENUITEM "90%", ID_OPACITY_90 - MENUITEM "Opaque", ID_OPACITY_OPAQUE - END - MENUITEM SEPARATOR - MENUITEM "&Refresh\aF5", ID_VIEW_REFRESH - POPUP "Refresh i&nterval" - BEGIN - MENUITEM "Fast (0.5s)", ID_UPDATEINTERVAL_FAST - MENUITEM "Normal (1s)", ID_UPDATEINTERVAL_NORMAL - MENUITEM "Below normal (2s)", ID_UPDATEINTERVAL_BELOWNORMAL - MENUITEM "Slow (5s)", ID_UPDATEINTERVAL_SLOW - MENUITEM "Very slow (10s)", ID_UPDATEINTERVAL_VERYSLOW - END - MENUITEM "Refresh a&utomatically\aF6", ID_VIEW_UPDATEAUTOMATICALLY - END - POPUP "&Tools" - BEGIN - MENUITEM "Create service...", ID_TOOLS_CREATESERVICE - MENUITEM "Hidden processes", ID_TOOLS_HIDDENPROCESSES - MENUITEM "Inspect executable file...", ID_TOOLS_INSPECTEXECUTABLEFILE - MENUITEM "Pagefiles", ID_TOOLS_PAGEFILES - MENUITEM "Start Task Manager", ID_TOOLS_STARTTASKMANAGER - END - POPUP "&Users" - BEGIN - MENUITEM "Dummy", ID_USERS_DUMMY - END - POPUP "H&elp" - BEGIN - MENUITEM "&Log\aCtrl+L", ID_HELP_LOG - MENUITEM "&Donate", ID_HELP_DONATE - MENUITEM "Debu&g console", ID_HELP_DEBUGCONSOLE - MENUITEM "&About", ID_HELP_ABOUT - END -END - -IDR_THREAD MENU -BEGIN - POPUP "Thread" - BEGIN - MENUITEM "&Inspect\aEnter", ID_THREAD_INSPECT - MENUITEM "T&erminate\aDel", ID_THREAD_TERMINATE - MENUITEM "&Suspend", ID_THREAD_SUSPEND - MENUITEM "Res&ume", ID_THREAD_RESUME - MENUITEM SEPARATOR - MENUITEM "&Affinity", ID_THREAD_AFFINITY - MENUITEM "Per&missions", ID_THREAD_PERMISSIONS - MENUITEM "&Token", ID_THREAD_TOKEN - POPUP "Analy&ze" - BEGIN - MENUITEM "Wait", ID_ANALYZE_WAIT - END - POPUP "&Priority" - BEGIN - MENUITEM "Time critical", ID_PRIORITY_TIMECRITICAL - MENUITEM "Highest", ID_PRIORITY_HIGHEST - MENUITEM "Above normal", ID_PRIORITY_ABOVENORMAL - MENUITEM "Normal", ID_PRIORITY_NORMAL - MENUITEM "Below normal", ID_PRIORITY_BELOWNORMAL - MENUITEM "Lowest", ID_PRIORITY_LOWEST - MENUITEM "Idle", ID_PRIORITY_IDLE - END - POPUP "I/O priority" - BEGIN - MENUITEM "High", ID_IOPRIORITY_HIGH - MENUITEM "Normal", ID_IOPRIORITY_NORMAL - MENUITEM "Low", ID_IOPRIORITY_LOW - MENUITEM "Very low", ID_IOPRIORITY_VERYLOW - END - POPUP "Page priority" - BEGIN - MENUITEM "Normal", ID_PAGEPRIORITY_NORMAL - MENUITEM "Below normal", ID_PAGEPRIORITY_BELOWNORMAL - MENUITEM "Medium", ID_PAGEPRIORITY_MEDIUM - MENUITEM "Low", ID_PAGEPRIORITY_LOW - MENUITEM "Very low", ID_PAGEPRIORITY_VERYLOW - END - MENUITEM SEPARATOR - MENUITEM "&Copy\aCtrl+C", ID_THREAD_COPY - END -END - -IDR_HANDLE MENU -BEGIN - POPUP "Handle" - BEGIN - MENUITEM "C&lose\aDel", ID_HANDLE_CLOSE - MENUITEM "&Protected", ID_HANDLE_PROTECTED - MENUITEM "&Inherit", ID_HANDLE_INHERIT - MENUITEM SEPARATOR - MENUITEM "&Copy\aCtrl+C", ID_HANDLE_COPY - MENUITEM "Prope&rties\aEnter", ID_HANDLE_PROPERTIES - END -END - -IDR_MODULE MENU -BEGIN - POPUP "Module" - BEGIN - MENUITEM "&Unload\aDel", ID_MODULE_UNLOAD - MENUITEM SEPARATOR - MENUITEM "&Inspect\aEnter", ID_MODULE_INSPECT - MENUITEM "&Search online\aCtrl+M", ID_MODULE_SEARCHONLINE - MENUITEM "Open &file location\aCtrl+Enter", ID_MODULE_OPENFILELOCATION - MENUITEM "P&roperties", ID_MODULE_PROPERTIES - MENUITEM "&Copy\aCtrl+C", ID_MODULE_COPY - END -END - -IDR_COMPUTER MENU -BEGIN - POPUP "Computer" - BEGIN - MENUITEM "&Lock", ID_COMPUTER_LOCK - MENUITEM "Log o&ff", ID_COMPUTER_LOGOFF - MENUITEM SEPARATOR - MENUITEM "&Sleep", ID_COMPUTER_SLEEP - MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE - MENUITEM SEPARATOR - MENUITEM "R&estart", ID_COMPUTER_RESTART - MENUITEM "Restart to boot &options", ID_COMPUTER_RESTARTBOOTOPTIONS - MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN - MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID - END -END - -IDR_PROCESS MENU -BEGIN - POPUP "Process" - BEGIN - MENUITEM "T&erminate\aDel", ID_PROCESS_TERMINATE - MENUITEM "Terminate tree\aShift+Del", ID_PROCESS_TERMINATETREE - MENUITEM "&Suspend", ID_PROCESS_SUSPEND - MENUITEM "Res&ume", ID_PROCESS_RESUME - MENUITEM "Res&tart", ID_PROCESS_RESTART - MENUITEM SEPARATOR - MENUITEM "Create dump file...", ID_PROCESS_CREATEDUMPFILE - MENUITEM "Debug", ID_PROCESS_DEBUG - MENUITEM "Virtualization", ID_PROCESS_VIRTUALIZATION - MENUITEM SEPARATOR - MENUITEM "&Affinity", ID_PROCESS_AFFINITY - POPUP "&Priority" - BEGIN - MENUITEM "Real time", ID_PRIORITY_REALTIME - MENUITEM "High", ID_PRIORITY_HIGH - MENUITEM "Above normal", ID_PRIORITY_ABOVENORMAL - MENUITEM "Normal", ID_PRIORITY_NORMAL - MENUITEM "Below normal", ID_PRIORITY_BELOWNORMAL - MENUITEM "Idle", ID_PRIORITY_IDLE - END - POPUP "&I/O priority" - BEGIN - MENUITEM "High", ID_IOPRIORITY_HIGH - MENUITEM "Normal", ID_IOPRIORITY_NORMAL - MENUITEM "Low", ID_IOPRIORITY_LOW - MENUITEM "Very low", ID_IOPRIORITY_VERYLOW - END - POPUP "&Miscellaneous" - BEGIN - MENUITEM "Detach from debugger", ID_MISCELLANEOUS_DETACHFROMDEBUGGER - MENUITEM "GDI handles", ID_MISCELLANEOUS_GDIHANDLES - MENUITEM "Inject DLL...", ID_MISCELLANEOUS_INJECTDLL - POPUP "Page priority" - BEGIN - MENUITEM "Normal", ID_PAGEPRIORITY_NORMAL - MENUITEM "Below normal", ID_PAGEPRIORITY_BELOWNORMAL - MENUITEM "Medium", ID_PAGEPRIORITY_MEDIUM - MENUITEM "Low", ID_PAGEPRIORITY_LOW - MENUITEM "Very low", ID_PAGEPRIORITY_VERYLOW - END - MENUITEM "Reduce working set", ID_MISCELLANEOUS_REDUCEWORKINGSET - MENUITEM "Run as...", ID_MISCELLANEOUS_RUNAS - MENUITEM "Run as this user...", ID_MISCELLANEOUS_RUNASTHISUSER - END - POPUP "&Window" - BEGIN - MENUITEM "Bring to front", ID_WINDOW_BRINGTOFRONT - MENUITEM "Restore", ID_WINDOW_RESTORE - MENUITEM "Minimize", ID_WINDOW_MINIMIZE - MENUITEM "Maximize", ID_WINDOW_MAXIMIZE - MENUITEM SEPARATOR - MENUITEM "Close", ID_WINDOW_CLOSE - END - MENUITEM SEPARATOR - MENUITEM "Search online\aCtrl+M", ID_PROCESS_SEARCHONLINE - MENUITEM "Open &file location\aCtrl+Enter", ID_PROCESS_OPENFILELOCATION - MENUITEM "P&roperties\aEnter", ID_PROCESS_PROPERTIES - MENUITEM "&Copy\aCtrl+C", ID_PROCESS_COPY - END -END - -IDR_SERVICE MENU -BEGIN - POPUP "Service" - BEGIN - MENUITEM "&Go to process", ID_SERVICE_GOTOPROCESS - MENUITEM "&Start", ID_SERVICE_START - MENUITEM "C&ontinue", ID_SERVICE_CONTINUE - MENUITEM "&Pause", ID_SERVICE_PAUSE - MENUITEM "S&top", ID_SERVICE_STOP - MENUITEM "&Delete\aDel", ID_SERVICE_DELETE - MENUITEM SEPARATOR - MENUITEM "Open &key", ID_SERVICE_OPENKEY - MENUITEM "Open &file location\aCtrl+Enter", ID_SERVICE_OPENFILELOCATION - MENUITEM "P&roperties\aEnter", ID_SERVICE_PROPERTIES - MENUITEM "&Copy\aCtrl+C", ID_SERVICE_COPY - END -END - -IDR_PRIVILEGE MENU -BEGIN - POPUP "Privilege" - BEGIN - MENUITEM "&Enable", ID_PRIVILEGE_ENABLE - MENUITEM "&Disable", ID_PRIVILEGE_DISABLE - MENUITEM "&Remove", ID_PRIVILEGE_REMOVE - MENUITEM SEPARATOR - MENUITEM "&Copy\aCtrl+C", ID_PRIVILEGE_COPY - END -END - -IDR_FINDOBJ MENU -BEGIN - POPUP "Object" - BEGIN - MENUITEM "C&lose\aDel", ID_OBJECT_CLOSE - MENUITEM SEPARATOR - MENUITEM "Go to owning &process", ID_OBJECT_GOTOOWNINGPROCESS - MENUITEM "Prope&rties", ID_OBJECT_PROPERTIES - MENUITEM "&Copy\aCtrl+C", ID_OBJECT_COPY - END -END - -IDR_USER MENU -BEGIN - POPUP "User" - BEGIN - MENUITEM "&Connect", ID_USER_CONNECT - MENUITEM "&Disconnect", ID_USER_DISCONNECT - MENUITEM "&Logoff", ID_USER_LOGOFF - MENUITEM "Rem&ote control", ID_USER_REMOTECONTROL - MENUITEM "Send &message...", ID_USER_SENDMESSAGE - MENUITEM "P&roperties", ID_USER_PROPERTIES - END -END - -IDR_NETWORK MENU -BEGIN - POPUP "Network" - BEGIN - MENUITEM "&Go to process\aEnter", ID_NETWORK_GOTOPROCESS - MENUITEM "Go to service", ID_NETWORK_GOTOSERVICE - MENUITEM "View &stack", ID_NETWORK_VIEWSTACK - MENUITEM "C&lose", ID_NETWORK_CLOSE - MENUITEM SEPARATOR - MENUITEM "&Copy\aCtrl+C", ID_NETWORK_COPY - END -END - -IDR_ICON MENU -BEGIN - POPUP "Icon" - BEGIN - MENUITEM "&Show/Hide Process Hacker", ID_ICON_SHOWHIDEPROCESSHACKER - MENUITEM "System &information", ID_ICON_SYSTEMINFORMATION - POPUP "N&otifications" - BEGIN - MENUITEM "Enable all", ID_NOTIFICATIONS_ENABLEALL - MENUITEM "Disable all", ID_NOTIFICATIONS_DISABLEALL - MENUITEM SEPARATOR - MENUITEM "New processes", ID_NOTIFICATIONS_NEWPROCESSES - MENUITEM "Terminated processes", ID_NOTIFICATIONS_TERMINATEDPROCESSES - MENUITEM "New services", ID_NOTIFICATIONS_NEWSERVICES - MENUITEM "Started services", ID_NOTIFICATIONS_STARTEDSERVICES - MENUITEM "Stopped services", ID_NOTIFICATIONS_STOPPEDSERVICES - MENUITEM "Deleted services", ID_NOTIFICATIONS_DELETEDSERVICES - END - POPUP "&Processes" - BEGIN - MENUITEM "Dummy", ID_PROCESSES_DUMMY - END - MENUITEM SEPARATOR - POPUP "&Computer" - BEGIN - MENUITEM "&Lock", ID_COMPUTER_LOCK - MENUITEM "Log o&ff", ID_COMPUTER_LOGOFF - MENUITEM SEPARATOR - MENUITEM "&Sleep", ID_COMPUTER_SLEEP - MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE - MENUITEM SEPARATOR - MENUITEM "R&estart", ID_COMPUTER_RESTART - MENUITEM "Restart to boot options", ID_COMPUTER_RESTARTBOOTOPTIONS - MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN - MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID - END - MENUITEM "E&xit", ID_ICON_EXIT - END -END - -IDR_MEMORY MENU -BEGIN - POPUP "Memory" - BEGIN - MENUITEM "&Read/Write memory", ID_MEMORY_READWRITEMEMORY - MENUITEM "&Save...", ID_MEMORY_SAVE - MENUITEM "Change &protection...", ID_MEMORY_CHANGEPROTECTION - MENUITEM "&Free", ID_MEMORY_FREE - MENUITEM "&Decommit", ID_MEMORY_DECOMMIT - MENUITEM SEPARATOR - MENUITEM "Read/Write &address...", ID_MEMORY_READWRITEADDRESS - MENUITEM "&Copy\aCtrl+C", ID_MEMORY_COPY - END -END - -IDR_MEMFILTER MENU -BEGIN - POPUP "Filter" - BEGIN - MENUITEM "Contains...", ID_FILTER_CONTAINS - MENUITEM "Contains (case-insensitive)...", ID_FILTER_CONTAINS_CASEINSENSITIVE - MENUITEM "Regex...", ID_FILTER_REGEX - MENUITEM "Regex (case-insensitive)...", ID_FILTER_REGEX_CASEINSENSITIVE - END -END - -IDR_EMPTYMEMLISTS MENU -BEGIN - POPUP "Empty" - BEGIN - MENUITEM "Empty working sets", ID_EMPTY_EMPTYWORKINGSETS - MENUITEM "Empty modified page list", ID_EMPTY_EMPTYMODIFIEDPAGELIST - MENUITEM "Empty standby list", ID_EMPTY_EMPTYSTANDBYLIST - MENUITEM "Empty priority 0 standby list", ID_EMPTY_EMPTYPRIORITY0STANDBYLIST - END -END - -IDR_MINIINFO MENU -BEGIN - POPUP "Mini Info" - BEGIN - POPUP "&Opacity" - BEGIN - MENUITEM "10%", ID_OPACITY_10 - MENUITEM "20%", ID_OPACITY_20 - MENUITEM "30%", ID_OPACITY_30 - MENUITEM "40%", ID_OPACITY_40 - MENUITEM "50%", ID_OPACITY_50 - MENUITEM "60%", ID_OPACITY_60 - MENUITEM "70%", ID_OPACITY_70 - MENUITEM "80%", ID_OPACITY_80 - MENUITEM "90%", ID_OPACITY_90 - MENUITEM "Opaque", ID_OPACITY_OPAQUE - END - MENUITEM SEPARATOR - MENUITEM "&Refresh\aF5", ID_MINIINFO_REFRESH - MENUITEM "Refresh a&utomatically\aF6", ID_MINIINFO_REFRESHAUTOMATICALLY - END -END - -IDR_MINIINFO_PROCESS MENU -BEGIN - POPUP "Process" - BEGIN - MENUITEM SEPARATOR - MENUITEM "&Go to process", ID_PROCESS_GOTOPROCESS - END -END - -IDR_ENVIRONMENT MENU -BEGIN - POPUP "Environment" - BEGIN - MENUITEM "&Edit", ID_ENVIRONMENT_EDIT - MENUITEM "&Delete\aDel", ID_ENVIRONMENT_DELETE - MENUITEM "&Copy\aCtrl+C", ID_ENVIRONMENT_COPY - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_PROCGENERAL DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Permissions",IDC_PERMISSIONS,143,197,50,14 - PUSHBUTTON "Terminate",IDC_TERMINATE,197,197,50,14 - GROUPBOX "File",IDC_FILE,7,7,246,79 - ICON "",IDC_FILEICON,14,18,20,20 - EDITTEXT IDC_NAME,44,17,182,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - EDITTEXT IDC_COMPANYNAME,44,29,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - CONTROL "Company name link",IDC_COMPANYNAME_LINK,"SysLink",LWS_NOPREFIX | NOT WS_VISIBLE | WS_TABSTOP,46,29,183,9 - LTEXT "Version:",IDC_STATIC,15,41,27,8 - EDITTEXT IDC_VERSION,44,41,113,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Image file name:",IDC_STATIC,15,55,56,8 - EDITTEXT IDC_FILENAME,15,65,191,12,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Inspect",IDC_INSPECT,210,64,17,15,BS_ICON - PUSHBUTTON "Open",IDC_OPENFILENAME,230,64,17,15,BS_ICON - GROUPBOX "Process",IDC_PROCESS,7,92,246,161 - LTEXT "Command line:",IDC_STATIC,13,103,50,8 - EDITTEXT IDC_CMDLINE,79,101,147,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Current directory:",IDC_STATIC,13,119,60,8 - EDITTEXT IDC_CURDIR,79,117,168,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Started:",IDC_STATIC,13,135,28,8 - EDITTEXT IDC_STARTED,79,133,168,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "PEB address:",IDC_STATIC,13,151,44,8 - EDITTEXT IDC_PEBADDRESS,79,149,101,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Image type:",IDC_PROCESSTYPELABEL,185,151,41,8,NOT WS_VISIBLE - LTEXT "32-bit",IDC_PROCESSTYPETEXT,228,151,20,8,NOT WS_VISIBLE - LTEXT "Parent:",IDC_STATIC,13,167,25,8 - EDITTEXT IDC_PARENTPROCESS,79,165,147,12,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "View",IDC_VIEWPARENTPROCESS,230,163,17,15,BS_ICON - LTEXT "Mitigation policies:",IDC_STATIC,13,183,59,8 - EDITTEXT IDC_MITIGATION,79,181,126,12,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Details",IDC_VIEWMITIGATION,209,180,38,14 - LTEXT "Protection:",IDC_STATIC,13,200,36,8 - EDITTEXT IDC_PROTECTION,51,200,80,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - PUSHBUTTON "View",IDC_VIEWCOMMANDLINE,230,100,17,15,BS_ICON -END - -IDD_PROCMODULES DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Modules" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,7,7,246,246,WS_EX_CLIENTEDGE -END - -IDD_PROCTHREADS DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Threads" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Start module:",IDC_STATICBL1,7,170,44,8 - EDITTEXT IDC_STARTMODULE,55,168,177,12,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Open",IDC_OPENSTARTMODULE,236,166,17,15,BS_ICON | WS_DISABLED - LTEXT "Started:",IDC_STATICBL2,7,184,28,8 - LTEXT "N/A",IDC_STARTED,75,184,178,8 - LTEXT "Kernel time:",IDC_STATICBL3,7,207,40,8 - LTEXT "N/A",IDC_KERNELTIME,75,207,51,8 - LTEXT "User time:",IDC_STATICBL4,7,217,35,8 - LTEXT "N/A",IDC_USERTIME,75,217,51,8 - LTEXT "Context switches:",IDC_STATICBL5,7,228,60,8 - LTEXT "N/A",IDC_CONTEXTSWITCHES,75,228,51,8,SS_ENDELLIPSIS - LTEXT "Cycles:",IDC_STATICBL6,7,239,24,8 - LTEXT "N/A",IDC_CYCLES,41,239,85,8,SS_ENDELLIPSIS - LTEXT "Base priority:",IDC_STATICBL9,136,205,44,8 - LTEXT "Priority:",IDC_STATICBL8,136,195,26,8 - LTEXT "I/O priority:",IDC_STATICBL10,136,216,39,8 - LTEXT "Page priority:",IDC_STATICBL11,136,227,44,8 - LTEXT "State:",IDC_STATICBL7,7,195,21,8 - LTEXT "N/A",IDC_STATE,41,195,85,8,SS_ENDELLIPSIS - LTEXT "N/A",IDC_PRIORITY,195,195,58,8 - LTEXT "N/A",IDC_BASEPRIORITY,195,205,58,8 - LTEXT "N/A",IDC_IOPRIORITY,195,216,58,8 - LTEXT "N/A",IDC_PAGEPRIORITY,195,227,58,8 - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x22,7,7,246,156,WS_EX_CLIENTEDGE - LTEXT "Ideal processor:",IDC_STATICBL12,136,239,53,8 - LTEXT "N/A",IDC_IDEALPROCESSOR,195,239,58,8 -END - -IDD_PROCHANDLES DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Handles" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Hide unnamed handles",IDC_HIDEUNNAMEDHANDLES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,88,10 - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,7,21,246,232,WS_EX_CLIENTEDGE -END - -IDD_PROCENVIRONMENT DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Environment" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,246,227 - PUSHBUTTON "New...",IDC_NEW,93,239,50,14 - PUSHBUTTON "Edit...",IDC_EDIT,148,239,50,14 - PUSHBUTTON "Delete",IDC_DELETE,203,239,50,14 -END - -IDD_THRDSTACK DIALOGEX 0, 0, 261, 228 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Thread Stack" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,247,196 - PUSHBUTTON "Copy",IDC_COPY,96,207,50,14 - PUSHBUTTON "Refresh",IDC_REFRESH,150,207,50,14 - PUSHBUTTON "Close",IDOK,204,207,50,14 -END - -IDD_ABOUT DIALOGEX 0, 0, 270, 195 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "About" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - ICON IDI_PROCESSHACKER,IDC_STATIC,16,15,20,20 - LTEXT "Process Hacker",IDC_ABOUT_NAME,45,14,192,8 - LTEXT "Licensed under the GNU GPL, v3.",IDC_STATIC,45,27,193,8 - LTEXT "Copyright (c) 2008-2017 Wen Jia Liu (wj32)",IDC_STATIC,15,40,140,8 - CONTROL "Credits.",IDC_CREDITS,"SysLink",WS_TABSTOP,15,55,248,115 - CONTROL "Process Hacker on SourceForge.net",IDC_LINK_SF, - "SysLink",WS_TABSTOP,7,177,130,11 - PUSHBUTTON "Diagnostics",IDC_DIAGNOSTICS,160,174,50,14 - DEFPUSHBUTTON "OK",IDOK,213,174,50,14 -END - -IDD_SRVLIST DIALOGEX 0, 0, 237, 201 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,0,237,141 - EDITTEXT IDC_DESCRIPTION,0,144,237,40,ES_MULTILINE | ES_READONLY | NOT WS_BORDER - PUSHBUTTON "&Start",IDC_START,134,187,50,14 - PUSHBUTTON "&Pause",IDC_PAUSE,187,187,50,14 -END - -IDD_SRVGENERAL DIALOGEX 0, 0, 282, 183 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - EDITTEXT IDC_DESCRIPTION,7,7,268,45,ES_MULTILINE | ES_READONLY | WS_VSCROLL - LTEXT "Type:",IDC_STATIC,7,57,20,8 - COMBOBOX IDC_TYPE,30,56,110,65,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Start type:",IDC_STATIC,147,57,38,8 - COMBOBOX IDC_STARTTYPE,188,56,87,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Error control:",IDC_STATIC,7,75,47,8 - COMBOBOX IDC_ERRORCONTROL,58,73,82,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Group:",IDC_STATIC,147,75,23,8 - EDITTEXT IDC_GROUP,173,74,102,12,ES_AUTOHSCROLL - LTEXT "Binary path:",IDC_STATIC,7,92,40,8 - EDITTEXT IDC_BINARYPATH,58,91,163,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,225,90,50,14 - LTEXT "User account:",IDC_STATIC,7,110,46,8 - EDITTEXT IDC_USERACCOUNT,58,108,217,12,ES_AUTOHSCROLL - LTEXT "Password:",IDC_STATIC,7,127,34,8 - EDITTEXT IDC_PASSWORD,58,125,204,12,ES_PASSWORD | ES_AUTOHSCROLL - CONTROL "",IDC_PASSWORDCHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,266,126,9,10 - LTEXT "Service DLL:",IDC_STATIC,7,144,48,8 - EDITTEXT IDC_SERVICEDLL,58,142,217,12,ES_AUTOHSCROLL | ES_READONLY - CONTROL "Delayed start",IDC_DELAYEDSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,158,59,10 -END - -IDD_HNDLGENERAL DIALOGEX 0, 0, 260, 181 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Basic information",IDC_BASICINFORMATION,7,7,246,67 - LTEXT "Name:",IDC_STATIC,14,19,22,8 - EDITTEXT IDC_NAME,40,19,206,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Type:",IDC_STATIC,14,32,20,8 - EDITTEXT IDC_TYPE,40,32,206,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Object address:",IDC_STATIC,14,44,53,8 - EDITTEXT IDC_ADDRESS,71,44,175,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Granted access:",IDC_STATIC,14,56,54,8 - EDITTEXT IDC_GRANTED_ACCESS,71,56,175,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - GROUPBOX "References",IDC_STATIC,7,78,119,39 - LTEXT "References:",IDC_STATIC,14,90,40,8 - LTEXT "Static",IDC_REFERENCES,58,90,41,8 - LTEXT "Handles:",IDC_STATIC,14,101,29,8 - LTEXT "Static",IDC_HANDLES,58,101,40,8 - GROUPBOX "Quota charges",IDC_STATIC,131,78,122,39 - LTEXT "Paged:",IDC_STATIC,138,89,24,8 - LTEXT "Static",IDC_PAGED,182,89,39,8 - LTEXT "Non-paged:",IDC_STATIC,138,100,39,8 - LTEXT "Static",IDC_NONPAGED,182,100,39,8 -END - -IDD_INFORMATION DIALOGEX 0, 0, 317, 184 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Information" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - EDITTEXT IDC_TEXT,7,7,303,152,ES_MULTILINE | ES_READONLY | WS_VSCROLL - PUSHBUTTON "Save...",IDC_SAVE,154,163,50,14 - PUSHBUTTON "Copy",IDC_COPY,207,163,50,14 - DEFPUSHBUTTON "Close",IDOK,260,163,50,14 -END - -IDD_FINDOBJECTS DIALOGEX 0, 0, 357, 233 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Find Handles or DLLs" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Filter:",IDC_STATIC,7,9,20,8 - EDITTEXT IDC_FILTER,32,8,223,12,ES_AUTOHSCROLL - PUSHBUTTON "Find",IDOK,300,7,50,14 - CONTROL "",IDC_RESULTS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,26,343,200 - CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,261,9,36,10 -END - -IDD_OBJTOKEN DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Token" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "User:",IDC_STATIC,7,7,18,8 - EDITTEXT IDC_USER,48,7,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "User SID:",IDC_STATIC,7,18,32,8 - EDITTEXT IDC_USERSID,48,18,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Session:",IDC_STATIC,7,29,28,8 - LTEXT "Unknown",IDC_SESSIONID,38,29,30,8 - LTEXT "Elevated:",IDC_STATIC,85,29,32,8 - LTEXT "Unknown",IDC_ELEVATED,120,29,30,8 - LTEXT "Virtualized:",IDC_STATIC,169,29,36,8 - LTEXT "Unknown",IDC_VIRTUALIZED,209,29,44,8 - LTEXT "App container SID:",IDC_STATIC,7,40,62,8 - EDITTEXT IDC_APPCONTAINERSID,75,40,178,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - CONTROL "",IDC_GROUPS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,53,246,84 - CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,142,246,83 - LTEXT "To view capabilities, claims and other attributes, click Advanced.",IDC_INSTRUCTION,7,228,206,8 - PUSHBUTTON "Integrity",IDC_INTEGRITY,149,239,50,14 - PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14 -END - -IDD_HIDDENPROCESSES DIALOGEX 0, 0, 337, 221 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Hidden Processes" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Processes highlighted red are hidden while those highlighted grey have terminated.",IDC_INTRO,7,7,323,12 - CONTROL "",IDC_PROCESSES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,23,323,162 - RTEXT "",IDC_DESCRIPTION,7,187,323,11 - COMBOBOX IDC_METHOD,7,201,73,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Terminate",IDC_TERMINATE,115,200,50,14 - PUSHBUTTON "Save...",IDC_SAVE,170,200,50,14 - PUSHBUTTON "&Scan",IDC_SCAN,225,200,50,14 - DEFPUSHBUTTON "Close",IDOK,280,200,50,14 -END - -IDD_RUNAS DIALOGEX 0, 0, 278, 127 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Run As" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Enter the command to start as the specified user.",IDC_STATIC,7,7,160,8 - LTEXT "Program:",IDC_STATIC,7,23,30,8 - EDITTEXT IDC_PROGRAM,53,21,163,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,221,20,50,14 - LTEXT "User name:",IDC_STATIC,7,40,38,8 - COMBOBOX IDC_USERNAME,53,38,123,12,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Type:",IDC_STATIC,183,40,20,8 - COMBOBOX IDC_TYPE,207,38,64,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Password:",IDC_STATIC,7,56,34,8 - EDITTEXT IDC_PASSWORD,53,55,123,12,ES_PASSWORD | ES_AUTOHSCROLL - CONTROL "Toggle elevation",IDC_TOGGLEELEVATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,56,69,10 - LTEXT "Session ID:",IDC_STATIC,7,73,37,8 - EDITTEXT IDC_SESSIONID,53,72,104,12,ES_AUTOHSCROLL | ES_NUMBER - PUSHBUTTON "...",IDC_SESSIONS,161,71,16,14 - LTEXT "Desktop:",IDC_STATIC,7,90,30,8 - EDITTEXT IDC_DESKTOP,53,88,104,12,ES_AUTOHSCROLL - PUSHBUTTON "...",IDC_DESKTOPS,161,87,16,14 - DEFPUSHBUTTON "OK",IDOK,168,106,50,14 - PUSHBUTTON "Cancel",IDCANCEL,221,106,50,14 -END - -IDD_PROGRESS DIALOGEX 0, 0, 216, 62 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Progress" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,159,41,50,14 - CONTROL "",IDC_PROGRESS,"msctls_progress32",0x0,7,23,202,14 - LTEXT "Please wait...",IDC_PROGRESSTEXT,7,7,202,12 -END - -IDD_PAGEFILES DIALOGEX 0, 0, 322, 162 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Pagefiles" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,308,128 - PUSHBUTTON "Refresh",IDC_REFRESH,211,141,50,14 - DEFPUSHBUTTON "Close",IDOK,265,141,50,14 -END - -IDD_TOKGENERAL DIALOGEX 0, 0, 270, 228 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Token",IDC_STATIC,7,7,256,132 - LTEXT "User:",IDC_STATIC,15,19,18,8 - EDITTEXT IDC_USER,71,17,185,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "User SID:",IDC_STATIC,15,36,32,8 - EDITTEXT IDC_USERSID,71,34,185,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Owner:",IDC_STATIC,15,53,25,8 - EDITTEXT IDC_OWNER,71,51,185,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Primary group:",IDC_STATIC,15,70,49,8 - EDITTEXT IDC_PRIMARYGROUP,71,68,185,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Session ID:",IDC_STATIC,15,87,37,8 - EDITTEXT IDC_SESSIONID,71,85,88,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Elevated:",IDC_STATIC,15,104,32,8 - EDITTEXT IDC_ELEVATED,71,102,117,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Virtualization:",IDC_STATIC,15,121,44,8 - EDITTEXT IDC_VIRTUALIZATION,71,119,185,12,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Linked token",IDC_LINKEDTOKEN,193,101,63,14 - GROUPBOX "Source",IDC_STATIC,7,143,256,47 - LTEXT "Name:",IDC_STATIC,15,155,22,8 - EDITTEXT IDC_SOURCENAME,71,153,185,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "LUID:",IDC_STATIC,15,173,19,8 - EDITTEXT IDC_SOURCELUID,71,171,185,12,ES_AUTOHSCROLL | ES_READONLY -END - -IDD_TOKADVANCED DIALOGEX 0, 0, 236, 186 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Advanced" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Type:",IDC_STATIC,7,8,20,8 - EDITTEXT IDC_TYPE,81,7,116,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Impersonation level:",IDC_STATIC,7,26,68,8 - EDITTEXT IDC_IMPERSONATIONLEVEL,81,24,116,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Token LUID:",IDC_STATIC,7,43,55,8 - EDITTEXT IDC_TOKENLUID,81,41,116,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Authentication LUID:",IDC_STATIC,7,61,68,8 - EDITTEXT IDC_AUTHENTICATIONLUID,81,59,116,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Memory used:",IDC_STATIC,7,78,47,8 - EDITTEXT IDC_MEMORYUSED,81,76,116,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Memory available:",IDC_STATIC,7,95,60,8 - EDITTEXT IDC_MEMORYAVAILABLE,81,93,116,12,ES_AUTOHSCROLL | ES_READONLY -END - -IDD_OBJJOB DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Job" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Name:",IDC_STATIC,7,9,22,8 - EDITTEXT IDC_NAME,35,8,164,12,ES_AUTOHSCROLL - PUSHBUTTON "Terminate",IDC_TERMINATE,203,7,50,14 - LTEXT "Processes in job:",IDC_STATIC,7,25,55,8 - CONTROL "",IDC_PROCESSES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,38,246,62 - PUSHBUTTON "Add...",IDC_ADD,203,103,50,14 - LTEXT "Limits:",IDC_STATIC,7,117,21,8 - CONTROL "",IDC_LIMITS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,129,246,107 - PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14 -END - -IDD_OBJEVENT DIALOGEX 0, 0, 186, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Event" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Type:",IDC_STATIC,7,7,20,8 - LTEXT "Unknown",IDC_TYPE,42,7,137,8 - LTEXT "Signaled:",IDC_STATIC,7,18,30,8 - LTEXT "Unknown",IDC_SIGNALED,42,18,137,8 - PUSHBUTTON "Set",IDC_SET,7,34,50,14 - PUSHBUTTON "Reset",IDC_RESET,61,34,50,14 - PUSHBUTTON "Pulse",IDC_PULSE,115,34,50,14 -END - -IDD_OBJMUTANT DIALOGEX 0, 0, 186, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Mutant" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Count:",IDC_STATIC,7,7,23,8 - LTEXT "Unknown",IDC_COUNT,55,7,124,8 - LTEXT "Abandoned:",IDC_STATIC,7,18,40,8 - LTEXT "Unknown",IDC_ABANDONED,55,18,124,8 - LTEXT "Owner:",IDC_OWNERLABEL,7,29,25,8 - LTEXT "Unknown",IDC_OWNER,55,29,124,8 -END - -IDD_OBJSEMAPHORE DIALOGEX 0, 0, 186, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Semaphore" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Current count:",IDC_STATIC,7,7,50,8 - LTEXT "Unknown",IDC_CURRENTCOUNT,66,7,113,8 - LTEXT "Maximum count:",IDC_STATIC,7,18,54,8 - LTEXT "Unknown",IDC_MAXIMUMCOUNT,66,18,113,8 - PUSHBUTTON "Acquire",IDC_ACQUIRE,7,34,50,14 - PUSHBUTTON "Release",IDC_RELEASE,61,34,50,14 -END - -IDD_OBJTIMER DIALOGEX 0, 0, 186, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Timer" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Signaled:",-1,7,7,30,8 - LTEXT "Unknown",IDC_SIGNALED,42,7,137,8 - PUSHBUTTON "Cancel",IDC_CANCEL,7,19,50,14 -END - -IDD_JOBSTATISTICS DIALOGEX 0, 0, 259, 186 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Statistics" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "General",IDC_STATIC,7,7,133,47 - LTEXT "Active processes",IDC_STATIC,15,18,55,8 - RTEXT "Static",IDC_ZACTIVEPROCESSES_V,99,18,37,8,SS_ENDELLIPSIS - LTEXT "Total processes",IDC_STATIC,15,28,51,8 - RTEXT "Static",IDC_ZTOTALPROCESSES_V,95,29,41,8,SS_ENDELLIPSIS - LTEXT "Terminated processes",IDC_STATIC,15,39,71,8 - RTEXT "Static",IDC_ZTERMINATEDPROCESSES_V,99,39,37,8,SS_ENDELLIPSIS - GROUPBOX "Time",IDC_STATIC,7,57,133,57 - LTEXT "User time",IDC_STATIC,15,68,32,8 - LTEXT "Kernel time",IDC_STATIC,15,79,38,8 - LTEXT "User time (period)",IDC_STATIC,15,89,60,8 - LTEXT "Kernel time (period)",IDC_STATIC,15,99,65,8 - RTEXT "Static",IDC_ZUSERTIME_V,83,68,53,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZKERNELTIME_V,85,79,51,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZUSERTIMEPERIOD_V,87,89,49,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZKERNELTIMEPERIOD_V,87,99,49,8,SS_ENDELLIPSIS - GROUPBOX "Memory",IDC_STATIC,7,118,133,48 - LTEXT "Page faults",IDC_STATIC,15,129,38,8 - LTEXT "Peak process usage",IDC_STATIC,15,140,65,8 - LTEXT "Peak job usage",IDC_STATIC,15,151,52,8 - RTEXT "Static",IDC_ZPAGEFAULTS_V,86,129,50,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPEAKPROCESSUSAGE_V,85,140,51,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPEAKJOBUSAGE_V,86,151,50,8,SS_ENDELLIPSIS - GROUPBOX "I/O",IDC_STATIC,146,7,106,75 - LTEXT "Reads",IDC_STATIC,152,17,21,8 - LTEXT "Read bytes",IDC_STATIC,152,27,38,8 - LTEXT "Writes",IDC_STATIC,152,37,22,8 - LTEXT "Write bytes",IDC_STATIC,152,47,38,8 - LTEXT "Other",IDC_STATIC,152,57,20,8 - LTEXT "Other bytes",IDC_STATIC,152,67,40,8 - RTEXT "Static",IDC_ZIOREADS_V,200,17,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOREADBYTES_V,200,27,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOWRITES_V,200,37,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOWRITEBYTES_V,200,47,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOOTHER_V,200,57,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOOTHERBYTES_V,200,67,47,8,SS_ENDELLIPSIS -END - -IDD_OBJEVENTPAIR DIALOGEX 0, 0, 186, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Event Pair" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Set low",IDC_SETLOW,7,7,50,14 - PUSHBUTTON "Set high",IDC_SETHIGH,61,7,50,14 -END - -IDD_OBJSECTION DIALOGEX 0, 0, 255, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Section" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Type:",IDC_STATIC,7,7,20,8 - LTEXT "Unknown",IDC_TYPE,43,7,205,8 - LTEXT "Size:",IDC_STATIC,7,18,16,8 - LTEXT "Unknown",IDC_SIZE_,43,18,205,8 - LTEXT "File:",IDC_STATIC,7,29,14,8 - EDITTEXT IDC_FILE,43,29,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER -END - -IDD_AFFINITY DIALOGEX 0, 0, 279, 227 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Affinity" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,169,206,50,14 - PUSHBUTTON "Cancel",IDCANCEL,222,206,50,14 - LTEXT "Affinity controls which CPUs threads are allowed to execute on.",IDC_STATIC,7,7,265,11 - CONTROL "CPU 0",IDC_CPU0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,21,35,10 - CONTROL "CPU 1",IDC_CPU1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,33,35,10 - CONTROL "CPU 2",IDC_CPU2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,44,35,10 - CONTROL "CPU 3",IDC_CPU3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,56,35,10 - CONTROL "CPU 4",IDC_CPU4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,35,10 - CONTROL "CPU 5",IDC_CPU5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,78,35,10 - CONTROL "CPU 6",IDC_CPU6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,89,35,10 - CONTROL "CPU 7",IDC_CPU7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,35,10 - CONTROL "CPU 8",IDC_CPU8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,111,35,10 - CONTROL "CPU 9",IDC_CPU9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,122,35,10 - CONTROL "CPU 10",IDC_CPU10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,134,39,10 - CONTROL "CPU 11",IDC_CPU11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,145,39,10 - CONTROL "CPU 12",IDC_CPU12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,156,39,10 - CONTROL "CPU 13",IDC_CPU13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,167,39,10 - CONTROL "CPU 14",IDC_CPU14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,178,39,10 - CONTROL "CPU 15",IDC_CPU15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,190,39,10 - CONTROL "CPU 16",IDC_CPU16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,21,39,10 - CONTROL "CPU 17",IDC_CPU17,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,33,39,10 - CONTROL "CPU 18",IDC_CPU18,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,44,39,10 - CONTROL "CPU 19",IDC_CPU19,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,56,39,10 - CONTROL "CPU 20",IDC_CPU20,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,67,39,10 - CONTROL "CPU 21",IDC_CPU21,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,78,39,10 - CONTROL "CPU 22",IDC_CPU22,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,89,39,10 - CONTROL "CPU 23",IDC_CPU23,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,100,39,10 - CONTROL "CPU 24",IDC_CPU24,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,111,39,10 - CONTROL "CPU 25",IDC_CPU25,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,122,39,10 - CONTROL "CPU 26",IDC_CPU26,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,134,39,10 - CONTROL "CPU 27",IDC_CPU27,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,145,39,10 - CONTROL "CPU 28",IDC_CPU28,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,156,39,10 - CONTROL "CPU 29",IDC_CPU29,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,167,39,10 - CONTROL "CPU 30",IDC_CPU30,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,178,39,10 - CONTROL "CPU 31",IDC_CPU31,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,190,39,10 - CONTROL "CPU 32",IDC_CPU32,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,21,39,10 - CONTROL "CPU 33",IDC_CPU33,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,33,39,10 - CONTROL "CPU 34",IDC_CPU34,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,44,39,10 - CONTROL "CPU 35",IDC_CPU35,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,56,39,10 - CONTROL "CPU 36",IDC_CPU36,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,67,39,10 - CONTROL "CPU 37",IDC_CPU37,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,78,39,10 - CONTROL "CPU 38",IDC_CPU38,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,89,39,10 - CONTROL "CPU 39",IDC_CPU39,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,100,39,10 - CONTROL "CPU 40",IDC_CPU40,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,111,39,10 - CONTROL "CPU 41",IDC_CPU41,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,122,39,10 - CONTROL "CPU 42",IDC_CPU42,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,134,39,10 - CONTROL "CPU 43",IDC_CPU43,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,145,39,10 - CONTROL "CPU 44",IDC_CPU44,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,156,39,10 - CONTROL "CPU 45",IDC_CPU45,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,167,39,10 - CONTROL "CPU 46",IDC_CPU46,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,178,39,10 - CONTROL "CPU 47",IDC_CPU47,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,190,39,10 - CONTROL "CPU 48",IDC_CPU48,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,21,39,10 - CONTROL "CPU 49",IDC_CPU49,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,33,39,10 - CONTROL "CPU 50",IDC_CPU50,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,44,39,10 - CONTROL "CPU 51",IDC_CPU51,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,56,39,10 - CONTROL "CPU 52",IDC_CPU52,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,67,39,10 - CONTROL "CPU 53",IDC_CPU53,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,78,39,10 - CONTROL "CPU 54",IDC_CPU54,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,89,39,10 - CONTROL "CPU 55",IDC_CPU55,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,100,39,10 - CONTROL "CPU 56",IDC_CPU56,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,111,39,10 - CONTROL "CPU 57",IDC_CPU57,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,122,39,10 - CONTROL "CPU 58",IDC_CPU58,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,134,39,10 - CONTROL "CPU 59",IDC_CPU59,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,145,39,10 - CONTROL "CPU 60",IDC_CPU60,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,156,39,10 - CONTROL "CPU 61",IDC_CPU61,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,167,39,10 - CONTROL "CPU 62",IDC_CPU62,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,178,39,10 - CONTROL "CPU 63",IDC_CPU63,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,190,39,10 - PUSHBUTTON "Select all",IDC_SELECTALL,7,206,50,14 - PUSHBUTTON "Deselect all",IDC_DESELECTALL,60,206,50,14 -END - -IDD_SYSINFO DIALOGEX 0, 0, 423, 247 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "System Information" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Click a graph to view details for that section.",IDC_INSTRUCTION,7,228,144,8 - CONTROL "Always on &top",IDC_ALWAYSONTOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,297,228,63,10 - DEFPUSHBUTTON "Close",IDOK,366,226,50,14 -END - -IDD_EDITMESSAGE DIALOGEX 0, 0, 282, 163 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Message" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Title:",IDC_STATIC,7,8,17,8 - EDITTEXT IDC_TITLE,52,7,223,12,ES_AUTOHSCROLL - LTEXT "Text:",IDC_STATIC,7,24,18,8 - EDITTEXT IDC_TEXT,52,23,223,79,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL - LTEXT "Icon:",IDC_STATIC,7,107,18,8 - COMBOBOX IDC_TYPE,52,105,80,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Timeout (s):",IDC_STATIC,7,123,40,8 - EDITTEXT IDC_TIMEOUT,52,122,43,12,ES_AUTOHSCROLL - DEFPUSHBUTTON "OK",IDOK,172,142,50,14 - PUSHBUTTON "Cancel",IDCANCEL,225,142,50,14 -END - -IDD_SESSION DIALOGEX 0, 0, 201, 149 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Session Properties" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "Close",IDOK,144,128,50,14 - LTEXT "User name:",IDC_STATIC,7,7,38,8 - LTEXT "Session ID:",IDC_STATIC,7,18,37,8 - LTEXT "State:",IDC_STATIC,7,29,21,8 - LTEXT "Client name:",IDC_STATIC,7,84,41,8 - LTEXT "Client address:",IDC_STATIC,7,95,49,8 - LTEXT "Client display:",IDC_STATIC,7,106,46,8 - LTEXT "N/A",IDC_USERNAME,66,7,128,8 - LTEXT "N/A",IDC_SESSIONID,66,18,128,8 - LTEXT "N/A",IDC_STATE,66,29,128,8 - LTEXT "N/A",IDC_CLIENTNAME,66,84,128,8 - LTEXT "N/A",IDC_CLIENTADDRESS,66,95,128,8 - LTEXT "N/A",IDC_CLIENTDISPLAY,66,106,128,8 - LTEXT "Logon time:",IDC_STATIC,7,40,38,8 - LTEXT "N/A",IDC_LOGONTIME,66,40,128,8 - LTEXT "Connect time:",IDC_STATIC,7,51,46,8 - LTEXT "Disconnect time:",IDC_STATIC,7,62,54,8 - LTEXT "Last input time:",IDC_STATIC,7,73,50,8 - LTEXT "N/A",IDC_CONNECTTIME,66,51,128,8 - LTEXT "N/A",IDC_DISCONNECTTIME,66,62,128,8 - LTEXT "N/A",IDC_LASTINPUTTIME,66,74,128,8 -END - -IDD_PROCMEMORY DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Memory" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Strings...",IDC_STRINGS,150,7,50,14 - PUSHBUTTON "Refresh",IDC_REFRESH,203,7,50,14 - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,7,26,246,227,WS_EX_CLIENTEDGE - CONTROL "Hide free regions",IDC_HIDEFREEREGIONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,9,71,10 -END - -IDD_CHOOSE DIALOGEX 0, 0, 199, 73 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Dialog" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Text:",IDC_MESSAGE,7,7,185,8,SS_ENDELLIPSIS - COMBOBOX IDC_CHOICE,7,20,185,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP - CONTROL "Option",IDC_OPTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,39,185,10 - DEFPUSHBUTTON "OK",IDOK,89,52,50,14 - PUSHBUTTON "Cancel",IDCANCEL,142,52,50,14 - COMBOBOX IDC_CHOICEUSER,7,20,185,30,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP - EDITTEXT IDC_CHOICESIMPLE,7,20,185,13,ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_VISIBLE -END - -IDD_OPTGENERAL DIALOGEX 0, 0, 250, 154 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Search engine:",IDC_STATIC,7,8,49,8 - EDITTEXT IDC_SEARCHENGINE,61,7,182,12,ES_AUTOHSCROLL - LTEXT "PE viewer:",IDC_STATIC,7,23,35,8 - EDITTEXT IDC_PEVIEWER,61,22,182,12,ES_AUTOHSCROLL - LTEXT "Max. size unit:",IDC_STATIC,7,38,49,8 - COMBOBOX IDC_MAXSIZEUNIT,61,37,39,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Icon processes:",IDC_STATIC,7,55,52,8 - EDITTEXT IDC_ICONPROCESSES,61,53,40,12,ES_AUTOHSCROLL | ES_NUMBER - CONTROL "Allow only one instance",IDC_ALLOWONLYONEINSTANCE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,71,91,10 - CONTROL "Hide when closed",IDC_HIDEONCLOSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,83,71,10 - CONTROL "Hide when minimized",IDC_HIDEONMINIMIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,95,81,10 - CONTROL "Start hidden",IDC_STARTHIDDEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,119,55,10 - CONTROL "Collapse services on start",IDC_COLLAPSESERVICES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,71,98,10 - CONTROL "Single-click tray icons",IDC_ICONSINGLECLICK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,83,83,10 - CONTROL "Icon click toggles visibility",IDC_ICONTOGGLESVISIBILITY, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,95,97,10 - CONTROL "Enable plugins",IDC_ENABLEPLUGINS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,122,107,61,10 - PUSHBUTTON "Font...",IDC_FONT,7,133,50,14 - CONTROL "Start when I log on",IDC_STARTATLOGON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,107,77,10 -END - -IDD_OPTHIGHLIGHTING DIALOGEX 0, 0, 250, 174 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Highlighting" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Highlighting duration:",IDC_STATIC,7,8,70,8 - EDITTEXT IDC_HIGHLIGHTINGDURATION,79,7,40,12,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "New objects:",IDC_STATIC,7,25,44,8 - CONTROL "New objects",IDC_NEWOBJECTS,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,57,23,33,13 - LTEXT "Removed objects:",IDC_STATIC,127,25,60,8 - CONTROL "Removed objects",IDC_REMOVEDOBJECTS,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,193,23,33,13 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,7,41,236,107 - LTEXT "Double-click an item to change it.",IDC_STATIC,7,156,106,8 - PUSHBUTTON "Enable all",IDC_ENABLEALL,140,153,50,14 - PUSHBUTTON "Disable all",IDC_DISABLEALL,193,153,50,14 -END - -IDD_CHOOSECOLUMNS DIALOGEX 0, 0, 381, 207 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Choose Columns" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Select the columns that will appear in the list.",IDC_MESSAGE,7,7,367,10 - LTEXT "Inactive columns:",IDC_STATIC,7,21,57,8 - LISTBOX IDC_INACTIVE,7,31,129,150,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Show >",IDC_SHOW,139,86,50,14 - PUSHBUTTON "< Hide",IDC_HIDE,139,103,50,14 - LISTBOX IDC_ACTIVE,192,31,129,150,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Move up",IDC_MOVEUP,324,31,50,14 - PUSHBUTTON "Move down",IDC_MOVEDOWN,324,48,50,14 - DEFPUSHBUTTON "OK",IDOK,272,186,50,14 - PUSHBUTTON "Cancel",IDCANCEL,324,186,50,14 - LTEXT "Active columns:",IDC_STATIC,192,21,51,8 -END - -IDD_NETSTACK DIALOGEX 0, 0, 261, 228 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Network Stack" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,247,196 - PUSHBUTTON "Close",IDOK,204,207,50,14 -END - -IDD_CREATESERVICE DIALOGEX 0, 0, 287, 133 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Create Service" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Name:",IDC_STATIC,7,8,22,8 - EDITTEXT IDC_NAME,61,7,219,12,ES_AUTOHSCROLL - LTEXT "Display name:",IDC_STATIC,7,24,46,8 - EDITTEXT IDC_DISPLAYNAME,61,23,219,12,ES_AUTOHSCROLL - LTEXT "Type:",IDC_STATIC,7,41,20,8 - COMBOBOX IDC_TYPE,61,39,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Start type:",IDC_STATIC,7,58,38,8 - COMBOBOX IDC_STARTTYPE,61,56,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Error control:",IDC_STATIC,7,75,45,8 - COMBOBOX IDC_ERRORCONTROL,61,73,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Binary path:",IDC_STATIC,7,91,40,8 - EDITTEXT IDC_BINARYPATH,61,90,165,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,230,89,50,14 - DEFPUSHBUTTON "OK",IDOK,176,112,50,14 - PUSHBUTTON "Cancel",IDCANCEL,230,112,50,14 -END - -IDD_PROCPERFORMANCE DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Performance" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "CPU",IDC_GROUPCPU,7,7,246,74,0,WS_EX_TRANSPARENT - GROUPBOX "Private bytes",IDC_GROUPPRIVATEBYTES,7,86,246,77,0,WS_EX_TRANSPARENT - GROUPBOX "I/O",IDC_GROUPIO,7,164,246,89,0,WS_EX_TRANSPARENT - CONTROL "",IDC_CPU,"PhGraph",WS_CLIPSIBLINGS,105,42,50,14 - CONTROL "",IDC_PRIVATEBYTES,"PhGraph",WS_CLIPSIBLINGS,105,122,50,14 - CONTROL "",IDC_IO,"PhGraph",WS_CLIPSIBLINGS,105,204,50,14 -END - -IDD_PROCSTATISTICS DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Statistics" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "CPU",IDC_STATIC,7,7,119,64 - GROUPBOX "I/O",IDC_STATIC,131,7,122,83 - LTEXT "Priority",IDC_STATIC,14,17,24,8 - LTEXT "Cycles",IDC_STATIC,14,27,22,8 - LTEXT "Kernel time",IDC_STATIC,14,37,38,8 - LTEXT "User time",IDC_STATIC,14,47,32,8 - LTEXT "Total time",IDC_STATIC,14,57,34,8 - RTEXT "Static",IDC_ZPRIORITY_V,59,17,61,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCYCLES_V,43,27,77,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZKERNELTIME_V,59,37,61,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZUSERTIME_V,59,47,61,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZTOTALTIME_V,59,57,61,8,SS_ENDELLIPSIS - GROUPBOX "Memory",IDC_STATIC,7,74,120,128 - LTEXT "Private bytes",IDC_STATIC,14,84,44,8 - LTEXT "Working set",IDC_STATIC,14,136,40,8 - LTEXT "Peak working set",IDC_STATIC,14,178,57,8 - LTEXT "Virtual size",IDC_STATIC,14,105,36,8 - LTEXT "Peak virtual size",IDC_STATIC,14,115,53,8 - LTEXT "Peak private bytes",IDC_STATIC,14,95,61,8 - LTEXT "Page faults",IDC_STATIC,14,125,38,8 - LTEXT "Page priority",IDC_STATIC,14,188,42,8 - RTEXT "Static",IDC_ZPRIVATEBYTES_V,72,84,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWORKINGSET_V,72,136,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPEAKWORKINGSET_V,78,178,42,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZVIRTUALSIZE_V,72,105,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPEAKVIRTUALSIZE_V,72,115,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPEAKPRIVATEBYTES_V,80,95,40,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGEFAULTS_V,72,125,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGEPRIORITY_V,72,188,48,8,SS_ENDELLIPSIS - LTEXT "Reads",IDC_STATIC,138,17,21,8 - LTEXT "Read bytes",IDC_STATIC,138,27,38,8 - LTEXT "Writes",IDC_STATIC,138,37,22,8 - LTEXT "Write bytes",IDC_STATIC,138,47,38,8 - LTEXT "Other",IDC_STATIC,138,57,20,8 - LTEXT "Other bytes",IDC_STATIC,138,67,40,8 - LTEXT "I/O priority",IDC_STATIC,138,77,36,8 - RTEXT "Static",IDC_ZIOREADS_V,184,17,63,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOREADBYTES_V,184,27,63,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOWRITES_V,184,37,63,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOWRITEBYTES_V,184,47,63,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOOTHER_V,184,57,63,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOOTHERBYTES_V,184,67,63,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZIOPRIORITY_V,184,77,63,8,SS_ENDELLIPSIS - GROUPBOX "Other",IDC_STATIC,131,92,122,70 - LTEXT "Handles",IDC_STATIC,138,102,26,8 - LTEXT "GDI handles",IDC_STATIC,138,122,40,8 - LTEXT "USER handles",IDC_STATIC,138,132,46,8 - RTEXT "Static",IDC_ZHANDLES_V,193,102,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZGDIHANDLES_V,193,122,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZUSERHANDLES_V,193,132,54,8,SS_ENDELLIPSIS - PUSHBUTTON "Details",IDC_DETAILS,137,143,50,14 - LTEXT "Peak handles",IDC_STATIC,138,112,44,8 - RTEXT "Static",IDC_ZPEAKHANDLES_V,192,112,55,8,SS_ENDELLIPSIS - LTEXT "Private WS",IDC_STATIC,22,147,36,8 - LTEXT "Shareable WS",IDC_STATIC,22,157,46,8 - LTEXT "Shared WS",IDC_STATIC,22,167,36,8 - RTEXT "Static",IDC_ZPRIVATEWS_V,78,147,42,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSHAREABLEWS_V,78,157,42,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSHAREDWS_V,78,167,42,8,SS_ENDELLIPSIS -END - -IDD_OPTADVANCED DIALOGEX 0, 0, 250, 150 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Advanced" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Enable warnings",IDC_ENABLEWARNINGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,68,10 - CONTROL "Enable kernel-mode driver",IDC_ENABLEKERNELMODEDRIVER, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,99,10 - CONTROL "Hide unnamed handles",IDC_HIDEUNNAMEDHANDLES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,31,88,10 - CONTROL "Check images for digital signatures and packing",IDC_ENABLESTAGE2, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,43,167,10 - CONTROL "Resolve addresses for network connections",IDC_ENABLENETWORKRESOLVE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,155,10 - CONTROL "Include CPU (and other) usage of children in collapsed processes",IDC_PROPAGATECPUUSAGE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,223,10 - CONTROL "Show tooltips instantly",IDC_ENABLEINSTANTTOOLTIPS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,79,88,10 - CONTROL "Enable cycle-based CPU usage",IDC_ENABLECYCLECPUUSAGE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,91,114,10 - CONTROL "Replace Task Manager with Process Hacker",IDC_REPLACETASKMANAGER, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,105,154,10 - PUSHBUTTON "Change...",IDC_CHANGE,161,103,62,14 - LTEXT "History sample count:",IDC_SAMPLECOUNTLABEL,7,121,70,8 - EDITTEXT IDC_SAMPLECOUNT,82,120,39,12,ES_AUTOHSCROLL | ES_NUMBER - CONTROL "Automatic",IDC_SAMPLECOUNTAUTOMATIC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,126,121,48,10 -END - -IDD_GDIHANDLES DIALOGEX 0, 0, 351, 307 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "GDI Handles" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,337,275 - PUSHBUTTON "Refresh",IDC_REFRESH,240,286,50,14 - DEFPUSHBUTTON "Close",IDOK,294,286,50,14 -END - -IDD_LOG DIALOGEX 0, 0, 313, 303 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Log" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,299,272 - PUSHBUTTON "Clear",IDC_CLEAR,7,282,50,14 - CONTROL "Auto-scroll",IDC_AUTOSCROLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61,284,50,10 - PUSHBUTTON "Save...",IDC_SAVE,148,282,50,14 - PUSHBUTTON "Copy",IDC_COPY,202,282,50,14 - DEFPUSHBUTTON "Close",IDOK,256,282,50,14 -END - -IDD_OPTSYMBOLS DIALOGEX 0, 0, 250, 75 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Symbols" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Dbghelp.dll path:",IDC_STATIC,7,9,56,8 - EDITTEXT IDC_DBGHELPPATH,66,8,123,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,193,7,50,14 - LTEXT "Search path:",IDC_STATIC,7,24,42,8 - EDITTEXT IDC_DBGHELPSEARCHPATH,66,23,177,12,ES_AUTOHSCROLL - CONTROL "Undecorate symbols",IDC_UNDECORATESYMBOLS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,38,81,10 -END - -IDD_MEMEDIT DIALOGEX 0, 0, 441, 269 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Memory" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_MEMORY,"PhHexEdit",WS_CLIPSIBLINGS | WS_VSCROLL | WS_TABSTOP,7,7,427,239 - PUSHBUTTON "Re-read",IDC_REREAD,7,248,50,14 - PUSHBUTTON "Write",IDC_WRITE,60,248,50,14 - PUSHBUTTON "Go to...",IDC_GOTO,112,248,50,14 - PUSHBUTTON "Save...",IDC_SAVE,331,248,50,14 - PUSHBUTTON "Close",IDOK,384,248,50,14 - COMBOBOX IDC_BYTESPERROW,164,249,86,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP -END - -IDD_MEMPROTECT DIALOGEX 0, 0, 270, 171 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Memory Protection" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - EDITTEXT IDC_INTRO,7,7,256,122,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "New value:",IDC_STATIC,120,136,37,8 - EDITTEXT IDC_VALUE,161,135,102,12,ES_AUTOHSCROLL - DEFPUSHBUTTON "OK",IDOK,161,150,50,14 - PUSHBUTTON "Cancel",IDCANCEL,213,150,50,14 -END - -IDD_MEMRESULTS DIALOGEX 0, 0, 313, 266 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Results" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Results.",IDC_INTRO,7,9,299,8 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,21,299,221 - PUSHBUTTON "Filter",IDC_FILTER,7,245,50,14 - PUSHBUTTON "Save...",IDC_SAVE,149,245,50,14 - PUSHBUTTON "Copy",IDC_COPY,203,245,50,14 - DEFPUSHBUTTON "Close",IDOK,256,245,50,14 -END - -IDD_MEMSTRING DIALOGEX 0, 0, 241, 86 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "String Search" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Minimum length:",IDC_STATIC,7,8,54,8 - EDITTEXT IDC_MINIMUMLENGTH,67,7,51,12,ES_AUTOHSCROLL - CONTROL "Detect Unicode",IDC_DETECTUNICODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,22,65,10 - LTEXT "Search in the following types of memory regions:",IDC_STATIC,7,36,157,8 - CONTROL "Private",IDC_PRIVATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,49,39,10 - CONTROL "Image",IDC_IMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,55,49,36,10 - CONTROL "Mapped",IDC_MAPPED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,101,49,41,10 - DEFPUSHBUTTON "OK",IDOK,131,65,50,14 - PUSHBUTTON "Cancel",IDCANCEL,184,65,50,14 -END - -IDD_OPTGRAPHS DIALOGEX 0, 0, 250, 156 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Graphs" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Show text",IDC_SHOWTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,49,10 - CONTROL "Use old colors (black background)",IDC_USEOLDCOLORS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,123,10 - CONTROL "Show commit charge instead of physical memory in summary view",IDC_SHOWCOMMITINSUMMARY, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,31,227,10 - LTEXT "CPU kernel:",IDC_STATIC,7,48,39,8 - CONTROL "CPU kernel",IDC_CPUKERNEL,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,46,33,13 - LTEXT "CPU user:",IDC_STATIC,7,65,34,8 - CONTROL "CPU user",IDC_CPUUSER,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,63,33,13 - LTEXT "I/O R+O:",IDC_STATIC,7,82,32,8 - CONTROL "I/O R+O",IDC_IORO,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,80,33,13 - LTEXT "I/O W:",IDC_STATIC,7,98,23,8 - CONTROL "I/O W",IDC_IOW,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,97,33,13 - LTEXT "Private bytes:",IDC_STATIC,7,115,46,8 - CONTROL "Private bytes",IDC_PRIVATE,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,113,33,13 - LTEXT "Physical memory:",IDC_STATIC,7,131,56,8 - CONTROL "Physical memory",IDC_PHYSICAL,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,129,33,13 -END - -IDD_PLUGINS DIALOGEX 0, 0, 291, 272 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Plugins" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,277,119 - GROUPBOX "Plugin",IDC_STATIC,7,129,277,118 - LTEXT "Name:",IDC_STATIC,15,141,22,8 - EDITTEXT IDC_NAME,71,141,104,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Internal name:",IDC_STATIC,15,152,49,8 - EDITTEXT IDC_INTERNALNAME,71,152,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Author:",IDC_STATIC,15,163,26,8 - EDITTEXT IDC_AUTHOR,71,163,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "URL:",IDC_STATIC,15,174,16,8 - EDITTEXT IDC_URL,71,174,174,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - CONTROL "Open",IDC_OPENURL,"SysLink",WS_TABSTOP,250,174,26,10 - LTEXT "File name:",IDC_STATIC,15,185,34,8 - EDITTEXT IDC_FILENAME,71,185,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Version:",IDC_STATIC,183,141,27,8 - EDITTEXT IDC_VERSION,215,141,59,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Description:",IDC_STATIC,15,196,39,8 - EDITTEXT IDC_DESCRIPTION,14,208,211,34,ES_MULTILINE | ES_READONLY - PUSHBUTTON "Disable",IDC_DISABLE,230,211,50,14 - PUSHBUTTON "Options...",IDC_OPTIONS,230,228,50,14 - PUSHBUTTON "Clean up",IDC_CLEANUP,7,251,50,14 - LTEXT "Changes may require a restart to take effect.",IDC_INSTRUCTION,82,254,148,8,NOT WS_VISIBLE - DEFPUSHBUTTON "Close",IDOK,234,251,50,14 -END - -IDD_HANDLESTATS DIALOGEX 0, 0, 219, 175 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Handle Statistics" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,205,143 - DEFPUSHBUTTON "Close",IDOK,162,154,50,14 -END - -IDD_PROCRECORD DIALOGEX 0, 0, 260, 202 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Process Record" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Name:",IDC_STATIC,7,9,22,8 - EDITTEXT IDC_PROCESSNAME,39,9,214,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Parent:",IDC_STATIC,7,21,25,8 - EDITTEXT IDC_PARENT,39,21,214,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - GROUPBOX "File",IDC_FILE,7,38,246,79 - ICON "",IDC_FILEICON,14,49,20,20 - EDITTEXT IDC_NAME,44,48,182,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - EDITTEXT IDC_COMPANYNAME,44,60,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Version:",IDC_STATIC,15,72,27,8 - EDITTEXT IDC_VERSION,44,72,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Image file name:",IDC_STATIC,15,86,56,8 - EDITTEXT IDC_FILENAME,15,96,211,12,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Open",IDC_OPENFILENAME,230,95,17,15,BS_ICON - LTEXT "Command line:",IDC_STATIC,7,123,50,8 - EDITTEXT IDC_CMDLINE,65,121,188,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Started:",IDC_STATIC,7,139,28,8 - EDITTEXT IDC_STARTED,65,137,188,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Terminated:",IDC_STATIC,7,155,40,8 - EDITTEXT IDC_TERMINATED,65,153,188,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Session ID:",IDC_STATIC,7,170,37,8 - LTEXT "Static",IDC_SESSIONID,50,170,19,8 - PUSHBUTTON "Properties",IDC_PROPERTIES,150,181,50,14 - DEFPUSHBUTTON "Close",IDOK,203,181,50,14 -END - -IDD_CHOOSEPROCESS DIALOGEX 0, 0, 317, 239 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Select a Process" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Select a process from the list below.",IDC_MESSAGE,7,7,303,8,SS_ENDELLIPSIS - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,21,303,193 - PUSHBUTTON "Refresh",IDC_REFRESH,7,218,50,14 - DEFPUSHBUTTON "OK",IDOK,205,218,50,14 - PUSHBUTTON "Cancel",IDCANCEL,260,218,50,14 -END - -IDD_PROCSERVICES DIALOGEX 0, 0, 260, 261 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Services" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Services",IDC_SERVICES_LAYOUT,7,7,246,247,NOT WS_VISIBLE | WS_BORDER -END - -IDD_SHADOWSESSION DIALOGEX 0, 0, 228, 84 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Remote Control" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "To end the remote control session, press this key and the modifiers selected below:",IDC_STATIC,7,7,214,19 - COMBOBOX IDC_VIRTUALKEY,7,27,74,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Shift",IDC_SHIFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,47,31,10 - CONTROL "Ctrl",IDC_CTRL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,49,47,27,10 - CONTROL "Alt",IDC_ALT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,89,47,25,10 - DEFPUSHBUTTON "OK",IDOK,117,63,50,14 - PUSHBUTTON "Cancel",IDCANCEL,171,63,50,14 -END - -IDD_TOKCAPABILITIES DIALOGEX 0, 0, 270, 228 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Capabilities" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,256,214 -END - -IDD_TOKATTRIBUTES DIALOGEX 0, 0, 270, 228 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Attributes" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x32,7,7,256,214,WS_EX_CLIENTEDGE -END - -IDD_SYSINFO_CPU DIALOGEX 0, 0, 316, 195 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -CAPTION "CPU" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - RTEXT "CPU name",IDC_CPUNAME,41,4,274,16,SS_WORDELLIPSIS - LTEXT "CPU",IDC_TITLE,0,0,37,21 - LTEXT "Panel layout",IDC_LAYOUT,0,105,315,88,NOT WS_VISIBLE - LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,82,NOT WS_VISIBLE -END - -IDD_SYSINFO_CPUPANEL DIALOGEX 0, 0, 237, 86 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Utilization:",IDC_STATIC,0,3,34,8 - LTEXT "Speed:",IDC_STATIC,108,3,24,8 - LTEXT "Static",IDC_UTILIZATION,40,0,62,15 - LTEXT "Static",IDC_SPEED,137,0,100,15 - GROUPBOX "System",IDC_STATIC,0,17,97,55 - LTEXT "Processes",IDC_STATIC,7,28,33,8 - LTEXT "Threads",IDC_STATIC,7,38,27,8 - LTEXT "Handles",IDC_STATIC,7,48,26,8 - LTEXT "Uptime",IDC_STATIC,7,58,23,8 - RTEXT "Static",IDC_ZPROCESSES_V,45,28,46,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZTHREADS_V,45,38,46,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZHANDLES_V,45,48,46,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZUPTIME_V,46,58,45,8,SS_ENDELLIPSIS - CONTROL "&Show one graph per CPU",IDC_ONEGRAPHPERCPU,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,76,96,10 - GROUPBOX "CPU",IDC_STATIC,101,17,136,55 - LTEXT "Context switches delta",IDC_STATIC,109,28,76,8 - LTEXT "Interrupts delta",IDC_STATIC,109,38,52,8 - LTEXT "DPCs delta",IDC_STATIC,109,48,36,8 - LTEXT "System calls delta",IDC_STATIC,109,58,60,8 - RTEXT "Static",IDC_ZCONTEXTSWITCHESDELTA_V,191,28,40,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZINTERRUPTSDELTA_V,185,39,46,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZDPCSDELTA_V,181,48,50,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSYSTEMCALLSDELTA_V,179,59,52,8,SS_ENDELLIPSIS -END - -IDD_SYSINFO_MEMPANEL DIALOGEX 0, 0, 358, 171 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Commit charge",IDC_STATIC,0,0,108,44 - LTEXT "Current",IDC_STATIC,7,11,26,8 - LTEXT "Peak",IDC_STATIC,7,21,16,8 - LTEXT "Limit",IDC_STATIC,7,31,15,8 - RTEXT "Static",IDC_ZCOMMITCURRENT_V,45,11,56,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCOMMITPEAK_V,41,21,60,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCOMMITLIMIT_V,40,31,61,8,SS_ENDELLIPSIS - GROUPBOX "Physical memory",IDC_STATIC,112,0,118,73 - LTEXT "Current",IDC_STATIC,120,11,26,8 - LTEXT "Cache WS",IDC_STATIC,120,41,34,8 - RTEXT "Static",IDC_ZPHYSICALCURRENT_V,165,11,57,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPHYSICALTOTAL_V,167,21,55,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPHYSICALCACHEWS_V,159,41,63,8,SS_ENDELLIPSIS - LTEXT "Total",IDC_STATIC,120,21,17,8 - LTEXT "Kernel WS",IDC_STATIC,120,51,34,8 - RTEXT "Static",IDC_ZPHYSICALKERNELWS_V,167,51,55,8,SS_ENDELLIPSIS - LTEXT "Driver WS",IDC_STATIC,120,61,33,8 - RTEXT "Static",IDC_ZPHYSICALDRIVERWS_V,163,61,59,8,SS_ENDELLIPSIS - GROUPBOX "Paged pool",IDC_STATIC,0,47,108,64 - LTEXT "Working set",IDC_STATIC,7,58,40,8 - LTEXT "Limit",IDC_STATIC,7,78,15,8 - RTEXT "Static",IDC_ZPAGEDWORKINGSET_V,54,58,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGEDVIRTUALSIZE_V,52,68,49,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGEDLIMIT_V,48,78,53,8,SS_ENDELLIPSIS - LTEXT "Virtual size",IDC_STATIC,7,68,36,8 - LTEXT "Allocs delta",IDC_STATIC,7,88,38,8 - RTEXT "Static",IDC_ZPAGEDALLOCSDELTA_V,53,88,48,8,SS_ENDELLIPSIS - LTEXT "Frees delta",IDC_STATIC,7,98,38,8 - RTEXT "Static",IDC_ZPAGEDFREESDELTA_V,51,98,50,8,SS_ENDELLIPSIS - GROUPBOX "Non-paged pool",IDC_STATIC,0,114,108,55 - LTEXT "Usage",IDC_STATIC,7,125,21,8 - LTEXT "Allocs delta",IDC_STATIC,7,145,38,8 - RTEXT "Static",IDC_ZNONPAGEDUSAGE_V,54,125,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZNONPAGEDLIMIT_V,52,135,49,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZNONPAGEDALLOCSDELTA_V,56,145,45,8,SS_ENDELLIPSIS - LTEXT "Limit",IDC_STATIC,7,135,15,8 - LTEXT "Frees delta",IDC_STATIC,7,155,38,8 - RTEXT "Static",IDC_ZNONPAGEDFREESDELTA_V,56,155,45,8,SS_ENDELLIPSIS - GROUPBOX "Memory lists",IDC_STATIC,234,0,123,169 - LTEXT "Zeroed",IDC_STATIC,242,11,24,8 - LTEXT "Modified",IDC_STATIC,242,31,28,8 - RTEXT "Static",IDC_ZLISTZEROED_V,304,11,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTFREE_V,302,21,49,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTMODIFIED_V,298,31,53,8,SS_ENDELLIPSIS - LTEXT "Free",IDC_STATIC,242,21,16,8 - LTEXT "Modified no-write",IDC_STATIC,242,41,56,8 - RTEXT "Static",IDC_ZLISTMODIFIEDNOWRITE_V,303,41,48,8,SS_ENDELLIPSIS - LTEXT "Standby",IDC_STATIC,242,61,28,8 - LTEXT "Priority 0",IDC_STATIC,251,71,30,8 - LTEXT "Priority 1",IDC_STATIC,251,80,30,8 - LTEXT "Priority 2",IDC_STATIC,251,90,30,8 - LTEXT "Priority 3",IDC_STATIC,251,100,30,8 - LTEXT "Priority 4",IDC_STATIC,251,110,30,8 - LTEXT "Priority 5",IDC_STATIC,251,120,30,8 - LTEXT "Priority 6",IDC_STATIC,251,130,30,8 - LTEXT "Priority 7",IDC_STATIC,251,140,30,8 - RTEXT "Static",IDC_ZLISTSTANDBY_V,303,61,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY0_V,303,71,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY1_V,303,80,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY2_V,303,90,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY3_V,303,100,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY4_V,303,110,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY5_V,303,120,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY6_V,303,130,48,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY7_V,303,140,48,8,SS_ENDELLIPSIS - GROUPBOX "Paging",IDC_STATIC,112,75,118,55 - LTEXT "Page faults delta",IDC_STATIC,120,86,57,8 - LTEXT "Pagefile writes delta",IDC_STATIC,120,106,68,8 - RTEXT "Static",IDC_ZPAGINGPAGEFAULTSDELTA_V,183,86,39,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGINGPAGEREADSDELTA_V,179,96,43,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGINGPAGEFILEWRITESDELTA_V,189,106,33,8,SS_ENDELLIPSIS - LTEXT "Page reads delta",IDC_STATIC,120,96,58,8 - LTEXT "Mapped writes delta",IDC_STATIC,120,116,68,8 - RTEXT "Static",IDC_ZPAGINGMAPPEDWRITESDELTA_V,191,116,31,8,SS_ENDELLIPSIS - LTEXT "Modified pagefile",IDC_STATIC,242,51,55,8 - RTEXT "Static",IDC_ZLISTMODIFIEDPAGEFILE_V,303,51,48,8,SS_ENDELLIPSIS - PUSHBUTTON "&More",IDC_MORE,242,152,50,14 - RTEXT "Static",IDC_ZPHYSICALRESERVED_V,167,31,55,8,SS_ENDELLIPSIS - LTEXT "Reserved",IDC_STATIC,120,31,32,8 -END - -IDD_SYSINFO_MEM DIALOGEX 0, 0, 316, 250 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -CAPTION "Memory" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Memory",IDC_TITLE,0,0,110,21 - LTEXT "Panel layout",IDC_LAYOUT,0,74,315,175,NOT WS_VISIBLE - LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,51,NOT WS_VISIBLE - RTEXT "Total physical",IDC_TOTALPHYSICAL,127,4,188,16,SS_WORDELLIPSIS - LTEXT "Commit charge:",IDC_COMMIT_L,111,1,52,8 - LTEXT "Physical memory:",IDC_PHYSICAL_L,111,7,56,8 -END - -IDD_SYSINFO_IO DIALOGEX 0, 0, 316, 187 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU -CAPTION "I/O" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "I/O",IDC_TITLE,0,0,110,21 - LTEXT "Panel layout",IDC_LAYOUT,0,109,315,78,NOT WS_VISIBLE - LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,84,NOT WS_VISIBLE -END - -IDD_SYSINFO_IOPANEL DIALOGEX 0, 0, 276, 75 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "I/O deltas",IDC_STATIC,0,0,133,74 - LTEXT "Reads delta",IDC_STATIC,7,11,40,8 - LTEXT "Read bytes delta",IDC_STATIC,7,21,56,8 - LTEXT "Writes delta",IDC_STATIC,7,31,40,8 - LTEXT "Write bytes delta",IDC_STATIC,7,41,57,8 - RTEXT "Static",IDC_ZREADSDELTA_V,59,11,67,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZREADBYTESDELTA_V,75,21,51,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITESDELTA_V,57,31,69,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,73,41,53,8,SS_ENDELLIPSIS - LTEXT "Other delta",IDC_STATIC,7,51,38,8 - LTEXT "Other bytes delta",IDC_STATIC,7,61,58,8 - RTEXT "Static",IDC_ZOTHERDELTA_V,67,51,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZOTHERBYTESDELTA_V,81,61,45,8,SS_ENDELLIPSIS - GROUPBOX "I/O totals",IDC_STATIC,137,0,133,74 - LTEXT "Reads",IDC_STATIC,145,11,21,8 - LTEXT "Read bytes",IDC_STATIC,145,21,38,8 - LTEXT "Writes",IDC_STATIC,145,31,22,8 - LTEXT "Write bytes",IDC_STATIC,145,41,38,8 - RTEXT "Static",IDC_ZREADS_V,197,11,67,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZREADBYTES_V,192,21,72,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITES_V,195,31,69,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITEBYTES_V,190,41,74,8,SS_ENDELLIPSIS - LTEXT "Other",IDC_STATIC,145,51,20,8 - LTEXT "Other bytes",IDC_STATIC,145,61,40,8 - RTEXT "Static",IDC_ZOTHER_V,191,51,73,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZOTHERBYTES_V,192,61,72,8,SS_ENDELLIPSIS -END - -IDD_MEMLISTS DIALOGEX 0, 0, 228, 195 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU -EXSTYLE WS_EX_TOPMOST -CAPTION "Memory Lists" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "Close",IDOK,171,174,50,14 - LTEXT "Zeroed",IDC_STATIC,7,7,24,8 - LTEXT "Free",IDC_STATIC,7,17,16,8 - LTEXT "Modified",IDC_STATIC,7,28,28,8 - LTEXT "Modified no-write",IDC_STATIC,7,39,56,8 - LTEXT "Bad",IDC_STATIC,7,60,13,8 - LTEXT "Standby",IDC_STATIC,7,72,28,8 - LTEXT "Priority 0",IDC_STATIC,23,83,30,8 - LTEXT "Priority 1",IDC_STATIC,23,94,30,8 - LTEXT "Priority 2",IDC_STATIC,23,105,30,8 - LTEXT "Priority 3",IDC_STATIC,23,116,30,8 - LTEXT "Priority 4",IDC_STATIC,23,128,30,8 - LTEXT "Priority 5",IDC_STATIC,23,139,30,8 - LTEXT "Priority 6",IDC_STATIC,23,150,30,8 - LTEXT "Priority 7",IDC_STATIC,23,161,30,8 - RTEXT "Static",IDC_ZLISTZEROED_V,73,7,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTFREE_V,73,17,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTMODIFIED_V,73,28,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTMODIFIEDNOWRITE_V,73,39,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTBAD_V,73,60,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY_V,73,72,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY0_V,73,83,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY1_V,73,94,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY2_V,73,105,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY3_V,73,116,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY4_V,73,128,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY5_V,73,139,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY6_V,73,150,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTSTANDBY7_V,73,161,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED_V,162,72,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED0_V,162,84,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED1_V,162,95,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED2_V,162,106,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED3_V,162,117,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED4_V,162,128,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED5_V,162,139,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED6_V,162,150,59,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZLISTREPURPOSED7_V,162,161,59,8,SS_ENDELLIPSIS - RTEXT "(Repurposed)",IDC_STATIC,162,60,59,8 - PUSHBUTTON "&Empty",IDC_EMPTY,117,174,50,14 - LTEXT "Modified pagefile",IDC_STATIC,7,49,55,8 - RTEXT "Static",IDC_ZLISTMODIFIEDPAGEFILE_V,73,49,59,8,SS_ENDELLIPSIS -END - -IDD_CONTAINER DIALOGEX 0, 0, 316, 182 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN -END - -IDD_SYSINFO_MEMPANELXP DIALOGEX 0, 0, 230, 171 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU -EXSTYLE WS_EX_CONTROLPARENT -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Commit charge",-1,0,0,108,44 - LTEXT "Current",-1,7,11,26,8 - LTEXT "Peak",-1,7,21,16,8 - LTEXT "Limit",-1,7,31,15,8 - RTEXT "Static",IDC_ZCOMMITCURRENT_V,45,11,56,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCOMMITPEAK_V,41,21,60,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCOMMITLIMIT_V,40,31,61,8,SS_ENDELLIPSIS - GROUPBOX "Physical memory",-1,112,0,118,64 - LTEXT "Current",-1,120,11,26,8 - LTEXT "Cache WS",-1,120,31,34,8 - RTEXT "Static",IDC_ZPHYSICALCURRENT_V,165,11,57,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPHYSICALTOTAL_V,167,21,55,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPHYSICALCACHEWS_V,159,31,63,8,SS_ENDELLIPSIS - LTEXT "Total",-1,120,21,17,8 - LTEXT "Kernel WS",-1,120,41,34,8 - RTEXT "Static",IDC_ZPHYSICALKERNELWS_V,167,41,55,8,SS_ENDELLIPSIS - LTEXT "Driver WS",-1,120,51,33,8 - RTEXT "Static",IDC_ZPHYSICALDRIVERWS_V,163,51,59,8,SS_ENDELLIPSIS - GROUPBOX "Paged pool",-1,0,47,108,64 - LTEXT "Working set",-1,7,58,40,8 - LTEXT "Limit",-1,7,78,15,8 - RTEXT "Static",IDC_ZPAGEDWORKINGSET_V,54,58,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGEDVIRTUALSIZE_V,52,68,49,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGEDLIMIT_V,48,78,53,8,SS_ENDELLIPSIS - LTEXT "Virtual size",-1,7,68,36,8 - LTEXT "Allocs delta",-1,7,88,38,8 - RTEXT "Static",IDC_ZPAGEDALLOCSDELTA_V,53,88,48,8,SS_ENDELLIPSIS - LTEXT "Frees delta",-1,7,98,38,8 - RTEXT "Static",IDC_ZPAGEDFREESDELTA_V,51,98,50,8,SS_ENDELLIPSIS - GROUPBOX "Non-paged pool",-1,0,114,108,55 - LTEXT "Usage",-1,7,125,21,8 - LTEXT "Allocs delta",-1,7,145,38,8 - RTEXT "Static",IDC_ZNONPAGEDUSAGE_V,54,125,47,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZNONPAGEDLIMIT_V,52,135,49,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZNONPAGEDALLOCSDELTA_V,56,145,45,8,SS_ENDELLIPSIS - LTEXT "Limit",-1,7,135,15,8 - LTEXT "Frees delta",-1,7,155,38,8 - RTEXT "Static",IDC_ZNONPAGEDFREESDELTA_V,56,155,45,8,SS_ENDELLIPSIS - GROUPBOX "Paging",-1,112,67,118,55 - LTEXT "Page faults delta",-1,120,78,57,8 - LTEXT "Pagefile writes delta",-1,120,98,68,8 - RTEXT "Static",IDC_ZPAGINGPAGEFAULTSDELTA_V,183,78,39,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGINGPAGEREADSDELTA_V,179,88,43,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZPAGINGPAGEFILEWRITESDELTA_V,189,98,33,8,SS_ENDELLIPSIS - LTEXT "Page reads delta",-1,120,88,58,8 - LTEXT "Mapped writes delta",-1,120,108,68,8 - RTEXT "Static",IDC_ZPAGINGMAPPEDWRITESDELTA_V,191,108,31,8,SS_ENDELLIPSIS -END - -IDD_MINIINFO DIALOGEX 0, 0, 217, 150 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Content layout",IDC_LAYOUT,4,3,210,128,NOT WS_VISIBLE - CONTROL "Section",IDC_SECTION,"Static",SS_LEFTNOWORDWRAP | SS_NOTIFY | SS_ENDELLIPSIS | WS_GROUP,4,134,177,13 - PUSHBUTTON "Options",IDC_OPTIONS,183,133,15,14,BS_ICON - CONTROL "Pin",IDC_PIN,"Button",BS_AUTOCHECKBOX | BS_ICON | BS_PUSHLIKE | WS_TABSTOP,199,133,15,14 -END - -IDD_MINIINFO_LIST DIALOGEX 0, 0, 217, 141 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "CPU" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x82,0,0,217,140,WS_EX_CLIENTEDGE -END - -IDD_MITIGATION DIALOGEX 0, 0, 277, 217 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Mitigation Policies" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,263,108 - LTEXT "Description:",IDC_DESCRIPTIONLABEL,7,118,38,8 - EDITTEXT IDC_DESCRIPTION,7,129,263,62,ES_MULTILINE | ES_READONLY | WS_VSCROLL - DEFPUSHBUTTON "OK",IDOK,220,196,50,14 -END - -IDD_EDITENV DIALOGEX 0, 0, 311, 177 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Edit Environment Variable" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Name:",IDC_STATIC,7,8,21,8 - EDITTEXT IDC_NAME,38,7,266,12,ES_AUTOHSCROLL - LTEXT "Value:",IDC_STATIC,7,25,21,8 - EDITTEXT IDC_VALUE,38,24,266,128,ES_MULTILINE | WS_VSCROLL - DEFPUSHBUTTON "OK",IDOK,200,156,50,14 - PUSHBUTTON "Cancel",IDCANCEL,254,156,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_PROCGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_PROCMODULES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_PROCTHREADS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_PROCHANDLES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_PROCENVIRONMENT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_THRDSTACK, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 254 - TOPMARGIN, 7 - BOTTOMMARGIN, 221 - END - - IDD_ABOUT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 263 - TOPMARGIN, 7 - BOTTOMMARGIN, 188 - END - - IDD_SRVLIST, DIALOG - BEGIN - END - - IDD_SRVGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 176 - END - - IDD_HNDLGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 174 - END - - IDD_INFORMATION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 310 - TOPMARGIN, 7 - BOTTOMMARGIN, 177 - END - - IDD_FINDOBJECTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 350 - TOPMARGIN, 7 - BOTTOMMARGIN, 226 - END - - IDD_OBJTOKEN, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_HIDDENPROCESSES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 330 - TOPMARGIN, 7 - BOTTOMMARGIN, 214 - END - - IDD_RUNAS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 271 - TOPMARGIN, 7 - BOTTOMMARGIN, 120 - END - - IDD_PROGRESS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 209 - TOPMARGIN, 7 - BOTTOMMARGIN, 55 - END - - IDD_PAGEFILES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 315 - TOPMARGIN, 7 - BOTTOMMARGIN, 155 - END - - IDD_TOKGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 263 - TOPMARGIN, 7 - BOTTOMMARGIN, 221 - END - - IDD_TOKADVANCED, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 229 - TOPMARGIN, 7 - BOTTOMMARGIN, 179 - END - - IDD_OBJJOB, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_OBJEVENT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 69 - END - - IDD_OBJMUTANT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 69 - END - - IDD_OBJSEMAPHORE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 69 - END - - IDD_OBJTIMER, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 69 - END - - IDD_JOBSTATISTICS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 252 - TOPMARGIN, 7 - BOTTOMMARGIN, 179 - END - - IDD_OBJEVENTPAIR, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 69 - END - - IDD_OBJSECTION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 248 - TOPMARGIN, 7 - BOTTOMMARGIN, 69 - END - - IDD_AFFINITY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 272 - TOPMARGIN, 7 - BOTTOMMARGIN, 220 - END - - IDD_SYSINFO, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 416 - TOPMARGIN, 7 - BOTTOMMARGIN, 240 - END - - IDD_EDITMESSAGE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 156 - END - - IDD_SESSION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 194 - TOPMARGIN, 7 - BOTTOMMARGIN, 142 - END - - IDD_PROCMEMORY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_CHOOSE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 192 - TOPMARGIN, 7 - BOTTOMMARGIN, 66 - END - - IDD_OPTGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 147 - END - - IDD_OPTHIGHLIGHTING, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 167 - END - - IDD_CHOOSECOLUMNS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 374 - TOPMARGIN, 7 - BOTTOMMARGIN, 200 - END - - IDD_NETSTACK, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 254 - TOPMARGIN, 7 - BOTTOMMARGIN, 221 - END - - IDD_CREATESERVICE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 280 - TOPMARGIN, 7 - BOTTOMMARGIN, 126 - END - - IDD_PROCPERFORMANCE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_PROCSTATISTICS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_OPTADVANCED, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 143 - END - - IDD_GDIHANDLES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 344 - TOPMARGIN, 7 - BOTTOMMARGIN, 300 - END - - IDD_LOG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 306 - TOPMARGIN, 7 - BOTTOMMARGIN, 296 - END - - IDD_OPTSYMBOLS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 68 - END - - IDD_MEMEDIT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 434 - TOPMARGIN, 7 - BOTTOMMARGIN, 262 - END - - IDD_MEMPROTECT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 263 - TOPMARGIN, 7 - BOTTOMMARGIN, 164 - END - - IDD_MEMRESULTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 306 - TOPMARGIN, 7 - BOTTOMMARGIN, 259 - END - - IDD_MEMSTRING, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 234 - TOPMARGIN, 7 - BOTTOMMARGIN, 79 - END - - IDD_OPTGRAPHS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 149 - END - - IDD_PLUGINS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 284 - TOPMARGIN, 7 - BOTTOMMARGIN, 265 - END - - IDD_HANDLESTATS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 212 - TOPMARGIN, 7 - BOTTOMMARGIN, 168 - END - - IDD_PROCRECORD, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 195 - END - - IDD_CHOOSEPROCESS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 310 - TOPMARGIN, 7 - BOTTOMMARGIN, 232 - END - - IDD_PROCSERVICES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 254 - END - - IDD_SHADOWSESSION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 221 - TOPMARGIN, 7 - BOTTOMMARGIN, 77 - END - - IDD_TOKCAPABILITIES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 263 - TOPMARGIN, 7 - BOTTOMMARGIN, 221 - END - - IDD_TOKATTRIBUTES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 263 - TOPMARGIN, 7 - BOTTOMMARGIN, 221 - END - - IDD_SYSINFO_CPU, DIALOG - BEGIN - BOTTOMMARGIN, 193 - END - - IDD_SYSINFO_CPUPANEL, DIALOG - BEGIN - BOTTOMMARGIN, 85 - END - - IDD_SYSINFO_MEMPANEL, DIALOG - BEGIN - END - - IDD_SYSINFO_MEM, DIALOG - BEGIN - BOTTOMMARGIN, 247 - END - - IDD_SYSINFO_IO, DIALOG - BEGIN - END - - IDD_SYSINFO_IOPANEL, DIALOG - BEGIN - BOTTOMMARGIN, 74 - END - - IDD_MEMLISTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 221 - TOPMARGIN, 7 - BOTTOMMARGIN, 188 - END - - IDD_CONTAINER, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 309 - TOPMARGIN, 7 - BOTTOMMARGIN, 175 - END - - IDD_SYSINFO_MEMPANELXP, DIALOG - BEGIN - END - - IDD_MINIINFO, DIALOG - BEGIN - LEFTMARGIN, 4 - RIGHTMARGIN, 214 - TOPMARGIN, 3 - BOTTOMMARGIN, 147 - END - - IDD_MINIINFO_LIST, DIALOG - BEGIN - END - - IDD_MITIGATION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 270 - TOPMARGIN, 7 - BOTTOMMARGIN, 210 - END - - IDD_EDITENV, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 304 - TOPMARGIN, 7 - BOTTOMMARGIN, 170 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Accelerator -// - -IDR_MAINWND_ACCEL ACCELERATORS -BEGIN - VK_ESCAPE, ID_ESC_EXIT, VIRTKEY, NOINVERT - "F", ID_HACKER_FINDHANDLESORDLLS, VIRTKEY, CONTROL, NOINVERT - "R", ID_HACKER_RUN, VIRTKEY, CONTROL, NOINVERT - "R", ID_HACKER_RUNAS, VIRTKEY, SHIFT, CONTROL, NOINVERT - "S", ID_HACKER_SAVE, VIRTKEY, CONTROL, NOINVERT - "L", ID_HELP_LOG, VIRTKEY, CONTROL, NOINVERT - "M", ID_PROCESS_SEARCHONLINE, VIRTKEY, CONTROL, NOINVERT - VK_TAB, ID_TAB_NEXT, VIRTKEY, CONTROL, NOINVERT - VK_TAB, ID_TAB_PREV, VIRTKEY, SHIFT, CONTROL, NOINVERT - VK_F5, ID_VIEW_REFRESH, VIRTKEY, NOINVERT - "I", ID_VIEW_SYSTEMINFORMATION, VIRTKEY, CONTROL, NOINVERT - VK_PAUSE, ID_VIEW_UPDATEAUTOMATICALLY, VIRTKEY, NOINVERT - VK_F6, ID_VIEW_UPDATEAUTOMATICALLY, VIRTKEY, NOINVERT -END - -IDR_SYSINFO_ACCEL ACCELERATORS -BEGIN - "1", ID_DIGIT1, VIRTKEY, NOINVERT - "2", ID_DIGIT2, VIRTKEY, NOINVERT - "3", ID_DIGIT3, VIRTKEY, NOINVERT - "4", ID_DIGIT4, VIRTKEY, NOINVERT - "5", ID_DIGIT5, VIRTKEY, NOINVERT - "6", ID_DIGIT6, VIRTKEY, NOINVERT - "7", ID_DIGIT7, VIRTKEY, NOINVERT - "8", ID_DIGIT8, VIRTKEY, NOINVERT - "9", ID_DIGIT9, VIRTKEY, NOINVERT - "B", IDC_BACK, VIRTKEY, ALT, NOINVERT - VK_BACK, IDC_BACK, VIRTKEY, NOINVERT - VK_F6, IDC_PAUSE, VIRTKEY, NOINVERT - VK_PAUSE, IDC_PAUSE, VIRTKEY, NOINVERT - VK_F5, IDC_REFRESH, VIRTKEY, NOINVERT - VK_F11, IDC_MAXSCREEN, VIRTKEY, NOINVERT -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Bitmap -// - -IDB_CROSS BITMAP "resources\\cross.bmp" - -IDB_TICK BITMAP "resources\\tick.bmp" - - -///////////////////////////////////////////////////////////////////////////// -// -// RT_MANIFEST -// - -IDR_RT_MANIFEST RT_MANIFEST "ProcessHacker.manifest" - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_FINDOBJECTS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_OPTADVANCED AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_SYSINFO_MEMPANEL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_ABOUT AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCGENERAL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_MITIGATION AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_INFORMATION AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_OPTGENERAL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_MINIINFO AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCTHREADS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCRECORD AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCENVIRONMENT AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_EDITENV AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" +#include "include/phappres.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "#include ""include/phappres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_PROCESSHACKER ICON "ProcessHacker.ico" + +IDI_PHAPPLICATION ICON "resources\\application.ico" + +IDI_COG ICON "resources\\cog.ico" + +IDI_PIN ICON "resources\\pin.ico" + +IDI_FOLDER ICON "resources\\folder.ico" + +IDI_PENCIL ICON "resources\\pencil.ico" + +IDI_MAGNIFIER ICON "resources\\magnifier.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINWND MENU +BEGIN + POPUP "&Hacker" + BEGIN + MENUITEM "&Run...\aCtrl+R", ID_HACKER_RUN + MENUITEM "Run &as...\aCtrl+Shift+R", ID_HACKER_RUNAS + MENUITEM "Show &details for all processes", ID_HACKER_SHOWDETAILSFORALLPROCESSES + MENUITEM SEPARATOR + MENUITEM "&Save...\aCtrl+S", ID_HACKER_SAVE + MENUITEM "&Find handles or DLLs...\aCtrl+F", ID_HACKER_FINDHANDLESORDLLS + MENUITEM "&Options...", ID_HACKER_OPTIONS + MENUITEM "&Plugins...", ID_HACKER_PLUGINS + MENUITEM SEPARATOR + POPUP "&Computer" + BEGIN + MENUITEM "&Lock", ID_COMPUTER_LOCK + MENUITEM "Log o&ff", ID_COMPUTER_LOGOFF + MENUITEM SEPARATOR + MENUITEM "&Sleep", ID_COMPUTER_SLEEP + MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE + MENUITEM SEPARATOR + MENUITEM "R&estart", ID_COMPUTER_RESTART + MENUITEM "Restart to boot options", ID_COMPUTER_RESTARTBOOTOPTIONS + MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN + MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID + END + MENUITEM "E&xit", ID_HACKER_EXIT + END + POPUP "&View" + BEGIN + MENUITEM "System &information\aCtrl+I", ID_VIEW_SYSTEMINFORMATION + MENUITEM "&Tray icons", ID_VIEW_TRAYICONS + MENUITEM SEPARATOR + MENUITEM "
", ID_VIEW_SECTIONPLACEHOLDER + MENUITEM SEPARATOR + MENUITEM "&Always on top", ID_VIEW_ALWAYSONTOP + POPUP "&Opacity" + BEGIN + MENUITEM "&10%", ID_OPACITY_10 + MENUITEM "&20%", ID_OPACITY_20 + MENUITEM "&30%", ID_OPACITY_30 + MENUITEM "&40%", ID_OPACITY_40 + MENUITEM "&50%", ID_OPACITY_50 + MENUITEM "&60%", ID_OPACITY_60 + MENUITEM "&70%", ID_OPACITY_70 + MENUITEM "&80%", ID_OPACITY_80 + MENUITEM "&90%", ID_OPACITY_90 + MENUITEM "&Opaque", ID_OPACITY_OPAQUE + END + MENUITEM SEPARATOR + MENUITEM "&Refresh\aF5", ID_VIEW_REFRESH + POPUP "Refresh i&nterval" + BEGIN + MENUITEM "&Fast (0.5s)", ID_UPDATEINTERVAL_FAST + MENUITEM "&Normal (1s)", ID_UPDATEINTERVAL_NORMAL + MENUITEM "&Below normal (2s)", ID_UPDATEINTERVAL_BELOWNORMAL + MENUITEM "&Slow (5s)", ID_UPDATEINTERVAL_SLOW + MENUITEM "&Very slow (10s)", ID_UPDATEINTERVAL_VERYSLOW + END + MENUITEM "Refresh a&utomatically\aF6", ID_VIEW_UPDATEAUTOMATICALLY + END + POPUP "&Tools" + BEGIN + MENUITEM "&Create service...", ID_TOOLS_CREATESERVICE + MENUITEM "&Hidden processes", ID_TOOLS_HIDDENPROCESSES + MENUITEM "Inspect e&xecutable file...", ID_TOOLS_INSPECTEXECUTABLEFILE + MENUITEM "&Pagefiles", ID_TOOLS_PAGEFILES + MENUITEM "Start &Task Manager", ID_TOOLS_STARTTASKMANAGER + POPUP "&Permissions" + BEGIN + MENUITEM "Service Control Manager", ID_TOOLS_SCM_PERMISSIONS + END + END + POPUP "&Users" + BEGIN + MENUITEM "Dummy", ID_USERS_DUMMY + END + POPUP "H&elp" + BEGIN + MENUITEM "&Log\aCtrl+L", ID_HELP_LOG + MENUITEM "&Donate", ID_HELP_DONATE + MENUITEM "Debu&g console", ID_HELP_DEBUGCONSOLE + MENUITEM "&About", ID_HELP_ABOUT + END +END + +IDR_THREAD MENU +BEGIN + POPUP "Thread" + BEGIN + MENUITEM "&Inspect\aEnter", ID_THREAD_INSPECT + MENUITEM "T&erminate\aDel", ID_THREAD_TERMINATE + MENUITEM "&Suspend", ID_THREAD_SUSPEND + MENUITEM "Res&ume", ID_THREAD_RESUME + MENUITEM SEPARATOR + MENUITEM "Analy&ze", ID_ANALYZE_WAIT + MENUITEM "&Affinity", ID_THREAD_AFFINITY + MENUITEM "Critical", ID_THREAD_CRITICAL + MENUITEM "Per&missions", ID_THREAD_PERMISSIONS + MENUITEM "&Token", ID_THREAD_TOKEN + POPUP "&Priority" + BEGIN + MENUITEM "Time &critical", ID_PRIORITY_TIMECRITICAL + MENUITEM "&Highest", ID_PRIORITY_HIGHEST + MENUITEM "&Above normal", ID_PRIORITY_ABOVENORMAL + MENUITEM "&Normal", ID_PRIORITY_NORMAL + MENUITEM "&Below normal", ID_PRIORITY_BELOWNORMAL + MENUITEM "&Lowest", ID_PRIORITY_LOWEST + MENUITEM "&Idle", ID_PRIORITY_IDLE + END + POPUP "&I/O priority" + BEGIN + MENUITEM "&High", ID_IOPRIORITY_HIGH + MENUITEM "&Normal", ID_IOPRIORITY_NORMAL + MENUITEM "&Low", ID_IOPRIORITY_LOW + MENUITEM "&Very low", ID_IOPRIORITY_VERYLOW + END + POPUP "Pa&ge priority" + BEGIN + MENUITEM "&Normal", ID_PAGEPRIORITY_NORMAL + MENUITEM "&Below normal", ID_PAGEPRIORITY_BELOWNORMAL + MENUITEM "&Medium", ID_PAGEPRIORITY_MEDIUM + MENUITEM "&Low", ID_PAGEPRIORITY_LOW + MENUITEM "&Very low", ID_PAGEPRIORITY_VERYLOW + END + MENUITEM SEPARATOR + MENUITEM "&Copy\aCtrl+C", ID_THREAD_COPY + END +END + +IDR_HANDLE MENU +BEGIN + POPUP "Handle" + BEGIN + MENUITEM "C&lose\aDel", ID_HANDLE_CLOSE + MENUITEM "&Protected", ID_HANDLE_PROTECTED + MENUITEM "&Inherit", ID_HANDLE_INHERIT + MENUITEM SEPARATOR + MENUITEM "Prope&rties\aEnter", ID_HANDLE_PROPERTIES + MENUITEM SEPARATOR + MENUITEM "&Copy\aCtrl+C", ID_HANDLE_COPY + END +END + +IDR_MODULE MENU +BEGIN + POPUP "Module" + BEGIN + MENUITEM "&Unload\aDel", ID_MODULE_UNLOAD + MENUITEM SEPARATOR + MENUITEM "&Inspect\aEnter", ID_MODULE_INSPECT + MENUITEM "&Search online\aCtrl+M", ID_MODULE_SEARCHONLINE + MENUITEM "Open &file location\aCtrl+Enter", ID_MODULE_OPENFILELOCATION + MENUITEM "P&roperties", ID_MODULE_PROPERTIES + MENUITEM "&Copy\aCtrl+C", ID_MODULE_COPY + END +END + +IDR_COMPUTER MENU +BEGIN + POPUP "Computer" + BEGIN + MENUITEM "&Lock", ID_COMPUTER_LOCK + MENUITEM "Log o&ff", ID_COMPUTER_LOGOFF + MENUITEM SEPARATOR + MENUITEM "&Sleep", ID_COMPUTER_SLEEP + MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE + MENUITEM SEPARATOR + MENUITEM "R&estart", ID_COMPUTER_RESTART + MENUITEM "Restart to boot &options", ID_COMPUTER_RESTARTBOOTOPTIONS + MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN + MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID + END +END + +IDR_PROCESS MENU +BEGIN + POPUP "Process" + BEGIN + MENUITEM "T&erminate\aDel", ID_PROCESS_TERMINATE + MENUITEM "Terminate tree\aShift+Del", ID_PROCESS_TERMINATETREE + MENUITEM "&Suspend", ID_PROCESS_SUSPEND + MENUITEM "Res&ume", ID_PROCESS_RESUME + MENUITEM "Res&tart", ID_PROCESS_RESTART + MENUITEM SEPARATOR + MENUITEM "Create dump fi&le...", ID_PROCESS_CREATEDUMPFILE + MENUITEM "De&bug", ID_PROCESS_DEBUG + MENUITEM "Virtuali&zation", ID_PROCESS_VIRTUALIZATION + MENUITEM SEPARATOR + MENUITEM "&Affinity", ID_PROCESS_AFFINITY + POPUP "&Priority" + BEGIN + MENUITEM "&Real time", ID_PRIORITY_REALTIME + MENUITEM "&High", ID_PRIORITY_HIGH + MENUITEM "&Above normal", ID_PRIORITY_ABOVENORMAL + MENUITEM "&Normal", ID_PRIORITY_NORMAL + MENUITEM "&Below normal", ID_PRIORITY_BELOWNORMAL + MENUITEM "&Idle", ID_PRIORITY_IDLE + END + POPUP "&I/O priority" + BEGIN + MENUITEM "&High", ID_IOPRIORITY_HIGH + MENUITEM "&Normal", ID_IOPRIORITY_NORMAL + MENUITEM "&Low", ID_IOPRIORITY_LOW + MENUITEM "&Very low", ID_IOPRIORITY_VERYLOW + END + POPUP "&Miscellaneous" + BEGIN + MENUITEM "&Critical", ID_MISCELLANEOUS_SETCRITICAL + MENUITEM "&Detach from debugger", ID_MISCELLANEOUS_DETACHFROMDEBUGGER + MENUITEM "GDI &handles", ID_MISCELLANEOUS_GDIHANDLES + POPUP "Pa&ge priority" + BEGIN + MENUITEM "&Normal", ID_PAGEPRIORITY_NORMAL + MENUITEM "&Below normal", ID_PAGEPRIORITY_BELOWNORMAL + MENUITEM "&Medium", ID_PAGEPRIORITY_MEDIUM + MENUITEM "&Low", ID_PAGEPRIORITY_LOW + MENUITEM "&Very low", ID_PAGEPRIORITY_VERYLOW + END + MENUITEM "Reduce working &set", ID_MISCELLANEOUS_REDUCEWORKINGSET + MENUITEM "&Run as...", ID_MISCELLANEOUS_RUNAS + MENUITEM "Run &as this user...", ID_MISCELLANEOUS_RUNASTHISUSER + END + POPUP "&Window" + BEGIN + MENUITEM "Bring to &front", ID_WINDOW_BRINGTOFRONT + MENUITEM "&Restore", ID_WINDOW_RESTORE + MENUITEM "M&inimize", ID_WINDOW_MINIMIZE + MENUITEM "M&aximize", ID_WINDOW_MAXIMIZE + MENUITEM SEPARATOR + MENUITEM "&Close", ID_WINDOW_CLOSE + END + MENUITEM SEPARATOR + MENUITEM "Search &online\aCtrl+M", ID_PROCESS_SEARCHONLINE + MENUITEM "Open &file location\aCtrl+Enter", ID_PROCESS_OPENFILELOCATION + MENUITEM "P&roperties\aEnter", ID_PROCESS_PROPERTIES + MENUITEM "&Copy\aCtrl+C", ID_PROCESS_COPY + END +END + +IDR_SERVICE MENU +BEGIN + POPUP "Service" + BEGIN + MENUITEM "&Go to process", ID_SERVICE_GOTOPROCESS + MENUITEM "&Start", ID_SERVICE_START + MENUITEM "C&ontinue", ID_SERVICE_CONTINUE + MENUITEM "&Pause", ID_SERVICE_PAUSE + MENUITEM "S&top", ID_SERVICE_STOP + MENUITEM "&Delete\aDel", ID_SERVICE_DELETE + MENUITEM SEPARATOR + MENUITEM "Open &key", ID_SERVICE_OPENKEY + MENUITEM "Open &file location\aCtrl+Enter", ID_SERVICE_OPENFILELOCATION + MENUITEM "P&roperties\aEnter", ID_SERVICE_PROPERTIES + MENUITEM "&Copy\aCtrl+C", ID_SERVICE_COPY + END +END + +IDR_FINDOBJ MENU +BEGIN + POPUP "Object" + BEGIN + MENUITEM "C&lose\aDel", ID_OBJECT_CLOSE + MENUITEM SEPARATOR + MENUITEM "Go to owning &process", ID_OBJECT_GOTOOWNINGPROCESS + MENUITEM "Prope&rties", ID_OBJECT_PROPERTIES + MENUITEM SEPARATOR + MENUITEM "&Copy\aCtrl+C", ID_OBJECT_COPY + END +END + +IDR_USER MENU +BEGIN + POPUP "User" + BEGIN + MENUITEM "&Connect", ID_USER_CONNECT + MENUITEM "&Disconnect", ID_USER_DISCONNECT + MENUITEM "&Logoff", ID_USER_LOGOFF + MENUITEM "Rem&ote control", ID_USER_REMOTECONTROL + MENUITEM "Send &message...", ID_USER_SENDMESSAGE + MENUITEM "P&roperties", ID_USER_PROPERTIES + END +END + +IDR_NETWORK MENU +BEGIN + POPUP "Network" + BEGIN + MENUITEM "&Go to process\aEnter", ID_NETWORK_GOTOPROCESS + MENUITEM "Go to service", ID_NETWORK_GOTOSERVICE + MENUITEM "C&lose", ID_NETWORK_CLOSE + MENUITEM SEPARATOR + MENUITEM "&Copy\aCtrl+C", ID_NETWORK_COPY + END +END + +IDR_ICON MENU +BEGIN + POPUP "Icon" + BEGIN + MENUITEM "&Show/Hide Process Hacker", ID_ICON_SHOWHIDEPROCESSHACKER + MENUITEM "System &information", ID_ICON_SYSTEMINFORMATION + POPUP "N&otifications" + BEGIN + MENUITEM "&Enable all", ID_NOTIFICATIONS_ENABLEALL + MENUITEM "&Disable all", ID_NOTIFICATIONS_DISABLEALL + MENUITEM SEPARATOR + MENUITEM "New &processes", ID_NOTIFICATIONS_NEWPROCESSES + MENUITEM "T&erminated processes", ID_NOTIFICATIONS_TERMINATEDPROCESSES + MENUITEM "New &services", ID_NOTIFICATIONS_NEWSERVICES + MENUITEM "St&arted services", ID_NOTIFICATIONS_STARTEDSERVICES + MENUITEM "St&opped services", ID_NOTIFICATIONS_STOPPEDSERVICES + MENUITEM "&Deleted services", ID_NOTIFICATIONS_DELETEDSERVICES + END + POPUP "&Processes" + BEGIN + MENUITEM "Dummy", ID_PROCESSES_DUMMY + END + MENUITEM SEPARATOR + POPUP "&Computer" + BEGIN + MENUITEM "&Lock", ID_COMPUTER_LOCK + MENUITEM "Log o&ff", ID_COMPUTER_LOGOFF + MENUITEM SEPARATOR + MENUITEM "&Sleep", ID_COMPUTER_SLEEP + MENUITEM "&Hibernate", ID_COMPUTER_HIBERNATE + MENUITEM SEPARATOR + MENUITEM "R&estart", ID_COMPUTER_RESTART + MENUITEM "Restart to boot &options", ID_COMPUTER_RESTARTBOOTOPTIONS + MENUITEM "Shu&t down", ID_COMPUTER_SHUTDOWN + MENUITEM "H&ybrid shut down", ID_COMPUTER_SHUTDOWNHYBRID + END + MENUITEM "E&xit", ID_ICON_EXIT + END +END + +IDR_MEMORY MENU +BEGIN + POPUP "Memory" + BEGIN + MENUITEM "&Read/Write memory", ID_MEMORY_READWRITEMEMORY + MENUITEM "&Save...", ID_MEMORY_SAVE + MENUITEM "Change &protection...", ID_MEMORY_CHANGEPROTECTION + MENUITEM "&Free", ID_MEMORY_FREE + MENUITEM "&Decommit", ID_MEMORY_DECOMMIT + MENUITEM SEPARATOR + MENUITEM "&Copy\aCtrl+C", ID_MEMORY_COPY + END +END + +IDR_MINIINFO MENU +BEGIN + POPUP "Mini Info" + BEGIN + POPUP "&Opacity" + BEGIN + MENUITEM "10%", ID_OPACITY_10 + MENUITEM "20%", ID_OPACITY_20 + MENUITEM "30%", ID_OPACITY_30 + MENUITEM "40%", ID_OPACITY_40 + MENUITEM "50%", ID_OPACITY_50 + MENUITEM "60%", ID_OPACITY_60 + MENUITEM "70%", ID_OPACITY_70 + MENUITEM "80%", ID_OPACITY_80 + MENUITEM "90%", ID_OPACITY_90 + MENUITEM "Opaque", ID_OPACITY_OPAQUE + END + MENUITEM SEPARATOR + MENUITEM "&Refresh\aF5", ID_MINIINFO_REFRESH + MENUITEM "Refresh a&utomatically\aF6", ID_MINIINFO_REFRESHAUTOMATICALLY + END +END + +IDR_MINIINFO_PROCESS MENU +BEGIN + POPUP "Process" + BEGIN + MENUITEM SEPARATOR + MENUITEM "&Go to process", ID_PROCESS_GOTOPROCESS + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROCGENERAL DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Permissions",IDC_PERMISSIONS,143,197,50,14 + PUSHBUTTON "Terminate",IDC_TERMINATE,197,197,50,14 + GROUPBOX "File",IDC_FILE,7,7,246,79 + ICON "",IDC_FILEICON,14,18,20,20 + EDITTEXT IDC_NAME,44,17,182,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + EDITTEXT IDC_COMPANYNAME,44,29,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + CONTROL "Company name link",IDC_COMPANYNAME_LINK,"SysLink",LWS_NOPREFIX | NOT WS_VISIBLE | WS_TABSTOP,46,29,183,9 + LTEXT "Version:",IDC_STATIC,15,41,27,8 + EDITTEXT IDC_VERSION,44,41,113,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Image file name:",IDC_STATIC,15,55,56,8 + EDITTEXT IDC_FILENAME,15,65,191,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Inspect",IDC_INSPECT,210,64,17,15,BS_ICON + PUSHBUTTON "Open",IDC_OPENFILENAME,230,64,17,15,BS_ICON + GROUPBOX "Process",IDC_PROCESS,7,92,246,161 + LTEXT "Command line:",IDC_STATIC,13,103,50,8 + EDITTEXT IDC_CMDLINE,79,101,147,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Current directory:",IDC_STATIC,13,119,60,8 + EDITTEXT IDC_CURDIR,79,117,168,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Started:",IDC_STATIC,13,135,28,8 + EDITTEXT IDC_STARTED,79,133,168,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "PEB address:",IDC_STATIC,13,151,44,8 + EDITTEXT IDC_PEBADDRESS,79,149,101,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Image type:",IDC_PROCESSTYPELABEL,185,151,41,8,NOT WS_VISIBLE + LTEXT "32-bit",IDC_PROCESSTYPETEXT,228,151,20,8,NOT WS_VISIBLE + LTEXT "Parent:",IDC_STATIC,13,167,25,8 + EDITTEXT IDC_PARENTPROCESS,79,165,147,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "View",IDC_VIEWPARENTPROCESS,230,163,17,15,BS_ICON + LTEXT "Mitigation policies:",IDC_STATIC,13,183,59,8 + EDITTEXT IDC_MITIGATION,79,181,126,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Details",IDC_VIEWMITIGATION,209,180,38,14 + LTEXT "Protection:",IDC_STATIC,13,200,36,8 + EDITTEXT IDC_PROTECTION,51,200,80,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + PUSHBUTTON "View",IDC_VIEWCOMMANDLINE,230,100,17,15,BS_ICON +END + +IDD_PROCMODULES DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Modules" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE + EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL + PUSHBUTTON "Options",IDC_FILTEROPTIONS,2,2,50,14 +END + +IDD_PROCTHREADS DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Threads" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE + EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL + PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14 +END + +IDD_PROCHANDLES DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Handles" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14 + EDITTEXT IDC_HANDLESEARCH,114,3,144,14,ES_AUTOHSCROLL + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,256,239,WS_EX_CLIENTEDGE +END + +IDD_PROCENVIRONMENT DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Environment" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE + EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL + PUSHBUTTON "Refresh",IDC_REFRESH,53,2,50,14 + PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14 +END + +IDD_THRDSTACK DIALOGEX 0, 0, 273, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Thread Stack" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Copy",IDC_COPY,111,212,50,14 + PUSHBUTTON "Refresh",IDC_REFRESH,165,212,50,14 + PUSHBUTTON "Close",IDOK,219,212,50,14 + CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,2,269,209,WS_EX_CLIENTEDGE +END + +IDD_ABOUT DIALOGEX 0, 0, 270, 195 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + ICON IDI_PROCESSHACKER,IDC_STATIC,16,11,21,20 + LTEXT "Process Hacker",IDC_ABOUT_NAME,45,10,192,8 + LTEXT "Licensed under the GNU GPL, v3.",IDC_STATIC,45,23,193,8 + LTEXT "Copyright (c) 2008-2019",IDC_STATIC,45,36,80,8 + CONTROL "Credits.",IDC_CREDITS,"SysLink",WS_TABSTOP,15,49,248,121 + CONTROL "Process Hacker on SourceForge.net",IDC_LINK_SF, + "SysLink",WS_TABSTOP,7,177,130,11 + PUSHBUTTON "Diagnostics",IDC_DIAGNOSTICS,160,174,50,14 + DEFPUSHBUTTON "OK",IDOK,213,174,50,14 +END + +IDD_SRVLIST DIALOGEX 0, 0, 237, 201 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,0,237,141 + EDITTEXT IDC_DESCRIPTION,0,144,237,40,ES_MULTILINE | ES_READONLY | NOT WS_BORDER + PUSHBUTTON "&Start",IDC_START,134,187,50,14 + PUSHBUTTON "&Pause",IDC_PAUSE,187,187,50,14 +END + +IDD_SRVGENERAL DIALOGEX 0, 0, 282, 183 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_DESCRIPTION,7,7,268,45,ES_MULTILINE | ES_READONLY | WS_VSCROLL + LTEXT "Type:",IDC_STATIC,7,57,20,8 + COMBOBOX IDC_TYPE,30,56,110,65,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Start type:",IDC_STATIC,147,57,38,8 + COMBOBOX IDC_STARTTYPE,188,56,87,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Error control:",IDC_STATIC,7,75,47,8 + COMBOBOX IDC_ERRORCONTROL,58,73,82,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Group:",IDC_STATIC,147,75,23,8 + EDITTEXT IDC_GROUP,173,74,102,12,ES_AUTOHSCROLL + LTEXT "Binary path:",IDC_STATIC,7,92,40,8 + EDITTEXT IDC_BINARYPATH,58,91,163,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,225,90,50,14 + LTEXT "User account:",IDC_STATIC,7,110,46,8 + EDITTEXT IDC_USERACCOUNT,58,108,217,12,ES_AUTOHSCROLL + LTEXT "Password:",IDC_STATIC,7,127,34,8 + EDITTEXT IDC_PASSWORD,58,125,204,12,ES_PASSWORD | ES_AUTOHSCROLL + CONTROL "",IDC_PASSWORDCHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,266,126,9,10 + LTEXT "Service DLL:",IDC_STATIC,7,144,48,8 + EDITTEXT IDC_SERVICEDLL,58,142,217,12,ES_AUTOHSCROLL | ES_READONLY + CONTROL "Delayed start",IDC_DELAYEDSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,158,59,10 + PUSHBUTTON "Permissions",IDC_PERMISSIONS,225,162,50,14 +END + +IDD_HNDLGENERAL DIALOGEX 0, 0, 260, 181 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_TABSTOP,0,2,260,179 +END + +IDD_INFORMATION DIALOGEX 0, 0, 317, 184 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Information" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_TEXT,7,7,303,152,ES_MULTILINE | ES_READONLY | WS_VSCROLL + PUSHBUTTON "Save...",IDC_SAVE,154,163,50,14 + PUSHBUTTON "Copy",IDC_COPY,207,163,50,14 + DEFPUSHBUTTON "Close",IDOK,260,163,50,14 +END + +IDD_FINDOBJECTS DIALOGEX 0, 0, 357, 233 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Find Handles or DLLs" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_FILTER,97,4,171,13,ES_AUTOHSCROLL + PUSHBUTTON "Find",IDOK,306,3,50,14 + CONTROL "&Regex",IDC_REGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,271,6,34,10 + COMBOBOX IDC_FILTERTYPE,2,4,93,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_TREELIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x2,2,19,353,212,WS_EX_CLIENTEDGE +END + +IDD_OBJTOKEN DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Token" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "User:",IDC_TOKENUSER,7,7,18,8 + EDITTEXT IDC_USER,48,7,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "User SID:",IDC_TOKENSID,7,18,32,8 + EDITTEXT IDC_USERSID,48,18,205,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Session:",IDC_STATIC,7,29,28,8 + LTEXT "Unknown",IDC_SESSIONID,38,29,30,8 + LTEXT "Elevated:",IDC_STATIC,85,29,32,8 + LTEXT "Unknown",IDC_ELEVATED,120,29,30,8 + LTEXT "Virtualized:",IDC_STATIC,169,29,36,8 + LTEXT "Unknown",IDC_VIRTUALIZED,209,29,44,8 + CONTROL "",IDC_GROUPS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,42,246,194 + PUSHBUTTON "Integrity",IDC_INTEGRITY,149,239,50,14 + PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14 + PUSHBUTTON "Permissions",IDC_PERMISSIONS,95,239,50,14 + PUSHBUTTON "Default token",IDC_DEFAULTPERM,41,239,50,14 +END + +IDD_HIDDENPROCESSES DIALOGEX 0, 0, 337, 221 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Hidden Processes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Processes highlighted red are hidden while those highlighted grey have terminated.",IDC_INTRO,7,7,323,12 + CONTROL "",IDC_PROCESSES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,23,323,162 + RTEXT "",IDC_DESCRIPTION,7,187,323,11 + COMBOBOX IDC_METHOD,7,201,73,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Terminate",IDC_TERMINATE,115,200,50,14 + PUSHBUTTON "Save...",IDC_SAVE,170,200,50,14 + PUSHBUTTON "&Scan",IDC_SCAN,225,200,50,14 + DEFPUSHBUTTON "Close",IDOK,280,200,50,14 +END + +IDD_RUNAS DIALOGEX 0, 0, 293, 127 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Run As" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Enter the command to start as the specified user.",IDC_TITLE,7,7,160,8 + LTEXT "Program:",IDC_STATIC,7,23,30,8 + LTEXT "User name:",IDC_STATIC,7,40,38,8 + COMBOBOX IDC_USERNAME,53,38,123,12,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Type:",IDC_STATIC,184,40,20,8 + COMBOBOX IDC_TYPE,208,38,78,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Password:",IDC_STATIC,7,56,34,8 + EDITTEXT IDC_PASSWORD,53,55,123,12,ES_PASSWORD | ES_AUTOHSCROLL + CONTROL "Toggle elevation",IDC_TOGGLEELEVATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,56,69,10 + LTEXT "Session ID:",IDC_STATIC,7,73,37,8 + LTEXT "Desktop:",IDC_STATIC,7,90,30,8 + DEFPUSHBUTTON "OK",IDOK,127,106,50,14 + PUSHBUTTON "Cancel",IDCANCEL,182,106,50,14 + COMBOBOX IDC_SESSIONCOMBO,53,72,123,12,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DESKTOPCOMBO,53,88,123,12,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Browse",IDC_BROWSE,237,106,50,14 + CONTROL "Create suspended",IDC_TOGGLESUSPENDED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,70,75,10 + COMBOBOX IDC_PROGRAMCOMBO,53,21,233,12,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP +END + +IDD_PROGRESS DIALOGEX 0, 0, 216, 62 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Progress" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,159,41,50,14 + CONTROL "",IDC_PROGRESS,"msctls_progress32",0x0,7,23,202,14 + LTEXT "Please wait...",IDC_PROGRESSTEXT,7,7,202,12 +END + +IDD_PAGEFILES DIALOGEX 0, 0, 322, 162 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Pagefiles" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,308,128 + PUSHBUTTON "Refresh",IDC_REFRESH,211,141,50,14 + DEFPUSHBUTTON "Close",IDOK,265,141,50,14 +END + +IDD_TOKGENERAL DIALOGEX 0, 0, 270, 228 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Token",IDC_STATIC,7,7,256,149 + LTEXT "User:",IDC_STATIC,15,19,18,8 + EDITTEXT IDC_USER,71,17,185,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "User SID:",IDC_STATIC,15,36,32,8 + EDITTEXT IDC_USERSID,71,34,185,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Owner:",IDC_STATIC,15,53,25,8 + EDITTEXT IDC_OWNER,71,51,185,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Primary group:",IDC_STATIC,15,70,49,8 + EDITTEXT IDC_PRIMARYGROUP,71,68,185,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Session ID:",IDC_STATIC,15,87,37,8 + EDITTEXT IDC_SESSIONID,71,85,88,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Elevated:",IDC_STATIC,15,104,32,8 + EDITTEXT IDC_ELEVATED,71,102,117,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Virtualization:",IDC_STATIC,15,121,44,8 + EDITTEXT IDC_VIRTUALIZATION,71,119,185,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "UIAccess:",IDC_STATIC,15,138,32,8 + EDITTEXT IDC_UIACCESS,71,136,185,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Linked token",IDC_LINKEDTOKEN,193,101,63,14 + GROUPBOX "Source",IDC_STATIC,7,160,256,47 + LTEXT "Name:",IDC_STATIC,15,172,22,8 + EDITTEXT IDC_SOURCENAME,71,170,185,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "LUID:",IDC_STATIC,15,190,19,8 + EDITTEXT IDC_SOURCELUID,71,188,185,12,ES_AUTOHSCROLL | ES_READONLY +END + +IDD_TOKADVANCED DIALOGEX 0, 0, 236, 186 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,0,0,236,186 +END + +IDD_OBJJOB DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Job" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Name:",IDC_STATIC,7,9,22,8 + EDITTEXT IDC_NAME,35,8,164,12,ES_AUTOHSCROLL + PUSHBUTTON "Terminate",IDC_TERMINATE,203,7,50,14 + LTEXT "Processes in job:",IDC_STATIC,7,25,55,8 + CONTROL "",IDC_PROCESSES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,38,246,62 + PUSHBUTTON "Add...",IDC_ADD,203,103,50,14 + LTEXT "Limits:",IDC_STATIC,7,117,21,8 + CONTROL "",IDC_LIMITS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,129,246,107 + PUSHBUTTON "Advanced",IDC_ADVANCED,203,239,50,14 +END + +IDD_OBJEVENT DIALOGEX 0, 0, 186, 76 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Event" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Type:",IDC_STATIC,7,7,20,8 + LTEXT "Unknown",IDC_TYPE,42,7,137,8 + LTEXT "Signaled:",IDC_STATIC,7,18,30,8 + LTEXT "Unknown",IDC_SIGNALED,42,18,137,8 + PUSHBUTTON "Set",IDC_SET,7,34,50,14 + PUSHBUTTON "Reset",IDC_RESET,61,34,50,14 + PUSHBUTTON "Pulse",IDC_PULSE,115,34,50,14 +END + +IDD_OBJMUTANT DIALOGEX 0, 0, 186, 76 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mutant" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Count:",IDC_STATIC,7,7,23,8 + LTEXT "Unknown",IDC_COUNT,55,7,124,8 + LTEXT "Abandoned:",IDC_STATIC,7,18,40,8 + LTEXT "Unknown",IDC_ABANDONED,55,18,124,8 + LTEXT "Owner:",IDC_OWNERLABEL,7,29,25,8 + LTEXT "Unknown",IDC_OWNER,55,29,124,8 +END + +IDD_OBJSEMAPHORE DIALOGEX 0, 0, 186, 76 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Semaphore" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Current count:",IDC_STATIC,7,7,50,8 + LTEXT "Unknown",IDC_CURRENTCOUNT,66,7,113,8 + LTEXT "Maximum count:",IDC_STATIC,7,18,54,8 + LTEXT "Unknown",IDC_MAXIMUMCOUNT,66,18,113,8 + PUSHBUTTON "Acquire",IDC_ACQUIRE,7,34,50,14 + PUSHBUTTON "Release",IDC_RELEASE,61,34,50,14 +END + +IDD_OBJTIMER DIALOGEX 0, 0, 186, 76 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Timer" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Signaled:",-1,7,7,30,8 + LTEXT "Unknown",IDC_SIGNALED,42,7,137,8 + PUSHBUTTON "Cancel",IDC_CANCEL,7,19,50,14 +END + +IDD_JOBSTATISTICS DIALOGEX 0, 0, 259, 186 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Statistics" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "General",IDC_STATIC,7,7,133,47 + LTEXT "Active processes",IDC_STATIC,15,18,55,8 + RTEXT "Static",IDC_ZACTIVEPROCESSES_V,99,18,37,8,SS_ENDELLIPSIS + LTEXT "Total processes",IDC_STATIC,15,28,51,8 + RTEXT "Static",IDC_ZTOTALPROCESSES_V,95,29,41,8,SS_ENDELLIPSIS + LTEXT "Terminated due to job limits",IDC_STATIC,15,39,98,8 + RTEXT "Static",IDC_ZTERMINATEDPROCESSES_V,107,39,29,8,SS_ENDELLIPSIS + GROUPBOX "Time",IDC_STATIC,7,57,133,57 + LTEXT "User time",IDC_STATIC,15,68,32,8 + LTEXT "Kernel time",IDC_STATIC,15,79,38,8 + LTEXT "User time (period)",IDC_STATIC,15,89,60,8 + LTEXT "Kernel time (period)",IDC_STATIC,15,99,65,8 + RTEXT "Static",IDC_ZUSERTIME_V,83,68,53,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZKERNELTIME_V,85,79,51,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZUSERTIMEPERIOD_V,87,89,49,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZKERNELTIMEPERIOD_V,87,99,49,8,SS_ENDELLIPSIS + GROUPBOX "Memory",IDC_STATIC,7,118,133,48 + LTEXT "Page faults",IDC_STATIC,15,129,38,8 + LTEXT "Peak process usage",IDC_STATIC,15,140,65,8 + LTEXT "Peak job usage",IDC_STATIC,15,151,52,8 + RTEXT "Static",IDC_ZPAGEFAULTS_V,86,129,50,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPEAKPROCESSUSAGE_V,85,140,51,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPEAKJOBUSAGE_V,86,151,50,8,SS_ENDELLIPSIS + GROUPBOX "I/O",IDC_STATIC,146,7,106,75 + LTEXT "Reads",IDC_STATIC,152,17,21,8 + LTEXT "Read bytes",IDC_STATIC,152,27,38,8 + LTEXT "Writes",IDC_STATIC,152,37,22,8 + LTEXT "Write bytes",IDC_STATIC,152,47,38,8 + LTEXT "Other",IDC_STATIC,152,57,20,8 + LTEXT "Other bytes",IDC_STATIC,152,67,40,8 + RTEXT "Static",IDC_ZIOREADS_V,200,17,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZIOREADBYTES_V,200,27,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZIOWRITES_V,200,37,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZIOWRITEBYTES_V,200,47,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZIOOTHER_V,200,57,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZIOOTHERBYTES_V,200,67,47,8,SS_ENDELLIPSIS +END + +IDD_OBJEVENTPAIR DIALOGEX 0, 0, 186, 76 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Event Pair" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Set low",IDC_SETLOW,7,7,50,14 + PUSHBUTTON "Set high",IDC_SETHIGH,61,7,50,14 +END + +IDD_AFFINITY DIALOGEX 0, 0, 279, 227 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Affinity" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,169,206,50,14 + PUSHBUTTON "Cancel",IDCANCEL,222,206,50,14 + LTEXT "Affinity controls which CPUs threads are allowed to execute on.",IDC_STATIC,7,7,265,11 + CONTROL "CPU 0",IDC_CPU0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,21,35,10 + CONTROL "CPU 1",IDC_CPU1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,33,35,10 + CONTROL "CPU 2",IDC_CPU2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,44,35,10 + CONTROL "CPU 3",IDC_CPU3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,56,35,10 + CONTROL "CPU 4",IDC_CPU4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,35,10 + CONTROL "CPU 5",IDC_CPU5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,78,35,10 + CONTROL "CPU 6",IDC_CPU6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,89,35,10 + CONTROL "CPU 7",IDC_CPU7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,35,10 + CONTROL "CPU 8",IDC_CPU8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,111,35,10 + CONTROL "CPU 9",IDC_CPU9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,122,35,10 + CONTROL "CPU 10",IDC_CPU10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,134,39,10 + CONTROL "CPU 11",IDC_CPU11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,145,39,10 + CONTROL "CPU 12",IDC_CPU12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,156,39,10 + CONTROL "CPU 13",IDC_CPU13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,167,39,10 + CONTROL "CPU 14",IDC_CPU14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,178,39,10 + CONTROL "CPU 15",IDC_CPU15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,190,39,10 + CONTROL "CPU 16",IDC_CPU16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,21,39,10 + CONTROL "CPU 17",IDC_CPU17,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,33,39,10 + CONTROL "CPU 18",IDC_CPU18,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,44,39,10 + CONTROL "CPU 19",IDC_CPU19,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,56,39,10 + CONTROL "CPU 20",IDC_CPU20,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,67,39,10 + CONTROL "CPU 21",IDC_CPU21,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,78,39,10 + CONTROL "CPU 22",IDC_CPU22,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,89,39,10 + CONTROL "CPU 23",IDC_CPU23,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,100,39,10 + CONTROL "CPU 24",IDC_CPU24,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,111,39,10 + CONTROL "CPU 25",IDC_CPU25,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,122,39,10 + CONTROL "CPU 26",IDC_CPU26,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,134,39,10 + CONTROL "CPU 27",IDC_CPU27,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,145,39,10 + CONTROL "CPU 28",IDC_CPU28,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,156,39,10 + CONTROL "CPU 29",IDC_CPU29,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,167,39,10 + CONTROL "CPU 30",IDC_CPU30,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,178,39,10 + CONTROL "CPU 31",IDC_CPU31,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,190,39,10 + CONTROL "CPU 32",IDC_CPU32,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,21,39,10 + CONTROL "CPU 33",IDC_CPU33,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,33,39,10 + CONTROL "CPU 34",IDC_CPU34,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,44,39,10 + CONTROL "CPU 35",IDC_CPU35,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,56,39,10 + CONTROL "CPU 36",IDC_CPU36,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,67,39,10 + CONTROL "CPU 37",IDC_CPU37,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,78,39,10 + CONTROL "CPU 38",IDC_CPU38,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,89,39,10 + CONTROL "CPU 39",IDC_CPU39,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,100,39,10 + CONTROL "CPU 40",IDC_CPU40,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,111,39,10 + CONTROL "CPU 41",IDC_CPU41,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,122,39,10 + CONTROL "CPU 42",IDC_CPU42,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,134,39,10 + CONTROL "CPU 43",IDC_CPU43,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,145,39,10 + CONTROL "CPU 44",IDC_CPU44,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,156,39,10 + CONTROL "CPU 45",IDC_CPU45,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,167,39,10 + CONTROL "CPU 46",IDC_CPU46,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,178,39,10 + CONTROL "CPU 47",IDC_CPU47,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,190,39,10 + CONTROL "CPU 48",IDC_CPU48,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,21,39,10 + CONTROL "CPU 49",IDC_CPU49,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,33,39,10 + CONTROL "CPU 50",IDC_CPU50,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,44,39,10 + CONTROL "CPU 51",IDC_CPU51,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,56,39,10 + CONTROL "CPU 52",IDC_CPU52,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,67,39,10 + CONTROL "CPU 53",IDC_CPU53,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,78,39,10 + CONTROL "CPU 54",IDC_CPU54,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,89,39,10 + CONTROL "CPU 55",IDC_CPU55,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,100,39,10 + CONTROL "CPU 56",IDC_CPU56,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,111,39,10 + CONTROL "CPU 57",IDC_CPU57,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,122,39,10 + CONTROL "CPU 58",IDC_CPU58,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,134,39,10 + CONTROL "CPU 59",IDC_CPU59,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,145,39,10 + CONTROL "CPU 60",IDC_CPU60,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,156,39,10 + CONTROL "CPU 61",IDC_CPU61,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,167,39,10 + CONTROL "CPU 62",IDC_CPU62,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,178,39,10 + CONTROL "CPU 63",IDC_CPU63,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,224,190,39,10 + PUSHBUTTON "Select all",IDC_SELECTALL,7,206,50,14 + PUSHBUTTON "Deselect all",IDC_DESELECTALL,60,206,50,14 +END + +IDD_SYSINFO DIALOGEX 0, 0, 423, 247 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "System Information" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +END + +IDD_EDITMESSAGE DIALOGEX 0, 0, 282, 163 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Message" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Title:",IDC_STATIC,7,8,17,8 + EDITTEXT IDC_TITLE,52,7,223,12,ES_AUTOHSCROLL + LTEXT "Text:",IDC_STATIC,7,24,18,8 + EDITTEXT IDC_TEXT,52,23,223,79,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL + LTEXT "Icon:",IDC_STATIC,7,107,18,8 + COMBOBOX IDC_TYPE,52,105,80,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Timeout (s):",IDC_STATIC,7,123,40,8 + EDITTEXT IDC_TIMEOUT,52,122,43,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,172,142,50,14 + PUSHBUTTON "Cancel",IDCANCEL,225,142,50,14 +END + +IDD_SESSION DIALOGEX 0, 0, 201, 149 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Session Properties" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Close",IDOK,144,128,50,14 + LTEXT "User name:",IDC_STATIC,7,7,38,8 + LTEXT "Session ID:",IDC_STATIC,7,18,37,8 + LTEXT "State:",IDC_STATIC,7,29,21,8 + LTEXT "Client name:",IDC_STATIC,7,84,41,8 + LTEXT "Client address:",IDC_STATIC,7,95,49,8 + LTEXT "Client display:",IDC_STATIC,7,106,46,8 + LTEXT "N/A",IDC_USERNAME,66,7,128,8 + LTEXT "N/A",IDC_SESSIONID,66,18,128,8 + LTEXT "N/A",IDC_STATE,66,29,128,8 + LTEXT "N/A",IDC_CLIENTNAME,66,84,128,8 + LTEXT "N/A",IDC_CLIENTADDRESS,66,95,128,8 + LTEXT "N/A",IDC_CLIENTDISPLAY,66,106,128,8 + LTEXT "Logon time:",IDC_STATIC,7,40,38,8 + LTEXT "N/A",IDC_LOGONTIME,66,40,128,8 + LTEXT "Connect time:",IDC_STATIC,7,51,46,8 + LTEXT "Disconnect time:",IDC_STATIC,7,62,54,8 + LTEXT "Last input time:",IDC_STATIC,7,73,50,8 + LTEXT "N/A",IDC_CONNECTTIME,66,51,128,8 + LTEXT "N/A",IDC_DISCONNECTTIME,66,62,128,8 + LTEXT "N/A",IDC_LASTINPUTTIME,66,74,128,8 +END + +IDD_PROCMEMORY DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Memory" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Refresh",IDC_REFRESH,53,2,50,14 + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,19,256,239,WS_EX_CLIENTEDGE + PUSHBUTTON "Options",IDC_FILTEROPTIONS,2,2,50,14 + EDITTEXT IDC_SEARCH,114,3,144,14,ES_AUTOHSCROLL +END + +IDD_CHOOSE DIALOGEX 0, 0, 199, 73 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Text:",IDC_MESSAGE,7,7,185,8,SS_ENDELLIPSIS + COMBOBOX IDC_CHOICE,7,20,185,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP + CONTROL "Option",IDC_OPTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,39,185,10 + DEFPUSHBUTTON "OK",IDOK,89,52,50,14 + PUSHBUTTON "Cancel",IDCANCEL,142,52,50,14 + COMBOBOX IDC_CHOICEUSER,7,20,185,30,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_CHOICESIMPLE,7,20,185,13,ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_VISIBLE +END + +IDD_OPTGENERAL DIALOGEX 0, 0, 315, 220 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Search engine:",IDC_STATIC,7,8,49,8 + EDITTEXT IDC_SEARCHENGINE,61,7,247,12,ES_AUTOHSCROLL + LTEXT "PE viewer:",IDC_STATIC,7,23,35,8 + EDITTEXT IDC_PEVIEWER,61,22,247,12,ES_AUTOHSCROLL + LTEXT "Max. size unit:",IDC_STATIC,7,55,49,8 + COMBOBOX IDC_MAXSIZEUNIT,61,54,39,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Icon processes:",IDC_STATIC,7,72,52,8 + EDITTEXT IDC_ICONPROCESSES,61,70,40,12,ES_AUTOHSCROLL | ES_NUMBER + PUSHBUTTON "Font...",IDC_FONT,179,70,49,14 + PUSHBUTTON "Make default...",IDC_REPLACETASKMANAGER,179,86,72,14 + LTEXT "Graph history length:",IDC_STATIC,106,56,69,8 + EDITTEXT IDC_SAMPLECOUNT,180,55,48,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Automatic",IDC_SAMPLECOUNTAUTOMATIC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,233,56,48,10 + CONTROL "",IDC_SETTINGS,"SysListView32",LVS_LIST | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,103,301,110 + LTEXT "Application font:",IDC_STATIC,121,72,54,8 + RTEXT "Process Hacker is the default Task Manager:",IDC_DEFSTATE,7,88,166,8 + LTEXT "Symbol path:",IDC_STATIC,7,39,43,8 + EDITTEXT IDC_DBGHELPSEARCHPATH,61,38,247,12,ES_AUTOHSCROLL +END + +IDD_OPTHIGHLIGHTING DIALOGEX 0, 0, 250, 174 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Highlighting" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Highlighting duration:",IDC_STATIC,7,8,70,8 + EDITTEXT IDC_HIGHLIGHTINGDURATION,79,7,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "New objects:",IDC_STATIC,7,25,44,8 + CONTROL "New objects",IDC_NEWOBJECTS,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,57,23,33,13 + LTEXT "Removed objects:",IDC_STATIC,127,25,60,8 + CONTROL "Removed objects",IDC_REMOVEDOBJECTS,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,193,23,33,13 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,7,41,236,107 + LTEXT "Double-click an item to change it.",IDC_INFO,7,156,106,8 + PUSHBUTTON "Enable all",IDC_ENABLEALL,140,153,50,14 + PUSHBUTTON "Disable all",IDC_DISABLEALL,193,153,50,14 +END + +IDD_CHOOSECOLUMNS DIALOGEX 0, 0, 381, 207 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Choose Columns" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Select the columns that will appear in the list.",IDC_MESSAGE,7,7,367,10 + LTEXT "Inactive columns:",IDC_STATIC,7,21,57,8 + LISTBOX IDC_INACTIVE,7,31,129,150,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Show >",IDC_SHOW,139,86,50,14 + PUSHBUTTON "< Hide",IDC_HIDE,139,103,50,14 + LISTBOX IDC_ACTIVE,192,31,129,150,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Move up",IDC_MOVEUP,324,31,50,14 + PUSHBUTTON "Move down",IDC_MOVEDOWN,324,48,50,14 + DEFPUSHBUTTON "OK",IDOK,272,186,50,14 + PUSHBUTTON "Cancel",IDCANCEL,324,186,50,14 + LTEXT "Active columns:",IDC_STATIC,192,21,51,8 +END + +IDD_NETSTACK DIALOGEX 0, 0, 261, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Network Stack" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,247,196 + PUSHBUTTON "Close",IDOK,204,207,50,14 +END + +IDD_CREATESERVICE DIALOGEX 0, 0, 287, 133 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Create Service" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Name:",IDC_STATIC,7,8,22,8 + EDITTEXT IDC_NAME,61,7,219,12,ES_AUTOHSCROLL + LTEXT "Display name:",IDC_STATIC,7,24,46,8 + EDITTEXT IDC_DISPLAYNAME,61,23,219,12,ES_AUTOHSCROLL + LTEXT "Type:",IDC_STATIC,7,41,20,8 + COMBOBOX IDC_TYPE,61,39,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Start type:",IDC_STATIC,7,58,38,8 + COMBOBOX IDC_STARTTYPE,61,56,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Error control:",IDC_STATIC,7,75,45,8 + COMBOBOX IDC_ERRORCONTROL,61,73,97,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Binary path:",IDC_STATIC,7,91,40,8 + EDITTEXT IDC_BINARYPATH,61,90,165,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,230,89,50,14 + DEFPUSHBUTTON "OK",IDOK,176,112,50,14 + PUSHBUTTON "Cancel",IDCANCEL,230,112,50,14 +END + +IDD_PROCPERFORMANCE DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Performance" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "CPU",IDC_GROUPCPU,7,7,246,74,0,WS_EX_TRANSPARENT + GROUPBOX "Private bytes",IDC_GROUPPRIVATEBYTES,7,86,246,77,0,WS_EX_TRANSPARENT + GROUPBOX "I/O",IDC_GROUPIO,7,164,246,89,0,WS_EX_TRANSPARENT + CONTROL "",IDC_CPU,"PhGraph",WS_CLIPSIBLINGS,105,42,50,14 + CONTROL "",IDC_PRIVATEBYTES,"PhGraph",WS_CLIPSIBLINGS,105,122,50,14 + CONTROL "",IDC_IO,"PhGraph",WS_CLIPSIBLINGS,105,204,50,14 +END + +IDD_PROCSTATISTICS DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Statistics" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_STATISTICS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,2,3,255,255 +END + +IDD_OPTADVANCED DIALOGEX 0, 0, 317, 225 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_SETTINGS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,303,211 +END + +IDD_GDIHANDLES DIALOGEX 0, 0, 351, 307 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "GDI Handles" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,337,275 + PUSHBUTTON "Refresh",IDC_REFRESH,240,286,50,14 + DEFPUSHBUTTON "Close",IDOK,294,286,50,14 +END + +IDD_LOG DIALOGEX 0, 0, 313, 303 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Log" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,299,272 + PUSHBUTTON "Clear",IDC_CLEAR,7,282,50,14 + CONTROL "Auto-scroll",IDC_AUTOSCROLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61,284,50,10 + PUSHBUTTON "Save...",IDC_SAVE,148,282,50,14 + PUSHBUTTON "Copy",IDC_COPY,202,282,50,14 + DEFPUSHBUTTON "Close",IDOK,256,282,50,14 +END + +IDD_MEMEDIT DIALOGEX 0, 0, 441, 269 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Memory" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_MEMORY,"PhHexEdit",WS_CLIPSIBLINGS | WS_VSCROLL | WS_TABSTOP,7,7,427,239 + PUSHBUTTON "Re-read",IDC_REREAD,7,248,50,14 + PUSHBUTTON "Write",IDC_WRITE,60,248,50,14 + PUSHBUTTON "Go to...",IDC_GOTO,112,248,50,14 + PUSHBUTTON "Save...",IDC_SAVE,331,248,50,14 + PUSHBUTTON "Close",IDOK,384,248,50,14 + COMBOBOX IDC_BYTESPERROW,164,249,86,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +END + +IDD_MEMPROTECT DIALOGEX 0, 0, 270, 171 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Memory Protection" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_INTRO,7,7,256,122,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "New value:",IDC_STATIC,120,136,37,8 + EDITTEXT IDC_VALUE,161,135,102,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,161,150,50,14 + PUSHBUTTON "Cancel",IDCANCEL,213,150,50,14 +END + +IDD_MEMRESULTS DIALOGEX 0, 0, 313, 266 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Results" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Results.",IDC_INTRO,7,9,299,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,21,299,221 + PUSHBUTTON "Filter",IDC_FILTER,7,245,50,14 + PUSHBUTTON "Save...",IDC_SAVE,149,245,50,14 + PUSHBUTTON "Copy",IDC_COPY,203,245,50,14 + DEFPUSHBUTTON "Close",IDOK,256,245,50,14 +END + +IDD_MEMSTRING DIALOGEX 0, 0, 241, 86 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "String Search" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Minimum length:",IDC_STATIC,7,8,54,8 + EDITTEXT IDC_MINIMUMLENGTH,67,7,51,12,ES_AUTOHSCROLL + CONTROL "Detect Unicode",IDC_DETECTUNICODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,22,65,10 + LTEXT "Search in the following types of memory regions:",IDC_STATIC,7,36,157,8 + CONTROL "Private",IDC_PRIVATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,49,39,10 + CONTROL "Image",IDC_IMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,55,49,36,10 + CONTROL "Mapped",IDC_MAPPED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,101,49,41,10 + DEFPUSHBUTTON "OK",IDOK,131,65,50,14 + PUSHBUTTON "Cancel",IDCANCEL,184,65,50,14 + CONTROL "Extended Unicode",IDC_EXTENDEDUNICODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,80,22,74,10 +END + +IDD_OPTGRAPHS DIALOGEX 0, 0, 250, 156 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Graphs" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Show text",IDC_SHOWTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,49,10 + CONTROL "Use old colors (black background)",IDC_USEOLDCOLORS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,123,10 + CONTROL "Show commit charge instead of physical memory in summary view",IDC_SHOWCOMMITINSUMMARY, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,31,227,10 + LTEXT "CPU kernel:",IDC_STATIC,7,48,39,8 + CONTROL "CPU kernel",IDC_CPUKERNEL,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,46,33,13 + LTEXT "CPU user:",IDC_STATIC,7,65,34,8 + CONTROL "CPU user",IDC_CPUUSER,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,63,33,13 + LTEXT "I/O R+O:",IDC_STATIC,7,82,32,8 + CONTROL "I/O R+O",IDC_IORO,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,80,33,13 + LTEXT "I/O W:",IDC_STATIC,7,98,23,8 + CONTROL "I/O W",IDC_IOW,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,97,33,13 + LTEXT "Private bytes:",IDC_STATIC,7,115,46,8 + CONTROL "Private bytes",IDC_PRIVATE,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,113,33,13 + LTEXT "Physical memory:",IDC_STATIC,7,131,56,8 + CONTROL "Physical memory",IDC_PHYSICAL,"PhColorBox",WS_CLIPSIBLINGS | WS_TABSTOP,73,129,33,13 +END + +IDD_PLUGINMANAGER DIALOGEX 0, 0, 501, 272 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Plugins" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Changes may require a restart to take effect.",IDC_INSTRUCTION,187,262,148,8,NOT WS_VISIBLE + DEFPUSHBUTTON "Close",IDOK,449,256,50,14 + CONTROL "",IDC_PLUGINTREE,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,2,497,253,WS_EX_CLIENTEDGE + PUSHBUTTON "Disabled plugins (0)",IDC_DISABLED,2,256,72,14 +END + +IDD_HANDLESTATS DIALOGEX 0, 0, 219, 175 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Handle Statistics" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,205,143 + DEFPUSHBUTTON "Close",IDOK,162,154,50,14 +END + +IDD_PROCRECORD DIALOGEX 0, 0, 260, 202 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Process Record" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Name:",IDC_STATIC,7,9,22,8 + EDITTEXT IDC_PROCESSNAME,39,9,214,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Parent:",IDC_STATIC,7,21,25,8 + EDITTEXT IDC_PARENT,39,21,214,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + GROUPBOX "File",IDC_FILE,7,38,246,79 + ICON "",IDC_FILEICON,14,49,20,20 + EDITTEXT IDC_NAME,44,48,182,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + EDITTEXT IDC_COMPANYNAME,44,60,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Version:",IDC_STATIC,15,72,27,8 + EDITTEXT IDC_VERSION,44,72,183,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Image file name:",IDC_STATIC,15,86,56,8 + EDITTEXT IDC_FILENAME,15,96,211,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Open",IDC_OPENFILENAME,230,95,17,15,BS_ICON + LTEXT "Command line:",IDC_STATIC,7,123,50,8 + EDITTEXT IDC_CMDLINE,65,121,188,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Started:",IDC_STATIC,7,139,28,8 + EDITTEXT IDC_STARTED,65,137,188,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Terminated:",IDC_STATIC,7,155,40,8 + EDITTEXT IDC_TERMINATED,65,153,188,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Session ID:",IDC_STATIC,7,170,37,8 + LTEXT "Static",IDC_SESSIONID,50,170,19,8 + PUSHBUTTON "Properties",IDC_PROPERTIES,150,181,50,14 + DEFPUSHBUTTON "Close",IDOK,203,181,50,14 +END + +IDD_CHOOSEPROCESS DIALOGEX 0, 0, 317, 239 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Select a Process" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Select a process from the list below.",IDC_MESSAGE,7,7,303,8,SS_ENDELLIPSIS + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,21,303,193 + PUSHBUTTON "Refresh",IDC_REFRESH,7,218,50,14 + DEFPUSHBUTTON "OK",IDOK,205,218,50,14 + PUSHBUTTON "Cancel",IDCANCEL,260,218,50,14 +END + +IDD_PROCSERVICES DIALOGEX 0, 0, 260, 261 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Services" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Services",IDC_SERVICES_LAYOUT,7,7,246,247,NOT WS_VISIBLE | WS_BORDER +END + +IDD_SHADOWSESSION DIALOGEX 0, 0, 228, 84 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Remote Control" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "To end the remote control session, press this key and the modifiers selected below:",IDC_STATIC,7,7,214,19 + COMBOBOX IDC_VIRTUALKEY,7,27,74,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Shift",IDC_SHIFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,47,31,10 + CONTROL "Ctrl",IDC_CTRL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,49,47,27,10 + CONTROL "Alt",IDC_ALT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,89,47,25,10 + DEFPUSHBUTTON "OK",IDOK,117,63,50,14 + PUSHBUTTON "Cancel",IDCANCEL,171,63,50,14 +END + +IDD_TOKCAPABILITIES DIALOGEX 0, 0, 270, 228 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Capabilities" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x32,0,0,270,228 +END + +IDD_TOKATTRIBUTES DIALOGEX 0, 0, 270, 228 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Attributes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x32,0,0,270,228 +END + +IDD_SYSINFO_CPU DIALOGEX 0, 0, 316, 195 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "CPU" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + RTEXT "CPU name",IDC_CPUNAME,41,4,274,16,SS_WORDELLIPSIS + LTEXT "CPU",IDC_TITLE,0,0,37,21 + LTEXT "Panel layout",IDC_LAYOUT,0,105,315,88,NOT WS_VISIBLE + LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,82,NOT WS_VISIBLE +END + +IDD_SYSINFO_CPUPANEL DIALOGEX 0, 0, 237, 86 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Utilization:",IDC_STATIC,0,3,34,8 + LTEXT "Speed:",IDC_STATIC,108,3,24,8 + LTEXT "Static",IDC_UTILIZATION,40,0,62,15 + LTEXT "Static",IDC_SPEED,137,0,100,15 + GROUPBOX "System",IDC_STATIC,0,17,97,55 + LTEXT "Processes",IDC_STATIC,7,28,33,8 + LTEXT "Threads",IDC_STATIC,7,38,27,8 + LTEXT "Handles",IDC_STATIC,7,48,26,8 + LTEXT "Uptime",IDC_STATIC,7,58,23,8 + RTEXT "Static",IDC_ZPROCESSES_V,45,28,46,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZTHREADS_V,45,38,46,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZHANDLES_V,45,48,46,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZUPTIME_V,46,58,45,8,SS_ENDELLIPSIS + CONTROL "&Show one graph per CPU",IDC_ONEGRAPHPERCPU,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,76,96,10 + GROUPBOX "CPU",IDC_STATIC,101,17,136,55 + LTEXT "Context switches delta",IDC_STATIC,109,28,76,8 + LTEXT "Interrupts delta",IDC_STATIC,109,38,52,8 + LTEXT "DPCs delta",IDC_STATIC,109,48,36,8 + LTEXT "System calls delta",IDC_STATIC,109,58,60,8 + RTEXT "Static",IDC_ZCONTEXTSWITCHESDELTA_V,191,28,40,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZINTERRUPTSDELTA_V,185,39,46,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZDPCSDELTA_V,181,48,50,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSYSTEMCALLSDELTA_V,179,59,52,8,SS_ENDELLIPSIS +END + +IDD_SYSINFO_MEMPANEL DIALOGEX 0, 0, 358, 171 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Commit charge",IDC_STATIC,0,0,108,44 + LTEXT "Current",IDC_STATIC,7,11,26,8 + LTEXT "Peak",IDC_STATIC,7,21,16,8 + LTEXT "Limit",IDC_STATIC,7,31,15,8 + RTEXT "Static",IDC_ZCOMMITCURRENT_V,45,11,56,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZCOMMITPEAK_V,41,21,60,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZCOMMITLIMIT_V,40,31,61,8,SS_ENDELLIPSIS + GROUPBOX "Physical memory",IDC_STATIC,112,0,118,73 + LTEXT "Current",IDC_STATIC,120,11,26,8 + LTEXT "Cache WS",IDC_STATIC,120,41,34,8 + RTEXT "Static",IDC_ZPHYSICALCURRENT_V,165,11,57,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPHYSICALTOTAL_V,167,21,55,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPHYSICALCACHEWS_V,159,41,63,8,SS_ENDELLIPSIS + LTEXT "Total",IDC_STATIC,120,21,17,8 + LTEXT "Kernel WS",IDC_STATIC,120,51,34,8 + RTEXT "Static",IDC_ZPHYSICALKERNELWS_V,167,51,55,8,SS_ENDELLIPSIS + LTEXT "Driver WS",IDC_STATIC,120,61,33,8 + RTEXT "Static",IDC_ZPHYSICALDRIVERWS_V,163,61,59,8,SS_ENDELLIPSIS + GROUPBOX "Paged pool",IDC_STATIC,0,47,108,64 + LTEXT "Working set",IDC_STATIC,7,58,40,8 + LTEXT "Limit",IDC_STATIC,7,78,15,8 + RTEXT "Static",IDC_ZPAGEDWORKINGSET_V,54,58,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPAGEDVIRTUALSIZE_V,52,68,49,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPAGEDLIMIT_V,48,78,53,8,SS_ENDELLIPSIS + LTEXT "Virtual size",IDC_STATIC,7,68,36,8 + LTEXT "Allocs delta",IDC_STATIC,7,88,38,8 + RTEXT "Static",IDC_ZPAGEDALLOCSDELTA_V,53,88,48,8,SS_ENDELLIPSIS + LTEXT "Frees delta",IDC_STATIC,7,98,38,8 + RTEXT "Static",IDC_ZPAGEDFREESDELTA_V,51,98,50,8,SS_ENDELLIPSIS + GROUPBOX "Non-paged pool",IDC_STATIC,0,114,108,55 + LTEXT "Usage",IDC_STATIC,7,125,21,8 + LTEXT "Allocs delta",IDC_STATIC,7,145,38,8 + RTEXT "Static",IDC_ZNONPAGEDUSAGE_V,54,125,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZNONPAGEDLIMIT_V,52,135,49,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZNONPAGEDALLOCSDELTA_V,56,145,45,8,SS_ENDELLIPSIS + LTEXT "Limit",IDC_STATIC,7,135,15,8 + LTEXT "Frees delta",IDC_STATIC,7,155,38,8 + RTEXT "Static",IDC_ZNONPAGEDFREESDELTA_V,56,155,45,8,SS_ENDELLIPSIS + GROUPBOX "Memory lists",IDC_STATIC,234,0,123,169 + LTEXT "Zeroed",IDC_STATIC,242,11,24,8 + LTEXT "Modified",IDC_STATIC,242,31,28,8 + RTEXT "Static",IDC_ZLISTZEROED_V,304,11,47,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTFREE_V,302,21,49,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTMODIFIED_V,298,31,53,8,SS_ENDELLIPSIS + LTEXT "Free",IDC_STATIC,242,21,16,8 + LTEXT "Modified no-write",IDC_STATIC,242,41,56,8 + RTEXT "Static",IDC_ZLISTMODIFIEDNOWRITE_V,303,41,48,8,SS_ENDELLIPSIS + LTEXT "Standby",IDC_STATIC,242,61,28,8 + LTEXT "Priority 0",IDC_STATIC,251,71,30,8 + LTEXT "Priority 1",IDC_STATIC,251,80,30,8 + LTEXT "Priority 2",IDC_STATIC,251,90,30,8 + LTEXT "Priority 3",IDC_STATIC,251,100,30,8 + LTEXT "Priority 4",IDC_STATIC,251,110,30,8 + LTEXT "Priority 5",IDC_STATIC,251,120,30,8 + LTEXT "Priority 6",IDC_STATIC,251,130,30,8 + LTEXT "Priority 7",IDC_STATIC,251,140,30,8 + RTEXT "Static",IDC_ZLISTSTANDBY_V,303,61,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY0_V,303,71,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY1_V,303,80,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY2_V,303,90,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY3_V,303,100,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY4_V,303,110,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY5_V,303,120,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY6_V,303,130,48,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY7_V,303,140,48,8,SS_ENDELLIPSIS + GROUPBOX "Paging",IDC_STATIC,112,75,118,55 + LTEXT "Page faults delta",IDC_STATIC,120,86,57,8 + LTEXT "Pagefile writes delta",IDC_STATIC,120,106,68,8 + RTEXT "Static",IDC_ZPAGINGPAGEFAULTSDELTA_V,183,86,39,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPAGINGPAGEREADSDELTA_V,179,96,43,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZPAGINGPAGEFILEWRITESDELTA_V,189,106,33,8,SS_ENDELLIPSIS + LTEXT "Page reads delta",IDC_STATIC,120,96,58,8 + LTEXT "Mapped writes delta",IDC_STATIC,120,116,68,8 + RTEXT "Static",IDC_ZPAGINGMAPPEDWRITESDELTA_V,191,116,31,8,SS_ENDELLIPSIS + LTEXT "Modified pagefile",IDC_STATIC,242,51,55,8 + RTEXT "Static",IDC_ZLISTMODIFIEDPAGEFILE_V,303,51,48,8,SS_ENDELLIPSIS + PUSHBUTTON "&More",IDC_MORE,242,152,50,14 + RTEXT "Static",IDC_ZPHYSICALRESERVED_V,167,31,55,8,SS_ENDELLIPSIS + LTEXT "Reserved",IDC_STATIC,120,31,32,8 + GROUPBOX "Mapped IO",IDC_STATIC,112,132,118,36 + LTEXT "Mapped reads",IDC_STATIC,119,145,68,8 + RTEXT "Static",IDC_ZMAPPEDREADIO,180,145,42,8,SS_ENDELLIPSIS + LTEXT "Mapped writes",IDC_STATIC,119,156,68,8 + RTEXT "Static",IDC_ZMAPPEDWRITEIO,180,156,42,8,SS_ENDELLIPSIS +END + +IDD_SYSINFO_MEM DIALOGEX 0, 0, 316, 250 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Memory" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Memory",IDC_TITLE,0,0,110,21 + LTEXT "Panel layout",IDC_LAYOUT,0,74,315,175,NOT WS_VISIBLE + LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,51,NOT WS_VISIBLE + RTEXT "Total physical",IDC_TOTALPHYSICAL,127,4,188,16,SS_WORDELLIPSIS + LTEXT "Commit charge:",IDC_COMMIT_L,111,1,52,8 + LTEXT "Physical memory:",IDC_PHYSICAL_L,111,7,56,8 +END + +IDD_SYSINFO_IO DIALOGEX 0, 0, 316, 187 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION | WS_SYSMENU +CAPTION "I/O" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "I/O",IDC_TITLE,0,0,110,21 + LTEXT "Panel layout",IDC_LAYOUT,0,109,315,78,NOT WS_VISIBLE + LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,84,NOT WS_VISIBLE +END + +IDD_SYSINFO_IOPANEL DIALOGEX 0, 0, 276, 75 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "I/O deltas",IDC_STATIC,0,0,133,74 + LTEXT "Reads delta",IDC_STATIC,7,11,40,8 + LTEXT "Read bytes delta",IDC_STATIC,7,21,56,8 + LTEXT "Writes delta",IDC_STATIC,7,31,40,8 + LTEXT "Write bytes delta",IDC_STATIC,7,41,57,8 + RTEXT "Static",IDC_ZREADSDELTA_V,59,11,67,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZREADBYTESDELTA_V,75,21,51,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITESDELTA_V,57,31,69,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,73,41,53,8,SS_ENDELLIPSIS + LTEXT "Other delta",IDC_STATIC,7,51,38,8 + LTEXT "Other bytes delta",IDC_STATIC,7,61,58,8 + RTEXT "Static",IDC_ZOTHERDELTA_V,67,51,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZOTHERBYTESDELTA_V,81,61,45,8,SS_ENDELLIPSIS + GROUPBOX "I/O totals",IDC_STATIC,137,0,133,74 + LTEXT "Reads",IDC_STATIC,145,11,21,8 + LTEXT "Read bytes",IDC_STATIC,145,21,38,8 + LTEXT "Writes",IDC_STATIC,145,31,22,8 + LTEXT "Write bytes",IDC_STATIC,145,41,38,8 + RTEXT "Static",IDC_ZREADS_V,197,11,67,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZREADBYTES_V,192,21,72,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITES_V,195,31,69,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITEBYTES_V,190,41,74,8,SS_ENDELLIPSIS + LTEXT "Other",IDC_STATIC,145,51,20,8 + LTEXT "Other bytes",IDC_STATIC,145,61,40,8 + RTEXT "Static",IDC_ZOTHER_V,191,51,73,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZOTHERBYTES_V,192,61,72,8,SS_ENDELLIPSIS +END + +IDD_MEMLISTS DIALOGEX 0, 0, 228, 195 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOPMOST +CAPTION "Memory Lists" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Close",IDOK,171,174,50,14 + LTEXT "Zeroed",IDC_STATIC,7,7,24,8 + LTEXT "Free",IDC_STATIC,7,17,16,8 + LTEXT "Modified",IDC_STATIC,7,28,28,8 + LTEXT "Modified no-write",IDC_STATIC,7,39,56,8 + LTEXT "Bad",IDC_STATIC,7,60,13,8 + LTEXT "Standby",IDC_STATIC,7,72,28,8 + LTEXT "Priority 0",IDC_STATIC,23,83,30,8 + LTEXT "Priority 1",IDC_STATIC,23,94,30,8 + LTEXT "Priority 2",IDC_STATIC,23,105,30,8 + LTEXT "Priority 3",IDC_STATIC,23,116,30,8 + LTEXT "Priority 4",IDC_STATIC,23,128,30,8 + LTEXT "Priority 5",IDC_STATIC,23,139,30,8 + LTEXT "Priority 6",IDC_STATIC,23,150,30,8 + LTEXT "Priority 7",IDC_STATIC,23,161,30,8 + RTEXT "Static",IDC_ZLISTZEROED_V,73,7,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTFREE_V,73,17,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTMODIFIED_V,73,28,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTMODIFIEDNOWRITE_V,73,39,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTBAD_V,73,60,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY_V,73,72,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY0_V,73,83,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY1_V,73,94,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY2_V,73,105,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY3_V,73,116,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY4_V,73,128,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY5_V,73,139,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY6_V,73,150,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTSTANDBY7_V,73,161,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED_V,162,72,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED0_V,162,84,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED1_V,162,95,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED2_V,162,106,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED3_V,162,117,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED4_V,162,128,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED5_V,162,139,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED6_V,162,150,59,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZLISTREPURPOSED7_V,162,161,59,8,SS_ENDELLIPSIS + RTEXT "(Repurposed)",IDC_STATIC,162,60,59,8 + PUSHBUTTON "&Empty",IDC_EMPTY,117,174,50,14 + LTEXT "Modified pagefile",IDC_STATIC,7,49,55,8 + RTEXT "Static",IDC_ZLISTMODIFIEDPAGEFILE_V,73,49,59,8,SS_ENDELLIPSIS +END + +IDD_CONTAINER DIALOGEX 0, 0, 316, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +END + +IDD_MINIINFO DIALOGEX 0, 0, 217, 150 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Content layout",IDC_LAYOUT,4,3,210,128,NOT WS_VISIBLE + CONTROL "Section",IDC_SECTION,"Static",SS_LEFTNOWORDWRAP | SS_NOTIFY | SS_ENDELLIPSIS | WS_GROUP,4,134,177,13 + PUSHBUTTON "Options",IDC_OPTIONS,183,133,15,14,BS_ICON + CONTROL "Pin",IDC_PINWINDOW,"Button",BS_AUTOCHECKBOX | BS_ICON | BS_PUSHLIKE | WS_TABSTOP,199,133,15,14 +END + +IDD_MINIINFO_LIST DIALOGEX 0, 0, 217, 141 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "CPU" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0x82,0,0,217,140,WS_EX_CLIENTEDGE +END + +IDD_MITIGATION DIALOGEX 0, 0, 277, 217 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mitigation Policies" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,7,7,263,108 + LTEXT "Description:",IDC_DESCRIPTIONLABEL,7,118,38,8 + EDITTEXT IDC_DESCRIPTION,7,129,263,62,ES_MULTILINE | ES_READONLY | WS_VSCROLL + DEFPUSHBUTTON "OK",IDOK,220,196,50,14 +END + +IDD_EDITENV DIALOGEX 0, 0, 311, 177 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Edit Environment Variable" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Name:",IDC_STATIC,7,8,21,8 + EDITTEXT IDC_NAME,38,7,266,12,ES_AUTOHSCROLL + LTEXT "Value:",IDC_STATIC,7,25,21,8 + EDITTEXT IDC_VALUE,38,24,266,128,ES_MULTILINE | WS_VSCROLL + DEFPUSHBUTTON "OK",IDOK,200,156,50,14 + PUSHBUTTON "Cancel",IDCANCEL,254,156,50,14 +END + +IDD_OPTIONS DIALOGEX 0, 0, 423, 247 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Close",IDOK,371,231,50,14 + CONTROL "",IDC_SECTIONTREE,"SysTreeView32",TVS_SHOWSELALWAYS | TVS_TRACKSELECT | TVS_FULLROWSELECT | WS_HSCROLL | WS_TABSTOP,2,1,103,244 + CONTROL "",IDD_CONTAINER,"#32770",0x44c,107,0,316,229,WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT | 0xc0000800L + LTEXT "",IDC_SEPARATOR,105,0,8,246 + PUSHBUTTON "Reset",IDC_RESET,113,231,50,14 + PUSHBUTTON "Apply",IDC_APPLY,319,231,50,14,NOT WS_VISIBLE + PUSHBUTTON "Cleanup",IDC_CLEANUP,165,231,50,14 +END + +IDD_PLUGINPROPERTIES DIALOGEX 0, 0, 291, 152 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Plugin Properties" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Plugin",IDC_STATIC,7,7,277,122 + LTEXT "Name:",IDC_STATIC,15,18,22,8 + EDITTEXT IDC_NAME,71,18,104,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Internal name:",IDC_STATIC,15,30,49,8 + EDITTEXT IDC_INTERNALNAME,71,30,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Author:",IDC_STATIC,15,41,26,8 + EDITTEXT IDC_AUTHOR,71,41,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "URL:",IDC_STATIC,15,52,16,8 + EDITTEXT IDC_URL,71,52,174,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + CONTROL "Open",IDC_OPENURL,"SysLink",WS_TABSTOP,250,52,26,10 + LTEXT "File name:",IDC_STATIC,15,63,34,8 + EDITTEXT IDC_FILENAME,71,63,203,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Version:",IDC_STATIC,183,18,27,8 + EDITTEXT IDC_VERSION,215,18,59,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Description:",IDC_STATIC,15,74,39,8 + EDITTEXT IDC_DESCRIPTION,14,86,263,37,ES_MULTILINE | ES_READONLY + PUSHBUTTON "Options...",IDC_OPTIONS,7,131,50,14 + DEFPUSHBUTTON "Close",IDOK,234,131,50,14 +END + +IDD_PLUGINSDISABLED DIALOGEX 0, 0, 309, 176 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Disabled Plugins" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,257,160,50,14 + CONTROL "",IDC_LIST_DISABLED,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,2,2,305,157 + LTEXT "Changes may require a restart to take effect.",IDC_INSTRUCTION,2,163,148,8,NOT WS_VISIBLE +END + +IDD_PROCWMIPROVIDERS DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "WMI Providers" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,2,256,256 +END + +IDD_COLUMNSETS DIALOGEX 0, 0, 231, 188 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Organize Column Sets" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Move up",IDC_MOVEUP,177,42,50,14 + PUSHBUTTON "Move down",IDC_MOVEDOWN,177,59,50,14 + PUSHBUTTON "OK",IDOK,177,171,50,14 + PUSHBUTTON "Rename",IDC_RENAME,177,4,50,14 + PUSHBUTTON "Delete",IDC_REMOVE,177,94,50,14 + CONTROL "",IDC_COLUMNSETLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_EDITLABELS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,4,4,170,180 +END + +IDD_RUNFILEDLG DIALOGEX 0, 0, 235, 105 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Run" +FONT 9, "Segoe UI", 0, 0, 0x1 +BEGIN + ICON 100,IDC_FILEICON,7,11,18,17 + LTEXT "Type the name of a program, folder, document, or Internet resource, and Windows will open it for you.",IDC_STATIC,36,11,182,22 + LTEXT "&Open:",IDC_STATIC,7,39,28,10 + COMBOBOX IDC_PROGRAMCOMBO,36,37,183,200,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP + CONTROL "Create this task with administrative privileges.",IDC_TOGGLEELEVATION, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,40,50,180,10 + DEFPUSHBUTTON "OK",IDOK,62,82,50,14 + PUSHBUTTON "Cancel",IDCANCEL,116,82,50,14 + PUSHBUTTON "&Browse...",IDC_BROWSE,170,82,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PROCGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 253 + END + + IDD_PROCMODULES, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 + END + + IDD_PROCTHREADS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 253 + END + + IDD_PROCHANDLES, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 + END + + IDD_PROCENVIRONMENT, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 + END + + IDD_THRDSTACK, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 271 + TOPMARGIN, 2 + BOTTOMMARGIN, 226 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 263 + TOPMARGIN, 7 + BOTTOMMARGIN, 188 + END + + IDD_SRVLIST, DIALOG + BEGIN + END + + IDD_SRVGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_HNDLGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 174 + END + + IDD_INFORMATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + TOPMARGIN, 7 + BOTTOMMARGIN, 177 + END + + IDD_FINDOBJECTS, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 355 + TOPMARGIN, 4 + BOTTOMMARGIN, 231 + END + + IDD_OBJTOKEN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 253 + END + + IDD_HIDDENPROCESSES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 330 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END + + IDD_RUNAS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 286 + TOPMARGIN, 7 + BOTTOMMARGIN, 120 + END + + IDD_PROGRESS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 209 + TOPMARGIN, 7 + BOTTOMMARGIN, 55 + END + + IDD_PAGEFILES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 315 + TOPMARGIN, 7 + BOTTOMMARGIN, 155 + END + + IDD_TOKGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 263 + TOPMARGIN, 7 + BOTTOMMARGIN, 221 + END + + IDD_TOKADVANCED, DIALOG + BEGIN + END + + IDD_OBJJOB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 253 + END + + IDD_OBJEVENT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 69 + END + + IDD_OBJMUTANT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 69 + END + + IDD_OBJSEMAPHORE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 69 + END + + IDD_OBJTIMER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 69 + END + + IDD_JOBSTATISTICS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 252 + TOPMARGIN, 7 + BOTTOMMARGIN, 179 + END + + IDD_OBJEVENTPAIR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 69 + END + + IDD_AFFINITY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 272 + TOPMARGIN, 7 + BOTTOMMARGIN, 220 + END + + IDD_SYSINFO, DIALOG + BEGIN + END + + IDD_EDITMESSAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 156 + END + + IDD_SESSION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 194 + TOPMARGIN, 7 + BOTTOMMARGIN, 142 + END + + IDD_PROCMEMORY, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 + END + + IDD_CHOOSE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 192 + TOPMARGIN, 7 + BOTTOMMARGIN, 66 + END + + IDD_OPTGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 308 + TOPMARGIN, 7 + BOTTOMMARGIN, 213 + END + + IDD_OPTHIGHLIGHTING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 243 + TOPMARGIN, 7 + BOTTOMMARGIN, 167 + END + + IDD_CHOOSECOLUMNS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 374 + TOPMARGIN, 7 + BOTTOMMARGIN, 200 + END + + IDD_NETSTACK, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 254 + TOPMARGIN, 7 + BOTTOMMARGIN, 221 + END + + IDD_CREATESERVICE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 280 + TOPMARGIN, 7 + BOTTOMMARGIN, 126 + END + + IDD_PROCPERFORMANCE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 253 + END + + IDD_PROCSTATISTICS, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 257 + TOPMARGIN, 3 + BOTTOMMARGIN, 258 + END + + IDD_OPTADVANCED, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + TOPMARGIN, 7 + BOTTOMMARGIN, 218 + END + + IDD_GDIHANDLES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 344 + TOPMARGIN, 7 + BOTTOMMARGIN, 300 + END + + IDD_LOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 306 + TOPMARGIN, 7 + BOTTOMMARGIN, 296 + END + + IDD_MEMEDIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 434 + TOPMARGIN, 7 + BOTTOMMARGIN, 262 + END + + IDD_MEMPROTECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 263 + TOPMARGIN, 7 + BOTTOMMARGIN, 164 + END + + IDD_MEMRESULTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 306 + TOPMARGIN, 7 + BOTTOMMARGIN, 259 + END + + IDD_MEMSTRING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 234 + TOPMARGIN, 7 + BOTTOMMARGIN, 79 + END + + IDD_OPTGRAPHS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 243 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END + + IDD_PLUGINMANAGER, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 499 + TOPMARGIN, 2 + BOTTOMMARGIN, 270 + END + + IDD_HANDLESTATS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 212 + TOPMARGIN, 7 + BOTTOMMARGIN, 168 + END + + IDD_PROCRECORD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 195 + END + + IDD_CHOOSEPROCESS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + TOPMARGIN, 7 + BOTTOMMARGIN, 232 + END + + IDD_PROCSERVICES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 254 + END + + IDD_SHADOWSESSION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 221 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_TOKCAPABILITIES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 263 + TOPMARGIN, 7 + BOTTOMMARGIN, 221 + END + + IDD_TOKATTRIBUTES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 263 + TOPMARGIN, 7 + BOTTOMMARGIN, 221 + END + + IDD_SYSINFO_CPU, DIALOG + BEGIN + BOTTOMMARGIN, 193 + END + + IDD_SYSINFO_CPUPANEL, DIALOG + BEGIN + BOTTOMMARGIN, 85 + END + + IDD_SYSINFO_MEMPANEL, DIALOG + BEGIN + END + + IDD_SYSINFO_MEM, DIALOG + BEGIN + BOTTOMMARGIN, 247 + END + + IDD_SYSINFO_IO, DIALOG + BEGIN + END + + IDD_SYSINFO_IOPANEL, DIALOG + BEGIN + BOTTOMMARGIN, 74 + END + + IDD_MEMLISTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 221 + TOPMARGIN, 7 + BOTTOMMARGIN, 188 + END + + IDD_CONTAINER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 309 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_MINIINFO, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 214 + TOPMARGIN, 3 + BOTTOMMARGIN, 147 + END + + IDD_MINIINFO_LIST, DIALOG + BEGIN + END + + IDD_MITIGATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 270 + TOPMARGIN, 7 + BOTTOMMARGIN, 210 + END + + IDD_EDITENV, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 304 + TOPMARGIN, 7 + BOTTOMMARGIN, 170 + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 2 + BOTTOMMARGIN, 245 + END + + IDD_PLUGINPROPERTIES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 284 + TOPMARGIN, 7 + BOTTOMMARGIN, 145 + END + + IDD_PLUGINSDISABLED, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 307 + TOPMARGIN, 2 + BOTTOMMARGIN, 174 + END + + IDD_PROCWMIPROVIDERS, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 2 + BOTTOMMARGIN, 258 + END + + IDD_COLUMNSETS, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 227 + TOPMARGIN, 4 + BOTTOMMARGIN, 184 + END + IDD_RUNFILEDLG, DIALOG + BEGIN + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINWND_ACCEL ACCELERATORS +BEGIN + VK_ESCAPE, ID_ESC_EXIT, VIRTKEY, NOINVERT + "F", ID_HACKER_FINDHANDLESORDLLS, VIRTKEY, CONTROL, NOINVERT + "R", ID_HACKER_RUN, VIRTKEY, CONTROL, NOINVERT + "R", ID_HACKER_RUNAS, VIRTKEY, SHIFT, CONTROL, NOINVERT + "S", ID_HACKER_SAVE, VIRTKEY, CONTROL, NOINVERT + "L", ID_HELP_LOG, VIRTKEY, CONTROL, NOINVERT + "M", ID_PROCESS_SEARCHONLINE, VIRTKEY, CONTROL, NOINVERT + VK_TAB, ID_TAB_NEXT, VIRTKEY, CONTROL, NOINVERT + VK_TAB, ID_TAB_PREV, VIRTKEY, SHIFT, CONTROL, NOINVERT + VK_F5, ID_VIEW_REFRESH, VIRTKEY, NOINVERT + "I", ID_VIEW_SYSTEMINFORMATION, VIRTKEY, CONTROL, NOINVERT + VK_PAUSE, ID_VIEW_UPDATEAUTOMATICALLY, VIRTKEY, NOINVERT + VK_F6, ID_VIEW_UPDATEAUTOMATICALLY, VIRTKEY, NOINVERT +END + +IDR_SYSINFO_ACCEL ACCELERATORS +BEGIN + "1", ID_DIGIT1, VIRTKEY, NOINVERT + "2", ID_DIGIT2, VIRTKEY, NOINVERT + "3", ID_DIGIT3, VIRTKEY, NOINVERT + "4", ID_DIGIT4, VIRTKEY, NOINVERT + "5", ID_DIGIT5, VIRTKEY, NOINVERT + "6", ID_DIGIT6, VIRTKEY, NOINVERT + "7", ID_DIGIT7, VIRTKEY, NOINVERT + "8", ID_DIGIT8, VIRTKEY, NOINVERT + "9", ID_DIGIT9, VIRTKEY, NOINVERT + "B", IDC_BACK, VIRTKEY, ALT, NOINVERT + VK_BACK, IDC_BACK, VIRTKEY, NOINVERT + VK_F6, IDC_PAUSE, VIRTKEY, NOINVERT + VK_PAUSE, IDC_PAUSE, VIRTKEY, NOINVERT + VK_F5, IDC_REFRESH, VIRTKEY, NOINVERT + VK_F11, IDC_MAXSCREEN, VIRTKEY, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_CROSS BITMAP "resources\\cross.bmp" + +IDB_TICK BITMAP "resources\\tick.bmp" + +IDB_SEARCH_ACTIVE_BMP BITMAP "resources\\active_search.bmp" + +IDB_SEARCH_INACTIVE_BMP BITMAP "resources\\inactive_search.bmp" + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +IDR_RT_MANIFEST RT_MANIFEST "ProcessHacker.manifest" + + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" + +IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/ProcessHacker/ProcessHacker.vcxproj b/ProcessHacker/ProcessHacker.vcxproj index 7b8342faa6c4..00285a8f03ad 100644 --- a/ProcessHacker/ProcessHacker.vcxproj +++ b/ProcessHacker/ProcessHacker.vcxproj @@ -1,439 +1,447 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {0271DD27-6707-4290-8DFE-285702B7115D} - ProcessHacker - Win32Proj - 10.0.15063.0 - - - - Application - Unicode - true - v141 - - - Application - Unicode - v141 - - - Application - Unicode - true - v141 - - - Application - Unicode - v141 - - - - - - - - - - - - - - - - - - - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - false - false - false - false - - - - Disabled - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - - - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) - true - Windows - MachineX86 - 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - - ..\build\CustomBuildTool.exe -updaterev - Generating revision number... - - - ..\build\CustomBuildTool.exe -cleanup - Cleaning up build... - - - - - Disabled - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - - - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) - true - Windows - MachineX64 - 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - - ..\build\CustomBuildTool.exe -updaterev - Generating revision number... - - - ..\build\CustomBuildTool.exe -cleanup - Cleaning up build... - - - - - MaxSpeed - true - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) - true - MultiThreaded - false - true - Level3 - ProgramDatabase - StdCall - true - true - StreamingSIMDExtensions - - - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) - true - Windows - true - true - MachineX86 - true - 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - - ..\build\CustomBuildTool.exe -updaterev - Generating revision number... - - - ..\build\CustomBuildTool.exe -cleanup - Cleaning up build... - - - - - MaxSpeed - true - ..\phnt\include;..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_PHAPP_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions) - true - MultiThreaded - false - true - Level3 - ProgramDatabase - StdCall - true - true - - - aclui.lib;comctl32.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;uxtheme.lib;version.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) - true - Windows - true - true - MachineX64 - true - 6.01 - ..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - ProcessHacker.def - aclui.dll;iphlpapi.dll;ws2_32.dll;%(DelayLoadDLLs) - - - ..\build\CustomBuildTool.exe -updaterev - Generating revision number... - - - ..\build\CustomBuildTool.exe -cleanup - Cleaning up build... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {0271DD27-6707-4290-8DFE-285702B7115D} + ProcessHacker + Win32Proj + 10.0 + + + + Application + Unicode + true + v142 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + Spectre + + + Application + Unicode + v142 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + + + Application + Unicode + true + v142 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + Spectre + + + Application + Unicode + v142 + WindowsLocalDebugger + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + false + false + false + false + + + + Disabled + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN32;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + true + + + aclui.lib;comctl32.lib;dnsapi.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;shlwapi.lib;userenv.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + true + Windows + MachineX86 + 6.01 + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ProcessHacker.def + advapi32.dll;aclui.dll;comdlg32.dll;comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;oleaut32.dll;ole32.dll;shell32.dll;shlwapi.dll;userenv.dll;user32.dll;uxtheme.dll;version.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + /DEPENDENTLOADFLAG:0xA00 %(AdditionalOptions) + + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + + + "$(SolutionDir)build\build_sdk.cmd" + + + + + Disabled + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN64;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + true + + + aclui.lib;comctl32.lib;dnsapi.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;shlwapi.lib;userenv.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + true + Windows + MachineX64 + 6.01 + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ProcessHacker.def + advapi32.dll;aclui.dll;comdlg32.dll;comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;oleaut32.dll;ole32.dll;shell32.dll;shlwapi.dll;userenv.dll;user32.dll;uxtheme.dll;version.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + /DEPENDENTLOADFLAG:0xA00 %(AdditionalOptions) + + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + + + + "$(SolutionDir)build\build_sdk.cmd" + + + + + MaxSpeed + true + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + true + MultiThreaded + false + true + Level3 + ProgramDatabase + StdCall + true + true + StreamingSIMDExtensions + Guard + + + aclui.lib;comctl32.lib;dnsapi.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;shlwapi.lib;userenv.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + Windows + true + true + MachineX86 + true + 6.01 + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ProcessHacker.def + advapi32.dll;aclui.dll;comdlg32.dll;comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;oleaut32.dll;ole32.dll;shell32.dll;shlwapi.dll;userenv.dll;user32.dll;uxtheme.dll;version.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + true + /DEPENDENTLOADFLAG:0xA00 %(AdditionalOptions) + + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + + + "$(SolutionDir)build\build_sdk.cmd" + + + + + MaxSpeed + true + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_PHAPP_;_WINDOWS;HAVE_CONFIG_H;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + true + MultiThreaded + false + true + Level3 + ProgramDatabase + StdCall + true + true + Guard + + + aclui.lib;comctl32.lib;dnsapi.lib;iphlpapi.lib;noarg.obj;noenv.obj;ntdll.lib;phlib.lib;shlwapi.lib;userenv.lib;uxtheme.lib;version.lib;wbemuuid.lib;windowscodecs.lib;winsta.lib;ws2_32.lib;%(AdditionalDependencies) + Windows + true + true + MachineX64 + true + 6.01 + $(SolutionDir)phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + ProcessHacker.def + advapi32.dll;aclui.dll;comdlg32.dll;comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;oleaut32.dll;ole32.dll;shell32.dll;shlwapi.dll;userenv.dll;user32.dll;uxtheme.dll;version.dll;winsta.dll;ws2_32.dll;%(DelayLoadDLLs) + true + /DEPENDENTLOADFLAG:0xA00 %(AdditionalOptions) + + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + $(SolutionDir)phnt\include;$(SolutionDir)phlib\include;include;%(AdditionalIncludeDirectories) + + + "$(SolutionDir)build\build_sdk.cmd" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProcessHacker/ProcessHacker.vcxproj.filters b/ProcessHacker/ProcessHacker.vcxproj.filters index 85a5e1b50318..a7ddace7b009 100644 --- a/ProcessHacker/ProcessHacker.vcxproj.filters +++ b/ProcessHacker/ProcessHacker.vcxproj.filters @@ -1,638 +1,611 @@ - - - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;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 - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {3fa0b151-12c1-4832-94fb-e16c52584b65} - - - {eaabb14f-9091-4b18-8764-45c8350f4887} - - - {84a3be0b-42cf-42ae-bcd3-16f165caa0db} - - - {e72b6c69-b510-4578-9ed5-34a16d8dffc1} - - - {c4b014d0-6bd0-4848-a23f-e639563d998e} - - - {67c40321-0b28-4c16-8c48-dc0b64c148d7} - - - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Mini-XML - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - phsvc - - - phsvc - - - phsvc - - - phsvc - - - phsvc - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - PCRE - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - Process Hacker - - - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - PCRE\Headers - - - PCRE\Headers - - - PCRE\Headers - - - PCRE\Headers - - - PCRE\Headers - - - PCRE\Headers - - - Mini-XML\Headers - - - Mini-XML\Headers - - - Mini-XML\Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - Headers - - - - - Resources - - - Resources - - - Resources - - - Resources - - - Resources - - - Resources - - - Resources - - - Resources - - - Module - - - - - Resources - - - Resources - - - - - Resources - - - - - Resources - - - Resources - - - Resources - - - Resources - - + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;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 + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {3fa0b151-12c1-4832-94fb-e16c52584b65} + + + {84a3be0b-42cf-42ae-bcd3-16f165caa0db} + + + {e72b6c69-b510-4578-9ed5-34a16d8dffc1} + + + {67c40321-0b28-4c16-8c48-dc0b64c148d7} + + + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + phsvc + + + phsvc + + + phsvc + + + phsvc + + + phsvc + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + PCRE + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + Process Hacker + + + PCRE + + + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + PCRE\Headers + + + PCRE\Headers + + + PCRE\Headers + + + PCRE\Headers + + + PCRE\Headers + + + PCRE\Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Module + + + + + Resources + + + + + Resources + + + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + \ No newline at end of file diff --git a/ProcessHacker/about.c b/ProcessHacker/about.c index fd9872cc7edf..357ca6374037 100644 --- a/ProcessHacker/about.c +++ b/ProcessHacker/about.c @@ -1,190 +1,220 @@ -/* - * Process Hacker - - * about dialog - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -static INT_PTR CALLBACK PhpAboutDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_STRING appName; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - -#if (PHAPP_VERSION_REVISION != 0) - appName = PhFormatString( - L"Process Hacker %u.%u.%u", - PHAPP_VERSION_MAJOR, - PHAPP_VERSION_MINOR, - PHAPP_VERSION_REVISION - ); -#else - appName = PhFormatString( - L"Process Hacker %u.%u", - PHAPP_VERSION_MAJOR, - PHAPP_VERSION_MINOR - ); -#endif - - SetDlgItemText(hwndDlg, IDC_ABOUT_NAME, appName->Buffer); - PhDereferenceObject(appName); - - SetDlgItemText(hwndDlg, IDC_CREDITS, - L" Installer by XhmikosR\n" - L"Thanks to:\n" - L" dmex\n" - L" Donors - thank you for your support!\n" - L" Sysinternals Forums\n" - L" ReactOS\n" - L"Process Hacker uses the following components:\n" - L" Mini-XML by Michael Sweet\n" - L" PCRE\n" - L" MD5 code by Jouni Malinen\n" - L" SHA1 code by Filip Navara, based on code by Steve Reid\n" - L" Silk icons\n" - L" Farm-fresh web icons\n" - ); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (LPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_DIAGNOSTICS: - { - PhShowInformationDialog(hwndDlg, PH_AUTO_T(PH_STRING, PhGetDiagnosticsString())->Buffer, 0); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case NM_CLICK: - { - switch (header->idFrom) - { - case IDC_CREDITS: - case IDC_LINK_SF: - PhShellExecute(hwndDlg, ((PNMLINK)header)->item.szUrl, NULL); - break; - } - } - break; - } - } - break; - } - - return FALSE; -} - -VOID PhShowAboutDialog( - _In_ HWND ParentWindowHandle - ) -{ - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_ABOUT), - ParentWindowHandle, - PhpAboutDlgProc - ); -} - -FORCEINLINE ULONG PhpGetObjectTypeObjectCount( - _In_ PPH_OBJECT_TYPE ObjectType - ) -{ - PH_OBJECT_TYPE_INFORMATION info; - - PhGetObjectTypeInformation(ObjectType, &info); - - return info.NumberOfObjects; -} - -PPH_STRING PhGetDiagnosticsString( - VOID - ) -{ - PH_STRING_BUILDER stringBuilder; - - PhInitializeStringBuilder(&stringBuilder, 50); - - PhAppendFormatStringBuilder(&stringBuilder, L"OBJECT INFORMATION\r\n"); - -#define OBJECT_TYPE_COUNT(Type) PhAppendFormatStringBuilder(&stringBuilder, \ - L#Type L": %u objects\r\n", PhpGetObjectTypeObjectCount(Type)) - - // ref - OBJECT_TYPE_COUNT(PhObjectTypeObject); - - // basesup - OBJECT_TYPE_COUNT(PhStringType); - OBJECT_TYPE_COUNT(PhBytesType); - OBJECT_TYPE_COUNT(PhListType); - OBJECT_TYPE_COUNT(PhPointerListType); - OBJECT_TYPE_COUNT(PhHashtableType); - OBJECT_TYPE_COUNT(PhFileStreamType); - - // ph - OBJECT_TYPE_COUNT(PhSymbolProviderType); - OBJECT_TYPE_COUNT(PhProcessItemType); - OBJECT_TYPE_COUNT(PhServiceItemType); - OBJECT_TYPE_COUNT(PhNetworkItemType); - OBJECT_TYPE_COUNT(PhModuleProviderType); - OBJECT_TYPE_COUNT(PhModuleItemType); - OBJECT_TYPE_COUNT(PhThreadProviderType); - OBJECT_TYPE_COUNT(PhThreadItemType); - OBJECT_TYPE_COUNT(PhHandleProviderType); - OBJECT_TYPE_COUNT(PhHandleItemType); - OBJECT_TYPE_COUNT(PhMemoryItemType); - - return PhFinalStringBuilderString(&stringBuilder); -} +/* + * Process Hacker - + * about dialog + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static HWND PhAboutWindowHandle = NULL; + +static INT_PTR CALLBACK PhpAboutDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_STRING appName; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); + +#if (PHAPP_VERSION_REVISION != 0) + appName = PhFormatString( + L"Process Hacker %lu.%lu.%lu (%hs)", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR, + PHAPP_VERSION_REVISION, + PHAPP_VERSION_COMMIT + ); +#else + appName = PhFormatString( + L"Process Hacker %lu.%lu", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR + ); +#endif + + PhSetDialogItemText(hwndDlg, IDC_ABOUT_NAME, appName->Buffer); + PhDereferenceObject(appName); + + PhSetDialogItemText(hwndDlg, IDC_CREDITS, + L"Thanks to:\n" + L" wj32 - Wen Jia Liu\n" + L" dmex - Steven G\n" + L" XhmikosR\n" + L" Contributors - thank you for your additions!\n" + L" Donors - thank you for your support!\n\n" + L"Process Hacker uses the following components:\n" + L" Mini-XML by Michael Sweet\n" + L" PCRE\n" + L" json-c\n" + L" MD5 code by Jouni Malinen\n" + L" SHA1 code by Filip Navara, based on code by Steve Reid\n" + L" Silk icons\n" + L" Farm-fresh web icons\n" + ); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhUnregisterDialog(PhAboutWindowHandle); + PhAboutWindowHandle = NULL; + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + case IDC_DIAGNOSTICS: + { + PhShowInformationDialog(hwndDlg, PH_AUTO_T(PH_STRING, PhGetDiagnosticsString())->Buffer, 0); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_CLICK: + { + switch (header->idFrom) + { + case IDC_CREDITS: + case IDC_LINK_SF: + PhShellExecute(hwndDlg, ((PNMLINK)header)->item.szUrl, NULL); + break; + } + } + break; + } + } + break; + } + + return FALSE; +} + +VOID PhShowAboutDialog( + VOID + ) +{ + if (!PhAboutWindowHandle) + { + PhAboutWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_ABOUT), + NULL, + PhpAboutDlgProc + ); + PhRegisterDialog(PhAboutWindowHandle); + ShowWindow(PhAboutWindowHandle, SW_SHOW); + } + + if (IsMinimized(PhAboutWindowHandle)) + ShowWindow(PhAboutWindowHandle, SW_RESTORE); + else + SetForegroundWindow(PhAboutWindowHandle); +} + +FORCEINLINE ULONG PhpGetObjectTypeObjectCount( + _In_ PPH_OBJECT_TYPE ObjectType + ) +{ + PH_OBJECT_TYPE_INFORMATION info; + + memset(&info, 0, sizeof(PH_OBJECT_TYPE_INFORMATION)); + if (ObjectType) PhGetObjectTypeInformation(ObjectType, &info); + + return info.NumberOfObjects; +} + +PPH_STRING PhGetDiagnosticsString( + VOID + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 50); + + PhAppendFormatStringBuilder(&stringBuilder, L"OBJECT INFORMATION\r\n"); + +#define OBJECT_TYPE_COUNT(Type) PhAppendFormatStringBuilder(&stringBuilder, \ + L#Type L": %lu objects\r\n", PhpGetObjectTypeObjectCount(Type)) + + // ref + OBJECT_TYPE_COUNT(PhObjectTypeObject); + + // basesup + OBJECT_TYPE_COUNT(PhStringType); + OBJECT_TYPE_COUNT(PhBytesType); + OBJECT_TYPE_COUNT(PhListType); + OBJECT_TYPE_COUNT(PhPointerListType); + OBJECT_TYPE_COUNT(PhHashtableType); + OBJECT_TYPE_COUNT(PhFileStreamType); + + // ph + OBJECT_TYPE_COUNT(PhSymbolProviderType); + OBJECT_TYPE_COUNT(PhProcessItemType); + OBJECT_TYPE_COUNT(PhServiceItemType); + OBJECT_TYPE_COUNT(PhNetworkItemType); + OBJECT_TYPE_COUNT(PhModuleProviderType); + OBJECT_TYPE_COUNT(PhModuleItemType); + OBJECT_TYPE_COUNT(PhThreadProviderType); + OBJECT_TYPE_COUNT(PhThreadItemType); + OBJECT_TYPE_COUNT(PhHandleProviderType); + OBJECT_TYPE_COUNT(PhHandleItemType); + OBJECT_TYPE_COUNT(PhMemoryItemType); + + return PhFinalStringBuilderString(&stringBuilder); +} diff --git a/ProcessHacker/actions.c b/ProcessHacker/actions.c index 36eaf7a16d21..c1865544e3a1 100644 --- a/ProcessHacker/actions.c +++ b/ProcessHacker/actions.c @@ -1,3130 +1,3243 @@ -/* - * Process Hacker - - * UI actions - * - * Copyright (C) 2010-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * These are a set of consistent functions which will perform actions on objects such as processes, - * threads and services, while displaying any necessary prompts and error messages. Automatic - * elevation can also easily be added if necessary. - */ - -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef DWORD (WINAPI *_SetTcpEntry)( - _In_ PMIB_TCPROW pTcpRow - ); - -static PWSTR DangerousProcesses[] = -{ - L"csrss.exe", L"dwm.exe", L"logonui.exe", L"lsass.exe", L"lsm.exe", - L"services.exe", L"smss.exe", L"wininit.exe", L"winlogon.exe" -}; - -static PPH_STRING DebuggerCommand = NULL; -static PH_INITONCE DebuggerCommandInitOnce = PH_INITONCE_INIT; -static ULONG PhSvcReferenceCount = 0; -static PH_PHSVC_MODE PhSvcCurrentMode; -static PH_QUEUED_LOCK PhSvcStartLock = PH_QUEUED_LOCK_INIT; - -HRESULT CALLBACK PhpElevateActionCallbackProc( - _In_ HWND hwnd, - _In_ UINT uNotification, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - switch (uNotification) - { - case TDN_CREATED: - SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE); - break; - } - - return S_OK; -} - -BOOLEAN PhpShowElevatePrompt( - _In_ HWND hWnd, - _In_ PWSTR Message, - _In_ NTSTATUS Status, - _Out_ PINT Button - ) -{ - TASKDIALOGCONFIG config = { sizeof(config) }; - TASKDIALOG_BUTTON buttons[1]; - INT button; - - // Currently the error dialog box is similar to the one displayed - // when you try to label a drive in Windows Explorer. It's much better - // than the clunky dialog in PH 1.x. - - config.hwndParent = hWnd; - config.hInstance = PhInstanceHandle; - config.dwFlags = IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0; - config.pszWindowTitle = L"Process Hacker"; - config.pszMainIcon = TD_ERROR_ICON; - config.pszMainInstruction = PhaConcatStrings2(Message, L".")->Buffer; - config.pszContent = L"You will need to provide administrator permission. " - L"Click Continue to complete this operation."; - config.dwCommonButtons = TDCBF_CANCEL_BUTTON; - - buttons[0].nButtonID = IDYES; - buttons[0].pszButtonText = L"Continue"; - - config.cButtons = 1; - config.pButtons = buttons; - config.nDefaultButton = IDYES; - - config.pfCallback = PhpElevateActionCallbackProc; - - if (TaskDialogIndirect( - &config, - &button, - NULL, - NULL - ) == S_OK) - { - *Button = button; - return TRUE; - } - else - { - return FALSE; - } -} - -/** - * Shows an error, prompts for elevation, and executes a command. - * - * \param hWnd The window to display user interface components on. - * \param Message A message describing the operation that failed. - * \param Status A NTSTATUS value. - * \param Command The arguments to pass to the new instance of - * the application, if required. - * \param Success A variable which receives TRUE if the elevated - * action succeeded or FALSE if the action failed. - * - * \return TRUE if the user was prompted for elevation, otherwise - * FALSE, in which case you need to show your own error message. - */ -BOOLEAN PhpShowErrorAndElevateAction( - _In_ HWND hWnd, - _In_ PWSTR Message, - _In_ NTSTATUS Status, - _In_ PWSTR Command, - _Out_ PBOOLEAN Success - ) -{ - PH_ACTION_ELEVATION_LEVEL elevationLevel; - INT button = IDNO; - - if (!( - Status == STATUS_ACCESS_DENIED || - Status == STATUS_PRIVILEGE_NOT_HELD || - (NT_NTWIN32(Status) && WIN32_FROM_NTSTATUS(Status) == ERROR_ACCESS_DENIED) - )) - return FALSE; - - if (!WINDOWS_HAS_UAC || PhGetOwnTokenAttributes().Elevated) - return FALSE; - - elevationLevel = PhGetIntegerSetting(L"ElevationLevel"); - - if (elevationLevel == NeverElevateAction) - return FALSE; - - if (elevationLevel == PromptElevateAction) - { - if (!PhpShowElevatePrompt(hWnd, Message, Status, &button)) - return FALSE; - } - - if (elevationLevel == AlwaysElevateAction || button == IDYES) - { - NTSTATUS status; - HANDLE processHandle; - LARGE_INTEGER timeout; - PROCESS_BASIC_INFORMATION basicInfo; - - if (PhShellProcessHacker( - hWnd, - Command, - SW_SHOW, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS, - 0, - &processHandle - )) - { - timeout.QuadPart = -10 * PH_TIMEOUT_SEC; - status = NtWaitForSingleObject(processHandle, FALSE, &timeout); - - if ( - status == STATUS_WAIT_0 && - NT_SUCCESS(status = PhGetProcessBasicInformation(processHandle, &basicInfo)) - ) - { - status = basicInfo.ExitStatus; - } - - NtClose(processHandle); - - if (NT_SUCCESS(status)) - { - *Success = TRUE; - } - else - { - *Success = FALSE; - PhShowStatus(hWnd, Message, status, 0); - } - } - } - - return TRUE; -} - -/** - * Shows an error, prompts for elevation, and connects to phsvc. - * - * \param hWnd The window to display user interface components on. - * \param Message A message describing the operation that failed. - * \param Status A NTSTATUS value. - * \param Connected A variable which receives TRUE if the user - * elevated the action and phsvc was started, or FALSE if the user - * cancelled elevation. If the value is TRUE, you need to - * perform any necessary phsvc calls and use PhUiDisconnectFromPhSvc() - * to disconnect from phsvc. - * - * \return TRUE if the user was prompted for elevation, otherwise - * FALSE, in which case you need to show your own error message. - */ -BOOLEAN PhpShowErrorAndConnectToPhSvc( - _In_ HWND hWnd, - _In_ PWSTR Message, - _In_ NTSTATUS Status, - _Out_ PBOOLEAN Connected - ) -{ - PH_ACTION_ELEVATION_LEVEL elevationLevel; - INT button = IDNO; - - *Connected = FALSE; - - if (!( - Status == STATUS_ACCESS_DENIED || - Status == STATUS_PRIVILEGE_NOT_HELD || - (NT_NTWIN32(Status) && WIN32_FROM_NTSTATUS(Status) == ERROR_ACCESS_DENIED) - )) - return FALSE; - - if (!WINDOWS_HAS_UAC || PhGetOwnTokenAttributes().Elevated) - return FALSE; - - elevationLevel = PhGetIntegerSetting(L"ElevationLevel"); - - if (elevationLevel == NeverElevateAction) - return FALSE; - - // Try to connect now so we can avoid prompting the user. - if (PhUiConnectToPhSvc(hWnd, TRUE)) - { - *Connected = TRUE; - return TRUE; - } - - if (elevationLevel == PromptElevateAction) - { - if (!PhpShowElevatePrompt(hWnd, Message, Status, &button)) - return FALSE; - } - - if (elevationLevel == AlwaysElevateAction || button == IDYES) - { - *Connected = PhUiConnectToPhSvc(hWnd, FALSE); - } - - return TRUE; -} - -/** - * Connects to phsvc. - * - * \param hWnd The window to display user interface components on. - * \param ConnectOnly TRUE to only try to connect to phsvc, otherwise - * FALSE to try to elevate and start phsvc if the initial connection - * attempt failed. - */ -BOOLEAN PhUiConnectToPhSvc( - _In_opt_ HWND hWnd, - _In_ BOOLEAN ConnectOnly - ) -{ - return PhUiConnectToPhSvcEx(hWnd, ElevatedPhSvcMode, ConnectOnly); -} - -VOID PhpGetPhSvcPortName( - _In_ PH_PHSVC_MODE Mode, - _Out_ PUNICODE_STRING PortName - ) -{ - switch (Mode) - { - case ElevatedPhSvcMode: - if (!PhIsExecutingInWow64()) - RtlInitUnicodeString(PortName, PHSVC_PORT_NAME); - else - RtlInitUnicodeString(PortName, PHSVC_WOW64_PORT_NAME); - break; - case Wow64PhSvcMode: - RtlInitUnicodeString(PortName, PHSVC_WOW64_PORT_NAME); - break; - default: - PhRaiseStatus(STATUS_INVALID_PARAMETER); - break; - } -} - -BOOLEAN PhpStartPhSvcProcess( - _In_opt_ HWND hWnd, - _In_ PH_PHSVC_MODE Mode - ) -{ - switch (Mode) - { - case ElevatedPhSvcMode: - if (PhShellProcessHacker( - hWnd, - L"-phsvc", - SW_HIDE, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS, - 0, - NULL - )) - { - return TRUE; - } - - break; - case Wow64PhSvcMode: - { - static PWSTR relativeFileNames[] = - { - L"\\x86\\ProcessHacker.exe", - L"\\..\\x86\\ProcessHacker.exe", -#ifdef DEBUG - L"\\..\\Debug32\\ProcessHacker.exe", -#endif - L"\\..\\Release32\\ProcessHacker.exe" - }; - - ULONG i; - - for (i = 0; i < sizeof(relativeFileNames) / sizeof(PWSTR); i++) - { - PPH_STRING fileName; - - fileName = PhConcatStrings2(PhApplicationDirectory->Buffer, relativeFileNames[i]); - PhMoveReference(&fileName, PhGetFullPath(fileName->Buffer, NULL)); - - if (fileName && RtlDoesFileExists_U(fileName->Buffer)) - { - if (PhShellProcessHackerEx( - hWnd, - fileName->Buffer, - L"-phsvc", - SW_HIDE, - 0, - PH_SHELL_APP_PROPAGATE_PARAMETERS, - 0, - NULL - )) - { - PhDereferenceObject(fileName); - return TRUE; - } - } - - PhClearReference(&fileName); - } - } - break; - } - - return FALSE; -} - -/** - * Connects to phsvc. - * - * \param hWnd The window to display user interface components on. - * \param Mode The type of phsvc instance to connect to. - * \param ConnectOnly TRUE to only try to connect to phsvc, otherwise - * FALSE to try to elevate and start phsvc if the initial connection - * attempt failed. - */ -BOOLEAN PhUiConnectToPhSvcEx( - _In_opt_ HWND hWnd, - _In_ PH_PHSVC_MODE Mode, - _In_ BOOLEAN ConnectOnly - ) -{ - NTSTATUS status; - BOOLEAN started; - UNICODE_STRING portName; - - if (_InterlockedIncrementNoZero(&PhSvcReferenceCount)) - { - if (PhSvcCurrentMode == Mode) - { - started = TRUE; - } - else - { - _InterlockedDecrement(&PhSvcReferenceCount); - started = FALSE; - } - } - else - { - PhAcquireQueuedLockExclusive(&PhSvcStartLock); - - if (PhSvcReferenceCount == 0) - { - started = FALSE; - PhpGetPhSvcPortName(Mode, &portName); - - // Try to connect first, then start the server if we failed. - status = PhSvcConnectToServer(&portName, 0); - - if (NT_SUCCESS(status)) - { - started = TRUE; - PhSvcCurrentMode = Mode; - _InterlockedIncrement(&PhSvcReferenceCount); - } - else if (!ConnectOnly) - { - // Prompt for elevation, and then try to connect to the server. - - if (PhpStartPhSvcProcess(hWnd, Mode)) - started = TRUE; - - if (started) - { - ULONG attempts = 10; - LARGE_INTEGER interval; - - // Try to connect several times because the server may take - // a while to initialize. - do - { - status = PhSvcConnectToServer(&portName, 0); - - if (NT_SUCCESS(status)) - break; - - interval.QuadPart = -50 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); - } while (--attempts != 0); - - // Increment the reference count even if we failed. - // We don't want to prompt the user again. - - PhSvcCurrentMode = Mode; - _InterlockedIncrement(&PhSvcReferenceCount); - } - } - } - else - { - if (PhSvcCurrentMode == Mode) - { - started = TRUE; - _InterlockedIncrement(&PhSvcReferenceCount); - } - else - { - started = FALSE; - } - } - - PhReleaseQueuedLockExclusive(&PhSvcStartLock); - } - - return started; -} - -/** - * Disconnects from phsvc. - */ -VOID PhUiDisconnectFromPhSvc( - VOID - ) -{ - PhAcquireQueuedLockExclusive(&PhSvcStartLock); - - if (_InterlockedDecrement(&PhSvcReferenceCount) == 0) - { - PhSvcDisconnectFromServer(); - } - - PhReleaseQueuedLockExclusive(&PhSvcStartLock); -} - -BOOLEAN PhUiLockComputer( - _In_ HWND hWnd - ) -{ - if (LockWorkStation()) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to lock the computer", 0, GetLastError()); - - return FALSE; -} - -BOOLEAN PhUiLogoffComputer( - _In_ HWND hWnd - ) -{ - if (ExitWindowsEx(EWX_LOGOFF, 0)) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to log off the computer", 0, GetLastError()); - - return FALSE; -} - -BOOLEAN PhUiSleepComputer( - _In_ HWND hWnd - ) -{ - NTSTATUS status; - - if (NT_SUCCESS(status = NtInitiatePowerAction( - PowerActionSleep, - PowerSystemSleeping1, - 0, - FALSE - ))) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to sleep the computer", status, 0); - - return FALSE; -} - -BOOLEAN PhUiHibernateComputer( - _In_ HWND hWnd - ) -{ - NTSTATUS status; - - if (NT_SUCCESS(status = NtInitiatePowerAction( - PowerActionHibernate, - PowerSystemSleeping1, - 0, - FALSE - ))) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to hibernate the computer", status, 0); - - return FALSE; -} - -BOOLEAN PhUiRestartComputer( - _In_ HWND hWnd, - _In_ ULONG Flags - ) -{ - if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( - hWnd, - L"restart", - L"the computer", - NULL, - FALSE - )) - { - if (ExitWindowsEx(EWX_REBOOT | Flags, 0)) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to restart the computer", 0, GetLastError()); - } - - return FALSE; -} - -BOOLEAN PhUiShutdownComputer( - _In_ HWND hWnd, - _In_ ULONG Flags - ) -{ - if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( - hWnd, - L"shut down", - L"the computer", - NULL, - FALSE - )) - { - if (ExitWindowsEx(EWX_POWEROFF | Flags, 0)) - { - return TRUE; - } - else if (ExitWindowsEx(EWX_SHUTDOWN | Flags, 0)) - { - return TRUE; - } - else - { - PhShowStatus(hWnd, L"Unable to shut down the computer", 0, GetLastError()); - } - } - - return FALSE; -} - -BOOLEAN PhUiConnectSession( - _In_ HWND hWnd, - _In_ ULONG SessionId - ) -{ - BOOLEAN success = FALSE; - PPH_STRING selectedChoice = NULL; - PPH_STRING oldSelectedChoice = NULL; - - // Try once with no password. - if (WinStationConnectW(NULL, SessionId, -1, L"", TRUE)) - return TRUE; - - while (PhaChoiceDialog( - hWnd, - L"Connect to session", - L"Password:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_PASSWORD, - &selectedChoice, - NULL, - NULL - )) - { - if (oldSelectedChoice) - { - RtlSecureZeroMemory(oldSelectedChoice->Buffer, oldSelectedChoice->Length); - PhDereferenceObject(oldSelectedChoice); - } - - oldSelectedChoice = selectedChoice; - - if (WinStationConnectW(NULL, SessionId, -1, selectedChoice->Buffer, TRUE)) - { - success = TRUE; - break; - } - else - { - if (!PhShowContinueStatus(hWnd, L"Unable to connect to the session", 0, GetLastError())) - break; - } - } - - if (oldSelectedChoice) - { - RtlSecureZeroMemory(oldSelectedChoice->Buffer, oldSelectedChoice->Length); - PhDereferenceObject(oldSelectedChoice); - } - - return success; -} - -BOOLEAN PhUiDisconnectSession( - _In_ HWND hWnd, - _In_ ULONG SessionId - ) -{ - if (WinStationDisconnect(NULL, SessionId, FALSE)) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to disconnect the session", 0, GetLastError()); - - return FALSE; -} - -BOOLEAN PhUiLogoffSession( - _In_ HWND hWnd, - _In_ ULONG SessionId - ) -{ - if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( - hWnd, - L"logoff", - L"the user", - NULL, - FALSE - )) - { - if (WinStationReset(NULL, SessionId, FALSE)) - return TRUE; - else - PhShowStatus(hWnd, L"Unable to logoff the session", 0, GetLastError()); - } - - return FALSE; -} - -/** - * Determines if a process is a system process. - * - * \param ProcessId The PID of the process to check. - */ -static BOOLEAN PhpIsDangerousProcess( - _In_ HANDLE ProcessId - ) -{ - NTSTATUS status; - HANDLE processHandle; - PPH_STRING fileName; - PPH_STRING systemDirectory; - ULONG i; - - if (ProcessId == SYSTEM_PROCESS_ID) - return TRUE; - - if (WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID) - { - status = PhGetProcessImageFileNameByProcessId(ProcessId, &fileName); - } - else - { - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - ProcessId - ))) - return FALSE; - - status = PhGetProcessImageFileName(processHandle, &fileName); - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - return FALSE; - - PhMoveReference(&fileName, PhGetFileName(fileName)); - PH_AUTO(fileName); - - systemDirectory = PH_AUTO(PhGetSystemDirectory()); - - for (i = 0; i < sizeof(DangerousProcesses) / sizeof(PWSTR); i++) - { - PPH_STRING fullName; - - fullName = PhaConcatStrings(3, systemDirectory->Buffer, L"\\", DangerousProcesses[i]); - - if (PhEqualString(fileName, fullName, TRUE)) - return TRUE; - } - - return FALSE; -} - -/** - * Checks if the user wants to proceed with an operation. - * - * \param hWnd A handle to the parent window. - * \param Verb A verb describing the action. - * \param Message A message containing additional information - * about the action. - * \param WarnOnlyIfDangerous TRUE to skip the confirmation - * dialog if none of the processes are system processes, - * FALSE to always show the confirmation dialog. - * \param Processes An array of pointers to process items. - * \param NumberOfProcesses The number of process items. - * - * \return TRUE if the user wants to proceed with the operation, - * otherwise FALSE. - */ -static BOOLEAN PhpShowContinueMessageProcesses( - _In_ HWND hWnd, - _In_ PWSTR Verb, - _In_opt_ PWSTR Message, - _In_ BOOLEAN WarnOnlyIfDangerous, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ) -{ - PWSTR object; - ULONG i; - BOOLEAN critical = FALSE; - BOOLEAN dangerous = FALSE; - BOOLEAN cont = FALSE; - - if (NumberOfProcesses == 0) - return FALSE; - - for (i = 0; i < NumberOfProcesses; i++) - { - HANDLE processHandle; - ULONG breakOnTermination; - - breakOnTermination = 0; - - if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, Processes[i]->ProcessId))) - { - NtQueryInformationProcess(processHandle, ProcessBreakOnTermination, &breakOnTermination, sizeof(ULONG), NULL); - NtClose(processHandle); - } - - if (breakOnTermination != 0) - { - critical = TRUE; - dangerous = TRUE; - break; - } - - if (PhpIsDangerousProcess(Processes[i]->ProcessId)) - { - dangerous = TRUE; - break; - } - } - - if (WarnOnlyIfDangerous && !dangerous) - return TRUE; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - if (NumberOfProcesses == 1) - { - object = Processes[0]->ProcessName->Buffer; - } - else if (NumberOfProcesses == 2) - { - object = PhaConcatStrings( - 3, - Processes[0]->ProcessName->Buffer, - L" and ", - Processes[1]->ProcessName->Buffer - )->Buffer; - } - else - { - object = L"the selected processes"; - } - - if (!dangerous) - { - cont = PhShowConfirmMessage( - hWnd, - Verb, - object, - Message, - FALSE - ); - } - else if (!critical) - { - cont = PhShowConfirmMessage( - hWnd, - Verb, - object, - PhaConcatStrings( - 3, - L"You are about to ", - Verb, - L" one or more system processes." - )->Buffer, - TRUE - ); - } - else - { - PPH_STRING message; - - if (PhEqualStringZ(Verb, L"terminate", FALSE)) - { - message = PhaConcatStrings( - 3, - L"You are about to ", - Verb, - L" one or more critical processes. This will shut down the operating system immediately." - ); - } - else - { - message = PhaConcatStrings( - 3, - L"You are about to ", - Verb, - L" one or more critical processes." - ); - } - - cont = PhShowConfirmMessage( - hWnd, - Verb, - object, - message->Buffer, - TRUE - ); - } - } - else - { - cont = TRUE; - } - - return cont; -} - -/** - * Shows an error message to the user and checks - * if the user wants to continue. - * - * \param hWnd A handle to the parent window. - * \param Verb A verb describing the action which - * resulted in an error. - * \param Process The process item which the action - * was performed on. - * \param Status A NT status value representing the - * error. - * \param Win32Result A Win32 error code representing - * the error. - * - * \return TRUE if the user wants to continue, otherwise - * FALSE. The result is typically only useful when - * executing an action on multiple processes. - */ -static BOOLEAN PhpShowErrorProcess( - _In_ HWND hWnd, - _In_ PWSTR Verb, - _In_ PPH_PROCESS_ITEM Process, - _In_ NTSTATUS Status, - _In_opt_ ULONG Win32Result - ) -{ - if (!PH_IS_FAKE_PROCESS_ID(Process->ProcessId)) - { - return PhShowContinueStatus( - hWnd, - PhaFormatString( - L"Unable to %s %s (PID %u)", - Verb, - Process->ProcessName->Buffer, - HandleToUlong(Process->ProcessId) - )->Buffer, - Status, - Win32Result - ); - } - else - { - return PhShowContinueStatus( - hWnd, - PhaFormatString( - L"Unable to %s %s", - Verb, - Process->ProcessName->Buffer - )->Buffer, - Status, - Win32Result - ); - } -} - -BOOLEAN PhUiTerminateProcesses( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - if (!PhpShowContinueMessageProcesses( - hWnd, - L"terminate", - L"Terminating a process will cause unsaved data to be lost.", - FALSE, - Processes, - NumberOfProcesses - )) - return FALSE; - - for (i = 0; i < NumberOfProcesses; i++) - { - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_TERMINATE, - Processes[i]->ProcessId - ))) - { - // An exit status of 1 is used here for compatibility reasons: - // 1. Both Task Manager and Process Explorer use 1. - // 2. winlogon tries to restart explorer.exe if the exit status is not 1. - - status = PhTerminateProcess(processHandle, 1); - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to terminate ", Processes[i]->ProcessName->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessTerminate, 0))) - success = TRUE; - else - PhpShowErrorProcess(hWnd, L"terminate", Processes[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorProcess(hWnd, L"terminate", Processes[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhpUiTerminateTreeProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process, - _In_ PVOID Processes, - _Inout_ PBOOLEAN Success - ) -{ - NTSTATUS status; - PSYSTEM_PROCESS_INFORMATION process; - HANDLE processHandle; - PPH_PROCESS_ITEM processItem; - - // Note: - // FALSE should be written to Success if any part of the operation failed. - // The return value of this function indicates whether to continue with - // the operation (FALSE if user cancelled). - - // Terminate the process. - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_TERMINATE, - Process->ProcessId - ))) - { - status = PhTerminateProcess(processHandle, 1); - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - *Success = FALSE; - - if (!PhpShowErrorProcess(hWnd, L"terminate", Process, status, 0)) - return FALSE; - } - - // Terminate the process' children. - - process = PH_FIRST_PROCESS(Processes); - - do - { - if (process->UniqueProcessId != Process->ProcessId && - process->InheritedFromUniqueProcessId == Process->ProcessId) - { - if (processItem = PhReferenceProcessItem(process->UniqueProcessId)) - { - // Check the creation time to make sure it is a descendant. - if (processItem->CreateTime.QuadPart >= Process->CreateTime.QuadPart) - { - if (!PhpUiTerminateTreeProcess(hWnd, processItem, Processes, Success)) - { - PhDereferenceObject(processItem); - return FALSE; - } - } - - PhDereferenceObject(processItem); - } - } - } while (process = PH_NEXT_PROCESS(process)); - - return TRUE; -} - -BOOLEAN PhUiTerminateTreeProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ) -{ - NTSTATUS status; - BOOLEAN success = TRUE; - BOOLEAN cont = FALSE; - PVOID processes; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - cont = PhShowConfirmMessage( - hWnd, - L"terminate", - PhaConcatStrings2(Process->ProcessName->Buffer, L" and its descendants")->Buffer, - L"Terminating a process tree will cause the process and its descendants to be terminated.", - FALSE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) - { - PhShowStatus(hWnd, L"Unable to enumerate processes", status, 0); - return FALSE; - } - - PhpUiTerminateTreeProcess(hWnd, Process, processes, &success); - PhFree(processes); - - return success; -} - -BOOLEAN PhUiSuspendProcesses( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - if (!PhpShowContinueMessageProcesses( - hWnd, - L"suspend", - NULL, - TRUE, - Processes, - NumberOfProcesses - )) - return FALSE; - - for (i = 0; i < NumberOfProcesses; i++) - { - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SUSPEND_RESUME, - Processes[i]->ProcessId - ))) - { - status = NtSuspendProcess(processHandle); - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to suspend ", Processes[i]->ProcessName->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessSuspend, 0))) - success = TRUE; - else - PhpShowErrorProcess(hWnd, L"suspend", Processes[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorProcess(hWnd, L"suspend", Processes[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhUiResumeProcesses( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - if (!PhpShowContinueMessageProcesses( - hWnd, - L"resume", - NULL, - TRUE, - Processes, - NumberOfProcesses - )) - return FALSE; - - for (i = 0; i < NumberOfProcesses; i++) - { - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SUSPEND_RESUME, - Processes[i]->ProcessId - ))) - { - status = NtResumeProcess(processHandle); - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to resume ", Processes[i]->ProcessName->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessResume, 0))) - success = TRUE; - else - PhpShowErrorProcess(hWnd, L"resume", Processes[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorProcess(hWnd, L"resume", Processes[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhUiRestartProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - HANDLE processHandle = NULL; - PPH_STRING commandLine; - PPH_STRING currentDirectory; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - cont = PhShowConfirmMessage( - hWnd, - L"restart", - Process->ProcessName->Buffer, - L"The process will be restarted with the same command line and " - L"working directory, but if it is running under a different user it " - L"will be restarted under the current user.", - FALSE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - // Open the process and get the command line and current directory. - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_VM_READ, - Process->ProcessId - ))) - goto ErrorExit; - - if (!NT_SUCCESS(status = PhGetProcessCommandLine( - processHandle, - &commandLine - ))) - goto ErrorExit; - - PH_AUTO(commandLine); - - if (!NT_SUCCESS(status = PhGetProcessPebString( - processHandle, - PhpoCurrentDirectory, - ¤tDirectory - ))) - goto ErrorExit; - - PH_AUTO(currentDirectory); - - NtClose(processHandle); - processHandle = NULL; - - // Open the process and terminate it. - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_TERMINATE, - Process->ProcessId - ))) - goto ErrorExit; - - if (!NT_SUCCESS(status = PhTerminateProcess( - processHandle, - 1 - ))) - goto ErrorExit; - - NtClose(processHandle); - processHandle = NULL; - - // Start the process. - - status = PhCreateProcessWin32( - PhGetString(Process->FileName), // we didn't wait for S1 processing - commandLine->Buffer, - NULL, - currentDirectory->Buffer, - 0, - NULL, - NULL, - NULL - ); - -ErrorExit: - if (processHandle) - NtClose(processHandle); - - if (!NT_SUCCESS(status)) - { - PhpShowErrorProcess(hWnd, L"restart", Process, status, 0); - return FALSE; - } - - return TRUE; -} - -// Contributed by evilpie (#2981421) -BOOLEAN PhUiDebugProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - PH_STRING_BUILDER commandLineBuilder; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - cont = PhShowConfirmMessage( - hWnd, - L"debug", - Process->ProcessName->Buffer, - L"Debugging a process may result in loss of data.", - FALSE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - if (PhBeginInitOnce(&DebuggerCommandInitOnce)) - { - static PH_STRINGREF aeDebugKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"); - - HANDLE keyHandle; - PPH_STRING debugger; - PH_STRINGREF commandPart; - PH_STRINGREF dummy; - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &aeDebugKeyName, - 0 - ))) - { - if (debugger = PH_AUTO(PhQueryRegistryString(keyHandle, L"Debugger"))) - { - if (PhSplitStringRefAtChar(&debugger->sr, '"', &dummy, &commandPart) && - PhSplitStringRefAtChar(&commandPart, '"', &commandPart, &dummy)) - { - DebuggerCommand = PhCreateString2(&commandPart); - } - } - - NtClose(keyHandle); - } - - PhEndInitOnce(&DebuggerCommandInitOnce); - } - - if (!DebuggerCommand) - { - PhShowError(hWnd, L"Unable to locate the debugger."); - return FALSE; - } - - PhInitializeStringBuilder(&commandLineBuilder, DebuggerCommand->Length + 30); - - PhAppendCharStringBuilder(&commandLineBuilder, '"'); - PhAppendStringBuilder(&commandLineBuilder, &DebuggerCommand->sr); - PhAppendCharStringBuilder(&commandLineBuilder, '"'); - PhAppendFormatStringBuilder(&commandLineBuilder, L" -p %u", HandleToUlong(Process->ProcessId)); - - status = PhCreateProcessWin32( - NULL, - commandLineBuilder.String->Buffer, - NULL, - NULL, - 0, - NULL, - NULL, - NULL - ); - - PhDeleteStringBuilder(&commandLineBuilder); - - if (!NT_SUCCESS(status)) - { - PhpShowErrorProcess(hWnd, L"debug", Process, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiReduceWorkingSetProcesses( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ) -{ - BOOLEAN success = TRUE; - ULONG i; - - for (i = 0; i < NumberOfProcesses; i++) - { - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SET_QUOTA, - Processes[i]->ProcessId - ))) - { - QUOTA_LIMITS quotaLimits; - - memset("aLimits, 0, sizeof(QUOTA_LIMITS)); - quotaLimits.MinimumWorkingSetSize = -1; - quotaLimits.MaximumWorkingSetSize = -1; - - status = NtSetInformationProcess( - processHandle, - ProcessQuotaLimits, - "aLimits, - sizeof(QUOTA_LIMITS) - ); - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - success = FALSE; - - if (!PhpShowErrorProcess(hWnd, L"reduce the working set of", Processes[i], status, 0)) - break; - } - } - - return success; -} - -BOOLEAN PhUiSetVirtualizationProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process, - _In_ BOOLEAN Enable - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - HANDLE processHandle; - HANDLE tokenHandle; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - cont = PhShowConfirmMessage( - hWnd, - L"set", - L"virtualization for the process", - L"Enabling or disabling virtualization for a process may " - L"alter its functionality and produce undesirable effects.", - FALSE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - Process->ProcessId - ))) - { - if (NT_SUCCESS(status = PhOpenProcessToken( - processHandle, - TOKEN_WRITE, - &tokenHandle - ))) - { - status = PhSetTokenIsVirtualizationEnabled(tokenHandle, Enable); - NtClose(tokenHandle); - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorProcess(hWnd, L"set virtualization for", Process, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiDetachFromDebuggerProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ) -{ - NTSTATUS status; - HANDLE processHandle; - HANDLE debugObjectHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME, - Process->ProcessId - ))) - { - if (NT_SUCCESS(status = PhGetProcessDebugObject( - processHandle, - &debugObjectHandle - ))) - { - ULONG flags; - - // Disable kill-on-close. - flags = 0; - NtSetInformationDebugObject( - debugObjectHandle, - DebugObjectFlags, - &flags, - sizeof(ULONG), - NULL - ); - - status = NtRemoveProcessDebug(processHandle, debugObjectHandle); - - NtClose(debugObjectHandle); - } - - NtClose(processHandle); - } - - if (status == STATUS_PORT_NOT_SET) - { - PhShowInformation(hWnd, L"The process is not being debugged."); - return FALSE; - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorProcess(hWnd, L"detach debugger from", Process, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiInjectDllProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ) -{ - static PH_FILETYPE_FILTER filters[] = - { - { L"DLL files (*.dll)", L"*.dll" }, - { L"All files (*.*)", L"*.*" } - }; - - NTSTATUS status; - PVOID fileDialog; - PPH_STRING fileName; - HANDLE processHandle; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - if (!PhShowFileDialog(hWnd, fileDialog)) - { - PhFreeFileDialog(fileDialog); - return FALSE; - } - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - PhFreeFileDialog(fileDialog); - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | - PROCESS_VM_READ | PROCESS_VM_WRITE, - Process->ProcessId - ))) - { - LARGE_INTEGER timeout; - - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - status = PhInjectDllProcess( - processHandle, - fileName->Buffer, - &timeout - ); - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorProcess(hWnd, L"inject the DLL into", Process, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiSetIoPriorityProcesses( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses, - _In_ IO_PRIORITY_HINT IoPriority - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - for (i = 0; i < NumberOfProcesses; i++) - { - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - Processes[i]->ProcessId - ))) - { - if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID) - { - status = PhSetProcessIoPriority(processHandle, IoPriority); - } - else - { - // See comment in PhUiSetPriorityProcesses. - status = STATUS_UNSUCCESSFUL; - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - // The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege. - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to set the I/O priority of ", Processes[i]->ProcessName->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessIoPriority, IoPriority))) - success = TRUE; - else - PhpShowErrorProcess(hWnd, L"set the I/O priority of", Processes[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorProcess(hWnd, L"set the I/O priority of", Processes[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhUiSetPagePriorityProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process, - _In_ ULONG PagePriority - ) -{ - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - Process->ProcessId - ))) - { - if (Process->ProcessId != SYSTEM_PROCESS_ID) - { - status = NtSetInformationProcess( - processHandle, - ProcessPagePriority, - &PagePriority, - sizeof(ULONG) - ); - } - else - { - // See comment in PhUiSetPriorityProcesses. - status = STATUS_UNSUCCESSFUL; - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorProcess(hWnd, L"set the page priority of", Process, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiSetPriorityProcesses( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses, - _In_ ULONG PriorityClass - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - for (i = 0; i < NumberOfProcesses; i++) - { - NTSTATUS status; - HANDLE processHandle; - PROCESS_PRIORITY_CLASS priorityClass; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - Processes[i]->ProcessId - ))) - { - if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID) - { - priorityClass.Foreground = FALSE; - priorityClass.PriorityClass = (UCHAR)PriorityClass; - status = NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); - } - else - { - // Changing the priority of System can lead to a BSOD on some versions of Windows, - // so disallow this. - status = STATUS_UNSUCCESSFUL; - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - // The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege. - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to set the priority of ", Processes[i]->ProcessName->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessPriority, PriorityClass))) - success = TRUE; - else - PhpShowErrorProcess(hWnd, L"set the priority of", Processes[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorProcess(hWnd, L"set the priority of", Processes[i], status, 0)) - break; - } - } - } - - return success; -} - -static VOID PhpShowErrorService( - _In_ HWND hWnd, - _In_ PWSTR Verb, - _In_ PPH_SERVICE_ITEM Service, - _In_ NTSTATUS Status, - _In_opt_ ULONG Win32Result - ) -{ - PhShowStatus( - hWnd, - PhaFormatString( - L"Unable to %s %s", - Verb, - Service->Name->Buffer - )->Buffer, - Status, - Win32Result - ); -} - -BOOLEAN PhUiStartService( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM Service - ) -{ - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; - - serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_START); - - if (serviceHandle) - { - if (StartService(serviceHandle, 0, NULL)) - success = TRUE; - - CloseServiceHandle(serviceHandle); - } - - if (!success) - { - NTSTATUS status; - BOOLEAN connected; - - status = PhGetLastWin32ErrorAsNtStatus(); - - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to start ", Service->Name->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceStart))) - success = TRUE; - else - PhpShowErrorService(hWnd, L"start", Service, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhpShowErrorService(hWnd, L"start", Service, status, 0); - } - } - - return success; -} - -BOOLEAN PhUiContinueService( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM Service - ) -{ - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; - - serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_PAUSE_CONTINUE); - - if (serviceHandle) - { - SERVICE_STATUS serviceStatus; - - if (ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus)) - success = TRUE; - - CloseServiceHandle(serviceHandle); - } - - if (!success) - { - NTSTATUS status; - BOOLEAN connected; - - status = PhGetLastWin32ErrorAsNtStatus(); - - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to continue ", Service->Name->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceContinue))) - success = TRUE; - else - PhpShowErrorService(hWnd, L"continue", Service, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhpShowErrorService(hWnd, L"continue", Service, status, 0); - } - } - - return success; -} - -BOOLEAN PhUiPauseService( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM Service - ) -{ - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; - - serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_PAUSE_CONTINUE); - - if (serviceHandle) - { - SERVICE_STATUS serviceStatus; - - if (ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus)) - success = TRUE; - - CloseServiceHandle(serviceHandle); - } - - if (!success) - { - NTSTATUS status; - BOOLEAN connected; - - status = PhGetLastWin32ErrorAsNtStatus(); - - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to pause ", Service->Name->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServicePause))) - success = TRUE; - else - PhpShowErrorService(hWnd, L"pause", Service, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhpShowErrorService(hWnd, L"pause", Service, status, 0); - } - } - - return success; -} - -BOOLEAN PhUiStopService( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM Service - ) -{ - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; - - serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_STOP); - - if (serviceHandle) - { - SERVICE_STATUS serviceStatus; - - if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus)) - success = TRUE; - - CloseServiceHandle(serviceHandle); - } - - if (!success) - { - NTSTATUS status; - BOOLEAN connected; - - status = PhGetLastWin32ErrorAsNtStatus(); - - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to stop ", Service->Name->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceStop))) - success = TRUE; - else - PhpShowErrorService(hWnd, L"stop", Service, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhpShowErrorService(hWnd, L"stop", Service, status, 0); - } - } - - return success; -} - -BOOLEAN PhUiDeleteService( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM Service - ) -{ - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; - - // Warnings cannot be disabled for service deletion. - if (!PhShowConfirmMessage( - hWnd, - L"delete", - Service->Name->Buffer, - L"Deleting a service can prevent the system from starting " - L"or functioning properly.", - TRUE - )) - return FALSE; - - serviceHandle = PhOpenService(Service->Name->Buffer, DELETE); - - if (serviceHandle) - { - if (DeleteService(serviceHandle)) - success = TRUE; - - CloseServiceHandle(serviceHandle); - } - - if (!success) - { - NTSTATUS status; - BOOLEAN connected; - - status = PhGetLastWin32ErrorAsNtStatus(); - - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to delete ", Service->Name->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceDelete))) - success = TRUE; - else - PhpShowErrorService(hWnd, L"delete", Service, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhpShowErrorService(hWnd, L"delete", Service, status, 0); - } - } - - return success; -} - -BOOLEAN PhUiCloseConnections( - _In_ HWND hWnd, - _In_ PPH_NETWORK_ITEM *Connections, - _In_ ULONG NumberOfConnections - ) -{ - - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG result; - ULONG i; - _SetTcpEntry SetTcpEntry_I; - MIB_TCPROW tcpRow; - - SetTcpEntry_I = PhGetModuleProcAddress(L"iphlpapi.dll", "SetTcpEntry"); - - if (!SetTcpEntry_I) - { - PhShowError( - hWnd, - L"This feature is not supported by your operating system." - ); - return FALSE; - } - - for (i = 0; i < NumberOfConnections; i++) - { - if ( - Connections[i]->ProtocolType != PH_TCP4_NETWORK_PROTOCOL || - Connections[i]->State != MIB_TCP_STATE_ESTAB - ) - continue; - - tcpRow.dwState = MIB_TCP_STATE_DELETE_TCB; - tcpRow.dwLocalAddr = Connections[i]->LocalEndpoint.Address.Ipv4; - tcpRow.dwLocalPort = _byteswap_ushort((USHORT)Connections[i]->LocalEndpoint.Port); - tcpRow.dwRemoteAddr = Connections[i]->RemoteEndpoint.Address.Ipv4; - tcpRow.dwRemotePort = _byteswap_ushort((USHORT)Connections[i]->RemoteEndpoint.Port); - - if ((result = SetTcpEntry_I(&tcpRow)) != 0) - { - NTSTATUS status; - BOOLEAN connected; - - success = FALSE; - - // SetTcpEntry returns ERROR_MR_MID_NOT_FOUND for access denied errors for some reason. - if (result == ERROR_MR_MID_NOT_FOUND) - result = ERROR_ACCESS_DENIED; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - L"Unable to close the TCP connection", - NTSTATUS_FROM_WIN32(result), - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallSetTcpEntry(&tcpRow))) - success = TRUE; - else - PhShowStatus(hWnd, L"Unable to close the TCP connection", status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (PhShowMessage( - hWnd, - MB_ICONERROR | MB_OKCANCEL, - L"Unable to close the TCP connection (from %s:%u). " - L"Make sure Process Hacker is running with administrative privileges.", - Connections[i]->LocalAddressString, - Connections[i]->LocalEndpoint.Port - ) != IDOK) - break; - } - } - } - - return success; -} - -static BOOLEAN PhpShowContinueMessageThreads( - _In_ HWND hWnd, - _In_ PWSTR Verb, - _In_ PWSTR Message, - _In_ BOOLEAN Warning, - _In_ PPH_THREAD_ITEM *Threads, - _In_ ULONG NumberOfThreads - ) -{ - PWSTR object; - BOOLEAN cont = FALSE; - - if (NumberOfThreads == 0) - return FALSE; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - if (NumberOfThreads == 1) - { - object = L"the selected thread"; - } - else - { - object = L"the selected threads"; - } - - cont = PhShowConfirmMessage( - hWnd, - Verb, - object, - Message, - Warning - ); - } - else - { - cont = TRUE; - } - - return cont; -} - -static BOOLEAN PhpShowErrorThread( - _In_ HWND hWnd, - _In_ PWSTR Verb, - _In_ PPH_THREAD_ITEM Thread, - _In_ NTSTATUS Status, - _In_opt_ ULONG Win32Result - ) -{ - return PhShowContinueStatus( - hWnd, - PhaFormatString( - L"Unable to %s thread %u", - Verb, - HandleToUlong(Thread->ThreadId) - )->Buffer, - Status, - Win32Result - ); -} - -BOOLEAN PhUiTerminateThreads( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM *Threads, - _In_ ULONG NumberOfThreads - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - if (!PhpShowContinueMessageThreads( - hWnd, - L"terminate", - L"Terminating a thread may cause the process to stop working.", - FALSE, - Threads, - NumberOfThreads - )) - return FALSE; - - for (i = 0; i < NumberOfThreads; i++) - { - NTSTATUS status; - HANDLE threadHandle; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - THREAD_TERMINATE, - Threads[i]->ThreadId - ))) - { - status = NtTerminateThread(threadHandle, STATUS_SUCCESS); - NtClose(threadHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaFormatString(L"Unable to terminate thread %u", HandleToUlong(Threads[i]->ThreadId))->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadTerminate, 0))) - success = TRUE; - else - PhpShowErrorThread(hWnd, L"terminate", Threads[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorThread(hWnd, L"terminate", Threads[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhUiSuspendThreads( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM *Threads, - _In_ ULONG NumberOfThreads - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - for (i = 0; i < NumberOfThreads; i++) - { - NTSTATUS status; - HANDLE threadHandle; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - THREAD_SUSPEND_RESUME, - Threads[i]->ThreadId - ))) - { - status = NtSuspendThread(threadHandle, NULL); - NtClose(threadHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaFormatString(L"Unable to suspend thread %u", HandleToUlong(Threads[i]->ThreadId))->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadSuspend, 0))) - success = TRUE; - else - PhpShowErrorThread(hWnd, L"suspend", Threads[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorThread(hWnd, L"suspend", Threads[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhUiResumeThreads( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM *Threads, - _In_ ULONG NumberOfThreads - ) -{ - BOOLEAN success = TRUE; - BOOLEAN cancelled = FALSE; - ULONG i; - - for (i = 0; i < NumberOfThreads; i++) - { - NTSTATUS status; - HANDLE threadHandle; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - THREAD_SUSPEND_RESUME, - Threads[i]->ThreadId - ))) - { - status = NtResumeThread(threadHandle, NULL); - NtClose(threadHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - if (!cancelled && PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaFormatString(L"Unable to resume thread %u", HandleToUlong(Threads[i]->ThreadId))->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadResume, 0))) - success = TRUE; - else - PhpShowErrorThread(hWnd, L"resume", Threads[i], status, 0); - - PhUiDisconnectFromPhSvc(); - } - else - { - cancelled = TRUE; - } - } - else - { - if (!PhpShowErrorThread(hWnd, L"resume", Threads[i], status, 0)) - break; - } - } - } - - return success; -} - -BOOLEAN PhUiSetPriorityThread( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM Thread, - _In_ LONG Increment - ) -{ - NTSTATUS status; - HANDLE threadHandle; - - // Special saturation values - if (Increment == THREAD_PRIORITY_TIME_CRITICAL) - Increment = THREAD_BASE_PRIORITY_LOWRT + 1; - else if (Increment == THREAD_PRIORITY_IDLE) - Increment = THREAD_BASE_PRIORITY_IDLE - 1; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - ThreadSetAccess, - Thread->ThreadId - ))) - { - status = NtSetInformationThread(threadHandle, ThreadBasePriority, &Increment, sizeof(LONG)); - NtClose(threadHandle); - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorThread(hWnd, L"set the priority of", Thread, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiSetIoPriorityThread( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM Thread, - _In_ IO_PRIORITY_HINT IoPriority - ) -{ - NTSTATUS status; - BOOLEAN success = TRUE; - HANDLE threadHandle; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - THREAD_SET_INFORMATION, - Thread->ThreadId - ))) - { - status = PhSetThreadIoPriority(threadHandle, IoPriority); - NtClose(threadHandle); - } - - if (!NT_SUCCESS(status)) - { - BOOLEAN connected; - - success = FALSE; - - // The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege. - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaFormatString(L"Unable to set the I/O priority of thread %u", HandleToUlong(Thread->ThreadId))->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallControlThread(Thread->ThreadId, PhSvcControlThreadIoPriority, IoPriority))) - success = TRUE; - else - PhpShowErrorThread(hWnd, L"set the I/O priority of", Thread, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhpShowErrorThread(hWnd, L"set the I/O priority of", Thread, status, 0); - } - } - - return success; -} - -BOOLEAN PhUiSetPagePriorityThread( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM Thread, - _In_ ULONG PagePriority - ) -{ - NTSTATUS status; - HANDLE threadHandle; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - THREAD_SET_INFORMATION, - Thread->ThreadId - ))) - { - status = NtSetInformationThread( - threadHandle, - ThreadPagePriority, - &PagePriority, - sizeof(ULONG) - ); - NtClose(threadHandle); - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorThread(hWnd, L"set the page priority of", Thread, status, 0); - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiUnloadModule( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ PPH_MODULE_ITEM Module - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - HANDLE processHandle; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - PWSTR verb; - PWSTR message; - - switch (Module->Type) - { - case PH_MODULE_TYPE_MODULE: - case PH_MODULE_TYPE_WOW64_MODULE: - verb = L"unload"; - message = L"Unloading a module may cause the process to crash."; - - if (WindowsVersion >= WINDOWS_8) - message = L"Unloading a module may cause the process to crash. NOTE: This feature may not work correctly on your version of Windows."; - - break; - case PH_MODULE_TYPE_KERNEL_MODULE: - verb = L"unload"; - message = L"Unloading a driver may cause system instability."; - break; - case PH_MODULE_TYPE_MAPPED_FILE: - case PH_MODULE_TYPE_MAPPED_IMAGE: - verb = L"unmap"; - message = L"Unmapping a section view may cause the process to crash."; - break; - default: - return FALSE; - } - - cont = PhShowConfirmMessage( - hWnd, - verb, - Module->Name->Buffer, - message, - TRUE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - switch (Module->Type) - { - case PH_MODULE_TYPE_MODULE: - case PH_MODULE_TYPE_WOW64_MODULE: - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | - PROCESS_VM_READ | PROCESS_VM_WRITE, - ProcessId - ))) - { - LARGE_INTEGER timeout; - - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - status = PhUnloadDllProcess( - processHandle, - Module->BaseAddress, - &timeout - ); - - NtClose(processHandle); - } - - if (status == STATUS_DLL_NOT_FOUND) - { - PhShowError(hWnd, L"Unable to find the module to unload."); - return FALSE; - } - - if (!NT_SUCCESS(status)) - { - PhShowStatus( - hWnd, - PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, - status, - 0 - ); - return FALSE; - } - - break; - - case PH_MODULE_TYPE_KERNEL_MODULE: - status = PhUnloadDriver(Module->BaseAddress, Module->Name->Buffer); - - if (!NT_SUCCESS(status)) - { - BOOLEAN success = FALSE; - BOOLEAN connected; - - if (PhpShowErrorAndConnectToPhSvc( - hWnd, - PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, - status, - &connected - )) - { - if (connected) - { - if (NT_SUCCESS(status = PhSvcCallUnloadDriver(Module->BaseAddress, Module->Name->Buffer))) - success = TRUE; - else - PhShowStatus(hWnd, PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, status, 0); - - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhShowStatus( - hWnd, - PhaConcatStrings( - 3, - L"Unable to unload ", - Module->Name->Buffer, - L". Make sure Process Hacker is running with " - L"administrative privileges. Error" - )->Buffer, - status, - 0 - ); - return FALSE; - } - - return success; - } - - break; - - case PH_MODULE_TYPE_MAPPED_FILE: - case PH_MODULE_TYPE_MAPPED_IMAGE: - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_VM_OPERATION, - ProcessId - ))) - { - status = NtUnmapViewOfSection(processHandle, Module->BaseAddress); - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - PhShowStatus( - hWnd, - PhaFormatString(L"Unable to unmap the section view at 0x%Ix", Module->BaseAddress)->Buffer, - status, - 0 - ); - return FALSE; - } - - break; - - default: - return FALSE; - } - - return TRUE; -} - -BOOLEAN PhUiFreeMemory( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ PPH_MEMORY_ITEM MemoryItem, - _In_ BOOLEAN Free - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - HANDLE processHandle; - - if (PhGetIntegerSetting(L"EnableWarnings")) - { - PWSTR verb; - PWSTR message; - - if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE))) - { - if (Free) - { - verb = L"free"; - message = L"Freeing memory regions may cause the process to crash."; - } - else - { - verb = L"decommit"; - message = L"Decommitting memory regions may cause the process to crash."; - } - } - else - { - verb = L"unmap"; - message = L"Unmapping a section view may cause the process to crash."; - } - - cont = PhShowConfirmMessage( - hWnd, - verb, - L"the memory region", - message, - TRUE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_VM_OPERATION, - ProcessId - ))) - { - PVOID baseAddress; - SIZE_T regionSize; - - baseAddress = MemoryItem->BaseAddress; - - if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE))) - { - // The size needs to be 0 if we're freeing. - if (Free) - regionSize = 0; - else - regionSize = MemoryItem->RegionSize; - - status = NtFreeVirtualMemory( - processHandle, - &baseAddress, - ®ionSize, - Free ? MEM_RELEASE : MEM_DECOMMIT - ); - } - else - { - status = NtUnmapViewOfSection(processHandle, baseAddress); - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - PWSTR message; - - if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE))) - { - if (Free) - message = L"Unable to free the memory region"; - else - message = L"Unable to decommit the memory region"; - } - else - { - message = L"Unable to unmap the section view"; - } - - PhShowStatus( - hWnd, - message, - status, - 0 - ); - return FALSE; - } - - return TRUE; -} - -static BOOLEAN PhpShowErrorHandle( - _In_ HWND hWnd, - _In_ PWSTR Verb, - _In_ PPH_HANDLE_ITEM Handle, - _In_ NTSTATUS Status, - _In_opt_ ULONG Win32Result - ) -{ - if (!PhIsNullOrEmptyString(Handle->BestObjectName)) - { - return PhShowContinueStatus( - hWnd, - PhaFormatString( - L"Unable to %s handle \"%s\" (0x%Ix)", - Verb, - Handle->BestObjectName->Buffer, - HandleToUlong(Handle->Handle) - )->Buffer, - Status, - Win32Result - ); - } - else - { - return PhShowContinueStatus( - hWnd, - PhaFormatString( - L"Unable to %s handle 0x%Ix", - Verb, - HandleToUlong(Handle->Handle) - )->Buffer, - Status, - Win32Result - ); - } -} - -BOOLEAN PhUiCloseHandles( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ PPH_HANDLE_ITEM *Handles, - _In_ ULONG NumberOfHandles, - _In_ BOOLEAN Warn - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - BOOLEAN success = TRUE; - HANDLE processHandle; - - if (NumberOfHandles == 0) - return FALSE; - - if (Warn && PhGetIntegerSetting(L"EnableWarnings")) - { - cont = PhShowConfirmMessage( - hWnd, - L"close", - NumberOfHandles == 1 ? L"the selected handle" : L"the selected handles", - L"Closing handles may cause system instability and data corruption.", - FALSE - ); - } - else - { - cont = TRUE; - } - - if (!cont) - return FALSE; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_DUP_HANDLE, - ProcessId - ))) - { - ULONG i; - - for (i = 0; i < NumberOfHandles; i++) - { - status = NtDuplicateObject( - processHandle, - Handles[i]->Handle, - NULL, - NULL, - 0, - 0, - DUPLICATE_CLOSE_SOURCE - ); - - if (!NT_SUCCESS(status)) - { - success = FALSE; - - if (!PhpShowErrorHandle( - hWnd, - L"close", - Handles[i], - status, - 0 - )) - break; - } - } - - NtClose(processHandle); - } - else - { - PhShowStatus(hWnd, L"Unable to open the process", status, 0); - return FALSE; - } - - return success; -} - -BOOLEAN PhUiSetAttributesHandle( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ PPH_HANDLE_ITEM Handle, - _In_ ULONG Attributes - ) -{ - NTSTATUS status; - HANDLE processHandle; - - if (!KphIsConnected()) - { - PhShowError(hWnd, PH_KPH_ERROR_MESSAGE); - return FALSE; - } - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - ProcessId - ))) - { - OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; - - handleFlagInfo.Inherit = !!(Attributes & OBJ_INHERIT); - handleFlagInfo.ProtectFromClose = !!(Attributes & OBJ_PROTECT_CLOSE); - - status = KphSetInformationObject( - processHandle, - Handle->Handle, - KphObjectHandleFlagInformation, - &handleFlagInfo, - sizeof(OBJECT_HANDLE_FLAG_INFORMATION) - ); - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - PhpShowErrorHandle(hWnd, L"set attributes of", Handle, status, 0); - return FALSE; - } - - return TRUE; -} +/* + * Process Hacker - + * UI actions + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * These are a set of consistent functions which will perform actions on objects such as processes, + * threads and services, while displaying any necessary prompts and error messages. Automatic + * elevation can also easily be added if necessary. + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static PWSTR DangerousProcesses[] = +{ + L"csrss.exe", L"dwm.exe", L"logonui.exe", L"lsass.exe", L"lsm.exe", + L"services.exe", L"smss.exe", L"wininit.exe", L"winlogon.exe" +}; + +static PPH_STRING DebuggerCommand = NULL; +static ULONG PhSvcReferenceCount = 0; +static PH_PHSVC_MODE PhSvcCurrentMode; +static PH_QUEUED_LOCK PhSvcStartLock = PH_QUEUED_LOCK_INIT; + +HRESULT CALLBACK PhpElevateActionCallbackProc( + _In_ HWND hwnd, + _In_ UINT uNotification, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + switch (uNotification) + { + case TDN_CREATED: + SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE); + break; + } + + return S_OK; +} + +BOOLEAN PhpShowElevatePrompt( + _In_ HWND hWnd, + _In_ PWSTR Message, + _Out_ PINT Button + ) +{ + TASKDIALOGCONFIG config = { sizeof(config) }; + TASKDIALOG_BUTTON buttons[1]; + INT button; + + // Currently the error dialog box is similar to the one displayed + // when you try to label a drive in Windows Explorer. It's much better + // than the clunky dialog in PH 1.x. + + config.hwndParent = hWnd; + config.hInstance = PhInstanceHandle; + config.dwFlags = IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0; + config.pszWindowTitle = PhApplicationName; + config.pszMainIcon = TD_ERROR_ICON; + config.pszMainInstruction = PhaConcatStrings2(Message, L".")->Buffer; + config.pszContent = L"You will need to provide administrator permission. " + L"Click Continue to complete this operation."; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + + buttons[0].nButtonID = IDYES; + buttons[0].pszButtonText = L"Continue"; + + config.cButtons = 1; + config.pButtons = buttons; + config.nDefaultButton = IDYES; + + config.pfCallback = PhpElevateActionCallbackProc; + + if (TaskDialogIndirect( + &config, + &button, + NULL, + NULL + ) == S_OK) + { + *Button = button; + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * Shows an error, prompts for elevation, and executes a command. + * + * \param hWnd The window to display user interface components on. + * \param Message A message describing the operation that failed. + * \param Status A NTSTATUS value. + * \param Command The arguments to pass to the new instance of + * the application, if required. + * \param Success A variable which receives TRUE if the elevated + * action succeeded or FALSE if the action failed. + * + * \return TRUE if the user was prompted for elevation, otherwise + * FALSE, in which case you need to show your own error message. + */ +BOOLEAN PhpShowErrorAndElevateAction( + _In_ HWND hWnd, + _In_ PWSTR Message, + _In_ NTSTATUS Status, + _In_ PWSTR Command, + _Out_ PBOOLEAN Success + ) +{ + PH_ACTION_ELEVATION_LEVEL elevationLevel; + INT button = IDNO; + + if (!( + Status == STATUS_ACCESS_DENIED || + Status == STATUS_PRIVILEGE_NOT_HELD || + (NT_NTWIN32(Status) && WIN32_FROM_NTSTATUS(Status) == ERROR_ACCESS_DENIED) + )) + return FALSE; + + if (PhGetOwnTokenAttributes().Elevated) + return FALSE; + + elevationLevel = PhGetIntegerSetting(L"ElevationLevel"); + + if (elevationLevel == NeverElevateAction) + return FALSE; + + if (elevationLevel == PromptElevateAction) + { + if (!PhpShowElevatePrompt(hWnd, Message, &button)) + return FALSE; + } + + if (elevationLevel == AlwaysElevateAction || button == IDYES) + { + NTSTATUS status; + HANDLE processHandle; + LARGE_INTEGER timeout; + PROCESS_BASIC_INFORMATION basicInfo; + + if (PhShellProcessHacker( + hWnd, + Command, + SW_SHOW, + PH_SHELL_EXECUTE_ADMIN, + PH_SHELL_APP_PROPAGATE_PARAMETERS, + 0, + &processHandle + )) + { + timeout.QuadPart = -(LONGLONG)UInt32x32To64(10, PH_TIMEOUT_SEC); + status = NtWaitForSingleObject(processHandle, FALSE, &timeout); + + if ( + status == STATUS_WAIT_0 && + NT_SUCCESS(status = PhGetProcessBasicInformation(processHandle, &basicInfo)) + ) + { + status = basicInfo.ExitStatus; + } + + NtClose(processHandle); + + if (NT_SUCCESS(status)) + { + *Success = TRUE; + } + else + { + *Success = FALSE; + PhShowStatus(hWnd, Message, status, 0); + } + } + } + + return TRUE; +} + +/** + * Shows an error, prompts for elevation, and connects to phsvc. + * + * \param hWnd The window to display user interface components on. + * \param Message A message describing the operation that failed. + * \param Status A NTSTATUS value. + * \param Connected A variable which receives TRUE if the user + * elevated the action and phsvc was started, or FALSE if the user + * cancelled elevation. If the value is TRUE, you need to + * perform any necessary phsvc calls and use PhUiDisconnectFromPhSvc() + * to disconnect from phsvc. + * + * \return TRUE if the user was prompted for elevation, otherwise + * FALSE, in which case you need to show your own error message. + */ +BOOLEAN PhpShowErrorAndConnectToPhSvc( + _In_ HWND hWnd, + _In_ PWSTR Message, + _In_ NTSTATUS Status, + _Out_ PBOOLEAN Connected + ) +{ + PH_ACTION_ELEVATION_LEVEL elevationLevel; + INT button = IDNO; + + *Connected = FALSE; + + if (!( + Status == STATUS_ACCESS_DENIED || + Status == STATUS_PRIVILEGE_NOT_HELD || + (NT_NTWIN32(Status) && WIN32_FROM_NTSTATUS(Status) == ERROR_ACCESS_DENIED) + )) + return FALSE; + + if (PhGetOwnTokenAttributes().Elevated) + return FALSE; + + elevationLevel = PhGetIntegerSetting(L"ElevationLevel"); + + if (elevationLevel == NeverElevateAction) + return FALSE; + + // Try to connect now so we can avoid prompting the user. + if (PhUiConnectToPhSvc(hWnd, TRUE)) + { + *Connected = TRUE; + return TRUE; + } + + if (elevationLevel == PromptElevateAction) + { + if (!PhpShowElevatePrompt(hWnd, Message, &button)) + return FALSE; + } + + if (elevationLevel == AlwaysElevateAction || button == IDYES) + { + *Connected = PhUiConnectToPhSvc(hWnd, FALSE); + } + + return TRUE; +} + +/** + * Connects to phsvc. + * + * \param hWnd The window to display user interface components on. + * \param ConnectOnly TRUE to only try to connect to phsvc, otherwise + * FALSE to try to elevate and start phsvc if the initial connection + * attempt failed. + */ +BOOLEAN PhUiConnectToPhSvc( + _In_opt_ HWND hWnd, + _In_ BOOLEAN ConnectOnly + ) +{ + return PhUiConnectToPhSvcEx(hWnd, ElevatedPhSvcMode, ConnectOnly); +} + +VOID PhpGetPhSvcPortName( + _In_ PH_PHSVC_MODE Mode, + _Out_ PUNICODE_STRING PortName + ) +{ + switch (Mode) + { + case ElevatedPhSvcMode: + if (!PhIsExecutingInWow64()) + RtlInitUnicodeString(PortName, PHSVC_PORT_NAME); + else + RtlInitUnicodeString(PortName, PHSVC_WOW64_PORT_NAME); + break; + case Wow64PhSvcMode: + RtlInitUnicodeString(PortName, PHSVC_WOW64_PORT_NAME); + break; + default: + PhRaiseStatus(STATUS_INVALID_PARAMETER); + break; + } +} + +BOOLEAN PhpStartPhSvcProcess( + _In_opt_ HWND hWnd, + _In_ PH_PHSVC_MODE Mode + ) +{ + switch (Mode) + { + case ElevatedPhSvcMode: + if (PhShellProcessHacker( + hWnd, + L"-phsvc", + SW_HIDE, + PH_SHELL_EXECUTE_ADMIN | PH_SHELL_EXECUTE_NOZONECHECKS, + PH_SHELL_APP_PROPAGATE_PARAMETERS, + 0, + NULL + )) + { + return TRUE; + } + + break; + case Wow64PhSvcMode: + { + static PWSTR relativeFileNames[] = + { + L"\\x86\\ProcessHacker.exe", + L"\\..\\x86\\ProcessHacker.exe", +#ifdef DEBUG + L"\\..\\Debug32\\ProcessHacker.exe", +#endif + L"\\..\\Release32\\ProcessHacker.exe" + }; + + ULONG i; + PPH_STRING applicationDirectory; + + if (!(applicationDirectory = PhGetApplicationDirectory())) + return FALSE; + + for (i = 0; i < RTL_NUMBER_OF(relativeFileNames); i++) + { + PPH_STRING fileName; + PPH_STRING fileFullPath; + + fileName = PhConcatStringRefZ(&applicationDirectory->sr, relativeFileNames[i]); + + if (fileFullPath = PhGetFullPath(fileName->Buffer, NULL)) + PhMoveReference(&fileName, fileFullPath); + + if (PhDoesFileExistsWin32(fileName->Buffer)) + { + if (PhShellProcessHackerEx( + hWnd, + fileName->Buffer, + L"-phsvc", + SW_HIDE, + PH_SHELL_EXECUTE_NOZONECHECKS, + PH_SHELL_APP_PROPAGATE_PARAMETERS, + 0, + NULL + )) + { + PhDereferenceObject(fileName); + PhDereferenceObject(applicationDirectory); + return TRUE; + } + } + + PhDereferenceObject(fileName); + } + + PhDereferenceObject(applicationDirectory); + } + break; + } + + return FALSE; +} + +/** + * Connects to phsvc. + * + * \param hWnd The window to display user interface components on. + * \param Mode The type of phsvc instance to connect to. + * \param ConnectOnly TRUE to only try to connect to phsvc, otherwise + * FALSE to try to elevate and start phsvc if the initial connection + * attempt failed. + */ +BOOLEAN PhUiConnectToPhSvcEx( + _In_opt_ HWND hWnd, + _In_ PH_PHSVC_MODE Mode, + _In_ BOOLEAN ConnectOnly + ) +{ + NTSTATUS status; + BOOLEAN started; + UNICODE_STRING portName; + + if (_InterlockedIncrementNoZero(&PhSvcReferenceCount)) + { + if (PhSvcCurrentMode == Mode) + { + started = TRUE; + } + else + { + _InterlockedDecrement(&PhSvcReferenceCount); + started = FALSE; + } + } + else + { + PhAcquireQueuedLockExclusive(&PhSvcStartLock); + + if (_InterlockedExchange(&PhSvcReferenceCount, 0) == 0) + { + started = FALSE; + PhpGetPhSvcPortName(Mode, &portName); + + // Try to connect first, then start the server if we failed. + status = PhSvcConnectToServer(&portName, 0); + + if (NT_SUCCESS(status)) + { + started = TRUE; + PhSvcCurrentMode = Mode; + _InterlockedIncrement(&PhSvcReferenceCount); + } + else if (!ConnectOnly) + { + // Prompt for elevation, and then try to connect to the server. + + if (PhpStartPhSvcProcess(hWnd, Mode)) + started = TRUE; + + if (started) + { + ULONG attempts = 50; + + // Try to connect several times because the server may take + // a while to initialize. + do + { + status = PhSvcConnectToServer(&portName, 0); + + if (NT_SUCCESS(status)) + break; + + PhDelayExecution(100); + + } while (--attempts != 0); + + // Increment the reference count even if we failed. + // We don't want to prompt the user again. + + PhSvcCurrentMode = Mode; + _InterlockedIncrement(&PhSvcReferenceCount); + } + } + } + else + { + if (PhSvcCurrentMode == Mode) + { + started = TRUE; + _InterlockedIncrement(&PhSvcReferenceCount); + } + else + { + started = FALSE; + } + } + + PhReleaseQueuedLockExclusive(&PhSvcStartLock); + } + + return started; +} + +/** + * Disconnects from phsvc. + */ +VOID PhUiDisconnectFromPhSvc( + VOID + ) +{ + PhAcquireQueuedLockExclusive(&PhSvcStartLock); + + if (_InterlockedDecrement(&PhSvcReferenceCount) == 0) + { + PhSvcDisconnectFromServer(); + } + + PhReleaseQueuedLockExclusive(&PhSvcStartLock); +} + +BOOLEAN PhUiLockComputer( + _In_ HWND hWnd + ) +{ + if (LockWorkStation()) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to lock the computer.", 0, GetLastError()); + + return FALSE; +} + +BOOLEAN PhUiLogoffComputer( + _In_ HWND hWnd + ) +{ + if (ExitWindowsEx(EWX_LOGOFF, 0)) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to log off the computer.", 0, GetLastError()); + + return FALSE; +} + +BOOLEAN PhUiSleepComputer( + _In_ HWND hWnd + ) +{ + NTSTATUS status; + + if (NT_SUCCESS(status = NtInitiatePowerAction( + PowerActionSleep, + PowerSystemSleeping1, + 0, + FALSE + ))) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to sleep the computer.", status, 0); + + return FALSE; +} + +BOOLEAN PhUiHibernateComputer( + _In_ HWND hWnd + ) +{ + NTSTATUS status; + + if (NT_SUCCESS(status = NtInitiatePowerAction( + PowerActionHibernate, + PowerSystemSleeping1, + 0, + FALSE + ))) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to hibernate the computer.", status, 0); + + return FALSE; +} + +BOOLEAN PhUiRestartComputer( + _In_ HWND hWnd, + _In_ ULONG Flags + ) +{ + NTSTATUS status; + BOOLEAN forceShutdown; + + // Taskmgr prior to Windows 8 included a feature to force shutdown via NT instead of CSRSS + // when holding the control key. (dmex) + forceShutdown = !!(GetKeyState(VK_CONTROL) < 0); + + if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + hWnd, + L"restart", + L"the computer", + NULL, + FALSE + )) + { + if (forceShutdown) + { + if (!NT_SUCCESS(status = NtShutdownSystem(ShutdownReboot))) + { + PhShowStatus(hWnd, L"Unable to restart the computer.", status, 0); + } + } + else + { + if (ExitWindowsEx(EWX_REBOOT | Flags, 0)) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to restart the computer.", 0, GetLastError()); + } + } + + return FALSE; +} + +BOOLEAN PhUiShutdownComputer( + _In_ HWND hWnd, + _In_ ULONG Flags + ) +{ + NTSTATUS status; + BOOLEAN forceShutdown; + + // Taskmgr prior to Windows 8 included a feature to force shutdown via NT instead of CSRSS + // when holding the control key. (dmex) + forceShutdown = !!(GetKeyState(VK_CONTROL) < 0); + + if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + hWnd, + L"shut down", + L"the computer", + NULL, + FALSE + )) + { + if (forceShutdown) + { + if (!NT_SUCCESS(status = NtShutdownSystem(ShutdownPowerOff))) + { + PhShowStatus(hWnd, L"Unable to shut down the computer.", status, 0); + } + } + else + { + if (ExitWindowsEx(EWX_POWEROFF | Flags, 0)) + { + return TRUE; + } + else if (ExitWindowsEx(EWX_SHUTDOWN | Flags, 0)) + { + return TRUE; + } + else + { + PhShowStatus(hWnd, L"Unable to shut down the computer.", 0, GetLastError()); + } + } + } + + return FALSE; +} + +BOOLEAN PhUiConnectSession( + _In_ HWND hWnd, + _In_ ULONG SessionId + ) +{ + BOOLEAN success = FALSE; + PPH_STRING selectedChoice = NULL; + PPH_STRING oldSelectedChoice = NULL; + + // Try once with no password. + if (WinStationConnectW(NULL, SessionId, LOGONID_CURRENT, L"", TRUE)) + return TRUE; + + while (PhaChoiceDialog( + hWnd, + L"Connect to session", + L"Password:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_PASSWORD, + &selectedChoice, + NULL, + NULL + )) + { + if (oldSelectedChoice) + { + RtlSecureZeroMemory(oldSelectedChoice->Buffer, oldSelectedChoice->Length); + PhDereferenceObject(oldSelectedChoice); + } + + oldSelectedChoice = selectedChoice; + + if (WinStationConnectW(NULL, SessionId, LOGONID_CURRENT, selectedChoice->Buffer, TRUE)) + { + success = TRUE; + break; + } + else + { + if (!PhShowContinueStatus(hWnd, L"Unable to connect to the session", 0, GetLastError())) + break; + } + } + + if (oldSelectedChoice) + { + RtlSecureZeroMemory(oldSelectedChoice->Buffer, oldSelectedChoice->Length); + PhDereferenceObject(oldSelectedChoice); + } + + return success; +} + +BOOLEAN PhUiDisconnectSession( + _In_ HWND hWnd, + _In_ ULONG SessionId + ) +{ + if (WinStationDisconnect(NULL, SessionId, FALSE)) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to disconnect the session", 0, GetLastError()); + + return FALSE; +} + +BOOLEAN PhUiLogoffSession( + _In_ HWND hWnd, + _In_ ULONG SessionId + ) +{ + if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + hWnd, + L"logoff", + L"the user", + NULL, + FALSE + )) + { + if (WinStationReset(NULL, SessionId, FALSE)) + return TRUE; + else + PhShowStatus(hWnd, L"Unable to logoff the session", 0, GetLastError()); + } + + return FALSE; +} + +/** + * Determines if a process is a system process. + * + * \param ProcessId The PID of the process to check. + */ +static BOOLEAN PhpIsDangerousProcess( + _In_ HANDLE ProcessId + ) +{ + NTSTATUS status; + PPH_STRING fileName; + PPH_STRING systemDirectory; + ULONG i; + + if (ProcessId == SYSTEM_PROCESS_ID) + return TRUE; + + if (!NT_SUCCESS(status = PhGetProcessImageFileNameByProcessId(ProcessId, &fileName))) + return FALSE; + + PhMoveReference(&fileName, PhGetFileName(fileName)); + PH_AUTO(fileName); + + systemDirectory = PH_AUTO(PhGetSystemDirectory()); + + for (i = 0; i < sizeof(DangerousProcesses) / sizeof(PWSTR); i++) + { + PPH_STRING fullName; + + fullName = PhaConcatStrings(3, systemDirectory->Buffer, L"\\", DangerousProcesses[i]); + + if (PhEqualString(fileName, fullName, TRUE)) + return TRUE; + } + + return FALSE; +} + +/** + * Checks if the user wants to proceed with an operation. + * + * \param hWnd A handle to the parent window. + * \param Verb A verb describing the action. + * \param Message A message containing additional information + * about the action. + * \param WarnOnlyIfDangerous TRUE to skip the confirmation + * dialog if none of the processes are system processes, + * FALSE to always show the confirmation dialog. + * \param Processes An array of pointers to process items. + * \param NumberOfProcesses The number of process items. + * + * \return TRUE if the user wants to proceed with the operation, + * otherwise FALSE. + */ +static BOOLEAN PhpShowContinueMessageProcesses( + _In_ HWND hWnd, + _In_ PWSTR Verb, + _In_opt_ PWSTR Message, + _In_ BOOLEAN WarnOnlyIfDangerous, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ) +{ + PWSTR object; + ULONG i; + BOOLEAN critical = FALSE; + BOOLEAN dangerous = FALSE; + BOOLEAN cont = FALSE; + + if (NumberOfProcesses == 0) + return FALSE; + + for (i = 0; i < NumberOfProcesses; i++) + { + HANDLE processHandle; + BOOLEAN breakOnTermination = FALSE; + + if (PhpIsDangerousProcess(Processes[i]->ProcessId)) + { + critical = TRUE; + dangerous = TRUE; + break; + } + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, Processes[i]->ProcessId))) + { + PhGetProcessBreakOnTermination(processHandle, &breakOnTermination); + NtClose(processHandle); + } + + if (breakOnTermination) + { + critical = TRUE; + dangerous = TRUE; + break; + } + } + + if (WarnOnlyIfDangerous && !dangerous) + return TRUE; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + if (NumberOfProcesses == 1) + { + object = Processes[0]->ProcessName->Buffer; + } + else if (NumberOfProcesses == 2) + { + object = PhaConcatStrings( + 3, + Processes[0]->ProcessName->Buffer, + L" and ", + Processes[1]->ProcessName->Buffer + )->Buffer; + } + else + { + object = L"the selected processes"; + } + + if (!dangerous) + { + cont = PhShowConfirmMessage( + hWnd, + Verb, + object, + Message, + FALSE + ); + } + else if (!critical) + { + cont = PhShowConfirmMessage( + hWnd, + Verb, + object, + PhaConcatStrings( + 3, + L"You are about to ", + Verb, + L" one or more system processes." + )->Buffer, + TRUE + ); + } + else + { + PPH_STRING message; + + if (PhEqualStringZ(Verb, L"terminate", FALSE)) + { + message = PhaConcatStrings( + 3, + L"You are about to ", + Verb, + L" one or more critical processes. This will shut down the operating system immediately." + ); + } + else + { + message = PhaConcatStrings( + 3, + L"You are about to ", + Verb, + L" one or more critical processes." + ); + } + + cont = PhShowConfirmMessage( + hWnd, + Verb, + object, + message->Buffer, + TRUE + ); + } + } + else + { + cont = TRUE; + } + + return cont; +} + +/** + * Shows an error message to the user and checks + * if the user wants to continue. + * + * \param hWnd A handle to the parent window. + * \param Verb A verb describing the action which + * resulted in an error. + * \param Process The process item which the action + * was performed on. + * \param Status A NT status value representing the + * error. + * \param Win32Result A Win32 error code representing + * the error. + * + * \return TRUE if the user wants to continue, otherwise + * FALSE. The result is typically only useful when + * executing an action on multiple processes. + */ +static BOOLEAN PhpShowErrorProcess( + _In_ HWND hWnd, + _In_ PWSTR Verb, + _In_ PPH_PROCESS_ITEM Process, + _In_ NTSTATUS Status, + _In_opt_ ULONG Win32Result + ) +{ + if (!PH_IS_FAKE_PROCESS_ID(Process->ProcessId)) + { + return PhShowContinueStatus( + hWnd, + PhaFormatString( + L"Unable to %s %s (PID %lu)", + Verb, + Process->ProcessName->Buffer, + HandleToUlong(Process->ProcessId) + )->Buffer, + Status, + Win32Result + ); + } + else + { + return PhShowContinueStatus( + hWnd, + PhaFormatString( + L"Unable to %s %s", + Verb, + Process->ProcessName->Buffer + )->Buffer, + Status, + Win32Result + ); + } +} + +BOOLEAN PhUiTerminateProcesses( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + if (!PhpShowContinueMessageProcesses( + hWnd, + L"terminate", + L"Terminating a process will cause unsaved data to be lost.", + FALSE, + Processes, + NumberOfProcesses + )) + return FALSE; + + for (i = 0; i < NumberOfProcesses; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_TERMINATE, + Processes[i]->ProcessId + ))) + { + // An exit status of 1 is used here for compatibility reasons: + // 1. Both Task Manager and Process Explorer use 1. + // 2. winlogon tries to restart explorer.exe if the exit status is not 1. + + status = PhTerminateProcess(processHandle, 1); + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to terminate ", Processes[i]->ProcessName->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessTerminate, 0))) + success = TRUE; + else + PhpShowErrorProcess(hWnd, L"terminate", Processes[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorProcess(hWnd, L"terminate", Processes[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhpUiTerminateTreeProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process, + _In_ PVOID Processes, + _Inout_ PBOOLEAN Success + ) +{ + NTSTATUS status; + PSYSTEM_PROCESS_INFORMATION process; + HANDLE processHandle; + PPH_PROCESS_ITEM processItem; + + // Note: + // FALSE should be written to Success if any part of the operation failed. + // The return value of this function indicates whether to continue with + // the operation (FALSE if user cancelled). + + // Terminate the process. + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_TERMINATE, + Process->ProcessId + ))) + { + status = PhTerminateProcess(processHandle, 1); + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + *Success = FALSE; + + if (!PhpShowErrorProcess(hWnd, L"terminate", Process, status, 0)) + return FALSE; + } + + // Terminate the process' children. + + process = PH_FIRST_PROCESS(Processes); + + do + { + if (process->UniqueProcessId != Process->ProcessId && + process->InheritedFromUniqueProcessId == Process->ProcessId) + { + if (processItem = PhReferenceProcessItem(process->UniqueProcessId)) + { + if (WindowsVersion >= WINDOWS_10_RS3) + { + // Check the sequence number to make sure it is a descendant. + if (processItem->ProcessSequenceNumber >= Process->ProcessSequenceNumber) + { + if (!PhpUiTerminateTreeProcess(hWnd, processItem, Processes, Success)) + { + PhDereferenceObject(processItem); + return FALSE; + } + } + } + else + { + // Check the creation time to make sure it is a descendant. + if (processItem->CreateTime.QuadPart >= Process->CreateTime.QuadPart) + { + if (!PhpUiTerminateTreeProcess(hWnd, processItem, Processes, Success)) + { + PhDereferenceObject(processItem); + return FALSE; + } + } + } + + PhDereferenceObject(processItem); + } + } + } while (process = PH_NEXT_PROCESS(process)); + + return TRUE; +} + +BOOLEAN PhUiTerminateTreeProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ) +{ + NTSTATUS status; + BOOLEAN success = TRUE; + BOOLEAN cont = FALSE; + PVOID processes; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + cont = PhShowConfirmMessage( + hWnd, + L"terminate", + PhaConcatStrings2(Process->ProcessName->Buffer, L" and its descendants")->Buffer, + L"Terminating a process tree will cause the process and its descendants to be terminated.", + FALSE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + { + PhShowStatus(hWnd, L"Unable to enumerate processes", status, 0); + return FALSE; + } + + PhpUiTerminateTreeProcess(hWnd, Process, processes, &success); + PhFree(processes); + + return success; +} + +BOOLEAN PhUiSuspendProcesses( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + if (!PhpShowContinueMessageProcesses( + hWnd, + L"suspend", + NULL, + TRUE, + Processes, + NumberOfProcesses + )) + return FALSE; + + for (i = 0; i < NumberOfProcesses; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SUSPEND_RESUME, + Processes[i]->ProcessId + ))) + { + status = NtSuspendProcess(processHandle); + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to suspend ", Processes[i]->ProcessName->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessSuspend, 0))) + success = TRUE; + else + PhpShowErrorProcess(hWnd, L"suspend", Processes[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorProcess(hWnd, L"suspend", Processes[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhUiResumeProcesses( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + if (!PhpShowContinueMessageProcesses( + hWnd, + L"resume", + NULL, + TRUE, + Processes, + NumberOfProcesses + )) + return FALSE; + + for (i = 0; i < NumberOfProcesses; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SUSPEND_RESUME, + Processes[i]->ProcessId + ))) + { + status = NtResumeProcess(processHandle); + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to resume ", Processes[i]->ProcessName->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessResume, 0))) + success = TRUE; + else + PhpShowErrorProcess(hWnd, L"resume", Processes[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorProcess(hWnd, L"resume", Processes[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhUiRestartProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ) +{ + NTSTATUS status; + BOOLEAN cont = FALSE; + HANDLE processHandle = NULL; + PPH_STRING commandLine; + PPH_STRING currentDirectory; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + cont = PhShowConfirmMessage( + hWnd, + L"restart", + Process->ProcessName->Buffer, + L"The process will be restarted with the same command line and " + L"working directory, but if it is running under a different user it " + L"will be restarted under the current user.", + FALSE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + // Open the process and get the command line and current directory. + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + Process->ProcessId + ))) + goto ErrorExit; + + if (!NT_SUCCESS(status = PhGetProcessCommandLine( + processHandle, + &commandLine + ))) + goto ErrorExit; + + PH_AUTO(commandLine); + + if (!NT_SUCCESS(status = PhGetProcessPebString( + processHandle, + PhpoCurrentDirectory, + ¤tDirectory + ))) + goto ErrorExit; + + PH_AUTO(currentDirectory); + + NtClose(processHandle); + processHandle = NULL; + + // Open the process and terminate it. + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_TERMINATE, + Process->ProcessId + ))) + goto ErrorExit; + + if (!NT_SUCCESS(status = PhTerminateProcess( + processHandle, + 1 + ))) + goto ErrorExit; + + NtClose(processHandle); + processHandle = NULL; + + // Start the process. + + status = PhCreateProcessWin32( + PhGetString(Process->FileName), // we didn't wait for S1 processing + commandLine->Buffer, + NULL, + currentDirectory->Buffer, + 0, + NULL, + NULL, + NULL + ); + +ErrorExit: + if (processHandle) + NtClose(processHandle); + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(hWnd, L"restart", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +// Contributed by evilpie (#2981421) +BOOLEAN PhUiDebugProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ) +{ + static PH_STRINGREF aeDebugKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"); +#ifdef _WIN64 + static PH_STRINGREF aeDebugWow64KeyName = PH_STRINGREF_INIT(L"Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"); +#endif + NTSTATUS status; + BOOLEAN cont = FALSE; + PH_STRING_BUILDER commandLineBuilder; + HANDLE keyHandle; + PPH_STRING debugger; + PH_STRINGREF commandPart; + PH_STRINGREF dummy; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + cont = PhShowConfirmMessage( + hWnd, + L"debug", + Process->ProcessName->Buffer, + L"Debugging a process may result in loss of data.", + FALSE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + status = PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, +#ifdef _WIN64 + Process->IsWow64 ? &aeDebugWow64KeyName : &aeDebugKeyName, +#else + &aeDebugKeyName, +#endif + 0 + ); + + if (NT_SUCCESS(status)) + { + if (debugger = PH_AUTO(PhQueryRegistryString(keyHandle, L"Debugger"))) + { + if (PhSplitStringRefAtChar(&debugger->sr, '"', &dummy, &commandPart) && + PhSplitStringRefAtChar(&commandPart, '"', &commandPart, &dummy)) + { + DebuggerCommand = PhCreateString2(&commandPart); + } + } + + NtClose(keyHandle); + } + + if (PhIsNullOrEmptyString(DebuggerCommand)) + { + PhShowError(hWnd, L"Unable to locate the debugger."); + return FALSE; + } + + PhInitializeStringBuilder(&commandLineBuilder, DebuggerCommand->Length + 30); + + PhAppendCharStringBuilder(&commandLineBuilder, '"'); + PhAppendStringBuilder(&commandLineBuilder, &DebuggerCommand->sr); + PhAppendCharStringBuilder(&commandLineBuilder, '"'); + PhAppendFormatStringBuilder(&commandLineBuilder, L" -p %lu", HandleToUlong(Process->ProcessId)); + + status = PhCreateProcessWin32( + NULL, + commandLineBuilder.String->Buffer, + NULL, + NULL, + 0, + NULL, + NULL, + NULL + ); + + PhDeleteStringBuilder(&commandLineBuilder); + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(hWnd, L"debug", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiReduceWorkingSetProcesses( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ) +{ + BOOLEAN success = TRUE; + ULONG i; + + for (i = 0; i < NumberOfProcesses; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SET_QUOTA, + Processes[i]->ProcessId + ))) + { + QUOTA_LIMITS quotaLimits; + + memset("aLimits, 0, sizeof(QUOTA_LIMITS)); + quotaLimits.MinimumWorkingSetSize = -1; + quotaLimits.MaximumWorkingSetSize = -1; + + status = PhSetProcessQuotaLimits(processHandle, quotaLimits); + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + success = FALSE; + + if (!PhpShowErrorProcess(hWnd, L"reduce the working set of", Processes[i], status, 0)) + break; + } + } + + return success; +} + +BOOLEAN PhUiSetVirtualizationProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process, + _In_ BOOLEAN Enable + ) +{ + NTSTATUS status; + BOOLEAN cont = FALSE; + HANDLE processHandle; + HANDLE tokenHandle; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + cont = PhShowConfirmMessage( + hWnd, + L"set", + L"virtualization for the process", + L"Enabling or disabling virtualization for a process may " + L"alter its functionality and produce undesirable effects.", + FALSE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + Process->ProcessId + ))) + { + if (NT_SUCCESS(status = PhOpenProcessToken( + processHandle, + TOKEN_WRITE, + &tokenHandle + ))) + { + status = PhSetTokenIsVirtualizationEnabled(tokenHandle, Enable); + NtClose(tokenHandle); + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(hWnd, L"set virtualization for", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiSetCriticalProcess( + _In_ HWND WindowHandle, + _In_ PPH_PROCESS_ITEM Process + ) +{ + NTSTATUS status; + HANDLE processHandle; + BOOLEAN breakOnTermination; + + status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION, + Process->ProcessId + ); + + if (NT_SUCCESS(status)) + { + status = PhGetProcessBreakOnTermination( + processHandle, + &breakOnTermination + ); + + if (NT_SUCCESS(status)) + { + if (!breakOnTermination && (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + WindowHandle, + L"enable", + L"critical status on the process", + L"If the process ends, the operating system will shut down immediately.", + TRUE + ))) + { + status = PhSetProcessBreakOnTermination(processHandle, TRUE); + } + else if (breakOnTermination && (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + WindowHandle, + L"disable", + L"critical status on the process", + NULL, + FALSE + ))) + { + status = PhSetProcessBreakOnTermination(processHandle, FALSE); + } + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(WindowHandle, L"set critical status", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiDetachFromDebuggerProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ) +{ + NTSTATUS status; + HANDLE processHandle; + HANDLE debugObjectHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME, + Process->ProcessId + ))) + { + if (NT_SUCCESS(status = PhGetProcessDebugObject( + processHandle, + &debugObjectHandle + ))) + { + // Disable kill-on-close. + if (NT_SUCCESS(status = PhSetDebugKillProcessOnExit( + debugObjectHandle, + FALSE + ))) + { + status = NtRemoveProcessDebug(processHandle, debugObjectHandle); + } + + NtClose(debugObjectHandle); + } + + NtClose(processHandle); + } + + if (status == STATUS_PORT_NOT_SET) + { + PhShowInformation2(hWnd, L"The process is not being debugged.", L""); + return FALSE; + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(hWnd, L"detach debugger from", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiLoadDllProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ) +{ + static PH_FILETYPE_FILTER filters[] = + { + { L"DLL files (*.dll)", L"*.dll" }, + { L"All files (*.*)", L"*.*" } + }; + + NTSTATUS status; + PVOID fileDialog; + PPH_STRING fileName; + HANDLE processHandle; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, RTL_NUMBER_OF(filters)); + + if (!PhShowFileDialog(hWnd, fileDialog)) + { + PhFreeFileDialog(fileDialog); + return FALSE; + } + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + PhFreeFileDialog(fileDialog); + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | + PROCESS_VM_READ | PROCESS_VM_WRITE, + Process->ProcessId + ))) + { + LARGE_INTEGER timeout; + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(5, PH_TIMEOUT_SEC); + status = PhLoadDllProcess( + processHandle, + fileName->Buffer, + &timeout + ); + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(hWnd, L"load the DLL into", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiSetIoPriorityProcesses( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses, + _In_ IO_PRIORITY_HINT IoPriority + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + for (i = 0; i < NumberOfProcesses; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + Processes[i]->ProcessId + ))) + { + if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID) + { + status = PhSetProcessIoPriority(processHandle, IoPriority); + } + else + { + // See comment in PhUiSetPriorityProcesses. + status = STATUS_UNSUCCESSFUL; + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + // The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege. + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to set the I/O priority of ", Processes[i]->ProcessName->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessIoPriority, IoPriority))) + success = TRUE; + else + PhpShowErrorProcess(hWnd, L"set the I/O priority of", Processes[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorProcess(hWnd, L"set the I/O priority of", Processes[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhUiSetPagePriorityProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process, + _In_ ULONG PagePriority + ) +{ + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + Process->ProcessId + ))) + { + if (Process->ProcessId != SYSTEM_PROCESS_ID) + { + status = PhSetProcessPagePriority(processHandle, PagePriority); + } + else + { + // See comment in PhUiSetPriorityProcesses. + status = STATUS_UNSUCCESSFUL; + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorProcess(hWnd, L"set the page priority of", Process, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiSetPriorityProcesses( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses, + _In_ ULONG PriorityClass + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + for (i = 0; i < NumberOfProcesses; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + Processes[i]->ProcessId + ))) + { + if (Processes[i]->ProcessId != SYSTEM_PROCESS_ID) + { + PROCESS_PRIORITY_CLASS priorityClass; + + priorityClass.Foreground = FALSE; + priorityClass.PriorityClass = (UCHAR)PriorityClass; + + status = PhSetProcessPriority(processHandle, priorityClass); + } + else + { + // Changing the priority of System can lead to a BSOD on some versions of Windows, + // so disallow this. + status = STATUS_UNSUCCESSFUL; + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + // The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege. + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to set the priority of ", Processes[i]->ProcessName->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlProcess(Processes[i]->ProcessId, PhSvcControlProcessPriority, PriorityClass))) + success = TRUE; + else + PhpShowErrorProcess(hWnd, L"set the priority of", Processes[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorProcess(hWnd, L"set the priority of", Processes[i], status, 0)) + break; + } + } + } + + return success; +} + +static VOID PhpShowErrorService( + _In_ HWND hWnd, + _In_ PWSTR Verb, + _In_ PPH_SERVICE_ITEM Service, + _In_ NTSTATUS Status, + _In_opt_ ULONG Win32Result + ) +{ + PhShowStatus( + hWnd, + PhaFormatString( + L"Unable to %s %s.", + Verb, + Service->Name->Buffer + )->Buffer, + Status, + Win32Result + ); +} + +BOOLEAN PhUiStartService( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM Service + ) +{ + SC_HANDLE serviceHandle; + BOOLEAN success = FALSE; + + serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_START); + + if (serviceHandle) + { + if (StartService(serviceHandle, 0, NULL)) + success = TRUE; + + CloseServiceHandle(serviceHandle); + } + + if (!success) + { + NTSTATUS status; + BOOLEAN connected; + + status = PhGetLastWin32ErrorAsNtStatus(); + + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to start ", Service->Name->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceStart))) + success = TRUE; + else + PhpShowErrorService(hWnd, L"start", Service, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhpShowErrorService(hWnd, L"start", Service, status, 0); + } + } + + return success; +} + +BOOLEAN PhUiContinueService( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM Service + ) +{ + SC_HANDLE serviceHandle; + BOOLEAN success = FALSE; + + serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_PAUSE_CONTINUE); + + if (serviceHandle) + { + SERVICE_STATUS serviceStatus; + + if (ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus)) + success = TRUE; + + CloseServiceHandle(serviceHandle); + } + + if (!success) + { + NTSTATUS status; + BOOLEAN connected; + + status = PhGetLastWin32ErrorAsNtStatus(); + + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to continue ", Service->Name->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceContinue))) + success = TRUE; + else + PhpShowErrorService(hWnd, L"continue", Service, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhpShowErrorService(hWnd, L"continue", Service, status, 0); + } + } + + return success; +} + +BOOLEAN PhUiPauseService( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM Service + ) +{ + SC_HANDLE serviceHandle; + BOOLEAN success = FALSE; + + serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_PAUSE_CONTINUE); + + if (serviceHandle) + { + SERVICE_STATUS serviceStatus; + + if (ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus)) + success = TRUE; + + CloseServiceHandle(serviceHandle); + } + + if (!success) + { + NTSTATUS status; + BOOLEAN connected; + + status = PhGetLastWin32ErrorAsNtStatus(); + + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to pause ", Service->Name->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServicePause))) + success = TRUE; + else + PhpShowErrorService(hWnd, L"pause", Service, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhpShowErrorService(hWnd, L"pause", Service, status, 0); + } + } + + return success; +} + +BOOLEAN PhUiStopService( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM Service + ) +{ + SC_HANDLE serviceHandle; + BOOLEAN success = FALSE; + + serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_STOP); + + if (serviceHandle) + { + SERVICE_STATUS serviceStatus; + + if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus)) + success = TRUE; + + CloseServiceHandle(serviceHandle); + } + + if (!success) + { + NTSTATUS status; + BOOLEAN connected; + + status = PhGetLastWin32ErrorAsNtStatus(); + + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to stop ", Service->Name->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceStop))) + success = TRUE; + else + PhpShowErrorService(hWnd, L"stop", Service, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhpShowErrorService(hWnd, L"stop", Service, status, 0); + } + } + + return success; +} + +BOOLEAN PhUiDeleteService( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM Service + ) +{ + SC_HANDLE serviceHandle; + BOOLEAN success = FALSE; + + // Warnings cannot be disabled for service deletion. + if (!PhShowConfirmMessage( + hWnd, + L"delete", + Service->Name->Buffer, + L"Deleting a service can prevent the system from starting " + L"or functioning properly.", + TRUE + )) + return FALSE; + + serviceHandle = PhOpenService(Service->Name->Buffer, DELETE); + + if (serviceHandle) + { + if (DeleteService(serviceHandle)) + success = TRUE; + + CloseServiceHandle(serviceHandle); + } + + if (!success) + { + NTSTATUS status; + BOOLEAN connected; + + status = PhGetLastWin32ErrorAsNtStatus(); + + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to delete ", Service->Name->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlService(Service->Name->Buffer, PhSvcControlServiceDelete))) + success = TRUE; + else + PhpShowErrorService(hWnd, L"delete", Service, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhpShowErrorService(hWnd, L"delete", Service, status, 0); + } + } + + return success; +} + +BOOLEAN PhUiCloseConnections( + _In_ HWND hWnd, + _In_ PPH_NETWORK_ITEM *Connections, + _In_ ULONG NumberOfConnections + ) +{ + + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG result; + ULONG i; + MIB_TCPROW tcpRow; + + for (i = 0; i < NumberOfConnections; i++) + { + if ( + Connections[i]->ProtocolType != PH_TCP4_NETWORK_PROTOCOL || + Connections[i]->State != MIB_TCP_STATE_ESTAB + ) + continue; + + tcpRow.dwState = MIB_TCP_STATE_DELETE_TCB; + tcpRow.dwLocalAddr = Connections[i]->LocalEndpoint.Address.Ipv4; + tcpRow.dwLocalPort = _byteswap_ushort((USHORT)Connections[i]->LocalEndpoint.Port); + tcpRow.dwRemoteAddr = Connections[i]->RemoteEndpoint.Address.Ipv4; + tcpRow.dwRemotePort = _byteswap_ushort((USHORT)Connections[i]->RemoteEndpoint.Port); + + if ((result = SetTcpEntry(&tcpRow)) != NO_ERROR) + { + NTSTATUS status; + BOOLEAN connected; + + success = FALSE; + + // SetTcpEntry returns ERROR_MR_MID_NOT_FOUND for access denied errors for some reason. + if (result == ERROR_MR_MID_NOT_FOUND) + result = ERROR_ACCESS_DENIED; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + L"Unable to close the TCP connection", + NTSTATUS_FROM_WIN32(result), + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallSetTcpEntry(&tcpRow))) + success = TRUE; + else + PhShowStatus(hWnd, L"Unable to close the TCP connection", status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (PhShowMessage2( + hWnd, + TDCBF_OK_BUTTON, + TD_ERROR_ICON, + L"Unable to close the TCP connection.", + L"Make sure Process Hacker is running with administrative privileges." + ) != IDOK) + break; + } + } + } + + return success; +} + +static BOOLEAN PhpShowContinueMessageThreads( + _In_ HWND hWnd, + _In_ PWSTR Verb, + _In_ PWSTR Message, + _In_ BOOLEAN Warning, + _In_ PPH_THREAD_ITEM *Threads, + _In_ ULONG NumberOfThreads + ) +{ + PWSTR object; + BOOLEAN cont = FALSE; + + if (NumberOfThreads == 0) + return FALSE; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + if (NumberOfThreads == 1) + { + object = L"the selected thread"; + } + else + { + object = L"the selected threads"; + } + + cont = PhShowConfirmMessage( + hWnd, + Verb, + object, + Message, + Warning + ); + } + else + { + cont = TRUE; + } + + return cont; +} + +static BOOLEAN PhpShowErrorThread( + _In_ HWND hWnd, + _In_ PWSTR Verb, + _In_ PPH_THREAD_ITEM Thread, + _In_ NTSTATUS Status, + _In_opt_ ULONG Win32Result + ) +{ + return PhShowContinueStatus( + hWnd, + PhaFormatString( + L"Unable to %s thread %lu", + Verb, + HandleToUlong(Thread->ThreadId) + )->Buffer, + Status, + Win32Result + ); +} + +BOOLEAN PhUiTerminateThreads( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM *Threads, + _In_ ULONG NumberOfThreads + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + if (!PhpShowContinueMessageThreads( + hWnd, + L"terminate", + L"Terminating a thread may cause the process to stop working.", + FALSE, + Threads, + NumberOfThreads + )) + return FALSE; + + for (i = 0; i < NumberOfThreads; i++) + { + NTSTATUS status; + HANDLE threadHandle; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_TERMINATE, + Threads[i]->ThreadId + ))) + { + status = NtTerminateThread(threadHandle, STATUS_SUCCESS); + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaFormatString(L"Unable to terminate thread %lu", HandleToUlong(Threads[i]->ThreadId))->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadTerminate, 0))) + success = TRUE; + else + PhpShowErrorThread(hWnd, L"terminate", Threads[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorThread(hWnd, L"terminate", Threads[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhUiSuspendThreads( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM *Threads, + _In_ ULONG NumberOfThreads + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + for (i = 0; i < NumberOfThreads; i++) + { + NTSTATUS status; + HANDLE threadHandle; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_SUSPEND_RESUME, + Threads[i]->ThreadId + ))) + { + status = NtSuspendThread(threadHandle, NULL); + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaFormatString(L"Unable to suspend thread %lu", HandleToUlong(Threads[i]->ThreadId))->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadSuspend, 0))) + success = TRUE; + else + PhpShowErrorThread(hWnd, L"suspend", Threads[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorThread(hWnd, L"suspend", Threads[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhUiResumeThreads( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM *Threads, + _In_ ULONG NumberOfThreads + ) +{ + BOOLEAN success = TRUE; + BOOLEAN cancelled = FALSE; + ULONG i; + + for (i = 0; i < NumberOfThreads; i++) + { + NTSTATUS status; + HANDLE threadHandle; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_SUSPEND_RESUME, + Threads[i]->ThreadId + ))) + { + status = NtResumeThread(threadHandle, NULL); + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + if (!cancelled && PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaFormatString(L"Unable to resume thread %lu", HandleToUlong(Threads[i]->ThreadId))->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlThread(Threads[i]->ThreadId, PhSvcControlThreadResume, 0))) + success = TRUE; + else + PhpShowErrorThread(hWnd, L"resume", Threads[i], status, 0); + + PhUiDisconnectFromPhSvc(); + } + else + { + cancelled = TRUE; + } + } + else + { + if (!PhpShowErrorThread(hWnd, L"resume", Threads[i], status, 0)) + break; + } + } + } + + return success; +} + +BOOLEAN PhUiSetPriorityThread( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM Thread, + _In_ LONG Increment + ) +{ + NTSTATUS status; + HANDLE threadHandle; + + // Special saturation values + if (Increment == THREAD_PRIORITY_TIME_CRITICAL) + Increment = THREAD_BASE_PRIORITY_LOWRT + 1; + else if (Increment == THREAD_PRIORITY_IDLE) + Increment = THREAD_BASE_PRIORITY_IDLE - 1; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_SET_LIMITED_INFORMATION, + Thread->ThreadId + ))) + { + status = PhSetThreadBasePriority(threadHandle, Increment); + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorThread(hWnd, L"set the priority of", Thread, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiSetIoPriorityThread( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM Thread, + _In_ IO_PRIORITY_HINT IoPriority + ) +{ + NTSTATUS status; + BOOLEAN success = TRUE; + HANDLE threadHandle; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_SET_INFORMATION, + Thread->ThreadId + ))) + { + status = PhSetThreadIoPriority(threadHandle, IoPriority); + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + BOOLEAN connected; + + success = FALSE; + + // The operation may have failed due to the lack of SeIncreaseBasePriorityPrivilege. + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaFormatString(L"Unable to set the I/O priority of thread %lu", HandleToUlong(Thread->ThreadId))->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallControlThread(Thread->ThreadId, PhSvcControlThreadIoPriority, IoPriority))) + success = TRUE; + else + PhpShowErrorThread(hWnd, L"set the I/O priority of", Thread, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhpShowErrorThread(hWnd, L"set the I/O priority of", Thread, status, 0); + } + } + + return success; +} + +BOOLEAN PhUiSetPagePriorityThread( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM Thread, + _In_ ULONG PagePriority + ) +{ + NTSTATUS status; + HANDLE threadHandle; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_SET_INFORMATION, + Thread->ThreadId + ))) + { + status = PhSetThreadPagePriority(threadHandle, PagePriority); + + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorThread(hWnd, L"set the page priority of", Thread, status, 0); + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiUnloadModule( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ PPH_MODULE_ITEM Module + ) +{ + NTSTATUS status; + BOOLEAN cont = FALSE; + HANDLE processHandle; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + PWSTR verb; + PWSTR message; + + switch (Module->Type) + { + case PH_MODULE_TYPE_MODULE: + case PH_MODULE_TYPE_WOW64_MODULE: + verb = L"unload"; + message = L"Unloading a module may cause the process to crash."; + + if (WindowsVersion >= WINDOWS_8) + message = L"Unloading a module may cause the process to crash. NOTE: This feature may not work correctly on your version of Windows and some programs may restrict access or ban your account."; + + break; + case PH_MODULE_TYPE_KERNEL_MODULE: + verb = L"unload"; + message = L"Unloading a driver may cause system instability."; + break; + case PH_MODULE_TYPE_MAPPED_FILE: + case PH_MODULE_TYPE_MAPPED_IMAGE: + verb = L"unmap"; + message = L"Unmapping a section view may cause the process to crash."; + break; + default: + return FALSE; + } + + cont = PhShowConfirmMessage( + hWnd, + verb, + Module->Name->Buffer, + message, + TRUE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + switch (Module->Type) + { + case PH_MODULE_TYPE_MODULE: + case PH_MODULE_TYPE_WOW64_MODULE: + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | + PROCESS_VM_READ | PROCESS_VM_WRITE, + ProcessId + ))) + { + LARGE_INTEGER timeout; + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(5, PH_TIMEOUT_SEC); + status = PhUnloadDllProcess( + processHandle, + Module->BaseAddress, + &timeout + ); + + NtClose(processHandle); + } + + if (status == STATUS_DLL_NOT_FOUND) + { + PhShowError(hWnd, L"Unable to find the module to unload."); + return FALSE; + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus( + hWnd, + PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, + status, + 0 + ); + return FALSE; + } + + break; + + case PH_MODULE_TYPE_KERNEL_MODULE: + status = PhUnloadDriver(Module->BaseAddress, Module->Name->Buffer); + + if (!NT_SUCCESS(status)) + { + BOOLEAN success = FALSE; + BOOLEAN connected; + + if (PhpShowErrorAndConnectToPhSvc( + hWnd, + PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, + status, + &connected + )) + { + if (connected) + { + if (NT_SUCCESS(status = PhSvcCallUnloadDriver(Module->BaseAddress, Module->Name->Buffer))) + success = TRUE; + else + PhShowStatus(hWnd, PhaConcatStrings2(L"Unable to unload ", Module->Name->Buffer)->Buffer, status, 0); + + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhShowStatus( + hWnd, + PhaConcatStrings( + 3, + L"Unable to unload ", + Module->Name->Buffer, + L". Make sure Process Hacker is running with " + L"administrative privileges." + )->Buffer, + status, + 0 + ); + return FALSE; + } + + return success; + } + + break; + + case PH_MODULE_TYPE_MAPPED_FILE: + case PH_MODULE_TYPE_MAPPED_IMAGE: + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_VM_OPERATION, + ProcessId + ))) + { + status = NtUnmapViewOfSection(processHandle, Module->BaseAddress); + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus( + hWnd, + PhaFormatString(L"Unable to unmap the section view at 0x%Ix", Module->BaseAddress)->Buffer, + status, + 0 + ); + return FALSE; + } + + break; + + default: + return FALSE; + } + + return TRUE; +} + +BOOLEAN PhUiFreeMemory( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ PPH_MEMORY_ITEM MemoryItem, + _In_ BOOLEAN Free + ) +{ + NTSTATUS status; + BOOLEAN cont = FALSE; + HANDLE processHandle; + + if (PhGetIntegerSetting(L"EnableWarnings")) + { + PWSTR verb; + PWSTR message; + + if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE))) + { + if (Free) + { + verb = L"free"; + message = L"Freeing memory regions may cause the process to crash.\r\n\r\nSome programs may also restrict access or ban your account when freeing the memory of the process."; + } + else + { + verb = L"decommit"; + message = L"Decommitting memory regions may cause the process to crash.\r\n\r\nSome programs may also restrict access or ban your account when decommitting the memory of the process."; + } + } + else + { + verb = L"unmap"; + message = L"Unmapping a section view may cause the process to crash.\r\n\r\nSome programs may also restrict access or ban your account when unmapping the memory of the process."; + } + + cont = PhShowConfirmMessage( + hWnd, + verb, + L"the memory region", + message, + TRUE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_VM_OPERATION, + ProcessId + ))) + { + PVOID baseAddress; + SIZE_T regionSize; + + baseAddress = MemoryItem->BaseAddress; + + if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE))) + { + // The size needs to be 0 if we're freeing. + if (Free) + regionSize = 0; + else + regionSize = MemoryItem->RegionSize; + + status = NtFreeVirtualMemory( + processHandle, + &baseAddress, + ®ionSize, + Free ? MEM_RELEASE : MEM_DECOMMIT + ); + } + else + { + status = NtUnmapViewOfSection(processHandle, baseAddress); + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PWSTR message; + + if (!(MemoryItem->Type & (MEM_MAPPED | MEM_IMAGE))) + { + if (Free) + message = L"Unable to free the memory region"; + else + message = L"Unable to decommit the memory region"; + } + else + { + message = L"Unable to unmap the section view"; + } + + PhShowStatus( + hWnd, + message, + status, + 0 + ); + return FALSE; + } + + return TRUE; +} + +static BOOLEAN PhpShowErrorHandle( + _In_ HWND hWnd, + _In_ PWSTR Verb, + _In_ PPH_HANDLE_ITEM Handle, + _In_ NTSTATUS Status, + _In_opt_ ULONG Win32Result + ) +{ + if (!PhIsNullOrEmptyString(Handle->BestObjectName)) + { + return PhShowContinueStatus( + hWnd, + PhaFormatString( + L"Unable to %s handle \"%s\" (0x%Ix)", + Verb, + Handle->BestObjectName->Buffer, + HandleToUlong(Handle->Handle) + )->Buffer, + Status, + Win32Result + ); + } + else + { + return PhShowContinueStatus( + hWnd, + PhaFormatString( + L"Unable to %s handle 0x%Ix", + Verb, + HandleToUlong(Handle->Handle) + )->Buffer, + Status, + Win32Result + ); + } +} + +BOOLEAN PhUiCloseHandles( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ PPH_HANDLE_ITEM *Handles, + _In_ ULONG NumberOfHandles, + _In_ BOOLEAN Warn + ) +{ + NTSTATUS status; + BOOLEAN cont = FALSE; + BOOLEAN success = TRUE; + HANDLE processHandle; + + if (NumberOfHandles == 0) + return FALSE; + + if (Warn && PhGetIntegerSetting(L"EnableWarnings")) + { + cont = PhShowConfirmMessage( + hWnd, + L"close", + NumberOfHandles == 1 ? L"the selected handle" : L"the selected handles", + L"Closing handles may cause system instability and data corruption.", + FALSE + ); + } + else + { + cont = TRUE; + } + + if (!cont) + return FALSE; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, + ProcessId + ))) + { + BOOLEAN critical = FALSE; + BOOLEAN strict = FALSE; + + if (WindowsVersion >= WINDOWS_10) + { + BOOLEAN breakOnTermination; + PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; + + if (NT_SUCCESS(PhGetProcessBreakOnTermination( + processHandle, + &breakOnTermination + ))) + { + if (breakOnTermination) + { + critical = TRUE; + } + } + + policyInfo.Policy = ProcessStrictHandleCheckPolicy; + policyInfo.StrictHandleCheckPolicy.Flags = 0; + + if (NT_SUCCESS(NtQueryInformationProcess( + processHandle, + ProcessMitigationPolicy, + &policyInfo, + sizeof(PROCESS_MITIGATION_POLICY_INFORMATION), + NULL + ))) + { + if (policyInfo.StrictHandleCheckPolicy.Flags != 0) + { + strict = TRUE; + } + } + } + + if (critical && strict) + { + cont = PhShowConfirmMessage( + hWnd, + L"close", + L"critical process handle(s)", + L"You are about to close one or more handles for a critical process with strict handle checks enabled. This will shut down the operating system immediately.\r\n\r\n", + TRUE + ); + } + + if (!cont) + return FALSE; + + for (ULONG i = 0; i < NumberOfHandles; i++) + { + status = NtDuplicateObject( + processHandle, + Handles[i]->Handle, + NULL, + NULL, + 0, + 0, + DUPLICATE_CLOSE_SOURCE + ); + + if (!NT_SUCCESS(status)) + { + success = FALSE; + + if (!PhpShowErrorHandle( + hWnd, + L"close", + Handles[i], + status, + 0 + )) + break; + } + } + + NtClose(processHandle); + } + else + { + PhShowStatus(hWnd, L"Unable to open the process", status, 0); + return FALSE; + } + + return success; +} + +BOOLEAN PhUiSetAttributesHandle( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ PPH_HANDLE_ITEM Handle, + _In_ ULONG Attributes + ) +{ + NTSTATUS status; + HANDLE processHandle; + + if (!KphIsConnected()) + { + PhShowError2(hWnd, PH_KPH_ERROR_TITLE, PH_KPH_ERROR_MESSAGE); + return FALSE; + } + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + ProcessId + ))) + { + OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; + + handleFlagInfo.Inherit = !!(Attributes & OBJ_INHERIT); + handleFlagInfo.ProtectFromClose = !!(Attributes & OBJ_PROTECT_CLOSE); + + status = KphSetInformationObject( + processHandle, + Handle->Handle, + KphObjectHandleFlagInformation, + &handleFlagInfo, + sizeof(OBJECT_HANDLE_FLAG_INFORMATION) + ); + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + PhpShowErrorHandle(hWnd, L"set attributes of", Handle, status, 0); + return FALSE; + } + + return TRUE; +} diff --git a/ProcessHacker/affinity.c b/ProcessHacker/affinity.c index 9515cb6933ee..e3f121299122 100644 --- a/ProcessHacker/affinity.c +++ b/ProcessHacker/affinity.c @@ -1,317 +1,315 @@ -/* - * Process Hacker - - * process affinity editor - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The affinity dialog was originally created to support the modification - * of process affinity masks, but now supports modifying thread affinity - * and generic masks. - */ - -#include - -#include - -#include -#include - -typedef struct _AFFINITY_DIALOG_CONTEXT -{ - PPH_PROCESS_ITEM ProcessItem; - PPH_THREAD_ITEM ThreadItem; - ULONG_PTR AffinityMask; - ULONG_PTR NewAffinityMask; -} AFFINITY_DIALOG_CONTEXT, *PAFFINITY_DIALOG_CONTEXT; - -INT_PTR CALLBACK PhpProcessAffinityDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowProcessAffinityDialog( - _In_ HWND ParentWindowHandle, - _In_opt_ PPH_PROCESS_ITEM ProcessItem, - _In_opt_ PPH_THREAD_ITEM ThreadItem - ) -{ - AFFINITY_DIALOG_CONTEXT context; - - assert(!!ProcessItem != !!ThreadItem); // make sure we have one and not the other - - context.ProcessItem = ProcessItem; - context.ThreadItem = ThreadItem; - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_AFFINITY), - ParentWindowHandle, - PhpProcessAffinityDlgProc, - (LPARAM)&context - ); -} - -BOOLEAN PhShowProcessAffinityDialog2( - _In_ HWND ParentWindowHandle, - _In_ ULONG_PTR AffinityMask, - _Out_ PULONG_PTR NewAffinityMask - ) -{ - AFFINITY_DIALOG_CONTEXT context; - - context.ProcessItem = NULL; - context.ThreadItem = NULL; - context.AffinityMask = AffinityMask; - - if (DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_AFFINITY), - ParentWindowHandle, - PhpProcessAffinityDlgProc, - (LPARAM)&context - ) == IDOK) - { - *NewAffinityMask = context.NewAffinityMask; - - return TRUE; - } - else - { - return FALSE; - } -} - -static INT_PTR CALLBACK PhpProcessAffinityDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - PAFFINITY_DIALOG_CONTEXT context = (PAFFINITY_DIALOG_CONTEXT)lParam; - SYSTEM_BASIC_INFORMATION systemBasicInfo; - ULONG_PTR systemAffinityMask; - ULONG_PTR affinityMask; - ULONG i; - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - systemAffinityMask = 0; - - if (context->ProcessItem) - { - HANDLE processHandle; - PROCESS_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - context->ProcessItem->ProcessId - ))) - { - status = PhGetProcessBasicInformation(processHandle, &basicInfo); - - if (NT_SUCCESS(status)) - affinityMask = basicInfo.AffinityMask; - - NtClose(processHandle); - } - } - else if (context->ThreadItem) - { - HANDLE threadHandle; - THREAD_BASIC_INFORMATION basicInfo; - HANDLE processHandle; - PROCESS_BASIC_INFORMATION processBasicInfo; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - ThreadQueryAccess, - context->ThreadItem->ThreadId - ))) - { - status = PhGetThreadBasicInformation(threadHandle, &basicInfo); - - if (NT_SUCCESS(status)) - { - affinityMask = basicInfo.AffinityMask; - - // A thread's affinity mask is restricted by the process affinity mask, - // so use that as the system affinity mask. - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - ProcessQueryAccess, - basicInfo.ClientId.UniqueProcess - ))) - { - if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &processBasicInfo))) - systemAffinityMask = processBasicInfo.AffinityMask; - - NtClose(processHandle); - } - } - - NtClose(threadHandle); - } - } - else - { - affinityMask = context->AffinityMask; - status = STATUS_SUCCESS; - } - - if (NT_SUCCESS(status) && systemAffinityMask == 0) - { - status = NtQuerySystemInformation( - SystemBasicInformation, - &systemBasicInfo, - sizeof(SYSTEM_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - systemAffinityMask = systemBasicInfo.ActiveProcessorsAffinityMask; - } - - if (!NT_SUCCESS(status)) - { - PhShowStatus(hwndDlg, L"Unable to retrieve the affinity", status, 0); - EndDialog(hwndDlg, IDCANCEL); - break; - } - - // Disable the CPU checkboxes which aren't part of the system affinity mask, - // and check the CPU checkboxes which are part of the affinity mask. - - for (i = 0; i < 8 * 8; i++) - { - if ((i < sizeof(ULONG_PTR) * 8) && ((systemAffinityMask >> i) & 0x1)) - { - if ((affinityMask >> i) & 0x1) - { - Button_SetCheck(GetDlgItem(hwndDlg, IDC_CPU0 + i), BST_CHECKED); - } - } - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_CPU0 + i), FALSE); - } - } - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - NTSTATUS status; - PAFFINITY_DIALOG_CONTEXT context = (PAFFINITY_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - ULONG i; - ULONG_PTR affinityMask; - - // Work out the affinity mask. - - affinityMask = 0; - - for (i = 0; i < sizeof(ULONG_PTR) * 8; i++) - { - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_CPU0 + i)) == BST_CHECKED) - affinityMask |= (ULONG_PTR)1 << i; - } - - if (context->ProcessItem) - { - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - context->ProcessItem->ProcessId - ))) - { - status = PhSetProcessAffinityMask(processHandle, affinityMask); - NtClose(processHandle); - } - } - else if (context->ThreadItem) - { - HANDLE threadHandle; - - if (NT_SUCCESS(status = PhOpenThread( - &threadHandle, - ThreadSetAccess, - context->ThreadItem->ThreadId - ))) - { - status = PhSetThreadAffinityMask(threadHandle, affinityMask); - NtClose(threadHandle); - } - } - else - { - context->NewAffinityMask = affinityMask; - status = STATUS_SUCCESS; - } - - if (NT_SUCCESS(status)) - EndDialog(hwndDlg, IDOK); - else - PhShowStatus(hwndDlg, L"Unable to set the affinity", status, 0); - } - break; - case IDC_SELECTALL: - case IDC_DESELECTALL: - { - ULONG i; - - for (i = 0; i < sizeof(ULONG_PTR) * 8; i++) - { - HWND checkBox = GetDlgItem(hwndDlg, IDC_CPU0 + i); - - if (IsWindowEnabled(checkBox)) - Button_SetCheck(checkBox, LOWORD(wParam) == IDC_SELECTALL ? BST_CHECKED : BST_UNCHECKED); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * process affinity editor + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The affinity dialog was originally created to support the modification + * of process affinity masks, but now supports modifying thread affinity + * and generic masks. + */ + +#include + +#include +#include + +typedef struct _AFFINITY_DIALOG_CONTEXT +{ + PPH_PROCESS_ITEM ProcessItem; + PPH_THREAD_ITEM ThreadItem; + ULONG_PTR AffinityMask; + ULONG_PTR NewAffinityMask; +} AFFINITY_DIALOG_CONTEXT, *PAFFINITY_DIALOG_CONTEXT; + +INT_PTR CALLBACK PhpProcessAffinityDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowProcessAffinityDialog( + _In_ HWND ParentWindowHandle, + _In_opt_ PPH_PROCESS_ITEM ProcessItem, + _In_opt_ PPH_THREAD_ITEM ThreadItem + ) +{ + AFFINITY_DIALOG_CONTEXT context; + + assert(!!ProcessItem != !!ThreadItem); // make sure we have one and not the other + + context.ProcessItem = ProcessItem; + context.ThreadItem = ThreadItem; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_AFFINITY), + ParentWindowHandle, + PhpProcessAffinityDlgProc, + (LPARAM)&context + ); +} + +BOOLEAN PhShowProcessAffinityDialog2( + _In_ HWND ParentWindowHandle, + _In_ ULONG_PTR AffinityMask, + _Out_ PULONG_PTR NewAffinityMask + ) +{ + AFFINITY_DIALOG_CONTEXT context; + + context.ProcessItem = NULL; + context.ThreadItem = NULL; + context.AffinityMask = AffinityMask; + + if (DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_AFFINITY), + ParentWindowHandle, + PhpProcessAffinityDlgProc, + (LPARAM)&context + ) == IDOK) + { + *NewAffinityMask = context.NewAffinityMask; + + return TRUE; + } + else + { + return FALSE; + } +} + +static INT_PTR CALLBACK PhpProcessAffinityDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + PAFFINITY_DIALOG_CONTEXT context = (PAFFINITY_DIALOG_CONTEXT)lParam; + SYSTEM_BASIC_INFORMATION systemBasicInfo; + ULONG_PTR systemAffinityMask; + ULONG_PTR affinityMask; + ULONG i; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + systemAffinityMask = 0; + + if (context->ProcessItem) + { + HANDLE processHandle; + PROCESS_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + context->ProcessItem->ProcessId + ))) + { + status = PhGetProcessBasicInformation(processHandle, &basicInfo); + + if (NT_SUCCESS(status)) + affinityMask = basicInfo.AffinityMask; + + NtClose(processHandle); + } + } + else if (context->ThreadItem) + { + HANDLE threadHandle; + THREAD_BASIC_INFORMATION basicInfo; + HANDLE processHandle; + PROCESS_BASIC_INFORMATION processBasicInfo; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_QUERY_LIMITED_INFORMATION, + context->ThreadItem->ThreadId + ))) + { + status = PhGetThreadBasicInformation(threadHandle, &basicInfo); + + if (NT_SUCCESS(status)) + { + affinityMask = basicInfo.AffinityMask; + + // A thread's affinity mask is restricted by the process affinity mask, + // so use that as the system affinity mask. + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + basicInfo.ClientId.UniqueProcess + ))) + { + if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &processBasicInfo))) + systemAffinityMask = processBasicInfo.AffinityMask; + + NtClose(processHandle); + } + } + + NtClose(threadHandle); + } + } + else + { + affinityMask = context->AffinityMask; + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status) && systemAffinityMask == 0) + { + status = NtQuerySystemInformation( + SystemBasicInformation, + &systemBasicInfo, + sizeof(SYSTEM_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + systemAffinityMask = systemBasicInfo.ActiveProcessorsAffinityMask; + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus(hwndDlg, L"Unable to retrieve the affinity", status, 0); + EndDialog(hwndDlg, IDCANCEL); + break; + } + + // Disable the CPU checkboxes which aren't part of the system affinity mask, + // and check the CPU checkboxes which are part of the affinity mask. + + for (i = 0; i < 8 * 8; i++) + { + if ((i < sizeof(ULONG_PTR) * 8) && ((systemAffinityMask >> i) & 0x1)) + { + if ((affinityMask >> i) & 0x1) + { + Button_SetCheck(GetDlgItem(hwndDlg, IDC_CPU0 + i), BST_CHECKED); + } + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_CPU0 + i), FALSE); + } + } + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + NTSTATUS status; + PAFFINITY_DIALOG_CONTEXT context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + ULONG i; + ULONG_PTR affinityMask; + + // Work out the affinity mask. + + affinityMask = 0; + + for (i = 0; i < sizeof(ULONG_PTR) * 8; i++) + { + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_CPU0 + i)) == BST_CHECKED) + affinityMask |= (ULONG_PTR)1 << i; + } + + if (context->ProcessItem) + { + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + context->ProcessItem->ProcessId + ))) + { + status = PhSetProcessAffinityMask(processHandle, affinityMask); + NtClose(processHandle); + } + } + else if (context->ThreadItem) + { + HANDLE threadHandle; + + if (NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_SET_LIMITED_INFORMATION, + context->ThreadItem->ThreadId + ))) + { + status = PhSetThreadAffinityMask(threadHandle, affinityMask); + NtClose(threadHandle); + } + } + else + { + context->NewAffinityMask = affinityMask; + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)) + EndDialog(hwndDlg, IDOK); + else + PhShowStatus(hwndDlg, L"Unable to set the affinity", status, 0); + } + break; + case IDC_SELECTALL: + case IDC_DESELECTALL: + { + ULONG i; + + for (i = 0; i < sizeof(ULONG_PTR) * 8; i++) + { + HWND checkBox = GetDlgItem(hwndDlg, IDC_CPU0 + i); + + if (IsWindowEnabled(checkBox)) + Button_SetCheck(checkBox, GET_WM_COMMAND_ID(wParam, lParam) == IDC_SELECTALL ? BST_CHECKED : BST_UNCHECKED); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/anawait.c b/ProcessHacker/anawait.c index 63f355a0e008..f32333f9b624 100644 --- a/ProcessHacker/anawait.c +++ b/ProcessHacker/anawait.c @@ -1,1076 +1,1046 @@ -/* - * Process Hacker - - * thread wait analysis - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * There are two ways of seeing what a thread is waiting on. The first method - * is to walk the stack of a thread and read the arguments to whatever system - * call it is blocking on; this only works on x86 because on x64 the arguments - * are passed in registers (at least the first four are). The second method - * involves using the ThreadLastSystemCall info class for NtQueryInformationThread - * to retrieve the first argument to the system call the thread is blocking on. - * This is obviously only useful for NtWaitForSingleObject. - * - * There are other methods for specific scenarios, like USER messages and ALPC - * calls. - */ - -#include - -#include -#include - -#include - -typedef HWND (WINAPI *_GetSendMessageReceiver)( - _In_ HANDLE ThreadId - ); - -typedef NTSTATUS (NTAPI *_NtAlpcQueryInformation)( - _In_ HANDLE PortHandle, - _In_ ALPC_PORT_INFORMATION_CLASS PortInformationClass, - _Out_writes_bytes_(Length) PVOID PortInformation, - _In_ ULONG Length, - _Out_opt_ PULONG ReturnLength - ); - -typedef struct _ANALYZE_WAIT_CONTEXT -{ - BOOLEAN Found; - BOOLEAN IsWow64; - HANDLE ProcessId; - HANDLE ThreadId; - HANDLE ProcessHandle; - - PPH_SYMBOL_PROVIDER SymbolProvider; - PH_STRING_BUILDER StringBuilder; - - PVOID PrevParams[4]; -} ANALYZE_WAIT_CONTEXT, *PANALYZE_WAIT_CONTEXT; - -VOID PhpAnalyzeWaitPassive( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId - ); - -BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback( - _In_ PPH_THREAD_STACK_FRAME StackFrame, - _In_opt_ PVOID Context - ); - -VOID PhpAnalyzeWaitFallbacks( - _In_ PANALYZE_WAIT_CONTEXT Context - ); - -VOID PhpInitializeServiceNumbers( - VOID - ); - -PPH_STRING PhpaGetHandleString( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle - ); - -VOID PhpGetWfmoInformation( - _In_ HANDLE ProcessHandle, - _In_ BOOLEAN IsWow64, - _In_ ULONG NumberOfHandles, - _In_ PHANDLE AddressOfHandles, - _In_ WAIT_TYPE WaitType, - _In_ BOOLEAN Alertable, - _Inout_ PPH_STRING_BUILDER StringBuilder - ); - -PPH_STRING PhpaGetSendMessageReceiver( - _In_ HANDLE ThreadId - ); - -PPH_STRING PhpaGetAlpcInformation( - _In_ HANDLE ThreadId - ); - -static PH_INITONCE ServiceNumbersInitOnce = PH_INITONCE_INIT; -static USHORT NumberForWfso = -1; -static USHORT NumberForWfmo = -1; -static USHORT NumberForRf = -1; - -VOID PhUiAnalyzeWaitThread( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId, - _In_ PPH_SYMBOL_PROVIDER SymbolProvider - ) -{ - NTSTATUS status; - HANDLE threadHandle; -#ifdef _WIN64 - HANDLE processHandle; - BOOLEAN isWow64; -#endif - CLIENT_ID clientId; - ANALYZE_WAIT_CONTEXT context; - -#ifdef _WIN64 - // Determine if the process is WOW64. If not, we use the passive method. - - if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessId))) - { - PhShowStatus(hWnd, L"Unable to open the process", status, 0); - return; - } - - if (!NT_SUCCESS(status = PhGetProcessIsWow64(processHandle, &isWow64)) || !isWow64) - { - PhpAnalyzeWaitPassive(hWnd, ProcessId, ThreadId); - return; - } - - NtClose(processHandle); -#endif - - if (!NT_SUCCESS(status = PhOpenThread( - &threadHandle, - ThreadQueryAccess | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, - ThreadId - ))) - { - PhShowStatus(hWnd, L"Unable to open the thread", status, 0); - return; - } - - context.ProcessId = ProcessId; - context.ThreadId = ThreadId; - - context.ProcessHandle = SymbolProvider->ProcessHandle; - context.SymbolProvider = SymbolProvider; - PhInitializeStringBuilder(&context.StringBuilder, 100); - - clientId.UniqueProcess = ProcessId; - clientId.UniqueThread = ThreadId; - - PhWalkThreadStack( - threadHandle, - SymbolProvider->ProcessHandle, - &clientId, - SymbolProvider, - PH_WALK_I386_STACK, - PhpWalkThreadStackAnalyzeCallback, - &context - ); - NtClose(threadHandle); - - PhpAnalyzeWaitFallbacks(&context); - - if (context.Found) - { - PhShowInformationDialog(hWnd, context.StringBuilder.String->Buffer, 0); - } - else - { - PhShowInformation(hWnd, L"The thread does not appear to be waiting."); - } - - PhDeleteStringBuilder(&context.StringBuilder); -} - -VOID PhpAnalyzeWaitPassive( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId - ) -{ - NTSTATUS status; - HANDLE processHandle; - HANDLE threadHandle; - THREAD_LAST_SYSCALL_INFORMATION lastSystemCall; - PH_STRING_BUILDER stringBuilder; - PPH_STRING string; - - PhpInitializeServiceNumbers(); - - if (!NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, ThreadId))) - { - PhShowStatus(hWnd, L"Unable to open the thread", status, 0); - return; - } - - if (!NT_SUCCESS(status = NtQueryInformationThread( - threadHandle, - ThreadLastSystemCall, - &lastSystemCall, - sizeof(THREAD_LAST_SYSCALL_INFORMATION), - NULL - ))) - { - NtClose(threadHandle); - PhShowInformation(hWnd, L"Unable to determine whether the thread is waiting."); - return; - } - - if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE, ProcessId))) - { - NtClose(threadHandle); - PhShowStatus(hWnd, L"Unable to open the process", status, 0); - return; - } - - PhInitializeStringBuilder(&stringBuilder, 100); - - if (lastSystemCall.SystemCallNumber == NumberForWfso) - { - string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument); - - PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for:\r\n"); - PhAppendStringBuilder(&stringBuilder, &string->sr); - } - else if (lastSystemCall.SystemCallNumber == NumberForWfmo) - { - PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for multiple (%u) objects.", PtrToUlong(lastSystemCall.FirstArgument)); - } - else if (lastSystemCall.SystemCallNumber == NumberForRf) - { - string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument); - - PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for file I/O:\r\n"); - PhAppendStringBuilder(&stringBuilder, &string->sr); - } - else - { - string = PhpaGetSendMessageReceiver(ThreadId); - - if (string) - { - PhAppendStringBuilder2(&stringBuilder, L"Thread is sending a USER message:\r\n"); - PhAppendStringBuilder(&stringBuilder, &string->sr); - } - else - { - string = PhpaGetAlpcInformation(ThreadId); - - if (string) - { - PhAppendStringBuilder2(&stringBuilder, L"Thread is waiting for an ALPC port:\r\n"); - PhAppendStringBuilder(&stringBuilder, &string->sr); - } - } - } - - if (stringBuilder.String->Length == 0) - PhAppendStringBuilder2(&stringBuilder, L"Unable to determine why the thread is waiting."); - - PhShowInformationDialog(hWnd, stringBuilder.String->Buffer, 0); - - PhDeleteStringBuilder(&stringBuilder); - NtClose(processHandle); - NtClose(threadHandle); -} - -static BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback( - _In_ PPH_THREAD_STACK_FRAME StackFrame, - _In_opt_ PVOID Context - ) -{ - PANALYZE_WAIT_CONTEXT context = (PANALYZE_WAIT_CONTEXT)Context; - PPH_STRING name; - - name = PhGetSymbolFromAddress( - context->SymbolProvider, - (ULONG64)StackFrame->PcAddress, - NULL, - NULL, - NULL, - NULL - ); - - if (!name) - return TRUE; - - context->Found = TRUE; - -#define FUNC_MATCH(Name) PhStartsWithString2(name, L##Name, TRUE) -#define NT_FUNC_MATCH(Name) ( \ - PhStartsWithString2(name, L"ntdll.dll!Nt" L##Name, TRUE) || \ - PhStartsWithString2(name, L"ntdll.dll!Zw" L##Name, TRUE) \ - ) - - if (!name) - { - // Dummy - } - else if (FUNC_MATCH("kernel32.dll!Sleep")) - { - PhAppendFormatStringBuilder( - &context->StringBuilder, - L"Thread is sleeping. Timeout: %u milliseconds.", - PtrToUlong(StackFrame->Params[0]) - ); - } - else if (NT_FUNC_MATCH("DelayExecution")) - { - BOOLEAN alertable = !!StackFrame->Params[0]; - PVOID timeoutAddress = StackFrame->Params[1]; - LARGE_INTEGER timeout; - - if (NT_SUCCESS(NtReadVirtualMemory( - context->ProcessHandle, - timeoutAddress, - &timeout, - sizeof(LARGE_INTEGER), - NULL - ))) - { - if (timeout.QuadPart < 0) - { - PhAppendFormatStringBuilder( - &context->StringBuilder, - L"Thread is sleeping. Timeout: %I64u milliseconds.", - -timeout.QuadPart / PH_TIMEOUT_MS - ); - } - else - { - // TODO - } - } - else - { - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is sleeping." - ); - } - } - else if (NT_FUNC_MATCH("DeviceIoControlFile")) - { - HANDLE handle = (HANDLE)StackFrame->Params[0]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for an I/O control request:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if (NT_FUNC_MATCH("FsControlFile")) - { - HANDLE handle = StackFrame->Params[0]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for a FS control request:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if (NT_FUNC_MATCH("QueryObject")) - { - HANDLE handle = StackFrame->Params[0]; - - // Use the KiFastSystemCall args if the handle we have is wrong. - if ((ULONG_PTR)handle % 4 != 0 || !handle) - handle = context->PrevParams[1]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is querying an object:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if (NT_FUNC_MATCH("ReadFile") || NT_FUNC_MATCH("WriteFile")) - { - HANDLE handle = StackFrame->Params[0]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for file I/O:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if (NT_FUNC_MATCH("RemoveIoCompletion")) - { - HANDLE handle = StackFrame->Params[0]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for an I/O completion port:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if ( - NT_FUNC_MATCH("ReplyWaitReceivePort") || - NT_FUNC_MATCH("RequestWaitReplyPort") || - NT_FUNC_MATCH("AlpcSendWaitReceivePort") - ) - { - HANDLE handle = StackFrame->Params[0]; - PPH_STRING alpcInfo; - - PhAppendStringBuilder2( - &context->StringBuilder, - WindowsVersion >= WINDOWS_VISTA ? L"Thread is waiting for an ALPC port:\r\n" : L"Thread is waiting for a LPC port:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - - if (alpcInfo = PhpaGetAlpcInformation(context->ThreadId)) - { - PhAppendStringBuilder2( - &context->StringBuilder, - L"\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &alpcInfo->sr - ); - } - } - else if ( - NT_FUNC_MATCH("SetHighWaitLowEventPair") || - NT_FUNC_MATCH("SetLowWaitHighEventPair") || - NT_FUNC_MATCH("WaitHighEventPair") || - NT_FUNC_MATCH("WaitLowEventPair") - ) - { - HANDLE handle = StackFrame->Params[0]; - - if ((ULONG_PTR)handle % 4 != 0 || !handle) - handle = context->PrevParams[1]; - - PhAppendFormatStringBuilder( - &context->StringBuilder, - L"Thread is waiting (%s) for an event pair:\r\n", - name->Buffer - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if ( - FUNC_MATCH("user32.dll!NtUserGetMessage") || - FUNC_MATCH("user32.dll!NtUserWaitMessage") - ) - { - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for a USER message.\r\n" - ); - } - else if (FUNC_MATCH("user32.dll!NtUserMessageCall")) - { - PPH_STRING receiverString; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is sending a USER message:\r\n" - ); - - receiverString = PhpaGetSendMessageReceiver(context->ThreadId); - - if (receiverString) - { - PhAppendStringBuilder(&context->StringBuilder, &receiverString->sr); - PhAppendStringBuilder2(&context->StringBuilder, L"\r\n"); - } - else - { - PhAppendStringBuilder2(&context->StringBuilder, L"Unknown.\r\n"); - } - } - else if (NT_FUNC_MATCH("WaitForDebugEvent")) - { - HANDLE handle = StackFrame->Params[0]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for a debug event:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if ( - NT_FUNC_MATCH("WaitForKeyedEvent") || - NT_FUNC_MATCH("ReleaseKeyedEvent") - ) - { - HANDLE handle = StackFrame->Params[0]; - PVOID key = StackFrame->Params[1]; - - PhAppendFormatStringBuilder( - &context->StringBuilder, - L"Thread is waiting (%s) for a keyed event (key 0x%Ix):\r\n", - name->Buffer, - key - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if ( - NT_FUNC_MATCH("WaitForMultipleObjects") || - FUNC_MATCH("kernel32.dll!WaitForMultipleObjects") - ) - { - ULONG numberOfHandles = PtrToUlong(StackFrame->Params[0]); - PVOID addressOfHandles = StackFrame->Params[1]; - WAIT_TYPE waitType = (WAIT_TYPE)StackFrame->Params[2]; - BOOLEAN alertable = !!StackFrame->Params[3]; - - if (numberOfHandles > MAXIMUM_WAIT_OBJECTS) - { - numberOfHandles = PtrToUlong(context->PrevParams[1]); - addressOfHandles = context->PrevParams[2]; - waitType = (WAIT_TYPE)context->PrevParams[3]; - alertable = FALSE; - } - - PhpGetWfmoInformation( - context->ProcessHandle, - TRUE, // on x64 this function is only called for WOW64 processes - numberOfHandles, - addressOfHandles, - waitType, - alertable, - &context->StringBuilder - ); - } - else if ( - NT_FUNC_MATCH("WaitForSingleObject") || - FUNC_MATCH("kernel32.dll!WaitForSingleObject") - ) - { - HANDLE handle = StackFrame->Params[0]; - BOOLEAN alertable = !!StackFrame->Params[1]; - - if ((ULONG_PTR)handle % 4 != 0 || !handle) - { - handle = context->PrevParams[1]; - alertable = !!context->PrevParams[2]; - } - - PhAppendFormatStringBuilder( - &context->StringBuilder, - L"Thread is waiting (%s) for:\r\n", - alertable ? L"alertable" : L"non-alertable" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else if (NT_FUNC_MATCH("WaitForWorkViaWorkerFactory")) - { - HANDLE handle = StackFrame->Params[0]; - - PhAppendStringBuilder2( - &context->StringBuilder, - L"Thread is waiting for work from a worker factory:\r\n" - ); - PhAppendStringBuilder( - &context->StringBuilder, - &PhpaGetHandleString(context->ProcessHandle, handle)->sr - ); - } - else - { - context->Found = FALSE; - } - - PhDereferenceObject(name); - memcpy(&context->PrevParams, StackFrame->Params, sizeof(StackFrame->Params)); - - return !context->Found; -} - -static VOID PhpAnalyzeWaitFallbacks( - _In_ PANALYZE_WAIT_CONTEXT Context - ) -{ - PPH_STRING info; - - // We didn't detect NtUserMessageCall, but this may still apply due to another - // win32k system call (e.g. from EnableWindow). - if (!Context->Found && (info = PhpaGetSendMessageReceiver(Context->ThreadId))) - { - PhAppendStringBuilder2( - &Context->StringBuilder, - L"Thread is sending a USER message:\r\n" - ); - PhAppendStringBuilder(&Context->StringBuilder, &info->sr); - PhAppendStringBuilder2(&Context->StringBuilder, L"\r\n"); - - Context->Found = TRUE; - } - - // Nt(Alpc)ConnectPort doesn't get detected anywhere else. - if (!Context->Found && (info = PhpaGetAlpcInformation(Context->ThreadId))) - { - PhAppendStringBuilder2( - &Context->StringBuilder, - L"Thread is waiting for an ALPC port:\r\n" - ); - PhAppendStringBuilder(&Context->StringBuilder, &info->sr); - PhAppendStringBuilder2(&Context->StringBuilder, L"\r\n"); - - Context->Found = TRUE; - } -} - -static BOOLEAN PhpWaitUntilThreadIsWaiting( - _In_ HANDLE ThreadHandle - ) -{ - ULONG attempts; - BOOLEAN isWaiting = FALSE; - THREAD_BASIC_INFORMATION basicInfo; - - if (!NT_SUCCESS(PhGetThreadBasicInformation(ThreadHandle, &basicInfo))) - return FALSE; - - for (attempts = 0; attempts < 5; attempts++) - { - PVOID processes; - PSYSTEM_PROCESS_INFORMATION processInfo; - ULONG i; - LARGE_INTEGER interval; - - interval.QuadPart = -100 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); - - if (!NT_SUCCESS(PhEnumProcesses(&processes))) - break; - - processInfo = PhFindProcessInformation(processes, basicInfo.ClientId.UniqueProcess); - - if (processInfo) - { - for (i = 0; i < processInfo->NumberOfThreads; i++) - { - if ( - processInfo->Threads[i].ClientId.UniqueThread == basicInfo.ClientId.UniqueThread && - processInfo->Threads[i].ThreadState == Waiting && - (processInfo->Threads[i].WaitReason == UserRequest || - processInfo->Threads[i].WaitReason == Executive) - ) - { - isWaiting = TRUE; - break; - } - } - } - - PhFree(processes); - - if (isWaiting) - break; - - interval.QuadPart = -500 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); - } - - return isWaiting; -} - -static VOID PhpGetThreadLastSystemCallNumber( - _In_ HANDLE ThreadHandle, - _Out_ PUSHORT LastSystemCallNumber - ) -{ - THREAD_LAST_SYSCALL_INFORMATION lastSystemCall; - - if (NT_SUCCESS(NtQueryInformationThread( - ThreadHandle, - ThreadLastSystemCall, - &lastSystemCall, - sizeof(THREAD_LAST_SYSCALL_INFORMATION), - NULL - ))) - { - *LastSystemCallNumber = lastSystemCall.SystemCallNumber; - } -} - -static NTSTATUS PhpWfsoThreadStart( - _In_ PVOID Parameter - ) -{ - HANDLE eventHandle; - LARGE_INTEGER timeout; - - eventHandle = Parameter; - - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - NtWaitForSingleObject(eventHandle, FALSE, &timeout); - - return STATUS_SUCCESS; -} - -static NTSTATUS PhpWfmoThreadStart( - _In_ PVOID Parameter - ) -{ - HANDLE eventHandle; - LARGE_INTEGER timeout; - - eventHandle = Parameter; - - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - NtWaitForMultipleObjects(1, &eventHandle, WaitAll, FALSE, &timeout); - - return STATUS_SUCCESS; -} - -static NTSTATUS PhpRfThreadStart( - _In_ PVOID Parameter - ) -{ - HANDLE fileHandle; - IO_STATUS_BLOCK isb; - ULONG data; - - fileHandle = Parameter; - - NtReadFile(fileHandle, NULL, NULL, NULL, &isb, &data, sizeof(ULONG), NULL, NULL); - - return STATUS_SUCCESS; -} - -static VOID PhpInitializeServiceNumbers( - VOID - ) -{ - if (PhBeginInitOnce(&ServiceNumbersInitOnce)) - { - NTSTATUS status; - HANDLE eventHandle; - HANDLE threadHandle; - HANDLE pipeReadHandle; - HANDLE pipeWriteHandle; - - // The ThreadLastSystemCall info class only works when the thread is in the Waiting - // state. We'll create a thread which blocks on an event object we create, then wait - // until it is in the Waiting state. Only then can we query the thread using - // ThreadLastSystemCall. - - // NtWaitForSingleObject - - status = NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); - - if (NT_SUCCESS(status)) - { - if (threadHandle = PhCreateThread(0, PhpWfsoThreadStart, eventHandle)) - { - if (PhpWaitUntilThreadIsWaiting(threadHandle)) - { - PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfso); - } - - // Allow the thread to exit. - NtSetEvent(eventHandle, NULL); - NtClose(threadHandle); - } - - NtClose(eventHandle); - } - - // NtWaitForMultipleObjects - - status = NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); - - if (NT_SUCCESS(status)) - { - if (threadHandle = PhCreateThread(0, PhpWfmoThreadStart, eventHandle)) - { - if (PhpWaitUntilThreadIsWaiting(threadHandle)) - { - PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfmo); - } - - NtSetEvent(eventHandle, NULL); - NtClose(threadHandle); - } - - NtClose(eventHandle); - } - - // NtReadFile - - if (CreatePipe(&pipeReadHandle, &pipeWriteHandle, NULL, 0)) - { - if (threadHandle = PhCreateThread(0, PhpRfThreadStart, pipeReadHandle)) - { - ULONG data = 0; - IO_STATUS_BLOCK isb; - - if (PhpWaitUntilThreadIsWaiting(threadHandle)) - { - PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForRf); - } - - NtWriteFile(pipeWriteHandle, NULL, NULL, NULL, &isb, &data, sizeof(data), NULL, NULL); - NtClose(threadHandle); - } - - NtClose(pipeReadHandle); - NtClose(pipeWriteHandle); - } - - PhEndInitOnce(&ServiceNumbersInitOnce); - } -} - -static PPH_STRING PhpaGetHandleString( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle - ) -{ - PPH_STRING typeName = NULL; - PPH_STRING name = NULL; - PPH_STRING result; - - PhGetHandleInformation( - ProcessHandle, - Handle, - -1, - NULL, - &typeName, - NULL, - &name - ); - PH_AUTO(typeName); - PH_AUTO(name); - - if (typeName && name) - { - result = PhaFormatString( - L"Handle 0x%Ix (%s): %s", - Handle, - typeName->Buffer, - !PhIsNullOrEmptyString(name) ? name->Buffer : L"(unnamed object)" - ); - } - else - { - result = PhaFormatString( - L"Handle 0x%Ix: (error querying handle)", - Handle - ); - } - - return result; -} - -static VOID PhpGetWfmoInformation( - _In_ HANDLE ProcessHandle, - _In_ BOOLEAN IsWow64, - _In_ ULONG NumberOfHandles, - _In_ PHANDLE AddressOfHandles, - _In_ WAIT_TYPE WaitType, - _In_ BOOLEAN Alertable, - _Inout_ PPH_STRING_BUILDER StringBuilder - ) -{ - NTSTATUS status; - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - ULONG i; - - status = STATUS_SUCCESS; - - if (NumberOfHandles <= MAXIMUM_WAIT_OBJECTS) - { -#ifdef _WIN64 - if (IsWow64) - { - ULONG handles32[MAXIMUM_WAIT_OBJECTS]; - - if (NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - AddressOfHandles, - handles32, - NumberOfHandles * sizeof(ULONG), - NULL - ))) - { - for (i = 0; i < NumberOfHandles; i++) - handles[i] = UlongToHandle(handles32[i]); - } - } - else - { -#endif - status = NtReadVirtualMemory( - ProcessHandle, - AddressOfHandles, - handles, - NumberOfHandles * sizeof(HANDLE), - NULL - ); -#ifdef _WIN64 - } -#endif - - if (NT_SUCCESS(status)) - { - PhAppendFormatStringBuilder( - StringBuilder, - L"Thread is waiting (%s, %s) for:\r\n", - Alertable ? L"alertable" : L"non-alertable", - WaitType == WaitAll ? L"wait all" : L"wait any" - ); - - for (i = 0; i < NumberOfHandles; i++) - { - PhAppendStringBuilder( - StringBuilder, - &PhpaGetHandleString(ProcessHandle, handles[i])->sr - ); - PhAppendStringBuilder2( - StringBuilder, - L"\r\n" - ); - } - } - } - - if (!NT_SUCCESS(status) || NumberOfHandles > MAXIMUM_WAIT_OBJECTS) - { - PhAppendStringBuilder2( - StringBuilder, - L"Thread is waiting for multiple objects." - ); - } -} - -static PPH_STRING PhpaGetSendMessageReceiver( - _In_ HANDLE ThreadId - ) -{ - static _GetSendMessageReceiver GetSendMessageReceiver_I; - - HWND windowHandle; - ULONG threadId; - ULONG processId; - CLIENT_ID clientId; - PPH_STRING clientIdName; - WCHAR windowClass[64]; - PPH_STRING windowText; - - // GetSendMessageReceiver is an undocumented function exported by - // user32.dll. It retrieves the handle of the window which a thread - // is sending a message to. - - if (!GetSendMessageReceiver_I) - GetSendMessageReceiver_I = PhGetModuleProcAddress(L"user32.dll", "GetSendMessageReceiver"); - - if (!GetSendMessageReceiver_I) - return NULL; - - windowHandle = GetSendMessageReceiver_I(ThreadId); - - if (!windowHandle) - return NULL; - - threadId = GetWindowThreadProcessId(windowHandle, &processId); - - clientId.UniqueProcess = UlongToHandle(processId); - clientId.UniqueThread = UlongToHandle(threadId); - clientIdName = PH_AUTO(PhGetClientIdName(&clientId)); - - if (!GetClassName(windowHandle, windowClass, sizeof(windowClass) / sizeof(WCHAR))) - windowClass[0] = 0; - - windowText = PH_AUTO(PhGetWindowText(windowHandle)); - - return PhaFormatString(L"Window 0x%Ix (%s): %s \"%s\"", windowHandle, clientIdName->Buffer, windowClass, PhGetStringOrEmpty(windowText)); -} - -static PPH_STRING PhpaGetAlpcInformation( - _In_ HANDLE ThreadId - ) -{ - static _NtAlpcQueryInformation NtAlpcQueryInformation_I; - - NTSTATUS status; - PPH_STRING string = NULL; - HANDLE threadHandle; - PALPC_SERVER_INFORMATION serverInfo; - ULONG bufferLength; - - if (!NtAlpcQueryInformation_I) - NtAlpcQueryInformation_I = PhGetModuleProcAddress(L"ntdll.dll", "NtAlpcQueryInformation"); - - if (!NtAlpcQueryInformation_I) - return NULL; - - if (!NT_SUCCESS(PhOpenThread(&threadHandle, THREAD_QUERY_INFORMATION, ThreadId))) - return NULL; - - bufferLength = 0x110; - serverInfo = PhAllocate(bufferLength); - serverInfo->In.ThreadHandle = threadHandle; - - status = NtAlpcQueryInformation_I(NULL, AlpcServerInformation, serverInfo, bufferLength, &bufferLength); - - if (status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(serverInfo); - serverInfo = PhAllocate(bufferLength); - serverInfo->In.ThreadHandle = threadHandle; - - status = NtAlpcQueryInformation_I(NULL, AlpcServerInformation, serverInfo, bufferLength, &bufferLength); - } - - if (NT_SUCCESS(status) && serverInfo->Out.ThreadBlocked) - { - CLIENT_ID clientId; - PPH_STRING clientIdName; - - clientId.UniqueProcess = serverInfo->Out.ConnectedProcessId; - clientId.UniqueThread = NULL; - clientIdName = PH_AUTO(PhGetClientIdName(&clientId)); - - string = PhaFormatString(L"ALPC Port: %.*s (%s)", serverInfo->Out.ConnectionPortName.Length / 2, serverInfo->Out.ConnectionPortName.Buffer, clientIdName->Buffer); - } - - PhFree(serverInfo); - NtClose(threadHandle); - - return string; -} +/* + * Process Hacker - + * thread wait analysis + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * There are two ways of seeing what a thread is waiting on. The first method + * is to walk the stack of a thread and read the arguments to whatever system + * call it is blocking on; this only works on x86 because on x64 the arguments + * are passed in registers (at least the first four are). The second method + * involves using the ThreadLastSystemCall info class for NtQueryInformationThread + * to retrieve the first argument to the system call the thread is blocking on. + * This is obviously only useful for NtWaitForSingleObject. + * + * There are other methods for specific scenarios, like USER messages and ALPC + * calls. + */ + +#include + +#include +#include + +#include + +typedef struct _ANALYZE_WAIT_CONTEXT +{ + BOOLEAN Found; + BOOLEAN IsWow64; + HANDLE ProcessId; + HANDLE ThreadId; + HANDLE ProcessHandle; + + PPH_SYMBOL_PROVIDER SymbolProvider; + PH_STRING_BUILDER StringBuilder; + + PVOID PrevParams[4]; +} ANALYZE_WAIT_CONTEXT, *PANALYZE_WAIT_CONTEXT; + +VOID PhpAnalyzeWaitPassive( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId + ); + +BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback( + _In_ PPH_THREAD_STACK_FRAME StackFrame, + _In_opt_ PVOID Context + ); + +VOID PhpAnalyzeWaitFallbacks( + _In_ PANALYZE_WAIT_CONTEXT Context + ); + +VOID PhpInitializeServiceNumbers( + VOID + ); + +PPH_STRING PhpaGetHandleString( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle + ); + +VOID PhpGetWfmoInformation( + _In_ HANDLE ProcessHandle, + _In_ BOOLEAN IsWow64, + _In_ ULONG NumberOfHandles, + _In_ PHANDLE AddressOfHandles, + _In_ WAIT_TYPE WaitType, + _In_ BOOLEAN Alertable, + _Inout_ PPH_STRING_BUILDER StringBuilder + ); + +PPH_STRING PhpaGetSendMessageReceiver( + _In_ HANDLE ThreadId + ); + +PPH_STRING PhpaGetAlpcInformation( + _In_ HANDLE ThreadId + ); + +static PH_INITONCE ServiceNumbersInitOnce = PH_INITONCE_INIT; +static USHORT NumberForWfso = USHRT_MAX; +static USHORT NumberForWfmo = USHRT_MAX; +static USHORT NumberForRf = USHRT_MAX; + +VOID PhUiAnalyzeWaitThread( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId, + _In_ PPH_SYMBOL_PROVIDER SymbolProvider + ) +{ + NTSTATUS status; + HANDLE threadHandle; +#ifdef _WIN64 + HANDLE processHandle; + BOOLEAN isWow64; +#endif + CLIENT_ID clientId; + ANALYZE_WAIT_CONTEXT context; + +#ifdef _WIN64 + // Determine if the process is WOW64. If not, we use the passive method. + + if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, ProcessId))) + { + PhShowStatus(hWnd, L"Unable to open the process", status, 0); + return; + } + + if (!NT_SUCCESS(status = PhGetProcessIsWow64(processHandle, &isWow64)) || !isWow64) + { + PhpAnalyzeWaitPassive(hWnd, ProcessId, ThreadId); + return; + } + + NtClose(processHandle); +#endif + + if (!NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_QUERY_LIMITED_INFORMATION | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, + ThreadId + ))) + { + PhShowStatus(hWnd, L"Unable to open the thread.", status, 0); + return; + } + + memset(&context, 0, sizeof(ANALYZE_WAIT_CONTEXT)); + context.ProcessId = ProcessId; + context.ThreadId = ThreadId; + + context.ProcessHandle = SymbolProvider->ProcessHandle; + context.SymbolProvider = SymbolProvider; + PhInitializeStringBuilder(&context.StringBuilder, 100); + + clientId.UniqueProcess = ProcessId; + clientId.UniqueThread = ThreadId; + + PhWalkThreadStack( + threadHandle, + SymbolProvider->ProcessHandle, + &clientId, + SymbolProvider, + PH_WALK_I386_STACK, + PhpWalkThreadStackAnalyzeCallback, + &context + ); + NtClose(threadHandle); + + PhpAnalyzeWaitFallbacks(&context); + + if (context.Found) + { + PhShowInformationDialog(hWnd, context.StringBuilder.String->Buffer, 0); + } + else + { + PhShowInformation2(hWnd, L"The thread does not appear to be waiting.", L""); + } + + PhDeleteStringBuilder(&context.StringBuilder); +} + +VOID PhpAnalyzeWaitPassive( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId + ) +{ + NTSTATUS status; + HANDLE processHandle; + HANDLE threadHandle; + THREAD_LAST_SYSCALL_INFORMATION lastSystemCall; + PH_STRING_BUILDER stringBuilder; + PPH_STRING string; + + PhpInitializeServiceNumbers(); + + if (!NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, ThreadId))) + { + PhShowStatus(hWnd, L"Unable to open the thread.", status, 0); + return; + } + + if (!NT_SUCCESS(status = PhGetThreadLastSystemCall(threadHandle, &lastSystemCall))) + { + NtClose(threadHandle); + PhShowStatus(hWnd, L"Unable to determine whether the thread is waiting.", status, 0); + return; + } + + if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE, ProcessId))) + { + NtClose(threadHandle); + PhShowStatus(hWnd, L"Unable to open the process.", status, 0); + return; + } + + PhInitializeStringBuilder(&stringBuilder, 100); + + if (lastSystemCall.SystemCallNumber == NumberForWfso) + { + string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument); + + PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for:\r\n"); + PhAppendStringBuilder(&stringBuilder, &string->sr); + } + else if (lastSystemCall.SystemCallNumber == NumberForWfmo) + { + PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for multiple (%lu) objects.", PtrToUlong(lastSystemCall.FirstArgument)); + } + else if (lastSystemCall.SystemCallNumber == NumberForRf) + { + string = PhpaGetHandleString(processHandle, lastSystemCall.FirstArgument); + + PhAppendFormatStringBuilder(&stringBuilder, L"Thread is waiting for file I/O:\r\n"); + PhAppendStringBuilder(&stringBuilder, &string->sr); + } + else + { + string = PhpaGetSendMessageReceiver(ThreadId); + + if (string) + { + PhAppendStringBuilder2(&stringBuilder, L"Thread is sending a USER message:\r\n"); + PhAppendStringBuilder(&stringBuilder, &string->sr); + } + else + { + string = PhpaGetAlpcInformation(ThreadId); + + if (string) + { + PhAppendStringBuilder2(&stringBuilder, L"Thread is waiting for an ALPC port:\r\n"); + PhAppendStringBuilder(&stringBuilder, &string->sr); + } + } + } + + if (stringBuilder.String->Length == 0) + PhAppendStringBuilder2(&stringBuilder, L"Unable to determine why the thread is waiting."); + + PhShowInformationDialog(hWnd, stringBuilder.String->Buffer, 0); + + PhDeleteStringBuilder(&stringBuilder); + NtClose(processHandle); + NtClose(threadHandle); +} + +static BOOLEAN NTAPI PhpWalkThreadStackAnalyzeCallback( + _In_ PPH_THREAD_STACK_FRAME StackFrame, + _In_opt_ PVOID Context + ) +{ + PANALYZE_WAIT_CONTEXT context = (PANALYZE_WAIT_CONTEXT)Context; + PPH_STRING name; + + name = PhGetSymbolFromAddress( + context->SymbolProvider, + (ULONG64)StackFrame->PcAddress, + NULL, + NULL, + NULL, + NULL + ); + + if (!name) + return TRUE; + + context->Found = TRUE; + +#define FUNC_MATCH(Name) PhStartsWithString2(name, L##Name, TRUE) +#define NT_FUNC_MATCH(Name) ( \ + PhStartsWithString2(name, L"ntdll.dll!Nt" L##Name, TRUE) || \ + PhStartsWithString2(name, L"ntdll.dll!Zw" L##Name, TRUE) \ + ) + + if (!name) + { + // Dummy + } + else if (FUNC_MATCH("kernel32.dll!Sleep")) + { + PhAppendFormatStringBuilder( + &context->StringBuilder, + L"Thread is sleeping. Timeout: %lu milliseconds.", + PtrToUlong(StackFrame->Params[0]) + ); + } + else if (NT_FUNC_MATCH("DelayExecution")) + { + BOOLEAN alertable = !!StackFrame->Params[0]; + PVOID timeoutAddress = StackFrame->Params[1]; + LARGE_INTEGER timeout; + + if (NT_SUCCESS(NtReadVirtualMemory( + context->ProcessHandle, + timeoutAddress, + &timeout, + sizeof(LARGE_INTEGER), + NULL + ))) + { + if (timeout.QuadPart < 0) + { + PhAppendFormatStringBuilder( + &context->StringBuilder, + L"Thread is sleeping. Timeout: %I64u milliseconds.", + -timeout.QuadPart / PH_TIMEOUT_MS + ); + } + else + { + // TODO + } + } + else + { + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is sleeping." + ); + } + } + else if (NT_FUNC_MATCH("DeviceIoControlFile")) + { + HANDLE handle = (HANDLE)StackFrame->Params[0]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for an I/O control request:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if (NT_FUNC_MATCH("FsControlFile")) + { + HANDLE handle = StackFrame->Params[0]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for a FS control request:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if (NT_FUNC_MATCH("QueryObject")) + { + HANDLE handle = StackFrame->Params[0]; + + // Use the KiFastSystemCall args if the handle we have is wrong. + if ((ULONG_PTR)handle % 4 != 0 || !handle) + handle = context->PrevParams[1]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is querying an object:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if (NT_FUNC_MATCH("ReadFile") || NT_FUNC_MATCH("WriteFile")) + { + HANDLE handle = StackFrame->Params[0]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for file I/O:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if (NT_FUNC_MATCH("RemoveIoCompletion")) + { + HANDLE handle = StackFrame->Params[0]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for an I/O completion port:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if ( + NT_FUNC_MATCH("ReplyWaitReceivePort") || + NT_FUNC_MATCH("RequestWaitReplyPort") || + NT_FUNC_MATCH("AlpcSendWaitReceivePort") + ) + { + HANDLE handle = StackFrame->Params[0]; + PPH_STRING alpcInfo; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for an ALPC port:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + + if (alpcInfo = PhpaGetAlpcInformation(context->ThreadId)) + { + PhAppendStringBuilder2( + &context->StringBuilder, + L"\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &alpcInfo->sr + ); + } + } + else if ( + NT_FUNC_MATCH("SetHighWaitLowEventPair") || + NT_FUNC_MATCH("SetLowWaitHighEventPair") || + NT_FUNC_MATCH("WaitHighEventPair") || + NT_FUNC_MATCH("WaitLowEventPair") + ) + { + HANDLE handle = StackFrame->Params[0]; + + if ((ULONG_PTR)handle % 4 != 0 || !handle) + handle = context->PrevParams[1]; + + PhAppendFormatStringBuilder( + &context->StringBuilder, + L"Thread is waiting (%s) for an event pair:\r\n", + name->Buffer + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if ( + FUNC_MATCH("user32.dll!NtUserGetMessage") || + FUNC_MATCH("user32.dll!NtUserWaitMessage") + ) + { + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for a USER message.\r\n" + ); + } + else if (FUNC_MATCH("user32.dll!NtUserMessageCall")) + { + PPH_STRING receiverString; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is sending a USER message:\r\n" + ); + + receiverString = PhpaGetSendMessageReceiver(context->ThreadId); + + if (receiverString) + { + PhAppendStringBuilder(&context->StringBuilder, &receiverString->sr); + PhAppendStringBuilder2(&context->StringBuilder, L"\r\n"); + } + else + { + PhAppendStringBuilder2(&context->StringBuilder, L"Unknown.\r\n"); + } + } + else if (NT_FUNC_MATCH("WaitForDebugEvent")) + { + HANDLE handle = StackFrame->Params[0]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for a debug event:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if ( + NT_FUNC_MATCH("WaitForKeyedEvent") || + NT_FUNC_MATCH("ReleaseKeyedEvent") + ) + { + HANDLE handle = StackFrame->Params[0]; + PVOID key = StackFrame->Params[1]; + + PhAppendFormatStringBuilder( + &context->StringBuilder, + L"Thread is waiting (%s) for a keyed event (key 0x%Ix):\r\n", + name->Buffer, + key + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if ( + NT_FUNC_MATCH("WaitForMultipleObjects") || + FUNC_MATCH("kernel32.dll!WaitForMultipleObjects") + ) + { + ULONG numberOfHandles = PtrToUlong(StackFrame->Params[0]); + PVOID addressOfHandles = StackFrame->Params[1]; + WAIT_TYPE waitType = (WAIT_TYPE)StackFrame->Params[2]; + BOOLEAN alertable = !!StackFrame->Params[3]; + + if (numberOfHandles > MAXIMUM_WAIT_OBJECTS) + { + numberOfHandles = PtrToUlong(context->PrevParams[1]); + addressOfHandles = context->PrevParams[2]; + waitType = (WAIT_TYPE)context->PrevParams[3]; + alertable = FALSE; + } + + PhpGetWfmoInformation( + context->ProcessHandle, + TRUE, // on x64 this function is only called for WOW64 processes + numberOfHandles, + addressOfHandles, + waitType, + alertable, + &context->StringBuilder + ); + } + else if ( + NT_FUNC_MATCH("WaitForSingleObject") || + FUNC_MATCH("kernel32.dll!WaitForSingleObject") + ) + { + HANDLE handle = StackFrame->Params[0]; + BOOLEAN alertable = !!StackFrame->Params[1]; + + if ((ULONG_PTR)handle % 4 != 0 || !handle) + { + handle = context->PrevParams[1]; + alertable = !!context->PrevParams[2]; + } + + PhAppendFormatStringBuilder( + &context->StringBuilder, + L"Thread is waiting (%s) for:\r\n", + alertable ? L"alertable" : L"non-alertable" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else if (NT_FUNC_MATCH("WaitForWorkViaWorkerFactory")) + { + HANDLE handle = StackFrame->Params[0]; + + PhAppendStringBuilder2( + &context->StringBuilder, + L"Thread is waiting for work from a worker factory:\r\n" + ); + PhAppendStringBuilder( + &context->StringBuilder, + &PhpaGetHandleString(context->ProcessHandle, handle)->sr + ); + } + else + { + context->Found = FALSE; + } + + PhDereferenceObject(name); + memcpy(&context->PrevParams, StackFrame->Params, sizeof(StackFrame->Params)); + + return !context->Found; +} + +static VOID PhpAnalyzeWaitFallbacks( + _In_ PANALYZE_WAIT_CONTEXT Context + ) +{ + PPH_STRING info; + + // We didn't detect NtUserMessageCall, but this may still apply due to another + // win32k system call (e.g. from EnableWindow). + if (!Context->Found && (info = PhpaGetSendMessageReceiver(Context->ThreadId))) + { + PhAppendStringBuilder2( + &Context->StringBuilder, + L"Thread is sending a USER message:\r\n" + ); + PhAppendStringBuilder(&Context->StringBuilder, &info->sr); + PhAppendStringBuilder2(&Context->StringBuilder, L"\r\n"); + + Context->Found = TRUE; + } + + // Nt(Alpc)ConnectPort doesn't get detected anywhere else. + if (!Context->Found && (info = PhpaGetAlpcInformation(Context->ThreadId))) + { + PhAppendStringBuilder2( + &Context->StringBuilder, + L"Thread is waiting for an ALPC port:\r\n" + ); + PhAppendStringBuilder(&Context->StringBuilder, &info->sr); + PhAppendStringBuilder2(&Context->StringBuilder, L"\r\n"); + + Context->Found = TRUE; + } +} + +static BOOLEAN PhpWaitUntilThreadIsWaiting( + _In_ HANDLE ThreadHandle + ) +{ + ULONG attempts; + BOOLEAN isWaiting = FALSE; + THREAD_BASIC_INFORMATION basicInfo; + + if (!NT_SUCCESS(PhGetThreadBasicInformation(ThreadHandle, &basicInfo))) + return FALSE; + + for (attempts = 0; attempts < 20; attempts++) + { + PVOID processes; + PSYSTEM_PROCESS_INFORMATION processInfo; + ULONG i; + + PhDelayExecution(100); + + if (!NT_SUCCESS(PhEnumProcesses(&processes))) + break; + + processInfo = PhFindProcessInformation(processes, basicInfo.ClientId.UniqueProcess); + + if (processInfo) + { + for (i = 0; i < processInfo->NumberOfThreads; i++) + { + if ( + processInfo->Threads[i].ClientId.UniqueThread == basicInfo.ClientId.UniqueThread && + processInfo->Threads[i].ThreadState == Waiting && + (processInfo->Threads[i].WaitReason == UserRequest || + processInfo->Threads[i].WaitReason == Executive) + ) + { + isWaiting = TRUE; + break; + } + } + } + + PhFree(processes); + + if (isWaiting) + break; + + PhDelayExecution(500); + } + + return isWaiting; +} + +static VOID PhpGetThreadLastSystemCallNumber( + _In_ HANDLE ThreadHandle, + _Out_ PUSHORT LastSystemCallNumber + ) +{ + THREAD_LAST_SYSCALL_INFORMATION lastSystemCall; + + if (NT_SUCCESS(PhGetThreadLastSystemCall(ThreadHandle, &lastSystemCall))) + { + *LastSystemCallNumber = lastSystemCall.SystemCallNumber; + } +} + +static NTSTATUS PhpWfsoThreadStart( + _In_ PVOID Parameter + ) +{ + HANDLE eventHandle; + LARGE_INTEGER timeout; + + eventHandle = Parameter; + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(5, PH_TIMEOUT_SEC); + NtWaitForSingleObject(eventHandle, FALSE, &timeout); + + return STATUS_SUCCESS; +} + +static NTSTATUS PhpWfmoThreadStart( + _In_ PVOID Parameter + ) +{ + HANDLE eventHandle; + LARGE_INTEGER timeout; + + eventHandle = Parameter; + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(5, PH_TIMEOUT_SEC); + NtWaitForMultipleObjects(1, &eventHandle, WaitAll, FALSE, &timeout); + + return STATUS_SUCCESS; +} + +static NTSTATUS PhpRfThreadStart( + _In_ PVOID Parameter + ) +{ + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + ULONG data; + + fileHandle = Parameter; + + NtReadFile(fileHandle, NULL, NULL, NULL, &isb, &data, sizeof(ULONG), NULL, NULL); + + return STATUS_SUCCESS; +} + +static VOID PhpInitializeServiceNumbers( + VOID + ) +{ + if (PhBeginInitOnce(&ServiceNumbersInitOnce)) + { + NTSTATUS status; + HANDLE eventHandle; + HANDLE threadHandle; + HANDLE pipeReadHandle; + HANDLE pipeWriteHandle; + + // The ThreadLastSystemCall info class only works when the thread is in the Waiting + // state. We'll create a thread which blocks on an event object we create, then wait + // until it is in the Waiting state. Only then can we query the thread using + // ThreadLastSystemCall. + + // NtWaitForSingleObject + + status = NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); + + if (NT_SUCCESS(status)) + { + if (NT_SUCCESS(PhCreateThreadEx(&threadHandle, PhpWfsoThreadStart, eventHandle))) + { + if (PhpWaitUntilThreadIsWaiting(threadHandle)) + { + PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfso); + } + + // Allow the thread to exit. + NtSetEvent(eventHandle, NULL); + NtClose(threadHandle); + } + + NtClose(eventHandle); + } + + // NtWaitForMultipleObjects + + status = NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); + + if (NT_SUCCESS(status)) + { + if (NT_SUCCESS(PhCreateThreadEx(&threadHandle, PhpWfmoThreadStart, eventHandle))) + { + if (PhpWaitUntilThreadIsWaiting(threadHandle)) + { + PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForWfmo); + } + + NtSetEvent(eventHandle, NULL); + NtClose(threadHandle); + } + + NtClose(eventHandle); + } + + // NtReadFile + + status = PhCreatePipe(&pipeReadHandle, &pipeWriteHandle); + + if (NT_SUCCESS(status)) + { + if (NT_SUCCESS(PhCreateThreadEx(&threadHandle, PhpRfThreadStart, pipeReadHandle))) + { + ULONG data = 0; + IO_STATUS_BLOCK isb; + + if (PhpWaitUntilThreadIsWaiting(threadHandle)) + { + PhpGetThreadLastSystemCallNumber(threadHandle, &NumberForRf); + } + + NtWriteFile(pipeWriteHandle, NULL, NULL, NULL, &isb, &data, sizeof(data), NULL, NULL); + NtClose(threadHandle); + } + + NtClose(pipeReadHandle); + NtClose(pipeWriteHandle); + } + + PhEndInitOnce(&ServiceNumbersInitOnce); + } +} + +static PPH_STRING PhpaGetHandleString( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle + ) +{ + PPH_STRING typeName = NULL; + PPH_STRING name = NULL; + PPH_STRING result; + + PhGetHandleInformation( + ProcessHandle, + Handle, + -1, + NULL, + &typeName, + NULL, + &name + ); + PH_AUTO(typeName); + PH_AUTO(name); + + if (typeName && name) + { + result = PhaFormatString( + L"Handle 0x%Ix (%s): %s", + Handle, + typeName->Buffer, + !PhIsNullOrEmptyString(name) ? name->Buffer : L"(unnamed object)" + ); + } + else + { + result = PhaFormatString( + L"Handle 0x%Ix: (error querying handle)", + Handle + ); + } + + return result; +} + +static VOID PhpGetWfmoInformation( + _In_ HANDLE ProcessHandle, + _In_ BOOLEAN IsWow64, + _In_ ULONG NumberOfHandles, + _In_ PHANDLE AddressOfHandles, + _In_ WAIT_TYPE WaitType, + _In_ BOOLEAN Alertable, + _Inout_ PPH_STRING_BUILDER StringBuilder + ) +{ + NTSTATUS status; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + ULONG i; + + status = STATUS_SUCCESS; + + if (NumberOfHandles <= MAXIMUM_WAIT_OBJECTS) + { +#ifdef _WIN64 + if (IsWow64) + { + ULONG handles32[MAXIMUM_WAIT_OBJECTS]; + + if (NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + AddressOfHandles, + handles32, + NumberOfHandles * sizeof(ULONG), + NULL + ))) + { + for (i = 0; i < NumberOfHandles; i++) + handles[i] = UlongToHandle(handles32[i]); + } + } + else + { +#endif + status = NtReadVirtualMemory( + ProcessHandle, + AddressOfHandles, + handles, + NumberOfHandles * sizeof(HANDLE), + NULL + ); +#ifdef _WIN64 + } +#endif + + if (NT_SUCCESS(status)) + { + PhAppendFormatStringBuilder( + StringBuilder, + L"Thread is waiting (%s, %s) for:\r\n", + Alertable ? L"alertable" : L"non-alertable", + WaitType == WaitAll ? L"wait all" : L"wait any" + ); + + for (i = 0; i < NumberOfHandles; i++) + { + PhAppendStringBuilder( + StringBuilder, + &PhpaGetHandleString(ProcessHandle, handles[i])->sr + ); + PhAppendStringBuilder2( + StringBuilder, + L"\r\n" + ); + } + } + } + + if (!NT_SUCCESS(status) || NumberOfHandles > MAXIMUM_WAIT_OBJECTS) + { + PhAppendStringBuilder2( + StringBuilder, + L"Thread is waiting for multiple objects." + ); + } +} + +static PPH_STRING PhpaGetSendMessageReceiver( + _In_ HANDLE ThreadId + ) +{ + static HWND (WINAPI *GetSendMessageReceiver_I)( + _In_ HANDLE ThreadId + ); + + HWND windowHandle; + ULONG threadId; + ULONG processId; + CLIENT_ID clientId; + PPH_STRING clientIdName; + WCHAR windowClass[64]; + PPH_STRING windowText; + + // GetSendMessageReceiver is an undocumented function exported by + // user32.dll. It retrieves the handle of the window which a thread + // is sending a message to. + + if (!GetSendMessageReceiver_I) + GetSendMessageReceiver_I = PhGetDllProcedureAddress(L"user32.dll", "GetSendMessageReceiver", 0); + + if (!GetSendMessageReceiver_I) + return NULL; + + windowHandle = GetSendMessageReceiver_I(ThreadId); + + if (!windowHandle) + return NULL; + + threadId = GetWindowThreadProcessId(windowHandle, &processId); + + clientId.UniqueProcess = UlongToHandle(processId); + clientId.UniqueThread = UlongToHandle(threadId); + clientIdName = PH_AUTO(PhGetClientIdName(&clientId)); + + if (!GetClassName(windowHandle, windowClass, sizeof(windowClass) / sizeof(WCHAR))) + windowClass[0] = UNICODE_NULL; + + windowText = PH_AUTO(PhGetWindowText(windowHandle)); + + return PhaFormatString(L"Window 0x%Ix (%s): %s \"%s\"", windowHandle, clientIdName->Buffer, windowClass, PhGetStringOrEmpty(windowText)); +} + +PPH_STRING PhpaGetAlpcInformation( + _In_ HANDLE ThreadId + ) +{ + NTSTATUS status; + PPH_STRING string = NULL; + HANDLE threadHandle; + PALPC_SERVER_INFORMATION serverInfo; + ULONG bufferLength; + + if (!NT_SUCCESS(PhOpenThread(&threadHandle, THREAD_QUERY_INFORMATION, ThreadId))) + return NULL; + + bufferLength = 0x110; + serverInfo = PhAllocate(bufferLength); + serverInfo->In.ThreadHandle = threadHandle; + + status = NtAlpcQueryInformation(NULL, AlpcServerInformation, serverInfo, bufferLength, &bufferLength); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(serverInfo); + serverInfo = PhAllocate(bufferLength); + serverInfo->In.ThreadHandle = threadHandle; + + status = NtAlpcQueryInformation(NULL, AlpcServerInformation, serverInfo, bufferLength, &bufferLength); + } + + if (NT_SUCCESS(status) && serverInfo->Out.ThreadBlocked) + { + CLIENT_ID clientId; + PPH_STRING clientIdName; + + clientId.UniqueProcess = serverInfo->Out.ConnectedProcessId; + clientId.UniqueThread = NULL; + clientIdName = PH_AUTO(PhGetClientIdName(&clientId)); + + string = PhaFormatString(L"ALPC Port: %.*s (%s)", serverInfo->Out.ConnectionPortName.Length / sizeof(WCHAR), serverInfo->Out.ConnectionPortName.Buffer, clientIdName->Buffer); + } + + PhFree(serverInfo); + NtClose(threadHandle); + + return string; +} diff --git a/ProcessHacker/appsup.c b/ProcessHacker/appsup.c index fa61833b5fda..4986206d7212 100644 --- a/ProcessHacker/appsup.c +++ b/ProcessHacker/appsup.c @@ -1,2207 +1,2142 @@ -/* - * Process Hacker - - * application support functions - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mxml/mxml.h" -#include "pcre/pcre2.h" - -typedef LONG (WINAPI *_GetPackageFullName)( - _In_ HANDLE hProcess, - _Inout_ UINT32 *packageFullNameLength, - _Out_opt_ PWSTR packageFullName - ); - -typedef LONG (WINAPI *_GetPackagePath)( - _In_ PACKAGE_ID *packageId, - _Reserved_ UINT32 reserved, - _Inout_ UINT32 *pathLength, - _Out_opt_ PWSTR path - ); - -typedef LONG (WINAPI *_PackageIdFromFullName)( - _In_ PCWSTR packageFullName, - _In_ UINT32 flags, - _Inout_ UINT32 *bufferLength, - _Out_opt_ BYTE *buffer - ); - -GUID XP_CONTEXT_GUID = { 0xbeb1b341, 0x6837, 0x4c83, { 0x83, 0x66, 0x2b, 0x45, 0x1e, 0x7c, 0xe6, 0x9b } }; -GUID VISTA_CONTEXT_GUID = { 0xe2011457, 0x1546, 0x43c5, { 0xa5, 0xfe, 0x00, 0x8d, 0xee, 0xe3, 0xd3, 0xf0 } }; -GUID WIN7_CONTEXT_GUID = { 0x35138b9a, 0x5d96, 0x4fbd, { 0x8e, 0x2d, 0xa2, 0x44, 0x02, 0x25, 0xf9, 0x3a } }; -GUID WIN8_CONTEXT_GUID = { 0x4a2f28e3, 0x53b9, 0x4441, { 0xba, 0x9c, 0xd6, 0x9d, 0x4a, 0x4a, 0x6e, 0x38 } }; -GUID WINBLUE_CONTEXT_GUID = { 0x1f676c76, 0x80e1, 0x4239, { 0x95, 0xbb, 0x83, 0xd0, 0xf6, 0xd0, 0xda, 0x78 } }; -GUID WINTHRESHOLD_CONTEXT_GUID = { 0x8e0f7a12, 0xbfb3, 0x4fe8, { 0xb9, 0xa5, 0x48, 0xfd, 0x50, 0xa1, 0x5a, 0x9a } }; - -/** - * Determines whether a process is suspended. - * - * \param Process The SYSTEM_PROCESS_INFORMATION structure - * of the process. - */ -BOOLEAN PhGetProcessIsSuspended( - _In_ PSYSTEM_PROCESS_INFORMATION Process - ) -{ - ULONG i; - - for (i = 0; i < Process->NumberOfThreads; i++) - { - if ( - Process->Threads[i].ThreadState != Waiting || - Process->Threads[i].WaitReason != Suspended - ) - return FALSE; - } - - return Process->NumberOfThreads != 0; -} - -/** - * Determines the OS compatibility context of a process. - * - * \param ProcessHandle A handle to a process. - * \param Guid A variable which receives a GUID identifying an - * operating system version. - */ -NTSTATUS PhGetProcessSwitchContext( - _In_ HANDLE ProcessHandle, - _Out_ PGUID Guid - ) -{ - NTSTATUS status; - PROCESS_BASIC_INFORMATION basicInfo; -#ifdef _WIN64 - PVOID peb32; - ULONG data32; -#endif - PVOID data; - - // Reverse-engineered from WdcGetProcessSwitchContext (wdc.dll). - // On Windows 8, the function is now SdbGetAppCompatData (apphelp.dll). - // On Windows 10, the function is again WdcGetProcessSwitchContext. - -#ifdef _WIN64 - if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32) - { - if (WindowsVersion >= WINDOWS_8) - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pShimData)), - &data32, - sizeof(ULONG), - NULL - ))) - return status; - } - else - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pContextData)), - &data32, - sizeof(ULONG), - NULL - ))) - return status; - } - - data = UlongToPtr(data32); - } - else - { -#endif - if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo))) - return status; - - if (WindowsVersion >= WINDOWS_8) - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pShimData)), - &data, - sizeof(PVOID), - NULL - ))) - return status; - } - else - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pContextData)), - &data, - sizeof(PVOID), - NULL - ))) - return status; - } -#ifdef _WIN64 - } -#endif - - if (!data) - return STATUS_UNSUCCESSFUL; // no compatibility context data - - if (WindowsVersion >= WINDOWS_10) - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(data, 2040 + 24), // Magic value from SbReadProcContextByHandle - Guid, - sizeof(GUID), - NULL - ))) - return status; - } - else if (WindowsVersion >= WINDOWS_8_1) - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(data, 2040 + 16), // Magic value from SbReadProcContextByHandle - Guid, - sizeof(GUID), - NULL - ))) - return status; - } - else if (WindowsVersion >= WINDOWS_8) - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(data, 2040), // Magic value from SbReadProcContextByHandle - Guid, - sizeof(GUID), - NULL - ))) - return status; - } - else - { - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(data, 32), // Magic value from WdcGetProcessSwitchContext - Guid, - sizeof(GUID), - NULL - ))) - return status; - } - - return STATUS_SUCCESS; -} - -PPH_STRING PhGetProcessPackageFullName( - _In_ HANDLE ProcessHandle - ) -{ - static _GetPackageFullName getPackageFullName = NULL; - - LONG result; - PPH_STRING name; - ULONG nameLength; - - if (!getPackageFullName) - getPackageFullName = PhGetModuleProcAddress(L"kernel32.dll", "GetPackageFullName"); - if (!getPackageFullName) - return NULL; - - nameLength = 101; - name = PhCreateStringEx(NULL, (nameLength - 1) * 2); - - result = getPackageFullName(ProcessHandle, &nameLength, name->Buffer); - - if (result == ERROR_INSUFFICIENT_BUFFER) - { - PhDereferenceObject(name); - name = PhCreateStringEx(NULL, (nameLength - 1) * 2); - - result = getPackageFullName(ProcessHandle, &nameLength, name->Buffer); - } - - if (result == ERROR_SUCCESS) - { - PhTrimToNullTerminatorString(name); - return name; - } - else - { - PhDereferenceObject(name); - return NULL; - } -} - -PACKAGE_ID *PhPackageIdFromFullName( - _In_ PWSTR PackageFullName - ) -{ - static _PackageIdFromFullName packageIdFromFullName = NULL; - - LONG result; - PVOID packageIdBuffer; - ULONG packageIdBufferSize; - - if (!packageIdFromFullName) - packageIdFromFullName = PhGetModuleProcAddress(L"kernel32.dll", "PackageIdFromFullName"); - if (!packageIdFromFullName) - return NULL; - - packageIdBufferSize = 100; - packageIdBuffer = PhAllocate(packageIdBufferSize); - - result = packageIdFromFullName(PackageFullName, PACKAGE_INFORMATION_BASIC, &packageIdBufferSize, (PBYTE)packageIdBuffer); - - if (result == ERROR_INSUFFICIENT_BUFFER) - { - PhFree(packageIdBuffer); - packageIdBuffer = PhAllocate(packageIdBufferSize); - - result = packageIdFromFullName(PackageFullName, PACKAGE_INFORMATION_BASIC, &packageIdBufferSize, (PBYTE)packageIdBuffer); - } - - if (result == ERROR_SUCCESS) - { - return packageIdBuffer; - } - else - { - PhFree(packageIdBuffer); - return NULL; - } -} - -PPH_STRING PhGetPackagePath( - _In_ PACKAGE_ID *PackageId - ) -{ - static _GetPackagePath getPackagePath = NULL; - - LONG result; - PPH_STRING path; - ULONG pathLength; - - if (!getPackagePath) - getPackagePath = PhGetModuleProcAddress(L"kernel32.dll", "GetPackagePath"); - if (!getPackagePath) - return NULL; - - pathLength = 101; - path = PhCreateStringEx(NULL, (pathLength - 1) * 2); - - result = getPackagePath(PackageId, 0, &pathLength, path->Buffer); - - if (result == ERROR_INSUFFICIENT_BUFFER) - { - PhDereferenceObject(path); - path = PhCreateStringEx(NULL, (pathLength - 1) * 2); - - result = getPackagePath(PackageId, 0, &pathLength, path->Buffer); - } - - if (result == ERROR_SUCCESS) - { - PhTrimToNullTerminatorString(path); - return path; - } - else - { - PhDereferenceObject(path); - return NULL; - } -} - -/** - * Determines the type of a process based on its image file name. - * - * \param ProcessHandle A handle to a process. - * \param KnownProcessType A variable which receives the process - * type. - */ -NTSTATUS PhGetProcessKnownType( - _In_ HANDLE ProcessHandle, - _Out_ PH_KNOWN_PROCESS_TYPE *KnownProcessType - ) -{ - NTSTATUS status; - PH_KNOWN_PROCESS_TYPE knownProcessType; - PROCESS_BASIC_INFORMATION basicInfo; - PH_STRINGREF systemRootPrefix; - PPH_STRING fileName; - PPH_STRING newFileName; - PH_STRINGREF name; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; -#endif - - if (!NT_SUCCESS(status = PhGetProcessBasicInformation( - ProcessHandle, - &basicInfo - ))) - return status; - - if (basicInfo.UniqueProcessId == SYSTEM_PROCESS_ID) - { - *KnownProcessType = SystemProcessType; - return STATUS_SUCCESS; - } - - PhGetSystemRoot(&systemRootPrefix); - - if (!NT_SUCCESS(status = PhGetProcessImageFileName( - ProcessHandle, - &fileName - ))) - { - return status; - } - - newFileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - name = newFileName->sr; - - knownProcessType = UnknownProcessType; - - if (PhStartsWithStringRef(&name, &systemRootPrefix, TRUE)) - { - // Skip the system root, and we now have three cases: - // 1. \\xyz.exe - Windows executable. - // 2. \\System32\\xyz.exe - system32 executable. - // 3. \\SysWow64\\xyz.exe - system32 executable + WOW64. - PhSkipStringRef(&name, systemRootPrefix.Length); - - if (PhEqualStringRef2(&name, L"\\explorer.exe", TRUE)) - { - knownProcessType = ExplorerProcessType; - } - else if ( - PhStartsWithStringRef2(&name, L"\\System32", TRUE) -#ifdef _WIN64 - || (PhStartsWithStringRef2(&name, L"\\SysWow64", TRUE) && (isWow64 = TRUE, TRUE)) // ugly but necessary -#endif - ) - { - // SysTem32 and SysWow64 are both 8 characters long. - PhSkipStringRef(&name, 9 * sizeof(WCHAR)); - - if (FALSE) - ; // Dummy - else if (PhEqualStringRef2(&name, L"\\smss.exe", TRUE)) - knownProcessType = SessionManagerProcessType; - else if (PhEqualStringRef2(&name, L"\\csrss.exe", TRUE)) - knownProcessType = WindowsSubsystemProcessType; - else if (PhEqualStringRef2(&name, L"\\wininit.exe", TRUE)) - knownProcessType = WindowsStartupProcessType; - else if (PhEqualStringRef2(&name, L"\\services.exe", TRUE)) - knownProcessType = ServiceControlManagerProcessType; - else if (PhEqualStringRef2(&name, L"\\lsass.exe", TRUE)) - knownProcessType = LocalSecurityAuthorityProcessType; - else if (PhEqualStringRef2(&name, L"\\lsm.exe", TRUE)) - knownProcessType = LocalSessionManagerProcessType; - else if (PhEqualStringRef2(&name, L"\\winlogon.exe", TRUE)) - knownProcessType = WindowsLogonProcessType; - else if (PhEqualStringRef2(&name, L"\\svchost.exe", TRUE)) - knownProcessType = ServiceHostProcessType; - else if (PhEqualStringRef2(&name, L"\\rundll32.exe", TRUE)) - knownProcessType = RunDllAsAppProcessType; - else if (PhEqualStringRef2(&name, L"\\dllhost.exe", TRUE)) - knownProcessType = ComSurrogateProcessType; - else if (PhEqualStringRef2(&name, L"\\taskeng.exe", TRUE)) - knownProcessType = TaskHostProcessType; - else if (PhEqualStringRef2(&name, L"\\taskhost.exe", TRUE)) - knownProcessType = TaskHostProcessType; - else if (PhEqualStringRef2(&name, L"\\taskhostex.exe", TRUE)) - knownProcessType = TaskHostProcessType; - else if (PhEqualStringRef2(&name, L"\\taskhostw.exe", TRUE)) - knownProcessType = TaskHostProcessType; - else if (PhEqualStringRef2(&name, L"\\wudfhost.exe", TRUE)) - knownProcessType = UmdfHostProcessType; - } - } - - PhDereferenceObject(newFileName); - -#ifdef _WIN64 - if (isWow64) - knownProcessType |= KnownProcessWow64; -#endif - - *KnownProcessType = knownProcessType; - - return status; -} - -static BOOLEAN NTAPI PhpSvchostCommandLineCallback( - _In_opt_ PPH_COMMAND_LINE_OPTION Option, - _In_opt_ PPH_STRING Value, - _In_opt_ PVOID Context - ) -{ - PPH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine = Context; - - if (Option && Option->Id == 1) - { - PhSwapReference(&knownCommandLine->ServiceHost.GroupName, Value); - } - - return TRUE; -} - -BOOLEAN PhaGetProcessKnownCommandLine( - _In_ PPH_STRING CommandLine, - _In_ PH_KNOWN_PROCESS_TYPE KnownProcessType, - _Out_ PPH_KNOWN_PROCESS_COMMAND_LINE KnownCommandLine - ) -{ - switch (KnownProcessType & KnownProcessTypeMask) - { - case ServiceHostProcessType: - { - // svchost.exe -k - - static PH_COMMAND_LINE_OPTION options[] = - { - { 1, L"k", MandatoryArgumentType } - }; - - KnownCommandLine->ServiceHost.GroupName = NULL; - - PhParseCommandLine( - &CommandLine->sr, - options, - sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), - PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS, - PhpSvchostCommandLineCallback, - KnownCommandLine - ); - - if (KnownCommandLine->ServiceHost.GroupName) - { - PH_AUTO(KnownCommandLine->ServiceHost.GroupName); - return TRUE; - } - else - { - return FALSE; - } - } - break; - case RunDllAsAppProcessType: - { - // rundll32.exe , ... - - SIZE_T i; - PH_STRINGREF dllNamePart; - PH_STRINGREF procedureNamePart; - PPH_STRING dllName; - PPH_STRING procedureName; - - i = 0; - - // Get the rundll32.exe part. - - dllName = PhParseCommandLinePart(&CommandLine->sr, &i); - - if (!dllName) - return FALSE; - - PhDereferenceObject(dllName); - - // Get the DLL name part. - - while (i < CommandLine->Length / 2 && CommandLine->Buffer[i] == ' ') - i++; - - dllName = PhParseCommandLinePart(&CommandLine->sr, &i); - - if (!dllName) - return FALSE; - - PH_AUTO(dllName); - - // The procedure name begins after the last comma. - - if (!PhSplitStringRefAtLastChar(&dllName->sr, ',', &dllNamePart, &procedureNamePart)) - return FALSE; - - dllName = PH_AUTO(PhCreateString2(&dllNamePart)); - procedureName = PH_AUTO(PhCreateString2(&procedureNamePart)); - - // If the DLL name isn't an absolute path, assume it's in system32. - // TODO: Use a proper search function. - - if (RtlDetermineDosPathNameType_U(dllName->Buffer) == RtlPathTypeRelative) - { - dllName = PhaConcatStrings( - 3, - PH_AUTO_T(PH_STRING, PhGetSystemDirectory())->Buffer, - L"\\", - dllName->Buffer - ); - } - - KnownCommandLine->RunDllAsApp.FileName = dllName; - KnownCommandLine->RunDllAsApp.ProcedureName = procedureName; - } - break; - case ComSurrogateProcessType: - { - // dllhost.exe /processid: - - static PH_STRINGREF inprocServer32Name = PH_STRINGREF_INIT(L"InprocServer32"); - - SIZE_T i; - ULONG_PTR indexOfProcessId; - PPH_STRING argPart; - PPH_STRING guidString; - UNICODE_STRING guidStringUs; - GUID guid; - HANDLE rootKeyHandle; - HANDLE inprocServer32KeyHandle; - PPH_STRING fileName; - - i = 0; - - // Get the dllhost.exe part. - - argPart = PhParseCommandLinePart(&CommandLine->sr, &i); - - if (!argPart) - return FALSE; - - PhDereferenceObject(argPart); - - // Get the argument part. - - while (i < (ULONG)CommandLine->Length / 2 && CommandLine->Buffer[i] == ' ') - i++; - - argPart = PhParseCommandLinePart(&CommandLine->sr, &i); - - if (!argPart) - return FALSE; - - PH_AUTO(argPart); - - // Find "/processid:"; the GUID is just after that. - - _wcsupr(argPart->Buffer); - indexOfProcessId = PhFindStringInString(argPart, 0, L"/PROCESSID:"); - - if (indexOfProcessId == -1) - return FALSE; - - guidString = PhaSubstring( - argPart, - indexOfProcessId + 11, - (ULONG)argPart->Length / 2 - indexOfProcessId - 11 - ); - PhStringRefToUnicodeString(&guidString->sr, &guidStringUs); - - if (!NT_SUCCESS(RtlGUIDFromString( - &guidStringUs, - &guid - ))) - return FALSE; - - KnownCommandLine->ComSurrogate.Guid = guid; - KnownCommandLine->ComSurrogate.Name = NULL; - KnownCommandLine->ComSurrogate.FileName = NULL; - - // Lookup the GUID in the registry to determine the name and file name. - - if (NT_SUCCESS(PhOpenKey( - &rootKeyHandle, - KEY_READ, - PH_KEY_CLASSES_ROOT, - &PhaConcatStrings2(L"CLSID\\", guidString->Buffer)->sr, - 0 - ))) - { - KnownCommandLine->ComSurrogate.Name = - PH_AUTO(PhQueryRegistryString(rootKeyHandle, NULL)); - - if (NT_SUCCESS(PhOpenKey( - &inprocServer32KeyHandle, - KEY_READ, - rootKeyHandle, - &inprocServer32Name, - 0 - ))) - { - KnownCommandLine->ComSurrogate.FileName = - PH_AUTO(PhQueryRegistryString(inprocServer32KeyHandle, NULL)); - - if (fileName = PH_AUTO(PhExpandEnvironmentStrings( - &KnownCommandLine->ComSurrogate.FileName->sr - ))) - { - KnownCommandLine->ComSurrogate.FileName = fileName; - } - - NtClose(inprocServer32KeyHandle); - } - - NtClose(rootKeyHandle); - } - else if (NT_SUCCESS(PhOpenKey( - &rootKeyHandle, - KEY_READ, - PH_KEY_CLASSES_ROOT, - &PhaConcatStrings2(L"AppID\\", guidString->Buffer)->sr, - 0 - ))) - { - KnownCommandLine->ComSurrogate.Name = - PH_AUTO(PhQueryRegistryString(rootKeyHandle, NULL)); - NtClose(rootKeyHandle); - } - } - break; - default: - return FALSE; - } - - return TRUE; -} - -VOID PhEnumChildWindows( - _In_opt_ HWND hWnd, - _In_ ULONG Limit, - _In_ WNDENUMPROC Callback, - _In_ LPARAM lParam - ) -{ - HWND childWindow = NULL; - ULONG i = 0; - - while (i < Limit && (childWindow = FindWindowEx(hWnd, childWindow, NULL, NULL))) - { - if (!Callback(childWindow, lParam)) - return; - - i++; - } -} - -typedef struct _GET_PROCESS_MAIN_WINDOW_CONTEXT -{ - HWND Window; - HWND ImmersiveWindow; - HANDLE ProcessId; - BOOLEAN IsImmersive; -} GET_PROCESS_MAIN_WINDOW_CONTEXT, *PGET_PROCESS_MAIN_WINDOW_CONTEXT; - -BOOL CALLBACK PhpGetProcessMainWindowEnumWindowsProc( - _In_ HWND hwnd, - _In_ LPARAM lParam - ) -{ - PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)lParam; - ULONG processId; - HWND parentWindow; - WINDOWINFO windowInfo; - - if (!IsWindowVisible(hwnd)) - return TRUE; - - GetWindowThreadProcessId(hwnd, &processId); - - if (UlongToHandle(processId) == context->ProcessId && - !((parentWindow = GetParent(hwnd)) && IsWindowVisible(parentWindow)) && // skip windows with a visible parent - PhGetWindowTextEx(hwnd, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0) // skip windows with no title - { - if (!context->ImmersiveWindow && context->IsImmersive && - GetProp(hwnd, L"Windows.ImmersiveShell.IdentifyAsMainCoreWindow")) - { - context->ImmersiveWindow = hwnd; - } - - windowInfo.cbSize = sizeof(WINDOWINFO); - - if (!context->Window && GetWindowInfo(hwnd, &windowInfo) && (windowInfo.dwStyle & WS_DLGFRAME)) - { - context->Window = hwnd; - - // If we're not looking at an immersive process, there's no need to search any more windows. - if (!context->IsImmersive) - return FALSE; - } - } - - return TRUE; -} - -HWND PhGetProcessMainWindow( - _In_ HANDLE ProcessId, - _In_opt_ HANDLE ProcessHandle - ) -{ - GET_PROCESS_MAIN_WINDOW_CONTEXT context; - HANDLE processHandle = NULL; - - memset(&context, 0, sizeof(GET_PROCESS_MAIN_WINDOW_CONTEXT)); - context.ProcessId = ProcessId; - - if (ProcessHandle) - processHandle = ProcessHandle; - else - PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessId); - - if (processHandle && IsImmersiveProcess_I) - context.IsImmersive = IsImmersiveProcess_I(processHandle); - - PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, (LPARAM)&context); - - if (!ProcessHandle && processHandle) - NtClose(processHandle); - - return context.ImmersiveWindow ? context.ImmersiveWindow : context.Window; -} - -PPH_STRING PhGetServiceRelevantFileName( - _In_ PPH_STRINGREF ServiceName, - _In_ SC_HANDLE ServiceHandle - ) -{ - PPH_STRING fileName = NULL; - LPQUERY_SERVICE_CONFIG config; - - if (config = PhGetServiceConfig(ServiceHandle)) - { - PhGetServiceDllParameter(ServiceName, &fileName); - - if (!fileName) - { - PPH_STRING commandLine; - - commandLine = PhCreateString(config->lpBinaryPathName); - - if (config->dwServiceType & SERVICE_WIN32) - { - PH_STRINGREF dummyFileName; - PH_STRINGREF dummyArguments; - - PhParseCommandLineFuzzy(&commandLine->sr, &dummyFileName, &dummyArguments, &fileName); - - if (!fileName) - PhSwapReference(&fileName, commandLine); - } - else - { - fileName = PhGetFileName(commandLine); - } - - PhDereferenceObject(commandLine); - } - - PhFree(config); - } - - return fileName; -} - -PPH_STRING PhEscapeStringForDelimiter( - _In_ PPH_STRING String, - _In_ WCHAR Delimiter - ) -{ - PH_STRING_BUILDER stringBuilder; - SIZE_T length; - SIZE_T i; - WCHAR temp[2]; - - length = String->Length / 2; - PhInitializeStringBuilder(&stringBuilder, String->Length / 2 * 3); - - temp[0] = '\\'; - - for (i = 0; i < length; i++) - { - if (String->Buffer[i] == '\\' || String->Buffer[i] == Delimiter) - { - temp[1] = String->Buffer[i]; - PhAppendStringBuilderEx(&stringBuilder, temp, 4); - } - else - { - PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); - } - } - - return PhFinalStringBuilderString(&stringBuilder); -} - -PPH_STRING PhUnescapeStringForDelimiter( - _In_ PPH_STRING String, - _In_ WCHAR Delimiter - ) -{ - PH_STRING_BUILDER stringBuilder; - SIZE_T length; - SIZE_T i; - - length = String->Length / 2; - PhInitializeStringBuilder(&stringBuilder, String->Length / 2 * 3); - - for (i = 0; i < length; i++) - { - if (String->Buffer[i] == '\\') - { - if (i != length - 1) - { - PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i + 1]); - i++; - } - else - { - // Trailing backslash. Just ignore it. - break; - } - } - else - { - PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); - } - } - - return PhFinalStringBuilderString(&stringBuilder); -} - -PPH_STRING PhGetOpaqueXmlNodeText( - _In_ mxml_node_t *node - ) -{ - if (node->child && node->child->type == MXML_OPAQUE && node->child->value.opaque) - { - return PhConvertUtf8ToUtf16(node->child->value.opaque); - } - else - { - return PhReferenceEmptyString(); - } -} - -VOID PhSearchOnlineString( - _In_ HWND hWnd, - _In_ PWSTR String - ) -{ - PhShellExecuteUserString(hWnd, L"SearchEngine", String, TRUE, NULL); -} - -VOID PhShellExecuteUserString( - _In_ HWND hWnd, - _In_ PWSTR Setting, - _In_ PWSTR String, - _In_ BOOLEAN UseShellExecute, - _In_opt_ PWSTR ErrorMessage - ) -{ - static PH_STRINGREF replacementToken = PH_STRINGREF_INIT(L"%s"); - - PPH_STRING executeString; - PH_STRINGREF stringBefore; - PH_STRINGREF stringMiddle; - PH_STRINGREF stringAfter; - PPH_STRING ntMessage; - - executeString = PhGetStringSetting(Setting); - - // Make sure the user executable string is absolute. We can't use RtlDetermineDosPathNameType_U - // here because the string may be a URL. - if (PhFindCharInString(executeString, 0, ':') == -1) - PhMoveReference(&executeString, PhConcatStringRef2(&PhApplicationDirectory->sr, &executeString->sr)); - - // Replace the token with the string, or use the original string if the token is not present. - if (PhSplitStringRefAtString(&executeString->sr, &replacementToken, FALSE, &stringBefore, &stringAfter)) - { - PhInitializeStringRef(&stringMiddle, String); - PhMoveReference(&executeString, PhConcatStringRef3(&stringBefore, &stringMiddle, &stringAfter)); - } - - if (UseShellExecute) - { - PhShellExecute(hWnd, executeString->Buffer, NULL); - } - else - { - NTSTATUS status; - - status = PhCreateProcessWin32(NULL, executeString->Buffer, NULL, NULL, 0, NULL, NULL, NULL); - - if (!NT_SUCCESS(status)) - { - if (ErrorMessage) - { - ntMessage = PhGetNtMessage(status); - PhShowError(hWnd, L"Unable to execute the command: %s\n%s", PhGetStringOrDefault(ntMessage, L"An unknown error occurred."), ErrorMessage); - PhDereferenceObject(ntMessage); - } - else - { - PhShowStatus(hWnd, L"Unable to execute the command", status, 0); - } - } - } - - PhDereferenceObject(executeString); -} - -VOID PhLoadSymbolProviderOptions( - _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider - ) -{ - PPH_STRING searchPath; - - PhSetOptionsSymbolProvider( - SYMOPT_UNDNAME, - PhGetIntegerSetting(L"DbgHelpUndecorate") ? SYMOPT_UNDNAME : 0 - ); - - searchPath = PhGetStringSetting(L"DbgHelpSearchPath"); - - if (searchPath->Length != 0) - PhSetSearchPathSymbolProvider(SymbolProvider, searchPath->Buffer); - - PhDereferenceObject(searchPath); -} - -PWSTR PhMakeContextAtom( - VOID - ) -{ - PH_DEFINE_MAKE_ATOM(L"PH2_Context"); -} - -/** - * Copies a string into a NMLVGETINFOTIP structure. - * - * \param GetInfoTip The NMLVGETINFOTIP structure. - * \param Tip The string to copy. - * - * \remarks The text is truncated if it is too long. - */ -VOID PhCopyListViewInfoTip( - _Inout_ LPNMLVGETINFOTIP GetInfoTip, - _In_ PPH_STRINGREF Tip - ) -{ - ULONG copyIndex; - ULONG bufferRemaining; - ULONG copyLength; - - if (GetInfoTip->dwFlags == 0) - { - copyIndex = (ULONG)PhCountStringZ(GetInfoTip->pszText) + 1; // plus one for newline - - if (GetInfoTip->cchTextMax - copyIndex < 2) // need at least two bytes - return; - - bufferRemaining = GetInfoTip->cchTextMax - copyIndex - 1; - GetInfoTip->pszText[copyIndex - 1] = '\n'; - } - else - { - copyIndex = 0; - bufferRemaining = GetInfoTip->cchTextMax; - } - - copyLength = min((ULONG)Tip->Length / 2, bufferRemaining - 1); - memcpy( - &GetInfoTip->pszText[copyIndex], - Tip->Buffer, - copyLength * 2 - ); - GetInfoTip->pszText[copyIndex + copyLength] = 0; -} - -VOID PhCopyListView( - _In_ HWND ListViewHandle - ) -{ - PPH_STRING text; - - text = PhGetListViewText(ListViewHandle); - PhSetClipboardString(ListViewHandle, &text->sr); - PhDereferenceObject(text); -} - -VOID PhHandleListViewNotifyForCopy( - _In_ LPARAM lParam, - _In_ HWND ListViewHandle - ) -{ - PhHandleListViewNotifyBehaviors(lParam, ListViewHandle, PH_LIST_VIEW_CTRL_C_BEHAVIOR); -} - -VOID PhHandleListViewNotifyBehaviors( - _In_ LPARAM lParam, - _In_ HWND ListViewHandle, - _In_ ULONG Behaviors - ) -{ - if (((LPNMHDR)lParam)->hwndFrom == ListViewHandle && ((LPNMHDR)lParam)->code == LVN_KEYDOWN) - { - LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)lParam; - - switch (keyDown->wVKey) - { - case 'C': - if (Behaviors & PH_LIST_VIEW_CTRL_C_BEHAVIOR) - { - if (GetKeyState(VK_CONTROL) < 0) - PhCopyListView(ListViewHandle); - } - break; - case 'A': - if (Behaviors & PH_LIST_VIEW_CTRL_A_BEHAVIOR) - { - if (GetKeyState(VK_CONTROL) < 0) - PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED); - } - break; - } - } -} - -BOOLEAN PhGetListViewContextMenuPoint( - _In_ HWND ListViewHandle, - _Out_ PPOINT Point - ) -{ - INT selectedIndex; - RECT bounds; - RECT clientRect; - - // The user pressed a key to display the context menu. - // Suggest where the context menu should display. - - if ((selectedIndex = ListView_GetNextItem(ListViewHandle, -1, LVNI_SELECTED)) != -1) - { - if (ListView_GetItemRect(ListViewHandle, selectedIndex, &bounds, LVIR_BOUNDS)) - { - Point->x = bounds.left + PhSmallIconSize.X / 2; - Point->y = bounds.top + PhSmallIconSize.Y / 2; - - GetClientRect(ListViewHandle, &clientRect); - - if (Point->x < 0 || Point->y < 0 || Point->x >= clientRect.right || Point->y >= clientRect.bottom) - { - // The menu is going to be outside of the control. Just put it at the top-left. - Point->x = 0; - Point->y = 0; - } - - ClientToScreen(ListViewHandle, Point); - - return TRUE; - } - } - - Point->x = 0; - Point->y = 0; - ClientToScreen(ListViewHandle, Point); - - return FALSE; -} - -HFONT PhDuplicateFontWithNewWeight( - _In_ HFONT Font, - _In_ LONG NewWeight - ) -{ - LOGFONT logFont; - - if (GetObject(Font, sizeof(LOGFONT), &logFont)) - { - logFont.lfWeight = NewWeight; - return CreateFontIndirect(&logFont); - } - else - { - return NULL; - } -} - -VOID PhSetWindowOpacity( - _In_ HWND WindowHandle, - _In_ ULONG OpacityPercent - ) -{ - if (OpacityPercent == 0) - { - // Make things a bit faster by removing the WS_EX_LAYERED bit. - PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, 0); - RedrawWindow(WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); - return; - } - - PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED); - - // Disallow opacity values of less than 10%. - OpacityPercent = min(OpacityPercent, 90); - - // The opacity value is backwards - 0 means opaque, 100 means transparent. - SetLayeredWindowAttributes( - WindowHandle, - 0, - (BYTE)(255 * (100 - OpacityPercent) / 100), - LWA_ALPHA - ); -} - -VOID PhLoadWindowPlacementFromSetting( - _In_opt_ PWSTR PositionSettingName, - _In_opt_ PWSTR SizeSettingName, - _In_ HWND WindowHandle - ) -{ - PH_RECTANGLE windowRectangle; - - if (PositionSettingName && SizeSettingName) - { - RECT rectForAdjust; - - windowRectangle.Position = PhGetIntegerPairSetting(PositionSettingName); - windowRectangle.Size = PhGetScalableIntegerPairSetting(SizeSettingName, TRUE).Pair; - PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); - - // Let the window adjust for the minimum size if needed. - rectForAdjust = PhRectangleToRect(windowRectangle); - SendMessage(WindowHandle, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&rectForAdjust); - windowRectangle = PhRectToRectangle(rectForAdjust); - - MoveWindow(WindowHandle, windowRectangle.Left, windowRectangle.Top, - windowRectangle.Width, windowRectangle.Height, FALSE); - } - else - { - PH_INTEGER_PAIR position; - PH_INTEGER_PAIR size; - ULONG flags; - - flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER; - - if (PositionSettingName) - { - position = PhGetIntegerPairSetting(PositionSettingName); - flags &= ~SWP_NOMOVE; - } - else - { - position.X = 0; - position.Y = 0; - } - - if (SizeSettingName) - { - size = PhGetScalableIntegerPairSetting(SizeSettingName, TRUE).Pair; - flags &= ~SWP_NOSIZE; - } - else - { - size.X = 16; - size.Y = 16; - } - - SetWindowPos(WindowHandle, NULL, position.X, position.Y, size.X, size.Y, flags); - } -} - -VOID PhSaveWindowPlacementToSetting( - _In_opt_ PWSTR PositionSettingName, - _In_opt_ PWSTR SizeSettingName, - _In_ HWND WindowHandle - ) -{ - WINDOWPLACEMENT placement = { sizeof(placement) }; - PH_RECTANGLE windowRectangle; - MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; - - GetWindowPlacement(WindowHandle, &placement); - windowRectangle = PhRectToRectangle(placement.rcNormalPosition); - - // The rectangle is in workspace coordinates. Convert the values back to screen coordinates. - if (GetMonitorInfo(MonitorFromRect(&placement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo)) - { - windowRectangle.Left += monitorInfo.rcWork.left - monitorInfo.rcMonitor.left; - windowRectangle.Top += monitorInfo.rcWork.top - monitorInfo.rcMonitor.top; - } - - if (PositionSettingName) - PhSetIntegerPairSetting(PositionSettingName, windowRectangle.Position); - if (SizeSettingName) - PhSetScalableIntegerPairSetting2(SizeSettingName, windowRectangle.Size); -} - -VOID PhLoadListViewColumnsFromSetting( - _In_ PWSTR Name, - _In_ HWND ListViewHandle - ) -{ - PPH_STRING string; - - string = PhGetStringSetting(Name); - PhLoadListViewColumnSettings(ListViewHandle, string); - PhDereferenceObject(string); -} - -VOID PhSaveListViewColumnsToSetting( - _In_ PWSTR Name, - _In_ HWND ListViewHandle - ) -{ - PPH_STRING string; - - string = PhSaveListViewColumnSettings(ListViewHandle); - PhSetStringSetting2(Name, &string->sr); - PhDereferenceObject(string); -} - -PPH_STRING PhGetPhVersion( - VOID - ) -{ - PH_FORMAT format[5]; - - PhInitFormatU(&format[0], PHAPP_VERSION_MAJOR); - PhInitFormatC(&format[1], '.'); - PhInitFormatU(&format[2], PHAPP_VERSION_MINOR); - PhInitFormatC(&format[3], '.'); - PhInitFormatU(&format[4], PHAPP_VERSION_REVISION); - - return PhFormat(format, 5, 16); -} - -VOID PhGetPhVersionNumbers( - _Out_opt_ PULONG MajorVersion, - _Out_opt_ PULONG MinorVersion, - _Reserved_ PULONG Reserved, - _Out_opt_ PULONG RevisionNumber - ) -{ - if (MajorVersion) - *MajorVersion = PHAPP_VERSION_MAJOR; - if (MinorVersion) - *MinorVersion = PHAPP_VERSION_MINOR; - if (RevisionNumber) - *RevisionNumber = PHAPP_VERSION_REVISION; -} - -VOID PhWritePhTextHeader( - _Inout_ PPH_FILE_STREAM FileStream - ) -{ - PPH_STRING version; - LARGE_INTEGER time; - SYSTEMTIME systemTime; - PPH_STRING dateString; - PPH_STRING timeString; - - PhWriteStringAsUtf8FileStream2(FileStream, L"Process Hacker "); - - if (version = PhGetPhVersion()) - { - PhWriteStringAsUtf8FileStream(FileStream, &version->sr); - PhDereferenceObject(version); - } - - PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\nWindows NT %u.%u", PhOsVersion.dwMajorVersion, PhOsVersion.dwMinorVersion); - - if (PhOsVersion.szCSDVersion[0] != 0) - PhWriteStringFormatAsUtf8FileStream(FileStream, L" %s", PhOsVersion.szCSDVersion); - -#ifdef _WIN64 - PhWriteStringAsUtf8FileStream2(FileStream, L" (64-bit)"); -#else - PhWriteStringAsUtf8FileStream2(FileStream, L" (32-bit)"); -#endif - - PhQuerySystemTime(&time); - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - - dateString = PhFormatDate(&systemTime, NULL); - timeString = PhFormatTime(&systemTime, NULL); - PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\n%s %s\r\n\r\n", dateString->Buffer, timeString->Buffer); - PhDereferenceObject(dateString); - PhDereferenceObject(timeString); -} - -BOOLEAN PhShellProcessHacker( - _In_opt_ HWND hWnd, - _In_opt_ PWSTR Parameters, - _In_ ULONG ShowWindowType, - _In_ ULONG Flags, - _In_ ULONG AppFlags, - _In_opt_ ULONG Timeout, - _Out_opt_ PHANDLE ProcessHandle - ) -{ - return PhShellProcessHackerEx( - hWnd, - NULL, - Parameters, - ShowWindowType, - Flags, - AppFlags, - Timeout, - ProcessHandle - ); -} - -VOID PhpAppendCommandLineArgument( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ PWSTR Name, - _In_ PPH_STRINGREF Value - ) -{ - PPH_STRING temp; - - PhAppendStringBuilder2(StringBuilder, L" -"); - PhAppendStringBuilder2(StringBuilder, Name); - PhAppendStringBuilder2(StringBuilder, L" \""); - temp = PhEscapeCommandLinePart(Value); - PhAppendStringBuilder(StringBuilder, &temp->sr); - PhDereferenceObject(temp); - PhAppendCharStringBuilder(StringBuilder, '\"'); -} - -BOOLEAN PhShellProcessHackerEx( - _In_opt_ HWND hWnd, - _In_opt_ PWSTR FileName, - _In_opt_ PWSTR Parameters, - _In_ ULONG ShowWindowType, - _In_ ULONG Flags, - _In_ ULONG AppFlags, - _In_opt_ ULONG Timeout, - _Out_opt_ PHANDLE ProcessHandle - ) -{ - BOOLEAN result; - PH_STRING_BUILDER sb; - PWSTR parameters; - - if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS) - { - PhInitializeStringBuilder(&sb, 128); - - // Propagate parameters. - - if (PhStartupParameters.NoSettings) - { - PhAppendStringBuilder2(&sb, L" -nosettings"); - } - else if (PhStartupParameters.SettingsFileName && (PhSettingsFileName || (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS_FORCE_SETTINGS))) - { - PPH_STRINGREF fileName; - - if (PhSettingsFileName) - fileName = &PhSettingsFileName->sr; - else - fileName = &PhStartupParameters.SettingsFileName->sr; - - PhpAppendCommandLineArgument(&sb, L"settings", fileName); - } - - if (PhStartupParameters.NoKph) - PhAppendStringBuilder2(&sb, L" -nokph"); - if (PhStartupParameters.InstallKph) - PhAppendStringBuilder2(&sb, L" -installkph"); - if (PhStartupParameters.UninstallKph) - PhAppendStringBuilder2(&sb, L" -uninstallkph"); - if (PhStartupParameters.Debug) - PhAppendStringBuilder2(&sb, L" -debug"); - if (PhStartupParameters.NoPlugins) - PhAppendStringBuilder2(&sb, L" -noplugins"); - if (PhStartupParameters.NewInstance) - PhAppendStringBuilder2(&sb, L" -newinstance"); - - if (PhStartupParameters.SelectPid != 0) - PhAppendFormatStringBuilder(&sb, L" -selectpid %u", PhStartupParameters.SelectPid); - - if (PhStartupParameters.PriorityClass != 0) - { - CHAR value = 0; - - switch (PhStartupParameters.PriorityClass) - { - case PROCESS_PRIORITY_CLASS_REALTIME: - value = L'r'; - break; - case PROCESS_PRIORITY_CLASS_HIGH: - value = L'h'; - break; - case PROCESS_PRIORITY_CLASS_NORMAL: - value = L'n'; - break; - case PROCESS_PRIORITY_CLASS_IDLE: - value = L'l'; - break; - } - - if (value != 0) - { - PhAppendStringBuilder2(&sb, L" -priority "); - PhAppendCharStringBuilder(&sb, value); - } - } - - if (PhStartupParameters.PluginParameters) - { - ULONG i; - - for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++) - { - PPH_STRING value = PhStartupParameters.PluginParameters->Items[i]; - PhpAppendCommandLineArgument(&sb, L"plugin", &value->sr); - } - } - - if (PhStartupParameters.SelectTab) - PhpAppendCommandLineArgument(&sb, L"selecttab", &PhStartupParameters.SelectTab->sr); - - if (!(AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY)) - { - if (PhStartupParameters.ShowVisible) - PhAppendStringBuilder2(&sb, L" -v"); - if (PhStartupParameters.ShowHidden) - PhAppendStringBuilder2(&sb, L" -hide"); - } - - // Add user-specified parameters last so they can override the propagated parameters. - if (Parameters) - { - PhAppendCharStringBuilder(&sb, ' '); - PhAppendStringBuilder2(&sb, Parameters); - } - - if (sb.String->Length != 0 && sb.String->Buffer[0] == ' ') - parameters = sb.String->Buffer + 1; - else - parameters = sb.String->Buffer; - } - else - { - parameters = Parameters; - } - - result = PhShellExecuteEx( - hWnd, - FileName ? FileName : PhApplicationFileName->Buffer, - parameters, - ShowWindowType, - Flags, - Timeout, - ProcessHandle - ); - - if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS) - PhDeleteStringBuilder(&sb); - - return result; -} - -BOOLEAN PhCreateProcessIgnoreIfeoDebugger( - _In_ PWSTR FileName - ) -{ - BOOLEAN result; - BOOL (NTAPI *debugSetProcessKillOnExit)(BOOL); - BOOL (NTAPI *debugActiveProcessStop)(DWORD); - BOOLEAN originalValue; - STARTUPINFO startupInfo; - PROCESS_INFORMATION processInfo; - - if (!(debugSetProcessKillOnExit = PhGetModuleProcAddress(L"kernel32.dll", "DebugSetProcessKillOnExit")) || - !(debugActiveProcessStop = PhGetModuleProcAddress(L"kernel32.dll", "DebugActiveProcessStop"))) - return FALSE; - - result = FALSE; - - // This is NOT thread-safe. - originalValue = NtCurrentPeb()->ReadImageFileExecOptions; - NtCurrentPeb()->ReadImageFileExecOptions = FALSE; - - memset(&startupInfo, 0, sizeof(STARTUPINFO)); - startupInfo.cb = sizeof(STARTUPINFO); - memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); - - // The combination of ReadImageFileExecOptions = FALSE and the DEBUG_PROCESS flag - // allows us to skip the Debugger IFEO value. - if (CreateProcess(FileName, NULL, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &processInfo)) - { - // Stop debugging the process now. - debugSetProcessKillOnExit(FALSE); - debugActiveProcessStop(processInfo.dwProcessId); - result = TRUE; - } - - if (processInfo.hProcess) - NtClose(processInfo.hProcess); - if (processInfo.hThread) - NtClose(processInfo.hThread); - - NtCurrentPeb()->ReadImageFileExecOptions = originalValue; - - return result; -} - -VOID PhInitializeTreeNewColumnMenu( - _Inout_ PPH_TN_COLUMN_MENU_DATA Data - ) -{ - PhInitializeTreeNewColumnMenuEx(Data, 0); -} - -VOID PhInitializeTreeNewColumnMenuEx( - _Inout_ PPH_TN_COLUMN_MENU_DATA Data, - _In_ ULONG Flags - ) -{ - PPH_EMENU_ITEM resetSortMenuItem = NULL; - PPH_EMENU_ITEM sizeColumnToFitMenuItem; - PPH_EMENU_ITEM sizeAllColumnsToFitMenuItem; - PPH_EMENU_ITEM hideColumnMenuItem; - PPH_EMENU_ITEM chooseColumnsMenuItem; - ULONG minimumNumberOfColumns; - - Data->Menu = PhCreateEMenu(); - Data->Selection = NULL; - Data->ProcessedId = 0; - - sizeColumnToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID, L"Size column to fit", NULL, NULL); - sizeAllColumnsToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID, L"Size all columns to fit", NULL, NULL); - - if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY)) - { - hideColumnMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_HIDE_COLUMN_ID, L"Hide column", NULL, NULL); - chooseColumnsMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID, L"Choose columns...", NULL, NULL); - } - - if (Flags & PH_TN_COLUMN_MENU_SHOW_RESET_SORT) - { - ULONG sortColumn; - PH_SORT_ORDER sortOrder; - - TreeNew_GetSort(Data->TreeNewHandle, &sortColumn, &sortOrder); - - if (sortOrder != Data->DefaultSortOrder || (Data->DefaultSortOrder != NoSortOrder && sortColumn != Data->DefaultSortColumn)) - resetSortMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_RESET_SORT_ID, L"Reset sort", NULL, NULL); - } - - PhInsertEMenuItem(Data->Menu, sizeColumnToFitMenuItem, -1); - PhInsertEMenuItem(Data->Menu, sizeAllColumnsToFitMenuItem, -1); - - if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY)) - { - PhInsertEMenuItem(Data->Menu, hideColumnMenuItem, -1); - - if (resetSortMenuItem) - PhInsertEMenuItem(Data->Menu, resetSortMenuItem, -1); - - PhInsertEMenuItem(Data->Menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), -1); - PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, -1); - - if (TreeNew_GetFixedColumn(Data->TreeNewHandle)) - minimumNumberOfColumns = 2; // don't allow user to remove all normal columns (the fixed column can never be removed) - else - minimumNumberOfColumns = 1; - - if (!Data->MouseEvent || !Data->MouseEvent->Column || - Data->MouseEvent->Column->Fixed || // don't allow the fixed column to be hidden - TreeNew_GetVisibleColumnCount(Data->TreeNewHandle) < minimumNumberOfColumns + 1 - ) - { - hideColumnMenuItem->Flags |= PH_EMENU_DISABLED; - } - } - else - { - if (resetSortMenuItem) - PhInsertEMenuItem(Data->Menu, resetSortMenuItem, -1); - } - - if (!Data->MouseEvent || !Data->MouseEvent->Column) - { - sizeColumnToFitMenuItem->Flags |= PH_EMENU_DISABLED; - } -} - -VOID PhpEnsureValidSortColumnTreeNew( - _Inout_ HWND TreeNewHandle, - _In_ ULONG DefaultSortColumn, - _In_ PH_SORT_ORDER DefaultSortOrder - ) -{ - ULONG sortColumn; - PH_SORT_ORDER sortOrder; - - // Make sure the column we're sorting by is actually visible, and if not, don't sort anymore. - - TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder); - - if (sortOrder != NoSortOrder) - { - PH_TREENEW_COLUMN column; - - TreeNew_GetColumn(TreeNewHandle, sortColumn, &column); - - if (!column.Visible) - { - if (DefaultSortOrder != NoSortOrder) - { - // Make sure the default sort column is visible. - TreeNew_GetColumn(TreeNewHandle, DefaultSortColumn, &column); - - if (!column.Visible) - { - ULONG maxId; - ULONG id; - BOOLEAN found; - - // Use the first visible column. - maxId = TreeNew_GetMaxId(TreeNewHandle); - id = 0; - found = FALSE; - - while (id <= maxId) - { - if (TreeNew_GetColumn(TreeNewHandle, id, &column)) - { - if (column.Visible) - { - DefaultSortColumn = id; - found = TRUE; - break; - } - } - - id++; - } - - if (!found) - { - DefaultSortColumn = 0; - DefaultSortOrder = NoSortOrder; - } - } - } - - TreeNew_SetSort(TreeNewHandle, DefaultSortColumn, DefaultSortOrder); - } - } -} - -BOOLEAN PhHandleTreeNewColumnMenu( - _Inout_ PPH_TN_COLUMN_MENU_DATA Data - ) -{ - if (!Data->Selection) - return FALSE; - - switch (Data->Selection->Id) - { - case PH_TN_COLUMN_MENU_RESET_SORT_ID: - { - TreeNew_SetSort(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); - } - break; - case PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID: - { - if (Data->MouseEvent && Data->MouseEvent->Column) - { - TreeNew_AutoSizeColumn(Data->TreeNewHandle, Data->MouseEvent->Column->Id, 0); - } - } - break; - case PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID: - { - ULONG maxId; - ULONG id; - - maxId = TreeNew_GetMaxId(Data->TreeNewHandle); - id = 0; - - while (id <= maxId) - { - TreeNew_AutoSizeColumn(Data->TreeNewHandle, id, 0); - id++; - } - } - break; - case PH_TN_COLUMN_MENU_HIDE_COLUMN_ID: - { - PH_TREENEW_COLUMN column; - - if (Data->MouseEvent && Data->MouseEvent->Column && !Data->MouseEvent->Column->Fixed) - { - column.Id = Data->MouseEvent->Column->Id; - column.Visible = FALSE; - TreeNew_SetColumn(Data->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column); - PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); - InvalidateRect(Data->TreeNewHandle, NULL, FALSE); - } - } - break; - case PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID: - { - PhShowChooseColumnsDialog(Data->TreeNewHandle, Data->TreeNewHandle, PH_CONTROL_TYPE_TREE_NEW); - PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); - } - break; - default: - return FALSE; - } - - Data->ProcessedId = Data->Selection->Id; - - return TRUE; -} - -VOID PhDeleteTreeNewColumnMenu( - _In_ PPH_TN_COLUMN_MENU_DATA Data - ) -{ - if (Data->Menu) - { - PhDestroyEMenu(Data->Menu); - Data->Menu = NULL; - } -} - -VOID PhInitializeTreeNewFilterSupport( - _Out_ PPH_TN_FILTER_SUPPORT Support, - _In_ HWND TreeNewHandle, - _In_ PPH_LIST NodeList - ) -{ - Support->FilterList = NULL; - Support->TreeNewHandle = TreeNewHandle; - Support->NodeList = NodeList; -} - -VOID PhDeleteTreeNewFilterSupport( - _In_ PPH_TN_FILTER_SUPPORT Support - ) -{ - PhDereferenceObject(Support->FilterList); -} - -PPH_TN_FILTER_ENTRY PhAddTreeNewFilter( - _In_ PPH_TN_FILTER_SUPPORT Support, - _In_ PPH_TN_FILTER_FUNCTION Filter, - _In_opt_ PVOID Context - ) -{ - PPH_TN_FILTER_ENTRY entry; - - entry = PhAllocate(sizeof(PH_TN_FILTER_ENTRY)); - entry->Filter = Filter; - entry->Context = Context; - - if (!Support->FilterList) - Support->FilterList = PhCreateList(2); - - PhAddItemList(Support->FilterList, entry); - - return entry; -} - -VOID PhRemoveTreeNewFilter( - _In_ PPH_TN_FILTER_SUPPORT Support, - _In_ PPH_TN_FILTER_ENTRY Entry - ) -{ - ULONG index; - - if (!Support->FilterList) - return; - - index = PhFindItemList(Support->FilterList, Entry); - - if (index != -1) - { - PhRemoveItemList(Support->FilterList, index); - PhFree(Entry); - } -} - -BOOLEAN PhApplyTreeNewFiltersToNode( - _In_ PPH_TN_FILTER_SUPPORT Support, - _In_ PPH_TREENEW_NODE Node - ) -{ - BOOLEAN show; - ULONG i; - - show = TRUE; - - if (Support->FilterList) - { - for (i = 0; i < Support->FilterList->Count; i++) - { - PPH_TN_FILTER_ENTRY entry; - - entry = Support->FilterList->Items[i]; - - if (!entry->Filter(Node, entry->Context)) - { - show = FALSE; - break; - } - } - } - - return show; -} - -VOID PhApplyTreeNewFilters( - _In_ PPH_TN_FILTER_SUPPORT Support - ) -{ - ULONG i; - - for (i = 0; i < Support->NodeList->Count; i++) - { - PPH_TREENEW_NODE node; - - node = Support->NodeList->Items[i]; - node->Visible = PhApplyTreeNewFiltersToNode(Support, node); - - if (!node->Visible && node->Selected) - { - node->Selected = FALSE; - } - } - - TreeNew_NodesStructured(Support->TreeNewHandle); -} - -VOID NTAPI PhpCopyCellEMenuItemDeleteFunction( - _In_ struct _PH_EMENU_ITEM *Item - ) -{ - PPH_COPY_CELL_CONTEXT context; - - context = Item->Context; - PhDereferenceObject(context->MenuItemText); - PhFree(context); -} - -BOOLEAN PhInsertCopyCellEMenuItem( - _In_ struct _PH_EMENU_ITEM *Menu, - _In_ ULONG InsertAfterId, - _In_ HWND TreeNewHandle, - _In_ PPH_TREENEW_COLUMN Column - ) -{ - PPH_EMENU_ITEM parentItem; - ULONG indexInParent; - PPH_COPY_CELL_CONTEXT context; - PH_STRINGREF columnText; - PPH_STRING escapedText; - PPH_STRING menuItemText; - PPH_EMENU_ITEM copyCellItem; - - if (!Column) - return FALSE; - - if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent)) - return FALSE; - - indexInParent++; - - context = PhAllocate(sizeof(PH_COPY_CELL_CONTEXT)); - context->TreeNewHandle = TreeNewHandle; - context->Id = Column->Id; - - PhInitializeStringRef(&columnText, Column->Text); - escapedText = PhEscapeStringForMenuPrefix(&columnText); - menuItemText = PhFormatString(L"Copy \"%s\"", escapedText->Buffer); - PhDereferenceObject(escapedText); - copyCellItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context); - copyCellItem->DeleteFunction = PhpCopyCellEMenuItemDeleteFunction; - context->MenuItemText = menuItemText; - - if (Column->CustomDraw) - copyCellItem->Flags |= PH_EMENU_DISABLED; - - PhInsertEMenuItem(parentItem, copyCellItem, indexInParent); - - return TRUE; -} - -BOOLEAN PhHandleCopyCellEMenuItem( - _In_ struct _PH_EMENU_ITEM *SelectedItem - ) -{ - PPH_COPY_CELL_CONTEXT context; - PH_STRING_BUILDER stringBuilder; - ULONG count; - ULONG selectedCount; - ULONG i; - PPH_TREENEW_NODE node; - PH_TREENEW_GET_CELL_TEXT getCellText; - - if (!SelectedItem) - return FALSE; - if (SelectedItem->Id != ID_COPY_CELL) - return FALSE; - - context = SelectedItem->Context; - - PhInitializeStringBuilder(&stringBuilder, 0x100); - count = TreeNew_GetFlatNodeCount(context->TreeNewHandle); - selectedCount = 0; - - for (i = 0; i < count; i++) - { - node = TreeNew_GetFlatNode(context->TreeNewHandle, i); - - if (node && node->Selected) - { - selectedCount++; - - getCellText.Flags = 0; - getCellText.Node = node; - getCellText.Id = context->Id; - PhInitializeEmptyStringRef(&getCellText.Text); - TreeNew_GetCellText(context->TreeNewHandle, &getCellText); - - PhAppendStringBuilder(&stringBuilder, &getCellText.Text); - PhAppendStringBuilder2(&stringBuilder, L"\r\n"); - } - } - - if (stringBuilder.String->Length != 0 && selectedCount == 1) - PhRemoveEndStringBuilder(&stringBuilder, 2); - - PhSetClipboardString(context->TreeNewHandle, &stringBuilder.String->sr); - PhDeleteStringBuilder(&stringBuilder); - - return TRUE; -} - -BOOLEAN PhpSelectFavoriteInRegedit( - _In_ HWND RegeditWindow, - _In_ PPH_STRINGREF FavoriteName, - _In_ BOOLEAN UsePhSvc - ) -{ - HMENU menu; - HMENU favoritesMenu; - ULONG count; - ULONG i; - ULONG id = -1; - - if (!(menu = GetMenu(RegeditWindow))) - return FALSE; - - // Cause the Registry Editor to refresh the Favorites menu. - if (UsePhSvc) - PhSvcCallSendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu); - else - SendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu); - - if (!(favoritesMenu = GetSubMenu(menu, 3))) - return FALSE; - - // Find our entry. - - count = GetMenuItemCount(favoritesMenu); - - if (count == -1) - return FALSE; - if (count > 1000) - count = 1000; - - for (i = 3; i < count; i++) - { - MENUITEMINFO info = { sizeof(MENUITEMINFO) }; - WCHAR buffer[32]; - - info.fMask = MIIM_ID | MIIM_STRING; - info.dwTypeData = buffer; - info.cch = sizeof(buffer) / sizeof(WCHAR); - GetMenuItemInfo(favoritesMenu, i, TRUE, &info); - - if (info.cch == FavoriteName->Length / 2) - { - PH_STRINGREF text; - - text.Buffer = buffer; - text.Length = info.cch * 2; - - if (PhEqualStringRef(&text, FavoriteName, TRUE)) - { - id = info.wID; - break; - } - } - } - - if (id == -1) - return FALSE; - - // Activate our entry. - if (UsePhSvc) - PhSvcCallSendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0); - else - SendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0); - - // "Close" the Favorites menu and restore normal status bar text. - if (UsePhSvc) - PhSvcCallPostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0); - else - PostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0); - - // Bring regedit to the top. - if (IsIconic(RegeditWindow)) - { - ShowWindow(RegeditWindow, SW_RESTORE); - SetForegroundWindow(RegeditWindow); - } - else - { - SetForegroundWindow(RegeditWindow); - } - - return TRUE; -} - -/** - * Opens a key in the Registry Editor. If the Registry Editor is already open, - * the specified key is selected in the Registry Editor. - * - * \param hWnd A handle to the parent window. - * \param KeyName The key name to open. - */ -BOOLEAN PhShellOpenKey2( - _In_ HWND hWnd, - _In_ PPH_STRING KeyName - ) -{ - static PH_STRINGREF favoritesKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit\\Favorites"); - - BOOLEAN result = FALSE; - HWND regeditWindow; - HANDLE favoritesKeyHandle; - WCHAR favoriteName[32]; - UNICODE_STRING valueName; - PH_STRINGREF valueNameSr; - PPH_STRING expandedKeyName; - - regeditWindow = FindWindow(L"RegEdit_RegEdit", NULL); - - if (!regeditWindow) - { - PhShellOpenKey(hWnd, KeyName); - return TRUE; - } - - if (!PhGetOwnTokenAttributes().Elevated) - { - if (!PhUiConnectToPhSvc(hWnd, FALSE)) - return FALSE; - } - - // Create our entry in Favorites. - - if (!NT_SUCCESS(PhCreateKey( - &favoritesKeyHandle, - KEY_WRITE, - PH_KEY_CURRENT_USER, - &favoritesKeyName, - 0, - 0, - NULL - ))) - goto CleanupExit; - - memcpy(favoriteName, L"A_ProcessHacker", 15 * sizeof(WCHAR)); - PhGenerateRandomAlphaString(&favoriteName[15], 16); - RtlInitUnicodeString(&valueName, favoriteName); - PhUnicodeStringToStringRef(&valueName, &valueNameSr); - - expandedKeyName = PhExpandKeyName(KeyName, TRUE); - NtSetValueKey(favoritesKeyHandle, &valueName, 0, REG_SZ, expandedKeyName->Buffer, (ULONG)expandedKeyName->Length + 2); - PhDereferenceObject(expandedKeyName); - - // Select our entry in regedit. - result = PhpSelectFavoriteInRegedit(regeditWindow, &valueNameSr, !PhGetOwnTokenAttributes().Elevated); - - NtDeleteValueKey(favoritesKeyHandle, &valueName); - NtClose(favoritesKeyHandle); - -CleanupExit: - if (!PhGetOwnTokenAttributes().Elevated) - PhUiDisconnectFromPhSvc(); - - return result; -} - -PPH_STRING PhPcre2GetErrorMessage( - _In_ INT ErrorCode - ) -{ - PPH_STRING buffer; - SIZE_T bufferLength; - INT_PTR returnLength; - - bufferLength = 128 * sizeof(WCHAR); - buffer = PhCreateStringEx(NULL, bufferLength); - - while (TRUE) - { - if ((returnLength = pcre2_get_error_message(ErrorCode, buffer->Buffer, bufferLength / sizeof(WCHAR) + 1)) >= 0) - break; - - PhDereferenceObject(buffer); - bufferLength *= 2; - - if (bufferLength > 0x1000 * sizeof(WCHAR)) - break; - - buffer = PhCreateStringEx(NULL, bufferLength); - } - - if (returnLength < 0) - return NULL; - - buffer->Length = returnLength * sizeof(WCHAR); - return buffer; -} +/* + * Process Hacker - + * application support functions + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pcre/pcre2.h" + +GUID XP_CONTEXT_GUID = { 0xbeb1b341, 0x6837, 0x4c83, { 0x83, 0x66, 0x2b, 0x45, 0x1e, 0x7c, 0xe6, 0x9b } }; +GUID VISTA_CONTEXT_GUID = { 0xe2011457, 0x1546, 0x43c5, { 0xa5, 0xfe, 0x00, 0x8d, 0xee, 0xe3, 0xd3, 0xf0 } }; +GUID WIN7_CONTEXT_GUID = { 0x35138b9a, 0x5d96, 0x4fbd, { 0x8e, 0x2d, 0xa2, 0x44, 0x02, 0x25, 0xf9, 0x3a } }; +GUID WIN8_CONTEXT_GUID = { 0x4a2f28e3, 0x53b9, 0x4441, { 0xba, 0x9c, 0xd6, 0x9d, 0x4a, 0x4a, 0x6e, 0x38 } }; +GUID WINBLUE_CONTEXT_GUID = { 0x1f676c76, 0x80e1, 0x4239, { 0x95, 0xbb, 0x83, 0xd0, 0xf6, 0xd0, 0xda, 0x78 } }; +GUID WIN10_CONTEXT_GUID = { 0x8e0f7a12, 0xbfb3, 0x4fe8, { 0xb9, 0xa5, 0x48, 0xfd, 0x50, 0xa1, 0x5a, 0x9a } }; + +/** + * Determines whether a process is suspended. + * + * \param Process The SYSTEM_PROCESS_INFORMATION structure + * of the process. + */ +BOOLEAN PhGetProcessIsSuspended( + _In_ PSYSTEM_PROCESS_INFORMATION Process + ) +{ + ULONG i; + + for (i = 0; i < Process->NumberOfThreads; i++) + { + if ( + Process->Threads[i].ThreadState != Waiting || + Process->Threads[i].WaitReason != Suspended + ) + return FALSE; + } + + return Process->NumberOfThreads != 0; +} + +BOOLEAN PhIsProcessSuspended( + _In_ HANDLE ProcessId + ) +{ + BOOLEAN suspended = FALSE; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + + if (NT_SUCCESS(PhEnumProcesses(&processes))) + { + if (process = PhFindProcessInformation(processes, ProcessId)) + { + suspended = PhGetProcessIsSuspended(process); + } + + PhFree(processes); + } + + return suspended; +} + +/** + * Determines the OS compatibility context of a process. + * + * \param ProcessHandle A handle to a process. + * \param Guid A variable which receives a GUID identifying an + * operating system version. + */ +NTSTATUS PhGetProcessSwitchContext( + _In_ HANDLE ProcessHandle, + _Out_ PGUID Guid + ) +{ + NTSTATUS status; + PROCESS_BASIC_INFORMATION basicInfo; +#ifdef _WIN64 + PVOID peb32; + ULONG data32; +#endif + PVOID data; + + // Reverse-engineered from WdcGetProcessSwitchContext (wdc.dll). + // On Windows 8, the function is now SdbGetAppCompatData (apphelp.dll). + // On Windows 10, the function is again WdcGetProcessSwitchContext. + +#ifdef _WIN64 + if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32) + { + if (WindowsVersion >= WINDOWS_8) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pShimData)), + &data32, + sizeof(ULONG), + NULL + ))) + return status; + } + else + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pContextData)), + &data32, + sizeof(ULONG), + NULL + ))) + return status; + } + + data = UlongToPtr(data32); + } + else + { +#endif + if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo))) + return status; + + if (WindowsVersion >= WINDOWS_8) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pShimData)), + &data, + sizeof(PVOID), + NULL + ))) + return status; + } + else + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pUnused)), + &data, + sizeof(PVOID), + NULL + ))) + return status; + } +#ifdef _WIN64 + } +#endif + + if (!data) + return STATUS_UNSUCCESSFUL; // no compatibility context data + + if (WindowsVersion >= WINDOWS_10_RS5) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(data, 2040 + 24), // Magic value from SbReadProcContextByHandle + Guid, + sizeof(GUID), + NULL + ))) + return status; + } + else if (WindowsVersion >= WINDOWS_10_RS2) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(data, 1544), + Guid, + sizeof(GUID), + NULL + ))) + return status; + } + else if (WindowsVersion >= WINDOWS_10) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(data, 2040 + 24), // Magic value from SbReadProcContextByHandle + Guid, + sizeof(GUID), + NULL + ))) + return status; + } + else if (WindowsVersion >= WINDOWS_8_1) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(data, 2040 + 16), // Magic value from SbReadProcContextByHandle + Guid, + sizeof(GUID), + NULL + ))) + return status; + } + else if (WindowsVersion >= WINDOWS_8) + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(data, 2040), // Magic value from SbReadProcContextByHandle + Guid, + sizeof(GUID), + NULL + ))) + return status; + } + else + { + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(data, 32), // Magic value from WdcGetProcessSwitchContext + Guid, + sizeof(GUID), + NULL + ))) + return status; + } + + return STATUS_SUCCESS; +} + +/** + * Determines the type of a process based on its image file name. + * + * \param ProcessHandle A handle to a process. + * \param KnownProcessType A variable which receives the process + * type. + */ +NTSTATUS PhGetProcessKnownType( + _In_ HANDLE ProcessHandle, + _Out_ PH_KNOWN_PROCESS_TYPE *KnownProcessType + ) +{ + NTSTATUS status; + PROCESS_BASIC_INFORMATION basicInfo; + PPH_STRING fileName; + PPH_STRING newFileName; + + if (!NT_SUCCESS(status = PhGetProcessBasicInformation( + ProcessHandle, + &basicInfo + ))) + return status; + + if (basicInfo.UniqueProcessId == SYSTEM_PROCESS_ID) + { + *KnownProcessType = SystemProcessType; + return STATUS_SUCCESS; + } + + if (!NT_SUCCESS(status = PhGetProcessImageFileName( + ProcessHandle, + &fileName + ))) + { + return status; + } + + newFileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + + *KnownProcessType = PhGetProcessKnownTypeEx( + basicInfo.UniqueProcessId, + newFileName + ); + + PhDereferenceObject(newFileName); + + return status; +} + +PH_KNOWN_PROCESS_TYPE PhGetProcessKnownTypeEx( + _In_ HANDLE ProcessId, + _In_ PPH_STRING FileName + ) +{ + PH_KNOWN_PROCESS_TYPE knownProcessType; + PH_STRINGREF systemRootPrefix; + PPH_STRING fileName; + PH_STRINGREF name; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; +#endif + + if (ProcessId == SYSTEM_PROCESS_ID || ProcessId == SYSTEM_IDLE_PROCESS_ID) + return SystemProcessType; + + if (PhIsNullOrEmptyString(FileName)) + return UnknownProcessType; + + PhGetSystemRoot(&systemRootPrefix); + + fileName = PhReferenceObject(FileName); + name = fileName->sr; + + knownProcessType = UnknownProcessType; + + if (PhStartsWithStringRef(&name, &systemRootPrefix, TRUE)) + { + // Skip the system root, and we now have three cases: + // 1. \\xyz.exe - Windows executable. + // 2. \\System32\\xyz.exe - system32 executable. + // 3. \\SysWow64\\xyz.exe - system32 executable + WOW64. + PhSkipStringRef(&name, systemRootPrefix.Length); + + if (PhEqualStringRef2(&name, L"\\explorer.exe", TRUE)) + { + knownProcessType = ExplorerProcessType; + } + else if ( + PhStartsWithStringRef2(&name, L"\\System32", TRUE) +#ifdef _WIN64 + || (PhStartsWithStringRef2(&name, L"\\SysWow64", TRUE) && (isWow64 = TRUE, TRUE)) // ugly but necessary +#endif + ) + { + // SysTem32 and SysWow64 are both 8 characters long. + PhSkipStringRef(&name, 9 * sizeof(WCHAR)); + + if (FALSE) + ; // Dummy + else if (PhEqualStringRef2(&name, L"\\smss.exe", TRUE)) + knownProcessType = SessionManagerProcessType; + else if (PhEqualStringRef2(&name, L"\\csrss.exe", TRUE)) + knownProcessType = WindowsSubsystemProcessType; + else if (PhEqualStringRef2(&name, L"\\wininit.exe", TRUE)) + knownProcessType = WindowsStartupProcessType; + else if (PhEqualStringRef2(&name, L"\\services.exe", TRUE)) + knownProcessType = ServiceControlManagerProcessType; + else if (PhEqualStringRef2(&name, L"\\lsass.exe", TRUE)) + knownProcessType = LocalSecurityAuthorityProcessType; + else if (PhEqualStringRef2(&name, L"\\lsm.exe", TRUE)) + knownProcessType = LocalSessionManagerProcessType; + else if (PhEqualStringRef2(&name, L"\\winlogon.exe", TRUE)) + knownProcessType = WindowsLogonProcessType; + else if (PhEqualStringRef2(&name, L"\\svchost.exe", TRUE)) + knownProcessType = ServiceHostProcessType; + else if (PhEqualStringRef2(&name, L"\\rundll32.exe", TRUE)) + knownProcessType = RunDllAsAppProcessType; + else if (PhEqualStringRef2(&name, L"\\dllhost.exe", TRUE)) + knownProcessType = ComSurrogateProcessType; + else if (PhEqualStringRef2(&name, L"\\taskeng.exe", TRUE)) + knownProcessType = TaskHostProcessType; + else if (PhEqualStringRef2(&name, L"\\taskhost.exe", TRUE)) + knownProcessType = TaskHostProcessType; + else if (PhEqualStringRef2(&name, L"\\taskhostex.exe", TRUE)) + knownProcessType = TaskHostProcessType; + else if (PhEqualStringRef2(&name, L"\\taskhostw.exe", TRUE)) + knownProcessType = TaskHostProcessType; + else if (PhEqualStringRef2(&name, L"\\wudfhost.exe", TRUE)) + knownProcessType = UmdfHostProcessType; + else if (PhEqualStringRef2(&name, L"\\wbem\\WmiPrvSE.exe", TRUE)) + knownProcessType = WmiProviderHostType; + else if (PhEqualStringRef2(&name, L"\\MicrosoftEdgeCP.exe", TRUE)) // RS5 + knownProcessType = EdgeProcessType; + else if (PhEqualStringRef2(&name, L"\\MicrosoftEdgeSH.exe", TRUE)) // RS5 + knownProcessType = EdgeProcessType; + } + else + { + if (PhEndsWithStringRef2(&name, L"\\MicrosoftEdgeCP.exe", TRUE)) // RS4 + knownProcessType = EdgeProcessType; + else if (PhEndsWithStringRef2(&name, L"\\MicrosoftEdge.exe", TRUE)) + knownProcessType = EdgeProcessType; + else if (PhEndsWithStringRef2(&name, L"\\ServiceWorkerHost.exe", TRUE)) + knownProcessType = EdgeProcessType; + else if (PhEndsWithStringRef2(&name, L"\\Windows.WARP.JITService.exe", TRUE)) + knownProcessType = EdgeProcessType; + } + } + + PhDereferenceObject(fileName); + +#ifdef _WIN64 + if (isWow64) + knownProcessType |= KnownProcessWow64; +#endif + + return knownProcessType; +} + +static BOOLEAN NTAPI PhpSvchostCommandLineCallback( + _In_opt_ PPH_COMMAND_LINE_OPTION Option, + _In_opt_ PPH_STRING Value, + _In_opt_ PVOID Context + ) +{ + PPH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine = Context; + + if (Option && Option->Id == 1) + { + PhSwapReference(&knownCommandLine->ServiceHost.GroupName, Value); + } + + return TRUE; +} + +BOOLEAN PhaGetProcessKnownCommandLine( + _In_ PPH_STRING CommandLine, + _In_ PH_KNOWN_PROCESS_TYPE KnownProcessType, + _Out_ PPH_KNOWN_PROCESS_COMMAND_LINE KnownCommandLine + ) +{ + switch (KnownProcessType & KnownProcessTypeMask) + { + case ServiceHostProcessType: + { + // svchost.exe -k + + static PH_COMMAND_LINE_OPTION options[] = + { + { 1, L"k", MandatoryArgumentType } + }; + + KnownCommandLine->ServiceHost.GroupName = NULL; + + PhParseCommandLine( + &CommandLine->sr, + options, + sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), + PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS, + PhpSvchostCommandLineCallback, + KnownCommandLine + ); + + if (KnownCommandLine->ServiceHost.GroupName) + { + PH_AUTO(KnownCommandLine->ServiceHost.GroupName); + return TRUE; + } + else + { + return FALSE; + } + } + break; + case RunDllAsAppProcessType: + { + // rundll32.exe , ... + + SIZE_T i; + PH_STRINGREF dllNamePart; + PH_STRINGREF procedureNamePart; + PPH_STRING dllName; + PPH_STRING procedureName; + + i = 0; + + // Get the rundll32.exe part. + + dllName = PhParseCommandLinePart(&CommandLine->sr, &i); + + if (!dllName) + return FALSE; + + PhDereferenceObject(dllName); + + // Get the DLL name part. + + while (i < CommandLine->Length / sizeof(WCHAR) && CommandLine->Buffer[i] == ' ') + i++; + + dllName = PhParseCommandLinePart(&CommandLine->sr, &i); + + if (!dllName) + return FALSE; + + PH_AUTO(dllName); + + // The procedure name begins after the last comma. + + if (!PhSplitStringRefAtLastChar(&dllName->sr, ',', &dllNamePart, &procedureNamePart)) + return FALSE; + + dllName = PH_AUTO(PhCreateString2(&dllNamePart)); + procedureName = PH_AUTO(PhCreateString2(&procedureNamePart)); + + // If the DLL name isn't an absolute path, assume it's in system32. + // TODO: Use a proper search function. + + if (RtlDetermineDosPathNameType_U(dllName->Buffer) == RtlPathTypeRelative) + { + dllName = PhaConcatStrings( + 3, + PH_AUTO_T(PH_STRING, PhGetSystemDirectory())->Buffer, + L"\\", + dllName->Buffer + ); + } + + KnownCommandLine->RunDllAsApp.FileName = dllName; + KnownCommandLine->RunDllAsApp.ProcedureName = procedureName; + } + break; + case ComSurrogateProcessType: + { + // dllhost.exe /processid: + + static PH_STRINGREF inprocServer32Name = PH_STRINGREF_INIT(L"InprocServer32"); + + SIZE_T i; + ULONG_PTR indexOfProcessId; + PPH_STRING argPart; + PPH_STRING guidString; + UNICODE_STRING guidStringUs; + GUID guid; + HANDLE rootKeyHandle; + HANDLE inprocServer32KeyHandle; + PPH_STRING fileName; + + i = 0; + + // Get the dllhost.exe part. + + argPart = PhParseCommandLinePart(&CommandLine->sr, &i); + + if (!argPart) + return FALSE; + + PhDereferenceObject(argPart); + + // Get the argument part. + + while (i < (ULONG)CommandLine->Length / sizeof(WCHAR) && CommandLine->Buffer[i] == ' ') + i++; + + argPart = PhParseCommandLinePart(&CommandLine->sr, &i); + + if (!argPart) + return FALSE; + + PH_AUTO(argPart); + + // Find "/processid:"; the GUID is just after that. + + _wcsupr(argPart->Buffer); + indexOfProcessId = PhFindStringInString(argPart, 0, L"/PROCESSID:"); + + if (indexOfProcessId == -1) + return FALSE; + + guidString = PhaSubstring( + argPart, + indexOfProcessId + 11, + (ULONG)argPart->Length / sizeof(WCHAR) - indexOfProcessId - 11 + ); + PhStringRefToUnicodeString(&guidString->sr, &guidStringUs); + + if (!NT_SUCCESS(RtlGUIDFromString( + &guidStringUs, + &guid + ))) + return FALSE; + + KnownCommandLine->ComSurrogate.Guid = guid; + KnownCommandLine->ComSurrogate.Name = NULL; + KnownCommandLine->ComSurrogate.FileName = NULL; + + // Lookup the GUID in the registry to determine the name and file name. + + if (NT_SUCCESS(PhOpenKey( + &rootKeyHandle, + KEY_READ, + PH_KEY_CLASSES_ROOT, + &PhaConcatStrings2(L"CLSID\\", guidString->Buffer)->sr, + 0 + ))) + { + KnownCommandLine->ComSurrogate.Name = + PH_AUTO(PhQueryRegistryString(rootKeyHandle, NULL)); + + if (NT_SUCCESS(PhOpenKey( + &inprocServer32KeyHandle, + KEY_READ, + rootKeyHandle, + &inprocServer32Name, + 0 + ))) + { + KnownCommandLine->ComSurrogate.FileName = + PH_AUTO(PhQueryRegistryString(inprocServer32KeyHandle, NULL)); + + if (fileName = PH_AUTO(PhExpandEnvironmentStrings( + &KnownCommandLine->ComSurrogate.FileName->sr + ))) + { + KnownCommandLine->ComSurrogate.FileName = fileName; + } + + NtClose(inprocServer32KeyHandle); + } + + NtClose(rootKeyHandle); + } + else if (NT_SUCCESS(PhOpenKey( + &rootKeyHandle, + KEY_READ, + PH_KEY_CLASSES_ROOT, + &PhaConcatStrings2(L"AppID\\", guidString->Buffer)->sr, + 0 + ))) + { + KnownCommandLine->ComSurrogate.Name = + PH_AUTO(PhQueryRegistryString(rootKeyHandle, NULL)); + NtClose(rootKeyHandle); + } + } + break; + default: + return FALSE; + } + + return TRUE; +} + +PPH_STRING PhGetServiceRelevantFileName( + _In_ PPH_STRINGREF ServiceName, + _In_ SC_HANDLE ServiceHandle + ) +{ + PPH_STRING fileName = NULL; + LPQUERY_SERVICE_CONFIG config; + + if (config = PhGetServiceConfig(ServiceHandle)) + { + PhGetServiceDllParameter(config->dwServiceType, ServiceName, &fileName); + + if (!fileName) + { + PPH_STRING commandLine; + + if (config->lpBinaryPathName[0]) + { + commandLine = PhCreateString(config->lpBinaryPathName); + + if (config->dwServiceType & SERVICE_WIN32) + { + PH_STRINGREF dummyFileName; + PH_STRINGREF dummyArguments; + + PhParseCommandLineFuzzy(&commandLine->sr, &dummyFileName, &dummyArguments, &fileName); + + if (!fileName) + PhSwapReference(&fileName, commandLine); + } + else + { + fileName = PhGetFileName(commandLine); + } + + PhDereferenceObject(commandLine); + } + } + + PhFree(config); + } + + return fileName; +} + +PPH_STRING PhEscapeStringForDelimiter( + _In_ PPH_STRING String, + _In_ WCHAR Delimiter + ) +{ + PH_STRING_BUILDER stringBuilder; + SIZE_T length; + SIZE_T i; + WCHAR temp[2]; + + length = String->Length / sizeof(WCHAR); + PhInitializeStringBuilder(&stringBuilder, String->Length / sizeof(WCHAR) * 3); + + temp[0] = '\\'; + + for (i = 0; i < length; i++) + { + if (String->Buffer[i] == '\\' || String->Buffer[i] == Delimiter) + { + temp[1] = String->Buffer[i]; + PhAppendStringBuilderEx(&stringBuilder, temp, 4); + } + else + { + PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); + } + } + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_STRING PhUnescapeStringForDelimiter( + _In_ PPH_STRING String, + _In_ WCHAR Delimiter + ) +{ + PH_STRING_BUILDER stringBuilder; + SIZE_T length; + SIZE_T i; + + length = String->Length / sizeof(WCHAR); + PhInitializeStringBuilder(&stringBuilder, String->Length / sizeof(WCHAR) * 3); + + for (i = 0; i < length; i++) + { + if (String->Buffer[i] == '\\') + { + if (i != length - 1) + { + PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i + 1]); + i++; + } + else + { + // Trailing backslash. Just ignore it. + break; + } + } + else + { + PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); + } + } + + return PhFinalStringBuilderString(&stringBuilder); +} + +VOID PhSearchOnlineString( + _In_ HWND hWnd, + _In_ PWSTR String + ) +{ + PhShellExecuteUserString(hWnd, L"SearchEngine", String, TRUE, NULL); +} + +VOID PhShellExecuteUserString( + _In_ HWND hWnd, + _In_ PWSTR Setting, + _In_ PWSTR String, + _In_ BOOLEAN UseShellExecute, + _In_opt_ PWSTR ErrorMessage + ) +{ + static PH_STRINGREF replacementToken = PH_STRINGREF_INIT(L"%s"); + PPH_STRING applicationDirectory; + PPH_STRING executeString; + PH_STRINGREF stringBefore; + PH_STRINGREF stringAfter; + PPH_STRING ntMessage; + + if (!(applicationDirectory = PhGetApplicationDirectory())) + { + PhShowStatus(hWnd, L"Unable to locate the application directory.", STATUS_NOT_FOUND, 0); + return; + } + + // Get the execute command. + executeString = PhGetStringSetting(Setting); + + // Expand environment strings. + PhMoveReference(&executeString, PhExpandEnvironmentStrings(&executeString->sr)); + + // Make sure the user executable string is absolute. We can't use RtlDetermineDosPathNameType_U + // here because the string may be a URL. + if (PhFindCharInString(executeString, 0, ':') == -1) + { + INT stringArgCount; + PWSTR* stringArgList; + + // (dmex) HACK: Escape the individual executeString components. + if ((stringArgList = CommandLineToArgvW(executeString->Buffer, &stringArgCount)) && stringArgCount == 2) + { + PPH_STRING fileName = PhCreateString(stringArgList[0]); + PPH_STRING fileArgs = PhCreateString(stringArgList[1]); + + // Make sure the string is absolute and escape the filename. + if (RtlDetermineDosPathNameType_U(fileName->Buffer) == RtlPathTypeRelative) + PhMoveReference(&fileName, PhConcatStrings(4, L"\"", applicationDirectory->Buffer, fileName->Buffer, L"\"")); + else + PhMoveReference(&fileName, PhConcatStrings(3, L"\"", fileName->Buffer, L"\"")); + + // Escape the parameters. + PhMoveReference(&fileArgs, PhConcatStrings(3, L"\"", fileArgs->Buffer, L"\"")); + + // Create the escaped execute string. + PhMoveReference(&executeString, PhConcatStrings(3, fileName->Buffer, L" ", fileArgs->Buffer)); + + PhDereferenceObject(fileArgs); + PhDereferenceObject(fileName); + LocalFree(stringArgList); + } + else + { + if (RtlDetermineDosPathNameType_U(executeString->Buffer) == RtlPathTypeRelative) + PhMoveReference(&executeString, PhConcatStrings(4, L"\"", applicationDirectory->Buffer, executeString->Buffer, L"\"")); + else + PhMoveReference(&executeString, PhConcatStrings(3, L"\"", executeString->Buffer, L"\"")); + } + } + + // Replace the token with the string, or use the original string if the token is not present. + if (PhSplitStringRefAtString(&executeString->sr, &replacementToken, FALSE, &stringBefore, &stringAfter)) + { + PPH_STRING stringTemp; + PPH_STRING stringMiddle; + + // Note: This code is needed to solve issues with faulty RamDisk software that doesn't use the Mount Manager API + // and instead returns \device\ FileName strings. We also can't change the way the process provider stores + // the FileName string since it'll break various features and use-cases required by developers + // who need the raw untranslated FileName string. + stringTemp = PhCreateString(String); + stringMiddle = PhGetFileName(stringTemp); + + PhMoveReference(&executeString, PhConcatStringRef3(&stringBefore, &stringMiddle->sr, &stringAfter)); + + PhDereferenceObject(stringMiddle); + PhDereferenceObject(stringTemp); + } + + if (UseShellExecute) + { + PhShellExecute(hWnd, executeString->Buffer, NULL); + } + else + { + NTSTATUS status; + + status = PhCreateProcessWin32(NULL, executeString->Buffer, NULL, NULL, 0, NULL, NULL, NULL); + + if (!NT_SUCCESS(status)) + { + if (ErrorMessage) + { + ntMessage = PhGetNtMessage(status); + PhShowError2( + hWnd, + L"Unable to execute the command.", + L"%s\n%s", + PhGetStringOrDefault(ntMessage, L"An unknown error occurred."), + ErrorMessage + ); + PhDereferenceObject(ntMessage); + } + else + { + PhShowStatus(hWnd, L"Unable to execute the command.", status, 0); + } + } + } + + PhDereferenceObject(executeString); + PhDereferenceObject(applicationDirectory); +} + +VOID PhLoadSymbolProviderOptions( + _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider + ) +{ + PPH_STRING searchPath; + + PhSetOptionsSymbolProvider( + SYMOPT_UNDNAME, + PhGetIntegerSetting(L"DbgHelpUndecorate") ? SYMOPT_UNDNAME : 0 + ); + + searchPath = PhGetStringSetting(L"DbgHelpSearchPath"); + + if (searchPath->Length != 0) + PhSetSearchPathSymbolProvider(SymbolProvider, searchPath->Buffer); + + PhDereferenceObject(searchPath); +} + +/** + * Copies a string into a NMLVGETINFOTIP structure. + * + * \param GetInfoTip The NMLVGETINFOTIP structure. + * \param Tip The string to copy. + * + * \remarks The text is truncated if it is too long. + */ +VOID PhCopyListViewInfoTip( + _Inout_ LPNMLVGETINFOTIP GetInfoTip, + _In_ PPH_STRINGREF Tip + ) +{ + ULONG copyIndex; + ULONG bufferRemaining; + ULONG copyLength; + + if (GetInfoTip->dwFlags == 0) + { + copyIndex = (ULONG)PhCountStringZ(GetInfoTip->pszText) + 1; // plus one for newline + + if (GetInfoTip->cchTextMax - copyIndex < 2) // need at least two bytes + return; + + bufferRemaining = GetInfoTip->cchTextMax - copyIndex - 1; + GetInfoTip->pszText[copyIndex - 1] = '\n'; + } + else + { + copyIndex = 0; + bufferRemaining = GetInfoTip->cchTextMax; + } + + copyLength = min((ULONG)Tip->Length / sizeof(WCHAR), bufferRemaining - 1); + memcpy( + &GetInfoTip->pszText[copyIndex], + Tip->Buffer, + copyLength * 2 + ); + GetInfoTip->pszText[copyIndex + copyLength] = UNICODE_NULL; +} + +VOID PhCopyListView( + _In_ HWND ListViewHandle + ) +{ + PPH_STRING text; + + text = PhGetListViewText(ListViewHandle); + PhSetClipboardString(ListViewHandle, &text->sr); + PhDereferenceObject(text); +} + +VOID PhHandleListViewNotifyForCopy( + _In_ LPARAM lParam, + _In_ HWND ListViewHandle + ) +{ + PhHandleListViewNotifyBehaviors(lParam, ListViewHandle, PH_LIST_VIEW_CTRL_C_BEHAVIOR); +} + +VOID PhHandleListViewNotifyBehaviors( + _In_ LPARAM lParam, + _In_ HWND ListViewHandle, + _In_ ULONG Behaviors + ) +{ + if (((LPNMHDR)lParam)->hwndFrom == ListViewHandle && ((LPNMHDR)lParam)->code == LVN_KEYDOWN) + { + LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)lParam; + + switch (keyDown->wVKey) + { + case 'C': + if (Behaviors & PH_LIST_VIEW_CTRL_C_BEHAVIOR) + { + if (GetKeyState(VK_CONTROL) < 0) + PhCopyListView(ListViewHandle); + } + break; + case 'A': + if (Behaviors & PH_LIST_VIEW_CTRL_A_BEHAVIOR) + { + if (GetKeyState(VK_CONTROL) < 0) + PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED); + } + break; + } + } +} + +BOOLEAN PhGetListViewContextMenuPoint( + _In_ HWND ListViewHandle, + _Out_ PPOINT Point + ) +{ + INT selectedIndex; + RECT bounds; + RECT clientRect; + + // The user pressed a key to display the context menu. + // Suggest where the context menu should display. + + if ((selectedIndex = ListView_GetNextItem(ListViewHandle, -1, LVNI_SELECTED)) != -1) + { + if (ListView_GetItemRect(ListViewHandle, selectedIndex, &bounds, LVIR_BOUNDS)) + { + Point->x = bounds.left + PhSmallIconSize.X / 2; + Point->y = bounds.top + PhSmallIconSize.Y / 2; + + GetClientRect(ListViewHandle, &clientRect); + + if (Point->x < 0 || Point->y < 0 || Point->x >= clientRect.right || Point->y >= clientRect.bottom) + { + // The menu is going to be outside of the control. Just put it at the top-left. + Point->x = 0; + Point->y = 0; + } + + ClientToScreen(ListViewHandle, Point); + + return TRUE; + } + } + + Point->x = 0; + Point->y = 0; + ClientToScreen(ListViewHandle, Point); + + return FALSE; +} + +VOID PhSetWindowOpacity( + _In_ HWND WindowHandle, + _In_ ULONG OpacityPercent + ) +{ + if (OpacityPercent == 0) + { + // Make things a bit faster by removing the WS_EX_LAYERED bit. + PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, 0); + RedrawWindow(WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); + return; + } + + PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED); + + // Disallow opacity values of less than 10%. + OpacityPercent = min(OpacityPercent, 90); + + // The opacity value is backwards - 0 means opaque, 100 means transparent. + SetLayeredWindowAttributes( + WindowHandle, + 0, + (BYTE)(255 * (100 - OpacityPercent) / 100), + LWA_ALPHA + ); +} + +PPH_STRING PhGetPhVersion( + VOID + ) +{ + PH_FORMAT format[5]; + + PhInitFormatU(&format[0], PHAPP_VERSION_MAJOR); + PhInitFormatC(&format[1], '.'); + PhInitFormatU(&format[2], PHAPP_VERSION_MINOR); + PhInitFormatC(&format[3], '.'); + PhInitFormatU(&format[4], PHAPP_VERSION_REVISION); + + return PhFormat(format, 5, 16); +} + +VOID PhGetPhVersionNumbers( + _Out_opt_ PULONG MajorVersion, + _Out_opt_ PULONG MinorVersion, + _Out_opt_ PULONG BuildNumber, + _Out_opt_ PULONG RevisionNumber + ) +{ + if (MajorVersion) + *MajorVersion = PHAPP_VERSION_MAJOR; + if (MinorVersion) + *MinorVersion = PHAPP_VERSION_MINOR; + if (BuildNumber) + *BuildNumber = PHAPP_VERSION_BUILD; + if (RevisionNumber) + *RevisionNumber = PHAPP_VERSION_REVISION; +} + +VOID PhWritePhTextHeader( + _Inout_ PPH_FILE_STREAM FileStream + ) +{ + PPH_STRING version; + LARGE_INTEGER time; + SYSTEMTIME systemTime; + PPH_STRING timeString; + + PhWriteStringAsUtf8FileStream2(FileStream, L"Process Hacker "); + + if (version = PhGetPhVersion()) + { + PhWriteStringAsUtf8FileStream(FileStream, &version->sr); + PhDereferenceObject(version); + } + + PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\nWindows NT %lu.%lu", PhOsVersion.dwMajorVersion, PhOsVersion.dwMinorVersion); + + if (PhOsVersion.szCSDVersion[0] != UNICODE_NULL) + PhWriteStringFormatAsUtf8FileStream(FileStream, L" %s", PhOsVersion.szCSDVersion); + +#ifdef _WIN64 + PhWriteStringAsUtf8FileStream2(FileStream, L" (64-bit)"); +#else + PhWriteStringAsUtf8FileStream2(FileStream, L" (32-bit)"); +#endif + + PhQuerySystemTime(&time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + + timeString = PhFormatDateTime(&systemTime); + PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\n%s\r\n\r\n", timeString->Buffer); + PhDereferenceObject(timeString); +} + +BOOLEAN PhShellProcessHacker( + _In_opt_ HWND hWnd, + _In_opt_ PWSTR Parameters, + _In_ ULONG ShowWindowType, + _In_ ULONG Flags, + _In_ ULONG AppFlags, + _In_opt_ ULONG Timeout, + _Out_opt_ PHANDLE ProcessHandle + ) +{ + return PhShellProcessHackerEx( + hWnd, + NULL, + Parameters, + ShowWindowType, + Flags, + AppFlags, + Timeout, + ProcessHandle + ); +} + +VOID PhpAppendCommandLineArgument( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ PWSTR Name, + _In_ PPH_STRINGREF Value + ) +{ + PPH_STRING temp; + + PhAppendStringBuilder2(StringBuilder, L" -"); + PhAppendStringBuilder2(StringBuilder, Name); + PhAppendStringBuilder2(StringBuilder, L" \""); + temp = PhEscapeCommandLinePart(Value); + PhAppendStringBuilder(StringBuilder, &temp->sr); + PhDereferenceObject(temp); + PhAppendCharStringBuilder(StringBuilder, '\"'); +} + +BOOLEAN PhShellProcessHackerEx( + _In_opt_ HWND hWnd, + _In_opt_ PWSTR FileName, + _In_opt_ PWSTR Parameters, + _In_ ULONG ShowWindowType, + _In_ ULONG Flags, + _In_ ULONG AppFlags, + _In_opt_ ULONG Timeout, + _Out_opt_ PHANDLE ProcessHandle + ) +{ + BOOLEAN result; + PPH_STRING applicationFileName; + PH_STRING_BUILDER sb; + PWSTR parameters; + + if (!(applicationFileName = PhGetApplicationFileName())) + return FALSE; + + if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS) + { + PhInitializeStringBuilder(&sb, 128); + + // Propagate parameters. + + if (PhStartupParameters.NoSettings) + { + PhAppendStringBuilder2(&sb, L" -nosettings"); + } + else if (PhStartupParameters.SettingsFileName && (PhSettingsFileName || (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS_FORCE_SETTINGS))) + { + PPH_STRINGREF fileName; + + if (PhSettingsFileName) + fileName = &PhSettingsFileName->sr; + else + fileName = &PhStartupParameters.SettingsFileName->sr; + + PhpAppendCommandLineArgument(&sb, L"settings", fileName); + } + + if (PhStartupParameters.NoKph) + PhAppendStringBuilder2(&sb, L" -nokph"); + if (PhStartupParameters.InstallKph) + PhAppendStringBuilder2(&sb, L" -installkph"); + if (PhStartupParameters.UninstallKph) + PhAppendStringBuilder2(&sb, L" -uninstallkph"); + if (PhStartupParameters.Debug) + PhAppendStringBuilder2(&sb, L" -debug"); + if (PhStartupParameters.NoPlugins) + PhAppendStringBuilder2(&sb, L" -noplugins"); + if (PhStartupParameters.NewInstance) + PhAppendStringBuilder2(&sb, L" -newinstance"); + + if (PhStartupParameters.SelectPid != 0) + PhAppendFormatStringBuilder(&sb, L" -selectpid %lu", PhStartupParameters.SelectPid); + + if (PhStartupParameters.PriorityClass != 0) + { + CHAR value = 0; + + switch (PhStartupParameters.PriorityClass) + { + case PROCESS_PRIORITY_CLASS_REALTIME: + value = L'r'; + break; + case PROCESS_PRIORITY_CLASS_HIGH: + value = L'h'; + break; + case PROCESS_PRIORITY_CLASS_NORMAL: + value = L'n'; + break; + case PROCESS_PRIORITY_CLASS_IDLE: + value = L'l'; + break; + } + + if (value != 0) + { + PhAppendStringBuilder2(&sb, L" -priority "); + PhAppendCharStringBuilder(&sb, value); + } + } + + if (PhStartupParameters.PluginParameters) + { + ULONG i; + + for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++) + { + PPH_STRING value = PhStartupParameters.PluginParameters->Items[i]; + PhpAppendCommandLineArgument(&sb, L"plugin", &value->sr); + } + } + + if (PhStartupParameters.SelectTab) + PhpAppendCommandLineArgument(&sb, L"selecttab", &PhStartupParameters.SelectTab->sr); + + if (!(AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY)) + { + if (PhStartupParameters.ShowVisible) + PhAppendStringBuilder2(&sb, L" -v"); + if (PhStartupParameters.ShowHidden) + PhAppendStringBuilder2(&sb, L" -hide"); + } + + // Add user-specified parameters last so they can override the propagated parameters. + if (Parameters) + { + PhAppendCharStringBuilder(&sb, ' '); + PhAppendStringBuilder2(&sb, Parameters); + } + + if (sb.String->Length != 0 && sb.String->Buffer[0] == ' ') + parameters = sb.String->Buffer + 1; + else + parameters = sb.String->Buffer; + } + else + { + parameters = Parameters; + } + + result = PhShellExecuteEx( + hWnd, + FileName ? FileName : PhGetString(applicationFileName), + parameters, + ShowWindowType, + Flags, + Timeout, + ProcessHandle + ); + + if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS) + PhDeleteStringBuilder(&sb); + + PhDereferenceObject(applicationFileName); + + return result; +} + +BOOLEAN PhCreateProcessIgnoreIfeoDebugger( + _In_ PWSTR FileName + ) +{ + BOOLEAN result; + BOOLEAN originalValue; + HANDLE processHandle; + + result = FALSE; + + RtlEnterCriticalSection(NtCurrentPeb()->FastPebLock); + originalValue = NtCurrentPeb()->ReadImageFileExecOptions; + NtCurrentPeb()->ReadImageFileExecOptions = FALSE; + RtlLeaveCriticalSection(NtCurrentPeb()->FastPebLock); + + // The combination of ReadImageFileExecOptions = FALSE and the DEBUG_PROCESS flag + // allows us to skip the Debugger IFEO value. (wj32) + + if (NT_SUCCESS(PhCreateProcessWin32( + FileName, + NULL, + NULL, + NULL, + PH_CREATE_PROCESS_DEBUG | PH_CREATE_PROCESS_DEBUG_ONLY_THIS_PROCESS, + NULL, + &processHandle, + NULL + ))) + { + HANDLE debugObjectHandle; + + if (NT_SUCCESS(PhGetProcessDebugObject( + processHandle, + &debugObjectHandle + ))) + { + // Disable kill-on-close. + if (NT_SUCCESS(PhSetDebugKillProcessOnExit( + debugObjectHandle, + FALSE + ))) + { + // Stop debugging the process now. + NtRemoveProcessDebug(processHandle, debugObjectHandle); + } + + NtClose(debugObjectHandle); + } + + result = TRUE; + + NtClose(processHandle); + } + + if (originalValue) + { + RtlEnterCriticalSection(NtCurrentPeb()->FastPebLock); + NtCurrentPeb()->ReadImageFileExecOptions = originalValue; + RtlLeaveCriticalSection(NtCurrentPeb()->FastPebLock); + } + + return result; +} + +VOID PhInitializeTreeNewColumnMenu( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data + ) +{ + PhInitializeTreeNewColumnMenuEx(Data, 0); +} + +VOID PhInitializeTreeNewColumnMenuEx( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data, + _In_ ULONG Flags + ) +{ + PPH_EMENU_ITEM resetSortMenuItem = NULL; + PPH_EMENU_ITEM sizeColumnToFitMenuItem; + PPH_EMENU_ITEM sizeAllColumnsToFitMenuItem; + PPH_EMENU_ITEM hideColumnMenuItem; + PPH_EMENU_ITEM chooseColumnsMenuItem; + ULONG minimumNumberOfColumns; + + Data->Menu = PhCreateEMenu(); + Data->Selection = NULL; + Data->ProcessedId = 0; + + sizeColumnToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID, L"Size column to fit", NULL, NULL); + sizeAllColumnsToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID, L"Size all columns to fit", NULL, NULL); + + if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY)) + { + hideColumnMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_HIDE_COLUMN_ID, L"Hide column", NULL, NULL); + chooseColumnsMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID, L"Choose columns...", NULL, NULL); + } + + if (Flags & PH_TN_COLUMN_MENU_SHOW_RESET_SORT) + { + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + TreeNew_GetSort(Data->TreeNewHandle, &sortColumn, &sortOrder); + + if (sortOrder != Data->DefaultSortOrder || (Data->DefaultSortOrder != NoSortOrder && sortColumn != Data->DefaultSortColumn)) + resetSortMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_RESET_SORT_ID, L"Reset sort", NULL, NULL); + } + + PhInsertEMenuItem(Data->Menu, sizeColumnToFitMenuItem, ULONG_MAX); + PhInsertEMenuItem(Data->Menu, sizeAllColumnsToFitMenuItem, ULONG_MAX); + + if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY)) + { + PhInsertEMenuItem(Data->Menu, hideColumnMenuItem, ULONG_MAX); + + if (resetSortMenuItem) + PhInsertEMenuItem(Data->Menu, resetSortMenuItem, ULONG_MAX); + + PhInsertEMenuItem(Data->Menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, ULONG_MAX); + + if (TreeNew_GetFixedColumn(Data->TreeNewHandle)) + minimumNumberOfColumns = 2; // don't allow user to remove all normal columns (the fixed column can never be removed) + else + minimumNumberOfColumns = 1; + + if (!Data->MouseEvent || !Data->MouseEvent->Column || + Data->MouseEvent->Column->Fixed || // don't allow the fixed column to be hidden + TreeNew_GetVisibleColumnCount(Data->TreeNewHandle) < minimumNumberOfColumns + 1 + ) + { + hideColumnMenuItem->Flags |= PH_EMENU_DISABLED; + } + } + else + { + if (resetSortMenuItem) + PhInsertEMenuItem(Data->Menu, resetSortMenuItem, ULONG_MAX); + } + + if (!Data->MouseEvent || !Data->MouseEvent->Column) + { + sizeColumnToFitMenuItem->Flags |= PH_EMENU_DISABLED; + } +} + +VOID PhpEnsureValidSortColumnTreeNew( + _Inout_ HWND TreeNewHandle, + _In_ ULONG DefaultSortColumn, + _In_ PH_SORT_ORDER DefaultSortOrder + ) +{ + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + // Make sure the column we're sorting by is actually visible, and if not, don't sort anymore. + + TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder); + + if (sortOrder != NoSortOrder) + { + PH_TREENEW_COLUMN column; + + TreeNew_GetColumn(TreeNewHandle, sortColumn, &column); + + if (!column.Visible) + { + if (DefaultSortOrder != NoSortOrder) + { + // Make sure the default sort column is visible. + TreeNew_GetColumn(TreeNewHandle, DefaultSortColumn, &column); + + if (!column.Visible) + { + ULONG maxId; + ULONG id; + BOOLEAN found; + + // Use the first visible column. + maxId = TreeNew_GetMaxId(TreeNewHandle); + id = 0; + found = FALSE; + + while (id <= maxId) + { + if (TreeNew_GetColumn(TreeNewHandle, id, &column)) + { + if (column.Visible) + { + DefaultSortColumn = id; + found = TRUE; + break; + } + } + + id++; + } + + if (!found) + { + DefaultSortColumn = 0; + DefaultSortOrder = NoSortOrder; + } + } + } + + TreeNew_SetSort(TreeNewHandle, DefaultSortColumn, DefaultSortOrder); + } + } +} + +BOOLEAN PhHandleTreeNewColumnMenu( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data + ) +{ + if (!Data->Selection) + return FALSE; + + switch (Data->Selection->Id) + { + case PH_TN_COLUMN_MENU_RESET_SORT_ID: + { + TreeNew_SetSort(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); + } + break; + case PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID: + { + if (Data->MouseEvent && Data->MouseEvent->Column) + { + TreeNew_AutoSizeColumn(Data->TreeNewHandle, Data->MouseEvent->Column->Id, 0); + } + } + break; + case PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID: + { + ULONG maxId; + ULONG id; + + maxId = TreeNew_GetMaxId(Data->TreeNewHandle); + id = 0; + + while (id <= maxId) + { + TreeNew_AutoSizeColumn(Data->TreeNewHandle, id, 0); + id++; + } + } + break; + case PH_TN_COLUMN_MENU_HIDE_COLUMN_ID: + { + PH_TREENEW_COLUMN column; + + if (Data->MouseEvent && Data->MouseEvent->Column && !Data->MouseEvent->Column->Fixed) + { + column.Id = Data->MouseEvent->Column->Id; + column.Visible = FALSE; + TreeNew_SetColumn(Data->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column); + PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); + InvalidateRect(Data->TreeNewHandle, NULL, FALSE); + } + } + break; + case PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID: + { + PhShowChooseColumnsDialog(Data->TreeNewHandle, Data->TreeNewHandle, PH_CONTROL_TYPE_TREE_NEW); + PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); + } + break; + default: + return FALSE; + } + + Data->ProcessedId = Data->Selection->Id; + + return TRUE; +} + +VOID PhDeleteTreeNewColumnMenu( + _In_ PPH_TN_COLUMN_MENU_DATA Data + ) +{ + if (Data->Menu) + { + PhDestroyEMenu(Data->Menu); + Data->Menu = NULL; + } +} + +VOID PhInitializeTreeNewFilterSupport( + _Out_ PPH_TN_FILTER_SUPPORT Support, + _In_ HWND TreeNewHandle, + _In_ PPH_LIST NodeList + ) +{ + Support->FilterList = NULL; + Support->TreeNewHandle = TreeNewHandle; + Support->NodeList = NodeList; +} + +VOID PhDeleteTreeNewFilterSupport( + _In_ PPH_TN_FILTER_SUPPORT Support + ) +{ + PhDereferenceObject(Support->FilterList); +} + +PPH_TN_FILTER_ENTRY PhAddTreeNewFilter( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TN_FILTER_FUNCTION Filter, + _In_opt_ PVOID Context + ) +{ + PPH_TN_FILTER_ENTRY entry; + + entry = PhAllocate(sizeof(PH_TN_FILTER_ENTRY)); + entry->Filter = Filter; + entry->Context = Context; + + if (!Support->FilterList) + Support->FilterList = PhCreateList(2); + + PhAddItemList(Support->FilterList, entry); + + return entry; +} + +VOID PhRemoveTreeNewFilter( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TN_FILTER_ENTRY Entry + ) +{ + ULONG index; + + if (!Support->FilterList) + return; + + index = PhFindItemList(Support->FilterList, Entry); + + if (index != ULONG_MAX) + { + PhRemoveItemList(Support->FilterList, index); + PhFree(Entry); + } +} + +BOOLEAN PhApplyTreeNewFiltersToNode( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TREENEW_NODE Node + ) +{ + BOOLEAN show; + ULONG i; + + show = TRUE; + + if (Support->FilterList) + { + for (i = 0; i < Support->FilterList->Count; i++) + { + PPH_TN_FILTER_ENTRY entry; + + entry = Support->FilterList->Items[i]; + + if (!entry->Filter(Node, entry->Context)) + { + show = FALSE; + break; + } + } + } + + return show; +} + +VOID PhApplyTreeNewFilters( + _In_ PPH_TN_FILTER_SUPPORT Support + ) +{ + ULONG i; + + for (i = 0; i < Support->NodeList->Count; i++) + { + PPH_TREENEW_NODE node; + + node = Support->NodeList->Items[i]; + node->Visible = PhApplyTreeNewFiltersToNode(Support, node); + + if (!node->Visible && node->Selected) + { + node->Selected = FALSE; + } + } + + TreeNew_NodesStructured(Support->TreeNewHandle); +} + +VOID NTAPI PhpCopyCellEMenuItemDeleteFunction( + _In_ struct _PH_EMENU_ITEM *Item + ) +{ + PPH_COPY_CELL_CONTEXT context; + + context = Item->Context; + PhDereferenceObject(context->MenuItemText); + PhFree(context); +} + +BOOLEAN PhInsertCopyCellEMenuItem( + _In_ struct _PH_EMENU_ITEM *Menu, + _In_ ULONG InsertAfterId, + _In_ HWND TreeNewHandle, + _In_ PPH_TREENEW_COLUMN Column + ) +{ + PPH_EMENU_ITEM parentItem; + ULONG indexInParent; + PPH_COPY_CELL_CONTEXT context; + PH_STRINGREF columnText; + PPH_STRING escapedText; + PPH_STRING menuItemText; + PPH_EMENU_ITEM copyCellItem; + + if (!Column) + return FALSE; + + if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent)) + return FALSE; + + indexInParent++; + + context = PhAllocate(sizeof(PH_COPY_CELL_CONTEXT)); + context->TreeNewHandle = TreeNewHandle; + context->Id = Column->Id; + + PhInitializeStringRef(&columnText, Column->Text); + escapedText = PhEscapeStringForMenuPrefix(&columnText); + menuItemText = PhFormatString(L"Copy \"%s\"", escapedText->Buffer); + PhDereferenceObject(escapedText); + copyCellItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context); + copyCellItem->DeleteFunction = PhpCopyCellEMenuItemDeleteFunction; + context->MenuItemText = menuItemText; + + if (Column->CustomDraw) + copyCellItem->Flags |= PH_EMENU_DISABLED; + + PhInsertEMenuItem(parentItem, copyCellItem, indexInParent); + + return TRUE; +} + +BOOLEAN PhHandleCopyCellEMenuItem( + _In_ struct _PH_EMENU_ITEM *SelectedItem + ) +{ + PPH_COPY_CELL_CONTEXT context; + PH_STRING_BUILDER stringBuilder; + ULONG count; + ULONG selectedCount; + ULONG i; + PPH_TREENEW_NODE node; + PH_TREENEW_GET_CELL_TEXT getCellText; + + if (!SelectedItem) + return FALSE; + if (SelectedItem->Id != ID_COPY_CELL) + return FALSE; + + context = SelectedItem->Context; + + PhInitializeStringBuilder(&stringBuilder, 0x100); + count = TreeNew_GetFlatNodeCount(context->TreeNewHandle); + selectedCount = 0; + + for (i = 0; i < count; i++) + { + node = TreeNew_GetFlatNode(context->TreeNewHandle, i); + + if (node && node->Selected) + { + selectedCount++; + + getCellText.Flags = 0; + getCellText.Node = node; + getCellText.Id = context->Id; + PhInitializeEmptyStringRef(&getCellText.Text); + TreeNew_GetCellText(context->TreeNewHandle, &getCellText); + + PhAppendStringBuilder(&stringBuilder, &getCellText.Text); + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + } + } + + if (stringBuilder.String->Length != 0 && selectedCount == 1) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhSetClipboardString(context->TreeNewHandle, &stringBuilder.String->sr); + PhDeleteStringBuilder(&stringBuilder); + + return TRUE; +} + +VOID NTAPI PhpCopyListViewEMenuItemDeleteFunction( + _In_ struct _PH_EMENU_ITEM *Item + ) +{ + PPH_COPY_ITEM_CONTEXT context; + + context = Item->Context; + PhDereferenceObject(context->MenuItemText); + PhFree(context); +} + +BOOLEAN PhInsertCopyListViewEMenuItem( + _In_ struct _PH_EMENU_ITEM *Menu, + _In_ ULONG InsertAfterId, + _In_ HWND ListViewHandle + ) +{ + PPH_EMENU_ITEM parentItem; + ULONG indexInParent; + PPH_COPY_ITEM_CONTEXT context; + PPH_STRING columnText = NULL; + PPH_STRING escapedText; + PPH_STRING menuItemText; + PPH_EMENU_ITEM copyMenuItem; + POINT location; + LVHITTESTINFO lvHitInfo; + HDITEM headerItem; + WCHAR headerText[MAX_PATH]; + + if (!GetCursorPos(&location)) + return FALSE; + if (!ScreenToClient(ListViewHandle, &location)) + return FALSE; + + memset(&lvHitInfo, 0, sizeof(LVHITTESTINFO)); + lvHitInfo.pt = location; + + if (ListView_SubItemHitTest(ListViewHandle, &lvHitInfo) == -1) + return FALSE; + + memset(headerText, 0, sizeof(headerText)); + memset(&headerItem, 0, sizeof(HDITEM)); + headerItem.mask = HDI_TEXT; + headerItem.cchTextMax = RTL_NUMBER_OF(headerText); + headerItem.pszText = headerText; + + if (!Header_GetItem(ListView_GetHeader(ListViewHandle), lvHitInfo.iSubItem, &headerItem)) + return FALSE; + + columnText = PhaCreateString(headerText); + + if (PhIsNullOrEmptyString(columnText)) + return FALSE; + + if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent)) + return FALSE; + + indexInParent++; + + context = PhAllocate(sizeof(PH_COPY_ITEM_CONTEXT)); + context->ListViewHandle = ListViewHandle; + context->Id = lvHitInfo.iItem; + context->SubId = lvHitInfo.iSubItem; + + escapedText = PhEscapeStringForMenuPrefix(&columnText->sr); + menuItemText = PhFormatString(L"Copy \"%s\"", escapedText->Buffer); + PhDereferenceObject(escapedText); + + copyMenuItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context); + copyMenuItem->DeleteFunction = PhpCopyListViewEMenuItemDeleteFunction; + context->MenuItemText = menuItemText; + + PhInsertEMenuItem(parentItem, copyMenuItem, indexInParent); + + return TRUE; +} + +BOOLEAN PhHandleCopyListViewEMenuItem( + _In_ struct _PH_EMENU_ITEM *SelectedItem + ) +{ + PPH_COPY_ITEM_CONTEXT context; + PH_STRING_BUILDER stringBuilder; + ULONG count; + ULONG selectedCount; + ULONG i; + PPH_STRING getItemText; + + if (!SelectedItem) + return FALSE; + if (SelectedItem->Id != ID_COPY_CELL) + return FALSE; + + context = SelectedItem->Context; + + PhInitializeStringBuilder(&stringBuilder, 0x100); + count = ListView_GetItemCount(context->ListViewHandle); + selectedCount = 0; + + for (i = 0; i < count; i++) + { + if (!(ListView_GetItemState(context->ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED)) + continue; + + getItemText = PhaGetListViewItemText(context->ListViewHandle, i, context->SubId); + + PhAppendStringBuilder(&stringBuilder, &getItemText->sr); + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + + selectedCount++; + } + + if (stringBuilder.String->Length != 0 && selectedCount == 1) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhSetClipboardString(context->ListViewHandle, &stringBuilder.String->sr); + PhDeleteStringBuilder(&stringBuilder); + + return TRUE; +} + +BOOLEAN PhpSelectFavoriteInRegedit( + _In_ HWND RegeditWindow, + _In_ PPH_STRINGREF FavoriteName, + _In_ BOOLEAN UsePhSvc + ) +{ + HMENU menu; + HMENU favoritesMenu; + ULONG count; + ULONG i; + ULONG id = ULONG_MAX; + + if (!(menu = GetMenu(RegeditWindow))) + return FALSE; + + // Cause the Registry Editor to refresh the Favorites menu. + if (UsePhSvc) + PhSvcCallSendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu); + else + SendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu); + + if (!(favoritesMenu = GetSubMenu(menu, 3))) + return FALSE; + + // Find our entry. + + count = GetMenuItemCount(favoritesMenu); + + if (count == -1) + return FALSE; + if (count > 1000) + count = 1000; + + for (i = 3; i < count; i++) + { + MENUITEMINFO info = { sizeof(MENUITEMINFO) }; + WCHAR buffer[MAX_PATH]; + + info.fMask = MIIM_ID | MIIM_STRING; + info.dwTypeData = buffer; + info.cch = RTL_NUMBER_OF(buffer); + GetMenuItemInfo(favoritesMenu, i, TRUE, &info); + + if (info.cch == FavoriteName->Length / sizeof(WCHAR)) + { + PH_STRINGREF text; + + text.Buffer = buffer; + text.Length = info.cch * sizeof(WCHAR); + + if (PhEqualStringRef(&text, FavoriteName, TRUE)) + { + id = info.wID; + break; + } + } + } + + if (id == ULONG_MAX) + return FALSE; + + // Activate our entry. + if (UsePhSvc) + PhSvcCallSendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0); + else + SendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0); + + // "Close" the Favorites menu and restore normal status bar text. + if (UsePhSvc) + PhSvcCallPostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0); + else + PostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0); + + // Bring regedit to the top. + if (IsMinimized(RegeditWindow)) + { + ShowWindow(RegeditWindow, SW_RESTORE); + SetForegroundWindow(RegeditWindow); + } + else + { + SetForegroundWindow(RegeditWindow); + } + + return TRUE; +} + +/** + * Opens a key in the Registry Editor. If the Registry Editor is already open, + * the specified key is selected in the Registry Editor. + * + * \param hWnd A handle to the parent window. + * \param KeyName The key name to open. + */ +BOOLEAN PhShellOpenKey2( + _In_ HWND hWnd, + _In_ PPH_STRING KeyName + ) +{ + static PH_STRINGREF favoritesKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit\\Favorites"); + + BOOLEAN result = FALSE; + HWND regeditWindow; + HANDLE favoritesKeyHandle; + WCHAR favoriteName[32]; + UNICODE_STRING valueName; + PH_STRINGREF valueNameSr; + PPH_STRING expandedKeyName; + + regeditWindow = FindWindow(L"RegEdit_RegEdit", NULL); + + if (!regeditWindow) + { + PhShellOpenKey(hWnd, KeyName); + return TRUE; + } + + if (!PhGetOwnTokenAttributes().Elevated) + { + if (!PhUiConnectToPhSvc(hWnd, FALSE)) + return FALSE; + } + + // Create our entry in Favorites. + + if (!NT_SUCCESS(PhCreateKey( + &favoritesKeyHandle, + KEY_WRITE, + PH_KEY_CURRENT_USER, + &favoritesKeyName, + 0, + 0, + NULL + ))) + goto CleanupExit; + + memcpy(favoriteName, L"A_ProcessHacker", 15 * sizeof(WCHAR)); + PhGenerateRandomAlphaString(&favoriteName[15], 16); + RtlInitUnicodeString(&valueName, favoriteName); + PhUnicodeStringToStringRef(&valueName, &valueNameSr); + + expandedKeyName = PhExpandKeyName(KeyName, FALSE); + NtSetValueKey(favoritesKeyHandle, &valueName, 0, REG_SZ, expandedKeyName->Buffer, (ULONG)expandedKeyName->Length + sizeof(UNICODE_NULL)); + PhDereferenceObject(expandedKeyName); + + // Select our entry in regedit. + result = PhpSelectFavoriteInRegedit(regeditWindow, &valueNameSr, !PhGetOwnTokenAttributes().Elevated); + + NtDeleteValueKey(favoritesKeyHandle, &valueName); + NtClose(favoritesKeyHandle); + +CleanupExit: + if (!PhGetOwnTokenAttributes().Elevated) + PhUiDisconnectFromPhSvc(); + + return result; +} + +PPH_STRING PhPcre2GetErrorMessage( + _In_ INT ErrorCode + ) +{ + PPH_STRING buffer; + SIZE_T bufferLength; + INT_PTR returnLength; + + bufferLength = 128 * sizeof(WCHAR); + buffer = PhCreateStringEx(NULL, bufferLength); + + while (TRUE) + { + if ((returnLength = pcre2_get_error_message(ErrorCode, buffer->Buffer, bufferLength / sizeof(WCHAR) + 1)) >= 0) + break; + + PhDereferenceObject(buffer); + bufferLength *= 2; + + if (bufferLength > 0x1000 * sizeof(WCHAR)) + break; + + buffer = PhCreateStringEx(NULL, bufferLength); + } + + if (returnLength < 0) + return NULL; + + buffer->Length = returnLength * sizeof(WCHAR); + return buffer; +} + +HBITMAP PhGetShieldBitmap( + VOID + ) +{ + static HBITMAP shieldBitmap = NULL; + + if (!shieldBitmap) + { + HICON shieldIcon; + + if (shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0)) + { + shieldBitmap = PhIconToBitmap(shieldIcon, PhSmallIconSize.X, PhSmallIconSize.Y); + DestroyIcon(shieldIcon); + } + } + + return shieldBitmap; +} + diff --git a/ProcessHacker/chcol.c b/ProcessHacker/chcol.c index 58f06e824743..515b8a66f7b0 100644 --- a/ProcessHacker/chcol.c +++ b/ProcessHacker/chcol.c @@ -1,425 +1,427 @@ -/* - * Process Hacker - - * column chooser - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -typedef struct _COLUMNS_DIALOG_CONTEXT -{ - HWND ControlHandle; - ULONG Type; - PPH_LIST Columns; - - HWND InactiveList; - HWND ActiveList; -} COLUMNS_DIALOG_CONTEXT, *PCOLUMNS_DIALOG_CONTEXT; - -INT_PTR CALLBACK PhpColumnsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowChooseColumnsDialog( - _In_ HWND ParentWindowHandle, - _In_ HWND ControlHandle, - _In_ ULONG Type - ) -{ - COLUMNS_DIALOG_CONTEXT context; - - context.ControlHandle = ControlHandle; - context.Type = Type; - - if (Type == PH_CONTROL_TYPE_TREE_NEW) - context.Columns = PhCreateList(TreeNew_GetColumnCount(ControlHandle)); - else - return; - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_CHOOSECOLUMNS), - ParentWindowHandle, - PhpColumnsDlgProc, - (LPARAM)&context - ); - - PhDereferenceObject(context.Columns); -} - -static int __cdecl PhpColumnsCompareDisplayIndexTn( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_TREENEW_COLUMN column1 = *(PPH_TREENEW_COLUMN *)elem1; - PPH_TREENEW_COLUMN column2 = *(PPH_TREENEW_COLUMN *)elem2; - - return uintcmp(column1->DisplayIndex, column2->DisplayIndex); -} - -_Success_(return != -1) -static ULONG IndexOfStringInList( - _In_ PPH_LIST List, - _In_ PWSTR String - ) -{ - ULONG i; - - for (i = 0; i < List->Count; i++) - { - if (PhEqualString2(List->Items[i], String, FALSE)) - return i; - } - - return -1; -} - -INT_PTR CALLBACK PhpColumnsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOLUMNS_DIALOG_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PCOLUMNS_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - } - else - { - context = (PCOLUMNS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG count; - ULONG total; - ULONG i; - PPH_LIST displayOrderList = NULL; - - context->InactiveList = GetDlgItem(hwndDlg, IDC_INACTIVE); - context->ActiveList = GetDlgItem(hwndDlg, IDC_ACTIVE); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - if (context->Type == PH_CONTROL_TYPE_TREE_NEW) - { - PH_TREENEW_COLUMN column; - - count = 0; - total = TreeNew_GetColumnCount(context->ControlHandle); - i = 0; - - displayOrderList = PhCreateList(total); - - while (count < total) - { - if (TreeNew_GetColumn(context->ControlHandle, i, &column)) - { - PPH_TREENEW_COLUMN copy; - - if (column.Fixed) - { - i++; - total--; - continue; - } - - copy = PhAllocateCopy(&column, sizeof(PH_TREENEW_COLUMN)); - PhAddItemList(context->Columns, copy); - count++; - - if (column.Visible) - { - PhAddItemList(displayOrderList, copy); - } - else - { - ListBox_AddString(context->InactiveList, column.Text); - } - } - - i++; - } - - qsort(displayOrderList->Items, displayOrderList->Count, sizeof(PVOID), PhpColumnsCompareDisplayIndexTn); - } - - if (displayOrderList) - { - for (i = 0; i < displayOrderList->Count; i++) - { - if (context->Type == PH_CONTROL_TYPE_TREE_NEW) - { - PPH_TREENEW_COLUMN copy = displayOrderList->Items[i]; - - ListBox_AddString(context->ActiveList, copy->Text); - } - } - - PhDereferenceObject(displayOrderList); - } - - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); - } - break; - case WM_DESTROY: - { - ULONG i; - - for (i = 0; i < context->Columns->Count; i++) - PhFree(context->Columns->Items[i]); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { -#define ORDER_LIMIT 100 - PPH_LIST activeList; - ULONG activeCount; - ULONG i; - INT orderArray[ORDER_LIMIT]; - INT maxOrder; - - memset(orderArray, 0, sizeof(orderArray)); - maxOrder = 0; - - activeCount = ListBox_GetCount(context->ActiveList); - activeList = PhCreateList(activeCount); - - for (i = 0; i < activeCount; i++) - PhAddItemList(activeList, PhGetListBoxString(context->ActiveList, i)); - - if (context->Type == PH_CONTROL_TYPE_TREE_NEW) - { - // Apply visiblity settings and build the order array. - - TreeNew_SetRedraw(context->ControlHandle, FALSE); - - for (i = 0; i < context->Columns->Count; i++) - { - PPH_TREENEW_COLUMN column = context->Columns->Items[i]; - ULONG index; - - index = IndexOfStringInList(activeList, column->Text); - column->Visible = index != -1; - - TreeNew_SetColumn(context->ControlHandle, TN_COLUMN_FLAG_VISIBLE, column); - - if (column->Visible && index < ORDER_LIMIT) - { - orderArray[index] = column->Id; - - if ((ULONG)maxOrder < index + 1) - maxOrder = index + 1; - } - } - - // Apply display order. - TreeNew_SetColumnOrderArray(context->ControlHandle, maxOrder, orderArray); - - TreeNew_SetRedraw(context->ControlHandle, TRUE); - - PhDereferenceObject(activeList); - - InvalidateRect(context->ControlHandle, NULL, FALSE); - } - - EndDialog(hwndDlg, IDOK); - } - break; - case IDC_INACTIVE: - { - switch (HIWORD(wParam)) - { - case LBN_DBLCLK: - { - SendMessage(hwndDlg, WM_COMMAND, IDC_SHOW, 0); - } - break; - case LBN_SELCHANGE: - { - INT sel = ListBox_GetCurSel(context->InactiveList); - - EnableWindow(GetDlgItem(hwndDlg, IDC_SHOW), sel != -1); - } - break; - } - } - break; - case IDC_ACTIVE: - { - switch (HIWORD(wParam)) - { - case LBN_DBLCLK: - { - SendMessage(hwndDlg, WM_COMMAND, IDC_HIDE, 0); - } - break; - case LBN_SELCHANGE: - { - INT sel = ListBox_GetCurSel(context->ActiveList); - INT count = ListBox_GetCount(context->ActiveList); - - EnableWindow(GetDlgItem(hwndDlg, IDC_HIDE), sel != -1 && count != 1); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0 && sel != -1); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1 && sel != -1); - } - break; - } - } - break; - case IDC_SHOW: - { - INT sel; - INT count; - PPH_STRING string; - - sel = ListBox_GetCurSel(context->InactiveList); - count = ListBox_GetCount(context->InactiveList); - - if (string = PhGetListBoxString(context->InactiveList, sel)) - { - ListBox_DeleteString(context->InactiveList, sel); - ListBox_AddString(context->ActiveList, string->Buffer); - PhDereferenceObject(string); - - count--; - - if (sel >= count - 1) - sel = count - 1; - - ListBox_SetCurSel(context->InactiveList, sel); - - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); - } - } - break; - case IDC_HIDE: - { - INT sel; - INT count; - PPH_STRING string; - - sel = ListBox_GetCurSel(context->ActiveList); - count = ListBox_GetCount(context->ActiveList); - - if (count != 1) - { - if (string = PhGetListBoxString(context->ActiveList, sel)) - { - ListBox_DeleteString(context->ActiveList, sel); - ListBox_AddString(context->InactiveList, string->Buffer); - PhDereferenceObject(string); - - count--; - - if (sel >= count - 1) - sel = count - 1; - - ListBox_SetCurSel(context->ActiveList, sel); - - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); - } - } - } - break; - case IDC_MOVEUP: - { - INT sel; - INT count; - PPH_STRING string; - - sel = ListBox_GetCurSel(context->ActiveList); - count = ListBox_GetCount(context->ActiveList); - - if (sel != 0) - { - if (string = PhGetListBoxString(context->ActiveList, sel)) - { - ListBox_DeleteString(context->ActiveList, sel); - ListBox_InsertString(context->ActiveList, sel - 1, string->Buffer); - PhDereferenceObject(string); - - sel -= 1; - ListBox_SetCurSel(context->ActiveList, sel); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1); - } - } - } - break; - case IDC_MOVEDOWN: - { - INT sel; - INT count; - PPH_STRING string; - - sel = ListBox_GetCurSel(context->ActiveList); - count = ListBox_GetCount(context->ActiveList); - - if (sel != count - 1) - { - if (string = PhGetListBoxString(context->ActiveList, sel)) - { - ListBox_DeleteString(context->ActiveList, sel); - ListBox_InsertString(context->ActiveList, sel + 1, string->Buffer); - PhDereferenceObject(string); - - sel += 1; - ListBox_SetCurSel(context->ActiveList, sel); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1); - } - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * column chooser + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +typedef struct _COLUMNS_DIALOG_CONTEXT +{ + HWND ControlHandle; + ULONG Type; + PPH_LIST Columns; + + HWND InactiveList; + HWND ActiveList; +} COLUMNS_DIALOG_CONTEXT, *PCOLUMNS_DIALOG_CONTEXT; + +INT_PTR CALLBACK PhpColumnsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowChooseColumnsDialog( + _In_ HWND ParentWindowHandle, + _In_ HWND ControlHandle, + _In_ ULONG Type + ) +{ + COLUMNS_DIALOG_CONTEXT context; + + context.ControlHandle = ControlHandle; + context.Type = Type; + + if (Type == PH_CONTROL_TYPE_TREE_NEW) + context.Columns = PhCreateList(TreeNew_GetColumnCount(ControlHandle)); + else + return; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_CHOOSECOLUMNS), + ParentWindowHandle, + PhpColumnsDlgProc, + (LPARAM)&context + ); + + PhDereferenceObject(context.Columns); +} + +static int __cdecl PhpColumnsCompareDisplayIndexTn( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_TREENEW_COLUMN column1 = *(PPH_TREENEW_COLUMN *)elem1; + PPH_TREENEW_COLUMN column2 = *(PPH_TREENEW_COLUMN *)elem2; + + return uintcmp(column1->DisplayIndex, column2->DisplayIndex); +} + +_Success_(return != -1) +static ULONG IndexOfStringInList( + _In_ PPH_LIST List, + _In_ PWSTR String + ) +{ + ULONG i; + + for (i = 0; i < List->Count; i++) + { + if (PhEqualString2(List->Items[i], String, FALSE)) + return i; + } + + return -1; +} + +INT_PTR CALLBACK PhpColumnsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCOLUMNS_DIALOG_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PCOLUMNS_DIALOG_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG count; + ULONG total; + ULONG i; + PPH_LIST displayOrderList = NULL; + + context->InactiveList = GetDlgItem(hwndDlg, IDC_INACTIVE); + context->ActiveList = GetDlgItem(hwndDlg, IDC_ACTIVE); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + if (context->Type == PH_CONTROL_TYPE_TREE_NEW) + { + PH_TREENEW_COLUMN column; + + count = 0; + total = TreeNew_GetColumnCount(context->ControlHandle); + i = 0; + + displayOrderList = PhCreateList(total); + + while (count < total) + { + if (TreeNew_GetColumn(context->ControlHandle, i, &column)) + { + PPH_TREENEW_COLUMN copy; + + if (column.Fixed) + { + i++; + total--; + continue; + } + + copy = PhAllocateCopy(&column, sizeof(PH_TREENEW_COLUMN)); + PhAddItemList(context->Columns, copy); + count++; + + if (column.Visible) + { + PhAddItemList(displayOrderList, copy); + } + else + { + ListBox_AddString(context->InactiveList, column.Text); + } + } + + i++; + } + + qsort(displayOrderList->Items, displayOrderList->Count, sizeof(PVOID), PhpColumnsCompareDisplayIndexTn); + } + + if (displayOrderList) + { + for (i = 0; i < displayOrderList->Count; i++) + { + if (context->Type == PH_CONTROL_TYPE_TREE_NEW) + { + PPH_TREENEW_COLUMN copy = displayOrderList->Items[i]; + + ListBox_AddString(context->ActiveList, copy->Text); + } + } + + PhDereferenceObject(displayOrderList); + } + + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + ULONG i; + + for (i = 0; i < context->Columns->Count; i++) + PhFree(context->Columns->Items[i]); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { +#define ORDER_LIMIT 200 + PPH_LIST activeList; + ULONG activeCount; + ULONG i; + INT orderArray[ORDER_LIMIT]; + INT maxOrder; + + memset(orderArray, 0, sizeof(orderArray)); + maxOrder = 0; + + activeCount = ListBox_GetCount(context->ActiveList); + activeList = PhCreateList(activeCount); + + for (i = 0; i < activeCount; i++) + PhAddItemList(activeList, PhGetListBoxString(context->ActiveList, i)); + + if (context->Type == PH_CONTROL_TYPE_TREE_NEW) + { + // Apply visiblity settings and build the order array. + + TreeNew_SetRedraw(context->ControlHandle, FALSE); + + for (i = 0; i < context->Columns->Count; i++) + { + PPH_TREENEW_COLUMN column = context->Columns->Items[i]; + ULONG index; + + index = IndexOfStringInList(activeList, column->Text); + column->Visible = index != -1; + + TreeNew_SetColumn(context->ControlHandle, TN_COLUMN_FLAG_VISIBLE, column); + + if (column->Visible && index < ORDER_LIMIT) + { + orderArray[index] = column->Id; + + if ((ULONG)maxOrder < index + 1) + maxOrder = index + 1; + } + } + + // Apply display order. + TreeNew_SetColumnOrderArray(context->ControlHandle, maxOrder, orderArray); + + TreeNew_SetRedraw(context->ControlHandle, TRUE); + + PhDereferenceObject(activeList); + + InvalidateRect(context->ControlHandle, NULL, FALSE); + } + + EndDialog(hwndDlg, IDOK); + } + break; + case IDC_INACTIVE: + { + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + { + SendMessage(hwndDlg, WM_COMMAND, IDC_SHOW, 0); + } + break; + case LBN_SELCHANGE: + { + INT sel = ListBox_GetCurSel(context->InactiveList); + + EnableWindow(GetDlgItem(hwndDlg, IDC_SHOW), sel != -1); + } + break; + } + } + break; + case IDC_ACTIVE: + { + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + { + SendMessage(hwndDlg, WM_COMMAND, IDC_HIDE, 0); + } + break; + case LBN_SELCHANGE: + { + INT sel = ListBox_GetCurSel(context->ActiveList); + INT count = ListBox_GetCount(context->ActiveList); + + EnableWindow(GetDlgItem(hwndDlg, IDC_HIDE), sel != -1 && count != 1); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0 && sel != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1 && sel != -1); + } + break; + } + } + break; + case IDC_SHOW: + { + INT sel; + INT count; + PPH_STRING string; + + sel = ListBox_GetCurSel(context->InactiveList); + count = ListBox_GetCount(context->InactiveList); + + if (string = PhGetListBoxString(context->InactiveList, sel)) + { + ListBox_DeleteString(context->InactiveList, sel); + ListBox_AddString(context->ActiveList, string->Buffer); + PhDereferenceObject(string); + + count--; + + if (sel >= count - 1) + sel = count - 1; + + ListBox_SetCurSel(context->InactiveList, sel); + + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); + } + } + break; + case IDC_HIDE: + { + INT sel; + INT count; + PPH_STRING string; + + sel = ListBox_GetCurSel(context->ActiveList); + count = ListBox_GetCount(context->ActiveList); + + if (count != 1) + { + if (string = PhGetListBoxString(context->ActiveList, sel)) + { + ListBox_DeleteString(context->ActiveList, sel); + ListBox_AddString(context->InactiveList, string->Buffer); + PhDereferenceObject(string); + + count--; + + if (sel >= count - 1) + sel = count - 1; + + ListBox_SetCurSel(context->ActiveList, sel); + + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_INACTIVE, LBN_SELCHANGE), (LPARAM)context->InactiveList); + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_ACTIVE, LBN_SELCHANGE), (LPARAM)context->ActiveList); + } + } + } + break; + case IDC_MOVEUP: + { + INT sel; + INT count; + PPH_STRING string; + + sel = ListBox_GetCurSel(context->ActiveList); + count = ListBox_GetCount(context->ActiveList); + + if (sel != 0) + { + if (string = PhGetListBoxString(context->ActiveList, sel)) + { + ListBox_DeleteString(context->ActiveList, sel); + ListBox_InsertString(context->ActiveList, sel - 1, string->Buffer); + PhDereferenceObject(string); + + sel -= 1; + ListBox_SetCurSel(context->ActiveList, sel); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1); + } + } + } + break; + case IDC_MOVEDOWN: + { + INT sel; + INT count; + PPH_STRING string; + + sel = ListBox_GetCurSel(context->ActiveList); + count = ListBox_GetCount(context->ActiveList); + + if (sel != count - 1) + { + if (string = PhGetListBoxString(context->ActiveList, sel)) + { + ListBox_DeleteString(context->ActiveList, sel); + ListBox_InsertString(context->ActiveList, sel + 1, string->Buffer); + PhDereferenceObject(string); + + sel += 1; + ListBox_SetCurSel(context->ActiveList, sel); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), sel != 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), sel != count - 1); + } + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/chdlg.c b/ProcessHacker/chdlg.c index 97c4bdac6eee..995946356e62 100644 --- a/ProcessHacker/chdlg.c +++ b/ProcessHacker/chdlg.c @@ -1,360 +1,360 @@ -/* - * Process Hacker - - * choice dialog - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -typedef struct _CHOICE_DIALOG_CONTEXT -{ - PWSTR Title; - PWSTR Message; - PWSTR *Choices; - ULONG NumberOfChoices; - PWSTR Option; - ULONG Flags; - PPH_STRING *SelectedChoice; - PBOOLEAN SelectedOption; - PWSTR SavedChoicesSettingName; - - HWND ComboBoxHandle; -} CHOICE_DIALOG_CONTEXT, *PCHOICE_DIALOG_CONTEXT; - -INT_PTR CALLBACK PhpChoiceDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -/** - * Prompts the user for input. - * - * \remarks If \c PH_CHOICE_DIALOG_PASSWORD is specified, the string - * returned in \a SelectedChoice is NOT auto-dereferenced. - */ -BOOLEAN PhaChoiceDialog( - _In_ HWND ParentWindowHandle, - _In_ PWSTR Title, - _In_ PWSTR Message, - _In_opt_ PWSTR *Choices, - _In_opt_ ULONG NumberOfChoices, - _In_opt_ PWSTR Option, - _In_ ULONG Flags, - _Inout_ PPH_STRING *SelectedChoice, - _Inout_opt_ PBOOLEAN SelectedOption, - _In_opt_ PWSTR SavedChoicesSettingName - ) -{ - CHOICE_DIALOG_CONTEXT context; - - context.Title = Title; - context.Message = Message; - context.Choices = Choices; - context.NumberOfChoices = NumberOfChoices; - context.Option = Option; - context.Flags = Flags; - context.SelectedChoice = SelectedChoice; - context.SelectedOption = SelectedOption; - context.SavedChoicesSettingName = SavedChoicesSettingName; - - return DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_CHOOSE), - ParentWindowHandle, - PhpChoiceDlgProc, - (LPARAM)&context - ) == IDOK; -} - -INT_PTR CALLBACK PhpChoiceDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PCHOICE_DIALOG_CONTEXT context = (PCHOICE_DIALOG_CONTEXT)lParam; - ULONG type; - SIZE_T i; - HWND comboBoxHandle; - HWND checkBoxHandle; - RECT checkBoxRect; - RECT rect; - ULONG diff; - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetWindowText(hwndDlg, context->Title); - SetWindowText(GetDlgItem(hwndDlg, IDC_MESSAGE), context->Message); - - type = context->Flags & PH_CHOICE_DIALOG_TYPE_MASK; - - // Select the control to show, depending on the type. This is - // because it is impossible to change the style of the combo box - // after it is created. - switch (type) - { - case PH_CHOICE_DIALOG_USER_CHOICE: - comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICEUSER); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICEUSER), SW_SHOW); - break; - case PH_CHOICE_DIALOG_PASSWORD: - comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICESIMPLE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICESIMPLE), SW_SHOW); - - // Disable combo box features since it isn't a combo box. - context->SavedChoicesSettingName = NULL; - break; - case PH_CHOICE_DIALOG_CHOICE: - default: - comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICE), SW_SHOW); - break; - } - - context->ComboBoxHandle = comboBoxHandle; - - checkBoxHandle = GetDlgItem(hwndDlg, IDC_OPTION); - - if (type == PH_CHOICE_DIALOG_PASSWORD) - { - // Nothing - } - else if (type == PH_CHOICE_DIALOG_USER_CHOICE && context->SavedChoicesSettingName) - { - PPH_STRING savedChoices = PhGetStringSetting(context->SavedChoicesSettingName); - ULONG_PTR indexOfDelim; - PPH_STRING savedChoice; - - i = 0; - - // Split the saved choices using the delimiter. - while (i < savedChoices->Length / 2) - { - // BUG BUG BUG - what if the user saves "\s"? - indexOfDelim = PhFindStringInString(savedChoices, i, L"\\s"); - - if (indexOfDelim == -1) - indexOfDelim = savedChoices->Length / 2; - - savedChoice = PhSubstring(savedChoices, i, indexOfDelim - i); - - if (savedChoice->Length != 0) - { - PPH_STRING unescaped; - - unescaped = PhUnescapeStringForDelimiter(savedChoice, '\\'); - ComboBox_InsertString(comboBoxHandle, -1, unescaped->Buffer); - PhDereferenceObject(unescaped); - } - - PhDereferenceObject(savedChoice); - - i = indexOfDelim + 2; - } - - PhDereferenceObject(savedChoices); - } - else - { - for (i = 0; i < context->NumberOfChoices; i++) - { - ComboBox_AddString(comboBoxHandle, context->Choices[i]); - } - - context->SavedChoicesSettingName = NULL; // make sure we don't try to save the choices - } - - if (type == PH_CHOICE_DIALOG_PASSWORD) - { - if (*context->SelectedChoice) - SetWindowText(comboBoxHandle, (*context->SelectedChoice)->Buffer); - - Edit_SetSel(comboBoxHandle, 0, -1); - } - else if (type == PH_CHOICE_DIALOG_USER_CHOICE || type == PH_CHOICE_DIALOG_CHOICE) - { - // If we failed to choose a default choice based on what was specified, - // select the first one if possible, or set the text directly. - if (!(*context->SelectedChoice) || PhSelectComboBoxString( - comboBoxHandle, (*context->SelectedChoice)->Buffer, FALSE) == CB_ERR) - { - if (type == PH_CHOICE_DIALOG_USER_CHOICE && *context->SelectedChoice) - { - SetWindowText(comboBoxHandle, (*context->SelectedChoice)->Buffer); - } - else if (type == PH_CHOICE_DIALOG_CHOICE && context->NumberOfChoices != 0) - { - ComboBox_SetCurSel(comboBoxHandle, 0); - } - } - - if (type == PH_CHOICE_DIALOG_USER_CHOICE) - ComboBox_SetEditSel(comboBoxHandle, 0, -1); - } - - if (context->Option) - { - SetWindowText(checkBoxHandle, context->Option); - - if (context->SelectedOption) - Button_SetCheck(checkBoxHandle, *context->SelectedOption ? BST_CHECKED : BST_UNCHECKED); - } - else - { - // Hide the check box and move the buttons up. - - ShowWindow(checkBoxHandle, SW_HIDE); - GetWindowRect(checkBoxHandle, &checkBoxRect); - MapWindowPoints(NULL, hwndDlg, (POINT *)&checkBoxRect, 2); - GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rect); - MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); - diff = rect.top - checkBoxRect.top; - - // OK - rect.top -= diff; - rect.bottom -= diff; - SetWindowPos(GetDlgItem(hwndDlg, IDOK), NULL, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER); - - // Cancel - GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rect); - MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); - rect.top -= diff; - rect.bottom -= diff; - SetWindowPos(GetDlgItem(hwndDlg, IDCANCEL), NULL, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER); - - // Window - GetWindowRect(hwndDlg, &rect); - rect.bottom -= diff; - SetWindowPos(hwndDlg, NULL, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)comboBoxHandle, TRUE); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PCHOICE_DIALOG_CONTEXT context = (PCHOICE_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - PPH_STRING selectedChoice; - - if ((context->Flags & PH_CHOICE_DIALOG_TYPE_MASK) != PH_CHOICE_DIALOG_PASSWORD) - { - selectedChoice = PH_AUTO(PhGetWindowText(context->ComboBoxHandle)); - *context->SelectedChoice = selectedChoice; - } - else - { - // Password values are never auto-dereferenced. - selectedChoice = PhGetWindowText(context->ComboBoxHandle); - *context->SelectedChoice = selectedChoice; - } - - if (context->Option && context->SelectedOption) - *context->SelectedOption = Button_GetCheck(GetDlgItem(hwndDlg, IDC_OPTION)) == BST_CHECKED; - - if (context->SavedChoicesSettingName) - { - PH_STRING_BUILDER savedChoices; - ULONG i; - ULONG choicesToSave = PH_CHOICE_DIALOG_SAVED_CHOICES; - PPH_STRING choice; - PPH_STRING escaped; - - PhInitializeStringBuilder(&savedChoices, 100); - - // Push the selected choice to the top, then save the others. - - if (selectedChoice->Length != 0) - { - escaped = PhEscapeStringForDelimiter(selectedChoice, '\\'); - PhAppendStringBuilder(&savedChoices, &escaped->sr); - PhDereferenceObject(escaped); - PhAppendStringBuilder2(&savedChoices, L"\\s"); - } - - for (i = 1; i < choicesToSave; i++) - { - choice = PhGetComboBoxString(context->ComboBoxHandle, i - 1); - - if (!choice) - break; - - // Don't save the choice if it's the same as the one - // entered by the user (since we already saved it above). - if (PhEqualString(choice, selectedChoice, FALSE)) - { - PhDereferenceObject(choice); - choicesToSave++; // useless for now, but may be needed in the future - continue; - } - - escaped = PhEscapeStringForDelimiter(choice, '\\'); - PhAppendStringBuilder(&savedChoices, &escaped->sr); - PhDereferenceObject(escaped); - PhDereferenceObject(choice); - - PhAppendStringBuilder2(&savedChoices, L"\\s"); - } - - if (PhEndsWithString2(savedChoices.String, L"\\s", FALSE)) - PhRemoveEndStringBuilder(&savedChoices, 2); - - PhSetStringSetting2(context->SavedChoicesSettingName, &savedChoices.String->sr); - PhDeleteStringBuilder(&savedChoices); - } - - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * choice dialog + * + * Copyright (C) 2010-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +typedef struct _CHOICE_DIALOG_CONTEXT +{ + PWSTR Title; + PWSTR Message; + PWSTR *Choices; + ULONG NumberOfChoices; + PWSTR Option; + ULONG Flags; + PPH_STRING *SelectedChoice; + PBOOLEAN SelectedOption; + PWSTR SavedChoicesSettingName; + + HWND ComboBoxHandle; +} CHOICE_DIALOG_CONTEXT, *PCHOICE_DIALOG_CONTEXT; + +INT_PTR CALLBACK PhpChoiceDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +/** + * Prompts the user for input. + * + * \remarks If \c PH_CHOICE_DIALOG_PASSWORD is specified, the string + * returned in \a SelectedChoice is NOT auto-dereferenced. + */ +BOOLEAN PhaChoiceDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR Title, + _In_ PWSTR Message, + _In_opt_ PWSTR *Choices, + _In_opt_ ULONG NumberOfChoices, + _In_opt_ PWSTR Option, + _In_ ULONG Flags, + _Inout_ PPH_STRING *SelectedChoice, + _Inout_opt_ PBOOLEAN SelectedOption, + _In_opt_ PWSTR SavedChoicesSettingName + ) +{ + CHOICE_DIALOG_CONTEXT context; + + context.Title = Title; + context.Message = Message; + context.Choices = Choices; + context.NumberOfChoices = NumberOfChoices; + context.Option = Option; + context.Flags = Flags; + context.SelectedChoice = SelectedChoice; + context.SelectedOption = SelectedOption; + context.SavedChoicesSettingName = SavedChoicesSettingName; + + return DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_CHOOSE), + ParentWindowHandle, + PhpChoiceDlgProc, + (LPARAM)&context + ) == IDOK; +} + +INT_PTR CALLBACK PhpChoiceDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PCHOICE_DIALOG_CONTEXT context = (PCHOICE_DIALOG_CONTEXT)lParam; + ULONG type; + SIZE_T i; + HWND comboBoxHandle; + HWND checkBoxHandle; + RECT checkBoxRect; + RECT rect; + ULONG diff; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetWindowText(hwndDlg, context->Title); + PhSetWindowText(GetDlgItem(hwndDlg, IDC_MESSAGE), context->Message); + + type = context->Flags & PH_CHOICE_DIALOG_TYPE_MASK; + + // Select the control to show, depending on the type. This is + // because it is impossible to change the style of the combo box + // after it is created. + switch (type) + { + case PH_CHOICE_DIALOG_USER_CHOICE: + comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICEUSER); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICEUSER), SW_SHOW); + break; + case PH_CHOICE_DIALOG_PASSWORD: + comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICESIMPLE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICESIMPLE), SW_SHOW); + + // Disable combo box features since it isn't a combo box. + context->SavedChoicesSettingName = NULL; + break; + case PH_CHOICE_DIALOG_CHOICE: + default: + comboBoxHandle = GetDlgItem(hwndDlg, IDC_CHOICE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHOICE), SW_SHOW); + break; + } + + context->ComboBoxHandle = comboBoxHandle; + + checkBoxHandle = GetDlgItem(hwndDlg, IDC_OPTION); + + if (type == PH_CHOICE_DIALOG_PASSWORD) + { + // Nothing + } + else if (type == PH_CHOICE_DIALOG_USER_CHOICE && context->SavedChoicesSettingName) + { + PPH_STRING savedChoices = PhGetStringSetting(context->SavedChoicesSettingName); + ULONG_PTR indexOfDelim; + PPH_STRING savedChoice; + + i = 0; + + // Split the saved choices using the delimiter. + while (i < savedChoices->Length / sizeof(WCHAR)) + { + // BUG BUG BUG - what if the user saves "\s"? + indexOfDelim = PhFindStringInString(savedChoices, i, L"\\s"); + + if (indexOfDelim == -1) + indexOfDelim = savedChoices->Length / sizeof(WCHAR); + + savedChoice = PhSubstring(savedChoices, i, indexOfDelim - i); + + if (savedChoice->Length != 0) + { + PPH_STRING unescaped; + + unescaped = PhUnescapeStringForDelimiter(savedChoice, '\\'); + ComboBox_InsertString(comboBoxHandle, -1, unescaped->Buffer); + PhDereferenceObject(unescaped); + } + + PhDereferenceObject(savedChoice); + + i = indexOfDelim + 2; + } + + PhDereferenceObject(savedChoices); + } + else + { + for (i = 0; i < context->NumberOfChoices; i++) + { + ComboBox_AddString(comboBoxHandle, context->Choices[i]); + } + + context->SavedChoicesSettingName = NULL; // make sure we don't try to save the choices + } + + if (type == PH_CHOICE_DIALOG_PASSWORD) + { + if (*context->SelectedChoice) + PhSetWindowText(comboBoxHandle, (*context->SelectedChoice)->Buffer); + + Edit_SetSel(comboBoxHandle, 0, -1); + } + else if (type == PH_CHOICE_DIALOG_USER_CHOICE || type == PH_CHOICE_DIALOG_CHOICE) + { + // If we failed to choose a default choice based on what was specified, + // select the first one if possible, or set the text directly. + if (!(*context->SelectedChoice) || PhSelectComboBoxString( + comboBoxHandle, (*context->SelectedChoice)->Buffer, FALSE) == CB_ERR) + { + if (type == PH_CHOICE_DIALOG_USER_CHOICE && *context->SelectedChoice) + { + PhSetWindowText(comboBoxHandle, (*context->SelectedChoice)->Buffer); + } + else if (type == PH_CHOICE_DIALOG_CHOICE && context->NumberOfChoices != 0) + { + ComboBox_SetCurSel(comboBoxHandle, 0); + } + } + + if (type == PH_CHOICE_DIALOG_USER_CHOICE) + ComboBox_SetEditSel(comboBoxHandle, 0, -1); + } + + if (context->Option) + { + PhSetWindowText(checkBoxHandle, context->Option); + + if (context->SelectedOption) + Button_SetCheck(checkBoxHandle, *context->SelectedOption ? BST_CHECKED : BST_UNCHECKED); + } + else + { + // Hide the check box and move the buttons up. + + ShowWindow(checkBoxHandle, SW_HIDE); + GetWindowRect(checkBoxHandle, &checkBoxRect); + MapWindowPoints(NULL, hwndDlg, (POINT *)&checkBoxRect, 2); + GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rect); + MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); + diff = rect.top - checkBoxRect.top; + + // OK + rect.top -= diff; + rect.bottom -= diff; + SetWindowPos(GetDlgItem(hwndDlg, IDOK), NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + + // Cancel + GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rect); + MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); + rect.top -= diff; + rect.bottom -= diff; + SetWindowPos(GetDlgItem(hwndDlg, IDCANCEL), NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + + // Window + GetWindowRect(hwndDlg, &rect); + rect.bottom -= diff; + SetWindowPos(hwndDlg, NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + } + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + + PhSetDialogFocus(hwndDlg, comboBoxHandle); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + PCHOICE_DIALOG_CONTEXT context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PPH_STRING selectedChoice; + + if ((context->Flags & PH_CHOICE_DIALOG_TYPE_MASK) != PH_CHOICE_DIALOG_PASSWORD) + { + selectedChoice = PH_AUTO(PhGetWindowText(context->ComboBoxHandle)); + *context->SelectedChoice = selectedChoice; + } + else + { + // Password values are never auto-dereferenced. + selectedChoice = PhGetWindowText(context->ComboBoxHandle); + *context->SelectedChoice = selectedChoice; + } + + if (context->Option && context->SelectedOption) + *context->SelectedOption = Button_GetCheck(GetDlgItem(hwndDlg, IDC_OPTION)) == BST_CHECKED; + + if (context->SavedChoicesSettingName) + { + PH_STRING_BUILDER savedChoices; + ULONG i; + ULONG choicesToSave = PH_CHOICE_DIALOG_SAVED_CHOICES; + PPH_STRING choice; + PPH_STRING escaped; + + PhInitializeStringBuilder(&savedChoices, 100); + + // Push the selected choice to the top, then save the others. + + if (selectedChoice->Length != 0) + { + escaped = PhEscapeStringForDelimiter(selectedChoice, '\\'); + PhAppendStringBuilder(&savedChoices, &escaped->sr); + PhDereferenceObject(escaped); + PhAppendStringBuilder2(&savedChoices, L"\\s"); + } + + for (i = 1; i < choicesToSave; i++) + { + choice = PhGetComboBoxString(context->ComboBoxHandle, i - 1); + + if (!choice) + break; + + // Don't save the choice if it's the same as the one + // entered by the user (since we already saved it above). + if (PhEqualString(choice, selectedChoice, FALSE)) + { + PhDereferenceObject(choice); + choicesToSave++; // useless for now, but may be needed in the future + continue; + } + + escaped = PhEscapeStringForDelimiter(choice, '\\'); + PhAppendStringBuilder(&savedChoices, &escaped->sr); + PhDereferenceObject(escaped); + PhDereferenceObject(choice); + + PhAppendStringBuilder2(&savedChoices, L"\\s"); + } + + if (PhEndsWithString2(savedChoices.String, L"\\s", FALSE)) + PhRemoveEndStringBuilder(&savedChoices, 2); + + PhSetStringSetting2(context->SavedChoicesSettingName, &savedChoices.String->sr); + PhDeleteStringBuilder(&savedChoices); + } + + EndDialog(hwndDlg, IDOK); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/chproc.c b/ProcessHacker/chproc.c index ef2b32659a40..de817e5c6c91 100644 --- a/ProcessHacker/chproc.c +++ b/ProcessHacker/chproc.c @@ -1,316 +1,318 @@ -/* - * Process Hacker - - * choose process dialog - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -typedef struct _CHOOSE_PROCESS_DIALOG_CONTEXT -{ - PWSTR Message; - HANDLE ProcessId; - - PH_LAYOUT_MANAGER LayoutManager; - RECT MinimumSize; - HIMAGELIST ImageList; - HWND ListViewHandle; -} CHOOSE_PROCESS_DIALOG_CONTEXT, *PCHOOSE_PROCESS_DIALOG_CONTEXT; - -INT_PTR CALLBACK PhpChooseProcessDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -BOOLEAN PhShowChooseProcessDialog( - _In_ HWND ParentWindowHandle, - _In_ PWSTR Message, - _Out_ PHANDLE ProcessId - ) -{ - CHOOSE_PROCESS_DIALOG_CONTEXT context; - - context.Message = Message; - context.ProcessId = NULL; - - if (DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_CHOOSEPROCESS), - ParentWindowHandle, - PhpChooseProcessDlgProc, - (LPARAM)&context - ) == IDOK) - { - *ProcessId = context.ProcessId; - - return TRUE; - } - else - { - return FALSE; - } -} - -static VOID PhpRefreshProcessList( - _In_ HWND hwndDlg, - _In_ PCHOOSE_PROCESS_DIALOG_CONTEXT Context - ) -{ - NTSTATUS status; - HWND lvHandle; - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - - lvHandle = Context->ListViewHandle; - - if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) - { - PhShowStatus(hwndDlg, L"Unable to enumerate processes", status, 0); - return; - } - - ExtendedListView_SetRedraw(lvHandle, FALSE); - - ListView_DeleteAllItems(lvHandle); - ImageList_RemoveAll(Context->ImageList); - - process = PH_FIRST_PROCESS(processes); - - do - { - INT lvItemIndex; - PPH_STRING name; - HANDLE processHandle; - PPH_STRING fileName = NULL; - HICON icon = NULL; - WCHAR processIdString[PH_INT32_STR_LEN_1]; - PPH_STRING userName = NULL; - INT imageIndex; - - if (process->UniqueProcessId != SYSTEM_IDLE_PROCESS_ID) - name = PhCreateStringFromUnicodeString(&process->ImageName); - else - name = PhCreateString(SYSTEM_IDLE_PROCESS_NAME); - - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, process->UniqueProcessId); - PhDereferenceObject(name); - - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, process->UniqueProcessId))) - { - HANDLE tokenHandle; - PTOKEN_USER user; - - if (!WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID && process->UniqueProcessId != SYSTEM_PROCESS_ID) - PhGetProcessImageFileName(processHandle, &fileName); - - if (NT_SUCCESS(PhOpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle))) - { - if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user))) - { - userName = PhGetSidFullName(user->User.Sid, TRUE, NULL); - PhFree(user); - } - - NtClose(tokenHandle); - } - - NtClose(processHandle); - } - - if (process->UniqueProcessId == SYSTEM_IDLE_PROCESS_ID && !userName && PhLocalSystemName) - PhSetReference(&userName, PhLocalSystemName); - - if (WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID && process->UniqueProcessId != SYSTEM_PROCESS_ID) - PhGetProcessImageFileNameByProcessId(process->UniqueProcessId, &fileName); - - if (process->UniqueProcessId == SYSTEM_PROCESS_ID) - fileName = PhGetKernelFileName(); - - if (fileName) - PhMoveReference(&fileName, PhGetFileName(fileName)); - - icon = PhGetFileShellIcon(PhGetString(fileName), L".exe", FALSE); - - // Icon - if (icon) - { - imageIndex = ImageList_AddIcon(Context->ImageList, icon); - PhSetListViewItemImageIndex(Context->ListViewHandle, lvItemIndex, imageIndex); - DestroyIcon(icon); - } - - // PID - PhPrintUInt32(processIdString, HandleToUlong(process->UniqueProcessId)); - PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, processIdString); - - // User Name - PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, PhGetString(userName)); - - if (userName) PhDereferenceObject(userName); - if (fileName) PhDereferenceObject(fileName); - } while (process = PH_NEXT_PROCESS(process)); - - PhFree(processes); - - ExtendedListView_SortItems(lvHandle); - ExtendedListView_SetRedraw(lvHandle, TRUE); -} - -INT_PTR CALLBACK PhpChooseProcessDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCHOOSE_PROCESS_DIALOG_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PCHOOSE_PROCESS_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - } - else - { - context = (PCHOOSE_PROCESS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (uMsg == WM_DESTROY) - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetDlgItemText(hwndDlg, IDC_MESSAGE, context->Message); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_MESSAGE), NULL, - PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhLayoutManagerLayout(&context->LayoutManager); - - context->MinimumSize.left = 0; - context->MinimumSize.top = 0; - context->MinimumSize.right = 280; - context->MinimumSize.bottom = 170; - MapDialogRect(hwndDlg, &context->MinimumSize); - - context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - context->ImageList = ImageList_Create(PhSmallIconSize.X, PhSmallIconSize.Y, ILC_COLOR32 | ILC_MASK, 0, 40); - - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 180, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 160, L"User name"); - PhSetExtendedListView(lvHandle); - - ListView_SetImageList(lvHandle, context->ImageList, LVSIL_SMALL); - - PhpRefreshProcessList(hwndDlg, context); - - EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); - } - break; - case WM_DESTROY: - { - ImageList_Destroy(context->ImageList); - PhDeleteLayoutManager(&context->LayoutManager); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - EndDialog(hwndDlg, IDCANCEL); - } - break; - case IDOK: - { - if (ListView_GetSelectedCount(context->ListViewHandle) == 1) - { - context->ProcessId = (HANDLE)PhGetSelectedListViewItemParam(context->ListViewHandle); - EndDialog(hwndDlg, IDOK); - } - } - break; - case IDC_REFRESH: - { - PhpRefreshProcessList(hwndDlg, context); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case LVN_ITEMCHANGED: - { - EnableWindow(GetDlgItem(hwndDlg, IDOK), ListView_GetSelectedCount(context->ListViewHandle) == 1); - } - break; - case NM_DBLCLK: - { - SendMessage(hwndDlg, WM_COMMAND, IDOK, 0); - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * choose process dialog + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +typedef struct _CHOOSE_PROCESS_DIALOG_CONTEXT +{ + PWSTR Message; + HANDLE ProcessId; + + PH_LAYOUT_MANAGER LayoutManager; + RECT MinimumSize; + HIMAGELIST ImageList; + HWND ListViewHandle; +} CHOOSE_PROCESS_DIALOG_CONTEXT, *PCHOOSE_PROCESS_DIALOG_CONTEXT; + +INT_PTR CALLBACK PhpChooseProcessDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOLEAN PhShowChooseProcessDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR Message, + _Out_ PHANDLE ProcessId + ) +{ + CHOOSE_PROCESS_DIALOG_CONTEXT context; + + memset(&context, 0, sizeof(CHOOSE_PROCESS_DIALOG_CONTEXT)); + context.Message = Message; + context.ProcessId = NULL; + + if (DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_CHOOSEPROCESS), + ParentWindowHandle, + PhpChooseProcessDlgProc, + (LPARAM)&context + ) == IDOK) + { + *ProcessId = context.ProcessId; + + return TRUE; + } + else + { + return FALSE; + } +} + +static VOID PhpRefreshProcessList( + _In_ HWND hwndDlg, + _In_ PCHOOSE_PROCESS_DIALOG_CONTEXT Context + ) +{ + NTSTATUS status; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + { + PhShowStatus(hwndDlg, L"Unable to enumerate processes", status, 0); + return; + } + + ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); + ListView_DeleteAllItems(Context->ListViewHandle); + ImageList_RemoveAll(Context->ImageList); + + process = PH_FIRST_PROCESS(processes); + + do + { + INT lvItemIndex; + PPH_STRING name; + HANDLE processHandle; + PPH_STRING fileName = NULL; + HICON icon = NULL; + WCHAR processIdString[PH_INT32_STR_LEN_1]; + PPH_STRING userName = NULL; + INT imageIndex = INT_MAX; + + if (process->UniqueProcessId != SYSTEM_IDLE_PROCESS_ID) + name = PhCreateStringFromUnicodeString(&process->ImageName); + else + name = PhCreateString(SYSTEM_IDLE_PROCESS_NAME); + + lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, name->Buffer, process->UniqueProcessId); + PhDereferenceObject(name); + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, process->UniqueProcessId))) + { + HANDLE tokenHandle; + PTOKEN_USER user; + + if (NT_SUCCESS(PhOpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle))) + { + if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user))) + { + userName = PhGetSidFullName(user->User.Sid, TRUE, NULL); + PhFree(user); + } + + NtClose(tokenHandle); + } + + NtClose(processHandle); + } + + if (process->UniqueProcessId == SYSTEM_IDLE_PROCESS_ID && !userName) + { + PhSetReference(&userName, PhGetSidFullName(&PhSeLocalSystemSid, TRUE, NULL)); + } + + if (process->UniqueProcessId == SYSTEM_PROCESS_ID) + fileName = PhGetKernelFileName(); + else if (PH_IS_REAL_PROCESS_ID(process->UniqueProcessId)) + PhGetProcessImageFileNameByProcessId(process->UniqueProcessId, &fileName); + + if (fileName) + PhMoveReference(&fileName, PhGetFileName(fileName)); + + // Icon + if (!PhIsNullOrEmptyString(fileName)) + { + PhExtractIcon(PhGetString(fileName), NULL, &icon); + } + + if (icon) + { + imageIndex = ImageList_AddIcon(Context->ImageList, icon); + PhSetListViewItemImageIndex(Context->ListViewHandle, lvItemIndex, imageIndex); + DestroyIcon(icon); + } + else + { + PhGetStockApplicationIcon(NULL, &icon); + imageIndex = ImageList_AddIcon(Context->ImageList, icon); + PhSetListViewItemImageIndex(Context->ListViewHandle, lvItemIndex, imageIndex); + } + + // PID + PhPrintUInt32(processIdString, HandleToUlong(process->UniqueProcessId)); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, processIdString); + + // User Name + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, PhGetString(userName)); + + if (userName) PhDereferenceObject(userName); + if (fileName) PhDereferenceObject(fileName); + } while (process = PH_NEXT_PROCESS(process)); + + PhFree(processes); + + ExtendedListView_SortItems(Context->ListViewHandle); + ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); +} + +INT_PTR CALLBACK PhpChooseProcessDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCHOOSE_PROCESS_DIALOG_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PCHOOSE_PROCESS_DIALOG_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetDialogItemText(hwndDlg, IDC_MESSAGE, context->Message); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_MESSAGE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhLayoutManagerLayout(&context->LayoutManager); + + context->MinimumSize.left = 0; + context->MinimumSize.top = 0; + context->MinimumSize.right = 280; + context->MinimumSize.bottom = 170; + MapDialogRect(hwndDlg, &context->MinimumSize); + + context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->ImageList = ImageList_Create(PhSmallIconSize.X, PhSmallIconSize.Y, ILC_COLOR32 | ILC_MASK, 0, 40); + + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 180, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 160, L"User name"); + PhSetExtendedListView(lvHandle); + + ListView_SetImageList(lvHandle, context->ImageList, LVSIL_SMALL); + + PhpRefreshProcessList(hwndDlg, context); + + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + } + break; + case WM_DESTROY: + { + ImageList_Destroy(context->ImageList); + PhDeleteLayoutManager(&context->LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + { + EndDialog(hwndDlg, IDCANCEL); + } + break; + case IDOK: + { + if (ListView_GetSelectedCount(context->ListViewHandle) == 1) + { + context->ProcessId = (HANDLE)PhGetSelectedListViewItemParam(context->ListViewHandle); + EndDialog(hwndDlg, IDOK); + } + } + break; + case IDC_REFRESH: + { + PhpRefreshProcessList(hwndDlg, context); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case LVN_ITEMCHANGED: + { + EnableWindow(GetDlgItem(hwndDlg, IDOK), ListView_GetSelectedCount(context->ListViewHandle) == 1); + } + break; + case NM_DBLCLK: + { + SendMessage(hwndDlg, WM_COMMAND, IDOK, 0); + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/cmdmode.c b/ProcessHacker/cmdmode.c index c47b048ecef2..fdc08b3a7dea 100644 --- a/ProcessHacker/cmdmode.c +++ b/ProcessHacker/cmdmode.c @@ -1,452 +1,399 @@ -/* - * Process Hacker - - * command line action mode - * - * Copyright (C) 2010-2012 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -NTSTATUS PhpGetDllBaseRemote( - _In_ HANDLE ProcessHandle, - _In_ PPH_STRINGREF BaseDllName, - _Out_ PVOID *DllBase - ); - -static HWND CommandModeWindowHandle; - -#define PH_COMMAND_OPTION_HWND 1 - -BOOLEAN NTAPI PhpCommandModeOptionCallback( - _In_opt_ PPH_COMMAND_LINE_OPTION Option, - _In_opt_ PPH_STRING Value, - _In_opt_ PVOID Context - ) -{ - ULONG64 integer; - - if (Option) - { - switch (Option->Id) - { - case PH_COMMAND_OPTION_HWND: - if (PhStringToInteger64(&Value->sr, 10, &integer)) - CommandModeWindowHandle = (HWND)integer; - break; - } - } - - return TRUE; -} - -NTSTATUS PhCommandModeStart( - VOID - ) -{ - static PH_COMMAND_LINE_OPTION options[] = - { - { PH_COMMAND_OPTION_HWND, L"hwnd", MandatoryArgumentType } - }; - NTSTATUS status = STATUS_SUCCESS; - PH_STRINGREF commandLine; - - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - - PhParseCommandLine( - &commandLine, - options, - sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), - PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS, - PhpCommandModeOptionCallback, - NULL - ); - - if (PhEqualString2(PhStartupParameters.CommandType, L"process", TRUE)) - { - SIZE_T i; - SIZE_T processIdLength; - HANDLE processId; - HANDLE processHandle; - - if (!PhStartupParameters.CommandObject) - return STATUS_INVALID_PARAMETER; - - processIdLength = PhStartupParameters.CommandObject->Length / 2; - - for (i = 0; i < processIdLength; i++) - { - if (!PhIsDigitCharacter(PhStartupParameters.CommandObject->Buffer[i])) - break; - } - - if (i == processIdLength) - { - ULONG64 processId64; - - if (!PhStringToInteger64(&PhStartupParameters.CommandObject->sr, 10, &processId64)) - return STATUS_INVALID_PARAMETER; - - processId = (HANDLE)processId64; - } - else - { - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - - if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) - return status; - - if (!(process = PhFindProcessInformationByImageName(processes, &PhStartupParameters.CommandObject->sr))) - { - PhFree(processes); - return STATUS_NOT_FOUND; - } - - processId = process->UniqueProcessId; - PhFree(processes); - } - - if (PhEqualString2(PhStartupParameters.CommandAction, L"terminate", TRUE)) - { - if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_TERMINATE, processId))) - { - status = NtTerminateProcess(processHandle, STATUS_SUCCESS); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"suspend", TRUE)) - { - if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SUSPEND_RESUME, processId))) - { - status = NtSuspendProcess(processHandle); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"resume", TRUE)) - { - if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SUSPEND_RESUME, processId))) - { - status = NtResumeProcess(processHandle); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"priority", TRUE)) - { - UCHAR priority; - - if (!PhStartupParameters.CommandValue) - return STATUS_INVALID_PARAMETER; - - if (PhEqualString2(PhStartupParameters.CommandValue, L"idle", TRUE)) - priority = PROCESS_PRIORITY_CLASS_IDLE; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"normal", TRUE)) - priority = PROCESS_PRIORITY_CLASS_NORMAL; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"high", TRUE)) - priority = PROCESS_PRIORITY_CLASS_HIGH; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"realtime", TRUE)) - priority = PROCESS_PRIORITY_CLASS_REALTIME; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"abovenormal", TRUE)) - priority = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"belownormal", TRUE)) - priority = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; - else - return STATUS_INVALID_PARAMETER; - - if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) - { - PROCESS_PRIORITY_CLASS priorityClass; - priorityClass.Foreground = FALSE; - priorityClass.PriorityClass = priority; - status = NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"iopriority", TRUE)) - { - ULONG ioPriority; - - if (!PhStartupParameters.CommandValue) - return STATUS_INVALID_PARAMETER; - - if (PhEqualString2(PhStartupParameters.CommandValue, L"verylow", TRUE)) - ioPriority = 0; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"low", TRUE)) - ioPriority = 1; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"normal", TRUE)) - ioPriority = 2; - else if (PhEqualString2(PhStartupParameters.CommandValue, L"high", TRUE)) - ioPriority = 3; - else - return STATUS_INVALID_PARAMETER; - - if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) - { - status = PhSetProcessIoPriority(processHandle, ioPriority); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"pagepriority", TRUE)) - { - ULONG64 pagePriority64; - ULONG pagePriority; - - if (!PhStartupParameters.CommandValue) - return STATUS_INVALID_PARAMETER; - - PhStringToInteger64(&PhStartupParameters.CommandValue->sr, 10, &pagePriority64); - pagePriority = (ULONG)pagePriority64; - - if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) - { - status = NtSetInformationProcess( - processHandle, - ProcessPagePriority, - &pagePriority, - sizeof(ULONG) - ); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"injectdll", TRUE)) - { - if (!PhStartupParameters.CommandValue) - return STATUS_INVALID_PARAMETER; - - if (NT_SUCCESS(status = PhOpenProcessPublic( - &processHandle, - ProcessQueryAccess | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, - processId - ))) - { - LARGE_INTEGER timeout; - - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - status = PhInjectDllProcess( - processHandle, - PhStartupParameters.CommandValue->Buffer, - &timeout - ); - NtClose(processHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"unloaddll", TRUE)) - { - if (!PhStartupParameters.CommandValue) - return STATUS_INVALID_PARAMETER; - - if (NT_SUCCESS(status = PhOpenProcessPublic( - &processHandle, - ProcessQueryAccess | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, - processId - ))) - { - PVOID baseAddress; - - if (NT_SUCCESS(status = PhpGetDllBaseRemote( - processHandle, - &PhStartupParameters.CommandValue->sr, - &baseAddress - ))) - { - LARGE_INTEGER timeout; - - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - status = PhUnloadDllProcess( - processHandle, - baseAddress, - &timeout - ); - } - - NtClose(processHandle); - } - } - } - else if (PhEqualString2(PhStartupParameters.CommandType, L"service", TRUE)) - { - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; - - if (!PhStartupParameters.CommandObject) - return STATUS_INVALID_PARAMETER; - - if (PhEqualString2(PhStartupParameters.CommandAction, L"start", TRUE)) - { - if (!(serviceHandle = PhOpenService( - PhStartupParameters.CommandObject->Buffer, - SERVICE_START - ))) - return PhGetLastWin32ErrorAsNtStatus(); - - if (!StartService(serviceHandle, 0, NULL)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"continue", TRUE)) - { - if (!(serviceHandle = PhOpenService( - PhStartupParameters.CommandObject->Buffer, - SERVICE_PAUSE_CONTINUE - ))) - return PhGetLastWin32ErrorAsNtStatus(); - - if (!ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"pause", TRUE)) - { - if (!(serviceHandle = PhOpenService( - PhStartupParameters.CommandObject->Buffer, - SERVICE_PAUSE_CONTINUE - ))) - return PhGetLastWin32ErrorAsNtStatus(); - - if (!ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"stop", TRUE)) - { - if (!(serviceHandle = PhOpenService( - PhStartupParameters.CommandObject->Buffer, - SERVICE_STOP - ))) - return PhGetLastWin32ErrorAsNtStatus(); - - if (!ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"delete", TRUE)) - { - if (!(serviceHandle = PhOpenService( - PhStartupParameters.CommandObject->Buffer, - DELETE - ))) - return PhGetLastWin32ErrorAsNtStatus(); - - if (!DeleteService(serviceHandle)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandType, L"thread", TRUE)) - { - ULONG64 threadId64; - HANDLE threadId; - HANDLE threadHandle; - - if (!PhStartupParameters.CommandObject) - return STATUS_INVALID_PARAMETER; - - if (!PhStringToInteger64(&PhStartupParameters.CommandObject->sr, 10, &threadId64)) - return STATUS_INVALID_PARAMETER; - - threadId = (HANDLE)threadId64; - - if (PhEqualString2(PhStartupParameters.CommandAction, L"terminate", TRUE)) - { - if (NT_SUCCESS(status = PhOpenThreadPublic(&threadHandle, THREAD_TERMINATE, threadId))) - { - status = NtTerminateThread(threadHandle, STATUS_SUCCESS); - NtClose(threadHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"suspend", TRUE)) - { - if (NT_SUCCESS(status = PhOpenThreadPublic(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) - { - status = NtSuspendThread(threadHandle, NULL); - NtClose(threadHandle); - } - } - else if (PhEqualString2(PhStartupParameters.CommandAction, L"resume", TRUE)) - { - if (NT_SUCCESS(status = PhOpenThreadPublic(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) - { - status = NtResumeThread(threadHandle, NULL); - NtClose(threadHandle); - } - } - } - - return status; -} - -typedef struct _GET_DLL_BASE_REMOTE_CONTEXT -{ - PH_STRINGREF BaseDllName; - PVOID DllBase; -} GET_DLL_BASE_REMOTE_CONTEXT, *PGET_DLL_BASE_REMOTE_CONTEXT; - -static BOOLEAN PhpGetDllBaseRemoteCallback( - _In_ PLDR_DATA_TABLE_ENTRY Module, - _In_opt_ PVOID Context - ) -{ - PGET_DLL_BASE_REMOTE_CONTEXT context = Context; - PH_STRINGREF baseDllName; - - PhUnicodeStringToStringRef(&Module->BaseDllName, &baseDllName); - - if (PhEqualStringRef(&baseDllName, &context->BaseDllName, TRUE)) - { - context->DllBase = Module->DllBase; - return FALSE; - } - - return TRUE; -} - -NTSTATUS PhpGetDllBaseRemote( - _In_ HANDLE ProcessHandle, - _In_ PPH_STRINGREF BaseDllName, - _Out_ PVOID *DllBase - ) -{ - NTSTATUS status; - GET_DLL_BASE_REMOTE_CONTEXT context; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; -#endif - - context.BaseDllName = *BaseDllName; - context.DllBase = NULL; - -#ifdef _WIN64 - PhGetProcessIsWow64(ProcessHandle, &isWow64); - - if (isWow64) - status = PhEnumProcessModules32(ProcessHandle, PhpGetDllBaseRemoteCallback, &context); - if (!context.DllBase) -#endif - status = PhEnumProcessModules(ProcessHandle, PhpGetDllBaseRemoteCallback, &context); - - if (NT_SUCCESS(status)) - *DllBase = context.DllBase; - - return status; -} +/* + * Process Hacker - + * command line action mode + * + * Copyright (C) 2010-2012 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include + +NTSTATUS PhpGetDllBaseRemote( + _In_ HANDLE ProcessHandle, + _In_ PPH_STRINGREF BaseDllName, + _Out_ PVOID *DllBase + ); + +static HWND CommandModeWindowHandle; + +#define PH_COMMAND_OPTION_HWND 1 + +BOOLEAN NTAPI PhpCommandModeOptionCallback( + _In_opt_ PPH_COMMAND_LINE_OPTION Option, + _In_opt_ PPH_STRING Value, + _In_opt_ PVOID Context + ) +{ + ULONG64 integer; + + if (Option) + { + switch (Option->Id) + { + case PH_COMMAND_OPTION_HWND: + if (PhStringToInteger64(&Value->sr, 10, &integer)) + CommandModeWindowHandle = (HWND)integer; + break; + } + } + + return TRUE; +} + +NTSTATUS PhCommandModeStart( + VOID + ) +{ + static PH_COMMAND_LINE_OPTION options[] = + { + { PH_COMMAND_OPTION_HWND, L"hwnd", MandatoryArgumentType } + }; + NTSTATUS status; + PPH_STRING commandLine; + + if (!NT_SUCCESS(status = PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) + return status; + + PhParseCommandLine( + &commandLine->sr, + options, + RTL_NUMBER_OF(options), + PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS, + PhpCommandModeOptionCallback, + NULL + ); + PhDereferenceObject(commandLine); + + if (PhEqualString2(PhStartupParameters.CommandType, L"process", TRUE)) + { + SIZE_T i; + SIZE_T processIdLength; + HANDLE processId; + HANDLE processHandle; + + if (!PhStartupParameters.CommandObject) + return STATUS_INVALID_PARAMETER; + + processIdLength = PhStartupParameters.CommandObject->Length / sizeof(WCHAR); + + for (i = 0; i < processIdLength; i++) + { + if (!PhIsDigitCharacter(PhStartupParameters.CommandObject->Buffer[i])) + break; + } + + if (i == processIdLength) + { + ULONG64 processId64; + + if (!PhStringToInteger64(&PhStartupParameters.CommandObject->sr, 10, &processId64)) + return STATUS_INVALID_PARAMETER; + + processId = (HANDLE)processId64; + } + else + { + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + return status; + + if (!(process = PhFindProcessInformationByImageName(processes, &PhStartupParameters.CommandObject->sr))) + { + PhFree(processes); + return STATUS_NOT_FOUND; + } + + processId = process->UniqueProcessId; + PhFree(processes); + } + + if (PhEqualString2(PhStartupParameters.CommandAction, L"terminate", TRUE)) + { + if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_TERMINATE, processId))) + { + status = NtTerminateProcess(processHandle, STATUS_SUCCESS); + NtClose(processHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"suspend", TRUE)) + { + if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SUSPEND_RESUME, processId))) + { + status = NtSuspendProcess(processHandle); + NtClose(processHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"resume", TRUE)) + { + if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SUSPEND_RESUME, processId))) + { + status = NtResumeProcess(processHandle); + NtClose(processHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"priority", TRUE)) + { + UCHAR priority; + + if (!PhStartupParameters.CommandValue) + return STATUS_INVALID_PARAMETER; + + if (PhEqualString2(PhStartupParameters.CommandValue, L"idle", TRUE)) + priority = PROCESS_PRIORITY_CLASS_IDLE; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"normal", TRUE)) + priority = PROCESS_PRIORITY_CLASS_NORMAL; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"high", TRUE)) + priority = PROCESS_PRIORITY_CLASS_HIGH; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"realtime", TRUE)) + priority = PROCESS_PRIORITY_CLASS_REALTIME; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"abovenormal", TRUE)) + priority = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"belownormal", TRUE)) + priority = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; + else + return STATUS_INVALID_PARAMETER; + + if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) + { + PROCESS_PRIORITY_CLASS priorityClass; + + priorityClass.Foreground = FALSE; + priorityClass.PriorityClass = priority; + + status = PhSetProcessPriority(processHandle, priorityClass); + + NtClose(processHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"iopriority", TRUE)) + { + ULONG ioPriority; + + if (!PhStartupParameters.CommandValue) + return STATUS_INVALID_PARAMETER; + + if (PhEqualString2(PhStartupParameters.CommandValue, L"verylow", TRUE)) + ioPriority = 0; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"low", TRUE)) + ioPriority = 1; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"normal", TRUE)) + ioPriority = 2; + else if (PhEqualString2(PhStartupParameters.CommandValue, L"high", TRUE)) + ioPriority = 3; + else + return STATUS_INVALID_PARAMETER; + + if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) + { + status = PhSetProcessIoPriority(processHandle, ioPriority); + NtClose(processHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"pagepriority", TRUE)) + { + ULONG64 pagePriority64; + ULONG pagePriority; + + if (!PhStartupParameters.CommandValue) + return STATUS_INVALID_PARAMETER; + + PhStringToInteger64(&PhStartupParameters.CommandValue->sr, 10, &pagePriority64); + pagePriority = (ULONG)pagePriority64; + + if (NT_SUCCESS(status = PhOpenProcessPublic(&processHandle, PROCESS_SET_INFORMATION, processId))) + { + status = PhSetProcessPagePriority(processHandle, pagePriority); + + NtClose(processHandle); + } + } + } + else if (PhEqualString2(PhStartupParameters.CommandType, L"service", TRUE)) + { + SC_HANDLE serviceHandle; + SERVICE_STATUS serviceStatus; + + if (!PhStartupParameters.CommandObject) + return STATUS_INVALID_PARAMETER; + + if (PhEqualString2(PhStartupParameters.CommandAction, L"start", TRUE)) + { + if (!(serviceHandle = PhOpenService( + PhStartupParameters.CommandObject->Buffer, + SERVICE_START + ))) + return PhGetLastWin32ErrorAsNtStatus(); + + if (!StartService(serviceHandle, 0, NULL)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"continue", TRUE)) + { + if (!(serviceHandle = PhOpenService( + PhStartupParameters.CommandObject->Buffer, + SERVICE_PAUSE_CONTINUE + ))) + return PhGetLastWin32ErrorAsNtStatus(); + + if (!ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"pause", TRUE)) + { + if (!(serviceHandle = PhOpenService( + PhStartupParameters.CommandObject->Buffer, + SERVICE_PAUSE_CONTINUE + ))) + return PhGetLastWin32ErrorAsNtStatus(); + + if (!ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"stop", TRUE)) + { + if (!(serviceHandle = PhOpenService( + PhStartupParameters.CommandObject->Buffer, + SERVICE_STOP + ))) + return PhGetLastWin32ErrorAsNtStatus(); + + if (!ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"delete", TRUE)) + { + if (!(serviceHandle = PhOpenService( + PhStartupParameters.CommandObject->Buffer, + DELETE + ))) + return PhGetLastWin32ErrorAsNtStatus(); + + if (!DeleteService(serviceHandle)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandType, L"thread", TRUE)) + { + ULONG64 threadId64; + HANDLE threadId; + HANDLE threadHandle; + + if (!PhStartupParameters.CommandObject) + return STATUS_INVALID_PARAMETER; + + if (!PhStringToInteger64(&PhStartupParameters.CommandObject->sr, 10, &threadId64)) + return STATUS_INVALID_PARAMETER; + + threadId = (HANDLE)threadId64; + + if (PhEqualString2(PhStartupParameters.CommandAction, L"terminate", TRUE)) + { + if (NT_SUCCESS(status = PhOpenThreadPublic(&threadHandle, THREAD_TERMINATE, threadId))) + { + status = NtTerminateThread(threadHandle, STATUS_SUCCESS); + NtClose(threadHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"suspend", TRUE)) + { + if (NT_SUCCESS(status = PhOpenThreadPublic(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) + { + status = NtSuspendThread(threadHandle, NULL); + NtClose(threadHandle); + } + } + else if (PhEqualString2(PhStartupParameters.CommandAction, L"resume", TRUE)) + { + if (NT_SUCCESS(status = PhOpenThreadPublic(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) + { + status = NtResumeThread(threadHandle, NULL); + NtClose(threadHandle); + } + } + } + + return status; +} + +typedef struct _GET_DLL_BASE_REMOTE_CONTEXT +{ + PH_STRINGREF BaseDllName; + PVOID DllBase; +} GET_DLL_BASE_REMOTE_CONTEXT, *PGET_DLL_BASE_REMOTE_CONTEXT; + +static BOOLEAN PhpGetDllBaseRemoteCallback( + _In_ PLDR_DATA_TABLE_ENTRY Module, + _In_opt_ PVOID Context + ) +{ + PGET_DLL_BASE_REMOTE_CONTEXT context = Context; + PH_STRINGREF baseDllName; + + PhUnicodeStringToStringRef(&Module->BaseDllName, &baseDllName); + + if (PhEqualStringRef(&baseDllName, &context->BaseDllName, TRUE)) + { + context->DllBase = Module->DllBase; + return FALSE; + } + + return TRUE; +} + +NTSTATUS PhpGetDllBaseRemote( + _In_ HANDLE ProcessHandle, + _In_ PPH_STRINGREF BaseDllName, + _Out_ PVOID *DllBase + ) +{ + NTSTATUS status; + GET_DLL_BASE_REMOTE_CONTEXT context; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; +#endif + + context.BaseDllName = *BaseDllName; + context.DllBase = NULL; + +#ifdef _WIN64 + PhGetProcessIsWow64(ProcessHandle, &isWow64); + + if (isWow64) + status = PhEnumProcessModules32(ProcessHandle, PhpGetDllBaseRemoteCallback, &context); + if (!context.DllBase) +#endif + status = PhEnumProcessModules(ProcessHandle, PhpGetDllBaseRemoteCallback, &context); + + if (NT_SUCCESS(status)) + *DllBase = context.DllBase; + + return status; +} diff --git a/ProcessHacker/colmgr.c b/ProcessHacker/colmgr.c index ba95bbcad89f..bfbbd792dc4f 100644 --- a/ProcessHacker/colmgr.c +++ b/ProcessHacker/colmgr.c @@ -1,710 +1,710 @@ -/* - * Process Hacker - - * tree new column manager - * - * Copyright (C) 2011-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -typedef struct _PH_CM_SORT_CONTEXT -{ - PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction; - ULONG SubId; - PVOID Context; - PPH_CM_POST_SORT_FUNCTION PostSortFunction; - PH_SORT_ORDER SortOrder; -} PH_CM_SORT_CONTEXT, *PPH_CM_SORT_CONTEXT; - -VOID PhCmInitializeManager( - _Out_ PPH_CM_MANAGER Manager, - _In_ HWND Handle, - _In_ ULONG MinId, - _In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction - ) -{ - Manager->Handle = Handle; - Manager->MinId = MinId; - Manager->NextId = MinId; - Manager->PostSortFunction = PostSortFunction; - InitializeListHead(&Manager->ColumnListHead); - Manager->NotifyList = NULL; -} - -VOID PhCmDeleteManager( - _In_ PPH_CM_MANAGER Manager - ) -{ - PLIST_ENTRY listEntry; - PPH_CM_COLUMN column; - - listEntry = Manager->ColumnListHead.Flink; - - while (listEntry != &Manager->ColumnListHead) - { - column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry); - listEntry = listEntry->Flink; - - PhFree(column); - } - - if (Manager->NotifyList) - PhDereferenceObject(Manager->NotifyList); -} - -PPH_CM_COLUMN PhCmCreateColumn( - _Inout_ PPH_CM_MANAGER Manager, - _In_ PPH_TREENEW_COLUMN Column, - _In_ struct _PH_PLUGIN *Plugin, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_ PVOID SortFunction - ) -{ - PPH_CM_COLUMN column; - PH_TREENEW_COLUMN tnColumn; - - column = PhAllocate(sizeof(PH_CM_COLUMN)); - memset(column, 0, sizeof(PH_CM_COLUMN)); - column->Id = Manager->NextId++; - column->Plugin = Plugin; - column->SubId = SubId; - column->Context = Context; - column->SortFunction = SortFunction; - InsertTailList(&Manager->ColumnListHead, &column->ListEntry); - - memset(&tnColumn, 0, sizeof(PH_TREENEW_COLUMN)); - tnColumn.Id = column->Id; - tnColumn.Context = column; - tnColumn.Visible = Column->Visible; - tnColumn.CustomDraw = Column->CustomDraw; - tnColumn.SortDescending = Column->SortDescending; - tnColumn.Text = Column->Text; - tnColumn.Width = Column->Width; - tnColumn.Alignment = Column->Alignment; - tnColumn.DisplayIndex = Column->Visible ? Column->DisplayIndex : -1; - tnColumn.TextFlags = Column->TextFlags; - TreeNew_AddColumn(Manager->Handle, &tnColumn); - - return column; -} - -PPH_CM_COLUMN PhCmFindColumn( - _In_ PPH_CM_MANAGER Manager, - _In_ PPH_STRINGREF PluginName, - _In_ ULONG SubId - ) -{ - PLIST_ENTRY listEntry; - PPH_CM_COLUMN column; - - listEntry = Manager->ColumnListHead.Flink; - - while (listEntry != &Manager->ColumnListHead) - { - column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry); - - if (column->SubId == SubId && PhEqualStringRef(PluginName, &column->Plugin->AppContext.AppName, FALSE)) - return column; - - listEntry = listEntry->Flink; - } - - return NULL; -} - -VOID PhCmSetNotifyPlugin( - _In_ PPH_CM_MANAGER Manager, - _In_ struct _PH_PLUGIN *Plugin - ) -{ - if (!Manager->NotifyList) - { - Manager->NotifyList = PhCreateList(8); - } - else - { - if (PhFindItemList(Manager->NotifyList, Plugin) != -1) - return; - } - - PhAddItemList(Manager->NotifyList, Plugin); -} - -BOOLEAN PhCmForwardMessage( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_ PPH_CM_MANAGER Manager - ) -{ - PH_PLUGIN_TREENEW_MESSAGE pluginMessage; - PPH_PLUGIN plugin; - - if (Message == TreeNewDestroying) - return FALSE; - - switch (Message) - { - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PH_TREENEW_COLUMN tnColumn; - PPH_CM_COLUMN column; - - if (getCellText->Id < Manager->MinId) - return FALSE; - - if (!TreeNew_GetColumn(hwnd, getCellText->Id, &tnColumn)) - return FALSE; - - column = tnColumn.Context; - pluginMessage.SubId = column->SubId; - pluginMessage.Context = column->Context; - plugin = column->Plugin; - } - break; - case TreeNewCustomDraw: - { - PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; - PPH_CM_COLUMN column; - - if (customDraw->Column->Id < Manager->MinId) - return FALSE; - - column = customDraw->Column->Context; - pluginMessage.SubId = column->SubId; - pluginMessage.Context = column->Context; - plugin = column->Plugin; - } - break; - case TreeNewColumnResized: - { - PPH_TREENEW_COLUMN tlColumn = Parameter1; - PPH_CM_COLUMN column; - - if (tlColumn->Id < Manager->MinId) - return FALSE; - - column = tlColumn->Context; - pluginMessage.SubId = column->SubId; - pluginMessage.Context = column->Context; - plugin = column->Plugin; - } - break; - default: - { - // Some plugins want to be notified about all messages. - if (Manager->NotifyList) - { - ULONG i; - - for (i = 0; i < Manager->NotifyList->Count; i++) - { - plugin = Manager->NotifyList->Items[i]; - - pluginMessage.TreeNewHandle = hwnd; - pluginMessage.Message = Message; - pluginMessage.Parameter1 = Parameter1; - pluginMessage.Parameter2 = Parameter2; - pluginMessage.SubId = 0; - pluginMessage.Context = NULL; - - PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); - } - } - } - return FALSE; - } - - pluginMessage.TreeNewHandle = hwnd; - pluginMessage.Message = Message; - pluginMessage.Parameter1 = Parameter1; - pluginMessage.Parameter2 = Parameter2; - - PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); - - return TRUE; -} - -static int __cdecl PhCmpSortFunction( - _In_ void *context, - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_CM_SORT_CONTEXT sortContext = context; - PVOID node1 = *(PVOID *)elem1; - PVOID node2 = *(PVOID *)elem2; - LONG result; - - result = sortContext->SortFunction(node1, node2, sortContext->SubId, sortContext->Context); - - return sortContext->PostSortFunction(result, node1, node2, sortContext->SortOrder); -} - -BOOLEAN PhCmForwardSort( - _In_ PPH_TREENEW_NODE *Nodes, - _In_ ULONG NumberOfNodes, - _In_ ULONG SortColumn, - _In_ PH_SORT_ORDER SortOrder, - _In_ PPH_CM_MANAGER Manager - ) -{ - PH_TREENEW_COLUMN tnColumn; - PPH_CM_COLUMN column; - PH_CM_SORT_CONTEXT sortContext; - - if (SortColumn < Manager->MinId) - return FALSE; - - if (!TreeNew_GetColumn(Manager->Handle, SortColumn, &tnColumn)) - return TRUE; - - column = tnColumn.Context; - - if (!column->SortFunction) - return TRUE; - - sortContext.SortFunction = column->SortFunction; - sortContext.SubId = column->SubId; - sortContext.Context = column->Context; - sortContext.PostSortFunction = Manager->PostSortFunction; - sortContext.SortOrder = SortOrder; - qsort_s(Nodes, NumberOfNodes, sizeof(PVOID), PhCmpSortFunction, &sortContext); - - return TRUE; -} - -BOOLEAN PhCmLoadSettings( - _In_ HWND TreeNewHandle, - _In_ PPH_STRINGREF Settings - ) -{ - return PhCmLoadSettingsEx(TreeNewHandle, NULL, 0, Settings, NULL); -} - -BOOLEAN PhCmLoadSettingsEx( - _In_ HWND TreeNewHandle, - _In_opt_ PPH_CM_MANAGER Manager, - _In_ ULONG Flags, - _In_ PPH_STRINGREF Settings, - _In_opt_ PPH_STRINGREF SortSettings - ) -{ - BOOLEAN result = FALSE; - PH_STRINGREF scalePart; - PH_STRINGREF columnPart; - PH_STRINGREF remainingColumnPart; - PH_STRINGREF valuePart; - PH_STRINGREF subPart; - ULONG64 integer; - ULONG scale; - ULONG total; - BOOLEAN hasFixedColumn; - ULONG count; - ULONG i; - PPH_HASHTABLE columnHashtable; - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_KEY_VALUE_PAIR pair; - LONG orderArray[PH_CM_ORDER_LIMIT]; - LONG maxOrder; - - if (Settings->Length != 0) - { - columnHashtable = PhCreateSimpleHashtable(20); - - remainingColumnPart = *Settings; - - if (remainingColumnPart.Length != 0 && remainingColumnPart.Buffer[0] == '@') - { - PhSkipStringRef(&remainingColumnPart, sizeof(WCHAR)); - PhSplitStringRefAtChar(&remainingColumnPart, '|', &scalePart, &remainingColumnPart); - - if (scalePart.Length == 0 || !PhStringToInteger64(&scalePart, 10, &integer)) - goto CleanupExit; - - scale = (ULONG)integer; - } - else - { - scale = PhGlobalDpi; - } - - while (remainingColumnPart.Length != 0) - { - PPH_TREENEW_COLUMN column; - ULONG id; - ULONG displayIndex; - ULONG width; - - PhSplitStringRefAtChar(&remainingColumnPart, '|', &columnPart, &remainingColumnPart); - - if (columnPart.Length != 0) - { - // Id - - PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); - - if (valuePart.Length == 0) - goto CleanupExit; - - if (valuePart.Buffer[0] == '+') - { - PH_STRINGREF pluginName; - ULONG subId; - PPH_CM_COLUMN cmColumn; - - // This is a plugin-owned column. - - if (!Manager) - continue; - if (!PhEmParseCompoundId(&valuePart, &pluginName, &subId)) - continue; - - cmColumn = PhCmFindColumn(Manager, &pluginName, subId); - - if (!cmColumn) - continue; // can't find the column, skip this part - - id = cmColumn->Id; - } - else - { - if (!PhStringToInteger64(&valuePart, 10, &integer)) - goto CleanupExit; - - id = (ULONG)integer; - } - - // Display Index - - PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); - - if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) - { - if (valuePart.Length == 0 || !PhStringToInteger64(&valuePart, 10, &integer)) - goto CleanupExit; - - displayIndex = (ULONG)integer; - } - else - { - if (valuePart.Length != 0) - goto CleanupExit; - - displayIndex = -1; - } - - // Width - - if (columnPart.Length == 0 || !PhStringToInteger64(&columnPart, 10, &integer)) - goto CleanupExit; - - width = (ULONG)integer; - - if (scale != PhGlobalDpi && scale != 0) - width = PhMultiplyDivide(width, PhGlobalDpi, scale); - - column = PhAllocate(sizeof(PH_TREENEW_COLUMN)); - column->Id = id; - column->DisplayIndex = displayIndex; - column->Width = width; - PhAddItemSimpleHashtable(columnHashtable, UlongToPtr(column->Id), column); - } - } - - TreeNew_SetRedraw(TreeNewHandle, FALSE); - - // Set visibility and width. - - i = 0; - count = 0; - total = TreeNew_GetColumnCount(TreeNewHandle); - hasFixedColumn = !!TreeNew_GetFixedColumn(TreeNewHandle); - memset(orderArray, 0, sizeof(orderArray)); - maxOrder = 0; - - while (count < total) - { - PH_TREENEW_COLUMN setColumn; - PPH_TREENEW_COLUMN *columnPtr; - - if (TreeNew_GetColumn(TreeNewHandle, i, &setColumn)) - { - columnPtr = (PPH_TREENEW_COLUMN *)PhFindItemSimpleHashtable(columnHashtable, UlongToPtr(i)); - - if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) - { - if (columnPtr) - { - setColumn.Visible = TRUE; - setColumn.Width = (*columnPtr)->Width; - TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE | TN_COLUMN_WIDTH, &setColumn); - - if (!setColumn.Fixed) - { - // For compatibility reasons, normal columns have their display indicies stored - // one higher than usual (so they start from 1, not 0). Fix that here. - if (hasFixedColumn && (*columnPtr)->DisplayIndex != 0) - (*columnPtr)->DisplayIndex--; - - if ((*columnPtr)->DisplayIndex < PH_CM_ORDER_LIMIT) - { - orderArray[(*columnPtr)->DisplayIndex] = i; - - if ((ULONG)maxOrder < (*columnPtr)->DisplayIndex + 1) - maxOrder = (*columnPtr)->DisplayIndex + 1; - } - } - } - else if (!setColumn.Fixed) // never hide the fixed column - { - setColumn.Visible = FALSE; - TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &setColumn); - } - } - else - { - if (columnPtr) - { - setColumn.Width = (*columnPtr)->Width; - TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_WIDTH, &setColumn); - } - } - - count++; - } - - i++; - } - - if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) - { - // Set the order array. - TreeNew_SetColumnOrderArray(TreeNewHandle, maxOrder, orderArray); - } - - TreeNew_SetRedraw(TreeNewHandle, TRUE); - - result = TRUE; - -CleanupExit: - PhBeginEnumHashtable(columnHashtable, &enumContext); - - while (pair = PhNextEnumHashtable(&enumContext)) - PhFree(pair->Value); - - PhDereferenceObject(columnHashtable); - } - - // Load sort settings. - - if (SortSettings && SortSettings->Length != 0) - { - PhSplitStringRefAtChar(SortSettings, ',', &valuePart, &subPart); - - if (valuePart.Length != 0 && subPart.Length != 0) - { - ULONG sortColumn; - PH_SORT_ORDER sortOrder; - - sortColumn = -1; - - if (valuePart.Buffer[0] == '+') - { - PH_STRINGREF pluginName; - ULONG subId; - PPH_CM_COLUMN cmColumn; - - if ( - Manager && - PhEmParseCompoundId(&valuePart, &pluginName, &subId) && - (cmColumn = PhCmFindColumn(Manager, &pluginName, subId)) - ) - { - sortColumn = cmColumn->Id; - } - } - else - { - PhStringToInteger64(&valuePart, 10, &integer); - sortColumn = (ULONG)integer; - } - - PhStringToInteger64(&subPart, 10, &integer); - sortOrder = (PH_SORT_ORDER)integer; - - if (sortColumn != -1 && sortOrder <= DescendingSortOrder) - { - TreeNew_SetSort(TreeNewHandle, sortColumn, sortOrder); - } - } - } - - return result; -} - -PPH_STRING PhCmSaveSettings( - _In_ HWND TreeNewHandle - ) -{ - return PhCmSaveSettingsEx(TreeNewHandle, NULL, 0, NULL); -} - -PPH_STRING PhCmSaveSettingsEx( - _In_ HWND TreeNewHandle, - _In_opt_ PPH_CM_MANAGER Manager, - _In_ ULONG Flags, - _Out_opt_ PPH_STRING *SortSettings - ) -{ - PH_STRING_BUILDER stringBuilder; - ULONG i = 0; - ULONG count = 0; - ULONG total; - ULONG increment; - PH_TREENEW_COLUMN column; - - total = TreeNew_GetColumnCount(TreeNewHandle); - - if (TreeNew_GetFixedColumn(TreeNewHandle)) - increment = 1; // the first normal column should have a display index that starts with 1, for compatibility - else - increment = 0; - - PhInitializeStringBuilder(&stringBuilder, 100); - - PhAppendFormatStringBuilder(&stringBuilder, L"@%u|", PhGlobalDpi); - - while (count < total) - { - if (TreeNew_GetColumn(TreeNewHandle, i, &column)) - { - if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) - { - if (column.Visible) - { - if (!Manager || i < Manager->MinId) - { - PhAppendFormatStringBuilder( - &stringBuilder, - L"%u,%u,%u|", - i, - column.Fixed ? 0 : column.DisplayIndex + increment, - column.Width - ); - } - else - { - PPH_CM_COLUMN cmColumn; - - cmColumn = column.Context; - PhAppendFormatStringBuilder( - &stringBuilder, - L"+%s+%u,%u,%u|", - cmColumn->Plugin->Name.Buffer, - cmColumn->SubId, - column.DisplayIndex + increment, - column.Width - ); - } - } - } - else - { - if (!Manager || i < Manager->MinId) - { - PhAppendFormatStringBuilder( - &stringBuilder, - L"%u,,%u|", - i, - column.Width - ); - } - else - { - PPH_CM_COLUMN cmColumn; - - cmColumn = column.Context; - PhAppendFormatStringBuilder( - &stringBuilder, - L"+%s+%u,,%u|", - cmColumn->Plugin->Name.Buffer, - cmColumn->SubId, - column.Width - ); - } - } - - count++; - } - - i++; - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - if (SortSettings) - { - ULONG sortColumn; - PH_SORT_ORDER sortOrder; - - if (TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder)) - { - if (sortOrder != NoSortOrder) - { - if (!Manager || sortColumn < Manager->MinId) - { - *SortSettings = PhFormatString(L"%u,%u", sortColumn, sortOrder); - } - else - { - PH_TREENEW_COLUMN column; - PPH_CM_COLUMN cmColumn; - - if (TreeNew_GetColumn(TreeNewHandle, sortColumn, &column)) - { - cmColumn = column.Context; - *SortSettings = PhFormatString(L"+%s+%u,%u", cmColumn->Plugin->Name.Buffer, cmColumn->SubId, sortOrder); - } - else - { - *SortSettings = PhReferenceEmptyString(); - } - } - } - else - { - *SortSettings = PhCreateString(L"0,0"); - } - } - else - { - *SortSettings = PhReferenceEmptyString(); - } - } - - return PhFinalStringBuilderString(&stringBuilder); -} +/* + * Process Hacker - + * tree new column manager + * + * Copyright (C) 2011-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +typedef struct _PH_CM_SORT_CONTEXT +{ + PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction; + ULONG SubId; + PVOID Context; + PPH_CM_POST_SORT_FUNCTION PostSortFunction; + PH_SORT_ORDER SortOrder; +} PH_CM_SORT_CONTEXT, *PPH_CM_SORT_CONTEXT; + +VOID PhCmInitializeManager( + _Out_ PPH_CM_MANAGER Manager, + _In_ HWND Handle, + _In_ ULONG MinId, + _In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction + ) +{ + Manager->Handle = Handle; + Manager->MinId = MinId; + Manager->NextId = MinId; + Manager->PostSortFunction = PostSortFunction; + InitializeListHead(&Manager->ColumnListHead); + Manager->NotifyList = NULL; +} + +VOID PhCmDeleteManager( + _In_ PPH_CM_MANAGER Manager + ) +{ + PLIST_ENTRY listEntry; + PPH_CM_COLUMN column; + + listEntry = Manager->ColumnListHead.Flink; + + while (listEntry != &Manager->ColumnListHead) + { + column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry); + listEntry = listEntry->Flink; + + PhFree(column); + } + + if (Manager->NotifyList) + PhDereferenceObject(Manager->NotifyList); +} + +PPH_CM_COLUMN PhCmCreateColumn( + _Inout_ PPH_CM_MANAGER Manager, + _In_ PPH_TREENEW_COLUMN Column, + _In_ struct _PH_PLUGIN *Plugin, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_ PVOID SortFunction + ) +{ + PPH_CM_COLUMN column; + PH_TREENEW_COLUMN tnColumn; + + column = PhAllocate(sizeof(PH_CM_COLUMN)); + memset(column, 0, sizeof(PH_CM_COLUMN)); + column->Id = Manager->NextId++; + column->Plugin = Plugin; + column->SubId = SubId; + column->Context = Context; + column->SortFunction = SortFunction; + InsertTailList(&Manager->ColumnListHead, &column->ListEntry); + + memset(&tnColumn, 0, sizeof(PH_TREENEW_COLUMN)); + tnColumn.Id = column->Id; + tnColumn.Context = column; + tnColumn.Visible = Column->Visible; + tnColumn.CustomDraw = Column->CustomDraw; + tnColumn.SortDescending = Column->SortDescending; + tnColumn.Text = Column->Text; + tnColumn.Width = Column->Width; + tnColumn.Alignment = Column->Alignment; + tnColumn.DisplayIndex = Column->Visible ? Column->DisplayIndex : -1; + tnColumn.TextFlags = Column->TextFlags; + TreeNew_AddColumn(Manager->Handle, &tnColumn); + + return column; +} + +PPH_CM_COLUMN PhCmFindColumn( + _In_ PPH_CM_MANAGER Manager, + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ) +{ + PLIST_ENTRY listEntry; + PPH_CM_COLUMN column; + + listEntry = Manager->ColumnListHead.Flink; + + while (listEntry != &Manager->ColumnListHead) + { + column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry); + + if (column->SubId == SubId && PhEqualStringRef(PluginName, &column->Plugin->AppContext.AppName, FALSE)) + return column; + + listEntry = listEntry->Flink; + } + + return NULL; +} + +VOID PhCmSetNotifyPlugin( + _In_ PPH_CM_MANAGER Manager, + _In_ struct _PH_PLUGIN *Plugin + ) +{ + if (!Manager->NotifyList) + { + Manager->NotifyList = PhCreateList(8); + } + else + { + if (PhFindItemList(Manager->NotifyList, Plugin) != -1) + return; + } + + PhAddItemList(Manager->NotifyList, Plugin); +} + +BOOLEAN PhCmForwardMessage( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_ PPH_CM_MANAGER Manager + ) +{ + PH_PLUGIN_TREENEW_MESSAGE pluginMessage; + PPH_PLUGIN plugin; + + if (Message == TreeNewDestroying) + return FALSE; + + switch (Message) + { + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PH_TREENEW_COLUMN tnColumn; + PPH_CM_COLUMN column; + + if (getCellText->Id < Manager->MinId) + return FALSE; + + if (!TreeNew_GetColumn(hwnd, getCellText->Id, &tnColumn)) + return FALSE; + + column = tnColumn.Context; + pluginMessage.SubId = column->SubId; + pluginMessage.Context = column->Context; + plugin = column->Plugin; + } + break; + case TreeNewCustomDraw: + { + PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; + PPH_CM_COLUMN column; + + if (customDraw->Column->Id < Manager->MinId) + return FALSE; + + column = customDraw->Column->Context; + pluginMessage.SubId = column->SubId; + pluginMessage.Context = column->Context; + plugin = column->Plugin; + } + break; + case TreeNewColumnResized: + { + PPH_TREENEW_COLUMN tlColumn = Parameter1; + PPH_CM_COLUMN column; + + if (tlColumn->Id < Manager->MinId) + return FALSE; + + column = tlColumn->Context; + pluginMessage.SubId = column->SubId; + pluginMessage.Context = column->Context; + plugin = column->Plugin; + } + break; + default: + { + // Some plugins want to be notified about all messages. + if (Manager->NotifyList) + { + ULONG i; + + for (i = 0; i < Manager->NotifyList->Count; i++) + { + plugin = Manager->NotifyList->Items[i]; + + pluginMessage.TreeNewHandle = hwnd; + pluginMessage.Message = Message; + pluginMessage.Parameter1 = Parameter1; + pluginMessage.Parameter2 = Parameter2; + pluginMessage.SubId = 0; + pluginMessage.Context = NULL; + + PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); + } + } + } + return FALSE; + } + + pluginMessage.TreeNewHandle = hwnd; + pluginMessage.Message = Message; + pluginMessage.Parameter1 = Parameter1; + pluginMessage.Parameter2 = Parameter2; + + PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackTreeNewMessage), &pluginMessage); + + return TRUE; +} + +static int __cdecl PhCmpSortFunction( + _In_ void *context, + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_CM_SORT_CONTEXT sortContext = context; + PVOID node1 = *(PVOID *)elem1; + PVOID node2 = *(PVOID *)elem2; + LONG result; + + result = sortContext->SortFunction(node1, node2, sortContext->SubId, sortContext->SortOrder, sortContext->Context); + + return sortContext->PostSortFunction(result, node1, node2, sortContext->SortOrder); +} + +BOOLEAN PhCmForwardSort( + _In_ PPH_TREENEW_NODE *Nodes, + _In_ ULONG NumberOfNodes, + _In_ ULONG SortColumn, + _In_ PH_SORT_ORDER SortOrder, + _In_ PPH_CM_MANAGER Manager + ) +{ + PH_TREENEW_COLUMN tnColumn; + PPH_CM_COLUMN column; + PH_CM_SORT_CONTEXT sortContext; + + if (SortColumn < Manager->MinId) + return FALSE; + + if (!TreeNew_GetColumn(Manager->Handle, SortColumn, &tnColumn)) + return TRUE; + + column = tnColumn.Context; + + if (!column->SortFunction) + return TRUE; + + sortContext.SortFunction = column->SortFunction; + sortContext.SubId = column->SubId; + sortContext.Context = column->Context; + sortContext.PostSortFunction = Manager->PostSortFunction; + sortContext.SortOrder = SortOrder; + qsort_s(Nodes, NumberOfNodes, sizeof(PVOID), PhCmpSortFunction, &sortContext); + + return TRUE; +} + +BOOLEAN PhCmLoadSettings( + _In_ HWND TreeNewHandle, + _In_ PPH_STRINGREF Settings + ) +{ + return PhCmLoadSettingsEx(TreeNewHandle, NULL, 0, Settings, NULL); +} + +BOOLEAN PhCmLoadSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _In_ PPH_STRINGREF Settings, + _In_opt_ PPH_STRINGREF SortSettings + ) +{ + BOOLEAN result = FALSE; + PH_STRINGREF scalePart; + PH_STRINGREF columnPart; + PH_STRINGREF remainingColumnPart; + PH_STRINGREF valuePart; + PH_STRINGREF subPart; + ULONG64 integer; + ULONG scale; + ULONG total; + BOOLEAN hasFixedColumn; + ULONG count; + ULONG i; + PPH_HASHTABLE columnHashtable; + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_KEY_VALUE_PAIR pair; + LONG orderArray[PH_CM_ORDER_LIMIT]; + LONG maxOrder; + + if (Settings->Length != 0) + { + columnHashtable = PhCreateSimpleHashtable(20); + + remainingColumnPart = *Settings; + + if (remainingColumnPart.Length != 0 && remainingColumnPart.Buffer[0] == '@') + { + PhSkipStringRef(&remainingColumnPart, sizeof(WCHAR)); + PhSplitStringRefAtChar(&remainingColumnPart, '|', &scalePart, &remainingColumnPart); + + if (scalePart.Length == 0 || !PhStringToInteger64(&scalePart, 10, &integer)) + goto CleanupExit; + + scale = (ULONG)integer; + } + else + { + scale = PhGlobalDpi; + } + + while (remainingColumnPart.Length != 0) + { + PPH_TREENEW_COLUMN column; + ULONG id; + ULONG displayIndex; + ULONG width; + + PhSplitStringRefAtChar(&remainingColumnPart, '|', &columnPart, &remainingColumnPart); + + if (columnPart.Length != 0) + { + // Id + + PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); + + if (valuePart.Length == 0) + goto CleanupExit; + + if (valuePart.Buffer[0] == '+') + { + PH_STRINGREF pluginName; + ULONG subId; + PPH_CM_COLUMN cmColumn; + + // This is a plugin-owned column. + + if (!Manager) + continue; + if (!PhEmParseCompoundId(&valuePart, &pluginName, &subId)) + continue; + + cmColumn = PhCmFindColumn(Manager, &pluginName, subId); + + if (!cmColumn) + continue; // can't find the column, skip this part + + id = cmColumn->Id; + } + else + { + if (!PhStringToInteger64(&valuePart, 10, &integer)) + goto CleanupExit; + + id = (ULONG)integer; + } + + // Display Index + + PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); + + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + if (valuePart.Length == 0 || !PhStringToInteger64(&valuePart, 10, &integer)) + goto CleanupExit; + + displayIndex = (ULONG)integer; + } + else + { + if (valuePart.Length != 0) + goto CleanupExit; + + displayIndex = -1; + } + + // Width + + if (columnPart.Length == 0 || !PhStringToInteger64(&columnPart, 10, &integer)) + goto CleanupExit; + + width = (ULONG)integer; + + if (scale != PhGlobalDpi && scale != 0) + width = PhMultiplyDivide(width, PhGlobalDpi, scale); + + column = PhAllocate(sizeof(PH_TREENEW_COLUMN)); + column->Id = id; + column->DisplayIndex = displayIndex; + column->Width = width; + PhAddItemSimpleHashtable(columnHashtable, UlongToPtr(column->Id), column); + } + } + + TreeNew_SetRedraw(TreeNewHandle, FALSE); + + // Set visibility and width. + + i = 0; + count = 0; + total = TreeNew_GetColumnCount(TreeNewHandle); + hasFixedColumn = !!TreeNew_GetFixedColumn(TreeNewHandle); + memset(orderArray, 0, sizeof(orderArray)); + maxOrder = 0; + + while (count < total) + { + PH_TREENEW_COLUMN setColumn; + PPH_TREENEW_COLUMN *columnPtr; + + if (TreeNew_GetColumn(TreeNewHandle, i, &setColumn)) + { + columnPtr = (PPH_TREENEW_COLUMN *)PhFindItemSimpleHashtable(columnHashtable, UlongToPtr(i)); + + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + if (columnPtr) + { + setColumn.Visible = TRUE; + setColumn.Width = (*columnPtr)->Width; + TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE | TN_COLUMN_WIDTH, &setColumn); + + if (!setColumn.Fixed) + { + // For compatibility reasons, normal columns have their display indicies stored + // one higher than usual (so they start from 1, not 0). Fix that here. + if (hasFixedColumn && (*columnPtr)->DisplayIndex != 0) + (*columnPtr)->DisplayIndex--; + + if ((*columnPtr)->DisplayIndex < PH_CM_ORDER_LIMIT) + { + orderArray[(*columnPtr)->DisplayIndex] = i; + + if ((ULONG)maxOrder < (*columnPtr)->DisplayIndex + 1) + maxOrder = (*columnPtr)->DisplayIndex + 1; + } + } + } + else if (!setColumn.Fixed) // never hide the fixed column + { + setColumn.Visible = FALSE; + TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &setColumn); + } + } + else + { + if (columnPtr) + { + setColumn.Width = (*columnPtr)->Width; + TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_WIDTH, &setColumn); + } + } + + count++; + } + + i++; + } + + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + // Set the order array. + TreeNew_SetColumnOrderArray(TreeNewHandle, maxOrder, orderArray); + } + + TreeNew_SetRedraw(TreeNewHandle, TRUE); + + result = TRUE; + +CleanupExit: + PhBeginEnumHashtable(columnHashtable, &enumContext); + + while (pair = PhNextEnumHashtable(&enumContext)) + PhFree(pair->Value); + + PhDereferenceObject(columnHashtable); + } + + // Load sort settings. + + if (SortSettings && SortSettings->Length != 0) + { + PhSplitStringRefAtChar(SortSettings, ',', &valuePart, &subPart); + + if (valuePart.Length != 0 && subPart.Length != 0) + { + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + sortColumn = -1; + + if (valuePart.Buffer[0] == '+') + { + PH_STRINGREF pluginName; + ULONG subId; + PPH_CM_COLUMN cmColumn; + + if ( + Manager && + PhEmParseCompoundId(&valuePart, &pluginName, &subId) && + (cmColumn = PhCmFindColumn(Manager, &pluginName, subId)) + ) + { + sortColumn = cmColumn->Id; + } + } + else + { + PhStringToInteger64(&valuePart, 10, &integer); + sortColumn = (ULONG)integer; + } + + PhStringToInteger64(&subPart, 10, &integer); + sortOrder = (PH_SORT_ORDER)integer; + + if (sortColumn != -1 && sortOrder <= DescendingSortOrder) + { + TreeNew_SetSort(TreeNewHandle, sortColumn, sortOrder); + } + } + } + + return result; +} + +PPH_STRING PhCmSaveSettings( + _In_ HWND TreeNewHandle + ) +{ + return PhCmSaveSettingsEx(TreeNewHandle, NULL, 0, NULL); +} + +PPH_STRING PhCmSaveSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _Out_opt_ PPH_STRING *SortSettings + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i = 0; + ULONG count = 0; + ULONG total; + ULONG increment; + PH_TREENEW_COLUMN column; + + total = TreeNew_GetColumnCount(TreeNewHandle); + + if (TreeNew_GetFixedColumn(TreeNewHandle)) + increment = 1; // the first normal column should have a display index that starts with 1, for compatibility + else + increment = 0; + + PhInitializeStringBuilder(&stringBuilder, 100); + + PhAppendFormatStringBuilder(&stringBuilder, L"@%lu|", PhGlobalDpi); + + while (count < total) + { + if (TreeNew_GetColumn(TreeNewHandle, i, &column)) + { + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + if (column.Visible) + { + if (!Manager || i < Manager->MinId) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu,%lu,%ld|", + i, + column.Fixed ? 0 : column.DisplayIndex + increment, + column.Width + ); + } + else + { + PPH_CM_COLUMN cmColumn; + + cmColumn = column.Context; + PhAppendFormatStringBuilder( + &stringBuilder, + L"+%s+%lu,%lu,%ld|", + cmColumn->Plugin->Name.Buffer, + cmColumn->SubId, + column.DisplayIndex + increment, + column.Width + ); + } + } + } + else + { + if (!Manager || i < Manager->MinId) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu,,%ld|", + i, + column.Width + ); + } + else + { + PPH_CM_COLUMN cmColumn; + + cmColumn = column.Context; + PhAppendFormatStringBuilder( + &stringBuilder, + L"+%s+%lu,,%ld|", + cmColumn->Plugin->Name.Buffer, + cmColumn->SubId, + column.Width + ); + } + } + + count++; + } + + i++; + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + if (SortSettings) + { + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + if (TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder)) + { + if (sortOrder != NoSortOrder) + { + if (!Manager || sortColumn < Manager->MinId) + { + *SortSettings = PhFormatString(L"%lu,%lu", sortColumn, sortOrder); + } + else + { + PH_TREENEW_COLUMN column; + PPH_CM_COLUMN cmColumn; + + if (TreeNew_GetColumn(TreeNewHandle, sortColumn, &column)) + { + cmColumn = column.Context; + *SortSettings = PhFormatString(L"+%s+%lu,%lu", cmColumn->Plugin->Name.Buffer, cmColumn->SubId, sortOrder); + } + else + { + *SortSettings = PhReferenceEmptyString(); + } + } + } + else + { + *SortSettings = PhCreateString(L"0,0"); + } + } + else + { + *SortSettings = PhReferenceEmptyString(); + } + } + + return PhFinalStringBuilderString(&stringBuilder); +} diff --git a/ProcessHacker/colsetmgr.c b/ProcessHacker/colsetmgr.c new file mode 100644 index 000000000000..2d524f8f8df4 --- /dev/null +++ b/ProcessHacker/colsetmgr.c @@ -0,0 +1,651 @@ +/* + * Process Hacker - + * tree new column set manager + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +PPH_LIST PhInitializeColumnSetList( + _In_ PWSTR SettingName + ) +{ + PPH_LIST columnSetList; + PPH_STRING settingsString; + ULONG64 count; + ULONG64 index; + PH_STRINGREF remaining; + PH_STRINGREF part; + + columnSetList = PhCreateList(10); + settingsString = PhaGetStringSetting(SettingName); + remaining = settingsString->sr; + + if (remaining.Length == 0) + goto CleanupExit; + if (!PhSplitStringRefAtChar(&remaining, '-', &part, &remaining)) + goto CleanupExit; + if (!PhStringToInteger64(&part, 10, &count)) + goto CleanupExit; + + for (index = 0; index < count; index++) + { + PH_STRINGREF columnSetNamePart; + PH_STRINGREF columnSetSettingPart; + PH_STRINGREF columnSetSortPart; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '-', &columnSetNamePart, &remaining); + PhSplitStringRefAtChar(&remaining, '-', &columnSetSettingPart, &remaining); + PhSplitStringRefAtChar(&remaining, '-', &columnSetSortPart, &remaining); + + { + PPH_COLUMN_SET_ENTRY entry; + + entry = PhAllocate(sizeof(PH_COLUMN_SET_ENTRY)); + entry->Name = PhCreateString2(&columnSetNamePart); + entry->Setting = PhCreateString2(&columnSetSettingPart); + entry->Sorting = PhCreateString2(&columnSetSortPart); + + PhAddItemList(columnSetList, entry); + } + } + +CleanupExit: + return columnSetList; +} + +VOID PhSaveSettingsColumnList( + _In_ PWSTR SettingName, + _In_ PPH_LIST ColumnSetList + ) +{ + ULONG index; + PPH_STRING settingsString; + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu-", + ColumnSetList->Count + ); + + for (index = 0; index < ColumnSetList->Count; index++) + { + PPH_COLUMN_SET_ENTRY entry = ColumnSetList->Items[index]; + + if (PhIsNullOrEmptyString(entry->Name)) + continue; + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%s-%s-%s-", + entry->Name->Buffer, + PhGetStringOrEmpty(entry->Setting), + PhGetStringOrEmpty(entry->Sorting) + ); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); + PhSetStringSetting2(SettingName, &settingsString->sr); +} + +VOID PhDeleteColumnSetList( + _In_ PPH_LIST ColumnSetList + ) +{ + for (ULONG i = 0; i < ColumnSetList->Count; i++) + { + PPH_COLUMN_SET_ENTRY entry = ColumnSetList->Items[i]; + + //PhRemoveItemList(ColumnSetList, PhFindItemList(ColumnSetList, entry)); + + PhClearReference(&entry->Name); + PhClearReference(&entry->Setting); + PhClearReference(&entry->Sorting); + + PhFree(entry); + } + + PhDereferenceObject(ColumnSetList); +} + +_Success_(return) +BOOLEAN PhLoadSettingsColumnSet( + _In_ PWSTR SettingName, + _In_ PPH_STRING ColumnSetName, + _Out_ PPH_STRING *TreeListSettings, + _Out_ PPH_STRING *TreeSortSettings + ) +{ + PPH_STRING treeSettings = NULL; + PPH_STRING sortSettings = NULL; + PPH_STRING settingsString; + ULONG64 count; + ULONG64 index; + PH_STRINGREF remaining; + PH_STRINGREF part; + + settingsString = PhaGetStringSetting(SettingName); + remaining = settingsString->sr; + + if (remaining.Length == 0) + return FALSE; + + if (!PhSplitStringRefAtChar(&remaining, '-', &part, &remaining)) + return FALSE; + if (!PhStringToInteger64(&part, 10, &count)) + return FALSE; + + for (index = 0; index < count; index++) + { + PH_STRINGREF columnSetNamePart; + PH_STRINGREF columnSetSettingPart; + PH_STRINGREF columnSetSortPart; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '-', &columnSetNamePart, &remaining); + PhSplitStringRefAtChar(&remaining, '-', &columnSetSettingPart, &remaining); + PhSplitStringRefAtChar(&remaining, '-', &columnSetSortPart, &remaining); + + if (PhEqualStringRef(&columnSetNamePart, &ColumnSetName->sr, FALSE)) + { + treeSettings = PhCreateString2(&columnSetSettingPart); + sortSettings = PhCreateString2(&columnSetSortPart); + break; + } + } + + if (!PhIsNullOrEmptyString(treeSettings) && !PhIsNullOrEmptyString(sortSettings)) + { + *TreeListSettings = treeSettings; + *TreeSortSettings = sortSettings; + return TRUE; + } + else + { + if (treeSettings) + PhDereferenceObject(treeSettings); + if (sortSettings) + PhDereferenceObject(sortSettings); + return FALSE; + } +} + +VOID PhSaveSettingsColumnSet( + _In_ PWSTR SettingName, + _In_ PPH_STRING ColumnSetName, + _In_ PPH_STRING TreeListSettings, + _In_ PPH_STRING TreeSortSettings + ) +{ + ULONG index; + BOOLEAN found = FALSE; + PPH_LIST columnSetList; + + columnSetList = PhInitializeColumnSetList(SettingName); + + for (index = 0; index < columnSetList->Count; index++) + { + PPH_COLUMN_SET_ENTRY entry = columnSetList->Items[index]; + + if (PhEqualString(entry->Name, ColumnSetName, FALSE)) + { + PhReferenceObject(TreeListSettings); + PhReferenceObject(TreeSortSettings); + + PhMoveReference(&entry->Setting, TreeListSettings); + PhMoveReference(&entry->Sorting, TreeSortSettings); + + found = TRUE; + break; + } + } + + if (!found) + { + PPH_COLUMN_SET_ENTRY entry; + + PhReferenceObject(ColumnSetName); + PhReferenceObject(TreeListSettings); + PhReferenceObject(TreeSortSettings); + + entry = PhAllocate(sizeof(PH_COLUMN_SET_ENTRY)); + entry->Name = ColumnSetName; + entry->Setting = TreeListSettings; + entry->Sorting = TreeSortSettings; + + PhAddItemList(columnSetList, entry); + } + + PhSaveSettingsColumnList(SettingName, columnSetList); + + PhDeleteColumnSetList(columnSetList); +} + +// Column Set Editor Dialog + +typedef struct _COLUMNSET_DIALOG_CONTEXT +{ + HWND DialogHandle; + HWND ListViewHandle; + HWND RenameButtonHandle; + HWND MoveUpButtonHandle; + HWND MoveDownButtonHandle; + HWND RemoveButtonHandle; + PPH_STRING SettingName; + PPH_LIST ColumnSetList; + BOOLEAN LabelEditActive; +} COLUMNSET_DIALOG_CONTEXT, *PCOLUMNSET_DIALOG_CONTEXT; + +INT_PTR CALLBACK PhpColumnSetEditorDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowColumnSetEditorDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR SettingName + ) +{ + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_COLUMNSETS), + ParentWindowHandle, + PhpColumnSetEditorDlgProc, + (LPARAM)SettingName + ); +} + +VOID PhpMoveListViewItem( + _In_ HWND ListViewHandle, + _In_ INT ItemIndex1, + _In_ INT ItemIndex2 + ) +{ + LVITEM item1; + LVITEM item2; + WCHAR buffer1[MAX_PATH]; + WCHAR buffer2[MAX_PATH]; + + item1.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + item1.stateMask = (UINT)-1; + item1.iItem = ItemIndex1; + item1.iSubItem = 0; + item1.cchTextMax = sizeof(buffer1); + item1.pszText = buffer1; + + item2.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; + item2.stateMask = (UINT)-1; + item2.iItem = ItemIndex2; + item2.iSubItem = 0; + item2.cchTextMax = sizeof(buffer2); + item2.pszText = buffer2; + + if ( + ListView_GetItem(ListViewHandle, &item1) && + ListView_GetItem(ListViewHandle, &item2) + ) + { + item1.iItem = ItemIndex2; + item2.iItem = ItemIndex1; + + ListView_SetItem(ListViewHandle, &item1); + ListView_SetItem(ListViewHandle, &item2); + } +} + +VOID PhpMoveSelectedListViewItemUp( + _In_ HWND ListViewHandle + ) +{ + INT lvItemIndex; + + lvItemIndex = ListView_GetNextItem(ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1) + { + PhpMoveListViewItem(ListViewHandle, lvItemIndex, lvItemIndex - 1); + } + + SetFocus(ListViewHandle); + ListView_SetItemState(ListViewHandle, lvItemIndex - 1, LVNI_SELECTED, LVNI_SELECTED); + //ListView_EnsureVisible(ListViewHandle, lvItemIndex - 1, FALSE); +} + +VOID PhpMoveSelectedListViewItemDown( + _In_ HWND ListViewHandle + ) +{ + INT lvItemIndex; + + lvItemIndex = ListView_GetNextItem(ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1) + { + PhpMoveListViewItem(ListViewHandle, lvItemIndex, lvItemIndex + 1); + } + + SetFocus(ListViewHandle); + ListView_SetItemState(ListViewHandle, lvItemIndex + 1, LVNI_SELECTED, LVNI_SELECTED); + //ListView_EnsureVisible(ListViewHandle, lvItemIndex + 1, FALSE); +} + +INT_PTR CALLBACK PhpColumnSetEditorDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCOLUMNSET_DIALOG_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(COLUMNSET_DIALOG_CONTEXT)); + memset(context, 0, sizeof(COLUMNSET_DIALOG_CONTEXT)); + + context->SettingName = PhCreateString((PWSTR)lParam); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + context->DialogHandle = hwndDlg; + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_COLUMNSETLIST); + context->RenameButtonHandle = GetDlgItem(hwndDlg, IDC_RENAME); + context->MoveUpButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEUP); + context->MoveDownButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN); + context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 250, L"Name"); + PhSetExtendedListView(context->ListViewHandle); + + context->ColumnSetList = PhInitializeColumnSetList(PhGetString(context->SettingName)); + + for (ULONG i = 0; i < context->ColumnSetList->Count; i++) + { + PPH_COLUMN_SET_ENTRY entry = context->ColumnSetList->Items[i]; + + PhAddListViewItem(context->ListViewHandle, MAXINT, entry->Name->Buffer, entry); + } + + Button_Enable(context->RenameButtonHandle, FALSE); + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + Button_Enable(context->RemoveButtonHandle, FALSE); + } + break; + case WM_DESTROY: + { + PhDeleteColumnSetList(context->ColumnSetList); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + if (context->LabelEditActive) + break; + + PhSaveSettingsColumnList(PhGetString(context->SettingName), context->ColumnSetList); + + EndDialog(hwndDlg, IDOK); + } + break; + case IDC_RENAME: + { + INT lvItemIndex; + + lvItemIndex = ListView_GetNextItem(context->ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1) + { + SetFocus(context->ListViewHandle); + ListView_EditLabel(context->ListViewHandle, lvItemIndex); + } + } + break; + case IDC_MOVEUP: + { + INT lvItemIndex; + PPH_COLUMN_SET_ENTRY entry; + ULONG index; + + PhpMoveSelectedListViewItemUp(context->ListViewHandle); + + lvItemIndex = ListView_GetNextItem(context->ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(context->ListViewHandle, lvItemIndex, (PVOID *)&entry)) + { + index = PhFindItemList(context->ColumnSetList, entry); + + if (index != -1) + { + PhRemoveItemList(context->ColumnSetList, index); + PhInsertItemList(context->ColumnSetList, lvItemIndex, entry); + } + } + } + break; + case IDC_MOVEDOWN: + { + INT lvItemIndex; + PPH_COLUMN_SET_ENTRY entry; + ULONG index; + + PhpMoveSelectedListViewItemDown(context->ListViewHandle); + + lvItemIndex = ListView_GetNextItem(context->ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(context->ListViewHandle, lvItemIndex, (PVOID *)&entry)) + { + index = PhFindItemList(context->ColumnSetList, entry); + + if (index != -1) + { + PhRemoveItemList(context->ColumnSetList, index); + PhInsertItemList(context->ColumnSetList, lvItemIndex, entry); + } + } + } + break; + case IDC_REMOVE: + { + INT lvItemIndex; + PPH_COLUMN_SET_ENTRY entry; + ULONG index; + + lvItemIndex = ListView_GetNextItem(context->ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(context->ListViewHandle, lvItemIndex, (PVOID *)&entry)) + { + index = PhFindItemList(context->ColumnSetList, entry); + + if (index != -1) + { + PhRemoveItemList(context->ColumnSetList, index); + PhRemoveListViewItem(context->ListViewHandle, lvItemIndex); + + PhClearReference(&entry->Name); + PhClearReference(&entry->Setting); + PhClearReference(&entry->Sorting); + PhFree(entry); + } + + SetFocus(context->ListViewHandle); + ListView_SetItemState(context->ListViewHandle, 0, LVNI_SELECTED, LVNI_SELECTED); + //ListView_EnsureVisible(context->ListViewHandle, 0, FALSE); + } + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_DBLCLK: + { + INT lvItemIndex; + + lvItemIndex = ListView_GetNextItem(context->ListViewHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1) + { + SetFocus(context->ListViewHandle); + ListView_EditLabel(context->ListViewHandle, lvItemIndex); + } + } + break; + case LVN_ITEMCHANGED: + { + LPNMLISTVIEW listview = (LPNMLISTVIEW)lParam; + INT index; + INT lvItemIndex; + INT count; + + index = listview->iItem; + lvItemIndex = ListView_GetNextItem(context->ListViewHandle, -1, LVNI_SELECTED); + count = ListView_GetItemCount(context->ListViewHandle); + + if (count == 0 || index == -1 || lvItemIndex == -1) + { + Button_Enable(context->RenameButtonHandle, FALSE); + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + Button_Enable(context->RemoveButtonHandle, FALSE); + break; + } + + if (index != lvItemIndex) + break; + + if (index == 0 && count == 1) + { + // First and last item + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == (count - 1)) + { + // Last item + Button_Enable(context->MoveUpButtonHandle, TRUE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == 0) + { + // First item + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, TRUE); + } + else + { + Button_Enable(context->MoveUpButtonHandle, TRUE); + Button_Enable(context->MoveDownButtonHandle, TRUE); + } + + Button_Enable(context->RenameButtonHandle, TRUE); + Button_Enable(context->RemoveButtonHandle, TRUE); + } + break; + case LVN_BEGINLABELEDIT: + context->LabelEditActive = TRUE; + break; + case LVN_ENDLABELEDIT: + { + LV_DISPINFO* lvinfo = (LV_DISPINFO*)lParam; + + if (lvinfo->item.iItem != -1 && lvinfo->item.pszText) + { + BOOLEAN found = FALSE; + PPH_COLUMN_SET_ENTRY entry; + ULONG index; + + for (ULONG i = 0; i < context->ColumnSetList->Count; i++) + { + entry = context->ColumnSetList->Items[i]; + + if (PhEqualStringRef2(&entry->Name->sr, lvinfo->item.pszText, FALSE)) + { + found = TRUE; + break; + } + } + + if (!found && PhGetListViewItemParam(context->ListViewHandle, lvinfo->item.iItem, (PVOID *)&entry)) + { + index = PhFindItemList(context->ColumnSetList, entry); + + if (index != -1) + { + PhMoveReference(&entry->Name, PhCreateString(lvinfo->item.pszText)); + ListView_SetItemText(context->ListViewHandle, lvinfo->item.iItem, 0, lvinfo->item.pszText); + } + } + } + + context->LabelEditActive = FALSE; + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/dbgcon.c b/ProcessHacker/dbgcon.c index d327bb58c58b..c1e742071539 100644 --- a/ProcessHacker/dbgcon.c +++ b/ProcessHacker/dbgcon.c @@ -1,1695 +1,1721 @@ -/* - * Process Hacker - - * debug console - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This is a simple debugging console which is able to explore phlib's - * systems easily. Commands are provided to debug reference counting - * problems and memory usage, as well as to do general performance testing. - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -typedef struct _STRING_TABLE_ENTRY -{ - PPH_STRING String; - ULONG_PTR Count; -} STRING_TABLE_ENTRY, *PSTRING_TABLE_ENTRY; - -BOOL ConsoleHandlerRoutine( - _In_ DWORD dwCtrlType - ); - -VOID PhpPrintHashtableStatistics( - _In_ PPH_HASHTABLE Hashtable - ); - -NTSTATUS PhpDebugConsoleThreadStart( - _In_ PVOID Parameter - ); - -extern PH_FREE_LIST PhObjectSmallFreeList; - -static HANDLE DebugConsoleThreadHandle; -static PPH_SYMBOL_PROVIDER DebugConsoleSymbolProvider; - -static PPH_HASHTABLE ObjectListSnapshot = NULL; -#ifdef DEBUG -static PPH_LIST NewObjectList = NULL; -static PH_QUEUED_LOCK NewObjectListLock; -#endif - -static BOOLEAN ShowAllLeaks = FALSE; -static BOOLEAN InLeakDetection = FALSE; -static ULONG NumberOfLeaks; -static ULONG NumberOfLeaksShown; - -VOID PhShowDebugConsole( - VOID - ) -{ - if (AllocConsole()) - { - HMENU menu; - - // Disable the close button because it's impossible to handle - // those events. - menu = GetSystemMenu(GetConsoleWindow(), FALSE); - EnableMenuItem(menu, SC_CLOSE, MF_GRAYED | MF_DISABLED); - DeleteMenu(menu, SC_CLOSE, 0); - - // Set a handler so we can catch Ctrl+C and Ctrl+Break. - SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE); - - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - freopen("CONIN$", "r", stdin); - DebugConsoleThreadHandle = PhCreateThread(0, PhpDebugConsoleThreadStart, NULL); - } - else - { - HWND consoleWindow; - - consoleWindow = GetConsoleWindow(); - - // Console window already exists, so bring it to the top. - if (IsIconic(consoleWindow)) - ShowWindow(consoleWindow, SW_RESTORE); - else - BringWindowToTop(consoleWindow); - - return; - } -} - -VOID PhCloseDebugConsole( - VOID - ) -{ - freopen("NUL", "w", stdout); - freopen("NUL", "w", stderr); - freopen("NUL", "r", stdin); - - FreeConsole(); -} - -static BOOL ConsoleHandlerRoutine( - _In_ DWORD dwCtrlType - ) -{ - switch (dwCtrlType) - { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - case CTRL_CLOSE_EVENT: - PhCloseDebugConsole(); - return TRUE; - } - - return FALSE; -} - -static BOOLEAN NTAPI PhpLoadCurrentProcessSymbolsCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PhLoadModuleSymbolProvider((PPH_SYMBOL_PROVIDER)Context, Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, Module->Size); - - return TRUE; -} - -static PWSTR PhpGetSymbolForAddress( - _In_ PVOID Address - ) -{ - return PH_AUTO_T(PH_STRING, PhGetSymbolFromAddress( - DebugConsoleSymbolProvider, (ULONG64)Address, NULL, NULL, NULL, NULL - ))->Buffer; -} - -static VOID PhpPrintObjectInfo( - _In_ PPH_OBJECT_HEADER ObjectHeader, - _In_ LONG RefToSubtract - ) -{ - PVOID object; - PPH_OBJECT_TYPE objectType; - WCHAR c = ' '; - - object = PhObjectHeaderToObject(ObjectHeader); - wprintf(L"%Ix", (ULONG_PTR)object); - objectType = PhGetObjectType(object); - - wprintf(L"\t% 20s", objectType->Name); - - if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST) - c = 'f'; - else if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST) - c = 'F'; - - wprintf(L"\t%4d %c", ObjectHeader->RefCount - RefToSubtract, c); - - if (!objectType) - { - // Dummy - } - else if (objectType == PhObjectTypeObject) - { - wprintf(L"\t%.32s", ((PPH_OBJECT_TYPE)object)->Name); - } - else if (objectType == PhStringType) - { - wprintf(L"\t%.32s", ((PPH_STRING)object)->Buffer); - } - else if (objectType == PhBytesType) - { - wprintf(L"\t%.32S", ((PPH_BYTES)object)->Buffer); - } - else if (objectType == PhListType) - { - wprintf(L"\tCount: %u", ((PPH_LIST)object)->Count); - } - else if (objectType == PhPointerListType) - { - wprintf(L"\tCount: %u", ((PPH_POINTER_LIST)object)->Count); - } - else if (objectType == PhHashtableType) - { - wprintf(L"\tCount: %u", ((PPH_HASHTABLE)object)->Count); - } - else if (objectType == PhProcessItemType) - { - wprintf( - L"\t%.28s (%d)", - ((PPH_PROCESS_ITEM)object)->ProcessName->Buffer, - HandleToLong(((PPH_PROCESS_ITEM)object)->ProcessId) - ); - } - else if (objectType == PhServiceItemType) - { - wprintf(L"\t%s", ((PPH_SERVICE_ITEM)object)->Name->Buffer); - } - else if (objectType == PhThreadItemType) - { - wprintf(L"\tTID: %u", HandleToUlong(((PPH_THREAD_ITEM)object)->ThreadId)); - } - - wprintf(L"\n"); -} - -static VOID PhpDumpObjectInfo( - _In_ PPH_OBJECT_HEADER ObjectHeader - ) -{ - PVOID object; - PPH_OBJECT_TYPE objectType; - - object = PhObjectHeaderToObject(ObjectHeader); - objectType = PhGetObjectType(object); - - __try - { - wprintf(L"Type: %s\n", objectType->Name); - wprintf(L"Reference count: %d\n", ObjectHeader->RefCount); - wprintf(L"Flags: %x\n", ObjectHeader->Flags); - - if (objectType == PhObjectTypeObject) - { - wprintf(L"Name: %s\n", ((PPH_OBJECT_TYPE)object)->Name); - wprintf(L"Number of objects: %u\n", ((PPH_OBJECT_TYPE)object)->NumberOfObjects); - wprintf(L"Flags: %u\n", ((PPH_OBJECT_TYPE)object)->Flags); - wprintf(L"Type index: %u\n", ((PPH_OBJECT_TYPE)object)->TypeIndex); - wprintf(L"Free list count: %u\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count); - } - else if (objectType == PhStringType) - { - wprintf(L"%s\n", ((PPH_STRING)object)->Buffer); - } - else if (objectType == PhBytesType) - { - wprintf(L"%S\n", ((PPH_BYTES)object)->Buffer); - } - else if (objectType == PhHashtableType) - { - PhpPrintHashtableStatistics((PPH_HASHTABLE)object); - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - wprintf(L"Error.\n"); - } -} - -static VOID PhpPrintHashtableStatistics( - _In_ PPH_HASHTABLE Hashtable - ) -{ - ULONG i; - ULONG expectedLookupMisses = 0; - - wprintf(L"Count: %u\n", Hashtable->Count); - wprintf(L"Allocated buckets: %u\n", Hashtable->AllocatedBuckets); - wprintf(L"Allocated entries: %u\n", Hashtable->AllocatedEntries); - wprintf(L"Next free entry: %d\n", Hashtable->FreeEntry); - wprintf(L"Next usable entry: %d\n", Hashtable->NextEntry); - - wprintf(L"Equal function: %s\n", PhpGetSymbolForAddress(Hashtable->EqualFunction)); - wprintf(L"Hash function: %s\n", PhpGetSymbolForAddress(Hashtable->HashFunction)); - - wprintf(L"\nBuckets:\n"); - - for (i = 0; i < Hashtable->AllocatedBuckets; i++) - { - ULONG index; - ULONG count = 0; - - // Count the number of entries in this bucket. - - index = Hashtable->Buckets[i]; - - while (index != -1) - { - index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next; - count++; - } - - if (count != 0) - { - expectedLookupMisses += count - 1; - } - - if (count != 0) - { - wprintf(L"%lu: ", i); - - // Print out the entry indicies. - - index = Hashtable->Buckets[i]; - - while (index != -1) - { - wprintf(L"%lu", index); - - index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next; - count--; - - if (count != 0) - wprintf(L", "); - } - - wprintf(L"\n"); - } - else - { - //wprintf(L"%u: (empty)\n"); - } - } - - wprintf(L"\nExpected lookup misses: %lu\n", expectedLookupMisses); -} - -#ifdef DEBUG -static VOID PhpDebugCreateObjectHook( - _In_ PVOID Object, - _In_ SIZE_T Size, - _In_ ULONG Flags, - _In_ PPH_OBJECT_TYPE ObjectType - ) -{ - PhAcquireQueuedLockExclusive(&NewObjectListLock); - - if (NewObjectList) - { - PhReferenceObject(Object); - PhAddItemList(NewObjectList, Object); - } - - PhReleaseQueuedLockExclusive(&NewObjectListLock); -} -#endif - -#ifdef DEBUG -static VOID PhpDeleteNewObjectList( - VOID - ) -{ - if (NewObjectList) - { - ULONG i; - - for (i = 0; i < NewObjectList->Count; i++) - PhDereferenceObject(NewObjectList->Items[i]); - - PhDereferenceObject(NewObjectList); - NewObjectList = NULL; - } -} -#endif - -static BOOLEAN PhpStringHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PSTRING_TABLE_ENTRY entry1 = Entry1; - PSTRING_TABLE_ENTRY entry2 = Entry2; - - return PhEqualString(entry1->String, entry2->String, FALSE); -} - -static ULONG PhpStringHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PSTRING_TABLE_ENTRY entry = Entry; - - return PhHashBytes((PUCHAR)entry->String->Buffer, entry->String->Length); -} - -static int __cdecl PhpStringEntryCompareByCount( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PSTRING_TABLE_ENTRY entry1 = *(PSTRING_TABLE_ENTRY *)elem1; - PSTRING_TABLE_ENTRY entry2 = *(PSTRING_TABLE_ENTRY *)elem2; - - return uintptrcmp(entry2->Count, entry1->Count); -} - -static NTSTATUS PhpLeakEnumerationRoutine( - _In_ LONG Reserved, - _In_ PVOID HeapHandle, - _In_ PVOID BaseAddress, - _In_ SIZE_T BlockSize, - _In_ ULONG StackTraceDepth, - _In_ PVOID *StackTrace - ) -{ - ULONG i; - - if (!InLeakDetection) - return 0; - - if (!HeapHandle) // means no more entries - return 0; - - if (ShowAllLeaks || HeapHandle == PhHeapHandle) - { - wprintf(L"Leak at 0x%Ix (%Iu bytes). Stack trace:\n", (ULONG_PTR)BaseAddress, BlockSize); - - for (i = 0; i < StackTraceDepth; i++) - { - PPH_STRING symbol; - - symbol = PhGetSymbolFromAddress(DebugConsoleSymbolProvider, (ULONG64)StackTrace[i], NULL, NULL, NULL, NULL); - - if (symbol) - wprintf(L"\t%s\n", symbol->Buffer); - else - wprintf(L"\t?\n"); - - PhDereferenceObject(symbol); - } - - NumberOfLeaksShown++; - } - - NumberOfLeaks++; - - return 0; -} - -typedef struct _STOPWATCH -{ - LARGE_INTEGER StartCounter; - LARGE_INTEGER EndCounter; - LARGE_INTEGER Frequency; -} STOPWATCH, *PSTOPWATCH; - -static VOID PhInitializeStopwatch( - _Out_ PSTOPWATCH Stopwatch - ) -{ - Stopwatch->StartCounter.QuadPart = 0; - Stopwatch->EndCounter.QuadPart = 0; -} - -static VOID PhStartStopwatch( - _Inout_ PSTOPWATCH Stopwatch - ) -{ - NtQueryPerformanceCounter(&Stopwatch->StartCounter, &Stopwatch->Frequency); -} - -static VOID PhStopStopwatch( - _Inout_ PSTOPWATCH Stopwatch - ) -{ - NtQueryPerformanceCounter(&Stopwatch->EndCounter, NULL); -} - -static ULONG PhGetMillisecondsStopwatch( - _In_ PSTOPWATCH Stopwatch - ) -{ - LARGE_INTEGER countsPerMs; - - countsPerMs = Stopwatch->Frequency; - countsPerMs.QuadPart /= 1000; - - return (ULONG)((Stopwatch->EndCounter.QuadPart - Stopwatch->StartCounter.QuadPart) / - countsPerMs.QuadPart); -} - -typedef VOID (FASTCALL *PPHF_RW_LOCK_FUNCTION)( - _In_ PVOID Parameter - ); - -typedef struct _RW_TEST_CONTEXT -{ - PWSTR Name; - - PPHF_RW_LOCK_FUNCTION AcquireExclusive; - PPHF_RW_LOCK_FUNCTION AcquireShared; - PPHF_RW_LOCK_FUNCTION ReleaseExclusive; - PPHF_RW_LOCK_FUNCTION ReleaseShared; - - PVOID Parameter; -} RW_TEST_CONTEXT, *PRW_TEST_CONTEXT; - -static PH_BARRIER RwStartBarrier; -static LONG RwReadersActive; -static LONG RwWritersActive; - -static NTSTATUS PhpRwLockTestThreadStart( - _In_ PVOID Parameter - ) -{ -#define RW_ITERS 10000 -#define RW_READ_ITERS 100 -#define RW_WRITE_ITERS 10 -#define RW_READ_SPIN_ITERS 60 -#define RW_WRITE_SPIN_ITERS 200 - - RW_TEST_CONTEXT context = *(PRW_TEST_CONTEXT)Parameter; - ULONG i; - ULONG j; - ULONG k; - ULONG m; - - PhWaitForBarrier(&RwStartBarrier, FALSE); - - for (i = 0; i < RW_ITERS; i++) - { - for (j = 0; j < RW_READ_ITERS; j++) - { - // Read zone - - context.AcquireShared(context.Parameter); - _InterlockedIncrement(&RwReadersActive); - - for (m = 0; m < RW_READ_SPIN_ITERS; m++) - YieldProcessor(); - - if (RwWritersActive != 0) - { - wprintf(L"[fail]: writers active in read zone!\n"); - NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL); - } - - _InterlockedDecrement(&RwReadersActive); - context.ReleaseShared(context.Parameter); - - // Spin for a while - - for (m = 0; m < 10; m++) - YieldProcessor(); - - if (j == RW_READ_ITERS / 2) - { - // Write zone - - for (k = 0; k < RW_WRITE_ITERS; k++) - { - context.AcquireExclusive(context.Parameter); - _InterlockedIncrement(&RwWritersActive); - - for (m = 0; m < RW_WRITE_SPIN_ITERS; m++) - YieldProcessor(); - - if (RwReadersActive != 0) - { - wprintf(L"[fail]: readers active in write zone!\n"); - NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL); - } - - _InterlockedDecrement(&RwWritersActive); - context.ReleaseExclusive(context.Parameter); - } - } - } - } - - return STATUS_SUCCESS; -} - -static VOID PhpTestRwLock( - _In_ PRW_TEST_CONTEXT Context - ) -{ -#define RW_PROCESSORS 4 - - STOPWATCH stopwatch; - ULONG i; - HANDLE threadHandles[RW_PROCESSORS]; - - // Dummy - - Context->AcquireExclusive(Context->Parameter); - Context->ReleaseExclusive(Context->Parameter); - Context->AcquireShared(Context->Parameter); - Context->ReleaseShared(Context->Parameter); - - // Null test - - PhStartStopwatch(&stopwatch); - - for (i = 0; i < 2000000; i++) - { - Context->AcquireExclusive(Context->Parameter); - Context->ReleaseExclusive(Context->Parameter); - Context->AcquireShared(Context->Parameter); - Context->ReleaseShared(Context->Parameter); - } - - PhStopStopwatch(&stopwatch); - - wprintf(L"[null] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch)); - - // Stress test - - PhInitializeBarrier(&RwStartBarrier, RW_PROCESSORS + 1); - RwReadersActive = 0; - RwWritersActive = 0; - - for (i = 0; i < RW_PROCESSORS; i++) - { - threadHandles[i] = PhCreateThread(0, PhpRwLockTestThreadStart, Context); - } - - PhWaitForBarrier(&RwStartBarrier, FALSE); - PhStartStopwatch(&stopwatch); - NtWaitForMultipleObjects(RW_PROCESSORS, threadHandles, WaitAll, FALSE, NULL); - PhStopStopwatch(&stopwatch); - - for (i = 0; i < RW_PROCESSORS; i++) - NtClose(threadHandles[i]); - - wprintf(L"[strs] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch)); -} - -VOID FASTCALL PhfAcquireCriticalSection( - _In_ PRTL_CRITICAL_SECTION CriticalSection - ) -{ - RtlEnterCriticalSection(CriticalSection); -} - -VOID FASTCALL PhfReleaseCriticalSection( - _In_ PRTL_CRITICAL_SECTION CriticalSection - ) -{ - RtlLeaveCriticalSection(CriticalSection); -} - -VOID FASTCALL PhfReleaseQueuedLockExclusiveUsingInline( - _In_ PPH_QUEUED_LOCK QueuedLock - ) -{ - PhReleaseQueuedLockExclusive(QueuedLock); -} - -NTSTATUS PhpDebugConsoleThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - BOOLEAN exit = FALSE; - - PhInitializeAutoPool(&autoPool); - - DebugConsoleSymbolProvider = PhCreateSymbolProvider(NtCurrentProcessId()); - - { - WCHAR buffer[512]; - UNICODE_STRING name = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH"); - UNICODE_STRING var; - PPH_STRING newSearchPath; - - var.Buffer = buffer; - var.MaximumLength = sizeof(buffer); - - if (!NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &name, &var))) - buffer[0] = 0; - - newSearchPath = PhFormatString(L"%s;%s", buffer, PhApplicationDirectory->Buffer); - PhSetSearchPathSymbolProvider(DebugConsoleSymbolProvider, newSearchPath->Buffer); - PhDereferenceObject(newSearchPath); - } - - PhEnumGenericModules(NtCurrentProcessId(), NtCurrentProcess(), - 0, PhpLoadCurrentProcessSymbolsCallback, DebugConsoleSymbolProvider); - -#ifdef DEBUG - PhInitializeQueuedLock(&NewObjectListLock); - PhDbgCreateObjectHook = PhpDebugCreateObjectHook; -#endif - - wprintf(L"Press Ctrl+C or type \"exit\" to close the debug console. Type \"help\" for a list of commands.\n"); - - while (!exit) - { - static PWSTR delims = L" \t"; - static PWSTR commandDebugOnly = L"This command is not available on non-debug builds.\n"; - - WCHAR line[201]; - ULONG inputLength; - PWSTR context; - PWSTR command; - - wprintf(L"dbg>"); - - if (!fgetws(line, sizeof(line) / 2 - 1, stdin)) - break; - - // Remove the terminating new line character. - - inputLength = (ULONG)PhCountStringZ(line); - - if (inputLength != 0) - line[inputLength - 1] = 0; - - context = NULL; - command = wcstok_s(line, delims, &context); - - if (!command) - { - continue; - } - else if (PhEqualStringZ(command, L"help", TRUE)) - { - wprintf( - L"Commands:\n" - L"exit\n" - L"testperf\n" - L"testlocks\n" - L"stats\n" - L"objects [type-name-filter]\n" - L"objtrace object-address\n" - L"objmksnap\n" - L"objcmpsnap\n" - L"objmknew\n" - L"objdelnew\n" - L"objviewnew\n" - L"dumpobj\n" - L"dumpautopool\n" - L"threads\n" - L"provthreads\n" - L"workqueues\n" - L"procrecords\n" - L"procitem\n" - L"uniquestr\n" - L"enableleakdetect\n" - L"leakdetect\n" - L"mem\n" - ); - } - else if (PhEqualStringZ(command, L"exit", TRUE)) - { - PhCloseDebugConsole(); - exit = TRUE; - } - else if (PhEqualStringZ(command, L"testperf", TRUE)) - { - STOPWATCH stopwatch; - ULONG i; - PPH_STRING testString; - RTL_CRITICAL_SECTION testCriticalSection; - PH_FAST_LOCK testFastLock; - PH_QUEUED_LOCK testQueuedLock; - - // Control (string reference counting) - - testString = PhCreateString(L""); - PhReferenceObject(testString); - PhDereferenceObject(testString); - PhStartStopwatch(&stopwatch); - - for (i = 0; i < 10000000; i++) - { - PhReferenceObject(testString); - PhDereferenceObject(testString); - } - - PhStopStopwatch(&stopwatch); - PhDereferenceObject(testString); - - wprintf(L"Referencing: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); - - // Critical section - - RtlInitializeCriticalSection(&testCriticalSection); - RtlEnterCriticalSection(&testCriticalSection); - RtlLeaveCriticalSection(&testCriticalSection); - PhStartStopwatch(&stopwatch); - - for (i = 0; i < 10000000; i++) - { - RtlEnterCriticalSection(&testCriticalSection); - RtlLeaveCriticalSection(&testCriticalSection); - } - - PhStopStopwatch(&stopwatch); - RtlDeleteCriticalSection(&testCriticalSection); - - wprintf(L"Critical section: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); - - // Fast lock - - PhInitializeFastLock(&testFastLock); - PhAcquireFastLockExclusive(&testFastLock); - PhReleaseFastLockExclusive(&testFastLock); - PhStartStopwatch(&stopwatch); - - for (i = 0; i < 10000000; i++) - { - PhAcquireFastLockExclusive(&testFastLock); - PhReleaseFastLockExclusive(&testFastLock); - } - - PhStopStopwatch(&stopwatch); - PhDeleteFastLock(&testFastLock); - - wprintf(L"Fast lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); - - // Queued lock - - PhInitializeQueuedLock(&testQueuedLock); - PhAcquireQueuedLockExclusive(&testQueuedLock); - PhReleaseQueuedLockExclusive(&testQueuedLock); - PhStartStopwatch(&stopwatch); - - for (i = 0; i < 10000000; i++) - { - PhAcquireQueuedLockExclusive(&testQueuedLock); - PhReleaseQueuedLockExclusive(&testQueuedLock); - } - - PhStopStopwatch(&stopwatch); - - wprintf(L"Queued lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); - } - else if (PhEqualStringZ(command, L"testlocks", TRUE)) - { - RW_TEST_CONTEXT testContext; - PH_FAST_LOCK fastLock; - PH_QUEUED_LOCK queuedLock; - RTL_CRITICAL_SECTION criticalSection; - - testContext.Name = L"FastLock"; - testContext.AcquireExclusive = PhfAcquireFastLockExclusive; - testContext.AcquireShared = PhfAcquireFastLockShared; - testContext.ReleaseExclusive = PhfReleaseFastLockExclusive; - testContext.ReleaseShared = PhfReleaseFastLockShared; - testContext.Parameter = &fastLock; - PhInitializeFastLock(&fastLock); - PhpTestRwLock(&testContext); - PhDeleteFastLock(&fastLock); - - testContext.Name = L"QueuedLock"; - testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive; - testContext.AcquireShared = PhfAcquireQueuedLockShared; - testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive; - testContext.ReleaseShared = PhfReleaseQueuedLockShared; - testContext.Parameter = &queuedLock; - PhInitializeQueuedLock(&queuedLock); - PhpTestRwLock(&testContext); - - testContext.Name = L"CriticalSection"; - testContext.AcquireExclusive = PhfAcquireCriticalSection; - testContext.AcquireShared = PhfAcquireCriticalSection; - testContext.ReleaseExclusive = PhfReleaseCriticalSection; - testContext.ReleaseShared = PhfReleaseCriticalSection; - testContext.Parameter = &criticalSection; - RtlInitializeCriticalSection(&criticalSection); - PhpTestRwLock(&testContext); - RtlDeleteCriticalSection(&criticalSection); - - testContext.Name = L"QueuedLockMutex"; - testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive; - testContext.AcquireShared = PhfAcquireQueuedLockExclusive; - testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive; - testContext.ReleaseShared = PhfReleaseQueuedLockExclusive; - testContext.Parameter = &queuedLock; - PhInitializeQueuedLock(&queuedLock); - PhpTestRwLock(&testContext); - } - else if (PhEqualStringZ(command, L"stats", TRUE)) - { -#ifdef DEBUG - wprintf(L"Object small free list count: %u\n", PhObjectSmallFreeList.Count); - wprintf(L"Statistics:\n"); -#define PRINT_STATISTIC(Name) wprintf(L#Name L": %u\n", PhLibStatisticsBlock.Name); - - PRINT_STATISTIC(BaseThreadsCreated); - PRINT_STATISTIC(BaseThreadsCreateFailed); - PRINT_STATISTIC(BaseStringBuildersCreated); - PRINT_STATISTIC(BaseStringBuildersResized); - PRINT_STATISTIC(RefObjectsCreated); - PRINT_STATISTIC(RefObjectsDestroyed); - PRINT_STATISTIC(RefObjectsAllocated); - PRINT_STATISTIC(RefObjectsFreed); - PRINT_STATISTIC(RefObjectsAllocatedFromSmallFreeList); - PRINT_STATISTIC(RefObjectsFreedToSmallFreeList); - PRINT_STATISTIC(RefObjectsAllocatedFromTypeFreeList); - PRINT_STATISTIC(RefObjectsFreedToTypeFreeList); - PRINT_STATISTIC(RefObjectsDeleteDeferred); - PRINT_STATISTIC(RefAutoPoolsCreated); - PRINT_STATISTIC(RefAutoPoolsDestroyed); - PRINT_STATISTIC(RefAutoPoolsDynamicAllocated); - PRINT_STATISTIC(RefAutoPoolsDynamicResized); - PRINT_STATISTIC(QlBlockSpins); - PRINT_STATISTIC(QlBlockWaits); - PRINT_STATISTIC(QlAcquireExclusiveBlocks); - PRINT_STATISTIC(QlAcquireSharedBlocks); - PRINT_STATISTIC(WqWorkQueueThreadsCreated); - PRINT_STATISTIC(WqWorkQueueThreadsCreateFailed); - PRINT_STATISTIC(WqWorkItemsQueued); - -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objects", TRUE)) - { -#ifdef DEBUG - PWSTR typeFilter = wcstok_s(NULL, delims, &context); - PLIST_ENTRY currentEntry; - ULONG totalNumberOfObjects = 0; - //SIZE_T totalNumberOfBytes = 0; - - if (typeFilter) - _wcslwr(typeFilter); - - PhAcquireQueuedLockShared(&PhDbgObjectListLock); - - currentEntry = PhDbgObjectListHead.Flink; - - while (currentEntry != &PhDbgObjectListHead) - { - PPH_OBJECT_HEADER objectHeader; - WCHAR typeName[32]; - - objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); - - // Make sure the object isn't being destroyed. - if (!PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader))) - { - currentEntry = currentEntry->Flink; - continue; - } - - totalNumberOfObjects++; - //totalNumberOfBytes += objectHeader->Size; - - if (typeFilter) - { - wcscpy_s(typeName, sizeof(typeName) / 2, PhGetObjectType(PhObjectHeaderToObject(objectHeader))->Name); - _wcslwr(typeName); - } - - if ( - !typeFilter || - (typeFilter && wcsstr(typeName, typeFilter)) - ) - { - PhpPrintObjectInfo(objectHeader, 1); - } - - currentEntry = currentEntry->Flink; - PhDereferenceObjectDeferDelete(PhObjectHeaderToObject(objectHeader)); - } - - PhReleaseQueuedLockShared(&PhDbgObjectListLock); - - wprintf(L"\n"); - wprintf(L"Total number: %lu\n", totalNumberOfObjects); - /*wprintf(L"Total size (excl. header): %s\n", - ((PPH_STRING)PH_AUTO(PhFormatSize(totalNumberOfBytes, 1)))->Buffer);*/ - wprintf(L"Total overhead (header): %s\n", - ((PPH_STRING)PH_AUTO( - PhFormatSize(PhAddObjectHeaderSize(0) * totalNumberOfObjects, 1) - ))->Buffer); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objtrace", TRUE)) - { -#ifdef DEBUG - PWSTR objectAddress = wcstok_s(NULL, delims, &context); - PH_STRINGREF objectAddressString; - ULONG64 address; - - if (!objectAddress) - { - wprintf(L"Missing object address.\n"); - goto EndCommand; - } - - PhInitializeStringRef(&objectAddressString, objectAddress); - - if (PhStringToInteger64(&objectAddressString, 16, &address)) - { - PPH_OBJECT_HEADER objectHeader = (PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address); - PVOID stackBackTrace[16]; - ULONG i; - - // The address may not be valid. - __try - { - memcpy(stackBackTrace, objectHeader->StackBackTrace, 16 * sizeof(PVOID)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - PPH_STRING message; - - message = PH_AUTO(PhGetNtMessage(GetExceptionCode())); - wprintf(L"Error: %s\n", PhGetString(message)); - - goto EndCommand; - } - - for (i = 0; i < 16; i++) - { - if (!stackBackTrace[i]) - break; - - wprintf(L"%s\n", PhpGetSymbolForAddress(stackBackTrace[i])); - } - } - else - { - wprintf(L"Invalid object address.\n"); - } -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objmksnap", TRUE)) - { -#ifdef DEBUG - PLIST_ENTRY currentEntry; - - if (ObjectListSnapshot) - { - PhDereferenceObject(ObjectListSnapshot); - ObjectListSnapshot = NULL; - } - - ObjectListSnapshot = PhCreateSimpleHashtable(100); - - PhAcquireQueuedLockShared(&PhDbgObjectListLock); - - currentEntry = PhDbgObjectListHead.Flink; - - while (currentEntry != &PhDbgObjectListHead) - { - PPH_OBJECT_HEADER objectHeader; - - objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); - currentEntry = currentEntry->Flink; - - if (PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot) - PhAddItemSimpleHashtable(ObjectListSnapshot, objectHeader, NULL); - } - - PhReleaseQueuedLockShared(&PhDbgObjectListLock); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objcmpsnap", TRUE)) - { -#ifdef DEBUG - PLIST_ENTRY currentEntry; - PPH_LIST newObjects; - ULONG i; - - if (!ObjectListSnapshot) - { - wprintf(L"No snapshot.\n"); - goto EndCommand; - } - - newObjects = PhCreateList(10); - - PhAcquireQueuedLockShared(&PhDbgObjectListLock); - - currentEntry = PhDbgObjectListHead.Flink; - - while (currentEntry != &PhDbgObjectListHead) - { - PPH_OBJECT_HEADER objectHeader; - - objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); - currentEntry = currentEntry->Flink; - - if ( - PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot && - PhObjectHeaderToObject(objectHeader) != newObjects - ) - { - if (!PhFindItemSimpleHashtable(ObjectListSnapshot, objectHeader)) - { - if (PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader))) - PhAddItemList(newObjects, objectHeader); - } - } - } - - PhReleaseQueuedLockShared(&PhDbgObjectListLock); - - for (i = 0; i < newObjects->Count; i++) - { - PPH_OBJECT_HEADER objectHeader = newObjects->Items[i]; - - PhpPrintObjectInfo(objectHeader, 1); - - PhDereferenceObject(PhObjectHeaderToObject(objectHeader)); - } - - PhDereferenceObject(newObjects); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objmknew", TRUE)) - { -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&NewObjectListLock); - PhpDeleteNewObjectList(); - PhReleaseQueuedLockExclusive(&NewObjectListLock); - - // Creation needs to be done outside of the lock, - // otherwise a deadlock will occur. - NewObjectList = PhCreateList(100); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objdelnew", TRUE)) - { -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&NewObjectListLock); - PhpDeleteNewObjectList(); - PhReleaseQueuedLockExclusive(&NewObjectListLock); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"objviewnew", TRUE)) - { -#ifdef DEBUG - ULONG i; - - PhAcquireQueuedLockExclusive(&NewObjectListLock); - - if (!NewObjectList) - { - wprintf(L"Object creation hooking not active.\n"); - PhReleaseQueuedLockExclusive(&NewObjectListLock); - goto EndCommand; - } - - for (i = 0; i < NewObjectList->Count; i++) - { - PhpPrintObjectInfo(PhObjectToObjectHeader(NewObjectList->Items[i]), 1); - } - - PhReleaseQueuedLockExclusive(&NewObjectListLock); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"dumpobj", TRUE)) - { - PWSTR addressString = wcstok_s(NULL, delims, &context); - PH_STRINGREF addressStringRef; - ULONG64 address; - - if (!addressString) - goto EndCommand; - - PhInitializeStringRef(&addressStringRef, addressString); - - if (PhStringToInteger64(&addressStringRef, 16, &address)) - { - PhpDumpObjectInfo((PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address)); - } - } - else if (PhEqualStringZ(command, L"dumpautopool", TRUE)) - { - PWSTR addressString = wcstok_s(NULL, delims, &context); - PH_STRINGREF addressStringRef; - ULONG64 address; - - if (!addressString) - goto EndCommand; - - PhInitializeStringRef(&addressStringRef, addressString); - - if (PhStringToInteger64(&addressStringRef, 16, &address)) - { - PPH_AUTO_POOL userAutoPool = (PPH_AUTO_POOL)address; - ULONG i; - - __try - { - wprintf(L"Static count: %u\n", userAutoPool->StaticCount); - wprintf(L"Dynamic count: %u\n", userAutoPool->DynamicCount); - wprintf(L"Dynamic allocated: %u\n", userAutoPool->DynamicAllocated); - - wprintf(L"Static objects:\n"); - - for (i = 0; i < userAutoPool->StaticCount; i++) - PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->StaticObjects[i]), 0); - - wprintf(L"Dynamic objects:\n"); - - for (i = 0; i < userAutoPool->DynamicCount; i++) - PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->DynamicObjects[i]), 0); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - goto EndCommand; - } - } - } - else if (PhEqualStringZ(command, L"threads", TRUE)) - { -#ifdef DEBUG - PLIST_ENTRY currentEntry; - - PhAcquireQueuedLockShared(&PhDbgThreadListLock); - - currentEntry = PhDbgThreadListHead.Flink; - - while (currentEntry != &PhDbgThreadListHead) - { - PPHP_BASE_THREAD_DBG dbg; - - dbg = CONTAINING_RECORD(currentEntry, PHP_BASE_THREAD_DBG, ListEntry); - - wprintf(L"Thread %u\n", HandleToUlong(dbg->ClientId.UniqueThread)); - wprintf(L"\tStart Address: %s\n", PhpGetSymbolForAddress(dbg->StartAddress)); - wprintf(L"\tParameter: %Ix\n", (ULONG_PTR)dbg->Parameter); - wprintf(L"\tCurrent auto-pool: %Ix\n", (ULONG_PTR)dbg->CurrentAutoPool); - - currentEntry = currentEntry->Flink; - } - - PhReleaseQueuedLockShared(&PhDbgThreadListLock); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"provthreads", TRUE)) - { -#ifdef DEBUG - ULONG i; - - if (PhDbgProviderList) - { - PhAcquireQueuedLockShared(&PhDbgProviderListLock); - - for (i = 0; i < PhDbgProviderList->Count; i++) - { - PPH_PROVIDER_THREAD providerThread = PhDbgProviderList->Items[i]; - THREAD_BASIC_INFORMATION basicInfo; - PLIST_ENTRY providerEntry; - - if (providerThread->ThreadHandle) - { - PhGetThreadBasicInformation(providerThread->ThreadHandle, &basicInfo); - wprintf(L"Thread %u\n", HandleToUlong(basicInfo.ClientId.UniqueThread)); - } - else - { - wprintf(L"Thread not running\n"); - } - - PhAcquireQueuedLockExclusive(&providerThread->Lock); - - providerEntry = providerThread->ListHead.Flink; - - while (providerEntry != &providerThread->ListHead) - { - PPH_PROVIDER_REGISTRATION registration; - - registration = CONTAINING_RECORD(providerEntry, PH_PROVIDER_REGISTRATION, ListEntry); - - wprintf(L"\tProvider registration at %Ix\n", (ULONG_PTR)registration); - wprintf(L"\t\tEnabled: %s\n", registration->Enabled ? L"Yes" : L"No"); - wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(registration->Function)); - - if (registration->Object) - { - wprintf(L"\t\tObject:\n"); - PhpPrintObjectInfo(PhObjectToObjectHeader(registration->Object), 0); - } - - providerEntry = providerEntry->Flink; - } - - PhReleaseQueuedLockExclusive(&providerThread->Lock); - - wprintf(L"\n"); - } - - PhReleaseQueuedLockShared(&PhDbgProviderListLock); - } -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"workqueues", TRUE)) - { -#ifdef DEBUG - ULONG i; - - if (PhDbgWorkQueueList) - { - PhAcquireQueuedLockShared(&PhDbgWorkQueueListLock); - - for (i = 0; i < PhDbgWorkQueueList->Count; i++) - { - PPH_WORK_QUEUE workQueue = PhDbgWorkQueueList->Items[i]; - PLIST_ENTRY workQueueItemEntry; - - wprintf(L"Work queue at %s\n", PhpGetSymbolForAddress(workQueue)); - wprintf(L"Maximum threads: %u\n", workQueue->MaximumThreads); - wprintf(L"Minimum threads: %u\n", workQueue->MinimumThreads); - wprintf(L"No work timeout: %d\n", workQueue->NoWorkTimeout); - - wprintf(L"Current threads: %u\n", workQueue->CurrentThreads); - wprintf(L"Busy count: %u\n", workQueue->BusyCount); - - PhAcquireQueuedLockExclusive(&workQueue->QueueLock); - - // List the items backwards. - workQueueItemEntry = workQueue->QueueListHead.Blink; - - while (workQueueItemEntry != &workQueue->QueueListHead) - { - PPH_WORK_QUEUE_ITEM workQueueItem; - - workQueueItem = CONTAINING_RECORD(workQueueItemEntry, PH_WORK_QUEUE_ITEM, ListEntry); - - wprintf(L"\tWork queue item at %Ix\n", (ULONG_PTR)workQueueItem); - wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(workQueueItem->Function)); - wprintf(L"\t\tContext: %Ix\n", (ULONG_PTR)workQueueItem->Context); - - workQueueItemEntry = workQueueItemEntry->Blink; - } - - PhReleaseQueuedLockExclusive(&workQueue->QueueLock); - - wprintf(L"\n"); - } - - PhReleaseQueuedLockShared(&PhDbgWorkQueueListLock); - } -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"procrecords", TRUE)) - { - PPH_PROCESS_RECORD record; - ULONG i; - SYSTEMTIME systemTime; - PPH_PROCESS_RECORD startRecord; - - PhAcquireQueuedLockShared(&PhProcessRecordListLock); - - for (i = 0; i < PhProcessRecordList->Count; i++) - { - record = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i]; - - PhLargeIntegerToLocalSystemTime(&systemTime, &record->CreateTime); - wprintf(L"Records for %s %s:\n", - ((PPH_STRING)PH_AUTO(PhFormatDate(&systemTime, NULL)))->Buffer, - ((PPH_STRING)PH_AUTO(PhFormatTime(&systemTime, NULL)))->Buffer - ); - - startRecord = record; - - do - { - wprintf(L"\tRecord at %Ix: %s (%u) (refs: %d)\n", (ULONG_PTR)record, record->ProcessName->Buffer, HandleToUlong(record->ProcessId), record->RefCount); - - if (record->FileName) - wprintf(L"\t\t%s\n", record->FileName->Buffer); - - record = CONTAINING_RECORD(record->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); - } while (record != startRecord); - } - - PhReleaseQueuedLockShared(&PhProcessRecordListLock); - } - else if (PhEqualStringZ(command, L"procitem", TRUE)) - { - PWSTR filterString; - PH_STRINGREF filterRef; - ULONG64 filter64; - LONG_PTR processIdFilter = -9; // can't use -2, -1 or 0 because they're all used for process IDs - ULONG_PTR processAddressFilter = 0; - PWSTR imageNameFilter = NULL; - BOOLEAN showAll = FALSE; - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - ULONG i; - - filterString = wcstok_s(NULL, delims, &context); - - if (filterString) - { - PhInitializeStringRef(&filterRef, filterString); - - if (PhStringToInteger64(&filterRef, 10, &filter64)) - processIdFilter = (LONG_PTR)filter64; - if (PhStringToInteger64(&filterRef, 16, &filter64)) - processAddressFilter = (ULONG_PTR)filter64; - - imageNameFilter = filterString; - } - else - { - showAll = TRUE; - } - - PhEnumProcessItems(&processes, &numberOfProcesses); - - for (i = 0; i < numberOfProcesses; i++) - { - PPH_PROCESS_ITEM process = processes[i]; - - if ( - showAll || - (processIdFilter != -9 && (LONG_PTR)process->ProcessId == processIdFilter) || - (processAddressFilter != 0 && (ULONG_PTR)process == processAddressFilter) || - (imageNameFilter && PhMatchWildcards(imageNameFilter, process->ProcessName->Buffer, TRUE)) - ) - { - wprintf(L"Process item at %Ix: %s (%u)\n", (ULONG_PTR)process, process->ProcessName->Buffer, HandleToUlong(process->ProcessId)); - wprintf(L"\tRecord at %Ix\n", (ULONG_PTR)process->Record); - wprintf(L"\tQuery handle %Ix\n", (ULONG_PTR)process->QueryHandle); - wprintf(L"\tFile name at %Ix: %s\n", (ULONG_PTR)process->FileName, PhGetStringOrDefault(process->FileName, L"(null)")); - wprintf(L"\tCommand line at %Ix: %s\n", (ULONG_PTR)process->CommandLine, PhGetStringOrDefault(process->CommandLine, L"(null)")); - wprintf(L"\tFlags: %u\n", process->Flags); - wprintf(L"\n"); - } - } - - PhDereferenceObjects(processes, numberOfProcesses); - } - else if (PhEqualStringZ(command, L"uniquestr", TRUE)) - { -#ifdef DEBUG - PLIST_ENTRY currentEntry; - PPH_HASHTABLE hashtable; - PPH_LIST list; - PSTRING_TABLE_ENTRY stringEntry; - ULONG enumerationKey; - ULONG i; - - hashtable = PhCreateHashtable( - sizeof(STRING_TABLE_ENTRY), - PhpStringHashtableEqualFunction, - PhpStringHashtableHashFunction, - 1024 - ); - - PhAcquireQueuedLockShared(&PhDbgObjectListLock); - - currentEntry = PhDbgObjectListHead.Flink; - - while (currentEntry != &PhDbgObjectListHead) - { - PPH_OBJECT_HEADER objectHeader; - PPH_STRING string; - STRING_TABLE_ENTRY localStringEntry; - BOOLEAN added; - - objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); - currentEntry = currentEntry->Flink; - string = PhObjectHeaderToObject(objectHeader); - - // Make sure this is a string. - if (PhGetObjectType(string) != PhStringType) - continue; - - // Make sure the object isn't being destroyed. - if (!PhReferenceObjectSafe(string)) - continue; - - localStringEntry.String = string; - stringEntry = PhAddEntryHashtableEx(hashtable, &localStringEntry, &added); - - if (added) - { - stringEntry->Count = 1; - PhReferenceObject(string); - } - else - { - stringEntry->Count++; - } - - PhDereferenceObjectDeferDelete(string); - } - - PhReleaseQueuedLockShared(&PhDbgObjectListLock); - - // Sort the string entries by count. - - list = PhCreateList(hashtable->Count); - - enumerationKey = 0; - - while (PhEnumHashtable(hashtable, &stringEntry, &enumerationKey)) - { - PhAddItemList(list, stringEntry); - } - - qsort(list->Items, list->Count, sizeof(PSTRING_TABLE_ENTRY), PhpStringEntryCompareByCount); - - // Display... - - for (i = 0; i < 40 && i < list->Count; i++) - { - stringEntry = list->Items[i]; - wprintf(L"%Iu\t%.64s\n", stringEntry->Count, stringEntry->String->Buffer); - } - - wprintf(L"\nTotal unique strings: %u\n", list->Count); - - // Cleanup - - for (i = 0; i < list->Count; i++) - { - stringEntry = list->Items[i]; - PhDereferenceObject(stringEntry->String); - } - - PhDereferenceObject(list); - PhDereferenceObject(hashtable); -#else - wprintf(commandDebugOnly); -#endif - } - else if (PhEqualStringZ(command, L"enableleakdetect", TRUE)) - { - HEAP_DEBUGGING_INFORMATION debuggingInfo; - - memset(&debuggingInfo, 0, sizeof(HEAP_DEBUGGING_INFORMATION)); - debuggingInfo.StackTraceDepth = 32; - debuggingInfo.HeapLeakEnumerationRoutine = PhpLeakEnumerationRoutine; - - if (!NT_SUCCESS(RtlSetHeapInformation(NULL, HeapDebuggingInformation, &debuggingInfo, sizeof(HEAP_DEBUGGING_INFORMATION)))) - { - wprintf(L"Unable to initialize heap debugging. Make sure that you are using Windows 7 or above."); - } - } - else if (PhEqualStringZ(command, L"leakdetect", TRUE)) - { - VOID (NTAPI *rtlDetectHeapLeaks)(VOID); - PWSTR options = wcstok_s(NULL, delims, &context); - - rtlDetectHeapLeaks = PhGetModuleProcAddress(L"ntdll.dll", "RtlDetectHeapLeaks"); - - if (!(NtCurrentPeb()->NtGlobalFlag & FLG_USER_STACK_TRACE_DB)) - { - wprintf(L"Warning: user-mode stack trace database is not enabled. Stack traces will not be displayed.\n"); - } - - ShowAllLeaks = FALSE; - - if (options) - { - if (PhEqualStringZ(options, L"all", TRUE)) - ShowAllLeaks = TRUE; - } - - if (rtlDetectHeapLeaks) - { - InLeakDetection = TRUE; - NumberOfLeaks = 0; - NumberOfLeaksShown = 0; - rtlDetectHeapLeaks(); - InLeakDetection = FALSE; - - wprintf(L"\nNumber of leaks: %lu (%lu displayed)\n", NumberOfLeaks, NumberOfLeaksShown); - } - } - else if (PhEqualStringZ(command, L"mem", TRUE)) - { - PWSTR addressString; - PWSTR bytesString; - PH_STRINGREF addressStringRef; - PH_STRINGREF bytesStringRef; - ULONG64 address64; - ULONG64 numberOfBytes64; - PUCHAR address; - ULONG numberOfBytes; - ULONG blockSize; - UCHAR buffer[16]; - ULONG i; - - addressString = wcstok_s(NULL, delims, &context); - - if (!addressString) - goto PrintMemUsage; - - bytesString = wcstok_s(NULL, delims, &context); - - if (!bytesString) - { - bytesString = L"16"; - } - - PhInitializeStringRef(&addressStringRef, addressString); - PhInitializeStringRef(&bytesStringRef, bytesString); - - if (PhStringToInteger64(&addressStringRef, 16, &address64) && PhStringToInteger64(&bytesStringRef, 10, &numberOfBytes64)) - { - address = (PUCHAR)address64; - numberOfBytes = (ULONG)numberOfBytes64; - - if (numberOfBytes > 256) - { - wprintf(L"Number of bytes must be 256 or smaller.\n"); - goto EndCommand; - } - - blockSize = sizeof(buffer); - - while (numberOfBytes != 0) - { - if (blockSize > numberOfBytes) - blockSize = numberOfBytes; - - __try - { - memcpy(buffer, address, blockSize); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - wprintf(L"Error reading address near %Ix.\n", (ULONG_PTR)address); - goto EndCommand; - } - - // Print hex dump - for (i = 0; i < blockSize; i++) - wprintf(L"%02x ", buffer[i]); - - // Fill remaining space (for last, possibly partial block) - for (; i < sizeof(buffer); i++) - wprintf(L" "); - - wprintf(L" "); - - // Print ASCII dump - for (i = 0; i < blockSize; i++) - putwchar((ULONG)(buffer[i] - ' ') <= (ULONG)('~' - ' ') ? buffer[i] : '.'); - - wprintf(L"\n"); - - address += blockSize; - numberOfBytes -= blockSize; - } - } - - goto EndCommand; -PrintMemUsage: - wprintf(L"Usage: mem address [numberOfBytes]\n"); - wprintf(L"Example: mem 12345678 16\n"); - } - else - { - wprintf(L"Unrecognized command.\n"); - goto EndCommand; // get rid of the compiler warning about the label being unreferenced - } - -EndCommand: - PhDrainAutoPool(&autoPool); - } - - PhDereferenceObject(DebugConsoleSymbolProvider); - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} +/* + * Process Hacker - + * debug console + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This is a simple debugging console which is able to explore phlib's + * systems easily. Commands are provided to debug reference counting + * problems and memory usage, as well as to do general performance testing. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct _STRING_TABLE_ENTRY +{ + PPH_STRING String; + ULONG_PTR Count; +} STRING_TABLE_ENTRY, *PSTRING_TABLE_ENTRY; + +BOOL ConsoleHandlerRoutine( + _In_ ULONG dwCtrlType + ); + +VOID PhpPrintHashtableStatistics( + _In_ PPH_HASHTABLE Hashtable + ); + +NTSTATUS PhpDebugConsoleThreadStart( + _In_ PVOID Parameter + ); + +extern PH_FREE_LIST PhObjectSmallFreeList; + +static HANDLE DebugConsoleThreadHandle; +static PPH_SYMBOL_PROVIDER DebugConsoleSymbolProvider; + +static PPH_HASHTABLE ObjectListSnapshot = NULL; +#ifdef DEBUG +static PPH_LIST NewObjectList = NULL; +static PH_QUEUED_LOCK NewObjectListLock; +#endif + +static BOOLEAN ShowAllLeaks = FALSE; +static BOOLEAN InLeakDetection = FALSE; +static ULONG NumberOfLeaks; +static ULONG NumberOfLeaksShown; + +VOID PhShowDebugConsole( + VOID + ) +{ + if (AllocConsole()) + { + HMENU menu; + + // Disable the close button because it's impossible to handle + // those events. + menu = GetSystemMenu(GetConsoleWindow(), FALSE); + EnableMenuItem(menu, SC_CLOSE, MF_GRAYED | MF_DISABLED); + DeleteMenu(menu, SC_CLOSE, 0); + + // Set a handler so we can catch Ctrl+C and Ctrl+Break. + SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE); + + _wfreopen(L"CONOUT$", L"w", stdout); + _wfreopen(L"CONOUT$", L"w", stderr); + _wfreopen(L"CONIN$", L"r", stdin); + + PhCreateThreadEx(&DebugConsoleThreadHandle, PhpDebugConsoleThreadStart, NULL); + } + else + { + HWND consoleWindow; + + consoleWindow = GetConsoleWindow(); + + // Console window already exists, so bring it to the top. + if (IsMinimized(consoleWindow)) + ShowWindow(consoleWindow, SW_RESTORE); + else + BringWindowToTop(consoleWindow); + + return; + } +} + +VOID PhCloseDebugConsole( + VOID + ) +{ + _wfreopen(L"NUL", L"w", stdout); + _wfreopen(L"NUL", L"w", stderr); + _wfreopen(L"NUL", L"r", stdin); + + FreeConsole(); +} + +static BOOL ConsoleHandlerRoutine( + _In_ ULONG dwCtrlType + ) +{ + switch (dwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + PhCloseDebugConsole(); + return TRUE; + } + + return FALSE; +} + +static BOOLEAN NTAPI PhpLoadCurrentProcessSymbolsCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PhLoadModuleSymbolProvider((PPH_SYMBOL_PROVIDER)Context, Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, Module->Size); + + return TRUE; +} + +static PWSTR PhpGetSymbolForAddress( + _In_ PVOID Address + ) +{ + return PH_AUTO_T(PH_STRING, PhGetSymbolFromAddress( + DebugConsoleSymbolProvider, (ULONG64)Address, NULL, NULL, NULL, NULL + ))->Buffer; +} + +static VOID PhpPrintObjectInfo( + _In_ PPH_OBJECT_HEADER ObjectHeader, + _In_ LONG RefToSubtract + ) +{ + PVOID object; + PPH_OBJECT_TYPE objectType; + WCHAR c = ' '; + + object = PhObjectHeaderToObject(ObjectHeader); + wprintf(L"%Ix", (ULONG_PTR)object); + objectType = PhGetObjectType(object); + + wprintf(L"\t% 20s", objectType->Name); + + if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST) + c = 'f'; + else if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST) + c = 'F'; + + wprintf(L"\t%4d %c", ObjectHeader->RefCount - RefToSubtract, c); + + if (!objectType) + { + // Dummy + } + else if (objectType == PhObjectTypeObject) + { + wprintf(L"\t%.32s", ((PPH_OBJECT_TYPE)object)->Name); + } + else if (objectType == PhStringType) + { + wprintf(L"\t%.32s", ((PPH_STRING)object)->Buffer); + } + else if (objectType == PhBytesType) + { + wprintf(L"\t%.32S", ((PPH_BYTES)object)->Buffer); + } + else if (objectType == PhListType) + { + wprintf(L"\tCount: %u", ((PPH_LIST)object)->Count); + } + else if (objectType == PhPointerListType) + { + wprintf(L"\tCount: %u", ((PPH_POINTER_LIST)object)->Count); + } + else if (objectType == PhHashtableType) + { + wprintf(L"\tCount: %u", ((PPH_HASHTABLE)object)->Count); + } + else if (objectType == PhProcessItemType) + { + wprintf( + L"\t%.28s (%lu)", + ((PPH_PROCESS_ITEM)object)->ProcessName->Buffer, + HandleToUlong(((PPH_PROCESS_ITEM)object)->ProcessId) + ); + } + else if (objectType == PhServiceItemType) + { + wprintf(L"\t%s", ((PPH_SERVICE_ITEM)object)->Name->Buffer); + } + else if (objectType == PhThreadItemType) + { + wprintf(L"\tTID: %lu", HandleToUlong(((PPH_THREAD_ITEM)object)->ThreadId)); + } + + wprintf(L"\n"); +} + +static VOID PhpDumpObjectInfo( + _In_ PPH_OBJECT_HEADER ObjectHeader + ) +{ + PVOID object; + PPH_OBJECT_TYPE objectType; + + object = PhObjectHeaderToObject(ObjectHeader); + objectType = PhGetObjectType(object); + + __try + { + wprintf(L"Type: %s\n", objectType->Name); + wprintf(L"Reference count: %ld\n", ObjectHeader->RefCount); + wprintf(L"Flags: %x\n", ObjectHeader->Flags); + + if (objectType == PhObjectTypeObject) + { + wprintf(L"Name: %s\n", ((PPH_OBJECT_TYPE)object)->Name); + wprintf(L"Number of objects: %lu\n", ((PPH_OBJECT_TYPE)object)->NumberOfObjects); + wprintf(L"Flags: %u\n", ((PPH_OBJECT_TYPE)object)->Flags); + wprintf(L"Type index: %u\n", ((PPH_OBJECT_TYPE)object)->TypeIndex); + wprintf(L"Free list count: %lu\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count); + } + else if (objectType == PhStringType) + { + wprintf(L"%s\n", ((PPH_STRING)object)->Buffer); + } + else if (objectType == PhBytesType) + { + wprintf(L"%S\n", ((PPH_BYTES)object)->Buffer); + } + else if (objectType == PhHashtableType) + { + PhpPrintHashtableStatistics((PPH_HASHTABLE)object); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + wprintf(L"Error.\n"); + } +} + +static VOID PhpPrintHashtableStatistics( + _In_ PPH_HASHTABLE Hashtable + ) +{ + ULONG i; + ULONG expectedLookupMisses = 0; + + wprintf(L"Count: %u\n", Hashtable->Count); + wprintf(L"Allocated buckets: %u\n", Hashtable->AllocatedBuckets); + wprintf(L"Allocated entries: %u\n", Hashtable->AllocatedEntries); + wprintf(L"Next free entry: %d\n", Hashtable->FreeEntry); + wprintf(L"Next usable entry: %d\n", Hashtable->NextEntry); + + wprintf(L"Equal function: %s\n", PhpGetSymbolForAddress(Hashtable->EqualFunction)); + wprintf(L"Hash function: %s\n", PhpGetSymbolForAddress(Hashtable->HashFunction)); + + wprintf(L"\nBuckets:\n"); + + for (i = 0; i < Hashtable->AllocatedBuckets; i++) + { + ULONG index; + ULONG count = 0; + + // Count the number of entries in this bucket. + + index = Hashtable->Buckets[i]; + + while (index != -1) + { + index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next; + count++; + } + + if (count != 0) + { + expectedLookupMisses += count - 1; + } + + if (count != 0) + { + wprintf(L"%lu: ", i); + + // Print out the entry indicies. + + index = Hashtable->Buckets[i]; + + while (index != -1) + { + wprintf(L"%lu", index); + + index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next; + count--; + + if (count != 0) + wprintf(L", "); + } + + wprintf(L"\n"); + } + else + { + //wprintf(L"%u: (empty)\n"); + } + } + + wprintf(L"\nExpected lookup misses: %lu\n", expectedLookupMisses); +} + +#ifdef DEBUG +static VOID PhpDebugCreateObjectHook( + _In_ PVOID Object, + _In_ SIZE_T Size, + _In_ ULONG Flags, + _In_ PPH_OBJECT_TYPE ObjectType + ) +{ + PhAcquireQueuedLockExclusive(&NewObjectListLock); + + if (NewObjectList) + { + PhReferenceObject(Object); + PhAddItemList(NewObjectList, Object); + } + + PhReleaseQueuedLockExclusive(&NewObjectListLock); +} +#endif + +#ifdef DEBUG +static VOID PhpDeleteNewObjectList( + VOID + ) +{ + if (NewObjectList) + { + PhDereferenceObjects(NewObjectList->Items, NewObjectList->Count); + PhDereferenceObject(NewObjectList); + NewObjectList = NULL; + } +} +#endif + +static BOOLEAN PhpStringHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PSTRING_TABLE_ENTRY entry1 = Entry1; + PSTRING_TABLE_ENTRY entry2 = Entry2; + + return PhEqualString(entry1->String, entry2->String, FALSE); +} + +static ULONG PhpStringHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PSTRING_TABLE_ENTRY entry = Entry; + + return PhHashBytes((PUCHAR)entry->String->Buffer, entry->String->Length); +} + +static int __cdecl PhpStringEntryCompareByCount( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PSTRING_TABLE_ENTRY entry1 = *(PSTRING_TABLE_ENTRY *)elem1; + PSTRING_TABLE_ENTRY entry2 = *(PSTRING_TABLE_ENTRY *)elem2; + + return uintptrcmp(entry2->Count, entry1->Count); +} + +static NTSTATUS PhpLeakEnumerationRoutine( + _In_ LONG Reserved, + _In_ PVOID HeapHandle, + _In_ PVOID BaseAddress, + _In_ SIZE_T BlockSize, + _In_ ULONG StackTraceDepth, + _In_ PVOID *StackTrace + ) +{ + ULONG i; + + if (!InLeakDetection) + return 0; + + if (!HeapHandle) // means no more entries + return 0; + + if (ShowAllLeaks || HeapHandle == PhHeapHandle) + { + wprintf(L"Leak at 0x%Ix (%Iu bytes). Stack trace:\n", (ULONG_PTR)BaseAddress, BlockSize); + + for (i = 0; i < StackTraceDepth; i++) + { + PPH_STRING symbol; + + symbol = PhGetSymbolFromAddress(DebugConsoleSymbolProvider, (ULONG64)StackTrace[i], NULL, NULL, NULL, NULL); + + if (symbol) + wprintf(L"\t%s\n", symbol->Buffer); + else + wprintf(L"\t?\n"); + + PhDereferenceObject(symbol); + } + + NumberOfLeaksShown++; + } + + NumberOfLeaks++; + + return 0; +} + +typedef struct _STOPWATCH +{ + LARGE_INTEGER StartCounter; + LARGE_INTEGER EndCounter; + LARGE_INTEGER Frequency; +} STOPWATCH, *PSTOPWATCH; + +static VOID PhInitializeStopwatch( + _Out_ PSTOPWATCH Stopwatch + ) +{ + Stopwatch->StartCounter.QuadPart = 0; + Stopwatch->EndCounter.QuadPart = 0; +} + +static VOID PhStartStopwatch( + _Inout_ PSTOPWATCH Stopwatch + ) +{ + NtQueryPerformanceCounter(&Stopwatch->StartCounter, &Stopwatch->Frequency); +} + +static VOID PhStopStopwatch( + _Inout_ PSTOPWATCH Stopwatch + ) +{ + NtQueryPerformanceCounter(&Stopwatch->EndCounter, NULL); +} + +static ULONG PhGetMillisecondsStopwatch( + _In_ PSTOPWATCH Stopwatch + ) +{ + LARGE_INTEGER countsPerMs; + + countsPerMs = Stopwatch->Frequency; + countsPerMs.QuadPart /= 1000; + + return (ULONG)((Stopwatch->EndCounter.QuadPart - Stopwatch->StartCounter.QuadPart) / + countsPerMs.QuadPart); +} + +typedef VOID (FASTCALL *PPHF_RW_LOCK_FUNCTION)( + _In_ PVOID Parameter + ); + +typedef struct _RW_TEST_CONTEXT +{ + PWSTR Name; + + PPHF_RW_LOCK_FUNCTION AcquireExclusive; + PPHF_RW_LOCK_FUNCTION AcquireShared; + PPHF_RW_LOCK_FUNCTION ReleaseExclusive; + PPHF_RW_LOCK_FUNCTION ReleaseShared; + + PVOID Parameter; +} RW_TEST_CONTEXT, *PRW_TEST_CONTEXT; + +static PH_BARRIER RwStartBarrier; +static LONG RwReadersActive; +static LONG RwWritersActive; + +static NTSTATUS PhpRwLockTestThreadStart( + _In_ PVOID Parameter + ) +{ +#define RW_ITERS 10000 +#define RW_READ_ITERS 100 +#define RW_WRITE_ITERS 10 +#define RW_READ_SPIN_ITERS 60 +#define RW_WRITE_SPIN_ITERS 200 + + RW_TEST_CONTEXT context = *(PRW_TEST_CONTEXT)Parameter; + ULONG i; + ULONG j; + ULONG k; + ULONG m; + + PhWaitForBarrier(&RwStartBarrier, FALSE); + + for (i = 0; i < RW_ITERS; i++) + { + for (j = 0; j < RW_READ_ITERS; j++) + { + // Read zone + + context.AcquireShared(context.Parameter); + _InterlockedIncrement(&RwReadersActive); + + for (m = 0; m < RW_READ_SPIN_ITERS; m++) + YieldProcessor(); + + if (RwWritersActive != 0) + { + wprintf(L"[fail]: writers active in read zone!\n"); + NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL); + } + + _InterlockedDecrement(&RwReadersActive); + context.ReleaseShared(context.Parameter); + + // Spin for a while + + for (m = 0; m < 10; m++) + YieldProcessor(); + + if (j == RW_READ_ITERS / 2) + { + // Write zone + + for (k = 0; k < RW_WRITE_ITERS; k++) + { + context.AcquireExclusive(context.Parameter); + _InterlockedIncrement(&RwWritersActive); + + for (m = 0; m < RW_WRITE_SPIN_ITERS; m++) + YieldProcessor(); + + if (RwReadersActive != 0) + { + wprintf(L"[fail]: readers active in write zone!\n"); + NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL); + } + + _InterlockedDecrement(&RwWritersActive); + context.ReleaseExclusive(context.Parameter); + } + } + } + } + + return STATUS_SUCCESS; +} + +static VOID PhpTestRwLock( + _In_ PRW_TEST_CONTEXT Context + ) +{ +#define RW_PROCESSORS 4 + + STOPWATCH stopwatch; + ULONG i; + HANDLE threadHandles[RW_PROCESSORS]; + + // Dummy + + Context->AcquireExclusive(Context->Parameter); + Context->ReleaseExclusive(Context->Parameter); + Context->AcquireShared(Context->Parameter); + Context->ReleaseShared(Context->Parameter); + + // Null test + PhInitializeStopwatch(&stopwatch); + PhStartStopwatch(&stopwatch); + + for (i = 0; i < 2000000; i++) + { + Context->AcquireExclusive(Context->Parameter); + Context->ReleaseExclusive(Context->Parameter); + Context->AcquireShared(Context->Parameter); + Context->ReleaseShared(Context->Parameter); + } + + PhStopStopwatch(&stopwatch); + + wprintf(L"[null] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch)); + + // Stress test + + PhInitializeBarrier(&RwStartBarrier, RW_PROCESSORS + 1); + RwReadersActive = 0; + RwWritersActive = 0; + + for (i = 0; i < RW_PROCESSORS; i++) + { + PhCreateThreadEx(&threadHandles[i], PhpRwLockTestThreadStart, Context); + } + + PhWaitForBarrier(&RwStartBarrier, FALSE); + PhInitializeStopwatch(&stopwatch); + PhStartStopwatch(&stopwatch); + NtWaitForMultipleObjects(RW_PROCESSORS, threadHandles, WaitAll, FALSE, NULL); + PhStopStopwatch(&stopwatch); + + for (i = 0; i < RW_PROCESSORS; i++) + NtClose(threadHandles[i]); + + wprintf(L"[strs] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch)); +} + +VOID FASTCALL PhfAcquireCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection + ) +{ + RtlEnterCriticalSection(CriticalSection); +} + +VOID FASTCALL PhfReleaseCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection + ) +{ + RtlLeaveCriticalSection(CriticalSection); +} + +VOID FASTCALL PhfReleaseQueuedLockExclusiveUsingInline( + _In_ PPH_QUEUED_LOCK QueuedLock + ) +{ + PhReleaseQueuedLockExclusive(QueuedLock); +} + +NTSTATUS PhpDebugConsoleThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + BOOLEAN exit = FALSE; + + PhInitializeAutoPool(&autoPool); + + DebugConsoleSymbolProvider = PhCreateSymbolProvider(NtCurrentProcessId()); + PhLoadSymbolProviderOptions(DebugConsoleSymbolProvider); + + { + static UNICODE_STRING variableNameUs = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH"); + UNICODE_STRING variableValueUs; + PPH_STRING newSearchPath; + WCHAR buffer[MAX_PATH]; + + RtlInitEmptyUnicodeString(&variableValueUs, buffer, sizeof(buffer)); + + if (NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &variableNameUs, &variableValueUs))) + { + PPH_STRING currentDirectory = PhGetApplicationDirectory(); + PPH_STRING currentSearchPath = PhGetStringSetting(L"DbgHelpSearchPath"); + + if (currentSearchPath->Length != 0) + { + newSearchPath = PhFormatString( + L"%s;%s;%s", + buffer, + PhGetStringOrEmpty(currentSearchPath), + PhGetStringOrEmpty(currentDirectory) + ); + } + else + { + newSearchPath = PhFormatString( + L"%s;%s", + buffer, + PhGetStringOrEmpty(currentDirectory) + ); + } + + PhSetSearchPathSymbolProvider(DebugConsoleSymbolProvider, PhGetString(newSearchPath)); + + PhDereferenceObject(newSearchPath); + PhDereferenceObject(currentDirectory); + } + } + + PhEnumGenericModules( + NtCurrentProcessId(), + NtCurrentProcess(), + 0, + PhpLoadCurrentProcessSymbolsCallback, + DebugConsoleSymbolProvider + ); + +#ifdef DEBUG + PhInitializeQueuedLock(&NewObjectListLock); + PhDbgCreateObjectHook = PhpDebugCreateObjectHook; +#endif + + wprintf(L"Press Ctrl+C or type \"exit\" to close the debug console. Type \"help\" for a list of commands.\n"); + + while (!exit) + { + static PWSTR delims = L" \t"; + static PWSTR commandDebugOnly = L"This command is not available on non-debug builds.\n"; + + WCHAR line[201]; + ULONG inputLength; + PWSTR context; + PWSTR command; + + wprintf(L"dbg>"); + + if (!fgetws(line, sizeof(line) / sizeof(WCHAR) - 1, stdin)) + break; + + // Remove the terminating new line character. + + inputLength = (ULONG)PhCountStringZ(line); + + if (inputLength != 0) + line[inputLength - 1] = UNICODE_NULL; + + context = NULL; + command = wcstok_s(line, delims, &context); + + if (!command) + { + continue; + } + else if (PhEqualStringZ(command, L"help", TRUE)) + { + wprintf( + L"Commands:\n" + L"exit\n" + L"testperf\n" + L"testlocks\n" + L"stats\n" + L"objects [type-name-filter]\n" + L"objtrace object-address\n" + L"objmksnap\n" + L"objcmpsnap\n" + L"objmknew\n" + L"objdelnew\n" + L"objviewnew\n" + L"dumpobj\n" + L"dumpautopool\n" + L"threads\n" + L"provthreads\n" + L"workqueues\n" + L"procrecords\n" + L"procitem\n" + L"uniquestr\n" + L"enableleakdetect\n" + L"leakdetect\n" + L"mem\n" + ); + } + else if (PhEqualStringZ(command, L"exit", TRUE)) + { + PhCloseDebugConsole(); + exit = TRUE; + } + else if (PhEqualStringZ(command, L"testperf", TRUE)) + { + STOPWATCH stopwatch; + ULONG i; + PPH_STRING testString; + RTL_CRITICAL_SECTION testCriticalSection; + PH_FAST_LOCK testFastLock; + PH_QUEUED_LOCK testQueuedLock; + + // Control (string reference counting) + + testString = PhCreateString(L""); + PhReferenceObject(testString); + PhDereferenceObject(testString); + PhStartStopwatch(&stopwatch); + + for (i = 0; i < 10000000; i++) + { + PhReferenceObject(testString); + PhDereferenceObject(testString); + } + + PhStopStopwatch(&stopwatch); + PhDereferenceObject(testString); + + wprintf(L"Referencing: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); + + // Critical section + + RtlInitializeCriticalSection(&testCriticalSection); + RtlEnterCriticalSection(&testCriticalSection); + RtlLeaveCriticalSection(&testCriticalSection); + PhStartStopwatch(&stopwatch); + + for (i = 0; i < 10000000; i++) + { + RtlEnterCriticalSection(&testCriticalSection); + RtlLeaveCriticalSection(&testCriticalSection); + } + + PhStopStopwatch(&stopwatch); + RtlDeleteCriticalSection(&testCriticalSection); + + wprintf(L"Critical section: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); + + // Fast lock + + PhInitializeFastLock(&testFastLock); + PhAcquireFastLockExclusive(&testFastLock); + PhReleaseFastLockExclusive(&testFastLock); + PhStartStopwatch(&stopwatch); + + for (i = 0; i < 10000000; i++) + { + PhAcquireFastLockExclusive(&testFastLock); + PhReleaseFastLockExclusive(&testFastLock); + } + + PhStopStopwatch(&stopwatch); + PhDeleteFastLock(&testFastLock); + + wprintf(L"Fast lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); + + // Queued lock + + PhInitializeQueuedLock(&testQueuedLock); + PhAcquireQueuedLockExclusive(&testQueuedLock); + PhReleaseQueuedLockExclusive(&testQueuedLock); + PhStartStopwatch(&stopwatch); + + for (i = 0; i < 10000000; i++) + { + PhAcquireQueuedLockExclusive(&testQueuedLock); + PhReleaseQueuedLockExclusive(&testQueuedLock); + } + + PhStopStopwatch(&stopwatch); + + wprintf(L"Queued lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch)); + } + else if (PhEqualStringZ(command, L"testlocks", TRUE)) + { + RW_TEST_CONTEXT testContext; + PH_FAST_LOCK fastLock; + PH_QUEUED_LOCK queuedLock; + RTL_CRITICAL_SECTION criticalSection; + + testContext.Name = L"FastLock"; + testContext.AcquireExclusive = PhfAcquireFastLockExclusive; + testContext.AcquireShared = PhfAcquireFastLockShared; + testContext.ReleaseExclusive = PhfReleaseFastLockExclusive; + testContext.ReleaseShared = PhfReleaseFastLockShared; + testContext.Parameter = &fastLock; + PhInitializeFastLock(&fastLock); + PhpTestRwLock(&testContext); + PhDeleteFastLock(&fastLock); + + testContext.Name = L"QueuedLock"; + testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive; + testContext.AcquireShared = PhfAcquireQueuedLockShared; + testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive; + testContext.ReleaseShared = PhfReleaseQueuedLockShared; + testContext.Parameter = &queuedLock; + PhInitializeQueuedLock(&queuedLock); + PhpTestRwLock(&testContext); + + testContext.Name = L"CriticalSection"; + testContext.AcquireExclusive = PhfAcquireCriticalSection; + testContext.AcquireShared = PhfAcquireCriticalSection; + testContext.ReleaseExclusive = PhfReleaseCriticalSection; + testContext.ReleaseShared = PhfReleaseCriticalSection; + testContext.Parameter = &criticalSection; + RtlInitializeCriticalSection(&criticalSection); + PhpTestRwLock(&testContext); + RtlDeleteCriticalSection(&criticalSection); + + testContext.Name = L"QueuedLockMutex"; + testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive; + testContext.AcquireShared = PhfAcquireQueuedLockExclusive; + testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive; + testContext.ReleaseShared = PhfReleaseQueuedLockExclusive; + testContext.Parameter = &queuedLock; + PhInitializeQueuedLock(&queuedLock); + PhpTestRwLock(&testContext); + } + else if (PhEqualStringZ(command, L"stats", TRUE)) + { +#ifdef DEBUG + wprintf(L"Object small free list count: %u\n", PhObjectSmallFreeList.Count); + wprintf(L"Statistics:\n"); +#define PRINT_STATISTIC(Name) wprintf(L#Name L": %u\n", PhLibStatisticsBlock.Name); + + PRINT_STATISTIC(BaseThreadsCreated); + PRINT_STATISTIC(BaseThreadsCreateFailed); + PRINT_STATISTIC(BaseStringBuildersCreated); + PRINT_STATISTIC(BaseStringBuildersResized); + PRINT_STATISTIC(RefObjectsCreated); + PRINT_STATISTIC(RefObjectsDestroyed); + PRINT_STATISTIC(RefObjectsAllocated); + PRINT_STATISTIC(RefObjectsFreed); + PRINT_STATISTIC(RefObjectsAllocatedFromSmallFreeList); + PRINT_STATISTIC(RefObjectsFreedToSmallFreeList); + PRINT_STATISTIC(RefObjectsAllocatedFromTypeFreeList); + PRINT_STATISTIC(RefObjectsFreedToTypeFreeList); + PRINT_STATISTIC(RefObjectsDeleteDeferred); + PRINT_STATISTIC(RefAutoPoolsCreated); + PRINT_STATISTIC(RefAutoPoolsDestroyed); + PRINT_STATISTIC(RefAutoPoolsDynamicAllocated); + PRINT_STATISTIC(RefAutoPoolsDynamicResized); + PRINT_STATISTIC(QlBlockSpins); + PRINT_STATISTIC(QlBlockWaits); + PRINT_STATISTIC(QlAcquireExclusiveBlocks); + PRINT_STATISTIC(QlAcquireSharedBlocks); + PRINT_STATISTIC(WqWorkQueueThreadsCreated); + PRINT_STATISTIC(WqWorkQueueThreadsCreateFailed); + PRINT_STATISTIC(WqWorkItemsQueued); + +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objects", TRUE)) + { +#ifdef DEBUG + PWSTR typeFilter = wcstok_s(NULL, delims, &context); + PLIST_ENTRY currentEntry; + ULONG totalNumberOfObjects = 0; + //SIZE_T totalNumberOfBytes = 0; + + if (typeFilter) + _wcslwr(typeFilter); + + PhAcquireQueuedLockShared(&PhDbgObjectListLock); + + currentEntry = PhDbgObjectListHead.Flink; + + while (currentEntry != &PhDbgObjectListHead) + { + PPH_OBJECT_HEADER objectHeader; + WCHAR typeName[32]; + + objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); + + // Make sure the object isn't being destroyed. + if (!PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader))) + { + currentEntry = currentEntry->Flink; + continue; + } + + totalNumberOfObjects++; + //totalNumberOfBytes += objectHeader->Size; + + if (typeFilter) + { + wcscpy_s(typeName, sizeof(typeName) / sizeof(WCHAR), PhGetObjectType(PhObjectHeaderToObject(objectHeader))->Name); + _wcslwr(typeName); + } + + if ( + !typeFilter || + (typeFilter && wcsstr(typeName, typeFilter)) + ) + { + PhpPrintObjectInfo(objectHeader, 1); + } + + currentEntry = currentEntry->Flink; + PhDereferenceObjectDeferDelete(PhObjectHeaderToObject(objectHeader)); + } + + PhReleaseQueuedLockShared(&PhDbgObjectListLock); + + wprintf(L"\n"); + wprintf(L"Total number: %lu\n", totalNumberOfObjects); + /*wprintf(L"Total size (excl. header): %s\n", + ((PPH_STRING)PH_AUTO(PhFormatSize(totalNumberOfBytes, 1)))->Buffer);*/ + wprintf(L"Total overhead (header): %s\n", + ((PPH_STRING)PH_AUTO( + PhFormatSize(PhAddObjectHeaderSize(0) * totalNumberOfObjects, 1) + ))->Buffer); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objtrace", TRUE)) + { +#ifdef DEBUG + PWSTR objectAddress = wcstok_s(NULL, delims, &context); + PH_STRINGREF objectAddressString; + ULONG64 address; + + if (!objectAddress) + { + wprintf(L"Missing object address.\n"); + goto EndCommand; + } + + PhInitializeStringRef(&objectAddressString, objectAddress); + + if (PhStringToInteger64(&objectAddressString, 16, &address)) + { + PPH_OBJECT_HEADER objectHeader = (PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address); + PVOID stackBackTrace[16]; + ULONG i; + + // The address may not be valid. + __try + { + memcpy(stackBackTrace, objectHeader->StackBackTrace, 16 * sizeof(PVOID)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + PPH_STRING message; + + message = PH_AUTO(PhGetNtMessage(GetExceptionCode())); + wprintf(L"Error: %s\n", PhGetString(message)); + + goto EndCommand; + } + + for (i = 0; i < 16; i++) + { + if (!stackBackTrace[i]) + break; + + wprintf(L"%s\n", PhpGetSymbolForAddress(stackBackTrace[i])); + } + } + else + { + wprintf(L"Invalid object address.\n"); + } +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objmksnap", TRUE)) + { +#ifdef DEBUG + PLIST_ENTRY currentEntry; + + if (ObjectListSnapshot) + { + PhDereferenceObject(ObjectListSnapshot); + ObjectListSnapshot = NULL; + } + + ObjectListSnapshot = PhCreateSimpleHashtable(100); + + PhAcquireQueuedLockShared(&PhDbgObjectListLock); + + currentEntry = PhDbgObjectListHead.Flink; + + while (currentEntry != &PhDbgObjectListHead) + { + PPH_OBJECT_HEADER objectHeader; + + objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); + currentEntry = currentEntry->Flink; + + if (PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot) + PhAddItemSimpleHashtable(ObjectListSnapshot, objectHeader, NULL); + } + + PhReleaseQueuedLockShared(&PhDbgObjectListLock); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objcmpsnap", TRUE)) + { +#ifdef DEBUG + PLIST_ENTRY currentEntry; + PPH_LIST newObjects; + ULONG i; + + if (!ObjectListSnapshot) + { + wprintf(L"No snapshot.\n"); + goto EndCommand; + } + + newObjects = PhCreateList(10); + + PhAcquireQueuedLockShared(&PhDbgObjectListLock); + + currentEntry = PhDbgObjectListHead.Flink; + + while (currentEntry != &PhDbgObjectListHead) + { + PPH_OBJECT_HEADER objectHeader; + + objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); + currentEntry = currentEntry->Flink; + + if ( + PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot && + PhObjectHeaderToObject(objectHeader) != newObjects + ) + { + if (!PhFindItemSimpleHashtable(ObjectListSnapshot, objectHeader)) + { + if (PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader))) + PhAddItemList(newObjects, objectHeader); + } + } + } + + PhReleaseQueuedLockShared(&PhDbgObjectListLock); + + for (i = 0; i < newObjects->Count; i++) + { + PPH_OBJECT_HEADER objectHeader = newObjects->Items[i]; + + PhpPrintObjectInfo(objectHeader, 1); + + PhDereferenceObject(PhObjectHeaderToObject(objectHeader)); + } + + PhDereferenceObject(newObjects); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objmknew", TRUE)) + { +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&NewObjectListLock); + PhpDeleteNewObjectList(); + PhReleaseQueuedLockExclusive(&NewObjectListLock); + + // Creation needs to be done outside of the lock, + // otherwise a deadlock will occur. + NewObjectList = PhCreateList(100); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objdelnew", TRUE)) + { +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&NewObjectListLock); + PhpDeleteNewObjectList(); + PhReleaseQueuedLockExclusive(&NewObjectListLock); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"objviewnew", TRUE)) + { +#ifdef DEBUG + ULONG i; + + PhAcquireQueuedLockExclusive(&NewObjectListLock); + + if (!NewObjectList) + { + wprintf(L"Object creation hooking not active.\n"); + PhReleaseQueuedLockExclusive(&NewObjectListLock); + goto EndCommand; + } + + for (i = 0; i < NewObjectList->Count; i++) + { + PhpPrintObjectInfo(PhObjectToObjectHeader(NewObjectList->Items[i]), 1); + } + + PhReleaseQueuedLockExclusive(&NewObjectListLock); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"dumpobj", TRUE)) + { + PWSTR addressString = wcstok_s(NULL, delims, &context); + PH_STRINGREF addressStringRef; + ULONG64 address; + + if (!addressString) + goto EndCommand; + + PhInitializeStringRef(&addressStringRef, addressString); + + if (PhStringToInteger64(&addressStringRef, 16, &address)) + { + PhpDumpObjectInfo((PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address)); + } + } + else if (PhEqualStringZ(command, L"dumpautopool", TRUE)) + { + PWSTR addressString = wcstok_s(NULL, delims, &context); + PH_STRINGREF addressStringRef; + ULONG64 address; + + if (!addressString) + goto EndCommand; + + PhInitializeStringRef(&addressStringRef, addressString); + + if (PhStringToInteger64(&addressStringRef, 16, &address)) + { + PPH_AUTO_POOL userAutoPool = (PPH_AUTO_POOL)address; + ULONG i; + + __try + { + wprintf(L"Static count: %u\n", userAutoPool->StaticCount); + wprintf(L"Dynamic count: %u\n", userAutoPool->DynamicCount); + wprintf(L"Dynamic allocated: %u\n", userAutoPool->DynamicAllocated); + + wprintf(L"Static objects:\n"); + + for (i = 0; i < userAutoPool->StaticCount; i++) + PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->StaticObjects[i]), 0); + + wprintf(L"Dynamic objects:\n"); + + for (i = 0; i < userAutoPool->DynamicCount; i++) + PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->DynamicObjects[i]), 0); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + goto EndCommand; + } + } + } + else if (PhEqualStringZ(command, L"threads", TRUE)) + { +#ifdef DEBUG + PLIST_ENTRY currentEntry; + + PhAcquireQueuedLockShared(&PhDbgThreadListLock); + + currentEntry = PhDbgThreadListHead.Flink; + + while (currentEntry != &PhDbgThreadListHead) + { + PPHP_BASE_THREAD_DBG dbg; + + dbg = CONTAINING_RECORD(currentEntry, PHP_BASE_THREAD_DBG, ListEntry); + + wprintf(L"Thread %u\n", HandleToUlong(dbg->ClientId.UniqueThread)); + wprintf(L"\tStart Address: %s\n", PhpGetSymbolForAddress(dbg->StartAddress)); + wprintf(L"\tParameter: %Ix\n", (ULONG_PTR)dbg->Parameter); + wprintf(L"\tCurrent auto-pool: %Ix\n", (ULONG_PTR)dbg->CurrentAutoPool); + + currentEntry = currentEntry->Flink; + } + + PhReleaseQueuedLockShared(&PhDbgThreadListLock); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"provthreads", TRUE)) + { +#ifdef DEBUG + ULONG i; + + if (PhDbgProviderList) + { + PhAcquireQueuedLockShared(&PhDbgProviderListLock); + + for (i = 0; i < PhDbgProviderList->Count; i++) + { + PPH_PROVIDER_THREAD providerThread = PhDbgProviderList->Items[i]; + THREAD_BASIC_INFORMATION basicInfo; + PLIST_ENTRY providerEntry; + + if (providerThread->ThreadHandle) + { + PhGetThreadBasicInformation(providerThread->ThreadHandle, &basicInfo); + wprintf(L"Thread %u\n", HandleToUlong(basicInfo.ClientId.UniqueThread)); + } + else + { + wprintf(L"Thread not running\n"); + } + + PhAcquireQueuedLockExclusive(&providerThread->Lock); + + providerEntry = providerThread->ListHead.Flink; + + while (providerEntry != &providerThread->ListHead) + { + PPH_PROVIDER_REGISTRATION registration; + + registration = CONTAINING_RECORD(providerEntry, PH_PROVIDER_REGISTRATION, ListEntry); + + wprintf(L"\tProvider registration at %Ix\n", (ULONG_PTR)registration); + wprintf(L"\t\tEnabled: %s\n", registration->Enabled ? L"Yes" : L"No"); + wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(registration->Function)); + + if (registration->Object) + { + wprintf(L"\t\tObject:\n"); + PhpPrintObjectInfo(PhObjectToObjectHeader(registration->Object), 0); + } + + providerEntry = providerEntry->Flink; + } + + PhReleaseQueuedLockExclusive(&providerThread->Lock); + + wprintf(L"\n"); + } + + PhReleaseQueuedLockShared(&PhDbgProviderListLock); + } +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"workqueues", TRUE)) + { +#ifdef DEBUG + ULONG i; + + if (PhDbgWorkQueueList) + { + PhAcquireQueuedLockShared(&PhDbgWorkQueueListLock); + + for (i = 0; i < PhDbgWorkQueueList->Count; i++) + { + PPH_WORK_QUEUE workQueue = PhDbgWorkQueueList->Items[i]; + PLIST_ENTRY workQueueItemEntry; + + wprintf(L"Work queue at %s\n", PhpGetSymbolForAddress(workQueue)); + wprintf(L"Maximum threads: %lu\n", workQueue->MaximumThreads); + wprintf(L"Minimum threads: %lu\n", workQueue->MinimumThreads); + wprintf(L"No work timeout: %lu\n", workQueue->NoWorkTimeout); + + wprintf(L"Current threads: %lu\n", workQueue->CurrentThreads); + wprintf(L"Busy count: %lu\n", workQueue->BusyCount); + + PhAcquireQueuedLockExclusive(&workQueue->QueueLock); + + // List the items backwards. + workQueueItemEntry = workQueue->QueueListHead.Blink; + + while (workQueueItemEntry != &workQueue->QueueListHead) + { + PPH_WORK_QUEUE_ITEM workQueueItem; + + workQueueItem = CONTAINING_RECORD(workQueueItemEntry, PH_WORK_QUEUE_ITEM, ListEntry); + + wprintf(L"\tWork queue item at %Ix\n", (ULONG_PTR)workQueueItem); + wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(workQueueItem->Function)); + wprintf(L"\t\tContext: %Ix\n", (ULONG_PTR)workQueueItem->Context); + + workQueueItemEntry = workQueueItemEntry->Blink; + } + + PhReleaseQueuedLockExclusive(&workQueue->QueueLock); + + wprintf(L"\n"); + } + + PhReleaseQueuedLockShared(&PhDbgWorkQueueListLock); + } +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"procrecords", TRUE)) + { + PPH_PROCESS_RECORD record; + ULONG i; + SYSTEMTIME systemTime; + PPH_PROCESS_RECORD startRecord; + + PhAcquireQueuedLockShared(&PhProcessRecordListLock); + + for (i = 0; i < PhProcessRecordList->Count; i++) + { + record = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i]; + + PhLargeIntegerToLocalSystemTime(&systemTime, &record->CreateTime); + wprintf(L"Records for %s %s:\n", + ((PPH_STRING)PH_AUTO(PhFormatDate(&systemTime, NULL)))->Buffer, + ((PPH_STRING)PH_AUTO(PhFormatTime(&systemTime, NULL)))->Buffer + ); + + startRecord = record; + + do + { + wprintf(L"\tRecord at %Ix: %s (%lu) (refs: %ld)\n", (ULONG_PTR)record, record->ProcessName->Buffer, HandleToUlong(record->ProcessId), record->RefCount); + + if (record->FileName) + wprintf(L"\t\t%s\n", record->FileName->Buffer); + + record = CONTAINING_RECORD(record->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); + } while (record != startRecord); + } + + PhReleaseQueuedLockShared(&PhProcessRecordListLock); + } + else if (PhEqualStringZ(command, L"procitem", TRUE)) + { + PWSTR filterString; + PH_STRINGREF filterRef; + ULONG64 filter64; + LONG_PTR processIdFilter = -9; // can't use -2, -1 or 0 because they're all used for process IDs + ULONG_PTR processAddressFilter = 0; + PWSTR imageNameFilter = NULL; + BOOLEAN showAll = FALSE; + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + ULONG i; + + filterString = wcstok_s(NULL, delims, &context); + + if (filterString) + { + PhInitializeStringRef(&filterRef, filterString); + + if (PhStringToInteger64(&filterRef, 10, &filter64)) + processIdFilter = (LONG_PTR)filter64; + if (PhStringToInteger64(&filterRef, 16, &filter64)) + processAddressFilter = (ULONG_PTR)filter64; + + imageNameFilter = filterString; + } + else + { + showAll = TRUE; + } + + PhEnumProcessItems(&processes, &numberOfProcesses); + + for (i = 0; i < numberOfProcesses; i++) + { + PPH_PROCESS_ITEM process = processes[i]; + + if ( + showAll || + (processIdFilter != -9 && (LONG_PTR)process->ProcessId == processIdFilter) || + (processAddressFilter != 0 && (ULONG_PTR)process == processAddressFilter) || + (imageNameFilter && PhMatchWildcards(imageNameFilter, process->ProcessName->Buffer, TRUE)) + ) + { + wprintf(L"Process item at %Ix: %s (%u)\n", (ULONG_PTR)process, process->ProcessName->Buffer, HandleToUlong(process->ProcessId)); + wprintf(L"\tRecord at %Ix\n", (ULONG_PTR)process->Record); + wprintf(L"\tQuery handle %Ix\n", (ULONG_PTR)process->QueryHandle); + wprintf(L"\tFile name at %Ix: %s\n", (ULONG_PTR)process->FileName, PhGetStringOrDefault(process->FileName, L"(null)")); + wprintf(L"\tCommand line at %Ix: %s\n", (ULONG_PTR)process->CommandLine, PhGetStringOrDefault(process->CommandLine, L"(null)")); + wprintf(L"\tFlags: %u\n", process->Flags); + wprintf(L"\n"); + } + } + + PhDereferenceObjects(processes, numberOfProcesses); + } + else if (PhEqualStringZ(command, L"uniquestr", TRUE)) + { +#ifdef DEBUG + PLIST_ENTRY currentEntry; + PPH_HASHTABLE hashtable; + PPH_LIST list; + PSTRING_TABLE_ENTRY stringEntry; + ULONG enumerationKey; + ULONG i; + + hashtable = PhCreateHashtable( + sizeof(STRING_TABLE_ENTRY), + PhpStringHashtableEqualFunction, + PhpStringHashtableHashFunction, + 1024 + ); + + PhAcquireQueuedLockShared(&PhDbgObjectListLock); + + currentEntry = PhDbgObjectListHead.Flink; + + while (currentEntry != &PhDbgObjectListHead) + { + PPH_OBJECT_HEADER objectHeader; + PPH_STRING string; + STRING_TABLE_ENTRY localStringEntry; + BOOLEAN added; + + objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry); + currentEntry = currentEntry->Flink; + string = PhObjectHeaderToObject(objectHeader); + + // Make sure this is a string. + if (PhGetObjectType(string) != PhStringType) + continue; + + // Make sure the object isn't being destroyed. + if (!PhReferenceObjectSafe(string)) + continue; + + localStringEntry.String = string; + stringEntry = PhAddEntryHashtableEx(hashtable, &localStringEntry, &added); + + if (added) + { + stringEntry->Count = 1; + PhReferenceObject(string); + } + else + { + stringEntry->Count++; + } + + PhDereferenceObjectDeferDelete(string); + } + + PhReleaseQueuedLockShared(&PhDbgObjectListLock); + + // Sort the string entries by count. + + list = PhCreateList(hashtable->Count); + + enumerationKey = 0; + + while (PhEnumHashtable(hashtable, &stringEntry, &enumerationKey)) + { + PhAddItemList(list, stringEntry); + } + + qsort(list->Items, list->Count, sizeof(PSTRING_TABLE_ENTRY), PhpStringEntryCompareByCount); + + // Display... + + for (i = 0; i < 40 && i < list->Count; i++) + { + stringEntry = list->Items[i]; + wprintf(L"%Iu\t%.64s\n", stringEntry->Count, stringEntry->String->Buffer); + } + + wprintf(L"\nTotal unique strings: %u\n", list->Count); + + // Cleanup + + for (i = 0; i < list->Count; i++) + { + stringEntry = list->Items[i]; + PhDereferenceObject(stringEntry->String); + } + + PhDereferenceObject(list); + PhDereferenceObject(hashtable); +#else + wprintf(commandDebugOnly); +#endif + } + else if (PhEqualStringZ(command, L"enableleakdetect", TRUE)) + { + HEAP_DEBUGGING_INFORMATION debuggingInfo; + + memset(&debuggingInfo, 0, sizeof(HEAP_DEBUGGING_INFORMATION)); + debuggingInfo.StackTraceDepth = 32; + debuggingInfo.HeapLeakEnumerationRoutine = PhpLeakEnumerationRoutine; + + if (!NT_SUCCESS(RtlSetHeapInformation(NULL, HeapSetDebuggingInformation, &debuggingInfo, sizeof(HEAP_DEBUGGING_INFORMATION)))) + { + wprintf(L"Unable to initialize heap debugging. Make sure that you are using Windows 7 or above."); + } + } + else if (PhEqualStringZ(command, L"leakdetect", TRUE)) + { + VOID (NTAPI *rtlDetectHeapLeaks)(VOID); + PWSTR options = wcstok_s(NULL, delims, &context); + + rtlDetectHeapLeaks = PhGetDllProcedureAddress(L"ntdll.dll", "RtlDetectHeapLeaks", 0); + + if (!(NtCurrentPeb()->NtGlobalFlag & FLG_USER_STACK_TRACE_DB)) + { + wprintf(L"Warning: user-mode stack trace database is not enabled. Stack traces will not be displayed.\n"); + } + + ShowAllLeaks = FALSE; + + if (options) + { + if (PhEqualStringZ(options, L"all", TRUE)) + ShowAllLeaks = TRUE; + } + + if (rtlDetectHeapLeaks) + { + InLeakDetection = TRUE; + NumberOfLeaks = 0; + NumberOfLeaksShown = 0; + rtlDetectHeapLeaks(); + InLeakDetection = FALSE; + + wprintf(L"\nNumber of leaks: %lu (%lu displayed)\n", NumberOfLeaks, NumberOfLeaksShown); + } + } + else if (PhEqualStringZ(command, L"mem", TRUE)) + { + PWSTR addressString; + PWSTR bytesString; + PH_STRINGREF addressStringRef; + PH_STRINGREF bytesStringRef; + ULONG64 address64; + ULONG64 numberOfBytes64; + PUCHAR address; + ULONG numberOfBytes; + ULONG blockSize; + UCHAR buffer[16]; + ULONG i; + + addressString = wcstok_s(NULL, delims, &context); + + if (!addressString) + goto PrintMemUsage; + + bytesString = wcstok_s(NULL, delims, &context); + + if (!bytesString) + { + bytesString = L"16"; + } + + PhInitializeStringRef(&addressStringRef, addressString); + PhInitializeStringRef(&bytesStringRef, bytesString); + + if (PhStringToInteger64(&addressStringRef, 16, &address64) && PhStringToInteger64(&bytesStringRef, 10, &numberOfBytes64)) + { + address = (PUCHAR)address64; + numberOfBytes = (ULONG)numberOfBytes64; + + if (numberOfBytes > 256) + { + wprintf(L"Number of bytes must be 256 or smaller.\n"); + goto EndCommand; + } + + blockSize = sizeof(buffer); + + while (numberOfBytes != 0) + { + if (blockSize > numberOfBytes) + blockSize = numberOfBytes; + + __try + { + memcpy(buffer, address, blockSize); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + wprintf(L"Error reading address near %Ix.\n", (ULONG_PTR)address); + goto EndCommand; + } + + // Print hex dump + for (i = 0; i < blockSize; i++) + wprintf(L"%02x ", buffer[i]); + + // Fill remaining space (for last, possibly partial block) + for (; i < sizeof(buffer); i++) + wprintf(L" "); + + wprintf(L" "); + + // Print ASCII dump + for (i = 0; i < blockSize; i++) + putwchar((ULONG)(buffer[i] - ' ') <= (ULONG)('~' - ' ') ? buffer[i] : '.'); + + wprintf(L"\n"); + + address += blockSize; + numberOfBytes -= blockSize; + } + } + + goto EndCommand; +PrintMemUsage: + wprintf(L"Usage: mem address [numberOfBytes]\n"); + wprintf(L"Example: mem 12345678 16\n"); + } + else + { + wprintf(L"Unrecognized command.\n"); + goto EndCommand; // get rid of the compiler warning about the label being unreferenced + } + +EndCommand: + PhDrainAutoPool(&autoPool); + } + + PhDereferenceObject(DebugConsoleSymbolProvider); + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} diff --git a/ProcessHacker/extmgr.c b/ProcessHacker/extmgr.c index 8d93706205d5..950d483b095d 100644 --- a/ProcessHacker/extmgr.c +++ b/ProcessHacker/extmgr.c @@ -1,226 +1,226 @@ -/* - * Process Hacker - - * extension manager - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The extension manager provides support for generic extensions. It sits directly - * underneath the plugin manager, and has no knowledge of plugin details (how they are - * loaded, plugin information, etc.). - */ - -#include -#include - -LIST_ENTRY PhEmAppContextListHead = { &PhEmAppContextListHead, &PhEmAppContextListHead }; -ULONG PhEmAppContextCount = 0; -PH_EM_OBJECT_TYPE_STATE PhEmObjectTypeState[EmMaximumObjectType] = { 0 }; - -/** - * Initializes the extension manager module. - */ -VOID PhEmInitialization( - VOID - ) -{ - ULONG i; - - for (i = 0; i < EmMaximumObjectType; i++) - { - InitializeListHead(&PhEmObjectTypeState[i].ExtensionListHead); - } -} - -/** - * Initializes an extension application context. - * - * \param AppContext The application context. - * \param AppName The application name. - */ -VOID PhEmInitializeAppContext( - _Out_ PPH_EM_APP_CONTEXT AppContext, - _In_ PPH_STRINGREF AppName - ) -{ - AppContext->AppName = *AppName; - memset(AppContext->Extensions, 0, sizeof(AppContext->Extensions)); - - InsertTailList(&PhEmAppContextListHead, &AppContext->ListEntry); - PhEmAppContextCount++; -} - -/** - * Sets the object extension size and callbacks for an object type. - * - * \param AppContext The application context. - * \param ObjectType The type of object for which the extension is being registered. - * \param ExtensionSize The size of the extension, in bytes. - * \param CreateCallback The object creation callback. - * \param DeleteCallback The object deletion callback. - */ -VOID PhEmSetObjectExtension( - _Inout_ PPH_EM_APP_CONTEXT AppContext, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ SIZE_T ExtensionSize, - _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, - _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback - ) -{ - PPH_EM_OBJECT_TYPE_STATE objectTypeState; - PPH_EM_OBJECT_EXTENSION objectExtension; - - objectTypeState = &PhEmObjectTypeState[ObjectType]; - objectExtension = AppContext->Extensions[ObjectType]; - - if (!objectExtension) - { - objectExtension = PhAllocate(sizeof(PH_EM_OBJECT_EXTENSION)); - memset(objectExtension, 0, sizeof(PH_EM_OBJECT_EXTENSION)); - InsertTailList(&objectTypeState->ExtensionListHead, &objectExtension->ListEntry); - AppContext->Extensions[ObjectType] = objectExtension; - - objectExtension->ExtensionSize = ExtensionSize; - objectExtension->ExtensionOffset = objectTypeState->ExtensionOffset; - - objectTypeState->ExtensionOffset += ExtensionSize; - } - - objectExtension->Callbacks[EmObjectCreate] = CreateCallback; - objectExtension->Callbacks[EmObjectDelete] = DeleteCallback; -} - -/** - * Gets the object extension for an object. - * - * \param AppContext The application context. - * \param ObjectType The type of object for which an extension has been registered. - * \param Object The object. - */ -PVOID PhEmGetObjectExtension( - _In_ PPH_EM_APP_CONTEXT AppContext, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Object - ) -{ - PPH_EM_OBJECT_EXTENSION objectExtension; - - objectExtension = AppContext->Extensions[ObjectType]; - - if (!objectExtension) - return NULL; - - return (PCHAR)Object + PhEmObjectTypeState[ObjectType].InitialSize + objectExtension->ExtensionOffset; -} - -/** - * Determines the size of an object, including extensions. - * - * \param ObjectType The object type. - * \param InitialSize The initial size of the object. - */ -SIZE_T PhEmGetObjectSize( - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ SIZE_T InitialSize - ) -{ - PhEmObjectTypeState[ObjectType].InitialSize = InitialSize; - - return InitialSize + PhEmObjectTypeState[ObjectType].ExtensionOffset; -} - -/** - * Invokes callbacks for an object operation. - * - * \param ObjectType The object type. - * \param Object The object. - * \param Operation The operation being performed. - */ -VOID PhEmCallObjectOperation( - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Object, - _In_ PH_EM_OBJECT_OPERATION Operation - ) -{ - PPH_EM_OBJECT_TYPE_STATE objectTypeState; - PLIST_ENTRY listEntry; - PPH_EM_OBJECT_EXTENSION objectExtension; - - if (PhEmAppContextCount == 0) - return; - - objectTypeState = &PhEmObjectTypeState[ObjectType]; - - listEntry = objectTypeState->ExtensionListHead.Flink; - - while (listEntry != &objectTypeState->ExtensionListHead) - { - objectExtension = CONTAINING_RECORD(listEntry, PH_EM_OBJECT_EXTENSION, ListEntry); - - if (objectExtension->Callbacks[Operation]) - { - objectExtension->Callbacks[Operation]( - Object, - ObjectType, - (PCHAR)Object + objectTypeState->InitialSize + objectExtension->ExtensionOffset - ); - } - - listEntry = listEntry->Flink; - } -} - -/** - * Parses an application name and sub-ID pair. - * - * \param CompoundId The compound identifier. - * \param AppName A variable which receives the application name. - * \param SubId A variable which receives the sub-ID. - */ -BOOLEAN PhEmParseCompoundId( - _In_ PPH_STRINGREF CompoundId, - _Out_ PPH_STRINGREF AppName, - _Out_ PULONG SubId - ) -{ - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - ULONG64 integer; - - firstPart = *CompoundId; - - if (firstPart.Length == 0) - return FALSE; - if (firstPart.Buffer[0] != '+') - return FALSE; - - PhSkipStringRef(&firstPart, sizeof(WCHAR)); - PhSplitStringRefAtChar(&firstPart, '+', &firstPart, &secondPart); - - if (firstPart.Length == 0 || secondPart.Length == 0) - return FALSE; - - if (!PhStringToInteger64(&secondPart, 10, &integer)) - return FALSE; - - *AppName = firstPart; - *SubId = (ULONG)integer; - - return TRUE; -} +/* + * Process Hacker - + * extension manager + * + * Copyright (C) 2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The extension manager provides support for generic extensions. It sits directly + * underneath the plugin manager, and has no knowledge of plugin details (how they are + * loaded, plugin information, etc.). + */ + +#include +#include + +LIST_ENTRY PhEmAppContextListHead = { &PhEmAppContextListHead, &PhEmAppContextListHead }; +ULONG PhEmAppContextCount = 0; +PH_EM_OBJECT_TYPE_STATE PhEmObjectTypeState[EmMaximumObjectType] = { 0 }; + +/** + * Initializes the extension manager module. + */ +VOID PhEmInitialization( + VOID + ) +{ + ULONG i; + + for (i = 0; i < EmMaximumObjectType; i++) + { + InitializeListHead(&PhEmObjectTypeState[i].ExtensionListHead); + } +} + +/** + * Initializes an extension application context. + * + * \param AppContext The application context. + * \param AppName The application name. + */ +VOID PhEmInitializeAppContext( + _Out_ PPH_EM_APP_CONTEXT AppContext, + _In_ PPH_STRINGREF AppName + ) +{ + AppContext->AppName = *AppName; + memset(AppContext->Extensions, 0, sizeof(AppContext->Extensions)); + + InsertTailList(&PhEmAppContextListHead, &AppContext->ListEntry); + PhEmAppContextCount++; +} + +/** + * Sets the object extension size and callbacks for an object type. + * + * \param AppContext The application context. + * \param ObjectType The type of object for which the extension is being registered. + * \param ExtensionSize The size of the extension, in bytes. + * \param CreateCallback The object creation callback. + * \param DeleteCallback The object deletion callback. + */ +VOID PhEmSetObjectExtension( + _Inout_ PPH_EM_APP_CONTEXT AppContext, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ SIZE_T ExtensionSize, + _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, + _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback + ) +{ + PPH_EM_OBJECT_TYPE_STATE objectTypeState; + PPH_EM_OBJECT_EXTENSION objectExtension; + + objectTypeState = &PhEmObjectTypeState[ObjectType]; + objectExtension = AppContext->Extensions[ObjectType]; + + if (!objectExtension) + { + objectExtension = PhAllocate(sizeof(PH_EM_OBJECT_EXTENSION)); + memset(objectExtension, 0, sizeof(PH_EM_OBJECT_EXTENSION)); + InsertTailList(&objectTypeState->ExtensionListHead, &objectExtension->ListEntry); + AppContext->Extensions[ObjectType] = objectExtension; + + objectExtension->ExtensionSize = ExtensionSize; + objectExtension->ExtensionOffset = objectTypeState->ExtensionOffset; + + objectTypeState->ExtensionOffset += ExtensionSize; + } + + objectExtension->Callbacks[EmObjectCreate] = CreateCallback; + objectExtension->Callbacks[EmObjectDelete] = DeleteCallback; +} + +/** + * Gets the object extension for an object. + * + * \param AppContext The application context. + * \param ObjectType The type of object for which an extension has been registered. + * \param Object The object. + */ +PVOID PhEmGetObjectExtension( + _In_ PPH_EM_APP_CONTEXT AppContext, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Object + ) +{ + PPH_EM_OBJECT_EXTENSION objectExtension; + + objectExtension = AppContext->Extensions[ObjectType]; + + if (!objectExtension) + return NULL; + + return PTR_ADD_OFFSET(Object, PhEmObjectTypeState[ObjectType].InitialSize + objectExtension->ExtensionOffset); +} + +/** + * Determines the size of an object, including extensions. + * + * \param ObjectType The object type. + * \param InitialSize The initial size of the object. + */ +SIZE_T PhEmGetObjectSize( + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ SIZE_T InitialSize + ) +{ + PhEmObjectTypeState[ObjectType].InitialSize = InitialSize; + + return InitialSize + PhEmObjectTypeState[ObjectType].ExtensionOffset; +} + +/** + * Invokes callbacks for an object operation. + * + * \param ObjectType The object type. + * \param Object The object. + * \param Operation The operation being performed. + */ +VOID PhEmCallObjectOperation( + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Object, + _In_ PH_EM_OBJECT_OPERATION Operation + ) +{ + PPH_EM_OBJECT_TYPE_STATE objectTypeState; + PLIST_ENTRY listEntry; + PPH_EM_OBJECT_EXTENSION objectExtension; + + if (PhEmAppContextCount == 0) + return; + + objectTypeState = &PhEmObjectTypeState[ObjectType]; + + listEntry = objectTypeState->ExtensionListHead.Flink; + + while (listEntry != &objectTypeState->ExtensionListHead) + { + objectExtension = CONTAINING_RECORD(listEntry, PH_EM_OBJECT_EXTENSION, ListEntry); + + if (objectExtension->Callbacks[Operation]) + { + objectExtension->Callbacks[Operation]( + Object, + ObjectType, + PTR_ADD_OFFSET(Object, objectTypeState->InitialSize + objectExtension->ExtensionOffset) + ); + } + + listEntry = listEntry->Flink; + } +} + +/** + * Parses an application name and sub-ID pair. + * + * \param CompoundId The compound identifier. + * \param AppName A variable which receives the application name. + * \param SubId A variable which receives the sub-ID. + */ +BOOLEAN PhEmParseCompoundId( + _In_ PPH_STRINGREF CompoundId, + _Out_ PPH_STRINGREF AppName, + _Out_ PULONG SubId + ) +{ + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + ULONG64 integer; + + firstPart = *CompoundId; + + if (firstPart.Length == 0) + return FALSE; + if (firstPart.Buffer[0] != '+') + return FALSE; + + PhSkipStringRef(&firstPart, sizeof(WCHAR)); + PhSplitStringRefAtChar(&firstPart, '+', &firstPart, &secondPart); + + if (firstPart.Length == 0 || secondPart.Length == 0) + return FALSE; + + if (!PhStringToInteger64(&secondPart, 10, &integer)) + return FALSE; + + *AppName = firstPart; + *SubId = (ULONG)integer; + + return TRUE; +} diff --git a/ProcessHacker/findobj.c b/ProcessHacker/findobj.c index 98c68b117ee3..50311eee5b28 100644 --- a/ProcessHacker/findobj.c +++ b/ProcessHacker/findobj.c @@ -1,1016 +1,1659 @@ -/* - * Process Hacker - - * object search - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "pcre/pcre2.h" - -#define WM_PH_SEARCH_UPDATE (WM_APP + 801) -#define WM_PH_SEARCH_FINISHED (WM_APP + 802) - -typedef enum _PHP_OBJECT_RESULT_TYPE -{ - HandleSearchResult, - ModuleSearchResult, - MappedFileSearchResult -} PHP_OBJECT_RESULT_TYPE; - -typedef struct _PHP_OBJECT_SEARCH_RESULT -{ - HANDLE ProcessId; - PHP_OBJECT_RESULT_TYPE ResultType; - - HANDLE Handle; - PPH_STRING TypeName; - PPH_STRING Name; - PPH_STRING ProcessName; - - WCHAR HandleString[PH_PTR_STR_LEN_1]; - - SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Info; -} PHP_OBJECT_SEARCH_RESULT, *PPHP_OBJECT_SEARCH_RESULT; - -INT_PTR CALLBACK PhpFindObjectsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -NTSTATUS PhpFindObjectsThreadStart( - _In_ PVOID Parameter - ); - -HWND PhFindObjectsWindowHandle = NULL; -HWND PhFindObjectsListViewHandle = NULL; -static PH_LAYOUT_MANAGER WindowLayoutManager; -static RECT MinimumSize; - -static HANDLE SearchThreadHandle = NULL; -static BOOLEAN SearchStop; -static PPH_STRING SearchString; -static pcre2_code *SearchRegexCompiledExpression; -static pcre2_match_data *SearchRegexMatchData; -static PPH_LIST SearchResults = NULL; -static ULONG SearchResultsAddIndex; -static PH_QUEUED_LOCK SearchResultsLock = PH_QUEUED_LOCK_INIT; - -static ULONG64 SearchPointer; -static BOOLEAN UseSearchPointer; - -VOID PhShowFindObjectsDialog( - VOID - ) -{ - if (!PhFindObjectsWindowHandle) - { - PhFindObjectsWindowHandle = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_FINDOBJECTS), - PhMainWndHandle, - PhpFindObjectsDlgProc - ); - } - - if (!IsWindowVisible(PhFindObjectsWindowHandle)) - ShowWindow(PhFindObjectsWindowHandle, SW_SHOW); - - SetForegroundWindow(PhFindObjectsWindowHandle); -} - -VOID PhpInitializeFindObjMenu( - _In_ PPH_EMENU Menu, - _In_ PPHP_OBJECT_SEARCH_RESULT *Results, - _In_ ULONG NumberOfResults - ) -{ - BOOLEAN allCanBeClosed = TRUE; - ULONG i; - - if (NumberOfResults == 1) - { - PH_HANDLE_ITEM_INFO info; - - info.ProcessId = Results[0]->ProcessId; - info.Handle = Results[0]->Handle; - info.TypeName = Results[0]->TypeName; - info.BestObjectName = Results[0]->Name; - PhInsertHandleObjectPropertiesEMenuItems(Menu, ID_OBJECT_PROPERTIES, FALSE, &info); - } - else - { - PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - PhEnableEMenuItem(Menu, ID_OBJECT_COPY, TRUE); - } - - for (i = 0; i < NumberOfResults; i++) - { - if (Results[i]->ResultType != HandleSearchResult) - { - allCanBeClosed = FALSE; - break; - } - } - - PhEnableEMenuItem(Menu, ID_OBJECT_CLOSE, allCanBeClosed); -} - -INT NTAPI PhpObjectProcessCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; - INT result; - - result = PhCompareStringWithNull(item1->ProcessName, item2->ProcessName, TRUE); - - if (result != 0) - return result; - else - return uintptrcmp((ULONG_PTR)item1->ProcessId, (ULONG_PTR)item2->ProcessId); -} - -INT NTAPI PhpObjectTypeCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; - - return PhCompareString(item1->TypeName, item2->TypeName, TRUE); -} - -INT NTAPI PhpObjectNameCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; - - return PhCompareString(item1->Name, item2->Name, TRUE); -} - -INT NTAPI PhpObjectHandleCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PPHP_OBJECT_SEARCH_RESULT item1 = Item1; - PPHP_OBJECT_SEARCH_RESULT item2 = Item2; - - return uintptrcmp((ULONG_PTR)item1->Handle, (ULONG_PTR)item2->Handle); -} - -static INT_PTR CALLBACK PhpFindObjectsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - PhFindObjectsListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_RESULTS); - - PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), - NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_REGEX), - NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), - NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&WindowLayoutManager, lvHandle, - NULL, PH_ANCHOR_ALL); - - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = 150; - MinimumSize.bottom = 100; - MapDialogRect(hwndDlg, &MinimumSize); - - PhRegisterDialog(hwndDlg); - - PhLoadWindowPlacementFromSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); - - PhSetListViewStyle(lvHandle, TRUE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 100, L"Process"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Type"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 200, L"Name"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Handle"); - - PhSetExtendedListView(lvHandle); - ExtendedListView_SetSortFast(lvHandle, TRUE); - ExtendedListView_SetCompareFunction(lvHandle, 0, PhpObjectProcessCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 1, PhpObjectTypeCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 2, PhpObjectNameCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 3, PhpObjectHandleCompareFunction); - PhLoadListViewColumnsFromSetting(L"FindObjListViewColumns", lvHandle); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); - } - break; - case WM_DESTROY: - { - PhSetIntegerSetting(L"FindObjRegex", Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED); - PhSaveWindowPlacementToSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); - PhSaveListViewColumnsToSetting(L"FindObjListViewColumns", PhFindObjectsListViewHandle); - } - break; - case WM_SHOWWINDOW: - { - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_FILTER), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_FILTER), 0, -1); - } - break; - case WM_CLOSE: - { - ShowWindow(hwndDlg, SW_HIDE); - // IMPORTANT - // Set the result to 0 so the default dialog message - // handler doesn't invoke IDCANCEL, which will send - // WM_CLOSE, creating an infinite loop. - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 0); - } - return TRUE; - case WM_SETCURSOR: - { - if (SearchThreadHandle) - { - SetCursor(LoadCursor(NULL, IDC_WAIT)); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDOK: - { - // Don't continue if the user requested cancellation. - if (SearchStop) - break; - - if (!SearchThreadHandle) - { - ULONG i; - - PhMoveReference(&SearchString, PhGetWindowText(GetDlgItem(hwndDlg, IDC_FILTER))); - - if (SearchRegexCompiledExpression) - { - pcre2_code_free(SearchRegexCompiledExpression); - SearchRegexCompiledExpression = NULL; - } - - if (SearchRegexMatchData) - { - pcre2_match_data_free(SearchRegexMatchData); - SearchRegexMatchData = NULL; - } - - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED) - { - int errorCode; - PCRE2_SIZE errorOffset; - - SearchRegexCompiledExpression = pcre2_compile( - SearchString->Buffer, - SearchString->Length / sizeof(WCHAR), - PCRE2_CASELESS | PCRE2_DOTALL, - &errorCode, - &errorOffset, - NULL - ); - - if (!SearchRegexCompiledExpression) - { - PhShowError(hwndDlg, L"Unable to compile the regular expression: \"%s\" at position %zu.", - PhGetStringOrDefault(PH_AUTO(PhPcre2GetErrorMessage(errorCode)), L"Unknown error"), - errorOffset - ); - break; - } - - SearchRegexMatchData = pcre2_match_data_create_from_pattern(SearchRegexCompiledExpression, NULL); - } - - // Clean up previous results. - - ListView_DeleteAllItems(PhFindObjectsListViewHandle); - - if (SearchResults) - { - for (i = 0; i < SearchResults->Count; i++) - { - PPHP_OBJECT_SEARCH_RESULT searchResult = SearchResults->Items[i]; - - PhDereferenceObject(searchResult->TypeName); - PhDereferenceObject(searchResult->Name); - - if (searchResult->ProcessName) - PhDereferenceObject(searchResult->ProcessName); - - PhFree(searchResult); - } - - PhDereferenceObject(SearchResults); - } - - // Start the search. - - SearchResults = PhCreateList(128); - SearchResultsAddIndex = 0; - - SearchThreadHandle = PhCreateThread(0, PhpFindObjectsThreadStart, NULL); - - if (!SearchThreadHandle) - { - PhClearReference(&SearchResults); - break; - } - - SetDlgItemText(hwndDlg, IDOK, L"Cancel"); - - SetCursor(LoadCursor(NULL, IDC_WAIT)); - } - else - { - SearchStop = TRUE; - EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); - } - } - break; - case IDCANCEL: - { - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - case ID_OBJECT_CLOSE: - { - PPHP_OBJECT_SEARCH_RESULT *results; - ULONG numberOfResults; - ULONG i; - - PhGetSelectedListViewItemParams( - PhFindObjectsListViewHandle, - &results, - &numberOfResults - ); - - if (numberOfResults != 0 && PhShowConfirmMessage( - hwndDlg, - L"close", - numberOfResults == 1 ? L"the selected handle" : L"the selected handles", - L"Closing handles may cause system instability and data corruption.", - FALSE - )) - { - for (i = 0; i < numberOfResults; i++) - { - NTSTATUS status; - HANDLE processHandle; - - if (results[i]->ResultType != HandleSearchResult) - continue; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_DUP_HANDLE, - results[i]->ProcessId - ))) - { - if (NT_SUCCESS(status = NtDuplicateObject( - processHandle, - results[i]->Handle, - NULL, - NULL, - 0, - 0, - DUPLICATE_CLOSE_SOURCE - ))) - { - PhRemoveListViewItem(PhFindObjectsListViewHandle, - PhFindListViewItemByParam(PhFindObjectsListViewHandle, 0, results[i])); - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - if (!PhShowContinueStatus(hwndDlg, - PhaFormatString(L"Unable to close \"%s\"", results[i]->Name->Buffer)->Buffer, - status, - 0 - )) - break; - } - } - } - - PhFree(results); - } - break; - case ID_HANDLE_OBJECTPROPERTIES1: - case ID_HANDLE_OBJECTPROPERTIES2: - { - PPHP_OBJECT_SEARCH_RESULT result = - PhGetSelectedListViewItemParam(PhFindObjectsListViewHandle); - - if (result) - { - PH_HANDLE_ITEM_INFO info; - - info.ProcessId = result->ProcessId; - info.Handle = result->Handle; - info.TypeName = result->TypeName; - info.BestObjectName = result->Name; - - if (LOWORD(wParam) == ID_HANDLE_OBJECTPROPERTIES1) - PhShowHandleObjectProperties1(hwndDlg, &info); - else - PhShowHandleObjectProperties2(hwndDlg, &info); - } - } - break; - case ID_OBJECT_GOTOOWNINGPROCESS: - { - PPHP_OBJECT_SEARCH_RESULT result = - PhGetSelectedListViewItemParam(PhFindObjectsListViewHandle); - - if (result) - { - PPH_PROCESS_NODE processNode; - - if (processNode = PhFindProcessNode(result->ProcessId)) - { - ProcessHacker_SelectTabPage(PhMainWndHandle, 0); - ProcessHacker_SelectProcessNode(PhMainWndHandle, processNode); - ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); - } - } - } - break; - case ID_OBJECT_PROPERTIES: - { - PPHP_OBJECT_SEARCH_RESULT result = - PhGetSelectedListViewItemParam(PhFindObjectsListViewHandle); - - if (result) - { - if (result->ResultType == HandleSearchResult) - { - PPH_HANDLE_ITEM handleItem; - - handleItem = PhCreateHandleItem(&result->Info); - - handleItem->BestObjectName = handleItem->ObjectName = result->Name; - PhReferenceObjectEx(result->Name, 2); - - handleItem->TypeName = result->TypeName; - PhReferenceObject(result->TypeName); - - PhShowHandleProperties( - hwndDlg, - result->ProcessId, - handleItem - ); - PhDereferenceObject(handleItem); - } - else - { - // DLL or Mapped File. Just show file properties. - PhShellProperties(hwndDlg, result->Name->Buffer); - } - } - } - break; - case ID_OBJECT_COPY: - { - PhCopyListView(PhFindObjectsListViewHandle); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case NM_DBLCLK: - { - if (header->hwndFrom == PhFindObjectsListViewHandle) - { - SendMessage(hwndDlg, WM_COMMAND, ID_OBJECT_PROPERTIES, 0); - } - } - break; - case LVN_KEYDOWN: - { - if (header->hwndFrom == PhFindObjectsListViewHandle) - { - LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)header; - - switch (keyDown->wVKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(hwndDlg, WM_COMMAND, ID_OBJECT_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - PhSetStateAllListViewItems(PhFindObjectsListViewHandle, LVIS_SELECTED, LVIS_SELECTED); - break; - case VK_DELETE: - SendMessage(hwndDlg, WM_COMMAND, ID_OBJECT_CLOSE, 0); - break; - } - } - } - break; - } - } - break; - case WM_CONTEXTMENU: - { - if ((HWND)wParam == PhFindObjectsListViewHandle) - { - POINT point; - PPHP_OBJECT_SEARCH_RESULT *results; - ULONG numberOfResults; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); - - if (point.x == -1 && point.y == -1) - PhGetListViewContextMenuPoint((HWND)wParam, &point); - - PhGetSelectedListViewItemParams(PhFindObjectsListViewHandle, &results, &numberOfResults); - - if (numberOfResults != 0) - { - PPH_EMENU menu; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_FINDOBJ), 0); - PhSetFlagsEMenuItem(menu, ID_OBJECT_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); - - PhpInitializeFindObjMenu(menu, results, numberOfResults); - PhShowEMenu( - menu, - hwndDlg, - PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - point.x, - point.y - ); - PhDestroyEMenu(menu); - } - - PhFree(results); - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&WindowLayoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - case WM_PH_SEARCH_UPDATE: - { - HWND lvHandle; - ULONG i; - - lvHandle = GetDlgItem(hwndDlg, IDC_RESULTS); - - ExtendedListView_SetRedraw(lvHandle, FALSE); - - PhAcquireQueuedLockExclusive(&SearchResultsLock); - - for (i = SearchResultsAddIndex; i < SearchResults->Count; i++) - { - PPHP_OBJECT_SEARCH_RESULT searchResult = SearchResults->Items[i]; - CLIENT_ID clientId; - PPH_PROCESS_ITEM processItem; - PPH_STRING clientIdName; - INT lvItemIndex; - - clientId.UniqueProcess = searchResult->ProcessId; - clientId.UniqueThread = NULL; - - processItem = PhReferenceProcessItem(clientId.UniqueProcess); - clientIdName = PhGetClientIdNameEx(&clientId, processItem ? processItem->ProcessName : NULL); - - lvItemIndex = PhAddListViewItem( - lvHandle, - MAXINT, - clientIdName->Buffer, - searchResult - ); - - PhDereferenceObject(clientIdName); - - if (processItem) - { - PhSetReference(&searchResult->ProcessName, processItem->ProcessName); - PhDereferenceObject(processItem); - } - else - { - searchResult->ProcessName = NULL; - } - - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, searchResult->TypeName->Buffer); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, searchResult->Name->Buffer); - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, searchResult->HandleString); - } - - SearchResultsAddIndex = i; - - PhReleaseQueuedLockExclusive(&SearchResultsLock); - - ExtendedListView_SetRedraw(lvHandle, TRUE); - } - break; - case WM_PH_SEARCH_FINISHED: - { - NTSTATUS handleSearchStatus = (NTSTATUS)wParam; - - // Add any un-added items. - SendMessage(hwndDlg, WM_PH_SEARCH_UPDATE, 0, 0); - - NtWaitForSingleObject(SearchThreadHandle, FALSE, NULL); - NtClose(SearchThreadHandle); - SearchThreadHandle = NULL; - SearchStop = FALSE; - - ExtendedListView_SortItems(GetDlgItem(hwndDlg, IDC_RESULTS)); - - SetDlgItemText(hwndDlg, IDOK, L"Find"); - EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); - - SetCursor(LoadCursor(NULL, IDC_ARROW)); - - if (handleSearchStatus == STATUS_INSUFFICIENT_RESOURCES) - { - PhShowWarning( - hwndDlg, - L"Unable to search for handles because the total number of handles on the system is too large. " - L"Please check if there are any processes with an extremely large number of handles open." - ); - } - } - break; - } - - return FALSE; -} - -static BOOLEAN MatchSearchString( - _In_ PPH_STRINGREF Input - ) -{ - if (SearchRegexCompiledExpression && SearchRegexMatchData) - { - return pcre2_match( - SearchRegexCompiledExpression, - Input->Buffer, - Input->Length / sizeof(WCHAR), - 0, - 0, - SearchRegexMatchData, - NULL - ) >= 0; - } - else - { - return PhFindStringInStringRef(Input, &SearchString->sr, TRUE) != -1; - } -} - -typedef struct _SEARCH_HANDLE_CONTEXT -{ - BOOLEAN NeedToFree; - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleInfo; - HANDLE ProcessHandle; -} SEARCH_HANDLE_CONTEXT, *PSEARCH_HANDLE_CONTEXT; - -static NTSTATUS NTAPI SearchHandleFunction( - _In_ PVOID Parameter - ) -{ - PSEARCH_HANDLE_CONTEXT context = Parameter; - PPH_STRING typeName; - PPH_STRING bestObjectName; - - if (!SearchStop && NT_SUCCESS(PhGetHandleInformation( - context->ProcessHandle, - (HANDLE)context->HandleInfo->HandleValue, - context->HandleInfo->ObjectTypeIndex, - NULL, - &typeName, - NULL, - &bestObjectName - ))) - { - PPH_STRING upperBestObjectName; - - upperBestObjectName = PhDuplicateString(bestObjectName); - _wcsupr(upperBestObjectName->Buffer); - - if (MatchSearchString(&upperBestObjectName->sr) || - (UseSearchPointer && context->HandleInfo->Object == (PVOID)SearchPointer)) - { - PPHP_OBJECT_SEARCH_RESULT searchResult; - - searchResult = PhAllocate(sizeof(PHP_OBJECT_SEARCH_RESULT)); - searchResult->ProcessId = (HANDLE)context->HandleInfo->UniqueProcessId; - searchResult->ResultType = HandleSearchResult; - searchResult->Handle = (HANDLE)context->HandleInfo->HandleValue; - searchResult->TypeName = typeName; - searchResult->Name = bestObjectName; - PhPrintPointer(searchResult->HandleString, (PVOID)searchResult->Handle); - searchResult->Info = *context->HandleInfo; - - PhAcquireQueuedLockExclusive(&SearchResultsLock); - - PhAddItemList(SearchResults, searchResult); - - // Update the search results in batches of 40. - if (SearchResults->Count % 40 == 0) - PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_UPDATE, 0, 0); - - PhReleaseQueuedLockExclusive(&SearchResultsLock); - } - else - { - PhDereferenceObject(typeName); - PhDereferenceObject(bestObjectName); - } - - PhDereferenceObject(upperBestObjectName); - } - - if (context->NeedToFree) - PhFree(context); - - return STATUS_SUCCESS; -} - -static BOOLEAN NTAPI EnumModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PPH_STRING upperFileName; - - upperFileName = PhDuplicateString(Module->FileName); - _wcsupr(upperFileName->Buffer); - - if (MatchSearchString(&upperFileName->sr) || - (UseSearchPointer && Module->BaseAddress == (PVOID)SearchPointer)) - { - PPHP_OBJECT_SEARCH_RESULT searchResult; - PWSTR typeName; - - switch (Module->Type) - { - case PH_MODULE_TYPE_MAPPED_FILE: - typeName = L"Mapped file"; - break; - case PH_MODULE_TYPE_MAPPED_IMAGE: - typeName = L"Mapped image"; - break; - default: - typeName = L"DLL"; - break; - } - - searchResult = PhAllocate(sizeof(PHP_OBJECT_SEARCH_RESULT)); - searchResult->ProcessId = (HANDLE)Context; - searchResult->ResultType = (Module->Type == PH_MODULE_TYPE_MAPPED_FILE || Module->Type == PH_MODULE_TYPE_MAPPED_IMAGE) ? MappedFileSearchResult : ModuleSearchResult; - searchResult->Handle = (HANDLE)Module->BaseAddress; - searchResult->TypeName = PhCreateString(typeName); - PhSetReference(&searchResult->Name, Module->FileName); - PhPrintPointer(searchResult->HandleString, Module->BaseAddress); - memset(&searchResult->Info, 0, sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX)); - - PhAcquireQueuedLockExclusive(&SearchResultsLock); - - PhAddItemList(SearchResults, searchResult); - - // Update the search results in batches of 40. - if (SearchResults->Count % 40 == 0) - PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_UPDATE, 0, 0); - - PhReleaseQueuedLockExclusive(&SearchResultsLock); - } - - PhDereferenceObject(upperFileName); - - return TRUE; -} - -static NTSTATUS PhpFindObjectsThreadStart( - _In_ PVOID Parameter - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PSYSTEM_HANDLE_INFORMATION_EX handles; - PPH_HASHTABLE processHandleHashtable; - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - ULONG i; - - // Refuse to search with no filter. - if (SearchString->Length == 0) - goto Exit; - - // Try to get a search pointer from the search string. - UseSearchPointer = PhStringToInteger64(&SearchString->sr, 0, &SearchPointer); - - _wcsupr(SearchString->Buffer); - - if (NT_SUCCESS(status = PhEnumHandlesEx(&handles))) - { - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static ULONG fileObjectTypeIndex = -1; - - BOOLEAN useWorkQueue = FALSE; - PH_WORK_QUEUE workQueue; - processHandleHashtable = PhCreateSimpleHashtable(8); - - if (!KphIsConnected() && WindowsVersion >= WINDOWS_VISTA) - { - useWorkQueue = TRUE; - PhInitializeWorkQueue(&workQueue, 1, 20, 1000); - - if (PhBeginInitOnce(&initOnce)) - { - UNICODE_STRING fileTypeName; - - RtlInitUnicodeString(&fileTypeName, L"File"); - fileObjectTypeIndex = PhGetObjectTypeNumber(&fileTypeName); - PhEndInitOnce(&initOnce); - } - } - - for (i = 0; i < handles->NumberOfHandles; i++) - { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = &handles->Handles[i]; - PVOID *processHandlePtr; - HANDLE processHandle; - - if (SearchStop) - break; - - // Open a handle to the process if we don't already have one. - - processHandlePtr = PhFindItemSimpleHashtable( - processHandleHashtable, - (PVOID)handleInfo->UniqueProcessId - ); - - if (processHandlePtr) - { - processHandle = (HANDLE)*processHandlePtr; - } - else - { - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_DUP_HANDLE, - (HANDLE)handleInfo->UniqueProcessId - ))) - { - PhAddItemSimpleHashtable( - processHandleHashtable, - (PVOID)handleInfo->UniqueProcessId, - processHandle - ); - } - else - { - continue; - } - } - - if (useWorkQueue && handleInfo->ObjectTypeIndex == (USHORT)fileObjectTypeIndex) - { - PSEARCH_HANDLE_CONTEXT searchHandleContext; - - searchHandleContext = PhAllocate(sizeof(SEARCH_HANDLE_CONTEXT)); - searchHandleContext->NeedToFree = TRUE; - searchHandleContext->HandleInfo = handleInfo; - searchHandleContext->ProcessHandle = processHandle; - PhQueueItemWorkQueue(&workQueue, SearchHandleFunction, searchHandleContext); - } - else - { - SEARCH_HANDLE_CONTEXT searchHandleContext; - - searchHandleContext.NeedToFree = FALSE; - searchHandleContext.HandleInfo = handleInfo; - searchHandleContext.ProcessHandle = processHandle; - SearchHandleFunction(&searchHandleContext); - } - } - - if (useWorkQueue) - { - PhWaitForWorkQueue(&workQueue); - PhDeleteWorkQueue(&workQueue); - } - - { - PPH_KEY_VALUE_PAIR entry; - - i = 0; - - while (PhEnumHashtable(processHandleHashtable, &entry, &i)) - NtClose((HANDLE)entry->Value); - } - - PhDereferenceObject(processHandleHashtable); - PhFree(handles); - } - - if (NT_SUCCESS(PhEnumProcesses(&processes))) - { - process = PH_FIRST_PROCESS(processes); - - do - { - PhEnumGenericModules( - process->UniqueProcessId, - NULL, - PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES, - EnumModulesCallback, - (PVOID)process->UniqueProcessId - ); - } while (process = PH_NEXT_PROCESS(process)); - - PhFree(processes); - } - -Exit: - PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_FINISHED, status, 0); - - return STATUS_SUCCESS; -} +/* + * Process Hacker - + * object search + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pcre/pcre2.h" + +#define WM_PH_SEARCH_SHOWDIALOG (WM_APP + 801) +#define WM_PH_SEARCH_FINISHED (WM_APP + 802) +#define WM_PH_SEARCH_SHOWMENU (WM_APP + 803) + +static HANDLE PhFindObjectsThreadHandle = NULL; +static HWND PhFindObjectsWindowHandle = NULL; +static PH_EVENT PhFindObjectsInitializedEvent = PH_EVENT_INIT; + +typedef struct _PH_HANDLE_SEARCH_CONTEXT +{ + PH_LAYOUT_MANAGER LayoutManager; + RECT MinimumSize; + + HWND WindowHandle; + HWND TreeNewHandle; + HWND TypeWindowHandle; + HWND SearchWindowHandle; + + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; + + HANDLE UpdateTimerHandle; + HANDLE SearchThreadHandle; + + BOOLEAN SearchStop; + PPH_STRING SearchString; + PPH_STRING SearchTypeString; + pcre2_code *SearchRegexCompiledExpression; + pcre2_match_data *SearchRegexMatchData; + PPH_LIST SearchResults; + ULONG SearchResultsAddIndex; + PH_QUEUED_LOCK SearchResultsLock; + ULONG64 SearchPointer; + BOOLEAN UseSearchPointer; +} PH_HANDLE_SEARCH_CONTEXT, *PPH_HANDLE_SEARCH_CONTEXT; + +typedef enum _PHP_OBJECT_RESULT_TYPE +{ + HandleSearchResult, + ModuleSearchResult, + MappedFileSearchResult +} PHP_OBJECT_RESULT_TYPE; + +typedef struct _PHP_OBJECT_SEARCH_RESULT +{ + HANDLE ProcessId; + PHP_OBJECT_RESULT_TYPE ResultType; + + HANDLE Handle; + PVOID Object; + PPH_STRING TypeName; + PPH_STRING ObjectName; + PPH_STRING BestObjectName; + + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Info; +} PHP_OBJECT_SEARCH_RESULT, *PPHP_OBJECT_SEARCH_RESULT; + +typedef enum _PH_HANDLE_OBJECT_TREE_COLUMN_ITEM_NAME +{ + PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS, + PH_OBJECT_SEARCH_TREE_COLUMN_TYPE, + PH_OBJECT_SEARCH_TREE_COLUMN_NAME, + PH_OBJECT_SEARCH_TREE_COLUMN_HANDLE, + PH_OBJECT_SEARCH_TREE_COLUMN_OBJECTADDRESS, + PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME, + PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM +} PH_HANDLE_OBJECT_TREE_COLUMN_ITEM_NAME; + +typedef struct _PH_HANDLE_OBJECT_TREE_ROOT_NODE +{ + PH_TREENEW_NODE Node; + ULONG64 UniqueId; // used to stabilize sorting + PHP_OBJECT_RESULT_TYPE ResultType; + PVOID HandleObject; + HANDLE Handle; + HANDLE ProcessId; + PPH_STRING ClientIdName; + PPH_STRING TypeNameString; + PPH_STRING ObjectNameString; + PPH_STRING BestObjectName; + WCHAR HandleString[PH_PTR_STR_LEN_1]; + WCHAR ObjectString[PH_PTR_STR_LEN_1]; + + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleInfo; + + PH_STRINGREF TextCache[PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM]; +} PH_HANDLE_OBJECT_TREE_ROOT_NODE, *PPH_HANDLE_OBJECT_TREE_ROOT_NODE; + +#define SORT_FUNCTION(Column) PhpHandleObjectTreeNewCompare##Column +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpHandleObjectTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_HANDLE_OBJECT_TREE_ROOT_NODE node1 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)_elem1; \ + PPH_HANDLE_OBJECT_TREE_ROOT_NODE node2 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)_elem2; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)node1->UniqueId, (ULONG_PTR)node2->UniqueId); \ + \ + return PhModifySort(sortResult, ((PPH_HANDLE_SEARCH_CONTEXT)_context)->TreeNewSortOrder); \ +} + +BEGIN_SORT_FUNCTION(Process) +{ + sortResult = PhCompareString(node1->ClientIdName, node2->ClientIdName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Type) +{ + sortResult = PhCompareString(node1->TypeNameString, node2->TypeNameString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareString(node1->BestObjectName, node2->BestObjectName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Handle) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ObjectAddress) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->HandleObject, (ULONG_PTR)node2->HandleObject); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(OriginalName) +{ + sortResult = PhCompareString(node1->ObjectNameString, node2->ObjectNameString, TRUE); +} +END_SORT_FUNCTION + +VOID PhpHandleObjectLoadSettingsTreeList( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhGetStringSetting(L"FindObjTreeListColumns"); + PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); + PhDereferenceObject(settings); +} + +VOID PhpHandleObjectSaveSettingsTreeList( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhCmSaveSettings(Context->TreeNewHandle); + PhSetStringSetting2(L"FindObjTreeListColumns", &settings->sr); + PhDereferenceObject(settings); +} + +BOOLEAN PhpHandleObjectNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_HANDLE_OBJECT_TREE_ROOT_NODE node1 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE *)Entry1; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE node2 = *(PPH_HANDLE_OBJECT_TREE_ROOT_NODE *)Entry2; + + return node1->HandleObject == node2->HandleObject; +} + +ULONG PhpHandleObjectNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)Entry)->Handle); +} + +VOID PhpDestroyHandleObjectNode( + _In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node + ) +{ + PhClearReference(&Node->ClientIdName); + PhClearReference(&Node->TypeNameString); + PhClearReference(&Node->ObjectNameString); + PhClearReference(&Node->BestObjectName); + + PhDereferenceObject(Node); +} + +PPH_HANDLE_OBJECT_TREE_ROOT_NODE PhpAddHandleObjectNode( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ HANDLE Handle + ) +{ + static ULONG64 NextUniqueId = 0; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; + + handleObjectNode = PhCreateAlloc(sizeof(PH_HANDLE_OBJECT_TREE_ROOT_NODE)); + memset(handleObjectNode, 0, sizeof(PH_HANDLE_OBJECT_TREE_ROOT_NODE)); + PhInitializeTreeNewNode(&handleObjectNode->Node); + + memset(handleObjectNode->TextCache, 0, sizeof(PH_STRINGREF) * PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM); + handleObjectNode->Node.TextCache = handleObjectNode->TextCache; + handleObjectNode->Node.TextCacheSize = PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM; + + handleObjectNode->Handle = Handle; + handleObjectNode->UniqueId = ++NextUniqueId; + + PhAddEntryHashtable(Context->NodeHashtable, &handleObjectNode); + PhAddItemList(Context->NodeList, handleObjectNode); + + //TreeNew_NodesStructured(Context->TreeNewHandle); + + return handleObjectNode; +} + +PPH_HANDLE_OBJECT_TREE_ROOT_NODE PhpFindHandleObjectNode( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ HANDLE Handle + ) +{ + PH_HANDLE_OBJECT_TREE_ROOT_NODE lookupHandleObjectNode; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE lookupHandleObjectNodePtr = &lookupHandleObjectNode; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNode; + + lookupHandleObjectNode.Handle = Handle; + + handleObjectNode = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE*)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupHandleObjectNodePtr + ); + + if (handleObjectNode) + return *handleObjectNode; + else + return NULL; +} + +VOID PhpRemoveHandleObjectNode( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node + ) +{ + ULONG index = 0; + + PhRemoveEntryHashtable(Context->NodeHashtable, &Node); + + if ((index = PhFindItemList(Context->NodeList, Node)) != ULONG_MAX) + { + PhRemoveItemList(Context->NodeList, index); + } + + PhpDestroyHandleObjectNode(Node); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhpUpdateHandleObjectNode( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE Node + ) +{ + memset(Node->TextCache, 0, sizeof(PH_STRINGREF) * PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM); + + PhInvalidateTreeNewNode(&Node->Node, TN_CACHE_COLOR); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +BOOLEAN NTAPI PhpHandleObjectTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_HANDLE_SEARCH_CONTEXT context = Context; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE node; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)getChildren->Node; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Process), + SORT_FUNCTION(Type), + SORT_FUNCTION(Name), + SORT_FUNCTION(Handle), + SORT_FUNCTION(ObjectAddress), + SORT_FUNCTION(OriginalName) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortColumn < PH_OBJECT_SEARCH_TREE_COLUMN_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1; + node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)isLeaf->Node; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1; + node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)getCellText->Node; + + switch (getCellText->Id) + { + case PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS: + getCellText->Text = PhGetStringRef(node->ClientIdName); + break; + case PH_OBJECT_SEARCH_TREE_COLUMN_TYPE: + getCellText->Text = PhGetStringRef(node->TypeNameString); + break; + case PH_OBJECT_SEARCH_TREE_COLUMN_NAME: + getCellText->Text = PhGetStringRef(node->BestObjectName); + break; + case PH_OBJECT_SEARCH_TREE_COLUMN_HANDLE: + PhInitializeStringRefLongHint(&getCellText->Text, node->HandleString); + break; + case PH_OBJECT_SEARCH_TREE_COLUMN_OBJECTADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, node->ObjectString); + break; + case PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME: + getCellText->Text = PhGetStringRef(node->ObjectNameString); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)getNodeColor->Node; + + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(context->TreeNewHandle, 0, -1); + break; + case VK_DELETE: + SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_CLOSE, 0); + break; + } + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_PROPERTIES, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + SendMessage( + context->WindowHandle, + WM_COMMAND, + WM_PH_SEARCH_SHOWMENU, + (LPARAM)contextMenuEvent + ); + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + } + + return FALSE; +} + +VOID PhpClearHandleObjectTree( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + PhpDestroyHandleObjectNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +PPH_HANDLE_OBJECT_TREE_ROOT_NODE PhpGetSelectedHandleObjectNode( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + PPH_HANDLE_OBJECT_TREE_ROOT_NODE windowNode = NULL; + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + windowNode = Context->NodeList->Items[i]; + + if (windowNode->Node.Selected) + return windowNode; + } + + return NULL; +} + +VOID PhpGetSelectedHandleObjectNodes( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _Out_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE **HandleObjectNodes, + _Out_ PULONG NumberOfHandleObjectNodes + ) +{ + PPH_LIST list; + + list = PhCreateList(2); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PPH_HANDLE_OBJECT_TREE_ROOT_NODE node = (PPH_HANDLE_OBJECT_TREE_ROOT_NODE)Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *HandleObjectNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfHandleObjectNodes = list->Count; + + PhDereferenceObject(list); +} + +VOID PhpInitializeHandleObjectTree( + _Inout_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + Context->NodeList = PhCreateList(100); + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_HANDLE_OBJECT_TREE_ROOT_NODE), + PhpHandleObjectNodeHashtableEqualFunction, + PhpHandleObjectNodeHashtableHashFunction, + 100 + ); + + PhSetControlTheme(Context->TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(Context->TreeNewHandle, PhpHandleObjectTreeNewCallback, Context); + + // Default columns + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_PROCESS, TRUE, L"Process", 100, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, 2, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_HANDLE, TRUE, L"Handle", 80, PH_ALIGN_LEFT, 3, 0); + + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_OBJECTADDRESS, FALSE, L"Object address", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_OBJECT_SEARCH_TREE_COLUMN_ORIGINALNAME, FALSE, L"Original name", 200, PH_ALIGN_LEFT, ULONG_MAX, 0); + + TreeNew_SetTriState(Context->TreeNewHandle, TRUE); + + PhpHandleObjectLoadSettingsTreeList(Context); +} + +VOID PhpDeleteHandleObjectTree( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + PhpHandleObjectSaveSettingsTreeList(Context); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PhpDestroyHandleObjectNode(Context->NodeList->Items[i]); + } + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +VOID PhpInitializeFindObjMenu( + _In_ PPH_EMENU Menu, + _In_ PPH_HANDLE_OBJECT_TREE_ROOT_NODE *Results, + _In_ ULONG NumberOfResults + ) +{ + BOOLEAN allCanBeClosed = TRUE; + ULONG i; + + if (NumberOfResults == 1) + { + PH_HANDLE_ITEM_INFO info; + + info.ProcessId = Results[0]->ProcessId; + info.Handle = Results[0]->Handle; + info.TypeName = Results[0]->TypeNameString; + info.BestObjectName = Results[0]->BestObjectName; + PhInsertHandleObjectPropertiesEMenuItems(Menu, ID_OBJECT_PROPERTIES, FALSE, &info); + } + else + { + PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhEnableEMenuItem(Menu, ID_OBJECT_COPY, TRUE); + } + + for (i = 0; i < NumberOfResults; i++) + { + if (Results[i]->ResultType != HandleSearchResult) + { + allCanBeClosed = FALSE; + break; + } + } + + PhEnableEMenuItem(Menu, ID_OBJECT_CLOSE, allCanBeClosed); +} + +static int __cdecl PhpStringObjectTypeCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_STRING entry1 = *(PPH_STRING *)elem1; + PPH_STRING entry2 = *(PPH_STRING *)elem2; + + return PhCompareString(entry1, entry2, TRUE); +} + +VOID PhpPopulateObjectTypes( + _In_ HWND FilterTypeCombo + ) +{ + POBJECT_TYPES_INFORMATION objectTypes; + POBJECT_TYPE_INFORMATION objectType; + PPH_LIST objectTypeList; + + objectTypeList = PhCreateList(100); + + // Add a custom object type for searching all objects. + ComboBox_AddString(FilterTypeCombo, L"Everything"); + ComboBox_SetCurSel(FilterTypeCombo, 0); + + // Enumerate the available object types. + if (NT_SUCCESS(PhEnumObjectTypes(&objectTypes))) + { + objectType = PH_FIRST_OBJECT_TYPE(objectTypes); + + for (ULONG i = 0; i < objectTypes->NumberOfTypes; i++) + { + PhAddItemList(objectTypeList, PhCreateStringFromUnicodeString(&objectType->TypeName)); + objectType = PH_NEXT_OBJECT_TYPE(objectType); + } + + PhFree(objectTypes); + } + + // Sort the object types. + qsort(objectTypeList->Items, objectTypeList->Count, sizeof(PVOID), PhpStringObjectTypeCompare); + + { + LONG maxLength; + HDC comboDc; + + maxLength = 0; + comboDc = GetDC(FilterTypeCombo); + + SetWindowFont(FilterTypeCombo, PhApplicationFont, TRUE); + + for (ULONG i = 0; i < objectTypeList->Count; i++) + { + PPH_STRING entry = objectTypeList->Items[i]; + SIZE textSize; + + if (GetTextExtentPoint32(comboDc, entry->Buffer, (ULONG)entry->Length / sizeof(WCHAR), &textSize)) + { + if (textSize.cx > maxLength) + maxLength = textSize.cx; + } + + ComboBox_AddString(FilterTypeCombo, PhGetString(objectTypeList->Items[i])); + PhDereferenceObject(objectTypeList->Items[i]); + } + + ReleaseDC(FilterTypeCombo, comboDc); + + if (maxLength) + { + SendMessage(FilterTypeCombo, CB_SETDROPPEDWIDTH, maxLength, 0); + } + } + + PhDereferenceObject(objectTypeList); +} + +VOID PhpFindObjectAddResultEntries( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + ULONG i; + + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); + + PhAcquireQueuedLockExclusive(&Context->SearchResultsLock); + + for (i = Context->SearchResultsAddIndex; i < Context->SearchResults->Count; i++) + { + PPHP_OBJECT_SEARCH_RESULT searchResult = Context->SearchResults->Items[i]; + CLIENT_ID clientId; + PPH_PROCESS_ITEM processItem; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE objectNode; + + clientId.UniqueProcess = searchResult->ProcessId; + clientId.UniqueThread = NULL; + + processItem = PhReferenceProcessItem(clientId.UniqueProcess); + + objectNode = PhpAddHandleObjectNode(Context, searchResult->Handle); + objectNode->ProcessId = searchResult->ProcessId; + objectNode->ResultType = searchResult->ResultType; + objectNode->ClientIdName = PhGetClientIdNameEx(&clientId, processItem ? processItem->ProcessName : NULL); + objectNode->TypeNameString = searchResult->TypeName; + objectNode->ObjectNameString = searchResult->ObjectName; + objectNode->BestObjectName = searchResult->BestObjectName; + objectNode->HandleInfo = searchResult->Info; + PhPrintPointer(objectNode->HandleString, searchResult->Handle); + + if (searchResult->Object) + { + objectNode->HandleObject = searchResult->Object; + PhPrintPointer(objectNode->ObjectString, searchResult->Object); + } + + if (processItem) + { + PhDereferenceObject(processItem); + } + } + + Context->SearchResultsAddIndex = i; + + PhReleaseQueuedLockExclusive(&Context->SearchResultsLock); + + TreeNew_NodesStructured(Context->TreeNewHandle); + TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); +} + +VOID PhpFindObjectClearResultEntries( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context + ) +{ + PhpClearHandleObjectTree(Context); + + Context->SearchResultsAddIndex = 0; + + for (ULONG i = 0; i < Context->SearchResults->Count; i++) + PhFree(Context->SearchResults->Items[i]); + + PhClearList(Context->SearchResults); +} + +VOID CALLBACK PhpFindObjectTreeUpdateCallback( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ BOOLEAN TimerOrWaitFired + ) +{ + if (!Context->SearchThreadHandle) + { + RtlUpdateTimer(PhGetGlobalTimerQueue(), Context->UpdateTimerHandle, 1000, INFINITE); + return; + } + + // Update the search results. + PhpFindObjectAddResultEntries(Context); + + RtlUpdateTimer(PhGetGlobalTimerQueue(), Context->UpdateTimerHandle, 1000, INFINITE); +} + +static BOOLEAN MatchSearchString( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PPH_STRINGREF Input + ) +{ + if (Context->SearchRegexCompiledExpression && Context->SearchRegexMatchData) + { + return pcre2_match( + Context->SearchRegexCompiledExpression, + Input->Buffer, + Input->Length / sizeof(WCHAR), + 0, + 0, + Context->SearchRegexMatchData, + NULL + ) >= 0; + } + else + { + return PhFindStringInStringRef(Input, &Context->SearchString->sr, TRUE) != -1; + } +} + +static BOOLEAN MatchTypeString( + _In_ PPH_HANDLE_SEARCH_CONTEXT Context, + _In_ PPH_STRINGREF Input + ) +{ + if (PhEqualString2(Context->SearchTypeString, L"Everything", FALSE)) + return TRUE; + + return PhEqualStringRef(Input, &Context->SearchTypeString->sr, TRUE); +} + +typedef struct _SEARCH_HANDLE_CONTEXT +{ + PPH_HANDLE_SEARCH_CONTEXT WindowContext; + BOOLEAN NeedToFree; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleInfo; + HANDLE ProcessHandle; +} SEARCH_HANDLE_CONTEXT, *PSEARCH_HANDLE_CONTEXT; + +static NTSTATUS NTAPI SearchHandleFunction( + _In_ PVOID Parameter + ) +{ + PSEARCH_HANDLE_CONTEXT handleContext = Parameter; + PPH_HANDLE_SEARCH_CONTEXT context = handleContext->WindowContext; + PPH_STRING typeName; + PPH_STRING objectName; + PPH_STRING bestObjectName; + + if (NT_SUCCESS(PhGetHandleInformation( + handleContext->ProcessHandle, + (HANDLE)handleContext->HandleInfo->HandleValue, + handleContext->HandleInfo->ObjectTypeIndex, + NULL, + &typeName, + &objectName, + &bestObjectName + ))) + { + PPH_STRING upperObjectName; + PPH_STRING upperBestObjectName; + PPH_STRING upperTypeName; + + upperObjectName = PhDuplicateString(objectName); + _wcsupr(upperObjectName->Buffer); + + upperBestObjectName = PhDuplicateString(bestObjectName); + _wcsupr(upperBestObjectName->Buffer); + + upperTypeName = PhDuplicateString(typeName); + _wcsupr(upperTypeName->Buffer); + + if (((MatchSearchString(context, &upperObjectName->sr) || MatchSearchString(context, &upperBestObjectName->sr)) && MatchTypeString(context, &upperTypeName->sr)) || + (context->UseSearchPointer && (handleContext->HandleInfo->Object == (PVOID)context->SearchPointer || handleContext->HandleInfo->HandleValue == context->SearchPointer))) + { + PPHP_OBJECT_SEARCH_RESULT searchResult; + + searchResult = PhAllocateZero(sizeof(PHP_OBJECT_SEARCH_RESULT)); + searchResult->ProcessId = (HANDLE)handleContext->HandleInfo->UniqueProcessId; + searchResult->ResultType = HandleSearchResult; + searchResult->Object = handleContext->HandleInfo->Object; + searchResult->Handle = (HANDLE)handleContext->HandleInfo->HandleValue; + searchResult->TypeName = typeName; + searchResult->ObjectName = objectName; + searchResult->BestObjectName = bestObjectName; + searchResult->Info = *handleContext->HandleInfo; + + PhAcquireQueuedLockExclusive(&context->SearchResultsLock); + PhAddItemList(context->SearchResults, searchResult); + PhReleaseQueuedLockExclusive(&context->SearchResultsLock); + } + else + { + PhDereferenceObject(typeName); + PhDereferenceObject(objectName); + PhDereferenceObject(bestObjectName); + } + + PhDereferenceObject(upperTypeName); + PhDereferenceObject(upperBestObjectName); + PhDereferenceObject(upperObjectName); + } + + if (handleContext->NeedToFree) + PhFree(handleContext); + + return STATUS_SUCCESS; +} + +typedef struct _SEARCH_MODULE_CONTEXT +{ + PPH_HANDLE_SEARCH_CONTEXT WindowContext; + HANDLE ProcessId; +} SEARCH_MODULE_CONTEXT, *PSEARCH_MODULE_CONTEXT; + +static BOOLEAN NTAPI EnumModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PSEARCH_MODULE_CONTEXT moduleContext = Context; + PPH_HANDLE_SEARCH_CONTEXT context = moduleContext->WindowContext; + PPH_STRING upperFileName; + PPH_STRING upperOriginalFileName; + + upperFileName = PhDuplicateString(Module->FileName); + _wcsupr(upperFileName->Buffer); + + upperOriginalFileName = PhDuplicateString(Module->OriginalFileName); + _wcsupr(upperOriginalFileName->Buffer); + + if ((MatchSearchString(context, &upperFileName->sr) || MatchSearchString(context, &upperOriginalFileName->sr)) || + (context->UseSearchPointer && Module->BaseAddress == (PVOID)context->SearchPointer)) + { + PPHP_OBJECT_SEARCH_RESULT searchResult; + PWSTR typeName; + + switch (Module->Type) + { + case PH_MODULE_TYPE_MAPPED_FILE: + typeName = L"Mapped file"; + break; + case PH_MODULE_TYPE_MAPPED_IMAGE: + typeName = L"Mapped image"; + break; + default: + typeName = L"DLL"; + break; + } + + searchResult = PhAllocate(sizeof(PHP_OBJECT_SEARCH_RESULT)); + memset(searchResult, 0, sizeof(PHP_OBJECT_SEARCH_RESULT)); + + searchResult->ProcessId = moduleContext->ProcessId; + searchResult->ResultType = (Module->Type == PH_MODULE_TYPE_MAPPED_FILE || Module->Type == PH_MODULE_TYPE_MAPPED_IMAGE) ? MappedFileSearchResult : ModuleSearchResult; + searchResult->Handle = (HANDLE)Module->BaseAddress; + searchResult->TypeName = PhCreateString(typeName); + PhSetReference(&searchResult->BestObjectName, Module->FileName); + PhSetReference(&searchResult->ObjectName, Module->OriginalFileName); + + PhAcquireQueuedLockExclusive(&context->SearchResultsLock); + PhAddItemList(context->SearchResults, searchResult); + PhReleaseQueuedLockExclusive(&context->SearchResultsLock); + } + + PhDereferenceObject(upperOriginalFileName); + PhDereferenceObject(upperFileName); + + return TRUE; +} + +NTSTATUS PhpFindObjectsThreadStart( + _In_ PVOID Parameter + ) +{ + PPH_HANDLE_SEARCH_CONTEXT context = Parameter; + NTSTATUS status = STATUS_SUCCESS; + PSYSTEM_HANDLE_INFORMATION_EX handles; + PPH_HASHTABLE processHandleHashtable; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + ULONG i; + + // Refuse to search with no filter. + if (context->SearchString->Length == 0) + goto Exit; + + // Try to get a search pointer from the search string. + context->UseSearchPointer = PhStringToInteger64(&context->SearchString->sr, 0, &context->SearchPointer); + + _wcsupr(context->SearchString->Buffer); + + if (NT_SUCCESS(status = PhEnumHandlesEx(&handles))) + { + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static ULONG fileObjectTypeIndex = ULONG_MAX; + + BOOLEAN useWorkQueue = FALSE; + PH_WORK_QUEUE workQueue; + processHandleHashtable = PhCreateSimpleHashtable(8); + + if (!KphIsConnected()) + { + useWorkQueue = TRUE; + PhInitializeWorkQueue(&workQueue, 1, 20, 1000); + + if (PhBeginInitOnce(&initOnce)) + { + UNICODE_STRING fileTypeName = RTL_CONSTANT_STRING(L"File"); + + fileObjectTypeIndex = PhGetObjectTypeNumber(&fileTypeName); + + PhEndInitOnce(&initOnce); + } + } + + for (i = 0; i < handles->NumberOfHandles; i++) + { + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = &handles->Handles[i]; + PVOID *processHandlePtr; + HANDLE processHandle; + + // Don't continue if the user requested cancellation. + if (context->SearchStop) + break; + + // Open a handle to the process if we don't already have one. + + processHandlePtr = PhFindItemSimpleHashtable( + processHandleHashtable, + (PVOID)handleInfo->UniqueProcessId + ); + + if (processHandlePtr) + { + processHandle = (HANDLE)*processHandlePtr; + } + else + { + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + (HANDLE)handleInfo->UniqueProcessId + ))) + { + PhAddItemSimpleHashtable( + processHandleHashtable, + (PVOID)handleInfo->UniqueProcessId, + processHandle + ); + } + else + { + continue; + } + } + + if (useWorkQueue && handleInfo->ObjectTypeIndex == (USHORT)fileObjectTypeIndex) + { + PSEARCH_HANDLE_CONTEXT searchHandleContext; + + searchHandleContext = PhAllocate(sizeof(SEARCH_HANDLE_CONTEXT)); + searchHandleContext->WindowContext = context; + searchHandleContext->NeedToFree = TRUE; + searchHandleContext->HandleInfo = handleInfo; + searchHandleContext->ProcessHandle = processHandle; + + PhQueueItemWorkQueue(&workQueue, SearchHandleFunction, searchHandleContext); + } + else + { + SEARCH_HANDLE_CONTEXT searchHandleContext; + + searchHandleContext.WindowContext = context; + searchHandleContext.NeedToFree = FALSE; + searchHandleContext.HandleInfo = handleInfo; + searchHandleContext.ProcessHandle = processHandle; + + SearchHandleFunction(&searchHandleContext); + } + } + + if (useWorkQueue) + { + PhWaitForWorkQueue(&workQueue); + PhDeleteWorkQueue(&workQueue); + } + + { + PPH_KEY_VALUE_PAIR entry; + + i = 0; + + while (PhEnumHashtable(processHandleHashtable, &entry, &i)) + NtClose((HANDLE)entry->Value); + } + + PhDereferenceObject(processHandleHashtable); + PhFree(handles); + } + + if (context->SearchStop) + goto Exit; + + if (PhEqualString2(context->SearchTypeString, L"File", TRUE) || + PhEqualString2(context->SearchTypeString, L"Everything", FALSE)) + { + if (NT_SUCCESS(PhEnumProcesses(&processes))) + { + process = PH_FIRST_PROCESS(processes); + + do + { + SEARCH_MODULE_CONTEXT searchModuleContext; + + if (context->SearchStop) + break; + + searchModuleContext.WindowContext = context; + searchModuleContext.ProcessId = process->UniqueProcessId; + + PhEnumGenericModules( + process->UniqueProcessId, + NULL, + PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES, + EnumModulesCallback, + &searchModuleContext + ); + } while (process = PH_NEXT_PROCESS(process)); + + PhFree(processes); + } + } + +Exit: + PostMessage(context->WindowHandle, WM_PH_SEARCH_FINISHED, status, 0); + + PhDereferenceObject(context); + return STATUS_SUCCESS; +} + +INT_PTR CALLBACK PhpFindObjectsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_HANDLE_SEARCH_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhCreateAlloc(sizeof(PH_HANDLE_SEARCH_CONTEXT)); + memset(context, 0, sizeof(PH_HANDLE_SEARCH_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + context->WindowHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_TREELIST); + context->TypeWindowHandle = GetDlgItem(hwndDlg, IDC_FILTERTYPE); + context->SearchWindowHandle = GetDlgItem(hwndDlg, IDC_FILTER); + + PhRegisterDialog(hwndDlg); + PhCreateSearchControl(hwndDlg, context->SearchWindowHandle, L"Find Handles or DLLs"); + PhpPopulateObjectTypes(context->TypeWindowHandle); + PhpInitializeHandleObjectTree(context); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->TypeWindowHandle, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP); + PhAddLayoutItem(&context->LayoutManager, context->SearchWindowHandle, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REGEX), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); + + context->MinimumSize.left = 0; + context->MinimumSize.top = 0; + context->MinimumSize.right = 300; + context->MinimumSize.bottom = 100; + MapDialogRect(hwndDlg, &context->MinimumSize); + + if (PhGetIntegerPairSetting(L"FindObjWindowPosition").X) + PhLoadWindowPlacementFromSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + + PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + + context->SearchResults = PhCreateList(128); + context->SearchResultsAddIndex = 0; + + RtlCreateTimer( + PhGetGlobalTimerQueue(), + &context->UpdateTimerHandle, + PhpFindObjectTreeUpdateCallback, + context, + 0, + 1000, + 0 + ); + + Edit_SetSel(context->SearchWindowHandle, 0, -1); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_REGEX), PhGetIntegerSetting(L"FindObjRegex") ? BST_CHECKED : BST_UNCHECKED); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + context->SearchStop = TRUE; + + if (context->UpdateTimerHandle) + { + RtlDeleteTimer(PhGetGlobalTimerQueue(), context->UpdateTimerHandle, NULL); + context->UpdateTimerHandle = NULL; + } + + if (context->SearchThreadHandle) + { + NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL); + NtClose(context->SearchThreadHandle); + context->SearchThreadHandle = NULL; + } + + PhClearReference(&context->SearchString); + PhClearReference(&context->SearchTypeString); + + if (context->SearchRegexCompiledExpression) + { + pcre2_code_free(context->SearchRegexCompiledExpression); + context->SearchRegexCompiledExpression = NULL; + } + + if (context->SearchRegexMatchData) + { + pcre2_match_data_free(context->SearchRegexMatchData); + context->SearchRegexMatchData = NULL; + } + + PhSetIntegerSetting(L"FindObjRegex", Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED); + PhSaveWindowPlacementToSetting(L"FindObjWindowPosition", L"FindObjWindowSize", hwndDlg); + + PhUnregisterWindowCallback(hwndDlg); + + PhDeleteLayoutManager(&context->LayoutManager); + + PhpDeleteHandleObjectTree(context); + + if (context->SearchResults) + { + for (ULONG i = 0; i < context->SearchResults->Count; i++) + PhFree(context->SearchResults->Items[i]); + + PhClearList(context->SearchResults); + } + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhDereferenceObject(context); + + PostQuitMessage(0); + } + break; + case WM_PH_SEARCH_SHOWDIALOG: + { + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); + + SetForegroundWindow(hwndDlg); + } + break; + case WM_SETCURSOR: + { + if (context->SearchThreadHandle) + { + SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + break; + case WM_COMMAND: + { + if (GET_WM_COMMAND_HWND(wParam, lParam) == context->TypeWindowHandle) + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case CBN_SELCHANGE: + { + // Change focus from the dropdown list to the searchbox. + SetFocus(context->SearchWindowHandle); + } + break; + } + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDOK: + { + // Don't continue if the user requested cancellation. + if (context->SearchStop) + break; + + if (!context->SearchThreadHandle) + { + PhMoveReference(&context->SearchString, PhGetWindowText(context->SearchWindowHandle)); + PhMoveReference(&context->SearchTypeString, PhGetWindowText(context->TypeWindowHandle)); + + if (context->SearchRegexCompiledExpression) + { + pcre2_code_free(context->SearchRegexCompiledExpression); + context->SearchRegexCompiledExpression = NULL; + } + + if (context->SearchRegexMatchData) + { + pcre2_match_data_free(context->SearchRegexMatchData); + context->SearchRegexMatchData = NULL; + } + + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_REGEX)) == BST_CHECKED) + { + int errorCode; + PCRE2_SIZE errorOffset; + + context->SearchRegexCompiledExpression = pcre2_compile( + context->SearchString->Buffer, + context->SearchString->Length / sizeof(WCHAR), + PCRE2_CASELESS | PCRE2_DOTALL, + &errorCode, + &errorOffset, + NULL + ); + + if (!context->SearchRegexCompiledExpression) + { + PhShowError2(hwndDlg, L"Unable to compile the regular expression.", L"\"%s\" at position %zu.", + PhGetStringOrDefault(PH_AUTO(PhPcre2GetErrorMessage(errorCode)), L"Unknown error"), + errorOffset + ); + break; + } + + context->SearchRegexMatchData = pcre2_match_data_create_from_pattern(context->SearchRegexCompiledExpression, NULL); + } + + // Clean up previous results. + + PhpFindObjectClearResultEntries(context); + + // Start the search. + + PhReferenceObject(context); + + if (!NT_SUCCESS(PhCreateThreadEx(&context->SearchThreadHandle, PhpFindObjectsThreadStart, context))) + { + PhDereferenceObject(context); + break; + } + + PhSetDialogItemText(hwndDlg, IDOK, L"Cancel"); + + SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); + } + else + { + context->SearchStop = TRUE; + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + } + } + break; + case IDCANCEL: + { + DestroyWindow(hwndDlg); + } + break; + case WM_PH_SEARCH_SHOWMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNodes = NULL; + ULONG numberOfHandleObjectNodes = 0; + + PhpGetSelectedHandleObjectNodes(context, &handleObjectNodes, &numberOfHandleObjectNodes); + + if (numberOfHandleObjectNodes != 0) + { + menu = PhCreateEMenu(); + PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_FINDOBJ), 0); + PhInsertCopyCellEMenuItem(menu, ID_OBJECT_COPY, context->TreeNewHandle, contextMenuEvent->Column); + PhSetFlagsEMenuItem(menu, ID_OBJECT_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); + PhpInitializeFindObjMenu(menu, handleObjectNodes, numberOfHandleObjectNodes); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + + PhDestroyEMenu(menu); + } + + PhFree(handleObjectNodes); + } + break; + case ID_OBJECT_CLOSE: + { + PPH_HANDLE_OBJECT_TREE_ROOT_NODE *handleObjectNodes = NULL; + ULONG numberOfHandleObjectNodes = 0; + + PhpGetSelectedHandleObjectNodes(context, &handleObjectNodes, &numberOfHandleObjectNodes); + + if (numberOfHandleObjectNodes != 0 && PhShowConfirmMessage( + hwndDlg, + L"close", + numberOfHandleObjectNodes == 1 ? L"the selected handle" : L"the selected handles", + L"Closing handles may cause system instability and data corruption.", + FALSE + )) + { + for (ULONG i = 0; i < numberOfHandleObjectNodes; i++) + { + NTSTATUS status; + HANDLE processHandle; + + if (handleObjectNodes[i]->ResultType != HandleSearchResult) + continue; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + handleObjectNodes[i]->ProcessId + ))) + { + if (NT_SUCCESS(status = NtDuplicateObject( + processHandle, + handleObjectNodes[i]->Handle, + NULL, + NULL, + 0, + 0, + DUPLICATE_CLOSE_SOURCE + ))) + { + PhpRemoveHandleObjectNode(context, handleObjectNodes[i]); + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + if (!PhShowContinueStatus(hwndDlg, + PhaFormatString(L"Unable to close \"%s\"", PhGetStringOrDefault(handleObjectNodes[i]->BestObjectName, L"??"))->Buffer, + status, + 0 + )) + break; + } + } + } + + PhFree(handleObjectNodes); + } + break; + case ID_HANDLE_OBJECTPROPERTIES1: + case ID_HANDLE_OBJECTPROPERTIES2: + { + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; + + if (handleObjectNode = PhpGetSelectedHandleObjectNode(context)) + { + PH_HANDLE_ITEM_INFO info; + + info.ProcessId = handleObjectNode->ProcessId; + info.Handle = handleObjectNode->Handle; + info.TypeName = handleObjectNode->TypeNameString; + info.BestObjectName = handleObjectNode->BestObjectName; + + if (GET_WM_COMMAND_ID(wParam, lParam) == ID_HANDLE_OBJECTPROPERTIES1) + PhShowHandleObjectProperties1(hwndDlg, &info); + else + PhShowHandleObjectProperties2(hwndDlg, &info); + } + } + break; + case ID_OBJECT_GOTOOWNINGPROCESS: + { + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; + + if (handleObjectNode = PhpGetSelectedHandleObjectNode(context)) + { + PPH_PROCESS_NODE processNode; + + if (processNode = PhFindProcessNode(handleObjectNode->ProcessId)) + { + ProcessHacker_SelectTabPage(PhMainWndHandle, 0); + ProcessHacker_SelectProcessNode(PhMainWndHandle, processNode); + ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); + } + } + } + break; + case ID_OBJECT_PROPERTIES: + { + PPH_HANDLE_OBJECT_TREE_ROOT_NODE handleObjectNode; + + if (handleObjectNode = PhpGetSelectedHandleObjectNode(context)) + { + if (handleObjectNode->ResultType == HandleSearchResult) + { + PPH_HANDLE_ITEM handleItem; + + handleItem = PhCreateHandleItem(&handleObjectNode->HandleInfo); + + if (!PhIsNullOrEmptyString(handleObjectNode->BestObjectName)) + { + handleItem->BestObjectName = handleItem->ObjectName = handleObjectNode->BestObjectName; + PhReferenceObjectEx(handleObjectNode->BestObjectName, 2); + } + + if (!PhIsNullOrEmptyString(handleObjectNode->TypeNameString)) + { + handleItem->TypeName = handleObjectNode->TypeNameString; + PhReferenceObject(handleObjectNode->TypeNameString); + } + + PhShowHandleProperties( + hwndDlg, + handleObjectNode->ProcessId, + handleItem + ); + PhDereferenceObject(handleItem); + } + else + { + // DLL or Mapped File. Just show file properties. + PhShellProperties(hwndDlg, handleObjectNode->BestObjectName->Buffer); + } + } + } + break; + case ID_OBJECT_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom); + } + break; + case WM_PH_SEARCH_FINISHED: + { + // Add any un-added items. + PhpFindObjectAddResultEntries(context); + + NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL); + NtClose(context->SearchThreadHandle); + context->SearchThreadHandle = NULL; + context->SearchStop = FALSE; + + PhSetDialogItemText(hwndDlg, IDOK, L"Find"); + EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); + + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + if ((NTSTATUS)wParam == STATUS_INSUFFICIENT_RESOURCES) + { + PhShowWarning( + hwndDlg, + L"Unable to search for handles because the total number of handles on the system is too large. " + L"Please check if there are any processes with an extremely large number of handles open." + ); + } + } + break; + } + + return FALSE; +} + +NTSTATUS PhpFindObjectsDialogThreadStart( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + PhFindObjectsWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_FINDOBJECTS), + NULL, + PhpFindObjectsDlgProc + ); + + PhSetEvent(&PhFindObjectsInitializedEvent); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(PhFindObjectsWindowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + PhResetEvent(&PhFindObjectsInitializedEvent); + + if (PhFindObjectsThreadHandle) + { + NtClose(PhFindObjectsThreadHandle); + PhFindObjectsThreadHandle = NULL; + } + + return STATUS_SUCCESS; +} + +VOID PhShowFindObjectsDialog( + VOID + ) +{ + if (!PhFindObjectsThreadHandle) + { + if (!NT_SUCCESS(PhCreateThreadEx(&PhFindObjectsThreadHandle, PhpFindObjectsDialogThreadStart, NULL))) + { + PhShowError(PhMainWndHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&PhFindObjectsInitializedEvent, NULL); + } + + PostMessage(PhFindObjectsWindowHandle, WM_PH_SEARCH_SHOWDIALOG, 0, 0); +} diff --git a/ProcessHacker/gdihndl.c b/ProcessHacker/gdihndl.c index 69184df2e480..b667b7b58331 100644 --- a/ProcessHacker/gdihndl.c +++ b/ProcessHacker/gdihndl.c @@ -1,382 +1,393 @@ -/* - * Process Hacker - - * GDI handles dialog - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -typedef struct _GDI_HANDLES_CONTEXT -{ - PPH_PROCESS_ITEM ProcessItem; - PPH_LIST List; -} GDI_HANDLES_CONTEXT, *PGDI_HANDLES_CONTEXT; - -typedef struct _PH_GDI_HANDLE_ITEM -{ - PGDI_HANDLE_ENTRY Entry; - ULONG Handle; - PVOID Object; - PWSTR TypeName; - PPH_STRING Information; -} PH_GDI_HANDLE_ITEM, *PPH_GDI_HANDLE_ITEM; - -INT_PTR CALLBACK PhpGdiHandlesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowGdiHandlesDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - GDI_HANDLES_CONTEXT context; - ULONG i; - - context.ProcessItem = ProcessItem; - context.List = PhCreateList(20); - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_GDIHANDLES), - ParentWindowHandle, - PhpGdiHandlesDlgProc, - (LPARAM)&context - ); - - for (i = 0; i < context.List->Count; i++) - { - PPH_GDI_HANDLE_ITEM gdiHandleItem = context.List->Items[i]; - - if (gdiHandleItem->Information) - PhDereferenceObject(gdiHandleItem->Information); - - PhFree(context.List->Items[i]); - } - - PhDereferenceObject(context.List); -} - -PWSTR PhpGetGdiHandleTypeName( - _In_ ULONG Unique - ) -{ - switch (GDI_CLIENT_TYPE_FROM_UNIQUE(Unique)) - { - case GDI_CLIENT_ALTDC_TYPE: - return L"Alt. DC"; - case GDI_CLIENT_BITMAP_TYPE: - return L"Bitmap"; - case GDI_CLIENT_BRUSH_TYPE: - return L"Brush"; - case GDI_CLIENT_CLIENTOBJ_TYPE: - return L"Client Object"; - case GDI_CLIENT_DIBSECTION_TYPE: - return L"DIB Section"; - case GDI_CLIENT_DC_TYPE: - return L"DC"; - case GDI_CLIENT_EXTPEN_TYPE: - return L"ExtPen"; - case GDI_CLIENT_FONT_TYPE: - return L"Font"; - case GDI_CLIENT_METADC16_TYPE: - return L"Metafile DC"; - case GDI_CLIENT_METAFILE_TYPE: - return L"Enhanced Metafile"; - case GDI_CLIENT_METAFILE16_TYPE: - return L"Metafile"; - case GDI_CLIENT_PALETTE_TYPE: - return L"Palette"; - case GDI_CLIENT_PEN_TYPE: - return L"Pen"; - case GDI_CLIENT_REGION_TYPE: - return L"Region"; - default: - return NULL; - } -} - -PPH_STRING PhpGetGdiHandleInformation( - _In_ ULONG Handle - ) -{ - HGDIOBJ handle; - - handle = (HGDIOBJ)UlongToPtr(Handle); - - switch (GDI_CLIENT_TYPE_FROM_HANDLE(Handle)) - { - case GDI_CLIENT_BITMAP_TYPE: - case GDI_CLIENT_DIBSECTION_TYPE: - { - BITMAP bitmap; - - if (GetObject(handle, sizeof(BITMAP), &bitmap)) - { - return PhFormatString( - L"Width: %u, Height: %u, Depth: %u", - bitmap.bmWidth, - bitmap.bmHeight, - bitmap.bmBitsPixel - ); - } - } - break; - case GDI_CLIENT_BRUSH_TYPE: - { - LOGBRUSH brush; - - if (GetObject(handle, sizeof(LOGBRUSH), &brush)) - { - return PhFormatString( - L"Style: %u, Color: 0x%08x, Hatch: 0x%Ix", - brush.lbStyle, - _byteswap_ulong(brush.lbColor), - brush.lbHatch - ); - } - } - break; - case GDI_CLIENT_EXTPEN_TYPE: - { - EXTLOGPEN pen; - - if (GetObject(handle, sizeof(EXTLOGPEN), &pen)) - { - return PhFormatString( - L"Style: 0x%x, Width: %u, Color: 0x%08x", - pen.elpPenStyle, - pen.elpWidth, - _byteswap_ulong(pen.elpColor) - ); - } - } - break; - case GDI_CLIENT_FONT_TYPE: - { - LOGFONT font; - - if (GetObject(handle, sizeof(LOGFONT), &font)) - { - return PhFormatString( - L"Face: %s, Height: %d", - font.lfFaceName, - font.lfHeight - ); - } - } - break; - case GDI_CLIENT_PALETTE_TYPE: - { - USHORT count; - - if (GetObject(handle, sizeof(USHORT), &count)) - { - return PhFormatString( - L"Entries: %u", - (ULONG)count - ); - } - } - break; - case GDI_CLIENT_PEN_TYPE: - { - LOGPEN pen; - - if (GetObject(handle, sizeof(LOGPEN), &pen)) - { - return PhFormatString( - L"Style: %u, Width: %u, Color: 0x%08x", - pen.lopnStyle, - pen.lopnWidth.x, - _byteswap_ulong(pen.lopnColor) - ); - } - } - break; - } - - return NULL; -} - -VOID PhpRefreshGdiHandles( - _In_ HWND hwndDlg, - _In_ PGDI_HANDLES_CONTEXT Context - ) -{ - HWND lvHandle; - ULONG i; - PGDI_SHARED_MEMORY gdiShared; - USHORT processId; - PGDI_HANDLE_ENTRY handle; - PPH_GDI_HANDLE_ITEM gdiHandleItem; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - ExtendedListView_SetRedraw(lvHandle, FALSE); - ListView_DeleteAllItems(lvHandle); - - for (i = 0; i < Context->List->Count; i++) - { - gdiHandleItem = Context->List->Items[i]; - - if (gdiHandleItem->Information) - PhDereferenceObject(gdiHandleItem->Information); - - PhFree(Context->List->Items[i]); - } - - PhClearList(Context->List); - - gdiShared = (PGDI_SHARED_MEMORY)NtCurrentPeb()->GdiSharedHandleTable; - processId = (USHORT)Context->ProcessItem->ProcessId; - - for (i = 0; i < GDI_MAX_HANDLE_COUNT; i++) - { - PWSTR typeName; - INT lvItemIndex; - WCHAR pointer[PH_PTR_STR_LEN_1]; - - handle = &gdiShared->Handles[i]; - - if (handle->Owner.ProcessId != processId) - continue; - - typeName = PhpGetGdiHandleTypeName(handle->Unique); - - if (!typeName) - continue; - - gdiHandleItem = PhAllocate(sizeof(PH_GDI_HANDLE_ITEM)); - gdiHandleItem->Entry = handle; - gdiHandleItem->Handle = GDI_MAKE_HANDLE(i, handle->Unique); - gdiHandleItem->Object = handle->Object; - gdiHandleItem->TypeName = typeName; - gdiHandleItem->Information = PhpGetGdiHandleInformation(gdiHandleItem->Handle); - PhAddItemList(Context->List, gdiHandleItem); - - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, gdiHandleItem->TypeName, gdiHandleItem); - PhPrintPointer(pointer, UlongToPtr(gdiHandleItem->Handle)); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); - PhPrintPointer(pointer, gdiHandleItem->Object); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, PhGetString(gdiHandleItem->Information)); - } - - ExtendedListView_SortItems(lvHandle); - ExtendedListView_SetRedraw(lvHandle, TRUE); -} - -INT NTAPI PhpGdiHandleHandleCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PPH_GDI_HANDLE_ITEM item1 = Item1; - PPH_GDI_HANDLE_ITEM item2 = Item2; - - return uintcmp(item1->Handle, item2->Handle); -} - -INT NTAPI PhpGdiHandleObjectCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PPH_GDI_HANDLE_ITEM item1 = Item1; - PPH_GDI_HANDLE_ITEM item2 = Item2; - - return uintptrcmp((ULONG_PTR)item1->Object, (ULONG_PTR)item2->Object); -} - -INT_PTR CALLBACK PhpGdiHandlesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PGDI_HANDLES_CONTEXT context = (PGDI_HANDLES_CONTEXT)lParam; - HWND lvHandle; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 100, L"Type"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Handle"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 102, L"Object"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 200, L"Information"); - - PhSetExtendedListView(lvHandle); - ExtendedListView_SetCompareFunction(lvHandle, 1, PhpGdiHandleHandleCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 2, PhpGdiHandleObjectCompareFunction); - ExtendedListView_AddFallbackColumn(lvHandle, 0); - ExtendedListView_AddFallbackColumn(lvHandle, 1); - - PhpRefreshGdiHandles(hwndDlg, context); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_REFRESH: - { - PhpRefreshGdiHandles(hwndDlg, (PGDI_HANDLES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom())); - } - break; - } - } - break; - case WM_NOTIFY: - { - PhHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * GDI handles dialog + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +typedef struct _GDI_HANDLES_CONTEXT +{ + PPH_PROCESS_ITEM ProcessItem; + PPH_LIST List; +} GDI_HANDLES_CONTEXT, *PGDI_HANDLES_CONTEXT; + +typedef struct _PH_GDI_HANDLE_ITEM +{ + PGDI_HANDLE_ENTRY Entry; + ULONG Handle; + PVOID Object; + PWSTR TypeName; + PPH_STRING Information; +} PH_GDI_HANDLE_ITEM, *PPH_GDI_HANDLE_ITEM; + +INT_PTR CALLBACK PhpGdiHandlesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowGdiHandlesDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + GDI_HANDLES_CONTEXT context; + ULONG i; + + context.ProcessItem = ProcessItem; + context.List = PhCreateList(20); + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_GDIHANDLES), + ParentWindowHandle, + PhpGdiHandlesDlgProc, + (LPARAM)&context + ); + + for (i = 0; i < context.List->Count; i++) + { + PPH_GDI_HANDLE_ITEM gdiHandleItem = context.List->Items[i]; + + if (gdiHandleItem->Information) + PhDereferenceObject(gdiHandleItem->Information); + + PhFree(context.List->Items[i]); + } + + PhDereferenceObject(context.List); +} + +PWSTR PhpGetGdiHandleTypeName( + _In_ ULONG Unique + ) +{ + switch (GDI_CLIENT_TYPE_FROM_UNIQUE(Unique)) + { + case GDI_CLIENT_ALTDC_TYPE: + return L"Alt. DC"; + case GDI_CLIENT_BITMAP_TYPE: + return L"Bitmap"; + case GDI_CLIENT_BRUSH_TYPE: + return L"Brush"; + case GDI_CLIENT_CLIENTOBJ_TYPE: + return L"Client Object"; + case GDI_CLIENT_DIBSECTION_TYPE: + return L"DIB Section"; + case GDI_CLIENT_DC_TYPE: + return L"DC"; + case GDI_CLIENT_EXTPEN_TYPE: + return L"ExtPen"; + case GDI_CLIENT_FONT_TYPE: + return L"Font"; + case GDI_CLIENT_METADC16_TYPE: + return L"Metafile DC"; + case GDI_CLIENT_METAFILE_TYPE: + return L"Enhanced Metafile"; + case GDI_CLIENT_METAFILE16_TYPE: + return L"Metafile"; + case GDI_CLIENT_PALETTE_TYPE: + return L"Palette"; + case GDI_CLIENT_PEN_TYPE: + return L"Pen"; + case GDI_CLIENT_REGION_TYPE: + return L"Region"; + default: + return NULL; + } +} + +PPH_STRING PhpGetGdiHandleInformation( + _In_ ULONG Handle + ) +{ + HGDIOBJ handle; + + handle = (HGDIOBJ)UlongToPtr(Handle); + + switch (GDI_CLIENT_TYPE_FROM_HANDLE(Handle)) + { + case GDI_CLIENT_BITMAP_TYPE: + case GDI_CLIENT_DIBSECTION_TYPE: + { + BITMAP bitmap; + + if (GetObject(handle, sizeof(BITMAP), &bitmap)) + { + return PhFormatString( + L"Width: %u, Height: %u, Depth: %u", + bitmap.bmWidth, + bitmap.bmHeight, + bitmap.bmBitsPixel + ); + } + } + break; + case GDI_CLIENT_BRUSH_TYPE: + { + LOGBRUSH brush; + + if (GetObject(handle, sizeof(LOGBRUSH), &brush)) + { + return PhFormatString( + L"Style: %u, Color: 0x%08x, Hatch: 0x%Ix", + brush.lbStyle, + _byteswap_ulong(brush.lbColor), + brush.lbHatch + ); + } + } + break; + case GDI_CLIENT_EXTPEN_TYPE: + { + EXTLOGPEN pen; + + if (GetObject(handle, sizeof(EXTLOGPEN), &pen)) + { + return PhFormatString( + L"Style: 0x%x, Width: %u, Color: 0x%08x", + pen.elpPenStyle, + pen.elpWidth, + _byteswap_ulong(pen.elpColor) + ); + } + } + break; + case GDI_CLIENT_FONT_TYPE: + { + LOGFONT font; + + if (GetObject(handle, sizeof(LOGFONT), &font)) + { + return PhFormatString( + L"Face: %s, Height: %d", + font.lfFaceName, + font.lfHeight + ); + } + } + break; + case GDI_CLIENT_PALETTE_TYPE: + { + USHORT count; + + if (GetObject(handle, sizeof(USHORT), &count)) + { + return PhFormatString( + L"Entries: %u", + (ULONG)count + ); + } + } + break; + case GDI_CLIENT_PEN_TYPE: + { + LOGPEN pen; + + if (GetObject(handle, sizeof(LOGPEN), &pen)) + { + return PhFormatString( + L"Style: %u, Width: %u, Color: 0x%08x", + pen.lopnStyle, + pen.lopnWidth.x, + _byteswap_ulong(pen.lopnColor) + ); + } + } + break; + } + + return NULL; +} + +VOID PhpRefreshGdiHandles( + _In_ HWND hwndDlg, + _In_ PGDI_HANDLES_CONTEXT Context + ) +{ + HWND lvHandle; + ULONG i; + PGDI_SHARED_MEMORY gdiShared; + USHORT processId; + PGDI_HANDLE_ENTRY handle; + PPH_GDI_HANDLE_ITEM gdiHandleItem; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + + ExtendedListView_SetRedraw(lvHandle, FALSE); + ListView_DeleteAllItems(lvHandle); + + for (i = 0; i < Context->List->Count; i++) + { + gdiHandleItem = Context->List->Items[i]; + + if (gdiHandleItem->Information) + PhDereferenceObject(gdiHandleItem->Information); + + PhFree(Context->List->Items[i]); + } + + PhClearList(Context->List); + + gdiShared = (PGDI_SHARED_MEMORY)NtCurrentPeb()->GdiSharedHandleTable; + processId = (USHORT)Context->ProcessItem->ProcessId; + + for (i = 0; i < GDI_MAX_HANDLE_COUNT; i++) + { + PWSTR typeName; + INT lvItemIndex; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + handle = &gdiShared->Handles[i]; + + if (handle->Owner.ProcessId != processId) + continue; + + typeName = PhpGetGdiHandleTypeName(handle->Unique); + + if (!typeName) + continue; + + gdiHandleItem = PhAllocate(sizeof(PH_GDI_HANDLE_ITEM)); + gdiHandleItem->Entry = handle; + gdiHandleItem->Handle = GDI_MAKE_HANDLE(i, handle->Unique); + gdiHandleItem->Object = handle->Object; + gdiHandleItem->TypeName = typeName; + gdiHandleItem->Information = PhpGetGdiHandleInformation(gdiHandleItem->Handle); + PhAddItemList(Context->List, gdiHandleItem); + + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, gdiHandleItem->TypeName, gdiHandleItem); + PhPrintPointer(pointer, UlongToPtr(gdiHandleItem->Handle)); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); + PhPrintPointer(pointer, gdiHandleItem->Object); + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, PhGetString(gdiHandleItem->Information)); + } + + ExtendedListView_SortItems(lvHandle); + ExtendedListView_SetRedraw(lvHandle, TRUE); +} + +INT NTAPI PhpGdiHandleHandleCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ + PPH_GDI_HANDLE_ITEM item1 = Item1; + PPH_GDI_HANDLE_ITEM item2 = Item2; + + return uintcmp(item1->Handle, item2->Handle); +} + +INT NTAPI PhpGdiHandleObjectCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ + PPH_GDI_HANDLE_ITEM item1 = Item1; + PPH_GDI_HANDLE_ITEM item2 = Item2; + + return uintptrcmp((ULONG_PTR)item1->Object, (ULONG_PTR)item2->Object); +} + +INT_PTR CALLBACK PhpGdiHandlesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PGDI_HANDLES_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PGDI_HANDLES_CONTEXT)lParam; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 100, L"Type"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Handle"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 102, L"Object"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 200, L"Information"); + + PhSetExtendedListView(lvHandle); + ExtendedListView_SetCompareFunction(lvHandle, 1, PhpGdiHandleHandleCompareFunction); + ExtendedListView_SetCompareFunction(lvHandle, 2, PhpGdiHandleObjectCompareFunction); + ExtendedListView_AddFallbackColumn(lvHandle, 0); + ExtendedListView_AddFallbackColumn(lvHandle, 1); + + PhpRefreshGdiHandles(hwndDlg, context); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_REFRESH: + { + PhpRefreshGdiHandles(hwndDlg, context); + } + break; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/hidnproc.c b/ProcessHacker/hidnproc.c index f28d5d5e260d..05bb15d90559 100644 --- a/ProcessHacker/hidnproc.c +++ b/ProcessHacker/hidnproc.c @@ -1,1249 +1,1308 @@ -/* - * Process Hacker - - * hidden processes detection - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * There are two methods of hidden process detection implemented in this module. - * - * Brute Force. This attempts to open all possible PIDs within a certain range - * in order to find processes which have been unlinked from the active process - * list (EPROCESS.ActiveProcessLinks). This method is not effective when - * either NtOpenProcess is hooked or PsLookupProcessByProcessId is hooked - * (KProcessHacker cannot bypass this). - * - * CSR Handles. This enumerates handles in all running CSR processes, and works - * even when a process has been unlinked from the active process list and - * has been removed from the client ID table (PspCidTable). However, the method - * does not detect native executables since CSR is not notified about them. - * Some rootkits hook NtQuerySystemInformation in order to modify the returned - * handle information; Process Hacker bypasses this by using KProcessHacker, - * which calls ExEnumHandleTable directly. Note that both process and thread - * handles are examined. - */ - -#include -#include - -#include -#include - -#include - -#include -#include -#include - -INT_PTR CALLBACK PhpHiddenProcessesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -COLORREF NTAPI PhpHiddenProcessesColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ); - -BOOLEAN NTAPI PhpHiddenProcessesCallback( - _In_ PPH_HIDDEN_PROCESS_ENTRY Process, - _In_opt_ PVOID Context - ); - -PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( - _In_ PPH_HIDDEN_PROCESS_ENTRY Entry - ); - -HWND PhHiddenProcessesWindowHandle = NULL; -HWND PhHiddenProcessesListViewHandle = NULL; -static PH_LAYOUT_MANAGER WindowLayoutManager; -static RECT MinimumSize; - -static PH_HIDDEN_PROCESS_METHOD ProcessesMethod; -static PPH_LIST ProcessesList = NULL; -static ULONG NumberOfHiddenProcesses; -static ULONG NumberOfTerminatedProcesses; - -VOID PhShowHiddenProcessesDialog( - VOID - ) -{ - if (!KphIsConnected()) - { - PhShowWarning( - PhMainWndHandle, - L"Hidden process detection cannot function properly without KProcessHacker. " - L"Make sure Process Hacker is running with administrative privileges." - ); - } - - if (!PhHiddenProcessesWindowHandle) - { - PhHiddenProcessesWindowHandle = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_HIDDENPROCESSES), - PhMainWndHandle, - PhpHiddenProcessesDlgProc - ); - } - - if (!IsWindowVisible(PhHiddenProcessesWindowHandle)) - ShowWindow(PhHiddenProcessesWindowHandle, SW_SHOW); - else - SetForegroundWindow(PhHiddenProcessesWindowHandle); -} - -static INT_PTR CALLBACK PhpHiddenProcessesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - PhHiddenProcessesListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_PROCESSES); - - PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_INTRO), - NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); - PhAddLayoutItem(&WindowLayoutManager, lvHandle, - NULL, PH_ANCHOR_ALL); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_DESCRIPTION), - NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM | PH_LAYOUT_FORCE_INVALIDATE); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_METHOD), - NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_TERMINATE), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SCAN), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = 330; - MinimumSize.bottom = 140; - MapDialogRect(hwndDlg, &MinimumSize); - - PhRegisterDialog(hwndDlg); - - PhLoadWindowPlacementFromSetting(L"HiddenProcessesWindowPosition", L"HiddenProcessesWindowSize", hwndDlg); - - PhSetListViewStyle(lvHandle, TRUE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 320, L"Process"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID"); - - PhSetExtendedListView(lvHandle); - PhLoadListViewColumnsFromSetting(L"HiddenProcessesListViewColumns", lvHandle); - ExtendedListView_AddFallbackColumn(lvHandle, 0); - ExtendedListView_AddFallbackColumn(lvHandle, 1); - ExtendedListView_SetItemColorFunction(lvHandle, PhpHiddenProcessesColorFunction); - - ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"Brute force"); - ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"CSR handles"); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_METHOD), L"CSR handles", FALSE); - - EnableWindow(GetDlgItem(hwndDlg, IDC_TERMINATE), FALSE); - } - break; - case WM_DESTROY: - { - PhSaveWindowPlacementToSetting(L"HiddenProcessesWindowPosition", L"HiddenProcessesWindowSize", hwndDlg); - PhSaveListViewColumnsToSetting(L"HiddenProcessesListViewColumns", PhHiddenProcessesListViewHandle); - } - break; - case WM_CLOSE: - { - // Hide, don't close. - ShowWindow(hwndDlg, SW_HIDE); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 0); - } - return TRUE; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - { - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - case IDC_SCAN: - { - NTSTATUS status; - PPH_STRING method; - - method = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_METHOD))); - - if (ProcessesList) - { - ULONG i; - - for (i = 0; i < ProcessesList->Count; i++) - { - PPH_HIDDEN_PROCESS_ENTRY entry = ProcessesList->Items[i]; - - if (entry->FileName) - PhDereferenceObject(entry->FileName); - - PhFree(entry); - } - - PhDereferenceObject(ProcessesList); - } - - ProcessesList = PhCreateList(40); - - ProcessesMethod = - PhEqualString2(method, L"Brute force", TRUE) ? - BruteForceScanMethod : - CsrHandlesScanMethod; - NumberOfHiddenProcesses = 0; - NumberOfTerminatedProcesses = 0; - - ExtendedListView_SetRedraw(PhHiddenProcessesListViewHandle, FALSE); - ListView_DeleteAllItems(PhHiddenProcessesListViewHandle); - status = PhEnumHiddenProcesses( - ProcessesMethod, - PhpHiddenProcessesCallback, - NULL - ); - ExtendedListView_SortItems(PhHiddenProcessesListViewHandle); - ExtendedListView_SetRedraw(PhHiddenProcessesListViewHandle, TRUE); - - if (NT_SUCCESS(status)) - { - SetDlgItemText(hwndDlg, IDC_DESCRIPTION, - PhaFormatString(L"%u hidden process(es), %u terminated process(es).", - NumberOfHiddenProcesses, NumberOfTerminatedProcesses)->Buffer - ); - InvalidateRect(GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, TRUE); - } - else - { - PhShowStatus(hwndDlg, L"Unable to perform the scan", status, 0); - } - } - break; - case IDC_TERMINATE: - { - PPH_HIDDEN_PROCESS_ENTRY *entries; - ULONG numberOfEntries; - ULONG i; - - PhGetSelectedListViewItemParams(PhHiddenProcessesListViewHandle, &entries, &numberOfEntries); - - if (numberOfEntries != 0) - { - if (!PhGetIntegerSetting(L"EnableWarnings") || - PhShowConfirmMessage( - hwndDlg, - L"terminate", - L"the selected process(es)", - L"Terminating a hidden process may cause the system to become unstable " - L"or crash.", - TRUE - )) - { - NTSTATUS status; - HANDLE processHandle; - BOOLEAN refresh; - - refresh = FALSE; - - for (i = 0; i < numberOfEntries; i++) - { - if (ProcessesMethod == BruteForceScanMethod) - { - status = PhOpenProcess( - &processHandle, - PROCESS_TERMINATE, - entries[i]->ProcessId - ); - } - else - { - status = PhOpenProcessByCsrHandles( - &processHandle, - PROCESS_TERMINATE, - entries[i]->ProcessId - ); - } - - if (NT_SUCCESS(status)) - { - status = PhTerminateProcess(processHandle, STATUS_SUCCESS); - NtClose(processHandle); - - if (NT_SUCCESS(status)) - refresh = TRUE; - } - else - { - PhShowStatus(hwndDlg, L"Unable to terminate the process", status, 0); - } - } - - if (refresh) - { - LARGE_INTEGER interval; - - // Sleep for a bit before continuing. It seems to help avoid - // BSODs. - interval.QuadPart = -250 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); - SendMessage(hwndDlg, WM_COMMAND, IDC_SCAN, 0); - } - } - } - - PhFree(entries); - } - break; - case IDC_SAVE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Text files (*.txt)", L"*.txt" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - - fileDialog = PhCreateSaveFileDialog(); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, L"Hidden Processes.txt"); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - NTSTATUS status; - PPH_STRING fileName; - PPH_FILE_STREAM fileStream; - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - - if (NT_SUCCESS(status = PhCreateFileStream( - &fileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - 0 - ))) - { - PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); - PhWritePhTextHeader(fileStream); - PhWriteStringAsUtf8FileStream2(fileStream, L"Method: "); - PhWriteStringAsUtf8FileStream2(fileStream, - ProcessesMethod == BruteForceScanMethod ? L"Brute Force\r\n" : L"CSR Handles\r\n"); - PhWriteStringFormatAsUtf8FileStream( - fileStream, - L"Hidden: %u\r\nTerminated: %u\r\n\r\n", - NumberOfHiddenProcesses, - NumberOfTerminatedProcesses - ); - - if (ProcessesList) - { - ULONG i; - - for (i = 0; i < ProcessesList->Count; i++) - { - PPH_HIDDEN_PROCESS_ENTRY entry = ProcessesList->Items[i]; - - if (entry->Type == HiddenProcess) - PhWriteStringAsUtf8FileStream2(fileStream, L"[HIDDEN] "); - else if (entry->Type == TerminatedProcess) - PhWriteStringAsUtf8FileStream2(fileStream, L"[Terminated] "); - else if (entry->Type != NormalProcess) - continue; - - PhWriteStringFormatAsUtf8FileStream( - fileStream, - L"%s (%u)\r\n", - entry->FileName->Buffer, - HandleToUlong(entry->ProcessId) - ); - } - } - - PhDereferenceObject(fileStream); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - PhHandleListViewNotifyBehaviors(lParam, PhHiddenProcessesListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - - switch (header->code) - { - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == PhHiddenProcessesListViewHandle) - { - EnableWindow( - GetDlgItem(hwndDlg, IDC_TERMINATE), - ListView_GetSelectedCount(PhHiddenProcessesListViewHandle) > 0 - ); - } - } - break; - case NM_DBLCLK: - { - if (header->hwndFrom == PhHiddenProcessesListViewHandle) - { - PPH_HIDDEN_PROCESS_ENTRY entry; - - entry = PhGetSelectedListViewItemParam(PhHiddenProcessesListViewHandle); - - if (entry) - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhpCreateProcessItemForHiddenProcess(entry)) - { - ProcessHacker_ShowProcessProperties(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - else - { - PhShowError(hwndDlg, L"Unable to create a process structure for the selected process."); - } - } - } - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&WindowLayoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - case WM_CTLCOLORSTATIC: - { - if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_DESCRIPTION)) - { - if (NumberOfHiddenProcesses != 0) - { - SetTextColor((HDC)wParam, RGB(0xff, 0x00, 0x00)); - } - - SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE)); - - return (INT_PTR)GetSysColorBrush(COLOR_3DFACE); - } - } - break; - } - - REFLECT_MESSAGE_DLG(hwndDlg, PhHiddenProcessesListViewHandle, uMsg, wParam, lParam); - - return FALSE; -} - -static COLORREF NTAPI PhpHiddenProcessesColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ) -{ - PPH_HIDDEN_PROCESS_ENTRY entry = Param; - - switch (entry->Type) - { - case UnknownProcess: - case HiddenProcess: - return RGB(0xff, 0x00, 0x00); - case TerminatedProcess: - return RGB(0x77, 0x77, 0x77); - } - - return GetSysColor(COLOR_WINDOW); -} - -static BOOLEAN NTAPI PhpHiddenProcessesCallback( - _In_ PPH_HIDDEN_PROCESS_ENTRY Process, - _In_opt_ PVOID Context - ) -{ - PPH_HIDDEN_PROCESS_ENTRY entry; - INT lvItemIndex; - WCHAR pidString[PH_INT32_STR_LEN_1]; - - entry = PhAllocateCopy(Process, sizeof(PH_HIDDEN_PROCESS_ENTRY)); - - if (entry->FileName) - PhReferenceObject(entry->FileName); - - PhAddItemList(ProcessesList, entry); - - lvItemIndex = PhAddListViewItem(PhHiddenProcessesListViewHandle, MAXINT, - PhGetStringOrDefault(entry->FileName, L"(unknown)"), entry); - PhPrintUInt32(pidString, HandleToUlong(entry->ProcessId)); - PhSetListViewSubItem(PhHiddenProcessesListViewHandle, lvItemIndex, 1, pidString); - - if (entry->Type == HiddenProcess) - NumberOfHiddenProcesses++; - else if (entry->Type == TerminatedProcess) - NumberOfTerminatedProcesses++; - - return TRUE; -} - -static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( - _In_ PPH_HIDDEN_PROCESS_ENTRY Entry - ) -{ - NTSTATUS status; - PPH_PROCESS_ITEM processItem; - PPH_PROCESS_ITEM idleProcessItem; - HANDLE processHandle; - PROCESS_BASIC_INFORMATION basicInfo; - KERNEL_USER_TIMES times; - PROCESS_PRIORITY_CLASS priorityClass; - ULONG handleCount; - HANDLE processHandle2; - - if (Entry->Type == NormalProcess) - { - processItem = PhReferenceProcessItem(Entry->ProcessId); - - if (processItem) - return processItem; - } - - processItem = PhCreateProcessItem(Entry->ProcessId); - - // Mark the process as terminated if necessary. - if (Entry->Type == TerminatedProcess) - processItem->State |= PH_PROCESS_ITEM_REMOVED; - - // We need a process record. Just use the record of System Idle Process. - if (idleProcessItem = PhReferenceProcessItem(SYSTEM_IDLE_PROCESS_ID)) - { - processItem->Record = idleProcessItem->Record; - PhReferenceProcessRecord(processItem->Record); - } - else - { - PhDereferenceObject(processItem); - return NULL; - } - - // Set up the file name and process name. - - PhSwapReference(&processItem->FileName, Entry->FileName); - - if (processItem->FileName) - { - processItem->ProcessName = PhGetBaseName(processItem->FileName); - } - else - { - processItem->ProcessName = PhCreateString(L"Unknown"); - } - - if (ProcessesMethod == BruteForceScanMethod) - { - status = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - Entry->ProcessId - ); - } - else - { - status = PhOpenProcessByCsrHandles( - &processHandle, - ProcessQueryAccess, - Entry->ProcessId - ); - } - - if (NT_SUCCESS(status)) - { - // Basic information and not-so-dynamic information - - processItem->QueryHandle = processHandle; - - if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo))) - { - processItem->ParentProcessId = basicInfo.InheritedFromUniqueProcessId; - processItem->BasePriority = basicInfo.BasePriority; - } - - PhGetProcessSessionId(processHandle, &processItem->SessionId); - - PhPrintUInt32(processItem->ParentProcessIdString, HandleToUlong(processItem->ParentProcessId)); - PhPrintUInt32(processItem->SessionIdString, processItem->SessionId); - - if (NT_SUCCESS(PhGetProcessTimes(processHandle, ×))) - { - processItem->CreateTime = times.CreateTime; - processItem->KernelTime = times.KernelTime; - processItem->UserTime = times.UserTime; - } - - // TODO: Token information? - - if (NT_SUCCESS(NtQueryInformationProcess( - processHandle, - ProcessPriorityClass, - &priorityClass, - sizeof(PROCESS_PRIORITY_CLASS), - NULL - ))) - { - processItem->PriorityClass = priorityClass.PriorityClass; - } - - if (NT_SUCCESS(NtQueryInformationProcess( - processHandle, - ProcessHandleCount, - &handleCount, - sizeof(ULONG), - NULL - ))) - { - processItem->NumberOfHandles = handleCount; - } - } - - // Stage 1 - // Some copy and paste magic here... - - if (processItem->FileName) - { - // Small icon, large icon. - ExtractIconEx( - processItem->FileName->Buffer, - 0, - &processItem->LargeIcon, - &processItem->SmallIcon, - 1 - ); - - // Version info. - PhInitializeImageVersionInfo(&processItem->VersionInfo, processItem->FileName->Buffer); - } - - // Use the default EXE icon if we didn't get the file's icon. - { - if (!processItem->SmallIcon || !processItem->LargeIcon) - { - if (processItem->SmallIcon) - { - DestroyIcon(processItem->SmallIcon); - processItem->SmallIcon = NULL; - } - else if (processItem->LargeIcon) - { - DestroyIcon(processItem->LargeIcon); - processItem->LargeIcon = NULL; - } - - PhGetStockApplicationIcon(&processItem->SmallIcon, &processItem->LargeIcon); - processItem->SmallIcon = CopyIcon(processItem->SmallIcon); - processItem->LargeIcon = CopyIcon(processItem->LargeIcon); - } - } - - // Command line - - status = PhOpenProcess( - &processHandle2, - ProcessQueryAccess | PROCESS_VM_READ, - Entry->ProcessId - ); - - if (NT_SUCCESS(status)) - { - PPH_STRING commandLine; - ULONG i; - - if (NT_SUCCESS(status = PhGetProcessCommandLine(processHandle2, &commandLine))) - { - // Some command lines (e.g. from taskeng.exe) have nulls in them. - // Since Windows can't display them, we'll replace them with - // spaces. - for (i = 0; i < (ULONG)commandLine->Length / 2; i++) - { - if (commandLine->Buffer[i] == 0) - commandLine->Buffer[i] = ' '; - } - } - - if (NT_SUCCESS(status)) - { - processItem->CommandLine = commandLine; - } - - NtClose(processHandle2); - } - - // TODO: Other stage 1 tasks. - - PhSetEvent(&processItem->Stage1Event); - - return processItem; -} - -NTSTATUS PhpEnumHiddenProcessesBruteForce( - _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - PPH_LIST pids; - ULONG pid; - BOOLEAN stop = FALSE; - - if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) - return status; - - pids = PhCreateList(40); - - process = PH_FIRST_PROCESS(processes); - - do - { - PhAddItemList(pids, process->UniqueProcessId); - } while (process = PH_NEXT_PROCESS(process)); - - PhFree(processes); - - for (pid = 8; pid <= 65536; pid += 4) - { - NTSTATUS status2; - HANDLE processHandle; - PH_HIDDEN_PROCESS_ENTRY entry; - KERNEL_USER_TIMES times; - PPH_STRING fileName; - - status2 = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - UlongToHandle(pid) - ); - - if (NT_SUCCESS(status2)) - { - entry.ProcessId = UlongToHandle(pid); - - if (NT_SUCCESS(status2 = PhGetProcessTimes( - processHandle, - × - )) && - NT_SUCCESS(status2 = PhGetProcessImageFileName( - processHandle, - &fileName - ))) - { - entry.FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - - if (times.ExitTime.QuadPart != 0) - entry.Type = TerminatedProcess; - else if (PhFindItemList(pids, UlongToHandle(pid)) != -1) - entry.Type = NormalProcess; - else - entry.Type = HiddenProcess; - - if (!Callback(&entry, Context)) - stop = TRUE; - - PhDereferenceObject(entry.FileName); - } - - NtClose(processHandle); - } - - // Use an alternative method if we don't have sufficient access. - if (status2 == STATUS_ACCESS_DENIED && WindowsVersion >= WINDOWS_VISTA) - { - if (NT_SUCCESS(status2 = PhGetProcessImageFileNameByProcessId(UlongToHandle(pid), &fileName))) - { - entry.ProcessId = UlongToHandle(pid); - entry.FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - - if (PhFindItemList(pids, UlongToHandle(pid)) != -1) - entry.Type = NormalProcess; - else - entry.Type = HiddenProcess; - - if (!Callback(&entry, Context)) - stop = TRUE; - - PhDereferenceObject(entry.FileName); - } - } - - if (status2 == STATUS_INVALID_CID || status2 == STATUS_INVALID_PARAMETER) - status2 = STATUS_SUCCESS; - - if (!NT_SUCCESS(status2)) - { - entry.ProcessId = UlongToHandle(pid); - entry.FileName = NULL; - entry.Type = UnknownProcess; - - if (!Callback(&entry, Context)) - stop = TRUE; - } - - if (stop) - break; - } - - PhDereferenceObject(pids); - - return status; -} - -typedef struct _CSR_HANDLES_CONTEXT -{ - PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback; - PVOID Context; - PPH_LIST Pids; -} CSR_HANDLES_CONTEXT, *PCSR_HANDLES_CONTEXT; - -static BOOLEAN NTAPI PhpCsrProcessHandlesCallback( - _In_ PPH_CSR_HANDLE_INFO Handle, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - BOOLEAN cont = TRUE; - PCSR_HANDLES_CONTEXT context = Context; - HANDLE processHandle; - KERNEL_USER_TIMES times; - PPH_STRING fileName; - PH_HIDDEN_PROCESS_ENTRY entry; - - entry.ProcessId = Handle->ProcessId; - - if (NT_SUCCESS(status = PhOpenProcessByCsrHandle( - &processHandle, - ProcessQueryAccess, - Handle - ))) - { - if (NT_SUCCESS(status = PhGetProcessTimes( - processHandle, - × - )) && - NT_SUCCESS(status = PhGetProcessImageFileName( - processHandle, - &fileName - ))) - { - entry.FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - - if (times.ExitTime.QuadPart != 0) - entry.Type = TerminatedProcess; - else if (PhFindItemList(context->Pids, Handle->ProcessId) != -1) - entry.Type = NormalProcess; - else - entry.Type = HiddenProcess; - - if (!context->Callback(&entry, context->Context)) - cont = FALSE; - - PhDereferenceObject(entry.FileName); - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - { - entry.FileName = NULL; - entry.Type = UnknownProcess; - - if (!context->Callback(&entry, context->Context)) - cont = FALSE; - } - - return cont; -} - -NTSTATUS PhpEnumHiddenProcessesCsrHandles( - _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - PPH_LIST pids; - CSR_HANDLES_CONTEXT context; - - if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) - return status; - - pids = PhCreateList(40); - - process = PH_FIRST_PROCESS(processes); - - do - { - PhAddItemList(pids, process->UniqueProcessId); - } while (process = PH_NEXT_PROCESS(process)); - - PhFree(processes); - - context.Callback = Callback; - context.Context = Context; - context.Pids = pids; - - status = PhEnumCsrProcessHandles(PhpCsrProcessHandlesCallback, &context); - - PhDereferenceObject(pids); - - return status; -} - -NTSTATUS PhEnumHiddenProcesses( - _In_ PH_HIDDEN_PROCESS_METHOD Method, - _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - if (Method == BruteForceScanMethod) - { - return PhpEnumHiddenProcessesBruteForce( - Callback, - Context - ); - } - else - { - return PhpEnumHiddenProcessesCsrHandles( - Callback, - Context - ); - } -} - -NTSTATUS PhpOpenCsrProcesses( - _Out_ PHANDLE *ProcessHandles, - _Out_ PULONG NumberOfProcessHandles - ) -{ - NTSTATUS status; - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - PPH_LIST processHandleList; - - if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) - return status; - - processHandleList = PhCreateList(8); - - process = PH_FIRST_PROCESS(processes); - - do - { - HANDLE processHandle; - PH_KNOWN_PROCESS_TYPE knownProcessType; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_DUP_HANDLE, - process->UniqueProcessId - ))) - { - if (NT_SUCCESS(PhGetProcessKnownType( - processHandle, - &knownProcessType - )) && - (knownProcessType & KnownProcessTypeMask) == WindowsSubsystemProcessType) - { - PhAddItemList(processHandleList, processHandle); - } - else - { - NtClose(processHandle); - } - } - } while (process = PH_NEXT_PROCESS(process)); - - PhFree(processes); - - *ProcessHandles = PhAllocateCopy(processHandleList->Items, processHandleList->Count * sizeof(HANDLE)); - *NumberOfProcessHandles = processHandleList->Count; - - PhDereferenceObject(processHandleList); - - return status; -} - -NTSTATUS PhpGetCsrHandleProcessId( - _Inout_ PPH_CSR_HANDLE_INFO Handle - ) -{ - NTSTATUS status; - PROCESS_BASIC_INFORMATION processBasicInfo; - THREAD_BASIC_INFORMATION threadBasicInfo; - - Handle->IsThreadHandle = FALSE; - Handle->ProcessId = NULL; - - // Assume the handle is a process handle, and get the - // process ID. - - status = KphQueryInformationObject( - Handle->CsrProcessHandle, - Handle->Handle, - KphObjectProcessBasicInformation, - &processBasicInfo, - sizeof(PROCESS_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - { - Handle->ProcessId = processBasicInfo.UniqueProcessId; - } - else - { - // We failed to get the process ID. Assume the handle - // is a thread handle, and get the process ID. - - status = KphQueryInformationObject( - Handle->CsrProcessHandle, - Handle->Handle, - KphObjectThreadBasicInformation, - &threadBasicInfo, - sizeof(THREAD_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - { - Handle->ProcessId = threadBasicInfo.ClientId.UniqueProcess; - Handle->IsThreadHandle = TRUE; - } - } - - return status; -} - -NTSTATUS PhEnumCsrProcessHandles( - _In_ PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PHANDLE csrProcessHandles; - ULONG numberOfCsrProcessHandles; - ULONG i; - BOOLEAN stop = FALSE; - PPH_LIST pids; - - if (!NT_SUCCESS(status = PhpOpenCsrProcesses( - &csrProcessHandles, - &numberOfCsrProcessHandles - ))) - return status; - - pids = PhCreateList(40); - - for (i = 0; i < numberOfCsrProcessHandles; i++) - { - PKPH_PROCESS_HANDLE_INFORMATION handles; - ULONG j; - - if (stop) - break; - - if (NT_SUCCESS(KphEnumerateProcessHandles2(csrProcessHandles[i], &handles))) - { - for (j = 0; j < handles->HandleCount; j++) - { - PH_CSR_HANDLE_INFO handle; - - handle.CsrProcessHandle = csrProcessHandles[i]; - handle.Handle = handles->Handles[j].Handle; - - // Get the process ID associated with the handle. - // This call will fail if the handle is not a - // process or thread handle. - if (!NT_SUCCESS(PhpGetCsrHandleProcessId(&handle))) - continue; - - // Avoid duplicate PIDs. - if (PhFindItemList(pids, handle.ProcessId) != -1) - continue; - - PhAddItemList(pids, handle.ProcessId); - - if (!Callback(&handle, Context)) - { - stop = TRUE; - break; - } - } - - PhFree(handles); - } - } - - PhDereferenceObject(pids); - - for (i = 0; i < numberOfCsrProcessHandles; i++) - NtClose(csrProcessHandles[i]); - - PhFree(csrProcessHandles); - - return status; -} - -NTSTATUS PhOpenProcessByCsrHandle( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PPH_CSR_HANDLE_INFO Handle - ) -{ - NTSTATUS status; - - if (!Handle->IsThreadHandle) - { - status = NtDuplicateObject( - Handle->CsrProcessHandle, - Handle->Handle, - NtCurrentProcess(), - ProcessHandle, - DesiredAccess, - 0, - 0 - ); - } - else - { - HANDLE threadHandle; - - if (!NT_SUCCESS(status = NtDuplicateObject( - Handle->CsrProcessHandle, - Handle->Handle, - NtCurrentProcess(), - &threadHandle, - ThreadQueryAccess, - 0, - 0 - ))) - return status; - - status = KphOpenThreadProcess( - threadHandle, - DesiredAccess, - ProcessHandle - ); - NtClose(threadHandle); - } - - return status; -} - -typedef struct _OPEN_PROCESS_BY_CSR_CONTEXT -{ - NTSTATUS Status; - PHANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - HANDLE ProcessId; -} OPEN_PROCESS_BY_CSR_CONTEXT, *POPEN_PROCESS_BY_CSR_CONTEXT; - -static BOOLEAN NTAPI PhpOpenProcessByCsrHandlesCallback( - _In_ PPH_CSR_HANDLE_INFO Handle, - _In_opt_ PVOID Context - ) -{ - POPEN_PROCESS_BY_CSR_CONTEXT context = Context; - - if (Handle->ProcessId == context->ProcessId) - { - context->Status = PhOpenProcessByCsrHandle( - context->ProcessHandle, - context->DesiredAccess, - Handle - ); - - return FALSE; - } - - return TRUE; -} - -NTSTATUS PhOpenProcessByCsrHandles( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ HANDLE ProcessId - ) -{ - NTSTATUS status; - OPEN_PROCESS_BY_CSR_CONTEXT context; - - context.Status = STATUS_INVALID_CID; - context.ProcessHandle = ProcessHandle; - context.DesiredAccess = DesiredAccess; - context.ProcessId = ProcessId; - - if (!NT_SUCCESS(status = PhEnumCsrProcessHandles( - PhpOpenProcessByCsrHandlesCallback, - &context - ))) - return status; - - return context.Status; -} +/* + * Process Hacker - + * hidden processes detection + * + * Copyright (C) 2010-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * There are two methods of hidden process detection implemented in this module. + * + * Brute Force. This attempts to open all possible PIDs within a certain range + * in order to find processes which have been unlinked from the active process + * list (EPROCESS.ActiveProcessLinks). This method is not effective when + * either NtOpenProcess is hooked or PsLookupProcessByProcessId is hooked + * (KProcessHacker cannot bypass this). + * + * CSR Handles. This enumerates handles in all running CSR processes, and works + * even when a process has been unlinked from the active process list and + * has been removed from the client ID table (PspCidTable). However, the method + * does not detect native executables since CSR is not notified about them. + * Some rootkits hook NtQuerySystemInformation in order to modify the returned + * handle information; Process Hacker bypasses this by using KProcessHacker, + * which calls ExEnumHandleTable directly. Note that both process and thread + * handles are examined. + */ + +#include +#include + +#include + +#include + +#include +#include +#include + +INT_PTR CALLBACK PhpHiddenProcessesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +COLORREF NTAPI PhpHiddenProcessesColorFunction( + _In_ INT Index, + _In_ PVOID Param, + _In_opt_ PVOID Context + ); + +BOOLEAN NTAPI PhpHiddenProcessesCallback( + _In_ PPH_HIDDEN_PROCESS_ENTRY Process, + _In_opt_ PVOID Context + ); + +PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( + _In_ PPH_HIDDEN_PROCESS_ENTRY Entry + ); + +static HWND PhHiddenProcessesWindowHandle = NULL; +static HWND PhHiddenProcessesListViewHandle = NULL; +static PH_LAYOUT_MANAGER WindowLayoutManager; +static RECT MinimumSize; + +static PH_HIDDEN_PROCESS_METHOD ProcessesMethod; +static PPH_LIST ProcessesList = NULL; +static ULONG NumberOfHiddenProcesses; +static ULONG NumberOfTerminatedProcesses; + +VOID PhShowHiddenProcessesDialog( + VOID + ) +{ + if (!KphIsConnected()) + { + PhShowWarning( + PhMainWndHandle, + L"Hidden process detection cannot function properly without KProcessHacker. " + L"Make sure Process Hacker is running with administrative privileges." + ); + } + + if (!PhHiddenProcessesWindowHandle) + { + PhHiddenProcessesWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_HIDDENPROCESSES), + NULL, + PhpHiddenProcessesDlgProc + ); + } + + if (!IsWindowVisible(PhHiddenProcessesWindowHandle)) + ShowWindow(PhHiddenProcessesWindowHandle, SW_SHOW); + else + SetForegroundWindow(PhHiddenProcessesWindowHandle); +} + +INT_PTR CALLBACK PhpHiddenProcessesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + PhHiddenProcessesListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_PROCESSES); + + PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_INTRO), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&WindowLayoutManager, lvHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM | PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_METHOD), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_TERMINATE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SCAN), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 320, L"Process"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID"); + + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"HiddenProcessesListViewColumns", lvHandle); + ExtendedListView_AddFallbackColumn(lvHandle, 0); + ExtendedListView_AddFallbackColumn(lvHandle, 1); + ExtendedListView_SetItemColorFunction(lvHandle, PhpHiddenProcessesColorFunction); + + ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"Brute force"); + ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"CSR handles"); + ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"Process handles"); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_METHOD), L"Process handles", FALSE); + + MinimumSize.left = 0; + MinimumSize.top = 0; + MinimumSize.right = 330; + MinimumSize.bottom = 140; + MapDialogRect(hwndDlg, &MinimumSize); + + PhLoadWindowPlacementFromSetting(L"HiddenProcessesWindowPosition", L"HiddenProcessesWindowSize", hwndDlg); + + EnableWindow(GetDlgItem(hwndDlg, IDC_TERMINATE), FALSE); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(L"HiddenProcessesWindowPosition", L"HiddenProcessesWindowSize", hwndDlg); + PhSaveListViewColumnsToSetting(L"HiddenProcessesListViewColumns", PhHiddenProcessesListViewHandle); + + PhHiddenProcessesWindowHandle = NULL; + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + { + DestroyWindow(hwndDlg); + } + break; + case IDC_SCAN: + { + NTSTATUS status; + PPH_STRING method; + + method = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_METHOD))); + + if (ProcessesList) + { + ULONG i; + + for (i = 0; i < ProcessesList->Count; i++) + { + PPH_HIDDEN_PROCESS_ENTRY entry = ProcessesList->Items[i]; + + if (entry->FileName) + PhDereferenceObject(entry->FileName); + + PhFree(entry); + } + + PhDereferenceObject(ProcessesList); + } + + ProcessesList = PhCreateList(40); + + if (PhEqualString2(method, L"Brute force", TRUE)) + ProcessesMethod = BruteForceScanMethod; + else if (PhEqualString2(method, L"CSR handles", TRUE)) + ProcessesMethod = CsrHandlesScanMethod; + else if (PhEqualString2(method, L"Process handles", TRUE)) + ProcessesMethod = ProcessHandleScanMethod; + + NumberOfHiddenProcesses = 0; + NumberOfTerminatedProcesses = 0; + + ExtendedListView_SetRedraw(PhHiddenProcessesListViewHandle, FALSE); + ListView_DeleteAllItems(PhHiddenProcessesListViewHandle); + status = PhEnumHiddenProcesses( + ProcessesMethod, + PhpHiddenProcessesCallback, + NULL + ); + ExtendedListView_SortItems(PhHiddenProcessesListViewHandle); + ExtendedListView_SetRedraw(PhHiddenProcessesListViewHandle, TRUE); + + if (NT_SUCCESS(status)) + { + PhSetDialogItemText(hwndDlg, IDC_DESCRIPTION, + PhaFormatString(L"%u hidden process(es), %u terminated process(es).", + NumberOfHiddenProcesses, NumberOfTerminatedProcesses)->Buffer + ); + InvalidateRect(GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, TRUE); + } + else + { + PhShowStatus(hwndDlg, L"Unable to perform the scan", status, 0); + } + } + break; + case IDC_TERMINATE: + { + PPH_HIDDEN_PROCESS_ENTRY *entries; + ULONG numberOfEntries; + ULONG i; + + PhGetSelectedListViewItemParams(PhHiddenProcessesListViewHandle, &entries, &numberOfEntries); + + if (numberOfEntries != 0) + { + if (!PhGetIntegerSetting(L"EnableWarnings") || + PhShowConfirmMessage( + hwndDlg, + L"terminate", + L"the selected process(es)", + L"Terminating a hidden process may cause the system to become unstable " + L"or crash.", + TRUE + )) + { + NTSTATUS status; + HANDLE processHandle; + BOOLEAN refresh; + + refresh = FALSE; + + for (i = 0; i < numberOfEntries; i++) + { + if (ProcessesMethod == BruteForceScanMethod) + { + status = PhOpenProcess( + &processHandle, + PROCESS_TERMINATE, + entries[i]->ProcessId + ); + } + else + { + status = PhOpenProcessByCsrHandles( + &processHandle, + PROCESS_TERMINATE, + entries[i]->ProcessId + ); + } + + if (NT_SUCCESS(status)) + { + status = PhTerminateProcess(processHandle, STATUS_SUCCESS); + NtClose(processHandle); + + if (NT_SUCCESS(status)) + refresh = TRUE; + } + else + { + PhShowStatus(hwndDlg, L"Unable to terminate the process", status, 0); + } + } + + if (refresh) + { + // Sleep for a bit before continuing. It seems to help avoid BSODs. + + PhDelayExecution(250); + + SendMessage(hwndDlg, WM_COMMAND, IDC_SCAN, 0); + } + } + } + + PhFree(entries); + } + break; + case IDC_SAVE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Text files (*.txt)", L"*.txt" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + + fileDialog = PhCreateSaveFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, L"Hidden Processes.txt"); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + NTSTATUS status; + PPH_STRING fileName; + PPH_FILE_STREAM fileStream; + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + + if (NT_SUCCESS(status = PhCreateFileStream( + &fileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + 0 + ))) + { + PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); + PhWritePhTextHeader(fileStream); + PhWriteStringAsUtf8FileStream2(fileStream, L"Method: "); + PhWriteStringAsUtf8FileStream2(fileStream, + ProcessesMethod == BruteForceScanMethod ? L"Brute Force\r\n" : L"CSR Handles\r\n"); + PhWriteStringFormatAsUtf8FileStream( + fileStream, + L"Hidden: %u\r\nTerminated: %u\r\n\r\n", + NumberOfHiddenProcesses, + NumberOfTerminatedProcesses + ); + + if (ProcessesList) + { + ULONG i; + + for (i = 0; i < ProcessesList->Count; i++) + { + PPH_HIDDEN_PROCESS_ENTRY entry = ProcessesList->Items[i]; + + if (entry->Type == HiddenProcess) + PhWriteStringAsUtf8FileStream2(fileStream, L"[HIDDEN] "); + else if (entry->Type == TerminatedProcess) + PhWriteStringAsUtf8FileStream2(fileStream, L"[Terminated] "); + else if (entry->Type != NormalProcess) + continue; + + PhWriteStringFormatAsUtf8FileStream( + fileStream, + L"%s (%u)\r\n", + entry->FileName->Buffer, + HandleToUlong(entry->ProcessId) + ); + } + } + + PhDereferenceObject(fileStream); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + PhHandleListViewNotifyBehaviors(lParam, PhHiddenProcessesListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + + switch (header->code) + { + case LVN_ITEMCHANGED: + { + if (header->hwndFrom == PhHiddenProcessesListViewHandle) + { + EnableWindow( + GetDlgItem(hwndDlg, IDC_TERMINATE), + ListView_GetSelectedCount(PhHiddenProcessesListViewHandle) > 0 + ); + } + } + break; + case NM_DBLCLK: + { + if (header->hwndFrom == PhHiddenProcessesListViewHandle) + { + PPH_HIDDEN_PROCESS_ENTRY entry; + + entry = PhGetSelectedListViewItemParam(PhHiddenProcessesListViewHandle); + + if (entry) + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhpCreateProcessItemForHiddenProcess(entry)) + { + ProcessHacker_ShowProcessProperties(PhMainWndHandle, processItem); + PhDereferenceObject(processItem); + } + else + { + PhShowError(hwndDlg, L"Unable to create a process structure for the selected process."); + } + } + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&WindowLayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + case WM_CTLCOLORSTATIC: + { + if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_DESCRIPTION)) + { + if (NumberOfHiddenProcesses != 0) + { + SetTextColor((HDC)wParam, RGB(0xff, 0x00, 0x00)); + } + + SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE)); + + return (INT_PTR)GetSysColorBrush(COLOR_3DFACE); + } + } + break; + } + + REFLECT_MESSAGE_DLG(hwndDlg, PhHiddenProcessesListViewHandle, uMsg, wParam, lParam); + + return FALSE; +} + +static COLORREF NTAPI PhpHiddenProcessesColorFunction( + _In_ INT Index, + _In_ PVOID Param, + _In_opt_ PVOID Context + ) +{ + PPH_HIDDEN_PROCESS_ENTRY entry = Param; + + switch (entry->Type) + { + case UnknownProcess: + case HiddenProcess: + return RGB(0xff, 0x00, 0x00); + case TerminatedProcess: + return RGB(0x77, 0x77, 0x77); + } + + return GetSysColor(COLOR_WINDOW); +} + +static BOOLEAN NTAPI PhpHiddenProcessesCallback( + _In_ PPH_HIDDEN_PROCESS_ENTRY Process, + _In_opt_ PVOID Context + ) +{ + PPH_HIDDEN_PROCESS_ENTRY entry; + INT lvItemIndex; + WCHAR pidString[PH_INT32_STR_LEN_1]; + + entry = PhAllocateCopy(Process, sizeof(PH_HIDDEN_PROCESS_ENTRY)); + + if (entry->FileName) + PhReferenceObject(entry->FileName); + + PhAddItemList(ProcessesList, entry); + + lvItemIndex = PhAddListViewItem(PhHiddenProcessesListViewHandle, MAXINT, + PhGetStringOrDefault(entry->FileName, L"(unknown)"), entry); + PhPrintUInt32(pidString, HandleToUlong(entry->ProcessId)); + PhSetListViewSubItem(PhHiddenProcessesListViewHandle, lvItemIndex, 1, pidString); + + if (entry->Type == HiddenProcess) + NumberOfHiddenProcesses++; + else if (entry->Type == TerminatedProcess) + NumberOfTerminatedProcesses++; + + return TRUE; +} + +static PPH_PROCESS_ITEM PhpCreateProcessItemForHiddenProcess( + _In_ PPH_HIDDEN_PROCESS_ENTRY Entry + ) +{ + NTSTATUS status; + PPH_PROCESS_ITEM processItem; + PPH_PROCESS_ITEM idleProcessItem; + HANDLE processHandle; + PROCESS_BASIC_INFORMATION basicInfo; + KERNEL_USER_TIMES times; + PROCESS_PRIORITY_CLASS priorityClass; + PROCESS_HANDLE_INFORMATION handleInfo; + HANDLE processHandle2; + + if (Entry->Type == NormalProcess) + { + processItem = PhReferenceProcessItem(Entry->ProcessId); + + if (processItem) + return processItem; + } + + processItem = PhCreateProcessItem(Entry->ProcessId); + + // Mark the process as terminated if necessary. + if (Entry->Type == TerminatedProcess) + processItem->State |= PH_PROCESS_ITEM_REMOVED; + + // We need a process record. Just use the record of System Idle Process. + if (idleProcessItem = PhReferenceProcessItem(SYSTEM_IDLE_PROCESS_ID)) + { + processItem->Record = idleProcessItem->Record; + PhReferenceProcessRecord(processItem->Record); + } + else + { + PhDereferenceObject(processItem); + return NULL; + } + + // Set up the file name and process name. + + PhSwapReference(&processItem->FileName, Entry->FileName); + + if (processItem->FileName) + { + processItem->ProcessName = PhGetBaseName(processItem->FileName); + } + else + { + processItem->ProcessName = PhCreateString(L"Unknown"); + } + + if (ProcessesMethod == BruteForceScanMethod) + { + status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + Entry->ProcessId + ); + } + else + { + status = PhOpenProcessByCsrHandles( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + Entry->ProcessId + ); + } + + if (NT_SUCCESS(status)) + { + // Basic information and not-so-dynamic information + + processItem->QueryHandle = processHandle; + + if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo))) + { + processItem->ParentProcessId = basicInfo.InheritedFromUniqueProcessId; + processItem->BasePriority = basicInfo.BasePriority; + } + + PhGetProcessSessionId(processHandle, &processItem->SessionId); + + PhPrintUInt32(processItem->ParentProcessIdString, HandleToUlong(processItem->ParentProcessId)); + PhPrintUInt32(processItem->SessionIdString, processItem->SessionId); + + if (NT_SUCCESS(PhGetProcessTimes(processHandle, ×))) + { + processItem->CreateTime = times.CreateTime; + processItem->KernelTime = times.KernelTime; + processItem->UserTime = times.UserTime; + } + + // TODO: Token information? + + if (NT_SUCCESS(PhGetProcessPriority(processHandle, &priorityClass))) + { + processItem->PriorityClass = priorityClass.PriorityClass; + } + + if (NT_SUCCESS(PhGetProcessHandleCount(processHandle, &handleInfo))) + { + processItem->NumberOfHandles = handleInfo.HandleCount; + } + } + + // Stage 1 + // Some copy and paste magic here... + + if (processItem->FileName) + { + // Small icon, large icon. + PhExtractIcon( + processItem->FileName->Buffer, + &processItem->LargeIcon, + &processItem->SmallIcon + ); + + // Version info. + PhInitializeImageVersionInfo(&processItem->VersionInfo, processItem->FileName->Buffer); + } + + // Use the default EXE icon if we didn't get the file's icon. + { + if (!processItem->SmallIcon || !processItem->LargeIcon) + { + if (processItem->SmallIcon) + { + DestroyIcon(processItem->SmallIcon); + processItem->SmallIcon = NULL; + } + else if (processItem->LargeIcon) + { + DestroyIcon(processItem->LargeIcon); + processItem->LargeIcon = NULL; + } + + PhGetStockApplicationIcon(&processItem->SmallIcon, &processItem->LargeIcon); + processItem->SmallIcon = CopyIcon(processItem->SmallIcon); + processItem->LargeIcon = CopyIcon(processItem->LargeIcon); + } + } + + // Command line + + status = PhOpenProcess( + &processHandle2, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + Entry->ProcessId + ); + + if (NT_SUCCESS(status)) + { + PPH_STRING commandLine; + ULONG i; + + if (NT_SUCCESS(status = PhGetProcessCommandLine(processHandle2, &commandLine))) + { + // Some command lines (e.g. from taskeng.exe) have nulls in them. + // Since Windows can't display them, we'll replace them with + // spaces. + for (i = 0; i < (ULONG)commandLine->Length / sizeof(WCHAR); i++) + { + if (commandLine->Buffer[i] == 0) + commandLine->Buffer[i] = ' '; + } + } + + if (NT_SUCCESS(status)) + { + processItem->CommandLine = commandLine; + } + + NtClose(processHandle2); + } + + // TODO: Other stage 1 tasks. + + PhSetEvent(&processItem->Stage1Event); + + return processItem; +} + +NTSTATUS PhpEnumHiddenProcessesBruteForce( + _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + PPH_LIST pids; + ULONG pid; + BOOLEAN stop = FALSE; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + return status; + + pids = PhCreateList(40); + + process = PH_FIRST_PROCESS(processes); + + do + { + PhAddItemList(pids, process->UniqueProcessId); + } while (process = PH_NEXT_PROCESS(process)); + + PhFree(processes); + + for (pid = 8; pid <= 65536; pid += 4) + { + NTSTATUS status2; + HANDLE processHandle; + PH_HIDDEN_PROCESS_ENTRY entry; + KERNEL_USER_TIMES times; + PPH_STRING fileName; + + status2 = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + UlongToHandle(pid) + ); + + if (NT_SUCCESS(status2)) + { + entry.ProcessId = UlongToHandle(pid); + + if (NT_SUCCESS(status2 = PhGetProcessTimes( + processHandle, + × + )) && + NT_SUCCESS(status2 = PhGetProcessImageFileName( + processHandle, + &fileName + ))) + { + entry.FileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + + if (times.ExitTime.QuadPart != 0) + entry.Type = TerminatedProcess; + else if (PhFindItemList(pids, UlongToHandle(pid)) != -1) + entry.Type = NormalProcess; + else + entry.Type = HiddenProcess; + + if (!Callback(&entry, Context)) + stop = TRUE; + + PhDereferenceObject(entry.FileName); + } + + NtClose(processHandle); + } + + // Use an alternative method if we don't have sufficient access. + if (status2 == STATUS_ACCESS_DENIED) + { + if (NT_SUCCESS(status2 = PhGetProcessImageFileNameByProcessId(UlongToHandle(pid), &fileName))) + { + entry.ProcessId = UlongToHandle(pid); + entry.FileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + + if (PhFindItemList(pids, UlongToHandle(pid)) != -1) + entry.Type = NormalProcess; + else + entry.Type = HiddenProcess; + + if (!Callback(&entry, Context)) + stop = TRUE; + + PhDereferenceObject(entry.FileName); + } + } + + if (status2 == STATUS_INVALID_CID || status2 == STATUS_INVALID_PARAMETER) + status2 = STATUS_SUCCESS; + + if (!NT_SUCCESS(status2)) + { + entry.ProcessId = UlongToHandle(pid); + entry.FileName = NULL; + entry.Type = UnknownProcess; + + if (!Callback(&entry, Context)) + stop = TRUE; + } + + if (stop) + break; + } + + PhDereferenceObject(pids); + + return status; +} + +typedef struct _CSR_HANDLES_CONTEXT +{ + PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback; + PVOID Context; + PPH_LIST Pids; +} CSR_HANDLES_CONTEXT, *PCSR_HANDLES_CONTEXT; + +static BOOLEAN NTAPI PhpCsrProcessHandlesCallback( + _In_ PPH_CSR_HANDLE_INFO Handle, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + BOOLEAN cont = TRUE; + PCSR_HANDLES_CONTEXT context = Context; + HANDLE processHandle; + KERNEL_USER_TIMES times; + PPH_STRING fileName; + PH_HIDDEN_PROCESS_ENTRY entry; + + entry.ProcessId = Handle->ProcessId; + + if (NT_SUCCESS(status = PhOpenProcessByCsrHandle( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + Handle + ))) + { + if (NT_SUCCESS(status = PhGetProcessTimes( + processHandle, + × + )) && + NT_SUCCESS(status = PhGetProcessImageFileName( + processHandle, + &fileName + ))) + { + entry.FileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + + if (times.ExitTime.QuadPart != 0) + entry.Type = TerminatedProcess; + else if (PhFindItemList(context->Pids, Handle->ProcessId) != -1) + entry.Type = NormalProcess; + else + entry.Type = HiddenProcess; + + if (!context->Callback(&entry, context->Context)) + cont = FALSE; + + PhDereferenceObject(entry.FileName); + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + { + entry.FileName = NULL; + entry.Type = UnknownProcess; + + if (!context->Callback(&entry, context->Context)) + cont = FALSE; + } + + return cont; +} + +NTSTATUS PhpEnumHiddenProcessesCsrHandles( + _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + PPH_LIST pids; + CSR_HANDLES_CONTEXT context; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + return status; + + pids = PhCreateList(40); + + process = PH_FIRST_PROCESS(processes); + + do + { + PhAddItemList(pids, process->UniqueProcessId); + } while (process = PH_NEXT_PROCESS(process)); + + PhFree(processes); + + context.Callback = Callback; + context.Context = Context; + context.Pids = pids; + + status = PhEnumCsrProcessHandles(PhpCsrProcessHandlesCallback, &context); + + PhDereferenceObject(pids); + + return status; +} + +NTSTATUS PhpEnumHiddenProcessHandles( + _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PVOID processes; + HANDLE processHandle; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + return status; + + if (!NT_SUCCESS(status = NtGetNextProcess( + NULL, + PROCESS_QUERY_LIMITED_INFORMATION, + 0, + 0, + &processHandle + ))) + goto CleanupExit; + + while (TRUE) + { + HANDLE enumProcessHandle; + PROCESS_EXTENDED_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandle, &basicInfo))) + { + if (!PhFindProcessInformation(processes, basicInfo.BasicInfo.UniqueProcessId)) + { + PH_HIDDEN_PROCESS_ENTRY entry; + PPH_STRING fileName; + + entry.ProcessId = basicInfo.BasicInfo.UniqueProcessId; + + if (NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName))) + { + entry.FileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + entry.Type = HiddenProcess; + + if (basicInfo.IsProcessDeleting) + entry.Type = TerminatedProcess; + + if (!Callback(&entry, Context)) + break; + + PhDereferenceObject(entry.FileName); + } + else + { + entry.FileName = NULL; + entry.Type = UnknownProcess; + + if (!Callback(&entry, Context)) + break; + } + } + } + + if (NT_SUCCESS(status = NtGetNextProcess( + processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + 0, + 0, + &enumProcessHandle + ))) + { + NtClose(processHandle); + processHandle = enumProcessHandle; + } + else + { + NtClose(processHandle); + break; + } + } + + if (status == STATUS_NO_MORE_ENTRIES) + status = STATUS_SUCCESS; // HACK + +CleanupExit: + PhFree(processes); + + return status; +} + +NTSTATUS PhEnumHiddenProcesses( + _In_ PH_HIDDEN_PROCESS_METHOD Method, + _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + switch (Method) + { + case BruteForceScanMethod: + return PhpEnumHiddenProcessesBruteForce(Callback, Context); + case CsrHandlesScanMethod: + return PhpEnumHiddenProcessesCsrHandles(Callback, Context); + case ProcessHandleScanMethod: + return PhpEnumHiddenProcessHandles(Callback, Context); + } + + return STATUS_FAIL_CHECK; +} + +NTSTATUS PhpOpenCsrProcesses( + _Out_ PHANDLE *ProcessHandles, + _Out_ PULONG NumberOfProcessHandles + ) +{ + NTSTATUS status; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + PPH_LIST processHandleList; + + if (!NT_SUCCESS(status = PhEnumProcesses(&processes))) + return status; + + processHandleList = PhCreateList(8); + + process = PH_FIRST_PROCESS(processes); + + do + { + HANDLE processHandle; + PH_KNOWN_PROCESS_TYPE knownProcessType; + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_DUP_HANDLE, + process->UniqueProcessId + ))) + { + if (NT_SUCCESS(PhGetProcessKnownType( + processHandle, + &knownProcessType + )) && + (knownProcessType & KnownProcessTypeMask) == WindowsSubsystemProcessType) + { + PhAddItemList(processHandleList, processHandle); + } + else + { + NtClose(processHandle); + } + } + } while (process = PH_NEXT_PROCESS(process)); + + PhFree(processes); + + *ProcessHandles = PhAllocateCopy(processHandleList->Items, processHandleList->Count * sizeof(HANDLE)); + *NumberOfProcessHandles = processHandleList->Count; + + PhDereferenceObject(processHandleList); + + return status; +} + +NTSTATUS PhpGetCsrHandleProcessId( + _Inout_ PPH_CSR_HANDLE_INFO Handle + ) +{ + NTSTATUS status; + PROCESS_BASIC_INFORMATION processBasicInfo; + THREAD_BASIC_INFORMATION threadBasicInfo; + + Handle->IsThreadHandle = FALSE; + Handle->ProcessId = NULL; + + // Assume the handle is a process handle, and get the + // process ID. + + status = KphQueryInformationObject( + Handle->CsrProcessHandle, + Handle->Handle, + KphObjectProcessBasicInformation, + &processBasicInfo, + sizeof(PROCESS_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + Handle->ProcessId = processBasicInfo.UniqueProcessId; + } + else + { + // We failed to get the process ID. Assume the handle + // is a thread handle, and get the process ID. + + status = KphQueryInformationObject( + Handle->CsrProcessHandle, + Handle->Handle, + KphObjectThreadBasicInformation, + &threadBasicInfo, + sizeof(THREAD_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + Handle->ProcessId = threadBasicInfo.ClientId.UniqueProcess; + Handle->IsThreadHandle = TRUE; + } + } + + return status; +} + +NTSTATUS PhEnumCsrProcessHandles( + _In_ PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PHANDLE csrProcessHandles; + ULONG numberOfCsrProcessHandles; + ULONG i; + BOOLEAN stop = FALSE; + PPH_LIST pids; + + if (!NT_SUCCESS(status = PhpOpenCsrProcesses( + &csrProcessHandles, + &numberOfCsrProcessHandles + ))) + return status; + + pids = PhCreateList(40); + + for (i = 0; i < numberOfCsrProcessHandles; i++) + { + PKPH_PROCESS_HANDLE_INFORMATION handles; + ULONG j; + + if (stop) + break; + + if (NT_SUCCESS(KphEnumerateProcessHandles2(csrProcessHandles[i], &handles))) + { + for (j = 0; j < handles->HandleCount; j++) + { + PH_CSR_HANDLE_INFO handle; + + handle.CsrProcessHandle = csrProcessHandles[i]; + handle.Handle = handles->Handles[j].Handle; + + // Get the process ID associated with the handle. + // This call will fail if the handle is not a + // process or thread handle. + if (!NT_SUCCESS(PhpGetCsrHandleProcessId(&handle))) + continue; + + // Avoid duplicate PIDs. + if (PhFindItemList(pids, handle.ProcessId) != -1) + continue; + + PhAddItemList(pids, handle.ProcessId); + + if (!Callback(&handle, Context)) + { + stop = TRUE; + break; + } + } + + PhFree(handles); + } + } + + PhDereferenceObject(pids); + + for (i = 0; i < numberOfCsrProcessHandles; i++) + NtClose(csrProcessHandles[i]); + + PhFree(csrProcessHandles); + + return status; +} + +NTSTATUS PhOpenProcessByCsrHandle( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PPH_CSR_HANDLE_INFO Handle + ) +{ + NTSTATUS status; + + if (!Handle->IsThreadHandle) + { + status = NtDuplicateObject( + Handle->CsrProcessHandle, + Handle->Handle, + NtCurrentProcess(), + ProcessHandle, + DesiredAccess, + 0, + 0 + ); + } + else + { + HANDLE threadHandle; + + if (!NT_SUCCESS(status = NtDuplicateObject( + Handle->CsrProcessHandle, + Handle->Handle, + NtCurrentProcess(), + &threadHandle, + THREAD_QUERY_LIMITED_INFORMATION, + 0, + 0 + ))) + return status; + + status = KphOpenThreadProcess( + threadHandle, + DesiredAccess, + ProcessHandle + ); + NtClose(threadHandle); + } + + return status; +} + +typedef struct _OPEN_PROCESS_BY_CSR_CONTEXT +{ + NTSTATUS Status; + PHANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + HANDLE ProcessId; +} OPEN_PROCESS_BY_CSR_CONTEXT, *POPEN_PROCESS_BY_CSR_CONTEXT; + +static BOOLEAN NTAPI PhpOpenProcessByCsrHandlesCallback( + _In_ PPH_CSR_HANDLE_INFO Handle, + _In_opt_ PVOID Context + ) +{ + POPEN_PROCESS_BY_CSR_CONTEXT context = Context; + + if (Handle->ProcessId == context->ProcessId) + { + context->Status = PhOpenProcessByCsrHandle( + context->ProcessHandle, + context->DesiredAccess, + Handle + ); + + return FALSE; + } + + return TRUE; +} + +NTSTATUS PhOpenProcessByCsrHandles( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ProcessId + ) +{ + NTSTATUS status; + OPEN_PROCESS_BY_CSR_CONTEXT context; + + context.Status = STATUS_INVALID_CID; + context.ProcessHandle = ProcessHandle; + context.DesiredAccess = DesiredAccess; + context.ProcessId = ProcessId; + + if (!NT_SUCCESS(status = PhEnumCsrProcessHandles( + PhpOpenProcessByCsrHandlesCallback, + &context + ))) + return status; + + return context.Status; +} diff --git a/ProcessHacker/hndllist.c b/ProcessHacker/hndllist.c index 0f6de031b6a6..3aab612a7ddf 100644 --- a/ProcessHacker/hndllist.c +++ b/ProcessHacker/hndllist.c @@ -1,753 +1,763 @@ -/* - * Process Hacker - - * handle list - * - * Copyright (C) 2011-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -BOOLEAN PhpHandleNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG PhpHandleNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpDestroyHandleNode( - _In_ PPH_HANDLE_NODE HandleNode - ); - -VOID PhpRemoveHandleNode( - _In_ PPH_HANDLE_NODE HandleNode, - _In_ PPH_HANDLE_LIST_CONTEXT Context - ); - -LONG PhpHandleTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpHandleTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -VOID PhInitializeHandleList( - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle, - _Out_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - HWND hwnd; - - memset(Context, 0, sizeof(PH_HANDLE_LIST_CONTEXT)); - Context->EnableStateHighlighting = TRUE; - - Context->NodeHashtable = PhCreateHashtable( - sizeof(PPH_HANDLE_NODE), - PhpHandleNodeHashtableEqualFunction, - PhpHandleNodeHashtableHashFunction, - 100 - ); - Context->NodeList = PhCreateList(100); - - Context->ParentWindowHandle = ParentWindowHandle; - Context->TreeNewHandle = TreeNewHandle; - hwnd = TreeNewHandle; - PhSetControlTheme(hwnd, L"explorer"); - - TreeNew_SetCallback(hwnd, PhpHandleTreeNewCallback, Context); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHHNTLC_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumn(hwnd, PHHNTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, 1, 0); - PhAddTreeNewColumn(hwnd, PHHNTLC_HANDLE, TRUE, L"Handle", 80, PH_ALIGN_LEFT, 2, 0); - - PhAddTreeNewColumn(hwnd, PHHNTLC_OBJECTADDRESS, FALSE, L"Object address", 80, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHHNTLC_ATTRIBUTES, FALSE, L"Attributes", 120, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumn(hwnd, PHHNTLC_GRANTEDACCESS, FALSE, L"Granted access", 80, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHHNTLC_GRANTEDACCESSSYMBOLIC, FALSE, L"Granted access (symbolic)", 140, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHHNTLC_ORIGINALNAME, FALSE, L"Original name", 200, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHHNTLC_FILESHAREACCESS, FALSE, L"File share access", 50, PH_ALIGN_LEFT, -1, 0, TRUE); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetSort(hwnd, 0, AscendingSortOrder); - - PhCmInitializeManager(&Context->Cm, hwnd, PHHNTLC_MAXIMUM, PhpHandleTreeNewPostSortFunction); -} - -VOID PhDeleteHandleList( - _In_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - ULONG i; - - PhCmDeleteManager(&Context->Cm); - - for (i = 0; i < Context->NodeList->Count; i++) - PhpDestroyHandleNode(Context->NodeList->Items[i]); - - PhDereferenceObject(Context->NodeHashtable); - PhDereferenceObject(Context->NodeList); -} - -BOOLEAN PhpHandleNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_HANDLE_NODE handleNode1 = *(PPH_HANDLE_NODE *)Entry1; - PPH_HANDLE_NODE handleNode2 = *(PPH_HANDLE_NODE *)Entry2; - - return handleNode1->Handle == handleNode2->Handle; -} - -ULONG PhpHandleNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return HandleToUlong((*(PPH_HANDLE_NODE *)Entry)->Handle) / 4; -} - -VOID PhLoadSettingsHandleList( - _Inout_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhGetStringSetting(L"HandleTreeListColumns"); - sortSettings = PhGetStringSetting(L"HandleTreeListSort"); - PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSaveSettingsHandleList( - _Inout_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); - PhSetStringSetting2(L"HandleTreeListColumns", &settings->sr); - PhSetStringSetting2(L"HandleTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSetOptionsHandleList( - _Inout_ PPH_HANDLE_LIST_CONTEXT Context, - _In_ BOOLEAN HideUnnamedHandles - ) -{ - ULONG i; - BOOLEAN modified; - - if (Context->HideUnnamedHandles != HideUnnamedHandles) - { - Context->HideUnnamedHandles = HideUnnamedHandles; - - modified = FALSE; - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_HANDLE_NODE node = Context->NodeList->Items[i]; - BOOLEAN visible; - - visible = TRUE; - - if (HideUnnamedHandles && PhIsNullOrEmptyString(node->HandleItem->BestObjectName)) - visible = FALSE; - - if (node->Node.Visible != visible) - { - node->Node.Visible = visible; - modified = TRUE; - - if (!visible) - node->Node.Selected = FALSE; - } - } - - if (modified) - { - TreeNew_NodesStructured(Context->TreeNewHandle); - } - } -} - -PPH_HANDLE_NODE PhAddHandleNode( - _Inout_ PPH_HANDLE_LIST_CONTEXT Context, - _In_ PPH_HANDLE_ITEM HandleItem, - _In_ ULONG RunId - ) -{ - PPH_HANDLE_NODE handleNode; - - handleNode = PhAllocate(PhEmGetObjectSize(EmHandleNodeType, sizeof(PH_HANDLE_NODE))); - memset(handleNode, 0, sizeof(PH_HANDLE_NODE)); - PhInitializeTreeNewNode(&handleNode->Node); - - if (Context->EnableStateHighlighting && RunId != 1) - { - PhChangeShStateTn( - &handleNode->Node, - &handleNode->ShState, - &Context->NodeStateList, - NewItemState, - PhCsColorNew, - NULL - ); - } - - handleNode->Handle = HandleItem->Handle; - handleNode->HandleItem = HandleItem; - PhReferenceObject(HandleItem); - - memset(handleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHHNTLC_MAXIMUM); - handleNode->Node.TextCache = handleNode->TextCache; - handleNode->Node.TextCacheSize = PHHNTLC_MAXIMUM; - - PhAddEntryHashtable(Context->NodeHashtable, &handleNode); - PhAddItemList(Context->NodeList, handleNode); - - if (Context->HideUnnamedHandles && PhIsNullOrEmptyString(HandleItem->BestObjectName)) - handleNode->Node.Visible = FALSE; - - PhEmCallObjectOperation(EmHandleNodeType, handleNode, EmObjectCreate); - - TreeNew_NodesStructured(Context->TreeNewHandle); - - return handleNode; -} - -PPH_HANDLE_NODE PhFindHandleNode( - _In_ PPH_HANDLE_LIST_CONTEXT Context, - _In_ HANDLE Handle - ) -{ - PH_HANDLE_NODE lookupHandleNode; - PPH_HANDLE_NODE lookupHandleNodePtr = &lookupHandleNode; - PPH_HANDLE_NODE *handleNode; - - lookupHandleNode.Handle = Handle; - - handleNode = (PPH_HANDLE_NODE *)PhFindEntryHashtable( - Context->NodeHashtable, - &lookupHandleNodePtr - ); - - if (handleNode) - return *handleNode; - else - return NULL; -} - -VOID PhRemoveHandleNode( - _In_ PPH_HANDLE_LIST_CONTEXT Context, - _In_ PPH_HANDLE_NODE HandleNode - ) -{ - // Remove from the hashtable here to avoid problems in case the key is re-used. - PhRemoveEntryHashtable(Context->NodeHashtable, &HandleNode); - - if (Context->EnableStateHighlighting) - { - PhChangeShStateTn( - &HandleNode->Node, - &HandleNode->ShState, - &Context->NodeStateList, - RemovingItemState, - PhCsColorRemoved, - Context->TreeNewHandle - ); - } - else - { - PhpRemoveHandleNode(HandleNode, Context); - } -} - -VOID PhpDestroyHandleNode( - _In_ PPH_HANDLE_NODE HandleNode - ) -{ - PhEmCallObjectOperation(EmHandleNodeType, HandleNode, EmObjectDelete); - - if (HandleNode->GrantedAccessSymbolicText) PhDereferenceObject(HandleNode->GrantedAccessSymbolicText); - - PhDereferenceObject(HandleNode->HandleItem); - - PhFree(HandleNode); -} - -VOID PhpRemoveHandleNode( - _In_ PPH_HANDLE_NODE HandleNode, - _In_ PPH_HANDLE_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after HandleNode - ) -{ - ULONG index; - - // Remove from list and cleanup. - - if ((index = PhFindItemList(Context->NodeList, HandleNode)) != -1) - PhRemoveItemList(Context->NodeList, index); - - PhpDestroyHandleNode(HandleNode); - - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhUpdateHandleNode( - _In_ PPH_HANDLE_LIST_CONTEXT Context, - _In_ PPH_HANDLE_NODE HandleNode - ) -{ - memset(HandleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHHNTLC_MAXIMUM); - - PhInvalidateTreeNewNode(&HandleNode->Node, TN_CACHE_COLOR); - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhTickHandleNodes( - _In_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - PH_TICK_SH_STATE_TN(PH_HANDLE_NODE, ShState, Context->NodeStateList, PhpRemoveHandleNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context); -} - -#define SORT_FUNCTION(Column) PhpHandleTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpHandleTreeNewCompare##Column( \ - _In_ void *_context, \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_HANDLE_NODE node1 = *(PPH_HANDLE_NODE *)_elem1; \ - PPH_HANDLE_NODE node2 = *(PPH_HANDLE_NODE *)_elem2; \ - PPH_HANDLE_ITEM handleItem1 = node1->HandleItem; \ - PPH_HANDLE_ITEM handleItem2 = node2->HandleItem; \ - PPH_HANDLE_LIST_CONTEXT context = (PPH_HANDLE_LIST_CONTEXT)_context; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle); \ - \ - return PhModifySort(sortResult, context->TreeNewSortOrder); \ -} - -LONG PhpHandleTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - if (Result == 0) - Result = uintptrcmp((ULONG_PTR)((PPH_HANDLE_NODE)Node1)->Handle, (ULONG_PTR)((PPH_HANDLE_NODE)Node2)->Handle); - - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(Type) -{ - sortResult = PhCompareString(handleItem1->TypeName, handleItem2->TypeName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Name) -{ - sortResult = PhCompareStringWithNull(handleItem1->BestObjectName, handleItem2->BestObjectName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Handle) -{ - sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ObjectAddress) -{ - sortResult = uintptrcmp((ULONG_PTR)handleItem1->Object, (ULONG_PTR)handleItem2->Object); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Attributes) -{ - sortResult = uintcmp(handleItem1->Attributes, handleItem2->Attributes); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(GrantedAccess) -{ - sortResult = uintcmp(handleItem1->GrantedAccess, handleItem2->GrantedAccess); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(OriginalName) -{ - sortResult = PhCompareStringWithNull(handleItem1->ObjectName, handleItem2->ObjectName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileShareAccess) -{ - sortResult = uintcmp(handleItem1->FileFlags & (PH_HANDLE_FILE_SHARED_MASK), handleItem2->FileFlags & (PH_HANDLE_FILE_SHARED_MASK)); - - // Make sure all file handles get grouped together regardless of share access. - if (sortResult == 0) - sortResult = intcmp(PhEqualString2(handleItem1->TypeName, L"File", TRUE), PhEqualString2(handleItem2->TypeName, L"File", TRUE)); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpHandleTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_HANDLE_LIST_CONTEXT context; - PPH_HANDLE_NODE node; - - context = Context; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Type), - SORT_FUNCTION(Name), - SORT_FUNCTION(Handle), - SORT_FUNCTION(ObjectAddress), - SORT_FUNCTION(Attributes), - SORT_FUNCTION(GrantedAccess), - SORT_FUNCTION(GrantedAccess), // Granted Access (Symbolic) - SORT_FUNCTION(OriginalName), - SORT_FUNCTION(FileShareAccess) - }; - int (__cdecl *sortFunction)(void *, const void *, const void *); - - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)context->NodeList->Items, - context->NodeList->Count, - context->TreeNewSortColumn, - context->TreeNewSortOrder, - &context->Cm - )) - { - if (context->TreeNewSortColumn < PHHNTLC_MAXIMUM) - sortFunction = sortFunctions[context->TreeNewSortColumn]; - else - sortFunction = NULL; - } - else - { - sortFunction = NULL; - } - - if (sortFunction) - { - qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); - } - - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; - getChildren->NumberOfChildren = context->NodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_HANDLE_ITEM handleItem; - - node = (PPH_HANDLE_NODE)getCellText->Node; - handleItem = node->HandleItem; - - switch (getCellText->Id) - { - case PHHNTLC_TYPE: - getCellText->Text = handleItem->TypeName->sr; - break; - case PHHNTLC_NAME: - getCellText->Text = PhGetStringRef(handleItem->BestObjectName); - break; - case PHHNTLC_HANDLE: - PhInitializeStringRefLongHint(&getCellText->Text, handleItem->HandleString); - break; - case PHHNTLC_OBJECTADDRESS: - PhInitializeStringRefLongHint(&getCellText->Text, handleItem->ObjectString); - break; - case PHHNTLC_ATTRIBUTES: - switch (handleItem->Attributes & (OBJ_PROTECT_CLOSE | OBJ_INHERIT)) - { - case OBJ_PROTECT_CLOSE: - PhInitializeStringRef(&getCellText->Text, L"Protected"); - break; - case OBJ_INHERIT: - PhInitializeStringRef(&getCellText->Text, L"Inherit"); - break; - case OBJ_PROTECT_CLOSE | OBJ_INHERIT: - PhInitializeStringRef(&getCellText->Text, L"Protected, Inherit"); - break; - } - break; - case PHHNTLC_GRANTEDACCESS: - PhInitializeStringRefLongHint(&getCellText->Text, handleItem->GrantedAccessString); - break; - case PHHNTLC_GRANTEDACCESSSYMBOLIC: - if (handleItem->GrantedAccess != 0) - { - if (!node->GrantedAccessSymbolicText) - { - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - - if (PhGetAccessEntries(handleItem->TypeName->Buffer, &accessEntries, &numberOfAccessEntries)) - { - node->GrantedAccessSymbolicText = PhGetAccessString(handleItem->GrantedAccess, accessEntries, numberOfAccessEntries); - PhFree(accessEntries); - } - else - { - node->GrantedAccessSymbolicText = PhReferenceEmptyString(); - } - } - - if (node->GrantedAccessSymbolicText->Length != 0) - getCellText->Text = node->GrantedAccessSymbolicText->sr; - else - PhInitializeStringRefLongHint(&getCellText->Text, handleItem->GrantedAccessString); - } - break; - case PHHNTLC_ORIGINALNAME: - getCellText->Text = PhGetStringRef(handleItem->ObjectName); - break; - case PHHNTLC_FILESHAREACCESS: - if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_MASK) - { - node->FileShareAccessText[0] = '-'; - node->FileShareAccessText[1] = '-'; - node->FileShareAccessText[2] = '-'; - node->FileShareAccessText[3] = 0; - - if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_READ) - node->FileShareAccessText[0] = 'R'; - if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_WRITE) - node->FileShareAccessText[1] = 'W'; - if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_DELETE) - node->FileShareAccessText[2] = 'D'; - - PhInitializeStringRef(&getCellText->Text, node->FileShareAccessText); - } - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; - PPH_HANDLE_ITEM handleItem; - - node = (PPH_HANDLE_NODE)getNodeColor->Node; - handleItem = node->HandleItem; - - if (!handleItem) - ; // Dummy - else if (PhCsUseColorProtectedHandles && (handleItem->Attributes & OBJ_PROTECT_CLOSE)) - getNodeColor->BackColor = PhCsColorProtectedHandles; - else if (PhCsUseColorInheritHandles && (handleItem->Attributes & OBJ_INHERIT)) - getNodeColor->BackColor = PhCsColorInheritHandles; - - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(context->TreeNewHandle, 0, -1); - break; - case VK_DELETE: - // Pass a 1 in lParam to indicate that warnings should be enabled. - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_CLOSE, 1); - break; - case VK_RETURN: - if (GetKeyState(VK_CONTROL) >= 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_PROPERTIES, 0); - else - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_OBJECTPROPERTIES1, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = AscendingSortOrder; - PhInitializeTreeNewColumnMenu(&data); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_PROPERTIES, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); - } - return TRUE; - case TreeNewGetDialogCode: - { - PULONG code = Parameter2; - - if (PtrToUlong(Parameter1) == VK_RETURN) - { - *code = DLGC_WANTMESSAGE; - return TRUE; - } - } - return FALSE; - } - - return FALSE; -} - -PPH_HANDLE_ITEM PhGetSelectedHandleItem( - _In_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - PPH_HANDLE_ITEM handleItem = NULL; - ULONG i; - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_HANDLE_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - { - handleItem = node->HandleItem; - break; - } - } - - return handleItem; -} - -VOID PhGetSelectedHandleItems( - _In_ PPH_HANDLE_LIST_CONTEXT Context, - _Out_ PPH_HANDLE_ITEM **Handles, - _Out_ PULONG NumberOfHandles - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_HANDLE_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node->HandleItem); - } - - *NumberOfHandles = (ULONG)array.Count; - *Handles = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllHandleNodes( - _In_ PPH_HANDLE_LIST_CONTEXT Context - ) -{ - TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); -} +/* + * Process Hacker - + * handle list + * + * Copyright (C) 2011-2013 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +BOOLEAN PhpHandleNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG PhpHandleNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpDestroyHandleNode( + _In_ PPH_HANDLE_NODE HandleNode + ); + +VOID PhpRemoveHandleNode( + _In_ PPH_HANDLE_NODE HandleNode, + _In_ PPH_HANDLE_LIST_CONTEXT Context + ); + +LONG PhpHandleTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpHandleTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +VOID PhInitializeHandleList( + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle, + _Out_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + HWND hwnd; + + memset(Context, 0, sizeof(PH_HANDLE_LIST_CONTEXT)); + Context->EnableStateHighlighting = TRUE; + + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_HANDLE_NODE), + PhpHandleNodeHashtableEqualFunction, + PhpHandleNodeHashtableHashFunction, + 100 + ); + Context->NodeList = PhCreateList(100); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + hwnd = TreeNewHandle; + PhSetControlTheme(hwnd, L"explorer"); + + TreeNew_SetCallback(hwnd, PhpHandleTreeNewCallback, Context); + + TreeNew_SetRedraw(hwnd, FALSE); + + // Default columns + PhAddTreeNewColumn(hwnd, PHHNTLC_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(hwnd, PHHNTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, 1, 0); + PhAddTreeNewColumn(hwnd, PHHNTLC_GRANTEDACCESSSYMBOLIC, TRUE, L"Granted access (symbolic)", 140, PH_ALIGN_LEFT, 2, 0); + + PhAddTreeNewColumn(hwnd, PHHNTLC_HANDLE, FALSE, L"Handle", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHHNTLC_OBJECTADDRESS, FALSE, L"Object address", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHHNTLC_ATTRIBUTES, FALSE, L"Attributes", 120, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumn(hwnd, PHHNTLC_GRANTEDACCESS, FALSE, L"Granted access", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHHNTLC_ORIGINALNAME, FALSE, L"Original name", 200, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHHNTLC_FILESHAREACCESS, FALSE, L"File share access", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + + TreeNew_SetRedraw(hwnd, TRUE); + + TreeNew_SetSort(hwnd, 0, AscendingSortOrder); + + PhCmInitializeManager(&Context->Cm, hwnd, PHHNTLC_MAXIMUM, PhpHandleTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, hwnd, Context->NodeList); +} + +VOID PhDeleteHandleList( + _In_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + ULONG i; + + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + + PhCmDeleteManager(&Context->Cm); + + for (i = 0; i < Context->NodeList->Count; i++) + PhpDestroyHandleNode(Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +BOOLEAN PhpHandleNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_HANDLE_NODE handleNode1 = *(PPH_HANDLE_NODE *)Entry1; + PPH_HANDLE_NODE handleNode2 = *(PPH_HANDLE_NODE *)Entry2; + + return handleNode1->Handle == handleNode2->Handle; +} + +ULONG PhpHandleNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return HandleToUlong((*(PPH_HANDLE_NODE *)Entry)->Handle) / 4; +} + +VOID PhLoadSettingsHandleList( + _Inout_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhGetStringSetting(L"HandleTreeListColumns"); + sortSettings = PhGetStringSetting(L"HandleTreeListSort"); + Context->Flags = PhGetIntegerSetting(L"HandleTreeListFlags"); + + PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSaveSettingsHandleList( + _Inout_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); + + PhSetIntegerSetting(L"HandleTreeListFlags", Context->Flags); + PhSetStringSetting2(L"HandleTreeListColumns", &settings->sr); + PhSetStringSetting2(L"HandleTreeListSort", &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSetOptionsHandleList( + _Inout_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ ULONG Options + ) +{ + switch (Options) + { + case PH_HANDLE_TREE_MENUITEM_HIDEUNNAMEDHANDLES: + Context->HideUnnamedHandles = !Context->HideUnnamedHandles; + break; + case PH_HANDLE_TREE_MENUITEM_HIDEETWHANDLES: + Context->HideEtwHandles = !Context->HideEtwHandles; + break; + } +} + +PPH_HANDLE_NODE PhAddHandleNode( + _Inout_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ PPH_HANDLE_ITEM HandleItem, + _In_ ULONG RunId + ) +{ + PPH_HANDLE_NODE handleNode; + + handleNode = PhAllocate(PhEmGetObjectSize(EmHandleNodeType, sizeof(PH_HANDLE_NODE))); + memset(handleNode, 0, sizeof(PH_HANDLE_NODE)); + PhInitializeTreeNewNode(&handleNode->Node); + + if (Context->EnableStateHighlighting && RunId != 1) + { + PhChangeShStateTn( + &handleNode->Node, + &handleNode->ShState, + &Context->NodeStateList, + NewItemState, + PhCsColorNew, + NULL + ); + } + + handleNode->Handle = HandleItem->Handle; + handleNode->HandleItem = HandleItem; + PhReferenceObject(HandleItem); + + memset(handleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHHNTLC_MAXIMUM); + handleNode->Node.TextCache = handleNode->TextCache; + handleNode->Node.TextCacheSize = PHHNTLC_MAXIMUM; + + PhAddEntryHashtable(Context->NodeHashtable, &handleNode); + PhAddItemList(Context->NodeList, handleNode); + + if (Context->TreeFilterSupport.FilterList) + handleNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &handleNode->Node); + + PhEmCallObjectOperation(EmHandleNodeType, handleNode, EmObjectCreate); + + TreeNew_NodesStructured(Context->TreeNewHandle); + + return handleNode; +} + +PPH_HANDLE_NODE PhFindHandleNode( + _In_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ HANDLE Handle + ) +{ + PH_HANDLE_NODE lookupHandleNode; + PPH_HANDLE_NODE lookupHandleNodePtr = &lookupHandleNode; + PPH_HANDLE_NODE *handleNode; + + lookupHandleNode.Handle = Handle; + + handleNode = (PPH_HANDLE_NODE *)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupHandleNodePtr + ); + + if (handleNode) + return *handleNode; + else + return NULL; +} + +VOID PhRemoveHandleNode( + _In_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ PPH_HANDLE_NODE HandleNode + ) +{ + // Remove from the hashtable here to avoid problems in case the key is re-used. + PhRemoveEntryHashtable(Context->NodeHashtable, &HandleNode); + + if (Context->EnableStateHighlighting) + { + PhChangeShStateTn( + &HandleNode->Node, + &HandleNode->ShState, + &Context->NodeStateList, + RemovingItemState, + PhCsColorRemoved, + Context->TreeNewHandle + ); + } + else + { + PhpRemoveHandleNode(HandleNode, Context); + } +} + +VOID PhpDestroyHandleNode( + _In_ PPH_HANDLE_NODE HandleNode + ) +{ + PhEmCallObjectOperation(EmHandleNodeType, HandleNode, EmObjectDelete); + + if (HandleNode->GrantedAccessSymbolicText) PhDereferenceObject(HandleNode->GrantedAccessSymbolicText); + + PhDereferenceObject(HandleNode->HandleItem); + + PhFree(HandleNode); +} + +VOID PhpRemoveHandleNode( + _In_ PPH_HANDLE_NODE HandleNode, + _In_ PPH_HANDLE_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after HandleNode + ) +{ + ULONG index; + + // Remove from list and cleanup. + + if ((index = PhFindItemList(Context->NodeList, HandleNode)) != ULONG_MAX) + PhRemoveItemList(Context->NodeList, index); + + PhpDestroyHandleNode(HandleNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhUpdateHandleNode( + _In_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ PPH_HANDLE_NODE HandleNode + ) +{ + memset(HandleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHHNTLC_MAXIMUM); + + PhInvalidateTreeNewNode(&HandleNode->Node, TN_CACHE_COLOR); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhExpandAllHandleNodes( + _In_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_HANDLE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhTickHandleNodes( + _In_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + PH_TICK_SH_STATE_TN(PH_HANDLE_NODE, ShState, Context->NodeStateList, PhpRemoveHandleNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context); +} + +#define SORT_FUNCTION(Column) PhpHandleTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpHandleTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_HANDLE_NODE node1 = *(PPH_HANDLE_NODE *)_elem1; \ + PPH_HANDLE_NODE node2 = *(PPH_HANDLE_NODE *)_elem2; \ + PPH_HANDLE_ITEM handleItem1 = node1->HandleItem; \ + PPH_HANDLE_ITEM handleItem2 = node2->HandleItem; \ + PPH_HANDLE_LIST_CONTEXT context = (PPH_HANDLE_LIST_CONTEXT)_context; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle); \ + \ + return PhModifySort(sortResult, context->TreeNewSortOrder); \ +} + +LONG PhpHandleTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = uintptrcmp((ULONG_PTR)((PPH_HANDLE_NODE)Node1)->Handle, (ULONG_PTR)((PPH_HANDLE_NODE)Node2)->Handle); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(Type) +{ + sortResult = PhCompareStringWithNullSortOrder(handleItem1->TypeName, handleItem2->TypeName, context->TreeNewSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareStringWithNullSortOrder(handleItem1->BestObjectName, handleItem2->BestObjectName, context->TreeNewSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Handle) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->Handle, (ULONG_PTR)node2->Handle); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ObjectAddress) +{ + sortResult = uintptrcmp((ULONG_PTR)handleItem1->Object, (ULONG_PTR)handleItem2->Object); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Attributes) +{ + sortResult = uintcmp(handleItem1->Attributes, handleItem2->Attributes); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(GrantedAccess) +{ + sortResult = uintcmp(handleItem1->GrantedAccess, handleItem2->GrantedAccess); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(OriginalName) +{ + sortResult = PhCompareStringWithNull(handleItem1->ObjectName, handleItem2->ObjectName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileShareAccess) +{ + sortResult = uintcmp(handleItem1->FileFlags & (PH_HANDLE_FILE_SHARED_MASK), handleItem2->FileFlags & (PH_HANDLE_FILE_SHARED_MASK)); + + // Make sure all file handles get grouped together regardless of share access. + if (sortResult == 0 && !PhIsNullOrEmptyString(handleItem1->TypeName)) + sortResult = intcmp(PhEqualString2(handleItem1->TypeName, L"File", TRUE), PhEqualString2(handleItem2->TypeName, L"File", TRUE)); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpHandleTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_HANDLE_LIST_CONTEXT context; + PPH_HANDLE_NODE node; + + context = Context; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Type), + SORT_FUNCTION(Name), + SORT_FUNCTION(Handle), + SORT_FUNCTION(ObjectAddress), + SORT_FUNCTION(Attributes), + SORT_FUNCTION(GrantedAccess), + SORT_FUNCTION(GrantedAccess), // Granted Access (Symbolic) + SORT_FUNCTION(OriginalName), + SORT_FUNCTION(FileShareAccess) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)context->NodeList->Items, + context->NodeList->Count, + context->TreeNewSortColumn, + context->TreeNewSortOrder, + &context->Cm + )) + { + if (context->TreeNewSortColumn < PHHNTLC_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + } + else + { + sortFunction = NULL; + } + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_HANDLE_ITEM handleItem; + + node = (PPH_HANDLE_NODE)getCellText->Node; + handleItem = node->HandleItem; + + switch (getCellText->Id) + { + case PHHNTLC_TYPE: + getCellText->Text = PhGetStringRef(handleItem->TypeName); + break; + case PHHNTLC_NAME: + getCellText->Text = PhGetStringRef(handleItem->BestObjectName); + break; + case PHHNTLC_HANDLE: + PhPrintPointer(handleItem->HandleString, (PVOID)handleItem->Handle); + PhInitializeStringRefLongHint(&getCellText->Text, handleItem->HandleString); + break; + case PHHNTLC_OBJECTADDRESS: + { + if (handleItem->Object) + PhPrintPointer(node->ObjectString, handleItem->Object); + + PhInitializeStringRefLongHint(&getCellText->Text, node->ObjectString); + } + break; + case PHHNTLC_ATTRIBUTES: + switch (handleItem->Attributes & (OBJ_PROTECT_CLOSE | OBJ_INHERIT)) + { + case OBJ_PROTECT_CLOSE: + PhInitializeStringRef(&getCellText->Text, L"Protected"); + break; + case OBJ_INHERIT: + PhInitializeStringRef(&getCellText->Text, L"Inherit"); + break; + case OBJ_PROTECT_CLOSE | OBJ_INHERIT: + PhInitializeStringRef(&getCellText->Text, L"Protected, Inherit"); + break; + } + break; + case PHHNTLC_GRANTEDACCESS: + PhPrintPointer(handleItem->GrantedAccessString, UlongToPtr(handleItem->GrantedAccess)); + PhInitializeStringRefLongHint(&getCellText->Text, handleItem->GrantedAccessString); + break; + case PHHNTLC_GRANTEDACCESSSYMBOLIC: + if (handleItem->GrantedAccess != 0) + { + if (!node->GrantedAccessSymbolicText) + { + PPH_ACCESS_ENTRY accessEntries; + ULONG numberOfAccessEntries; + + if (PhGetAccessEntries(PhGetStringOrEmpty(handleItem->TypeName), &accessEntries, &numberOfAccessEntries)) + { + node->GrantedAccessSymbolicText = PhGetAccessString(handleItem->GrantedAccess, accessEntries, numberOfAccessEntries); + PhFree(accessEntries); + } + } + + getCellText->Text = PhGetStringRef(node->GrantedAccessSymbolicText); + } + break; + case PHHNTLC_ORIGINALNAME: + getCellText->Text = PhGetStringRef(handleItem->ObjectName); + break; + case PHHNTLC_FILESHAREACCESS: + if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_MASK) + { + node->FileShareAccessText[0] = '-'; + node->FileShareAccessText[1] = '-'; + node->FileShareAccessText[2] = '-'; + node->FileShareAccessText[3] = UNICODE_NULL; + + if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_READ) + node->FileShareAccessText[0] = 'R'; + if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_WRITE) + node->FileShareAccessText[1] = 'W'; + if (handleItem->FileFlags & PH_HANDLE_FILE_SHARED_DELETE) + node->FileShareAccessText[2] = 'D'; + + PhInitializeStringRef(&getCellText->Text, node->FileShareAccessText); + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PPH_HANDLE_ITEM handleItem; + + node = (PPH_HANDLE_NODE)getNodeColor->Node; + handleItem = node->HandleItem; + + if (!handleItem) + ; // Dummy + else if (PhCsUseColorProtectedHandles && (handleItem->Attributes & OBJ_PROTECT_CLOSE)) + getNodeColor->BackColor = PhCsColorProtectedHandles; + else if (PhCsUseColorInheritHandles && (handleItem->Attributes & OBJ_INHERIT)) + getNodeColor->BackColor = PhCsColorInheritHandles; + + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(context->TreeNewHandle, 0, -1); + break; + case VK_DELETE: + // Pass a 1 in lParam to indicate that warnings should be enabled. + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_CLOSE, 1); + break; + case VK_RETURN: + if (GetKeyState(VK_CONTROL) >= 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_PROPERTIES, 0); + else + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_OBJECTPROPERTIES1, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_HANDLE_PROPERTIES, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); + } + return TRUE; + case TreeNewGetDialogCode: + { + PULONG code = Parameter2; + + if (PtrToUlong(Parameter1) == VK_RETURN) + { + *code = DLGC_WANTMESSAGE; + return TRUE; + } + } + return FALSE; + } + + return FALSE; +} + +PPH_HANDLE_ITEM PhGetSelectedHandleItem( + _In_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + PPH_HANDLE_ITEM handleItem = NULL; + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_HANDLE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + handleItem = node->HandleItem; + break; + } + } + + return handleItem; +} + +VOID PhGetSelectedHandleItems( + _In_ PPH_HANDLE_LIST_CONTEXT Context, + _Out_ PPH_HANDLE_ITEM **Handles, + _Out_ PULONG NumberOfHandles + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_HANDLE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node->HandleItem); + } + + *NumberOfHandles = (ULONG)array.Count; + *Handles = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllHandleNodes( + _In_ PPH_HANDLE_LIST_CONTEXT Context + ) +{ + TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); +} diff --git a/ProcessHacker/hndlmenu.c b/ProcessHacker/hndlmenu.c index 8c842f7fd0d1..20bb605fdb9b 100644 --- a/ProcessHacker/hndlmenu.c +++ b/ProcessHacker/hndlmenu.c @@ -44,29 +44,32 @@ VOID PhInsertHandleObjectPropertiesEMenuItems( if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertBeforeId, &parentItem, &indexInParent)) return; + if (PhIsNullOrEmptyString(Info->TypeName)) + return; + if (PhEqualString2(Info->TypeName, L"File", TRUE) || PhEqualString2(Info->TypeName, L"DLL", TRUE) || PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE)) { if (PhEqualString2(Info->TypeName, L"File", TRUE)) - PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES2, L"File properties", NULL, NULL), indexInParent); + PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES2, L"File propert&ies", NULL, NULL), indexInParent); PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Open &file location", EnableShortcut), NULL, NULL), indexInParent); } else if (PhEqualString2(Info->TypeName, L"Key", TRUE)) { - PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Open key", EnableShortcut), NULL, NULL), indexInParent); + PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Open &key", EnableShortcut), NULL, NULL), indexInParent); } else if (PhEqualString2(Info->TypeName, L"Process", TRUE)) { - PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Process properties", EnableShortcut), NULL, NULL), indexInParent); + PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Process propert&ies", EnableShortcut), NULL, NULL), indexInParent); } else if (PhEqualString2(Info->TypeName, L"Section", TRUE)) { - PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Read/Write memory", EnableShortcut), NULL, NULL), indexInParent); + PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Read/Write &memory", EnableShortcut), NULL, NULL), indexInParent); } else if (PhEqualString2(Info->TypeName, L"Thread", TRUE)) { - PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Go to thread", EnableShortcut), NULL, NULL), indexInParent); + PhInsertEMenuItem(parentItem, PhCreateEMenuItem(0, ID_HANDLE_OBJECTPROPERTIES1, PhaAppendCtrlEnter(L"Go to t&hread", EnableShortcut), NULL, NULL), indexInParent); } } @@ -114,11 +117,22 @@ VOID PhShowHandleObjectProperties1( _In_ PPH_HANDLE_ITEM_INFO Info ) { + if (PhIsNullOrEmptyString(Info->TypeName)) + return; + if (PhEqualString2(Info->TypeName, L"File", TRUE) || PhEqualString2(Info->TypeName, L"DLL", TRUE) || PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE)) { if (Info->BestObjectName) - PhShellExploreFile(hWnd, Info->BestObjectName->Buffer); + { + PhShellExecuteUserString( + hWnd, + L"FileBrowseExecutable", + Info->BestObjectName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } else PhShowError(hWnd, L"Unable to open file location because the object is unnamed."); } @@ -141,7 +155,7 @@ VOID PhShowHandleObjectProperties1( { if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, Info->ProcessId ))) { @@ -169,7 +183,7 @@ VOID PhShowHandleObjectProperties1( if (NT_SUCCESS(PhpDuplicateHandleFromProcessItem( &handle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, Info->ProcessId, Info->Handle ))) @@ -198,17 +212,18 @@ VOID PhShowHandleObjectProperties1( } else if (PhEqualString2(Info->TypeName, L"Section", TRUE)) { + NTSTATUS status; HANDLE handle = NULL; BOOLEAN readOnly = FALSE; - if (!NT_SUCCESS(PhpDuplicateHandleFromProcessItem( + if (!NT_SUCCESS(status = PhpDuplicateHandleFromProcessItem( &handle, SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE, Info->ProcessId, Info->Handle ))) { - PhpDuplicateHandleFromProcessItem( + status = PhpDuplicateHandleFromProcessItem( &handle, SECTION_QUERY | SECTION_MAP_READ, Info->ProcessId, @@ -219,16 +234,15 @@ VOID PhShowHandleObjectProperties1( if (handle) { - NTSTATUS status; PPH_STRING sectionName = NULL; SECTION_BASIC_INFORMATION basicInfo; SIZE_T viewSize = PH_MAX_SECTION_EDIT_SIZE; PVOID viewBase = NULL; BOOLEAN tooBig = FALSE; - PhGetHandleInformation(NtCurrentProcess(), handle, -1, NULL, NULL, NULL, §ionName); + PhGetHandleInformation(NtCurrentProcess(), handle, ULONG_MAX, NULL, NULL, NULL, §ionName); - if (NT_SUCCESS(PhGetSectionBasicInformation(handle, &basicInfo))) + if (NT_SUCCESS(status = PhGetSectionBasicInformation(handle, &basicInfo))) { if (basicInfo.MaximumSize.QuadPart <= PH_MAX_SECTION_EDIT_SIZE) viewSize = (SIZE_T)basicInfo.MaximumSize.QuadPart; @@ -275,7 +289,7 @@ VOID PhShowHandleObjectProperties1( showMemoryEditor->ProcessId = NtCurrentProcessId(); showMemoryEditor->BaseAddress = viewBase; showMemoryEditor->RegionSize = viewSize; - showMemoryEditor->SelectOffset = -1; + showMemoryEditor->SelectOffset = ULONG_MAX; showMemoryEditor->SelectLength = 0; showMemoryEditor->Title = sectionName ? PhConcatStrings2(L"Section - ", sectionName->Buffer) : PhCreateString(L"Section"); showMemoryEditor->Flags = PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION; @@ -283,7 +297,7 @@ VOID PhShowHandleObjectProperties1( } else { - PhShowStatus(hWnd, L"Unable to map a view of the section", status, 0); + PhShowStatus(hWnd, L"Unable to map a view of the section.", status, 0); } } @@ -291,6 +305,11 @@ VOID PhShowHandleObjectProperties1( NtClose(handle); } + + if (!NT_SUCCESS(status)) + { + PhShowStatus(hWnd, L"Unable to query the section.", status, 0); + } } else if (PhEqualString2(Info->TypeName, L"Thread", TRUE)) { @@ -306,7 +325,7 @@ VOID PhShowHandleObjectProperties1( { if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, Info->ProcessId ))) { @@ -334,7 +353,7 @@ VOID PhShowHandleObjectProperties1( if (NT_SUCCESS(PhpDuplicateHandleFromProcessItem( &handle, - ThreadQueryAccess, + THREAD_QUERY_LIMITED_INFORMATION, Info->ProcessId, Info->Handle ))) @@ -370,6 +389,9 @@ VOID PhShowHandleObjectProperties2( _In_ PPH_HANDLE_ITEM_INFO Info ) { + if (PhIsNullOrEmptyString(Info->TypeName)) + return; + if (PhEqualString2(Info->TypeName, L"File", TRUE) || PhEqualString2(Info->TypeName, L"DLL", TRUE) || PhEqualString2(Info->TypeName, L"Mapped file", TRUE) || PhEqualString2(Info->TypeName, L"Mapped image", TRUE)) { diff --git a/ProcessHacker/hndlprp.c b/ProcessHacker/hndlprp.c index 4c1123c36cf2..fe5158ae44ea 100644 --- a/ProcessHacker/hndlprp.c +++ b/ProcessHacker/hndlprp.c @@ -1,336 +1,1348 @@ -/* - * Process Hacker - - * handle properties - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include -#include - -#include -#include - -typedef struct _HANDLE_PROPERTIES_CONTEXT -{ - HANDLE ProcessId; - PPH_HANDLE_ITEM HandleItem; -} HANDLE_PROPERTIES_CONTEXT, *PHANDLE_PROPERTIES_CONTEXT; - -INT_PTR CALLBACK PhpHandleGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static NTSTATUS PhpDuplicateHandleFromProcess( - _Out_ PHANDLE Handle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PHANDLE_PROPERTIES_CONTEXT context = Context; - HANDLE processHandle; - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_DUP_HANDLE, - context->ProcessId - ))) - return status; - - status = NtDuplicateObject( - processHandle, - context->HandleItem->Handle, - NtCurrentProcess(), - Handle, - DesiredAccess, - 0, - 0 - ); - NtClose(processHandle); - - return status; -} - -VOID PhShowHandleProperties( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId, - _In_ PPH_HANDLE_ITEM HandleItem - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[16]; - HANDLE_PROPERTIES_CONTEXT context; - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - - context.ProcessId = ProcessId; - context.HandleItem = HandleItem; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = L"Handle"; - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - // General page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_HNDLGENERAL); - propSheetPage.pfnDlgProc = PhpHandleGeneralDlgProc; - propSheetPage.lParam = (LPARAM)&context; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - // Object-specific page - if (!HandleItem->TypeName) - { - // Dummy - } - else if (PhEqualString2(HandleItem->TypeName, L"Event", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateEventPage( - PhpDuplicateHandleFromProcess, - &context - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"EventPair", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateEventPairPage( - PhpDuplicateHandleFromProcess, - &context - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"Job", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateJobPage( - PhpDuplicateHandleFromProcess, - &context, - NULL - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"Mutant", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateMutantPage( - PhpDuplicateHandleFromProcess, - &context - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"Section", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateSectionPage( - PhpDuplicateHandleFromProcess, - &context - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"Semaphore", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateSemaphorePage( - PhpDuplicateHandleFromProcess, - &context - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"Timer", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateTimerPage( - PhpDuplicateHandleFromProcess, - &context - ); - } - else if (PhEqualString2(HandleItem->TypeName, L"Token", TRUE)) - { - pages[propSheetHeader.nPages++] = PhCreateTokenPage( - PhpDuplicateHandleFromProcess, - &context, - NULL - ); - } - - // Security page - stdObjectSecurity.OpenObject = PhpDuplicateHandleFromProcess; - stdObjectSecurity.ObjectType = HandleItem->TypeName->Buffer; - stdObjectSecurity.Context = &context; - - if (PhGetAccessEntries(HandleItem->TypeName->Buffer, &accessEntries, &numberOfAccessEntries)) - { - pages[propSheetHeader.nPages++] = PhCreateSecurityPage( - PhGetStringOrEmpty(HandleItem->BestObjectName), - PhStdGetObjectSecurity, - PhStdSetObjectSecurity, - &stdObjectSecurity, - accessEntries, - numberOfAccessEntries - ); - PhFree(accessEntries); - } - - if (PhPluginsEnabled) - { - PH_PLUGIN_OBJECT_PROPERTIES objectProperties; - PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT propertiesContext; - - propertiesContext.ProcessId = ProcessId; - propertiesContext.HandleItem = HandleItem; - - objectProperties.Parameter = &propertiesContext; - objectProperties.NumberOfPages = propSheetHeader.nPages; - objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); - objectProperties.Pages = pages; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackHandlePropertiesInitializing), &objectProperties); - - propSheetHeader.nPages = objectProperties.NumberOfPages; - } - - PhModalPropertySheet(&propSheetHeader); -} - -INT_PTR CALLBACK PhpHandleGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PHANDLE_PROPERTIES_CONTEXT context = (PHANDLE_PROPERTIES_CONTEXT)propSheetPage->lParam; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - HANDLE processHandle; - OBJECT_BASIC_INFORMATION basicInfo; - BOOLEAN haveBasicInfo = FALSE; - - // HACK - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - - SetDlgItemText(hwndDlg, IDC_NAME, PhGetString(context->HandleItem->BestObjectName)); - SetDlgItemText(hwndDlg, IDC_TYPE, context->HandleItem->TypeName->Buffer); - SetDlgItemText(hwndDlg, IDC_ADDRESS, context->HandleItem->ObjectString); - - if (PhGetAccessEntries( - context->HandleItem->TypeName->Buffer, - &accessEntries, - &numberOfAccessEntries - )) - { - PPH_STRING accessString; - PPH_STRING grantedAccessString; - - accessString = PH_AUTO(PhGetAccessString( - context->HandleItem->GrantedAccess, - accessEntries, - numberOfAccessEntries - )); - - if (accessString->Length != 0) - { - grantedAccessString = PhaFormatString( - L"%s (%s)", - context->HandleItem->GrantedAccessString, - accessString->Buffer - ); - SetDlgItemText(hwndDlg, IDC_GRANTED_ACCESS, grantedAccessString->Buffer); - } - else - { - SetDlgItemText(hwndDlg, IDC_GRANTED_ACCESS, context->HandleItem->GrantedAccessString); - } - - PhFree(accessEntries); - } - else - { - SetDlgItemText(hwndDlg, IDC_GRANTED_ACCESS, context->HandleItem->GrantedAccessString); - } - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_DUP_HANDLE, - context->ProcessId - ))) - { - if (NT_SUCCESS(PhGetHandleInformation( - processHandle, - context->HandleItem->Handle, - -1, - &basicInfo, - NULL, - NULL, - NULL - ))) - { - SetDlgItemInt(hwndDlg, IDC_REFERENCES, basicInfo.PointerCount, FALSE); - SetDlgItemInt(hwndDlg, IDC_HANDLES, basicInfo.HandleCount, FALSE); - SetDlgItemInt(hwndDlg, IDC_PAGED, basicInfo.PagedPoolCharge, FALSE); - SetDlgItemInt(hwndDlg, IDC_NONPAGED, basicInfo.NonPagedPoolCharge, FALSE); - - haveBasicInfo = TRUE; - } - - NtClose(processHandle); - } - - if (!haveBasicInfo) - { - SetDlgItemText(hwndDlg, IDC_REFERENCES, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_HANDLES, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_PAGED, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_NONPAGED, L"Unknown"); - } - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_BASICINFORMATION)); - } - return TRUE; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * handle properties + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef enum _PHP_HANDLE_GENERAL_CATEGORY +{ + // common + PH_HANDLE_GENERAL_CATEGORY_BASICINFO, + PH_HANDLE_GENERAL_CATEGORY_REFERENCES, + PH_HANDLE_GENERAL_CATEGORY_QUOTA, + // extra + PH_HANDLE_GENERAL_CATEGORY_ALPC, + PH_HANDLE_GENERAL_CATEGORY_FILE, + PH_HANDLE_GENERAL_CATEGORY_SECTION, + PH_HANDLE_GENERAL_CATEGORY_MUTANT, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + + PH_HANDLE_GENERAL_CATEGORY_MAXIMUM +} PHP_HANDLE_GENERAL_CATEGORY; + +typedef enum _PHP_HANDLE_GENERAL_INDEX +{ + PH_HANDLE_GENERAL_INDEX_NAME, + PH_HANDLE_GENERAL_INDEX_TYPE, + PH_HANDLE_GENERAL_INDEX_OBJECT, + PH_HANDLE_GENERAL_INDEX_ACCESSMASK, + + PH_HANDLE_GENERAL_INDEX_REFERENCES, + PH_HANDLE_GENERAL_INDEX_HANDLES, + + PH_HANDLE_GENERAL_INDEX_PAGED, + PH_HANDLE_GENERAL_INDEX_NONPAGED, + + PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER, + PH_HANDLE_GENERAL_INDEX_PORTCONTEXT, + + PH_HANDLE_GENERAL_INDEX_FILETYPE, + PH_HANDLE_GENERAL_INDEX_FILEMODE, + PH_HANDLE_GENERAL_INDEX_FILEPOSITION, + PH_HANDLE_GENERAL_INDEX_FILESIZE, + + PH_HANDLE_GENERAL_INDEX_SECTIONTYPE, + PH_HANDLE_GENERAL_INDEX_SECTIONFILE, + PH_HANDLE_GENERAL_INDEX_SECTIONSIZE, + + PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT, + PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED, + PH_HANDLE_GENERAL_INDEX_MUTANTOWNER, + + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, + + PH_HANDLE_GENERAL_INDEX_MAXIMUM +} PHP_PROCESS_STATISTICS_INDEX; + +typedef struct _HANDLE_PROPERTIES_CONTEXT +{ + HWND ListViewHandle; + HANDLE ProcessId; + PPH_HANDLE_ITEM HandleItem; + PH_LAYOUT_MANAGER LayoutManager; + ULONG ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MAXIMUM]; +} HANDLE_PROPERTIES_CONTEXT, *PHANDLE_PROPERTIES_CONTEXT; + +#define PH_FILEMODE_ASYNC 0x01000000 +#define PhFileModeUpdAsyncFlag(mode) \ + (mode & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT) ? mode &~ PH_FILEMODE_ASYNC: mode | PH_FILEMODE_ASYNC) + +PH_ACCESS_ENTRY FileModeAccessEntries[6] = +{ + { L"FILE_FLAG_OVERLAPPED", PH_FILEMODE_ASYNC, FALSE, FALSE, L"Asynchronous" }, + { L"FILE_FLAG_WRITE_THROUGH", FILE_WRITE_THROUGH, FALSE, FALSE, L"Write through" }, + { L"FILE_FLAG_SEQUENTIAL_SCAN", FILE_SEQUENTIAL_ONLY, FALSE, FALSE, L"Sequental" }, + { L"FILE_FLAG_NO_BUFFERING", FILE_NO_INTERMEDIATE_BUFFERING, FALSE, FALSE, L"No buffering" }, + { L"FILE_SYNCHRONOUS_IO_ALERT", FILE_SYNCHRONOUS_IO_ALERT, FALSE, FALSE, L"Synchronous alert" }, + { L"FILE_SYNCHRONOUS_IO_NONALERT", FILE_SYNCHRONOUS_IO_NONALERT, FALSE, FALSE, L"Synchronous non-alert" }, +}; + +INT_PTR CALLBACK PhpHandleGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static NTSTATUS PhpDuplicateHandleFromProcess( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PHANDLE_PROPERTIES_CONTEXT context = Context; + HANDLE processHandle; + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + context->ProcessId + ))) + return status; + + status = NtDuplicateObject( + processHandle, + context->HandleItem->Handle, + NtCurrentProcess(), + Handle, + DesiredAccess, + 0, + 0 + ); + NtClose(processHandle); + + return status; +} + +typedef struct _HANDLE_PROPERTIES_THREAD_CONTEXT +{ + HWND ParentWindowHandle; + HANDLE ProcessId; + PPH_HANDLE_ITEM HandleItem; +} HANDLE_PROPERTIES_THREAD_CONTEXT, *PHANDLE_PROPERTIES_THREAD_CONTEXT; + +NTSTATUS PhpShowHandlePropertiesThread( + _In_ PVOID Parameter + ) +{ + PHANDLE_PROPERTIES_THREAD_CONTEXT handleContext = Parameter; + PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) }; + PROPSHEETPAGE propSheetPage; + HPROPSHEETPAGE pages[16]; + HANDLE_PROPERTIES_CONTEXT context; + PH_AUTO_POOL autoPool; + + context.ProcessId = handleContext->ProcessId; + context.HandleItem = handleContext->HandleItem; + + PhInitializeAutoPool(&autoPool); + + propSheetHeader.dwFlags = + PSH_MODELESS | + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.hwndParent = handleContext->ParentWindowHandle; + propSheetHeader.pszCaption = L"Handle"; + propSheetHeader.nPages = 0; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = pages; + + // General page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_HNDLGENERAL); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = PhpHandleGeneralDlgProc; + propSheetPage.lParam = (LPARAM)&context; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + // Object-specific page + if (PhIsNullOrEmptyString(handleContext->HandleItem->TypeName)) + { + NOTHING; + } + else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Event", TRUE)) + { + pages[propSheetHeader.nPages++] = PhCreateEventPage( + PhpDuplicateHandleFromProcess, + &context + ); + } + else if (PhEqualString2(handleContext->HandleItem->TypeName, L"EventPair", TRUE)) + { + pages[propSheetHeader.nPages++] = PhCreateEventPairPage( + PhpDuplicateHandleFromProcess, + &context + ); + } + else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Job", TRUE)) + { + pages[propSheetHeader.nPages++] = PhCreateJobPage( + PhpDuplicateHandleFromProcess, + &context, + NULL + ); + } + else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Semaphore", TRUE)) + { + pages[propSheetHeader.nPages++] = PhCreateSemaphorePage( + PhpDuplicateHandleFromProcess, + &context + ); + } + else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Timer", TRUE)) + { + pages[propSheetHeader.nPages++] = PhCreateTimerPage( + PhpDuplicateHandleFromProcess, + &context + ); + } + else if (PhEqualString2(handleContext->HandleItem->TypeName, L"Token", TRUE)) + { + pages[propSheetHeader.nPages++] = PhCreateTokenPage( + PhpDuplicateHandleFromProcess, + context.ProcessId, + &context, + NULL + ); + } + + // Security page + pages[propSheetHeader.nPages++] = PhCreateSecurityPage( + PhGetStringOrEmpty(handleContext->HandleItem->BestObjectName), + PhGetStringOrEmpty(handleContext->HandleItem->TypeName), + PhpDuplicateHandleFromProcess, + NULL, + &context + ); + + if (PhPluginsEnabled) + { + PH_PLUGIN_OBJECT_PROPERTIES objectProperties; + PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT propertiesContext; + + propertiesContext.ProcessId = handleContext->ProcessId; + propertiesContext.HandleItem = handleContext->HandleItem; + + objectProperties.Parameter = &propertiesContext; + objectProperties.NumberOfPages = propSheetHeader.nPages; + objectProperties.MaximumNumberOfPages = RTL_NUMBER_OF(pages); + objectProperties.Pages = pages; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackHandlePropertiesInitializing), &objectProperties); + + propSheetHeader.nPages = objectProperties.NumberOfPages; + } + + PhModalPropertySheet(&propSheetHeader); + + PhDeleteAutoPool(&autoPool); + + PhDereferenceObject(handleContext->HandleItem); + PhFree(handleContext); + + return STATUS_SUCCESS; +} + +VOID PhShowHandleProperties( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId, + _In_ PPH_HANDLE_ITEM HandleItem + ) +{ + PHANDLE_PROPERTIES_THREAD_CONTEXT context; + + context = PhAllocate(sizeof(HANDLE_PROPERTIES_THREAD_CONTEXT)); + context->ParentWindowHandle = PhCsForceNoParent ? NULL : ParentWindowHandle; + context->ProcessId = ProcessId; + context->HandleItem = HandleItem; + PhReferenceObject(HandleItem); + + PhCreateThread2(PhpShowHandlePropertiesThread, context); +} + +VOID PhpUpdateHandleGeneralListViewGroups( + _In_ PHANDLE_PROPERTIES_CONTEXT Context + ) +{ + ListView_EnableGroupView(Context->ListViewHandle, TRUE); + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_BASICINFO, L"Basic information"); + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_REFERENCES, L"References"); + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_QUOTA, L"Quota charges"); + + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_NAME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_BASICINFO, + PH_HANDLE_GENERAL_INDEX_NAME, + L"Name", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_TYPE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_BASICINFO, + PH_HANDLE_GENERAL_INDEX_TYPE, + L"Type", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_OBJECT] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_BASICINFO, + PH_HANDLE_GENERAL_INDEX_OBJECT, + L"Object address", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_ACCESSMASK] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_BASICINFO, + PH_HANDLE_GENERAL_INDEX_ACCESSMASK, + L"Granted access", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_REFERENCES] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_REFERENCES, + PH_HANDLE_GENERAL_INDEX_REFERENCES, + L"References", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_HANDLES] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_REFERENCES, + PH_HANDLE_GENERAL_INDEX_HANDLES, + L"Handles", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PAGED] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_QUOTA, + PH_HANDLE_GENERAL_INDEX_PAGED, + L"Paged", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_NONPAGED] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_QUOTA, + PH_HANDLE_GENERAL_INDEX_NONPAGED, + L"Virtual size", + NULL + ); + + if (PhIsNullOrEmptyString(Context->HandleItem->TypeName)) + { + NOTHING; + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"ALPC Port", TRUE)) + { + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_ALPC, L"ALPC Port"); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_ALPC, + PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER, + L"Sequence Number", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PORTCONTEXT] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_ALPC, + PH_HANDLE_GENERAL_INDEX_PORTCONTEXT, + L"Port Context", + NULL + ); + } + else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"File", TRUE)) + { + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_FILE, L"File information"); + + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILETYPE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_FILE, + PH_HANDLE_GENERAL_INDEX_FILETYPE, + L"Type", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILEMODE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_FILE, + PH_HANDLE_GENERAL_INDEX_FILEMODE, + L"Mode", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILEPOSITION] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_FILE, + PH_HANDLE_GENERAL_INDEX_FILEPOSITION, + L"Position", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILESIZE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_FILE, + PH_HANDLE_GENERAL_INDEX_FILESIZE, + L"Size", + NULL + ); + } + else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Section", TRUE)) + { + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_SECTION, L"Section information"); + + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SECTIONTYPE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_SECTION, + PH_HANDLE_GENERAL_INDEX_SECTIONTYPE, + L"Type", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SECTIONFILE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_SECTION, + PH_HANDLE_GENERAL_INDEX_SECTIONFILE, + L"File", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SECTIONSIZE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_SECTION, + PH_HANDLE_GENERAL_INDEX_SECTIONSIZE, + L"Size", + NULL + ); + } + else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Mutant", TRUE)) + { + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_MUTANT, L"Mutant information"); + + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_MUTANT, + PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT, + L"Count", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_MUTANT, + PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED, + L"Abandoned", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MUTANTOWNER] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_MUTANT, + PH_HANDLE_GENERAL_INDEX_MUTANTOWNER, + L"Owner", + NULL + ); + } + else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Process", TRUE)) + { + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, L"Process information"); + + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, + L"Name", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, + L"Created", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, + L"Exited", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, + L"Exit status", + NULL + ); + } + else if (PhEqualStringRef2(&Context->HandleItem->TypeName->sr, L"Thread", TRUE)) + { + PhAddListViewGroup(Context->ListViewHandle, PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, L"Thread information"); + + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME, + L"Name", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME, + L"Created", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME, + L"Exited", + NULL + ); + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE] = PhAddListViewGroupItem( + Context->ListViewHandle, + PH_HANDLE_GENERAL_CATEGORY_PROCESSTHREAD, + PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE, + L"Exit status", + NULL + ); + } +} + +VOID PhpUpdateHandleGeneral( + _In_ PHANDLE_PROPERTIES_CONTEXT Context + ) +{ + HANDLE processHandle; + PPH_ACCESS_ENTRY accessEntries; + ULONG numberOfAccessEntries; + OBJECT_BASIC_INFORMATION basicInfo; + WCHAR string[PH_INT64_STR_LEN_1]; + + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_NAME], 1, PhGetStringOrEmpty(Context->HandleItem->BestObjectName)); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_TYPE], 1, PhGetStringOrEmpty(Context->HandleItem->TypeName)); + + if (Context->HandleItem->Object) + { + PhPrintPointer(string, Context->HandleItem->Object); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_OBJECT], 1, string); + } + + if (PhGetAccessEntries( + PhGetStringOrEmpty(Context->HandleItem->TypeName), + &accessEntries, + &numberOfAccessEntries + )) + { + PPH_STRING accessString; + PPH_STRING grantedAccessString; + + accessString = PH_AUTO(PhGetAccessString( + Context->HandleItem->GrantedAccess, + accessEntries, + numberOfAccessEntries + )); + + if (accessString->Length != 0) + { + grantedAccessString = PH_AUTO(PhFormatString( + L"0x%x (%s)", + Context->HandleItem->GrantedAccess, + accessString->Buffer + )); + + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_ACCESSMASK, 1, grantedAccessString->Buffer); + } + else + { + PhPrintPointer(string, UlongToPtr(Context->HandleItem->GrantedAccess)); + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_ACCESSMASK, 1, string); + } + + PhFree(accessEntries); + } + else + { + PhPrintPointer(string, UlongToPtr(Context->HandleItem->GrantedAccess)); + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_ACCESSMASK, 1, string); + } + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + if (NT_SUCCESS(PhGetHandleInformation( + processHandle, + Context->HandleItem->Handle, + ULONG_MAX, + &basicInfo, + NULL, + NULL, + NULL + ))) + { + PhPrintUInt32(string, basicInfo.PointerCount); + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_REFERENCES, 1, string); + + PhPrintUInt32(string, basicInfo.HandleCount); + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_HANDLES, 1, string); + + PhPrintUInt32(string, basicInfo.PagedPoolCharge); + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_PAGED, 1, string); + + PhPrintUInt32(string, basicInfo.NonPagedPoolCharge); + PhSetListViewSubItem(Context->ListViewHandle, PH_HANDLE_GENERAL_INDEX_NONPAGED, 1, string); + } + + NtClose(processHandle); + } + + if (PhIsNullOrEmptyString(Context->HandleItem->TypeName)) + { + NOTHING; + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"ALPC Port", TRUE)) + { + NTSTATUS status; + HANDLE processHandle; + HANDLE alpcPortHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + &alpcPortHandle, + READ_CONTROL, + 0, + 0 + ); + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + ALPC_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(NtAlpcQueryInformation( + alpcPortHandle, + AlpcBasicInformation, + &basicInfo, + sizeof(ALPC_BASIC_INFORMATION), + NULL + ))) + { + PH_FORMAT format[1]; + PPH_STRING string; + + PhInitFormatI64UGroupDigits(&format[0], basicInfo.SequenceNo); + + string = PhFormat(format, 1, 128); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SEQUENCENUMBER], 1, string->Buffer); + PhDereferenceObject(string); + + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PORTCONTEXT], 1, + PhaFormatString(L"0x%Ix", basicInfo.PortContext)->Buffer); + } + + NtClose(alpcPortHandle); + } + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"File", TRUE)) + { + NTSTATUS status; + HANDLE processHandle; + HANDLE fileHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + &fileHandle, + MAXIMUM_ALLOWED, + 0, + 0 + ); + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + BOOLEAN disableFlushButton = FALSE; + BOOLEAN isFileOrDirectory = FALSE; + BOOLEAN isConsoleHandle = FALSE; + BOOLEAN isPipeHandle = FALSE; + FILE_FS_DEVICE_INFORMATION fileDeviceInfo; + FILE_MODE_INFORMATION fileModeInfo; + FILE_STANDARD_INFORMATION fileStandardInfo; + FILE_POSITION_INFORMATION filePositionInfo; + IO_STATUS_BLOCK isb; + + if (NT_SUCCESS(NtQueryVolumeInformationFile( + fileHandle, + &isb, + &fileDeviceInfo, + sizeof(FILE_FS_DEVICE_INFORMATION), + FileFsDeviceInformation + ))) + { + switch (fileDeviceInfo.DeviceType) + { + case FILE_DEVICE_NAMED_PIPE: + isPipeHandle = TRUE; + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILETYPE], 1, L"Pipe"); + break; + case FILE_DEVICE_CD_ROM: + case FILE_DEVICE_CD_ROM_FILE_SYSTEM: + case FILE_DEVICE_CONTROLLER: + case FILE_DEVICE_DATALINK: + case FILE_DEVICE_DFS: + case FILE_DEVICE_DISK: + case FILE_DEVICE_DISK_FILE_SYSTEM: + case FILE_DEVICE_VIRTUAL_DISK: + isFileOrDirectory = TRUE; + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILETYPE], 1, L"File or directory"); + break; + case FILE_DEVICE_CONSOLE: + isConsoleHandle = TRUE; + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILETYPE], 1, L"Console"); + break; + default: + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILETYPE], 1, L"Other"); + break; + } + } + + if (isPipeHandle || isConsoleHandle) + { + // NOTE: NtQueryInformationFile for '\Device\ConDrv\CurrentIn' causes a deadlock but + // we can query other '\Device\ConDrv' console handles. NtQueryInformationFile also + // causes a deadlock for some types of named pipes and only on Win10 (dmex) + status = PhCallNtQueryFileInformationWithTimeout( + fileHandle, + FileModeInformation, + &fileModeInfo, + sizeof(FILE_MODE_INFORMATION) + ); + } + else + { + status = NtQueryInformationFile( + fileHandle, + &isb, + &fileModeInfo, + sizeof(FILE_MODE_INFORMATION), + FileModeInformation + ); + } + + if (NT_SUCCESS(status)) + { + PH_FORMAT format[5]; + PPH_STRING fileModeAccessStr; + WCHAR fileModeString[MAX_PATH]; + + // Since FILE_MODE_INFORMATION has no flag for asynchronous I/O we should use our own flag and set + // it only if none of synchronous flags are present. That's why we need PhFileModeUpdAsyncFlag. + fileModeAccessStr = PhGetAccessString( + PhFileModeUpdAsyncFlag(fileModeInfo.Mode), + FileModeAccessEntries, + RTL_NUMBER_OF(FileModeAccessEntries) + ); + + PhInitFormatS(&format[0], L"0x"); + PhInitFormatX(&format[1], fileModeInfo.Mode); + PhInitFormatS(&format[2], L" ("); + PhInitFormatSR(&format[3], fileModeAccessStr->sr); + PhInitFormatS(&format[4], L")"); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), fileModeString, sizeof(fileModeString), NULL)) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILEMODE], 1, fileModeString); + } + + PhDereferenceObject(fileModeAccessStr); + } + + if (!isConsoleHandle) + { + if (NT_SUCCESS(NtQueryInformationFile( + fileHandle, + &isb, + &fileStandardInfo, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation + ))) + { + PH_FORMAT format[1]; + WCHAR fileSizeString[PH_INT64_STR_LEN]; + + PhInitFormatSize(&format[0], fileStandardInfo.EndOfFile.QuadPart); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), fileSizeString, sizeof(fileSizeString), NULL)) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILESIZE], 1, fileSizeString); + } + + if (isFileOrDirectory) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILETYPE], 1, fileStandardInfo.Directory ? L"Directory" : L"File"); + } + + disableFlushButton |= fileStandardInfo.Directory; + } + + if (NT_SUCCESS(NtQueryInformationFile( + fileHandle, + &isb, + &filePositionInfo, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation + ))) + { + if (filePositionInfo.CurrentByteOffset.QuadPart != 0 && fileStandardInfo.EndOfFile.QuadPart != 0) + { + PH_FORMAT format[4]; + WCHAR filePositionString[PH_INT64_STR_LEN]; + + PhInitFormatI64UGroupDigits(&format[0], filePositionInfo.CurrentByteOffset.QuadPart); + PhInitFormatS(&format[1], L" ("); + PhInitFormatF(&format[2], (DOUBLE)filePositionInfo.CurrentByteOffset.QuadPart / (DOUBLE)fileStandardInfo.EndOfFile.QuadPart * 100.0, 1); + PhInitFormatS(&format[3], L"%)"); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), filePositionString, sizeof(filePositionString), NULL)) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILEPOSITION], 1, filePositionString); + } + } + else if (filePositionInfo.CurrentByteOffset.QuadPart != 0) + { + PH_FORMAT format[1]; + WCHAR filePositionString[PH_INT64_STR_LEN]; + + PhInitFormatI64UGroupDigits(&format[0], filePositionInfo.CurrentByteOffset.QuadPart); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), filePositionString, sizeof(filePositionString), NULL)) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_FILEPOSITION], 1, filePositionString); + } + } + } + } + + NtClose(fileHandle); + } + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"Section", TRUE)) + { + NTSTATUS status; + HANDLE processHandle; + HANDLE sectionHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + §ionHandle, + SECTION_QUERY | SECTION_MAP_READ, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + §ionHandle, + SECTION_QUERY, + 0, + 0 + ); + } + + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + SECTION_BASIC_INFORMATION basicInfo; + PWSTR sectionType = L"Unknown"; + PPH_STRING sectionSize = NULL; + PPH_STRING fileName = NULL; + + if (NT_SUCCESS(PhGetSectionBasicInformation(sectionHandle, &basicInfo))) + { + if (basicInfo.AllocationAttributes & SEC_COMMIT) + sectionType = L"Commit"; + else if (basicInfo.AllocationAttributes & SEC_FILE) + sectionType = L"File"; + else if (basicInfo.AllocationAttributes & SEC_IMAGE) + sectionType = L"Image"; + else if (basicInfo.AllocationAttributes & SEC_RESERVE) + sectionType = L"Reserve"; + + sectionSize = PhaFormatSize(basicInfo.MaximumSize.QuadPart, -1); + } + + if (NT_SUCCESS(PhGetSectionFileName(sectionHandle, &fileName))) + { + PPH_STRING newFileName; + + PH_AUTO(fileName); + + if (newFileName = PhResolveDevicePrefix(fileName)) + fileName = PH_AUTO(newFileName); + } + + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SECTIONTYPE], 1, sectionType); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SECTIONFILE], 1, PhGetStringOrDefault(fileName, L"N/A")); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_SECTIONSIZE], 1, PhGetStringOrDefault(sectionSize, L"Unknown")); + + NtClose(sectionHandle); + } + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"Mutant", TRUE)) + { + NTSTATUS status; + HANDLE processHandle; + HANDLE mutantHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + &mutantHandle, + SEMAPHORE_QUERY_STATE, + 0, + 0 + ); + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + MUTANT_BASIC_INFORMATION basicInfo; + MUTANT_OWNER_INFORMATION ownerInfo; + + if (NT_SUCCESS(PhGetMutantBasicInformation(mutantHandle, &basicInfo))) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MUTANTCOUNT], 1, PhaFormatUInt64(basicInfo.CurrentCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MUTANTABANDONED], 1, basicInfo.AbandonedState ? L"True" : L"False"); + } + + if (NT_SUCCESS(PhGetMutantOwnerInformation(mutantHandle, &ownerInfo))) + { + PPH_STRING name; + + if (ownerInfo.ClientId.UniqueProcess) + { + name = PhStdGetClientIdName(&ownerInfo.ClientId); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_MUTANTOWNER], 1, name->Buffer); + PhDereferenceObject(name); + } + } + + NtClose(mutantHandle); + } + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"Process", TRUE)) + { + NTSTATUS status; + HANDLE processHandle; + HANDLE dupHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + &dupHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + 0, + 0 + ); + + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + NTSTATUS exitStatus = STATUS_PENDING; + PPH_STRING fileName; + PROCESS_BASIC_INFORMATION basicInfo; + KERNEL_USER_TIMES times; + + if (NT_SUCCESS(PhGetProcessImageFileName(dupHandle, &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME], 1, PhGetStringOrEmpty(fileName)); + PhDereferenceObject(fileName); + } + + if (NT_SUCCESS(PhGetProcessBasicInformation(dupHandle, &basicInfo))) + { + exitStatus = basicInfo.ExitStatus; + } + + if (NT_SUCCESS(PhGetProcessTimes(dupHandle, ×))) + { + SYSTEMTIME time; + + PhLargeIntegerToLocalSystemTime(&time, ×.CreateTime); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME], 1, PhaFormatDateTime(&time)->Buffer); + + if (exitStatus != STATUS_PENDING) + { + PhLargeIntegerToLocalSystemTime(&time, ×.ExitTime); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME], 1, PhaFormatDateTime(&time)->Buffer); + } + } + + if (exitStatus != STATUS_PENDING) + { + PPH_STRING status; + PPH_STRING exitcode; + + status = PhGetStatusMessage(exitStatus, 0); + exitcode = PhFormatString( + L"0x%x (%s)", + exitStatus, + PhGetStringOrDefault(status, L"Unknown") + ); + + PhSetListViewSubItem( + Context->ListViewHandle, + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE], + 1, + PhGetStringOrEmpty(exitcode) + ); + + PhDereferenceObject(exitcode); + PhClearReference(&status); + } + + NtClose(dupHandle); + } + } + else if (PhEqualString2(Context->HandleItem->TypeName, L"Thread", TRUE)) + { + NTSTATUS status; + HANDLE processHandle; + HANDLE dupHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + { + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + &dupHandle, + THREAD_QUERY_LIMITED_INFORMATION, + 0, + 0 + ); + + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + NTSTATUS exitStatus = STATUS_PENDING; + THREAD_BASIC_INFORMATION basicInfo; + KERNEL_USER_TIMES times; + PPH_STRING name; + + if (NT_SUCCESS(PhGetThreadName(dupHandle, &name))) + { + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME], 1, PhGetStringOrEmpty(name)); + PhDereferenceObject(name); + } + + + if (NT_SUCCESS(PhGetThreadBasicInformation(dupHandle, &basicInfo))) + { + exitStatus = basicInfo.ExitStatus; + + //if (NT_SUCCESS(PhOpenProcess( + // &processHandle, + // PROCESS_QUERY_LIMITED_INFORMATION, + // basicInfo.ClientId.UniqueProcess + // ))) + //{ + // if (NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName))) + // { + // PhMoveReference(&fileName, PhGetFileName(fileName)); + // PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADNAME], 1, PhGetStringOrEmpty(fileName)); + // PhDereferenceObject(fileName); + // } + // + // NtClose(processHandle); + //} + } + + if (NT_SUCCESS(PhGetThreadTimes(dupHandle, ×))) + { + SYSTEMTIME time; + + PhLargeIntegerToLocalSystemTime(&time, ×.CreateTime); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADCREATETIME], 1, PhaFormatDateTime(&time)->Buffer); + + if (exitStatus != STATUS_PENDING) + { + PhLargeIntegerToLocalSystemTime(&time, ×.ExitTime); + PhSetListViewSubItem(Context->ListViewHandle, Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITTIME], 1, PhaFormatDateTime(&time)->Buffer); + } + } + + if (exitStatus != STATUS_PENDING) + { + PPH_STRING status; + PPH_STRING exitcode; + + status = PhGetStatusMessage(exitStatus, 0); + exitcode = PhFormatString( + L"0x%x (%s)", + exitStatus, + PhGetStringOrDefault(status, L"Unknown") + ); + + PhSetListViewSubItem( + Context->ListViewHandle, + Context->ListViewRowCache[PH_HANDLE_GENERAL_INDEX_PROCESSTHREADEXITCODE], + 1, + PhGetStringOrEmpty(exitcode) + ); + + PhDereferenceObject(exitcode); + PhClearReference(&status); + } + + NtClose(dupHandle); + } + } +} + +INT_PTR CALLBACK PhpHandleGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PHANDLE_PROPERTIES_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + context = (PHANDLE_PROPERTIES_CONTEXT)propSheetPage->lParam; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // HACK + SendMessage(GetParent(hwndDlg), WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(GetParent(hwndDlg), WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 250, L"Value"); + PhSetExtendedListView(context->ListViewHandle); + + // HACK + if (PhGetIntegerPairSetting(L"HandlePropertiesWindowPosition").X != 0) + PhLoadWindowPlacementFromSetting(L"HandlePropertiesWindowPosition", NULL, GetParent(hwndDlg)); + else + PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); // HACK + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + + PhpUpdateHandleGeneralListViewGroups(context); + PhpUpdateHandleGeneral(context); + + PhRegisterWindowCallback(GetParent(hwndDlg), PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + + if (PhEnableThemeSupport) // TODO: Required for compat (dmex) + PhInitializeWindowTheme(GetParent(hwndDlg), PhEnableThemeSupport); + else + PhInitializeWindowTheme(hwndDlg, FALSE); + } + break; + case WM_DESTROY: + { + PhUnregisterWindowCallback(GetParent(hwndDlg)); + + PhSaveWindowPlacementToSetting(L"HandlePropertiesWindowPosition", NULL, GetParent(hwndDlg)); // HACK + + PhDeleteLayoutManager(&context->LayoutManager); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_BASICINFORMATION)); + } + return TRUE; + } + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + REFLECT_MESSAGE_DLG(hwndDlg, context->ListViewHandle, uMsg, wParam, lParam); + + return FALSE; +} diff --git a/ProcessHacker/hndlprv.c b/ProcessHacker/hndlprv.c index 97819bd18658..cd55a44af6ef 100644 --- a/ProcessHacker/hndlprv.c +++ b/ProcessHacker/hndlprv.c @@ -1,720 +1,710 @@ -/* - * Process Hacker - - * handle provider - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include -#include - -#include - -typedef struct _PHP_CREATE_HANDLE_ITEM_CONTEXT -{ - PPH_HANDLE_PROVIDER Provider; - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle; -} PHP_CREATE_HANDLE_ITEM_CONTEXT, *PPHP_CREATE_HANDLE_ITEM_CONTEXT; - -VOID NTAPI PhpHandleProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID NTAPI PhpHandleItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -PPH_OBJECT_TYPE PhHandleProviderType; -PPH_OBJECT_TYPE PhHandleItemType; - -BOOLEAN PhHandleProviderInitialization( - VOID - ) -{ - PhHandleProviderType = PhCreateObjectType(L"HandleProvider", 0, PhpHandleProviderDeleteProcedure); - PhHandleItemType = PhCreateObjectType(L"HandleItem", 0, PhpHandleItemDeleteProcedure); - - return TRUE; -} - -PPH_HANDLE_PROVIDER PhCreateHandleProvider( - _In_ HANDLE ProcessId - ) -{ - PPH_HANDLE_PROVIDER handleProvider; - - handleProvider = PhCreateObject( - PhEmGetObjectSize(EmHandleProviderType, sizeof(PH_HANDLE_PROVIDER)), - PhHandleProviderType - ); - - handleProvider->HandleHashSetSize = 128; - handleProvider->HandleHashSet = PhCreateHashSet(handleProvider->HandleHashSetSize); - handleProvider->HandleHashSetCount = 0; - PhInitializeQueuedLock(&handleProvider->HandleHashSetLock); - - PhInitializeCallback(&handleProvider->HandleAddedEvent); - PhInitializeCallback(&handleProvider->HandleModifiedEvent); - PhInitializeCallback(&handleProvider->HandleRemovedEvent); - PhInitializeCallback(&handleProvider->UpdatedEvent); - - handleProvider->ProcessId = ProcessId; - handleProvider->ProcessHandle = NULL; - - handleProvider->RunStatus = PhOpenProcess( - &handleProvider->ProcessHandle, - PROCESS_DUP_HANDLE, - ProcessId - ); - - handleProvider->TempListHashtable = PhCreateSimpleHashtable(20); - - PhEmCallObjectOperation(EmHandleProviderType, handleProvider, EmObjectCreate); - - return handleProvider; -} - -VOID PhpHandleProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object; - - PhEmCallObjectOperation(EmHandleProviderType, handleProvider, EmObjectDelete); - - // Dereference all handle items (we referenced them - // when we added them to the hashtable). - PhDereferenceAllHandleItems(handleProvider); - - PhFree(handleProvider->HandleHashSet); - PhDeleteCallback(&handleProvider->HandleAddedEvent); - PhDeleteCallback(&handleProvider->HandleModifiedEvent); - PhDeleteCallback(&handleProvider->HandleRemovedEvent); - - if (handleProvider->ProcessHandle) NtClose(handleProvider->ProcessHandle); - - PhDereferenceObject(handleProvider->TempListHashtable); -} - -PPH_HANDLE_ITEM PhCreateHandleItem( - _In_opt_ PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle - ) -{ - PPH_HANDLE_ITEM handleItem; - - handleItem = PhCreateObject( - PhEmGetObjectSize(EmHandleItemType, sizeof(PH_HANDLE_ITEM)), - PhHandleItemType - ); - memset(handleItem, 0, sizeof(PH_HANDLE_ITEM)); - - if (Handle) - { - handleItem->Handle = (HANDLE)Handle->HandleValue; - PhPrintPointer(handleItem->HandleString, (PVOID)handleItem->Handle); - handleItem->Object = Handle->Object; - PhPrintPointer(handleItem->ObjectString, handleItem->Object); - handleItem->Attributes = Handle->HandleAttributes; - handleItem->GrantedAccess = (ACCESS_MASK)Handle->GrantedAccess; - PhPrintPointer(handleItem->GrantedAccessString, UlongToPtr(handleItem->GrantedAccess)); - } - - PhEmCallObjectOperation(EmHandleItemType, handleItem, EmObjectCreate); - - return handleItem; -} - -VOID PhpHandleItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_HANDLE_ITEM handleItem = (PPH_HANDLE_ITEM)Object; - - PhEmCallObjectOperation(EmHandleItemType, handleItem, EmObjectDelete); - - if (handleItem->TypeName) PhDereferenceObject(handleItem->TypeName); - if (handleItem->ObjectName) PhDereferenceObject(handleItem->ObjectName); - if (handleItem->BestObjectName) PhDereferenceObject(handleItem->BestObjectName); -} - -FORCEINLINE BOOLEAN PhCompareHandleItem( - _In_ PPH_HANDLE_ITEM Value1, - _In_ PPH_HANDLE_ITEM Value2 - ) -{ - return Value1->Handle == Value2->Handle; -} - -FORCEINLINE ULONG PhHashHandleItem( - _In_ PPH_HANDLE_ITEM Value - ) -{ - return HandleToUlong(Value->Handle) / 4; -} - -PPH_HANDLE_ITEM PhpLookupHandleItem( - _In_ PPH_HANDLE_PROVIDER HandleProvider, - _In_ HANDLE Handle - ) -{ - PH_HANDLE_ITEM lookupHandleItem; - PPH_HASH_ENTRY entry; - PPH_HANDLE_ITEM handleItem; - - lookupHandleItem.Handle = Handle; - entry = PhFindEntryHashSet( - HandleProvider->HandleHashSet, - HandleProvider->HandleHashSetSize, - PhHashHandleItem(&lookupHandleItem) - ); - - for (; entry; entry = entry->Next) - { - handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry); - - if (PhCompareHandleItem(&lookupHandleItem, handleItem)) - return handleItem; - } - - return NULL; -} - -PPH_HANDLE_ITEM PhReferenceHandleItem( - _In_ PPH_HANDLE_PROVIDER HandleProvider, - _In_ HANDLE Handle - ) -{ - PPH_HANDLE_ITEM handleItem; - - PhAcquireQueuedLockShared(&HandleProvider->HandleHashSetLock); - - handleItem = PhpLookupHandleItem(HandleProvider, Handle); - - if (handleItem) - PhReferenceObject(handleItem); - - PhReleaseQueuedLockShared(&HandleProvider->HandleHashSetLock); - - return handleItem; -} - -VOID PhDereferenceAllHandleItems( - _In_ PPH_HANDLE_PROVIDER HandleProvider - ) -{ - ULONG i; - PPH_HASH_ENTRY entry; - PPH_HANDLE_ITEM handleItem; - - PhAcquireQueuedLockExclusive(&HandleProvider->HandleHashSetLock); - - for (i = 0; i < HandleProvider->HandleHashSetSize; i++) - { - entry = HandleProvider->HandleHashSet[i]; - - while (entry) - { - handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry); - entry = entry->Next; - PhDereferenceObject(handleItem); - } - } - - PhReleaseQueuedLockExclusive(&HandleProvider->HandleHashSetLock); -} - -VOID PhpAddHandleItem( - _In_ PPH_HANDLE_PROVIDER HandleProvider, - _In_ _Assume_refs_(1) PPH_HANDLE_ITEM HandleItem - ) -{ - if (HandleProvider->HandleHashSetSize < HandleProvider->HandleHashSetCount + 1) - { - PhResizeHashSet( - &HandleProvider->HandleHashSet, - &HandleProvider->HandleHashSetSize, - HandleProvider->HandleHashSetSize * 2 - ); - } - - PhAddEntryHashSet( - HandleProvider->HandleHashSet, - HandleProvider->HandleHashSetSize, - &HandleItem->HashEntry, - PhHashHandleItem(HandleItem) - ); - HandleProvider->HandleHashSetCount++; -} - -VOID PhpRemoveHandleItem( - _In_ PPH_HANDLE_PROVIDER HandleProvider, - _In_ PPH_HANDLE_ITEM HandleItem - ) -{ - PhRemoveEntryHashSet(HandleProvider->HandleHashSet, HandleProvider->HandleHashSetSize, &HandleItem->HashEntry); - HandleProvider->HandleHashSetCount--; - PhDereferenceObject(HandleItem); -} - -/** - * Enumerates all handles in a process. - * - * \param ProcessId The ID of the process. - * \param ProcessHandle A handle to the process. - * \param Handles A variable which receives a pointer to a buffer containing - * information about the handles. - * \param FilterNeeded A variable which receives a boolean indicating - * whether the handle information needs to be filtered by process ID. - */ -NTSTATUS PhEnumHandlesGeneric( - _In_ HANDLE ProcessId, - _In_ HANDLE ProcessHandle, - _Out_ PSYSTEM_HANDLE_INFORMATION_EX *Handles, - _Out_ PBOOLEAN FilterNeeded - ) -{ - NTSTATUS status; - - // There are three ways of enumerating handles: - // * When KProcessHacker is available, using KphEnumerateProcessHandles - // is the most efficient method. - // * On Windows XP and later, NtQuerySystemInformation with - // SystemExtendedHandleInformation can be used. - // * Otherwise, NtQuerySystemInformation with SystemHandleInformation - // can be used. - - if (KphIsConnected()) - { - PKPH_PROCESS_HANDLE_INFORMATION handles; - PSYSTEM_HANDLE_INFORMATION_EX convertedHandles; - ULONG i; - - // Enumerate handles using KProcessHacker. Unlike with NtQuerySystemInformation, - // this only enumerates handles for a single process and saves a lot of processing. - - if (NT_SUCCESS(status = KphEnumerateProcessHandles2(ProcessHandle, &handles))) - { - convertedHandles = PhAllocate( - FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + - sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * handles->HandleCount - ); - - convertedHandles->NumberOfHandles = handles->HandleCount; - - for (i = 0; i < handles->HandleCount; i++) - { - convertedHandles->Handles[i].Object = handles->Handles[i].Object; - convertedHandles->Handles[i].UniqueProcessId = (ULONG_PTR)ProcessId; - convertedHandles->Handles[i].HandleValue = (ULONG_PTR)handles->Handles[i].Handle; - convertedHandles->Handles[i].GrantedAccess = (ULONG)handles->Handles[i].GrantedAccess; - convertedHandles->Handles[i].CreatorBackTraceIndex = 0; - convertedHandles->Handles[i].ObjectTypeIndex = handles->Handles[i].ObjectTypeIndex; - convertedHandles->Handles[i].HandleAttributes = handles->Handles[i].HandleAttributes; - } - - PhFree(handles); - - *Handles = convertedHandles; - *FilterNeeded = FALSE; - - return status; - } - } - - if (WindowsVersion >= WINDOWS_XP) - { - PSYSTEM_HANDLE_INFORMATION_EX handles; - - // Enumerate handles using the new method; no conversion - // necessary. - - if (!NT_SUCCESS(status = PhEnumHandlesEx(&handles))) - return status; - - *Handles = handles; - *FilterNeeded = TRUE; - } - else - { - PSYSTEM_HANDLE_INFORMATION handles; - PSYSTEM_HANDLE_INFORMATION_EX convertedHandles; - ULONG count; - ULONG allocatedCount; - ULONG i; - - // Enumerate handles using the old info class and convert - // the relevant entries to the new format. - - if (!NT_SUCCESS(status = PhEnumHandles(&handles))) - return status; - - count = 0; - allocatedCount = 100; - - convertedHandles = PhAllocate( - FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + - sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * allocatedCount - ); - - for (i = 0; i < handles->NumberOfHandles; i++) - { - if ((HANDLE)handles->Handles[i].UniqueProcessId != ProcessId) - continue; - - if (count == allocatedCount) - { - allocatedCount *= 2; - convertedHandles = PhReAllocate( - convertedHandles, - FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + - sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * allocatedCount - ); - } - - convertedHandles->Handles[count].Object = handles->Handles[i].Object; - convertedHandles->Handles[count].UniqueProcessId = (ULONG_PTR)handles->Handles[i].UniqueProcessId; - convertedHandles->Handles[count].HandleValue = (ULONG_PTR)handles->Handles[i].HandleValue; - convertedHandles->Handles[count].GrantedAccess = handles->Handles[i].GrantedAccess; - convertedHandles->Handles[count].CreatorBackTraceIndex = handles->Handles[i].CreatorBackTraceIndex; - convertedHandles->Handles[count].ObjectTypeIndex = handles->Handles[i].ObjectTypeIndex; - convertedHandles->Handles[count].HandleAttributes = (ULONG)handles->Handles[i].HandleAttributes; - - count++; - } - - convertedHandles->NumberOfHandles = count; - - PhFree(handles); - - *Handles = convertedHandles; - *FilterNeeded = FALSE; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhpCreateHandleItemFunction( - _In_ PVOID Parameter - ) -{ - PPHP_CREATE_HANDLE_ITEM_CONTEXT context = Parameter; - PPH_HANDLE_ITEM handleItem; - - handleItem = PhCreateHandleItem(context->Handle); - - PhGetHandleInformationEx( - context->Provider->ProcessHandle, - handleItem->Handle, - context->Handle->ObjectTypeIndex, - 0, - NULL, - NULL, - &handleItem->TypeName, - &handleItem->ObjectName, - &handleItem->BestObjectName, - NULL - ); - - if (handleItem->TypeName) - { - // Add the handle item to the hashtable. - PhAcquireQueuedLockExclusive(&context->Provider->HandleHashSetLock); - PhpAddHandleItem(context->Provider, handleItem); - PhReleaseQueuedLockExclusive(&context->Provider->HandleHashSetLock); - - // Raise the handle added event. - PhInvokeCallback(&context->Provider->HandleAddedEvent, handleItem); - } - else - { - PhDereferenceObject(handleItem); - } - - PhFree(context); - - return STATUS_SUCCESS; -} - -VOID PhHandleProviderUpdate( - _In_ PVOID Object - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static ULONG fileObjectTypeIndex = -1; - - PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object; - PSYSTEM_HANDLE_INFORMATION_EX handleInfo; - BOOLEAN filterNeeded; - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handles; - ULONG numberOfHandles; - ULONG i; - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_KEY_VALUE_PAIR handlePair; - BOOLEAN useWorkQueue = FALSE; - PH_WORK_QUEUE workQueue; - - if (!handleProvider->ProcessHandle) - goto UpdateExit; - - if (!NT_SUCCESS(handleProvider->RunStatus = PhEnumHandlesGeneric( - handleProvider->ProcessId, - handleProvider->ProcessHandle, - &handleInfo, - &filterNeeded - ))) - goto UpdateExit; - - if (!KphIsConnected() && WindowsVersion >= WINDOWS_VISTA) - { - useWorkQueue = TRUE; - PhInitializeWorkQueue(&workQueue, 1, 20, 1000); - - if (PhBeginInitOnce(&initOnce)) - { - UNICODE_STRING fileTypeName; - - RtlInitUnicodeString(&fileTypeName, L"File"); - fileObjectTypeIndex = PhGetObjectTypeNumber(&fileTypeName); - PhEndInitOnce(&initOnce); - } - } - - handles = handleInfo->Handles; - numberOfHandles = (ULONG)handleInfo->NumberOfHandles; - - // Make a list of the relevant handles. - if (filterNeeded) - { - for (i = 0; i < (ULONG)numberOfHandles; i++) - { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i]; - - if (handle->UniqueProcessId == (ULONG_PTR)handleProvider->ProcessId) - { - PhAddItemSimpleHashtable( - handleProvider->TempListHashtable, - (PVOID)handle->HandleValue, - handle - ); - } - } - } - else - { - for (i = 0; i < (ULONG)numberOfHandles; i++) - { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i]; - - PhAddItemSimpleHashtable( - handleProvider->TempListHashtable, - (PVOID)handle->HandleValue, - handle - ); - } - } - - // Look for closed handles. - { - PPH_LIST handlesToRemove = NULL; - PPH_HASH_ENTRY entry; - PPH_HANDLE_ITEM handleItem; - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *tempHashtableValue; - - for (i = 0; i < handleProvider->HandleHashSetSize; i++) - { - for (entry = handleProvider->HandleHashSet[i]; entry; entry = entry->Next) - { - BOOLEAN found = FALSE; - - handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry); - - // Check if the handle still exists. - - tempHashtableValue = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *)PhFindItemSimpleHashtable( - handleProvider->TempListHashtable, - (PVOID)(handleItem->Handle) - ); - - if (tempHashtableValue) - { - // Also compare the object pointers to make sure a - // different object wasn't re-opened with the same - // handle value. This isn't 100% accurate as pool - // addresses may be re-used, but it works well. - if (handleItem->Object == (*tempHashtableValue)->Object) - { - found = TRUE; - } - } - - if (!found) - { - // Raise the handle removed event. - PhInvokeCallback(&handleProvider->HandleRemovedEvent, handleItem); - - if (!handlesToRemove) - handlesToRemove = PhCreateList(2); - - PhAddItemList(handlesToRemove, handleItem); - } - } - } - - if (handlesToRemove) - { - PhAcquireQueuedLockExclusive(&handleProvider->HandleHashSetLock); - - for (i = 0; i < handlesToRemove->Count; i++) - { - PhpRemoveHandleItem( - handleProvider, - (PPH_HANDLE_ITEM)handlesToRemove->Items[i] - ); - } - - PhReleaseQueuedLockExclusive(&handleProvider->HandleHashSetLock); - PhDereferenceObject(handlesToRemove); - } - } - - // Look for new handles and update existing ones. - - PhBeginEnumHashtable(handleProvider->TempListHashtable, &enumContext); - - while (handlePair = PhNextEnumHashtable(&enumContext)) - { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = handlePair->Value; - PPH_HANDLE_ITEM handleItem; - - handleItem = PhpLookupHandleItem(handleProvider, (HANDLE)handle->HandleValue); - - if (!handleItem) - { - // When we don't have KPH, query handle information in parallel to take full advantage of the - // PhCallWithTimeout functionality. - if (useWorkQueue && handle->ObjectTypeIndex == fileObjectTypeIndex) - { - PPHP_CREATE_HANDLE_ITEM_CONTEXT context; - - context = PhAllocate(sizeof(PHP_CREATE_HANDLE_ITEM_CONTEXT)); - context->Provider = handleProvider; - context->Handle = handle; - PhQueueItemWorkQueue(&workQueue, PhpCreateHandleItemFunction, context); - continue; - } - - handleItem = PhCreateHandleItem(handle); - - PhGetHandleInformationEx( - handleProvider->ProcessHandle, - handleItem->Handle, - handle->ObjectTypeIndex, - 0, - NULL, - NULL, - &handleItem->TypeName, - &handleItem->ObjectName, - &handleItem->BestObjectName, - NULL - ); - - // We need at least a type name to continue. - if (!handleItem->TypeName) - { - PhDereferenceObject(handleItem); - continue; - } - - if (PhEqualString2(handleItem->TypeName, L"File", TRUE) && KphIsConnected()) - { - KPH_FILE_OBJECT_INFORMATION objectInfo; - - if (NT_SUCCESS(KphQueryInformationObject( - handleProvider->ProcessHandle, - handleItem->Handle, - KphObjectFileObjectInformation, - &objectInfo, - sizeof(KPH_FILE_OBJECT_INFORMATION), - NULL - ))) - { - if (objectInfo.SharedRead) - handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_READ; - if (objectInfo.SharedWrite) - handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_WRITE; - if (objectInfo.SharedDelete) - handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_DELETE; - } - } - - // Add the handle item to the hashtable. - PhAcquireQueuedLockExclusive(&handleProvider->HandleHashSetLock); - PhpAddHandleItem(handleProvider, handleItem); - PhReleaseQueuedLockExclusive(&handleProvider->HandleHashSetLock); - - // Raise the handle added event. - PhInvokeCallback(&handleProvider->HandleAddedEvent, handleItem); - } - else - { - BOOLEAN modified = FALSE; - - if (handleItem->Attributes != handle->HandleAttributes) - { - handleItem->Attributes = handle->HandleAttributes; - modified = TRUE; - } - - if (modified) - { - // Raise the handle modified event. - PhInvokeCallback(&handleProvider->HandleModifiedEvent, handleItem); - } - } - } - - if (useWorkQueue) - { - PhWaitForWorkQueue(&workQueue); - PhDeleteWorkQueue(&workQueue); - } - - PhFree(handleInfo); - - // Re-create the temporary hashtable if it got too big. - if (handleProvider->TempListHashtable->AllocatedEntries > 8192) - { - PhDereferenceObject(handleProvider->TempListHashtable); - handleProvider->TempListHashtable = PhCreateSimpleHashtable(512); - } - else - { - PhClearHashtable(handleProvider->TempListHashtable); - } - -UpdateExit: - PhInvokeCallback(&handleProvider->UpdatedEvent, NULL); -} +/* + * Process Hacker - + * handle provider + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include + +typedef struct _PHP_CREATE_HANDLE_ITEM_CONTEXT +{ + PPH_HANDLE_PROVIDER Provider; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle; +} PHP_CREATE_HANDLE_ITEM_CONTEXT, *PPHP_CREATE_HANDLE_ITEM_CONTEXT; + +VOID NTAPI PhpHandleProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID NTAPI PhpHandleItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +PPH_OBJECT_TYPE PhHandleProviderType = NULL; +PPH_OBJECT_TYPE PhHandleItemType = NULL; + +PPH_HANDLE_PROVIDER PhCreateHandleProvider( + _In_ HANDLE ProcessId + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_HANDLE_PROVIDER handleProvider; + + if (PhBeginInitOnce(&initOnce)) + { + PhHandleProviderType = PhCreateObjectType(L"HandleProvider", 0, PhpHandleProviderDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + handleProvider = PhCreateObject( + PhEmGetObjectSize(EmHandleProviderType, sizeof(PH_HANDLE_PROVIDER)), + PhHandleProviderType + ); + memset(handleProvider, 0, sizeof(PH_HANDLE_PROVIDER)); + + handleProvider->HandleHashSetSize = 128; + handleProvider->HandleHashSet = PhCreateHashSet(handleProvider->HandleHashSetSize); + handleProvider->HandleHashSetCount = 0; + PhInitializeQueuedLock(&handleProvider->HandleHashSetLock); + + PhInitializeCallback(&handleProvider->HandleAddedEvent); + PhInitializeCallback(&handleProvider->HandleModifiedEvent); + PhInitializeCallback(&handleProvider->HandleRemovedEvent); + PhInitializeCallback(&handleProvider->UpdatedEvent); + + handleProvider->ProcessId = ProcessId; + handleProvider->ProcessHandle = NULL; + + handleProvider->RunStatus = PhOpenProcess( + &handleProvider->ProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, + ProcessId + ); + + handleProvider->TempListHashtable = PhCreateSimpleHashtable(512); + + PhEmCallObjectOperation(EmHandleProviderType, handleProvider, EmObjectCreate); + + return handleProvider; +} + +VOID PhpHandleProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object; + + PhEmCallObjectOperation(EmHandleProviderType, handleProvider, EmObjectDelete); + + // Dereference all handle items (we referenced them + // when we added them to the hashtable). + PhDereferenceAllHandleItems(handleProvider); + + PhFree(handleProvider->HandleHashSet); + PhDeleteCallback(&handleProvider->HandleAddedEvent); + PhDeleteCallback(&handleProvider->HandleModifiedEvent); + PhDeleteCallback(&handleProvider->HandleRemovedEvent); + + if (handleProvider->ProcessHandle) NtClose(handleProvider->ProcessHandle); + + PhDereferenceObject(handleProvider->TempListHashtable); +} + +PPH_HANDLE_ITEM PhCreateHandleItem( + _In_opt_ PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_HANDLE_ITEM handleItem; + + if (PhBeginInitOnce(&initOnce)) + { + PhHandleItemType = PhCreateObjectType(L"HandleItem", 0, PhpHandleItemDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + handleItem = PhCreateObject( + PhEmGetObjectSize(EmHandleItemType, sizeof(PH_HANDLE_ITEM)), + PhHandleItemType + ); + memset(handleItem, 0, sizeof(PH_HANDLE_ITEM)); + + if (Handle) + { + handleItem->Handle = (HANDLE)Handle->HandleValue; + handleItem->Object = Handle->Object; + handleItem->Attributes = Handle->HandleAttributes; + handleItem->GrantedAccess = (ACCESS_MASK)Handle->GrantedAccess; + handleItem->TypeIndex = Handle->ObjectTypeIndex; + } + + PhEmCallObjectOperation(EmHandleItemType, handleItem, EmObjectCreate); + + return handleItem; +} + +VOID PhpHandleItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_HANDLE_ITEM handleItem = (PPH_HANDLE_ITEM)Object; + + PhEmCallObjectOperation(EmHandleItemType, handleItem, EmObjectDelete); + + if (handleItem->TypeName) PhDereferenceObject(handleItem->TypeName); + if (handleItem->ObjectName) PhDereferenceObject(handleItem->ObjectName); + if (handleItem->BestObjectName) PhDereferenceObject(handleItem->BestObjectName); +} + +FORCEINLINE BOOLEAN PhCompareHandleItem( + _In_ PPH_HANDLE_ITEM Value1, + _In_ PPH_HANDLE_ITEM Value2 + ) +{ + return Value1->Handle == Value2->Handle; +} + +FORCEINLINE ULONG PhHashHandleItem( + _In_ PPH_HANDLE_ITEM Value + ) +{ + return HandleToUlong(Value->Handle) / 4; +} + +PPH_HANDLE_ITEM PhpLookupHandleItem( + _In_ PPH_HANDLE_PROVIDER HandleProvider, + _In_ HANDLE Handle + ) +{ + PH_HANDLE_ITEM lookupHandleItem; + PPH_HASH_ENTRY entry; + PPH_HANDLE_ITEM handleItem; + + lookupHandleItem.Handle = Handle; + entry = PhFindEntryHashSet( + HandleProvider->HandleHashSet, + HandleProvider->HandleHashSetSize, + PhHashHandleItem(&lookupHandleItem) + ); + + for (; entry; entry = entry->Next) + { + handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry); + + if (PhCompareHandleItem(&lookupHandleItem, handleItem)) + return handleItem; + } + + return NULL; +} + +PPH_HANDLE_ITEM PhReferenceHandleItem( + _In_ PPH_HANDLE_PROVIDER HandleProvider, + _In_ HANDLE Handle + ) +{ + PPH_HANDLE_ITEM handleItem; + + PhAcquireQueuedLockShared(&HandleProvider->HandleHashSetLock); + + handleItem = PhpLookupHandleItem(HandleProvider, Handle); + + if (handleItem) + PhReferenceObject(handleItem); + + PhReleaseQueuedLockShared(&HandleProvider->HandleHashSetLock); + + return handleItem; +} + +VOID PhDereferenceAllHandleItems( + _In_ PPH_HANDLE_PROVIDER HandleProvider + ) +{ + ULONG i; + PPH_HASH_ENTRY entry; + PPH_HANDLE_ITEM handleItem; + + PhAcquireQueuedLockExclusive(&HandleProvider->HandleHashSetLock); + + for (i = 0; i < HandleProvider->HandleHashSetSize; i++) + { + entry = HandleProvider->HandleHashSet[i]; + + while (entry) + { + handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry); + entry = entry->Next; + PhDereferenceObject(handleItem); + } + } + + PhReleaseQueuedLockExclusive(&HandleProvider->HandleHashSetLock); +} + +VOID PhpAddHandleItem( + _In_ PPH_HANDLE_PROVIDER HandleProvider, + _In_ _Assume_refs_(1) PPH_HANDLE_ITEM HandleItem + ) +{ + if (HandleProvider->HandleHashSetSize < HandleProvider->HandleHashSetCount + 1) + { + PhResizeHashSet( + &HandleProvider->HandleHashSet, + &HandleProvider->HandleHashSetSize, + HandleProvider->HandleHashSetSize * 2 + ); + } + + PhAddEntryHashSet( + HandleProvider->HandleHashSet, + HandleProvider->HandleHashSetSize, + &HandleItem->HashEntry, + PhHashHandleItem(HandleItem) + ); + HandleProvider->HandleHashSetCount++; +} + +VOID PhpRemoveHandleItem( + _In_ PPH_HANDLE_PROVIDER HandleProvider, + _In_ PPH_HANDLE_ITEM HandleItem + ) +{ + PhRemoveEntryHashSet(HandleProvider->HandleHashSet, HandleProvider->HandleHashSetSize, &HandleItem->HashEntry); + HandleProvider->HandleHashSetCount--; + PhDereferenceObject(HandleItem); +} + +/** + * Enumerates all handles in a process. + * + * \param ProcessId The ID of the process. + * \param ProcessHandle A handle to the process. + * \param Handles A variable which receives a pointer to a buffer containing + * information about the handles. + * \param FilterNeeded A variable which receives a boolean indicating + * whether the handle information needs to be filtered by process ID. + */ +NTSTATUS PhEnumHandlesGeneric( + _In_ HANDLE ProcessId, + _In_ HANDLE ProcessHandle, + _Out_ PSYSTEM_HANDLE_INFORMATION_EX *Handles, + _Out_ PBOOLEAN FilterNeeded + ) +{ + NTSTATUS status; + + // There are three ways of enumerating handles: + // * On Windows 8 and later, NtQueryInformationProcess with ProcessHandleInformation is the most efficient method. + // * On Windows XP and later, NtQuerySystemInformation with SystemExtendedHandleInformation. + // * Otherwise, NtQuerySystemInformation with SystemHandleInformation can be used. + + if (KphIsConnected()) + { + PKPH_PROCESS_HANDLE_INFORMATION handles; + PSYSTEM_HANDLE_INFORMATION_EX convertedHandles; + ULONG i; + + // Enumerate handles using KProcessHacker. Unlike with NtQuerySystemInformation, + // this only enumerates handles for a single process and saves a lot of processing. + + if (!NT_SUCCESS(status = KphEnumerateProcessHandles2(ProcessHandle, &handles))) + goto FAILED; + + convertedHandles = PhAllocate( + FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + + sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * handles->HandleCount + ); + + convertedHandles->NumberOfHandles = handles->HandleCount; + + for (i = 0; i < handles->HandleCount; i++) + { + convertedHandles->Handles[i].Object = handles->Handles[i].Object; + convertedHandles->Handles[i].UniqueProcessId = (ULONG_PTR)ProcessId; + convertedHandles->Handles[i].HandleValue = (ULONG_PTR)handles->Handles[i].Handle; + convertedHandles->Handles[i].GrantedAccess = (ULONG)handles->Handles[i].GrantedAccess; + convertedHandles->Handles[i].CreatorBackTraceIndex = 0; + convertedHandles->Handles[i].ObjectTypeIndex = handles->Handles[i].ObjectTypeIndex; + convertedHandles->Handles[i].HandleAttributes = handles->Handles[i].HandleAttributes; + } + + PhFree(handles); + + *Handles = convertedHandles; + *FilterNeeded = FALSE; + } + else if (WindowsVersion >= WINDOWS_8 && PhGetIntegerSetting(L"EnableHandleSnapshot")) + { + PPROCESS_HANDLE_SNAPSHOT_INFORMATION handles; + PSYSTEM_HANDLE_INFORMATION_EX convertedHandles; + ULONG i; + + if (!NT_SUCCESS(status = PhEnumHandlesEx2(ProcessHandle, &handles))) + goto FAILED; + + convertedHandles = PhAllocate( + FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) + + sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * handles->NumberOfHandles + ); + + convertedHandles->NumberOfHandles = handles->NumberOfHandles; + + for (i = 0; i < handles->NumberOfHandles; i++) + { + convertedHandles->Handles[i].Object = 0; + convertedHandles->Handles[i].UniqueProcessId = (ULONG_PTR)ProcessId; + convertedHandles->Handles[i].HandleValue = (ULONG_PTR)handles->Handles[i].HandleValue; + convertedHandles->Handles[i].GrantedAccess = handles->Handles[i].GrantedAccess; + convertedHandles->Handles[i].CreatorBackTraceIndex = 0; + convertedHandles->Handles[i].ObjectTypeIndex = (USHORT)handles->Handles[i].ObjectTypeIndex; + convertedHandles->Handles[i].HandleAttributes = handles->Handles[i].HandleAttributes; + } + + PhFree(handles); + + *Handles = convertedHandles; + *FilterNeeded = FALSE; + } + else + { + PSYSTEM_HANDLE_INFORMATION_EX handles; +FAILED: + if (!NT_SUCCESS(status = PhEnumHandlesEx(&handles))) + return status; + + *Handles = handles; + *FilterNeeded = TRUE; + } + + return status; +} + +NTSTATUS PhpCreateHandleItemFunction( + _In_ PVOID Parameter + ) +{ + PPHP_CREATE_HANDLE_ITEM_CONTEXT context = Parameter; + PPH_HANDLE_ITEM handleItem; + + handleItem = PhCreateHandleItem(context->Handle); + + PhGetHandleInformationEx( + context->Provider->ProcessHandle, + handleItem->Handle, + context->Handle->ObjectTypeIndex, + 0, + NULL, + NULL, + &handleItem->TypeName, + &handleItem->ObjectName, + &handleItem->BestObjectName, + NULL + ); + + if (handleItem->TypeName) + { + // Add the handle item to the hashtable. + PhAcquireQueuedLockExclusive(&context->Provider->HandleHashSetLock); + PhpAddHandleItem(context->Provider, handleItem); + PhReleaseQueuedLockExclusive(&context->Provider->HandleHashSetLock); + + // Raise the handle added event. + PhInvokeCallback(&context->Provider->HandleAddedEvent, handleItem); + } + else + { + PhDereferenceObject(handleItem); + } + + PhFree(context); + + return STATUS_SUCCESS; +} + +VOID PhHandleProviderUpdate( + _In_ PVOID Object + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static ULONG fileObjectTypeIndex = ULONG_MAX; + + PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object; + PSYSTEM_HANDLE_INFORMATION_EX handleInfo; + BOOLEAN filterNeeded; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handles; + ULONG numberOfHandles; + ULONG i; + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_KEY_VALUE_PAIR handlePair; + BOOLEAN useWorkQueue = FALSE; + PH_WORK_QUEUE workQueue; + + if (!handleProvider->ProcessHandle) + goto UpdateExit; + + if (!NT_SUCCESS(handleProvider->RunStatus = PhEnumHandlesGeneric( + handleProvider->ProcessId, + handleProvider->ProcessHandle, + &handleInfo, + &filterNeeded + ))) + goto UpdateExit; + + if (!KphIsConnected()) + { + useWorkQueue = TRUE; + PhInitializeWorkQueue(&workQueue, 1, 20, 1000); + + if (PhBeginInitOnce(&initOnce)) + { + UNICODE_STRING fileTypeName = RTL_CONSTANT_STRING(L"File"); + + fileObjectTypeIndex = PhGetObjectTypeNumber(&fileTypeName); + + PhEndInitOnce(&initOnce); + } + } + + handles = handleInfo->Handles; + numberOfHandles = (ULONG)handleInfo->NumberOfHandles; + + // Make a list of the relevant handles. + if (filterNeeded) + { + for (i = 0; i < numberOfHandles; i++) + { + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i]; + + if (handle->UniqueProcessId == (ULONG_PTR)handleProvider->ProcessId) + { + PhAddItemSimpleHashtable( + handleProvider->TempListHashtable, + (PVOID)handle->HandleValue, + handle + ); + } + } + } + else + { + for (i = 0; i < numberOfHandles; i++) + { + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i]; + + PhAddItemSimpleHashtable( + handleProvider->TempListHashtable, + (PVOID)handle->HandleValue, + handle + ); + } + } + + // Look for closed handles. + { + PPH_LIST handlesToRemove = NULL; + PPH_HASH_ENTRY entry; + PPH_HANDLE_ITEM handleItem; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *tempHashtableValue; + + for (i = 0; i < handleProvider->HandleHashSetSize; i++) + { + for (entry = handleProvider->HandleHashSet[i]; entry; entry = entry->Next) + { + BOOLEAN found = FALSE; + + handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry); + + // Check if the handle still exists. + + tempHashtableValue = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *)PhFindItemSimpleHashtable( + handleProvider->TempListHashtable, + (PVOID)(handleItem->Handle) + ); + + if (tempHashtableValue) + { + // Also compare the object pointers to make sure a + // different object wasn't re-opened with the same + // handle value. This isn't 100% accurate as pool + // addresses may be re-used, but it works well. + if (handleItem->Object && handleItem->Object == (*tempHashtableValue)->Object) + { + found = TRUE; + } + else + { + if ( + handleItem->Handle == (HANDLE)(*tempHashtableValue)->HandleValue && + handleItem->GrantedAccess == (*tempHashtableValue)->GrantedAccess && + handleItem->TypeIndex == (*tempHashtableValue)->ObjectTypeIndex + ) + { + found = TRUE; + } + } + } + + if (!found) + { + // Raise the handle removed event. + PhInvokeCallback(&handleProvider->HandleRemovedEvent, handleItem); + + if (!handlesToRemove) + handlesToRemove = PhCreateList(2); + + PhAddItemList(handlesToRemove, handleItem); + } + } + } + + if (handlesToRemove) + { + PhAcquireQueuedLockExclusive(&handleProvider->HandleHashSetLock); + + for (i = 0; i < handlesToRemove->Count; i++) + { + PhpRemoveHandleItem( + handleProvider, + (PPH_HANDLE_ITEM)handlesToRemove->Items[i] + ); + } + + PhReleaseQueuedLockExclusive(&handleProvider->HandleHashSetLock); + PhDereferenceObject(handlesToRemove); + } + } + + // Look for new handles and update existing ones. + + PhBeginEnumHashtable(handleProvider->TempListHashtable, &enumContext); + + while (handlePair = PhNextEnumHashtable(&enumContext)) + { + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = handlePair->Value; + PPH_HANDLE_ITEM handleItem; + + handleItem = PhpLookupHandleItem(handleProvider, (HANDLE)handle->HandleValue); + + if (!handleItem) + { + // When we don't have KPH, query handle information in parallel to take full advantage of the + // PhCallWithTimeout functionality. + if (useWorkQueue && handle->ObjectTypeIndex == fileObjectTypeIndex) + { + PPHP_CREATE_HANDLE_ITEM_CONTEXT context; + + context = PhAllocate(sizeof(PHP_CREATE_HANDLE_ITEM_CONTEXT)); + context->Provider = handleProvider; + context->Handle = handle; + PhQueueItemWorkQueue(&workQueue, PhpCreateHandleItemFunction, context); + continue; + } + + handleItem = PhCreateHandleItem(handle); + + PhGetHandleInformationEx( + handleProvider->ProcessHandle, + handleItem->Handle, + handle->ObjectTypeIndex, + 0, + NULL, + NULL, + &handleItem->TypeName, + &handleItem->ObjectName, + &handleItem->BestObjectName, + NULL + ); + + // HACK: Some security products block NtQueryObject with ObjectTypeInformation and return an invalid type + // so we need to lookup the TypeName using the TypeIndex. We should improve PhGetHandleInformationEx for this case + // but for now we'll preserve backwards compat by doing the lookup here. (dmex) + if (PhIsNullOrEmptyString(handleItem->TypeName)) + { + PPH_STRING typeName; + + if (typeName = PhGetObjectTypeName(handleItem->TypeIndex)) + { + PhMoveReference(&handleItem->TypeName, typeName); + } + } + + if (handleItem->TypeName && PhEqualString2(handleItem->TypeName, L"File", TRUE) && KphIsConnected()) + { + KPH_FILE_OBJECT_INFORMATION objectInfo; + + if (NT_SUCCESS(KphQueryInformationObject( + handleProvider->ProcessHandle, + handleItem->Handle, + KphObjectFileObjectInformation, + &objectInfo, + sizeof(KPH_FILE_OBJECT_INFORMATION), + NULL + ))) + { + if (objectInfo.SharedRead) + handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_READ; + if (objectInfo.SharedWrite) + handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_WRITE; + if (objectInfo.SharedDelete) + handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_DELETE; + } + } + + // Add the handle item to the hashtable. + PhAcquireQueuedLockExclusive(&handleProvider->HandleHashSetLock); + PhpAddHandleItem(handleProvider, handleItem); + PhReleaseQueuedLockExclusive(&handleProvider->HandleHashSetLock); + + // Raise the handle added event. + PhInvokeCallback(&handleProvider->HandleAddedEvent, handleItem); + } + else + { + BOOLEAN modified = FALSE; + + if (handleItem->Attributes != handle->HandleAttributes) + { + handleItem->Attributes = handle->HandleAttributes; + modified = TRUE; + } + + if (modified) + { + // Raise the handle modified event. + PhInvokeCallback(&handleProvider->HandleModifiedEvent, handleItem); + } + } + } + + if (useWorkQueue) + { + PhWaitForWorkQueue(&workQueue); + PhDeleteWorkQueue(&workQueue); + } + + PhFree(handleInfo); + + // Re-create the temporary hashtable if it got too big. + if (handleProvider->TempListHashtable->AllocatedEntries > 8192) + { + PhDereferenceObject(handleProvider->TempListHashtable); + handleProvider->TempListHashtable = PhCreateSimpleHashtable(512); + } + else + { + PhClearHashtable(handleProvider->TempListHashtable); + } + +UpdateExit: + PhInvokeCallback(&handleProvider->UpdatedEvent, NULL); +} diff --git a/ProcessHacker/hndlstat.c b/ProcessHacker/hndlstat.c index 810948439965..ce783cfb42d3 100644 --- a/ProcessHacker/hndlstat.c +++ b/ProcessHacker/hndlstat.c @@ -1,240 +1,239 @@ -/* - * Process Hacker - - * handle statistics window - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -typedef struct _HANDLE_STATISTICS_ENTRY -{ - PPH_STRING Name; - ULONG Count; -} HANDLE_STATISTICS_ENTRY, *PHANDLE_STATISTICS_ENTRY; - -typedef struct _HANDLE_STATISTICS_CONTEXT -{ - HANDLE ProcessId; - HANDLE ProcessHandle; - - PSYSTEM_HANDLE_INFORMATION_EX Handles; - HANDLE_STATISTICS_ENTRY Entries[MAX_OBJECT_TYPE_NUMBER]; -} HANDLE_STATISTICS_CONTEXT, *PHANDLE_STATISTICS_CONTEXT; - -INT_PTR CALLBACK PhpHandleStatisticsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowHandleStatisticsDialog( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId - ) -{ - NTSTATUS status; - HANDLE_STATISTICS_CONTEXT context; - BOOLEAN filterNeeded; - ULONG i; - - context.ProcessId = ProcessId; - - if (!NT_SUCCESS(status = PhOpenProcess( - &context.ProcessHandle, - PROCESS_DUP_HANDLE, - ProcessId - ))) - { - PhShowStatus(ParentWindowHandle, L"Unable to open the process", status, 0); - return; - } - - status = PhEnumHandlesGeneric( - context.ProcessId, - context.ProcessHandle, - &context.Handles, - &filterNeeded - ); - - if (!NT_SUCCESS(status)) - { - NtClose(context.ProcessHandle); - PhShowStatus(ParentWindowHandle, L"Unable to enumerate process handles", status, 0); - return; - } - - memset(&context.Entries, 0, sizeof(context.Entries)); - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_HANDLESTATS), - ParentWindowHandle, - PhpHandleStatisticsDlgProc, - (LPARAM)&context - ); - - for (i = 0; i < MAX_OBJECT_TYPE_NUMBER; i++) - { - if (context.Entries[i].Name) - PhDereferenceObject(context.Entries[i].Name); - } - - PhFree(context.Handles); - NtClose(context.ProcessHandle); -} - -static INT NTAPI PhpTypeCountCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PHANDLE_STATISTICS_ENTRY entry1 = Item1; - PHANDLE_STATISTICS_ENTRY entry2 = Item2; - - return uintcmp(entry1->Count, entry2->Count); -} - -INT_PTR CALLBACK PhpHandleStatisticsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PHANDLE_STATISTICS_CONTEXT context = (PHANDLE_STATISTICS_CONTEXT)lParam; - HANDLE processId; - ULONG_PTR i; - HWND lvHandle; - - processId = context->ProcessId; - - for (i = 0; i < context->Handles->NumberOfHandles; i++) - { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo; - PHANDLE_STATISTICS_ENTRY entry; - PPH_STRING typeName; - - handleInfo = &context->Handles->Handles[i]; - - if (handleInfo->UniqueProcessId != (ULONG_PTR)processId) - continue; - if (handleInfo->ObjectTypeIndex >= MAX_OBJECT_TYPE_NUMBER) - continue; - - entry = &context->Entries[handleInfo->ObjectTypeIndex]; - - if (!entry->Name) - { - typeName = NULL; - PhGetHandleInformation( - context->ProcessHandle, - (HANDLE)handleInfo->HandleValue, - handleInfo->ObjectTypeIndex, - NULL, - &typeName, - NULL, - NULL - ); - entry->Name = typeName; - } - - entry->Count++; - } - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Type"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Count"); - - PhSetExtendedListView(lvHandle); - ExtendedListView_SetCompareFunction(lvHandle, 1, PhpTypeCountCompareFunction); - - for (i = 0; i < MAX_OBJECT_TYPE_NUMBER; i++) - { - PHANDLE_STATISTICS_ENTRY entry; - PPH_STRING unknownType; - PPH_STRING countString; - INT lvItemIndex; - - entry = &context->Entries[i]; - - if (entry->Count == 0) - continue; - - unknownType = NULL; - - if (!entry->Name) - unknownType = PhFormatString(L"(unknown: %u)", i); - - countString = PhFormatUInt64(entry->Count, TRUE); - - lvItemIndex = PhAddListViewItem( - lvHandle, - MAXINT, - entry->Name ? entry->Name->Buffer : unknownType->Buffer, - entry - ); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, countString->Buffer); - - PhDereferenceObject(countString); - - if (unknownType) - PhDereferenceObject(unknownType); - } - - ExtendedListView_SortItems(lvHandle); - } - break; - case WM_DESTROY: - { - // Nothing - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - case WM_NOTIFY: - { - PhHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * handle statistics window + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +typedef struct _HANDLE_STATISTICS_ENTRY +{ + PPH_STRING Name; + ULONG Count; +} HANDLE_STATISTICS_ENTRY, *PHANDLE_STATISTICS_ENTRY; + +typedef struct _HANDLE_STATISTICS_CONTEXT +{ + HANDLE ProcessId; + HANDLE ProcessHandle; + + PSYSTEM_HANDLE_INFORMATION_EX Handles; + HANDLE_STATISTICS_ENTRY Entries[MAX_OBJECT_TYPE_NUMBER]; +} HANDLE_STATISTICS_CONTEXT, *PHANDLE_STATISTICS_CONTEXT; + +INT_PTR CALLBACK PhpHandleStatisticsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowHandleStatisticsDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId + ) +{ + NTSTATUS status; + HANDLE_STATISTICS_CONTEXT context; + BOOLEAN filterNeeded; + ULONG i; + + memset(&context, 0, sizeof(HANDLE_STATISTICS_CONTEXT)); + context.ProcessId = ProcessId; + + if (!NT_SUCCESS(status = PhOpenProcess( + &context.ProcessHandle, + PROCESS_DUP_HANDLE, + ProcessId + ))) + { + PhShowStatus(ParentWindowHandle, L"Unable to open the process", status, 0); + return; + } + + status = PhEnumHandlesGeneric( + context.ProcessId, + context.ProcessHandle, + &context.Handles, + &filterNeeded + ); + + if (!NT_SUCCESS(status)) + { + NtClose(context.ProcessHandle); + PhShowStatus(ParentWindowHandle, L"Unable to enumerate process handles", status, 0); + return; + } + + memset(&context.Entries, 0, sizeof(context.Entries)); + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_HANDLESTATS), + ParentWindowHandle, + PhpHandleStatisticsDlgProc, + (LPARAM)&context + ); + + for (i = 0; i < MAX_OBJECT_TYPE_NUMBER; i++) + { + if (context.Entries[i].Name) + PhDereferenceObject(context.Entries[i].Name); + } + + PhFree(context.Handles); + NtClose(context.ProcessHandle); +} + +static INT NTAPI PhpTypeCountCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ + PHANDLE_STATISTICS_ENTRY entry1 = Item1; + PHANDLE_STATISTICS_ENTRY entry2 = Item2; + + return uintcmp(entry1->Count, entry2->Count); +} + +INT_PTR CALLBACK PhpHandleStatisticsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PHANDLE_STATISTICS_CONTEXT context = (PHANDLE_STATISTICS_CONTEXT)lParam; + HANDLE processId; + ULONG_PTR i; + HWND lvHandle; + + processId = context->ProcessId; + + for (i = 0; i < context->Handles->NumberOfHandles; i++) + { + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo; + PHANDLE_STATISTICS_ENTRY entry; + PPH_STRING typeName; + + handleInfo = &context->Handles->Handles[i]; + + if (handleInfo->UniqueProcessId != (ULONG_PTR)processId) + continue; + if (handleInfo->ObjectTypeIndex >= MAX_OBJECT_TYPE_NUMBER) + continue; + + entry = &context->Entries[handleInfo->ObjectTypeIndex]; + + if (!entry->Name) + { + typeName = NULL; + PhGetHandleInformation( + context->ProcessHandle, + (HANDLE)handleInfo->HandleValue, + handleInfo->ObjectTypeIndex, + NULL, + &typeName, + NULL, + NULL + ); + entry->Name = typeName; + } + + entry->Count++; + } + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Type"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Count"); + + PhSetExtendedListView(lvHandle); + ExtendedListView_SetCompareFunction(lvHandle, 1, PhpTypeCountCompareFunction); + + for (i = 0; i < MAX_OBJECT_TYPE_NUMBER; i++) + { + PHANDLE_STATISTICS_ENTRY entry; + PPH_STRING unknownType; + PPH_STRING countString; + INT lvItemIndex; + + entry = &context->Entries[i]; + + if (entry->Count == 0) + continue; + + unknownType = NULL; + + if (!entry->Name) + unknownType = PhFormatString(L"(unknown: %lu)", i); + + countString = PhFormatUInt64(entry->Count, TRUE); + + lvItemIndex = PhAddListViewItem( + lvHandle, + MAXINT, + entry->Name ? entry->Name->Buffer : unknownType->Buffer, + entry + ); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, countString->Buffer); + + PhDereferenceObject(countString); + + if (unknownType) + PhDereferenceObject(unknownType); + } + + ExtendedListView_SortItems(lvHandle); + } + break; + case WM_DESTROY: + { + // Nothing + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/include/actions.h b/ProcessHacker/include/actions.h index dd484c00333d..a14ea1a52387 100644 --- a/ProcessHacker/include/actions.h +++ b/ProcessHacker/include/actions.h @@ -1,6 +1,7 @@ #ifndef PH_ACTIONS_H #define PH_ACTIONS_H +#define PH_KPH_ERROR_TITLE (L"KProcessHacker could not be loaded.") #define PH_KPH_ERROR_MESSAGE (L"KProcessHacker does not support your operating system " \ L"or could not be loaded. Make sure Process Hacker is running " \ L"with administrative privileges.") @@ -180,6 +181,14 @@ PhUiSetVirtualizationProcess( _In_ BOOLEAN Enable ); +PHAPPAPI +BOOLEAN +NTAPI +PhUiSetCriticalProcess( + _In_ HWND WindowHandle, + _In_ PPH_PROCESS_ITEM Process + ); + PHAPPAPI BOOLEAN NTAPI @@ -191,7 +200,7 @@ PhUiDetachFromDebuggerProcess( PHAPPAPI BOOLEAN NTAPI -PhUiInjectDllProcess( +PhUiLoadDllProcess( _In_ HWND hWnd, _In_ PPH_PROCESS_ITEM Process ); diff --git a/ProcessHacker/include/appsup.h b/ProcessHacker/include/appsup.h index e631485c30b9..d5d3b2a38dcc 100644 --- a/ProcessHacker/include/appsup.h +++ b/ProcessHacker/include/appsup.h @@ -6,9 +6,7 @@ extern GUID VISTA_CONTEXT_GUID; extern GUID WIN7_CONTEXT_GUID; extern GUID WIN8_CONTEXT_GUID; extern GUID WINBLUE_CONTEXT_GUID; -extern GUID WINTHRESHOLD_CONTEXT_GUID; - -typedef struct PACKAGE_ID PACKAGE_ID; +extern GUID WIN10_CONTEXT_GUID; // begin_phapppub PHAPPAPI @@ -17,6 +15,13 @@ NTAPI PhGetProcessIsSuspended( _In_ PSYSTEM_PROCESS_INFORMATION Process ); + +PHAPPAPI +BOOLEAN +NTAPI +PhIsProcessSuspended( + _In_ HANDLE ProcessId + ); // end_phapppub NTSTATUS PhGetProcessSwitchContext( @@ -24,18 +29,6 @@ NTSTATUS PhGetProcessSwitchContext( _Out_ PGUID Guid ); -PPH_STRING PhGetProcessPackageFullName( - _In_ HANDLE ProcessHandle - ); - -PACKAGE_ID *PhPackageIdFromFullName( - _In_ PWSTR PackageFullName - ); - -PPH_STRING PhGetPackagePath( - _In_ PACKAGE_ID *PackageId - ); - // begin_phapppub typedef enum _PH_KNOWN_PROCESS_TYPE { @@ -54,6 +47,8 @@ typedef enum _PH_KNOWN_PROCESS_TYPE TaskHostProcessType, // taskeng, taskhost, taskhostex ExplorerProcessType, // explorer UmdfHostProcessType, // wudfhost + EdgeProcessType, // Microsoft Edge + WmiProviderHostType, MaximumProcessType, KnownProcessTypeMask = 0xffff, @@ -68,6 +63,14 @@ PhGetProcessKnownType( _Out_ PH_KNOWN_PROCESS_TYPE *KnownProcessType ); +PHAPPAPI +PH_KNOWN_PROCESS_TYPE +NTAPI +PhGetProcessKnownTypeEx( + _In_ HANDLE ProcessId, + _In_ PPH_STRING FileName + ); + typedef union _PH_KNOWN_PROCESS_COMMAND_LINE { struct @@ -97,18 +100,6 @@ PhaGetProcessKnownCommandLine( ); // end_phapppub -VOID PhEnumChildWindows( - _In_opt_ HWND hWnd, - _In_ ULONG Limit, - _In_ WNDENUMPROC Callback, - _In_ LPARAM lParam - ); - -HWND PhGetProcessMainWindow( - _In_ HANDLE ProcessId, - _In_opt_ HANDLE ProcessHandle - ); - PPH_STRING PhGetServiceRelevantFileName( _In_ PPH_STRINGREF ServiceName, _In_ SC_HANDLE ServiceHandle @@ -124,12 +115,6 @@ PPH_STRING PhUnescapeStringForDelimiter( _In_ WCHAR Delimiter ); -typedef struct mxml_node_s mxml_node_t; - -PPH_STRING PhGetOpaqueXmlNodeText( - _In_ mxml_node_t *node - ); - // begin_phapppub PHAPPAPI VOID @@ -156,13 +141,7 @@ NTAPI PhLoadSymbolProviderOptions( _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider ); -// end_phapppub -PWSTR PhMakeContextAtom( - VOID - ); - -// begin_phapppub PHAPPAPI VOID NTAPI @@ -185,19 +164,20 @@ PhHandleListViewNotifyForCopy( _In_ LPARAM lParam, _In_ HWND ListViewHandle ); -// end_phapppub #define PH_LIST_VIEW_CTRL_C_BEHAVIOR 0x1 #define PH_LIST_VIEW_CTRL_A_BEHAVIOR 0x2 #define PH_LIST_VIEW_DEFAULT_1_BEHAVIORS (PH_LIST_VIEW_CTRL_C_BEHAVIOR | PH_LIST_VIEW_CTRL_A_BEHAVIOR) -VOID PhHandleListViewNotifyBehaviors( +PHAPPAPI +VOID +NTAPI +PhHandleListViewNotifyBehaviors( _In_ LPARAM lParam, _In_ HWND ListViewHandle, _In_ ULONG Behaviors ); -// begin_phapppub PHAPPAPI BOOLEAN NTAPI @@ -207,11 +187,6 @@ PhGetListViewContextMenuPoint( ); // end_phapppub -HFONT PhDuplicateFontWithNewWeight( - _In_ HFONT Font, - _In_ LONG NewWeight - ); - VOID PhSetWindowOpacity( _In_ HWND WindowHandle, _In_ ULONG OpacityPercent @@ -221,40 +196,6 @@ VOID PhSetWindowOpacity( #define PH_ID_TO_OPACITY(Id) (100 - (((Id) - ID_OPACITY_10) + 1) * 10) // begin_phapppub -PHAPPAPI -VOID -NTAPI -PhLoadWindowPlacementFromSetting( - _In_opt_ PWSTR PositionSettingName, - _In_opt_ PWSTR SizeSettingName, - _In_ HWND WindowHandle - ); - -PHAPPAPI -VOID -NTAPI -PhSaveWindowPlacementToSetting( - _In_opt_ PWSTR PositionSettingName, - _In_opt_ PWSTR SizeSettingName, - _In_ HWND WindowHandle - ); - -PHAPPAPI -VOID -NTAPI -PhLoadListViewColumnsFromSetting( - _In_ PWSTR Name, - _In_ HWND ListViewHandle - ); - -PHAPPAPI -VOID -NTAPI -PhSaveListViewColumnsToSetting( - _In_ PWSTR Name, - _In_ HWND ListViewHandle - ); - PHAPPAPI PPH_STRING NTAPI @@ -268,7 +209,7 @@ NTAPI PhGetPhVersionNumbers( _Out_opt_ PULONG MajorVersion, _Out_opt_ PULONG MinorVersion, - _Reserved_ PULONG Reserved, + _Out_opt_ PULONG BuildNumber, _Out_opt_ PULONG RevisionNumber ); @@ -427,7 +368,6 @@ NTAPI PhApplyTreeNewFilters( _In_ PPH_TN_FILTER_SUPPORT Support ); -// end_phapppub typedef struct _PH_COPY_CELL_CONTEXT { @@ -436,17 +376,49 @@ typedef struct _PH_COPY_CELL_CONTEXT PPH_STRING MenuItemText; } PH_COPY_CELL_CONTEXT, *PPH_COPY_CELL_CONTEXT; -BOOLEAN PhInsertCopyCellEMenuItem( +PHAPPAPI +BOOLEAN +NTAPI +PhInsertCopyCellEMenuItem( _In_ struct _PH_EMENU_ITEM *Menu, _In_ ULONG InsertAfterId, _In_ HWND TreeNewHandle, _In_ PPH_TREENEW_COLUMN Column ); -BOOLEAN PhHandleCopyCellEMenuItem( +PHAPPAPI +BOOLEAN +NTAPI +PhHandleCopyCellEMenuItem( + _In_ struct _PH_EMENU_ITEM *SelectedItem + ); + +typedef struct _PH_COPY_ITEM_CONTEXT +{ + HWND ListViewHandle; + ULONG Id; + ULONG SubId; + PPH_STRING MenuItemText; +} PH_COPY_ITEM_CONTEXT, *PPH_COPY_ITEM_CONTEXT; + +PHAPPAPI +BOOLEAN +NTAPI +PhInsertCopyListViewEMenuItem( + _In_ struct _PH_EMENU_ITEM *Menu, + _In_ ULONG InsertAfterId, + _In_ HWND ListViewHandle + ); + +PHAPPAPI +BOOLEAN +NTAPI +PhHandleCopyListViewEMenuItem( _In_ struct _PH_EMENU_ITEM *SelectedItem ); +// end_phapppub + BOOLEAN PhShellOpenKey2( _In_ HWND hWnd, _In_ PPH_STRING KeyName @@ -456,15 +428,19 @@ PPH_STRING PhPcre2GetErrorMessage( _In_ INT ErrorCode ); -#define PH_LOAD_SHARED_ICON_SMALL(Name) PhLoadIcon(PhInstanceHandle, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) -#define PH_LOAD_SHARED_ICON_LARGE(Name) PhLoadIcon(PhInstanceHandle, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) +HBITMAP PhGetShieldBitmap( + VOID + ); + +#define PH_LOAD_SHARED_ICON_SMALL(BaseAddress, Name) PhLoadIcon(BaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_SMALL, 0, 0) // phapppub +#define PH_LOAD_SHARED_ICON_LARGE(BaseAddress, Name) PhLoadIcon(BaseAddress, (Name), PH_LOAD_ICON_SHARED | PH_LOAD_ICON_SIZE_LARGE, 0, 0) // phapppub FORCEINLINE PVOID PhpGenericPropertyPageHeader( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam, - _In_ PWSTR ContextName + _In_ ULONG ContextHash ) { PVOID context; @@ -476,18 +452,18 @@ FORCEINLINE PVOID PhpGenericPropertyPageHeader( LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; context = (PVOID)propSheetPage->lParam; - SetProp(hwndDlg, ContextName, (HANDLE)context); + PhSetWindowContext(hwndDlg, ContextHash, context); } break; case WM_DESTROY: { - context = (PVOID)GetProp(hwndDlg, ContextName); - RemoveProp(hwndDlg, ContextName); + context = PhGetWindowContext(hwndDlg, ContextHash); + PhRemoveWindowContext(hwndDlg, ContextHash); } break; default: { - context = (PVOID)GetProp(hwndDlg, ContextName); + context = PhGetWindowContext(hwndDlg, ContextHash); } break; } diff --git a/ProcessHacker/include/colmgr.h b/ProcessHacker/include/colmgr.h index 92ae8658f634..6d5489889856 100644 --- a/ProcessHacker/include/colmgr.h +++ b/ProcessHacker/include/colmgr.h @@ -1,118 +1,118 @@ -#ifndef PH_COLMGR_H -#define PH_COLMGR_H - -#define PH_CM_ORDER_LIMIT 160 - -// begin_phapppub -typedef LONG (NTAPI *PPH_CM_POST_SORT_FUNCTION)( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); -// end_phapppub - -typedef struct _PH_CM_MANAGER -{ - HWND Handle; - ULONG MinId; - ULONG NextId; - PPH_CM_POST_SORT_FUNCTION PostSortFunction; - LIST_ENTRY ColumnListHead; - PPH_LIST NotifyList; -} PH_CM_MANAGER, *PPH_CM_MANAGER; - -typedef struct _PH_CM_COLUMN -{ - LIST_ENTRY ListEntry; - ULONG Id; - struct _PH_PLUGIN *Plugin; - ULONG SubId; - PVOID Context; - PVOID SortFunction; -} PH_CM_COLUMN, *PPH_CM_COLUMN; - -VOID PhCmInitializeManager( - _Out_ PPH_CM_MANAGER Manager, - _In_ HWND Handle, - _In_ ULONG MinId, - _In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction - ); - -VOID PhCmDeleteManager( - _In_ PPH_CM_MANAGER Manager - ); - -PPH_CM_COLUMN PhCmCreateColumn( - _Inout_ PPH_CM_MANAGER Manager, - _In_ PPH_TREENEW_COLUMN Column, - _In_ struct _PH_PLUGIN *Plugin, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_ PVOID SortFunction - ); - -PPH_CM_COLUMN PhCmFindColumn( - _In_ PPH_CM_MANAGER Manager, - _In_ PPH_STRINGREF PluginName, - _In_ ULONG SubId - ); - -VOID PhCmSetNotifyPlugin( - _In_ PPH_CM_MANAGER Manager, - _In_ struct _PH_PLUGIN *Plugin - ); - -BOOLEAN PhCmForwardMessage( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_ PPH_CM_MANAGER Manager - ); - -BOOLEAN PhCmForwardSort( - _In_ PPH_TREENEW_NODE *Nodes, - _In_ ULONG NumberOfNodes, - _In_ ULONG SortColumn, - _In_ PH_SORT_ORDER SortOrder, - _In_ PPH_CM_MANAGER Manager - ); - -// begin_phapppub -PHAPPAPI -BOOLEAN -NTAPI -PhCmLoadSettings( - _In_ HWND TreeNewHandle, - _In_ PPH_STRINGREF Settings - ); -// end_phapppub - -#define PH_CM_COLUMN_WIDTHS_ONLY 0x1 - -BOOLEAN PhCmLoadSettingsEx( - _In_ HWND TreeNewHandle, - _In_opt_ PPH_CM_MANAGER Manager, - _In_ ULONG Flags, - _In_ PPH_STRINGREF Settings, - _In_opt_ PPH_STRINGREF SortSettings - ); - -// begin_phapppub -PHAPPAPI -PPH_STRING -NTAPI -PhCmSaveSettings( - _In_ HWND TreeNewHandle - ); -// end_phapppub - -PPH_STRING PhCmSaveSettingsEx( - _In_ HWND TreeNewHandle, - _In_opt_ PPH_CM_MANAGER Manager, - _In_ ULONG Flags, - _Out_opt_ PPH_STRING *SortSettings - ); - -#endif +#ifndef PH_COLMGR_H +#define PH_COLMGR_H + +#define PH_CM_ORDER_LIMIT 160 + +// begin_phapppub +typedef LONG (NTAPI *PPH_CM_POST_SORT_FUNCTION)( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); +// end_phapppub + +typedef struct _PH_CM_MANAGER +{ + HWND Handle; + ULONG MinId; + ULONG NextId; + PPH_CM_POST_SORT_FUNCTION PostSortFunction; + LIST_ENTRY ColumnListHead; + PPH_LIST NotifyList; +} PH_CM_MANAGER, *PPH_CM_MANAGER; + +typedef struct _PH_CM_COLUMN +{ + LIST_ENTRY ListEntry; + ULONG Id; + struct _PH_PLUGIN *Plugin; + ULONG SubId; + PVOID Context; + PVOID SortFunction; +} PH_CM_COLUMN, *PPH_CM_COLUMN; + +VOID PhCmInitializeManager( + _Out_ PPH_CM_MANAGER Manager, + _In_ HWND Handle, + _In_ ULONG MinId, + _In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction + ); + +VOID PhCmDeleteManager( + _In_ PPH_CM_MANAGER Manager + ); + +PPH_CM_COLUMN PhCmCreateColumn( + _Inout_ PPH_CM_MANAGER Manager, + _In_ PPH_TREENEW_COLUMN Column, + _In_ struct _PH_PLUGIN *Plugin, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_ PVOID SortFunction + ); + +PPH_CM_COLUMN PhCmFindColumn( + _In_ PPH_CM_MANAGER Manager, + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ); + +VOID PhCmSetNotifyPlugin( + _In_ PPH_CM_MANAGER Manager, + _In_ struct _PH_PLUGIN *Plugin + ); + +BOOLEAN PhCmForwardMessage( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_ PPH_CM_MANAGER Manager + ); + +BOOLEAN PhCmForwardSort( + _In_ PPH_TREENEW_NODE *Nodes, + _In_ ULONG NumberOfNodes, + _In_ ULONG SortColumn, + _In_ PH_SORT_ORDER SortOrder, + _In_ PPH_CM_MANAGER Manager + ); + +// begin_phapppub +PHAPPAPI +BOOLEAN +NTAPI +PhCmLoadSettings( + _In_ HWND TreeNewHandle, + _In_ PPH_STRINGREF Settings + ); +// end_phapppub + +#define PH_CM_COLUMN_WIDTHS_ONLY 0x1 + +BOOLEAN PhCmLoadSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _In_ PPH_STRINGREF Settings, + _In_opt_ PPH_STRINGREF SortSettings + ); + +// begin_phapppub +PHAPPAPI +PPH_STRING +NTAPI +PhCmSaveSettings( + _In_ HWND TreeNewHandle + ); +// end_phapppub + +PPH_STRING PhCmSaveSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _Out_opt_ PPH_STRING *SortSettings + ); + +#endif diff --git a/ProcessHacker/include/colsetmgr.h b/ProcessHacker/include/colsetmgr.h new file mode 100644 index 000000000000..4b39d58f2342 --- /dev/null +++ b/ProcessHacker/include/colsetmgr.h @@ -0,0 +1,40 @@ +#ifndef PH_COLSETMGR_H +#define PH_COLSETMGR_H + +typedef struct _PH_COLUMN_SET_ENTRY +{ + PPH_STRING Name; + PPH_STRING Setting; + PPH_STRING Sorting; +} PH_COLUMN_SET_ENTRY, *PPH_COLUMN_SET_ENTRY; + +PPH_LIST PhInitializeColumnSetList( + _In_ PWSTR SettingName + ); + +VOID PhDeleteColumnSetList( + _In_ PPH_LIST ColumnSetList + ); + +BOOLEAN PhLoadSettingsColumnSet( + _In_ PWSTR SettingName, + _In_ PPH_STRING ColumnSetName, + _Out_ PPH_STRING *TreeListSettings, + _Out_ PPH_STRING *TreeSortSettings + ); + +VOID PhSaveSettingsColumnSet( + _In_ PWSTR SettingName, + _In_ PPH_STRING ColumnSetName, + _In_ PPH_STRING TreeListSettings, + _In_ PPH_STRING TreeSortSettings + ); + +// Column Set Editor Dialog + +VOID PhShowColumnSetEditorDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR SettingName + ); + +#endif diff --git a/ProcessHacker/include/extmgr.h b/ProcessHacker/include/extmgr.h index 9e45dc00c35a..321e78d3cbd8 100644 --- a/ProcessHacker/include/extmgr.h +++ b/ProcessHacker/include/extmgr.h @@ -1,51 +1,51 @@ -#ifndef PH_EXTMGR_H -#define PH_EXTMGR_H - -// begin_phapppub -typedef enum _PH_EM_OBJECT_TYPE -{ - EmProcessItemType, - EmProcessNodeType, - EmServiceItemType, - EmServiceNodeType, - EmNetworkItemType, - EmNetworkNodeType, - EmThreadItemType, - EmThreadNodeType, - EmModuleItemType, - EmModuleNodeType, - EmHandleItemType, - EmHandleNodeType, - EmThreadsContextType, - EmModulesContextType, - EmHandlesContextType, - EmThreadProviderType, - EmModuleProviderType, - EmHandleProviderType, - EmMemoryNodeType, - EmMemoryContextType, - EmMaximumObjectType -} PH_EM_OBJECT_TYPE; - -typedef enum _PH_EM_OBJECT_OPERATION -{ - EmObjectCreate, - EmObjectDelete, - EmMaximumObjectOperation -} PH_EM_OBJECT_OPERATION; - -typedef VOID (NTAPI *PPH_EM_OBJECT_CALLBACK)( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ); -// end_phapppub - -typedef struct _PH_EM_APP_CONTEXT -{ - LIST_ENTRY ListEntry; - PH_STRINGREF AppName; - struct _PH_EM_OBJECT_EXTENSION *Extensions[EmMaximumObjectType]; -} PH_EM_APP_CONTEXT, *PPH_EM_APP_CONTEXT; - -#endif +#ifndef PH_EXTMGR_H +#define PH_EXTMGR_H + +// begin_phapppub +typedef enum _PH_EM_OBJECT_TYPE +{ + EmProcessItemType, + EmProcessNodeType, + EmServiceItemType, + EmServiceNodeType, + EmNetworkItemType, + EmNetworkNodeType, + EmThreadItemType, + EmThreadNodeType, + EmModuleItemType, + EmModuleNodeType, + EmHandleItemType, + EmHandleNodeType, + EmThreadsContextType, + EmModulesContextType, + EmHandlesContextType, + EmThreadProviderType, + EmModuleProviderType, + EmHandleProviderType, + EmMemoryNodeType, + EmMemoryContextType, + EmMaximumObjectType +} PH_EM_OBJECT_TYPE; + +typedef enum _PH_EM_OBJECT_OPERATION +{ + EmObjectCreate, + EmObjectDelete, + EmMaximumObjectOperation +} PH_EM_OBJECT_OPERATION; + +typedef VOID (NTAPI *PPH_EM_OBJECT_CALLBACK)( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ); +// end_phapppub + +typedef struct _PH_EM_APP_CONTEXT +{ + LIST_ENTRY ListEntry; + PH_STRINGREF AppName; + struct _PH_EM_OBJECT_EXTENSION *Extensions[EmMaximumObjectType]; +} PH_EM_APP_CONTEXT, *PPH_EM_APP_CONTEXT; + +#endif diff --git a/ProcessHacker/include/extmgri.h b/ProcessHacker/include/extmgri.h index dcbf701b574c..932a8e57f9fa 100644 --- a/ProcessHacker/include/extmgri.h +++ b/ProcessHacker/include/extmgri.h @@ -1,61 +1,61 @@ -#ifndef PH_EXTMGRI_H -#define PH_EXTMGRI_H - -#include - -typedef struct _PH_EM_OBJECT_TYPE_STATE -{ - SIZE_T InitialSize; - SIZE_T ExtensionOffset; - LIST_ENTRY ExtensionListHead; -} PH_EM_OBJECT_TYPE_STATE, *PPH_EM_OBJECT_TYPE_STATE; - -typedef struct _PH_EM_OBJECT_EXTENSION -{ - LIST_ENTRY ListEntry; - SIZE_T ExtensionSize; - SIZE_T ExtensionOffset; - PPH_EM_OBJECT_CALLBACK Callbacks[EmMaximumObjectOperation]; -} PH_EM_OBJECT_EXTENSION, *PPH_EM_OBJECT_EXTENSION; - -VOID PhEmInitialization( - VOID - ); - -VOID PhEmInitializeAppContext( - _Out_ PPH_EM_APP_CONTEXT AppContext, - _In_ PPH_STRINGREF AppName - ); - -VOID PhEmSetObjectExtension( - _Inout_ PPH_EM_APP_CONTEXT AppContext, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ SIZE_T ExtensionSize, - _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, - _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback - ); - -PVOID PhEmGetObjectExtension( - _In_ PPH_EM_APP_CONTEXT AppContext, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Object - ); - -SIZE_T PhEmGetObjectSize( - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ SIZE_T InitialSize - ); - -VOID PhEmCallObjectOperation( - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Object, - _In_ PH_EM_OBJECT_OPERATION Operation - ); - -BOOLEAN PhEmParseCompoundId( - _In_ PPH_STRINGREF CompoundId, - _Out_ PPH_STRINGREF AppName, - _Out_ PULONG SubId - ); - -#endif +#ifndef PH_EXTMGRI_H +#define PH_EXTMGRI_H + +#include + +typedef struct _PH_EM_OBJECT_TYPE_STATE +{ + SIZE_T InitialSize; + SIZE_T ExtensionOffset; + LIST_ENTRY ExtensionListHead; +} PH_EM_OBJECT_TYPE_STATE, *PPH_EM_OBJECT_TYPE_STATE; + +typedef struct _PH_EM_OBJECT_EXTENSION +{ + LIST_ENTRY ListEntry; + SIZE_T ExtensionSize; + SIZE_T ExtensionOffset; + PPH_EM_OBJECT_CALLBACK Callbacks[EmMaximumObjectOperation]; +} PH_EM_OBJECT_EXTENSION, *PPH_EM_OBJECT_EXTENSION; + +VOID PhEmInitialization( + VOID + ); + +VOID PhEmInitializeAppContext( + _Out_ PPH_EM_APP_CONTEXT AppContext, + _In_ PPH_STRINGREF AppName + ); + +VOID PhEmSetObjectExtension( + _Inout_ PPH_EM_APP_CONTEXT AppContext, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ SIZE_T ExtensionSize, + _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, + _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback + ); + +PVOID PhEmGetObjectExtension( + _In_ PPH_EM_APP_CONTEXT AppContext, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Object + ); + +SIZE_T PhEmGetObjectSize( + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ SIZE_T InitialSize + ); + +VOID PhEmCallObjectOperation( + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Object, + _In_ PH_EM_OBJECT_OPERATION Operation + ); + +BOOLEAN PhEmParseCompoundId( + _In_ PPH_STRINGREF CompoundId, + _Out_ PPH_STRINGREF AppName, + _Out_ PULONG SubId + ); + +#endif diff --git a/ProcessHacker/include/heapstruct.h b/ProcessHacker/include/heapstruct.h index 5ace0d6c9c75..c85b1fb30a42 100644 --- a/ProcessHacker/include/heapstruct.h +++ b/ProcessHacker/include/heapstruct.h @@ -1,69 +1,69 @@ -#ifndef PH_HEAPSTRUCT_H -#define PH_HEAPSTRUCT_H - -// Not the actual structure, but has the same size. -typedef struct _HEAP_ENTRY -{ - PVOID Data1; - PVOID Data2; -} HEAP_ENTRY, *PHEAP_ENTRY; - -#define HEAP_SEGMENT_SIGNATURE 0xffeeffee - -// First few fields of HEAP_SEGMENT, VISTA and above -typedef struct _HEAP_SEGMENT -{ - HEAP_ENTRY HeapEntry; - ULONG SegmentSignature; - ULONG SegmentFlags; - LIST_ENTRY SegmentListEntry; - struct _HEAP *Heap; - - // ... -} HEAP_SEGMENT, *PHEAP_SEGMENT; - -// First few fields of HEAP_SEGMENT, WS03 and below -typedef struct _HEAP_SEGMENT_OLD -{ - HEAP_ENTRY Entry; - ULONG Signature; - ULONG Flags; - struct _HEAP *Heap; - - // ... -} HEAP_SEGMENT_OLD, *PHEAP_SEGMENT_OLD; - -// 32-bit versions - -typedef struct _HEAP_ENTRY32 -{ - WOW64_POINTER(PVOID) Data1; - WOW64_POINTER(PVOID) Data2; -} HEAP_ENTRY32, *PHEAP_ENTRY32; - -typedef struct _HEAP_SEGMENT32 -{ - HEAP_ENTRY32 HeapEntry; - ULONG SegmentSignature; - ULONG SegmentFlags; - LIST_ENTRY32 SegmentListEntry; - WOW64_POINTER(struct _HEAP *) Heap; - - // ... -} HEAP_SEGMENT32, *PHEAP_SEGMENT32; - -typedef struct _HEAP_SEGMENT_OLD32 -{ - HEAP_ENTRY32 Entry; - ULONG Signature; - ULONG Flags; - WOW64_POINTER(struct _HEAP *) Heap; - - // ... -} HEAP_SEGMENT_OLD32, *PHEAP_SEGMENT_OLD32; - -#define HEAP_SEGMENT_MAX_SIZE \ - (max(sizeof(HEAP_SEGMENT), max(sizeof(HEAP_SEGMENT_OLD), \ - max(sizeof(HEAP_SEGMENT32), sizeof(HEAP_SEGMENT_OLD32))))) - -#endif +#ifndef PH_HEAPSTRUCT_H +#define PH_HEAPSTRUCT_H + +// Not the actual structure, but has the same size. +typedef struct _HEAP_ENTRY +{ + PVOID Data1; + PVOID Data2; +} HEAP_ENTRY, *PHEAP_ENTRY; + +#define HEAP_SEGMENT_SIGNATURE 0xffeeffee + +// First few fields of HEAP_SEGMENT, VISTA and above +typedef struct _HEAP_SEGMENT +{ + HEAP_ENTRY HeapEntry; + ULONG SegmentSignature; + ULONG SegmentFlags; + LIST_ENTRY SegmentListEntry; + struct _HEAP *Heap; + + // ... +} HEAP_SEGMENT, *PHEAP_SEGMENT; + +// First few fields of HEAP_SEGMENT, WS03 and below +typedef struct _HEAP_SEGMENT_OLD +{ + HEAP_ENTRY Entry; + ULONG Signature; + ULONG Flags; + struct _HEAP *Heap; + + // ... +} HEAP_SEGMENT_OLD, *PHEAP_SEGMENT_OLD; + +// 32-bit versions + +typedef struct _HEAP_ENTRY32 +{ + WOW64_POINTER(PVOID) Data1; + WOW64_POINTER(PVOID) Data2; +} HEAP_ENTRY32, *PHEAP_ENTRY32; + +typedef struct _HEAP_SEGMENT32 +{ + HEAP_ENTRY32 HeapEntry; + ULONG SegmentSignature; + ULONG SegmentFlags; + LIST_ENTRY32 SegmentListEntry; + WOW64_POINTER(struct _HEAP *) Heap; + + // ... +} HEAP_SEGMENT32, *PHEAP_SEGMENT32; + +typedef struct _HEAP_SEGMENT_OLD32 +{ + HEAP_ENTRY32 Entry; + ULONG Signature; + ULONG Flags; + WOW64_POINTER(struct _HEAP *) Heap; + + // ... +} HEAP_SEGMENT_OLD32, *PHEAP_SEGMENT_OLD32; + +#define HEAP_SEGMENT_MAX_SIZE \ + (max(sizeof(HEAP_SEGMENT), max(sizeof(HEAP_SEGMENT_OLD), \ + max(sizeof(HEAP_SEGMENT32), sizeof(HEAP_SEGMENT_OLD32))))) + +#endif diff --git a/ProcessHacker/include/hidnproc.h b/ProcessHacker/include/hidnproc.h index 916efc0b9d46..4cf157f28ab8 100644 --- a/ProcessHacker/include/hidnproc.h +++ b/ProcessHacker/include/hidnproc.h @@ -1,75 +1,76 @@ -#ifndef PH_HIDNPROC_H -#define PH_HIDNPROC_H - -typedef enum _PH_HIDDEN_PROCESS_METHOD -{ - BruteForceScanMethod, - CsrHandlesScanMethod -} PH_HIDDEN_PROCESS_METHOD; - -typedef enum _PH_HIDDEN_PROCESS_TYPE -{ - UnknownProcess, - NormalProcess, - HiddenProcess, - TerminatedProcess -} PH_HIDDEN_PROCESS_TYPE; - -typedef struct _PH_HIDDEN_PROCESS_ENTRY -{ - HANDLE ProcessId; - PPH_STRING FileName; - PH_HIDDEN_PROCESS_TYPE Type; -} PH_HIDDEN_PROCESS_ENTRY, *PPH_HIDDEN_PROCESS_ENTRY; - -typedef struct _PH_CSR_HANDLE_INFO -{ - HANDLE CsrProcessHandle; - HANDLE Handle; - BOOLEAN IsThreadHandle; - - HANDLE ProcessId; -} PH_CSR_HANDLE_INFO, *PPH_CSR_HANDLE_INFO; - -typedef BOOLEAN (NTAPI *PPH_ENUM_HIDDEN_PROCESSES_CALLBACK)( - _In_ PPH_HIDDEN_PROCESS_ENTRY Process, - _In_opt_ PVOID Context - ); - -NTSTATUS -NTAPI -PhEnumHiddenProcesses( - _In_ PH_HIDDEN_PROCESS_METHOD Method, - _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, - _In_opt_ PVOID Context - ); - -typedef BOOLEAN (NTAPI *PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK)( - _In_ PPH_CSR_HANDLE_INFO Handle, - _In_opt_ PVOID Context - ); - -NTSTATUS -NTAPI -PhEnumCsrProcessHandles( - _In_ PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK Callback, - _In_opt_ PVOID Context - ); - -NTSTATUS -NTAPI -PhOpenProcessByCsrHandle( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PPH_CSR_HANDLE_INFO Handle - ); - -NTSTATUS -NTAPI -PhOpenProcessByCsrHandles( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ HANDLE ProcessId - ); - -#endif +#ifndef PH_HIDNPROC_H +#define PH_HIDNPROC_H + +typedef enum _PH_HIDDEN_PROCESS_METHOD +{ + BruteForceScanMethod, + CsrHandlesScanMethod, + ProcessHandleScanMethod +} PH_HIDDEN_PROCESS_METHOD; + +typedef enum _PH_HIDDEN_PROCESS_TYPE +{ + UnknownProcess, + NormalProcess, + HiddenProcess, + TerminatedProcess +} PH_HIDDEN_PROCESS_TYPE; + +typedef struct _PH_HIDDEN_PROCESS_ENTRY +{ + HANDLE ProcessId; + PPH_STRING FileName; + PH_HIDDEN_PROCESS_TYPE Type; +} PH_HIDDEN_PROCESS_ENTRY, *PPH_HIDDEN_PROCESS_ENTRY; + +typedef struct _PH_CSR_HANDLE_INFO +{ + HANDLE CsrProcessHandle; + HANDLE Handle; + BOOLEAN IsThreadHandle; + + HANDLE ProcessId; +} PH_CSR_HANDLE_INFO, *PPH_CSR_HANDLE_INFO; + +typedef BOOLEAN (NTAPI *PPH_ENUM_HIDDEN_PROCESSES_CALLBACK)( + _In_ PPH_HIDDEN_PROCESS_ENTRY Process, + _In_opt_ PVOID Context + ); + +NTSTATUS +NTAPI +PhEnumHiddenProcesses( + _In_ PH_HIDDEN_PROCESS_METHOD Method, + _In_ PPH_ENUM_HIDDEN_PROCESSES_CALLBACK Callback, + _In_opt_ PVOID Context + ); + +typedef BOOLEAN (NTAPI *PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK)( + _In_ PPH_CSR_HANDLE_INFO Handle, + _In_opt_ PVOID Context + ); + +NTSTATUS +NTAPI +PhEnumCsrProcessHandles( + _In_ PPH_ENUM_CSR_PROCESS_HANDLES_CALLBACK Callback, + _In_opt_ PVOID Context + ); + +NTSTATUS +NTAPI +PhOpenProcessByCsrHandle( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PPH_CSR_HANDLE_INFO Handle + ); + +NTSTATUS +NTAPI +PhOpenProcessByCsrHandles( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ProcessId + ); + +#endif diff --git a/ProcessHacker/include/hndllist.h b/ProcessHacker/include/hndllist.h index 579b06ced3a2..d6dcbaa7233a 100644 --- a/ProcessHacker/include/hndllist.h +++ b/ProcessHacker/include/hndllist.h @@ -19,6 +19,16 @@ #define PHHNTLC_MAXIMUM 9 +// begin_phapppub +typedef enum _PH_HANDLE_TREE_MENUITEM +{ + PH_HANDLE_TREE_MENUITEM_NONE, + PH_HANDLE_TREE_MENUITEM_HIDEUNNAMEDHANDLES, + PH_HANDLE_TREE_MENUITEM_HIDEETWHANDLES, + PH_HANDLE_TREE_MENUITEM_MAXIMUM +} PH_HANDLE_TREE_MENUITEM; +// end_phapppub + // begin_phapppub typedef struct _PH_HANDLE_NODE { @@ -34,6 +44,7 @@ typedef struct _PH_HANDLE_NODE PPH_STRING GrantedAccessSymbolicText; WCHAR FileShareAccessText[4]; + WCHAR ObjectString[PH_PTR_STR_LEN_1]; // begin_phapppub } PH_HANDLE_NODE, *PPH_HANDLE_NODE; // end_phapppub @@ -43,14 +54,25 @@ typedef struct _PH_HANDLE_LIST_CONTEXT HWND ParentWindowHandle; HWND TreeNewHandle; ULONG TreeNewSortColumn; + + PH_TN_FILTER_SUPPORT TreeFilterSupport; PH_SORT_ORDER TreeNewSortOrder; PH_CM_MANAGER Cm; - BOOLEAN HideUnnamedHandles; + + union + { + ULONG Flags; + struct + { + ULONG EnableStateHighlighting : 1; + ULONG HideUnnamedHandles : 1; + ULONG HideEtwHandles : 1; + ULONG Spare : 22; + }; + }; PPH_HASHTABLE NodeHashtable; PPH_LIST NodeList; - - BOOLEAN EnableStateHighlighting; PPH_POINTER_LIST NodeStateList; } PH_HANDLE_LIST_CONTEXT, *PPH_HANDLE_LIST_CONTEXT; @@ -74,7 +96,7 @@ VOID PhSaveSettingsHandleList( VOID PhSetOptionsHandleList( _Inout_ PPH_HANDLE_LIST_CONTEXT Context, - _In_ BOOLEAN HideUnnamedHandles + _In_ ULONG Options ); PPH_HANDLE_NODE PhAddHandleNode( @@ -98,6 +120,11 @@ VOID PhUpdateHandleNode( _In_ PPH_HANDLE_NODE HandleNode ); +VOID PhExpandAllHandleNodes( + _In_ PPH_HANDLE_LIST_CONTEXT Context, + _In_ BOOLEAN Expand + ); + VOID PhTickHandleNodes( _In_ PPH_HANDLE_LIST_CONTEXT Context ); diff --git a/ProcessHacker/include/hndlprv.h b/ProcessHacker/include/hndlprv.h index 7238239efc06..2edc43ef7c6e 100644 --- a/ProcessHacker/include/hndlprv.h +++ b/ProcessHacker/include/hndlprv.h @@ -18,6 +18,7 @@ typedef struct _PH_HANDLE_ITEM PVOID Object; ULONG Attributes; ACCESS_MASK GrantedAccess; + ULONG TypeIndex; ULONG FileFlags; PPH_STRING TypeName; @@ -25,7 +26,6 @@ typedef struct _PH_HANDLE_ITEM PPH_STRING BestObjectName; WCHAR HandleString[PH_PTR_STR_LEN_1]; - WCHAR ObjectString[PH_PTR_STR_LEN_1]; WCHAR GrantedAccessString[PH_PTR_STR_LEN_1]; } PH_HANDLE_ITEM, *PPH_HANDLE_ITEM; @@ -49,10 +49,6 @@ typedef struct _PH_HANDLE_PROVIDER } PH_HANDLE_PROVIDER, *PPH_HANDLE_PROVIDER; // end_phapppub -BOOLEAN PhHandleProviderInitialization( - VOID - ); - PPH_HANDLE_PROVIDER PhCreateHandleProvider( _In_ HANDLE ProcessId ); diff --git a/ProcessHacker/include/mainwnd.h b/ProcessHacker/include/mainwnd.h index d7607624acd1..c13d48d33056 100644 --- a/ProcessHacker/include/mainwnd.h +++ b/ProcessHacker/include/mainwnd.h @@ -1,8 +1,6 @@ #ifndef PH_MAINWND_H #define PH_MAINWND_H -#define PH_MAINWND_CLASSNAME L"ProcessHacker" // phapppub - PHAPPAPI extern HWND PhMainWndHandle; // phapppub extern BOOLEAN PhMainWndExiting; @@ -10,21 +8,6 @@ extern BOOLEAN PhMainWndExiting; #define WM_PH_ACTIVATE (WM_APP + 99) #define PH_ACTIVATE_REPLY 0x1119 -#define WM_PH_PROCESS_ADDED (WM_APP + 101) -#define WM_PH_PROCESS_MODIFIED (WM_APP + 102) -#define WM_PH_PROCESS_REMOVED (WM_APP + 103) -#define WM_PH_PROCESSES_UPDATED (WM_APP + 104) - -#define WM_PH_SERVICE_ADDED (WM_APP + 105) -#define WM_PH_SERVICE_MODIFIED (WM_APP + 106) -#define WM_PH_SERVICE_REMOVED (WM_APP + 107) -#define WM_PH_SERVICES_UPDATED (WM_APP + 108) - -#define WM_PH_NETWORK_ITEM_ADDED (WM_APP + 109) -#define WM_PH_NETWORK_ITEM_MODIFIED (WM_APP + 110) -#define WM_PH_NETWORK_ITEM_REMOVED (WM_APP + 111) -#define WM_PH_NETWORK_ITEMS_UPDATED (WM_APP + 112) - // begin_phapppub #define WM_PH_SHOW_PROCESS_PROPERTIES (WM_APP + 120) #define WM_PH_DESTROY (WM_APP + 121) @@ -44,12 +27,12 @@ extern BOOLEAN PhMainWndExiting; #define WM_PH_SELECT_PROCESS_NODE (WM_APP + 133) #define WM_PH_SELECT_SERVICE_ITEM (WM_APP + 134) #define WM_PH_SELECT_NETWORK_ITEM (WM_APP + 135) -// end_phapppub #define WM_PH_UPDATE_FONT (WM_APP + 136) #define WM_PH_GET_FONT (WM_APP + 137) +// end_phapppub // begin_phapppub #define WM_PH_INVOKE (WM_APP + 138) -#define WM_PH_ADD_MENU_ITEM (WM_APP + 139) +// WM_PH_DEPRECATED (WM_APP + 139) #define WM_PH_CREATE_TAB_PAGE (WM_APP + 140) #define WM_PH_REFRESH (WM_APP + 141) #define WM_PH_GET_UPDATE_AUTOMATICALLY (WM_APP + 142) @@ -89,8 +72,6 @@ extern BOOLEAN PhMainWndExiting; SendMessage(hWnd, WM_PH_SELECT_NETWORK_ITEM, 0, (LPARAM)(NetworkItem)) #define ProcessHacker_Invoke(hWnd, Function, Parameter) \ PostMessage(hWnd, WM_PH_INVOKE, (WPARAM)(Parameter), (LPARAM)(Function)) -#define ProcessHacker_AddMenuItem(hWnd, AddMenuItem) \ - ((ULONG_PTR)SendMessage(hWnd, WM_PH_ADD_MENU_ITEM, 0, (LPARAM)(AddMenuItem))) #define ProcessHacker_CreateTabPage(hWnd, Template) \ ((struct _PH_MAIN_TAB_PAGE *)SendMessage(hWnd, WM_PH_CREATE_TAB_PAGE, 0, (LPARAM)(Template))) #define ProcessHacker_Refresh(hWnd) \ @@ -105,6 +86,7 @@ extern BOOLEAN PhMainWndExiting; typedef struct _PH_SHOW_MEMORY_EDITOR { + HWND OwnerWindow; HANDLE ProcessId; PVOID BaseAddress; SIZE_T RegionSize; @@ -127,17 +109,6 @@ typedef struct _PH_LAYOUT_PADDING_DATA } PH_LAYOUT_PADDING_DATA, *PPH_LAYOUT_PADDING_DATA; // end_phapppub -typedef struct _PH_ADD_MENU_ITEM -{ - _In_ PVOID Plugin; - _In_ ULONG Location; - _In_opt_ PWSTR InsertAfter; - _In_ ULONG Flags; - _In_ ULONG Id; - _In_ PWSTR Text; - _In_opt_ PVOID Context; -} PH_ADD_MENU_ITEM, *PPH_ADD_MENU_ITEM; - // begin_phapppub typedef enum _PH_MAIN_TAB_PAGE_MESSAGE { @@ -221,10 +192,6 @@ BOOLEAN PhMainWndInitialization( _In_ INT ShowCommand ); -VOID PhLoadDbgHelpFromPath( - _In_ PWSTR DbgHelpPath - ); - VOID PhAddMiniProcessMenuItems( _Inout_ struct _PH_EMENU_ITEM *Menu, _In_ HANDLE ProcessId diff --git a/ProcessHacker/include/mainwndp.h b/ProcessHacker/include/mainwndp.h index 04e1cfd8c8f6..e7bd8f8d3833 100644 --- a/ProcessHacker/include/mainwndp.h +++ b/ProcessHacker/include/mainwndp.h @@ -1,512 +1,452 @@ -#ifndef PH_MAINWNDP_H -#define PH_MAINWNDP_H - -#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1 250 -#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_2 750 -#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM 1000 - -#define TIMER_FLUSH_PROCESS_QUERY_DATA 1 -#define TIMER_ICON_CLICK_ACTIVATE 2 -#define TIMER_ICON_RESTORE_HOVER 3 - -typedef union _PH_MWP_NOTIFICATION_DETAILS -{ - HANDLE ProcessId; - PPH_STRING ServiceName; -} PH_MWP_NOTIFICATION_DETAILS, *PPH_MWP_NOTIFICATION_DETAILS; - -extern PH_PROVIDER_REGISTRATION PhMwpProcessProviderRegistration; -extern PH_PROVIDER_REGISTRATION PhMwpServiceProviderRegistration; -extern PH_PROVIDER_REGISTRATION PhMwpNetworkProviderRegistration; -extern BOOLEAN PhMwpUpdateAutomatically; - -extern ULONG PhMwpNotifyIconNotifyMask; -extern ULONG PhMwpLastNotificationType; -extern PH_MWP_NOTIFICATION_DETAILS PhMwpLastNotificationDetails; - -LRESULT CALLBACK PhMwpWndProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// Initialization - -BOOLEAN PhMwpInitializeWindowClass( - VOID - ); - -VOID PhMwpInitializeProviders( - VOID - ); - -VOID PhMwpApplyUpdateInterval( - _In_ ULONG Interval - ); - -VOID PhMwpInitializeControls( - VOID - ); - -NTSTATUS PhMwpDelayedLoadFunction( - _In_ PVOID Parameter - ); - -PPH_STRING PhMwpFindDbghelpPath( - VOID - ); - -// Event handlers - -VOID PhMwpOnDestroy( - VOID - ); - -VOID PhMwpOnEndSession( - VOID - ); - -VOID PhMwpOnSettingChange( - VOID - ); - -VOID PhMwpOnCommand( - _In_ ULONG Id - ); - -VOID PhMwpOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ); - -BOOLEAN PhMwpOnSysCommand( - _In_ ULONG Type, - _In_ LONG CursorScreenX, - _In_ LONG CursorScreenY - ); - -VOID PhMwpOnMenuCommand( - _In_ ULONG Index, - _In_ HMENU Menu - ); - -VOID PhMwpOnInitMenuPopup( - _In_ HMENU Menu, - _In_ ULONG Index, - _In_ BOOLEAN IsWindowMenu - ); - -VOID PhMwpOnSize( - VOID - ); - -VOID PhMwpOnSizing( - _In_ ULONG Edge, - _In_ PRECT DragRectangle - ); - -VOID PhMwpOnSetFocus( - VOID - ); - -VOID PhMwpOnTimer( - _In_ ULONG Id - ); - -BOOLEAN PhMwpOnNotify( - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ); - -VOID PhMwpOnWtsSessionChange( - _In_ ULONG Reason, - _In_ ULONG SessionId - ); - -ULONG_PTR PhMwpOnUserMessage( - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ); - -// Callbacks - -VOID NTAPI PhMwpNetworkItemAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpNetworkItemModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpNetworkItemRemovedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpNetworkItemsUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -// Settings - -VOID PhMwpLoadSettings( - VOID - ); - -VOID PhMwpSaveSettings( - VOID - ); - -VOID PhMwpSaveWindowState( - VOID - ); - -// Misc. - -VOID PhMwpSymInitHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID PhMwpUpdateLayoutPadding( - VOID - ); - -VOID PhMwpApplyLayoutPadding( - _Inout_ PRECT Rect, - _In_ PRECT Padding - ); - -VOID PhMwpLayout( - _Inout_ HDWP *DeferHandle - ); - -VOID PhMwpSetupComputerMenu( - _In_ PPH_EMENU_ITEM Root - ); - -BOOLEAN PhMwpExecuteComputerCommand( - _In_ ULONG Id - ); - -VOID PhMwpActivateWindow( - _In_ BOOLEAN Toggle - ); - -// Main menu - -VOID PhMwpInitializeMainMenu( - _In_ HMENU Menu - ); - -VOID PhMwpDispatchMenuCommand( - _In_ HMENU MenuHandle, - _In_ ULONG ItemIndex, - _In_ ULONG ItemId, - _In_ ULONG_PTR ItemData - ); - -ULONG_PTR PhMwpLegacyAddPluginMenuItem( - _In_ PPH_ADD_MENU_ITEM AddMenuItem - ); - -HBITMAP PhMwpGetShieldBitmap( - VOID - ); - -VOID PhMwpInitializeSubMenu( - _In_ PPH_EMENU Menu, - _In_ ULONG Index - ); - -PPH_EMENU_ITEM PhMwpFindTrayIconsMenuItem( - _In_ PPH_EMENU Menu - ); - -VOID PhMwpInitializeSectionMenuItems( - _In_ PPH_EMENU Menu, - _In_ ULONG StartIndex - ); - -// Tab control - -VOID PhMwpLayoutTabControl( - _Inout_ HDWP *DeferHandle - ); - -VOID PhMwpNotifyTabControl( - _In_ NMHDR *Header - ); - -VOID PhMwpSelectionChangedTabControl( - _In_ ULONG OldIndex - ); - -PPH_MAIN_TAB_PAGE PhMwpCreatePage( - _In_ PPH_MAIN_TAB_PAGE Template - ); - -VOID PhMwpSelectPage( - _In_ ULONG Index - ); - -PPH_MAIN_TAB_PAGE PhMwpFindPage( - _In_ PPH_STRINGREF Name - ); - -PPH_MAIN_TAB_PAGE PhMwpCreateInternalPage( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MAIN_TAB_PAGE_CALLBACK Callback - ); - -VOID PhMwpNotifyAllPages( - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -// Notifications - -VOID PhMwpAddIconProcesses( - _In_ PPH_EMENU_ITEM Menu, - _In_ ULONG NumberOfProcesses - ); - -VOID PhMwpClearLastNotificationDetails( - VOID - ); - -BOOLEAN PhMwpPluginNotifyEvent( - _In_ ULONG Type, - _In_ PVOID Parameter - ); - -// Processes - -extern PPH_MAIN_TAB_PAGE PhMwpProcessesPage; -extern HWND PhMwpProcessTreeNewHandle; -extern HWND PhMwpSelectedProcessWindowHandle; -extern BOOLEAN PhMwpSelectedProcessVirtualizationEnabled; - -BOOLEAN PhMwpProcessesPageCallback( - _In_ struct _PH_MAIN_TAB_PAGE *Page, - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID PhMwpShowProcessProperties( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -VOID PhMwpToggleCurrentUserProcessTreeFilter( - VOID - ); - -BOOLEAN PhMwpCurrentUserProcessTreeFilter( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -VOID PhMwpToggleSignedProcessTreeFilter( - VOID - ); - -BOOLEAN PhMwpSignedProcessTreeFilter( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -BOOLEAN PhMwpExecuteProcessPriorityCommand( - _In_ ULONG Id, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ); - -BOOLEAN PhMwpExecuteProcessIoPriorityCommand( - _In_ ULONG Id, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ); - -VOID PhMwpSetProcessMenuPriorityChecks( - _In_ PPH_EMENU Menu, - _In_ HANDLE ProcessId, - _In_ BOOLEAN SetPriority, - _In_ BOOLEAN SetIoPriority, - _In_ BOOLEAN SetPagePriority - ); - -VOID PhMwpInitializeProcessMenu( - _In_ PPH_EMENU Menu, - _In_ PPH_PROCESS_ITEM *Processes, - _In_ ULONG NumberOfProcesses - ); - -VOID NTAPI PhMwpProcessAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpProcessModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpProcessRemovedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpProcessesUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID PhMwpOnProcessAdded( - _In_ _Assume_refs_(1) PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG RunId - ); - -VOID PhMwpOnProcessModified( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -VOID PhMwpOnProcessRemoved( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -VOID PhMwpOnProcessesUpdated( - VOID - ); - -// Services - -extern PPH_MAIN_TAB_PAGE PhMwpServicesPage; -extern HWND PhMwpServiceTreeNewHandle; - -BOOLEAN PhMwpServicesPageCallback( - _In_ struct _PH_MAIN_TAB_PAGE *Page, - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID PhMwpNeedServiceTreeList( - VOID - ); - -VOID PhMwpToggleDriverServiceTreeFilter( - VOID - ); - -BOOLEAN PhMwpDriverServiceTreeFilter( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -VOID PhMwpInitializeServiceMenu( - _In_ PPH_EMENU Menu, - _In_ PPH_SERVICE_ITEM *Services, - _In_ ULONG NumberOfServices - ); - -VOID NTAPI PhMwpServiceAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpServiceModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpServiceRemovedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI PhMwpServicesUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID PhMwpOnServiceAdded( - _In_ _Assume_refs_(1) PPH_SERVICE_ITEM ServiceItem, - _In_ ULONG RunId - ); - -VOID PhMwpOnServiceModified( - _In_ struct _PH_SERVICE_MODIFIED_DATA *ServiceModifiedData - ); - -VOID PhMwpOnServiceRemoved( - _In_ PPH_SERVICE_ITEM ServiceItem - ); - -VOID PhMwpOnServicesUpdated( - VOID - ); - -// Network - -extern PPH_MAIN_TAB_PAGE PhMwpNetworkPage; -extern HWND PhMwpNetworkTreeNewHandle; - -BOOLEAN PhMwpNetworkPageCallback( - _In_ struct _PH_MAIN_TAB_PAGE *Page, - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID PhMwpNeedNetworkTreeList( - VOID - ); - -BOOLEAN PhMwpCurrentUserNetworkTreeFilter( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -BOOLEAN PhMwpSignedNetworkTreeFilter( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -VOID PhMwpInitializeNetworkMenu( - _In_ PPH_EMENU Menu, - _In_ PPH_NETWORK_ITEM *NetworkItems, - _In_ ULONG NumberOfNetworkItems - ); - -VOID PhMwpOnNetworkItemAdded( - _In_ ULONG RunId, - _In_ _Assume_refs_(1) PPH_NETWORK_ITEM NetworkItem - ); - -VOID PhMwpOnNetworkItemModified( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -VOID PhMwpOnNetworkItemRemoved( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -VOID PhMwpOnNetworkItemsUpdated( - VOID - ); - -// Users - -VOID PhMwpUpdateUsersMenu( - VOID - ); - -#endif +#ifndef PH_MAINWNDP_H +#define PH_MAINWNDP_H + +#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1 250 +#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_2 750 +#define PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM 1000 + +#define TIMER_FLUSH_PROCESS_QUERY_DATA 1 +#define TIMER_ICON_CLICK_ACTIVATE 2 +#define TIMER_ICON_RESTORE_HOVER 3 + +typedef union _PH_MWP_NOTIFICATION_DETAILS +{ + HANDLE ProcessId; + PPH_STRING ServiceName; +} PH_MWP_NOTIFICATION_DETAILS, *PPH_MWP_NOTIFICATION_DETAILS; + +extern PH_PROVIDER_REGISTRATION PhMwpProcessProviderRegistration; +extern PH_PROVIDER_REGISTRATION PhMwpServiceProviderRegistration; +extern PH_PROVIDER_REGISTRATION PhMwpNetworkProviderRegistration; +extern BOOLEAN PhMwpUpdateAutomatically; + +extern ULONG PhMwpNotifyIconNotifyMask; +extern ULONG PhMwpLastNotificationType; +extern PH_MWP_NOTIFICATION_DETAILS PhMwpLastNotificationDetails; + +LRESULT CALLBACK PhMwpWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// Initialization + +RTL_ATOM PhMwpInitializeWindowClass( + VOID + ); + +VOID PhMwpInitializeProviders( + VOID + ); + +VOID PhMwpApplyUpdateInterval( + _In_ ULONG Interval + ); + +VOID PhMwpInitializeControls( + _In_ HWND WindowHandle + ); + +NTSTATUS PhMwpLoadStage1Worker( + _In_ PVOID Parameter + ); + +// Event handlers + +VOID PhMwpOnDestroy( + _In_ HWND WindowHandle + ); + +VOID PhMwpOnEndSession( + _In_ HWND WindowHandle + ); + +VOID PhMwpOnSettingChange( + VOID + ); + +VOID PhMwpOnCommand( + _In_ HWND WindowHandle, + _In_ ULONG Id + ); + +VOID PhMwpOnShowWindow( + _In_ HWND WindowHandle, + _In_ BOOLEAN Showing, + _In_ ULONG State + ); + +BOOLEAN PhMwpOnSysCommand( + _In_ HWND WindowHandle, + _In_ ULONG Type, + _In_ LONG CursorScreenX, + _In_ LONG CursorScreenY + ); + +VOID PhMwpOnMenuCommand( + _In_ HWND WindowHandle, + _In_ ULONG Index, + _In_ HMENU Menu + ); + +VOID PhMwpOnInitMenuPopup( + _In_ HWND WindowHandle, + _In_ HMENU Menu, + _In_ ULONG Index, + _In_ BOOLEAN IsWindowMenu + ); + +VOID PhMwpOnSize( + _In_ HWND WindowHandle + ); + +VOID PhMwpOnSizing( + _In_ ULONG Edge, + _In_ PRECT DragRectangle + ); + +VOID PhMwpOnSetFocus( + VOID + ); + +BOOLEAN PhMwpOnNotify( + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ); + +ULONG_PTR PhMwpOnUserMessage( + _In_ HWND WindowHandle, + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ); + +// Settings + +VOID PhMwpLoadSettings( + _In_ HWND WindowHandle + ); + +VOID PhMwpSaveSettings( + _In_ HWND WindowHandle + ); + +VOID PhMwpSaveWindowState( + _In_ HWND WindowHandle + ); + +// Misc. + +VOID PhMwpUpdateLayoutPadding( + VOID + ); + +VOID PhMwpApplyLayoutPadding( + _Inout_ PRECT Rect, + _In_ PRECT Padding + ); + +VOID PhMwpLayout( + _Inout_ HDWP *DeferHandle + ); + +VOID PhMwpSetupComputerMenu( + _In_ PPH_EMENU_ITEM Root + ); + +BOOLEAN PhMwpExecuteComputerCommand( + _In_ HWND WindowHandle, + _In_ ULONG Id + ); + +VOID PhMwpActivateWindow( + _In_ HWND WindowHandle, + _In_ BOOLEAN Toggle + ); + +// Main menu + +VOID PhMwpInitializeMainMenu( + _In_ HMENU Menu + ); + +VOID PhMwpDispatchMenuCommand( + _In_ HWND WindowHandle, + _In_ HMENU MenuHandle, + _In_ ULONG ItemIndex, + _In_ ULONG ItemId, + _In_ ULONG_PTR ItemData + ); + +VOID PhMwpInitializeSubMenu( + _In_ PPH_EMENU Menu, + _In_ ULONG Index + ); + +VOID PhMwpInitializeSectionMenuItems( + _In_ PPH_EMENU Menu, + _In_ ULONG StartIndex + ); + +// Tab control + +VOID PhMwpLayoutTabControl( + _Inout_ HDWP *DeferHandle + ); + +VOID PhMwpNotifyTabControl( + _In_ NMHDR *Header + ); + +VOID PhMwpSelectionChangedTabControl( + _In_ ULONG OldIndex + ); + +PPH_MAIN_TAB_PAGE PhMwpCreatePage( + _In_ PPH_MAIN_TAB_PAGE Template + ); + +VOID PhMwpSelectPage( + _In_ ULONG Index + ); + +PPH_MAIN_TAB_PAGE PhMwpFindPage( + _In_ PPH_STRINGREF Name + ); + +PPH_MAIN_TAB_PAGE PhMwpCreateInternalPage( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MAIN_TAB_PAGE_CALLBACK Callback + ); + +VOID PhMwpNotifyAllPages( + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +// Notifications + +VOID PhMwpAddIconProcesses( + _In_ PPH_EMENU_ITEM Menu, + _In_ ULONG NumberOfProcesses + ); + +VOID PhMwpClearLastNotificationDetails( + VOID + ); + +BOOLEAN PhMwpPluginNotifyEvent( + _In_ ULONG Type, + _In_ PVOID Parameter + ); + +// Processes + +extern PPH_MAIN_TAB_PAGE PhMwpProcessesPage; +extern HWND PhMwpProcessTreeNewHandle; +extern HWND PhMwpSelectedProcessWindowHandle; +extern BOOLEAN PhMwpSelectedProcessVirtualizationEnabled; +extern struct _PH_PROVIDER_EVENT_QUEUE PhMwpProcessEventQueue; + +BOOLEAN PhMwpProcessesPageCallback( + _In_ struct _PH_MAIN_TAB_PAGE *Page, + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID PhMwpShowProcessProperties( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +VOID PhMwpToggleCurrentUserProcessTreeFilter( + VOID + ); + +BOOLEAN PhMwpCurrentUserProcessTreeFilter( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +VOID PhMwpToggleSignedProcessTreeFilter( + VOID + ); + +BOOLEAN PhMwpSignedProcessTreeFilter( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +BOOLEAN PhMwpExecuteProcessPriorityCommand( + _In_ ULONG Id, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ); + +BOOLEAN PhMwpExecuteProcessIoPriorityCommand( + _In_ ULONG Id, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ); + +VOID PhMwpSetProcessMenuPriorityChecks( + _In_ PPH_EMENU Menu, + _In_ HANDLE ProcessId, + _In_ BOOLEAN SetPriority, + _In_ BOOLEAN SetIoPriority, + _In_ BOOLEAN SetPagePriority + ); + +VOID PhMwpInitializeProcessMenu( + _In_ PPH_EMENU Menu, + _In_ PPH_PROCESS_ITEM *Processes, + _In_ ULONG NumberOfProcesses + ); + +VOID NTAPI PhMwpProcessAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI PhMwpProcessModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI PhMwpProcessRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI PhMwpProcessesUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpOnProcessesUpdated( + _In_ ULONG RunId + ); + +// Services + +extern PPH_MAIN_TAB_PAGE PhMwpServicesPage; +extern HWND PhMwpServiceTreeNewHandle; +extern struct _PH_PROVIDER_EVENT_QUEUE PhMwpServiceEventQueue; + +BOOLEAN PhMwpServicesPageCallback( + _In_ struct _PH_MAIN_TAB_PAGE *Page, + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID PhMwpNeedServiceTreeList( + VOID + ); + +VOID PhMwpToggleDriverServiceTreeFilter( + VOID + ); + +BOOLEAN PhMwpDriverServiceTreeFilter( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +VOID PhMwpInitializeServiceMenu( + _In_ PPH_EMENU Menu, + _In_ PPH_SERVICE_ITEM *Services, + _In_ ULONG NumberOfServices + ); + +VOID NTAPI PhMwpServiceAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI PhMwpServiceModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI PhMwpServiceRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI PhMwpServicesUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpOnServicesUpdated( + _In_ ULONG RunId + ); + +// Network + +extern PPH_MAIN_TAB_PAGE PhMwpNetworkPage; +extern HWND PhMwpNetworkTreeNewHandle; +extern struct _PH_PROVIDER_EVENT_QUEUE PhMwpNetworkEventQueue; + +BOOLEAN PhMwpNetworkPageCallback( + _In_ struct _PH_MAIN_TAB_PAGE *Page, + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID PhMwpNeedNetworkTreeList( + VOID + ); + +VOID PhMwpToggleNetworkWaitingConnectionTreeFilter( + VOID + ); + +BOOLEAN PhMwpNetworkTreeFilter( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +VOID PhMwpInitializeNetworkMenu( + _In_ PPH_EMENU Menu, + _In_ PPH_NETWORK_ITEM *NetworkItems, + _In_ ULONG NumberOfNetworkItems + ); + +VOID PhMwpNetworkItemAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpNetworkItemModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpNetworkItemRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpNetworkItemsUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhMwpOnNetworkItemsUpdated( + _In_ ULONG RunId + ); + +// Users + +VOID PhMwpUpdateUsersMenu( + _In_ PPH_EMENU UsersMenu + ); + +#endif diff --git a/ProcessHacker/include/memlist.h b/ProcessHacker/include/memlist.h index c5f3dcdf2dda..dc52e9e53838 100644 --- a/ProcessHacker/include/memlist.h +++ b/ProcessHacker/include/memlist.h @@ -53,14 +53,39 @@ typedef struct _PH_MEMORY_NODE } PH_MEMORY_NODE, *PPH_MEMORY_NODE; // end_phapppub +#define PH_MEMORY_FLAGS_FREE_OPTION 1 +#define PH_MEMORY_FLAGS_RESERVED_OPTION 2 +#define PH_MEMORY_FLAGS_PRIVATE_OPTION 3 +#define PH_MEMORY_FLAGS_SYSTEM_OPTION 4 +#define PH_MEMORY_FLAGS_CFG_OPTION 5 +#define PH_MEMORY_FLAGS_EXECUTE_OPTION 6 +#define PH_MEMORY_FLAGS_GUARD_OPTION 7 + typedef struct _PH_MEMORY_LIST_CONTEXT { HWND ParentWindowHandle; HWND TreeNewHandle; ULONG TreeNewSortColumn; + PH_TN_FILTER_SUPPORT AllocationTreeFilterSupport; + PH_TN_FILTER_SUPPORT TreeFilterSupport; PH_SORT_ORDER TreeNewSortOrder; PH_CM_MANAGER Cm; - BOOLEAN HideFreeRegions; + + union + { + ULONG Flags; + struct + { + ULONG HideFreeRegions : 1; + ULONG HideReservedRegions : 1; + ULONG HighlightPrivatePages : 1; + ULONG HighlightSystemPages : 1; + ULONG HighlightCfgPages : 1; + ULONG HighlightExecutePages : 1; + ULONG HideGuardRegions : 1; + ULONG Spare : 25; + }; + }; PPH_LIST AllocationBaseNodeList; // Allocation base nodes (list should always be sorted by base address) PPH_LIST RegionNodeList; // Memory region nodes @@ -86,12 +111,18 @@ VOID PhSaveSettingsMemoryList( VOID PhSetOptionsMemoryList( _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ BOOLEAN HideFreeRegions + _In_ ULONG Options ); VOID PhReplaceMemoryList( _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ PPH_MEMORY_ITEM_LIST List + _In_opt_ PPH_MEMORY_ITEM_LIST List + ); + +VOID PhRemoveMemoryNode( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ PPH_MEMORY_NODE MemoryNode ); VOID PhUpdateMemoryNode( @@ -99,6 +130,15 @@ VOID PhUpdateMemoryNode( _In_ PPH_MEMORY_NODE MemoryNode ); +VOID PhExpandAllMemoryNodes( + _In_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ BOOLEAN Expand + ); + +PPH_STRING PhGetMemoryRegionUseText( + _In_ PPH_MEMORY_ITEM MemoryItem + ); + PPH_MEMORY_NODE PhGetSelectedMemoryNode( _In_ PPH_MEMORY_LIST_CONTEXT Context ); diff --git a/ProcessHacker/include/memprv.h b/ProcessHacker/include/memprv.h index b3d7baf86f8f..c6772298c146 100644 --- a/ProcessHacker/include/memprv.h +++ b/ProcessHacker/include/memprv.h @@ -21,7 +21,10 @@ typedef enum _PH_MEMORY_REGION_TYPE Heap32Region, HeapSegmentRegion, HeapSegment32Region, - CfgBitmapRegion + CfgBitmapRegion, + CfgBitmap32Region, + ApiSetMapRegion, + HypervisorSharedDataRegion, } PH_MEMORY_REGION_TYPE; typedef struct _PH_MEMORY_ITEM @@ -31,6 +34,7 @@ typedef struct _PH_MEMORY_ITEM union { + MEMORY_BASIC_INFORMATION BasicInfo; struct { PVOID BaseAddress; @@ -41,7 +45,17 @@ typedef struct _PH_MEMORY_ITEM ULONG Protect; ULONG Type; }; - MEMORY_BASIC_INFORMATION BasicInfo; + }; + + union + { + BOOLEAN Attributes; + struct + { + BOOLEAN Valid : 1; + BOOLEAN Bad : 1; + BOOLEAN Spare : 6; + }; }; struct _PH_MEMORY_ITEM *AllocationBaseItem; @@ -95,10 +109,6 @@ typedef struct _PH_MEMORY_ITEM_LIST } PH_MEMORY_ITEM_LIST, *PPH_MEMORY_ITEM_LIST; // end_phapppub -BOOLEAN PhMemoryProviderInitialization( - VOID - ); - VOID PhGetMemoryProtectionString( _In_ ULONG Protection, _Out_writes_(17) PWSTR String diff --git a/ProcessHacker/include/memsrch.h b/ProcessHacker/include/memsrch.h index 76f1318af3f5..5dc8cc52c9e2 100644 --- a/ProcessHacker/include/memsrch.h +++ b/ProcessHacker/include/memsrch.h @@ -1,61 +1,72 @@ -#ifndef PH_MEMSRCH_H -#define PH_MEMSRCH_H - -typedef struct _PH_MEMORY_RESULT -{ - LONG RefCount; - PVOID Address; - SIZE_T Length; - PH_STRINGREF Display; -} PH_MEMORY_RESULT, *PPH_MEMORY_RESULT; - -typedef VOID (NTAPI *PPH_MEMORY_RESULT_CALLBACK)( - _In_ _Assume_refs_(1) PPH_MEMORY_RESULT Result, - _In_opt_ PVOID Context - ); - -#define PH_DISPLAY_BUFFER_COUNT (PAGE_SIZE * 2 - 1) - -typedef struct _PH_MEMORY_SEARCH_OPTIONS -{ - BOOLEAN Cancel; - PPH_MEMORY_RESULT_CALLBACK Callback; - PVOID Context; -} PH_MEMORY_SEARCH_OPTIONS, *PPH_MEMORY_SEARCH_OPTIONS; - -typedef struct _PH_MEMORY_STRING_OPTIONS -{ - PH_MEMORY_SEARCH_OPTIONS Header; - - ULONG MinimumLength; - BOOLEAN DetectUnicode; - ULONG MemoryTypeMask; -} PH_MEMORY_STRING_OPTIONS, *PPH_MEMORY_STRING_OPTIONS; - -PVOID PhAllocateForMemorySearch( - _In_ SIZE_T Size - ); - -VOID PhFreeForMemorySearch( - _In_ _Post_invalid_ PVOID Memory - ); - -PVOID PhCreateMemoryResult( - _In_ PVOID Address, - _In_ SIZE_T Length - ); - -VOID PhReferenceMemoryResult( - _In_ PPH_MEMORY_RESULT Result - ); - -VOID PhDereferenceMemoryResult( - _In_ PPH_MEMORY_RESULT Result - ); - -VOID PhDereferenceMemoryResults( - _In_reads_(NumberOfResults) PPH_MEMORY_RESULT *Results, - _In_ ULONG NumberOfResults - ); - -#endif +#ifndef PH_MEMSRCH_H +#define PH_MEMSRCH_H + +typedef struct _PH_MEMORY_RESULT +{ + LONG RefCount; + PVOID Address; + PVOID BaseAddress; + SIZE_T Length; + PH_STRINGREF Display; +} PH_MEMORY_RESULT, *PPH_MEMORY_RESULT; + +typedef VOID (NTAPI *PPH_MEMORY_RESULT_CALLBACK)( + _In_ _Assume_refs_(1) PPH_MEMORY_RESULT Result, + _In_opt_ PVOID Context + ); + +#define PH_DISPLAY_BUFFER_COUNT (PAGE_SIZE * 2 - 1) + +typedef struct _PH_MEMORY_SEARCH_OPTIONS +{ + BOOLEAN Cancel; + PPH_MEMORY_RESULT_CALLBACK Callback; + PVOID Context; +} PH_MEMORY_SEARCH_OPTIONS, *PPH_MEMORY_SEARCH_OPTIONS; + +typedef struct _PH_MEMORY_STRING_OPTIONS +{ + PH_MEMORY_SEARCH_OPTIONS Header; + + ULONG MinimumLength; + ULONG MemoryTypeMask; + union + { + BOOLEAN Flags; + struct + { + BOOLEAN DetectUnicode : 1; + BOOLEAN ExtendedUnicode : 1; + BOOLEAN Spare : 6; + }; + }; +} PH_MEMORY_STRING_OPTIONS, *PPH_MEMORY_STRING_OPTIONS; + +PVOID PhAllocateForMemorySearch( + _In_ SIZE_T Size + ); + +VOID PhFreeForMemorySearch( + _In_ _Post_invalid_ PVOID Memory + ); + +PVOID PhCreateMemoryResult( + _In_ PVOID Address, + _In_ PVOID BaseAddress, + _In_ SIZE_T Length + ); + +VOID PhReferenceMemoryResult( + _In_ PPH_MEMORY_RESULT Result + ); + +VOID PhDereferenceMemoryResult( + _In_ PPH_MEMORY_RESULT Result + ); + +VOID PhDereferenceMemoryResults( + _In_reads_(NumberOfResults) PPH_MEMORY_RESULT *Results, + _In_ ULONG NumberOfResults + ); + +#endif diff --git a/ProcessHacker/include/miniinfo.h b/ProcessHacker/include/miniinfo.h index 761cc644beb0..fe95f7b6db4d 100644 --- a/ProcessHacker/include/miniinfo.h +++ b/ProcessHacker/include/miniinfo.h @@ -1,222 +1,222 @@ -#ifndef PH_MINIINFO_H -#define PH_MINIINFO_H - -#include - -// begin_phapppub -// Section - -typedef VOID (NTAPI *PPH_MINIINFO_SET_SECTION_TEXT)( - _In_ struct _PH_MINIINFO_SECTION *Section, - _In_opt_ PPH_STRING Text - ); - -typedef struct _PH_MINIINFO_PARAMETERS -{ - HWND ContainerWindowHandle; - HWND MiniInfoWindowHandle; - - HFONT Font; - HFONT MediumFont; - ULONG FontHeight; - ULONG FontAverageWidth; - ULONG MediumFontHeight; - ULONG MediumFontAverageWidth; - - PPH_MINIINFO_SET_SECTION_TEXT SetSectionText; -} PH_MINIINFO_PARAMETERS, *PPH_MINIINFO_PARAMETERS; - -typedef enum _PH_MINIINFO_SECTION_MESSAGE -{ - MiniInfoCreate, - MiniInfoDestroy, - MiniInfoTick, - MiniInfoSectionChanging, // PPH_MINIINFO_SECTION Parameter1 - MiniInfoShowing, // BOOLEAN Parameter1 (Showing) - MiniInfoCreateDialog, // PPH_MINIINFO_CREATE_DIALOG Parameter1 - MaxMiniInfoMessage -} PH_MINIINFO_SECTION_MESSAGE; - -typedef BOOLEAN (NTAPI *PPH_MINIINFO_SECTION_CALLBACK)( - _In_ struct _PH_MINIINFO_SECTION *Section, - _In_ PH_MINIINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -typedef struct _PH_MINIINFO_CREATE_DIALOG -{ - BOOLEAN CustomCreate; - - // Parameters for default create - PVOID Instance; - PWSTR Template; - DLGPROC DialogProc; - PVOID Parameter; -} PH_MINIINFO_CREATE_DIALOG, *PPH_MINIINFO_CREATE_DIALOG; - -#define PH_MINIINFO_SECTION_NO_UPPER_MARGINS 0x1 -// end_phapppub - -// begin_phapppub -typedef struct _PH_MINIINFO_SECTION -{ - // Public - - // Initialization - PH_STRINGREF Name; - ULONG Flags; - PPH_MINIINFO_SECTION_CALLBACK Callback; - PVOID Context; - PVOID Reserved1[3]; - - PPH_MINIINFO_PARAMETERS Parameters; - PVOID Reserved2[3]; -// end_phapppub - - // Private - - struct - { - ULONG SpareFlags : 32; - }; - HWND DialogHandle; - PPH_STRING Text; -// begin_phapppub -} PH_MINIINFO_SECTION, *PPH_MINIINFO_SECTION; -// end_phapppub - -typedef enum _PH_MINIINFO_PIN_TYPE -{ - MiniInfoManualPinType, // User pin - MiniInfoIconPinType, // Notification icon - MiniInfoActivePinType, // Window is active - MiniInfoHoverPinType, // Cursor is over mini info window - MiniInfoChildControlPinType, // Interacting with child control - MaxMiniInfoPinType -} PH_MINIINFO_PIN_TYPE; - -#define PH_MINIINFO_ACTIVATE_WINDOW 0x1 -#define PH_MINIINFO_LOAD_POSITION 0x2 -#define PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED 0x4 - -VOID PhPinMiniInformation( - _In_ PH_MINIINFO_PIN_TYPE PinType, - _In_ LONG PinCount, - _In_opt_ ULONG PinDelayMs, - _In_ ULONG Flags, - _In_opt_ PWSTR SectionName, - _In_opt_ PPOINT SourcePoint - ); - -// begin_phapppub -// List section - -typedef enum _PH_MINIINFO_LIST_SECTION_MESSAGE -{ - MiListSectionCreate, - MiListSectionDestroy, - MiListSectionTick, - MiListSectionShowing, // BOOLEAN Parameter1 (Showing) - MiListSectionDialogCreated, // HWND Parameter1 (DialogHandle) - MiListSectionSortProcessList, // PPH_MINIINFO_LIST_SECTION_SORT_LIST Parameter1 - MiListSectionAssignSortData, // PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA Parameter1 - MiListSectionSortGroupList, // PPH_MINIINFO_LIST_SECTION_SORT_LIST Parameter1 - MiListSectionGetTitleText, // PPH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT Parameter1 - MiListSectionGetUsageText, // PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT Parameter1 - MiListSectionInitializeContextMenu, // PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION Parameter1 - MiListSectionHandleContextMenu, // PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION Parameter1 - MaxMiListSectionMessage -} PH_MINIINFO_LIST_SECTION_MESSAGE; - -typedef BOOLEAN (NTAPI *PPH_MINIINFO_LIST_SECTION_CALLBACK)( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -// The list section performs the following steps when constructing the list of process groups: -// 1. MiListSectionSortProcessList is sent in order to sort the process list. -// 2. A small number of process groups is created from the first few processes in the sorted list (typically high -// resource consumers). -// 3. MiListSectionAssignSortData is sent for each process group so that the user can assign custom sort data to -// each process group. -// 4. MiListSectionSortGroupList is sent in order to ensure that the process groups are correctly sorted by resource -// usage. -// The user also has access to the sort data when handling MiListSectionGetTitleText and MiListSectionGetUsageText. - -typedef struct _PH_MINIINFO_LIST_SECTION_SORT_DATA -{ - PH_TREENEW_NODE DoNotModify; - ULONGLONG UserData[4]; -} PH_MINIINFO_LIST_SECTION_SORT_DATA, *PPH_MINIINFO_LIST_SECTION_SORT_DATA; - -typedef struct _PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA -{ - PPH_PROCESS_GROUP ProcessGroup; - PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; -} PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA, *PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA; - -typedef struct _PH_MINIINFO_LIST_SECTION_SORT_LIST -{ - // MiListSectionSortProcessList: List of PPH_PROCESS_NODE - // MiListSectionSortGroupList: List of PPH_MINIINFO_LIST_SECTION_SORT_DATA - PPH_LIST List; -} PH_MINIINFO_LIST_SECTION_SORT_LIST, *PPH_MINIINFO_LIST_SECTION_SORT_LIST; - -typedef struct _PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT -{ - PPH_PROCESS_GROUP ProcessGroup; - PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; - PPH_STRING Title; // Top line (may already contain a string) - PPH_STRING Subtitle; // Bottom line (may already contain a string) - COLORREF TitleColor; - COLORREF SubtitleColor; -} PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT, *PPH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT; - -typedef struct _PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT -{ - PPH_PROCESS_GROUP ProcessGroup; - PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; - PPH_STRING Line1; // Top line - PPH_STRING Line2; // Bottom line - COLORREF Line1Color; - COLORREF Line2Color; -} PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT, *PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT; - -typedef struct _PH_MINIINFO_LIST_SECTION_MENU_INFORMATION -{ - PPH_PROCESS_GROUP ProcessGroup; - PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; - PPH_TREENEW_CONTEXT_MENU ContextMenu; - struct _PH_EMENU_ITEM *SelectedItem; -} PH_MINIINFO_LIST_SECTION_MENU_INFORMATION, *PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION; -// end_phapppub - -// begin_phapppub -typedef struct _PH_MINIINFO_LIST_SECTION -{ - // Public - - PPH_MINIINFO_SECTION Section; // State - HWND DialogHandle; // State - HWND TreeNewHandle; // State - PVOID Context; // Initialization - PPH_MINIINFO_LIST_SECTION_CALLBACK Callback; // Initialization -// end_phapppub - - // Private - - PH_LAYOUT_MANAGER LayoutManager; - ULONG RunCount; - LONG SuspendUpdate; - PPH_LIST ProcessGroupList; - PPH_LIST NodeList; - HANDLE SelectedRepresentativeProcessId; - LARGE_INTEGER SelectedRepresentativeCreateTime; -// begin_phapppub -} PH_MINIINFO_LIST_SECTION, *PPH_MINIINFO_LIST_SECTION; -// end_phapppub - +#ifndef PH_MINIINFO_H +#define PH_MINIINFO_H + +#include + +// begin_phapppub +// Section + +typedef VOID (NTAPI *PPH_MINIINFO_SET_SECTION_TEXT)( + _In_ struct _PH_MINIINFO_SECTION *Section, + _In_opt_ PPH_STRING Text + ); + +typedef struct _PH_MINIINFO_PARAMETERS +{ + HWND ContainerWindowHandle; + HWND MiniInfoWindowHandle; + + HFONT Font; + HFONT MediumFont; + ULONG FontHeight; + ULONG FontAverageWidth; + ULONG MediumFontHeight; + ULONG MediumFontAverageWidth; + + PPH_MINIINFO_SET_SECTION_TEXT SetSectionText; +} PH_MINIINFO_PARAMETERS, *PPH_MINIINFO_PARAMETERS; + +typedef enum _PH_MINIINFO_SECTION_MESSAGE +{ + MiniInfoCreate, + MiniInfoDestroy, + MiniInfoTick, + MiniInfoSectionChanging, // PPH_MINIINFO_SECTION Parameter1 + MiniInfoShowing, // BOOLEAN Parameter1 (Showing) + MiniInfoCreateDialog, // PPH_MINIINFO_CREATE_DIALOG Parameter1 + MaxMiniInfoMessage +} PH_MINIINFO_SECTION_MESSAGE; + +typedef BOOLEAN (NTAPI *PPH_MINIINFO_SECTION_CALLBACK)( + _In_ struct _PH_MINIINFO_SECTION *Section, + _In_ PH_MINIINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +typedef struct _PH_MINIINFO_CREATE_DIALOG +{ + BOOLEAN CustomCreate; + + // Parameters for default create + PVOID Instance; + PWSTR Template; + DLGPROC DialogProc; + PVOID Parameter; +} PH_MINIINFO_CREATE_DIALOG, *PPH_MINIINFO_CREATE_DIALOG; + +#define PH_MINIINFO_SECTION_NO_UPPER_MARGINS 0x1 +// end_phapppub + +// begin_phapppub +typedef struct _PH_MINIINFO_SECTION +{ + // Public + + // Initialization + PH_STRINGREF Name; + ULONG Flags; + PPH_MINIINFO_SECTION_CALLBACK Callback; + PVOID Context; + PVOID Reserved1[3]; + + PPH_MINIINFO_PARAMETERS Parameters; + PVOID Reserved2[3]; +// end_phapppub + + // Private + + struct + { + ULONG SpareFlags : 32; + }; + HWND DialogHandle; + PPH_STRING Text; +// begin_phapppub +} PH_MINIINFO_SECTION, *PPH_MINIINFO_SECTION; +// end_phapppub + +typedef enum _PH_MINIINFO_PIN_TYPE +{ + MiniInfoManualPinType, // User pin + MiniInfoIconPinType, // Notification icon + MiniInfoActivePinType, // Window is active + MiniInfoHoverPinType, // Cursor is over mini info window + MiniInfoChildControlPinType, // Interacting with child control + MaxMiniInfoPinType +} PH_MINIINFO_PIN_TYPE; + +#define PH_MINIINFO_ACTIVATE_WINDOW 0x1 +#define PH_MINIINFO_LOAD_POSITION 0x2 +#define PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED 0x4 + +VOID PhPinMiniInformation( + _In_ PH_MINIINFO_PIN_TYPE PinType, + _In_ LONG PinCount, + _In_opt_ ULONG PinDelayMs, + _In_ ULONG Flags, + _In_opt_ PWSTR SectionName, + _In_opt_ PPOINT SourcePoint + ); + +// begin_phapppub +// List section + +typedef enum _PH_MINIINFO_LIST_SECTION_MESSAGE +{ + MiListSectionCreate, + MiListSectionDestroy, + MiListSectionTick, + MiListSectionShowing, // BOOLEAN Parameter1 (Showing) + MiListSectionDialogCreated, // HWND Parameter1 (DialogHandle) + MiListSectionSortProcessList, // PPH_MINIINFO_LIST_SECTION_SORT_LIST Parameter1 + MiListSectionAssignSortData, // PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA Parameter1 + MiListSectionSortGroupList, // PPH_MINIINFO_LIST_SECTION_SORT_LIST Parameter1 + MiListSectionGetTitleText, // PPH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT Parameter1 + MiListSectionGetUsageText, // PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT Parameter1 + MiListSectionInitializeContextMenu, // PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION Parameter1 + MiListSectionHandleContextMenu, // PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION Parameter1 + MaxMiListSectionMessage +} PH_MINIINFO_LIST_SECTION_MESSAGE; + +typedef BOOLEAN (NTAPI *PPH_MINIINFO_LIST_SECTION_CALLBACK)( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +// The list section performs the following steps when constructing the list of process groups: +// 1. MiListSectionSortProcessList is sent in order to sort the process list. +// 2. A small number of process groups is created from the first few processes in the sorted list (typically high +// resource consumers). +// 3. MiListSectionAssignSortData is sent for each process group so that the user can assign custom sort data to +// each process group. +// 4. MiListSectionSortGroupList is sent in order to ensure that the process groups are correctly sorted by resource +// usage. +// The user also has access to the sort data when handling MiListSectionGetTitleText and MiListSectionGetUsageText. + +typedef struct _PH_MINIINFO_LIST_SECTION_SORT_DATA +{ + PH_TREENEW_NODE DoNotModify; + ULONGLONG UserData[4]; +} PH_MINIINFO_LIST_SECTION_SORT_DATA, *PPH_MINIINFO_LIST_SECTION_SORT_DATA; + +typedef struct _PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA +{ + PPH_PROCESS_GROUP ProcessGroup; + PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; +} PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA, *PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA; + +typedef struct _PH_MINIINFO_LIST_SECTION_SORT_LIST +{ + // MiListSectionSortProcessList: List of PPH_PROCESS_NODE + // MiListSectionSortGroupList: List of PPH_MINIINFO_LIST_SECTION_SORT_DATA + PPH_LIST List; +} PH_MINIINFO_LIST_SECTION_SORT_LIST, *PPH_MINIINFO_LIST_SECTION_SORT_LIST; + +typedef struct _PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT +{ + PPH_PROCESS_GROUP ProcessGroup; + PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; + PPH_STRING Title; // Top line (may already contain a string) + PPH_STRING Subtitle; // Bottom line (may already contain a string) + COLORREF TitleColor; + COLORREF SubtitleColor; +} PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT, *PPH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT; + +typedef struct _PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT +{ + PPH_PROCESS_GROUP ProcessGroup; + PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; + PPH_STRING Line1; // Top line + PPH_STRING Line2; // Bottom line + COLORREF Line1Color; + COLORREF Line2Color; +} PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT, *PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT; + +typedef struct _PH_MINIINFO_LIST_SECTION_MENU_INFORMATION +{ + PPH_PROCESS_GROUP ProcessGroup; + PPH_MINIINFO_LIST_SECTION_SORT_DATA SortData; + PPH_TREENEW_CONTEXT_MENU ContextMenu; + struct _PH_EMENU_ITEM *SelectedItem; +} PH_MINIINFO_LIST_SECTION_MENU_INFORMATION, *PPH_MINIINFO_LIST_SECTION_MENU_INFORMATION; +// end_phapppub + +// begin_phapppub +typedef struct _PH_MINIINFO_LIST_SECTION +{ + // Public + + PPH_MINIINFO_SECTION Section; // State + HWND DialogHandle; // State + HWND TreeNewHandle; // State + PVOID Context; // Initialization + PPH_MINIINFO_LIST_SECTION_CALLBACK Callback; // Initialization +// end_phapppub + + // Private + + PH_LAYOUT_MANAGER LayoutManager; + ULONG RunCount; + LONG SuspendUpdate; + PPH_LIST ProcessGroupList; + PPH_LIST NodeList; + HANDLE SelectedRepresentativeProcessId; + LARGE_INTEGER SelectedRepresentativeCreateTime; +// begin_phapppub +} PH_MINIINFO_LIST_SECTION, *PPH_MINIINFO_LIST_SECTION; +// end_phapppub + #endif \ No newline at end of file diff --git a/ProcessHacker/include/miniinfop.h b/ProcessHacker/include/miniinfop.h index eaa9bfa4e153..00e81b5eca46 100644 --- a/ProcessHacker/include/miniinfop.h +++ b/ProcessHacker/include/miniinfop.h @@ -1,405 +1,401 @@ -#ifndef PH_MINIINFOP_H -#define PH_MINIINFOP_H - -// Constants - -#define MIP_CONTAINER_CLASSNAME L"ProcessHackerMiniInfo" - -#define MIP_TIMER_PIN_FIRST 1 -#define MIP_TIMER_PIN_LAST (MIP_TIMER_PIN_FIRST + MaxMiniInfoPinType - 1) - -#define MIP_MSG_FIRST (WM_APP + 150) -#define MIP_MSG_UPDATE (WM_APP + 150) -#define MIP_MSG_LAST (WM_APP + 151) - -#define MIP_UNPIN_CHILD_CONTROL_DELAY 1000 -#define MIP_UNPIN_HOVER_DELAY 250 - -#define MIP_REFRESH_AUTOMATICALLY_PINNED 0x1 -#define MIP_REFRESH_AUTOMATICALLY_UNPINNED 0x2 -#define MIP_REFRESH_AUTOMATICALLY_FLAG(Pinned) \ - ((Pinned) ? MIP_REFRESH_AUTOMATICALLY_PINNED : MIP_REFRESH_AUTOMATICALLY_UNPINNED) - -// Misc. - -#define SET_BUTTON_ICON(hwndDlg, Id, Icon) \ - SendMessage(GetDlgItem(hwndDlg, (Id)), BM_SETIMAGE, IMAGE_ICON, (LPARAM)(Icon)) - -// Dialog procedure - -LRESULT CALLBACK PhMipContainerWndProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhMipMiniInfoDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// Container event handlers - -VOID PhMipContainerOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ); - -VOID PhMipContainerOnActivate( - _In_ ULONG Type, - _In_ BOOLEAN Minimized - ); - -VOID PhMipContainerOnSize( - VOID - ); - -VOID PhMipContainerOnSizing( - _In_ ULONG Edge, - _In_ PRECT DragRectangle - ); - -VOID PhMipContainerOnExitSizeMove( - VOID - ); - -BOOLEAN PhMipContainerOnEraseBkgnd( - _In_ HDC hdc - ); - -VOID PhMipContainerOnTimer( - _In_ ULONG Id - ); - -// Child dialog event handlers - -VOID PhMipOnInitDialog( - VOID - ); - -VOID PhMipOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ); - -VOID PhMipOnCommand( - _In_ ULONG Id, - _In_ ULONG Code - ); - -BOOLEAN PhMipOnNotify( - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ); - -BOOLEAN PhMipOnCtlColorXxx( - _In_ ULONG Message, - _In_ HWND hwnd, - _In_ HDC hdc, - _Out_ HBRUSH *Brush - ); - -BOOLEAN PhMipOnDrawItem( - _In_ ULONG_PTR Id, - _In_ DRAWITEMSTRUCT *DrawItemStruct - ); - -VOID PhMipOnUserMessage( - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ); - -// Framework - -typedef enum _PH_MIP_ADJUST_PIN_RESULT -{ - NoAdjustPinResult, - ShowAdjustPinResult, - HideAdjustPinResult -} PH_MIP_ADJUST_PIN_RESULT; - -BOOLEAN NTAPI PhMipMessageLoopFilter( - _In_ PMSG Message, - _In_ PVOID Context - ); - -VOID NTAPI PhMipUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -PH_MIP_ADJUST_PIN_RESULT PhMipAdjustPin( - _In_ PH_MINIINFO_PIN_TYPE PinType, - _In_ LONG PinCount - ); - -VOID PhMipCalculateWindowRectangle( - _In_ PPOINT SourcePoint, - _Out_ PPH_RECTANGLE WindowRectangle - ); - -VOID PhMipInitializeParameters( - VOID - ); - -PPH_MINIINFO_SECTION PhMipCreateSection( - _In_ PPH_MINIINFO_SECTION Template - ); - -VOID PhMipDestroySection( - _In_ PPH_MINIINFO_SECTION Section - ); - -PPH_MINIINFO_SECTION PhMipFindSection( - _In_ PPH_STRINGREF Name - ); - -PPH_MINIINFO_SECTION PhMipCreateInternalSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_SECTION_CALLBACK Callback - ); - -VOID PhMipCreateSectionDialog( - _In_ PPH_MINIINFO_SECTION Section - ); - -VOID PhMipChangeSection( - _In_ PPH_MINIINFO_SECTION NewSection - ); - -VOID PhMipSetSectionText( - _In_ struct _PH_MINIINFO_SECTION *Section, - _In_opt_ PPH_STRING Text - ); - -VOID PhMipUpdateSectionText( - _In_ PPH_MINIINFO_SECTION Section - ); - -VOID PhMipLayout( - VOID - ); - -VOID PhMipBeginChildControlPin( - VOID - ); - -VOID PhMipEndChildControlPin( - VOID - ); - -VOID PhMipRefresh( - VOID - ); - -VOID PhMipToggleRefreshAutomatically( - VOID - ); - -VOID PhMipSetPinned( - _In_ BOOLEAN Pinned - ); - -VOID PhMipShowSectionMenu( - VOID - ); - -VOID PhMipShowOptionsMenu( - VOID - ); - -LRESULT CALLBACK PhMipSectionControlHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -// List-based section - -#define MIP_MAX_PROCESS_GROUPS 15 -#define MIP_SINGLE_COLUMN_ID 0 - -#define MIP_CELL_PADDING 5 -#define MIP_ICON_PADDING 3 -#define MIP_INNER_PADDING 3 - -typedef struct _PH_MIP_GROUP_NODE -{ - union - { - PH_TREENEW_NODE Node; - PH_MINIINFO_LIST_SECTION_SORT_DATA SortData; - }; - PPH_PROCESS_GROUP ProcessGroup; - HANDLE RepresentativeProcessId; - LARGE_INTEGER RepresentativeCreateTime; - BOOLEAN RepresentativeIsHung; - - PPH_STRING TooltipText; -} PH_MIP_GROUP_NODE, *PPH_MIP_GROUP_NODE; - -PPH_MINIINFO_LIST_SECTION PhMipCreateListSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_LIST_SECTION Template - ); - -PPH_MINIINFO_LIST_SECTION PhMipCreateInternalListSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_LIST_SECTION_CALLBACK Callback - ); - -BOOLEAN PhMipListSectionCallback( - _In_ PPH_MINIINFO_SECTION Section, - _In_ PH_MINIINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -INT_PTR CALLBACK PhMipListSectionDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhMipListSectionSortFunction( - _In_ PPH_LIST List, - _In_opt_ PVOID Context - ); - -VOID PhMipTickListSection( - _In_ PPH_MINIINFO_LIST_SECTION ListSection - ); - -VOID PhMipClearListSection( - _In_ PPH_MINIINFO_LIST_SECTION ListSection - ); - -LONG PhMipCalculateRowHeight( - VOID - ); - -PPH_MIP_GROUP_NODE PhMipAddGroupNode( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_PROCESS_GROUP ProcessGroup - ); - -VOID PhMipDestroyGroupNode( - _In_ PPH_MIP_GROUP_NODE Node - ); - -BOOLEAN PhMipListSectionTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -PPH_STRING PhMipGetGroupNodeTooltip( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_MIP_GROUP_NODE Node - ); - -PPH_MIP_GROUP_NODE PhMipGetSelectedGroupNode( - _In_ PPH_MINIINFO_LIST_SECTION ListSection - ); - -VOID PhMipShowListSectionContextMenu( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_TREENEW_CONTEXT_MENU ContextMenu - ); - -VOID PhMipHandleListSectionCommand( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_PROCESS_GROUP ProcessGroup, - _In_ ULONG Id - ); - -// CPU section - -BOOLEAN PhMipCpuListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl PhMipCpuListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl PhMipCpuListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -// Commit charge section - -BOOLEAN PhMipCommitListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl PhMipCommitListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl PhMipCommitListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -// Physical memory section - -BOOLEAN PhMipPhysicalListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl PhMipPhysicalListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl PhMipPhysicalListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -// I/O section - -BOOLEAN PhMipIoListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl PhMipIoListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl PhMipIoListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - +#ifndef PH_MINIINFOP_H +#define PH_MINIINFOP_H + +// Constants + +#define MIP_TIMER_PIN_FIRST 1 +#define MIP_TIMER_PIN_LAST (MIP_TIMER_PIN_FIRST + MaxMiniInfoPinType - 1) + +#define MIP_MSG_FIRST (WM_APP + 150) +#define MIP_MSG_UPDATE (WM_APP + 150) +#define MIP_MSG_LAST (WM_APP + 151) + +#define MIP_UNPIN_CHILD_CONTROL_DELAY 1000 +#define MIP_UNPIN_HOVER_DELAY 250 + +#define MIP_REFRESH_AUTOMATICALLY_PINNED 0x1 +#define MIP_REFRESH_AUTOMATICALLY_UNPINNED 0x2 +#define MIP_REFRESH_AUTOMATICALLY_FLAG(Pinned) \ + ((Pinned) ? MIP_REFRESH_AUTOMATICALLY_PINNED : MIP_REFRESH_AUTOMATICALLY_UNPINNED) + +// Misc. + +#define SET_BUTTON_ICON(hwndDlg, Id, Icon) \ + SendMessage(GetDlgItem(hwndDlg, (Id)), BM_SETIMAGE, IMAGE_ICON, (LPARAM)(Icon)) + +// Dialog procedure + +LRESULT CALLBACK PhMipContainerWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhMipMiniInfoDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// Container event handlers + +VOID PhMipContainerOnShowWindow( + _In_ BOOLEAN Showing, + _In_ ULONG State + ); + +VOID PhMipContainerOnActivate( + _In_ ULONG Type, + _In_ BOOLEAN Minimized + ); + +VOID PhMipContainerOnSize( + VOID + ); + +VOID PhMipContainerOnSizing( + _In_ ULONG Edge, + _In_ PRECT DragRectangle + ); + +VOID PhMipContainerOnExitSizeMove( + VOID + ); + +BOOLEAN PhMipContainerOnEraseBkgnd( + _In_ HDC hdc + ); + +VOID PhMipContainerOnTimer( + _In_ ULONG Id + ); + +// Child dialog event handlers + +VOID PhMipOnInitDialog( + VOID + ); + +VOID PhMipOnShowWindow( + _In_ BOOLEAN Showing, + _In_ ULONG State + ); + +VOID PhMipOnCommand( + _In_ ULONG Id, + _In_ ULONG Code + ); + +BOOLEAN PhMipOnNotify( + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ); + +BOOLEAN PhMipOnCtlColorXxx( + _In_ ULONG Message, + _In_ HWND hwnd, + _In_ HDC hdc, + _Out_ HBRUSH *Brush + ); + +BOOLEAN PhMipOnDrawItem( + _In_ ULONG_PTR Id, + _In_ DRAWITEMSTRUCT *DrawItemStruct + ); + +VOID PhMipOnUserMessage( + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ); + +// Framework + +typedef enum _PH_MIP_ADJUST_PIN_RESULT +{ + NoAdjustPinResult, + ShowAdjustPinResult, + HideAdjustPinResult +} PH_MIP_ADJUST_PIN_RESULT; + +BOOLEAN NTAPI PhMipMessageLoopFilter( + _In_ PMSG Message, + _In_ PVOID Context + ); + +VOID NTAPI PhMipUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +PH_MIP_ADJUST_PIN_RESULT PhMipAdjustPin( + _In_ PH_MINIINFO_PIN_TYPE PinType, + _In_ LONG PinCount + ); + +VOID PhMipCalculateWindowRectangle( + _In_ PPOINT SourcePoint, + _Out_ PPH_RECTANGLE WindowRectangle + ); + +VOID PhMipInitializeParameters( + VOID + ); + +PPH_MINIINFO_SECTION PhMipCreateSection( + _In_ PPH_MINIINFO_SECTION Template + ); + +VOID PhMipDestroySection( + _In_ PPH_MINIINFO_SECTION Section + ); + +PPH_MINIINFO_SECTION PhMipFindSection( + _In_ PPH_STRINGREF Name + ); + +PPH_MINIINFO_SECTION PhMipCreateInternalSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_SECTION_CALLBACK Callback + ); + +VOID PhMipCreateSectionDialog( + _In_ PPH_MINIINFO_SECTION Section + ); + +VOID PhMipChangeSection( + _In_ PPH_MINIINFO_SECTION NewSection + ); + +VOID PhMipSetSectionText( + _In_ struct _PH_MINIINFO_SECTION *Section, + _In_opt_ PPH_STRING Text + ); + +VOID PhMipUpdateSectionText( + _In_ PPH_MINIINFO_SECTION Section + ); + +VOID PhMipLayout( + VOID + ); + +VOID PhMipBeginChildControlPin( + VOID + ); + +VOID PhMipEndChildControlPin( + VOID + ); + +VOID PhMipRefresh( + VOID + ); + +VOID PhMipToggleRefreshAutomatically( + VOID + ); + +VOID PhMipSetPinned( + _In_ BOOLEAN Pinned + ); + +VOID PhMipShowSectionMenu( + VOID + ); + +VOID PhMipShowOptionsMenu( + VOID + ); + +LRESULT CALLBACK PhMipSectionControlHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// List-based section + +#define MIP_MAX_PROCESS_GROUPS 15 +#define MIP_SINGLE_COLUMN_ID 0 + +#define MIP_CELL_PADDING 5 +#define MIP_ICON_PADDING 3 +#define MIP_INNER_PADDING 3 + +typedef struct _PH_MIP_GROUP_NODE +{ + union + { + PH_TREENEW_NODE Node; + PH_MINIINFO_LIST_SECTION_SORT_DATA SortData; + }; + PPH_PROCESS_GROUP ProcessGroup; + HANDLE RepresentativeProcessId; + LARGE_INTEGER RepresentativeCreateTime; + BOOLEAN RepresentativeIsHung; + + PPH_STRING TooltipText; +} PH_MIP_GROUP_NODE, *PPH_MIP_GROUP_NODE; + +PPH_MINIINFO_LIST_SECTION PhMipCreateListSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_LIST_SECTION Template + ); + +PPH_MINIINFO_LIST_SECTION PhMipCreateInternalListSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_LIST_SECTION_CALLBACK Callback + ); + +BOOLEAN PhMipListSectionCallback( + _In_ PPH_MINIINFO_SECTION Section, + _In_ PH_MINIINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +INT_PTR CALLBACK PhMipListSectionDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhMipListSectionSortFunction( + _In_ PPH_LIST List, + _In_opt_ PVOID Context + ); + +VOID PhMipTickListSection( + _In_ PPH_MINIINFO_LIST_SECTION ListSection + ); + +VOID PhMipClearListSection( + _In_ PPH_MINIINFO_LIST_SECTION ListSection + ); + +LONG PhMipCalculateRowHeight( + VOID + ); + +PPH_MIP_GROUP_NODE PhMipAddGroupNode( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_PROCESS_GROUP ProcessGroup + ); + +VOID PhMipDestroyGroupNode( + _In_ PPH_MIP_GROUP_NODE Node + ); + +BOOLEAN PhMipListSectionTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +PPH_STRING PhMipGetGroupNodeTooltip( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_MIP_GROUP_NODE Node + ); + +PPH_MIP_GROUP_NODE PhMipGetSelectedGroupNode( + _In_ PPH_MINIINFO_LIST_SECTION ListSection + ); + +VOID PhMipShowListSectionContextMenu( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenu + ); + +VOID PhMipHandleListSectionCommand( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_PROCESS_GROUP ProcessGroup, + _In_ ULONG Id + ); + +// CPU section + +BOOLEAN PhMipCpuListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl PhMipCpuListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl PhMipCpuListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +// Commit charge section + +BOOLEAN PhMipCommitListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl PhMipCommitListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl PhMipCommitListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +// Physical memory section + +BOOLEAN PhMipPhysicalListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl PhMipPhysicalListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl PhMipPhysicalListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +// I/O section + +BOOLEAN PhMipIoListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl PhMipIoListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl PhMipIoListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + #endif \ No newline at end of file diff --git a/ProcessHacker/include/modlist.h b/ProcessHacker/include/modlist.h index d665a6564a06..bf14ac60db92 100644 --- a/ProcessHacker/include/modlist.h +++ b/ProcessHacker/include/modlist.h @@ -26,8 +26,10 @@ #define PHMOTLC_LOADREASON 15 #define PHMOTLC_FILEMODIFIEDTIME 16 #define PHMOTLC_FILESIZE 17 +#define PHMOTLC_ENTRYPOINT 18 +#define PHMOTLC_PARENTBASEADDRESS 19 -#define PHMOTLC_MAXIMUM 18 +#define PHMOTLC_MAXIMUM 20 // begin_phapppub typedef struct _PH_MODULE_NODE @@ -51,22 +53,58 @@ typedef struct _PH_MODULE_NODE PPH_STRING LoadTimeText; PPH_STRING FileModifiedTimeText; PPH_STRING FileSizeText; + + struct _PH_MODULE_NODE *Parent; + PPH_LIST Children; // begin_phapppub } PH_MODULE_NODE, *PPH_MODULE_NODE; // end_phapppub +#define PH_MODULE_FLAGS_DYNAMIC_OPTION 1 +#define PH_MODULE_FLAGS_MAPPED_OPTION 2 +#define PH_MODULE_FLAGS_STATIC_OPTION 3 +#define PH_MODULE_FLAGS_SIGNED_OPTION 4 +#define PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION 5 +#define PH_MODULE_FLAGS_HIGHLIGHT_DOTNET_OPTION 6 +#define PH_MODULE_FLAGS_HIGHLIGHT_IMMERSIVE_OPTION 7 +#define PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION 8 +#define PH_MODULE_FLAGS_LOAD_MODULE_OPTION 9 +#define PH_MODULE_FLAGS_MODULE_STRINGS_OPTION 10 +#define PH_MODULE_FLAGS_SYSTEM_OPTION 11 +#define PH_MODULE_FLAGS_HIGHLIGHT_SYSTEM_OPTION 12 + typedef struct _PH_MODULE_LIST_CONTEXT { HWND ParentWindowHandle; HWND TreeNewHandle; ULONG TreeNewSortColumn; + PH_TN_FILTER_SUPPORT TreeFilterSupport; PH_SORT_ORDER TreeNewSortOrder; PH_CM_MANAGER Cm; + union + { + ULONG Flags; + struct + { + ULONG EnableStateHighlighting : 1; + ULONG HideDynamicModules : 1; + ULONG HideMappedModules : 1; + ULONG HideSignedModules : 1; + ULONG HideStaticModules : 1; + ULONG HighlightUntrustedModules : 1; + ULONG HighlightDotNetModules : 1; + ULONG HighlightImmersiveModules : 1; + ULONG HighlightRelocatedModules : 1; + ULONG HideSystemModules : 1; + ULONG HighlightSystemModules : 1; + ULONG Spare : 21; + }; + }; + PPH_HASHTABLE NodeHashtable; PPH_LIST NodeList; - - BOOLEAN EnableStateHighlighting; + PPH_LIST NodeRootList; PPH_POINTER_LIST NodeStateList; HFONT BoldFont; @@ -90,6 +128,11 @@ VOID PhSaveSettingsModuleList( _Inout_ PPH_MODULE_LIST_CONTEXT Context ); +VOID PhSetOptionsModuleList( + _Inout_ PPH_MODULE_LIST_CONTEXT Context, + _In_ ULONG Options + ); + PPH_MODULE_NODE PhAddModuleNode( _Inout_ PPH_MODULE_LIST_CONTEXT Context, _In_ PPH_MODULE_ITEM ModuleItem, @@ -111,6 +154,11 @@ VOID PhUpdateModuleNode( _In_ PPH_MODULE_NODE ModuleNode ); +VOID PhExpandAllModuleNodes( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ BOOLEAN Expand + ); + VOID PhTickModuleNodes( _In_ PPH_MODULE_LIST_CONTEXT Context ); diff --git a/ProcessHacker/include/modprv.h b/ProcessHacker/include/modprv.h index b08bc9c6697f..1779bcd827e0 100644 --- a/ProcessHacker/include/modprv.h +++ b/ProcessHacker/include/modprv.h @@ -8,6 +8,8 @@ extern PPH_OBJECT_TYPE PhModuleItemType; typedef struct _PH_MODULE_ITEM { PVOID BaseAddress; + PVOID ParentBaseAddress; + PVOID EntryPoint; ULONG Size; ULONG Flags; ULONG Type; @@ -17,10 +19,16 @@ typedef struct _PH_MODULE_ITEM PPH_STRING FileName; PH_IMAGE_VERSION_INFO VersionInfo; - WCHAR BaseAddressString[PH_PTR_STR_LEN_1]; - - BOOLEAN IsFirst; - BOOLEAN JustProcessed; + union + { + BOOLEAN StateFlags; + struct + { + BOOLEAN JustProcessed : 1; + BOOLEAN IsFirst : 1; + BOOLEAN Spare : 6; + }; + }; enum _VERIFY_RESULT VerifyResult; PPH_STRING VerifySignerName; @@ -30,9 +38,12 @@ typedef struct _PH_MODULE_ITEM USHORT ImageDllCharacteristics; LARGE_INTEGER LoadTime; - LARGE_INTEGER FileLastWriteTime; LARGE_INTEGER FileEndOfFile; + + WCHAR BaseAddressString[PH_PTR_STR_LEN_1]; + WCHAR ParentBaseAddressString[PH_PTR_STR_LEN_1]; + WCHAR EntryPointAddressString[PH_PTR_STR_LEN_1]; } PH_MODULE_ITEM, *PPH_MODULE_ITEM; typedef struct _PH_MODULE_PROVIDER @@ -46,16 +57,25 @@ typedef struct _PH_MODULE_PROVIDER HANDLE ProcessId; HANDLE ProcessHandle; + PPH_STRING ProcessFileName; PPH_STRING PackageFullName; SLIST_HEADER QueryListHead; NTSTATUS RunStatus; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN HaveFirst : 1; + BOOLEAN ControlFlowGuardEnabled : 1; + BOOLEAN IsSubsystemProcess : 1; + BOOLEAN Spare : 5; + }; + }; } PH_MODULE_PROVIDER, *PPH_MODULE_PROVIDER; // end_phapppub -BOOLEAN PhModuleProviderInitialization( - VOID - ); - PPH_MODULE_PROVIDER PhCreateModuleProvider( _In_ HANDLE ProcessId ); diff --git a/ProcessHacker/include/netlist.h b/ProcessHacker/include/netlist.h index 1268df1a3d34..77e9537b3f46 100644 --- a/ProcessHacker/include/netlist.h +++ b/ProcessHacker/include/netlist.h @@ -30,7 +30,7 @@ typedef struct _PH_NETWORK_NODE PH_STRINGREF TextCache[PHNETLC_MAXIMUM]; - LONG UniqueId; + ULONG64 UniqueId; PPH_STRING ProcessNameText; PPH_STRING TimeStampText; diff --git a/ProcessHacker/include/netprv.h b/ProcessHacker/include/netprv.h index beaecea2b4a1..76f8ed87efeb 100644 --- a/ProcessHacker/include/netprv.h +++ b/ProcessHacker/include/netprv.h @@ -2,11 +2,6 @@ #define PH_NETPRV_H extern PPH_OBJECT_TYPE PhNetworkItemType; -PHAPPAPI extern PH_CALLBACK PhNetworkItemAddedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhNetworkItemModifiedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhNetworkItemRemovedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhNetworkItemsUpdatedEvent; // phapppub - extern BOOLEAN PhEnableNetworkProviderResolve; // begin_phapppub @@ -25,17 +20,32 @@ typedef struct _PH_NETWORK_ITEM BOOLEAN ProcessIconValid; PPH_STRING OwnerName; - BOOLEAN JustResolved; + ULONG JustResolved; - WCHAR LocalAddressString[65]; + WCHAR LocalAddressString[INET6_ADDRSTRLEN]; WCHAR LocalPortString[PH_INT32_STR_LEN_1]; - WCHAR RemoteAddressString[65]; + WCHAR RemoteAddressString[INET6_ADDRSTRLEN]; WCHAR RemotePortString[PH_INT32_STR_LEN_1]; PPH_STRING LocalHostString; PPH_STRING RemoteHostString; LARGE_INTEGER CreateTime; ULONGLONG OwnerInfo[PH_NETWORK_OWNER_INFO_SIZE]; + ULONG LocalScopeId; + ULONG RemoteScopeId; + + union + { + ULONG Flags; + struct + { + ULONG UnknownProcess : 1; + ULONG SubsystemProcess : 1; + ULONG Spare : 30; + }; + }; + + PPH_PROCESS_ITEM ProcessItem; } PH_NETWORK_ITEM, *PPH_NETWORK_ITEM; // end_phapppub @@ -59,9 +69,9 @@ PhReferenceNetworkItem( ); // end_phapppub -PPH_STRING PhGetHostNameFromAddress( - _In_ PPH_IP_ADDRESS Address - ); +//PPH_STRING PhGetHostNameFromAddress( +// _In_ PPH_IP_ADDRESS Address +// ); VOID PhNetworkProviderUpdate( _In_ PVOID Object diff --git a/ProcessHacker/include/notifico.h b/ProcessHacker/include/notifico.h index fe0c17781497..639723e8b7da 100644 --- a/ProcessHacker/include/notifico.h +++ b/ProcessHacker/include/notifico.h @@ -1,171 +1,206 @@ -#ifndef PH_NOTIFICO_H -#define PH_NOTIFICO_H - -#define PH_ICON_MINIMUM 0x1 -#define PH_ICON_CPU_HISTORY 0x1 -#define PH_ICON_IO_HISTORY 0x2 -#define PH_ICON_COMMIT_HISTORY 0x4 -#define PH_ICON_PHYSICAL_HISTORY 0x8 -#define PH_ICON_CPU_USAGE 0x10 -#define PH_ICON_DEFAULT_MAXIMUM 0x20 -#define PH_ICON_DEFAULT_ALL 0x1f - -#define PH_ICON_LIMIT 0x80000000 -#define PH_ICON_ALL 0xffffffff - -// begin_phapppub -typedef VOID (NTAPI *PPH_NF_UPDATE_REGISTERED_ICON)( - _In_ struct _PH_NF_ICON *Icon - ); - -typedef VOID (NTAPI *PPH_NF_BEGIN_BITMAP)( - _Out_ PULONG Width, - _Out_ PULONG Height, - _Out_ HBITMAP *Bitmap, - _Out_opt_ PVOID *Bits, - _Out_ HDC *Hdc, - _Out_ HBITMAP *OldBitmap - ); - -typedef struct _PH_NF_POINTERS -{ - PPH_NF_UPDATE_REGISTERED_ICON UpdateRegisteredIcon; - PPH_NF_BEGIN_BITMAP BeginBitmap; -} PH_NF_POINTERS, *PPH_NF_POINTERS; - -#define PH_NF_UPDATE_IS_BITMAP 0x1 -#define PH_NF_UPDATE_DESTROY_RESOURCE 0x2 - -typedef VOID (NTAPI *PPH_NF_ICON_UPDATE_CALLBACK)( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ); - -typedef BOOLEAN (NTAPI *PPH_NF_ICON_MESSAGE_CALLBACK)( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ); - -// Special messages -// The message type is stored in LOWORD(LParam), and the message data is in WParam. - -#define PH_NF_MSG_SHOWMINIINFOSECTION (WM_APP + 1) - -typedef struct _PH_NF_MSG_SHOWMINIINFOSECTION_DATA -{ - PWSTR SectionName; // NULL to leave unchanged -} PH_NF_MSG_SHOWMINIINFOSECTION_DATA, *PPH_NF_MSG_SHOWMINIINFOSECTION_DATA; - -// Structures and internal functions - -#define PH_NF_ICON_UNAVAILABLE 0x1 -#define PH_NF_ICON_SHOW_MINIINFO 0x2 -// end_phapppub - -// begin_phapppub -typedef struct _PH_NF_ICON -{ - // Public - - struct _PH_PLUGIN *Plugin; - ULONG SubId; - PVOID Context; - PPH_NF_POINTERS Pointers; -// end_phapppub - - // Private - - PWSTR Text; - ULONG Flags; - ULONG IconId; - PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback; - PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback; -// begin_phapppub -} PH_NF_ICON, *PPH_NF_ICON; -// end_phapppub - -VOID PhNfLoadStage1( - VOID - ); - -VOID PhNfLoadStage2( - VOID - ); - -VOID PhNfSaveSettings( - VOID - ); - -VOID PhNfUninitialization( - VOID - ); - -VOID PhNfForwardMessage( - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ); - -ULONG PhNfGetMaximumIconId( - VOID - ); - -ULONG PhNfTestIconMask( - _In_ ULONG Id - ); - -VOID PhNfSetVisibleIcon( - _In_ ULONG Id, - _In_ BOOLEAN Visible - ); - -BOOLEAN PhNfShowBalloonTip( - _In_opt_ ULONG Id, - _In_ PWSTR Title, - _In_ PWSTR Text, - _In_ ULONG Timeout, - _In_ ULONG Flags - ); - -HICON PhNfBitmapToIcon( - _In_ HBITMAP Bitmap - ); - -PPH_NF_ICON PhNfRegisterIcon( - _In_ struct _PH_PLUGIN *Plugin, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_ PWSTR Text, - _In_ ULONG Flags, - _In_opt_ PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback, - _In_opt_ PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback - ); - -PPH_NF_ICON PhNfGetIconById( - _In_ ULONG Id - ); - -PPH_NF_ICON PhNfFindIcon( - _In_ PPH_STRINGREF PluginName, - _In_ ULONG SubId - ); - -VOID PhNfNotifyMiniInfoPinned( - _In_ BOOLEAN Pinned - ); - -// begin_phapppub -// Public registration data - -typedef struct _PH_NF_ICON_REGISTRATION_DATA -{ - PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback; - PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback; -} PH_NF_ICON_REGISTRATION_DATA, *PPH_NF_ICON_REGISTRATION_DATA; -// end_phapppub - -#endif +#ifndef PH_NOTIFICO_H +#define PH_NOTIFICO_H + +extern PPH_LIST PhTrayIconItemList; + +typedef enum _PH_TRAY_ICON_ID +{ + PH_TRAY_ICON_ID_NONE, + PH_TRAY_ICON_ID_CPU_USAGE, + PH_TRAY_ICON_ID_CPU_HISTORY, + PH_TRAY_ICON_ID_IO_HISTORY, + PH_TRAY_ICON_ID_COMMIT_HISTORY, + PH_TRAY_ICON_ID_PHYSICAL_HISTORY, + PH_TRAY_ICON_ID_CPU_TEXT, + PH_TRAY_ICON_ID_IO_TEXT, + PH_TRAY_ICON_ID_COMMIT_TEXT, + PH_TRAY_ICON_ID_PHYSICAL_TEXT, + PH_TRAY_ICON_ID_MAXIMUM +} PH_TRAY_ICON_ID; + +typedef enum _PH_TRAY_ICON_GUID +{ + PH_TRAY_ICON_GUID_CPU_USAGE, + PH_TRAY_ICON_GUID_CPU_HISTORY, + PH_TRAY_ICON_GUID_IO_HISTORY, + PH_TRAY_ICON_GUID_COMMIT_HISTORY, + PH_TRAY_ICON_GUID_PHYSICAL_HISTORY, + PH_TRAY_ICON_GUID_CPU_TEXT, + PH_TRAY_ICON_GUID_IO_TEXT, + PH_TRAY_ICON_GUID_COMMIT_TEXT, + PH_TRAY_ICON_GUID_PHYSICAL_TEXT, + PH_TRAY_ICON_GUID_MAXIMUM +} PH_TRAY_ICON_GUID; + +#define PH_TRAY_ICON_ID_PLUGIN 0x80 + +#define PH_ICON_LIMIT 0x80000000 +#define PH_ICON_ALL 0xffffffff + +// begin_phapppub +typedef VOID (NTAPI *PPH_NF_UPDATE_REGISTERED_ICON)( + _In_ struct _PH_NF_ICON *Icon + ); + +typedef VOID (NTAPI *PPH_NF_BEGIN_BITMAP)( + _Out_ PULONG Width, + _Out_ PULONG Height, + _Out_ HBITMAP *Bitmap, + _Out_opt_ PVOID *Bits, + _Out_ HDC *Hdc, + _Out_ HBITMAP *OldBitmap + ); + +typedef struct _PH_NF_POINTERS +{ + PPH_NF_BEGIN_BITMAP BeginBitmap; +} PH_NF_POINTERS, *PPH_NF_POINTERS; + +#define PH_NF_UPDATE_IS_BITMAP 0x1 +#define PH_NF_UPDATE_DESTROY_RESOURCE 0x2 + +typedef VOID (NTAPI *PPH_NF_ICON_UPDATE_CALLBACK)( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +typedef BOOLEAN (NTAPI *PPH_NF_ICON_MESSAGE_CALLBACK)( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ); + +// Special messages +// The message type is stored in LOWORD(LParam), and the message data is in WParam. + +#define PH_NF_MSG_SHOWMINIINFOSECTION (WM_APP + 1) + +typedef struct _PH_NF_MSG_SHOWMINIINFOSECTION_DATA +{ + PWSTR SectionName; // NULL to leave unchanged +} PH_NF_MSG_SHOWMINIINFOSECTION_DATA, *PPH_NF_MSG_SHOWMINIINFOSECTION_DATA; + +// Structures and internal functions + +#define PH_NF_ICON_ENABLED 0x1 +#define PH_NF_ICON_UNAVAILABLE 0x2 +#define PH_NF_ICON_NOSHOW_MINIINFO 0x4 +// end_phapppub + +// begin_phapppub +typedef struct _PH_NF_ICON +{ + // Public + + struct _PH_PLUGIN *Plugin; + ULONG SubId; + PVOID Context; + PPH_NF_POINTERS Pointers; +// end_phapppub + + // Private + + PWSTR Text; + ULONG Flags; + ULONG IconId; + GUID IconGuid; + PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback; + PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback; + + PPH_STRING TextCache; +// begin_phapppub +} PH_NF_ICON, *PPH_NF_ICON; +// end_phapppub + +VOID PhNfLoadStage1( + VOID + ); + +VOID PhNfLoadStage2( + VOID + ); + +VOID PhNfSaveSettings( + VOID + ); + +VOID PhNfUninitialization( + VOID + ); + +VOID PhNfForwardMessage( + _In_ HWND WindowHandle, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ); + +VOID PhNfSetVisibleIcon( + _In_ PPH_NF_ICON Icon, + _In_ BOOLEAN Visible + ); + +BOOLEAN PhNfShowBalloonTip( + _In_ PWSTR Title, + _In_ PWSTR Text, + _In_ ULONG Timeout, + _In_ ULONG Flags + ); + +HICON PhNfBitmapToIcon( + _In_ HBITMAP Bitmap + ); + +struct _PH_NF_ICON *PhNfPluginRegisterIcon( + _In_ struct _PH_PLUGIN * Plugin, + _In_ ULONG SubId, + _In_ GUID Guid, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData + ); + +PPH_NF_ICON PhNfRegisterIcon( + _In_opt_ struct _PH_PLUGIN *Plugin, + _In_ ULONG Id, + _In_ GUID Guid, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_opt_ PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback, + _In_opt_ PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback + ); + +PPH_NF_ICON PhNfGetIconById( + _In_ ULONG Id + ); + +PPH_NF_ICON PhNfFindIcon( + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ); + +BOOLEAN PhNfIconsEnabled( + VOID + ); + +VOID PhNfNotifyMiniInfoPinned( + _In_ BOOLEAN Pinned + ); + +// begin_phapppub +// Public registration data + +typedef struct _PH_NF_ICON_REGISTRATION_DATA +{ + PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback; + PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback; +} PH_NF_ICON_REGISTRATION_DATA, *PPH_NF_ICON_REGISTRATION_DATA; +// end_phapppub + + +#endif diff --git a/ProcessHacker/include/notificop.h b/ProcessHacker/include/notificop.h index 5c512ec22400..5518f9b83a21 100644 --- a/ProcessHacker/include/notificop.h +++ b/ProcessHacker/include/notificop.h @@ -15,18 +15,18 @@ HICON PhNfpGetBlackIcon( ); BOOLEAN PhNfpAddNotifyIcon( - _In_ ULONG Id + _In_ PPH_NF_ICON Icon ); BOOLEAN PhNfpRemoveNotifyIcon( - _In_ ULONG Id + _In_ PPH_NF_ICON Icon ); BOOLEAN PhNfpModifyNotifyIcon( - _In_ ULONG Id, + _In_ PPH_NF_ICON Icon, _In_ ULONG Flags, _In_opt_ PPH_STRING Text, - _In_opt_ HICON Icon + _In_opt_ HICON IconHandle ); VOID PhNfpProcessesUpdatedHandler( @@ -57,24 +57,71 @@ VOID PhNfpBeginBitmap2( _Out_ HBITMAP *OldBitmap ); -VOID PhNfpUpdateIconCpuHistory( - VOID +VOID PhNfpCpuHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context ); - -VOID PhNfpUpdateIconIoHistory( - VOID +VOID PhNfpIoHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context ); - -VOID PhNfpUpdateIconCommitHistory( - VOID +VOID PhNfpCommitHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context ); - -VOID PhNfpUpdateIconPhysicalHistory( - VOID +VOID PhNfpPhysicalHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); +VOID PhNfpCpuUsageIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context ); -VOID PhNfpUpdateIconCpuUsage( - VOID +// Text icons + +VOID PhNfpCpuUsageTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); +VOID PhNfpIoUsageTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); +VOID PhNfpCommitTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); +VOID PhNfpPhysicalUsageTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context ); BOOLEAN PhNfpGetShowMiniInfoSectionData( @@ -90,7 +137,7 @@ VOID PhNfpIconClickActivateTimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, - _In_ DWORD dwTime + _In_ ULONG dwTime ); VOID PhNfpDisableHover( @@ -101,7 +148,7 @@ VOID PhNfpIconRestoreHoverTimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, - _In_ DWORD dwTime + _In_ ULONG dwTime ); #endif diff --git a/ProcessHacker/include/phapp.h b/ProcessHacker/include/phapp.h index 077f2e7b828a..ce37bc4f036a 100644 --- a/ProcessHacker/include/phapp.h +++ b/ProcessHacker/include/phapp.h @@ -1,689 +1,827 @@ -#ifndef PHAPP_H -#define PHAPP_H - -#define PHNT_VERSION PHNT_WIN7 - -#if !defined(_PHAPP_) -#define PHAPPAPI __declspec(dllimport) -#else -#define PHAPPAPI __declspec(dllexport) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../resource.h" -#include -#include - -// main - -typedef struct _PH_STARTUP_PARAMETERS -{ - union - { - struct - { - ULONG NoSettings : 1; - ULONG ShowVisible : 1; - ULONG ShowHidden : 1; - ULONG CommandMode : 1; - ULONG NoKph : 1; - ULONG InstallKph : 1; - ULONG UninstallKph : 1; - ULONG Debug : 1; - ULONG ShowOptions : 1; - ULONG PhSvc : 1; - ULONG NoPlugins : 1; - ULONG NewInstance : 1; - ULONG Elevate : 1; - ULONG Silent : 1; - ULONG Help : 1; - ULONG Spare : 17; - }; - ULONG Flags; - }; - - PPH_STRING SettingsFileName; - - HWND WindowHandle; - POINT Point; - - PPH_STRING CommandType; - PPH_STRING CommandObject; - PPH_STRING CommandAction; - PPH_STRING CommandValue; - - PPH_STRING RunAsServiceMode; - - ULONG SelectPid; - ULONG PriorityClass; - - PPH_LIST PluginParameters; - PPH_STRING SelectTab; - PPH_STRING SysInfo; -} PH_STARTUP_PARAMETERS, *PPH_STARTUP_PARAMETERS; - -extern PPH_STRING PhApplicationDirectory; -extern PPH_STRING PhApplicationFileName; -PHAPPAPI extern HFONT PhApplicationFont; // phapppub -extern PPH_STRING PhCurrentUserName; -extern HINSTANCE PhInstanceHandle; -extern PPH_STRING PhLocalSystemName; -extern BOOLEAN PhPluginsEnabled; -extern PPH_STRING PhSettingsFileName; -extern PH_INTEGER_PAIR PhSmallIconSize; -extern PH_INTEGER_PAIR PhLargeIconSize; -extern PH_STARTUP_PARAMETERS PhStartupParameters; - -extern PH_PROVIDER_THREAD PhPrimaryProviderThread; -extern PH_PROVIDER_THREAD PhSecondaryProviderThread; - -#define PH_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) - -// begin_phapppub -PHAPPAPI -VOID -NTAPI -PhRegisterDialog( - _In_ HWND DialogWindowHandle - ); - -PHAPPAPI -VOID -NTAPI -PhUnregisterDialog( - _In_ HWND DialogWindowHandle - ); - -typedef BOOLEAN (NTAPI *PPH_MESSAGE_LOOP_FILTER)( - _In_ PMSG Message, - _In_ PVOID Context - ); - -typedef struct _PH_MESSAGE_LOOP_FILTER_ENTRY -{ - PPH_MESSAGE_LOOP_FILTER Filter; - PVOID Context; -} PH_MESSAGE_LOOP_FILTER_ENTRY, *PPH_MESSAGE_LOOP_FILTER_ENTRY; - -PHAPPAPI -struct _PH_MESSAGE_LOOP_FILTER_ENTRY * -NTAPI -PhRegisterMessageLoopFilter( - _In_ PPH_MESSAGE_LOOP_FILTER Filter, - _In_opt_ PVOID Context - ); - -PHAPPAPI -VOID -NTAPI -PhUnregisterMessageLoopFilter( - _In_ struct _PH_MESSAGE_LOOP_FILTER_ENTRY *FilterEntry - ); -// end_phapppub - -VOID PhInitializeFont( - _In_ HWND hWnd - ); - -// plugin - -extern PH_AVL_TREE PhPluginsByName; - -VOID PhPluginsInitialization( - VOID - ); - -BOOLEAN PhIsPluginDisabled( - _In_ PPH_STRINGREF BaseName - ); - -VOID PhSetPluginDisabled( - _In_ PPH_STRINGREF BaseName, - _In_ BOOLEAN Disable - ); - -VOID PhLoadPlugins( - VOID - ); - -VOID PhUnloadPlugins( - VOID - ); - -struct _PH_PLUGIN *PhFindPlugin2( - _In_ PPH_STRINGREF Name - ); - -// log - -#define PH_LOG_ENTRY_PROCESS_FIRST 1 -#define PH_LOG_ENTRY_PROCESS_CREATE 1 -#define PH_LOG_ENTRY_PROCESS_DELETE 2 -#define PH_LOG_ENTRY_PROCESS_LAST 2 - -#define PH_LOG_ENTRY_SERVICE_FIRST 3 -#define PH_LOG_ENTRY_SERVICE_CREATE 3 -#define PH_LOG_ENTRY_SERVICE_DELETE 4 -#define PH_LOG_ENTRY_SERVICE_START 5 -#define PH_LOG_ENTRY_SERVICE_STOP 6 -#define PH_LOG_ENTRY_SERVICE_CONTINUE 7 -#define PH_LOG_ENTRY_SERVICE_PAUSE 8 -#define PH_LOG_ENTRY_SERVICE_LAST 8 - -#define PH_LOG_ENTRY_MESSAGE 9 // phapppub - -typedef struct _PH_LOG_ENTRY *PPH_LOG_ENTRY; // phapppub - -typedef struct _PH_LOG_ENTRY -{ - UCHAR Type; - UCHAR Reserved1; - USHORT Flags; - LARGE_INTEGER Time; - union - { - struct - { - HANDLE ProcessId; - PPH_STRING Name; - HANDLE ParentProcessId; - PPH_STRING ParentName; - NTSTATUS ExitStatus; - } Process; - struct - { - PPH_STRING Name; - PPH_STRING DisplayName; - } Service; - PPH_STRING Message; - }; - UCHAR Buffer[1]; -} PH_LOG_ENTRY, *PPH_LOG_ENTRY; - -extern PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; -PHAPPAPI extern PH_CALLBACK PhLoggedCallback; // phapppub - -VOID PhLogInitialization( - VOID - ); - -VOID PhClearLogEntries( - VOID - ); - -VOID PhLogProcessEntry( - _In_ UCHAR Type, - _In_ HANDLE ProcessId, - _In_opt_ HANDLE QueryHandle, - _In_ PPH_STRING Name, - _In_opt_ HANDLE ParentProcessId, - _In_opt_ PPH_STRING ParentName - ); - -VOID PhLogServiceEntry( - _In_ UCHAR Type, - _In_ PPH_STRING Name, - _In_ PPH_STRING DisplayName - ); - -// begin_phapppub -PHAPPAPI -VOID -NTAPI -PhLogMessageEntry( - _In_ UCHAR Type, - _In_ PPH_STRING Message - ); - -PHAPPAPI -PPH_STRING -NTAPI -PhFormatLogEntry( - _In_ PPH_LOG_ENTRY Entry - ); -// end_phapppub - -// dbgcon - -VOID PhShowDebugConsole( - VOID - ); - -// itemtips - -PPH_STRING PhGetProcessTooltipText( - _In_ PPH_PROCESS_ITEM Process, - _Out_opt_ PULONG ValidToTickCount - ); - -PPH_STRING PhGetServiceTooltipText( - _In_ PPH_SERVICE_ITEM Service - ); - -// cmdmode - -NTSTATUS PhCommandModeStart( - VOID - ); - -// anawait - -VOID PhUiAnalyzeWaitThread( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId, - _In_ PPH_SYMBOL_PROVIDER SymbolProvider - ); - -// mdump - -BOOLEAN PhUiCreateDumpFileProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ); - -// about - -VOID PhShowAboutDialog( - _In_ HWND ParentWindowHandle - ); - -PPH_STRING PhGetDiagnosticsString( - VOID - ); - -// affinity - -VOID PhShowProcessAffinityDialog( - _In_ HWND ParentWindowHandle, - _In_opt_ PPH_PROCESS_ITEM ProcessItem, - _In_opt_ PPH_THREAD_ITEM ThreadItem - ); - -// begin_phapppub -PHAPPAPI -BOOLEAN -NTAPI -PhShowProcessAffinityDialog2( - _In_ HWND ParentWindowHandle, - _In_ ULONG_PTR AffinityMask, - _Out_ PULONG_PTR NewAffinityMask - ); -// end_phapppub - -// chcol - -#define PH_CONTROL_TYPE_TREE_NEW 1 - -VOID PhShowChooseColumnsDialog( - _In_ HWND ParentWindowHandle, - _In_ HWND ControlHandle, - _In_ ULONG Type - ); - -// chdlg - -// begin_phapppub -#define PH_CHOICE_DIALOG_SAVED_CHOICES 10 - -#define PH_CHOICE_DIALOG_CHOICE 0x0 -#define PH_CHOICE_DIALOG_USER_CHOICE 0x1 -#define PH_CHOICE_DIALOG_PASSWORD 0x2 -#define PH_CHOICE_DIALOG_TYPE_MASK 0x3 - -PHAPPAPI -BOOLEAN -NTAPI -PhaChoiceDialog( - _In_ HWND ParentWindowHandle, - _In_ PWSTR Title, - _In_ PWSTR Message, - _In_opt_ PWSTR *Choices, - _In_opt_ ULONG NumberOfChoices, - _In_opt_ PWSTR Option, - _In_ ULONG Flags, - _Inout_ PPH_STRING *SelectedChoice, - _Inout_opt_ PBOOLEAN SelectedOption, - _In_opt_ PWSTR SavedChoicesSettingName - ); -// end_phapppub - -// chproc - -// begin_phapppub -PHAPPAPI -BOOLEAN -NTAPI -PhShowChooseProcessDialog( - _In_ HWND ParentWindowHandle, - _In_ PWSTR Message, - _Out_ PHANDLE ProcessId - ); -// end_phapppub - -// findobj - -VOID PhShowFindObjectsDialog( - VOID - ); - -// gdihndl - -VOID PhShowGdiHandlesDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -// hidnproc - -VOID PhShowHiddenProcessesDialog( - VOID - ); - -// hndlprp - -VOID PhShowHandleProperties( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId, - _In_ PPH_HANDLE_ITEM HandleItem - ); - -// hndlstat - -VOID PhShowHandleStatisticsDialog( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId - ); - -// infodlg - -VOID PhShowInformationDialog( - _In_ HWND ParentWindowHandle, - _In_ PWSTR String, - _Reserved_ ULONG Flags - ); - -// jobprp - -VOID PhShowJobProperties( - _In_ HWND ParentWindowHandle, - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ PWSTR Title - ); - -HPROPSHEETPAGE PhCreateJobPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ DLGPROC HookProc - ); - -// logwnd - -VOID PhShowLogDialog( - VOID - ); - -// memedit - -#define PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION 0x1 - -VOID PhShowMemoryEditorDialog( - _In_ HANDLE ProcessId, - _In_ PVOID BaseAddress, - _In_ SIZE_T RegionSize, - _In_ ULONG SelectOffset, - _In_ ULONG SelectLength, - _In_opt_ PPH_STRING Title, - _In_ ULONG Flags - ); - -// memlists - -VOID PhShowMemoryListsDialog( - _In_ HWND ParentWindowHandle, - _In_opt_ VOID (NTAPI *RegisterDialog)(HWND), - _In_opt_ VOID (NTAPI *UnregisterDialog)(HWND) - ); - -// memprot - -VOID PhShowMemoryProtectDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ PPH_MEMORY_ITEM MemoryItem - ); - -// memrslt - -VOID PhShowMemoryResultsDialog( - _In_ HANDLE ProcessId, - _In_ PPH_LIST Results - ); - -// memsrch - -VOID PhShowMemoryStringDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -// mtgndlg - -VOID PhShowProcessMitigationPolicyDialog( - _In_ HWND ParentWindowHandle, - _In_ struct _PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION *Information - ); - -// netstk - -VOID PhShowNetworkStackDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -// ntobjprp - -HPROPSHEETPAGE PhCreateEventPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ); - -HPROPSHEETPAGE PhCreateEventPairPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ); - -HPROPSHEETPAGE PhCreateMutantPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ); - -HPROPSHEETPAGE PhCreateSectionPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ); - -HPROPSHEETPAGE PhCreateSemaphorePage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ); - -HPROPSHEETPAGE PhCreateTimerPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ); - -// options - -VOID PhShowOptionsDialog( - _In_ HWND ParentWindowHandle - ); - -// pagfiles - -VOID PhShowPagefilesDialog( - _In_ HWND ParentWindowHandle - ); - -// plugman - -VOID PhShowPluginsDialog( - _In_ HWND ParentWindowHandle - ); - -// procrec - -// begin_phapppub -PHAPPAPI -VOID -NTAPI -PhShowProcessRecordDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_RECORD Record - ); -// end_phapppub - -// runas - -typedef struct _PH_RUNAS_SERVICE_PARAMETERS -{ - ULONG ProcessId; - PWSTR UserName; - PWSTR Password; - ULONG LogonType; - ULONG SessionId; - PWSTR CurrentDirectory; - PWSTR CommandLine; - PWSTR FileName; - PWSTR DesktopName; - BOOLEAN UseLinkedToken; - PWSTR ServiceName; -} PH_RUNAS_SERVICE_PARAMETERS, *PPH_RUNAS_SERVICE_PARAMETERS; - -VOID PhShowRunAsDialog( - _In_ HWND ParentWindowHandle, - _In_opt_ HANDLE ProcessId - ); - -NTSTATUS PhExecuteRunAsCommand( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ); - -// begin_phapppub -PHAPPAPI -NTSTATUS -NTAPI -PhExecuteRunAsCommand2( - _In_ HWND hWnd, - _In_ PWSTR Program, - _In_opt_ PWSTR UserName, - _In_opt_ PWSTR Password, - _In_opt_ ULONG LogonType, - _In_opt_ HANDLE ProcessIdWithToken, - _In_ ULONG SessionId, - _In_ PWSTR DesktopName, - _In_ BOOLEAN UseLinkedToken - ); -// end_phapppub - -NTSTATUS PhRunAsServiceStart( - _In_ PPH_STRING ServiceName - ); - -NTSTATUS PhInvokeRunAsService( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ); - -// sessmsg - -VOID PhShowSessionSendMessageDialog( - _In_ HWND ParentWindowHandle, - _In_ ULONG SessionId - ); - -// sessprp - -VOID PhShowSessionProperties( - _In_ HWND ParentWindowHandle, - _In_ ULONG SessionId - ); - -// sessshad - -VOID PhShowSessionShadowDialog( - _In_ HWND ParentWindowHandle, - _In_ ULONG SessionId - ); - -// srvcr - -VOID PhShowCreateServiceDialog( - _In_ HWND ParentWindowHandle - ); - -// srvctl - -// begin_phapppub -#define WM_PH_SET_LIST_VIEW_SETTINGS (WM_APP + 701) - -PHAPPAPI -HWND -NTAPI -PhCreateServiceListControl( - _In_ HWND ParentWindowHandle, - _In_ PPH_SERVICE_ITEM *Services, - _In_ ULONG NumberOfServices - ); -// end_phapppub - -// srvprp - -VOID PhShowServiceProperties( - _In_ HWND ParentWindowHandle, - _In_ PPH_SERVICE_ITEM ServiceItem - ); - -// thrdstk - -VOID PhShowThreadStackDialog( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId, - _In_ PPH_THREAD_PROVIDER ThreadProvider - ); - -// tokprp - -PPH_STRING PhGetGroupAttributesString( - _In_ ULONG Attributes - ); - -PWSTR PhGetPrivilegeAttributesString( - _In_ ULONG Attributes - ); - -VOID PhShowTokenProperties( - _In_ HWND ParentWindowHandle, - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ PWSTR Title - ); - -HPROPSHEETPAGE PhCreateTokenPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ DLGPROC HookProc - ); - -#endif +#ifndef PHAPP_H +#define PHAPP_H + +#if !defined(_PHAPP_) +#define PHAPPAPI __declspec(dllimport) +#else +#define PHAPPAPI __declspec(dllexport) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../resource.h" +#include +#include + +// main + +typedef struct _PH_STARTUP_PARAMETERS +{ + union + { + struct + { + ULONG NoSettings : 1; + ULONG ShowVisible : 1; + ULONG ShowHidden : 1; + ULONG CommandMode : 1; + ULONG NoKph : 1; + ULONG InstallKph : 1; + ULONG UninstallKph : 1; + ULONG Debug : 1; + ULONG ShowOptions : 1; + ULONG PhSvc : 1; + ULONG NoPlugins : 1; + ULONG NewInstance : 1; + ULONG Elevate : 1; + ULONG Silent : 1; + ULONG Help : 1; + ULONG Spare : 17; + }; + ULONG Flags; + }; + + PPH_STRING SettingsFileName; + + HWND WindowHandle; + POINT Point; + + PPH_STRING CommandType; + PPH_STRING CommandObject; + PPH_STRING CommandAction; + PPH_STRING CommandValue; + + PPH_STRING RunAsServiceMode; + + ULONG SelectPid; + ULONG PriorityClass; + + PPH_LIST PluginParameters; + PPH_STRING SelectTab; + PPH_STRING SysInfo; +} PH_STARTUP_PARAMETERS, *PPH_STARTUP_PARAMETERS; + +extern BOOLEAN PhPluginsEnabled; +extern PPH_STRING PhSettingsFileName; +extern PH_STARTUP_PARAMETERS PhStartupParameters; + +extern PH_PROVIDER_THREAD PhPrimaryProviderThread; +extern PH_PROVIDER_THREAD PhSecondaryProviderThread; + +#define PH_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) // phapppub + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhRegisterDialog( + _In_ HWND DialogWindowHandle + ); + +PHAPPAPI +VOID +NTAPI +PhUnregisterDialog( + _In_ HWND DialogWindowHandle + ); + +typedef BOOLEAN (NTAPI *PPH_MESSAGE_LOOP_FILTER)( + _In_ PMSG Message, + _In_ PVOID Context + ); + +typedef struct _PH_MESSAGE_LOOP_FILTER_ENTRY +{ + PPH_MESSAGE_LOOP_FILTER Filter; + PVOID Context; +} PH_MESSAGE_LOOP_FILTER_ENTRY, *PPH_MESSAGE_LOOP_FILTER_ENTRY; + +PHAPPAPI +PPH_MESSAGE_LOOP_FILTER_ENTRY +NTAPI +PhRegisterMessageLoopFilter( + _In_ PPH_MESSAGE_LOOP_FILTER Filter, + _In_opt_ PVOID Context + ); + +PHAPPAPI +VOID +NTAPI +PhUnregisterMessageLoopFilter( + _In_ PPH_MESSAGE_LOOP_FILTER_ENTRY FilterEntry + ); +// end_phapppub + +VOID PhInitializeFont( + VOID + ); + +// plugin + +extern PH_AVL_TREE PhPluginsByName; + +VOID PhInitializeCallbacks( + VOID + ); + +BOOLEAN PhIsPluginDisabled( + _In_ PPH_STRINGREF BaseName + ); + +VOID PhSetPluginDisabled( + _In_ PPH_STRINGREF BaseName, + _In_ BOOLEAN Disable + ); + +VOID PhLoadPlugins( + VOID + ); + +VOID PhUnloadPlugins( + VOID + ); + +struct _PH_PLUGIN *PhFindPlugin2( + _In_ PPH_STRINGREF Name + ); + +// log + +#define PH_LOG_ENTRY_PROCESS_FIRST 1 +#define PH_LOG_ENTRY_PROCESS_CREATE 1 +#define PH_LOG_ENTRY_PROCESS_DELETE 2 +#define PH_LOG_ENTRY_PROCESS_LAST 2 + +#define PH_LOG_ENTRY_SERVICE_FIRST 3 +#define PH_LOG_ENTRY_SERVICE_CREATE 3 +#define PH_LOG_ENTRY_SERVICE_DELETE 4 +#define PH_LOG_ENTRY_SERVICE_START 5 +#define PH_LOG_ENTRY_SERVICE_STOP 6 +#define PH_LOG_ENTRY_SERVICE_CONTINUE 7 +#define PH_LOG_ENTRY_SERVICE_PAUSE 8 +#define PH_LOG_ENTRY_SERVICE_LAST 8 + +#define PH_LOG_ENTRY_MESSAGE 9 // phapppub + +typedef struct _PH_LOG_ENTRY *PPH_LOG_ENTRY; // phapppub + +typedef struct _PH_LOG_ENTRY +{ + UCHAR Type; + UCHAR Reserved1; + USHORT Flags; + LARGE_INTEGER Time; + union + { + struct + { + HANDLE ProcessId; + PPH_STRING Name; + HANDLE ParentProcessId; + PPH_STRING ParentName; + NTSTATUS ExitStatus; + } Process; + struct + { + PPH_STRING Name; + PPH_STRING DisplayName; + } Service; + PPH_STRING Message; + }; + UCHAR Buffer[1]; +} PH_LOG_ENTRY, *PPH_LOG_ENTRY; + +extern PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; + +VOID PhLogInitialization( + VOID + ); + +VOID PhClearLogEntries( + VOID + ); + +VOID PhLogProcessEntry( + _In_ UCHAR Type, + _In_ HANDLE ProcessId, + _In_opt_ HANDLE QueryHandle, + _In_ PPH_STRING Name, + _In_opt_ HANDLE ParentProcessId, + _In_opt_ PPH_STRING ParentName + ); + +VOID PhLogServiceEntry( + _In_ UCHAR Type, + _In_ PPH_STRING Name, + _In_ PPH_STRING DisplayName + ); + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhLogMessageEntry( + _In_ UCHAR Type, + _In_ PPH_STRING Message + ); + +PHAPPAPI +PPH_STRING +NTAPI +PhFormatLogEntry( + _In_ PPH_LOG_ENTRY Entry + ); +// end_phapppub + +// dbgcon + +VOID PhShowDebugConsole( + VOID + ); + +// itemtips + +PPH_STRING PhGetProcessTooltipText( + _In_ PPH_PROCESS_ITEM Process, + _Out_opt_ PULONG64 ValidToTickCount + ); + +PPH_STRING PhGetServiceTooltipText( + _In_ PPH_SERVICE_ITEM Service + ); + +// cmdmode + +NTSTATUS PhCommandModeStart( + VOID + ); + +// anawait + +VOID PhUiAnalyzeWaitThread( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId, + _In_ PPH_SYMBOL_PROVIDER SymbolProvider + ); + +// mdump + +BOOLEAN PhUiCreateDumpFileProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ); + +// about + +VOID PhShowAboutDialog( + VOID + ); + +PPH_STRING PhGetDiagnosticsString( + VOID + ); + +// affinity + +VOID PhShowProcessAffinityDialog( + _In_ HWND ParentWindowHandle, + _In_opt_ PPH_PROCESS_ITEM ProcessItem, + _In_opt_ PPH_THREAD_ITEM ThreadItem + ); + +// begin_phapppub +PHAPPAPI +BOOLEAN +NTAPI +PhShowProcessAffinityDialog2( + _In_ HWND ParentWindowHandle, + _In_ ULONG_PTR AffinityMask, + _Out_ PULONG_PTR NewAffinityMask + ); +// end_phapppub + +// chcol + +#define PH_CONTROL_TYPE_TREE_NEW 1 + +VOID PhShowChooseColumnsDialog( + _In_ HWND ParentWindowHandle, + _In_ HWND ControlHandle, + _In_ ULONG Type + ); + +// chdlg + +// begin_phapppub +#define PH_CHOICE_DIALOG_SAVED_CHOICES 10 + +#define PH_CHOICE_DIALOG_CHOICE 0x0 +#define PH_CHOICE_DIALOG_USER_CHOICE 0x1 +#define PH_CHOICE_DIALOG_PASSWORD 0x2 +#define PH_CHOICE_DIALOG_TYPE_MASK 0x3 + +PHAPPAPI +BOOLEAN +NTAPI +PhaChoiceDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR Title, + _In_ PWSTR Message, + _In_opt_ PWSTR *Choices, + _In_opt_ ULONG NumberOfChoices, + _In_opt_ PWSTR Option, + _In_ ULONG Flags, + _Inout_ PPH_STRING *SelectedChoice, + _Inout_opt_ PBOOLEAN SelectedOption, + _In_opt_ PWSTR SavedChoicesSettingName + ); +// end_phapppub + +// chproc + +// begin_phapppub +PHAPPAPI +BOOLEAN +NTAPI +PhShowChooseProcessDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR Message, + _Out_ PHANDLE ProcessId + ); +// end_phapppub + +// findobj + +VOID PhShowFindObjectsDialog( + VOID + ); + +// gdihndl + +VOID PhShowGdiHandlesDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +// hidnproc + +VOID PhShowHiddenProcessesDialog( + VOID + ); + +// hndlprp + +VOID PhShowHandleProperties( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId, + _In_ PPH_HANDLE_ITEM HandleItem + ); + +// hndlstat + +VOID PhShowHandleStatisticsDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId + ); + +// infodlg + +VOID PhShowInformationDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR String, + _Reserved_ ULONG Flags + ); + +// jobprp + +VOID PhShowJobProperties( + _In_ HWND ParentWindowHandle, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context, + _In_opt_ PWSTR Title + ); + +HPROPSHEETPAGE PhCreateJobPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context, + _In_opt_ DLGPROC HookProc + ); + +// logwnd + +VOID PhShowLogDialog( + VOID + ); + +// memedit + +#define PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION 0x1 + +VOID PhShowMemoryEditorDialog( + _In_ HWND OwnerWindow, + _In_ HANDLE ProcessId, + _In_ PVOID BaseAddress, + _In_ SIZE_T RegionSize, + _In_ ULONG SelectOffset, + _In_ ULONG SelectLength, + _In_opt_ PPH_STRING Title, + _In_ ULONG Flags + ); + +// memlists + +VOID PhShowMemoryListsDialog( + _In_ HWND ParentWindowHandle, + _In_opt_ VOID (NTAPI *RegisterDialog)(HWND), + _In_opt_ VOID (NTAPI *UnregisterDialog)(HWND) + ); + +// memprot + +VOID PhShowMemoryProtectDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ PPH_MEMORY_ITEM MemoryItem + ); + +// memrslt + +VOID PhShowMemoryResultsDialog( + _In_ HANDLE ProcessId, + _In_ PPH_LIST Results + ); + +// memsrch + +VOID PhShowMemoryStringDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +// mtgndlg + +VOID PhShowProcessMitigationPolicyDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId + ); + +// netstk + +VOID PhShowNetworkStackDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +// ntobjprp + +HPROPSHEETPAGE PhCreateEventPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ); + +HPROPSHEETPAGE PhCreateEventPairPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ); + +HPROPSHEETPAGE PhCreateSemaphorePage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ); + +HPROPSHEETPAGE PhCreateTimerPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ); + +// options + +VOID PhShowOptionsDialog( + _In_ HWND ParentWindowHandle + ); + +// pagfiles + +VOID PhShowPagefilesDialog( + _In_ HWND ParentWindowHandle + ); + +// plugman + +VOID PhShowPluginsDialog( + _In_ HWND ParentWindowHandle + ); + +// procrec + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhShowProcessRecordDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_RECORD Record + ); +// end_phapppub + +// runas + +typedef struct _PH_RUNAS_SERVICE_PARAMETERS +{ + ULONG ProcessId; + PWSTR UserName; + PWSTR Password; + ULONG LogonType; + ULONG SessionId; + PWSTR CurrentDirectory; + PWSTR CommandLine; + PWSTR FileName; + PWSTR DesktopName; + BOOLEAN UseLinkedToken; + PWSTR ServiceName; + BOOLEAN CreateSuspendedProcess; +} PH_RUNAS_SERVICE_PARAMETERS, *PPH_RUNAS_SERVICE_PARAMETERS; + +VOID PhShowRunAsDialog( + _In_ HWND ParentWindowHandle, + _In_opt_ HANDLE ProcessId + ); + +// begin_phapppub +PHLIBAPI +BOOLEAN +NTAPI +PhShowRunFileDialog( + _In_ HWND ParentWindowHandle + ); +// end_phapppub + +NTSTATUS PhExecuteRunAsCommand( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ); + +// begin_phapppub +PHAPPAPI +NTSTATUS +NTAPI +PhExecuteRunAsCommand2( + _In_ HWND hWnd, + _In_ PWSTR Program, + _In_opt_ PWSTR UserName, + _In_opt_ PWSTR Password, + _In_opt_ ULONG LogonType, + _In_opt_ HANDLE ProcessIdWithToken, + _In_ ULONG SessionId, + _In_ PWSTR DesktopName, + _In_ BOOLEAN UseLinkedToken + ); +// end_phapppub + +PHAPPAPI +NTSTATUS +NTAPI +PhExecuteRunAsCommand3( + _In_ HWND hWnd, + _In_ PWSTR Program, + _In_opt_ PWSTR UserName, + _In_opt_ PWSTR Password, + _In_opt_ ULONG LogonType, + _In_opt_ HANDLE ProcessIdWithToken, + _In_ ULONG SessionId, + _In_ PWSTR DesktopName, + _In_ BOOLEAN UseLinkedToken, + _In_ BOOLEAN CreateSuspendedProcess + ); + +NTSTATUS PhRunAsServiceStart( + _In_ PPH_STRING ServiceName + ); + +NTSTATUS PhInvokeRunAsService( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ); + +// searchbox + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhCreateSearchControl( + _In_ HWND Parent, + _In_ HWND WindowHandle, + _In_opt_ PWSTR BannerText + ); + +PHAPPAPI +HBITMAP +NTAPI +PhLoadPngImageFromResource( + _In_ PVOID DllBase, + _In_ UINT Width, + _In_ UINT Height, + _In_ PCWSTR Name, + _In_ BOOLEAN RGBAImage + ); + +FORCEINLINE +HFONT +PhCreateFont( + _In_ PWSTR Name, + _In_ ULONG Size, + _In_ ULONG Weight + ) +{ + return CreateFont( + -(LONG)PhMultiplyDivide(Size, PhGlobalDpi, 72), + 0, + 0, + 0, + Weight, + FALSE, + FALSE, + FALSE, + ANSI_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH, + Name + ); +} + +FORCEINLINE +HFONT +PhCreateCommonFont( + _In_ LONG Size, + _In_ INT Weight, + _In_opt_ HWND hwnd + ) +{ + HFONT fontHandle; + LOGFONT logFont; + + if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) + return NULL; + + fontHandle = CreateFont( + -PhMultiplyDivideSigned(Size, PhGlobalDpi, 72), + 0, + 0, + 0, + Weight, + FALSE, + FALSE, + FALSE, + ANSI_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY | ANTIALIASED_QUALITY, + DEFAULT_PITCH, + logFont.lfFaceName + ); + + if (!fontHandle) + return NULL; + + if (hwnd) + SetWindowFont(hwnd, fontHandle, TRUE); + + return fontHandle; +} + +FORCEINLINE +HFONT +PhDuplicateFont( + _In_ HFONT Font + ) +{ + LOGFONT logFont; + + if (GetObject(Font, sizeof(LOGFONT), &logFont)) + return CreateFontIndirect(&logFont); + + return NULL; +} + +FORCEINLINE +HFONT +PhDuplicateFontWithNewWeight( + _In_ HFONT Font, + _In_ LONG NewWeight + ) +{ + LOGFONT logFont; + + if (GetObject(Font, sizeof(LOGFONT), &logFont)) + { + logFont.lfWeight = NewWeight; + return CreateFontIndirect(&logFont); + } + + return NULL; +} + +// end_phapppub + +// sessmsg + +VOID PhShowSessionSendMessageDialog( + _In_ HWND ParentWindowHandle, + _In_ ULONG SessionId + ); + +// sessprp + +VOID PhShowSessionProperties( + _In_ HWND ParentWindowHandle, + _In_ ULONG SessionId + ); + +// sessshad + +VOID PhShowSessionShadowDialog( + _In_ HWND ParentWindowHandle, + _In_ ULONG SessionId + ); + +// srvcr + +VOID PhShowCreateServiceDialog( + _In_ HWND ParentWindowHandle + ); + +// srvctl + +// begin_phapppub +#define WM_PH_SET_LIST_VIEW_SETTINGS (WM_APP + 701) + +PHAPPAPI +HWND +NTAPI +PhCreateServiceListControl( + _In_ HWND ParentWindowHandle, + _In_ PPH_SERVICE_ITEM *Services, + _In_ ULONG NumberOfServices + ); +// end_phapppub + +// srvprp + +VOID PhShowServiceProperties( + _In_ HWND ParentWindowHandle, + _In_ PPH_SERVICE_ITEM ServiceItem + ); + +// thrdstk + +VOID PhShowThreadStackDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId, + _In_ PPH_THREAD_PROVIDER ThreadProvider + ); + +// tokprp + +PPH_STRING PhGetGroupAttributesString( + _In_ ULONG Attributes, + _In_ BOOLEAN Restricted + ); + +PWSTR PhGetPrivilegeAttributesString( + _In_ ULONG Attributes + ); + +VOID PhShowTokenProperties( + _In_ HWND ParentWindowHandle, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_ HANDLE ProcessId, + _In_opt_ PVOID Context, + _In_opt_ PWSTR Title + ); + +HPROPSHEETPAGE PhCreateTokenPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_ HANDLE ProcessId, + _In_opt_ PVOID Context, + _In_opt_ DLGPROC HookProc + ); + +// prpggen + +PPH_STRING PhGetProcessItemProtectionText( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +#endif diff --git a/ProcessHacker/include/phappres.h b/ProcessHacker/include/phappres.h index e0ff77b3e913..d019dcd74080 100644 --- a/ProcessHacker/include/phappres.h +++ b/ProcessHacker/include/phappres.h @@ -1,34 +1,43 @@ -// Notes: -// * Do not use /* comments */ since ISPP is buggy and it will throw an error. - -#ifndef PH_PHAPPRES_H -#define PH_PHAPPRES_H - -#include "phapprev.h" - -#define PHAPP_VERSION_MAJOR 3 -#define PHAPP_VERSION_MINOR 0 -#define PHAPP_VERSION_BUILD 0 - -#if (PHAPP_VERSION_BUILD == 0) -#define TWO_DIGIT_VER 1 -#else -#define THREE_DIGIT_VER 1 -#endif - -#define DO_MAKE_STR(x) #x -#define MAKE_STR(x) DO_MAKE_STR(x) - -#ifndef ISPP_INVOKED - -#if defined(TWO_DIGIT_VER) -#define PHAPP_VERSION_STRING MAKE_STR(PHAPP_VERSION_MAJOR) "." MAKE_STR(PHAPP_VERSION_MINOR) ".0" "." MAKE_STR(PHAPP_VERSION_REVISION) -#elif defined(THREE_DIGIT_VER) -#define PHAPP_VERSION_STRING MAKE_STR(PHAPP_VERSION_MAJOR) "." MAKE_STR(PHAPP_VERSION_MINOR) "." MAKE_STR(PHAPP_VERSION_BUILD) "." MAKE_STR(PHAPP_VERSION_REVISION) -#endif - -#define PHAPP_VERSION_NUMBER PHAPP_VERSION_MAJOR,PHAPP_VERSION_MINOR,PHAPP_VERSION_BUILD,PHAPP_VERSION_REVISION - -#endif // ISPP_INVOKED - -#endif // PHAPPRES_H +#ifndef PH_PHAPPRES_H +#define PH_PHAPPRES_H + +#ifndef PHAPP_VERSION_MAJOR +#define PHAPP_VERSION_MAJOR 3 +#endif + +#ifndef PHAPP_VERSION_MINOR +#define PHAPP_VERSION_MINOR 0 +#endif + +#ifndef PHAPP_VERSION_BUILD +#define PHAPP_VERSION_BUILD 0 +#endif + +#ifndef PHAPP_VERSION_REVISION +#define PHAPP_VERSION_REVISION 0 +#endif + +#ifndef PHAPP_VERSION_COMMITHASH +#define PHAPP_VERSION_COMMITHASH "" +#endif + +#if (PHAPP_VERSION_BUILD == 0) +#define TWO_DIGIT_VER 1 +#else +#define THREE_DIGIT_VER 1 +#endif + +#define DO_MAKE_STR(x) #x +#define MAKE_STR(x) DO_MAKE_STR(x) + +#if defined(TWO_DIGIT_VER) +#define PHAPP_VERSION_STRING MAKE_STR(PHAPP_VERSION_MAJOR) "." MAKE_STR(PHAPP_VERSION_MINOR) ".0" "." MAKE_STR(PHAPP_VERSION_REVISION) +#elif defined(THREE_DIGIT_VER) +#define PHAPP_VERSION_STRING MAKE_STR(PHAPP_VERSION_MAJOR) "." MAKE_STR(PHAPP_VERSION_MINOR) "." MAKE_STR(PHAPP_VERSION_BUILD) "." MAKE_STR(PHAPP_VERSION_REVISION) +#endif + +#define PHAPP_VERSION_NUMBER PHAPP_VERSION_MAJOR,PHAPP_VERSION_MINOR,PHAPP_VERSION_BUILD,PHAPP_VERSION_REVISION + +#define PHAPP_VERSION_COMMIT MAKE_STR(PHAPP_VERSION_COMMITHASH) + +#endif // PHAPPRES_H diff --git a/ProcessHacker/include/phapprev.h b/ProcessHacker/include/phapprev.h deleted file mode 100644 index 7fb3bd0b5cb6..000000000000 --- a/ProcessHacker/include/phapprev.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION 0 - -#endif // PHAPPREV_H diff --git a/ProcessHacker/include/phapprev_in.h b/ProcessHacker/include/phapprev_in.h deleted file mode 100644 index 5f08adeb2006..000000000000 --- a/ProcessHacker/include/phapprev_in.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PHAPPREV_H -#define PHAPPREV_H - -#define PHAPP_VERSION_REVISION $COMMITS$ - -#endif // PHAPPREV_H diff --git a/ProcessHacker/include/phplug.h b/ProcessHacker/include/phplug.h index ab54b1c22be3..eeaff9264c90 100644 --- a/ProcessHacker/include/phplug.h +++ b/ProcessHacker/include/phplug.h @@ -1,675 +1,762 @@ -#ifndef PH_PHPLUG_H -#define PH_PHPLUG_H - -#include -#include -#include - -// begin_phapppub -// Callbacks - -typedef enum _PH_GENERAL_CALLBACK -{ - GeneralCallbackMainWindowShowing = 0, // INT ShowCommand [main thread] - GeneralCallbackProcessesUpdated = 1, // [main thread] - GeneralCallbackGetProcessHighlightingColor = 2, // PPH_PLUGIN_GET_HIGHLIGHTING_COLOR Data [main thread] - GeneralCallbackGetProcessTooltipText = 3, // PPH_PLUGIN_GET_TOOLTIP_TEXT Data [main thread] - GeneralCallbackProcessPropertiesInitializing = 4, // PPH_PLUGIN_PROCESS_PROPCONTEXT Data [properties thread] - GeneralCallbackMainMenuInitializing = 5, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] - GeneralCallbackNotifyEvent = 6, // PPH_PLUGIN_NOTIFY_EVENT Data [main thread] - GeneralCallbackServicePropertiesInitializing = 7, // PPH_PLUGIN_OBJECT_PROPERTIES Data [properties thread] - GeneralCallbackHandlePropertiesInitializing = 8, // PPH_PLUGIN_OBJECT_PROPERTIES Data [properties thread] - GeneralCallbackProcessMenuInitializing = 9, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] - GeneralCallbackServiceMenuInitializing = 10, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] - GeneralCallbackNetworkMenuInitializing = 11, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] - GeneralCallbackIconMenuInitializing = 12, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] - GeneralCallbackThreadMenuInitializing = 13, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] - GeneralCallbackModuleMenuInitializing = 14, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] - GeneralCallbackMemoryMenuInitializing = 15, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] - GeneralCallbackHandleMenuInitializing = 16, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] - GeneralCallbackProcessTreeNewInitializing = 17, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread] - GeneralCallbackServiceTreeNewInitializing = 18, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread] - GeneralCallbackNetworkTreeNewInitializing = 19, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread] - GeneralCallbackModuleTreeNewInitializing = 20, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackModuleTreeNewUninitializing = 21, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackThreadTreeNewInitializing = 22, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackThreadTreeNewUninitializing = 23, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackHandleTreeNewInitializing = 24, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackHandleTreeNewUninitializing = 25, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackThreadStackControl = 26, // PPH_PLUGIN_THREAD_STACK_CONTROL Data [properties thread] - GeneralCallbackSystemInformationInitializing = 27, // PPH_PLUGIN_SYSINFO_POINTERS Data [system information thread] - GeneralCallbackMainWindowTabChanged = 28, // INT NewIndex [main thread] - GeneralCallbackMemoryTreeNewInitializing = 29, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackMemoryTreeNewUninitializing = 30, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] - GeneralCallbackMemoryItemListControl = 31, // PPH_PLUGIN_MEMORY_ITEM_LIST_CONTROL Data [properties thread] - GeneralCallbackMiniInformationInitializing = 32, // PPH_PLUGIN_MINIINFO_POINTERS Data [main thread] - GeneralCallbackMiListSectionMenuInitializing = 33, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] - GeneralCallbackMaximum -} PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK; - -typedef enum _PH_PLUGIN_CALLBACK -{ - PluginCallbackLoad = 0, // PPH_LIST Parameters [main thread] // list of strings, might be NULL - PluginCallbackUnload = 1, // [main thread] - PluginCallbackShowOptions = 2, // HWND ParentWindowHandle [main thread] - PluginCallbackMenuItem = 3, // PPH_PLUGIN_MENU_ITEM MenuItem [main/properties thread] - PluginCallbackTreeNewMessage = 4, // PPH_PLUGIN_TREENEW_MESSAGE Message [main/properties thread] - PluginCallbackPhSvcRequest = 5, // PPH_PLUGIN_PHSVC_REQUEST Message [phsvc thread] - PluginCallbackMenuHook = 6, // PH_PLUGIN_MENU_HOOK_INFORMATION MenuHookInfo [menu thread] - PluginCallbackMaximum -} PH_PLUGIN_CALLBACK, *PPH_PLUGIN_CALLBACK; - -typedef struct _PH_PLUGIN_GET_HIGHLIGHTING_COLOR -{ - // Parameter is: - // PPH_PROCESS_ITEM for GeneralCallbackGetProcessHighlightingColor - - PVOID Parameter; - COLORREF BackColor; - BOOLEAN Handled; - BOOLEAN Cache; -} PH_PLUGIN_GET_HIGHLIGHTING_COLOR, *PPH_PLUGIN_GET_HIGHLIGHTING_COLOR; - -typedef struct _PH_PLUGIN_GET_TOOLTIP_TEXT -{ - // Parameter is: - // PPH_PROCESS_ITEM for GeneralCallbackGetProcessTooltipText - - PVOID Parameter; - PPH_STRING_BUILDER StringBuilder; - ULONG ValidForMs; -} PH_PLUGIN_GET_TOOLTIP_TEXT, *PPH_PLUGIN_GET_TOOLTIP_TEXT; - -typedef struct _PH_PLUGIN_PROCESS_PROPCONTEXT -{ - PPH_PROCESS_PROPCONTEXT PropContext; - PPH_PROCESS_ITEM ProcessItem; -} PH_PLUGIN_PROCESS_PROPCONTEXT, *PPH_PLUGIN_PROCESS_PROPCONTEXT; - -typedef struct _PH_PLUGIN_NOTIFY_EVENT -{ - // Parameter is: - // PPH_PROCESS_ITEM for Type = PH_NOTIFY_PROCESS_* - // PPH_SERVICE_ITEM for Type = PH_NOTIFY_SERVICE_* - - ULONG Type; - BOOLEAN Handled; - PVOID Parameter; -} PH_PLUGIN_NOTIFY_EVENT, *PPH_PLUGIN_NOTIFY_EVENT; - -typedef struct _PH_PLUGIN_OBJECT_PROPERTIES -{ - // Parameter is: - // PPH_SERVICE_ITEM for GeneralCallbackServicePropertiesInitializing - // PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT for GeneralCallbackHandlePropertiesInitializing - - PVOID Parameter; - ULONG NumberOfPages; - ULONG MaximumNumberOfPages; - HPROPSHEETPAGE *Pages; -} PH_PLUGIN_OBJECT_PROPERTIES, *PPH_PLUGIN_OBJECT_PROPERTIES; - -typedef struct _PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT -{ - HANDLE ProcessId; - PPH_HANDLE_ITEM HandleItem; -} PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT, *PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT; - -typedef struct _PH_EMENU_ITEM *PPH_EMENU_ITEM, *PPH_EMENU; - -#define PH_PLUGIN_MENU_DISALLOW_HOOKS 0x1 - -typedef struct _PH_PLUGIN_MENU_INFORMATION -{ - PPH_EMENU Menu; - HWND OwnerWindow; - - union - { - struct - { - PVOID Reserved[8]; // Reserve space for future expansion of this union - } DoNotUse; - struct - { - ULONG SubMenuIndex; - } MainMenu; - struct - { - PPH_PROCESS_ITEM *Processes; - ULONG NumberOfProcesses; - } Process; - struct - { - PPH_SERVICE_ITEM *Services; - ULONG NumberOfServices; - } Service; - struct - { - PPH_NETWORK_ITEM *NetworkItems; - ULONG NumberOfNetworkItems; - } Network; - struct - { - HANDLE ProcessId; - PPH_THREAD_ITEM *Threads; - ULONG NumberOfThreads; - } Thread; - struct - { - HANDLE ProcessId; - PPH_MODULE_ITEM *Modules; - ULONG NumberOfModules; - } Module; - struct - { - HANDLE ProcessId; - PPH_MEMORY_NODE *MemoryNodes; - ULONG NumberOfMemoryNodes; - } Memory; - struct - { - HANDLE ProcessId; - PPH_HANDLE_ITEM *Handles; - ULONG NumberOfHandles; - } Handle; - struct - { - PPH_STRINGREF SectionName; - PPH_PROCESS_GROUP ProcessGroup; - } MiListSection; - } u; - - ULONG Flags; - PPH_LIST PluginHookList; -} PH_PLUGIN_MENU_INFORMATION, *PPH_PLUGIN_MENU_INFORMATION; - -C_ASSERT(RTL_FIELD_SIZE(PH_PLUGIN_MENU_INFORMATION, u) == RTL_FIELD_SIZE(PH_PLUGIN_MENU_INFORMATION, u.DoNotUse)); - -typedef struct _PH_PLUGIN_MENU_HOOK_INFORMATION -{ - PPH_PLUGIN_MENU_INFORMATION MenuInfo; - PPH_EMENU SelectedItem; - PVOID Context; - BOOLEAN Handled; -} PH_PLUGIN_MENU_HOOK_INFORMATION, *PPH_PLUGIN_MENU_HOOK_INFORMATION; - -typedef struct _PH_PLUGIN_TREENEW_INFORMATION -{ - HWND TreeNewHandle; - PVOID CmData; - PVOID SystemContext; // e.g. PPH_THREADS_CONTEXT -} PH_PLUGIN_TREENEW_INFORMATION, *PPH_PLUGIN_TREENEW_INFORMATION; - -typedef enum _PH_PLUGIN_THREAD_STACK_CONTROL_TYPE -{ - PluginThreadStackInitializing, - PluginThreadStackUninitializing, - PluginThreadStackResolveSymbol, - PluginThreadStackGetTooltip, - PluginThreadStackWalkStack, - PluginThreadStackBeginDefaultWalkStack, - PluginThreadStackEndDefaultWalkStack, - PluginThreadStackMaximum -} PH_PLUGIN_THREAD_STACK_CONTROL_TYPE; - -typedef struct _PH_SYMBOL_PROVIDER *PPH_SYMBOL_PROVIDER; -typedef struct _PH_THREAD_STACK_FRAME *PPH_THREAD_STACK_FRAME; - -typedef BOOLEAN (NTAPI *PPH_PLUGIN_WALK_THREAD_STACK_CALLBACK)( - _In_ PPH_THREAD_STACK_FRAME StackFrame, - _In_opt_ PVOID Context - ); - -typedef struct _PH_PLUGIN_THREAD_STACK_CONTROL -{ - PH_PLUGIN_THREAD_STACK_CONTROL_TYPE Type; - PVOID UniqueKey; - - union - { - struct - { - HANDLE ProcessId; - HANDLE ThreadId; - HANDLE ThreadHandle; - PPH_SYMBOL_PROVIDER SymbolProvider; - BOOLEAN CustomWalk; - } Initializing; - struct - { - PPH_THREAD_STACK_FRAME StackFrame; - PPH_STRING Symbol; - } ResolveSymbol; - struct - { - PPH_THREAD_STACK_FRAME StackFrame; - PPH_STRING_BUILDER StringBuilder; - } GetTooltip; - struct - { - NTSTATUS Status; - HANDLE ThreadHandle; - HANDLE ProcessHandle; - PCLIENT_ID ClientId; - ULONG Flags; - PPH_PLUGIN_WALK_THREAD_STACK_CALLBACK Callback; - PVOID CallbackContext; - } WalkStack; - } u; -} PH_PLUGIN_THREAD_STACK_CONTROL, *PPH_PLUGIN_THREAD_STACK_CONTROL; - -typedef enum _PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE -{ - PluginMemoryItemListInitialized, - PluginMemoryItemListMaximum -} PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE; - -typedef struct _PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL -{ - PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE Type; - - union - { - struct - { - PPH_MEMORY_ITEM_LIST List; - } Initialized; - } u; -} PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL, *PPH_PLUGIN_MEMORY_ITEM_LIST_CONTROL; - -typedef PPH_SYSINFO_SECTION (NTAPI *PPH_SYSINFO_CREATE_SECTION)( - _In_ PPH_SYSINFO_SECTION Template - ); - -typedef PPH_SYSINFO_SECTION (NTAPI *PPH_SYSINFO_FIND_SECTION)( - _In_ PPH_STRINGREF Name - ); - -typedef VOID (NTAPI *PPH_SYSINFO_ENTER_SECTION_VIEW)( - _In_ PPH_SYSINFO_SECTION NewSection - ); - -typedef VOID (NTAPI *PPH_SYSINFO_RESTORE_SUMMARY_VIEW)( - VOID - ); - -typedef struct _PH_PLUGIN_SYSINFO_POINTERS -{ - HWND WindowHandle; - PPH_SYSINFO_CREATE_SECTION CreateSection; - PPH_SYSINFO_FIND_SECTION FindSection; - PPH_SYSINFO_ENTER_SECTION_VIEW EnterSectionView; - PPH_SYSINFO_RESTORE_SUMMARY_VIEW RestoreSummaryView; -} PH_PLUGIN_SYSINFO_POINTERS, *PPH_PLUGIN_SYSINFO_POINTERS; - -typedef PPH_MINIINFO_SECTION (NTAPI *PPH_MINIINFO_CREATE_SECTION)( - _In_ PPH_MINIINFO_SECTION Template - ); - -typedef PPH_MINIINFO_SECTION (NTAPI *PPH_MINIINFO_FIND_SECTION)( - _In_ PPH_STRINGREF Name - ); - -typedef PPH_MINIINFO_LIST_SECTION (NTAPI *PPH_MINIINFO_CREATE_LIST_SECTION)( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_LIST_SECTION Template - ); - -typedef struct _PH_PLUGIN_MINIINFO_POINTERS -{ - HWND WindowHandle; - PPH_MINIINFO_CREATE_SECTION CreateSection; - PPH_MINIINFO_FIND_SECTION FindSection; - PPH_MINIINFO_CREATE_LIST_SECTION CreateListSection; -} PH_PLUGIN_MINIINFO_POINTERS, *PPH_PLUGIN_MINIINFO_POINTERS; - -typedef struct _PH_PLUGIN_TREENEW_MESSAGE -{ - HWND TreeNewHandle; - PH_TREENEW_MESSAGE Message; - PVOID Parameter1; - PVOID Parameter2; - ULONG SubId; - PVOID Context; -} PH_PLUGIN_TREENEW_MESSAGE, *PPH_PLUGIN_TREENEW_MESSAGE; - -typedef LONG (NTAPI *PPH_PLUGIN_TREENEW_SORT_FUNCTION)( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ); - -typedef NTSTATUS (NTAPI *PPHSVC_SERVER_PROBE_BUFFER)( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ ULONG Alignment, - _In_ BOOLEAN AllowNull, - _Out_ PVOID *Pointer - ); - -typedef NTSTATUS (NTAPI *PPHSVC_SERVER_CAPTURE_BUFFER)( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PVOID *CapturedBuffer - ); - -typedef NTSTATUS (NTAPI *PPHSVC_SERVER_CAPTURE_STRING)( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PPH_STRING *CapturedString - ); - -typedef struct _PH_PLUGIN_PHSVC_REQUEST -{ - ULONG SubId; - NTSTATUS ReturnStatus; - PVOID InBuffer; - ULONG InLength; - PVOID OutBuffer; - ULONG OutLength; - - PPHSVC_SERVER_PROBE_BUFFER ProbeBuffer; - PPHSVC_SERVER_CAPTURE_BUFFER CaptureBuffer; - PPHSVC_SERVER_CAPTURE_STRING CaptureString; -} PH_PLUGIN_PHSVC_REQUEST, *PPH_PLUGIN_PHSVC_REQUEST; - -typedef VOID (NTAPI *PPHSVC_CLIENT_FREE_HEAP)( - _In_ PVOID Memory - ); - -typedef PVOID (NTAPI *PPHSVC_CLIENT_CREATE_STRING)( - _In_opt_ PVOID String, - _In_ SIZE_T Length, - _Out_ PPH_RELATIVE_STRINGREF StringRef - ); - -typedef struct _PH_PLUGIN_PHSVC_CLIENT -{ - HANDLE ServerProcessId; - PPHSVC_CLIENT_FREE_HEAP FreeHeap; - PPHSVC_CLIENT_CREATE_STRING CreateString; -} PH_PLUGIN_PHSVC_CLIENT, *PPH_PLUGIN_PHSVC_CLIENT; - -// Plugin structures - -typedef struct _PH_PLUGIN_INFORMATION -{ - PWSTR DisplayName; - PWSTR Author; - PWSTR Description; - PWSTR Url; - BOOLEAN HasOptions; - BOOLEAN Reserved1[3]; - PVOID Interface; -} PH_PLUGIN_INFORMATION, *PPH_PLUGIN_INFORMATION; - -#define PH_PLUGIN_FLAG_RESERVED 0x1 -// end_phapppub - -// begin_phapppub -typedef struct _PH_PLUGIN -{ - // Public - - PH_AVL_LINKS Links; - - PVOID Reserved; - PVOID DllBase; -// end_phapppub - - // Private - - PPH_STRING FileName; - ULONG Flags; - PH_STRINGREF Name; - PH_PLUGIN_INFORMATION Information; - - PH_CALLBACK Callbacks[PluginCallbackMaximum]; - PH_EM_APP_CONTEXT AppContext; -// begin_phapppub -} PH_PLUGIN, *PPH_PLUGIN; -// end_phapppub - -// begin_phapppub -// Plugin API - -PHAPPAPI -PPH_PLUGIN -NTAPI -PhRegisterPlugin( - _In_ PWSTR Name, - _In_ PVOID DllBase, - _Out_opt_ PPH_PLUGIN_INFORMATION *Information - ); - -PHAPPAPI -PPH_PLUGIN -NTAPI -PhFindPlugin( - _In_ PWSTR Name - ); - -PHAPPAPI -PPH_PLUGIN_INFORMATION -NTAPI -PhGetPluginInformation( - _In_ PPH_PLUGIN Plugin - ); - -PHAPPAPI -PPH_CALLBACK -NTAPI -PhGetPluginCallback( - _In_ PPH_PLUGIN Plugin, - _In_ PH_PLUGIN_CALLBACK Callback - ); - -PHAPPAPI -PPH_CALLBACK -NTAPI -PhGetGeneralCallback( - _In_ PH_GENERAL_CALLBACK Callback - ); - -PHAPPAPI -ULONG -NTAPI -PhPluginReserveIds( - _In_ ULONG Count - ); - -typedef VOID (NTAPI *PPH_PLUGIN_MENU_ITEM_DELETE_FUNCTION)( - _In_ struct _PH_PLUGIN_MENU_ITEM *MenuItem - ); - -typedef struct _PH_PLUGIN_MENU_ITEM -{ - PPH_PLUGIN Plugin; - ULONG Id; - ULONG Reserved1; - PVOID Context; - - HWND OwnerWindow; // valid only when the menu item is chosen - PVOID Reserved2; - PVOID Reserved3; - PPH_PLUGIN_MENU_ITEM_DELETE_FUNCTION DeleteFunction; // valid only for EMENU-based menu items -} PH_PLUGIN_MENU_ITEM, *PPH_PLUGIN_MENU_ITEM; - -// Location -#define PH_MENU_ITEM_LOCATION_VIEW 1 -#define PH_MENU_ITEM_LOCATION_TOOLS 2 - -// Id flags (non-functional) -#define PH_MENU_ITEM_SUB_MENU 0x80000000 -#define PH_MENU_ITEM_RETURN_MENU 0x40000000 -#define PH_MENU_ITEM_VALID_FLAGS 0xc0000000 - -PHAPPAPI -ULONG_PTR -NTAPI -PhPluginAddMenuItem( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG_PTR Location, - _In_opt_ PWSTR InsertAfter, - _In_ ULONG Id, - _In_ PWSTR Text, - _In_opt_ PVOID Context - ); - -typedef struct _PH_PLUGIN_SYSTEM_STATISTICS -{ - PSYSTEM_PERFORMANCE_INFORMATION Performance; - - ULONG NumberOfProcesses; - ULONG NumberOfThreads; - ULONG NumberOfHandles; - - FLOAT CpuKernelUsage; - FLOAT CpuUserUsage; - - PH_UINT64_DELTA IoReadDelta; - PH_UINT64_DELTA IoWriteDelta; - PH_UINT64_DELTA IoOtherDelta; - - ULONG CommitPages; - ULONG PhysicalPages; - - HANDLE MaxCpuProcessId; - HANDLE MaxIoProcessId; - - PPH_CIRCULAR_BUFFER_FLOAT CpuKernelHistory; - PPH_CIRCULAR_BUFFER_FLOAT CpuUserHistory; - PPH_CIRCULAR_BUFFER_FLOAT *CpusKernelHistory; - PPH_CIRCULAR_BUFFER_FLOAT *CpusUserHistory; - PPH_CIRCULAR_BUFFER_ULONG64 IoReadHistory; - PPH_CIRCULAR_BUFFER_ULONG64 IoWriteHistory; - PPH_CIRCULAR_BUFFER_ULONG64 IoOtherHistory; - PPH_CIRCULAR_BUFFER_ULONG CommitHistory; - PPH_CIRCULAR_BUFFER_ULONG PhysicalHistory; - PPH_CIRCULAR_BUFFER_ULONG MaxCpuHistory; // ID of max. CPU process - PPH_CIRCULAR_BUFFER_ULONG MaxIoHistory; // ID of max. I/O process - PPH_CIRCULAR_BUFFER_FLOAT MaxCpuUsageHistory; - PPH_CIRCULAR_BUFFER_ULONG64 MaxIoReadOtherHistory; - PPH_CIRCULAR_BUFFER_ULONG64 MaxIoWriteHistory; -} PH_PLUGIN_SYSTEM_STATISTICS, *PPH_PLUGIN_SYSTEM_STATISTICS; - -PHAPPAPI -VOID -NTAPI -PhPluginGetSystemStatistics( - _Out_ PPH_PLUGIN_SYSTEM_STATISTICS Statistics - ); - -PHAPPAPI -PPH_EMENU_ITEM -NTAPI -PhPluginCreateEMenuItem( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG Flags, - _In_ ULONG Id, - _In_ PWSTR Text, - _In_opt_ PVOID Context - ); - -PHAPPAPI -BOOLEAN -NTAPI -PhPluginAddMenuHook( - _Inout_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_ PPH_PLUGIN Plugin, - _In_opt_ PVOID Context - ); -// end_phapppub - -VOID -NTAPI -PhPluginInitializeMenuInfo( - _Out_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_opt_ PPH_EMENU Menu, - _In_ HWND OwnerWindow, - _In_ ULONG Flags - ); - -BOOLEAN -NTAPI -PhPluginTriggerEMenuItem( - _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_ PPH_EMENU_ITEM Item - ); - -// begin_phapppub -PHAPPAPI -BOOLEAN -NTAPI -PhPluginAddTreeNewColumn( - _In_ PPH_PLUGIN Plugin, - _In_ PVOID CmData, - _In_ PPH_TREENEW_COLUMN Column, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_opt_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction - ); - -PHAPPAPI -VOID -NTAPI -PhPluginSetObjectExtension( - _In_ PPH_PLUGIN Plugin, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ ULONG ExtensionSize, - _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, - _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback - ); - -PHAPPAPI -PVOID -NTAPI -PhPluginGetObjectExtension( - _In_ PPH_PLUGIN Plugin, - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType - ); - -PHAPPAPI -struct _PH_NF_ICON * -NTAPI -PhPluginRegisterIcon( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_ PWSTR Text, - _In_ ULONG Flags, - _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData - ); - -PHAPPAPI -VOID -NTAPI -PhPluginEnableTreeNewNotify( - _In_ PPH_PLUGIN Plugin, - _In_ PVOID CmData - ); - -PHAPPAPI -BOOLEAN -NTAPI -PhPluginQueryPhSvc( - _Out_ PPH_PLUGIN_PHSVC_CLIENT Client - ); - -PHAPPAPI -NTSTATUS -NTAPI -PhPluginCallPhSvc( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG SubId, - _In_reads_bytes_opt_(InLength) PVOID InBuffer, - _In_ ULONG InLength, - _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, - _In_ ULONG OutLength - ); -// end_phapppub - -#endif +#ifndef PH_PHPLUG_H +#define PH_PHPLUG_H + +#include +#include +#include + +// begin_phapppub +// Callbacks + +typedef enum _PH_GENERAL_CALLBACK +{ + GeneralCallbackMainWindowShowing = 0, // INT ShowCommand [main thread] + GeneralCallbackProcessesUpdated = 1, // [main thread] + GeneralCallbackGetProcessHighlightingColor = 2, // PPH_PLUGIN_GET_HIGHLIGHTING_COLOR Data [main thread] + GeneralCallbackGetProcessTooltipText = 3, // PPH_PLUGIN_GET_TOOLTIP_TEXT Data [main thread] + GeneralCallbackProcessPropertiesInitializing = 4, // PPH_PLUGIN_PROCESS_PROPCONTEXT Data [properties thread] + GeneralCallbackMainMenuInitializing = 5, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] + GeneralCallbackNotifyEvent = 6, // PPH_PLUGIN_NOTIFY_EVENT Data [main thread] + GeneralCallbackServicePropertiesInitializing = 7, // PPH_PLUGIN_OBJECT_PROPERTIES Data [properties thread] + GeneralCallbackHandlePropertiesInitializing = 8, // PPH_PLUGIN_OBJECT_PROPERTIES Data [properties thread] + GeneralCallbackProcessMenuInitializing = 9, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] + GeneralCallbackServiceMenuInitializing = 10, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] + GeneralCallbackNetworkMenuInitializing = 11, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] + GeneralCallbackIconMenuInitializing = 12, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] + GeneralCallbackThreadMenuInitializing = 13, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] + GeneralCallbackModuleMenuInitializing = 14, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] + GeneralCallbackMemoryMenuInitializing = 15, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] + GeneralCallbackHandleMenuInitializing = 16, // PPH_PLUGIN_MENU_INFORMATION Data [properties thread] + GeneralCallbackProcessTreeNewInitializing = 17, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread] + GeneralCallbackServiceTreeNewInitializing = 18, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread] + GeneralCallbackNetworkTreeNewInitializing = 19, // PPH_PLUGIN_TREENEW_INFORMATION Data [main thread] + GeneralCallbackModuleTreeNewInitializing = 20, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackModuleTreeNewUninitializing = 21, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackThreadTreeNewInitializing = 22, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackThreadTreeNewUninitializing = 23, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackHandleTreeNewInitializing = 24, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackHandleTreeNewUninitializing = 25, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackThreadStackControl = 26, // PPH_PLUGIN_THREAD_STACK_CONTROL Data [properties thread] + GeneralCallbackSystemInformationInitializing = 27, // PPH_PLUGIN_SYSINFO_POINTERS Data [system information thread] + GeneralCallbackMainWindowTabChanged = 28, // INT NewIndex [main thread] + GeneralCallbackMemoryTreeNewInitializing = 29, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackMemoryTreeNewUninitializing = 30, // PPH_PLUGIN_TREENEW_INFORMATION Data [properties thread] + GeneralCallbackMemoryItemListControl = 31, // PPH_PLUGIN_MEMORY_ITEM_LIST_CONTROL Data [properties thread] + GeneralCallbackMiniInformationInitializing = 32, // PPH_PLUGIN_MINIINFO_POINTERS Data [main thread] + GeneralCallbackMiListSectionMenuInitializing = 33, // PPH_PLUGIN_MENU_INFORMATION Data [main thread] + GeneralCallbackOptionsWindowInitializing = 34, // PPH_PLUGIN_OBJECT_PROPERTIES Data [main thread] + + GeneralCallbackProcessProviderAddedEvent, // [process provider thread] + GeneralCallbackProcessProviderModifiedEvent, // [process provider thread] + GeneralCallbackProcessProviderRemovedEvent, // [process provider thread] + GeneralCallbackProcessProviderUpdatedEvent, // [process provider thread] + GeneralCallbackServiceProviderAddedEvent, // [service provider thread] + GeneralCallbackServiceProviderModifiedEvent, // [service provider thread] + GeneralCallbackServiceProviderRemovedEvent, // [service provider thread] + GeneralCallbackServiceProviderUpdatedEvent, // [service provider thread] + GeneralCallbackNetworkProviderAddedEvent, // [network provider thread] + GeneralCallbackNetworkProviderModifiedEvent, // [network provider thread] + GeneralCallbackNetworkProviderRemovedEvent, // [network provider thread] + GeneralCallbackNetworkProviderUpdatedEvent, // [network provider thread] + + GeneralCallbackLoggedEvent, + GeneralCallbackTrayIconsInitializing, + GeneralCallbackWindowNotifyEvent, + GeneralCallbackProcessStatsNotifyEvent, + GeneralCallbackMaximum +} PH_GENERAL_CALLBACK, *PPH_GENERAL_CALLBACK; + +typedef enum _PH_PLUGIN_CALLBACK +{ + PluginCallbackLoad = 0, // PPH_LIST Parameters [main thread] // list of strings, might be NULL + PluginCallbackUnload = 1, // [main thread] + PluginCallbackShowOptions = 2, // HWND ParentWindowHandle [main thread] + PluginCallbackMenuItem = 3, // PPH_PLUGIN_MENU_ITEM MenuItem [main/properties thread] + PluginCallbackTreeNewMessage = 4, // PPH_PLUGIN_TREENEW_MESSAGE Message [main/properties thread] + PluginCallbackPhSvcRequest = 5, // PPH_PLUGIN_PHSVC_REQUEST Message [phsvc thread] + PluginCallbackMenuHook = 6, // PH_PLUGIN_MENU_HOOK_INFORMATION MenuHookInfo [menu thread] + PluginCallbackMaximum +} PH_PLUGIN_CALLBACK, *PPH_PLUGIN_CALLBACK; + +typedef struct _PH_PLUGIN_GET_HIGHLIGHTING_COLOR +{ + // Parameter is: + // PPH_PROCESS_ITEM for GeneralCallbackGetProcessHighlightingColor + + PVOID Parameter; + COLORREF BackColor; + COLORREF ForeColor; + BOOLEAN Handled; + BOOLEAN Cache; +} PH_PLUGIN_GET_HIGHLIGHTING_COLOR, *PPH_PLUGIN_GET_HIGHLIGHTING_COLOR; + +typedef struct _PH_PLUGIN_GET_TOOLTIP_TEXT +{ + // Parameter is: + // PPH_PROCESS_ITEM for GeneralCallbackGetProcessTooltipText + + PVOID Parameter; + PPH_STRING_BUILDER StringBuilder; + ULONG ValidForMs; +} PH_PLUGIN_GET_TOOLTIP_TEXT, *PPH_PLUGIN_GET_TOOLTIP_TEXT; + +typedef struct _PH_PLUGIN_PROCESS_PROPCONTEXT +{ + PPH_PROCESS_PROPCONTEXT PropContext; + PPH_PROCESS_ITEM ProcessItem; +} PH_PLUGIN_PROCESS_PROPCONTEXT, *PPH_PLUGIN_PROCESS_PROPCONTEXT; + +typedef struct _PH_PLUGIN_NOTIFY_EVENT +{ + // Parameter is: + // PPH_PROCESS_ITEM for Type = PH_NOTIFY_PROCESS_* + // PPH_SERVICE_ITEM for Type = PH_NOTIFY_SERVICE_* + + ULONG Type; + BOOLEAN Handled; + PVOID Parameter; +} PH_PLUGIN_NOTIFY_EVENT, *PPH_PLUGIN_NOTIFY_EVENT; + +typedef struct _PH_PLUGIN_OBJECT_PROPERTIES +{ + // Parameter is: + // PPH_SERVICE_ITEM for GeneralCallbackServicePropertiesInitializing + // PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT for GeneralCallbackHandlePropertiesInitializing + + PVOID Parameter; + ULONG NumberOfPages; + ULONG MaximumNumberOfPages; + HPROPSHEETPAGE *Pages; +} PH_PLUGIN_OBJECT_PROPERTIES, *PPH_PLUGIN_OBJECT_PROPERTIES; + +typedef struct _PH_PLUGIN_PROCESS_STATS_EVENT +{ + ULONG Version; + ULONG Type; + PPH_PROCESS_ITEM ProcessItem; + PVOID Parameter; +} PH_PLUGIN_PROCESS_STATS_EVENT, *PPH_PLUGIN_PROCESS_STATS_EVENT; + +typedef struct _PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT +{ + HANDLE ProcessId; + PPH_HANDLE_ITEM HandleItem; +} PH_PLUGIN_HANDLE_PROPERTIES_CONTEXT, *PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT; + +typedef struct _PH_EMENU_ITEM *PPH_EMENU_ITEM, *PPH_EMENU; + +#define PH_PLUGIN_MENU_DISALLOW_HOOKS 0x1 + +typedef struct _PH_PLUGIN_MENU_INFORMATION +{ + PPH_EMENU Menu; + HWND OwnerWindow; + + union + { + struct + { + PVOID Reserved[8]; // Reserve space for future expansion of this union + } DoNotUse; + struct + { + ULONG SubMenuIndex; + } MainMenu; + struct + { + PPH_PROCESS_ITEM *Processes; + ULONG NumberOfProcesses; + } Process; + struct + { + PPH_SERVICE_ITEM *Services; + ULONG NumberOfServices; + } Service; + struct + { + PPH_NETWORK_ITEM *NetworkItems; + ULONG NumberOfNetworkItems; + } Network; + struct + { + HANDLE ProcessId; + PPH_THREAD_ITEM *Threads; + ULONG NumberOfThreads; + } Thread; + struct + { + HANDLE ProcessId; + PPH_MODULE_ITEM *Modules; + ULONG NumberOfModules; + } Module; + struct + { + HANDLE ProcessId; + PPH_MEMORY_NODE *MemoryNodes; + ULONG NumberOfMemoryNodes; + } Memory; + struct + { + HANDLE ProcessId; + PPH_HANDLE_ITEM *Handles; + ULONG NumberOfHandles; + } Handle; + struct + { + PPH_STRINGREF SectionName; + PPH_PROCESS_GROUP ProcessGroup; + } MiListSection; + } u; + + ULONG Flags; + PPH_LIST PluginHookList; +} PH_PLUGIN_MENU_INFORMATION, *PPH_PLUGIN_MENU_INFORMATION; + +C_ASSERT(RTL_FIELD_SIZE(PH_PLUGIN_MENU_INFORMATION, u) == RTL_FIELD_SIZE(PH_PLUGIN_MENU_INFORMATION, u.DoNotUse)); + +typedef struct _PH_PLUGIN_MENU_HOOK_INFORMATION +{ + PPH_PLUGIN_MENU_INFORMATION MenuInfo; + PPH_EMENU SelectedItem; + PVOID Context; + BOOLEAN Handled; +} PH_PLUGIN_MENU_HOOK_INFORMATION, *PPH_PLUGIN_MENU_HOOK_INFORMATION; + +typedef struct _PH_PLUGIN_TREENEW_INFORMATION +{ + HWND TreeNewHandle; + PVOID CmData; + PVOID SystemContext; // e.g. PPH_THREADS_CONTEXT +} PH_PLUGIN_TREENEW_INFORMATION, *PPH_PLUGIN_TREENEW_INFORMATION; + +typedef enum _PH_PLUGIN_THREAD_STACK_CONTROL_TYPE +{ + PluginThreadStackInitializing, + PluginThreadStackUninitializing, + PluginThreadStackResolveSymbol, + PluginThreadStackGetTooltip, + PluginThreadStackWalkStack, + PluginThreadStackBeginDefaultWalkStack, + PluginThreadStackEndDefaultWalkStack, + PluginThreadStackMaximum +} PH_PLUGIN_THREAD_STACK_CONTROL_TYPE; + +typedef struct _PH_SYMBOL_PROVIDER *PPH_SYMBOL_PROVIDER; +typedef struct _PH_THREAD_STACK_FRAME *PPH_THREAD_STACK_FRAME; + +typedef BOOLEAN (NTAPI *PPH_PLUGIN_WALK_THREAD_STACK_CALLBACK)( + _In_ PPH_THREAD_STACK_FRAME StackFrame, + _In_opt_ PVOID Context + ); + +typedef struct _PH_PLUGIN_THREAD_STACK_CONTROL +{ + PH_PLUGIN_THREAD_STACK_CONTROL_TYPE Type; + PVOID UniqueKey; + + union + { + struct + { + HANDLE ProcessId; + HANDLE ThreadId; + HANDLE ThreadHandle; + PPH_SYMBOL_PROVIDER SymbolProvider; + BOOLEAN CustomWalk; + } Initializing; + struct + { + PPH_THREAD_STACK_FRAME StackFrame; + PPH_STRING Symbol; + } ResolveSymbol; + struct + { + PPH_THREAD_STACK_FRAME StackFrame; + PPH_STRING_BUILDER StringBuilder; + } GetTooltip; + struct + { + NTSTATUS Status; + HANDLE ThreadHandle; + HANDLE ProcessHandle; + PCLIENT_ID ClientId; + ULONG Flags; + PPH_PLUGIN_WALK_THREAD_STACK_CALLBACK Callback; + PVOID CallbackContext; + } WalkStack; + } u; +} PH_PLUGIN_THREAD_STACK_CONTROL, *PPH_PLUGIN_THREAD_STACK_CONTROL; + +typedef enum _PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE +{ + PluginMemoryItemListInitialized, + PluginMemoryItemListMaximum +} PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE; + +typedef struct _PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL +{ + PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL_TYPE Type; + + union + { + struct + { + PPH_MEMORY_ITEM_LIST List; + } Initialized; + } u; +} PH_PLUGIN_MEMORY_ITEM_LIST_CONTROL, *PPH_PLUGIN_MEMORY_ITEM_LIST_CONTROL; + +typedef PPH_SYSINFO_SECTION (NTAPI *PPH_SYSINFO_CREATE_SECTION)( + _In_ PPH_SYSINFO_SECTION Template + ); + +typedef PPH_SYSINFO_SECTION (NTAPI *PPH_SYSINFO_FIND_SECTION)( + _In_ PPH_STRINGREF Name + ); + +typedef VOID (NTAPI *PPH_SYSINFO_ENTER_SECTION_VIEW)( + _In_ PPH_SYSINFO_SECTION NewSection + ); + +typedef VOID (NTAPI *PPH_SYSINFO_RESTORE_SUMMARY_VIEW)( + VOID + ); + +typedef struct _PH_PLUGIN_SYSINFO_POINTERS +{ + HWND WindowHandle; + PPH_SYSINFO_CREATE_SECTION CreateSection; + PPH_SYSINFO_FIND_SECTION FindSection; + PPH_SYSINFO_ENTER_SECTION_VIEW EnterSectionView; + PPH_SYSINFO_RESTORE_SUMMARY_VIEW RestoreSummaryView; +} PH_PLUGIN_SYSINFO_POINTERS, *PPH_PLUGIN_SYSINFO_POINTERS; + +typedef PPH_MINIINFO_SECTION (NTAPI *PPH_MINIINFO_CREATE_SECTION)( + _In_ PPH_MINIINFO_SECTION Template + ); + +typedef PPH_MINIINFO_SECTION (NTAPI *PPH_MINIINFO_FIND_SECTION)( + _In_ PPH_STRINGREF Name + ); + +typedef PPH_MINIINFO_LIST_SECTION (NTAPI *PPH_MINIINFO_CREATE_LIST_SECTION)( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_LIST_SECTION Template + ); + +typedef struct _PH_PLUGIN_MINIINFO_POINTERS +{ + HWND WindowHandle; + PPH_MINIINFO_CREATE_SECTION CreateSection; + PPH_MINIINFO_FIND_SECTION FindSection; + PPH_MINIINFO_CREATE_LIST_SECTION CreateListSection; +} PH_PLUGIN_MINIINFO_POINTERS, *PPH_PLUGIN_MINIINFO_POINTERS; +// end_phapppub + +// begin_phapppub +/** + * Creates a notification icon. + * + * \param Plugin A plugin instance structure. + * \param SubId An identifier for the column. This should be unique within the + * plugin. + * \param Guid A unique guid for this icon. + * \param Context A user-defined value. + * \param Text A string describing the notification icon. + * \param Flags A combination of flags. + * \li \c PH_NF_ICON_UNAVAILABLE The notification icon is currently unavailable. + * \param RegistrationData A \ref PH_NF_ICON_REGISTRATION_DATA structure that + * contains registration information. + */ +typedef struct _PH_NF_ICON * (NTAPI *PPH_REGISTER_TRAY_ICON)( + _In_ struct _PH_PLUGIN * Plugin, + _In_ ULONG SubId, + _In_ GUID Guid, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData + ); + +typedef struct _PH_TRAY_ICON_POINTERS +{ + PPH_REGISTER_TRAY_ICON RegisterTrayIcon; +} PH_TRAY_ICON_POINTERS, *PPH_TRAY_ICON_POINTERS; +// end_phapppub + +// begin_phapppub +typedef struct _PH_OPTIONS_SECTION +{ + PH_STRINGREF Name; + // end_phapppub + + PVOID Instance; + PWSTR Template; + DLGPROC DialogProc; + PVOID Parameter; + + HWND DialogHandle; + HTREEITEM TreeItemHandle; + // begin_phapppub +} PH_OPTIONS_SECTION, *PPH_OPTIONS_SECTION; +// end_phapppub + +// begin_phapppub +typedef PPH_OPTIONS_SECTION (NTAPI *PPH_OPTIONS_CREATE_SECTION)( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_opt_ PVOID Parameter + ); + +typedef PPH_OPTIONS_SECTION (NTAPI *PPH_OPTIONS_FIND_SECTION)( + _In_ PPH_STRINGREF Name + ); + +typedef VOID (NTAPI *PPH_OPTIONS_ENTER_SECTION_VIEW)( + _In_ PPH_OPTIONS_SECTION NewSection + ); + +typedef struct _PH_PLUGIN_OPTIONS_POINTERS +{ + HWND WindowHandle; + PPH_OPTIONS_CREATE_SECTION CreateSection; + PPH_OPTIONS_FIND_SECTION FindSection; + PPH_OPTIONS_ENTER_SECTION_VIEW EnterSectionView; +} PH_PLUGIN_OPTIONS_POINTERS, *PPH_PLUGIN_OPTIONS_POINTERS; +// end_phapppub + +// begin_phapppub +typedef struct _PH_PLUGIN_TREENEW_MESSAGE +{ + HWND TreeNewHandle; + PH_TREENEW_MESSAGE Message; + PVOID Parameter1; + PVOID Parameter2; + ULONG SubId; + PVOID Context; +} PH_PLUGIN_TREENEW_MESSAGE, *PPH_PLUGIN_TREENEW_MESSAGE; + +typedef LONG (NTAPI *PPH_PLUGIN_TREENEW_SORT_FUNCTION)( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ); + +typedef NTSTATUS (NTAPI *PPHSVC_SERVER_PROBE_BUFFER)( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ ULONG Alignment, + _In_ BOOLEAN AllowNull, + _Out_ PVOID *Pointer + ); + +typedef NTSTATUS (NTAPI *PPHSVC_SERVER_CAPTURE_BUFFER)( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PVOID *CapturedBuffer + ); + +typedef NTSTATUS (NTAPI *PPHSVC_SERVER_CAPTURE_STRING)( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PPH_STRING *CapturedString + ); + +typedef struct _PH_PLUGIN_PHSVC_REQUEST +{ + ULONG SubId; + NTSTATUS ReturnStatus; + PVOID InBuffer; + ULONG InLength; + PVOID OutBuffer; + ULONG OutLength; + + PPHSVC_SERVER_PROBE_BUFFER ProbeBuffer; + PPHSVC_SERVER_CAPTURE_BUFFER CaptureBuffer; + PPHSVC_SERVER_CAPTURE_STRING CaptureString; +} PH_PLUGIN_PHSVC_REQUEST, *PPH_PLUGIN_PHSVC_REQUEST; + +typedef VOID (NTAPI *PPHSVC_CLIENT_FREE_HEAP)( + _In_ PVOID Memory + ); + +typedef PVOID (NTAPI *PPHSVC_CLIENT_CREATE_STRING)( + _In_opt_ PVOID String, + _In_ SIZE_T Length, + _Out_ PPH_RELATIVE_STRINGREF StringRef + ); + +typedef struct _PH_PLUGIN_PHSVC_CLIENT +{ + HANDLE ServerProcessId; + PPHSVC_CLIENT_FREE_HEAP FreeHeap; + PPHSVC_CLIENT_CREATE_STRING CreateString; +} PH_PLUGIN_PHSVC_CLIENT, *PPH_PLUGIN_PHSVC_CLIENT; + +// Plugin structures + +typedef struct _PH_PLUGIN_INFORMATION +{ + PWSTR DisplayName; + PWSTR Author; + PWSTR Description; + PWSTR Url; + BOOLEAN HasOptions; + BOOLEAN Reserved1[3]; + PVOID Interface; +} PH_PLUGIN_INFORMATION, *PPH_PLUGIN_INFORMATION; + +#define PH_PLUGIN_FLAG_RESERVED 0x1 +// end_phapppub + +// begin_phapppub +typedef struct _PH_PLUGIN +{ + // Public + + PH_AVL_LINKS Links; + + PVOID Reserved; + PVOID DllBase; +// end_phapppub + + // Private + + PPH_STRING FileName; + ULONG Flags; + PH_STRINGREF Name; + PH_PLUGIN_INFORMATION Information; + + PH_CALLBACK Callbacks[PluginCallbackMaximum]; + PH_EM_APP_CONTEXT AppContext; +// begin_phapppub +} PH_PLUGIN, *PPH_PLUGIN; +// end_phapppub + +// begin_phapppub +// Plugin API + +PHAPPAPI +PPH_PLUGIN +NTAPI +PhRegisterPlugin( + _In_ PWSTR Name, + _In_ PVOID DllBase, + _Out_opt_ PPH_PLUGIN_INFORMATION *Information + ); + +PHAPPAPI +PPH_PLUGIN +NTAPI +PhFindPlugin( + _In_ PWSTR Name + ); + +PHAPPAPI +PPH_PLUGIN_INFORMATION +NTAPI +PhGetPluginInformation( + _In_ PPH_PLUGIN Plugin + ); + +PHAPPAPI +PPH_CALLBACK +NTAPI +PhGetPluginCallback( + _In_ PPH_PLUGIN Plugin, + _In_ PH_PLUGIN_CALLBACK Callback + ); + +PHAPPAPI +PPH_CALLBACK +NTAPI +PhGetGeneralCallback( + _In_ PH_GENERAL_CALLBACK Callback + ); + +PHAPPAPI +ULONG +NTAPI +PhPluginReserveIds( + _In_ ULONG Count + ); + +typedef VOID (NTAPI *PPH_PLUGIN_MENU_ITEM_DELETE_FUNCTION)( + _In_ struct _PH_PLUGIN_MENU_ITEM *MenuItem + ); + +typedef struct _PH_PLUGIN_MENU_ITEM +{ + PPH_PLUGIN Plugin; + ULONG Id; + ULONG Reserved1; + PVOID Context; + + HWND OwnerWindow; // valid only when the menu item is chosen + PVOID Reserved2; + PVOID Reserved3; + PPH_PLUGIN_MENU_ITEM_DELETE_FUNCTION DeleteFunction; // valid only for EMENU-based menu items +} PH_PLUGIN_MENU_ITEM, *PPH_PLUGIN_MENU_ITEM; + +// Location +#define PH_MENU_ITEM_LOCATION_HACKER 0 +#define PH_MENU_ITEM_LOCATION_VIEW 1 +#define PH_MENU_ITEM_LOCATION_TOOLS 2 +#define PH_MENU_ITEM_LOCATION_USERS 3 +#define PH_MENU_ITEM_LOCATION_HELP 4 + +typedef struct _PH_PLUGIN_SYSTEM_STATISTICS +{ + PSYSTEM_PERFORMANCE_INFORMATION Performance; + + ULONG NumberOfProcesses; + ULONG NumberOfThreads; + ULONG NumberOfHandles; + + FLOAT CpuKernelUsage; + FLOAT CpuUserUsage; + + PH_UINT64_DELTA IoReadDelta; + PH_UINT64_DELTA IoWriteDelta; + PH_UINT64_DELTA IoOtherDelta; + + ULONG CommitPages; + ULONG PhysicalPages; + + HANDLE MaxCpuProcessId; + HANDLE MaxIoProcessId; + + PPH_CIRCULAR_BUFFER_FLOAT CpuKernelHistory; + PPH_CIRCULAR_BUFFER_FLOAT CpuUserHistory; + PPH_CIRCULAR_BUFFER_FLOAT *CpusKernelHistory; + PPH_CIRCULAR_BUFFER_FLOAT *CpusUserHistory; + PPH_CIRCULAR_BUFFER_ULONG64 IoReadHistory; + PPH_CIRCULAR_BUFFER_ULONG64 IoWriteHistory; + PPH_CIRCULAR_BUFFER_ULONG64 IoOtherHistory; + PPH_CIRCULAR_BUFFER_ULONG CommitHistory; + PPH_CIRCULAR_BUFFER_ULONG PhysicalHistory; + PPH_CIRCULAR_BUFFER_ULONG MaxCpuHistory; // ID of max. CPU process + PPH_CIRCULAR_BUFFER_ULONG MaxIoHistory; // ID of max. I/O process + PPH_CIRCULAR_BUFFER_FLOAT MaxCpuUsageHistory; + PPH_CIRCULAR_BUFFER_ULONG64 MaxIoReadOtherHistory; + PPH_CIRCULAR_BUFFER_ULONG64 MaxIoWriteHistory; +} PH_PLUGIN_SYSTEM_STATISTICS, *PPH_PLUGIN_SYSTEM_STATISTICS; + +PHAPPAPI +VOID +NTAPI +PhPluginGetSystemStatistics( + _Out_ PPH_PLUGIN_SYSTEM_STATISTICS Statistics + ); + +PHAPPAPI +PPH_EMENU_ITEM +NTAPI +PhPluginCreateEMenuItem( + _In_ PPH_PLUGIN Plugin, + _In_ ULONG Flags, + _In_ ULONG Id, + _In_ PWSTR Text, + _In_opt_ PVOID Context + ); + +PHAPPAPI +BOOLEAN +NTAPI +PhPluginAddMenuHook( + _Inout_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_ PPH_PLUGIN Plugin, + _In_opt_ PVOID Context + ); +// end_phapppub + +VOID +NTAPI +PhPluginInitializeMenuInfo( + _Out_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_opt_ PPH_EMENU Menu, + _In_ HWND OwnerWindow, + _In_ ULONG Flags + ); + +BOOLEAN +NTAPI +PhPluginTriggerEMenuItem( + _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_ PPH_EMENU_ITEM Item + ); + +// begin_phapppub +PHAPPAPI +BOOLEAN +NTAPI +PhPluginAddTreeNewColumn( + _In_ PPH_PLUGIN Plugin, + _In_ PVOID CmData, + _In_ PPH_TREENEW_COLUMN Column, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_opt_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction + ); + +PHAPPAPI +VOID +NTAPI +PhPluginSetObjectExtension( + _In_ PPH_PLUGIN Plugin, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ ULONG ExtensionSize, + _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, + _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback + ); + +PHAPPAPI +PVOID +NTAPI +PhPluginGetObjectExtension( + _In_ PPH_PLUGIN Plugin, + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType + ); + +PHAPPAPI +VOID +NTAPI +PhPluginEnableTreeNewNotify( + _In_ PPH_PLUGIN Plugin, + _In_ PVOID CmData + ); + +PHAPPAPI +BOOLEAN +NTAPI +PhPluginQueryPhSvc( + _Out_ PPH_PLUGIN_PHSVC_CLIENT Client + ); + +PHAPPAPI +NTSTATUS +NTAPI +PhPluginCallPhSvc( + _In_ PPH_PLUGIN Plugin, + _In_ ULONG SubId, + _In_reads_bytes_opt_(InLength) PVOID InBuffer, + _In_ ULONG InLength, + _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, + _In_ ULONG OutLength + ); + +PHAPPAPI +PPH_STRING +NTAPI +PhGetPluginName( + _In_ PPH_PLUGIN Plugin + ); + +// end_phapppub + +#endif diff --git a/ProcessHacker/include/phsettings.h b/ProcessHacker/include/phsettings.h new file mode 100644 index 000000000000..82efa0913059 --- /dev/null +++ b/ProcessHacker/include/phsettings.h @@ -0,0 +1,87 @@ +#ifndef PH_SETTINGS_H +#define PH_SETTINGS_H + +// Cached settings + +#undef EXT + +#ifdef PH_SETTINGS_PRIVATE +#define EXT +#else +#define EXT extern +#endif + +EXT BOOLEAN PhEnableProcessQueryStage2; +EXT BOOLEAN PhEnableServiceQueryStage2; +EXT BOOLEAN PhEnableThemeSupport; +EXT BOOLEAN PhEnableTooltipSupport; +EXT BOOLEAN PhEnableLinuxSubsystemSupport; + +EXT ULONG PhCsCollapseServicesOnStart; +EXT ULONG PhCsForceNoParent; +EXT ULONG PhCsHighlightingDuration; +EXT ULONG PhCsPropagateCpuUsage; +EXT ULONG PhCsScrollToNewProcesses; +EXT ULONG PhCsShowCpuBelow001; +EXT ULONG PhCsUpdateInterval; + +EXT ULONG PhCsColorNew; +EXT ULONG PhCsColorRemoved; +EXT ULONG PhCsUseColorOwnProcesses; +EXT ULONG PhCsColorOwnProcesses; +EXT ULONG PhCsUseColorSystemProcesses; +EXT ULONG PhCsColorSystemProcesses; +EXT ULONG PhCsUseColorServiceProcesses; +EXT ULONG PhCsColorServiceProcesses; +EXT ULONG PhCsUseColorJobProcesses; +EXT ULONG PhCsColorJobProcesses; +EXT ULONG PhCsUseColorWow64Processes; +EXT ULONG PhCsColorWow64Processes; +EXT ULONG PhCsUseColorDebuggedProcesses; +EXT ULONG PhCsColorDebuggedProcesses; +EXT ULONG PhCsUseColorElevatedProcesses; +EXT ULONG PhCsColorElevatedProcesses; +EXT ULONG PhCsUseColorHandleFiltered; +EXT ULONG PhCsColorHandleFiltered; +EXT ULONG PhCsUseColorImmersiveProcesses; +EXT ULONG PhCsColorImmersiveProcesses; +EXT ULONG PhCsUseColorPicoProcesses; +EXT ULONG PhCsColorPicoProcesses; +EXT ULONG PhCsUseColorSuspended; +EXT ULONG PhCsColorSuspended; +EXT ULONG PhCsUseColorDotNet; +EXT ULONG PhCsColorDotNet; +EXT ULONG PhCsUseColorPacked; +EXT ULONG PhCsColorPacked; +EXT ULONG PhCsUseColorGuiThreads; +EXT ULONG PhCsColorGuiThreads; +EXT ULONG PhCsUseColorRelocatedModules; +EXT ULONG PhCsColorRelocatedModules; +EXT ULONG PhCsUseColorProtectedHandles; +EXT ULONG PhCsColorProtectedHandles; +EXT ULONG PhCsUseColorInheritHandles; +EXT ULONG PhCsColorInheritHandles; +EXT ULONG PhCsGraphShowText; +EXT ULONG PhCsGraphColorMode; +EXT ULONG PhCsColorCpuKernel; +EXT ULONG PhCsColorCpuUser; +EXT ULONG PhCsColorIoReadOther; +EXT ULONG PhCsColorIoWrite; +EXT ULONG PhCsColorPrivate; +EXT ULONG PhCsColorPhysical; + +EXT ULONG PhCsUseColorUnknown; +EXT ULONG PhCsColorUnknown; +EXT ULONG PhCsUseColorServiceDisabled; +EXT ULONG PhCsColorServiceDisabled; +EXT ULONG PhCsUseColorServiceStop; +EXT ULONG PhCsColorServiceStop; + +EXT ULONG PhCsUseColorSystemThreadStack; +EXT ULONG PhCsColorSystemThreadStack; +EXT ULONG PhCsUseColorUserThreadStack; +EXT ULONG PhCsColorUserThreadStack; + +#define PH_SET_INTEGER_CACHED_SETTING(Name, Value) (PhSetIntegerSetting(L#Name, PhCs##Name = (Value))) + +#endif diff --git a/ProcessHacker/include/phsvc.h b/ProcessHacker/include/phsvc.h index 80fe51e05d21..57bb44b88870 100644 --- a/ProcessHacker/include/phsvc.h +++ b/ProcessHacker/include/phsvc.h @@ -1,238 +1,224 @@ -#ifndef PH_PHSVC_H -#define PH_PHSVC_H - -#include - -#define PHSVC_SHARED_SECTION_SIZE (512 * 1024) - -// svcmain - -typedef struct _PHSVC_STOP -{ - BOOLEAN Stop; - HANDLE Event1; - HANDLE Event2; -} PHSVC_STOP, *PPHSVC_STOP; - -NTSTATUS PhSvcMain( - _In_opt_ PUNICODE_STRING PortName, - _In_opt_ PLARGE_INTEGER Timeout, - _Inout_opt_ PPHSVC_STOP Stop - ); - -VOID PhSvcStop( - _Inout_ PPHSVC_STOP Stop - ); - -// svcclient - -typedef struct _PHSVC_CLIENT -{ - LIST_ENTRY ListEntry; - - PH_EVENT ReadyEvent; - CLIENT_ID ClientId; - HANDLE PortHandle; - PVOID ClientViewBase; - PVOID ClientViewLimit; -} PHSVC_CLIENT, *PPHSVC_CLIENT; - -NTSTATUS PhSvcClientInitialization( - VOID - ); - -PPHSVC_CLIENT PhSvcCreateClient( - _In_opt_ PCLIENT_ID ClientId - ); - -PPHSVC_CLIENT PhSvcReferenceClientByClientId( - _In_ PCLIENT_ID ClientId - ); - -PPHSVC_CLIENT PhSvcGetCurrentClient( - VOID - ); - -BOOLEAN PhSvcAttachClient( - _In_ PPHSVC_CLIENT Client - ); - -VOID PhSvcDetachClient( - _In_ PPHSVC_CLIENT Client - ); - -// svcapiport - -typedef struct _PHSVC_THREAD_CONTEXT -{ - PPHSVC_CLIENT CurrentClient; - PPHSVC_CLIENT OldClient; -} PHSVC_THREAD_CONTEXT, *PPHSVC_THREAD_CONTEXT; - -NTSTATUS PhSvcApiPortInitialization( - _In_ PUNICODE_STRING PortName - ); - -PPHSVC_THREAD_CONTEXT PhSvcGetCurrentThreadContext( - VOID - ); - -VOID PhSvcHandleConnectionRequest( - _In_ PPORT_MESSAGE PortMessage - ); - -// svcapi - -NTSTATUS PhSvcApiInitialization( - VOID - ); - -typedef NTSTATUS (NTAPI *PPHSVC_API_PROCEDURE)( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -VOID PhSvcDispatchApiCall( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload, - _Out_ PHANDLE ReplyPortHandle - ); - -PVOID PhSvcValidateString( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ ULONG Alignment - ); - -NTSTATUS PhSvcProbeBuffer( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ ULONG Alignment, - _In_ BOOLEAN AllowNull, - _Out_ PVOID *Pointer - ); - -NTSTATUS PhSvcCaptureBuffer( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PVOID *CapturedBuffer - ); - -NTSTATUS PhSvcCaptureString( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PPH_STRING *CapturedString - ); - -NTSTATUS PhSvcCaptureSid( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PSID *CapturedSid - ); - -NTSTATUS PhSvcCaptureSecurityDescriptor( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _In_ SECURITY_INFORMATION RequiredInformation, - _Out_ PSECURITY_DESCRIPTOR *CapturedSecurityDescriptor - ); - -NTSTATUS PhSvcApiDefault( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiPlugin( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiExecuteRunAsCommand( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiUnloadDriver( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiControlProcess( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiControlService( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiCreateService( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiChangeServiceConfig( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiChangeServiceConfig2( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiSetTcpEntry( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiControlThread( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiAddAccountRight( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiInvokeRunAsService( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiIssueMemoryListCommand( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiPostMessage( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiSendMessage( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiCreateProcessIgnoreIfeoDebugger( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiSetServiceSecurity( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiLoadDbgHelp( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -NTSTATUS PhSvcApiWriteMiniDumpProcess( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ); - -#endif +#ifndef PH_PHSVC_H +#define PH_PHSVC_H + +#include + +#define PHSVC_SHARED_SECTION_SIZE (512 * 1024) + +// svcmain + +typedef struct _PHSVC_STOP +{ + BOOLEAN Stop; + HANDLE Event1; + HANDLE Event2; +} PHSVC_STOP, *PPHSVC_STOP; + +NTSTATUS PhSvcMain( + _In_opt_ PPH_STRING PortName, + _Inout_opt_ PPHSVC_STOP Stop + ); + +VOID PhSvcStop( + _Inout_ PPHSVC_STOP Stop + ); + +// svcclient + +typedef struct _PHSVC_CLIENT +{ + LIST_ENTRY ListEntry; + + PH_EVENT ReadyEvent; + CLIENT_ID ClientId; + HANDLE PortHandle; + PVOID ClientViewBase; + PVOID ClientViewLimit; +} PHSVC_CLIENT, *PPHSVC_CLIENT; + +PPHSVC_CLIENT PhSvcCreateClient( + _In_opt_ PCLIENT_ID ClientId + ); + +PPHSVC_CLIENT PhSvcReferenceClientByClientId( + _In_ PCLIENT_ID ClientId + ); + +PPHSVC_CLIENT PhSvcGetCurrentClient( + VOID + ); + +BOOLEAN PhSvcAttachClient( + _In_ PPHSVC_CLIENT Client + ); + +VOID PhSvcDetachClient( + _In_ PPHSVC_CLIENT Client + ); + +// svcapiport + +typedef struct _PHSVC_THREAD_CONTEXT +{ + PPHSVC_CLIENT CurrentClient; + PPHSVC_CLIENT OldClient; +} PHSVC_THREAD_CONTEXT, *PPHSVC_THREAD_CONTEXT; + +NTSTATUS PhSvcApiPortInitialization( + _In_ PUNICODE_STRING PortName + ); + +PPHSVC_THREAD_CONTEXT PhSvcGetCurrentThreadContext( + VOID + ); + +VOID PhSvcHandleConnectionRequest( + _In_ PPORT_MESSAGE PortMessage + ); + +// svcapi + +typedef NTSTATUS (NTAPI *PPHSVC_API_PROCEDURE)( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +VOID PhSvcDispatchApiCall( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload, + _Out_ PHANDLE ReplyPortHandle + ); + +PVOID PhSvcValidateString( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ ULONG Alignment + ); + +NTSTATUS PhSvcProbeBuffer( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ ULONG Alignment, + _In_ BOOLEAN AllowNull, + _Out_ PVOID *Pointer + ); + +NTSTATUS PhSvcCaptureBuffer( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PVOID *CapturedBuffer + ); + +NTSTATUS PhSvcCaptureString( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PPH_STRING *CapturedString + ); + +NTSTATUS PhSvcCaptureSid( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PSID *CapturedSid + ); + +NTSTATUS PhSvcCaptureSecurityDescriptor( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _In_ SECURITY_INFORMATION RequiredInformation, + _Out_ PSECURITY_DESCRIPTOR *CapturedSecurityDescriptor + ); + +NTSTATUS PhSvcApiDefault( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiPlugin( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiExecuteRunAsCommand( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiUnloadDriver( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiControlProcess( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiControlService( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiCreateService( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiChangeServiceConfig( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiChangeServiceConfig2( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiSetTcpEntry( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiControlThread( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiAddAccountRight( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiInvokeRunAsService( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiIssueMemoryListCommand( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiPostMessage( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiSendMessage( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiCreateProcessIgnoreIfeoDebugger( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiSetServiceSecurity( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +NTSTATUS PhSvcApiWriteMiniDumpProcess( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ); + +#endif diff --git a/ProcessHacker/include/phsvcapi.h b/ProcessHacker/include/phsvcapi.h index ca4061239101..a4a1b5d1bab8 100644 --- a/ProcessHacker/include/phsvcapi.h +++ b/ProcessHacker/include/phsvcapi.h @@ -1,310 +1,301 @@ -#ifndef PH_PHSVCAPI_H -#define PH_PHSVCAPI_H - -#define PHSVC_PORT_NAME (L"\\BaseNamedObjects\\PhSvcApiPort") -#define PHSVC_WOW64_PORT_NAME (L"\\BaseNamedObjects\\PhSvcWow64ApiPort") - -typedef enum _PHSVC_API_NUMBER -{ - PhSvcPluginApiNumber = 1, - PhSvcExecuteRunAsCommandApiNumber = 2, - PhSvcUnloadDriverApiNumber = 3, - PhSvcControlProcessApiNumber = 4, - PhSvcControlServiceApiNumber = 5, - PhSvcCreateServiceApiNumber = 6, - PhSvcChangeServiceConfigApiNumber = 7, - PhSvcChangeServiceConfig2ApiNumber = 8, - PhSvcSetTcpEntryApiNumber = 9, - PhSvcControlThreadApiNumber = 10, - PhSvcAddAccountRightApiNumber = 11, - PhSvcInvokeRunAsServiceApiNumber = 12, - PhSvcIssueMemoryListCommandApiNumber = 13, - PhSvcPostMessageApiNumber = 14, - PhSvcSendMessageApiNumber = 15, - PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber = 16, - PhSvcSetServiceSecurityApiNumber = 17, - PhSvcLoadDbgHelpApiNumber = 18, // WOW64 compatible - PhSvcWriteMiniDumpProcessApiNumber = 19, // WOW64 compatible - PhSvcMaximumApiNumber -} PHSVC_API_NUMBER, *PPHSVC_API_NUMBER; - -typedef struct _PHSVC_API_CONNECTINFO -{ - ULONG ServerProcessId; -} PHSVC_API_CONNECTINFO, *PPHSVC_API_CONNECTINFO; - -typedef union _PHSVC_API_PLUGIN -{ - struct - { - PH_RELATIVE_STRINGREF ApiId; - ULONG Data[30]; - } i; - struct - { - ULONG Data[32]; - } o; -} PHSVC_API_PLUGIN, *PPHSVC_API_PLUGIN; - -typedef union _PHSVC_API_EXECUTERUNASCOMMAND -{ - struct - { - ULONG ProcessId; - PH_RELATIVE_STRINGREF UserName; - PH_RELATIVE_STRINGREF Password; - ULONG LogonType; - ULONG SessionId; - PH_RELATIVE_STRINGREF CurrentDirectory; - PH_RELATIVE_STRINGREF CommandLine; - PH_RELATIVE_STRINGREF FileName; - PH_RELATIVE_STRINGREF DesktopName; - BOOLEAN UseLinkedToken; - PH_RELATIVE_STRINGREF ServiceName; - } i; -} PHSVC_API_EXECUTERUNASCOMMAND, *PPHSVC_API_EXECUTERUNASCOMMAND; - -typedef union _PHSVC_API_UNLOADDRIVER -{ - struct - { - PVOID BaseAddress; - PH_RELATIVE_STRINGREF Name; - } i; -} PHSVC_API_UNLOADDRIVER, *PPHSVC_API_UNLOADDRIVER; - -typedef enum _PHSVC_API_CONTROLPROCESS_COMMAND -{ - PhSvcControlProcessTerminate = 1, - PhSvcControlProcessSuspend, - PhSvcControlProcessResume, - PhSvcControlProcessPriority, - PhSvcControlProcessIoPriority -} PHSVC_API_CONTROLPROCESS_COMMAND; - -typedef union _PHSVC_API_CONTROLPROCESS -{ - struct - { - HANDLE ProcessId; - PHSVC_API_CONTROLPROCESS_COMMAND Command; - ULONG Argument; - } i; -} PHSVC_API_CONTROLPROCESS, *PPHSVC_API_CONTROLPROCESS; - -typedef enum _PHSVC_API_CONTROLSERVICE_COMMAND -{ - PhSvcControlServiceStart = 1, - PhSvcControlServiceContinue, - PhSvcControlServicePause, - PhSvcControlServiceStop, - PhSvcControlServiceDelete -} PHSVC_API_CONTROLSERVICE_COMMAND; - -typedef union _PHSVC_API_CONTROLSERVICE -{ - struct - { - PH_RELATIVE_STRINGREF ServiceName; - PHSVC_API_CONTROLSERVICE_COMMAND Command; - } i; -} PHSVC_API_CONTROLSERVICE, *PPHSVC_API_CONTROLSERVICE; - -typedef union _PHSVC_API_CREATESERVICE -{ - struct - { - // ServiceName is the only required string. - PH_RELATIVE_STRINGREF ServiceName; - PH_RELATIVE_STRINGREF DisplayName; - ULONG ServiceType; - ULONG StartType; - ULONG ErrorControl; - PH_RELATIVE_STRINGREF BinaryPathName; - PH_RELATIVE_STRINGREF LoadOrderGroup; - PH_RELATIVE_STRINGREF Dependencies; - PH_RELATIVE_STRINGREF ServiceStartName; - PH_RELATIVE_STRINGREF Password; - BOOLEAN TagIdSpecified; - } i; - struct - { - ULONG TagId; - } o; -} PHSVC_API_CREATESERVICE, *PPHSVC_API_CREATESERVICE; - -typedef union _PHSVC_API_CHANGESERVICECONFIG -{ - struct - { - PH_RELATIVE_STRINGREF ServiceName; - ULONG ServiceType; - ULONG StartType; - ULONG ErrorControl; - PH_RELATIVE_STRINGREF BinaryPathName; - PH_RELATIVE_STRINGREF LoadOrderGroup; - PH_RELATIVE_STRINGREF Dependencies; - PH_RELATIVE_STRINGREF ServiceStartName; - PH_RELATIVE_STRINGREF Password; - PH_RELATIVE_STRINGREF DisplayName; - BOOLEAN TagIdSpecified; - } i; - struct - { - ULONG TagId; - } o; -} PHSVC_API_CHANGESERVICECONFIG, *PPHSVC_API_CHANGESERVICECONFIG; - -typedef union _PHSVC_API_CHANGESERVICECONFIG2 -{ - struct - { - PH_RELATIVE_STRINGREF ServiceName; - ULONG InfoLevel; - PH_RELATIVE_STRINGREF Info; - } i; -} PHSVC_API_CHANGESERVICECONFIG2, *PPHSVC_API_CHANGESERVICECONFIG2; - -typedef union _PHSVC_API_SETTCPENTRY -{ - struct - { - ULONG State; - ULONG LocalAddress; - ULONG LocalPort; - ULONG RemoteAddress; - ULONG RemotePort; - } i; -} PHSVC_API_SETTCPENTRY, *PPHSVC_API_SETTCPENTRY; - -typedef enum _PHSVC_API_CONTROLTHREAD_COMMAND -{ - PhSvcControlThreadTerminate = 1, - PhSvcControlThreadSuspend, - PhSvcControlThreadResume, - PhSvcControlThreadIoPriority -} PHSVC_API_CONTROLTHREAD_COMMAND; - -typedef union _PHSVC_API_CONTROLTHREAD -{ - struct - { - HANDLE ThreadId; - PHSVC_API_CONTROLTHREAD_COMMAND Command; - ULONG Argument; - } i; -} PHSVC_API_CONTROLTHREAD, *PPHSVC_API_CONTROLTHREAD; - -typedef union _PHSVC_API_ADDACCOUNTRIGHT -{ - struct - { - PH_RELATIVE_STRINGREF AccountSid; - PH_RELATIVE_STRINGREF UserRight; - } i; -} PHSVC_API_ADDACCOUNTRIGHT, *PPHSVC_API_ADDACCOUNTRIGHT; - -typedef union _PHSVC_API_ISSUEMEMORYLISTCOMMAND -{ - struct - { - SYSTEM_MEMORY_LIST_COMMAND Command; - } i; -} PHSVC_API_ISSUEMEMORYLISTCOMMAND, *PPHSVC_API_ISSUEMEMORYLISTCOMMAND; - -typedef union _PHSVC_API_POSTMESSAGE -{ - struct - { - HWND hWnd; - UINT Msg; - WPARAM wParam; - LPARAM lParam; - } i; -} PHSVC_API_POSTMESSAGE, *PPHSVC_API_POSTMESSAGE; - -typedef union _PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER -{ - struct - { - PH_RELATIVE_STRINGREF FileName; - } i; -} PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER, *PPHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER; - -typedef union _PHSVC_API_SETSERVICESECURITY -{ - struct - { - PH_RELATIVE_STRINGREF ServiceName; - SECURITY_INFORMATION SecurityInformation; - PH_RELATIVE_STRINGREF SecurityDescriptor; - } i; -} PHSVC_API_SETSERVICESECURITY, *PPHSVC_API_SETSERVICESECURITY; - -typedef union _PHSVC_API_LOADDBGHELP -{ - struct - { - PH_RELATIVE_STRINGREF DbgHelpPath; - } i; -} PHSVC_API_LOADDBGHELP, *PPHSVC_API_LOADDBGHELP; - -typedef union _PHSVC_API_WRITEMINIDUMPPROCESS -{ - struct - { - ULONG LocalProcessHandle; - ULONG ProcessId; - ULONG LocalFileHandle; - ULONG DumpType; - } i; -} PHSVC_API_WRITEMINIDUMPPROCESS, *PPHSVC_API_WRITEMINIDUMPPROCESS; - -typedef union _PHSVC_API_PAYLOAD -{ - PHSVC_API_CONNECTINFO ConnectInfo; - struct - { - PHSVC_API_NUMBER ApiNumber; - NTSTATUS ReturnStatus; - - union - { - PHSVC_API_PLUGIN Plugin; - PHSVC_API_EXECUTERUNASCOMMAND ExecuteRunAsCommand; - PHSVC_API_UNLOADDRIVER UnloadDriver; - PHSVC_API_CONTROLPROCESS ControlProcess; - PHSVC_API_CONTROLSERVICE ControlService; - PHSVC_API_CREATESERVICE CreateService; - PHSVC_API_CHANGESERVICECONFIG ChangeServiceConfig; - PHSVC_API_CHANGESERVICECONFIG2 ChangeServiceConfig2; - PHSVC_API_SETTCPENTRY SetTcpEntry; - PHSVC_API_CONTROLTHREAD ControlThread; - PHSVC_API_ADDACCOUNTRIGHT AddAccountRight; - PHSVC_API_ISSUEMEMORYLISTCOMMAND IssueMemoryListCommand; - PHSVC_API_POSTMESSAGE PostMessage; - PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER CreateProcessIgnoreIfeoDebugger; - PHSVC_API_SETSERVICESECURITY SetServiceSecurity; - PHSVC_API_LOADDBGHELP LoadDbgHelp; - PHSVC_API_WRITEMINIDUMPPROCESS WriteMiniDumpProcess; - } u; - }; -} PHSVC_API_PAYLOAD, *PPHSVC_API_PAYLOAD; - -typedef struct _PHSVC_API_MSG -{ - PORT_MESSAGE h; - PHSVC_API_PAYLOAD p; -} PHSVC_API_MSG, *PPHSVC_API_MSG; - -typedef struct _PHSVC_API_MSG64 -{ - PORT_MESSAGE64 h; - PHSVC_API_PAYLOAD p; -} PHSVC_API_MSG64, *PPHSVC_API_MSG64; - -C_ASSERT(FIELD_OFFSET(PHSVC_API_PAYLOAD, u) == 8); -C_ASSERT(sizeof(PHSVC_API_MSG) <= PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH); -C_ASSERT(sizeof(PHSVC_API_MSG64) <= PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH); - -#endif +#ifndef PH_PHSVCAPI_H +#define PH_PHSVCAPI_H + +#define PHSVC_PORT_NAME (L"\\BaseNamedObjects\\PhSvcApiPort") +#define PHSVC_WOW64_PORT_NAME (L"\\BaseNamedObjects\\PhSvcWow64ApiPort") + +typedef enum _PHSVC_API_NUMBER +{ + PhSvcPluginApiNumber = 1, + PhSvcExecuteRunAsCommandApiNumber = 2, + PhSvcUnloadDriverApiNumber = 3, + PhSvcControlProcessApiNumber = 4, + PhSvcControlServiceApiNumber = 5, + PhSvcCreateServiceApiNumber = 6, + PhSvcChangeServiceConfigApiNumber = 7, + PhSvcChangeServiceConfig2ApiNumber = 8, + PhSvcSetTcpEntryApiNumber = 9, + PhSvcControlThreadApiNumber = 10, + PhSvcAddAccountRightApiNumber = 11, + PhSvcInvokeRunAsServiceApiNumber = 12, + PhSvcIssueMemoryListCommandApiNumber = 13, + PhSvcPostMessageApiNumber = 14, + PhSvcSendMessageApiNumber = 15, + PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber = 16, + PhSvcSetServiceSecurityApiNumber = 17, + PhSvcWriteMiniDumpProcessApiNumber = 18, // WOW64 compatible + PhSvcMaximumApiNumber +} PHSVC_API_NUMBER, *PPHSVC_API_NUMBER; + +typedef struct _PHSVC_API_CONNECTINFO +{ + ULONG ServerProcessId; +} PHSVC_API_CONNECTINFO, *PPHSVC_API_CONNECTINFO; + +typedef union _PHSVC_API_PLUGIN +{ + struct + { + PH_RELATIVE_STRINGREF ApiId; + ULONG Data[30]; + } i; + struct + { + ULONG Data[32]; + } o; +} PHSVC_API_PLUGIN, *PPHSVC_API_PLUGIN; + +typedef union _PHSVC_API_EXECUTERUNASCOMMAND +{ + struct + { + ULONG ProcessId; + PH_RELATIVE_STRINGREF UserName; + PH_RELATIVE_STRINGREF Password; + ULONG LogonType; + ULONG SessionId; + PH_RELATIVE_STRINGREF CurrentDirectory; + PH_RELATIVE_STRINGREF CommandLine; + PH_RELATIVE_STRINGREF FileName; + PH_RELATIVE_STRINGREF DesktopName; + BOOLEAN UseLinkedToken; + PH_RELATIVE_STRINGREF ServiceName; + BOOLEAN CreateSuspendedProcess; + } i; +} PHSVC_API_EXECUTERUNASCOMMAND, *PPHSVC_API_EXECUTERUNASCOMMAND; + +typedef union _PHSVC_API_UNLOADDRIVER +{ + struct + { + PVOID BaseAddress; + PH_RELATIVE_STRINGREF Name; + } i; +} PHSVC_API_UNLOADDRIVER, *PPHSVC_API_UNLOADDRIVER; + +typedef enum _PHSVC_API_CONTROLPROCESS_COMMAND +{ + PhSvcControlProcessTerminate = 1, + PhSvcControlProcessSuspend, + PhSvcControlProcessResume, + PhSvcControlProcessPriority, + PhSvcControlProcessIoPriority +} PHSVC_API_CONTROLPROCESS_COMMAND; + +typedef union _PHSVC_API_CONTROLPROCESS +{ + struct + { + HANDLE ProcessId; + PHSVC_API_CONTROLPROCESS_COMMAND Command; + ULONG Argument; + } i; +} PHSVC_API_CONTROLPROCESS, *PPHSVC_API_CONTROLPROCESS; + +typedef enum _PHSVC_API_CONTROLSERVICE_COMMAND +{ + PhSvcControlServiceStart = 1, + PhSvcControlServiceContinue, + PhSvcControlServicePause, + PhSvcControlServiceStop, + PhSvcControlServiceDelete +} PHSVC_API_CONTROLSERVICE_COMMAND; + +typedef union _PHSVC_API_CONTROLSERVICE +{ + struct + { + PH_RELATIVE_STRINGREF ServiceName; + PHSVC_API_CONTROLSERVICE_COMMAND Command; + } i; +} PHSVC_API_CONTROLSERVICE, *PPHSVC_API_CONTROLSERVICE; + +typedef union _PHSVC_API_CREATESERVICE +{ + struct + { + // ServiceName is the only required string. + PH_RELATIVE_STRINGREF ServiceName; + PH_RELATIVE_STRINGREF DisplayName; + ULONG ServiceType; + ULONG StartType; + ULONG ErrorControl; + PH_RELATIVE_STRINGREF BinaryPathName; + PH_RELATIVE_STRINGREF LoadOrderGroup; + PH_RELATIVE_STRINGREF Dependencies; + PH_RELATIVE_STRINGREF ServiceStartName; + PH_RELATIVE_STRINGREF Password; + BOOLEAN TagIdSpecified; + } i; + struct + { + ULONG TagId; + } o; +} PHSVC_API_CREATESERVICE, *PPHSVC_API_CREATESERVICE; + +typedef union _PHSVC_API_CHANGESERVICECONFIG +{ + struct + { + PH_RELATIVE_STRINGREF ServiceName; + ULONG ServiceType; + ULONG StartType; + ULONG ErrorControl; + PH_RELATIVE_STRINGREF BinaryPathName; + PH_RELATIVE_STRINGREF LoadOrderGroup; + PH_RELATIVE_STRINGREF Dependencies; + PH_RELATIVE_STRINGREF ServiceStartName; + PH_RELATIVE_STRINGREF Password; + PH_RELATIVE_STRINGREF DisplayName; + BOOLEAN TagIdSpecified; + } i; + struct + { + ULONG TagId; + } o; +} PHSVC_API_CHANGESERVICECONFIG, *PPHSVC_API_CHANGESERVICECONFIG; + +typedef union _PHSVC_API_CHANGESERVICECONFIG2 +{ + struct + { + PH_RELATIVE_STRINGREF ServiceName; + ULONG InfoLevel; + PH_RELATIVE_STRINGREF Info; + } i; +} PHSVC_API_CHANGESERVICECONFIG2, *PPHSVC_API_CHANGESERVICECONFIG2; + +typedef union _PHSVC_API_SETTCPENTRY +{ + struct + { + ULONG State; + ULONG LocalAddress; + ULONG LocalPort; + ULONG RemoteAddress; + ULONG RemotePort; + } i; +} PHSVC_API_SETTCPENTRY, *PPHSVC_API_SETTCPENTRY; + +typedef enum _PHSVC_API_CONTROLTHREAD_COMMAND +{ + PhSvcControlThreadTerminate = 1, + PhSvcControlThreadSuspend, + PhSvcControlThreadResume, + PhSvcControlThreadIoPriority +} PHSVC_API_CONTROLTHREAD_COMMAND; + +typedef union _PHSVC_API_CONTROLTHREAD +{ + struct + { + HANDLE ThreadId; + PHSVC_API_CONTROLTHREAD_COMMAND Command; + ULONG Argument; + } i; +} PHSVC_API_CONTROLTHREAD, *PPHSVC_API_CONTROLTHREAD; + +typedef union _PHSVC_API_ADDACCOUNTRIGHT +{ + struct + { + PH_RELATIVE_STRINGREF AccountSid; + PH_RELATIVE_STRINGREF UserRight; + } i; +} PHSVC_API_ADDACCOUNTRIGHT, *PPHSVC_API_ADDACCOUNTRIGHT; + +typedef union _PHSVC_API_ISSUEMEMORYLISTCOMMAND +{ + struct + { + SYSTEM_MEMORY_LIST_COMMAND Command; + } i; +} PHSVC_API_ISSUEMEMORYLISTCOMMAND, *PPHSVC_API_ISSUEMEMORYLISTCOMMAND; + +typedef union _PHSVC_API_POSTMESSAGE +{ + struct + { + HWND hWnd; + UINT Msg; + WPARAM wParam; + LPARAM lParam; + } i; +} PHSVC_API_POSTMESSAGE, *PPHSVC_API_POSTMESSAGE; + +typedef union _PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER +{ + struct + { + PH_RELATIVE_STRINGREF FileName; + } i; +} PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER, *PPHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER; + +typedef union _PHSVC_API_SETSERVICESECURITY +{ + struct + { + PH_RELATIVE_STRINGREF ServiceName; + SECURITY_INFORMATION SecurityInformation; + PH_RELATIVE_STRINGREF SecurityDescriptor; + } i; +} PHSVC_API_SETSERVICESECURITY, *PPHSVC_API_SETSERVICESECURITY; + +typedef union _PHSVC_API_WRITEMINIDUMPPROCESS +{ + struct + { + ULONG LocalProcessHandle; + ULONG ProcessId; + ULONG LocalFileHandle; + ULONG DumpType; + } i; +} PHSVC_API_WRITEMINIDUMPPROCESS, *PPHSVC_API_WRITEMINIDUMPPROCESS; + +typedef union _PHSVC_API_PAYLOAD +{ + PHSVC_API_CONNECTINFO ConnectInfo; + struct + { + PHSVC_API_NUMBER ApiNumber; + NTSTATUS ReturnStatus; + + union + { + PHSVC_API_PLUGIN Plugin; + PHSVC_API_EXECUTERUNASCOMMAND ExecuteRunAsCommand; + PHSVC_API_UNLOADDRIVER UnloadDriver; + PHSVC_API_CONTROLPROCESS ControlProcess; + PHSVC_API_CONTROLSERVICE ControlService; + PHSVC_API_CREATESERVICE CreateService; + PHSVC_API_CHANGESERVICECONFIG ChangeServiceConfig; + PHSVC_API_CHANGESERVICECONFIG2 ChangeServiceConfig2; + PHSVC_API_SETTCPENTRY SetTcpEntry; + PHSVC_API_CONTROLTHREAD ControlThread; + PHSVC_API_ADDACCOUNTRIGHT AddAccountRight; + PHSVC_API_ISSUEMEMORYLISTCOMMAND IssueMemoryListCommand; + PHSVC_API_POSTMESSAGE PostMessage; + PHSVC_API_CREATEPROCESSIGNOREIFEODEBUGGER CreateProcessIgnoreIfeoDebugger; + PHSVC_API_SETSERVICESECURITY SetServiceSecurity; + PHSVC_API_WRITEMINIDUMPPROCESS WriteMiniDumpProcess; + } u; + }; +} PHSVC_API_PAYLOAD, *PPHSVC_API_PAYLOAD; + +typedef struct _PHSVC_API_MSG +{ + PORT_MESSAGE h; + PHSVC_API_PAYLOAD p; +} PHSVC_API_MSG, *PPHSVC_API_MSG; + +typedef struct _PHSVC_API_MSG64 +{ + PORT_MESSAGE64 h; + PHSVC_API_PAYLOAD p; +} PHSVC_API_MSG64, *PPHSVC_API_MSG64; + +C_ASSERT(FIELD_OFFSET(PHSVC_API_PAYLOAD, u) == 8); +C_ASSERT(sizeof(PHSVC_API_MSG) <= PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH); +C_ASSERT(sizeof(PHSVC_API_MSG64) <= PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH); + +#endif diff --git a/ProcessHacker/include/phsvccl.h b/ProcessHacker/include/phsvccl.h index 491669865b80..94edd38987e5 100644 --- a/ProcessHacker/include/phsvccl.h +++ b/ProcessHacker/include/phsvccl.h @@ -1,155 +1,151 @@ -#ifndef PH_PHSVCCL_H -#define PH_PHSVCCL_H - -#include - -extern HANDLE PhSvcClServerProcessId; - -NTSTATUS PhSvcConnectToServer( - _In_ PUNICODE_STRING PortName, - _In_opt_ SIZE_T PortSectionSize - ); - -VOID PhSvcDisconnectFromServer( - VOID - ); - -VOID PhSvcpFreeHeap( - _In_ PVOID Memory - ); - -PVOID PhSvcpCreateString( - _In_opt_ PVOID String, - _In_ SIZE_T Length, - _Out_ PPH_RELATIVE_STRINGREF StringRef - ); - -NTSTATUS PhSvcCallPlugin( - _In_ PPH_STRINGREF ApiId, - _In_reads_bytes_opt_(InLength) PVOID InBuffer, - _In_ ULONG InLength, - _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, - _In_ ULONG OutLength - ); - -NTSTATUS PhSvcCallExecuteRunAsCommand( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ); - -NTSTATUS PhSvcCallUnloadDriver( - _In_opt_ PVOID BaseAddress, - _In_opt_ PWSTR Name - ); - -NTSTATUS PhSvcCallControlProcess( - _In_ HANDLE ProcessId, - _In_ PHSVC_API_CONTROLPROCESS_COMMAND Command, - _In_ ULONG Argument - ); - -NTSTATUS PhSvcCallControlService( - _In_ PWSTR ServiceName, - _In_ PHSVC_API_CONTROLSERVICE_COMMAND Command - ); - -NTSTATUS PhSvcCallCreateService( - _In_ PWSTR ServiceName, - _In_opt_ PWSTR DisplayName, - _In_ ULONG ServiceType, - _In_ ULONG StartType, - _In_ ULONG ErrorControl, - _In_opt_ PWSTR BinaryPathName, - _In_opt_ PWSTR LoadOrderGroup, - _Out_opt_ PULONG TagId, - _In_opt_ PWSTR Dependencies, - _In_opt_ PWSTR ServiceStartName, - _In_opt_ PWSTR Password - ); - -// begin_phapppub -PHAPPAPI -NTSTATUS PhSvcCallChangeServiceConfig( - _In_ PWSTR ServiceName, - _In_ ULONG ServiceType, - _In_ ULONG StartType, - _In_ ULONG ErrorControl, - _In_opt_ PWSTR BinaryPathName, - _In_opt_ PWSTR LoadOrderGroup, - _Out_opt_ PULONG TagId, - _In_opt_ PWSTR Dependencies, - _In_opt_ PWSTR ServiceStartName, - _In_opt_ PWSTR Password, - _In_opt_ PWSTR DisplayName - ); - -PHAPPAPI -NTSTATUS PhSvcCallChangeServiceConfig2( - _In_ PWSTR ServiceName, - _In_ ULONG InfoLevel, - _In_ PVOID Info - ); -// end_phapppub - -NTSTATUS PhSvcCallSetTcpEntry( - _In_ PVOID TcpRow - ); - -NTSTATUS PhSvcCallControlThread( - _In_ HANDLE ThreadId, - _In_ PHSVC_API_CONTROLTHREAD_COMMAND Command, - _In_ ULONG Argument - ); - -NTSTATUS PhSvcCallAddAccountRight( - _In_ PSID AccountSid, - _In_ PUNICODE_STRING UserRight - ); - -NTSTATUS PhSvcCallInvokeRunAsService( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ); - -NTSTATUS PhSvcCallIssueMemoryListCommand( - _In_ SYSTEM_MEMORY_LIST_COMMAND Command - ); - -// begin_phapppub -PHAPPAPI -NTSTATUS PhSvcCallPostMessage( - _In_opt_ HWND hWnd, - _In_ UINT Msg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PHAPPAPI -NTSTATUS PhSvcCallSendMessage( - _In_opt_ HWND hWnd, - _In_ UINT Msg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); -// end_phapppub - -NTSTATUS PhSvcCallCreateProcessIgnoreIfeoDebugger( - _In_ PWSTR FileName - ); - -NTSTATUS PhSvcCallSetServiceSecurity( - _In_ PWSTR ServiceName, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ); - -NTSTATUS PhSvcCallLoadDbgHelp( - _In_ PWSTR DbgHelpPath - ); - -NTSTATUS PhSvcCallWriteMiniDumpProcess( - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId, - _In_ HANDLE FileHandle, - _In_ ULONG DumpType - ); - -#endif +#ifndef PH_PHSVCCL_H +#define PH_PHSVCCL_H + +#include + +extern HANDLE PhSvcClServerProcessId; + +NTSTATUS PhSvcConnectToServer( + _In_ PUNICODE_STRING PortName, + _In_opt_ SIZE_T PortSectionSize + ); + +VOID PhSvcDisconnectFromServer( + VOID + ); + +VOID PhSvcpFreeHeap( + _In_ PVOID Memory + ); + +PVOID PhSvcpCreateString( + _In_opt_ PVOID String, + _In_ SIZE_T Length, + _Out_ PPH_RELATIVE_STRINGREF StringRef + ); + +NTSTATUS PhSvcCallPlugin( + _In_ PPH_STRINGREF ApiId, + _In_reads_bytes_opt_(InLength) PVOID InBuffer, + _In_ ULONG InLength, + _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, + _In_ ULONG OutLength + ); + +NTSTATUS PhSvcCallExecuteRunAsCommand( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ); + +NTSTATUS PhSvcCallUnloadDriver( + _In_opt_ PVOID BaseAddress, + _In_opt_ PWSTR Name + ); + +NTSTATUS PhSvcCallControlProcess( + _In_ HANDLE ProcessId, + _In_ PHSVC_API_CONTROLPROCESS_COMMAND Command, + _In_ ULONG Argument + ); + +NTSTATUS PhSvcCallControlService( + _In_ PWSTR ServiceName, + _In_ PHSVC_API_CONTROLSERVICE_COMMAND Command + ); + +NTSTATUS PhSvcCallCreateService( + _In_ PWSTR ServiceName, + _In_opt_ PWSTR DisplayName, + _In_ ULONG ServiceType, + _In_ ULONG StartType, + _In_ ULONG ErrorControl, + _In_opt_ PWSTR BinaryPathName, + _In_opt_ PWSTR LoadOrderGroup, + _Out_opt_ PULONG TagId, + _In_opt_ PWSTR Dependencies, + _In_opt_ PWSTR ServiceStartName, + _In_opt_ PWSTR Password + ); + +// begin_phapppub +PHAPPAPI +NTSTATUS PhSvcCallChangeServiceConfig( + _In_ PWSTR ServiceName, + _In_ ULONG ServiceType, + _In_ ULONG StartType, + _In_ ULONG ErrorControl, + _In_opt_ PWSTR BinaryPathName, + _In_opt_ PWSTR LoadOrderGroup, + _Out_opt_ PULONG TagId, + _In_opt_ PWSTR Dependencies, + _In_opt_ PWSTR ServiceStartName, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DisplayName + ); + +PHAPPAPI +NTSTATUS PhSvcCallChangeServiceConfig2( + _In_ PWSTR ServiceName, + _In_ ULONG InfoLevel, + _In_ PVOID Info + ); +// end_phapppub + +NTSTATUS PhSvcCallSetTcpEntry( + _In_ PVOID TcpRow + ); + +NTSTATUS PhSvcCallControlThread( + _In_ HANDLE ThreadId, + _In_ PHSVC_API_CONTROLTHREAD_COMMAND Command, + _In_ ULONG Argument + ); + +NTSTATUS PhSvcCallAddAccountRight( + _In_ PSID AccountSid, + _In_ PUNICODE_STRING UserRight + ); + +NTSTATUS PhSvcCallInvokeRunAsService( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ); + +NTSTATUS PhSvcCallIssueMemoryListCommand( + _In_ SYSTEM_MEMORY_LIST_COMMAND Command + ); + +// begin_phapppub +PHAPPAPI +NTSTATUS PhSvcCallPostMessage( + _In_opt_ HWND hWnd, + _In_ UINT Msg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +PHAPPAPI +NTSTATUS PhSvcCallSendMessage( + _In_opt_ HWND hWnd, + _In_ UINT Msg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); +// end_phapppub + +NTSTATUS PhSvcCallCreateProcessIgnoreIfeoDebugger( + _In_ PWSTR FileName + ); + +NTSTATUS PhSvcCallSetServiceSecurity( + _In_ PWSTR ServiceName, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +NTSTATUS PhSvcCallWriteMiniDumpProcess( + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE FileHandle, + _In_ ULONG DumpType + ); + +#endif diff --git a/ProcessHacker/include/phuisup.h b/ProcessHacker/include/phuisup.h index b7160daaf5b3..8b8c8d0a03e3 100644 --- a/ProcessHacker/include/phuisup.h +++ b/ProcessHacker/include/phuisup.h @@ -1,3 +1,25 @@ +/* + * Process Hacker - + * Provider Event Queue + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef PH_PHUISUP_H #define PH_PHUISUP_H @@ -8,7 +30,7 @@ typedef struct _PH_SH_STATE { PH_ITEM_STATE State; HANDLE StateListHandle; - ULONG TickCount; + ULONG64 TickCount; } PH_SH_STATE, *PPH_SH_STATE; // end_phapppub @@ -27,7 +49,7 @@ FORCEINLINE VOID PhChangeShStateTn( if (ShState->State == NormalItemState) ShState->StateListHandle = PhAddItemPointerList(*StateList, Node); - ShState->TickCount = GetTickCount(); + ShState->TickCount = NtGetTickCount64(); ShState->State = NewState; Node->UseTempBackColor = TRUE; @@ -41,16 +63,15 @@ FORCEINLINE VOID PhChangeShStateTn( do { \ NodeType *node; \ ULONG enumerationKey = 0; \ - ULONG tickCount; \ + ULONG64 tickCount; \ BOOLEAN preferFullInvalidate; \ HANDLE stateListHandle; \ - BOOLEAN redrawDisabled = FALSE; \ BOOLEAN needsFullInvalidate = FALSE; \ \ if (!StateList || StateList->Count == 0) \ break; \ \ - tickCount = GetTickCount(); \ + tickCount = NtGetTickCount64(); \ preferFullInvalidate = StateList->Count > 8; \ \ while (PhEnumPointerList(StateList, &enumerationKey, &node)) \ @@ -72,21 +93,13 @@ FORCEINLINE VOID PhChangeShStateTn( } \ else \ { \ - TreeNew_InvalidateNode(TreeNewHandleForUpdate, node); \ + if (TreeNewHandleForUpdate) \ + TreeNew_InvalidateNode((TreeNewHandleForUpdate), node); \ } \ } \ } \ else if (node->ShStateFieldName.State == RemovingItemState) \ { \ - if (TreeNewHandleForUpdate) \ - { \ - if (!redrawDisabled) \ - { \ - TreeNew_SetRedraw((TreeNewHandleForUpdate), FALSE); \ - redrawDisabled = TRUE; \ - } \ - } \ -\ RemoveFunction(node, __VA_ARGS__); \ needsFullInvalidate = TRUE; \ } \ @@ -96,8 +109,6 @@ FORCEINLINE VOID PhChangeShStateTn( \ if (TreeNewHandleForUpdate) \ { \ - if (redrawDisabled) \ - TreeNew_SetRedraw((TreeNewHandleForUpdate), TRUE); \ if (needsFullInvalidate) \ { \ InvalidateRect((TreeNewHandleForUpdate), NULL, FALSE); \ @@ -107,6 +118,7 @@ FORCEINLINE VOID PhChangeShStateTn( } \ } while (0) +// begin_phapppub // Provider event queues typedef enum _PH_PROVIDER_EVENT_TYPE @@ -209,5 +221,6 @@ FORCEINLINE PPH_PROVIDER_EVENT PhFlushProviderEventQueue( return events; } +// end_phapppub #endif diff --git a/ProcessHacker/include/procgrp.h b/ProcessHacker/include/procgrp.h index 02ce0fc4c213..0a6c947654cd 100644 --- a/ProcessHacker/include/procgrp.h +++ b/ProcessHacker/include/procgrp.h @@ -1,32 +1,32 @@ -#ifndef PH_PROCGRP_H -#define PH_PROCGRP_H - -// begin_phapppub -typedef struct _PH_PROCESS_GROUP -{ - PPH_PROCESS_ITEM Representative; // An element of Processes (no extra reference added) - PPH_LIST Processes; // List of PPH_PROCESS_ITEM - HWND WindowHandle; // Window handle of representative -} PH_PROCESS_GROUP, *PPH_PROCESS_GROUP; -// end_phapppub - -typedef VOID (NTAPI *PPH_SORT_LIST_FUNCTION)( - _In_ PPH_LIST List, - _In_opt_ PVOID Context - ); - -#define PH_GROUP_PROCESSES_DONT_GROUP 0x1 -#define PH_GROUP_PROCESSES_FILE_PATH 0x2 - -PPH_LIST PhCreateProcessGroupList( - _In_opt_ PPH_SORT_LIST_FUNCTION SortListFunction, // Sort a list of PPH_PROCESS_NODE - _In_opt_ PVOID Context, - _In_ ULONG MaximumGroups, - _In_ ULONG Flags - ); - -VOID PhFreeProcessGroupList( - _In_ PPH_LIST List - ); - +#ifndef PH_PROCGRP_H +#define PH_PROCGRP_H + +// begin_phapppub +typedef struct _PH_PROCESS_GROUP +{ + PPH_PROCESS_ITEM Representative; // An element of Processes (no extra reference added) + PPH_LIST Processes; // List of PPH_PROCESS_ITEM + HWND WindowHandle; // Window handle of representative +} PH_PROCESS_GROUP, *PPH_PROCESS_GROUP; +// end_phapppub + +typedef VOID (NTAPI *PPH_SORT_LIST_FUNCTION)( + _In_ PPH_LIST List, + _In_opt_ PVOID Context + ); + +#define PH_GROUP_PROCESSES_DONT_GROUP 0x1 +#define PH_GROUP_PROCESSES_FILE_PATH 0x2 + +PPH_LIST PhCreateProcessGroupList( + _In_opt_ PPH_SORT_LIST_FUNCTION SortListFunction, // Sort a list of PPH_PROCESS_NODE + _In_opt_ PVOID Context, + _In_ ULONG MaximumGroups, + _In_ ULONG Flags + ); + +VOID PhFreeProcessGroupList( + _In_ PPH_LIST List + ); + #endif \ No newline at end of file diff --git a/ProcessHacker/include/procmtgn.h b/ProcessHacker/include/procmtgn.h index 480e4d1c6415..72a931e3d144 100644 --- a/ProcessHacker/include/procmtgn.h +++ b/ProcessHacker/include/procmtgn.h @@ -4,16 +4,21 @@ typedef struct _PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION { PVOID Pointers[MaxProcessMitigationPolicy]; - PROCESS_MITIGATION_DEP_POLICY DEPPolicy; - PROCESS_MITIGATION_ASLR_POLICY ASLRPolicy; - PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY StrictHandleCheckPolicy; - PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY SystemCallDisablePolicy; - PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY ExtensionPointDisablePolicy; - PROCESS_MITIGATION_DYNAMIC_CODE_POLICY DynamicCodePolicy; - PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY ControlFlowGuardPolicy; - PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY SignaturePolicy; - PROCESS_MITIGATION_FONT_DISABLE_POLICY FontDisablePolicy; - PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy; + PROCESS_MITIGATION_DEP_POLICY DEPPolicy; // ProcessDEPPolicy + PROCESS_MITIGATION_ASLR_POLICY ASLRPolicy; // ProcessASLRPolicy + PROCESS_MITIGATION_DYNAMIC_CODE_POLICY DynamicCodePolicy; // ProcessDynamicCodePolicy + PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY StrictHandleCheckPolicy; // ProcessStrictHandleCheckPolicy + PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY SystemCallDisablePolicy; // ProcessSystemCallDisablePolicy + // ProcessMitigationOptionsMask + PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY ExtensionPointDisablePolicy; // ProcessExtensionPointDisablePolicy + PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY ControlFlowGuardPolicy; // ProcessControlFlowGuardPolicy + PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY SignaturePolicy; // ProcessSignaturePolicy + PROCESS_MITIGATION_FONT_DISABLE_POLICY FontDisablePolicy; // ProcessFontDisablePolicy + PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy; // ProcessImageLoadPolicy + PROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY SystemCallFilterPolicy; // ProcessSystemCallFilterPolicy + PROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY PayloadRestrictionPolicy; // ProcessPayloadRestrictionPolicy + PROCESS_MITIGATION_CHILD_PROCESS_POLICY ChildProcessPolicy; // ProcessChildProcessPolicy + PROCESS_MITIGATION_SIDE_CHANNEL_ISOLATION_POLICY SideChannelIsolationPolicy; // ProcessSideChannelIsolationPolicy } PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION, *PPH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION; NTSTATUS PhGetProcessMitigationPolicy( @@ -21,6 +26,11 @@ NTSTATUS PhGetProcessMitigationPolicy( _Out_ PPH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION Information ); +NTSTATUS PhGetProcessSystemDllInitBlock( + _In_ HANDLE ProcessHandle, + _Out_ PPS_SYSTEM_DLL_INIT_BLOCK *SystemDllInitBlock + ); + BOOLEAN PhDescribeProcessMitigationPolicy( _In_ PROCESS_MITIGATION_POLICY Policy, _In_ PVOID Data, diff --git a/ProcessHacker/include/procprp.h b/ProcessHacker/include/procprp.h index 3da58d000c31..769afddd4f61 100644 --- a/ProcessHacker/include/procprp.h +++ b/ProcessHacker/include/procprp.h @@ -6,8 +6,6 @@ typedef struct _PH_PROCESS_PROPCONTEXT { PPH_PROCESS_ITEM ProcessItem; - HWND WindowHandle; - PH_EVENT CreatedEvent; PPH_STRING Title; PROPSHEETHEADER PropSheetHeader; HPROPSHEETPAGE *PropSheetPages; @@ -26,10 +24,6 @@ typedef struct _PH_PROCESS_PROPPAGECONTEXT } PH_PROCESS_PROPPAGECONTEXT, *PPH_PROCESS_PROPPAGECONTEXT; // end_phapppub -BOOLEAN PhProcessPropInitialization( - VOID - ); - // begin_phapppub PHAPPAPI PPH_PROCESS_PROPCONTEXT @@ -95,16 +89,9 @@ PhPropPageDlgProcHeader( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ LPARAM lParam, - _Out_ LPPROPSHEETPAGE *PropSheetPage, - _Out_ PPH_PROCESS_PROPPAGECONTEXT *PropPageContext, - _Out_ PPH_PROCESS_ITEM *ProcessItem - ); - -PHAPPAPI -VOID -NTAPI -PhPropPageDlgProcDestroy( - _In_ HWND hwndDlg + _Out_opt_ LPPROPSHEETPAGE *PropSheetPage, + _Out_opt_ PPH_PROCESS_PROPPAGECONTEXT *PropPageContext, + _Out_opt_ PPH_PROCESS_ITEM *ProcessItem ); #define PH_PROP_PAGE_TAB_CONTROL_PARENT ((PPH_LAYOUT_ITEM)0x1) @@ -156,7 +143,7 @@ PhEndPropPageLayout( } PHAPPAPI -BOOLEAN +VOID NTAPI PhShowProcessProperties( _In_ PPH_PROCESS_PROPCONTEXT Context diff --git a/ProcessHacker/include/procprpp.h b/ProcessHacker/include/procprpp.h index ffa14bee1e82..7e5747b345ca 100644 --- a/ProcessHacker/include/procprpp.h +++ b/ProcessHacker/include/procprpp.h @@ -1,342 +1,364 @@ -#ifndef PH_PROCPRPP_H -#define PH_PROCPRPP_H - -#include -#include -#include -#include -#include - -typedef struct _PH_PROCESS_PROPSHEETCONTEXT -{ - PH_LAYOUT_MANAGER LayoutManager; - PPH_LAYOUT_ITEM TabPageItem; - BOOLEAN LayoutInitialized; -} PH_PROCESS_PROPSHEETCONTEXT, *PPH_PROCESS_PROPSHEETCONTEXT; - -VOID NTAPI PhpProcessPropContextDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -INT CALLBACK PhpPropSheetProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam - ); - -PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( - _In_ HWND hwnd - ); - -LRESULT CALLBACK PhpPropSheetWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -VOID NTAPI PhpProcessPropPageContextDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -INT CALLBACK PhpStandardPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ); - -FORCEINLINE BOOLEAN PhpPropPageDlgProcHeader( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam, - _Out_ LPPROPSHEETPAGE *PropSheetPage, - _Out_ PPH_PROCESS_PROPPAGECONTEXT *PropPageContext, - _Out_ PPH_PROCESS_ITEM *ProcessItem - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - - if (uMsg == WM_INITDIALOG) - { - // Save the context. - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)lParam); - } - - propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, PhMakeContextAtom()); - - if (!propSheetPage) - return FALSE; - - *PropSheetPage = propSheetPage; - *PropPageContext = propPageContext = (PPH_PROCESS_PROPPAGECONTEXT)propSheetPage->lParam; - *ProcessItem = propPageContext->PropContext->ProcessItem; - - return TRUE; -} - -FORCEINLINE VOID PhpPropPageDlgProcDestroy( - _In_ HWND hwndDlg - ) -{ - RemoveProp(hwndDlg, PhMakeContextAtom()); -} - -#define SET_BUTTON_ICON(Id, Icon) \ - SendMessage(GetDlgItem(hwndDlg, (Id)), BM_SETIMAGE, IMAGE_ICON, (LPARAM)(Icon)) - -INT_PTR CALLBACK PhpProcessGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessStatisticsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessPerformanceDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessThreadsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -NTSTATUS NTAPI PhpOpenProcessTokenForPage( - _Out_ PHANDLE Handle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ PVOID Context - ); - -INT_PTR CALLBACK PhpProcessTokenHookProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessModulesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessMemoryDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessHandlesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -NTSTATUS NTAPI PhpOpenProcessJobForPage( - _Out_ PHANDLE Handle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ PVOID Context - ); - -INT_PTR CALLBACK PhpProcessJobHookProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpProcessServicesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -extern PH_STRINGREF PhpLoadingText; - -#define WM_PH_THREADS_UPDATED (WM_APP + 200) -#define WM_PH_THREAD_SELECTION_CHANGED (WM_APP + 201) - -// begin_phapppub -typedef struct _PH_THREADS_CONTEXT -{ - PPH_THREAD_PROVIDER Provider; - PH_CALLBACK_REGISTRATION ProviderRegistration; - PH_CALLBACK_REGISTRATION AddedEventRegistration; - PH_CALLBACK_REGISTRATION ModifiedEventRegistration; - PH_CALLBACK_REGISTRATION RemovedEventRegistration; - PH_CALLBACK_REGISTRATION UpdatedEventRegistration; - PH_CALLBACK_REGISTRATION LoadingStateChangedEventRegistration; - - HWND WindowHandle; -// end_phapppub - - union - { - PH_THREAD_LIST_CONTEXT ListContext; - struct - { - HWND Private; // phapppub - HWND TreeNewHandle; // phapppub - } PublicUse; - }; - PH_PROVIDER_EVENT_QUEUE EventQueue; -// begin_phapppub -} PH_THREADS_CONTEXT, *PPH_THREADS_CONTEXT; -// end_phapppub - -#define WM_PH_MODULES_UPDATED (WM_APP + 210) - -// begin_phapppub -typedef struct _PH_MODULES_CONTEXT -{ - PPH_MODULE_PROVIDER Provider; - PH_PROVIDER_REGISTRATION ProviderRegistration; - PH_CALLBACK_REGISTRATION AddedEventRegistration; - PH_CALLBACK_REGISTRATION ModifiedEventRegistration; - PH_CALLBACK_REGISTRATION RemovedEventRegistration; - PH_CALLBACK_REGISTRATION UpdatedEventRegistration; - - HWND WindowHandle; -// end_phapppub - - union - { - PH_MODULE_LIST_CONTEXT ListContext; - struct - { - HWND Private; // phapppub - HWND TreeNewHandle; // phapppub - } PublicUse; - }; - PH_PROVIDER_EVENT_QUEUE EventQueue; - NTSTATUS LastRunStatus; - PPH_STRING ErrorMessage; -// begin_phapppub -} PH_MODULES_CONTEXT, *PPH_MODULES_CONTEXT; -// end_phapppub - -#define WM_PH_HANDLES_UPDATED (WM_APP + 220) - -// begin_phapppub -typedef struct _PH_HANDLES_CONTEXT -{ - PPH_HANDLE_PROVIDER Provider; - PH_PROVIDER_REGISTRATION ProviderRegistration; - PH_CALLBACK_REGISTRATION AddedEventRegistration; - PH_CALLBACK_REGISTRATION ModifiedEventRegistration; - PH_CALLBACK_REGISTRATION RemovedEventRegistration; - PH_CALLBACK_REGISTRATION UpdatedEventRegistration; - - HWND WindowHandle; -// end_phapppub - - union - { - PH_HANDLE_LIST_CONTEXT ListContext; - struct - { - HWND Private; // phapppub - HWND TreeNewHandle; // phapppub - } PublicUse; - }; - PH_PROVIDER_EVENT_QUEUE EventQueue; - BOOLEAN SelectedHandleProtected; - BOOLEAN SelectedHandleInherit; - NTSTATUS LastRunStatus; - PPH_STRING ErrorMessage; -// begin_phapppub -} PH_HANDLES_CONTEXT, *PPH_HANDLES_CONTEXT; -// end_phapppub - -// begin_phapppub -typedef struct _PH_MEMORY_CONTEXT -{ - HANDLE ProcessId; - HWND WindowHandle; -// end_phapppub - - union - { - PH_MEMORY_LIST_CONTEXT ListContext; - struct - { - HWND Private; // phapppub - HWND TreeNewHandle; // phapppub - } PublicUse; - }; - PH_MEMORY_ITEM_LIST MemoryItemList; - BOOLEAN MemoryItemListValid; - NTSTATUS LastRunStatus; - PPH_STRING ErrorMessage; -// begin_phapppub -} PH_MEMORY_CONTEXT, *PPH_MEMORY_CONTEXT; -// end_phapppub - -#define WM_PH_STATISTICS_UPDATE (WM_APP + 231) - -typedef struct _PH_STATISTICS_CONTEXT -{ - PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - - HWND WindowHandle; - BOOLEAN Enabled; - HANDLE ProcessHandle; -} PH_STATISTICS_CONTEXT, *PPH_STATISTICS_CONTEXT; - -#define WM_PH_PERFORMANCE_UPDATE (WM_APP + 241) - -typedef struct _PH_PERFORMANCE_CONTEXT -{ - PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - - HWND WindowHandle; - - PH_GRAPH_STATE CpuGraphState; - PH_GRAPH_STATE PrivateGraphState; - PH_GRAPH_STATE IoGraphState; - - HWND CpuGraphHandle; - HWND PrivateGraphHandle; - HWND IoGraphHandle; -} PH_PERFORMANCE_CONTEXT, *PPH_PERFORMANCE_CONTEXT; - -typedef struct _PH_ENVIRONMENT_ITEM -{ - PPH_STRING Name; - PPH_STRING Value; -} PH_ENVIRONMENT_ITEM, *PPH_ENVIRONMENT_ITEM; - -typedef struct _PH_ENVIRONMENT_CONTEXT -{ - HWND ListViewHandle; - PH_ARRAY Items; -} PH_ENVIRONMENT_CONTEXT, *PPH_ENVIRONMENT_CONTEXT; - -#endif +#ifndef PH_PROCPRPP_H +#define PH_PROCPRPP_H + +#include +#include +#include +#include +#include + +typedef struct _PH_PROCESS_PROPSHEETCONTEXT +{ + WNDPROC PropSheetWindowHookProc; + PH_LAYOUT_MANAGER LayoutManager; + PPH_LAYOUT_ITEM TabPageItem; + BOOLEAN LayoutInitialized; +} PH_PROCESS_PROPSHEETCONTEXT, *PPH_PROCESS_PROPSHEETCONTEXT; + +VOID NTAPI PhpProcessPropContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +INT CALLBACK PhpPropSheetProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam + ); + +PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( + _In_ HWND hwnd + ); + +LRESULT CALLBACK PhpPropSheetWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID NTAPI PhpProcessPropPageContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +INT CALLBACK PhpStandardPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +#define SET_BUTTON_ICON(Id, Icon) \ + SendMessage(GetDlgItem(hwndDlg, (Id)), BM_SETIMAGE, IMAGE_ICON, (LPARAM)(Icon)) + +INT_PTR CALLBACK PhpProcessGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessStatisticsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessPerformanceDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessThreadsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +NTSTATUS NTAPI PhpOpenProcessTokenForPage( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ); + +INT_PTR CALLBACK PhpProcessTokenHookProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessModulesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessMemoryDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessHandlesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +NTSTATUS NTAPI PhpOpenProcessJobForPage( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ); + +INT_PTR CALLBACK PhpProcessJobHookProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessServicesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpProcessWmiProvidersDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +extern PH_STRINGREF PhpLoadingText; + +#define WM_PH_THREADS_UPDATED (WM_APP + 200) +#define WM_PH_THREAD_SELECTION_CHANGED (WM_APP + 201) + +// begin_phapppub +typedef struct _PH_THREADS_CONTEXT +{ + PPH_THREAD_PROVIDER Provider; + PH_CALLBACK_REGISTRATION ProviderRegistration; + PH_CALLBACK_REGISTRATION AddedEventRegistration; + PH_CALLBACK_REGISTRATION ModifiedEventRegistration; + PH_CALLBACK_REGISTRATION RemovedEventRegistration; + PH_CALLBACK_REGISTRATION UpdatedEventRegistration; + PH_CALLBACK_REGISTRATION LoadingStateChangedEventRegistration; + + HWND WindowHandle; + HWND SearchboxHandle; +// end_phapppub + PPH_STRING SearchboxText; + PPH_TN_FILTER_ENTRY FilterEntry; + union + { + PH_THREAD_LIST_CONTEXT ListContext; + struct + { + HWND Private; // phapppub + HWND TreeNewHandle; // phapppub + } PublicUse; + }; + PH_PROVIDER_EVENT_QUEUE EventQueue; +// begin_phapppub +} PH_THREADS_CONTEXT, *PPH_THREADS_CONTEXT; +// end_phapppub + +#define WM_PH_MODULES_UPDATED (WM_APP + 210) + +// begin_phapppub +typedef struct _PH_MODULES_CONTEXT +{ + PPH_MODULE_PROVIDER Provider; + PH_PROVIDER_REGISTRATION ProviderRegistration; + PH_CALLBACK_REGISTRATION AddedEventRegistration; + PH_CALLBACK_REGISTRATION ModifiedEventRegistration; + PH_CALLBACK_REGISTRATION RemovedEventRegistration; + PH_CALLBACK_REGISTRATION UpdatedEventRegistration; + + HWND WindowHandle; + HWND SearchboxHandle; +// end_phapppub + + union + { + PH_MODULE_LIST_CONTEXT ListContext; + struct + { + HWND Private; // phapppub + HWND TreeNewHandle; // phapppub + } PublicUse; + }; + PH_PROVIDER_EVENT_QUEUE EventQueue; + NTSTATUS LastRunStatus; + PPH_STRING ErrorMessage; + PPH_STRING SearchboxText; + PPH_TN_FILTER_ENTRY FilterEntry; +// begin_phapppub +} PH_MODULES_CONTEXT, *PPH_MODULES_CONTEXT; +// end_phapppub + +#define WM_PH_HANDLES_UPDATED (WM_APP + 220) + +// begin_phapppub +typedef struct _PH_HANDLES_CONTEXT +{ + PPH_HANDLE_PROVIDER Provider; + PH_PROVIDER_REGISTRATION ProviderRegistration; + PH_CALLBACK_REGISTRATION AddedEventRegistration; + PH_CALLBACK_REGISTRATION ModifiedEventRegistration; + PH_CALLBACK_REGISTRATION RemovedEventRegistration; + PH_CALLBACK_REGISTRATION UpdatedEventRegistration; + + HWND WindowHandle; + HWND SearchboxHandle; +// end_phapppub + + union + { + PH_HANDLE_LIST_CONTEXT ListContext; + struct + { + HWND Private; // phapppub + HWND TreeNewHandle; // phapppub + } PublicUse; + }; + PH_PROVIDER_EVENT_QUEUE EventQueue; + BOOLEAN SelectedHandleProtected; + BOOLEAN SelectedHandleInherit; + NTSTATUS LastRunStatus; + PPH_STRING ErrorMessage; + + PPH_STRING SearchboxText; + PPH_TN_FILTER_ENTRY FilterEntry; +// begin_phapppub +} PH_HANDLES_CONTEXT, *PPH_HANDLES_CONTEXT; +// end_phapppub + +// begin_phapppub +typedef struct _PH_MEMORY_CONTEXT +{ + HANDLE ProcessId; + HWND WindowHandle; + HWND SearchboxHandle; +// end_phapppub + + union + { + PH_MEMORY_LIST_CONTEXT ListContext; + struct + { + HWND Private; // phapppub + HWND TreeNewHandle; // phapppub + } PublicUse; + }; + PH_MEMORY_ITEM_LIST MemoryItemList; + BOOLEAN MemoryItemListValid; + NTSTATUS LastRunStatus; + PPH_STRING ErrorMessage; + + BOOLEAN UseSearchPointer; + ULONG64 SearchPointer; + PPH_STRING SearchboxText; + PPH_TN_FILTER_ENTRY AllocationFilterEntry; + PPH_TN_FILTER_ENTRY FilterEntry; +// begin_phapppub +} PH_MEMORY_CONTEXT, *PPH_MEMORY_CONTEXT; +// end_phapppub + +#define WM_PH_STATISTICS_UPDATE (WM_APP + 231) + +typedef struct _PH_STATISTICS_CONTEXT +{ + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + HWND WindowHandle; + HWND ListViewHandle; + BOOLEAN Enabled; + HANDLE ProcessHandle; + PPH_PROCESS_ITEM ProcessItem; +} PH_STATISTICS_CONTEXT, *PPH_STATISTICS_CONTEXT; + +#define WM_PH_PERFORMANCE_UPDATE (WM_APP + 241) + +typedef struct _PH_PERFORMANCE_CONTEXT +{ + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + + HWND WindowHandle; + + PH_GRAPH_STATE CpuGraphState; + PH_GRAPH_STATE PrivateGraphState; + PH_GRAPH_STATE IoGraphState; + + HWND CpuGraphHandle; + HWND PrivateGraphHandle; + HWND IoGraphHandle; +} PH_PERFORMANCE_CONTEXT, *PPH_PERFORMANCE_CONTEXT; + +typedef struct _PH_ENVIRONMENT_ITEM +{ + PPH_STRING Name; + PPH_STRING Value; +} PH_ENVIRONMENT_ITEM, *PPH_ENVIRONMENT_ITEM; + +typedef struct _PH_ENVIRONMENT_CONTEXT +{ + HWND WindowHandle; + HWND TreeNewHandle; + HWND SearchWindowHandle; + + PPH_PROCESS_ITEM ProcessItem; + PPH_STRING SearchboxText; + PPH_STRING StatusMessage; + + PPH_LIST NodeList; + PPH_LIST NodeRootList; + PPH_HASHTABLE NodeHashtable; + PPH_TN_FILTER_ENTRY TreeFilterEntry; + ULONG TreeNewSortColumn; + PH_TN_FILTER_SUPPORT TreeFilterSupport; + PH_SORT_ORDER TreeNewSortOrder; + PH_CM_MANAGER Cm; + + union + { + ULONG Flags; + struct + { + ULONG EnableStateHighlighting : 1; + ULONG HideProcessEnvironment : 1; + ULONG HideUserEnvironment : 1; + ULONG HideSystemEnvironment : 1; + ULONG HighlightProcessEnvironment : 1; + ULONG HighlightUserEnvironment : 1; + ULONG HighlightSystemEnvironment : 1; + ULONG HideCmdTypeEnvironment : 1; + ULONG HighlightCmdEnvironment : 1; + ULONG Spare : 23; + }; + }; + + PVOID SystemDefaultEnvironment; + PVOID UserDefaultEnvironment; + PH_ARRAY Items; +} PH_ENVIRONMENT_CONTEXT, *PPH_ENVIRONMENT_CONTEXT; + +#endif diff --git a/ProcessHacker/include/procprv.h b/ProcessHacker/include/procprv.h index f79546f534fd..ec48d83b810c 100644 --- a/ProcessHacker/include/procprv.h +++ b/ProcessHacker/include/procprv.h @@ -2,20 +2,12 @@ #define PH_PROCPRV_H #define PH_RECORD_MAX_USAGE -#define PH_ENABLE_VERIFY_CACHE extern PPH_OBJECT_TYPE PhProcessItemType; - -PHAPPAPI extern PH_CALLBACK PhProcessAddedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhProcessModifiedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhProcessRemovedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhProcessesUpdatedEvent; // phapppub - extern PPH_LIST PhProcessRecordList; extern PH_QUEUED_LOCK PhProcessRecordListLock; extern ULONG PhStatisticsSampleCount; -extern BOOLEAN PhEnableProcessQueryStage2; extern BOOLEAN PhEnablePurgeProcessRecords; extern BOOLEAN PhEnableCycleCpuUsage; @@ -128,14 +120,13 @@ typedef struct _PH_PROCESS_ITEM // Security - PPH_STRING UserName; + PSID Sid; TOKEN_ELEVATION_TYPE ElevationType; MANDATORY_LEVEL IntegrityLevel; PWSTR IntegrityString; // Other - PPH_STRING JobName; HANDLE ConsoleHostProcessId; // Signature, Packed @@ -159,18 +150,18 @@ typedef struct _PH_PROCESS_ITEM ULONG IsInJob : 1; ULONG IsInSignificantJob : 1; ULONG IsPacked : 1; - ULONG Reserved : 1; + ULONG IsHandleValid : 1; ULONG IsSuspended : 1; ULONG IsWow64 : 1; ULONG IsImmersive : 1; ULONG IsWow64Valid : 1; ULONG IsPartiallySuspended : 1; - ULONG AddedEventSent : 1; + ULONG IsProtectedHandle : 1; ULONG IsProtectedProcess : 1; ULONG IsSecureProcess : 1; ULONG IsSubsystemProcess : 1; - - ULONG Spare : 15; + ULONG IsControlFlowGuardEnabled : 1; + ULONG Spare : 14; }; }; @@ -217,7 +208,7 @@ typedef struct _PH_PROCESS_ITEM ULONG PeakNumberOfThreads; // since WIN7 ULONG HardFaultCount; // since WIN7 - ULONG SequenceNumber; + ULONG TimeSequenceNumber; PH_CIRCULAR_BUFFER_FLOAT CpuKernelHistory; PH_CIRCULAR_BUFFER_FLOAT CpuUserHistory; PH_CIRCULAR_BUFFER_ULONG64 IoReadHistory; @@ -229,8 +220,12 @@ typedef struct _PH_PROCESS_ITEM // New fields PH_UINTPTR_DELTA PrivateBytesDelta; PPH_STRING PackageFullName; + PPH_STRING UserName; - PH_QUEUED_LOCK RemoveLock; + ULONGLONG ProcessSequenceNumber; + PH_KNOWN_PROCESS_TYPE KnownProcessType; + PS_PROTECTION Protection; + ULONG JobObjectId; } PH_PROCESS_ITEM, *PPH_PROCESS_ITEM; // end_phapppub @@ -249,6 +244,7 @@ typedef struct _PH_PROCESS_RECORD HANDLE ProcessId; HANDLE ParentProcessId; ULONG SessionId; + ULONGLONG ProcessSequenceNumber; LARGE_INTEGER CreateTime; LARGE_INTEGER ExitTime; @@ -312,13 +308,13 @@ typedef struct _PH_VERIFY_FILE_INFO *PPH_VERIFY_FILE_INFO; VERIFY_RESULT PhVerifyFileWithAdditionalCatalog( _In_ PPH_VERIFY_FILE_INFO Information, - _In_opt_ PWSTR PackageFullName, + _In_opt_ PPH_STRING PackageFullName, _Out_opt_ PPH_STRING *SignerName ); VERIFY_RESULT PhVerifyFileCached( _In_ PPH_STRING FileName, - _In_opt_ PWSTR PackageFullName, + _In_opt_ PPH_STRING PackageFullName, _Out_opt_ PPH_STRING *SignerName, _In_ BOOLEAN CachedOnly ); @@ -343,7 +339,7 @@ PhGetStatisticsTimeString( // end_phapppub VOID PhFlushProcessQueryData( - _In_ BOOLEAN SendModifiedEvent + VOID ); VOID PhProcessProviderUpdate( @@ -397,9 +393,7 @@ PHAPPAPI PPH_PROCESS_ITEM NTAPI PhReferenceProcessItemForParent( - _In_ HANDLE ParentProcessId, - _In_ HANDLE ProcessId, - _In_ PLARGE_INTEGER CreateTime + _In_ PPH_PROCESS_ITEM ProcessItem ); PHAPPAPI diff --git a/ProcessHacker/include/proctree.h b/ProcessHacker/include/proctree.h index bce21d481390..ba807c3fff78 100644 --- a/ProcessHacker/include/proctree.h +++ b/ProcessHacker/include/proctree.h @@ -89,8 +89,14 @@ #define PHPRTLC_TIMESTAMP 76 #define PHPRTLC_FILEMODIFIEDTIME 77 #define PHPRTLC_FILESIZE 78 - -#define PHPRTLC_MAXIMUM 79 +#define PHPRTLC_SUBPROCESSCOUNT 79 +#define PHPRTLC_JOBOBJECTID 80 +#define PHPRTLC_PROTECTION 81 +#define PHPRTLC_DESKTOP 82 +#define PHPRTLC_CRITICAL 83 +#define PHPRTLC_PIDHEX 84 + +#define PHPRTLC_MAXIMUM 85 #define PHPRTLC_IOGROUP_COUNT 9 #define PHPN_WSCOUNTERS 0x1 @@ -105,6 +111,9 @@ #define PHPN_APPID 0x200 #define PHPN_DPIAWARENESS 0x400 #define PHPN_FILEATTRIBUTES 0x800 +#define PHPN_DESKTOPINFO 0x1000 +#define PHPN_USERNAME 0x2000 +#define PHPN_CRITICAL 0x4000 // begin_phapppub typedef struct _PH_PROCESS_NODE @@ -161,25 +170,25 @@ typedef struct _PH_PROCESS_NODE USHORT ImageDllCharacteristics; // App ID PPH_STRING AppIdText; - // Cycles (Vista only) - PH_UINT64_DELTA CyclesDelta; // DPI awareness ULONG DpiAwareness; // File attributes LARGE_INTEGER FileLastWriteTime; LARGE_INTEGER FileEndOfFile; + // Critical + BOOLEAN BreakOnTerminationEnabled; PPH_STRING TooltipText; - ULONG TooltipTextValidToTickCount; + ULONG64 TooltipTextValidToTickCount; // Text buffers - WCHAR CpuUsageText[PH_INT32_STR_LEN_1]; - PPH_STRING IoTotalRateText; - PPH_STRING PrivateBytesText; + WCHAR CpuUsageText[PH_INT32_STR_LEN_1 + 3]; + WCHAR IoTotalRateText[PH_INT32_STR_LEN_1 + 3]; + WCHAR PrivateBytesText[PH_INT32_STR_LEN_1]; PPH_STRING PeakPrivateBytesText; PPH_STRING WorkingSetText; PPH_STRING PeakWorkingSetText; - PPH_STRING PrivateWsText; + WCHAR PrivateWsText[PH_INT32_STR_LEN_1]; PPH_STRING SharedWsText; PPH_STRING ShareableWsText; PPH_STRING VirtualSizeText; @@ -215,6 +224,11 @@ typedef struct _PH_PROCESS_NODE PPH_STRING TimeStampText; PPH_STRING FileModifiedTimeText; PPH_STRING FileSizeText; + PPH_STRING SubprocessCountText; + WCHAR JobObjectIdText[PH_INT32_STR_LEN_1]; + PPH_STRING ProtectionText; + PPH_STRING DesktopInfoText; + WCHAR PidHexText[PH_PTR_STR_LEN_1]; // Graph buffers PH_GRAPH_BUFFERS CpuGraphBuffers; @@ -240,6 +254,16 @@ VOID PhSaveSettingsProcessTreeList( VOID ); +VOID PhLoadSettingsProcessTreeListEx( + _In_ PPH_STRING TreeListSettings, + _In_ PPH_STRING TreeSortSettings + ); + +VOID PhSaveSettingsProcessTreeListEx( + _Out_ PPH_STRING *TreeListSettings, + _Out_ PPH_STRING *TreeSortSettings + ); + VOID PhReloadSettingsProcessTreeList( VOID ); diff --git a/ProcessHacker/include/settings.h b/ProcessHacker/include/settings.h deleted file mode 100644 index 476961404c1e..000000000000 --- a/ProcessHacker/include/settings.h +++ /dev/null @@ -1,229 +0,0 @@ -#ifndef PH_SETTINGS_H -#define PH_SETTINGS_H - -// begin_phapppub -typedef enum _PH_SETTING_TYPE -{ - StringSettingType, - IntegerSettingType, - IntegerPairSettingType, - ScalableIntegerPairSettingType -} PH_SETTING_TYPE, PPH_SETTING_TYPE; -// end_phapppub - -typedef struct _PH_SETTING -{ - PH_SETTING_TYPE Type; - PH_STRINGREF Name; - PH_STRINGREF DefaultValue; - - union - { - PVOID Pointer; - ULONG Integer; - PH_INTEGER_PAIR IntegerPair; - } u; -} PH_SETTING, *PPH_SETTING; - -VOID PhSettingsInitialization( - VOID - ); - -VOID PhUpdateCachedSettings( - VOID - ); - -// begin_phapppub -_May_raise_ -PHAPPAPI -ULONG -NTAPI -PhGetIntegerSetting( - _In_ PWSTR Name - ); - -_May_raise_ -PHAPPAPI -PH_INTEGER_PAIR -NTAPI -PhGetIntegerPairSetting( - _In_ PWSTR Name - ); - -_May_raise_ -PHAPPAPI -PH_SCALABLE_INTEGER_PAIR -NTAPI -PhGetScalableIntegerPairSetting( - _In_ PWSTR Name, - _In_ BOOLEAN ScaleToCurrent - ); - -_May_raise_ -PHAPPAPI -PPH_STRING -NTAPI -PhGetStringSetting( - _In_ PWSTR Name - ); - -_May_raise_ -PHAPPAPI -VOID -NTAPI -PhSetIntegerSetting( - _In_ PWSTR Name, - _In_ ULONG Value - ); - -_May_raise_ -PHAPPAPI -VOID -NTAPI -PhSetIntegerPairSetting( - _In_ PWSTR Name, - _In_ PH_INTEGER_PAIR Value - ); - -_May_raise_ -PHAPPAPI -VOID -NTAPI -PhSetScalableIntegerPairSetting( - _In_ PWSTR Name, - _In_ PH_SCALABLE_INTEGER_PAIR Value - ); - -_May_raise_ -PHAPPAPI -VOID -NTAPI -PhSetScalableIntegerPairSetting2( - _In_ PWSTR Name, - _In_ PH_INTEGER_PAIR Value - ); - -_May_raise_ -PHAPPAPI -VOID -NTAPI -PhSetStringSetting( - _In_ PWSTR Name, - _In_ PWSTR Value - ); - -_May_raise_ -PHAPPAPI -VOID -NTAPI -PhSetStringSetting2( - _In_ PWSTR Name, - _In_ PPH_STRINGREF Value - ); -// end_phapppub - -VOID PhClearIgnoredSettings( - VOID - ); - -VOID PhConvertIgnoredSettings( - VOID - ); - -NTSTATUS PhLoadSettings( - _In_ PWSTR FileName - ); - -NTSTATUS PhSaveSettings( - _In_ PWSTR FileName - ); - -VOID PhResetSettings( - VOID - ); - -#define PhaGetStringSetting(Name) PH_AUTO_T(PH_STRING, PhGetStringSetting(Name)) // phapppub - -// begin_phapppub -// High-level settings creation - -typedef struct _PH_SETTING_CREATE -{ - PH_SETTING_TYPE Type; - PWSTR Name; - PWSTR DefaultValue; -} PH_SETTING_CREATE, *PPH_SETTING_CREATE; - -PHAPPAPI -VOID -NTAPI -PhAddSettings( - _In_ PPH_SETTING_CREATE Settings, - _In_ ULONG NumberOfSettings - ); -// end_phapppub - -// Cached settings - -#undef EXT - -#ifdef PH_SETTINGS_PRIVATE -#define EXT -#else -#define EXT extern -#endif - -EXT ULONG PhCsCollapseServicesOnStart; -EXT ULONG PhCsForceNoParent; -EXT ULONG PhCsHighlightingDuration; -EXT ULONG PhCsPropagateCpuUsage; -EXT ULONG PhCsScrollToNewProcesses; -EXT ULONG PhCsShowCpuBelow001; -EXT ULONG PhCsUpdateInterval; - -EXT ULONG PhCsColorNew; -EXT ULONG PhCsColorRemoved; -EXT ULONG PhCsUseColorOwnProcesses; -EXT ULONG PhCsColorOwnProcesses; -EXT ULONG PhCsUseColorSystemProcesses; -EXT ULONG PhCsColorSystemProcesses; -EXT ULONG PhCsUseColorServiceProcesses; -EXT ULONG PhCsColorServiceProcesses; -EXT ULONG PhCsUseColorJobProcesses; -EXT ULONG PhCsColorJobProcesses; -EXT ULONG PhCsUseColorWow64Processes; -EXT ULONG PhCsColorWow64Processes; -EXT ULONG PhCsUseColorDebuggedProcesses; -EXT ULONG PhCsColorDebuggedProcesses; -EXT ULONG PhCsUseColorElevatedProcesses; -EXT ULONG PhCsColorElevatedProcesses; -EXT ULONG PhCsUseColorPicoProcesses; -EXT ULONG PhCsColorPicoProcesses; -EXT ULONG PhCsUseColorImmersiveProcesses; -EXT ULONG PhCsColorImmersiveProcesses; -EXT ULONG PhCsUseColorSuspended; -EXT ULONG PhCsColorSuspended; -EXT ULONG PhCsUseColorDotNet; -EXT ULONG PhCsColorDotNet; -EXT ULONG PhCsUseColorPacked; -EXT ULONG PhCsColorPacked; -EXT ULONG PhCsUseColorGuiThreads; -EXT ULONG PhCsColorGuiThreads; -EXT ULONG PhCsUseColorRelocatedModules; -EXT ULONG PhCsColorRelocatedModules; -EXT ULONG PhCsUseColorProtectedHandles; -EXT ULONG PhCsColorProtectedHandles; -EXT ULONG PhCsUseColorInheritHandles; -EXT ULONG PhCsColorInheritHandles; -EXT ULONG PhCsGraphShowText; -EXT ULONG PhCsGraphColorMode; -EXT ULONG PhCsColorCpuKernel; -EXT ULONG PhCsColorCpuUser; -EXT ULONG PhCsColorIoReadOther; -EXT ULONG PhCsColorIoWrite; -EXT ULONG PhCsColorPrivate; -EXT ULONG PhCsColorPhysical; - -#define PH_SET_INTEGER_CACHED_SETTING(Name, Value) (PhSetIntegerSetting(L#Name, PhCs##Name = (Value))) - -#endif diff --git a/ProcessHacker/include/settingsp.h b/ProcessHacker/include/settingsp.h deleted file mode 100644 index 4d8350c8daa4..000000000000 --- a/ProcessHacker/include/settingsp.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef PH_SETTINGSP_H -#define PH_SETTINGSP_H - -#include - -BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI PhpSettingsHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpAddSetting( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_STRINGREF Name, - _In_ PPH_STRINGREF DefaultValue - ); - -ULONG PhpGetCurrentScale( - VOID - ); - -PPH_STRING PhpSettingToString( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_SETTING Setting - ); - -BOOLEAN PhpSettingFromString( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_STRINGREF StringRef, - _In_opt_ PPH_STRING String, - _Inout_ PPH_SETTING Setting - ); - -VOID PhpFreeSettingValue( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_SETTING Setting - ); - -PVOID PhpLookupSetting( - _In_ PPH_STRINGREF Name - ); - -#endif diff --git a/ProcessHacker/include/srvlist.h b/ProcessHacker/include/srvlist.h index 499823274b3b..193d8127adbc 100644 --- a/ProcessHacker/include/srvlist.h +++ b/ProcessHacker/include/srvlist.h @@ -17,8 +17,11 @@ #define PHSVTLC_GROUP 8 #define PHSVTLC_DESCRIPTION 9 #define PHSVTLC_KEYMODIFIEDTIME 10 +#define PHSVTLC_VERIFICATIONSTATUS 11 +#define PHSVTLC_VERIFIEDSIGNER 12 +#define PHSVTLC_FILENAME 13 -#define PHSVTLC_MAXIMUM 11 +#define PHSVTLC_MAXIMUM 14 #define PHSN_CONFIG 0x1 #define PHSN_DESCRIPTION 0x2 @@ -46,9 +49,7 @@ typedef struct _PH_SERVICE_NODE PPH_STRING Description; // Key LARGE_INTEGER KeyLastWriteTime; - PPH_STRING TooltipText; - PPH_STRING KeyModifiedTimeText; // begin_phapppub } PH_SERVICE_NODE, *PPH_SERVICE_NODE; diff --git a/ProcessHacker/include/srvprv.h b/ProcessHacker/include/srvprv.h index a01349d8005e..3b403b6db480 100644 --- a/ProcessHacker/include/srvprv.h +++ b/ProcessHacker/include/srvprv.h @@ -2,20 +2,20 @@ #define PH_SRVPRV_H extern PPH_OBJECT_TYPE PhServiceItemType; - -PHAPPAPI extern PH_CALLBACK PhServiceAddedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhServiceModifiedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhServiceRemovedEvent; // phapppub -PHAPPAPI extern PH_CALLBACK PhServicesUpdatedEvent; // phapppub - extern BOOLEAN PhEnableServiceNonPoll; // begin_phapppub +typedef enum _VERIFY_RESULT VERIFY_RESULT; + typedef struct _PH_SERVICE_ITEM { PH_STRINGREF Key; // points to Name PPH_STRING Name; PPH_STRING DisplayName; + PPH_STRING FileName; // only available after first update + + HICON SmallIcon; + HICON LargeIcon; // State ULONG Type; @@ -28,14 +28,25 @@ typedef struct _PH_SERVICE_ITEM ULONG StartType; ULONG ErrorControl; // end_phapppub - BOOLEAN DelayedStart; - BOOLEAN HasTriggers; - BOOLEAN PendingProcess; - BOOLEAN NeedsConfigUpdate; + union + { + BOOLEAN BitFlags; + struct + { + BOOLEAN DelayedStart : 1; + BOOLEAN HasTriggers : 1; + BOOLEAN PendingProcess : 1; + BOOLEAN NeedsConfigUpdate : 1; + BOOLEAN JustProcessed : 1; + BOOLEAN Spare : 3; + }; + }; +// begin_phapppub + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; WCHAR ProcessIdString[PH_INT32_STR_LEN_1]; -// begin_phapppub } PH_SERVICE_ITEM, *PPH_SERVICE_ITEM; // end_phapppub @@ -89,6 +100,10 @@ VOID PhUpdateProcessItemServices( _In_ PPH_PROCESS_ITEM ProcessItem ); +VOID PhQueueServiceQueryStage2( // HACK + _In_ PPH_SERVICE_ITEM ServiceItem + ); + VOID PhServiceProviderUpdate( _In_ PVOID Object ); diff --git a/ProcessHacker/include/sysinfo.h b/ProcessHacker/include/sysinfo.h index 88d2cf5fa8c4..4d7931ff1b18 100644 --- a/ProcessHacker/include/sysinfo.h +++ b/ProcessHacker/include/sysinfo.h @@ -1,164 +1,167 @@ -#ifndef PH_SYSINFO_H -#define PH_SYSINFO_H - -// begin_phapppub -typedef enum _PH_SYSINFO_VIEW_TYPE -{ - SysInfoSummaryView, - SysInfoSectionView -} PH_SYSINFO_VIEW_TYPE; - -typedef VOID (NTAPI *PPH_SYSINFO_COLOR_SETUP_FUNCTION)( - _Out_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ COLORREF Color1, - _In_ COLORREF Color2 - ); - -typedef struct _PH_SYSINFO_PARAMETERS -{ - HWND SysInfoWindowHandle; - HWND ContainerWindowHandle; - - HFONT Font; - HFONT MediumFont; - HFONT LargeFont; - ULONG FontHeight; - ULONG FontAverageWidth; - ULONG MediumFontHeight; - ULONG MediumFontAverageWidth; - COLORREF GraphBackColor; - COLORREF PanelForeColor; - PPH_SYSINFO_COLOR_SETUP_FUNCTION ColorSetupFunction; - - ULONG MinimumGraphHeight; - ULONG SectionViewGraphHeight; - ULONG PanelWidth; -// end_phapppub - - ULONG PanelPadding; - ULONG WindowPadding; - ULONG GraphPadding; - ULONG SmallGraphWidth; - ULONG SmallGraphPadding; - ULONG SeparatorWidth; - ULONG CpuPadding; - ULONG MemoryPadding; -// begin_phapppub -} PH_SYSINFO_PARAMETERS, *PPH_SYSINFO_PARAMETERS; - -typedef enum _PH_SYSINFO_SECTION_MESSAGE -{ - SysInfoCreate, - SysInfoDestroy, - SysInfoTick, - SysInfoViewChanging, // PH_SYSINFO_VIEW_TYPE Parameter1, PPH_SYSINFO_SECTION Parameter2 - SysInfoCreateDialog, // PPH_SYSINFO_CREATE_DIALOG Parameter1 - SysInfoGraphGetDrawInfo, // PPH_GRAPH_DRAW_INFO Parameter1 - SysInfoGraphGetTooltipText, // PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT Parameter1 - SysInfoGraphDrawPanel, // PPH_SYSINFO_DRAW_PANEL Parameter1 - MaxSysInfoMessage -} PH_SYSINFO_SECTION_MESSAGE; - -typedef BOOLEAN (NTAPI *PPH_SYSINFO_SECTION_CALLBACK)( - _In_ struct _PH_SYSINFO_SECTION *Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -typedef struct _PH_SYSINFO_CREATE_DIALOG -{ - BOOLEAN CustomCreate; - - // Parameters for default create - PVOID Instance; - PWSTR Template; - DLGPROC DialogProc; - PVOID Parameter; -} PH_SYSINFO_CREATE_DIALOG, *PPH_SYSINFO_CREATE_DIALOG; - -typedef struct _PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT -{ - ULONG Index; - PH_STRINGREF Text; -} PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT, *PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT; - -typedef struct _PH_SYSINFO_DRAW_PANEL -{ - HDC hdc; - RECT Rect; - BOOLEAN CustomDraw; - - // Parameters for default draw - PPH_STRING Title; - PPH_STRING SubTitle; - PPH_STRING SubTitleOverflow; -} PH_SYSINFO_DRAW_PANEL, *PPH_SYSINFO_DRAW_PANEL; -// end_phapppub - -// begin_phapppub -typedef struct _PH_SYSINFO_SECTION -{ - // Public - - // Initialization - PH_STRINGREF Name; - ULONG Flags; - PPH_SYSINFO_SECTION_CALLBACK Callback; - PVOID Context; - PVOID Reserved[3]; - - // State - HWND GraphHandle; - PH_GRAPH_STATE GraphState; - PPH_SYSINFO_PARAMETERS Parameters; - PVOID Reserved2[3]; -// end_phapppub - - // Private - - struct - { - ULONG GraphHot : 1; - ULONG PanelHot : 1; - ULONG HasFocus : 1; - ULONG HideFocus : 1; - ULONG SpareFlags : 28; - }; - HWND DialogHandle; - HWND PanelHandle; - ULONG PanelId; -// begin_phapppub -} PH_SYSINFO_SECTION, *PPH_SYSINFO_SECTION; -// end_phapppub - -VOID PhSiNotifyChangeSettings( - VOID - ); - -// begin_phapppub -PHAPPAPI -VOID -NTAPI -PhSiSetColorsGraphDrawInfo( - _Out_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ COLORREF Color1, - _In_ COLORREF Color2 - ); - -PHAPPAPI -PPH_STRING -NTAPI -PhSiSizeLabelYFunction( - _In_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ ULONG DataIndex, - _In_ FLOAT Value, - _In_ FLOAT Parameter - ); -// end_phapppub - -VOID PhShowSystemInformationDialog( - _In_opt_ PWSTR SectionName - ); - +#ifndef PH_SYSINFO_H +#define PH_SYSINFO_H + +// begin_phapppub +typedef enum _PH_SYSINFO_VIEW_TYPE +{ + SysInfoSummaryView, + SysInfoSectionView +} PH_SYSINFO_VIEW_TYPE; + +typedef VOID (NTAPI *PPH_SYSINFO_COLOR_SETUP_FUNCTION)( + _Out_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ COLORREF Color1, + _In_ COLORREF Color2 + ); + +typedef struct _PH_SYSINFO_PARAMETERS +{ + HWND SysInfoWindowHandle; + HWND ContainerWindowHandle; + + HFONT Font; + HFONT MediumFont; + HFONT LargeFont; + ULONG FontHeight; + ULONG FontAverageWidth; + ULONG MediumFontHeight; + ULONG MediumFontAverageWidth; + COLORREF GraphBackColor; + COLORREF PanelForeColor; + PPH_SYSINFO_COLOR_SETUP_FUNCTION ColorSetupFunction; + + ULONG MinimumGraphHeight; + ULONG SectionViewGraphHeight; + ULONG PanelWidth; +// end_phapppub + + ULONG PanelPadding; + ULONG WindowPadding; + ULONG GraphPadding; + ULONG SmallGraphWidth; + ULONG SmallGraphPadding; + ULONG SeparatorWidth; + ULONG CpuPadding; + ULONG MemoryPadding; +// begin_phapppub +} PH_SYSINFO_PARAMETERS, *PPH_SYSINFO_PARAMETERS; + +typedef enum _PH_SYSINFO_SECTION_MESSAGE +{ + SysInfoCreate, + SysInfoDestroy, + SysInfoTick, + SysInfoViewChanging, // PH_SYSINFO_VIEW_TYPE Parameter1, PPH_SYSINFO_SECTION Parameter2 + SysInfoCreateDialog, // PPH_SYSINFO_CREATE_DIALOG Parameter1 + SysInfoGraphGetDrawInfo, // PPH_GRAPH_DRAW_INFO Parameter1 + SysInfoGraphGetTooltipText, // PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT Parameter1 + SysInfoGraphDrawPanel, // PPH_SYSINFO_DRAW_PANEL Parameter1 + MaxSysInfoMessage +} PH_SYSINFO_SECTION_MESSAGE; + +typedef BOOLEAN (NTAPI *PPH_SYSINFO_SECTION_CALLBACK)( + _In_ struct _PH_SYSINFO_SECTION *Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +typedef struct _PH_SYSINFO_CREATE_DIALOG +{ + BOOLEAN CustomCreate; + + // Parameters for default create + PVOID Instance; + PWSTR Template; + DLGPROC DialogProc; + PVOID Parameter; +} PH_SYSINFO_CREATE_DIALOG, *PPH_SYSINFO_CREATE_DIALOG; + +typedef struct _PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT +{ + ULONG Index; + PH_STRINGREF Text; +} PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT, *PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT; + +typedef struct _PH_SYSINFO_DRAW_PANEL +{ + HDC hdc; + RECT Rect; + BOOLEAN CustomDraw; + + // Parameters for default draw + PPH_STRING Title; + PPH_STRING SubTitle; + PPH_STRING SubTitleOverflow; +} PH_SYSINFO_DRAW_PANEL, *PPH_SYSINFO_DRAW_PANEL; +// end_phapppub + +// begin_phapppub +typedef struct _PH_SYSINFO_SECTION +{ + // Public + + // Initialization + PH_STRINGREF Name; + ULONG Flags; + PPH_SYSINFO_SECTION_CALLBACK Callback; + PVOID Context; + PVOID Reserved[3]; + + // State + HWND GraphHandle; + PH_GRAPH_STATE GraphState; + PPH_SYSINFO_PARAMETERS Parameters; + PVOID Reserved2[3]; +// end_phapppub + + // Private + + struct + { + ULONG GraphHot : 1; + ULONG PanelHot : 1; + ULONG HasFocus : 1; + ULONG HideFocus : 1; + ULONG SpareFlags : 28; + }; + HWND DialogHandle; + HWND PanelHandle; + ULONG PanelId; + + WNDPROC GraphWindowProc; + WNDPROC PanelWindowProc; +// begin_phapppub +} PH_SYSINFO_SECTION, *PPH_SYSINFO_SECTION; +// end_phapppub + +VOID PhSiNotifyChangeSettings( + VOID + ); + +// begin_phapppub +PHAPPAPI +VOID +NTAPI +PhSiSetColorsGraphDrawInfo( + _Out_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ COLORREF Color1, + _In_ COLORREF Color2 + ); + +PHAPPAPI +PPH_STRING +NTAPI +PhSiSizeLabelYFunction( + _In_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ ULONG DataIndex, + _In_ FLOAT Value, + _In_ FLOAT Parameter + ); +// end_phapppub + +VOID PhShowSystemInformationDialog( + _In_opt_ PWSTR SectionName + ); + #endif \ No newline at end of file diff --git a/ProcessHacker/include/sysinfop.h b/ProcessHacker/include/sysinfop.h index 01897f9ce828..fe52c0660b92 100644 --- a/ProcessHacker/include/sysinfop.h +++ b/ProcessHacker/include/sysinfop.h @@ -1,439 +1,419 @@ -#ifndef PH_SYSINFOP_H -#define PH_SYSINFOP_H - -// Constants - -#define PH_SYSINFO_FADE_ADD 50 -#define PH_SYSINFO_PANEL_PADDING 3 -#define PH_SYSINFO_WINDOW_PADDING 13 -#define PH_SYSINFO_GRAPH_PADDING 9 -#define PH_SYSINFO_SMALL_GRAPH_WIDTH 48 -#define PH_SYSINFO_SMALL_GRAPH_PADDING 5 -#define PH_SYSINFO_SEPARATOR_WIDTH 2 - -#define PH_SYSINFO_CPU_PADDING 5 -#define PH_SYSINFO_MEMORY_PADDING 3 - -#define SI_MSG_SYSINFO_FIRST (WM_APP + 150) -#define SI_MSG_SYSINFO_ACTIVATE (WM_APP + 150) -#define SI_MSG_SYSINFO_UPDATE (WM_APP + 151) -#define SI_MSG_SYSINFO_CHANGE_SETTINGS (WM_APP + 152) -#define SI_MSG_SYSINFO_LAST (WM_APP + 152) - -// Thread & window - -extern HWND PhSipWindow; - -NTSTATUS PhSipSysInfoThreadStart( - _In_ PVOID Parameter - ); - -INT_PTR CALLBACK PhSipSysInfoDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhSipContainerDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// Event handlers - -VOID PhSipOnInitDialog( - VOID - ); - -VOID PhSipOnDestroy( - VOID - ); - -VOID PhSipOnNcDestroy( - VOID - ); - -VOID PhSipOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ); - -BOOLEAN PhSipOnSysCommand( - _In_ ULONG Type, - _In_ LONG CursorScreenX, - _In_ LONG CursorScreenY - ); - -VOID PhSipOnSize( - VOID - ); - -VOID PhSipOnSizing( - _In_ ULONG Edge, - _In_ PRECT DragRectangle - ); - -VOID PhSipOnThemeChanged( - VOID - ); - -VOID PhSipOnCommand( - _In_ ULONG Id, - _In_ ULONG Code - ); - -BOOLEAN PhSipOnNotify( - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ); - -BOOLEAN PhSipOnDrawItem( - _In_ ULONG_PTR Id, - _In_ DRAWITEMSTRUCT *DrawItemStruct - ); - -VOID PhSipOnUserMessage( - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ); - -// Framework - -VOID PhSipRegisterDialog( - _In_ HWND DialogWindowHandle - ); - -VOID PhSipUnregisterDialog( - _In_ HWND DialogWindowHandle - ); - -VOID PhSipInitializeParameters( - VOID - ); - -VOID PhSipDeleteParameters( - VOID - ); - -VOID PhSipUpdateColorParameters( - VOID - ); - -PPH_SYSINFO_SECTION PhSipCreateSection( - _In_ PPH_SYSINFO_SECTION Template - ); - -VOID PhSipDestroySection( - _In_ PPH_SYSINFO_SECTION Section - ); - -PPH_SYSINFO_SECTION PhSipFindSection( - _In_ PPH_STRINGREF Name - ); - -PPH_SYSINFO_SECTION PhSipCreateInternalSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_SYSINFO_SECTION_CALLBACK Callback - ); - -VOID PhSipDrawRestoreSummaryPanel( - _In_ HDC hdc, - _In_ PRECT Rect - ); - -VOID PhSipDrawSeparator( - _In_ HDC hdc, - _In_ PRECT Rect - ); - -VOID PhSipDrawPanel( - _In_ PPH_SYSINFO_SECTION Section, - _In_ HDC hdc, - _In_ PRECT Rect - ); - -VOID PhSipDefaultDrawPanel( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PPH_SYSINFO_DRAW_PANEL DrawPanel - ); - -VOID PhSipLayoutSummaryView( - VOID - ); - -VOID PhSipLayoutSectionView( - VOID - ); - -VOID PhSipEnterSectionView( - _In_ PPH_SYSINFO_SECTION NewSection - ); - -VOID PhSipEnterSectionViewInner( - _In_ PPH_SYSINFO_SECTION Section, - _In_ BOOLEAN FromSummaryView, - _Inout_ HDWP *DeferHandle, - _Inout_ HDWP *ContainerDeferHandle - ); - -VOID PhSipRestoreSummaryView( - VOID - ); - -VOID PhSipCreateSectionDialog( - _In_ PPH_SYSINFO_SECTION Section - ); - -LRESULT CALLBACK PhSipGraphHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -LRESULT CALLBACK PhSipPanelHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -// Misc. - -VOID PhSipUpdateThemeData( - VOID - ); - -VOID PhSipSetAlwaysOnTop( - VOID - ); - -VOID PhSipSaveWindowState( - VOID - ); - -VOID NTAPI PhSipSysInfoUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -PPH_STRING PhSipFormatSizeWithPrecision( - _In_ ULONG64 Size, - _In_ USHORT Precision - ); - -// CPU section - -typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT_WIN8 -{ - ULONG Hits; - UCHAR PercentFrequency; -} SYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT_WIN8, *PSYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT_WIN8; - -BOOLEAN PhSipCpuSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID PhSipInitializeCpuDialog( - VOID - ); - -VOID PhSipUninitializeCpuDialog( - VOID - ); - -VOID PhSipTickCpuDialog( - VOID - ); - -INT_PTR CALLBACK PhSipCpuDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhSipCpuPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhSipCreateCpuGraphs( - VOID - ); - -VOID PhSipLayoutCpuGraphs( - VOID - ); - -VOID PhSipSetOneGraphPerCpu( - VOID - ); - -VOID PhSipNotifyCpuGraph( - _In_ ULONG Index, - _In_ NMHDR *Header - ); - -VOID PhSipUpdateCpuGraphs( - VOID - ); - -VOID PhSipUpdateCpuPanel( - VOID - ); - -PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord( - _In_ LONG Index - ); - -PPH_STRING PhSipGetMaxCpuString( - _In_ LONG Index - ); - -VOID PhSipGetCpuBrandString( - _Out_writes_(49) PWSTR BrandString - ); - -BOOLEAN PhSipGetCpuFrequencyFromDistribution( - _Out_ DOUBLE *Fraction - ); - -NTSTATUS PhSipQueryProcessorPerformanceDistribution( - _Out_ PVOID *Buffer - ); - -// Memory section - -BOOLEAN PhSipMemorySectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID PhSipInitializeMemoryDialog( - VOID - ); - -VOID PhSipUninitializeMemoryDialog( - VOID - ); - -VOID PhSipTickMemoryDialog( - VOID - ); - -INT_PTR CALLBACK PhSipMemoryDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhSipMemoryPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhSipLayoutMemoryGraphs( - VOID - ); - -VOID PhSipNotifyCommitGraph( - _In_ NMHDR *Header - ); - -VOID PhSipNotifyPhysicalGraph( - _In_ NMHDR *Header - ); - -VOID PhSipUpdateMemoryGraphs( - VOID - ); - -VOID PhSipUpdateMemoryPanel( - VOID - ); - -NTSTATUS PhSipLoadMmAddresses( - _In_ PVOID Parameter - ); - -VOID PhSipGetPoolLimits( - _Out_ PSIZE_T Paged, - _Out_ PSIZE_T NonPaged - ); - -// I/O section - -BOOLEAN PhSipIoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID PhSipInitializeIoDialog( - VOID - ); - -VOID PhSipUninitializeIoDialog( - VOID - ); - -VOID PhSipTickIoDialog( - VOID - ); - -INT_PTR CALLBACK PhSipIoDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhSipIoPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhSipNotifyIoGraph( - _In_ NMHDR *Header - ); - -VOID PhSipUpdateIoGraph( - VOID - ); - -VOID PhSipUpdateIoPanel( - VOID - ); - -PPH_PROCESS_RECORD PhSipReferenceMaxIoRecord( - _In_ LONG Index - ); - -PPH_STRING PhSipGetMaxIoString( - _In_ LONG Index - ); - -#endif +#ifndef PH_SYSINFOP_H +#define PH_SYSINFOP_H + +// Constants + +#define PH_SYSINFO_FADE_ADD 50 +#define PH_SYSINFO_PANEL_PADDING 3 +#define PH_SYSINFO_WINDOW_PADDING 7 +#define PH_SYSINFO_GRAPH_PADDING 4 +#define PH_SYSINFO_SMALL_GRAPH_WIDTH 48 +#define PH_SYSINFO_SMALL_GRAPH_PADDING 5 +#define PH_SYSINFO_SEPARATOR_WIDTH 2 + +#define PH_SYSINFO_CPU_PADDING 5 +#define PH_SYSINFO_MEMORY_PADDING 3 + +#define SI_MSG_SYSINFO_FIRST (WM_APP + 150) +#define SI_MSG_SYSINFO_ACTIVATE (WM_APP + 150) +#define SI_MSG_SYSINFO_UPDATE (WM_APP + 151) +#define SI_MSG_SYSINFO_CHANGE_SETTINGS (WM_APP + 152) +#define SI_MSG_SYSINFO_LAST (WM_APP + 152) + +// Thread & window + +extern HWND PhSipWindow; + +NTSTATUS PhSipSysInfoThreadStart( + _In_ PVOID Parameter + ); + +INT_PTR CALLBACK PhSipSysInfoDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhSipContainerDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// Event handlers + +VOID PhSipOnInitDialog( + VOID + ); + +VOID PhSipOnDestroy( + VOID + ); + +VOID PhSipOnNcDestroy( + VOID + ); + +BOOLEAN PhSipOnSysCommand( + _In_ ULONG Type, + _In_ LONG CursorScreenX, + _In_ LONG CursorScreenY + ); + +VOID PhSipOnSize( + VOID + ); + +VOID PhSipOnSizing( + _In_ ULONG Edge, + _In_ PRECT DragRectangle + ); + +VOID PhSipOnThemeChanged( + VOID + ); + +VOID PhSipOnCommand( + _In_ HWND HwndControl, + _In_ ULONG Id, + _In_ ULONG Code + ); + +BOOLEAN PhSipOnNotify( + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ); + +BOOLEAN PhSipOnDrawItem( + _In_ ULONG_PTR Id, + _In_ PDRAWITEMSTRUCT DrawItemStruct + ); + +VOID PhSipOnUserMessage( + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ); + +// Framework + +VOID PhSipRegisterDialog( + _In_ HWND DialogWindowHandle + ); + +VOID PhSipUnregisterDialog( + _In_ HWND DialogWindowHandle + ); + +VOID PhSipInitializeParameters( + VOID + ); + +VOID PhSipDeleteParameters( + VOID + ); + +VOID PhSipUpdateColorParameters( + VOID + ); + +PPH_SYSINFO_SECTION PhSipCreateSection( + _In_ PPH_SYSINFO_SECTION Template + ); + +VOID PhSipDestroySection( + _In_ PPH_SYSINFO_SECTION Section + ); + +PPH_SYSINFO_SECTION PhSipFindSection( + _In_ PPH_STRINGREF Name + ); + +PPH_SYSINFO_SECTION PhSipCreateInternalSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_SYSINFO_SECTION_CALLBACK Callback + ); + +VOID PhSipDrawRestoreSummaryPanel( + _In_ PDRAWITEMSTRUCT DrawItemStruct + ); + +VOID PhSipDrawSeparator( + _In_ PDRAWITEMSTRUCT DrawItemStruct + ); + +VOID PhSipDrawPanel( + _In_ PPH_SYSINFO_SECTION Section, + _In_ HDC hdc, + _In_ PRECT Rect + ); + +VOID PhSipDefaultDrawPanel( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PPH_SYSINFO_DRAW_PANEL DrawPanel + ); + +VOID PhSipLayoutSummaryView( + VOID + ); + +VOID PhSipLayoutSectionView( + VOID + ); + +VOID PhSipEnterSectionView( + _In_ PPH_SYSINFO_SECTION NewSection + ); + +VOID PhSipEnterSectionViewInner( + _In_ PPH_SYSINFO_SECTION Section, + _In_ BOOLEAN FromSummaryView, + _Inout_ HDWP *DeferHandle, + _Inout_ HDWP *ContainerDeferHandle + ); + +VOID PhSipRestoreSummaryView( + VOID + ); + +VOID PhSipCreateSectionDialog( + _In_ PPH_SYSINFO_SECTION Section + ); + +LRESULT CALLBACK PhSipGraphHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +LRESULT CALLBACK PhSipPanelHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// Misc. + +VOID PhSipUpdateThemeData( + VOID + ); + +VOID PhSipSaveWindowState( + VOID + ); + +VOID NTAPI PhSipSysInfoUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +PPH_STRING PhSipFormatSizeWithPrecision( + _In_ ULONG64 Size, + _In_ USHORT Precision + ); + +// CPU section + +BOOLEAN PhSipCpuSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID PhSipInitializeCpuDialog( + VOID + ); + +VOID PhSipUninitializeCpuDialog( + VOID + ); + +VOID PhSipTickCpuDialog( + VOID + ); + +INT_PTR CALLBACK PhSipCpuDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhSipCpuPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhSipCreateCpuGraphs( + VOID + ); + +VOID PhSipLayoutCpuGraphs( + VOID + ); + +VOID PhSipSetOneGraphPerCpu( + VOID + ); + +VOID PhSipNotifyCpuGraph( + _In_ ULONG Index, + _In_ NMHDR *Header + ); + +VOID PhSipUpdateCpuGraphs( + VOID + ); + +VOID PhSipUpdateCpuPanel( + VOID + ); + +PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord( + _In_ LONG Index + ); + +PPH_STRING PhSipGetMaxCpuString( + _In_ LONG Index + ); + +VOID PhSipGetCpuBrandString( + _Out_writes_(49) PWSTR BrandString + ); + +BOOLEAN PhSipGetCpuFrequencyFromDistribution( + _Out_ DOUBLE *Fraction + ); + +NTSTATUS PhSipQueryProcessorPerformanceDistribution( + _Out_ PVOID *Buffer + ); + +// Memory section + +BOOLEAN PhSipMemorySectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID PhSipInitializeMemoryDialog( + VOID + ); + +VOID PhSipUninitializeMemoryDialog( + VOID + ); + +VOID PhSipTickMemoryDialog( + VOID + ); + +INT_PTR CALLBACK PhSipMemoryDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhSipMemoryPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhSipLayoutMemoryGraphs( + VOID + ); + +VOID PhSipNotifyCommitGraph( + _In_ NMHDR *Header + ); + +VOID PhSipNotifyPhysicalGraph( + _In_ NMHDR *Header + ); + +VOID PhSipUpdateMemoryGraphs( + VOID + ); + +VOID PhSipUpdateMemoryPanel( + VOID + ); + +NTSTATUS PhSipLoadMmAddresses( + _In_ PVOID Parameter + ); + +VOID PhSipGetPoolLimits( + _Out_ PSIZE_T Paged, + _Out_ PSIZE_T NonPaged + ); + +// I/O section + +BOOLEAN PhSipIoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID PhSipInitializeIoDialog( + VOID + ); + +VOID PhSipUninitializeIoDialog( + VOID + ); + +VOID PhSipTickIoDialog( + VOID + ); + +INT_PTR CALLBACK PhSipIoDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhSipIoPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhSipNotifyIoGraph( + _In_ NMHDR *Header + ); + +VOID PhSipUpdateIoGraph( + VOID + ); + +VOID PhSipUpdateIoPanel( + VOID + ); + +PPH_PROCESS_RECORD PhSipReferenceMaxIoRecord( + _In_ LONG Index + ); + +PPH_STRING PhSipGetMaxIoString( + _In_ LONG Index + ); + +#endif diff --git a/ProcessHacker/include/thrdlist.h b/ProcessHacker/include/thrdlist.h index f1132eb4549b..a64ffc790787 100644 --- a/ProcessHacker/include/thrdlist.h +++ b/ProcessHacker/include/thrdlist.h @@ -6,14 +6,41 @@ // Columns -#define PHTHTLC_TID 0 -#define PHTHTLC_CPU 1 -#define PHTHTLC_CYCLESDELTA 2 -#define PHTHTLC_STARTADDRESS 3 -#define PHTHTLC_PRIORITY 4 -#define PHTHTLC_SERVICE 5 - -#define PHTHTLC_MAXIMUM 6 +typedef enum _PH_THREAD_TREELIST_COLUMN +{ + PH_THREAD_TREELIST_COLUMN_TID, + PH_THREAD_TREELIST_COLUMN_CPU, + PH_THREAD_TREELIST_COLUMN_CYCLESDELTA, + PH_THREAD_TREELIST_COLUMN_STARTADDRESS, + PH_THREAD_TREELIST_COLUMN_PRIORITYSYMBOLIC, + PH_THREAD_TREELIST_COLUMN_SERVICE, + PH_THREAD_TREELIST_COLUMN_NAME, + PH_THREAD_TREELIST_COLUMN_STARTED, + PH_THREAD_TREELIST_COLUMN_STARTMODULE, + PH_THREAD_TREELIST_COLUMN_CONTEXTSWITCHES, + PH_THREAD_TREELIST_COLUMN_PRIORITY, + PH_THREAD_TREELIST_COLUMN_BASEPRIORITY, + PH_THREAD_TREELIST_COLUMN_PAGEPRIORITY, + PH_THREAD_TREELIST_COLUMN_IOPRIORITY, + PH_THREAD_TREELIST_COLUMN_CYCLES, + PH_THREAD_TREELIST_COLUMN_STATE, + PH_THREAD_TREELIST_COLUMN_KERNELTIME, + PH_THREAD_TREELIST_COLUMN_USERTIME, + PH_THREAD_TREELIST_COLUMN_IDEALPROCESSOR, + PH_THREAD_TREELIST_COLUMN_CRITICAL, + PH_THREAD_TREELIST_COLUMN_TIDHEX, + PH_THREAD_TREELIST_COLUMN_MAXIMUM +} PH_THREAD_TREELIST_COLUMN; + +typedef enum _PH_THREAD_TREELIST_MENUITEM +{ + PH_THREAD_TREELIST_MENUITEM_HIDE_SUSPENDED = 1, + PH_THREAD_TREELIST_MENUITEM_HIDE_GUITHREADS, + PH_THREAD_TREELIST_MENUITEM_HIDE_UNKNOWNSTARTADDRESS, + PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_SUSPENDED, + PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_GUITHREADS, + PH_THREAD_TREELIST_MENUITEM_MAXIMUM +} PH_THREAD_TREELIST_MENUITEM; // begin_phapppub typedef struct _PH_THREAD_NODE @@ -26,14 +53,29 @@ typedef struct _PH_THREAD_NODE PPH_THREAD_ITEM ThreadItem; // end_phapppub - PH_STRINGREF TextCache[PHTHTLC_MAXIMUM]; + PH_STRINGREF TextCache[PH_THREAD_TREELIST_COLUMN_MAXIMUM]; ULONG ValidMask; + ULONG PagePriority; + IO_PRIORITY_HINT IoPriority; + BOOLEAN BreakOnTermination; + WCHAR ThreadIdText[PH_INT32_STR_LEN_1]; WCHAR CpuUsageText[PH_INT32_STR_LEN_1]; PPH_STRING CyclesDeltaText; // used for Context Switches Delta as well PPH_STRING StartAddressText; - PPH_STRING PriorityText; + PPH_STRING PrioritySymbolicText; + PPH_STRING CreatedText; + PPH_STRING NameText; + PPH_STRING StateText; + WCHAR ContextSwitchesText[PH_INT64_STR_LEN_1]; + WCHAR PriorityText[PH_INT32_STR_LEN_1]; + WCHAR BasePriorityText[PH_INT32_STR_LEN_1]; + WCHAR CyclesText[PH_INT64_STR_LEN_1]; + WCHAR KernelTimeText[PH_TIMESPAN_STR_LEN_1]; + WCHAR UserTimeText[PH_TIMESPAN_STR_LEN_1]; + WCHAR IdealProcessorText[PH_INT32_STR_LEN + 1 + PH_INT32_STR_LEN + 1]; + WCHAR ThreadIdHexText[PH_INT32_STR_LEN_1]; // begin_phapppub } PH_THREAD_NODE, *PPH_THREAD_NODE; // end_phapppub @@ -45,14 +87,29 @@ typedef struct _PH_THREAD_LIST_CONTEXT ULONG TreeNewSortColumn; PH_SORT_ORDER TreeNewSortOrder; PH_CM_MANAGER Cm; - BOOLEAN UseCycleTime; - BOOLEAN HasServices; PPH_HASHTABLE NodeHashtable; PPH_LIST NodeList; - - BOOLEAN EnableStateHighlighting; PPH_POINTER_LIST NodeStateList; + PH_TN_FILTER_SUPPORT TreeFilterSupport; + + union + { + ULONG Flags; + struct + { + ULONG EnableStateHighlighting : 1; + ULONG UseCycleTime : 1; + ULONG HasServices : 1; + + ULONG HideSuspended : 1; + ULONG HideGuiThreads : 1; + ULONG HighlightSuspended : 1; + ULONG HighlightGuiThreads : 1; + + ULONG Spare : 25; + }; + }; } PH_THREAD_LIST_CONTEXT, *PPH_THREAD_LIST_CONTEXT; VOID PhInitializeThreadList( @@ -73,6 +130,11 @@ VOID PhSaveSettingsThreadList( _Inout_ PPH_THREAD_LIST_CONTEXT Context ); +VOID PhSetOptionsThreadList( + _Inout_ PPH_THREAD_LIST_CONTEXT Context, + _In_ ULONG Options + ); + PPH_THREAD_NODE PhAddThreadNode( _Inout_ PPH_THREAD_LIST_CONTEXT Context, _In_ PPH_THREAD_ITEM ThreadItem, diff --git a/ProcessHacker/include/thrdprv.h b/ProcessHacker/include/thrdprv.h index ef6fe42f6cfa..6eb0b11f8634 100644 --- a/ProcessHacker/include/thrdprv.h +++ b/ProcessHacker/include/thrdprv.h @@ -12,30 +12,29 @@ typedef struct _PH_THREAD_ITEM LARGE_INTEGER CreateTime; LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; - - FLOAT CpuUsage; PH_UINT64_DELTA CpuKernelDelta; PH_UINT64_DELTA CpuUserDelta; - PH_UINT32_DELTA ContextSwitchesDelta; PH_UINT64_DELTA CyclesDelta; + + FLOAT CpuUsage; LONG Priority; LONG BasePriority; - ULONG64 StartAddress; - PPH_STRING StartAddressString; - PPH_STRING StartAddressFileName; - enum _PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel; KTHREAD_STATE State; KWAIT_REASON WaitReason; LONG BasePriorityIncrement; - PPH_STRING ServiceName; HANDLE ThreadHandle; + PPH_STRING ServiceName; + + ULONG64 StartAddress; + PPH_STRING StartAddressString; + PPH_STRING StartAddressFileName; + enum _PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel; + BOOLEAN IsGuiThread; BOOLEAN JustResolved; - - WCHAR ThreadIdString[PH_INT32_STR_LEN_1]; } PH_THREAD_ITEM, *PPH_THREAD_ITEM; typedef enum _PH_KNOWN_PROCESS_TYPE PH_KNOWN_PROCESS_TYPE; @@ -65,10 +64,6 @@ typedef struct _PH_THREAD_PROVIDER } PH_THREAD_PROVIDER, *PPH_THREAD_PROVIDER; // end_phapppub -BOOLEAN PhThreadProviderInitialization( - VOID - ); - PPH_THREAD_PROVIDER PhCreateThreadProvider( _In_ HANDLE ProcessId ); diff --git a/ProcessHacker/infodlg.c b/ProcessHacker/infodlg.c index 10584a25dfc1..7e68454fa77c 100644 --- a/ProcessHacker/infodlg.c +++ b/ProcessHacker/infodlg.c @@ -1,215 +1,216 @@ -/* - * Process Hacker - - * information dialog - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -typedef struct _INFORMATION_CONTEXT -{ - PWSTR String; - ULONG Flags; -} INFORMATION_CONTEXT, *PINFORMATION_CONTEXT; - -static RECT MinimumSize = { -1, -1, -1, -1 }; - -static INT_PTR CALLBACK PhpInformationDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PINFORMATION_CONTEXT context = (PINFORMATION_CONTEXT)lParam; - PPH_LAYOUT_MANAGER layoutManager; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetDlgItemText(hwndDlg, IDC_TEXT, context->String); - - layoutManager = PhAllocate(sizeof(PH_LAYOUT_MANAGER)); - PhInitializeLayoutManager(layoutManager, hwndDlg); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDOK), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - if (MinimumSize.left == -1) - { - RECT rect; - - rect.left = 0; - rect.top = 0; - rect.right = 200; - rect.bottom = 140; - MapDialogRect(hwndDlg, &rect); - MinimumSize = rect; - MinimumSize.left = 0; - } - - SetProp(hwndDlg, L"LayoutManager", (HANDLE)layoutManager); - SetProp(hwndDlg, L"String", (HANDLE)context->String); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (LPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); - } - break; - case WM_DESTROY: - { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhDeleteLayoutManager(layoutManager); - PhFree(layoutManager); - RemoveProp(hwndDlg, L"String"); - RemoveProp(hwndDlg, L"LayoutManager"); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_COPY: - { - HWND editControl; - LONG selStart; - LONG selEnd; - PWSTR buffer; - PH_STRINGREF string; - - editControl = GetDlgItem(hwndDlg, IDC_TEXT); - SendMessage(editControl, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd); - buffer = (PWSTR)GetProp(hwndDlg, L"String"); - - if (selStart == selEnd) - { - // Select and copy the entire string. - PhInitializeStringRefLongHint(&string, buffer); - Edit_SetSel(editControl, 0, -1); - } - else - { - string.Buffer = buffer + selStart; - string.Length = (selEnd - selStart) * 2; - } - - PhSetClipboardString(hwndDlg, &string); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)editControl, TRUE); - } - break; - case IDC_SAVE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Text files (*.txt)", L"*.txt" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - - fileDialog = PhCreateSaveFileDialog(); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, L"Information.txt"); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - NTSTATUS status; - PPH_STRING fileName; - PPH_FILE_STREAM fileStream; - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - - if (NT_SUCCESS(status = PhCreateFileStream( - &fileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - 0 - ))) - { - PH_STRINGREF string; - - PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); - PhInitializeStringRef(&string, (PWSTR)GetProp(hwndDlg, L"String")); - PhWriteStringAsUtf8FileStream(fileStream, &string); - PhDereferenceObject(fileStream); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - case WM_SIZE: - { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhLayoutManagerLayout(layoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - } - - return FALSE; -} - -VOID PhShowInformationDialog( - _In_ HWND ParentWindowHandle, - _In_ PWSTR String, - _Reserved_ ULONG Flags - ) -{ - INFORMATION_CONTEXT context; - - context.String = String; - context.Flags = Flags; - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_INFORMATION), - ParentWindowHandle, - PhpInformationDlgProc, - (LPARAM)&context - ); -} +/* + * Process Hacker - + * information dialog + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +typedef struct _INFORMATION_CONTEXT +{ + PWSTR String; + ULONG Flags; + PH_LAYOUT_MANAGER LayoutManager; +} INFORMATION_CONTEXT, *PINFORMATION_CONTEXT; + +static RECT MinimumSize = { -1, -1, -1, -1 }; + +static INT_PTR CALLBACK PhpInformationDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PINFORMATION_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PINFORMATION_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 200; + rect.bottom = 140; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + + PhSetDialogItemText(hwndDlg, IDC_TEXT, context->String); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_COPY: + { + HWND editControl; + LONG selStart; + LONG selEnd; + PH_STRINGREF string; + + editControl = GetDlgItem(hwndDlg, IDC_TEXT); + SendMessage(editControl, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd); + + if (selStart == selEnd) + { + // Select and copy the entire string. + PhInitializeStringRefLongHint(&string, context->String); + Edit_SetSel(editControl, 0, -1); + } + else + { + string.Buffer = context->String + selStart; + string.Length = (selEnd - selStart) * 2; + } + + PhSetClipboardString(hwndDlg, &string); + + PhSetDialogFocus(hwndDlg, editControl); + } + break; + case IDC_SAVE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Text files (*.txt)", L"*.txt" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + + fileDialog = PhCreateSaveFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, L"Information.txt"); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + NTSTATUS status; + PPH_STRING fileName; + PPH_FILE_STREAM fileStream; + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + + if (NT_SUCCESS(status = PhCreateFileStream( + &fileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + 0 + ))) + { + PH_STRINGREF string; + + PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); + PhInitializeStringRef(&string, context->String); + PhWriteStringAsUtf8FileStream(fileStream, &string); + PhDereferenceObject(fileStream); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + } + + return FALSE; +} + +VOID PhShowInformationDialog( + _In_ HWND ParentWindowHandle, + _In_ PWSTR String, + _Reserved_ ULONG Flags + ) +{ + INFORMATION_CONTEXT context; + + context.String = String; + context.Flags = Flags; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_INFORMATION), + ParentWindowHandle, + PhpInformationDlgProc, + (LPARAM)&context + ); +} diff --git a/ProcessHacker/itemtips.c b/ProcessHacker/itemtips.c index 184cdb7c2239..b82229aed72d 100644 --- a/ProcessHacker/itemtips.c +++ b/ProcessHacker/itemtips.c @@ -1,704 +1,827 @@ -/* - * Process Hacker - - * item tooltips - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#define CINTERFACE -#define COBJMACROS -#include - -#include -#include - -#include -#include -#include - -VOID PhpFillUmdfDrivers( - _In_ PPH_PROCESS_ITEM Process, - _Inout_ PPH_STRING_BUILDER Drivers - ); - -VOID PhpFillRunningTasks( - _In_ PPH_PROCESS_ITEM Process, - _Inout_ PPH_STRING_BUILDER Tasks - ); - -static PH_STRINGREF StandardIndent = PH_STRINGREF_INIT(L" "); - -VOID PhpAppendStringWithLineBreaks( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ PPH_STRINGREF String, - _In_ ULONG CharactersPerLine, - _In_opt_ PPH_STRINGREF IndentAfterFirstLine - ) -{ - PH_STRINGREF line; - SIZE_T bytesPerLine; - BOOLEAN afterFirstLine; - SIZE_T bytesToAppend; - - line = *String; - bytesPerLine = CharactersPerLine * sizeof(WCHAR); - afterFirstLine = FALSE; - - while (line.Length != 0) - { - bytesToAppend = line.Length; - - if (bytesToAppend > bytesPerLine) - bytesToAppend = bytesPerLine; - - if (afterFirstLine) - { - PhAppendCharStringBuilder(StringBuilder, '\n'); - - if (IndentAfterFirstLine) - PhAppendStringBuilder(StringBuilder, IndentAfterFirstLine); - } - - PhAppendStringBuilderEx(StringBuilder, line.Buffer, bytesToAppend); - afterFirstLine = TRUE; - PhSkipStringRef(&line, bytesToAppend); - } -} - -static int __cdecl ServiceForTooltipCompare( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1; - PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2; - - return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); -} - -PPH_STRING PhGetProcessTooltipText( - _In_ PPH_PROCESS_ITEM Process, - _Out_opt_ PULONG ValidToTickCount - ) -{ - PH_STRING_BUILDER stringBuilder; - ULONG validForMs = 60 * 60 * 1000; // 1 hour - PPH_STRING tempString; - PH_KNOWN_PROCESS_TYPE knownProcessType = UnknownProcessType; - - PhInitializeStringBuilder(&stringBuilder, 200); - - // Command line - - if (Process->CommandLine) - { - tempString = PhEllipsisString(Process->CommandLine, 100 * 10); - - // This is necessary because the tooltip control seems to use some kind of O(n^9999) word-wrapping - // algorithm. - PhpAppendStringWithLineBreaks(&stringBuilder, &tempString->sr, 100, NULL); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - - PhDereferenceObject(tempString); - } - - // File information - - tempString = PhFormatImageVersionInfo( - Process->FileName, - &Process->VersionInfo, - &StandardIndent, - 0 - ); - - if (!PhIsNullOrEmptyString(tempString)) - { - PhAppendStringBuilder2(&stringBuilder, L"File:\n"); - PhAppendStringBuilder(&stringBuilder, &tempString->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - } - - if (tempString) - PhDereferenceObject(tempString); - - // Known command line information - - if (Process->QueryHandle) - PhGetProcessKnownType(Process->QueryHandle, &knownProcessType); - - if (Process->CommandLine && Process->QueryHandle) - { - PH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine; - - if (knownProcessType != UnknownProcessType && PhaGetProcessKnownCommandLine( - Process->CommandLine, - knownProcessType, - &knownCommandLine - )) - { - switch (knownProcessType & KnownProcessTypeMask) - { - case ServiceHostProcessType: - PhAppendStringBuilder2(&stringBuilder, L"Service group name:\n "); - PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ServiceHost.GroupName->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - break; - case RunDllAsAppProcessType: - { - PH_IMAGE_VERSION_INFO versionInfo; - - if (PhInitializeImageVersionInfo( - &versionInfo, - knownCommandLine.RunDllAsApp.FileName->Buffer - )) - { - tempString = PhFormatImageVersionInfo( - knownCommandLine.RunDllAsApp.FileName, - &versionInfo, - &StandardIndent, - 0 - ); - - if (!PhIsNullOrEmptyString(tempString)) - { - PhAppendStringBuilder2(&stringBuilder, L"Run DLL target file:\n"); - PhAppendStringBuilder(&stringBuilder, &tempString->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - } - - if (tempString) - PhDereferenceObject(tempString); - - PhDeleteImageVersionInfo(&versionInfo); - } - } - break; - case ComSurrogateProcessType: - { - PH_IMAGE_VERSION_INFO versionInfo; - PPH_STRING guidString; - - PhAppendStringBuilder2(&stringBuilder, L"COM target:\n"); - - if (knownCommandLine.ComSurrogate.Name) - { - PhAppendStringBuilder(&stringBuilder, &StandardIndent); - PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ComSurrogate.Name->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - } - - if (guidString = PhFormatGuid(&knownCommandLine.ComSurrogate.Guid)) - { - PhAppendStringBuilder(&stringBuilder, &StandardIndent); - PhAppendStringBuilder(&stringBuilder, &guidString->sr); - PhDereferenceObject(guidString); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - } - - if (knownCommandLine.ComSurrogate.FileName && PhInitializeImageVersionInfo( - &versionInfo, - knownCommandLine.ComSurrogate.FileName->Buffer - )) - { - tempString = PhFormatImageVersionInfo( - knownCommandLine.ComSurrogate.FileName, - &versionInfo, - &StandardIndent, - 0 - ); - - if (!PhIsNullOrEmptyString(tempString)) - { - PhAppendStringBuilder2(&stringBuilder, L"COM target file:\n"); - PhAppendStringBuilder(&stringBuilder, &tempString->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - } - - if (tempString) - PhDereferenceObject(tempString); - - PhDeleteImageVersionInfo(&versionInfo); - } - } - break; - } - } - } - - // Services - - if (Process->ServiceList && Process->ServiceList->Count != 0) - { - ULONG enumerationKey = 0; - PPH_SERVICE_ITEM serviceItem; - PPH_LIST serviceList; - ULONG i; - - // Copy the service list into our own list so we can sort it. - - serviceList = PhCreateList(Process->ServiceList->Count); - - PhAcquireQueuedLockShared(&Process->ServiceListLock); - - while (PhEnumPointerList( - Process->ServiceList, - &enumerationKey, - &serviceItem - )) - { - PhReferenceObject(serviceItem); - PhAddItemList(serviceList, serviceItem); - } - - PhReleaseQueuedLockShared(&Process->ServiceListLock); - - qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForTooltipCompare); - - PhAppendStringBuilder2(&stringBuilder, L"Services:\n"); - - // Add the services. - for (i = 0; i < serviceList->Count; i++) - { - serviceItem = serviceList->Items[i]; - - PhAppendStringBuilder(&stringBuilder, &StandardIndent); - PhAppendStringBuilder(&stringBuilder, &serviceItem->Name->sr); - PhAppendStringBuilder2(&stringBuilder, L" ("); - PhAppendStringBuilder(&stringBuilder, &serviceItem->DisplayName->sr); - PhAppendStringBuilder2(&stringBuilder, L")\n"); - } - - PhDereferenceObjects(serviceList->Items, serviceList->Count); - PhDereferenceObject(serviceList); - } - - // Tasks, Drivers - switch (knownProcessType & KnownProcessTypeMask) - { - case TaskHostProcessType: - { - PH_STRING_BUILDER tasks; - - PhInitializeStringBuilder(&tasks, 40); - - PhpFillRunningTasks(Process, &tasks); - - if (tasks.String->Length != 0) - { - PhAppendStringBuilder2(&stringBuilder, L"Tasks:\n"); - PhAppendStringBuilder(&stringBuilder, &tasks.String->sr); - } - - PhDeleteStringBuilder(&tasks); - } - break; - case UmdfHostProcessType: - { - PH_STRING_BUILDER drivers; - - PhInitializeStringBuilder(&drivers, 40); - - PhpFillUmdfDrivers(Process, &drivers); - - if (drivers.String->Length != 0) - { - PhAppendStringBuilder2(&stringBuilder, L"Drivers:\n"); - PhAppendStringBuilder(&stringBuilder, &drivers.String->sr); - } - - PhDeleteStringBuilder(&drivers); - - validForMs = 10 * 1000; // 10 seconds - } - break; - } - - // Plugin - if (PhPluginsEnabled) - { - PH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText; - - getTooltipText.Parameter = Process; - getTooltipText.StringBuilder = &stringBuilder; - getTooltipText.ValidForMs = validForMs; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), &getTooltipText); - validForMs = getTooltipText.ValidForMs; - } - - // Notes - - { - PH_STRING_BUILDER notes; - - PhInitializeStringBuilder(¬es, 40); - - if (Process->FileName) - { - if (Process->VerifyResult == VrTrusted) - { - if (!PhIsNullOrEmptyString(Process->VerifySignerName)) - PhAppendFormatStringBuilder(¬es, L" Signer: %s\n", Process->VerifySignerName->Buffer); - else - PhAppendStringBuilder2(¬es, L" Signed.\n"); - } - else if (Process->VerifyResult == VrUnknown) - { - // Nothing - } - else if (Process->VerifyResult != VrNoSignature) - { - PhAppendStringBuilder2(¬es, L" Signature invalid.\n"); - } - } - - if (Process->IsPacked) - { - PhAppendFormatStringBuilder( - ¬es, - L" Image is probably packed (%u imports over %u modules).\n", - Process->ImportFunctions, - Process->ImportModules - ); - } - - if ((ULONG_PTR)Process->ConsoleHostProcessId & ~3) - { - CLIENT_ID clientId; - PWSTR description = L"Console host"; - PPH_STRING clientIdString; - - clientId.UniqueProcess = (HANDLE)((ULONG_PTR)Process->ConsoleHostProcessId & ~3); - clientId.UniqueThread = NULL; - - if ((ULONG_PTR)Process->ConsoleHostProcessId & 2) - description = L"Console application"; - - clientIdString = PhGetClientIdName(&clientId); - PhAppendFormatStringBuilder(¬es, L" %s: %s\n", description, clientIdString->Buffer); - PhDereferenceObject(clientIdString); - } - - if (Process->PackageFullName) - { - PhAppendFormatStringBuilder(¬es, L" Package name: %s\n", Process->PackageFullName->Buffer); - } - - if (Process->IsDotNet) - PhAppendStringBuilder2(¬es, L" Process is managed (.NET).\n"); - if (Process->IsElevated) - PhAppendStringBuilder2(¬es, L" Process is elevated.\n"); - if (Process->IsImmersive) - PhAppendStringBuilder2(¬es, L" Process is a Modern UI app.\n"); - if (Process->IsInJob) - PhAppendStringBuilder2(¬es, L" Process is in a job.\n"); - if (Process->IsWow64) - PhAppendStringBuilder2(¬es, L" Process is 32-bit (WOW64).\n"); - - if (notes.String->Length != 0) - { - PhAppendStringBuilder2(&stringBuilder, L"Notes:\n"); - PhAppendStringBuilder(&stringBuilder, ¬es.String->sr); - } - - PhDeleteStringBuilder(¬es); - } - - if (ValidToTickCount) - *ValidToTickCount = GetTickCount() + validForMs; - - // Remove the trailing newline. - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - return PhFinalStringBuilderString(&stringBuilder); -} - -VOID PhpFillUmdfDrivers( - _In_ PPH_PROCESS_ITEM Process, - _Inout_ PPH_STRING_BUILDER Drivers - ) -{ - static PH_STRINGREF activeDevices = PH_STRINGREF_INIT(L"ACTIVE_DEVICES"); - static PH_STRINGREF currentControlSetEnum = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Enum\\"); - - HANDLE processHandle; - ULONG flags = 0; - PVOID environment; - ULONG environmentLength; - ULONG enumerationKey; - PH_ENVIRONMENT_VARIABLE variable; - - if (!NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - Process->ProcessId - ))) - return; - -#ifdef _WIN64 - // Just in case. - if (Process->IsWow64) - flags |= PH_GET_PROCESS_ENVIRONMENT_WOW64; -#endif - - if (NT_SUCCESS(PhGetProcessEnvironment( - processHandle, - flags, - &environment, - &environmentLength - ))) - { - enumerationKey = 0; - - while (PhEnumProcessEnvironmentVariables(environment, environmentLength, &enumerationKey, &variable)) - { - PH_STRINGREF part; - PH_STRINGREF remainingPart; - - if (!PhEqualStringRef(&variable.Name, &activeDevices, TRUE)) - continue; - - remainingPart = variable.Value; - - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, ';', &part, &remainingPart); - - if (part.Length != 0) - { - HANDLE driverKeyHandle; - PPH_STRING driverKeyPath; - - driverKeyPath = PhConcatStringRef2(¤tControlSetEnum, &part); - - if (NT_SUCCESS(PhOpenKey( - &driverKeyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &driverKeyPath->sr, - 0 - ))) - { - PPH_STRING deviceDesc; - PH_STRINGREF deviceName; - PPH_STRING hardwareId; - - if (deviceDesc = PhQueryRegistryString(driverKeyHandle, L"DeviceDesc")) - { - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - - if (PhSplitStringRefAtLastChar(&deviceDesc->sr, ';', &firstPart, &secondPart)) - deviceName = secondPart; - else - deviceName = deviceDesc->sr; - } - else - { - PhInitializeStringRef(&deviceName, L"Unknown Device"); - } - - hardwareId = PhQueryRegistryString(driverKeyHandle, L"HardwareID"); - - PhAppendStringBuilder(Drivers, &StandardIndent); - PhAppendStringBuilder(Drivers, &deviceName); - - if (hardwareId) - { - PhTrimToNullTerminatorString(hardwareId); - - if (hardwareId->Length != 0) - { - PhAppendStringBuilder2(Drivers, L" ("); - PhAppendStringBuilder(Drivers, &hardwareId->sr); - PhAppendCharStringBuilder(Drivers, ')'); - } - } - - PhAppendCharStringBuilder(Drivers, '\n'); - - PhClearReference(&hardwareId); - PhClearReference(&deviceDesc); - NtClose(driverKeyHandle); - } - - PhDereferenceObject(driverKeyPath); - } - } - - break; - } - - PhFreePage(environment); - } - - NtClose(processHandle); -} - -VOID PhpFillRunningTasks( - _In_ PPH_PROCESS_ITEM Process, - _Inout_ PPH_STRING_BUILDER Tasks - ) -{ - static CLSID CLSID_TaskScheduler_I = { 0x0f87369f, 0xa4e5, 0x4cfc, { 0xbd, 0x3e, 0x73, 0xe6, 0x15, 0x45, 0x72, 0xdd } }; - static IID IID_ITaskService_I = { 0x2faba4c7, 0x4da9, 0x4013, { 0x96, 0x97, 0x20, 0xcc, 0x3f, 0xd4, 0x0f, 0x85 } }; - - ITaskService *taskService; - - if (SUCCEEDED(CoCreateInstance( - &CLSID_TaskScheduler_I, - NULL, - CLSCTX_INPROC_SERVER, - &IID_ITaskService_I, - &taskService - ))) - { - VARIANT empty = { 0 }; - - if (SUCCEEDED(ITaskService_Connect(taskService, empty, empty, empty, empty))) - { - IRunningTaskCollection *runningTasks; - - if (SUCCEEDED(ITaskService_GetRunningTasks( - taskService, - TASK_ENUM_HIDDEN, - &runningTasks - ))) - { - LONG count; - LONG i; - VARIANT index; - - index.vt = VT_INT; - - if (SUCCEEDED(IRunningTaskCollection_get_Count(runningTasks, &count))) - { - for (i = 1; i <= count; i++) // collections are 1-based - { - IRunningTask *runningTask; - - index.lVal = i; - - if (SUCCEEDED(IRunningTaskCollection_get_Item(runningTasks, index, &runningTask))) - { - ULONG pid; - BSTR action = NULL; - BSTR path = NULL; - - if ( - SUCCEEDED(IRunningTask_get_EnginePID(runningTask, &pid)) && - pid == HandleToUlong(Process->ProcessId) - ) - { - IRunningTask_get_CurrentAction(runningTask, &action); - IRunningTask_get_Path(runningTask, &path); - - PhAppendStringBuilder(Tasks, &StandardIndent); - PhAppendStringBuilder2(Tasks, action ? action : L"Unknown action"); - PhAppendStringBuilder2(Tasks, L" ("); - PhAppendStringBuilder2(Tasks, path ? path : L"Unknown path"); - PhAppendStringBuilder2(Tasks, L")\n"); - - if (action) - SysFreeString(action); - if (path) - SysFreeString(path); - } - - IRunningTask_Release(runningTask); - } - } - } - - IRunningTaskCollection_Release(runningTasks); - } - } - - ITaskService_Release(taskService); - } -} - -PPH_STRING PhGetServiceTooltipText( - _In_ PPH_SERVICE_ITEM Service - ) -{ - PH_STRING_BUILDER stringBuilder; - SC_HANDLE serviceHandle; - - PhInitializeStringBuilder(&stringBuilder, 200); - - if (serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_QUERY_CONFIG)) - { - PPH_STRING fileName; - PPH_STRING description; - - // File information - - if (fileName = PhGetServiceRelevantFileName(&Service->Name->sr, serviceHandle)) - { - PH_IMAGE_VERSION_INFO versionInfo; - PPH_STRING versionInfoText; - - if (PhInitializeImageVersionInfo( - &versionInfo, - fileName->Buffer - )) - { - versionInfoText = PhFormatImageVersionInfo( - fileName, - &versionInfo, - &StandardIndent, - 0 - ); - - if (!PhIsNullOrEmptyString(versionInfoText)) - { - PhAppendStringBuilder2(&stringBuilder, L"File:\n"); - PhAppendStringBuilder(&stringBuilder, &versionInfoText->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - } - - PhClearReference(&versionInfoText); - PhDeleteImageVersionInfo(&versionInfo); - } - - PhDereferenceObject(fileName); - } - - // Description - - if (description = PhGetServiceDescription(serviceHandle)) - { - PhAppendStringBuilder2(&stringBuilder, L"Description:\n "); - PhAppendStringBuilder(&stringBuilder, &description->sr); - PhAppendCharStringBuilder(&stringBuilder, '\n'); - PhDereferenceObject(description); - } - - CloseServiceHandle(serviceHandle); - } - - // Remove the trailing newline. - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - return PhFinalStringBuilderString(&stringBuilder); -} +/* + * Process Hacker - + * item tooltips + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +VOID PhpFillUmdfDrivers( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Drivers + ); + +VOID PhpFillRunningTasks( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Tasks + ); + +VOID PhpFillMicrosoftEdge( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Tasks + ); + +VOID PhpFillWmiProviderHost( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Providers + ); + +// HACK +PPH_STRING PhQueryWmiHostProcessString( + _In_ PPH_PROCESS_ITEM ProcessItem, + _Inout_ PPH_STRING_BUILDER Providers + ); + +static PH_STRINGREF StandardIndent = PH_STRINGREF_INIT(L" "); + +VOID PhpAppendStringWithLineBreaks( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ PPH_STRINGREF String, + _In_ ULONG CharactersPerLine, + _In_opt_ PPH_STRINGREF IndentAfterFirstLine + ) +{ + PH_STRINGREF line; + SIZE_T bytesPerLine; + BOOLEAN afterFirstLine; + SIZE_T bytesToAppend; + + line = *String; + bytesPerLine = CharactersPerLine * sizeof(WCHAR); + afterFirstLine = FALSE; + + while (line.Length != 0) + { + bytesToAppend = line.Length; + + if (bytesToAppend > bytesPerLine) + bytesToAppend = bytesPerLine; + + if (afterFirstLine) + { + PhAppendCharStringBuilder(StringBuilder, '\n'); + + if (IndentAfterFirstLine) + PhAppendStringBuilder(StringBuilder, IndentAfterFirstLine); + } + + PhAppendStringBuilderEx(StringBuilder, line.Buffer, bytesToAppend); + afterFirstLine = TRUE; + PhSkipStringRef(&line, bytesToAppend); + } +} + +static int __cdecl ServiceForTooltipCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1; + PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2; + + return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); +} + +PPH_STRING PhGetProcessTooltipText( + _In_ PPH_PROCESS_ITEM Process, + _Out_opt_ PULONG64 ValidToTickCount + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG validForMs = 60 * 60 * 1000; // 1 hour + PPH_STRING tempString; + + PhInitializeStringBuilder(&stringBuilder, 200); + + // Command line + + if (Process->CommandLine) + { + tempString = PhEllipsisString(Process->CommandLine, 100 * 10); + + // This is necessary because the tooltip control seems to use some kind of O(n^9999) word-wrapping + // algorithm. + PhpAppendStringWithLineBreaks(&stringBuilder, &tempString->sr, 100, NULL); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + + PhDereferenceObject(tempString); + } + + // File information + + tempString = PhFormatImageVersionInfo( + Process->FileName, + &Process->VersionInfo, + &StandardIndent, + 0 + ); + + if (!PhIsNullOrEmptyString(tempString)) + { + PhAppendStringBuilder2(&stringBuilder, L"File:\n"); + PhAppendStringBuilder(&stringBuilder, &tempString->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + } + + if (tempString) + PhDereferenceObject(tempString); + + // Known command line information + + if (Process->CommandLine && Process->QueryHandle) + { + PH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine; + + if (Process->KnownProcessType != UnknownProcessType && PhaGetProcessKnownCommandLine( + Process->CommandLine, + Process->KnownProcessType, + &knownCommandLine + )) + { + switch (Process->KnownProcessType & KnownProcessTypeMask) + { + case ServiceHostProcessType: + PhAppendStringBuilder2(&stringBuilder, L"Service group name:\n "); + PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ServiceHost.GroupName->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + break; + case RunDllAsAppProcessType: + { + PH_IMAGE_VERSION_INFO versionInfo; + + if (PhInitializeImageVersionInfo( + &versionInfo, + knownCommandLine.RunDllAsApp.FileName->Buffer + )) + { + tempString = PhFormatImageVersionInfo( + knownCommandLine.RunDllAsApp.FileName, + &versionInfo, + &StandardIndent, + 0 + ); + + if (!PhIsNullOrEmptyString(tempString)) + { + PhAppendStringBuilder2(&stringBuilder, L"Run DLL target file:\n"); + PhAppendStringBuilder(&stringBuilder, &tempString->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + } + + if (tempString) + PhDereferenceObject(tempString); + + PhDeleteImageVersionInfo(&versionInfo); + } + } + break; + case ComSurrogateProcessType: + { + PH_IMAGE_VERSION_INFO versionInfo; + PPH_STRING guidString; + + PhAppendStringBuilder2(&stringBuilder, L"COM target:\n"); + + if (knownCommandLine.ComSurrogate.Name) + { + PhAppendStringBuilder(&stringBuilder, &StandardIndent); + PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ComSurrogate.Name->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + } + + if (guidString = PhFormatGuid(&knownCommandLine.ComSurrogate.Guid)) + { + PhAppendStringBuilder(&stringBuilder, &StandardIndent); + PhAppendStringBuilder(&stringBuilder, &guidString->sr); + PhDereferenceObject(guidString); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + } + + if (knownCommandLine.ComSurrogate.FileName && PhInitializeImageVersionInfo( + &versionInfo, + knownCommandLine.ComSurrogate.FileName->Buffer + )) + { + tempString = PhFormatImageVersionInfo( + knownCommandLine.ComSurrogate.FileName, + &versionInfo, + &StandardIndent, + 0 + ); + + if (!PhIsNullOrEmptyString(tempString)) + { + PhAppendStringBuilder2(&stringBuilder, L"COM target file:\n"); + PhAppendStringBuilder(&stringBuilder, &tempString->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + } + + if (tempString) + PhDereferenceObject(tempString); + + PhDeleteImageVersionInfo(&versionInfo); + } + } + break; + } + } + } + + // Services + + if (Process->ServiceList && Process->ServiceList->Count != 0) + { + ULONG enumerationKey = 0; + PPH_SERVICE_ITEM serviceItem; + PPH_LIST serviceList; + ULONG i; + + // Copy the service list into our own list so we can sort it. + + serviceList = PhCreateList(Process->ServiceList->Count); + + PhAcquireQueuedLockShared(&Process->ServiceListLock); + + while (PhEnumPointerList( + Process->ServiceList, + &enumerationKey, + &serviceItem + )) + { + PhReferenceObject(serviceItem); + PhAddItemList(serviceList, serviceItem); + } + + PhReleaseQueuedLockShared(&Process->ServiceListLock); + + qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForTooltipCompare); + + PhAppendStringBuilder2(&stringBuilder, L"Services:\n"); + + // Add the services. + for (i = 0; i < serviceList->Count; i++) + { + serviceItem = serviceList->Items[i]; + + PhAppendStringBuilder(&stringBuilder, &StandardIndent); + PhAppendStringBuilder(&stringBuilder, &serviceItem->Name->sr); + PhAppendStringBuilder2(&stringBuilder, L" ("); + PhAppendStringBuilder(&stringBuilder, &serviceItem->DisplayName->sr); + PhAppendStringBuilder2(&stringBuilder, L")\n"); + } + + PhDereferenceObjects(serviceList->Items, serviceList->Count); + PhDereferenceObject(serviceList); + } + + // Tasks, Drivers + switch (Process->KnownProcessType & KnownProcessTypeMask) + { + case TaskHostProcessType: + { + PH_STRING_BUILDER tasks; + + PhInitializeStringBuilder(&tasks, 40); + + PhpFillRunningTasks(Process, &tasks); + + if (tasks.String->Length != 0) + { + PhAppendStringBuilder2(&stringBuilder, L"Tasks:\n"); + PhAppendStringBuilder(&stringBuilder, &tasks.String->sr); + } + + PhDeleteStringBuilder(&tasks); + } + break; + case UmdfHostProcessType: + { + PH_STRING_BUILDER drivers; + + PhInitializeStringBuilder(&drivers, 40); + + PhpFillUmdfDrivers(Process, &drivers); + + if (drivers.String->Length != 0) + { + PhAppendStringBuilder2(&stringBuilder, L"Drivers:\n"); + PhAppendStringBuilder(&stringBuilder, &drivers.String->sr); + } + + PhDeleteStringBuilder(&drivers); + + validForMs = 10 * 1000; // 10 seconds + } + break; + case EdgeProcessType: + { + PH_STRING_BUILDER container; + + PhInitializeStringBuilder(&container, 40); + + PhpFillMicrosoftEdge(Process, &container); + + if (container.String->Length != 0) + { + PhAppendStringBuilder2(&stringBuilder, L"Edge:\n"); + PhAppendStringBuilder(&stringBuilder, &container.String->sr); + } + + PhDeleteStringBuilder(&container); + } + break; + case WmiProviderHostType: + { + PH_STRING_BUILDER provider; + + PhInitializeStringBuilder(&provider, 40); + + PhpFillWmiProviderHost(Process, &provider); + + if (provider.String->Length != 0) + { + PhAppendStringBuilder2(&stringBuilder, L"WMI Providers:\n"); + PhAppendStringBuilder(&stringBuilder, &provider.String->sr); + } + + PhDeleteStringBuilder(&provider); + + validForMs = 10 * 1000; // 10 seconds + } + break; + } + + // Plugin + if (PhPluginsEnabled) + { + PH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText; + + getTooltipText.Parameter = Process; + getTooltipText.StringBuilder = &stringBuilder; + getTooltipText.ValidForMs = validForMs; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), &getTooltipText); + validForMs = getTooltipText.ValidForMs; + } + + // Notes + + { + PH_STRING_BUILDER notes; + + PhInitializeStringBuilder(¬es, 40); + + if (Process->FileName) + { + if (Process->VerifyResult == VrTrusted) + { + if (!PhIsNullOrEmptyString(Process->VerifySignerName)) + PhAppendFormatStringBuilder(¬es, L" Signer: %s\n", Process->VerifySignerName->Buffer); + else + PhAppendStringBuilder2(¬es, L" Signed.\n"); + } + else if (Process->VerifyResult == VrUnknown) + { + // Nothing + } + else if (Process->VerifyResult != VrNoSignature) + { + PhAppendStringBuilder2(¬es, L" Signature invalid.\n"); + } + } + + if (Process->IsPacked) + { + PhAppendFormatStringBuilder( + ¬es, + L" Image is probably packed (%lu imports over %lu modules).\n", + Process->ImportFunctions, + Process->ImportModules + ); + } + + if ((ULONG_PTR)Process->ConsoleHostProcessId & ~3) + { + CLIENT_ID clientId; + PWSTR description = L"Console host"; + PPH_STRING clientIdString; + + clientId.UniqueProcess = (HANDLE)((ULONG_PTR)Process->ConsoleHostProcessId & ~3); + clientId.UniqueThread = NULL; + + if ((ULONG_PTR)Process->ConsoleHostProcessId & 2) + description = L"Console application"; + + clientIdString = PhGetClientIdName(&clientId); + PhAppendFormatStringBuilder(¬es, L" %s: %s\n", description, clientIdString->Buffer); + PhDereferenceObject(clientIdString); + } + + if (Process->PackageFullName) + { + PhAppendFormatStringBuilder(¬es, L" Package name: %s\n", Process->PackageFullName->Buffer); + } + + if (Process->IsDotNet) + PhAppendStringBuilder2(¬es, L" Process is managed (.NET).\n"); + if (Process->IsElevated) + PhAppendStringBuilder2(¬es, L" Process is elevated.\n"); + if (Process->IsImmersive) + PhAppendStringBuilder2(¬es, L" Process is a Modern UI app.\n"); + if (Process->IsInJob) + PhAppendStringBuilder2(¬es, L" Process is in a job.\n"); + if (Process->IsWow64) + PhAppendStringBuilder2(¬es, L" Process is 32-bit (WOW64).\n"); + + if (notes.String->Length != 0) + { + PhAppendStringBuilder2(&stringBuilder, L"Notes:\n"); + PhAppendStringBuilder(&stringBuilder, ¬es.String->sr); + } + + PhDeleteStringBuilder(¬es); + } + + if (ValidToTickCount) + *ValidToTickCount = NtGetTickCount64() + validForMs; + + // Remove the trailing newline. + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + return PhFinalStringBuilderString(&stringBuilder); +} + +VOID PhpFillUmdfDrivers( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Drivers + ) +{ + static PH_STRINGREF activeDevices = PH_STRINGREF_INIT(L"ACTIVE_DEVICES"); + static PH_STRINGREF currentControlSetEnum = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Enum\\"); + + HANDLE processHandle; + ULONG flags = 0; + PVOID environment; + ULONG environmentLength; + ULONG enumerationKey; + PH_ENVIRONMENT_VARIABLE variable; + + if (!NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + Process->ProcessId + ))) + return; + +#ifdef _WIN64 + // Just in case. + if (Process->IsWow64) + flags |= PH_GET_PROCESS_ENVIRONMENT_WOW64; +#endif + + if (NT_SUCCESS(PhGetProcessEnvironment( + processHandle, + flags, + &environment, + &environmentLength + ))) + { + enumerationKey = 0; + + while (PhEnumProcessEnvironmentVariables(environment, environmentLength, &enumerationKey, &variable)) + { + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + if (!PhEqualStringRef(&variable.Name, &activeDevices, TRUE)) + continue; + + remainingPart = variable.Value; + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, ';', &part, &remainingPart); + + if (part.Length != 0) + { + HANDLE driverKeyHandle; + PPH_STRING driverKeyPath; + + driverKeyPath = PhConcatStringRef2(¤tControlSetEnum, &part); + + if (NT_SUCCESS(PhOpenKey( + &driverKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &driverKeyPath->sr, + 0 + ))) + { + PPH_STRING deviceDesc; + PH_STRINGREF deviceName; + PPH_STRING hardwareId; + + if (deviceDesc = PhQueryRegistryString(driverKeyHandle, L"DeviceDesc")) + { + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + + if (PhSplitStringRefAtLastChar(&deviceDesc->sr, ';', &firstPart, &secondPart)) + deviceName = secondPart; + else + deviceName = deviceDesc->sr; + } + else + { + PhInitializeStringRef(&deviceName, L"Unknown Device"); + } + + hardwareId = PhQueryRegistryString(driverKeyHandle, L"HardwareID"); + + PhAppendStringBuilder(Drivers, &StandardIndent); + PhAppendStringBuilder(Drivers, &deviceName); + + if (hardwareId) + { + PhTrimToNullTerminatorString(hardwareId); + + if (hardwareId->Length != 0) + { + PhAppendStringBuilder2(Drivers, L" ("); + PhAppendStringBuilder(Drivers, &hardwareId->sr); + PhAppendCharStringBuilder(Drivers, ')'); + } + } + + PhAppendCharStringBuilder(Drivers, '\n'); + + PhClearReference(&hardwareId); + PhClearReference(&deviceDesc); + NtClose(driverKeyHandle); + } + + PhDereferenceObject(driverKeyPath); + } + } + + break; + } + + PhFreePage(environment); + } + + NtClose(processHandle); +} + +VOID PhpFillRunningTasks( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Tasks + ) +{ + static CLSID CLSID_TaskScheduler_I = { 0x0f87369f, 0xa4e5, 0x4cfc, { 0xbd, 0x3e, 0x73, 0xe6, 0x15, 0x45, 0x72, 0xdd } }; + static IID IID_ITaskService_I = { 0x2faba4c7, 0x4da9, 0x4013, { 0x96, 0x97, 0x20, 0xcc, 0x3f, 0xd4, 0x0f, 0x85 } }; + + ITaskService *taskService; + + if (SUCCEEDED(CoCreateInstance( + &CLSID_TaskScheduler_I, + NULL, + CLSCTX_INPROC_SERVER, + &IID_ITaskService_I, + &taskService + ))) + { + VARIANT empty = { 0 }; + + if (SUCCEEDED(ITaskService_Connect(taskService, empty, empty, empty, empty))) + { + IRunningTaskCollection *runningTasks; + + if (SUCCEEDED(ITaskService_GetRunningTasks( + taskService, + TASK_ENUM_HIDDEN, + &runningTasks + ))) + { + LONG count; + LONG i; + VARIANT index; + + index.vt = VT_INT; + + if (SUCCEEDED(IRunningTaskCollection_get_Count(runningTasks, &count))) + { + for (i = 1; i <= count; i++) // collections are 1-based + { + IRunningTask *runningTask; + + index.lVal = i; + + if (SUCCEEDED(IRunningTaskCollection_get_Item(runningTasks, index, &runningTask))) + { + ULONG pid; + BSTR action = NULL; + BSTR path = NULL; + + if ( + SUCCEEDED(IRunningTask_get_EnginePID(runningTask, &pid)) && + pid == HandleToUlong(Process->ProcessId) + ) + { + IRunningTask_get_CurrentAction(runningTask, &action); + IRunningTask_get_Path(runningTask, &path); + + PhAppendStringBuilder(Tasks, &StandardIndent); + PhAppendStringBuilder2(Tasks, action ? action : L"Unknown action"); + PhAppendStringBuilder2(Tasks, L" ("); + PhAppendStringBuilder2(Tasks, path ? path : L"Unknown path"); + PhAppendStringBuilder2(Tasks, L")\n"); + + if (action) + SysFreeString(action); + if (path) + SysFreeString(path); + } + + IRunningTask_Release(runningTask); + } + } + } + + IRunningTaskCollection_Release(runningTasks); + } + } + + ITaskService_Release(taskService); + } +} + +VOID PhpFillMicrosoftEdge( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Containers + ) +{ + HANDLE tokenHandle; + PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; + PPH_STRING appContainerSid = NULL; + + if (NT_SUCCESS(PhOpenProcessToken( + Process->QueryHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) + { + if (appContainerInfo->TokenAppContainer) + appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); + + PhFree(appContainerInfo); + } + } + + if (appContainerSid) + { + static PH_STRINGREF managerSid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194"); + static PH_STRINGREF extensionsSid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-1206159417-1570029349-2913729690-1184509225"); + static PH_STRINGREF serviceUiSid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-3513710562-3729412521-1863153555-1462103995"); + static PH_STRINGREF chakraJitSid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-1821068571-1793888307-623627345-1529106238"); + static PH_STRINGREF flashSid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-3859068477-1314311106-1651661491-1685393560"); + static PH_STRINGREF backgroundTabPool1Sid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-4256926629-1688279915-2739229046-3928706915"); + static PH_STRINGREF backgroundTabPool2Sid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-2385269614-3243675-834220592-3047885450"); + static PH_STRINGREF backgroundTabPool3Sid = PH_STRINGREF_INIT(L"S-1-15-2-3624051433-2125758914-1423191267-1740899205-1073925389-3782572162-737981194-355265979-2879959831-980936148-1241729999"); + + if (PhEqualStringRef(&appContainerSid->sr, &managerSid, FALSE)) + { + PhAppendStringBuilder2(Containers, L" Microsoft Edge Manager\n"); + } + else if (PhEqualStringRef(&appContainerSid->sr, &extensionsSid, FALSE)) + { + PhAppendStringBuilder2(Containers, L" Browser Extensions\n"); + } + else if (PhEqualStringRef(&appContainerSid->sr, &serviceUiSid, FALSE)) + { + PhAppendStringBuilder2(Containers, L" User Interface Service\n"); + } + else if (PhEqualStringRef(&appContainerSid->sr, &chakraJitSid, FALSE)) + { + PhAppendStringBuilder2(Containers, L" Chakra Jit Compiler\n"); + } + else if (PhEqualStringRef(&appContainerSid->sr, &flashSid, FALSE)) + { + PhAppendStringBuilder2(Containers, L" Adobe Flash Player\n"); + } + else if ( + PhEqualStringRef(&appContainerSid->sr, &backgroundTabPool1Sid, FALSE) || + PhEqualStringRef(&appContainerSid->sr, &backgroundTabPool2Sid, FALSE) || + PhEqualStringRef(&appContainerSid->sr, &backgroundTabPool3Sid, FALSE) + ) + { + PhAppendStringBuilder2(Containers, L" Background Tab Pool\n"); + } + + PhDereferenceObject(appContainerSid); + } +} + +VOID PhpFillWmiProviderHost( + _In_ PPH_PROCESS_ITEM Process, + _Inout_ PPH_STRING_BUILDER Providers + ) +{ + PhQueryWmiHostProcessString(Process, Providers); +} + +PPH_STRING PhGetServiceTooltipText( + _In_ PPH_SERVICE_ITEM Service + ) +{ + PH_STRING_BUILDER stringBuilder; + SC_HANDLE serviceHandle; + + PhInitializeStringBuilder(&stringBuilder, 200); + + if (serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + PPH_STRING fileName; + PPH_STRING description; + + // File information + + if (fileName = PhGetServiceRelevantFileName(&Service->Name->sr, serviceHandle)) + { + PH_IMAGE_VERSION_INFO versionInfo; + PPH_STRING versionInfoText; + + if (PhInitializeImageVersionInfo( + &versionInfo, + fileName->Buffer + )) + { + versionInfoText = PhFormatImageVersionInfo( + fileName, + &versionInfo, + &StandardIndent, + 0 + ); + + if (!PhIsNullOrEmptyString(versionInfoText)) + { + PhAppendStringBuilder2(&stringBuilder, L"File:\n"); + PhAppendStringBuilder(&stringBuilder, &versionInfoText->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + } + + PhClearReference(&versionInfoText); + PhDeleteImageVersionInfo(&versionInfo); + } + + PhDereferenceObject(fileName); + } + + // Description + + if (description = PhGetServiceDescription(serviceHandle)) + { + PhAppendStringBuilder2(&stringBuilder, L"Description:\n "); + PhAppendStringBuilder(&stringBuilder, &description->sr); + PhAppendCharStringBuilder(&stringBuilder, '\n'); + PhDereferenceObject(description); + } + + CloseServiceHandle(serviceHandle); + } + + // Remove the trailing newline. + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + return PhFinalStringBuilderString(&stringBuilder); +} diff --git a/ProcessHacker/jobprp.c b/ProcessHacker/jobprp.c index e43759b49b5e..62e559713e5e 100644 --- a/ProcessHacker/jobprp.c +++ b/ProcessHacker/jobprp.c @@ -1,660 +1,766 @@ -/* - * Process Hacker - - * job properties - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -#include -#include - -typedef struct _JOB_PAGE_CONTEXT -{ - PPH_OPEN_OBJECT OpenObject; - PVOID Context; - DLGPROC HookProc; -} JOB_PAGE_CONTEXT, *PJOB_PAGE_CONTEXT; - -INT CALLBACK PhpJobPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ); - -INT_PTR CALLBACK PhpJobPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhpShowJobAdvancedProperties( - _In_ HWND ParentWindowHandle, - _In_ PJOB_PAGE_CONTEXT Context - ); - -INT_PTR CALLBACK PhpJobStatisticsPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowJobProperties( - _In_ HWND ParentWindowHandle, - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ PWSTR Title - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - HPROPSHEETPAGE pages[1]; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = Title ? Title : L"Job"; - propSheetHeader.nPages = 1; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - pages[0] = PhCreateJobPage(OpenObject, Context, NULL); - - PhModalPropertySheet(&propSheetHeader); -} - -HPROPSHEETPAGE PhCreateJobPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ DLGPROC HookProc - ) -{ - HPROPSHEETPAGE propSheetPageHandle; - PROPSHEETPAGE propSheetPage; - PJOB_PAGE_CONTEXT jobPageContext; - - jobPageContext = PhCreateAlloc(sizeof(JOB_PAGE_CONTEXT)); - memset(jobPageContext, 0, sizeof(JOB_PAGE_CONTEXT)); - jobPageContext->OpenObject = OpenObject; - jobPageContext->Context = Context; - jobPageContext->HookProc = HookProc; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USECALLBACK; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OBJJOB); - propSheetPage.pfnDlgProc = PhpJobPageProc; - propSheetPage.lParam = (LPARAM)jobPageContext; - propSheetPage.pfnCallback = PhpJobPropPageProc; - - propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); - // CreatePropertySheetPage would have sent PSPCB_ADDREF (below), - // which would have added a reference. - PhDereferenceObject(jobPageContext); - - return propSheetPageHandle; -} - -INT CALLBACK PhpJobPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ) -{ - PJOB_PAGE_CONTEXT jobPageContext; - - jobPageContext = (PJOB_PAGE_CONTEXT)ppsp->lParam; - - if (uMsg == PSPCB_ADDREF) - { - PhReferenceObject(jobPageContext); - } - else if (uMsg == PSPCB_RELEASE) - { - PhDereferenceObject(jobPageContext); - } - - return 1; -} - -FORCEINLINE PJOB_PAGE_CONTEXT PhpJobPageHeader( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return (PJOB_PAGE_CONTEXT)PhpGenericPropertyPageHeader( - hwndDlg, uMsg, wParam, lParam, L"JobPageContext"); -} - -static VOID PhpAddLimit( - _In_ HWND Handle, - _In_ PWSTR Name, - _In_ PWSTR Value - ) -{ - INT lvItemIndex; - - lvItemIndex = PhAddListViewItem(Handle, MAXINT, Name, NULL); - PhSetListViewSubItem(Handle, lvItemIndex, 1, Value); -} - -static VOID PhpAddJobProcesses( - _In_ HWND hwndDlg, - _In_ HANDLE JobHandle - ) -{ - PJOBOBJECT_BASIC_PROCESS_ID_LIST processIdList; - HWND processesLv; - - processesLv = GetDlgItem(hwndDlg, IDC_PROCESSES); - - if (NT_SUCCESS(PhGetJobProcessIdList(JobHandle, &processIdList))) - { - ULONG i; - CLIENT_ID clientId; - PPH_STRING name; - - clientId.UniqueThread = NULL; - - for (i = 0; i < processIdList->NumberOfProcessIdsInList; i++) - { - clientId.UniqueProcess = (HANDLE)processIdList->ProcessIdList[i]; - name = PH_AUTO(PhGetClientIdName(&clientId)); - - PhAddListViewItem(processesLv, MAXINT, PhGetString(name), NULL); - } - - PhFree(processIdList); - } -} - -INT_PTR CALLBACK PhpJobPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PJOB_PAGE_CONTEXT jobPageContext; - - jobPageContext = PhpJobPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!jobPageContext) - return FALSE; - - if (jobPageContext->HookProc) - { - if (jobPageContext->HookProc(hwndDlg, uMsg, wParam, lParam)) - return TRUE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - HANDLE jobHandle; - HWND processesLv; - HWND limitsLv; - - processesLv = GetDlgItem(hwndDlg, IDC_PROCESSES); - limitsLv = GetDlgItem(hwndDlg, IDC_LIMITS); - PhSetListViewStyle(processesLv, FALSE, TRUE); - PhSetListViewStyle(limitsLv, FALSE, TRUE); - PhSetControlTheme(processesLv, L"explorer"); - PhSetControlTheme(limitsLv, L"explorer"); - - PhAddListViewColumn(processesLv, 0, 0, 0, LVCFMT_LEFT, 240, L"Name"); - - PhAddListViewColumn(limitsLv, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); - PhAddListViewColumn(limitsLv, 1, 1, 1, LVCFMT_LEFT, 160, L"Value"); - PhLoadListViewColumnsFromSetting(L"JobListViewColumns", limitsLv); - - SetDlgItemText(hwndDlg, IDC_NAME, L"Unknown"); - - if (NT_SUCCESS(jobPageContext->OpenObject( - &jobHandle, - JOB_OBJECT_QUERY, - jobPageContext->Context - ))) - { - PPH_STRING jobObjectName = NULL; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedLimits; - JOBOBJECT_BASIC_UI_RESTRICTIONS basicUiRestrictions; - - // Name - - PhGetHandleInformation( - NtCurrentProcess(), - jobHandle, - -1, - NULL, - NULL, - NULL, - &jobObjectName - ); - PH_AUTO(jobObjectName); - - if (jobObjectName && jobObjectName->Length == 0) - jobObjectName = NULL; - - SetDlgItemText(hwndDlg, IDC_NAME, PhGetStringOrDefault(jobObjectName, L"(unnamed job)")); - - // Processes - PhpAddJobProcesses(hwndDlg, jobHandle); - - // Limits - - if (NT_SUCCESS(PhGetJobExtendedLimits(jobHandle, &extendedLimits))) - { - ULONG flags = extendedLimits.BasicLimitInformation.LimitFlags; - - if (flags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS) - { - WCHAR value[PH_INT32_STR_LEN_1]; - PhPrintUInt32(value, extendedLimits.BasicLimitInformation.ActiveProcessLimit); - PhpAddLimit(limitsLv, L"Active processes", value); - } - - if (flags & JOB_OBJECT_LIMIT_AFFINITY) - { - WCHAR value[PH_PTR_STR_LEN_1]; - PhPrintPointer(value, (PVOID)extendedLimits.BasicLimitInformation.Affinity); - PhpAddLimit(limitsLv, L"Affinity", value); - } - - if (flags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) - { - PhpAddLimit(limitsLv, L"Breakaway OK", L"Enabled"); - } - - if (flags & JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION) - { - PhpAddLimit(limitsLv, L"Die on unhandled exception", L"Enabled"); - } - - if (flags & JOB_OBJECT_LIMIT_JOB_MEMORY) - { - PPH_STRING value = PhaFormatSize(extendedLimits.JobMemoryLimit, -1); - PhpAddLimit(limitsLv, L"Job memory", value->Buffer); - } - - if (flags & JOB_OBJECT_LIMIT_JOB_TIME) - { - WCHAR value[PH_TIMESPAN_STR_LEN_1]; - PhPrintTimeSpan(value, extendedLimits.BasicLimitInformation.PerJobUserTimeLimit.QuadPart, - PH_TIMESPAN_DHMS); - PhpAddLimit(limitsLv, L"Job time", value); - } - - if (flags & JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) - { - PhpAddLimit(limitsLv, L"Kill on job close", L"Enabled"); - } - - if (flags & JOB_OBJECT_LIMIT_PRIORITY_CLASS) - { - PhpAddLimit(limitsLv, L"Priority class", - PhGetProcessPriorityClassString(extendedLimits.BasicLimitInformation.PriorityClass)); - } - - if (flags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) - { - PPH_STRING value = PhaFormatSize(extendedLimits.ProcessMemoryLimit, -1); - PhpAddLimit(limitsLv, L"Process memory", value->Buffer); - } - - if (flags & JOB_OBJECT_LIMIT_PROCESS_TIME) - { - WCHAR value[PH_TIMESPAN_STR_LEN_1]; - PhPrintTimeSpan(value, extendedLimits.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart, - PH_TIMESPAN_DHMS); - PhpAddLimit(limitsLv, L"Process time", value); - } - - if (flags & JOB_OBJECT_LIMIT_SCHEDULING_CLASS) - { - WCHAR value[PH_INT32_STR_LEN_1]; - PhPrintUInt32(value, extendedLimits.BasicLimitInformation.SchedulingClass); - PhpAddLimit(limitsLv, L"Scheduling class", value); - } - - if (flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) - { - PhpAddLimit(limitsLv, L"Silent breakaway OK", L"Enabled"); - } - - if (flags & JOB_OBJECT_LIMIT_WORKINGSET) - { - PPH_STRING value; - - value = PhaFormatSize(extendedLimits.BasicLimitInformation.MinimumWorkingSetSize, -1); - PhpAddLimit(limitsLv, L"Working set minimum", value->Buffer); - - value = PhaFormatSize(extendedLimits.BasicLimitInformation.MaximumWorkingSetSize, -1); - PhpAddLimit(limitsLv, L"Working set maximum", value->Buffer); - } - } - - if (NT_SUCCESS(PhGetJobBasicUiRestrictions(jobHandle, &basicUiRestrictions))) - { - ULONG flags = basicUiRestrictions.UIRestrictionsClass; - - if (flags & JOB_OBJECT_UILIMIT_DESKTOP) - PhpAddLimit(limitsLv, L"Desktop", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_DISPLAYSETTINGS) - PhpAddLimit(limitsLv, L"Display settings", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_EXITWINDOWS) - PhpAddLimit(limitsLv, L"Exit windows", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_GLOBALATOMS) - PhpAddLimit(limitsLv, L"Global atoms", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_HANDLES) - PhpAddLimit(limitsLv, L"Handles", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_READCLIPBOARD) - PhpAddLimit(limitsLv, L"Read clipboard", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS) - PhpAddLimit(limitsLv, L"System parameters", L"Limited"); - if (flags & JOB_OBJECT_UILIMIT_WRITECLIPBOARD) - PhpAddLimit(limitsLv, L"Write clipboard", L"Limited"); - } - - NtClose(jobHandle); - } - } - break; - case WM_DESTROY: - PhSaveListViewColumnsToSetting(L"JobListViewColumns", GetDlgItem(hwndDlg, IDC_LIMITS)); - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_TERMINATE: - { - if (PhShowConfirmMessage( - hwndDlg, - L"terminate", - L"the job", - L"Terminating a job will terminate all processes assigned to it.", - TRUE - )) - { - NTSTATUS status; - HANDLE jobHandle; - - if (NT_SUCCESS(status = jobPageContext->OpenObject( - &jobHandle, - JOB_OBJECT_TERMINATE, - jobPageContext->Context - ))) - { - status = NtTerminateJobObject(jobHandle, STATUS_SUCCESS); - NtClose(jobHandle); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to terminate the job", status, 0); - } - } - break; - case IDC_ADD: - { - NTSTATUS status; - HANDLE processId; - HANDLE processHandle; - HANDLE jobHandle; - - while (PhShowChooseProcessDialog( - hwndDlg, - L"Select a process to add to the job permanently.", - &processId - )) - { - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_TERMINATE | PROCESS_SET_QUOTA, - processId - ))) - { - if (NT_SUCCESS(status = jobPageContext->OpenObject( - &jobHandle, - JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_QUERY, - jobPageContext->Context - ))) - { - status = NtAssignProcessToJobObject(jobHandle, processHandle); - - if (NT_SUCCESS(status)) - { - ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_PROCESSES)); - PhpAddJobProcesses(hwndDlg, jobHandle); - } - - NtClose(jobHandle); - } - - NtClose(processHandle); - } - - if (NT_SUCCESS(status)) - break; - else - PhShowStatus(hwndDlg, L"Unable to add the process to the job", status, 0); - } - } - break; - case IDC_ADVANCED: - { - PhpShowJobAdvancedProperties(hwndDlg, jobPageContext); - } - break; - } - } - break; - case WM_NOTIFY: - { - PhHandleListViewNotifyBehaviors(lParam, GetDlgItem(hwndDlg, IDC_PROCESSES), PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - PhHandleListViewNotifyBehaviors(lParam, GetDlgItem(hwndDlg, IDC_LIMITS), PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - } - break; - } - - return FALSE; -} - -VOID PhpShowJobAdvancedProperties( - _In_ HWND ParentWindowHandle, - _In_ PJOB_PAGE_CONTEXT Context - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - HPROPSHEETPAGE pages[2]; - PROPSHEETPAGE statisticsPage; - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = L"Job"; - propSheetHeader.nPages = 2; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - // General - - memset(&statisticsPage, 0, sizeof(PROPSHEETPAGE)); - statisticsPage.dwSize = sizeof(PROPSHEETPAGE); - statisticsPage.pszTemplate = MAKEINTRESOURCE(IDD_JOBSTATISTICS); - statisticsPage.pfnDlgProc = PhpJobStatisticsPageProc; - statisticsPage.lParam = (LPARAM)Context; - pages[0] = CreatePropertySheetPage(&statisticsPage); - - // Security - - stdObjectSecurity.OpenObject = Context->OpenObject; - stdObjectSecurity.ObjectType = L"Job"; - stdObjectSecurity.Context = Context->Context; - - if (PhGetAccessEntries(L"Job", &accessEntries, &numberOfAccessEntries)) - { - pages[1] = PhCreateSecurityPage( - L"Job", - PhStdGetObjectSecurity, - PhStdSetObjectSecurity, - &stdObjectSecurity, - accessEntries, - numberOfAccessEntries - ); - PhFree(accessEntries); - } - - PhModalPropertySheet(&propSheetHeader); -} - -static VOID PhpRefreshJobStatisticsInfo( - _In_ HWND hwndDlg, - _In_ PJOB_PAGE_CONTEXT Context - ) -{ - HANDLE jobHandle = NULL; - JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION basicAndIo; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedLimitInfo; - - Context->OpenObject( - &jobHandle, - JOB_OBJECT_QUERY, - Context->Context - ); - - if (jobHandle && NT_SUCCESS(PhGetJobBasicAndIoAccounting( - jobHandle, - &basicAndIo - ))) - { - WCHAR timeSpan[PH_TIMESPAN_STR_LEN_1]; - - SetDlgItemInt(hwndDlg, IDC_ZACTIVEPROCESSES_V, basicAndIo.BasicInfo.ActiveProcesses, FALSE); - SetDlgItemInt(hwndDlg, IDC_ZTOTALPROCESSES_V, basicAndIo.BasicInfo.TotalProcesses, FALSE); - SetDlgItemInt(hwndDlg, IDC_ZTERMINATEDPROCESSES_V, basicAndIo.BasicInfo.TotalTerminatedProcesses, FALSE); - - PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.TotalUserTime.QuadPart, PH_TIMESPAN_HMSM); - SetDlgItemText(hwndDlg, IDC_ZUSERTIME_V, timeSpan); - PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.TotalKernelTime.QuadPart, PH_TIMESPAN_HMSM); - SetDlgItemText(hwndDlg, IDC_ZKERNELTIME_V, timeSpan); - PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.ThisPeriodTotalUserTime.QuadPart, PH_TIMESPAN_HMSM); - SetDlgItemText(hwndDlg, IDC_ZUSERTIMEPERIOD_V, timeSpan); - PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart, PH_TIMESPAN_HMSM); - SetDlgItemText(hwndDlg, IDC_ZKERNELTIMEPERIOD_V, timeSpan); - - SetDlgItemText(hwndDlg, IDC_ZPAGEFAULTS_V, PhaFormatUInt64(basicAndIo.BasicInfo.TotalPageFaultCount, TRUE)->Buffer); - - SetDlgItemText(hwndDlg, IDC_ZIOREADS_V, PhaFormatUInt64(basicAndIo.IoInfo.ReadOperationCount, TRUE)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZIOREADBYTES_V, PhaFormatSize(basicAndIo.IoInfo.ReadTransferCount, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZIOWRITES_V, PhaFormatUInt64(basicAndIo.IoInfo.WriteOperationCount, TRUE)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZIOWRITEBYTES_V, PhaFormatSize(basicAndIo.IoInfo.WriteTransferCount, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZIOOTHER_V, PhaFormatUInt64(basicAndIo.IoInfo.OtherOperationCount, TRUE)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZIOOTHERBYTES_V, PhaFormatSize(basicAndIo.IoInfo.OtherTransferCount, -1)->Buffer); - } - else - { - SetDlgItemText(hwndDlg, IDC_ZACTIVEPROCESSES_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZTOTALPROCESSES_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZTERMINATEDPROCESSES_V, L"Unknown"); - - SetDlgItemText(hwndDlg, IDC_ZUSERTIME_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZKERNELTIME_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZUSERTIMEPERIOD_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZKERNELTIMEPERIOD_V, L"Unknown"); - - SetDlgItemText(hwndDlg, IDC_ZPAGEFAULTS_V, L"Unknown"); - - SetDlgItemText(hwndDlg, IDC_ZIOREADS_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZIOREADBYTES_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZIOWRITES_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZIOWRITEBYTES_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZIOOTHER_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZIOOTHERBYTES_V, L"Unknown"); - } - - if (jobHandle && NT_SUCCESS(PhGetJobExtendedLimits( - jobHandle, - &extendedLimitInfo - ))) - { - SetDlgItemText(hwndDlg, IDC_ZPEAKPROCESSUSAGE_V, PhaFormatSize(extendedLimitInfo.PeakProcessMemoryUsed, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZPEAKJOBUSAGE_V, PhaFormatSize(extendedLimitInfo.PeakJobMemoryUsed, -1)->Buffer); - } - else - { - SetDlgItemText(hwndDlg, IDC_ZPEAKPROCESSUSAGE_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZPEAKJOBUSAGE_V, L"Unknown"); - } - - if (jobHandle) - NtClose(jobHandle); -} - -INT_PTR CALLBACK PhpJobStatisticsPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PJOB_PAGE_CONTEXT jobPageContext; - - jobPageContext = PhpJobPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!jobPageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - // HACK - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - - PhpRefreshJobStatisticsInfo(hwndDlg, jobPageContext); - SetTimer(hwndDlg, 1, PhGetIntegerSetting(L"UpdateInterval"), NULL); - } - break; - case WM_TIMER: - { - if (wParam == 1) - { - PhpRefreshJobStatisticsInfo(hwndDlg, jobPageContext); - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * job properties + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MSG_UPDATE (WM_APP + 1) + +typedef struct _JOB_PAGE_CONTEXT +{ + PPH_OPEN_OBJECT OpenObject; + PVOID Context; + DLGPROC HookProc; + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; +} JOB_PAGE_CONTEXT, *PJOB_PAGE_CONTEXT; + +INT CALLBACK PhpJobPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +INT_PTR CALLBACK PhpJobPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhpShowJobAdvancedProperties( + _In_ HWND ParentWindowHandle, + _In_ PJOB_PAGE_CONTEXT Context + ); + +INT_PTR CALLBACK PhpJobStatisticsPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowJobProperties( + _In_ HWND ParentWindowHandle, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context, + _In_opt_ PWSTR Title + ) +{ + PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; + HPROPSHEETPAGE pages[1]; + + propSheetHeader.dwFlags = + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.hwndParent = ParentWindowHandle; + propSheetHeader.pszCaption = Title ? Title : L"Job"; + propSheetHeader.nPages = 1; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = pages; + + pages[0] = PhCreateJobPage(OpenObject, Context, NULL); + + PhModalPropertySheet(&propSheetHeader); +} + +HPROPSHEETPAGE PhCreateJobPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context, + _In_opt_ DLGPROC HookProc + ) +{ + HPROPSHEETPAGE propSheetPageHandle; + PROPSHEETPAGE propSheetPage; + PJOB_PAGE_CONTEXT jobPageContext; + + jobPageContext = PhCreateAlloc(sizeof(JOB_PAGE_CONTEXT)); + memset(jobPageContext, 0, sizeof(JOB_PAGE_CONTEXT)); + jobPageContext->OpenObject = OpenObject; + jobPageContext->Context = Context; + jobPageContext->HookProc = HookProc; + + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USECALLBACK; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OBJJOB); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = PhpJobPageProc; + propSheetPage.lParam = (LPARAM)jobPageContext; + propSheetPage.pfnCallback = PhpJobPropPageProc; + + propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); + // CreatePropertySheetPage would have sent PSPCB_ADDREF (below), + // which would have added a reference. + PhDereferenceObject(jobPageContext); + + return propSheetPageHandle; +} + +INT CALLBACK PhpJobPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) +{ + PJOB_PAGE_CONTEXT jobPageContext; + + jobPageContext = (PJOB_PAGE_CONTEXT)ppsp->lParam; + + if (uMsg == PSPCB_ADDREF) + { + PhReferenceObject(jobPageContext); + } + else if (uMsg == PSPCB_RELEASE) + { + PhDereferenceObject(jobPageContext); + } + + return 1; +} + +FORCEINLINE PJOB_PAGE_CONTEXT PhpJobPageHeader( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return PhpGenericPropertyPageHeader(hwndDlg, uMsg, wParam, lParam, 1); +} + +static VOID PhpAddLimit( + _In_ HWND Handle, + _In_ PWSTR Name, + _In_ PWSTR Value + ) +{ + INT lvItemIndex; + + lvItemIndex = PhAddListViewItem(Handle, MAXINT, Name, NULL); + PhSetListViewSubItem(Handle, lvItemIndex, 1, Value); +} + +static VOID PhpAddJobProcesses( + _In_ HWND hwndDlg, + _In_ HANDLE JobHandle + ) +{ + PJOBOBJECT_BASIC_PROCESS_ID_LIST processIdList; + HWND processesLv; + + processesLv = GetDlgItem(hwndDlg, IDC_PROCESSES); + + if (NT_SUCCESS(PhGetJobProcessIdList(JobHandle, &processIdList))) + { + ULONG i; + CLIENT_ID clientId; + PPH_STRING name; + + clientId.UniqueThread = NULL; + + for (i = 0; i < processIdList->NumberOfProcessIdsInList; i++) + { + clientId.UniqueProcess = (HANDLE)processIdList->ProcessIdList[i]; + name = PH_AUTO(PhGetClientIdName(&clientId)); + + PhAddListViewItem(processesLv, MAXINT, PhGetString(name), NULL); + } + + PhFree(processIdList); + } +} + +INT_PTR CALLBACK PhpJobPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PJOB_PAGE_CONTEXT jobPageContext; + + jobPageContext = PhpJobPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!jobPageContext) + return FALSE; + + if (jobPageContext->HookProc) + { + if (jobPageContext->HookProc(hwndDlg, uMsg, wParam, lParam)) + return TRUE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE jobHandle; + HWND processesLv; + HWND limitsLv; + + processesLv = GetDlgItem(hwndDlg, IDC_PROCESSES); + limitsLv = GetDlgItem(hwndDlg, IDC_LIMITS); + PhSetListViewStyle(processesLv, FALSE, TRUE); + PhSetListViewStyle(limitsLv, FALSE, TRUE); + PhSetControlTheme(processesLv, L"explorer"); + PhSetControlTheme(limitsLv, L"explorer"); + PhSetExtendedListView(processesLv); + PhSetExtendedListView(limitsLv); + + PhAddListViewColumn(processesLv, 0, 0, 0, LVCFMT_LEFT, 240, L"Name"); + PhAddListViewColumn(limitsLv, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); + PhAddListViewColumn(limitsLv, 1, 1, 1, LVCFMT_LEFT, 160, L"Value"); + PhLoadListViewColumnsFromSetting(L"JobListViewColumns", limitsLv); + + PhSetDialogItemText(hwndDlg, IDC_NAME, L"Unknown"); + + if (NT_SUCCESS(jobPageContext->OpenObject( + &jobHandle, + JOB_OBJECT_QUERY, + jobPageContext->Context + ))) + { + PPH_STRING jobObjectName = NULL; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedLimits; + JOBOBJECT_BASIC_UI_RESTRICTIONS basicUiRestrictions; + + // Name + + PhGetHandleInformation( + NtCurrentProcess(), + jobHandle, + ULONG_MAX, + NULL, + NULL, + NULL, + &jobObjectName + ); + PH_AUTO(jobObjectName); + + if (jobObjectName && jobObjectName->Length == 0) + jobObjectName = NULL; + + PhSetDialogItemText(hwndDlg, IDC_NAME, PhGetStringOrDefault(jobObjectName, L"(unnamed job)")); + + // Processes + PhpAddJobProcesses(hwndDlg, jobHandle); + + // Limits + + if (NT_SUCCESS(PhGetJobExtendedLimits(jobHandle, &extendedLimits))) + { + ULONG flags = extendedLimits.BasicLimitInformation.LimitFlags; + + if (flags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS) + { + WCHAR value[PH_INT32_STR_LEN_1]; + PhPrintUInt32(value, extendedLimits.BasicLimitInformation.ActiveProcessLimit); + PhpAddLimit(limitsLv, L"Active processes", value); + } + + if (flags & JOB_OBJECT_LIMIT_AFFINITY) + { + WCHAR value[PH_PTR_STR_LEN_1]; + PhPrintPointer(value, (PVOID)extendedLimits.BasicLimitInformation.Affinity); + PhpAddLimit(limitsLv, L"Affinity", value); + } + + if (flags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) + { + PhpAddLimit(limitsLv, L"Breakaway OK", L"Enabled"); + } + + if (flags & JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION) + { + PhpAddLimit(limitsLv, L"Die on unhandled exception", L"Enabled"); + } + + if (flags & JOB_OBJECT_LIMIT_JOB_MEMORY) + { + PPH_STRING value = PhaFormatSize(extendedLimits.JobMemoryLimit, -1); + PhpAddLimit(limitsLv, L"Job memory", value->Buffer); + } + + if (flags & JOB_OBJECT_LIMIT_JOB_TIME) + { + WCHAR value[PH_TIMESPAN_STR_LEN_1]; + PhPrintTimeSpan(value, extendedLimits.BasicLimitInformation.PerJobUserTimeLimit.QuadPart, + PH_TIMESPAN_DHMS); + PhpAddLimit(limitsLv, L"Job time", value); + } + + if (flags & JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) + { + PhpAddLimit(limitsLv, L"Kill on job close", L"Enabled"); + } + + if (flags & JOB_OBJECT_LIMIT_PRIORITY_CLASS) + { + PhpAddLimit(limitsLv, L"Priority class", + PhGetProcessPriorityClassString(extendedLimits.BasicLimitInformation.PriorityClass)); + } + + if (flags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) + { + PPH_STRING value = PhaFormatSize(extendedLimits.ProcessMemoryLimit, -1); + PhpAddLimit(limitsLv, L"Process memory", value->Buffer); + } + + if (flags & JOB_OBJECT_LIMIT_PROCESS_TIME) + { + WCHAR value[PH_TIMESPAN_STR_LEN_1]; + PhPrintTimeSpan(value, extendedLimits.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart, + PH_TIMESPAN_DHMS); + PhpAddLimit(limitsLv, L"Process time", value); + } + + if (flags & JOB_OBJECT_LIMIT_SCHEDULING_CLASS) + { + WCHAR value[PH_INT32_STR_LEN_1]; + PhPrintUInt32(value, extendedLimits.BasicLimitInformation.SchedulingClass); + PhpAddLimit(limitsLv, L"Scheduling class", value); + } + + if (flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) + { + PhpAddLimit(limitsLv, L"Silent breakaway OK", L"Enabled"); + } + + if (flags & JOB_OBJECT_LIMIT_WORKINGSET) + { + PPH_STRING value; + + value = PhaFormatSize(extendedLimits.BasicLimitInformation.MinimumWorkingSetSize, -1); + PhpAddLimit(limitsLv, L"Working set minimum", value->Buffer); + + value = PhaFormatSize(extendedLimits.BasicLimitInformation.MaximumWorkingSetSize, -1); + PhpAddLimit(limitsLv, L"Working set maximum", value->Buffer); + } + } + + if (NT_SUCCESS(PhGetJobBasicUiRestrictions(jobHandle, &basicUiRestrictions))) + { + ULONG flags = basicUiRestrictions.UIRestrictionsClass; + + if (flags & JOB_OBJECT_UILIMIT_DESKTOP) + PhpAddLimit(limitsLv, L"Desktop", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_DISPLAYSETTINGS) + PhpAddLimit(limitsLv, L"Display settings", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_EXITWINDOWS) + PhpAddLimit(limitsLv, L"Exit windows", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_GLOBALATOMS) + PhpAddLimit(limitsLv, L"Global atoms", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_HANDLES) + PhpAddLimit(limitsLv, L"Handles", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_READCLIPBOARD) + PhpAddLimit(limitsLv, L"Read clipboard", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS) + PhpAddLimit(limitsLv, L"System parameters", L"Limited"); + if (flags & JOB_OBJECT_UILIMIT_WRITECLIPBOARD) + PhpAddLimit(limitsLv, L"Write clipboard", L"Limited"); + } + + NtClose(jobHandle); + } + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + PhSaveListViewColumnsToSetting(L"JobListViewColumns", GetDlgItem(hwndDlg, IDC_LIMITS)); + break; + case WM_SHOWWINDOW: + { + ExtendedListView_SetColumnWidth(GetDlgItem(hwndDlg, IDC_PROCESSES), 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_TERMINATE: + { + if (PhShowConfirmMessage( + hwndDlg, + L"terminate", + L"the job", + L"Terminating a job will terminate all processes assigned to it.", + TRUE + )) + { + NTSTATUS status; + HANDLE jobHandle; + + if (NT_SUCCESS(status = jobPageContext->OpenObject( + &jobHandle, + JOB_OBJECT_TERMINATE, + jobPageContext->Context + ))) + { + status = NtTerminateJobObject(jobHandle, STATUS_SUCCESS); + NtClose(jobHandle); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to terminate the job", status, 0); + } + } + break; + case IDC_ADD: + { + NTSTATUS status; + HANDLE processId; + HANDLE processHandle; + HANDLE jobHandle; + + while (PhShowChooseProcessDialog( + hwndDlg, + L"Select a process to add to the job permanently.", + &processId + )) + { + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_TERMINATE | PROCESS_SET_QUOTA, + processId + ))) + { + if (NT_SUCCESS(status = jobPageContext->OpenObject( + &jobHandle, + JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_QUERY, + jobPageContext->Context + ))) + { + status = NtAssignProcessToJobObject(jobHandle, processHandle); + + if (NT_SUCCESS(status)) + { + ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_PROCESSES)); + PhpAddJobProcesses(hwndDlg, jobHandle); + } + + NtClose(jobHandle); + } + + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + break; + else + PhShowStatus(hwndDlg, L"Unable to add the process to the job", status, 0); + } + } + break; + case IDC_ADVANCED: + { + PhpShowJobAdvancedProperties(hwndDlg, jobPageContext); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_PROCESSES)); + return TRUE; + } + + PhHandleListViewNotifyBehaviors(lParam, GetDlgItem(hwndDlg, IDC_PROCESSES), PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + PhHandleListViewNotifyBehaviors(lParam, GetDlgItem(hwndDlg, IDC_LIMITS), PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_SIZE: + { + ExtendedListView_SetColumnWidth(GetDlgItem(hwndDlg, IDC_PROCESSES), 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_CONTEXTMENU: + { + HWND listViewHandle = NULL; + + if ((HWND)wParam == GetDlgItem(hwndDlg, IDC_PROCESSES)) + listViewHandle = GetDlgItem(hwndDlg, IDC_PROCESSES); + else if ((HWND)wParam == GetDlgItem(hwndDlg, IDC_LIMITS)) + listViewHandle = GetDlgItem(hwndDlg, IDC_LIMITS); + + if (listViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(listViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, listViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(listViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} + +VOID PhpShowJobAdvancedProperties( + _In_ HWND ParentWindowHandle, + _In_ PJOB_PAGE_CONTEXT Context + ) +{ + PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; + HPROPSHEETPAGE pages[2]; + PROPSHEETPAGE statisticsPage; + + propSheetHeader.dwFlags = + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.hwndParent = ParentWindowHandle; + propSheetHeader.pszCaption = L"Job"; + propSheetHeader.nPages = 2; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = pages; + + // General + + memset(&statisticsPage, 0, sizeof(PROPSHEETPAGE)); + statisticsPage.dwSize = sizeof(PROPSHEETPAGE); + statisticsPage.pszTemplate = MAKEINTRESOURCE(IDD_JOBSTATISTICS); + statisticsPage.hInstance = PhInstanceHandle; + statisticsPage.pfnDlgProc = PhpJobStatisticsPageProc; + statisticsPage.lParam = (LPARAM)Context; + pages[0] = CreatePropertySheetPage(&statisticsPage); + + // Security + + pages[1] = PhCreateSecurityPage( + L"Job", + L"Job", + Context->OpenObject, + NULL, + Context->Context + ); + + PhModalPropertySheet(&propSheetHeader); +} + +static VOID PhpRefreshJobStatisticsInfo( + _In_ HWND hwndDlg, + _In_ PJOB_PAGE_CONTEXT Context + ) +{ + HANDLE jobHandle = NULL; + JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION basicAndIo; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedLimitInfo; + + Context->OpenObject( + &jobHandle, + JOB_OBJECT_QUERY, + Context->Context + ); + + if (jobHandle && NT_SUCCESS(PhGetJobBasicAndIoAccounting( + jobHandle, + &basicAndIo + ))) + { + WCHAR timeSpan[PH_TIMESPAN_STR_LEN_1]; + + PhSetDialogItemValue(hwndDlg, IDC_ZACTIVEPROCESSES_V, basicAndIo.BasicInfo.ActiveProcesses, FALSE); + PhSetDialogItemValue(hwndDlg, IDC_ZTOTALPROCESSES_V, basicAndIo.BasicInfo.TotalProcesses, FALSE); + PhSetDialogItemValue(hwndDlg, IDC_ZTERMINATEDPROCESSES_V, basicAndIo.BasicInfo.TotalTerminatedProcesses, FALSE); + + PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.TotalUserTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetDialogItemText(hwndDlg, IDC_ZUSERTIME_V, timeSpan); + PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.TotalKernelTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetDialogItemText(hwndDlg, IDC_ZKERNELTIME_V, timeSpan); + PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.ThisPeriodTotalUserTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetDialogItemText(hwndDlg, IDC_ZUSERTIMEPERIOD_V, timeSpan); + PhPrintTimeSpan(timeSpan, basicAndIo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetDialogItemText(hwndDlg, IDC_ZKERNELTIMEPERIOD_V, timeSpan); + + PhSetDialogItemText(hwndDlg, IDC_ZPAGEFAULTS_V, PhaFormatUInt64(basicAndIo.BasicInfo.TotalPageFaultCount, TRUE)->Buffer); + + PhSetDialogItemText(hwndDlg, IDC_ZIOREADS_V, PhaFormatUInt64(basicAndIo.IoInfo.ReadOperationCount, TRUE)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZIOREADBYTES_V, PhaFormatSize(basicAndIo.IoInfo.ReadTransferCount, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZIOWRITES_V, PhaFormatUInt64(basicAndIo.IoInfo.WriteOperationCount, TRUE)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZIOWRITEBYTES_V, PhaFormatSize(basicAndIo.IoInfo.WriteTransferCount, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZIOOTHER_V, PhaFormatUInt64(basicAndIo.IoInfo.OtherOperationCount, TRUE)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZIOOTHERBYTES_V, PhaFormatSize(basicAndIo.IoInfo.OtherTransferCount, ULONG_MAX)->Buffer); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_ZACTIVEPROCESSES_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZTOTALPROCESSES_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZTERMINATEDPROCESSES_V, L"Unknown"); + + PhSetDialogItemText(hwndDlg, IDC_ZUSERTIME_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZKERNELTIME_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZUSERTIMEPERIOD_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZKERNELTIMEPERIOD_V, L"Unknown"); + + PhSetDialogItemText(hwndDlg, IDC_ZPAGEFAULTS_V, L"Unknown"); + + PhSetDialogItemText(hwndDlg, IDC_ZIOREADS_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZIOREADBYTES_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZIOWRITES_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZIOWRITEBYTES_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZIOOTHER_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZIOOTHERBYTES_V, L"Unknown"); + } + + if (jobHandle && NT_SUCCESS(PhGetJobExtendedLimits( + jobHandle, + &extendedLimitInfo + ))) + { + PhSetDialogItemText(hwndDlg, IDC_ZPEAKPROCESSUSAGE_V, PhaFormatSize(extendedLimitInfo.PeakProcessMemoryUsed, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZPEAKJOBUSAGE_V, PhaFormatSize(extendedLimitInfo.PeakJobMemoryUsed, ULONG_MAX)->Buffer); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_ZPEAKPROCESSUSAGE_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZPEAKJOBUSAGE_V, L"Unknown"); + } + + if (jobHandle) + NtClose(jobHandle); +} + +static VOID NTAPI ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PostMessage(Context, MSG_UPDATE, 0, 0); +} + +INT_PTR CALLBACK PhpJobStatisticsPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PJOB_PAGE_CONTEXT jobPageContext; + + jobPageContext = PhpJobPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!jobPageContext) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); // HACK + + PhpRefreshJobStatisticsInfo(hwndDlg, jobPageContext); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + ProcessesUpdatedCallback, + hwndDlg, + &jobPageContext->ProcessesUpdatedRegistration + ); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &jobPageContext->ProcessesUpdatedRegistration); + } + break; + case MSG_UPDATE: + { + PhpRefreshJobStatisticsInfo(hwndDlg, jobPageContext); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/log.c b/ProcessHacker/log.c index 64e799ae92a0..8b183142e93d 100644 --- a/ProcessHacker/log.c +++ b/ProcessHacker/log.c @@ -1,240 +1,239 @@ -/* - * Process Hacker - - * logging system - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; -PHAPPAPI PH_CALLBACK_DECLARE(PhLoggedCallback); - -VOID PhLogInitialization( - VOID - ) -{ - ULONG entries; - - entries = PhGetIntegerSetting(L"LogEntries"); - if (entries > 0x1000) entries = 0x1000; - PhInitializeCircularBuffer_PVOID(&PhLogBuffer, entries); - memset(PhLogBuffer.Data, 0, sizeof(PVOID) * PhLogBuffer.Size); -} - -PPH_LOG_ENTRY PhpCreateLogEntry( - _In_ UCHAR Type - ) -{ - PPH_LOG_ENTRY entry; - - entry = PhAllocate(sizeof(PH_LOG_ENTRY)); - memset(entry, 0, sizeof(PH_LOG_ENTRY)); - - entry->Type = Type; - PhQuerySystemTime(&entry->Time); - - return entry; -} - -VOID PhpFreeLogEntry( - _Inout_ PPH_LOG_ENTRY Entry - ) -{ - if (Entry->Type >= PH_LOG_ENTRY_PROCESS_FIRST && Entry->Type <= PH_LOG_ENTRY_PROCESS_LAST) - { - PhDereferenceObject(Entry->Process.Name); - if (Entry->Process.ParentName) PhDereferenceObject(Entry->Process.ParentName); - } - else if (Entry->Type >= PH_LOG_ENTRY_SERVICE_FIRST && Entry->Type <= PH_LOG_ENTRY_SERVICE_LAST) - { - PhDereferenceObject(Entry->Service.Name); - PhDereferenceObject(Entry->Service.DisplayName); - } - else if (Entry->Type == PH_LOG_ENTRY_MESSAGE) - { - PhDereferenceObject(Entry->Message); - } - - PhFree(Entry); -} - -PPH_LOG_ENTRY PhpCreateProcessLogEntry( - _In_ UCHAR Type, - _In_ HANDLE ProcessId, - _In_opt_ HANDLE QueryHandle, - _In_ PPH_STRING Name, - _In_opt_ HANDLE ParentProcessId, - _In_opt_ PPH_STRING ParentName - ) -{ - PPH_LOG_ENTRY entry; - - entry = PhpCreateLogEntry(Type); - entry->Process.ProcessId = ProcessId; - PhReferenceObject(Name); - entry->Process.Name = Name; - - entry->Process.ParentProcessId = ParentProcessId; - - if (ParentName) - { - PhReferenceObject(ParentName); - entry->Process.ParentName = ParentName; - } - - if (QueryHandle && entry->Type == PH_LOG_ENTRY_PROCESS_DELETE) - { - PROCESS_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhGetProcessBasicInformation(QueryHandle, &basicInfo))) - { - entry->Process.ExitStatus = basicInfo.ExitStatus; - } - } - - return entry; -} - -PPH_LOG_ENTRY PhpCreateServiceLogEntry( - _In_ UCHAR Type, - _In_ PPH_STRING Name, - _In_ PPH_STRING DisplayName - ) -{ - PPH_LOG_ENTRY entry; - - entry = PhpCreateLogEntry(Type); - PhReferenceObject(Name); - entry->Service.Name = Name; - PhReferenceObject(DisplayName); - entry->Service.DisplayName = DisplayName; - - return entry; -} - -PPH_LOG_ENTRY PhpCreateMessageLogEntry( - _In_ UCHAR Type, - _In_ PPH_STRING Message - ) -{ - PPH_LOG_ENTRY entry; - - entry = PhpCreateLogEntry(Type); - PhReferenceObject(Message); - entry->Message = Message; - - return entry; -} - -VOID PhpLogEntry( - _In_ PPH_LOG_ENTRY Entry - ) -{ - PPH_LOG_ENTRY oldEntry; - - oldEntry = PhAddItemCircularBuffer2_PVOID(&PhLogBuffer, Entry); - - if (oldEntry) - PhpFreeLogEntry(oldEntry); - - PhInvokeCallback(&PhLoggedCallback, Entry); -} - -VOID PhClearLogEntries( - VOID - ) -{ - ULONG i; - - for (i = 0; i < PhLogBuffer.Size; i++) - { - if (PhLogBuffer.Data[i]) - PhpFreeLogEntry(PhLogBuffer.Data[i]); - } - - PhClearCircularBuffer_PVOID(&PhLogBuffer); - memset(PhLogBuffer.Data, 0, sizeof(PVOID) * PhLogBuffer.Size); -} - -VOID PhLogProcessEntry( - _In_ UCHAR Type, - _In_ HANDLE ProcessId, - _In_opt_ HANDLE QueryHandle, - _In_ PPH_STRING Name, - _In_opt_ HANDLE ParentProcessId, - _In_opt_ PPH_STRING ParentName - ) -{ - PhpLogEntry(PhpCreateProcessLogEntry(Type, ProcessId, QueryHandle, Name, ParentProcessId, ParentName)); -} - -VOID PhLogServiceEntry( - _In_ UCHAR Type, - _In_ PPH_STRING Name, - _In_ PPH_STRING DisplayName - ) -{ - PhpLogEntry(PhpCreateServiceLogEntry(Type, Name, DisplayName)); -} - -VOID PhLogMessageEntry( - _In_ UCHAR Type, - _In_ PPH_STRING Message - ) -{ - PhpLogEntry(PhpCreateMessageLogEntry(Type, Message)); -} - -PPH_STRING PhFormatLogEntry( - _In_ PPH_LOG_ENTRY Entry - ) -{ - switch (Entry->Type) - { - case PH_LOG_ENTRY_PROCESS_CREATE: - return PhFormatString( - L"Process created: %s (%u) started by %s (%u)", - Entry->Process.Name->Buffer, - HandleToUlong(Entry->Process.ProcessId), - PhGetStringOrDefault(Entry->Process.ParentName, L"Unknown process"), - HandleToUlong(Entry->Process.ParentProcessId) - ); - case PH_LOG_ENTRY_PROCESS_DELETE: - return PhFormatString(L"Process terminated: %s (%u); exit status 0x%x", Entry->Process.Name->Buffer, HandleToUlong(Entry->Process.ProcessId), Entry->Process.ExitStatus); - case PH_LOG_ENTRY_SERVICE_CREATE: - return PhFormatString(L"Service created: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); - case PH_LOG_ENTRY_SERVICE_DELETE: - return PhFormatString(L"Service deleted: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); - case PH_LOG_ENTRY_SERVICE_START: - return PhFormatString(L"Service started: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); - case PH_LOG_ENTRY_SERVICE_STOP: - return PhFormatString(L"Service stopped: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); - case PH_LOG_ENTRY_SERVICE_CONTINUE: - return PhFormatString(L"Service continued: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); - case PH_LOG_ENTRY_SERVICE_PAUSE: - return PhFormatString(L"Service paused: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); - case PH_LOG_ENTRY_MESSAGE: - PhReferenceObject(Entry->Message); - return Entry->Message; - default: - return PhReferenceEmptyString(); - } -} +/* + * Process Hacker - + * logging system + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +PH_CIRCULAR_BUFFER_PVOID PhLogBuffer; + +VOID PhLogInitialization( + VOID + ) +{ + ULONG entries; + + entries = PhGetIntegerSetting(L"LogEntries"); + if (entries > 0x1000) entries = 0x1000; + PhInitializeCircularBuffer_PVOID(&PhLogBuffer, entries); + memset(PhLogBuffer.Data, 0, sizeof(PVOID) * PhLogBuffer.Size); +} + +PPH_LOG_ENTRY PhpCreateLogEntry( + _In_ UCHAR Type + ) +{ + PPH_LOG_ENTRY entry; + + entry = PhAllocate(sizeof(PH_LOG_ENTRY)); + memset(entry, 0, sizeof(PH_LOG_ENTRY)); + + entry->Type = Type; + PhQuerySystemTime(&entry->Time); + + return entry; +} + +VOID PhpFreeLogEntry( + _Inout_ PPH_LOG_ENTRY Entry + ) +{ + if (Entry->Type >= PH_LOG_ENTRY_PROCESS_FIRST && Entry->Type <= PH_LOG_ENTRY_PROCESS_LAST) + { + PhDereferenceObject(Entry->Process.Name); + if (Entry->Process.ParentName) PhDereferenceObject(Entry->Process.ParentName); + } + else if (Entry->Type >= PH_LOG_ENTRY_SERVICE_FIRST && Entry->Type <= PH_LOG_ENTRY_SERVICE_LAST) + { + PhDereferenceObject(Entry->Service.Name); + PhDereferenceObject(Entry->Service.DisplayName); + } + else if (Entry->Type == PH_LOG_ENTRY_MESSAGE) + { + PhDereferenceObject(Entry->Message); + } + + PhFree(Entry); +} + +PPH_LOG_ENTRY PhpCreateProcessLogEntry( + _In_ UCHAR Type, + _In_ HANDLE ProcessId, + _In_opt_ HANDLE QueryHandle, + _In_ PPH_STRING Name, + _In_opt_ HANDLE ParentProcessId, + _In_opt_ PPH_STRING ParentName + ) +{ + PPH_LOG_ENTRY entry; + + entry = PhpCreateLogEntry(Type); + entry->Process.ProcessId = ProcessId; + PhReferenceObject(Name); + entry->Process.Name = Name; + + entry->Process.ParentProcessId = ParentProcessId; + + if (ParentName) + { + PhReferenceObject(ParentName); + entry->Process.ParentName = ParentName; + } + + if (QueryHandle && entry->Type == PH_LOG_ENTRY_PROCESS_DELETE) + { + PROCESS_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetProcessBasicInformation(QueryHandle, &basicInfo))) + { + entry->Process.ExitStatus = basicInfo.ExitStatus; + } + } + + return entry; +} + +PPH_LOG_ENTRY PhpCreateServiceLogEntry( + _In_ UCHAR Type, + _In_ PPH_STRING Name, + _In_ PPH_STRING DisplayName + ) +{ + PPH_LOG_ENTRY entry; + + entry = PhpCreateLogEntry(Type); + PhReferenceObject(Name); + entry->Service.Name = Name; + PhReferenceObject(DisplayName); + entry->Service.DisplayName = DisplayName; + + return entry; +} + +PPH_LOG_ENTRY PhpCreateMessageLogEntry( + _In_ UCHAR Type, + _In_ PPH_STRING Message + ) +{ + PPH_LOG_ENTRY entry; + + entry = PhpCreateLogEntry(Type); + PhReferenceObject(Message); + entry->Message = Message; + + return entry; +} + +VOID PhpLogEntry( + _In_ PPH_LOG_ENTRY Entry + ) +{ + PPH_LOG_ENTRY oldEntry; + + oldEntry = PhAddItemCircularBuffer2_PVOID(&PhLogBuffer, Entry); + + if (oldEntry) + PhpFreeLogEntry(oldEntry); + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), Entry); +} + +VOID PhClearLogEntries( + VOID + ) +{ + ULONG i; + + for (i = 0; i < PhLogBuffer.Size; i++) + { + if (PhLogBuffer.Data[i]) + PhpFreeLogEntry(PhLogBuffer.Data[i]); + } + + PhClearCircularBuffer_PVOID(&PhLogBuffer); + memset(PhLogBuffer.Data, 0, sizeof(PVOID) * PhLogBuffer.Size); +} + +VOID PhLogProcessEntry( + _In_ UCHAR Type, + _In_ HANDLE ProcessId, + _In_opt_ HANDLE QueryHandle, + _In_ PPH_STRING Name, + _In_opt_ HANDLE ParentProcessId, + _In_opt_ PPH_STRING ParentName + ) +{ + PhpLogEntry(PhpCreateProcessLogEntry(Type, ProcessId, QueryHandle, Name, ParentProcessId, ParentName)); +} + +VOID PhLogServiceEntry( + _In_ UCHAR Type, + _In_ PPH_STRING Name, + _In_ PPH_STRING DisplayName + ) +{ + PhpLogEntry(PhpCreateServiceLogEntry(Type, Name, DisplayName)); +} + +VOID PhLogMessageEntry( + _In_ UCHAR Type, + _In_ PPH_STRING Message + ) +{ + PhpLogEntry(PhpCreateMessageLogEntry(Type, Message)); +} + +PPH_STRING PhFormatLogEntry( + _In_ PPH_LOG_ENTRY Entry + ) +{ + switch (Entry->Type) + { + case PH_LOG_ENTRY_PROCESS_CREATE: + return PhFormatString( + L"Process created: %s (%lu) started by %s (%lu)", + Entry->Process.Name->Buffer, + HandleToUlong(Entry->Process.ProcessId), + PhGetStringOrDefault(Entry->Process.ParentName, L"Unknown process"), + HandleToUlong(Entry->Process.ParentProcessId) + ); + case PH_LOG_ENTRY_PROCESS_DELETE: + return PhFormatString(L"Process terminated: %s (%lu); exit status 0x%x", Entry->Process.Name->Buffer, HandleToUlong(Entry->Process.ProcessId), Entry->Process.ExitStatus); + case PH_LOG_ENTRY_SERVICE_CREATE: + return PhFormatString(L"Service created: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); + case PH_LOG_ENTRY_SERVICE_DELETE: + return PhFormatString(L"Service deleted: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); + case PH_LOG_ENTRY_SERVICE_START: + return PhFormatString(L"Service started: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); + case PH_LOG_ENTRY_SERVICE_STOP: + return PhFormatString(L"Service stopped: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); + case PH_LOG_ENTRY_SERVICE_CONTINUE: + return PhFormatString(L"Service continued: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); + case PH_LOG_ENTRY_SERVICE_PAUSE: + return PhFormatString(L"Service paused: %s (%s)", Entry->Service.Name->Buffer, Entry->Service.DisplayName->Buffer); + case PH_LOG_ENTRY_MESSAGE: + PhReferenceObject(Entry->Message); + return Entry->Message; + default: + return PhReferenceEmptyString(); + } +} diff --git a/ProcessHacker/logwnd.c b/ProcessHacker/logwnd.c index 4c598eaa8e65..d03dd2c8f250 100644 --- a/ProcessHacker/logwnd.c +++ b/ProcessHacker/logwnd.c @@ -1,356 +1,358 @@ -/* - * Process Hacker - - * log window - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -#define WM_PH_LOG_UPDATED (WM_APP + 300) - -INT_PTR CALLBACK PhpLogDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -HWND PhLogWindowHandle = NULL; -static PH_LAYOUT_MANAGER WindowLayoutManager; -static RECT MinimumSize; -static HWND ListViewHandle; -static ULONG ListViewCount; -static PH_CALLBACK_REGISTRATION LoggedRegistration; - -VOID PhShowLogDialog( - VOID - ) -{ - if (!PhLogWindowHandle) - { - PhLogWindowHandle = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_LOG), - PhMainWndHandle, - PhpLogDlgProc - ); - PhRegisterDialog(PhLogWindowHandle); - ShowWindow(PhLogWindowHandle, SW_SHOW); - } - - if (IsIconic(PhLogWindowHandle)) - ShowWindow(PhLogWindowHandle, SW_RESTORE); - else - SetForegroundWindow(PhLogWindowHandle); -} - -static VOID NTAPI LoggedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PostMessage(PhLogWindowHandle, WM_PH_LOG_UPDATED, 0, 0); -} - -static VOID PhpUpdateLogList( - VOID - ) -{ - ListViewCount = PhLogBuffer.Count; - ListView_SetItemCountEx(ListViewHandle, ListViewCount, LVSICF_NOSCROLL); - - if (ListViewCount >= 2 && Button_GetCheck(GetDlgItem(PhLogWindowHandle, IDC_AUTOSCROLL)) == BST_CHECKED) - { - if (ListView_IsItemVisible(ListViewHandle, ListViewCount - 2)) - { - ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE); - } - } -} - -static PPH_STRING PhpGetStringForSelectedLogEntries( - _In_ BOOLEAN All - ) -{ - PH_STRING_BUILDER stringBuilder; - ULONG i; - - if (ListViewCount == 0) - return PhReferenceEmptyString(); - - PhInitializeStringBuilder(&stringBuilder, 0x100); - - i = ListViewCount - 1; - - while (TRUE) - { - PPH_LOG_ENTRY entry; - SYSTEMTIME systemTime; - PPH_STRING temp; - - if (!All) - { - // The list view displays the items in reverse order... - if (!(ListView_GetItemState(ListViewHandle, ListViewCount - i - 1, LVIS_SELECTED) & LVIS_SELECTED)) - { - goto ContinueLoop; - } - } - - entry = PhGetItemCircularBuffer_PVOID(&PhLogBuffer, i); - - if (!entry) - goto ContinueLoop; - - PhLargeIntegerToLocalSystemTime(&systemTime, &entry->Time); - temp = PhFormatDateTime(&systemTime); - PhAppendStringBuilder(&stringBuilder, &temp->sr); - PhDereferenceObject(temp); - PhAppendStringBuilder2(&stringBuilder, L": "); - - temp = PhFormatLogEntry(entry); - PhAppendStringBuilder(&stringBuilder, &temp->sr); - PhDereferenceObject(temp); - PhAppendStringBuilder2(&stringBuilder, L"\r\n"); - -ContinueLoop: - - if (i == 0) - break; - - i--; - } - - return PhFinalStringBuilderString(&stringBuilder); -} - -INT_PTR CALLBACK PhpLogDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(ListViewHandle, FALSE, TRUE); - PhSetControlTheme(ListViewHandle, L"explorer"); - PhAddListViewColumn(ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Time"); - PhAddListViewColumn(ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 260, L"Message"); - PhLoadListViewColumnsFromSetting(L"LogListViewColumns", ListViewHandle); - - PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_AUTOSCROLL), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_CLEAR), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = 290; - MinimumSize.bottom = 150; - MapDialogRect(hwndDlg, &MinimumSize); - - PhLoadWindowPlacementFromSetting(L"LogWindowPosition", L"LogWindowSize", hwndDlg); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOSCROLL), BST_CHECKED); - - PhRegisterCallback(&PhLoggedCallback, LoggedCallback, NULL, &LoggedRegistration); - PhpUpdateLogList(); - ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE); - } - break; - case WM_DESTROY: - { - PhSaveListViewColumnsToSetting(L"LogListViewColumns", ListViewHandle); - PhSaveWindowPlacementToSetting(L"LogWindowPosition", L"LogWindowSize", hwndDlg); - - PhDeleteLayoutManager(&WindowLayoutManager); - - PhUnregisterCallback(&PhLoggedCallback, &LoggedRegistration); - PhUnregisterDialog(PhLogWindowHandle); - PhLogWindowHandle = NULL; - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - DestroyWindow(hwndDlg); - break; - case IDC_CLEAR: - { - PhClearLogEntries(); - PhpUpdateLogList(); - } - break; - case IDC_COPY: - { - PPH_STRING string; - ULONG selectedCount; - - selectedCount = ListView_GetSelectedCount(ListViewHandle); - - if (selectedCount == 0) - { - // User didn't select anything, so copy all items. - string = PhpGetStringForSelectedLogEntries(TRUE); - PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED); - } - else - { - string = PhpGetStringForSelectedLogEntries(FALSE); - } - - PhSetClipboardString(hwndDlg, &string->sr); - PhDereferenceObject(string); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)ListViewHandle, TRUE); - } - break; - case IDC_SAVE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Text files (*.txt)", L"*.txt" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - - fileDialog = PhCreateSaveFileDialog(); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, L"Process Hacker Log.txt"); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - NTSTATUS status; - PPH_STRING fileName; - PPH_FILE_STREAM fileStream; - PPH_STRING string; - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - - if (NT_SUCCESS(status = PhCreateFileStream( - &fileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - 0 - ))) - { - PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); - PhWritePhTextHeader(fileStream); - - string = PhpGetStringForSelectedLogEntries(TRUE); - PhWriteStringAsUtf8FileStreamEx(fileStream, string->Buffer, string->Length); - PhDereferenceObject(string); - - PhDereferenceObject(fileStream); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case LVN_GETDISPINFO: - { - NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header; - PPH_LOG_ENTRY entry; - - entry = PhGetItemCircularBuffer_PVOID(&PhLogBuffer, ListViewCount - dispInfo->item.iItem - 1); - - if (dispInfo->item.iSubItem == 0) - { - if (dispInfo->item.mask & LVIF_TEXT) - { - SYSTEMTIME systemTime; - PPH_STRING dateTime; - - PhLargeIntegerToLocalSystemTime(&systemTime, &entry->Time); - dateTime = PhFormatDateTime(&systemTime); - wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, dateTime->Buffer, _TRUNCATE); - PhDereferenceObject(dateTime); - } - } - else if (dispInfo->item.iSubItem == 1) - { - if (dispInfo->item.mask & LVIF_TEXT) - { - PPH_STRING string; - - string = PhFormatLogEntry(entry); - wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, string->Buffer, _TRUNCATE); - PhDereferenceObject(string); - } - } - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&WindowLayoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - case WM_PH_LOG_UPDATED: - { - PhpUpdateLogList(); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * log window + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include + +#define WM_PH_LOG_UPDATED (WM_APP + 300) + +INT_PTR CALLBACK PhpLogDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +HWND PhLogWindowHandle = NULL; +static PH_LAYOUT_MANAGER WindowLayoutManager; +static RECT MinimumSize; +static HWND ListViewHandle; +static ULONG ListViewCount; +static PH_CALLBACK_REGISTRATION LoggedRegistration; + +VOID PhShowLogDialog( + VOID + ) +{ + if (!PhLogWindowHandle) + { + PhLogWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_LOG), + NULL, + PhpLogDlgProc + ); + PhRegisterDialog(PhLogWindowHandle); + ShowWindow(PhLogWindowHandle, SW_SHOW); + } + + if (IsMinimized(PhLogWindowHandle)) + ShowWindow(PhLogWindowHandle, SW_RESTORE); + else + SetForegroundWindow(PhLogWindowHandle); +} + +static VOID NTAPI LoggedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PostMessage(PhLogWindowHandle, WM_PH_LOG_UPDATED, 0, 0); +} + +static VOID PhpUpdateLogList( + VOID + ) +{ + ListViewCount = PhLogBuffer.Count; + ListView_SetItemCountEx(ListViewHandle, ListViewCount, LVSICF_NOSCROLL); + + if (ListViewCount >= 2 && Button_GetCheck(GetDlgItem(PhLogWindowHandle, IDC_AUTOSCROLL)) == BST_CHECKED) + { + if (ListView_IsItemVisible(ListViewHandle, ListViewCount - 2)) + { + ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE); + } + } +} + +static PPH_STRING PhpGetStringForSelectedLogEntries( + _In_ BOOLEAN All + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i; + + if (ListViewCount == 0) + return PhReferenceEmptyString(); + + PhInitializeStringBuilder(&stringBuilder, 0x100); + + i = ListViewCount - 1; + + while (TRUE) + { + PPH_LOG_ENTRY entry; + SYSTEMTIME systemTime; + PPH_STRING temp; + + if (!All) + { + // The list view displays the items in reverse order... + if (!(ListView_GetItemState(ListViewHandle, ListViewCount - i - 1, LVIS_SELECTED) & LVIS_SELECTED)) + { + goto ContinueLoop; + } + } + + entry = PhGetItemCircularBuffer_PVOID(&PhLogBuffer, i); + + if (!entry) + goto ContinueLoop; + + PhLargeIntegerToLocalSystemTime(&systemTime, &entry->Time); + temp = PhFormatDateTime(&systemTime); + PhAppendStringBuilder(&stringBuilder, &temp->sr); + PhDereferenceObject(temp); + PhAppendStringBuilder2(&stringBuilder, L": "); + + temp = PhFormatLogEntry(entry); + PhAppendStringBuilder(&stringBuilder, &temp->sr); + PhDereferenceObject(temp); + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + +ContinueLoop: + + if (i == 0) + break; + + i--; + } + + return PhFinalStringBuilderString(&stringBuilder); +} + +INT_PTR CALLBACK PhpLogDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(ListViewHandle, FALSE, TRUE); + PhSetControlTheme(ListViewHandle, L"explorer"); + PhAddListViewColumn(ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Time"); + PhAddListViewColumn(ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 260, L"Message"); + PhLoadListViewColumnsFromSetting(L"LogListViewColumns", ListViewHandle); + + PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_AUTOSCROLL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_CLEAR), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + + MinimumSize.left = 0; + MinimumSize.top = 0; + MinimumSize.right = 290; + MinimumSize.bottom = 150; + MapDialogRect(hwndDlg, &MinimumSize); + + if (PhGetIntegerPairSetting(L"LogWindowPosition").X) + PhLoadWindowPlacementFromSetting(L"LogWindowPosition", L"LogWindowSize", hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOSCROLL), BST_CHECKED); + + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), LoggedCallback, NULL, &LoggedRegistration); + PhpUpdateLogList(); + ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"LogListViewColumns", ListViewHandle); + PhSaveWindowPlacementToSetting(L"LogWindowPosition", L"LogWindowSize", hwndDlg); + + PhDeleteLayoutManager(&WindowLayoutManager); + + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackLoggedEvent), &LoggedRegistration); + PhUnregisterDialog(PhLogWindowHandle); + PhLogWindowHandle = NULL; + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + case IDC_CLEAR: + { + PhClearLogEntries(); + PhpUpdateLogList(); + } + break; + case IDC_COPY: + { + PPH_STRING string; + ULONG selectedCount; + + selectedCount = ListView_GetSelectedCount(ListViewHandle); + + if (selectedCount == 0) + { + // User didn't select anything, so copy all items. + string = PhpGetStringForSelectedLogEntries(TRUE); + PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED); + } + else + { + string = PhpGetStringForSelectedLogEntries(FALSE); + } + + PhSetClipboardString(hwndDlg, &string->sr); + PhDereferenceObject(string); + + PhSetDialogFocus(hwndDlg, ListViewHandle); + } + break; + case IDC_SAVE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Text files (*.txt)", L"*.txt" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + + fileDialog = PhCreateSaveFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, L"Process Hacker Log.txt"); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + NTSTATUS status; + PPH_STRING fileName; + PPH_FILE_STREAM fileStream; + PPH_STRING string; + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + + if (NT_SUCCESS(status = PhCreateFileStream( + &fileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + 0 + ))) + { + PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); + PhWritePhTextHeader(fileStream); + + string = PhpGetStringForSelectedLogEntries(TRUE); + PhWriteStringAsUtf8FileStreamEx(fileStream, string->Buffer, string->Length); + PhDereferenceObject(string); + + PhDereferenceObject(fileStream); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case LVN_GETDISPINFO: + { + NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header; + PPH_LOG_ENTRY entry; + + entry = PhGetItemCircularBuffer_PVOID(&PhLogBuffer, ListViewCount - dispInfo->item.iItem - 1); + + if (dispInfo->item.iSubItem == 0) + { + if (dispInfo->item.mask & LVIF_TEXT) + { + SYSTEMTIME systemTime; + PPH_STRING dateTime; + + PhLargeIntegerToLocalSystemTime(&systemTime, &entry->Time); + dateTime = PhFormatDateTime(&systemTime); + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, dateTime->Buffer, _TRUNCATE); + PhDereferenceObject(dateTime); + } + } + else if (dispInfo->item.iSubItem == 1) + { + if (dispInfo->item.mask & LVIF_TEXT) + { + PPH_STRING string; + + string = PhFormatLogEntry(entry); + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, string->Buffer, _TRUNCATE); + PhDereferenceObject(string); + } + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&WindowLayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + case WM_PH_LOG_UPDATED: + { + PhpUpdateLogList(); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/main.c b/ProcessHacker/main.c index ce5c652bb60d..bad6f146f4ec 100644 --- a/ProcessHacker/main.c +++ b/ProcessHacker/main.c @@ -1,1072 +1,1490 @@ -/* - * Process Hacker - - * main program - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LONG PhMainMessageLoop( - VOID - ); - -VOID PhActivatePreviousInstance( - VOID - ); - -VOID PhInitializeCommonControls( - VOID - ); - -VOID PhInitializeKph( - VOID - ); - -BOOLEAN PhInitializeAppSystem( - VOID - ); - -VOID PhpInitializeSettings( - VOID - ); - -VOID PhpProcessStartupParameters( - VOID - ); - -VOID PhpEnablePrivileges( - VOID - ); - -PPH_STRING PhApplicationDirectory; -PPH_STRING PhApplicationFileName; -PHAPPAPI HFONT PhApplicationFont; -PPH_STRING PhCurrentUserName = NULL; -HINSTANCE PhInstanceHandle; -PPH_STRING PhLocalSystemName = NULL; -BOOLEAN PhPluginsEnabled = FALSE; -PPH_STRING PhSettingsFileName = NULL; -PH_INTEGER_PAIR PhSmallIconSize = { 16, 16 }; -PH_INTEGER_PAIR PhLargeIconSize = { 32, 32 }; -PH_STARTUP_PARAMETERS PhStartupParameters; - -PH_PROVIDER_THREAD PhPrimaryProviderThread; -PH_PROVIDER_THREAD PhSecondaryProviderThread; - -static PPH_LIST DialogList = NULL; -static PPH_LIST FilterList = NULL; -static PH_AUTO_POOL BaseAutoPool; - -INT WINAPI wWinMain( - _In_ HINSTANCE hInstance, - _In_opt_ HINSTANCE hPrevInstance, - _In_ PWSTR lpCmdLine, - _In_ INT nCmdShow - ) -{ - LONG result; -#ifdef DEBUG - PHP_BASE_THREAD_DBG dbg; -#endif - HANDLE currentTokenHandle; - - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); -#ifndef DEBUG - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); -#endif - - PhInstanceHandle = (HINSTANCE)NtCurrentPeb()->ImageBaseAddress; - - if (!NT_SUCCESS(PhInitializePhLib())) - return 1; - if (!PhInitializeAppSystem()) - return 1; - - PhInitializeCommonControls(); - - currentTokenHandle = PhGetOwnTokenAttributes().TokenHandle; - - if (currentTokenHandle) - { - PTOKEN_USER tokenUser; - - if (NT_SUCCESS(PhGetTokenUser(currentTokenHandle, &tokenUser))) - { - PhCurrentUserName = PhGetSidFullName(tokenUser->User.Sid, TRUE, NULL); - PhFree(tokenUser); - } - } - - PhLocalSystemName = PhGetSidFullName(&PhSeLocalSystemSid, TRUE, NULL); - - // There has been a report of the above call failing. - if (!PhLocalSystemName) - PhLocalSystemName = PhCreateString(L"NT AUTHORITY\\SYSTEM"); - - PhApplicationFileName = PhGetApplicationFileName(); - PhApplicationDirectory = PhGetApplicationDirectory(); - - // Just in case - if (!PhApplicationFileName) - PhApplicationFileName = PhCreateString(L"ProcessHacker.exe"); - if (!PhApplicationDirectory) - PhApplicationDirectory = PhReferenceEmptyString(); - - PhpProcessStartupParameters(); - PhSettingsInitialization(); - PhpEnablePrivileges(); - - if (PhStartupParameters.RunAsServiceMode) - { - RtlExitUserProcess(PhRunAsServiceStart(PhStartupParameters.RunAsServiceMode)); - } - - PhpInitializeSettings(); - - // Activate a previous instance if required. - if (PhGetIntegerSetting(L"AllowOnlyOneInstance") && - !PhStartupParameters.NewInstance && - !PhStartupParameters.ShowOptions && - !PhStartupParameters.CommandMode && - !PhStartupParameters.PhSvc) - { - PhActivatePreviousInstance(); - } - - if (PhGetIntegerSetting(L"EnableKph") && !PhStartupParameters.NoKph && !PhStartupParameters.CommandMode && !PhIsExecutingInWow64()) - PhInitializeKph(); - - if (PhStartupParameters.CommandMode && PhStartupParameters.CommandType && PhStartupParameters.CommandAction) - { - NTSTATUS status; - - status = PhCommandModeStart(); - - if (!NT_SUCCESS(status) && !PhStartupParameters.Silent) - { - PhShowStatus(NULL, L"Unable to execute the command", status, 0); - } - - RtlExitUserProcess(status); - } - -#ifdef DEBUG - dbg.ClientId = NtCurrentTeb()->ClientId; - dbg.StartAddress = wWinMain; - dbg.Parameter = NULL; - InsertTailList(&PhDbgThreadListHead, &dbg.ListEntry); - TlsSetValue(PhDbgThreadDbgTlsIndex, &dbg); -#endif - - PhInitializeAutoPool(&BaseAutoPool); - - PhEmInitialization(); - PhGuiSupportInitialization(); - PhTreeNewInitialization(); - PhGraphControlInitialization(); - PhHexEditInitialization(); - PhColorBoxInitialization(); - - PhSmallIconSize.X = GetSystemMetrics(SM_CXSMICON); - PhSmallIconSize.Y = GetSystemMetrics(SM_CYSMICON); - PhLargeIconSize.X = GetSystemMetrics(SM_CXICON); - PhLargeIconSize.Y = GetSystemMetrics(SM_CYICON); - - if (PhStartupParameters.ShowOptions) - { - // Elevated options dialog for changing the value of Replace Task Manager with Process Hacker. - PhShowOptionsDialog(PhStartupParameters.WindowHandle); - RtlExitUserProcess(STATUS_SUCCESS); - } - -#ifndef DEBUG - if (PhIsExecutingInWow64() && !PhStartupParameters.PhSvc) - { - PhShowWarning( - NULL, - L"You are attempting to run the 32-bit version of Process Hacker on 64-bit Windows. " - L"Most features will not work correctly.\n\n" - L"Please run the 64-bit version of Process Hacker instead." - ); - } -#endif - - PhPluginsEnabled = PhGetIntegerSetting(L"EnablePlugins") && !PhStartupParameters.NoPlugins; - - if (PhPluginsEnabled) - { - PhPluginsInitialization(); - PhLoadPlugins(); - } - - if (PhStartupParameters.PhSvc) - { - MSG message; - - // Turn the feedback cursor off. - PostMessage(NULL, WM_NULL, 0, 0); - GetMessage(&message, NULL, 0, 0); - - RtlExitUserProcess(PhSvcMain(NULL, NULL, NULL)); - } - - // Create a mutant for the installer. - { - HANDLE mutantHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - - RtlInitUnicodeString(&mutantName, L"\\BaseNamedObjects\\ProcessHackerMutant"); - InitializeObjectAttributes( - &oa, - &mutantName, - 0, - NULL, - NULL - ); - - NtCreateMutant(&mutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); - } - - // Set priority. - { - PROCESS_PRIORITY_CLASS priorityClass; - - priorityClass.Foreground = FALSE; - priorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; - - if (PhStartupParameters.PriorityClass != 0) - priorityClass.PriorityClass = (UCHAR)PhStartupParameters.PriorityClass; - - NtSetInformationProcess(NtCurrentProcess(), ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); - } - - if (!PhMainWndInitialization(nCmdShow)) - { - PhShowError(NULL, L"Unable to initialize the main window."); - return 1; - } - - PhDrainAutoPool(&BaseAutoPool); - - result = PhMainMessageLoop(); - RtlExitUserProcess(result); -} - -LONG PhMainMessageLoop( - VOID - ) -{ - BOOL result; - MSG message; - HACCEL acceleratorTable; - - acceleratorTable = LoadAccelerators(PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND_ACCEL)); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - BOOLEAN processed = FALSE; - ULONG i; - - if (result == -1) - return 1; - - if (FilterList) - { - for (i = 0; i < FilterList->Count; i++) - { - PPH_MESSAGE_LOOP_FILTER_ENTRY entry = FilterList->Items[i]; - - if (entry->Filter(&message, entry->Context)) - { - processed = TRUE; - break; - } - } - } - - if (!processed) - { - if ( - message.hwnd == PhMainWndHandle || - IsChild(PhMainWndHandle, message.hwnd) - ) - { - if (TranslateAccelerator(PhMainWndHandle, acceleratorTable, &message)) - processed = TRUE; - } - - if (DialogList) - { - for (i = 0; i < DialogList->Count; i++) - { - if (IsDialogMessage((HWND)DialogList->Items[i], &message)) - { - processed = TRUE; - break; - } - } - } - } - - if (!processed) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&BaseAutoPool); - } - - return (LONG)message.wParam; -} - -VOID PhRegisterDialog( - _In_ HWND DialogWindowHandle - ) -{ - if (!DialogList) - DialogList = PhCreateList(2); - - PhAddItemList(DialogList, (PVOID)DialogWindowHandle); -} - -VOID PhUnregisterDialog( - _In_ HWND DialogWindowHandle - ) -{ - ULONG indexOfDialog; - - if (!DialogList) - return; - - indexOfDialog = PhFindItemList(DialogList, (PVOID)DialogWindowHandle); - - if (indexOfDialog != -1) - PhRemoveItemList(DialogList, indexOfDialog); -} - -struct _PH_MESSAGE_LOOP_FILTER_ENTRY *PhRegisterMessageLoopFilter( - _In_ PPH_MESSAGE_LOOP_FILTER Filter, - _In_opt_ PVOID Context - ) -{ - PPH_MESSAGE_LOOP_FILTER_ENTRY entry; - - if (!FilterList) - FilterList = PhCreateList(2); - - entry = PhAllocate(sizeof(PH_MESSAGE_LOOP_FILTER_ENTRY)); - entry->Filter = Filter; - entry->Context = Context; - PhAddItemList(FilterList, entry); - - return entry; -} - -VOID PhUnregisterMessageLoopFilter( - _In_ struct _PH_MESSAGE_LOOP_FILTER_ENTRY *FilterEntry - ) -{ - ULONG indexOfFilter; - - if (!FilterList) - return; - - indexOfFilter = PhFindItemList(FilterList, FilterEntry); - - if (indexOfFilter != -1) - PhRemoveItemList(FilterList, indexOfFilter); - - PhFree(FilterEntry); -} - -VOID PhActivatePreviousInstance( - VOID - ) -{ - HWND hwnd; - - hwnd = FindWindow(PH_MAINWND_CLASSNAME, NULL); - - if (hwnd) - { - ULONG_PTR result; - - SendMessageTimeout(hwnd, WM_PH_ACTIVATE, PhStartupParameters.SelectPid, 0, SMTO_BLOCK, 5000, &result); - - if (result == PH_ACTIVATE_REPLY) - { - SetForegroundWindow(hwnd); - RtlExitUserProcess(STATUS_SUCCESS); - } - } -} - -VOID PhInitializeCommonControls( - VOID - ) -{ - INITCOMMONCONTROLSEX icex; - - icex.dwSize = sizeof(INITCOMMONCONTROLSEX); - icex.dwICC = - ICC_LINK_CLASS | - ICC_LISTVIEW_CLASSES | - ICC_PROGRESS_CLASS | - ICC_TAB_CLASSES - ; - - InitCommonControlsEx(&icex); -} - -HFONT PhpCreateFont( - _In_ PWSTR Name, - _In_ ULONG Size, - _In_ ULONG Weight - ) -{ - return CreateFont( - -(LONG)PhMultiplyDivide(Size, PhGlobalDpi, 72), - 0, - 0, - 0, - Weight, - FALSE, - FALSE, - FALSE, - ANSI_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - DEFAULT_PITCH, - Name - ); -} - -VOID PhInitializeFont( - _In_ HWND hWnd - ) -{ - NONCLIENTMETRICS metrics = { sizeof(metrics) }; - BOOLEAN success; - HDC hdc; - - if (hdc = GetDC(hWnd)) - { - PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(hWnd, hdc); - } - else - { - PhGlobalDpi = 96; - } - - success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0); - - if ( - !(PhApplicationFont = PhpCreateFont(L"Microsoft Sans Serif", 8, FW_NORMAL)) && - !(PhApplicationFont = PhpCreateFont(L"Tahoma", 8, FW_NORMAL)) - ) - { - if (success) - PhApplicationFont = CreateFontIndirect(&metrics.lfMessageFont); - else - PhApplicationFont = NULL; - } -} - -PUCHAR PhpReadSignature( - _In_ PWSTR FileName, - _Out_ PULONG SignatureSize - ) -{ - NTSTATUS status; - HANDLE fileHandle; - PUCHAR signature; - ULONG bufferSize; - IO_STATUS_BLOCK iosb; - - if (!NT_SUCCESS(PhCreateFileWin32(&fileHandle, FileName, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT))) - { - return NULL; - } - - bufferSize = 1024; - signature = PhAllocate(bufferSize); - - status = NtReadFile(fileHandle, NULL, NULL, NULL, &iosb, signature, bufferSize, NULL, NULL); - NtClose(fileHandle); - - if (NT_SUCCESS(status)) - { - *SignatureSize = (ULONG)iosb.Information; - return signature; - } - else - { - PhFree(signature); - return NULL; - } -} - -VOID PhInitializeKph( - VOID - ) -{ - static PH_STRINGREF kprocesshacker = PH_STRINGREF_INIT(L"kprocesshacker.sys"); - static PH_STRINGREF processhackerSig = PH_STRINGREF_INIT(L"ProcessHacker.sig"); - - PPH_STRING kprocesshackerFileName; - PPH_STRING processhackerSigFileName; - KPH_PARAMETERS parameters; - PUCHAR signature; - ULONG signatureSize; - - if (WindowsVersion < WINDOWS_7) - return; - - kprocesshackerFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &kprocesshacker); - processhackerSigFileName = PhConcatStringRef2(&PhApplicationDirectory->sr, &processhackerSig); - - parameters.SecurityLevel = KphSecurityPrivilegeCheck; - parameters.CreateDynamicConfiguration = TRUE; - KphConnect2Ex(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters); - - if (signature = PhpReadSignature(processhackerSigFileName->Buffer, &signatureSize)) - { - KphVerifyClient(signature, signatureSize); - PhFree(signature); - } - - PhDereferenceObject(kprocesshackerFileName); - PhDereferenceObject(processhackerSigFileName); -} - -BOOLEAN PhInitializeAppSystem( - VOID - ) -{ - PhApplicationName = L"Process Hacker"; - - if (!PhProcessProviderInitialization()) - return FALSE; - if (!PhServiceProviderInitialization()) - return FALSE; - if (!PhNetworkProviderInitialization()) - return FALSE; - if (!PhModuleProviderInitialization()) - return FALSE; - if (!PhThreadProviderInitialization()) - return FALSE; - if (!PhHandleProviderInitialization()) - return FALSE; - if (!PhMemoryProviderInitialization()) - return FALSE; - if (!PhProcessPropInitialization()) - return FALSE; - - PhSetHandleClientIdFunction(PhGetClientIdName); - - return TRUE; -} - -VOID PhpInitializeSettings( - VOID - ) -{ - NTSTATUS status; - - if (!PhStartupParameters.NoSettings) - { - static PH_STRINGREF settingsSuffix = PH_STRINGREF_INIT(L".settings.xml"); - PPH_STRING settingsFileName; - - // There are three possible locations for the settings file: - // 1. The file name given in the command line. - // 2. A file named ProcessHacker.exe.settings.xml in the program directory. (This changes - // based on the executable file name.) - // 3. The default location. - - // 1. File specified in command line - if (PhStartupParameters.SettingsFileName) - { - // Get an absolute path now. - PhSettingsFileName = PhGetFullPath(PhStartupParameters.SettingsFileName->Buffer, NULL); - } - - // 2. File in program directory - if (!PhSettingsFileName) - { - settingsFileName = PhConcatStringRef2(&PhApplicationFileName->sr, &settingsSuffix); - - if (RtlDoesFileExists_U(settingsFileName->Buffer)) - { - PhSettingsFileName = settingsFileName; - } - else - { - PhDereferenceObject(settingsFileName); - } - } - - // 3. Default location - if (!PhSettingsFileName) - { - PhSettingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker\\settings.xml"); - } - - if (PhSettingsFileName) - { - status = PhLoadSettings(PhSettingsFileName->Buffer); - - // If we didn't find the file, it will be created. Otherwise, - // there was probably a parsing error and we don't want to - // change anything. - if (status == STATUS_FILE_CORRUPT_ERROR) - { - if (PhShowMessage( - NULL, - MB_ICONWARNING | MB_YESNO, - L"Process Hacker's settings file is corrupt. Do you want to reset it?\n" - L"If you select No, the settings system will not function properly." - ) == IDYES) - { - HANDLE fileHandle; - IO_STATUS_BLOCK isb; - CHAR data[] = ""; - - // This used to delete the file. But it's better to keep the file there - // and overwrite it with some valid XML, especially with case (2) above. - if (NT_SUCCESS(PhCreateFileWin32( - &fileHandle, - PhSettingsFileName->Buffer, - FILE_GENERIC_WRITE, - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE, - FILE_OVERWRITE, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - NtWriteFile(fileHandle, NULL, NULL, NULL, &isb, data, sizeof(data) - 1, NULL, NULL); - NtClose(fileHandle); - } - } - else - { - // Pretend we don't have a settings store so bad things - // don't happen. - PhDereferenceObject(PhSettingsFileName); - PhSettingsFileName = NULL; - } - } - } - } - - // Apply basic global settings. - PhMaxSizeUnit = PhGetIntegerSetting(L"MaxSizeUnit"); - - if (PhGetIntegerSetting(L"SampleCountAutomatic")) - { - ULONG sampleCount; - - sampleCount = (GetSystemMetrics(SM_CXVIRTUALSCREEN) + 1) / 2; - - if (sampleCount > 2048) - sampleCount = 2048; - - PhSetIntegerSetting(L"SampleCount", sampleCount); - } -} - -#define PH_ARG_SETTINGS 1 -#define PH_ARG_NOSETTINGS 2 -#define PH_ARG_SHOWVISIBLE 3 -#define PH_ARG_SHOWHIDDEN 4 -#define PH_ARG_COMMANDMODE 5 -#define PH_ARG_COMMANDTYPE 6 -#define PH_ARG_COMMANDOBJECT 7 -#define PH_ARG_COMMANDACTION 8 -#define PH_ARG_COMMANDVALUE 9 -#define PH_ARG_RUNASSERVICEMODE 10 -#define PH_ARG_NOKPH 11 -#define PH_ARG_INSTALLKPH 12 -#define PH_ARG_UNINSTALLKPH 13 -#define PH_ARG_DEBUG 14 -#define PH_ARG_HWND 15 -#define PH_ARG_POINT 16 -#define PH_ARG_SHOWOPTIONS 17 -#define PH_ARG_PHSVC 18 -#define PH_ARG_NOPLUGINS 19 -#define PH_ARG_NEWINSTANCE 20 -#define PH_ARG_ELEVATE 21 -#define PH_ARG_SILENT 22 -#define PH_ARG_HELP 23 -#define PH_ARG_SELECTPID 24 -#define PH_ARG_PRIORITY 25 -#define PH_ARG_PLUGIN 26 -#define PH_ARG_SELECTTAB 27 -#define PH_ARG_SYSINFO 28 - -BOOLEAN NTAPI PhpCommandLineOptionCallback( - _In_opt_ PPH_COMMAND_LINE_OPTION Option, - _In_opt_ PPH_STRING Value, - _In_opt_ PVOID Context - ) -{ - ULONG64 integer; - - if (Option) - { - switch (Option->Id) - { - case PH_ARG_SETTINGS: - PhSwapReference(&PhStartupParameters.SettingsFileName, Value); - break; - case PH_ARG_NOSETTINGS: - PhStartupParameters.NoSettings = TRUE; - break; - case PH_ARG_SHOWVISIBLE: - PhStartupParameters.ShowVisible = TRUE; - break; - case PH_ARG_SHOWHIDDEN: - PhStartupParameters.ShowHidden = TRUE; - break; - case PH_ARG_COMMANDMODE: - PhStartupParameters.CommandMode = TRUE; - break; - case PH_ARG_COMMANDTYPE: - PhSwapReference(&PhStartupParameters.CommandType, Value); - break; - case PH_ARG_COMMANDOBJECT: - PhSwapReference(&PhStartupParameters.CommandObject, Value); - break; - case PH_ARG_COMMANDACTION: - PhSwapReference(&PhStartupParameters.CommandAction, Value); - break; - case PH_ARG_COMMANDVALUE: - PhSwapReference(&PhStartupParameters.CommandValue, Value); - break; - case PH_ARG_RUNASSERVICEMODE: - PhSwapReference(&PhStartupParameters.RunAsServiceMode, Value); - break; - case PH_ARG_NOKPH: - PhStartupParameters.NoKph = TRUE; - break; - case PH_ARG_INSTALLKPH: - PhStartupParameters.InstallKph = TRUE; - break; - case PH_ARG_UNINSTALLKPH: - PhStartupParameters.UninstallKph = TRUE; - break; - case PH_ARG_DEBUG: - PhStartupParameters.Debug = TRUE; - break; - case PH_ARG_HWND: - if (PhStringToInteger64(&Value->sr, 16, &integer)) - PhStartupParameters.WindowHandle = (HWND)(ULONG_PTR)integer; - break; - case PH_ARG_POINT: - { - PH_STRINGREF xString; - PH_STRINGREF yString; - - if (PhSplitStringRefAtChar(&Value->sr, ',', &xString, &yString)) - { - LONG64 x; - LONG64 y; - - if (PhStringToInteger64(&xString, 10, &x) && PhStringToInteger64(&yString, 10, &y)) - { - PhStartupParameters.Point.x = (LONG)x; - PhStartupParameters.Point.y = (LONG)y; - } - } - } - break; - case PH_ARG_SHOWOPTIONS: - PhStartupParameters.ShowOptions = TRUE; - break; - case PH_ARG_PHSVC: - PhStartupParameters.PhSvc = TRUE; - break; - case PH_ARG_NOPLUGINS: - PhStartupParameters.NoPlugins = TRUE; - break; - case PH_ARG_NEWINSTANCE: - PhStartupParameters.NewInstance = TRUE; - break; - case PH_ARG_ELEVATE: - PhStartupParameters.Elevate = TRUE; - break; - case PH_ARG_SILENT: - PhStartupParameters.Silent = TRUE; - break; - case PH_ARG_HELP: - PhStartupParameters.Help = TRUE; - break; - case PH_ARG_SELECTPID: - if (PhStringToInteger64(&Value->sr, 0, &integer)) - PhStartupParameters.SelectPid = (ULONG)integer; - break; - case PH_ARG_PRIORITY: - if (PhEqualString2(Value, L"r", TRUE)) - PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME; - else if (PhEqualString2(Value, L"h", TRUE)) - PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; - else if (PhEqualString2(Value, L"n", TRUE)) - PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; - else if (PhEqualString2(Value, L"l", TRUE)) - PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; - break; - case PH_ARG_PLUGIN: - if (!PhStartupParameters.PluginParameters) - PhStartupParameters.PluginParameters = PhCreateList(3); - PhAddItemList(PhStartupParameters.PluginParameters, PhReferenceObject(Value)); - break; - case PH_ARG_SELECTTAB: - PhSwapReference(&PhStartupParameters.SelectTab, Value); - break; - case PH_ARG_SYSINFO: - PhSwapReference(&PhStartupParameters.SysInfo, Value ? Value : PhReferenceEmptyString()); - break; - } - } - else - { - PPH_STRING upperValue; - - upperValue = PhDuplicateString(Value); - _wcsupr(upperValue->Buffer); - - if (PhFindStringInString(upperValue, 0, L"TASKMGR.EXE") != -1) - { - // User probably has Process Hacker replacing Task Manager. Force - // the main window to start visible. - PhStartupParameters.ShowVisible = TRUE; - } - - PhDereferenceObject(upperValue); - } - - return TRUE; -} - -VOID PhpProcessStartupParameters( - VOID - ) -{ - static PH_COMMAND_LINE_OPTION options[] = - { - { PH_ARG_SETTINGS, L"settings", MandatoryArgumentType }, - { PH_ARG_NOSETTINGS, L"nosettings", NoArgumentType }, - { PH_ARG_SHOWVISIBLE, L"v", NoArgumentType }, - { PH_ARG_SHOWHIDDEN, L"hide", NoArgumentType }, - { PH_ARG_COMMANDMODE, L"c", NoArgumentType }, - { PH_ARG_COMMANDTYPE, L"ctype", MandatoryArgumentType }, - { PH_ARG_COMMANDOBJECT, L"cobject", MandatoryArgumentType }, - { PH_ARG_COMMANDACTION, L"caction", MandatoryArgumentType }, - { PH_ARG_COMMANDVALUE, L"cvalue", MandatoryArgumentType }, - { PH_ARG_RUNASSERVICEMODE, L"ras", MandatoryArgumentType }, - { PH_ARG_NOKPH, L"nokph", NoArgumentType }, - { PH_ARG_INSTALLKPH, L"installkph", NoArgumentType }, - { PH_ARG_UNINSTALLKPH, L"uninstallkph", NoArgumentType }, - { PH_ARG_DEBUG, L"debug", NoArgumentType }, - { PH_ARG_HWND, L"hwnd", MandatoryArgumentType }, - { PH_ARG_POINT, L"point", MandatoryArgumentType }, - { PH_ARG_SHOWOPTIONS, L"showoptions", NoArgumentType }, - { PH_ARG_PHSVC, L"phsvc", NoArgumentType }, - { PH_ARG_NOPLUGINS, L"noplugins", NoArgumentType }, - { PH_ARG_NEWINSTANCE, L"newinstance", NoArgumentType }, - { PH_ARG_ELEVATE, L"elevate", NoArgumentType }, - { PH_ARG_SILENT, L"s", NoArgumentType }, - { PH_ARG_HELP, L"help", NoArgumentType }, - { PH_ARG_SELECTPID, L"selectpid", MandatoryArgumentType }, - { PH_ARG_PRIORITY, L"priority", MandatoryArgumentType }, - { PH_ARG_PLUGIN, L"plugin", MandatoryArgumentType }, - { PH_ARG_SELECTTAB, L"selecttab", MandatoryArgumentType }, - { PH_ARG_SYSINFO, L"sysinfo", OptionalArgumentType } - }; - PH_STRINGREF commandLine; - - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - - memset(&PhStartupParameters, 0, sizeof(PH_STARTUP_PARAMETERS)); - - if (!PhParseCommandLine( - &commandLine, - options, - sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), - PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS | PH_COMMAND_LINE_IGNORE_FIRST_PART, - PhpCommandLineOptionCallback, - NULL - ) || PhStartupParameters.Help) - { - PhShowInformation( - NULL, - L"Command line options:\n\n" - L"-c\n" - L"-ctype command-type\n" - L"-cobject command-object\n" - L"-caction command-action\n" - L"-cvalue command-value\n" - L"-debug\n" - L"-elevate\n" - L"-help\n" - L"-hide\n" - L"-installkph\n" - L"-newinstance\n" - L"-nokph\n" - L"-noplugins\n" - L"-nosettings\n" - L"-plugin pluginname:value\n" - L"-priority r|h|n|l\n" - L"-s\n" - L"-selectpid pid-to-select\n" - L"-selecttab name-of-tab-to-select\n" - L"-settings filename\n" - L"-sysinfo [section-name]\n" - L"-uninstallkph\n" - L"-v\n" - ); - - if (PhStartupParameters.Help) - RtlExitUserProcess(STATUS_SUCCESS); - } - - if (PhStartupParameters.InstallKph) - { - NTSTATUS status; - PPH_STRING kprocesshackerFileName; - KPH_PARAMETERS parameters; - - kprocesshackerFileName = PhConcatStrings2(PhApplicationDirectory->Buffer, L"\\kprocesshacker.sys"); - - parameters.SecurityLevel = KphSecuritySignatureCheck; - parameters.CreateDynamicConfiguration = TRUE; - - status = KphInstallEx(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters); - - if (!NT_SUCCESS(status) && !PhStartupParameters.Silent) - PhShowStatus(NULL, L"Unable to install KProcessHacker", status, 0); - - RtlExitUserProcess(status); - } - - if (PhStartupParameters.UninstallKph) - { - NTSTATUS status; - - status = KphUninstall(KPH_DEVICE_SHORT_NAME); - - if (!NT_SUCCESS(status) && !PhStartupParameters.Silent) - PhShowStatus(NULL, L"Unable to uninstall KProcessHacker", status, 0); - - RtlExitUserProcess(status); - } - - if (PhStartupParameters.Elevate && !PhGetOwnTokenAttributes().Elevated) - { - PhShellProcessHacker( - NULL, - NULL, - SW_SHOW, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_FORCE_SETTINGS, - 0, - NULL - ); - RtlExitUserProcess(STATUS_SUCCESS); - } - - if (PhStartupParameters.Debug) - { - // The symbol provider won't work if this is chosen. - PhShowDebugConsole(); - } -} - -VOID PhpEnablePrivileges( - VOID - ) -{ - HANDLE tokenHandle; - - if (NT_SUCCESS(NtOpenProcessToken( - NtCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES, - &tokenHandle - ))) - { - CHAR privilegesBuffer[FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES) * 8]; - PTOKEN_PRIVILEGES privileges; - ULONG i; - - privileges = (PTOKEN_PRIVILEGES)privilegesBuffer; - privileges->PrivilegeCount = 8; - - for (i = 0; i < privileges->PrivilegeCount; i++) - { - privileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED; - privileges->Privileges[i].Luid.HighPart = 0; - } - - privileges->Privileges[0].Luid.LowPart = SE_DEBUG_PRIVILEGE; - privileges->Privileges[1].Luid.LowPart = SE_INC_BASE_PRIORITY_PRIVILEGE; - privileges->Privileges[2].Luid.LowPart = SE_INC_WORKING_SET_PRIVILEGE; - privileges->Privileges[3].Luid.LowPart = SE_LOAD_DRIVER_PRIVILEGE; - privileges->Privileges[4].Luid.LowPart = SE_PROF_SINGLE_PROCESS_PRIVILEGE; - privileges->Privileges[5].Luid.LowPart = SE_RESTORE_PRIVILEGE; - privileges->Privileges[6].Luid.LowPart = SE_SHUTDOWN_PRIVILEGE; - privileges->Privileges[7].Luid.LowPart = SE_TAKE_OWNERSHIP_PRIVILEGE; - - NtAdjustPrivilegesToken( - tokenHandle, - FALSE, - privileges, - 0, - NULL, - NULL - ); - - NtClose(tokenHandle); - } -} +/* + * Process Hacker - + * main program + * + * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LONG PhMainMessageLoop( + VOID + ); + +VOID PhActivatePreviousInstance( + VOID + ); + +VOID PhInitializeCommonControls( + VOID + ); + +VOID PhInitializeKph( + VOID + ); + +BOOLEAN PhInitializeAppSystem( + VOID + ); + +VOID PhpInitializeSettings( + VOID + ); + +VOID PhpProcessStartupParameters( + VOID + ); + +VOID PhpEnablePrivileges( + VOID + ); + +BOOLEAN PhInitializeExceptionPolicy( + VOID + ); + +BOOLEAN PhInitializeNamespacePolicy( + VOID + ); + +BOOLEAN PhInitializeMitigationPolicy( + VOID + ); + +BOOLEAN PhPluginsEnabled = FALSE; +PPH_STRING PhSettingsFileName = NULL; +PH_STARTUP_PARAMETERS PhStartupParameters; + +PH_PROVIDER_THREAD PhPrimaryProviderThread; +PH_PROVIDER_THREAD PhSecondaryProviderThread; + +static PPH_LIST DialogList = NULL; +static PPH_LIST FilterList = NULL; +static PH_AUTO_POOL BaseAutoPool; + +INT WINAPI wWinMain( + _In_ HINSTANCE Instance, + _In_opt_ HINSTANCE PrevInstance, + _In_ PWSTR CmdLine, + _In_ INT CmdShow + ) +{ + LONG result; +#ifdef DEBUG + PHP_BASE_THREAD_DBG dbg; +#endif + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + + if (!NT_SUCCESS(PhInitializePhLibEx(L"Process Hacker", ULONG_MAX, Instance, 0, 0))) + return 1; + if (!PhInitializeExceptionPolicy()) + return 1; + if (!PhInitializeNamespacePolicy()) + return 1; + if (!PhInitializeMitigationPolicy()) + return 1; + //if (!PhInitializeRestartPolicy()) + // return 1; + + PhpProcessStartupParameters(); + PhpEnablePrivileges(); + + if (PhStartupParameters.RunAsServiceMode) + { + RtlExitUserProcess(PhRunAsServiceStart(PhStartupParameters.RunAsServiceMode)); + } + + if (PhStartupParameters.CommandMode && + PhStartupParameters.CommandType && + PhStartupParameters.CommandAction) + { + RtlExitUserProcess(PhCommandModeStart()); + } + + PhSettingsInitialization(); + PhpInitializeSettings(); + + if (PhGetIntegerSetting(L"AllowOnlyOneInstance") && + !PhStartupParameters.NewInstance && + !PhStartupParameters.ShowOptions && + !PhStartupParameters.CommandMode && + !PhStartupParameters.PhSvc) + { + PhActivatePreviousInstance(); + } + + if (PhGetIntegerSetting(L"EnableKph") && + !PhStartupParameters.NoKph && + !PhStartupParameters.CommandMode && + !PhIsExecutingInWow64() + ) + { + PhInitializeKph(); + } + +#ifdef DEBUG + dbg.ClientId = NtCurrentTeb()->ClientId; + dbg.StartAddress = wWinMain; + dbg.Parameter = NULL; + InsertTailList(&PhDbgThreadListHead, &dbg.ListEntry); + TlsSetValue(PhDbgThreadDbgTlsIndex, &dbg); +#endif + + PhInitializeAutoPool(&BaseAutoPool); + + PhInitializeAppSystem(); + PhInitializeCallbacks(); + PhInitializeCommonControls(); + + PhEmInitialization(); + PhGuiSupportInitialization(); + PhTreeNewInitialization(); + PhGraphControlInitialization(); + PhHexEditInitialization(); + PhColorBoxInitialization(); + + if (PhStartupParameters.ShowOptions) + { + PhShowOptionsDialog(PhStartupParameters.WindowHandle); + RtlExitUserProcess(STATUS_SUCCESS); + } + + if (PhPluginsEnabled && !PhStartupParameters.NoPlugins) + { + PhLoadPlugins(); + } + +#ifndef DEBUG + if (WindowsVersion >= WINDOWS_10) + { + PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; + + // Note: The PhInitializeMitigationPolicy function enables the other mitigation policies. + // However, we can only enable the ProcessSignaturePolicy after loading plugins. + policyInfo.Policy = ProcessSignaturePolicy; + policyInfo.SignaturePolicy.Flags = 0; + policyInfo.SignaturePolicy.MicrosoftSignedOnly = TRUE; + + NtSetInformationProcess(NtCurrentProcess(), ProcessMitigationPolicy, &policyInfo, sizeof(PROCESS_MITIGATION_POLICY_INFORMATION)); + } +#endif + + if (PhStartupParameters.PhSvc) + { + MSG message; + + // Turn the feedback cursor off. + PostMessage(NULL, WM_NULL, 0, 0); + GetMessage(&message, NULL, 0, 0); + + RtlExitUserProcess(PhSvcMain(NULL, NULL)); + } + +#ifndef DEBUG + if (PhIsExecutingInWow64()) + { + PhShowWarning( + NULL, + L"You are attempting to run the 32-bit version of Process Hacker on 64-bit Windows. " + L"Most features will not work correctly.\n\n" + L"Please run the 64-bit version of Process Hacker instead." + ); + RtlExitUserProcess(STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT); + } +#endif + + // Set the default priority. + { + PROCESS_PRIORITY_CLASS priorityClass; + + priorityClass.Foreground = FALSE; + priorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; + + if (PhStartupParameters.PriorityClass != 0) + priorityClass.PriorityClass = (UCHAR)PhStartupParameters.PriorityClass; + + PhSetProcessPriority(NtCurrentProcess(), priorityClass); + } + + if (!PhMainWndInitialization(CmdShow)) + { + PhShowError(NULL, L"Unable to initialize the main window."); + return 1; + } + + PhDrainAutoPool(&BaseAutoPool); + + result = PhMainMessageLoop(); + RtlExitUserProcess(result); +} + +LONG PhMainMessageLoop( + VOID + ) +{ + BOOL result; + MSG message; + HACCEL acceleratorTable; + + acceleratorTable = LoadAccelerators(PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND_ACCEL)); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + BOOLEAN processed = FALSE; + ULONG i; + + if (result == -1) + return 1; + + if (FilterList) + { + for (i = 0; i < FilterList->Count; i++) + { + PPH_MESSAGE_LOOP_FILTER_ENTRY entry = FilterList->Items[i]; + + if (entry->Filter(&message, entry->Context)) + { + processed = TRUE; + break; + } + } + } + + if (!processed) + { + if ( + message.hwnd == PhMainWndHandle || + IsChild(PhMainWndHandle, message.hwnd) + ) + { + if (TranslateAccelerator(PhMainWndHandle, acceleratorTable, &message)) + processed = TRUE; + } + + if (DialogList) + { + for (i = 0; i < DialogList->Count; i++) + { + if (IsDialogMessage((HWND)DialogList->Items[i], &message)) + { + processed = TRUE; + break; + } + } + } + } + + if (!processed) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&BaseAutoPool); + } + + return (LONG)message.wParam; +} + +VOID PhRegisterDialog( + _In_ HWND DialogWindowHandle + ) +{ + if (!DialogList) + DialogList = PhCreateList(2); + + PhAddItemList(DialogList, (PVOID)DialogWindowHandle); +} + +VOID PhUnregisterDialog( + _In_ HWND DialogWindowHandle + ) +{ + ULONG indexOfDialog; + + if (!DialogList) + return; + + indexOfDialog = PhFindItemList(DialogList, (PVOID)DialogWindowHandle); + + if (indexOfDialog != -1) + PhRemoveItemList(DialogList, indexOfDialog); +} + +PPH_MESSAGE_LOOP_FILTER_ENTRY PhRegisterMessageLoopFilter( + _In_ PPH_MESSAGE_LOOP_FILTER Filter, + _In_opt_ PVOID Context + ) +{ + PPH_MESSAGE_LOOP_FILTER_ENTRY entry; + + if (!FilterList) + FilterList = PhCreateList(2); + + entry = PhAllocate(sizeof(PH_MESSAGE_LOOP_FILTER_ENTRY)); + entry->Filter = Filter; + entry->Context = Context; + PhAddItemList(FilterList, entry); + + return entry; +} + +VOID PhUnregisterMessageLoopFilter( + _In_ PPH_MESSAGE_LOOP_FILTER_ENTRY FilterEntry + ) +{ + ULONG indexOfFilter; + + if (!FilterList) + return; + + indexOfFilter = PhFindItemList(FilterList, FilterEntry); + + if (indexOfFilter != -1) + PhRemoveItemList(FilterList, indexOfFilter); + + PhFree(FilterEntry); +} + +static BOOLEAN NTAPI PhpPreviousInstancesCallback( + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF TypeName, + _In_opt_ PVOID Context + ) +{ + static PH_STRINGREF objectNameSr = PH_STRINGREF_INIT(L"PhMutant_"); + HANDLE objectHandle; + UNICODE_STRING objectNameUs; + OBJECT_ATTRIBUTES objectAttributes; + MUTANT_OWNER_INFORMATION objectInfo; + + if (!PhStartsWithStringRef(Name, &objectNameSr, FALSE)) + return TRUE; + if (!PhStringRefToUnicodeString(Name, &objectNameUs)) + return TRUE; + + InitializeObjectAttributes( + &objectAttributes, + &objectNameUs, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), + NULL + ); + + if (!NT_SUCCESS(NtOpenMutant( + &objectHandle, + MUTANT_QUERY_STATE, + &objectAttributes + ))) + { + return TRUE; + } + + if (NT_SUCCESS(PhGetMutantOwnerInformation( + objectHandle, + &objectInfo + ))) + { + HWND hwnd; + HANDLE processHandle = NULL; + HANDLE tokenHandle = NULL; + PTOKEN_USER tokenUser = NULL; + ULONG attempts = 50; + + if (objectInfo.ClientId.UniqueProcess == NtCurrentProcessId()) + goto CleanupExit; + if (!NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, objectInfo.ClientId.UniqueProcess))) + goto CleanupExit; + if (!NT_SUCCESS(PhOpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle))) + goto CleanupExit; + if (!NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) + goto CleanupExit; + if (!RtlEqualSid(tokenUser->User.Sid, PhGetOwnTokenAttributes().TokenSid)) + goto CleanupExit; + + // Try to locate the window a few times because some users reported that it might not yet have been created. (dmex) + do + { + if (hwnd = PhGetProcessMainWindowEx( + objectInfo.ClientId.UniqueProcess, + processHandle, + FALSE + )) + { + break; + } + + PhDelayExecution(100); + } while (--attempts != 0); + + if (hwnd) + { + ULONG_PTR result; + + SendMessageTimeout(hwnd, WM_PH_ACTIVATE, PhStartupParameters.SelectPid, 0, SMTO_BLOCK, 5000, &result); + + if (result == PH_ACTIVATE_REPLY) + { + SetForegroundWindow(hwnd); + RtlExitUserProcess(STATUS_SUCCESS); + } + } + + CleanupExit: + if (tokenUser) PhFree(tokenUser); + if (tokenHandle) NtClose(tokenHandle); + if (processHandle) NtClose(processHandle); + } + + NtClose(objectHandle); + return TRUE; +} + +VOID PhActivatePreviousInstance( + VOID + ) +{ + PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); +} + +VOID PhInitializeCommonControls( + VOID + ) +{ + INITCOMMONCONTROLSEX icex; + + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = + ICC_LISTVIEW_CLASSES | + ICC_TREEVIEW_CLASSES | + ICC_BAR_CLASSES | + ICC_TAB_CLASSES | + ICC_PROGRESS_CLASS | + ICC_COOL_CLASSES | + ICC_STANDARD_CLASSES | + ICC_LINK_CLASS + ; + + InitCommonControlsEx(&icex); +} + +VOID PhInitializeFont( + VOID + ) +{ + NONCLIENTMETRICS metrics = { sizeof(metrics) }; + + if (PhApplicationFont) + DeleteObject(PhApplicationFont); + + if ( + !(PhApplicationFont = PhCreateFont(L"Microsoft Sans Serif", 8, FW_NORMAL)) && + !(PhApplicationFont = PhCreateFont(L"Tahoma", 8, FW_NORMAL)) + ) + { + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + PhApplicationFont = CreateFontIndirect(&metrics.lfMessageFont); + else + PhApplicationFont = NULL; + } +} + +BOOLEAN PhInitializeRestartPolicy( + VOID + ) +{ +#ifndef DEBUG + PH_STRINGREF commandLineSr; + PH_STRINGREF fileNameSr; + PH_STRINGREF argumentsSr; + PPH_STRING argumentsString = NULL; + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLineSr); + + if (!PhParseCommandLineFuzzy(&commandLineSr, &fileNameSr, &argumentsSr, NULL)) + return FALSE; + + if (argumentsSr.Length) + { + static PH_STRINGREF commandlinePart = PH_STRINGREF_INIT(L"-nomp"); + + if (PhEndsWithStringRef(&argumentsSr, &commandlinePart, FALSE)) + PhTrimStringRef(&argumentsSr, &commandlinePart, PH_TRIM_END_ONLY); + + argumentsString = PhCreateString2(&argumentsSr); + } + + // MSDN: Do not include the file name in the command line. + RegisterApplicationRestart(PhGetString(argumentsString), 0); + + if (argumentsString) + PhDereferenceObject(argumentsString); +#endif + return TRUE; +} + +#ifndef DEBUG +#include +#include + +static ULONG CALLBACK PhpUnhandledExceptionCallback( + _In_ PEXCEPTION_POINTERS ExceptionInfo + ) +{ + PPH_STRING errorMessage; + INT result; + PPH_STRING message; + TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; + TASKDIALOG_BUTTON buttons[2]; + + if (NT_NTWIN32(ExceptionInfo->ExceptionRecord->ExceptionCode)) + errorMessage = PhGetStatusMessage(0, WIN32_FROM_NTSTATUS(ExceptionInfo->ExceptionRecord->ExceptionCode)); + else + errorMessage = PhGetStatusMessage(ExceptionInfo->ExceptionRecord->ExceptionCode, 0); + + message = PhFormatString( + L"Error code: 0x%08X (%s)", + ExceptionInfo->ExceptionRecord->ExceptionCode, + PhGetStringOrEmpty(errorMessage) + ); + + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; + config.dwCommonButtons = TDCBF_CLOSE_BUTTON; + config.pszWindowTitle = PhApplicationName; + config.pszMainIcon = TD_ERROR_ICON; + config.pszMainInstruction = L"Process Hacker has crashed :("; + config.pszContent = message->Buffer; + + buttons[0].nButtonID = IDYES; + buttons[0].pszButtonText = L"Minidump"; + buttons[1].nButtonID = IDRETRY; + buttons[1].pszButtonText = L"Restart"; + + config.cButtons = RTL_NUMBER_OF(buttons); + config.pButtons = buttons; + config.nDefaultButton = IDCLOSE; + + if (TaskDialogIndirect( + &config, + &result, + NULL, + NULL + ) == S_OK) + { + switch (result) + { + case IDRETRY: + { + PhShellProcessHacker( + NULL, + NULL, + SW_SHOW, + 0, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, + 0, + NULL + ); + } + break; + case IDYES: + { + static PH_STRINGREF dumpFilePath = PH_STRINGREF_INIT(L"%USERPROFILE%\\Desktop\\"); + HANDLE fileHandle; + PPH_STRING dumpDirectory; + PPH_STRING dumpFileName; + WCHAR alphastring[16] = L""; + + dumpDirectory = PhExpandEnvironmentStrings(&dumpFilePath); + PhGenerateRandomAlphaString(alphastring, RTL_NUMBER_OF(alphastring)); + + dumpFileName = PhConcatStrings( + 4, + PhGetString(dumpDirectory), + L"\\ProcessHacker_", + alphastring, + L"_DumpFile.dmp" + ); + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + dumpFileName->Buffer, + FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + MINIDUMP_EXCEPTION_INFORMATION exceptionInfo; + + exceptionInfo.ThreadId = HandleToUlong(NtCurrentThreadId()); + exceptionInfo.ExceptionPointers = ExceptionInfo; + exceptionInfo.ClientPointers = FALSE; + + PhWriteMiniDumpProcess( + NtCurrentProcess(), + NtCurrentProcessId(), + fileHandle, + MiniDumpNormal, + &exceptionInfo, + NULL, + NULL + ); + + NtClose(fileHandle); + } + + PhDereferenceObject(dumpFileName); + PhDereferenceObject(dumpDirectory); + } + break; + } + } + + RtlExitUserProcess(ExceptionInfo->ExceptionRecord->ExceptionCode); + + PhDereferenceObject(message); + PhDereferenceObject(errorMessage); + + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + +BOOLEAN PhInitializeExceptionPolicy( + VOID + ) +{ +#ifndef DEBUG + ULONG errorMode; + + if (NT_SUCCESS(PhGetProcessErrorMode(NtCurrentProcess(), &errorMode))) + { + errorMode &= ~(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + PhSetProcessErrorMode(NtCurrentProcess(), errorMode); + } + + // NOTE: We really shouldn't be using this function since it can be + // preempted by the Win32 SetUnhandledExceptionFilter function. (dmex) + RtlSetUnhandledExceptionFilter(PhpUnhandledExceptionCallback); +#endif + + return TRUE; +} + +BOOLEAN PhInitializeNamespacePolicy( + VOID + ) +{ + HANDLE mutantHandle; + WCHAR objectName[PH_INT64_STR_LEN_1]; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING objectNameUs; + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"PhMutant_"); + PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + + if (!PhFormatToBuffer( + format, + RTL_NUMBER_OF(format), + objectName, + sizeof(objectName), + NULL + )) + { + return FALSE; + } + + RtlInitUnicodeString(&objectNameUs, objectName); + InitializeObjectAttributes( + &objectAttributes, + &objectNameUs, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), + NULL + ); + + if (NT_SUCCESS(NtCreateMutant( + &mutantHandle, + MUTANT_QUERY_STATE, + &objectAttributes, + TRUE + ))) + { + return TRUE; + } + + return FALSE; +} + +BOOLEAN PhInitializeMitigationPolicy( + VOID + ) +{ +#ifndef DEBUG +#define DEFAULT_MITIGATION_POLICY_FLAGS \ + (PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON | \ + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON) + + static PH_STRINGREF nompCommandlinePart = PH_STRINGREF_INIT(L" -nomp"); + static PH_STRINGREF rasCommandlinePart = PH_STRINGREF_INIT(L" -ras"); + BOOLEAN success = TRUE; + PH_STRINGREF commandlineSr; + PPH_STRING commandline = NULL; + PS_SYSTEM_DLL_INIT_BLOCK (*LdrSystemDllInitBlock_I) = NULL; + STARTUPINFOEX startupInfo = { sizeof(STARTUPINFOEX) }; + SIZE_T attributeListLength; + + if (WindowsVersion < WINDOWS_10_RS3) + return TRUE; + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandlineSr); + + // NOTE: The SCM has a bug where calling CreateProcess with PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY to restart the service with mitigations + // causes the SCM to spew EVENT_SERVICE_DIFFERENT_PID_CONNECTED in the system eventlog and terminate the service. (dmex) + // WARN: This bug makes it impossible to start services with mitigation polices when the service doesn't have an IFEO key... + if (PhFindStringInStringRef(&commandlineSr, &rasCommandlinePart, FALSE) != -1) + return TRUE; + if (PhEndsWithStringRef(&commandlineSr, &nompCommandlinePart, FALSE)) + return TRUE; + + if (!(LdrSystemDllInitBlock_I = PhGetDllProcedureAddress(L"ntdll.dll", "LdrSystemDllInitBlock", 0))) + goto CleanupExit; + + if (!RTL_CONTAINS_FIELD(LdrSystemDllInitBlock_I, LdrSystemDllInitBlock_I->Size, MitigationOptionsMap)) + goto CleanupExit; + + if ((LdrSystemDllInitBlock_I->MitigationOptionsMap.Map[0] & DEFAULT_MITIGATION_POLICY_FLAGS) == DEFAULT_MITIGATION_POLICY_FLAGS) + goto CleanupExit; + + if (!InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto CleanupExit; + + startupInfo.lpAttributeList = PhAllocate(attributeListLength); + + if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, &attributeListLength)) + goto CleanupExit; + + if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &(ULONG64){ DEFAULT_MITIGATION_POLICY_FLAGS }, sizeof(ULONG64), NULL, NULL)) + goto CleanupExit; + + commandline = PhConcatStringRef2(&commandlineSr, &nompCommandlinePart); + + if (NT_SUCCESS(PhCreateProcessWin32Ex( + NULL, + PhGetString(commandline), + NULL, + NULL, + &startupInfo.StartupInfo, + PH_CREATE_PROCESS_EXTENDED_STARTUPINFO | PH_CREATE_PROCESS_BREAKAWAY_FROM_JOB, + NULL, + NULL, + NULL, + NULL + ))) + { + success = FALSE; + } + +CleanupExit: + + if (commandline) + PhDereferenceObject(commandline); + + if (startupInfo.lpAttributeList) + { + DeleteProcThreadAttributeList(startupInfo.lpAttributeList); + PhFree(startupInfo.lpAttributeList); + } + + return success; +#else + return TRUE; +#endif +} + +NTSTATUS PhpReadSignature( + _In_ PWSTR FileName, + _Out_ PUCHAR *Signature, + _Out_ PULONG SignatureSize + ) +{ + NTSTATUS status; + HANDLE fileHandle; + PUCHAR signature; + ULONG bufferSize; + IO_STATUS_BLOCK iosb; + + if (!NT_SUCCESS(status = PhCreateFileWin32(&fileHandle, FileName, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT))) + { + return status; + } + + bufferSize = 1024; + signature = PhAllocate(bufferSize); + + status = NtReadFile(fileHandle, NULL, NULL, NULL, &iosb, signature, bufferSize, NULL, NULL); + NtClose(fileHandle); + + if (NT_SUCCESS(status)) + { + *Signature = signature; + *SignatureSize = (ULONG)iosb.Information; + return status; + } + else + { + PhFree(signature); + return status; + } +} + +VOID PhpShowKphError( + _In_ PWSTR Message, + _In_opt_ NTSTATUS Status + ) +{ + if (Status == STATUS_NO_SUCH_FILE) + { + PhShowError2( + NULL, + Message, + L"You will be unable to use more advanced features, view details about system processes or terminate malicious software." + ); + } + else + { + PPH_STRING errorMessage; + PPH_STRING statusMessage; + + if (errorMessage = PhGetStatusMessage(Status, 0)) + { + statusMessage = PhConcatStrings( + 3, + errorMessage->Buffer, + L"\r\n\r\n", + L"You will be unable to use more advanced features, view details about system processes or terminate malicious software." + ); + PhShowError2(NULL, Message, statusMessage->Buffer); + PhDereferenceObject(statusMessage); + PhDereferenceObject(errorMessage); + } + else + { + PhShowError2( + NULL, + Message, + L"You will be unable to use more advanced features, view details about system processes or terminate malicious software." + ); + } + } +} + +VOID PhInitializeKph( + VOID + ) +{ + NTSTATUS status; + PPH_STRING applicationDirectory; + PPH_STRING kprocesshackerFileName; + PPH_STRING processhackerSigFileName; + KPH_PARAMETERS parameters; + + if (!(applicationDirectory = PhGetApplicationDirectory())) + return; + + kprocesshackerFileName = PhConcatStringRefZ(&applicationDirectory->sr, L"kprocesshacker.sys"); + processhackerSigFileName = PhConcatStringRefZ(&applicationDirectory->sr, L"ProcessHacker.sig"); + PhDereferenceObject(applicationDirectory); + + if (!RtlDoesFileExists_U(kprocesshackerFileName->Buffer)) + { + if (PhGetIntegerSetting(L"EnableKphWarnings") && !PhStartupParameters.PhSvc) + PhpShowKphError(L"The Process Hacker kernel driver 'kprocesshacker.sys' was not found in the application directory.", STATUS_NO_SUCH_FILE); + return; + } + + parameters.SecurityLevel = KphSecuritySignatureAndPrivilegeCheck; + parameters.CreateDynamicConfiguration = TRUE; + + if (NT_SUCCESS(status = KphConnect2Ex( + KPH_DEVICE_SHORT_NAME, + kprocesshackerFileName->Buffer, + ¶meters + ))) + { + PUCHAR signature; + ULONG signatureSize; + + status = PhpReadSignature( + processhackerSigFileName->Buffer, + &signature, + &signatureSize + ); + + if (NT_SUCCESS(status)) + { + status = KphVerifyClient(signature, signatureSize); + + if (!NT_SUCCESS(status)) + { + if (PhGetIntegerSetting(L"EnableKphWarnings") && !PhStartupParameters.PhSvc) + PhpShowKphError(L"Unable to verify the kernel driver signature.", status); + } + + PhFree(signature); + } + else + { + if (PhGetIntegerSetting(L"EnableKphWarnings") && !PhStartupParameters.PhSvc) + PhpShowKphError(L"Unable to load the kernel driver signature.", status); + } + } + else + { + if (PhGetIntegerSetting(L"EnableKphWarnings") && PhGetOwnTokenAttributes().Elevated && !PhStartupParameters.PhSvc) + PhpShowKphError(L"Unable to load the kernel driver.", status); + } + + PhDereferenceObject(kprocesshackerFileName); + PhDereferenceObject(processhackerSigFileName); +} + +BOOLEAN PhInitializeAppSystem( + VOID + ) +{ + if (!PhProcessProviderInitialization()) + return FALSE; + if (!PhServiceProviderInitialization()) + return FALSE; + if (!PhNetworkProviderInitialization()) + return FALSE; + + PhSetHandleClientIdFunction(PhGetClientIdName); + + return TRUE; +} + +VOID PhpInitializeSettings( + VOID + ) +{ + NTSTATUS status; + + if (!PhStartupParameters.NoSettings) + { + static PH_STRINGREF settingsPath = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker\\settings.xml"); + static PH_STRINGREF settingsSuffix = PH_STRINGREF_INIT(L".settings.xml"); + PPH_STRING settingsFileName; + + // There are three possible locations for the settings file: + // 1. The file name given in the command line. + // 2. A file named ProcessHacker.exe.settings.xml in the program directory. (This changes + // based on the executable file name.) + // 3. The default location. + + // 1. File specified in command line + if (PhStartupParameters.SettingsFileName) + { + // Get an absolute path now. + PhSettingsFileName = PhGetFullPath(PhStartupParameters.SettingsFileName->Buffer, NULL); + } + + // 2. File in program directory + if (!PhSettingsFileName) + { + PPH_STRING applicationFileName; + + if (applicationFileName = PhGetApplicationFileName()) + { + settingsFileName = PhConcatStringRef2(&applicationFileName->sr, &settingsSuffix); + + if (RtlDoesFileExists_U(settingsFileName->Buffer)) + { + PhSettingsFileName = settingsFileName; + } + else + { + PhDereferenceObject(settingsFileName); + } + + PhDereferenceObject(applicationFileName); + } + } + + // 3. Default location + if (!PhSettingsFileName) + { + PhSettingsFileName = PhExpandEnvironmentStrings(&settingsPath); + } + + if (PhSettingsFileName) + { + status = PhLoadSettings(PhSettingsFileName->Buffer); + + // If we didn't find the file, it will be created. Otherwise, + // there was probably a parsing error and we don't want to + // change anything. + if (status == STATUS_FILE_CORRUPT_ERROR) + { + if (PhShowMessage2( + NULL, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_WARNING_ICON, + L"Process Hacker's settings file is corrupt. Do you want to reset it?", + L"If you select No, the settings system will not function properly." + ) == IDYES) + { + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + CHAR data[] = ""; + + // This used to delete the file. But it's better to keep the file there + // and overwrite it with some valid XML, especially with case (2) above. + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhSettingsFileName->Buffer, + FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OVERWRITE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + NtWriteFile(fileHandle, NULL, NULL, NULL, &isb, data, sizeof(data) - 1, NULL, NULL); + NtClose(fileHandle); + } + } + else + { + // Pretend we don't have a settings store so bad things + // don't happen. + PhDereferenceObject(PhSettingsFileName); + PhSettingsFileName = NULL; + } + } + } + } + + // Apply basic global settings. + PhPluginsEnabled = !!PhGetIntegerSetting(L"EnablePlugins"); + PhMaxSizeUnit = PhGetIntegerSetting(L"MaxSizeUnit"); + + if (PhGetIntegerSetting(L"SampleCountAutomatic")) + { + ULONG sampleCount; + + sampleCount = (GetSystemMetrics(SM_CXVIRTUALSCREEN) + 1) / 2; + + if (sampleCount > 2048) + sampleCount = 2048; + + PhSetIntegerSetting(L"SampleCount", sampleCount); + } +} + +#define PH_ARG_SETTINGS 1 +#define PH_ARG_NOSETTINGS 2 +#define PH_ARG_SHOWVISIBLE 3 +#define PH_ARG_SHOWHIDDEN 4 +#define PH_ARG_COMMANDMODE 5 +#define PH_ARG_COMMANDTYPE 6 +#define PH_ARG_COMMANDOBJECT 7 +#define PH_ARG_COMMANDACTION 8 +#define PH_ARG_COMMANDVALUE 9 +#define PH_ARG_RUNASSERVICEMODE 10 +#define PH_ARG_NOKPH 11 +#define PH_ARG_INSTALLKPH 12 +#define PH_ARG_UNINSTALLKPH 13 +#define PH_ARG_DEBUG 14 +#define PH_ARG_HWND 15 +#define PH_ARG_POINT 16 +#define PH_ARG_SHOWOPTIONS 17 +#define PH_ARG_PHSVC 18 +#define PH_ARG_NOPLUGINS 19 +#define PH_ARG_NEWINSTANCE 20 +#define PH_ARG_ELEVATE 21 +#define PH_ARG_SILENT 22 +#define PH_ARG_HELP 23 +#define PH_ARG_SELECTPID 24 +#define PH_ARG_PRIORITY 25 +#define PH_ARG_PLUGIN 26 +#define PH_ARG_SELECTTAB 27 +#define PH_ARG_SYSINFO 28 + +BOOLEAN NTAPI PhpCommandLineOptionCallback( + _In_opt_ PPH_COMMAND_LINE_OPTION Option, + _In_opt_ PPH_STRING Value, + _In_opt_ PVOID Context + ) +{ + ULONG64 integer; + + if (Option) + { + switch (Option->Id) + { + case PH_ARG_SETTINGS: + PhSwapReference(&PhStartupParameters.SettingsFileName, Value); + break; + case PH_ARG_NOSETTINGS: + PhStartupParameters.NoSettings = TRUE; + break; + case PH_ARG_SHOWVISIBLE: + PhStartupParameters.ShowVisible = TRUE; + break; + case PH_ARG_SHOWHIDDEN: + PhStartupParameters.ShowHidden = TRUE; + break; + case PH_ARG_COMMANDMODE: + PhStartupParameters.CommandMode = TRUE; + break; + case PH_ARG_COMMANDTYPE: + PhSwapReference(&PhStartupParameters.CommandType, Value); + break; + case PH_ARG_COMMANDOBJECT: + PhSwapReference(&PhStartupParameters.CommandObject, Value); + break; + case PH_ARG_COMMANDACTION: + PhSwapReference(&PhStartupParameters.CommandAction, Value); + break; + case PH_ARG_COMMANDVALUE: + PhSwapReference(&PhStartupParameters.CommandValue, Value); + break; + case PH_ARG_RUNASSERVICEMODE: + PhSwapReference(&PhStartupParameters.RunAsServiceMode, Value); + break; + case PH_ARG_NOKPH: + PhStartupParameters.NoKph = TRUE; + break; + case PH_ARG_INSTALLKPH: + PhStartupParameters.InstallKph = TRUE; + break; + case PH_ARG_UNINSTALLKPH: + PhStartupParameters.UninstallKph = TRUE; + break; + case PH_ARG_DEBUG: + PhStartupParameters.Debug = TRUE; + break; + case PH_ARG_HWND: + if (PhStringToInteger64(&Value->sr, 16, &integer)) + PhStartupParameters.WindowHandle = (HWND)(ULONG_PTR)integer; + break; + case PH_ARG_POINT: + { + PH_STRINGREF xString; + PH_STRINGREF yString; + + if (PhSplitStringRefAtChar(&Value->sr, ',', &xString, &yString)) + { + LONG64 x; + LONG64 y; + + if (PhStringToInteger64(&xString, 10, &x) && PhStringToInteger64(&yString, 10, &y)) + { + PhStartupParameters.Point.x = (LONG)x; + PhStartupParameters.Point.y = (LONG)y; + } + } + } + break; + case PH_ARG_SHOWOPTIONS: + PhStartupParameters.ShowOptions = TRUE; + break; + case PH_ARG_PHSVC: + PhStartupParameters.PhSvc = TRUE; + break; + case PH_ARG_NOPLUGINS: + PhStartupParameters.NoPlugins = TRUE; + break; + case PH_ARG_NEWINSTANCE: + PhStartupParameters.NewInstance = TRUE; + break; + case PH_ARG_ELEVATE: + PhStartupParameters.Elevate = TRUE; + break; + case PH_ARG_SILENT: + PhStartupParameters.Silent = TRUE; + break; + case PH_ARG_HELP: + PhStartupParameters.Help = TRUE; + break; + case PH_ARG_SELECTPID: + if (PhStringToInteger64(&Value->sr, 0, &integer)) + PhStartupParameters.SelectPid = (ULONG)integer; + break; + case PH_ARG_PRIORITY: + if (PhEqualString2(Value, L"r", TRUE)) + PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME; + else if (PhEqualString2(Value, L"h", TRUE)) + PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; + else if (PhEqualString2(Value, L"n", TRUE)) + PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; + else if (PhEqualString2(Value, L"l", TRUE)) + PhStartupParameters.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; + break; + case PH_ARG_PLUGIN: + if (!PhStartupParameters.PluginParameters) + PhStartupParameters.PluginParameters = PhCreateList(3); + PhAddItemList(PhStartupParameters.PluginParameters, PhReferenceObject(Value)); + break; + case PH_ARG_SELECTTAB: + PhSwapReference(&PhStartupParameters.SelectTab, Value); + break; + case PH_ARG_SYSINFO: + PhSwapReference(&PhStartupParameters.SysInfo, Value ? Value : PhReferenceEmptyString()); + break; + } + } + else + { + PPH_STRING upperValue; + + upperValue = PhDuplicateString(Value); + _wcsupr(upperValue->Buffer); + + if (PhFindStringInString(upperValue, 0, L"TASKMGR.EXE") != -1) + { + // User probably has Process Hacker replacing Task Manager. Force + // the main window to start visible. + PhStartupParameters.ShowVisible = TRUE; + } + + PhDereferenceObject(upperValue); + } + + return TRUE; +} + +VOID PhpProcessStartupParameters( + VOID + ) +{ + static PH_COMMAND_LINE_OPTION options[] = + { + { PH_ARG_SETTINGS, L"settings", MandatoryArgumentType }, + { PH_ARG_NOSETTINGS, L"nosettings", NoArgumentType }, + { PH_ARG_SHOWVISIBLE, L"v", NoArgumentType }, + { PH_ARG_SHOWHIDDEN, L"hide", NoArgumentType }, + { PH_ARG_COMMANDMODE, L"c", NoArgumentType }, + { PH_ARG_COMMANDTYPE, L"ctype", MandatoryArgumentType }, + { PH_ARG_COMMANDOBJECT, L"cobject", MandatoryArgumentType }, + { PH_ARG_COMMANDACTION, L"caction", MandatoryArgumentType }, + { PH_ARG_COMMANDVALUE, L"cvalue", MandatoryArgumentType }, + { PH_ARG_RUNASSERVICEMODE, L"ras", MandatoryArgumentType }, + { PH_ARG_NOKPH, L"nokph", NoArgumentType }, + { PH_ARG_INSTALLKPH, L"installkph", NoArgumentType }, + { PH_ARG_UNINSTALLKPH, L"uninstallkph", NoArgumentType }, + { PH_ARG_DEBUG, L"debug", NoArgumentType }, + { PH_ARG_HWND, L"hwnd", MandatoryArgumentType }, + { PH_ARG_POINT, L"point", MandatoryArgumentType }, + { PH_ARG_SHOWOPTIONS, L"showoptions", NoArgumentType }, + { PH_ARG_PHSVC, L"phsvc", NoArgumentType }, + { PH_ARG_NOPLUGINS, L"noplugins", NoArgumentType }, + { PH_ARG_NEWINSTANCE, L"newinstance", NoArgumentType }, + { PH_ARG_ELEVATE, L"elevate", NoArgumentType }, + { PH_ARG_SILENT, L"s", NoArgumentType }, + { PH_ARG_HELP, L"help", NoArgumentType }, + { PH_ARG_SELECTPID, L"selectpid", MandatoryArgumentType }, + { PH_ARG_PRIORITY, L"priority", MandatoryArgumentType }, + { PH_ARG_PLUGIN, L"plugin", MandatoryArgumentType }, + { PH_ARG_SELECTTAB, L"selecttab", MandatoryArgumentType }, + { PH_ARG_SYSINFO, L"sysinfo", OptionalArgumentType } + }; + PH_STRINGREF commandLine; + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + + memset(&PhStartupParameters, 0, sizeof(PH_STARTUP_PARAMETERS)); + + if (!PhParseCommandLine( + &commandLine, + options, + sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), + PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS | PH_COMMAND_LINE_IGNORE_FIRST_PART, + PhpCommandLineOptionCallback, + NULL + ) || PhStartupParameters.Help) + { + PhShowInformation( + NULL, + L"Command line options:\n\n" + L"-c\n" + L"-ctype command-type\n" + L"-cobject command-object\n" + L"-caction command-action\n" + L"-cvalue command-value\n" + L"-debug\n" + L"-elevate\n" + L"-help\n" + L"-hide\n" + L"-installkph\n" + L"-newinstance\n" + L"-nokph\n" + L"-noplugins\n" + L"-nosettings\n" + L"-plugin pluginname:value\n" + L"-priority r|h|n|l\n" + L"-s\n" + L"-selectpid pid-to-select\n" + L"-selecttab name-of-tab-to-select\n" + L"-settings filename\n" + L"-sysinfo [section-name]\n" + L"-uninstallkph\n" + L"-v\n" + ); + + if (PhStartupParameters.Help) + RtlExitUserProcess(STATUS_SUCCESS); + } + + if (PhStartupParameters.InstallKph) + { + NTSTATUS status; + PPH_STRING applicationDirectory; + PPH_STRING kprocesshackerFileName; + KPH_PARAMETERS parameters; + + if (!(applicationDirectory = PhGetApplicationDirectory())) + return; + + kprocesshackerFileName = PhConcatStringRefZ(&applicationDirectory->sr, L"\\kprocesshacker.sys"); + PhDereferenceObject(applicationDirectory); + + parameters.SecurityLevel = KphSecuritySignatureCheck; + parameters.CreateDynamicConfiguration = TRUE; + + status = KphInstallEx(KPH_DEVICE_SHORT_NAME, kprocesshackerFileName->Buffer, ¶meters); + + if (!NT_SUCCESS(status) && !PhStartupParameters.Silent) + PhShowStatus(NULL, L"Unable to install KProcessHacker", status, 0); + + RtlExitUserProcess(status); + } + + if (PhStartupParameters.UninstallKph) + { + NTSTATUS status; + + status = KphUninstall(KPH_DEVICE_SHORT_NAME); + + if (!NT_SUCCESS(status) && !PhStartupParameters.Silent) + PhShowStatus(NULL, L"Unable to uninstall KProcessHacker", status, 0); + + RtlExitUserProcess(status); + } + + if (PhStartupParameters.Elevate && !PhGetOwnTokenAttributes().Elevated) + { + PhShellProcessHacker( + NULL, + NULL, + SW_SHOW, + PH_SHELL_EXECUTE_ADMIN, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_FORCE_SETTINGS, + 0, + NULL + ); + RtlExitUserProcess(STATUS_SUCCESS); + } + + if (PhStartupParameters.Debug) + { + // The symbol provider won't work if this is chosen. + PhShowDebugConsole(); + } +} + +VOID PhpEnablePrivileges( + VOID + ) +{ + HANDLE tokenHandle; + + if (NT_SUCCESS(PhOpenProcessToken( + NtCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES, + &tokenHandle + ))) + { + CHAR privilegesBuffer[FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES) * 9]; + PTOKEN_PRIVILEGES privileges; + ULONG i; + + privileges = (PTOKEN_PRIVILEGES)privilegesBuffer; + privileges->PrivilegeCount = 9; + + for (i = 0; i < privileges->PrivilegeCount; i++) + { + privileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED; + privileges->Privileges[i].Luid.HighPart = 0; + } + + privileges->Privileges[0].Luid.LowPart = SE_DEBUG_PRIVILEGE; + privileges->Privileges[1].Luid.LowPart = SE_INC_BASE_PRIORITY_PRIVILEGE; + privileges->Privileges[2].Luid.LowPart = SE_INC_WORKING_SET_PRIVILEGE; + privileges->Privileges[3].Luid.LowPart = SE_LOAD_DRIVER_PRIVILEGE; + privileges->Privileges[4].Luid.LowPart = SE_PROF_SINGLE_PROCESS_PRIVILEGE; + privileges->Privileges[5].Luid.LowPart = SE_BACKUP_PRIVILEGE; + privileges->Privileges[6].Luid.LowPart = SE_RESTORE_PRIVILEGE; + privileges->Privileges[7].Luid.LowPart = SE_SHUTDOWN_PRIVILEGE; + privileges->Privileges[8].Luid.LowPart = SE_TAKE_OWNERSHIP_PRIVILEGE; + + NtAdjustPrivilegesToken( + tokenHandle, + FALSE, + privileges, + 0, + NULL, + NULL + ); + + NtClose(tokenHandle); + } +} diff --git a/ProcessHacker/mainwnd.c b/ProcessHacker/mainwnd.c index 518c71b74c18..98cb38f126e7 100644 --- a/ProcessHacker/mainwnd.c +++ b/ProcessHacker/mainwnd.c @@ -1,3641 +1,3230 @@ -/* - * Process Hacker - - * Main window - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define RUNAS_MODE_ADMIN 1 -#define RUNAS_MODE_LIMITED 2 - -PHAPPAPI HWND PhMainWndHandle; -BOOLEAN PhMainWndExiting = FALSE; -HMENU PhMainWndMenuHandle; - -PH_PROVIDER_REGISTRATION PhMwpProcessProviderRegistration; -PH_PROVIDER_REGISTRATION PhMwpServiceProviderRegistration; -PH_PROVIDER_REGISTRATION PhMwpNetworkProviderRegistration; -BOOLEAN PhMwpUpdateAutomatically = TRUE; - -ULONG PhMwpNotifyIconNotifyMask; -ULONG PhMwpLastNotificationType; -PH_MWP_NOTIFICATION_DETAILS PhMwpLastNotificationDetails; - -static BOOLEAN NeedsMaximize = FALSE; -static BOOLEAN AlwaysOnTop = FALSE; - -static BOOLEAN DelayedLoadCompleted = FALSE; - -static PH_CALLBACK_DECLARE(LayoutPaddingCallback); -static RECT LayoutPadding = { 0, 0, 0, 0 }; -static BOOLEAN LayoutPaddingValid = TRUE; - -static HWND TabControlHandle; -static PPH_LIST PageList; -static PPH_MAIN_TAB_PAGE CurrentPage; -static INT OldTabIndex; -static HFONT CurrentCustomFont; - -static HMENU SubMenuHandles[5]; -static PPH_EMENU SubMenuObjects[5]; -static PPH_LIST LegacyAddMenuItemList; -static BOOLEAN UsersMenuInitialized = FALSE; - -static PH_CALLBACK_REGISTRATION SymInitRegistration; - -static ULONG SelectedRunAsMode; -static ULONG SelectedUserSessionId; - -BOOLEAN PhMainWndInitialization( - _In_ INT ShowCommand - ) -{ - PH_STRING_BUILDER stringBuilder; - PH_RECTANGLE windowRectangle; - - if (PhGetIntegerSetting(L"FirstRun")) - { - PPH_STRING autoDbghelpPath; - - // Try to set up the dbghelp path automatically if this is the first run. - if (autoDbghelpPath = PH_AUTO(PhMwpFindDbghelpPath())) - PhSetStringSetting2(L"DbgHelpPath", &autoDbghelpPath->sr); - - PhSetIntegerSetting(L"FirstRun", FALSE); - } - - // This was added to be able to delay-load dbghelp.dll and symsrv.dll. - PhRegisterCallback(&PhSymInitCallback, PhMwpSymInitHandler, NULL, &SymInitRegistration); - - PhMwpInitializeProviders(); - - if (!PhMwpInitializeWindowClass()) - return FALSE; - - windowRectangle.Position = PhGetIntegerPairSetting(L"MainWindowPosition"); - windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MainWindowSize", TRUE).Pair; - - // Create the window title. - - PhInitializeStringBuilder(&stringBuilder, 100); - PhAppendStringBuilder2(&stringBuilder, L"Process Hacker"); - - if (PhCurrentUserName) - { - PhAppendStringBuilder2(&stringBuilder, L" ["); - PhAppendStringBuilder(&stringBuilder, &PhCurrentUserName->sr); - PhAppendCharStringBuilder(&stringBuilder, ']'); - if (KphIsConnected()) PhAppendCharStringBuilder(&stringBuilder, '+'); - } - - if (WINDOWS_HAS_UAC && PhGetOwnTokenAttributes().ElevationType == TokenElevationTypeFull) - PhAppendStringBuilder2(&stringBuilder, L" (Administrator)"); - - // Create the window. - - PhMainWndHandle = CreateWindow( - PH_MAINWND_CLASSNAME, - stringBuilder.String->Buffer, - WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, - windowRectangle.Left, - windowRectangle.Top, - windowRectangle.Width, - windowRectangle.Height, - NULL, - NULL, - PhInstanceHandle, - NULL - ); - PhDeleteStringBuilder(&stringBuilder); - PhMainWndMenuHandle = GetMenu(PhMainWndHandle); - - if (!PhMainWndHandle) - return FALSE; - - PhMwpInitializeMainMenu(PhMainWndMenuHandle); - - // Choose a more appropriate rectangle for the window. - PhAdjustRectangleToWorkingArea(PhMainWndHandle, &windowRectangle); - MoveWindow(PhMainWndHandle, windowRectangle.Left, windowRectangle.Top, - windowRectangle.Width, windowRectangle.Height, FALSE); - - // Allow WM_PH_ACTIVATE to pass through UIPI. - if (WINDOWS_HAS_UAC) - ChangeWindowMessageFilter_I(WM_PH_ACTIVATE, MSGFLT_ADD); - - PhMwpOnSettingChange(); - - // Initialize child controls. - PhMwpInitializeControls(); - - PhMwpLoadSettings(); - PhLogInitialization(); - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhMwpDelayedLoadFunction, NULL); - - PhMwpSelectionChangedTabControl(-1); - - // Perform a layout. - PhMwpOnSize(); - - PhStartProviderThread(&PhPrimaryProviderThread); - PhStartProviderThread(&PhSecondaryProviderThread); - - // See PhMwpOnTimer for more details. - if (PhCsUpdateInterval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1) - SetTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA, PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_1, NULL); - - UpdateWindow(PhMainWndHandle); - - if ((PhStartupParameters.ShowHidden || PhGetIntegerSetting(L"StartHidden")) && PhNfTestIconMask(PH_ICON_ALL)) - ShowCommand = SW_HIDE; - if (PhStartupParameters.ShowVisible) - ShowCommand = SW_SHOW; - - if (PhGetIntegerSetting(L"MainWindowState") == SW_MAXIMIZE) - { - if (ShowCommand != SW_HIDE) - { - ShowCommand = SW_MAXIMIZE; - } - else - { - // We can't maximize it while having it hidden. Set it as pending. - NeedsMaximize = TRUE; - } - } - - if (PhPluginsEnabled) - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMainWindowShowing), IntToPtr(ShowCommand)); - - if (PhStartupParameters.SelectTab) - { - PPH_MAIN_TAB_PAGE page = PhMwpFindPage(&PhStartupParameters.SelectTab->sr); - - if (page) - PhMwpSelectPage(page->Index); - } - - if (PhStartupParameters.SysInfo) - PhShowSystemInformationDialog(PhStartupParameters.SysInfo->Buffer); - - if (ShowCommand != SW_HIDE) - ShowWindow(PhMainWndHandle, ShowCommand); - - if (PhGetIntegerSetting(L"MiniInfoWindowPinned")) - PhPinMiniInformation(MiniInfoManualPinType, 1, 0, PH_MINIINFO_LOAD_POSITION, NULL, NULL); - - return TRUE; -} - -LRESULT CALLBACK PhMwpWndProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_DESTROY: - { - PhMwpOnDestroy(); - } - break; - case WM_ENDSESSION: - { - PhMwpOnEndSession(); - } - break; - case WM_SETTINGCHANGE: - { - PhMwpOnSettingChange(); - } - break; - case WM_COMMAND: - { - PhMwpOnCommand(LOWORD(wParam)); - } - break; - case WM_SHOWWINDOW: - { - PhMwpOnShowWindow(!!wParam, (ULONG)lParam); - } - break; - case WM_SYSCOMMAND: - { - if (PhMwpOnSysCommand((ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))) - return 0; - } - break; - case WM_MENUCOMMAND: - { - PhMwpOnMenuCommand((ULONG)wParam, (HMENU)lParam); - } - break; - case WM_INITMENUPOPUP: - { - PhMwpOnInitMenuPopup((HMENU)wParam, LOWORD(lParam), !!HIWORD(lParam)); - } - break; - case WM_SIZE: - { - PhMwpOnSize(); - } - break; - case WM_SIZING: - { - PhMwpOnSizing((ULONG)wParam, (PRECT)lParam); - } - break; - case WM_SETFOCUS: - { - PhMwpOnSetFocus(); - } - break; - case WM_TIMER: - { - PhMwpOnTimer((ULONG)wParam); - } - break; - case WM_NOTIFY: - { - LRESULT result; - - if (PhMwpOnNotify((NMHDR *)lParam, &result)) - return result; - } - break; - case WM_WTSSESSION_CHANGE: - { - PhMwpOnWtsSessionChange((ULONG)wParam, (ULONG)lParam); - } - break; - } - - if (uMsg >= WM_PH_FIRST && uMsg <= WM_PH_LAST) - { - return PhMwpOnUserMessage(uMsg, wParam, lParam); - } - - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - -BOOLEAN PhMwpInitializeWindowClass( - VOID - ) -{ - WNDCLASSEX wcex; - - memset(&wcex, 0, sizeof(WNDCLASSEX)); - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = 0; - wcex.lpfnWndProc = PhMwpWndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = PhInstanceHandle; - wcex.hIcon = LoadIcon(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - //wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MAINWND); - wcex.lpszClassName = PH_MAINWND_CLASSNAME; - wcex.hIconSm = (HICON)LoadImage(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER), IMAGE_ICON, 16, 16, 0); - - if (!RegisterClassEx(&wcex)) - return FALSE; - - return TRUE; -} - -VOID PhMwpInitializeProviders( - VOID - ) -{ - ULONG interval; - - interval = PhGetIntegerSetting(L"UpdateInterval"); - - if (interval == 0) - { - interval = 1000; - PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, interval); - } - - PhInitializeProviderThread(&PhPrimaryProviderThread, interval); - PhInitializeProviderThread(&PhSecondaryProviderThread, interval); - - PhRegisterProvider(&PhPrimaryProviderThread, PhProcessProviderUpdate, NULL, &PhMwpProcessProviderRegistration); - PhSetEnabledProvider(&PhMwpProcessProviderRegistration, TRUE); - PhRegisterProvider(&PhPrimaryProviderThread, PhServiceProviderUpdate, NULL, &PhMwpServiceProviderRegistration); - PhSetEnabledProvider(&PhMwpServiceProviderRegistration, TRUE); - PhRegisterProvider(&PhPrimaryProviderThread, PhNetworkProviderUpdate, NULL, &PhMwpNetworkProviderRegistration); -} - -VOID PhMwpApplyUpdateInterval( - _In_ ULONG Interval - ) -{ - PhSetIntervalProviderThread(&PhPrimaryProviderThread, Interval); - PhSetIntervalProviderThread(&PhSecondaryProviderThread, Interval); - - if (Interval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) - SetTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA, PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM, NULL); - else - KillTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA); // Might not exist -} - -VOID PhMwpInitializeControls( - VOID - ) -{ - ULONG thinRows; - - TabControlHandle = CreateWindow( - WC_TABCONTROL, - NULL, - WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | TCS_MULTILINE, - 0, - 0, - 3, - 3, - PhMainWndHandle, - NULL, - PhInstanceHandle, - NULL - ); - SendMessage(TabControlHandle, WM_SETFONT, (WPARAM)PhApplicationFont, FALSE); - BringWindowToTop(TabControlHandle); - - thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0; - - PhMwpProcessTreeNewHandle = CreateWindow( - PH_TREENEW_CLASSNAME, - NULL, - WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | TN_STYLE_ANIMATE_DIVIDER | thinRows, - 0, - 0, - 3, - 3, - PhMainWndHandle, - (HMENU)ID_MAINWND_PROCESSTL, - PhLibImageBase, - NULL - ); - BringWindowToTop(PhMwpProcessTreeNewHandle); - - PhMwpServiceTreeNewHandle = CreateWindow( - PH_TREENEW_CLASSNAME, - NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows, - 0, - 0, - 3, - 3, - PhMainWndHandle, - (HMENU)ID_MAINWND_SERVICETL, - PhLibImageBase, - NULL - ); - BringWindowToTop(PhMwpServiceTreeNewHandle); - - PhMwpNetworkTreeNewHandle = CreateWindow( - PH_TREENEW_CLASSNAME, - NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows, - 0, - 0, - 3, - 3, - PhMainWndHandle, - (HMENU)ID_MAINWND_NETWORKTL, - PhLibImageBase, - NULL - ); - BringWindowToTop(PhMwpNetworkTreeNewHandle); - - PageList = PhCreateList(10); - - PhMwpCreateInternalPage(L"Processes", 0, PhMwpProcessesPageCallback); - PhProcessTreeListInitialization(); - PhInitializeProcessTreeList(PhMwpProcessTreeNewHandle); - - PhMwpCreateInternalPage(L"Services", 0, PhMwpServicesPageCallback); - PhServiceTreeListInitialization(); - PhInitializeServiceTreeList(PhMwpServiceTreeNewHandle); - - PhMwpCreateInternalPage(L"Network", 0, PhMwpNetworkPageCallback); - PhNetworkTreeListInitialization(); - PhInitializeNetworkTreeList(PhMwpNetworkTreeNewHandle); - - CurrentPage = PageList->Items[0]; -} - -NTSTATUS PhMwpDelayedLoadFunction( - _In_ PVOID Parameter - ) -{ - // Register for window station notifications. - WinStationRegisterConsoleNotification(NULL, PhMainWndHandle, WNOTIFY_ALL_SESSIONS); - - PhNfLoadStage2(); - - // Make sure we get closed late in the shutdown process. - SetProcessShutdownParameters(0x100, 0); - - DelayedLoadCompleted = TRUE; - //PostMessage(PhMainWndHandle, WM_PH_DELAYED_LOAD_COMPLETED, 0, 0); - - return STATUS_SUCCESS; -} - -PPH_STRING PhMwpFindDbghelpPath( - VOID - ) -{ - static struct - { - ULONG Folder; - PWSTR AppendPath; - } locations[] = - { -#ifdef _WIN64 - { CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\8.1\\Debuggers\\x64\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILESX86, L"\\Windows Kits\\8.0\\Debuggers\\x64\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILES, L"\\Debugging Tools for Windows (x64)\\dbghelp.dll" } -#else - { CSIDL_PROGRAM_FILES, L"\\Windows Kits\\10\\Debuggers\\x86\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILES, L"\\Windows Kits\\8.1\\Debuggers\\x86\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILES, L"\\Windows Kits\\8.0\\Debuggers\\x86\\dbghelp.dll" }, - { CSIDL_PROGRAM_FILES, L"\\Debugging Tools for Windows (x86)\\dbghelp.dll" } -#endif - }; - - PPH_STRING path; - ULONG i; - - for (i = 0; i < sizeof(locations) / sizeof(locations[0]); i++) - { - path = PhGetKnownLocation(locations[i].Folder, locations[i].AppendPath); - - if (path) - { - if (RtlDoesFileExists_U(path->Buffer)) - return path; - - PhDereferenceObject(path); - } - } - - return NULL; -} - -VOID PhMwpOnDestroy( - VOID - ) -{ - // Notify pages and plugins that we are shutting down. - - PhMwpNotifyAllPages(MainTabPageDestroy, NULL, NULL); - - if (PhPluginsEnabled) - PhUnloadPlugins(); - - if (!PhMainWndExiting) - ProcessHacker_SaveAllSettings(PhMainWndHandle); - - PhNfUninitialization(); - - PostQuitMessage(0); -} - -VOID PhMwpOnEndSession( - VOID - ) -{ - PhMwpOnDestroy(); -} - -VOID PhMwpOnSettingChange( - VOID - ) -{ - if (PhApplicationFont) - DeleteObject(PhApplicationFont); - - PhInitializeFont(PhMainWndHandle); - - SendMessage(TabControlHandle, WM_SETFONT, (WPARAM)PhApplicationFont, FALSE); -} - -VOID PhMwpOnCommand( - _In_ ULONG Id - ) -{ - switch (Id) - { - case ID_ESC_EXIT: - { - if (PhGetIntegerSetting(L"HideOnClose")) - { - if (PhNfTestIconMask(PH_ICON_ALL)) - ShowWindow(PhMainWndHandle, SW_HIDE); - } - else if (PhGetIntegerSetting(L"CloseOnEscape")) - { - ProcessHacker_Destroy(PhMainWndHandle); - } - } - break; - case ID_HACKER_RUN: - { - if (RunFileDlg) - { - SelectedRunAsMode = 0; - RunFileDlg(PhMainWndHandle, NULL, NULL, NULL, NULL, 0); - } - } - break; - case ID_HACKER_RUNASADMINISTRATOR: - { - if (RunFileDlg) - { - SelectedRunAsMode = RUNAS_MODE_ADMIN; - RunFileDlg( - PhMainWndHandle, - NULL, - NULL, - NULL, - L"Type the name of a program that will be opened under alternate credentials.", - 0 - ); - } - } - break; - case ID_HACKER_RUNASLIMITEDUSER: - { - if (RunFileDlg) - { - SelectedRunAsMode = RUNAS_MODE_LIMITED; - RunFileDlg( - PhMainWndHandle, - NULL, - NULL, - NULL, - L"Type the name of a program that will be opened under standard user privileges.", - 0 - ); - } - } - break; - case ID_HACKER_RUNAS: - { - PhShowRunAsDialog(PhMainWndHandle, NULL); - } - break; - case ID_HACKER_SHOWDETAILSFORALLPROCESSES: - { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - - if (PhShellProcessHacker( - PhMainWndHandle, - L"-v", - SW_SHOW, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - )) - { - ProcessHacker_Destroy(PhMainWndHandle); - } - else - { - ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); - } - } - break; - case ID_HACKER_SAVE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Text files (*.txt;*.log)", L"*.txt;*.log" }, - { L"Comma-separated values (*.csv)", L"*.csv" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog = PhCreateSaveFileDialog(); - PH_FORMAT format[3]; - - PhInitFormatS(&format[0], L"Process Hacker "); - PhInitFormatSR(&format[1], CurrentPage->Name); - PhInitFormatS(&format[2], L".txt"); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, PH_AUTO_T(PH_STRING, PhFormat(format, 3, 60))->Buffer); - - if (PhShowFileDialog(PhMainWndHandle, fileDialog)) - { - NTSTATUS status; - PPH_STRING fileName; - ULONG filterIndex; - PPH_FILE_STREAM fileStream; - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - filterIndex = PhGetFileDialogFilterIndex(fileDialog); - - if (NT_SUCCESS(status = PhCreateFileStream( - &fileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - 0 - ))) - { - ULONG mode; - PH_MAIN_TAB_PAGE_EXPORT_CONTENT exportContent; - - if (filterIndex == 2) - mode = PH_EXPORT_MODE_CSV; - else - mode = PH_EXPORT_MODE_TABS; - - PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); - PhWritePhTextHeader(fileStream); - - exportContent.FileStream = fileStream; - exportContent.Mode = mode; - CurrentPage->Callback(CurrentPage, MainTabPageExportContent, &exportContent, NULL); - - PhDereferenceObject(fileStream); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(PhMainWndHandle, L"Unable to create the file", status, 0); - } - - PhFreeFileDialog(fileDialog); - } - break; - case ID_HACKER_FINDHANDLESORDLLS: - { - PhShowFindObjectsDialog(); - } - break; - case ID_HACKER_OPTIONS: - { - PhShowOptionsDialog(PhMainWndHandle); - } - break; - case ID_HACKER_PLUGINS: - { - PhShowPluginsDialog(PhMainWndHandle); - } - break; - case ID_COMPUTER_LOCK: - case ID_COMPUTER_LOGOFF: - case ID_COMPUTER_SLEEP: - case ID_COMPUTER_HIBERNATE: - case ID_COMPUTER_RESTART: - case ID_COMPUTER_RESTARTBOOTOPTIONS: - case ID_COMPUTER_SHUTDOWN: - case ID_COMPUTER_SHUTDOWNHYBRID: - PhMwpExecuteComputerCommand(Id); - break; - case ID_HACKER_EXIT: - ProcessHacker_Destroy(PhMainWndHandle); - break; - case ID_VIEW_SYSTEMINFORMATION: - PhShowSystemInformationDialog(NULL); - break; - case ID_TRAYICONS_CPUHISTORY: - case ID_TRAYICONS_CPUUSAGE: - case ID_TRAYICONS_IOHISTORY: - case ID_TRAYICONS_COMMITHISTORY: - case ID_TRAYICONS_PHYSICALMEMORYHISTORY: - { - ULONG i; - - switch (Id) - { - case ID_TRAYICONS_CPUHISTORY: - i = PH_ICON_CPU_HISTORY; - break; - case ID_TRAYICONS_CPUUSAGE: - i = PH_ICON_CPU_USAGE; - break; - case ID_TRAYICONS_IOHISTORY: - i = PH_ICON_IO_HISTORY; - break; - case ID_TRAYICONS_COMMITHISTORY: - i = PH_ICON_COMMIT_HISTORY; - break; - case ID_TRAYICONS_PHYSICALMEMORYHISTORY: - i = PH_ICON_PHYSICAL_HISTORY; - break; - } - - PhNfSetVisibleIcon(i, !PhNfTestIconMask(i)); - } - break; - case ID_VIEW_HIDEPROCESSESFROMOTHERUSERS: - { - PhMwpToggleCurrentUserProcessTreeFilter(); - } - break; - case ID_VIEW_HIDESIGNEDPROCESSES: - { - PhMwpToggleSignedProcessTreeFilter(); - } - break; - case ID_VIEW_SCROLLTONEWPROCESSES: - { - PH_SET_INTEGER_CACHED_SETTING(ScrollToNewProcesses, !PhCsScrollToNewProcesses); - } - break; - case ID_VIEW_SHOWCPUBELOW001: - { - PH_SET_INTEGER_CACHED_SETTING(ShowCpuBelow001, !PhCsShowCpuBelow001); - PhInvalidateAllProcessNodes(); - } - break; - case ID_VIEW_HIDEDRIVERSERVICES: - { - PhMwpToggleDriverServiceTreeFilter(); - } - break; - case ID_VIEW_ALWAYSONTOP: - { - AlwaysOnTop = !AlwaysOnTop; - SetWindowPos(PhMainWndHandle, AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - PhSetIntegerSetting(L"MainWindowAlwaysOnTop", AlwaysOnTop); - } - break; - case ID_OPACITY_10: - case ID_OPACITY_20: - case ID_OPACITY_30: - case ID_OPACITY_40: - case ID_OPACITY_50: - case ID_OPACITY_60: - case ID_OPACITY_70: - case ID_OPACITY_80: - case ID_OPACITY_90: - case ID_OPACITY_OPAQUE: - { - ULONG opacity; - - opacity = PH_ID_TO_OPACITY(Id); - PhSetIntegerSetting(L"MainWindowOpacity", opacity); - PhSetWindowOpacity(PhMainWndHandle, opacity); - } - break; - case ID_VIEW_REFRESH: - { - PhBoostProvider(&PhMwpProcessProviderRegistration, NULL); - PhBoostProvider(&PhMwpServiceProviderRegistration, NULL); - } - break; - case ID_UPDATEINTERVAL_FAST: - case ID_UPDATEINTERVAL_NORMAL: - case ID_UPDATEINTERVAL_BELOWNORMAL: - case ID_UPDATEINTERVAL_SLOW: - case ID_UPDATEINTERVAL_VERYSLOW: - { - ULONG interval; - - switch (Id) - { - case ID_UPDATEINTERVAL_FAST: - interval = 500; - break; - case ID_UPDATEINTERVAL_NORMAL: - interval = 1000; - break; - case ID_UPDATEINTERVAL_BELOWNORMAL: - interval = 2000; - break; - case ID_UPDATEINTERVAL_SLOW: - interval = 5000; - break; - case ID_UPDATEINTERVAL_VERYSLOW: - interval = 10000; - break; - } - - PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, interval); - PhMwpApplyUpdateInterval(interval); - } - break; - case ID_VIEW_UPDATEAUTOMATICALLY: - { - PhMwpUpdateAutomatically = !PhMwpUpdateAutomatically; - PhMwpNotifyAllPages(MainTabPageUpdateAutomaticallyChanged, (PVOID)PhMwpUpdateAutomatically, NULL); - } - break; - case ID_TOOLS_CREATESERVICE: - { - PhShowCreateServiceDialog(PhMainWndHandle); - } - break; - case ID_TOOLS_HIDDENPROCESSES: - { - PhShowHiddenProcessesDialog(); - } - break; - case ID_TOOLS_INSPECTEXECUTABLEFILE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Executable files (*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl)", L"*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog = PhCreateOpenFileDialog(); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - if (PhShowFileDialog(PhMainWndHandle, fileDialog)) - { - PhShellExecuteUserString( - PhMainWndHandle, - L"ProgramInspectExecutables", - PH_AUTO_T(PH_STRING, PhGetFileDialogFileName(fileDialog))->Buffer, - FALSE, - L"Make sure the PE Viewer executable file is present." - ); - } - - PhFreeFileDialog(fileDialog); - } - break; - case ID_TOOLS_PAGEFILES: - { - PhShowPagefilesDialog(PhMainWndHandle); - } - break; - case ID_TOOLS_STARTTASKMANAGER: - { - PPH_STRING systemDirectory; - PPH_STRING taskmgrFileName; - - systemDirectory = PH_AUTO(PhGetSystemDirectory()); - taskmgrFileName = PH_AUTO(PhConcatStrings2(systemDirectory->Buffer, L"\\taskmgr.exe")); - - if (WindowsVersion >= WINDOWS_8 && !PhGetOwnTokenAttributes().Elevated) - { - if (PhUiConnectToPhSvc(PhMainWndHandle, FALSE)) - { - PhSvcCallCreateProcessIgnoreIfeoDebugger(taskmgrFileName->Buffer); - PhUiDisconnectFromPhSvc(); - } - } - else - { - PhCreateProcessIgnoreIfeoDebugger(taskmgrFileName->Buffer); - } - } - break; - case ID_USER_CONNECT: - { - PhUiConnectSession(PhMainWndHandle, SelectedUserSessionId); - } - break; - case ID_USER_DISCONNECT: - { - PhUiDisconnectSession(PhMainWndHandle, SelectedUserSessionId); - } - break; - case ID_USER_LOGOFF: - { - PhUiLogoffSession(PhMainWndHandle, SelectedUserSessionId); - } - break; - case ID_USER_REMOTECONTROL: - { - PhShowSessionShadowDialog(PhMainWndHandle, SelectedUserSessionId); - } - break; - case ID_USER_SENDMESSAGE: - { - PhShowSessionSendMessageDialog(PhMainWndHandle, SelectedUserSessionId); - } - break; - case ID_USER_PROPERTIES: - { - PhShowSessionProperties(PhMainWndHandle, SelectedUserSessionId); - } - break; - case ID_HELP_LOG: - { - PhShowLogDialog(); - } - break; - case ID_HELP_DONATE: - { - PhShellExecute(PhMainWndHandle, L"https://sourceforge.net/project/project_donations.php?group_id=242527", NULL); - } - break; - case ID_HELP_DEBUGCONSOLE: - { - PhShowDebugConsole(); - } - break; - case ID_HELP_ABOUT: - { - PhShowAboutDialog(PhMainWndHandle); - } - break; - case ID_PROCESS_TERMINATE: - { - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - PhReferenceObjects(processes, numberOfProcesses); - - if (PhUiTerminateProcesses(PhMainWndHandle, processes, numberOfProcesses)) - PhDeselectAllProcessNodes(); - - PhDereferenceObjects(processes, numberOfProcesses); - PhFree(processes); - } - break; - case ID_PROCESS_TERMINATETREE: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - - if (PhUiTerminateTreeProcess(PhMainWndHandle, processItem)) - PhDeselectAllProcessNodes(); - - PhDereferenceObject(processItem); - } - } - break; - case ID_PROCESS_SUSPEND: - { - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - PhReferenceObjects(processes, numberOfProcesses); - PhUiSuspendProcesses(PhMainWndHandle, processes, numberOfProcesses); - PhDereferenceObjects(processes, numberOfProcesses); - PhFree(processes); - } - break; - case ID_PROCESS_RESUME: - { - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - PhReferenceObjects(processes, numberOfProcesses); - PhUiResumeProcesses(PhMainWndHandle, processes, numberOfProcesses); - PhDereferenceObjects(processes, numberOfProcesses); - PhFree(processes); - } - break; - case ID_PROCESS_RESTART: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - - if (PhUiRestartProcess(PhMainWndHandle, processItem)) - PhDeselectAllProcessNodes(); - - PhDereferenceObject(processItem); - } - } - break; - case ID_PROCESS_CREATEDUMPFILE: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhUiCreateDumpFileProcess(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - } - break; - case ID_PROCESS_DEBUG: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhUiDebugProcess(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - } - break; - case ID_PROCESS_VIRTUALIZATION: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhUiSetVirtualizationProcess( - PhMainWndHandle, - processItem, - !PhMwpSelectedProcessVirtualizationEnabled - ); - PhDereferenceObject(processItem); - } - } - break; - case ID_PROCESS_AFFINITY: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhShowProcessAffinityDialog(PhMainWndHandle, processItem, NULL); - PhDereferenceObject(processItem); - } - } - break; - case ID_MISCELLANEOUS_DETACHFROMDEBUGGER: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhUiDetachFromDebuggerProcess(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - } - break; - case ID_MISCELLANEOUS_GDIHANDLES: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhShowGdiHandlesDialog(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - } - break; - case ID_MISCELLANEOUS_INJECTDLL: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhReferenceObject(processItem); - PhUiInjectDllProcess(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - } - break; - case ID_PAGEPRIORITY_VERYLOW: - case ID_PAGEPRIORITY_LOW: - case ID_PAGEPRIORITY_MEDIUM: - case ID_PAGEPRIORITY_BELOWNORMAL: - case ID_PAGEPRIORITY_NORMAL: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - ULONG pagePriority; - - switch (Id) - { - case ID_PAGEPRIORITY_VERYLOW: - pagePriority = MEMORY_PRIORITY_VERY_LOW; - break; - case ID_PAGEPRIORITY_LOW: - pagePriority = MEMORY_PRIORITY_LOW; - break; - case ID_PAGEPRIORITY_MEDIUM: - pagePriority = MEMORY_PRIORITY_MEDIUM; - break; - case ID_PAGEPRIORITY_BELOWNORMAL: - pagePriority = MEMORY_PRIORITY_BELOW_NORMAL; - break; - case ID_PAGEPRIORITY_NORMAL: - pagePriority = MEMORY_PRIORITY_NORMAL; - break; - } - - PhReferenceObject(processItem); - PhUiSetPagePriorityProcess(PhMainWndHandle, processItem, pagePriority); - PhDereferenceObject(processItem); - } - } - break; - case ID_MISCELLANEOUS_REDUCEWORKINGSET: - { - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - PhReferenceObjects(processes, numberOfProcesses); - PhUiReduceWorkingSetProcesses(PhMainWndHandle, processes, numberOfProcesses); - PhDereferenceObjects(processes, numberOfProcesses); - PhFree(processes); - } - break; - case ID_MISCELLANEOUS_RUNAS: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem && processItem->FileName) - { - PhSetStringSetting2(L"RunAsProgram", &processItem->FileName->sr); - PhShowRunAsDialog(PhMainWndHandle, NULL); - } - } - break; - case ID_MISCELLANEOUS_RUNASTHISUSER: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhShowRunAsDialog(PhMainWndHandle, processItem->ProcessId); - } - } - break; - case ID_PRIORITY_REALTIME: - case ID_PRIORITY_HIGH: - case ID_PRIORITY_ABOVENORMAL: - case ID_PRIORITY_NORMAL: - case ID_PRIORITY_BELOWNORMAL: - case ID_PRIORITY_IDLE: - { - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - PhReferenceObjects(processes, numberOfProcesses); - PhMwpExecuteProcessPriorityCommand(Id, processes, numberOfProcesses); - PhDereferenceObjects(processes, numberOfProcesses); - PhFree(processes); - } - break; - case ID_IOPRIORITY_VERYLOW: - case ID_IOPRIORITY_LOW: - case ID_IOPRIORITY_NORMAL: - case ID_IOPRIORITY_HIGH: - { - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - PhReferenceObjects(processes, numberOfProcesses); - PhMwpExecuteProcessIoPriorityCommand(Id, processes, numberOfProcesses); - PhDereferenceObjects(processes, numberOfProcesses); - PhFree(processes); - } - break; - case ID_WINDOW_BRINGTOFRONT: - { - if (IsWindow(PhMwpSelectedProcessWindowHandle)) - { - WINDOWPLACEMENT placement = { sizeof(placement) }; - - GetWindowPlacement(PhMwpSelectedProcessWindowHandle, &placement); - - if (placement.showCmd == SW_MINIMIZE) - ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_RESTORE); - else - SetForegroundWindow(PhMwpSelectedProcessWindowHandle); - } - } - break; - case ID_WINDOW_RESTORE: - { - if (IsWindow(PhMwpSelectedProcessWindowHandle)) - { - ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_RESTORE); - } - } - break; - case ID_WINDOW_MINIMIZE: - { - if (IsWindow(PhMwpSelectedProcessWindowHandle)) - { - ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_MINIMIZE); - } - } - break; - case ID_WINDOW_MAXIMIZE: - { - if (IsWindow(PhMwpSelectedProcessWindowHandle)) - { - ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_MAXIMIZE); - } - } - break; - case ID_WINDOW_CLOSE: - { - if (IsWindow(PhMwpSelectedProcessWindowHandle)) - { - PostMessage(PhMwpSelectedProcessWindowHandle, WM_CLOSE, 0, 0); - } - } - break; - case ID_PROCESS_OPENFILELOCATION: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem && processItem->FileName) - { - PhReferenceObject(processItem); - PhShellExploreFile(PhMainWndHandle, processItem->FileName->Buffer); - PhDereferenceObject(processItem); - } - } - break; - case ID_PROCESS_SEARCHONLINE: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - PhSearchOnlineString(PhMainWndHandle, processItem->ProcessName->Buffer); - } - } - break; - case ID_PROCESS_PROPERTIES: - { - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (processItem) - { - // No reference needed; no messages pumped. - PhMwpShowProcessProperties(processItem); - } - } - break; - case ID_PROCESS_COPY: - { - PhCopyProcessTree(); - } - break; - case ID_SERVICE_GOTOPROCESS: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - PPH_PROCESS_NODE processNode; - - if (serviceItem) - { - if (processNode = PhFindProcessNode(serviceItem->ProcessId)) - { - PhMwpSelectPage(PhMwpProcessesPage->Index); - SetFocus(PhMwpProcessTreeNewHandle); - PhSelectAndEnsureVisibleProcessNode(processNode); - } - } - } - break; - case ID_SERVICE_START: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - PhReferenceObject(serviceItem); - PhUiStartService(PhMainWndHandle, serviceItem); - PhDereferenceObject(serviceItem); - } - } - break; - case ID_SERVICE_CONTINUE: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - PhReferenceObject(serviceItem); - PhUiContinueService(PhMainWndHandle, serviceItem); - PhDereferenceObject(serviceItem); - } - } - break; - case ID_SERVICE_PAUSE: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - PhReferenceObject(serviceItem); - PhUiPauseService(PhMainWndHandle, serviceItem); - PhDereferenceObject(serviceItem); - } - } - break; - case ID_SERVICE_STOP: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - PhReferenceObject(serviceItem); - PhUiStopService(PhMainWndHandle, serviceItem); - PhDereferenceObject(serviceItem); - } - } - break; - case ID_SERVICE_DELETE: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - PhReferenceObject(serviceItem); - - if (PhUiDeleteService(PhMainWndHandle, serviceItem)) - PhDeselectAllServiceNodes(); - - PhDereferenceObject(serviceItem); - } - } - break; - case ID_SERVICE_OPENKEY: - { - static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); - static PH_STRINGREF hklm = PH_STRINGREF_INIT(L"HKLM\\"); - - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - HANDLE keyHandle; - PPH_STRING serviceKeyName = PH_AUTO(PhConcatStringRef2(&servicesKeyName, &serviceItem->Name->sr)); - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &serviceKeyName->sr, - 0 - ))) - { - PPH_STRING hklmServiceKeyName; - - hklmServiceKeyName = PH_AUTO(PhConcatStringRef2(&hklm, &serviceKeyName->sr)); - PhShellOpenKey2(PhMainWndHandle, hklmServiceKeyName); - NtClose(keyHandle); - } - } - } - break; - case ID_SERVICE_OPENFILELOCATION: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - SC_HANDLE serviceHandle; - - if (serviceItem && (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) - { - PPH_STRING fileName; - - if (fileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle)) - { - PhShellExploreFile(PhMainWndHandle, fileName->Buffer); - PhDereferenceObject(fileName); - } - - CloseServiceHandle(serviceHandle); - } - } - break; - case ID_SERVICE_PROPERTIES: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); - - if (serviceItem) - { - // The object relies on the list view reference, which could - // disappear if we don't reference the object here. - PhReferenceObject(serviceItem); - PhShowServiceProperties(PhMainWndHandle, serviceItem); - PhDereferenceObject(serviceItem); - } - } - break; - case ID_SERVICE_COPY: - { - PhCopyServiceList(); - } - break; - case ID_NETWORK_GOTOPROCESS: - { - PPH_NETWORK_ITEM networkItem = PhGetSelectedNetworkItem(); - PPH_PROCESS_NODE processNode; - - if (networkItem) - { - if (processNode = PhFindProcessNode(networkItem->ProcessId)) - { - PhMwpSelectPage(PhMwpProcessesPage->Index); - SetFocus(PhMwpProcessTreeNewHandle); - PhSelectAndEnsureVisibleProcessNode(processNode); - } - } - } - break; - case ID_NETWORK_GOTOSERVICE: - { - PPH_NETWORK_ITEM networkItem = PhGetSelectedNetworkItem(); - PPH_SERVICE_ITEM serviceItem; - - if (networkItem && networkItem->OwnerName) - { - if (serviceItem = PhReferenceServiceItem(networkItem->OwnerName->Buffer)) - { - PhMwpSelectPage(PhMwpServicesPage->Index); - SetFocus(PhMwpServiceTreeNewHandle); - ProcessHacker_SelectServiceItem(PhMainWndHandle, serviceItem); - - PhDereferenceObject(serviceItem); - } - } - } - break; - case ID_NETWORK_VIEWSTACK: - { - PPH_NETWORK_ITEM networkItem = PhGetSelectedNetworkItem(); - - if (networkItem) - { - PhReferenceObject(networkItem); - PhShowNetworkStackDialog(PhMainWndHandle, networkItem); - PhDereferenceObject(networkItem); - } - } - break; - case ID_NETWORK_CLOSE: - { - PPH_NETWORK_ITEM *networkItems; - ULONG numberOfNetworkItems; - - PhGetSelectedNetworkItems(&networkItems, &numberOfNetworkItems); - PhReferenceObjects(networkItems, numberOfNetworkItems); - - if (PhUiCloseConnections(PhMainWndHandle, networkItems, numberOfNetworkItems)) - PhDeselectAllNetworkNodes(); - - PhDereferenceObjects(networkItems, numberOfNetworkItems); - PhFree(networkItems); - } - break; - case ID_NETWORK_COPY: - { - PhCopyNetworkList(); - } - break; - case ID_TAB_NEXT: - { - ULONG selectedIndex = TabCtrl_GetCurSel(TabControlHandle); - - if (selectedIndex != PageList->Count - 1) - selectedIndex++; - else - selectedIndex = 0; - - PhMwpSelectPage(selectedIndex); - } - break; - case ID_TAB_PREV: - { - ULONG selectedIndex = TabCtrl_GetCurSel(TabControlHandle); - - if (selectedIndex != 0) - selectedIndex--; - else - selectedIndex = PageList->Count - 1; - - PhMwpSelectPage(selectedIndex); - } - break; - } -} - -VOID PhMwpOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ) -{ - if (NeedsMaximize) - { - ShowWindow(PhMainWndHandle, SW_MAXIMIZE); - NeedsMaximize = FALSE; - } -} - -BOOLEAN PhMwpOnSysCommand( - _In_ ULONG Type, - _In_ LONG CursorScreenX, - _In_ LONG CursorScreenY - ) -{ - switch (Type) - { - case SC_CLOSE: - { - if (PhGetIntegerSetting(L"HideOnClose") && PhNfTestIconMask(PH_ICON_ALL)) - { - ShowWindow(PhMainWndHandle, SW_HIDE); - return TRUE; - } - } - break; - case SC_MINIMIZE: - { - // Save the current window state because we may not have a chance to later. - PhMwpSaveWindowState(); - - if (PhGetIntegerSetting(L"HideOnMinimize") && PhNfTestIconMask(PH_ICON_ALL)) - { - ShowWindow(PhMainWndHandle, SW_HIDE); - return TRUE; - } - } - break; - } - - return FALSE; -} - -VOID PhMwpOnMenuCommand( - _In_ ULONG Index, - _In_ HMENU Menu - ) -{ - MENUITEMINFO menuItemInfo; - - menuItemInfo.cbSize = sizeof(MENUITEMINFO); - menuItemInfo.fMask = MIIM_ID | MIIM_DATA; - - if (GetMenuItemInfo(Menu, Index, TRUE, &menuItemInfo)) - { - PhMwpDispatchMenuCommand(Menu, Index, menuItemInfo.wID, menuItemInfo.dwItemData); - } -} - -VOID PhMwpOnInitMenuPopup( - _In_ HMENU Menu, - _In_ ULONG Index, - _In_ BOOLEAN IsWindowMenu - ) -{ - ULONG i; - BOOLEAN found; - MENUINFO menuInfo; - PPH_EMENU menu; - - found = FALSE; - - for (i = 0; i < sizeof(SubMenuHandles) / sizeof(HWND); i++) - { - if (Menu == SubMenuHandles[i]) - { - found = TRUE; - break; - } - } - - if (!found) - return; - - if (Index == 3) - { - // Special case for Users menu. - if (!UsersMenuInitialized) - { - PhMwpUpdateUsersMenu(); - UsersMenuInitialized = TRUE; - } - - return; - } - - // Delete all items in this submenu. - while (DeleteMenu(Menu, 0, MF_BYPOSITION)) ; - - // Delete the previous EMENU for this submenu. - if (SubMenuObjects[Index]) - PhDestroyEMenu(SubMenuObjects[Index]); - - // Make sure the menu style is set correctly. - memset(&menuInfo, 0, sizeof(MENUINFO)); - menuInfo.cbSize = sizeof(MENUINFO); - menuInfo.fMask = MIM_STYLE; - menuInfo.dwStyle = MNS_CHECKORBMP; - SetMenuInfo(Menu, &menuInfo); - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND), Index); - - PhMwpInitializeSubMenu(menu, Index); - - if (PhPluginsEnabled) - { - PH_PLUGIN_MENU_INFORMATION menuInfo; - - PhPluginInitializeMenuInfo(&menuInfo, menu, PhMainWndHandle, PH_PLUGIN_MENU_DISALLOW_HOOKS); - menuInfo.u.MainMenu.SubMenuIndex = Index; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), &menuInfo); - } - - PhEMenuToHMenu2(Menu, menu, 0, NULL); - SubMenuObjects[Index] = menu; -} - -VOID PhMwpOnSize( - VOID - ) -{ - if (!IsIconic(PhMainWndHandle)) - { - HDWP deferHandle; - - deferHandle = BeginDeferWindowPos(2); - PhMwpLayout(&deferHandle); - EndDeferWindowPos(deferHandle); - } -} - -VOID PhMwpOnSizing( - _In_ ULONG Edge, - _In_ PRECT DragRectangle - ) -{ - PhResizingMinimumSize(DragRectangle, Edge, 400, 340); -} - -VOID PhMwpOnSetFocus( - VOID - ) -{ - if (CurrentPage->WindowHandle) - SetFocus(CurrentPage->WindowHandle); -} - -VOID PhMwpOnTimer( - _In_ ULONG Id - ) -{ - if (Id == TIMER_FLUSH_PROCESS_QUERY_DATA) - { - static ULONG state = 1; - - // If the update interval is too large, the user might have to wait a while before seeing some types of - // process-related data. Here we force an update. - // - // In addition, we force updates shortly after the program starts up to make things appear more quickly. - - switch (state) - { - case 1: - state = 2; - - if (PhCsUpdateInterval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_2) - SetTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA, PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_2, NULL); - else - KillTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA); - - break; - case 2: - state = 3; - - if (PhCsUpdateInterval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) - SetTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA, PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM, NULL); - else - KillTimer(PhMainWndHandle, TIMER_FLUSH_PROCESS_QUERY_DATA); - - break; - default: - NOTHING; - break; - } - - PhFlushProcessQueryData(TRUE); - } -} - -BOOLEAN PhMwpOnNotify( - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ) -{ - if (Header->hwndFrom == TabControlHandle) - { - PhMwpNotifyTabControl(Header); - } - else if (Header->code == RFN_VALIDATE) - { - LPNMRUNFILEDLG runFileDlg = (LPNMRUNFILEDLG)Header; - - if (SelectedRunAsMode == RUNAS_MODE_ADMIN) - { - PH_STRINGREF string; - PH_STRINGREF fileName; - PH_STRINGREF arguments; - PPH_STRING fullFileName; - PPH_STRING argumentsString; - - PhInitializeStringRefLongHint(&string, (PWSTR)runFileDlg->lpszFile); - PhParseCommandLineFuzzy(&string, &fileName, &arguments, &fullFileName); - - if (!fullFileName) - fullFileName = PhCreateString2(&fileName); - - argumentsString = PhCreateString2(&arguments); - - if (PhShellExecuteEx(PhMainWndHandle, fullFileName->Buffer, argumentsString->Buffer, - runFileDlg->nShow, PH_SHELL_EXECUTE_ADMIN, 0, NULL)) - { - *Result = RF_CANCEL; - } - else - { - *Result = RF_RETRY; - } - - PhDereferenceObject(fullFileName); - PhDereferenceObject(argumentsString); - - return TRUE; - } - else if (SelectedRunAsMode == RUNAS_MODE_LIMITED) - { - NTSTATUS status; - HANDLE tokenHandle; - HANDLE newTokenHandle; - - if (NT_SUCCESS(status = PhOpenProcessToken( - NtCurrentProcess(), - TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_GROUPS | - TOKEN_ADJUST_DEFAULT | READ_CONTROL | WRITE_DAC, - &tokenHandle - ))) - { - if (NT_SUCCESS(status = PhFilterTokenForLimitedUser( - tokenHandle, - &newTokenHandle - ))) - { - status = PhCreateProcessWin32( - NULL, - (PWSTR)runFileDlg->lpszFile, - NULL, - NULL, - 0, - newTokenHandle, - NULL, - NULL - ); - - NtClose(newTokenHandle); - } - - NtClose(tokenHandle); - } - - if (NT_SUCCESS(status)) - { - *Result = RF_CANCEL; - } - else - { - PhShowStatus(PhMainWndHandle, L"Unable to execute the program", status, 0); - *Result = RF_RETRY; - } - - return TRUE; - } - } - - return FALSE; -} - -VOID PhMwpOnWtsSessionChange( - _In_ ULONG Reason, - _In_ ULONG SessionId - ) -{ - if (Reason == WTS_SESSION_LOGON || Reason == WTS_SESSION_LOGOFF) - { - if (UsersMenuInitialized) - { - PhMwpUpdateUsersMenu(); - } - } -} - -ULONG_PTR PhMwpOnUserMessage( - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ) -{ - switch (Message) - { - case WM_PH_ACTIVATE: - { - if (!PhMainWndExiting) - { - if (WParam != 0) - { - PPH_PROCESS_NODE processNode; - - if (processNode = PhFindProcessNode((HANDLE)WParam)) - PhSelectAndEnsureVisibleProcessNode(processNode); - } - - if (!IsWindowVisible(PhMainWndHandle)) - { - ShowWindow(PhMainWndHandle, SW_SHOW); - } - - if (IsIconic(PhMainWndHandle)) - { - ShowWindow(PhMainWndHandle, SW_RESTORE); - } - - return PH_ACTIVATE_REPLY; - } - else - { - return 0; - } - } - break; - case WM_PH_SHOW_PROCESS_PROPERTIES: - { - PhMwpShowProcessProperties((PPH_PROCESS_ITEM)LParam); - } - break; - case WM_PH_DESTROY: - { - DestroyWindow(PhMainWndHandle); - } - break; - case WM_PH_SAVE_ALL_SETTINGS: - { - PhMwpSaveSettings(); - } - break; - case WM_PH_PREPARE_FOR_EARLY_SHUTDOWN: - { - PhMwpSaveSettings(); - PhMainWndExiting = TRUE; - } - break; - case WM_PH_CANCEL_EARLY_SHUTDOWN: - { - PhMainWndExiting = FALSE; - } - break; - case WM_PH_DELAYED_LOAD_COMPLETED: - { - // Nothing - } - break; - case WM_PH_NOTIFY_ICON_MESSAGE: - { - PhNfForwardMessage(WParam, LParam); - } - break; - case WM_PH_TOGGLE_VISIBLE: - { - PhMwpActivateWindow(!WParam); - } - break; - case WM_PH_SHOW_MEMORY_EDITOR: - { - PPH_SHOW_MEMORY_EDITOR showMemoryEditor = (PPH_SHOW_MEMORY_EDITOR)LParam; - - PhShowMemoryEditorDialog( - showMemoryEditor->ProcessId, - showMemoryEditor->BaseAddress, - showMemoryEditor->RegionSize, - showMemoryEditor->SelectOffset, - showMemoryEditor->SelectLength, - showMemoryEditor->Title, - showMemoryEditor->Flags - ); - PhClearReference(&showMemoryEditor->Title); - PhFree(showMemoryEditor); - } - break; - case WM_PH_SHOW_MEMORY_RESULTS: - { - PPH_SHOW_MEMORY_RESULTS showMemoryResults = (PPH_SHOW_MEMORY_RESULTS)LParam; - - PhShowMemoryResultsDialog( - showMemoryResults->ProcessId, - showMemoryResults->Results - ); - PhDereferenceMemoryResults( - (PPH_MEMORY_RESULT *)showMemoryResults->Results->Items, - showMemoryResults->Results->Count - ); - PhDereferenceObject(showMemoryResults->Results); - PhFree(showMemoryResults); - } - break; - case WM_PH_SELECT_TAB_PAGE: - { - ULONG index = (ULONG)WParam; - - PhMwpSelectPage(index); - - if (CurrentPage->WindowHandle) - SetFocus(CurrentPage->WindowHandle); - } - break; - case WM_PH_GET_CALLBACK_LAYOUT_PADDING: - { - return (ULONG_PTR)&LayoutPaddingCallback; - } - break; - case WM_PH_INVALIDATE_LAYOUT_PADDING: - { - LayoutPaddingValid = FALSE; - } - break; - case WM_PH_SELECT_PROCESS_NODE: - { - PhSelectAndEnsureVisibleProcessNode((PPH_PROCESS_NODE)LParam); - } - break; - case WM_PH_SELECT_SERVICE_ITEM: - { - PPH_SERVICE_NODE serviceNode; - - PhMwpNeedServiceTreeList(); - - // For compatibility, LParam is a service item, not node. - if (serviceNode = PhFindServiceNode((PPH_SERVICE_ITEM)LParam)) - { - PhSelectAndEnsureVisibleServiceNode(serviceNode); - } - } - break; - case WM_PH_SELECT_NETWORK_ITEM: - { - PPH_NETWORK_NODE networkNode; - - PhMwpNeedNetworkTreeList(); - - // For compatibility, LParam is a network item, not node. - if (networkNode = PhFindNetworkNode((PPH_NETWORK_ITEM)LParam)) - { - PhSelectAndEnsureVisibleNetworkNode(networkNode); - } - } - break; - case WM_PH_UPDATE_FONT: - { - PPH_STRING fontHexString; - LOGFONT font; - - fontHexString = PhaGetStringSetting(L"Font"); - - if ( - fontHexString->Length / 2 / 2 == sizeof(LOGFONT) && - PhHexStringToBuffer(&fontHexString->sr, (PUCHAR)&font) - ) - { - HFONT newFont; - - newFont = CreateFontIndirect(&font); - - if (newFont) - { - if (CurrentCustomFont) - DeleteObject(CurrentCustomFont); - CurrentCustomFont = newFont; - - PhMwpNotifyAllPages(MainTabPageFontChanged, newFont, NULL); - } - } - } - break; - case WM_PH_GET_FONT: - return SendMessage(PhMwpProcessTreeNewHandle, WM_GETFONT, 0, 0); - case WM_PH_INVOKE: - { - VOID (NTAPI *function)(PVOID); - - function = (PVOID)LParam; - function((PVOID)WParam); - } - break; - case WM_PH_ADD_MENU_ITEM: - { - PPH_ADD_MENU_ITEM addMenuItem = (PPH_ADD_MENU_ITEM)LParam; - - return PhMwpLegacyAddPluginMenuItem(addMenuItem); - } - break; - case WM_PH_CREATE_TAB_PAGE: - { - return (ULONG_PTR)PhMwpCreatePage((PPH_MAIN_TAB_PAGE)LParam); - } - break; - case WM_PH_REFRESH: - { - SendMessage(PhMainWndHandle, WM_COMMAND, ID_VIEW_REFRESH, 0); - } - break; - case WM_PH_GET_UPDATE_AUTOMATICALLY: - { - return PhMwpUpdateAutomatically; - } - break; - case WM_PH_SET_UPDATE_AUTOMATICALLY: - { - if (!!WParam != PhMwpUpdateAutomatically) - { - SendMessage(PhMainWndHandle, WM_COMMAND, ID_VIEW_UPDATEAUTOMATICALLY, 0); - } - } - break; - case WM_PH_ICON_CLICK: - { - PhMwpActivateWindow(!!PhGetIntegerSetting(L"IconTogglesVisibility")); - } - break; - case WM_PH_PROCESS_ADDED: - { - ULONG runId = (ULONG)WParam; - PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)LParam; - - PhMwpOnProcessAdded(processItem, runId); - } - break; - case WM_PH_PROCESS_MODIFIED: - { - PhMwpOnProcessModified((PPH_PROCESS_ITEM)LParam); - } - break; - case WM_PH_PROCESS_REMOVED: - { - PhMwpOnProcessRemoved((PPH_PROCESS_ITEM)LParam); - } - break; - case WM_PH_PROCESSES_UPDATED: - { - PhMwpOnProcessesUpdated(); - } - break; - case WM_PH_SERVICE_ADDED: - { - ULONG runId = (ULONG)WParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)LParam; - - PhMwpOnServiceAdded(serviceItem, runId); - } - break; - case WM_PH_SERVICE_MODIFIED: - { - PPH_SERVICE_MODIFIED_DATA serviceModifiedData = (PPH_SERVICE_MODIFIED_DATA)LParam; - - PhMwpOnServiceModified(serviceModifiedData); - PhFree(serviceModifiedData); - } - break; - case WM_PH_SERVICE_REMOVED: - { - PhMwpOnServiceRemoved((PPH_SERVICE_ITEM)LParam); - } - break; - case WM_PH_SERVICES_UPDATED: - { - PhMwpOnServicesUpdated(); - } - break; - case WM_PH_NETWORK_ITEM_ADDED: - { - ULONG runId = (ULONG)WParam; - PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)LParam; - - PhMwpOnNetworkItemAdded(runId, networkItem); - } - break; - case WM_PH_NETWORK_ITEM_MODIFIED: - { - PhMwpOnNetworkItemModified((PPH_NETWORK_ITEM)LParam); - } - break; - case WM_PH_NETWORK_ITEM_REMOVED: - { - PhMwpOnNetworkItemRemoved((PPH_NETWORK_ITEM)LParam); - } - break; - case WM_PH_NETWORK_ITEMS_UPDATED: - { - PhMwpOnNetworkItemsUpdated(); - } - break; - } - - return 0; -} - -VOID NTAPI PhMwpNetworkItemAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; - - PhReferenceObject(networkItem); - PostMessage( - PhMainWndHandle, - WM_PH_NETWORK_ITEM_ADDED, - PhGetRunIdProvider(&PhMwpNetworkProviderRegistration), - (LPARAM)networkItem - ); -} - -VOID NTAPI PhMwpNetworkItemModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; - - PostMessage(PhMainWndHandle, WM_PH_NETWORK_ITEM_MODIFIED, 0, (LPARAM)networkItem); -} - -VOID NTAPI PhMwpNetworkItemRemovedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; - - PostMessage(PhMainWndHandle, WM_PH_NETWORK_ITEM_REMOVED, 0, (LPARAM)networkItem); -} - -VOID NTAPI PhMwpNetworkItemsUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PostMessage(PhMainWndHandle, WM_PH_NETWORK_ITEMS_UPDATED, 0, 0); -} - -VOID PhMwpLoadSettings( - VOID - ) -{ - ULONG opacity; - PPH_STRING customFont; - - if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) - { - AlwaysOnTop = TRUE; - SetWindowPos(PhMainWndHandle, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); - } - - opacity = PhGetIntegerSetting(L"MainWindowOpacity"); - - if (opacity != 0) - PhSetWindowOpacity(PhMainWndHandle, opacity); - - PhStatisticsSampleCount = PhGetIntegerSetting(L"SampleCount"); - PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); - PhEnablePurgeProcessRecords = !PhGetIntegerSetting(L"NoPurgeProcessRecords"); - PhEnableCycleCpuUsage = !!PhGetIntegerSetting(L"EnableCycleCpuUsage"); - PhEnableServiceNonPoll = !!PhGetIntegerSetting(L"EnableServiceNonPoll"); - PhEnableNetworkProviderResolve = !!PhGetIntegerSetting(L"EnableNetworkResolve"); - - PhNfLoadStage1(); - PhMwpNotifyIconNotifyMask = PhGetIntegerSetting(L"IconNotifyMask"); - - customFont = PhaGetStringSetting(L"Font"); - - if (customFont->Length / 2 / 2 == sizeof(LOGFONT)) - SendMessage(PhMainWndHandle, WM_PH_UPDATE_FONT, 0, 0); - - PhMwpNotifyAllPages(MainTabPageLoadSettings, NULL, NULL); -} - -VOID PhMwpSaveSettings( - VOID - ) -{ - PhMwpNotifyAllPages(MainTabPageSaveSettings, NULL, NULL); - - PhNfSaveSettings(); - PhSetIntegerSetting(L"IconNotifyMask", PhMwpNotifyIconNotifyMask); - - PhSaveWindowPlacementToSetting(L"MainWindowPosition", L"MainWindowSize", PhMainWndHandle); - PhMwpSaveWindowState(); - - if (PhSettingsFileName) - PhSaveSettings(PhSettingsFileName->Buffer); -} - -VOID PhMwpSaveWindowState( - VOID - ) -{ - WINDOWPLACEMENT placement = { sizeof(placement) }; - - GetWindowPlacement(PhMainWndHandle, &placement); - - if (placement.showCmd == SW_NORMAL) - PhSetIntegerSetting(L"MainWindowState", SW_NORMAL); - else if (placement.showCmd == SW_MAXIMIZE) - PhSetIntegerSetting(L"MainWindowState", SW_MAXIMIZE); -} - -VOID PhLoadDbgHelpFromPath( - _In_ PWSTR DbgHelpPath - ) -{ - HMODULE dbghelpModule; - - if (dbghelpModule = LoadLibrary(DbgHelpPath)) - { - PPH_STRING fullDbghelpPath; - ULONG indexOfFileName; - PH_STRINGREF dbghelpFolder; - PPH_STRING symsrvPath; - - fullDbghelpPath = PhGetDllFileName(dbghelpModule, &indexOfFileName); - - if (fullDbghelpPath) - { - if (indexOfFileName != 0) - { - static PH_STRINGREF symsrvString = PH_STRINGREF_INIT(L"\\symsrv.dll"); - - dbghelpFolder.Buffer = fullDbghelpPath->Buffer; - dbghelpFolder.Length = indexOfFileName * sizeof(WCHAR); - - symsrvPath = PhConcatStringRef2(&dbghelpFolder, &symsrvString); - LoadLibrary(symsrvPath->Buffer); - PhDereferenceObject(symsrvPath); - } - - PhDereferenceObject(fullDbghelpPath); - } - } - else - { - dbghelpModule = LoadLibrary(L"dbghelp.dll"); - } - - PhSymbolProviderCompleteInitialization(dbghelpModule); -} - -VOID PhMwpSymInitHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_STRING dbghelpPath; - - dbghelpPath = PhGetStringSetting(L"DbgHelpPath"); - PhLoadDbgHelpFromPath(dbghelpPath->Buffer); - PhDereferenceObject(dbghelpPath); -} - -VOID PhMwpUpdateLayoutPadding( - VOID - ) -{ - PH_LAYOUT_PADDING_DATA data; - - memset(&data, 0, sizeof(PH_LAYOUT_PADDING_DATA)); - PhInvokeCallback(&LayoutPaddingCallback, &data); - - LayoutPadding = data.Padding; -} - -VOID PhMwpApplyLayoutPadding( - _Inout_ PRECT Rect, - _In_ PRECT Padding - ) -{ - Rect->left += Padding->left; - Rect->top += Padding->top; - Rect->right -= Padding->right; - Rect->bottom -= Padding->bottom; -} - -VOID PhMwpLayout( - _Inout_ HDWP *DeferHandle - ) -{ - RECT rect; - - // Resize the tab control. - // Don't defer the resize. The tab control doesn't repaint properly. - - if (!LayoutPaddingValid) - { - PhMwpUpdateLayoutPadding(); - LayoutPaddingValid = TRUE; - } - - GetClientRect(PhMainWndHandle, &rect); - PhMwpApplyLayoutPadding(&rect, &LayoutPadding); - - SetWindowPos(TabControlHandle, NULL, - rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER); - UpdateWindow(TabControlHandle); - - PhMwpLayoutTabControl(DeferHandle); -} - -VOID PhMwpSetupComputerMenu( - _In_ PPH_EMENU_ITEM Root - ) -{ - PPH_EMENU_ITEM menuItem; - - if (WindowsVersion < WINDOWS_8) - { - if (menuItem = PhFindEMenuItem(Root, PH_EMENU_FIND_DESCEND, NULL, ID_COMPUTER_RESTARTBOOTOPTIONS)) - PhDestroyEMenuItem(menuItem); - if (menuItem = PhFindEMenuItem(Root, PH_EMENU_FIND_DESCEND, NULL, ID_COMPUTER_SHUTDOWNHYBRID)) - PhDestroyEMenuItem(menuItem); - } -} - -BOOLEAN PhMwpExecuteComputerCommand( - _In_ ULONG Id - ) -{ - switch (Id) - { - case ID_COMPUTER_LOCK: - PhUiLockComputer(PhMainWndHandle); - return TRUE; - case ID_COMPUTER_LOGOFF: - PhUiLogoffComputer(PhMainWndHandle); - return TRUE; - case ID_COMPUTER_SLEEP: - PhUiSleepComputer(PhMainWndHandle); - return TRUE; - case ID_COMPUTER_HIBERNATE: - PhUiHibernateComputer(PhMainWndHandle); - return TRUE; - case ID_COMPUTER_RESTART: - PhUiRestartComputer(PhMainWndHandle, 0); - return TRUE; - case ID_COMPUTER_RESTARTBOOTOPTIONS: - PhUiRestartComputer(PhMainWndHandle, EWX_BOOTOPTIONS); - return TRUE; - case ID_COMPUTER_SHUTDOWN: - PhUiShutdownComputer(PhMainWndHandle, 0); - return TRUE; - case ID_COMPUTER_SHUTDOWNHYBRID: - PhUiShutdownComputer(PhMainWndHandle, EWX_HYBRID_SHUTDOWN); - return TRUE; - } - - return FALSE; -} - -VOID PhMwpActivateWindow( - _In_ BOOLEAN Toggle - ) -{ - if (IsIconic(PhMainWndHandle)) - { - ShowWindow(PhMainWndHandle, SW_RESTORE); - SetForegroundWindow(PhMainWndHandle); - } - else if (IsWindowVisible(PhMainWndHandle)) - { - if (Toggle) - ShowWindow(PhMainWndHandle, SW_HIDE); - else - SetForegroundWindow(PhMainWndHandle); - } - else - { - ShowWindow(PhMainWndHandle, SW_SHOW); - SetForegroundWindow(PhMainWndHandle); - } -} - -VOID PhMwpInitializeMainMenu( - _In_ HMENU Menu - ) -{ - MENUINFO menuInfo; - ULONG i; - - menuInfo.cbSize = sizeof(MENUINFO); - menuInfo.fMask = MIM_STYLE; - menuInfo.dwStyle = MNS_NOTIFYBYPOS; - SetMenuInfo(Menu, &menuInfo); - - for (i = 0; i < sizeof(SubMenuHandles) / sizeof(HMENU); i++) - { - SubMenuHandles[i] = GetSubMenu(PhMainWndMenuHandle, i); - } -} - -VOID PhMwpDispatchMenuCommand( - _In_ HMENU MenuHandle, - _In_ ULONG ItemIndex, - _In_ ULONG ItemId, - _In_ ULONG_PTR ItemData - ) -{ - switch (ItemId) - { - case ID_PLUGIN_MENU_ITEM: - { - PPH_EMENU_ITEM menuItem; - PH_PLUGIN_MENU_INFORMATION menuInfo; - - menuItem = (PPH_EMENU_ITEM)ItemData; - - if (menuItem) - { - PhPluginInitializeMenuInfo(&menuInfo, NULL, PhMainWndHandle, 0); - PhPluginTriggerEMenuItem(&menuInfo, menuItem); - } - - return; - } - break; - case ID_TRAYICONS_REGISTERED: - { - PPH_EMENU_ITEM menuItem; - - menuItem = (PPH_EMENU_ITEM)ItemData; - - if (menuItem) - { - PPH_NF_ICON icon; - - icon = menuItem->Context; - PhNfSetVisibleIcon(icon->IconId, !PhNfTestIconMask(icon->IconId)); - } - - return; - } - break; - case ID_USER_CONNECT: - case ID_USER_DISCONNECT: - case ID_USER_LOGOFF: - case ID_USER_REMOTECONTROL: - case ID_USER_SENDMESSAGE: - case ID_USER_PROPERTIES: - { - SelectedUserSessionId = (ULONG)ItemData; - } - break; - } - - SendMessage(PhMainWndHandle, WM_COMMAND, ItemId, 0); -} - -ULONG_PTR PhMwpLegacyAddPluginMenuItem( - _In_ PPH_ADD_MENU_ITEM AddMenuItem - ) -{ - PPH_ADD_MENU_ITEM addMenuItem; - PPH_PLUGIN_MENU_ITEM pluginMenuItem; - - if (!LegacyAddMenuItemList) - LegacyAddMenuItemList = PhCreateList(8); - - addMenuItem = PhAllocateCopy(AddMenuItem, sizeof(PH_ADD_MENU_ITEM)); - PhAddItemList(LegacyAddMenuItemList, addMenuItem); - - pluginMenuItem = PhAllocate(sizeof(PH_PLUGIN_MENU_ITEM)); - memset(pluginMenuItem, 0, sizeof(PH_PLUGIN_MENU_ITEM)); - pluginMenuItem->Plugin = AddMenuItem->Plugin; - pluginMenuItem->Id = AddMenuItem->Id; - pluginMenuItem->Context = AddMenuItem->Context; - - addMenuItem->Context = pluginMenuItem; - - return TRUE; -} - -HBITMAP PhMwpGetShieldBitmap( - VOID - ) -{ - static HBITMAP shieldBitmap = NULL; - - if (!shieldBitmap) - { - HICON shieldIcon; - - if (shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0)) - { - shieldBitmap = PhIconToBitmap(shieldIcon, PhSmallIconSize.X, PhSmallIconSize.Y); - DestroyIcon(shieldIcon); - } - } - - return shieldBitmap; -} - -VOID PhMwpInitializeSubMenu( - _In_ PPH_EMENU Menu, - _In_ ULONG Index - ) -{ - PPH_EMENU_ITEM menuItem; - - if (Index == 0) // Hacker - { - // Fix some menu items. - if (PhGetOwnTokenAttributes().Elevated) - { - if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_RUNASADMINISTRATOR)) - PhDestroyEMenuItem(menuItem); - if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_SHOWDETAILSFORALLPROCESSES)) - PhDestroyEMenuItem(menuItem); - } - else - { - HBITMAP shieldBitmap; - - if (shieldBitmap = PhMwpGetShieldBitmap()) - { - if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_SHOWDETAILSFORALLPROCESSES)) - menuItem->Bitmap = shieldBitmap; - } - } - - // Fix up the Computer menu. - PhMwpSetupComputerMenu(Menu); - } - else if (Index == 1) // View - { - PPH_EMENU_ITEM trayIconsMenuItem; - ULONG i; - PPH_EMENU_ITEM menuItem; - ULONG id; - ULONG placeholderIndex; - - trayIconsMenuItem = PhMwpFindTrayIconsMenuItem(Menu); - - if (trayIconsMenuItem) - { - ULONG maximum; - PPH_NF_ICON icon; - - // Add menu items for the registered tray icons. - - id = PH_ICON_DEFAULT_MAXIMUM; - maximum = PhNfGetMaximumIconId(); - - for (; id != maximum; id <<= 1) - { - if (icon = PhNfGetIconById(id)) - { - PhInsertEMenuItem(trayIconsMenuItem, PhCreateEMenuItem(0, ID_TRAYICONS_REGISTERED, icon->Text, NULL, icon), -1); - } - } - - // Update the text and check marks on the menu items. - - for (i = 0; i < trayIconsMenuItem->Items->Count; i++) - { - menuItem = trayIconsMenuItem->Items->Items[i]; - - id = -1; - icon = NULL; - - switch (menuItem->Id) - { - case ID_TRAYICONS_CPUHISTORY: - id = PH_ICON_CPU_HISTORY; - break; - case ID_TRAYICONS_IOHISTORY: - id = PH_ICON_IO_HISTORY; - break; - case ID_TRAYICONS_COMMITHISTORY: - id = PH_ICON_COMMIT_HISTORY; - break; - case ID_TRAYICONS_PHYSICALMEMORYHISTORY: - id = PH_ICON_PHYSICAL_HISTORY; - break; - case ID_TRAYICONS_CPUUSAGE: - id = PH_ICON_CPU_USAGE; - break; - case ID_TRAYICONS_REGISTERED: - icon = menuItem->Context; - id = icon->IconId; - break; - } - - if (id != -1) - { - if (PhNfTestIconMask(id)) - menuItem->Flags |= PH_EMENU_CHECKED; - - if (icon && (icon->Flags & PH_NF_ICON_UNAVAILABLE)) - { - PPH_STRING newText; - - newText = PhaConcatStrings2(icon->Text, L" (Unavailable)"); - PhModifyEMenuItem(menuItem, PH_EMENU_MODIFY_TEXT, PH_EMENU_TEXT_OWNED, - PhAllocateCopy(newText->Buffer, newText->Length + sizeof(WCHAR)), NULL); - } - } - } - } - - if (menuItem = PhFindEMenuItemEx(Menu, 0, NULL, ID_VIEW_SECTIONPLACEHOLDER, NULL, &placeholderIndex)) - { - PhDestroyEMenuItem(menuItem); - PhMwpInitializeSectionMenuItems(Menu, placeholderIndex); - } - - if (AlwaysOnTop && (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_VIEW_ALWAYSONTOP))) - menuItem->Flags |= PH_EMENU_CHECKED; - - id = PH_OPACITY_TO_ID(PhGetIntegerSetting(L"MainWindowOpacity")); - - if (menuItem = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, NULL, id)) - menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; - - switch (PhGetIntegerSetting(L"UpdateInterval")) - { - case 500: - id = ID_UPDATEINTERVAL_FAST; - break; - case 1000: - id = ID_UPDATEINTERVAL_NORMAL; - break; - case 2000: - id = ID_UPDATEINTERVAL_BELOWNORMAL; - break; - case 5000: - id = ID_UPDATEINTERVAL_SLOW; - break; - case 10000: - id = ID_UPDATEINTERVAL_VERYSLOW; - break; - default: - id = -1; - break; - } - - if (id != -1 && (menuItem = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, NULL, id))) - menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; - - if (PhMwpUpdateAutomatically && (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_VIEW_UPDATEAUTOMATICALLY))) - menuItem->Flags |= PH_EMENU_CHECKED; - } - else if (Index == 2) // Tools - { -#ifdef _WIN64 - if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_TOOLS_HIDDENPROCESSES)) - PhDestroyEMenuItem(menuItem); -#endif - - // Windows 8 Task Manager requires elevation. - if (WindowsVersion >= WINDOWS_8 && !PhGetOwnTokenAttributes().Elevated) - { - HBITMAP shieldBitmap; - - if (shieldBitmap = PhMwpGetShieldBitmap()) - { - if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_TOOLS_STARTTASKMANAGER)) - menuItem->Bitmap = shieldBitmap; - } - } - } - - if (LegacyAddMenuItemList) - { - ULONG i; - PPH_ADD_MENU_ITEM addMenuItem; - - for (i = 0; i < LegacyAddMenuItemList->Count; i++) - { - addMenuItem = LegacyAddMenuItemList->Items[i]; - - if (addMenuItem->Location == Index) - { - ULONG insertIndex; - - if (addMenuItem->InsertAfter) - { - for (insertIndex = 0; insertIndex < Menu->Items->Count; insertIndex++) - { - menuItem = Menu->Items->Items[insertIndex]; - - if (!(menuItem->Flags & PH_EMENU_SEPARATOR) && (PhCompareUnicodeStringZIgnoreMenuPrefix( - addMenuItem->InsertAfter, - menuItem->Text, - TRUE, - TRUE - ) == 0)) - { - insertIndex++; - break; - } - } - } - else - { - insertIndex = 0; - } - - if (addMenuItem->Text[0] == '-' && addMenuItem->Text[1] == 0) - PhInsertEMenuItem(Menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), insertIndex); - else - PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PLUGIN_MENU_ITEM, addMenuItem->Text, NULL, addMenuItem->Context), insertIndex); - } - } - } -} - -PPH_EMENU_ITEM PhMwpFindTrayIconsMenuItem( - _In_ PPH_EMENU Menu - ) -{ - ULONG i; - PPH_EMENU_ITEM menuItem; - - for (i = 0; i < Menu->Items->Count; i++) - { - menuItem = Menu->Items->Items[i]; - - if (PhFindEMenuItem(menuItem, 0, NULL, ID_TRAYICONS_CPUHISTORY)) - return menuItem; - } - - return NULL; -} - -VOID PhMwpInitializeSectionMenuItems( - _In_ PPH_EMENU Menu, - _In_ ULONG StartIndex - ) -{ - if (CurrentPage) - { - PH_MAIN_TAB_PAGE_MENU_INFORMATION menuInfo; - - menuInfo.Menu = Menu; - menuInfo.StartIndex = StartIndex; - - if (!CurrentPage->Callback(CurrentPage, MainTabPageInitializeSectionMenuItems, &menuInfo, NULL)) - { - // Remove the extra separator. - PhRemoveEMenuItem(Menu, NULL, StartIndex); - } - } -} - -VOID PhMwpLayoutTabControl( - _Inout_ HDWP *DeferHandle - ) -{ - RECT rect; - - if (!LayoutPaddingValid) - { - PhMwpUpdateLayoutPadding(); - LayoutPaddingValid = TRUE; - } - - GetClientRect(PhMainWndHandle, &rect); - PhMwpApplyLayoutPadding(&rect, &LayoutPadding); - TabCtrl_AdjustRect(TabControlHandle, FALSE, &rect); - - if (CurrentPage && CurrentPage->WindowHandle) - { - *DeferHandle = DeferWindowPos(*DeferHandle, CurrentPage->WindowHandle, NULL, - rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER); - } -} - -VOID PhMwpNotifyTabControl( - _In_ NMHDR *Header - ) -{ - if (Header->code == TCN_SELCHANGING) - { - OldTabIndex = TabCtrl_GetCurSel(TabControlHandle); - } - else if (Header->code == TCN_SELCHANGE) - { - PhMwpSelectionChangedTabControl(OldTabIndex); - } -} - -VOID PhMwpSelectionChangedTabControl( - _In_ ULONG OldIndex - ) -{ - ULONG selectedIndex; - HDWP deferHandle; - ULONG i; - - selectedIndex = TabCtrl_GetCurSel(TabControlHandle); - - if (selectedIndex == OldIndex) - return; - - deferHandle = BeginDeferWindowPos(3); - - for (i = 0; i < PageList->Count; i++) - { - PPH_MAIN_TAB_PAGE page = PageList->Items[i]; - - page->Selected = page->Index == selectedIndex; - - if (page->Index == selectedIndex) - { - CurrentPage = page; - - // Create the tab page window if it doesn't exist. - if (!page->WindowHandle && !page->CreateWindowCalled) - { - if (page->Callback(page, MainTabPageCreateWindow, &page->WindowHandle, NULL)) - page->CreateWindowCalled = TRUE; - - if (page->WindowHandle) - BringWindowToTop(page->WindowHandle); - if (CurrentCustomFont) - page->Callback(page, MainTabPageFontChanged, CurrentCustomFont, NULL); - } - - page->Callback(page, MainTabPageSelected, (PVOID)TRUE, NULL); - - if (page->WindowHandle) - { - deferHandle = DeferWindowPos(deferHandle, page->WindowHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); - SetFocus(page->WindowHandle); - } - } - else if (page->Index == OldIndex) - { - page->Callback(page, MainTabPageSelected, (PVOID)FALSE, NULL); - - if (page->WindowHandle) - { - deferHandle = DeferWindowPos(deferHandle, page->WindowHandle, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW_ONLY); - } - } - } - - PhMwpLayoutTabControl(&deferHandle); - - EndDeferWindowPos(deferHandle); - - if (PhPluginsEnabled) - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMainWindowTabChanged), IntToPtr(selectedIndex)); -} - -PPH_MAIN_TAB_PAGE PhMwpCreatePage( - _In_ PPH_MAIN_TAB_PAGE Template - ) -{ - PPH_MAIN_TAB_PAGE page; - PPH_STRING name; - HDWP deferHandle; - - page = PhAllocate(sizeof(PH_MAIN_TAB_PAGE)); - memset(page, 0, sizeof(PH_MAIN_TAB_PAGE)); - - page->Name = Template->Name; - page->Flags = Template->Flags; - page->Callback = Template->Callback; - page->Context = Template->Context; - - PhAddItemList(PageList, page); - - name = PhCreateString2(&page->Name); - page->Index = PhAddTabControlTab(TabControlHandle, MAXINT, name->Buffer); - PhDereferenceObject(name); - - page->Callback(page, MainTabPageCreate, NULL, NULL); - - // The tab control might need multiple lines, so we need to refresh the layout. - deferHandle = BeginDeferWindowPos(1); - PhMwpLayoutTabControl(&deferHandle); - EndDeferWindowPos(deferHandle); - - return page; -} - -VOID PhMwpSelectPage( - _In_ ULONG Index - ) -{ - INT oldIndex; - - oldIndex = TabCtrl_GetCurSel(TabControlHandle); - TabCtrl_SetCurSel(TabControlHandle, Index); - PhMwpSelectionChangedTabControl(oldIndex); -} - -PPH_MAIN_TAB_PAGE PhMwpFindPage( - _In_ PPH_STRINGREF Name - ) -{ - ULONG i; - - for (i = 0; i < PageList->Count; i++) - { - PPH_MAIN_TAB_PAGE page = PageList->Items[i]; - - if (PhEqualStringRef(&page->Name, Name, TRUE)) - return page; - } - - return NULL; -} - -PPH_MAIN_TAB_PAGE PhMwpCreateInternalPage( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MAIN_TAB_PAGE_CALLBACK Callback - ) -{ - PH_MAIN_TAB_PAGE page; - - memset(&page, 0, sizeof(PH_MAIN_TAB_PAGE)); - PhInitializeStringRef(&page.Name, Name); - page.Flags = Flags; - page.Callback = Callback; - - return PhMwpCreatePage(&page); -} - -VOID PhMwpNotifyAllPages( - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - ULONG i; - PPH_MAIN_TAB_PAGE page; - - for (i = 0; i < PageList->Count; i++) - { - page = PageList->Items[i]; - page->Callback(page, Message, Parameter1, Parameter2); - } -} - -static int __cdecl IconProcessesCpuUsageCompare( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_PROCESS_ITEM processItem1 = *(PPH_PROCESS_ITEM *)elem1; - PPH_PROCESS_ITEM processItem2 = *(PPH_PROCESS_ITEM *)elem2; - - return -singlecmp(processItem1->CpuUsage, processItem2->CpuUsage); -} - -static int __cdecl IconProcessesNameCompare( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_PROCESS_ITEM processItem1 = *(PPH_PROCESS_ITEM *)elem1; - PPH_PROCESS_ITEM processItem2 = *(PPH_PROCESS_ITEM *)elem2; - - return PhCompareString(processItem1->ProcessName, processItem2->ProcessName, TRUE); -} - -VOID PhAddMiniProcessMenuItems( - _Inout_ struct _PH_EMENU_ITEM *Menu, - _In_ HANDLE ProcessId - ) -{ - PPH_EMENU_ITEM priorityMenu; - PPH_EMENU_ITEM ioPriorityMenu = NULL; - PPH_PROCESS_ITEM processItem; - BOOLEAN isSuspended = FALSE; - BOOLEAN isPartiallySuspended = TRUE; - - // Priority - - priorityMenu = PhCreateEMenuItem(0, 0, L"Priority", NULL, ProcessId); - - PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_REALTIME, L"Real time", NULL, ProcessId), -1); - PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_HIGH, L"High", NULL, ProcessId), -1); - PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_ABOVENORMAL, L"Above normal", NULL, ProcessId), -1); - PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_NORMAL, L"Normal", NULL, ProcessId), -1); - PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_BELOWNORMAL, L"Below normal", NULL, ProcessId), -1); - PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_IDLE, L"Idle", NULL, ProcessId), -1); - - // I/O priority - - if (WindowsVersion >= WINDOWS_VISTA) - { - ioPriorityMenu = PhCreateEMenuItem(0, 0, L"I/O priority", NULL, ProcessId); - - PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_HIGH, L"High", NULL, ProcessId), -1); - PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_NORMAL, L"Normal", NULL, ProcessId), -1); - PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_LOW, L"Low", NULL, ProcessId), -1); - PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_VERYLOW, L"Very low", NULL, ProcessId), -1); - } - - // Menu - - PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_TERMINATE, L"T&erminate", NULL, ProcessId), -1); - - if (processItem = PhReferenceProcessItem(ProcessId)) - { - isSuspended = (BOOLEAN)processItem->IsSuspended; - isPartiallySuspended = (BOOLEAN)processItem->IsPartiallySuspended; - PhDereferenceObject(processItem); - } - - if (!isSuspended) - PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_SUSPEND, L"&Suspend", NULL, ProcessId), -1); - if (isPartiallySuspended) - PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_RESUME, L"Res&ume", NULL, ProcessId), -1); - - PhInsertEMenuItem(Menu, priorityMenu, -1); - - if (ioPriorityMenu) - PhInsertEMenuItem(Menu, ioPriorityMenu, -1); - - PhMwpSetProcessMenuPriorityChecks(Menu, ProcessId, TRUE, TRUE, FALSE); - - PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_PROPERTIES, L"P&roperties", NULL, ProcessId), -1); -} - -BOOLEAN PhHandleMiniProcessMenuItem( - _Inout_ struct _PH_EMENU_ITEM *MenuItem - ) -{ - switch (MenuItem->Id) - { - case ID_PROCESS_TERMINATE: - case ID_PROCESS_SUSPEND: - case ID_PROCESS_RESUME: - case ID_PROCESS_PROPERTIES: - { - HANDLE processId = MenuItem->Context; - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(processId)) - { - switch (MenuItem->Id) - { - case ID_PROCESS_TERMINATE: - PhUiTerminateProcesses(PhMainWndHandle, &processItem, 1); - break; - case ID_PROCESS_SUSPEND: - PhUiSuspendProcesses(PhMainWndHandle, &processItem, 1); - break; - case ID_PROCESS_RESUME: - PhUiResumeProcesses(PhMainWndHandle, &processItem, 1); - break; - case ID_PROCESS_PROPERTIES: - ProcessHacker_ShowProcessProperties(PhMainWndHandle, processItem); - break; - } - - PhDereferenceObject(processItem); - } - else - { - PhShowError(PhMainWndHandle, L"The process does not exist."); - } - } - break; - case ID_PRIORITY_REALTIME: - case ID_PRIORITY_HIGH: - case ID_PRIORITY_ABOVENORMAL: - case ID_PRIORITY_NORMAL: - case ID_PRIORITY_BELOWNORMAL: - case ID_PRIORITY_IDLE: - { - HANDLE processId = MenuItem->Context; - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(processId)) - { - PhMwpExecuteProcessPriorityCommand(MenuItem->Id, &processItem, 1); - PhDereferenceObject(processItem); - } - else - { - PhShowError(PhMainWndHandle, L"The process does not exist."); - } - } - break; - case ID_IOPRIORITY_HIGH: - case ID_IOPRIORITY_NORMAL: - case ID_IOPRIORITY_LOW: - case ID_IOPRIORITY_VERYLOW: - { - HANDLE processId = MenuItem->Context; - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(processId)) - { - PhMwpExecuteProcessIoPriorityCommand(MenuItem->Id, &processItem, 1); - PhDereferenceObject(processItem); - } - else - { - PhShowError(PhMainWndHandle, L"The process does not exist."); - } - } - break; - } - - return FALSE; -} - -VOID PhMwpAddIconProcesses( - _In_ PPH_EMENU_ITEM Menu, - _In_ ULONG NumberOfProcesses - ) -{ - ULONG i; - PPH_PROCESS_ITEM *processItems; - ULONG numberOfProcessItems; - PPH_LIST processList; - PPH_PROCESS_ITEM processItem; - - PhEnumProcessItems(&processItems, &numberOfProcessItems); - processList = PhCreateList(numberOfProcessItems); - PhAddItemsList(processList, processItems, numberOfProcessItems); - - // Remove non-real processes. - for (i = 0; i < processList->Count; i++) - { - processItem = processList->Items[i]; - - if (!PH_IS_REAL_PROCESS_ID(processItem->ProcessId)) - { - PhRemoveItemList(processList, i); - i--; - } - } - - // Remove processes with zero CPU usage and those running as other users. - for (i = 0; i < processList->Count && processList->Count > NumberOfProcesses; i++) - { - processItem = processList->Items[i]; - - if ( - processItem->CpuUsage == 0 || - !processItem->UserName || - (PhCurrentUserName && !PhEqualString(processItem->UserName, PhCurrentUserName, TRUE)) - ) - { - PhRemoveItemList(processList, i); - i--; - } - } - - // Sort the processes by CPU usage and remove the extra processes at the end of the list. - qsort(processList->Items, processList->Count, sizeof(PVOID), IconProcessesCpuUsageCompare); - - if (processList->Count > NumberOfProcesses) - { - PhRemoveItemsList(processList, NumberOfProcesses, processList->Count - NumberOfProcesses); - } - - // Lastly, sort by name. - qsort(processList->Items, processList->Count, sizeof(PVOID), IconProcessesNameCompare); - - // Delete all menu items. - PhRemoveAllEMenuItems(Menu); - - // Add the processes. - - for (i = 0; i < processList->Count; i++) - { - PPH_EMENU_ITEM subMenu; - HBITMAP iconBitmap; - CLIENT_ID clientId; - PPH_STRING clientIdName; - PPH_STRING escapedName; - - processItem = processList->Items[i]; - - // Process - - clientId.UniqueProcess = processItem->ProcessId; - clientId.UniqueThread = NULL; - - clientIdName = PH_AUTO(PhGetClientIdName(&clientId)); - escapedName = PH_AUTO(PhEscapeStringForMenuPrefix(&clientIdName->sr)); - - subMenu = PhCreateEMenuItem( - 0, - 0, - escapedName->Buffer, - NULL, - processItem->ProcessId - ); - - if (processItem->SmallIcon) - { - iconBitmap = PhIconToBitmap(processItem->SmallIcon, PhSmallIconSize.X, PhSmallIconSize.Y); - } - else - { - HICON icon; - - PhGetStockApplicationIcon(&icon, NULL); - iconBitmap = PhIconToBitmap(icon, PhSmallIconSize.X, PhSmallIconSize.Y); - } - - subMenu->Bitmap = iconBitmap; - subMenu->Flags |= PH_EMENU_BITMAP_OWNED; // automatically destroy the bitmap when necessary - - PhAddMiniProcessMenuItems(subMenu, processItem->ProcessId); - PhInsertEMenuItem(Menu, subMenu, -1); - } - - PhDereferenceObject(processList); - PhDereferenceObjects(processItems, numberOfProcessItems); - PhFree(processItems); -} - -VOID PhShowIconContextMenu( - _In_ POINT Location - ) -{ - PPH_EMENU menu; - PPH_EMENU_ITEM item; - PH_PLUGIN_MENU_INFORMATION menuInfo; - ULONG numberOfProcesses; - ULONG id; - ULONG i; - - // This function seems to be called recursively under some circumstances. - // To reproduce: - // 1. Hold right mouse button on tray icon, then left click. - // 2. Make the menu disappear by clicking on the menu then clicking somewhere else. - // So, don't store any global state or bad things will happen. - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_ICON), 0); - - // Check the Notifications menu items. - for (i = PH_NOTIFY_MINIMUM; i != PH_NOTIFY_MAXIMUM; i <<= 1) - { - if (PhMwpNotifyIconNotifyMask & i) - { - switch (i) - { - case PH_NOTIFY_PROCESS_CREATE: - id = ID_NOTIFICATIONS_NEWPROCESSES; - break; - case PH_NOTIFY_PROCESS_DELETE: - id = ID_NOTIFICATIONS_TERMINATEDPROCESSES; - break; - case PH_NOTIFY_SERVICE_CREATE: - id = ID_NOTIFICATIONS_NEWSERVICES; - break; - case PH_NOTIFY_SERVICE_DELETE: - id = ID_NOTIFICATIONS_DELETEDSERVICES; - break; - case PH_NOTIFY_SERVICE_START: - id = ID_NOTIFICATIONS_STARTEDSERVICES; - break; - case PH_NOTIFY_SERVICE_STOP: - id = ID_NOTIFICATIONS_STOPPEDSERVICES; - break; - } - - PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - } - - // Add processes to the menu. - - numberOfProcesses = PhGetIntegerSetting(L"IconProcesses"); - item = PhFindEMenuItem(menu, 0, L"Processes", 0); - - if (item) - PhMwpAddIconProcesses(item, numberOfProcesses); - - // Fix up the Computer menu. - PhMwpSetupComputerMenu(menu); - - // Give plugins a chance to modify the menu. - - if (PhPluginsEnabled) - { - PhPluginInitializeMenuInfo(&menuInfo, menu, PhMainWndHandle, 0); - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackIconMenuInitializing), &menuInfo); - } - - SetForegroundWindow(PhMainWndHandle); // window must be foregrounded so menu will disappear properly - item = PhShowEMenu( - menu, - PhMainWndHandle, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - Location.x, - Location.y - ); - - if (item) - { - BOOLEAN handled = FALSE; - - if (PhPluginsEnabled && !handled) - handled = PhPluginTriggerEMenuItem(&menuInfo, item); - - if (!handled) - handled = PhHandleMiniProcessMenuItem(item); - - if (!handled) - handled = PhMwpExecuteComputerCommand(item->Id); - - if (!handled) - { - switch (item->Id) - { - case ID_ICON_SHOWHIDEPROCESSHACKER: - SendMessage(PhMainWndHandle, WM_PH_TOGGLE_VISIBLE, 0, 0); - break; - case ID_ICON_SYSTEMINFORMATION: - SendMessage(PhMainWndHandle, WM_COMMAND, ID_VIEW_SYSTEMINFORMATION, 0); - break; - case ID_NOTIFICATIONS_ENABLEALL: - PhMwpNotifyIconNotifyMask |= PH_NOTIFY_VALID_MASK; - break; - case ID_NOTIFICATIONS_DISABLEALL: - PhMwpNotifyIconNotifyMask &= ~PH_NOTIFY_VALID_MASK; - break; - case ID_NOTIFICATIONS_NEWPROCESSES: - case ID_NOTIFICATIONS_TERMINATEDPROCESSES: - case ID_NOTIFICATIONS_NEWSERVICES: - case ID_NOTIFICATIONS_STARTEDSERVICES: - case ID_NOTIFICATIONS_STOPPEDSERVICES: - case ID_NOTIFICATIONS_DELETEDSERVICES: - { - ULONG bit; - - switch (item->Id) - { - case ID_NOTIFICATIONS_NEWPROCESSES: - bit = PH_NOTIFY_PROCESS_CREATE; - break; - case ID_NOTIFICATIONS_TERMINATEDPROCESSES: - bit = PH_NOTIFY_PROCESS_DELETE; - break; - case ID_NOTIFICATIONS_NEWSERVICES: - bit = PH_NOTIFY_SERVICE_CREATE; - break; - case ID_NOTIFICATIONS_STARTEDSERVICES: - bit = PH_NOTIFY_SERVICE_START; - break; - case ID_NOTIFICATIONS_STOPPEDSERVICES: - bit = PH_NOTIFY_SERVICE_STOP; - break; - case ID_NOTIFICATIONS_DELETEDSERVICES: - bit = PH_NOTIFY_SERVICE_DELETE; - break; - } - - PhMwpNotifyIconNotifyMask ^= bit; - } - break; - case ID_ICON_EXIT: - SendMessage(PhMainWndHandle, WM_COMMAND, ID_HACKER_EXIT, 0); - break; - } - } - } - - PhDestroyEMenu(menu); -} - -VOID PhShowIconNotification( - _In_ PWSTR Title, - _In_ PWSTR Text, - _In_ ULONG Flags - ) -{ - PhNfShowBalloonTip(0, Title, Text, 10, Flags); -} - -VOID PhShowDetailsForIconNotification( - VOID - ) -{ - switch (PhMwpLastNotificationType) - { - case PH_NOTIFY_PROCESS_CREATE: - { - PPH_PROCESS_NODE processNode; - - if (processNode = PhFindProcessNode(PhMwpLastNotificationDetails.ProcessId)) - { - ProcessHacker_SelectTabPage(PhMainWndHandle, PhMwpProcessesPage->Index); - ProcessHacker_SelectProcessNode(PhMainWndHandle, processNode); - ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); - } - } - break; - case PH_NOTIFY_SERVICE_CREATE: - case PH_NOTIFY_SERVICE_START: - case PH_NOTIFY_SERVICE_STOP: - { - PPH_SERVICE_ITEM serviceItem; - - if (PhMwpLastNotificationDetails.ServiceName && - (serviceItem = PhReferenceServiceItem(PhMwpLastNotificationDetails.ServiceName->Buffer))) - { - ProcessHacker_SelectTabPage(PhMainWndHandle, PhMwpServicesPage->Index); - ProcessHacker_SelectServiceItem(PhMainWndHandle, serviceItem); - ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); - - PhDereferenceObject(serviceItem); - } - } - break; - } -} - -VOID PhMwpClearLastNotificationDetails( - VOID - ) -{ - if (PhMwpLastNotificationType & - (PH_NOTIFY_SERVICE_CREATE | PH_NOTIFY_SERVICE_DELETE | PH_NOTIFY_SERVICE_START | PH_NOTIFY_SERVICE_STOP)) - { - PhClearReference(&PhMwpLastNotificationDetails.ServiceName); - } - - PhMwpLastNotificationType = 0; - memset(&PhMwpLastNotificationDetails, 0, sizeof(PhMwpLastNotificationDetails)); -} - -BOOLEAN PhMwpPluginNotifyEvent( - _In_ ULONG Type, - _In_ PVOID Parameter - ) -{ - PH_PLUGIN_NOTIFY_EVENT notifyEvent; - - notifyEvent.Type = Type; - notifyEvent.Handled = FALSE; - notifyEvent.Parameter = Parameter; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNotifyEvent), ¬ifyEvent); - - return notifyEvent.Handled; -} - -VOID PhMwpUpdateUsersMenu( - VOID - ) -{ - HMENU menu; - PSESSIONIDW sessions; - ULONG numberOfSessions; - ULONG i; - ULONG j; - MENUITEMINFO menuItemInfo = { sizeof(MENUITEMINFO) }; - - menu = SubMenuHandles[3]; - - // Delete all items in the Users menu. - while (DeleteMenu(menu, 0, MF_BYPOSITION)) ; - - if (WinStationEnumerateW(NULL, &sessions, &numberOfSessions)) - { - for (i = 0; i < numberOfSessions; i++) - { - HMENU userMenu; - PPH_STRING menuText; - PPH_STRING escapedMenuText; - WINSTATIONINFORMATION winStationInfo; - ULONG returnLength; - ULONG numberOfItems; - - if (!WinStationQueryInformationW( - NULL, - sessions[i].SessionId, - WinStationInformation, - &winStationInfo, - sizeof(WINSTATIONINFORMATION), - &returnLength - )) - { - winStationInfo.Domain[0] = 0; - winStationInfo.UserName[0] = 0; - } - - if (winStationInfo.Domain[0] == 0 || winStationInfo.UserName[0] == 0) - { - // Probably the Services or RDP-Tcp session. - continue; - } - - menuText = PhFormatString( - L"%u: %s\\%s", - sessions[i].SessionId, - winStationInfo.Domain, - winStationInfo.UserName - ); - escapedMenuText = PhEscapeStringForMenuPrefix(&menuText->sr); - PhDereferenceObject(menuText); - - userMenu = GetSubMenu(LoadMenu(PhInstanceHandle, MAKEINTRESOURCE(IDR_USER)), 0); - AppendMenu( - menu, - MF_STRING | MF_POPUP, - (UINT_PTR)userMenu, - escapedMenuText->Buffer - ); - - PhDereferenceObject(escapedMenuText); - - menuItemInfo.fMask = MIIM_DATA; - menuItemInfo.dwItemData = sessions[i].SessionId; - - numberOfItems = GetMenuItemCount(userMenu); - - if (numberOfItems != -1) - { - for (j = 0; j < numberOfItems; j++) - SetMenuItemInfo(userMenu, j, TRUE, &menuItemInfo); - } - } - - WinStationFreeMemory(sessions); - } - - DrawMenuBar(PhMainWndHandle); -} +/* + * Process Hacker - + * Main window + * + * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RUNAS_MODE_ADMIN 1 +#define RUNAS_MODE_LIMITED 2 + +PHAPPAPI HWND PhMainWndHandle = NULL; +BOOLEAN PhMainWndExiting = FALSE; +BOOLEAN PhMainWndEarlyExit = FALSE; +HMENU PhMainWndMenuHandle = NULL; + +PH_PROVIDER_REGISTRATION PhMwpProcessProviderRegistration; +PH_PROVIDER_REGISTRATION PhMwpServiceProviderRegistration; +PH_PROVIDER_REGISTRATION PhMwpNetworkProviderRegistration; +BOOLEAN PhMwpUpdateAutomatically = TRUE; + +ULONG PhMwpNotifyIconNotifyMask = 0; +ULONG PhMwpLastNotificationType = 0; +PH_MWP_NOTIFICATION_DETAILS PhMwpLastNotificationDetails; + +static BOOLEAN NeedsMaximize = FALSE; +static BOOLEAN AlwaysOnTop = FALSE; +static BOOLEAN DelayedLoadCompleted = FALSE; + +static PH_CALLBACK_DECLARE(LayoutPaddingCallback); +static RECT LayoutPadding = { 0, 0, 0, 0 }; +static BOOLEAN LayoutPaddingValid = TRUE; + +static HWND TabControlHandle = NULL; +static PPH_LIST PageList = NULL; +static PPH_MAIN_TAB_PAGE CurrentPage = NULL; +static INT OldTabIndex = 0; + +static HMENU SubMenuHandles[5]; +static PPH_EMENU SubMenuObjects[5]; + +static ULONG SelectedRunAsMode = ULONG_MAX; +static ULONG SelectedUserSessionId = ULONG_MAX; + +BOOLEAN PhMainWndInitialization( + _In_ INT ShowCommand + ) +{ + RTL_ATOM windowAtom; + PPH_STRING windowName; + PH_RECTANGLE windowRectangle; + + // Set FirstRun default settings. + + if (PhGetIntegerSetting(L"FirstRun")) + PhSetIntegerSetting(L"FirstRun", FALSE); + + // Initialize the window. + + if ((windowAtom = PhMwpInitializeWindowClass()) == INVALID_ATOM) + return FALSE; + + windowRectangle.Position = PhGetIntegerPairSetting(L"MainWindowPosition"); + windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MainWindowSize", TRUE).Pair; + + // Create the window title. + windowName = NULL; + + if (PhGetIntegerSetting(L"EnableWindowText")) + { + PH_STRING_BUILDER stringBuilder; + PPH_STRING currentUserName; + + PhInitializeStringBuilder(&stringBuilder, 100); + PhAppendStringBuilder2(&stringBuilder, PhApplicationName); + + if (currentUserName = PhGetSidFullName(PhGetOwnTokenAttributes().TokenSid, TRUE, NULL)) + { + PhAppendStringBuilder2(&stringBuilder, L" ["); + PhAppendStringBuilder(&stringBuilder, ¤tUserName->sr); + PhAppendCharStringBuilder(&stringBuilder, ']'); + if (KphIsConnected()) PhAppendCharStringBuilder(&stringBuilder, '+'); + PhDereferenceObject(currentUserName); + } + + if (PhGetOwnTokenAttributes().ElevationType == TokenElevationTypeFull) + PhAppendStringBuilder2(&stringBuilder, L" (Administrator)"); + + windowName = PhFinalStringBuilderString(&stringBuilder); + } + + // Create the window. + + PhMainWndHandle = CreateWindow( + MAKEINTATOM(windowAtom), + PhGetString(windowName), + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + windowRectangle.Left, + windowRectangle.Top, + windowRectangle.Width, + windowRectangle.Height, + NULL, + NULL, + PhInstanceHandle, + NULL + ); + PhClearReference(&windowName); + + if (!PhMainWndHandle) + return FALSE; + + if (PhGetIntegerSetting(L"EnableWindowText")) // HACK + { + SendMessage(PhMainWndHandle, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhMainWndHandle, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + } + + // TODO: plugin suppport -dmex + //PPH_EMENU menu; + //menu = PhCreateEMenu(); + //PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND), ULONG_MAX); + //PhMainWndMenuHandle = CreateMenu(); + //PhEMenuToHMenu2(PhMainWndMenuHandle, menu, 0, NULL); + //SetMenu(PhMainWndHandle, PhMainWndMenuHandle); + //PhMwpInitializeMainMenu(PhMainWndMenuHandle); + + // Load the main menu + + PhMainWndMenuHandle = PhLoadMenu(PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND)); + SetMenu(PhMainWndHandle, PhMainWndMenuHandle); + PhMwpInitializeMainMenu(PhMainWndMenuHandle); + + // Choose a more appropriate rectangle for the window. + + PhAdjustRectangleToWorkingArea(PhMainWndHandle, &windowRectangle); + MoveWindow( + PhMainWndHandle, + windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, + FALSE + ); + UpdateWindow(PhMainWndHandle); + + // Allow WM_PH_ACTIVATE to pass through UIPI. + ChangeWindowMessageFilterEx(PhMainWndHandle, WM_PH_ACTIVATE, MSGFLT_ADD, NULL); + + // Initialize child controls. + PhMwpInitializeControls(PhMainWndHandle); + + PhMwpOnSettingChange(); + + PhMwpLoadSettings(PhMainWndHandle); + PhLogInitialization(); + + PhInitializeWindowTheme(PhMainWndHandle, PhEnableThemeSupport); // HACK + + // Initialize the main providers. + PhMwpInitializeProviders(); + + // Queue delayed init functions. + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhMwpLoadStage1Worker, PhMainWndHandle); + + // Perform a layout. + PhMwpSelectionChangedTabControl(ULONG_MAX); + PhMwpOnSize(PhMainWndHandle); + + if ((PhStartupParameters.ShowHidden || PhGetIntegerSetting(L"StartHidden")) && PhNfIconsEnabled()) + ShowCommand = SW_HIDE; + if (PhStartupParameters.ShowVisible) + ShowCommand = SW_SHOW; + + if (PhGetIntegerSetting(L"MainWindowState") == SW_MAXIMIZE) + { + if (ShowCommand != SW_HIDE) + { + ShowCommand = SW_MAXIMIZE; + } + else + { + // We can't maximize it while having it hidden. Set it as pending. + NeedsMaximize = TRUE; + } + } + + if (PhPluginsEnabled) + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMainWindowShowing), IntToPtr(ShowCommand)); + + if (PhStartupParameters.SelectTab) + { + PPH_MAIN_TAB_PAGE page = PhMwpFindPage(&PhStartupParameters.SelectTab->sr); + + if (page) + PhMwpSelectPage(page->Index); + } + else + { + if (PhGetIntegerSetting(L"MainWindowTabRestoreEnabled")) + PhMwpSelectPage(PhGetIntegerSetting(L"MainWindowTabRestoreIndex")); + } + + if (PhStartupParameters.SysInfo) + PhShowSystemInformationDialog(PhStartupParameters.SysInfo->Buffer); + + if (ShowCommand != SW_HIDE) + ShowWindow(PhMainWndHandle, ShowCommand); + + if (PhGetIntegerSetting(L"MiniInfoWindowPinned")) + PhPinMiniInformation(MiniInfoManualPinType, 1, 0, PH_MINIINFO_LOAD_POSITION, NULL, NULL); + + return TRUE; +} + +LRESULT CALLBACK PhMwpWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_DESTROY: + { + PhMwpOnDestroy(hWnd); + } + break; + case WM_ENDSESSION: + { + PhMwpOnEndSession(hWnd); + } + break; + case WM_SETTINGCHANGE: + { + PhMwpOnSettingChange(); + } + break; + case WM_COMMAND: + { + PhMwpOnCommand(hWnd, GET_WM_COMMAND_ID(wParam, lParam)); + } + break; + case WM_SHOWWINDOW: + { + PhMwpOnShowWindow(hWnd, !!wParam, (ULONG)lParam); + } + break; + case WM_SYSCOMMAND: + { + if (PhMwpOnSysCommand(hWnd, (ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))) + return 0; + } + break; + case WM_MENUCOMMAND: + { + PhMwpOnMenuCommand(hWnd, (ULONG)wParam, (HMENU)lParam); + } + break; + case WM_INITMENUPOPUP: + { + PhMwpOnInitMenuPopup(hWnd, (HMENU)wParam, LOWORD(lParam), !!HIWORD(lParam)); + } + break; + case WM_SIZE: + { + PhMwpOnSize(hWnd); + } + break; + case WM_SIZING: + { + PhMwpOnSizing((ULONG)wParam, (PRECT)lParam); + } + break; + case WM_SETFOCUS: + { + PhMwpOnSetFocus(); + } + break; + case WM_NOTIFY: + { + LRESULT result; + + if (PhMwpOnNotify((NMHDR *)lParam, &result)) + return result; + } + break; + case WM_DEVICECHANGE: + { + MSG message; + + memset(&message, 0, sizeof(MSG)); + message.hwnd = hWnd; + message.message = uMsg; + message.wParam = wParam; + message.lParam = lParam; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackWindowNotifyEvent), &message); + } + break; + } + + if (uMsg >= WM_PH_FIRST && uMsg <= WM_PH_LAST) + { + return PhMwpOnUserMessage(hWnd, uMsg, wParam, lParam); + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +RTL_ATOM PhMwpInitializeWindowClass( + VOID + ) +{ + WNDCLASSEX wcex; + PPH_STRING className; + + memset(&wcex, 0, sizeof(WNDCLASSEX)); + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpfnWndProc = PhMwpWndProc; + wcex.hInstance = PhInstanceHandle; + className = PhaGetStringSetting(L"MainWindowClassName"); + wcex.lpszClassName = PhGetStringOrDefault(className, L"MainWindowClassName"); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + + return RegisterClassEx(&wcex); +} + +VOID PhMwpInitializeProviders( + VOID + ) +{ + if (PhCsUpdateInterval == 0) + { + PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM); + } + + // See PhMwpLoadStage1Worker for more details. + + PhInitializeProviderThread(&PhPrimaryProviderThread, PhCsUpdateInterval); + PhInitializeProviderThread(&PhSecondaryProviderThread, PhCsUpdateInterval); + + PhRegisterProvider(&PhPrimaryProviderThread, PhProcessProviderUpdate, NULL, &PhMwpProcessProviderRegistration); + PhRegisterProvider(&PhPrimaryProviderThread, PhServiceProviderUpdate, NULL, &PhMwpServiceProviderRegistration); + PhRegisterProvider(&PhPrimaryProviderThread, PhNetworkProviderUpdate, NULL, &PhMwpNetworkProviderRegistration); + + PhSetEnabledProvider(&PhMwpProcessProviderRegistration, TRUE); + PhSetEnabledProvider(&PhMwpServiceProviderRegistration, TRUE); + + PhStartProviderThread(&PhPrimaryProviderThread); + PhStartProviderThread(&PhSecondaryProviderThread); +} + +VOID PhMwpApplyUpdateInterval( + _In_ ULONG Interval + ) +{ + PhSetIntervalProviderThread(&PhPrimaryProviderThread, Interval); + PhSetIntervalProviderThread(&PhSecondaryProviderThread, Interval); +} + +VOID PhMwpInitializeControls( + _In_ HWND WindowHandle + ) +{ + ULONG thinRows; + ULONG treelistBorder; + ULONG treelistCustomColors; + PH_TREENEW_CREATEPARAMS treelistCreateParams = { 0 }; + + thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0; + treelistBorder = (PhGetIntegerSetting(L"TreeListBorderEnable") && !PhEnableThemeSupport) ? WS_BORDER : 0; + treelistCustomColors = PhGetIntegerSetting(L"TreeListCustomColorsEnable") ? TN_STYLE_CUSTOM_COLORS : 0; + + if (treelistCustomColors) + { + treelistCreateParams.TextColor = PhGetIntegerSetting(L"TreeListCustomColorText"); + treelistCreateParams.FocusColor = PhGetIntegerSetting(L"TreeListCustomColorFocus"); + treelistCreateParams.SelectionColor = PhGetIntegerSetting(L"TreeListCustomColorSelection"); + } + + TabControlHandle = CreateWindow( + WC_TABCONTROL, + NULL, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | TCS_MULTILINE, + 0, + 0, + 3, + 3, + WindowHandle, + NULL, + PhInstanceHandle, + NULL + ); + + PhMwpProcessTreeNewHandle = CreateWindow( + PH_TREENEW_CLASSNAME, + NULL, + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | TN_STYLE_ANIMATE_DIVIDER | thinRows | treelistBorder | treelistCustomColors, + 0, + 0, + 3, + 3, + WindowHandle, + NULL, + PhInstanceHandle, + &treelistCreateParams + ); + + PhMwpServiceTreeNewHandle = CreateWindow( + PH_TREENEW_CLASSNAME, + NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder | treelistCustomColors, + 0, + 0, + 3, + 3, + WindowHandle, + NULL, + PhInstanceHandle, + &treelistCreateParams + ); + + PhMwpNetworkTreeNewHandle = CreateWindow( + PH_TREENEW_CLASSNAME, + NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder | treelistCustomColors, + 0, + 0, + 3, + 3, + WindowHandle, + NULL, + PhInstanceHandle, + &treelistCreateParams + ); + + PageList = PhCreateList(10); + + PhMwpCreateInternalPage(L"Processes", 0, PhMwpProcessesPageCallback); + PhProcessTreeListInitialization(); + PhInitializeProcessTreeList(PhMwpProcessTreeNewHandle); + + PhMwpCreateInternalPage(L"Services", 0, PhMwpServicesPageCallback); + PhServiceTreeListInitialization(); + PhInitializeServiceTreeList(PhMwpServiceTreeNewHandle); + + PhMwpCreateInternalPage(L"Network", 0, PhMwpNetworkPageCallback); + PhNetworkTreeListInitialization(); + PhInitializeNetworkTreeList(PhMwpNetworkTreeNewHandle); + + CurrentPage = PageList->Items[0]; +} + +NTSTATUS PhMwpLoadStage1Worker( + _In_ PVOID Parameter + ) +{ + // If the update interval is too large, the user might have to wait a while before seeing some types of + // process-related data. We force an update by boosting the provider shortly after the program + // starts up to make things appear more quickly. + + if (PhCsUpdateInterval > PH_FLUSH_PROCESS_QUERY_DATA_INTERVAL_LONG_TERM) + { + PhBoostProvider(&PhMwpProcessProviderRegistration, NULL); + PhBoostProvider(&PhMwpServiceProviderRegistration, NULL); + } + + PhNfLoadStage2(); + + // Make sure we get closed late in the shutdown process. + SetProcessShutdownParameters(0x100, 0); + + DelayedLoadCompleted = TRUE; + //PostMessage((HWND)Parameter, WM_PH_DELAYED_LOAD_COMPLETED, 0, 0); + + return STATUS_SUCCESS; +} + +VOID PhMwpOnDestroy( + _In_ HWND WindowHandle + ) +{ + PhMainWndExiting = TRUE; + + PhSetIntegerSetting(L"MainWindowTabRestoreIndex", TabCtrl_GetCurSel(TabControlHandle)); + + // Notify pages and plugins that we are shutting down. + + PhMwpNotifyAllPages(MainTabPageDestroy, NULL, NULL); + + if (PhPluginsEnabled) + PhUnloadPlugins(); + + if (!PhMainWndEarlyExit) + PhMwpSaveSettings(WindowHandle); + + PhNfUninitialization(); + + PostQuitMessage(0); +} + +VOID PhMwpOnEndSession( + _In_ HWND WindowHandle + ) +{ + PhMwpOnDestroy(WindowHandle); +} + +VOID PhMwpOnSettingChange( + VOID + ) +{ + PhInitializeFont(); + + if (TabControlHandle) + { + SetWindowFont(TabControlHandle, PhApplicationFont, TRUE); + } +} + +static NTSTATUS PhpOpenServiceControlManager( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ) +{ + SC_HANDLE serviceHandle; + + if (serviceHandle = OpenSCManager(NULL, NULL, DesiredAccess)) + { + *Handle = serviceHandle; + return STATUS_SUCCESS; + } + + return PhGetLastWin32ErrorAsNtStatus(); +} + +VOID PhMwpOnCommand( + _In_ HWND WindowHandle, + _In_ ULONG Id + ) +{ + switch (Id) + { + case ID_ESC_EXIT: + { + if (PhGetIntegerSetting(L"HideOnClose")) + { + if (PhNfIconsEnabled()) + ShowWindow(WindowHandle, SW_HIDE); + } + else if (PhGetIntegerSetting(L"CloseOnEscape")) + { + ProcessHacker_Destroy(WindowHandle); + } + } + break; + case ID_HACKER_RUN: + { + PhShowRunFileDialog(WindowHandle); + } + break; + case ID_HACKER_RUNAS: + { + PhShowRunAsDialog(WindowHandle, NULL); + } + break; + case ID_HACKER_SHOWDETAILSFORALLPROCESSES: + { + ProcessHacker_PrepareForEarlyShutdown(WindowHandle); + + if (PhShellProcessHacker( + WindowHandle, + L"-v", + SW_SHOW, + PH_SHELL_EXECUTE_ADMIN, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, + 0, + NULL + )) + { + ProcessHacker_Destroy(WindowHandle); + } + else + { + ProcessHacker_CancelEarlyShutdown(WindowHandle); + } + } + break; + case ID_HACKER_SAVE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Text files (*.txt;*.log)", L"*.txt;*.log" }, + { L"Comma-separated values (*.csv)", L"*.csv" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog = PhCreateSaveFileDialog(); + PH_FORMAT format[3]; + + PhInitFormatS(&format[0], L"Process Hacker "); + PhInitFormatSR(&format[1], CurrentPage->Name); + PhInitFormatS(&format[2], L".txt"); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, PH_AUTO_T(PH_STRING, PhFormat(format, 3, 60))->Buffer); + + if (PhShowFileDialog(WindowHandle, fileDialog)) + { + NTSTATUS status; + PPH_STRING fileName; + ULONG filterIndex; + PPH_FILE_STREAM fileStream; + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + filterIndex = PhGetFileDialogFilterIndex(fileDialog); + + if (NT_SUCCESS(status = PhCreateFileStream( + &fileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + 0 + ))) + { + ULONG mode; + PH_MAIN_TAB_PAGE_EXPORT_CONTENT exportContent; + + if (filterIndex == 2) + mode = PH_EXPORT_MODE_CSV; + else + mode = PH_EXPORT_MODE_TABS; + + PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); + PhWritePhTextHeader(fileStream); + + exportContent.FileStream = fileStream; + exportContent.Mode = mode; + CurrentPage->Callback(CurrentPage, MainTabPageExportContent, &exportContent, NULL); + + PhDereferenceObject(fileStream); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(WindowHandle, L"Unable to create the file", status, 0); + } + + PhFreeFileDialog(fileDialog); + } + break; + case ID_HACKER_FINDHANDLESORDLLS: + { + PhShowFindObjectsDialog(); + } + break; + case ID_HACKER_OPTIONS: + { + PhShowOptionsDialog(WindowHandle); + } + break; + case ID_HACKER_PLUGINS: + { + PhShowPluginsDialog(WindowHandle); + } + break; + case ID_COMPUTER_LOCK: + case ID_COMPUTER_LOGOFF: + case ID_COMPUTER_SLEEP: + case ID_COMPUTER_HIBERNATE: + case ID_COMPUTER_RESTART: + case ID_COMPUTER_RESTARTBOOTOPTIONS: + case ID_COMPUTER_SHUTDOWN: + case ID_COMPUTER_SHUTDOWNHYBRID: + PhMwpExecuteComputerCommand(WindowHandle, Id); + break; + case ID_HACKER_EXIT: + ProcessHacker_Destroy(WindowHandle); + break; + case ID_VIEW_SYSTEMINFORMATION: + PhShowSystemInformationDialog(NULL); + break; + case ID_VIEW_HIDEPROCESSESFROMOTHERUSERS: + { + PhMwpToggleCurrentUserProcessTreeFilter(); + } + break; + case ID_VIEW_HIDESIGNEDPROCESSES: + { + PhMwpToggleSignedProcessTreeFilter(); + } + break; + case ID_VIEW_SCROLLTONEWPROCESSES: + { + PH_SET_INTEGER_CACHED_SETTING(ScrollToNewProcesses, !PhCsScrollToNewProcesses); + } + break; + case ID_VIEW_SHOWCPUBELOW001: + { + PH_SET_INTEGER_CACHED_SETTING(ShowCpuBelow001, !PhCsShowCpuBelow001); + PhInvalidateAllProcessNodes(); + } + break; + case ID_VIEW_HIDEDRIVERSERVICES: + { + PhMwpToggleDriverServiceTreeFilter(); + } + break; + case ID_VIEW_HIDEWAITINGCONNECTIONS: + { + PhMwpToggleNetworkWaitingConnectionTreeFilter(); + } + break; + case ID_VIEW_ALWAYSONTOP: + { + AlwaysOnTop = !AlwaysOnTop; + SetWindowPos(WindowHandle, AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + PhSetIntegerSetting(L"MainWindowAlwaysOnTop", AlwaysOnTop); + + PhWindowNotifyTopMostEvent(AlwaysOnTop); + } + break; + case ID_OPACITY_10: + case ID_OPACITY_20: + case ID_OPACITY_30: + case ID_OPACITY_40: + case ID_OPACITY_50: + case ID_OPACITY_60: + case ID_OPACITY_70: + case ID_OPACITY_80: + case ID_OPACITY_90: + case ID_OPACITY_OPAQUE: + { + ULONG opacity; + + opacity = PH_ID_TO_OPACITY(Id); + PhSetIntegerSetting(L"MainWindowOpacity", opacity); + PhSetWindowOpacity(WindowHandle, opacity); + } + break; + case ID_VIEW_REFRESH: + { + PhBoostProvider(&PhMwpProcessProviderRegistration, NULL); + PhBoostProvider(&PhMwpServiceProviderRegistration, NULL); + + // Note: Don't boost the network provider unless it's currently enabled. (dmex) + if (PhGetEnabledProvider(&PhMwpNetworkProviderRegistration)) + { + PhBoostProvider(&PhMwpNetworkProviderRegistration, NULL); + } + } + break; + case ID_UPDATEINTERVAL_FAST: + case ID_UPDATEINTERVAL_NORMAL: + case ID_UPDATEINTERVAL_BELOWNORMAL: + case ID_UPDATEINTERVAL_SLOW: + case ID_UPDATEINTERVAL_VERYSLOW: + { + ULONG interval; + + switch (Id) + { + case ID_UPDATEINTERVAL_FAST: + interval = 500; + break; + case ID_UPDATEINTERVAL_NORMAL: + interval = 1000; + break; + case ID_UPDATEINTERVAL_BELOWNORMAL: + interval = 2000; + break; + case ID_UPDATEINTERVAL_SLOW: + interval = 5000; + break; + case ID_UPDATEINTERVAL_VERYSLOW: + interval = 10000; + break; + } + + PH_SET_INTEGER_CACHED_SETTING(UpdateInterval, interval); + PhMwpApplyUpdateInterval(interval); + } + break; + case ID_VIEW_UPDATEAUTOMATICALLY: + { + PhMwpUpdateAutomatically = !PhMwpUpdateAutomatically; + PhMwpNotifyAllPages(MainTabPageUpdateAutomaticallyChanged, (PVOID)PhMwpUpdateAutomatically, NULL); + } + break; + case ID_TOOLS_CREATESERVICE: + { + PhShowCreateServiceDialog(WindowHandle); + } + break; + case ID_TOOLS_HIDDENPROCESSES: + { + PhShowHiddenProcessesDialog(); + } + break; + case ID_TOOLS_INSPECTEXECUTABLEFILE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Executable files (*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl)", L"*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog = PhCreateOpenFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + if (PhShowFileDialog(WindowHandle, fileDialog)) + { + PhShellExecuteUserString( + WindowHandle, + L"ProgramInspectExecutables", + PH_AUTO_T(PH_STRING, PhGetFileDialogFileName(fileDialog))->Buffer, + FALSE, + L"Make sure the PE Viewer executable file is present." + ); + } + + PhFreeFileDialog(fileDialog); + } + break; + case ID_TOOLS_PAGEFILES: + { + PhShowPagefilesDialog(WindowHandle); + } + break; + case ID_TOOLS_STARTTASKMANAGER: + { + PPH_STRING systemDirectory; + PPH_STRING taskmgrFileName; + + systemDirectory = PH_AUTO(PhGetSystemDirectory()); + taskmgrFileName = PH_AUTO(PhConcatStrings2(systemDirectory->Buffer, L"\\taskmgr.exe")); + + if (WindowsVersion >= WINDOWS_8 && !PhGetOwnTokenAttributes().Elevated) + { + if (PhUiConnectToPhSvc(WindowHandle, FALSE)) + { + PhSvcCallCreateProcessIgnoreIfeoDebugger(taskmgrFileName->Buffer); + PhUiDisconnectFromPhSvc(); + } + } + else + { + PhCreateProcessIgnoreIfeoDebugger(taskmgrFileName->Buffer); + } + } + break; + case ID_TOOLS_SCM_PERMISSIONS: + { + PhEditSecurity( + NULL, + L"Service Control Manager", + L"SCManager", + PhpOpenServiceControlManager, + NULL, + NULL + ); + } + break; + case ID_USER_CONNECT: + { + PhUiConnectSession(WindowHandle, SelectedUserSessionId); + } + break; + case ID_USER_DISCONNECT: + { + PhUiDisconnectSession(WindowHandle, SelectedUserSessionId); + } + break; + case ID_USER_LOGOFF: + { + PhUiLogoffSession(WindowHandle, SelectedUserSessionId); + } + break; + case ID_USER_REMOTECONTROL: + { + PhShowSessionShadowDialog(WindowHandle, SelectedUserSessionId); + } + break; + case ID_USER_SENDMESSAGE: + { + PhShowSessionSendMessageDialog(WindowHandle, SelectedUserSessionId); + } + break; + case ID_USER_PROPERTIES: + { + PhShowSessionProperties(WindowHandle, SelectedUserSessionId); + } + break; + case ID_HELP_LOG: + { + PhShowLogDialog(); + } + break; + case ID_HELP_DONATE: + { + PhShellExecute(WindowHandle, L"https://sourceforge.net/project/project_donations.php?group_id=242527", NULL); + } + break; + case ID_HELP_DEBUGCONSOLE: + { + PhShowDebugConsole(); + } + break; + case ID_HELP_ABOUT: + { + PhShowAboutDialog(); + } + break; + case ID_PROCESS_TERMINATE: + { + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + PhReferenceObjects(processes, numberOfProcesses); + + if (PhUiTerminateProcesses(WindowHandle, processes, numberOfProcesses)) + PhDeselectAllProcessNodes(); + + PhDereferenceObjects(processes, numberOfProcesses); + PhFree(processes); + } + break; + case ID_PROCESS_TERMINATETREE: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + + if (PhUiTerminateTreeProcess(WindowHandle, processItem)) + PhDeselectAllProcessNodes(); + + PhDereferenceObject(processItem); + } + } + break; + case ID_PROCESS_SUSPEND: + { + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + PhReferenceObjects(processes, numberOfProcesses); + PhUiSuspendProcesses(WindowHandle, processes, numberOfProcesses); + PhDereferenceObjects(processes, numberOfProcesses); + PhFree(processes); + } + break; + case ID_PROCESS_RESUME: + { + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + PhReferenceObjects(processes, numberOfProcesses); + PhUiResumeProcesses(WindowHandle, processes, numberOfProcesses); + PhDereferenceObjects(processes, numberOfProcesses); + PhFree(processes); + } + break; + case ID_PROCESS_RESTART: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + + if (PhUiRestartProcess(WindowHandle, processItem)) + PhDeselectAllProcessNodes(); + + PhDereferenceObject(processItem); + } + } + break; + case ID_PROCESS_CREATEDUMPFILE: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhUiCreateDumpFileProcess(WindowHandle, processItem); + PhDereferenceObject(processItem); + } + } + break; + case ID_PROCESS_DEBUG: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhUiDebugProcess(WindowHandle, processItem); + PhDereferenceObject(processItem); + } + } + break; + case ID_PROCESS_VIRTUALIZATION: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhUiSetVirtualizationProcess( + WindowHandle, + processItem, + !PhMwpSelectedProcessVirtualizationEnabled + ); + PhDereferenceObject(processItem); + } + } + break; + case ID_PROCESS_AFFINITY: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhShowProcessAffinityDialog(WindowHandle, processItem, NULL); + PhDereferenceObject(processItem); + } + } + break; + case ID_MISCELLANEOUS_SETCRITICAL: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhUiSetCriticalProcess(WindowHandle, processItem); + PhDereferenceObject(processItem); + } + } + break; + case ID_MISCELLANEOUS_DETACHFROMDEBUGGER: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhUiDetachFromDebuggerProcess(WindowHandle, processItem); + PhDereferenceObject(processItem); + } + } + break; + case ID_MISCELLANEOUS_GDIHANDLES: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhReferenceObject(processItem); + PhShowGdiHandlesDialog(WindowHandle, processItem); + PhDereferenceObject(processItem); + } + } + break; + case ID_PAGEPRIORITY_VERYLOW: + case ID_PAGEPRIORITY_LOW: + case ID_PAGEPRIORITY_MEDIUM: + case ID_PAGEPRIORITY_BELOWNORMAL: + case ID_PAGEPRIORITY_NORMAL: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + ULONG pagePriority; + + switch (Id) + { + case ID_PAGEPRIORITY_VERYLOW: + pagePriority = MEMORY_PRIORITY_VERY_LOW; + break; + case ID_PAGEPRIORITY_LOW: + pagePriority = MEMORY_PRIORITY_LOW; + break; + case ID_PAGEPRIORITY_MEDIUM: + pagePriority = MEMORY_PRIORITY_MEDIUM; + break; + case ID_PAGEPRIORITY_BELOWNORMAL: + pagePriority = MEMORY_PRIORITY_BELOW_NORMAL; + break; + case ID_PAGEPRIORITY_NORMAL: + pagePriority = MEMORY_PRIORITY_NORMAL; + break; + } + + PhReferenceObject(processItem); + PhUiSetPagePriorityProcess(WindowHandle, processItem, pagePriority); + PhDereferenceObject(processItem); + } + } + break; + case ID_MISCELLANEOUS_REDUCEWORKINGSET: + { + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + PhReferenceObjects(processes, numberOfProcesses); + PhUiReduceWorkingSetProcesses(WindowHandle, processes, numberOfProcesses); + PhDereferenceObjects(processes, numberOfProcesses); + PhFree(processes); + } + break; + case ID_MISCELLANEOUS_RUNAS: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem && processItem->FileName) + { + PhSetStringSetting2(L"RunAsProgram", &processItem->FileName->sr); + PhShowRunAsDialog(WindowHandle, NULL); + } + } + break; + case ID_MISCELLANEOUS_RUNASTHISUSER: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhShowRunAsDialog(WindowHandle, processItem->ProcessId); + } + } + break; + case ID_PRIORITY_REALTIME: + case ID_PRIORITY_HIGH: + case ID_PRIORITY_ABOVENORMAL: + case ID_PRIORITY_NORMAL: + case ID_PRIORITY_BELOWNORMAL: + case ID_PRIORITY_IDLE: + { + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + PhReferenceObjects(processes, numberOfProcesses); + PhMwpExecuteProcessPriorityCommand(Id, processes, numberOfProcesses); + PhDereferenceObjects(processes, numberOfProcesses); + PhFree(processes); + } + break; + case ID_IOPRIORITY_VERYLOW: + case ID_IOPRIORITY_LOW: + case ID_IOPRIORITY_NORMAL: + case ID_IOPRIORITY_HIGH: + { + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + PhReferenceObjects(processes, numberOfProcesses); + PhMwpExecuteProcessIoPriorityCommand(Id, processes, numberOfProcesses); + PhDereferenceObjects(processes, numberOfProcesses); + PhFree(processes); + } + break; + case ID_WINDOW_BRINGTOFRONT: + { + if (IsWindow(PhMwpSelectedProcessWindowHandle)) + { + WINDOWPLACEMENT placement = { sizeof(placement) }; + + GetWindowPlacement(PhMwpSelectedProcessWindowHandle, &placement); + + if (placement.showCmd == SW_MINIMIZE) + ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_RESTORE); + else + SetForegroundWindow(PhMwpSelectedProcessWindowHandle); + } + } + break; + case ID_WINDOW_RESTORE: + { + if (IsWindow(PhMwpSelectedProcessWindowHandle)) + { + ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_RESTORE); + } + } + break; + case ID_WINDOW_MINIMIZE: + { + if (IsWindow(PhMwpSelectedProcessWindowHandle)) + { + ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_MINIMIZE); + } + } + break; + case ID_WINDOW_MAXIMIZE: + { + if (IsWindow(PhMwpSelectedProcessWindowHandle)) + { + ShowWindowAsync(PhMwpSelectedProcessWindowHandle, SW_MAXIMIZE); + } + } + break; + case ID_WINDOW_CLOSE: + { + if (IsWindow(PhMwpSelectedProcessWindowHandle)) + { + PostMessage(PhMwpSelectedProcessWindowHandle, WM_CLOSE, 0, 0); + } + } + break; + case ID_PROCESS_OPENFILELOCATION: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem && + !PhIsNullOrEmptyString(processItem->FileName) && + PhDoesFileExistsWin32(PhGetString(processItem->FileName) + )) + { + PhReferenceObject(processItem); + PhShellExecuteUserString( + WindowHandle, + L"FileBrowseExecutable", + processItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + PhDereferenceObject(processItem); + } + } + break; + case ID_PROCESS_SEARCHONLINE: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + PhSearchOnlineString(WindowHandle, processItem->ProcessName->Buffer); + } + } + break; + case ID_PROCESS_PROPERTIES: + { + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (processItem) + { + // No reference needed; no messages pumped. + PhMwpShowProcessProperties(processItem); + } + } + break; + case ID_PROCESS_COPY: + { + PhCopyProcessTree(); + } + break; + case ID_SERVICE_GOTOPROCESS: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + PPH_PROCESS_NODE processNode; + + if (serviceItem) + { + if (processNode = PhFindProcessNode(serviceItem->ProcessId)) + { + PhMwpSelectPage(PhMwpProcessesPage->Index); + SetFocus(PhMwpProcessTreeNewHandle); + PhSelectAndEnsureVisibleProcessNode(processNode); + } + } + } + break; + case ID_SERVICE_START: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + PhReferenceObject(serviceItem); + PhUiStartService(WindowHandle, serviceItem); + PhDereferenceObject(serviceItem); + } + } + break; + case ID_SERVICE_CONTINUE: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + PhReferenceObject(serviceItem); + PhUiContinueService(WindowHandle, serviceItem); + PhDereferenceObject(serviceItem); + } + } + break; + case ID_SERVICE_PAUSE: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + PhReferenceObject(serviceItem); + PhUiPauseService(WindowHandle, serviceItem); + PhDereferenceObject(serviceItem); + } + } + break; + case ID_SERVICE_STOP: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + PhReferenceObject(serviceItem); + PhUiStopService(WindowHandle, serviceItem); + PhDereferenceObject(serviceItem); + } + } + break; + case ID_SERVICE_DELETE: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + PhReferenceObject(serviceItem); + + if (PhUiDeleteService(WindowHandle, serviceItem)) + PhDeselectAllServiceNodes(); + + PhDereferenceObject(serviceItem); + } + } + break; + case ID_SERVICE_OPENKEY: + { + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + static PH_STRINGREF hklm = PH_STRINGREF_INIT(L"HKLM\\"); + + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + HANDLE keyHandle; + PPH_STRING serviceKeyName = PH_AUTO(PhConcatStringRef2(&servicesKeyName, &serviceItem->Name->sr)); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &serviceKeyName->sr, + 0 + ))) + { + PPH_STRING hklmServiceKeyName; + + hklmServiceKeyName = PH_AUTO(PhConcatStringRef2(&hklm, &serviceKeyName->sr)); + PhShellOpenKey2(WindowHandle, hklmServiceKeyName); + NtClose(keyHandle); + } + } + } + break; + case ID_SERVICE_OPENFILELOCATION: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + SC_HANDLE serviceHandle; + + if (serviceItem && (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) + { + PPH_STRING fileName; + + if (fileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle)) + { + PhShellExecuteUserString( + WindowHandle, + L"FileBrowseExecutable", + fileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + PhDereferenceObject(fileName); + } + + CloseServiceHandle(serviceHandle); + } + } + break; + case ID_SERVICE_PROPERTIES: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedServiceItem(); + + if (serviceItem) + { + // The object relies on the list view reference, which could + // disappear if we don't reference the object here. + PhReferenceObject(serviceItem); + PhShowServiceProperties(WindowHandle, serviceItem); + PhDereferenceObject(serviceItem); + } + } + break; + case ID_SERVICE_COPY: + { + PhCopyServiceList(); + } + break; + case ID_NETWORK_GOTOPROCESS: + { + PPH_NETWORK_ITEM networkItem = PhGetSelectedNetworkItem(); + PPH_PROCESS_NODE processNode; + + if (networkItem) + { + if (processNode = PhFindProcessNode(networkItem->ProcessId)) + { + PhMwpSelectPage(PhMwpProcessesPage->Index); + SetFocus(PhMwpProcessTreeNewHandle); + PhSelectAndEnsureVisibleProcessNode(processNode); + } + } + } + break; + case ID_NETWORK_GOTOSERVICE: + { + PPH_NETWORK_ITEM networkItem = PhGetSelectedNetworkItem(); + PPH_SERVICE_ITEM serviceItem; + + if (networkItem && networkItem->OwnerName) + { + if (serviceItem = PhReferenceServiceItem(networkItem->OwnerName->Buffer)) + { + PhMwpSelectPage(PhMwpServicesPage->Index); + SetFocus(PhMwpServiceTreeNewHandle); + ProcessHacker_SelectServiceItem(WindowHandle, serviceItem); + + PhDereferenceObject(serviceItem); + } + } + } + break; + case ID_NETWORK_CLOSE: + { + PPH_NETWORK_ITEM *networkItems; + ULONG numberOfNetworkItems; + + PhGetSelectedNetworkItems(&networkItems, &numberOfNetworkItems); + PhReferenceObjects(networkItems, numberOfNetworkItems); + + if (PhUiCloseConnections(WindowHandle, networkItems, numberOfNetworkItems)) + PhDeselectAllNetworkNodes(); + + PhDereferenceObjects(networkItems, numberOfNetworkItems); + PhFree(networkItems); + } + break; + case ID_NETWORK_COPY: + { + PhCopyNetworkList(); + } + break; + case ID_TAB_NEXT: + { + ULONG selectedIndex = TabCtrl_GetCurSel(TabControlHandle); + + if (selectedIndex != PageList->Count - 1) + selectedIndex++; + else + selectedIndex = 0; + + PhMwpSelectPage(selectedIndex); + } + break; + case ID_TAB_PREV: + { + ULONG selectedIndex = TabCtrl_GetCurSel(TabControlHandle); + + if (selectedIndex != 0) + selectedIndex--; + else + selectedIndex = PageList->Count - 1; + + PhMwpSelectPage(selectedIndex); + } + break; + } +} + +VOID PhMwpOnShowWindow( + _In_ HWND WindowHandle, + _In_ BOOLEAN Showing, + _In_ ULONG State + ) +{ + if (NeedsMaximize) + { + ShowWindow(WindowHandle, SW_MAXIMIZE); + NeedsMaximize = FALSE; + } +} + +BOOLEAN PhMwpOnSysCommand( + _In_ HWND WindowHandle, + _In_ ULONG Type, + _In_ LONG CursorScreenX, + _In_ LONG CursorScreenY + ) +{ + switch (Type) + { + case SC_CLOSE: + { + if (PhGetIntegerSetting(L"HideOnClose") && PhNfIconsEnabled()) + { + ShowWindow(WindowHandle, SW_HIDE); + return TRUE; + } + } + break; + case SC_MINIMIZE: + { + // Save the current window state because we may not have a chance to later. + PhMwpSaveWindowState(WindowHandle); + + if (PhGetIntegerSetting(L"HideOnMinimize") && PhNfIconsEnabled()) + { + ShowWindow(WindowHandle, SW_HIDE); + return TRUE; + } + } + break; + } + + return FALSE; +} + +VOID PhMwpOnMenuCommand( + _In_ HWND WindowHandle, + _In_ ULONG Index, + _In_ HMENU Menu + ) +{ + MENUITEMINFO menuItemInfo; + + memset(&menuItemInfo, 0, sizeof(MENUITEMINFO)); + menuItemInfo.cbSize = sizeof(MENUITEMINFO); + menuItemInfo.fMask = MIIM_ID | MIIM_DATA; + + if (GetMenuItemInfo(Menu, Index, TRUE, &menuItemInfo)) + { + PhMwpDispatchMenuCommand( + WindowHandle, + Menu, + Index, + menuItemInfo.wID, + menuItemInfo.dwItemData + ); + } +} + +VOID PhMwpOnInitMenuPopup( + _In_ HWND WindowHandle, + _In_ HMENU Menu, + _In_ ULONG Index, + _In_ BOOLEAN IsWindowMenu + ) +{ + ULONG i; + BOOLEAN found; + MENUINFO menuInfo; + PPH_EMENU menu; + + found = FALSE; + + for (i = 0; i < sizeof(SubMenuHandles) / sizeof(HMENU); i++) + { + if (Menu == SubMenuHandles[i]) + { + found = TRUE; + break; + } + } + + if (!found) + return; + + // Delete all items in this submenu. + while (DeleteMenu(Menu, 0, MF_BYPOSITION)) + NOTHING; + + // Delete the previous EMENU for this submenu. + if (SubMenuObjects[Index]) + PhDestroyEMenu(SubMenuObjects[Index]); + + // Make sure the menu style is set correctly. + memset(&menuInfo, 0, sizeof(MENUINFO)); + menuInfo.cbSize = sizeof(MENUINFO); + menuInfo.fMask = MIM_STYLE; + menuInfo.dwStyle = MNS_CHECKORBMP; + + if (PhEnableThemeSupport) + { + menuInfo.fMask |= MIM_BACKGROUND; + menuInfo.hbrBack = PhMenuBackgroundBrush; + } + + SetMenuInfo(Menu, &menuInfo); + + menu = PhCreateEMenu(); + + if (Index == 3) // Special case for Users menu. + { + PhMwpUpdateUsersMenu(menu); + } + else + { + PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MAINWND), Index); + PhMwpInitializeSubMenu(menu, Index); + } + + if (PhPluginsEnabled) + { + PH_PLUGIN_MENU_INFORMATION menuInfo; + + PhPluginInitializeMenuInfo(&menuInfo, menu, WindowHandle, PH_PLUGIN_MENU_DISALLOW_HOOKS); + menuInfo.u.MainMenu.SubMenuIndex = Index; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), &menuInfo); + } + + PhEMenuToHMenu2(Menu, menu, 0, NULL); + SubMenuObjects[Index] = menu; +} + +VOID PhMwpOnSize( + _In_ HWND WindowHandle + ) +{ + if (!IsMinimized(WindowHandle)) + { + HDWP deferHandle; + + deferHandle = BeginDeferWindowPos(2); + PhMwpLayout(&deferHandle); + EndDeferWindowPos(deferHandle); + } +} + +VOID PhMwpOnSizing( + _In_ ULONG Edge, + _In_ PRECT DragRectangle + ) +{ + PhResizingMinimumSize(DragRectangle, Edge, 400, 340); +} + +VOID PhMwpOnSetFocus( + VOID + ) +{ + if (CurrentPage->WindowHandle) + SetFocus(CurrentPage->WindowHandle); +} + +BOOLEAN PhMwpOnNotify( + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ) +{ + if (Header->hwndFrom == TabControlHandle) + { + PhMwpNotifyTabControl(Header); + } + + return FALSE; +} + +ULONG_PTR PhMwpOnUserMessage( + _In_ HWND WindowHandle, + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ) +{ + switch (Message) + { + case WM_PH_ACTIVATE: + { + if (!PhMainWndEarlyExit && !PhMainWndExiting) + { + if (WParam != 0) + { + PPH_PROCESS_NODE processNode; + + if (processNode = PhFindProcessNode((HANDLE)WParam)) + PhSelectAndEnsureVisibleProcessNode(processNode); + } + + if (!IsWindowVisible(WindowHandle)) + { + ShowWindow(WindowHandle, SW_SHOW); + } + + if (IsMinimized(WindowHandle)) + { + ShowWindow(WindowHandle, SW_RESTORE); + } + + return PH_ACTIVATE_REPLY; + } + else + { + return 0; + } + } + break; + case WM_PH_SHOW_PROCESS_PROPERTIES: + { + PhMwpShowProcessProperties((PPH_PROCESS_ITEM)LParam); + } + break; + case WM_PH_DESTROY: + { + DestroyWindow(WindowHandle); + } + break; + case WM_PH_SAVE_ALL_SETTINGS: + { + PhMwpSaveSettings(WindowHandle); + } + break; + case WM_PH_PREPARE_FOR_EARLY_SHUTDOWN: + { + PhMwpSaveSettings(WindowHandle); + PhMainWndEarlyExit = TRUE; + } + break; + case WM_PH_CANCEL_EARLY_SHUTDOWN: + { + PhMainWndEarlyExit = FALSE; + } + break; + case WM_PH_DELAYED_LOAD_COMPLETED: + { + // Nothing + } + break; + case WM_PH_NOTIFY_ICON_MESSAGE: + { + PhNfForwardMessage(WindowHandle, WParam, LParam); + } + break; + case WM_PH_TOGGLE_VISIBLE: + { + PhMwpActivateWindow(WindowHandle, !WParam); + } + break; + case WM_PH_SHOW_MEMORY_EDITOR: + { + PPH_SHOW_MEMORY_EDITOR showMemoryEditor = (PPH_SHOW_MEMORY_EDITOR)LParam; + + PhShowMemoryEditorDialog( + showMemoryEditor->OwnerWindow, + showMemoryEditor->ProcessId, + showMemoryEditor->BaseAddress, + showMemoryEditor->RegionSize, + showMemoryEditor->SelectOffset, + showMemoryEditor->SelectLength, + showMemoryEditor->Title, + showMemoryEditor->Flags + ); + PhClearReference(&showMemoryEditor->Title); + PhFree(showMemoryEditor); + } + break; + case WM_PH_SHOW_MEMORY_RESULTS: + { + PPH_SHOW_MEMORY_RESULTS showMemoryResults = (PPH_SHOW_MEMORY_RESULTS)LParam; + + PhShowMemoryResultsDialog( + showMemoryResults->ProcessId, + showMemoryResults->Results + ); + PhDereferenceMemoryResults( + (PPH_MEMORY_RESULT *)showMemoryResults->Results->Items, + showMemoryResults->Results->Count + ); + PhDereferenceObject(showMemoryResults->Results); + PhFree(showMemoryResults); + } + break; + case WM_PH_SELECT_TAB_PAGE: + { + ULONG index = (ULONG)WParam; + + PhMwpSelectPage(index); + + if (CurrentPage->WindowHandle) + SetFocus(CurrentPage->WindowHandle); + } + break; + case WM_PH_GET_CALLBACK_LAYOUT_PADDING: + { + return (ULONG_PTR)&LayoutPaddingCallback; + } + break; + case WM_PH_INVALIDATE_LAYOUT_PADDING: + { + LayoutPaddingValid = FALSE; + } + break; + case WM_PH_SELECT_PROCESS_NODE: + { + PhSelectAndEnsureVisibleProcessNode((PPH_PROCESS_NODE)LParam); + } + break; + case WM_PH_SELECT_SERVICE_ITEM: + { + PPH_SERVICE_NODE serviceNode; + + PhMwpNeedServiceTreeList(); + + // For compatibility, LParam is a service item, not node. + if (serviceNode = PhFindServiceNode((PPH_SERVICE_ITEM)LParam)) + { + PhSelectAndEnsureVisibleServiceNode(serviceNode); + } + } + break; + case WM_PH_SELECT_NETWORK_ITEM: + { + PPH_NETWORK_NODE networkNode; + + PhMwpNeedNetworkTreeList(); + + // For compatibility, LParam is a network item, not node. + if (networkNode = PhFindNetworkNode((PPH_NETWORK_ITEM)LParam)) + { + PhSelectAndEnsureVisibleNetworkNode(networkNode); + } + } + break; + case WM_PH_UPDATE_FONT: + { + PPH_STRING fontHexString; + LOGFONT font; + + fontHexString = PhaGetStringSetting(L"Font"); + + if ( + fontHexString->Length / sizeof(WCHAR) / 2 == sizeof(LOGFONT) && + PhHexStringToBuffer(&fontHexString->sr, (PUCHAR)&font) + ) + { + HFONT newFont; + + newFont = CreateFontIndirect(&font); + + if (newFont) + { + if (PhTreeWindowFont) + DeleteFont(PhTreeWindowFont); + PhTreeWindowFont = newFont; + + PhMwpNotifyAllPages(MainTabPageFontChanged, newFont, NULL); + } + } + } + break; + case WM_PH_GET_FONT: + return (ULONG_PTR)GetWindowFont(PhMwpProcessTreeNewHandle); + case WM_PH_INVOKE: + { + VOID (NTAPI *function)(PVOID); + + function = (PVOID)LParam; + function((PVOID)WParam); + } + break; + case WM_PH_CREATE_TAB_PAGE: + { + return (ULONG_PTR)PhMwpCreatePage((PPH_MAIN_TAB_PAGE)LParam); + } + break; + case WM_PH_REFRESH: + { + SendMessage(WindowHandle, WM_COMMAND, ID_VIEW_REFRESH, 0); + } + break; + case WM_PH_GET_UPDATE_AUTOMATICALLY: + { + return PhMwpUpdateAutomatically; + } + break; + case WM_PH_SET_UPDATE_AUTOMATICALLY: + { + if (!!WParam != PhMwpUpdateAutomatically) + { + SendMessage(WindowHandle, WM_COMMAND, ID_VIEW_UPDATEAUTOMATICALLY, 0); + } + } + break; + case WM_PH_ICON_CLICK: + { + PhMwpActivateWindow(WindowHandle, !!PhGetIntegerSetting(L"IconTogglesVisibility")); + } + break; + } + + return 0; +} + +VOID PhMwpLoadSettings( + _In_ HWND WindowHandle + ) +{ + ULONG opacity; + PPH_STRING customFont; + + customFont = PhaGetStringSetting(L"Font"); + opacity = PhGetIntegerSetting(L"MainWindowOpacity"); + PhStatisticsSampleCount = PhGetIntegerSetting(L"SampleCount"); + PhEnablePurgeProcessRecords = !PhGetIntegerSetting(L"NoPurgeProcessRecords"); + PhEnableCycleCpuUsage = !!PhGetIntegerSetting(L"EnableCycleCpuUsage"); + PhEnableServiceNonPoll = !!PhGetIntegerSetting(L"EnableServiceNonPoll"); + PhEnableNetworkProviderResolve = !!PhGetIntegerSetting(L"EnableNetworkResolve"); + PhEnableProcessQueryStage2 = !!PhGetIntegerSetting(L"EnableStage2"); + PhEnableServiceQueryStage2 = !!PhGetIntegerSetting(L"EnableServiceStage2"); + PhEnableThemeSupport = !!PhGetIntegerSetting(L"EnableThemeSupport"); + PhEnableTooltipSupport = !!PhGetIntegerSetting(L"EnableTooltipSupport"); + PhEnableLinuxSubsystemSupport = !!PhGetIntegerSetting(L"EnableLinuxSubsystemSupport"); + PhMwpNotifyIconNotifyMask = PhGetIntegerSetting(L"IconNotifyMask"); + + if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) + { + AlwaysOnTop = TRUE; + SetWindowPos(WindowHandle, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); + } + + if (opacity != 0) + PhSetWindowOpacity(WindowHandle, opacity); + + PhNfLoadStage1(); + + if (customFont->Length / sizeof(WCHAR) / 2 == sizeof(LOGFONT)) + SendMessage(WindowHandle, WM_PH_UPDATE_FONT, 0, 0); + + PhMwpNotifyAllPages(MainTabPageLoadSettings, NULL, NULL); +} + +VOID PhMwpSaveSettings( + _In_ HWND WindowHandle + ) +{ + PhMwpNotifyAllPages(MainTabPageSaveSettings, NULL, NULL); + + PhNfSaveSettings(); + PhSetIntegerSetting(L"IconNotifyMask", PhMwpNotifyIconNotifyMask); + + PhSaveWindowPlacementToSetting(L"MainWindowPosition", L"MainWindowSize", WindowHandle); + PhMwpSaveWindowState(WindowHandle); + + if (PhSettingsFileName) + PhSaveSettings(PhSettingsFileName->Buffer); +} + +VOID PhMwpSaveWindowState( + _In_ HWND WindowHandle + ) +{ + WINDOWPLACEMENT placement = { sizeof(placement) }; + + GetWindowPlacement(WindowHandle, &placement); + + if (placement.showCmd == SW_NORMAL) + PhSetIntegerSetting(L"MainWindowState", SW_NORMAL); + else if (placement.showCmd == SW_MAXIMIZE) + PhSetIntegerSetting(L"MainWindowState", SW_MAXIMIZE); +} + +VOID PhMwpUpdateLayoutPadding( + VOID + ) +{ + PH_LAYOUT_PADDING_DATA data; + + memset(&data, 0, sizeof(PH_LAYOUT_PADDING_DATA)); + PhInvokeCallback(&LayoutPaddingCallback, &data); + + LayoutPadding = data.Padding; +} + +VOID PhMwpApplyLayoutPadding( + _Inout_ PRECT Rect, + _In_ PRECT Padding + ) +{ + Rect->left += Padding->left; + Rect->top += Padding->top; + Rect->right -= Padding->right; + Rect->bottom -= Padding->bottom; +} + +VOID PhMwpLayout( + _Inout_ HDWP *DeferHandle + ) +{ + RECT rect; + + // Resize the tab control. + // Don't defer the resize. The tab control doesn't repaint properly. + + if (!LayoutPaddingValid) + { + PhMwpUpdateLayoutPadding(); + LayoutPaddingValid = TRUE; + } + + GetClientRect(PhMainWndHandle, &rect); + PhMwpApplyLayoutPadding(&rect, &LayoutPadding); + + SetWindowPos(TabControlHandle, NULL, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + UpdateWindow(TabControlHandle); + + PhMwpLayoutTabControl(DeferHandle); +} + +VOID PhMwpSetupComputerMenu( + _In_ PPH_EMENU_ITEM Root + ) +{ + PPH_EMENU_ITEM menuItem; + + if (WindowsVersion < WINDOWS_8) + { + if (menuItem = PhFindEMenuItem(Root, PH_EMENU_FIND_DESCEND, NULL, ID_COMPUTER_RESTARTBOOTOPTIONS)) + PhDestroyEMenuItem(menuItem); + if (menuItem = PhFindEMenuItem(Root, PH_EMENU_FIND_DESCEND, NULL, ID_COMPUTER_SHUTDOWNHYBRID)) + PhDestroyEMenuItem(menuItem); + } +} + +BOOLEAN PhMwpExecuteComputerCommand( + _In_ HWND WindowHandle, + _In_ ULONG Id + ) +{ + switch (Id) + { + case ID_COMPUTER_LOCK: + PhUiLockComputer(WindowHandle); + return TRUE; + case ID_COMPUTER_LOGOFF: + PhUiLogoffComputer(WindowHandle); + return TRUE; + case ID_COMPUTER_SLEEP: + PhUiSleepComputer(WindowHandle); + return TRUE; + case ID_COMPUTER_HIBERNATE: + PhUiHibernateComputer(WindowHandle); + return TRUE; + case ID_COMPUTER_RESTART: + PhUiRestartComputer(WindowHandle, 0); + return TRUE; + case ID_COMPUTER_RESTARTBOOTOPTIONS: + PhUiRestartComputer(WindowHandle, EWX_BOOTOPTIONS); + return TRUE; + case ID_COMPUTER_SHUTDOWN: + PhUiShutdownComputer(WindowHandle, 0); + return TRUE; + case ID_COMPUTER_SHUTDOWNHYBRID: + PhUiShutdownComputer(WindowHandle, EWX_HYBRID_SHUTDOWN); + return TRUE; + } + + return FALSE; +} + +VOID PhMwpActivateWindow( + _In_ HWND WindowHandle, + _In_ BOOLEAN Toggle + ) +{ + if (IsMinimized(WindowHandle)) + { + ShowWindow(WindowHandle, SW_RESTORE); + SetForegroundWindow(WindowHandle); + } + else if (IsWindowVisible(WindowHandle)) + { + if (Toggle) + ShowWindow(WindowHandle, SW_HIDE); + else + SetForegroundWindow(WindowHandle); + } + else + { + ShowWindow(WindowHandle, SW_SHOW); + SetForegroundWindow(WindowHandle); + } +} + +VOID PhMwpInitializeMainMenu( + _In_ HMENU Menu + ) +{ + MENUINFO menuInfo; + ULONG i; + + memset(&menuInfo, 0, sizeof(MENUINFO)); + menuInfo.cbSize = sizeof(MENUINFO); + menuInfo.fMask = MIM_STYLE; + menuInfo.dwStyle = MNS_NOTIFYBYPOS | MNS_AUTODISMISS; + + SetMenuInfo(Menu, &menuInfo); + + for (i = 0; i < RTL_NUMBER_OF(SubMenuHandles); i++) + { + SubMenuHandles[i] = GetSubMenu(Menu, i); + } +} + +VOID PhMwpDispatchMenuCommand( + _In_ HWND WindowHandle, + _In_ HMENU MenuHandle, + _In_ ULONG ItemIndex, + _In_ ULONG ItemId, + _In_ ULONG_PTR ItemData + ) +{ + switch (ItemId) + { + case ID_PLUGIN_MENU_ITEM: + { + PPH_EMENU_ITEM menuItem; + PH_PLUGIN_MENU_INFORMATION menuInfo; + + menuItem = (PPH_EMENU_ITEM)ItemData; + + if (menuItem) + { + PhPluginInitializeMenuInfo(&menuInfo, NULL, WindowHandle, 0); + PhPluginTriggerEMenuItem(&menuInfo, menuItem); + } + + return; + } + break; + case ID_TRAYICONS_REGISTERED: + { + PPH_EMENU_ITEM menuItem; + + menuItem = (PPH_EMENU_ITEM)ItemData; + + if (menuItem) + { + PPH_NF_ICON icon; + + icon = menuItem->Context; + PhNfSetVisibleIcon(icon, !(icon->Flags & PH_NF_ICON_ENABLED)); + } + + return; + } + break; + case ID_USER_CONNECT: + case ID_USER_DISCONNECT: + case ID_USER_LOGOFF: + case ID_USER_REMOTECONTROL: + case ID_USER_SENDMESSAGE: + case ID_USER_PROPERTIES: + { + PPH_EMENU_ITEM menuItem; + + menuItem = (PPH_EMENU_ITEM)ItemData; + + if (menuItem && menuItem->Parent) + { + SelectedUserSessionId = PtrToUlong(menuItem->Parent->Context); + } + } + break; + case ID_VIEW_ORGANIZECOLUMNSETS: + { + PhShowColumnSetEditorDialog(WindowHandle, L"ProcessTreeColumnSetConfig"); + } + return; + case ID_VIEW_SAVECOLUMNSET: + { + PPH_EMENU_ITEM menuItem; + PPH_STRING columnSetName = NULL; + + menuItem = (PPH_EMENU_ITEM)ItemData; + + while (PhaChoiceDialog( + WindowHandle, + L"Column Set Name", + L"Enter a name for this column set:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &columnSetName, + NULL, + NULL + )) + { + if (!PhIsNullOrEmptyString(columnSetName)) + break; + } + + if (!PhIsNullOrEmptyString(columnSetName)) + { + PPH_STRING treeSettings; + PPH_STRING sortSettings; + + // Query the current column configuration. + PhSaveSettingsProcessTreeListEx(&treeSettings, &sortSettings); + // Create the column set for this column configuration. + PhSaveSettingsColumnSet(L"ProcessTreeColumnSetConfig", columnSetName, treeSettings, sortSettings); + + PhDereferenceObject(treeSettings); + PhDereferenceObject(sortSettings); + } + } + return; + case ID_VIEW_LOADCOLUMNSET: + { + PPH_EMENU_ITEM menuItem; + PPH_STRING columnSetName; + PPH_STRING treeSettings; + PPH_STRING sortSettings; + + menuItem = (PPH_EMENU_ITEM)ItemData; + columnSetName = PhCreateString(menuItem->Text); + + // Query the selected column set. + if (PhLoadSettingsColumnSet(L"ProcessTreeColumnSetConfig", columnSetName, &treeSettings, &sortSettings)) + { + // Load the column configuration from the selected column set. + PhLoadSettingsProcessTreeListEx(treeSettings, sortSettings); + + PhDereferenceObject(treeSettings); + PhDereferenceObject(sortSettings); + } + + PhDereferenceObject(columnSetName); + } + return; + } + + SendMessage(WindowHandle, WM_COMMAND, ItemId, 0); +} + +VOID PhMwpInitializeSubMenu( + _In_ PPH_EMENU Menu, + _In_ ULONG Index + ) +{ + PPH_EMENU_ITEM menuItem; + + if (Index == PH_MENU_ITEM_LOCATION_HACKER) // Hacker + { + // Fix some menu items. + if (PhGetOwnTokenAttributes().Elevated) + { + if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_RUNASADMINISTRATOR)) + PhDestroyEMenuItem(menuItem); + if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_SHOWDETAILSFORALLPROCESSES)) + PhDestroyEMenuItem(menuItem); + } + else + { + HBITMAP shieldBitmap; + + if (shieldBitmap = PhGetShieldBitmap()) + { + if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_HACKER_SHOWDETAILSFORALLPROCESSES)) + menuItem->Bitmap = shieldBitmap; + } + } + + // Fix up the Computer menu. + PhMwpSetupComputerMenu(Menu); + } + else if (Index == PH_MENU_ITEM_LOCATION_VIEW) // View + { + PPH_EMENU_ITEM trayIconsMenuItem; + ULONG i; + PPH_EMENU_ITEM menuItem; + ULONG id; + ULONG placeholderIndex; + + if (trayIconsMenuItem = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, NULL, ID_VIEW_TRAYICONS)) + { + // Add menu items for the registered tray icons. + + for (i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + menuItem = PhCreateEMenuItem(0, ID_TRAYICONS_REGISTERED, icon->Text, NULL, icon); + PhInsertEMenuItem(trayIconsMenuItem, menuItem, ULONG_MAX); + + // Update the text and check marks on the menu items. + + if (icon->Flags & PH_NF_ICON_ENABLED) + { + menuItem->Flags |= PH_EMENU_CHECKED; + } + + if (icon->Flags & PH_NF_ICON_UNAVAILABLE) + { + PPH_STRING newText; + + newText = PhaConcatStrings2(icon->Text, L" (Unavailable)"); + PhModifyEMenuItem(menuItem, PH_EMENU_MODIFY_TEXT, PH_EMENU_TEXT_OWNED, + PhAllocateCopy(newText->Buffer, newText->Length + sizeof(WCHAR)), NULL); + } + } + } + + if (menuItem = PhFindEMenuItemEx(Menu, 0, NULL, ID_VIEW_SECTIONPLACEHOLDER, NULL, &placeholderIndex)) + { + PhDestroyEMenuItem(menuItem); + PhMwpInitializeSectionMenuItems(Menu, placeholderIndex); + } + + if (AlwaysOnTop && (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_VIEW_ALWAYSONTOP))) + menuItem->Flags |= PH_EMENU_CHECKED; + + id = PH_OPACITY_TO_ID(PhGetIntegerSetting(L"MainWindowOpacity")); + + if (menuItem = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, NULL, id)) + menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; + + switch (PhGetIntegerSetting(L"UpdateInterval")) + { + case 500: + id = ID_UPDATEINTERVAL_FAST; + break; + case 1000: + id = ID_UPDATEINTERVAL_NORMAL; + break; + case 2000: + id = ID_UPDATEINTERVAL_BELOWNORMAL; + break; + case 5000: + id = ID_UPDATEINTERVAL_SLOW; + break; + case 10000: + id = ID_UPDATEINTERVAL_VERYSLOW; + break; + default: + id = ULONG_MAX; + break; + } + + if (id != ULONG_MAX && (menuItem = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, NULL, id))) + menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; + + if (PhMwpUpdateAutomatically && (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_VIEW_UPDATEAUTOMATICALLY))) + menuItem->Flags |= PH_EMENU_CHECKED; + } + else if (Index == PH_MENU_ITEM_LOCATION_TOOLS) // Tools + { + if (!PhGetIntegerSetting(L"HiddenProcessesMenuEnabled")) + { + if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_TOOLS_HIDDENPROCESSES)) + PhDestroyEMenuItem(menuItem); + } + + // Windows 8 Task Manager requires elevation. + if (WindowsVersion >= WINDOWS_8 && !PhGetOwnTokenAttributes().Elevated) + { + HBITMAP shieldBitmap; + + if (shieldBitmap = PhGetShieldBitmap()) + { + if (menuItem = PhFindEMenuItem(Menu, 0, NULL, ID_TOOLS_STARTTASKMANAGER)) + menuItem->Bitmap = shieldBitmap; + } + } + } +} + +VOID PhMwpInitializeSectionMenuItems( + _In_ PPH_EMENU Menu, + _In_ ULONG StartIndex + ) +{ + if (CurrentPage) + { + PH_MAIN_TAB_PAGE_MENU_INFORMATION menuInfo; + + menuInfo.Menu = Menu; + menuInfo.StartIndex = StartIndex; + + if (!CurrentPage->Callback(CurrentPage, MainTabPageInitializeSectionMenuItems, &menuInfo, NULL)) + { + // Remove the extra separator. + PhRemoveEMenuItem(Menu, NULL, StartIndex); + } + } +} + +VOID PhMwpLayoutTabControl( + _Inout_ HDWP *DeferHandle + ) +{ + RECT rect; + + if (!LayoutPaddingValid) + { + PhMwpUpdateLayoutPadding(); + LayoutPaddingValid = TRUE; + } + + GetClientRect(PhMainWndHandle, &rect); + PhMwpApplyLayoutPadding(&rect, &LayoutPadding); + TabCtrl_AdjustRect(TabControlHandle, FALSE, &rect); + + if (CurrentPage && CurrentPage->WindowHandle) + { + *DeferHandle = DeferWindowPos(*DeferHandle, CurrentPage->WindowHandle, NULL, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + } +} + +VOID PhMwpNotifyTabControl( + _In_ NMHDR *Header + ) +{ + if (Header->code == TCN_SELCHANGING) + { + OldTabIndex = TabCtrl_GetCurSel(TabControlHandle); + } + else if (Header->code == TCN_SELCHANGE) + { + PhMwpSelectionChangedTabControl(OldTabIndex); + } +} + +VOID PhMwpSelectionChangedTabControl( + _In_ ULONG OldIndex + ) +{ + ULONG selectedIndex; + HDWP deferHandle; + ULONG i; + + selectedIndex = TabCtrl_GetCurSel(TabControlHandle); + + if (selectedIndex == OldIndex) + return; + + deferHandle = BeginDeferWindowPos(3); + + for (i = 0; i < PageList->Count; i++) + { + PPH_MAIN_TAB_PAGE page = PageList->Items[i]; + + page->Selected = page->Index == selectedIndex; + + if (page->Index == selectedIndex) + { + CurrentPage = page; + + // Create the tab page window if it doesn't exist. (wj32) + if (!page->WindowHandle && !page->CreateWindowCalled) + { + if (page->Callback(page, MainTabPageCreateWindow, &page->WindowHandle, NULL)) + { + page->CreateWindowCalled = TRUE; + PhInitializeWindowTheme(PhMainWndHandle, PhEnableThemeSupport); // TODO: Remove PhMainWndHandle enumeration (dmex) + } + + if (page->WindowHandle) + BringWindowToTop(page->WindowHandle); + if (PhTreeWindowFont) + page->Callback(page, MainTabPageFontChanged, PhTreeWindowFont, NULL); + } + + page->Callback(page, MainTabPageSelected, (PVOID)TRUE, NULL); + + if (page->WindowHandle) + { + deferHandle = DeferWindowPos(deferHandle, page->WindowHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); + SetFocus(page->WindowHandle); + } + } + else if (page->Index == OldIndex) + { + page->Callback(page, MainTabPageSelected, (PVOID)FALSE, NULL); + + if (page->WindowHandle) + { + deferHandle = DeferWindowPos(deferHandle, page->WindowHandle, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW_ONLY); + } + } + } + + PhMwpLayoutTabControl(&deferHandle); + + EndDeferWindowPos(deferHandle); + + if (PhPluginsEnabled) + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMainWindowTabChanged), IntToPtr(selectedIndex)); +} + +PPH_MAIN_TAB_PAGE PhMwpCreatePage( + _In_ PPH_MAIN_TAB_PAGE Template + ) +{ + PPH_MAIN_TAB_PAGE page; + PPH_STRING name; + HDWP deferHandle; + + page = PhAllocate(sizeof(PH_MAIN_TAB_PAGE)); + memset(page, 0, sizeof(PH_MAIN_TAB_PAGE)); + + page->Name = Template->Name; + page->Flags = Template->Flags; + page->Callback = Template->Callback; + page->Context = Template->Context; + + PhAddItemList(PageList, page); + + name = PhCreateString2(&page->Name); + page->Index = PhAddTabControlTab(TabControlHandle, MAXINT, name->Buffer); + PhDereferenceObject(name); + + page->Callback(page, MainTabPageCreate, NULL, NULL); + + // The tab control might need multiple lines, so we need to refresh the layout. + deferHandle = BeginDeferWindowPos(1); + PhMwpLayoutTabControl(&deferHandle); + EndDeferWindowPos(deferHandle); + + return page; +} + +VOID PhMwpSelectPage( + _In_ ULONG Index + ) +{ + INT oldIndex; + + oldIndex = TabCtrl_GetCurSel(TabControlHandle); + TabCtrl_SetCurSel(TabControlHandle, Index); + PhMwpSelectionChangedTabControl(oldIndex); +} + +PPH_MAIN_TAB_PAGE PhMwpFindPage( + _In_ PPH_STRINGREF Name + ) +{ + ULONG i; + + for (i = 0; i < PageList->Count; i++) + { + PPH_MAIN_TAB_PAGE page = PageList->Items[i]; + + if (PhEqualStringRef(&page->Name, Name, TRUE)) + return page; + } + + return NULL; +} + +PPH_MAIN_TAB_PAGE PhMwpCreateInternalPage( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MAIN_TAB_PAGE_CALLBACK Callback + ) +{ + PH_MAIN_TAB_PAGE page; + + memset(&page, 0, sizeof(PH_MAIN_TAB_PAGE)); + PhInitializeStringRef(&page.Name, Name); + page.Flags = Flags; + page.Callback = Callback; + + return PhMwpCreatePage(&page); +} + +VOID PhMwpNotifyAllPages( + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + ULONG i; + PPH_MAIN_TAB_PAGE page; + + for (i = 0; i < PageList->Count; i++) + { + page = PageList->Items[i]; + page->Callback(page, Message, Parameter1, Parameter2); + } +} + +static int __cdecl IconProcessesCpuUsageCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_PROCESS_ITEM processItem1 = *(PPH_PROCESS_ITEM *)elem1; + PPH_PROCESS_ITEM processItem2 = *(PPH_PROCESS_ITEM *)elem2; + + return -singlecmp(processItem1->CpuUsage, processItem2->CpuUsage); +} + +static int __cdecl IconProcessesNameCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_PROCESS_ITEM processItem1 = *(PPH_PROCESS_ITEM *)elem1; + PPH_PROCESS_ITEM processItem2 = *(PPH_PROCESS_ITEM *)elem2; + + return PhCompareString(processItem1->ProcessName, processItem2->ProcessName, TRUE); +} + +VOID PhAddMiniProcessMenuItems( + _Inout_ struct _PH_EMENU_ITEM *Menu, + _In_ HANDLE ProcessId + ) +{ + PPH_EMENU_ITEM priorityMenu; + PPH_EMENU_ITEM ioPriorityMenu = NULL; + PPH_PROCESS_ITEM processItem; + BOOLEAN isSuspended = FALSE; + BOOLEAN isPartiallySuspended = TRUE; + + // Priority + + priorityMenu = PhCreateEMenuItem(0, 0, L"&Priority", NULL, ProcessId); + + PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_REALTIME, L"&Real time", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_HIGH, L"&High", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_ABOVENORMAL, L"&Above normal", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_NORMAL, L"&Normal", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_BELOWNORMAL, L"&Below normal", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(priorityMenu, PhCreateEMenuItem(0, ID_PRIORITY_IDLE, L"&Idle", NULL, ProcessId), ULONG_MAX); + + // I/O priority + + ioPriorityMenu = PhCreateEMenuItem(0, 0, L"&I/O priority", NULL, ProcessId); + + PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_HIGH, L"&High", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_NORMAL, L"&Normal", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_LOW, L"&Low", NULL, ProcessId), ULONG_MAX); + PhInsertEMenuItem(ioPriorityMenu, PhCreateEMenuItem(0, ID_IOPRIORITY_VERYLOW, L"&Very low", NULL, ProcessId), ULONG_MAX); + + // Menu + + PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_TERMINATE, L"T&erminate", NULL, ProcessId), ULONG_MAX); + + if (processItem = PhReferenceProcessItem(ProcessId)) + { + isSuspended = (BOOLEAN)processItem->IsSuspended; + isPartiallySuspended = (BOOLEAN)processItem->IsPartiallySuspended; + PhDereferenceObject(processItem); + } + + if (!isSuspended) + PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_SUSPEND, L"&Suspend", NULL, ProcessId), ULONG_MAX); + if (isPartiallySuspended) + PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_RESUME, L"Res&ume", NULL, ProcessId), ULONG_MAX); + + PhInsertEMenuItem(Menu, priorityMenu, ULONG_MAX); + + if (ioPriorityMenu) + PhInsertEMenuItem(Menu, ioPriorityMenu, ULONG_MAX); + + PhMwpSetProcessMenuPriorityChecks(Menu, ProcessId, TRUE, TRUE, FALSE); + + PhInsertEMenuItem(Menu, PhCreateEMenuItem(0, ID_PROCESS_PROPERTIES, L"P&roperties", NULL, ProcessId), ULONG_MAX); +} + +BOOLEAN PhHandleMiniProcessMenuItem( + _Inout_ struct _PH_EMENU_ITEM *MenuItem + ) +{ + switch (MenuItem->Id) + { + case ID_PROCESS_TERMINATE: + case ID_PROCESS_SUSPEND: + case ID_PROCESS_RESUME: + case ID_PROCESS_PROPERTIES: + { + HANDLE processId = MenuItem->Context; + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(processId)) + { + switch (MenuItem->Id) + { + case ID_PROCESS_TERMINATE: + PhUiTerminateProcesses(PhMainWndHandle, &processItem, 1); + break; + case ID_PROCESS_SUSPEND: + PhUiSuspendProcesses(PhMainWndHandle, &processItem, 1); + break; + case ID_PROCESS_RESUME: + PhUiResumeProcesses(PhMainWndHandle, &processItem, 1); + break; + case ID_PROCESS_PROPERTIES: + ProcessHacker_ShowProcessProperties(PhMainWndHandle, processItem); + break; + } + + PhDereferenceObject(processItem); + } + else + { + PhShowError(PhMainWndHandle, L"The process does not exist."); + } + } + break; + case ID_PRIORITY_REALTIME: + case ID_PRIORITY_HIGH: + case ID_PRIORITY_ABOVENORMAL: + case ID_PRIORITY_NORMAL: + case ID_PRIORITY_BELOWNORMAL: + case ID_PRIORITY_IDLE: + { + HANDLE processId = MenuItem->Context; + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(processId)) + { + PhMwpExecuteProcessPriorityCommand(MenuItem->Id, &processItem, 1); + PhDereferenceObject(processItem); + } + else + { + PhShowError(PhMainWndHandle, L"The process does not exist."); + } + } + break; + case ID_IOPRIORITY_HIGH: + case ID_IOPRIORITY_NORMAL: + case ID_IOPRIORITY_LOW: + case ID_IOPRIORITY_VERYLOW: + { + HANDLE processId = MenuItem->Context; + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(processId)) + { + PhMwpExecuteProcessIoPriorityCommand(MenuItem->Id, &processItem, 1); + PhDereferenceObject(processItem); + } + else + { + PhShowError(PhMainWndHandle, L"The process does not exist."); + } + } + break; + } + + return FALSE; +} + +VOID PhMwpAddIconProcesses( + _In_ PPH_EMENU_ITEM Menu, + _In_ ULONG NumberOfProcesses + ) +{ + ULONG i; + PPH_PROCESS_ITEM *processItems; + ULONG numberOfProcessItems; + PPH_LIST processList; + PPH_PROCESS_ITEM processItem; + + PhEnumProcessItems(&processItems, &numberOfProcessItems); + processList = PhCreateList(numberOfProcessItems); + PhAddItemsList(processList, processItems, numberOfProcessItems); + + // Remove non-real processes. + for (i = 0; i < processList->Count; i++) + { + processItem = processList->Items[i]; + + if (!PH_IS_REAL_PROCESS_ID(processItem->ProcessId)) + { + PhRemoveItemList(processList, i); + i--; + } + } + + // Remove processes with zero CPU usage and those running as other users. + for (i = 0; i < processList->Count && processList->Count > NumberOfProcesses; i++) + { + processItem = processList->Items[i]; + + if ( + processItem->CpuUsage == 0 || + (processItem->Sid && !RtlEqualSid(processItem->Sid, PhGetOwnTokenAttributes().TokenSid)) + ) + { + PhRemoveItemList(processList, i); + i--; + } + } + + // Sort the processes by CPU usage and remove the extra processes at the end of the list. + qsort(processList->Items, processList->Count, sizeof(PVOID), IconProcessesCpuUsageCompare); + + if (processList->Count > NumberOfProcesses) + { + PhRemoveItemsList(processList, NumberOfProcesses, processList->Count - NumberOfProcesses); + } + + // Lastly, sort by name. + qsort(processList->Items, processList->Count, sizeof(PVOID), IconProcessesNameCompare); + + // Delete all menu items. + PhRemoveAllEMenuItems(Menu); + + // Add the processes. + + for (i = 0; i < processList->Count; i++) + { + PPH_EMENU_ITEM subMenu; + HBITMAP iconBitmap; + CLIENT_ID clientId; + PPH_STRING clientIdName; + PPH_STRING escapedName; + + processItem = processList->Items[i]; + + // Process + + clientId.UniqueProcess = processItem->ProcessId; + clientId.UniqueThread = NULL; + + clientIdName = PH_AUTO(PhGetClientIdName(&clientId)); + escapedName = PH_AUTO(PhEscapeStringForMenuPrefix(&clientIdName->sr)); + + subMenu = PhCreateEMenuItem( + 0, + 0, + escapedName->Buffer, + NULL, + processItem->ProcessId + ); + + if (processItem->SmallIcon) + { + iconBitmap = PhIconToBitmap(processItem->SmallIcon, PhSmallIconSize.X, PhSmallIconSize.Y); + } + else + { + HICON icon; + + PhGetStockApplicationIcon(&icon, NULL); + iconBitmap = PhIconToBitmap(icon, PhSmallIconSize.X, PhSmallIconSize.Y); + } + + subMenu->Bitmap = iconBitmap; + subMenu->Flags |= PH_EMENU_BITMAP_OWNED; // automatically destroy the bitmap when necessary + + PhAddMiniProcessMenuItems(subMenu, processItem->ProcessId); + PhInsertEMenuItem(Menu, subMenu, ULONG_MAX); + } + + PhDereferenceObject(processList); + PhDereferenceObjects(processItems, numberOfProcessItems); + PhFree(processItems); +} + +VOID PhShowIconContextMenu( + _In_ POINT Location + ) +{ + PPH_EMENU menu; + PPH_EMENU_ITEM item; + PH_PLUGIN_MENU_INFORMATION menuInfo; + ULONG numberOfProcesses; + ULONG id; + ULONG i; + + // This function seems to be called recursively under some circumstances. + // To reproduce: + // 1. Hold right mouse button on tray icon, then left click. + // 2. Make the menu disappear by clicking on the menu then clicking somewhere else. + // So, don't store any global state or bad things will happen. + + menu = PhCreateEMenu(); + PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_ICON), 0); + + // Check the Notifications menu items. + for (i = PH_NOTIFY_MINIMUM; i != PH_NOTIFY_MAXIMUM; i <<= 1) + { + if (PhMwpNotifyIconNotifyMask & i) + { + switch (i) + { + case PH_NOTIFY_PROCESS_CREATE: + id = ID_NOTIFICATIONS_NEWPROCESSES; + break; + case PH_NOTIFY_PROCESS_DELETE: + id = ID_NOTIFICATIONS_TERMINATEDPROCESSES; + break; + case PH_NOTIFY_SERVICE_CREATE: + id = ID_NOTIFICATIONS_NEWSERVICES; + break; + case PH_NOTIFY_SERVICE_DELETE: + id = ID_NOTIFICATIONS_DELETEDSERVICES; + break; + case PH_NOTIFY_SERVICE_START: + id = ID_NOTIFICATIONS_STARTEDSERVICES; + break; + case PH_NOTIFY_SERVICE_STOP: + id = ID_NOTIFICATIONS_STOPPEDSERVICES; + break; + } + + PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + } + } + + // Add processes to the menu. + + numberOfProcesses = PhGetIntegerSetting(L"IconProcesses"); + item = PhFindEMenuItem(menu, 0, L"Processes", 0); + + if (item) + PhMwpAddIconProcesses(item, numberOfProcesses); + + // Fix up the Computer menu. + PhMwpSetupComputerMenu(menu); + + // Give plugins a chance to modify the menu. + + if (PhPluginsEnabled) + { + PhPluginInitializeMenuInfo(&menuInfo, menu, PhMainWndHandle, 0); + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackIconMenuInitializing), &menuInfo); + } + + SetForegroundWindow(PhMainWndHandle); // window must be foregrounded so menu will disappear properly + item = PhShowEMenu( + menu, + PhMainWndHandle, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + Location.x, + Location.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + if (PhPluginsEnabled && !handled) + handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + handled = PhHandleMiniProcessMenuItem(item); + + if (!handled) + handled = PhMwpExecuteComputerCommand(PhMainWndHandle, item->Id); + + if (!handled) + { + switch (item->Id) + { + case ID_ICON_SHOWHIDEPROCESSHACKER: + SendMessage(PhMainWndHandle, WM_PH_TOGGLE_VISIBLE, 0, 0); + break; + case ID_ICON_SYSTEMINFORMATION: + SendMessage(PhMainWndHandle, WM_COMMAND, ID_VIEW_SYSTEMINFORMATION, 0); + break; + case ID_NOTIFICATIONS_ENABLEALL: + PhMwpNotifyIconNotifyMask |= PH_NOTIFY_VALID_MASK; + break; + case ID_NOTIFICATIONS_DISABLEALL: + PhMwpNotifyIconNotifyMask &= ~PH_NOTIFY_VALID_MASK; + break; + case ID_NOTIFICATIONS_NEWPROCESSES: + case ID_NOTIFICATIONS_TERMINATEDPROCESSES: + case ID_NOTIFICATIONS_NEWSERVICES: + case ID_NOTIFICATIONS_STARTEDSERVICES: + case ID_NOTIFICATIONS_STOPPEDSERVICES: + case ID_NOTIFICATIONS_DELETEDSERVICES: + { + ULONG bit; + + switch (item->Id) + { + case ID_NOTIFICATIONS_NEWPROCESSES: + bit = PH_NOTIFY_PROCESS_CREATE; + break; + case ID_NOTIFICATIONS_TERMINATEDPROCESSES: + bit = PH_NOTIFY_PROCESS_DELETE; + break; + case ID_NOTIFICATIONS_NEWSERVICES: + bit = PH_NOTIFY_SERVICE_CREATE; + break; + case ID_NOTIFICATIONS_STARTEDSERVICES: + bit = PH_NOTIFY_SERVICE_START; + break; + case ID_NOTIFICATIONS_STOPPEDSERVICES: + bit = PH_NOTIFY_SERVICE_STOP; + break; + case ID_NOTIFICATIONS_DELETEDSERVICES: + bit = PH_NOTIFY_SERVICE_DELETE; + break; + } + + PhMwpNotifyIconNotifyMask ^= bit; + } + break; + case ID_ICON_EXIT: + SendMessage(PhMainWndHandle, WM_COMMAND, ID_HACKER_EXIT, 0); + break; + } + } + } + + PhDestroyEMenu(menu); +} + +VOID PhShowIconNotification( + _In_ PWSTR Title, + _In_ PWSTR Text, + _In_ ULONG Flags + ) +{ + PhNfShowBalloonTip(Title, Text, 10, Flags); +} + +VOID PhShowDetailsForIconNotification( + VOID + ) +{ + switch (PhMwpLastNotificationType) + { + case PH_NOTIFY_PROCESS_CREATE: + { + PPH_PROCESS_NODE processNode; + + if (processNode = PhFindProcessNode(PhMwpLastNotificationDetails.ProcessId)) + { + ProcessHacker_SelectTabPage(PhMainWndHandle, PhMwpProcessesPage->Index); + ProcessHacker_SelectProcessNode(PhMainWndHandle, processNode); + ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); + } + } + break; + case PH_NOTIFY_SERVICE_CREATE: + case PH_NOTIFY_SERVICE_START: + case PH_NOTIFY_SERVICE_STOP: + { + PPH_SERVICE_ITEM serviceItem; + + if (PhMwpLastNotificationDetails.ServiceName && + (serviceItem = PhReferenceServiceItem(PhMwpLastNotificationDetails.ServiceName->Buffer))) + { + ProcessHacker_SelectTabPage(PhMainWndHandle, PhMwpServicesPage->Index); + ProcessHacker_SelectServiceItem(PhMainWndHandle, serviceItem); + ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); + + PhDereferenceObject(serviceItem); + } + } + break; + } +} + +VOID PhMwpClearLastNotificationDetails( + VOID + ) +{ + if (PhMwpLastNotificationType & + (PH_NOTIFY_SERVICE_CREATE | PH_NOTIFY_SERVICE_DELETE | PH_NOTIFY_SERVICE_START | PH_NOTIFY_SERVICE_STOP)) + { + PhClearReference(&PhMwpLastNotificationDetails.ServiceName); + } + + PhMwpLastNotificationType = 0; + memset(&PhMwpLastNotificationDetails, 0, sizeof(PhMwpLastNotificationDetails)); +} + +BOOLEAN PhMwpPluginNotifyEvent( + _In_ ULONG Type, + _In_ PVOID Parameter + ) +{ + PH_PLUGIN_NOTIFY_EVENT notifyEvent; + + notifyEvent.Type = Type; + notifyEvent.Handled = FALSE; + notifyEvent.Parameter = Parameter; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNotifyEvent), ¬ifyEvent); + + return notifyEvent.Handled; +} + +VOID PhMwpUpdateUsersMenu( + _In_ PPH_EMENU UsersMenu + ) +{ + PSESSIONIDW sessions; + ULONG numberOfSessions; + ULONG i; + + if (WinStationEnumerateW(NULL, &sessions, &numberOfSessions)) + { + for (i = 0; i < numberOfSessions; i++) + { + PPH_EMENU_ITEM userMenu; + PPH_STRING menuText; + PPH_STRING escapedMenuText; + WINSTATIONINFORMATION winStationInfo; + ULONG returnLength; + + if (!WinStationQueryInformationW( + NULL, + sessions[i].SessionId, + WinStationInformation, + &winStationInfo, + sizeof(WINSTATIONINFORMATION), + &returnLength + )) + { + winStationInfo.Domain[0] = UNICODE_NULL; + winStationInfo.UserName[0] = UNICODE_NULL; + } + + if (winStationInfo.Domain[0] == UNICODE_NULL || winStationInfo.UserName[0] == UNICODE_NULL) + { + // Probably the Services or RDP-Tcp session. + continue; + } + + menuText = PhFormatString( + L"%lu: %s\\%s", + sessions[i].SessionId, + winStationInfo.Domain, + winStationInfo.UserName + ); + escapedMenuText = PhEscapeStringForMenuPrefix(&menuText->sr); + PhDereferenceObject(menuText); + + userMenu = PhCreateEMenuItem( + PH_EMENU_TEXT_OWNED, + IDR_USER, + PhAllocateCopy(escapedMenuText->Buffer, escapedMenuText->Length + sizeof(WCHAR)), + NULL, + UlongToPtr(sessions[i].SessionId) + ); + PhLoadResourceEMenuItem(userMenu, PhInstanceHandle, MAKEINTRESOURCE(IDR_USER), 0); + PhInsertEMenuItem(UsersMenu, userMenu, ULONG_MAX); + + PhDereferenceObject(escapedMenuText); + } + + WinStationFreeMemory(sessions); + } +} diff --git a/ProcessHacker/mdump.c b/ProcessHacker/mdump.c index 25fa6284c8e6..f7303d13ac37 100644 --- a/ProcessHacker/mdump.c +++ b/ProcessHacker/mdump.c @@ -1,466 +1,410 @@ -/* - * Process Hacker - - * minidump writer - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -#include -#include -#include -#include - -#define WM_PH_MINIDUMP_STATUS_UPDATE (WM_APP + 301) - -#define PH_MINIDUMP_STATUS_UPDATE 1 -#define PH_MINIDUMP_COMPLETED 2 -#define PH_MINIDUMP_ERROR 3 - -typedef struct _PROCESS_MINIDUMP_CONTEXT -{ - HANDLE ProcessId; - PWSTR FileName; - MINIDUMP_TYPE DumpType; - BOOLEAN IsWow64; - - HANDLE ProcessHandle; - HANDLE FileHandle; - - HWND WindowHandle; - HANDLE ThreadHandle; - BOOLEAN Stop; - BOOLEAN Succeeded; - - ULONG LastTickCount; -} PROCESS_MINIDUMP_CONTEXT, *PPROCESS_MINIDUMP_CONTEXT; - -BOOLEAN PhpCreateProcessMiniDumpWithProgress( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ PWSTR FileName, - _In_ MINIDUMP_TYPE DumpType - ); - -INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -BOOLEAN PhUiCreateDumpFileProcess( - _In_ HWND hWnd, - _In_ PPH_PROCESS_ITEM Process - ) -{ - static PH_FILETYPE_FILTER filters[] = - { - { L"Dump files (*.dmp)", L"*.dmp" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateSaveFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, PhaConcatStrings2(Process->ProcessName->Buffer, L".dmp")->Buffer); - - if (!PhShowFileDialog(hWnd, fileDialog)) - { - PhFreeFileDialog(fileDialog); - return FALSE; - } - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - PhFreeFileDialog(fileDialog); - - return PhpCreateProcessMiniDumpWithProgress( - hWnd, - Process->ProcessId, - fileName->Buffer, - // task manager uses these flags - MiniDumpWithFullMemory | - MiniDumpWithHandleData | - MiniDumpWithUnloadedModules | - MiniDumpWithFullMemoryInfo | - MiniDumpWithThreadInfo - ); -} - -BOOLEAN PhpCreateProcessMiniDumpWithProgress( - _In_ HWND hWnd, - _In_ HANDLE ProcessId, - _In_ PWSTR FileName, - _In_ MINIDUMP_TYPE DumpType - ) -{ - NTSTATUS status; - PROCESS_MINIDUMP_CONTEXT context; - - memset(&context, 0, sizeof(PROCESS_MINIDUMP_CONTEXT)); - context.ProcessId = ProcessId; - context.FileName = FileName; - context.DumpType = DumpType; - - if (!NT_SUCCESS(status = PhOpenProcess( - &context.ProcessHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - ProcessId - ))) - { - PhShowStatus(hWnd, L"Unable to open the process", status, 0); - return FALSE; - } - -#ifdef _WIN64 - PhGetProcessIsWow64(context.ProcessHandle, &context.IsWow64); -#endif - - status = PhCreateFileWin32( - &context.FileHandle, - FileName, - FILE_GENERIC_WRITE | DELETE, - 0, - 0, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - { - PhShowStatus(hWnd, L"Unable to access the dump file", status, 0); - NtClose(context.ProcessHandle); - return FALSE; - } - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PROGRESS), - hWnd, - PhpProcessMiniDumpDlgProc, - (LPARAM)&context - ); - - NtClose(context.FileHandle); - NtClose(context.ProcessHandle); - - return context.Succeeded; -} - -static BOOL CALLBACK PhpProcessMiniDumpCallback( - _In_ PVOID CallbackParam, - _In_ const PMINIDUMP_CALLBACK_INPUT CallbackInput, - _Inout_ PMINIDUMP_CALLBACK_OUTPUT CallbackOutput - ) -{ - PPROCESS_MINIDUMP_CONTEXT context = CallbackParam; - PPH_STRING message = NULL; - - // Don't try to send status updates if we're creating a dump of the current process. - if (context->ProcessId == NtCurrentProcessId()) - return TRUE; - - // MiniDumpWriteDump seems to get bored of calling the callback - // after it begins dumping the process handles. The code is - // still here in case they fix this problem in the future. - - switch (CallbackInput->CallbackType) - { - case CancelCallback: - { - if (context->Stop) - CallbackOutput->Cancel = TRUE; - } - break; - case ModuleCallback: - { - message = PhFormatString(L"Processing module %s...", CallbackInput->Module.FullPath); - } - break; - case ThreadCallback: - { - message = PhFormatString(L"Processing thread %u...", CallbackInput->Thread.ThreadId); - } - break; - } - - if (message) - { - SendMessage( - context->WindowHandle, - WM_PH_MINIDUMP_STATUS_UPDATE, - PH_MINIDUMP_STATUS_UPDATE, - (LPARAM)message->Buffer - ); - PhDereferenceObject(message); - } - - return TRUE; -} - -NTSTATUS PhpProcessMiniDumpThreadStart( - _In_ PVOID Parameter - ) -{ - PPROCESS_MINIDUMP_CONTEXT context = Parameter; - MINIDUMP_CALLBACK_INFORMATION callbackInfo; - - callbackInfo.CallbackRoutine = PhpProcessMiniDumpCallback; - callbackInfo.CallbackParam = context; - -#ifdef _WIN64 - if (context->IsWow64) - { - if (PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE)) - { - NTSTATUS status; - PPH_STRING dbgHelpPath; - - dbgHelpPath = PhGetStringSetting(L"DbgHelpPath"); - PhSvcCallLoadDbgHelp(dbgHelpPath->Buffer); - PhDereferenceObject(dbgHelpPath); - - if (NT_SUCCESS(status = PhSvcCallWriteMiniDumpProcess( - context->ProcessHandle, - context->ProcessId, - context->FileHandle, - context->DumpType - ))) - { - context->Succeeded = TRUE; - } - else - { - // We may have an old version of dbghelp - in that case, try using minimal dump flags. - if (status == STATUS_INVALID_PARAMETER && NT_SUCCESS(status = PhSvcCallWriteMiniDumpProcess( - context->ProcessHandle, - context->ProcessId, - context->FileHandle, - MiniDumpWithFullMemory | MiniDumpWithHandleData - ))) - { - context->Succeeded = TRUE; - } - else - { - SendMessage( - context->WindowHandle, - WM_PH_MINIDUMP_STATUS_UPDATE, - PH_MINIDUMP_ERROR, - (LPARAM)PhNtStatusToDosError(status) - ); - } - } - - PhUiDisconnectFromPhSvc(); - - goto Completed; - } - else - { - if (PhShowMessage( - context->WindowHandle, - MB_YESNO | MB_ICONWARNING, - L"The process is 32-bit, but the 32-bit version of Process Hacker could not be located. " - L"A 64-bit dump will be created instead. Do you want to continue?" - ) == IDNO) - { - FILE_DISPOSITION_INFORMATION dispositionInfo; - IO_STATUS_BLOCK isb; - - dispositionInfo.DeleteFile = TRUE; - NtSetInformationFile( - context->FileHandle, - &isb, - &dispositionInfo, - sizeof(FILE_DISPOSITION_INFORMATION), - FileDispositionInformation - ); - - goto Completed; - } - } - } -#endif - - if (PhWriteMiniDumpProcess( - context->ProcessHandle, - context->ProcessId, - context->FileHandle, - context->DumpType, - NULL, - NULL, - &callbackInfo - )) - { - context->Succeeded = TRUE; - } - else - { - // We may have an old version of dbghelp - in that case, try using minimal dump flags. - if (GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) && PhWriteMiniDumpProcess( - context->ProcessHandle, - context->ProcessId, - context->FileHandle, - MiniDumpWithFullMemory | MiniDumpWithHandleData, - NULL, - NULL, - &callbackInfo - )) - { - context->Succeeded = TRUE; - } - else - { - SendMessage( - context->WindowHandle, - WM_PH_MINIDUMP_STATUS_UPDATE, - PH_MINIDUMP_ERROR, - (LPARAM)GetLastError() - ); - } - } - -#ifdef _WIN64 -Completed: -#endif - SendMessage( - context->WindowHandle, - WM_PH_MINIDUMP_STATUS_UPDATE, - PH_MINIDUMP_COMPLETED, - 0 - ); - - return STATUS_SUCCESS; -} - -INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPROCESS_MINIDUMP_CONTEXT context = (PPROCESS_MINIDUMP_CONTEXT)lParam; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, L"Creating the dump file..."); - - PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); - SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); - - context->WindowHandle = hwndDlg; - context->ThreadHandle = PhCreateThread(0, PhpProcessMiniDumpThreadStart, context); - - if (!context->ThreadHandle) - { - PhShowStatus(hwndDlg, L"Unable to create the minidump thread", 0, GetLastError()); - EndDialog(hwndDlg, IDCANCEL); - } - - SetTimer(hwndDlg, 1, 500, NULL); - } - break; - case WM_DESTROY: - { - PPROCESS_MINIDUMP_CONTEXT context; - - context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - NtClose(context->ThreadHandle); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - PPROCESS_MINIDUMP_CONTEXT context = - (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); - context->Stop = TRUE; - } - break; - } - } - break; - case WM_TIMER: - { - if (wParam == 1) - { - PPROCESS_MINIDUMP_CONTEXT context = - (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - ULONG currentTickCount; - - currentTickCount = GetTickCount(); - - if (currentTickCount - context->LastTickCount >= 2000) - { - // No status message update for 2 seconds. - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, - (PWSTR)L"Creating the dump file..."); - InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE); - - context->LastTickCount = currentTickCount; - } - } - } - break; - case WM_PH_MINIDUMP_STATUS_UPDATE: - { - PPROCESS_MINIDUMP_CONTEXT context; - - context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - switch (wParam) - { - case PH_MINIDUMP_STATUS_UPDATE: - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, (PWSTR)lParam); - InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE); - context->LastTickCount = GetTickCount(); - break; - case PH_MINIDUMP_ERROR: - PhShowStatus(hwndDlg, L"Unable to create the minidump", 0, (ULONG)lParam); - break; - case PH_MINIDUMP_COMPLETED: - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * minidump writer + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#define WM_PH_MINIDUMP_STATUS_UPDATE (WM_APP + 301) + +#define PH_MINIDUMP_STATUS_UPDATE 1 +#define PH_MINIDUMP_COMPLETED 2 +#define PH_MINIDUMP_ERROR 3 + +typedef struct _PROCESS_MINIDUMP_CONTEXT +{ + HANDLE ProcessId; + PWSTR FileName; + MINIDUMP_TYPE DumpType; + BOOLEAN IsWow64; + + HANDLE ProcessHandle; + HANDLE FileHandle; + + HWND WindowHandle; + BOOLEAN Stop; + BOOLEAN Succeeded; + + ULONG64 LastTickCount; +} PROCESS_MINIDUMP_CONTEXT, *PPROCESS_MINIDUMP_CONTEXT; + +BOOLEAN PhpCreateProcessMiniDumpWithProgress( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ PWSTR FileName, + _In_ MINIDUMP_TYPE DumpType + ); + +INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOLEAN PhUiCreateDumpFileProcess( + _In_ HWND hWnd, + _In_ PPH_PROCESS_ITEM Process + ) +{ + static PH_FILETYPE_FILTER filters[] = + { + { L"Dump files (*.dmp)", L"*.dmp" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateSaveFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, PhaConcatStrings2(Process->ProcessName->Buffer, L".dmp")->Buffer); + + if (!PhShowFileDialog(hWnd, fileDialog)) + { + PhFreeFileDialog(fileDialog); + return FALSE; + } + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + PhFreeFileDialog(fileDialog); + + return PhpCreateProcessMiniDumpWithProgress( + hWnd, + Process->ProcessId, + fileName->Buffer, + // task manager uses these flags + MiniDumpWithFullMemory | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo + ); +} + +BOOLEAN PhpCreateProcessMiniDumpWithProgress( + _In_ HWND hWnd, + _In_ HANDLE ProcessId, + _In_ PWSTR FileName, + _In_ MINIDUMP_TYPE DumpType + ) +{ + NTSTATUS status; + PROCESS_MINIDUMP_CONTEXT context; + + memset(&context, 0, sizeof(PROCESS_MINIDUMP_CONTEXT)); + context.ProcessId = ProcessId; + context.FileName = FileName; + context.DumpType = DumpType; + + if (!NT_SUCCESS(status = PhOpenProcess( + &context.ProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ProcessId + ))) + { + PhShowStatus(hWnd, L"Unable to open the process", status, 0); + return FALSE; + } + +#ifdef _WIN64 + PhGetProcessIsWow64(context.ProcessHandle, &context.IsWow64); +#endif + + status = PhCreateFileWin32( + &context.FileHandle, + FileName, + FILE_GENERIC_WRITE | DELETE, + 0, + 0, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + { + PhShowStatus(hWnd, L"Unable to access the dump file", status, 0); + NtClose(context.ProcessHandle); + return FALSE; + } + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PROGRESS), + hWnd, + PhpProcessMiniDumpDlgProc, + (LPARAM)&context + ); + + NtClose(context.FileHandle); + NtClose(context.ProcessHandle); + + return context.Succeeded; +} + +static BOOL CALLBACK PhpProcessMiniDumpCallback( + _In_ PVOID CallbackParam, + _In_ const PMINIDUMP_CALLBACK_INPUT CallbackInput, + _Inout_ PMINIDUMP_CALLBACK_OUTPUT CallbackOutput + ) +{ + PPROCESS_MINIDUMP_CONTEXT context = CallbackParam; + PPH_STRING message = NULL; + + // Don't try to send status updates if we're creating a dump of the current process. + if (context->ProcessId == NtCurrentProcessId()) + return TRUE; + + // MiniDumpWriteDump seems to get bored of calling the callback + // after it begins dumping the process handles. The code is + // still here in case they fix this problem in the future. + + switch (CallbackInput->CallbackType) + { + case CancelCallback: + { + if (context->Stop) + CallbackOutput->Cancel = TRUE; + } + break; + case ModuleCallback: + { + message = PhFormatString(L"Processing module %s...", CallbackInput->Module.FullPath); + } + break; + case ThreadCallback: + { + message = PhFormatString(L"Processing thread %u...", CallbackInput->Thread.ThreadId); + } + break; + } + + if (message) + { + SendMessage( + context->WindowHandle, + WM_PH_MINIDUMP_STATUS_UPDATE, + PH_MINIDUMP_STATUS_UPDATE, + (LPARAM)message->Buffer + ); + PhDereferenceObject(message); + } + + return TRUE; +} + +NTSTATUS PhpProcessMiniDumpThreadStart( + _In_ PVOID Parameter + ) +{ + PPROCESS_MINIDUMP_CONTEXT context = Parameter; + MINIDUMP_CALLBACK_INFORMATION callbackInfo; + + callbackInfo.CallbackRoutine = PhpProcessMiniDumpCallback; + callbackInfo.CallbackParam = context; + +#ifdef _WIN64 + if (context->IsWow64) + { + if (PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE)) + { + NTSTATUS status; + + if (NT_SUCCESS(status = PhSvcCallWriteMiniDumpProcess( + context->ProcessHandle, + context->ProcessId, + context->FileHandle, + context->DumpType + ))) + { + context->Succeeded = TRUE; + } + else + { + SendMessage( + context->WindowHandle, + WM_PH_MINIDUMP_STATUS_UPDATE, + PH_MINIDUMP_ERROR, + (LPARAM)PhNtStatusToDosError(status) + ); + } + + PhUiDisconnectFromPhSvc(); + + goto Completed; + } + else + { + if (PhShowMessage2( + context->WindowHandle, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_WARNING_ICON, + L"The 32-bit version of Process Hacker could not be located.", + L"A 64-bit dump will be created instead. Do you want to continue?" + ) == IDNO) + { + PhDeleteFile(context->FileHandle); + goto Completed; + } + } + } +#endif + + if (PhWriteMiniDumpProcess( + context->ProcessHandle, + context->ProcessId, + context->FileHandle, + context->DumpType, + NULL, + NULL, + &callbackInfo + )) + { + context->Succeeded = TRUE; + } + else + { + SendMessage( + context->WindowHandle, + WM_PH_MINIDUMP_STATUS_UPDATE, + PH_MINIDUMP_ERROR, + (LPARAM)GetLastError() + ); + } + +#ifdef _WIN64 +Completed: +#endif + SendMessage( + context->WindowHandle, + WM_PH_MINIDUMP_STATUS_UPDATE, + PH_MINIDUMP_COMPLETED, + 0 + ); + + return STATUS_SUCCESS; +} + +INT_PTR CALLBACK PhpProcessMiniDumpDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPROCESS_MINIDUMP_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PPROCESS_MINIDUMP_CONTEXT)lParam; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetDialogItemText(hwndDlg, IDC_PROGRESSTEXT, L"Creating the dump file..."); + + PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); + SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); + + context->WindowHandle = hwndDlg; + + PhCreateThread2(PhpProcessMiniDumpThreadStart, context); + + SetTimer(hwndDlg, 1, 500, NULL); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + { + EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); + context->Stop = TRUE; + } + break; + } + } + break; + case WM_TIMER: + { + if (wParam == 1) + { + ULONG64 currentTickCount; + + currentTickCount = NtGetTickCount64(); + + if (currentTickCount - context->LastTickCount >= 2000) + { + // No status message update for 2 seconds. + + PhSetDialogItemText(hwndDlg, IDC_PROGRESSTEXT, L"Creating the dump file..."); + + context->LastTickCount = currentTickCount; + } + } + } + break; + case WM_PH_MINIDUMP_STATUS_UPDATE: + { + switch (wParam) + { + case PH_MINIDUMP_STATUS_UPDATE: + PhSetDialogItemText(hwndDlg, IDC_PROGRESSTEXT, (PWSTR)lParam); + context->LastTickCount = NtGetTickCount64(); + break; + case PH_MINIDUMP_ERROR: + PhShowStatus(hwndDlg, L"Unable to create the minidump", 0, (ULONG)lParam); + break; + case PH_MINIDUMP_COMPLETED: + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/memedit.c b/ProcessHacker/memedit.c index b5c78285c8c3..ad83c8438a4a 100644 --- a/ProcessHacker/memedit.c +++ b/ProcessHacker/memedit.c @@ -1,536 +1,551 @@ -/* - * Process Hacker - - * memory editor window - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -#include -#include -#include - -#define WM_PH_SELECT_OFFSET (WM_APP + 300) - -typedef struct _MEMORY_EDITOR_CONTEXT -{ - PH_AVL_LINKS Links; - union - { - struct - { - HANDLE ProcessId; - PVOID BaseAddress; - SIZE_T RegionSize; - }; - ULONG_PTR Key[3]; - }; - HANDLE ProcessHandle; - HWND WindowHandle; - PH_LAYOUT_MANAGER LayoutManager; - HWND HexEditHandle; - PUCHAR Buffer; - ULONG SelectOffset; - PPH_STRING Title; - ULONG Flags; - - BOOLEAN LoadCompleted; - BOOLEAN WriteAccess; -} MEMORY_EDITOR_CONTEXT, *PMEMORY_EDITOR_CONTEXT; - -INT NTAPI PhpMemoryEditorCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ); - -INT_PTR CALLBACK PhpMemoryEditorDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PH_AVL_TREE PhMemoryEditorSet = PH_AVL_TREE_INIT(PhpMemoryEditorCompareFunction); -static RECT MinimumSize = { -1, -1, -1, -1 }; - -VOID PhShowMemoryEditorDialog( - _In_ HANDLE ProcessId, - _In_ PVOID BaseAddress, - _In_ SIZE_T RegionSize, - _In_ ULONG SelectOffset, - _In_ ULONG SelectLength, - _In_opt_ PPH_STRING Title, - _In_ ULONG Flags - ) -{ - PMEMORY_EDITOR_CONTEXT context; - MEMORY_EDITOR_CONTEXT lookupContext; - PPH_AVL_LINKS links; - - lookupContext.ProcessId = ProcessId; - lookupContext.BaseAddress = BaseAddress; - lookupContext.RegionSize = RegionSize; - - links = PhFindElementAvlTree(&PhMemoryEditorSet, &lookupContext.Links); - - if (!links) - { - context = PhAllocate(sizeof(MEMORY_EDITOR_CONTEXT)); - memset(context, 0, sizeof(MEMORY_EDITOR_CONTEXT)); - - context->ProcessId = ProcessId; - context->BaseAddress = BaseAddress; - context->RegionSize = RegionSize; - context->SelectOffset = SelectOffset; - PhSwapReference(&context->Title, Title); - context->Flags = Flags; - - context->WindowHandle = CreateDialogParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MEMEDIT), - NULL, - PhpMemoryEditorDlgProc, - (LPARAM)context - ); - - if (!context->LoadCompleted) - { - DestroyWindow(context->WindowHandle); - return; - } - - if (SelectOffset != -1) - PostMessage(context->WindowHandle, WM_PH_SELECT_OFFSET, SelectOffset, SelectLength); - - PhRegisterDialog(context->WindowHandle); - PhAddElementAvlTree(&PhMemoryEditorSet, &context->Links); - - ShowWindow(context->WindowHandle, SW_SHOW); - SetForegroundWindow(context->WindowHandle); - } - else - { - context = CONTAINING_RECORD(links, MEMORY_EDITOR_CONTEXT, Links); - - if (IsIconic(context->WindowHandle)) - ShowWindow(context->WindowHandle, SW_RESTORE); - else - SetForegroundWindow(context->WindowHandle); - - if (SelectOffset != -1) - PostMessage(context->WindowHandle, WM_PH_SELECT_OFFSET, SelectOffset, SelectLength); - - // Just in case. - if ((Flags & PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION) && ProcessId == NtCurrentProcessId()) - NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress); - } -} - -INT NTAPI PhpMemoryEditorCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ) -{ - PMEMORY_EDITOR_CONTEXT context1 = CONTAINING_RECORD(Links1, MEMORY_EDITOR_CONTEXT, Links); - PMEMORY_EDITOR_CONTEXT context2 = CONTAINING_RECORD(Links2, MEMORY_EDITOR_CONTEXT, Links); - - return memcmp(context1->Key, context2->Key, sizeof(context1->Key)); -} - -INT_PTR CALLBACK PhpMemoryEditorDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PMEMORY_EDITOR_CONTEXT context; - - if (uMsg != WM_INITDIALOG) - { - context = GetProp(hwndDlg, PhMakeContextAtom()); - } - else - { - context = (PMEMORY_EDITOR_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - - if (context->Title) - { - SetWindowText(hwndDlg, context->Title->Buffer); - } - else - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(context->ProcessId)) - { - SetWindowText(hwndDlg, PhaFormatString(L"%s (%u) (0x%Ix - 0x%Ix)", - processItem->ProcessName->Buffer, HandleToUlong(context->ProcessId), - context->BaseAddress, (ULONG_PTR)context->BaseAddress + context->RegionSize)->Buffer); - PhDereferenceObject(processItem); - } - } - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - - if (context->RegionSize > 1024 * 1024 * 1024) // 1 GB - { - PhShowError(NULL, L"Unable to edit the memory region because it is too large."); - return TRUE; - } - - if (!NT_SUCCESS(status = PhOpenProcess( - &context->ProcessHandle, - PROCESS_VM_READ, - context->ProcessId - ))) - { - PhShowStatus(NULL, L"Unable to open the process", status, 0); - return TRUE; - } - - context->Buffer = PhAllocatePage(context->RegionSize, NULL); - - if (!context->Buffer) - { - PhShowError(NULL, L"Unable to allocate memory for the buffer."); - return TRUE; - } - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - context->ProcessHandle, - context->BaseAddress, - context->Buffer, - context->RegionSize, - NULL - ))) - { - PhShowStatus(NULL, L"Unable to read memory", status, 0); - return TRUE; - } - - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_BYTESPERROW), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_GOTO), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_WRITE), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REREAD), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - - if (MinimumSize.left == -1) - { - RECT rect; - - rect.left = 0; - rect.top = 0; - rect.right = 290; - rect.bottom = 140; - MapDialogRect(hwndDlg, &rect); - MinimumSize = rect; - MinimumSize.left = 0; - } - - context->HexEditHandle = GetDlgItem(hwndDlg, IDC_MEMORY); - PhAddLayoutItem(&context->LayoutManager, context->HexEditHandle, NULL, PH_ANCHOR_ALL); - HexEdit_SetBuffer(context->HexEditHandle, context->Buffer, (ULONG)context->RegionSize); - - { - PH_RECTANGLE windowRectangle; - - windowRectangle.Position = PhGetIntegerPairSetting(L"MemEditPosition"); - windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MemEditSize", TRUE).Pair; - PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); - - MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top, - windowRectangle.Width, windowRectangle.Height, FALSE); - - // Implement cascading by saving an offsetted rectangle. - windowRectangle.Left += 20; - windowRectangle.Top += 20; - - PhSetIntegerPairSetting(L"MemEditPosition", windowRectangle.Position); - PhSetScalableIntegerPairSetting2(L"MemEditSize", windowRectangle.Size); - } - - { - PWSTR bytesPerRowStrings[7]; - ULONG i; - ULONG bytesPerRow; - - for (i = 0; i < sizeof(bytesPerRowStrings) / sizeof(PWSTR); i++) - bytesPerRowStrings[i] = PhaFormatString(L"%u bytes per row", 1 << (2 + i))->Buffer; - - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_BYTESPERROW), - bytesPerRowStrings, sizeof(bytesPerRowStrings) / sizeof(PWSTR)); - - bytesPerRow = PhGetIntegerSetting(L"MemEditBytesPerRow"); - - if (bytesPerRow >= 4) - { - HexEdit_SetBytesPerRow(context->HexEditHandle, bytesPerRow); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_BYTESPERROW), - PhaFormatString(L"%u bytes per row", bytesPerRow)->Buffer, FALSE); - } - } - - context->LoadCompleted = TRUE; - } - break; - case WM_DESTROY: - { - if (context->LoadCompleted) - { - PhSaveWindowPlacementToSetting(L"MemEditPosition", L"MemEditSize", hwndDlg); - PhRemoveElementAvlTree(&PhMemoryEditorSet, &context->Links); - PhUnregisterDialog(hwndDlg); - } - - RemoveProp(hwndDlg, PhMakeContextAtom()); - - PhDeleteLayoutManager(&context->LayoutManager); - - if (context->Buffer) PhFreePage(context->Buffer); - if (context->ProcessHandle) NtClose(context->ProcessHandle); - PhClearReference(&context->Title); - - if ((context->Flags & PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION) && context->ProcessId == NtCurrentProcessId()) - NtUnmapViewOfSection(NtCurrentProcess(), context->BaseAddress); - - PhFree(context); - } - break; - case WM_SHOWWINDOW: - { - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->HexEditHandle, TRUE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - DestroyWindow(hwndDlg); - break; - case IDC_SAVE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Binary files (*.bin)", L"*.bin" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_PROCESS_ITEM processItem; - - fileDialog = PhCreateSaveFileDialog(); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - if (!context->Title && (processItem = PhReferenceProcessItem(context->ProcessId))) - { - PhSetFileDialogFileName(fileDialog, - PhaFormatString(L"%s_0x%Ix-0x%Ix.bin", processItem->ProcessName->Buffer, - context->BaseAddress, context->RegionSize)->Buffer); - PhDereferenceObject(processItem); - } - else - { - PhSetFileDialogFileName(fileDialog, L"Memory.bin"); - } - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - NTSTATUS status; - PPH_STRING fileName; - PPH_FILE_STREAM fileStream; - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - - if (NT_SUCCESS(status = PhCreateFileStream( - &fileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - 0 - ))) - { - status = PhWriteFileStream(fileStream, context->Buffer, (ULONG)context->RegionSize); - PhDereferenceObject(fileStream); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); - } - - PhFreeFileDialog(fileDialog); - } - break; - case IDC_GOTO: - { - PPH_STRING selectedChoice = NULL; - - while (PhaChoiceDialog( - hwndDlg, - L"Go to Offset", - L"Enter an offset:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - L"MemEditGotoChoices" - )) - { - ULONG64 offset; - - if (selectedChoice->Length == 0) - continue; - - if (PhStringToInteger64(&selectedChoice->sr, 0, &offset)) - { - if (offset >= context->RegionSize) - { - PhShowError(hwndDlg, L"The offset is too large."); - continue; - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->HexEditHandle, TRUE); - HexEdit_SetSel(context->HexEditHandle, (LONG)offset, (LONG)offset); - break; - } - } - } - break; - case IDC_WRITE: - { - NTSTATUS status; - - if (!context->WriteAccess) - { - HANDLE processHandle; - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_VM_READ | PROCESS_VM_WRITE, - context->ProcessId - ))) - { - PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); - break; - } - - if (context->ProcessHandle) NtClose(context->ProcessHandle); - context->ProcessHandle = processHandle; - context->WriteAccess = TRUE; - } - - if (!NT_SUCCESS(status = NtWriteVirtualMemory( - context->ProcessHandle, - context->BaseAddress, - context->Buffer, - context->RegionSize, - NULL - ))) - { - PhShowStatus(hwndDlg, L"Unable to write memory", status, 0); - } - } - break; - case IDC_REREAD: - { - NTSTATUS status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - context->ProcessHandle, - context->BaseAddress, - context->Buffer, - context->RegionSize, - NULL - ))) - { - PhShowStatus(hwndDlg, L"Unable to read memory", status, 0); - } - - InvalidateRect(context->HexEditHandle, NULL, TRUE); - } - break; - case IDC_BYTESPERROW: - if (HIWORD(wParam) == CBN_SELCHANGE) - { - PPH_STRING bytesPerRowString = PhaGetDlgItemText(hwndDlg, IDC_BYTESPERROW); - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - ULONG64 bytesPerRow64; - - if (PhSplitStringRefAtChar(&bytesPerRowString->sr, ' ', &firstPart, &secondPart)) - { - if (PhStringToInteger64(&firstPart, 10, &bytesPerRow64)) - { - PhSetIntegerSetting(L"MemEditBytesPerRow", (ULONG)bytesPerRow64); - HexEdit_SetBytesPerRow(context->HexEditHandle, (ULONG)bytesPerRow64); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->HexEditHandle, TRUE); - } - } - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - case WM_PH_SELECT_OFFSET: - { - HexEdit_SetEditMode(context->HexEditHandle, EDIT_ASCII); - HexEdit_SetSel(context->HexEditHandle, (ULONG)wParam, (ULONG)wParam + (ULONG)lParam); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * memory editor window + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include + +#define WM_PH_SELECT_OFFSET (WM_APP + 300) + +typedef struct _MEMORY_EDITOR_CONTEXT +{ + PH_AVL_LINKS Links; + union + { + struct + { + HANDLE ProcessId; + PVOID BaseAddress; + SIZE_T RegionSize; + }; + ULONG_PTR Key[3]; + }; + HANDLE ProcessHandle; + HWND WindowHandle; + HWND OwnerHandle; + HWND HexEditHandle; + PH_LAYOUT_MANAGER LayoutManager; + + PUCHAR Buffer; + ULONG SelectOffset; + PPH_STRING Title; + ULONG Flags; + + BOOLEAN LoadCompleted; + BOOLEAN WriteAccess; +} MEMORY_EDITOR_CONTEXT, *PMEMORY_EDITOR_CONTEXT; + +INT NTAPI PhpMemoryEditorCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ); + +INT_PTR CALLBACK PhpMemoryEditorDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +PH_AVL_TREE PhMemoryEditorSet = PH_AVL_TREE_INIT(PhpMemoryEditorCompareFunction); +static RECT MinimumSize = { -1, -1, -1, -1 }; + +VOID PhShowMemoryEditorDialog( + _In_ HWND OwnerWindow, + _In_ HANDLE ProcessId, + _In_ PVOID BaseAddress, + _In_ SIZE_T RegionSize, + _In_ ULONG SelectOffset, + _In_ ULONG SelectLength, + _In_opt_ PPH_STRING Title, + _In_ ULONG Flags + ) +{ + PMEMORY_EDITOR_CONTEXT context; + MEMORY_EDITOR_CONTEXT lookupContext; + PPH_AVL_LINKS links; + + lookupContext.ProcessId = ProcessId; + lookupContext.BaseAddress = BaseAddress; + lookupContext.RegionSize = RegionSize; + + links = PhFindElementAvlTree(&PhMemoryEditorSet, &lookupContext.Links); + + if (!links) + { + context = PhAllocate(sizeof(MEMORY_EDITOR_CONTEXT)); + memset(context, 0, sizeof(MEMORY_EDITOR_CONTEXT)); + + context->OwnerHandle = OwnerWindow; + context->ProcessId = ProcessId; + context->BaseAddress = BaseAddress; + context->RegionSize = RegionSize; + context->SelectOffset = SelectOffset; + PhSwapReference(&context->Title, Title); + context->Flags = Flags; + + context->WindowHandle = CreateDialogParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MEMEDIT), + NULL, + PhpMemoryEditorDlgProc, + (LPARAM)context + ); + + if (!context->LoadCompleted) + { + DestroyWindow(context->WindowHandle); + return; + } + + if (SelectOffset != ULONG_MAX) + PostMessage(context->WindowHandle, WM_PH_SELECT_OFFSET, SelectOffset, SelectLength); + + PhRegisterDialog(context->WindowHandle); + PhAddElementAvlTree(&PhMemoryEditorSet, &context->Links); + + ShowWindow(context->WindowHandle, SW_SHOW); + SetForegroundWindow(context->WindowHandle); + } + else + { + context = CONTAINING_RECORD(links, MEMORY_EDITOR_CONTEXT, Links); + + if (IsMinimized(context->WindowHandle)) + ShowWindow(context->WindowHandle, SW_RESTORE); + else + SetForegroundWindow(context->WindowHandle); + + if (SelectOffset != ULONG_MAX) + PostMessage(context->WindowHandle, WM_PH_SELECT_OFFSET, SelectOffset, SelectLength); + + // Just in case. + if ((Flags & PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION) && ProcessId == NtCurrentProcessId()) + NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress); + } +} + +INT NTAPI PhpMemoryEditorCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ) +{ + PMEMORY_EDITOR_CONTEXT context1 = CONTAINING_RECORD(Links1, MEMORY_EDITOR_CONTEXT, Links); + PMEMORY_EDITOR_CONTEXT context2 = CONTAINING_RECORD(Links2, MEMORY_EDITOR_CONTEXT, Links); + + return memcmp(context1->Key, context2->Key, sizeof(context1->Key)); +} + +INT_PTR CALLBACK PhpMemoryEditorDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PMEMORY_EDITOR_CONTEXT context; + + if (uMsg != WM_INITDIALOG) + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + else + { + context = (PMEMORY_EDITOR_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + if (context->Title) + { + PhSetWindowText(hwndDlg, context->Title->Buffer); + } + else + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(context->ProcessId)) + { + PhSetWindowText(hwndDlg, PhaFormatString(L"%s (%u) (0x%Ix - 0x%Ix)", + processItem->ProcessName->Buffer, HandleToUlong(context->ProcessId), + context->BaseAddress, (ULONG_PTR)context->BaseAddress + context->RegionSize)->Buffer); + PhDereferenceObject(processItem); + } + } + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + + if (context->RegionSize > 1024 * 1024 * 1024) // 1 GB + { + PhShowError(context->OwnerHandle, L"Unable to edit the memory region because it is too large."); + return TRUE; + } + + if (!NT_SUCCESS(status = PhOpenProcess( + &context->ProcessHandle, + PROCESS_VM_READ, + context->ProcessId + ))) + { + PhShowStatus(context->OwnerHandle, L"Unable to open the process", status, 0); + return TRUE; + } + + context->Buffer = PhAllocatePage(context->RegionSize, NULL); + + if (!context->Buffer) + { + PhShowError(context->OwnerHandle, L"Unable to allocate memory for the buffer."); + return TRUE; + } + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + context->ProcessHandle, + context->BaseAddress, + context->Buffer, + context->RegionSize, + NULL + ))) + { + PhShowStatus(context->OwnerHandle, L"Unable to read memory", status, 0); + return TRUE; + } + + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, + PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, + PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_BYTESPERROW), NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_GOTO), NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_WRITE), NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REREAD), NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 290; + rect.bottom = 140; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + + context->HexEditHandle = GetDlgItem(hwndDlg, IDC_MEMORY); + PhAddLayoutItem(&context->LayoutManager, context->HexEditHandle, NULL, PH_ANCHOR_ALL); + HexEdit_SetBuffer(context->HexEditHandle, context->Buffer, (ULONG)context->RegionSize); + + { + PH_RECTANGLE windowRectangle; + + windowRectangle.Position = PhGetIntegerPairSetting(L"MemEditPosition"); + windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MemEditSize", TRUE).Pair; + PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); + + MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, FALSE); + + // Implement cascading by saving an offsetted rectangle. + windowRectangle.Left += 20; + windowRectangle.Top += 20; + + PhSetIntegerPairSetting(L"MemEditPosition", windowRectangle.Position); + PhSetScalableIntegerPairSetting2(L"MemEditSize", windowRectangle.Size); + } + + { + PWSTR bytesPerRowStrings[7]; + ULONG i; + ULONG bytesPerRow; + + for (i = 0; i < sizeof(bytesPerRowStrings) / sizeof(PWSTR); i++) + bytesPerRowStrings[i] = PhaFormatString(L"%u bytes per row", 1 << (2 + i))->Buffer; + + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_BYTESPERROW), + bytesPerRowStrings, sizeof(bytesPerRowStrings) / sizeof(PWSTR)); + + bytesPerRow = PhGetIntegerSetting(L"MemEditBytesPerRow"); + + if (bytesPerRow >= 4) + { + HexEdit_SetBytesPerRow(context->HexEditHandle, bytesPerRow); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_BYTESPERROW), + PhaFormatString(L"%u bytes per row", bytesPerRow)->Buffer, FALSE); + } + } + + context->LoadCompleted = TRUE; + } + break; + case WM_DESTROY: + { + if (context->LoadCompleted) + { + PhSaveWindowPlacementToSetting(L"MemEditPosition", L"MemEditSize", hwndDlg); + PhRemoveElementAvlTree(&PhMemoryEditorSet, &context->Links); + PhUnregisterDialog(hwndDlg); + } + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhDeleteLayoutManager(&context->LayoutManager); + + if (context->Buffer) PhFreePage(context->Buffer); + if (context->ProcessHandle) NtClose(context->ProcessHandle); + PhClearReference(&context->Title); + + if ((context->Flags & PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION) && context->ProcessId == NtCurrentProcessId()) + NtUnmapViewOfSection(NtCurrentProcess(), context->BaseAddress); + + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + PhSetDialogFocus(hwndDlg, context->HexEditHandle); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + case IDC_SAVE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Binary files (*.bin)", L"*.bin" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_PROCESS_ITEM processItem; + + fileDialog = PhCreateSaveFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + if (!context->Title && (processItem = PhReferenceProcessItem(context->ProcessId))) + { + PhSetFileDialogFileName(fileDialog, + PhaFormatString(L"%s_0x%Ix-0x%Ix.bin", processItem->ProcessName->Buffer, + context->BaseAddress, context->RegionSize)->Buffer); + PhDereferenceObject(processItem); + } + else + { + PhSetFileDialogFileName(fileDialog, L"Memory.bin"); + } + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + NTSTATUS status; + PPH_STRING fileName; + PPH_FILE_STREAM fileStream; + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + + if (NT_SUCCESS(status = PhCreateFileStream( + &fileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + 0 + ))) + { + status = PhWriteFileStream(fileStream, context->Buffer, (ULONG)context->RegionSize); + PhDereferenceObject(fileStream); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); + } + + PhFreeFileDialog(fileDialog); + } + break; + case IDC_GOTO: + { + PPH_STRING selectedChoice = NULL; + + while (PhaChoiceDialog( + hwndDlg, + L"Go to Offset", + L"Enter an offset:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + L"MemEditGotoChoices" + )) + { + ULONG64 offset; + + if (selectedChoice->Length == 0) + continue; + + if (PhStringToInteger64(&selectedChoice->sr, 0, &offset)) + { + if (offset >= context->RegionSize) + { + PhShowError(hwndDlg, L"The offset is too large."); + continue; + } + + PhSetDialogFocus(hwndDlg, context->HexEditHandle); + HexEdit_SetSel(context->HexEditHandle, (LONG)offset, (LONG)offset); + break; + } + } + } + break; + case IDC_WRITE: + { + NTSTATUS status; + + if (PhGetIntegerSetting(L"EnableWarnings") && !PhShowConfirmMessage( + hwndDlg, + L"write", + L"process memory", + L"Some programs may restrict access or ban your account when editing the memory of the process.", + FALSE + )) + { + break; + } + + if (!context->WriteAccess) + { + HANDLE processHandle; + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_VM_READ | PROCESS_VM_WRITE, + context->ProcessId + ))) + { + PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); + break; + } + + if (context->ProcessHandle) NtClose(context->ProcessHandle); + context->ProcessHandle = processHandle; + context->WriteAccess = TRUE; + } + + if (!NT_SUCCESS(status = NtWriteVirtualMemory( + context->ProcessHandle, + context->BaseAddress, + context->Buffer, + context->RegionSize, + NULL + ))) + { + PhShowStatus(hwndDlg, L"Unable to write memory", status, 0); + } + } + break; + case IDC_REREAD: + { + NTSTATUS status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + context->ProcessHandle, + context->BaseAddress, + context->Buffer, + context->RegionSize, + NULL + ))) + { + PhShowStatus(hwndDlg, L"Unable to read memory", status, 0); + } + + InvalidateRect(context->HexEditHandle, NULL, TRUE); + } + break; + case IDC_BYTESPERROW: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + PPH_STRING bytesPerRowString = PhaGetDlgItemText(hwndDlg, IDC_BYTESPERROW); + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + ULONG64 bytesPerRow64; + + if (PhSplitStringRefAtChar(&bytesPerRowString->sr, ' ', &firstPart, &secondPart)) + { + if (PhStringToInteger64(&firstPart, 10, &bytesPerRow64)) + { + PhSetIntegerSetting(L"MemEditBytesPerRow", (ULONG)bytesPerRow64); + HexEdit_SetBytesPerRow(context->HexEditHandle, (ULONG)bytesPerRow64); + PhSetDialogFocus(hwndDlg, context->HexEditHandle); + } + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + case WM_PH_SELECT_OFFSET: + { + HexEdit_SetEditMode(context->HexEditHandle, EDIT_ASCII); + HexEdit_SetSel(context->HexEditHandle, (ULONG)wParam, PtrToUlong(PTR_ADD_OFFSET(wParam, lParam))); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/memlist.c b/ProcessHacker/memlist.c index cc36d29727cf..f2748348cc8b 100644 --- a/ProcessHacker/memlist.c +++ b/ProcessHacker/memlist.c @@ -1,918 +1,1025 @@ -/* - * Process Hacker - - * memory region list - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include -#include -#include -#include - -VOID PhpClearMemoryList( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context - ); - -VOID PhpDestroyMemoryNode( - _In_ PPH_MEMORY_NODE MemoryNode - ); - -LONG PhpMemoryTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpMemoryTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -VOID PhInitializeMemoryList( - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle, - _Out_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - HWND hwnd; - - memset(Context, 0, sizeof(PH_MEMORY_LIST_CONTEXT)); - - Context->AllocationBaseNodeList = PhCreateList(100); - Context->RegionNodeList = PhCreateList(400); - - Context->ParentWindowHandle = ParentWindowHandle; - Context->TreeNewHandle = TreeNewHandle; - hwnd = TreeNewHandle; - PhSetControlTheme(hwnd, L"explorer"); - - TreeNew_SetCallback(hwnd, PhpMemoryTreeNewCallback, Context); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHMMTLC_BASEADDRESS, TRUE, L"Base address", 120, PH_ALIGN_LEFT, -2, 0); - PhAddTreeNewColumn(hwnd, PHMMTLC_TYPE, TRUE, L"Type", 90, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_SIZE, TRUE, L"Size", 80, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHMMTLC_PROTECTION, TRUE, L"Protection", 60, PH_ALIGN_LEFT, 2, 0); - PhAddTreeNewColumn(hwnd, PHMMTLC_USE, TRUE, L"Use", 200, PH_ALIGN_LEFT, 3, 0); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_TOTALWS, TRUE, L"Total WS", 80, PH_ALIGN_RIGHT, 4, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_PRIVATEWS, TRUE, L"Private WS", 80, PH_ALIGN_RIGHT, 5, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_SHAREABLEWS, TRUE, L"Shareable WS", 80, PH_ALIGN_RIGHT, 6, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_SHAREDWS, TRUE, L"Shared WS", 80, PH_ALIGN_RIGHT, 7, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_LOCKEDWS, TRUE, L"Locked WS", 80, PH_ALIGN_RIGHT, 8, DT_RIGHT, TRUE); - - PhAddTreeNewColumnEx(hwnd, PHMMTLC_COMMITTED, FALSE, L"Committed", 80, PH_ALIGN_RIGHT, 9, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMMTLC_PRIVATE, FALSE, L"Private", 80, PH_ALIGN_RIGHT, 10, DT_RIGHT, TRUE); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetTriState(hwnd, TRUE); - TreeNew_SetSort(hwnd, 0, NoSortOrder); - - PhCmInitializeManager(&Context->Cm, hwnd, PHMMTLC_MAXIMUM, PhpMemoryTreeNewPostSortFunction); -} - -VOID PhpClearMemoryList( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - ULONG i; - - for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) - PhpDestroyMemoryNode(Context->AllocationBaseNodeList->Items[i]); - for (i = 0; i < Context->RegionNodeList->Count; i++) - PhpDestroyMemoryNode(Context->RegionNodeList->Items[i]); - - PhClearList(Context->AllocationBaseNodeList); - PhClearList(Context->RegionNodeList); -} - -VOID PhDeleteMemoryList( - _In_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - PhCmDeleteManager(&Context->Cm); - - PhpClearMemoryList(Context); - PhDereferenceObject(Context->AllocationBaseNodeList); - PhDereferenceObject(Context->RegionNodeList); -} - -VOID PhLoadSettingsMemoryList( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhGetStringSetting(L"MemoryTreeListColumns"); - sortSettings = PhGetStringSetting(L"MemoryTreeListSort"); - PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSaveSettingsMemoryList( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); - PhSetStringSetting2(L"MemoryTreeListColumns", &settings->sr); - PhSetStringSetting2(L"MemoryTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSetOptionsMemoryList( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ BOOLEAN HideFreeRegions - ) -{ - ULONG i; - ULONG k; - BOOLEAN modified; - - if (Context->HideFreeRegions != HideFreeRegions) - { - PPH_LIST lists[2]; - - Context->HideFreeRegions = HideFreeRegions; - modified = FALSE; - lists[0] = Context->AllocationBaseNodeList; - lists[1] = Context->RegionNodeList; - - for (k = 0; k < 2; k++) - { - for (i = 0; i < lists[k]->Count; i++) - { - PPH_MEMORY_NODE node = lists[k]->Items[i]; - BOOLEAN visible; - - visible = TRUE; - - if (HideFreeRegions && (node->MemoryItem->State & MEM_FREE)) - visible = FALSE; - - if (node->Node.Visible != visible) - { - node->Node.Visible = visible; - modified = TRUE; - - if (!visible) - node->Node.Selected = FALSE; - } - } - } - - if (modified) - { - TreeNew_NodesStructured(Context->TreeNewHandle); - } - } -} - -VOID PhpDestroyMemoryNode( - _In_ PPH_MEMORY_NODE MemoryNode - ) -{ - PhEmCallObjectOperation(EmMemoryNodeType, MemoryNode, EmObjectDelete); - - PhClearReference(&MemoryNode->SizeText); - PhClearReference(&MemoryNode->UseText); - PhClearReference(&MemoryNode->TotalWsText); - PhClearReference(&MemoryNode->PrivateWsText); - PhClearReference(&MemoryNode->ShareableWsText); - PhClearReference(&MemoryNode->SharedWsText); - PhClearReference(&MemoryNode->LockedWsText); - PhClearReference(&MemoryNode->CommittedText); - PhClearReference(&MemoryNode->PrivateText); - - PhClearReference(&MemoryNode->Children); - PhDereferenceObject(MemoryNode->MemoryItem); - - PhFree(MemoryNode); -} - -PPH_MEMORY_NODE PhpAddAllocationBaseNode( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ PVOID AllocationBase - ) -{ - PPH_MEMORY_NODE memoryNode; - PPH_MEMORY_ITEM memoryItem; - - memoryNode = PhAllocate(PhEmGetObjectSize(EmMemoryNodeType, sizeof(PH_MEMORY_NODE))); - memset(memoryNode, 0, sizeof(PH_MEMORY_NODE)); - PhInitializeTreeNewNode(&memoryNode->Node); - memoryNode->Node.Expanded = FALSE; - - memoryNode->IsAllocationBase = TRUE; - memoryItem = PhCreateMemoryItem(); - memoryNode->MemoryItem = memoryItem; - - memoryItem->BaseAddress = AllocationBase; - memoryItem->AllocationBase = AllocationBase; - - memoryNode->Children = PhCreateList(1); - - memset(memoryNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMMTLC_MAXIMUM); - memoryNode->Node.TextCache = memoryNode->TextCache; - memoryNode->Node.TextCacheSize = PHMMTLC_MAXIMUM; - - PhAddItemList(Context->AllocationBaseNodeList, memoryNode); - - PhEmCallObjectOperation(EmMemoryNodeType, memoryNode, EmObjectCreate); - - return memoryNode; -} - -PPH_MEMORY_NODE PhpAddRegionNode( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ PPH_MEMORY_ITEM MemoryItem - ) -{ - PPH_MEMORY_NODE memoryNode; - - memoryNode = PhAllocate(PhEmGetObjectSize(EmMemoryNodeType, sizeof(PH_MEMORY_NODE))); - memset(memoryNode, 0, sizeof(PH_MEMORY_NODE)); - PhInitializeTreeNewNode(&memoryNode->Node); - - memoryNode->IsAllocationBase = FALSE; - memoryNode->MemoryItem = MemoryItem; - PhReferenceObject(MemoryItem); - - memset(memoryNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMMTLC_MAXIMUM); - memoryNode->Node.TextCache = memoryNode->TextCache; - memoryNode->Node.TextCacheSize = PHMMTLC_MAXIMUM; - - PhAddItemList(Context->RegionNodeList, memoryNode); - - PhEmCallObjectOperation(EmMemoryNodeType, memoryNode, EmObjectCreate); - - return memoryNode; -} - -VOID PhpCopyMemoryRegionTypeInfo( - _In_ PPH_MEMORY_ITEM Source, - _Inout_ PPH_MEMORY_ITEM Destination - ) -{ - if (Destination->RegionType == CustomRegion) - PhClearReference(&Destination->u.Custom.Text); - else if (Destination->RegionType == MappedFileRegion) - PhClearReference(&Destination->u.MappedFile.FileName); - - Destination->RegionType = Source->RegionType; - Destination->u = Source->u; - - if (Destination->RegionType == CustomRegion) - PhReferenceObject(Destination->u.Custom.Text); - else if (Destination->RegionType == MappedFileRegion) - PhReferenceObject(Destination->u.MappedFile.FileName); -} - -VOID PhReplaceMemoryList( - _Inout_ PPH_MEMORY_LIST_CONTEXT Context, - _In_opt_ PPH_MEMORY_ITEM_LIST List - ) -{ - PLIST_ENTRY listEntry; - PPH_MEMORY_NODE allocationBaseNode = NULL; - - PhpClearMemoryList(Context); - - if (!List) - { - TreeNew_NodesStructured(Context->TreeNewHandle); - return; - } - - for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink) - { - PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - PPH_MEMORY_NODE memoryNode; - - if (memoryItem->AllocationBaseItem == memoryItem) - allocationBaseNode = PhpAddAllocationBaseNode(Context, memoryItem->AllocationBase); - - memoryNode = PhpAddRegionNode(Context, memoryItem); - - if (Context->HideFreeRegions && (memoryItem->State & MEM_FREE)) - memoryNode->Node.Visible = FALSE; - - if (allocationBaseNode && memoryItem->AllocationBase == allocationBaseNode->MemoryItem->BaseAddress) - { - if (!(memoryItem->State & MEM_FREE)) - { - memoryNode->Parent = allocationBaseNode; - PhAddItemList(allocationBaseNode->Children, memoryNode); - } - - // Aggregate various statistics. - allocationBaseNode->MemoryItem->RegionSize += memoryItem->RegionSize; - allocationBaseNode->MemoryItem->CommittedSize += memoryItem->CommittedSize; - allocationBaseNode->MemoryItem->PrivateSize += memoryItem->PrivateSize; - allocationBaseNode->MemoryItem->TotalWorkingSetPages += memoryItem->TotalWorkingSetPages; - allocationBaseNode->MemoryItem->PrivateWorkingSetPages += memoryItem->PrivateWorkingSetPages; - allocationBaseNode->MemoryItem->SharedWorkingSetPages += memoryItem->SharedWorkingSetPages; - allocationBaseNode->MemoryItem->ShareableWorkingSetPages += memoryItem->ShareableWorkingSetPages; - allocationBaseNode->MemoryItem->LockedWorkingSetPages += memoryItem->LockedWorkingSetPages; - - if (memoryItem->AllocationBaseItem == memoryItem) - { - if (memoryItem->State & MEM_FREE) - allocationBaseNode->MemoryItem->State = MEM_FREE; - - allocationBaseNode->MemoryItem->Protect = memoryItem->AllocationProtect; - PhGetMemoryProtectionString(allocationBaseNode->MemoryItem->Protect, allocationBaseNode->ProtectionText); - allocationBaseNode->MemoryItem->Type = memoryItem->Type; - - if (memoryItem->RegionType != CustomRegion || memoryItem->u.Custom.PropertyOfAllocationBase) - PhpCopyMemoryRegionTypeInfo(memoryItem, allocationBaseNode->MemoryItem); - - if (Context->HideFreeRegions && (allocationBaseNode->MemoryItem->State & MEM_FREE)) - allocationBaseNode->Node.Visible = FALSE; - } - else - { - if (memoryItem->RegionType == UnknownRegion) - PhpCopyMemoryRegionTypeInfo(allocationBaseNode->MemoryItem, memoryItem); - } - } - - PhGetMemoryProtectionString(memoryItem->Protect, memoryNode->ProtectionText); - } - - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhUpdateMemoryNode( - _In_ PPH_MEMORY_LIST_CONTEXT Context, - _In_ PPH_MEMORY_NODE MemoryNode - ) -{ - PhGetMemoryProtectionString(MemoryNode->MemoryItem->Protect, MemoryNode->ProtectionText); - memset(MemoryNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMMTLC_MAXIMUM); - TreeNew_InvalidateNode(Context->TreeNewHandle, &MemoryNode->Node); -} - -PPH_STRING PhpGetMemoryRegionUseText( - _In_ PPH_MEMORY_ITEM MemoryItem - ) -{ - PH_MEMORY_REGION_TYPE type = MemoryItem->RegionType; - - switch (type) - { - case UnknownRegion: - return PhReferenceEmptyString(); - case CustomRegion: - PhReferenceObject(MemoryItem->u.Custom.Text); - return MemoryItem->u.Custom.Text; - case UnusableRegion: - return PhReferenceEmptyString(); - case MappedFileRegion: - PhReferenceObject(MemoryItem->u.MappedFile.FileName); - return MemoryItem->u.MappedFile.FileName; - case UserSharedDataRegion: - return PhCreateString(L"USER_SHARED_DATA"); - case PebRegion: - case Peb32Region: - return PhFormatString(L"PEB%s", type == Peb32Region ? L" 32-bit" : L""); - case TebRegion: - case Teb32Region: - return PhFormatString(L"TEB%s (thread %u)", - type == Teb32Region ? L" 32-bit" : L"", HandleToUlong(MemoryItem->u.Teb.ThreadId)); - case StackRegion: - case Stack32Region: - return PhFormatString(L"Stack%s (thread %u)", - type == Stack32Region ? L" 32-bit" : L"", HandleToUlong(MemoryItem->u.Stack.ThreadId)); - case HeapRegion: - case Heap32Region: - return PhFormatString(L"Heap%s (ID %u)", - type == Heap32Region ? L" 32-bit" : L"", (ULONG)MemoryItem->u.Heap.Index + 1); - case HeapSegmentRegion: - case HeapSegment32Region: - return PhFormatString(L"Heap segment%s (ID %u)", - type == HeapSegment32Region ? L" 32-bit" : L"", (ULONG)MemoryItem->u.HeapSegment.HeapItem->u.Heap.Index + 1); - case CfgBitmapRegion: - return PhFormatString(L"CFG Bitmap"); - default: - return PhReferenceEmptyString(); - } -} - -VOID PhpUpdateMemoryNodeUseText( - _Inout_ PPH_MEMORY_NODE MemoryNode - ) -{ - if (!MemoryNode->UseText) - MemoryNode->UseText = PhpGetMemoryRegionUseText(MemoryNode->MemoryItem); -} - -PPH_STRING PhpFormatSizeIfNonZero( - _In_ ULONG64 Size - ) -{ - if (Size != 0) - return PhFormatSize(Size, -1); - else - return NULL; -} - -#define SORT_FUNCTION(Column) PhpMemoryTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpMemoryTreeNewCompare##Column( \ - _In_ void *_context, \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_MEMORY_NODE node1 = *(PPH_MEMORY_NODE *)_elem1; \ - PPH_MEMORY_NODE node2 = *(PPH_MEMORY_NODE *)_elem2; \ - PPH_MEMORY_ITEM memoryItem1 = node1->MemoryItem; \ - PPH_MEMORY_ITEM memoryItem2 = node2->MemoryItem; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress); \ - \ - return PhModifySort(sortResult, ((PPH_MEMORY_LIST_CONTEXT)_context)->TreeNewSortOrder); \ -} - -LONG PhpMemoryTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - if (Result == 0) - Result = uintptrcmp((ULONG_PTR)((PPH_MEMORY_NODE)Node1)->MemoryItem->BaseAddress, (ULONG_PTR)((PPH_MEMORY_NODE)Node2)->MemoryItem->BaseAddress); - - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(BaseAddress) -{ - sortResult = uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Type) -{ - sortResult = uintcmp(memoryItem1->Type | memoryItem1->State, memoryItem2->Type | memoryItem2->State); - - if (sortResult == 0) - sortResult = intcmp(memoryItem1->RegionType, memoryItem2->RegionType); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Size) -{ - sortResult = uintptrcmp(memoryItem1->RegionSize, memoryItem2->RegionSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Protection) -{ - sortResult = PhCompareStringZ(node1->ProtectionText, node2->ProtectionText, FALSE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Use) -{ - PhpUpdateMemoryNodeUseText(node1); - PhpUpdateMemoryNodeUseText(node2); - sortResult = PhCompareStringWithNull(node1->UseText, node2->UseText, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(TotalWs) -{ - sortResult = uintptrcmp(memoryItem1->TotalWorkingSetPages, memoryItem2->TotalWorkingSetPages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PrivateWs) -{ - sortResult = uintptrcmp(memoryItem1->PrivateWorkingSetPages, memoryItem2->PrivateWorkingSetPages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ShareableWs) -{ - sortResult = uintptrcmp(memoryItem1->ShareableWorkingSetPages, memoryItem2->ShareableWorkingSetPages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(SharedWs) -{ - sortResult = uintptrcmp(memoryItem1->SharedWorkingSetPages, memoryItem2->SharedWorkingSetPages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LockedWs) -{ - sortResult = uintptrcmp(memoryItem1->LockedWorkingSetPages, memoryItem2->LockedWorkingSetPages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Committed) -{ - sortResult = uintptrcmp(memoryItem1->CommittedSize, memoryItem2->CommittedSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Private) -{ - sortResult = uintptrcmp(memoryItem1->PrivateSize, memoryItem2->PrivateSize); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpMemoryTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_MEMORY_LIST_CONTEXT context; - PPH_MEMORY_NODE node; - - context = Context; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - node = (PPH_MEMORY_NODE)getChildren->Node; - - if (context->TreeNewSortOrder == NoSortOrder) - { - if (!node) - { - getChildren->Children = (PPH_TREENEW_NODE *)context->AllocationBaseNodeList->Items; - getChildren->NumberOfChildren = context->AllocationBaseNodeList->Count; - } - else - { - getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; - getChildren->NumberOfChildren = node->Children->Count; - } - } - else - { - if (!node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(BaseAddress), - SORT_FUNCTION(Type), - SORT_FUNCTION(Size), - SORT_FUNCTION(Protection), - SORT_FUNCTION(Use), - SORT_FUNCTION(TotalWs), - SORT_FUNCTION(PrivateWs), - SORT_FUNCTION(ShareableWs), - SORT_FUNCTION(SharedWs), - SORT_FUNCTION(LockedWs), - SORT_FUNCTION(Committed), - SORT_FUNCTION(Private) - }; - int (__cdecl *sortFunction)(void *, const void *, const void *); - - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)context->RegionNodeList->Items, - context->RegionNodeList->Count, - context->TreeNewSortColumn, - context->TreeNewSortOrder, - &context->Cm - )) - { - if (context->TreeNewSortColumn < PHMMTLC_MAXIMUM) - sortFunction = sortFunctions[context->TreeNewSortColumn]; - else - sortFunction = NULL; - } - else - { - sortFunction = NULL; - } - - if (sortFunction) - { - qsort_s(context->RegionNodeList->Items, context->RegionNodeList->Count, sizeof(PVOID), sortFunction, context); - } - - getChildren->Children = (PPH_TREENEW_NODE *)context->RegionNodeList->Items; - getChildren->NumberOfChildren = context->RegionNodeList->Count; - } - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - node = (PPH_MEMORY_NODE)isLeaf->Node; - - if (context->TreeNewSortOrder == NoSortOrder) - isLeaf->IsLeaf = !node->Children || node->Children->Count == 0; - else - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_MEMORY_ITEM memoryItem; - - node = (PPH_MEMORY_NODE)getCellText->Node; - memoryItem = node->MemoryItem; - - switch (getCellText->Id) - { - case PHMMTLC_BASEADDRESS: - PhPrintPointer(node->BaseAddressText, memoryItem->BaseAddress); - PhInitializeStringRefLongHint(&getCellText->Text, node->BaseAddressText); - break; - case PHMMTLC_TYPE: - if (memoryItem->State & MEM_FREE) - { - if (memoryItem->RegionType == UnusableRegion) - PhInitializeStringRef(&getCellText->Text, L"Free (Unusable)"); - else - PhInitializeStringRef(&getCellText->Text, L"Free"); - } - else if (node->IsAllocationBase) - { - PhInitializeStringRefLongHint(&getCellText->Text, PhGetMemoryTypeString(memoryItem->Type)); - } - else - { - PH_FORMAT format[3]; - SIZE_T returnLength; - - PhInitFormatS(&format[0], PhGetMemoryTypeString(memoryItem->Type)); - PhInitFormatS(&format[1], L": "); - PhInitFormatS(&format[2], PhGetMemoryStateString(memoryItem->State)); - - if (PhFormatToBuffer(format, 3, node->TypeText, sizeof(node->TypeText), &returnLength)) - { - getCellText->Text.Buffer = node->TypeText; - getCellText->Text.Length = returnLength - sizeof(WCHAR); - } - } - break; - case PHMMTLC_SIZE: - PhMoveReference(&node->SizeText, PhFormatSize(memoryItem->RegionSize, -1)); - getCellText->Text = PhGetStringRef(node->SizeText); - break; - case PHMMTLC_PROTECTION: - PhInitializeStringRefLongHint(&getCellText->Text, node->ProtectionText); - break; - case PHMMTLC_USE: - PhpUpdateMemoryNodeUseText(node); - getCellText->Text = PhGetStringRef(node->UseText); - break; - case PHMMTLC_TOTALWS: - PhMoveReference(&node->TotalWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->TotalWorkingSetPages * PAGE_SIZE)); - getCellText->Text = PhGetStringRef(node->TotalWsText); - break; - case PHMMTLC_PRIVATEWS: - PhMoveReference(&node->PrivateWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->PrivateWorkingSetPages * PAGE_SIZE)); - getCellText->Text = PhGetStringRef(node->PrivateWsText); - break; - case PHMMTLC_SHAREABLEWS: - PhMoveReference(&node->ShareableWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->ShareableWorkingSetPages * PAGE_SIZE)); - getCellText->Text = PhGetStringRef(node->ShareableWsText); - break; - case PHMMTLC_SHAREDWS: - PhMoveReference(&node->SharedWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->SharedWorkingSetPages * PAGE_SIZE)); - getCellText->Text = PhGetStringRef(node->SharedWsText); - break; - case PHMMTLC_LOCKEDWS: - PhMoveReference(&node->LockedWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->LockedWorkingSetPages * PAGE_SIZE)); - getCellText->Text = PhGetStringRef(node->LockedWsText); - break; - case PHMMTLC_COMMITTED: - PhMoveReference(&node->CommittedText, PhpFormatSizeIfNonZero(memoryItem->CommittedSize)); - getCellText->Text = PhGetStringRef(node->CommittedText); - break; - case PHMMTLC_PRIVATE: - PhMoveReference(&node->PrivateText, PhpFormatSizeIfNonZero(memoryItem->PrivateSize)); - getCellText->Text = PhGetStringRef(node->PrivateText); - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - //PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; - //PPH_MEMORY_ITEM memoryItem; - - //node = (PPH_MEMORY_NODE)getNodeColor->Node; - //memoryItem = node->MemoryItem; - - //if (!memoryItem) - // ; // Dummy - //else if (PhCsUseColorRelocatedModules && (memoryItem->Protect & PAGE_EXECUTE_WRITECOPY)) - // getNodeColor->BackColor = PhCsColorRelocatedModules; - //else if (PhCsUseColorRelocatedModules && (memoryItem->Protect & PAGE_EXECUTE_READWRITE)) - // getNodeColor->BackColor = PhCsColorRelocatedModules; - //else if (PhCsUseColorSystemProcesses && (memoryItem->Type & MEM_PRIVATE)) - // getNodeColor->BackColor = PhCsColorSystemProcesses; - - //getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MEMORY_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(context->TreeNewHandle, 0, -1); - break; - case VK_RETURN: - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MEMORY_READWRITEMEMORY, 0); - break; - case VK_F5: - SendMessage(context->ParentWindowHandle, WM_COMMAND, IDC_REFRESH, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = NoSortOrder; - PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; - - node = (PPH_MEMORY_NODE)mouseEvent->Node; - - if (node && node->IsAllocationBase) - TreeNew_SetNodeExpanded(hwnd, node, !node->Node.Expanded); - else - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MEMORY_READWRITEMEMORY, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); - } - return TRUE; - case TreeNewGetDialogCode: - { - PULONG code = Parameter2; - - if (PtrToUlong(Parameter1) == VK_RETURN) - { - *code = DLGC_WANTMESSAGE; - return TRUE; - } - } - return FALSE; - } - - return FALSE; -} - -PPH_MEMORY_NODE PhGetSelectedMemoryNode( - _In_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - ULONG i; - - if (Context->TreeNewSortOrder == NoSortOrder) - { - for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) - { - PPH_MEMORY_NODE node = Context->AllocationBaseNodeList->Items[i]; - - if (node->Node.Selected) - return node; - } - } - - for (i = 0; i < Context->RegionNodeList->Count; i++) - { - PPH_MEMORY_NODE node = Context->RegionNodeList->Items[i]; - - if (node->Node.Selected) - return node; - } - - return NULL; -} - -VOID PhGetSelectedMemoryNodes( - _In_ PPH_MEMORY_LIST_CONTEXT Context, - _Out_ PPH_MEMORY_NODE **MemoryNodes, - _Out_ PULONG NumberOfMemoryNodes - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - if (Context->TreeNewSortOrder == NoSortOrder) - { - for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) - { - PPH_MEMORY_NODE node = Context->AllocationBaseNodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node); - } - } - - for (i = 0; i < Context->RegionNodeList->Count; i++) - { - PPH_MEMORY_NODE node = Context->RegionNodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node); - } - - *NumberOfMemoryNodes = (ULONG)array.Count; - *MemoryNodes = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllMemoryNodes( - _In_ PPH_MEMORY_LIST_CONTEXT Context - ) -{ - TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); -} +/* + * Process Hacker - + * memory region list + * + * Copyright (C) 2015 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +VOID PhpClearMemoryList( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context + ); + +VOID PhpDestroyMemoryNode( + _In_ PPH_MEMORY_NODE MemoryNode + ); + +LONG PhpMemoryTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpMemoryTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +VOID PhInitializeMemoryList( + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle, + _Out_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + memset(Context, 0, sizeof(PH_MEMORY_LIST_CONTEXT)); + + Context->AllocationBaseNodeList = PhCreateList(100); + Context->RegionNodeList = PhCreateList(400); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + PhSetControlTheme(TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(TreeNewHandle, PhpMemoryTreeNewCallback, Context); + + TreeNew_SetRedraw(TreeNewHandle, FALSE); + + // Default columns + PhAddTreeNewColumn(TreeNewHandle, PHMMTLC_BASEADDRESS, TRUE, L"Base address", 120, PH_ALIGN_LEFT, -2, 0); + PhAddTreeNewColumn(TreeNewHandle, PHMMTLC_TYPE, TRUE, L"Type", 90, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_SIZE, TRUE, L"Size", 80, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); + PhAddTreeNewColumn(TreeNewHandle, PHMMTLC_PROTECTION, TRUE, L"Protection", 60, PH_ALIGN_LEFT, 2, 0); + PhAddTreeNewColumn(TreeNewHandle, PHMMTLC_USE, TRUE, L"Use", 200, PH_ALIGN_LEFT, 3, 0); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_TOTALWS, TRUE, L"Total WS", 80, PH_ALIGN_RIGHT, 4, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_PRIVATEWS, TRUE, L"Private WS", 80, PH_ALIGN_RIGHT, 5, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_SHAREABLEWS, TRUE, L"Shareable WS", 80, PH_ALIGN_RIGHT, 6, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_SHAREDWS, TRUE, L"Shared WS", 80, PH_ALIGN_RIGHT, 7, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_LOCKEDWS, TRUE, L"Locked WS", 80, PH_ALIGN_RIGHT, 8, DT_RIGHT, TRUE); + + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_COMMITTED, FALSE, L"Committed", 80, PH_ALIGN_RIGHT, 9, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(TreeNewHandle, PHMMTLC_PRIVATE, FALSE, L"Private", 80, PH_ALIGN_RIGHT, 10, DT_RIGHT, TRUE); + + TreeNew_SetRedraw(TreeNewHandle, TRUE); + + TreeNew_SetTriState(TreeNewHandle, TRUE); + TreeNew_SetSort(TreeNewHandle, 0, NoSortOrder); + + PhCmInitializeManager(&Context->Cm, TreeNewHandle, PHMMTLC_MAXIMUM, PhpMemoryTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->AllocationTreeFilterSupport, TreeNewHandle, Context->AllocationBaseNodeList); + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, TreeNewHandle, Context->RegionNodeList); +} + +VOID PhpClearMemoryList( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + ULONG i; + + for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) + PhpDestroyMemoryNode(Context->AllocationBaseNodeList->Items[i]); + for (i = 0; i < Context->RegionNodeList->Count; i++) + PhpDestroyMemoryNode(Context->RegionNodeList->Items[i]); + + PhClearList(Context->AllocationBaseNodeList); + PhClearList(Context->RegionNodeList); +} + +VOID PhDeleteMemoryList( + _In_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + PhDeleteTreeNewFilterSupport(&Context->AllocationTreeFilterSupport); + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + + PhCmDeleteManager(&Context->Cm); + + PhpClearMemoryList(Context); + PhDereferenceObject(Context->AllocationBaseNodeList); + PhDereferenceObject(Context->RegionNodeList); +} + +VOID PhLoadSettingsMemoryList( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + ULONG flags; + PPH_STRING settings; + PPH_STRING sortSettings; + + flags = PhGetIntegerSetting(L"MemoryListFlags"); + settings = PhGetStringSetting(L"MemoryTreeListColumns"); + sortSettings = PhGetStringSetting(L"MemoryTreeListSort"); + + Context->Flags = flags; + PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSaveSettingsMemoryList( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); + + PhSetIntegerSetting(L"MemoryListFlags", Context->Flags); + PhSetStringSetting2(L"MemoryTreeListColumns", &settings->sr); + PhSetStringSetting2(L"MemoryTreeListSort", &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSetOptionsMemoryList( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ ULONG Options + ) +{ + switch (Options) + { + case PH_MEMORY_FLAGS_FREE_OPTION: + Context->HideFreeRegions = !Context->HideFreeRegions; + break; + case PH_MEMORY_FLAGS_RESERVED_OPTION: + Context->HideReservedRegions = !Context->HideReservedRegions; + break; + case PH_MEMORY_FLAGS_PRIVATE_OPTION: + Context->HighlightPrivatePages = !Context->HighlightPrivatePages; + break; + case PH_MEMORY_FLAGS_SYSTEM_OPTION: + Context->HighlightSystemPages = !Context->HighlightSystemPages; + break; + case PH_MEMORY_FLAGS_CFG_OPTION: + Context->HighlightCfgPages = !Context->HighlightCfgPages; + break; + case PH_MEMORY_FLAGS_EXECUTE_OPTION: + Context->HighlightExecutePages = !Context->HighlightExecutePages; + break; + case PH_MEMORY_FLAGS_GUARD_OPTION: + Context->HideGuardRegions = !Context->HideGuardRegions; + break; + } +} + +VOID PhpDestroyMemoryNode( + _In_ PPH_MEMORY_NODE MemoryNode + ) +{ + PhEmCallObjectOperation(EmMemoryNodeType, MemoryNode, EmObjectDelete); + + PhClearReference(&MemoryNode->SizeText); + PhClearReference(&MemoryNode->UseText); + PhClearReference(&MemoryNode->TotalWsText); + PhClearReference(&MemoryNode->PrivateWsText); + PhClearReference(&MemoryNode->ShareableWsText); + PhClearReference(&MemoryNode->SharedWsText); + PhClearReference(&MemoryNode->LockedWsText); + PhClearReference(&MemoryNode->CommittedText); + PhClearReference(&MemoryNode->PrivateText); + + PhClearReference(&MemoryNode->Children); + PhDereferenceObject(MemoryNode->MemoryItem); + + PhFree(MemoryNode); +} + +PPH_MEMORY_NODE PhpAddAllocationBaseNode( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ PVOID AllocationBase + ) +{ + PPH_MEMORY_NODE memoryNode; + PPH_MEMORY_ITEM memoryItem; + + memoryNode = PhAllocate(PhEmGetObjectSize(EmMemoryNodeType, sizeof(PH_MEMORY_NODE))); + memset(memoryNode, 0, sizeof(PH_MEMORY_NODE)); + PhInitializeTreeNewNode(&memoryNode->Node); + memoryNode->Node.Expanded = FALSE; + + memoryNode->IsAllocationBase = TRUE; + memoryItem = PhCreateMemoryItem(); + memoryNode->MemoryItem = memoryItem; + + memoryItem->BaseAddress = AllocationBase; + memoryItem->AllocationBase = AllocationBase; + + memoryNode->Children = PhCreateList(1); + + memset(memoryNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMMTLC_MAXIMUM); + memoryNode->Node.TextCache = memoryNode->TextCache; + memoryNode->Node.TextCacheSize = PHMMTLC_MAXIMUM; + + PhAddItemList(Context->AllocationBaseNodeList, memoryNode); + + if (Context->AllocationTreeFilterSupport.FilterList) + memoryNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->AllocationTreeFilterSupport, &memoryNode->Node); + + PhEmCallObjectOperation(EmMemoryNodeType, memoryNode, EmObjectCreate); + + return memoryNode; +} + +PPH_MEMORY_NODE PhpAddRegionNode( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ PPH_MEMORY_ITEM MemoryItem + ) +{ + PPH_MEMORY_NODE memoryNode; + + memoryNode = PhAllocate(PhEmGetObjectSize(EmMemoryNodeType, sizeof(PH_MEMORY_NODE))); + memset(memoryNode, 0, sizeof(PH_MEMORY_NODE)); + PhInitializeTreeNewNode(&memoryNode->Node); + + memoryNode->IsAllocationBase = FALSE; + memoryNode->MemoryItem = MemoryItem; + PhReferenceObject(MemoryItem); + + memset(memoryNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMMTLC_MAXIMUM); + memoryNode->Node.TextCache = memoryNode->TextCache; + memoryNode->Node.TextCacheSize = PHMMTLC_MAXIMUM; + + PhAddItemList(Context->RegionNodeList, memoryNode); + + if (Context->TreeFilterSupport.FilterList) + memoryNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &memoryNode->Node); + + PhEmCallObjectOperation(EmMemoryNodeType, memoryNode, EmObjectCreate); + + return memoryNode; +} + +VOID PhpCopyMemoryRegionTypeInfo( + _In_ PPH_MEMORY_ITEM Source, + _Inout_ PPH_MEMORY_ITEM Destination + ) +{ + if (Destination->RegionType == CustomRegion) + PhClearReference(&Destination->u.Custom.Text); + else if (Destination->RegionType == MappedFileRegion) + PhClearReference(&Destination->u.MappedFile.FileName); + + Destination->RegionType = Source->RegionType; + Destination->u = Source->u; + + if (Destination->RegionType == CustomRegion) + PhReferenceObject(Destination->u.Custom.Text); + else if (Destination->RegionType == MappedFileRegion) + PhReferenceObject(Destination->u.MappedFile.FileName); +} + +VOID PhReplaceMemoryList( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context, + _In_opt_ PPH_MEMORY_ITEM_LIST List + ) +{ + PLIST_ENTRY listEntry; + PPH_MEMORY_NODE allocationBaseNode = NULL; + + PhpClearMemoryList(Context); + + if (!List) + { + TreeNew_NodesStructured(Context->TreeNewHandle); + return; + } + + for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink) + { + PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + PPH_MEMORY_NODE memoryNode; + + if (memoryItem->AllocationBaseItem == memoryItem) + allocationBaseNode = PhpAddAllocationBaseNode(Context, memoryItem->AllocationBase); + + memoryNode = PhpAddRegionNode(Context, memoryItem); + + if (Context->HideFreeRegions && (memoryItem->State & MEM_FREE)) + memoryNode->Node.Visible = FALSE; + if (Context->HideGuardRegions && (memoryItem->Protect & PAGE_GUARD)) + memoryNode->Node.Visible = FALSE; + + if (allocationBaseNode && memoryItem->AllocationBase == allocationBaseNode->MemoryItem->BaseAddress) + { + if (!(memoryItem->State & MEM_FREE)) + { + memoryNode->Parent = allocationBaseNode; + PhAddItemList(allocationBaseNode->Children, memoryNode); + } + + // Aggregate various statistics. + allocationBaseNode->MemoryItem->RegionSize += memoryItem->RegionSize; + allocationBaseNode->MemoryItem->CommittedSize += memoryItem->CommittedSize; + allocationBaseNode->MemoryItem->PrivateSize += memoryItem->PrivateSize; + allocationBaseNode->MemoryItem->TotalWorkingSetPages += memoryItem->TotalWorkingSetPages; + allocationBaseNode->MemoryItem->PrivateWorkingSetPages += memoryItem->PrivateWorkingSetPages; + allocationBaseNode->MemoryItem->SharedWorkingSetPages += memoryItem->SharedWorkingSetPages; + allocationBaseNode->MemoryItem->ShareableWorkingSetPages += memoryItem->ShareableWorkingSetPages; + allocationBaseNode->MemoryItem->LockedWorkingSetPages += memoryItem->LockedWorkingSetPages; + + if (memoryItem->AllocationBaseItem == memoryItem) + { + allocationBaseNode->MemoryItem->Protect = memoryItem->AllocationProtect; + allocationBaseNode->MemoryItem->State = memoryItem->State; + allocationBaseNode->MemoryItem->Type = memoryItem->Type; + + PhGetMemoryProtectionString(allocationBaseNode->MemoryItem->Protect, allocationBaseNode->ProtectionText); + + if (memoryItem->RegionType != CustomRegion || memoryItem->u.Custom.PropertyOfAllocationBase) + PhpCopyMemoryRegionTypeInfo(memoryItem, allocationBaseNode->MemoryItem); + + if (Context->HideFreeRegions && (allocationBaseNode->MemoryItem->State & MEM_FREE)) + allocationBaseNode->Node.Visible = FALSE; + if (Context->HideGuardRegions && (allocationBaseNode->MemoryItem->State & PAGE_GUARD)) + memoryNode->Node.Visible = FALSE; + } + else + { + if (memoryItem->RegionType == UnknownRegion) + PhpCopyMemoryRegionTypeInfo(allocationBaseNode->MemoryItem, memoryItem); + } + } + + PhGetMemoryProtectionString(memoryItem->Protect, memoryNode->ProtectionText); + } + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhRemoveMemoryNode( + _Inout_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ PPH_MEMORY_NODE MemoryNode + ) +{ + ULONG index; + + // Remove from list and cleanup. + + PhRemoveElementAvlTree(&List->Set, &MemoryNode->MemoryItem->Links); + RemoveEntryList(&MemoryNode->MemoryItem->ListEntry); + + if ((index = PhFindItemList(Context->RegionNodeList, MemoryNode)) != -1) + PhRemoveItemList(Context->RegionNodeList, index); + + if (MemoryNode->MemoryItem->AllocationBaseItem == MemoryNode->MemoryItem) + { + if ((index = PhFindItemList(Context->AllocationBaseNodeList, MemoryNode->Parent)) != -1) + PhRemoveItemList(Context->AllocationBaseNodeList, index); + } + + PhpDestroyMemoryNode(MemoryNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhUpdateMemoryNode( + _In_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ PPH_MEMORY_NODE MemoryNode + ) +{ + PhGetMemoryProtectionString(MemoryNode->MemoryItem->Protect, MemoryNode->ProtectionText); + memset(MemoryNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMMTLC_MAXIMUM); + TreeNew_InvalidateNode(Context->TreeNewHandle, &MemoryNode->Node); +} + +VOID PhExpandAllMemoryNodes( + _In_ PPH_MEMORY_LIST_CONTEXT Context, + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) + { + PPH_MEMORY_NODE node = Context->AllocationBaseNodeList->Items[i]; + + if (node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + for (i = 0; i < Context->RegionNodeList->Count; i++) + { + PPH_MEMORY_NODE node = Context->RegionNodeList->Items[i]; + + if (node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +PPH_STRING PhGetMemoryRegionUseText( + _In_ PPH_MEMORY_ITEM MemoryItem + ) +{ + PH_MEMORY_REGION_TYPE type = MemoryItem->RegionType; + + switch (type) + { + case UnknownRegion: + return PhReferenceEmptyString(); + case CustomRegion: + PhReferenceObject(MemoryItem->u.Custom.Text); + return MemoryItem->u.Custom.Text; + case UnusableRegion: + return PhReferenceEmptyString(); + case MappedFileRegion: + PhReferenceObject(MemoryItem->u.MappedFile.FileName); + return MemoryItem->u.MappedFile.FileName; + case UserSharedDataRegion: + return PhCreateString(L"USER_SHARED_DATA"); + case HypervisorSharedDataRegion: + return PhCreateString(L"HYPERVISOR_SHARED_DATA"); + case PebRegion: + case Peb32Region: + return PhFormatString(L"PEB%s", type == Peb32Region ? L" 32-bit" : L""); + case TebRegion: + case Teb32Region: + return PhFormatString(L"TEB%s (thread %lu)", + type == Teb32Region ? L" 32-bit" : L"", HandleToUlong(MemoryItem->u.Teb.ThreadId)); + case StackRegion: + case Stack32Region: + return PhFormatString(L"Stack%s (thread %lu)", + type == Stack32Region ? L" 32-bit" : L"", HandleToUlong(MemoryItem->u.Stack.ThreadId)); + case HeapRegion: + case Heap32Region: + return PhFormatString(L"Heap%s (ID %lu)", + type == Heap32Region ? L" 32-bit" : L"", (ULONG)MemoryItem->u.Heap.Index + 1); + case HeapSegmentRegion: + case HeapSegment32Region: + return PhFormatString(L"Heap segment%s (ID %lu)", + type == HeapSegment32Region ? L" 32-bit" : L"", (ULONG)MemoryItem->u.HeapSegment.HeapItem->u.Heap.Index + 1); + case CfgBitmapRegion: + case CfgBitmap32Region: + return PhFormatString(L"CFG Bitmap%s", + type == CfgBitmap32Region ? L" 32-bit" : L""); + case ApiSetMapRegion: + return PhFormatString(L"ApiSetMap"); + default: + return PhReferenceEmptyString(); + } +} + +VOID PhpUpdateMemoryNodeUseText( + _Inout_ PPH_MEMORY_NODE MemoryNode + ) +{ + if (!MemoryNode->UseText) + MemoryNode->UseText = PhGetMemoryRegionUseText(MemoryNode->MemoryItem); +} + +PPH_STRING PhpFormatSizeIfNonZero( + _In_ ULONG64 Size + ) +{ + if (Size != 0) + return PhFormatSize(Size, -1); + else + return NULL; +} + +#define SORT_FUNCTION(Column) PhpMemoryTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpMemoryTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_MEMORY_NODE node1 = *(PPH_MEMORY_NODE *)_elem1; \ + PPH_MEMORY_NODE node2 = *(PPH_MEMORY_NODE *)_elem2; \ + PPH_MEMORY_ITEM memoryItem1 = node1->MemoryItem; \ + PPH_MEMORY_ITEM memoryItem2 = node2->MemoryItem; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress); \ + \ + return PhModifySort(sortResult, ((PPH_MEMORY_LIST_CONTEXT)_context)->TreeNewSortOrder); \ +} + +LONG PhpMemoryTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = uintptrcmp((ULONG_PTR)((PPH_MEMORY_NODE)Node1)->MemoryItem->BaseAddress, (ULONG_PTR)((PPH_MEMORY_NODE)Node2)->MemoryItem->BaseAddress); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(BaseAddress) +{ + sortResult = uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Type) +{ + sortResult = uintcmp(memoryItem1->Type | memoryItem1->State, memoryItem2->Type | memoryItem2->State); + + if (sortResult == 0) + sortResult = intcmp(memoryItem1->RegionType, memoryItem2->RegionType); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Size) +{ + sortResult = uintptrcmp(memoryItem1->RegionSize, memoryItem2->RegionSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Protection) +{ + sortResult = PhCompareStringZ(node1->ProtectionText, node2->ProtectionText, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Use) +{ + PhpUpdateMemoryNodeUseText(node1); + PhpUpdateMemoryNodeUseText(node2); + sortResult = PhCompareStringWithNull(node1->UseText, node2->UseText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TotalWs) +{ + sortResult = uintptrcmp(memoryItem1->TotalWorkingSetPages, memoryItem2->TotalWorkingSetPages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PrivateWs) +{ + sortResult = uintptrcmp(memoryItem1->PrivateWorkingSetPages, memoryItem2->PrivateWorkingSetPages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ShareableWs) +{ + sortResult = uintptrcmp(memoryItem1->ShareableWorkingSetPages, memoryItem2->ShareableWorkingSetPages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(SharedWs) +{ + sortResult = uintptrcmp(memoryItem1->SharedWorkingSetPages, memoryItem2->SharedWorkingSetPages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LockedWs) +{ + sortResult = uintptrcmp(memoryItem1->LockedWorkingSetPages, memoryItem2->LockedWorkingSetPages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Committed) +{ + sortResult = uintptrcmp(memoryItem1->CommittedSize, memoryItem2->CommittedSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Private) +{ + sortResult = uintptrcmp(memoryItem1->PrivateSize, memoryItem2->PrivateSize); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpMemoryTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_MEMORY_LIST_CONTEXT context; + PPH_MEMORY_NODE node; + + context = Context; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + node = (PPH_MEMORY_NODE)getChildren->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + { + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)context->AllocationBaseNodeList->Items; + getChildren->NumberOfChildren = context->AllocationBaseNodeList->Count; + } + else + { + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + } + else + { + if (!node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(BaseAddress), + SORT_FUNCTION(Type), + SORT_FUNCTION(Size), + SORT_FUNCTION(Protection), + SORT_FUNCTION(Use), + SORT_FUNCTION(TotalWs), + SORT_FUNCTION(PrivateWs), + SORT_FUNCTION(ShareableWs), + SORT_FUNCTION(SharedWs), + SORT_FUNCTION(LockedWs), + SORT_FUNCTION(Committed), + SORT_FUNCTION(Private) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)context->RegionNodeList->Items, + context->RegionNodeList->Count, + context->TreeNewSortColumn, + context->TreeNewSortOrder, + &context->Cm + )) + { + if (context->TreeNewSortColumn < PHMMTLC_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + } + else + { + sortFunction = NULL; + } + + if (sortFunction) + { + qsort_s(context->RegionNodeList->Items, context->RegionNodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->RegionNodeList->Items; + getChildren->NumberOfChildren = context->RegionNodeList->Count; + } + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + node = (PPH_MEMORY_NODE)isLeaf->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + isLeaf->IsLeaf = !node->Children || node->Children->Count == 0; + else + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_MEMORY_ITEM memoryItem; + + node = (PPH_MEMORY_NODE)getCellText->Node; + memoryItem = node->MemoryItem; + + switch (getCellText->Id) + { + case PHMMTLC_BASEADDRESS: + PhPrintPointer(node->BaseAddressText, memoryItem->BaseAddress); + PhInitializeStringRefLongHint(&getCellText->Text, node->BaseAddressText); + break; + case PHMMTLC_TYPE: + if (memoryItem->State & MEM_FREE) + { + if (memoryItem->RegionType == UnusableRegion) + PhInitializeStringRef(&getCellText->Text, L"Free (Unusable)"); + else + PhInitializeStringRef(&getCellText->Text, L"Free"); + } + else if (node->IsAllocationBase) + { + PhInitializeStringRefLongHint(&getCellText->Text, PhGetMemoryTypeString(memoryItem->Type)); + } + else + { + PH_FORMAT format[3]; + SIZE_T returnLength; + + PhInitFormatS(&format[0], PhGetMemoryTypeString(memoryItem->Type)); + PhInitFormatS(&format[1], L": "); + PhInitFormatS(&format[2], PhGetMemoryStateString(memoryItem->State)); + + if (PhFormatToBuffer(format, 3, node->TypeText, sizeof(node->TypeText), &returnLength)) + { + getCellText->Text.Buffer = node->TypeText; + getCellText->Text.Length = returnLength - sizeof(WCHAR); + } + } + break; + case PHMMTLC_SIZE: + PhMoveReference(&node->SizeText, PhFormatSize(memoryItem->RegionSize, -1)); + getCellText->Text = PhGetStringRef(node->SizeText); + break; + case PHMMTLC_PROTECTION: + PhInitializeStringRefLongHint(&getCellText->Text, node->ProtectionText); + break; + case PHMMTLC_USE: + PhpUpdateMemoryNodeUseText(node); + getCellText->Text = PhGetStringRef(node->UseText); + break; + case PHMMTLC_TOTALWS: + PhMoveReference(&node->TotalWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->TotalWorkingSetPages * PAGE_SIZE)); + getCellText->Text = PhGetStringRef(node->TotalWsText); + break; + case PHMMTLC_PRIVATEWS: + PhMoveReference(&node->PrivateWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->PrivateWorkingSetPages * PAGE_SIZE)); + getCellText->Text = PhGetStringRef(node->PrivateWsText); + break; + case PHMMTLC_SHAREABLEWS: + PhMoveReference(&node->ShareableWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->ShareableWorkingSetPages * PAGE_SIZE)); + getCellText->Text = PhGetStringRef(node->ShareableWsText); + break; + case PHMMTLC_SHAREDWS: + PhMoveReference(&node->SharedWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->SharedWorkingSetPages * PAGE_SIZE)); + getCellText->Text = PhGetStringRef(node->SharedWsText); + break; + case PHMMTLC_LOCKEDWS: + PhMoveReference(&node->LockedWsText, PhpFormatSizeIfNonZero((ULONG64)memoryItem->LockedWorkingSetPages * PAGE_SIZE)); + getCellText->Text = PhGetStringRef(node->LockedWsText); + break; + case PHMMTLC_COMMITTED: + PhMoveReference(&node->CommittedText, PhpFormatSizeIfNonZero(memoryItem->CommittedSize)); + getCellText->Text = PhGetStringRef(node->CommittedText); + break; + case PHMMTLC_PRIVATE: + PhMoveReference(&node->PrivateText, PhpFormatSizeIfNonZero(memoryItem->PrivateSize)); + getCellText->Text = PhGetStringRef(node->PrivateText); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PPH_MEMORY_ITEM memoryItem; + + node = (PPH_MEMORY_NODE)getNodeColor->Node; + memoryItem = node->MemoryItem; + + if (!memoryItem) + NOTHING; + else if ( + context->HighlightExecutePages && ( + memoryItem->Protect & PAGE_EXECUTE || + memoryItem->Protect & PAGE_EXECUTE_READ || + memoryItem->Protect & PAGE_EXECUTE_READWRITE || + memoryItem->Protect & PAGE_EXECUTE_WRITECOPY + )) + { + getNodeColor->BackColor = PhCsColorPacked; + } + else if ( + context->HighlightCfgPages && ( + memoryItem->RegionType == CfgBitmapRegion || + memoryItem->RegionType == CfgBitmap32Region + )) + { + getNodeColor->BackColor = PhCsColorElevatedProcesses; + } + else if ( + context->HighlightSystemPages && ( + memoryItem->Type & SEC_IMAGE + )) + { + getNodeColor->BackColor = PhCsColorSystemProcesses; + } + else if (context->HighlightPrivatePages && memoryItem->Type & MEM_PRIVATE) + { + getNodeColor->BackColor = PhCsColorOwnProcesses; + } + //else if ( + // memoryItem->RegionType == StackRegion || memoryItem->RegionType == Stack32Region || + // memoryItem->RegionType == HeapRegion || memoryItem->RegionType == Heap32Region || + // memoryItem->RegionType == HeapSegmentRegion || memoryItem->RegionType == HeapSegment32Region + // ((memoryItem->Protect & PAGE_EXECUTE_WRITECOPY || memoryItem->Protect & PAGE_EXECUTE_READWRITE || + // memoryItem->Protect & PAGE_READWRITE) && !(memoryItem->Type & SEC_IMAGE)) + // ) + //{ + // getNodeColor->BackColor = PhCsColorElevatedProcesses; + //} + //else if (memoryItem->Type & SEC_IMAGE) + // getNodeColor->BackColor = PhCsColorImmersiveProcesses; + + getNodeColor->Flags = TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MEMORY_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(context->TreeNewHandle, 0, -1); + break; + case VK_RETURN: + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MEMORY_READWRITEMEMORY, 0); + break; + case VK_F5: + SendMessage(context->ParentWindowHandle, WM_COMMAND, IDC_REFRESH, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = NoSortOrder; + PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + + node = (PPH_MEMORY_NODE)mouseEvent->Node; + + if (node && node->IsAllocationBase) + TreeNew_SetNodeExpanded(hwnd, node, !node->Node.Expanded); + else + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MEMORY_READWRITEMEMORY, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); + } + return TRUE; + case TreeNewGetDialogCode: + { + PULONG code = Parameter2; + + if (PtrToUlong(Parameter1) == VK_RETURN) + { + *code = DLGC_WANTMESSAGE; + return TRUE; + } + } + return FALSE; + } + + return FALSE; +} + +PPH_MEMORY_NODE PhGetSelectedMemoryNode( + _In_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + ULONG i; + + if (Context->TreeNewSortOrder == NoSortOrder) + { + for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) + { + PPH_MEMORY_NODE node = Context->AllocationBaseNodeList->Items[i]; + + if (node->Node.Selected) + return node; + } + } + + for (i = 0; i < Context->RegionNodeList->Count; i++) + { + PPH_MEMORY_NODE node = Context->RegionNodeList->Items[i]; + + if (node->Node.Selected) + return node; + } + + return NULL; +} + +VOID PhGetSelectedMemoryNodes( + _In_ PPH_MEMORY_LIST_CONTEXT Context, + _Out_ PPH_MEMORY_NODE **MemoryNodes, + _Out_ PULONG NumberOfMemoryNodes + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + if (Context->TreeNewSortOrder == NoSortOrder) + { + for (i = 0; i < Context->AllocationBaseNodeList->Count; i++) + { + PPH_MEMORY_NODE node = Context->AllocationBaseNodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node); + } + } + + for (i = 0; i < Context->RegionNodeList->Count; i++) + { + PPH_MEMORY_NODE node = Context->RegionNodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node); + } + + *NumberOfMemoryNodes = (ULONG)array.Count; + *MemoryNodes = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllMemoryNodes( + _In_ PPH_MEMORY_LIST_CONTEXT Context + ) +{ + TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); +} diff --git a/ProcessHacker/memlists.c b/ProcessHacker/memlists.c index c1abb62565b2..137fa87b759b 100644 --- a/ProcessHacker/memlists.c +++ b/ProcessHacker/memlists.c @@ -1,288 +1,330 @@ -/* - * Process Hacker - - * memory list information - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include - -#define MSG_UPDATE (WM_APP + 1) - -INT_PTR CALLBACK PhpMemoryListsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -HWND PhMemoryListsWindowHandle = NULL; -static VOID (NTAPI *UnregisterDialogFunction)(HWND); -static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - -VOID PhShowMemoryListsDialog( - _In_ HWND ParentWindowHandle, - _In_opt_ VOID (NTAPI *RegisterDialog)(HWND), - _In_opt_ VOID (NTAPI *UnregisterDialog)(HWND) - ) -{ - if (!PhMemoryListsWindowHandle) - { - PhMemoryListsWindowHandle = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MEMLISTS), - ParentWindowHandle, - PhpMemoryListsDlgProc - ); - RegisterDialog(PhMemoryListsWindowHandle); - UnregisterDialogFunction = UnregisterDialog; - } - - if (!IsWindowVisible(PhMemoryListsWindowHandle)) - ShowWindow(PhMemoryListsWindowHandle, SW_SHOW); - else if (IsIconic(PhMemoryListsWindowHandle)) - ShowWindow(PhMemoryListsWindowHandle, SW_RESTORE); - else - SetForegroundWindow(PhMemoryListsWindowHandle); -} - -static VOID NTAPI ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PostMessage(PhMemoryListsWindowHandle, MSG_UPDATE, 0, 0); -} - -static VOID PhpUpdateMemoryListInfo( - _In_ HWND hwndDlg - ) -{ - SYSTEM_MEMORY_LIST_INFORMATION memoryListInfo; - - if (NT_SUCCESS(NtQuerySystemInformation( - SystemMemoryListInformation, - &memoryListInfo, - sizeof(SYSTEM_MEMORY_LIST_INFORMATION), - NULL - ))) - { - ULONG_PTR standbyPageCount; - ULONG_PTR repurposedPageCount; - ULONG i; - - standbyPageCount = 0; - repurposedPageCount = 0; - - for (i = 0; i < 8; i++) - { - standbyPageCount += memoryListInfo.PageCountByPriority[i]; - repurposedPageCount += memoryListInfo.RepurposedPagesByPriority[i]; - } - - SetDlgItemText(hwndDlg, IDC_ZLISTZEROED_V, PhaFormatSize((ULONG64)memoryListInfo.ZeroPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTFREE_V, PhaFormatSize((ULONG64)memoryListInfo.FreePageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIED_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIEDNOWRITE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedNoWritePageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTBAD_V, PhaFormatSize((ULONG64)memoryListInfo.BadPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY_V, PhaFormatSize((ULONG64)standbyPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY0_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[0] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY1_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[1] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY2_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[2] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY3_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[3] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY4_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[4] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY5_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[5] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY6_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[6] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY7_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[7] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED_V, PhaFormatSize((ULONG64)repurposedPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED0_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[0] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED1_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[1] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED2_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[2] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED3_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[3] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED4_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[4] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED5_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[5] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED6_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[6] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED7_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[7] * PAGE_SIZE, -1)->Buffer); - - if (WindowsVersion >= WINDOWS_8) - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIEDPAGEFILE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCountPageFile * PAGE_SIZE, -1)->Buffer); - else - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIEDPAGEFILE_V, L"N/A"); - } - else - { - SetDlgItemText(hwndDlg, IDC_ZLISTZEROED_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTFREE_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIED_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIEDNOWRITE_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTMODIFIEDPAGEFILE_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTBAD_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY0_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY1_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY2_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY3_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY4_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY5_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY6_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTSTANDBY7_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED0_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED1_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED2_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED3_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED4_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED5_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED6_V, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ZLISTREPURPOSED7_V, L"Unknown"); - } -} - -INT_PTR CALLBACK PhpMemoryListsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); - PhpUpdateMemoryListInfo(hwndDlg); - - PhLoadWindowPlacementFromSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); - PhRegisterDialog(hwndDlg); - } - break; - case WM_DESTROY: - { - PhUnregisterDialog(hwndDlg); - PhSaveWindowPlacementToSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); - - PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedRegistration); - - UnregisterDialogFunction(hwndDlg); - PhMemoryListsWindowHandle = NULL; - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - DestroyWindow(hwndDlg); - break; - case IDC_EMPTY: - { - PPH_EMENU menu; - RECT buttonRect; - POINT point; - PPH_EMENU_ITEM selectedItem; - SYSTEM_MEMORY_LIST_COMMAND command = -1; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_EMPTYMEMLISTS), 0); - - GetClientRect(GetDlgItem(hwndDlg, IDC_EMPTY), &buttonRect); - point.x = 0; - point.y = buttonRect.bottom; - - ClientToScreen(GetDlgItem(hwndDlg, IDC_EMPTY), &point); - selectedItem = PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); - - if (selectedItem) - { - switch (selectedItem->Id) - { - case ID_EMPTY_EMPTYWORKINGSETS: - command = MemoryEmptyWorkingSets; - break; - case ID_EMPTY_EMPTYMODIFIEDPAGELIST: - command = MemoryFlushModifiedList; - break; - case ID_EMPTY_EMPTYSTANDBYLIST: - command = MemoryPurgeStandbyList; - break; - case ID_EMPTY_EMPTYPRIORITY0STANDBYLIST: - command = MemoryPurgeLowPriorityStandbyList; - break; - } - } - - if (command != -1) - { - NTSTATUS status; - - SetCursor(LoadCursor(NULL, IDC_WAIT)); - status = NtSetSystemInformation( - SystemMemoryListInformation, - &command, - sizeof(SYSTEM_MEMORY_LIST_COMMAND) - ); - SetCursor(LoadCursor(NULL, IDC_ARROW)); - - if (status == STATUS_PRIVILEGE_NOT_HELD) - { - if (!PhGetOwnTokenAttributes().Elevated) - { - if (PhUiConnectToPhSvc(hwndDlg, FALSE)) - { - SetCursor(LoadCursor(NULL, IDC_WAIT)); - status = PhSvcCallIssueMemoryListCommand(command); - SetCursor(LoadCursor(NULL, IDC_ARROW)); - PhUiDisconnectFromPhSvc(); - } - else - { - // User cancelled eleavtion. - status = STATUS_SUCCESS; - } - } - } - - if (!NT_SUCCESS(status)) - { - PhShowStatus(hwndDlg, L"Unable to execute the memory list command", status, 0); - } - } - - PhDestroyEMenu(menu); - } - break; - } - } - break; - case MSG_UPDATE: - { - PhpUpdateMemoryListInfo(hwndDlg); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * memory list information + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSG_UPDATE (WM_APP + 1) + +INT_PTR CALLBACK PhpMemoryListsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +HWND PhMemoryListsWindowHandle = NULL; +static VOID (NTAPI *UnregisterDialogFunction)(HWND); +static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + +VOID PhShowMemoryListsDialog( + _In_ HWND ParentWindowHandle, + _In_opt_ VOID (NTAPI *RegisterDialog)(HWND), + _In_opt_ VOID (NTAPI *UnregisterDialog)(HWND) + ) +{ + if (!PhMemoryListsWindowHandle) + { + PhMemoryListsWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MEMLISTS), + ParentWindowHandle, + PhpMemoryListsDlgProc + ); + RegisterDialog(PhMemoryListsWindowHandle); + UnregisterDialogFunction = UnregisterDialog; + } + + if (!IsWindowVisible(PhMemoryListsWindowHandle)) + ShowWindow(PhMemoryListsWindowHandle, SW_SHOW); + else if (IsMinimized(PhMemoryListsWindowHandle)) + ShowWindow(PhMemoryListsWindowHandle, SW_RESTORE); + else + SetForegroundWindow(PhMemoryListsWindowHandle); +} + +static VOID NTAPI ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PostMessage(PhMemoryListsWindowHandle, MSG_UPDATE, 0, 0); +} + +static VOID PhpUpdateMemoryListInfo( + _In_ HWND hwndDlg + ) +{ + SYSTEM_MEMORY_LIST_INFORMATION memoryListInfo; + + if (NT_SUCCESS(NtQuerySystemInformation( + SystemMemoryListInformation, + &memoryListInfo, + sizeof(SYSTEM_MEMORY_LIST_INFORMATION), + NULL + ))) + { + ULONG_PTR standbyPageCount; + ULONG_PTR repurposedPageCount; + ULONG i; + + standbyPageCount = 0; + repurposedPageCount = 0; + + for (i = 0; i < 8; i++) + { + standbyPageCount += memoryListInfo.PageCountByPriority[i]; + repurposedPageCount += memoryListInfo.RepurposedPagesByPriority[i]; + } + + PhSetDialogItemText(hwndDlg, IDC_ZLISTZEROED_V, PhaFormatSize((ULONG64)memoryListInfo.ZeroPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTFREE_V, PhaFormatSize((ULONG64)memoryListInfo.FreePageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIED_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIEDNOWRITE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedNoWritePageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTBAD_V, PhaFormatSize((ULONG64)memoryListInfo.BadPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY_V, PhaFormatSize((ULONG64)standbyPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY0_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[0] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY1_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[1] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY2_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[2] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY3_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[3] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY4_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[4] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY5_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[5] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY6_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[6] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY7_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[7] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED_V, PhaFormatSize((ULONG64)repurposedPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED0_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[0] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED1_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[1] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED2_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[2] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED3_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[3] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED4_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[4] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED5_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[5] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED6_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[6] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED7_V, PhaFormatSize((ULONG64)memoryListInfo.RepurposedPagesByPriority[7] * PAGE_SIZE, ULONG_MAX)->Buffer); + + if (WindowsVersion >= WINDOWS_8) + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIEDPAGEFILE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCountPageFile * PAGE_SIZE, ULONG_MAX)->Buffer); + else + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIEDPAGEFILE_V, L"N/A"); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_ZLISTZEROED_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTFREE_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIED_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIEDNOWRITE_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTMODIFIEDPAGEFILE_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTBAD_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY0_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY1_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY2_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY3_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY4_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY5_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY6_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTSTANDBY7_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED0_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED1_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED2_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED3_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED4_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED5_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED6_V, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_ZLISTREPURPOSED7_V, L"Unknown"); + } +} + +INT_PTR CALLBACK PhpMemoryListsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE tokenHandle; + + if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))) + { + PhSetTokenPrivilege2(tokenHandle, SE_PROF_SINGLE_PROCESS_PRIVILEGE, SE_PRIVILEGE_ENABLED); + NtClose(tokenHandle); + } + + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedRegistration); + PhpUpdateMemoryListInfo(hwndDlg); + + PhLoadWindowPlacementFromSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); + PhRegisterDialog(hwndDlg); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhUnregisterDialog(hwndDlg); + PhSaveWindowPlacementToSetting(L"MemoryListsWindowPosition", NULL, hwndDlg); + + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &ProcessesUpdatedRegistration); + + UnregisterDialogFunction(hwndDlg); + PhMemoryListsWindowHandle = NULL; + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + case IDC_EMPTY: + { + PPH_EMENU menu; + RECT buttonRect; + POINT point; + PPH_EMENU_ITEM selectedItem; + SYSTEM_MEMORY_LIST_COMMAND command = ULONG_MAX; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_EMPTY_COMBINEMEMORYLISTS, L"&Combine memory lists", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_EMPTY_EMPTYWORKINGSETS, L"Empty &working sets", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_EMPTY_EMPTYMODIFIEDPAGELIST, L"Empty &modified page list", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_EMPTY_EMPTYSTANDBYLIST, L"Empty &standby list", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_EMPTY_EMPTYPRIORITY0STANDBYLIST, L"Empty &priority 0 standby list", NULL, NULL), ULONG_MAX); + + GetClientRect(GetDlgItem(hwndDlg, IDC_EMPTY), &buttonRect); + point.x = 0; + point.y = buttonRect.bottom; + + ClientToScreen(GetDlgItem(hwndDlg, IDC_EMPTY), &point); + selectedItem = PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); + + if (selectedItem) + { + switch (selectedItem->Id) + { + case ID_EMPTY_EMPTYWORKINGSETS: + command = MemoryEmptyWorkingSets; + break; + case ID_EMPTY_EMPTYMODIFIEDPAGELIST: + command = MemoryFlushModifiedList; + break; + case ID_EMPTY_EMPTYSTANDBYLIST: + command = MemoryPurgeStandbyList; + break; + case ID_EMPTY_EMPTYPRIORITY0STANDBYLIST: + command = MemoryPurgeLowPriorityStandbyList; + break; + case ID_EMPTY_COMBINEMEMORYLISTS: + { + NTSTATUS status; + MEMORY_COMBINE_INFORMATION_EX combineInfo = { 0 }; + + status = NtSetSystemInformation( + SystemCombinePhysicalMemoryInformation, + &combineInfo, + sizeof(MEMORY_COMBINE_INFORMATION_EX) + ); + + if (NT_SUCCESS(status)) + { + PhShowInformation2( + hwndDlg, + L"Memory pages combined", + L"%s (%llu pages)", + PhaFormatSize(combineInfo.PagesCombined * PAGE_SIZE, -1)->Buffer, + combineInfo.PagesCombined + ); + } + else + { + PhShowStatus(hwndDlg, L"Unable to combine memory pages", status, 0); + } + } + break; + } + } + + if (command != ULONG_MAX) + { + NTSTATUS status; + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + status = NtSetSystemInformation( + SystemMemoryListInformation, + &command, + sizeof(SYSTEM_MEMORY_LIST_COMMAND) + ); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + if (status == STATUS_PRIVILEGE_NOT_HELD) + { + if (!PhGetOwnTokenAttributes().Elevated) + { + if (PhUiConnectToPhSvc(hwndDlg, FALSE)) + { + SetCursor(LoadCursor(NULL, IDC_WAIT)); + status = PhSvcCallIssueMemoryListCommand(command); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + PhUiDisconnectFromPhSvc(); + } + else + { + // User cancelled eleavtion. + status = STATUS_SUCCESS; + } + } + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus(hwndDlg, L"Unable to execute the memory list command", status, 0); + } + } + + PhDestroyEMenu(menu); + } + break; + } + } + break; + case MSG_UPDATE: + { + PhpUpdateMemoryListInfo(hwndDlg); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/memprot.c b/ProcessHacker/memprot.c index d52168c948c2..5182ac42182b 100644 --- a/ProcessHacker/memprot.c +++ b/ProcessHacker/memprot.c @@ -1,162 +1,159 @@ -/* - * Process Hacker - - * memory protection window - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include - -typedef struct _MEMORY_PROTECT_CONTEXT -{ - PPH_PROCESS_ITEM ProcessItem; - PPH_MEMORY_ITEM MemoryItem; -} MEMORY_PROTECT_CONTEXT, *PMEMORY_PROTECT_CONTEXT; - -INT_PTR CALLBACK PhpMemoryProtectDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowMemoryProtectDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ PPH_MEMORY_ITEM MemoryItem - ) -{ - MEMORY_PROTECT_CONTEXT context; - - context.ProcessItem = ProcessItem; - context.MemoryItem = MemoryItem; - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MEMPROTECT), - ParentWindowHandle, - PhpMemoryProtectDlgProc, - (LPARAM)&context - ); -} - -static INT_PTR CALLBACK PhpMemoryProtectDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)lParam); - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetDlgItemText(hwndDlg, IDC_INTRO, - L"Possible values:\r\n" - L"\r\n" - L"0x01 - PAGE_NOACCESS\r\n" - L"0x02 - PAGE_READONLY\r\n" - L"0x04 - PAGE_READWRITE\r\n" - L"0x08 - PAGE_WRITECOPY\r\n" - L"0x10 - PAGE_EXECUTE\r\n" - L"0x20 - PAGE_EXECUTE_READ\r\n" - L"0x40 - PAGE_EXECUTE_READWRITE\r\n" - L"0x80 - PAGE_EXECUTE_WRITECOPY\r\n" - L"Modifiers:\r\n" - L"0x100 - PAGE_GUARD\r\n" - L"0x200 - PAGE_NOCACHE\r\n" - L"0x400 - PAGE_WRITECOMBINE\r\n" - ); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_VALUE), TRUE); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - NTSTATUS status; - PMEMORY_PROTECT_CONTEXT context = (PMEMORY_PROTECT_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - HANDLE processHandle; - ULONG64 protect; - - PhStringToInteger64(&PhaGetDlgItemText(hwndDlg, IDC_VALUE)->sr, 0, &protect); - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_VM_OPERATION, - context->ProcessItem->ProcessId - ))) - { - PVOID baseAddress; - SIZE_T regionSize; - ULONG oldProtect; - - baseAddress = context->MemoryItem->BaseAddress; - regionSize = context->MemoryItem->RegionSize; - - status = NtProtectVirtualMemory( - processHandle, - &baseAddress, - ®ionSize, - (ULONG)protect, - &oldProtect - ); - - if (NT_SUCCESS(status)) - context->MemoryItem->Protect = (ULONG)protect; - } - - if (NT_SUCCESS(status)) - { - EndDialog(hwndDlg, IDOK); - } - else - { - PhShowStatus(hwndDlg, L"Unable to change memory protection", status, 0); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_VALUE), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUE), 0, -1); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * memory protection window + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +typedef struct _MEMORY_PROTECT_CONTEXT +{ + PPH_PROCESS_ITEM ProcessItem; + PPH_MEMORY_ITEM MemoryItem; +} MEMORY_PROTECT_CONTEXT, *PMEMORY_PROTECT_CONTEXT; + +INT_PTR CALLBACK PhpMemoryProtectDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowMemoryProtectDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ PPH_MEMORY_ITEM MemoryItem + ) +{ + MEMORY_PROTECT_CONTEXT context; + + context.ProcessItem = ProcessItem; + context.MemoryItem = MemoryItem; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MEMPROTECT), + ParentWindowHandle, + PhpMemoryProtectDlgProc, + (LPARAM)&context + ); +} + +static INT_PTR CALLBACK PhpMemoryProtectDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, (PVOID)lParam); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetDialogItemText(hwndDlg, IDC_INTRO, + L"Possible values:\r\n" + L"\r\n" + L"0x01 - PAGE_NOACCESS\r\n" + L"0x02 - PAGE_READONLY\r\n" + L"0x04 - PAGE_READWRITE\r\n" + L"0x08 - PAGE_WRITECOPY\r\n" + L"0x10 - PAGE_EXECUTE\r\n" + L"0x20 - PAGE_EXECUTE_READ\r\n" + L"0x40 - PAGE_EXECUTE_READWRITE\r\n" + L"0x80 - PAGE_EXECUTE_WRITECOPY\r\n" + L"Modifiers:\r\n" + L"0x100 - PAGE_GUARD\r\n" + L"0x200 - PAGE_NOCACHE\r\n" + L"0x400 - PAGE_WRITECOMBINE\r\n" + ); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_VALUE)); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + NTSTATUS status; + PMEMORY_PROTECT_CONTEXT context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + HANDLE processHandle; + ULONG64 protect; + + PhStringToInteger64(&PhaGetDlgItemText(hwndDlg, IDC_VALUE)->sr, 0, &protect); + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_VM_OPERATION, + context->ProcessItem->ProcessId + ))) + { + PVOID baseAddress; + SIZE_T regionSize; + ULONG oldProtect; + + baseAddress = context->MemoryItem->BaseAddress; + regionSize = context->MemoryItem->RegionSize; + + status = NtProtectVirtualMemory( + processHandle, + &baseAddress, + ®ionSize, + (ULONG)protect, + &oldProtect + ); + + if (NT_SUCCESS(status)) + context->MemoryItem->Protect = (ULONG)protect; + } + + if (NT_SUCCESS(status)) + { + EndDialog(hwndDlg, IDOK); + } + else + { + PhShowStatus(hwndDlg, L"Unable to change memory protection", status, 0); + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_VALUE)); + Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUE), 0, -1); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/memprv.c b/ProcessHacker/memprv.c index 35e10d10ff50..21ca87a079f6 100644 --- a/ProcessHacker/memprv.c +++ b/ProcessHacker/memprv.c @@ -1,810 +1,865 @@ -/* - * Process Hacker - - * memory provider - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#define MAX_HEAPS 1000 -#define WS_REQUEST_COUNT (PAGE_SIZE / sizeof(MEMORY_WORKING_SET_EX_INFORMATION)) - -VOID PhpMemoryItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -PPH_OBJECT_TYPE PhMemoryItemType; - -BOOLEAN PhMemoryProviderInitialization( - VOID - ) -{ - PhMemoryItemType = PhCreateObjectType(L"MemoryItem", 0, PhpMemoryItemDeleteProcedure); - - return TRUE; -} - -VOID PhGetMemoryProtectionString( - _In_ ULONG Protection, - _Out_writes_(17) PWSTR String - ) -{ - PWSTR string; - PH_STRINGREF base; - - if (!Protection) - { - String[0] = 0; - return; - } - - if (Protection & PAGE_NOACCESS) - PhInitializeStringRef(&base, L"NA"); - else if (Protection & PAGE_READONLY) - PhInitializeStringRef(&base, L"R"); - else if (Protection & PAGE_READWRITE) - PhInitializeStringRef(&base, L"RW"); - else if (Protection & PAGE_WRITECOPY) - PhInitializeStringRef(&base, L"WC"); - else if (Protection & PAGE_EXECUTE) - PhInitializeStringRef(&base, L"X"); - else if (Protection & PAGE_EXECUTE_READ) - PhInitializeStringRef(&base, L"RX"); - else if (Protection & PAGE_EXECUTE_READWRITE) - PhInitializeStringRef(&base, L"RWX"); - else if (Protection & PAGE_EXECUTE_WRITECOPY) - PhInitializeStringRef(&base, L"WCX"); - else - PhInitializeStringRef(&base, L"?"); - - string = String; - - memcpy(string, base.Buffer, base.Length); - string += base.Length / sizeof(WCHAR); - - if (Protection & PAGE_GUARD) - { - memcpy(string, L"+G", 2 * 2); - string += 2; - } - - if (Protection & PAGE_NOCACHE) - { - memcpy(string, L"+NC", 3 * 2); - string += 3; - } - - if (Protection & PAGE_WRITECOMBINE) - { - memcpy(string, L"+WCM", 4 * 2); - string += 4; - } - - *string = 0; -} - -PWSTR PhGetMemoryStateString( - _In_ ULONG State - ) -{ - if (State & MEM_COMMIT) - return L"Commit"; - else if (State & MEM_RESERVE) - return L"Reserved"; - else if (State & MEM_FREE) - return L"Free"; - else - return L"Unknown"; -} - -PWSTR PhGetMemoryTypeString( - _In_ ULONG Type - ) -{ - if (Type & MEM_PRIVATE) - return L"Private"; - else if (Type & MEM_MAPPED) - return L"Mapped"; - else if (Type & MEM_IMAGE) - return L"Image"; - else - return L"Unknown"; -} - -PPH_MEMORY_ITEM PhCreateMemoryItem( - VOID - ) -{ - PPH_MEMORY_ITEM memoryItem; - - memoryItem = PhCreateObject(sizeof(PH_MEMORY_ITEM), PhMemoryItemType); - memset(memoryItem, 0, sizeof(PH_MEMORY_ITEM)); - - return memoryItem; -} - -VOID PhpMemoryItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_MEMORY_ITEM memoryItem = Object; - - switch (memoryItem->RegionType) - { - case CustomRegion: - PhClearReference(&memoryItem->u.Custom.Text); - break; - case MappedFileRegion: - PhClearReference(&memoryItem->u.MappedFile.FileName); - break; - } -} - -static LONG NTAPI PhpMemoryItemCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ) -{ - PPH_MEMORY_ITEM memoryItem1 = CONTAINING_RECORD(Links1, PH_MEMORY_ITEM, Links); - PPH_MEMORY_ITEM memoryItem2 = CONTAINING_RECORD(Links2, PH_MEMORY_ITEM, Links); - - return uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress); -} - -VOID PhDeleteMemoryItemList( - _In_ PPH_MEMORY_ITEM_LIST List - ) -{ - PLIST_ENTRY listEntry; - PPH_MEMORY_ITEM memoryItem; - - listEntry = List->ListHead.Flink; - - while (listEntry != &List->ListHead) - { - memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - listEntry = listEntry->Flink; - - PhDereferenceObject(memoryItem); - } -} - -PPH_MEMORY_ITEM PhLookupMemoryItemList( - _In_ PPH_MEMORY_ITEM_LIST List, - _In_ PVOID Address - ) -{ - PH_MEMORY_ITEM lookupMemoryItem; - PPH_AVL_LINKS links; - PPH_MEMORY_ITEM memoryItem; - - // Do an approximate search on the set to locate the memory item with the largest - // base address that is still smaller than the given address. - lookupMemoryItem.BaseAddress = Address; - links = PhUpperDualBoundElementAvlTree(&List->Set, &lookupMemoryItem.Links); - - if (links) - { - memoryItem = CONTAINING_RECORD(links, PH_MEMORY_ITEM, Links); - - if ((ULONG_PTR)Address < (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize) - return memoryItem; - } - - return NULL; -} - -PPH_MEMORY_ITEM PhpSetMemoryRegionType( - _In_ PPH_MEMORY_ITEM_LIST List, - _In_ PVOID Address, - _In_ BOOLEAN GoToAllocationBase, - _In_ PH_MEMORY_REGION_TYPE RegionType - ) -{ - PPH_MEMORY_ITEM memoryItem; - - memoryItem = PhLookupMemoryItemList(List, Address); - - if (!memoryItem) - return NULL; - - if (GoToAllocationBase && memoryItem->AllocationBaseItem) - memoryItem = memoryItem->AllocationBaseItem; - - if (memoryItem->RegionType != UnknownRegion) - return NULL; - - memoryItem->RegionType = RegionType; - - return memoryItem; -} - -NTSTATUS PhpUpdateMemoryRegionTypes( - _In_ PPH_MEMORY_ITEM_LIST List, - _In_ HANDLE ProcessHandle - ) -{ - NTSTATUS status; - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - ULONG i; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; -#endif - PPH_MEMORY_ITEM memoryItem; - PLIST_ENTRY listEntry; - - if (!NT_SUCCESS(status = PhEnumProcessesEx(&processes, SystemExtendedProcessInformation))) - return status; - - process = PhFindProcessInformation(processes, List->ProcessId); - - if (!process) - { - PhFree(processes); - return STATUS_NOT_FOUND; - } - - // USER_SHARED_DATA - PhpSetMemoryRegionType(List, USER_SHARED_DATA, TRUE, UserSharedDataRegion); - - // PEB, heap - { - PROCESS_BASIC_INFORMATION basicInfo; - ULONG numberOfHeaps; - PVOID processHeapsPtr; - PVOID *processHeaps; - ULONG i; -#ifdef _WIN64 - PVOID peb32; - ULONG processHeapsPtr32; - ULONG *processHeaps32; -#endif - - if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) - { - PhpSetMemoryRegionType(List, basicInfo.PebBaseAddress, TRUE, PebRegion); - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, NumberOfHeaps)), - &numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS) - { - processHeaps = PhAllocate(numberOfHeaps * sizeof(PVOID)); - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessHeaps)), - &processHeapsPtr, sizeof(PVOID), NULL)) && - NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, - processHeapsPtr, - processHeaps, numberOfHeaps * sizeof(PVOID), NULL))) - { - for (i = 0; i < numberOfHeaps; i++) - { - if (memoryItem = PhpSetMemoryRegionType(List, processHeaps[i], TRUE, HeapRegion)) - memoryItem->u.Heap.Index = i; - } - } - - PhFree(processHeaps); - } - } -#ifdef _WIN64 - - if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32 != 0) - { - isWow64 = TRUE; - PhpSetMemoryRegionType(List, peb32, TRUE, Peb32Region); - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, NumberOfHeaps)), - &numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS) - { - processHeaps32 = PhAllocate(numberOfHeaps * sizeof(ULONG)); - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessHeaps)), - &processHeapsPtr32, sizeof(ULONG), NULL)) && - NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, - UlongToPtr(processHeapsPtr32), - processHeaps32, numberOfHeaps * sizeof(ULONG), NULL))) - { - for (i = 0; i < numberOfHeaps; i++) - { - if (memoryItem = PhpSetMemoryRegionType(List, UlongToPtr(processHeaps32[i]), TRUE, Heap32Region)) - memoryItem->u.Heap.Index = i; - } - } - - PhFree(processHeaps32); - } - } -#endif - } - - // TEB, stack - for (i = 0; i < process->NumberOfThreads; i++) - { - PSYSTEM_EXTENDED_THREAD_INFORMATION thread = (PSYSTEM_EXTENDED_THREAD_INFORMATION)process->Threads + i; - - if (WindowsVersion < WINDOWS_VISTA) - { - HANDLE threadHandle; - THREAD_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhOpenThread(&threadHandle, ThreadQueryAccess, thread->ThreadInfo.ClientId.UniqueThread))) - { - if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo))) - thread->TebBase = basicInfo.TebBaseAddress; - - NtClose(threadHandle); - } - } - - if (thread->TebBase) - { - NT_TIB ntTib; - SIZE_T bytesRead; - - if (memoryItem = PhpSetMemoryRegionType(List, thread->TebBase, TRUE, TebRegion)) - memoryItem->u.Teb.ThreadId = thread->ThreadInfo.ClientId.UniqueThread; - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, thread->TebBase, &ntTib, sizeof(NT_TIB), &bytesRead)) && - bytesRead == sizeof(NT_TIB)) - { - if ((ULONG_PTR)ntTib.StackLimit < (ULONG_PTR)ntTib.StackBase) - { - if (memoryItem = PhpSetMemoryRegionType(List, ntTib.StackLimit, TRUE, StackRegion)) - memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread; - } -#ifdef _WIN64 - - if (isWow64 && ntTib.ExceptionList) - { - ULONG teb32 = PtrToUlong(ntTib.ExceptionList); - NT_TIB32 ntTib32; - - // 64-bit and 32-bit TEBs usually share the same memory region, so don't do anything for the 32-bit - // TEB. - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, UlongToPtr(teb32), &ntTib32, sizeof(NT_TIB32), &bytesRead)) && - bytesRead == sizeof(NT_TIB32)) - { - if (ntTib32.StackLimit < ntTib32.StackBase) - { - if (memoryItem = PhpSetMemoryRegionType(List, UlongToPtr(ntTib32.StackLimit), TRUE, Stack32Region)) - memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread; - } - } - } -#endif - } - } - } - - // Mapped file, heap segment, unusable - for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink) - { - memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - - if (memoryItem->RegionType != UnknownRegion) - continue; - - if ((memoryItem->Type & (MEM_MAPPED | MEM_IMAGE)) && memoryItem->AllocationBaseItem == memoryItem) - { - PPH_STRING fileName; - - if (NT_SUCCESS(PhGetProcessMappedFileName(ProcessHandle, memoryItem->BaseAddress, &fileName))) - { - PPH_STRING newFileName = PhResolveDevicePrefix(fileName); - - if (newFileName) - PhMoveReference(&fileName, newFileName); - - memoryItem->RegionType = MappedFileRegion; - memoryItem->u.MappedFile.FileName = fileName; - continue; - } - } - - if (memoryItem->State & MEM_COMMIT) - { - UCHAR buffer[HEAP_SEGMENT_MAX_SIZE]; - - if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, memoryItem->BaseAddress, - buffer, sizeof(buffer), NULL))) - { - PVOID candidateHeap = NULL; - ULONG candidateHeap32 = 0; - PPH_MEMORY_ITEM heapMemoryItem; - - if (WindowsVersion >= WINDOWS_VISTA) - { - PHEAP_SEGMENT heapSegment = (PHEAP_SEGMENT)buffer; - PHEAP_SEGMENT32 heapSegment32 = (PHEAP_SEGMENT32)buffer; - - if (heapSegment->SegmentSignature == HEAP_SEGMENT_SIGNATURE) - candidateHeap = heapSegment->Heap; - if (heapSegment32->SegmentSignature == HEAP_SEGMENT_SIGNATURE) - candidateHeap32 = heapSegment32->Heap; - } - else - { - PHEAP_SEGMENT_OLD heapSegment = (PHEAP_SEGMENT_OLD)buffer; - PHEAP_SEGMENT_OLD32 heapSegment32 = (PHEAP_SEGMENT_OLD32)buffer; - - if (heapSegment->Signature == HEAP_SEGMENT_SIGNATURE) - candidateHeap = heapSegment->Heap; - if (heapSegment32->Signature == HEAP_SEGMENT_SIGNATURE) - candidateHeap32 = heapSegment32->Heap; - } - - if (candidateHeap) - { - heapMemoryItem = PhLookupMemoryItemList(List, candidateHeap); - - if (heapMemoryItem && heapMemoryItem->BaseAddress == candidateHeap && - heapMemoryItem->RegionType == HeapRegion) - { - memoryItem->RegionType = HeapSegmentRegion; - memoryItem->u.HeapSegment.HeapItem = heapMemoryItem; - continue; - } - } - else if (candidateHeap32) - { - heapMemoryItem = PhLookupMemoryItemList(List, UlongToPtr(candidateHeap32)); - - if (heapMemoryItem && heapMemoryItem->BaseAddress == UlongToPtr(candidateHeap32) && - heapMemoryItem->RegionType == Heap32Region) - { - memoryItem->RegionType = HeapSegment32Region; - memoryItem->u.HeapSegment.HeapItem = heapMemoryItem; - continue; - } - } - } - } - } - -#ifdef _WIN64 - // CFG bitmaps for 64-bit processes - if (!isWow64) - { - LDR_INIT_BLOCK ldrInitBlock = { 0 }; - PVOID ldrInitBlockBaseAddress = NULL; - PPH_MEMORY_ITEM cfgBitmapMemoryItem; - PPH_STRING ntdllFileName; - - ntdllFileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\System32\\ntdll.dll"); - status = PhGetProcedureAddressRemote( - ProcessHandle, - ntdllFileName->Buffer, - "LdrSystemDllInitBlock", - 0, - &ldrInitBlockBaseAddress, - NULL - ); - - if (NT_SUCCESS(status) && ldrInitBlockBaseAddress) - { - status = NtReadVirtualMemory( - ProcessHandle, - ldrInitBlockBaseAddress, - &ldrInitBlock, - sizeof(LDR_INIT_BLOCK), - NULL - ); - } - - PhDereferenceObject(ntdllFileName); - - if (NT_SUCCESS(status)) - { - PVOID cfgBitmapAddress = NULL; - - // TODO: Remove this code once most users have updated their machines. - if (ldrInitBlock.Size == sizeof(LDR_INIT_BLOCK)) - cfgBitmapAddress = ldrInitBlock.CfgBitmapAddress; // 15063 - else if (ldrInitBlock.Size == 128) - cfgBitmapAddress = ldrInitBlock.Unknown1[11]; // 14393 - - if (cfgBitmapAddress && (cfgBitmapMemoryItem = PhLookupMemoryItemList(List, cfgBitmapAddress))) - { - PLIST_ENTRY listEntry = &cfgBitmapMemoryItem->ListEntry; - PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - - // Tagging memory items - while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem) - { - // NB : we could do a finer tagging since each MEM_COMMIT memory - // map is the CFG bitmap of a loaded module. However that would - // imply to heavily rely on reverse-engineer results, and might be - // brittle to changes made by Windows dev teams. - memoryItem->RegionType = CfgBitmapRegion; - - listEntry = listEntry->Flink; - memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - } - } - } - } -#endif - - PhFree(processes); - - return STATUS_SUCCESS; -} - -NTSTATUS PhpUpdateMemoryWsCounters( - _In_ PPH_MEMORY_ITEM_LIST List, - _In_ HANDLE ProcessHandle - ) -{ - PLIST_ENTRY listEntry; - PMEMORY_WORKING_SET_EX_INFORMATION info; - - info = PhAllocatePage(WS_REQUEST_COUNT * sizeof(MEMORY_WORKING_SET_EX_INFORMATION), NULL); - - if (!info) - return STATUS_NO_MEMORY; - - for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink) - { - PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); - ULONG_PTR virtualAddress; - SIZE_T remainingPages; - SIZE_T requestPages; - SIZE_T i; - - if (!(memoryItem->State & MEM_COMMIT)) - continue; - - virtualAddress = (ULONG_PTR)memoryItem->BaseAddress; - remainingPages = memoryItem->RegionSize / PAGE_SIZE; - - while (remainingPages != 0) - { - requestPages = min(remainingPages, WS_REQUEST_COUNT); - - for (i = 0; i < requestPages; i++) - { - info[i].VirtualAddress = (PVOID)virtualAddress; - virtualAddress += PAGE_SIZE; - } - - if (NT_SUCCESS(NtQueryVirtualMemory( - ProcessHandle, - NULL, - MemoryWorkingSetExInformation, - info, - requestPages * sizeof(MEMORY_WORKING_SET_EX_INFORMATION), - NULL - ))) - { - for (i = 0; i < requestPages; i++) - { - PMEMORY_WORKING_SET_EX_BLOCK block = &info[i].u1.VirtualAttributes; - - if (block->Valid) - { - memoryItem->TotalWorkingSetPages++; - - if (block->ShareCount > 1) - memoryItem->SharedWorkingSetPages++; - if (block->ShareCount == 0) - memoryItem->PrivateWorkingSetPages++; - if (block->Shared) - memoryItem->ShareableWorkingSetPages++; - if (block->Locked) - memoryItem->LockedWorkingSetPages++; - } - } - } - - remainingPages -= requestPages; - } - } - - PhFreePage(info); - - return STATUS_SUCCESS; -} - -NTSTATUS PhpUpdateMemoryWsCountersOld( - _In_ PPH_MEMORY_ITEM_LIST List, - _In_ HANDLE ProcessHandle - ) -{ - NTSTATUS status; - PMEMORY_WORKING_SET_INFORMATION info; - PPH_MEMORY_ITEM memoryItem = NULL; - ULONG_PTR i; - - if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation(ProcessHandle, &info))) - return status; - - for (i = 0; i < info->NumberOfEntries; i++) - { - PMEMORY_WORKING_SET_BLOCK block = &info->WorkingSetInfo[i]; - ULONG_PTR virtualAddress = block->VirtualPage * PAGE_SIZE; - - if (!memoryItem || virtualAddress < (ULONG_PTR)memoryItem->BaseAddress || - virtualAddress >= (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize) - { - memoryItem = PhLookupMemoryItemList(List, (PVOID)virtualAddress); - } - - if (memoryItem) - { - memoryItem->TotalWorkingSetPages++; - - if (block->ShareCount > 1) - memoryItem->SharedWorkingSetPages++; - if (block->ShareCount == 0) - memoryItem->PrivateWorkingSetPages++; - if (block->Shared) - memoryItem->ShareableWorkingSetPages++; - } - } - - PhFree(info); - - return STATUS_SUCCESS; -} - -NTSTATUS PhQueryMemoryItemList( - _In_ HANDLE ProcessId, - _In_ ULONG Flags, - _Out_ PPH_MEMORY_ITEM_LIST List - ) -{ - NTSTATUS status; - HANDLE processHandle; - ULONG_PTR allocationGranularity; - PVOID baseAddress = (PVOID)0; - MEMORY_BASIC_INFORMATION basicInfo; - PPH_MEMORY_ITEM allocationBaseItem = NULL; - PPH_MEMORY_ITEM previousMemoryItem = NULL; - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - ProcessId - ))) - { - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION, - ProcessId - ))) - { - return status; - } - } - - List->ProcessId = ProcessId; - PhInitializeAvlTree(&List->Set, PhpMemoryItemCompareFunction); - InitializeListHead(&List->ListHead); - - allocationGranularity = PhSystemBasicInformation.AllocationGranularity; - - while (NT_SUCCESS(NtQueryVirtualMemory( - processHandle, - baseAddress, - MemoryBasicInformation, - &basicInfo, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - { - PPH_MEMORY_ITEM memoryItem; - - if (basicInfo.State & MEM_FREE) - { - if (Flags & PH_QUERY_MEMORY_IGNORE_FREE) - goto ContinueLoop; - - basicInfo.AllocationBase = basicInfo.BaseAddress; - } - - memoryItem = PhCreateMemoryItem(); - memoryItem->BasicInfo = basicInfo; - - if (basicInfo.AllocationBase == basicInfo.BaseAddress) - allocationBaseItem = memoryItem; - if (allocationBaseItem && basicInfo.AllocationBase == allocationBaseItem->BaseAddress) - memoryItem->AllocationBaseItem = allocationBaseItem; - - if (basicInfo.State & MEM_COMMIT) - { - memoryItem->CommittedSize = memoryItem->RegionSize; - - if (basicInfo.Type & MEM_PRIVATE) - memoryItem->PrivateSize = memoryItem->RegionSize; - } - - PhAddElementAvlTree(&List->Set, &memoryItem->Links); - InsertTailList(&List->ListHead, &memoryItem->ListEntry); - - if (basicInfo.State & MEM_FREE) - { - if ((ULONG_PTR)basicInfo.BaseAddress & (allocationGranularity - 1)) - { - ULONG_PTR nextAllocationBase; - ULONG_PTR potentialUnusableSize; - - // Split this free region into an unusable and a (possibly empty) usable region. - - nextAllocationBase = ALIGN_UP_BY(basicInfo.BaseAddress, allocationGranularity); - potentialUnusableSize = nextAllocationBase - (ULONG_PTR)basicInfo.BaseAddress; - - memoryItem->RegionType = UnusableRegion; - - // VMMap does this, but is it correct? - //if (previousMemoryItem && (previousMemoryItem->State & MEM_COMMIT)) - // memoryItem->CommittedSize = min(potentialUnusableSize, basicInfo.RegionSize); - - if (nextAllocationBase < (ULONG_PTR)basicInfo.BaseAddress + basicInfo.RegionSize) - { - PPH_MEMORY_ITEM otherMemoryItem; - - memoryItem->RegionSize = potentialUnusableSize; - - otherMemoryItem = PhCreateMemoryItem(); - otherMemoryItem->BasicInfo = basicInfo; - otherMemoryItem->BaseAddress = (PVOID)nextAllocationBase; - otherMemoryItem->AllocationBase = otherMemoryItem->BaseAddress; - otherMemoryItem->RegionSize = basicInfo.RegionSize - potentialUnusableSize; - otherMemoryItem->AllocationBaseItem = otherMemoryItem; - - PhAddElementAvlTree(&List->Set, &otherMemoryItem->Links); - InsertTailList(&List->ListHead, &otherMemoryItem->ListEntry); - - previousMemoryItem = otherMemoryItem; - goto ContinueLoop; - } - } - } - - previousMemoryItem = memoryItem; - -ContinueLoop: - baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); - } - - if (Flags & PH_QUERY_MEMORY_REGION_TYPE) - PhpUpdateMemoryRegionTypes(List, processHandle); - - if (Flags & PH_QUERY_MEMORY_WS_COUNTERS) - { - if (WindowsVersion >= WINDOWS_SERVER_2003) - PhpUpdateMemoryWsCounters(List, processHandle); - else - PhpUpdateMemoryWsCountersOld(List, processHandle); - } - - NtClose(processHandle); - - return STATUS_SUCCESS; -} +/* + * Process Hacker - + * memory provider + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#define MAX_HEAPS 1000 +#define WS_REQUEST_COUNT (PAGE_SIZE / sizeof(MEMORY_WORKING_SET_EX_INFORMATION)) + +VOID PhpMemoryItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +PPH_OBJECT_TYPE PhMemoryItemType = NULL; + +VOID PhGetMemoryProtectionString( + _In_ ULONG Protection, + _Out_writes_(17) PWSTR String + ) +{ + PWSTR string; + PH_STRINGREF base; + + if (!Protection) + { + String[0] = UNICODE_NULL; + return; + } + + if (Protection & PAGE_NOACCESS) + PhInitializeStringRef(&base, L"NA"); + else if (Protection & PAGE_READONLY) + PhInitializeStringRef(&base, L"R"); + else if (Protection & PAGE_READWRITE) + PhInitializeStringRef(&base, L"RW"); + else if (Protection & PAGE_WRITECOPY) + PhInitializeStringRef(&base, L"WC"); + else if (Protection & PAGE_EXECUTE) + PhInitializeStringRef(&base, L"X"); + else if (Protection & PAGE_EXECUTE_READ) + PhInitializeStringRef(&base, L"RX"); + else if (Protection & PAGE_EXECUTE_READWRITE) + PhInitializeStringRef(&base, L"RWX"); + else if (Protection & PAGE_EXECUTE_WRITECOPY) + PhInitializeStringRef(&base, L"WCX"); + else + PhInitializeStringRef(&base, L"?"); + + string = String; + + memcpy(string, base.Buffer, base.Length); + string += base.Length / sizeof(WCHAR); + + if (Protection & PAGE_GUARD) + { + memcpy(string, L"+G", 2 * sizeof(WCHAR)); + string += 2; + } + + if (Protection & PAGE_NOCACHE) + { + memcpy(string, L"+NC", 3 * sizeof(WCHAR)); + string += 3; + } + + if (Protection & PAGE_WRITECOMBINE) + { + memcpy(string, L"+WCM", 4 * sizeof(WCHAR)); + string += 4; + } + + *string = 0; +} + +PWSTR PhGetMemoryStateString( + _In_ ULONG State + ) +{ + if (State & MEM_COMMIT) + return L"Commit"; + else if (State & MEM_RESERVE) + return L"Reserved"; + else if (State & MEM_FREE) + return L"Free"; + else + return L"Unknown"; +} + +PWSTR PhGetMemoryTypeString( + _In_ ULONG Type + ) +{ + if (Type & MEM_PRIVATE) + return L"Private"; + else if (Type & MEM_MAPPED) + return L"Mapped"; + else if (Type & MEM_IMAGE) + return L"Image"; + else + return L"Unknown"; +} + +PPH_MEMORY_ITEM PhCreateMemoryItem( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_MEMORY_ITEM memoryItem; + + if (PhBeginInitOnce(&initOnce)) + { + PhMemoryItemType = PhCreateObjectType(L"MemoryItem", 0, PhpMemoryItemDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + memoryItem = PhCreateObject(sizeof(PH_MEMORY_ITEM), PhMemoryItemType); + memset(memoryItem, 0, sizeof(PH_MEMORY_ITEM)); + + return memoryItem; +} + +VOID PhpMemoryItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_MEMORY_ITEM memoryItem = Object; + + switch (memoryItem->RegionType) + { + case CustomRegion: + PhClearReference(&memoryItem->u.Custom.Text); + break; + case MappedFileRegion: + PhClearReference(&memoryItem->u.MappedFile.FileName); + break; + } +} + +static LONG NTAPI PhpMemoryItemCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ) +{ + PPH_MEMORY_ITEM memoryItem1 = CONTAINING_RECORD(Links1, PH_MEMORY_ITEM, Links); + PPH_MEMORY_ITEM memoryItem2 = CONTAINING_RECORD(Links2, PH_MEMORY_ITEM, Links); + + return uintptrcmp((ULONG_PTR)memoryItem1->BaseAddress, (ULONG_PTR)memoryItem2->BaseAddress); +} + +VOID PhDeleteMemoryItemList( + _In_ PPH_MEMORY_ITEM_LIST List + ) +{ + PLIST_ENTRY listEntry; + PPH_MEMORY_ITEM memoryItem; + + listEntry = List->ListHead.Flink; + + while (listEntry != &List->ListHead) + { + memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + listEntry = listEntry->Flink; + + PhDereferenceObject(memoryItem); + } +} + +PPH_MEMORY_ITEM PhLookupMemoryItemList( + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ PVOID Address + ) +{ + PH_MEMORY_ITEM lookupMemoryItem; + PPH_AVL_LINKS links; + PPH_MEMORY_ITEM memoryItem; + + // Do an approximate search on the set to locate the memory item with the largest + // base address that is still smaller than the given address. + lookupMemoryItem.BaseAddress = Address; + links = PhUpperDualBoundElementAvlTree(&List->Set, &lookupMemoryItem.Links); + + if (links) + { + memoryItem = CONTAINING_RECORD(links, PH_MEMORY_ITEM, Links); + + if ((ULONG_PTR)Address < (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize) + return memoryItem; + } + + return NULL; +} + +PPH_MEMORY_ITEM PhpSetMemoryRegionType( + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ PVOID Address, + _In_ BOOLEAN GoToAllocationBase, + _In_ PH_MEMORY_REGION_TYPE RegionType + ) +{ + PPH_MEMORY_ITEM memoryItem; + + memoryItem = PhLookupMemoryItemList(List, Address); + + if (!memoryItem) + return NULL; + + if (GoToAllocationBase && memoryItem->AllocationBaseItem) + memoryItem = memoryItem->AllocationBaseItem; + + if (memoryItem->RegionType != UnknownRegion) + return NULL; + + memoryItem->RegionType = RegionType; + + return memoryItem; +} + +NTSTATUS PhpUpdateMemoryRegionTypes( + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ HANDLE ProcessHandle + ) +{ + NTSTATUS status; + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + ULONG i; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; +#endif + PPH_MEMORY_ITEM memoryItem; + PLIST_ENTRY listEntry; + + if (!NT_SUCCESS(status = PhEnumProcessesEx(&processes, SystemExtendedProcessInformation))) + return status; + + process = PhFindProcessInformation(processes, List->ProcessId); + + if (!process) + { + PhFree(processes); + return STATUS_NOT_FOUND; + } + + // USER_SHARED_DATA + PhpSetMemoryRegionType(List, USER_SHARED_DATA, TRUE, UserSharedDataRegion); + + // HYPERVISOR_SHARED_DATA + if (WindowsVersion >= WINDOWS_10_RS4) + { + static PVOID HypervisorSharedDataVa = NULL; + static PH_INITONCE HypervisorSharedDataInitOnce = PH_INITONCE_INIT; + + if (PhBeginInitOnce(&HypervisorSharedDataInitOnce)) + { + SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION hypervSharedPageInfo; + + if (NT_SUCCESS(NtQuerySystemInformation( + SystemHypervisorSharedPageInformation, + &hypervSharedPageInfo, + sizeof(SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION), + NULL + ))) + { + HypervisorSharedDataVa = hypervSharedPageInfo.HypervisorSharedUserVa; + } + + PhEndInitOnce(&HypervisorSharedDataInitOnce); + } + + if (HypervisorSharedDataVa) + { + PhpSetMemoryRegionType(List, HypervisorSharedDataVa, TRUE, HypervisorSharedDataRegion); + } + } + + // PEB, heap + { + PROCESS_BASIC_INFORMATION basicInfo; + ULONG numberOfHeaps; + PVOID processHeapsPtr; + PVOID *processHeaps; + PVOID apiSetMap; + ULONG i; +#ifdef _WIN64 + PVOID peb32; + ULONG processHeapsPtr32; + ULONG *processHeaps32; + ULONG apiSetMap32; +#endif + + if (NT_SUCCESS(PhGetProcessBasicInformation(ProcessHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) + { + // HACK: Windows 10 RS2 and above 'added TEB/PEB sub-VAD segments' and we need to tag individual sections. (dmex) + PhpSetMemoryRegionType(List, basicInfo.PebBaseAddress, WindowsVersion < WINDOWS_10_RS2 ? TRUE : FALSE, PebRegion); + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, NumberOfHeaps)), + &numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS) + { + processHeaps = PhAllocate(numberOfHeaps * sizeof(PVOID)); + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessHeaps)), + &processHeapsPtr, sizeof(PVOID), NULL)) && + NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, + processHeapsPtr, + processHeaps, numberOfHeaps * sizeof(PVOID), NULL))) + { + for (i = 0; i < numberOfHeaps; i++) + { + if (memoryItem = PhpSetMemoryRegionType(List, processHeaps[i], TRUE, HeapRegion)) + memoryItem->u.Heap.Index = i; + } + } + + PhFree(processHeaps); + } + + // ApiSet schema map + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ApiSetMap)), + &apiSetMap, + sizeof(PVOID), + NULL + ))) + { + PhpSetMemoryRegionType(List, apiSetMap, TRUE, ApiSetMapRegion); + } + } +#ifdef _WIN64 + + if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32 != 0) + { + isWow64 = TRUE; + PhpSetMemoryRegionType(List, peb32, TRUE, Peb32Region); + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, NumberOfHeaps)), + &numberOfHeaps, sizeof(ULONG), NULL)) && numberOfHeaps < MAX_HEAPS) + { + processHeaps32 = PhAllocate(numberOfHeaps * sizeof(ULONG)); + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessHeaps)), + &processHeapsPtr32, sizeof(ULONG), NULL)) && + NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, + UlongToPtr(processHeapsPtr32), + processHeaps32, numberOfHeaps * sizeof(ULONG), NULL))) + { + for (i = 0; i < numberOfHeaps; i++) + { + if (memoryItem = PhpSetMemoryRegionType(List, UlongToPtr(processHeaps32[i]), TRUE, Heap32Region)) + memoryItem->u.Heap.Index = i; + } + } + + PhFree(processHeaps32); + } + + // ApiSet schema map + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ApiSetMap)), + &apiSetMap32, + sizeof(ULONG), + NULL + ))) + { + PhpSetMemoryRegionType(List, UlongToPtr(apiSetMap32), TRUE, ApiSetMapRegion); + } + } +#endif + } + + // TEB, stack + for (i = 0; i < process->NumberOfThreads; i++) + { + PSYSTEM_EXTENDED_THREAD_INFORMATION thread = (PSYSTEM_EXTENDED_THREAD_INFORMATION)process->Threads + i; + + if (thread->TebBase) + { + NT_TIB ntTib; + SIZE_T bytesRead; + + // HACK: Windows 10 RS2 and above 'added TEB/PEB sub-VAD segments' and we need to tag individual sections. + if (memoryItem = PhpSetMemoryRegionType(List, thread->TebBase, WindowsVersion < WINDOWS_10_RS2 ? TRUE : FALSE, TebRegion)) + memoryItem->u.Teb.ThreadId = thread->ThreadInfo.ClientId.UniqueThread; + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, thread->TebBase, &ntTib, sizeof(NT_TIB), &bytesRead)) && + bytesRead == sizeof(NT_TIB)) + { + if ((ULONG_PTR)ntTib.StackLimit < (ULONG_PTR)ntTib.StackBase) + { + if (memoryItem = PhpSetMemoryRegionType(List, ntTib.StackLimit, TRUE, StackRegion)) + memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread; + } +#ifdef _WIN64 + + if (isWow64 && ntTib.ExceptionList) + { + ULONG teb32 = PtrToUlong(ntTib.ExceptionList); + NT_TIB32 ntTib32; + + // 64-bit and 32-bit TEBs usually share the same memory region, so don't do anything for the 32-bit + // TEB. + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, UlongToPtr(teb32), &ntTib32, sizeof(NT_TIB32), &bytesRead)) && + bytesRead == sizeof(NT_TIB32)) + { + if (ntTib32.StackLimit < ntTib32.StackBase) + { + if (memoryItem = PhpSetMemoryRegionType(List, UlongToPtr(ntTib32.StackLimit), TRUE, Stack32Region)) + memoryItem->u.Stack.ThreadId = thread->ThreadInfo.ClientId.UniqueThread; + } + } + } +#endif + } + } + } + + // Mapped file, heap segment, unusable + for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink) + { + memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + + if (memoryItem->RegionType != UnknownRegion) + continue; + + if ((memoryItem->Type & (MEM_MAPPED | MEM_IMAGE)) && memoryItem->AllocationBaseItem == memoryItem) + { + PPH_STRING fileName; + + if (NT_SUCCESS(PhGetProcessMappedFileName(ProcessHandle, memoryItem->BaseAddress, &fileName))) + { + PPH_STRING newFileName = PhResolveDevicePrefix(fileName); + + if (newFileName) + PhMoveReference(&fileName, newFileName); + + memoryItem->RegionType = MappedFileRegion; + memoryItem->u.MappedFile.FileName = fileName; + continue; + } + } + + if (memoryItem->State & MEM_COMMIT && memoryItem->Valid && !memoryItem->Bad) + { + UCHAR buffer[HEAP_SEGMENT_MAX_SIZE]; + + if (NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, memoryItem->BaseAddress, buffer, sizeof(buffer), NULL))) + { + PVOID candidateHeap = NULL; + ULONG candidateHeap32 = 0; + PPH_MEMORY_ITEM heapMemoryItem; + PHEAP_SEGMENT heapSegment = (PHEAP_SEGMENT)buffer; + PHEAP_SEGMENT32 heapSegment32 = (PHEAP_SEGMENT32)buffer; + + if (heapSegment->SegmentSignature == HEAP_SEGMENT_SIGNATURE) + candidateHeap = heapSegment->Heap; + if (heapSegment32->SegmentSignature == HEAP_SEGMENT_SIGNATURE) + candidateHeap32 = heapSegment32->Heap; + + if (candidateHeap) + { + heapMemoryItem = PhLookupMemoryItemList(List, candidateHeap); + + if (heapMemoryItem && heapMemoryItem->BaseAddress == candidateHeap && + heapMemoryItem->RegionType == HeapRegion) + { + memoryItem->RegionType = HeapSegmentRegion; + memoryItem->u.HeapSegment.HeapItem = heapMemoryItem; + continue; + } + } + else if (candidateHeap32) + { + heapMemoryItem = PhLookupMemoryItemList(List, UlongToPtr(candidateHeap32)); + + if (heapMemoryItem && heapMemoryItem->BaseAddress == UlongToPtr(candidateHeap32) && + heapMemoryItem->RegionType == Heap32Region) + { + memoryItem->RegionType = HeapSegment32Region; + memoryItem->u.HeapSegment.HeapItem = heapMemoryItem; + continue; + } + } + } + } + } + +#ifdef _WIN64 + + PS_SYSTEM_DLL_INIT_BLOCK ldrInitBlock = { 0 }; + PVOID ldrInitBlockBaseAddress = NULL; + PPH_MEMORY_ITEM cfgBitmapMemoryItem; + PH_STRINGREF systemRootString; + PPH_STRING ntdllFileName; + + PhGetSystemRoot(&systemRootString); + ntdllFileName = PhConcatStringRefZ(&systemRootString, L"\\System32\\ntdll.dll"); + + status = PhGetProcedureAddressRemote( + ProcessHandle, + ntdllFileName->Buffer, + "LdrSystemDllInitBlock", + 0, + &ldrInitBlockBaseAddress, + NULL + ); + + if (NT_SUCCESS(status) && ldrInitBlockBaseAddress) + { + status = NtReadVirtualMemory( + ProcessHandle, + ldrInitBlockBaseAddress, + &ldrInitBlock, + sizeof(PS_SYSTEM_DLL_INIT_BLOCK), + NULL + ); + } + + PhDereferenceObject(ntdllFileName); + + if (NT_SUCCESS(status) && ldrInitBlock.Size != 0) + { + PVOID cfgBitmapAddress = NULL; + PVOID cfgBitmapWow64Address = NULL; + + if (RTL_CONTAINS_FIELD(&ldrInitBlock, ldrInitBlock.Size, Wow64CfgBitMap)) + { + cfgBitmapAddress = (PVOID)ldrInitBlock.CfgBitMap; + cfgBitmapWow64Address = (PVOID)ldrInitBlock.Wow64CfgBitMap; + } + + if (cfgBitmapAddress && (cfgBitmapMemoryItem = PhLookupMemoryItemList(List, cfgBitmapAddress))) + { + PLIST_ENTRY listEntry = &cfgBitmapMemoryItem->ListEntry; + PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + + while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem) + { + // lucasg: We could do a finer tagging since each MEM_COMMIT memory + // map is the CFG bitmap of a loaded module. However that might be + // brittle to changes made by Windows dev teams. + memoryItem->RegionType = CfgBitmapRegion; + + listEntry = listEntry->Flink; + memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + } + } + + // Note: Wow64 processes on 64bit also have CfgBitmap regions. + if (isWow64 && cfgBitmapWow64Address && (cfgBitmapMemoryItem = PhLookupMemoryItemList(List, cfgBitmapWow64Address))) + { + PLIST_ENTRY listEntry = &cfgBitmapMemoryItem->ListEntry; + PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + + while (memoryItem->AllocationBaseItem == cfgBitmapMemoryItem) + { + memoryItem->RegionType = CfgBitmap32Region; + + listEntry = listEntry->Flink; + memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + } + } + } +#endif + + PhFree(processes); + + return STATUS_SUCCESS; +} + +NTSTATUS PhpUpdateMemoryWsCounters( + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ HANDLE ProcessHandle + ) +{ + PLIST_ENTRY listEntry; + PMEMORY_WORKING_SET_EX_INFORMATION info; + + info = PhAllocatePage(WS_REQUEST_COUNT * sizeof(MEMORY_WORKING_SET_EX_INFORMATION), NULL); + + if (!info) + return STATUS_NO_MEMORY; + + for (listEntry = List->ListHead.Flink; listEntry != &List->ListHead; listEntry = listEntry->Flink) + { + PPH_MEMORY_ITEM memoryItem = CONTAINING_RECORD(listEntry, PH_MEMORY_ITEM, ListEntry); + ULONG_PTR virtualAddress; + SIZE_T remainingPages; + SIZE_T requestPages; + SIZE_T i; + + if (!(memoryItem->State & MEM_COMMIT)) + continue; + + virtualAddress = (ULONG_PTR)memoryItem->BaseAddress; + remainingPages = memoryItem->RegionSize / PAGE_SIZE; + + while (remainingPages != 0) + { + requestPages = min(remainingPages, WS_REQUEST_COUNT); + + for (i = 0; i < requestPages; i++) + { + info[i].VirtualAddress = (PVOID)virtualAddress; + virtualAddress += PAGE_SIZE; + } + + if (NT_SUCCESS(NtQueryVirtualMemory( + ProcessHandle, + NULL, + MemoryWorkingSetExInformation, + info, + requestPages * sizeof(MEMORY_WORKING_SET_EX_INFORMATION), + NULL + ))) + { + for (i = 0; i < requestPages; i++) + { + PMEMORY_WORKING_SET_EX_BLOCK block = &info[i].u1.VirtualAttributes; + + if (block->Valid) + { + memoryItem->TotalWorkingSetPages++; + + if (block->ShareCount > 1) + memoryItem->SharedWorkingSetPages++; + if (block->ShareCount == 0) + memoryItem->PrivateWorkingSetPages++; + if (block->Shared) + memoryItem->ShareableWorkingSetPages++; + if (block->Locked) + memoryItem->LockedWorkingSetPages++; + } + } + } + + remainingPages -= requestPages; + } + } + + PhFreePage(info); + + return STATUS_SUCCESS; +} + +NTSTATUS PhpUpdateMemoryWsCountersOld( + _In_ PPH_MEMORY_ITEM_LIST List, + _In_ HANDLE ProcessHandle + ) +{ + NTSTATUS status; + PMEMORY_WORKING_SET_INFORMATION info; + PPH_MEMORY_ITEM memoryItem = NULL; + ULONG_PTR i; + + if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation(ProcessHandle, &info))) + return status; + + for (i = 0; i < info->NumberOfEntries; i++) + { + PMEMORY_WORKING_SET_BLOCK block = &info->WorkingSetInfo[i]; + ULONG_PTR virtualAddress = block->VirtualPage * PAGE_SIZE; + + if (!memoryItem || virtualAddress < (ULONG_PTR)memoryItem->BaseAddress || + virtualAddress >= (ULONG_PTR)memoryItem->BaseAddress + memoryItem->RegionSize) + { + memoryItem = PhLookupMemoryItemList(List, (PVOID)virtualAddress); + } + + if (memoryItem) + { + memoryItem->TotalWorkingSetPages++; + + if (block->ShareCount > 1) + memoryItem->SharedWorkingSetPages++; + if (block->ShareCount == 0) + memoryItem->PrivateWorkingSetPages++; + if (block->Shared) + memoryItem->ShareableWorkingSetPages++; + } + } + + PhFree(info); + + return STATUS_SUCCESS; +} + +NTSTATUS PhQueryMemoryItemList( + _In_ HANDLE ProcessId, + _In_ ULONG Flags, + _Out_ PPH_MEMORY_ITEM_LIST List + ) +{ + NTSTATUS status; + HANDLE processHandle; + ULONG_PTR allocationGranularity; + PVOID baseAddress = (PVOID)0; + MEMORY_BASIC_INFORMATION basicInfo; + PPH_MEMORY_ITEM allocationBaseItem = NULL; + PPH_MEMORY_ITEM previousMemoryItem = NULL; + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ProcessId + ))) + { + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION, + ProcessId + ))) + { + return status; + } + } + + List->ProcessId = ProcessId; + PhInitializeAvlTree(&List->Set, PhpMemoryItemCompareFunction); + InitializeListHead(&List->ListHead); + + allocationGranularity = PhSystemBasicInformation.AllocationGranularity; + + while (NT_SUCCESS(NtQueryVirtualMemory( + processHandle, + baseAddress, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + PPH_MEMORY_ITEM memoryItem; + MEMORY_WORKING_SET_EX_INFORMATION info; + + if (basicInfo.State & MEM_FREE) + { + if (Flags & PH_QUERY_MEMORY_IGNORE_FREE) + goto ContinueLoop; + + basicInfo.AllocationBase = basicInfo.BaseAddress; + } + + memoryItem = PhCreateMemoryItem(); + memoryItem->BasicInfo = basicInfo; + + if (basicInfo.AllocationBase == basicInfo.BaseAddress) + allocationBaseItem = memoryItem; + if (allocationBaseItem && basicInfo.AllocationBase == allocationBaseItem->BaseAddress) + memoryItem->AllocationBaseItem = allocationBaseItem; + + if (basicInfo.State & MEM_COMMIT) + { + memoryItem->CommittedSize = memoryItem->RegionSize; + + if (basicInfo.Type & MEM_PRIVATE) + memoryItem->PrivateSize = memoryItem->RegionSize; + } + + // Query the region attributes (dmex) + info.VirtualAddress = baseAddress; + + if (NT_SUCCESS(NtQueryVirtualMemory( + processHandle, + NULL, + MemoryWorkingSetExInformation, + &info, + sizeof(MEMORY_WORKING_SET_EX_INFORMATION), + NULL + ))) + { + PMEMORY_WORKING_SET_EX_BLOCK block = &info.u1.VirtualAttributes; + + memoryItem->Valid = !!block->Valid; + memoryItem->Bad = !!block->Bad; + } + + PhAddElementAvlTree(&List->Set, &memoryItem->Links); + InsertTailList(&List->ListHead, &memoryItem->ListEntry); + + if (basicInfo.State & MEM_FREE) + { + if ((ULONG_PTR)basicInfo.BaseAddress & (allocationGranularity - 1)) + { + ULONG_PTR nextAllocationBase; + ULONG_PTR potentialUnusableSize; + + // Split this free region into an unusable and a (possibly empty) usable region. + + nextAllocationBase = ALIGN_UP_BY(basicInfo.BaseAddress, allocationGranularity); + potentialUnusableSize = nextAllocationBase - (ULONG_PTR)basicInfo.BaseAddress; + + memoryItem->RegionType = UnusableRegion; + + // VMMap does this, but is it correct? + //if (previousMemoryItem && (previousMemoryItem->State & MEM_COMMIT)) + // memoryItem->CommittedSize = min(potentialUnusableSize, basicInfo.RegionSize); + + if (nextAllocationBase < (ULONG_PTR)basicInfo.BaseAddress + basicInfo.RegionSize) + { + PPH_MEMORY_ITEM otherMemoryItem; + + memoryItem->RegionSize = potentialUnusableSize; + + otherMemoryItem = PhCreateMemoryItem(); + otherMemoryItem->BasicInfo = basicInfo; + otherMemoryItem->BaseAddress = (PVOID)nextAllocationBase; + otherMemoryItem->AllocationBase = otherMemoryItem->BaseAddress; + otherMemoryItem->RegionSize = basicInfo.RegionSize - potentialUnusableSize; + otherMemoryItem->AllocationBaseItem = otherMemoryItem; + + PhAddElementAvlTree(&List->Set, &otherMemoryItem->Links); + InsertTailList(&List->ListHead, &otherMemoryItem->ListEntry); + + previousMemoryItem = otherMemoryItem; + goto ContinueLoop; + } + } + } + + previousMemoryItem = memoryItem; + +ContinueLoop: + baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); + } + + if (Flags & PH_QUERY_MEMORY_REGION_TYPE) + PhpUpdateMemoryRegionTypes(List, processHandle); + + if (Flags & PH_QUERY_MEMORY_WS_COUNTERS) + PhpUpdateMemoryWsCounters(List, processHandle); + + NtClose(processHandle); + + return STATUS_SUCCESS; +} diff --git a/ProcessHacker/memrslt.c b/ProcessHacker/memrslt.c index c9c7bb79a94b..7b5fe8a7ebe1 100644 --- a/ProcessHacker/memrslt.c +++ b/ProcessHacker/memrslt.c @@ -1,622 +1,753 @@ -/* - * Process Hacker - - * memory search results - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -#include -#include -#include -#include - -#include "pcre/pcre2.h" - -#define FILTER_CONTAINS 1 -#define FILTER_CONTAINS_IGNORECASE 2 -#define FILTER_REGEX 3 -#define FILTER_REGEX_IGNORECASE 4 - -typedef struct _MEMORY_RESULTS_CONTEXT -{ - HANDLE ProcessId; - PPH_LIST Results; - - PH_LAYOUT_MANAGER LayoutManager; -} MEMORY_RESULTS_CONTEXT, *PMEMORY_RESULTS_CONTEXT; - -INT_PTR CALLBACK PhpMemoryResultsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static RECT MinimumSize = { -1, -1, -1, -1 }; - -VOID PhShowMemoryResultsDialog( - _In_ HANDLE ProcessId, - _In_ PPH_LIST Results - ) -{ - HWND windowHandle; - PMEMORY_RESULTS_CONTEXT context; - ULONG i; - - context = PhAllocate(sizeof(MEMORY_RESULTS_CONTEXT)); - context->ProcessId = ProcessId; - context->Results = Results; - - PhReferenceObject(Results); - - for (i = 0; i < Results->Count; i++) - PhReferenceMemoryResult(Results->Items[i]); - - windowHandle = CreateDialogParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MEMRESULTS), - NULL, - PhpMemoryResultsDlgProc, - (LPARAM)context - ); - ShowWindow(windowHandle, SW_SHOW); -} - -static PPH_STRING PhpGetStringForSelectedResults( - _In_ HWND ListViewHandle, - _In_ PPH_LIST Results, - _In_ BOOLEAN All - ) -{ - PH_STRING_BUILDER stringBuilder; - ULONG i; - - PhInitializeStringBuilder(&stringBuilder, 0x100); - - for (i = 0; i < Results->Count; i++) - { - PPH_MEMORY_RESULT result; - - if (!All) - { - if (!(ListView_GetItemState(ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED)) - continue; - } - - result = Results->Items[i]; - - PhAppendFormatStringBuilder(&stringBuilder, L"0x%Ix (%u): %s\r\n", result->Address, result->Length, - result->Display.Buffer ? result->Display.Buffer : L""); - } - - return PhFinalStringBuilderString(&stringBuilder); -} - -static VOID FilterResults( - _In_ HWND hwndDlg, - _In_ PMEMORY_RESULTS_CONTEXT Context, - _In_ ULONG Type - ) -{ - PPH_STRING selectedChoice = NULL; - PPH_LIST results; - pcre2_code *compiledExpression; - pcre2_match_data *matchData; - - results = Context->Results; - - SetCursor(LoadCursor(NULL, IDC_WAIT)); - - while (PhaChoiceDialog( - hwndDlg, - L"Filter", - L"Enter the filter pattern:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - L"MemFilterChoices" - )) - { - PPH_LIST newResults = NULL; - ULONG i; - - if (Type == FILTER_CONTAINS || Type == FILTER_CONTAINS_IGNORECASE) - { - newResults = PhCreateList(1024); - - if (Type == FILTER_CONTAINS) - { - for (i = 0; i < results->Count; i++) - { - PPH_MEMORY_RESULT result = results->Items[i]; - - if (wcsstr(result->Display.Buffer, selectedChoice->Buffer)) - { - PhReferenceMemoryResult(result); - PhAddItemList(newResults, result); - } - } - } - else - { - PPH_STRING upperChoice; - - upperChoice = PhaUpperString(selectedChoice); - - for (i = 0; i < results->Count; i++) - { - PPH_MEMORY_RESULT result = results->Items[i]; - PWSTR upperDisplay; - - upperDisplay = PhAllocateForMemorySearch(result->Display.Length + sizeof(WCHAR)); - // Copy the null terminator as well. - memcpy(upperDisplay, result->Display.Buffer, result->Display.Length + sizeof(WCHAR)); - - _wcsupr(upperDisplay); - - if (wcsstr(upperDisplay, upperChoice->Buffer)) - { - PhReferenceMemoryResult(result); - PhAddItemList(newResults, result); - } - - PhFreeForMemorySearch(upperDisplay); - } - } - } - else if (Type == FILTER_REGEX || Type == FILTER_REGEX_IGNORECASE) - { - int errorCode; - PCRE2_SIZE errorOffset; - - compiledExpression = pcre2_compile( - selectedChoice->Buffer, - selectedChoice->Length / sizeof(WCHAR), - (Type == FILTER_REGEX_IGNORECASE ? PCRE2_CASELESS : 0) | PCRE2_DOTALL, - &errorCode, - &errorOffset, - NULL - ); - - if (!compiledExpression) - { - PhShowError(hwndDlg, L"Unable to compile the regular expression: \"%s\" at position %zu.", - PhGetStringOrDefault(PH_AUTO(PhPcre2GetErrorMessage(errorCode)), L"Unknown error"), - errorOffset - ); - continue; - } - - matchData = pcre2_match_data_create_from_pattern(compiledExpression, NULL); - - newResults = PhCreateList(1024); - - for (i = 0; i < results->Count; i++) - { - PPH_MEMORY_RESULT result = results->Items[i]; - - if (pcre2_match( - compiledExpression, - result->Display.Buffer, - result->Display.Length / sizeof(WCHAR), - 0, - 0, - matchData, - NULL - ) >= 0) - { - PhReferenceMemoryResult(result); - PhAddItemList(newResults, result); - } - } - - pcre2_match_data_free(matchData); - pcre2_code_free(compiledExpression); - } - - if (newResults) - { - PhShowMemoryResultsDialog(Context->ProcessId, newResults); - PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)newResults->Items, newResults->Count); - PhDereferenceObject(newResults); - break; - } - } - - SetCursor(LoadCursor(NULL, IDC_ARROW)); -} - -INT_PTR CALLBACK PhpMemoryResultsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PMEMORY_RESULTS_CONTEXT context; - - if (uMsg != WM_INITDIALOG) - { - context = GetProp(hwndDlg, PhMakeContextAtom()); - } - else - { - context = (PMEMORY_RESULTS_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - PhRegisterDialog(hwndDlg); - - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(context->ProcessId)) - { - SetWindowText(hwndDlg, PhaFormatString(L"Results - %s (%u)", - processItem->ProcessName->Buffer, HandleToUlong(processItem->ProcessId))->Buffer); - PhDereferenceObject(processItem); - } - } - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Address"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Length"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 200, L"Result"); - - PhLoadListViewColumnsFromSetting(L"MemResultsListViewColumns", lvHandle); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - - if (MinimumSize.left == -1) - { - RECT rect; - - rect.left = 0; - rect.top = 0; - rect.right = 250; - rect.bottom = 180; - MapDialogRect(hwndDlg, &rect); - MinimumSize = rect; - MinimumSize.left = 0; - } - - ListView_SetItemCount(lvHandle, context->Results->Count); - - SetDlgItemText(hwndDlg, IDC_INTRO, PhaFormatString(L"%s results.", - PhaFormatUInt64(context->Results->Count, TRUE)->Buffer)->Buffer); - - { - PH_RECTANGLE windowRectangle; - - windowRectangle.Position = PhGetIntegerPairSetting(L"MemResultsPosition"); - windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MemResultsSize", TRUE).Pair; - PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); - - MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top, - windowRectangle.Width, windowRectangle.Height, FALSE); - - // Implement cascading by saving an offsetted rectangle. - windowRectangle.Left += 20; - windowRectangle.Top += 20; - - PhSetIntegerPairSetting(L"MemResultsPosition", windowRectangle.Position); - PhSetScalableIntegerPairSetting2(L"MemResultsSize", windowRectangle.Size); - } - } - break; - case WM_DESTROY: - { - PhSaveWindowPlacementToSetting(L"MemResultsPosition", L"MemResultsSize", hwndDlg); - PhSaveListViewColumnsToSetting(L"MemResultsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); - - PhDeleteLayoutManager(&context->LayoutManager); - PhUnregisterDialog(hwndDlg); - RemoveProp(hwndDlg, PhMakeContextAtom()); - - PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)context->Results->Items, context->Results->Count); - PhDereferenceObject(context->Results); - PhFree(context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - DestroyWindow(hwndDlg); - break; - case IDC_COPY: - { - HWND lvHandle; - PPH_STRING string; - ULONG selectedCount; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - selectedCount = ListView_GetSelectedCount(lvHandle); - - if (selectedCount == 0) - { - // User didn't select anything, so copy all items. - string = PhpGetStringForSelectedResults(lvHandle, context->Results, TRUE); - PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED); - } - else - { - string = PhpGetStringForSelectedResults(lvHandle, context->Results, FALSE); - } - - PhSetClipboardString(hwndDlg, &string->sr); - PhDereferenceObject(string); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)lvHandle, TRUE); - } - break; - case IDC_SAVE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Text files (*.txt)", L"*.txt" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - - fileDialog = PhCreateSaveFileDialog(); - - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, L"Search results.txt"); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - NTSTATUS status; - PPH_STRING fileName; - PPH_FILE_STREAM fileStream; - PPH_STRING string; - - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - - if (NT_SUCCESS(status = PhCreateFileStream( - &fileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - 0 - ))) - { - PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); - PhWritePhTextHeader(fileStream); - - string = PhpGetStringForSelectedResults(GetDlgItem(hwndDlg, IDC_LIST), context->Results, TRUE); - PhWriteStringAsUtf8FileStreamEx(fileStream, string->Buffer, string->Length); - PhDereferenceObject(string); - - PhDereferenceObject(fileStream); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); - } - - PhFreeFileDialog(fileDialog); - } - break; - case IDC_FILTER: - { - PPH_EMENU menu; - RECT buttonRect; - POINT point; - PPH_EMENU_ITEM selectedItem; - ULONG filterType = 0; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MEMFILTER), 0); - - GetClientRect(GetDlgItem(hwndDlg, IDC_FILTER), &buttonRect); - point.x = 0; - point.y = buttonRect.bottom; - - ClientToScreen(GetDlgItem(hwndDlg, IDC_FILTER), &point); - selectedItem = PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); - - if (selectedItem) - { - switch (selectedItem->Id) - { - case ID_FILTER_CONTAINS: - filterType = FILTER_CONTAINS; - break; - case ID_FILTER_CONTAINS_CASEINSENSITIVE: - filterType = FILTER_CONTAINS_IGNORECASE; - break; - case ID_FILTER_REGEX: - filterType = FILTER_REGEX; - break; - case ID_FILTER_REGEX_CASEINSENSITIVE: - filterType = FILTER_REGEX_IGNORECASE; - break; - } - } - - if (filterType != 0) - FilterResults(hwndDlg, context, filterType); - - PhDestroyEMenu(menu); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - HWND lvHandle; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhHandleListViewNotifyForCopy(lParam, lvHandle); - - switch (header->code) - { - case LVN_GETDISPINFO: - { - NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header; - - if (dispInfo->item.mask & LVIF_TEXT) - { - PPH_MEMORY_RESULT result = context->Results->Items[dispInfo->item.iItem]; - - switch (dispInfo->item.iSubItem) - { - case 0: - { - WCHAR addressString[PH_PTR_STR_LEN_1]; - - PhPrintPointer(addressString, result->Address); - wcsncpy_s( - dispInfo->item.pszText, - dispInfo->item.cchTextMax, - addressString, - _TRUNCATE - ); - } - break; - case 1: - { - WCHAR lengthString[PH_INT32_STR_LEN_1]; - - PhPrintUInt32(lengthString, (ULONG)result->Length); - wcsncpy_s( - dispInfo->item.pszText, - dispInfo->item.cchTextMax, - lengthString, - _TRUNCATE - ); - } - break; - case 2: - wcsncpy_s( - dispInfo->item.pszText, - dispInfo->item.cchTextMax, - result->Display.Buffer, - _TRUNCATE - ); - break; - } - } - } - break; - case NM_DBLCLK: - { - if (header->hwndFrom == lvHandle) - { - INT index; - - if ((index = ListView_GetNextItem( - lvHandle, - -1, - LVNI_SELECTED - )) != -1) - { - NTSTATUS status; - PPH_MEMORY_RESULT result = context->Results->Items[index]; - HANDLE processHandle; - MEMORY_BASIC_INFORMATION basicInfo; - PPH_SHOW_MEMORY_EDITOR showMemoryEditor; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - context->ProcessId - ))) - { - if (NT_SUCCESS(status = NtQueryVirtualMemory( - processHandle, - result->Address, - MemoryBasicInformation, - &basicInfo, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - { - showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); - memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); - showMemoryEditor->ProcessId = context->ProcessId; - showMemoryEditor->BaseAddress = basicInfo.BaseAddress; - showMemoryEditor->RegionSize = basicInfo.RegionSize; - showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)result->Address - (ULONG_PTR)basicInfo.BaseAddress); - showMemoryEditor->SelectLength = (ULONG)result->Length; - ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); - } - - NtClose(processHandle); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to edit memory", status, 0); - } - } - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * memory search results + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include "pcre/pcre2.h" + +#define FILTER_CONTAINS 1 +#define FILTER_CONTAINS_IGNORECASE 2 +#define FILTER_REGEX 3 +#define FILTER_REGEX_IGNORECASE 4 + +typedef struct _MEMORY_RESULTS_CONTEXT +{ + HANDLE ProcessId; + PPH_LIST Results; + + PH_LAYOUT_MANAGER LayoutManager; +} MEMORY_RESULTS_CONTEXT, *PMEMORY_RESULTS_CONTEXT; + +INT_PTR CALLBACK PhpMemoryResultsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static RECT MinimumSize = { -1, -1, -1, -1 }; + +VOID PhShowMemoryResultsDialog( + _In_ HANDLE ProcessId, + _In_ PPH_LIST Results + ) +{ + HWND windowHandle; + PMEMORY_RESULTS_CONTEXT context; + ULONG i; + + context = PhAllocate(sizeof(MEMORY_RESULTS_CONTEXT)); + context->ProcessId = ProcessId; + context->Results = Results; + + PhReferenceObject(Results); + + for (i = 0; i < Results->Count; i++) + PhReferenceMemoryResult(Results->Items[i]); + + windowHandle = CreateDialogParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MEMRESULTS), + NULL, + PhpMemoryResultsDlgProc, + (LPARAM)context + ); + ShowWindow(windowHandle, SW_SHOW); + SetForegroundWindow(windowHandle); +} + +static PPH_STRING PhpGetStringForSelectedResults( + _In_ HWND ListViewHandle, + _In_ PPH_LIST Results, + _In_ BOOLEAN All + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i; + + PhInitializeStringBuilder(&stringBuilder, 0x100); + + for (i = 0; i < Results->Count; i++) + { + PPH_MEMORY_RESULT result; + + if (!All) + { + if (!(ListView_GetItemState(ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED)) + continue; + } + + result = Results->Items[i]; + + PhAppendFormatStringBuilder(&stringBuilder, L"0x%Ix (%lu): %s\r\n", result->Address, result->Length, + result->Display.Buffer ? result->Display.Buffer : L""); + } + + return PhFinalStringBuilderString(&stringBuilder); +} + +static VOID FilterResults( + _In_ HWND hwndDlg, + _In_ PMEMORY_RESULTS_CONTEXT Context, + _In_ ULONG Type + ) +{ + PPH_STRING selectedChoice = NULL; + PPH_LIST results; + pcre2_code *compiledExpression; + pcre2_match_data *matchData; + + results = Context->Results; + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + + while (PhaChoiceDialog( + hwndDlg, + L"Filter", + L"Enter the filter pattern:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + L"MemFilterChoices" + )) + { + PPH_LIST newResults = NULL; + ULONG i; + + if (Type == FILTER_CONTAINS || Type == FILTER_CONTAINS_IGNORECASE) + { + newResults = PhCreateList(1024); + + if (Type == FILTER_CONTAINS) + { + for (i = 0; i < results->Count; i++) + { + PPH_MEMORY_RESULT result = results->Items[i]; + + if (wcsstr(result->Display.Buffer, selectedChoice->Buffer)) + { + PhReferenceMemoryResult(result); + PhAddItemList(newResults, result); + } + } + } + else + { + PPH_STRING upperChoice; + + upperChoice = PhaUpperString(selectedChoice); + + for (i = 0; i < results->Count; i++) + { + PPH_MEMORY_RESULT result = results->Items[i]; + PWSTR upperDisplay; + + upperDisplay = PhAllocateForMemorySearch(result->Display.Length + sizeof(WCHAR)); + // Copy the null terminator as well. + memcpy(upperDisplay, result->Display.Buffer, result->Display.Length + sizeof(WCHAR)); + + _wcsupr(upperDisplay); + + if (wcsstr(upperDisplay, upperChoice->Buffer)) + { + PhReferenceMemoryResult(result); + PhAddItemList(newResults, result); + } + + PhFreeForMemorySearch(upperDisplay); + } + } + } + else if (Type == FILTER_REGEX || Type == FILTER_REGEX_IGNORECASE) + { + int errorCode; + PCRE2_SIZE errorOffset; + + compiledExpression = pcre2_compile( + selectedChoice->Buffer, + selectedChoice->Length / sizeof(WCHAR), + (Type == FILTER_REGEX_IGNORECASE ? PCRE2_CASELESS : 0) | PCRE2_DOTALL, + &errorCode, + &errorOffset, + NULL + ); + + if (!compiledExpression) + { + PhShowError2(hwndDlg, L"Unable to compile the regular expression.", + L"\"%s\" at position %zu.", + PhGetStringOrDefault(PH_AUTO(PhPcre2GetErrorMessage(errorCode)), L"Unknown error"), + errorOffset + ); + continue; + } + + matchData = pcre2_match_data_create_from_pattern(compiledExpression, NULL); + + newResults = PhCreateList(1024); + + for (i = 0; i < results->Count; i++) + { + PPH_MEMORY_RESULT result = results->Items[i]; + + if (pcre2_match( + compiledExpression, + result->Display.Buffer, + result->Display.Length / sizeof(WCHAR), + 0, + 0, + matchData, + NULL + ) >= 0) + { + PhReferenceMemoryResult(result); + PhAddItemList(newResults, result); + } + } + + pcre2_match_data_free(matchData); + pcre2_code_free(compiledExpression); + } + + if (newResults) + { + PhShowMemoryResultsDialog(Context->ProcessId, newResults); + PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)newResults->Items, newResults->Count); + PhDereferenceObject(newResults); + break; + } + } + + SetCursor(LoadCursor(NULL, IDC_ARROW)); +} + +INT_PTR CALLBACK PhpMemoryResultsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PMEMORY_RESULTS_CONTEXT context; + + if (uMsg != WM_INITDIALOG) + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + else + { + context = (PMEMORY_RESULTS_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhRegisterDialog(hwndDlg); + + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(context->ProcessId)) + { + PhSetWindowText(hwndDlg, PhaFormatString(L"Results - %s (%u)", + processItem->ProcessName->Buffer, HandleToUlong(processItem->ProcessId))->Buffer); + PhDereferenceObject(processItem); + } + } + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Address"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 120, L"Base Address"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Length"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 200, L"Result"); + + PhLoadListViewColumnsFromSetting(L"MemResultsListViewColumns", lvHandle); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, + PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, + PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, + PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, + PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 250; + rect.bottom = 180; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + + ListView_SetItemCount(lvHandle, context->Results->Count); + + PhSetDialogItemText(hwndDlg, IDC_INTRO, PhaFormatString(L"%s results.", + PhaFormatUInt64(context->Results->Count, TRUE)->Buffer)->Buffer); + + { + PH_RECTANGLE windowRectangle; + + windowRectangle.Position = PhGetIntegerPairSetting(L"MemResultsPosition"); + windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MemResultsSize", TRUE).Pair; + PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); + + MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, FALSE); + + // Implement cascading by saving an offsetted rectangle. + windowRectangle.Left += 20; + windowRectangle.Top += 20; + + PhSetIntegerPairSetting(L"MemResultsPosition", windowRectangle.Position); + PhSetScalableIntegerPairSetting2(L"MemResultsSize", windowRectangle.Size); + } + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(L"MemResultsPosition", L"MemResultsSize", hwndDlg); + PhSaveListViewColumnsToSetting(L"MemResultsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + + PhDeleteLayoutManager(&context->LayoutManager); + PhUnregisterDialog(hwndDlg); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)context->Results->Items, context->Results->Count); + PhDereferenceObject(context->Results); + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + case IDC_COPY: + { + HWND lvHandle; + PPH_STRING string; + ULONG selectedCount; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + selectedCount = ListView_GetSelectedCount(lvHandle); + + if (selectedCount == 0) + { + // User didn't select anything, so copy all items. + string = PhpGetStringForSelectedResults(lvHandle, context->Results, TRUE); + PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED); + } + else + { + string = PhpGetStringForSelectedResults(lvHandle, context->Results, FALSE); + } + + PhSetClipboardString(hwndDlg, &string->sr); + PhDereferenceObject(string); + + PhSetDialogFocus(hwndDlg, lvHandle); + } + break; + case IDC_SAVE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Text files (*.txt)", L"*.txt" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + + fileDialog = PhCreateSaveFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, L"Search results.txt"); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + NTSTATUS status; + PPH_STRING fileName; + PPH_FILE_STREAM fileStream; + PPH_STRING string; + + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + + if (NT_SUCCESS(status = PhCreateFileStream( + &fileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + 0 + ))) + { + PhWriteStringAsUtf8FileStream(fileStream, &PhUnicodeByteOrderMark); + PhWritePhTextHeader(fileStream); + + string = PhpGetStringForSelectedResults(GetDlgItem(hwndDlg, IDC_LIST), context->Results, TRUE); + PhWriteStringAsUtf8FileStreamEx(fileStream, string->Buffer, string->Length); + PhDereferenceObject(string); + + PhDereferenceObject(fileStream); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); + } + + PhFreeFileDialog(fileDialog); + } + break; + case IDC_FILTER: + { + PPH_EMENU menu; + RECT buttonRect; + POINT point; + PPH_EMENU_ITEM selectedItem; + ULONG filterType = 0; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_FILTER_CONTAINS, L"Contains...", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_FILTER_CONTAINS_CASEINSENSITIVE, L"Contains (case-insensitive)...", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_FILTER_REGEX, L"Regex...", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_FILTER_REGEX_CASEINSENSITIVE, L"Regex (case-insensitive)...", NULL, NULL), ULONG_MAX); + + GetClientRect(GetDlgItem(hwndDlg, IDC_FILTER), &buttonRect); + point.x = 0; + point.y = buttonRect.bottom; + + ClientToScreen(GetDlgItem(hwndDlg, IDC_FILTER), &point); + selectedItem = PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); + + if (selectedItem) + { + switch (selectedItem->Id) + { + case ID_FILTER_CONTAINS: + filterType = FILTER_CONTAINS; + break; + case ID_FILTER_CONTAINS_CASEINSENSITIVE: + filterType = FILTER_CONTAINS_IGNORECASE; + break; + case ID_FILTER_REGEX: + filterType = FILTER_REGEX; + break; + case ID_FILTER_REGEX_CASEINSENSITIVE: + filterType = FILTER_REGEX_IGNORECASE; + break; + } + } + + if (filterType != 0) + FilterResults(hwndDlg, context, filterType); + + PhDestroyEMenu(menu); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhHandleListViewNotifyForCopy(lParam, lvHandle); + + switch (header->code) + { + case LVN_GETDISPINFO: + { + NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header; + + if (dispInfo->item.mask & LVIF_TEXT) + { + PPH_MEMORY_RESULT result = context->Results->Items[dispInfo->item.iItem]; + + switch (dispInfo->item.iSubItem) + { + case 0: + { + WCHAR addressString[PH_PTR_STR_LEN_1]; + + PhPrintPointer(addressString, result->Address); + wcsncpy_s( + dispInfo->item.pszText, + dispInfo->item.cchTextMax, + addressString, + _TRUNCATE + ); + } + break; + case 1: + { + WCHAR baseAddressString[PH_PTR_STR_LEN_1]; + + PhPrintPointer(baseAddressString, result->BaseAddress); + wcsncpy_s( + dispInfo->item.pszText, + dispInfo->item.cchTextMax, + baseAddressString, + _TRUNCATE + ); + } + break; + case 2: + { + WCHAR lengthString[PH_INT32_STR_LEN_1]; + + PhPrintUInt32(lengthString, (ULONG)result->Length); + wcsncpy_s( + dispInfo->item.pszText, + dispInfo->item.cchTextMax, + lengthString, + _TRUNCATE + ); + } + break; + case 3: + wcsncpy_s( + dispInfo->item.pszText, + dispInfo->item.cchTextMax, + result->Display.Buffer, + _TRUNCATE + ); + break; + } + } + } + break; + case NM_DBLCLK: + { + if (header->hwndFrom == lvHandle) + { + INT index; + + if ((index = ListView_GetNextItem( + lvHandle, + -1, + LVNI_SELECTED + )) != -1) + { + NTSTATUS status; + PPH_MEMORY_RESULT result = context->Results->Items[index]; + HANDLE processHandle; + MEMORY_BASIC_INFORMATION basicInfo; + PPH_SHOW_MEMORY_EDITOR showMemoryEditor; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + context->ProcessId + ))) + { + if (NT_SUCCESS(status = NtQueryVirtualMemory( + processHandle, + result->Address, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); + memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); + showMemoryEditor->ProcessId = context->ProcessId; + showMemoryEditor->BaseAddress = basicInfo.BaseAddress; + showMemoryEditor->RegionSize = basicInfo.RegionSize; + showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)result->Address - (ULONG_PTR)basicInfo.BaseAddress); + showMemoryEditor->SelectLength = (ULONG)result->Length; + ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to edit memory", status, 0); + } + } + } + break; + case NM_RCLICK: + { + if (header->hwndFrom == lvHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + ULONG filterType = 0; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MEMORY_READWRITEMEMORY, L"Read/Write memory", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), ULONG_MAX); + + GetCursorPos(&point); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (selectedItem) + { + switch (selectedItem->Id) + { + case ID_MEMORY_READWRITEMEMORY: + { + INT index; + + if ((index = ListView_GetNextItem( + lvHandle, + -1, + LVNI_SELECTED + )) != -1) + { + NTSTATUS status; + PPH_MEMORY_RESULT result = context->Results->Items[index]; + HANDLE processHandle; + MEMORY_BASIC_INFORMATION basicInfo; + PPH_SHOW_MEMORY_EDITOR showMemoryEditor; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + context->ProcessId + ))) + { + if (NT_SUCCESS(status = NtQueryVirtualMemory( + processHandle, + result->Address, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); + memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); + showMemoryEditor->ProcessId = context->ProcessId; + showMemoryEditor->BaseAddress = basicInfo.BaseAddress; + showMemoryEditor->RegionSize = basicInfo.RegionSize; + showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)result->Address - (ULONG_PTR)basicInfo.BaseAddress); + showMemoryEditor->SelectLength = (ULONG)result->Length; + ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); + } + + NtClose(processHandle); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to edit memory", status, 0); + } + } + break; + case IDC_COPY: + { + HWND lvHandle; + PPH_STRING string; + ULONG selectedCount; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + selectedCount = ListView_GetSelectedCount(lvHandle); + + if (selectedCount == 0) + { + // User didn't select anything, so copy all items. + string = PhpGetStringForSelectedResults(lvHandle, context->Results, TRUE); + PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED); + } + else + { + string = PhpGetStringForSelectedResults(lvHandle, context->Results, FALSE); + } + + PhSetClipboardString(hwndDlg, &string->sr); + PhDereferenceObject(string); + + PhSetDialogFocus(hwndDlg, lvHandle); + } + break; + } + } + + PhDestroyEMenu(menu); + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/memsrch.c b/ProcessHacker/memsrch.c index a417630d5686..4e8b50410167 100644 --- a/ProcessHacker/memsrch.c +++ b/ProcessHacker/memsrch.c @@ -1,731 +1,824 @@ -/* - * Process Hacker - - * memory searchers - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include - -#define WM_PH_MEMORY_STATUS_UPDATE (WM_APP + 301) - -#define PH_SEARCH_UPDATE 1 -#define PH_SEARCH_COMPLETED 2 - -typedef struct _MEMORY_STRING_CONTEXT -{ - HANDLE ProcessId; - HANDLE ProcessHandle; - ULONG MinimumLength; - BOOLEAN DetectUnicode; - BOOLEAN Private; - BOOLEAN Image; - BOOLEAN Mapped; - - HWND WindowHandle; - HANDLE ThreadHandle; - PH_MEMORY_STRING_OPTIONS Options; - PPH_LIST Results; -} MEMORY_STRING_CONTEXT, *PMEMORY_STRING_CONTEXT; - -INT_PTR CALLBACK PhpMemoryStringDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpMemoryStringProgressDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PVOID PhMemorySearchHeap = NULL; -LONG PhMemorySearchHeapRefCount = 0; -PH_QUEUED_LOCK PhMemorySearchHeapLock = PH_QUEUED_LOCK_INIT; - -PVOID PhAllocateForMemorySearch( - _In_ SIZE_T Size - ) -{ - PVOID memory; - - PhAcquireQueuedLockExclusive(&PhMemorySearchHeapLock); - - if (!PhMemorySearchHeap) - { - assert(PhMemorySearchHeapRefCount == 0); - PhMemorySearchHeap = RtlCreateHeap( - HEAP_GROWABLE | HEAP_CLASS_1, - NULL, - 8192 * 1024, // 8 MB - 2048 * 1024, // 2 MB - NULL, - NULL - ); - } - - if (PhMemorySearchHeap) - { - // Don't use HEAP_NO_SERIALIZE - it's very slow on Vista and above. - memory = RtlAllocateHeap(PhMemorySearchHeap, 0, Size); - - if (memory) - PhMemorySearchHeapRefCount++; - } - else - { - memory = NULL; - } - - PhReleaseQueuedLockExclusive(&PhMemorySearchHeapLock); - - return memory; -} - -VOID PhFreeForMemorySearch( - _In_ _Post_invalid_ PVOID Memory - ) -{ - PhAcquireQueuedLockExclusive(&PhMemorySearchHeapLock); - - RtlFreeHeap(PhMemorySearchHeap, 0, Memory); - - if (--PhMemorySearchHeapRefCount == 0) - { - RtlDestroyHeap(PhMemorySearchHeap); - PhMemorySearchHeap = NULL; - } - - PhReleaseQueuedLockExclusive(&PhMemorySearchHeapLock); -} - -PVOID PhCreateMemoryResult( - _In_ PVOID Address, - _In_ SIZE_T Length - ) -{ - PPH_MEMORY_RESULT result; - - result = PhAllocateForMemorySearch(sizeof(PH_MEMORY_RESULT)); - - if (!result) - return NULL; - - result->RefCount = 1; - result->Address = Address; - result->Length = Length; - result->Display.Length = 0; - result->Display.Buffer = NULL; - - return result; -} - -VOID PhReferenceMemoryResult( - _In_ PPH_MEMORY_RESULT Result - ) -{ - _InterlockedIncrement(&Result->RefCount); -} - -VOID PhDereferenceMemoryResult( - _In_ PPH_MEMORY_RESULT Result - ) -{ - if (_InterlockedDecrement(&Result->RefCount) == 0) - { - if (Result->Display.Buffer) - PhFreeForMemorySearch(Result->Display.Buffer); - - PhFreeForMemorySearch(Result); - } -} - -VOID PhDereferenceMemoryResults( - _In_reads_(NumberOfResults) PPH_MEMORY_RESULT *Results, - _In_ ULONG NumberOfResults - ) -{ - ULONG i; - - for (i = 0; i < NumberOfResults; i++) - PhDereferenceMemoryResult(Results[i]); -} - -VOID PhSearchMemoryString( - _In_ HANDLE ProcessHandle, - _In_ PPH_MEMORY_STRING_OPTIONS Options - ) -{ - ULONG minimumLength; - BOOLEAN detectUnicode; - ULONG memoryTypeMask; - PVOID baseAddress; - MEMORY_BASIC_INFORMATION basicInfo; - PUCHAR buffer; - SIZE_T bufferSize; - PWSTR displayBuffer; - SIZE_T displayBufferCount; - - minimumLength = Options->MinimumLength; - detectUnicode = Options->DetectUnicode; - memoryTypeMask = Options->MemoryTypeMask; - - if (minimumLength < 4) - return; - - baseAddress = (PVOID)0; - - bufferSize = PAGE_SIZE * 64; - buffer = PhAllocatePage(bufferSize, NULL); - - if (!buffer) - return; - - displayBufferCount = PH_DISPLAY_BUFFER_COUNT; - displayBuffer = PhAllocatePage((displayBufferCount + 1) * sizeof(WCHAR), NULL); - - if (!displayBuffer) - { - PhFreePage(buffer); - return; - } - - while (NT_SUCCESS(NtQueryVirtualMemory( - ProcessHandle, - baseAddress, - MemoryBasicInformation, - &basicInfo, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - { - ULONG_PTR offset; - SIZE_T readSize; - - if (Options->Header.Cancel) - break; - if (basicInfo.State != MEM_COMMIT) - goto ContinueLoop; - if ((basicInfo.Type & memoryTypeMask) == 0) - goto ContinueLoop; - if (basicInfo.Protect == PAGE_NOACCESS) - goto ContinueLoop; - if (basicInfo.Protect & PAGE_GUARD) - goto ContinueLoop; - - readSize = basicInfo.RegionSize; - - if (basicInfo.RegionSize > bufferSize) - { - // Don't allocate a huge buffer though. - if (basicInfo.RegionSize <= 16 * 1024 * 1024) // 16 MB - { - PhFreePage(buffer); - bufferSize = basicInfo.RegionSize; - buffer = PhAllocatePage(bufferSize, NULL); - - if (!buffer) - break; - } - else - { - readSize = bufferSize; - } - } - - for (offset = 0; offset < basicInfo.RegionSize; offset += readSize) - { - ULONG_PTR i; - UCHAR byte; // current byte - UCHAR byte1; // previous byte - UCHAR byte2; // byte before previous byte - BOOLEAN printable; - BOOLEAN printable1; - BOOLEAN printable2; - ULONG length; - - if (!NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(baseAddress, offset), - buffer, - readSize, - NULL - ))) - continue; - - byte1 = 0; - byte2 = 0; - printable1 = FALSE; - printable2 = FALSE; - length = 0; - - for (i = 0; i < readSize; i++) - { - byte = buffer[i]; - printable = PhCharIsPrintable[byte]; - - // To find strings Process Hacker uses a state table. - // * byte2 - byte before previous byte - // * byte1 - previous byte - // * byte - current byte - // * length - length of current string run - // - // The states are described below. - // - // [byte2] [byte1] [byte] ... - // [char] means printable, [oth] means non-printable. - // - // 1. [char] [char] [char] ... - // (we're in a non-wide sequence) - // -> append char. - // 2. [char] [char] [oth] ... - // (we reached the end of a non-wide sequence, or we need to start a wide sequence) - // -> if current string is big enough, create result (non-wide). - // otherwise if byte = null, reset to new string with byte1 as first character. - // otherwise if byte != null, reset to new string. - // 3. [char] [oth] [char] ... - // (we're in a wide sequence) - // -> (byte1 should = null) append char. - // 4. [char] [oth] [oth] ... - // (we reached the end of a wide sequence) - // -> (byte1 should = null) if the current string is big enough, create result (wide). - // otherwise, reset to new string. - // 5. [oth] [char] [char] ... - // (we reached the end of a wide sequence, or we need to start a non-wide sequence) - // -> (excluding byte1) if the current string is big enough, create result (wide). - // otherwise, reset to new string with byte1 as first character and byte as - // second character. - // 6. [oth] [char] [oth] ... - // (we're in a wide sequence) - // -> (byte2 and byte should = null) do nothing. - // 7. [oth] [oth] [char] ... - // (we're starting a sequence, but we don't know if it's a wide or non-wide sequence) - // -> append char. - // 8. [oth] [oth] [oth] ... - // (nothing) - // -> do nothing. - - if (printable2 && printable1 && printable) - { - if (length < displayBufferCount) - displayBuffer[length] = byte; - - length++; - } - else if (printable2 && printable1 && !printable) - { - if (length >= minimumLength) - { - goto CreateResult; - } - else if (byte == 0) - { - length = 1; - displayBuffer[0] = byte1; - } - else - { - length = 0; - } - } - else if (printable2 && !printable1 && printable) - { - if (byte1 == 0) - { - if (length < displayBufferCount) - displayBuffer[length] = byte; - - length++; - } - } - else if (printable2 && !printable1 && !printable) - { - if (length >= minimumLength) - { - goto CreateResult; - } - else - { - length = 0; - } - } - else if (!printable2 && printable1 && printable) - { - if (length >= minimumLength + 1) // length - 1 >= minimumLength but avoiding underflow - { - length--; // exclude byte1 - goto CreateResult; - } - else - { - length = 2; - displayBuffer[0] = byte1; - displayBuffer[1] = byte; - } - } - else if (!printable2 && printable1 && !printable) - { - // Nothing - } - else if (!printable2 && !printable1 && printable) - { - if (length < displayBufferCount) - displayBuffer[length] = byte; - - length++; - } - else if (!printable2 && !printable1 && !printable) - { - // Nothing - } - - goto AfterCreateResult; - -CreateResult: - { - PPH_MEMORY_RESULT result; - ULONG lengthInBytes; - ULONG bias; - BOOLEAN isWide; - ULONG displayLength; - - lengthInBytes = length; - bias = 0; - isWide = FALSE; - - if (printable1 == printable) // determine if string was wide (refer to state table, 4 and 5) - { - isWide = TRUE; - lengthInBytes *= 2; - } - - if (printable) // byte1 excluded (refer to state table, 5) - { - bias = 1; - } - - if (!(isWide && !detectUnicode) && (result = PhCreateMemoryResult( - PTR_ADD_OFFSET(baseAddress, i - bias - lengthInBytes), - lengthInBytes - ))) - { - displayLength = (ULONG)(min(length, displayBufferCount) * sizeof(WCHAR)); - - if (result->Display.Buffer = PhAllocateForMemorySearch(displayLength + sizeof(WCHAR))) - { - memcpy(result->Display.Buffer, displayBuffer, displayLength); - result->Display.Buffer[displayLength / sizeof(WCHAR)] = 0; - result->Display.Length = displayLength; - } - - Options->Header.Callback( - result, - Options->Header.Context - ); - } - - length = 0; - } -AfterCreateResult: - - byte2 = byte1; - byte1 = byte; - printable2 = printable1; - printable1 = printable; - } - } - -ContinueLoop: - baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); - } - - if (buffer) - PhFreePage(buffer); -} - -VOID PhShowMemoryStringDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - NTSTATUS status; - HANDLE processHandle; - MEMORY_STRING_CONTEXT context; - PPH_SHOW_MEMORY_RESULTS showMemoryResults; - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - ProcessItem->ProcessId - ))) - { - PhShowStatus(ParentWindowHandle, L"Unable to open the process", status, 0); - return; - } - - memset(&context, 0, sizeof(MEMORY_STRING_CONTEXT)); - context.ProcessId = ProcessItem->ProcessId; - context.ProcessHandle = processHandle; - - if (DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MEMSTRING), - ParentWindowHandle, - PhpMemoryStringDlgProc, - (LPARAM)&context - ) != IDOK) - { - NtClose(processHandle); - return; - } - - context.Results = PhCreateList(1024); - - if (DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PROGRESS), - ParentWindowHandle, - PhpMemoryStringProgressDlgProc, - (LPARAM)&context - ) == IDOK) - { - showMemoryResults = PhAllocate(sizeof(PH_SHOW_MEMORY_RESULTS)); - showMemoryResults->ProcessId = ProcessItem->ProcessId; - showMemoryResults->Results = context.Results; - - PhReferenceObject(context.Results); - ProcessHacker_ShowMemoryResults( - PhMainWndHandle, - showMemoryResults - ); - } - - PhDereferenceObject(context.Results); - NtClose(processHandle); -} - -INT_PTR CALLBACK PhpMemoryStringDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)lParam); - - SetDlgItemText(hwndDlg, IDC_MINIMUMLENGTH, L"10"); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_DETECTUNICODE), BST_CHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_PRIVATE), BST_CHECKED); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - ULONG64 minimumLength = 10; - - PhStringToInteger64(&PhaGetDlgItemText(hwndDlg, IDC_MINIMUMLENGTH)->sr, 0, &minimumLength); - - if (minimumLength < 4) - { - PhShowError(hwndDlg, L"The minimum length must be at least 4."); - break; - } - - context->MinimumLength = (ULONG)minimumLength; - context->DetectUnicode = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DETECTUNICODE)) == BST_CHECKED; - context->Private = Button_GetCheck(GetDlgItem(hwndDlg, IDC_PRIVATE)) == BST_CHECKED; - context->Image = Button_GetCheck(GetDlgItem(hwndDlg, IDC_IMAGE)) == BST_CHECKED; - context->Mapped = Button_GetCheck(GetDlgItem(hwndDlg, IDC_MAPPED)) == BST_CHECKED; - - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - } - - return FALSE; -} - -static BOOL NTAPI PhpMemoryStringResultCallback( - _In_ _Assume_refs_(1) PPH_MEMORY_RESULT Result, - _In_opt_ PVOID Context - ) -{ - PMEMORY_STRING_CONTEXT context = Context; - - PhAddItemList(context->Results, Result); - - return TRUE; -} - -NTSTATUS PhpMemoryStringThreadStart( - _In_ PVOID Parameter - ) -{ - PMEMORY_STRING_CONTEXT context = Parameter; - - context->Options.Header.Callback = PhpMemoryStringResultCallback; - context->Options.Header.Context = context; - context->Options.MinimumLength = context->MinimumLength; - context->Options.DetectUnicode = context->DetectUnicode; - - if (context->Private) - context->Options.MemoryTypeMask |= MEM_PRIVATE; - if (context->Image) - context->Options.MemoryTypeMask |= MEM_IMAGE; - if (context->Mapped) - context->Options.MemoryTypeMask |= MEM_MAPPED; - - PhSearchMemoryString(context->ProcessHandle, &context->Options); - - SendMessage( - context->WindowHandle, - WM_PH_MEMORY_STATUS_UPDATE, - PH_SEARCH_COMPLETED, - 0 - ); - - return STATUS_SUCCESS; -} - -INT_PTR CALLBACK PhpMemoryStringProgressDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)lParam; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, L"Searching..."); - - PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); - SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); - - context->WindowHandle = hwndDlg; - context->ThreadHandle = PhCreateThread(0, PhpMemoryStringThreadStart, context); - - if (!context->ThreadHandle) - { - PhShowStatus(hwndDlg, L"Unable to create the search thread", 0, GetLastError()); - EndDialog(hwndDlg, IDCANCEL); - return FALSE; - } - - SetTimer(hwndDlg, 1, 500, NULL); - } - break; - case WM_DESTROY: - { - PMEMORY_STRING_CONTEXT context; - - context = (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (context->ThreadHandle) - NtClose(context->ThreadHandle); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - PMEMORY_STRING_CONTEXT context = - (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); - context->Options.Header.Cancel = TRUE; - } - break; - } - } - break; - case WM_TIMER: - { - if (wParam == 1) - { - PMEMORY_STRING_CONTEXT context = - (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - PPH_STRING progressText; - PPH_STRING numberText; - - numberText = PhFormatUInt64(context->Results->Count, TRUE); - progressText = PhFormatString(L"%s strings found...", numberText->Buffer); - PhDereferenceObject(numberText); - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, progressText->Buffer); - PhDereferenceObject(progressText); - InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE); - } - } - break; - case WM_PH_MEMORY_STATUS_UPDATE: - { - PMEMORY_STRING_CONTEXT context; - - context = (PMEMORY_STRING_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - switch (wParam) - { - case PH_SEARCH_COMPLETED: - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * memory searchers + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include + +#define WM_PH_MEMORY_STATUS_UPDATE (WM_APP + 301) + +#define PH_SEARCH_UPDATE 1 +#define PH_SEARCH_COMPLETED 2 + +typedef struct _MEMORY_STRING_CONTEXT +{ + HANDLE ProcessId; + HANDLE ProcessHandle; + ULONG MinimumLength; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN DetectUnicode : 1; + BOOLEAN Private : 1; + BOOLEAN Image : 1; + BOOLEAN Mapped : 1; + BOOLEAN EnableCloseDialog : 1; + BOOLEAN ExtendedUnicode : 1; + BOOLEAN Spare : 2; + }; + }; + + HWND ParentWindowHandle; + HWND WindowHandle; + WNDPROC DefaultWindowProc; + PH_MEMORY_STRING_OPTIONS Options; + PPH_LIST Results; +} MEMORY_STRING_CONTEXT, *PMEMORY_STRING_CONTEXT; + +INT_PTR CALLBACK PhpMemoryStringDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOLEAN PhpShowMemoryStringProgressDialog( + _In_ PMEMORY_STRING_CONTEXT Context + ); + +PVOID PhMemorySearchHeap = NULL; +LONG PhMemorySearchHeapRefCount = 0; +PH_QUEUED_LOCK PhMemorySearchHeapLock = PH_QUEUED_LOCK_INIT; + +PVOID PhAllocateForMemorySearch( + _In_ SIZE_T Size + ) +{ + PVOID memory; + + PhAcquireQueuedLockExclusive(&PhMemorySearchHeapLock); + + if (!PhMemorySearchHeap) + { + assert(PhMemorySearchHeapRefCount == 0); + PhMemorySearchHeap = RtlCreateHeap( + HEAP_GROWABLE | HEAP_CLASS_1, + NULL, + 8192 * 1024, // 8 MB + 2048 * 1024, // 2 MB + NULL, + NULL + ); + } + + if (PhMemorySearchHeap) + { + RtlSetHeapInformation( + PhMemorySearchHeap, + HeapCompatibilityInformation, + &(ULONG){ HEAP_COMPATIBILITY_LFH }, + sizeof(ULONG) + ); + + // Don't use HEAP_NO_SERIALIZE - it's very slow on Vista and above. + memory = RtlAllocateHeap(PhMemorySearchHeap, 0, Size); + + if (memory) + PhMemorySearchHeapRefCount++; + } + else + { + memory = NULL; + } + + PhReleaseQueuedLockExclusive(&PhMemorySearchHeapLock); + + return memory; +} + +VOID PhFreeForMemorySearch( + _In_ _Post_invalid_ PVOID Memory + ) +{ + PhAcquireQueuedLockExclusive(&PhMemorySearchHeapLock); + + RtlFreeHeap(PhMemorySearchHeap, 0, Memory); + + if (--PhMemorySearchHeapRefCount == 0) + { + RtlDestroyHeap(PhMemorySearchHeap); + PhMemorySearchHeap = NULL; + } + + PhReleaseQueuedLockExclusive(&PhMemorySearchHeapLock); +} + +PVOID PhCreateMemoryResult( + _In_ PVOID Address, + _In_ PVOID BaseAddress, + _In_ SIZE_T Length + ) +{ + PPH_MEMORY_RESULT result; + + result = PhAllocateForMemorySearch(sizeof(PH_MEMORY_RESULT)); + + if (!result) + return NULL; + + result->RefCount = 1; + result->Address = Address; + result->BaseAddress = BaseAddress; + result->Length = Length; + result->Display.Length = 0; + result->Display.Buffer = NULL; + + return result; +} + +VOID PhReferenceMemoryResult( + _In_ PPH_MEMORY_RESULT Result + ) +{ + _InterlockedIncrement(&Result->RefCount); +} + +VOID PhDereferenceMemoryResult( + _In_ PPH_MEMORY_RESULT Result + ) +{ + if (_InterlockedDecrement(&Result->RefCount) == 0) + { + if (Result->Display.Buffer) + PhFreeForMemorySearch(Result->Display.Buffer); + + PhFreeForMemorySearch(Result); + } +} + +VOID PhDereferenceMemoryResults( + _In_reads_(NumberOfResults) PPH_MEMORY_RESULT *Results, + _In_ ULONG NumberOfResults + ) +{ + for (ULONG i = 0; i < NumberOfResults; i++) + PhDereferenceMemoryResult(Results[i]); +} + +VOID PhSearchMemoryString( + _In_ HANDLE ProcessHandle, + _In_ PPH_MEMORY_STRING_OPTIONS Options + ) +{ + ULONG minimumLength; + BOOLEAN detectUnicode; + BOOLEAN extendedUnicode; + ULONG memoryTypeMask; + PVOID baseAddress; + MEMORY_BASIC_INFORMATION basicInfo; + PUCHAR buffer; + SIZE_T bufferSize; + PWSTR displayBuffer; + SIZE_T displayBufferCount; + + minimumLength = Options->MinimumLength; + memoryTypeMask = Options->MemoryTypeMask; + detectUnicode = Options->DetectUnicode; + extendedUnicode = Options->ExtendedUnicode; + + if (minimumLength < 4) + return; + + baseAddress = (PVOID)0; + + bufferSize = PAGE_SIZE * 64; + buffer = PhAllocatePage(bufferSize, NULL); + + if (!buffer) + return; + + displayBufferCount = PH_DISPLAY_BUFFER_COUNT; + displayBuffer = PhAllocatePage((displayBufferCount + 1) * sizeof(WCHAR), NULL); + + if (!displayBuffer) + { + PhFreePage(buffer); + return; + } + + while (NT_SUCCESS(NtQueryVirtualMemory( + ProcessHandle, + baseAddress, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + ULONG_PTR offset; + SIZE_T readSize; + + if (Options->Header.Cancel) + break; + if (basicInfo.State != MEM_COMMIT) + goto ContinueLoop; + if ((basicInfo.Type & memoryTypeMask) == 0) + goto ContinueLoop; + if (basicInfo.Protect == PAGE_NOACCESS) + goto ContinueLoop; + if (basicInfo.Protect & PAGE_GUARD) + goto ContinueLoop; + + readSize = basicInfo.RegionSize; + + if (basicInfo.RegionSize > bufferSize) + { + // Don't allocate a huge buffer though. + if (basicInfo.RegionSize <= 16 * 1024 * 1024) // 16 MB + { + PhFreePage(buffer); + bufferSize = basicInfo.RegionSize; + buffer = PhAllocatePage(bufferSize, NULL); + + if (!buffer) + break; + } + else + { + readSize = bufferSize; + } + } + + for (offset = 0; offset < basicInfo.RegionSize; offset += readSize) + { + ULONG_PTR i; + UCHAR byte; // current byte + UCHAR byte1; // previous byte + UCHAR byte2; // byte before previous byte + BOOLEAN printable; + BOOLEAN printable1; + BOOLEAN printable2; + ULONG length; + + if (!NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(baseAddress, offset), + buffer, + readSize, + NULL + ))) + continue; + + byte1 = 0; + byte2 = 0; + printable1 = FALSE; + printable2 = FALSE; + length = 0; + + for (i = 0; i < readSize; i++) + { + byte = buffer[i]; + + // dmex: We don't want to enable extra bits in the PhCharIsPrintable array by default + // or we'll get higher amounts of false positive search results. If the user selects the + // ExtendedUnicode option then we'll use iswprint (GetStringTypeW) which does check + // every available character by default. + if (detectUnicode && extendedUnicode && !iswascii(byte)) + printable = !!iswprint(byte); + else + printable = PhCharIsPrintable[byte]; + + // To find strings Process Hacker uses a state table. + // * byte2 - byte before previous byte + // * byte1 - previous byte + // * byte - current byte + // * length - length of current string run + // + // The states are described below. + // + // [byte2] [byte1] [byte] ... + // [char] means printable, [oth] means non-printable. + // + // 1. [char] [char] [char] ... + // (we're in a non-wide sequence) + // -> append char. + // 2. [char] [char] [oth] ... + // (we reached the end of a non-wide sequence, or we need to start a wide sequence) + // -> if current string is big enough, create result (non-wide). + // otherwise if byte = null, reset to new string with byte1 as first character. + // otherwise if byte != null, reset to new string. + // 3. [char] [oth] [char] ... + // (we're in a wide sequence) + // -> (byte1 should = null) append char. + // 4. [char] [oth] [oth] ... + // (we reached the end of a wide sequence) + // -> (byte1 should = null) if the current string is big enough, create result (wide). + // otherwise, reset to new string. + // 5. [oth] [char] [char] ... + // (we reached the end of a wide sequence, or we need to start a non-wide sequence) + // -> (excluding byte1) if the current string is big enough, create result (wide). + // otherwise, reset to new string with byte1 as first character and byte as + // second character. + // 6. [oth] [char] [oth] ... + // (we're in a wide sequence) + // -> (byte2 and byte should = null) do nothing. + // 7. [oth] [oth] [char] ... + // (we're starting a sequence, but we don't know if it's a wide or non-wide sequence) + // -> append char. + // 8. [oth] [oth] [oth] ... + // (nothing) + // -> do nothing. + + if (printable2 && printable1 && printable) + { + if (length < displayBufferCount) + displayBuffer[length] = byte; + + length++; + } + else if (printable2 && printable1 && !printable) + { + if (length >= minimumLength) + { + goto CreateResult; + } + else if (byte == 0) + { + length = 1; + displayBuffer[0] = byte1; + } + else + { + length = 0; + } + } + else if (printable2 && !printable1 && printable) + { + if (byte1 == 0) + { + if (length < displayBufferCount) + displayBuffer[length] = byte; + + length++; + } + } + else if (printable2 && !printable1 && !printable) + { + if (length >= minimumLength) + { + goto CreateResult; + } + else + { + length = 0; + } + } + else if (!printable2 && printable1 && printable) + { + if (length >= minimumLength + 1) // length - 1 >= minimumLength but avoiding underflow + { + length--; // exclude byte1 + goto CreateResult; + } + else + { + length = 2; + displayBuffer[0] = byte1; + displayBuffer[1] = byte; + } + } + else if (!printable2 && printable1 && !printable) + { + // Nothing + } + else if (!printable2 && !printable1 && printable) + { + if (length < displayBufferCount) + displayBuffer[length] = byte; + + length++; + } + else if (!printable2 && !printable1 && !printable) + { + // Nothing + } + + goto AfterCreateResult; + +CreateResult: + { + PPH_MEMORY_RESULT result; + ULONG lengthInBytes; + ULONG bias; + BOOLEAN isWide; + ULONG displayLength; + + lengthInBytes = length; + bias = 0; + isWide = FALSE; + + if (printable1 == printable) // determine if string was wide (refer to state table, 4 and 5) + { + isWide = TRUE; + lengthInBytes *= 2; + } + + if (printable) // byte1 excluded (refer to state table, 5) + { + bias = 1; + } + + if (!(isWide && !detectUnicode) && (result = PhCreateMemoryResult( + PTR_ADD_OFFSET(baseAddress, i - bias - lengthInBytes), + baseAddress, + lengthInBytes + ))) + { + displayLength = (ULONG)(min(length, displayBufferCount) * sizeof(WCHAR)); + + if (result->Display.Buffer = PhAllocateForMemorySearch(displayLength + sizeof(WCHAR))) + { + memcpy(result->Display.Buffer, displayBuffer, displayLength); + result->Display.Buffer[displayLength / sizeof(WCHAR)] = UNICODE_NULL; + result->Display.Length = displayLength; + } + + Options->Header.Callback( + result, + Options->Header.Context + ); + } + + length = 0; + } +AfterCreateResult: + + byte2 = byte1; + byte1 = byte; + printable2 = printable1; + printable1 = printable; + } + } + +ContinueLoop: + baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); + } + + if (displayBuffer) + PhFreePage(displayBuffer); + + if (buffer) + PhFreePage(buffer); +} + +VOID PhShowMemoryStringDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + NTSTATUS status; + HANDLE processHandle; + MEMORY_STRING_CONTEXT context; + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ProcessItem->ProcessId + ))) + { + PhShowStatus(ParentWindowHandle, L"Unable to open the process", status, 0); + return; + } + + memset(&context, 0, sizeof(MEMORY_STRING_CONTEXT)); + context.ParentWindowHandle = ParentWindowHandle; + context.ProcessId = ProcessItem->ProcessId; + context.ProcessHandle = processHandle; + + if (DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MEMSTRING), + ParentWindowHandle, + PhpMemoryStringDlgProc, + (LPARAM)&context + ) != IDOK) + { + NtClose(processHandle); + return; + } + + context.Results = PhCreateList(1024); + + if (PhpShowMemoryStringProgressDialog(&context)) + { + PPH_SHOW_MEMORY_RESULTS showMemoryResults; + + showMemoryResults = PhAllocate(sizeof(PH_SHOW_MEMORY_RESULTS)); + showMemoryResults->ProcessId = ProcessItem->ProcessId; + showMemoryResults->Results = context.Results; + + PhReferenceObject(context.Results); + ProcessHacker_ShowMemoryResults( + PhMainWndHandle, + showMemoryResults + ); + } + + PhDereferenceObject(context.Results); + NtClose(processHandle); +} + +INT_PTR CALLBACK PhpMemoryStringDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PMEMORY_STRING_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PMEMORY_STRING_CONTEXT)lParam; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetDialogItemText(hwndDlg, IDC_MINIMUMLENGTH, L"10"); + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_DETECTUNICODE), BST_CHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_PRIVATE), BST_CHECKED); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + ULONG64 minimumLength = 10; + + PhStringToInteger64(&PhaGetDlgItemText(hwndDlg, IDC_MINIMUMLENGTH)->sr, 0, &minimumLength); + + if (minimumLength < 4) + { + PhShowError(hwndDlg, L"The minimum length must be at least 4."); + break; + } + + context->MinimumLength = (ULONG)minimumLength; + context->DetectUnicode = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DETECTUNICODE)) == BST_CHECKED; + context->ExtendedUnicode = Button_GetCheck(GetDlgItem(hwndDlg, IDC_EXTENDEDUNICODE)) == BST_CHECKED; + context->Private = Button_GetCheck(GetDlgItem(hwndDlg, IDC_PRIVATE)) == BST_CHECKED; + context->Image = Button_GetCheck(GetDlgItem(hwndDlg, IDC_IMAGE)) == BST_CHECKED; + context->Mapped = Button_GetCheck(GetDlgItem(hwndDlg, IDC_MAPPED)) == BST_CHECKED; + + EndDialog(hwndDlg, IDOK); + } + break; + case IDC_DETECTUNICODE: + { + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_DETECTUNICODE)) == BST_UNCHECKED) + { + Button_SetCheck(GetDlgItem(hwndDlg, IDC_EXTENDEDUNICODE), BST_UNCHECKED); + Button_Enable(GetDlgItem(hwndDlg, IDC_EXTENDEDUNICODE), FALSE); + } + else + { + Button_Enable(GetDlgItem(hwndDlg, IDC_EXTENDEDUNICODE), TRUE); + } + } + break; + } + } + break; + } + + return FALSE; +} + +static BOOL NTAPI PhpMemoryStringResultCallback( + _In_ _Assume_refs_(1) PPH_MEMORY_RESULT Result, + _In_opt_ PVOID Context + ) +{ + PMEMORY_STRING_CONTEXT context = Context; + + PhAddItemList(context->Results, Result); + + return TRUE; +} + +NTSTATUS PhpMemoryStringThreadStart( + _In_ PVOID Parameter + ) +{ + PMEMORY_STRING_CONTEXT context = Parameter; + + context->Options.Header.Callback = PhpMemoryStringResultCallback; + context->Options.Header.Context = context; + context->Options.MinimumLength = context->MinimumLength; + context->Options.DetectUnicode = context->DetectUnicode; + context->Options.ExtendedUnicode = context->ExtendedUnicode; + + if (context->Private) + context->Options.MemoryTypeMask |= MEM_PRIVATE; + if (context->Image) + context->Options.MemoryTypeMask |= MEM_IMAGE; + if (context->Mapped) + context->Options.MemoryTypeMask |= MEM_MAPPED; + + PhSearchMemoryString(context->ProcessHandle, &context->Options); + + SendMessage( + context->WindowHandle, + WM_PH_MEMORY_STATUS_UPDATE, + PH_SEARCH_COMPLETED, + 0 + ); + + return STATUS_SUCCESS; +} + +LRESULT CALLBACK PhpMemoryStringTaskDialogSubclassProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PMEMORY_STRING_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hwndDlg, 0xF))) + return 0; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwndDlg, 0xF); + } + break; + case WM_PH_MEMORY_STATUS_UPDATE: + { + switch (wParam) + { + case PH_SEARCH_COMPLETED: + { + context->EnableCloseDialog = TRUE; + SendMessage(hwndDlg, TDM_CLICK_BUTTON, IDOK, 0); + } + break; + } + } + break; + } + + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); +} + +HRESULT CALLBACK PhpMemoryStringTaskDialogCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PMEMORY_STRING_CONTEXT context = (PMEMORY_STRING_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_CREATED: + { + HICON iconSmall; + HICON iconLarge; + + context->WindowHandle = hwndDlg; + + // Create the Taskdialog icons. + iconSmall = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + iconLarge = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)iconSmall); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)iconLarge); + SendMessage(hwndDlg, TDM_UPDATE_ICON, TDIE_ICON_MAIN, (LPARAM)iconLarge); + + // Set the progress state. + SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + + // Subclass the Taskdialog. + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, 0xF, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PhpMemoryStringTaskDialogSubclassProc); + + // Create the search thread. + PhCreateThread2(PhpMemoryStringThreadStart, context); + } + break; + case TDN_BUTTON_CLICKED: + { + if ((INT)wParam == IDCANCEL) + context->Options.Header.Cancel = TRUE; + + if (!context->EnableCloseDialog) + return S_FALSE; + } + break; + case TDN_TIMER: + { + PPH_STRING numberText; + PPH_STRING progressText; + + numberText = PhFormatUInt64(context->Results->Count, TRUE); + progressText = PhFormatString(L"%s strings found...", numberText->Buffer); + + SendMessage(hwndDlg, TDM_SET_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)progressText->Buffer); + + PhDereferenceObject(progressText); + PhDereferenceObject(numberText); + } + break; + } + + return S_OK; +} + +BOOLEAN PhpShowMemoryStringProgressDialog( + _In_ PMEMORY_STRING_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + INT result = 0; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SHOW_MARQUEE_PROGRESS_BAR | TDF_CALLBACK_TIMER; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.pfCallback = PhpMemoryStringTaskDialogCallback; + config.lpCallbackData = (LONG_PTR)Context; + config.hwndParent = Context->ParentWindowHandle; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Searching memory strings..."; + config.pszContent = L" "; + config.cxWidth = 200; + + if (SUCCEEDED(TaskDialogIndirect(&config, &result, NULL, NULL)) && result == IDOK) + { + return TRUE; + } + + return FALSE; +} diff --git a/ProcessHacker/miniinfo.c b/ProcessHacker/miniinfo.c index 2ff47409664f..c89dc59be6cc 100644 --- a/ProcessHacker/miniinfo.c +++ b/ProcessHacker/miniinfo.c @@ -1,2273 +1,2307 @@ -/* - * Process Hacker - - * mini information window - * - * Copyright (C) 2015-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static HWND PhMipContainerWindow = NULL; -static POINT PhMipSourcePoint; -static LONG PhMipPinCounts[MaxMiniInfoPinType]; -static LONG PhMipMaxPinCounts[] = -{ - 1, // MiniInfoManualPinType - 1, // MiniInfoIconPinType - 1, // MiniInfoActivePinType - 1, // MiniInfoHoverPinType - 1, // MiniInfoChildControlPinType -}; -C_ASSERT(sizeof(PhMipMaxPinCounts) / sizeof(LONG) == MaxMiniInfoPinType); -static LONG PhMipDelayedPinAdjustments[MaxMiniInfoPinType]; -static PPH_MESSAGE_LOOP_FILTER_ENTRY PhMipMessageLoopFilterEntry; -static HWND PhMipLastTrackedWindow; -static HWND PhMipLastNcTrackedWindow; -static ULONG PhMipRefreshAutomatically; -static BOOLEAN PhMipPinned; - -static HWND PhMipWindow = NULL; -static PH_LAYOUT_MANAGER PhMipLayoutManager; -static RECT MinimumSize; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; -static PH_STRINGREF DownArrowPrefix = PH_STRINGREF_INIT(L"\u25be "); - -static PPH_LIST SectionList; -static PH_MINIINFO_PARAMETERS CurrentParameters; -static PPH_MINIINFO_SECTION CurrentSection; - -VOID PhPinMiniInformation( - _In_ PH_MINIINFO_PIN_TYPE PinType, - _In_ LONG PinCount, - _In_opt_ ULONG PinDelayMs, - _In_ ULONG Flags, - _In_opt_ PWSTR SectionName, - _In_opt_ PPOINT SourcePoint - ) -{ - PH_MIP_ADJUST_PIN_RESULT adjustPinResult; - - if (PinDelayMs && PinCount < 0) - { - PhMipDelayedPinAdjustments[PinType] = PinCount; - SetTimer(PhMipContainerWindow, MIP_TIMER_PIN_FIRST + PinType, PinDelayMs, NULL); - return; - } - else - { - PhMipDelayedPinAdjustments[PinType] = 0; - KillTimer(PhMipContainerWindow, MIP_TIMER_PIN_FIRST + PinType); - } - - adjustPinResult = PhMipAdjustPin(PinType, PinCount); - - if (adjustPinResult == ShowAdjustPinResult) - { - PH_RECTANGLE windowRectangle; - ULONG opacity; - - if (SourcePoint) - PhMipSourcePoint = *SourcePoint; - - if (!PhMipContainerWindow) - { - WNDCLASSEX wcex; - - memset(&wcex, 0, sizeof(WNDCLASSEX)); - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = 0; - wcex.lpfnWndProc = PhMipContainerWndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = PhInstanceHandle; - wcex.hIcon = LoadIcon(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wcex.lpszClassName = MIP_CONTAINER_CLASSNAME; - wcex.hIconSm = (HICON)LoadImage(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER), IMAGE_ICON, 16, 16, 0); - RegisterClassEx(&wcex); - - PhMipContainerWindow = CreateWindow( - MIP_CONTAINER_CLASSNAME, - L"Process Hacker", - WS_BORDER | WS_THICKFRAME | WS_POPUP, - 0, - 0, - 400, - 400, - NULL, - NULL, - PhInstanceHandle, - NULL - ); - PhSetWindowExStyle(PhMipContainerWindow, WS_EX_TOOLWINDOW, WS_EX_TOOLWINDOW); - PhMipWindow = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MINIINFO), - PhMipContainerWindow, - PhMipMiniInfoDialogProc - ); - ShowWindow(PhMipWindow, SW_SHOW); - - if (PhGetIntegerSetting(L"MiniInfoWindowPinned")) - PhMipSetPinned(TRUE); - - PhMipRefreshAutomatically = PhGetIntegerSetting(L"MiniInfoWindowRefreshAutomatically"); - - opacity = PhGetIntegerSetting(L"MiniInfoWindowOpacity"); - - if (opacity != 0) - PhSetWindowOpacity(PhMipContainerWindow, opacity); - - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = 210; - MinimumSize.bottom = 60; - MapDialogRect(PhMipWindow, &MinimumSize); - } - - if (!(Flags & PH_MINIINFO_LOAD_POSITION)) - { - PhMipCalculateWindowRectangle(&PhMipSourcePoint, &windowRectangle); - SetWindowPos( - PhMipContainerWindow, - HWND_TOPMOST, - windowRectangle.Left, - windowRectangle.Top, - windowRectangle.Width, - windowRectangle.Height, - SWP_NOACTIVATE - ); - } - else - { - PhLoadWindowPlacementFromSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow); - SetWindowPos(PhMipContainerWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - } - - ShowWindow(PhMipContainerWindow, (Flags & PH_MINIINFO_ACTIVATE_WINDOW) ? SW_SHOW : SW_SHOWNOACTIVATE); - } - else if (adjustPinResult == HideAdjustPinResult) - { - if (PhMipContainerWindow) - ShowWindow(PhMipContainerWindow, SW_HIDE); - } - else - { - if ((Flags & PH_MINIINFO_ACTIVATE_WINDOW) && IsWindowVisible(PhMipContainerWindow)) - SetActiveWindow(PhMipContainerWindow); - } - - if (Flags & PH_MINIINFO_ACTIVATE_WINDOW) - SetForegroundWindow(PhMipContainerWindow); - - if (SectionName && (!PhMipPinned || !(Flags & PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED))) - { - PH_STRINGREF sectionName; - PPH_MINIINFO_SECTION section; - - PhInitializeStringRefLongHint(§ionName, SectionName); - - if (section = PhMipFindSection(§ionName)) - PhMipChangeSection(section); - } -} - -LRESULT CALLBACK PhMipContainerWndProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_SHOWWINDOW: - { - PhMipContainerOnShowWindow(!!wParam, (ULONG)lParam); - } - break; - case WM_ACTIVATE: - { - PhMipContainerOnActivate(LOWORD(wParam), !!HIWORD(wParam)); - } - break; - case WM_SIZE: - { - PhMipContainerOnSize(); - } - break; - case WM_SIZING: - { - PhMipContainerOnSizing((ULONG)wParam, (PRECT)lParam); - } - break; - case WM_EXITSIZEMOVE: - { - PhMipContainerOnExitSizeMove(); - } - break; - case WM_CLOSE: - { - // Hide, don't close. - ShowWindow(hWnd, SW_HIDE); - SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 0); - } - return TRUE; - case WM_ERASEBKGND: - { - if (PhMipContainerOnEraseBkgnd((HDC)wParam)) - return TRUE; - } - break; - case WM_TIMER: - { - PhMipContainerOnTimer((ULONG)wParam); - } - break; - } - - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - -INT_PTR CALLBACK PhMipMiniInfoDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhMipWindow = hwndDlg; - PhMipOnInitDialog(); - } - break; - case WM_SHOWWINDOW: - { - PhMipOnShowWindow(!!wParam, (ULONG)lParam); - } - break; - case WM_COMMAND: - { - PhMipOnCommand(LOWORD(wParam), HIWORD(wParam)); - } - break; - case WM_NOTIFY: - { - LRESULT result; - - if (PhMipOnNotify((NMHDR *)lParam, &result)) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, result); - return TRUE; - } - } - break; - case WM_CTLCOLORBTN: - case WM_CTLCOLORDLG: - case WM_CTLCOLORSTATIC: - { - HBRUSH brush; - - if (PhMipOnCtlColorXxx(uMsg, (HWND)lParam, (HDC)wParam, &brush)) - return (INT_PTR)brush; - } - break; - case WM_DRAWITEM: - { - if (PhMipOnDrawItem(wParam, (DRAWITEMSTRUCT *)lParam)) - return TRUE; - } - break; - } - - if (uMsg >= MIP_MSG_FIRST && uMsg <= MIP_MSG_LAST) - { - PhMipOnUserMessage(uMsg, wParam, lParam); - } - - return FALSE; -} - -VOID PhMipContainerOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ) -{ - ULONG i; - PPH_MINIINFO_SECTION section; - - if (Showing) - { - PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0); - - PhMipMessageLoopFilterEntry = PhRegisterMessageLoopFilter(PhMipMessageLoopFilter, NULL); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - PhMipUpdateHandler, - NULL, - &ProcessesUpdatedRegistration - ); - - PhMipContainerOnSize(); - } - else - { - ULONG i; - - for (i = 0; i < MaxMiniInfoPinType; i++) - PhMipPinCounts[i] = 0; - - Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PIN), BST_UNCHECKED); - PhMipSetPinned(FALSE); - PhSetIntegerSetting(L"MiniInfoWindowPinned", FALSE); - - PhUnregisterCallback( - &PhProcessesUpdatedEvent, - &ProcessesUpdatedRegistration - ); - - if (PhMipMessageLoopFilterEntry) - { - PhUnregisterMessageLoopFilter(PhMipMessageLoopFilterEntry); - PhMipMessageLoopFilterEntry = NULL; - } - - PhSaveWindowPlacementToSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow); - } - - if (SectionList) - { - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - section->Callback(section, MiniInfoShowing, (PVOID)Showing, NULL); - } - } -} - -VOID PhMipContainerOnActivate( - _In_ ULONG Type, - _In_ BOOLEAN Minimized - ) -{ - if (Type == WA_ACTIVE || Type == WA_CLICKACTIVE) - { - PhPinMiniInformation(MiniInfoActivePinType, 1, 0, 0, NULL, NULL); - } - else if (Type == WA_INACTIVE) - { - PhPinMiniInformation(MiniInfoActivePinType, -1, 0, 0, NULL, NULL); - } -} - -VOID PhMipContainerOnSize( - VOID - ) -{ - if (PhMipWindow) - { - InvalidateRect(PhMipContainerWindow, NULL, FALSE); - PhMipLayout(); - } -} - -VOID PhMipContainerOnSizing( - _In_ ULONG Edge, - _In_ PRECT DragRectangle - ) -{ - PhResizingMinimumSize(DragRectangle, Edge, MinimumSize.right, MinimumSize.bottom); -} - -VOID PhMipContainerOnExitSizeMove( - VOID - ) -{ - PhSaveWindowPlacementToSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow); -} - -BOOLEAN PhMipContainerOnEraseBkgnd( - _In_ HDC hdc - ) -{ - return FALSE; -} - -VOID PhMipContainerOnTimer( - _In_ ULONG Id - ) -{ - if (Id >= MIP_TIMER_PIN_FIRST && Id <= MIP_TIMER_PIN_LAST) - { - PH_MINIINFO_PIN_TYPE pinType = Id - MIP_TIMER_PIN_FIRST; - - // PhPinMiniInformation kills the timer for us. - PhPinMiniInformation(pinType, PhMipDelayedPinAdjustments[pinType], 0, 0, NULL, NULL); - } -} - -VOID PhMipOnInitDialog( - VOID - ) -{ - HICON cog; - HICON pin; - - cog = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_COG)); - SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); - - pin = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PIN)); - SET_BUTTON_ICON(PhMipWindow, IDC_PIN, pin); - - PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); - PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_LAYOUT), NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_SECTION), NULL, - PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM | PH_LAYOUT_FORCE_INVALIDATE); - PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_OPTIONS), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_PIN), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - SetWindowSubclass(GetDlgItem(PhMipWindow, IDC_SECTION), PhMipSectionControlHookWndProc, 0, 0); - - Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PIN), !!PhGetIntegerSetting(L"MiniInfoWindowPinned")); -} - -VOID PhMipOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ) -{ - if (SectionList) - return; - - SectionList = PhCreateList(8); - PhMipInitializeParameters(); - - SendMessage(GetDlgItem(PhMipWindow, IDC_SECTION), WM_SETFONT, (WPARAM)CurrentParameters.MediumFont, FALSE); - - PhMipCreateInternalListSection(L"CPU", 0, PhMipCpuListSectionCallback); - PhMipCreateInternalListSection(L"Commit charge", 0, PhMipCommitListSectionCallback); - PhMipCreateInternalListSection(L"Physical memory", 0, PhMipPhysicalListSectionCallback); - PhMipCreateInternalListSection(L"I/O", 0, PhMipIoListSectionCallback); - - if (PhPluginsEnabled) - { - PH_PLUGIN_MINIINFO_POINTERS pointers; - - pointers.WindowHandle = PhMipContainerWindow; - pointers.CreateSection = PhMipCreateSection; - pointers.FindSection = PhMipFindSection; - pointers.CreateListSection = PhMipCreateListSection; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMiniInformationInitializing), &pointers); - } - - PhMipChangeSection(SectionList->Items[0]); -} - -VOID PhMipOnCommand( - _In_ ULONG Id, - _In_ ULONG Code - ) -{ - switch (Id) - { - case IDC_SECTION: - switch (Code) - { - case STN_CLICKED: - PhMipShowSectionMenu(); - break; - } - break; - case IDC_OPTIONS: - PhMipShowOptionsMenu(); - break; - case IDC_PIN: - { - BOOLEAN pinned; - - pinned = Button_GetCheck(GetDlgItem(PhMipWindow, IDC_PIN)) == BST_CHECKED; - PhPinMiniInformation(MiniInfoManualPinType, pinned ? 1 : -1, 0, 0, NULL, NULL); - PhMipSetPinned(pinned); - PhSetIntegerSetting(L"MiniInfoWindowPinned", pinned); - } - break; - } -} - -BOOLEAN PhMipOnNotify( - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ) -{ - return FALSE; -} - -BOOLEAN PhMipOnCtlColorXxx( - _In_ ULONG Message, - _In_ HWND hwnd, - _In_ HDC hdc, - _Out_ HBRUSH *Brush - ) -{ - return FALSE; -} - -BOOLEAN PhMipOnDrawItem( - _In_ ULONG_PTR Id, - _In_ DRAWITEMSTRUCT *DrawItemStruct - ) -{ - return FALSE; -} - -VOID PhMipOnUserMessage( - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ) -{ - switch (Message) - { - case MIP_MSG_UPDATE: - { - ULONG i; - PPH_MINIINFO_SECTION section; - - if (SectionList) - { - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - section->Callback(section, MiniInfoTick, NULL, NULL); - } - } - } - break; - } -} - -BOOLEAN PhMipMessageLoopFilter( - _In_ PMSG Message, - _In_ PVOID Context - ) -{ - if (Message->hwnd == PhMipContainerWindow || IsChild(PhMipContainerWindow, Message->hwnd)) - { - if (Message->message == WM_MOUSEMOVE || Message->message == WM_NCMOUSEMOVE) - { - TRACKMOUSEEVENT trackMouseEvent; - - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); - trackMouseEvent.dwFlags = TME_LEAVE | (Message->message == WM_NCMOUSEMOVE ? TME_NONCLIENT : 0); - trackMouseEvent.hwndTrack = Message->hwnd; - trackMouseEvent.dwHoverTime = 0; - TrackMouseEvent(&trackMouseEvent); - - if (Message->message == WM_MOUSEMOVE) - PhMipLastTrackedWindow = Message->hwnd; - else - PhMipLastNcTrackedWindow = Message->hwnd; - - PhPinMiniInformation(MiniInfoHoverPinType, 1, 0, 0, NULL, NULL); - } - else if (Message->message == WM_MOUSELEAVE && Message->hwnd == PhMipLastTrackedWindow) - { - PhPinMiniInformation(MiniInfoHoverPinType, -1, MIP_UNPIN_HOVER_DELAY, 0, NULL, NULL); - } - else if (Message->message == WM_NCMOUSELEAVE && Message->hwnd == PhMipLastNcTrackedWindow) - { - PhPinMiniInformation(MiniInfoHoverPinType, -1, MIP_UNPIN_HOVER_DELAY, 0, NULL, NULL); - } - else if (Message->message == WM_KEYDOWN) - { - switch (Message->wParam) - { - case VK_F5: - PhMipRefresh(); - break; - case VK_F6: - case VK_PAUSE: - PhMipToggleRefreshAutomatically(); - break; - } - } - } - - return FALSE; -} - -VOID NTAPI PhMipUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (PhMipRefreshAutomatically & MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned)) - PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0); -} - -PH_MIP_ADJUST_PIN_RESULT PhMipAdjustPin( - _In_ PH_MINIINFO_PIN_TYPE PinType, - _In_ LONG PinCount - ) -{ - LONG oldTotalPinCount; - LONG oldPinCount; - LONG newPinCount; - ULONG i; - - oldTotalPinCount = 0; - - for (i = 0; i < MaxMiniInfoPinType; i++) - oldTotalPinCount += PhMipPinCounts[i]; - - oldPinCount = PhMipPinCounts[PinType]; - newPinCount = max(oldPinCount + PinCount, 0); - newPinCount = min(newPinCount, PhMipMaxPinCounts[PinType]); - PhMipPinCounts[PinType] = newPinCount; - - if (oldTotalPinCount == 0 && newPinCount > oldPinCount) - return ShowAdjustPinResult; - else if (oldTotalPinCount > 0 && oldTotalPinCount - oldPinCount + newPinCount == 0) - return HideAdjustPinResult; - else - return NoAdjustPinResult; -} - -VOID PhMipCalculateWindowRectangle( - _In_ PPOINT SourcePoint, - _Out_ PPH_RECTANGLE WindowRectangle - ) -{ - RECT windowRect; - PH_RECTANGLE windowRectangle; - PH_RECTANGLE point; - MONITORINFO monitorInfo = { sizeof(monitorInfo) }; - - PhLoadWindowPlacementFromSetting(NULL, L"MiniInfoWindowSize", PhMipContainerWindow); - GetWindowRect(PhMipContainerWindow, &windowRect); - SendMessage(PhMipContainerWindow, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&windowRect); // Adjust for the minimum size. - windowRectangle = PhRectToRectangle(windowRect); - - point.Left = SourcePoint->x; - point.Top = SourcePoint->y; - point.Width = 0; - point.Height = 0; - PhCenterRectangle(&windowRectangle, &point); - - if (GetMonitorInfo( - MonitorFromPoint(*SourcePoint, MONITOR_DEFAULTTOPRIMARY), - &monitorInfo - )) - { - PH_RECTANGLE bounds; - - if (memcmp(&monitorInfo.rcWork, &monitorInfo.rcMonitor, sizeof(RECT)) == 0) - { - HWND trayWindow; - RECT taskbarRect; - - // The taskbar probably has auto-hide enabled. We need to adjust for that. - if ((trayWindow = FindWindow(L"Shell_TrayWnd", NULL)) && - GetMonitorInfo(MonitorFromWindow(trayWindow, MONITOR_DEFAULTTOPRIMARY), &monitorInfo) && // Just in case - GetWindowRect(trayWindow, &taskbarRect)) - { - LONG monitorMidX = (monitorInfo.rcMonitor.left + monitorInfo.rcMonitor.right) / 2; - LONG monitorMidY = (monitorInfo.rcMonitor.top + monitorInfo.rcMonitor.bottom) / 2; - - if (taskbarRect.right < monitorMidX) - { - // Left - monitorInfo.rcWork.left += taskbarRect.right - taskbarRect.left; - } - else if (taskbarRect.bottom < monitorMidY) - { - // Top - monitorInfo.rcWork.top += taskbarRect.bottom - taskbarRect.top; - } - else if (taskbarRect.left > monitorMidX) - { - // Right - monitorInfo.rcWork.right -= taskbarRect.right - taskbarRect.left; - } - else if (taskbarRect.top > monitorMidY) - { - // Bottom - monitorInfo.rcWork.bottom -= taskbarRect.bottom - taskbarRect.top; - } - } - } - - bounds = PhRectToRectangle(monitorInfo.rcWork); - - PhAdjustRectangleToBounds(&windowRectangle, &bounds); - } - - *WindowRectangle = windowRectangle; -} - -VOID PhMipInitializeParameters( - VOID - ) -{ - LOGFONT logFont; - HDC hdc; - TEXTMETRIC textMetrics; - HFONT originalFont; - - memset(&CurrentParameters, 0, sizeof(PH_MINIINFO_PARAMETERS)); - - CurrentParameters.ContainerWindowHandle = PhMipContainerWindow; - CurrentParameters.MiniInfoWindowHandle = PhMipWindow; - - if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) - { - CurrentParameters.Font = CreateFontIndirect(&logFont); - } - else - { - CurrentParameters.Font = PhApplicationFont; - GetObject(PhApplicationFont, sizeof(LOGFONT), &logFont); - } - - hdc = GetDC(PhMipWindow); - - logFont.lfHeight -= PhMultiplyDivide(2, GetDeviceCaps(hdc, LOGPIXELSY), 72); - CurrentParameters.MediumFont = CreateFontIndirect(&logFont); - - originalFont = SelectObject(hdc, CurrentParameters.Font); - GetTextMetrics(hdc, &textMetrics); - CurrentParameters.FontHeight = textMetrics.tmHeight; - CurrentParameters.FontAverageWidth = textMetrics.tmAveCharWidth; - - SelectObject(hdc, CurrentParameters.MediumFont); - GetTextMetrics(hdc, &textMetrics); - CurrentParameters.MediumFontHeight = textMetrics.tmHeight; - CurrentParameters.MediumFontAverageWidth = textMetrics.tmAveCharWidth; - - CurrentParameters.SetSectionText = PhMipSetSectionText; - - SelectObject(hdc, originalFont); - ReleaseDC(PhMipWindow, hdc); -} - -PPH_MINIINFO_SECTION PhMipCreateSection( - _In_ PPH_MINIINFO_SECTION Template - ) -{ - PPH_MINIINFO_SECTION section; - - section = PhAllocate(sizeof(PH_MINIINFO_SECTION)); - memset(section, 0, sizeof(PH_MINIINFO_SECTION)); - - section->Name = Template->Name; - section->Flags = Template->Flags; - section->Callback = Template->Callback; - section->Context = Template->Context; - section->Parameters = &CurrentParameters; - - PhAddItemList(SectionList, section); - - section->Callback(section, MiniInfoCreate, NULL, NULL); - - return section; -} - -VOID PhMipDestroySection( - _In_ PPH_MINIINFO_SECTION Section - ) -{ - Section->Callback(Section, MiniInfoDestroy, NULL, NULL); - - PhClearReference(&Section->Text); - PhFree(Section); -} - -PPH_MINIINFO_SECTION PhMipFindSection( - _In_ PPH_STRINGREF Name - ) -{ - ULONG i; - PPH_MINIINFO_SECTION section; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (PhEqualStringRef(§ion->Name, Name, TRUE)) - return section; - } - - return NULL; -} - -PPH_MINIINFO_SECTION PhMipCreateInternalSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_SECTION_CALLBACK Callback - ) -{ - PH_MINIINFO_SECTION section; - - memset(§ion, 0, sizeof(PH_MINIINFO_SECTION)); - PhInitializeStringRef(§ion.Name, Name); - section.Flags = Flags; - section.Callback = Callback; - - return PhMipCreateSection(§ion); -} - -VOID PhMipCreateSectionDialog( - _In_ PPH_MINIINFO_SECTION Section - ) -{ - PH_MINIINFO_CREATE_DIALOG createDialog; - - memset(&createDialog, 0, sizeof(PH_MINIINFO_CREATE_DIALOG)); - - if (Section->Callback(Section, MiniInfoCreateDialog, &createDialog, NULL)) - { - if (!createDialog.CustomCreate) - { - Section->DialogHandle = PhCreateDialogFromTemplate( - PhMipWindow, - DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, - createDialog.Instance, - createDialog.Template, - createDialog.DialogProc, - createDialog.Parameter - ); - } - } -} - -VOID PhMipChangeSection( - _In_ PPH_MINIINFO_SECTION NewSection - ) -{ - PPH_MINIINFO_SECTION oldSection; - - if (NewSection == CurrentSection) - return; - - oldSection = CurrentSection; - CurrentSection = NewSection; - - if (oldSection) - { - oldSection->Callback(oldSection, MiniInfoSectionChanging, CurrentSection, NULL); - - if (oldSection->DialogHandle) - ShowWindow(oldSection->DialogHandle, SW_HIDE); - } - - if (!NewSection->DialogHandle) - PhMipCreateSectionDialog(NewSection); - if (NewSection->DialogHandle) - ShowWindow(NewSection->DialogHandle, SW_SHOW); - - PhMipUpdateSectionText(NewSection); - PhMipLayout(); - - NewSection->Callback(NewSection, MiniInfoTick, NULL, NULL); -} - -VOID PhMipSetSectionText( - _In_ struct _PH_MINIINFO_SECTION *Section, - _In_opt_ PPH_STRING Text - ) -{ - PhSwapReference(&Section->Text, Text); - - if (Section == CurrentSection) - PhMipUpdateSectionText(Section); -} - -VOID PhMipUpdateSectionText( - _In_ PPH_MINIINFO_SECTION Section - ) -{ - if (Section->Text) - { - SetDlgItemText(PhMipWindow, IDC_SECTION, - PH_AUTO_T(PH_STRING, PhConcatStringRef2(&DownArrowPrefix, &Section->Text->sr))->Buffer); - } - else - { - SetDlgItemText(PhMipWindow, IDC_SECTION, - PH_AUTO_T(PH_STRING, PhConcatStringRef2(&DownArrowPrefix, &Section->Name))->Buffer); - } -} - -VOID PhMipLayout( - VOID - ) -{ - RECT clientRect; - RECT rect; - - GetClientRect(PhMipContainerWindow, &clientRect); - MoveWindow( - PhMipWindow, - clientRect.left, clientRect.top, - clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, - FALSE - ); - - PhLayoutManagerLayout(&PhMipLayoutManager); - - GetWindowRect(GetDlgItem(PhMipWindow, IDC_LAYOUT), &rect); - MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2); - - if (CurrentSection && CurrentSection->DialogHandle) - { - if (CurrentSection->Flags & PH_MINIINFO_SECTION_NO_UPPER_MARGINS) - { - rect.left = 0; - rect.top = 0; - rect.right = clientRect.right; - } - else - { - LONG leftDistance = rect.left - clientRect.left; - LONG rightDistance = clientRect.right - rect.right; - LONG minDistance; - - if (leftDistance != rightDistance) - { - // HACK: Enforce symmetry. Sometimes these are off by a pixel. - minDistance = min(leftDistance, rightDistance); - rect.left = clientRect.left + minDistance; - rect.right = clientRect.right - minDistance; - } - } - - MoveWindow( - CurrentSection->DialogHandle, - rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - TRUE - ); - } - - GetWindowRect(GetDlgItem(PhMipWindow, IDC_PIN), &rect); - MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2); -} - -VOID PhMipBeginChildControlPin( - VOID - ) -{ - PhPinMiniInformation(MiniInfoChildControlPinType, 1, 0, 0, NULL, NULL); -} - -VOID PhMipEndChildControlPin( - VOID - ) -{ - PhPinMiniInformation(MiniInfoChildControlPinType, -1, MIP_UNPIN_CHILD_CONTROL_DELAY, 0, NULL, NULL); - PostMessage(PhMipWindow, WM_MOUSEMOVE, 0, 0); // Re-evaluate hover pin -} - -VOID PhMipRefresh( - VOID - ) -{ - if (PhMipPinned) - ProcessHacker_Refresh(PhMainWndHandle); - - PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0); -} - -VOID PhMipToggleRefreshAutomatically( - VOID - ) -{ - PhMipRefreshAutomatically ^= MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned); - PhSetIntegerSetting(L"MiniInfoWindowRefreshAutomatically", PhMipRefreshAutomatically); -} - -VOID PhMipSetPinned( - _In_ BOOLEAN Pinned - ) -{ - PhSetWindowStyle(PhMipContainerWindow, WS_DLGFRAME | WS_SYSMENU, Pinned ? (WS_DLGFRAME | WS_SYSMENU) : 0); - SetWindowPos(PhMipContainerWindow, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - PhMipPinned = Pinned; - - PhNfNotifyMiniInfoPinned(Pinned); -} - -VOID PhMipShowSectionMenu( - VOID - ) -{ - PPH_EMENU menu; - ULONG i; - PPH_MINIINFO_SECTION section; - PPH_EMENU_ITEM menuItem; - POINT point; - - PhMipBeginChildControlPin(); - menu = PhCreateEMenu(); - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - menuItem = PhCreateEMenuItem( - (section == CurrentSection ? (PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK) : 0), - 0, - PH_AUTO_T(PH_STRING, PhCreateString2(§ion->Name))->Buffer, - NULL, - section - ); - PhInsertEMenuItem(menu, menuItem, -1); - } - - GetCursorPos(&point); - menuItem = PhShowEMenu(menu, PhMipWindow, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); - - if (menuItem) - { - PhMipChangeSection(menuItem->Context); - } - - PhDestroyEMenu(menu); - PhMipEndChildControlPin(); -} - -VOID PhMipShowOptionsMenu( - VOID - ) -{ - PPH_EMENU menu; - PPH_EMENU_ITEM menuItem; - ULONG id; - RECT rect; - - PhMipBeginChildControlPin(); - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MINIINFO), 0); - - // Opacity - - id = PH_OPACITY_TO_ID(PhGetIntegerSetting(L"MiniInfoWindowOpacity")); - - if (menuItem = PhFindEMenuItem(menu, PH_EMENU_FIND_DESCEND, NULL, id)) - menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; - - // Refresh Automatically - - if (PhMipRefreshAutomatically & MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned)) - PhSetFlagsEMenuItem(menu, ID_MINIINFO_REFRESHAUTOMATICALLY, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - - // Show the menu. - - GetWindowRect(GetDlgItem(PhMipWindow, IDC_OPTIONS), &rect); - menuItem = PhShowEMenu(menu, PhMipWindow, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, rect.left, rect.top); - - if (menuItem) - { - switch (menuItem->Id) - { - case ID_OPACITY_10: - case ID_OPACITY_20: - case ID_OPACITY_30: - case ID_OPACITY_40: - case ID_OPACITY_50: - case ID_OPACITY_60: - case ID_OPACITY_70: - case ID_OPACITY_80: - case ID_OPACITY_90: - case ID_OPACITY_OPAQUE: - { - ULONG opacity; - - opacity = PH_ID_TO_OPACITY(menuItem->Id); - PhSetIntegerSetting(L"MiniInfoWindowOpacity", opacity); - PhSetWindowOpacity(PhMipContainerWindow, opacity); - } - break; - case ID_MINIINFO_REFRESH: - PhMipRefresh(); - break; - case ID_MINIINFO_REFRESHAUTOMATICALLY: - PhMipToggleRefreshAutomatically(); - break; - } - } - - PhDestroyEMenu(menu); - PhMipEndChildControlPin(); -} - -LRESULT CALLBACK PhMipSectionControlHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - switch (uMsg) - { - case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhMipSectionControlHookWndProc, uIdSubclass); - break; - case WM_SETCURSOR: - { - SetCursor(LoadCursor(NULL, IDC_HAND)); - } - return TRUE; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -PPH_MINIINFO_LIST_SECTION PhMipCreateListSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_LIST_SECTION Template - ) -{ - PPH_MINIINFO_LIST_SECTION listSection; - PH_MINIINFO_SECTION section; - - listSection = PhAllocate(sizeof(PH_MINIINFO_LIST_SECTION)); - memset(listSection, 0, sizeof(PH_MINIINFO_LIST_SECTION)); - - listSection->Context = Template->Context; - listSection->Callback = Template->Callback; - - memset(§ion, 0, sizeof(PH_MINIINFO_SECTION)); - PhInitializeStringRef(§ion.Name, Name); - section.Flags = PH_MINIINFO_SECTION_NO_UPPER_MARGINS; - section.Callback = PhMipListSectionCallback; - section.Context = listSection; - listSection->Section = PhMipCreateSection(§ion); - - return listSection; -} - -PPH_MINIINFO_LIST_SECTION PhMipCreateInternalListSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_MINIINFO_LIST_SECTION_CALLBACK Callback - ) -{ - PH_MINIINFO_LIST_SECTION listSection; - - memset(&listSection, 0, sizeof(PH_MINIINFO_LIST_SECTION)); - listSection.Callback = Callback; - - return PhMipCreateListSection(Name, Flags, &listSection); -} - -BOOLEAN PhMipListSectionCallback( - _In_ PPH_MINIINFO_SECTION Section, - _In_ PH_MINIINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - PPH_MINIINFO_LIST_SECTION listSection = Section->Context; - - switch (Message) - { - case MiniInfoCreate: - { - listSection->NodeList = PhCreateList(2); - listSection->Callback(listSection, MiListSectionCreate, NULL, NULL); - } - break; - case MiniInfoDestroy: - { - listSection->Callback(listSection, MiListSectionDestroy, NULL, NULL); - - PhMipClearListSection(listSection); - PhDereferenceObject(listSection->NodeList); - PhFree(listSection); - } - break; - case MiniInfoTick: - if (listSection->SuspendUpdate == 0) - PhMipTickListSection(listSection); - break; - case MiniInfoShowing: - { - listSection->Callback(listSection, MiListSectionShowing, Parameter1, Parameter2); - - if (!Parameter1) // Showing - { - // We don't want to hold process item references while the mini info window - // is hidden. - PhMipClearListSection(listSection); - TreeNew_NodesStructured(listSection->TreeNewHandle); - } - } - break; - case MiniInfoCreateDialog: - { - PPH_MINIINFO_CREATE_DIALOG createDialog = Parameter1; - - createDialog->Instance = PhInstanceHandle; - createDialog->Template = MAKEINTRESOURCE(IDD_MINIINFO_LIST); - createDialog->DialogProc = PhMipListSectionDialogProc; - createDialog->Parameter = listSection; - } - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK PhMipListSectionDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPH_MINIINFO_LIST_SECTION listSection = (PPH_MINIINFO_LIST_SECTION)GetProp(hwndDlg, PhMakeContextAtom()); - - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_LAYOUT_ITEM layoutItem; - - listSection = (PPH_MINIINFO_LIST_SECTION)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)listSection); - - listSection->DialogHandle = hwndDlg; - listSection->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); - - PhInitializeLayoutManager(&listSection->LayoutManager, hwndDlg); - layoutItem = PhAddLayoutItem(&listSection->LayoutManager, listSection->TreeNewHandle, NULL, PH_ANCHOR_ALL); - - // Use negative margins to maximize our use of the window area. - layoutItem->Margin.left = -1; - layoutItem->Margin.top = -1; - layoutItem->Margin.right = -1; - - PhSetControlTheme(listSection->TreeNewHandle, L"explorer"); - TreeNew_SetCallback(listSection->TreeNewHandle, PhMipListSectionTreeNewCallback, listSection); - TreeNew_SetRowHeight(listSection->TreeNewHandle, PhMipCalculateRowHeight()); - PhAddTreeNewColumnEx2(listSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TRUE, L"Process", 1, - PH_ALIGN_LEFT, 0, 0, TN_COLUMN_FLAG_CUSTOMDRAW); - - listSection->Callback(listSection, MiListSectionDialogCreated, hwndDlg, NULL); - PhMipTickListSection(listSection); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&listSection->LayoutManager); - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&listSection->LayoutManager); - TreeNew_AutoSizeColumn(listSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TN_AUTOSIZE_REMAINING_SPACE); - } - break; - } - - return FALSE; -} - -VOID PhMipListSectionSortFunction( - _In_ PPH_LIST List, - _In_opt_ PVOID Context - ) -{ - PPH_MINIINFO_LIST_SECTION listSection = Context; - PH_MINIINFO_LIST_SECTION_SORT_LIST sortList; - - sortList.List = List; - listSection->Callback(listSection, MiListSectionSortProcessList, &sortList, NULL); -} - -VOID PhMipTickListSection( - _In_ PPH_MINIINFO_LIST_SECTION ListSection - ) -{ - ULONG i; - PPH_MIP_GROUP_NODE node; - PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData; - - PhMipClearListSection(ListSection); - - ListSection->ProcessGroupList = PhCreateProcessGroupList( - PhMipListSectionSortFunction, - ListSection, - MIP_MAX_PROCESS_GROUPS, - 0 - ); - - if (!ListSection->ProcessGroupList) - return; - - for (i = 0; i < ListSection->ProcessGroupList->Count; i++) - { - node = PhMipAddGroupNode(ListSection, ListSection->ProcessGroupList->Items[i]); - - if (node->RepresentativeProcessId == ListSection->SelectedRepresentativeProcessId && - node->RepresentativeCreateTime.QuadPart == ListSection->SelectedRepresentativeCreateTime.QuadPart) - { - node->Node.Selected = TRUE; - } - - assignSortData.ProcessGroup = node->ProcessGroup; - assignSortData.SortData = &node->SortData; - ListSection->Callback(ListSection, MiListSectionAssignSortData, &assignSortData, NULL); - } - - TreeNew_NodesStructured(ListSection->TreeNewHandle); - TreeNew_AutoSizeColumn(ListSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TN_AUTOSIZE_REMAINING_SPACE); - - ListSection->Callback(ListSection, MiListSectionTick, NULL, NULL); -} - -VOID PhMipClearListSection( - _In_ PPH_MINIINFO_LIST_SECTION ListSection - ) -{ - ULONG i; - - if (ListSection->ProcessGroupList) - { - PhFreeProcessGroupList(ListSection->ProcessGroupList); - ListSection->ProcessGroupList = NULL; - } - - for (i = 0; i < ListSection->NodeList->Count; i++) - PhMipDestroyGroupNode(ListSection->NodeList->Items[i]); - - PhClearList(ListSection->NodeList); -} - -LONG PhMipCalculateRowHeight( - VOID - ) -{ - LONG iconHeight; - LONG titleAndSubtitleHeight; - - iconHeight = MIP_ICON_PADDING + PhLargeIconSize.Y + MIP_CELL_PADDING; - titleAndSubtitleHeight = - MIP_CELL_PADDING * 2 + CurrentParameters.FontHeight + MIP_INNER_PADDING + CurrentParameters.FontHeight; - - return max(iconHeight, titleAndSubtitleHeight); -} - -PPH_MIP_GROUP_NODE PhMipAddGroupNode( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_PROCESS_GROUP ProcessGroup - ) -{ - // This is an undocumented function exported by user32.dll that - // retrieves the hung window represented by a ghost window. - static HWND (WINAPI *HungWindowFromGhostWindow_I)( - _In_ HWND hWnd - ); - - PPH_MIP_GROUP_NODE node; - - node = PhAllocate(sizeof(PH_MIP_GROUP_NODE)); - memset(node, 0, sizeof(PH_MIP_GROUP_NODE)); - - PhInitializeTreeNewNode(&node->Node); - node->ProcessGroup = ProcessGroup; - node->RepresentativeProcessId = ProcessGroup->Representative->ProcessId; - node->RepresentativeCreateTime = ProcessGroup->Representative->CreateTime; - node->RepresentativeIsHung = ProcessGroup->WindowHandle && IsHungAppWindow(ProcessGroup->WindowHandle); - - if (node->RepresentativeIsHung) - { - if (!HungWindowFromGhostWindow_I) - HungWindowFromGhostWindow_I = PhGetModuleProcAddress(L"user32.dll", "HungWindowFromGhostWindow"); - - // Make sure this is a real hung window, not a ghost window. - if (HungWindowFromGhostWindow_I && HungWindowFromGhostWindow_I(ProcessGroup->WindowHandle)) - node->RepresentativeIsHung = FALSE; - } - - PhAddItemList(ListSection->NodeList, node); - - return node; -} - -VOID PhMipDestroyGroupNode( - _In_ PPH_MIP_GROUP_NODE Node - ) -{ - PhClearReference(&Node->TooltipText); - PhFree(Node); -} - -BOOLEAN PhMipListSectionTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_MINIINFO_LIST_SECTION listSection = Context; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - PH_MINIINFO_LIST_SECTION_SORT_LIST sortList; - - if (!getChildren->Node) - { - getChildren->Children = (PPH_TREENEW_NODE *)listSection->NodeList->Items; - getChildren->NumberOfChildren = listSection->NodeList->Count; - - sortList.List = listSection->NodeList; - listSection->Callback(listSection, MiListSectionSortGroupList, &sortList, NULL); - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewCustomDraw: - { - PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; - PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)customDraw->Node; - PPH_PROCESS_ITEM processItem = node->ProcessGroup->Representative; - HDC hdc = customDraw->Dc; - RECT rect = customDraw->CellRect; - ULONG baseTextFlags = DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE; - HICON icon; - COLORREF originalTextColor; - RECT topRect; - RECT bottomRect; - PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText; - ULONG usageTextTopWidth = 0; - ULONG usageTextBottomWidth = 0; - PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT getTitleText; - - rect.left += MIP_ICON_PADDING; - rect.top += MIP_ICON_PADDING; - rect.right -= MIP_CELL_PADDING; - rect.bottom -= MIP_CELL_PADDING; - - if (processItem->LargeIcon) - icon = processItem->LargeIcon; - else - PhGetStockApplicationIcon(NULL, &icon); - - DrawIconEx(hdc, rect.left, rect.top, icon, PhLargeIconSize.X, PhLargeIconSize.Y, - 0, NULL, DI_NORMAL); - rect.left += (MIP_CELL_PADDING - MIP_ICON_PADDING) + PhLargeIconSize.X + MIP_CELL_PADDING; - rect.top += MIP_CELL_PADDING - MIP_ICON_PADDING; - SelectObject(hdc, CurrentParameters.Font); - - // This color changes depending on whether the node is selected, etc. - originalTextColor = GetTextColor(hdc); - - // Usage text - - topRect = rect; - topRect.bottom = topRect.top + CurrentParameters.FontHeight; - bottomRect = rect; - bottomRect.top = bottomRect.bottom - CurrentParameters.FontHeight; - - getUsageText.ProcessGroup = node->ProcessGroup; - getUsageText.SortData = &node->SortData; - getUsageText.Line1 = NULL; - getUsageText.Line2 = NULL; - getUsageText.Line1Color = originalTextColor; - getUsageText.Line2Color = originalTextColor; - - if (listSection->Callback(listSection, MiListSectionGetUsageText, &getUsageText, NULL)) - { - PH_STRINGREF text; - RECT textRect; - SIZE textSize; - - // Top - text = PhGetStringRef(getUsageText.Line1); - GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / 2, &textSize); - usageTextTopWidth = textSize.cx; - textRect = topRect; - textRect.left = textRect.right - textSize.cx; - SetTextColor(hdc, getUsageText.Line1Color); - DrawText(hdc, text.Buffer, (ULONG)text.Length / 2, &textRect, baseTextFlags | DT_RIGHT); - PhClearReference(&getUsageText.Line1); - - // Bottom - text = PhGetStringRef(getUsageText.Line2); - GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / 2, &textSize); - usageTextBottomWidth = textSize.cx; - textRect = bottomRect; - textRect.left = textRect.right - textSize.cx; - SetTextColor(hdc, getUsageText.Line2Color); - DrawText(hdc, text.Buffer, (ULONG)text.Length / 2, &textRect, baseTextFlags | DT_RIGHT); - PhClearReference(&getUsageText.Line2); - } - - // Title, subtitle - - getTitleText.ProcessGroup = node->ProcessGroup; - getTitleText.SortData = &node->SortData; - - if (!PhIsNullOrEmptyString(processItem->VersionInfo.FileDescription)) - PhSetReference(&getTitleText.Title, processItem->VersionInfo.FileDescription); - else - PhSetReference(&getTitleText.Title, processItem->ProcessName); - - if (node->ProcessGroup->Processes->Count == 1) - { - PhSetReference(&getTitleText.Subtitle, processItem->ProcessName); - } - else - { - getTitleText.Subtitle = PhFormatString( - L"%s (%u processes)", - processItem->ProcessName->Buffer, - node->ProcessGroup->Processes->Count - ); - } - - getTitleText.TitleColor = originalTextColor; - getTitleText.SubtitleColor = GetSysColor(COLOR_GRAYTEXT); - - // Special text for hung windows - if (node->RepresentativeIsHung) - { - static PH_STRINGREF hungPrefix = PH_STRINGREF_INIT(L"(Not responding) "); - - PhMoveReference(&getTitleText.Title, PhConcatStringRef2(&hungPrefix, &getTitleText.Title->sr)); - getTitleText.TitleColor = RGB(0xff, 0x00, 0x00); - } - - listSection->Callback(listSection, MiListSectionGetTitleText, &getTitleText, NULL); - - if (!PhIsNullOrEmptyString(getTitleText.Title)) - { - RECT textRect; - - textRect = topRect; - textRect.right -= usageTextTopWidth + MIP_INNER_PADDING; - SetTextColor(hdc, getTitleText.TitleColor); - DrawText( - hdc, - getTitleText.Title->Buffer, - (ULONG)getTitleText.Title->Length / 2, - &textRect, - baseTextFlags | DT_END_ELLIPSIS - ); - } - - if (!PhIsNullOrEmptyString(getTitleText.Subtitle)) - { - RECT textRect; - - textRect = bottomRect; - textRect.right -= usageTextBottomWidth + MIP_INNER_PADDING; - SetTextColor(hdc, getTitleText.SubtitleColor); - DrawText( - hdc, - getTitleText.Subtitle->Buffer, - (ULONG)getTitleText.Subtitle->Length / 2, - &textRect, - baseTextFlags | DT_END_ELLIPSIS - ); - } - - PhClearReference(&getTitleText.Title); - PhClearReference(&getTitleText.Subtitle); - } - return TRUE; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)getCellTooltip->Node; - ULONG tickCount; - - tickCount = GetTickCount(); - - // This is useless most of the time because the tooltip doesn't display unless the window is active. - // TODO: Find a way to make the tooltip display all the time. - - if (!node->TooltipText) - node->TooltipText = PhMipGetGroupNodeTooltip(listSection, node); - - if (!PhIsNullOrEmptyString(node->TooltipText)) - { - getCellTooltip->Text = node->TooltipText->sr; - getCellTooltip->Unfolding = FALSE; - getCellTooltip->MaximumWidth = -1; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewSelectionChanged: - { - ULONG i; - PPH_MIP_GROUP_NODE node; - - listSection->SelectedRepresentativeProcessId = NULL; - listSection->SelectedRepresentativeCreateTime.QuadPart = 0; - - for (i = 0; i < listSection->NodeList->Count; i++) - { - node = listSection->NodeList->Items[i]; - - if (node->Node.Selected) - { - listSection->SelectedRepresentativeProcessId = node->RepresentativeProcessId; - listSection->SelectedRepresentativeCreateTime = node->RepresentativeCreateTime; - break; - } - } - } - break; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - PPH_MIP_GROUP_NODE node; - - listSection->SuspendUpdate++; - - switch (keyEvent->VirtualKey) - { - case VK_DELETE: - if (node = PhMipGetSelectedGroupNode(listSection)) - PhUiTerminateProcesses(listSection->DialogHandle, &node->ProcessGroup->Representative, 1); - break; - case VK_RETURN: - if (node = PhMipGetSelectedGroupNode(listSection)) - { - if (GetKeyState(VK_CONTROL) >= 0) - { - PhMipHandleListSectionCommand(listSection, node->ProcessGroup, ID_PROCESS_GOTOPROCESS); - } - else - { - if (node->ProcessGroup->Representative->FileName) - PhShellExploreFile(listSection->DialogHandle, node->ProcessGroup->Representative->FileName->Buffer); - } - } - break; - } - - listSection->SuspendUpdate--; - } - return TRUE; - case TreeNewLeftDoubleClick: - { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; - PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)mouseEvent->Node; - - if (node) - { - listSection->SuspendUpdate++; - PhMipHandleListSectionCommand(listSection, node->ProcessGroup, ID_PROCESS_GOTOPROCESS); - listSection->SuspendUpdate--; - } - } - break; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - // Prevent the node list from being updated (otherwise any nodes we're using might be destroyed while we're - // in a modal message loop). - listSection->SuspendUpdate++; - PhMipBeginChildControlPin(); - PhMipShowListSectionContextMenu(listSection, contextMenu); - PhMipEndChildControlPin(); - listSection->SuspendUpdate--; - } - break; - } - - return FALSE; -} - -PPH_STRING PhMipGetGroupNodeTooltip( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_MIP_GROUP_NODE Node - ) -{ - PH_STRING_BUILDER sb; - - PhInitializeStringBuilder(&sb, 100); - - // TODO - - return PhFinalStringBuilderString(&sb); -} - -PPH_MIP_GROUP_NODE PhMipGetSelectedGroupNode( - _In_ PPH_MINIINFO_LIST_SECTION ListSection - ) -{ - ULONG i; - PPH_MIP_GROUP_NODE node; - - for (i = 0; i < ListSection->NodeList->Count; i++) - { - node = ListSection->NodeList->Items[i]; - - if (node->Node.Selected) - return node; - } - - return NULL; -} - -VOID PhMipShowListSectionContextMenu( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_TREENEW_CONTEXT_MENU ContextMenu - ) -{ - PPH_MIP_GROUP_NODE selectedNode; - PPH_EMENU menu; - PPH_EMENU_ITEM item; - PH_MINIINFO_LIST_SECTION_MENU_INFORMATION menuInfo; - PH_PLUGIN_MENU_INFORMATION pluginMenuInfo; - - selectedNode = PhMipGetSelectedGroupNode(ListSection); - - if (!selectedNode) - return; - - menu = PhCreateEMenu(); - // TODO: If there are multiple processes, then create submenus for each process. - PhAddMiniProcessMenuItems(menu, ListSection->SelectedRepresentativeProcessId); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MINIINFO_PROCESS), 0); - PhSetFlagsEMenuItem(menu, ID_PROCESS_GOTOPROCESS, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); - - if (selectedNode->ProcessGroup->Processes->Count != 1) - { - if (item = PhFindEMenuItem(menu, 0, NULL, ID_PROCESS_GOTOPROCESS)) - PhModifyEMenuItem(item, PH_EMENU_MODIFY_TEXT, 0, L"&Go to processes", NULL); - } - - memset(&menuInfo, 0, sizeof(PH_MINIINFO_LIST_SECTION_MENU_INFORMATION)); - menuInfo.ProcessGroup = selectedNode->ProcessGroup; - menuInfo.SortData = &selectedNode->SortData; - menuInfo.ContextMenu = ContextMenu; - ListSection->Callback(ListSection, MiListSectionInitializeContextMenu, &menuInfo, NULL); - - if (PhPluginsEnabled) - { - PhPluginInitializeMenuInfo(&pluginMenuInfo, menu, ListSection->DialogHandle, 0); - pluginMenuInfo.Menu = menu; - pluginMenuInfo.OwnerWindow = PhMipWindow; - pluginMenuInfo.u.MiListSection.SectionName = &ListSection->Section->Name; - pluginMenuInfo.u.MiListSection.ProcessGroup = selectedNode->ProcessGroup; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMiListSectionMenuInitializing), &pluginMenuInfo); - } - - item = PhShowEMenu( - menu, - PhMipWindow, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - ContextMenu->Location.x, - ContextMenu->Location.y - ); - - if (item) - { - BOOLEAN handled = FALSE; - - if (!handled && PhPluginsEnabled) - handled = PhPluginTriggerEMenuItem(&pluginMenuInfo, item); - - if (!handled) - { - menuInfo.SelectedItem = item; - handled = ListSection->Callback(ListSection, MiListSectionHandleContextMenu, &menuInfo, NULL); - } - - if (!handled) - PhHandleMiniProcessMenuItem(item); - - if (!handled) - PhMipHandleListSectionCommand(ListSection, selectedNode->ProcessGroup, item->Id); - } - - PhDestroyEMenu(menu); -} - -VOID PhMipHandleListSectionCommand( - _In_ PPH_MINIINFO_LIST_SECTION ListSection, - _In_ PPH_PROCESS_GROUP ProcessGroup, - _In_ ULONG Id - ) -{ - switch (Id) - { - case ID_PROCESS_GOTOPROCESS: - { - PPH_LIST nodes; - ULONG i; - - nodes = PhCreateList(ProcessGroup->Processes->Count); - - for (i = 0; i < ProcessGroup->Processes->Count; i++) - { - PPH_PROCESS_NODE node; - - if (node = PhFindProcessNode(((PPH_PROCESS_ITEM)ProcessGroup->Processes->Items[i])->ProcessId)) - PhAddItemList(nodes, node); - } - - PhPinMiniInformation(MiniInfoIconPinType, -1, 0, 0, NULL, NULL); - PhPinMiniInformation(MiniInfoActivePinType, -1, 0, 0, NULL, NULL); - PhPinMiniInformation(MiniInfoHoverPinType, -1, 0, 0, NULL, NULL); - - ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); - ProcessHacker_SelectTabPage(PhMainWndHandle, 0); - PhSelectAndEnsureVisibleProcessNodes((PPH_PROCESS_NODE *)nodes->Items, nodes->Count); - PhDereferenceObject(nodes); - } - break; - } -} - -BOOLEAN PhMipCpuListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PhaFormatString(L"CPU %.2f%%", (PhCpuUserUsage + PhCpuKernelUsage) * 100)); - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), PhMipCpuListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - FLOAT cpuUsage = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - cpuUsage += ((PPH_PROCESS_ITEM)processes->Items[i])->CpuUsage; - - *(PFLOAT)assignSortData->SortData->UserData = cpuUsage; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipCpuListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - FLOAT cpuUsage = *(PFLOAT)getUsageText->SortData->UserData * 100; - PPH_STRING cpuUsageText; - - if (cpuUsage >= 0.01) - cpuUsageText = PhFormatString(L"%.2f%%", cpuUsage); - else if (cpuUsage != 0) - cpuUsageText = PhCreateString(L"< 0.01%"); - else - cpuUsageText = NULL; - - PhMoveReference(&getUsageText->Line1, cpuUsageText); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl PhMipCpuListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - int result; - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - - result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); - - if (result == 0) - result = uint64cmp(node2->ProcessItem->UserTime.QuadPart, node1->ProcessItem->UserTime.QuadPart); - - return result; -} - -int __cdecl PhMipCpuListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return singlecmp(*(PFLOAT)data2->UserData, *(PFLOAT)data1->UserData); -} - -BOOLEAN PhMipCommitListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - { - PH_FORMAT format[5]; - DOUBLE commitFraction = (DOUBLE)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit; - - PhInitFormatS(&format[0], L"Commit "); - PhInitFormatSize(&format[1], UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE)); - PhInitFormatS(&format[2], L" ("); - PhInitFormatF(&format[3], commitFraction * 100, 2); - PhInitFormatS(&format[4], L"%)"); - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PH_AUTO(PhFormat(format, 5, 96))); - } - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), PhMipCommitListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - ULONG64 privateBytes = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - privateBytes += ((PPH_PROCESS_ITEM)processes->Items[i])->VmCounters.PagefileUsage; - - *(PULONG64)assignSortData->SortData->UserData = privateBytes; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipCommitListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - ULONG64 privateBytes = *(PULONG64)getUsageText->SortData->UserData; - - PhMoveReference(&getUsageText->Line1, PhFormatSize(privateBytes, -1)); - PhMoveReference(&getUsageText->Line2, PhCreateString(L"Private bytes")); - getUsageText->Line2Color = GetSysColor(COLOR_GRAYTEXT); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl PhMipCommitListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - - return uintptrcmp(node2->ProcessItem->VmCounters.PagefileUsage, node1->ProcessItem->VmCounters.PagefileUsage); -} - -int __cdecl PhMipCommitListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return uint64cmp(*(PULONG64)data2->UserData, *(PULONG64)data1->UserData); -} - -BOOLEAN PhMipPhysicalListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - { - PH_FORMAT format[5]; - ULONG physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages; - FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages; - - PhInitFormatS(&format[0], L"Physical "); - PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE)); - PhInitFormatS(&format[2], L" ("); - PhInitFormatF(&format[3], physicalFraction * 100, 2); - PhInitFormatS(&format[4], L"%)"); - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PH_AUTO(PhFormat(format, 5, 96))); - } - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), PhMipPhysicalListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - ULONG64 workingSet = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - workingSet += ((PPH_PROCESS_ITEM)processes->Items[i])->VmCounters.WorkingSetSize; - - *(PULONG64)assignSortData->SortData->UserData = workingSet; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipPhysicalListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - ULONG64 privateBytes = *(PULONG64)getUsageText->SortData->UserData; - - PhMoveReference(&getUsageText->Line1, PhFormatSize(privateBytes, -1)); - PhMoveReference(&getUsageText->Line2, PhCreateString(L"Working set")); - getUsageText->Line2Color = GetSysColor(COLOR_GRAYTEXT); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl PhMipPhysicalListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - - return uintptrcmp(node2->ProcessItem->VmCounters.WorkingSetSize, node1->ProcessItem->VmCounters.WorkingSetSize); -} - -int __cdecl PhMipPhysicalListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return uint64cmp(*(PULONG64)data2->UserData, *(PULONG64)data1->UserData); -} - -BOOLEAN PhMipIoListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - { - PH_FORMAT format[6]; - - PhInitFormatS(&format[0], L"I/O R: "); - PhInitFormatSize(&format[1], PhIoReadDelta.Delta); - format[1].Type |= FormatUsePrecision; - format[1].Precision = 0; - PhInitFormatS(&format[2], L" W: "); - PhInitFormatSize(&format[3], PhIoWriteDelta.Delta); - format[3].Type |= FormatUsePrecision; - format[3].Precision = 0; - PhInitFormatS(&format[4], L" O: "); - PhInitFormatSize(&format[5], PhIoOtherDelta.Delta); - format[5].Type |= FormatUsePrecision; - format[5].Precision = 0; - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PH_AUTO(PhFormat(format, 6, 80))); - } - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), PhMipIoListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - ULONG64 ioReadOtherDelta = 0; - ULONG64 ioWriteDelta = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - { - PPH_PROCESS_ITEM processItem = processes->Items[i]; - ioReadOtherDelta += processItem->IoReadDelta.Delta; - ioWriteDelta += processItem->IoWriteDelta.Delta; - ioReadOtherDelta += processItem->IoOtherDelta.Delta; - } - - assignSortData->SortData->UserData[0] = ioReadOtherDelta; - assignSortData->SortData->UserData[1] = ioWriteDelta; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipIoListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - ULONG64 ioReadOtherDelta = getUsageText->SortData->UserData[0]; - ULONG64 ioWriteDelta = getUsageText->SortData->UserData[1]; - PH_FORMAT format[1]; - - PhInitFormatSize(&format[0], ioReadOtherDelta + ioWriteDelta); - PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16)); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl PhMipIoListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - int result; - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - ULONG64 delta1 = node1->ProcessItem->IoReadDelta.Delta + node1->ProcessItem->IoWriteDelta.Delta + node1->ProcessItem->IoOtherDelta.Delta; - ULONG64 delta2 = node2->ProcessItem->IoReadDelta.Delta + node2->ProcessItem->IoWriteDelta.Delta + node2->ProcessItem->IoOtherDelta.Delta; - ULONG64 value1 = node1->ProcessItem->IoReadDelta.Value + node1->ProcessItem->IoWriteDelta.Value + node1->ProcessItem->IoOtherDelta.Value; - ULONG64 value2 = node2->ProcessItem->IoReadDelta.Value + node2->ProcessItem->IoWriteDelta.Value + node2->ProcessItem->IoOtherDelta.Value; - - result = uint64cmp(delta2, delta1); - - if (result == 0) - result = uint64cmp(value2, value1); - - return result; -} - -int __cdecl PhMipIoListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]); -} +/* + * Process Hacker - + * mini information window + * + * Copyright (C) 2015-2016 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static HWND PhMipContainerWindow = NULL; +static POINT PhMipSourcePoint; +static LONG PhMipPinCounts[MaxMiniInfoPinType]; +static LONG PhMipMaxPinCounts[] = +{ + 1, // MiniInfoManualPinType + 1, // MiniInfoIconPinType + 1, // MiniInfoActivePinType + 1, // MiniInfoHoverPinType + 1, // MiniInfoChildControlPinType +}; +C_ASSERT(sizeof(PhMipMaxPinCounts) / sizeof(LONG) == MaxMiniInfoPinType); +static LONG PhMipDelayedPinAdjustments[MaxMiniInfoPinType]; +static PPH_MESSAGE_LOOP_FILTER_ENTRY PhMipMessageLoopFilterEntry; +static HWND PhMipLastTrackedWindow; +static HWND PhMipLastNcTrackedWindow; +static ULONG PhMipRefreshAutomatically; +static BOOLEAN PhMipPinned; + +static HWND PhMipWindow = NULL; +static PH_LAYOUT_MANAGER PhMipLayoutManager; +static RECT MinimumSize; +static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; +static PH_STRINGREF DownArrowPrefix = PH_STRINGREF_INIT(L"\u25be "); + +static PPH_LIST SectionList; +static PH_MINIINFO_PARAMETERS CurrentParameters; +static PPH_MINIINFO_SECTION CurrentSection; + +VOID PhPinMiniInformation( + _In_ PH_MINIINFO_PIN_TYPE PinType, + _In_ LONG PinCount, + _In_opt_ ULONG PinDelayMs, + _In_ ULONG Flags, + _In_opt_ PWSTR SectionName, + _In_opt_ PPOINT SourcePoint + ) +{ + PH_MIP_ADJUST_PIN_RESULT adjustPinResult; + + if (PinDelayMs && PinCount < 0) + { + PhMipDelayedPinAdjustments[PinType] = PinCount; + SetTimer(PhMipContainerWindow, MIP_TIMER_PIN_FIRST + PinType, PinDelayMs, NULL); + return; + } + else + { + PhMipDelayedPinAdjustments[PinType] = 0; + KillTimer(PhMipContainerWindow, MIP_TIMER_PIN_FIRST + PinType); + } + + adjustPinResult = PhMipAdjustPin(PinType, PinCount); + + if (adjustPinResult == ShowAdjustPinResult) + { + PH_RECTANGLE windowRectangle; + ULONG opacity; + + if (SourcePoint) + PhMipSourcePoint = *SourcePoint; + + if (!PhMipContainerWindow) + { + WNDCLASSEX wcex; + RTL_ATOM windowAtom; + PPH_STRING className; + + memset(&wcex, 0, sizeof(WNDCLASSEX)); + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = 0; + wcex.lpfnWndProc = PhMipContainerWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = PhInstanceHandle; + wcex.hIcon = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + className = PhaGetStringSetting(L"MiniInfoWindowClassName"); + wcex.lpszClassName = PhGetStringOrDefault(className, L"MiniInfoWindowClassName"); + wcex.hIconSm = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER)); + windowAtom = RegisterClassEx(&wcex); + + PhMipContainerWindow = CreateWindow( + MAKEINTATOM(windowAtom), + PhGetIntegerSetting(L"EnableWindowText") ? PhApplicationName : NULL, + WS_BORDER | WS_THICKFRAME | WS_POPUP, + 0, + 0, + 400, + 400, + NULL, + NULL, + PhInstanceHandle, + NULL + ); + PhSetWindowExStyle(PhMipContainerWindow, WS_EX_TOOLWINDOW, WS_EX_TOOLWINDOW); + PhMipWindow = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MINIINFO), + PhMipContainerWindow, + PhMipMiniInfoDialogProc + ); + ShowWindow(PhMipWindow, SW_SHOW); + + if (PhGetIntegerSetting(L"MiniInfoWindowPinned")) + PhMipSetPinned(TRUE); + + PhMipRefreshAutomatically = PhGetIntegerSetting(L"MiniInfoWindowRefreshAutomatically"); + + opacity = PhGetIntegerSetting(L"MiniInfoWindowOpacity"); + + if (opacity != 0) + PhSetWindowOpacity(PhMipContainerWindow, opacity); + + MinimumSize.left = 0; + MinimumSize.top = 0; + MinimumSize.right = 210; + MinimumSize.bottom = 60; + MapDialogRect(PhMipWindow, &MinimumSize); + } + + if (!(Flags & PH_MINIINFO_LOAD_POSITION)) + { + PhMipCalculateWindowRectangle(&PhMipSourcePoint, &windowRectangle); + SetWindowPos( + PhMipContainerWindow, + HWND_TOPMOST, + windowRectangle.Left, + windowRectangle.Top, + windowRectangle.Width, + windowRectangle.Height, + SWP_NOACTIVATE + ); + } + else + { + PhLoadWindowPlacementFromSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow); + SetWindowPos(PhMipContainerWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + } + + ShowWindow(PhMipContainerWindow, (Flags & PH_MINIINFO_ACTIVATE_WINDOW) ? SW_SHOW : SW_SHOWNOACTIVATE); + } + else if (adjustPinResult == HideAdjustPinResult) + { + if (PhMipContainerWindow) + ShowWindow(PhMipContainerWindow, SW_HIDE); + } + else + { + if ((Flags & PH_MINIINFO_ACTIVATE_WINDOW) && IsWindowVisible(PhMipContainerWindow)) + SetActiveWindow(PhMipContainerWindow); + } + + if (Flags & PH_MINIINFO_ACTIVATE_WINDOW) + SetForegroundWindow(PhMipContainerWindow); + + if (SectionName && (!PhMipPinned || !(Flags & PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED))) + { + PH_STRINGREF sectionName; + PPH_MINIINFO_SECTION section; + + PhInitializeStringRefLongHint(§ionName, SectionName); + + if (section = PhMipFindSection(§ionName)) + PhMipChangeSection(section); + } +} + +LRESULT CALLBACK PhMipContainerWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_SHOWWINDOW: + { + PhMipContainerOnShowWindow(!!wParam, (ULONG)lParam); + } + break; + case WM_ACTIVATE: + { + PhMipContainerOnActivate(GET_WM_COMMAND_ID(wParam, lParam), !!HIWORD(wParam)); + } + break; + case WM_SIZE: + { + PhMipContainerOnSize(); + } + break; + case WM_SIZING: + { + PhMipContainerOnSizing((ULONG)wParam, (PRECT)lParam); + } + break; + case WM_EXITSIZEMOVE: + { + PhMipContainerOnExitSizeMove(); + } + break; + case WM_CLOSE: + { + // Hide, don't close. + ShowWindow(hWnd, SW_HIDE); + SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 0); + } + return TRUE; + case WM_ERASEBKGND: + { + if (PhMipContainerOnEraseBkgnd((HDC)wParam)) + return TRUE; + } + break; + case WM_TIMER: + { + PhMipContainerOnTimer((ULONG)wParam); + } + break; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +INT_PTR CALLBACK PhMipMiniInfoDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PhMipWindow = hwndDlg; + PhMipOnInitDialog(); + } + break; + case WM_SHOWWINDOW: + { + PhMipOnShowWindow(!!wParam, (ULONG)lParam); + } + break; + case WM_COMMAND: + { + PhMipOnCommand(GET_WM_COMMAND_ID(wParam, lParam), HIWORD(wParam)); + } + break; + case WM_NOTIFY: + { + LRESULT result; + + if (PhMipOnNotify((NMHDR *)lParam, &result)) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, result); + return TRUE; + } + } + break; + case WM_CTLCOLORBTN: + case WM_CTLCOLORDLG: + case WM_CTLCOLORSTATIC: + { + HBRUSH brush; + + if (PhMipOnCtlColorXxx(uMsg, (HWND)lParam, (HDC)wParam, &brush)) + return (INT_PTR)brush; + } + break; + case WM_DRAWITEM: + { + if (PhMipOnDrawItem(wParam, (DRAWITEMSTRUCT *)lParam)) + return TRUE; + } + break; + } + + if (uMsg >= MIP_MSG_FIRST && uMsg <= MIP_MSG_LAST) + { + PhMipOnUserMessage(uMsg, wParam, lParam); + } + + return FALSE; +} + +VOID PhMipContainerOnShowWindow( + _In_ BOOLEAN Showing, + _In_ ULONG State + ) +{ + ULONG i; + PPH_MINIINFO_SECTION section; + + if (Showing) + { + PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0); + + PhMipMessageLoopFilterEntry = PhRegisterMessageLoopFilter(PhMipMessageLoopFilter, NULL); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + PhMipUpdateHandler, + NULL, + &ProcessesUpdatedRegistration + ); + + PhMipContainerOnSize(); + } + else + { + ULONG i; + + for (i = 0; i < MaxMiniInfoPinType; i++) + PhMipPinCounts[i] = 0; + + Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PINWINDOW), BST_UNCHECKED); + PhMipSetPinned(FALSE); + PhSetIntegerSetting(L"MiniInfoWindowPinned", FALSE); + + PhUnregisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + &ProcessesUpdatedRegistration + ); + + if (PhMipMessageLoopFilterEntry) + { + PhUnregisterMessageLoopFilter(PhMipMessageLoopFilterEntry); + PhMipMessageLoopFilterEntry = NULL; + } + + PhSaveWindowPlacementToSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow); + } + + if (SectionList) + { + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + section->Callback(section, MiniInfoShowing, (PVOID)Showing, NULL); + } + } +} + +VOID PhMipContainerOnActivate( + _In_ ULONG Type, + _In_ BOOLEAN Minimized + ) +{ + if (Type == WA_ACTIVE || Type == WA_CLICKACTIVE) + { + PhPinMiniInformation(MiniInfoActivePinType, 1, 0, 0, NULL, NULL); + } + else if (Type == WA_INACTIVE) + { + PhPinMiniInformation(MiniInfoActivePinType, -1, 0, 0, NULL, NULL); + } +} + +VOID PhMipContainerOnSize( + VOID + ) +{ + if (PhMipWindow) + { + InvalidateRect(PhMipContainerWindow, NULL, FALSE); + PhMipLayout(); + } +} + +VOID PhMipContainerOnSizing( + _In_ ULONG Edge, + _In_ PRECT DragRectangle + ) +{ + PhResizingMinimumSize(DragRectangle, Edge, MinimumSize.right, MinimumSize.bottom); +} + +VOID PhMipContainerOnExitSizeMove( + VOID + ) +{ + PhSaveWindowPlacementToSetting(L"MiniInfoWindowPosition", L"MiniInfoWindowSize", PhMipContainerWindow); +} + +BOOLEAN PhMipContainerOnEraseBkgnd( + _In_ HDC hdc + ) +{ + return FALSE; +} + +VOID PhMipContainerOnTimer( + _In_ ULONG Id + ) +{ + if (Id >= MIP_TIMER_PIN_FIRST && Id <= MIP_TIMER_PIN_LAST) + { + PH_MINIINFO_PIN_TYPE pinType = Id - MIP_TIMER_PIN_FIRST; + + // PhPinMiniInformation kills the timer for us. + PhPinMiniInformation(pinType, PhMipDelayedPinAdjustments[pinType], 0, 0, NULL, NULL); + } +} + +VOID PhMipOnInitDialog( + VOID + ) +{ + HICON cog; + HICON pin; + WNDPROC oldWndProc; + + cog = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); + SET_BUTTON_ICON(PhMipWindow, IDC_OPTIONS, cog); + + pin = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PIN)); + SET_BUTTON_ICON(PhMipWindow, IDC_PINWINDOW, pin); + + PhInitializeLayoutManager(&PhMipLayoutManager, PhMipWindow); + PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_LAYOUT), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_SECTION), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM | PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_OPTIONS), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&PhMipLayoutManager, GetDlgItem(PhMipWindow, IDC_PINWINDOW), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + Button_SetCheck(GetDlgItem(PhMipWindow, IDC_PINWINDOW), !!PhGetIntegerSetting(L"MiniInfoWindowPinned")); + + // Subclass the window procedure. + oldWndProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC); + PhSetWindowContext(GetDlgItem(PhMipWindow, IDC_SECTION), 0xF, oldWndProc); + SetWindowLongPtr(GetDlgItem(PhMipWindow, IDC_SECTION), GWLP_WNDPROC, (LONG_PTR)PhMipSectionControlHookWndProc); + + PhInitializeWindowTheme(PhMipWindow, PhEnableThemeSupport); +} + +VOID PhMipOnShowWindow( + _In_ BOOLEAN Showing, + _In_ ULONG State + ) +{ + if (SectionList) + return; + + SectionList = PhCreateList(8); + PhMipInitializeParameters(); + + SetWindowFont(GetDlgItem(PhMipWindow, IDC_SECTION), CurrentParameters.MediumFont, FALSE); + + PhMipCreateInternalListSection(L"CPU", 0, PhMipCpuListSectionCallback); + PhMipCreateInternalListSection(L"Commit charge", 0, PhMipCommitListSectionCallback); + PhMipCreateInternalListSection(L"Physical memory", 0, PhMipPhysicalListSectionCallback); + PhMipCreateInternalListSection(L"I/O", 0, PhMipIoListSectionCallback); + + if (PhPluginsEnabled) + { + PH_PLUGIN_MINIINFO_POINTERS pointers; + + pointers.WindowHandle = PhMipContainerWindow; + pointers.CreateSection = PhMipCreateSection; + pointers.FindSection = PhMipFindSection; + pointers.CreateListSection = PhMipCreateListSection; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMiniInformationInitializing), &pointers); + } + + PhMipChangeSection(SectionList->Items[0]); +} + +VOID PhMipOnCommand( + _In_ ULONG Id, + _In_ ULONG Code + ) +{ + switch (Id) + { + case IDC_SECTION: + switch (Code) + { + case STN_CLICKED: + PhMipShowSectionMenu(); + break; + } + break; + case IDC_OPTIONS: + PhMipShowOptionsMenu(); + break; + case IDC_PINWINDOW: + { + BOOLEAN pinned; + + pinned = Button_GetCheck(GetDlgItem(PhMipWindow, IDC_PINWINDOW)) == BST_CHECKED; + PhPinMiniInformation(MiniInfoManualPinType, pinned ? 1 : -1, 0, 0, NULL, NULL); + PhMipSetPinned(pinned); + PhSetIntegerSetting(L"MiniInfoWindowPinned", pinned); + } + break; + } +} + +BOOLEAN PhMipOnNotify( + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ) +{ + return FALSE; +} + +BOOLEAN PhMipOnCtlColorXxx( + _In_ ULONG Message, + _In_ HWND hwnd, + _In_ HDC hdc, + _Out_ HBRUSH *Brush + ) +{ + return FALSE; +} + +BOOLEAN PhMipOnDrawItem( + _In_ ULONG_PTR Id, + _In_ DRAWITEMSTRUCT *DrawItemStruct + ) +{ + return FALSE; +} + +VOID PhMipOnUserMessage( + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ) +{ + switch (Message) + { + case MIP_MSG_UPDATE: + { + ULONG i; + PPH_MINIINFO_SECTION section; + + if (SectionList) + { + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + section->Callback(section, MiniInfoTick, NULL, NULL); + } + } + } + break; + } +} + +BOOLEAN PhMipMessageLoopFilter( + _In_ PMSG Message, + _In_ PVOID Context + ) +{ + if (Message->hwnd == PhMipContainerWindow || IsChild(PhMipContainerWindow, Message->hwnd)) + { + if (Message->message == WM_MOUSEMOVE || Message->message == WM_NCMOUSEMOVE) + { + TRACKMOUSEEVENT trackMouseEvent; + + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE | (Message->message == WM_NCMOUSEMOVE ? TME_NONCLIENT : 0); + trackMouseEvent.hwndTrack = Message->hwnd; + trackMouseEvent.dwHoverTime = 0; + TrackMouseEvent(&trackMouseEvent); + + if (Message->message == WM_MOUSEMOVE) + PhMipLastTrackedWindow = Message->hwnd; + else + PhMipLastNcTrackedWindow = Message->hwnd; + + PhPinMiniInformation(MiniInfoHoverPinType, 1, 0, 0, NULL, NULL); + } + else if (Message->message == WM_MOUSELEAVE && Message->hwnd == PhMipLastTrackedWindow) + { + PhPinMiniInformation(MiniInfoHoverPinType, -1, MIP_UNPIN_HOVER_DELAY, 0, NULL, NULL); + } + else if (Message->message == WM_NCMOUSELEAVE && Message->hwnd == PhMipLastNcTrackedWindow) + { + PhPinMiniInformation(MiniInfoHoverPinType, -1, MIP_UNPIN_HOVER_DELAY, 0, NULL, NULL); + } + else if (Message->message == WM_KEYDOWN) + { + switch (Message->wParam) + { + case VK_F5: + PhMipRefresh(); + break; + case VK_F6: + case VK_PAUSE: + PhMipToggleRefreshAutomatically(); + break; + } + } + } + + return FALSE; +} + +VOID NTAPI PhMipUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (PhMipRefreshAutomatically & MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned)) + PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0); +} + +PH_MIP_ADJUST_PIN_RESULT PhMipAdjustPin( + _In_ PH_MINIINFO_PIN_TYPE PinType, + _In_ LONG PinCount + ) +{ + LONG oldTotalPinCount; + LONG oldPinCount; + LONG newPinCount; + ULONG i; + + oldTotalPinCount = 0; + + for (i = 0; i < MaxMiniInfoPinType; i++) + oldTotalPinCount += PhMipPinCounts[i]; + + oldPinCount = PhMipPinCounts[PinType]; + newPinCount = max(oldPinCount + PinCount, 0); + newPinCount = min(newPinCount, PhMipMaxPinCounts[PinType]); + PhMipPinCounts[PinType] = newPinCount; + + if (oldTotalPinCount == 0 && newPinCount > oldPinCount) + return ShowAdjustPinResult; + else if (oldTotalPinCount > 0 && oldTotalPinCount - oldPinCount + newPinCount == 0) + return HideAdjustPinResult; + else + return NoAdjustPinResult; +} + +VOID PhMipCalculateWindowRectangle( + _In_ PPOINT SourcePoint, + _Out_ PPH_RECTANGLE WindowRectangle + ) +{ + RECT windowRect; + PH_RECTANGLE windowRectangle; + PH_RECTANGLE point; + MONITORINFO monitorInfo = { sizeof(monitorInfo) }; + + PhLoadWindowPlacementFromSetting(NULL, L"MiniInfoWindowSize", PhMipContainerWindow); + GetWindowRect(PhMipContainerWindow, &windowRect); + SendMessage(PhMipContainerWindow, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&windowRect); // Adjust for the minimum size. + windowRectangle = PhRectToRectangle(windowRect); + + point.Left = SourcePoint->x; + point.Top = SourcePoint->y; + point.Width = 0; + point.Height = 0; + PhCenterRectangle(&windowRectangle, &point); + + if (GetMonitorInfo( + MonitorFromPoint(*SourcePoint, MONITOR_DEFAULTTOPRIMARY), + &monitorInfo + )) + { + PH_RECTANGLE bounds; + + if (RtlEqualMemory(&monitorInfo.rcWork, &monitorInfo.rcMonitor, sizeof(RECT))) + { + APPBARDATA taskbarRect = { sizeof(APPBARDATA) }; + + // dmex: FindWindow + Shell_TrayWnd causes a lot of FPs by security software (malware uses this string to inject code into Explorer)... + // TODO: This comment block should be removed if the SHAppBarMessage function is more reliable. + //HWND trayWindow; + //RECT taskbarRect; + //if ((trayWindow = FindWindow(L"Shell_TrayWnd", NULL)) && + // GetMonitorInfo(MonitorFromWindow(trayWindow, MONITOR_DEFAULTTOPRIMARY), &monitorInfo) && // Just in case + // GetWindowRect(trayWindow, &taskbarRect)) + + // The taskbar probably has auto-hide enabled. We need to adjust for that. + if (SHAppBarMessage(ABM_GETTASKBARPOS, &taskbarRect)) + { + LONG monitorMidX = (monitorInfo.rcMonitor.left + monitorInfo.rcMonitor.right) / 2; + LONG monitorMidY = (monitorInfo.rcMonitor.top + monitorInfo.rcMonitor.bottom) / 2; + + if (taskbarRect.rc.right < monitorMidX) + { + // Left + monitorInfo.rcWork.left += taskbarRect.rc.right - taskbarRect.rc.left; + } + else if (taskbarRect.rc.bottom < monitorMidY) + { + // Top + monitorInfo.rcWork.top += taskbarRect.rc.bottom - taskbarRect.rc.top; + } + else if (taskbarRect.rc.left > monitorMidX) + { + // Right + monitorInfo.rcWork.right -= taskbarRect.rc.right - taskbarRect.rc.left; + } + else if (taskbarRect.rc.top > monitorMidY) + { + // Bottom + monitorInfo.rcWork.bottom -= taskbarRect.rc.bottom - taskbarRect.rc.top; + } + } + } + + bounds = PhRectToRectangle(monitorInfo.rcWork); + + PhAdjustRectangleToBounds(&windowRectangle, &bounds); + } + + *WindowRectangle = windowRectangle; +} + +VOID PhMipInitializeParameters( + VOID + ) +{ + LOGFONT logFont; + HDC hdc; + TEXTMETRIC textMetrics; + HFONT originalFont; + + memset(&CurrentParameters, 0, sizeof(PH_MINIINFO_PARAMETERS)); + + CurrentParameters.ContainerWindowHandle = PhMipContainerWindow; + CurrentParameters.MiniInfoWindowHandle = PhMipWindow; + + if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) + { + CurrentParameters.Font = CreateFontIndirect(&logFont); + } + else + { + CurrentParameters.Font = PhApplicationFont; + GetObject(PhApplicationFont, sizeof(LOGFONT), &logFont); + } + + hdc = GetDC(PhMipWindow); + + logFont.lfHeight -= PhMultiplyDivide(2, PhGlobalDpi, 72); + CurrentParameters.MediumFont = CreateFontIndirect(&logFont); + + originalFont = SelectFont(hdc, CurrentParameters.Font); + GetTextMetrics(hdc, &textMetrics); + CurrentParameters.FontHeight = textMetrics.tmHeight; + CurrentParameters.FontAverageWidth = textMetrics.tmAveCharWidth; + + SelectFont(hdc, CurrentParameters.MediumFont); + GetTextMetrics(hdc, &textMetrics); + CurrentParameters.MediumFontHeight = textMetrics.tmHeight; + CurrentParameters.MediumFontAverageWidth = textMetrics.tmAveCharWidth; + + CurrentParameters.SetSectionText = PhMipSetSectionText; + + SelectFont(hdc, originalFont); + ReleaseDC(PhMipWindow, hdc); +} + +PPH_MINIINFO_SECTION PhMipCreateSection( + _In_ PPH_MINIINFO_SECTION Template + ) +{ + PPH_MINIINFO_SECTION section; + + section = PhAllocate(sizeof(PH_MINIINFO_SECTION)); + memset(section, 0, sizeof(PH_MINIINFO_SECTION)); + + section->Name = Template->Name; + section->Flags = Template->Flags; + section->Callback = Template->Callback; + section->Context = Template->Context; + section->Parameters = &CurrentParameters; + + PhAddItemList(SectionList, section); + + section->Callback(section, MiniInfoCreate, NULL, NULL); + + return section; +} + +VOID PhMipDestroySection( + _In_ PPH_MINIINFO_SECTION Section + ) +{ + Section->Callback(Section, MiniInfoDestroy, NULL, NULL); + + PhClearReference(&Section->Text); + PhFree(Section); +} + +PPH_MINIINFO_SECTION PhMipFindSection( + _In_ PPH_STRINGREF Name + ) +{ + ULONG i; + PPH_MINIINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (PhEqualStringRef(§ion->Name, Name, TRUE)) + return section; + } + + return NULL; +} + +PPH_MINIINFO_SECTION PhMipCreateInternalSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_SECTION_CALLBACK Callback + ) +{ + PH_MINIINFO_SECTION section; + + memset(§ion, 0, sizeof(PH_MINIINFO_SECTION)); + PhInitializeStringRef(§ion.Name, Name); + section.Flags = Flags; + section.Callback = Callback; + + return PhMipCreateSection(§ion); +} + +VOID PhMipCreateSectionDialog( + _In_ PPH_MINIINFO_SECTION Section + ) +{ + PH_MINIINFO_CREATE_DIALOG createDialog; + + memset(&createDialog, 0, sizeof(PH_MINIINFO_CREATE_DIALOG)); + + if (Section->Callback(Section, MiniInfoCreateDialog, &createDialog, NULL)) + { + if (!createDialog.CustomCreate) + { + Section->DialogHandle = PhCreateDialogFromTemplate( + PhMipWindow, + DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, + createDialog.Instance, + createDialog.Template, + createDialog.DialogProc, + createDialog.Parameter + ); + + PhInitializeWindowTheme(Section->DialogHandle, PhEnableThemeSupport); + } + } +} + +VOID PhMipChangeSection( + _In_ PPH_MINIINFO_SECTION NewSection + ) +{ + PPH_MINIINFO_SECTION oldSection; + + if (NewSection == CurrentSection) + return; + + oldSection = CurrentSection; + CurrentSection = NewSection; + + if (oldSection) + { + oldSection->Callback(oldSection, MiniInfoSectionChanging, CurrentSection, NULL); + + if (oldSection->DialogHandle) + ShowWindow(oldSection->DialogHandle, SW_HIDE); + } + + if (!NewSection->DialogHandle) + PhMipCreateSectionDialog(NewSection); + if (NewSection->DialogHandle) + ShowWindow(NewSection->DialogHandle, SW_SHOW); + + PhMipUpdateSectionText(NewSection); + PhMipLayout(); + + NewSection->Callback(NewSection, MiniInfoTick, NULL, NULL); +} + +VOID PhMipSetSectionText( + _In_ struct _PH_MINIINFO_SECTION *Section, + _In_opt_ PPH_STRING Text + ) +{ + PhSwapReference(&Section->Text, Text); + + if (Section == CurrentSection) + PhMipUpdateSectionText(Section); +} + +VOID PhMipUpdateSectionText( + _In_ PPH_MINIINFO_SECTION Section + ) +{ + if (Section->Text) + { + PhSetDialogItemText(PhMipWindow, IDC_SECTION, + PH_AUTO_T(PH_STRING, PhConcatStringRef2(&DownArrowPrefix, &Section->Text->sr))->Buffer); + } + else + { + PhSetDialogItemText(PhMipWindow, IDC_SECTION, + PH_AUTO_T(PH_STRING, PhConcatStringRef2(&DownArrowPrefix, &Section->Name))->Buffer); + } +} + +VOID PhMipLayout( + VOID + ) +{ + RECT clientRect; + RECT rect; + + GetClientRect(PhMipContainerWindow, &clientRect); + MoveWindow( + PhMipWindow, + clientRect.left, clientRect.top, + clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, + FALSE + ); + + PhLayoutManagerLayout(&PhMipLayoutManager); + + GetWindowRect(GetDlgItem(PhMipWindow, IDC_LAYOUT), &rect); + MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2); + + if (CurrentSection && CurrentSection->DialogHandle) + { + if (CurrentSection->Flags & PH_MINIINFO_SECTION_NO_UPPER_MARGINS) + { + rect.left = 0; + rect.top = 0; + rect.right = clientRect.right; + } + else + { + LONG leftDistance = rect.left - clientRect.left; + LONG rightDistance = clientRect.right - rect.right; + LONG minDistance; + + if (leftDistance != rightDistance) + { + // HACK: Enforce symmetry. Sometimes these are off by a pixel. + minDistance = min(leftDistance, rightDistance); + rect.left = clientRect.left + minDistance; + rect.right = clientRect.right - minDistance; + } + } + + MoveWindow( + CurrentSection->DialogHandle, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + TRUE + ); + } + + GetWindowRect(GetDlgItem(PhMipWindow, IDC_PINWINDOW), &rect); + MapWindowPoints(NULL, PhMipWindow, (POINT *)&rect, 2); +} + +VOID PhMipBeginChildControlPin( + VOID + ) +{ + PhPinMiniInformation(MiniInfoChildControlPinType, 1, 0, 0, NULL, NULL); +} + +VOID PhMipEndChildControlPin( + VOID + ) +{ + PhPinMiniInformation(MiniInfoChildControlPinType, -1, MIP_UNPIN_CHILD_CONTROL_DELAY, 0, NULL, NULL); + PostMessage(PhMipWindow, WM_MOUSEMOVE, 0, 0); // Re-evaluate hover pin +} + +VOID PhMipRefresh( + VOID + ) +{ + if (PhMipPinned) + ProcessHacker_Refresh(PhMainWndHandle); + + PostMessage(PhMipWindow, MIP_MSG_UPDATE, 0, 0); +} + +VOID PhMipToggleRefreshAutomatically( + VOID + ) +{ + PhMipRefreshAutomatically ^= MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned); + PhSetIntegerSetting(L"MiniInfoWindowRefreshAutomatically", PhMipRefreshAutomatically); +} + +VOID PhMipSetPinned( + _In_ BOOLEAN Pinned + ) +{ + PhSetWindowStyle(PhMipContainerWindow, WS_DLGFRAME | WS_SYSMENU, Pinned ? (WS_DLGFRAME | WS_SYSMENU) : 0); + SetWindowPos(PhMipContainerWindow, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + PhMipPinned = Pinned; + + PhNfNotifyMiniInfoPinned(Pinned); +} + +VOID PhMipShowSectionMenu( + VOID + ) +{ + PPH_EMENU menu; + ULONG i; + PPH_MINIINFO_SECTION section; + PPH_EMENU_ITEM menuItem; + POINT point; + + PhMipBeginChildControlPin(); + menu = PhCreateEMenu(); + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + menuItem = PhCreateEMenuItem( + (section == CurrentSection ? (PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK) : 0), + 0, + PH_AUTO_T(PH_STRING, PhCreateString2(§ion->Name))->Buffer, + NULL, + section + ); + PhInsertEMenuItem(menu, menuItem, -1); + } + + GetCursorPos(&point); + menuItem = PhShowEMenu(menu, PhMipWindow, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); + + if (menuItem) + { + PhMipChangeSection(menuItem->Context); + } + + PhDestroyEMenu(menu); + PhMipEndChildControlPin(); +} + +VOID PhMipShowOptionsMenu( + VOID + ) +{ + PPH_EMENU menu; + PPH_EMENU_ITEM menuItem; + ULONG id; + RECT rect; + + PhMipBeginChildControlPin(); + menu = PhCreateEMenu(); + PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MINIINFO), 0); + + // Opacity + + id = PH_OPACITY_TO_ID(PhGetIntegerSetting(L"MiniInfoWindowOpacity")); + + if (menuItem = PhFindEMenuItem(menu, PH_EMENU_FIND_DESCEND, NULL, id)) + menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; + + // Refresh Automatically + + if (PhMipRefreshAutomatically & MIP_REFRESH_AUTOMATICALLY_FLAG(PhMipPinned)) + PhSetFlagsEMenuItem(menu, ID_MINIINFO_REFRESHAUTOMATICALLY, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + + // Show the menu. + + GetWindowRect(GetDlgItem(PhMipWindow, IDC_OPTIONS), &rect); + menuItem = PhShowEMenu(menu, PhMipWindow, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, rect.left, rect.top); + + if (menuItem) + { + switch (menuItem->Id) + { + case ID_OPACITY_10: + case ID_OPACITY_20: + case ID_OPACITY_30: + case ID_OPACITY_40: + case ID_OPACITY_50: + case ID_OPACITY_60: + case ID_OPACITY_70: + case ID_OPACITY_80: + case ID_OPACITY_90: + case ID_OPACITY_OPAQUE: + { + ULONG opacity; + + opacity = PH_ID_TO_OPACITY(menuItem->Id); + PhSetIntegerSetting(L"MiniInfoWindowOpacity", opacity); + PhSetWindowOpacity(PhMipContainerWindow, opacity); + } + break; + case ID_MINIINFO_REFRESH: + PhMipRefresh(); + break; + case ID_MINIINFO_REFRESHAUTOMATICALLY: + PhMipToggleRefreshAutomatically(); + break; + } + } + + PhDestroyEMenu(menu); + PhMipEndChildControlPin(); +} + +LRESULT CALLBACK PhMipSectionControlHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + WNDPROC oldWndProc; + + if (!(oldWndProc = PhGetWindowContext(hwnd, 0xF))) + return 0; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + } + break; + case WM_SETCURSOR: + { + SetCursor(LoadCursor(NULL, IDC_HAND)); + } + return TRUE; + } + + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); +} + +PPH_MINIINFO_LIST_SECTION PhMipCreateListSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_LIST_SECTION Template + ) +{ + PPH_MINIINFO_LIST_SECTION listSection; + PH_MINIINFO_SECTION section; + + listSection = PhAllocate(sizeof(PH_MINIINFO_LIST_SECTION)); + memset(listSection, 0, sizeof(PH_MINIINFO_LIST_SECTION)); + + listSection->Context = Template->Context; + listSection->Callback = Template->Callback; + + memset(§ion, 0, sizeof(PH_MINIINFO_SECTION)); + PhInitializeStringRef(§ion.Name, Name); + section.Flags = PH_MINIINFO_SECTION_NO_UPPER_MARGINS; + section.Callback = PhMipListSectionCallback; + section.Context = listSection; + listSection->Section = PhMipCreateSection(§ion); + + return listSection; +} + +PPH_MINIINFO_LIST_SECTION PhMipCreateInternalListSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_MINIINFO_LIST_SECTION_CALLBACK Callback + ) +{ + PH_MINIINFO_LIST_SECTION listSection; + + memset(&listSection, 0, sizeof(PH_MINIINFO_LIST_SECTION)); + listSection.Callback = Callback; + + return PhMipCreateListSection(Name, Flags, &listSection); +} + +BOOLEAN PhMipListSectionCallback( + _In_ PPH_MINIINFO_SECTION Section, + _In_ PH_MINIINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + PPH_MINIINFO_LIST_SECTION listSection = Section->Context; + + switch (Message) + { + case MiniInfoCreate: + { + listSection->NodeList = PhCreateList(2); + listSection->Callback(listSection, MiListSectionCreate, NULL, NULL); + } + break; + case MiniInfoDestroy: + { + listSection->Callback(listSection, MiListSectionDestroy, NULL, NULL); + + PhMipClearListSection(listSection); + PhDereferenceObject(listSection->NodeList); + PhFree(listSection); + } + break; + case MiniInfoTick: + if (listSection->SuspendUpdate == 0) + PhMipTickListSection(listSection); + break; + case MiniInfoShowing: + { + listSection->Callback(listSection, MiListSectionShowing, Parameter1, Parameter2); + + if (!Parameter1) // Showing + { + // We don't want to hold process item references while the mini info window + // is hidden. + PhMipClearListSection(listSection); + TreeNew_NodesStructured(listSection->TreeNewHandle); + } + } + break; + case MiniInfoCreateDialog: + { + PPH_MINIINFO_CREATE_DIALOG createDialog = Parameter1; + + createDialog->Instance = PhInstanceHandle; + createDialog->Template = MAKEINTRESOURCE(IDD_MINIINFO_LIST); + createDialog->DialogProc = PhMipListSectionDialogProc; + createDialog->Parameter = listSection; + } + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK PhMipListSectionDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_MINIINFO_LIST_SECTION listSection; + + if (uMsg == WM_INITDIALOG) + { + listSection = (PPH_MINIINFO_LIST_SECTION)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, listSection); + } + else + { + listSection = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!listSection) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_LAYOUT_ITEM layoutItem; + + listSection->DialogHandle = hwndDlg; + listSection->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhInitializeLayoutManager(&listSection->LayoutManager, hwndDlg); + layoutItem = PhAddLayoutItem(&listSection->LayoutManager, listSection->TreeNewHandle, NULL, PH_ANCHOR_ALL); + + // Use negative margins to maximize our use of the window area. + layoutItem->Margin.left = -1; + layoutItem->Margin.top = -1; + layoutItem->Margin.right = -1; + + PhSetControlTheme(listSection->TreeNewHandle, L"explorer"); + TreeNew_SetCallback(listSection->TreeNewHandle, PhMipListSectionTreeNewCallback, listSection); + TreeNew_SetRowHeight(listSection->TreeNewHandle, PhMipCalculateRowHeight()); + PhAddTreeNewColumnEx2(listSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TRUE, L"Process", 1, + PH_ALIGN_LEFT, 0, 0, TN_COLUMN_FLAG_CUSTOMDRAW); + + listSection->Callback(listSection, MiListSectionDialogCreated, hwndDlg, NULL); + PhMipTickListSection(listSection); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&listSection->LayoutManager); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&listSection->LayoutManager); + TreeNew_AutoSizeColumn(listSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TN_AUTOSIZE_REMAINING_SPACE); + } + break; + } + + return FALSE; +} + +VOID PhMipListSectionSortFunction( + _In_ PPH_LIST List, + _In_opt_ PVOID Context + ) +{ + PPH_MINIINFO_LIST_SECTION listSection = Context; + PH_MINIINFO_LIST_SECTION_SORT_LIST sortList; + + sortList.List = List; + listSection->Callback(listSection, MiListSectionSortProcessList, &sortList, NULL); +} + +VOID PhMipTickListSection( + _In_ PPH_MINIINFO_LIST_SECTION ListSection + ) +{ + ULONG i; + PPH_MIP_GROUP_NODE node; + PH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData; + + PhMipClearListSection(ListSection); + + ListSection->ProcessGroupList = PhCreateProcessGroupList( + PhMipListSectionSortFunction, + ListSection, + MIP_MAX_PROCESS_GROUPS, + 0 + ); + + if (!ListSection->ProcessGroupList) + return; + + for (i = 0; i < ListSection->ProcessGroupList->Count; i++) + { + node = PhMipAddGroupNode(ListSection, ListSection->ProcessGroupList->Items[i]); + + if (node->RepresentativeProcessId == ListSection->SelectedRepresentativeProcessId && + node->RepresentativeCreateTime.QuadPart == ListSection->SelectedRepresentativeCreateTime.QuadPart) + { + node->Node.Selected = TRUE; + } + + assignSortData.ProcessGroup = node->ProcessGroup; + assignSortData.SortData = &node->SortData; + ListSection->Callback(ListSection, MiListSectionAssignSortData, &assignSortData, NULL); + } + + TreeNew_NodesStructured(ListSection->TreeNewHandle); + TreeNew_AutoSizeColumn(ListSection->TreeNewHandle, MIP_SINGLE_COLUMN_ID, TN_AUTOSIZE_REMAINING_SPACE); + + ListSection->Callback(ListSection, MiListSectionTick, NULL, NULL); +} + +VOID PhMipClearListSection( + _In_ PPH_MINIINFO_LIST_SECTION ListSection + ) +{ + ULONG i; + + if (ListSection->ProcessGroupList) + { + PhFreeProcessGroupList(ListSection->ProcessGroupList); + ListSection->ProcessGroupList = NULL; + } + + for (i = 0; i < ListSection->NodeList->Count; i++) + PhMipDestroyGroupNode(ListSection->NodeList->Items[i]); + + PhClearList(ListSection->NodeList); +} + +LONG PhMipCalculateRowHeight( + VOID + ) +{ + LONG iconHeight; + LONG titleAndSubtitleHeight; + + iconHeight = MIP_ICON_PADDING + PhLargeIconSize.Y + MIP_CELL_PADDING; + titleAndSubtitleHeight = + MIP_CELL_PADDING * 2 + CurrentParameters.FontHeight + MIP_INNER_PADDING + CurrentParameters.FontHeight; + + return max(iconHeight, titleAndSubtitleHeight); +} + +PPH_MIP_GROUP_NODE PhMipAddGroupNode( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_PROCESS_GROUP ProcessGroup + ) +{ + // This is an undocumented function exported by user32.dll that + // retrieves the hung window represented by a ghost window. + static HWND (WINAPI *HungWindowFromGhostWindow_I)( + _In_ HWND hWnd + ); + + PPH_MIP_GROUP_NODE node; + + node = PhAllocate(sizeof(PH_MIP_GROUP_NODE)); + memset(node, 0, sizeof(PH_MIP_GROUP_NODE)); + + PhInitializeTreeNewNode(&node->Node); + node->ProcessGroup = ProcessGroup; + node->RepresentativeProcessId = ProcessGroup->Representative->ProcessId; + node->RepresentativeCreateTime = ProcessGroup->Representative->CreateTime; + node->RepresentativeIsHung = ProcessGroup->WindowHandle && IsHungAppWindow(ProcessGroup->WindowHandle); + + if (node->RepresentativeIsHung) + { + if (!HungWindowFromGhostWindow_I) + HungWindowFromGhostWindow_I = PhGetDllProcedureAddress(L"user32.dll", "HungWindowFromGhostWindow", 0); + + // Make sure this is a real hung window, not a ghost window. + if (HungWindowFromGhostWindow_I && HungWindowFromGhostWindow_I(ProcessGroup->WindowHandle)) + node->RepresentativeIsHung = FALSE; + } + + PhAddItemList(ListSection->NodeList, node); + + return node; +} + +VOID PhMipDestroyGroupNode( + _In_ PPH_MIP_GROUP_NODE Node + ) +{ + PhClearReference(&Node->TooltipText); + PhFree(Node); +} + +BOOLEAN PhMipListSectionTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_MINIINFO_LIST_SECTION listSection = Context; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + PH_MINIINFO_LIST_SECTION_SORT_LIST sortList; + + if (!getChildren->Node) + { + getChildren->Children = (PPH_TREENEW_NODE *)listSection->NodeList->Items; + getChildren->NumberOfChildren = listSection->NodeList->Count; + + sortList.List = listSection->NodeList; + listSection->Callback(listSection, MiListSectionSortGroupList, &sortList, NULL); + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewCustomDraw: + { + PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; + PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)customDraw->Node; + PPH_PROCESS_ITEM processItem = node->ProcessGroup->Representative; + HDC hdc = customDraw->Dc; + RECT rect = customDraw->CellRect; + ULONG baseTextFlags = DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE; + HICON icon; + COLORREF originalTextColor; + RECT topRect; + RECT bottomRect; + PH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText; + ULONG usageTextTopWidth = 0; + ULONG usageTextBottomWidth = 0; + PH_MINIINFO_LIST_SECTION_GET_TITLE_TEXT getTitleText; + + rect.left += MIP_ICON_PADDING; + rect.top += MIP_ICON_PADDING; + rect.right -= MIP_CELL_PADDING; + rect.bottom -= MIP_CELL_PADDING; + + if (processItem->LargeIcon) + icon = processItem->LargeIcon; + else + PhGetStockApplicationIcon(NULL, &icon); + + DrawIconEx(hdc, rect.left, rect.top, icon, PhLargeIconSize.X, PhLargeIconSize.Y, + 0, NULL, DI_NORMAL); + rect.left += (MIP_CELL_PADDING - MIP_ICON_PADDING) + PhLargeIconSize.X + MIP_CELL_PADDING; + rect.top += MIP_CELL_PADDING - MIP_ICON_PADDING; + SelectFont(hdc, CurrentParameters.Font); + + // This color changes depending on whether the node is selected, etc. + originalTextColor = GetTextColor(hdc); + + // Usage text + + topRect = rect; + topRect.bottom = topRect.top + CurrentParameters.FontHeight; + bottomRect = rect; + bottomRect.top = bottomRect.bottom - CurrentParameters.FontHeight; + + getUsageText.ProcessGroup = node->ProcessGroup; + getUsageText.SortData = &node->SortData; + getUsageText.Line1 = NULL; + getUsageText.Line2 = NULL; + getUsageText.Line1Color = originalTextColor; + getUsageText.Line2Color = originalTextColor; + + if (listSection->Callback(listSection, MiListSectionGetUsageText, &getUsageText, NULL)) + { + PH_STRINGREF text; + RECT textRect; + SIZE textSize; + + // Top + text = PhGetStringRef(getUsageText.Line1); + GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textSize); + usageTextTopWidth = textSize.cx; + textRect = topRect; + textRect.left = textRect.right - textSize.cx; + SetTextColor(hdc, getUsageText.Line1Color); + DrawText(hdc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textRect, baseTextFlags | DT_RIGHT); + PhClearReference(&getUsageText.Line1); + + // Bottom + text = PhGetStringRef(getUsageText.Line2); + GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textSize); + usageTextBottomWidth = textSize.cx; + textRect = bottomRect; + textRect.left = textRect.right - textSize.cx; + SetTextColor(hdc, getUsageText.Line2Color); + DrawText(hdc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textRect, baseTextFlags | DT_RIGHT); + PhClearReference(&getUsageText.Line2); + } + + // Title, subtitle + + getTitleText.ProcessGroup = node->ProcessGroup; + getTitleText.SortData = &node->SortData; + + if (!PhIsNullOrEmptyString(processItem->VersionInfo.FileDescription)) + PhSetReference(&getTitleText.Title, processItem->VersionInfo.FileDescription); + else + PhSetReference(&getTitleText.Title, processItem->ProcessName); + + if (node->ProcessGroup->Processes->Count == 1) + { + PhSetReference(&getTitleText.Subtitle, processItem->ProcessName); + } + else + { + getTitleText.Subtitle = PhFormatString( + L"%s (%u processes)", + processItem->ProcessName->Buffer, + node->ProcessGroup->Processes->Count + ); + } + + getTitleText.TitleColor = originalTextColor; + getTitleText.SubtitleColor = GetSysColor(COLOR_GRAYTEXT); + + // Special text for hung windows + if (node->RepresentativeIsHung) + { + static PH_STRINGREF hungPrefix = PH_STRINGREF_INIT(L"(Not responding) "); + + PhMoveReference(&getTitleText.Title, PhConcatStringRef2(&hungPrefix, &getTitleText.Title->sr)); + getTitleText.TitleColor = RGB(0xff, 0x00, 0x00); + } + + listSection->Callback(listSection, MiListSectionGetTitleText, &getTitleText, NULL); + + if (!PhIsNullOrEmptyString(getTitleText.Title)) + { + RECT textRect; + + textRect = topRect; + textRect.right -= usageTextTopWidth + MIP_INNER_PADDING; + SetTextColor(hdc, getTitleText.TitleColor); + DrawText( + hdc, + getTitleText.Title->Buffer, + (ULONG)getTitleText.Title->Length / sizeof(WCHAR), + &textRect, + baseTextFlags | DT_END_ELLIPSIS + ); + } + + if (!PhIsNullOrEmptyString(getTitleText.Subtitle)) + { + RECT textRect; + + textRect = bottomRect; + textRect.right -= usageTextBottomWidth + MIP_INNER_PADDING; + SetTextColor(hdc, getTitleText.SubtitleColor); + DrawText( + hdc, + getTitleText.Subtitle->Buffer, + (ULONG)getTitleText.Subtitle->Length / sizeof(WCHAR), + &textRect, + baseTextFlags | DT_END_ELLIPSIS + ); + } + + PhClearReference(&getTitleText.Title); + PhClearReference(&getTitleText.Subtitle); + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)getCellTooltip->Node; + + // This is useless most of the time because the tooltip doesn't display unless the window is active. + // TODO: Find a way to make the tooltip display all the time. + + if (!node->TooltipText) + node->TooltipText = PhMipGetGroupNodeTooltip(listSection, node); + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->MaximumWidth = -1; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewSelectionChanged: + { + ULONG i; + PPH_MIP_GROUP_NODE node; + + listSection->SelectedRepresentativeProcessId = NULL; + listSection->SelectedRepresentativeCreateTime.QuadPart = 0; + + for (i = 0; i < listSection->NodeList->Count; i++) + { + node = listSection->NodeList->Items[i]; + + if (node->Node.Selected) + { + listSection->SelectedRepresentativeProcessId = node->RepresentativeProcessId; + listSection->SelectedRepresentativeCreateTime = node->RepresentativeCreateTime; + break; + } + } + } + break; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + PPH_MIP_GROUP_NODE node; + + listSection->SuspendUpdate++; + + switch (keyEvent->VirtualKey) + { + case VK_DELETE: + if (node = PhMipGetSelectedGroupNode(listSection)) + PhUiTerminateProcesses(listSection->DialogHandle, &node->ProcessGroup->Representative, 1); + break; + case VK_RETURN: + if (node = PhMipGetSelectedGroupNode(listSection)) + { + if (GetKeyState(VK_CONTROL) >= 0) + { + PhMipHandleListSectionCommand(listSection, node->ProcessGroup, ID_PROCESS_GOTOPROCESS); + } + else + { + if (node->ProcessGroup->Representative->FileName) + { + PhShellExecuteUserString( + listSection->DialogHandle, + L"FileBrowseExecutable", + node->ProcessGroup->Representative->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } + } + } + break; + } + + listSection->SuspendUpdate--; + } + return TRUE; + case TreeNewLeftDoubleClick: + { + PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + PPH_MIP_GROUP_NODE node = (PPH_MIP_GROUP_NODE)mouseEvent->Node; + + if (node) + { + listSection->SuspendUpdate++; + PhMipHandleListSectionCommand(listSection, node->ProcessGroup, ID_PROCESS_GOTOPROCESS); + listSection->SuspendUpdate--; + } + } + break; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + // Prevent the node list from being updated (otherwise any nodes we're using might be destroyed while we're + // in a modal message loop). + listSection->SuspendUpdate++; + PhMipBeginChildControlPin(); + PhMipShowListSectionContextMenu(listSection, contextMenu); + PhMipEndChildControlPin(); + listSection->SuspendUpdate--; + } + break; + } + + return FALSE; +} + +PPH_STRING PhMipGetGroupNodeTooltip( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_MIP_GROUP_NODE Node + ) +{ + PH_STRING_BUILDER sb; + + PhInitializeStringBuilder(&sb, 100); + + // TODO + + return PhFinalStringBuilderString(&sb); +} + +PPH_MIP_GROUP_NODE PhMipGetSelectedGroupNode( + _In_ PPH_MINIINFO_LIST_SECTION ListSection + ) +{ + ULONG i; + PPH_MIP_GROUP_NODE node; + + for (i = 0; i < ListSection->NodeList->Count; i++) + { + node = ListSection->NodeList->Items[i]; + + if (node->Node.Selected) + return node; + } + + return NULL; +} + +VOID PhMipShowListSectionContextMenu( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenu + ) +{ + PPH_MIP_GROUP_NODE selectedNode; + PPH_EMENU menu; + PPH_EMENU_ITEM item; + PH_MINIINFO_LIST_SECTION_MENU_INFORMATION menuInfo; + PH_PLUGIN_MENU_INFORMATION pluginMenuInfo; + + selectedNode = PhMipGetSelectedGroupNode(ListSection); + + if (!selectedNode) + return; + + menu = PhCreateEMenu(); + // TODO: If there are multiple processes, then create submenus for each process. + PhAddMiniProcessMenuItems(menu, ListSection->SelectedRepresentativeProcessId); + PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MINIINFO_PROCESS), 0); + PhSetFlagsEMenuItem(menu, ID_PROCESS_GOTOPROCESS, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); + + if (selectedNode->ProcessGroup->Processes->Count != 1) + { + if (item = PhFindEMenuItem(menu, 0, NULL, ID_PROCESS_GOTOPROCESS)) + PhModifyEMenuItem(item, PH_EMENU_MODIFY_TEXT, 0, L"&Go to processes", NULL); + } + + memset(&menuInfo, 0, sizeof(PH_MINIINFO_LIST_SECTION_MENU_INFORMATION)); + menuInfo.ProcessGroup = selectedNode->ProcessGroup; + menuInfo.SortData = &selectedNode->SortData; + menuInfo.ContextMenu = ContextMenu; + ListSection->Callback(ListSection, MiListSectionInitializeContextMenu, &menuInfo, NULL); + + if (PhPluginsEnabled) + { + PhPluginInitializeMenuInfo(&pluginMenuInfo, menu, ListSection->DialogHandle, 0); + pluginMenuInfo.Menu = menu; + pluginMenuInfo.OwnerWindow = PhMipWindow; + pluginMenuInfo.u.MiListSection.SectionName = &ListSection->Section->Name; + pluginMenuInfo.u.MiListSection.ProcessGroup = selectedNode->ProcessGroup; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackMiListSectionMenuInitializing), &pluginMenuInfo); + } + + item = PhShowEMenu( + menu, + PhMipWindow, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + ContextMenu->Location.x, + ContextMenu->Location.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + if (!handled && PhPluginsEnabled) + handled = PhPluginTriggerEMenuItem(&pluginMenuInfo, item); + + if (!handled) + { + menuInfo.SelectedItem = item; + handled = ListSection->Callback(ListSection, MiListSectionHandleContextMenu, &menuInfo, NULL); + } + + if (!handled) + PhHandleMiniProcessMenuItem(item); + + if (!handled) + PhMipHandleListSectionCommand(ListSection, selectedNode->ProcessGroup, item->Id); + } + + PhDestroyEMenu(menu); +} + +VOID PhMipHandleListSectionCommand( + _In_ PPH_MINIINFO_LIST_SECTION ListSection, + _In_ PPH_PROCESS_GROUP ProcessGroup, + _In_ ULONG Id + ) +{ + switch (Id) + { + case ID_PROCESS_GOTOPROCESS: + { + PPH_LIST nodes; + ULONG i; + + nodes = PhCreateList(ProcessGroup->Processes->Count); + + for (i = 0; i < ProcessGroup->Processes->Count; i++) + { + PPH_PROCESS_NODE node; + + if (node = PhFindProcessNode(((PPH_PROCESS_ITEM)ProcessGroup->Processes->Items[i])->ProcessId)) + PhAddItemList(nodes, node); + } + + PhPinMiniInformation(MiniInfoIconPinType, -1, 0, 0, NULL, NULL); + PhPinMiniInformation(MiniInfoActivePinType, -1, 0, 0, NULL, NULL); + PhPinMiniInformation(MiniInfoHoverPinType, -1, 0, 0, NULL, NULL); + + ProcessHacker_ToggleVisible(PhMainWndHandle, TRUE); + ProcessHacker_SelectTabPage(PhMainWndHandle, 0); + PhSelectAndEnsureVisibleProcessNodes((PPH_PROCESS_NODE *)nodes->Items, nodes->Count); + PhDereferenceObject(nodes); + } + break; + } +} + +BOOLEAN PhMipCpuListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PhaFormatString(L"CPU %.2f%%", (PhCpuUserUsage + PhCpuKernelUsage) * 100)); + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), PhMipCpuListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + FLOAT cpuUsage = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + cpuUsage += ((PPH_PROCESS_ITEM)processes->Items[i])->CpuUsage; + + *(PFLOAT)assignSortData->SortData->UserData = cpuUsage; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipCpuListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + FLOAT cpuUsage = *(PFLOAT)getUsageText->SortData->UserData * 100; + PPH_STRING cpuUsageText; + + if (cpuUsage >= 0.01) + cpuUsageText = PhFormatString(L"%.2f%%", cpuUsage); + else if (cpuUsage != 0) + cpuUsageText = PhCreateString(L"< 0.01%"); + else + cpuUsageText = NULL; + + PhMoveReference(&getUsageText->Line1, cpuUsageText); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl PhMipCpuListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + int result; + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + + result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); + + if (result == 0) + result = uint64cmp(node2->ProcessItem->UserTime.QuadPart, node1->ProcessItem->UserTime.QuadPart); + + return result; +} + +int __cdecl PhMipCpuListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return singlecmp(*(PFLOAT)data2->UserData, *(PFLOAT)data1->UserData); +} + +BOOLEAN PhMipCommitListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + { + PH_FORMAT format[5]; + DOUBLE commitFraction = (DOUBLE)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit; + + PhInitFormatS(&format[0], L"Commit "); + PhInitFormatSize(&format[1], UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], commitFraction * 100, 2); + PhInitFormatS(&format[4], L"%)"); + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PH_AUTO(PhFormat(format, 5, 96))); + } + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), PhMipCommitListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + ULONG64 privateBytes = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + privateBytes += ((PPH_PROCESS_ITEM)processes->Items[i])->VmCounters.PagefileUsage; + + *(PULONG64)assignSortData->SortData->UserData = privateBytes; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipCommitListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + ULONG64 privateBytes = *(PULONG64)getUsageText->SortData->UserData; + + PhMoveReference(&getUsageText->Line1, PhFormatSize(privateBytes, -1)); + PhMoveReference(&getUsageText->Line2, PhCreateString(L"Private bytes")); + getUsageText->Line2Color = GetSysColor(COLOR_GRAYTEXT); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl PhMipCommitListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + + return uintptrcmp(node2->ProcessItem->VmCounters.PagefileUsage, node1->ProcessItem->VmCounters.PagefileUsage); +} + +int __cdecl PhMipCommitListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return uint64cmp(*(PULONG64)data2->UserData, *(PULONG64)data1->UserData); +} + +BOOLEAN PhMipPhysicalListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + { + PH_FORMAT format[5]; + ULONG physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages; + FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages; + + PhInitFormatS(&format[0], L"Physical "); + PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], physicalFraction * 100, 2); + PhInitFormatS(&format[4], L"%)"); + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PH_AUTO(PhFormat(format, 5, 96))); + } + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), PhMipPhysicalListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + ULONG64 workingSet = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + workingSet += ((PPH_PROCESS_ITEM)processes->Items[i])->VmCounters.WorkingSetSize; + + *(PULONG64)assignSortData->SortData->UserData = workingSet; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipPhysicalListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + ULONG64 privateBytes = *(PULONG64)getUsageText->SortData->UserData; + + PhMoveReference(&getUsageText->Line1, PhFormatSize(privateBytes, -1)); + PhMoveReference(&getUsageText->Line2, PhCreateString(L"Working set")); + getUsageText->Line2Color = GetSysColor(COLOR_GRAYTEXT); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl PhMipPhysicalListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + + return uintptrcmp(node2->ProcessItem->VmCounters.WorkingSetSize, node1->ProcessItem->VmCounters.WorkingSetSize); +} + +int __cdecl PhMipPhysicalListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return uint64cmp(*(PULONG64)data2->UserData, *(PULONG64)data1->UserData); +} + +BOOLEAN PhMipIoListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + { + PH_FORMAT format[6]; + + PhInitFormatS(&format[0], L"I/O R: "); + PhInitFormatSize(&format[1], PhIoReadDelta.Delta); + format[1].Type |= FormatUsePrecision; + format[1].Precision = 0; + PhInitFormatS(&format[2], L" W: "); + PhInitFormatSize(&format[3], PhIoWriteDelta.Delta); + format[3].Type |= FormatUsePrecision; + format[3].Precision = 0; + PhInitFormatS(&format[4], L" O: "); + PhInitFormatSize(&format[5], PhIoOtherDelta.Delta); + format[5].Type |= FormatUsePrecision; + format[5].Precision = 0; + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PH_AUTO(PhFormat(format, 6, 80))); + } + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), PhMipIoListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + ULONG64 ioReadOtherDelta = 0; + ULONG64 ioWriteDelta = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + { + PPH_PROCESS_ITEM processItem = processes->Items[i]; + ioReadOtherDelta += processItem->IoReadDelta.Delta; + ioWriteDelta += processItem->IoWriteDelta.Delta; + ioReadOtherDelta += processItem->IoOtherDelta.Delta; + } + + assignSortData->SortData->UserData[0] = ioReadOtherDelta; + assignSortData->SortData->UserData[1] = ioWriteDelta; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), PhMipIoListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + ULONG64 ioReadOtherDelta = getUsageText->SortData->UserData[0]; + ULONG64 ioWriteDelta = getUsageText->SortData->UserData[1]; + PH_FORMAT format[1]; + + PhInitFormatSize(&format[0], ioReadOtherDelta + ioWriteDelta); + PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16)); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl PhMipIoListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + int result; + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + ULONG64 delta1 = node1->ProcessItem->IoReadDelta.Delta + node1->ProcessItem->IoWriteDelta.Delta + node1->ProcessItem->IoOtherDelta.Delta; + ULONG64 delta2 = node2->ProcessItem->IoReadDelta.Delta + node2->ProcessItem->IoWriteDelta.Delta + node2->ProcessItem->IoOtherDelta.Delta; + ULONG64 value1 = node1->ProcessItem->IoReadDelta.Value + node1->ProcessItem->IoWriteDelta.Value + node1->ProcessItem->IoOtherDelta.Value; + ULONG64 value2 = node2->ProcessItem->IoReadDelta.Value + node2->ProcessItem->IoWriteDelta.Value + node2->ProcessItem->IoOtherDelta.Value; + + result = uint64cmp(delta2, delta1); + + if (result == 0) + result = uint64cmp(value2, value1); + + return result; +} + +int __cdecl PhMipIoListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]); +} diff --git a/ProcessHacker/modlist.c b/ProcessHacker/modlist.c index 45df208186d0..f1627ecb1af0 100644 --- a/ProcessHacker/modlist.c +++ b/ProcessHacker/modlist.c @@ -1,1012 +1,1218 @@ -/* - * Process Hacker - - * module list - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -BOOLEAN PhpModuleNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG PhpModuleNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpDestroyModuleNode( - _In_ PPH_MODULE_NODE ModuleNode - ); - -VOID PhpRemoveModuleNode( - _In_ PPH_MODULE_NODE ModuleNode, - _In_ PPH_MODULE_LIST_CONTEXT Context - ); - -LONG PhpModuleTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpModuleTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -VOID PhInitializeModuleList( - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle, - _Out_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - HWND hwnd; - - memset(Context, 0, sizeof(PH_MODULE_LIST_CONTEXT)); - Context->EnableStateHighlighting = TRUE; - - Context->NodeHashtable = PhCreateHashtable( - sizeof(PPH_MODULE_NODE), - PhpModuleNodeHashtableEqualFunction, - PhpModuleNodeHashtableHashFunction, - 100 - ); - Context->NodeList = PhCreateList(100); - - Context->ParentWindowHandle = ParentWindowHandle; - Context->TreeNewHandle = TreeNewHandle; - hwnd = TreeNewHandle; - PhSetControlTheme(hwnd, L"explorer"); - - TreeNew_SetCallback(hwnd, PhpModuleTreeNewCallback, Context); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHMOTLC_NAME, TRUE, L"Name", 100, PH_ALIGN_LEFT, -2, 0); - PhAddTreeNewColumn(hwnd, PHMOTLC_BASEADDRESS, TRUE, L"Base address", 80, PH_ALIGN_RIGHT, 0, DT_RIGHT); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_SIZE, TRUE, L"Size", 60, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHMOTLC_DESCRIPTION, TRUE, L"Description", 160, PH_ALIGN_LEFT, 2, 0); - - PhAddTreeNewColumn(hwnd, PHMOTLC_COMPANYNAME, FALSE, L"Company name", 180, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHMOTLC_VERSION, FALSE, L"Version", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHMOTLC_FILENAME, FALSE, L"File name", 180, PH_ALIGN_LEFT, -1, DT_PATH_ELLIPSIS); - - PhAddTreeNewColumn(hwnd, PHMOTLC_TYPE, FALSE, L"Type", 80, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_LOADCOUNT, FALSE, L"Load count", 40, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHMOTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHMOTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_ASLR, FALSE, L"ASLR", 50, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_TIMESTAMP, FALSE, L"Time stamp", 100, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_CFGUARD, FALSE, L"CF Guard", 70, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_LOADTIME, FALSE, L"Load time", 100, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumn(hwnd, PHMOTLC_LOADREASON, FALSE, L"Load reason", 80, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_FILEMODIFIEDTIME, FALSE, L"File modified time", 140, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHMOTLC_FILESIZE, FALSE, L"File size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetTriState(hwnd, TRUE); - TreeNew_SetSort(hwnd, 0, NoSortOrder); - - PhCmInitializeManager(&Context->Cm, hwnd, PHMOTLC_MAXIMUM, PhpModuleTreeNewPostSortFunction); -} - -VOID PhDeleteModuleList( - _In_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - ULONG i; - - if (Context->BoldFont) - DeleteObject(Context->BoldFont); - - PhCmDeleteManager(&Context->Cm); - - for (i = 0; i < Context->NodeList->Count; i++) - PhpDestroyModuleNode(Context->NodeList->Items[i]); - - PhDereferenceObject(Context->NodeHashtable); - PhDereferenceObject(Context->NodeList); -} - -BOOLEAN PhpModuleNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_MODULE_NODE moduleNode1 = *(PPH_MODULE_NODE *)Entry1; - PPH_MODULE_NODE moduleNode2 = *(PPH_MODULE_NODE *)Entry2; - - return moduleNode1->ModuleItem == moduleNode2->ModuleItem; -} - -ULONG PhpModuleNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashIntPtr((ULONG_PTR)(*(PPH_MODULE_NODE *)Entry)->ModuleItem); -} - -VOID PhLoadSettingsModuleList( - _Inout_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhGetStringSetting(L"ModuleTreeListColumns"); - sortSettings = PhGetStringSetting(L"ModuleTreeListSort"); - PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSaveSettingsModuleList( - _Inout_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); - PhSetStringSetting2(L"ModuleTreeListColumns", &settings->sr); - PhSetStringSetting2(L"ModuleTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -PPH_MODULE_NODE PhAddModuleNode( - _Inout_ PPH_MODULE_LIST_CONTEXT Context, - _In_ PPH_MODULE_ITEM ModuleItem, - _In_ ULONG RunId - ) -{ - PPH_MODULE_NODE moduleNode; - - moduleNode = PhAllocate(PhEmGetObjectSize(EmModuleNodeType, sizeof(PH_MODULE_NODE))); - memset(moduleNode, 0, sizeof(PH_MODULE_NODE)); - PhInitializeTreeNewNode(&moduleNode->Node); - - if (Context->EnableStateHighlighting && RunId != 1) - { - PhChangeShStateTn( - &moduleNode->Node, - &moduleNode->ShState, - &Context->NodeStateList, - NewItemState, - PhCsColorNew, - NULL - ); - } - - moduleNode->ModuleItem = ModuleItem; - PhReferenceObject(ModuleItem); - - memset(moduleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMOTLC_MAXIMUM); - moduleNode->Node.TextCache = moduleNode->TextCache; - moduleNode->Node.TextCacheSize = PHMOTLC_MAXIMUM; - - PhAddEntryHashtable(Context->NodeHashtable, &moduleNode); - PhAddItemList(Context->NodeList, moduleNode); - - PhEmCallObjectOperation(EmModuleNodeType, moduleNode, EmObjectCreate); - - TreeNew_NodesStructured(Context->TreeNewHandle); - - return moduleNode; -} - -PPH_MODULE_NODE PhFindModuleNode( - _In_ PPH_MODULE_LIST_CONTEXT Context, - _In_ PPH_MODULE_ITEM ModuleItem - ) -{ - PH_MODULE_NODE lookupModuleNode; - PPH_MODULE_NODE lookupModuleNodePtr = &lookupModuleNode; - PPH_MODULE_NODE *moduleNode; - - lookupModuleNode.ModuleItem = ModuleItem; - - moduleNode = (PPH_MODULE_NODE *)PhFindEntryHashtable( - Context->NodeHashtable, - &lookupModuleNodePtr - ); - - if (moduleNode) - return *moduleNode; - else - return NULL; -} - -VOID PhRemoveModuleNode( - _In_ PPH_MODULE_LIST_CONTEXT Context, - _In_ PPH_MODULE_NODE ModuleNode - ) -{ - // Remove from the hashtable here to avoid problems in case the key is re-used. - PhRemoveEntryHashtable(Context->NodeHashtable, &ModuleNode); - - if (Context->EnableStateHighlighting) - { - PhChangeShStateTn( - &ModuleNode->Node, - &ModuleNode->ShState, - &Context->NodeStateList, - RemovingItemState, - PhCsColorRemoved, - Context->TreeNewHandle - ); - } - else - { - PhpRemoveModuleNode(ModuleNode, Context); - } -} - -VOID PhpDestroyModuleNode( - _In_ PPH_MODULE_NODE ModuleNode - ) -{ - PhEmCallObjectOperation(EmModuleNodeType, ModuleNode, EmObjectDelete); - - PhClearReference(&ModuleNode->TooltipText); - - PhClearReference(&ModuleNode->SizeText); - PhClearReference(&ModuleNode->TimeStampText); - PhClearReference(&ModuleNode->LoadTimeText); - PhClearReference(&ModuleNode->FileModifiedTimeText); - PhClearReference(&ModuleNode->FileSizeText); - - PhDereferenceObject(ModuleNode->ModuleItem); - - PhFree(ModuleNode); -} - -VOID PhpRemoveModuleNode( - _In_ PPH_MODULE_NODE ModuleNode, - _In_ PPH_MODULE_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after ModuleNode - ) -{ - ULONG index; - - // Remove from list and cleanup. - - if ((index = PhFindItemList(Context->NodeList, ModuleNode)) != -1) - PhRemoveItemList(Context->NodeList, index); - - PhpDestroyModuleNode(ModuleNode); - - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhUpdateModuleNode( - _In_ PPH_MODULE_LIST_CONTEXT Context, - _In_ PPH_MODULE_NODE ModuleNode - ) -{ - memset(ModuleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMOTLC_MAXIMUM); - PhClearReference(&ModuleNode->TooltipText); - - ModuleNode->ValidMask = 0; - PhInvalidateTreeNewNode(&ModuleNode->Node, TN_CACHE_COLOR); - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhTickModuleNodes( - _In_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - PH_TICK_SH_STATE_TN(PH_MODULE_NODE, ShState, Context->NodeStateList, PhpRemoveModuleNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context); -} - -#define SORT_FUNCTION(Column) PhpModuleTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpModuleTreeNewCompare##Column( \ - _In_ void *_context, \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_MODULE_NODE node1 = *(PPH_MODULE_NODE *)_elem1; \ - PPH_MODULE_NODE node2 = *(PPH_MODULE_NODE *)_elem2; \ - PPH_MODULE_ITEM moduleItem1 = node1->ModuleItem; \ - PPH_MODULE_ITEM moduleItem2 = node2->ModuleItem; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)moduleItem1->BaseAddress, (ULONG_PTR)moduleItem2->BaseAddress); \ - \ - return PhModifySort(sortResult, ((PPH_MODULE_LIST_CONTEXT)_context)->TreeNewSortOrder); \ -} - -LONG PhpModuleTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - if (Result == 0) - Result = uintptrcmp((ULONG_PTR)((PPH_MODULE_NODE)Node1)->ModuleItem->BaseAddress, (ULONG_PTR)((PPH_MODULE_NODE)Node2)->ModuleItem->BaseAddress); - - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(TriState) -{ - if (moduleItem1->IsFirst) - { - sortResult = -1; - } - else if (moduleItem2->IsFirst) - { - sortResult = 1; - } - else - { - sortResult = PhCompareString(moduleItem1->Name, moduleItem2->Name, TRUE); // fall back to sorting by name - } -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Name) -{ - sortResult = PhCompareString(moduleItem1->Name, moduleItem2->Name, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(BaseAddress) -{ - sortResult = uintptrcmp((ULONG_PTR)moduleItem1->BaseAddress, (ULONG_PTR)moduleItem2->BaseAddress); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Size) -{ - sortResult = uintcmp(moduleItem1->Size, moduleItem2->Size); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Description) -{ - sortResult = PhCompareStringWithNull(moduleItem1->VersionInfo.FileDescription, moduleItem2->VersionInfo.FileDescription, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CompanyName) -{ - sortResult = PhCompareStringWithNull(moduleItem1->VersionInfo.CompanyName, moduleItem2->VersionInfo.CompanyName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Version) -{ - sortResult = PhCompareStringWithNull(moduleItem1->VersionInfo.FileVersion, moduleItem2->VersionInfo.FileVersion, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileName) -{ - sortResult = PhCompareStringWithNull(moduleItem1->FileName, moduleItem2->FileName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Type) -{ - sortResult = uintcmp(moduleItem1->Type, moduleItem2->Type); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LoadCount) -{ - sortResult = uintcmp(moduleItem1->LoadCount, moduleItem2->LoadCount); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(VerificationStatus) -{ - sortResult = intcmp(moduleItem1->VerifyResult, moduleItem2->VerifyResult); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(VerifiedSigner) -{ - sortResult = PhCompareStringWithNull( - moduleItem1->VerifySignerName, - moduleItem2->VerifySignerName, - TRUE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Aslr) -{ - sortResult = intcmp( - moduleItem1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, - moduleItem2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(TimeStamp) -{ - sortResult = uintcmp(moduleItem1->ImageTimeDateStamp, moduleItem2->ImageTimeDateStamp); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CfGuard) -{ - sortResult = intcmp( - moduleItem1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF, - moduleItem2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LoadTime) -{ - sortResult = uint64cmp(moduleItem1->LoadTime.QuadPart, moduleItem2->LoadTime.QuadPart); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LoadReason) -{ - sortResult = uintcmp(moduleItem1->LoadReason, moduleItem2->LoadReason); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileModifiedTime) -{ - sortResult = int64cmp(moduleItem1->FileLastWriteTime.QuadPart, moduleItem2->FileLastWriteTime.QuadPart); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileSize) -{ - sortResult = int64cmp(moduleItem1->FileEndOfFile.QuadPart, moduleItem2->FileEndOfFile.QuadPart); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpModuleTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_MODULE_LIST_CONTEXT context; - PPH_MODULE_NODE node; - - context = Context; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Name), - SORT_FUNCTION(BaseAddress), - SORT_FUNCTION(Size), - SORT_FUNCTION(Description), - SORT_FUNCTION(CompanyName), - SORT_FUNCTION(Version), - SORT_FUNCTION(FileName), - SORT_FUNCTION(Type), - SORT_FUNCTION(LoadCount), - SORT_FUNCTION(VerificationStatus), - SORT_FUNCTION(VerifiedSigner), - SORT_FUNCTION(Aslr), - SORT_FUNCTION(TimeStamp), - SORT_FUNCTION(CfGuard), - SORT_FUNCTION(LoadTime), - SORT_FUNCTION(LoadReason), - SORT_FUNCTION(FileModifiedTime), - SORT_FUNCTION(FileSize) - }; - int (__cdecl *sortFunction)(void *, const void *, const void *); - - if (context->TreeNewSortOrder == NoSortOrder) - { - sortFunction = SORT_FUNCTION(TriState); - } - else - { - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)context->NodeList->Items, - context->NodeList->Count, - context->TreeNewSortColumn, - context->TreeNewSortOrder, - &context->Cm - )) - { - if (context->TreeNewSortColumn < PHMOTLC_MAXIMUM) - sortFunction = sortFunctions[context->TreeNewSortColumn]; - else - sortFunction = NULL; - } - else - { - sortFunction = NULL; - } - } - - if (sortFunction) - { - qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); - } - - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; - getChildren->NumberOfChildren = context->NodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_MODULE_ITEM moduleItem; - - node = (PPH_MODULE_NODE)getCellText->Node; - moduleItem = node->ModuleItem; - - switch (getCellText->Id) - { - case PHMOTLC_NAME: - getCellText->Text = moduleItem->Name->sr; - break; - case PHMOTLC_BASEADDRESS: - PhInitializeStringRefLongHint(&getCellText->Text, moduleItem->BaseAddressString); - break; - case PHMOTLC_SIZE: - if (!node->SizeText) - node->SizeText = PhFormatSize(moduleItem->Size, -1); - getCellText->Text = PhGetStringRef(node->SizeText); - break; - case PHMOTLC_DESCRIPTION: - getCellText->Text = PhGetStringRef(moduleItem->VersionInfo.FileDescription); - break; - case PHMOTLC_COMPANYNAME: - getCellText->Text = PhGetStringRef(moduleItem->VersionInfo.CompanyName); - break; - case PHMOTLC_VERSION: - getCellText->Text = PhGetStringRef(moduleItem->VersionInfo.FileVersion); - break; - case PHMOTLC_FILENAME: - getCellText->Text = PhGetStringRef(moduleItem->FileName); - break; - case PHMOTLC_TYPE: - { - PWSTR typeString; - - switch (moduleItem->Type) - { - case PH_MODULE_TYPE_MODULE: - typeString = L"DLL"; - break; - case PH_MODULE_TYPE_MAPPED_FILE: - typeString = L"Mapped file"; - break; - case PH_MODULE_TYPE_MAPPED_IMAGE: - typeString = L"Mapped image"; - break; - case PH_MODULE_TYPE_WOW64_MODULE: - typeString = L"WOW64 DLL"; - break; - case PH_MODULE_TYPE_KERNEL_MODULE: - typeString = L"Kernel module"; - break; - default: - typeString = L"Unknown"; - break; - } - - PhInitializeStringRefLongHint(&getCellText->Text, typeString); - } - break; - case PHMOTLC_LOADCOUNT: - if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE || - moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE) - { - if (moduleItem->LoadCount != (USHORT)-1) - { - PhPrintInt32(node->LoadCountText, moduleItem->LoadCount); - PhInitializeStringRefLongHint(&getCellText->Text, node->LoadCountText); - } - else - { - PhInitializeStringRef(&getCellText->Text, L"Static"); - } - } - else - { - PhInitializeEmptyStringRef(&getCellText->Text); - } - break; - case PHMOTLC_VERIFICATIONSTATUS: - if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE || - moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE) - { - PhInitializeStringRef(&getCellText->Text, - moduleItem->VerifyResult == VrTrusted ? L"Trusted" : L"Not trusted"); - } - else - { - PhInitializeEmptyStringRef(&getCellText->Text); - } - break; - case PHMOTLC_VERIFIEDSIGNER: - getCellText->Text = PhGetStringRef(moduleItem->VerifySignerName); - break; - case PHMOTLC_ASLR: - if (WindowsVersion >= WINDOWS_VISTA) - { - if (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) - PhInitializeStringRef(&getCellText->Text, L"ASLR"); - } - else - { - PhInitializeStringRef(&getCellText->Text, L"N/A"); - } - break; - case PHMOTLC_TIMESTAMP: - { - LARGE_INTEGER time; - SYSTEMTIME systemTime; - - if (moduleItem->ImageTimeDateStamp != 0) - { - RtlSecondsSince1970ToTime(moduleItem->ImageTimeDateStamp, &time); - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->TimeStampText->sr; - } - else - { - PhInitializeEmptyStringRef(&getCellText->Text); - } - } - break; - case PHMOTLC_CFGUARD: - if (WindowsVersion >= WINDOWS_8_1) - { - if (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) - PhInitializeStringRef(&getCellText->Text, L"CF Guard"); - } - else - { - PhInitializeStringRef(&getCellText->Text, L"N/A"); - } - break; - case PHMOTLC_LOADTIME: - { - SYSTEMTIME systemTime; - - if (moduleItem->LoadTime.QuadPart != 0) - { - PhLargeIntegerToLocalSystemTime(&systemTime, &moduleItem->LoadTime); - PhMoveReference(&node->LoadTimeText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->LoadTimeText->sr; - } - else - { - PhInitializeEmptyStringRef(&getCellText->Text); - } - } - break; - case PHMOTLC_LOADREASON: - { - PWSTR string = L""; - - if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE) - { - switch (moduleItem->LoadReason) - { - case LoadReasonStaticDependency: - string = L"Static dependency"; - break; - case LoadReasonStaticForwarderDependency: - string = L"Static forwarder dependency"; - break; - case LoadReasonDynamicForwarderDependency: - string = L"Dynamic forwarder dependency"; - break; - case LoadReasonDelayloadDependency: - string = L"Delay load dependency"; - break; - case LoadReasonDynamicLoad: - string = L"Dynamic"; - break; - case LoadReasonAsImageLoad: - string = L"As image"; - break; - case LoadReasonAsDataLoad: - string = L"As data"; - break; - default: - if (WindowsVersion >= WINDOWS_8) - string = L"Unknown"; - else - string = L"N/A"; - break; - } - } - - PhInitializeStringRefLongHint(&getCellText->Text, string); - } - break; - case PHMOTLC_FILEMODIFIEDTIME: - if (moduleItem->FileLastWriteTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - - PhLargeIntegerToLocalSystemTime(&systemTime, &moduleItem->FileLastWriteTime); - PhMoveReference(&node->FileModifiedTimeText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->FileModifiedTimeText->sr; - } - break; - case PHMOTLC_FILESIZE: - if (moduleItem->FileEndOfFile.QuadPart != -1) - { - PhMoveReference(&node->FileSizeText, PhFormatSize(moduleItem->FileEndOfFile.QuadPart, -1)); - getCellText->Text = node->FileSizeText->sr; - } - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; - PPH_MODULE_ITEM moduleItem; - - node = (PPH_MODULE_NODE)getNodeColor->Node; - moduleItem = node->ModuleItem; - - if (!moduleItem) - ; // Dummy - else if (PhCsUseColorDotNet && (moduleItem->Flags & LDRP_COR_IMAGE)) - getNodeColor->BackColor = PhCsColorDotNet; - else if (PhCsUseColorImmersiveProcesses && (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER)) - getNodeColor->BackColor = PhCsColorImmersiveProcesses; - else if (PhCsUseColorRelocatedModules && (moduleItem->Flags & LDRP_IMAGE_NOT_AT_BASE)) - getNodeColor->BackColor = PhCsColorRelocatedModules; - - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; - } - return TRUE; - case TreeNewGetNodeFont: - { - PPH_TREENEW_GET_NODE_FONT getNodeFont = Parameter1; - - node = (PPH_MODULE_NODE)getNodeFont->Node; - - // Make the executable file module item bold. - if (node->ModuleItem->IsFirst) - { - if (!context->BoldFont) - context->BoldFont = PhDuplicateFontWithNewWeight((HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0), FW_BOLD); - - getNodeFont->Font = context->BoldFont ? context->BoldFont : NULL; - getNodeFont->Flags = TN_CACHE; - return TRUE; - } - } - break; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - - node = (PPH_MODULE_NODE)getCellTooltip->Node; - - if (getCellTooltip->Column->Id != 0) - return FALSE; - - if (!node->TooltipText) - { - node->TooltipText = PhFormatImageVersionInfo( - node->ModuleItem->FileName, - &node->ModuleItem->VersionInfo, - NULL, - 0 - ); - - // Make sure we don't try to create the tooltip text again. - if (!node->TooltipText) - node->TooltipText = PhReferenceEmptyString(); - } - - if (!PhIsNullOrEmptyString(node->TooltipText)) - { - getCellTooltip->Text = node->TooltipText->sr; - getCellTooltip->Unfolding = FALSE; - getCellTooltip->Font = NULL; // use default font - getCellTooltip->MaximumWidth = 550; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(context->TreeNewHandle, 0, -1); - break; - case 'M': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_SEARCHONLINE, 0); - break; - case VK_DELETE: - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_UNLOAD, 0); - break; - case VK_RETURN: - if (GetKeyState(VK_CONTROL) >= 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_INSPECT, 0); - else - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_OPENFILELOCATION, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = NoSortOrder; - PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_INSPECT, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); - } - return TRUE; - case TreeNewGetDialogCode: - { - PULONG code = Parameter2; - - if (PtrToUlong(Parameter1) == VK_RETURN) - { - *code = DLGC_WANTMESSAGE; - return TRUE; - } - } - return FALSE; - } - - return FALSE; -} - -PPH_MODULE_ITEM PhGetSelectedModuleItem( - _In_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - PPH_MODULE_ITEM moduleItem = NULL; - ULONG i; - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_MODULE_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - { - moduleItem = node->ModuleItem; - break; - } - } - - return moduleItem; -} - -VOID PhGetSelectedModuleItems( - _In_ PPH_MODULE_LIST_CONTEXT Context, - _Out_ PPH_MODULE_ITEM **Modules, - _Out_ PULONG NumberOfModules - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_MODULE_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node->ModuleItem); - } - - *NumberOfModules = (ULONG)array.Count; - *Modules = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllModuleNodes( - _In_ PPH_MODULE_LIST_CONTEXT Context - ) -{ - TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); -} +/* + * Process Hacker - + * module list + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +BOOLEAN PhpModuleNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG PhpModuleNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpDestroyModuleNode( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_NODE ModuleNode + ); + +VOID PhpRemoveModuleNode( + _In_ PPH_MODULE_NODE ModuleNode, + _In_ PPH_MODULE_LIST_CONTEXT Context + ); + +LONG PhpModuleTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpModuleTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +VOID PhInitializeModuleList( + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle, + _Out_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + memset(Context, 0, sizeof(PH_MODULE_LIST_CONTEXT)); + Context->EnableStateHighlighting = TRUE; + + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_MODULE_NODE), + PhpModuleNodeHashtableEqualFunction, + PhpModuleNodeHashtableHashFunction, + 100 + ); + Context->NodeList = PhCreateList(100); + Context->NodeRootList = PhCreateList(2); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + + PhSetControlTheme(Context->TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(Context->TreeNewHandle, PhpModuleTreeNewCallback, Context); + + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); + + // Default columns + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_NAME, TRUE, L"Name", 100, PH_ALIGN_LEFT, -2, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_BASEADDRESS, TRUE, L"Base address", 80, PH_ALIGN_RIGHT, 0, DT_RIGHT); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_SIZE, TRUE, L"Size", 60, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_DESCRIPTION, TRUE, L"Description", 160, PH_ALIGN_LEFT, 2, 0); + + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_COMPANYNAME, FALSE, L"Company name", 180, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_VERSION, FALSE, L"Version", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_FILENAME, FALSE, L"File name", 180, PH_ALIGN_LEFT, ULONG_MAX, DT_PATH_ELLIPSIS); + + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_TYPE, FALSE, L"Type", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_LOADCOUNT, FALSE, L"Load count", 40, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_ASLR, FALSE, L"ASLR", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_TIMESTAMP, FALSE, L"Time stamp", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_CFGUARD, FALSE, L"CF Guard", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_LOADTIME, FALSE, L"Load time", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumn(Context->TreeNewHandle, PHMOTLC_LOADREASON, FALSE, L"Load reason", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_FILEMODIFIEDTIME, FALSE, L"File modified time", 140, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_FILESIZE, FALSE, L"File size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_ENTRYPOINT, FALSE, L"Entry point", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(Context->TreeNewHandle, PHMOTLC_PARENTBASEADDRESS, FALSE, L"Parent base address", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + + TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); + + TreeNew_SetTriState(Context->TreeNewHandle, TRUE); + TreeNew_SetSort(Context->TreeNewHandle, 0, NoSortOrder); + + PhCmInitializeManager(&Context->Cm, Context->TreeNewHandle, PHMOTLC_MAXIMUM, PhpModuleTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, Context->TreeNewHandle, Context->NodeList); +} + +VOID PhDeleteModuleList( + _In_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + ULONG i; + + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + + if (Context->BoldFont) + DeleteFont(Context->BoldFont); + + PhCmDeleteManager(&Context->Cm); + + for (i = 0; i < Context->NodeList->Count; i++) + PhpDestroyModuleNode(Context, Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); + PhDereferenceObject(Context->NodeRootList); +} + +BOOLEAN PhpModuleNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_MODULE_NODE moduleNode1 = *(PPH_MODULE_NODE *)Entry1; + PPH_MODULE_NODE moduleNode2 = *(PPH_MODULE_NODE *)Entry2; + + return moduleNode1->ModuleItem == moduleNode2->ModuleItem; +} + +ULONG PhpModuleNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PPH_MODULE_NODE *)Entry)->ModuleItem); +} + +VOID PhLoadSettingsModuleList( + _Inout_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + ULONG flags; + PPH_STRING settings; + PPH_STRING sortSettings; + + flags = PhGetIntegerSetting(L"ModuleTreeListFlags"); + settings = PhGetStringSetting(L"ModuleTreeListColumns"); + sortSettings = PhGetStringSetting(L"ModuleTreeListSort"); + + Context->Flags = flags; + PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSaveSettingsModuleList( + _Inout_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); + + PhSetIntegerSetting(L"ModuleTreeListFlags", Context->Flags); + PhSetStringSetting2(L"ModuleTreeListColumns", &settings->sr); + PhSetStringSetting2(L"ModuleTreeListSort", &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSetOptionsModuleList( + _Inout_ PPH_MODULE_LIST_CONTEXT Context, + _In_ ULONG Options + ) +{ + switch (Options) + { + case PH_MODULE_FLAGS_DYNAMIC_OPTION: + Context->HideDynamicModules = !Context->HideDynamicModules; + break; + case PH_MODULE_FLAGS_MAPPED_OPTION: + Context->HideMappedModules = !Context->HideMappedModules; + break; + case PH_MODULE_FLAGS_STATIC_OPTION: + Context->HideStaticModules = !Context->HideStaticModules; + break; + case PH_MODULE_FLAGS_SIGNED_OPTION: + Context->HideSignedModules = !Context->HideSignedModules; + break; + case PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION: + Context->HighlightUntrustedModules = !Context->HighlightUntrustedModules; + break; + case PH_MODULE_FLAGS_HIGHLIGHT_DOTNET_OPTION: + Context->HighlightDotNetModules = !Context->HighlightDotNetModules; + break; + case PH_MODULE_FLAGS_HIGHLIGHT_IMMERSIVE_OPTION: + Context->HighlightImmersiveModules = !Context->HighlightImmersiveModules; + break; + case PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION: + Context->HighlightRelocatedModules = !Context->HighlightRelocatedModules; + break; + case PH_MODULE_FLAGS_SYSTEM_OPTION: + Context->HideSystemModules = !Context->HideSystemModules; + break; + case PH_MODULE_FLAGS_HIGHLIGHT_SYSTEM_OPTION: + Context->HighlightSystemModules = !Context->HighlightSystemModules; + break; + } +} + +PPH_MODULE_NODE PhCreateModuleNode( + _Inout_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_ITEM ModuleItem, + _In_ ULONG RunId + ) +{ + PPH_MODULE_NODE moduleNode; + + moduleNode = PhAllocate(PhEmGetObjectSize(EmModuleNodeType, sizeof(PH_MODULE_NODE))); + memset(moduleNode, 0, sizeof(PH_MODULE_NODE)); + PhInitializeTreeNewNode(&moduleNode->Node); + + moduleNode->Children = PhCreateList(1); + + if (Context->EnableStateHighlighting && RunId != 1) + { + PhChangeShStateTn( + &moduleNode->Node, + &moduleNode->ShState, + &Context->NodeStateList, + NewItemState, + PhCsColorNew, + NULL + ); + } + + moduleNode->ModuleItem = PhReferenceObject(ModuleItem); + + memset(moduleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMOTLC_MAXIMUM); + moduleNode->Node.TextCache = moduleNode->TextCache; + moduleNode->Node.TextCacheSize = PHMOTLC_MAXIMUM; + + PhAddEntryHashtable(Context->NodeHashtable, &moduleNode); + PhAddItemList(Context->NodeList, moduleNode); + + if (Context->TreeFilterSupport.FilterList) + moduleNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &moduleNode->Node); + + PhEmCallObjectOperation(EmModuleNodeType, moduleNode, EmObjectCreate); + + TreeNew_NodesStructured(Context->TreeNewHandle); + + return moduleNode; +} + +PPH_MODULE_NODE PhAddModuleNode( + _Inout_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_ITEM ModuleItem, + _In_ ULONG RunId + ) +{ + PPH_MODULE_NODE moduleNode; + PPH_MODULE_NODE parentNode; + ULONG i; + + moduleNode = PhCreateModuleNode(Context, ModuleItem, RunId); + + for (i = 0; i < Context->NodeList->Count; i++) + { + parentNode = Context->NodeList->Items[i]; + + if (parentNode != moduleNode && parentNode->ModuleItem->BaseAddress == ModuleItem->ParentBaseAddress) + { + moduleNode->Parent = parentNode; + PhAddItemList(parentNode->Children, moduleNode); + break; + } + } + + if (!moduleNode->Parent) + { + moduleNode->Node.Expanded = TRUE; + PhAddItemList(Context->NodeRootList, moduleNode); + } + + return moduleNode; +} + +PPH_MODULE_NODE PhFindModuleNode( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_ITEM ModuleItem + ) +{ + PH_MODULE_NODE lookupModuleNode; + PPH_MODULE_NODE lookupModuleNodePtr = &lookupModuleNode; + PPH_MODULE_NODE *moduleNode; + + lookupModuleNode.ModuleItem = ModuleItem; + + moduleNode = (PPH_MODULE_NODE *)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupModuleNodePtr + ); + + if (moduleNode) + return *moduleNode; + else + return NULL; +} + +VOID PhRemoveModuleNode( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_NODE ModuleNode + ) +{ + // Remove from the hashtable here to avoid problems in case the key is re-used. + PhRemoveEntryHashtable(Context->NodeHashtable, &ModuleNode); + + if (Context->EnableStateHighlighting) + { + PhChangeShStateTn( + &ModuleNode->Node, + &ModuleNode->ShState, + &Context->NodeStateList, + RemovingItemState, + PhCsColorRemoved, + Context->TreeNewHandle + ); + } + else + { + PhpRemoveModuleNode(ModuleNode, Context); + } +} + +VOID PhpDestroyModuleNode( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_NODE ModuleNode + ) +{ + ULONG index; + + PhEmCallObjectOperation(EmModuleNodeType, ModuleNode, EmObjectDelete); + + if (ModuleNode->Parent) + { + // Remove the node from its parent. + + if ((index = PhFindItemList(ModuleNode->Parent->Children, ModuleNode)) != ULONG_MAX) + PhRemoveItemList(ModuleNode->Parent->Children, index); + } + else + { + // Remove the node from the root list. + + if ((index = PhFindItemList(Context->NodeRootList, ModuleNode)) != ULONG_MAX) + PhRemoveItemList(Context->NodeRootList, index); + } + + // Move the node's children to the root list. + for (index = 0; index < ModuleNode->Children->Count; index++) + { + PPH_MODULE_NODE node = ModuleNode->Children->Items[index]; + + node->Parent = NULL; + PhAddItemList(Context->NodeRootList, node); + } + + PhClearReference(&ModuleNode->TooltipText); + + PhClearReference(&ModuleNode->SizeText); + PhClearReference(&ModuleNode->TimeStampText); + PhClearReference(&ModuleNode->LoadTimeText); + PhClearReference(&ModuleNode->FileModifiedTimeText); + PhClearReference(&ModuleNode->FileSizeText); + + PhDereferenceObject(ModuleNode->ModuleItem); + + PhFree(ModuleNode); +} + +VOID PhpRemoveModuleNode( + _In_ PPH_MODULE_NODE ModuleNode, + _In_ PPH_MODULE_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after ModuleNode + ) +{ + ULONG index; + + // Remove from list and cleanup. + + if ((index = PhFindItemList(Context->NodeList, ModuleNode)) != ULONG_MAX) + PhRemoveItemList(Context->NodeList, index); + + PhpDestroyModuleNode(Context, ModuleNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhUpdateModuleNode( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ PPH_MODULE_NODE ModuleNode + ) +{ + memset(ModuleNode->TextCache, 0, sizeof(PH_STRINGREF) * PHMOTLC_MAXIMUM); + PhClearReference(&ModuleNode->TooltipText); + + ModuleNode->ValidMask = 0; + PhInvalidateTreeNewNode(&ModuleNode->Node, TN_CACHE_COLOR); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhExpandAllModuleNodes( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_MODULE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhTickModuleNodes( + _In_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + PH_TICK_SH_STATE_TN(PH_MODULE_NODE, ShState, Context->NodeStateList, PhpRemoveModuleNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context); +} + +#define SORT_FUNCTION(Column) PhpModuleTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpModuleTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_MODULE_NODE node1 = *(PPH_MODULE_NODE *)_elem1; \ + PPH_MODULE_NODE node2 = *(PPH_MODULE_NODE *)_elem2; \ + PPH_MODULE_ITEM moduleItem1 = node1->ModuleItem; \ + PPH_MODULE_ITEM moduleItem2 = node2->ModuleItem; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)moduleItem1->BaseAddress, (ULONG_PTR)moduleItem2->BaseAddress); \ + \ + return PhModifySort(sortResult, ((PPH_MODULE_LIST_CONTEXT)_context)->TreeNewSortOrder); \ +} + +LONG PhpModuleTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = uintptrcmp((ULONG_PTR)((PPH_MODULE_NODE)Node1)->ModuleItem->BaseAddress, (ULONG_PTR)((PPH_MODULE_NODE)Node2)->ModuleItem->BaseAddress); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(TriState) +{ + if (moduleItem1->IsFirst) + { + sortResult = -1; + } + else if (moduleItem2->IsFirst) + { + sortResult = 1; + } + else + { + sortResult = PhCompareString(moduleItem1->Name, moduleItem2->Name, TRUE); // fall back to sorting by name + } +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareString(moduleItem1->Name, moduleItem2->Name, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(BaseAddress) +{ + sortResult = uintptrcmp((ULONG_PTR)moduleItem1->BaseAddress, (ULONG_PTR)moduleItem2->BaseAddress); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Size) +{ + sortResult = uintcmp(moduleItem1->Size, moduleItem2->Size); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Description) +{ + sortResult = PhCompareStringWithNull(moduleItem1->VersionInfo.FileDescription, moduleItem2->VersionInfo.FileDescription, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CompanyName) +{ + sortResult = PhCompareStringWithNull(moduleItem1->VersionInfo.CompanyName, moduleItem2->VersionInfo.CompanyName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Version) +{ + sortResult = PhCompareStringWithNull(moduleItem1->VersionInfo.FileVersion, moduleItem2->VersionInfo.FileVersion, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileName) +{ + sortResult = PhCompareStringWithNull(moduleItem1->FileName, moduleItem2->FileName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Type) +{ + sortResult = uintcmp(moduleItem1->Type, moduleItem2->Type); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LoadCount) +{ + sortResult = uintcmp(moduleItem1->LoadCount, moduleItem2->LoadCount); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerificationStatus) +{ + sortResult = intcmp(moduleItem1->VerifyResult, moduleItem2->VerifyResult); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerifiedSigner) +{ + sortResult = PhCompareStringWithNull( + moduleItem1->VerifySignerName, + moduleItem2->VerifySignerName, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Aslr) +{ + sortResult = intcmp( + moduleItem1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, + moduleItem2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TimeStamp) +{ + sortResult = uintcmp(moduleItem1->ImageTimeDateStamp, moduleItem2->ImageTimeDateStamp); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CfGuard) +{ + sortResult = intcmp( + moduleItem1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF, + moduleItem2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LoadTime) +{ + sortResult = uint64cmp(moduleItem1->LoadTime.QuadPart, moduleItem2->LoadTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LoadReason) +{ + sortResult = uintcmp(moduleItem1->LoadReason, moduleItem2->LoadReason); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileModifiedTime) +{ + sortResult = int64cmp(moduleItem1->FileLastWriteTime.QuadPart, moduleItem2->FileLastWriteTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileSize) +{ + sortResult = int64cmp(moduleItem1->FileEndOfFile.QuadPart, moduleItem2->FileEndOfFile.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(EntryPoint) +{ + sortResult = uintptrcmp((ULONG_PTR)moduleItem1->EntryPoint, (ULONG_PTR)moduleItem2->EntryPoint); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ParentBaseAddress) +{ + sortResult = uintptrcmp((ULONG_PTR)moduleItem1->ParentBaseAddress, (ULONG_PTR)moduleItem2->ParentBaseAddress); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpModuleTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_MODULE_LIST_CONTEXT context; + PPH_MODULE_NODE node; + + context = Context; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + node = (PPH_MODULE_NODE)getChildren->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + { + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items; + getChildren->NumberOfChildren = context->NodeRootList->Count; + } + else + { + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + + qsort_s(getChildren->Children, getChildren->NumberOfChildren, sizeof(PVOID), SORT_FUNCTION(TriState), context); + } + else + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Name), + SORT_FUNCTION(BaseAddress), + SORT_FUNCTION(Size), + SORT_FUNCTION(Description), + SORT_FUNCTION(CompanyName), + SORT_FUNCTION(Version), + SORT_FUNCTION(FileName), + SORT_FUNCTION(Type), + SORT_FUNCTION(LoadCount), + SORT_FUNCTION(VerificationStatus), + SORT_FUNCTION(VerifiedSigner), + SORT_FUNCTION(Aslr), + SORT_FUNCTION(TimeStamp), + SORT_FUNCTION(CfGuard), + SORT_FUNCTION(LoadTime), + SORT_FUNCTION(LoadReason), + SORT_FUNCTION(FileModifiedTime), + SORT_FUNCTION(FileSize), + SORT_FUNCTION(EntryPoint), + SORT_FUNCTION(ParentBaseAddress) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortOrder == NoSortOrder) + { + sortFunction = SORT_FUNCTION(TriState); + } + else + { + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)context->NodeList->Items, + context->NodeList->Count, + context->TreeNewSortColumn, + context->TreeNewSortOrder, + &context->Cm + )) + { + if (context->TreeNewSortColumn < PHMOTLC_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + } + else + { + sortFunction = NULL; + } + } + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + node = (PPH_MODULE_NODE)isLeaf->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + isLeaf->IsLeaf = node->Children->Count == 0; + else + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_MODULE_ITEM moduleItem; + + node = (PPH_MODULE_NODE)getCellText->Node; + moduleItem = node->ModuleItem; + + switch (getCellText->Id) + { + case PHMOTLC_NAME: + getCellText->Text = PhGetStringRef(moduleItem->Name); + break; + case PHMOTLC_BASEADDRESS: + PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); + PhInitializeStringRefLongHint(&getCellText->Text, moduleItem->BaseAddressString); + break; + case PHMOTLC_SIZE: + if (!node->SizeText) + node->SizeText = PhFormatSize(moduleItem->Size, ULONG_MAX); + getCellText->Text = PhGetStringRef(node->SizeText); + break; + case PHMOTLC_DESCRIPTION: + getCellText->Text = PhGetStringRef(moduleItem->VersionInfo.FileDescription); + break; + case PHMOTLC_COMPANYNAME: + getCellText->Text = PhGetStringRef(moduleItem->VersionInfo.CompanyName); + break; + case PHMOTLC_VERSION: + getCellText->Text = PhGetStringRef(moduleItem->VersionInfo.FileVersion); + break; + case PHMOTLC_FILENAME: + getCellText->Text = PhGetStringRef(moduleItem->FileName); + break; + case PHMOTLC_TYPE: + { + PWSTR typeString; + + switch (moduleItem->Type) + { + case PH_MODULE_TYPE_MODULE: + typeString = L"DLL"; + break; + case PH_MODULE_TYPE_MAPPED_FILE: + typeString = L"Mapped file"; + break; + case PH_MODULE_TYPE_MAPPED_IMAGE: + case PH_MODULE_TYPE_ELF_MAPPED_IMAGE: + typeString = L"Mapped image"; + break; + case PH_MODULE_TYPE_WOW64_MODULE: + typeString = L"WOW64 DLL"; + break; + case PH_MODULE_TYPE_KERNEL_MODULE: + typeString = L"Kernel module"; + break; + default: + typeString = L"Unknown"; + break; + } + + PhInitializeStringRefLongHint(&getCellText->Text, typeString); + } + break; + case PHMOTLC_LOADCOUNT: + if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE || + moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE) + { + if (moduleItem->LoadCount != USHRT_MAX) + { + PhPrintInt32(node->LoadCountText, moduleItem->LoadCount); + PhInitializeStringRefLongHint(&getCellText->Text, node->LoadCountText); + } + else + { + PhInitializeStringRef(&getCellText->Text, L"Static"); + } + } + else + { + PhInitializeEmptyStringRef(&getCellText->Text); + } + break; + case PHMOTLC_VERIFICATIONSTATUS: + { + if (moduleItem->Type != PH_MODULE_TYPE_ELF_MAPPED_IMAGE) + { + PhInitializeStringRef(&getCellText->Text, + moduleItem->VerifyResult == VrTrusted ? L"Trusted" : L"Not trusted"); + } + else + { + PhInitializeEmptyStringRef(&getCellText->Text); + } + } + break; + case PHMOTLC_VERIFIEDSIGNER: + getCellText->Text = PhGetStringRef(moduleItem->VerifySignerName); + break; + case PHMOTLC_ASLR: + if (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) + PhInitializeStringRef(&getCellText->Text, L"ASLR"); + break; + case PHMOTLC_TIMESTAMP: + { + LARGE_INTEGER time; + SYSTEMTIME systemTime; + + if (moduleItem->ImageTimeDateStamp != 0) + { + RtlSecondsSince1970ToTime(moduleItem->ImageTimeDateStamp, &time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->TimeStampText->sr; + } + else + { + PhInitializeEmptyStringRef(&getCellText->Text); + } + } + break; + case PHMOTLC_CFGUARD: + if (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) + PhInitializeStringRef(&getCellText->Text, L"CF Guard"); + break; + case PHMOTLC_LOADTIME: + { + SYSTEMTIME systemTime; + + if (moduleItem->LoadTime.QuadPart != 0) + { + PhLargeIntegerToLocalSystemTime(&systemTime, &moduleItem->LoadTime); + PhMoveReference(&node->LoadTimeText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->LoadTimeText->sr; + } + else + { + PhInitializeEmptyStringRef(&getCellText->Text); + } + } + break; + case PHMOTLC_LOADREASON: + { + PWSTR string = L""; + + if (moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) + { + string = L"Dynamic"; + } + else if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE) + { + switch (moduleItem->LoadReason) + { + case LoadReasonStaticDependency: + string = L"Static dependency"; + break; + case LoadReasonStaticForwarderDependency: + string = L"Static forwarder dependency"; + break; + case LoadReasonDynamicForwarderDependency: + string = L"Dynamic forwarder dependency"; + break; + case LoadReasonDelayloadDependency: + string = L"Delay load dependency"; + break; + case LoadReasonDynamicLoad: + string = L"Dynamic"; + break; + case LoadReasonAsImageLoad: + string = L"As image"; + break; + case LoadReasonAsDataLoad: + string = L"As data"; + break; + case LoadReasonEnclavePrimary: + string = L"Enclave"; + break; + case LoadReasonEnclaveDependency: + string = L"Enclave dependency"; + break; + default: + if (WindowsVersion >= WINDOWS_8) + string = L"Unknown"; + else + string = L"N/A"; + break; + } + } + + PhInitializeStringRefLongHint(&getCellText->Text, string); + } + break; + case PHMOTLC_FILEMODIFIEDTIME: + if (moduleItem->FileLastWriteTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + + PhLargeIntegerToLocalSystemTime(&systemTime, &moduleItem->FileLastWriteTime); + PhMoveReference(&node->FileModifiedTimeText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->FileModifiedTimeText->sr; + } + break; + case PHMOTLC_FILESIZE: + if (moduleItem->FileEndOfFile.QuadPart != -1) + { + PhMoveReference(&node->FileSizeText, PhFormatSize(moduleItem->FileEndOfFile.QuadPart, ULONG_MAX)); + getCellText->Text = node->FileSizeText->sr; + } + break; + case PHMOTLC_ENTRYPOINT: + if (moduleItem->EntryPoint != 0) + { + PhPrintPointer(moduleItem->EntryPointAddressString, moduleItem->EntryPoint); + PhInitializeStringRef(&getCellText->Text, moduleItem->EntryPointAddressString); + } + break; + case PHMOTLC_PARENTBASEADDRESS: + if (moduleItem->ParentBaseAddress != 0) + { + PhPrintPointer(moduleItem->ParentBaseAddressString, moduleItem->ParentBaseAddress); + PhInitializeStringRefLongHint(&getCellText->Text, moduleItem->ParentBaseAddressString); + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PPH_MODULE_ITEM moduleItem; + + node = (PPH_MODULE_NODE)getNodeColor->Node; + moduleItem = node->ModuleItem; + + if (!moduleItem) + ; // Dummy + else if (PhEnableProcessQueryStage2 && + context->HighlightUntrustedModules && + moduleItem->VerifyResult != VrTrusted && + moduleItem->Type != PH_MODULE_TYPE_ELF_MAPPED_IMAGE + ) + { + getNodeColor->BackColor = PhCsColorUnknown; + } + else if (context->HighlightDotNetModules && (moduleItem->Flags & LDRP_COR_IMAGE)) + getNodeColor->BackColor = PhCsColorDotNet; + else if (context->HighlightImmersiveModules && (moduleItem->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER)) + getNodeColor->BackColor = PhCsColorImmersiveProcesses; + else if (context->HighlightRelocatedModules && (moduleItem->Flags & LDRP_IMAGE_NOT_AT_BASE)) + getNodeColor->BackColor = PhCsColorRelocatedModules; + else if (PhEnableProcessQueryStage2 && + context->HighlightSystemModules && + moduleItem->VerifyResult == VrTrusted && + PhEqualStringRef2(&moduleItem->VerifySignerName->sr, L"Microsoft Windows", TRUE) + ) + { + getNodeColor->BackColor = PhCsColorSystemProcesses; + } + + getNodeColor->Flags = TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewGetNodeFont: + { + PPH_TREENEW_GET_NODE_FONT getNodeFont = Parameter1; + + node = (PPH_MODULE_NODE)getNodeFont->Node; + + // Make the executable file module item bold. + if (node->ModuleItem->IsFirst) + { + if (!context->BoldFont) + context->BoldFont = PhDuplicateFontWithNewWeight(GetWindowFont(hwnd), FW_BOLD); + + getNodeFont->Font = context->BoldFont ? context->BoldFont : NULL; + getNodeFont->Flags = TN_CACHE; + return TRUE; + } + } + break; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + + node = (PPH_MODULE_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0) + return FALSE; + + if (!node->TooltipText) + { + node->TooltipText = PhFormatImageVersionInfo( + node->ModuleItem->FileName, + &node->ModuleItem->VersionInfo, + NULL, + 0 + ); + + // Make sure we don't try to create the tooltip text again. + if (!node->TooltipText) + node->TooltipText = PhReferenceEmptyString(); + } + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->Font = NULL; // use default font + getCellTooltip->MaximumWidth = 550; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(context->TreeNewHandle, 0, -1); + break; + case 'M': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_SEARCHONLINE, 0); + break; + case VK_DELETE: + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_UNLOAD, 0); + break; + case VK_RETURN: + if (GetKeyState(VK_CONTROL) >= 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_INSPECT, 0); + else + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_OPENFILELOCATION, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = NoSortOrder; + PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_MODULE_INSPECT, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); + } + return TRUE; + case TreeNewGetDialogCode: + { + PULONG code = Parameter2; + + if (PtrToUlong(Parameter1) == VK_RETURN) + { + *code = DLGC_WANTMESSAGE; + return TRUE; + } + } + return FALSE; + } + + return FALSE; +} + +PPH_MODULE_ITEM PhGetSelectedModuleItem( + _In_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + PPH_MODULE_ITEM moduleItem = NULL; + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_MODULE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + moduleItem = node->ModuleItem; + break; + } + } + + return moduleItem; +} + +VOID PhGetSelectedModuleItems( + _In_ PPH_MODULE_LIST_CONTEXT Context, + _Out_ PPH_MODULE_ITEM **Modules, + _Out_ PULONG NumberOfModules + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_MODULE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node->ModuleItem); + } + + *NumberOfModules = (ULONG)array.Count; + *Modules = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllModuleNodes( + _In_ PPH_MODULE_LIST_CONTEXT Context + ) +{ + TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); +} diff --git a/ProcessHacker/modprv.c b/ProcessHacker/modprv.c index f3483ad3ef41..1b942fa75c1f 100644 --- a/ProcessHacker/modprv.c +++ b/ProcessHacker/modprv.c @@ -1,589 +1,710 @@ -/* - * Process Hacker - - * module provider - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include -#include - -#include -#include - -typedef struct _PH_MODULE_QUERY_DATA -{ - SLIST_ENTRY ListEntry; - PPH_MODULE_PROVIDER ModuleProvider; - PPH_MODULE_ITEM ModuleItem; - - VERIFY_RESULT VerifyResult; - PPH_STRING VerifySignerName; -} PH_MODULE_QUERY_DATA, *PPH_MODULE_QUERY_DATA; - -VOID NTAPI PhpModuleProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID NTAPI PhpModuleItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -BOOLEAN NTAPI PhpModuleHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI PhpModuleHashtableHashFunction( - _In_ PVOID Entry - ); - -PPH_OBJECT_TYPE PhModuleProviderType; -PPH_OBJECT_TYPE PhModuleItemType; - -BOOLEAN PhModuleProviderInitialization( - VOID - ) -{ - PhModuleProviderType = PhCreateObjectType(L"ModuleProvider", 0, PhpModuleProviderDeleteProcedure); - PhModuleItemType = PhCreateObjectType(L"ModuleItem", 0, PhpModuleItemDeleteProcedure); - - return TRUE; -} - -PPH_MODULE_PROVIDER PhCreateModuleProvider( - _In_ HANDLE ProcessId - ) -{ - NTSTATUS status; - PPH_MODULE_PROVIDER moduleProvider; - - moduleProvider = PhCreateObject( - PhEmGetObjectSize(EmModuleProviderType, sizeof(PH_MODULE_PROVIDER)), - PhModuleProviderType - ); - - moduleProvider->ModuleHashtable = PhCreateHashtable( - sizeof(PPH_MODULE_ITEM), - PhpModuleHashtableEqualFunction, - PhpModuleHashtableHashFunction, - 20 - ); - PhInitializeFastLock(&moduleProvider->ModuleHashtableLock); - - PhInitializeCallback(&moduleProvider->ModuleAddedEvent); - PhInitializeCallback(&moduleProvider->ModuleModifiedEvent); - PhInitializeCallback(&moduleProvider->ModuleRemovedEvent); - PhInitializeCallback(&moduleProvider->UpdatedEvent); - - moduleProvider->ProcessId = ProcessId; - moduleProvider->ProcessHandle = NULL; - moduleProvider->PackageFullName = NULL; - moduleProvider->RunStatus = STATUS_SUCCESS; - - // It doesn't matter if we can't get a process handle. - - // Try to get a handle with query information + vm read access. - if (!NT_SUCCESS(status = PhOpenProcess( - &moduleProvider->ProcessHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - ProcessId - ))) - { - if (WINDOWS_HAS_LIMITED_ACCESS) - { - // Try to get a handle with query limited information + vm read access. - status = PhOpenProcess( - &moduleProvider->ProcessHandle, - PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, - ProcessId - ); - } - - moduleProvider->RunStatus = status; - } - - if (moduleProvider->ProcessHandle) - moduleProvider->PackageFullName = PhGetProcessPackageFullName(moduleProvider->ProcessHandle); - - RtlInitializeSListHead(&moduleProvider->QueryListHead); - - PhEmCallObjectOperation(EmModuleProviderType, moduleProvider, EmObjectCreate); - - return moduleProvider; -} - -VOID PhpModuleProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; - - PhEmCallObjectOperation(EmModuleProviderType, moduleProvider, EmObjectDelete); - - // Dereference all module items (we referenced them - // when we added them to the hashtable). - PhDereferenceAllModuleItems(moduleProvider); - - PhDereferenceObject(moduleProvider->ModuleHashtable); - PhDeleteFastLock(&moduleProvider->ModuleHashtableLock); - PhDeleteCallback(&moduleProvider->ModuleAddedEvent); - PhDeleteCallback(&moduleProvider->ModuleModifiedEvent); - PhDeleteCallback(&moduleProvider->ModuleRemovedEvent); - PhDeleteCallback(&moduleProvider->UpdatedEvent); - - // Destroy all queue items. - { - PSLIST_ENTRY entry; - PPH_MODULE_QUERY_DATA data; - - entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry); - entry = entry->Next; - - if (data->VerifySignerName) PhDereferenceObject(data->VerifySignerName); - PhDereferenceObject(data->ModuleItem); - PhFree(data); - } - } - - if (moduleProvider->PackageFullName) PhDereferenceObject(moduleProvider->PackageFullName); - if (moduleProvider->ProcessHandle) NtClose(moduleProvider->ProcessHandle); -} - -PPH_MODULE_ITEM PhCreateModuleItem( - VOID - ) -{ - PPH_MODULE_ITEM moduleItem; - - moduleItem = PhCreateObject( - PhEmGetObjectSize(EmModuleItemType, sizeof(PH_MODULE_ITEM)), - PhModuleItemType - ); - memset(moduleItem, 0, sizeof(PH_MODULE_ITEM)); - PhEmCallObjectOperation(EmModuleItemType, moduleItem, EmObjectCreate); - - return moduleItem; -} - -VOID PhpModuleItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_MODULE_ITEM moduleItem = (PPH_MODULE_ITEM)Object; - - PhEmCallObjectOperation(EmModuleItemType, moduleItem, EmObjectDelete); - - if (moduleItem->Name) PhDereferenceObject(moduleItem->Name); - if (moduleItem->FileName) PhDereferenceObject(moduleItem->FileName); - if (moduleItem->VerifySignerName) PhDereferenceObject(moduleItem->VerifySignerName); - PhDeleteImageVersionInfo(&moduleItem->VersionInfo); -} - -BOOLEAN NTAPI PhpModuleHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - return - (*(PPH_MODULE_ITEM *)Entry1)->BaseAddress == - (*(PPH_MODULE_ITEM *)Entry2)->BaseAddress; -} - -ULONG NTAPI PhpModuleHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PVOID baseAddress = (*(PPH_MODULE_ITEM *)Entry)->BaseAddress; - - return PhHashIntPtr((ULONG_PTR)baseAddress); -} - -PPH_MODULE_ITEM PhReferenceModuleItem( - _In_ PPH_MODULE_PROVIDER ModuleProvider, - _In_ PVOID BaseAddress - ) -{ - PH_MODULE_ITEM lookupModuleItem; - PPH_MODULE_ITEM lookupModuleItemPtr = &lookupModuleItem; - PPH_MODULE_ITEM *moduleItemPtr; - PPH_MODULE_ITEM moduleItem; - - lookupModuleItem.BaseAddress = BaseAddress; - - PhAcquireFastLockShared(&ModuleProvider->ModuleHashtableLock); - - moduleItemPtr = (PPH_MODULE_ITEM *)PhFindEntryHashtable( - ModuleProvider->ModuleHashtable, - &lookupModuleItemPtr - ); - - if (moduleItemPtr) - { - moduleItem = *moduleItemPtr; - PhReferenceObject(moduleItem); - } - else - { - moduleItem = NULL; - } - - PhReleaseFastLockShared(&ModuleProvider->ModuleHashtableLock); - - return moduleItem; -} - -VOID PhDereferenceAllModuleItems( - _In_ PPH_MODULE_PROVIDER ModuleProvider - ) -{ - ULONG enumerationKey = 0; - PPH_MODULE_ITEM *moduleItem; - - PhAcquireFastLockExclusive(&ModuleProvider->ModuleHashtableLock); - - while (PhEnumHashtable(ModuleProvider->ModuleHashtable, (PVOID *)&moduleItem, &enumerationKey)) - { - PhDereferenceObject(*moduleItem); - } - - PhReleaseFastLockExclusive(&ModuleProvider->ModuleHashtableLock); -} - -VOID PhpRemoveModuleItem( - _In_ PPH_MODULE_PROVIDER ModuleProvider, - _In_ PPH_MODULE_ITEM ModuleItem - ) -{ - PhRemoveEntryHashtable(ModuleProvider->ModuleHashtable, &ModuleItem); - PhDereferenceObject(ModuleItem); -} - -NTSTATUS PhpModuleQueryWorker( - _In_ PVOID Parameter - ) -{ - PPH_MODULE_QUERY_DATA data = (PPH_MODULE_QUERY_DATA)Parameter; - - data->VerifyResult = PhVerifyFileCached( - data->ModuleItem->FileName, - PhGetString(data->ModuleProvider->PackageFullName), - &data->VerifySignerName, - FALSE - ); - - RtlInterlockedPushEntrySList(&data->ModuleProvider->QueryListHead, &data->ListEntry); - - PhDereferenceObject(data->ModuleProvider); - - return STATUS_SUCCESS; -} - -VOID PhpQueueModuleQuery( - _In_ PPH_MODULE_PROVIDER ModuleProvider, - _In_ PPH_MODULE_ITEM ModuleItem - ) -{ - PPH_MODULE_QUERY_DATA data; - PH_WORK_QUEUE_ENVIRONMENT environment; - - if (!PhEnableProcessQueryStage2) - return; - - data = PhAllocate(sizeof(PH_MODULE_QUERY_DATA)); - memset(data, 0, sizeof(PH_MODULE_QUERY_DATA)); - data->ModuleProvider = ModuleProvider; - data->ModuleItem = ModuleItem; - - PhReferenceObject(ModuleProvider); - PhReferenceObject(ModuleItem); - - PhInitializeWorkQueueEnvironment(&environment); - environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; - environment.IoPriority = IoPriorityLow; - environment.PagePriority = MEMORY_PRIORITY_LOW; - - PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpModuleQueryWorker, data, NULL, &environment); -} - -static BOOLEAN NTAPI EnumModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PPH_MODULE_INFO copy; - - copy = PhAllocateCopy(Module, sizeof(PH_MODULE_INFO)); - PhReferenceObject(copy->Name); - PhReferenceObject(copy->FileName); - - PhAddItemList((PPH_LIST)Context, copy); - - return TRUE; -} - -VOID PhModuleProviderUpdate( - _In_ PVOID Object - ) -{ - PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; - PPH_LIST modules; - ULONG i; - - // If we didn't get a handle when we created the provider, - // abort (unless this is the System process - in that case - // we don't need a handle). - if (!moduleProvider->ProcessHandle && moduleProvider->ProcessId != SYSTEM_PROCESS_ID) - goto UpdateExit; - - modules = PhCreateList(20); - - moduleProvider->RunStatus = PhEnumGenericModules( - moduleProvider->ProcessId, - moduleProvider->ProcessHandle, - PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES, - EnumModulesCallback, - modules - ); - - // Look for removed modules. - { - PPH_LIST modulesToRemove = NULL; - ULONG enumerationKey = 0; - PPH_MODULE_ITEM *moduleItem; - - while (PhEnumHashtable(moduleProvider->ModuleHashtable, (PVOID *)&moduleItem, &enumerationKey)) - { - BOOLEAN found = FALSE; - - // Check if the module still exists. - for (i = 0; i < modules->Count; i++) - { - PPH_MODULE_INFO module = modules->Items[i]; - - if ((*moduleItem)->BaseAddress == module->BaseAddress && - PhEqualString((*moduleItem)->FileName, module->FileName, TRUE)) - { - found = TRUE; - break; - } - } - - if (!found) - { - // Raise the module removed event. - PhInvokeCallback(&moduleProvider->ModuleRemovedEvent, *moduleItem); - - if (!modulesToRemove) - modulesToRemove = PhCreateList(2); - - PhAddItemList(modulesToRemove, *moduleItem); - } - } - - if (modulesToRemove) - { - PhAcquireFastLockExclusive(&moduleProvider->ModuleHashtableLock); - - for (i = 0; i < modulesToRemove->Count; i++) - { - PhpRemoveModuleItem( - moduleProvider, - (PPH_MODULE_ITEM)modulesToRemove->Items[i] - ); - } - - PhReleaseFastLockExclusive(&moduleProvider->ModuleHashtableLock); - PhDereferenceObject(modulesToRemove); - } - } - - // Go through the queued thread query data. - { - PSLIST_ENTRY entry; - PPH_MODULE_QUERY_DATA data; - - entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry); - entry = entry->Next; - - data->ModuleItem->VerifyResult = data->VerifyResult; - data->ModuleItem->VerifySignerName = data->VerifySignerName; - data->ModuleItem->JustProcessed = TRUE; - - PhDereferenceObject(data->ModuleItem); - PhFree(data); - } - } - - // Look for new modules. - for (i = 0; i < modules->Count; i++) - { - PPH_MODULE_INFO module = modules->Items[i]; - PPH_MODULE_ITEM moduleItem; - - moduleItem = PhReferenceModuleItem(moduleProvider, module->BaseAddress); - - if (!moduleItem) - { - FILE_NETWORK_OPEN_INFORMATION networkOpenInfo; - - moduleItem = PhCreateModuleItem(); - - moduleItem->BaseAddress = module->BaseAddress; - PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress); - moduleItem->Size = module->Size; - moduleItem->Flags = module->Flags; - moduleItem->Type = module->Type; - moduleItem->LoadReason = module->LoadReason; - moduleItem->LoadCount = module->LoadCount; - moduleItem->LoadTime = module->LoadTime; - - moduleItem->Name = module->Name; - PhReferenceObject(moduleItem->Name); - moduleItem->FileName = module->FileName; - PhReferenceObject(moduleItem->FileName); - - PhInitializeImageVersionInfo(&moduleItem->VersionInfo, moduleItem->FileName->Buffer); - - moduleItem->IsFirst = i == 0; - - // Fix up the load count. If this is not an ordinary DLL or kernel module, set the load count to 0. - if (moduleItem->Type != PH_MODULE_TYPE_MODULE && - moduleItem->Type != PH_MODULE_TYPE_WOW64_MODULE && - moduleItem->Type != PH_MODULE_TYPE_KERNEL_MODULE) - { - moduleItem->LoadCount = 0; - } - - if (moduleItem->Type == PH_MODULE_TYPE_MODULE || - moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || - moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE) - { - PH_REMOTE_MAPPED_IMAGE remoteMappedImage; - - // Note: - // On Windows 7 the LDRP_IMAGE_NOT_AT_BASE flag doesn't appear to be used - // anymore. Instead we'll check ImageBase in the image headers. We read this in - // from the process' memory because: - // - // 1. It (should be) faster than opening the file and mapping it in, and - // 2. It contains the correct original image base relocated by ASLR, if present. - - moduleItem->Flags &= ~LDRP_IMAGE_NOT_AT_BASE; - - if (NT_SUCCESS(PhLoadRemoteMappedImage(moduleProvider->ProcessHandle, moduleItem->BaseAddress, &remoteMappedImage))) - { - moduleItem->ImageTimeDateStamp = remoteMappedImage.NtHeaders->FileHeader.TimeDateStamp; - moduleItem->ImageCharacteristics = remoteMappedImage.NtHeaders->FileHeader.Characteristics; - - if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - if ((ULONG_PTR)((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->ImageBase != (ULONG_PTR)moduleItem->BaseAddress) - moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE; - - moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; - } - else if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) - { - if ((ULONG_PTR)((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->ImageBase != (ULONG_PTR)moduleItem->BaseAddress) - moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE; - - moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; - } - - PhUnloadRemoteMappedImage(&remoteMappedImage); - } - } - - if (NT_SUCCESS(PhQueryFullAttributesFileWin32(moduleItem->FileName->Buffer, &networkOpenInfo))) - { - moduleItem->FileLastWriteTime = networkOpenInfo.LastWriteTime; - moduleItem->FileEndOfFile = networkOpenInfo.EndOfFile; - } - else - { - moduleItem->FileEndOfFile.QuadPart = -1; - } - - if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE || - moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE) - { - // See if the file has already been verified; if not, queue for verification. - - moduleItem->VerifyResult = PhVerifyFileCached(moduleItem->FileName, NULL, &moduleItem->VerifySignerName, TRUE); - - if (moduleItem->VerifyResult == VrUnknown) - PhpQueueModuleQuery(moduleProvider, moduleItem); - } - - // Add the module item to the hashtable. - PhAcquireFastLockExclusive(&moduleProvider->ModuleHashtableLock); - PhAddEntryHashtable(moduleProvider->ModuleHashtable, &moduleItem); - PhReleaseFastLockExclusive(&moduleProvider->ModuleHashtableLock); - - // Raise the module added event. - PhInvokeCallback(&moduleProvider->ModuleAddedEvent, moduleItem); - } - else - { - BOOLEAN modified = FALSE; - - if (moduleItem->JustProcessed) - modified = TRUE; - - moduleItem->JustProcessed = FALSE; - - if (modified) - PhInvokeCallback(&moduleProvider->ModuleModifiedEvent, moduleItem); - - PhDereferenceObject(moduleItem); - } - } - - // Free the modules list. - - for (i = 0; i < modules->Count; i++) - { - PPH_MODULE_INFO module = modules->Items[i]; - - PhDereferenceObject(module->Name); - PhDereferenceObject(module->FileName); - PhFree(module); - } - - PhDereferenceObject(modules); - -UpdateExit: - PhInvokeCallback(&moduleProvider->UpdatedEvent, NULL); -} +/* + * Process Hacker - + * module provider + * + * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +typedef struct _PH_MODULE_QUERY_DATA +{ + SLIST_ENTRY ListEntry; + PPH_MODULE_PROVIDER ModuleProvider; + PPH_MODULE_ITEM ModuleItem; + + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; + ULONG ImageFlags; +} PH_MODULE_QUERY_DATA, *PPH_MODULE_QUERY_DATA; + +VOID NTAPI PhpModuleProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID NTAPI PhpModuleItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +BOOLEAN NTAPI PhpModuleHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpModuleHashtableHashFunction( + _In_ PVOID Entry + ); + +PPH_OBJECT_TYPE PhModuleProviderType = NULL; +PPH_OBJECT_TYPE PhModuleItemType = NULL; + +PPH_MODULE_PROVIDER PhCreateModuleProvider( + _In_ HANDLE ProcessId + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + NTSTATUS status; + PPH_MODULE_PROVIDER moduleProvider; + PPH_STRING fileName; + + if (PhBeginInitOnce(&initOnce)) + { + PhModuleProviderType = PhCreateObjectType(L"ModuleProvider", 0, PhpModuleProviderDeleteProcedure); + PhModuleItemType = PhCreateObjectType(L"ModuleItem", 0, PhpModuleItemDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + moduleProvider = PhCreateObject( + PhEmGetObjectSize(EmModuleProviderType, sizeof(PH_MODULE_PROVIDER)), + PhModuleProviderType + ); + memset(moduleProvider, 0, sizeof(PH_MODULE_PROVIDER)); + + moduleProvider->ModuleHashtable = PhCreateHashtable( + sizeof(PPH_MODULE_ITEM), + PhpModuleHashtableEqualFunction, + PhpModuleHashtableHashFunction, + 20 + ); + PhInitializeFastLock(&moduleProvider->ModuleHashtableLock); + + PhInitializeCallback(&moduleProvider->ModuleAddedEvent); + PhInitializeCallback(&moduleProvider->ModuleModifiedEvent); + PhInitializeCallback(&moduleProvider->ModuleRemovedEvent); + PhInitializeCallback(&moduleProvider->UpdatedEvent); + + moduleProvider->ProcessId = ProcessId; + moduleProvider->ProcessHandle = NULL; + moduleProvider->ProcessFileName = NULL; + moduleProvider->PackageFullName = NULL; + moduleProvider->RunStatus = STATUS_SUCCESS; + + // It doesn't matter if we can't get a process handle. + + // Try to get a handle with query information + vm read access. + if (!NT_SUCCESS(status = PhOpenProcess( + &moduleProvider->ProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ProcessId + ))) + { + // Try to get a handle with query limited information + vm read access. + status = PhOpenProcess( + &moduleProvider->ProcessHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + ProcessId + ); + + moduleProvider->RunStatus = status; + } + + if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(ProcessId, &fileName))) + { + PhMoveReference(&moduleProvider->ProcessFileName, PhGetFileName(fileName)); + } + + if (WindowsVersion >= WINDOWS_8 && moduleProvider->ProcessHandle) + { + moduleProvider->PackageFullName = PhGetProcessPackageFullName(moduleProvider->ProcessHandle); + + if (WindowsVersion >= WINDOWS_8_1) + { + BOOLEAN cfguardEnabled; + + if (NT_SUCCESS(PhGetProcessIsCFGuardEnabled(moduleProvider->ProcessHandle, &cfguardEnabled))) + { + moduleProvider->ControlFlowGuardEnabled = cfguardEnabled; + } + } + } + + if (WindowsVersion >= WINDOWS_10 && moduleProvider->ProcessHandle) + { + PROCESS_EXTENDED_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(moduleProvider->ProcessHandle, &basicInfo))) + { + moduleProvider->IsSubsystemProcess = !!basicInfo.IsSubsystemProcess; + } + } + + RtlInitializeSListHead(&moduleProvider->QueryListHead); + + PhEmCallObjectOperation(EmModuleProviderType, moduleProvider, EmObjectCreate); + + return moduleProvider; +} + +VOID PhpModuleProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; + + PhEmCallObjectOperation(EmModuleProviderType, moduleProvider, EmObjectDelete); + + // Dereference all module items (we referenced them + // when we added them to the hashtable). + PhDereferenceAllModuleItems(moduleProvider); + + PhDereferenceObject(moduleProvider->ModuleHashtable); + PhDeleteFastLock(&moduleProvider->ModuleHashtableLock); + PhDeleteCallback(&moduleProvider->ModuleAddedEvent); + PhDeleteCallback(&moduleProvider->ModuleModifiedEvent); + PhDeleteCallback(&moduleProvider->ModuleRemovedEvent); + PhDeleteCallback(&moduleProvider->UpdatedEvent); + + // Destroy all queue items. + { + PSLIST_ENTRY entry; + PPH_MODULE_QUERY_DATA data; + + entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry); + entry = entry->Next; + + if (data->VerifySignerName) PhDereferenceObject(data->VerifySignerName); + PhDereferenceObject(data->ModuleItem); + PhFree(data); + } + } + + if (moduleProvider->ProcessFileName) PhDereferenceObject(moduleProvider->ProcessFileName); + if (moduleProvider->PackageFullName) PhDereferenceObject(moduleProvider->PackageFullName); + if (moduleProvider->ProcessHandle) NtClose(moduleProvider->ProcessHandle); +} + +PPH_MODULE_ITEM PhCreateModuleItem( + VOID + ) +{ + PPH_MODULE_ITEM moduleItem; + + moduleItem = PhCreateObject( + PhEmGetObjectSize(EmModuleItemType, sizeof(PH_MODULE_ITEM)), + PhModuleItemType + ); + memset(moduleItem, 0, sizeof(PH_MODULE_ITEM)); + PhEmCallObjectOperation(EmModuleItemType, moduleItem, EmObjectCreate); + + return moduleItem; +} + +VOID PhpModuleItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_MODULE_ITEM moduleItem = (PPH_MODULE_ITEM)Object; + + PhEmCallObjectOperation(EmModuleItemType, moduleItem, EmObjectDelete); + + if (moduleItem->Name) PhDereferenceObject(moduleItem->Name); + if (moduleItem->FileName) PhDereferenceObject(moduleItem->FileName); + if (moduleItem->VerifySignerName) PhDereferenceObject(moduleItem->VerifySignerName); + PhDeleteImageVersionInfo(&moduleItem->VersionInfo); +} + +BOOLEAN NTAPI PhpModuleHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + return + (*(PPH_MODULE_ITEM *)Entry1)->BaseAddress == + (*(PPH_MODULE_ITEM *)Entry2)->BaseAddress; +} + +ULONG NTAPI PhpModuleHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PVOID baseAddress = (*(PPH_MODULE_ITEM *)Entry)->BaseAddress; + + return PhHashIntPtr((ULONG_PTR)baseAddress); +} + +PPH_MODULE_ITEM PhReferenceModuleItem( + _In_ PPH_MODULE_PROVIDER ModuleProvider, + _In_ PVOID BaseAddress + ) +{ + PH_MODULE_ITEM lookupModuleItem; + PPH_MODULE_ITEM lookupModuleItemPtr = &lookupModuleItem; + PPH_MODULE_ITEM *moduleItemPtr; + PPH_MODULE_ITEM moduleItem; + + lookupModuleItem.BaseAddress = BaseAddress; + + PhAcquireFastLockShared(&ModuleProvider->ModuleHashtableLock); + + moduleItemPtr = (PPH_MODULE_ITEM *)PhFindEntryHashtable( + ModuleProvider->ModuleHashtable, + &lookupModuleItemPtr + ); + + if (moduleItemPtr) + { + moduleItem = *moduleItemPtr; + PhReferenceObject(moduleItem); + } + else + { + moduleItem = NULL; + } + + PhReleaseFastLockShared(&ModuleProvider->ModuleHashtableLock); + + return moduleItem; +} + +VOID PhDereferenceAllModuleItems( + _In_ PPH_MODULE_PROVIDER ModuleProvider + ) +{ + ULONG enumerationKey = 0; + PPH_MODULE_ITEM *moduleItem; + + PhAcquireFastLockExclusive(&ModuleProvider->ModuleHashtableLock); + + while (PhEnumHashtable(ModuleProvider->ModuleHashtable, (PVOID *)&moduleItem, &enumerationKey)) + { + PhDereferenceObject(*moduleItem); + } + + PhReleaseFastLockExclusive(&ModuleProvider->ModuleHashtableLock); +} + +VOID PhpRemoveModuleItem( + _In_ PPH_MODULE_PROVIDER ModuleProvider, + _In_ PPH_MODULE_ITEM ModuleItem + ) +{ + PhRemoveEntryHashtable(ModuleProvider->ModuleHashtable, &ModuleItem); + PhDereferenceObject(ModuleItem); +} + +NTSTATUS PhpModuleQueryWorker( + _In_ PVOID Parameter + ) +{ + PPH_MODULE_QUERY_DATA data = (PPH_MODULE_QUERY_DATA)Parameter; + PH_MAPPED_IMAGE mappedImage = { 0 }; + + if (PhEnableProcessQueryStage2) // HACK (dmex) + { + data->VerifyResult = PhVerifyFileCached( + data->ModuleItem->FileName, + data->ModuleProvider->PackageFullName, + &data->VerifySignerName, + FALSE + ); + } + + { + // HACK HACK HACK (dmex) + // 3rd party CLR's don't set the LDRP_COR_IMAGE flag so we'll check binaries for a CLR section and set the flag ourselves. + // This is needed to detect standard .NET images loaded by .NET core, Mono and other CLR runtimes. + if (NT_SUCCESS(PhLoadMappedImageEx(PhGetString(data->ModuleItem->FileName), NULL, TRUE, &mappedImage))) + { + if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PIMAGE_OPTIONAL_HEADER32 optionalHeader = (PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader; + + if (optionalHeader->NumberOfRvaAndSizes >= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + { + if (optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress) + { + data->ImageFlags |= LDRP_COR_IMAGE; + } + } + } + else if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + PIMAGE_OPTIONAL_HEADER64 optionalHeader = (PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader; + + if (optionalHeader->NumberOfRvaAndSizes >= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + { + if (optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress) + { + data->ImageFlags |= LDRP_COR_IMAGE; + } + } + } + + PhUnloadMappedImage(&mappedImage); + } + } + + RtlInterlockedPushEntrySList(&data->ModuleProvider->QueryListHead, &data->ListEntry); + + PhDereferenceObject(data->ModuleProvider); + + return STATUS_SUCCESS; +} + +VOID PhpQueueModuleQuery( + _In_ PPH_MODULE_PROVIDER ModuleProvider, + _In_ PPH_MODULE_ITEM ModuleItem + ) +{ + PPH_MODULE_QUERY_DATA data; + PH_WORK_QUEUE_ENVIRONMENT environment; + + data = PhAllocate(sizeof(PH_MODULE_QUERY_DATA)); + memset(data, 0, sizeof(PH_MODULE_QUERY_DATA)); + data->ModuleProvider = ModuleProvider; + data->ModuleItem = ModuleItem; + + PhReferenceObject(ModuleProvider); + PhReferenceObject(ModuleItem); + + PhInitializeWorkQueueEnvironment(&environment); + environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; + environment.IoPriority = IoPriorityLow; + environment.PagePriority = MEMORY_PRIORITY_LOW; + + PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpModuleQueryWorker, data, NULL, &environment); +} + +static BOOLEAN NTAPI EnumModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PPH_MODULE_INFO copy; + + copy = PhAllocateCopy(Module, sizeof(PH_MODULE_INFO)); + PhReferenceObject(copy->Name); + PhReferenceObject(copy->FileName); + + PhAddItemList((PPH_LIST)Context, copy); + + return TRUE; +} + +VOID PhModuleProviderUpdate( + _In_ PVOID Object + ) +{ + PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object; + PPH_LIST modules; + ULONG i; + + // If we didn't get a handle when we created the provider, + // abort (unless this is the System process - in that case + // we don't need a handle). + if (!moduleProvider->ProcessHandle && moduleProvider->ProcessId != SYSTEM_PROCESS_ID) + goto UpdateExit; + + modules = PhCreateList(100); + + moduleProvider->RunStatus = PhEnumGenericModules( + moduleProvider->ProcessId, + moduleProvider->ProcessHandle, + PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES, + EnumModulesCallback, + modules + ); + + // Look for removed modules. + { + PPH_LIST modulesToRemove = NULL; + ULONG enumerationKey = 0; + PPH_MODULE_ITEM *moduleItem; + + while (PhEnumHashtable(moduleProvider->ModuleHashtable, (PVOID *)&moduleItem, &enumerationKey)) + { + BOOLEAN found = FALSE; + + // Check if the module still exists. + for (i = 0; i < modules->Count; i++) + { + PPH_MODULE_INFO module = modules->Items[i]; + + if ((*moduleItem)->BaseAddress == module->BaseAddress && + PhEqualString((*moduleItem)->FileName, module->FileName, TRUE)) + { + found = TRUE; + break; + } + } + + if (!found) + { + // Raise the module removed event. + PhInvokeCallback(&moduleProvider->ModuleRemovedEvent, *moduleItem); + + if (!modulesToRemove) + modulesToRemove = PhCreateList(2); + + PhAddItemList(modulesToRemove, *moduleItem); + } + } + + if (modulesToRemove) + { + PhAcquireFastLockExclusive(&moduleProvider->ModuleHashtableLock); + + for (i = 0; i < modulesToRemove->Count; i++) + { + PhpRemoveModuleItem( + moduleProvider, + (PPH_MODULE_ITEM)modulesToRemove->Items[i] + ); + } + + PhReleaseFastLockExclusive(&moduleProvider->ModuleHashtableLock); + PhDereferenceObject(modulesToRemove); + } + } + + // Go through the queued thread query data. + { + PSLIST_ENTRY entry; + PPH_MODULE_QUERY_DATA data; + + entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry); + entry = entry->Next; + + data->ModuleItem->VerifyResult = data->VerifyResult; + data->ModuleItem->VerifySignerName = data->VerifySignerName; + data->ModuleItem->Flags |= data->ImageFlags; + data->ModuleItem->JustProcessed = TRUE; + + PhDereferenceObject(data->ModuleItem); + PhFree(data); + } + } + + // Look for new modules. + for (i = 0; i < modules->Count; i++) + { + PPH_MODULE_INFO module = modules->Items[i]; + PPH_MODULE_ITEM moduleItem; + + moduleItem = PhReferenceModuleItem(moduleProvider, module->BaseAddress); + + if (!moduleItem) + { + FILE_NETWORK_OPEN_INFORMATION networkOpenInfo; + + PhReferenceObject(module->Name); + PhReferenceObject(module->FileName); + + moduleItem = PhCreateModuleItem(); + moduleItem->BaseAddress = module->BaseAddress; + moduleItem->EntryPoint = module->EntryPoint; + moduleItem->Size = module->Size; + moduleItem->Flags = module->Flags; + moduleItem->Type = module->Type; + moduleItem->LoadReason = module->LoadReason; + moduleItem->LoadCount = module->LoadCount; + moduleItem->LoadTime = module->LoadTime; + moduleItem->Name = module->Name; + moduleItem->FileName = module->FileName; + moduleItem->ParentBaseAddress = module->ParentBaseAddress; + + PhInitializeImageVersionInfo(&moduleItem->VersionInfo, moduleItem->FileName->Buffer); + + if (moduleProvider->IsSubsystemProcess) + { + // HACK: Update the module type. (TODO: Move into PhEnumGenericModules) (dmex) + moduleItem->Type = PH_MODULE_TYPE_ELF_MAPPED_IMAGE; + } + else + { + // Fix up the load count. If this is not an ordinary DLL or kernel module, set the load count to 0. + if (moduleItem->Type != PH_MODULE_TYPE_MODULE && + moduleItem->Type != PH_MODULE_TYPE_WOW64_MODULE && + moduleItem->Type != PH_MODULE_TYPE_KERNEL_MODULE) + { + moduleItem->LoadCount = 0; + } + } + + if (!moduleProvider->HaveFirst) + { + if (WindowsVersion < WINDOWS_10) + { + moduleItem->IsFirst = i == 0; + moduleProvider->HaveFirst = TRUE; + } + else + { + // Windows loads the PE image first and WSL loads the ELF image last (dmex) + if (moduleProvider->ProcessFileName && PhEqualString(moduleProvider->ProcessFileName, moduleItem->FileName, FALSE)) + { + moduleItem->IsFirst = TRUE; + moduleProvider->HaveFirst = TRUE; + } + } + } + + if (moduleItem->Type == PH_MODULE_TYPE_MODULE || + moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || + moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE || + moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) + { + PH_REMOTE_MAPPED_IMAGE remoteMappedImage; + PPH_READ_VIRTUAL_MEMORY_CALLBACK readVirtualMemoryCallback; + + if (moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE) + readVirtualMemoryCallback = KphReadVirtualMemoryUnsafe; + else + readVirtualMemoryCallback = NtReadVirtualMemory; + + // Note: + // On Windows 7 the LDRP_IMAGE_NOT_AT_BASE flag doesn't appear to be used + // anymore. Instead we'll check ImageBase in the image headers. We read this in + // from the process' memory because: + // + // 1. It (should be) faster than opening the file and mapping it in, and + // 2. It contains the correct original image base relocated by ASLR, if present. + + moduleItem->Flags &= ~LDRP_IMAGE_NOT_AT_BASE; + + if (NT_SUCCESS(PhLoadRemoteMappedImageEx(moduleProvider->ProcessHandle, moduleItem->BaseAddress, readVirtualMemoryCallback, &remoteMappedImage))) + { + ULONG_PTR imageBase = 0; + ULONG entryPoint = 0; + + moduleItem->ImageTimeDateStamp = remoteMappedImage.NtHeaders->FileHeader.TimeDateStamp; + moduleItem->ImageCharacteristics = remoteMappedImage.NtHeaders->FileHeader.Characteristics; + + if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PIMAGE_OPTIONAL_HEADER32 optionalHeader = (PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader; + + imageBase = (ULONG_PTR)optionalHeader->ImageBase; + entryPoint = optionalHeader->AddressOfEntryPoint; + moduleItem->ImageDllCharacteristics = optionalHeader->DllCharacteristics; + } + else if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + PIMAGE_OPTIONAL_HEADER64 optionalHeader = (PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader; + + imageBase = (ULONG_PTR)optionalHeader->ImageBase; + entryPoint = optionalHeader->AddressOfEntryPoint; + moduleItem->ImageDllCharacteristics = optionalHeader->DllCharacteristics; + } + + if (imageBase != (ULONG_PTR)moduleItem->BaseAddress) + moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE; + + if (entryPoint != 0) + moduleItem->EntryPoint = PTR_ADD_OFFSET(moduleItem->BaseAddress, entryPoint); + + PhUnloadRemoteMappedImage(&remoteMappedImage); + } + } + + // remove CF Guard flag if CFG mitigation is not enabled for the process + if (!moduleProvider->ControlFlowGuardEnabled) + moduleItem->ImageDllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_GUARD_CF; + + if (NT_SUCCESS(PhQueryFullAttributesFileWin32(moduleItem->FileName->Buffer, &networkOpenInfo))) + { + moduleItem->FileLastWriteTime = networkOpenInfo.LastWriteTime; + moduleItem->FileEndOfFile = networkOpenInfo.EndOfFile; + } + else + { + moduleItem->FileEndOfFile.QuadPart = -1; + } + + if (moduleItem->Type != PH_MODULE_TYPE_ELF_MAPPED_IMAGE) + { + // See if the file has already been verified; if not, queue for verification. + moduleItem->VerifyResult = PhVerifyFileCached(moduleItem->FileName, NULL, &moduleItem->VerifySignerName, TRUE); + + //if (moduleItem->VerifyResult == VrUnknown) // (dmex) + PhpQueueModuleQuery(moduleProvider, moduleItem); + } + + // Add the module item to the hashtable. + PhAcquireFastLockExclusive(&moduleProvider->ModuleHashtableLock); + PhAddEntryHashtable(moduleProvider->ModuleHashtable, &moduleItem); + PhReleaseFastLockExclusive(&moduleProvider->ModuleHashtableLock); + + // Raise the module added event. + PhInvokeCallback(&moduleProvider->ModuleAddedEvent, moduleItem); + } + else + { + BOOLEAN modified = FALSE; + + if (moduleItem->JustProcessed) + modified = TRUE; + + moduleItem->JustProcessed = FALSE; + + if (moduleItem->LoadCount != module->LoadCount) + { + moduleItem->LoadCount = module->LoadCount; + modified = TRUE; + } + + if (modified) + PhInvokeCallback(&moduleProvider->ModuleModifiedEvent, moduleItem); + + PhDereferenceObject(moduleItem); + } + } + + if (!moduleProvider->HaveFirst) // Some processes don't have a primary image (e.g. System/Registry/Secure System) (dmex) + moduleProvider->HaveFirst = TRUE; + + // Free the modules list. + + for (i = 0; i < modules->Count; i++) + { + PPH_MODULE_INFO module = modules->Items[i]; + + PhDereferenceObject(module->Name); + PhDereferenceObject(module->FileName); + PhFree(module); + } + + PhDereferenceObject(modules); + +UpdateExit: + PhInvokeCallback(&moduleProvider->UpdatedEvent, NULL); +} diff --git a/ProcessHacker/mtgndlg.c b/ProcessHacker/mtgndlg.c index 1c9e6249de3e..d874475dedad 100644 --- a/ProcessHacker/mtgndlg.c +++ b/ProcessHacker/mtgndlg.c @@ -3,6 +3,7 @@ * process mitigation policy details * * Copyright (C) 2016 wj32 + * Copyright (C) 2016-2018 dmex * * This file is part of Process Hacker. * @@ -21,17 +22,20 @@ */ #include - +#include #include typedef struct _MITIGATION_POLICY_ENTRY { + BOOLEAN NonStandard; PPH_STRING ShortDescription; PPH_STRING LongDescription; } MITIGATION_POLICY_ENTRY, *PMITIGATION_POLICY_ENTRY; typedef struct _MITIGATION_POLICY_CONTEXT { + HWND ListViewHandle; + PPS_SYSTEM_DLL_INIT_BLOCK SystemDllInitBlock; MITIGATION_POLICY_ENTRY Entries[MaxProcessMitigationPolicy]; } MITIGATION_POLICY_CONTEXT, *PMITIGATION_POLICY_CONTEXT; @@ -44,42 +48,83 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( VOID PhShowProcessMitigationPolicyDialog( _In_ HWND ParentWindowHandle, - _In_ struct _PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION *Information + _In_ HANDLE ProcessId ) { + NTSTATUS status; + HANDLE processHandle; + PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION information; MITIGATION_POLICY_CONTEXT context; PROCESS_MITIGATION_POLICY policy; PPH_STRING shortDescription; PPH_STRING longDescription; + memset(&context, 0, sizeof(MITIGATION_POLICY_CONTEXT)); memset(&context.Entries, 0, sizeof(context.Entries)); - for (policy = 0; policy < MaxProcessMitigationPolicy; policy++) + // Try to get a handle with query information + vm read access. + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ProcessId + ))) + { + // Try to get a handle with query information. + status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION, + ProcessId + ); + } + + if (NT_SUCCESS(status)) { - if (Information->Pointers[policy] && PhDescribeProcessMitigationPolicy( - policy, - Information->Pointers[policy], - &shortDescription, - &longDescription - )) + PPS_SYSTEM_DLL_INIT_BLOCK dllInitBlock; + + if (NT_SUCCESS(PhGetProcessSystemDllInitBlock(processHandle, &dllInitBlock))) { - context.Entries[policy].ShortDescription = shortDescription; - context.Entries[policy].LongDescription = longDescription; + context.SystemDllInitBlock = dllInitBlock; } - } - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_MITIGATION), - ParentWindowHandle, - PhpProcessMitigationPolicyDlgProc, - (LPARAM)&context - ); + if (NT_SUCCESS(PhGetProcessMitigationPolicy(processHandle, &information))) + { + for (policy = 0; policy < MaxProcessMitigationPolicy; policy++) + { + if (information.Pointers[policy] && PhDescribeProcessMitigationPolicy( + policy, + information.Pointers[policy], + &shortDescription, + &longDescription + )) + { + context.Entries[policy].ShortDescription = shortDescription; + context.Entries[policy].LongDescription = longDescription; + } + } - for (policy = 0; policy < MaxProcessMitigationPolicy; policy++) + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_MITIGATION), + ParentWindowHandle, + PhpProcessMitigationPolicyDlgProc, + (LPARAM)&context + ); + + for (policy = 0; policy < MaxProcessMitigationPolicy; policy++) + { + PhClearReference(&context.Entries[policy].ShortDescription); + PhClearReference(&context.Entries[policy].LongDescription); + } + } + + if (context.SystemDllInitBlock) + PhFree(context.SystemDllInitBlock); + + NtClose(processHandle); + } + else { - PhClearReference(&context.Entries[policy].ShortDescription); - PhClearReference(&context.Entries[policy].LongDescription); + PhShowStatus(ParentWindowHandle, L"Unable to open the process.", status, 0); } } @@ -90,16 +135,35 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( _In_ LPARAM lParam ) { + PMITIGATION_POLICY_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PMITIGATION_POLICY_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (context == NULL) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: { - PMITIGATION_POLICY_CONTEXT context = (PMITIGATION_POLICY_CONTEXT)lParam; HWND lvHandle; PROCESS_MITIGATION_POLICY policy; PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); PhSetListViewStyle(lvHandle, FALSE, TRUE); PhSetControlTheme(lvHandle, L"explorer"); PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 350, L"Policy"); @@ -115,19 +179,106 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); } + if (context->SystemDllInitBlock && RTL_CONTAINS_FIELD(context->SystemDllInitBlock, context->SystemDllInitBlock->Size, MitigationOptionsMap)) + { + // TODO: Windows doesn't propagate these flags into the MitigationOptionsMap array. (dmex) + //if (context->SystemDllInitBlock->MitigationOptionsMap.Map[0] & PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_ALWAYS_ON) + //{ + // PMITIGATION_POLICY_ENTRY entry; + // + // entry = PhAllocate(sizeof(MITIGATION_POLICY_ENTRY)); + // entry->NonStandard = TRUE; + // entry->ShortDescription = PhCreateString(L"Loader Integrity"); + // entry->LongDescription = PhCreateString(L"OS signing levels for dependent module loads are enabled."); + // + // PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); + //} + + //if (context->SystemDllInitBlock->MitigationOptionsMap.Map[0] & PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_ALWAYS_ON) + //{ + // PMITIGATION_POLICY_ENTRY entry; + // + // entry = PhAllocate(sizeof(MITIGATION_POLICY_ENTRY)); + // entry->NonStandard = TRUE; + // entry->ShortDescription = PhCreateString(L"Module Tampering"); + // entry->LongDescription = PhCreateString(L"Module Tampering protection is enabled."); + // + // PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); + //} + + if (context->SystemDllInitBlock->MitigationOptionsMap.Map[0] & PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON) + { + PMITIGATION_POLICY_ENTRY entry; + + entry = PhAllocate(sizeof(MITIGATION_POLICY_ENTRY)); + entry->NonStandard = TRUE; + entry->ShortDescription = PhCreateString(L"Indirect branch prediction"); + entry->LongDescription = PhCreateString(L"Protects against sibling hardware threads (hyperthreads) from interfering with indirect branch predictions."); + + PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); + } + + if (context->SystemDllInitBlock->MitigationOptionsMap.Map[0] & PROCESS_CREATION_MITIGATION_POLICY2_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY_ALWAYS_ON) + { + PMITIGATION_POLICY_ENTRY entry; + + entry = PhAllocate(sizeof(MITIGATION_POLICY_ENTRY)); + entry->NonStandard = TRUE; + entry->ShortDescription = PhCreateString(L"Dynamic code (downgrade)"); + entry->LongDescription = PhCreateString(L"Allows a broker to downgrade the dynamic code policy for a process."); + + PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); + } + + if (context->SystemDllInitBlock->MitigationOptionsMap.Map[0] & PROCESS_CREATION_MITIGATION_POLICY2_SPECULATIVE_STORE_BYPASS_DISABLE_ALWAYS_ON) + { + PMITIGATION_POLICY_ENTRY entry; + + entry = PhAllocate(sizeof(MITIGATION_POLICY_ENTRY)); + entry->NonStandard = TRUE; + entry->ShortDescription = PhCreateString(L"Speculative store bypass"); + entry->LongDescription = PhCreateString(L"Disables spectre mitigations for the process."); + + PhAddListViewItem(lvHandle, MAXINT, entry->ShortDescription->Buffer, entry); + } + } + ExtendedListView_SortItems(lvHandle); + ExtendedListView_SetColumnWidth(lvHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); ListView_SetItemState(lvHandle, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)lvHandle, TRUE); + + PhSetDialogFocus(hwndDlg, lvHandle); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: { - // Nothing + ULONG index = -1; + + while ((index = PhFindListViewItemByFlags( + context->ListViewHandle, + index, + LVNI_ALL + )) != -1) + { + PMITIGATION_POLICY_ENTRY entry; + + if (PhGetListViewItemParam(context->ListViewHandle, index, &entry)) + { + if (entry->NonStandard) + { + PhClearReference(&entry->ShortDescription); + PhClearReference(&entry->LongDescription); + PhFree(entry); + } + } + } } break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: @@ -154,7 +305,7 @@ INT_PTR CALLBACK PhpProcessMitigationPolicyDlgProc( else description = L""; - SetDlgItemText(hwndDlg, IDC_DESCRIPTION, description); + PhSetDialogItemText(hwndDlg, IDC_DESCRIPTION, description); } } break; diff --git a/ProcessHacker/mwpgnet.c b/ProcessHacker/mwpgnet.c index c8fa1d5cd18f..2bbdc0504c26 100644 --- a/ProcessHacker/mwpgnet.c +++ b/ProcessHacker/mwpgnet.c @@ -3,6 +3,7 @@ * Main window: Network tab * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -21,6 +22,7 @@ */ #include +#include #include #include @@ -38,6 +40,7 @@ PPH_MAIN_TAB_PAGE PhMwpNetworkPage; HWND PhMwpNetworkTreeNewHandle; +PH_PROVIDER_EVENT_QUEUE PhMwpNetworkEventQueue; static PH_CALLBACK_REGISTRATION NetworkItemAddedRegistration; static PH_CALLBACK_REGISTRATION NetworkItemModifiedRegistration; @@ -46,7 +49,7 @@ static PH_CALLBACK_REGISTRATION NetworkItemsUpdatedRegistration; static BOOLEAN NetworkFirstTime = TRUE; static BOOLEAN NetworkTreeListLoaded = FALSE; -static BOOLEAN NetworkNeedsRedraw = FALSE; +static PPH_TN_FILTER_ENTRY NetworkFilterEntry = NULL; BOOLEAN PhMwpNetworkPageCallback( _In_ struct _PH_MAIN_TAB_PAGE *Page, @@ -61,26 +64,28 @@ BOOLEAN PhMwpNetworkPageCallback( { PhMwpNetworkPage = Page; + PhInitializeProviderEventQueue(&PhMwpNetworkEventQueue, 100); + PhRegisterCallback( - &PhNetworkItemAddedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderAddedEvent), PhMwpNetworkItemAddedHandler, NULL, &NetworkItemAddedRegistration ); PhRegisterCallback( - &PhNetworkItemModifiedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderModifiedEvent), PhMwpNetworkItemModifiedHandler, NULL, &NetworkItemModifiedRegistration ); PhRegisterCallback( - &PhNetworkItemRemovedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderRemovedEvent), PhMwpNetworkItemRemovedHandler, NULL, &NetworkItemRemovedRegistration ); PhRegisterCallback( - &PhNetworkItemsUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdatedEvent), PhMwpNetworkItemsUpdatedHandler, NULL, &NetworkItemsUpdatedRegistration @@ -114,9 +119,23 @@ BOOLEAN PhMwpNetworkPageCallback( } } break; + case MainTabPageInitializeSectionMenuItems: + { + PPH_MAIN_TAB_PAGE_MENU_INFORMATION menuInfo = Parameter1; + PPH_EMENU menu = menuInfo->Menu; + ULONG startIndex = menuInfo->StartIndex; + PPH_EMENU_ITEM menuItem; + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDEWAITINGCONNECTIONS, L"&Hide waiting connections", NULL, NULL), startIndex); + + if (NetworkFilterEntry && (menuItem = PhFindEMenuItem(menu, 0, NULL, ID_VIEW_HIDEWAITINGCONNECTIONS))) + menuItem->Flags |= PH_EMENU_CHECKED; + } + return TRUE; case MainTabPageLoadSettings: { - // Nothing + if (PhGetIntegerSetting(L"HideWaitingConnections")) + NetworkFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportNetworkTreeList(), PhMwpNetworkTreeFilter, NULL); } return TRUE; case MainTabPageSaveSettings: @@ -136,7 +155,7 @@ BOOLEAN PhMwpNetworkPageCallback( { HFONT font = (HFONT)Parameter1; - SendMessage(PhMwpNetworkTreeNewHandle, WM_SETFONT, (WPARAM)font, TRUE); + SetWindowFont(PhMwpNetworkTreeNewHandle, font, TRUE); } break; case MainTabPageUpdateAutomaticallyChanged: @@ -164,34 +183,35 @@ VOID PhMwpNeedNetworkTreeList( } } -BOOLEAN PhMwpCurrentUserNetworkTreeFilter( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context +VOID PhMwpToggleNetworkWaitingConnectionTreeFilter( + VOID ) { - PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)Node; - PPH_PROCESS_NODE processNode; - - processNode = PhFindProcessNode(networkNode->NetworkItem->ProcessId); + if (NetworkFilterEntry) + { + PhRemoveTreeNewFilter(PhGetFilterSupportNetworkTreeList(), NetworkFilterEntry); + NetworkFilterEntry = NULL; + } + else + { + NetworkFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportNetworkTreeList(), PhMwpNetworkTreeFilter, NULL); + } - if (processNode) - return PhMwpCurrentUserProcessTreeFilter(&processNode->Node, NULL); + PhApplyTreeNewFilters(PhGetFilterSupportNetworkTreeList()); - return TRUE; + PhSetIntegerSetting(L"HideWaitingConnections", !!NetworkFilterEntry); } -BOOLEAN PhMwpSignedNetworkTreeFilter( +BOOLEAN PhMwpNetworkTreeFilter( _In_ PPH_TREENEW_NODE Node, _In_opt_ PVOID Context ) { PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)Node; - PPH_PROCESS_NODE processNode; - - processNode = PhFindProcessNode(networkNode->NetworkItem->ProcessId); - if (processNode) - return PhMwpSignedProcessTreeFilter(&processNode->Node, NULL); + // Waiting connections don't have a ProcessId. (dmex) + if (!networkNode->NetworkItem->ProcessId) + return FALSE; return TRUE; } @@ -221,12 +241,6 @@ VOID PhMwpInitializeNetworkMenu( PhEnableEMenuItem(Menu, ID_NETWORK_COPY, TRUE); } - if (WindowsVersion >= WINDOWS_VISTA) - { - if (item = PhFindEMenuItem(Menu, 0, NULL, ID_NETWORK_VIEWSTACK)) - PhDestroyEMenuItem(item); - } - // Go to Service if (NumberOfNetworkItems != 1 || !NetworkItems[0]->OwnerName) { @@ -315,52 +329,84 @@ VOID PhShowNetworkContextMenu( PhFree(networkItems); } -VOID PhMwpOnNetworkItemAdded( - _In_ ULONG RunId, - _In_ _Assume_refs_(1) PPH_NETWORK_ITEM NetworkItem +VOID NTAPI PhMwpNetworkItemAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ) { - PPH_NETWORK_NODE networkNode; + PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; - if (!NetworkNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpNetworkTreeNewHandle, FALSE); - NetworkNeedsRedraw = TRUE; - } - - networkNode = PhAddNetworkNode(NetworkItem, RunId); - PhDereferenceObject(NetworkItem); + PhReferenceObject(networkItem); + PhPushProviderEventQueue(&PhMwpNetworkEventQueue, ProviderAddedEvent, Parameter, PhGetRunIdProvider(&PhMwpNetworkProviderRegistration)); } -VOID PhMwpOnNetworkItemModified( - _In_ PPH_NETWORK_ITEM NetworkItem +VOID NTAPI PhMwpNetworkItemModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ) { - PhUpdateNetworkNode(PhFindNetworkNode(NetworkItem)); + PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; + + PhPushProviderEventQueue(&PhMwpNetworkEventQueue, ProviderModifiedEvent, Parameter, PhGetRunIdProvider(&PhMwpNetworkProviderRegistration)); } -VOID PhMwpOnNetworkItemRemoved( - _In_ PPH_NETWORK_ITEM NetworkItem +VOID NTAPI PhMwpNetworkItemRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ) { - if (!NetworkNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpNetworkTreeNewHandle, FALSE); - NetworkNeedsRedraw = TRUE; - } + PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Parameter; - PhRemoveNetworkNode(PhFindNetworkNode(NetworkItem)); + PhPushProviderEventQueue(&PhMwpNetworkEventQueue, ProviderRemovedEvent, Parameter, PhGetRunIdProvider(&PhMwpNetworkProviderRegistration)); +} + +VOID NTAPI PhMwpNetworkItemsUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ProcessHacker_Invoke(PhMainWndHandle, PhMwpOnNetworkItemsUpdated, PhGetRunIdProvider(&PhMwpNetworkProviderRegistration)); } VOID PhMwpOnNetworkItemsUpdated( - VOID + _In_ ULONG RunId ) { - PhTickNetworkNodes(); + PPH_PROVIDER_EVENT events; + ULONG count; + ULONG i; - if (NetworkNeedsRedraw) + events = PhFlushProviderEventQueue(&PhMwpNetworkEventQueue, RunId, &count); + + if (events) { - TreeNew_SetRedraw(PhMwpNetworkTreeNewHandle, TRUE); - NetworkNeedsRedraw = FALSE; + TreeNew_SetRedraw(PhMwpNetworkTreeNewHandle, FALSE); + + for (i = 0; i < count; i++) + { + PH_PROVIDER_EVENT_TYPE type = PH_PROVIDER_EVENT_TYPE(events[i]); + PPH_NETWORK_ITEM networkItem = PH_PROVIDER_EVENT_OBJECT(events[i]); + + switch (type) + { + case ProviderAddedEvent: + PhAddNetworkNode(networkItem, events[i].RunId); + PhDereferenceObject(networkItem); + break; + case ProviderModifiedEvent: + PhUpdateNetworkNode(PhFindNetworkNode(networkItem)); + break; + case ProviderRemovedEvent: + PhRemoveNetworkNode(PhFindNetworkNode(networkItem)); + break; + } + } + + PhFree(events); } + + PhTickNetworkNodes(); + + if (count != 0) + TreeNew_SetRedraw(PhMwpNetworkTreeNewHandle, TRUE); } diff --git a/ProcessHacker/mwpgproc.c b/ProcessHacker/mwpgproc.c index e9d633637bc9..57a482bfa05f 100644 --- a/ProcessHacker/mwpgproc.c +++ b/ProcessHacker/mwpgproc.c @@ -3,6 +3,7 @@ * Main window: Processes tab * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2019 dmex * * This file is part of Process Hacker. * @@ -28,8 +29,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -42,6 +45,7 @@ PPH_MAIN_TAB_PAGE PhMwpProcessesPage; HWND PhMwpProcessTreeNewHandle; HWND PhMwpSelectedProcessWindowHandle; BOOLEAN PhMwpSelectedProcessVirtualizationEnabled; +PH_PROVIDER_EVENT_QUEUE PhMwpProcessEventQueue; static PH_CALLBACK_REGISTRATION ProcessAddedRegistration; static PH_CALLBACK_REGISTRATION ProcessModifiedRegistration; @@ -49,9 +53,7 @@ static PH_CALLBACK_REGISTRATION ProcessRemovedRegistration; static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; static ULONG NeedsSelectPid = 0; -static BOOLEAN ProcessesNeedsRedraw = FALSE; static PPH_PROCESS_NODE ProcessToScrollTo = NULL; - static PPH_TN_FILTER_ENTRY CurrentUserFilterEntry = NULL; static PPH_TN_FILTER_ENTRY SignedFilterEntry = NULL; @@ -68,26 +70,28 @@ BOOLEAN PhMwpProcessesPageCallback( { PhMwpProcessesPage = Page; + PhInitializeProviderEventQueue(&PhMwpProcessEventQueue, 100); + PhRegisterCallback( - &PhProcessAddedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderAddedEvent), PhMwpProcessAddedHandler, NULL, &ProcessAddedRegistration ); PhRegisterCallback( - &PhProcessModifiedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderModifiedEvent), PhMwpProcessModifiedHandler, NULL, &ProcessModifiedRegistration ); PhRegisterCallback( - &PhProcessRemovedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderRemovedEvent), PhMwpProcessRemovedHandler, NULL, &ProcessRemovedRegistration ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), PhMwpProcessesUpdatedHandler, NULL, &ProcessesUpdatedRegistration @@ -107,11 +111,12 @@ BOOLEAN PhMwpProcessesPageCallback( PPH_EMENU menu = menuInfo->Menu; ULONG startIndex = menuInfo->StartIndex; PPH_EMENU_ITEM menuItem; + PPH_EMENU_ITEM columnSetMenuItem; - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDEPROCESSESFROMOTHERUSERS, L"Hide processes from other users", NULL, NULL), startIndex); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDESIGNEDPROCESSES, L"Hide signed processes", NULL, NULL), startIndex + 1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SCROLLTONEWPROCESSES, L"Scroll to new processes", NULL, NULL), startIndex + 2); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SHOWCPUBELOW001, L"Show CPU below 0.01", NULL, NULL), startIndex + 3); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDEPROCESSESFROMOTHERUSERS, L"&Hide processes from other users", NULL, NULL), startIndex); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDESIGNEDPROCESSES, L"Hide si&gned processes", NULL, NULL), startIndex + 1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SCROLLTONEWPROCESSES, L"Scrol&l to new processes", NULL, NULL), startIndex + 2); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SHOWCPUBELOW001, L"Show CPU &below 0.01", NULL, NULL), startIndex + 3); if (CurrentUserFilterEntry && (menuItem = PhFindEMenuItem(menu, 0, NULL, ID_VIEW_HIDEPROCESSESFROMOTHERUSERS))) menuItem->Flags |= PH_EMENU_CHECKED; @@ -122,7 +127,7 @@ BOOLEAN PhMwpProcessesPageCallback( if (menuItem = PhFindEMenuItem(menu, 0, NULL, ID_VIEW_SHOWCPUBELOW001)) { - if (WindowsVersion >= WINDOWS_7 && PhEnableCycleCpuUsage) + if (PhEnableCycleCpuUsage) { if (PhCsShowCpuBelow001) menuItem->Flags |= PH_EMENU_CHECKED; @@ -132,6 +137,38 @@ BOOLEAN PhMwpProcessesPageCallback( menuItem->Flags |= PH_EMENU_DISABLED; } } + + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), startIndex + 4); + PhInsertEMenuItem(menu, menuItem = PhCreateEMenuItem(0, ID_VIEW_ORGANIZECOLUMNSETS, L"Organi&ze column sets...", NULL, NULL), startIndex + 5); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_SAVECOLUMNSET, L"Sa&ve column set...", NULL, NULL), startIndex + 6); + PhInsertEMenuItem(menu, columnSetMenuItem = PhCreateEMenuItem(0, 0, L"Loa&d column set", NULL, NULL), startIndex + 7); + + // Add column set sub menu entries. + { + ULONG index; + PPH_LIST columnSetList; + + columnSetList = PhInitializeColumnSetList(L"ProcessTreeColumnSetConfig"); + + if (!columnSetList->Count) + { + menuItem->Flags |= PH_EMENU_DISABLED; + columnSetMenuItem->Flags |= PH_EMENU_DISABLED; + } + else + { + for (index = 0; index < columnSetList->Count; index++) + { + PPH_COLUMN_SET_ENTRY entry = columnSetList->Items[index]; + + menuItem = PhCreateEMenuItem(PH_EMENU_TEXT_OWNED, ID_VIEW_LOADCOLUMNSET, + PhAllocateCopy(entry->Name->Buffer, entry->Name->Length + sizeof(UNICODE_NULL)), NULL, NULL); + PhInsertEMenuItem(columnSetMenuItem, menuItem, ULONG_MAX); + } + } + + PhDeleteColumnSetList(columnSetList); + } } return TRUE; case MainTabPageLoadSettings: @@ -160,7 +197,7 @@ BOOLEAN PhMwpProcessesPageCallback( { HFONT font = (HFONT)Parameter1; - SendMessage(PhMwpProcessTreeNewHandle, WM_SETFONT, (WPARAM)font, TRUE); + SetWindowFont(PhMwpProcessTreeNewHandle, font, TRUE); } break; case MainTabPageUpdateAutomaticallyChanged: @@ -219,13 +256,10 @@ BOOLEAN PhMwpCurrentUserProcessTreeFilter( { PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)Node; - if (!processNode->ProcessItem->UserName) - return FALSE; - - if (!PhCurrentUserName) + if (!processNode->ProcessItem->Sid) return FALSE; - if (!PhEqualString(processNode->ProcessItem->UserName, PhCurrentUserName, TRUE)) + if (!RtlEqualSid(processNode->ProcessItem->Sid, PhGetOwnTokenAttributes().TokenSid)) return FALSE; return TRUE; @@ -239,11 +273,13 @@ VOID PhMwpToggleSignedProcessTreeFilter( { if (!PhEnableProcessQueryStage2) { - PhShowInformation( + PhShowInformation2( PhMainWndHandle, + NULL, L"This filter cannot function because digital signature checking is not enabled. " - L"Enable it in Options > Advanced and restart Process Hacker." + L"Enable it in Options > General and restart Process Hacker." ); + return; } SignedFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportProcessTreeList(), PhMwpSignedProcessTreeFilter, NULL); @@ -350,40 +386,40 @@ VOID PhMwpSetProcessMenuPriorityChecks( { HANDLE processHandle; PROCESS_PRIORITY_CLASS priorityClass = { 0 }; - IO_PRIORITY_HINT ioPriority = -1; - ULONG pagePriority = -1; + IO_PRIORITY_HINT ioPriority = ULONG_MAX; + ULONG pagePriority = ULONG_MAX; ULONG id = 0; if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, ProcessId ))) { if (SetPriority) { - NtQueryInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS), NULL); + PhGetProcessPriority(processHandle, &priorityClass); } - if (SetIoPriority && WindowsVersion >= WINDOWS_VISTA) + if (SetIoPriority) { if (!NT_SUCCESS(PhGetProcessIoPriority( processHandle, &ioPriority ))) { - ioPriority = -1; + ioPriority = ULONG_MAX; } } - if (SetPagePriority && WindowsVersion >= WINDOWS_VISTA) + if (SetPagePriority) { if (!NT_SUCCESS(PhGetProcessPagePriority( processHandle, &pagePriority ))) { - pagePriority = -1; + pagePriority = ULONG_MAX; } } @@ -422,7 +458,7 @@ VOID PhMwpSetProcessMenuPriorityChecks( } } - if (SetIoPriority && ioPriority != -1) + if (SetIoPriority && ioPriority != ULONG_MAX) { id = 0; @@ -450,7 +486,7 @@ VOID PhMwpSetProcessMenuPriorityChecks( } } - if (SetPagePriority && pagePriority != -1) + if (SetPagePriority && pagePriority != ULONG_MAX) { id = 0; @@ -498,14 +534,39 @@ VOID PhMwpInitializeProcessMenu( { // All menu items are enabled by default. - // If the user selected a fake process, disable all but - // a few menu items. - if (PH_IS_FAKE_PROCESS_ID(Processes[0]->ProcessId)) + // If the user selected a fake process, disable all but a few menu items. + if ( + PH_IS_FAKE_PROCESS_ID(Processes[0]->ProcessId) || + Processes[0]->ProcessId == SYSTEM_IDLE_PROCESS_ID + //Processes[0]->ProcessId == SYSTEM_PROCESS_ID // (dmex) + ) { PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); PhEnableEMenuItem(Menu, ID_PROCESS_PROPERTIES, TRUE); PhEnableEMenuItem(Menu, ID_PROCESS_SEARCHONLINE, TRUE); } + + if (PhIsNullOrEmptyString(Processes[0]->FileName) || !PhDoesFileExistsWin32(PhGetString(Processes[0]->FileName))) + { + PhEnableEMenuItem(Menu, ID_PROCESS_OPENFILELOCATION, FALSE); + } + + // Critical + if (Processes[0]->QueryHandle) + { + BOOLEAN breakOnTermination; + + if (NT_SUCCESS(PhGetProcessBreakOnTermination( + Processes[0]->QueryHandle, + &breakOnTermination + ))) + { + if (breakOnTermination) + { + PhSetFlagsEMenuItem(Menu, ID_MISCELLANEOUS_SETCRITICAL, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + } + } + } } else { @@ -544,17 +605,6 @@ VOID PhMwpInitializeProcessMenu( } } - // Remove irrelevant menu items. - if (WindowsVersion < WINDOWS_VISTA) - { - // Remove I/O priority. - if (item = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, L"I/O Priority", 0)) - PhDestroyEMenuItem(item); - // Remove page priority. - if (item = PhFindEMenuItem(Menu, PH_EMENU_FIND_DESCEND, L"Page Priority", 0)) - PhDestroyEMenuItem(item); - } - // Suspend/Resume if (NumberOfProcesses == 1) { @@ -574,19 +624,14 @@ VOID PhMwpInitializeProcessMenu( // Virtualization if (NumberOfProcesses == 1) { - HANDLE processHandle; HANDLE tokenHandle; BOOLEAN allowed = FALSE; BOOLEAN enabled = FALSE; - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - ProcessQueryAccess, - Processes[0]->ProcessId - ))) + if (Processes[0]->QueryHandle) { if (NT_SUCCESS(PhOpenProcessToken( - processHandle, + Processes[0]->QueryHandle, TOKEN_QUERY, &tokenHandle ))) @@ -597,8 +642,6 @@ VOID PhMwpInitializeProcessMenu( NtClose(tokenHandle); } - - NtClose(processHandle); } if (!allowed) @@ -642,13 +685,6 @@ VOID PhMwpInitializeProcessMenu( item->Flags |= PH_EMENU_DISABLED; } } - - // Remove irrelevant menu items (continued) - if (!WINDOWS_HAS_UAC) - { - if (item = PhFindEMenuItem(Menu, 0, NULL, ID_PROCESS_VIRTUALIZATION)) - PhDestroyEMenuItem(item); - } } VOID PhShowProcessContextMenu( @@ -720,12 +756,7 @@ VOID NTAPI PhMwpProcessAddedHandler( // Reference the process item so it doesn't get deleted before // we handle the event in the main thread. PhReferenceObject(processItem); - PostMessage( - PhMainWndHandle, - WM_PH_PROCESS_ADDED, - (WPARAM)PhGetRunIdProvider(&PhMwpProcessProviderRegistration), - (LPARAM)processItem - ); + PhPushProviderEventQueue(&PhMwpProcessEventQueue, ProviderAddedEvent, Parameter, PhGetRunIdProvider(&PhMwpProcessProviderRegistration)); } VOID NTAPI PhMwpProcessModifiedHandler( @@ -735,7 +766,7 @@ VOID NTAPI PhMwpProcessModifiedHandler( { PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Parameter; - PostMessage(PhMainWndHandle, WM_PH_PROCESS_MODIFIED, 0, (LPARAM)processItem); + PhPushProviderEventQueue(&PhMwpProcessEventQueue, ProviderModifiedEvent, Parameter, PhGetRunIdProvider(&PhMwpProcessProviderRegistration)); } VOID NTAPI PhMwpProcessRemovedHandler( @@ -747,7 +778,7 @@ VOID NTAPI PhMwpProcessRemovedHandler( // We already have a reference to the process item, so we don't need to // reference it here. - PostMessage(PhMainWndHandle, WM_PH_PROCESS_REMOVED, 0, (LPARAM)processItem); + PhPushProviderEventQueue(&PhMwpProcessEventQueue, ProviderRemovedEvent, Parameter, PhGetRunIdProvider(&PhMwpProcessProviderRegistration)); } VOID NTAPI PhMwpProcessesUpdatedHandler( @@ -755,7 +786,7 @@ VOID NTAPI PhMwpProcessesUpdatedHandler( _In_opt_ PVOID Context ) { - PostMessage(PhMainWndHandle, WM_PH_PROCESSES_UPDATED, 0, 0); + ProcessHacker_Invoke(PhMainWndHandle, PhMwpOnProcessesUpdated, PhGetRunIdProvider(&PhMwpProcessProviderRegistration)); } VOID PhMwpOnProcessAdded( @@ -765,12 +796,6 @@ VOID PhMwpOnProcessAdded( { PPH_PROCESS_NODE processNode; - if (!ProcessesNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpProcessTreeNewHandle, FALSE); - ProcessesNeedsRedraw = TRUE; - } - processNode = PhAddProcessNode(ProcessItem, RunId); if (RunId != 1) @@ -779,11 +804,7 @@ VOID PhMwpOnProcessAdded( HANDLE parentProcessId = NULL; PPH_STRING parentName = NULL; - if (parentProcess = PhReferenceProcessItemForParent( - ProcessItem->ParentProcessId, - ProcessItem->ProcessId, - &ProcessItem->CreateTime - )) + if (parentProcess = PhReferenceProcessItemForParent(ProcessItem)) { parentProcessId = parentProcess->ProcessId; parentName = parentProcess->ProcessName; @@ -807,7 +828,7 @@ VOID PhMwpOnProcessAdded( PhMwpLastNotificationDetails.ProcessId = ProcessItem->ProcessId; PhShowIconNotification(L"Process Created", PhaFormatString( - L"The process %s (%u) was created by %s (%u)", + L"The process %s (%lu) was created by %s (%lu)", ProcessItem->ProcessName->Buffer, HandleToUlong(ProcessItem->ProcessId), PhGetStringOrDefault(parentName, L"Unknown process"), @@ -851,12 +872,6 @@ VOID PhMwpOnProcessRemoved( { PPH_PROCESS_NODE processNode; - if (!ProcessesNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpProcessTreeNewHandle, FALSE); - ProcessesNeedsRedraw = TRUE; - } - PhLogProcessEntry(PH_LOG_ENTRY_PROCESS_DELETE, ProcessItem->ProcessId, ProcessItem->QueryHandle, ProcessItem->ProcessName, NULL, NULL); if (PhMwpNotifyIconNotifyMask & PH_NOTIFY_PROCESS_DELETE) @@ -868,7 +883,7 @@ VOID PhMwpOnProcessRemoved( PhMwpLastNotificationDetails.ProcessId = ProcessItem->ProcessId; PhShowIconNotification(L"Process Terminated", PhaFormatString( - L"The process %s (%u) was terminated.", + L"The process %s (%lu) was terminated.", ProcessItem->ProcessName->Buffer, HandleToUlong(ProcessItem->ProcessId) )->Buffer, NIIF_INFO); @@ -883,9 +898,41 @@ VOID PhMwpOnProcessRemoved( } VOID PhMwpOnProcessesUpdated( - VOID + _In_ ULONG RunId ) { + PPH_PROVIDER_EVENT events; + ULONG count; + ULONG i; + + events = PhFlushProviderEventQueue(&PhMwpProcessEventQueue, RunId, &count); + + if (events) + { + TreeNew_SetRedraw(PhMwpProcessTreeNewHandle, FALSE); + + for (i = 0; i < count; i++) + { + PH_PROVIDER_EVENT_TYPE type = PH_PROVIDER_EVENT_TYPE(events[i]); + PPH_PROCESS_ITEM processItem = PH_PROVIDER_EVENT_OBJECT(events[i]); + + switch (type) + { + case ProviderAddedEvent: + PhMwpOnProcessAdded(processItem, events[i].RunId); + break; + case ProviderModifiedEvent: + PhMwpOnProcessModified(processItem); + break; + case ProviderRemovedEvent: + PhMwpOnProcessRemoved(processItem); + break; + } + } + + PhFree(events); + } + // The modified notification is only sent for special cases. // We have to invalidate the text on each update. PhTickProcessNodes(); @@ -895,11 +942,8 @@ VOID PhMwpOnProcessesUpdated( PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessesUpdated), NULL); } - if (ProcessesNeedsRedraw) - { + if (count != 0) TreeNew_SetRedraw(PhMwpProcessTreeNewHandle, TRUE); - ProcessesNeedsRedraw = FALSE; - } if (NeedsSelectPid != 0) { diff --git a/ProcessHacker/mwpgsrv.c b/ProcessHacker/mwpgsrv.c index 866ca49dc2e3..ac9b464dcb14 100644 --- a/ProcessHacker/mwpgsrv.c +++ b/ProcessHacker/mwpgsrv.c @@ -3,6 +3,7 @@ * Main window: Services tab * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -37,16 +38,14 @@ PPH_MAIN_TAB_PAGE PhMwpServicesPage; HWND PhMwpServiceTreeNewHandle; +PH_PROVIDER_EVENT_QUEUE PhMwpServiceEventQueue; static PH_CALLBACK_REGISTRATION ServiceAddedRegistration; static PH_CALLBACK_REGISTRATION ServiceModifiedRegistration; static PH_CALLBACK_REGISTRATION ServiceRemovedRegistration; static PH_CALLBACK_REGISTRATION ServicesUpdatedRegistration; -static PPH_POINTER_LIST ServicesPendingList; static BOOLEAN ServiceTreeListLoaded = FALSE; -static BOOLEAN ServicesNeedsRedraw = FALSE; - static PPH_TN_FILTER_ENTRY DriverFilterEntry = NULL; BOOLEAN PhMwpServicesPageCallback( @@ -62,26 +61,28 @@ BOOLEAN PhMwpServicesPageCallback( { PhMwpServicesPage = Page; + PhInitializeProviderEventQueue(&PhMwpServiceEventQueue, 100); + PhRegisterCallback( - &PhServiceAddedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderAddedEvent), PhMwpServiceAddedHandler, NULL, &ServiceAddedRegistration ); PhRegisterCallback( - &PhServiceModifiedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderModifiedEvent), PhMwpServiceModifiedHandler, NULL, &ServiceModifiedRegistration ); PhRegisterCallback( - &PhServiceRemovedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderRemovedEvent), PhMwpServiceRemovedHandler, NULL, &ServiceRemovedRegistration ); PhRegisterCallback( - &PhServicesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackServiceProviderUpdatedEvent), PhMwpServicesUpdatedHandler, NULL, &ServicesUpdatedRegistration @@ -108,7 +109,7 @@ BOOLEAN PhMwpServicesPageCallback( ULONG startIndex = menuInfo->StartIndex; PPH_EMENU_ITEM menuItem; - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDEDRIVERSERVICES, L"Hide driver services", NULL, NULL), startIndex); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_VIEW_HIDEDRIVERSERVICES, L"&Hide driver services", NULL, NULL), startIndex); if (DriverFilterEntry && (menuItem = PhFindEMenuItem(menu, 0, NULL, ID_VIEW_HIDEDRIVERSERVICES))) menuItem->Flags |= PH_EMENU_CHECKED; @@ -116,7 +117,8 @@ BOOLEAN PhMwpServicesPageCallback( return TRUE; case MainTabPageLoadSettings: { - // Nothing + if (PhGetIntegerSetting(L"HideDriverServices")) + DriverFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), PhMwpDriverServiceTreeFilter, NULL); } return TRUE; case MainTabPageSaveSettings: @@ -136,7 +138,7 @@ BOOLEAN PhMwpServicesPageCallback( { HFONT font = (HFONT)Parameter1; - SendMessage(PhMwpServiceTreeNewHandle, WM_SETFONT, (WPARAM)font, TRUE); + SetWindowFont(PhMwpServiceTreeNewHandle, font, TRUE); } break; case MainTabPageUpdateAutomaticallyChanged: @@ -158,27 +160,7 @@ VOID PhMwpNeedServiceTreeList( if (!ServiceTreeListLoaded) { ServiceTreeListLoaded = TRUE; - PhLoadSettingsServiceTreeList(); - - if (PhGetIntegerSetting(L"HideDriverServices")) - DriverFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), PhMwpDriverServiceTreeFilter, NULL); - - if (ServicesPendingList) - { - PPH_SERVICE_ITEM serviceItem; - ULONG enumerationKey = 0; - - while (PhEnumPointerList(ServicesPendingList, &enumerationKey, (PVOID *)&serviceItem)) - { - PhMwpOnServiceAdded(serviceItem, 1); - } - - // Force a re-draw. - PhMwpOnServicesUpdated(); - - PhClearReference(&ServicesPendingList); - } } } @@ -358,12 +340,7 @@ VOID NTAPI PhMwpServiceAddedHandler( PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Parameter; PhReferenceObject(serviceItem); - PostMessage( - PhMainWndHandle, - WM_PH_SERVICE_ADDED, - PhGetRunIdProvider(&PhMwpServiceProviderRegistration), - (LPARAM)serviceItem - ); + PhPushProviderEventQueue(&PhMwpServiceEventQueue, ProviderAddedEvent, Parameter, PhGetRunIdProvider(&PhMwpServiceProviderRegistration)); } VOID NTAPI PhMwpServiceModifiedHandler( @@ -376,7 +353,7 @@ VOID NTAPI PhMwpServiceModifiedHandler( copy = PhAllocateCopy(serviceModifiedData, sizeof(PH_SERVICE_MODIFIED_DATA)); - PostMessage(PhMainWndHandle, WM_PH_SERVICE_MODIFIED, 0, (LPARAM)copy); + PhPushProviderEventQueue(&PhMwpServiceEventQueue, ProviderModifiedEvent, copy, PhGetRunIdProvider(&PhMwpServiceProviderRegistration)); } VOID NTAPI PhMwpServiceRemovedHandler( @@ -386,7 +363,7 @@ VOID NTAPI PhMwpServiceRemovedHandler( { PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Parameter; - PostMessage(PhMainWndHandle, WM_PH_SERVICE_REMOVED, 0, (LPARAM)serviceItem); + PhPushProviderEventQueue(&PhMwpServiceEventQueue, ProviderRemovedEvent, Parameter, PhGetRunIdProvider(&PhMwpServiceProviderRegistration)); } VOID NTAPI PhMwpServicesUpdatedHandler( @@ -394,7 +371,7 @@ VOID NTAPI PhMwpServicesUpdatedHandler( _In_opt_ PVOID Context ) { - PostMessage(PhMainWndHandle, WM_PH_SERVICES_UPDATED, 0, 0); + ProcessHacker_Invoke(PhMainWndHandle, PhMwpOnServicesUpdated, PhGetRunIdProvider(&PhMwpServiceProviderRegistration)); } VOID PhMwpOnServiceAdded( @@ -404,24 +381,8 @@ VOID PhMwpOnServiceAdded( { PPH_SERVICE_NODE serviceNode; - if (ServiceTreeListLoaded) - { - if (!ServicesNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpServiceTreeNewHandle, FALSE); - ServicesNeedsRedraw = TRUE; - } - - serviceNode = PhAddServiceNode(ServiceItem, RunId); - // ServiceItem dereferenced below - } - else - { - if (!ServicesPendingList) - ServicesPendingList = PhCreatePointerList(100); - - PhAddItemPointerList(ServicesPendingList, ServiceItem); - } + serviceNode = PhAddServiceNode(ServiceItem, RunId); + // ServiceItem dereferenced below if (RunId != 1) { @@ -444,30 +405,20 @@ VOID PhMwpOnServiceAdded( } } - if (ServiceTreeListLoaded) - PhDereferenceObject(ServiceItem); + PhDereferenceObject(ServiceItem); } VOID PhMwpOnServiceModified( - _In_ struct _PH_SERVICE_MODIFIED_DATA *ServiceModifiedData + _In_ PPH_SERVICE_MODIFIED_DATA ServiceModifiedData ) { PH_SERVICE_CHANGE serviceChange; UCHAR logEntryType; - if (ServiceTreeListLoaded) - { - //if (!ServicesNeedsRedraw) - //{ - // TreeNew_SetRedraw(PhMwpServiceTreeNewHandle, FALSE); - // ServicesNeedsRedraw = TRUE; - //} - - PhUpdateServiceNode(PhFindServiceNode(ServiceModifiedData->Service)); + PhUpdateServiceNode(PhFindServiceNode(ServiceModifiedData->Service)); - if (DriverFilterEntry) - PhApplyTreeNewFilters(PhGetFilterSupportServiceTreeList()); - } + if (DriverFilterEntry) + PhApplyTreeNewFilters(PhGetFilterSupportServiceTreeList()); serviceChange = PhGetServiceChange(ServiceModifiedData); @@ -536,15 +487,6 @@ VOID PhMwpOnServiceRemoved( _In_ PPH_SERVICE_ITEM ServiceItem ) { - if (ServiceTreeListLoaded) - { - if (!ServicesNeedsRedraw) - { - TreeNew_SetRedraw(PhMwpServiceTreeNewHandle, FALSE); - ServicesNeedsRedraw = TRUE; - } - } - PhLogServiceEntry(PH_LOG_ENTRY_SERVICE_DELETE, ServiceItem->Name, ServiceItem->DisplayName); if (PhMwpNotifyIconNotifyMask & PH_NOTIFY_SERVICE_CREATE) @@ -563,38 +505,47 @@ VOID PhMwpOnServiceRemoved( } } - if (ServiceTreeListLoaded) - { - PhRemoveServiceNode(PhFindServiceNode(ServiceItem)); - } - else - { - if (ServicesPendingList) - { - HANDLE pointerHandle; - - // Remove the service from the pending list so we don't try to add it later. - - if (pointerHandle = PhFindItemPointerList(ServicesPendingList, ServiceItem)) - PhRemoveItemPointerList(ServicesPendingList, pointerHandle); - - PhDereferenceObject(ServiceItem); - } - } + PhRemoveServiceNode(PhFindServiceNode(ServiceItem)); } VOID PhMwpOnServicesUpdated( - VOID + _In_ ULONG RunId ) { - if (ServiceTreeListLoaded) + PPH_PROVIDER_EVENT events; + ULONG count; + ULONG i; + + events = PhFlushProviderEventQueue(&PhMwpServiceEventQueue, RunId, &count); + + if (events) { - PhTickServiceNodes(); + TreeNew_SetRedraw(PhMwpServiceTreeNewHandle, FALSE); - if (ServicesNeedsRedraw) + for (i = 0; i < count; i++) { - TreeNew_SetRedraw(PhMwpServiceTreeNewHandle, TRUE); - ServicesNeedsRedraw = FALSE; + PH_PROVIDER_EVENT_TYPE type = PH_PROVIDER_EVENT_TYPE(events[i]); + PPH_SERVICE_ITEM serviceItem = PH_PROVIDER_EVENT_OBJECT(events[i]); + + switch (type) + { + case ProviderAddedEvent: + PhMwpOnServiceAdded(serviceItem, events[i].RunId); + break; + case ProviderModifiedEvent: + PhMwpOnServiceModified((PPH_SERVICE_MODIFIED_DATA)serviceItem); + break; + case ProviderRemovedEvent: + PhMwpOnServiceRemoved(serviceItem); + break; + } } + + PhFree(events); } + + PhTickServiceNodes(); + + if (count != 0) + TreeNew_SetRedraw(PhMwpServiceTreeNewHandle, TRUE); } diff --git a/ProcessHacker/mxml/COPYING b/ProcessHacker/mxml/COPYING deleted file mode 100644 index 4d0aa78af224..000000000000 --- a/ProcessHacker/mxml/COPYING +++ /dev/null @@ -1,507 +0,0 @@ - Mini-XML License - September 18, 2010 - - -The Mini-XML library and included programs are provided under the -terms of the GNU Library General Public License version 2 (LGPL2) -with the following exceptions: - - 1. Static linking of applications to the Mini-XML library -does not constitute a derivative work and does not require -the author to provide source code for the application, use -the shared Mini-XML libraries, or link their applications -against a user-supplied version of Mini-XML. - -If you link the application to a modified version of -Mini-XML, then the changes to Mini-XML must be provided -under the terms of the LGPL2 in sections 1, 2, and 4. - - 2. You do not have to provide a copy of the Mini-XML license -with programs that are linked to the Mini-XML library, nor -do you have to identify the Mini-XML license in your -program or documentation as required by section 6 of the -LGPL2. - - - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - [This is the first released version of the library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the library, or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link a program with the library, you must provide -complete object files to the recipients so that they can relink them -with the library, after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal -permission to copy, distribute and/or modify the library. - - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also compile or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - c) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - d) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Library General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/ProcessHacker/mxml/config.h b/ProcessHacker/mxml/config.h deleted file mode 100644 index e059e56d2b7a..000000000000 --- a/ProcessHacker/mxml/config.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * "$Id: config.h 451 2014-01-04 21:50:06Z msweet $" - * - * Configuration file for Mini-XML, a small XML-like file parsing library. - * - * Copyright 2003-2014 by Michael R Sweet. - * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML - */ - -/* - * Include necessary headers... - */ - -#include -#include -#include -#include -#include - - -/* - * Version number... - */ - -#define MXML_VERSION "Mini-XML v2.10" - - -/* - * Inline function support... - */ - -#define inline __inline - - -/* - * Long long support... - */ - -#define HAVE_LONG_LONG 1 - - -/* - * Do we have the snprintf() and vsnprintf() functions? - */ - -#define HAVE_SNPRINTF 1 -#define HAVE_VSNPRINTF 1 - - -/* - * Do we have the strXXX() functions? - */ - -#define HAVE_STRDUP 1 - - -/* - * Do we have threading support? - */ - -#undef HAVE_PTHREAD_H - - -/* - * Define prototypes for string functions as needed... - */ - -# ifndef HAVE_STRDUP -extern char *_mxml_strdup(const char *); -# define strdup _mxml_strdup -# endif /* !HAVE_STRDUP */ - -extern char *_mxml_strdupf(const char *, ...); -extern char *_mxml_vstrdupf(const char *, va_list); - -# ifndef HAVE_SNPRINTF -extern int _mxml_snprintf(char *, size_t, const char *, ...); -# define snprintf _mxml_snprintf -# endif /* !HAVE_SNPRINTF */ - -# ifndef HAVE_VSNPRINTF -extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); -# define vsnprintf _mxml_vsnprintf -# endif /* !HAVE_VSNPRINTF */ - -/* - * End of "$Id: config.h 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-private.h b/ProcessHacker/mxml/mxml-private.h deleted file mode 100644 index c5e4e6b6f27a..000000000000 --- a/ProcessHacker/mxml/mxml-private.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * "$Id: mxml-private.h 451 2014-01-04 21:50:06Z msweet $" - * - * Private definitions for Mini-XML, a small XML-like file parsing library. - * - * Copyright 2003-2014 by Michael R Sweet. - * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML - */ - -/* - * Include necessary headers... - */ - -#include "config.h" -#include "mxml.h" - - -/* - * Global, per-thread data... - */ - -typedef struct _mxml_global_s -{ - void (*error_cb)(const char *); - int num_entity_cbs; - int (*entity_cbs[100])(const char *name); - int wrap; - mxml_custom_load_cb_t custom_load_cb; - mxml_custom_save_cb_t custom_save_cb; -} _mxml_global_t; - - -/* - * Functions... - */ - -extern _mxml_global_t *_mxml_global(void); -extern int _mxml_entity_cb(const char *name); - - -/* - * End of "$Id: mxml-private.h 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-string.c b/ProcessHacker/mxml/mxml-string.c deleted file mode 100644 index ee4efc200436..000000000000 --- a/ProcessHacker/mxml/mxml-string.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * "$Id: mxml-string.c 454 2014-01-05 03:25:07Z msweet $" - * - * String functions for Mini-XML, a small XML-like file parsing library. - * - * Copyright 2003-2014 by Michael R Sweet. - * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML - */ - -/* - * Include necessary headers... - */ - -#include -#include "config.h" - - -/* - * The va_copy macro is part of C99, but many compilers don't implement it. - * Provide a "direct assignment" implmentation when va_copy isn't defined... - */ - -#ifndef va_copy -# ifdef __va_copy -# define va_copy(dst,src) __va_copy(dst,src) -# else -# define va_copy(dst,src) memcpy(&dst, src, sizeof(va_list)) -# endif /* __va_copy */ -#endif /* va_copy */ - - -#ifndef HAVE_SNPRINTF -/* - * '_mxml_snprintf()' - Format a string. - */ - -int /* O - Number of bytes formatted */ -_mxml_snprintf(char *buffer, /* I - Output buffer */ - size_t bufsize, /* I - Size of output buffer */ - const char *format, /* I - Printf-style format string */ - ...) /* I - Additional arguments as needed */ -{ - va_list ap; /* Argument list */ - int bytes; /* Number of bytes formatted */ - - - va_start(ap, format); - bytes = vsnprintf(buffer, bufsize, format, ap); - va_end(ap); - - return (bytes); -} -#endif /* !HAVE_SNPRINTF */ - - -/* - * '_mxml_strdup()' - Duplicate a string. - */ - -#ifndef HAVE_STRDUP -char * /* O - New string pointer */ -_mxml_strdup(const char *s) /* I - String to duplicate */ -{ - char *t; /* New string pointer */ - - - if (s == NULL) - return (NULL); - - if ((t = malloc(strlen(s) + 1)) == NULL) - return (NULL); - - return (strcpy(t, s)); -} -#endif /* !HAVE_STRDUP */ - - -/* - * '_mxml_strdupf()' - Format and duplicate a string. - */ - -char * /* O - New string pointer */ -_mxml_strdupf(const char *format, /* I - Printf-style format string */ - ...) /* I - Additional arguments as needed */ -{ - va_list ap; /* Pointer to additional arguments */ - char *s; /* Pointer to formatted string */ - - - /* - * Get a pointer to the additional arguments, format the string, - * and return it... - */ - - va_start(ap, format); - s = _mxml_vstrdupf(format, ap); - va_end(ap); - - return (s); -} - - -#ifndef HAVE_VSNPRINTF -/* - * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. - */ - -int /* O - Number of bytes formatted */ -_mxml_vsnprintf(char *buffer, /* O - Output buffer */ - size_t bufsize, /* O - Size of output buffer */ - const char *format, /* I - Printf-style format string */ - va_list ap) /* I - Pointer to additional arguments */ -{ - char *bufptr, /* Pointer to position in buffer */ - *bufend, /* Pointer to end of buffer */ - sign, /* Sign of format width */ - size, /* Size character (h, l, L) */ - type; /* Format type character */ - int width, /* Width of field */ - prec; /* Number of characters of precision */ - char tformat[100], /* Temporary format string for sprintf() */ - *tptr, /* Pointer into temporary format */ - temp[1024]; /* Buffer for formatted numbers */ - char *s; /* Pointer to string */ - int slen; /* Length of string */ - int bytes; /* Total number of bytes needed */ - - - /* - * Loop through the format string, formatting as needed... - */ - - bufptr = buffer; - bufend = buffer + bufsize - 1; - bytes = 0; - - while (*format) - { - if (*format == '%') - { - tptr = tformat; - *tptr++ = *format++; - - if (*format == '%') - { - if (bufptr && bufptr < bufend) - *bufptr++ = *format; - bytes ++; - format ++; - continue; - } - else if (strchr(" -+#\'", *format)) - { - *tptr++ = *format; - sign = *format++; - } - else - sign = 0; - - if (*format == '*') - { - /* - * Get width from argument... - */ - - format ++; - width = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); - tptr += strlen(tptr); - } - else - { - width = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - width = width * 10 + *format++ - '0'; - } - } - - if (*format == '.') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - format ++; - - if (*format == '*') - { - /* - * Get precision from argument... - */ - - format ++; - prec = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); - tptr += strlen(tptr); - } - else - { - prec = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - prec = prec * 10 + *format++ - '0'; - } - } - } - else - prec = -1; - - if (*format == 'l' && format[1] == 'l') - { - size = 'L'; - - if (tptr < (tformat + sizeof(tformat) - 2)) - { - *tptr++ = 'l'; - *tptr++ = 'l'; - } - - format += 2; - } - else if (*format == 'h' || *format == 'l' || *format == 'L') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - size = *format++; - } - - if (!*format) - break; - - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - type = *format++; - *tptr = '\0'; - - switch (type) - { - case 'E' : /* Floating point formats */ - case 'G' : - case 'e' : - case 'f' : - case 'g' : - if ((width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, double)); - - bytes += strlen(temp); - - if (bufptr) - { - if ((bufptr + strlen(temp)) > bufend) - { - strncpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr = bufend; - } - else - { - strcpy(bufptr, temp); - bufptr += strlen(temp); - } - } - break; - - case 'B' : /* Integer formats */ - case 'X' : - case 'b' : - case 'd' : - case 'i' : - case 'o' : - case 'u' : - case 'x' : - if ((width + 2) > sizeof(temp)) - break; - -#ifdef HAVE_LONG_LONG - if (size == 'L') - sprintf(temp, tformat, va_arg(ap, long long)); - else -#endif /* HAVE_LONG_LONG */ - sprintf(temp, tformat, va_arg(ap, int)); - - bytes += strlen(temp); - - if (bufptr) - { - if ((bufptr + strlen(temp)) > bufend) - { - strncpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr = bufend; - } - else - { - strcpy(bufptr, temp); - bufptr += strlen(temp); - } - } - break; - - case 'p' : /* Pointer value */ - if ((width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, void *)); - - bytes += strlen(temp); - - if (bufptr) - { - if ((bufptr + strlen(temp)) > bufend) - { - strncpy(bufptr, temp, (size_t)(bufend - bufptr)); - bufptr = bufend; - } - else - { - strcpy(bufptr, temp); - bufptr += strlen(temp); - } - } - break; - - case 'c' : /* Character or character array */ - bytes += width; - - if (bufptr) - { - if (width <= 1) - *bufptr++ = va_arg(ap, int); - else - { - if ((bufptr + width) > bufend) - width = bufend - bufptr; - - memcpy(bufptr, va_arg(ap, char *), (size_t)width); - bufptr += width; - } - } - break; - - case 's' : /* String */ - if ((s = va_arg(ap, char *)) == NULL) - s = "(null)"; - - slen = strlen(s); - if (slen > width && prec != width) - width = slen; - - bytes += width; - - if (bufptr) - { - if ((bufptr + width) > bufend) - width = bufend - bufptr; - - if (slen > width) - slen = width; - - if (sign == '-') - { - strncpy(bufptr, s, (size_t)slen); - memset(bufptr + slen, ' ', (size_t)(width - slen)); - } - else - { - memset(bufptr, ' ', (size_t)(width - slen)); - strncpy(bufptr + width - slen, s, (size_t)slen); - } - - bufptr += width; - } - break; - - case 'n' : /* Output number of chars so far */ - *(va_arg(ap, int *)) = bytes; - break; - } - } - else - { - bytes ++; - - if (bufptr && bufptr < bufend) - *bufptr++ = *format; - - format ++; - } - } - - /* - * Nul-terminate the string and return the number of characters needed. - */ - - *bufptr = '\0'; - - return (bytes); -} -#endif /* !HAVE_VSNPRINTF */ - - -/* - * '_mxml_vstrdupf()' - Format and duplicate a string. - */ - -char * /* O - New string pointer */ -_mxml_vstrdupf(const char *format, /* I - Printf-style format string */ - va_list ap) /* I - Pointer to additional arguments */ -{ - int bytes; /* Number of bytes required */ - char *buffer, /* String buffer */ - temp[256]; /* Small buffer for first vsnprintf */ - va_list apcopy; /* Copy of argument list */ - - - /* - * First format with a tiny buffer; this will tell us how many bytes are - * needed... - */ - - va_copy(apcopy, ap); - bytes = vsnprintf(temp, sizeof(temp), format, apcopy); - - if (bytes < sizeof(temp)) - { - /* - * Hey, the formatted string fits in the tiny buffer, so just dup that... - */ - - return (PhDuplicateBytesZSafe(temp)); - } - - /* - * Allocate memory for the whole thing and reformat to the new, larger - * buffer... - */ - - if ((buffer = PhAllocateExSafe(bytes + 1, HEAP_ZERO_MEMORY)) != NULL) - vsnprintf(buffer, bytes + 1, format, ap); - - /* - * Return the new string... - */ - - return (buffer); -} - - -/* - * End of "$Id: mxml-string.c 454 2014-01-05 03:25:07Z msweet $". - */ diff --git a/ProcessHacker/netlist.c b/ProcessHacker/netlist.c index 4ccd7eac42b1..1ed8fa84a3e5 100644 --- a/ProcessHacker/netlist.c +++ b/ProcessHacker/netlist.c @@ -1,797 +1,818 @@ -/* - * Process Hacker - - * network list - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -BOOLEAN PhpNetworkNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG PhpNetworkNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpRemoveNetworkNode( - _In_ PPH_NETWORK_NODE NetworkNode - ); - -LONG PhpNetworkTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpNetworkTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -PPH_STRING PhpGetNetworkItemProcessName( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -static HWND NetworkTreeListHandle; -static ULONG NetworkTreeListSortColumn; -static PH_SORT_ORDER NetworkTreeListSortOrder; -static PH_CM_MANAGER NetworkTreeListCm; - -static PPH_HASHTABLE NetworkNodeHashtable; // hashtable of all nodes -static PPH_LIST NetworkNodeList; // list of all nodes -static LONG NextUniqueId = 1; - -BOOLEAN PhNetworkTreeListStateHighlighting = TRUE; -static PPH_POINTER_LIST NetworkNodeStateList = NULL; // list of nodes which need to be processed - -static PH_TN_FILTER_SUPPORT FilterSupport; - -VOID PhNetworkTreeListInitialization( - VOID - ) -{ - NetworkNodeHashtable = PhCreateHashtable( - sizeof(PPH_NETWORK_NODE), - PhpNetworkNodeHashtableEqualFunction, - PhpNetworkNodeHashtableHashFunction, - 100 - ); - NetworkNodeList = PhCreateList(100); -} - -BOOLEAN PhpNetworkNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_NETWORK_NODE networkNode1 = *(PPH_NETWORK_NODE *)Entry1; - PPH_NETWORK_NODE networkNode2 = *(PPH_NETWORK_NODE *)Entry2; - - return networkNode1->NetworkItem == networkNode2->NetworkItem; -} - -ULONG PhpNetworkNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashIntPtr((ULONG_PTR)(*(PPH_NETWORK_NODE *)Entry)->NetworkItem); -} - -VOID PhInitializeNetworkTreeList( - _In_ HWND hwnd - ) -{ - NetworkTreeListHandle = hwnd; - PhSetControlTheme(NetworkTreeListHandle, L"explorer"); - SendMessage(TreeNew_GetTooltips(NetworkTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); - - TreeNew_SetCallback(hwnd, PhpNetworkTreeNewCallback, NULL); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHNETLC_PROCESS, TRUE, L"Name", 100, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumn(hwnd, PHNETLC_LOCALADDRESS, TRUE, L"Local address", 120, PH_ALIGN_LEFT, 1, 0); - PhAddTreeNewColumn(hwnd, PHNETLC_LOCALPORT, TRUE, L"Local port", 50, PH_ALIGN_RIGHT, 2, DT_RIGHT); - PhAddTreeNewColumn(hwnd, PHNETLC_REMOTEADDRESS, TRUE, L"Remote address", 120, PH_ALIGN_LEFT, 3, 0); - PhAddTreeNewColumn(hwnd, PHNETLC_REMOTEPORT, TRUE, L"Remote port", 50, PH_ALIGN_RIGHT, 4, DT_RIGHT); - PhAddTreeNewColumn(hwnd, PHNETLC_PROTOCOL, TRUE, L"Protocol", 45, PH_ALIGN_LEFT, 5, 0); - PhAddTreeNewColumn(hwnd, PHNETLC_STATE, TRUE, L"State", 70, PH_ALIGN_LEFT, 6, 0); - PhAddTreeNewColumn(hwnd, PHNETLC_OWNER, WINDOWS_HAS_SERVICE_TAGS, L"Owner", 80, PH_ALIGN_LEFT, 7, 0); - PhAddTreeNewColumnEx(hwnd, PHNETLC_TIMESTAMP, FALSE, L"Time stamp", 100, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumn(hwnd, PHNETLC_LOCALHOSTNAME, FALSE, L"Local hostname", 120, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHNETLC_REMOTEHOSTNAME, FALSE, L"Remote hostname", 120, PH_ALIGN_LEFT, -1, 0); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetSort(hwnd, 0, AscendingSortOrder); - - PhCmInitializeManager(&NetworkTreeListCm, hwnd, PHNETLC_MAXIMUM, PhpNetworkTreeNewPostSortFunction); - - if (PhPluginsEnabled) - { - PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; - - treeNewInfo.TreeNewHandle = hwnd; - treeNewInfo.CmData = &NetworkTreeListCm; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), &treeNewInfo); - } - - PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, NetworkNodeList); -} - -VOID PhLoadSettingsNetworkTreeList( - VOID - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhGetStringSetting(L"NetworkTreeListColumns"); - sortSettings = PhGetStringSetting(L"NetworkTreeListSort"); - PhCmLoadSettingsEx(NetworkTreeListHandle, &NetworkTreeListCm, 0, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSaveSettingsNetworkTreeList( - VOID - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(NetworkTreeListHandle, &NetworkTreeListCm, 0, &sortSettings); - PhSetStringSetting2(L"NetworkTreeListColumns", &settings->sr); - PhSetStringSetting2(L"NetworkTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportNetworkTreeList( - VOID - ) -{ - return &FilterSupport; -} - -PPH_NETWORK_NODE PhAddNetworkNode( - _In_ PPH_NETWORK_ITEM NetworkItem, - _In_ ULONG RunId - ) -{ - PPH_NETWORK_NODE networkNode; - - networkNode = PhAllocate(PhEmGetObjectSize(EmNetworkNodeType, sizeof(PH_NETWORK_NODE))); - memset(networkNode, 0, sizeof(PH_NETWORK_NODE)); - PhInitializeTreeNewNode(&networkNode->Node); - - if (PhNetworkTreeListStateHighlighting && RunId != 1) - { - PhChangeShStateTn( - &networkNode->Node, - &networkNode->ShState, - &NetworkNodeStateList, - NewItemState, - PhCsColorNew, - NULL - ); - } - - networkNode->NetworkItem = NetworkItem; - PhReferenceObject(NetworkItem); - networkNode->UniqueId = NextUniqueId++; // used to stabilize sorting - - memset(networkNode->TextCache, 0, sizeof(PH_STRINGREF) * PHNETLC_MAXIMUM); - networkNode->Node.TextCache = networkNode->TextCache; - networkNode->Node.TextCacheSize = PHNETLC_MAXIMUM; - - networkNode->ProcessNameText = PhpGetNetworkItemProcessName(NetworkItem); - - PhAddEntryHashtable(NetworkNodeHashtable, &networkNode); - PhAddItemList(NetworkNodeList, networkNode); - - if (FilterSupport.NodeList) - networkNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &networkNode->Node); - - PhEmCallObjectOperation(EmNetworkNodeType, networkNode, EmObjectCreate); - - TreeNew_NodesStructured(NetworkTreeListHandle); - - return networkNode; -} - -PPH_NETWORK_NODE PhFindNetworkNode( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - PH_NETWORK_NODE lookupNetworkNode; - PPH_NETWORK_NODE lookupNetworkNodePtr = &lookupNetworkNode; - PPH_NETWORK_NODE *networkNode; - - lookupNetworkNode.NetworkItem = NetworkItem; - - networkNode = (PPH_NETWORK_NODE *)PhFindEntryHashtable( - NetworkNodeHashtable, - &lookupNetworkNodePtr - ); - - if (networkNode) - return *networkNode; - else - return NULL; -} - -VOID PhRemoveNetworkNode( - _In_ PPH_NETWORK_NODE NetworkNode - ) -{ - // Remove from the hashtable here to avoid problems in case the key is re-used. - PhRemoveEntryHashtable(NetworkNodeHashtable, &NetworkNode); - - if (PhNetworkTreeListStateHighlighting) - { - PhChangeShStateTn( - &NetworkNode->Node, - &NetworkNode->ShState, - &NetworkNodeStateList, - RemovingItemState, - PhCsColorRemoved, - NetworkTreeListHandle - ); - } - else - { - PhpRemoveNetworkNode(NetworkNode); - } -} - -VOID PhpRemoveNetworkNode( - _In_ PPH_NETWORK_NODE NetworkNode - ) -{ - ULONG index; - - PhEmCallObjectOperation(EmNetworkNodeType, NetworkNode, EmObjectDelete); - - // Remove from list and cleanup. - - if ((index = PhFindItemList(NetworkNodeList, NetworkNode)) != -1) - PhRemoveItemList(NetworkNodeList, index); - - if (NetworkNode->ProcessNameText) PhDereferenceObject(NetworkNode->ProcessNameText); - if (NetworkNode->TimeStampText) PhDereferenceObject(NetworkNode->TimeStampText); - if (NetworkNode->TooltipText) PhDereferenceObject(NetworkNode->TooltipText); - - PhDereferenceObject(NetworkNode->NetworkItem); - - PhFree(NetworkNode); - - TreeNew_NodesStructured(NetworkTreeListHandle); -} - -VOID PhUpdateNetworkNode( - _In_ PPH_NETWORK_NODE NetworkNode - ) -{ - memset(NetworkNode->TextCache, 0, sizeof(PH_STRINGREF) * PHNETLC_MAXIMUM); - PhClearReference(&NetworkNode->TooltipText); - - PhInvalidateTreeNewNode(&NetworkNode->Node, TN_CACHE_ICON); - TreeNew_NodesStructured(NetworkTreeListHandle); -} - -VOID PhTickNetworkNodes( - VOID - ) -{ - if (NetworkTreeListSortOrder != NoSortOrder && NetworkTreeListSortColumn >= PHNETLC_MAXIMUM) - { - // Sorting is on, but it's not one of our columns. Force a rebuild. (If it was one of our - // columns, the restructure would have been handled in PhUpdateNetworkNode.) - TreeNew_NodesStructured(NetworkTreeListHandle); - } - - PH_TICK_SH_STATE_TN(PH_NETWORK_NODE, ShState, NetworkNodeStateList, PhpRemoveNetworkNode, PhCsHighlightingDuration, NetworkTreeListHandle, TRUE, NULL); -} - -#define SORT_FUNCTION(Column) PhpNetworkTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpNetworkTreeNewCompare##Column( \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_NETWORK_NODE node1 = *(PPH_NETWORK_NODE *)_elem1; \ - PPH_NETWORK_NODE node2 = *(PPH_NETWORK_NODE *)_elem2; \ - PPH_NETWORK_ITEM networkItem1 = node1->NetworkItem; \ - PPH_NETWORK_ITEM networkItem2 = node2->NetworkItem; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = intcmp(node1->UniqueId, node2->UniqueId); \ - \ - return PhModifySort(sortResult, NetworkTreeListSortOrder); \ -} - -LONG PhpNetworkTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - if (Result == 0) - Result = intcmp(((PPH_NETWORK_NODE)Node1)->UniqueId, ((PPH_NETWORK_NODE)Node2)->UniqueId); - - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(Process) -{ - sortResult = PhCompareString(node1->ProcessNameText, node2->ProcessNameText, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LocalAddress) -{ - sortResult = PhCompareStringZ(networkItem1->LocalAddressString, networkItem2->LocalAddressString, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LocalHostname) -{ - sortResult = PhCompareStringWithNull(networkItem1->LocalHostString, networkItem2->LocalHostString, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(LocalPort) -{ - sortResult = uintcmp(networkItem1->LocalEndpoint.Port, networkItem2->LocalEndpoint.Port); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(RemoteAddress) -{ - sortResult = PhCompareStringZ(networkItem1->RemoteAddressString, networkItem2->RemoteAddressString, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(RemoteHostname) -{ - sortResult = PhCompareStringWithNull(networkItem1->RemoteHostString, networkItem2->RemoteHostString, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(RemotePort) -{ - sortResult = uintcmp(networkItem1->RemoteEndpoint.Port, networkItem2->RemoteEndpoint.Port); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Protocol) -{ - sortResult = uintcmp(networkItem1->ProtocolType, networkItem2->ProtocolType); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(State) -{ - sortResult = uintcmp(networkItem1->State, networkItem2->State); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Owner) -{ - sortResult = PhCompareStringWithNull(networkItem1->OwnerName, networkItem2->OwnerName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(TimeStamp) -{ - sortResult = uint64cmp(networkItem1->CreateTime.QuadPart, networkItem2->CreateTime.QuadPart); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpNetworkTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_NETWORK_NODE node; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &NetworkTreeListCm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Process), - SORT_FUNCTION(LocalAddress), - SORT_FUNCTION(LocalHostname), - SORT_FUNCTION(LocalPort), - SORT_FUNCTION(RemoteAddress), - SORT_FUNCTION(RemoteHostname), - SORT_FUNCTION(RemotePort), - SORT_FUNCTION(Protocol), - SORT_FUNCTION(State), - SORT_FUNCTION(Owner), - SORT_FUNCTION(TimeStamp) - }; - int (__cdecl *sortFunction)(const void *, const void *); - - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)NetworkNodeList->Items, - NetworkNodeList->Count, - NetworkTreeListSortColumn, - NetworkTreeListSortOrder, - &NetworkTreeListCm - )) - { - if (NetworkTreeListSortColumn < PHNETLC_MAXIMUM) - sortFunction = sortFunctions[NetworkTreeListSortColumn]; - else - sortFunction = NULL; - - if (sortFunction) - { - qsort(NetworkNodeList->Items, NetworkNodeList->Count, sizeof(PVOID), sortFunction); - } - } - - getChildren->Children = (PPH_TREENEW_NODE *)NetworkNodeList->Items; - getChildren->NumberOfChildren = NetworkNodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_NETWORK_ITEM networkItem; - - node = (PPH_NETWORK_NODE)getCellText->Node; - networkItem = node->NetworkItem; - - switch (getCellText->Id) - { - case PHNETLC_PROCESS: - getCellText->Text = node->ProcessNameText->sr; - break; - case PHNETLC_LOCALADDRESS: - PhInitializeStringRefLongHint(&getCellText->Text, networkItem->LocalAddressString); - break; - case PHNETLC_LOCALHOSTNAME: - getCellText->Text = PhGetStringRef(networkItem->LocalHostString); - break; - case PHNETLC_LOCALPORT: - PhInitializeStringRefLongHint(&getCellText->Text, networkItem->LocalPortString); - break; - case PHNETLC_REMOTEADDRESS: - PhInitializeStringRefLongHint(&getCellText->Text, networkItem->RemoteAddressString); - break; - case PHNETLC_REMOTEHOSTNAME: - getCellText->Text = PhGetStringRef(networkItem->RemoteHostString); - break; - case PHNETLC_REMOTEPORT: - PhInitializeStringRefLongHint(&getCellText->Text, networkItem->RemotePortString); - break; - case PHNETLC_PROTOCOL: - PhInitializeStringRefLongHint(&getCellText->Text, PhGetProtocolTypeName(networkItem->ProtocolType)); - break; - case PHNETLC_STATE: - if (networkItem->ProtocolType & PH_TCP_PROTOCOL_TYPE) - PhInitializeStringRefLongHint(&getCellText->Text, PhGetTcpStateName(networkItem->State)); - else - PhInitializeEmptyStringRef(&getCellText->Text); - break; - case PHNETLC_OWNER: - if (WINDOWS_HAS_SERVICE_TAGS) - getCellText->Text = PhGetStringRef(networkItem->OwnerName); - else - PhInitializeStringRef(&getCellText->Text, L"N/A"); // make sure the user knows this column doesn't work on XP - break; - case PHNETLC_TIMESTAMP: - { - SYSTEMTIME systemTime; - - if (networkItem->CreateTime.QuadPart != 0) - { - PhLargeIntegerToLocalSystemTime(&systemTime, &networkItem->CreateTime); - PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->TimeStampText->sr; - } - else - { - PhInitializeEmptyStringRef(&getCellText->Text); - } - } - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeIcon: - { - PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; - - node = (PPH_NETWORK_NODE)getNodeIcon->Node; - - if (node->NetworkItem->ProcessIconValid) - { - // TODO: Check if the icon handle is actually valid, since the process item - // might get destroyed while the network node is still valid. - getNodeIcon->Icon = node->NetworkItem->ProcessIcon; - } - else - { - PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL); - } - - getNodeIcon->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - PPH_PROCESS_ITEM processItem; - - node = (PPH_NETWORK_NODE)getCellTooltip->Node; - - if (getCellTooltip->Column->Id != 0) - return FALSE; - - if (!node->TooltipText) - { - if (processItem = PhReferenceProcessItem(node->NetworkItem->ProcessId)) - { - node->TooltipText = PhGetProcessTooltipText(processItem, NULL); - PhDereferenceObject(processItem); - } - } - - if (!PhIsNullOrEmptyString(node->TooltipText)) - { - getCellTooltip->Text = node->TooltipText->sr; - getCellTooltip->Unfolding = FALSE; - getCellTooltip->MaximumWidth = -1; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &NetworkTreeListSortColumn, &NetworkTreeListSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(PhMainWndHandle, WM_COMMAND, ID_NETWORK_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(NetworkTreeListHandle, 0, -1); - break; - case VK_RETURN: - SendMessage(PhMainWndHandle, WM_COMMAND, ID_NETWORK_GOTOPROCESS, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = AscendingSortOrder; - PhInitializeTreeNewColumnMenu(&data); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(PhMainWndHandle, WM_COMMAND, ID_NETWORK_GOTOPROCESS, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - PhShowNetworkContextMenu(contextMenu); - } - return TRUE; - } - - return FALSE; -} - -PPH_STRING PhpGetNetworkItemProcessName( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - PH_FORMAT format[4]; - - if (!NetworkItem->ProcessId) - return PhCreateString(L"Waiting connections"); - - PhInitFormatS(&format[1], L" ("); - PhInitFormatU(&format[2], HandleToUlong(NetworkItem->ProcessId)); - PhInitFormatC(&format[3], ')'); - - if (NetworkItem->ProcessName) - PhInitFormatSR(&format[0], NetworkItem->ProcessName->sr); - else - PhInitFormatS(&format[0], L"Unknown process"); - - return PhFormat(format, 4, 96); -} - -PPH_NETWORK_ITEM PhGetSelectedNetworkItem( - VOID - ) -{ - PPH_NETWORK_ITEM networkItem = NULL; - ULONG i; - - for (i = 0; i < NetworkNodeList->Count; i++) - { - PPH_NETWORK_NODE node = NetworkNodeList->Items[i]; - - if (node->Node.Selected) - { - networkItem = node->NetworkItem; - break; - } - } - - return networkItem; -} - -VOID PhGetSelectedNetworkItems( - _Out_ PPH_NETWORK_ITEM **NetworkItems, - _Out_ PULONG NumberOfNetworkItems - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - for (i = 0; i < NetworkNodeList->Count; i++) - { - PPH_NETWORK_NODE node = NetworkNodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node->NetworkItem); - } - - *NumberOfNetworkItems = (ULONG)array.Count; - *NetworkItems = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllNetworkNodes( - VOID - ) -{ - TreeNew_DeselectRange(NetworkTreeListHandle, 0, -1); -} - -VOID PhSelectAndEnsureVisibleNetworkNode( - _In_ PPH_NETWORK_NODE NetworkNode - ) -{ - PhDeselectAllNetworkNodes(); - - if (!NetworkNode->Node.Visible) - return; - - TreeNew_SetFocusNode(NetworkTreeListHandle, &NetworkNode->Node); - TreeNew_SetMarkNode(NetworkTreeListHandle, &NetworkNode->Node); - TreeNew_SelectRange(NetworkTreeListHandle, NetworkNode->Node.Index, NetworkNode->Node.Index); - TreeNew_EnsureVisible(NetworkTreeListHandle, &NetworkNode->Node); -} - -VOID PhCopyNetworkList( - VOID - ) -{ - PPH_STRING text; - - text = PhGetTreeNewText(NetworkTreeListHandle, 0); - PhSetClipboardString(NetworkTreeListHandle, &text->sr); - PhDereferenceObject(text); -} - -VOID PhWriteNetworkList( - _Inout_ PPH_FILE_STREAM FileStream, - _In_ ULONG Mode - ) -{ - PPH_LIST lines; - ULONG i; - - lines = PhGetGenericTreeNewLines(NetworkTreeListHandle, Mode); - - for (i = 0; i < lines->Count; i++) - { - PPH_STRING line; - - line = lines->Items[i]; - PhWriteStringAsUtf8FileStream(FileStream, &line->sr); - PhDereferenceObject(line); - PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); - } - - PhDereferenceObject(lines); -} +/* + * Process Hacker - + * network list + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +BOOLEAN PhpNetworkNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG PhpNetworkNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpRemoveNetworkNode( + _In_ PPH_NETWORK_NODE NetworkNode + ); + +LONG PhpNetworkTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpNetworkTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +PPH_STRING PhpGetNetworkItemProcessName( + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +static HWND NetworkTreeListHandle; +static ULONG NetworkTreeListSortColumn; +static PH_SORT_ORDER NetworkTreeListSortOrder; +static PH_CM_MANAGER NetworkTreeListCm; + +static PPH_HASHTABLE NetworkNodeHashtable; // hashtable of all nodes +static PPH_LIST NetworkNodeList; // list of all nodes +static ULONG64 NextUniqueId = 0; + +BOOLEAN PhNetworkTreeListStateHighlighting = TRUE; +static PPH_POINTER_LIST NetworkNodeStateList = NULL; // list of nodes which need to be processed + +static PH_TN_FILTER_SUPPORT FilterSupport; + +VOID PhNetworkTreeListInitialization( + VOID + ) +{ + NetworkNodeHashtable = PhCreateHashtable( + sizeof(PPH_NETWORK_NODE), + PhpNetworkNodeHashtableEqualFunction, + PhpNetworkNodeHashtableHashFunction, + 100 + ); + NetworkNodeList = PhCreateList(100); +} + +BOOLEAN PhpNetworkNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_NETWORK_NODE networkNode1 = *(PPH_NETWORK_NODE *)Entry1; + PPH_NETWORK_NODE networkNode2 = *(PPH_NETWORK_NODE *)Entry2; + + return networkNode1->NetworkItem == networkNode2->NetworkItem; +} + +ULONG PhpNetworkNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PPH_NETWORK_NODE *)Entry)->NetworkItem); +} + +VOID PhInitializeNetworkTreeList( + _In_ HWND hwnd + ) +{ + NetworkTreeListHandle = hwnd; + PhSetControlTheme(NetworkTreeListHandle, L"explorer"); + SendMessage(TreeNew_GetTooltips(NetworkTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); + + TreeNew_SetCallback(hwnd, PhpNetworkTreeNewCallback, NULL); + + TreeNew_SetRedraw(hwnd, FALSE); + + // Default columns + PhAddTreeNewColumn(hwnd, PHNETLC_PROCESS, TRUE, L"Name", 100, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(hwnd, PHNETLC_LOCALADDRESS, TRUE, L"Local address", 120, PH_ALIGN_LEFT, 1, 0); + PhAddTreeNewColumn(hwnd, PHNETLC_LOCALPORT, TRUE, L"Local port", 50, PH_ALIGN_RIGHT, 2, DT_RIGHT); + PhAddTreeNewColumn(hwnd, PHNETLC_REMOTEADDRESS, TRUE, L"Remote address", 120, PH_ALIGN_LEFT, 3, 0); + PhAddTreeNewColumn(hwnd, PHNETLC_REMOTEPORT, TRUE, L"Remote port", 50, PH_ALIGN_RIGHT, 4, DT_RIGHT); + PhAddTreeNewColumn(hwnd, PHNETLC_PROTOCOL, TRUE, L"Protocol", 45, PH_ALIGN_LEFT, 5, 0); + PhAddTreeNewColumn(hwnd, PHNETLC_STATE, TRUE, L"State", 70, PH_ALIGN_LEFT, 6, 0); + PhAddTreeNewColumn(hwnd, PHNETLC_OWNER, TRUE, L"Owner", 80, PH_ALIGN_LEFT, 7, 0); + PhAddTreeNewColumnEx(hwnd, PHNETLC_TIMESTAMP, FALSE, L"Time stamp", 100, PH_ALIGN_LEFT, -1, 0, TRUE); + PhAddTreeNewColumn(hwnd, PHNETLC_LOCALHOSTNAME, FALSE, L"Local hostname", 120, PH_ALIGN_LEFT, -1, 0); + PhAddTreeNewColumn(hwnd, PHNETLC_REMOTEHOSTNAME, FALSE, L"Remote hostname", 120, PH_ALIGN_LEFT, -1, 0); + + TreeNew_SetRedraw(hwnd, TRUE); + + TreeNew_SetSort(hwnd, 0, AscendingSortOrder); + + PhCmInitializeManager(&NetworkTreeListCm, hwnd, PHNETLC_MAXIMUM, PhpNetworkTreeNewPostSortFunction); + + if (PhPluginsEnabled) + { + PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; + + treeNewInfo.TreeNewHandle = hwnd; + treeNewInfo.CmData = &NetworkTreeListCm; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), &treeNewInfo); + } + + PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, NetworkNodeList); +} + +VOID PhLoadSettingsNetworkTreeList( + VOID + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhGetStringSetting(L"NetworkTreeListColumns"); + sortSettings = PhGetStringSetting(L"NetworkTreeListSort"); + PhCmLoadSettingsEx(NetworkTreeListHandle, &NetworkTreeListCm, 0, &settings->sr, &sortSettings->sr); + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSaveSettingsNetworkTreeList( + VOID + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(NetworkTreeListHandle, &NetworkTreeListCm, 0, &sortSettings); + PhSetStringSetting2(L"NetworkTreeListColumns", &settings->sr); + PhSetStringSetting2(L"NetworkTreeListSort", &sortSettings->sr); + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportNetworkTreeList( + VOID + ) +{ + return &FilterSupport; +} + +PPH_NETWORK_NODE PhAddNetworkNode( + _In_ PPH_NETWORK_ITEM NetworkItem, + _In_ ULONG RunId + ) +{ + PPH_NETWORK_NODE networkNode; + + networkNode = PhAllocate(PhEmGetObjectSize(EmNetworkNodeType, sizeof(PH_NETWORK_NODE))); + memset(networkNode, 0, sizeof(PH_NETWORK_NODE)); + PhInitializeTreeNewNode(&networkNode->Node); + + if (PhNetworkTreeListStateHighlighting && RunId != 1) + { + PhChangeShStateTn( + &networkNode->Node, + &networkNode->ShState, + &NetworkNodeStateList, + NewItemState, + PhCsColorNew, + NULL + ); + } + + networkNode->NetworkItem = NetworkItem; + PhReferenceObject(NetworkItem); + networkNode->UniqueId = ++NextUniqueId; // used to stabilize sorting + + memset(networkNode->TextCache, 0, sizeof(PH_STRINGREF) * PHNETLC_MAXIMUM); + networkNode->Node.TextCache = networkNode->TextCache; + networkNode->Node.TextCacheSize = PHNETLC_MAXIMUM; + + networkNode->ProcessNameText = PhpGetNetworkItemProcessName(NetworkItem); + + PhAddEntryHashtable(NetworkNodeHashtable, &networkNode); + PhAddItemList(NetworkNodeList, networkNode); + + if (FilterSupport.NodeList) + networkNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &networkNode->Node); + + PhEmCallObjectOperation(EmNetworkNodeType, networkNode, EmObjectCreate); + + TreeNew_NodesStructured(NetworkTreeListHandle); + + return networkNode; +} + +PPH_NETWORK_NODE PhFindNetworkNode( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PH_NETWORK_NODE lookupNetworkNode; + PPH_NETWORK_NODE lookupNetworkNodePtr = &lookupNetworkNode; + PPH_NETWORK_NODE *networkNode; + + lookupNetworkNode.NetworkItem = NetworkItem; + + networkNode = (PPH_NETWORK_NODE *)PhFindEntryHashtable( + NetworkNodeHashtable, + &lookupNetworkNodePtr + ); + + if (networkNode) + return *networkNode; + else + return NULL; +} + +VOID PhRemoveNetworkNode( + _In_ PPH_NETWORK_NODE NetworkNode + ) +{ + // Remove from the hashtable here to avoid problems in case the key is re-used. + PhRemoveEntryHashtable(NetworkNodeHashtable, &NetworkNode); + + if (PhNetworkTreeListStateHighlighting) + { + PhChangeShStateTn( + &NetworkNode->Node, + &NetworkNode->ShState, + &NetworkNodeStateList, + RemovingItemState, + PhCsColorRemoved, + NetworkTreeListHandle + ); + } + else + { + PhpRemoveNetworkNode(NetworkNode); + } +} + +VOID PhpRemoveNetworkNode( + _In_ PPH_NETWORK_NODE NetworkNode + ) +{ + ULONG index; + + PhEmCallObjectOperation(EmNetworkNodeType, NetworkNode, EmObjectDelete); + + // Remove from list and cleanup. + + if ((index = PhFindItemList(NetworkNodeList, NetworkNode)) != -1) + PhRemoveItemList(NetworkNodeList, index); + + if (NetworkNode->ProcessNameText) PhDereferenceObject(NetworkNode->ProcessNameText); + if (NetworkNode->TimeStampText) PhDereferenceObject(NetworkNode->TimeStampText); + if (NetworkNode->TooltipText) PhDereferenceObject(NetworkNode->TooltipText); + + PhDereferenceObject(NetworkNode->NetworkItem); + + PhFree(NetworkNode); + + TreeNew_NodesStructured(NetworkTreeListHandle); +} + +VOID PhUpdateNetworkNode( + _In_ PPH_NETWORK_NODE NetworkNode + ) +{ + memset(NetworkNode->TextCache, 0, sizeof(PH_STRINGREF) * PHNETLC_MAXIMUM); + PhClearReference(&NetworkNode->TooltipText); + + PhInvalidateTreeNewNode(&NetworkNode->Node, TN_CACHE_ICON); + TreeNew_NodesStructured(NetworkTreeListHandle); +} + +VOID PhTickNetworkNodes( + VOID + ) +{ + if (NetworkTreeListSortOrder != NoSortOrder && NetworkTreeListSortColumn >= PHNETLC_MAXIMUM) + { + // Sorting is on, but it's not one of our columns. Force a rebuild. (If it was one of our + // columns, the restructure would have been handled in PhUpdateNetworkNode.) + TreeNew_NodesStructured(NetworkTreeListHandle); + } + + PH_TICK_SH_STATE_TN(PH_NETWORK_NODE, ShState, NetworkNodeStateList, PhpRemoveNetworkNode, PhCsHighlightingDuration, NetworkTreeListHandle, TRUE, NULL); +} + +#define SORT_FUNCTION(Column) PhpNetworkTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpNetworkTreeNewCompare##Column( \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_NETWORK_NODE node1 = *(PPH_NETWORK_NODE *)_elem1; \ + PPH_NETWORK_NODE node2 = *(PPH_NETWORK_NODE *)_elem2; \ + PPH_NETWORK_ITEM networkItem1 = node1->NetworkItem; \ + PPH_NETWORK_ITEM networkItem2 = node2->NetworkItem; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uint64cmp(node1->UniqueId, node2->UniqueId); \ + \ + return PhModifySort(sortResult, NetworkTreeListSortOrder); \ +} + +LONG PhpNetworkTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = uint64cmp(((PPH_NETWORK_NODE)Node1)->UniqueId, ((PPH_NETWORK_NODE)Node2)->UniqueId); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(Process) +{ + sortResult = PhCompareString(node1->ProcessNameText, node2->ProcessNameText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LocalAddress) +{ + sortResult = PhCompareStringZ(networkItem1->LocalAddressString, networkItem2->LocalAddressString, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LocalHostname) +{ + sortResult = PhCompareStringWithNullSortOrder(networkItem1->LocalHostString, networkItem2->LocalHostString, NetworkTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(LocalPort) +{ + sortResult = uintcmp(networkItem1->LocalEndpoint.Port, networkItem2->LocalEndpoint.Port); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(RemoteAddress) +{ + sortResult = PhCompareStringZ(networkItem1->RemoteAddressString, networkItem2->RemoteAddressString, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(RemoteHostname) +{ + sortResult = PhCompareStringWithNullSortOrder(networkItem1->RemoteHostString, networkItem2->RemoteHostString, NetworkTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(RemotePort) +{ + sortResult = uintcmp(networkItem1->RemoteEndpoint.Port, networkItem2->RemoteEndpoint.Port); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Protocol) +{ + sortResult = uintcmp(networkItem1->ProtocolType, networkItem2->ProtocolType); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(State) +{ + sortResult = uintcmp(networkItem1->State, networkItem2->State); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Owner) +{ + sortResult = PhCompareStringWithNullSortOrder(networkItem1->OwnerName, networkItem2->OwnerName, NetworkTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TimeStamp) +{ + sortResult = uint64cmp(networkItem1->CreateTime.QuadPart, networkItem2->CreateTime.QuadPart); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpNetworkTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_NETWORK_NODE node; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &NetworkTreeListCm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Process), + SORT_FUNCTION(LocalAddress), + SORT_FUNCTION(LocalPort), + SORT_FUNCTION(RemoteAddress), + SORT_FUNCTION(RemotePort), + SORT_FUNCTION(Protocol), + SORT_FUNCTION(State), + SORT_FUNCTION(Owner), + SORT_FUNCTION(TimeStamp), + SORT_FUNCTION(LocalHostname), + SORT_FUNCTION(RemoteHostname) + }; + int (__cdecl *sortFunction)(const void *, const void *); + + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)NetworkNodeList->Items, + NetworkNodeList->Count, + NetworkTreeListSortColumn, + NetworkTreeListSortOrder, + &NetworkTreeListCm + )) + { + if (NetworkTreeListSortColumn < PHNETLC_MAXIMUM) + sortFunction = sortFunctions[NetworkTreeListSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort(NetworkNodeList->Items, NetworkNodeList->Count, sizeof(PVOID), sortFunction); + } + } + + getChildren->Children = (PPH_TREENEW_NODE *)NetworkNodeList->Items; + getChildren->NumberOfChildren = NetworkNodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_NETWORK_ITEM networkItem; + + node = (PPH_NETWORK_NODE)getCellText->Node; + networkItem = node->NetworkItem; + + switch (getCellText->Id) + { + case PHNETLC_PROCESS: + getCellText->Text = node->ProcessNameText->sr; + break; + case PHNETLC_LOCALADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, networkItem->LocalAddressString); + break; + case PHNETLC_LOCALHOSTNAME: + getCellText->Text = PhGetStringRef(networkItem->LocalHostString); + break; + case PHNETLC_LOCALPORT: + PhInitializeStringRefLongHint(&getCellText->Text, networkItem->LocalPortString); + break; + case PHNETLC_REMOTEADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, networkItem->RemoteAddressString); + break; + case PHNETLC_REMOTEHOSTNAME: + getCellText->Text = PhGetStringRef(networkItem->RemoteHostString); + break; + case PHNETLC_REMOTEPORT: + PhInitializeStringRefLongHint(&getCellText->Text, networkItem->RemotePortString); + break; + case PHNETLC_PROTOCOL: + PhInitializeStringRefLongHint(&getCellText->Text, PhGetProtocolTypeName(networkItem->ProtocolType)); + break; + case PHNETLC_STATE: + if (networkItem->ProtocolType & PH_TCP_PROTOCOL_TYPE) + PhInitializeStringRefLongHint(&getCellText->Text, PhGetTcpStateName(networkItem->State)); + else + PhInitializeEmptyStringRef(&getCellText->Text); + break; + case PHNETLC_OWNER: + getCellText->Text = PhGetStringRef(networkItem->OwnerName); + break; + case PHNETLC_TIMESTAMP: + { + SYSTEMTIME systemTime; + + if (networkItem->CreateTime.QuadPart != 0) + { + PhLargeIntegerToLocalSystemTime(&systemTime, &networkItem->CreateTime); + PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->TimeStampText->sr; + } + else + { + PhInitializeEmptyStringRef(&getCellText->Text); + } + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeIcon: + { + PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; + + node = (PPH_NETWORK_NODE)getNodeIcon->Node; + + if (node->NetworkItem->ProcessIconValid && node->NetworkItem->ProcessIcon) + { + getNodeIcon->Icon = node->NetworkItem->ProcessIcon; + } + else + { + PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL); + } + + getNodeIcon->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + PPH_PROCESS_ITEM processItem; + + node = (PPH_NETWORK_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0) + return FALSE; + + if (!node->TooltipText) + { + if (processItem = PhReferenceProcessItem(node->NetworkItem->ProcessId)) + { + node->TooltipText = PhGetProcessTooltipText(processItem, NULL); + PhDereferenceObject(processItem); + } + } + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->MaximumWidth = -1; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &NetworkTreeListSortColumn, &NetworkTreeListSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(PhMainWndHandle, WM_COMMAND, ID_NETWORK_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(NetworkTreeListHandle, 0, -1); + break; + case VK_RETURN: + SendMessage(PhMainWndHandle, WM_COMMAND, ID_NETWORK_GOTOPROCESS, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(PhMainWndHandle, WM_COMMAND, ID_NETWORK_GOTOPROCESS, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + PhShowNetworkContextMenu(contextMenu); + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + node = (PPH_NETWORK_NODE)getNodeColor->Node; + + if (!node->NetworkItem) + { + NOTHING; + } + else if (!node->NetworkItem->ProcessId) + { + NOTHING; + } + else if (PhCsUseColorPacked && node->NetworkItem->UnknownProcess) + { + getNodeColor->BackColor = PhCsColorPacked; + } + else if (PhCsUseColorPicoProcesses && node->NetworkItem->SubsystemProcess) + { + getNodeColor->BackColor = PhCsColorPicoProcesses; + } + + getNodeColor->Flags |= TN_AUTO_FORECOLOR; + } + return TRUE; + } + + return FALSE; +} + +PPH_STRING PhpGetNetworkItemProcessName( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PH_FORMAT format[4]; + + if (!NetworkItem->ProcessId) + return PhCreateString(L"Waiting connections"); + + PhInitFormatS(&format[1], L" ("); + PhInitFormatU(&format[2], HandleToUlong(NetworkItem->ProcessId)); + PhInitFormatC(&format[3], ')'); + + if (NetworkItem->ProcessName) + PhInitFormatSR(&format[0], NetworkItem->ProcessName->sr); + else + PhInitFormatS(&format[0], L"Unknown process"); + + return PhFormat(format, 4, 96); +} + +PPH_NETWORK_ITEM PhGetSelectedNetworkItem( + VOID + ) +{ + PPH_NETWORK_ITEM networkItem = NULL; + ULONG i; + + for (i = 0; i < NetworkNodeList->Count; i++) + { + PPH_NETWORK_NODE node = NetworkNodeList->Items[i]; + + if (node->Node.Selected) + { + networkItem = node->NetworkItem; + break; + } + } + + return networkItem; +} + +VOID PhGetSelectedNetworkItems( + _Out_ PPH_NETWORK_ITEM **NetworkItems, + _Out_ PULONG NumberOfNetworkItems + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + for (i = 0; i < NetworkNodeList->Count; i++) + { + PPH_NETWORK_NODE node = NetworkNodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node->NetworkItem); + } + + *NumberOfNetworkItems = (ULONG)array.Count; + *NetworkItems = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllNetworkNodes( + VOID + ) +{ + TreeNew_DeselectRange(NetworkTreeListHandle, 0, -1); +} + +VOID PhSelectAndEnsureVisibleNetworkNode( + _In_ PPH_NETWORK_NODE NetworkNode + ) +{ + PhDeselectAllNetworkNodes(); + + if (!NetworkNode->Node.Visible) + return; + + TreeNew_SetFocusNode(NetworkTreeListHandle, &NetworkNode->Node); + TreeNew_SetMarkNode(NetworkTreeListHandle, &NetworkNode->Node); + TreeNew_SelectRange(NetworkTreeListHandle, NetworkNode->Node.Index, NetworkNode->Node.Index); + TreeNew_EnsureVisible(NetworkTreeListHandle, &NetworkNode->Node); +} + +VOID PhCopyNetworkList( + VOID + ) +{ + PPH_STRING text; + + text = PhGetTreeNewText(NetworkTreeListHandle, 0); + PhSetClipboardString(NetworkTreeListHandle, &text->sr); + PhDereferenceObject(text); +} + +VOID PhWriteNetworkList( + _Inout_ PPH_FILE_STREAM FileStream, + _In_ ULONG Mode + ) +{ + PPH_LIST lines; + ULONG i; + + lines = PhGetGenericTreeNewLines(NetworkTreeListHandle, Mode); + + for (i = 0; i < lines->Count; i++) + { + PPH_STRING line; + + line = lines->Items[i]; + PhWriteStringAsUtf8FileStream(FileStream, &line->sr); + PhDereferenceObject(line); + PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); + } + + PhDereferenceObject(lines); +} diff --git a/ProcessHacker/netprv.c b/ProcessHacker/netprv.c index 9d79ec30219c..b825d19564a1 100644 --- a/ProcessHacker/netprv.c +++ b/ProcessHacker/netprv.c @@ -1,996 +1,1098 @@ -/* - * Process Hacker - - * network provider - * - * Copyright (C) 2010 wj32 - * Copyright (C) 2010 evilpie - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -typedef struct _PH_NETWORK_CONNECTION -{ - ULONG ProtocolType; - PH_IP_ENDPOINT LocalEndpoint; - PH_IP_ENDPOINT RemoteEndpoint; - ULONG State; - HANDLE ProcessId; - LARGE_INTEGER CreateTime; - ULONGLONG OwnerInfo[PH_NETWORK_OWNER_INFO_SIZE]; -} PH_NETWORK_CONNECTION, *PPH_NETWORK_CONNECTION; - -typedef struct _PH_NETWORK_ITEM_QUERY_DATA -{ - SLIST_ENTRY ListEntry; - PPH_NETWORK_ITEM NetworkItem; - - PH_IP_ADDRESS Address; - BOOLEAN Remote; - PPH_STRING HostString; -} PH_NETWORK_ITEM_QUERY_DATA, *PPH_NETWORK_ITEM_QUERY_DATA; - -typedef struct _PHP_RESOLVE_CACHE_ITEM -{ - PH_IP_ADDRESS Address; - PPH_STRING HostString; -} PHP_RESOLVE_CACHE_ITEM, *PPHP_RESOLVE_CACHE_ITEM; - -VOID NTAPI PhpNetworkItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -BOOLEAN PhpNetworkHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI PhpNetworkHashtableHashFunction( - _In_ PVOID Entry - ); - -BOOLEAN PhpResolveCacheHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI PhpResolveCacheHashtableHashFunction( - _In_ PVOID Entry - ); - -BOOLEAN PhGetNetworkConnections( - _Out_ PPH_NETWORK_CONNECTION *Connections, - _Out_ PULONG NumberOfConnections - ); - -PPH_OBJECT_TYPE PhNetworkItemType; - -PPH_HASHTABLE PhNetworkHashtable; -PH_QUEUED_LOCK PhNetworkHashtableLock = PH_QUEUED_LOCK_INIT; - -PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemAddedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemModifiedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemRemovedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemsUpdatedEvent); - -PH_INITONCE PhNetworkProviderWorkQueueInitOnce = PH_INITONCE_INIT; -PH_WORK_QUEUE PhNetworkProviderWorkQueue; -SLIST_HEADER PhNetworkItemQueryListHead; - -BOOLEAN PhEnableNetworkProviderResolve = TRUE; -static BOOLEAN NetworkImportDone = FALSE; -static PPH_HASHTABLE PhpResolveCacheHashtable; -static PH_QUEUED_LOCK PhpResolveCacheHashtableLock = PH_QUEUED_LOCK_INIT; - -BOOLEAN PhNetworkProviderInitialization( - VOID - ) -{ - PhNetworkItemType = PhCreateObjectType(L"NetworkItem", 0, PhpNetworkItemDeleteProcedure); - PhNetworkHashtable = PhCreateHashtable( - sizeof(PPH_NETWORK_ITEM), - PhpNetworkHashtableEqualFunction, - PhpNetworkHashtableHashFunction, - 40 - ); - - RtlInitializeSListHead(&PhNetworkItemQueryListHead); - - PhpResolveCacheHashtable = PhCreateHashtable( - sizeof(PHP_RESOLVE_CACHE_ITEM), - PhpResolveCacheHashtableEqualFunction, - PhpResolveCacheHashtableHashFunction, - 20 - ); - - return TRUE; -} - -PPH_NETWORK_ITEM PhCreateNetworkItem( - VOID - ) -{ - PPH_NETWORK_ITEM networkItem; - - networkItem = PhCreateObject( - PhEmGetObjectSize(EmNetworkItemType, sizeof(PH_NETWORK_ITEM)), - PhNetworkItemType - ); - memset(networkItem, 0, sizeof(PH_NETWORK_ITEM)); - PhEmCallObjectOperation(EmNetworkItemType, networkItem, EmObjectCreate); - - return networkItem; -} - -VOID NTAPI PhpNetworkItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Object; - - PhEmCallObjectOperation(EmNetworkItemType, networkItem, EmObjectDelete); - - if (networkItem->ProcessName) - PhDereferenceObject(networkItem->ProcessName); - if (networkItem->OwnerName) - PhDereferenceObject(networkItem->OwnerName); - if (networkItem->LocalHostString) - PhDereferenceObject(networkItem->LocalHostString); - if (networkItem->RemoteHostString) - PhDereferenceObject(networkItem->RemoteHostString); -} - -BOOLEAN PhpNetworkHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_NETWORK_ITEM networkItem1 = *(PPH_NETWORK_ITEM *)Entry1; - PPH_NETWORK_ITEM networkItem2 = *(PPH_NETWORK_ITEM *)Entry2; - - return - networkItem1->ProtocolType == networkItem2->ProtocolType && - PhEqualIpEndpoint(&networkItem1->LocalEndpoint, &networkItem2->LocalEndpoint) && - PhEqualIpEndpoint(&networkItem1->RemoteEndpoint, &networkItem2->RemoteEndpoint) && - networkItem1->ProcessId == networkItem2->ProcessId; -} - -ULONG NTAPI PhpNetworkHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPH_NETWORK_ITEM networkItem = *(PPH_NETWORK_ITEM *)Entry; - - return - networkItem->ProtocolType ^ - PhHashIpEndpoint(&networkItem->LocalEndpoint) ^ - PhHashIpEndpoint(&networkItem->RemoteEndpoint) ^ - HandleToUlong(networkItem->ProcessId); -} - -PPH_NETWORK_ITEM PhReferenceNetworkItem( - _In_ ULONG ProtocolType, - _In_ PPH_IP_ENDPOINT LocalEndpoint, - _In_ PPH_IP_ENDPOINT RemoteEndpoint, - _In_ HANDLE ProcessId - ) -{ - PH_NETWORK_ITEM lookupNetworkItem; - PPH_NETWORK_ITEM lookupNetworkItemPtr = &lookupNetworkItem; - PPH_NETWORK_ITEM *networkItemPtr; - PPH_NETWORK_ITEM networkItem; - - lookupNetworkItem.ProtocolType = ProtocolType; - lookupNetworkItem.LocalEndpoint = *LocalEndpoint; - lookupNetworkItem.RemoteEndpoint = *RemoteEndpoint; - lookupNetworkItem.ProcessId = ProcessId; - - PhAcquireQueuedLockShared(&PhNetworkHashtableLock); - - networkItemPtr = (PPH_NETWORK_ITEM *)PhFindEntryHashtable( - PhNetworkHashtable, - &lookupNetworkItemPtr - ); - - if (networkItemPtr) - { - networkItem = *networkItemPtr; - PhReferenceObject(networkItem); - } - else - { - networkItem = NULL; - } - - PhReleaseQueuedLockShared(&PhNetworkHashtableLock); - - return networkItem; -} - -VOID PhpRemoveNetworkItem( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - PhRemoveEntryHashtable(PhNetworkHashtable, &NetworkItem); - PhDereferenceObject(NetworkItem); -} - -BOOLEAN PhpResolveCacheHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPHP_RESOLVE_CACHE_ITEM cacheItem1 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry1; - PPHP_RESOLVE_CACHE_ITEM cacheItem2 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry2; - - return PhEqualIpAddress(&cacheItem1->Address, &cacheItem2->Address); -} - -ULONG NTAPI PhpResolveCacheHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPHP_RESOLVE_CACHE_ITEM cacheItem = *(PPHP_RESOLVE_CACHE_ITEM *)Entry; - - return PhHashIpAddress(&cacheItem->Address); -} - -PPHP_RESOLVE_CACHE_ITEM PhpLookupResolveCacheItem( - _In_ PPH_IP_ADDRESS Address - ) -{ - PHP_RESOLVE_CACHE_ITEM lookupCacheItem; - PPHP_RESOLVE_CACHE_ITEM lookupCacheItemPtr = &lookupCacheItem; - PPHP_RESOLVE_CACHE_ITEM *cacheItemPtr; - - // Construct a temporary cache item for the lookup. - lookupCacheItem.Address = *Address; - - cacheItemPtr = (PPHP_RESOLVE_CACHE_ITEM *)PhFindEntryHashtable( - PhpResolveCacheHashtable, - &lookupCacheItemPtr - ); - - if (cacheItemPtr) - return *cacheItemPtr; - else - return NULL; -} - -PPH_STRING PhGetHostNameFromAddress( - _In_ PPH_IP_ADDRESS Address - ) -{ - struct sockaddr_in ipv4Address; - struct sockaddr_in6 ipv6Address; - struct sockaddr *address; - socklen_t length; - PPH_STRING hostName; - - if (Address->Type == PH_IPV4_NETWORK_TYPE) - { - ipv4Address.sin_family = AF_INET; - ipv4Address.sin_port = 0; - ipv4Address.sin_addr = Address->InAddr; - address = (struct sockaddr *)&ipv4Address; - length = sizeof(ipv4Address); - } - else if (Address->Type == PH_IPV6_NETWORK_TYPE) - { - ipv6Address.sin6_family = AF_INET6; - ipv6Address.sin6_port = 0; - ipv6Address.sin6_flowinfo = 0; - ipv6Address.sin6_addr = Address->In6Addr; - ipv6Address.sin6_scope_id = 0; - address = (struct sockaddr *)&ipv6Address; - length = sizeof(ipv6Address); - } - else - { - return NULL; - } - - hostName = PhCreateStringEx(NULL, 128); - - if (GetNameInfoW( - address, - length, - hostName->Buffer, - (ULONG)hostName->Length / 2 + 1, - NULL, - 0, - NI_NAMEREQD - ) != 0) - { - // Try with the maximum host name size. - PhDereferenceObject(hostName); - hostName = PhCreateStringEx(NULL, NI_MAXHOST * 2); - - if (GetNameInfoW( - address, - length, - hostName->Buffer, - (ULONG)hostName->Length / 2 + 1, - NULL, - 0, - NI_NAMEREQD - ) != 0) - { - PhDereferenceObject(hostName); - - return NULL; - } - } - - PhTrimToNullTerminatorString(hostName); - - return hostName; -} - -NTSTATUS PhpNetworkItemQueryWorker( - _In_ PVOID Parameter - ) -{ - PPH_NETWORK_ITEM_QUERY_DATA data = (PPH_NETWORK_ITEM_QUERY_DATA)Parameter; - PPH_STRING hostString; - PPHP_RESOLVE_CACHE_ITEM cacheItem; - - // Last minute check of the cache. - - PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock); - cacheItem = PhpLookupResolveCacheItem(&data->Address); - PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock); - - if (!cacheItem) - { - hostString = PhGetHostNameFromAddress(&data->Address); - - if (hostString) - { - data->HostString = hostString; - - // Update the cache. - - PhAcquireQueuedLockExclusive(&PhpResolveCacheHashtableLock); - - cacheItem = PhpLookupResolveCacheItem(&data->Address); - - if (!cacheItem) - { - cacheItem = PhAllocate(sizeof(PHP_RESOLVE_CACHE_ITEM)); - cacheItem->Address = data->Address; - cacheItem->HostString = hostString; - PhReferenceObject(hostString); - - PhAddEntryHashtable(PhpResolveCacheHashtable, &cacheItem); - } - - PhReleaseQueuedLockExclusive(&PhpResolveCacheHashtableLock); - } - } - else - { - PhReferenceObject(cacheItem->HostString); - data->HostString = cacheItem->HostString; - } - - RtlInterlockedPushEntrySList(&PhNetworkItemQueryListHead, &data->ListEntry); - - return STATUS_SUCCESS; -} - -VOID PhpQueueNetworkItemQuery( - _In_ PPH_NETWORK_ITEM NetworkItem, - _In_ BOOLEAN Remote - ) -{ - PPH_NETWORK_ITEM_QUERY_DATA data; - - if (!PhEnableNetworkProviderResolve) - return; - - data = PhAllocate(sizeof(PH_NETWORK_ITEM_QUERY_DATA)); - memset(data, 0, sizeof(PH_NETWORK_ITEM_QUERY_DATA)); - data->NetworkItem = NetworkItem; - data->Remote = Remote; - - if (Remote) - data->Address = NetworkItem->RemoteEndpoint.Address; - else - data->Address = NetworkItem->LocalEndpoint.Address; - - PhReferenceObject(NetworkItem); - - if (PhBeginInitOnce(&PhNetworkProviderWorkQueueInitOnce)) - { - PhInitializeWorkQueue(&PhNetworkProviderWorkQueue, 0, 3, 500); - PhEndInitOnce(&PhNetworkProviderWorkQueueInitOnce); - } - - PhQueueItemWorkQueue(&PhNetworkProviderWorkQueue, PhpNetworkItemQueryWorker, data); -} - -VOID PhpUpdateNetworkItemOwner( - _In_ PPH_NETWORK_ITEM NetworkItem, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - if (*(PULONG64)NetworkItem->OwnerInfo) - { - PVOID serviceTag; - PPH_STRING serviceName; - - // May change in the future... - serviceTag = UlongToPtr(*(PULONG)NetworkItem->OwnerInfo); - serviceName = PhGetServiceNameFromTag(NetworkItem->ProcessId, serviceTag); - - if (serviceName) - PhMoveReference(&NetworkItem->OwnerName, serviceName); - } -} - -VOID PhNetworkProviderUpdate( - _In_ PVOID Object - ) -{ - PPH_NETWORK_CONNECTION connections; - ULONG numberOfConnections; - ULONG i; - - if (!NetworkImportDone) - { - WSADATA wsaData; - - // Make sure WSA is initialized. - WSAStartup(WINSOCK_VERSION, &wsaData); - NetworkImportDone = TRUE; - } - - if (!PhGetNetworkConnections(&connections, &numberOfConnections)) - return; - - { - PPH_LIST connectionsToRemove = NULL; - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_NETWORK_ITEM *networkItem; - - PhBeginEnumHashtable(PhNetworkHashtable, &enumContext); - - while (networkItem = PhNextEnumHashtable(&enumContext)) - { - BOOLEAN found = FALSE; - - for (i = 0; i < numberOfConnections; i++) - { - if ( - (*networkItem)->ProtocolType == connections[i].ProtocolType && - PhEqualIpEndpoint(&(*networkItem)->LocalEndpoint, &connections[i].LocalEndpoint) && - PhEqualIpEndpoint(&(*networkItem)->RemoteEndpoint, &connections[i].RemoteEndpoint) && - (*networkItem)->ProcessId == connections[i].ProcessId - ) - { - found = TRUE; - break; - } - } - - if (!found) - { - PhInvokeCallback(&PhNetworkItemRemovedEvent, *networkItem); - - if (!connectionsToRemove) - connectionsToRemove = PhCreateList(2); - - PhAddItemList(connectionsToRemove, *networkItem); - } - } - - if (connectionsToRemove) - { - PhAcquireQueuedLockExclusive(&PhNetworkHashtableLock); - - for (i = 0; i < connectionsToRemove->Count; i++) - { - PhpRemoveNetworkItem(connectionsToRemove->Items[i]); - } - - PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock); - PhDereferenceObject(connectionsToRemove); - } - } - - // Go through the queued network item query data. - { - PSLIST_ENTRY entry; - PPH_NETWORK_ITEM_QUERY_DATA data; - - entry = RtlInterlockedFlushSList(&PhNetworkItemQueryListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_NETWORK_ITEM_QUERY_DATA, ListEntry); - entry = entry->Next; - - if (data->Remote) - PhMoveReference(&data->NetworkItem->RemoteHostString, data->HostString); - else - PhMoveReference(&data->NetworkItem->LocalHostString, data->HostString); - - data->NetworkItem->JustResolved = TRUE; - - PhDereferenceObject(data->NetworkItem); - PhFree(data); - } - } - - for (i = 0; i < numberOfConnections; i++) - { - PPH_NETWORK_ITEM networkItem; - - // Try to find the connection in our hashtable. - networkItem = PhReferenceNetworkItem( - connections[i].ProtocolType, - &connections[i].LocalEndpoint, - &connections[i].RemoteEndpoint, - connections[i].ProcessId - ); - - if (!networkItem) - { - PPHP_RESOLVE_CACHE_ITEM cacheItem; - PPH_PROCESS_ITEM processItem; - - // Network item not found, create it. - - networkItem = PhCreateNetworkItem(); - - // Fill in basic information. - networkItem->ProtocolType = connections[i].ProtocolType; - networkItem->LocalEndpoint = connections[i].LocalEndpoint; - networkItem->RemoteEndpoint = connections[i].RemoteEndpoint; - networkItem->State = connections[i].State; - networkItem->ProcessId = connections[i].ProcessId; - networkItem->CreateTime = connections[i].CreateTime; - memcpy(networkItem->OwnerInfo, connections[i].OwnerInfo, sizeof(ULONGLONG) * PH_NETWORK_OWNER_INFO_SIZE); - - // Format various strings. - - if (networkItem->LocalEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - RtlIpv4AddressToString(&networkItem->LocalEndpoint.Address.InAddr, networkItem->LocalAddressString); - else - RtlIpv6AddressToString(&networkItem->LocalEndpoint.Address.In6Addr, networkItem->LocalAddressString); - - PhPrintUInt32(networkItem->LocalPortString, networkItem->LocalEndpoint.Port); - - if ( - networkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE && - networkItem->RemoteEndpoint.Address.Ipv4 != 0 - ) - { - RtlIpv4AddressToString(&networkItem->RemoteEndpoint.Address.InAddr, networkItem->RemoteAddressString); - } - else if ( - networkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE && - !PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address) - ) - { - RtlIpv6AddressToString(&networkItem->RemoteEndpoint.Address.In6Addr, networkItem->RemoteAddressString); - } - - if (networkItem->RemoteEndpoint.Address.Type != 0 && networkItem->RemoteEndpoint.Port != 0) - PhPrintUInt32(networkItem->RemotePortString, networkItem->RemoteEndpoint.Port); - - // Get host names. - - // Local - { - PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock); - cacheItem = PhpLookupResolveCacheItem(&networkItem->LocalEndpoint.Address); - PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock); - - if (cacheItem) - { - PhReferenceObject(cacheItem->HostString); - networkItem->LocalHostString = cacheItem->HostString; - } - else - { - PhpQueueNetworkItemQuery(networkItem, FALSE); - } - } - - // Remote - if (!PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address)) - { - PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock); - cacheItem = PhpLookupResolveCacheItem(&networkItem->RemoteEndpoint.Address); - PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock); - - if (cacheItem) - { - PhReferenceObject(cacheItem->HostString); - networkItem->RemoteHostString = cacheItem->HostString; - } - else - { - PhpQueueNetworkItemQuery(networkItem, TRUE); - } - } - - // Get process information. - if (processItem = PhReferenceProcessItem(networkItem->ProcessId)) - { - networkItem->ProcessName = processItem->ProcessName; - PhReferenceObject(processItem->ProcessName); - PhpUpdateNetworkItemOwner(networkItem, processItem); - - if (PhTestEvent(&processItem->Stage1Event)) - { - networkItem->ProcessIcon = processItem->SmallIcon; - networkItem->ProcessIconValid = TRUE; - } - - PhDereferenceObject(processItem); - } - - // Add the network item to the hashtable. - PhAcquireQueuedLockExclusive(&PhNetworkHashtableLock); - PhAddEntryHashtable(PhNetworkHashtable, &networkItem); - PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock); - - // Raise the network item added event. - PhInvokeCallback(&PhNetworkItemAddedEvent, networkItem); - } - else - { - BOOLEAN modified = FALSE; - PPH_PROCESS_ITEM processItem; - - if (networkItem->JustResolved) - modified = TRUE; - - if (networkItem->State != connections[i].State) - { - networkItem->State = connections[i].State; - modified = TRUE; - } - - if (!networkItem->ProcessName || !networkItem->ProcessIconValid) - { - if (processItem = PhReferenceProcessItem(networkItem->ProcessId)) - { - if (!networkItem->ProcessName) - { - networkItem->ProcessName = processItem->ProcessName; - PhReferenceObject(processItem->ProcessName); - PhpUpdateNetworkItemOwner(networkItem, processItem); - modified = TRUE; - } - - if (!networkItem->ProcessIconValid && PhTestEvent(&processItem->Stage1Event)) - { - networkItem->ProcessIcon = processItem->SmallIcon; - networkItem->ProcessIconValid = TRUE; - modified = TRUE; - } - - PhDereferenceObject(processItem); - } - } - - networkItem->JustResolved = FALSE; - - if (modified) - { - // Raise the network item modified event. - PhInvokeCallback(&PhNetworkItemModifiedEvent, networkItem); - } - - PhDereferenceObject(networkItem); - } - } - - PhFree(connections); - - PhInvokeCallback(&PhNetworkItemsUpdatedEvent, NULL); -} - -PWSTR PhGetProtocolTypeName( - _In_ ULONG ProtocolType - ) -{ - switch (ProtocolType) - { - case PH_TCP4_NETWORK_PROTOCOL: - return L"TCP"; - case PH_TCP6_NETWORK_PROTOCOL: - return L"TCP6"; - case PH_UDP4_NETWORK_PROTOCOL: - return L"UDP"; - case PH_UDP6_NETWORK_PROTOCOL: - return L"UDP6"; - default: - return L"Unknown"; - } -} - -PWSTR PhGetTcpStateName( - _In_ ULONG State - ) -{ - switch (State) - { - case MIB_TCP_STATE_CLOSED: - return L"Closed"; - case MIB_TCP_STATE_LISTEN: - return L"Listen"; - case MIB_TCP_STATE_SYN_SENT: - return L"SYN sent"; - case MIB_TCP_STATE_SYN_RCVD: - return L"SYN received"; - case MIB_TCP_STATE_ESTAB: - return L"Established"; - case MIB_TCP_STATE_FIN_WAIT1: - return L"FIN wait 1"; - case MIB_TCP_STATE_FIN_WAIT2: - return L"FIN wait 2"; - case MIB_TCP_STATE_CLOSE_WAIT: - return L"Close wait"; - case MIB_TCP_STATE_CLOSING: - return L"Closing"; - case MIB_TCP_STATE_LAST_ACK: - return L"Last ACK"; - case MIB_TCP_STATE_TIME_WAIT: - return L"Time wait"; - case MIB_TCP_STATE_DELETE_TCB: - return L"Delete TCB"; - default: - return L"Unknown"; - } -} - -BOOLEAN PhGetNetworkConnections( - _Out_ PPH_NETWORK_CONNECTION *Connections, - _Out_ PULONG NumberOfConnections - ) -{ - PVOID table; - DWORD tableSize; - PMIB_TCPTABLE_OWNER_MODULE tcp4Table; - PMIB_UDPTABLE_OWNER_MODULE udp4Table; - PMIB_TCP6TABLE_OWNER_MODULE tcp6Table; - PMIB_UDP6TABLE_OWNER_MODULE udp6Table; - ULONG count = 0; - ULONG i; - ULONG index = 0; - PPH_NETWORK_CONNECTION connections; - - // TCP IPv4 - - tableSize = 0; - GetExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0); - table = PhAllocate(tableSize); - - if (GetExtendedTcpTable(table, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0) == 0) - { - tcp4Table = table; - count += tcp4Table->dwNumEntries; - } - else - { - PhFree(table); - tcp4Table = NULL; - } - - // TCP IPv6 - - tableSize = 0; - GetExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0); - - // Note: On Windows XP, GetExtendedTcpTable had a bug where it calculated the required buffer size - // for IPv6 TCP_TABLE_OWNER_MODULE_ALL requests incorrectly, causing it to return the wrong size - // and overrun the provided buffer instead of returning an error. The size should be: - // = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + sizeof(MIB_TCP6ROW_OWNER_MODULE) * (number of entries) - // However, the function calculated it as: - // = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + sizeof(MIB_TCP6ROW_OWNER_PID) * (number of entries) - // A workaround is implemented below. - if (WindowsVersion <= WINDOWS_XP && tableSize >= (ULONG)FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table)) // make sure we don't wrap around - { - tableSize = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + - (tableSize - FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table)) / sizeof(MIB_TCP6ROW_OWNER_PID) * sizeof(MIB_TCP6ROW_OWNER_MODULE); - } - - table = PhAllocate(tableSize); - - if (GetExtendedTcpTable(table, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0) == 0) - { - tcp6Table = table; - count += tcp6Table->dwNumEntries; - } - else - { - PhFree(table); - tcp6Table = NULL; - } - - // UDP IPv4 - - tableSize = 0; - GetExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0); - table = PhAllocate(tableSize); - - if (GetExtendedUdpTable(table, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0) == 0) - { - udp4Table = table; - count += udp4Table->dwNumEntries; - } - else - { - PhFree(table); - udp4Table = NULL; - } - - // UDP IPv6 - - tableSize = 0; - GetExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0); - table = PhAllocate(tableSize); - - if (GetExtendedUdpTable(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0) == 0) - { - udp6Table = table; - count += udp6Table->dwNumEntries; - } - else - { - PhFree(table); - udp6Table = NULL; - } - - connections = PhAllocate(sizeof(PH_NETWORK_CONNECTION) * count); - memset(connections, 0, sizeof(PH_NETWORK_CONNECTION) * count); - - if (tcp4Table) - { - for (i = 0; i < tcp4Table->dwNumEntries; i++) - { - connections[index].ProtocolType = PH_TCP4_NETWORK_PROTOCOL; - - connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - connections[index].LocalEndpoint.Address.Ipv4 = tcp4Table->table[i].dwLocalAddr; - connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwLocalPort); - - connections[index].RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - connections[index].RemoteEndpoint.Address.Ipv4 = tcp4Table->table[i].dwRemoteAddr; - connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwRemotePort); - - connections[index].State = tcp4Table->table[i].dwState; - connections[index].ProcessId = UlongToHandle(tcp4Table->table[i].dwOwningPid); - connections[index].CreateTime = tcp4Table->table[i].liCreateTimestamp; - memcpy( - connections[index].OwnerInfo, - tcp4Table->table[i].OwningModuleInfo, - sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) - ); - - index++; - } - - PhFree(tcp4Table); - } - - if (tcp6Table) - { - for (i = 0; i < tcp6Table->dwNumEntries; i++) - { - connections[index].ProtocolType = PH_TCP6_NETWORK_PROTOCOL; - - connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - memcpy(connections[index].LocalEndpoint.Address.Ipv6, tcp6Table->table[i].ucLocalAddr, 16); - connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwLocalPort); - - connections[index].RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - memcpy(connections[index].RemoteEndpoint.Address.Ipv6, tcp6Table->table[i].ucRemoteAddr, 16); - connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwRemotePort); - - connections[index].State = tcp6Table->table[i].dwState; - connections[index].ProcessId = UlongToHandle(tcp6Table->table[i].dwOwningPid); - connections[index].CreateTime = tcp6Table->table[i].liCreateTimestamp; - memcpy( - connections[index].OwnerInfo, - tcp6Table->table[i].OwningModuleInfo, - sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) - ); - - index++; - } - - PhFree(tcp6Table); - } - - if (udp4Table) - { - for (i = 0; i < udp4Table->dwNumEntries; i++) - { - connections[index].ProtocolType = PH_UDP4_NETWORK_PROTOCOL; - - connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - connections[index].LocalEndpoint.Address.Ipv4 = udp4Table->table[i].dwLocalAddr; - connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp4Table->table[i].dwLocalPort); - - connections[index].RemoteEndpoint.Address.Type = 0; - - connections[index].State = 0; - connections[index].ProcessId = UlongToHandle(udp4Table->table[i].dwOwningPid); - connections[index].CreateTime = udp4Table->table[i].liCreateTimestamp; - memcpy( - connections[index].OwnerInfo, - udp4Table->table[i].OwningModuleInfo, - sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) - ); - - index++; - } - - PhFree(udp4Table); - } - - if (udp6Table) - { - for (i = 0; i < udp6Table->dwNumEntries; i++) - { - connections[index].ProtocolType = PH_UDP6_NETWORK_PROTOCOL; - - connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - memcpy(connections[index].LocalEndpoint.Address.Ipv6, udp6Table->table[i].ucLocalAddr, 16); - connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp6Table->table[i].dwLocalPort); - - connections[index].RemoteEndpoint.Address.Type = 0; - - connections[index].State = 0; - connections[index].ProcessId = UlongToHandle(udp6Table->table[i].dwOwningPid); - connections[index].CreateTime = udp6Table->table[i].liCreateTimestamp; - memcpy( - connections[index].OwnerInfo, - udp6Table->table[i].OwningModuleInfo, - sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) - ); - - index++; - } - - PhFree(udp6Table); - } - - *NumberOfConnections = count; - *Connections = connections; - - return TRUE; -} +/* + * Process Hacker - + * network provider + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2010 evilpie + * Copyright (C) 2016-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +typedef struct _PH_NETWORK_CONNECTION +{ + ULONG ProtocolType; + PH_IP_ENDPOINT LocalEndpoint; + PH_IP_ENDPOINT RemoteEndpoint; + ULONG State; + HANDLE ProcessId; + LARGE_INTEGER CreateTime; + ULONGLONG OwnerInfo[PH_NETWORK_OWNER_INFO_SIZE]; + ULONG LocalScopeId; // Ipv6 + ULONG RemoteScopeId; // Ipv6 +} PH_NETWORK_CONNECTION, *PPH_NETWORK_CONNECTION; + +typedef struct _PH_NETWORK_ITEM_QUERY_DATA +{ + SLIST_ENTRY ListEntry; + PPH_NETWORK_ITEM NetworkItem; + + PH_IP_ADDRESS Address; + BOOLEAN Remote; + PPH_STRING HostString; +} PH_NETWORK_ITEM_QUERY_DATA, *PPH_NETWORK_ITEM_QUERY_DATA; + +typedef struct _PHP_RESOLVE_CACHE_ITEM +{ + PH_IP_ADDRESS Address; + PPH_STRING HostString; +} PHP_RESOLVE_CACHE_ITEM, *PPHP_RESOLVE_CACHE_ITEM; + +VOID NTAPI PhpNetworkItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +BOOLEAN PhpNetworkHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpNetworkHashtableHashFunction( + _In_ PVOID Entry + ); + +BOOLEAN PhpResolveCacheHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpResolveCacheHashtableHashFunction( + _In_ PVOID Entry + ); + +BOOLEAN PhGetNetworkConnections( + _Out_ PPH_NETWORK_CONNECTION *Connections, + _Out_ PULONG NumberOfConnections + ); + +PPH_OBJECT_TYPE PhNetworkItemType = NULL; +PPH_HASHTABLE PhNetworkHashtable = NULL; +PH_QUEUED_LOCK PhNetworkHashtableLock = PH_QUEUED_LOCK_INIT; + +PH_INITONCE PhNetworkProviderWorkQueueInitOnce = PH_INITONCE_INIT; +PH_WORK_QUEUE PhNetworkProviderWorkQueue; +SLIST_HEADER PhNetworkItemQueryListHead; + +BOOLEAN PhEnableNetworkProviderResolve = TRUE; +static BOOLEAN NetworkImportDone = FALSE; +static PPH_HASHTABLE PhpResolveCacheHashtable = NULL; +static PH_QUEUED_LOCK PhpResolveCacheHashtableLock = PH_QUEUED_LOCK_INIT; + +BOOLEAN PhNetworkProviderInitialization( + VOID + ) +{ + PhNetworkItemType = PhCreateObjectType(L"NetworkItem", 0, PhpNetworkItemDeleteProcedure); + PhNetworkHashtable = PhCreateHashtable( + sizeof(PPH_NETWORK_ITEM), + PhpNetworkHashtableEqualFunction, + PhpNetworkHashtableHashFunction, + 40 + ); + + RtlInitializeSListHead(&PhNetworkItemQueryListHead); + + PhpResolveCacheHashtable = PhCreateHashtable( + sizeof(PHP_RESOLVE_CACHE_ITEM), + PhpResolveCacheHashtableEqualFunction, + PhpResolveCacheHashtableHashFunction, + 20 + ); + + return TRUE; +} + +PPH_NETWORK_ITEM PhCreateNetworkItem( + VOID + ) +{ + PPH_NETWORK_ITEM networkItem; + + networkItem = PhCreateObject( + PhEmGetObjectSize(EmNetworkItemType, sizeof(PH_NETWORK_ITEM)), + PhNetworkItemType + ); + memset(networkItem, 0, sizeof(PH_NETWORK_ITEM)); + PhEmCallObjectOperation(EmNetworkItemType, networkItem, EmObjectCreate); + + return networkItem; +} + +VOID NTAPI PhpNetworkItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Object; + + PhEmCallObjectOperation(EmNetworkItemType, networkItem, EmObjectDelete); + + if (networkItem->ProcessName) + PhDereferenceObject(networkItem->ProcessName); + if (networkItem->OwnerName) + PhDereferenceObject(networkItem->OwnerName); + if (networkItem->LocalHostString) + PhDereferenceObject(networkItem->LocalHostString); + if (networkItem->RemoteHostString) + PhDereferenceObject(networkItem->RemoteHostString); + + // NOTE: Dereferencing the ProcessItem will destroy the NetworkItem->ProcessIcon handle. + if (networkItem->ProcessItem) + PhDereferenceObject(networkItem->ProcessItem); +} + +BOOLEAN PhpNetworkHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_NETWORK_ITEM networkItem1 = *(PPH_NETWORK_ITEM *)Entry1; + PPH_NETWORK_ITEM networkItem2 = *(PPH_NETWORK_ITEM *)Entry2; + + return + networkItem1->ProtocolType == networkItem2->ProtocolType && + PhEqualIpEndpoint(&networkItem1->LocalEndpoint, &networkItem2->LocalEndpoint) && + PhEqualIpEndpoint(&networkItem1->RemoteEndpoint, &networkItem2->RemoteEndpoint) && + networkItem1->ProcessId == networkItem2->ProcessId; +} + +ULONG NTAPI PhpNetworkHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_NETWORK_ITEM networkItem = *(PPH_NETWORK_ITEM *)Entry; + + return + networkItem->ProtocolType ^ + PhHashIpEndpoint(&networkItem->LocalEndpoint) ^ + PhHashIpEndpoint(&networkItem->RemoteEndpoint) ^ + (HandleToUlong(networkItem->ProcessId) / 4); +} + +PPH_NETWORK_ITEM PhReferenceNetworkItem( + _In_ ULONG ProtocolType, + _In_ PPH_IP_ENDPOINT LocalEndpoint, + _In_ PPH_IP_ENDPOINT RemoteEndpoint, + _In_ HANDLE ProcessId + ) +{ + PH_NETWORK_ITEM lookupNetworkItem; + PPH_NETWORK_ITEM lookupNetworkItemPtr = &lookupNetworkItem; + PPH_NETWORK_ITEM *networkItemPtr; + PPH_NETWORK_ITEM networkItem; + + lookupNetworkItem.ProtocolType = ProtocolType; + lookupNetworkItem.LocalEndpoint = *LocalEndpoint; + lookupNetworkItem.RemoteEndpoint = *RemoteEndpoint; + lookupNetworkItem.ProcessId = ProcessId; + + PhAcquireQueuedLockShared(&PhNetworkHashtableLock); + + networkItemPtr = (PPH_NETWORK_ITEM *)PhFindEntryHashtable( + PhNetworkHashtable, + &lookupNetworkItemPtr + ); + + if (networkItemPtr) + { + networkItem = *networkItemPtr; + PhReferenceObject(networkItem); + } + else + { + networkItem = NULL; + } + + PhReleaseQueuedLockShared(&PhNetworkHashtableLock); + + return networkItem; +} + +VOID PhpRemoveNetworkItem( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PhRemoveEntryHashtable(PhNetworkHashtable, &NetworkItem); + PhDereferenceObject(NetworkItem); +} + +BOOLEAN PhpResolveCacheHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPHP_RESOLVE_CACHE_ITEM cacheItem1 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry1; + PPHP_RESOLVE_CACHE_ITEM cacheItem2 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry2; + + return PhEqualIpAddress(&cacheItem1->Address, &cacheItem2->Address); +} + +ULONG NTAPI PhpResolveCacheHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPHP_RESOLVE_CACHE_ITEM cacheItem = *(PPHP_RESOLVE_CACHE_ITEM *)Entry; + + return PhHashIpAddress(&cacheItem->Address); +} + +PPHP_RESOLVE_CACHE_ITEM PhpLookupResolveCacheItem( + _In_ PPH_IP_ADDRESS Address + ) +{ + PHP_RESOLVE_CACHE_ITEM lookupCacheItem; + PPHP_RESOLVE_CACHE_ITEM lookupCacheItemPtr = &lookupCacheItem; + PPHP_RESOLVE_CACHE_ITEM *cacheItemPtr; + + // Construct a temporary cache item for the lookup. + lookupCacheItem.Address = *Address; + + cacheItemPtr = (PPHP_RESOLVE_CACHE_ITEM *)PhFindEntryHashtable( + PhpResolveCacheHashtable, + &lookupCacheItemPtr + ); + + if (cacheItemPtr) + return *cacheItemPtr; + else + return NULL; +} + +//PPH_STRING PhGetHostNameFromAddress( +// _In_ PPH_IP_ADDRESS Address +// ) +//{ +// SOCKADDR_IN ipv4Address; +// SOCKADDR_IN6 ipv6Address; +// PSOCKADDR address; +// socklen_t length; +// PPH_STRING hostName; +// +// if (Address->Type == PH_IPV4_NETWORK_TYPE) +// { +// ipv4Address.sin_family = AF_INET; +// ipv4Address.sin_port = 0; +// ipv4Address.sin_addr = Address->InAddr; +// address = (PSOCKADDR)&ipv4Address; +// length = sizeof(ipv4Address); +// } +// else if (Address->Type == PH_IPV6_NETWORK_TYPE) +// { +// ipv6Address.sin6_family = AF_INET6; +// ipv6Address.sin6_port = 0; +// ipv6Address.sin6_flowinfo = 0; +// ipv6Address.sin6_addr = Address->In6Addr; +// ipv6Address.sin6_scope_id = 0; +// address = (PSOCKADDR)&ipv6Address; +// length = sizeof(ipv6Address); +// } +// else +// { +// return NULL; +// } +// +// hostName = PhCreateStringEx(NULL, 128); +// +// if (GetNameInfo( +// address, +// length, +// hostName->Buffer, +// (ULONG)hostName->Length / sizeof(WCHAR) + 1, +// NULL, +// 0, +// NI_NAMEREQD +// ) != 0) +// { +// // Try with the maximum host name size. +// PhDereferenceObject(hostName); +// hostName = PhCreateStringEx(NULL, NI_MAXHOST * sizeof(WCHAR)); +// +// if (GetNameInfo( +// address, +// length, +// hostName->Buffer, +// (ULONG)hostName->Length / sizeof(WCHAR) + 1, +// NULL, +// 0, +// NI_NAMEREQD +// ) != 0) +// { +// PhDereferenceObject(hostName); +// +// return NULL; +// } +// } +// +// PhTrimToNullTerminatorString(hostName); +// +// return hostName; +//} + +PPH_STRING PhpGetIp4ReverseNameFromAddress( + _In_ IN_ADDR Address + ) +{ + return PhFormatString( + L"%hhu.%hhu.%hhu.%hhu.%s", + Address.s_impno, + Address.s_lh, + Address.s_host, + Address.s_net, + DNS_IP4_REVERSE_DOMAIN_STRING_W + ); +} + +PPH_STRING PhpGetIp6ReverseNameFromAddress( + _In_ IN6_ADDR Address + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, DNS_MAX_NAME_BUFFER_LENGTH); + + for (INT i = sizeof(IN6_ADDR) - 1; i >= 0; i--) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%hhx.%hhx.", + Address.s6_addr[i] & 0xF, + (Address.s6_addr[i] >> 4) & 0xF + ); + } + + PhAppendStringBuilder2(&stringBuilder, DNS_IP6_REVERSE_DOMAIN_STRING_W); + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_STRING PhGetHostNameFromAddressEx( + _In_ PPH_IP_ADDRESS Address + ) +{ + PPH_STRING addressHostName = NULL; + PPH_STRING addressReverse = NULL; + PDNS_RECORD addressResults = NULL; + + if (Address->Type == PH_IPV4_NETWORK_TYPE) + { + addressReverse = PhpGetIp4ReverseNameFromAddress(Address->InAddr); + } + else if (Address->Type == PH_IPV6_NETWORK_TYPE) + { + addressReverse = PhpGetIp6ReverseNameFromAddress(Address->In6Addr); + } + else + { + return NULL; + } + + DnsQuery( + addressReverse->Buffer, + DNS_TYPE_PTR, + DNS_QUERY_NO_HOSTS_FILE, // DNS_QUERY_BYPASS_CACHE + NULL, + &addressResults, + NULL + ); + + if (addressResults) + { + addressHostName = PhCreateString(addressResults->Data.PTR.pNameHost); // Return the first result (dmex) + DnsRecordListFree(addressResults, DnsFreeRecordList); + } + + PhDereferenceObject(addressReverse); + + return addressHostName; +} + +NTSTATUS PhpNetworkItemQueryWorker( + _In_ PVOID Parameter + ) +{ + PPH_NETWORK_ITEM_QUERY_DATA data = (PPH_NETWORK_ITEM_QUERY_DATA)Parameter; + PPH_STRING hostString; + PPHP_RESOLVE_CACHE_ITEM cacheItem; + + // Last minute check of the cache. + + PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock); + cacheItem = PhpLookupResolveCacheItem(&data->Address); + PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock); + + if (!cacheItem) + { + hostString = PhGetHostNameFromAddressEx(&data->Address); + + if (hostString) + { + data->HostString = hostString; + + // Update the cache. + + PhAcquireQueuedLockExclusive(&PhpResolveCacheHashtableLock); + + cacheItem = PhpLookupResolveCacheItem(&data->Address); + + if (!cacheItem) + { + cacheItem = PhAllocate(sizeof(PHP_RESOLVE_CACHE_ITEM)); + cacheItem->Address = data->Address; + cacheItem->HostString = hostString; + PhReferenceObject(hostString); + + PhAddEntryHashtable(PhpResolveCacheHashtable, &cacheItem); + } + + PhReleaseQueuedLockExclusive(&PhpResolveCacheHashtableLock); + } + } + else + { + PhReferenceObject(cacheItem->HostString); + data->HostString = cacheItem->HostString; + } + + RtlInterlockedPushEntrySList(&PhNetworkItemQueryListHead, &data->ListEntry); + + return STATUS_SUCCESS; +} + +VOID PhpQueueNetworkItemQuery( + _In_ PPH_NETWORK_ITEM NetworkItem, + _In_ BOOLEAN Remote + ) +{ + PPH_NETWORK_ITEM_QUERY_DATA data; + + if (!PhEnableNetworkProviderResolve) + return; + + data = PhAllocate(sizeof(PH_NETWORK_ITEM_QUERY_DATA)); + memset(data, 0, sizeof(PH_NETWORK_ITEM_QUERY_DATA)); + data->NetworkItem = NetworkItem; + data->Remote = Remote; + + if (Remote) + data->Address = NetworkItem->RemoteEndpoint.Address; + else + data->Address = NetworkItem->LocalEndpoint.Address; + + PhReferenceObject(NetworkItem); + + if (PhBeginInitOnce(&PhNetworkProviderWorkQueueInitOnce)) + { + PhInitializeWorkQueue(&PhNetworkProviderWorkQueue, 0, 3, 500); + PhEndInitOnce(&PhNetworkProviderWorkQueueInitOnce); + } + + PhQueueItemWorkQueue(&PhNetworkProviderWorkQueue, PhpNetworkItemQueryWorker, data); +} + +VOID PhpUpdateNetworkItemOwner( + _In_ PPH_NETWORK_ITEM NetworkItem, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + if (*(PULONG64)NetworkItem->OwnerInfo) + { + PVOID serviceTag; + PPH_STRING serviceName; + + // May change in the future... + serviceTag = UlongToPtr(*(PULONG)NetworkItem->OwnerInfo); + serviceName = PhGetServiceNameFromTag(NetworkItem->ProcessId, serviceTag); + + if (serviceName) + PhMoveReference(&NetworkItem->OwnerName, serviceName); + } +} + +VOID PhFlushNetworkQueryData( + VOID + ) +{ + PSLIST_ENTRY entry; + PPH_NETWORK_ITEM_QUERY_DATA data; + + if (!RtlFirstEntrySList(&PhNetworkItemQueryListHead)) + return; + + entry = RtlInterlockedFlushSList(&PhNetworkItemQueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_NETWORK_ITEM_QUERY_DATA, ListEntry); + entry = entry->Next; + + if (data->Remote) + PhMoveReference(&data->NetworkItem->RemoteHostString, data->HostString); + else + PhMoveReference(&data->NetworkItem->LocalHostString, data->HostString); + + data->NetworkItem->JustResolved = TRUE; + + PhDereferenceObject(data->NetworkItem); + PhFree(data); + } +} + +VOID PhNetworkProviderUpdate( + _In_ PVOID Object + ) +{ + PPH_NETWORK_CONNECTION connections; + ULONG numberOfConnections; + ULONG i; + + if (!NetworkImportDone) + { + WSADATA wsaData; + // Make sure WSA is initialized. (wj32) + WSAStartup(WINSOCK_VERSION, &wsaData); + NetworkImportDone = TRUE; + } + + if (!PhGetNetworkConnections(&connections, &numberOfConnections)) + return; + + // Look for closed connections. + { + PPH_LIST connectionsToRemove = NULL; + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_NETWORK_ITEM *networkItem; + + PhBeginEnumHashtable(PhNetworkHashtable, &enumContext); + + while (networkItem = PhNextEnumHashtable(&enumContext)) + { + BOOLEAN found = FALSE; + + for (i = 0; i < numberOfConnections; i++) + { + if ( + (*networkItem)->ProtocolType == connections[i].ProtocolType && + PhEqualIpEndpoint(&(*networkItem)->LocalEndpoint, &connections[i].LocalEndpoint) && + PhEqualIpEndpoint(&(*networkItem)->RemoteEndpoint, &connections[i].RemoteEndpoint) && + (*networkItem)->ProcessId == connections[i].ProcessId + ) + { + found = TRUE; + break; + } + } + + if (!found) + { + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderRemovedEvent), *networkItem); + + if (!connectionsToRemove) + connectionsToRemove = PhCreateList(2); + + PhAddItemList(connectionsToRemove, *networkItem); + } + } + + if (connectionsToRemove) + { + PhAcquireQueuedLockExclusive(&PhNetworkHashtableLock); + + for (i = 0; i < connectionsToRemove->Count; i++) + { + PhpRemoveNetworkItem(connectionsToRemove->Items[i]); + } + + PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock); + PhDereferenceObject(connectionsToRemove); + } + } + + // Go through the queued network item query data. + PhFlushNetworkQueryData(); + + // Look for new network connections and update existing ones. + for (i = 0; i < numberOfConnections; i++) + { + PPH_NETWORK_ITEM networkItem; + + // Try to find the connection in our hashtable. + networkItem = PhReferenceNetworkItem( + connections[i].ProtocolType, + &connections[i].LocalEndpoint, + &connections[i].RemoteEndpoint, + connections[i].ProcessId + ); + + if (!networkItem) + { + PPHP_RESOLVE_CACHE_ITEM cacheItem; + PPH_PROCESS_ITEM processItem; + + // Network item not found, create it. + + networkItem = PhCreateNetworkItem(); + + // Fill in basic information. + networkItem->ProtocolType = connections[i].ProtocolType; + networkItem->LocalEndpoint = connections[i].LocalEndpoint; + networkItem->RemoteEndpoint = connections[i].RemoteEndpoint; + networkItem->State = connections[i].State; + networkItem->ProcessId = connections[i].ProcessId; + networkItem->CreateTime = connections[i].CreateTime; + memcpy(networkItem->OwnerInfo, connections[i].OwnerInfo, sizeof(ULONGLONG) * PH_NETWORK_OWNER_INFO_SIZE); + networkItem->LocalScopeId = connections[i].LocalScopeId; + networkItem->RemoteScopeId = connections[i].RemoteScopeId; + + // Format various strings. + + if (networkItem->LocalEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + RtlIpv4AddressToString(&networkItem->LocalEndpoint.Address.InAddr, networkItem->LocalAddressString); + else + RtlIpv6AddressToString(&networkItem->LocalEndpoint.Address.In6Addr, networkItem->LocalAddressString); + + PhPrintUInt32(networkItem->LocalPortString, networkItem->LocalEndpoint.Port); + + if ( + networkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE && + networkItem->RemoteEndpoint.Address.Ipv4 != 0 + ) + { + RtlIpv4AddressToString(&networkItem->RemoteEndpoint.Address.InAddr, networkItem->RemoteAddressString); + } + else if ( + networkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE && + !PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address) + ) + { + RtlIpv6AddressToString(&networkItem->RemoteEndpoint.Address.In6Addr, networkItem->RemoteAddressString); + } + + if (networkItem->RemoteEndpoint.Address.Type != 0 && networkItem->RemoteEndpoint.Port != 0) + PhPrintUInt32(networkItem->RemotePortString, networkItem->RemoteEndpoint.Port); + + // Get host names. + + // Local + { + PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock); + cacheItem = PhpLookupResolveCacheItem(&networkItem->LocalEndpoint.Address); + PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock); + + if (cacheItem) + { + PhReferenceObject(cacheItem->HostString); + networkItem->LocalHostString = cacheItem->HostString; + } + else + { + PhpQueueNetworkItemQuery(networkItem, FALSE); + } + } + + // Remote + if (!PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address)) + { + PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock); + cacheItem = PhpLookupResolveCacheItem(&networkItem->RemoteEndpoint.Address); + PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock); + + if (cacheItem) + { + PhReferenceObject(cacheItem->HostString); + networkItem->RemoteHostString = cacheItem->HostString; + } + else + { + PhpQueueNetworkItemQuery(networkItem, TRUE); + } + } + + // Get process information. + if (processItem = PhReferenceProcessItem(networkItem->ProcessId)) + { + networkItem->ProcessItem = processItem; + PhSetReference(&networkItem->ProcessName, processItem->ProcessName); + networkItem->SubsystemProcess = !!processItem->IsSubsystemProcess; + PhpUpdateNetworkItemOwner(networkItem, processItem); + + if (PhTestEvent(&processItem->Stage1Event)) + { + networkItem->ProcessIcon = processItem->SmallIcon; + networkItem->ProcessIconValid = TRUE; + } + + // NOTE: We dereference processItem in PhpNetworkItemDeleteProcedure. (dmex) + } + else + { + HANDLE processHandle; + PPH_STRING fileName; + PROCESS_EXTENDED_BASIC_INFORMATION basicInfo; + + // HACK HACK HACK + // WSL subsystem processes (e.g. apache/nginx) create sockets, clone/fork themselves, duplicate the socket into the child process and then terminate. + // The socket handle remains valid and in-use by the child process BUT the socket continues returning the PID of the exited process??? + // Fixing this causes a major performance problem; If we have 100,000 sockets then on previous versions of Windows we would only need 2 system calls maximum + // (for the process list) to identify the owner of every socket but now we need to make 4 system calls for every_last_socket totaling 400,000 system calls... great. (dmex) + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, networkItem->ProcessId))) + { + if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandle, &basicInfo))) + { + networkItem->SubsystemProcess = !!basicInfo.IsSubsystemProcess; + } + + if (NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName))) + { + PhMoveReference(&networkItem->ProcessName, PhGetBaseName(fileName)); + } + + NtClose(processHandle); + } + + networkItem->UnknownProcess = TRUE; + } + + // Add the network item to the hashtable. + PhAcquireQueuedLockExclusive(&PhNetworkHashtableLock); + PhAddEntryHashtable(PhNetworkHashtable, &networkItem); + PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock); + + // Raise the network item added event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderAddedEvent), networkItem); + } + else + { + BOOLEAN modified = FALSE; + + if (InterlockedExchange(&networkItem->JustResolved, 0) != 0) + modified = TRUE; + + if (networkItem->State != connections[i].State) + { + networkItem->State = connections[i].State; + modified = TRUE; + } + + if (!networkItem->ProcessItem) + { + networkItem->ProcessItem = PhReferenceProcessItem(networkItem->ProcessId); + // NOTE: We dereference processItem in PhpNetworkItemDeleteProcedure. (dmex) + } + + if (networkItem->ProcessItem) + { + if (!networkItem->ProcessName) + { + networkItem->ProcessName = PhReferenceObject(networkItem->ProcessItem->ProcessName); + PhpUpdateNetworkItemOwner(networkItem, networkItem->ProcessItem); + modified = TRUE; + } + + if (!networkItem->ProcessIconValid && PhTestEvent(&networkItem->ProcessItem->Stage1Event)) + { + networkItem->ProcessIcon = networkItem->ProcessItem->SmallIcon; + networkItem->ProcessIconValid = TRUE; + modified = TRUE; + } + } + + if (modified) + { + // Raise the network item modified event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderModifiedEvent), networkItem); + } + + PhDereferenceObject(networkItem); + } + } + + PhFree(connections); + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdatedEvent), NULL); +} + +PWSTR PhGetProtocolTypeName( + _In_ ULONG ProtocolType + ) +{ + switch (ProtocolType) + { + case PH_TCP4_NETWORK_PROTOCOL: + return L"TCP"; + case PH_TCP6_NETWORK_PROTOCOL: + return L"TCP6"; + case PH_UDP4_NETWORK_PROTOCOL: + return L"UDP"; + case PH_UDP6_NETWORK_PROTOCOL: + return L"UDP6"; + default: + return L"Unknown"; + } +} + +PWSTR PhGetTcpStateName( + _In_ ULONG State + ) +{ + switch (State) + { + case MIB_TCP_STATE_CLOSED: + return L"Closed"; + case MIB_TCP_STATE_LISTEN: + return L"Listen"; + case MIB_TCP_STATE_SYN_SENT: + return L"SYN sent"; + case MIB_TCP_STATE_SYN_RCVD: + return L"SYN received"; + case MIB_TCP_STATE_ESTAB: + return L"Established"; + case MIB_TCP_STATE_FIN_WAIT1: + return L"FIN wait 1"; + case MIB_TCP_STATE_FIN_WAIT2: + return L"FIN wait 2"; + case MIB_TCP_STATE_CLOSE_WAIT: + return L"Close wait"; + case MIB_TCP_STATE_CLOSING: + return L"Closing"; + case MIB_TCP_STATE_LAST_ACK: + return L"Last ACK"; + case MIB_TCP_STATE_TIME_WAIT: + return L"Time wait"; + case MIB_TCP_STATE_DELETE_TCB: + return L"Delete TCB"; + default: + return L"Unknown"; + } +} + +BOOLEAN PhGetNetworkConnections( + _Out_ PPH_NETWORK_CONNECTION *Connections, + _Out_ PULONG NumberOfConnections + ) +{ + PVOID table; + ULONG tableSize; + PMIB_TCPTABLE_OWNER_MODULE tcp4Table; + PMIB_UDPTABLE_OWNER_MODULE udp4Table; + PMIB_TCP6TABLE_OWNER_MODULE tcp6Table; + PMIB_UDP6TABLE_OWNER_MODULE udp6Table; + ULONG count = 0; + ULONG i; + ULONG index = 0; + PPH_NETWORK_CONNECTION connections; + + // TCP IPv4 + + tableSize = 0; + GetExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0); + table = PhAllocate(tableSize); + + if (GetExtendedTcpTable(table, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0) == NO_ERROR) + { + tcp4Table = table; + count += tcp4Table->dwNumEntries; + } + else + { + PhFree(table); + tcp4Table = NULL; + } + + // TCP IPv6 + + tableSize = 0; + GetExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0); + + table = PhAllocate(tableSize); + + if (GetExtendedTcpTable(table, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0) == NO_ERROR) + { + tcp6Table = table; + count += tcp6Table->dwNumEntries; + } + else + { + PhFree(table); + tcp6Table = NULL; + } + + // UDP IPv4 + + tableSize = 0; + GetExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0); + table = PhAllocate(tableSize); + + if (GetExtendedUdpTable(table, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0) == NO_ERROR) + { + udp4Table = table; + count += udp4Table->dwNumEntries; + } + else + { + PhFree(table); + udp4Table = NULL; + } + + // UDP IPv6 + + tableSize = 0; + GetExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0); + table = PhAllocate(tableSize); + + if (GetExtendedUdpTable(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0) == NO_ERROR) + { + udp6Table = table; + count += udp6Table->dwNumEntries; + } + else + { + PhFree(table); + udp6Table = NULL; + } + + connections = PhAllocate(sizeof(PH_NETWORK_CONNECTION) * count); + memset(connections, 0, sizeof(PH_NETWORK_CONNECTION) * count); + + if (tcp4Table) + { + for (i = 0; i < tcp4Table->dwNumEntries; i++) + { + connections[index].ProtocolType = PH_TCP4_NETWORK_PROTOCOL; + + connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; + connections[index].LocalEndpoint.Address.Ipv4 = tcp4Table->table[i].dwLocalAddr; + connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwLocalPort); + + connections[index].RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; + connections[index].RemoteEndpoint.Address.Ipv4 = tcp4Table->table[i].dwRemoteAddr; + connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwRemotePort); + + connections[index].State = tcp4Table->table[i].dwState; + connections[index].ProcessId = UlongToHandle(tcp4Table->table[i].dwOwningPid); + connections[index].CreateTime = tcp4Table->table[i].liCreateTimestamp; + memcpy( + connections[index].OwnerInfo, + tcp4Table->table[i].OwningModuleInfo, + sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) + ); + + index++; + } + + PhFree(tcp4Table); + } + + if (tcp6Table) + { + for (i = 0; i < tcp6Table->dwNumEntries; i++) + { + connections[index].ProtocolType = PH_TCP6_NETWORK_PROTOCOL; + + connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; + memcpy(connections[index].LocalEndpoint.Address.Ipv6, tcp6Table->table[i].ucLocalAddr, 16); + connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwLocalPort); + + connections[index].RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; + memcpy(connections[index].RemoteEndpoint.Address.Ipv6, tcp6Table->table[i].ucRemoteAddr, 16); + connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwRemotePort); + + connections[index].State = tcp6Table->table[i].dwState; + connections[index].ProcessId = UlongToHandle(tcp6Table->table[i].dwOwningPid); + connections[index].CreateTime = tcp6Table->table[i].liCreateTimestamp; + memcpy( + connections[index].OwnerInfo, + tcp6Table->table[i].OwningModuleInfo, + sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) + ); + + connections[index].LocalScopeId = tcp6Table->table[i].dwLocalScopeId; + connections[index].RemoteScopeId = tcp6Table->table[i].dwRemoteScopeId; + + index++; + } + + PhFree(tcp6Table); + } + + if (udp4Table) + { + for (i = 0; i < udp4Table->dwNumEntries; i++) + { + connections[index].ProtocolType = PH_UDP4_NETWORK_PROTOCOL; + + connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; + connections[index].LocalEndpoint.Address.Ipv4 = udp4Table->table[i].dwLocalAddr; + connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp4Table->table[i].dwLocalPort); + + connections[index].RemoteEndpoint.Address.Type = 0; + + connections[index].State = 0; + connections[index].ProcessId = UlongToHandle(udp4Table->table[i].dwOwningPid); + connections[index].CreateTime = udp4Table->table[i].liCreateTimestamp; + memcpy( + connections[index].OwnerInfo, + udp4Table->table[i].OwningModuleInfo, + sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) + ); + + index++; + } + + PhFree(udp4Table); + } + + if (udp6Table) + { + for (i = 0; i < udp6Table->dwNumEntries; i++) + { + connections[index].ProtocolType = PH_UDP6_NETWORK_PROTOCOL; + + connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; + memcpy(connections[index].LocalEndpoint.Address.Ipv6, udp6Table->table[i].ucLocalAddr, 16); + connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp6Table->table[i].dwLocalPort); + + connections[index].RemoteEndpoint.Address.Type = 0; + + connections[index].State = 0; + connections[index].ProcessId = UlongToHandle(udp6Table->table[i].dwOwningPid); + connections[index].CreateTime = udp6Table->table[i].liCreateTimestamp; + memcpy( + connections[index].OwnerInfo, + udp6Table->table[i].OwningModuleInfo, + sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE) + ); + + index++; + } + + PhFree(udp6Table); + } + + *NumberOfConnections = count; + *Connections = connections; + + return TRUE; +} diff --git a/ProcessHacker/netstk.c b/ProcessHacker/netstk.c index 2fda236e61d0..2854b06a8eaf 100644 --- a/ProcessHacker/netstk.c +++ b/ProcessHacker/netstk.c @@ -1,240 +1,234 @@ -/* - * Process Hacker - - * network stack viewer - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -typedef struct NETWORK_STACK_CONTEXT -{ - HWND ListViewHandle; - PPH_NETWORK_ITEM NetworkItem; - PPH_SYMBOL_PROVIDER SymbolProvider; - HANDLE LoadingProcessId; -} NETWORK_STACK_CONTEXT, *PNETWORK_STACK_CONTEXT; - -INT_PTR CALLBACK PhpNetworkStackDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static RECT MinimumSize = { -1, -1, -1, -1 }; - -static BOOLEAN LoadSymbolsEnumGenericModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PNETWORK_STACK_CONTEXT context = Context; - PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider; - - // If we're loading kernel module symbols for a process other than - // System, ignore modules which are in user space. This may happen - // in Windows 7. - if ( - context->LoadingProcessId == SYSTEM_PROCESS_ID && - context->NetworkItem->ProcessId != SYSTEM_PROCESS_ID && - (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress - ) - return TRUE; - - PhLoadModuleSymbolProvider( - symbolProvider, - Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, - Module->Size - ); - - return TRUE; -} - -VOID PhShowNetworkStackDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - NETWORK_STACK_CONTEXT networkStackContext; - - networkStackContext.NetworkItem = NetworkItem; - networkStackContext.SymbolProvider = PhCreateSymbolProvider(NetworkItem->ProcessId); - - if (networkStackContext.SymbolProvider->IsRealHandle) - { - // Load symbols for the process. - networkStackContext.LoadingProcessId = NetworkItem->ProcessId; - PhEnumGenericModules( - NetworkItem->ProcessId, - networkStackContext.SymbolProvider->ProcessHandle, - 0, - LoadSymbolsEnumGenericModulesCallback, - &networkStackContext - ); - // Load symbols for kernel-mode. - networkStackContext.LoadingProcessId = SYSTEM_PROCESS_ID; - PhEnumGenericModules( - SYSTEM_PROCESS_ID, - NULL, - 0, - LoadSymbolsEnumGenericModulesCallback, - &networkStackContext - ); - } - else - { - PhDereferenceObject(networkStackContext.SymbolProvider); - PhShowError(ParentWindowHandle, L"Unable to open the process."); - return; - } - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_NETSTACK), - ParentWindowHandle, - PhpNetworkStackDlgProc, - (LPARAM)&networkStackContext - ); - - PhDereferenceObject(networkStackContext.SymbolProvider); -} - -static INT_PTR CALLBACK PhpNetworkStackDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PNETWORK_STACK_CONTEXT networkStackContext; - HWND lvHandle; - PPH_LAYOUT_MANAGER layoutManager; - PVOID address; - ULONG i; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - networkStackContext = (PNETWORK_STACK_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)networkStackContext); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 350, L"Name"); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - - networkStackContext->ListViewHandle = lvHandle; - - layoutManager = PhAllocate(sizeof(PH_LAYOUT_MANAGER)); - PhInitializeLayoutManager(layoutManager, hwndDlg); - SetProp(hwndDlg, L"LayoutManager", (HANDLE)layoutManager); - - PhAddLayoutItem(layoutManager, lvHandle, NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDOK), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - if (MinimumSize.left == -1) - { - RECT rect; - - rect.left = 0; - rect.top = 0; - rect.right = 190; - rect.bottom = 120; - MapDialogRect(hwndDlg, &rect); - MinimumSize = rect; - MinimumSize.left = 0; - } - - for (i = 0; i < PH_NETWORK_OWNER_INFO_SIZE; i++) - { - PPH_STRING name; - - address = *(PVOID *)&networkStackContext->NetworkItem->OwnerInfo[i]; - - if ((ULONG_PTR)address < PAGE_SIZE) // stop at an invalid address - break; - - name = PhGetSymbolFromAddress( - networkStackContext->SymbolProvider, - (ULONG64)address, - NULL, - NULL, - NULL, - NULL - ); - PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); - PhDereferenceObject(name); - } - } - break; - case WM_DESTROY: - { - PPH_LAYOUT_MANAGER layoutManager; - PNETWORK_STACK_CONTEXT networkStackContext; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhDeleteLayoutManager(layoutManager); - PhFree(layoutManager); - - networkStackContext = (PNETWORK_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - RemoveProp(hwndDlg, L"LayoutManager"); - } - break; - case WM_COMMAND: - { - INT id = LOWORD(wParam); - - switch (id) - { - case IDCANCEL: // Esc and X button to close - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - case WM_SIZE: - { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhLayoutManagerLayout(layoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * network stack viewer + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +typedef struct NETWORK_STACK_CONTEXT +{ + HWND ListViewHandle; + PH_LAYOUT_MANAGER LayoutManager; + PPH_NETWORK_ITEM NetworkItem; + PPH_SYMBOL_PROVIDER SymbolProvider; + HANDLE LoadingProcessId; +} NETWORK_STACK_CONTEXT, *PNETWORK_STACK_CONTEXT; + +INT_PTR CALLBACK PhpNetworkStackDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static RECT MinimumSize = { -1, -1, -1, -1 }; + +static BOOLEAN LoadSymbolsEnumGenericModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PNETWORK_STACK_CONTEXT context = Context; + PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider; + + // If we're loading kernel module symbols for a process other than + // System, ignore modules which are in user space. This may happen + // in Windows 7. + if ( + context->LoadingProcessId == SYSTEM_PROCESS_ID && + context->NetworkItem->ProcessId != SYSTEM_PROCESS_ID && + (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress + ) + return TRUE; + + PhLoadModuleSymbolProvider( + symbolProvider, + Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, + Module->Size + ); + + return TRUE; +} + +VOID PhShowNetworkStackDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + NETWORK_STACK_CONTEXT networkStackContext; + + networkStackContext.NetworkItem = NetworkItem; + networkStackContext.SymbolProvider = PhCreateSymbolProvider(NetworkItem->ProcessId); + + if (networkStackContext.SymbolProvider->IsRealHandle) + { + // Load symbols for the process. + networkStackContext.LoadingProcessId = NetworkItem->ProcessId; + PhEnumGenericModules( + NetworkItem->ProcessId, + networkStackContext.SymbolProvider->ProcessHandle, + 0, + LoadSymbolsEnumGenericModulesCallback, + &networkStackContext + ); + // Load symbols for kernel-mode. + networkStackContext.LoadingProcessId = SYSTEM_PROCESS_ID; + PhEnumGenericModules( + SYSTEM_PROCESS_ID, + NULL, + 0, + LoadSymbolsEnumGenericModulesCallback, + &networkStackContext + ); + } + else + { + PhDereferenceObject(networkStackContext.SymbolProvider); + PhShowError(ParentWindowHandle, L"Unable to open the process."); + return; + } + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_NETSTACK), + ParentWindowHandle, + PhpNetworkStackDlgProc, + (LPARAM)&networkStackContext + ); + + PhDereferenceObject(networkStackContext.SymbolProvider); +} + +static INT_PTR CALLBACK PhpNetworkStackDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PNETWORK_STACK_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PNETWORK_STACK_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + PVOID address; + ULONG i; + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 350, L"Name"); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, lvHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 190; + rect.bottom = 120; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + + for (i = 0; i < PH_NETWORK_OWNER_INFO_SIZE; i++) + { + PPH_STRING name; + + address = *(PVOID *)&context->NetworkItem->OwnerInfo[i]; + + if ((ULONG_PTR)address < PAGE_SIZE) // stop at an invalid address + break; + + name = PhGetSymbolFromAddress( + context->SymbolProvider, + (ULONG64)address, + NULL, + NULL, + NULL, + NULL + ); + PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); + PhDereferenceObject(name); + } + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: // Esc and X button to close + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/notifico.c b/ProcessHacker/notifico.c index f7e842a2d6cf..d0c2002576d6 100644 --- a/ProcessHacker/notifico.c +++ b/ProcessHacker/notifico.c @@ -1,1345 +1,1746 @@ -/* - * Process Hacker - - * notification icon manager - * - * Copyright (C) 2011-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -BOOLEAN PhNfTerminating = FALSE; -ULONG PhNfIconMask; -ULONG PhNfIconNotifyMask; -ULONG PhNfMaximumIconId = PH_ICON_DEFAULT_MAXIMUM; -PPH_NF_ICON PhNfRegisteredIcons[32] = { 0 }; -PPH_STRING PhNfIconTextCache[32] = { 0 }; -BOOLEAN PhNfMiniInfoEnabled; -BOOLEAN PhNfMiniInfoPinned; - -PH_NF_POINTERS PhNfpPointers; -PH_CALLBACK_REGISTRATION PhNfpProcessesUpdatedRegistration; -PH_NF_BITMAP PhNfpDefaultBitmapContext = { 0 }; -PH_NF_BITMAP PhNfpBlackBitmapContext = { 0 }; -HBITMAP PhNfpBlackBitmap = NULL; -HICON PhNfpBlackIcon = NULL; - -static POINT IconClickLocation; -static PH_NF_MSG_SHOWMINIINFOSECTION_DATA IconClickShowMiniInfoSectionData; -static BOOLEAN IconClickUpDueToDown; -static BOOLEAN IconDisableHover; - -VOID PhNfLoadStage1( - VOID - ) -{ - PPH_STRING iconList; - PH_STRINGREF part; - PH_STRINGREF remainingPart; - - PhNfpPointers.UpdateRegisteredIcon = PhNfpUpdateRegisteredIcon; - PhNfpPointers.BeginBitmap = PhNfpBeginBitmap; - - // Load settings for default icons. - PhNfIconMask = PhGetIntegerSetting(L"IconMask"); - - // Load settings for registered icons. - - iconList = PhGetStringSetting(L"IconMaskList"); - remainingPart = iconList->sr; - - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length != 0) - { - PH_STRINGREF pluginName; - ULONG subId; - PPH_NF_ICON icon; - - if (PhEmParseCompoundId(&part, &pluginName, &subId) && - (icon = PhNfFindIcon(&pluginName, subId))) - { - PhNfIconMask |= icon->IconId; - } - } - } - - PhDereferenceObject(iconList); -} - -VOID PhNfLoadStage2( - VOID - ) -{ - ULONG i; - - PhNfMiniInfoEnabled = WindowsVersion >= WINDOWS_VISTA && !!PhGetIntegerSetting(L"MiniInfoWindowEnabled"); - - for (i = PH_ICON_MINIMUM; i != PhNfMaximumIconId; i <<= 1) - { - if (PhNfIconMask & i) - PhNfpAddNotifyIcon(i); - } - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - PhNfpProcessesUpdatedHandler, - NULL, - &PhNfpProcessesUpdatedRegistration - ); -} - -VOID PhNfSaveSettings( - VOID - ) -{ - ULONG registeredIconMask; - - PhSetIntegerSetting(L"IconMask", PhNfIconMask & PH_ICON_DEFAULT_ALL); - - registeredIconMask = PhNfIconMask & ~PH_ICON_DEFAULT_ALL; - - if (registeredIconMask != 0) - { - PH_STRING_BUILDER iconListBuilder; - ULONG i; - - PhInitializeStringBuilder(&iconListBuilder, 60); - - for (i = 0; i < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON); i++) - { - if (PhNfRegisteredIcons[i]) - { - if (registeredIconMask & PhNfRegisteredIcons[i]->IconId) - { - PhAppendFormatStringBuilder( - &iconListBuilder, - L"+%s+%u|", - PhNfRegisteredIcons[i]->Plugin->Name.Buffer, - PhNfRegisteredIcons[i]->SubId - ); - } - } - } - - if (iconListBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&iconListBuilder, 1); - - PhSetStringSetting2(L"IconMaskList", &iconListBuilder.String->sr); - PhDeleteStringBuilder(&iconListBuilder); - } - else - { - PhSetStringSetting(L"IconMaskList", L""); - } -} - -VOID PhNfUninitialization( - VOID - ) -{ - ULONG i; - - // Remove all icons to prevent them hanging around after we exit. - - PhNfTerminating = TRUE; // prevent further icon updating - - for (i = PH_ICON_MINIMUM; i != PhNfMaximumIconId; i <<= 1) - { - if (PhNfIconMask & i) - PhNfpRemoveNotifyIcon(i); - } -} - -VOID PhNfForwardMessage( - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ) -{ - ULONG iconIndex = HIWORD(LParam); - PPH_NF_ICON registeredIcon = NULL; - - if (iconIndex < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON) && PhNfRegisteredIcons[iconIndex]) - { - registeredIcon = PhNfRegisteredIcons[iconIndex]; - - if (registeredIcon->MessageCallback) - { - if (registeredIcon->MessageCallback( - registeredIcon, - WParam, - LParam, - registeredIcon->Context - )) - { - return; - } - } - } - - switch (LOWORD(LParam)) - { - case WM_LBUTTONDOWN: - { - if (PhGetIntegerSetting(L"IconSingleClick")) - { - ProcessHacker_IconClick(PhMainWndHandle); - PhNfpDisableHover(); - } - else - { - IconClickUpDueToDown = TRUE; - } - } - break; - case WM_LBUTTONUP: - { - if (!PhGetIntegerSetting(L"IconSingleClick") && PhNfMiniInfoEnabled && IconClickUpDueToDown) - { - PH_NF_MSG_SHOWMINIINFOSECTION_DATA showMiniInfoSectionData; - - if (PhNfpGetShowMiniInfoSectionData(iconIndex, registeredIcon, &showMiniInfoSectionData)) - { - GetCursorPos(&IconClickLocation); - - if (IconClickShowMiniInfoSectionData.SectionName) - { - PhFree(IconClickShowMiniInfoSectionData.SectionName); - IconClickShowMiniInfoSectionData.SectionName = NULL; - } - - if (showMiniInfoSectionData.SectionName) - { - IconClickShowMiniInfoSectionData.SectionName = PhDuplicateStringZ(showMiniInfoSectionData.SectionName); - } - - SetTimer(PhMainWndHandle, TIMER_ICON_CLICK_ACTIVATE, GetDoubleClickTime() + NFP_ICON_CLICK_ACTIVATE_DELAY, PhNfpIconClickActivateTimerProc); - } - else - { - KillTimer(PhMainWndHandle, TIMER_ICON_CLICK_ACTIVATE); - } - } - } - break; - case WM_LBUTTONDBLCLK: - { - if (!PhGetIntegerSetting(L"IconSingleClick")) - { - if (PhNfMiniInfoEnabled) - { - // We will get another WM_LBUTTONUP message corresponding to the double-click, - // and we need to make sure that it doesn't start the activation timer again. - KillTimer(PhMainWndHandle, TIMER_ICON_CLICK_ACTIVATE); - IconClickUpDueToDown = FALSE; - PhNfpDisableHover(); - } - - ProcessHacker_IconClick(PhMainWndHandle); - } - } - break; - case WM_RBUTTONUP: - case WM_CONTEXTMENU: - { - POINT location; - - if (!PhGetIntegerSetting(L"IconSingleClick") && PhNfMiniInfoEnabled) - KillTimer(PhMainWndHandle, TIMER_ICON_CLICK_ACTIVATE); - - PhPinMiniInformation(MiniInfoIconPinType, -1, 0, 0, NULL, NULL); - GetCursorPos(&location); - PhShowIconContextMenu(location); - } - break; - case NIN_KEYSELECT: - // HACK: explorer seems to send two NIN_KEYSELECT messages when the user selects the icon and presses ENTER. - if (GetForegroundWindow() != PhMainWndHandle) - ProcessHacker_IconClick(PhMainWndHandle); - break; - case NIN_BALLOONUSERCLICK: - PhShowDetailsForIconNotification(); - break; - case NIN_POPUPOPEN: - { - PH_NF_MSG_SHOWMINIINFOSECTION_DATA showMiniInfoSectionData; - POINT location; - - if (PhNfMiniInfoEnabled && !IconDisableHover && PhNfpGetShowMiniInfoSectionData(iconIndex, registeredIcon, &showMiniInfoSectionData)) - { - GetCursorPos(&location); - PhPinMiniInformation(MiniInfoIconPinType, 1, 0, PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED, - showMiniInfoSectionData.SectionName, &location); - } - } - break; - case NIN_POPUPCLOSE: - PhPinMiniInformation(MiniInfoIconPinType, -1, 350, 0, NULL, NULL); - break; - } -} - -ULONG PhNfGetMaximumIconId( - VOID - ) -{ - return PhNfMaximumIconId; -} - -ULONG PhNfTestIconMask( - _In_ ULONG Id - ) -{ - return PhNfIconMask & Id; -} - -VOID PhNfSetVisibleIcon( - _In_ ULONG Id, - _In_ BOOLEAN Visible - ) -{ - if (Visible) - { - PhNfIconMask |= Id; - PhNfpAddNotifyIcon(Id); - } - else - { - PhNfIconMask &= ~Id; - PhNfpRemoveNotifyIcon(Id); - } -} - -BOOLEAN PhNfShowBalloonTip( - _In_opt_ ULONG Id, - _In_ PWSTR Title, - _In_ PWSTR Text, - _In_ ULONG Timeout, - _In_ ULONG Flags - ) -{ - NOTIFYICONDATA notifyIcon = { NOTIFYICONDATA_V3_SIZE }; - - if (Id == 0) - { - // Choose the first visible icon. - Id = PhNfIconMask; - } - - if (!_BitScanForward(&Id, Id)) - return FALSE; - - notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = Id; - notifyIcon.uFlags = NIF_INFO; - wcsncpy_s(notifyIcon.szInfoTitle, sizeof(notifyIcon.szInfoTitle) / sizeof(WCHAR), Title, _TRUNCATE); - wcsncpy_s(notifyIcon.szInfo, sizeof(notifyIcon.szInfo) / sizeof(WCHAR), Text, _TRUNCATE); - notifyIcon.uTimeout = Timeout; - notifyIcon.dwInfoFlags = Flags; - - Shell_NotifyIcon(NIM_MODIFY, ¬ifyIcon); - - return TRUE; -} - -HICON PhNfBitmapToIcon( - _In_ HBITMAP Bitmap - ) -{ - ICONINFO iconInfo; - - PhNfpGetBlackIcon(); - - iconInfo.fIcon = TRUE; - iconInfo.xHotspot = 0; - iconInfo.yHotspot = 0; - iconInfo.hbmMask = PhNfpBlackBitmap; - iconInfo.hbmColor = Bitmap; - - return CreateIconIndirect(&iconInfo); -} - -PPH_NF_ICON PhNfRegisterIcon( - _In_ struct _PH_PLUGIN *Plugin, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_ PWSTR Text, - _In_ ULONG Flags, - _In_opt_ PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback, - _In_opt_ PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback - ) -{ - PPH_NF_ICON icon; - ULONG iconId; - ULONG iconIndex; - - if (PhNfMaximumIconId == PH_ICON_LIMIT) - { - // No room for any more icons. - return NULL; - } - - iconId = PhNfMaximumIconId; - - if (!_BitScanReverse(&iconIndex, iconId) || - iconIndex >= sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON)) - { - // Should never happen. - return NULL; - } - - PhNfMaximumIconId <<= 1; - - icon = PhAllocate(sizeof(PH_NF_ICON)); - icon->Plugin = Plugin; - icon->SubId = SubId; - icon->Context = Context; - icon->Pointers = &PhNfpPointers; - icon->Text = Text; - icon->Flags = Flags; - icon->IconId = iconId; - icon->UpdateCallback = UpdateCallback; - icon->MessageCallback = MessageCallback; - - PhNfRegisteredIcons[iconIndex] = icon; - - return icon; -} - -PPH_NF_ICON PhNfGetIconById( - _In_ ULONG Id - ) -{ - ULONG iconIndex; - - if (!_BitScanReverse(&iconIndex, Id)) - return NULL; - - return PhNfRegisteredIcons[iconIndex]; -} - -PPH_NF_ICON PhNfFindIcon( - _In_ PPH_STRINGREF PluginName, - _In_ ULONG SubId - ) -{ - ULONG i; - - for (i = 0; i < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON); i++) - { - if (PhNfRegisteredIcons[i]) - { - if (PhNfRegisteredIcons[i]->SubId == SubId && - PhEqualStringRef(PluginName, &PhNfRegisteredIcons[i]->Plugin->AppContext.AppName, FALSE)) - { - return PhNfRegisteredIcons[i]; - } - } - } - - return NULL; -} - -VOID PhNfNotifyMiniInfoPinned( - _In_ BOOLEAN Pinned - ) -{ - ULONG i; - ULONG id; - - if (PhNfMiniInfoPinned != Pinned) - { - PhNfMiniInfoPinned = Pinned; - - // Go through every icon and set/clear the NIF_SHOWTIP flag depending on whether the mini info window is - // pinned. If it's pinned then we want to show normal tooltips, because the section doesn't change - // automatically when the cursor hovers over an icon. - for (i = 0; i < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON); i++) - { - id = 1 << i; - - if (PhNfIconMask & id) - { - PhNfpModifyNotifyIcon(id, NIF_TIP, PhNfIconTextCache[i], NULL); - } - } - } -} - -HICON PhNfpGetBlackIcon( - VOID - ) -{ - if (!PhNfpBlackIcon) - { - ULONG width; - ULONG height; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - ICONINFO iconInfo; - - PhNfpBeginBitmap2(&PhNfpBlackBitmapContext, &width, &height, &PhNfpBlackBitmap, &bits, &hdc, &oldBitmap); - memset(bits, 0, width * height * sizeof(ULONG)); - - iconInfo.fIcon = TRUE; - iconInfo.xHotspot = 0; - iconInfo.yHotspot = 0; - iconInfo.hbmMask = PhNfpBlackBitmap; - iconInfo.hbmColor = PhNfpBlackBitmap; - PhNfpBlackIcon = CreateIconIndirect(&iconInfo); - - SelectObject(hdc, oldBitmap); - } - - return PhNfpBlackIcon; -} - -BOOLEAN PhNfpAddNotifyIcon( - _In_ ULONG Id - ) -{ - NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; - PPH_NF_ICON icon; - - if (PhNfTerminating) - return FALSE; - if ((icon = PhNfGetIconById(Id)) && (icon->Flags & PH_NF_ICON_UNAVAILABLE)) - return FALSE; - - // The IDs we pass to explorer are bit indicies, not the normal mask values. - - if (!_BitScanForward(&Id, Id)) - return FALSE; - - notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = Id; - notifyIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; - notifyIcon.uCallbackMessage = WM_PH_NOTIFY_ICON_MESSAGE; - wcsncpy_s( - notifyIcon.szTip, sizeof(notifyIcon.szTip) / sizeof(WCHAR), - PhGetStringOrDefault(PhNfIconTextCache[Id], PhApplicationName), - _TRUNCATE - ); - notifyIcon.hIcon = PhNfpGetBlackIcon(); - - if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (icon && !(icon->Flags & PH_NF_ICON_SHOW_MINIINFO))) - notifyIcon.uFlags |= NIF_SHOWTIP; - - Shell_NotifyIcon(NIM_ADD, ¬ifyIcon); - - if (WindowsVersion >= WINDOWS_VISTA) - { - notifyIcon.uVersion = NOTIFYICON_VERSION_4; - Shell_NotifyIcon(NIM_SETVERSION, ¬ifyIcon); - } - - return TRUE; -} - -BOOLEAN PhNfpRemoveNotifyIcon( - _In_ ULONG Id - ) -{ - NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; - PPH_NF_ICON icon; - - if ((icon = PhNfGetIconById(Id)) && (icon->Flags & PH_NF_ICON_UNAVAILABLE)) - return FALSE; - - if (!_BitScanForward(&Id, Id)) - return FALSE; - - notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = Id; - - Shell_NotifyIcon(NIM_DELETE, ¬ifyIcon); - - return TRUE; -} - -BOOLEAN PhNfpModifyNotifyIcon( - _In_ ULONG Id, - _In_ ULONG Flags, - _In_opt_ PPH_STRING Text, - _In_opt_ HICON Icon - ) -{ - NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; - PPH_NF_ICON icon; - ULONG notifyId; - - if (PhNfTerminating) - return FALSE; - if ((icon = PhNfGetIconById(Id)) && (icon->Flags & PH_NF_ICON_UNAVAILABLE)) - return FALSE; - - if (!_BitScanForward(¬ifyId, Id)) - return FALSE; - - notifyIcon.hWnd = PhMainWndHandle; - notifyIcon.uID = notifyId; - notifyIcon.uFlags = Flags; - - if (Flags & NIF_TIP) - { - PhSwapReference(&PhNfIconTextCache[notifyId], Text); - wcsncpy_s( - notifyIcon.szTip, - sizeof(notifyIcon.szTip) / sizeof(WCHAR), - PhGetStringOrDefault(Text, PhApplicationName), - _TRUNCATE - ); - } - - notifyIcon.hIcon = Icon; - - if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (icon && !(icon->Flags & PH_NF_ICON_SHOW_MINIINFO))) - notifyIcon.uFlags |= NIF_SHOWTIP; - - if (!Shell_NotifyIcon(NIM_MODIFY, ¬ifyIcon)) - { - // Explorer probably died and we lost our icon. Try to add the icon, and try again. - PhNfpAddNotifyIcon(Id); - Shell_NotifyIcon(NIM_MODIFY, ¬ifyIcon); - } - - return TRUE; -} - -VOID PhNfpProcessesUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ULONG registeredIconMask; - - // We do icon updating on the provider thread so we don't block the main GUI when - // explorer is not responding. - - if (PhNfIconMask & PH_ICON_CPU_HISTORY) - PhNfpUpdateIconCpuHistory(); - if (PhNfIconMask & PH_ICON_IO_HISTORY) - PhNfpUpdateIconIoHistory(); - if (PhNfIconMask & PH_ICON_COMMIT_HISTORY) - PhNfpUpdateIconCommitHistory(); - if (PhNfIconMask & PH_ICON_PHYSICAL_HISTORY) - PhNfpUpdateIconPhysicalHistory(); - if (PhNfIconMask & PH_ICON_CPU_USAGE) - PhNfpUpdateIconCpuUsage(); - - registeredIconMask = PhNfIconMask & ~PH_ICON_DEFAULT_ALL; - - if (registeredIconMask != 0) - { - ULONG i; - - for (i = 0; i < sizeof(PhNfRegisteredIcons) / sizeof(PPH_NF_ICON); i++) - { - if (PhNfRegisteredIcons[i] && (registeredIconMask & PhNfRegisteredIcons[i]->IconId)) - { - PhNfpUpdateRegisteredIcon(PhNfRegisteredIcons[i]); - } - } - } -} - -VOID PhNfpUpdateRegisteredIcon( - _In_ PPH_NF_ICON Icon - ) -{ - PVOID newIconOrBitmap; - ULONG updateFlags; - PPH_STRING newText; - HICON newIcon; - ULONG flags; - - if (!Icon->UpdateCallback) - return; - - newIconOrBitmap = NULL; - newText = NULL; - newIcon = NULL; - flags = 0; - - Icon->UpdateCallback( - Icon, - &newIconOrBitmap, - &updateFlags, - &newText, - Icon->Context - ); - - if (newIconOrBitmap) - { - if (updateFlags & PH_NF_UPDATE_IS_BITMAP) - newIcon = PhNfBitmapToIcon(newIconOrBitmap); - else - newIcon = newIconOrBitmap; - - flags |= NIF_ICON; - } - - if (newText) - flags |= NIF_TIP; - - if (flags != 0) - PhNfpModifyNotifyIcon(Icon->IconId, flags, newText, newIcon); - - if (newIcon && (updateFlags & PH_NF_UPDATE_IS_BITMAP)) - DestroyIcon(newIcon); - - if (newIconOrBitmap && (updateFlags & PH_NF_UPDATE_DESTROY_RESOURCE)) - { - if (updateFlags & PH_NF_UPDATE_IS_BITMAP) - DeleteObject(newIconOrBitmap); - else - DestroyIcon(newIconOrBitmap); - } - - if (newText) - PhDereferenceObject(newText); -} - -VOID PhNfpBeginBitmap( - _Out_ PULONG Width, - _Out_ PULONG Height, - _Out_ HBITMAP *Bitmap, - _Out_opt_ PVOID *Bits, - _Out_ HDC *Hdc, - _Out_ HBITMAP *OldBitmap - ) -{ - PhNfpBeginBitmap2(&PhNfpDefaultBitmapContext, Width, Height, Bitmap, Bits, Hdc, OldBitmap); -} - -VOID PhNfpBeginBitmap2( - _Inout_ PPH_NF_BITMAP Context, - _Out_ PULONG Width, - _Out_ PULONG Height, - _Out_ HBITMAP *Bitmap, - _Out_opt_ PVOID *Bits, - _Out_ HDC *Hdc, - _Out_ HBITMAP *OldBitmap - ) -{ - if (!Context->Initialized) - { - HDC screenHdc; - - screenHdc = GetDC(NULL); - Context->Hdc = CreateCompatibleDC(screenHdc); - - memset(&Context->Header, 0, sizeof(BITMAPINFOHEADER)); - Context->Header.biSize = sizeof(BITMAPINFOHEADER); - Context->Header.biWidth = PhSmallIconSize.X; - Context->Header.biHeight = PhSmallIconSize.Y; - Context->Header.biPlanes = 1; - Context->Header.biBitCount = 32; - Context->Bitmap = CreateDIBSection(screenHdc, (BITMAPINFO *)&Context->Header, DIB_RGB_COLORS, &Context->Bits, NULL, 0); - - ReleaseDC(NULL, screenHdc); - - Context->Initialized = TRUE; - } - - *Width = PhSmallIconSize.X; - *Height = PhSmallIconSize.Y; - *Bitmap = Context->Bitmap; - if (Bits) *Bits = Context->Bits; - *Hdc = Context->Hdc; - *OldBitmap = SelectObject(Context->Hdc, Context->Bitmap); -} - -VOID PhNfpUpdateIconCpuHistory( - VOID - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - PH_GRAPH_USE_LINE_2, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - PFLOAT lineData2; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HICON icon; - HANDLE maxCpuProcessId; - PPH_PROCESS_ITEM maxCpuProcessItem; - PH_FORMAT format[8]; - PPH_STRING text; - - // Icon - - PhNfpBeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - lineData2 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, PhCpuKernelHistory.Count); - PhCopyCircularBuffer_FLOAT(&PhCpuKernelHistory, lineData1, lineDataCount); - PhCopyCircularBuffer_FLOAT(&PhCpuUserHistory, lineData2, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineData2 = lineData2; - drawInfo.LineColor1 = PhCsColorCpuKernel; - drawInfo.LineColor2 = PhCsColorCpuUser; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorCpuKernel); - drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorCpuUser); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); - - // Text - - if (PhMaxCpuHistory.Count != 0) - maxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); - else - maxCpuProcessId = NULL; - - if (maxCpuProcessId) - maxCpuProcessItem = PhReferenceProcessItem(maxCpuProcessId); - else - maxCpuProcessItem = NULL; - - PhInitFormatS(&format[0], L"CPU Usage: "); - PhInitFormatF(&format[1], (PhCpuKernelUsage + PhCpuUserUsage) * 100, 2); - PhInitFormatC(&format[2], '%'); - - if (maxCpuProcessItem) - { - PhInitFormatC(&format[3], '\n'); - PhInitFormatSR(&format[4], maxCpuProcessItem->ProcessName->sr); - PhInitFormatS(&format[5], L": "); - PhInitFormatF(&format[6], maxCpuProcessItem->CpuUsage * 100, 2); - PhInitFormatC(&format[7], '%'); - } - - text = PhFormat(format, maxCpuProcessItem ? 8 : 3, 128); - if (maxCpuProcessItem) PhDereferenceObject(maxCpuProcessItem); - - PhNfpModifyNotifyIcon(PH_ICON_CPU_HISTORY, NIF_TIP | NIF_ICON, text, icon); - - DestroyIcon(icon); - PhDereferenceObject(text); -} - -VOID PhNfpUpdateIconIoHistory( - VOID - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - PH_GRAPH_USE_LINE_2, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - PFLOAT lineData2; - FLOAT max; - ULONG i; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HICON icon; - HANDLE maxIoProcessId; - PPH_PROCESS_ITEM maxIoProcessItem; - PH_FORMAT format[8]; - PPH_STRING text; - - // Icon - - PhNfpBeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - lineData2 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, PhIoReadHistory.Count); - max = 1024 * 1024; // minimum scaling of 1 MB. - - for (i = 0; i < lineDataCount; i++) - { - lineData1[i] = - (FLOAT)PhGetItemCircularBuffer_ULONG64(&PhIoReadHistory, i) + - (FLOAT)PhGetItemCircularBuffer_ULONG64(&PhIoOtherHistory, i); - lineData2[i] = - (FLOAT)PhGetItemCircularBuffer_ULONG64(&PhIoWriteHistory, i); - - if (max < lineData1[i] + lineData2[i]) - max = lineData1[i] + lineData2[i]; - } - - PhDivideSinglesBySingle(lineData1, max, lineDataCount); - PhDivideSinglesBySingle(lineData2, max, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineData2 = lineData2; - drawInfo.LineColor1 = PhCsColorIoReadOther; - drawInfo.LineColor2 = PhCsColorIoWrite; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorIoReadOther); - drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorIoWrite); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); - - // Text - - if (PhMaxIoHistory.Count != 0) - maxIoProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxIoHistory, 0)); - else - maxIoProcessId = NULL; - - if (maxIoProcessId) - maxIoProcessItem = PhReferenceProcessItem(maxIoProcessId); - else - maxIoProcessItem = NULL; - - PhInitFormatS(&format[0], L"I/O\nR: "); - PhInitFormatSize(&format[1], PhIoReadDelta.Delta); - PhInitFormatS(&format[2], L"\nW: "); - PhInitFormatSize(&format[3], PhIoWriteDelta.Delta); - PhInitFormatS(&format[4], L"\nO: "); - PhInitFormatSize(&format[5], PhIoOtherDelta.Delta); - - if (maxIoProcessItem) - { - PhInitFormatC(&format[6], '\n'); - PhInitFormatSR(&format[7], maxIoProcessItem->ProcessName->sr); - } - - text = PhFormat(format, maxIoProcessItem ? 8 : 6, 128); - if (maxIoProcessItem) PhDereferenceObject(maxIoProcessItem); - - PhNfpModifyNotifyIcon(PH_ICON_IO_HISTORY, NIF_TIP | NIF_ICON, text, icon); - - DestroyIcon(icon); - PhDereferenceObject(text); -} - -VOID PhNfpUpdateIconCommitHistory( - VOID - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - 0, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - ULONG i; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HICON icon; - DOUBLE commitFraction; - PH_FORMAT format[5]; - PPH_STRING text; - - // Icon - - PhNfpBeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, PhCommitHistory.Count); - - for (i = 0; i < lineDataCount; i++) - lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&PhCommitHistory, i); - - PhDivideSinglesBySingle(lineData1, (FLOAT)PhPerfInformation.CommitLimit, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineColor1 = PhCsColorPrivate; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPrivate); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); - - // Text - - commitFraction = (DOUBLE)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit; - - PhInitFormatS(&format[0], L"Commit: "); - PhInitFormatSize(&format[1], UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE)); - PhInitFormatS(&format[2], L" ("); - PhInitFormatF(&format[3], commitFraction * 100, 2); - PhInitFormatS(&format[4], L"%)"); - - text = PhFormat(format, 5, 96); - - PhNfpModifyNotifyIcon(PH_ICON_COMMIT_HISTORY, NIF_TIP | NIF_ICON, text, icon); - - DestroyIcon(icon); - PhDereferenceObject(text); -} - -VOID PhNfpUpdateIconPhysicalHistory( - VOID - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - 0, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - ULONG i; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HICON icon; - ULONG physicalUsage; - FLOAT physicalFraction; - PH_FORMAT format[5]; - PPH_STRING text; - - // Icon - - PhNfpBeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, PhCommitHistory.Count); - - for (i = 0; i < lineDataCount; i++) - lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&PhPhysicalHistory, i); - - PhDivideSinglesBySingle(lineData1, (FLOAT)PhSystemBasicInformation.NumberOfPhysicalPages, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineColor1 = PhCsColorPhysical; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPhysical); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); - - // Text - - physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages; - physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages; - - PhInitFormatS(&format[0], L"Physical memory: "); - PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE)); - PhInitFormatS(&format[2], L" ("); - PhInitFormatF(&format[3], physicalFraction * 100, 2); - PhInitFormatS(&format[4], L"%)"); - - text = PhFormat(format, 5, 96); - - PhNfpModifyNotifyIcon(PH_ICON_PHYSICAL_HISTORY, NIF_TIP | NIF_ICON, text, icon); - - DestroyIcon(icon); - PhDereferenceObject(text); -} - -VOID PhNfpUpdateIconCpuUsage( - VOID - ) -{ - ULONG width; - ULONG height; - HBITMAP bitmap; - HDC hdc; - HBITMAP oldBitmap; - HICON icon; - HANDLE maxCpuProcessId; - PPH_PROCESS_ITEM maxCpuProcessItem; - PPH_STRING maxCpuText = NULL; - PPH_STRING text; - - // Icon - - PhNfpBeginBitmap(&width, &height, &bitmap, NULL, &hdc, &oldBitmap); - - // This stuff is copied from CpuUsageIcon.cs (PH 1.x). - { - COLORREF kColor = PhCsColorCpuKernel; - COLORREF uColor = PhCsColorCpuUser; - COLORREF kbColor = PhHalveColorBrightness(PhCsColorCpuKernel); - COLORREF ubColor = PhHalveColorBrightness(PhCsColorCpuUser); - FLOAT k = PhCpuKernelUsage; - FLOAT u = PhCpuUserUsage; - LONG kl = (LONG)(k * height); - LONG ul = (LONG)(u * height); - RECT rect; - HBRUSH dcBrush; - HBRUSH dcPen; - POINT points[2]; - - dcBrush = GetStockObject(DC_BRUSH); - dcPen = GetStockObject(DC_PEN); - rect.left = 0; - rect.top = 0; - rect.right = width; - rect.bottom = height; - SetDCBrushColor(hdc, RGB(0x00, 0x00, 0x00)); - FillRect(hdc, &rect, dcBrush); - - // Draw the base line. - if (kl + ul == 0) - { - SelectObject(hdc, dcPen); - SetDCPenColor(hdc, uColor); - points[0].x = 0; - points[0].y = height - 1; - points[1].x = width; - points[1].y = height - 1; - Polyline(hdc, points, 2); - } - else - { - rect.left = 0; - rect.top = height - ul - kl; - rect.right = width; - rect.bottom = height - kl; - SetDCBrushColor(hdc, ubColor); - FillRect(hdc, &rect, dcBrush); - - points[0].x = 0; - points[0].y = height - 1 - ul - kl; - if (points[0].y < 0) points[0].y = 0; - points[1].x = width; - points[1].y = points[0].y; - SelectObject(hdc, dcPen); - SetDCPenColor(hdc, uColor); - Polyline(hdc, points, 2); - - if (kl != 0) - { - rect.left = 0; - rect.top = height - kl; - rect.right = width; - rect.bottom = height; - SetDCBrushColor(hdc, kbColor); - FillRect(hdc, &rect, dcBrush); - - points[0].x = 0; - points[0].y = height - 1 - kl; - if (points[0].y < 0) points[0].y = 0; - points[1].x = width; - points[1].y = points[0].y; - SelectObject(hdc, dcPen); - SetDCPenColor(hdc, kColor); - Polyline(hdc, points, 2); - } - } - } - - SelectObject(hdc, oldBitmap); - icon = PhNfBitmapToIcon(bitmap); - - // Text - - if (PhMaxCpuHistory.Count != 0) - maxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); - else - maxCpuProcessId = NULL; - - if (maxCpuProcessId) - { - if (maxCpuProcessItem = PhReferenceProcessItem(maxCpuProcessId)) - { - maxCpuText = PhFormatString( - L"\n%s: %.2f%%", - maxCpuProcessItem->ProcessName->Buffer, - maxCpuProcessItem->CpuUsage * 100 - ); - PhDereferenceObject(maxCpuProcessItem); - } - } - - text = PhFormatString(L"CPU usage: %.2f%%%s", (PhCpuKernelUsage + PhCpuUserUsage) * 100, PhGetStringOrEmpty(maxCpuText)); - if (maxCpuText) PhDereferenceObject(maxCpuText); - - PhNfpModifyNotifyIcon(PH_ICON_CPU_USAGE, NIF_TIP | NIF_ICON, text, icon); - - DestroyIcon(icon); - PhDereferenceObject(text); -} - -BOOLEAN PhNfpGetShowMiniInfoSectionData( - _In_ ULONG IconIndex, - _In_ PPH_NF_ICON RegisteredIcon, - _Out_ PPH_NF_MSG_SHOWMINIINFOSECTION_DATA Data - ) -{ - BOOLEAN showMiniInfo = FALSE; - - if (RegisteredIcon) - { - Data->SectionName = NULL; - - if (RegisteredIcon->Flags & PH_NF_ICON_SHOW_MINIINFO) - { - if (RegisteredIcon->MessageCallback) - { - RegisteredIcon->MessageCallback( - RegisteredIcon, - (ULONG_PTR)Data, - MAKELPARAM(PH_NF_MSG_SHOWMINIINFOSECTION, 0), - RegisteredIcon->Context - ); - } - - showMiniInfo = TRUE; - } - } - else - { - switch (1 << IconIndex) - { - case PH_ICON_CPU_HISTORY: - case PH_ICON_CPU_USAGE: - Data->SectionName = L"CPU"; - break; - case PH_ICON_IO_HISTORY: - Data->SectionName = L"I/O"; - break; - case PH_ICON_COMMIT_HISTORY: - Data->SectionName = L"Commit charge"; - break; - case PH_ICON_PHYSICAL_HISTORY: - Data->SectionName = L"Physical memory"; - break; - } - - showMiniInfo = TRUE; - } - - return showMiniInfo; -} - -VOID PhNfpIconClickActivateTimerProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ UINT_PTR idEvent, - _In_ DWORD dwTime - ) -{ - PhPinMiniInformation(MiniInfoActivePinType, 1, 0, - PH_MINIINFO_ACTIVATE_WINDOW | PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED, - IconClickShowMiniInfoSectionData.SectionName, &IconClickLocation); - KillTimer(PhMainWndHandle, TIMER_ICON_CLICK_ACTIVATE); -} - -VOID PhNfpDisableHover( - VOID - ) -{ - IconDisableHover = TRUE; - SetTimer(PhMainWndHandle, TIMER_ICON_RESTORE_HOVER, NFP_ICON_RESTORE_HOVER_DELAY, PhNfpIconRestoreHoverTimerProc); -} - -VOID PhNfpIconRestoreHoverTimerProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ UINT_PTR idEvent, - _In_ DWORD dwTime - ) -{ - IconDisableHover = FALSE; - KillTimer(PhMainWndHandle, TIMER_ICON_RESTORE_HOVER); -} +/* + * Process Hacker - + * notification icon manager + * + * Copyright (C) 2011-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +BOOLEAN PhNfMiniInfoEnabled = FALSE; +BOOLEAN PhNfMiniInfoPinned = FALSE; +// Note: no lock is needed because we only ever modify the list on this same thread. +PPH_LIST PhTrayIconItemList = NULL; + +PH_NF_POINTERS PhNfpPointers; +PH_CALLBACK_REGISTRATION PhNfpProcessesUpdatedRegistration; +PH_NF_BITMAP PhNfpDefaultBitmapContext = { 0 }; +PH_NF_BITMAP PhNfpBlackBitmapContext = { 0 }; +HBITMAP PhNfpBlackBitmap = NULL; +HICON PhNfpBlackIcon = NULL; +GUID PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_MAXIMUM]; + +static POINT IconClickLocation; +static PH_NF_MSG_SHOWMINIINFOSECTION_DATA IconClickShowMiniInfoSectionData; +static BOOLEAN IconClickUpDueToDown = FALSE; +static BOOLEAN IconDisableHover = FALSE; + +VOID PhNfLoadStage1( + VOID + ) +{ + PhTrayIconItemList = PhCreateList(20); + + PhNfpPointers.BeginBitmap = PhNfpBeginBitmap; +} + +VOID PhNfLoadSettings( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRINGREF remaining; + + settingsString = PhGetStringSetting(L"IconSettings"); + remaining = PhGetStringRef(settingsString); + + if (remaining.Length == 0) + return; + + while (remaining.Length != 0) + { + PH_STRINGREF idPart; + PH_STRINGREF flagsPart; + PH_STRINGREF pluginNamePart; + ULONG64 idInteger; + ULONG64 flagsInteger; + + PhSplitStringRefAtChar(&remaining, '|', &idPart, &remaining); + PhSplitStringRefAtChar(&remaining, '|', &flagsPart, &remaining); + PhSplitStringRefAtChar(&remaining, '|', &pluginNamePart, &remaining); + + if (!PhStringToInteger64(&idPart, 10, &idInteger)) + break; + if (!PhStringToInteger64(&flagsPart, 10, &flagsInteger)) + break; + + if (flagsInteger) + { + PPH_NF_ICON icon; + + if (pluginNamePart.Length) + { + if (icon = PhNfFindIcon(&pluginNamePart, (ULONG)idInteger)) + icon->Flags |= PH_NF_ICON_ENABLED; + } + else + { + if (icon = PhNfGetIconById((ULONG)idInteger)) + icon->Flags |= PH_NF_ICON_ENABLED; + } + } + } + + PhDereferenceObject(settingsString); +} + +VOID PhNfSaveSettings( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRING_BUILDER iconListBuilder; + + PhInitializeStringBuilder(&iconListBuilder, 100); + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhAppendFormatStringBuilder( + &iconListBuilder, + L"%lu|%lu|%s|", + icon->SubId, + icon->Flags & PH_NF_ICON_ENABLED ? 1 : 0, + icon->Plugin ? icon->Plugin->Name.Buffer : L"" + ); + } + + if (iconListBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&iconListBuilder, 1); + + settingsString = PhFinalStringBuilderString(&iconListBuilder); + PhSetStringSetting2(L"IconSettings", &settingsString->sr); + PhDereferenceObject(settingsString); +} + +VOID PhNfLoadGuids( + VOID + ) +{ + PPH_STRING settingsString = NULL; + PH_STRINGREF remaining; + ULONG i; + + settingsString = PhGetStringSetting(L"IconGuids"); + + if (PhIsNullOrEmptyString(settingsString)) + { + PH_STRING_BUILDER iconListBuilder; + PPH_STRING iconGuid; + + PhInitializeStringBuilder(&iconListBuilder, 100); + + for (i = 0; i < RTL_NUMBER_OF(PhNfpTrayIconItemGuids); i++) + { + PhGenerateGuid(&PhNfpTrayIconItemGuids[i]); + + if (iconGuid = PhFormatGuid(&PhNfpTrayIconItemGuids[i])) + { + PhAppendFormatStringBuilder( + &iconListBuilder, + L"%s|", + iconGuid->Buffer + ); + PhDereferenceObject(iconGuid); + } + } + + if (iconListBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&iconListBuilder, 1); + + PhMoveReference(&settingsString, PhFinalStringBuilderString(&iconListBuilder)); + PhSetStringSetting2(L"IconGuids", &settingsString->sr); + PhDereferenceObject(settingsString); + } + else + { + remaining = PhGetStringRef(settingsString); + + for (i = 0; i < RTL_NUMBER_OF(PhNfpTrayIconItemGuids); i++) + { + PH_STRINGREF guidPart; + UNICODE_STRING guidStringUs; + GUID guid; + + if (remaining.Length == 0) + continue; + + PhSplitStringRefAtChar(&remaining, '|', &guidPart, &remaining); + + if (guidPart.Length == 0) + continue; + + if (!PhStringRefToUnicodeString(&guidPart, &guidStringUs)) + continue; + + if (!NT_SUCCESS(RtlGUIDFromString(&guidStringUs, &guid))) + PhGenerateGuid(&PhNfpTrayIconItemGuids[i]); + else + PhNfpTrayIconItemGuids[i] = guid; + } + + PhDereferenceObject(settingsString); + } +} + +VOID PhNfLoadStage2( + VOID + ) +{ + PhNfMiniInfoEnabled = !!PhGetIntegerSetting(L"MiniInfoWindowEnabled"); + PhNfLoadGuids(); + + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_CPU_USAGE, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_CPU_USAGE], NULL, L"CPU &usage", 0, PhNfpCpuUsageIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_CPU_HISTORY, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_CPU_HISTORY], NULL, L"CPU &history", 0, PhNfpCpuHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_IO_HISTORY, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_IO_HISTORY], NULL, L"&I/O history", 0, PhNfpIoHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_COMMIT_HISTORY, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_COMMIT_HISTORY], NULL, L"&Commit charge history", 0, PhNfpCommitHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_PHYSICAL_HISTORY, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_PHYSICAL_HISTORY], NULL, L"&Physical memory history", 0, PhNfpPhysicalHistoryIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_CPU_TEXT, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_CPU_TEXT], NULL, L"CPU usage (text)", 0, PhNfpCpuUsageTextIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_IO_TEXT, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_IO_TEXT], NULL, L"IO usage (text)", 0, PhNfpIoUsageTextIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_COMMIT_TEXT, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_COMMIT_TEXT], NULL, L"Commit usage (text)", 0, PhNfpCommitTextIconUpdateCallback, NULL); + PhNfRegisterIcon(NULL, PH_TRAY_ICON_ID_PHYSICAL_TEXT, PhNfpTrayIconItemGuids[PH_TRAY_ICON_GUID_PHYSICAL_TEXT], NULL, L"Physical usage (text)", 0, PhNfpPhysicalUsageTextIconUpdateCallback, NULL); + + if (PhPluginsEnabled) + { + PH_TRAY_ICON_POINTERS pointers; + + pointers.RegisterTrayIcon = PhNfPluginRegisterIcon; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackTrayIconsInitializing), &pointers); + } + + // Load tray icon settings. + PhNfLoadSettings(); + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpAddNotifyIcon(icon); + } + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + PhNfpProcessesUpdatedHandler, + NULL, + &PhNfpProcessesUpdatedRegistration + ); +} + +VOID PhNfUninitialization( + VOID + ) +{ + // Remove all icons to prevent them hanging around after we exit. + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpRemoveNotifyIcon(icon); + } +} + +VOID PhNfForwardMessage( + _In_ HWND WindowHandle, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ) +{ + ULONG iconIndex = HIWORD(LParam); + PPH_NF_ICON registeredIcon = NULL; + + if (iconIndex == 0) + return; + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (icon->IconId == iconIndex) + { + registeredIcon = icon; + break; + } + } + + if (!registeredIcon) + return; + + if (registeredIcon->MessageCallback) + { + if (registeredIcon->MessageCallback( + registeredIcon, + WParam, + LParam, + registeredIcon->Context + )) + { + return; + } + } + + switch (LOWORD(LParam)) + { + case WM_LBUTTONDOWN: + { + if (PhGetIntegerSetting(L"IconSingleClick")) + { + ProcessHacker_IconClick(WindowHandle); + PhNfpDisableHover(); + } + else + { + IconClickUpDueToDown = TRUE; + } + } + break; + case WM_LBUTTONUP: + { + if (!PhGetIntegerSetting(L"IconSingleClick") && PhNfMiniInfoEnabled && IconClickUpDueToDown) + { + PH_NF_MSG_SHOWMINIINFOSECTION_DATA showMiniInfoSectionData; + + if (PhNfpGetShowMiniInfoSectionData(iconIndex, registeredIcon, &showMiniInfoSectionData)) + { + GetCursorPos(&IconClickLocation); + + if (IconClickShowMiniInfoSectionData.SectionName) + { + PhFree(IconClickShowMiniInfoSectionData.SectionName); + IconClickShowMiniInfoSectionData.SectionName = NULL; + } + + if (showMiniInfoSectionData.SectionName) + { + IconClickShowMiniInfoSectionData.SectionName = PhDuplicateStringZ(showMiniInfoSectionData.SectionName); + } + + SetTimer(WindowHandle, TIMER_ICON_CLICK_ACTIVATE, GetDoubleClickTime() + NFP_ICON_CLICK_ACTIVATE_DELAY, PhNfpIconClickActivateTimerProc); + } + else + { + KillTimer(WindowHandle, TIMER_ICON_CLICK_ACTIVATE); + } + } + } + break; + case WM_LBUTTONDBLCLK: + { + if (!PhGetIntegerSetting(L"IconSingleClick")) + { + if (PhNfMiniInfoEnabled) + { + // We will get another WM_LBUTTONUP message corresponding to the double-click, + // and we need to make sure that it doesn't start the activation timer again. + KillTimer(WindowHandle, TIMER_ICON_CLICK_ACTIVATE); + IconClickUpDueToDown = FALSE; + PhNfpDisableHover(); + } + + ProcessHacker_IconClick(WindowHandle); + } + } + break; + case WM_RBUTTONUP: + case WM_CONTEXTMENU: + { + POINT location; + + if (!PhGetIntegerSetting(L"IconSingleClick") && PhNfMiniInfoEnabled) + KillTimer(WindowHandle, TIMER_ICON_CLICK_ACTIVATE); + + PhPinMiniInformation(MiniInfoIconPinType, -1, 0, 0, NULL, NULL); + GetCursorPos(&location); + PhShowIconContextMenu(location); + } + break; + case NIN_KEYSELECT: + // HACK: explorer seems to send two NIN_KEYSELECT messages when the user selects the icon and presses ENTER. + if (GetForegroundWindow() != WindowHandle) + ProcessHacker_IconClick(WindowHandle); + break; + case NIN_BALLOONUSERCLICK: + PhShowDetailsForIconNotification(); + break; + case NIN_POPUPOPEN: + { + PH_NF_MSG_SHOWMINIINFOSECTION_DATA showMiniInfoSectionData; + POINT location; + + if (PhNfMiniInfoEnabled && !IconDisableHover && PhNfpGetShowMiniInfoSectionData(iconIndex, registeredIcon, &showMiniInfoSectionData)) + { + GetCursorPos(&location); + PhPinMiniInformation(MiniInfoIconPinType, 1, 0, PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED, + showMiniInfoSectionData.SectionName, &location); + } + } + break; + case NIN_POPUPCLOSE: + PhPinMiniInformation(MiniInfoIconPinType, -1, 350, 0, NULL, NULL); + break; + } +} + +VOID PhNfSetVisibleIcon( + _In_ PPH_NF_ICON Icon, + _In_ BOOLEAN Visible + ) +{ + if (Visible) + { + Icon->Flags |= PH_NF_ICON_ENABLED; + PhNfpAddNotifyIcon(Icon); + } + else + { + Icon->Flags &= ~PH_NF_ICON_ENABLED; + PhNfpRemoveNotifyIcon(Icon); + } +} + +BOOLEAN PhNfShowBalloonTip( + _In_ PWSTR Title, + _In_ PWSTR Text, + _In_ ULONG Timeout, + _In_ ULONG Flags + ) +{ + NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; + PPH_NF_ICON registeredIcon = NULL; + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + registeredIcon = icon; + break; + } + + if (!registeredIcon) + return FALSE; + + notifyIcon.uFlags = NIF_INFO | NIF_GUID; + notifyIcon.hWnd = PhMainWndHandle; + notifyIcon.uID = registeredIcon->IconId; + notifyIcon.guidItem = registeredIcon->IconGuid; + wcsncpy_s(notifyIcon.szInfoTitle, RTL_NUMBER_OF(notifyIcon.szInfoTitle), Title, _TRUNCATE); + wcsncpy_s(notifyIcon.szInfo, RTL_NUMBER_OF(notifyIcon.szInfo), Text, _TRUNCATE); + notifyIcon.uTimeout = Timeout; + notifyIcon.dwInfoFlags = Flags; + + Shell_NotifyIcon(NIM_MODIFY, ¬ifyIcon); + + return TRUE; +} + +HICON PhNfBitmapToIcon( + _In_ HBITMAP Bitmap + ) +{ + ICONINFO iconInfo; + + PhNfpGetBlackIcon(); + + iconInfo.fIcon = TRUE; + iconInfo.xHotspot = 0; + iconInfo.yHotspot = 0; + iconInfo.hbmMask = PhNfpBlackBitmap; + iconInfo.hbmColor = Bitmap; + + return CreateIconIndirect(&iconInfo); +} + +PPH_NF_ICON PhNfRegisterIcon( + _In_opt_ struct _PH_PLUGIN *Plugin, + _In_ ULONG Id, + _In_ GUID Guid, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_opt_ PPH_NF_ICON_UPDATE_CALLBACK UpdateCallback, + _In_opt_ PPH_NF_ICON_MESSAGE_CALLBACK MessageCallback + ) +{ + PPH_NF_ICON icon; + + icon = PhAllocateZero(sizeof(PH_NF_ICON)); + icon->Plugin = Plugin; + icon->SubId = Id; + icon->Context = Context; + icon->Pointers = &PhNfpPointers; + icon->Text = Text; + icon->Flags = Flags; + icon->UpdateCallback = UpdateCallback; + icon->MessageCallback = MessageCallback; + icon->TextCache = PhReferenceEmptyString(); + icon->IconId = PhTrayIconItemList->Count + 1; // HACK + icon->IconGuid = Guid; + + PhAddItemList(PhTrayIconItemList, icon); + + return icon; +} + +struct _PH_NF_ICON *PhNfPluginRegisterIcon( + _In_ struct _PH_PLUGIN * Plugin, + _In_ ULONG Id, + _In_ GUID Guid, + _In_opt_ PVOID Context, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData + ) +{ + return PhNfRegisterIcon( + Plugin, + Id, + Guid, + Context, + Text, + Flags, + RegistrationData->UpdateCallback, + RegistrationData->MessageCallback + ); +} + +PPH_NF_ICON PhNfGetIconById( + _In_ ULONG SubId + ) +{ + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (icon->SubId == SubId) + return icon; + } + + return NULL; +} + +PPH_NF_ICON PhNfFindIcon( + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ) +{ + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (icon->Plugin) + { + if (icon->SubId == SubId && + PhEqualStringRef(PluginName, &icon->Plugin->Name, TRUE)) + { + return icon; + } + } + } + + return NULL; +} + +BOOLEAN PhNfIconsEnabled( + VOID + ) +{ + BOOLEAN enabled = FALSE; + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (icon->Flags & PH_NF_ICON_ENABLED) + { + enabled = TRUE; + break; + } + } + + return enabled; +} + +VOID PhNfNotifyMiniInfoPinned( + _In_ BOOLEAN Pinned + ) +{ + if (PhNfMiniInfoPinned != Pinned) + { + PhNfMiniInfoPinned = Pinned; + + // Go through every icon and set/clear the NIF_SHOWTIP flag depending on whether the mini info window is + // pinned. If it's pinned then we want to show normal tooltips, because the section doesn't change + // automatically when the cursor hovers over an icon. + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpModifyNotifyIcon(icon, NIF_TIP, icon->TextCache, NULL); + } + } +} + +HICON PhNfpGetBlackIcon( + VOID + ) +{ + if (!PhNfpBlackIcon) + { + ULONG width; + ULONG height; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + ICONINFO iconInfo; + + PhNfpBeginBitmap2(&PhNfpBlackBitmapContext, &width, &height, &PhNfpBlackBitmap, &bits, &hdc, &oldBitmap); + memset(bits, 0, width * height * sizeof(ULONG)); + + iconInfo.fIcon = TRUE; + iconInfo.xHotspot = 0; + iconInfo.yHotspot = 0; + iconInfo.hbmMask = PhNfpBlackBitmap; + iconInfo.hbmColor = PhNfpBlackBitmap; + PhNfpBlackIcon = CreateIconIndirect(&iconInfo); + + SelectBitmap(hdc, oldBitmap); + } + + return PhNfpBlackIcon; +} + +BOOLEAN PhNfpAddNotifyIcon( + _In_ PPH_NF_ICON Icon + ) +{ + NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; + + if (PhMainWndExiting) + return FALSE; + + notifyIcon.hWnd = PhMainWndHandle; + notifyIcon.uID = Icon->IconId; + notifyIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_GUID; + notifyIcon.uCallbackMessage = WM_PH_NOTIFY_ICON_MESSAGE; + notifyIcon.guidItem = Icon->IconGuid; + wcsncpy_s( + notifyIcon.szTip, sizeof(notifyIcon.szTip) / sizeof(WCHAR), + PhGetStringOrDefault(Icon->TextCache, PhApplicationName), + _TRUNCATE + ); + notifyIcon.hIcon = PhNfpGetBlackIcon(); + + if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (Icon->Flags & PH_NF_ICON_NOSHOW_MINIINFO)) + notifyIcon.uFlags |= NIF_SHOWTIP; + + Shell_NotifyIcon(NIM_ADD, ¬ifyIcon); + + notifyIcon.uVersion = NOTIFYICON_VERSION_4; + Shell_NotifyIcon(NIM_SETVERSION, ¬ifyIcon); + + return TRUE; +} + +BOOLEAN PhNfpRemoveNotifyIcon( + _In_ PPH_NF_ICON Icon + ) +{ + NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; + + notifyIcon.uFlags = NIF_GUID; + notifyIcon.hWnd = PhMainWndHandle; + notifyIcon.uID = Icon->IconId; + notifyIcon.guidItem = Icon->IconGuid; + + Shell_NotifyIcon(NIM_DELETE, ¬ifyIcon); + + return TRUE; +} + +BOOLEAN PhNfpModifyNotifyIcon( + _In_ PPH_NF_ICON Icon, + _In_ ULONG Flags, + _In_opt_ PPH_STRING Text, + _In_opt_ HICON IconHandle + ) +{ + NOTIFYICONDATA notifyIcon = { sizeof(NOTIFYICONDATA) }; + + if (PhMainWndExiting) + return FALSE; + if (Icon->Flags & PH_NF_ICON_UNAVAILABLE) + return FALSE; + + notifyIcon.uFlags = Flags | NIF_GUID; + notifyIcon.hWnd = PhMainWndHandle; + notifyIcon.uID = Icon->IconId; + notifyIcon.guidItem = Icon->IconGuid; + notifyIcon.hIcon = IconHandle; + + if (!PhNfMiniInfoEnabled || PhNfMiniInfoPinned || (Icon->Flags & PH_NF_ICON_NOSHOW_MINIINFO)) + notifyIcon.uFlags |= NIF_SHOWTIP; + + if (Flags & NIF_TIP) + { + PhSwapReference(&Icon->TextCache, Text); + wcsncpy_s( + notifyIcon.szTip, + ARRAYSIZE(notifyIcon.szTip), + PhGetStringOrDefault(Text, PhApplicationName), + _TRUNCATE + ); + } + + if (!Shell_NotifyIcon(NIM_MODIFY, ¬ifyIcon)) + { + // Explorer probably died and we lost our icon. Try to add the icon, and try again. + PhNfpAddNotifyIcon(Icon); + Shell_NotifyIcon(NIM_MODIFY, ¬ifyIcon); + } + + return TRUE; +} + +//BOOLEAN PhNfpGetNotifyIconRect( +// _In_ ULONG Id, +// _In_opt_ PPH_RECTANGLE IconRectangle +// ) +//{ +// NOTIFYICONIDENTIFIER notifyIconId = { sizeof(NOTIFYICONIDENTIFIER) }; +// PPH_NF_ICON icon; +// RECT notifyRect; +// +// if (PhMainWndExiting) +// return FALSE; +// if ((icon = PhNfGetIconById(Id)) && (icon->Flags & PH_NF_ICON_UNAVAILABLE)) +// return FALSE; +// +// notifyIconId.hWnd = PhMainWndHandle; +// notifyIconId.uID = Id; +// +// if (SUCCEEDED(Shell_NotifyIconGetRect(¬ifyIconId, ¬ifyRect))) +// { +// *IconRectangle = PhRectToRectangle(notifyRect); +// return TRUE; +// } +// +// return FALSE; +//} + +VOID PhNfpProcessesUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + // We do icon updating on the provider thread so we don't block the main GUI when + // explorer is not responding. + + for (ULONG i = 0; i < PhTrayIconItemList->Count; i++) + { + PPH_NF_ICON icon = PhTrayIconItemList->Items[i]; + + if (!(icon->Flags & PH_NF_ICON_ENABLED)) + continue; + + PhNfpUpdateRegisteredIcon(icon); + } +} + +VOID PhNfpUpdateRegisteredIcon( + _In_ PPH_NF_ICON Icon + ) +{ + PVOID newIconOrBitmap; + ULONG updateFlags; + PPH_STRING newText; + HICON newIcon; + ULONG flags; + + if (!Icon->UpdateCallback) + return; + + newIconOrBitmap = NULL; + newText = NULL; + newIcon = NULL; + flags = 0; + + Icon->UpdateCallback( + Icon, + &newIconOrBitmap, + &updateFlags, + &newText, + Icon->Context + ); + + if (newIconOrBitmap) + { + if (updateFlags & PH_NF_UPDATE_IS_BITMAP) + newIcon = PhNfBitmapToIcon(newIconOrBitmap); + else + newIcon = newIconOrBitmap; + + flags |= NIF_ICON; + } + + if (newText) + flags |= NIF_TIP; + + if (flags != 0) + PhNfpModifyNotifyIcon(Icon, flags, newText, newIcon); + + if (newIcon && (updateFlags & PH_NF_UPDATE_IS_BITMAP)) + DestroyIcon(newIcon); + + if (newIconOrBitmap && (updateFlags & PH_NF_UPDATE_DESTROY_RESOURCE)) + { + if (updateFlags & PH_NF_UPDATE_IS_BITMAP) + DeleteObject(newIconOrBitmap); + else + DestroyIcon(newIconOrBitmap); + } + + if (newText) + PhDereferenceObject(newText); +} + +VOID PhNfpBeginBitmap( + _Out_ PULONG Width, + _Out_ PULONG Height, + _Out_ HBITMAP *Bitmap, + _Out_opt_ PVOID *Bits, + _Out_ HDC *Hdc, + _Out_ HBITMAP *OldBitmap + ) +{ + PhNfpBeginBitmap2(&PhNfpDefaultBitmapContext, Width, Height, Bitmap, Bits, Hdc, OldBitmap); +} + +VOID PhNfpBeginBitmap2( + _Inout_ PPH_NF_BITMAP Context, + _Out_ PULONG Width, + _Out_ PULONG Height, + _Out_ HBITMAP *Bitmap, + _Out_opt_ PVOID *Bits, + _Out_ HDC *Hdc, + _Out_ HBITMAP *OldBitmap + ) +{ + if (!Context->Initialized) + { + HDC screenHdc; + + screenHdc = GetDC(NULL); + Context->Hdc = CreateCompatibleDC(screenHdc); + + memset(&Context->Header, 0, sizeof(BITMAPINFOHEADER)); + Context->Header.biSize = sizeof(BITMAPINFOHEADER); + Context->Header.biWidth = PhSmallIconSize.X; + Context->Header.biHeight = PhSmallIconSize.Y; + Context->Header.biPlanes = 1; + Context->Header.biBitCount = 32; + Context->Bitmap = CreateDIBSection(screenHdc, (BITMAPINFO *)&Context->Header, DIB_RGB_COLORS, &Context->Bits, NULL, 0); + + ReleaseDC(NULL, screenHdc); + + Context->Initialized = TRUE; + } + + *Width = PhSmallIconSize.X; + *Height = PhSmallIconSize.Y; + *Bitmap = Context->Bitmap; + if (Bits) *Bits = Context->Bits; + *Hdc = Context->Hdc; + *OldBitmap = SelectBitmap(Context->Hdc, Context->Bitmap); +} + +VOID PhNfpCpuHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + PH_GRAPH_USE_LINE_2, + 2, + RGB(0x00, 0x00, 0x00), + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + PFLOAT lineData2; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxCpuProcessId; + PPH_PROCESS_ITEM maxCpuProcessItem; + PH_FORMAT format[8]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + lineData2 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, PhCpuKernelHistory.Count); + PhCopyCircularBuffer_FLOAT(&PhCpuKernelHistory, lineData1, lineDataCount); + PhCopyCircularBuffer_FLOAT(&PhCpuUserHistory, lineData2, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineData2 = lineData2; + drawInfo.LineColor1 = PhCsColorCpuKernel; + drawInfo.LineColor2 = PhCsColorCpuUser; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorCpuKernel); + drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorCpuUser); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (PhMaxCpuHistory.Count != 0) + maxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); + else + maxCpuProcessId = NULL; + + if (maxCpuProcessId) + maxCpuProcessItem = PhReferenceProcessItem(maxCpuProcessId); + else + maxCpuProcessItem = NULL; + + PhInitFormatS(&format[0], L"CPU Usage: "); + PhInitFormatF(&format[1], (PhCpuKernelUsage + PhCpuUserUsage) * 100, 2); + PhInitFormatC(&format[2], '%'); + + if (maxCpuProcessItem) + { + PhInitFormatC(&format[3], '\n'); + PhInitFormatSR(&format[4], maxCpuProcessItem->ProcessName->sr); + PhInitFormatS(&format[5], L": "); + PhInitFormatF(&format[6], maxCpuProcessItem->CpuUsage * 100, 2); + PhInitFormatC(&format[7], '%'); + } + + *NewText = PhFormat(format, maxCpuProcessItem ? 8 : 3, 128); + if (maxCpuProcessItem) PhDereferenceObject(maxCpuProcessItem); + + _freea(lineData2); + _freea(lineData1); +} + +VOID PhNfpIoHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + PH_GRAPH_USE_LINE_2, + 2, + RGB(0x00, 0x00, 0x00), + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + PFLOAT lineData2; + FLOAT max; + ULONG i; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxIoProcessId; + PPH_PROCESS_ITEM maxIoProcessItem; + PH_FORMAT format[8]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + lineData2 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, PhIoReadHistory.Count); + max = 1024 * 1024; // minimum scaling of 1 MB. + + for (i = 0; i < lineDataCount; i++) + { + lineData1[i] = + (FLOAT)PhGetItemCircularBuffer_ULONG64(&PhIoReadHistory, i) + + (FLOAT)PhGetItemCircularBuffer_ULONG64(&PhIoOtherHistory, i); + lineData2[i] = + (FLOAT)PhGetItemCircularBuffer_ULONG64(&PhIoWriteHistory, i); + + if (max < lineData1[i] + lineData2[i]) + max = lineData1[i] + lineData2[i]; + } + + PhDivideSinglesBySingle(lineData1, max, lineDataCount); + PhDivideSinglesBySingle(lineData2, max, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineData2 = lineData2; + drawInfo.LineColor1 = PhCsColorIoReadOther; + drawInfo.LineColor2 = PhCsColorIoWrite; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorIoReadOther); + drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorIoWrite); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (PhMaxIoHistory.Count != 0) + maxIoProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxIoHistory, 0)); + else + maxIoProcessId = NULL; + + if (maxIoProcessId) + maxIoProcessItem = PhReferenceProcessItem(maxIoProcessId); + else + maxIoProcessItem = NULL; + + PhInitFormatS(&format[0], L"I/O\nR: "); + PhInitFormatSize(&format[1], PhIoReadDelta.Delta); + PhInitFormatS(&format[2], L"\nW: "); + PhInitFormatSize(&format[3], PhIoWriteDelta.Delta); + PhInitFormatS(&format[4], L"\nO: "); + PhInitFormatSize(&format[5], PhIoOtherDelta.Delta); + + if (maxIoProcessItem) + { + PhInitFormatC(&format[6], '\n'); + PhInitFormatSR(&format[7], maxIoProcessItem->ProcessName->sr); + } + + *NewText = PhFormat(format, maxIoProcessItem ? 8 : 6, 128); + if (maxIoProcessItem) PhDereferenceObject(maxIoProcessItem); + + _freea(lineData2); + _freea(lineData1); +} + +VOID PhNfpCommitHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 2, + RGB(0x00, 0x00, 0x00), + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + ULONG i; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + DOUBLE commitFraction; + PH_FORMAT format[5]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, PhCommitHistory.Count); + + for (i = 0; i < lineDataCount; i++) + lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&PhCommitHistory, i); + + PhDivideSinglesBySingle(lineData1, (FLOAT)PhPerfInformation.CommitLimit, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineColor1 = PhCsColorPrivate; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPrivate); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + commitFraction = (DOUBLE)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit; + + PhInitFormatS(&format[0], L"Commit: "); + PhInitFormatSize(&format[1], UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], commitFraction * 100, 2); + PhInitFormatS(&format[4], L"%)"); + + *NewText = PhFormat(format, 5, 96); + + _freea(lineData1); +} + +VOID PhNfpPhysicalHistoryIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 2, + RGB(0x00, 0x00, 0x00), + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + ULONG i; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + ULONG physicalUsage; + FLOAT physicalFraction; + PH_FORMAT format[5]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, PhPhysicalHistory.Count); + + for (i = 0; i < lineDataCount; i++) + lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&PhPhysicalHistory, i); + + PhDivideSinglesBySingle(lineData1, (FLOAT)PhSystemBasicInformation.NumberOfPhysicalPages, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineColor1 = PhCsColorPhysical; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPhysical); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages; + physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages; + + PhInitFormatS(&format[0], L"Physical memory: "); + PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], physicalFraction * 100, 2); + PhInitFormatS(&format[4], L"%)"); + + *NewText = PhFormat(format, 5, 96); + + _freea(lineData1); +} + +VOID PhNfpCpuUsageIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + ULONG width; + ULONG height; + HBITMAP bitmap; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxCpuProcessId; + PPH_PROCESS_ITEM maxCpuProcessItem; + PPH_STRING maxCpuText = NULL; + + // Icon + + Icon->Pointers->BeginBitmap(&width, &height, &bitmap, NULL, &hdc, &oldBitmap); + + // This stuff is copied from CpuUsageIcon.cs (PH 1.x). + { + COLORREF kColor = PhCsColorCpuKernel; + COLORREF uColor = PhCsColorCpuUser; + COLORREF kbColor = PhHalveColorBrightness(PhCsColorCpuKernel); + COLORREF ubColor = PhHalveColorBrightness(PhCsColorCpuUser); + FLOAT k = PhCpuKernelUsage; + FLOAT u = PhCpuUserUsage; + LONG kl = (LONG)(k * height); + LONG ul = (LONG)(u * height); + RECT rect; + HBRUSH dcBrush; + HPEN dcPen; + POINT points[2]; + + dcBrush = GetStockBrush(DC_BRUSH); + dcPen = GetStockPen(DC_PEN); + rect.left = 0; + rect.top = 0; + rect.right = width; + rect.bottom = height; + SetDCBrushColor(hdc, RGB(0x00, 0x00, 0x00)); + FillRect(hdc, &rect, dcBrush); + + // Draw the base line. + if (kl + ul == 0) + { + SelectPen(hdc, dcPen); + SetDCPenColor(hdc, uColor); + points[0].x = 0; + points[0].y = height - 1; + points[1].x = width; + points[1].y = height - 1; + Polyline(hdc, points, 2); + } + else + { + rect.left = 0; + rect.top = height - ul - kl; + rect.right = width; + rect.bottom = height - kl; + SetDCBrushColor(hdc, ubColor); + FillRect(hdc, &rect, dcBrush); + + points[0].x = 0; + points[0].y = height - 1 - ul - kl; + if (points[0].y < 0) points[0].y = 0; + points[1].x = width; + points[1].y = points[0].y; + SelectPen(hdc, dcPen); + SetDCPenColor(hdc, uColor); + Polyline(hdc, points, 2); + + if (kl != 0) + { + rect.left = 0; + rect.top = height - kl; + rect.right = width; + rect.bottom = height; + SetDCBrushColor(hdc, kbColor); + FillRect(hdc, &rect, dcBrush); + + points[0].x = 0; + points[0].y = height - 1 - kl; + if (points[0].y < 0) points[0].y = 0; + points[1].x = width; + points[1].y = points[0].y; + SelectPen(hdc, dcPen); + SetDCPenColor(hdc, kColor); + Polyline(hdc, points, 2); + } + } + } + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (PhMaxCpuHistory.Count != 0) + maxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); + else + maxCpuProcessId = NULL; + + if (maxCpuProcessId) + { + if (maxCpuProcessItem = PhReferenceProcessItem(maxCpuProcessId)) + { + maxCpuText = PhFormatString( + L"\n%s: %.2f%%", + maxCpuProcessItem->ProcessName->Buffer, + maxCpuProcessItem->CpuUsage * 100 + ); + PhDereferenceObject(maxCpuProcessItem); + } + } + + *NewText = PhFormatString(L"CPU usage: %.2f%%%s", (PhCpuKernelUsage + PhCpuUserUsage) * 100, PhGetStringOrEmpty(maxCpuText)); + if (maxCpuText) PhDereferenceObject(maxCpuText); +} + +// Text icons + +VOID PhNfpCpuUsageTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 2, + RGB(0x00, 0x00, 0x00), + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + PH_FORMAT format[5]; + HANDLE maxCpuProcessId; + PPH_PROCESS_ITEM maxCpuProcessItem; + PPH_STRING maxCpuText = NULL; + PPH_STRING text; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + PhInitFormatF(&format[0], (PhCpuKernelUsage + PhCpuUserUsage) * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhCsColorCpuKernel; + if (bits) + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (PhMaxCpuHistory.Count != 0) + maxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); + else + maxCpuProcessId = NULL; + + if (maxCpuProcessId) + { + if (maxCpuProcessItem = PhReferenceProcessItem(maxCpuProcessId)) + { + maxCpuText = PhFormatString( + L"\n%s: %.2f%%", + maxCpuProcessItem->ProcessName->Buffer, + maxCpuProcessItem->CpuUsage * 100 + ); + PhDereferenceObject(maxCpuProcessItem); + } + } + + *NewText = PhFormatString(L"CPU usage: %.2f%%%s", (PhCpuKernelUsage + PhCpuUserUsage) * 100, PhGetStringOrEmpty(maxCpuText)); + if (maxCpuText) PhDereferenceObject(maxCpuText); +} + +VOID PhNfpIoUsageTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 0, + RGB(0x00, 0x00, 0x00), + 0, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxIoProcessId; + PPH_PROCESS_ITEM maxIoProcessItem; + PH_FORMAT format[8]; + PPH_STRING text; + static ULONG64 maxValue = 100000 * 1024; // minimum scaling of 100 MB. + + // TODO: Reset maxValue every X amount of time. + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + if (maxValue < (PhIoReadDelta.Delta + PhIoWriteDelta.Delta + PhIoOtherDelta.Delta)) + maxValue = (PhIoReadDelta.Delta + PhIoWriteDelta.Delta + PhIoOtherDelta.Delta); + + PhInitFormatF(&format[0], (FLOAT)(PhIoReadDelta.Delta + PhIoWriteDelta.Delta + PhIoOtherDelta.Delta) / maxValue * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhCsColorIoReadOther; + if (bits) + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (PhMaxIoHistory.Count != 0) + maxIoProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxIoHistory, 0)); + else + maxIoProcessId = NULL; + + if (maxIoProcessId) + maxIoProcessItem = PhReferenceProcessItem(maxIoProcessId); + else + maxIoProcessItem = NULL; + + PhInitFormatS(&format[0], L"I/O\nR: "); + PhInitFormatSize(&format[1], PhIoReadDelta.Delta); + PhInitFormatS(&format[2], L"\nW: "); + PhInitFormatSize(&format[3], PhIoWriteDelta.Delta); + PhInitFormatS(&format[4], L"\nO: "); + PhInitFormatSize(&format[5], PhIoOtherDelta.Delta); + + if (maxIoProcessItem) + { + PhInitFormatC(&format[6], '\n'); + PhInitFormatSR(&format[7], maxIoProcessItem->ProcessName->sr); + } + + *NewText = PhFormat(format, maxIoProcessItem ? 8 : 6, 128); + if (maxIoProcessItem) PhDereferenceObject(maxIoProcessItem); +} + +VOID PhNfpCommitTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 0, + RGB(0x00, 0x00, 0x00), + 0, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + DOUBLE commitFraction; + PH_FORMAT format[5]; + PPH_STRING text; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + PhInitFormatF(&format[0], (FLOAT)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhCsColorPrivate; + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + commitFraction = (DOUBLE)PhPerfInformation.CommittedPages / PhPerfInformation.CommitLimit; + + PhInitFormatS(&format[0], L"Commit: "); + PhInitFormatSize(&format[1], UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], commitFraction * 100, 2); + PhInitFormatS(&format[4], L"%)"); + + *NewText = PhFormat(format, 5, 96); +} + +VOID PhNfpPhysicalUsageTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 0, + RGB(0x00, 0x00, 0x00), + 0, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + ULONG physicalUsage; + FLOAT physicalFraction; + PH_FORMAT format[5]; + PPH_STRING text; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages; + physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages; + + PhInitFormatF(&format[0], (FLOAT)physicalFraction * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhCsColorPhysical; + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages; + physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages; + + PhInitFormatS(&format[0], L"Physical memory: "); + PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], physicalFraction * 100, 2); + PhInitFormatS(&format[4], L"%)"); + + *NewText = PhFormat(format, 5, 96); +} + +BOOLEAN PhNfpGetShowMiniInfoSectionData( + _In_ ULONG IconIndex, + _In_ PPH_NF_ICON RegisteredIcon, + _Out_ PPH_NF_MSG_SHOWMINIINFOSECTION_DATA Data + ) +{ + BOOLEAN showMiniInfo = FALSE; + + if (RegisteredIcon && RegisteredIcon->MessageCallback) + { + Data->SectionName = NULL; + + if (!(RegisteredIcon->Flags & PH_NF_ICON_NOSHOW_MINIINFO)) + { + if (RegisteredIcon->MessageCallback) + { + RegisteredIcon->MessageCallback( + RegisteredIcon, + (ULONG_PTR)Data, + MAKELPARAM(PH_NF_MSG_SHOWMINIINFOSECTION, 0), + RegisteredIcon->Context + ); + } + + showMiniInfo = TRUE; + } + } + else + { + switch (IconIndex) + { + case PH_TRAY_ICON_ID_CPU_HISTORY: + case PH_TRAY_ICON_ID_CPU_USAGE: + case PH_TRAY_ICON_ID_CPU_TEXT: + Data->SectionName = L"CPU"; + break; + case PH_TRAY_ICON_ID_IO_HISTORY: + case PH_TRAY_ICON_ID_IO_TEXT: + Data->SectionName = L"I/O"; + break; + case PH_TRAY_ICON_ID_COMMIT_HISTORY: + case PH_TRAY_ICON_ID_COMMIT_TEXT: + Data->SectionName = L"Commit charge"; + break; + case PH_TRAY_ICON_ID_PHYSICAL_HISTORY: + case PH_TRAY_ICON_ID_PHYSICAL_TEXT: + Data->SectionName = L"Physical memory"; + break; + } + + showMiniInfo = TRUE; + } + + return showMiniInfo; +} + +VOID PhNfpIconClickActivateTimerProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ UINT_PTR idEvent, + _In_ ULONG dwTime + ) +{ + PhPinMiniInformation(MiniInfoActivePinType, 1, 0, + PH_MINIINFO_ACTIVATE_WINDOW | PH_MINIINFO_DONT_CHANGE_SECTION_IF_PINNED, + IconClickShowMiniInfoSectionData.SectionName, &IconClickLocation); + KillTimer(PhMainWndHandle, TIMER_ICON_CLICK_ACTIVATE); +} + +VOID PhNfpDisableHover( + VOID + ) +{ + IconDisableHover = TRUE; + SetTimer(PhMainWndHandle, TIMER_ICON_RESTORE_HOVER, NFP_ICON_RESTORE_HOVER_DELAY, PhNfpIconRestoreHoverTimerProc); +} + +VOID PhNfpIconRestoreHoverTimerProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ UINT_PTR idEvent, + _In_ ULONG dwTime + ) +{ + IconDisableHover = FALSE; + KillTimer(PhMainWndHandle, TIMER_ICON_RESTORE_HOVER); +} diff --git a/ProcessHacker/ntobjprp.c b/ProcessHacker/ntobjprp.c index 344d607ca98a..ff950c13fbc1 100644 --- a/ProcessHacker/ntobjprp.c +++ b/ProcessHacker/ntobjprp.c @@ -1,757 +1,547 @@ -/* - * Process Hacker - - * properties for NT objects - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -typedef struct _COMMON_PAGE_CONTEXT -{ - PPH_OPEN_OBJECT OpenObject; - PVOID Context; -} COMMON_PAGE_CONTEXT, *PCOMMON_PAGE_CONTEXT; - -HPROPSHEETPAGE PhpCommonCreatePage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_ PWSTR Template, - _In_ DLGPROC DlgProc - ); - -INT CALLBACK PhpCommonPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ); - -INT_PTR CALLBACK PhpEventPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpEventPairPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpMutantPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpSectionPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpSemaphorePageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpTimerPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static HPROPSHEETPAGE PhpCommonCreatePage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_ PWSTR Template, - _In_ DLGPROC DlgProc - ) -{ - HPROPSHEETPAGE propSheetPageHandle; - PROPSHEETPAGE propSheetPage; - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhCreateAlloc(sizeof(COMMON_PAGE_CONTEXT)); - memset(pageContext, 0, sizeof(COMMON_PAGE_CONTEXT)); - pageContext->OpenObject = OpenObject; - pageContext->Context = Context; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USECALLBACK; - propSheetPage.pszTemplate = Template; - propSheetPage.pfnDlgProc = DlgProc; - propSheetPage.lParam = (LPARAM)pageContext; - propSheetPage.pfnCallback = PhpCommonPropPageProc; - - propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); - // CreatePropertySheetPage would have sent PSPCB_ADDREF (below), - // which would have added a reference. - PhDereferenceObject(pageContext); - - return propSheetPageHandle; -} - -INT CALLBACK PhpCommonPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = (PCOMMON_PAGE_CONTEXT)ppsp->lParam; - - if (uMsg == PSPCB_ADDREF) - { - PhReferenceObject(pageContext); - } - else if (uMsg == PSPCB_RELEASE) - { - PhDereferenceObject(pageContext); - } - - return 1; -} - -FORCEINLINE PCOMMON_PAGE_CONTEXT PhpCommonPageHeader( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return (PCOMMON_PAGE_CONTEXT)PhpGenericPropertyPageHeader( - hwndDlg, uMsg, wParam, lParam, L"PageContext"); -} - -HPROPSHEETPAGE PhCreateEventPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ) -{ - return PhpCommonCreatePage( - OpenObject, - Context, - MAKEINTRESOURCE(IDD_OBJEVENT), - PhpEventPageProc - ); -} - -static VOID PhpRefreshEventPageInfo( - _In_ HWND hwndDlg, - _In_ PCOMMON_PAGE_CONTEXT PageContext - ) -{ - HANDLE eventHandle; - - if (NT_SUCCESS(PageContext->OpenObject( - &eventHandle, - EVENT_QUERY_STATE, - PageContext->Context - ))) - { - EVENT_BASIC_INFORMATION basicInfo; - PWSTR eventType = L"Unknown"; - PWSTR eventState = L"Unknown"; - - if (NT_SUCCESS(PhGetEventBasicInformation(eventHandle, &basicInfo))) - { - switch (basicInfo.EventType) - { - case NotificationEvent: - eventType = L"Notification"; - break; - case SynchronizationEvent: - eventType = L"Synchronization"; - break; - } - - eventState = basicInfo.EventState > 0 ? L"True" : L"False"; - } - - SetDlgItemText(hwndDlg, IDC_TYPE, eventType); - SetDlgItemText(hwndDlg, IDC_SIGNALED, eventState); - - NtClose(eventHandle); - } -} - -INT_PTR CALLBACK PhpEventPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!pageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpRefreshEventPageInfo(hwndDlg, pageContext); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_SET: - case IDC_RESET: - case IDC_PULSE: - { - NTSTATUS status; - HANDLE eventHandle; - - if (NT_SUCCESS(status = pageContext->OpenObject( - &eventHandle, - EVENT_MODIFY_STATE, - pageContext->Context - ))) - { - switch (LOWORD(wParam)) - { - case IDC_SET: - NtSetEvent(eventHandle, NULL); - break; - case IDC_RESET: - NtResetEvent(eventHandle, NULL); - break; - case IDC_PULSE: - NtPulseEvent(eventHandle, NULL); - break; - } - - NtClose(eventHandle); - } - - PhpRefreshEventPageInfo(hwndDlg, pageContext); - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to open the event", status, 0); - } - break; - } - } - break; - } - - return FALSE; -} - -HPROPSHEETPAGE PhCreateEventPairPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ) -{ - return PhpCommonCreatePage( - OpenObject, - Context, - MAKEINTRESOURCE(IDD_OBJEVENTPAIR), - PhpEventPairPageProc - ); -} - -INT_PTR CALLBACK PhpEventPairPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!pageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - // Nothing - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_SETLOW: - case IDC_SETHIGH: - { - NTSTATUS status; - HANDLE eventPairHandle; - - if (NT_SUCCESS(status = pageContext->OpenObject( - &eventPairHandle, - EVENT_PAIR_ALL_ACCESS, - pageContext->Context - ))) - { - switch (LOWORD(wParam)) - { - case IDC_SETLOW: - NtSetLowEventPair(eventPairHandle); - break; - case IDC_SETHIGH: - NtSetHighEventPair(eventPairHandle); - break; - } - - NtClose(eventPairHandle); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to open the event pair", status, 0); - } - break; - } - } - break; - } - - return FALSE; -} - -HPROPSHEETPAGE PhCreateMutantPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ) -{ - return PhpCommonCreatePage( - OpenObject, - Context, - MAKEINTRESOURCE(IDD_OBJMUTANT), - PhpMutantPageProc - ); -} - -static VOID PhpRefreshMutantPageInfo( - _In_ HWND hwndDlg, - _In_ PCOMMON_PAGE_CONTEXT PageContext - ) -{ - HANDLE mutantHandle; - - if (NT_SUCCESS(PageContext->OpenObject( - &mutantHandle, - SEMAPHORE_QUERY_STATE, - PageContext->Context - ))) - { - MUTANT_BASIC_INFORMATION basicInfo; - MUTANT_OWNER_INFORMATION ownerInfo; - - if (NT_SUCCESS(PhGetMutantBasicInformation(mutantHandle, &basicInfo))) - { - SetDlgItemInt(hwndDlg, IDC_COUNT, basicInfo.CurrentCount, TRUE); - SetDlgItemText(hwndDlg, IDC_ABANDONED, basicInfo.AbandonedState ? L"True" : L"False"); - } - else - { - SetDlgItemText(hwndDlg, IDC_COUNT, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_ABANDONED, L"Unknown"); - } - - if ( - WindowsVersion >= WINDOWS_VISTA && - NT_SUCCESS(PhGetMutantOwnerInformation(mutantHandle, &ownerInfo)) - ) - { - PPH_STRING name; - - if (ownerInfo.ClientId.UniqueProcess != NULL) - { - name = PhGetClientIdName(&ownerInfo.ClientId); - SetDlgItemText(hwndDlg, IDC_OWNER, name->Buffer); - PhDereferenceObject(name); - } - else - { - SetDlgItemText(hwndDlg, IDC_OWNER, L"N/A"); - } - } - else - { - SetDlgItemText(hwndDlg, IDC_OWNER, L"Unknown"); - } - - NtClose(mutantHandle); - } -} - -INT_PTR CALLBACK PhpMutantPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!pageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - if (WindowsVersion < WINDOWS_VISTA) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_OWNERLABEL), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_OWNER), FALSE); - } - - PhpRefreshMutantPageInfo(hwndDlg, pageContext); - } - break; - } - - return FALSE; -} - -HPROPSHEETPAGE PhCreateSectionPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ) -{ - return PhpCommonCreatePage( - OpenObject, - Context, - MAKEINTRESOURCE(IDD_OBJSECTION), - PhpSectionPageProc - ); -} - -static VOID PhpRefreshSectionPageInfo( - _In_ HWND hwndDlg, - _In_ PCOMMON_PAGE_CONTEXT PageContext - ) -{ - HANDLE sectionHandle; - SECTION_BASIC_INFORMATION basicInfo; - PWSTR sectionType = L"Unknown"; - PPH_STRING sectionSize = NULL; - PPH_STRING fileName = NULL; - - if (!NT_SUCCESS(PageContext->OpenObject( - §ionHandle, - SECTION_QUERY | SECTION_MAP_READ, - PageContext->Context - ))) - { - if (!NT_SUCCESS(PageContext->OpenObject( - §ionHandle, - SECTION_QUERY | SECTION_MAP_READ, - PageContext->Context - ))) - { - return; - } - } - - if (NT_SUCCESS(PhGetSectionBasicInformation(sectionHandle, &basicInfo))) - { - if (basicInfo.AllocationAttributes & SEC_COMMIT) - sectionType = L"Commit"; - else if (basicInfo.AllocationAttributes & SEC_FILE) - sectionType = L"File"; - else if (basicInfo.AllocationAttributes & SEC_IMAGE) - sectionType = L"Image"; - else if (basicInfo.AllocationAttributes & SEC_RESERVE) - sectionType = L"Reserve"; - - sectionSize = PhaFormatSize(basicInfo.MaximumSize.QuadPart, -1); - } - - if (NT_SUCCESS(PhGetSectionFileName(sectionHandle, &fileName))) - { - PPH_STRING newFileName; - - PH_AUTO(fileName); - - if (newFileName = PhResolveDevicePrefix(fileName)) - fileName = PH_AUTO(newFileName); - } - - SetDlgItemText(hwndDlg, IDC_TYPE, sectionType); - SetDlgItemText(hwndDlg, IDC_SIZE_, PhGetStringOrDefault(sectionSize, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_FILE, PhGetStringOrDefault(fileName, L"N/A")); - - NtClose(sectionHandle); -} - -INT_PTR CALLBACK PhpSectionPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!pageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpRefreshSectionPageInfo(hwndDlg, pageContext); - } - break; - } - - return FALSE; -} - -HPROPSHEETPAGE PhCreateSemaphorePage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ) -{ - return PhpCommonCreatePage( - OpenObject, - Context, - MAKEINTRESOURCE(IDD_OBJSEMAPHORE), - PhpSemaphorePageProc - ); -} - -static VOID PhpRefreshSemaphorePageInfo( - _In_ HWND hwndDlg, - _In_ PCOMMON_PAGE_CONTEXT PageContext - ) -{ - HANDLE semaphoreHandle; - - if (NT_SUCCESS(PageContext->OpenObject( - &semaphoreHandle, - SEMAPHORE_QUERY_STATE, - PageContext->Context - ))) - { - SEMAPHORE_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhGetSemaphoreBasicInformation(semaphoreHandle, &basicInfo))) - { - SetDlgItemInt(hwndDlg, IDC_CURRENTCOUNT, basicInfo.CurrentCount, TRUE); - SetDlgItemInt(hwndDlg, IDC_MAXIMUMCOUNT, basicInfo.MaximumCount, TRUE); - } - else - { - SetDlgItemText(hwndDlg, IDC_CURRENTCOUNT, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_MAXIMUMCOUNT, L"Unknown"); - } - - NtClose(semaphoreHandle); - } -} - -INT_PTR CALLBACK PhpSemaphorePageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!pageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpRefreshSemaphorePageInfo(hwndDlg, pageContext); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_ACQUIRE: - case IDC_RELEASE: - { - NTSTATUS status; - HANDLE semaphoreHandle; - - if (NT_SUCCESS(status = pageContext->OpenObject( - &semaphoreHandle, - LOWORD(wParam) == IDC_ACQUIRE ? SYNCHRONIZE : SEMAPHORE_MODIFY_STATE, - pageContext->Context - ))) - { - switch (LOWORD(wParam)) - { - case IDC_ACQUIRE: - { - LARGE_INTEGER timeout; - - timeout.QuadPart = 0; - NtWaitForSingleObject(semaphoreHandle, FALSE, &timeout); - } - break; - case IDC_RELEASE: - NtReleaseSemaphore(semaphoreHandle, 1, NULL); - break; - } - - NtClose(semaphoreHandle); - } - - PhpRefreshSemaphorePageInfo(hwndDlg, pageContext); - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to open the semaphore", status, 0); - } - break; - } - } - break; - } - - return FALSE; -} - -HPROPSHEETPAGE PhCreateTimerPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context - ) -{ - return PhpCommonCreatePage( - OpenObject, - Context, - MAKEINTRESOURCE(IDD_OBJTIMER), - PhpTimerPageProc - ); -} - -static VOID PhpRefreshTimerPageInfo( - _In_ HWND hwndDlg, - _In_ PCOMMON_PAGE_CONTEXT PageContext - ) -{ - HANDLE timerHandle; - - if (NT_SUCCESS(PageContext->OpenObject( - &timerHandle, - TIMER_QUERY_STATE, - PageContext->Context - ))) - { - TIMER_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhGetTimerBasicInformation(timerHandle, &basicInfo))) - { - SetDlgItemText(hwndDlg, IDC_SIGNALED, basicInfo.TimerState ? L"True" : L"False"); - } - else - { - SetDlgItemText(hwndDlg, IDC_SIGNALED, L"Unknown"); - } - - NtClose(timerHandle); - } -} - -INT_PTR CALLBACK PhpTimerPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!pageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpRefreshTimerPageInfo(hwndDlg, pageContext); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_CANCEL: - { - NTSTATUS status; - HANDLE timerHandle; - - if (NT_SUCCESS(status = pageContext->OpenObject( - &timerHandle, - TIMER_MODIFY_STATE, - pageContext->Context - ))) - { - switch (LOWORD(wParam)) - { - case IDC_CANCEL: - NtCancelTimer(timerHandle, NULL); - break; - } - - NtClose(timerHandle); - } - - PhpRefreshTimerPageInfo(hwndDlg, pageContext); - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to open the timer", status, 0); - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * properties for NT objects + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include +#include + +typedef struct _COMMON_PAGE_CONTEXT +{ + PPH_OPEN_OBJECT OpenObject; + PVOID Context; +} COMMON_PAGE_CONTEXT, *PCOMMON_PAGE_CONTEXT; + +HPROPSHEETPAGE PhpCommonCreatePage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context, + _In_ PWSTR Template, + _In_ DLGPROC DlgProc + ); + +INT CALLBACK PhpCommonPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +INT_PTR CALLBACK PhpEventPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpEventPairPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpSemaphorePageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpTimerPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static HPROPSHEETPAGE PhpCommonCreatePage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context, + _In_ PWSTR Template, + _In_ DLGPROC DlgProc + ) +{ + HPROPSHEETPAGE propSheetPageHandle; + PROPSHEETPAGE propSheetPage; + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = PhCreateAlloc(sizeof(COMMON_PAGE_CONTEXT)); + memset(pageContext, 0, sizeof(COMMON_PAGE_CONTEXT)); + pageContext->OpenObject = OpenObject; + pageContext->Context = Context; + + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USECALLBACK; + propSheetPage.pszTemplate = Template; + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = DlgProc; + propSheetPage.lParam = (LPARAM)pageContext; + propSheetPage.pfnCallback = PhpCommonPropPageProc; + + propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); + // CreatePropertySheetPage would have sent PSPCB_ADDREF (below), + // which would have added a reference. + PhDereferenceObject(pageContext); + + return propSheetPageHandle; +} + +INT CALLBACK PhpCommonPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) +{ + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = (PCOMMON_PAGE_CONTEXT)ppsp->lParam; + + if (uMsg == PSPCB_ADDREF) + { + PhReferenceObject(pageContext); + } + else if (uMsg == PSPCB_RELEASE) + { + PhDereferenceObject(pageContext); + } + + return 1; +} + +FORCEINLINE PCOMMON_PAGE_CONTEXT PhpCommonPageHeader( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return PhpGenericPropertyPageHeader(hwndDlg, uMsg, wParam, lParam, 2); +} + +HPROPSHEETPAGE PhCreateEventPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ) +{ + return PhpCommonCreatePage( + OpenObject, + Context, + MAKEINTRESOURCE(IDD_OBJEVENT), + PhpEventPageProc + ); +} + +static VOID PhpRefreshEventPageInfo( + _In_ HWND hwndDlg, + _In_ PCOMMON_PAGE_CONTEXT PageContext + ) +{ + HANDLE eventHandle; + + if (NT_SUCCESS(PageContext->OpenObject( + &eventHandle, + EVENT_QUERY_STATE, + PageContext->Context + ))) + { + EVENT_BASIC_INFORMATION basicInfo; + PWSTR eventType = L"Unknown"; + PWSTR eventState = L"Unknown"; + + if (NT_SUCCESS(PhGetEventBasicInformation(eventHandle, &basicInfo))) + { + switch (basicInfo.EventType) + { + case NotificationEvent: + eventType = L"Notification"; + break; + case SynchronizationEvent: + eventType = L"Synchronization"; + break; + } + + eventState = basicInfo.EventState > 0 ? L"True" : L"False"; + } + + PhSetDialogItemText(hwndDlg, IDC_TYPE, eventType); + PhSetDialogItemText(hwndDlg, IDC_SIGNALED, eventState); + + NtClose(eventHandle); + } +} + +INT_PTR CALLBACK PhpEventPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!pageContext) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhpRefreshEventPageInfo(hwndDlg, pageContext); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_SET: + case IDC_RESET: + case IDC_PULSE: + { + NTSTATUS status; + HANDLE eventHandle; + + if (NT_SUCCESS(status = pageContext->OpenObject( + &eventHandle, + EVENT_MODIFY_STATE, + pageContext->Context + ))) + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_SET: + NtSetEvent(eventHandle, NULL); + break; + case IDC_RESET: + NtResetEvent(eventHandle, NULL); + break; + case IDC_PULSE: + NtPulseEvent(eventHandle, NULL); + break; + } + + NtClose(eventHandle); + } + + PhpRefreshEventPageInfo(hwndDlg, pageContext); + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to open the event", status, 0); + } + break; + } + } + break; + } + + return FALSE; +} + +HPROPSHEETPAGE PhCreateEventPairPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ) +{ + return PhpCommonCreatePage( + OpenObject, + Context, + MAKEINTRESOURCE(IDD_OBJEVENTPAIR), + PhpEventPairPageProc + ); +} + +INT_PTR CALLBACK PhpEventPairPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!pageContext) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Nothing + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_SETLOW: + case IDC_SETHIGH: + { + NTSTATUS status; + HANDLE eventPairHandle; + + if (NT_SUCCESS(status = pageContext->OpenObject( + &eventPairHandle, + EVENT_PAIR_ALL_ACCESS, + pageContext->Context + ))) + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_SETLOW: + NtSetLowEventPair(eventPairHandle); + break; + case IDC_SETHIGH: + NtSetHighEventPair(eventPairHandle); + break; + } + + NtClose(eventPairHandle); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to open the event pair", status, 0); + } + break; + } + } + break; + } + + return FALSE; +} + +HPROPSHEETPAGE PhCreateSemaphorePage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ) +{ + return PhpCommonCreatePage( + OpenObject, + Context, + MAKEINTRESOURCE(IDD_OBJSEMAPHORE), + PhpSemaphorePageProc + ); +} + +static VOID PhpRefreshSemaphorePageInfo( + _In_ HWND hwndDlg, + _In_ PCOMMON_PAGE_CONTEXT PageContext + ) +{ + HANDLE semaphoreHandle; + + if (NT_SUCCESS(PageContext->OpenObject( + &semaphoreHandle, + SEMAPHORE_QUERY_STATE, + PageContext->Context + ))) + { + SEMAPHORE_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetSemaphoreBasicInformation(semaphoreHandle, &basicInfo))) + { + PhSetDialogItemValue(hwndDlg, IDC_CURRENTCOUNT, basicInfo.CurrentCount, TRUE); + PhSetDialogItemValue(hwndDlg, IDC_MAXIMUMCOUNT, basicInfo.MaximumCount, TRUE); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_CURRENTCOUNT, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_MAXIMUMCOUNT, L"Unknown"); + } + + NtClose(semaphoreHandle); + } +} + +INT_PTR CALLBACK PhpSemaphorePageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!pageContext) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhpRefreshSemaphorePageInfo(hwndDlg, pageContext); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_ACQUIRE: + case IDC_RELEASE: + { + NTSTATUS status; + HANDLE semaphoreHandle; + + if (NT_SUCCESS(status = pageContext->OpenObject( + &semaphoreHandle, + GET_WM_COMMAND_ID(wParam, lParam) == IDC_ACQUIRE ? SYNCHRONIZE : SEMAPHORE_MODIFY_STATE, + pageContext->Context + ))) + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_ACQUIRE: + { + LARGE_INTEGER timeout; + + timeout.QuadPart = 0; + NtWaitForSingleObject(semaphoreHandle, FALSE, &timeout); + } + break; + case IDC_RELEASE: + NtReleaseSemaphore(semaphoreHandle, 1, NULL); + break; + } + + NtClose(semaphoreHandle); + } + + PhpRefreshSemaphorePageInfo(hwndDlg, pageContext); + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to open the semaphore", status, 0); + } + break; + } + } + break; + } + + return FALSE; +} + +HPROPSHEETPAGE PhCreateTimerPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PVOID Context + ) +{ + return PhpCommonCreatePage( + OpenObject, + Context, + MAKEINTRESOURCE(IDD_OBJTIMER), + PhpTimerPageProc + ); +} + +static VOID PhpRefreshTimerPageInfo( + _In_ HWND hwndDlg, + _In_ PCOMMON_PAGE_CONTEXT PageContext + ) +{ + HANDLE timerHandle; + + if (NT_SUCCESS(PageContext->OpenObject( + &timerHandle, + TIMER_QUERY_STATE, + PageContext->Context + ))) + { + TIMER_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetTimerBasicInformation(timerHandle, &basicInfo))) + { + PhSetDialogItemText(hwndDlg, IDC_SIGNALED, basicInfo.TimerState ? L"True" : L"False"); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_SIGNALED, L"Unknown"); + } + + NtClose(timerHandle); + } +} + +INT_PTR CALLBACK PhpTimerPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = PhpCommonPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!pageContext) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhpRefreshTimerPageInfo(hwndDlg, pageContext); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_CANCEL: + { + NTSTATUS status; + HANDLE timerHandle; + + if (NT_SUCCESS(status = pageContext->OpenObject( + &timerHandle, + TIMER_MODIFY_STATE, + pageContext->Context + ))) + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_CANCEL: + NtCancelTimer(timerHandle, NULL); + break; + } + + NtClose(timerHandle); + } + + PhpRefreshTimerPageInfo(hwndDlg, pageContext); + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to open the timer", status, 0); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/options.c b/ProcessHacker/options.c index 8651a210c01e..49579d3f7adb 100644 --- a/ProcessHacker/options.c +++ b/ProcessHacker/options.c @@ -1,1265 +1,1958 @@ -/* - * Process Hacker - - * options window - * - * Copyright (C) 2010-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#define WM_PH_CHILD_EXIT (WM_APP + 301) - -INT CALLBACK PhpOptionsPropSheetProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam - ); - -LRESULT CALLBACK PhpOptionsWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -INT_PTR CALLBACK PhpOptionsGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpOptionsGraphsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// All -static BOOLEAN PageInit; -static BOOLEAN PressedOk; -static BOOLEAN RestartRequired; -static POINT StartLocation; - -// General -static PH_STRINGREF CurrentUserRunKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"); -static BOOLEAN CurrentUserRunPresent; -static BOOLEAN CurrentUserRunStartHidden; -static HFONT CurrentFontInstance; -static PPH_STRING NewFontSelection; - -// Advanced -static PH_STRINGREF TaskMgrImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\taskmgr.exe"); -static PPH_STRING OldTaskMgrDebugger; -static BOOLEAN OldReplaceTaskMgr; -static HWND WindowHandleForElevate; - -// Highlighting -static HWND HighlightingListViewHandle; - -VOID PhShowOptionsDialog( - _In_ HWND ParentWindowHandle - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[5]; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_USECALLBACK | - PSH_USEPSTARTPAGE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = L"Options"; - propSheetHeader.nPages = 0; - propSheetHeader.pStartPage = !PhStartupParameters.ShowOptions ? L"General" : L"Advanced"; - propSheetHeader.phpage = pages; - propSheetHeader.pfnCallback = PhpOptionsPropSheetProc; - - if (!PhStartupParameters.ShowOptions) - { - // Disable all pages other than Advanced. - // General page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTGENERAL); - propSheetPage.pfnDlgProc = PhpOptionsGeneralDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - } - - // Advanced page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTADVANCED); - propSheetPage.pfnDlgProc = PhpOptionsAdvancedDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - if (!PhStartupParameters.ShowOptions) - { - // Symbols page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTSYMBOLS); - propSheetPage.pfnDlgProc = PhpOptionsSymbolsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - } - - if (!PhStartupParameters.ShowOptions) - { - // Highlighting page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING); - propSheetPage.pfnDlgProc = PhpOptionsHighlightingDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - } - - if (!PhStartupParameters.ShowOptions) - { - // Graphs page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OPTGRAPHS); - propSheetPage.pfnDlgProc = PhpOptionsGraphsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - } - - PageInit = FALSE; - PressedOk = FALSE; - RestartRequired = FALSE; - - if (PhStartupParameters.ShowOptions) - StartLocation = PhStartupParameters.Point; - else - StartLocation.x = MINLONG; - - OldTaskMgrDebugger = NULL; - - PhModalPropertySheet(&propSheetHeader); - - if (PressedOk) - { - if (!PhStartupParameters.ShowOptions) - { - PhUpdateCachedSettings(); - ProcessHacker_SaveAllSettings(PhMainWndHandle); - PhInvalidateAllProcessNodes(); - PhReloadSettingsProcessTreeList(); - PhSiNotifyChangeSettings(); - - if (RestartRequired) - { - if (PhShowMessage( - PhMainWndHandle, - MB_ICONQUESTION | MB_YESNO, - L"One or more options you have changed requires a restart of Process Hacker. " - L"Do you want to restart Process Hacker now?" - ) == IDYES) - { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - PhShellProcessHacker( - PhMainWndHandle, - L"-v", - SW_SHOW, - 0, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - ); - ProcessHacker_Destroy(PhMainWndHandle); - } - } - } - else - { - // Main window not available. - if (PhSettingsFileName) - PhSaveSettings(PhSettingsFileName->Buffer); - } - } -} - -INT CALLBACK PhpOptionsPropSheetProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case PSCB_BUTTONPRESSED: - { - if (lParam == PSBTN_OK) - { - PressedOk = TRUE; - } - } - break; - } - - return 0; -} - -static VOID PhpPageInit( - _In_ HWND hwndDlg - ) -{ - if (!PageInit) - { - HWND optionsWindow; - HWND resetButton; - RECT clientRect; - RECT rect; - - optionsWindow = GetParent(hwndDlg); - SetWindowSubclass(optionsWindow, PhpOptionsWndProc, 0, 0); - - // Create the Reset button. - GetClientRect(optionsWindow, &clientRect); - GetWindowRect(GetDlgItem(optionsWindow, IDCANCEL), &rect); - MapWindowPoints(NULL, optionsWindow, (POINT *)&rect, 2); - resetButton = CreateWindowEx( - WS_EX_NOPARENTNOTIFY, - L"BUTTON", - L"Reset", - WS_CHILD | WS_VISIBLE | WS_TABSTOP, - clientRect.right - rect.right, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - optionsWindow, - (HMENU)IDC_RESET, - PhInstanceHandle, - NULL - ); - SendMessage(resetButton, WM_SETFONT, SendMessage(GetDlgItem(optionsWindow, IDCANCEL), WM_GETFONT, 0, 0), TRUE); - - if (PhStartupParameters.ShowOptions) - ShowWindow(resetButton, SW_HIDE); - - // Set the location of the options window. - if (StartLocation.x == MINLONG) - { - PhCenterWindow(optionsWindow, GetParent(optionsWindow)); - } - else - { - SetWindowPos(optionsWindow, NULL, StartLocation.x, StartLocation.y, 0, 0, - SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER); - } - - PageInit = TRUE; - } -} - -LRESULT CALLBACK PhpOptionsWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - switch (uMsg) - { - case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhpOptionsWndProc, uIdSubclass); - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_RESET: - { - if (PhShowMessage( - hwnd, - MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2, - L"Do you want to reset all settings and restart Process Hacker?" - ) == IDYES) - { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - - PhResetSettings(); - - if (PhSettingsFileName) - PhSaveSettings(PhSettingsFileName->Buffer); - - PhShellProcessHacker( - PhMainWndHandle, - L"-v", - SW_SHOW, - 0, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - ); - ProcessHacker_Destroy(PhMainWndHandle); - } - } - break; - } - } - break; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -#define SetDlgItemCheckForSetting(hwndDlg, Id, Name) \ - Button_SetCheck(GetDlgItem(hwndDlg, Id), PhGetIntegerSetting(Name) ? BST_CHECKED : BST_UNCHECKED) -#define SetSettingForDlgItemCheck(hwndDlg, Id, Name) \ - PhSetIntegerSetting(Name, Button_GetCheck(GetDlgItem(hwndDlg, Id)) == BST_CHECKED) -#define SetSettingForDlgItemCheckRestartRequired(hwndDlg, Id, Name) \ - do { \ - BOOLEAN __oldValue = !!PhGetIntegerSetting(Name); \ - BOOLEAN __newValue = Button_GetCheck(GetDlgItem(hwndDlg, Id)) == BST_CHECKED; \ - if (__newValue != __oldValue) \ - RestartRequired = TRUE; \ - PhSetIntegerSetting(Name, __newValue); \ - } while (0) -#define DialogChanged PropSheet_Changed(GetParent(hwndDlg), hwndDlg) - -static BOOLEAN GetCurrentFont( - _Out_ PLOGFONT Font - ) -{ - BOOLEAN result; - PPH_STRING fontHexString; - - if (NewFontSelection) - fontHexString = NewFontSelection; - else - fontHexString = PhaGetStringSetting(L"Font"); - - if (fontHexString->Length / 2 / 2 == sizeof(LOGFONT)) - result = PhHexStringToBuffer(&fontHexString->sr, (PUCHAR)Font); - else - result = FALSE; - - return result; -} - -static VOID ReadCurrentUserRun( - VOID - ) -{ - HANDLE keyHandle; - PPH_STRING value; - - CurrentUserRunPresent = FALSE; - CurrentUserRunStartHidden = FALSE; - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_CURRENT_USER, - &CurrentUserRunKeyName, - 0 - ))) - { - if (value = PhQueryRegistryString(keyHandle, L"Process Hacker")) - { - PH_STRINGREF fileName; - PH_STRINGREF arguments; - PPH_STRING fullFileName; - - PH_AUTO(value); - - if (PhParseCommandLineFuzzy(&value->sr, &fileName, &arguments, &fullFileName)) - { - PH_AUTO(fullFileName); - - if (fullFileName && PhEqualString(fullFileName, PhApplicationFileName, TRUE)) - { - CurrentUserRunPresent = TRUE; - CurrentUserRunStartHidden = PhEqualStringRef2(&arguments, L"-hide", FALSE); - } - } - } - - NtClose(keyHandle); - } -} - -static VOID WriteCurrentUserRun( - _In_ BOOLEAN Present, - _In_ BOOLEAN StartHidden - ) -{ - HANDLE keyHandle; - - if (CurrentUserRunPresent == Present && (!Present || CurrentUserRunStartHidden == StartHidden)) - return; - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_WRITE, - PH_KEY_CURRENT_USER, - &CurrentUserRunKeyName, - 0 - ))) - { - UNICODE_STRING valueName; - - RtlInitUnicodeString(&valueName, L"Process Hacker"); - - if (Present) - { - PPH_STRING value; - - value = PH_AUTO(PhConcatStrings(3, L"\"", PhApplicationFileName->Buffer, L"\"")); - - if (StartHidden) - value = PhaConcatStrings2(value->Buffer, L" -hide"); - - NtSetValueKey(keyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); - } - else - { - NtDeleteValueKey(keyHandle, &valueName); - } - - NtClose(keyHandle); - } -} - - -INT_PTR CALLBACK PhpOptionsGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND comboBoxHandle; - ULONG i; - LOGFONT font; - - PhpPageInit(hwndDlg); - - comboBoxHandle = GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT); - - for (i = 0; i < sizeof(PhSizeUnitNames) / sizeof(PWSTR); i++) - ComboBox_AddString(comboBoxHandle, PhSizeUnitNames[i]); - - SetDlgItemText(hwndDlg, IDC_SEARCHENGINE, PhaGetStringSetting(L"SearchEngine")->Buffer); - SetDlgItemText(hwndDlg, IDC_PEVIEWER, PhaGetStringSetting(L"ProgramInspectExecutables")->Buffer); - - if (PhMaxSizeUnit != -1) - ComboBox_SetCurSel(comboBoxHandle, PhMaxSizeUnit); - else - ComboBox_SetCurSel(comboBoxHandle, sizeof(PhSizeUnitNames) / sizeof(PWSTR) - 1); - - SetDlgItemInt(hwndDlg, IDC_ICONPROCESSES, PhGetIntegerSetting(L"IconProcesses"), FALSE); - - SetDlgItemCheckForSetting(hwndDlg, IDC_ALLOWONLYONEINSTANCE, L"AllowOnlyOneInstance"); - SetDlgItemCheckForSetting(hwndDlg, IDC_HIDEONCLOSE, L"HideOnClose"); - SetDlgItemCheckForSetting(hwndDlg, IDC_HIDEONMINIMIZE, L"HideOnMinimize"); - SetDlgItemCheckForSetting(hwndDlg, IDC_COLLAPSESERVICES, L"CollapseServicesOnStart"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ICONSINGLECLICK, L"IconSingleClick"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ICONTOGGLESVISIBILITY, L"IconTogglesVisibility"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLEPLUGINS, L"EnablePlugins"); - - ReadCurrentUserRun(); - - if (CurrentUserRunPresent) - { - Button_SetCheck(GetDlgItem(hwndDlg, IDC_STARTATLOGON), BST_CHECKED); - - if (CurrentUserRunStartHidden) - Button_SetCheck(GetDlgItem(hwndDlg, IDC_STARTHIDDEN), BST_CHECKED); - } - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_STARTHIDDEN), FALSE); - } - - // Set the font of the button for a nice preview. - if (GetCurrentFont(&font)) - { - CurrentFontInstance = CreateFontIndirect(&font); - - if (CurrentFontInstance) - SendMessage(GetDlgItem(hwndDlg, IDC_FONT), WM_SETFONT, (WPARAM)CurrentFontInstance, TRUE); - } - } - break; - case WM_DESTROY: - { - if (CurrentFontInstance) - DeleteObject(CurrentFontInstance); - - PhClearReference(&NewFontSelection); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_STARTATLOGON: - { - EnableWindow(GetDlgItem(hwndDlg, IDC_STARTHIDDEN), Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTATLOGON)) == BST_CHECKED); - } - break; - case IDC_FONT: - { - LOGFONT font; - CHOOSEFONT chooseFont; - - if (!GetCurrentFont(&font)) - { - // Can't get LOGFONT from the existing setting, probably - // because the user hasn't ever chosen a font before. - // Set the font to something familiar. - GetObject((HFONT)SendMessage(PhMainWndHandle, WM_PH_GET_FONT, 0, 0), sizeof(LOGFONT), &font); - } - - memset(&chooseFont, 0, sizeof(CHOOSEFONT)); - chooseFont.lStructSize = sizeof(CHOOSEFONT); - chooseFont.hwndOwner = hwndDlg; - chooseFont.lpLogFont = &font; - chooseFont.Flags = CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; - - if (ChooseFont(&chooseFont)) - { - PhMoveReference(&NewFontSelection, PhBufferToHexString((PUCHAR)&font, sizeof(LOGFONT))); - - // Update the button's font. - - if (CurrentFontInstance) - DeleteObject(CurrentFontInstance); - - CurrentFontInstance = CreateFontIndirect(&font); - SendMessage(GetDlgItem(hwndDlg, IDC_FONT), WM_SETFONT, (WPARAM)CurrentFontInstance, TRUE); - } - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - BOOLEAN startAtLogon; - BOOLEAN startHidden; - - PhSetStringSetting2(L"SearchEngine", &(PhaGetDlgItemText(hwndDlg, IDC_SEARCHENGINE)->sr)); - PhSetStringSetting2(L"ProgramInspectExecutables", &(PhaGetDlgItemText(hwndDlg, IDC_PEVIEWER)->sr)); - PhSetIntegerSetting(L"MaxSizeUnit", PhMaxSizeUnit = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT))); - PhSetIntegerSetting(L"IconProcesses", GetDlgItemInt(hwndDlg, IDC_ICONPROCESSES, NULL, FALSE)); - SetSettingForDlgItemCheck(hwndDlg, IDC_ALLOWONLYONEINSTANCE, L"AllowOnlyOneInstance"); - SetSettingForDlgItemCheck(hwndDlg, IDC_HIDEONCLOSE, L"HideOnClose"); - SetSettingForDlgItemCheck(hwndDlg, IDC_HIDEONMINIMIZE, L"HideOnMinimize"); - SetSettingForDlgItemCheck(hwndDlg, IDC_COLLAPSESERVICES, L"CollapseServicesOnStart"); - SetSettingForDlgItemCheck(hwndDlg, IDC_ICONSINGLECLICK, L"IconSingleClick"); - SetSettingForDlgItemCheck(hwndDlg, IDC_ICONTOGGLESVISIBILITY, L"IconTogglesVisibility"); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLEPLUGINS, L"EnablePlugins"); - - startAtLogon = Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTATLOGON)) == BST_CHECKED; - startHidden = Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTHIDDEN)) == BST_CHECKED; - WriteCurrentUserRun(startAtLogon, startHidden); - - if (NewFontSelection) - { - PhSetStringSetting2(L"Font", &NewFontSelection->sr); - PostMessage(PhMainWndHandle, WM_PH_UPDATE_FONT, 0, 0); - } - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -static BOOLEAN PathMatchesPh( - _In_ PPH_STRING Path - ) -{ - BOOLEAN match = FALSE; - - if (PhEqualString(OldTaskMgrDebugger, PhApplicationFileName, TRUE)) - { - match = TRUE; - } - // Allow for a quoted value. - else if ( - OldTaskMgrDebugger->Length == PhApplicationFileName->Length + sizeof(WCHAR) * 2 && - OldTaskMgrDebugger->Buffer[0] == '"' && - OldTaskMgrDebugger->Buffer[OldTaskMgrDebugger->Length / sizeof(WCHAR) - 1] == '"' - ) - { - PH_STRINGREF partInside; - - partInside.Buffer = &OldTaskMgrDebugger->Buffer[1]; - partInside.Length = OldTaskMgrDebugger->Length - sizeof(WCHAR) * 2; - - if (PhEqualStringRef(&partInside, &PhApplicationFileName->sr, TRUE)) - match = TRUE; - } - - return match; -} - -VOID PhpAdvancedPageLoad( - _In_ HWND hwndDlg - ) -{ - HWND changeButton; - - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLEWARNINGS, L"EnableWarnings"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLEKERNELMODEDRIVER, L"EnableKph"); - SetDlgItemCheckForSetting(hwndDlg, IDC_HIDEUNNAMEDHANDLES, L"HideUnnamedHandles"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLESTAGE2, L"EnableStage2"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLENETWORKRESOLVE, L"EnableNetworkResolve"); - SetDlgItemCheckForSetting(hwndDlg, IDC_PROPAGATECPUUSAGE, L"PropagateCpuUsage"); - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLEINSTANTTOOLTIPS, L"EnableInstantTooltips"); - - if (WindowsVersion >= WINDOWS_7) - SetDlgItemCheckForSetting(hwndDlg, IDC_ENABLECYCLECPUUSAGE, L"EnableCycleCpuUsage"); - - SetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, PhGetIntegerSetting(L"SampleCount"), FALSE); - SetDlgItemCheckForSetting(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); - - if (PhGetIntegerSetting(L"SampleCountAutomatic")) - EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), FALSE); - - // Replace Task Manager - - changeButton = GetDlgItem(hwndDlg, IDC_CHANGE); - - if (PhGetOwnTokenAttributes().Elevated) - { - ShowWindow(changeButton, SW_HIDE); - } - else - { - SendMessage(changeButton, BCM_SETSHIELD, 0, TRUE); - } - - { - HANDLE taskmgrKeyHandle = NULL; - ULONG disposition; - BOOLEAN success = FALSE; - BOOLEAN alreadyReplaced = FALSE; - - // See if we can write to the key. - if (NT_SUCCESS(PhCreateKey( - &taskmgrKeyHandle, - KEY_READ | KEY_WRITE, - PH_KEY_LOCAL_MACHINE, - &TaskMgrImageOptionsKeyName, - 0, - 0, - &disposition - ))) - { - success = TRUE; - } - - if (taskmgrKeyHandle || NT_SUCCESS(PhOpenKey( - &taskmgrKeyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &TaskMgrImageOptionsKeyName, - 0 - ))) - { - PhClearReference(&OldTaskMgrDebugger); - - if (OldTaskMgrDebugger = PhQueryRegistryString(taskmgrKeyHandle, L"Debugger")) - { - alreadyReplaced = PathMatchesPh(OldTaskMgrDebugger); - } - - NtClose(taskmgrKeyHandle); - } - - if (!success) - EnableWindow(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER), FALSE); - - OldReplaceTaskMgr = alreadyReplaced; - Button_SetCheck(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER), alreadyReplaced ? BST_CHECKED : BST_UNCHECKED); - } -} - -VOID PhpAdvancedPageSave( - _In_ HWND hwndDlg - ) -{ - ULONG sampleCount; - - SetSettingForDlgItemCheck(hwndDlg, IDC_ENABLEWARNINGS, L"EnableWarnings"); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLEKERNELMODEDRIVER, L"EnableKph"); - SetSettingForDlgItemCheck(hwndDlg, IDC_HIDEUNNAMEDHANDLES, L"HideUnnamedHandles"); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLESTAGE2, L"EnableStage2"); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLENETWORKRESOLVE, L"EnableNetworkResolve"); - SetSettingForDlgItemCheck(hwndDlg, IDC_PROPAGATECPUUSAGE, L"PropagateCpuUsage"); - SetSettingForDlgItemCheck(hwndDlg, IDC_ENABLEINSTANTTOOLTIPS, L"EnableInstantTooltips"); - - if (WindowsVersion >= WINDOWS_7) - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_ENABLECYCLECPUUSAGE, L"EnableCycleCpuUsage"); - - sampleCount = GetDlgItemInt(hwndDlg, IDC_SAMPLECOUNT, NULL, FALSE); - SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); - - if (sampleCount == 0) - sampleCount = 1; - - if (sampleCount != PhGetIntegerSetting(L"SampleCount")) - RestartRequired = TRUE; - - PhSetIntegerSetting(L"SampleCount", sampleCount); - - // Replace Task Manager - if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER))) - { - NTSTATUS status; - HANDLE taskmgrKeyHandle; - BOOLEAN replaceTaskMgr; - UNICODE_STRING valueName; - - replaceTaskMgr = Button_GetCheck(GetDlgItem(hwndDlg, IDC_REPLACETASKMANAGER)) == BST_CHECKED; - - if (OldReplaceTaskMgr != replaceTaskMgr) - { - // We should have created the key back in PhpAdvancedPageLoad, which is why - // we're opening the key here. - if (NT_SUCCESS(PhOpenKey( - &taskmgrKeyHandle, - KEY_WRITE, - PH_KEY_LOCAL_MACHINE, - &TaskMgrImageOptionsKeyName, - 0 - ))) - { - RtlInitUnicodeString(&valueName, L"Debugger"); - - if (replaceTaskMgr) - { - PPH_STRING quotedFileName; - - quotedFileName = PH_AUTO(PhConcatStrings(3, L"\"", PhApplicationFileName->Buffer, L"\"")); - status = NtSetValueKey(taskmgrKeyHandle, &valueName, 0, REG_SZ, quotedFileName->Buffer, (ULONG)quotedFileName->Length + 2); - } - else - { - status = NtDeleteValueKey(taskmgrKeyHandle, &valueName); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to replace Task Manager", status, 0); - - NtClose(taskmgrKeyHandle); - } - } - } -} - -NTSTATUS PhpElevateAdvancedThreadStart( - _In_ PVOID Parameter - ) -{ - PPH_STRING arguments; - - arguments = Parameter; - PhShellProcessHacker( - WindowHandleForElevate, - arguments->Buffer, - SW_SHOW, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS, - INFINITE, - NULL - ); - PhDereferenceObject(arguments); - - PostMessage(WindowHandleForElevate, WM_PH_CHILD_EXIT, 0, 0); - - return STATUS_SUCCESS; -} - -INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpPageInit(hwndDlg); - PhpAdvancedPageLoad(hwndDlg); - - if (PhStartupParameters.ShowOptions) - { - // Disable all controls except for Replace Task Manager. - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEWARNINGS), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEKERNELMODEDRIVER), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_HIDEUNNAMEDHANDLES), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLESTAGE2), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLENETWORKRESOLVE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROPAGATECPUUSAGE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEINSTANTTOOLTIPS), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLECYCLECPUUSAGE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNTLABEL), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC), FALSE); - } - else - { - if (WindowsVersion < WINDOWS_7) - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLECYCLECPUUSAGE), FALSE); // cycle-based CPU usage not available before Windows 7 - } - } - break; - case WM_DESTROY: - { - PhClearReference(&OldTaskMgrDebugger); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_CHANGE: - { - HANDLE threadHandle; - RECT windowRect; - - // Save the options so they don't get "overwritten" when - // WM_PH_CHILD_EXIT gets sent. - PhpAdvancedPageSave(hwndDlg); - - GetWindowRect(GetParent(hwndDlg), &windowRect); - WindowHandleForElevate = hwndDlg; - threadHandle = PhCreateThread(0, PhpElevateAdvancedThreadStart, PhFormatString( - L"-showoptions -hwnd %Ix -point %u,%u", - (ULONG_PTR)GetParent(hwndDlg), - windowRect.left + 20, - windowRect.top + 20 - )); - - if (threadHandle) - NtClose(threadHandle); - } - break; - case IDC_SAMPLECOUNTAUTOMATIC: - { - EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC)) != BST_CHECKED); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - PhpAdvancedPageSave(hwndDlg); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - case WM_PH_CHILD_EXIT: - { - PhpAdvancedPageLoad(hwndDlg); - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK PhpOptionsSymbolsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpPageInit(hwndDlg); - - SetDlgItemText(hwndDlg, IDC_DBGHELPPATH, PhaGetStringSetting(L"DbgHelpPath")->Buffer); - SetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH, PhaGetStringSetting(L"DbgHelpSearchPath")->Buffer); - - SetDlgItemCheckForSetting(hwndDlg, IDC_UNDECORATESYMBOLS, L"DbgHelpUndecorate"); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"dbghelp.dll", L"dbghelp.dll" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPPATH))); - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - SetDlgItemText(hwndDlg, IDC_DBGHELPPATH, fileName->Buffer); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - PPH_STRING dbgHelpPath = PhaGetDlgItemText(hwndDlg, IDC_DBGHELPPATH); - - if (!PhEqualString(dbgHelpPath, PhaGetStringSetting(L"DbgHelpPath"), TRUE)) - RestartRequired = TRUE; - - PhSetStringSetting2(L"DbgHelpPath", &dbgHelpPath->sr); - PhSetStringSetting2(L"DbgHelpSearchPath", &(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH)->sr)); - SetSettingForDlgItemCheck(hwndDlg, IDC_UNDECORATESYMBOLS, L"DbgHelpUndecorate"); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -#define CROSS_INDEX 0 -#define TICK_INDEX 1 - -typedef struct _COLOR_ITEM -{ - PWSTR SettingName; - PWSTR UseSettingName; - PWSTR Name; - PWSTR Description; - - BOOLEAN CurrentUse; - COLORREF CurrentColor; -} COLOR_ITEM, *PCOLOR_ITEM; - -#define COLOR_ITEM(SettingName, Name, Description) { SettingName, L"Use" SettingName, Name, Description } - -static COLOR_ITEM ColorItems[] = -{ - COLOR_ITEM(L"ColorOwnProcesses", L"Own processes", L"Processes running under the same user account as Process Hacker."), - COLOR_ITEM(L"ColorSystemProcesses", L"System processes", L"Processes running under the NT AUTHORITY\\SYSTEM user account."), - COLOR_ITEM(L"ColorServiceProcesses", L"Service processes", L"Processes which host one or more services."), - COLOR_ITEM(L"ColorJobProcesses", L"Job processes", L"Processes associated with a job."), -#ifdef _WIN64 - COLOR_ITEM(L"ColorWow64Processes", L"32-bit processes", L"Processes running under WOW64, i.e. 32-bit."), -#endif - COLOR_ITEM(L"ColorDebuggedProcesses", L"Debugged processes", L"Processes that are currently being debugged."), - COLOR_ITEM(L"ColorElevatedProcesses", L"Elevated processes", L"Processes with full privileges on a system with UAC enabled."), - COLOR_ITEM(L"ColorPicoProcesses", L"Pico processes", L"Processes that belong to the Windows subsystem for Linux."), - COLOR_ITEM(L"ColorImmersiveProcesses", L"Immersive processes and DLLs", L"Processes and DLLs that belong to a Modern UI app."), - COLOR_ITEM(L"ColorSuspended", L"Suspended processes and threads", L"Processes and threads that are suspended from execution."), - COLOR_ITEM(L"ColorDotNet", L".NET processes and DLLs", L".NET (i.e. managed) processes and DLLs."), - COLOR_ITEM(L"ColorPacked", L"Packed processes", L"Executables are sometimes \"packed\" to reduce their size."), - COLOR_ITEM(L"ColorGuiThreads", L"GUI threads", L"Threads that have made at least one GUI-related system call."), - COLOR_ITEM(L"ColorRelocatedModules", L"Relocated DLLs", L"DLLs that were not loaded at their preferred image bases."), - COLOR_ITEM(L"ColorProtectedHandles", L"Protected handles", L"Handles that are protected from being closed."), - COLOR_ITEM(L"ColorInheritHandles", L"Inheritable handles", L"Handles that can be inherited by child processes.") -}; - -COLORREF NTAPI PhpColorItemColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ) -{ - PCOLOR_ITEM item = Param; - - return item->CurrentColor; -} - -INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG i; - - PhpPageInit(hwndDlg); - - // Highlighting Duration - SetDlgItemInt(hwndDlg, IDC_HIGHLIGHTINGDURATION, PhCsHighlightingDuration, FALSE); - - // New Objects - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_NEWOBJECTS), PhCsColorNew); - - // Removed Objects - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_REMOVEDOBJECTS), PhCsColorRemoved); - - // Highlighting - HighlightingListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(HighlightingListViewHandle, FALSE, TRUE); - ListView_SetExtendedListViewStyleEx(HighlightingListViewHandle, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); - PhAddListViewColumn(HighlightingListViewHandle, 0, 0, 0, LVCFMT_LEFT, 240, L"Name"); - PhSetExtendedListView(HighlightingListViewHandle); - ExtendedListView_SetItemColorFunction(HighlightingListViewHandle, PhpColorItemColorFunction); - - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) - { - INT lvItemIndex; - - lvItemIndex = PhAddListViewItem(HighlightingListViewHandle, MAXINT, ColorItems[i].Name, &ColorItems[i]); - ColorItems[i].CurrentColor = PhGetIntegerSetting(ColorItems[i].SettingName); - ColorItems[i].CurrentUse = !!PhGetIntegerSetting(ColorItems[i].UseSettingName); - ListView_SetCheckState(HighlightingListViewHandle, lvItemIndex, ColorItems[i].CurrentUse); - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_ENABLEALL: - { - ULONG i; - - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) - ListView_SetCheckState(HighlightingListViewHandle, i, TRUE); - } - break; - case IDC_DISABLEALL: - { - ULONG i; - - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) - ListView_SetCheckState(HighlightingListViewHandle, i, FALSE); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - ULONG i; - - PH_SET_INTEGER_CACHED_SETTING(HighlightingDuration, GetDlgItemInt(hwndDlg, IDC_HIGHLIGHTINGDURATION, NULL, FALSE)); - PH_SET_INTEGER_CACHED_SETTING(ColorNew, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_NEWOBJECTS))); - PH_SET_INTEGER_CACHED_SETTING(ColorRemoved, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_REMOVEDOBJECTS))); - - for (i = 0; i < sizeof(ColorItems) / sizeof(COLOR_ITEM); i++) - { - ColorItems[i].CurrentUse = !!ListView_GetCheckState(HighlightingListViewHandle, i); - PhSetIntegerSetting(ColorItems[i].SettingName, ColorItems[i].CurrentColor); - PhSetIntegerSetting(ColorItems[i].UseSettingName, ColorItems[i].CurrentUse); - } - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - case NM_DBLCLK: - { - if (header->hwndFrom == HighlightingListViewHandle) - { - PCOLOR_ITEM item; - - if (item = PhGetSelectedListViewItemParam(HighlightingListViewHandle)) - { - CHOOSECOLOR chooseColor = { sizeof(chooseColor) }; - COLORREF customColors[16] = { 0 }; - - chooseColor.hwndOwner = hwndDlg; - chooseColor.rgbResult = item->CurrentColor; - chooseColor.lpCustColors = customColors; - chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; - - if (ChooseColor(&chooseColor)) - { - item->CurrentColor = chooseColor.rgbResult; - InvalidateRect(HighlightingListViewHandle, NULL, TRUE); - } - } - } - } - break; - case LVN_GETINFOTIP: - { - if (header->hwndFrom == HighlightingListViewHandle) - { - NMLVGETINFOTIP *getInfoTip = (NMLVGETINFOTIP *)lParam; - PH_STRINGREF tip; - - PhInitializeStringRefLongHint(&tip, ColorItems[getInfoTip->iItem].Description); - PhCopyListViewInfoTip(getInfoTip, &tip); - } - } - break; - } - } - break; - } - - REFLECT_MESSAGE_DLG(hwndDlg, HighlightingListViewHandle, uMsg, wParam, lParam); - - return FALSE; -} - -INT_PTR CALLBACK PhpOptionsGraphsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpPageInit(hwndDlg); - - // Show Text - SetDlgItemCheckForSetting(hwndDlg, IDC_SHOWTEXT, L"GraphShowText"); - SetDlgItemCheckForSetting(hwndDlg, IDC_USEOLDCOLORS, L"GraphColorMode"); - SetDlgItemCheckForSetting(hwndDlg, IDC_SHOWCOMMITINSUMMARY, L"ShowCommitInSummary"); - - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_CPUUSER), PhCsColorCpuUser); - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_CPUKERNEL), PhCsColorCpuKernel); - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_IORO), PhCsColorIoReadOther); - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_IOW), PhCsColorIoWrite); - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_PRIVATE), PhCsColorPrivate); - ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_PHYSICAL), PhCsColorPhysical); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - SetSettingForDlgItemCheck(hwndDlg, IDC_SHOWTEXT, L"GraphShowText"); - SetSettingForDlgItemCheck(hwndDlg, IDC_USEOLDCOLORS, L"GraphColorMode"); - SetSettingForDlgItemCheck(hwndDlg, IDC_SHOWCOMMITINSUMMARY, L"ShowCommitInSummary"); - PH_SET_INTEGER_CACHED_SETTING(ColorCpuUser, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_CPUUSER))); - PH_SET_INTEGER_CACHED_SETTING(ColorCpuKernel, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_CPUKERNEL))); - PH_SET_INTEGER_CACHED_SETTING(ColorIoReadOther, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_IORO))); - PH_SET_INTEGER_CACHED_SETTING(ColorIoWrite, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_IOW))); - PH_SET_INTEGER_CACHED_SETTING(ColorPrivate, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_PRIVATE))); - PH_SET_INTEGER_CACHED_SETTING(ColorPhysical, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_PHYSICAL))); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * options window + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define WM_PH_CHILD_EXIT (WM_APP + 301) + +INT_PTR CALLBACK PhpOptionsGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpOptionsGraphsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhOptionsDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhOptionsDestroySection( + _In_ PPH_OPTIONS_SECTION Section + ); + +VOID PhOptionsEnterSectionView( + _In_ PPH_OPTIONS_SECTION NewSection + ); + +VOID PhOptionsLayoutSectionView( + VOID + ); + +VOID PhOptionsEnterSectionViewInner( + _In_ PPH_OPTIONS_SECTION Section, + _Inout_ HDWP *ContainerDeferHandle + ); + +VOID PhOptionsCreateSectionDialog( + _In_ PPH_OPTIONS_SECTION Section + ); + +PPH_OPTIONS_SECTION PhOptionsFindSection( + _In_ PPH_STRINGREF Name + ); + +VOID PhOptionsOnSize( + VOID + ); + +PPH_OPTIONS_SECTION PhOptionsCreateSection( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_opt_ PVOID Parameter + ); + +PPH_OPTIONS_SECTION PhOptionsCreateSectionAdvanced( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_opt_ PVOID Parameter + ); + +BOOLEAN PhpIsDefaultTaskManager( + VOID + ); + +VOID PhpSetDefaultTaskManager( + _In_ HWND ParentWindowHandle + ); + +static HWND PhOptionsWindowHandle = NULL; +static PH_LAYOUT_MANAGER WindowLayoutManager; + +static PPH_LIST SectionList = NULL; +static PPH_OPTIONS_SECTION CurrentSection = NULL; +static HWND OptionsTreeControl = NULL; +static HWND ContainerControl = NULL; +static HIMAGELIST OptionsTreeImageList = NULL; + +// All +static BOOLEAN RestartRequired = FALSE; + +// General +static PH_STRINGREF CurrentUserRunKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"); +static BOOLEAN CurrentUserRunPresent = FALSE; +static HFONT CurrentFontInstance = NULL; +static PPH_STRING NewFontSelection = NULL; +static HIMAGELIST GeneralListviewImageList = NULL; + +// Advanced +static PH_STRINGREF TaskMgrImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\taskmgr.exe"); +static PPH_STRING OldTaskMgrDebugger = NULL; +static HWND WindowHandleForElevate = NULL; + +// Highlighting +static HWND HighlightingListViewHandle = NULL; + +VOID PhShowOptionsDialog( + _In_ HWND ParentWindowHandle + ) +{ + if (PhStartupParameters.ShowOptions) + { + PhpSetDefaultTaskManager(ParentWindowHandle); + } + else + { + if (!PhOptionsWindowHandle) + { + PhOptionsWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_OPTIONS), + NULL, + PhOptionsDialogProc + ); + + PhRegisterDialog(PhOptionsWindowHandle); + ShowWindow(PhOptionsWindowHandle, SW_SHOW); + } + + if (IsMinimized(PhOptionsWindowHandle)) + ShowWindow(PhOptionsWindowHandle, SW_RESTORE); + else + SetForegroundWindow(PhOptionsWindowHandle); + } +} + +static HTREEITEM PhpTreeViewInsertItem( + _In_opt_ HTREEITEM HandleInsertAfter, + _In_ PWSTR Text, + _In_ PVOID Context + ) +{ + TV_INSERTSTRUCT insert; + + memset(&insert, 0, sizeof(TV_INSERTSTRUCT)); + + insert.hParent = TVI_ROOT; + insert.hInsertAfter = HandleInsertAfter; + insert.item.mask = TVIF_TEXT | TVIF_PARAM; + insert.item.pszText = Text; + insert.item.lParam = (LPARAM)Context; + + return TreeView_InsertItem(OptionsTreeControl, &insert); +} + +static VOID PhpOptionsShowHideTreeViewItem( + _In_ BOOLEAN Hide + ) +{ + static PH_STRINGREF generalName = PH_STRINGREF_INIT(L"General"); + static PH_STRINGREF advancedName = PH_STRINGREF_INIT(L"Advanced"); + + if (Hide) + { + PPH_OPTIONS_SECTION advancedSection; + + if (advancedSection = PhOptionsFindSection(&advancedName)) + { + if (advancedSection->TreeItemHandle) + { + TreeView_DeleteItem(OptionsTreeControl, advancedSection->TreeItemHandle); + advancedSection->TreeItemHandle = NULL; + } + } + } + else + { + PPH_OPTIONS_SECTION generalSection; + PPH_OPTIONS_SECTION advancedSection; + + generalSection = PhOptionsFindSection(&generalName); + advancedSection = PhOptionsFindSection(&advancedName); + + if (generalSection && advancedSection) + { + advancedSection->TreeItemHandle = PhpTreeViewInsertItem( + generalSection->TreeItemHandle, + advancedName.Buffer, + advancedSection + ); + } + } +} + +static PPH_OPTIONS_SECTION PhpTreeViewGetSelectedSection( + _In_ HTREEITEM SelectedTreeItem + ) +{ + TVITEM item; + + if (!SelectedTreeItem) + return NULL; + + item.mask = TVIF_PARAM | TVIF_HANDLE; + item.hItem = SelectedTreeItem; + + if (!TreeView_GetItem(OptionsTreeControl, &item)) + return NULL; + + return (PPH_OPTIONS_SECTION)item.lParam; +} + +INT_PTR CALLBACK PhOptionsDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + OptionsTreeImageList = ImageList_Create(2, PH_SCALE_DPI(22), ILC_COLOR, 1, 1); + OptionsTreeControl = GetDlgItem(hwndDlg, IDC_SECTIONTREE); + ContainerControl = GetDlgItem(hwndDlg, IDD_CONTAINER); + + PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_SEPARATOR), SS_OWNERDRAW, SS_OWNERDRAW); + + PhSetControlTheme(OptionsTreeControl, L"explorer"); + TreeView_SetExtendedStyle(OptionsTreeControl, TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER); + TreeView_SetImageList(OptionsTreeControl, OptionsTreeImageList, TVSIL_NORMAL); + TreeView_SetBkColor(OptionsTreeControl, GetSysColor(COLOR_3DFACE)); + + PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg); + PhAddLayoutItem(&WindowLayoutManager, OptionsTreeControl, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SEPARATOR), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, ContainerControl, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_RESET), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_CLEANUP), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + //PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_APPLY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + + if (PhEnableThemeSupport) // TODO: fix options dialog theme (dmex) + PhInitializeWindowTheme(hwndDlg, TRUE); + + { + PPH_OPTIONS_SECTION section; + + SectionList = PhCreateList(8); + CurrentSection = NULL; + + section = PhOptionsCreateSection(L"General", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGENERAL), PhpOptionsGeneralDlgProc, NULL); + PhOptionsCreateSectionAdvanced(L"Advanced", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTADVANCED), PhpOptionsAdvancedDlgProc, NULL); + PhOptionsCreateSection(L"Highlighting", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTHIGHLIGHTING), PhpOptionsHighlightingDlgProc, NULL); + PhOptionsCreateSection(L"Graphs", PhInstanceHandle, MAKEINTRESOURCE(IDD_OPTGRAPHS), PhpOptionsGraphsDlgProc, NULL); + + if (PhPluginsEnabled) + { + PH_PLUGIN_OPTIONS_POINTERS pointers; + + pointers.WindowHandle = hwndDlg; + pointers.CreateSection = PhOptionsCreateSection; + pointers.FindSection = PhOptionsFindSection; + pointers.EnterSectionView = PhOptionsEnterSectionView; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), &pointers); + } + + PhOptionsEnterSectionView(section); + PhOptionsOnSize(); + } + + if (PhGetIntegerPairSetting(L"OptionsWindowPosition").X) + PhLoadWindowPlacementFromSetting(L"OptionsWindowPosition", L"OptionsWindowSize", hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + } + break; + case WM_DESTROY: + { + ULONG i; + PPH_OPTIONS_SECTION section; + + PhSaveWindowPlacementToSetting(L"OptionsWindowPosition", L"OptionsWindowSize", hwndDlg); + + PhDeleteLayoutManager(&WindowLayoutManager); + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + PhOptionsDestroySection(section); + } + + PhDereferenceObject(SectionList); + SectionList = NULL; + + if (OptionsTreeImageList) ImageList_Destroy(OptionsTreeImageList); + + PhUnregisterWindowCallback(hwndDlg); + + PhUnregisterDialog(PhOptionsWindowHandle); + PhOptionsWindowHandle = NULL; + } + break; + case WM_SIZE: + { + PhOptionsOnSize(); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + { + DestroyWindow(hwndDlg); + } + break; + case IDC_RESET: + { + if (PhShowMessage2( + hwndDlg, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_WARNING_ICON, + L"Do you want to reset all settings and restart Process Hacker?", + L"" + ) == IDYES) + { + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + + PhResetSettings(); + + if (PhSettingsFileName) + PhSaveSettings(PhSettingsFileName->Buffer); + + PhShellProcessHacker( + PhMainWndHandle, + L"-v", + SW_SHOW, + 0, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, + 0, + NULL + ); + ProcessHacker_Destroy(PhMainWndHandle); + } + } + break; + case IDC_CLEANUP: + { + if (PhShowMessage2( + hwndDlg, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_INFORMATION_ICON, + L"Do you want to clean up unused plugin settings?", + L"" + ) == IDYES) + { + PhClearIgnoredSettings(); + } + } + break; + } + } + break; + case WM_DRAWITEM: + { + PDRAWITEMSTRUCT drawInfo = (PDRAWITEMSTRUCT)lParam; + + if (drawInfo->CtlID == IDC_SEPARATOR) + { + RECT rect; + + rect = drawInfo->rcItem; + rect.right = 2; + + if (PhEnableThemeSupport) + { + switch (PhCsGraphColorMode) + { + case 0: // New colors + { + FillRect(drawInfo->hDC, &rect, GetSysColorBrush(COLOR_3DHIGHLIGHT)); + rect.left += 1; + FillRect(drawInfo->hDC, &rect, GetSysColorBrush(COLOR_3DSHADOW)); + } + break; + case 1: // Old colors + { + SetDCBrushColor(drawInfo->hDC, RGB(0, 0, 0)); + FillRect(drawInfo->hDC, &rect, GetStockBrush(DC_BRUSH)); + } + break; + } + } + else + { + FillRect(drawInfo->hDC, &rect, GetSysColorBrush(COLOR_3DHIGHLIGHT)); + rect.left += 1; + FillRect(drawInfo->hDC, &rect, GetSysColorBrush(COLOR_3DSHADOW)); + } + + return TRUE; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case TVN_SELCHANGED: + { + LPNMTREEVIEW treeview = (LPNMTREEVIEW)lParam; + PPH_OPTIONS_SECTION section; + + if (section = PhpTreeViewGetSelectedSection(treeview->itemNew.hItem)) + { + PhOptionsEnterSectionView(section); + } + } + break; + case NM_SETCURSOR: + { + if (header->hwndFrom == OptionsTreeControl) + { + HCURSOR cursor = (HCURSOR)LoadImage( + NULL, + IDC_ARROW, + IMAGE_CURSOR, + 0, + 0, + LR_SHARED + ); + + if (cursor != GetCursor()) + { + SetCursor(cursor); + } + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + break; + } + } + break; + } + + return FALSE; +} + +VOID PhOptionsOnSize( + VOID + ) +{ + PhLayoutManagerLayout(&WindowLayoutManager); + + if (SectionList && SectionList->Count != 0) + { + PhOptionsLayoutSectionView(); + } +} + +PPH_OPTIONS_SECTION PhOptionsCreateSection( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_opt_ PVOID Parameter + ) +{ + PPH_OPTIONS_SECTION section; + + section = PhAllocateZero(sizeof(PH_OPTIONS_SECTION)); + PhInitializeStringRefLongHint(§ion->Name, Name); + section->Instance = Instance; + section->Template = Template; + section->DialogProc = DialogProc; + section->Parameter = Parameter; + section->TreeItemHandle = PhpTreeViewInsertItem(TVI_LAST, Name, section); + + PhAddItemList(SectionList, section); + + return section; +} + +PPH_OPTIONS_SECTION PhOptionsCreateSectionAdvanced( + _In_ PWSTR Name, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_opt_ PVOID Parameter + ) +{ + PPH_OPTIONS_SECTION section; + + section = PhAllocateZero(sizeof(PH_OPTIONS_SECTION)); + PhInitializeStringRefLongHint(§ion->Name, Name); + section->Instance = Instance; + section->Template = Template; + section->DialogProc = DialogProc; + section->Parameter = Parameter; + + PhAddItemList(SectionList, section); + + return section; +} + +VOID PhOptionsDestroySection( + _In_ PPH_OPTIONS_SECTION Section + ) +{ + PhFree(Section); +} + +PPH_OPTIONS_SECTION PhOptionsFindSection( + _In_ PPH_STRINGREF Name + ) +{ + ULONG i; + PPH_OPTIONS_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (PhEqualStringRef(§ion->Name, Name, TRUE)) + return section; + } + + return NULL; +} + +VOID PhOptionsLayoutSectionView( + VOID + ) +{ + if (CurrentSection && CurrentSection->DialogHandle) + { + RECT clientRect; + + GetClientRect(ContainerControl, &clientRect); + + SetWindowPos( + CurrentSection->DialogHandle, + NULL, + 0, + 0, + clientRect.right - clientRect.left, + clientRect.bottom - clientRect.top, + SWP_NOACTIVATE | SWP_NOZORDER + ); + } +} + +VOID PhOptionsEnterSectionView( + _In_ PPH_OPTIONS_SECTION NewSection + ) +{ + ULONG i; + PPH_OPTIONS_SECTION section; + PPH_OPTIONS_SECTION oldSection; + HDWP containerDeferHandle; + + if (CurrentSection == NewSection) + return; + + oldSection = CurrentSection; + CurrentSection = NewSection; + + containerDeferHandle = BeginDeferWindowPos(SectionList->Count); + + PhOptionsEnterSectionViewInner(NewSection, &containerDeferHandle); + PhOptionsLayoutSectionView(); + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (section != NewSection) + PhOptionsEnterSectionViewInner(section, &containerDeferHandle); + } + + EndDeferWindowPos(containerDeferHandle); + + if (NewSection->DialogHandle) + RedrawWindow(NewSection->DialogHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW); +} + +VOID PhOptionsEnterSectionViewInner( + _In_ PPH_OPTIONS_SECTION Section, + _Inout_ HDWP *ContainerDeferHandle + ) +{ + if (Section == CurrentSection && !Section->DialogHandle) + PhOptionsCreateSectionDialog(Section); + + if (Section->DialogHandle) + { + if (Section == CurrentSection) + *ContainerDeferHandle = DeferWindowPos(*ContainerDeferHandle, Section->DialogHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY | SWP_NOREDRAW); + else + *ContainerDeferHandle = DeferWindowPos(*ContainerDeferHandle, Section->DialogHandle, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW_ONLY | SWP_NOREDRAW); + } +} + +VOID PhOptionsCreateSectionDialog( + _In_ PPH_OPTIONS_SECTION Section + ) +{ + Section->DialogHandle = PhCreateDialogFromTemplate( + ContainerControl, + DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, + Section->Instance, + Section->Template, + Section->DialogProc, + Section->Parameter + ); + + PhInitializeWindowTheme(Section->DialogHandle, PhEnableThemeSupport); +} + +#define SetDlgItemCheckForSetting(hwndDlg, Id, Name) \ + Button_SetCheck(GetDlgItem(hwndDlg, Id), PhGetIntegerSetting(Name) ? BST_CHECKED : BST_UNCHECKED) +#define SetSettingForDlgItemCheck(hwndDlg, Id, Name) \ + PhSetIntegerSetting(Name, Button_GetCheck(GetDlgItem(hwndDlg, Id)) == BST_CHECKED) +#define SetSettingForDlgItemCheckRestartRequired(hwndDlg, Id, Name) \ +{ \ + BOOLEAN __oldValue = !!PhGetIntegerSetting(Name); \ + BOOLEAN __newValue = Button_GetCheck(GetDlgItem(hwndDlg, Id)) == BST_CHECKED; \ + if (__newValue != __oldValue) \ + RestartRequired = TRUE; \ + PhSetIntegerSetting(Name, __newValue); \ +} + +#define SetLvItemCheckForSetting(ListViewHandle, Index, Name) \ + ListView_SetCheckState(ListViewHandle, Index, !!PhGetIntegerSetting(Name)); +#define SetSettingForLvItemCheck(ListViewHandle, Index, Name) \ + PhSetIntegerSetting(Name, ListView_GetCheckState(ListViewHandle, Index) == BST_CHECKED) +#define SetSettingForLvItemCheckRestartRequired(ListViewHandle, Index, Name) \ +{ \ + BOOLEAN __oldValue = !!PhGetIntegerSetting(Name); \ + BOOLEAN __newValue = ListView_GetCheckState(ListViewHandle, Index) == BST_CHECKED; \ + if (__newValue != __oldValue) \ + RestartRequired = TRUE; \ + PhSetIntegerSetting(Name, __newValue); \ +} + +static BOOLEAN GetCurrentFont( + _Out_ PLOGFONT Font + ) +{ + BOOLEAN result; + PPH_STRING fontHexString; + + if (NewFontSelection) + fontHexString = NewFontSelection; + else + fontHexString = PhaGetStringSetting(L"Font"); + + if (fontHexString->Length / sizeof(WCHAR) / 2 == sizeof(LOGFONT)) + result = PhHexStringToBuffer(&fontHexString->sr, (PUCHAR)Font); + else + result = FALSE; + + return result; +} + +static VOID ReadCurrentUserRun( + VOID + ) +{ + HANDLE keyHandle; + PPH_STRING value; + + CurrentUserRunPresent = FALSE; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &CurrentUserRunKeyName, + 0 + ))) + { + if (value = PhQueryRegistryString(keyHandle, L"Process Hacker")) + { + PH_STRINGREF fileName; + PH_STRINGREF arguments; + PPH_STRING fullFileName; + PPH_STRING applicationFileName; + + PH_AUTO(value); + + if (PhParseCommandLineFuzzy(&value->sr, &fileName, &arguments, &fullFileName)) + { + PH_AUTO(fullFileName); + + if (applicationFileName = PhGetApplicationFileName()) + { + if (fullFileName && PhEqualString(fullFileName, applicationFileName, TRUE)) + { + CurrentUserRunPresent = TRUE; + } + + PhDereferenceObject(applicationFileName); + } + } + } + + NtClose(keyHandle); + } +} + +static VOID WriteCurrentUserRun( + _In_ BOOLEAN Present, + _In_ BOOLEAN StartHidden + ) +{ + HANDLE keyHandle; + + if (CurrentUserRunPresent == Present) + return; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE, + PH_KEY_CURRENT_USER, + &CurrentUserRunKeyName, + 0 + ))) + { + UNICODE_STRING valueName; + + RtlInitUnicodeString(&valueName, L"Process Hacker"); + + if (Present) + { + PPH_STRING value; + PPH_STRING fileName; + + if (fileName = PhGetApplicationFileName()) + { + value = PH_AUTO(PhConcatStrings(3, L"\"", PhGetStringOrEmpty(fileName), L"\"")); + + if (StartHidden) + value = PhaConcatStrings2(value->Buffer, L" -hide"); + + NtSetValueKey( + keyHandle, + &valueName, + 0, + REG_SZ, + value->Buffer, + (ULONG)value->Length + sizeof(WCHAR) + ); + + PhDereferenceObject(fileName); + } + } + else + { + NtDeleteValueKey(keyHandle, &valueName); + } + + NtClose(keyHandle); + } +} + +static BOOLEAN PathMatchesPh( + _In_ PPH_STRING Path + ) +{ + BOOLEAN match = FALSE; + PPH_STRING fileName; + + if (!(fileName = PhGetApplicationFileName())) + return FALSE; + + if (PhEqualString(OldTaskMgrDebugger, fileName, TRUE)) + { + match = TRUE; + } + // Allow for a quoted value. + else if ( + OldTaskMgrDebugger->Length == fileName->Length + 4 && + OldTaskMgrDebugger->Buffer[0] == L'"' && + OldTaskMgrDebugger->Buffer[OldTaskMgrDebugger->Length / sizeof(WCHAR) - 1] == L'"' + ) + { + PH_STRINGREF partInside; + + partInside.Buffer = &OldTaskMgrDebugger->Buffer[1]; + partInside.Length = OldTaskMgrDebugger->Length - sizeof(WCHAR) * sizeof(WCHAR); + + if (PhEqualStringRef(&partInside, &fileName->sr, TRUE)) + match = TRUE; + } + + PhDereferenceObject(fileName); + + return match; +} + +BOOLEAN PhpIsDefaultTaskManager( + VOID + ) +{ + HANDLE taskmgrKeyHandle; + BOOLEAN alreadyReplaced = FALSE; + + if (NT_SUCCESS(PhOpenKey( + &taskmgrKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &TaskMgrImageOptionsKeyName, + 0 + ))) + { + PhClearReference(&OldTaskMgrDebugger); + + if (OldTaskMgrDebugger = PhQueryRegistryString(taskmgrKeyHandle, L"Debugger")) + { + alreadyReplaced = PathMatchesPh(OldTaskMgrDebugger); + } + + NtClose(taskmgrKeyHandle); + } + + return alreadyReplaced; +} + +VOID PhpSetDefaultTaskManager( + _In_ HWND ParentWindowHandle + ) +{ + PWSTR message; + + if (PhpIsDefaultTaskManager()) + message = L"Do you want to restore the default Windows Task Manager?"; + else + message = L"Do you want to make Process Hacker the default Windows Task Manager?"; + + if (PhShowMessage2( + ParentWindowHandle, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_INFORMATION_ICON, + L"", + message + ) == IDYES) + { + NTSTATUS status; + HANDLE taskmgrKeyHandle; + UNICODE_STRING valueName; + + status = PhCreateKey( + &taskmgrKeyHandle, + KEY_READ | KEY_WRITE, + PH_KEY_LOCAL_MACHINE, + &TaskMgrImageOptionsKeyName, + OBJ_OPENIF, + 0, + NULL + ); + + if (NT_SUCCESS(status)) + { + RtlInitUnicodeString(&valueName, L"Debugger"); + + if (PhpIsDefaultTaskManager()) + { + status = NtDeleteValueKey(taskmgrKeyHandle, &valueName); + } + else + { + PPH_STRING quotedFileName; + PPH_STRING applicationFileName; + + if (applicationFileName = PhGetApplicationFileName()) + { + quotedFileName = PH_AUTO(PhConcatStrings(3, L"\"", PhGetStringOrEmpty(applicationFileName), L"\"")); + + status = NtSetValueKey( + taskmgrKeyHandle, + &valueName, + 0, + REG_SZ, + quotedFileName->Buffer, + (ULONG)quotedFileName->Length + sizeof(UNICODE_NULL) + ); + + PhDereferenceObject(applicationFileName); + } + } + + NtClose(taskmgrKeyHandle); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(ParentWindowHandle, L"Unable to replace Task Manager", status, 0); + + if (PhSettingsFileName) + PhSaveSettings(PhSettingsFileName->Buffer); + } +} + +VOID PhpRefreshTaskManagerState( + _In_ HWND WindowHandle + ) +{ + if (!PhGetOwnTokenAttributes().Elevated) + { + Button_SetElevationRequiredState(GetDlgItem(WindowHandle, IDC_REPLACETASKMANAGER), TRUE); + } + + if (PhpIsDefaultTaskManager()) + { + Static_SetText(GetDlgItem(WindowHandle, IDC_DEFSTATE), L"Process Hacker is the default Task Manager:"); + Button_SetText(GetDlgItem(WindowHandle, IDC_REPLACETASKMANAGER), L"Restore default..."); + } + else + { + Static_SetText(GetDlgItem(WindowHandle, IDC_DEFSTATE), L"Process Hacker is not the default Task Manager:"); + Button_SetText(GetDlgItem(WindowHandle, IDC_REPLACETASKMANAGER), L"Make default..."); + } +} + +typedef enum _PHP_OPTIONS_INDEX +{ + PHP_OPTIONS_INDEX_SINGLE_INSTANCE, + PHP_OPTIONS_INDEX_HIDE_WHENCLOSED, + PHP_OPTIONS_INDEX_HIDE_WHENMINIMIZED, + PHP_OPTIONS_INDEX_START_ATLOGON, + PHP_OPTIONS_INDEX_START_HIDDEN, + PHP_OPTIONS_INDEX_ENABLE_MINIINFO_WINDOW, + PHP_OPTIONS_INDEX_ENABLE_DRIVER, + PHP_OPTIONS_INDEX_ENABLE_WARNINGS, + PHP_OPTIONS_INDEX_ENABLE_PLUGINS, + PHP_OPTIONS_INDEX_ENABLE_UNDECORATE_SYMBOLS, + PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, + PHP_OPTIONS_INDEX_ENABLE_THEME_SUPPORT, + PHP_OPTIONS_INDEX_ENABLE_LINUX_SUPPORT, + PHP_OPTIONS_INDEX_ENABLE_NETWORK_RESOLVE, + PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, + PHP_OPTIONS_INDEX_ENABLE_STAGE2, + PHP_OPTIONS_INDEX_ENABLE_SERVICE_STAGE2, + PHP_OPTIONS_INDEX_COLLAPSE_SERVICES_ON_START, + PHP_OPTIONS_INDEX_ICON_SINGLE_CLICK, + PHP_OPTIONS_INDEX_ICON_TOGGLE_VISIBILITY, + PHP_OPTIONS_INDEX_PROPAGATE_CPU_USAGE, + PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS +} PHP_OPTIONS_GENERAL_INDEX; + +static VOID PhpAdvancedPageLoad( + _In_ HWND hwndDlg + ) +{ + HWND listViewHandle; + + listViewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); + + PhSetDialogItemValue(hwndDlg, IDC_SAMPLECOUNT, PhGetIntegerSetting(L"SampleCount"), FALSE); + SetDlgItemCheckForSetting(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); + + if (PhGetIntegerSetting(L"SampleCountAutomatic")) + EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), FALSE); + + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_SINGLE_INSTANCE, L"Allow only one instance", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_HIDE_WHENCLOSED, L"Hide when closed", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_HIDE_WHENMINIMIZED, L"Hide when minimized", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_START_ATLOGON, L"Start when I log on", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_START_HIDDEN, L"Start hidden", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_MINIINFO_WINDOW, L"Enable tray information window", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_DRIVER, L"Enable kernel-mode driver", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_WARNINGS, L"Enable warnings", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_PLUGINS, L"Enable plugins", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_UNDECORATE_SYMBOLS, L"Enable undecorated symbols", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, L"Enable cycle-based CPU usage", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_THEME_SUPPORT, L"Enable theme support (experimental)", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_LINUX_SUPPORT, L"Enable Windows subsystem for Linux support", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_NETWORK_RESOLVE, L"Resolve network addresses", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, L"Show tooltips instantly", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_STAGE2, L"Check images for digital signatures", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_SERVICE_STAGE2, L"Check services for digital signatures", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_COLLAPSE_SERVICES_ON_START, L"Collapse services on start", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ICON_SINGLE_CLICK, L"Single-click tray icons", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_ICON_TOGGLE_VISIBILITY, L"Icon click toggles visibility", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_PROPAGATE_CPU_USAGE, L"Include usage of collapsed processes", NULL); + PhAddListViewItem(listViewHandle, PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS, L"Show advanced options (experimental)", NULL); + + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_SINGLE_INSTANCE, L"AllowOnlyOneInstance"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_HIDE_WHENCLOSED, L"HideOnClose"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_HIDE_WHENMINIMIZED, L"HideOnMinimize"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_START_HIDDEN, L"StartHidden"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_MINIINFO_WINDOW, L"MiniInfoWindowEnabled"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_DRIVER, L"EnableKph"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_WARNINGS, L"EnableWarnings"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_PLUGINS, L"EnablePlugins"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_UNDECORATE_SYMBOLS, L"DbgHelpUndecorate"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, L"EnableCycleCpuUsage"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_THEME_SUPPORT, L"EnableThemeSupport"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_LINUX_SUPPORT, L"EnableLinuxSubsystemSupport"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_NETWORK_RESOLVE, L"EnableNetworkResolve"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, L"EnableInstantTooltips"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_STAGE2, L"EnableStage2"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_SERVICE_STAGE2, L"EnableServiceStage2"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_COLLAPSE_SERVICES_ON_START, L"CollapseServicesOnStart"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ICON_SINGLE_CLICK, L"IconSingleClick"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_ICON_TOGGLE_VISIBILITY, L"IconTogglesVisibility"); + SetLvItemCheckForSetting(listViewHandle, PHP_OPTIONS_INDEX_PROPAGATE_CPU_USAGE, L"PropagateCpuUsage"); + + if (CurrentUserRunPresent) + ListView_SetCheckState(listViewHandle, PHP_OPTIONS_INDEX_START_ATLOGON, TRUE); +} + +static VOID PhpOptionsNotifyChangeCallback( + _In_ PVOID Context + ) +{ + PhUpdateCachedSettings(); + ProcessHacker_SaveAllSettings(PhMainWndHandle); + PhInvalidateAllProcessNodes(); + PhReloadSettingsProcessTreeList(); + PhSiNotifyChangeSettings(); + + PhReInitializeWindowTheme(PhMainWndHandle); + + if (RestartRequired) + { + if (PhShowMessage2( + (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_INFORMATION_ICON, + L"One or more options you have changed requires a restart of Process Hacker.", + L"Do you want to restart Process Hacker now?" + ) == IDYES) + { + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + PhShellProcessHacker( + PhMainWndHandle, + L"-v", + SW_SHOW, + 0, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, + 0, + NULL + ); + ProcessHacker_Destroy(PhMainWndHandle); + } + } +} + +static VOID PhpAdvancedPageSave( + _In_ HWND hwndDlg + ) +{ + HWND listViewHandle; + ULONG sampleCount; + + listViewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); + sampleCount = PhGetDialogItemValue(hwndDlg, IDC_SAMPLECOUNT); + + SetSettingForDlgItemCheckRestartRequired(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC, L"SampleCountAutomatic"); + + if (sampleCount == 0) + sampleCount = 1; + + if (sampleCount != PhGetIntegerSetting(L"SampleCount")) + RestartRequired = TRUE; + + PhSetIntegerSetting(L"SampleCount", sampleCount); + PhSetStringSetting2(L"SearchEngine", &PhaGetDlgItemText(hwndDlg, IDC_SEARCHENGINE)->sr); + PhSetStringSetting2(L"ProgramInspectExecutables", &PhaGetDlgItemText(hwndDlg, IDC_PEVIEWER)->sr); + PhSetIntegerSetting(L"MaxSizeUnit", PhMaxSizeUnit = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT))); + PhSetIntegerSetting(L"IconProcesses", PhGetDialogItemValue(hwndDlg, IDC_ICONPROCESSES)); + + if (!PhEqualString(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH), PhaGetStringSetting(L"DbgHelpSearchPath"), TRUE)) + { + PhSetStringSetting2(L"DbgHelpSearchPath", &(PhaGetDlgItemText(hwndDlg, IDC_DBGHELPSEARCHPATH)->sr)); + RestartRequired = TRUE; + } + + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_SINGLE_INSTANCE, L"AllowOnlyOneInstance"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_HIDE_WHENCLOSED, L"HideOnClose"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_HIDE_WHENMINIMIZED, L"HideOnMinimize"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_START_HIDDEN, L"StartHidden"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_MINIINFO_WINDOW, L"MiniInfoWindowEnabled"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_DRIVER, L"EnableKph"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_WARNINGS, L"EnableWarnings"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_PLUGINS, L"EnablePlugins"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_UNDECORATE_SYMBOLS, L"DbgHelpUndecorate"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_CYCLE_CPU_USAGE, L"EnableCycleCpuUsage"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_THEME_SUPPORT, L"EnableThemeSupport"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_LINUX_SUPPORT, L"EnableLinuxSubsystemSupport"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_NETWORK_RESOLVE, L"EnableNetworkResolve"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_INSTANT_TOOLTIPS, L"EnableInstantTooltips"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_STAGE2, L"EnableStage2"); + SetSettingForLvItemCheckRestartRequired(listViewHandle, PHP_OPTIONS_INDEX_ENABLE_SERVICE_STAGE2, L"EnableServiceStage2"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_COLLAPSE_SERVICES_ON_START, L"CollapseServicesOnStart"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_ICON_SINGLE_CLICK, L"IconSingleClick"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_ICON_TOGGLE_VISIBILITY, L"IconTogglesVisibility"); + SetSettingForLvItemCheck(listViewHandle, PHP_OPTIONS_INDEX_PROPAGATE_CPU_USAGE, L"PropagateCpuUsage"); + + WriteCurrentUserRun( + ListView_GetCheckState(listViewHandle, PHP_OPTIONS_INDEX_START_ATLOGON) == BST_CHECKED, + ListView_GetCheckState(listViewHandle, PHP_OPTIONS_INDEX_START_HIDDEN) == BST_CHECKED + ); + + ProcessHacker_Invoke(PhMainWndHandle, PhpOptionsNotifyChangeCallback, NULL); +} + +static NTSTATUS PhpElevateAdvancedThreadStart( + _In_ PVOID Parameter + ) +{ + PPH_STRING arguments; + + arguments = Parameter; + PhShellProcessHacker( + WindowHandleForElevate, + arguments->Buffer, + SW_SHOW, + PH_SHELL_EXECUTE_ADMIN, + PH_SHELL_APP_PROPAGATE_PARAMETERS, + INFINITE, + NULL + ); + PhDereferenceObject(arguments); + + PostMessage(WindowHandleForElevate, WM_PH_CHILD_EXIT, 0, 0); + + return STATUS_SUCCESS; +} + +UINT_PTR CALLBACK PhpChooseFontDlgHookProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, PhOptionsWindowHandle); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK PhpOptionsGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + static BOOLEAN GeneralListViewStateInitializing = FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND comboBoxHandle; + HWND listviewHandle; + ULONG i; + LOGFONT font; + + comboBoxHandle = GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT); + listviewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); + GeneralListviewImageList = ImageList_Create(1, PH_SCALE_DPI(22), ILC_COLOR, 1, 1); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_SEARCHENGINE), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_PEVIEWER), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, listviewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DBGHELPSEARCHPATH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + + PhSetListViewStyle(listviewHandle, FALSE, TRUE); + ListView_SetExtendedListViewStyleEx(listviewHandle, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); + ListView_SetImageList(listviewHandle, GeneralListviewImageList, LVSIL_SMALL); + PhSetControlTheme(listviewHandle, L"explorer"); + PhAddListViewColumn(listviewHandle, 0, 0, 0, LVCFMT_LEFT, 250, L"Name"); + PhSetExtendedListView(listviewHandle); + + for (i = 0; i < RTL_NUMBER_OF(PhSizeUnitNames); i++) + ComboBox_AddString(comboBoxHandle, PhSizeUnitNames[i]); + + if (PhMaxSizeUnit != ULONG_MAX) + ComboBox_SetCurSel(comboBoxHandle, PhMaxSizeUnit); + else + ComboBox_SetCurSel(comboBoxHandle, RTL_NUMBER_OF(PhSizeUnitNames) - 1); + + PhSetDialogItemText(hwndDlg, IDC_SEARCHENGINE, PhaGetStringSetting(L"SearchEngine")->Buffer); + PhSetDialogItemText(hwndDlg, IDC_PEVIEWER, PhaGetStringSetting(L"ProgramInspectExecutables")->Buffer); + PhSetDialogItemValue(hwndDlg, IDC_ICONPROCESSES, PhGetIntegerSetting(L"IconProcesses"), FALSE); + PhSetDialogItemText(hwndDlg, IDC_DBGHELPSEARCHPATH, PhaGetStringSetting(L"DbgHelpSearchPath")->Buffer); + + ReadCurrentUserRun(); + + // Set the font of the button for a nice preview. + if (GetCurrentFont(&font)) + { + CurrentFontInstance = CreateFontIndirect(&font); + + if (CurrentFontInstance) + { + SetWindowFont(OptionsTreeControl, CurrentFontInstance, TRUE); // HACK + SetWindowFont(listviewHandle, CurrentFontInstance, TRUE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_FONT), CurrentFontInstance, TRUE); + } + } + + GeneralListViewStateInitializing = TRUE; + PhpAdvancedPageLoad(hwndDlg); + PhpRefreshTaskManagerState(hwndDlg); + GeneralListViewStateInitializing = FALSE; + } + break; + case WM_DESTROY: + { + if (NewFontSelection) + { + PhSetStringSetting2(L"Font", &NewFontSelection->sr); + PostMessage(PhMainWndHandle, WM_PH_UPDATE_FONT, 0, 0); + } + + PhpAdvancedPageSave(hwndDlg); + + if (CurrentFontInstance) + DeleteFont(CurrentFontInstance); + + if (GeneralListviewImageList) + ImageList_Destroy(GeneralListviewImageList); + + PhClearReference(&NewFontSelection); + PhClearReference(&OldTaskMgrDebugger); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_FONT: + { + LOGFONT font; + CHOOSEFONT chooseFont; + + if (!GetCurrentFont(&font)) + { + // Can't get LOGFONT from the existing setting, probably + // because the user hasn't ever chosen a font before. + // Set the font to something familiar. + GetObject((HFONT)SendMessage(PhMainWndHandle, WM_PH_GET_FONT, 0, 0), sizeof(LOGFONT), &font); + } + + memset(&chooseFont, 0, sizeof(CHOOSEFONT)); + chooseFont.lStructSize = sizeof(CHOOSEFONT); + chooseFont.hwndOwner = hwndDlg; + chooseFont.lpfnHook = PhpChooseFontDlgHookProc; + chooseFont.lpLogFont = &font; + chooseFont.Flags = CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_ENABLEHOOK | CF_SCREENFONTS; + + if (ChooseFont(&chooseFont)) + { + PhMoveReference(&NewFontSelection, PhBufferToHexString((PUCHAR)&font, sizeof(LOGFONT))); + + // Update the button's font. + + if (CurrentFontInstance) + DeleteFont(CurrentFontInstance); + + CurrentFontInstance = CreateFontIndirect(&font); + + SetWindowFont(OptionsTreeControl, CurrentFontInstance, TRUE); // HACK + SetWindowFont(GetDlgItem(hwndDlg, IDC_SETTINGS), CurrentFontInstance, TRUE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_FONT), CurrentFontInstance, TRUE); + RestartRequired = TRUE; // HACK: Fix ToolStatus plugin toolbar resize on font change + } + } + break; + case IDC_REPLACETASKMANAGER: + { + WindowHandleForElevate = hwndDlg; + + PhCreateThread2(PhpElevateAdvancedThreadStart, PhFormatString( + L"-showoptions -hwnd %Ix", + (ULONG_PTR)PhOptionsWindowHandle // GetParent(GetParent(hwndDlg)) + )); + } + break; + case IDC_SAMPLECOUNTAUTOMATIC: + { + EnableWindow(GetDlgItem(hwndDlg, IDC_SAMPLECOUNT), Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAMPLECOUNTAUTOMATIC)) != BST_CHECKED); + } + break; + } + } + break; + case WM_PH_CHILD_EXIT: + { + PhpRefreshTaskManagerState(hwndDlg); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_CLICK: + case NM_DBLCLK: + { + LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; + LVHITTESTINFO lvHitInfo; + + lvHitInfo.pt = itemActivate->ptAction; + + if (ListView_HitTest(GetDlgItem(hwndDlg, IDC_SETTINGS), &lvHitInfo) != -1) + { + // Ignore click notifications for the listview checkbox region. + if (!(lvHitInfo.flags & LVHT_ONITEMSTATEICON)) + { + BOOLEAN itemChecked; + + // Emulate the checkbox control label click behavior and check/uncheck the checkbox when the listview item is clicked. + itemChecked = ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), itemActivate->iItem) == BST_CHECKED; + ListView_SetCheckState(GetDlgItem(hwndDlg, IDC_SETTINGS), itemActivate->iItem, !itemChecked); + } + } + } + break; + case LVN_ITEMCHANGING: + { + LPNM_LISTVIEW listView = (LPNM_LISTVIEW)lParam; + + if (listView->uChanged & LVIF_STATE) + { + if (GeneralListViewStateInitializing) + break; + + switch (listView->uNewState & LVIS_STATEIMAGEMASK) + { + case INDEXTOSTATEIMAGEMASK(1): // unchecked + { + switch (listView->iItem) + { + case PHP_OPTIONS_INDEX_ENABLE_DRIVER: + { + if (PhShowMessage2( + PhOptionsWindowHandle, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_WARNING_ICON, + L"Are you sure you want to disable the kernel-mode driver?", + L"You will be unable to use more advanced features, view details about system processes or terminate malicious software." + ) == IDNO) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + break; + } + } + break; + } + } + } + break; + case LVN_ITEMCHANGED: + { + LPNM_LISTVIEW listView = (LPNM_LISTVIEW)lParam; + + if (listView->uChanged & LVIF_STATE) + { + switch (listView->uNewState & LVIS_STATEIMAGEMASK) + { + case INDEXTOSTATEIMAGEMASK(2): // checked + { + switch (listView->iItem) + { + case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: + { + PhpOptionsShowHideTreeViewItem(FALSE); + } + break; + } + } + break; + case INDEXTOSTATEIMAGEMASK(1): // unchecked + { + switch (listView->iItem) + { + case PHP_OPTIONS_INDEX_SHOW_ADVANCED_OPTIONS: + { + PhpOptionsShowHideTreeViewItem(TRUE); + } + break; + } + } + break; + } + } + } + break; + } + } + break; + //case WM_CTLCOLORSTATIC: + // { + // static HFONT BoldFont = NULL; // leak + // HWND control = (HWND)lParam; + // HDC hdc = (HDC)wParam; + // + // if (GetDlgCtrlID(control) == IDC_DEFSTATE) + // { + // if (!BoldFont) + // BoldFont = PhDuplicateFontWithNewWeight(GetWindowFont(control), FW_BOLD); + // + // SetBkMode(hdc, TRANSPARENT); + // + // if (!PhpIsDefaultTaskManager()) + // { + // SelectFont(hdc, BoldFont); + // } + // } + // } + // break; + } + + return FALSE; +} + +static BOOLEAN PhpOptionsSettingsCallback( + _In_ PPH_SETTING Setting, + _In_ PVOID Context + ) +{ + INT lvItemIndex; + + lvItemIndex = PhAddListViewItem(Context, MAXINT, Setting->Name.Buffer, Setting); + + switch (Setting->Type) + { + case StringSettingType: + PhSetListViewSubItem(Context, lvItemIndex, 1, L"String"); + break; + case IntegerSettingType: + PhSetListViewSubItem(Context, lvItemIndex, 1, L"Integer"); + break; + case IntegerPairSettingType: + PhSetListViewSubItem(Context, lvItemIndex, 1, L"IntegerPair"); + break; + case ScalableIntegerPairSettingType: + PhSetListViewSubItem(Context, lvItemIndex, 1, L"ScalableIntegerPair"); + break; + } + + PhSetListViewSubItem(Context, lvItemIndex, 2, PH_AUTO_T(PH_STRING, PhSettingToString(Setting->Type, Setting))->Buffer); + PhSetListViewSubItem(Context, lvItemIndex, 3, Setting->DefaultValue.Buffer); + + return TRUE; +} + +static INT_PTR CALLBACK PhpOptionsAdvancedEditDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_SETTING setting = (PPH_SETTING)lParam; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhSetWindowText(hwndDlg, L"Setting Editor"); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, setting); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_NAME), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_VALUE), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + PhSetDialogItemText(hwndDlg, IDC_NAME, setting->Name.Buffer); + PhSetDialogItemText(hwndDlg, IDC_VALUE, PH_AUTO_T(PH_STRING, PhSettingToString(setting->Type, setting))->Buffer); + + EnableWindow(GetDlgItem(hwndDlg, IDC_NAME), FALSE); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDCANCEL)); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + PPH_SETTING setting = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PPH_STRING settingValue = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUE))); + + if (!PhSettingFromString( + setting->Type, + &settingValue->sr, + settingValue, + setting + )) + { + PhSettingFromString( + setting->Type, + &setting->DefaultValue, + NULL, + setting + ); + } + + EndDialog(hwndDlg, IDOK); + } + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK PhpOptionsAdvancedDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND listviewHandle; + + listviewHandle = GetDlgItem(hwndDlg, IDC_SETTINGS); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, listviewHandle, NULL, PH_ANCHOR_ALL); + + PhSetListViewStyle(listviewHandle, FALSE, TRUE); + PhSetControlTheme(listviewHandle, L"explorer"); + PhAddListViewColumn(listviewHandle, 0, 0, 0, LVCFMT_LEFT, 180, L"Name"); + PhAddListViewColumn(listviewHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Type"); + PhAddListViewColumn(listviewHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Value"); + PhAddListViewColumn(listviewHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Default"); + PhSetExtendedListView(listviewHandle); + + PhEnumSettings(PhpOptionsSettingsCallback, listviewHandle); + ExtendedListView_SortItems(listviewHandle); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_DBLCLK: + { + if (header->idFrom == IDC_SETTINGS) + { + PPH_SETTING setting; + INT index; + + if (setting = PhGetSelectedListViewItemParam(header->hwndFrom)) + { + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_EDITENV), + hwndDlg, + PhpOptionsAdvancedEditDlgProc, + (LPARAM)setting + ); + + if ((index = PhFindListViewItemByFlags(header->hwndFrom, -1, LVNI_SELECTED)) != -1) + { + PhSetListViewSubItem(header->hwndFrom, index, 2, PH_AUTO_T(PH_STRING, PhSettingToString(setting->Type, setting))->Buffer); + } + } + } + } + break; + } + } + break; + } + + return FALSE; +} + +typedef struct _COLOR_ITEM +{ + PWSTR SettingName; + PWSTR UseSettingName; + PWSTR Name; + PWSTR Description; + + BOOLEAN CurrentUse; + COLORREF CurrentColor; +} COLOR_ITEM, *PCOLOR_ITEM; + +#define COLOR_ITEM(SettingName, Name, Description) { SettingName, L"Use" SettingName, Name, Description } + +static COLOR_ITEM ColorItems[] = +{ + COLOR_ITEM(L"ColorOwnProcesses", L"Own processes", L"Processes running under the same user account as Process Hacker."), + COLOR_ITEM(L"ColorSystemProcesses", L"System processes", L"Processes running under the NT AUTHORITY\\SYSTEM user account."), + COLOR_ITEM(L"ColorServiceProcesses", L"Service processes", L"Processes which host one or more services."), + COLOR_ITEM(L"ColorJobProcesses", L"Job processes", L"Processes associated with a job."), +#ifdef _WIN64 + COLOR_ITEM(L"ColorWow64Processes", L"32-bit processes", L"Processes running under WOW64, i.e. 32-bit."), +#endif + COLOR_ITEM(L"ColorDebuggedProcesses", L"Debugged processes", L"Processes that are currently being debugged."), + COLOR_ITEM(L"ColorElevatedProcesses", L"Elevated processes", L"Processes with full privileges on a system with UAC enabled."), + COLOR_ITEM(L"ColorPicoProcesses", L"Pico processes", L"Processes that belong to the Windows subsystem for Linux."), + COLOR_ITEM(L"ColorImmersiveProcesses", L"Immersive processes and DLLs", L"Processes and DLLs that belong to a Modern UI app."), + COLOR_ITEM(L"ColorSuspended", L"Suspended processes and threads", L"Processes and threads that are suspended from execution."), + COLOR_ITEM(L"ColorDotNet", L".NET processes and DLLs", L".NET (i.e. managed) processes and DLLs."), + COLOR_ITEM(L"ColorPacked", L"Packed processes", L"Executables are sometimes \"packed\" to reduce their size."), + COLOR_ITEM(L"ColorGuiThreads", L"GUI threads", L"Threads that have made at least one GUI-related system call."), + COLOR_ITEM(L"ColorRelocatedModules", L"Relocated DLLs", L"DLLs that were not loaded at their preferred image bases."), + COLOR_ITEM(L"ColorProtectedHandles", L"Protected handles", L"Handles that are protected from being closed."), + COLOR_ITEM(L"ColorInheritHandles", L"Inheritable handles", L"Handles that can be inherited by child processes."), + + COLOR_ITEM(L"ColorHandleFiltered", L"Filtered processes", L"Processes that are protected by handle object callbacks."), + COLOR_ITEM(L"ColorUnknown", L"Untrusted DLLs and Services", L"Services and DLLs which are not digitally signed."), + COLOR_ITEM(L"ColorServiceDisabled", L"Disabled Services", L"Services which have been disabled."), + //COLOR_ITEM(L"ColorServiceStop", L"Stopped Services", L"Services that are not running.") +}; + +COLORREF NTAPI PhpColorItemColorFunction( + _In_ INT Index, + _In_ PVOID Param, + _In_opt_ PVOID Context + ) +{ + PCOLOR_ITEM item = Param; + + return item->CurrentColor; +} + +UINT_PTR CALLBACK PhpColorDlgHookProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, PhOptionsWindowHandle); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK PhpOptionsHighlightingDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Highlighting Duration + PhSetDialogItemValue(hwndDlg, IDC_HIGHLIGHTINGDURATION, PhCsHighlightingDuration, FALSE); + + // New Objects + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_NEWOBJECTS), PhCsColorNew); + + // Removed Objects + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_REMOVEDOBJECTS), PhCsColorRemoved); + + // Highlighting + HighlightingListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(HighlightingListViewHandle, FALSE, TRUE); + ListView_SetExtendedListViewStyleEx(HighlightingListViewHandle, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); + PhAddListViewColumn(HighlightingListViewHandle, 0, 0, 0, LVCFMT_LEFT, 240, L"Name"); + PhSetExtendedListView(HighlightingListViewHandle); + ExtendedListView_SetItemColorFunction(HighlightingListViewHandle, PhpColorItemColorFunction); + + for (ULONG i = 0; i < RTL_NUMBER_OF(ColorItems); i++) + { + INT lvItemIndex; + + lvItemIndex = PhAddListViewItem(HighlightingListViewHandle, MAXINT, ColorItems[i].Name, &ColorItems[i]); + ColorItems[i].CurrentColor = PhGetIntegerSetting(ColorItems[i].SettingName); + ColorItems[i].CurrentUse = !!PhGetIntegerSetting(ColorItems[i].UseSettingName); + ListView_SetCheckState(HighlightingListViewHandle, lvItemIndex, ColorItems[i].CurrentUse); + } + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, HighlightingListViewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_INFO), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT | PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_ENABLEALL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DISABLEALL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + } + break; + case WM_DESTROY: + { + PH_SET_INTEGER_CACHED_SETTING(HighlightingDuration, PhGetDialogItemValue(hwndDlg, IDC_HIGHLIGHTINGDURATION)); + PH_SET_INTEGER_CACHED_SETTING(ColorNew, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_NEWOBJECTS))); + PH_SET_INTEGER_CACHED_SETTING(ColorRemoved, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_REMOVEDOBJECTS))); + + for (ULONG i = 0; i < RTL_NUMBER_OF(ColorItems); i++) + { + ColorItems[i].CurrentUse = !!ListView_GetCheckState(HighlightingListViewHandle, i); + PhSetIntegerSetting(ColorItems[i].SettingName, ColorItems[i].CurrentColor); + PhSetIntegerSetting(ColorItems[i].UseSettingName, ColorItems[i].CurrentUse); + } + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + + ExtendedListView_SetColumnWidth(HighlightingListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_ENABLEALL: + { + for (ULONG i = 0; i < RTL_NUMBER_OF(ColorItems); i++) + ListView_SetCheckState(HighlightingListViewHandle, i, TRUE); + } + break; + case IDC_DISABLEALL: + { + for (ULONG i = 0; i < RTL_NUMBER_OF(ColorItems); i++) + ListView_SetCheckState(HighlightingListViewHandle, i, FALSE); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_DBLCLK: + { + if (header->hwndFrom == HighlightingListViewHandle) + { + PCOLOR_ITEM item; + + if (item = PhGetSelectedListViewItemParam(HighlightingListViewHandle)) + { + CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; + COLORREF customColors[16] = { 0 }; + + chooseColor.hwndOwner = hwndDlg; + chooseColor.rgbResult = item->CurrentColor; + chooseColor.lpCustColors = customColors; + chooseColor.lpfnHook = PhpColorDlgHookProc; + chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK | CC_RGBINIT; + + if (ChooseColor(&chooseColor)) + { + item->CurrentColor = chooseColor.rgbResult; + InvalidateRect(HighlightingListViewHandle, NULL, TRUE); + } + } + } + } + break; + case LVN_GETINFOTIP: + { + if (header->hwndFrom == HighlightingListViewHandle) + { + NMLVGETINFOTIP *getInfoTip = (NMLVGETINFOTIP *)lParam; + PH_STRINGREF tip; + + PhInitializeStringRefLongHint(&tip, ColorItems[getInfoTip->iItem].Description); + PhCopyListViewInfoTip(getInfoTip, &tip); + } + } + break; + } + } + break; + } + + REFLECT_MESSAGE_DLG(hwndDlg, HighlightingListViewHandle, uMsg, wParam, lParam); + + return FALSE; +} + +INT_PTR CALLBACK PhpOptionsGraphsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + // Show Text + SetDlgItemCheckForSetting(hwndDlg, IDC_SHOWTEXT, L"GraphShowText"); + SetDlgItemCheckForSetting(hwndDlg, IDC_USEOLDCOLORS, L"GraphColorMode"); + SetDlgItemCheckForSetting(hwndDlg, IDC_SHOWCOMMITINSUMMARY, L"ShowCommitInSummary"); + + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_CPUUSER), PhCsColorCpuUser); + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_CPUKERNEL), PhCsColorCpuKernel); + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_IORO), PhCsColorIoReadOther); + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_IOW), PhCsColorIoWrite); + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_PRIVATE), PhCsColorPrivate); + ColorBox_SetColor(GetDlgItem(hwndDlg, IDC_PHYSICAL), PhCsColorPhysical); + } + break; + case WM_DESTROY: + { + SetSettingForDlgItemCheck(hwndDlg, IDC_SHOWTEXT, L"GraphShowText"); + SetSettingForDlgItemCheck(hwndDlg, IDC_USEOLDCOLORS, L"GraphColorMode"); + SetSettingForDlgItemCheck(hwndDlg, IDC_SHOWCOMMITINSUMMARY, L"ShowCommitInSummary"); + PH_SET_INTEGER_CACHED_SETTING(ColorCpuUser, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_CPUUSER))); + PH_SET_INTEGER_CACHED_SETTING(ColorCpuKernel, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_CPUKERNEL))); + PH_SET_INTEGER_CACHED_SETTING(ColorIoReadOther, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_IORO))); + PH_SET_INTEGER_CACHED_SETTING(ColorIoWrite, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_IOW))); + PH_SET_INTEGER_CACHED_SETTING(ColorPrivate, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_PRIVATE))); + PH_SET_INTEGER_CACHED_SETTING(ColorPhysical, ColorBox_GetColor(GetDlgItem(hwndDlg, IDC_PHYSICAL))); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/pagfiles.c b/ProcessHacker/pagfiles.c index 067e31ad334c..27eecd3b4fdf 100644 --- a/ProcessHacker/pagfiles.c +++ b/ProcessHacker/pagfiles.c @@ -1,159 +1,159 @@ -/* - * Process Hacker - - * pagefiles viewer - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -INT_PTR CALLBACK PhpPagefilesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowPagefilesDialog( - _In_ HWND ParentWindowHandle - ) -{ - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PAGEFILES), - ParentWindowHandle, - PhpPagefilesDlgProc - ); -} - -static VOID PhpAddPagefileItems( - _In_ HWND ListViewHandle, - _In_ PVOID Pagefiles - ) -{ - PSYSTEM_PAGEFILE_INFORMATION pagefile; - - pagefile = PH_FIRST_PAGEFILE(Pagefiles); - - while (pagefile) - { - INT lvItemIndex; - PPH_STRING fileName; - PPH_STRING newFileName; - PPH_STRING usage; - - fileName = PhCreateStringFromUnicodeString(&pagefile->PageFileName); - newFileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - - lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, - newFileName->Buffer, NULL); - - PhDereferenceObject(newFileName); - - // Usage - usage = PhFormatSize(UInt32x32To64(pagefile->TotalInUse, PAGE_SIZE), -1); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, usage->Buffer); - PhDereferenceObject(usage); - - // Peak usage - usage = PhFormatSize(UInt32x32To64(pagefile->PeakUsage, PAGE_SIZE), -1); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, usage->Buffer); - PhDereferenceObject(usage); - - // Total - usage = PhFormatSize(UInt32x32To64(pagefile->TotalSize, PAGE_SIZE), -1); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, usage->Buffer); - PhDereferenceObject(usage); - - pagefile = PH_NEXT_PAGEFILE(pagefile); - } -} - -INT_PTR CALLBACK PhpPagefilesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - HWND lvHandle; - PVOID pagefiles; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"File name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Usage"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 100, L"Peak usage"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Total"); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - - if (NT_SUCCESS(status = PhEnumPagefiles(&pagefiles))) - { - PhpAddPagefileItems(lvHandle, pagefiles); - PhFree(pagefiles); - } - else - { - PhShowStatus(hwndDlg, L"Unable to get pagefile information", status, 0); - EndDialog(hwndDlg, IDCANCEL); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_REFRESH: - { - NTSTATUS status; - PVOID pagefiles; - - if (NT_SUCCESS(status = PhEnumPagefiles(&pagefiles))) - { - ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_LIST)); - PhpAddPagefileItems(GetDlgItem(hwndDlg, IDC_LIST), pagefiles); - PhFree(pagefiles); - } - else - { - PhShowStatus(hwndDlg, L"Unable to get pagefile information", status, 0); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * pagefiles viewer + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +INT_PTR CALLBACK PhpPagefilesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowPagefilesDialog( + _In_ HWND ParentWindowHandle + ) +{ + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PAGEFILES), + ParentWindowHandle, + PhpPagefilesDlgProc + ); +} + +static VOID PhpAddPagefileItems( + _In_ HWND ListViewHandle, + _In_ PVOID Pagefiles + ) +{ + PSYSTEM_PAGEFILE_INFORMATION pagefile; + + pagefile = PH_FIRST_PAGEFILE(Pagefiles); + + while (pagefile) + { + INT lvItemIndex; + PPH_STRING fileName; + PPH_STRING newFileName; + PPH_STRING usage; + + fileName = PhCreateStringFromUnicodeString(&pagefile->PageFileName); + newFileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, + newFileName->Buffer, NULL); + + PhDereferenceObject(newFileName); + + // Usage + usage = PhFormatSize(UInt32x32To64(pagefile->TotalInUse, PAGE_SIZE), ULONG_MAX); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, usage->Buffer); + PhDereferenceObject(usage); + + // Peak usage + usage = PhFormatSize(UInt32x32To64(pagefile->PeakUsage, PAGE_SIZE), ULONG_MAX); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, usage->Buffer); + PhDereferenceObject(usage); + + // Total + usage = PhFormatSize(UInt32x32To64(pagefile->TotalSize, PAGE_SIZE), ULONG_MAX); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, usage->Buffer); + PhDereferenceObject(usage); + + pagefile = PH_NEXT_PAGEFILE(pagefile); + } +} + +INT_PTR CALLBACK PhpPagefilesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + HWND lvHandle; + PVOID pagefiles; + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"File name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Usage"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 100, L"Peak usage"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Total"); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + + if (NT_SUCCESS(status = PhEnumPagefiles(&pagefiles))) + { + PhpAddPagefileItems(lvHandle, pagefiles); + PhFree(pagefiles); + } + else + { + PhShowStatus(hwndDlg, L"Unable to get pagefile information", status, 0); + EndDialog(hwndDlg, IDCANCEL); + } + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_REFRESH: + { + NTSTATUS status; + PVOID pagefiles; + + if (NT_SUCCESS(status = PhEnumPagefiles(&pagefiles))) + { + ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_LIST)); + PhpAddPagefileItems(GetDlgItem(hwndDlg, IDC_LIST), pagefiles); + PhFree(pagefiles); + } + else + { + PhShowStatus(hwndDlg, L"Unable to get pagefile information", status, 0); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/pcre/LICENCE b/ProcessHacker/pcre/LICENCE index 6600a659074f..b0f8804fffa0 100644 --- a/ProcessHacker/pcre/LICENCE +++ b/ProcessHacker/pcre/LICENCE @@ -4,10 +4,11 @@ PCRE2 LICENCE PCRE2 is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. -Release 10 of PCRE2 is distributed under the terms of the "BSD" licence, as -specified below. The documentation for PCRE2, supplied in the "doc" -directory, is distributed under the same terms as the software itself. The data -in the testdata directory is not copyrighted and is in the public domain. +Releases 10.00 and above of PCRE2 are distributed under the terms of the "BSD" +licence, as specified below, with one exemption for certain binary +redistributions. The documentation for PCRE2, supplied in the "doc" directory, +is distributed under the same terms as the software itself. The data in the +testdata directory is not copyrighted and is in the public domain. The basic library functions are written in C and are freestanding. Also included in the distribution is a just-in-time compiler that can be used to @@ -25,7 +26,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2016 University of Cambridge +Copyright (c) 1997-2018 University of Cambridge All rights reserved. @@ -34,9 +35,9 @@ PCRE2 JUST-IN-TIME COMPILATION SUPPORT Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2010-2016 Zoltan Herczeg +Copyright(c) 2010-2018 Zoltan Herczeg All rights reserved. @@ -45,9 +46,9 @@ STACK-LESS JUST-IN-TIME COMPILER Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2009-2016 Zoltan Herczeg +Copyright(c) 2009-2018 Zoltan Herczeg All rights reserved. @@ -57,11 +58,11 @@ THE "BSD" LICENCE Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, + * Redistributions of source code must retain the above copyright notices, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the + notices, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the names of any @@ -80,4 +81,14 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES +------------------------------------------ + +The second condition in the BSD licence (covering binary redistributions) does +not apply all the way down a chain of software. If binary package A includes +PCRE2, it must respect the condition, but if package B is software that +includes package A, the condition is not imposed on package B unless it uses +PCRE2 independently. + End diff --git a/ProcessHacker/pcre/README b/ProcessHacker/pcre/README index 48d2ffdd7cbc..2eb621b0ffcd 100644 --- a/ProcessHacker/pcre/README +++ b/ProcessHacker/pcre/README @@ -15,8 +15,8 @@ subscribe or manage your subscription here: https://lists.exim.org/mailman/listinfo/pcre-dev -Please read the NEWS file if you are upgrading from a previous release. -The contents of this README file are: +Please read the NEWS file if you are upgrading from a previous release. The +contents of this README file are: The PCRE2 APIs Documentation for PCRE2 @@ -44,8 +44,8 @@ wrappers. The distribution does contain a set of C wrapper functions for the 8-bit library that are based on the POSIX regular expression API (see the pcre2posix -man page). These can be found in a library called libpcre2posix. Note that this -just provides a POSIX calling interface to PCRE2; the regular expressions +man page). These can be found in a library called libpcre2-posix. Note that +this just provides a POSIX calling interface to PCRE2; the regular expressions themselves still follow Perl syntax and semantics. The POSIX API is restricted, and does not give full access to all of PCRE2's facilities. @@ -58,8 +58,8 @@ renamed or pointed at by a link. If you are using the POSIX interface to PCRE2 and there is already a POSIX regex library installed on your system, as well as worrying about the regex.h header file (as mentioned above), you must also take care when linking programs -to ensure that they link with PCRE2's libpcre2posix library. Otherwise they may -pick up the POSIX functions of the same name from the other library. +to ensure that they link with PCRE2's libpcre2-posix library. Otherwise they +may pick up the POSIX functions of the same name from the other library. One way of avoiding this confusion is to compile PCRE2 with the addition of -Dregcomp=PCRE2regcomp (and similarly for the other POSIX functions) to the @@ -95,10 +95,9 @@ PCRE2 documentation is supplied in two other forms: Building PCRE2 on non-Unix-like systems --------------------------------------- -For a non-Unix-like system, please read the comments in the file -NON-AUTOTOOLS-BUILD, though if your system supports the use of "configure" and -"make" you may be able to build PCRE2 using autotools in the same way as for -many Unix-like systems. +For a non-Unix-like system, please read the file NON-AUTOTOOLS-BUILD, though if +your system supports the use of "configure" and "make" you may be able to build +PCRE2 using autotools in the same way as for many Unix-like systems. PCRE2 can also be configured using CMake, which can be run in various ways (command line, GUI, etc). This creates Makefiles, solution files, etc. The file @@ -168,28 +167,30 @@ library. They are also documented in the pcre2build man page. built. If you want only the 16-bit or 32-bit library, use --disable-pcre2-8 to disable building the 8-bit library. -. If you want to include support for just-in-time compiling, which can give - large performance improvements on certain platforms, add --enable-jit to the - "configure" command. This support is available only for certain hardware +. If you want to include support for just-in-time (JIT) compiling, which can + give large performance improvements on certain platforms, add --enable-jit to + the "configure" command. This support is available only for certain hardware architectures. If you try to enable it on an unsupported architecture, there - will be a compile time error. - -. When JIT support is enabled, pcre2grep automatically makes use of it, unless - you add --disable-pcre2grep-jit to the "configure" command. - -. If you do not want to make use of the support for UTF-8 Unicode character - strings in the 8-bit library, UTF-16 Unicode character strings in the 16-bit - library, or UTF-32 Unicode character strings in the 32-bit library, you can - add --disable-unicode to the "configure" command. This reduces the size of - the libraries. It is not possible to configure one library with Unicode - support, and another without, in the same configuration. + will be a compile time error. If in doubt, use --enable-jit=auto, which + enables JIT only if the current hardware is supported. + +. If you are enabling JIT under SELinux you may also want to add + --enable-jit-sealloc, which enables the use of an execmem allocator in JIT + that is compatible with SELinux. This has no effect if JIT is not enabled. + +. If you do not want to make use of the default support for UTF-8 Unicode + character strings in the 8-bit library, UTF-16 Unicode character strings in + the 16-bit library, or UTF-32 Unicode character strings in the 32-bit + library, you can add --disable-unicode to the "configure" command. This + reduces the size of the libraries. It is not possible to configure one + library with Unicode support, and another without, in the same configuration. + It is also not possible to use --enable-ebcdic (see below) with Unicode + support, so if this option is set, you must also use --disable-unicode. When Unicode support is available, the use of a UTF encoding still has to be enabled by setting the PCRE2_UTF option at run time or starting a pattern with (*UTF). When PCRE2 is compiled with Unicode support, its input can only - either be ASCII or UTF-8/16/32, even when running on EBCDIC platforms. It is - not possible to use both --enable-unicode and --enable-ebcdic at the same - time. + either be ASCII or UTF-8/16/32, even when running on EBCDIC platforms. As well as supporting UTF strings, Unicode support includes support for the \P, \p, and \X sequences that recognize Unicode character properties. @@ -199,20 +200,14 @@ library. They are also documented in the pcre2build man page. or starting a pattern with (*UCP). . You can build PCRE2 to recognize either CR or LF or the sequence CRLF, or any - of the preceding, or any of the Unicode newline sequences, as indicating the - end of a line. Whatever you specify at build time is the default; the caller - of PCRE2 can change the selection at run time. The default newline indicator - is a single LF character (the Unix standard). You can specify the default - newline indicator by adding --enable-newline-is-cr, --enable-newline-is-lf, - --enable-newline-is-crlf, --enable-newline-is-anycrlf, or - --enable-newline-is-any to the "configure" command, respectively. - - If you specify --enable-newline-is-cr or --enable-newline-is-crlf, some of - the standard tests will fail, because the lines in the test files end with - LF. Even if the files are edited to change the line endings, there are likely - to be some failures. With --enable-newline-is-anycrlf or - --enable-newline-is-any, many tests should succeed, but there may be some - failures. + of the preceding, or any of the Unicode newline sequences, or the NUL (zero) + character as indicating the end of a line. Whatever you specify at build time + is the default; the caller of PCRE2 can change the selection at run time. The + default newline indicator is a single LF character (the Unix standard). You + can specify the default newline indicator by adding --enable-newline-is-cr, + --enable-newline-is-lf, --enable-newline-is-crlf, + --enable-newline-is-anycrlf, --enable-newline-is-any, or + --enable-newline-is-nul to the "configure" command, respectively. . By default, the sequence \R in a pattern matches any Unicode line ending sequence. This is independent of the option specifying what PCRE2 considers @@ -234,49 +229,47 @@ library. They are also documented in the pcre2build man page. --with-parens-nest-limit=500 -. PCRE2 has a counter that can be set to limit the amount of resources it uses - when matching a pattern. If the limit is exceeded during a match, the match - fails. The default is ten million. You can change the default by setting, for - example, +. PCRE2 has a counter that can be set to limit the amount of computing resource + it uses when matching a pattern. If the limit is exceeded during a match, the + match fails. The default is ten million. You can change the default by + setting, for example, --with-match-limit=500000 on the "configure" command. This is just the default; individual calls to - pcre2_match() can supply their own value. There is more discussion on the - pcre2api man page. + pcre2_match() or pcre2_dfa_match() can supply their own value. There is more + discussion in the pcre2api man page (search for pcre2_set_match_limit). + +. There is a separate counter that limits the depth of nested backtracking + (pcre2_match()) or nested function calls (pcre2_dfa_match()) during a + matching process, which indirectly limits the amount of heap memory that is + used, and in the case of pcre2_dfa_match() the amount of stack as well. This + counter also has a default of ten million, which is essentially "unlimited". + You can change the default by setting, for example, -. There is a separate counter that limits the depth of recursive function calls - during a matching process. This also has a default of ten million, which is - essentially "unlimited". You can change the default by setting, for example, + --with-match-limit-depth=5000 - --with-match-limit-recursion=500000 + There is more discussion in the pcre2api man page (search for + pcre2_set_depth_limit). - Recursive function calls use up the runtime stack; running out of stack can - cause programs to crash in strange ways. There is a discussion about stack - sizes in the pcre2stack man page. +. You can also set an explicit limit on the amount of heap memory used by + the pcre2_match() and pcre2_dfa_match() interpreters: + + --with-heap-limit=500 + + The units are kibibytes (units of 1024 bytes). This limit does not apply when + the JIT optimization (which has its own memory control features) is used. + There is more discussion on the pcre2api man page (search for + pcre2_set_heap_limit). . In the 8-bit library, the default maximum compiled pattern size is around - 64K. You can increase this by adding --with-link-size=3 to the "configure" - command. PCRE2 then uses three bytes instead of two for offsets to different - parts of the compiled pattern. In the 16-bit library, --with-link-size=3 is - the same as --with-link-size=4, which (in both libraries) uses four-byte - offsets. Increasing the internal link size reduces performance in the 8-bit - and 16-bit libraries. In the 32-bit library, the link size setting is - ignored, as 4-byte offsets are always used. - -. You can build PCRE2 so that its internal match() function that is called from - pcre2_match() does not call itself recursively. Instead, it uses memory - blocks obtained from the heap to save data that would otherwise be saved on - the stack. To build PCRE2 like this, use - - --disable-stack-for-recursion - - on the "configure" command. PCRE2 runs more slowly in this mode, but it may - be necessary in environments with limited stack sizes. This applies only to - the normal execution of the pcre2_match() function; if JIT support is being - successfully used, it is not relevant. Equally, it does not apply to - pcre2_dfa_match(), which does not use deeply nested recursion. There is a - discussion about stack sizes in the pcre2stack man page. + 64 kibibytes. You can increase this by adding --with-link-size=3 to the + "configure" command. PCRE2 then uses three bytes instead of two for offsets + to different parts of the compiled pattern. In the 16-bit library, + --with-link-size=3 is the same as --with-link-size=4, which (in both + libraries) uses four-byte offsets. Increasing the internal link size reduces + performance in the 8-bit and 16-bit libraries. In the 32-bit library, the + link size setting is ignored, as 4-byte offsets are always used. . For speed, PCRE2 uses four tables for manipulating and identifying characters whose code point values are less than 256. By default, it uses a set of @@ -324,6 +317,14 @@ library. They are also documented in the pcre2build man page. running "make" to build PCRE2. There is more information about coverage reporting in the "pcre2build" documentation. +. When JIT support is enabled, pcre2grep automatically makes use of it, unless + you add --disable-pcre2grep-jit to the "configure" command. + +. There is support for calling external programs during matching in the + pcre2grep command, using PCRE2's callout facility with string arguments. This + support can be disabled by adding --disable-pcre2grep-callout to the + "configure" command. + . The pcre2grep program currently supports only 8-bit data files, and so requires the 8-bit PCRE2 library. It is possible to compile pcre2grep to use libz and/or libbz2, in order to read .gz and .bz2 files (respectively), by @@ -334,12 +335,23 @@ library. They are also documented in the pcre2build man page. Of course, the relevant libraries must be installed on your system. -. The default size (in bytes) of the internal buffer used by pcre2grep can be - set by, for example: +. The default starting size (in bytes) of the internal buffer used by pcre2grep + can be set by, for example: --with-pcre2grep-bufsize=51200 - The value must be a plain integer. The default is 20480. + The value must be a plain integer. The default is 20480. The amount of memory + used by pcre2grep is actually three times this number, to allow for "before" + and "after" lines. If very long lines are encountered, the buffer is + automatically enlarged, up to a fixed maximum size. + +. The default maximum size of pcre2grep's internal buffer can be set by, for + example: + + --with-pcre2grep-max-bufsize=2097152 + + The default is either 1048576 or the value of --with-pcre2grep-bufsize, + whichever is the larger. . It is possible to compile pcre2test so that it links with the libreadline or libedit libraries, by specifying, respectively, @@ -364,6 +376,29 @@ library. They are also documented in the pcre2build man page. tgetflag, or tgoto, this is the problem, and linking with the ncurses library should fix it. +. There is a special option called --enable-fuzz-support for use by people who + want to run fuzzing tests on PCRE2. At present this applies only to the 8-bit + library. If set, it causes an extra library called libpcre2-fuzzsupport.a to + be built, but not installed. This contains a single function called + LLVMFuzzerTestOneInput() whose arguments are a pointer to a string and the + length of the string. When called, this function tries to compile the string + as a pattern, and if that succeeds, to match it. This is done both with no + options and with some random options bits that are generated from the string. + Setting --enable-fuzz-support also causes a binary called pcre2fuzzcheck to + be created. This is normally run under valgrind or used when PCRE2 is + compiled with address sanitizing enabled. It calls the fuzzing function and + outputs information about it is doing. The input strings are specified by + arguments: if an argument starts with "=" the rest of it is a literal input + string. Otherwise, it is assumed to be a file name, and the contents of the + file are the test string. + +. Releases before 10.30 could be compiled with --disable-stack-for-recursion, + which caused pcre2_match() to use individual blocks on the heap for + backtracking instead of recursive function calls (which use the stack). This + is now obsolete since pcre2_match() was refactored always to use the heap (in + a much more efficient way than before). This option is retained for backwards + compatibility, but has no effect other than to output a warning. + The "configure" script builds the following files for the basic C library: . Makefile the makefile that builds the library @@ -538,7 +573,7 @@ script creates the .txt and HTML forms of the documentation from the man pages. Testing PCRE2 ------------- +------------- To test the basic PCRE2 library on a Unix-like system, run the RunTest script. There is another script called RunGrepTest that tests the pcre2grep command. @@ -630,32 +665,43 @@ with the perltest.sh script, and test 5 checking PCRE2-specific things. Tests 6 and 7 check the pcre2_dfa_match() alternative matching function, in non-UTF mode and UTF-mode with Unicode property support, respectively. -Test 8 checks some internal offsets and code size features; it is run only when -the default "link size" of 2 is set (in other cases the sizes change) and when -Unicode support is enabled. +Test 8 checks some internal offsets and code size features, but it is run only +when Unicode support is enabled. The output is different in 8-bit, 16-bit, and +32-bit modes and for different link sizes, so there are different output files +for each mode and link size. Tests 9 and 10 are run only in 8-bit mode, and tests 11 and 12 are run only in 16-bit and 32-bit modes. These are tests that generate different output in 8-bit mode. Each pair are for general cases and Unicode support, respectively. + Test 13 checks the handling of non-UTF characters greater than 255 by pcre2_dfa_match() in 16-bit and 32-bit modes. -Test 14 contains a number of tests that must not be run with JIT. They check, +Test 14 contains some special UTF and UCP tests that give different output for +different code unit widths. + +Test 15 contains a number of tests that must not be run with JIT. They check, among other non-JIT things, the match-limiting features of the intepretive matcher. -Test 15 is run only when JIT support is not available. It checks that an +Test 16 is run only when JIT support is not available. It checks that an attempt to use JIT has the expected behaviour. -Test 16 is run only when JIT support is available. It checks JIT complete and +Test 17 is run only when JIT support is available. It checks JIT complete and partial modes, match-limiting under JIT, and other JIT-specific features. -Tests 17 and 18 are run only in 8-bit mode. They check the POSIX interface to +Tests 18 and 19 are run only in 8-bit mode. They check the POSIX interface to the 8-bit library, without and with Unicode support, respectively. -Test 19 checks the serialization functions by writing a set of compiled +Test 20 checks the serialization functions by writing a set of compiled patterns to a file, and then reloading and checking them. +Tests 21 and 22 test \C support when the use of \C is not locked out, without +and with UTF support, respectively. Test 23 tests \C when it is locked out. + +Tests 24 and 25 test the experimental pattern conversion functions, without and +with UTF support, respectively. + Character tables ---------------- @@ -674,7 +720,7 @@ specified for ./configure, a different version of pcre2_chartables.c is built by the program dftables (compiled from dftables.c), which uses the ANSI C character handling functions such as isalnum(), isalpha(), isupper(), islower(), etc. to build the table sources. This means that the default C -locale which is set for your system will control the contents of these default +locale that is set for your system will control the contents of these default tables. You can change the default tables by editing pcre2_chartables.c and then re-building PCRE2. If you do this, you should take care to ensure that the file does not get automatically re-generated. The best way to do this is to @@ -729,8 +775,10 @@ The distribution should contain the files listed below. src/pcre2_compile.c ) src/pcre2_config.c ) src/pcre2_context.c ) + src/pcre2_convert.c ) src/pcre2_dfa_match.c ) src/pcre2_error.c ) + src/pcre2_extuni.c ) src/pcre2_find_bracket.c ) src/pcre2_jit_compile.c ) src/pcre2_jit_match.c ) sources for the functions in the library, @@ -752,6 +800,7 @@ The distribution should contain the files listed below. src/pcre2_xclass.c ) src/pcre2_printint.c debugging function that is used by pcre2test, + src/pcre2_fuzzsupport.c function for (optional) fuzzing support src/config.h.in template for config.h, when built by "configure" src/pcre2.h.in template for pcre2.h when built by "configure" @@ -767,7 +816,6 @@ The distribution should contain the files listed below. src/pcre2demo.c simple demonstration of coding calls to PCRE2 src/pcre2grep.c source of a grep utility that uses PCRE2 src/pcre2test.c comprehensive test program - src/pcre2_printint.c part of pcre2test src/pcre2_jit_test.c JIT test program (C) Auxiliary files: @@ -809,7 +857,7 @@ The distribution should contain the files listed below. libpcre2-8.pc.in template for libpcre2-8.pc for pkg-config libpcre2-16.pc.in template for libpcre2-16.pc for pkg-config libpcre2-32.pc.in template for libpcre2-32.pc for pkg-config - libpcre2posix.pc.in template for libpcre2posix.pc for pkg-config + libpcre2-posix.pc.in template for libpcre2-posix.pc for pkg-config ltmain.sh file used to build a libtool script missing ) common stub for a few missing GNU programs while ) installing, generated by automake @@ -832,12 +880,12 @@ The distribution should contain the files listed below. (E) Auxiliary files for building PCRE2 "by hand" - pcre2.h.generic ) a version of the public PCRE2 header file + src/pcre2.h.generic ) a version of the public PCRE2 header file ) for use in non-"configure" environments - config.h.generic ) a version of config.h for use in non-"configure" + src/config.h.generic ) a version of config.h for use in non-"configure" ) environments Philip Hazel Email local part: ph10 Email domain: cam.ac.uk -Last updated: 16 October 2015 +Last updated: 17 June 2018 diff --git a/ProcessHacker/pcre/config.h b/ProcessHacker/pcre/config.h index 566feb63c5ed..05cd1469c416 100644 --- a/ProcessHacker/pcre/config.h +++ b/ProcessHacker/pcre/config.h @@ -18,10 +18,10 @@ to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H, but if you do, default values will be taken from config.h for non-boolean macros that are not defined on the command line. -Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be defined -(conventionally to 1) for TRUE, and not defined at all for FALSE. All such -macros are listed as a commented #undef in config.h.generic. Macros such as -MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are +Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be +defined (conventionally to 1) for TRUE, and not defined at all for FALSE. All +such macros are listed as a commented #undef in config.h.generic. Macros such +as MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are surrounded by #ifndef/#endif lines so that the value can be overridden by -D. PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if @@ -33,7 +33,9 @@ sure both macros are undefined; an emulation function will then be used. */ value), this is changed so that backslash-R matches only CR, LF, or CRLF. The build-time default can be overridden by the user of PCRE2 at runtime. */ -/* #undef BSR_ANYCRLF */ +#ifndef BSR_ANYCRLF +#define BSR_ANYCRLF 1 +#endif /* If you are compiling for a system that uses EBCDIC instead of ASCII character codes, define this macro to any value. When EBCDIC is set, PCRE2 @@ -86,6 +88,9 @@ sure both macros are undefined; an emulation function will then be used. */ #define HAVE_MEMORY_H 1 #endif +/* Define to 1 if you have the `mkostemp' function. */ +/* #undef HAVE_MKOSTEMP */ + /* Define if you have POSIX threads libraries and header files. */ /* #undef HAVE_PTHREAD */ @@ -98,6 +103,9 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_READLINE_H */ +/* Define to 1 if you have the `secure_getenv' function. */ +/* #undef HAVE_SECURE_GETENV */ + /* Define to 1 if you have the header file. */ #ifndef HAVE_STDINT_H #define HAVE_STDINT_H 1 @@ -113,7 +121,6 @@ sure both macros are undefined; an emulation function will then be used. */ #define HAVE_STRERROR 1 #endif - /* Define to 1 if you have the header file. */ /* #undef HAVE_STRINGS_H */ @@ -149,19 +156,18 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ZLIB_H */ -/* PCRE2 uses recursive function calls to handle backtracking while matching. - This can sometimes be a problem on systems that have stacks of limited - size. Define HEAP_MATCH_RECURSE to any value to get a version that doesn't - use recursion in the match() function; instead it creates its own stack by - steam using memory from the heap. For more detail, see the comments and - other stuff just above the match() function. */ -/* #undef HEAP_MATCH_RECURSE */ +/* This limits the amount of memory that may be used while matching a pattern. + It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply + to JIT matching. The value is in kibibytes (units of 1024 bytes). */ +#ifndef HEAP_LIMIT +#define HEAP_LIMIT 20000000 +#endif /* The value of LINK_SIZE determines the number of bytes used to store links as offsets within the compiled regex. The default is 2, which allows for - compiled patterns up to 64K long. This covers the vast majority of cases. - However, PCRE2 can also be compiled to use 3 or 4 bytes instead. This - allows for longer patterns in extreme cases. */ + compiled patterns up to 65535 code units long. This covers the vast + majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes + instead. This allows for longer patterns in extreme cases. */ #ifndef LINK_SIZE #define LINK_SIZE 2 #endif @@ -173,25 +179,28 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* The value of MATCH_LIMIT determines the default number of times the - internal match() function can be called during a single execution of - pcre2_match(). There is a runtime interface for setting a different limit. - The limit exists in order to catch runaway regular expressions that take - for ever to determine that they do not match. The default is set very large - so that it does not accidentally catch legitimate cases. */ + pcre2_match() function can record a backtrack position during a single + matching attempt. The value is also used to limit a loop counter in + pcre2_dfa_match(). There is a runtime interface for setting a different + limit. The limit exists in order to catch runaway regular expressions that + take for ever to determine that they do not match. The default is set very + large so that it does not accidentally catch legitimate cases. */ #ifndef MATCH_LIMIT #define MATCH_LIMIT 10000000 #endif -/* The above limit applies to all calls of match(), whether or not they - increase the recursion depth. In some environments it is desirable to limit - the depth of recursive calls of match() more strictly, in order to restrict - the maximum amount of stack (or heap, if HEAP_MATCH_RECURSE is defined) - that is used. The value of MATCH_LIMIT_RECURSION applies only to recursive - calls of match(). To have any useful effect, it must be less than the value - of MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There - is a runtime method for setting a different limit. */ -#ifndef MATCH_LIMIT_RECURSION -#define MATCH_LIMIT_RECURSION MATCH_LIMIT +/* The above limit applies to all backtracks, whether or not they are nested. + In some environments it is desirable to limit the nesting of backtracking + (that is, the depth of tree that is searched) more strictly, in order to + restrict the maximum amount of heap memory that is used. The value of + MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it + must be less than the value of MATCH_LIMIT. The default is to use the same + value as MATCH_LIMIT. There is a runtime method for setting a different + limit. In the case of pcre2_dfa_match(), this limit controls the depth of + the internal nested function calls that are used for pattern recursions, + lookarounds, and atomic groups. */ +#ifndef MATCH_LIMIT_DEPTH +#define MATCH_LIMIT_DEPTH MATCH_LIMIT #endif /* This limit is parameterized just in case anybody ever wants to change it. @@ -213,10 +222,10 @@ sure both macros are undefined; an emulation function will then be used. */ /* The value of NEWLINE_DEFAULT determines the default newline character sequence. PCRE2 client programs can override this by selecting other values - at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), and 5 - (ANYCRLF). */ + at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5 + (ANYCRLF), and 6 (NUL). */ #ifndef NEWLINE_DEFAULT -#define NEWLINE_DEFAULT 2 +#define NEWLINE_DEFAULT 4 #endif /* Name of package */ @@ -229,7 +238,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_NAME "PCRE2" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE2 10.22" +#define PACKAGE_STRING "PCRE2 10.32" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre2" @@ -238,7 +247,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "10.22" +#define PACKAGE_VERSION "10.32" /* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested parentheses (of any kind) in a pattern. This limits the amount of system @@ -247,15 +256,24 @@ sure both macros are undefined; an emulation function will then be used. */ #define PARENS_NEST_LIMIT 250 #endif -/* The value of PCRE2GREP_BUFSIZE determines the size of buffer used by - pcre2grep to hold parts of the file it is searching. This is also the - minimum value. The actual amount of memory used by pcre2grep is three times - this number, because it allows for the buffering of "before" and "after" - lines. */ +/* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by + pcre2grep to hold parts of the file it is searching. The buffer will be + expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing + very long lines. The actual amount of memory used by pcre2grep is three + times this number, because it allows for the buffering of "before" and + "after" lines. */ #ifndef PCRE2GREP_BUFSIZE #define PCRE2GREP_BUFSIZE 20480 #endif +/* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer + used by pcre2grep to hold parts of the file it is searching. The actual + amount of memory used by pcre2grep is three times this number, because it + allows for the buffering of "before" and "after" lines. */ +#ifndef PCRE2GREP_MAX_BUFSIZE +#define PCRE2GREP_MAX_BUFSIZE 1048576 +#endif + /* Define to any value to include debugging code. */ /* #undef PCRE2_DEBUG */ @@ -279,6 +297,11 @@ sure both macros are undefined; an emulation function will then be used. */ your system. */ /* #undef PTHREAD_CREATE_JOINABLE */ +/* Define to any non-zero number to enable support for SELinux compatible + executable memory allocator in JIT. Note that this will have no effect + unless SUPPORT_JIT is also defined. */ +/* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */ + /* Define to 1 if you have the ANSI C header files. */ #ifndef STDC_HEADERS #define STDC_HEADERS 1 @@ -304,7 +327,8 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to any value to enable callout script support in pcre2grep. */ /* #undef SUPPORT_PCRE2GREP_CALLOUT */ -/* Define to any value to enable JIT support in pcre2grep. */ +/* Define to any value to enable JIT support in pcre2grep. Note that this will + have no effect unless SUPPORT_JIT is also defined. */ /* #undef SUPPORT_PCRE2GREP_JIT */ /* Define to any value to enable the 16 bit PCRE2 library. */ @@ -329,8 +353,39 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to any value for valgrind support to find invalid memory reads. */ /* #undef SUPPORT_VALGRIND */ +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + /* Version number of package */ -#define VERSION "10.22" +#define VERSION "10.32" + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/ProcessHacker/pcre/dftables.c b/ProcessHacker/pcre/dftables.c new file mode 100644 index 000000000000..c0af36252a81 --- /dev/null +++ b/ProcessHacker/pcre/dftables.c @@ -0,0 +1,219 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This is a freestanding support program to generate a file containing +character tables for PCRE2. The tables are built according to the current +locale using the pcre2_maketables() function, which is part of the PCRE2 API. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 0 /* Must be set, but not relevant here */ +#include "pcre2_internal.h" + +#define DFTABLES /* pcre2_maketables.c notices this */ +#include "pcre2_maketables.c" + +int main(int argc, char **argv) +{ +FILE *f; +int i = 1; +const unsigned char *tables; +const unsigned char *base_of_tables; + +/* By default, the default C locale is used rather than what the building user +happens to have set. However, if the -L option is given, set the locale from +the LC_xxx environment variables. */ + +if (argc > 1 && strcmp(argv[1], "-L") == 0) + { + setlocale(LC_ALL, ""); /* Set from environment variables */ + i++; + } + +if (argc < i + 1) + { + fprintf(stderr, "dftables: one filename argument is required\n"); + return 1; + } + +tables = maketables(); +base_of_tables = tables; + +f = fopen(argv[i], "wb"); +if (f == NULL) + { + fprintf(stderr, "dftables: failed to open %s for writing\n", argv[1]); + return 1; + } + +/* There are several fprintf() calls here, because gcc in pedantic mode +complains about the very long string otherwise. */ + +fprintf(f, + "/*************************************************\n" + "* Perl-Compatible Regular Expressions *\n" + "*************************************************/\n\n" + "/* This file was automatically written by the dftables auxiliary\n" + "program. It contains character tables that are used when no external\n" + "tables are passed to PCRE2 by the application that calls it. The tables\n" + "are used only for characters whose code values are less than 256. */\n\n"); + +fprintf(f, + "/*The dftables program (which is distributed with PCRE2) can be used to\n" + "build alternative versions of this file. This is necessary if you are\n" + "running in an EBCDIC environment, or if you want to default to a different\n" + "encoding, for example ISO-8859-1. When dftables is run, it creates these\n" + "tables in the current locale. This happens automatically if PCRE2 is\n" + "configured with --enable-rebuild-chartables. */\n\n"); + +/* Force config.h in z/OS */ + +#if defined NATIVE_ZOS +fprintf(f, + "/* For z/OS, config.h is forced */\n" + "#ifndef HAVE_CONFIG_H\n" + "#define HAVE_CONFIG_H 1\n" + "#endif\n\n"); +#endif + +fprintf(f, + "/* The following #include is present because without it gcc 4.x may remove\n" + "the array definition from the final binary if PCRE2 is built into a static\n" + "library and dead code stripping is activated. This leads to link errors.\n" + "Pulling in the header ensures that the array gets flagged as \"someone\n" + "outside this compilation unit might reference this\" and so it will always\n" + "be supplied to the linker. */\n\n"); + +fprintf(f, + "#ifdef HAVE_CONFIG_H\n" + "#include \"config.h\"\n" + "#endif\n\n" + "#include \"pcre2_internal.h\"\n\n"); + +fprintf(f, + "const uint8_t PRIV(default_tables)[] = {\n\n" + "/* This table is a lower casing table. */\n\n"); + +fprintf(f, " "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) fprintf(f, "\n "); + fprintf(f, "%3d", *tables++); + if (i != 255) fprintf(f, ","); + } +fprintf(f, ",\n\n"); + +fprintf(f, "/* This table is a case flipping table. */\n\n"); + +fprintf(f, " "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) fprintf(f, "\n "); + fprintf(f, "%3d", *tables++); + if (i != 255) fprintf(f, ","); + } +fprintf(f, ",\n\n"); + +fprintf(f, + "/* This table contains bit maps for various character classes. Each map is 32\n" + "bytes long and the bits run from the least significant end of each byte. The\n" + "classes that have their own maps are: space, xdigit, digit, upper, lower, word,\n" + "graph print, punct, and cntrl. Other classes are built from combinations. */\n\n"); + +fprintf(f, " "); +for (i = 0; i < cbit_length; i++) + { + if ((i & 7) == 0 && i != 0) + { + if ((i & 31) == 0) fprintf(f, "\n"); + fprintf(f, "\n "); + } + fprintf(f, "0x%02x", *tables++); + if (i != cbit_length - 1) fprintf(f, ","); + } +fprintf(f, ",\n\n"); + +fprintf(f, + "/* This table identifies various classes of character by individual bits:\n" + " 0x%02x white space character\n" + " 0x%02x letter\n" + " 0x%02x decimal digit\n" + " 0x%02x hexadecimal digit\n" + " 0x%02x alphanumeric or '_'\n*/\n\n", + ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word); + +fprintf(f, " "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) + { + fprintf(f, " /* "); + if (isprint(i-8)) fprintf(f, " %c -", i-8); + else fprintf(f, "%3d-", i-8); + if (isprint(i-1)) fprintf(f, " %c ", i-1); + else fprintf(f, "%3d", i-1); + fprintf(f, " */\n "); + } + fprintf(f, "0x%02x", *tables++); + if (i != 255) fprintf(f, ","); + } + +fprintf(f, "};/* "); +if (isprint(i-8)) fprintf(f, " %c -", i-8); + else fprintf(f, "%3d-", i-8); +if (isprint(i-1)) fprintf(f, " %c ", i-1); + else fprintf(f, "%3d", i-1); +fprintf(f, " */\n\n/* End of pcre2_chartables.c */\n"); + +fclose(f); +free((void *)base_of_tables); +return 0; +} + +/* End of dftables.c */ diff --git a/ProcessHacker/pcre/pcre2.h b/ProcessHacker/pcre/pcre2.h index 757784e97254..dfbc75677a39 100644 --- a/ProcessHacker/pcre/pcre2.h +++ b/ProcessHacker/pcre/pcre2.h @@ -5,7 +5,7 @@ /* This is the public header file for the PCRE library, second API, to be #included by applications that call PCRE2 functions. - Copyright (c) 2016 University of Cambridge + Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -35,10 +35,6 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ - -#ifndef _PCRE2_H -#define _PCRE2_H - // dmex: This must match PCRE2_STATIC in config.h #define PCRE2_STATIC 1 @@ -46,12 +42,21 @@ POSSIBILITY OF SUCH DAMAGE. #undef PCRE2_CODE_UNIT_WIDTH #define PCRE2_CODE_UNIT_WIDTH 16 +#ifndef PCRE2_H_IDEMPOTENT_GUARD +#define PCRE2_H_IDEMPOTENT_GUARD + /* The current PCRE version information. */ -#define PCRE2_MAJOR 10 -#define PCRE2_MINOR 22 -#define PCRE2_PRERELEASE -#define PCRE2_DATE 2016-07-29 +#define PCRE2_MAJOR 10 +#define PCRE2_MINOR 32 +#define PCRE2_PRERELEASE +#define PCRE2_DATE 2018-09-10 + +/* For the benefit of systems without stdint.h, an alternative is to use +inttypes.h. The existence of these headers is checked by configure or CMake. */ + +#define PCRE2_HAVE_STDINT_H 1 +#define PCRE2_HAVE_INTTYPES_H 1 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE2, the appropriate @@ -74,12 +79,32 @@ don't change existing definitions of PCRE2_EXP_DECL. */ # endif #endif -/* Have to include limits.h, stdlib.h and stdint.h to ensure that size_t and -uint8_t, UCHAR_MAX, etc are defined. */ +/* When compiling with the MSVC compiler, it is sometimes necessary to include +a "calling convention" before exported function names. (This is secondhand +information; I know nothing about MSVC myself). For example, something like + + void __cdecl function(....) + +might be needed. In order so make this easy, all the exported functions have +PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not +set, we ensure here that it has no effect. */ + +#ifndef PCRE2_CALL_CONVENTION +#define PCRE2_CALL_CONVENTION +#endif + +/* Have to include limits.h, stdlib.h and stdint.h (or inttypes.h) to ensure +that size_t and uint8_t, UCHAR_MAX, etc are defined. If the system has neither +header, the relevant values must be provided by some other means. */ #include #include + +#if PCRE2_HAVE_STDINT_H #include +#elif PCRE2_HAVE_INTTYPES_H +#include +#endif /* Allow for C++ users compiling this directly. */ @@ -87,23 +112,24 @@ uint8_t, UCHAR_MAX, etc are defined. */ extern "C" { #endif -/* The following option bits can be passed to pcre2_compile(), pcre2_match(), -or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it -is passed. Put these bits at the most significant end of the options word so -others can be added next to them */ + /* The following option bits can be passed to pcre2_compile(), pcre2_match(), + or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it + is passed. Put these bits at the most significant end of the options word so + others can be added next to them */ #define PCRE2_ANCHORED 0x80000000u #define PCRE2_NO_UTF_CHECK 0x40000000u +#define PCRE2_ENDANCHORED 0x20000000u -/* The following option bits can be passed only to pcre2_compile(). However, -they may affect compilation, JIT compilation, and/or interpretive execution. -The following tags indicate which: + /* The following option bits can be passed only to pcre2_compile(). However, + they may affect compilation, JIT compilation, and/or interpretive execution. + The following tags indicate which: -C alters what is compiled by pcre2_compile() -J alters what is compiled by pcre2_jit_compile() -M is inspected during pcre2_match() execution -D is inspected during pcre2_dfa_match() execution -*/ + C alters what is compiled by pcre2_compile() + J alters what is compiled by pcre2_jit_compile() + M is inspected during pcre2_match() execution + D is inspected during pcre2_dfa_match() execution + */ #define PCRE2_ALLOW_EMPTY_CLASS 0x00000001u /* C */ #define PCRE2_ALT_BSUX 0x00000002u /* C */ @@ -129,6 +155,15 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ #define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ #define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ +#define PCRE2_EXTENDED_MORE 0x01000000u /* C */ +#define PCRE2_LITERAL 0x02000000u /* C */ + + /* An additional compile options word is available in the compile context. */ + +#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */ +#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */ +#define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */ +#define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */ /* These are for pcre2_jit_compile(). */ @@ -167,6 +202,16 @@ ignored for pcre2_jit_match(). */ #define PCRE2_NO_JIT 0x00002000u +/* Options for pcre2_pattern_convert(). */ + +#define PCRE2_CONVERT_UTF 0x00000001u +#define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u +#define PCRE2_CONVERT_POSIX_BASIC 0x00000004u +#define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u +#define PCRE2_CONVERT_GLOB 0x00000010u +#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u +#define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u + /* Newline and \R settings, for use in compile contexts. The newline values must be kept in step with values set in config.h and both sets must all be greater than zero. */ @@ -176,11 +221,112 @@ greater than zero. */ #define PCRE2_NEWLINE_CRLF 3 #define PCRE2_NEWLINE_ANY 4 #define PCRE2_NEWLINE_ANYCRLF 5 +#define PCRE2_NEWLINE_NUL 6 #define PCRE2_BSR_UNICODE 1 #define PCRE2_BSR_ANYCRLF 2 -/* Error codes: no match and partial match are "expected" errors. */ +/* Error codes for pcre2_compile(). Some of these are also used by +pcre2_pattern_convert(). */ + +#define PCRE2_ERROR_END_BACKSLASH 101 +#define PCRE2_ERROR_END_BACKSLASH_C 102 +#define PCRE2_ERROR_UNKNOWN_ESCAPE 103 +#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104 +#define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105 +#define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106 +#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107 +#define PCRE2_ERROR_CLASS_RANGE_ORDER 108 +#define PCRE2_ERROR_QUANTIFIER_INVALID 109 +#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110 +#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111 +#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112 +#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113 +#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114 +#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115 +#define PCRE2_ERROR_NULL_PATTERN 116 +#define PCRE2_ERROR_BAD_OPTIONS 117 +#define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118 +#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119 +#define PCRE2_ERROR_PATTERN_TOO_LARGE 120 +#define PCRE2_ERROR_HEAP_FAILED 121 +#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122 +#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123 +#define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124 +#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125 +#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126 +#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127 +#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128 +#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129 +#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130 +#define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131 +#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132 +#define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133 +#define PCRE2_ERROR_CODE_POINT_TOO_BIG 134 +#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135 +#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136 +#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137 +#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138 +#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139 +#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140 +#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141 +#define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142 +#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143 +#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144 +#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145 +#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146 +#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147 +#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148 +#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149 +#define PCRE2_ERROR_CLASS_INVALID_RANGE 150 +#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151 +#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152 +#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153 +#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154 +#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155 +#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156 +#define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157 +#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158 +/* Error 159 is obsolete and should now never occur */ +#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159 +#define PCRE2_ERROR_VERB_UNKNOWN 160 +#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161 +#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162 +#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163 +#define PCRE2_ERROR_INVALID_OCTAL 164 +#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165 +#define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166 +#define PCRE2_ERROR_INVALID_HEXADECIMAL 167 +#define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168 +#define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170 +#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171 +#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172 +#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173 +#define PCRE2_ERROR_UTF_IS_DISABLED 174 +#define PCRE2_ERROR_UCP_IS_DISABLED 175 +#define PCRE2_ERROR_VERB_NAME_TOO_LONG 176 +#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177 +#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178 +#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180 +#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181 +#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182 +#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183 +#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184 +#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185 +#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186 +#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187 +#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188 +#define PCRE2_ERROR_INTERNAL_BAD_CODE 189 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190 +#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191 +#define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192 +#define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE 193 +#define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS 194 + + +/* "Expected" matching error codes: no match and partial match. */ #define PCRE2_ERROR_NOMATCH (-1) #define PCRE2_ERROR_PARTIAL (-2) @@ -220,10 +366,10 @@ greater than zero. */ #define PCRE2_ERROR_UTF32_ERR1 (-27) #define PCRE2_ERROR_UTF32_ERR2 (-28) -/* Error codes for pcre2[_dfa]_match(), substring extraction functions, context -functions, and serializing functions. They are in numerical order. Originally -they were in alphabetical order too, but now that PCRE2 is released, the -numbers must not be changed. */ +/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction +functions, context functions, and serializing functions. They are in numerical +order. Originally they were in alphabetical order too, but now that PCRE2 is +released, the numbers must not be changed. */ #define PCRE2_ERROR_BADDATA (-29) #define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */ @@ -249,7 +395,8 @@ numbers must not be changed. */ #define PCRE2_ERROR_NOUNIQUESUBSTRING (-50) #define PCRE2_ERROR_NULL (-51) #define PCRE2_ERROR_RECURSELOOP (-52) -#define PCRE2_ERROR_RECURSIONLIMIT (-53) +#define PCRE2_ERROR_DEPTHLIMIT (-53) +#define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */ #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) #define PCRE2_ERROR_BADOFFSETLIMIT (-56) @@ -259,6 +406,10 @@ numbers must not be changed. */ #define PCRE2_ERROR_BADSUBSPATTERN (-60) #define PCRE2_ERROR_TOOMANYREPLACE (-61) #define PCRE2_ERROR_BADSERIALIZEDDATA (-62) +#define PCRE2_ERROR_HEAPLIMIT (-63) +#define PCRE2_ERROR_CONVERT_SYNTAX (-64) +#define PCRE2_ERROR_INTERNAL_DUPMATCH (-65) + /* Request types for pcre2_pattern_info() */ @@ -283,9 +434,13 @@ numbers must not be changed. */ #define PCRE2_INFO_NAMEENTRYSIZE 18 #define PCRE2_INFO_NAMETABLE 19 #define PCRE2_INFO_NEWLINE 20 -#define PCRE2_INFO_RECURSIONLIMIT 21 +#define PCRE2_INFO_DEPTHLIMIT 21 +#define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */ #define PCRE2_INFO_SIZE 22 #define PCRE2_INFO_HASBACKSLASHC 23 +#define PCRE2_INFO_FRAMESIZE 24 +#define PCRE2_INFO_HEAPLIMIT 25 +#define PCRE2_INFO_EXTRAOPTIONS 26 /* Request types for pcre2_config(). */ @@ -296,34 +451,39 @@ numbers must not be changed. */ #define PCRE2_CONFIG_MATCHLIMIT 4 #define PCRE2_CONFIG_NEWLINE 5 #define PCRE2_CONFIG_PARENSLIMIT 6 -#define PCRE2_CONFIG_RECURSIONLIMIT 7 -#define PCRE2_CONFIG_STACKRECURSE 8 +#define PCRE2_CONFIG_DEPTHLIMIT 7 +#define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */ +#define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */ #define PCRE2_CONFIG_UNICODE 9 #define PCRE2_CONFIG_UNICODE_VERSION 10 #define PCRE2_CONFIG_VERSION 11 +#define PCRE2_CONFIG_HEAPLIMIT 12 +#define PCRE2_CONFIG_NEVER_BACKSLASH_C 13 +#define PCRE2_CONFIG_COMPILED_WIDTHS 14 + /* Types for code units in patterns and subject strings. */ -typedef uint8_t PCRE2_UCHAR8; -typedef uint16_t PCRE2_UCHAR16; -typedef uint32_t PCRE2_UCHAR32; + typedef uint8_t PCRE2_UCHAR8; + typedef uint16_t PCRE2_UCHAR16; + typedef uint32_t PCRE2_UCHAR32; -typedef const PCRE2_UCHAR8 *PCRE2_SPTR8; -typedef const PCRE2_UCHAR16 *PCRE2_SPTR16; -typedef const PCRE2_UCHAR32 *PCRE2_SPTR32; + typedef const PCRE2_UCHAR8* PCRE2_SPTR8; + typedef const PCRE2_UCHAR16* PCRE2_SPTR16; + typedef const PCRE2_UCHAR32* PCRE2_SPTR32; -/* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2, -including pattern offsets for errors and subject offsets after a match. We -define special values to indicate zero-terminated strings and unset offsets in -the offset vector (ovector). */ + /* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2, + including pattern offsets for errors and subject offsets after a match. We + define special values to indicate zero-terminated strings and unset offsets in + the offset vector (ovector). */ #define PCRE2_SIZE size_t #define PCRE2_SIZE_MAX SIZE_MAX #define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0) #define PCRE2_UNSET (~(PCRE2_SIZE)0) -/* Generic types for opaque structures and JIT callback functions. These -declarations are defined in a macro that is expanded for each width later. */ + /* Generic types for opaque structures and JIT callback functions. These + declarations are defined in a macro that is expanded for each width later. */ #define PCRE2_TYPES_LIST \ struct pcre2_real_general_context; \ @@ -335,6 +495,9 @@ typedef struct pcre2_real_compile_context pcre2_compile_context; \ struct pcre2_real_match_context; \ typedef struct pcre2_real_match_context pcre2_match_context; \ \ +struct pcre2_real_convert_context; \ +typedef struct pcre2_real_convert_context pcre2_convert_context; \ +\ struct pcre2_real_code; \ typedef struct pcre2_real_code pcre2_code; \ \ @@ -347,11 +510,16 @@ typedef struct pcre2_real_jit_stack pcre2_jit_stack; \ typedef pcre2_jit_stack *(*pcre2_jit_callback)(void *); -/* The structure for passing out data via the pcre_callout_function. We use a -structure so that new fields can be added on the end in future versions, -without changing the API of the function, thereby allowing old clients to work -without modification. Define the generic version in a macro; the width-specific -versions are generated from this macro below. */ + /* The structure for passing out data via the pcre_callout_function. We use a + structure so that new fields can be added on the end in future versions, + without changing the API of the function, thereby allowing old clients to work + without modification. Define the generic version in a macro; the width-specific + versions are generated from this macro below. */ + + /* Flags for the callout_flags field. These are cleared after a callout. */ + +#define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */ +#define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */ #define PCRE2_STRUCTURE_LIST \ typedef struct pcre2_callout_block { \ @@ -372,6 +540,8 @@ typedef struct pcre2_callout_block { \ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ PCRE2_SPTR callout_string; /* String compiled into pattern */ \ + /* ------------------- Added for Version 2 -------------------------- */ \ + uint32_t callout_flags; /* See above for list */ \ /* ------------------------------------------------------------------ */ \ } pcre2_callout_block; \ \ @@ -393,170 +563,220 @@ expanded for each width below. Start with functions that give general information. */ #define PCRE2_GENERAL_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_config(uint32_t, void *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *); /* Functions for manipulating contexts. */ #define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_copy(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_create( \ - void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); \ -PCRE2_EXP_DECL void pcre2_general_context_free(pcre2_general_context *); +PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ + *pcre2_general_context_copy(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ + *pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \ + void (*)(void *, void *), void *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_general_context_free(pcre2_general_context *); #define PCRE2_COMPILE_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_copy(pcre2_compile_context *); \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_create(pcre2_general_context *);\ -PCRE2_EXP_DECL void pcre2_compile_context_free(pcre2_compile_context *); \ -PCRE2_EXP_DECL int pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ -PCRE2_EXP_DECL int pcre2_set_character_tables(pcre2_compile_context *, \ - const unsigned char *); \ -PCRE2_EXP_DECL int pcre2_set_max_pattern_length(pcre2_compile_context *, \ - PCRE2_SIZE); \ -PCRE2_EXP_DECL int pcre2_set_newline(pcre2_compile_context *, uint32_t); \ -PCRE2_EXP_DECL int pcre2_set_parens_nest_limit(pcre2_compile_context *, \ - uint32_t); \ -PCRE2_EXP_DECL int pcre2_set_compile_recursion_guard(\ - pcre2_compile_context *, int (*)(uint32_t, void *), \ - void *); +PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ + *pcre2_compile_context_copy(pcre2_compile_context *); \ +PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ + *pcre2_compile_context_create(pcre2_general_context *);\ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_compile_context_free(pcre2_compile_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_character_tables(pcre2_compile_context *, const unsigned char *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_newline(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_compile_recursion_guard(pcre2_compile_context *, \ + int (*)(uint32_t, void *), void *); #define PCRE2_MATCH_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_copy(pcre2_match_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_create(pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_match_context_free(pcre2_match_context *); \ -PCRE2_EXP_DECL int pcre2_set_callout(pcre2_match_context *, \ - int (*)(pcre2_callout_block *, void *), void *); \ -PCRE2_EXP_DECL int pcre2_set_match_limit(pcre2_match_context *, \ - uint32_t); \ -PCRE2_EXP_DECL int pcre2_set_offset_limit(pcre2_match_context *, \ - PCRE2_SIZE); \ -PCRE2_EXP_DECL int pcre2_set_recursion_limit(pcre2_match_context *, \ - uint32_t); \ -PCRE2_EXP_DECL int pcre2_set_recursion_memory_management( \ - pcre2_match_context *, void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); +PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ + *pcre2_match_context_copy(pcre2_match_context *); \ +PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ + *pcre2_match_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_match_context_free(pcre2_match_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_callout(pcre2_match_context *, \ + int (*)(pcre2_callout_block *, void *), void *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_match_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_recursion_memory_management(pcre2_match_context *, \ + void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *); + +#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \ +PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ + *pcre2_convert_context_copy(pcre2_convert_context *); \ +PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ + *pcre2_convert_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_convert_context_free(pcre2_convert_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_glob_separator(pcre2_convert_context *, uint32_t); /* Functions concerned with compiling a pattern to PCRE internal code. */ #define PCRE2_COMPILE_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, \ - int *, PCRE2_SIZE *, pcre2_compile_context *); \ -PCRE2_EXP_DECL void pcre2_code_free(pcre2_code *); \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_code_copy(const pcre2_code *); +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \ + pcre2_compile_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_code_free(pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_code_copy(const pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_code_copy_with_tables(const pcre2_code *); /* Functions that give information about a compiled pattern. */ #define PCRE2_PATTERN_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_pattern_info(const pcre2_code *, uint32_t, \ - void *); \ -PCRE2_EXP_DECL int pcre2_callout_enumerate(const pcre2_code *, \ - int (*)(pcre2_callout_enumerate_block *, void *), \ - void *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_callout_enumerate(const pcre2_code *, \ + int (*)(pcre2_callout_enumerate_block *, void *), void *); /* Functions for running a match and inspecting the result. */ #define PCRE2_MATCH_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create(uint32_t, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create_from_pattern(\ - const pcre2_code *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, \ - PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, int *, \ - PCRE2_SIZE); \ -PCRE2_EXP_DECL int pcre2_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_match_data_free(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SPTR pcre2_get_mark(pcre2_match_data *); \ -PCRE2_EXP_DECL uint32_t pcre2_get_ovector_count(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE *pcre2_get_ovector_pointer(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE pcre2_get_startchar(pcre2_match_data *); +PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ + *pcre2_match_data_create(uint32_t, pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ + *pcre2_match_data_create_from_pattern(const pcre2_code *, \ + pcre2_general_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_match_data_free(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \ + pcre2_get_mark(pcre2_match_data *); \ +PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \ + pcre2_get_ovector_count(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ + *pcre2_get_ovector_pointer(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ + pcre2_get_startchar(pcre2_match_data *); /* Convenience functions for handling matched substrings. */ #define PCRE2_SUBSTRING_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_substring_copy_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_copy_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL void pcre2_substring_free(PCRE2_UCHAR *); \ -PCRE2_EXP_DECL int pcre2_substring_get_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_get_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_nametable_scan(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SPTR *, PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_number_from_name(\ - const pcre2_code *, PCRE2_SPTR); \ -PCRE2_EXP_DECL void pcre2_substring_list_free(PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_list_get(pcre2_match_data *, \ - PCRE2_UCHAR ***, PCRE2_SIZE **); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_substring_free(PCRE2_UCHAR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \ + PCRE2_SPTR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_substring_list_free(PCRE2_SPTR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **); /* Functions for serializing / deserializing compiled patterns. */ #define PCRE2_SERIALIZE_FUNCTIONS \ -PCRE2_EXP_DECL int32_t pcre2_serialize_encode(const pcre2_code **, \ - int32_t, uint8_t **, PCRE2_SIZE *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_decode(pcre2_code **, int32_t, \ - const uint8_t *, pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_get_number_of_codes(const uint8_t *); \ -PCRE2_EXP_DECL void pcre2_serialize_free(uint8_t *); +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \ + PCRE2_SIZE *, pcre2_general_context *); \ +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \ + pcre2_general_context *); \ +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_get_number_of_codes(const uint8_t *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_serialize_free(uint8_t *); /* Convenience function for match + substitute. */ #define PCRE2_SUBSTITUTE_FUNCTION \ -PCRE2_EXP_DECL int pcre2_substitute(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *, \ - PCRE2_SIZE *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \ + PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *); + + +/* Functions for converting pattern source strings. */ + +#define PCRE2_CONVERT_FUNCTIONS \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \ + PCRE2_SIZE *, pcre2_convert_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_converted_pattern_free(PCRE2_UCHAR *); /* Functions for JIT processing */ #define PCRE2_JIT_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_jit_compile(pcre2_code *, uint32_t); \ -PCRE2_EXP_DECL int pcre2_jit_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_jit_free_unused_memory(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_jit_stack *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_assign(pcre2_match_context *, \ - pcre2_jit_callback, void *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_free(pcre2_jit_stack *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_jit_compile(pcre2_code *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_free_unused_memory(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_jit_stack PCRE2_CALL_CONVENTION \ + *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_free(pcre2_jit_stack *); /* Other miscellaneous functions. */ #define PCRE2_OTHER_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ -PCRE2_EXP_DECL \ - const uint8_t *pcre2_maketables(pcre2_general_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ +PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ + *pcre2_maketables(pcre2_general_context *); \ /* Define macros that generate width-specific names from generic versions. The @@ -583,6 +803,7 @@ pcre2_compile are called by application code. */ #define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_) #define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_) #define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_) +#define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_) #define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_) #define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_) #define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_) @@ -594,6 +815,7 @@ pcre2_compile are called by application code. */ #define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_) #define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_) #define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_) +#define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_) #define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_) #define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_) @@ -602,12 +824,17 @@ pcre2_compile are called by application code. */ #define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_) #define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_) +#define pcre2_code_copy_with_tables PCRE2_SUFFIX(pcre2_code_copy_with_tables_) #define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_) #define pcre2_compile PCRE2_SUFFIX(pcre2_compile_) #define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_) #define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_) #define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_) #define pcre2_config PCRE2_SUFFIX(pcre2_config_) +#define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_) +#define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_) +#define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_) +#define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_) #define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_) #define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_) #define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_) @@ -631,6 +858,7 @@ pcre2_compile are called by application code. */ #define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_) #define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_) #define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_) +#define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_) #define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_) #define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_) #define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_) @@ -639,14 +867,17 @@ pcre2_compile are called by application code. */ #define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_) #define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_) #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) +#define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) +#define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_) +#define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_) +#define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_) +#define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_) #define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_) #define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_) #define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_) #define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_) #define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_) -#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) -#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) #define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_) #define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_) #define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_) @@ -660,6 +891,11 @@ pcre2_compile are called by application code. */ #define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_) #define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_) +/* Keep this old function name for backwards compatibility */ +#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) + +/* Keep this obsolete function for backwards compatibility: it is now a noop. */ +#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) /* Now generate all three sets of width-specific structures and function prototypes. */ @@ -670,6 +906,8 @@ PCRE2_STRUCTURE_LIST \ PCRE2_GENERAL_INFO_FUNCTIONS \ PCRE2_GENERAL_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_CONTEXT_FUNCTIONS \ +PCRE2_CONVERT_CONTEXT_FUNCTIONS \ +PCRE2_CONVERT_FUNCTIONS \ PCRE2_MATCH_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_FUNCTIONS \ PCRE2_PATTERN_INFO_FUNCTIONS \ @@ -681,24 +919,25 @@ PCRE2_JIT_FUNCTIONS \ PCRE2_OTHER_FUNCTIONS #define PCRE2_LOCAL_WIDTH 8 -PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS + PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_LOCAL_WIDTH #define PCRE2_LOCAL_WIDTH 16 -PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS + PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_LOCAL_WIDTH #define PCRE2_LOCAL_WIDTH 32 -PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS + PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_LOCAL_WIDTH -/* Undefine the list macros; they are no longer needed. */ + /* Undefine the list macros; they are no longer needed. */ #undef PCRE2_TYPES_LIST #undef PCRE2_STRUCTURE_LIST #undef PCRE2_GENERAL_INFO_FUNCTIONS #undef PCRE2_GENERAL_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_CONTEXT_FUNCTIONS +#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS #undef PCRE2_MATCH_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_FUNCTIONS #undef PCRE2_PATTERN_INFO_FUNCTIONS @@ -736,4 +975,6 @@ PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ } /* extern "C" */ #endif -#endif /* End of pcre2.h */ +#endif /* PCRE2_H_IDEMPOTENT_GUARD */ + +/* End of pcre2.h */ diff --git a/ProcessHacker/pcre/pcre2_auto_possess.c b/ProcessHacker/pcre/pcre2_auto_possess.c index 24c0c2986523..2ce152e9522f 100644 --- a/ProcessHacker/pcre/pcre2_auto_possess.c +++ b/ProcessHacker/pcre/pcre2_auto_possess.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ POSSIBILITY OF SUCH DAMAGE. /* This module contains functions that scan a compiled pattern and change repeats into possessive repeats where possible. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -505,7 +505,7 @@ which case the base cannot be possessified. utf TRUE in UTF mode cb compile data block base_list the data list of the base opcode - base_end the end of the data list + base_end the end of the base opcode rec_limit points to recursion depth counter Returns: TRUE if the auto-possessification is possible @@ -558,54 +558,82 @@ for(;;) continue; } + /* At the end of a branch, skip to the end of the group. */ + if (c == OP_ALT) { do code += GET(code, 1); while (*code == OP_ALT); c = *code; } + /* Inspect the next opcode. */ + switch(c) { - case OP_END: - case OP_KETRPOS: - /* TRUE only in greedy case. The non-greedy case could be replaced by - an OP_EXACT, but it is probably not worth it. (And note that OP_EXACT - uses more memory, which we cannot get at this stage.) */ + /* We can always possessify a greedy iterator at the end of the pattern, + which is reached after skipping over the final OP_KET. A non-greedy + iterator must never be possessified. */ + case OP_END: return base_list[1] != 0; + /* When an iterator is at the end of certain kinds of group we can inspect + what follows the group by skipping over the closing ket. Note that this + does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given + iteration is variable (could be another iteration or could be the next + item). As these two opcodes are not listed in the next switch, they will + end up as the next code to inspect, and return FALSE by virtue of being + unsupported. */ + case OP_KET: - /* If the bracket is capturing, and referenced by an OP_RECURSE, or - it is an atomic sub-pattern (assert, once, etc.) the non-greedy case - cannot be converted to a possessive form. */ + case OP_KETRPOS: + /* The non-greedy case cannot be converted to a possessive form. */ if (base_list[1] == 0) return FALSE; + /* If the bracket is capturing it might be referenced by an OP_RECURSE + so its last iterator can never be possessified if the pattern contains + recursions. (This could be improved by keeping a list of group numbers that + are called by recursion.) */ + switch(*(code - GET(code, 1))) { + case OP_CBRA: + case OP_SCBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + if (cb->had_recurse) return FALSE; + break; + + /* Atomic sub-patterns and assertions can always auto-possessify their + last iterator. However, if the group was entered as a result of checking + a previous iterator, this is not possible. */ + case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: - /* Atomic sub-patterns and assertions can always auto-possessify their - last iterator. However, if the group was entered as a result of checking - a previous iterator, this is not possible. */ return !entered_a_group; } + /* Skip over the bracket and inspect what comes next. */ + code += PRIV(OP_lengths)[c]; continue; + /* Handle cases where the next item is a group. */ + case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: next_code = code + GET(code, 1); code += PRIV(OP_lengths)[c]; + /* Check each branch. We have to recurse a level for all but the last + branch. */ + while (*next_code == OP_ALT) { if (!compare_opcodes(code, utf, cb, base_list, base_end, rec_limit)) @@ -621,8 +649,8 @@ for(;;) case OP_BRAMINZERO: next_code = code + 1; - if (*next_code != OP_BRA && *next_code != OP_CBRA - && *next_code != OP_ONCE && *next_code != OP_ONCE_NC) return FALSE; + if (*next_code != OP_BRA && *next_code != OP_CBRA && + *next_code != OP_ONCE) return FALSE; do next_code += GET(next_code, 1); while (*next_code == OP_ALT); @@ -635,11 +663,15 @@ for(;;) code += PRIV(OP_lengths)[c]; continue; + /* The next opcode does not need special handling; fall through and use it + to see if the base can be possessified. */ + default: break; } - /* Check for a supported opcode, and load its properties. */ + /* We now have the next appropriate opcode to compare with the base. Check + for a supported opcode, and load its properties. */ code = get_chr_property_list(code, utf, cb->fcc, list); if (code == NULL) return FALSE; /* Unsupported */ @@ -698,7 +730,7 @@ for(;;) if ((*xclass_flags & XCL_MAP) == 0) { /* No bits are set for characters < 256. */ - if (list[1] == 0) return TRUE; + if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0; /* Might be an empty repeat. */ continue; } @@ -1046,8 +1078,10 @@ but some compilers complain about an unreachable statement. */ /* Replaces single character iterations with their possessive alternatives if appropriate. This function modifies the compiled opcode! Hitting a -non-existant opcode may indicate a bug in PCRE2, but it can also be caused if a -bad UTF string was compiled with PCRE2_NO_UTF_CHECK. +non-existent opcode may indicate a bug in PCRE2, but it can also be caused if a +bad UTF string was compiled with PCRE2_NO_UTF_CHECK. The rec_limit catches +overly complicated or large patterns. In these cases, the check just stops, +leaving the remainder of the pattern unpossessified. Arguments: code points to start of the byte code @@ -1061,17 +1095,17 @@ Returns: 0 for success int PRIV(auto_possessify)(PCRE2_UCHAR *code, BOOL utf, const compile_block *cb) { -register PCRE2_UCHAR c; +PCRE2_UCHAR c; PCRE2_SPTR end; PCRE2_UCHAR *repeat_opcode; uint32_t list[8]; -int rec_limit; +int rec_limit = 1000; /* Was 10,000 but clang+ASAN uses a lot of stack. */ for (;;) { c = *code; - if (c > OP_TABLE_LENGTH) return -1; /* Something gone wrong */ + if (c >= OP_TABLE_LENGTH) return -1; /* Something gone wrong */ if (c >= OP_STAR && c <= OP_TYPEPOSUPTO) { @@ -1080,7 +1114,6 @@ for (;;) get_chr_property_list(code, utf, cb->fcc, list) : NULL; list[1] = c == OP_STAR || c == OP_PLUS || c == OP_QUERY || c == OP_UPTO; - rec_limit = 1000; if (end != NULL && compare_opcodes(end, utf, cb, list, end, &rec_limit)) { switch(c) @@ -1137,7 +1170,6 @@ for (;;) list[1] = (c & 1) == 0; - rec_limit = 1000; if (compare_opcodes(end, utf, cb, list, end, &rec_limit)) { switch (c) @@ -1203,6 +1235,7 @@ for (;;) #endif case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/ProcessHacker/pcre/pcre2_chartables.c b/ProcessHacker/pcre/pcre2_chartables.c index 23578d8932b0..4046500c00d0 100644 --- a/ProcessHacker/pcre/pcre2_chartables.c +++ b/ProcessHacker/pcre/pcre2_chartables.c @@ -2,25 +2,25 @@ * Perl-Compatible Regular Expressions * *************************************************/ -/* This file contains character tables that are used when no external tables -are passed to PCRE2 by the application that calls it. The tables are used only -for characters whose code values are less than 256. - -This is a default version of the tables that assumes ASCII encoding. A program -called dftables (which is distributed with PCRE2) can be used to build -alternative versions of this file. This is necessary if you are running in an -EBCDIC environment, or if you want to default to a different encoding, for -example ISO-8859-1. When dftables is run, it creates these tables in the -current locale. If PCRE2 is configured with --enable-rebuild-chartables, this -happens automatically. - -The following #includes are present because without them gcc 4.x may remove the -array definition from the final binary if PCRE2 is built into a static library -and dead code stripping is activated. This leads to link errors. Pulling in the -header ensures that the array gets flagged as "someone outside this compilation -unit might reference this" and so it will always be supplied to the linker. */ - -#define HAVE_CONFIG_H +/* This file was automatically written by the dftables auxiliary +program. It contains character tables that are used when no external +tables are passed to PCRE2 by the application that calls it. The tables +are used only for characters whose code values are less than 256. */ + +/*The dftables program (which is distributed with PCRE2) can be used to +build alternative versions of this file. This is necessary if you are +running in an EBCDIC environment, or if you want to default to a different +encoding, for example ISO-8859-1. When dftables is run, it creates these +tables in the current locale. This happens automatically if PCRE2 is +configured with --enable-rebuild-chartables. */ + +/* The following #include is present because without it gcc 4.x may remove +the array definition from the final binary if PCRE2 is built into a static +library and dead code stripping is activated. This leads to link errors. +Pulling in the header ensures that the array gets flagged as "someone +outside this compilation unit might reference this" and so it will always +be supplied to the linker. */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -102,7 +102,7 @@ const uint8_t PRIV(default_tables)[] = { /* This table contains bit maps for various character classes. Each map is 32 bytes long and the bits run from the least significant end of each byte. The classes that have their own maps are: space, xdigit, digit, upper, lower, word, -graph, print, punct, and cntrl. Other classes are built from combinations. */ +graph print, punct, and cntrl. Other classes are built from combinations. */ 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -160,25 +160,24 @@ graph, print, punct, and cntrl. Other classes are built from combinations. */ 0x04 decimal digit 0x08 hexadecimal digit 0x10 alphanumeric or '_' - 0x80 regular expression metacharacter or binary zero */ - 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ - 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ - 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ - 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ - 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x10, /* X - _ */ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ - 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x00, /* x -127 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ diff --git a/ProcessHacker/pcre/pcre2_compile.c b/ProcessHacker/pcre/pcre2_compile.c index 0b93e22c4b23..6bb1de361001 100644 --- a/ProcessHacker/pcre/pcre2_compile.c +++ b/ProcessHacker/pcre/pcre2_compile.c @@ -1,9087 +1,9921 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* PCRE is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. - - Written by Philip Hazel - Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - -// dmex: Disable warnings -#pragma warning(push) -#pragma warning(disable : 4244 4267) - -#define HAVE_CONFIG_H -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define NLBLOCK cb /* Block containing newline information */ -#define PSSTART start_pattern /* Field containing processed string start */ -#define PSEND end_pattern /* Field containing processed string end */ - -#include "pcre2_internal.h" - -/* In rare error cases debugging might require calling pcre2_printint(). */ - -#if 0 -#ifdef EBCDIC -#define PRINTABLE(c) ((c) >= 64 && (c) < 255) -#else -#define PRINTABLE(c) ((c) >= 32 && (c) < 127) -#endif -#include "pcre2_printint.c" -#define CALL_PRINTINT -#endif - -/* There are a few things that vary with different code unit sizes. Handle them -by defining macros in order to minimize #if usage. */ - -#if PCRE2_CODE_UNIT_WIDTH == 8 -#define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5 -#define XDIGIT(c) xdigitab[c] - -#else /* Either 16-bit or 32-bit */ -#define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff) - -#if PCRE2_CODE_UNIT_WIDTH == 16 -#define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6 - -#else /* 32-bit */ -#define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6 -#endif -#endif - -/* Function definitions to allow mutual recursion */ - -static unsigned int - add_list_to_class(uint8_t *, PCRE2_UCHAR **, uint32_t, compile_block *, - const uint32_t *, unsigned int); - -static BOOL - compile_regex(uint32_t, PCRE2_UCHAR **, PCRE2_SPTR *, int *, BOOL, BOOL, - uint32_t, int, uint32_t *, int32_t *, uint32_t *, int32_t *, - branch_chain *, compile_block *, size_t *); - - - -/************************************************* -* Code parameters and static tables * -*************************************************/ - -/* This value specifies the size of stack workspace, which is used in different -ways in the different pattern scans. The group-identifying pre-scan uses it to -handle nesting, and needs it to be 16-bit aligned. - -During the first compiling phase, when determining how much memory is required, -the regex is partly compiled into this space, but the compiled parts are -discarded as soon as they can be, so that hopefully there will never be an -overrun. The code does, however, check for an overrun, which can occur for -pathological patterns. The size of the workspace depends on LINK_SIZE because -the length of compiled items varies with this. - -In the real compile phase, the workspace is used for remembering data about -numbered groups, provided there are not too many of them (if there are, extra -memory is acquired). For this phase the memory must be 32-bit aligned. Having -defined the size in code units, we set up C32_WORK_SIZE as the number of -elements in the 32-bit vector. */ - -#define COMPILE_WORK_SIZE (2048*LINK_SIZE) /* Size in code units */ - -#define C32_WORK_SIZE \ - ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint32_t)) - -/* The overrun tests check for a slightly smaller size so that they detect the -overrun before it actually does run off the end of the data block. */ - -#define WORK_SIZE_SAFETY_MARGIN (100) - -/* This value determines the size of the initial vector that is used for -remembering named groups during the pre-compile. It is allocated on the stack, -but if it is too small, it is expanded, in a similar way to the workspace. The -value is the number of slots in the list. */ - -#define NAMED_GROUP_LIST_SIZE 20 - -/* The original PCRE required patterns to be zero-terminated, and it simplifies -the compiling code if it is guaranteed that there is a zero code unit at the -end of the pattern, because this means that tests for coding sequences such as -(*SKIP) or even just (?<= can check a sequence of code units without having to -keep checking for the end of the pattern. The new PCRE2 API allows zero code -units within patterns if a positive length is given, but in order to keep most -of the compiling code as it was, we copy such patterns and add a zero on the -end. This value determines the size of space on the stack that is used if the -pattern fits; if not, heap memory is used. */ - -#define COPIED_PATTERN_SIZE 1024 - -/* Maximum length value to check against when making sure that the variable -that holds the compiled pattern length does not overflow. We make it a bit less -than INT_MAX to allow for adding in group terminating bytes, so that we don't -have to check them every time. */ - -#define OFLOW_MAX (INT_MAX - 20) - -/* Macro for setting individual bits in class bitmaps. It took some -experimenting to figure out how to stop gcc 5.3.0 from warning with --Wconversion. This version gets a warning: - - #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1 << ((b)&7)) - -Let's hope the apparently less efficient version isn't actually so bad if the -compiler is clever with identical subexpressions. */ - -#define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1 << ((b)&7))) - -/* Private flags added to firstcu and reqcu. */ - -#define REQ_CASELESS (1 << 0) /* Indicates caselessness */ -#define REQ_VARY (1 << 1) /* reqcu followed non-literal item */ -/* Negative values for the firstcu and reqcu flags */ -#define REQ_UNSET (-2) /* Not yet found anything */ -#define REQ_NONE (-1) /* Found not fixed char */ - -/* These flags are used in the groupinfo vector. */ - -#define GI_SET_COULD_BE_EMPTY 0x80000000u -#define GI_COULD_BE_EMPTY 0x40000000u -#define GI_NOT_FIXED_LENGTH 0x20000000u -#define GI_SET_FIXED_LENGTH 0x10000000u -#define GI_FIXED_LENGTH_MASK 0x0000ffffu - -/* This bit (which is greater than any UTF value) is used to indicate that a -variable contains a number of code units instead of an actual code point. */ - -#define UTF_LENGTH 0x10000000l - -/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC -and is fast (a good compiler can turn it into a subtraction and unsigned -comparison). */ - -#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) - -/* Table to identify hex digits. The tables in chartables are dependent on the -locale, and may mark arbitrary characters as digits. We want to recognize only -0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It -costs 256 bytes, but it is a lot faster than doing character value tests (at -least in some simple cases I timed), and in some applications one wants PCRE to -compile efficiently as well as match efficiently. The value in the table is -the binary hex digit value, or 0xff for non-hex digits. */ - -/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in -UTF-8 mode. */ - -#ifndef EBCDIC -static const uint8_t xdigitab[] = - { - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */ - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */ - 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */ - -#else - -/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ - -static const uint8_t xdigitab[] = - { - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */ - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */ - 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */ -#endif /* EBCDIC */ - - -/* Table for handling alphanumeric escaped characters. Positive returns are -simple data values; negative values are for special things like \d and so on. -Zero means further processing is needed (for things like \x), or the escape is -invalid. */ - -/* This is the "normal" table for ASCII systems or for EBCDIC systems running -in UTF-8 mode. It runs from '0' to 'z'. */ - -#ifndef EBCDIC -#define ESCAPES_FIRST CHAR_0 -#define ESCAPES_LAST CHAR_z -#define UPPER_CASE(c) (c-32) - -static const short int escapes[] = { - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - CHAR_COLON, CHAR_SEMICOLON, - CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, - CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, - CHAR_COMMERCIAL_AT, -ESC_A, - -ESC_B, -ESC_C, - -ESC_D, -ESC_E, - 0, -ESC_G, - -ESC_H, 0, - 0, -ESC_K, - 0, 0, - -ESC_N, 0, - -ESC_P, -ESC_Q, - -ESC_R, -ESC_S, - 0, 0, - -ESC_V, -ESC_W, - -ESC_X, 0, - -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, - CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, - CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, - CHAR_GRAVE_ACCENT, ESC_a, - -ESC_b, 0, - -ESC_d, ESC_e, - ESC_f, 0, - -ESC_h, 0, - 0, -ESC_k, - 0, 0, - ESC_n, 0, - -ESC_p, 0, - ESC_r, -ESC_s, - ESC_tee, 0, - -ESC_v, -ESC_w, - 0, 0, - -ESC_z -}; - -#else - -/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. -It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code -is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a -because it is defined as 'a', which of course picks up the ASCII value. */ - -#if 'a' == 0x81 /* Check for a real EBCDIC environment */ -#define ESCAPES_FIRST CHAR_a -#define ESCAPES_LAST CHAR_9 -#define UPPER_CASE(c) (c+64) -#else /* Testing in an ASCII environment */ -#define ESCAPES_FIRST ((unsigned char)'\x81') /* EBCDIC 'a' */ -#define ESCAPES_LAST ((unsigned char)'\xf9') /* EBCDIC '9' */ -#define UPPER_CASE(c) (c-32) -#endif - -static const short int escapes[] = { -/* 80 */ ESC_a, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, -/* 88 */-ESC_h, 0, 0, '{', 0, 0, 0, 0, -/* 90 */ 0, 0, -ESC_k, 0, 0, ESC_n, 0, -ESC_p, -/* 98 */ 0, ESC_r, 0, '}', 0, 0, 0, 0, -/* A0 */ 0, '~', -ESC_s, ESC_tee, 0,-ESC_v, -ESC_w, 0, -/* A8 */ 0,-ESC_z, 0, 0, 0, '[', 0, 0, -/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, -/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', -/* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G, -/* C8 */-ESC_H, 0, 0, 0, 0, 0, 0, 0, -/* D0 */ '}', 0, -ESC_K, 0, 0,-ESC_N, 0, -ESC_P, -/* D8 */-ESC_Q,-ESC_R, 0, 0, 0, 0, 0, 0, -/* E0 */ '\\', 0, -ESC_S, 0, 0,-ESC_V, -ESC_W, -ESC_X, -/* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0, -/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, -/* F8 */ 0, 0 -}; - -/* We also need a table of characters that may follow \c in an EBCDIC -environment for characters 0-31. */ - -static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; - -#endif /* EBCDIC */ - - -/* Table of special "verbs" like (*PRUNE). This is a short table, so it is -searched linearly. Put all the names into a single string, in order to reduce -the number of relocations when a shared library is dynamically linked. The -string is built from string macros so that it works in UTF-8 mode on EBCDIC -platforms. */ - -typedef struct verbitem { - int len; /* Length of verb name */ - int op; /* Op when no arg, or -1 if arg mandatory */ - int op_arg; /* Op when arg present, or -1 if not allowed */ -} verbitem; - -static const char verbnames[] = - "\0" /* Empty name is a shorthand for MARK */ - STRING_MARK0 - STRING_ACCEPT0 - STRING_COMMIT0 - STRING_F0 - STRING_FAIL0 - STRING_PRUNE0 - STRING_SKIP0 - STRING_THEN; - -static const verbitem verbs[] = { - { 0, -1, OP_MARK }, - { 4, -1, OP_MARK }, - { 6, OP_ACCEPT, -1 }, - { 6, OP_COMMIT, -1 }, - { 1, OP_FAIL, -1 }, - { 4, OP_FAIL, -1 }, - { 5, OP_PRUNE, OP_PRUNE_ARG }, - { 4, OP_SKIP, OP_SKIP_ARG }, - { 4, OP_THEN, OP_THEN_ARG } -}; - -static const int verbcount = sizeof(verbs)/sizeof(verbitem); - - -/* Substitutes for [[:<:]] and [[:>:]], which mean start and end of word in -another regex library. */ - -static const PCRE2_UCHAR sub_start_of_word[] = { - CHAR_BACKSLASH, CHAR_b, CHAR_LEFT_PARENTHESIS, CHAR_QUESTION_MARK, - CHAR_EQUALS_SIGN, CHAR_BACKSLASH, CHAR_w, CHAR_RIGHT_PARENTHESIS, '\0' }; - -static const PCRE2_UCHAR sub_end_of_word[] = { - CHAR_BACKSLASH, CHAR_b, CHAR_LEFT_PARENTHESIS, CHAR_QUESTION_MARK, - CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, CHAR_BACKSLASH, CHAR_w, - CHAR_RIGHT_PARENTHESIS, '\0' }; - - -/* Tables of names of POSIX character classes and their lengths. The names are -now all in a single string, to reduce the number of relocations when a shared -library is dynamically loaded. The list of lengths is terminated by a zero -length entry. The first three must be alpha, lower, upper, as this is assumed -for handling case independence. The indices for graph, print, and punct are -needed, so identify them. */ - -static const char posix_names[] = - STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 - STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 - STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 - STRING_word0 STRING_xdigit; - -static const uint8_t posix_name_lengths[] = { - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; - -#define PC_GRAPH 8 -#define PC_PRINT 9 -#define PC_PUNCT 10 - - -/* Table of class bit maps for each POSIX class. Each class is formed from a -base map, with an optional addition or removal of another map. Then, for some -classes, there is some additional tweaking: for [:blank:] the vertical space -characters are removed, and for [:alpha:] and [:alnum:] the underscore -character is removed. The triples in the table consist of the base map offset, -second map offset or -1 if no second map, and a non-negative value for map -addition or a negative value for map subtraction (if there are two maps). The -absolute value of the third field has these meanings: 0 => no tweaking, 1 => -remove vertical space characters, 2 => remove underscore. */ - -static const int posix_class_maps[] = { - cbit_word, cbit_digit, -2, /* alpha */ - cbit_lower, -1, 0, /* lower */ - cbit_upper, -1, 0, /* upper */ - cbit_word, -1, 2, /* alnum - word without underscore */ - cbit_print, cbit_cntrl, 0, /* ascii */ - cbit_space, -1, 1, /* blank - a GNU extension */ - cbit_cntrl, -1, 0, /* cntrl */ - cbit_digit, -1, 0, /* digit */ - cbit_graph, -1, 0, /* graph */ - cbit_print, -1, 0, /* print */ - cbit_punct, -1, 0, /* punct */ - cbit_space, -1, 0, /* space */ - cbit_word, -1, 0, /* word - a Perl extension */ - cbit_xdigit,-1, 0 /* xdigit */ -}; - -/* Table of substitutes for \d etc when PCRE2_UCP is set. They are replaced by -Unicode property escapes. */ - -#ifdef SUPPORT_UNICODE -static const PCRE2_UCHAR string_PNd[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pNd[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PXsp[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pXsp[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PXwd[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pXwd[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; - -static PCRE2_SPTR substitutes[] = { - string_PNd, /* \D */ - string_pNd, /* \d */ - string_PXsp, /* \S */ /* Xsp is Perl space, but from 8.34, Perl */ - string_pXsp, /* \s */ /* space and POSIX space are the same. */ - string_PXwd, /* \W */ - string_pXwd /* \w */ -}; - -/* The POSIX class substitutes must be in the order of the POSIX class names, -defined above, and there are both positive and negative cases. NULL means no -general substitute of a Unicode property escape (\p or \P). However, for some -POSIX classes (e.g. graph, print, punct) a special property code is compiled -directly. */ - -static const PCRE2_UCHAR string_pCc[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_C, CHAR_c, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pL[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pLl[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pLu[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pXan[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_h[] = { - CHAR_BACKSLASH, CHAR_h, '\0' }; -static const PCRE2_UCHAR string_pXps[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PCc[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_C, CHAR_c, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PL[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PLl[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PLu[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PXan[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_H[] = { - CHAR_BACKSLASH, CHAR_H, '\0' }; -static const PCRE2_UCHAR string_PXps[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; - -static PCRE2_SPTR posix_substitutes[] = { - string_pL, /* alpha */ - string_pLl, /* lower */ - string_pLu, /* upper */ - string_pXan, /* alnum */ - NULL, /* ascii */ - string_h, /* blank */ - string_pCc, /* cntrl */ - string_pNd, /* digit */ - NULL, /* graph */ - NULL, /* print */ - NULL, /* punct */ - string_pXps, /* space */ /* Xps is POSIX space, but from 8.34 */ - string_pXwd, /* word */ /* Perl and POSIX space are the same */ - NULL, /* xdigit */ - /* Negated cases */ - string_PL, /* ^alpha */ - string_PLl, /* ^lower */ - string_PLu, /* ^upper */ - string_PXan, /* ^alnum */ - NULL, /* ^ascii */ - string_H, /* ^blank */ - string_PCc, /* ^cntrl */ - string_PNd, /* ^digit */ - NULL, /* ^graph */ - NULL, /* ^print */ - NULL, /* ^punct */ - string_PXps, /* ^space */ /* Xps is POSIX space, but from 8.34 */ - string_PXwd, /* ^word */ /* Perl and POSIX space are the same */ - NULL /* ^xdigit */ -}; -#define POSIX_SUBSIZE (sizeof(posix_substitutes) / sizeof(PCRE2_UCHAR *)) -#endif /* SUPPORT_UNICODE */ - -/* Masks for checking option settings. */ - -#define PUBLIC_COMPILE_OPTIONS \ - (PCRE2_ANCHORED|PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ - PCRE2_ALT_VERBNAMES|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_DOLLAR_ENDONLY| \ - PCRE2_DOTALL|PCRE2_DUPNAMES|PCRE2_EXTENDED|PCRE2_FIRSTLINE| \ - PCRE2_MATCH_UNSET_BACKREF|PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C| \ - PCRE2_NEVER_UCP|PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE| \ - PCRE2_NO_AUTO_POSSESS|PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_NO_START_OPTIMIZE| \ - PCRE2_NO_UTF_CHECK|PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_USE_OFFSET_LIMIT| \ - PCRE2_UTF) - -/* Compile time error code numbers. They are given names so that they can more -easily be tracked. When a new number is added, the tables called eint1 and -eint2 in pcre2posix.c may need to be updated, and a new error text must be -added to compile_error_texts in pcre2_error.c. */ - -enum { ERR0 = COMPILE_ERROR_BASE, - ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10, - ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20, - ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30, - ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40, - ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50, - ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60, - ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70, - ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80, - ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88 }; - -/* Error codes that correspond to negative error codes returned by -find_fixedlength(). */ - -static int fixed_length_errors[] = - { - ERR0, /* Not an error */ - ERR0, /* Not an error; -1 is used for "process later" */ - ERR25, /* Lookbehind is not fixed length */ - ERR36, /* \C in lookbehind is not allowed */ - ERR87, /* Lookbehind is too long */ - ERR86, /* Pattern too complicated */ - ERR70 /* Internal error: unknown opcode encountered */ - }; - -/* This is a table of start-of-pattern options such as (*UTF) and settings such -as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward -compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is -generic and always supported. */ - -enum { PSO_OPT, /* Value is an option bit */ - PSO_FLG, /* Value is a flag bit */ - PSO_NL, /* Value is a newline type */ - PSO_BSR, /* Value is a \R type */ - PSO_LIMM, /* Read integer value for match limit */ - PSO_LIMR }; /* Read integer value for recursion limit */ - -typedef struct pso { - const uint8_t *name; - uint16_t length; - uint16_t type; - uint32_t value; -} pso; - -/* NB: STRING_UTFn_RIGHTPAR contains the length as well */ - -static pso pso_list[] = { - { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF }, - { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF }, - { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP }, - { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET }, - { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET }, - { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS }, - { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR }, - { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT }, - { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE }, - { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 }, - { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMR, 0 }, - { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR }, - { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF }, - { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF }, - { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY }, - { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF }, - { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF }, - { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE } -}; - -/* This table is used when converting repeating opcodes into possessified -versions as a result of an explicit possessive quantifier such as ++. A zero -value means there is no possessified version - in those cases the item in -question must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT -because all relevant opcodes are less than that. */ - -static const uint8_t opcode_possessify[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ - - 0, /* NOTI */ - OP_POSSTAR, 0, /* STAR, MINSTAR */ - OP_POSPLUS, 0, /* PLUS, MINPLUS */ - OP_POSQUERY, 0, /* QUERY, MINQUERY */ - OP_POSUPTO, 0, /* UPTO, MINUPTO */ - 0, /* EXACT */ - 0, 0, 0, 0, /* POS{STAR,PLUS,QUERY,UPTO} */ - - OP_POSSTARI, 0, /* STARI, MINSTARI */ - OP_POSPLUSI, 0, /* PLUSI, MINPLUSI */ - OP_POSQUERYI, 0, /* QUERYI, MINQUERYI */ - OP_POSUPTOI, 0, /* UPTOI, MINUPTOI */ - 0, /* EXACTI */ - 0, 0, 0, 0, /* POS{STARI,PLUSI,QUERYI,UPTOI} */ - - OP_NOTPOSSTAR, 0, /* NOTSTAR, NOTMINSTAR */ - OP_NOTPOSPLUS, 0, /* NOTPLUS, NOTMINPLUS */ - OP_NOTPOSQUERY, 0, /* NOTQUERY, NOTMINQUERY */ - OP_NOTPOSUPTO, 0, /* NOTUPTO, NOTMINUPTO */ - 0, /* NOTEXACT */ - 0, 0, 0, 0, /* NOTPOS{STAR,PLUS,QUERY,UPTO} */ - - OP_NOTPOSSTARI, 0, /* NOTSTARI, NOTMINSTARI */ - OP_NOTPOSPLUSI, 0, /* NOTPLUSI, NOTMINPLUSI */ - OP_NOTPOSQUERYI, 0, /* NOTQUERYI, NOTMINQUERYI */ - OP_NOTPOSUPTOI, 0, /* NOTUPTOI, NOTMINUPTOI */ - 0, /* NOTEXACTI */ - 0, 0, 0, 0, /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */ - - OP_TYPEPOSSTAR, 0, /* TYPESTAR, TYPEMINSTAR */ - OP_TYPEPOSPLUS, 0, /* TYPEPLUS, TYPEMINPLUS */ - OP_TYPEPOSQUERY, 0, /* TYPEQUERY, TYPEMINQUERY */ - OP_TYPEPOSUPTO, 0, /* TYPEUPTO, TYPEMINUPTO */ - 0, /* TYPEEXACT */ - 0, 0, 0, 0, /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */ - - OP_CRPOSSTAR, 0, /* CRSTAR, CRMINSTAR */ - OP_CRPOSPLUS, 0, /* CRPLUS, CRMINPLUS */ - OP_CRPOSQUERY, 0, /* CRQUERY, CRMINQUERY */ - OP_CRPOSRANGE, 0, /* CRRANGE, CRMINRANGE */ - 0, 0, 0, 0, /* CRPOS{STAR,PLUS,QUERY,RANGE} */ - - 0, 0, 0, /* CLASS, NCLASS, XCLASS */ - 0, 0, /* REF, REFI */ - 0, 0, /* DNREF, DNREFI */ - 0, 0 /* RECURSE, CALLOUT */ -}; - - - -/************************************************* -* Copy compiled code * -*************************************************/ - -/* Compiled JIT code cannot be copied, so the new compiled block has no -associated JIT data. */ - -PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION -pcre2_code_copy(const pcre2_code *code) -{ -PCRE2_SIZE* ref_count; -pcre2_code *newcode; - -if (code == NULL) return NULL; -newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); -if (newcode == NULL) return NULL; -memcpy(newcode, code, code->blocksize); -newcode->executable_jit = NULL; - -/* If the code is one that has been deserialized, increment the reference count -in the decoded tables. */ - -if ((code->flags & PCRE2_DEREF_TABLES) != 0) - { - ref_count = (PCRE2_SIZE *)(code->tables + tables_length); - (*ref_count)++; - } - -return newcode; -} - - - -/************************************************* -* Free compiled code * -*************************************************/ - -PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION -pcre2_code_free(pcre2_code *code) -{ -PCRE2_SIZE* ref_count; - -if (code != NULL) - { - if (code->executable_jit != NULL) - PRIV(jit_free)(code->executable_jit, &code->memctl); - - if ((code->flags & PCRE2_DEREF_TABLES) != 0) - { - /* Decoded tables belong to the codes after deserialization, and they must - be freed when there are no more reference to them. The *ref_count should - always be > 0. */ - - ref_count = (PCRE2_SIZE *)(code->tables + tables_length); - if (*ref_count > 0) - { - (*ref_count)--; - if (*ref_count == 0) - code->memctl.free((void *)code->tables, code->memctl.memory_data); - } - } - - code->memctl.free(code, code->memctl.memory_data); - } -} - - - -/************************************************* -* Insert an automatic callout point * -*************************************************/ - -/* This function is called when the PCRE2_AUTO_CALLOUT option is set, to insert -callout points before each pattern item. - -Arguments: - code current code pointer - ptr current pattern pointer - cb general compile-time data - -Returns: new code pointer -*/ - -static PCRE2_UCHAR * -auto_callout(PCRE2_UCHAR *code, PCRE2_SPTR ptr, compile_block *cb) -{ -code[0] = OP_CALLOUT; -PUT(code, 1, ptr - cb->start_pattern); /* Pattern offset */ -PUT(code, 1 + LINK_SIZE, 0); /* Default length */ -code[1 + 2*LINK_SIZE] = 255; -return code + PRIV(OP_lengths)[OP_CALLOUT]; -} - - - -/************************************************* -* Complete a callout item * -*************************************************/ - -/* A callout item contains the length of the next item in the pattern, which -we can't fill in till after we have reached the relevant point. This is used -for both automatic and manual callouts. - -Arguments: - previous_callout points to previous callout item - ptr current pattern pointer - cb general compile-time data - -Returns: nothing -*/ - -static void -complete_callout(PCRE2_UCHAR *previous_callout, PCRE2_SPTR ptr, - compile_block *cb) -{ -size_t length = (size_t)(ptr - cb->start_pattern - GET(previous_callout, 1)); -PUT(previous_callout, 1 + LINK_SIZE, length); -} - - - -/************************************************* -* Find the fixed length of a branch * -*************************************************/ - -/* Scan a branch and compute the fixed length of subject that will match it, if -the length is fixed. This is needed for dealing with lookbehind assertions. In -UTF mode, the result is in code units rather than bytes. The branch is -temporarily terminated with OP_END when this function is called. - -This function is called when a lookbehind assertion is encountered, so that if -it fails, the error message can point to the correct place in the pattern. -However, we cannot do this when the assertion contains subroutine calls, -because they can be forward references. We solve this by remembering this case -and doing the check at the end; a flag specifies which mode we are running in. - -Lookbehind lengths are held in 16-bit fields and the maximum value is defined -as LOOKBEHIND_MAX. - -Arguments: - code points to the start of the pattern (the bracket) - utf TRUE in UTF mode - atend TRUE if called when the pattern is complete - cb the "compile data" structure - recurses chain of recurse_check to catch mutual recursion - countptr pointer to counter, to catch over-complexity - -Returns: if non-negative, the fixed length, - or -1 if an OP_RECURSE item was encountered and atend is FALSE - or -2 if there is no fixed length, - or -3 if \C was encountered (in UTF mode only) - or -4 if length is too long - or -5 if regex is too complicated - or -6 if an unknown opcode was encountered (internal error) -*/ - -#define FFL_LATER (-1) -#define FFL_NOTFIXED (-2) -#define FFL_BACKSLASHC (-3) -#define FFL_TOOLONG (-4) -#define FFL_TOOCOMPLICATED (-5) -#define FFL_UNKNOWNOP (-6) - -static int -find_fixedlength(PCRE2_UCHAR *code, BOOL utf, BOOL atend, compile_block *cb, - recurse_check *recurses, int *countptr) -{ -uint32_t length = 0xffffffffu; /* Unset */ -uint32_t group = 0; -uint32_t groupinfo = 0; -recurse_check this_recurse; -register uint32_t branchlength = 0; -register PCRE2_UCHAR *cc = code + 1 + LINK_SIZE; - -/* If this is a capturing group, we may have the answer cached, but we can only -use this information if there are no (?| groups in the pattern, because -otherwise group numbers are not unique. */ - -if (*code == OP_CBRA || *code == OP_CBRAPOS || *code == OP_SCBRA || - *code == OP_SCBRAPOS) - { - group = GET2(cc, 0); - cc += IMM2_SIZE; - groupinfo = cb->groupinfo[group]; - if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0) - { - if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return FFL_NOTFIXED; - if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) - return groupinfo & GI_FIXED_LENGTH_MASK; - } - } - -/* A large and/or complex regex can take too long to process. This can happen -more often when (?| groups are present in the pattern. */ - -if ((*countptr)++ > 2000) return FFL_TOOCOMPLICATED; - -/* Scan along the opcodes for this branch. If we get to the end of the -branch, check the length against that of the other branches. */ - -for (;;) - { - int d; - PCRE2_UCHAR *ce, *cs; - register PCRE2_UCHAR op = *cc; - - if (branchlength > LOOKBEHIND_MAX) return FFL_TOOLONG; - - switch (op) - { - /* We only need to continue for OP_CBRA (normal capturing bracket) and - OP_BRA (normal non-capturing bracket) because the other variants of these - opcodes are all concerned with unlimited repeated groups, which of course - are not of fixed length. */ - - case OP_CBRA: - case OP_BRA: - case OP_ONCE: - case OP_ONCE_NC: - case OP_COND: - d = find_fixedlength(cc, utf, atend, cb, recurses, countptr); - if (d < 0) return d; - branchlength += (uint32_t)d; - do cc += GET(cc, 1); while (*cc == OP_ALT); - cc += 1 + LINK_SIZE; - break; - - /* Reached end of a branch; if it's a ket it is the end of a nested call. - If it's ALT it is an alternation in a nested call. An ACCEPT is effectively - an ALT. If it is END it's the end of the outer call. All can be handled by - the same code. Note that we must not include the OP_KETRxxx opcodes here, - because they all imply an unlimited repeat. */ - - case OP_ALT: - case OP_KET: - case OP_END: - case OP_ACCEPT: - case OP_ASSERT_ACCEPT: - if (length == 0xffffffffu) length = branchlength; - else if (length != branchlength) goto ISNOTFIXED; - if (*cc != OP_ALT) - { - if (group > 0) - { - groupinfo |= (uint32_t)(GI_SET_FIXED_LENGTH | length); - cb->groupinfo[group] = groupinfo; - } - return (int)length; - } - cc += 1 + LINK_SIZE; - branchlength = 0; - break; - - /* A true recursion implies not fixed length, but a subroutine call may - be OK. If the subroutine is a forward reference, we can't deal with - it until the end of the pattern, so return FFL_LATER. */ - - case OP_RECURSE: - if (!atend) return FFL_LATER; - cs = ce = (PCRE2_UCHAR *)cb->start_code + GET(cc, 1); /* Start subpattern */ - do ce += GET(ce, 1); while (*ce == OP_ALT); /* End subpattern */ - if (cc > cs && cc < ce) goto ISNOTFIXED; /* Recursion */ - else /* Check for mutual recursion */ - { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */ - } - this_recurse.prev = recurses; - this_recurse.group = cs; - d = find_fixedlength(cs, utf, atend, cb, &this_recurse, countptr); - if (d < 0) return d; - branchlength += (uint32_t)d; - cc += 1 + LINK_SIZE; - break; - - /* Skip over assertive subpatterns. Note that we must increment cc by - 1 + LINK_SIZE at the end, not by OP_length[*cc] because in a recursive - situation this assertion may be the one that is ultimately being checked - for having a fixed length, in which case its terminating OP_KET will have - been temporarily replaced by OP_END. */ - - case OP_ASSERT: - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - do cc += GET(cc, 1); while (*cc == OP_ALT); - cc += 1 + LINK_SIZE; - break; - - /* Skip over things that don't match chars */ - - case OP_MARK: - case OP_PRUNE_ARG: - case OP_SKIP_ARG: - case OP_THEN_ARG: - cc += cc[1] + PRIV(OP_lengths)[*cc]; - break; - - case OP_CALLOUT: - case OP_CIRC: - case OP_CIRCM: - case OP_CLOSE: - case OP_COMMIT: - case OP_CREF: - case OP_FALSE: - case OP_TRUE: - case OP_DNCREF: - case OP_DNRREF: - case OP_DOLL: - case OP_DOLLM: - case OP_EOD: - case OP_EODN: - case OP_FAIL: - case OP_NOT_WORD_BOUNDARY: - case OP_PRUNE: - case OP_REVERSE: - case OP_RREF: - case OP_SET_SOM: - case OP_SKIP: - case OP_SOD: - case OP_SOM: - case OP_THEN: - case OP_WORD_BOUNDARY: - cc += PRIV(OP_lengths)[*cc]; - break; - - case OP_CALLOUT_STR: - cc += GET(cc, 1 + 2*LINK_SIZE); - break; - - /* Handle literal characters */ - - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - branchlength++; - cc += 2; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; - - /* Handle exact repetitions. The count is already in characters, but we - need to skip over a multibyte character in UTF8 mode. */ - - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - branchlength += GET2(cc,1); - cc += 2 + IMM2_SIZE; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; - - case OP_TYPEEXACT: - branchlength += GET2(cc,1); - if (cc[1 + IMM2_SIZE] == OP_PROP || cc[1 + IMM2_SIZE] == OP_NOTPROP) - cc += 2; - cc += 1 + IMM2_SIZE + 1; - break; - - /* Handle single-char matchers */ - - case OP_PROP: - case OP_NOTPROP: - cc += 2; - /* Fall through */ - - case OP_HSPACE: - case OP_VSPACE: - case OP_NOT_HSPACE: - case OP_NOT_VSPACE: - case OP_NOT_DIGIT: - case OP_DIGIT: - case OP_NOT_WHITESPACE: - case OP_WHITESPACE: - case OP_NOT_WORDCHAR: - case OP_WORDCHAR: - case OP_ANY: - case OP_ALLANY: - branchlength++; - cc++; - break; - - /* The single-byte matcher isn't allowed. This only happens in UTF-8 or - UTF-16 mode; otherwise \C is coded as OP_ALLANY. */ - - case OP_ANYBYTE: - return FFL_BACKSLASHC; - - /* Check a class for variable quantification */ - - case OP_CLASS: - case OP_NCLASS: -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - /* The original code caused an unsigned overflow in 64 bit systems, - so now we use a conditional statement. */ - if (op == OP_XCLASS) - cc += GET(cc, 1); - else - cc += PRIV(OP_lengths)[OP_CLASS]; -#else - cc += PRIV(OP_lengths)[OP_CLASS]; -#endif - - switch (*cc) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSPLUS: - case OP_CRPOSQUERY: - goto ISNOTFIXED; - - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - if (GET2(cc,1) != GET2(cc,1+IMM2_SIZE)) goto ISNOTFIXED; - branchlength += GET2(cc,1); - cc += 1 + 2 * IMM2_SIZE; - break; - - default: - branchlength++; - } - break; - - /* Anything else is variable length */ - - case OP_ANYNL: - case OP_BRAMINZERO: - case OP_BRAPOS: - case OP_BRAPOSZERO: - case OP_BRAZERO: - case OP_CBRAPOS: - case OP_EXTUNI: - case OP_KETRMAX: - case OP_KETRMIN: - case OP_KETRPOS: - case OP_MINPLUS: - case OP_MINPLUSI: - case OP_MINQUERY: - case OP_MINQUERYI: - case OP_MINSTAR: - case OP_MINSTARI: - case OP_MINUPTO: - case OP_MINUPTOI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - case OP_NOTSTAR: - case OP_NOTSTARI: - case OP_NOTUPTO: - case OP_NOTUPTOI: - case OP_PLUS: - case OP_PLUSI: - case OP_POSPLUS: - case OP_POSPLUSI: - case OP_POSQUERY: - case OP_POSQUERYI: - case OP_POSSTAR: - case OP_POSSTARI: - case OP_POSUPTO: - case OP_POSUPTOI: - case OP_QUERY: - case OP_QUERYI: - case OP_REF: - case OP_REFI: - case OP_DNREF: - case OP_DNREFI: - case OP_SBRA: - case OP_SBRAPOS: - case OP_SCBRA: - case OP_SCBRAPOS: - case OP_SCOND: - case OP_SKIPZERO: - case OP_STAR: - case OP_STARI: - case OP_TYPEMINPLUS: - case OP_TYPEMINQUERY: - case OP_TYPEMINSTAR: - case OP_TYPEMINUPTO: - case OP_TYPEPLUS: - case OP_TYPEPOSPLUS: - case OP_TYPEPOSQUERY: - case OP_TYPEPOSSTAR: - case OP_TYPEPOSUPTO: - case OP_TYPEQUERY: - case OP_TYPESTAR: - case OP_TYPEUPTO: - case OP_UPTO: - case OP_UPTOI: - goto ISNOTFIXED; - - /* Catch unrecognized opcodes so that when new ones are added they - are not forgotten, as has happened in the past. */ - - default: - return FFL_UNKNOWNOP; - } - } -/* Control never gets here except by goto. */ - -ISNOTFIXED: -if (group > 0) - { - groupinfo |= GI_NOT_FIXED_LENGTH; - cb->groupinfo[group] = groupinfo; - } -return FFL_NOTFIXED; -} - - - -/************************************************* -* Find first significant op code * -*************************************************/ - -/* This is called by several functions that scan a compiled expression looking -for a fixed first character, or an anchoring op code etc. It skips over things -that do not influence this. For some calls, it makes sense to skip negative -forward and all backward assertions, and also the \b assertion; for others it -does not. - -Arguments: - code pointer to the start of the group - skipassert TRUE if certain assertions are to be skipped - -Returns: pointer to the first significant opcode -*/ - -static const PCRE2_UCHAR* -first_significant_code(PCRE2_SPTR code, BOOL skipassert) -{ -for (;;) - { - switch ((int)*code) - { - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - if (!skipassert) return code; - do code += GET(code, 1); while (*code == OP_ALT); - code += PRIV(OP_lengths)[*code]; - break; - - case OP_WORD_BOUNDARY: - case OP_NOT_WORD_BOUNDARY: - if (!skipassert) return code; - /* Fall through */ - - case OP_CALLOUT: - case OP_CREF: - case OP_DNCREF: - case OP_RREF: - case OP_DNRREF: - case OP_FALSE: - case OP_TRUE: - code += PRIV(OP_lengths)[*code]; - break; - - case OP_CALLOUT_STR: - code += GET(code, 1 + 2*LINK_SIZE); - break; - - default: - return code; - } - } -/* Control never reaches here */ -} - - - -/************************************************* -* Scan compiled branch for non-emptiness * -*************************************************/ - -/* This function scans through a branch of a compiled pattern to see whether it -can match the empty string. It is called at the end of compiling to check the -entire pattern, and from compile_branch() when checking for an unlimited repeat -of a group that can match nothing. In the latter case it is called only when -doing the real compile, not during the pre-compile that measures the size of -the compiled pattern. - -Note that first_significant_code() skips over backward and negative forward -assertions when its final argument is TRUE. If we hit an unclosed bracket, we -return "empty" - this means we've struck an inner bracket whose current branch -will already have been scanned. - -Arguments: - code points to start of search - endcode points to where to stop - utf TRUE if in UTF mode - cb compile data - atend TRUE if being called to check an entire pattern - recurses chain of recurse_check to catch mutual recursion - countptr pointer to count to catch over-complicated pattern - -Returns: 0 if what is matched cannot be empty - 1 if what is matched could be empty - -1 if the pattern is too complicated -*/ - -#define CBE_NOTEMPTY 0 -#define CBE_EMPTY 1 -#define CBE_TOOCOMPLICATED (-1) - - -static int -could_be_empty_branch(PCRE2_SPTR code, PCRE2_SPTR endcode, BOOL utf, - compile_block *cb, BOOL atend, recurse_check *recurses, int *countptr) -{ -uint32_t group = 0; -uint32_t groupinfo = 0; -register PCRE2_UCHAR c; -recurse_check this_recurse; - -/* If what we are checking has already been set as "could be empty", we know -the answer. */ - -if (*code >= OP_SBRA && *code <= OP_SCOND) return CBE_EMPTY; - -/* If this is a capturing group, we may have the answer cached, but we can only -use this information if there are no (?| groups in the pattern, because -otherwise group numbers are not unique. */ - -if ((cb->external_flags & PCRE2_DUPCAPUSED) == 0 && - (*code == OP_CBRA || *code == OP_CBRAPOS)) - { - group = GET2(code, 1 + LINK_SIZE); - groupinfo = cb->groupinfo[group]; - if ((groupinfo & GI_SET_COULD_BE_EMPTY) != 0) - return ((groupinfo & GI_COULD_BE_EMPTY) != 0)? CBE_EMPTY : CBE_NOTEMPTY; - } - -/* A large and/or complex regex can take too long to process. We have to assume -it can match an empty string. This can happen more often when (?| groups are -present in the pattern and the caching is disabled. Setting the cap at 1100 -allows the test for more than 1023 capturing patterns to work. */ - -if ((*countptr)++ > 1100) return CBE_TOOCOMPLICATED; - -/* Scan the opcodes for this branch. */ - -for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); - code < endcode; - code = first_significant_code(code + PRIV(OP_lengths)[c], TRUE)) - { - PCRE2_SPTR ccode; - - c = *code; - - /* Skip over forward assertions; the other assertions are skipped by - first_significant_code() with a TRUE final argument. */ - - if (c == OP_ASSERT) - { - do code += GET(code, 1); while (*code == OP_ALT); - c = *code; - continue; - } - - /* For a recursion/subroutine call we can scan the recursion when this - function is called at the end, to check a complete pattern. Before then, - recursions just have the group number as their argument and in any case may - be forward references. In that situation, we return CBE_EMPTY, just in case. - It means that unlimited repeats of groups that contain recursions are always - treated as "could be empty" - which just adds a bit more processing time - because of the runtime check. */ - - if (c == OP_RECURSE) - { - PCRE2_SPTR scode, endgroup; - BOOL empty_branch; - - if (!atend) goto ISTRUE; - scode = cb->start_code + GET(code, 1); - endgroup = scode; - - /* We need to detect whether this is a recursive call, as otherwise there - will be an infinite loop. If it is a recursion, just skip over it. Simple - recursions are easily detected. For mutual recursions we keep a chain on - the stack. */ - - do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); - if (code >= scode && code <= endgroup) continue; /* Simple recursion */ - else - { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) - if (r->group == scode) break; - if (r != NULL) continue; /* Mutual recursion */ - } - - /* Scan the referenced group, remembering it on the stack chain to detect - mutual recursions. */ - - empty_branch = FALSE; - this_recurse.prev = recurses; - this_recurse.group = scode; - - do - { - int rc = could_be_empty_branch(scode, endcode, utf, cb, atend, - &this_recurse, countptr); - if (rc < 0) return rc; - if (rc > 0) - { - empty_branch = TRUE; - break; - } - scode += GET(scode, 1); - } - while (*scode == OP_ALT); - - if (!empty_branch) goto ISFALSE; /* All branches are non-empty */ - continue; - } - - /* Groups with zero repeats can of course be empty; skip them. */ - - if (c == OP_BRAZERO || c == OP_BRAMINZERO || c == OP_SKIPZERO || - c == OP_BRAPOSZERO) - { - code += PRIV(OP_lengths)[c]; - do code += GET(code, 1); while (*code == OP_ALT); - c = *code; - continue; - } - - /* A nested group that is already marked as "could be empty" can just be - skipped. */ - - if (c == OP_SBRA || c == OP_SBRAPOS || - c == OP_SCBRA || c == OP_SCBRAPOS) - { - do code += GET(code, 1); while (*code == OP_ALT); - c = *code; - continue; - } - - /* For other groups, scan the branches. */ - - if (c == OP_BRA || c == OP_BRAPOS || - c == OP_CBRA || c == OP_CBRAPOS || - c == OP_ONCE || c == OP_ONCE_NC || - c == OP_COND || c == OP_SCOND) - { - BOOL empty_branch; - if (GET(code, 1) == 0) goto ISTRUE; /* Hit unclosed bracket */ - - /* If a conditional group has only one branch, there is a second, implied, - empty branch, so just skip over the conditional, because it could be empty. - Otherwise, scan the individual branches of the group. */ - - if (c == OP_COND && code[GET(code, 1)] != OP_ALT) - code += GET(code, 1); - else - { - empty_branch = FALSE; - do - { - if (!empty_branch) - { - int rc = could_be_empty_branch(code, endcode, utf, cb, atend, - recurses, countptr); - if (rc < 0) return rc; - if (rc > 0) empty_branch = TRUE; - } - code += GET(code, 1); - } - while (*code == OP_ALT); - if (!empty_branch) goto ISFALSE; /* All branches are non-empty */ - } - - c = *code; - continue; - } - - /* Handle the other opcodes */ - - switch (c) - { - /* Check for quantifiers after a class. XCLASS is used for classes that - cannot be represented just by a bit map. This includes negated single - high-valued characters. The length in PRIV(OP_lengths)[] is zero; the - actual length is stored in the compiled code, so we must update "code" - here. */ - -#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 - case OP_XCLASS: - ccode = code += GET(code, 1); - goto CHECK_CLASS_REPEAT; -#endif - - case OP_CLASS: - case OP_NCLASS: - ccode = code + PRIV(OP_lengths)[OP_CLASS]; - -#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 - CHECK_CLASS_REPEAT: -#endif - - switch (*ccode) - { - case OP_CRSTAR: /* These could be empty; continue */ - case OP_CRMINSTAR: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSQUERY: - break; - - default: /* Non-repeat => class must match */ - case OP_CRPLUS: /* These repeats aren't empty */ - case OP_CRMINPLUS: - case OP_CRPOSPLUS: - goto ISFALSE; - - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - if (GET2(ccode, 1) > 0) goto ISFALSE; /* Minimum > 0 */ - break; - } - break; - - /* Opcodes that must match a character */ - - case OP_ANY: - case OP_ALLANY: - case OP_ANYBYTE: - - case OP_PROP: - case OP_NOTPROP: - case OP_ANYNL: - - case OP_NOT_HSPACE: - case OP_HSPACE: - case OP_NOT_VSPACE: - case OP_VSPACE: - case OP_EXTUNI: - - case OP_NOT_DIGIT: - case OP_DIGIT: - case OP_NOT_WHITESPACE: - case OP_WHITESPACE: - case OP_NOT_WORDCHAR: - case OP_WORDCHAR: - - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - - case OP_PLUS: - case OP_PLUSI: - case OP_MINPLUS: - case OP_MINPLUSI: - - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - - case OP_POSPLUS: - case OP_POSPLUSI: - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEPOSPLUS: - case OP_TYPEEXACT: - goto ISFALSE; - - /* These are going to continue, as they may be empty, but we have to - fudge the length for the \p and \P cases. */ - - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPOSSTAR: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - case OP_TYPEPOSQUERY: - if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; - break; - - /* Same for these */ - - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - case OP_TYPEPOSUPTO: - if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) - code += 2; - break; - - /* End of branch */ - - case OP_KET: - case OP_KETRMAX: - case OP_KETRMIN: - case OP_KETRPOS: - case OP_ALT: - goto ISTRUE; - - /* In UTF-8 or UTF-16 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, - POSQUERY, UPTO, MINUPTO, and POSUPTO and their caseless and negative - versions may be followed by a multibyte character. */ - -#ifdef MAYBE_UTF_MULTI - case OP_STAR: - case OP_STARI: - case OP_NOTSTAR: - case OP_NOTSTARI: - - case OP_MINSTAR: - case OP_MINSTARI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - - case OP_POSSTAR: - case OP_POSSTARI: - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - - case OP_QUERY: - case OP_QUERYI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - - case OP_MINQUERY: - case OP_MINQUERYI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - - case OP_POSQUERY: - case OP_POSQUERYI: - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - if (utf && HAS_EXTRALEN(code[1])) code += GET_EXTRALEN(code[1]); - break; - - case OP_UPTO: - case OP_UPTOI: - case OP_NOTUPTO: - case OP_NOTUPTOI: - - case OP_MINUPTO: - case OP_MINUPTOI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - - case OP_POSUPTO: - case OP_POSUPTOI: - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - if (utf && HAS_EXTRALEN(code[1 + IMM2_SIZE])) code += GET_EXTRALEN(code[1 + IMM2_SIZE]); - break; -#endif /* MAYBE_UTF_MULTI */ - - /* MARK, and PRUNE/SKIP/THEN with an argument must skip over the argument - string. */ - - case OP_MARK: - case OP_PRUNE_ARG: - case OP_SKIP_ARG: - case OP_THEN_ARG: - code += code[1]; - break; - - /* None of the remaining opcodes are required to match a character. */ - - default: - break; - } - } - -ISTRUE: -groupinfo |= GI_COULD_BE_EMPTY; - -ISFALSE: -if (group > 0) cb->groupinfo[group] = groupinfo | GI_SET_COULD_BE_EMPTY; - -return ((groupinfo & GI_COULD_BE_EMPTY) != 0)? CBE_EMPTY : CBE_NOTEMPTY; -} - - - -/************************************************* -* Check for counted repeat * -*************************************************/ - -/* This function is called when a '{' is encountered in a place where it might -start a quantifier. It looks ahead to see if it really is a quantifier, that -is, one of the forms {ddd} {ddd,} or {ddd,ddd} where the ddds are digits. - -Argument: pointer to the first char after '{' -Returns: TRUE or FALSE -*/ - -static BOOL -is_counted_repeat(PCRE2_SPTR p) -{ -if (!IS_DIGIT(*p)) return FALSE; -p++; -while (IS_DIGIT(*p)) p++; -if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; - -if (*p++ != CHAR_COMMA) return FALSE; -if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; - -if (!IS_DIGIT(*p)) return FALSE; -p++; -while (IS_DIGIT(*p)) p++; - -return (*p == CHAR_RIGHT_CURLY_BRACKET); -} - - - -/************************************************* -* Handle escapes * -*************************************************/ - -/* This function is called when a \ has been encountered. It either returns a -positive value for a simple escape such as \d, or 0 for a data character, which -is placed in chptr. A backreference to group n is returned as negative n. On -entry, ptr is pointing at the \. On exit, it points the final code unit of the -escape sequence. - -This function is also called from pcre2_substitute() to handle escape sequences -in replacement strings. In this case, the cb argument is NULL, and only -sequences that define a data character are recognised. The isclass argument is -not relevant, but the options argument is the final value of the compiled -pattern's options. - -There is one "trick" case: when a sequence such as [[:>:]] or \s in UCP mode is -processed, it is replaced by a nested alternative sequence. If this contains a -backslash (which is usually does), ptrend does not point to its end - it still -points to the end of the whole pattern. However, we can detect this case -because cb->nestptr[0] will be non-NULL. The nested sequences are all zero- -terminated and there are only ever two levels of nesting. - -Arguments: - ptrptr points to the input position pointer - ptrend points to the end of the input - chptr points to a returned data character - errorcodeptr points to the errorcode variable (containing zero) - options the current options bits - isclass TRUE if inside a character class - cb compile data block - -Returns: zero => a data character - positive => a special escape sequence - negative => a back reference - on error, errorcodeptr is set non-zero -*/ - -int -PRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr, - int *errorcodeptr, uint32_t options, BOOL isclass, compile_block *cb) -{ -BOOL utf = (options & PCRE2_UTF) != 0; -PCRE2_SPTR ptr = *ptrptr + 1; -register uint32_t c, cc; -int escape = 0; -int i; - -/* Find the end of a nested insert. */ - -if (cb != NULL && cb->nestptr[0] != NULL) - ptrend = ptr + PRIV(strlen)(ptr); - -/* If backslash is at the end of the string, it's an error. */ - -if (ptr >= ptrend) - { - *errorcodeptr = ERR1; - return 0; - } - -GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ -ptr--; /* Set pointer back to the last code unit */ - -/* Non-alphanumerics are literals, so we just leave the value in c. An initial -value test saves a memory lookup for code points outside the alphanumeric -range. Otherwise, do a table lookup. A non-zero result is something that can be -returned immediately. Otherwise further processing is required. */ - -if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ - -else if ((i = escapes[c - ESCAPES_FIRST]) != 0) - { - if (i > 0) c = (uint32_t)i; else /* Positive is a data character */ - { - escape = -i; /* Else return a special escape */ - if (escape == ESC_P || escape == ESC_p || escape == ESC_X) - cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */ - } - } - -/* Escapes that need further processing, including those that are unknown. -When called from pcre2_substitute(), only \c, \o, and \x are recognized (and \u -when BSUX is set). */ - -else - { - PCRE2_SPTR oldptr; - BOOL braced, negated, overflow; - unsigned int s; - - /* Filter calls from pcre2_substitute(). */ - - if (cb == NULL && c != CHAR_c && c != CHAR_o && c != CHAR_x && - (c != CHAR_u || (options & PCRE2_ALT_BSUX) != 0)) - { - *errorcodeptr = ERR3; - return 0; - } - - switch (c) - { - /* A number of Perl escapes are not handled by PCRE. We give an explicit - error. */ - - case CHAR_l: - case CHAR_L: - *errorcodeptr = ERR37; - break; - - /* \u is unrecognized when PCRE2_ALT_BSUX is not set. When it is treated - specially, \u must be followed by four hex digits. Otherwise it is a - lowercase u letter. */ - - case CHAR_u: - if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; else - { - uint32_t xc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ - cc = (cc << 4) | xc; - if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */ - cc = (cc << 4) | xc; - if ((xc = XDIGIT(ptr[4])) == 0xff) break; /* Not a hex digit */ - c = (cc << 4) | xc; - ptr += 4; - if (utf) - { - if (c > 0x10ffffU) *errorcodeptr = ERR77; - else if (c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; - } - else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77; - } - break; - - case CHAR_U: - /* \U is unrecognized unless PCRE2_ALT_BSUX is set, in which case it is an - upper case letter. */ - if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; - break; - - /* In a character class, \g is just a literal "g". Outside a character - class, \g must be followed by one of a number of specific things: - - (1) A number, either plain or braced. If positive, it is an absolute - backreference. If negative, it is a relative backreference. This is a Perl - 5.10 feature. - - (2) Perl 5.10 also supports \g{name} as a reference to a named group. This - is part of Perl's movement towards a unified syntax for back references. As - this is synonymous with \k{name}, we fudge it up by pretending it really - was \k. - - (3) For Oniguruma compatibility we also support \g followed by a name or a - number either in angle brackets or in single quotes. However, these are - (possibly recursive) subroutine calls, _not_ backreferences. Just return - the ESC_g code (cf \k). */ - - case CHAR_g: - if (isclass) break; - if (ptr[1] == CHAR_LESS_THAN_SIGN || ptr[1] == CHAR_APOSTROPHE) - { - escape = ESC_g; - break; - } - - /* Handle the Perl-compatible cases */ - - if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) - { - PCRE2_SPTR p; - for (p = ptr+2; *p != CHAR_NULL && *p != CHAR_RIGHT_CURLY_BRACKET; p++) - if (*p != CHAR_MINUS && !IS_DIGIT(*p)) break; - if (*p != CHAR_NULL && *p != CHAR_RIGHT_CURLY_BRACKET) - { - escape = ESC_k; - break; - } - braced = TRUE; - ptr++; - } - else braced = FALSE; - - if (ptr[1] == CHAR_MINUS) - { - negated = TRUE; - ptr++; - } - else negated = FALSE; - - /* The integer range is limited by the machine's int representation. */ - s = 0; - overflow = FALSE; - while (IS_DIGIT(ptr[1])) - { - if (s > INT_MAX / 10 - 1) /* Integer overflow */ - { - overflow = TRUE; - break; - } - s = s * 10 + (unsigned int)(*(++ptr) - CHAR_0); - } - if (overflow) /* Integer overflow */ - { - while (IS_DIGIT(ptr[1])) ptr++; - *errorcodeptr = ERR61; - break; - } - - if (braced && *(++ptr) != CHAR_RIGHT_CURLY_BRACKET) - { - *errorcodeptr = ERR57; - break; - } - - if (s == 0) - { - *errorcodeptr = ERR58; - break; - } - - if (negated) - { - if (s > cb->bracount) - { - *errorcodeptr = ERR15; - break; - } - s = cb->bracount - (s - 1); - } - - escape = -(int)s; - break; - - /* The handling of escape sequences consisting of a string of digits - starting with one that is not zero is not straightforward. Perl has changed - over the years. Nowadays \g{} for backreferences and \o{} for octal are - recommended to avoid the ambiguities in the old syntax. - - Outside a character class, the digits are read as a decimal number. If the - number is less than 10, or if there are that many previous extracting left - brackets, it is a back reference. Otherwise, up to three octal digits are - read to form an escaped character code. Thus \123 is likely to be octal 123 - (cf \0123, which is octal 012 followed by the literal 3). - - Inside a character class, \ followed by a digit is always either a literal - 8 or 9 or an octal number. */ - - case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: - case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: - - if (!isclass) - { - oldptr = ptr; - /* The integer range is limited by the machine's int representation. */ - s = c - CHAR_0; - overflow = FALSE; - while (IS_DIGIT(ptr[1])) - { - if (s > INT_MAX / 10 - 1) /* Integer overflow */ - { - overflow = TRUE; - break; - } - s = s * 10 + (unsigned int)(*(++ptr) - CHAR_0); - } - if (overflow) /* Integer overflow */ - { - while (IS_DIGIT(ptr[1])) ptr++; - *errorcodeptr = ERR61; - break; - } - - /* \1 to \9 are always back references. \8x and \9x are too; \1x to \7x - are octal escapes if there are not that many previous captures. */ - - if (s < 10 || *oldptr >= CHAR_8 || s <= cb->bracount) - { - escape = -(int)s; /* Indicates a back reference */ - break; - } - ptr = oldptr; /* Put the pointer back and fall through */ - } - - /* Handle a digit following \ when the number is not a back reference, or - we are within a character class. If the first digit is 8 or 9, Perl used to - generate a binary zero byte and then treat the digit as a following - literal. At least by Perl 5.18 this changed so as not to insert the binary - zero. */ - - if ((c = *ptr) >= CHAR_8) break; - - /* Fall through with a digit less than 8 */ - - /* \0 always starts an octal number, but we may drop through to here with a - larger first octal digit. The original code used just to take the least - significant 8 bits of octal numbers (I think this is what early Perls used - to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, - but no more than 3 octal digits. */ - - case CHAR_0: - c -= CHAR_0; - while(i++ < 2 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7) - c = c * 8 + *(++ptr) - CHAR_0; -#if PCRE2_CODE_UNIT_WIDTH == 8 - if (!utf && c > 0xff) *errorcodeptr = ERR51; -#endif - break; - - /* \o is a relatively new Perl feature, supporting a more general way of - specifying character codes in octal. The only supported form is \o{ddd}. */ - - case CHAR_o: - if (ptr[1] != CHAR_LEFT_CURLY_BRACKET) *errorcodeptr = ERR55; else - if (ptr[2] == CHAR_RIGHT_CURLY_BRACKET) *errorcodeptr = ERR78; else - { - ptr += 2; - c = 0; - overflow = FALSE; - while (*ptr >= CHAR_0 && *ptr <= CHAR_7) - { - cc = *ptr++; - if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ -#if PCRE2_CODE_UNIT_WIDTH == 32 - if (c >= 0x20000000l) { overflow = TRUE; break; } -#endif - c = (c << 3) + (cc - CHAR_0); -#if PCRE2_CODE_UNIT_WIDTH == 8 - if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; } -#elif PCRE2_CODE_UNIT_WIDTH == 16 - if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; } -#elif PCRE2_CODE_UNIT_WIDTH == 32 - if (utf && c > 0x10ffffU) { overflow = TRUE; break; } -#endif - } - if (overflow) - { - while (*ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; - *errorcodeptr = ERR34; - } - else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) - { - if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; - } - else *errorcodeptr = ERR64; - } - break; - - /* \x is complicated. When PCRE2_ALT_BSUX is set, \x must be followed by - two hexadecimal digits. Otherwise it is a lowercase x letter. */ - - case CHAR_x: - if ((options & PCRE2_ALT_BSUX) != 0) - { - uint32_t xc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ - c = (cc << 4) | xc; - ptr += 2; - } /* End PCRE2_ALT_BSUX handling */ - - /* Handle \x in Perl's style. \x{ddd} is a character number which can be - greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex - digits. If not, { used to be treated as a data character. However, Perl - seems to read hex digits up to the first non-such, and ignore the rest, so - that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE - now gives an error. */ - - else - { - if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) - { - ptr += 2; - if (*ptr == CHAR_RIGHT_CURLY_BRACKET) - { - *errorcodeptr = ERR78; - break; - } - c = 0; - overflow = FALSE; - - while ((cc = XDIGIT(*ptr)) != 0xff) - { - ptr++; - if (c == 0 && cc == 0) continue; /* Leading zeroes */ -#if PCRE2_CODE_UNIT_WIDTH == 32 - if (c >= 0x10000000l) { overflow = TRUE; break; } -#endif - c = (c << 4) | cc; - if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR)) - { - overflow = TRUE; - break; - } - } - - if (overflow) - { - while (XDIGIT(*ptr) != 0xff) ptr++; - *errorcodeptr = ERR34; - } - else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) - { - if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; - } - - /* If the sequence of hex digits does not end with '}', give an error. - We used just to recognize this construct and fall through to the normal - \x handling, but nowadays Perl gives an error, which seems much more - sensible, so we do too. */ - - else *errorcodeptr = ERR67; - } /* End of \x{} processing */ - - /* Read a single-byte hex-defined char (up to two hex digits after \x) */ - - else - { - c = 0; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - ptr++; - c = cc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - ptr++; - c = (c << 4) | cc; - } /* End of \xdd handling */ - } /* End of Perl-style \x handling */ - break; - - /* The handling of \c is different in ASCII and EBCDIC environments. In an - ASCII (or Unicode) environment, an error is given if the character - following \c is not a printable ASCII character. Otherwise, the following - character is upper-cased if it is a letter, and after that the 0x40 bit is - flipped. The result is the value of the escape. - - In an EBCDIC environment the handling of \c is compatible with the - specification in the perlebcdic document. The following character must be - a letter or one of small number of special characters. These provide a - means of defining the character values 0-31. - - For testing the EBCDIC handling of \c in an ASCII environment, recognize - the EBCDIC value of 'c' explicitly. */ - -#if defined EBCDIC && 'a' != 0x81 - case 0x83: -#else - case CHAR_c: -#endif - - c = *(++ptr); - if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); - if (c == CHAR_NULL && ptr >= ptrend) - { - *errorcodeptr = ERR2; - break; - } - - /* Handle \c in an ASCII/Unicode environment. */ - -#ifndef EBCDIC /* ASCII/UTF-8 coding */ - if (c < 32 || c > 126) /* Excludes all non-printable ASCII */ - { - *errorcodeptr = ERR68; - break; - } - c ^= 0x40; - - /* Handle \c in an EBCDIC environment. The special case \c? is converted to - 255 (0xff) or 95 (0x5f) if other character suggest we are using th POSIX-BC - encoding. (This is the way Perl indicates that it handles \c?.) The other - valid sequences correspond to a list of specific characters. */ - -#else - if (c == CHAR_QUESTION_MARK) - c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff; - else - { - for (i = 0; i < 32; i++) - { - if (c == ebcdic_escape_c[i]) break; - } - if (i < 32) c = i; else *errorcodeptr = ERR68; - } -#endif /* EBCDIC */ - - break; - - /* Any other alphanumeric following \ is an error. Perl gives an error only - if in warning mode, but PCRE doesn't have a warning mode. */ - - default: - *errorcodeptr = ERR3; - break; - } - } - -/* Perl supports \N{name} for character names, as well as plain \N for "not -newline". PCRE does not support \N{name}. However, it does support -quantification such as \N{2,3}. */ - -if (escape == ESC_N && ptr[1] == CHAR_LEFT_CURLY_BRACKET && - !is_counted_repeat(ptr+2)) - *errorcodeptr = ERR37; - -/* If PCRE2_UCP is set, we change the values for \d etc. */ - -if ((options & PCRE2_UCP) != 0 && escape >= ESC_D && escape <= ESC_w) - escape += (ESC_DU - ESC_D); - -/* Set the pointer to the final character before returning. */ - -*ptrptr = ptr; -*chptr = c; -return escape; -} - - - -#ifdef SUPPORT_UNICODE -/************************************************* -* Handle \P and \p * -*************************************************/ - -/* This function is called after \P or \p has been encountered, provided that -PCRE2 is compiled with support for UTF and Unicode properties. On entry, the -contents of ptrptr are pointing at the P or p. On exit, it is left pointing at -the final code unit of the escape sequence. - -Arguments: - ptrptr the pattern position pointer - negptr a boolean that is set TRUE for negation else FALSE - ptypeptr an unsigned int that is set to the type value - pdataptr an unsigned int that is set to the detailed property value - errorcodeptr the error code variable - cb the compile data - -Returns: TRUE if the type value was found, or FALSE for an invalid type -*/ - -static BOOL -get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, unsigned int *ptypeptr, - unsigned int *pdataptr, int *errorcodeptr, compile_block *cb) -{ -register PCRE2_UCHAR c; -size_t i, bot, top; -PCRE2_SPTR ptr = *ptrptr; -PCRE2_UCHAR name[32]; - -*negptr = FALSE; -c = *(++ptr); - -/* \P or \p can be followed by a name in {}, optionally preceded by ^ for -negation. */ - -if (c == CHAR_LEFT_CURLY_BRACKET) - { - if (ptr[1] == CHAR_CIRCUMFLEX_ACCENT) - { - *negptr = TRUE; - ptr++; - } - for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++) - { - c = *(++ptr); - if (c == CHAR_NULL) goto ERROR_RETURN; - if (c == CHAR_RIGHT_CURLY_BRACKET) break; - name[i] = c; - } - if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; - name[i] = 0; - } - -/* Otherwise there is just one following character, which must be an ASCII -letter. */ - -else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0) - { - name[0] = c; - name[1] = 0; - } -else goto ERROR_RETURN; - -*ptrptr = ptr; - -/* Search for a recognized property name using binary chop. */ - -bot = 0; -top = PRIV(utt_size); - -while (bot < top) - { - int r; - i = (bot + top) >> 1; - r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); - if (r == 0) - { - *ptypeptr = PRIV(utt)[i].type; - *pdataptr = PRIV(utt)[i].value; - return TRUE; - } - if (r > 0) bot = i + 1; else top = i; - } -*errorcodeptr = ERR47; /* Unrecognized name */ -return FALSE; - -ERROR_RETURN: /* Malformed \P or \p */ -*errorcodeptr = ERR46; -*ptrptr = ptr; -return FALSE; -} -#endif - - - -/************************************************* -* Read repeat counts * -*************************************************/ - -/* Read an item of the form {n,m} and return the values. This is called only -after is_counted_repeat() has confirmed that a repeat-count quantifier exists, -so the syntax is guaranteed to be correct, but we need to check the values. - -Arguments: - p pointer to first char after '{' - minp pointer to int for min - maxp pointer to int for max - returned as -1 if no max - errorcodeptr points to error code variable - -Returns: pointer to '}' on success; - current ptr on error, with errorcodeptr set non-zero -*/ - -static PCRE2_SPTR -read_repeat_counts(PCRE2_SPTR p, int *minp, int *maxp, int *errorcodeptr) -{ -int min = 0; -int max = -1; - -while (IS_DIGIT(*p)) - { - min = min * 10 + (int)(*p++ - CHAR_0); - if (min > 65535) - { - *errorcodeptr = ERR5; - return p; - } - } - -if (*p == CHAR_RIGHT_CURLY_BRACKET) max = min; else - { - if (*(++p) != CHAR_RIGHT_CURLY_BRACKET) - { - max = 0; - while(IS_DIGIT(*p)) - { - max = max * 10 + (int)(*p++ - CHAR_0); - if (max > 65535) - { - *errorcodeptr = ERR5; - return p; - } - } - if (max < min) - { - *errorcodeptr = ERR4; - return p; - } - } - } - -*minp = min; -*maxp = max; -return p; -} - - - -/************************************************* -* Scan compiled regex for recursion reference * -*************************************************/ - -/* This function scans through a compiled pattern until it finds an instance of -OP_RECURSE. - -Arguments: - code points to start of expression - utf TRUE in UTF mode - -Returns: pointer to the opcode for OP_RECURSE, or NULL if not found -*/ - -static PCRE2_SPTR -find_recurse(PCRE2_SPTR code, BOOL utf) -{ -for (;;) - { - register PCRE2_UCHAR c = *code; - if (c == OP_END) return NULL; - if (c == OP_RECURSE) return code; - - /* XCLASS is used for classes that cannot be represented just by a bit map. - This includes negated single high-valued characters. CALLOUT_STR is used for - callouts with string arguments. In both cases the length in the table is - zero; the actual length is stored in the compiled code. */ - - if (c == OP_XCLASS) code += GET(code, 1); - else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE); - - /* Otherwise, we can get the item's length from the table, except that for - repeated character types, we have to test for \p and \P, which have an extra - two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we - must add in its length. */ - - else - { - switch(c) - { - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - case OP_TYPEPOSSTAR: - case OP_TYPEPOSPLUS: - case OP_TYPEPOSQUERY: - if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; - break; - - case OP_TYPEPOSUPTO: - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - case OP_TYPEEXACT: - if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) - code += 2; - break; - - case OP_MARK: - case OP_PRUNE_ARG: - case OP_SKIP_ARG: - case OP_THEN_ARG: - code += code[1]; - break; - } - - /* Add in the fixed length from the table */ - - code += PRIV(OP_lengths)[c]; - - /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may - be followed by a multi-unit character. The length in the table is a - minimum, so we have to arrange to skip the extra units. */ - -#ifdef MAYBE_UTF_MULTI - if (utf) switch(c) - { - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - case OP_UPTO: - case OP_UPTOI: - case OP_NOTUPTO: - case OP_NOTUPTOI: - case OP_MINUPTO: - case OP_MINUPTOI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - case OP_POSUPTO: - case OP_POSUPTOI: - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - case OP_STAR: - case OP_STARI: - case OP_NOTSTAR: - case OP_NOTSTARI: - case OP_MINSTAR: - case OP_MINSTARI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - case OP_POSSTAR: - case OP_POSSTARI: - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - case OP_PLUS: - case OP_PLUSI: - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_MINPLUS: - case OP_MINPLUSI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - case OP_POSPLUS: - case OP_POSPLUSI: - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - case OP_QUERY: - case OP_QUERYI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - case OP_MINQUERY: - case OP_MINQUERYI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - case OP_POSQUERY: - case OP_POSQUERYI: - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); - break; - } -#else - (void)(utf); /* Keep compiler happy by referencing function argument */ -#endif /* MAYBE_UTF_MULTI */ - } - } -} - - - -/************************************************* -* Check for POSIX class syntax * -*************************************************/ - -/* This function is called when the sequence "[:" or "[." or "[=" is -encountered in a character class. It checks whether this is followed by a -sequence of characters terminated by a matching ":]" or ".]" or "=]". If we -reach an unescaped ']' without the special preceding character, return FALSE. - -Originally, this function only recognized a sequence of letters between the -terminators, but it seems that Perl recognizes any sequence of characters, -though of course unknown POSIX names are subsequently rejected. Perl gives an -"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE -didn't consider this to be a POSIX class. Likewise for [:1234:]. - -The problem in trying to be exactly like Perl is in the handling of escapes. We -have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX -class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code -below handles the special cases \\ and \], but does not try to do any other -escape processing. This makes it different from Perl for cases such as -[:l\ower:] where Perl recognizes it as the POSIX class "lower" but PCRE does -not recognize "l\ower". This is a lesser evil than not diagnosing bad classes -when Perl does, I think. - -A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not. -It seems that the appearance of a nested POSIX class supersedes an apparent -external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or -a digit. This is handled by returning FALSE if the start of a new group with -the same terminator is encountered, since the next closing sequence must close -the nested group, not the outer one. - -In Perl, unescaped square brackets may also appear as part of class names. For -example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for -[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not -seem right at all. PCRE does not allow closing square brackets in POSIX class -names. - -Arguments: - ptr pointer to the initial [ - endptr where to return a pointer to the terminating ':', '.', or '=' - -Returns: TRUE or FALSE -*/ - -static BOOL -check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR *endptr) -{ -PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */ -terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ - -for (++ptr; *ptr != CHAR_NULL; ptr++) - { - if (*ptr == CHAR_BACKSLASH && - (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH)) - ptr++; - else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) || - *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; - else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) - { - *endptr = ptr; - return TRUE; - } - } - -return FALSE; -} - - - -/************************************************* -* Check POSIX class name * -*************************************************/ - -/* This function is called to check the name given in a POSIX-style class entry -such as [:alnum:]. - -Arguments: - ptr points to the first letter - len the length of the name - -Returns: a value representing the name, or -1 if unknown -*/ - -static int -check_posix_name(PCRE2_SPTR ptr, int len) -{ -const char *pn = posix_names; -register int yield = 0; -while (posix_name_lengths[yield] != 0) - { - if (len == posix_name_lengths[yield] && - PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield; - pn += posix_name_lengths[yield] + 1; - yield++; - } -return -1; -} - - - -#ifdef SUPPORT_UNICODE -/************************************************* -* Get othercase range * -*************************************************/ - -/* This function is passed the start and end of a class range in UCT mode. It -searches up the characters, looking for ranges of characters in the "other" -case. Each call returns the next one, updating the start address. A character -with multiple other cases is returned on its own with a special return value. - -Arguments: - cptr points to starting character value; updated - d end value - ocptr where to put start of othercase range - odptr where to put end of othercase range - -Yield: -1 when no more - 0 when a range is returned - >0 the CASESET offset for char with multiple other cases - in this case, ocptr contains the original -*/ - -static int -get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr, - uint32_t *odptr) -{ -uint32_t c, othercase, next; -unsigned int co; - -/* Find the first character that has an other case. If it has multiple other -cases, return its case offset value. */ - -for (c = *cptr; c <= d; c++) - { - if ((co = UCD_CASESET(c)) != 0) - { - *ocptr = c++; /* Character that has the set */ - *cptr = c; /* Rest of input range */ - return (int)co; - } - if ((othercase = UCD_OTHERCASE(c)) != c) break; - } - -if (c > d) return -1; /* Reached end of range */ - -/* Found a character that has a single other case. Search for the end of the -range, which is either the end of the input range, or a character that has zero -or more than one other cases. */ - -*ocptr = othercase; -next = othercase + 1; - -for (++c; c <= d; c++) - { - if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; - next++; - } - -*odptr = next - 1; /* End of othercase range */ -*cptr = c; /* Rest of input range */ -return 0; -} -#endif /* SUPPORT_UNICODE */ - - - -/************************************************* -* Add a character or range to a class * -*************************************************/ - -/* This function packages up the logic of adding a character or range of -characters to a class. The character values in the arguments will be within the -valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is -mutually recursive with the function immediately below. - -Arguments: - classbits the bit map for characters < 256 - uchardptr points to the pointer for extra data - options the options word - cb compile data - start start of range character - end end of range character - -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ - -static unsigned int -add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, - compile_block *cb, uint32_t start, uint32_t end) -{ -uint32_t c; -uint32_t classbits_end = (end <= 0xff ? end : 0xff); -unsigned int n8 = 0; - -/* If caseless matching is required, scan the range and process alternate -cases. In Unicode, there are 8-bit characters that have alternate cases that -are greater than 255 and vice-versa. Sometimes we can just extend the original -range. */ - -if ((options & PCRE2_CASELESS) != 0) - { -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UTF) != 0) - { - int rc; - uint32_t oc, od; - - options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ - c = start; - - while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) - { - /* Handle a single character that has more than one other case. */ - - if (rc > 0) n8 += add_list_to_class(classbits, uchardptr, options, cb, - PRIV(ucd_caseless_sets) + rc, oc); - - /* Do nothing if the other case range is within the original range. */ - - else if (oc >= start && od <= end) continue; - - /* Extend the original range if there is overlap, noting that if oc < c, we - can't have od > end because a subrange is always shorter than the basic - range. Otherwise, use a recursive call to add the additional range. */ - - else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */ - else if (od > end && oc <= end + 1) - { - end = od; /* Extend upwards */ - if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff); - } - else n8 += add_to_class(classbits, uchardptr, options, cb, oc, od); - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Not UTF mode */ - - for (c = start; c <= classbits_end; c++) - { - SETBIT(classbits, cb->fcc[c]); - n8++; - } - } - -/* Now handle the original range. Adjust the final value according to the bit -length - this means that the same lists of (e.g.) horizontal spaces can be used -in all cases. */ - -if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) - end = MAX_NON_UTF_CHAR; - -/* Use the bitmap for characters < 256. Otherwise use extra data.*/ - -for (c = start; c <= classbits_end; c++) - { - /* Regardless of start, c will always be <= 255. */ - SETBIT(classbits, c); - n8++; - } - -#ifdef SUPPORT_WIDE_CHARS -if (start <= 0xff) start = 0xff + 1; - -if (end >= start) - { - PCRE2_UCHAR *uchardata = *uchardptr; - -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UTF) != 0) - { - if (start < end) - { - *uchardata++ = XCL_RANGE; - uchardata += PRIV(ord2utf)(start, uchardata); - uchardata += PRIV(ord2utf)(end, uchardata); - } - else if (start == end) - { - *uchardata++ = XCL_SINGLE; - uchardata += PRIV(ord2utf)(start, uchardata); - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Without UTF support, character values are constrained by the bit length, - and can only be > 256 for 16-bit and 32-bit libraries. */ - -#if PCRE2_CODE_UNIT_WIDTH == 8 - {} -#else - if (start < end) - { - *uchardata++ = XCL_RANGE; - *uchardata++ = start; - *uchardata++ = end; - } - else if (start == end) - { - *uchardata++ = XCL_SINGLE; - *uchardata++ = start; - } -#endif - *uchardptr = uchardata; /* Updata extra data pointer */ - } -#else - (void)uchardptr; /* Avoid compiler warning */ -#endif /* SUPPORT_WIDE_CHARS */ - -return n8; /* Number of 8-bit characters */ -} - - - -/************************************************* -* Add a list of characters to a class * -*************************************************/ - -/* This function is used for adding a list of case-equivalent characters to a -class, and also for adding a list of horizontal or vertical whitespace. If the -list is in order (which it should be), ranges of characters are detected and -handled appropriately. This function is mutually recursive with the function -above. - -Arguments: - classbits the bit map for characters < 256 - uchardptr points to the pointer for extra data - options the options word - cb contains pointers to tables etc. - p points to row of 32-bit values, terminated by NOTACHAR - except character to omit; this is used when adding lists of - case-equivalent characters to avoid including the one we - already know about - -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ - -static unsigned int -add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, - compile_block *cb, const uint32_t *p, unsigned int except) -{ -unsigned int n8 = 0; -while (p[0] < NOTACHAR) - { - unsigned int n = 0; - if (p[0] != except) - { - while(p[n+1] == p[0] + n + 1) n++; - n8 += add_to_class(classbits, uchardptr, options, cb, p[0], p[n]); - } - p += n + 1; - } -return n8; -} - - - -/************************************************* -* Add characters not in a list to a class * -*************************************************/ - -/* This function is used for adding the complement of a list of horizontal or -vertical whitespace to a class. The list must be in order. - -Arguments: - classbits the bit map for characters < 256 - uchardptr points to the pointer for extra data - options the options word - cb contains pointers to tables etc. - p points to row of 32-bit values, terminated by NOTACHAR - -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ - -static unsigned int -add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, - uint32_t options, compile_block *cb, const uint32_t *p) -{ -BOOL utf = (options & PCRE2_UTF) != 0; -unsigned int n8 = 0; -if (p[0] > 0) - n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1); -while (p[0] < NOTACHAR) - { - while (p[1] == p[0] + 1) p++; - n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1, - (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1); - p++; - } -return n8; -} - - - -/************************************************* -* Process (*VERB) name for escapes * -*************************************************/ - -/* This function is called when the PCRE2_ALT_VERBNAMES option is set, to -process the characters in a verb's name argument. It is called twice, once with -codeptr == NULL, to find out the length of the processed name, and again to put -the name into memory. - -Arguments: - ptrptr pointer to the input pointer - codeptr pointer to the compiled code pointer - errorcodeptr pointer to the error code - options the options bits - utf TRUE if processing UTF - cb compile data block - -Returns: length of the processed name, or < 0 on error -*/ - -static int -process_verb_name(PCRE2_SPTR *ptrptr, PCRE2_UCHAR **codeptr, int *errorcodeptr, - uint32_t options, BOOL utf, compile_block *cb) -{ -int32_t arglen = 0; -BOOL inescq = FALSE; -PCRE2_SPTR ptr = *ptrptr; -PCRE2_UCHAR *code = (codeptr == NULL)? NULL : *codeptr; - -for (; ptr < cb->end_pattern; ptr++) - { - uint32_t x = *ptr; - - /* Skip over literals */ - - if (inescq) - { - if (x == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { - inescq = FALSE; - ptr++;; - continue; - } - } - - else /* Not a literal character */ - { - if (x == CHAR_RIGHT_PARENTHESIS) break; - - /* Skip over comments and whitespace in extended mode. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - PCRE2_SPTR wscptr = ptr; - while (MAX_255(x) && (cb->ctypes[x] & ctype_space) != 0) x = *(++ptr); - if (x == CHAR_NUMBER_SIGN) - { - ptr++; - while (*ptr != CHAR_NULL || ptr < cb->end_pattern) - { - if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ - { /* IS_NEWLINE sets cb->nllen. */ - ptr += cb->nllen; - break; - } - ptr++; -#ifdef SUPPORT_UNICODE - if (utf) FORWARDCHAR(ptr); -#endif - } - } - - /* If we have skipped any characters, restart the loop. */ - - if (ptr > wscptr) - { - ptr--; - continue; - } - } - - /* Process escapes */ - - if (x == '\\') - { - int rc; - *errorcodeptr = 0; - rc = PRIV(check_escape)(&ptr, cb->end_pattern, &x, errorcodeptr, options, - FALSE, cb); - *ptrptr = ptr; /* For possible error */ - if (*errorcodeptr != 0) return -1; - if (rc != 0) - { - if (rc == ESC_Q) - { - inescq = TRUE; - continue; - } - if (rc == ESC_E) continue; - *errorcodeptr = ERR40; - return -1; - } - } - } - - /* We have the next character in the name. */ - -#ifdef SUPPORT_UNICODE - if (utf) - { - if (code == NULL) /* Just want the length */ - { -#if PCRE2_CODE_UNIT_WIDTH == 8 - int i; - for (i = 0; i < PRIV(utf8_table1_size); i++) - if ((int)x <= PRIV(utf8_table1)[i]) break; - arglen += i; -#elif PCRE2_CODE_UNIT_WIDTH == 16 - if (x > 0xffff) arglen++; -#endif - } - else - { - PCRE2_UCHAR cbuff[8]; - x = PRIV(ord2utf)(x, cbuff); - memcpy(code, cbuff, CU2BYTES(x)); - code += x; - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Not UTF */ - { - if (code != NULL) *code++ = (PCRE2_UCHAR)x; - } - - arglen++; - - if ((unsigned int)arglen > MAX_MARK) - { - *errorcodeptr = ERR76; - *ptrptr = ptr; - return -1; - } - } - -/* Update the pointers before returning. */ - -*ptrptr = ptr; -if (codeptr != NULL) *codeptr = code; -return arglen; -} - - - -/************************************************* -* Macro for the next two functions * -*************************************************/ - -/* Both scan_for_captures() and compile_branch() use this macro to generate a -fragment of code that reads the characters of a name and sets its length -(checking for not being too long). Count the characters dynamically, to avoid -the possibility of integer overflow. The same macro is used for reading *VERB -names. */ - -#define READ_NAME(ctype, errno, errset) \ - namelen = 0; \ - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype) != 0) \ - { \ - ptr++; \ - namelen++; \ - if (namelen > MAX_NAME_SIZE) \ - { \ - errset = errno; \ - goto FAILED; \ - } \ - } - - - -/************************************************* -* Scan regex to identify named groups * -*************************************************/ - -/* This function is called first of all, to scan for named capturing groups so -that information about them is fully available to both the compiling scans. -It skips over everything except parenthesized items. - -Arguments: - ptrptr points to pointer to the start of the pattern - options compiling dynamic options - cb pointer to the compile data block - -Returns: zero on success or a non-zero error code, with pointer updated -*/ - -typedef struct nest_save { - uint16_t nest_depth; - uint16_t reset_group; - uint16_t max_group; - uint16_t flags; -} nest_save; - -#define NSF_RESET 0x0001u -#define NSF_EXTENDED 0x0002u -#define NSF_DUPNAMES 0x0004u - -static int scan_for_captures(PCRE2_SPTR *ptrptr, uint32_t options, - compile_block *cb) -{ -uint32_t c; -uint32_t delimiter; -uint32_t set, unset, *optset; -uint32_t skiptoket = 0; -uint16_t nest_depth = 0; -int errorcode = 0; -int escape; -int namelen; -int i; -BOOL inescq = FALSE; -BOOL isdupname; -BOOL utf = (options & PCRE2_UTF) != 0; -BOOL negate_class; -PCRE2_SPTR name; -PCRE2_SPTR start; -PCRE2_SPTR ptr = *ptrptr; -named_group *ng; -nest_save *top_nest = NULL; -nest_save *end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size); - -/* The size of the nest_save structure might not be a factor of the size of the -workspace. Therefore we must round down end_nests so as to correctly avoid -creating a nest_save that spans the end of the workspace. */ - -end_nests = (nest_save *)((char *)end_nests - - ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save))); - -/* Now scan the pattern */ - -for (; ptr < cb->end_pattern; ptr++) - { - c = *ptr; - - /* Parenthesized groups set skiptoket when all following characters up to the - next closing parenthesis must be ignored. The parenthesis itself must be - processed (to end the nested parenthesized item). */ - - if (skiptoket != 0) - { - if (c != CHAR_RIGHT_PARENTHESIS) continue; - skiptoket = 0; - } - - /* Skip over literals */ - - if (inescq) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { - inescq = FALSE; - ptr++; - } - continue; - } - - /* Skip over # comments and whitespace in extended mode. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - PCRE2_SPTR wscptr = ptr; - while (MAX_255(c) && (cb->ctypes[c] & ctype_space) != 0) c = *(++ptr); - if (c == CHAR_NUMBER_SIGN) - { - ptr++; - while (ptr < cb->end_pattern) - { - if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ - { /* IS_NEWLINE sets cb->nllen. */ - ptr += cb->nllen; - break; - } - ptr++; -#ifdef SUPPORT_UNICODE - if (utf) FORWARDCHAR(ptr); -#endif - } - } - - /* If we skipped any characters, restart the loop. Otherwise, we didn't see - a comment. */ - - if (ptr > wscptr) - { - ptr--; - continue; - } - } - - /* Process the next pattern item. */ - - switch(c) - { - default: /* Most characters are just skipped */ - break; - - /* Skip escapes except for \Q */ - - case CHAR_BACKSLASH: - errorcode = 0; - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &c, &errorcode, options, - FALSE, cb); - if (errorcode != 0) goto FAILED; - if (escape == ESC_Q) inescq = TRUE; - break; - - /* Skip a character class. The syntax is complicated so we have to - replicate some of what happens when a class is processed for real. */ - - case CHAR_LEFT_SQUARE_BRACKET: - if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_STARTWORD, 6) == 0 || - PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0) - { - ptr += 6; - break; - } - - /* If the first character is '^', set the negation flag (not actually used - here, except to recognize only one ^) and skip it. If the first few - characters (either before or after ^) are \Q\E or \E we skip them too. This - makes for compatibility with Perl. */ - - negate_class = FALSE; - for (;;) - { - c = *(++ptr); /* First character in class */ - if (c == CHAR_BACKSLASH) - { - if (ptr[1] == CHAR_E) - ptr++; - else if (PRIV(strncmp_c8)(ptr + 1, STR_Q STR_BACKSLASH STR_E, 3) == 0) - ptr += 3; - else - break; - } - else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) - negate_class = TRUE; - else break; - } - - if (c == CHAR_RIGHT_SQUARE_BRACKET && - (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) - break; - - /* Loop for the contents of the class */ - - for (;;) - { - PCRE2_SPTR tempptr; - - if (c == CHAR_NULL && ptr >= cb->end_pattern) - { - errorcode = ERR6; /* Missing terminating ']' */ - goto FAILED; - } - -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - { /* Braces are required because the */ - GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ - } -#endif - - /* Inside \Q...\E everything is literal except \E */ - - if (inescq) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */ - { - inescq = FALSE; /* Reset literal state */ - ptr++; /* Skip the 'E' */ - } - goto CONTINUE_CLASS; - } - - /* Skip POSIX class names. */ - if (c == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) - { - ptr = tempptr + 1; - } - else if (c == CHAR_BACKSLASH) - { - errorcode = 0; - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &c, &errorcode, - options, TRUE, cb); - if (errorcode != 0) goto FAILED; - if (escape == ESC_Q) inescq = TRUE; - } - - CONTINUE_CLASS: - c = *(++ptr); - if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; - } /* End of class-processing loop */ - break; - - /* This is the real work of this function - handling parentheses. */ - - case CHAR_LEFT_PARENTHESIS: - nest_depth++; - - if (ptr[1] != CHAR_QUESTION_MARK) - { - if (ptr[1] != CHAR_ASTERISK) - { - if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) cb->bracount++; - } - - /* (*something) - skip over a name, and then just skip to closing ket - unless PCRE2_ALT_VERBNAMES is set, in which case we have to process - escapes in the string after a verb name terminated by a colon. */ - - else - { - ptr += 2; - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) ptr++; - if (*ptr == CHAR_COLON && (options & PCRE2_ALT_VERBNAMES) != 0) - { - ptr++; - if (process_verb_name(&ptr, NULL, &errorcode, options, utf, cb) < 0) - goto FAILED; - } - else - { - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) - ptr++; - } - nest_depth--; - } - } - - /* Handle (?...) groups */ - - else switch(ptr[2]) - { - default: - ptr += 2; - if (ptr[0] == CHAR_R || /* (?R) */ - ptr[0] == CHAR_NUMBER_SIGN || /* (?#) */ - IS_DIGIT(ptr[0]) || /* (?n) */ - (ptr[0] == CHAR_MINUS && IS_DIGIT(ptr[1]))) /* (?-n) */ - { - skiptoket = ptr[0]; - break; - } - - /* Handle (?| and (?imsxJU: which are the only other valid forms. Both - need a new block on the nest stack. */ - - if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); - else if (++top_nest >= end_nests) - { - errorcode = ERR84; - goto FAILED; - } - top_nest->nest_depth = nest_depth; - top_nest->flags = 0; - if ((options & PCRE2_EXTENDED) != 0) top_nest->flags |= NSF_EXTENDED; - if ((options & PCRE2_DUPNAMES) != 0) top_nest->flags |= NSF_DUPNAMES; - - if (*ptr == CHAR_VERTICAL_LINE) - { - top_nest->reset_group = (uint16_t)cb->bracount; - top_nest->max_group = (uint16_t)cb->bracount; - top_nest->flags |= NSF_RESET; - cb->external_flags |= PCRE2_DUPCAPUSED; - break; - } - - /* Scan options */ - - top_nest->reset_group = 0; - top_nest->max_group = 0; - - set = unset = 0; - optset = &set; - - /* Need only track (?x: and (?J: at this stage */ - - while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) - { - switch (*ptr++) - { - case CHAR_MINUS: optset = &unset; break; - - case CHAR_x: *optset |= PCRE2_EXTENDED; break; - - case CHAR_J: - *optset |= PCRE2_DUPNAMES; - cb->external_flags |= PCRE2_JCHANGED; - break; - - case CHAR_i: - case CHAR_m: - case CHAR_s: - case CHAR_U: - break; - - default: - errorcode = ERR11; - ptr--; /* Correct the offset */ - goto FAILED; - } - } - - options = (options | set) & (~unset); - - /* If the options ended with ')' this is not the start of a nested - group with option changes, so the options change at this level. If the - previous level set up a nest block, discard the one we have just created. - Otherwise adjust it for the previous level. */ - - if (*ptr == CHAR_RIGHT_PARENTHESIS) - { - nest_depth--; - if (top_nest > (nest_save *)(cb->start_workspace) && - (top_nest-1)->nest_depth == nest_depth) top_nest --; - else top_nest->nest_depth = nest_depth; - } - break; - - /* Skip over a numerical or string argument for a callout. */ - - case CHAR_C: - ptr += 2; - if (ptr[1] == CHAR_RIGHT_PARENTHESIS) break; - if (IS_DIGIT(ptr[1])) - { - while (IS_DIGIT(ptr[1])) ptr++; - } - - /* Handle a string argument */ - - else - { - ptr++; - delimiter = 0; - for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) - { - if (*ptr == PRIV(callout_start_delims)[i]) - { - delimiter = PRIV(callout_end_delims)[i]; - break; - } - } - - if (delimiter == 0) - { - errorcode = ERR82; - goto FAILED; - } - - start = ptr; - do - { - if (++ptr >= cb->end_pattern) - { - errorcode = ERR81; - ptr = start; /* To give a more useful message */ - goto FAILED; - } - if (ptr[0] == delimiter && ptr[1] == delimiter) ptr += 2; - } - while (ptr[0] != delimiter); - } - - /* Check terminating ) */ - - if (ptr[1] != CHAR_RIGHT_PARENTHESIS) - { - errorcode = ERR39; - ptr++; - goto FAILED; - } - break; - - /* Conditional group */ - - case CHAR_LEFT_PARENTHESIS: - if (ptr[3] != CHAR_QUESTION_MARK) /* Not assertion or callout */ - { - nest_depth++; - ptr += 2; - break; - } - - /* Must be an assertion or a callout */ - - switch(ptr[4]) - { - case CHAR_LESS_THAN_SIGN: - if (ptr[5] != CHAR_EXCLAMATION_MARK && ptr[5] != CHAR_EQUALS_SIGN) - goto MISSING_ASSERTION; - /* Fall through */ - - case CHAR_C: - case CHAR_EXCLAMATION_MARK: - case CHAR_EQUALS_SIGN: - ptr++; - break; - - default: - MISSING_ASSERTION: - ptr += 3; /* To improve error message */ - errorcode = ERR28; - goto FAILED; - } - break; - - case CHAR_COLON: - case CHAR_GREATER_THAN_SIGN: - case CHAR_EQUALS_SIGN: - case CHAR_EXCLAMATION_MARK: - case CHAR_AMPERSAND: - case CHAR_PLUS: - ptr += 2; - break; - - case CHAR_P: - if (ptr[3] != CHAR_LESS_THAN_SIGN) - { - ptr += 3; - break; - } - ptr++; - c = CHAR_GREATER_THAN_SIGN; /* Terminator */ - goto DEFINE_NAME; - - case CHAR_LESS_THAN_SIGN: - if (ptr[3] == CHAR_EQUALS_SIGN || ptr[3] == CHAR_EXCLAMATION_MARK) - { - ptr += 3; - break; - } - c = CHAR_GREATER_THAN_SIGN; /* Terminator */ - goto DEFINE_NAME; - - case CHAR_APOSTROPHE: - c = CHAR_APOSTROPHE; /* Terminator */ - - DEFINE_NAME: - name = ptr = ptr + 3; - - if (*ptr == c) /* Empty name */ - { - errorcode = ERR62; - goto FAILED; - } - - if (IS_DIGIT(*ptr)) - { - errorcode = ERR44; /* Group name must start with non-digit */ - goto FAILED; - } - - if (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) == 0) - { - errorcode = ERR24; - goto FAILED; - } - - /* Advance ptr, set namelen and check its length. */ - READ_NAME(ctype_word, ERR48, errorcode); - - if (*ptr != c) - { - errorcode = ERR42; - goto FAILED; - } - - if (cb->names_found >= MAX_NAME_COUNT) - { - errorcode = ERR49; - goto FAILED; - } - - if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) - cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); - - /* We have a valid name for this capturing group. */ - - cb->bracount++; - - /* Scan the list to check for duplicates. For duplicate names, if the - number is the same, break the loop, which causes the name to be - discarded; otherwise, if DUPNAMES is not set, give an error. - If it is set, allow the name with a different number, but continue - scanning in case this is a duplicate with the same number. For - non-duplicate names, give an error if the number is duplicated. */ - - isdupname = FALSE; - ng = cb->named_groups; - for (i = 0; i < cb->names_found; i++, ng++) - { - if (namelen == ng->length && - PRIV(strncmp)(name, ng->name, (size_t)namelen) == 0) - { - if (ng->number == cb->bracount) break; - if ((options & PCRE2_DUPNAMES) == 0) - { - errorcode = ERR43; - goto FAILED; - } - isdupname = ng->isdup = TRUE; /* Mark as a duplicate */ - cb->dupnames = TRUE; /* Duplicate names exist */ - } - else if (ng->number == cb->bracount) - { - errorcode = ERR65; - goto FAILED; - } - } - - if (i < cb->names_found) break; /* Ignore duplicate with same number */ - - /* Increase the list size if necessary */ - - if (cb->names_found >= cb->named_group_list_size) - { - uint32_t newsize = cb->named_group_list_size * 2; - named_group *newspace = - cb->cx->memctl.malloc(newsize * sizeof(named_group), - cb->cx->memctl.memory_data); - if (newspace == NULL) - { - errorcode = ERR21; - goto FAILED; - } - - memcpy(newspace, cb->named_groups, - cb->named_group_list_size * sizeof(named_group)); - if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE) - cb->cx->memctl.free((void *)cb->named_groups, - cb->cx->memctl.memory_data); - cb->named_groups = newspace; - cb->named_group_list_size = newsize; - } - - /* Add this name to the list */ - - cb->named_groups[cb->names_found].name = name; - cb->named_groups[cb->names_found].length = (uint16_t)namelen; - cb->named_groups[cb->names_found].number = cb->bracount; - cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname; - cb->names_found++; - break; - } /* End of (? switch */ - break; /* End of ( handling */ - - /* At an alternation, reset the capture count if we are in a (?| group. */ - - case CHAR_VERTICAL_LINE: - if (top_nest != NULL && top_nest->nest_depth == nest_depth && - (top_nest->flags & NSF_RESET) != 0) - { - if (cb->bracount > top_nest->max_group) - top_nest->max_group = (uint16_t)cb->bracount; - cb->bracount = top_nest->reset_group; - } - break; - - /* At a right parenthesis, reset the capture count to the maximum if we - are in a (?| group and/or reset the extended option. */ - - case CHAR_RIGHT_PARENTHESIS: - if (top_nest != NULL && top_nest->nest_depth == nest_depth) - { - if ((top_nest->flags & NSF_RESET) != 0 && - top_nest->max_group > cb->bracount) - cb->bracount = top_nest->max_group; - if ((top_nest->flags & NSF_EXTENDED) != 0) options |= PCRE2_EXTENDED; - else options &= ~PCRE2_EXTENDED; - if ((top_nest->flags & NSF_DUPNAMES) != 0) options |= PCRE2_DUPNAMES; - else options &= ~PCRE2_DUPNAMES; - if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; - else top_nest--; - } - if (nest_depth == 0) /* Unmatched closing parenthesis */ - { - errorcode = ERR22; - goto FAILED; - } - nest_depth--; - break; - } - } - -if (nest_depth == 0) - { - cb->final_bracount = cb->bracount; - return 0; - } - -/* We give a special error for a missing closing parentheses after (?# because -it might otherwise be hard to see where the missing character is. */ - -errorcode = (skiptoket == CHAR_NUMBER_SIGN)? ERR18 : ERR14; - -FAILED: -*ptrptr = ptr; -return errorcode; -} - - - -/************************************************* -* Compile one branch * -*************************************************/ - -/* Scan the pattern, compiling it into the a vector. If the options are -changed during the branch, the pointer is used to change the external options -bits. This function is used during the pre-compile phase when we are trying -to find out the amount of memory needed, as well as during the real compile -phase. The value of lengthptr distinguishes the two phases. - -Arguments: - optionsptr pointer to the option bits - codeptr points to the pointer to the current code point - ptrptr points to the current pattern pointer - errorcodeptr points to error code variable - firstcuptr place to put the first required code unit - firstcuflagsptr place to put the first code unit flags, or a negative number - reqcuptr place to put the last required code unit - reqcuflagsptr place to put the last required code unit flags, or a negative number - bcptr points to current branch chain - cond_depth conditional nesting depth - cb contains pointers to tables etc. - lengthptr NULL during the real compile phase - points to length accumulator during pre-compile phase - -Returns: TRUE on success - FALSE, with *errorcodeptr set non-zero on error -*/ - -static BOOL -compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, - PCRE2_SPTR *ptrptr, int *errorcodeptr, - uint32_t *firstcuptr, int32_t *firstcuflagsptr, - uint32_t *reqcuptr, int32_t *reqcuflagsptr, - branch_chain *bcptr, int cond_depth, - compile_block *cb, size_t *lengthptr) -{ -int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ -int bravalue = 0; -uint32_t greedy_default, greedy_non_default; -uint32_t repeat_type, op_type; -uint32_t options = *optionsptr; /* May change dynamically */ -uint32_t firstcu, reqcu; -int32_t firstcuflags, reqcuflags; -uint32_t zeroreqcu, zerofirstcu; -int32_t zeroreqcuflags, zerofirstcuflags; -int32_t req_caseopt, reqvary, tempreqvary; -int after_manual_callout = 0; -int escape; -size_t length_prevgroup = 0; -register uint32_t c; -register PCRE2_UCHAR *code = *codeptr; -PCRE2_UCHAR *last_code = code; -PCRE2_UCHAR *orig_code = code; -PCRE2_UCHAR *tempcode; -BOOL inescq = FALSE; -BOOL groupsetfirstcu = FALSE; -PCRE2_SPTR ptr = *ptrptr; -PCRE2_SPTR tempptr; -PCRE2_UCHAR *previous = NULL; -PCRE2_UCHAR *previous_callout = NULL; -uint8_t classbits[32]; - -/* We can fish out the UTF setting once and for all into a BOOL, but we must -not do this for other options (e.g. PCRE2_EXTENDED) because they may change -dynamically as we process the pattern. */ - -#ifdef SUPPORT_UNICODE -BOOL utf = (options & PCRE2_UTF) != 0; -#if PCRE2_CODE_UNIT_WIDTH != 32 -PCRE2_UCHAR utf_units[6]; /* For setting up multi-cu chars */ -#endif - -#else /* No UTF support */ -BOOL utf = FALSE; -#endif - -/* Helper variables for OP_XCLASS opcode (for characters > 255). We define -class_uchardata always so that it can be passed to add_to_class() always, -though it will not be used in non-UTF 8-bit cases. This avoids having to supply -alternative calls for the different cases. */ - -PCRE2_UCHAR *class_uchardata; -#ifdef SUPPORT_WIDE_CHARS -BOOL xclass; -PCRE2_UCHAR *class_uchardata_base; -#endif - -/* Set up the default and non-default settings for greediness */ - -greedy_default = ((options & PCRE2_UNGREEDY) != 0); -greedy_non_default = greedy_default ^ 1; - -/* Initialize no first unit, no required unit. REQ_UNSET means "no char -matching encountered yet". It gets changed to REQ_NONE if we hit something that -matches a non-fixed first unit; reqcu just remains unset if we never find one. - -When we hit a repeat whose minimum is zero, we may have to adjust these values -to take the zero repeat into account. This is implemented by setting them to -zerofirstcu and zeroreqcu when such a repeat is encountered. The individual -item types that can be repeated set these backoff variables appropriately. */ - -firstcu = reqcu = zerofirstcu = zeroreqcu = 0; -firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; - -/* The variable req_caseopt contains either the REQ_CASELESS value or zero, -according to the current setting of the caseless flag. The REQ_CASELESS value -leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables -to record the case status of the value. This is used only for ASCII characters. -*/ - -req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; - -/* Switch on next character until the end of the branch */ - -for (;; ptr++) - { - BOOL negate_class; - BOOL should_flip_negation; - BOOL match_all_or_no_wide_chars; - BOOL possessive_quantifier; - BOOL is_quantifier; - BOOL is_recurse; - BOOL is_dupname; - BOOL reset_bracount; - int class_has_8bitchar; - int class_one_char; -#ifdef SUPPORT_WIDE_CHARS - BOOL xclass_has_prop; -#endif - int recno; /* Must be signed */ - int refsign; /* Must be signed */ - int terminator; /* Must be signed */ - unsigned int mclength; - unsigned int tempbracount; - uint32_t ec; - uint32_t newoptions; - uint32_t skipunits; - uint32_t subreqcu, subfirstcu; - int32_t subreqcuflags, subfirstcuflags; /* Must be signed */ - PCRE2_UCHAR mcbuffer[8]; - - /* Come here to restart the loop. */ - - REDO_LOOP: - - /* Get next character in the pattern */ - - c = *ptr; - - /* If we are at the end of a nested substitution, revert to the outer level - string. Nesting only happens one or two levels deep, and the inserted string - is always zero terminated. */ - - if (c == CHAR_NULL && cb->nestptr[0] != NULL) - { - ptr = cb->nestptr[0]; - cb->nestptr[0] = cb->nestptr[1]; - cb->nestptr[1] = NULL; - c = *ptr; - } - - /* If we are in the pre-compile phase, accumulate the length used for the - previous cycle of this loop. */ - - if (lengthptr != NULL) - { - if (code > cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ - { - *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? - ERR52 : ERR86; - goto FAILED; - } - - /* There is at least one situation where code goes backwards: this is the - case of a zero quantifier after a class (e.g. [ab]{0}). At compile time, - the class is simply eliminated. However, it is created first, so we have to - allow memory for it. Therefore, don't ever reduce the length at this point. - */ - - if (code < last_code) code = last_code; - - /* Paranoid check for integer overflow */ - - if (OFLOW_MAX - *lengthptr < (size_t)(code - last_code)) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += (size_t)(code - last_code); - - /* If "previous" is set and it is not at the start of the work space, move - it back to there, in order to avoid filling up the work space. Otherwise, - if "previous" is NULL, reset the current code pointer to the start. */ - - if (previous != NULL) - { - if (previous > orig_code) - { - memmove(orig_code, previous, (size_t)CU2BYTES(code - previous)); - code -= previous - orig_code; - previous = orig_code; - } - } - else code = orig_code; - - /* Remember where this code item starts so we can pick up the length - next time round. */ - - last_code = code; - } - - /* Before doing anything else we must handle all the special items that do - nothing, and which may come between an item and its quantifier. Otherwise, - when auto-callouts are enabled, a callout gets incorrectly inserted before - the quantifier is recognized. After recognizing a "do nothing" item, restart - the loop in case another one follows. */ - - /* If c is not NULL we are not at the end of the pattern. If it is NULL, we - may still be in the pattern with a NULL data item. In these cases, if we are - in \Q...\E, check for the \E that ends the literal string; if not, we have a - literal character. If not in \Q...\E, an isolated \E is ignored. */ - - if (c != CHAR_NULL || ptr < cb->end_pattern) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { - inescq = FALSE; - ptr++; - continue; - } - else if (inescq) /* Literal character */ - { - if (previous_callout != NULL) - { - if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ - complete_callout(previous_callout, ptr, cb); - previous_callout = NULL; - } - if ((options & PCRE2_AUTO_CALLOUT) != 0) - { - previous_callout = code; - code = auto_callout(code, ptr, cb); - } - goto NORMAL_CHAR; - } - - /* Check for the start of a \Q...\E sequence. We must do this here rather - than later in case it is immediately followed by \E, which turns it into a - "do nothing" sequence. */ - - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_Q) - { - inescq = TRUE; - ptr++; - continue; - } - } - - /* In extended mode, skip white space and #-comments that end at newline. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - PCRE2_SPTR wscptr = ptr; - while (MAX_255(c) && (cb->ctypes[c] & ctype_space) != 0) c = *(++ptr); - if (c == CHAR_NUMBER_SIGN) - { - ptr++; - while (ptr < cb->end_pattern) - { - if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ - { /* IS_NEWLINE sets cb->nllen. */ - ptr += cb->nllen; - break; - } - ptr++; -#ifdef SUPPORT_UNICODE - if (utf) FORWARDCHAR(ptr); -#endif - } - } - - /* If we skipped any characters, restart the loop. Otherwise, we didn't see - a comment. */ - - if (ptr > wscptr) goto REDO_LOOP; - } - - /* Skip over (?# comments. */ - - if (c == CHAR_LEFT_PARENTHESIS && ptr[1] == CHAR_QUESTION_MARK && - ptr[2] == CHAR_NUMBER_SIGN) - { - ptr += 3; - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR18; - goto FAILED; - } - continue; - } - - /* End of processing "do nothing" items. See if the next thing is a - quantifier. */ - - is_quantifier = - c == CHAR_ASTERISK || c == CHAR_PLUS || c == CHAR_QUESTION_MARK || - (c == CHAR_LEFT_CURLY_BRACKET && is_counted_repeat(ptr+1)); - - /* Fill in length of a previous callout and create an auto callout if - required, except when the next thing is a quantifier or when processing a - property substitution string for \w etc in UCP mode. */ - - if (!is_quantifier && cb->nestptr[0] == NULL) - { - if (previous_callout != NULL && after_manual_callout-- <= 0) - { - if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ - complete_callout(previous_callout, ptr, cb); - previous_callout = NULL; - } - - if ((options & PCRE2_AUTO_CALLOUT) != 0) - { - previous_callout = code; - code = auto_callout(code, ptr, cb); - } - } - - /* Process the next pattern item. */ - - switch(c) - { - /* ===================================================================*/ - /* The branch terminates at string end or | or ) */ - - case CHAR_NULL: - if (ptr < cb->end_pattern) goto NORMAL_CHAR; /* Zero data character */ - /* Fall through */ - - case CHAR_VERTICAL_LINE: - case CHAR_RIGHT_PARENTHESIS: - *firstcuptr = firstcu; - *firstcuflagsptr = firstcuflags; - *reqcuptr = reqcu; - *reqcuflagsptr = reqcuflags; - *codeptr = code; - *ptrptr = ptr; - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < (size_t)(code - last_code)) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += (size_t)(code - last_code); /* To include callout length */ - } - return TRUE; - - - /* ===================================================================*/ - /* Handle single-character metacharacters. In multiline mode, ^ disables - the setting of any following char as a first character. */ - - case CHAR_CIRCUMFLEX_ACCENT: - previous = NULL; - if ((options & PCRE2_MULTILINE) != 0) - { - if (firstcuflags == REQ_UNSET) - zerofirstcuflags = firstcuflags = REQ_NONE; - *code++ = OP_CIRCM; - } - else *code++ = OP_CIRC; - break; - - case CHAR_DOLLAR_SIGN: - previous = NULL; - *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; - break; - - /* There can never be a first char if '.' is first, whatever happens about - repeats. The value of reqcu doesn't change either. */ - - case CHAR_DOT: - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - previous = code; - *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY; - break; - - - /* ===================================================================*/ - /* Character classes. If the included characters are all < 256, we build a - 32-byte bitmap of the permitted characters, except in the special case - where there is only one such character. For negated classes, we build the - map as usual, then invert it at the end. However, we use a different opcode - so that data characters > 255 can be handled correctly. - - If the class contains characters outside the 0-255 range, a different - opcode is compiled. It may optionally have a bit map for characters < 256, - but those above are are explicitly listed afterwards. A flag byte tells - whether the bitmap is present, and whether this is a negated class or not. - - An isolated ']' character is not treated specially, so is just another data - character. In earlier versions of PCRE that used the original API there was - a "JavaScript compatibility mode" in which it gave an error. However, - JavaScript itself has changed in this respect so there is no longer any - need for this special handling. - - In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is - used for "start of word" and "end of word". As these are otherwise illegal - sequences, we don't break anything by recognizing them. They are replaced - by \b(?=\w) and \b(?<=\w) respectively. This can only happen at the top - nesting level, as no other inserted sequences will contains these oddities. - Sequences like [a[:<:]] are erroneous and are handled by the normal code - below. */ - - case CHAR_LEFT_SQUARE_BRACKET: - if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_STARTWORD, 6) == 0) - { - cb->nestptr[0] = ptr + 7; - ptr = sub_start_of_word; - goto REDO_LOOP; - } - - if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0) - { - cb->nestptr[0] = ptr + 7; - ptr = sub_end_of_word; - goto REDO_LOOP; - } - - /* Handle a real character class. */ - - previous = code; - - /* PCRE supports POSIX class stuff inside a class. Perl gives an error if - they are encountered at the top level, so we'll do that too. */ - - if ((ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && - check_posix_syntax(ptr, &tempptr)) - { - *errorcodeptr = (ptr[1] == CHAR_COLON)? ERR12 : ERR13; - goto FAILED; - } - - /* If the first character is '^', set the negation flag and skip it. Also, - if the first few characters (either before or after ^) are \Q\E or \E we - skip them too. This makes for compatibility with Perl. */ - - negate_class = FALSE; - for (;;) - { - c = *(++ptr); - if (c == CHAR_BACKSLASH) - { - if (ptr[1] == CHAR_E) - ptr++; - else if (PRIV(strncmp_c8)(ptr + 1, STR_Q STR_BACKSLASH STR_E, 3) == 0) - ptr += 3; - else - break; - } - else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) - negate_class = TRUE; - else break; - } - - /* Empty classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set. Otherwise, - an initial ']' is taken as a data character -- the code below handles - that. When empty classes are allowed, [] must always fail, so generate - OP_FAIL, whereas [^] must match any character, so generate OP_ALLANY. */ - - if (c == CHAR_RIGHT_SQUARE_BRACKET && - (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) - { - *code++ = negate_class? OP_ALLANY : OP_FAIL; - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - break; - } - - /* If a non-extended class contains a negative special such as \S, we need - to flip the negation flag at the end, so that support for characters > 255 - works correctly (they are all included in the class). An extended class may - need to insert specific matching or non-matching code for wide characters. - */ - - should_flip_negation = match_all_or_no_wide_chars = FALSE; - - /* Extended class (xclass) will be used when characters > 255 - might match. */ - -#ifdef SUPPORT_WIDE_CHARS - xclass = FALSE; - class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ - class_uchardata_base = class_uchardata; /* Save the start */ -#endif - - /* For optimization purposes, we track some properties of the class: - class_has_8bitchar will be non-zero if the class contains at least one 256 - character with a code point less than 256; class_one_char will be 1 if the - class contains just one character; xclass_has_prop will be TRUE if Unicode - property checks are present in the class. */ - - class_has_8bitchar = 0; - class_one_char = 0; -#ifdef SUPPORT_WIDE_CHARS - xclass_has_prop = FALSE; -#endif - - /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map - in a temporary bit of memory, in case the class contains fewer than two - 8-bit characters because in that case the compiled code doesn't use the bit - map. */ - - memset(classbits, 0, 32 * sizeof(uint8_t)); - - /* Process characters until ] is reached. As the test is at the end of the - loop, an initial ] is taken as a data character. At the start of the loop, - c contains the first code unit of the character. If it is zero, check for - the end of the pattern, to allow binary zero as data. */ - - for(;;) - { - PCRE2_SPTR oldptr; -#ifdef EBCDIC - BOOL range_is_literal = TRUE; -#endif - - if (c == CHAR_NULL && ptr >= cb->end_pattern) - { - *errorcodeptr = ERR6; /* Missing terminating ']' */ - goto FAILED; - } - -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - { /* Braces are required because the */ - GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ - } -#endif - - /* Inside \Q...\E everything is literal except \E */ - - if (inescq) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */ - { - inescq = FALSE; /* Reset literal state */ - ptr++; /* Skip the 'E' */ - goto CONTINUE_CLASS; /* Carry on with next char */ - } - goto CHECK_RANGE; /* Could be range if \E follows */ - } - - /* Handle POSIX class names. Perl allows a negation extension of the - form [:^name:]. A square bracket that doesn't match the syntax is - treated as a literal. We also recognize the POSIX constructions - [.ch.] and [=ch=] ("collating elements") and fault them, as Perl - 5.6 and 5.8 do. */ - - if (c == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) - { - BOOL local_negate = FALSE; - int posix_class, taboffset, tabopt; - register const uint8_t *cbits = cb->cbits; - uint8_t pbits[32]; - - if (ptr[1] != CHAR_COLON) - { - *errorcodeptr = ERR13; - goto FAILED; - } - - ptr += 2; - if (*ptr == CHAR_CIRCUMFLEX_ACCENT) - { - local_negate = TRUE; - should_flip_negation = TRUE; /* Note negative special */ - ptr++; - } - - posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); - if (posix_class < 0) - { - *errorcodeptr = ERR30; - goto FAILED; - } - - /* If matching is caseless, upper and lower are converted to - alpha. This relies on the fact that the class table starts with - alpha, lower, upper as the first 3 entries. */ - - if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2) - posix_class = 0; - - /* When PCRE2_UCP is set, some of the POSIX classes are converted to - different escape sequences that use Unicode properties \p or \P. Others - that are not available via \p or \P generate XCL_PROP/XCL_NOTPROP - directly. UCP support is not available unless UTF support is.*/ - -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UCP) != 0) - { - unsigned int ptype = 0; - int pc = posix_class + ((local_negate)? POSIX_SUBSIZE/2 : 0); - - /* The posix_substitutes table specifies which POSIX classes can be - converted to \p or \P items. This can only happen at top nestling - level, as there will never be a POSIX class in a string that is - substituted for something else. */ - - if (posix_substitutes[pc] != NULL) - { - cb->nestptr[0] = tempptr + 1; - ptr = posix_substitutes[pc] - 1; - goto CONTINUE_CLASS; - } - - /* There are three other classes that generate special property calls - that are recognized only in an XCLASS. */ - - else switch(posix_class) - { - case PC_GRAPH: - ptype = PT_PXGRAPH; - /* Fall through */ - case PC_PRINT: - if (ptype == 0) ptype = PT_PXPRINT; - /* Fall through */ - case PC_PUNCT: - if (ptype == 0) ptype = PT_PXPUNCT; - *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP; - *class_uchardata++ = (PCRE2_UCHAR)ptype; - *class_uchardata++ = 0; - xclass_has_prop = TRUE; - ptr = tempptr + 1; - goto CONTINUE_CLASS; - - /* For the other POSIX classes (ascii, xdigit) we are going to fall - through to the non-UCP case and build a bit map for characters with - code points less than 256. However, if we are in a negated POSIX - class, characters with code points greater than 255 must either all - match or all not match, depending on whether the whole class is not - or is negated. For example, for [[:^ascii:]... they must all match, - whereas for [^[:^xdigit:]... they must not. - - In the special case where there are no xclass items, this is - automatically handled by the use of OP_CLASS or OP_NCLASS, but an - explicit range is needed for OP_XCLASS. Setting a flag here causes - the range to be generated later when it is known that OP_XCLASS is - required. */ - - default: - match_all_or_no_wide_chars |= local_negate; - break; - } - } -#endif /* SUPPORT_UNICODE */ - - /* In the non-UCP case, or when UCP makes no difference, we build the - bit map for the POSIX class in a chunk of local store because we may be - adding and subtracting from it, and we don't want to subtract bits that - may be in the main map already. At the end we or the result into the - bit map that is being built. */ - - posix_class *= 3; - - /* Copy in the first table (always present) */ - - memcpy(pbits, cbits + posix_class_maps[posix_class], - 32 * sizeof(uint8_t)); - - /* If there is a second table, add or remove it as required. */ - - taboffset = posix_class_maps[posix_class + 1]; - tabopt = posix_class_maps[posix_class + 2]; - - if (taboffset >= 0) - { - if (tabopt >= 0) - for (c = 0; c < 32; c++) pbits[c] |= cbits[(int)c + taboffset]; - else - for (c = 0; c < 32; c++) pbits[c] &= ~cbits[(int)c + taboffset]; - } - - /* Now see if we need to remove any special characters. An option - value of 1 removes vertical space and 2 removes underscore. */ - - if (tabopt < 0) tabopt = -tabopt; - if (tabopt == 1) pbits[1] &= ~0x3c; - else if (tabopt == 2) pbits[11] &= 0x7f; - - /* Add the POSIX table or its complement into the main table that is - being built and we are done. */ - - if (local_negate) - for (c = 0; c < 32; c++) classbits[c] |= ~pbits[c]; - else - for (c = 0; c < 32; c++) classbits[c] |= pbits[c]; - - ptr = tempptr + 1; - /* Every class contains at least one < 256 character. */ - class_has_8bitchar = 1; - /* Every class contains at least two characters. */ - class_one_char = 2; - goto CONTINUE_CLASS; /* End of POSIX syntax handling */ - } - - /* Backslash may introduce a single character, or it may introduce one - of the specials, which just set a flag. The sequence \b is a special - case. Inside a class (and only there) it is treated as backspace. We - assume that other escapes have more than one character in them, so - speculatively set both class_has_8bitchar and class_one_char bigger - than one. Unrecognized escapes fall through and are faulted. */ - - if (c == CHAR_BACKSLASH) - { - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &ec, errorcodeptr, - options, TRUE, cb); - if (*errorcodeptr != 0) goto FAILED; - if (escape == 0) /* Escaped single char */ - { - c = ec; -#ifdef EBCDIC - range_is_literal = FALSE; -#endif - } - else if (escape == ESC_b) c = CHAR_BS; /* \b is backspace in a class */ - else if (escape == ESC_N) /* \N is not supported in a class */ - { - *errorcodeptr = ERR71; - goto FAILED; - } - else if (escape == ESC_Q) /* Handle start of quoted string */ - { - if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) - { - ptr += 2; /* avoid empty string */ - } - else inescq = TRUE; - goto CONTINUE_CLASS; - } - else if (escape == ESC_E) goto CONTINUE_CLASS; /* Ignore orphan \E */ - - else /* Handle \d-type escapes */ - { - register const uint8_t *cbits = cb->cbits; - /* Every class contains at least two < 256 characters. */ - class_has_8bitchar++; - /* Every class contains at least two characters. */ - class_one_char += 2; - - switch (escape) - { -#ifdef SUPPORT_UNICODE - case ESC_du: /* These are the values given for \d etc */ - case ESC_DU: /* when PCRE2_UCP is set. We replace the */ - case ESC_wu: /* escape sequence with an appropriate \p */ - case ESC_WU: /* or \P to test Unicode properties instead */ - case ESC_su: /* of the default ASCII testing. This might be */ - case ESC_SU: /* a 2nd-level nesting for [[:<:]] or [[:>:]]. */ - cb->nestptr[1] = cb->nestptr[0]; - cb->nestptr[0] = ptr; - ptr = substitutes[escape - ESC_DU] - 1; /* Just before substitute */ - class_has_8bitchar--; /* Undo! */ - break; -#endif - case ESC_d: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit]; - break; - - case ESC_D: - should_flip_negation = TRUE; - for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_digit]; - break; - - case ESC_w: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_word]; - break; - - case ESC_W: - should_flip_negation = TRUE; - for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word]; - break; - - /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl - 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was - previously set by something earlier in the character class. - Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so - we could just adjust the appropriate bit. From PCRE 8.34 we no - longer treat \s and \S specially. */ - - case ESC_s: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_space]; - break; - - case ESC_S: - should_flip_negation = TRUE; - for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_space]; - break; - - /* The rest apply in both UCP and non-UCP cases. */ - - case ESC_h: - (void)add_list_to_class(classbits, &class_uchardata, options, cb, - PRIV(hspace_list), NOTACHAR); - break; - - case ESC_H: - (void)add_not_list_to_class(classbits, &class_uchardata, options, - cb, PRIV(hspace_list)); - break; - - case ESC_v: - (void)add_list_to_class(classbits, &class_uchardata, options, cb, - PRIV(vspace_list), NOTACHAR); - break; - - case ESC_V: - (void)add_not_list_to_class(classbits, &class_uchardata, options, - cb, PRIV(vspace_list)); - break; - - case ESC_p: - case ESC_P: -#ifdef SUPPORT_UNICODE - { - BOOL negated; - unsigned int ptype = 0, pdata = 0; - if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr, cb)) - goto FAILED; - *class_uchardata++ = ((escape == ESC_p) != negated)? - XCL_PROP : XCL_NOTPROP; - *class_uchardata++ = ptype; - *class_uchardata++ = pdata; - xclass_has_prop = TRUE; - class_has_8bitchar--; /* Undo! */ - } - break; -#else - *errorcodeptr = ERR45; - goto FAILED; -#endif - /* Unrecognized escapes are faulted. */ - - default: - *errorcodeptr = ERR7; - goto FAILED; - } - - /* Handled \d-type escape */ - - goto CONTINUE_CLASS; - } - - /* Control gets here if the escape just defined a single character. - This is in c and may be greater than 256. */ - - escape = 0; - } /* End of backslash handling */ - - /* A character may be followed by '-' to form a range. However, Perl does - not permit ']' to be the end of the range. A '-' character at the end is - treated as a literal. Perl ignores orphaned \E sequences entirely. The - code for handling \Q and \E is messy. */ - - CHECK_RANGE: - while (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) - { - inescq = FALSE; - ptr += 2; - } - oldptr = ptr; - - /* Remember if \r or \n were explicitly used */ - - if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; - - /* Check for range */ - - if (!inescq && ptr[1] == CHAR_MINUS) - { - uint32_t d; - ptr += 2; - while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) ptr += 2; - - /* If we hit \Q (not followed by \E) at this point, go into escaped - mode. */ - - while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_Q) - { - ptr += 2; - if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { ptr += 2; continue; } - inescq = TRUE; - break; - } - - /* Minus (hyphen) at the end of a class is treated as a literal, so put - back the pointer and jump to handle the character that preceded it. */ - - if (*ptr == CHAR_NULL || (!inescq && *ptr == CHAR_RIGHT_SQUARE_BRACKET)) - { - ptr = oldptr; - goto CLASS_SINGLE_CHARACTER; - } - - /* Otherwise, we have a potential range; pick up the next character */ - -#ifdef SUPPORT_UNICODE - if (utf) - { /* Braces are required because the */ - GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ - } - else -#endif - d = *ptr; /* Not UTF mode */ - - /* The second part of a range can be a single-character escape - sequence, but not any of the other escapes. Perl treats a hyphen as a - literal in such circumstances. However, in Perl's warning mode, a - warning is given, so PCRE now faults it as it is almost certainly a - mistake on the user's part. */ - - if (!inescq) - { - if (d == CHAR_BACKSLASH) - { - int descape; - descape = PRIV(check_escape)(&ptr, cb->end_pattern, &d, - errorcodeptr, options, TRUE, cb); - if (*errorcodeptr != 0) goto FAILED; -#ifdef EBCDIC - range_is_literal = FALSE; -#endif - /* 0 means a character was put into d; \b is backspace; any other - special causes an error. */ - - if (descape != 0) - { - if (descape == ESC_b) d = CHAR_BS; else - { - *errorcodeptr = ERR50; - goto FAILED; - } - } - } - - /* A hyphen followed by a POSIX class is treated in the same way. */ - - else if (d == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && - check_posix_syntax(ptr, &tempptr)) - { - *errorcodeptr = ERR50; - goto FAILED; - } - } - - /* Check that the two values are in the correct order. Optimize - one-character ranges. */ - - if (d < c) - { - *errorcodeptr = ERR8; - goto FAILED; - } - if (d == c) goto CLASS_SINGLE_CHARACTER; /* A few lines below */ - - /* We have found a character range, so single character optimizations - cannot be done anymore. Any value greater than 1 indicates that there - is more than one character. */ - - class_one_char = 2; - - /* Remember an explicit \r or \n, and add the range to the class. */ - - if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; - - /* In an EBCDIC environment, Perl treats alphabetic ranges specially - because there are holes in the encoding, and simply using the range A-Z - (for example) would include the characters in the holes. This applies - only to literal ranges; [\xC1-\xE9] is different to [A-Z]. */ - -#ifdef EBCDIC - if (range_is_literal && - (cb->ctypes[c] & ctype_letter) != 0 && - (cb->ctypes[d] & ctype_letter) != 0 && - (c <= CHAR_z) == (d <= CHAR_z)) - { - uint32_t uc = (c <= CHAR_z)? 0 : 64; - uint32_t C = c - uc; - uint32_t D = d - uc; - - if (C <= CHAR_i) - { - class_has_8bitchar += - add_to_class(classbits, &class_uchardata, options, cb, C + uc, - ((D < CHAR_i)? D : CHAR_i) + uc); - C = CHAR_j; - } - - if (C <= D && C <= CHAR_r) - { - class_has_8bitchar += - add_to_class(classbits, &class_uchardata, options, cb, C + uc, - ((D < CHAR_r)? D : CHAR_r) + uc); - C = CHAR_s; - } - - if (C <= D) - { - class_has_8bitchar += - add_to_class(classbits, &class_uchardata, options, cb, C + uc, - D + uc); - } - } - else -#endif - class_has_8bitchar += - add_to_class(classbits, &class_uchardata, options, cb, c, d); - goto CONTINUE_CLASS; /* Go get the next char in the class */ - } - - /* Handle a single character - we can get here for a normal non-escape - char, or after \ that introduces a single character or for an apparent - range that isn't. Only the value 1 matters for class_one_char, so don't - increase it if it is already 2 or more ... just in case there's a class - with a zillion characters in it. */ - - CLASS_SINGLE_CHARACTER: - if (class_one_char < 2) class_one_char++; - - /* If class_one_char is 1 and xclass_has_prop is false, we have the first - single character in the class, and there have been no prior ranges, or - XCLASS items generated by escapes. If this is the final character in the - class, we can optimize by turning the item into a 1-character OP_CHAR[I] - if it's positive, or OP_NOT[I] if it's negative. In the positive case, it - can cause firstcu to be set. Otherwise, there can be no first char if - this item is first, whatever repeat count may follow. In the case of - reqcu, save the previous value for reinstating. */ - - if (!inescq && -#ifdef SUPPORT_UNICODE - !xclass_has_prop && -#endif - class_one_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) - { - ptr++; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - if (negate_class) - { -#ifdef SUPPORT_UNICODE - int d; -#endif - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - - /* For caseless UTF mode, check whether this character has more than - one other case. If so, generate a special OP_NOTPROP item instead of - OP_NOTI. */ - -#ifdef SUPPORT_UNICODE - if (utf && (options & PCRE2_CASELESS) != 0 && - (d = UCD_CASESET(c)) != 0) - { - *code++ = OP_NOTPROP; - *code++ = PT_CLIST; - *code++ = d; - } - else -#endif - /* Char has only one other case, or UCP not available */ - - { - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT; - code += PUTCHAR(c, code); - } - - /* We are finished with this character class */ - - goto END_CLASS; - } - - /* For a single, positive character, get the value into mcbuffer, and - then we can handle this with the normal one-character code. */ - - mclength = PUTCHAR(c, mcbuffer); - goto ONE_CHAR; - } /* End of 1-char optimization */ - - /* There is more than one character in the class, or an XCLASS item - has been generated. Add this character to the class. */ - - class_has_8bitchar += - add_to_class(classbits, &class_uchardata, options, cb, c, c); - - /* Continue to the next character in the class. Closing square bracket - not within \Q..\E ends the class. A NULL character terminates a - nested substitution string, but may be a data character in the main - pattern (tested at the start of this loop). */ - - CONTINUE_CLASS: - c = *(++ptr); - if (c == CHAR_NULL && cb->nestptr[0] != NULL) - { - ptr = cb->nestptr[0]; - cb->nestptr[0] = cb->nestptr[1]; - cb->nestptr[1] = NULL; - c = *(++ptr); - } - -#ifdef SUPPORT_WIDE_CHARS - /* If any wide characters have been encountered, set xclass = TRUE. Then, - in the pre-compile phase, accumulate the length of the wide characters - and reset the pointer. This is so that very large classes that contain a - zillion wide characters do not overwrite the work space (which is on the - stack). */ - - if (class_uchardata > class_uchardata_base) - { - xclass = TRUE; - if (lengthptr != NULL) - { - *lengthptr += class_uchardata - class_uchardata_base; - class_uchardata = class_uchardata_base; - } - } -#endif - /* An unescaped ] ends the class */ - - if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; - } /* End of main class-processing loop */ - - /* If this is the first thing in the branch, there can be no first char - setting, whatever the repeat count. Any reqcu setting must remain - unchanged after any kind of repeat. */ - - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - /* If there are characters with values > 255, or Unicode property settings - (\p or \P), we have to compile an extended class, with its own opcode, - unless there were no property settings and there was a negated special such - as \S in the class, and PCRE2_UCP is not set, because in that case all - characters > 255 are in or not in the class, so any that were explicitly - given as well can be ignored. - - In the UCP case, if certain negated POSIX classes ([:^ascii:] or - [^:xdigit:]) were present in a class, we either have to match or not match - all wide characters (depending on whether the whole class is or is not - negated). This requirement is indicated by match_all_or_no_wide_chars being - true. We do this by including an explicit range, which works in both cases. - - If, when generating an xclass, there are no characters < 256, we can omit - the bitmap in the actual compiled code. */ - -#ifdef SUPPORT_WIDE_CHARS -#ifdef SUPPORT_UNICODE - if (xclass && (xclass_has_prop || !should_flip_negation || - (options & PCRE2_UCP) != 0)) -#elif PCRE2_CODE_UNIT_WIDTH != 8 - if (xclass && (xclass_has_prop || !should_flip_negation)) -#endif - { - if (match_all_or_no_wide_chars) - { - *class_uchardata++ = XCL_RANGE; - class_uchardata += PRIV(ord2utf)(0x100, class_uchardata); - class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata); - } - *class_uchardata++ = XCL_END; /* Marks the end of extra data */ - *code++ = OP_XCLASS; - code += LINK_SIZE; - *code = negate_class? XCL_NOT:0; - if (xclass_has_prop) *code |= XCL_HASPROP; - - /* If the map is required, move up the extra data to make room for it; - otherwise just move the code pointer to the end of the extra data. */ - - if (class_has_8bitchar > 0) - { - *code++ |= XCL_MAP; - memmove(code + (32 / sizeof(PCRE2_UCHAR)), code, - CU2BYTES(class_uchardata - code)); - if (negate_class && !xclass_has_prop) - for (c = 0; c < 32; c++) classbits[c] = ~classbits[c]; - memcpy(code, classbits, 32); - code = class_uchardata + (32 / sizeof(PCRE2_UCHAR)); - } - else code = class_uchardata; - - /* Now fill in the complete length of the item */ - - PUT(previous, 1, (int)(code - previous)); - break; /* End of class handling */ - } -#endif - - /* If there are no characters > 255, or they are all to be included or - excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the - whole class was negated and whether there were negative specials such as \S - (non-UCP) in the class. Then copy the 32-byte map into the code vector, - negating it if necessary. */ - - *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; - if (lengthptr == NULL) /* Save time in the pre-compile phase */ - { - if (negate_class) - for (c = 0; c < 32; c++) classbits[c] = ~classbits[c]; - memcpy(code, classbits, 32); - } - code += 32 / sizeof(PCRE2_UCHAR); - - END_CLASS: - break; - - - /* ===================================================================*/ - /* Various kinds of repeat; '{' is not necessarily a quantifier, but this - has been tested above. */ - - case CHAR_LEFT_CURLY_BRACKET: - if (!is_quantifier) goto NORMAL_CHAR; - ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorcodeptr); - if (*errorcodeptr != 0) goto FAILED; - goto REPEAT; - - case CHAR_ASTERISK: - repeat_min = 0; - repeat_max = -1; - goto REPEAT; - - case CHAR_PLUS: - repeat_min = 1; - repeat_max = -1; - goto REPEAT; - - case CHAR_QUESTION_MARK: - repeat_min = 0; - repeat_max = 1; - - REPEAT: - if (previous == NULL) - { - *errorcodeptr = ERR9; - goto FAILED; - } - - if (repeat_min == 0) - { - firstcu = zerofirstcu; /* Adjust for zero repeat */ - firstcuflags = zerofirstcuflags; - reqcu = zeroreqcu; /* Ditto */ - reqcuflags = zeroreqcuflags; - } - - /* Remember whether this is a variable length repeat */ - - reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; - - op_type = 0; /* Default single-char op codes */ - possessive_quantifier = FALSE; /* Default not possessive quantifier */ - - /* Save start of previous item, in case we have to move it up in order to - insert something before it. */ - - tempcode = previous; - - /* Before checking for a possessive quantifier, we must skip over - whitespace and comments in extended mode because Perl allows white space at - this point. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - ptr++; - for (;;) - { - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_space) != 0) ptr++; - if (*ptr != CHAR_NUMBER_SIGN) break; - ptr++; - while (ptr < cb->end_pattern) - { - if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ - { /* IS_NEWLINE sets cb->nllen. */ - ptr += cb->nllen; - break; - } - ptr++; -#ifdef SUPPORT_UNICODE - if (utf) FORWARDCHAR(ptr); -#endif - } /* Loop for comment characters */ - } /* Loop for multiple comments */ - ptr--; /* Last code unit of previous character. */ - } - - /* If the next character is '+', we have a possessive quantifier. This - implies greediness, whatever the setting of the PCRE2_UNGREEDY option. - If the next character is '?' this is a minimizing repeat, by default, - but if PCRE2_UNGREEDY is set, it works the other way round. We change the - repeat type to the non-default. */ - - if (ptr[1] == CHAR_PLUS) - { - repeat_type = 0; /* Force greedy */ - possessive_quantifier = TRUE; - ptr++; - } - else if (ptr[1] == CHAR_QUESTION_MARK) - { - repeat_type = greedy_non_default; - ptr++; - } - else repeat_type = greedy_default; - - /* If the repeat is {1} we can ignore it. */ - - if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; - - /* If previous was a recursion call, wrap it in atomic brackets so that - previous becomes the atomic group. All recursions were so wrapped in the - past, but it no longer happens for non-repeated recursions. In fact, the - repeated ones could be re-implemented independently so as not to need this, - but for the moment we rely on the code for repeating groups. */ - - if (*previous == OP_RECURSE) - { - memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); - *previous = OP_ONCE; - PUT(previous, 1, 2 + 2*LINK_SIZE); - previous[2 + 2*LINK_SIZE] = OP_KET; - PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); - code += 2 + 2 * LINK_SIZE; - length_prevgroup = 3 + 3*LINK_SIZE; - } - - /* Now handle repetition for the different types of item. */ - - /* If previous was a character or negated character match, abolish the item - and generate a repeat item instead. If a char item has a minimum of more - than one, ensure that it is set in reqcu - it might not be if a sequence - such as x{3} is the first thing in a branch because the x will have gone - into firstcu instead. */ - - if (*previous == OP_CHAR || *previous == OP_CHARI - || *previous == OP_NOT || *previous == OP_NOTI) - { - switch (*previous) - { - default: /* Make compiler happy. */ - case OP_CHAR: op_type = OP_STAR - OP_STAR; break; - case OP_CHARI: op_type = OP_STARI - OP_STAR; break; - case OP_NOT: op_type = OP_NOTSTAR - OP_STAR; break; - case OP_NOTI: op_type = OP_NOTSTARI - OP_STAR; break; - } - - /* Deal with UTF characters that take up more than one code unit. It's - easier to write this out separately than try to macrify it. Use c to - hold the length of the character in code units, plus UTF_LENGTH to flag - that it's a length rather than a small character. */ - -#ifdef MAYBE_UTF_MULTI - if (utf && NOT_FIRSTCU(code[-1])) - { - PCRE2_UCHAR *lastchar = code - 1; - BACKCHAR(lastchar); - c = (int)(code - lastchar); /* Length of UTF character */ - memcpy(utf_units, lastchar, CU2BYTES(c)); /* Save the char */ - c |= UTF_LENGTH; /* Flag c as a length */ - } - else -#endif /* MAYBE_UTF_MULTI */ - - /* Handle the case of a single charater - either with no UTF support, or - with UTF disabled, or for a single-code-unit UTF character. */ - { - c = code[-1]; - if (*previous <= OP_CHARI && repeat_min > 1) - { - reqcu = c; - reqcuflags = req_caseopt | cb->req_varyopt; - } - } - - goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ - } - - /* If previous was a character type match (\d or similar), abolish it and - create a suitable repeat item. The code is shared with single-character - repeats by setting op_type to add a suitable offset into repeat_type. Note - the the Unicode property types will be present only when SUPPORT_UNICODE is - defined, but we don't wrap the little bits of code here because it just - makes it horribly messy. */ - - else if (*previous < OP_EODN) - { - PCRE2_UCHAR *oldcode; - int prop_type, prop_value; - op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ - c = *previous; /* Save previous opcode */ - if (c == OP_PROP || c == OP_NOTPROP) - { - prop_type = previous[1]; - prop_value = previous[2]; - } - else - { - /* Come here from just above with a character in c */ - OUTPUT_SINGLE_REPEAT: - prop_type = prop_value = -1; - } - - /* At this point we either have prop_type == prop_value == -1 and either - a code point or a character type that is not OP_[NOT]PROP in c, or we - have OP_[NOT]PROP in c and prop_type/prop_value not negative. */ - - oldcode = code; /* Save where we were */ - code = previous; /* Usually overwrite previous item */ - - /* If the maximum is zero then the minimum must also be zero; Perl allows - this case, so we do too - by simply omitting the item altogether. */ - - if (repeat_max == 0) goto END_REPEAT; - - /* Combine the op_type with the repeat_type */ - - repeat_type += op_type; - - /* A minimum of zero is handled either as the special case * or ?, or as - an UPTO, with the maximum given. */ - - if (repeat_min == 0) - { - if (repeat_max == -1) *code++ = OP_STAR + repeat_type; - else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; - else - { - *code++ = OP_UPTO + repeat_type; - PUT2INC(code, 0, repeat_max); - } - } - - /* A repeat minimum of 1 is optimized into some special cases. If the - maximum is unlimited, we use OP_PLUS. Otherwise, the original item is - left in place and, if the maximum is greater than 1, we use OP_UPTO with - one less than the maximum. */ - - else if (repeat_min == 1) - { - if (repeat_max == -1) - *code++ = OP_PLUS + repeat_type; - else - { - code = oldcode; /* Leave previous item in place */ - if (repeat_max == 1) goto END_REPEAT; - *code++ = OP_UPTO + repeat_type; - PUT2INC(code, 0, repeat_max - 1); - } - } - - /* The case {n,n} is just an EXACT, while the general case {n,m} is - handled as an EXACT followed by an UPTO or STAR or QUERY. */ - - else - { - *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ - PUT2INC(code, 0, repeat_min); - - /* Unless repeat_max equals repeat_min, fill in the data for EXACT, and - then generate the second opcode. In UTF mode, multi-code-unit - characters have their length in c, with the UTF_LENGTH bit as a flag, - and the code units in utf_units. For a repeated Unicode property match, - there are two extra values that define the required property, and c - never has the UTF_LENGTH bit set. */ - - if (repeat_max != repeat_min) - { -#ifdef MAYBE_UTF_MULTI - if (utf && (c & UTF_LENGTH) != 0) - { - memcpy(code, utf_units, CU2BYTES(c & 7)); - code += c & 7; - } - else -#endif /* MAYBE_UTF_MULTI */ - { - *code++ = c; - if (prop_type >= 0) - { - *code++ = prop_type; - *code++ = prop_value; - } - } - - /* Now set up the following opcode */ - - if (repeat_max < 0) *code++ = OP_STAR + repeat_type; else - { - repeat_max -= repeat_min; - if (repeat_max == 1) - { - *code++ = OP_QUERY + repeat_type; - } - else - { - *code++ = OP_UPTO + repeat_type; - PUT2INC(code, 0, repeat_max); - } - } - } - } - - /* Fill in the character or character type for the final opcode. */ - -#ifdef MAYBE_UTF_MULTI - if (utf && (c & UTF_LENGTH) != 0) - { - memcpy(code, utf_units, CU2BYTES(c & 7)); - code += c & 7; - } - else -#endif /* MAYBEW_UTF_MULTI */ - { - *code++ = c; - if (prop_type >= 0) - { - *code++ = prop_type; - *code++ = prop_value; - } - } - } - - /* If previous was a character class or a back reference, we put the repeat - stuff after it, but just skip the item if the repeat was {0,0}. */ - - else if (*previous == OP_CLASS || *previous == OP_NCLASS || -#ifdef SUPPORT_WIDE_CHARS - *previous == OP_XCLASS || -#endif - *previous == OP_REF || *previous == OP_REFI || - *previous == OP_DNREF || *previous == OP_DNREFI) - { - if (repeat_max == 0) - { - code = previous; - goto END_REPEAT; - } - - if (repeat_min == 0 && repeat_max == -1) - *code++ = OP_CRSTAR + repeat_type; - else if (repeat_min == 1 && repeat_max == -1) - *code++ = OP_CRPLUS + repeat_type; - else if (repeat_min == 0 && repeat_max == 1) - *code++ = OP_CRQUERY + repeat_type; - else - { - *code++ = OP_CRRANGE + repeat_type; - PUT2INC(code, 0, repeat_min); - if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ - PUT2INC(code, 0, repeat_max); - } - } - - /* If previous was a bracket group, we may have to replicate it in certain - cases. Note that at this point we can encounter only the "basic" bracket - opcodes such as BRA and CBRA, as this is the place where they get converted - into the more special varieties such as BRAPOS and SBRA. A test for >= - OP_ASSERT and <= OP_COND includes ASSERT, ASSERT_NOT, ASSERTBACK, - ASSERTBACK_NOT, ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND. - Originally, PCRE did not allow repetition of assertions, but now it does, - for Perl compatibility. */ - - else if (*previous >= OP_ASSERT && *previous <= OP_COND) - { - register int i; - int len = (int)(code - previous); - PCRE2_UCHAR *bralink = NULL; - PCRE2_UCHAR *brazeroptr = NULL; - - /* Repeating a DEFINE group (or any group where the condition is always - FALSE and there is only one branch) is pointless, but Perl allows the - syntax, so we just ignore the repeat. */ - - if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && - previous[GET(previous, 1)] != OP_ALT) - goto END_REPEAT; - - /* There is no sense in actually repeating assertions. The only potential - use of repetition is in cases when the assertion is optional. Therefore, - if the minimum is greater than zero, just ignore the repeat. If the - maximum is not zero or one, set it to 1. */ - - if (*previous < OP_ONCE) /* Assertion */ - { - if (repeat_min > 0) goto END_REPEAT; - if (repeat_max < 0 || repeat_max > 1) repeat_max = 1; - } - - /* The case of a zero minimum is special because of the need to stick - OP_BRAZERO in front of it, and because the group appears once in the - data, whereas in other cases it appears the minimum number of times. For - this reason, it is simplest to treat this case separately, as otherwise - the code gets far too messy. There are several special subcases when the - minimum is zero. */ - - if (repeat_min == 0) - { - /* If the maximum is also zero, we used to just omit the group from the - output altogether, like this: - - ** if (repeat_max == 0) - ** { - ** code = previous; - ** goto END_REPEAT; - ** } - - However, that fails when a group or a subgroup within it is referenced - as a subroutine from elsewhere in the pattern, so now we stick in - OP_SKIPZERO in front of it so that it is skipped on execution. As we - don't have a list of which groups are referenced, we cannot do this - selectively. - - If the maximum is 1 or unlimited, we just have to stick in the BRAZERO - and do no more at this point. */ - - if (repeat_max <= 1) /* Covers 0, 1, and unlimited */ - { - memmove(previous + 1, previous, CU2BYTES(len)); - code++; - if (repeat_max == 0) - { - *previous++ = OP_SKIPZERO; - goto END_REPEAT; - } - brazeroptr = previous; /* Save for possessive optimizing */ - *previous++ = OP_BRAZERO + repeat_type; - } - - /* If the maximum is greater than 1 and limited, we have to replicate - in a nested fashion, sticking OP_BRAZERO before each set of brackets. - The first one has to be handled carefully because it's the original - copy, which has to be moved up. The remainder can be handled by code - that is common with the non-zero minimum case below. We have to - adjust the value or repeat_max, since one less copy is required. */ - - else - { - int offset; - memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len)); - code += 2 + LINK_SIZE; - *previous++ = OP_BRAZERO + repeat_type; - *previous++ = OP_BRA; - - /* We chain together the bracket offset fields that have to be - filled in later when the ends of the brackets are reached. */ - - offset = (bralink == NULL)? 0 : (int)(previous - bralink); - bralink = previous; - PUTINC(previous, 0, offset); - } - - repeat_max--; - } - - /* If the minimum is greater than zero, replicate the group as many - times as necessary, and adjust the maximum to the number of subsequent - copies that we need. */ - - else - { - if (repeat_min > 1) - { - /* In the pre-compile phase, we don't actually do the replication. We - just adjust the length as if we had. Do some paranoid checks for - potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit - integer type when available, otherwise double. */ - - if (lengthptr != NULL) - { - size_t delta = (repeat_min - 1)*length_prevgroup; - if ((INT64_OR_DOUBLE)(repeat_min - 1)* - (INT64_OR_DOUBLE)length_prevgroup > - (INT64_OR_DOUBLE)INT_MAX || - OFLOW_MAX - *lengthptr < delta) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += delta; - } - - /* This is compiling for real. If there is a set first byte for - the group, and we have not yet set a "required byte", set it. */ - - else - { - if (groupsetfirstcu && reqcuflags < 0) - { - reqcu = firstcu; - reqcuflags = firstcuflags; - } - for (i = 1; i < repeat_min; i++) - { - memcpy(code, previous, CU2BYTES(len)); - code += len; - } - } - } - - if (repeat_max > 0) repeat_max -= repeat_min; - } - - /* This code is common to both the zero and non-zero minimum cases. If - the maximum is limited, it replicates the group in a nested fashion, - remembering the bracket starts on a stack. In the case of a zero minimum, - the first one was set up above. In all cases the repeat_max now specifies - the number of additional copies needed. Again, we must remember to - replicate entries on the forward reference list. */ - - if (repeat_max >= 0) - { - /* In the pre-compile phase, we don't actually do the replication. We - just adjust the length as if we had. For each repetition we must add 1 - to the length for BRAZERO and for all but the last repetition we must - add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some - paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type is - a 64-bit integer type when available, otherwise double. */ - - if (lengthptr != NULL && repeat_max > 0) - { - size_t delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - - 2 - 2*LINK_SIZE; /* Last one doesn't nest */ - if ((INT64_OR_DOUBLE)repeat_max * - (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - > (INT64_OR_DOUBLE)INT_MAX || - OFLOW_MAX - *lengthptr < delta) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += delta; - } - - /* This is compiling for real */ - - else for (i = repeat_max - 1; i >= 0; i--) - { - *code++ = OP_BRAZERO + repeat_type; - - /* All but the final copy start a new nesting, maintaining the - chain of brackets outstanding. */ - - if (i != 0) - { - int offset; - *code++ = OP_BRA; - offset = (bralink == NULL)? 0 : (int)(code - bralink); - bralink = code; - PUTINC(code, 0, offset); - } - - memcpy(code, previous, CU2BYTES(len)); - code += len; - } - - /* Now chain through the pending brackets, and fill in their length - fields (which are holding the chain links pro tem). */ - - while (bralink != NULL) - { - int oldlinkoffset; - int offset = (int)(code - bralink + 1); - PCRE2_UCHAR *bra = code - offset; - oldlinkoffset = GET(bra, 1); - bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; - *code++ = OP_KET; - PUTINC(code, 0, offset); - PUT(bra, 1, offset); - } - } - - /* If the maximum is unlimited, set a repeater in the final copy. For - ONCE brackets, that's all we need to do. However, possessively repeated - ONCE brackets can be converted into non-capturing brackets, as the - behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to - deal with possessive ONCEs specially. - - Otherwise, when we are doing the actual compile phase, check to see - whether this group is one that could match an empty string. If so, - convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so - that runtime checking can be done. [This check is also applied to ONCE - groups at runtime, but in a different way.] - - Then, if the quantifier was possessive and the bracket is not a - conditional, we convert the BRA code to the POS form, and the KET code to - KETRPOS. (It turns out to be convenient at runtime to detect this kind of - subpattern at both the start and at the end.) The use of special opcodes - makes it possible to reduce greatly the stack usage in pcre2_match(). If - the group is preceded by OP_BRAZERO, convert this to OP_BRAPOSZERO. - - Then, if the minimum number of matches is 1 or 0, cancel the possessive - flag so that the default action below, of wrapping everything inside - atomic brackets, does not happen. When the minimum is greater than 1, - there will be earlier copies of the group, and so we still have to wrap - the whole thing. */ - - else - { - PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; - PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); - - /* Convert possessive ONCE brackets to non-capturing */ - - if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && - possessive_quantifier) *bracode = OP_BRA; - - /* For non-possessive ONCE brackets, all we need to do is to - set the KET. */ - - if (*bracode == OP_ONCE || *bracode == OP_ONCE_NC) - *ketcode = OP_KETRMAX + repeat_type; - - /* Handle non-ONCE brackets and possessive ONCEs (which have been - converted to non-capturing above). */ - - else - { - /* In the compile phase, check whether the group could match an empty - string. */ - - if (lengthptr == NULL) - { - PCRE2_UCHAR *scode = bracode; - do - { - int count = 0; - int rc = could_be_empty_branch(scode, ketcode, utf, cb, FALSE, - NULL, &count); - if (rc < 0) - { - *errorcodeptr = ERR86; - goto FAILED; - } - if (rc > 0) - { - *bracode += OP_SBRA - OP_BRA; - break; - } - scode += GET(scode, 1); - } - while (*scode == OP_ALT); - - /* A conditional group with only one branch has an implicit empty - alternative branch. */ - - if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT) - *bracode = OP_SCOND; - } - - /* Handle possessive quantifiers. */ - - if (possessive_quantifier) - { - /* For COND brackets, we wrap the whole thing in a possessively - repeated non-capturing bracket, because we have not invented POS - versions of the COND opcodes. */ - - if (*bracode == OP_COND || *bracode == OP_SCOND) - { - int nlen = (int)(code - bracode); - memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen)); - code += 1 + LINK_SIZE; - nlen += 1 + LINK_SIZE; - *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS; - *code++ = OP_KETRPOS; - PUTINC(code, 0, nlen); - PUT(bracode, 1, nlen); - } - - /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ - - else - { - *bracode += 1; /* Switch to xxxPOS opcodes */ - *ketcode = OP_KETRPOS; - } - - /* If the minimum is zero, mark it as possessive, then unset the - possessive flag when the minimum is 0 or 1. */ - - if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; - if (repeat_min < 2) possessive_quantifier = FALSE; - } - - /* Non-possessive quantifier */ - - else *ketcode = OP_KETRMAX + repeat_type; - } - } - } - - /* If previous is OP_FAIL, it was generated by an empty class [] - (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be - generated, that is by (*FAIL) or (?!), set previous to NULL, which gives a - "nothing to repeat" error above. We can just ignore the repeat in empty - class case. */ - - else if (*previous == OP_FAIL) goto END_REPEAT; - - /* Else there's some kind of shambles */ - - else - { - *errorcodeptr = ERR10; - goto FAILED; - } - - /* If the character following a repeat is '+', possessive_quantifier is - TRUE. For some opcodes, there are special alternative opcodes for this - case. For anything else, we wrap the entire repeated item inside OP_ONCE - brackets. Logically, the '+' notation is just syntactic sugar, taken from - Sun's Java package, but the special opcodes can optimize it. - - Some (but not all) possessively repeated subpatterns have already been - completely handled in the code just above. For them, possessive_quantifier - is always FALSE at this stage. Note that the repeated item starts at - tempcode, not at previous, which might be the first part of a string whose - (former) last char we repeated. */ - - if (possessive_quantifier) - { - int len; - - /* Possessifying an EXACT quantifier has no effect, so we can ignore it. - However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6}, - {5,}, or {5,10}). We skip over an EXACT item; if the length of what - remains is greater than zero, there's a further opcode that can be - handled. If not, do nothing, leaving the EXACT alone. */ - - switch(*tempcode) - { - case OP_TYPEEXACT: - tempcode += PRIV(OP_lengths)[*tempcode] + - ((tempcode[1 + IMM2_SIZE] == OP_PROP - || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); - break; - - /* CHAR opcodes are used for exacts whose count is 1. */ - - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - tempcode += PRIV(OP_lengths)[*tempcode]; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(tempcode[-1])) - tempcode += GET_EXTRALEN(tempcode[-1]); -#endif - break; - - /* For the class opcodes, the repeat operator appears at the end; - adjust tempcode to point to it. */ - - case OP_CLASS: - case OP_NCLASS: - tempcode += 1 + 32/sizeof(PCRE2_UCHAR); - break; - -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - tempcode += GET(tempcode, 1); - break; -#endif - } - - /* If tempcode is equal to code (which points to the end of the repeated - item), it means we have skipped an EXACT item but there is no following - QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In - all other cases, tempcode will be pointing to the repeat opcode, and will - be less than code, so the value of len will be greater than 0. */ - - len = (int)(code - tempcode); - if (len > 0) - { - unsigned int repcode = *tempcode; - - /* There is a table for possessifying opcodes, all of which are less - than OP_CALLOUT. A zero entry means there is no possessified version. - */ - - if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0) - *tempcode = opcode_possessify[repcode]; - - /* For opcode without a special possessified version, wrap the item in - ONCE brackets. */ - - else - { - memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len)); - code += 1 + LINK_SIZE; - len += 1 + LINK_SIZE; - tempcode[0] = OP_ONCE; - *code++ = OP_KET; - PUTINC(code, 0, len); - PUT(tempcode, 1, len); - } - } - } - - /* In all case we no longer have a previous item. We also set the - "follows varying string" flag for subsequently encountered reqcus if - it isn't already set and we have just passed a varying length item. */ - - END_REPEAT: - previous = NULL; - cb->req_varyopt |= reqvary; - break; - - - /* ===================================================================*/ - /* Start of nested parenthesized sub-expression, or lookahead or lookbehind - or option setting or condition or all the other extended parenthesis forms. - We must save the current high-water-mark for the forward reference list so - that we know where they start for this group. However, because the list may - be extended when there are very many forward references (usually the result - of a replicated inner group), we must use an offset rather than an absolute - address. Note that (?# comments are dealt with at the top of the loop; - they do not get this far. */ - - case CHAR_LEFT_PARENTHESIS: - ptr++; - - /* Deal with various "verbs" that can be introduced by '*'. */ - - if (ptr[0] == CHAR_ASTERISK && (ptr[1] == ':' - || (MAX_255(ptr[1]) && ((cb->ctypes[ptr[1]] & ctype_letter) != 0)))) - { - int i, namelen; - int arglen = 0; - const char *vn = verbnames; - PCRE2_SPTR name = ptr + 1; - PCRE2_SPTR arg = NULL; - previous = NULL; - ptr++; - - /* Increment ptr, set namelen, check length */ - - READ_NAME(ctype_letter, ERR60, *errorcodeptr); - - /* It appears that Perl allows any characters whatsoever, other than - a closing parenthesis, to appear in arguments, so we no longer insist on - letters, digits, and underscores. Perl does not, however, do any - interpretation within arguments, and has no means of including a closing - parenthesis. PCRE supports escape processing but only when it is - requested by an option. Note that check_escape() will not return values - greater than the code unit maximum when not in UTF mode. */ - - if (*ptr == CHAR_COLON) - { - arg = ++ptr; - - if ((options & PCRE2_ALT_VERBNAMES) == 0) - { - arglen = 0; - while (ptr < cb->end_pattern && *ptr != CHAR_RIGHT_PARENTHESIS) - { - ptr++; /* Check length as we go */ - arglen++; /* along, to avoid the */ - if ((unsigned int)arglen > MAX_MARK) /* possibility of overflow. */ - { - *errorcodeptr = ERR76; - goto FAILED; - } - } - } - else - { - /* The length check is in process_verb_names() */ - arglen = process_verb_name(&ptr, NULL, errorcodeptr, options, - utf, cb); - if (arglen < 0) goto FAILED; - } - } - - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR60; - goto FAILED; - } - - /* Scan the table of verb names */ - - for (i = 0; i < verbcount; i++) - { - if (namelen == verbs[i].len && - PRIV(strncmp_c8)(name, vn, namelen) == 0) - { - int setverb; - - /* Check for open captures before ACCEPT and convert it to - ASSERT_ACCEPT if in an assertion. */ - - if (verbs[i].op == OP_ACCEPT) - { - open_capitem *oc; - if (arglen != 0) - { - *errorcodeptr = ERR59; - goto FAILED; - } - cb->had_accept = TRUE; - - /* In the first pass, just accumulate the length required; - otherwise hitting (*ACCEPT) inside many nested parentheses can - cause workspace overflow. */ - - for (oc = cb->open_caps; oc != NULL; oc = oc->next) - { - if (lengthptr != NULL) - { - *lengthptr += CU2BYTES(1) + IMM2_SIZE; - } - else - { - *code++ = OP_CLOSE; - PUT2INC(code, 0, oc->number); - } - } - setverb = *code++ = - (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; - - /* Do not set firstcu after *ACCEPT */ - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - } - - /* Handle other cases with/without an argument */ - - else if (arglen == 0) /* There is no argument */ - { - if (verbs[i].op < 0) /* Argument is mandatory */ - { - *errorcodeptr = ERR66; - goto FAILED; - } - setverb = *code++ = verbs[i].op; - } - - else /* An argument is present */ - { - if (verbs[i].op_arg < 0) /* Argument is forbidden */ - { - *errorcodeptr = ERR59; - goto FAILED; - } - setverb = *code++ = verbs[i].op_arg; - - /* Arguments can be very long, especially in 16- and 32-bit modes, - and can overflow the workspace in the first pass. Instead of - putting the argument into memory, we just update the length counter - and set up an empty argument. */ - - if (lengthptr != NULL) - { - *lengthptr += arglen; - *code++ = 0; - } - else - { - *code++ = arglen; - if ((options & PCRE2_ALT_VERBNAMES) != 0) - { - PCRE2_UCHAR *memcode = code; /* code is "register" */ - (void)process_verb_name(&arg, &memcode, errorcodeptr, options, - utf, cb); - code = memcode; - } - else /* No argument processing */ - { - memcpy(code, arg, CU2BYTES(arglen)); - code += arglen; - } - } - - *code++ = 0; - } - - switch (setverb) - { - case OP_THEN: - case OP_THEN_ARG: - cb->external_flags |= PCRE2_HASTHEN; - break; - - case OP_PRUNE: - case OP_PRUNE_ARG: - case OP_SKIP: - case OP_SKIP_ARG: - cb->had_pruneorskip = TRUE; - break; - } - - break; /* Found verb, exit loop */ - } - - vn += verbs[i].len + 1; - } - - if (i < verbcount) continue; /* Successfully handled a verb */ - *errorcodeptr = ERR60; /* Verb not recognized */ - goto FAILED; - } - - /* Initialization for "real" parentheses */ - - newoptions = options; - skipunits = 0; - bravalue = OP_CBRA; - reset_bracount = FALSE; - - /* Deal with the extended parentheses; all are introduced by '?', and the - appearance of any of them means that this is not a capturing group. */ - - if (*ptr == CHAR_QUESTION_MARK) - { - int i, count; - int namelen; /* Must be signed */ - uint32_t index; - uint32_t set, unset, *optset; - named_group *ng; - PCRE2_SPTR name; - PCRE2_UCHAR *slot; - - switch (*(++ptr)) - { - /* ------------------------------------------------------------ */ - case CHAR_VERTICAL_LINE: /* Reset capture count for each branch */ - reset_bracount = TRUE; - /* Fall through */ - - /* ------------------------------------------------------------ */ - case CHAR_COLON: /* Non-capturing bracket */ - bravalue = OP_BRA; - ptr++; - break; - - /* ------------------------------------------------------------ */ - case CHAR_LEFT_PARENTHESIS: - bravalue = OP_COND; /* Conditional group */ - tempptr = ptr; - - /* A condition can be an assertion, a number (referring to a numbered - group's having been set), a name (referring to a named group), or 'R', - referring to recursion. R and R&name are also permitted for - recursion tests. - - There are ways of testing a named group: (?(name)) is used by Python; - Perl 5.10 onwards uses (?() or (?('name')). - - There is one unfortunate ambiguity, caused by history. 'R' can be the - recursive thing or the name 'R' (and similarly for 'R' followed by - digits). We look for a name first; if not found, we try the other case. - - For compatibility with auto-callouts, we allow a callout to be - specified before a condition that is an assertion. First, check for the - syntax of a callout; if found, adjust the temporary pointer that is - used to check for an assertion condition. That's all that is needed! */ - - if (ptr[1] == CHAR_QUESTION_MARK && ptr[2] == CHAR_C) - { - if (IS_DIGIT(ptr[3]) || ptr[3] == CHAR_RIGHT_PARENTHESIS) - { - for (i = 3;; i++) if (!IS_DIGIT(ptr[i])) break; - if (ptr[i] == CHAR_RIGHT_PARENTHESIS) - tempptr += i + 1; - } - else - { - uint32_t delimiter = 0; - for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) - { - if (ptr[3] == PRIV(callout_start_delims)[i]) - { - delimiter = PRIV(callout_end_delims)[i]; - break; - } - } - if (delimiter != 0) - { - for (i = 4; ptr + i < cb->end_pattern; i++) - { - if (ptr[i] == delimiter) - { - if (ptr[i+1] == delimiter) i++; - else - { - if (ptr[i+1] == CHAR_RIGHT_PARENTHESIS) tempptr += i + 2; - break; - } - } - } - } - } - - /* tempptr should now be pointing to the opening parenthesis of the - assertion condition. */ - - if (*tempptr != CHAR_LEFT_PARENTHESIS) - { - *errorcodeptr = ERR28; - goto FAILED; - } - } - - /* For conditions that are assertions, check the syntax, and then exit - the switch. This will take control down to where bracketed groups - are processed. The assertion will be handled as part of the group, - but we need to identify this case because the conditional assertion may - not be quantifier. */ - - if (tempptr[1] == CHAR_QUESTION_MARK && - (tempptr[2] == CHAR_EQUALS_SIGN || - tempptr[2] == CHAR_EXCLAMATION_MARK || - (tempptr[2] == CHAR_LESS_THAN_SIGN && - (tempptr[3] == CHAR_EQUALS_SIGN || - tempptr[3] == CHAR_EXCLAMATION_MARK)))) - { - cb->iscondassert = TRUE; - break; - } - - /* Other conditions use OP_CREF/OP_DNCREF/OP_RREF/OP_DNRREF, and all - need to skip at least 1+IMM2_SIZE bytes at the start of the group. */ - - code[1+LINK_SIZE] = OP_CREF; - skipunits = 1+IMM2_SIZE; - refsign = -1; /* => not a number */ - namelen = -1; /* => not a name; must set to avoid warning */ - name = NULL; /* Always set to avoid warning */ - recno = 0; /* Always set to avoid warning */ - - /* Point at character after (?( */ - - ptr++; - - /* Check for (?(VERSION[>]=n.m), which is a facility whereby indirect - users of PCRE2 via an application can discover which release of PCRE2 - is being used. */ - - if (PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 && - ptr[7] != CHAR_RIGHT_PARENTHESIS) - { - BOOL ge = FALSE; - int major = 0; - int minor = 0; - - ptr += 7; - if (*ptr == CHAR_GREATER_THAN_SIGN) - { - ge = TRUE; - ptr++; - } - - /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT - references its argument twice. */ - - if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) - { - *errorcodeptr = ERR79; - goto FAILED; - } - - while (IS_DIGIT(*ptr)) major = major * 10 + *ptr++ - '0'; - if (*ptr == CHAR_DOT) - { - ptr++; - while (IS_DIGIT(*ptr)) minor = minor * 10 + *ptr++ - '0'; - if (minor < 10) minor *= 10; - } - - if (*ptr != CHAR_RIGHT_PARENTHESIS || minor > 99) - { - *errorcodeptr = ERR79; - goto FAILED; - } - - if (ge) - code[1+LINK_SIZE] = ((PCRE2_MAJOR > major) || - (PCRE2_MAJOR == major && PCRE2_MINOR >= minor))? - OP_TRUE : OP_FALSE; - else - code[1+LINK_SIZE] = (PCRE2_MAJOR == major && PCRE2_MINOR == minor)? - OP_TRUE : OP_FALSE; - - ptr++; - skipunits = 1; - break; /* End of condition processing */ - } - - /* Check for a test for recursion in a named group. */ - - if (*ptr == CHAR_R && ptr[1] == CHAR_AMPERSAND) - { - terminator = -1; - ptr += 2; - code[1+LINK_SIZE] = OP_RREF; /* Change the type of test */ - } - - /* Check for a test for a named group's having been set, using the Perl - syntax (?() or (?('name'), and also allow for the original PCRE - syntax of (?(name) or for (?(+n), (?(-n), and just (?(n). */ - - else if (*ptr == CHAR_LESS_THAN_SIGN) - { - terminator = CHAR_GREATER_THAN_SIGN; - ptr++; - } - else if (*ptr == CHAR_APOSTROPHE) - { - terminator = CHAR_APOSTROPHE; - ptr++; - } - else - { - terminator = CHAR_NULL; - if (*ptr == CHAR_MINUS || *ptr == CHAR_PLUS) refsign = *ptr++; - else if (IS_DIGIT(*ptr)) refsign = 0; - } - - /* Handle a number */ - - if (refsign >= 0) - { - while (IS_DIGIT(*ptr)) - { - if (recno > INT_MAX / 10 - 1) /* Integer overflow */ - { - while (IS_DIGIT(*ptr)) ptr++; - *errorcodeptr = ERR61; - goto FAILED; - } - recno = recno * 10 + (int)(*ptr - CHAR_0); - ptr++; - } - } - - /* Otherwise we expect to read a name; anything else is an error. When - the referenced name is one of a number of duplicates, a different - opcode is used and it needs more memory. Unfortunately we cannot tell - whether this is the case in the first pass, so we have to allow for - more memory always. In the second pass, the additional to skipunits - happens later. */ - - else - { - if (IS_DIGIT(*ptr)) - { - *errorcodeptr = ERR44; /* Group name must start with non-digit */ - goto FAILED; - } - if (!MAX_255(*ptr) || (cb->ctypes[*ptr] & ctype_word) == 0) - { - *errorcodeptr = ERR28; /* Assertion expected */ - goto FAILED; - } - name = ptr; - /* Increment ptr, set namelen, check length */ - READ_NAME(ctype_word, ERR48, *errorcodeptr); - if (lengthptr != NULL) skipunits += IMM2_SIZE; - } - - /* Check the terminator */ - - if ((terminator > 0 && *ptr++ != (PCRE2_UCHAR)terminator) || - *ptr++ != CHAR_RIGHT_PARENTHESIS) - { - ptr--; /* Error offset */ - *errorcodeptr = ERR26; /* Malformed number or name */ - goto FAILED; - } - - /* Do no further checking in the pre-compile phase. */ - - if (lengthptr != NULL) break; - - /* In the real compile we do the work of looking for the actual - reference. If refsign is not negative, it means we have a number in - recno. */ - - if (refsign >= 0) - { - if (recno <= 0) - { - *errorcodeptr = ERR35; - goto FAILED; - } - if (refsign != 0) recno = (refsign == CHAR_MINUS)? - (cb->bracount + 1) - recno : recno + cb->bracount; - if (recno <= 0 || (uint32_t)recno > cb->final_bracount) - { - *errorcodeptr = ERR15; - goto FAILED; - } - PUT2(code, 2+LINK_SIZE, recno); - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - break; - } - - /* Otherwise look for the name. */ - - slot = cb->name_table; - for (i = 0; i < cb->names_found; i++) - { - if (PRIV(strncmp)(name, slot+IMM2_SIZE, namelen) == 0) break; - slot += cb->name_entry_size; - } - - /* Found the named subpattern. If the name is duplicated, add one to - the opcode to change CREF/RREF into DNCREF/DNRREF and insert - appropriate data values. Otherwise, just insert the unique subpattern - number. */ - - if (i < cb->names_found) - { - int offset = i; /* Offset of first name found */ - - count = 0; - for (;;) - { - recno = GET2(slot, 0); /* Number for last found */ - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - count++; - if (++i >= cb->names_found) break; - slot += cb->name_entry_size; - if (PRIV(strncmp)(name, slot+IMM2_SIZE, namelen) != 0 || - (slot+IMM2_SIZE)[namelen] != 0) break; - } - - if (count > 1) - { - PUT2(code, 2+LINK_SIZE, offset); - PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count); - skipunits += IMM2_SIZE; - code[1+LINK_SIZE]++; - } - else /* Not a duplicated name */ - { - PUT2(code, 2+LINK_SIZE, recno); - } - } - - /* If terminator == CHAR_NULL it means that the name followed directly - after the opening parenthesis [e.g. (?(abc)...] and in this case there - are some further alternatives to try. For the cases where terminator != - CHAR_NULL [things like (?(... or (?('name')... or (?(R&name)... ] - we have now checked all the possibilities, so give an error. */ - - else if (terminator != CHAR_NULL) - { - *errorcodeptr = ERR15; - goto FAILED; - } - - /* Check for (?(R) for recursion. Allow digits after R to specify a - specific group number. */ - - else if (*name == CHAR_R) - { - recno = 0; - for (i = 1; i < namelen; i++) - { - if (!IS_DIGIT(name[i])) - { - *errorcodeptr = ERR15; /* Non-existent subpattern */ - goto FAILED; - } - if (recno > INT_MAX / 10 - 1) /* Integer overflow */ - { - *errorcodeptr = ERR61; - goto FAILED; - } - recno = recno * 10 + name[i] - CHAR_0; - } - if (recno == 0) recno = RREF_ANY; - code[1+LINK_SIZE] = OP_RREF; /* Change test type */ - PUT2(code, 2+LINK_SIZE, recno); - } - - /* Similarly, check for the (?(DEFINE) "condition", which is always - false. During compilation we set OP_DEFINE to distinguish this from - other OP_FALSE conditions so that it can be checked for having only one - branch, but after that the opcode is changed to OP_FALSE. */ - - else if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) - { - code[1+LINK_SIZE] = OP_DEFINE; - skipunits = 1; - } - - /* Reference to an unidentified subpattern. */ - - else - { - *errorcodeptr = ERR15; - goto FAILED; - } - break; - - - /* ------------------------------------------------------------ */ - case CHAR_EQUALS_SIGN: /* Positive lookahead */ - bravalue = OP_ASSERT; - cb->assert_depth += 1; - ptr++; - break; - - /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird - thing to do, but Perl allows all assertions to be quantified, and when - they contain capturing parentheses there may be a potential use for - this feature. Not that that applies to a quantified (?!) but we allow - it for uniformity. */ - - /* ------------------------------------------------------------ */ - case CHAR_EXCLAMATION_MARK: /* Negative lookahead */ - ptr++; - if (*ptr == CHAR_RIGHT_PARENTHESIS && ptr[1] != CHAR_ASTERISK && - ptr[1] != CHAR_PLUS && ptr[1] != CHAR_QUESTION_MARK && - (ptr[1] != CHAR_LEFT_CURLY_BRACKET || !is_counted_repeat(ptr+2))) - { - *code++ = OP_FAIL; - previous = NULL; - continue; - } - bravalue = OP_ASSERT_NOT; - cb->assert_depth += 1; - break; - - - /* ------------------------------------------------------------ */ - case CHAR_LESS_THAN_SIGN: /* Lookbehind or named define */ - switch (ptr[1]) - { - case CHAR_EQUALS_SIGN: /* Positive lookbehind */ - bravalue = OP_ASSERTBACK; - cb->assert_depth += 1; - ptr += 2; - break; - - case CHAR_EXCLAMATION_MARK: /* Negative lookbehind */ - bravalue = OP_ASSERTBACK_NOT; - cb->assert_depth += 1; - ptr += 2; - break; - - /* Must be a name definition - as the syntax was checked in the - pre-pass, we can assume here that it is valid. Skip over the name - and go to handle the numbered group. */ - - default: - while (*(++ptr) != CHAR_GREATER_THAN_SIGN); - ptr++; - goto NUMBERED_GROUP; - } - break; - - - /* ------------------------------------------------------------ */ - case CHAR_GREATER_THAN_SIGN: /* One-time brackets */ - bravalue = OP_ONCE; - ptr++; - break; - - - /* ------------------------------------------------------------ */ - case CHAR_C: /* Callout */ - previous_callout = code; /* Save for later completion */ - after_manual_callout = 1; /* Skip one item before completing */ - ptr++; /* Character after (?C */ - - /* A callout may have a string argument, delimited by one of a fixed - number of characters, or an undelimited numerical argument, or no - argument, which is the same as (?C0). Different opcodes are used for - the two cases. */ - - if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) - { - uint32_t delimiter = 0; - - for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) - { - if (*ptr == PRIV(callout_start_delims)[i]) - { - delimiter = PRIV(callout_end_delims)[i]; - break; - } - } - - if (delimiter == 0) - { - *errorcodeptr = ERR82; - goto FAILED; - } - - /* During the pre-compile phase, we parse the string and update the - length. There is no need to generate any code. (In fact, the string - has already been parsed in the pre-pass that looks for named - parentheses, but it does no harm to leave this code in.) */ - - if (lengthptr != NULL) /* Only check the string */ - { - PCRE2_SPTR start = ptr; - do - { - if (++ptr >= cb->end_pattern) - { - *errorcodeptr = ERR81; - ptr = start; /* To give a more useful message */ - goto FAILED; - } - if (ptr[0] == delimiter && ptr[1] == delimiter) ptr += 2; - } - while (ptr[0] != delimiter); - - /* Start points to the opening delimiter, ptr points to the - closing delimiter. We must allow for including the delimiter and - for the terminating zero. Any doubled delimiters within the string - make this an overestimate, but it is not worth bothering about. */ - - (*lengthptr) += (ptr - start) + 2 + (1 + 4*LINK_SIZE); - } - - /* In the real compile we can copy the string, knowing that it is - syntactically OK. The starting delimiter is included so that the - client can discover it if they want. We also pass the start offset to - help a script language give better error messages. */ - - else - { - PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE); - *callout_string++ = *ptr++; - PUT(code, 1 + 3*LINK_SIZE, (int)(ptr - cb->start_pattern)); /* Start offset */ - for(;;) - { - if (*ptr == delimiter) - { - if (ptr[1] == delimiter) ptr++; else break; - } - *callout_string++ = *ptr++; - } - *callout_string++ = CHAR_NULL; - code[0] = OP_CALLOUT_STR; - PUT(code, 1, (int)(ptr + 2 - cb->start_pattern)); /* Next offset */ - PUT(code, 1 + LINK_SIZE, 0); /* Default length */ - PUT(code, 1 + 2*LINK_SIZE, /* Compute size */ - (int)(callout_string - code)); - code = callout_string; - } - - /* Advance to what should be the closing parenthesis, which is - checked below. */ - - ptr++; - } - - /* Handle a callout with an optional numerical argument, which must be - less than or equal to 255. A missing argument gives 0. */ - - else - { - int n = 0; - code[0] = OP_CALLOUT; /* Numerical callout */ - while (IS_DIGIT(*ptr)) - { - n = n * 10 + *ptr++ - CHAR_0; - if (n > 255) - { - *errorcodeptr = ERR38; - goto FAILED; - } - } - PUT(code, 1, (int)(ptr - cb->start_pattern + 1)); /* Next offset */ - PUT(code, 1 + LINK_SIZE, 0); /* Default length */ - code[1 + 2*LINK_SIZE] = n; /* Callout number */ - code += PRIV(OP_lengths)[OP_CALLOUT]; - } - - /* Both formats must have a closing parenthesis */ - - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR39; - goto FAILED; - } - - /* Callouts cannot be quantified. */ - - previous = NULL; - continue; - - - /* ------------------------------------------------------------ */ - case CHAR_P: /* Python-style named subpattern handling */ - if (*(++ptr) == CHAR_EQUALS_SIGN || - *ptr == CHAR_GREATER_THAN_SIGN) /* Reference or recursion */ - { - is_recurse = *ptr == CHAR_GREATER_THAN_SIGN; - terminator = CHAR_RIGHT_PARENTHESIS; - goto NAMED_REF_OR_RECURSE; - } - else if (*ptr != CHAR_LESS_THAN_SIGN) /* Test for Python-style defn */ - { - *errorcodeptr = ERR41; - goto FAILED; - } - /* Fall through to handle (?P< as (?< is handled */ - - - /* ------------------------------------------------------------ */ - case CHAR_APOSTROPHE: /* Define a name - note fall through above */ - - /* The syntax was checked and the list of names was set up in the - pre-pass, so there is nothing to be done now except to skip over the - name. */ - - terminator = (*ptr == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; - while (*(++ptr) != (unsigned int)terminator); - ptr++; - goto NUMBERED_GROUP; /* Set up numbered group */ - - - /* ------------------------------------------------------------ */ - case CHAR_AMPERSAND: /* Perl recursion/subroutine syntax */ - terminator = CHAR_RIGHT_PARENTHESIS; - is_recurse = TRUE; - /* Fall through */ - - /* We come here from the Python syntax above that handles both - references (?P=name) and recursion (?P>name), as well as falling - through from the Perl recursion syntax (?&name). We also come here from - the Perl \k or \k'name' back reference syntax and the \k{name} - .NET syntax, and the Oniguruma \g<...> and \g'...' subroutine syntax. */ - - NAMED_REF_OR_RECURSE: - name = ++ptr; - if (IS_DIGIT(*ptr)) - { - *errorcodeptr = ERR44; /* Group name must start with non-digit */ - goto FAILED; - } - /* Increment ptr, set namelen, check length */ - READ_NAME(ctype_word, ERR48, *errorcodeptr); - - /* In the pre-compile phase, do a syntax check. */ - - if (lengthptr != NULL) - { - if (namelen == 0) - { - *errorcodeptr = ERR62; - goto FAILED; - } - if (*ptr != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR42; - goto FAILED; - } - } - - /* Scan the list of names generated in the pre-pass in order to get - a number and whether or not this name is duplicated. */ - - recno = 0; - is_dupname = FALSE; - ng = cb->named_groups; - - for (i = 0; i < cb->names_found; i++, ng++) - { - if (namelen == ng->length && - PRIV(strncmp)(name, ng->name, namelen) == 0) - { - open_capitem *oc; - is_dupname = ng->isdup; - recno = ng->number; - - /* For a recursion, that's all that is needed. We can now go to the - code that handles numerical recursion. */ - - if (is_recurse) goto HANDLE_RECURSION; - - /* For a back reference, update the back reference map and the - maximum back reference. Then for each group we must check to see if - it is recursive, that is, it is inside the group that it - references. A flag is set so that the group can be made atomic. */ - - cb->backref_map |= (recno < 32)? (1u << recno) : 1; - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - - for (oc = cb->open_caps; oc != NULL; oc = oc->next) - { - if (oc->number == recno) - { - oc->flag = TRUE; - break; - } - } - } - } - - /* If the name was not found we have a bad reference. */ - - if (recno == 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - - /* If a back reference name is not duplicated, we can handle it as a - numerical reference. */ - - if (!is_dupname) goto HANDLE_REFERENCE; - - /* If a back reference name is duplicated, we generate a different - opcode to a numerical back reference. In the second pass we must search - for the index and count in the final name table. */ - - count = 0; - index = 0; - - if (lengthptr == NULL) - { - slot = cb->name_table; - for (i = 0; i < cb->names_found; i++) - { - if (PRIV(strncmp)(name, slot+IMM2_SIZE, namelen) == 0 && - slot[IMM2_SIZE+namelen] == 0) - { - if (count == 0) index = i; - count++; - } - slot += cb->name_entry_size; - } - - if (count == 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - } - - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - previous = code; - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF; - PUT2INC(code, 0, index); - PUT2INC(code, 0, count); - continue; /* End of back ref handling */ - - - /* ------------------------------------------------------------ */ - case CHAR_R: /* Recursion, same as (?0) */ - recno = 0; - if (*(++ptr) != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR29; - goto FAILED; - } - goto HANDLE_RECURSION; - - - /* ------------------------------------------------------------ */ - case CHAR_MINUS: case CHAR_PLUS: /* Recursion or subroutine */ - case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: - case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: - { - terminator = CHAR_RIGHT_PARENTHESIS; - - /* Come here from the \g<...> and \g'...' code (Oniguruma - compatibility). However, the syntax has been checked to ensure that - the ... are a (signed) number, so that neither ERR63 nor ERR29 will - be called on this path, nor with the jump to OTHER_CHAR_AFTER_QUERY - ever be taken. */ - - HANDLE_NUMERICAL_RECURSION: - - if ((refsign = *ptr) == CHAR_PLUS) - { - ptr++; - if (!IS_DIGIT(*ptr)) - { - *errorcodeptr = ERR63; - goto FAILED; - } - } - else if (refsign == CHAR_MINUS) - { - if (!IS_DIGIT(ptr[1])) - goto OTHER_CHAR_AFTER_QUERY; - ptr++; - } - - recno = 0; - while (IS_DIGIT(*ptr)) - { - if (recno > INT_MAX / 10 - 1) /* Integer overflow */ - { - while (IS_DIGIT(*ptr)) ptr++; - *errorcodeptr = ERR61; - goto FAILED; - } - recno = recno * 10 + *ptr++ - CHAR_0; - } - - if (*ptr != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR29; - goto FAILED; - } - - if (refsign == CHAR_MINUS) - { - if (recno == 0) - { - *errorcodeptr = ERR58; - goto FAILED; - } - recno = (int)(cb->bracount + 1) - recno; - if (recno <= 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - } - else if (refsign == CHAR_PLUS) - { - if (recno == 0) - { - *errorcodeptr = ERR58; - goto FAILED; - } - recno += cb->bracount; - } - - if ((uint32_t)recno > cb->final_bracount) - { - *errorcodeptr = ERR15; - goto FAILED; - } - - /* Come here from code above that handles a named recursion. - We insert the number of the called group after OP_RECURSE. At the - end of compiling the pattern is scanned and these numbers are - replaced by offsets within the pattern. It is done like this to avoid - problems with forward references and adjusting offsets when groups - are duplicated and moved (as discovered in previous implementations). - Note that a recursion does not have a set first character (relevant - if it is repeated, because it will then be wrapped with ONCE - brackets). */ - - HANDLE_RECURSION: - previous = code; - *code = OP_RECURSE; - PUT(code, 1, recno); - code += 1 + LINK_SIZE; - groupsetfirstcu = FALSE; - cb->had_recurse = TRUE; - } - - /* Can't determine a first byte now */ - - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - continue; - - - /* ------------------------------------------------------------ */ - default: /* Other characters: check option setting */ - OTHER_CHAR_AFTER_QUERY: - set = unset = 0; - optset = &set; - - while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) - { - switch (*ptr++) - { - case CHAR_MINUS: optset = &unset; break; - - case CHAR_J: /* Record that it changed in the external options */ - *optset |= PCRE2_DUPNAMES; - cb->external_flags |= PCRE2_JCHANGED; - break; - - case CHAR_i: *optset |= PCRE2_CASELESS; break; - case CHAR_m: *optset |= PCRE2_MULTILINE; break; - case CHAR_s: *optset |= PCRE2_DOTALL; break; - case CHAR_x: *optset |= PCRE2_EXTENDED; break; - case CHAR_U: *optset |= PCRE2_UNGREEDY; break; - - default: *errorcodeptr = ERR11; - ptr--; /* Correct the offset */ - goto FAILED; - } - } - - /* Set up the changed option bits, but don't change anything yet. */ - - newoptions = (options | set) & (~unset); - - /* If the options ended with ')' this is not the start of a nested - group with option changes, so the options change at this level. They - must also be passed back for use in subsequent branches. Reset the - greedy defaults and the case value for firstcu and reqcu. */ - - if (*ptr == CHAR_RIGHT_PARENTHESIS) - { - *optionsptr = options = newoptions; - greedy_default = ((newoptions & PCRE2_UNGREEDY) != 0); - greedy_non_default = greedy_default ^ 1; - req_caseopt = ((newoptions & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; - previous = NULL; /* This item can't be repeated */ - continue; /* It is complete */ - } - - /* If the options ended with ':' we are heading into a nested group - with possible change of options. Such groups are non-capturing and are - not assertions of any kind. All we need to do is skip over the ':'; - the newoptions value is handled below. */ - - bravalue = OP_BRA; - ptr++; - } /* End of switch for character following (? */ - } /* End of (? handling */ - - /* Opening parenthesis not followed by '*' or '?'. If PCRE2_NO_AUTO_CAPTURE - is set, all unadorned brackets become non-capturing and behave like (?:...) - brackets. */ - - else if ((options & PCRE2_NO_AUTO_CAPTURE) != 0) - { - bravalue = OP_BRA; - } - - /* Else we have a capturing group. */ - - else - { - NUMBERED_GROUP: - cb->bracount += 1; - PUT2(code, 1+LINK_SIZE, cb->bracount); - skipunits = IMM2_SIZE; - } - - /* Process nested bracketed regex. First check for parentheses nested too - deeply. */ - - if ((cb->parens_depth += 1) > (int)(cb->cx->parens_nest_limit)) - { - *errorcodeptr = ERR19; - goto FAILED; - } - - /* All assertions used not to be repeatable, but this was changed for Perl - compatibility. All kinds can now be repeated except for assertions that are - conditions (Perl also forbids these to be repeated). We copy code into a - non-register variable (tempcode) in order to be able to pass its address - because some compilers complain otherwise. At the start of a conditional - group whose condition is an assertion, cb->iscondassert is set. We unset it - here so as to allow assertions later in the group to be quantified. */ - - if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT && - cb->iscondassert) - { - previous = NULL; - cb->iscondassert = FALSE; - } - else - { - previous = code; - } - - *code = bravalue; - tempcode = code; - tempreqvary = cb->req_varyopt; /* Save value before bracket */ - tempbracount = cb->bracount; /* Save value before bracket */ - length_prevgroup = 0; /* Initialize for pre-compile phase */ - - if (!compile_regex( - newoptions, /* The complete new option state */ - &tempcode, /* Where to put code (updated) */ - &ptr, /* Input pointer (updated) */ - errorcodeptr, /* Where to put an error message */ - (bravalue == OP_ASSERTBACK || - bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ - reset_bracount, /* True if (?| group */ - skipunits, /* Skip over bracket number */ - cond_depth + - ((bravalue == OP_COND)?1:0), /* Depth of condition subpatterns */ - &subfirstcu, /* For possible first char */ - &subfirstcuflags, - &subreqcu, /* For possible last char */ - &subreqcuflags, - bcptr, /* Current branch chain */ - cb, /* Compile data block */ - (lengthptr == NULL)? NULL : /* Actual compile phase */ - &length_prevgroup /* Pre-compile phase */ - )) - goto FAILED; - - cb->parens_depth -= 1; - - /* If this was an atomic group and there are no capturing groups within it, - generate OP_ONCE_NC instead of OP_ONCE. */ - - if (bravalue == OP_ONCE && cb->bracount <= tempbracount) - *code = OP_ONCE_NC; - - if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) - cb->assert_depth -= 1; - - /* At the end of compiling, code is still pointing to the start of the - group, while tempcode has been updated to point past the end of the group. - The pattern pointer (ptr) is on the bracket. - - If this is a conditional bracket, check that there are no more than - two branches in the group, or just one if it's a DEFINE group. We do this - in the real compile phase, not in the pre-pass, where the whole group may - not be available. */ - - if (bravalue == OP_COND && lengthptr == NULL) - { - PCRE2_UCHAR *tc = code; - int condcount = 0; - - do { - condcount++; - tc += GET(tc,1); - } - while (*tc != OP_KET); - - /* A DEFINE group is never obeyed inline (the "condition" is always - false). It must have only one branch. Having checked this, change the - opcode to OP_FALSE. */ - - if (code[LINK_SIZE+1] == OP_DEFINE) - { - if (condcount > 1) - { - *errorcodeptr = ERR54; - goto FAILED; - } - code[LINK_SIZE+1] = OP_FALSE; - bravalue = OP_DEFINE; /* Just a flag to suppress char handling below */ - } - - /* A "normal" conditional group. If there is just one branch, we must not - make use of its firstcu or reqcu, because this is equivalent to an - empty second branch. */ - - else - { - if (condcount > 2) - { - *errorcodeptr = ERR27; - goto FAILED; - } - if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE; - } - } - - /* At the end of a group, it's an error if we hit end of pattern or - any non-closing parenthesis. This check also happens in the pre-scan, - so should not trigger here, but leave this code as an insurance. */ - - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR14; - goto FAILED; - } - - /* In the pre-compile phase, update the length by the length of the group, - less the brackets at either end. Then reduce the compiled code to just a - set of non-capturing brackets so that it doesn't use much memory if it is - duplicated by a quantifier.*/ - - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; - code++; /* This already contains bravalue */ - PUTINC(code, 0, 1 + LINK_SIZE); - *code++ = OP_KET; - PUTINC(code, 0, 1 + LINK_SIZE); - break; /* No need to waste time with special character handling */ - } - - /* Otherwise update the main code pointer to the end of the group. */ - - code = tempcode; - - /* For a DEFINE group, required and first character settings are not - relevant. */ - - if (bravalue == OP_DEFINE) break; - - /* Handle updating of the required and first characters for other types of - group. Update for normal brackets of all kinds, and conditions with two - branches (see code above). If the bracket is followed by a quantifier with - zero repeat, we have to back off. Hence the definition of zeroreqcu and - zerofirstcu outside the main loop so that they can be accessed for the - back off. */ - - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - groupsetfirstcu = FALSE; - - if (bravalue >= OP_ONCE) - { - /* If we have not yet set a firstcu in this branch, take it from the - subpattern, remembering that it was set here so that a repeat of more - than one can replicate it as reqcu if necessary. If the subpattern has - no firstcu, set "none" for the whole branch. In both cases, a zero - repeat forces firstcu to "none". */ - - if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET) - { - if (subfirstcuflags >= 0) - { - firstcu = subfirstcu; - firstcuflags = subfirstcuflags; - groupsetfirstcu = TRUE; - } - else firstcuflags = REQ_NONE; - zerofirstcuflags = REQ_NONE; - } - - /* If firstcu was previously set, convert the subpattern's firstcu - into reqcu if there wasn't one, using the vary flag that was in - existence beforehand. */ - - else if (subfirstcuflags >= 0 && subreqcuflags < 0) - { - subreqcu = subfirstcu; - subreqcuflags = subfirstcuflags | tempreqvary; - } - - /* If the subpattern set a required byte (or set a first byte that isn't - really the first byte - see above), set it. */ - - if (subreqcuflags >= 0) - { - reqcu = subreqcu; - reqcuflags = subreqcuflags; - } - } - - /* For a forward assertion, we take the reqcu, if set. This can be - helpful if the pattern that follows the assertion doesn't set a different - char. For example, it's useful for /(?=abcde).+/. We can't set firstcu - for an assertion, however because it leads to incorrect effect for patterns - such as /(?=a)a.+/ when the "real" "a" would then become a reqcu instead - of a firstcu. This is overcome by a scan at the end if there's no - firstcu, looking for an asserted first char. */ - - else if (bravalue == OP_ASSERT && subreqcuflags >= 0) - { - reqcu = subreqcu; - reqcuflags = subreqcuflags; - } - break; /* End of processing '(' */ - - - /* ===================================================================*/ - /* Handle metasequences introduced by \. For ones like \d, the ESC_ values - are arranged to be the negation of the corresponding OP_values in the - default case when PCRE2_UCP is not set. For the back references, the values - are negative the reference number. Only back references and those types - that consume a character may be repeated. We can test for values between - ESC_b and ESC_Z for the latter; this may have to change if any new ones are - ever created. - - Note: \Q and \E are handled at the start of the character-processing loop, - not here. */ - - case CHAR_BACKSLASH: - tempptr = ptr; - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &ec, errorcodeptr, - options, FALSE, cb); - if (*errorcodeptr != 0) goto FAILED; - - if (escape == 0) /* The escape coded a single character */ - c = ec; - else - { - /* For metasequences that actually match a character, we disable the - setting of a first character if it hasn't already been set. */ - - if (firstcuflags == REQ_UNSET && escape > ESC_b && escape < ESC_Z) - firstcuflags = REQ_NONE; - - /* Set values to reset to if this is followed by a zero repeat. */ - - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - /* \g or \g'name' is a subroutine call by name and \g or \g'n' - is a subroutine call by number (Oniguruma syntax). In fact, the value - ESC_g is returned only for these cases. So we don't need to check for < - or ' if the value is ESC_g. For the Perl syntax \g{n} the value is - -n, and for the Perl syntax \g{name} the result is ESC_k (as - that is a synonym for a named back reference). */ - - if (escape == ESC_g) - { - PCRE2_SPTR p; - uint32_t cf; - - terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; - - /* These two statements stop the compiler for warning about possibly - unset variables caused by the jump to HANDLE_NUMERICAL_RECURSION. In - fact, because we do the check for a number below, the paths that - would actually be in error are never taken. */ - - skipunits = 0; - reset_bracount = FALSE; - - /* If it's not a signed or unsigned number, treat it as a name. */ - - cf = ptr[1]; - if (cf != CHAR_PLUS && cf != CHAR_MINUS && !IS_DIGIT(cf)) - { - is_recurse = TRUE; - goto NAMED_REF_OR_RECURSE; - } - - /* Signed or unsigned number (cf = ptr[1]) is known to be plus or minus - or a digit. */ - - p = ptr + 2; - while (IS_DIGIT(*p)) p++; - if (*p != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR57; - goto FAILED; - } - ptr++; - goto HANDLE_NUMERICAL_RECURSION; - } - - /* \k or \k'name' is a back reference by name (Perl syntax). - We also support \k{name} (.NET syntax). */ - - if (escape == ESC_k) - { - if ((ptr[1] != CHAR_LESS_THAN_SIGN && - ptr[1] != CHAR_APOSTROPHE && ptr[1] != CHAR_LEFT_CURLY_BRACKET)) - { - *errorcodeptr = ERR69; - goto FAILED; - } - is_recurse = FALSE; - terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? - CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; - goto NAMED_REF_OR_RECURSE; - } - - /* Back references are handled specially; must disable firstcu if - not set to cope with cases like (?=(\w+))\1: which would otherwise set - ':' later. */ - - if (escape < 0) - { - open_capitem *oc; - recno = -escape; - - /* Come here from named backref handling when the reference is to a - single group (i.e. not to a duplicated name). */ - - HANDLE_REFERENCE: - if (recno > (int)cb->final_bracount) - { - *errorcodeptr = ERR15; - goto FAILED; - } - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - previous = code; - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF; - PUT2INC(code, 0, recno); - cb->backref_map |= (recno < 32)? (1u << recno) : 1; - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - - /* Check to see if this back reference is recursive, that it, it - is inside the group that it references. A flag is set so that the - group can be made atomic. */ - - for (oc = cb->open_caps; oc != NULL; oc = oc->next) - { - if (oc->number == recno) - { - oc->flag = TRUE; - break; - } - } - } - - /* So are Unicode property matches, if supported. */ - -#ifdef SUPPORT_UNICODE - else if (escape == ESC_P || escape == ESC_p) - { - BOOL negated; - unsigned int ptype = 0, pdata = 0; - if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr, cb)) - goto FAILED; - previous = code; - *code++ = ((escape == ESC_p) != negated)? OP_PROP : OP_NOTPROP; - *code++ = ptype; - *code++ = pdata; - } -#else - - /* If Unicode properties are not supported, \X, \P, and \p are not - allowed. */ - - else if (escape == ESC_X || escape == ESC_P || escape == ESC_p) - { - *errorcodeptr = ERR45; - goto FAILED; - } -#endif - - /* The use of \C can be locked out. */ - -#ifdef NEVER_BACKSLASH_C - else if (escape == ESC_C) - { - *errorcodeptr = ERR85; - goto FAILED; - } -#else - else if (escape == ESC_C && (options & PCRE2_NEVER_BACKSLASH_C) != 0) - { - *errorcodeptr = ERR83; - goto FAILED; - } -#endif - - /* For the rest (including \X when Unicode properties are supported), we - can obtain the OP value by negating the escape value in the default - situation when PCRE2_UCP is not set. When it *is* set, we substitute - Unicode property tests. Note that \b and \B do a one-character - lookbehind, and \A also behaves as if it does. */ - - else - { - if (escape == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */ - if ((escape == ESC_b || escape == ESC_B || escape == ESC_A) && - cb->max_lookbehind == 0) - cb->max_lookbehind = 1; -#ifdef SUPPORT_UNICODE - if (escape >= ESC_DU && escape <= ESC_wu) - { - cb->nestptr[1] = cb->nestptr[0]; /* Back up if at 2nd level */ - cb->nestptr[0] = ptr + 1; /* Where to resume */ - ptr = substitutes[escape - ESC_DU] - 1; /* Just before substitute */ - } - else -#endif - /* In non-UTF mode, and for both 32-bit modes, we turn \C into - OP_ALLANY instead of OP_ANYBYTE so that it works in DFA mode and in - lookbehinds. */ - - { - previous = (escape > ESC_b && escape < ESC_Z)? code : NULL; -#if PCRE2_CODE_UNIT_WIDTH == 32 - *code++ = (escape == ESC_C)? OP_ALLANY : escape; -#else - *code++ = (!utf && escape == ESC_C)? OP_ALLANY : escape; -#endif - } - } - continue; - } - - /* We have a data character whose value is in c. In UTF-8 mode it may have - a value > 127. We set its representation in the length/buffer, and then - handle it as a data character. */ - - mclength = PUTCHAR(c, mcbuffer); - goto ONE_CHAR; - - - /* ===================================================================*/ - /* Handle a literal character. It is guaranteed not to be whitespace or # - when the extended flag is set. If we are in a UTF mode, it may be a - multi-unit literal character. */ - - default: - NORMAL_CHAR: - mclength = 1; - mcbuffer[0] = c; - -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - ACROSSCHAR(TRUE, ptr[1], mcbuffer[mclength++] = *(++ptr)); -#endif - - /* At this point we have the character's bytes in mcbuffer, and the length - in mclength. When not in UTF mode, the length is always 1. */ - - ONE_CHAR: - previous = code; - - /* For caseless UTF mode, check whether this character has more than one - other case. If so, generate a special OP_PROP item instead of OP_CHARI. */ - -#ifdef SUPPORT_UNICODE - if (utf && (options & PCRE2_CASELESS) != 0) - { - GETCHAR(c, mcbuffer); - if ((c = UCD_CASESET(c)) != 0) - { - *code++ = OP_PROP; - *code++ = PT_CLIST; - *code++ = c; - if (firstcuflags == REQ_UNSET) - firstcuflags = zerofirstcuflags = REQ_NONE; - break; - } - } -#endif - - /* Caseful matches, or not one of the multicase characters. */ - - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR; - for (c = 0; c < mclength; c++) *code++ = mcbuffer[c]; - - /* Remember if \r or \n were seen */ - - if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) - cb->external_flags |= PCRE2_HASCRORLF; - - /* Set the first and required bytes appropriately. If no previous first - byte, set it from this character, but revert to none on a zero repeat. - Otherwise, leave the firstcu value alone, and don't change it on a zero - repeat. */ - - if (firstcuflags == REQ_UNSET) - { - zerofirstcuflags = REQ_NONE; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - /* If the character is more than one byte long, we can set firstcu - only if it is not to be matched caselessly. */ - - if (mclength == 1 || req_caseopt == 0) - { - firstcu = mcbuffer[0] | req_caseopt; - firstcu = mcbuffer[0]; - firstcuflags = req_caseopt; - - if (mclength != 1) - { - reqcu = code[-1]; - reqcuflags = cb->req_varyopt; - } - } - else firstcuflags = reqcuflags = REQ_NONE; - } - - /* firstcu was previously set; we can set reqcu only if the length is - 1 or the matching is caseful. */ - - else - { - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - if (mclength == 1 || req_caseopt == 0) - { - reqcu = code[-1]; - reqcuflags = req_caseopt | cb->req_varyopt; - } - } - - break; /* End of literal character handling */ - } - } /* end of big loop */ - -/* Control never reaches here by falling through, only by a goto for all the -error states. Pass back the position in the pattern so that it can be displayed -to the user for diagnosing the error. */ - -FAILED: -*ptrptr = ptr; -return FALSE; -} - - - -/************************************************* -* Compile regex: a sequence of alternatives * -*************************************************/ - -/* On entry, ptr is pointing past the bracket character, but on return it -points to the closing bracket, or vertical bar, or end of string. The code -variable is pointing at the byte into which the BRA operator has been stored. -This function is used during the pre-compile phase when we are trying to find -out the amount of memory needed, as well as during the real compile phase. The -value of lengthptr distinguishes the two phases. - -Arguments: - options option bits, including any changes for this subpattern - codeptr -> the address of the current code pointer - ptrptr -> the address of the current pattern pointer - errorcodeptr -> pointer to error code variable - lookbehind TRUE if this is a lookbehind assertion - reset_bracount TRUE to reset the count for each branch - skipunits skip this many code units at start (for brackets and OP_COND) - cond_depth depth of nesting for conditional subpatterns - firstcuptr place to put the first required code unit - firstcuflagsptr place to put the first code unit flags, or a negative number - reqcuptr place to put the last required code unit - reqcuflagsptr place to put the last required code unit flags, or a negative number - bcptr pointer to the chain of currently open branches - cb points to the data block with tables pointers etc. - lengthptr NULL during the real compile phase - points to length accumulator during pre-compile phase - -Returns: TRUE on success -*/ - -static BOOL -compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, PCRE2_SPTR *ptrptr, - int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, uint32_t skipunits, - int cond_depth, uint32_t *firstcuptr, int32_t *firstcuflagsptr, - uint32_t *reqcuptr, int32_t *reqcuflagsptr, branch_chain *bcptr, - compile_block *cb, size_t *lengthptr) -{ -PCRE2_SPTR ptr = *ptrptr; -PCRE2_UCHAR *code = *codeptr; -PCRE2_UCHAR *last_branch = code; -PCRE2_UCHAR *start_bracket = code; -PCRE2_UCHAR *reverse_count = NULL; -open_capitem capitem; -int capnumber = 0; -uint32_t firstcu, reqcu; -int32_t firstcuflags, reqcuflags; -uint32_t branchfirstcu, branchreqcu; -int32_t branchfirstcuflags, branchreqcuflags; -size_t length; -unsigned int orig_bracount; -unsigned int max_bracount; -branch_chain bc; - -/* If set, call the external function that checks for stack availability. */ - -if (cb->cx->stack_guard != NULL && - cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data)) - { - *errorcodeptr= ERR33; - return FALSE; - } - -/* Miscellaneous initialization */ - -bc.outer = bcptr; -bc.current_branch = code; - -firstcu = reqcu = 0; -firstcuflags = reqcuflags = REQ_UNSET; - -/* Accumulate the length for use in the pre-compile phase. Start with the -length of the BRA and KET and any extra code units that are required at the -beginning. We accumulate in a local variable to save frequent testing of -lengthptr for NULL. We cannot do this by looking at the value of 'code' at the -start and end of each alternative, because compiled items are discarded during -the pre-compile phase so that the work space is not exceeded. */ - -length = 2 + 2*LINK_SIZE + skipunits; - -/* WARNING: If the above line is changed for any reason, you must also change -the code that abstracts option settings at the start of the pattern and makes -them global. It tests the value of length for (2 + 2*LINK_SIZE) in the -pre-compile phase to find out whether or not anything has yet been compiled. - -If this is a capturing subpattern, add to the chain of open capturing items -so that we can detect them if (*ACCEPT) is encountered. This is also used to -detect groups that contain recursive back references to themselves. Note that -only OP_CBRA need be tested here; changing this opcode to one of its variants, -e.g. OP_SCBRAPOS, happens later, after the group has been compiled. */ - -if (*code == OP_CBRA) - { - capnumber = GET2(code, 1 + LINK_SIZE); - capitem.number = capnumber; - capitem.next = cb->open_caps; - capitem.flag = FALSE; - cb->open_caps = &capitem; - } - -/* Offset is set zero to mark that this bracket is still open */ - -PUT(code, 1, 0); -code += 1 + LINK_SIZE + skipunits; - -/* Loop for each alternative branch */ - -orig_bracount = max_bracount = cb->bracount; - -for (;;) - { - /* For a (?| group, reset the capturing bracket count so that each branch - uses the same numbers. */ - - if (reset_bracount) cb->bracount = orig_bracount; - - /* Set up dummy OP_REVERSE if lookbehind assertion */ - - if (lookbehind) - { - *code++ = OP_REVERSE; - reverse_count = code; - PUTINC(code, 0, 0); - length += 1 + LINK_SIZE; - } - - /* Now compile the branch; in the pre-compile phase its length gets added - into the length. */ - - if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstcu, - &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc, - cond_depth, cb, (lengthptr == NULL)? NULL : &length)) - { - *ptrptr = ptr; - return FALSE; - } - - /* Keep the highest bracket count in case (?| was used and some branch - has fewer than the rest. */ - - if (cb->bracount > max_bracount) max_bracount = cb->bracount; - - /* In the real compile phase, there is some post-processing to be done. */ - - if (lengthptr == NULL) - { - /* If this is the first branch, the firstcu and reqcu values for the - branch become the values for the regex. */ - - if (*last_branch != OP_ALT) - { - firstcu = branchfirstcu; - firstcuflags = branchfirstcuflags; - reqcu = branchreqcu; - reqcuflags = branchreqcuflags; - } - - /* If this is not the first branch, the first char and reqcu have to - match the values from all the previous branches, except that if the - previous value for reqcu didn't have REQ_VARY set, it can still match, - and we set REQ_VARY for the regex. */ - - else - { - /* If we previously had a firstcu, but it doesn't match the new branch, - we have to abandon the firstcu for the regex, but if there was - previously no reqcu, it takes on the value of the old firstcu. */ - - if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu) - { - if (firstcuflags >= 0) - { - if (reqcuflags < 0) - { - reqcu = firstcu; - reqcuflags = firstcuflags; - } - } - firstcuflags = REQ_NONE; - } - - /* If we (now or from before) have no firstcu, a firstcu from the - branch becomes a reqcu if there isn't a branch reqcu. */ - - if (firstcuflags < 0 && branchfirstcuflags >= 0 && - branchreqcuflags < 0) - { - branchreqcu = branchfirstcu; - branchreqcuflags = branchfirstcuflags; - } - - /* Now ensure that the reqcus match */ - - if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) || - reqcu != branchreqcu) - reqcuflags = REQ_NONE; - else - { - reqcu = branchreqcu; - reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY */ - } - } - - /* If lookbehind, check that this branch matches a fixed-length string, and - put the length into the OP_REVERSE item. Temporarily mark the end of the - branch with OP_END. If the branch contains OP_RECURSE, the result is - FFL_LATER (a negative value) because there may be forward references that - we can't check here. Set a flag to cause another lookbehind check at the - end. Why not do it all at the end? Because common errors can be picked up - here and the offset of the problem can be shown. */ - - if (lookbehind) - { - int fixed_length; - int count = 0; - *code = OP_END; - fixed_length = find_fixedlength(last_branch, (options & PCRE2_UTF) != 0, - FALSE, cb, NULL, &count); - if (fixed_length == FFL_LATER) - { - cb->check_lookbehind = TRUE; - } - else if (fixed_length < 0) - { - *errorcodeptr = fixed_length_errors[-fixed_length]; - *ptrptr = ptr; - return FALSE; - } - else - { - if (fixed_length > cb->max_lookbehind) - cb->max_lookbehind = fixed_length; - PUT(reverse_count, 0, fixed_length); - } - } - } - - /* Reached end of expression, either ')' or end of pattern. In the real - compile phase, go back through the alternative branches and reverse the chain - of offsets, with the field in the BRA item now becoming an offset to the - first alternative. If there are no alternatives, it points to the end of the - group. The length in the terminating ket is always the length of the whole - bracketed item. Return leaving the pointer at the terminating char. */ - - if (*ptr != CHAR_VERTICAL_LINE) - { - if (lengthptr == NULL) - { - size_t branch_length = code - last_branch; - do - { - size_t prev_length = GET(last_branch, 1); - PUT(last_branch, 1, branch_length); - branch_length = prev_length; - last_branch -= branch_length; - } - while (branch_length > 0); - } - - /* Fill in the ket */ - - *code = OP_KET; - PUT(code, 1, (int)(code - start_bracket)); - code += 1 + LINK_SIZE; - - /* If it was a capturing subpattern, check to see if it contained any - recursive back references. If so, we must wrap it in atomic brackets. In - any event, remove the block from the chain. */ - - if (capnumber > 0) - { - if (cb->open_caps->flag) - { - memmove(start_bracket + 1 + LINK_SIZE, start_bracket, - CU2BYTES(code - start_bracket)); - *start_bracket = OP_ONCE; - code += 1 + LINK_SIZE; - PUT(start_bracket, 1, (int)(code - start_bracket)); - *code = OP_KET; - PUT(code, 1, (int)(code - start_bracket)); - code += 1 + LINK_SIZE; - length += 2 + 2*LINK_SIZE; - } - cb->open_caps = cb->open_caps->next; - } - - /* Retain the highest bracket number, in case resetting was used. */ - - cb->bracount = max_bracount; - - /* Set values to pass back */ - - *codeptr = code; - *ptrptr = ptr; - *firstcuptr = firstcu; - *firstcuflagsptr = firstcuflags; - *reqcuptr = reqcu; - *reqcuflagsptr = reqcuflags; - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < length) - { - *errorcodeptr = ERR20; - return FALSE; - } - *lengthptr += length; - } - return TRUE; - } - - /* Another branch follows. In the pre-compile phase, we can move the code - pointer back to where it was for the start of the first branch. (That is, - pretend that each branch is the only one.) - - In the real compile phase, insert an ALT node. Its length field points back - to the previous branch while the bracket remains open. At the end the chain - is reversed. It's done like this so that the start of the bracket has a - zero offset until it is closed, making it possible to detect recursion. */ - - if (lengthptr != NULL) - { - code = *codeptr + 1 + LINK_SIZE + skipunits; - length += 1 + LINK_SIZE; - } - else - { - *code = OP_ALT; - PUT(code, 1, (int)(code - last_branch)); - bc.current_branch = last_branch = code; - code += 1 + LINK_SIZE; - } - - /* Advance past the vertical bar */ - - ptr++; - } -/* Control never reaches here */ -} - - - -/************************************************* -* Check for anchored pattern * -*************************************************/ - -/* Try to find out if this is an anchored regular expression. Consider each -alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket -all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then -it's anchored. However, if this is a multiline pattern, then only OP_SOD will -be found, because ^ generates OP_CIRCM in that mode. - -We can also consider a regex to be anchored if OP_SOM starts all its branches. -This is the code for \G, which means "match at start of match position, taking -into account the match offset". - -A branch is also implicitly anchored if it starts with .* and DOTALL is set, -because that will try the rest of the pattern at all possible matching points, -so there is no point trying again.... er .... - -.... except when the .* appears inside capturing parentheses, and there is a -subsequent back reference to those parentheses. We haven't enough information -to catch that case precisely. - -At first, the best we could do was to detect when .* was in capturing brackets -and the highest back reference was greater than or equal to that level. -However, by keeping a bitmap of the first 31 back references, we can catch some -of the more common cases more precisely. - -... A second exception is when the .* appears inside an atomic group, because -this prevents the number of characters it matches from being adjusted. - -Arguments: - code points to start of the compiled pattern - bracket_map a bitmap of which brackets we are inside while testing; this - handles up to substring 31; after that we just have to take - the less precise approach - cb points to the compile data block - atomcount atomic group level - -Returns: TRUE or FALSE -*/ - -static BOOL -is_anchored(register PCRE2_SPTR code, unsigned int bracket_map, - compile_block *cb, int atomcount) -{ -do { - PCRE2_SPTR scode = first_significant_code( - code + PRIV(OP_lengths)[*code], FALSE); - register int op = *scode; - - /* Non-capturing brackets */ - - if (op == OP_BRA || op == OP_BRAPOS || - op == OP_SBRA || op == OP_SBRAPOS) - { - if (!is_anchored(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Capturing brackets */ - - else if (op == OP_CBRA || op == OP_CBRAPOS || - op == OP_SCBRA || op == OP_SCBRAPOS) - { - int n = GET2(scode, 1+LINK_SIZE); - int new_map = bracket_map | ((n < 32)? (1u << n) : 1); - if (!is_anchored(scode, new_map, cb, atomcount)) return FALSE; - } - - /* Positive forward assertions and conditions */ - - else if (op == OP_ASSERT || op == OP_COND) - { - if (!is_anchored(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Atomic groups */ - - else if (op == OP_ONCE || op == OP_ONCE_NC) - { - if (!is_anchored(scode, bracket_map, cb, atomcount + 1)) - return FALSE; - } - - /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and - it isn't in brackets that are or may be referenced or inside an atomic - group. There is also an option that disables auto-anchoring. */ - - else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || - op == OP_TYPEPOSSTAR)) - { - if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 || - atomcount > 0 || cb->had_pruneorskip || - (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) - return FALSE; - } - - /* Check for explicit anchoring */ - - else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; - - code += GET(code, 1); - } -while (*code == OP_ALT); /* Loop for each alternative */ -return TRUE; -} - - - -/************************************************* -* Check for starting with ^ or .* * -*************************************************/ - -/* This is called to find out if every branch starts with ^ or .* so that -"first char" processing can be done to speed things up in multiline -matching and for non-DOTALL patterns that start with .* (which must start at -the beginning or after \n). As in the case of is_anchored() (see above), we -have to take account of back references to capturing brackets that contain .* -because in that case we can't make the assumption. Also, the appearance of .* -inside atomic brackets or in a pattern that contains *PRUNE or *SKIP does not -count, because once again the assumption no longer holds. - -Arguments: - code points to start of the compiled pattern or a group - bracket_map a bitmap of which brackets we are inside while testing; this - handles up to substring 31; after that we just have to take - the less precise approach - cb points to the compile data - atomcount atomic group level - -Returns: TRUE or FALSE -*/ - -static BOOL -is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, - int atomcount) -{ -do { - PCRE2_SPTR scode = first_significant_code( - code + PRIV(OP_lengths)[*code], FALSE); - register int op = *scode; - - /* If we are at the start of a conditional assertion group, *both* the - conditional assertion *and* what follows the condition must satisfy the test - for start of line. Other kinds of condition fail. Note that there may be an - auto-callout at the start of a condition. */ - - if (op == OP_COND) - { - scode += 1 + LINK_SIZE; - - if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; - else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE); - - switch (*scode) - { - case OP_CREF: - case OP_DNCREF: - case OP_RREF: - case OP_DNRREF: - case OP_FAIL: - case OP_FALSE: - case OP_TRUE: - return FALSE; - - default: /* Assertion */ - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - do scode += GET(scode, 1); while (*scode == OP_ALT); - scode += 1 + LINK_SIZE; - break; - } - scode = first_significant_code(scode, FALSE); - op = *scode; - } - - /* Non-capturing brackets */ - - if (op == OP_BRA || op == OP_BRAPOS || - op == OP_SBRA || op == OP_SBRAPOS) - { - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Capturing brackets */ - - else if (op == OP_CBRA || op == OP_CBRAPOS || - op == OP_SCBRA || op == OP_SCBRAPOS) - { - int n = GET2(scode, 1+LINK_SIZE); - int new_map = bracket_map | ((n < 32)? (1u << n) : 1); - if (!is_startline(scode, new_map, cb, atomcount)) return FALSE; - } - - /* Positive forward assertions */ - - else if (op == OP_ASSERT) - { - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Atomic brackets */ - - else if (op == OP_ONCE || op == OP_ONCE_NC) - { - if (!is_startline(scode, bracket_map, cb, atomcount + 1)) return FALSE; - } - - /* .* means "start at start or after \n" if it isn't in atomic brackets or - brackets that may be referenced, as long as the pattern does not contain - *PRUNE or *SKIP, because these break the feature. Consider, for example, - /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. not at the - start of a line. There is also an option that disables this optimization. */ - - else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) - { - if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 || - atomcount > 0 || cb->had_pruneorskip || - (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) - return FALSE; - } - - /* Check for explicit circumflex; anything else gives a FALSE result. Note - in particular that this includes atomic brackets OP_ONCE and OP_ONCE_NC - because the number of characters matched by .* cannot be adjusted inside - them. */ - - else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; - - /* Move on to the next alternative */ - - code += GET(code, 1); - } -while (*code == OP_ALT); /* Loop for each alternative */ -return TRUE; -} - - - -/************************************************* -* Check for asserted fixed first code unit * -*************************************************/ - -/* During compilation, the "first code unit" settings from forward assertions -are discarded, because they can cause conflicts with actual literals that -follow. However, if we end up without a first code unit setting for an -unanchored pattern, it is worth scanning the regex to see if there is an -initial asserted first code unit. If all branches start with the same asserted -code unit, or with a non-conditional bracket all of whose alternatives start -with the same asserted code unit (recurse ad lib), then we return that code -unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with -REQ_NONE in the flags. - -Arguments: - code points to start of compiled pattern - flags points to the first code unit flags - inassert TRUE if in an assertion - -Returns: the fixed first code unit, or 0 with REQ_NONE in flags -*/ - -static uint32_t -find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, BOOL inassert) -{ -register uint32_t c = 0; -int cflags = REQ_NONE; - -*flags = REQ_NONE; -do { - uint32_t d; - int dflags; - int xl = (*code == OP_CBRA || *code == OP_SCBRA || - *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; - PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE); - register PCRE2_UCHAR op = *scode; - - switch(op) - { - default: - return 0; - - case OP_BRA: - case OP_BRAPOS: - case OP_CBRA: - case OP_SCBRA: - case OP_CBRAPOS: - case OP_SCBRAPOS: - case OP_ASSERT: - case OP_ONCE: - case OP_ONCE_NC: - d = find_firstassertedcu(scode, &dflags, op == OP_ASSERT); - if (dflags < 0) - return 0; - if (cflags < 0) { c = d; cflags = dflags; } - else if (c != d || cflags != dflags) return 0; - break; - - case OP_EXACT: - scode += IMM2_SIZE; - /* Fall through */ - - case OP_CHAR: - case OP_PLUS: - case OP_MINPLUS: - case OP_POSPLUS: - if (!inassert) return 0; - if (cflags < 0) { c = scode[1]; cflags = 0; } - else if (c != scode[1]) return 0; - break; - - case OP_EXACTI: - scode += IMM2_SIZE; - /* Fall through */ - - case OP_CHARI: - case OP_PLUSI: - case OP_MINPLUSI: - case OP_POSPLUSI: - if (!inassert) return 0; - if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; } - else if (c != scode[1]) return 0; - break; - } - - code += GET(code, 1); - } -while (*code == OP_ALT); - -*flags = cflags; -return c; -} - - - -/************************************************* -* Add an entry to the name/number table * -*************************************************/ - -/* This function is called between compiling passes to add an entry to the -name/number table, maintaining alphabetical order. Checking for permitted -and forbidden duplicates has already been done. - -Arguments: - cb the compile data block - name the name to add - length the length of the name - groupno the group number - -Returns: nothing -*/ - -static void -add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length, - unsigned int groupno) -{ -int i; -PCRE2_UCHAR *slot = cb->name_table; - -for (i = 0; i < cb->names_found; i++) - { - int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length)); - if (crc == 0 && slot[IMM2_SIZE+length] != 0) - crc = -1; /* Current name is a substring */ - - /* Make space in the table and break the loop for an earlier name. For a - duplicate or later name, carry on. We do this for duplicates so that in the - simple case (when ?(| is not used) they are in order of their numbers. In all - cases they are in the order in which they appear in the pattern. */ - - if (crc < 0) - { - memmove(slot + cb->name_entry_size, slot, - CU2BYTES((cb->names_found - i) * cb->name_entry_size)); - break; - } - - /* Continue the loop for a later or duplicate name */ - - slot += cb->name_entry_size; - } - -PUT2(slot, 0, groupno); -memcpy(slot + IMM2_SIZE, name, CU2BYTES(length)); -cb->names_found++; - -/* Add a terminating zero and fill the rest of the slot with zeroes so that -the memory is all initialized. Otherwise valgrind moans about uninitialized -memory when saving serialized compiled patterns. */ - -memset(slot + IMM2_SIZE + length, 0, - CU2BYTES(cb->name_entry_size - length - IMM2_SIZE)); -} - - - -/************************************************* -* External function to compile a pattern * -*************************************************/ - -/* This function reads a regular expression in the form of a string and returns -a pointer to a block of store holding a compiled version of the expression. - -Arguments: - pattern the regular expression - patlen the length of the pattern, or PCRE2_ZERO_TERMINATED - options option bits - errorptr pointer to errorcode - erroroffset pointer to error offset - ccontext points to a compile context or is NULL - -Returns: pointer to compiled data block, or NULL on error, - with errorcode and erroroffset set -*/ - -PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION -pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options, - int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext) -{ -BOOL utf; /* Set TRUE for UTF mode */ -pcre2_real_code *re = NULL; /* What we will return */ -compile_block cb; /* "Static" compile-time data */ -const uint8_t *tables; /* Char tables base pointer */ - -PCRE2_UCHAR *code; /* Current pointer in compiled code */ -PCRE2_SPTR codestart; /* Start of compiled code */ -PCRE2_SPTR ptr; /* Current pointer in pattern */ - -size_t length = 1; /* Allow or final END opcode */ -size_t usedlength; /* Actual length used */ -size_t re_blocksize; /* Size of memory block */ - -int32_t firstcuflags, reqcuflags; /* Type of first/req code unit */ -uint32_t firstcu, reqcu; /* Value of first/req code unit */ -uint32_t setflags = 0; /* NL and BSR set flags */ - -uint32_t skipatstart; /* When checking (*UTF) etc */ -uint32_t limit_match = UINT32_MAX; /* Unset match limits */ -uint32_t limit_recursion = UINT32_MAX; - -int newline = 0; /* Unset; can be set by the pattern */ -int bsr = 0; /* Unset; can be set by the pattern */ -int errorcode = 0; /* Initialize to avoid compiler warn */ - -/* Comments at the head of this file explain about these variables. */ - -PCRE2_UCHAR *copied_pattern = NULL; -PCRE2_UCHAR stack_copied_pattern[COPIED_PATTERN_SIZE]; -named_group named_groups[NAMED_GROUP_LIST_SIZE]; - -/* The workspace is used in different ways in the different compiling phases. -It needs to be 16-bit aligned for the preliminary group scan, and 32-bit -aligned for the group information cache. */ - -uint32_t c32workspace[C32_WORK_SIZE]; -PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c32workspace; - - -/* -------------- Check arguments and set up the pattern ----------------- */ - -/* There must be error code and offset pointers. */ - -if (errorptr == NULL || erroroffset == NULL) return NULL; -*errorptr = ERR0; -*erroroffset = 0; - -/* There must be a pattern! */ - -if (pattern == NULL) - { - *errorptr = ERR16; - return NULL; - } - -/* Check that all undefined public option bits are zero. */ - -if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0) - { - *errorptr = ERR17; - return NULL; - } - -/* A NULL compile context means "use a default context" */ - -if (ccontext == NULL) - ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context)); - -/* A zero-terminated pattern is indicated by the special length value -PCRE2_ZERO_TERMINATED. Otherwise, we make a copy of the pattern and add a zero, -to ensure that it is always possible to look one code unit beyond the end of -the pattern's characters. In both cases, check that the pattern is overlong. */ - -if (patlen == PCRE2_ZERO_TERMINATED) - { - patlen = PRIV(strlen)(pattern); - if (patlen > ccontext->max_pattern_length) - { - *errorptr = ERR88; - return NULL; - } - } -else - { - if (patlen > ccontext->max_pattern_length) - { - *errorptr = ERR88; - return NULL; - } - if (patlen < COPIED_PATTERN_SIZE) - copied_pattern = stack_copied_pattern; - else - { - copied_pattern = ccontext->memctl.malloc(CU2BYTES(patlen + 1), - ccontext->memctl.memory_data); - if (copied_pattern == NULL) - { - *errorptr = ERR21; - return NULL; - } - } - memcpy(copied_pattern, pattern, CU2BYTES(patlen)); - copied_pattern[patlen] = 0; - pattern = copied_pattern; - } - -/* ------------ Initialize the "static" compile data -------------- */ - -tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables); - -cb.lcc = tables + lcc_offset; /* Individual */ -cb.fcc = tables + fcc_offset; /* character */ -cb.cbits = tables + cbits_offset; /* tables */ -cb.ctypes = tables + ctypes_offset; - -cb.assert_depth = 0; -cb.bracount = cb.final_bracount = 0; -cb.cx = ccontext; -cb.dupnames = FALSE; -cb.end_pattern = pattern + patlen; -cb.nestptr[0] = cb.nestptr[1] = NULL; -cb.external_flags = 0; -cb.external_options = options; -cb.groupinfo = c32workspace; -cb.had_recurse = FALSE; -cb.iscondassert = FALSE; -cb.max_lookbehind = 0; -cb.name_entry_size = 0; -cb.name_table = NULL; -cb.named_groups = named_groups; -cb.named_group_list_size = NAMED_GROUP_LIST_SIZE; -cb.names_found = 0; -cb.open_caps = NULL; -cb.parens_depth = 0; -cb.req_varyopt = 0; -cb.start_code = cworkspace; -cb.start_pattern = pattern; -cb.start_workspace = cworkspace; -cb.workspace_size = COMPILE_WORK_SIZE; - -/* Maximum back reference and backref bitmap. The bitmap records up to 31 back -references to help in deciding whether (.*) can be treated as anchored or not. -*/ - -cb.top_backref = 0; -cb.backref_map = 0; - -/* --------------- Start looking at the pattern --------------- */ - -/* Check for global one-time option settings at the start of the pattern, and -remember the offset to the actual regex. */ - -ptr = pattern; -skipatstart = 0; - -while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && - ptr[skipatstart+1] == CHAR_ASTERISK) - { - unsigned int i; - for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) - { - pso *p = pso_list + i; - - if (PRIV(strncmp_c8)(ptr+skipatstart+2, (char *)(p->name), p->length) == 0) - { - uint32_t c, pp; - - skipatstart += p->length + 2; - switch(p->type) - { - case PSO_OPT: - cb.external_options |= p->value; - break; - - case PSO_FLG: - setflags |= p->value; - break; - - case PSO_NL: - newline = p->value; - setflags |= PCRE2_NL_SET; - break; - - case PSO_BSR: - bsr = p->value; - setflags |= PCRE2_BSR_SET; - break; - - case PSO_LIMM: - case PSO_LIMR: - c = 0; - pp = skipatstart; - if (!IS_DIGIT(ptr[pp])) - { - errorcode = ERR60; - ptr += pp; - goto HAD_ERROR; - } - while (IS_DIGIT(ptr[pp])) - { - if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */ - c = c*10 + (ptr[pp++] - CHAR_0); - } - if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS) - { - errorcode = ERR60; - ptr += pp; - goto HAD_ERROR; - } - if (p->type == PSO_LIMM) limit_match = c; - else limit_recursion = c; - skipatstart += pp - skipatstart; - break; - } - break; /* Out of the table scan loop */ - } - } - if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ - } - -/* End of pattern-start options; advance to start of real regex. */ - -ptr += skipatstart; - -/* Can't support UTF or UCP unless PCRE2 has been compiled with UTF support. */ - -#ifndef SUPPORT_UNICODE -if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) - { - errorcode = ERR32; - goto HAD_ERROR; - } -#endif - -/* Check UTF. We have the original options in 'options', with that value as -modified by (*UTF) etc in cb->external_options. */ - -utf = (cb.external_options & PCRE2_UTF) != 0; -if (utf) - { - if ((options & PCRE2_NEVER_UTF) != 0) - { - errorcode = ERR74; - goto HAD_ERROR; - } - if ((options & PCRE2_NO_UTF_CHECK) == 0 && - (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) - goto HAD_UTF_ERROR; - } - -/* Check UCP lockout. */ - -if ((cb.external_options & (PCRE2_UCP|PCRE2_NEVER_UCP)) == - (PCRE2_UCP|PCRE2_NEVER_UCP)) - { - errorcode = ERR75; - goto HAD_ERROR; - } - -/* Process the BSR setting. */ - -if (bsr == 0) bsr = ccontext->bsr_convention; - -/* Process the newline setting. */ - -if (newline == 0) newline = ccontext->newline_convention; -cb.nltype = NLTYPE_FIXED; -switch(newline) - { - case PCRE2_NEWLINE_CR: - cb.nllen = 1; - cb.nl[0] = CHAR_CR; - break; - - case PCRE2_NEWLINE_LF: - cb.nllen = 1; - cb.nl[0] = CHAR_NL; - break; - - case PCRE2_NEWLINE_CRLF: - cb.nllen = 2; - cb.nl[0] = CHAR_CR; - cb.nl[1] = CHAR_NL; - break; - - case PCRE2_NEWLINE_ANY: - cb.nltype = NLTYPE_ANY; - break; - - case PCRE2_NEWLINE_ANYCRLF: - cb.nltype = NLTYPE_ANYCRLF; - break; - - default: - errorcode = ERR56; - goto HAD_ERROR; - } - -/* Before we do anything else, do a pre-scan of the pattern in order to -discover the named groups and their numerical equivalents, so that this -information is always available for the remaining processing. */ - -errorcode = scan_for_captures(&ptr, cb.external_options, &cb); -if (errorcode != 0) goto HAD_ERROR; - -/* For obscure debugging this code can be enabled. */ - -#if 0 - { - int i; - named_group *ng = cb.named_groups; - fprintf(stderr, "+++Captures: %d\n", cb.final_bracount); - for (i = 0; i < cb.names_found; i++, ng++) - { - fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name); - } - } -#endif - -/* Reset current bracket count to zero and current pointer to the start of the -pattern. */ - -cb.bracount = 0; -ptr = pattern + skipatstart; - -/* Pretend to compile the pattern while actually just accumulating the amount -of memory required in the 'length' variable. This behaviour is triggered by -passing a non-NULL final argument to compile_regex(). We pass a block of -workspace (cworkspace) for it to compile parts of the pattern into; the -compiled code is discarded when it is no longer needed, so hopefully this -workspace will never overflow, though there is a test for its doing so. - -On error, errorcode will be set non-zero, so we don't need to look at the -result of the function. The initial options have been put into the cb block so -that they can be changed if an option setting is found within the regex right -at the beginning. Bringing initial option settings outside can help speed up -starting point checks. We still have to pass a separate options variable (the -first argument) because that may change as the pattern is processed. */ - -code = cworkspace; -*code = OP_BRA; - -(void)compile_regex(cb.external_options, &code, &ptr, &errorcode, FALSE, - FALSE, 0, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, - &cb, &length); - -if (errorcode != 0) goto HAD_ERROR; -if (length > MAX_PATTERN_SIZE) - { - errorcode = ERR20; - goto HAD_ERROR; - } - -/* Compute the size of, and then get and initialize, the data block for storing -the compiled pattern and names table. Integer overflow should no longer be -possible because nowadays we limit the maximum value of cb.names_found and -cb.name_entry_size. */ - -re_blocksize = sizeof(pcre2_real_code) + - CU2BYTES(length + cb.names_found * cb.name_entry_size); -re = (pcre2_real_code *) - ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data); -if (re == NULL) - { - errorcode = ERR21; - goto HAD_ERROR; - } - -re->memctl = ccontext->memctl; -re->tables = tables; -re->executable_jit = NULL; -memset(re->start_bitmap, 0, 32 * sizeof(uint8_t)); -re->blocksize = re_blocksize; -re->magic_number = MAGIC_NUMBER; -re->compile_options = options; -re->overall_options = cb.external_options; -re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags; -re->limit_match = limit_match; -re->limit_recursion = limit_recursion; -re->first_codeunit = 0; -re->last_codeunit = 0; -re->bsr_convention = bsr; -re->newline_convention = newline; -re->max_lookbehind = 0; -re->minlength = 0; -re->top_bracket = 0; -re->top_backref = 0; -re->name_entry_size = cb.name_entry_size; -re->name_count = cb.names_found; - -/* The basic block is immediately followed by the name table, and the compiled -code follows after that. */ - -codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) + - re->name_entry_size * re->name_count; - -/* Workspace is needed to remember information about numbered groups: whether a -group can match an empty string and what its fixed length is. This is done to -avoid the possibility of recursive references causing very long compile times -when checking these features. Unnumbered groups do not have this exposure since -they cannot be referenced. We use an indexed vector for this purpose. If there -are sufficiently few groups, it can be the c32workspace vector, as set up -above. Otherwise we have to get/free a special vector. The vector must be -initialized to zero. */ - -if (cb.final_bracount >= C32_WORK_SIZE) - { - cb.groupinfo = ccontext->memctl.malloc( - (cb.final_bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data); - if (cb.groupinfo == NULL) - { - errorcode = ERR21; - goto HAD_ERROR; - } - } -memset(cb.groupinfo, 0, (cb.final_bracount + 1) * sizeof(uint32_t)); - -/* Update the compile data block for the actual compile. The starting points of -the name/number translation table and of the code are passed around in the -compile data block. The start/end pattern and initial options are already set -from the pre-compile phase, as is the name_entry_size field. Reset the bracket -count and the names_found field. */ - -cb.parens_depth = 0; -cb.assert_depth = 0; -cb.bracount = 0; -cb.max_lookbehind = 0; -cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); -cb.start_code = codestart; -cb.iscondassert = FALSE; -cb.req_varyopt = 0; -cb.had_accept = FALSE; -cb.had_pruneorskip = FALSE; -cb.check_lookbehind = FALSE; -cb.open_caps = NULL; - -/* If any named groups were found, create the name/number table from the list -created in the pre-pass. */ - -if (cb.names_found > 0) - { - int i = cb.names_found; - named_group *ng = cb.named_groups; - cb.names_found = 0; - for (; i > 0; i--, ng++) - add_name_to_table(&cb, ng->name, ng->length, ng->number); - } - -/* Set up a starting, non-extracting bracket, then compile the expression. On -error, errorcode will be set non-zero, so we don't need to look at the result -of the function here. */ - -ptr = pattern + skipatstart; -code = (PCRE2_UCHAR *)codestart; -*code = OP_BRA; -(void)compile_regex(re->overall_options, &code, &ptr, &errorcode, FALSE, FALSE, - 0, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL); - -re->top_bracket = cb.bracount; -re->top_backref = cb.top_backref; -re->max_lookbehind = cb.max_lookbehind; - -if (cb.had_accept) - { - reqcu = 0; /* Must disable after (*ACCEPT) */ - reqcuflags = REQ_NONE; - } - -/* Fill in the final opcode and check for disastrous overflow. If no overflow, -but the estimated length exceeds the really used length, adjust the value of -re->blocksize, and if valgrind support is configured, mark the extra allocated -memory as unaddressable, so that any out-of-bound reads can be detected. */ - -*code++ = OP_END; -usedlength = code - codestart; -if (usedlength > length) errorcode = ERR23; else - { - re->blocksize -= CU2BYTES(length - usedlength); -#ifdef SUPPORT_VALGRIND - VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength)); -#endif - } - -/* Scan the pattern for recursion/subroutine calls and convert the group -numbers into offsets. Maintain a small cache so that repeated groups containing -recursions are efficiently handled. */ - -#define RSCAN_CACHE_SIZE 8 - -if (errorcode == 0 && cb.had_recurse) - { - PCRE2_UCHAR *rcode; - PCRE2_SPTR rgroup; - int ccount = 0; - int start = RSCAN_CACHE_SIZE; - recurse_cache rc[RSCAN_CACHE_SIZE]; - - for (rcode = (PCRE2_UCHAR *)find_recurse(codestart, utf); - rcode != NULL; - rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf)) - { - int i, p, recno; - - recno = (int)GET(rcode, 1); - if (recno == 0) rgroup = codestart; else - { - PCRE2_SPTR search_from = codestart; - rgroup = NULL; - for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7) - { - if (recno == rc[p].recno) - { - rgroup = rc[p].group; - break; - } - - /* Group n+1 must always start to the right of group n, so we can save - search time below when the new group number is greater than any of the - previously found groups. */ - - if (recno > rc[p].recno) search_from = rc[p].group; - } - - if (rgroup == NULL) - { - rgroup = PRIV(find_bracket)(search_from, utf, recno); - if (rgroup == NULL) - { - errorcode = ERR53; - break; - } - if (--start < 0) start = RSCAN_CACHE_SIZE - 1; - rc[start].recno = recno; - rc[start].group = rgroup; - if (ccount < RSCAN_CACHE_SIZE) ccount++; - } - } - - PUT(rcode, 1, rgroup - codestart); - } - } - -/* In rare debugging situations we sometimes need to look at the compiled code -at this stage. */ - -#ifdef CALL_PRINTINT -pcre2_printint(re, stderr, TRUE); -fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength); -#endif - -/* After a successful compile, give an error if there's back reference to a -non-existent capturing subpattern. Then, unless disabled, check whether any -single character iterators can be auto-possessified. The function overwrites -the appropriate opcode values, so the type of the pointer must be cast. NOTE: -the intermediate variable "temp" is used in this code because at least one -compiler gives a warning about loss of "const" attribute if the cast -(PCRE2_UCHAR *)codestart is used directly in the function call. */ - -if (errorcode == 0) - { - if (re->top_backref > re->top_bracket) errorcode = ERR15; - else if ((re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0) - { - PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart; - if (PRIV(auto_possessify)(temp, utf, &cb) != 0) errorcode = ERR80; - } - } - -/* If there were any lookbehind assertions that contained OP_RECURSE -(recursions or subroutine calls), a flag is set for them to be checked here, -because they may contain forward references. Actual recursions cannot be fixed -length, but subroutine calls can. It is done like this so that those without -OP_RECURSE that are not fixed length get a diagnosic with a useful offset. The -exceptional ones forgo this. We scan the pattern to check that they are fixed -length, and set their lengths. */ - -if (errorcode == 0 && cb.check_lookbehind) - { - PCRE2_UCHAR *cc = (PCRE2_UCHAR *)codestart; - - /* Loop, searching for OP_REVERSE items, and process those that do not have - their length set. (Actually, it will also re-process any that have a length - of zero, but that is a pathological case, and it does no harm.) When we find - one, we temporarily terminate the branch it is in while we scan it. Note that - calling find_bracket() with a negative group number returns a pointer to the - OP_REVERSE item, not the actual lookbehind. */ - - for (cc = (PCRE2_UCHAR *)PRIV(find_bracket)(codestart, utf, -1); - cc != NULL; - cc = (PCRE2_UCHAR *)PRIV(find_bracket)(cc, utf, -1)) - { - if (GET(cc, 1) == 0) - { - int fixed_length; - int count = 0; - PCRE2_UCHAR *be = cc - 1 - LINK_SIZE + GET(cc, -LINK_SIZE); - int end_op = *be; - *be = OP_END; - fixed_length = find_fixedlength(cc, utf, TRUE, &cb, NULL, &count); - *be = end_op; - if (fixed_length < 0) - { - errorcode = fixed_length_errors[-fixed_length]; - break; - } - if (fixed_length > cb.max_lookbehind) cb.max_lookbehind = fixed_length; - PUT(cc, 1, fixed_length); - } - cc += 1 + LINK_SIZE; - } - - /* The previous value of the maximum lookbehind was transferred to the - compiled regex block above. We could have updated this value in the loop - above, but keep the two values in step, just in case some later code below - uses the cb value. */ - - re->max_lookbehind = cb.max_lookbehind; - } - -/* Failed to compile, or error while post-processing. Earlier errors get here -via the dreaded goto. */ - -if (errorcode != 0) - { - HAD_ERROR: - *erroroffset = (int)(ptr - pattern); - HAD_UTF_ERROR: - *errorptr = errorcode; - pcre2_code_free(re); - re = NULL; - goto EXIT; - } - -/* Successful compile. If the anchored option was not passed, set it if -we can determine that the pattern is anchored by virtue of ^ characters or \A -or anything else, such as starting with non-atomic .* when DOTALL is set and -there are no occurrences of *PRUNE or *SKIP (though there is an option to -disable this case). */ - -if ((re->overall_options & PCRE2_ANCHORED) == 0 && - is_anchored(codestart, 0, &cb, 0)) - re->overall_options |= PCRE2_ANCHORED; - -/* If the pattern is still not anchored and we do not have a first code unit, -see if there is one that is asserted (these are not saved during the compile -because they can cause conflicts with actual literals that follow). This code -need not be obeyed if PCRE2_NO_START_OPTIMIZE is set, as the data it would -create will not be used. */ - -if ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0) - { - if (firstcuflags < 0) - firstcu = find_firstassertedcu(codestart, &firstcuflags, FALSE); - - /* Save the data for a first code unit. */ - - if (firstcuflags >= 0) - { - re->first_codeunit = firstcu; - re->flags |= PCRE2_FIRSTSET; - - /* Handle caseless first code units. */ - - if ((firstcuflags & REQ_CASELESS) != 0) - { - if (firstcu < 128 || (!utf && firstcu < 255)) - { - if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS; - } - - /* The first code unit is > 128 in UTF mode, or > 255 otherwise. In - 8-bit UTF mode, codepoints in the range 128-255 are introductory code - points and cannot have another case. In 16-bit and 32-bit modes, we can - check wide characters when UTF (and therefore UCP) is supported. */ - -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - else if (firstcu <= MAX_UTF_CODE_POINT && - UCD_OTHERCASE(firstcu) != firstcu) - re->flags |= PCRE2_FIRSTCASELESS; -#endif - } - } - - /* When there is no first code unit, see if we can set the PCRE2_STARTLINE - flag. This is helpful for multiline matches when all branches start with ^ - and also when all branches start with non-atomic .* for non-DOTALL matches - when *PRUNE and SKIP are not present. (There is an option that disables this - case.) */ - - else if (is_startline(codestart, 0, &cb, 0)) re->flags |= PCRE2_STARTLINE; - } - -/* Handle the "required code unit", if one is set. In the case of an anchored -pattern, do this only if it follows a variable length item in the pattern. -Again, skip this if PCRE2_NO_START_OPTIMIZE is set. */ - -if (reqcuflags >= 0 && - ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0 || - (reqcuflags & REQ_VARY) != 0)) - { - re->last_codeunit = reqcu; - re->flags |= PCRE2_LASTSET; - - /* Handle caseless required code units as for first code units (above). */ - - if ((reqcuflags & REQ_CASELESS) != 0) - { - if (reqcu < 128 || (!utf && reqcu < 255)) - { - if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS; - } -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu) - re->flags |= PCRE2_LASTCASELESS; -#endif - } - } - -/* Check for a pattern than can match an empty string, so that this information -can be provided to applications. */ - -do - { - int count = 0; - int rc = could_be_empty_branch(codestart, code, utf, &cb, TRUE, NULL, &count); - if (rc < 0) - { - errorcode = ERR86; - goto HAD_ERROR; - } - if (rc > 0) - { - re->flags |= PCRE2_MATCH_EMPTY; - break; - } - codestart += GET(codestart, 1); - } -while (*codestart == OP_ALT); - -/* Finally, unless PCRE2_NO_START_OPTIMIZE is set, study the compiled pattern -to set up information such as a bitmap of starting code units and a minimum -matching length. */ - -if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && - PRIV(study)(re) != 0) - { - errorcode = ERR31; - goto HAD_ERROR; - } - -/* Control ends up here in all cases. If memory was obtained for a -zero-terminated copy of the pattern, remember to free it before returning. Also -free the list of named groups if a larger one had to be obtained, and likewise -the group information vector. */ - -EXIT: -if (copied_pattern != stack_copied_pattern) - ccontext->memctl.free(copied_pattern, ccontext->memctl.memory_data); -if (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE) - ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data); -if (cb.groupinfo != c32workspace) - ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data); - -return re; /* Will be NULL after an error */ -} - -/* End of pcre2_compile.c */ - -#pragma warning(pop) \ No newline at end of file +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK cb /* Block containing newline information */ +#define PSSTART start_pattern /* Field containing processed string start */ +#define PSEND end_pattern /* Field containing processed string end */ + +#include "pcre2_internal.h" + +/* In rare error cases debugging might require calling pcre2_printint(). */ + +#if 0 +#ifdef EBCDIC +#define PRINTABLE(c) ((c) >= 64 && (c) < 255) +#else +#define PRINTABLE(c) ((c) >= 32 && (c) < 127) +#endif +#include "pcre2_printint.c" +#define DEBUG_CALL_PRINTINT +#endif + +/* Other debugging code can be enabled by these defines. */ + +/* #define DEBUG_SHOW_CAPTURES */ +/* #define DEBUG_SHOW_PARSED */ + +/* There are a few things that vary with different code unit sizes. Handle them +by defining macros in order to minimize #if usage. */ + +#if PCRE2_CODE_UNIT_WIDTH == 8 +#define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5 +#define XDIGIT(c) xdigitab[c] + +#else /* Either 16-bit or 32-bit */ +#define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff) + +#if PCRE2_CODE_UNIT_WIDTH == 16 +#define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6 + +#else /* 32-bit */ +#define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6 +#endif +#endif + +/* Macros to store and retrieve a PCRE2_SIZE value in the parsed pattern, which +consists of uint32_t elements. Assume that if uint32_t can't hold it, two of +them will be able to (i.e. assume a 64-bit world). */ + +#if PCRE2_SIZE_MAX <= UINT32_MAX +#define PUTOFFSET(s,p) *p++ = s +#define GETOFFSET(s,p) s = *p++ +#define GETPLUSOFFSET(s,p) s = *(++p) +#define READPLUSOFFSET(s,p) s = p[1] +#define SKIPOFFSET(p) p++ +#define SIZEOFFSET 1 +#else +#define PUTOFFSET(s,p) \ + { *p++ = (uint32_t)(s >> 32); *p++ = (uint32_t)(s & 0xffffffff); } +#define GETOFFSET(s,p) \ + { s = ((PCRE2_SIZE)p[0] << 32) | (PCRE2_SIZE)p[1]; p += 2; } +#define GETPLUSOFFSET(s,p) \ + { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; p += 2; } +#define READPLUSOFFSET(s,p) \ + { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; } +#define SKIPOFFSET(p) p += 2 +#define SIZEOFFSET 2 +#endif + +/* Macros for manipulating elements of the parsed pattern vector. */ + +#define META_CODE(x) (x & 0xffff0000u) +#define META_DATA(x) (x & 0x0000ffffu) +#define META_DIFF(x,y) ((x-y)>>16) + +/* Function definitions to allow mutual recursion */ + +#ifdef SUPPORT_UNICODE +static unsigned int + add_list_to_class_internal(uint8_t *, PCRE2_UCHAR **, uint32_t, + compile_block *, const uint32_t *, unsigned int); +#endif + +static int + compile_regex(uint32_t, PCRE2_UCHAR **, uint32_t **, int *, uint32_t, + uint32_t *, int32_t *, uint32_t *, int32_t *, branch_chain *, + compile_block *, PCRE2_SIZE *); + +static int + get_branchlength(uint32_t **, int *, int *, parsed_recurse_check *, + compile_block *); + +static BOOL + set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *, + compile_block *); + + + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +#define MAX_GROUP_NUMBER 65535u +#define MAX_REPEAT_COUNT 65535u +#define REPEAT_UNLIMITED (MAX_REPEAT_COUNT+1) + +/* COMPILE_WORK_SIZE specifies the size of stack workspace, which is used in +different ways in the different pattern scans. The parsing and group- +identifying pre-scan uses it to handle nesting, and needs it to be 16-bit +aligned for this. Having defined the size in code units, we set up +C16_WORK_SIZE as the number of elements in the 16-bit vector. + +During the first compiling phase, when determining how much memory is required, +the regex is partly compiled into this space, but the compiled parts are +discarded as soon as they can be, so that hopefully there will never be an +overrun. The code does, however, check for an overrun, which can occur for +pathological patterns. The size of the workspace depends on LINK_SIZE because +the length of compiled items varies with this. + +In the real compile phase, this workspace is not currently used. */ + +#define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */ + +#define C16_WORK_SIZE \ + ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t)) + +/* A uint32_t vector is used for caching information about the size of +capturing groups, to improve performance. A default is created on the stack of +this size. */ + +#define GROUPINFO_DEFAULT_SIZE 256 + +/* The overrun tests check for a slightly smaller size so that they detect the +overrun before it actually does run off the end of the data block. */ + +#define WORK_SIZE_SAFETY_MARGIN (100) + +/* This value determines the size of the initial vector that is used for +remembering named groups during the pre-compile. It is allocated on the stack, +but if it is too small, it is expanded, in a similar way to the workspace. The +value is the number of slots in the list. */ + +#define NAMED_GROUP_LIST_SIZE 20 + +/* The pre-compiling pass over the pattern creates a parsed pattern in a vector +of uint32_t. For short patterns this lives on the stack, with this size. Heap +memory is used for longer patterns. */ + +#define PARSED_PATTERN_DEFAULT_SIZE 1024 + +/* Maximum length value to check against when making sure that the variable +that holds the compiled pattern length does not overflow. We make it a bit less +than INT_MAX to allow for adding in group terminating code units, so that we +don't have to check them every time. */ + +#define OFLOW_MAX (INT_MAX - 20) + +/* Code values for parsed patterns, which are stored in a vector of 32-bit +unsigned ints. Values less than META_END are literal data values. The coding +for identifying the item is in the top 16-bits, leaving 16 bits for the +additional data that some of them need. The META_CODE, META_DATA, and META_DIFF +macros are used to manipulate parsed pattern elements. + +NOTE: When these definitions are changed, the table of extra lengths for each +code (meta_extra_lengths, just below) must be updated to remain in step. */ + +#define META_END 0x80000000u /* End of pattern */ + +#define META_ALT 0x80010000u /* alternation */ +#define META_ATOMIC 0x80020000u /* atomic group */ +#define META_BACKREF 0x80030000u /* Back ref */ +#define META_BACKREF_BYNAME 0x80040000u /* \k'name' */ +#define META_BIGVALUE 0x80050000u /* Next is a literal > META_END */ +#define META_CALLOUT_NUMBER 0x80060000u /* (?C with numerical argument */ +#define META_CALLOUT_STRING 0x80070000u /* (?C with string argument */ +#define META_CAPTURE 0x80080000u /* Capturing parenthesis */ +#define META_CIRCUMFLEX 0x80090000u /* ^ metacharacter */ +#define META_CLASS 0x800a0000u /* start non-empty class */ +#define META_CLASS_EMPTY 0x800b0000u /* empty class */ +#define META_CLASS_EMPTY_NOT 0x800c0000u /* negative empty class */ +#define META_CLASS_END 0x800d0000u /* end of non-empty class */ +#define META_CLASS_NOT 0x800e0000u /* start non-empty negative class */ +#define META_COND_ASSERT 0x800f0000u /* (?(?assertion)... */ +#define META_COND_DEFINE 0x80100000u /* (?(DEFINE)... */ +#define META_COND_NAME 0x80110000u /* (?()... */ +#define META_COND_NUMBER 0x80120000u /* (?(digits)... */ +#define META_COND_RNAME 0x80130000u /* (?(R&name)... */ +#define META_COND_RNUMBER 0x80140000u /* (?(Rdigits)... */ +#define META_COND_VERSION 0x80150000u /* (?(VERSIONx.y)... */ +#define META_DOLLAR 0x80160000u /* $ metacharacter */ +#define META_DOT 0x80170000u /* . metacharacter */ +#define META_ESCAPE 0x80180000u /* \d and friends */ +#define META_KET 0x80190000u /* closing parenthesis */ +#define META_NOCAPTURE 0x801a0000u /* no capture parens */ +#define META_OPTIONS 0x801b0000u /* (?i) and friends */ +#define META_POSIX 0x801c0000u /* POSIX class item */ +#define META_POSIX_NEG 0x801d0000u /* negative POSIX class item */ +#define META_RANGE_ESCAPED 0x801e0000u /* range with at least one escape */ +#define META_RANGE_LITERAL 0x801f0000u /* range defined literally */ +#define META_RECURSE 0x80200000u /* Recursion */ +#define META_RECURSE_BYNAME 0x80210000u /* (?&name) */ + +/* These must be kept together to make it easy to check that an assertion +is present where expected in a conditional group. */ + +#define META_LOOKAHEAD 0x80220000u /* (?= */ +#define META_LOOKAHEADNOT 0x80230000u /* (?! */ +#define META_LOOKBEHIND 0x80240000u /* (?<= */ +#define META_LOOKBEHINDNOT 0x80250000u /* (?= 10 */ + 1+SIZEOFFSET, /* META_BACKREF_BYNAME */ + 1, /* META_BIGVALUE */ + 3, /* META_CALLOUT_NUMBER */ + 3+SIZEOFFSET, /* META_CALLOUT_STRING */ + 0, /* META_CAPTURE */ + 0, /* META_CIRCUMFLEX */ + 0, /* META_CLASS */ + 0, /* META_CLASS_EMPTY */ + 0, /* META_CLASS_EMPTY_NOT */ + 0, /* META_CLASS_END */ + 0, /* META_CLASS_NOT */ + 0, /* META_COND_ASSERT */ + SIZEOFFSET, /* META_COND_DEFINE */ + 1+SIZEOFFSET, /* META_COND_NAME */ + 1+SIZEOFFSET, /* META_COND_NUMBER */ + 1+SIZEOFFSET, /* META_COND_RNAME */ + 1+SIZEOFFSET, /* META_COND_RNUMBER */ + 3, /* META_COND_VERSION */ + 0, /* META_DOLLAR */ + 0, /* META_DOT */ + 0, /* META_ESCAPE - more for ESC_P, ESC_p, ESC_g, ESC_k */ + 0, /* META_KET */ + 0, /* META_NOCAPTURE */ + 1, /* META_OPTIONS */ + 1, /* META_POSIX */ + 1, /* META_POSIX_NEG */ + 0, /* META_RANGE_ESCAPED */ + 0, /* META_RANGE_LITERAL */ + SIZEOFFSET, /* META_RECURSE */ + 1+SIZEOFFSET, /* META_RECURSE_BYNAME */ + 0, /* META_LOOKAHEAD */ + 0, /* META_LOOKAHEADNOT */ + SIZEOFFSET, /* META_LOOKBEHIND */ + SIZEOFFSET, /* META_LOOKBEHINDNOT */ + 1, /* META_MARK - plus the string length */ + 0, /* META_ACCEPT */ + 0, /* META_FAIL */ + 0, /* META_COMMIT */ + 1, /* META_COMMIT_ARG - plus the string length */ + 0, /* META_PRUNE */ + 1, /* META_PRUNE_ARG - plus the string length */ + 0, /* META_SKIP */ + 1, /* META_SKIP_ARG - plus the string length */ + 0, /* META_THEN */ + 1, /* META_THEN_ARG - plus the string length */ + 0, /* META_ASTERISK */ + 0, /* META_ASTERISK_PLUS */ + 0, /* META_ASTERISK_QUERY */ + 0, /* META_PLUS */ + 0, /* META_PLUS_PLUS */ + 0, /* META_PLUS_QUERY */ + 0, /* META_QUERY */ + 0, /* META_QUERY_PLUS */ + 0, /* META_QUERY_QUERY */ + 2, /* META_MINMAX */ + 2, /* META_MINMAX_PLUS */ + 2 /* META_MINMAX_QUERY */ +}; + +/* Types for skipping parts of a parsed pattern. */ + +enum { PSKIP_ALT, PSKIP_CLASS, PSKIP_KET }; + +/* Macro for setting individual bits in class bitmaps. It took some +experimenting to figure out how to stop gcc 5.3.0 from warning with +-Wconversion. This version gets a warning: + + #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1 << ((b)&7)) + +Let's hope the apparently less efficient version isn't actually so bad if the +compiler is clever with identical subexpressions. */ + +#define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1 << ((b)&7))) + +/* Private flags added to firstcu and reqcu. */ + +#define REQ_CASELESS (1 << 0) /* Indicates caselessness */ +#define REQ_VARY (1 << 1) /* reqcu followed non-literal item */ +/* Negative values for the firstcu and reqcu flags */ +#define REQ_UNSET (-2) /* Not yet found anything */ +#define REQ_NONE (-1) /* Found not fixed char */ + +/* These flags are used in the groupinfo vector. */ + +#define GI_SET_FIXED_LENGTH 0x80000000u +#define GI_NOT_FIXED_LENGTH 0x40000000u +#define GI_FIXED_LENGTH_MASK 0x0000ffffu + +/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC +and is fast (a good compiler can turn it into a subtraction and unsigned +comparison). */ + +#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) + +/* Table to identify hex digits. The tables in chartables are dependent on the +locale, and may mark arbitrary characters as digits. We want to recognize only +0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It +costs 256 bytes, but it is a lot faster than doing character value tests (at +least in some simple cases I timed), and in some applications one wants PCRE2 +to compile efficiently as well as match efficiently. The value in the table is +the binary hex digit value, or 0xff for non-hex digits. */ + +/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in +UTF-8 mode. */ + +#ifndef EBCDIC +static const uint8_t xdigitab[] = + { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */ + 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */ + +#else + +/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ + +static const uint8_t xdigitab[] = + { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */ + 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */ +#endif /* EBCDIC */ + + +/* Table for handling alphanumeric escaped characters. Positive returns are +simple data values; negative values are for special things like \d and so on. +Zero means further processing is needed (for things like \x), or the escape is +invalid. */ + +/* This is the "normal" table for ASCII systems or for EBCDIC systems running +in UTF-8 mode. It runs from '0' to 'z'. */ + +#ifndef EBCDIC +#define ESCAPES_FIRST CHAR_0 +#define ESCAPES_LAST CHAR_z +#define UPPER_CASE(c) (c-32) + +static const short int escapes[] = { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + CHAR_COLON, CHAR_SEMICOLON, + CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, + CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, + CHAR_COMMERCIAL_AT, -ESC_A, + -ESC_B, -ESC_C, + -ESC_D, -ESC_E, + 0, -ESC_G, + -ESC_H, 0, + 0, -ESC_K, + 0, 0, + -ESC_N, 0, + -ESC_P, -ESC_Q, + -ESC_R, -ESC_S, + 0, 0, + -ESC_V, -ESC_W, + -ESC_X, 0, + -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, + CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, + CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, + CHAR_GRAVE_ACCENT, CHAR_BEL, + -ESC_b, 0, + -ESC_d, CHAR_ESC, + CHAR_FF, 0, + -ESC_h, 0, + 0, -ESC_k, + 0, 0, + CHAR_LF, 0, + -ESC_p, 0, + CHAR_CR, -ESC_s, + CHAR_HT, 0, + -ESC_v, -ESC_w, + 0, 0, + -ESC_z +}; + +#else + +/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. +It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code +is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a +because it is defined as 'a', which of course picks up the ASCII value. */ + +#if 'a' == 0x81 /* Check for a real EBCDIC environment */ +#define ESCAPES_FIRST CHAR_a +#define ESCAPES_LAST CHAR_9 +#define UPPER_CASE(c) (c+64) +#else /* Testing in an ASCII environment */ +#define ESCAPES_FIRST ((unsigned char)'\x81') /* EBCDIC 'a' */ +#define ESCAPES_LAST ((unsigned char)'\xf9') /* EBCDIC '9' */ +#define UPPER_CASE(c) (c-32) +#endif + +static const short int escapes[] = { +/* 80 */ CHAR_BEL, -ESC_b, 0, -ESC_d, CHAR_ESC, CHAR_FF, 0, +/* 88 */ -ESC_h, 0, 0, '{', 0, 0, 0, 0, +/* 90 */ 0, 0, -ESC_k, 0, 0, CHAR_LF, 0, -ESC_p, +/* 98 */ 0, CHAR_CR, 0, '}', 0, 0, 0, 0, +/* A0 */ 0, '~', -ESC_s, CHAR_HT, 0, -ESC_v, -ESC_w, 0, +/* A8 */ 0, -ESC_z, 0, 0, 0, '[', 0, 0, +/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', +/* C0 */ '{', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, +/* C8 */ -ESC_H, 0, 0, 0, 0, 0, 0, 0, +/* D0 */ '}', 0, -ESC_K, 0, 0, -ESC_N, 0, -ESC_P, +/* D8 */ -ESC_Q, -ESC_R, 0, 0, 0, 0, 0, 0, +/* E0 */ '\\', 0, -ESC_S, 0, 0, -ESC_V, -ESC_W, -ESC_X, +/* E8 */ 0, -ESC_Z, 0, 0, 0, 0, 0, 0, +/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* F8 */ 0, 0 +}; + +/* We also need a table of characters that may follow \c in an EBCDIC +environment for characters 0-31. */ + +static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; + +#endif /* EBCDIC */ + + +/* Table of special "verbs" like (*PRUNE). This is a short table, so it is +searched linearly. Put all the names into a single string, in order to reduce +the number of relocations when a shared library is dynamically linked. The +string is built from string macros so that it works in UTF-8 mode on EBCDIC +platforms. */ + +typedef struct verbitem { + unsigned int len; /* Length of verb name */ + uint32_t meta; /* Base META_ code */ + int has_arg; /* Argument requirement */ +} verbitem; + +static const char verbnames[] = + "\0" /* Empty name is a shorthand for MARK */ + STRING_MARK0 + STRING_ACCEPT0 + STRING_F0 + STRING_FAIL0 + STRING_COMMIT0 + STRING_PRUNE0 + STRING_SKIP0 + STRING_THEN; + +static const verbitem verbs[] = { + { 0, META_MARK, +1 }, /* > 0 => must have an argument */ + { 4, META_MARK, +1 }, + { 6, META_ACCEPT, -1 }, /* < 0 => Optional argument, convert to pre-MARK */ + { 1, META_FAIL, -1 }, + { 4, META_FAIL, -1 }, + { 6, META_COMMIT, 0 }, + { 5, META_PRUNE, 0 }, /* Optional argument; bump META code if found */ + { 4, META_SKIP, 0 }, + { 4, META_THEN, 0 } +}; + +static const int verbcount = sizeof(verbs)/sizeof(verbitem); + +/* Verb opcodes, indexed by their META code offset from META_MARK. */ + +static const uint32_t verbops[] = { + OP_MARK, OP_ACCEPT, OP_FAIL, OP_COMMIT, OP_COMMIT_ARG, OP_PRUNE, + OP_PRUNE_ARG, OP_SKIP, OP_SKIP_ARG, OP_THEN, OP_THEN_ARG }; + +/* Offsets from OP_STAR for case-independent and negative repeat opcodes. */ + +static uint32_t chartypeoffset[] = { + OP_STAR - OP_STAR, OP_STARI - OP_STAR, + OP_NOTSTAR - OP_STAR, OP_NOTSTARI - OP_STAR }; + +/* Tables of names of POSIX character classes and their lengths. The names are +now all in a single string, to reduce the number of relocations when a shared +library is dynamically loaded. The list of lengths is terminated by a zero +length entry. The first three must be alpha, lower, upper, as this is assumed +for handling case independence. The indices for graph, print, and punct are +needed, so identify them. */ + +static const char posix_names[] = + STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 + STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 + STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 + STRING_word0 STRING_xdigit; + +static const uint8_t posix_name_lengths[] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; + +#define PC_GRAPH 8 +#define PC_PRINT 9 +#define PC_PUNCT 10 + +/* Table of class bit maps for each POSIX class. Each class is formed from a +base map, with an optional addition or removal of another map. Then, for some +classes, there is some additional tweaking: for [:blank:] the vertical space +characters are removed, and for [:alpha:] and [:alnum:] the underscore +character is removed. The triples in the table consist of the base map offset, +second map offset or -1 if no second map, and a non-negative value for map +addition or a negative value for map subtraction (if there are two maps). The +absolute value of the third field has these meanings: 0 => no tweaking, 1 => +remove vertical space characters, 2 => remove underscore. */ + +static const int posix_class_maps[] = { + cbit_word, cbit_digit, -2, /* alpha */ + cbit_lower, -1, 0, /* lower */ + cbit_upper, -1, 0, /* upper */ + cbit_word, -1, 2, /* alnum - word without underscore */ + cbit_print, cbit_cntrl, 0, /* ascii */ + cbit_space, -1, 1, /* blank - a GNU extension */ + cbit_cntrl, -1, 0, /* cntrl */ + cbit_digit, -1, 0, /* digit */ + cbit_graph, -1, 0, /* graph */ + cbit_print, -1, 0, /* print */ + cbit_punct, -1, 0, /* punct */ + cbit_space, -1, 0, /* space */ + cbit_word, -1, 0, /* word - a Perl extension */ + cbit_xdigit,-1, 0 /* xdigit */ +}; + +#ifdef SUPPORT_UNICODE + +/* The POSIX class Unicode property substitutes that are used in UCP mode must +be in the order of the POSIX class names, defined above. */ + +static int posix_substitutes[] = { + PT_GC, ucp_L, /* alpha */ + PT_PC, ucp_Ll, /* lower */ + PT_PC, ucp_Lu, /* upper */ + PT_ALNUM, 0, /* alnum */ + -1, 0, /* ascii, treat as non-UCP */ + -1, 1, /* blank, treat as \h */ + PT_PC, ucp_Cc, /* cntrl */ + PT_PC, ucp_Nd, /* digit */ + PT_PXGRAPH, 0, /* graph */ + PT_PXPRINT, 0, /* print */ + PT_PXPUNCT, 0, /* punct */ + PT_PXSPACE, 0, /* space */ /* Xps is POSIX space, but from 8.34 */ + PT_WORD, 0, /* word */ /* Perl and POSIX space are the same */ + -1, 0 /* xdigit, treat as non-UCP */ +}; +#define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t))) +#endif /* SUPPORT_UNICODE */ + +/* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset +are allowed. */ + +#define PUBLIC_LITERAL_COMPILE_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \ + PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_NO_START_OPTIMIZE| \ + PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF) + +#define PUBLIC_COMPILE_OPTIONS \ + (PUBLIC_LITERAL_COMPILE_OPTIONS| \ + PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ + PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \ + PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MATCH_UNSET_BACKREF| \ + PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \ + PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \ + PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_UCP|PCRE2_UNGREEDY) + +#define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \ + (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD) + +#define PUBLIC_COMPILE_EXTRA_OPTIONS \ + (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \ + PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) + +/* Compile time error code numbers. They are given names so that they can more +easily be tracked. When a new number is added, the tables called eint1 and +eint2 in pcre2posix.c may need to be updated, and a new error text must be +added to compile_error_texts in pcre2_error.c. */ + +enum { ERR0 = COMPILE_ERROR_BASE, + ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10, + ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20, + ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30, + ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40, + ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50, + ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60, + ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70, + ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80, + ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90, + ERR91, ERR92, ERR93, ERR94 }; + +/* This is a table of start-of-pattern options such as (*UTF) and settings such +as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward +compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is +generic and always supported. */ + +enum { PSO_OPT, /* Value is an option bit */ + PSO_FLG, /* Value is a flag bit */ + PSO_NL, /* Value is a newline type */ + PSO_BSR, /* Value is a \R type */ + PSO_LIMH, /* Read integer value for heap limit */ + PSO_LIMM, /* Read integer value for match limit */ + PSO_LIMD }; /* Read integer value for depth limit */ + +typedef struct pso { + const uint8_t *name; + uint16_t length; + uint16_t type; + uint32_t value; +} pso; + +/* NB: STRING_UTFn_RIGHTPAR contains the length as well */ + +static pso pso_list[] = { + { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF }, + { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF }, + { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP }, + { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET }, + { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET }, + { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS }, + { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR }, + { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT }, + { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE }, + { (uint8_t *)STRING_LIMIT_HEAP_EQ, 11, PSO_LIMH, 0 }, + { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 }, + { (uint8_t *)STRING_LIMIT_DEPTH_EQ, 12, PSO_LIMD, 0 }, + { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMD, 0 }, + { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR }, + { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF }, + { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF }, + { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY }, + { (uint8_t *)STRING_NUL_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_NUL }, + { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF }, + { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF }, + { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE } +}; + +/* This table is used when converting repeating opcodes into possessified +versions as a result of an explicit possessive quantifier such as ++. A zero +value means there is no possessified version - in those cases the item in +question must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT +because all relevant opcodes are less than that. */ + +static const uint8_t opcode_possessify[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ + + 0, /* NOTI */ + OP_POSSTAR, 0, /* STAR, MINSTAR */ + OP_POSPLUS, 0, /* PLUS, MINPLUS */ + OP_POSQUERY, 0, /* QUERY, MINQUERY */ + OP_POSUPTO, 0, /* UPTO, MINUPTO */ + 0, /* EXACT */ + 0, 0, 0, 0, /* POS{STAR,PLUS,QUERY,UPTO} */ + + OP_POSSTARI, 0, /* STARI, MINSTARI */ + OP_POSPLUSI, 0, /* PLUSI, MINPLUSI */ + OP_POSQUERYI, 0, /* QUERYI, MINQUERYI */ + OP_POSUPTOI, 0, /* UPTOI, MINUPTOI */ + 0, /* EXACTI */ + 0, 0, 0, 0, /* POS{STARI,PLUSI,QUERYI,UPTOI} */ + + OP_NOTPOSSTAR, 0, /* NOTSTAR, NOTMINSTAR */ + OP_NOTPOSPLUS, 0, /* NOTPLUS, NOTMINPLUS */ + OP_NOTPOSQUERY, 0, /* NOTQUERY, NOTMINQUERY */ + OP_NOTPOSUPTO, 0, /* NOTUPTO, NOTMINUPTO */ + 0, /* NOTEXACT */ + 0, 0, 0, 0, /* NOTPOS{STAR,PLUS,QUERY,UPTO} */ + + OP_NOTPOSSTARI, 0, /* NOTSTARI, NOTMINSTARI */ + OP_NOTPOSPLUSI, 0, /* NOTPLUSI, NOTMINPLUSI */ + OP_NOTPOSQUERYI, 0, /* NOTQUERYI, NOTMINQUERYI */ + OP_NOTPOSUPTOI, 0, /* NOTUPTOI, NOTMINUPTOI */ + 0, /* NOTEXACTI */ + 0, 0, 0, 0, /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */ + + OP_TYPEPOSSTAR, 0, /* TYPESTAR, TYPEMINSTAR */ + OP_TYPEPOSPLUS, 0, /* TYPEPLUS, TYPEMINPLUS */ + OP_TYPEPOSQUERY, 0, /* TYPEQUERY, TYPEMINQUERY */ + OP_TYPEPOSUPTO, 0, /* TYPEUPTO, TYPEMINUPTO */ + 0, /* TYPEEXACT */ + 0, 0, 0, 0, /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */ + + OP_CRPOSSTAR, 0, /* CRSTAR, CRMINSTAR */ + OP_CRPOSPLUS, 0, /* CRPLUS, CRMINPLUS */ + OP_CRPOSQUERY, 0, /* CRQUERY, CRMINQUERY */ + OP_CRPOSRANGE, 0, /* CRRANGE, CRMINRANGE */ + 0, 0, 0, 0, /* CRPOS{STAR,PLUS,QUERY,RANGE} */ + + 0, 0, 0, /* CLASS, NCLASS, XCLASS */ + 0, 0, /* REF, REFI */ + 0, 0, /* DNREF, DNREFI */ + 0, 0 /* RECURSE, CALLOUT */ +}; + + +#ifdef DEBUG_SHOW_PARSED +/************************************************* +* Show the parsed pattern for debugging * +*************************************************/ + +/* For debugging the pre-scan, this code, which outputs the parsed data vector, +can be enabled. */ + +static void show_parsed(compile_block *cb) +{ +uint32_t *pptr = cb->parsed_pattern; + +for (;;) + { + int max, min; + PCRE2_SIZE offset; + uint32_t i; + uint32_t length; + uint32_t meta_arg = META_DATA(*pptr); + + fprintf(stderr, "+++ %02d %.8x ", (int)(pptr - cb->parsed_pattern), *pptr); + + if (*pptr < META_END) + { + if (*pptr > 32 && *pptr < 128) fprintf(stderr, "%c", *pptr); + pptr++; + } + + else switch (META_CODE(*pptr++)) + { + default: + fprintf(stderr, "**** OOPS - unknown META value - giving up ****\n"); + return; + + case META_END: + fprintf(stderr, "META_END\n"); + return; + + case META_CAPTURE: + fprintf(stderr, "META_CAPTURE %d", meta_arg); + break; + + case META_RECURSE: + GETOFFSET(offset, pptr); + fprintf(stderr, "META_RECURSE %d %zd", meta_arg, offset); + break; + + case META_BACKREF: + if (meta_arg < 10) + offset = cb->small_ref_offset[meta_arg]; + else + GETOFFSET(offset, pptr); + fprintf(stderr, "META_BACKREF %d %zd", meta_arg, offset); + break; + + case META_ESCAPE: + if (meta_arg == ESC_P || meta_arg == ESC_p) + { + uint32_t ptype = *pptr >> 16; + uint32_t pvalue = *pptr++ & 0xffff; + fprintf(stderr, "META \\%c %d %d", (meta_arg == ESC_P)? 'P':'p', + ptype, pvalue); + } + else + { + uint32_t cc; + /* There's just one escape we might have here that isn't negated in the + escapes table. */ + if (meta_arg == ESC_g) cc = CHAR_g; + else for (cc = ESCAPES_FIRST; cc <= ESCAPES_LAST; cc++) + { + if (meta_arg == (uint32_t)(-escapes[cc - ESCAPES_FIRST])) break; + } + if (cc > ESCAPES_LAST) cc = CHAR_QUESTION_MARK; + fprintf(stderr, "META \\%c", cc); + } + break; + + case META_MINMAX: + min = *pptr++; + max = *pptr++; + if (max != REPEAT_UNLIMITED) + fprintf(stderr, "META {%d,%d}", min, max); + else + fprintf(stderr, "META {%d,}", min); + break; + + case META_MINMAX_QUERY: + min = *pptr++; + max = *pptr++; + if (max != REPEAT_UNLIMITED) + fprintf(stderr, "META {%d,%d}?", min, max); + else + fprintf(stderr, "META {%d,}?", min); + break; + + case META_MINMAX_PLUS: + min = *pptr++; + max = *pptr++; + if (max != REPEAT_UNLIMITED) + fprintf(stderr, "META {%d,%d}+", min, max); + else + fprintf(stderr, "META {%d,}+", min); + break; + + case META_BIGVALUE: fprintf(stderr, "META_BIGVALUE %.8x", *pptr++); break; + case META_CIRCUMFLEX: fprintf(stderr, "META_CIRCUMFLEX"); break; + case META_COND_ASSERT: fprintf(stderr, "META_COND_ASSERT"); break; + case META_DOLLAR: fprintf(stderr, "META_DOLLAR"); break; + case META_DOT: fprintf(stderr, "META_DOT"); break; + case META_ASTERISK: fprintf(stderr, "META *"); break; + case META_ASTERISK_QUERY: fprintf(stderr, "META *?"); break; + case META_ASTERISK_PLUS: fprintf(stderr, "META *+"); break; + case META_PLUS: fprintf(stderr, "META +"); break; + case META_PLUS_QUERY: fprintf(stderr, "META +?"); break; + case META_PLUS_PLUS: fprintf(stderr, "META ++"); break; + case META_QUERY: fprintf(stderr, "META ?"); break; + case META_QUERY_QUERY: fprintf(stderr, "META ??"); break; + case META_QUERY_PLUS: fprintf(stderr, "META ?+"); break; + + case META_ATOMIC: fprintf(stderr, "META (?>"); break; + case META_NOCAPTURE: fprintf(stderr, "META (?:"); break; + case META_LOOKAHEAD: fprintf(stderr, "META (?="); break; + case META_LOOKAHEADNOT: fprintf(stderr, "META (?!"); break; + case META_KET: fprintf(stderr, "META )"); break; + case META_ALT: fprintf(stderr, "META | %d", meta_arg); break; + + case META_CLASS: fprintf(stderr, "META ["); break; + case META_CLASS_NOT: fprintf(stderr, "META [^"); break; + case META_CLASS_END: fprintf(stderr, "META ]"); break; + case META_CLASS_EMPTY: fprintf(stderr, "META []"); break; + case META_CLASS_EMPTY_NOT: fprintf(stderr, "META [^]"); break; + + case META_RANGE_LITERAL: fprintf(stderr, "META - (literal)"); break; + case META_RANGE_ESCAPED: fprintf(stderr, "META - (escaped)"); break; + + case META_POSIX: fprintf(stderr, "META_POSIX %d", *pptr++); break; + case META_POSIX_NEG: fprintf(stderr, "META_POSIX_NEG %d", *pptr++); break; + + case META_ACCEPT: fprintf(stderr, "META (*ACCEPT)"); break; + case META_FAIL: fprintf(stderr, "META (*FAIL)"); break; + case META_COMMIT: fprintf(stderr, "META (*COMMIT)"); break; + case META_PRUNE: fprintf(stderr, "META (*PRUNE)"); break; + case META_SKIP: fprintf(stderr, "META (*SKIP)"); break; + case META_THEN: fprintf(stderr, "META (*THEN)"); break; + + case META_OPTIONS: fprintf(stderr, "META_OPTIONS 0x%02x", *pptr++); break; + + case META_LOOKBEHIND: + fprintf(stderr, "META (?<= %d offset=", meta_arg); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + case META_LOOKBEHINDNOT: + fprintf(stderr, "META (?="); + fprintf(stderr, "%d.", *pptr++); + fprintf(stderr, "%d)", *pptr++); + break; + + case META_COND_NAME: + fprintf(stderr, "META (?() length=%d offset=", *pptr++); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + case META_COND_RNAME: + fprintf(stderr, "META (?(R&name) length=%d offset=", *pptr++); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + /* This is kept as a name, because it might be. */ + + case META_COND_RNUMBER: + fprintf(stderr, "META (?(Rnumber) length=%d offset=", *pptr++); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + case META_MARK: + fprintf(stderr, "META (*MARK:"); + goto SHOWARG; + + case META_COMMIT_ARG: + fprintf(stderr, "META (*COMMIT:"); + goto SHOWARG; + + case META_PRUNE_ARG: + fprintf(stderr, "META (*PRUNE:"); + goto SHOWARG; + + case META_SKIP_ARG: + fprintf(stderr, "META (*SKIP:"); + goto SHOWARG; + + case META_THEN_ARG: + fprintf(stderr, "META (*THEN:"); + SHOWARG: + length = *pptr++; + for (i = 0; i < length; i++) + { + uint32_t cc = *pptr++; + if (cc > 32 && cc < 128) fprintf(stderr, "%c", cc); + else fprintf(stderr, "\\x{%x}", cc); + } + fprintf(stderr, ") length=%u", length); + break; + } + fprintf(stderr, "\n"); + } +return; +} +#endif /* DEBUG_SHOW_PARSED */ + + + +/************************************************* +* Copy compiled code * +*************************************************/ + +/* Compiled JIT code cannot be copied, so the new compiled block has no +associated JIT data. */ + +PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION +pcre2_code_copy(const pcre2_code *code) +{ +PCRE2_SIZE* ref_count; +pcre2_code *newcode; + +if (code == NULL) return NULL; +newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); +if (newcode == NULL) return NULL; +memcpy(newcode, code, code->blocksize); +newcode->executable_jit = NULL; + +/* If the code is one that has been deserialized, increment the reference count +in the decoded tables. */ + +if ((code->flags & PCRE2_DEREF_TABLES) != 0) + { + ref_count = (PCRE2_SIZE *)(code->tables + tables_length); + (*ref_count)++; + } + +return newcode; +} + + + +/************************************************* +* Copy compiled code and character tables * +*************************************************/ + +/* Compiled JIT code cannot be copied, so the new compiled block has no +associated JIT data. This version of code_copy also makes a separate copy of +the character tables. */ + +PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION +pcre2_code_copy_with_tables(const pcre2_code *code) +{ +PCRE2_SIZE* ref_count; +pcre2_code *newcode; +uint8_t *newtables; + +if (code == NULL) return NULL; +newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); +if (newcode == NULL) return NULL; +memcpy(newcode, code, code->blocksize); +newcode->executable_jit = NULL; + +newtables = code->memctl.malloc(tables_length + sizeof(PCRE2_SIZE), + code->memctl.memory_data); +if (newtables == NULL) + { + code->memctl.free((void *)newcode, code->memctl.memory_data); + return NULL; + } +memcpy(newtables, code->tables, tables_length); +ref_count = (PCRE2_SIZE *)(newtables + tables_length); +*ref_count = 1; + +newcode->tables = newtables; +newcode->flags |= PCRE2_DEREF_TABLES; +return newcode; +} + + + +/************************************************* +* Free compiled code * +*************************************************/ + +PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION +pcre2_code_free(pcre2_code *code) +{ +PCRE2_SIZE* ref_count; + +if (code != NULL) + { + if (code->executable_jit != NULL) + PRIV(jit_free)(code->executable_jit, &code->memctl); + + if ((code->flags & PCRE2_DEREF_TABLES) != 0) + { + /* Decoded tables belong to the codes after deserialization, and they must + be freed when there are no more reference to them. The *ref_count should + always be > 0. */ + + ref_count = (PCRE2_SIZE *)(code->tables + tables_length); + if (*ref_count > 0) + { + (*ref_count)--; + if (*ref_count == 0) + code->memctl.free((void *)code->tables, code->memctl.memory_data); + } + } + + code->memctl.free(code, code->memctl.memory_data); + } +} + + + +/************************************************* +* Read a number, possibly signed * +*************************************************/ + +/* This function is used to read numbers in the pattern. The initial pointer +must be the sign or first digit of the number. When relative values (introduced +by + or -) are allowed, they are relative group numbers, and the result must be +greater than zero. + +Arguments: + ptrptr points to the character pointer variable + ptrend points to the end of the input string + allow_sign if < 0, sign not allowed; if >= 0, sign is relative to this + max_value the largest number allowed + max_error the error to give for an over-large number + intptr where to put the result + errcodeptr where to put an error code + +Returns: TRUE - a number was read + FALSE - errorcode == 0 => no number was found + errorcode != 0 => an error occurred +*/ + +static BOOL +read_number(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, int32_t allow_sign, + uint32_t max_value, uint32_t max_error, int *intptr, int *errorcodeptr) +{ +int sign = 0; +uint32_t n = 0; +PCRE2_SPTR ptr = *ptrptr; +BOOL yield = FALSE; + +*errorcodeptr = 0; + +if (allow_sign >= 0 && ptr < ptrend) + { + if (*ptr == CHAR_PLUS) + { + sign = +1; + max_value -= allow_sign; + ptr++; + } + else if (*ptr == CHAR_MINUS) + { + sign = -1; + ptr++; + } + } + +if (ptr >= ptrend || !IS_DIGIT(*ptr)) return FALSE; +while (ptr < ptrend && IS_DIGIT(*ptr)) + { + n = n * 10 + *ptr++ - CHAR_0; + if (n > max_value) + { + *errorcodeptr = max_error; + goto EXIT; + } + } + +if (allow_sign >= 0 && sign != 0) + { + if (n == 0) + { + *errorcodeptr = ERR26; /* +0 and -0 are not allowed */ + goto EXIT; + } + + if (sign > 0) n += allow_sign; + else if ((int)n > allow_sign) + { + *errorcodeptr = ERR15; /* Non-existent subpattern */ + goto EXIT; + } + else n = allow_sign + 1 - n; + } + +yield = TRUE; + +EXIT: +*intptr = n; +*ptrptr = ptr; +return yield; +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values if non-NULL pointers +are supplied. Repeat counts must be less than 65536 (MAX_REPEAT_COUNT); a +larger value is used for "unlimited". We have to use signed arguments for +read_number() because it is capable of returning a signed value. + +Arguments: + ptrptr points to pointer to character after'{' + ptrend pointer to end of input + minp if not NULL, pointer to int for min + maxp if not NULL, pointer to int for max (-1 if no max) + returned as -1 if no max + errorcodeptr points to error code variable + +Returns: FALSE if not a repeat quantifier, errorcode set zero + FALSE on error, with errorcode set non-zero + TRUE on success, with pointer updated to point after '}' +*/ + +static BOOL +read_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp, + uint32_t *maxp, int *errorcodeptr) +{ +PCRE2_SPTR p = *ptrptr; +BOOL yield = FALSE; +int32_t min = 0; +int32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */ + +/* NB read_number() initializes the error code to zero. The only error is for a +number that is too big. */ + +if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr)) + goto EXIT; + +if (p >= ptrend) goto EXIT; + +if (*p == CHAR_RIGHT_CURLY_BRACKET) + { + p++; + max = min; + } + +else + { + if (*p++ != CHAR_COMMA || p >= ptrend) goto EXIT; + if (*p != CHAR_RIGHT_CURLY_BRACKET) + { + if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max, + errorcodeptr) || p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) + goto EXIT; + if (max < min) + { + *errorcodeptr = ERR4; + goto EXIT; + } + } + p++; + } + +yield = TRUE; +if (minp != NULL) *minp = (uint32_t)min; +if (maxp != NULL) *maxp = (uint32_t)max; + +/* Update the pattern pointer on success, or after an error, but not when +the result is "not a repeat quantifier". */ + +EXIT: +if (yield || *errorcodeptr != 0) *ptrptr = p; +return yield; + + + +} + + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \d, or 0 for a data character, which +is placed in chptr. A backreference to group n is returned as negative n. On +entry, ptr is pointing at the character after \. On exit, it points after the +final code unit of the escape sequence. + +This function is also called from pcre2_substitute() to handle escape sequences +in replacement strings. In this case, the cb argument is NULL, and in the case +of escapes that have further processing, only sequences that define a data +character are recognised. The isclass argument is not relevant; the options +argument is the final value of the compiled pattern's options. + +Arguments: + ptrptr points to the input position pointer + ptrend points to the end of the input + chptr points to a returned data character + errorcodeptr points to the errorcode variable (containing zero) + options the current options bits + isclass TRUE if inside a character class + cb compile data block + +Returns: zero => a data character + positive => a special escape sequence + negative => a numerical back reference + on error, errorcodeptr is set non-zero +*/ + +int +PRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr, + int *errorcodeptr, uint32_t options, BOOL isclass, compile_block *cb) +{ +BOOL utf = (options & PCRE2_UTF) != 0; +PCRE2_SPTR ptr = *ptrptr; +uint32_t c, cc; +int escape = 0; +int i; + +/* If backslash is at the end of the string, it's an error. */ + +if (ptr >= ptrend) + { + *errorcodeptr = ERR1; + return 0; + } + +GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ +*errorcodeptr = 0; /* Be optimistic */ + +/* Non-alphanumerics are literals, so we just leave the value in c. An initial +value test saves a memory lookup for code points outside the alphanumeric +range. Otherwise, do a table lookup. A non-zero result is something that can be +returned immediately. Otherwise further processing is required. */ + +if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ + +else if ((i = escapes[c - ESCAPES_FIRST]) != 0) + { + if (i > 0) c = (uint32_t)i; else /* Positive is a data character */ + { + escape = -i; /* Else return a special escape */ + if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X)) + cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */ + + /* Perl supports \N{name} for character names and \N{U+dddd} for numerical + Unicode code points, as well as plain \N for "not newline". PCRE does not + support \N{name}. However, it does support quantification such as \N{2,3}, + so if \N{ is not followed by U+dddd we check for a quantifier. */ + + if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) + { + PCRE2_SPTR p = ptr + 1; + + /* \N{U+ can be handled by the \x{ code. However, this construction is + not valid in EBCDIC environments because it specifies a Unicode + character, not a codepoint in the local code. For example \N{U+0041} + must be "A" in all environments. Also, in Perl, \N{U+ forces Unicode + casing semantics for the entire pattern, so allow it only in UTF (i.e. + Unicode) mode. */ + + if (ptrend - p > 1 && *p == CHAR_U && p[1] == CHAR_PLUS) + { +#ifdef EBCDIC + *errorcodeptr = ERR93; +#else + if (utf) + { + ptr = p + 1; + escape = 0; /* Not a fancy escape after all */ + goto COME_FROM_NU; + } + else *errorcodeptr = ERR93; +#endif + } + + /* Give an error if what follows is not a quantifier, but don't override + an error set by the quantifier reader (e.g. number overflow). */ + + else + { + if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) && + *errorcodeptr == 0) + *errorcodeptr = ERR37; + } + } + } + } + +/* Escapes that need further processing, including those that are unknown. +When called from pcre2_substitute(), only \c, \o, and \x are recognized (and \u +when BSUX is set). */ + +else + { + PCRE2_SPTR oldptr; + BOOL overflow; + int s; + + /* Filter calls from pcre2_substitute(). */ + + if (cb == NULL && c != CHAR_c && c != CHAR_o && c != CHAR_x && + (c != CHAR_u || (options & PCRE2_ALT_BSUX) != 0)) + { + *errorcodeptr = ERR3; + return 0; + } + + switch (c) + { + /* A number of Perl escapes are not handled by PCRE. We give an explicit + error. */ + + case CHAR_F: + case CHAR_l: + case CHAR_L: + *errorcodeptr = ERR37; + break; + + /* \u is unrecognized when PCRE2_ALT_BSUX is not set. When it is treated + specially, \u must be followed by four hex digits. Otherwise it is a + lowercase u letter. */ + + case CHAR_u: + if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; else + { + uint32_t xc; + if (ptrend - ptr < 4) break; /* Less than 4 chars */ + if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */ + if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + cc = (cc << 4) | xc; + if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ + cc = (cc << 4) | xc; + if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */ + c = (cc << 4) | xc; + ptr += 4; + if (utf) + { + if (c > 0x10ffffU) *errorcodeptr = ERR77; + else + if (c >= 0xd800 && c <= 0xdfff && + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0) + *errorcodeptr = ERR73; + } + else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77; + } + break; + + /* \U is unrecognized unless PCRE2_ALT_BSUX is set, in which case it is an + upper case letter. */ + + case CHAR_U: + if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; + break; + + /* In a character class, \g is just a literal "g". Outside a character + class, \g must be followed by one of a number of specific things: + + (1) A number, either plain or braced. If positive, it is an absolute + backreference. If negative, it is a relative backreference. This is a Perl + 5.10 feature. + + (2) Perl 5.10 also supports \g{name} as a reference to a named group. This + is part of Perl's movement towards a unified syntax for back references. As + this is synonymous with \k{name}, we fudge it up by pretending it really + was \k{name}. + + (3) For Oniguruma compatibility we also support \g followed by a name or a + number either in angle brackets or in single quotes. However, these are + (possibly recursive) subroutine calls, _not_ backreferences. We return + the ESC_g code. + + Summary: Return a negative number for a numerical back reference, ESC_k for + a named back reference, and ESC_g for a named or numbered subroutine call. + */ + + case CHAR_g: + if (isclass) break; + + if (ptr >= ptrend) + { + *errorcodeptr = ERR57; + break; + } + + if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE) + { + escape = ESC_g; + break; + } + + /* If there is a brace delimiter, try to read a numerical reference. If + there isn't one, assume we have a name and treat it as \k. */ + + if (*ptr == CHAR_LEFT_CURLY_BRACKET) + { + PCRE2_SPTR p = ptr + 1; + if (!read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, + errorcodeptr)) + { + if (*errorcodeptr == 0) escape = ESC_k; /* No number found */ + break; + } + if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) + { + *errorcodeptr = ERR57; + break; + } + ptr = p + 1; + } + + /* Read an undelimited number */ + + else + { + if (!read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, + errorcodeptr)) + { + if (*errorcodeptr == 0) *errorcodeptr = ERR57; /* No number found */ + break; + } + } + + if (s <= 0) + { + *errorcodeptr = ERR15; + break; + } + + escape = -s; + break; + + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. Perl has changed + over the years. Nowadays \g{} for backreferences and \o{} for octal are + recommended to avoid the ambiguities in the old syntax. + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting left + brackets, it is a back reference. Otherwise, up to three octal digits are + read to form an escaped character code. Thus \123 is likely to be octal 123 + (cf \0123, which is octal 012 followed by the literal 3). + + Inside a character class, \ followed by a digit is always either a literal + 8 or 9 or an octal number. */ + + case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: + case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: + + if (!isclass) + { + oldptr = ptr; + ptr--; /* Back to the digit */ + if (!read_number(&ptr, ptrend, -1, INT_MAX/10 - 1, ERR61, &s, + errorcodeptr)) + break; + + /* \1 to \9 are always back references. \8x and \9x are too; \1x to \7x + are octal escapes if there are not that many previous captures. */ + + if (s < 10 || oldptr[-1] >= CHAR_8 || s <= (int)cb->bracount) + { + if (s > (int)MAX_GROUP_NUMBER) *errorcodeptr = ERR61; + else escape = -s; /* Indicates a back reference */ + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle a digit following \ when the number is not a back reference, or + we are within a character class. If the first digit is 8 or 9, Perl used to + generate a binary zero and then treat the digit as a following literal. At + least by Perl 5.18 this changed so as not to insert the binary zero. */ + + if (c >= CHAR_8) break; + + /* Fall through */ + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. The original code used just to take the least + significant 8 bits of octal numbers (I think this is what early Perls used + to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, + but no more than 3 octal digits. */ + + case CHAR_0: + c -= CHAR_0; + while(i++ < 2 && ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) + c = c * 8 + *ptr++ - CHAR_0; +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (!utf && c > 0xff) *errorcodeptr = ERR51; +#endif + break; + + /* \o is a relatively new Perl feature, supporting a more general way of + specifying character codes in octal. The only supported form is \o{ddd}. */ + + case CHAR_o: + if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET) + { + ptr--; + *errorcodeptr = ERR55; + } + else if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) + *errorcodeptr = ERR78; + else + { + c = 0; + overflow = FALSE; + while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) + { + cc = *ptr++; + if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ +#if PCRE2_CODE_UNIT_WIDTH == 32 + if (c >= 0x20000000l) { overflow = TRUE; break; } +#endif + c = (c << 3) + (cc - CHAR_0); +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; } +#elif PCRE2_CODE_UNIT_WIDTH == 16 + if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; } +#elif PCRE2_CODE_UNIT_WIDTH == 32 + if (utf && c > 0x10ffffU) { overflow = TRUE; break; } +#endif + } + if (overflow) + { + while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; + *errorcodeptr = ERR34; + } + else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) + { + if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL || + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)) + { + ptr--; + *errorcodeptr = ERR73; + } + } + else + { + ptr--; + *errorcodeptr = ERR64; + } + } + break; + + /* \x is complicated. When PCRE2_ALT_BSUX is set, \x must be followed by + two hexadecimal digits. Otherwise it is a lowercase x letter. */ + + case CHAR_x: + if ((options & PCRE2_ALT_BSUX) != 0) + { + uint32_t xc; + if (ptrend - ptr < 2) break; /* Less than 2 characters */ + if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */ + if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + c = (cc << 4) | xc; + ptr += 2; + } /* End PCRE2_ALT_BSUX handling */ + + /* Handle \x in Perl's style. \x{ddd} is a character number which can be + greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex + digits. If not, { used to be treated as a data character. However, Perl + seems to read hex digits up to the first non-such, and ignore the rest, so + that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE + now gives an error. */ + + else + { + if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) + { +#ifndef EBCDIC + COME_FROM_NU: +#endif + if (++ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) + { + *errorcodeptr = ERR78; + break; + } + c = 0; + overflow = FALSE; + + while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff) + { + ptr++; + if (c == 0 && cc == 0) continue; /* Leading zeroes */ +#if PCRE2_CODE_UNIT_WIDTH == 32 + if (c >= 0x10000000l) { overflow = TRUE; break; } +#endif + c = (c << 4) | cc; + if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR)) + { + overflow = TRUE; + break; + } + } + + if (overflow) + { + while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++; + *errorcodeptr = ERR34; + } + else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) + { + if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL || + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)) + { + ptr--; + *errorcodeptr = ERR73; + } + } + + /* If the sequence of hex digits does not end with '}', give an error. + We used just to recognize this construct and fall through to the normal + \x handling, but nowadays Perl gives an error, which seems much more + sensible, so we do too. */ + + else + { + ptr--; + *errorcodeptr = ERR67; + } + } /* End of \x{} processing */ + + /* Read a up to two hex digits after \x */ + + else + { + c = 0; + if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ + ptr++; + c = cc; + if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ + ptr++; + c = (c << 4) | cc; + } /* End of \xdd handling */ + } /* End of Perl-style \x handling */ + break; + + /* The handling of \c is different in ASCII and EBCDIC environments. In an + ASCII (or Unicode) environment, an error is given if the character + following \c is not a printable ASCII character. Otherwise, the following + character is upper-cased if it is a letter, and after that the 0x40 bit is + flipped. The result is the value of the escape. + + In an EBCDIC environment the handling of \c is compatible with the + specification in the perlebcdic document. The following character must be + a letter or one of small number of special characters. These provide a + means of defining the character values 0-31. + + For testing the EBCDIC handling of \c in an ASCII environment, recognize + the EBCDIC value of 'c' explicitly. */ + +#if defined EBCDIC && 'a' != 0x81 + case 0x83: +#else + case CHAR_c: +#endif + if (ptr >= ptrend) + { + *errorcodeptr = ERR2; + break; + } + c = *ptr; + if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); + + /* Handle \c in an ASCII/Unicode environment. */ + +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (c < 32 || c > 126) /* Excludes all non-printable ASCII */ + { + *errorcodeptr = ERR68; + break; + } + c ^= 0x40; + + /* Handle \c in an EBCDIC environment. The special case \c? is converted to + 255 (0xff) or 95 (0x5f) if other character suggest we are using th POSIX-BC + encoding. (This is the way Perl indicates that it handles \c?.) The other + valid sequences correspond to a list of specific characters. */ + +#else + if (c == CHAR_QUESTION_MARK) + c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff; + else + { + for (i = 0; i < 32; i++) + { + if (c == ebcdic_escape_c[i]) break; + } + if (i < 32) c = i; else *errorcodeptr = ERR68; + } +#endif /* EBCDIC */ + + ptr++; + break; + + /* Any other alphanumeric following \ is an error. Perl gives an error only + if in warning mode, but PCRE doesn't have a warning mode. */ + + default: + *errorcodeptr = ERR3; + *ptrptr = ptr - 1; /* Point to the character at fault */ + return 0; + } + } + +/* Set the pointer to the next character before returning. */ + +*ptrptr = ptr; +*chptr = c; +return escape; +} + + + +#ifdef SUPPORT_UNICODE +/************************************************* +* Handle \P and \p * +*************************************************/ + +/* This function is called after \P or \p has been encountered, provided that +PCRE2 is compiled with support for UTF and Unicode properties. On entry, the +contents of ptrptr are pointing after the P or p. On exit, it is left pointing +after the final code unit of the escape sequence. + +Arguments: + ptrptr the pattern position pointer + negptr a boolean that is set TRUE for negation else FALSE + ptypeptr an unsigned int that is set to the type value + pdataptr an unsigned int that is set to the detailed property value + errorcodeptr the error code variable + cb the compile data + +Returns: TRUE if the type value was found, or FALSE for an invalid type +*/ + +static BOOL +get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr, + uint16_t *pdataptr, int *errorcodeptr, compile_block *cb) +{ +PCRE2_UCHAR c; +PCRE2_SIZE i, bot, top; +PCRE2_SPTR ptr = *ptrptr; +PCRE2_UCHAR name[32]; + +if (ptr >= cb->end_pattern) goto ERROR_RETURN; +c = *ptr++; +*negptr = FALSE; + +/* \P or \p can be followed by a name in {}, optionally preceded by ^ for +negation. */ + +if (c == CHAR_LEFT_CURLY_BRACKET) + { + if (ptr >= cb->end_pattern) goto ERROR_RETURN; + if (*ptr == CHAR_CIRCUMFLEX_ACCENT) + { + *negptr = TRUE; + ptr++; + } + for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++) + { + if (ptr >= cb->end_pattern) goto ERROR_RETURN; + c = *ptr++; + if (c == CHAR_NUL) goto ERROR_RETURN; + if (c == CHAR_RIGHT_CURLY_BRACKET) break; + name[i] = c; + } + if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; + name[i] = 0; + } + +/* Otherwise there is just one following character, which must be an ASCII +letter. */ + +else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0) + { + name[0] = c; + name[1] = 0; + } +else goto ERROR_RETURN; + +*ptrptr = ptr; + +/* Search for a recognized property name using binary chop. */ + +bot = 0; +top = PRIV(utt_size); + +while (bot < top) + { + int r; + i = (bot + top) >> 1; + r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); + if (r == 0) + { + *ptypeptr = PRIV(utt)[i].type; + *pdataptr = PRIV(utt)[i].value; + return TRUE; + } + if (r > 0) bot = i + 1; else top = i; + } +*errorcodeptr = ERR47; /* Unrecognized name */ +return FALSE; + +ERROR_RETURN: /* Malformed \P or \p */ +*errorcodeptr = ERR46; +*ptrptr = ptr; +return FALSE; +} +#endif + + + +/************************************************* +* Check for POSIX class syntax * +*************************************************/ + +/* This function is called when the sequence "[:" or "[." or "[=" is +encountered in a character class. It checks whether this is followed by a +sequence of characters terminated by a matching ":]" or ".]" or "=]". If we +reach an unescaped ']' without the special preceding character, return FALSE. + +Originally, this function only recognized a sequence of letters between the +terminators, but it seems that Perl recognizes any sequence of characters, +though of course unknown POSIX names are subsequently rejected. Perl gives an +"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE +didn't consider this to be a POSIX class. Likewise for [:1234:]. + +The problem in trying to be exactly like Perl is in the handling of escapes. We +have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX +class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code +below handles the special cases \\ and \], but does not try to do any other +escape processing. This makes it different from Perl for cases such as +[:l\ower:] where Perl recognizes it as the POSIX class "lower" but PCRE does +not recognize "l\ower". This is a lesser evil than not diagnosing bad classes +when Perl does, I think. + +A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not. +It seems that the appearance of a nested POSIX class supersedes an apparent +external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or +a digit. This is handled by returning FALSE if the start of a new group with +the same terminator is encountered, since the next closing sequence must close +the nested group, not the outer one. + +In Perl, unescaped square brackets may also appear as part of class names. For +example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for +[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not +seem right at all. PCRE does not allow closing square brackets in POSIX class +names. + +Arguments: + ptr pointer to the character after the initial [ (colon, dot, equals) + ptrend pointer to the end of the pattern + endptr where to return a pointer to the terminating ':', '.', or '=' + +Returns: TRUE or FALSE +*/ + +static BOOL +check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, PCRE2_SPTR *endptr) +{ +PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */ +terminator = *ptr++; /* compiler warns about "non-constant" initializer. */ + +for (; ptrend - ptr >= 2; ptr++) + { + if (*ptr == CHAR_BACKSLASH && + (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH)) + ptr++; + + else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) || + *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; + + else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) + { + *endptr = ptr; + return TRUE; + } + } + +return FALSE; +} + + + +/************************************************* +* Check POSIX class name * +*************************************************/ + +/* This function is called to check the name given in a POSIX-style class entry +such as [:alnum:]. + +Arguments: + ptr points to the first letter + len the length of the name + +Returns: a value representing the name, or -1 if unknown +*/ + +static int +check_posix_name(PCRE2_SPTR ptr, int len) +{ +const char *pn = posix_names; +int yield = 0; +while (posix_name_lengths[yield] != 0) + { + if (len == posix_name_lengths[yield] && + PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield; + pn += posix_name_lengths[yield] + 1; + yield++; + } +return -1; +} + + + +/************************************************* +* Read a subpattern or VERB name * +*************************************************/ + +/* This function is called from parse_regex() below whenever it needs to read +the name of a subpattern or a (*VERB). The initial pointer must be to the +character before the name. If that character is '*' we are reading a verb name. +The pointer is updated to point after the name, for a VERB, or after tha name's +terminator for a subpattern name. Returning both the offset and the name +pointer is redundant information, but some callers use one and some the other, +so it is simplest just to return both. + +Arguments: + ptrptr points to the character pointer variable + ptrend points to the end of the input string + terminator the terminator of a subpattern name must be this + offsetptr where to put the offset from the start of the pattern + nameptr where to put a pointer to the name in the input + namelenptr where to put the length of the name + errcodeptr where to put an error code + cb pointer to the compile data block + +Returns: TRUE if a name was read + FALSE otherwise, with error code set +*/ + +static BOOL +read_name(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t terminator, + PCRE2_SIZE *offsetptr, PCRE2_SPTR *nameptr, uint32_t *namelenptr, + int *errorcodeptr, compile_block *cb) +{ +PCRE2_SPTR ptr = *ptrptr; +BOOL is_verb = (*ptr == CHAR_ASTERISK); +uint32_t namelen = 0; +uint32_t ctype = is_verb? ctype_letter : ctype_word; + +if (++ptr >= ptrend) + { + *errorcodeptr = is_verb? ERR60: /* Verb not recognized or malformed */ + ERR62; /* Subpattern name expected */ + goto FAILED; + } + +*nameptr = ptr; +*offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern); + +if (IS_DIGIT(*ptr)) + { + *errorcodeptr = ERR44; /* Group name must not start with digit */ + goto FAILED; + } + +while (ptr < ptrend && MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype) != 0) + { + ptr++; + namelen++; + if (namelen > MAX_NAME_SIZE) + { + *errorcodeptr = ERR48; + goto FAILED; + } + } + +/* Subpattern names must not be empty, and their terminator is checked here. +(What follows a verb name is checked separately.) */ + +if (!is_verb) + { + if (namelen == 0) + { + *errorcodeptr = ERR62; /* Subpattern name expected */ + goto FAILED; + } + if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + ptr++; + } + +*namelenptr = namelen; +*ptrptr = ptr; +return TRUE; + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + +/************************************************* +* Manage callouts at start of cycle * +*************************************************/ + +/* At the start of a new item in parse_regex() we are able to record the +details of the previous item in a prior callout, and also to set up an +automatic callout if enabled. Avoid having two adjacent automatic callouts, +which would otherwise happen for items such as \Q that contribute nothing to +the parsed pattern. + +Arguments: + ptr current pattern pointer + pcalloutptr points to a pointer to previous callout, or NULL + auto_callout TRUE if auto_callouts are enabled + parsed_pattern the parsed pattern pointer + cb compile block + +Returns: possibly updated parsed_pattern pointer. +*/ + +static uint32_t * +manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout, + uint32_t *parsed_pattern, compile_block *cb) +{ +uint32_t *previous_callout = *pcalloutptr; + +if (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr - + cb->start_pattern - (PCRE2_SIZE)previous_callout[1]); + +if (!auto_callout) previous_callout = NULL; else + { + if (previous_callout == NULL || + previous_callout != parsed_pattern - 4 || + previous_callout[3] != 255) + { + previous_callout = parsed_pattern; /* Set up new automatic callout */ + parsed_pattern += 4; + previous_callout[0] = META_CALLOUT_NUMBER; + previous_callout[2] = 0; + previous_callout[3] = 255; + } + previous_callout[1] = (uint32_t)(ptr - cb->start_pattern); + } + +*pcalloutptr = previous_callout; +return parsed_pattern; +} + + + +/************************************************* +* Parse regex and identify named groups * +*************************************************/ + +/* This function is called first of all. It scans the pattern and does two +things: (1) It identifies capturing groups and makes a table of named capturing +groups so that information about them is fully available to both the compiling +scans. (2) It writes a parsed version of the pattern with comments omitted and +escapes processed into the parsed_pattern vector. + +Arguments: + ptr points to the start of the pattern + options compiling dynamic options (may change during the scan) + has_lookbehind points to a boolean, set TRUE if a lookbehind is found + cb pointer to the compile data block + +Returns: zero on success or a non-zero error code, with the + error offset placed in the cb field +*/ + +/* A structure and some flags for dealing with nested groups. */ + +typedef struct nest_save { + uint16_t nest_depth; + uint16_t reset_group; + uint16_t max_group; + uint16_t flags; + uint32_t options; +} nest_save; + +#define NSF_RESET 0x0001u +#define NSF_CONDASSERT 0x0002u + +/* Options that are changeable within the pattern must be tracked during +parsing. Some (e.g. PCRE2_EXTENDED) are implemented entirely during parsing, +but all must be tracked so that META_OPTIONS items set the correct values for +the main compiling phase. */ + +#define PARSE_TRACKED_OPTIONS (PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_DUPNAMES| \ + PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| \ + PCRE2_UNGREEDY) + +/* States used for analyzing ranges in character classes. The two OK values +must be last. */ + +enum { RANGE_NO, RANGE_STARTED, RANGE_OK_ESCAPED, RANGE_OK_LITERAL }; + +/* Only in 32-bit mode can there be literals > META_END. A macros encapsulates +the storing of literal values in the parsed pattern. */ + +#if PCRE2_CODE_UNIT_WIDTH == 32 +#define PARSED_LITERAL(c, p) \ + { \ + if (c >= META_END) *p++ = META_BIGVALUE; \ + *p++ = c; \ + okquantifier = TRUE; \ + } +#else +#define PARSED_LITERAL(c, p) *p++ = c; okquantifier = TRUE; +#endif + +/* Here's the actual function. */ + +static int parse_regex(PCRE2_SPTR ptr, uint32_t options, BOOL *has_lookbehind, + compile_block *cb) +{ +uint32_t c; +uint32_t delimiter; +uint32_t namelen; +uint32_t class_range_state; +uint32_t *verblengthptr = NULL; /* Value avoids compiler warning */ +uint32_t *previous_callout = NULL; +uint32_t *parsed_pattern = cb->parsed_pattern; +uint32_t *parsed_pattern_end = cb->parsed_pattern_end; +uint32_t meta_quantifier = 0; +uint32_t add_after_mark = 0; +uint16_t nest_depth = 0; +int after_manual_callout = 0; +int expect_cond_assert = 0; +int errorcode = 0; +int escape; +int i; +BOOL inescq = FALSE; +BOOL inverbname = FALSE; +BOOL utf = (options & PCRE2_UTF) != 0; +BOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0; +BOOL isdupname; +BOOL negate_class; +BOOL okquantifier = FALSE; +PCRE2_SPTR thisptr; +PCRE2_SPTR name; +PCRE2_SPTR ptrend = cb->end_pattern; +PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */ +named_group *ng; +nest_save *top_nest, *end_nests; + +/* Insert leading items for word and line matching (features provided for the +benefit of pcre2grep). */ + +if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_LINE) != 0) + { + *parsed_pattern++ = META_CIRCUMFLEX; + *parsed_pattern++ = META_NOCAPTURE; + } +else if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_WORD) != 0) + { + *parsed_pattern++ = META_ESCAPE + ESC_b; + *parsed_pattern++ = META_NOCAPTURE; + } + +/* If the pattern is actually a literal string, process it separately to avoid +cluttering up the main loop. */ + +if ((options & PCRE2_LITERAL) != 0) + { + while (ptr < ptrend) + { + if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } + thisptr = ptr; + GETCHARINCTEST(c, ptr); + if (auto_callout) + parsed_pattern = manage_callouts(thisptr, &previous_callout, + auto_callout, parsed_pattern, cb); + PARSED_LITERAL(c, parsed_pattern); + } + goto PARSED_END; + } + +/* Process a real regex which may contain meta-characters. */ + +top_nest = NULL; +end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size); + +/* The size of the nest_save structure might not be a factor of the size of the +workspace. Therefore we must round down end_nests so as to correctly avoid +creating a nest_save that spans the end of the workspace. */ + +end_nests = (nest_save *)((char *)end_nests - + ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save))); + +/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */ + +if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED; + +/* Now scan the pattern */ + +while (ptr < ptrend) + { + int prev_expect_cond_assert; + uint32_t min_repeat, max_repeat; + uint32_t set, unset, *optset; + uint32_t terminator; + uint32_t prev_meta_quantifier; + BOOL prev_okquantifier; + PCRE2_SPTR tempptr; + PCRE2_SIZE offset; + + if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } + + if (nest_depth > cb->cx->parens_nest_limit) + { + errorcode = ERR19; + goto FAILED; /* Parentheses too deeply nested */ + } + + /* Get next input character, save its position for callout handling. */ + + thisptr = ptr; + GETCHARINCTEST(c, ptr); + + /* Copy quoted literals until \E, allowing for the possibility of automatic + callouts, except when processing a (*VERB) "name". */ + + if (inescq) + { + if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) + { + inescq = FALSE; + ptr++; /* Skip E */ + } + else + { + if (expect_cond_assert > 0) /* A literal is not allowed if we are */ + { /* expecting a conditional assertion, */ + ptr--; /* but an empty \Q\E sequence is OK. */ + errorcode = ERR28; + goto FAILED; + } + if (!inverbname && after_manual_callout-- <= 0) + parsed_pattern = manage_callouts(thisptr, &previous_callout, + auto_callout, parsed_pattern, cb); + PARSED_LITERAL(c, parsed_pattern); + meta_quantifier = 0; + } + continue; /* Next character */ + } + + /* If we are processing the "name" part of a (*VERB:NAME) item, all + characters up to the closing parenthesis are literals except when + PCRE2_ALT_VERBNAMES is set. That causes backslash interpretation, but only \Q + and \E and escaped characters are allowed (no character types such as \d). If + PCRE2_EXTENDED is also set, we must ignore white space and # comments. Do + this by not entering the special (*VERB:NAME) processing - they are then + picked up below. Note that c is a character, not a code unit, so we must not + use MAX_255 to test its size because MAX_255 tests code units and is assumed + TRUE in 8-bit mode. */ + + if (inverbname && + ( + /* EITHER: not both options set */ + ((options & (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) != + (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) || +#ifdef SUPPORT_UNICODE + /* OR: character > 255 AND not Unicode Pattern White Space */ + (c > 255 && (c|1) != 0x200f && (c|1) != 0x2029) || +#endif + /* OR: not a # comment or isspace() white space */ + (c < 256 && c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0 +#ifdef SUPPORT_UNICODE + /* and not CHAR_NEL when Unicode is supported */ + && c != CHAR_NEL +#endif + ))) + { + PCRE2_SIZE verbnamelength; + + switch(c) + { + default: + PARSED_LITERAL(c, parsed_pattern); + break; + + case CHAR_RIGHT_PARENTHESIS: + inverbname = FALSE; + okquantifier = FALSE; /* Was probably set by literals */ + /* This is the length in characters */ + verbnamelength = (PCRE2_SIZE)(parsed_pattern - verblengthptr - 1); + /* But the limit on the length is in code units */ + if (ptr - verbnamestart - 1 > (int)MAX_MARK) + { + ptr--; + errorcode = ERR76; + goto FAILED; + } + *verblengthptr = (uint32_t)verbnamelength; + + /* If this name was on a verb such as (*ACCEPT) which does not continue, + a (*MARK) was generated for the name. We now add the original verb as the + next item. */ + + if (add_after_mark != 0) + { + *parsed_pattern++ = add_after_mark; + add_after_mark = 0; + } + break; + + case CHAR_BACKSLASH: + if ((options & PCRE2_ALT_VERBNAMES) != 0) + { + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, + FALSE, cb); + if (errorcode != 0) goto FAILED; + } + else escape = 0; /* Treat all as literal */ + + switch(escape) + { + case 0: + PARSED_LITERAL(c, parsed_pattern); + break; + + case ESC_Q: + inescq = TRUE; + break; + + case ESC_E: /* Ignore */ + break; + + default: + errorcode = ERR40; /* Invalid in verb name */ + goto FAILED; + } + } + continue; /* Next character in pattern */ + } + + /* Not a verb name character. At this point we must process everything that + must not change the quantification state. This is mainly comments, but we + handle \Q and \E here as well, so that an item such as A\Q\E+ is treated as + A+, as in Perl. An isolated \E is ignored. */ + + if (c == CHAR_BACKSLASH && ptr < ptrend) + { + if (*ptr == CHAR_Q || *ptr == CHAR_E) + { + inescq = *ptr == CHAR_Q; + ptr++; + continue; + } + } + + /* Skip over whitespace and # comments in extended mode. Note that c is a + character, not a code unit, so we must not use MAX_255 to test its size + because MAX_255 tests code units and is assumed TRUE in 8-bit mode. The + whitespace characters are those designated as "Pattern White Space" by + Unicode, which are the isspace() characters plus CHAR_NEL (newline), which is + U+0085 in Unicode, plus U+200E, U+200F, U+2028, and U+2029. These are a + subset of space characters that match \h and \v. */ + + if ((options & PCRE2_EXTENDED) != 0) + { + if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue; +#ifdef SUPPORT_UNICODE + if (c == CHAR_NEL || (c|1) == 0x200f || (c|1) == 0x2029) continue; +#endif + if (c == CHAR_NUMBER_SIGN) + { + while (ptr < ptrend) + { + if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */ + { /* IS_NEWLINE sets cb->nllen. */ + ptr += cb->nllen; + break; + } + ptr++; +#ifdef SUPPORT_UNICODE + if (utf) FORWARDCHARTEST(ptr, ptrend); +#endif + } + continue; /* Next character in pattern */ + } + } + + /* Skip over bracketed comments */ + + if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 && + ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN) + { + while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS); + if (ptr >= ptrend) + { + errorcode = ERR18; /* A special error for missing ) in a comment */ + goto FAILED; /* to make it easier to debug. */ + } + ptr++; + continue; /* Next character in pattern */ + } + + /* If the next item is not a quantifier, fill in length of any previous + callout and create an auto callout if required. */ + + if (c != CHAR_ASTERISK && c != CHAR_PLUS && c != CHAR_QUESTION_MARK && + (c != CHAR_LEFT_CURLY_BRACKET || + (tempptr = ptr, + !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode)))) + { + if (after_manual_callout-- <= 0) + parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout, + parsed_pattern, cb); + } + + /* If expect_cond_assert is 2, we have just passed (?( and are expecting an + assertion, possibly preceded by a callout. If the value is 1, we have just + had the callout and expect an assertion. There must be at least 3 more + characters in all cases. When expect_cond_assert is 2, we know that the + current character is an opening parenthesis, as otherwise we wouldn't be + here. However, when it is 1, we need to check, and it's easiest just to check + always. Note that expect_cond_assert may be negative, since all callouts just + decrement it. */ + + if (expect_cond_assert > 0) + { + BOOL ok = c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 3 && + ptr[0] == CHAR_QUESTION_MARK; + if (ok) switch(ptr[1]) + { + case CHAR_C: + ok = expect_cond_assert == 2; + break; + + case CHAR_EQUALS_SIGN: + case CHAR_EXCLAMATION_MARK: + break; + + case CHAR_LESS_THAN_SIGN: + ok = ptr[2] == CHAR_EQUALS_SIGN || ptr[2] == CHAR_EXCLAMATION_MARK; + break; + + default: + ok = FALSE; + } + + if (!ok) + { + ptr--; /* Adjust error offset */ + errorcode = ERR28; + goto FAILED; + } + } + + /* Remember whether we are expecting a conditional assertion, and set the + default for this item. */ + + prev_expect_cond_assert = expect_cond_assert; + expect_cond_assert = 0; + + /* Remember quantification status for the previous significant item, then set + default for this item. */ + + prev_okquantifier = okquantifier; + prev_meta_quantifier = meta_quantifier; + okquantifier = FALSE; + meta_quantifier = 0; + + /* If the previous significant item was a quantifier, adjust the parsed code + if there is a following modifier. The base meta value is always followed by + the PLUS and QUERY values, in that order. We do this here rather than after + reading a quantifier so that intervening comments and /x whitespace can be + ignored without having to replicate code. */ + + if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS)) + { + parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] = + prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)? + 0x00020000u : 0x00010000u); + continue; /* Next character in pattern */ + } + + + /* Process the next item in the main part of a pattern. */ + + switch(c) + { + default: /* Non-special character */ + PARSED_LITERAL(c, parsed_pattern); + break; + + + /* ---- Escape sequence ---- */ + + case CHAR_BACKSLASH: + tempptr = ptr; + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, + FALSE, cb); + if (errorcode != 0) + { + ESCAPE_FAILED: + if ((cb->cx->extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0) + goto FAILED; + ptr = tempptr; + if (ptr >= ptrend) c = CHAR_BACKSLASH; else + { + GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ + } + escape = 0; /* Treat as literal character */ + } + + /* The escape was a data escape or literal character. */ + + if (escape == 0) + { + PARSED_LITERAL(c, parsed_pattern); + } + + /* The escape was a back (or forward) reference. We keep the offset in + order to give a more useful diagnostic for a bad forward reference. For + references to groups numbered less than 10 we can't use more than two items + in parsed_pattern because they may be just two characters in the input (and + in a 64-bit world an offset may need two elements). So for them, the offset + of the first occurrent is held in a special vector. */ + + else if (escape < 0) + { + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1); + escape = -escape; + *parsed_pattern++ = META_BACKREF | (uint32_t)escape; + if (escape < 10) + { + if (cb->small_ref_offset[escape] == PCRE2_UNSET) + cb->small_ref_offset[escape] = offset; + } + else + { + PUTOFFSET(offset, parsed_pattern); + } + okquantifier = TRUE; + } + + /* The escape was a character class such as \d etc. or other special + escape indicator such as \A or \X. Most of them generate just a single + parsed item, but \P and \p are followed by a 16-bit type and a 16-bit + value. They are supported only when Unicode is available. The type and + value are packed into a single 32-bit value so that the whole sequences + uses only two elements in the parsed_vector. This is because the same + coding is used if \d (for example) is turned into \p{Nd} when PCRE2_UCP is + set. + + There are also some cases where the escape sequence is followed by a name: + \k{name}, \k, and \k'name' are backreferences by name, and \g + and \g'name' are subroutine calls by name; \g{name} is a synonym for + \k{name}. Note that \g and \g'number' are handled by check_escape() + and returned as a negative value (handled above). A name is coded as an + offset into the pattern and a length. */ + + else switch (escape) + { + case ESC_C: +#ifdef NEVER_BACKSLASH_C + errorcode = ERR85; + goto ESCAPE_FAILED; +#else + if ((options & PCRE2_NEVER_BACKSLASH_C) != 0) + { + errorcode = ERR83; + goto ESCAPE_FAILED; + } +#endif + okquantifier = TRUE; + *parsed_pattern++ = META_ESCAPE + escape; + break; + + case ESC_X: +#ifndef SUPPORT_UNICODE + errorcode = ERR45; /* Supported only with Unicode support */ + goto ESCAPE_FAILED; +#endif + case ESC_H: + case ESC_h: + case ESC_N: + case ESC_R: + case ESC_V: + case ESC_v: + okquantifier = TRUE; + *parsed_pattern++ = META_ESCAPE + escape; + break; + + default: /* \A, \B, \b, \G, \K, \Z, \z cannot be quantified. */ + *parsed_pattern++ = META_ESCAPE + escape; + break; + + /* Escapes that change in UCP mode. Note that PCRE2_UCP will never be set + without Unicode support because it is checked when pcre2_compile() is + called. */ + + case ESC_d: + case ESC_D: + case ESC_s: + case ESC_S: + case ESC_w: + case ESC_W: + okquantifier = TRUE; + if ((options & PCRE2_UCP) == 0) + { + *parsed_pattern++ = META_ESCAPE + escape; + } + else + { + *parsed_pattern++ = META_ESCAPE + + ((escape == ESC_d || escape == ESC_s || escape == ESC_w)? + ESC_p : ESC_P); + switch(escape) + { + case ESC_d: + case ESC_D: + *parsed_pattern++ = (PT_PC << 16) | ucp_Nd; + break; + + case ESC_s: + case ESC_S: + *parsed_pattern++ = PT_SPACE << 16; + break; + + case ESC_w: + case ESC_W: + *parsed_pattern++ = PT_WORD << 16; + break; + } + } + break; + + /* Unicode property matching */ + + case ESC_P: + case ESC_p: +#ifdef SUPPORT_UNICODE + { + BOOL negated; + uint16_t ptype = 0, pdata = 0; + if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) + goto ESCAPE_FAILED; + if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; + *parsed_pattern++ = META_ESCAPE + escape; + *parsed_pattern++ = (ptype << 16) | pdata; + okquantifier = TRUE; + } +#else + errorcode = ERR45; + goto ESCAPE_FAILED; +#endif + break; /* End \P and \p */ + + /* When \g is used with quotes or angle brackets as delimiters, it is a + numerical or named subroutine call, and control comes here. When used + with brace delimiters it is a numberical back reference and does not come + here because check_escape() returns it directly as a reference. \k is + always a named back reference. */ + + case ESC_g: + case ESC_k: + if (ptr >= ptrend || (*ptr != CHAR_LEFT_CURLY_BRACKET && + *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE)) + { + errorcode = (escape == ESC_g)? ERR57 : ERR69; + goto ESCAPE_FAILED; + } + terminator = (*ptr == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? + CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; + + /* For a non-braced \g, check for a numerical recursion. */ + + if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET) + { + PCRE2_SPTR p = ptr + 1; + + if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, + &errorcode)) + { + if (p >= ptrend || *p != terminator) + { + errorcode = ERR57; + goto ESCAPE_FAILED; + } + ptr = p; + goto SET_RECURSION; + } + if (errorcode != 0) goto ESCAPE_FAILED; + } + + /* Not a numerical recursion */ + + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto ESCAPE_FAILED; + + /* \k and \g when used with braces are back references, whereas \g used + with quotes or angle brackets is a recursion */ + + *parsed_pattern++ = + (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)? + META_BACKREF_BYNAME : META_RECURSE_BYNAME; + *parsed_pattern++ = namelen; + + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; /* End special escape processing */ + } + break; /* End escape sequence processing */ + + + /* ---- Single-character special items ---- */ + + case CHAR_CIRCUMFLEX_ACCENT: + *parsed_pattern++ = META_CIRCUMFLEX; + break; + + case CHAR_DOLLAR_SIGN: + *parsed_pattern++ = META_DOLLAR; + break; + + case CHAR_DOT: + *parsed_pattern++ = META_DOT; + okquantifier = TRUE; + break; + + + /* ---- Single-character quantifiers ---- */ + + case CHAR_ASTERISK: + meta_quantifier = META_ASTERISK; + goto CHECK_QUANTIFIER; + + case CHAR_PLUS: + meta_quantifier = META_PLUS; + goto CHECK_QUANTIFIER; + + case CHAR_QUESTION_MARK: + meta_quantifier = META_QUERY; + goto CHECK_QUANTIFIER; + + + /* ---- Potential {n,m} quantifier ---- */ + + case CHAR_LEFT_CURLY_BRACKET: + if (!read_repeat_counts(&ptr, ptrend, &min_repeat, &max_repeat, + &errorcode)) + { + if (errorcode != 0) goto FAILED; /* Error in quantifier. */ + PARSED_LITERAL(c, parsed_pattern); /* Not a quantifier */ + break; /* No more quantifier processing */ + } + meta_quantifier = META_MINMAX; + /* Fall through */ + + + /* ---- Quantifier post-processing ---- */ + + /* Check that a quantifier is allowed after the previous item. */ + + CHECK_QUANTIFIER: + if (!prev_okquantifier) + { + errorcode = ERR9; + goto FAILED_BACK; + } + + /* Now we can put the quantifier into the parsed pattern vector. At this + stage, we have only the basic quantifier. The check for a following + or ? + modifier happens at the top of the loop, after any intervening comments + have been removed. */ + + *parsed_pattern++ = meta_quantifier; + if (c == CHAR_LEFT_CURLY_BRACKET) + { + *parsed_pattern++ = min_repeat; + *parsed_pattern++ = max_repeat; + } + break; + + + /* ---- Character class ---- */ + + case CHAR_LEFT_SQUARE_BRACKET: + okquantifier = TRUE; + + /* In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is + used for "start of word" and "end of word". As these are otherwise illegal + sequences, we don't break anything by recognizing them. They are replaced + by \b(?=\w) and \b(?<=\w) respectively. Sequences like [a[:<:]] are + erroneous and are handled by the normal code below. */ + + if (ptrend - ptr >= 6 && + (PRIV(strncmp_c8)(ptr, STRING_WEIRD_STARTWORD, 6) == 0 || + PRIV(strncmp_c8)(ptr, STRING_WEIRD_ENDWORD, 6) == 0)) + { + *parsed_pattern++ = META_ESCAPE + ESC_b; + + if (ptr[2] == CHAR_LESS_THAN_SIGN) + { + *parsed_pattern++ = META_LOOKAHEAD; + } + else + { + *parsed_pattern++ = META_LOOKBEHIND; + *has_lookbehind = TRUE; + + /* The offset is used only for the "non-fixed length" error; this won't + occur here, so just store zero. */ + + PUTOFFSET((PCRE2_SIZE)0, parsed_pattern); + } + + if ((options & PCRE2_UCP) == 0) + *parsed_pattern++ = META_ESCAPE + ESC_w; + else + { + *parsed_pattern++ = META_ESCAPE + ESC_p; + *parsed_pattern++ = PT_WORD << 16; + } + *parsed_pattern++ = META_KET; + ptr += 6; + break; + } + + /* PCRE supports POSIX class stuff inside a class. Perl gives an error if + they are encountered at the top level, so we'll do that too. */ + + if (ptr < ptrend && (*ptr == CHAR_COLON || *ptr == CHAR_DOT || + *ptr == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, ptrend, &tempptr)) + { + errorcode = (*ptr-- == CHAR_COLON)? ERR12 : ERR13; + goto FAILED; + } + + /* Process a regular character class. If the first character is '^', set + the negation flag. If the first few characters (either before or after ^) + are \Q\E or \E or space or tab in extended-more mode, we skip them too. + This makes for compatibility with Perl. */ + + negate_class = FALSE; + while (ptr < ptrend) + { + GETCHARINCTEST(c, ptr); + if (c == CHAR_BACKSLASH) + { + if (ptr < ptrend && *ptr == CHAR_E) ptr++; + else if (ptrend - ptr >= 3 && + PRIV(strncmp_c8)(ptr, STR_Q STR_BACKSLASH STR_E, 3) == 0) + ptr += 3; + else + break; + } + else if ((options & PCRE2_EXTENDED_MORE) != 0 && + (c == CHAR_SPACE || c == CHAR_HT)) /* Note: just these two */ + continue; + else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) + negate_class = TRUE; + else break; + } + + /* Now the real contents of the class; c has the first "real" character. + Empty classes are permitted only if the option is set. */ + + if (c == CHAR_RIGHT_SQUARE_BRACKET && + (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) + { + *parsed_pattern++ = negate_class? META_CLASS_EMPTY_NOT : META_CLASS_EMPTY; + break; /* End of class processing */ + } + + /* Process a non-empty class. */ + + *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS; + class_range_state = RANGE_NO; + + /* In an EBCDIC environment, Perl treats alphabetic ranges specially + because there are holes in the encoding, and simply using the range A-Z + (for example) would include the characters in the holes. This applies only + to ranges where both values are literal; [\xC1-\xE9] is different to [A-Z] + in this respect. In order to accommodate this, we keep track of whether + character values are literal or not, and a state variable for handling + ranges. */ + + /* Loop for the contents of the class */ + + for (;;) + { + BOOL char_is_literal = TRUE; + + /* Inside \Q...\E everything is literal except \E */ + + if (inescq) + { + if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) + { + inescq = FALSE; /* Reset literal state */ + ptr++; /* Skip the 'E' */ + goto CLASS_CONTINUE; + } + goto CLASS_LITERAL; + } + + /* Skip over space and tab (only) in extended-more mode. */ + + if ((options & PCRE2_EXTENDED_MORE) != 0 && + (c == CHAR_SPACE || c == CHAR_HT)) + goto CLASS_CONTINUE; + + /* Handle POSIX class names. Perl allows a negation extension of the + form [:^name:]. A square bracket that doesn't match the syntax is + treated as a literal. We also recognize the POSIX constructions + [.ch.] and [=ch=] ("collating elements") and fault them, as Perl + 5.6 and 5.8 do. */ + + if (c == CHAR_LEFT_SQUARE_BRACKET && + ptrend - ptr >= 3 && + (*ptr == CHAR_COLON || *ptr == CHAR_DOT || + *ptr == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, ptrend, &tempptr)) + { + BOOL posix_negate = FALSE; + int posix_class; + + /* Perl treats a hyphen before a POSIX class as a literal, not the + start of a range. However, it gives a warning in its warning mode. PCRE + does not have a warning mode, so we give an error, because this is + likely an error on the user's part. */ + + if (class_range_state == RANGE_STARTED) + { + errorcode = ERR50; + goto FAILED; + } + + if (*ptr != CHAR_COLON) + { + errorcode = ERR13; + goto FAILED_BACK; + } + + if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT) + { + posix_negate = TRUE; + ptr++; + } + + posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); + if (posix_class < 0) + { + errorcode = ERR30; + goto FAILED; + } + ptr = tempptr + 2; + + /* Perl treats a hyphen after a POSIX class as a literal, not the + start of a range. However, it gives a warning in its warning mode + unless the hyphen is the last character in the class. PCRE does not + have a warning mode, so we give an error, because this is likely an + error on the user's part. */ + + if (ptr < ptrend - 1 && *ptr == CHAR_MINUS && + ptr[1] != CHAR_RIGHT_SQUARE_BRACKET) + { + errorcode = ERR50; + goto FAILED; + } + + /* Set "a hyphen is not the start of a range" for the -] case, and also + in case the POSIX class is followed by \E or \Q\E (possibly repeated - + fuzzers do that kind of thing) and *then* a hyphen. This causes that + hyphen to be treated as a literal. I don't think it's worth setting up + special apparatus to do otherwise. */ + + class_range_state = RANGE_NO; + + /* When PCRE2_UCP is set, some of the POSIX classes are converted to + use Unicode properties \p or \P or, in one case, \h or \H. The + substitutes table has two values per class, containing the type and + value of a \p or \P item. The special cases are specified with a + negative type: a non-zero value causes \h or \H to be used, and a zero + value falls through to behave like a non-UCP POSIX class. */ + +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UCP) != 0) + { + int ptype = posix_substitutes[2*posix_class]; + int pvalue = posix_substitutes[2*posix_class + 1]; + if (ptype >= 0) + { + *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_P : ESC_p); + *parsed_pattern++ = (ptype << 16) | pvalue; + goto CLASS_CONTINUE; + } + + if (pvalue != 0) + { + *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h); + goto CLASS_CONTINUE; + } + + /* Fall through */ + } +#endif /* SUPPORT_UNICODE */ + + /* Non-UCP POSIX class */ + + *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX; + *parsed_pattern++ = posix_class; + } + + /* Handle potential start of range */ + + else if (c == CHAR_MINUS && class_range_state >= RANGE_OK_ESCAPED) + { + *parsed_pattern++ = (class_range_state == RANGE_OK_LITERAL)? + META_RANGE_LITERAL : META_RANGE_ESCAPED; + class_range_state = RANGE_STARTED; + } + + /* Handle a literal character */ + + else if (c != CHAR_BACKSLASH) + { + CLASS_LITERAL: + if (class_range_state == RANGE_STARTED) + { + if (c == parsed_pattern[-2]) /* Optimize one-char range */ + parsed_pattern--; + else if (parsed_pattern[-2] > c) /* Check range is in order */ + { + errorcode = ERR8; + goto FAILED_BACK; + } + else + { + if (!char_is_literal && parsed_pattern[-1] == META_RANGE_LITERAL) + parsed_pattern[-1] = META_RANGE_ESCAPED; + PARSED_LITERAL(c, parsed_pattern); + } + class_range_state = RANGE_NO; + } + else /* Potential start of range */ + { + class_range_state = char_is_literal? + RANGE_OK_LITERAL : RANGE_OK_ESCAPED; + PARSED_LITERAL(c, parsed_pattern); + } + } + + /* Handle escapes in a class */ + + else + { + tempptr = ptr; + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, + options, TRUE, cb); + if (errorcode != 0) + { + CLASS_ESCAPE_FAILED: + if ((cb->cx->extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0) + goto FAILED; + ptr = tempptr; + if (ptr >= ptrend) c = CHAR_BACKSLASH; else + { + GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ + } + escape = 0; /* Treat as literal character */ + } + + if (escape == 0) /* Escaped character code point is in c */ + { + char_is_literal = FALSE; + goto CLASS_LITERAL; + } + + /* These three escapes do not alter the class range state. */ + + if (escape == ESC_b) + { + c = CHAR_BS; /* \b is backspace in a class */ + char_is_literal = FALSE; + goto CLASS_LITERAL; + } + + else if (escape == ESC_Q) + { + inescq = TRUE; /* Enter literal mode */ + goto CLASS_CONTINUE; + } + + else if (escape == ESC_E) /* Ignore orphan \E */ + goto CLASS_CONTINUE; + + /* The second part of a range can be a single-character escape + sequence (detected above), but not any of the other escapes. Perl + treats a hyphen as a literal in such circumstances. However, in Perl's + warning mode, a warning is given, so PCRE now faults it, as it is + almost certainly a mistake on the user's part. */ + + if (class_range_state == RANGE_STARTED) + { + errorcode = ERR50; + goto CLASS_ESCAPE_FAILED; + } + + /* Of the remaining escapes, only those that define characters are + allowed in a class. None may start a range. */ + + class_range_state = RANGE_NO; + switch(escape) + { + case ESC_N: + errorcode = ERR71; /* Not supported in a class */ + goto CLASS_ESCAPE_FAILED; + + case ESC_H: + case ESC_h: + case ESC_V: + case ESC_v: + *parsed_pattern++ = META_ESCAPE + escape; + break; + + /* These escapes are converted to Unicode property tests when + PCRE2_UCP is set. */ + + case ESC_d: + case ESC_D: + case ESC_s: + case ESC_S: + case ESC_w: + case ESC_W: + if ((options & PCRE2_UCP) == 0) + { + *parsed_pattern++ = META_ESCAPE + escape; + } + else + { + *parsed_pattern++ = META_ESCAPE + + ((escape == ESC_d || escape == ESC_s || escape == ESC_w)? + ESC_p : ESC_P); + switch(escape) + { + case ESC_d: + case ESC_D: + *parsed_pattern++ = (PT_PC << 16) | ucp_Nd; + break; + + case ESC_s: + case ESC_S: + *parsed_pattern++ = PT_SPACE << 16; + break; + + case ESC_w: + case ESC_W: + *parsed_pattern++ = PT_WORD << 16; + break; + } + } + break; + + /* Explicit Unicode property matching */ + + case ESC_P: + case ESC_p: +#ifdef SUPPORT_UNICODE + { + BOOL negated; + uint16_t ptype = 0, pdata = 0; + if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) + goto FAILED; + if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; + *parsed_pattern++ = META_ESCAPE + escape; + *parsed_pattern++ = (ptype << 16) | pdata; + } +#else + errorcode = ERR45; + goto CLASS_ESCAPE_FAILED; +#endif + break; /* End \P and \p */ + + default: /* All others are not allowed in a class */ + errorcode = ERR7; + ptr--; + goto CLASS_ESCAPE_FAILED; + } + + /* Perl gives a warning unless a following hyphen is the last character + in the class. PCRE throws an error. */ + + if (ptr < ptrend - 1 && *ptr == CHAR_MINUS && + ptr[1] != CHAR_RIGHT_SQUARE_BRACKET) + { + errorcode = ERR50; + goto FAILED; + } + } + + /* Proceed to next thing in the class. */ + + CLASS_CONTINUE: + if (ptr >= ptrend) + { + errorcode = ERR6; /* Missing terminating ']' */ + goto FAILED; + } + GETCHARINCTEST(c, ptr); + if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; + } /* End of class-processing loop */ + + if (class_range_state == RANGE_STARTED) + { + parsed_pattern[-1] = CHAR_MINUS; + class_range_state = RANGE_NO; + } + + *parsed_pattern++ = META_CLASS_END; + break; /* End of character class */ + + + /* ---- Opening parenthesis ---- */ + + case CHAR_LEFT_PARENTHESIS: + if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + /* If ( is not followed by ? it is either a capture or a special verb. */ + + if (*ptr != CHAR_QUESTION_MARK) + { + const char *vn; + + /* Handle capturing brackets (or non-capturing if auto-capture is turned + off). */ + + if (*ptr != CHAR_ASTERISK) + { + nest_depth++; + if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) + { + cb->bracount++; + *parsed_pattern++ = META_CAPTURE | cb->bracount; + } + else *parsed_pattern++ = META_NOCAPTURE; + } + + + /* ---- Handle (*VERB) and (*VERB:NAME) ---- */ + + /* Do nothing for (*) so it gives a "bad quantifier" error rather than + "(*MARK) must have an argument". */ + + else if (ptrend - ptr > 1 && ptr[1] != CHAR_RIGHT_PARENTHESIS) + { + vn = verbnames; + if (!read_name(&ptr, ptrend, 0, &offset, &name, &namelen, &errorcode, + cb)) goto FAILED; + if (ptr >= ptrend || (*ptr != CHAR_COLON && + *ptr != CHAR_RIGHT_PARENTHESIS)) + { + errorcode = ERR60; /* Malformed */ + goto FAILED; + } + + /* Scan the table of verb names */ + + for (i = 0; i < verbcount; i++) + { + if (namelen == verbs[i].len && + PRIV(strncmp_c8)(name, vn, namelen) == 0) + break; + vn += verbs[i].len + 1; + } + + if (i >= verbcount) + { + errorcode = ERR60; /* Verb not recognized */ + goto FAILED; + } + + /* An empty argument is treated as no argument. */ + + if (*ptr == CHAR_COLON && ptr + 1 < ptrend && + ptr[1] == CHAR_RIGHT_PARENTHESIS) + ptr++; /* Advance to the closing parens */ + + /* Check for mandatory non-empty argument; this is (*MARK) */ + + if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON) + { + errorcode = ERR66; + goto FAILED; + } + + /* It appears that Perl allows any characters whatsoever, other than a + closing parenthesis, to appear in arguments ("names"), so we no longer + insist on letters, digits, and underscores. Perl does not, however, do + any interpretation within arguments, and has no means of including a + closing parenthesis. PCRE supports escape processing but only when it + is requested by an option. We set inverbname TRUE here, and let the + main loop take care of this so that escape and \x processing is done by + the main code above. */ + + if (*ptr++ == CHAR_COLON) /* Skip past : or ) */ + { + /* Some optional arguments can be treated as a preceding (*MARK) */ + + if (verbs[i].has_arg < 0) + { + add_after_mark = verbs[i].meta; + *parsed_pattern++ = META_MARK; + } + + /* The remaining verbs with arguments (except *MARK) need a different + opcode. */ + + else + { + *parsed_pattern++ = verbs[i].meta + + ((verbs[i].meta != META_MARK)? 0x00010000u:0); + } + + /* Set up for reading the name in the main loop. */ + + verblengthptr = parsed_pattern++; + verbnamestart = ptr; + inverbname = TRUE; + } + else /* No verb "name" argument */ + { + *parsed_pattern++ = verbs[i].meta; + } + } /* End of (*VERB) handling */ + break; /* Done with this parenthesis */ + } /* End of groups that don't start with (? */ + + + /* ---- Items starting (? ---- */ + + /* The type of item is determined by what follows (?. Handle (?| and option + changes under "default" because both need a new block on the nest stack. + Comments starting with (?# are handled above. Note that there is some + ambiguity about the sequence (?- because if a digit follows it's a relative + recursion or subroutine call whereas otherwise it's an option unsetting. */ + + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + switch(*ptr) + { + default: + if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1])) + goto RECURSION_BYNUMBER; /* The + case is handled by CHAR_PLUS */ + + /* We now have either (?| or a (possibly empty) option setting, + optionally followed by a non-capturing group. */ + + nest_depth++; + if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); + else if (++top_nest >= end_nests) + { + errorcode = ERR84; + goto FAILED; + } + top_nest->nest_depth = nest_depth; + top_nest->flags = 0; + top_nest->options = options & PARSE_TRACKED_OPTIONS; + + /* Start of non-capturing group that resets the capture count for each + branch. */ + + if (*ptr == CHAR_VERTICAL_LINE) + { + top_nest->reset_group = (uint16_t)cb->bracount; + top_nest->max_group = (uint16_t)cb->bracount; + top_nest->flags |= NSF_RESET; + cb->external_flags |= PCRE2_DUPCAPUSED; + *parsed_pattern++ = META_NOCAPTURE; + ptr++; + } + + /* Scan for options imnsxJU to be set or unset. */ + + else + { + BOOL hyphenok = TRUE; + uint32_t oldoptions = options; + + top_nest->reset_group = 0; + top_nest->max_group = 0; + set = unset = 0; + optset = &set; + + /* ^ at the start unsets imnsx and disables the subsequent use of - */ + + if (ptr < ptrend && *ptr == CHAR_CIRCUMFLEX_ACCENT) + { + options &= ~(PCRE2_CASELESS|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| + PCRE2_DOTALL|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE); + hyphenok = FALSE; + ptr++; + } + + while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS && + *ptr != CHAR_COLON) + { + switch (*ptr++) + { + case CHAR_MINUS: + if (!hyphenok) + { + errorcode = ERR94; + ptr--; /* Correct the offset */ + goto FAILED; + } + optset = &unset; + hyphenok = FALSE; + break; + + case CHAR_J: /* Record that it changed in the external options */ + *optset |= PCRE2_DUPNAMES; + cb->external_flags |= PCRE2_JCHANGED; + break; + + case CHAR_i: *optset |= PCRE2_CASELESS; break; + case CHAR_m: *optset |= PCRE2_MULTILINE; break; + case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break; + case CHAR_s: *optset |= PCRE2_DOTALL; break; + case CHAR_U: *optset |= PCRE2_UNGREEDY; break; + + /* If x appears twice it sets the extended extended option. */ + + case CHAR_x: + *optset |= PCRE2_EXTENDED; + if (ptr < ptrend && *ptr == CHAR_x) + { + *optset |= PCRE2_EXTENDED_MORE; + ptr++; + } + break; + + default: + errorcode = ERR11; + ptr--; /* Correct the offset */ + goto FAILED; + } + } + + /* If we are setting extended without extended-more, ensure that any + existing extended-more gets unset. Also, unsetting extended must also + unset extended-more. */ + + if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED || + (unset & PCRE2_EXTENDED) != 0) + unset |= PCRE2_EXTENDED_MORE; + + options = (options | set) & (~unset); + + /* If the options ended with ')' this is not the start of a nested + group with option changes, so the options change at this level. + In this case, if the previous level set up a nest block, discard the + one we have just created. Otherwise adjust it for the previous level. + If the options ended with ':' we are starting a non-capturing group, + possibly with an options setting. */ + + if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + if (*ptr++ == CHAR_RIGHT_PARENTHESIS) + { + nest_depth--; /* This is not a nested group after all. */ + if (top_nest > (nest_save *)(cb->start_workspace) && + (top_nest-1)->nest_depth == nest_depth) top_nest--; + else top_nest->nest_depth = nest_depth; + } + else *parsed_pattern++ = META_NOCAPTURE; + + /* If nothing changed, no need to record. */ + + if (options != oldoptions) + { + *parsed_pattern++ = META_OPTIONS; + *parsed_pattern++ = options; + } + } /* End options processing */ + break; /* End default case after (? */ + + + /* ---- Python syntax support ---- */ + + case CHAR_P: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + /* (?P is the same as (?, which defines a named group. */ + + if (*ptr == CHAR_LESS_THAN_SIGN) + { + terminator = CHAR_GREATER_THAN_SIGN; + goto DEFINE_NAME; + } + + /* (?P>name) is the same as (?&name), which is a recursion or subroutine + call. */ + + if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME; + + /* (?P=name) is the same as \k, a back reference by name. Anything + else after (?P is an error. */ + + if (*ptr != CHAR_EQUALS_SIGN) + { + errorcode = ERR41; + goto FAILED; + } + if (!read_name(&ptr, ptrend, CHAR_RIGHT_PARENTHESIS, &offset, &name, + &namelen, &errorcode, cb)) goto FAILED; + *parsed_pattern++ = META_BACKREF_BYNAME; + *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; /* End of (?P processing */ + + + /* ---- Recursion/subroutine calls by number ---- */ + + case CHAR_R: + i = 0; /* (?R) == (?R0) */ + ptr++; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR58; + goto FAILED; + } + goto SET_RECURSION; + + /* An item starting (?- followed by a digit comes here via the "default" + case because (?- followed by a non-digit is an options setting. */ + + case CHAR_PLUS: + if (ptrend - ptr < 2 || !IS_DIGIT(ptr[1])) + { + errorcode = ERR29; /* Missing number */ + goto FAILED; + } + /* Fall through */ + + case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: + case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: + RECURSION_BYNUMBER: + if (!read_number(&ptr, ptrend, + (IS_DIGIT(*ptr))? -1:(int)(cb->bracount), /* + and - are relative */ + MAX_GROUP_NUMBER, ERR61, + &i, &errorcode)) goto FAILED; + if (i < 0) /* NB (?0) is permitted */ + { + errorcode = ERR15; /* Unknown group */ + goto FAILED_BACK; + } + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + goto UNCLOSED_PARENTHESIS; + + SET_RECURSION: + *parsed_pattern++ = META_RECURSE | (uint32_t)i; + offset = (PCRE2_SIZE)(ptr - cb->start_pattern); + ptr++; + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; /* End of recursive call by number handling */ + + + /* ---- Recursion/subroutine calls by name ---- */ + + case CHAR_AMPERSAND: + RECURSE_BY_NAME: + if (!read_name(&ptr, ptrend, CHAR_RIGHT_PARENTHESIS, &offset, &name, + &namelen, &errorcode, cb)) goto FAILED; + *parsed_pattern++ = META_RECURSE_BYNAME; + *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; + + /* ---- Callout with numerical or string argument ---- */ + + case CHAR_C: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + /* If the previous item was a condition starting (?(? an assertion, + optionally preceded by a callout, is expected. This is checked later on, + during actual compilation. However we need to identify this kind of + assertion in this pass because it must not be qualified. The value of + expect_cond_assert is set to 2 after (?(? is processed. We decrement it + for a callout - still leaving a positive value that identifies the + assertion. Multiple callouts or any other items will make it zero or + less, which doesn't matter because they will cause an error later. */ + + expect_cond_assert = prev_expect_cond_assert - 1; + + /* If previous_callout is not NULL, it means this follows a previous + callout. If it was a manual callout, do nothing; this means its "length + of next pattern item" field will remain zero. If it was an automatic + callout, abolish it. */ + + if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 && + previous_callout == parsed_pattern - 4 && + parsed_pattern[-1] == 255) + parsed_pattern = previous_callout; + + /* Save for updating next pattern item length, and skip one item before + completing. */ + + previous_callout = parsed_pattern; + after_manual_callout = 1; + + /* Handle a string argument; specific delimiter is required. */ + + if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) + { + PCRE2_SIZE calloutlength; + PCRE2_SPTR startptr = ptr; + + delimiter = 0; + for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) + { + if (*ptr == PRIV(callout_start_delims)[i]) + { + delimiter = PRIV(callout_end_delims)[i]; + break; + } + } + if (delimiter == 0) + { + errorcode = ERR82; + goto FAILED; + } + + *parsed_pattern = META_CALLOUT_STRING; + parsed_pattern += 3; /* Skip pattern info */ + + for (;;) + { + if (++ptr >= ptrend) + { + errorcode = ERR81; + ptr = startptr; /* To give a more useful message */ + goto FAILED; + } + if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter)) + break; + } + + calloutlength = (PCRE2_SIZE)(ptr - startptr); + if (calloutlength > UINT32_MAX) + { + errorcode = ERR72; + goto FAILED; + } + *parsed_pattern++ = (uint32_t)calloutlength; + offset = (PCRE2_SIZE)(startptr - cb->start_pattern); + PUTOFFSET(offset, parsed_pattern); + } + + /* Handle a callout with an optional numerical argument, which must be + less than or equal to 255. A missing argument gives 0. */ + + else + { + int n = 0; + *parsed_pattern = META_CALLOUT_NUMBER; /* Numerical callout */ + parsed_pattern += 3; /* Skip pattern info */ + while (ptr < ptrend && IS_DIGIT(*ptr)) + { + n = n * 10 + *ptr++ - CHAR_0; + if (n > 255) + { + errorcode = ERR38; + goto FAILED; + } + } + *parsed_pattern++ = n; + } + + /* Both formats must have a closing parenthesis */ + + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR39; + goto FAILED; + } + ptr++; + + /* Remember the offset to the next item in the pattern, and set a default + length. This should get updated after the next item is read. */ + + previous_callout[1] = (uint32_t)(ptr - cb->start_pattern); + previous_callout[2] = 0; + break; /* End callout */ + + + /* ---- Conditional group ---- */ + + /* A condition can be an assertion, a number (referring to a numbered + group's having been set), a name (referring to a named group), or 'R', + referring to overall recursion. R and R&name are also permitted + for recursion state tests. Numbers may be preceded by + or - to specify a + relative group number. + + There are several syntaxes for testing a named group: (?(name)) is used + by Python; Perl 5.10 onwards uses (?() or (?('name')). + + There are two unfortunate ambiguities. 'R' can be the recursive thing or + the name 'R' (and similarly for 'R' followed by digits). 'DEFINE' can be + the Perl DEFINE feature or the Python named test. We look for a name + first; if not found, we try the other case. + + For compatibility with auto-callouts, we allow a callout to be specified + before a condition that is an assertion. */ + + case CHAR_LEFT_PARENTHESIS: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + nest_depth++; + + /* If the next character is ? there must be an assertion next (optionally + preceded by a callout). We do not check this here, but instead we set + expect_cond_assert to 2. If this is still greater than zero (callouts + decrement it) when the next assertion is read, it will be marked as a + condition that must not be repeated. A value greater than zero also + causes checking that an assertion (possibly with callout) follows. */ + + if (*ptr == CHAR_QUESTION_MARK) + { + *parsed_pattern++ = META_COND_ASSERT; + ptr--; /* Pull pointer back to the opening parenthesis. */ + expect_cond_assert = 2; + break; /* End of conditional */ + } + + /* Handle (?([+-]number)... */ + + if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, + &errorcode)) + { + if (i <= 0) + { + errorcode = ERR15; + goto FAILED; + } + *parsed_pattern++ = META_COND_NUMBER; + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2); + PUTOFFSET(offset, parsed_pattern); + *parsed_pattern++ = i; + } + else if (errorcode != 0) goto FAILED; /* Number too big */ + + /* No number found. Handle the special case (?(VERSION[>]=n.m)... */ + + else if (ptrend - ptr >= 10 && + PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 && + ptr[7] != CHAR_RIGHT_PARENTHESIS) + { + uint32_t ge = 0; + int major = 0; + int minor = 0; + + ptr += 7; + if (*ptr == CHAR_GREATER_THAN_SIGN) + { + ge = 1; + ptr++; + } + + /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT + references its argument twice. */ + + if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) + goto BAD_VERSION_CONDITION; + + if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode)) + goto FAILED; + + if (ptr >= ptrend) goto BAD_VERSION_CONDITION; + if (*ptr == CHAR_DOT) + { + if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION; + minor = (*ptr++ - CHAR_0) * 10; + if (IS_DIGIT(*ptr)) minor += *ptr++ - CHAR_0; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + goto BAD_VERSION_CONDITION; + } + + *parsed_pattern++ = META_COND_VERSION; + *parsed_pattern++ = ge; + *parsed_pattern++ = major; + *parsed_pattern++ = minor; + } + + /* All the remaining cases now require us to read a name. We cannot at + this stage distinguish ambiguous cases such as (?(R12) which might be a + recursion test by number or a name, because the named groups have not yet + all been identified. Those cases are treated as names, but given a + different META code. */ + + else + { + BOOL was_r_ampersand = FALSE; + + if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND) + { + terminator = CHAR_RIGHT_PARENTHESIS; + was_r_ampersand = TRUE; + ptr++; + } + else if (*ptr == CHAR_LESS_THAN_SIGN) + terminator = CHAR_GREATER_THAN_SIGN; + else if (*ptr == CHAR_APOSTROPHE) + terminator = CHAR_APOSTROPHE; + else + { + terminator = CHAR_RIGHT_PARENTHESIS; + ptr--; /* Point to char before name */ + } + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; + + /* Handle (?(R&name) */ + + if (was_r_ampersand) + { + *parsed_pattern = META_COND_RNAME; + ptr--; /* Back to closing parens */ + } + + /* Handle (?(name). If the name is "DEFINE" we identify it with a + special code. Likewise if the name consists of R followed only by + digits. Otherwise, handle it like a quoted name. */ + + else if (terminator == CHAR_RIGHT_PARENTHESIS) + { + if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) + *parsed_pattern = META_COND_DEFINE; + else + { + for (i = 1; i < (int)namelen; i++) + if (!IS_DIGIT(name[i])) break; + *parsed_pattern = (*name == CHAR_R && i >= (int)namelen)? + META_COND_RNUMBER : META_COND_NAME; + } + ptr--; /* Back to closing parens */ + } + + /* Handle (?('name') or (?() */ + + else *parsed_pattern = META_COND_NAME; + + /* All these cases except DEFINE end with the name length and offset; + DEFINE just has an offset (for the "too many branches" error). */ + + if (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + } /* End cases that read a name */ + + /* Check the closing parenthesis of the condition */ + + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR24; + goto FAILED; + } + ptr++; + break; /* End of condition processing */ + + + /* ---- Atomic group ---- */ + + case CHAR_GREATER_THAN_SIGN: + *parsed_pattern++ = META_ATOMIC; + nest_depth++; + ptr++; + break; + + + /* ---- Lookahead assertions ---- */ + + case CHAR_EQUALS_SIGN: + *parsed_pattern++ = META_LOOKAHEAD; + ptr++; + goto POST_ASSERTION; + + case CHAR_EXCLAMATION_MARK: + *parsed_pattern++ = META_LOOKAHEADNOT; + ptr++; + goto POST_ASSERTION; + + + /* ---- Lookbehind assertions ---- */ + + /* (?< followed by = or ! is a lookbehind assertion. Otherwise (?< is the + start of the name of a capturing group. */ + + case CHAR_LESS_THAN_SIGN: + if (ptrend - ptr <= 1 || + (ptr[1] != CHAR_EQUALS_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK)) + { + terminator = CHAR_GREATER_THAN_SIGN; + goto DEFINE_NAME; + } + *parsed_pattern++ = (ptr[1] == CHAR_EQUALS_SIGN)? + META_LOOKBEHIND : META_LOOKBEHINDNOT; + *has_lookbehind = TRUE; + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2); + PUTOFFSET(offset, parsed_pattern); + ptr += 2; + /* Fall through */ + + /* If the previous item was a condition starting (?(? an assertion, + optionally preceded by a callout, is expected. This is checked later on, + during actual compilation. However we need to identify this kind of + assertion in this pass because it must not be qualified. The value of + expect_cond_assert is set to 2 after (?(? is processed. We decrement it + for a callout - still leaving a positive value that identifies the + assertion. Multiple callouts or any other items will make it zero or + less, which doesn't matter because they will cause an error later. */ + + POST_ASSERTION: + nest_depth++; + if (prev_expect_cond_assert > 0) + { + if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); + else if (++top_nest >= end_nests) + { + errorcode = ERR84; + goto FAILED; + } + top_nest->nest_depth = nest_depth; + top_nest->flags = NSF_CONDASSERT; + top_nest->options = options & PARSE_TRACKED_OPTIONS; + } + break; + + + /* ---- Define a named group ---- */ + + /* A named group may be defined as (?'name') or (?). In the latter + case we jump to DEFINE_NAME from the disambiguation of (?< above with the + terminator set to '>'. */ + + case CHAR_APOSTROPHE: + terminator = CHAR_APOSTROPHE; /* Terminator */ + + DEFINE_NAME: + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; + + /* We have a name for this capturing group. It is also assigned a number, + which is its primary means of identification. */ + + cb->bracount++; + *parsed_pattern++ = META_CAPTURE | cb->bracount; + nest_depth++; + + /* Check not too many names */ + + if (cb->names_found >= MAX_NAME_COUNT) + { + errorcode = ERR49; + goto FAILED; + } + + /* Adjust the entry size to accommodate the longest name found. */ + + if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) + cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); + + /* Scan the list to check for duplicates. For duplicate names, if the + number is the same, break the loop, which causes the name to be + discarded; otherwise, if DUPNAMES is not set, give an error. + If it is set, allow the name with a different number, but continue + scanning in case this is a duplicate with the same number. For + non-duplicate names, give an error if the number is duplicated. */ + + isdupname = FALSE; + ng = cb->named_groups; + for (i = 0; i < cb->names_found; i++, ng++) + { + if (namelen == ng->length && + PRIV(strncmp)(name, ng->name, (PCRE2_SIZE)namelen) == 0) + { + if (ng->number == cb->bracount) break; + if ((options & PCRE2_DUPNAMES) == 0) + { + errorcode = ERR43; + goto FAILED; + } + isdupname = ng->isdup = TRUE; /* Mark as a duplicate */ + cb->dupnames = TRUE; /* Duplicate names exist */ + } + else if (ng->number == cb->bracount) + { + errorcode = ERR65; + goto FAILED; + } + } + + if (i < cb->names_found) break; /* Ignore duplicate with same number */ + + /* Increase the list size if necessary */ + + if (cb->names_found >= cb->named_group_list_size) + { + uint32_t newsize = cb->named_group_list_size * 2; + named_group *newspace = + cb->cx->memctl.malloc(newsize * sizeof(named_group), + cb->cx->memctl.memory_data); + if (newspace == NULL) + { + errorcode = ERR21; + goto FAILED; + } + + memcpy(newspace, cb->named_groups, + cb->named_group_list_size * sizeof(named_group)); + if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE) + cb->cx->memctl.free((void *)cb->named_groups, + cb->cx->memctl.memory_data); + cb->named_groups = newspace; + cb->named_group_list_size = newsize; + } + + /* Add this name to the list */ + + cb->named_groups[cb->names_found].name = name; + cb->named_groups[cb->names_found].length = (uint16_t)namelen; + cb->named_groups[cb->names_found].number = cb->bracount; + cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname; + cb->names_found++; + break; + } /* End of (? switch */ + break; /* End of ( handling */ + + + /* ---- Branch terminators ---- */ + + /* Alternation: reset the capture count if we are in a (?| group. */ + + case CHAR_VERTICAL_LINE: + if (top_nest != NULL && top_nest->nest_depth == nest_depth && + (top_nest->flags & NSF_RESET) != 0) + { + if (cb->bracount > top_nest->max_group) + top_nest->max_group = (uint16_t)cb->bracount; + cb->bracount = top_nest->reset_group; + } + *parsed_pattern++ = META_ALT; + break; + + /* End of group; reset the capture count to the maximum if we are in a (?| + group and/or reset the options that are tracked during parsing. Disallow + quantifier for a condition that is an assertion. */ + + case CHAR_RIGHT_PARENTHESIS: + okquantifier = TRUE; + if (top_nest != NULL && top_nest->nest_depth == nest_depth) + { + options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options; + if ((top_nest->flags & NSF_RESET) != 0 && + top_nest->max_group > cb->bracount) + cb->bracount = top_nest->max_group; + if ((top_nest->flags & NSF_CONDASSERT) != 0) + okquantifier = FALSE; + if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; + else top_nest--; + } + if (nest_depth == 0) /* Unmatched closing parenthesis */ + { + errorcode = ERR22; + goto FAILED_BACK; + } + nest_depth--; + *parsed_pattern++ = META_KET; + break; + } /* End of switch on pattern character */ + } /* End of main character scan loop */ + +/* End of pattern reached. Check for missing ) at the end of a verb name. */ + +if (inverbname && ptr >= ptrend) + { + errorcode = ERR60; + goto FAILED; + } + +/* Manage callout for the final item */ + +PARSED_END: +parsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout, + parsed_pattern, cb); + +/* Insert trailing items for word and line matching (features provided for the +benefit of pcre2grep). */ + +if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_LINE) != 0) + { + *parsed_pattern++ = META_KET; + *parsed_pattern++ = META_DOLLAR; + } +else if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_WORD) != 0) + { + *parsed_pattern++ = META_KET; + *parsed_pattern++ = META_ESCAPE + ESC_b; + } + +/* Terminate the parsed pattern, then return success if all groups are closed. +Otherwise we have unclosed parentheses. */ + +if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } + +*parsed_pattern = META_END; +if (nest_depth == 0) return 0; + +UNCLOSED_PARENTHESIS: +errorcode = ERR14; + +/* Come here for all failures. */ + +FAILED: +cb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern); +return errorcode; + +/* Some errors need to indicate the previous character. */ + +FAILED_BACK: +ptr--; +goto FAILED; + +/* This failure happens several times. */ + +BAD_VERSION_CONDITION: +errorcode = ERR79; +goto FAILED; +} + + + +/************************************************* +* Find first significant opcode * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring opcode etc. It skips over things +that do not influence this. For some calls, it makes sense to skip negative +forward and all backward assertions, and also the \b assertion; for others it +does not. + +Arguments: + code pointer to the start of the group + skipassert TRUE if certain assertions are to be skipped + +Returns: pointer to the first significant opcode +*/ + +static const PCRE2_UCHAR* +first_significant_code(PCRE2_SPTR code, BOOL skipassert) +{ +for (;;) + { + switch ((int)*code) + { + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + if (!skipassert) return code; + do code += GET(code, 1); while (*code == OP_ALT); + code += PRIV(OP_lengths)[*code]; + break; + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + if (!skipassert) return code; + /* Fall through */ + + case OP_CALLOUT: + case OP_CREF: + case OP_DNCREF: + case OP_RREF: + case OP_DNRREF: + case OP_FALSE: + case OP_TRUE: + code += PRIV(OP_lengths)[*code]; + break; + + case OP_CALLOUT_STR: + code += GET(code, 1 + 2*LINK_SIZE); + break; + + case OP_SKIPZERO: + code += 2 + GET(code, 2) + LINK_SIZE; + break; + + case OP_COND: + case OP_SCOND: + if (code[1+LINK_SIZE] != OP_FALSE || /* Not DEFINE */ + code[GET(code, 1)] != OP_KET) /* More than one branch */ + return code; + code += GET(code, 1) + 1 + LINK_SIZE; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + +#ifdef SUPPORT_UNICODE +/************************************************* +* Get othercase range * +*************************************************/ + +/* This function is passed the start and end of a class range in UCP mode. It +searches up the characters, looking for ranges of characters in the "other" +case. Each call returns the next one, updating the start address. A character +with multiple other cases is returned on its own with a special return value. + +Arguments: + cptr points to starting character value; updated + d end value + ocptr where to put start of othercase range + odptr where to put end of othercase range + +Yield: -1 when no more + 0 when a range is returned + >0 the CASESET offset for char with multiple other cases + in this case, ocptr contains the original +*/ + +static int +get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr, + uint32_t *odptr) +{ +uint32_t c, othercase, next; +unsigned int co; + +/* Find the first character that has an other case. If it has multiple other +cases, return its case offset value. */ + +for (c = *cptr; c <= d; c++) + { + if ((co = UCD_CASESET(c)) != 0) + { + *ocptr = c++; /* Character that has the set */ + *cptr = c; /* Rest of input range */ + return (int)co; + } + if ((othercase = UCD_OTHERCASE(c)) != c) break; + } + +if (c > d) return -1; /* Reached end of range */ + +/* Found a character that has a single other case. Search for the end of the +range, which is either the end of the input range, or a character that has zero +or more than one other cases. */ + +*ocptr = othercase; +next = othercase + 1; + +for (++c; c <= d; c++) + { + if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; + next++; + } + +*odptr = next - 1; /* End of othercase range */ +*cptr = c; /* Rest of input range */ +return 0; +} +#endif /* SUPPORT_UNICODE */ + + + +/************************************************* +* Add a character or range to a class (internal) * +*************************************************/ + +/* This function packages up the logic of adding a character or range of +characters to a class. The character values in the arguments will be within the +valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is +called only from within the "add to class" group of functions, some of which +are recursive and mutually recursive. The external entry point is +add_to_class(). + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb compile data + start start of range character + end end of range character + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr, + uint32_t options, compile_block *cb, uint32_t start, uint32_t end) +{ +uint32_t c; +uint32_t classbits_end = (end <= 0xff ? end : 0xff); +unsigned int n8 = 0; + +/* If caseless matching is required, scan the range and process alternate +cases. In Unicode, there are 8-bit characters that have alternate cases that +are greater than 255 and vice-versa. Sometimes we can just extend the original +range. */ + +if ((options & PCRE2_CASELESS) != 0) + { +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UTF) != 0) + { + int rc; + uint32_t oc, od; + + options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ + c = start; + + while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) + { + /* Handle a single character that has more than one other case. */ + + if (rc > 0) n8 += add_list_to_class_internal(classbits, uchardptr, options, cb, + PRIV(ucd_caseless_sets) + rc, oc); + + /* Do nothing if the other case range is within the original range. */ + + else if (oc >= cb->class_range_start && od <= cb->class_range_end) continue; + + /* Extend the original range if there is overlap, noting that if oc < c, we + can't have od > end because a subrange is always shorter than the basic + range. Otherwise, use a recursive call to add the additional range. */ + + else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */ + else if (od > end && oc <= end + 1) + { + end = od; /* Extend upwards */ + if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff); + } + else n8 += add_to_class_internal(classbits, uchardptr, options, cb, oc, od); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode */ + + for (c = start; c <= classbits_end; c++) + { + SETBIT(classbits, cb->fcc[c]); + n8++; + } + } + +/* Now handle the originally supplied range. Adjust the final value according +to the bit length - this means that the same lists of (e.g.) horizontal spaces +can be used in all cases. */ + +if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) + end = MAX_NON_UTF_CHAR; + +if (start > cb->class_range_start && end < cb->class_range_end) return n8; + +/* Use the bitmap for characters < 256. Otherwise use extra data.*/ + +for (c = start; c <= classbits_end; c++) + { + /* Regardless of start, c will always be <= 255. */ + SETBIT(classbits, c); + n8++; + } + +#ifdef SUPPORT_WIDE_CHARS +if (start <= 0xff) start = 0xff + 1; + +if (end >= start) + { + PCRE2_UCHAR *uchardata = *uchardptr; + +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UTF) != 0) + { + if (start < end) + { + *uchardata++ = XCL_RANGE; + uchardata += PRIV(ord2utf)(start, uchardata); + uchardata += PRIV(ord2utf)(end, uchardata); + } + else if (start == end) + { + *uchardata++ = XCL_SINGLE; + uchardata += PRIV(ord2utf)(start, uchardata); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Without UTF support, character values are constrained by the bit length, + and can only be > 256 for 16-bit and 32-bit libraries. */ + +#if PCRE2_CODE_UNIT_WIDTH == 8 + {} +#else + if (start < end) + { + *uchardata++ = XCL_RANGE; + *uchardata++ = start; + *uchardata++ = end; + } + else if (start == end) + { + *uchardata++ = XCL_SINGLE; + *uchardata++ = start; + } +#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + *uchardptr = uchardata; /* Updata extra data pointer */ + } +#else /* SUPPORT_WIDE_CHARS */ + (void)uchardptr; /* Avoid compiler warning */ +#endif /* SUPPORT_WIDE_CHARS */ + +return n8; /* Number of 8-bit characters */ +} + + + +#ifdef SUPPORT_UNICODE +/************************************************* +* Add a list of characters to a class (internal) * +*************************************************/ + +/* This function is used for adding a list of case-equivalent characters to a +class when in UTF mode. This function is called only from within +add_to_class_internal(), with which it is mutually recursive. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + except character to omit; this is used when adding lists of + case-equivalent characters to avoid including the one we + already know about + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_list_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr, + uint32_t options, compile_block *cb, const uint32_t *p, unsigned int except) +{ +unsigned int n8 = 0; +while (p[0] < NOTACHAR) + { + unsigned int n = 0; + if (p[0] != except) + { + while(p[n+1] == p[0] + n + 1) n++; + n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]); + } + p += n + 1; + } +return n8; +} +#endif + + + +/************************************************* +* External entry point for add range to class * +*************************************************/ + +/* This function sets the overall range so that the internal functions can try +to avoid duplication when handling case-independence. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb compile data + start start of range character + end end of range character + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, + compile_block *cb, uint32_t start, uint32_t end) +{ +cb->class_range_start = start; +cb->class_range_end = end; +return add_to_class_internal(classbits, uchardptr, options, cb, start, end); +} + + +/************************************************* +* External entry point for add list to class * +*************************************************/ + +/* This function is used for adding a list of horizontal or vertical whitespace +characters to a class. The list must be in order so that ranges of characters +can be detected and handled appropriately. This function sets the overall range +so that the internal functions can try to avoid duplication when handling +case-independence. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + except character to omit; this is used when adding lists of + case-equivalent characters to avoid including the one we + already know about + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, + compile_block *cb, const uint32_t *p, unsigned int except) +{ +unsigned int n8 = 0; +while (p[0] < NOTACHAR) + { + unsigned int n = 0; + if (p[0] != except) + { + while(p[n+1] == p[0] + n + 1) n++; + cb->class_range_start = p[0]; + cb->class_range_end = p[n]; + n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]); + } + p += n + 1; + } +return n8; +} + + + +/************************************************* +* Add characters not in a list to a class * +*************************************************/ + +/* This function is used for adding the complement of a list of horizontal or +vertical whitespace to a class. The list must be in order. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, + uint32_t options, compile_block *cb, const uint32_t *p) +{ +BOOL utf = (options & PCRE2_UTF) != 0; +unsigned int n8 = 0; +if (p[0] > 0) + n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1); +while (p[0] < NOTACHAR) + { + while (p[1] == p[0] + 1) p++; + n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1, + (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1); + p++; + } +return n8; +} + + + +/************************************************* +* Find details of duplicate group names * +*************************************************/ + +/* This is called from compile_branch() when it needs to know the index and +count of duplicates in the names table when processing named backreferences, +either directly, or as conditions. + +Arguments: + name points to the name + length the length of the name + indexptr where to put the index + countptr where to put the count of duplicates + errorcodeptr where to put an error code + cb the compile block + +Returns: TRUE if OK, FALSE if not, error code set +*/ + +static BOOL +find_dupname_details(PCRE2_SPTR name, uint32_t length, int *indexptr, + int *countptr, int *errorcodeptr, compile_block *cb) +{ +uint32_t i, groupnumber; +int count; +PCRE2_UCHAR *slot = cb->name_table; + +/* Find the first entry in the table */ + +for (i = 0; i < cb->names_found; i++) + { + if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) == 0 && + slot[IMM2_SIZE+length] == 0) break; + slot += cb->name_entry_size; + } + +/* This should not occur, because this function is called only when we know we +have duplicate names. Give an internal error. */ + +if (i >= cb->names_found) + { + *errorcodeptr = ERR53; + cb->erroroffset = name - cb->start_pattern; + return FALSE; + } + +/* Record the index and then see how many duplicates there are, updating the +backref map and maximum back reference as we do. */ + +*indexptr = i; +count = 0; + +for (;;) + { + count++; + groupnumber = GET2(slot,0); + cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; + if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; + if (++i >= cb->names_found) break; + slot += cb->name_entry_size; + if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) != 0 || + (slot+IMM2_SIZE)[length] != 0) break; + } + +*countptr = count; +return TRUE; +} + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the parsed pattern, compiling it into the a vector of PCRE2_UCHAR. If +the options are changed during the branch, the pointer is used to change the +external options bits. This function is used during the pre-compile phase when +we are trying to find out the amount of memory needed, as well as during the +real compile phase. The value of lengthptr distinguishes the two phases. + +Arguments: + optionsptr pointer to the option bits + codeptr points to the pointer to the current code point + pptrptr points to the current parsed pattern pointer + errorcodeptr points to error code variable + firstcuptr place to put the first required code unit + firstcuflagsptr place to put the first code unit flags, or a negative number + reqcuptr place to put the last required code unit + reqcuflagsptr place to put the last required code unit flags, or a negative number + bcptr points to current branch chain + cb contains pointers to tables etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: 0 There's been an error, *errorcodeptr is non-zero + +1 Success, this branch must match at least one character + -1 Success, this branch may match an empty string +*/ + +static int +compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, uint32_t **pptrptr, + int *errorcodeptr, uint32_t *firstcuptr, int32_t *firstcuflagsptr, + uint32_t *reqcuptr, int32_t *reqcuflagsptr, branch_chain *bcptr, + compile_block *cb, PCRE2_SIZE *lengthptr) +{ +int bravalue = 0; +int okreturn = -1; +int group_return = 0; +uint32_t repeat_min = 0, repeat_max = 0; /* To please picky compilers */ +uint32_t greedy_default, greedy_non_default; +uint32_t repeat_type, op_type; +uint32_t options = *optionsptr; /* May change dynamically */ +uint32_t firstcu, reqcu; +uint32_t zeroreqcu, zerofirstcu; +uint32_t escape; +uint32_t *pptr = *pptrptr; +uint32_t meta, meta_arg; +int32_t firstcuflags, reqcuflags; +int32_t zeroreqcuflags, zerofirstcuflags; +int32_t req_caseopt, reqvary, tempreqvary; +PCRE2_SIZE offset = 0; +PCRE2_SIZE length_prevgroup = 0; +PCRE2_UCHAR *code = *codeptr; +PCRE2_UCHAR *last_code = code; +PCRE2_UCHAR *orig_code = code; +PCRE2_UCHAR *tempcode; +PCRE2_UCHAR *previous = NULL; +PCRE2_UCHAR op_previous; +BOOL groupsetfirstcu = FALSE; +BOOL matched_char = FALSE; +BOOL previous_matched_char = FALSE; +const uint8_t *cbits = cb->cbits; +uint8_t classbits[32]; + +/* We can fish out the UTF setting once and for all into a BOOL, but we must +not do this for other options (e.g. PCRE2_EXTENDED) because they may change +dynamically as we process the pattern. */ + +#ifdef SUPPORT_UNICODE +BOOL utf = (options & PCRE2_UTF) != 0; +#else /* No UTF support */ +BOOL utf = FALSE; +#endif + +/* Helper variables for OP_XCLASS opcode (for characters > 255). We define +class_uchardata always so that it can be passed to add_to_class() always, +though it will not be used in non-UTF 8-bit cases. This avoids having to supply +alternative calls for the different cases. */ + +PCRE2_UCHAR *class_uchardata; +#ifdef SUPPORT_WIDE_CHARS +BOOL xclass; +PCRE2_UCHAR *class_uchardata_base; +#endif + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE2_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Initialize no first unit, no required unit. REQ_UNSET means "no char +matching encountered yet". It gets changed to REQ_NONE if we hit something that +matches a non-fixed first unit; reqcu just remains unset if we never find one. + +When we hit a repeat whose minimum is zero, we may have to adjust these values +to take the zero repeat into account. This is implemented by setting them to +zerofirstcu and zeroreqcu when such a repeat is encountered. The individual +item types that can be repeated set these backoff variables appropriately. */ + +firstcu = reqcu = zerofirstcu = zeroreqcu = 0; +firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; + +/* The variable req_caseopt contains either the REQ_CASELESS value or zero, +according to the current setting of the caseless flag. The REQ_CASELESS value +leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables +to record the case status of the value. This is used only for ASCII characters. +*/ + +req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; + +/* Switch on next META item until the end of the branch */ + +for (;; pptr++) + { +#ifdef SUPPORT_WIDE_CHARS + BOOL xclass_has_prop; +#endif + BOOL negate_class; + BOOL should_flip_negation; + BOOL match_all_or_no_wide_chars; + BOOL possessive_quantifier; + BOOL note_group_empty; + int class_has_8bitchar; + int i; + uint32_t mclength; + uint32_t skipunits; + uint32_t subreqcu, subfirstcu; + uint32_t groupnumber; + uint32_t verbarglen, verbculen; + int32_t subreqcuflags, subfirstcuflags; /* Must be signed */ + open_capitem *oc; + PCRE2_UCHAR mcbuffer[8]; + + /* Get next META item in the pattern and its potential argument. */ + + meta = META_CODE(*pptr); + meta_arg = META_DATA(*pptr); + + /* If we are in the pre-compile phase, accumulate the length used for the + previous cycle of this loop, unless the next item is a quantifier. */ + + if (lengthptr != NULL) + { + if (code > cb->start_workspace + cb->workspace_size - + WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ + { + *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? + ERR52 : ERR86; + return 0; + } + + /* There is at least one situation where code goes backwards: this is the + case of a zero quantifier after a class (e.g. [ab]{0}). When the quantifier + is processed, the whole class is eliminated. However, it is created first, + so we have to allow memory for it. Therefore, don't ever reduce the length + at this point. */ + + if (code < last_code) code = last_code; + + /* If the next thing is not a quantifier, we add the length of the previous + item into the total, and reset the code pointer to the start of the + workspace. Otherwise leave the previous item available to be quantified. */ + + if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) + { + if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code)) + { + *errorcodeptr = ERR20; /* Integer overflow */ + return 0; + } + *lengthptr += (PCRE2_SIZE)(code - orig_code); + if (*lengthptr > MAX_PATTERN_SIZE) + { + *errorcodeptr = ERR20; /* Pattern is too large */ + return 0; + } + code = orig_code; + } + + /* Remember where this code item starts so we can catch the "backwards" + case above next time round. */ + + last_code = code; + } + + /* Process the next parsed pattern item. If it is not a quantifier, remember + where it starts so that it can be quantified when a quantifier follows. + Checking for the legality of quantifiers happens in parse_regex(), except for + a quantifier after an assertion that is a condition. */ + + if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) + { + previous = code; + if (matched_char) okreturn = 1; + } + + previous_matched_char = matched_char; + matched_char = FALSE; + note_group_empty = FALSE; + skipunits = 0; /* Default value for most subgroups */ + + switch(meta) + { + /* ===================================================================*/ + /* The branch terminates at pattern end or | or ) */ + + case META_END: + case META_ALT: + case META_KET: + *firstcuptr = firstcu; + *firstcuflagsptr = firstcuflags; + *reqcuptr = reqcu; + *reqcuflagsptr = reqcuflags; + *codeptr = code; + *pptrptr = pptr; + return okreturn; + + + /* ===================================================================*/ + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case META_CIRCUMFLEX: + if ((options & PCRE2_MULTILINE) != 0) + { + if (firstcuflags == REQ_UNSET) + zerofirstcuflags = firstcuflags = REQ_NONE; + *code++ = OP_CIRCM; + } + else *code++ = OP_CIRC; + break; + + case META_DOLLAR: + *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqcu doesn't change either. */ + + case META_DOT: + matched_char = TRUE; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY; + break; + + + /* ===================================================================*/ + /* Empty character classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set. + Otherwise, an initial ']' is taken as a data character. When empty classes + are allowed, [] must always fail, so generate OP_FAIL, whereas [^] must + match any character, so generate OP_ALLANY. */ + + case META_CLASS_EMPTY: + case META_CLASS_EMPTY_NOT: + matched_char = TRUE; + *code++ = (meta == META_CLASS_EMPTY_NOT)? OP_ALLANY : OP_FAIL; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + break; + + + /* ===================================================================*/ + /* Non-empty character class. If the included characters are all < 256, we + build a 32-byte bitmap of the permitted characters, except in the special + case where there is only one such character. For negated classes, we build + the map as usual, then invert it at the end. However, we use a different + opcode so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag code unit + tells whether the bitmap is present, and whether this is a negated class or + not. */ + + case META_CLASS_NOT: + case META_CLASS: + matched_char = TRUE; + negate_class = meta == META_CLASS_NOT; + + /* We can optimize the case of a single character in a class by generating + OP_CHAR or OP_CHARI if it's positive, or OP_NOT or OP_NOTI if it's + negative. In the negative case there can be no first char if this item is + first, whatever repeat count may follow. In the case of reqcu, save the + previous value for reinstating. */ + + /* NOTE: at present this optimization is not effective if the only + character in a class in 32-bit, non-UCP mode has its top bit set. */ + + if (pptr[1] < META_END && pptr[2] == META_CLASS_END) + { +#ifdef SUPPORT_UNICODE + uint32_t d; +#endif + uint32_t c = pptr[1]; + + pptr += 2; /* Move on to class end */ + if (meta == META_CLASS) /* A positive one-char class can be */ + { /* handled as a normal literal character. */ + meta = c; /* Set up the character */ + goto NORMAL_CHAR_SET; + } + + /* Handle a negative one-character class */ + + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + + /* For caseless UTF mode, check whether this character has more than + one other case. If so, generate a special OP_NOTPROP item instead of + OP_NOTI. */ + +#ifdef SUPPORT_UNICODE + if (utf && (options & PCRE2_CASELESS) != 0 && + (d = UCD_CASESET(c)) != 0) + { + *code++ = OP_NOTPROP; + *code++ = PT_CLIST; + *code++ = d; + break; /* We are finished with this class */ + } +#endif + /* Char has only one other case, or UCP not available */ + + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT; + code += PUTCHAR(c, code); + break; /* We are finished with this class */ + } /* End of 1-char optimization */ + + /* Handle character classes that contain more than just one literal + character. */ + + /* If a non-extended class contains a negative special such as \S, we need + to flip the negation flag at the end, so that support for characters > 255 + works correctly (they are all included in the class). An extended class may + need to insert specific matching or non-matching code for wide characters. + */ + + should_flip_negation = match_all_or_no_wide_chars = FALSE; + + /* Extended class (xclass) will be used when characters > 255 + might match. */ + +#ifdef SUPPORT_WIDE_CHARS + xclass = FALSE; + class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ + class_uchardata_base = class_uchardata; /* Save the start */ +#endif + + /* For optimization purposes, we track some properties of the class: + class_has_8bitchar will be non-zero if the class contains at least one + character with a code point less than 256; xclass_has_prop will be TRUE if + Unicode property checks are present in the class. */ + + class_has_8bitchar = 0; +#ifdef SUPPORT_WIDE_CHARS + xclass_has_prop = FALSE; +#endif + + /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map + in a temporary bit of memory, in case the class contains fewer than two + 8-bit characters because in that case the compiled code doesn't use the bit + map. */ + + memset(classbits, 0, 32 * sizeof(uint8_t)); + + /* Process items until META_CLASS_END is reached. */ + + while ((meta = *(++pptr)) != META_CLASS_END) + { + /* Handle POSIX classes such as [:alpha:] etc. */ + + if (meta == META_POSIX || meta == META_POSIX_NEG) + { + BOOL local_negate = (meta == META_POSIX_NEG); + int posix_class = *(++pptr); + int taboffset, tabopt; + uint8_t pbits[32]; + + should_flip_negation = local_negate; /* Note negative special */ + + /* If matching is caseless, upper and lower are converted to alpha. + This relies on the fact that the class table starts with alpha, + lower, upper as the first 3 entries. */ + + if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; + + /* When PCRE2_UCP is set, some of the POSIX classes are converted to + different escape sequences that use Unicode properties \p or \P. + Others that are not available via \p or \P have to generate + XCL_PROP/XCL_NOTPROP directly, which is done here. */ + +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UCP) != 0) switch(posix_class) + { + case PC_GRAPH: + case PC_PRINT: + case PC_PUNCT: + *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP; + *class_uchardata++ = (PCRE2_UCHAR) + ((posix_class == PC_GRAPH)? PT_PXGRAPH : + (posix_class == PC_PRINT)? PT_PXPRINT : PT_PXPUNCT); + *class_uchardata++ = 0; + xclass_has_prop = TRUE; + goto CONTINUE_CLASS; + + /* For the other POSIX classes (ascii, xdigit) we are going to + fall through to the non-UCP case and build a bit map for + characters with code points less than 256. However, if we are in + a negated POSIX class, characters with code points greater than + 255 must either all match or all not match, depending on whether + the whole class is not or is negated. For example, for + [[:^ascii:]... they must all match, whereas for [^[:^xdigit:]... + they must not. + + In the special case where there are no xclass items, this is + automatically handled by the use of OP_CLASS or OP_NCLASS, but an + explicit range is needed for OP_XCLASS. Setting a flag here + causes the range to be generated later when it is known that + OP_XCLASS is required. In the 8-bit library this is relevant only in + utf mode, since no wide characters can exist otherwise. */ + + default: +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (utf) +#endif + match_all_or_no_wide_chars |= local_negate; + break; + } +#endif /* SUPPORT_UNICODE */ + + /* In the non-UCP case, or when UCP makes no difference, we build the + bit map for the POSIX class in a chunk of local store because we may + be adding and subtracting from it, and we don't want to subtract bits + that may be in the main map already. At the end we or the result into + the bit map that is being built. */ + + posix_class *= 3; + + /* Copy in the first table (always present) */ + + memcpy(pbits, cbits + posix_class_maps[posix_class], + 32 * sizeof(uint8_t)); + + /* If there is a second table, add or remove it as required. */ + + taboffset = posix_class_maps[posix_class + 1]; + tabopt = posix_class_maps[posix_class + 2]; + + if (taboffset >= 0) + { + if (tabopt >= 0) + for (i = 0; i < 32; i++) pbits[i] |= cbits[(int)i + taboffset]; + else + for (i = 0; i < 32; i++) pbits[i] &= ~cbits[(int)i + taboffset]; + } + + /* Now see if we need to remove any special characters. An option + value of 1 removes vertical space and 2 removes underscore. */ + + if (tabopt < 0) tabopt = -tabopt; + if (tabopt == 1) pbits[1] &= ~0x3c; + else if (tabopt == 2) pbits[11] &= 0x7f; + + /* Add the POSIX table or its complement into the main table that is + being built and we are done. */ + + if (local_negate) + for (i = 0; i < 32; i++) classbits[i] |= ~pbits[i]; + else + for (i = 0; i < 32; i++) classbits[i] |= pbits[i]; + + /* Every class contains at least one < 256 character. */ + + class_has_8bitchar = 1; + goto CONTINUE_CLASS; /* End of POSIX handling */ + } + + /* Other than POSIX classes, the only items we should encounter are + \d-type escapes and literal characters (possibly as ranges). */ + + if (meta == META_BIGVALUE) + { + meta = *(++pptr); + goto CLASS_LITERAL; + } + + /* Any other non-literal must be an escape */ + + if (meta >= META_END) + { + if (META_CODE(meta) != META_ESCAPE) + { +#ifdef DEBUG_SHOW_PARSED + fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x " + "in character class\n", meta); +#endif + *errorcodeptr = ERR89; /* Internal error - unrecognized. */ + return 0; + } + escape = META_DATA(meta); + + /* Every class contains at least one < 256 character. */ + + class_has_8bitchar++; + + switch(escape) + { + case ESC_d: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit]; + break; + + case ESC_D: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_digit]; + break; + + case ESC_w: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word]; + break; + + case ESC_W: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_word]; + break; + + /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl + 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was + previously set by something earlier in the character class. + Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so + we could just adjust the appropriate bit. From PCRE 8.34 we no + longer treat \s and \S specially. */ + + case ESC_s: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space]; + break; + + case ESC_S: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_space]; + break; + + /* When adding the horizontal or vertical space lists to a class, or + their complements, disable PCRE2_CASELESS, because it justs wastes + time, and in the "not-x" UTF cases can create unwanted duplicates in + the XCLASS list (provoked by characters that have more than one other + case and by both cases being in the same "not-x" sublist). */ + + case ESC_h: + (void)add_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(hspace_list), NOTACHAR); + break; + + case ESC_H: + (void)add_not_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(hspace_list)); + break; + + case ESC_v: + (void)add_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(vspace_list), NOTACHAR); + break; + + case ESC_V: + (void)add_not_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(vspace_list)); + break; + + /* If Unicode is not supported, \P and \p are not allowed and are + faulted at parse time, so will never appear here. */ + +#ifdef SUPPORT_UNICODE + case ESC_p: + case ESC_P: + { + uint32_t ptype = *(++pptr) >> 16; + uint32_t pdata = *pptr & 0xffff; + *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP; + *class_uchardata++ = ptype; + *class_uchardata++ = pdata; + xclass_has_prop = TRUE; + class_has_8bitchar--; /* Undo! */ + } + break; +#endif + } + + goto CONTINUE_CLASS; + } /* End handling \d-type escapes */ + + /* A literal character may be followed by a range meta. At parse time + there are checks for out-of-order characters, for ranges where the two + characters are equal, and for hyphens that cannot indicate a range. At + this point, therefore, no checking is needed. */ + + else + { + uint32_t c, d; + + CLASS_LITERAL: + c = d = meta; + + /* Remember if \r or \n were explicitly used */ + + if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; + + /* Process a character range */ + + if (pptr[1] == META_RANGE_LITERAL || pptr[1] == META_RANGE_ESCAPED) + { +#ifdef EBCDIC + BOOL range_is_literal = (pptr[1] == META_RANGE_LITERAL); +#endif + pptr += 2; + d = *pptr; + if (d == META_BIGVALUE) d = *(++pptr); + + /* Remember an explicit \r or \n, and add the range to the class. */ + + if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; + + /* In an EBCDIC environment, Perl treats alphabetic ranges specially + because there are holes in the encoding, and simply using the range + A-Z (for example) would include the characters in the holes. This + applies only to literal ranges; [\xC1-\xE9] is different to [A-Z]. */ + +#ifdef EBCDIC + if (range_is_literal && + (cb->ctypes[c] & ctype_letter) != 0 && + (cb->ctypes[d] & ctype_letter) != 0 && + (d <= CHAR_z) == (d <= CHAR_z)) + { + uint32_t uc = (d <= CHAR_z)? 0 : 64; + uint32_t C = d - uc; + uint32_t D = d - uc; + + if (C <= CHAR_i) + { + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + ((D < CHAR_i)? D : CHAR_i) + uc); + C = CHAR_j; + } + + if (C <= D && C <= CHAR_r) + { + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + ((D < CHAR_r)? D : CHAR_r) + uc); + C = CHAR_s; + } + + if (C <= D) + { + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, C + uc, + D + uc); + } + } + else +#endif + /* Not an EBCDIC special range */ + + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, c, d); + goto CONTINUE_CLASS; /* Go get the next char in the class */ + } /* End of range handling */ + + + /* Handle a single character. */ + + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, meta, meta); + } + + /* Continue to the next item in the class. */ + + CONTINUE_CLASS: + +#ifdef SUPPORT_WIDE_CHARS + /* If any wide characters or Unicode properties have been encountered, + set xclass = TRUE. Then, in the pre-compile phase, accumulate the length + of the extra data and reset the pointer. This is so that very large + classes that contain a zillion wide characters or Unicode property tests + do not overwrite the workspace (which is on the stack). */ + + if (class_uchardata > class_uchardata_base) + { + xclass = TRUE; + if (lengthptr != NULL) + { + *lengthptr += class_uchardata - class_uchardata_base; + class_uchardata = class_uchardata_base; + } + } +#endif + + continue; /* Needed to avoid error when not supporting wide chars */ + } /* End of main class-processing loop */ + + /* If this class is the first thing in the branch, there can be no first + char setting, whatever the repeat count. Any reqcu setting must remain + unchanged after any kind of repeat. */ + + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + + /* If there are characters with values > 255, or Unicode property settings + (\p or \P), we have to compile an extended class, with its own opcode, + unless there were no property settings and there was a negated special such + as \S in the class, and PCRE2_UCP is not set, because in that case all + characters > 255 are in or not in the class, so any that were explicitly + given as well can be ignored. + + In the UCP case, if certain negated POSIX classes ([:^ascii:] or + [^:xdigit:]) were present in a class, we either have to match or not match + all wide characters (depending on whether the whole class is or is not + negated). This requirement is indicated by match_all_or_no_wide_chars being + true. We do this by including an explicit range, which works in both cases. + This applies only in UTF and 16-bit and 32-bit non-UTF modes, since there + cannot be any wide characters in 8-bit non-UTF mode. + + When there *are* properties in a positive UTF-8 or any 16-bit or 32_bit + class where \S etc is present without PCRE2_UCP, causing an extended class + to be compiled, we make sure that all characters > 255 are included by + forcing match_all_or_no_wide_chars to be true. + + If, when generating an xclass, there are no characters < 256, we can omit + the bitmap in the actual compiled code. */ + +#ifdef SUPPORT_WIDE_CHARS /* Defined for 16/32 bits, or 8-bit with Unicode */ + if (xclass && ( +#ifdef SUPPORT_UNICODE + (options & PCRE2_UCP) != 0 || +#endif + xclass_has_prop || !should_flip_negation)) + { + if (match_all_or_no_wide_chars || ( +#if PCRE2_CODE_UNIT_WIDTH == 8 + utf && +#endif + should_flip_negation && !negate_class && (options & PCRE2_UCP) == 0)) + { + *class_uchardata++ = XCL_RANGE; + if (utf) /* Will always be utf in the 8-bit library */ + { + class_uchardata += PRIV(ord2utf)(0x100, class_uchardata); + class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata); + } + else /* Can only happen for the 16-bit & 32-bit libraries */ + { +#if PCRE2_CODE_UNIT_WIDTH == 16 + *class_uchardata++ = 0x100; + *class_uchardata++ = 0xffffu; +#elif PCRE2_CODE_UNIT_WIDTH == 32 + *class_uchardata++ = 0x100; + *class_uchardata++ = 0xffffffffu; +#endif + } + } + *class_uchardata++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negate_class? XCL_NOT:0; + if (xclass_has_prop) *code |= XCL_HASPROP; + + /* If the map is required, move up the extra data to make room for it; + otherwise just move the code pointer to the end of the extra data. */ + + if (class_has_8bitchar > 0) + { + *code++ |= XCL_MAP; + (void)memmove(code + (32 / sizeof(PCRE2_UCHAR)), code, + CU2BYTES(class_uchardata - code)); + if (negate_class && !xclass_has_prop) + for (i = 0; i < 32; i++) classbits[i] = ~classbits[i]; + memcpy(code, classbits, 32); + code = class_uchardata + (32 / sizeof(PCRE2_UCHAR)); + } + else code = class_uchardata; + + /* Now fill in the complete length of the item */ + + PUT(previous, 1, (int)(code - previous)); + break; /* End of class handling */ + } +#endif /* SUPPORT_WIDE_CHARS */ + + /* If there are no characters > 255, or they are all to be included or + excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the + whole class was negated and whether there were negative specials such as \S + (non-UCP) in the class. Then copy the 32-byte map into the code vector, + negating it if necessary. */ + + *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; + if (lengthptr == NULL) /* Save time in the pre-compile phase */ + { + if (negate_class) + for (i = 0; i < 32; i++) classbits[i] = ~classbits[i]; + memcpy(code, classbits, 32); + } + code += 32 / sizeof(PCRE2_UCHAR); + break; /* End of class processing */ + + + /* ===================================================================*/ + /* Deal with (*VERB)s. */ + + /* Check for open captures before ACCEPT and close those that are within + the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an + assertion. In the first pass, just accumulate the length required; + otherwise hitting (*ACCEPT) inside many nested parentheses can cause + workspace overflow. Do not set firstcu after *ACCEPT. */ + + case META_ACCEPT: + cb->had_accept = TRUE; + for (oc = cb->open_caps; + oc != NULL && oc->assert_depth >= cb->assert_depth; + oc = oc->next) + { + if (lengthptr != NULL) + { + *lengthptr += CU2BYTES(1) + IMM2_SIZE; + } + else + { + *code++ = OP_CLOSE; + PUT2INC(code, 0, oc->number); + } + } + *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + break; + + case META_PRUNE: + case META_SKIP: + cb->had_pruneorskip = TRUE; + /* Fall through */ + case META_COMMIT: + case META_FAIL: + *code++ = verbops[(meta - META_MARK) >> 16]; + break; + + case META_THEN: + cb->external_flags |= PCRE2_HASTHEN; + *code++ = OP_THEN; + break; + + /* Handle verbs with arguments. Arguments can be very long, especially in + 16- and 32-bit modes, and can overflow the workspace in the first pass. + However, the argument length is constrained to be small enough to fit in + one code unit. This check happens in parse_regex(). In the first pass, + instead of putting the argument into memory, we just update the length + counter and set up an empty argument. */ + + case META_THEN_ARG: + cb->external_flags |= PCRE2_HASTHEN; + goto VERB_ARG; + + case META_PRUNE_ARG: + case META_SKIP_ARG: + cb->had_pruneorskip = TRUE; + /* Fall through */ + case META_MARK: + case META_COMMIT_ARG: + VERB_ARG: + *code++ = verbops[(meta - META_MARK) >> 16]; + /* The length is in characters. */ + verbarglen = *(++pptr); + verbculen = 0; + tempcode = code++; + for (i = 0; i < (int)verbarglen; i++) + { + meta = *(++pptr); +#ifdef SUPPORT_UNICODE + if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else +#endif + { + mclength = 1; + mcbuffer[0] = meta; + } + if (lengthptr != NULL) *lengthptr += mclength; else + { + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + verbculen += mclength; + } + } + + *tempcode = verbculen; /* Fill in the code unit length */ + *code++ = 0; /* Terminating zero */ + break; + + + /* ===================================================================*/ + /* Handle options change. The new setting must be passed back for use in + subsequent branches. Reset the greedy defaults and the case value for + firstcu and reqcu. */ + + case META_OPTIONS: + *optionsptr = options = *(++pptr); + greedy_default = ((options & PCRE2_UNGREEDY) != 0); + greedy_non_default = greedy_default ^ 1; + req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0; + break; + + + /* ===================================================================*/ + /* Handle conditional subpatterns. The case of (?(Rdigits) is ambiguous + because it could be a numerical check on recursion, or a name check on a + group's being set. The pre-pass sets up META_COND_RNUMBER as a name so that + we can handle it either way. We first try for a name; if not found, process + the number. */ + + case META_COND_RNUMBER: /* (?(Rdigits) */ + case META_COND_NAME: /* (?(name) or (?'name') or ?() */ + case META_COND_RNAME: /* (?(R&name) - test for recursion */ + bravalue = OP_COND; + { + int count, index; + PCRE2_SPTR name; + named_group *ng = cb->named_groups; + uint32_t length = *(++pptr); + + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; + + /* In the first pass, the names generated in the pre-pass are available, + but the main name table has not yet been created. Scan the list of names + generated in the pre-pass in order to get a number and whether or not + this name is duplicated. If it is not duplicated, we can handle it as a + numerical group. */ + + for (i = 0; i < cb->names_found; i++, ng++) + { + if (length == ng->length && + PRIV(strncmp)(name, ng->name, length) == 0) + { + if (!ng->isdup) + { + code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; + PUT2(code, 2+LINK_SIZE, ng->number); + if (ng->number > cb->top_backref) cb->top_backref = ng->number; + skipunits = 1+IMM2_SIZE; + goto GROUP_PROCESS_NOTE_EMPTY; + } + break; /* Found a duplicated name */ + } + } + + /* If the name was not found we have a bad reference, unless we are + dealing with R, which is treated as a recursion test by number. + */ + + if (i >= cb->names_found) + { + groupnumber = 0; + if (meta == META_COND_RNUMBER) + { + for (i = 1; i < (int)length; i++) + { + groupnumber = groupnumber * 10 + name[i] - CHAR_0; + if (groupnumber > MAX_GROUP_NUMBER) + { + *errorcodeptr = ERR61; + cb->erroroffset = offset + i; + return 0; + } + } + } + + if (meta != META_COND_RNUMBER || groupnumber > cb->bracount) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; + } + + /* (?Rdigits) treated as a recursion reference by number. A value of + zero (which is the result of both (?R) and (?R0)) means "any", and is + translated into RREF_ANY (which is 0xffff). */ + + if (groupnumber == 0) groupnumber = RREF_ANY; + code[1+LINK_SIZE] = OP_RREF; + PUT2(code, 2+LINK_SIZE, groupnumber); + skipunits = 1+IMM2_SIZE; + goto GROUP_PROCESS_NOTE_EMPTY; + } + + /* A duplicated name was found. Note that if an R name is found + (META_COND_RNUMBER), it is a reference test, not a recursion test. */ + + code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; + + /* We have a duplicated name. In the compile pass we have to search the + main table in order to get the index and count values. */ + + count = 0; /* Values for first pass (avoids compiler warning) */ + index = 0; + if (lengthptr == NULL && !find_dupname_details(name, length, &index, + &count, errorcodeptr, cb)) return 0; + + /* Add one to the opcode to change CREF/RREF into DNCREF/DNRREF and + insert appropriate data values. */ + + code[1+LINK_SIZE]++; + skipunits = 1+2*IMM2_SIZE; + PUT2(code, 2+LINK_SIZE, index); + PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count); + } + goto GROUP_PROCESS_NOTE_EMPTY; + + /* The DEFINE condition is always false. It's internal groups may never + be called, so matched_char must remain false, hence the jump to + GROUP_PROCESS rather than GROUP_PROCESS_NOTE_EMPTY. */ + + case META_COND_DEFINE: + bravalue = OP_COND; + GETPLUSOFFSET(offset, pptr); + code[1+LINK_SIZE] = OP_DEFINE; + skipunits = 1; + goto GROUP_PROCESS; + + /* Conditional test of a group's being set. */ + + case META_COND_NUMBER: + bravalue = OP_COND; + GETPLUSOFFSET(offset, pptr); + groupnumber = *(++pptr); + if (groupnumber > cb->bracount) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; + } + if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; + offset -= 2; /* Point at initial ( for too many branches error */ + code[1+LINK_SIZE] = OP_CREF; + skipunits = 1+IMM2_SIZE; + PUT2(code, 2+LINK_SIZE, groupnumber); + goto GROUP_PROCESS_NOTE_EMPTY; + + /* Test for the PCRE2 version. */ + + case META_COND_VERSION: + bravalue = OP_COND; + if (pptr[1] > 0) + code[1+LINK_SIZE] = ((PCRE2_MAJOR > pptr[2]) || + (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR >= pptr[3]))? + OP_TRUE : OP_FALSE; + else + code[1+LINK_SIZE] = (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR == pptr[3])? + OP_TRUE : OP_FALSE; + skipunits = 1; + pptr += 3; + goto GROUP_PROCESS_NOTE_EMPTY; + + /* The condition is an assertion, possibly preceded by a callout. */ + + case META_COND_ASSERT: + bravalue = OP_COND; + goto GROUP_PROCESS_NOTE_EMPTY; + + + /* ===================================================================*/ + /* Handle all kinds of nested bracketed groups. The non-capturing, + non-conditional cases are here; others come to GROUP_PROCESS via goto. */ + + case META_LOOKAHEAD: + bravalue = OP_ASSERT; + cb->assert_depth += 1; + goto GROUP_PROCESS; + + /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird + thing to do, but Perl allows all assertions to be quantified, and when + they contain capturing parentheses there may be a potential use for + this feature. Not that that applies to a quantified (?!) but we allow + it for uniformity. */ + + case META_LOOKAHEADNOT: + if (pptr[1] == META_KET && + (pptr[2] < META_ASTERISK || pptr[2] > META_MINMAX_QUERY)) + { + *code++ = OP_FAIL; + pptr++; + } + else + { + bravalue = OP_ASSERT_NOT; + cb->assert_depth += 1; + goto GROUP_PROCESS; + } + break; + + case META_LOOKBEHIND: + bravalue = OP_ASSERTBACK; + cb->assert_depth += 1; + goto GROUP_PROCESS; + + case META_LOOKBEHINDNOT: + bravalue = OP_ASSERTBACK_NOT; + cb->assert_depth += 1; + goto GROUP_PROCESS; + + case META_ATOMIC: + bravalue = OP_ONCE; + goto GROUP_PROCESS_NOTE_EMPTY; + + case META_NOCAPTURE: + bravalue = OP_BRA; + /* Fall through */ + + /* Process nested bracketed regex. The nesting depth is maintained for the + benefit of the stackguard function. The test for too deep nesting is now + done in parse_regex(). Assertion and DEFINE groups come to GROUP_PROCESS; + others come to GROUP_PROCESS_NOTE_EMPTY, to indicate that we need to take + note of whether or not they may match an empty string. */ + + GROUP_PROCESS_NOTE_EMPTY: + note_group_empty = TRUE; + + GROUP_PROCESS: + cb->parens_depth += 1; + *code = bravalue; + pptr++; + tempcode = code; + tempreqvary = cb->req_varyopt; /* Save value before group */ + length_prevgroup = 0; /* Initialize for pre-compile phase */ + + if ((group_return = + compile_regex( + options, /* The option state */ + &tempcode, /* Where to put code (updated) */ + &pptr, /* Input pointer (updated) */ + errorcodeptr, /* Where to put an error message */ + skipunits, /* Skip over bracket number */ + &subfirstcu, /* For possible first char */ + &subfirstcuflags, + &subreqcu, /* For possible last char */ + &subreqcuflags, + bcptr, /* Current branch chain */ + cb, /* Compile data block */ + (lengthptr == NULL)? NULL : /* Actual compile phase */ + &length_prevgroup /* Pre-compile phase */ + )) == 0) + return 0; /* Error */ + + cb->parens_depth -= 1; + + /* If that was a non-conditional significant group (not an assertion, not a + DEFINE) that matches at least one character, then the current item matches + a character. Conditionals are handled below. */ + + if (note_group_empty && bravalue != OP_COND && group_return > 0) + matched_char = TRUE; + + /* If we've just compiled an assertion, pop the assert depth. */ + + if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) + cb->assert_depth -= 1; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group. + The parsed pattern pointer (pptr) is on the closing META_KET. + + If this is a conditional bracket, check that there are no more than + two branches in the group, or just one if it's a DEFINE group. We do this + in the real compile phase, not in the pre-pass, where the whole group may + not be available. */ + + if (bravalue == OP_COND && lengthptr == NULL) + { + PCRE2_UCHAR *tc = code; + int condcount = 0; + + do { + condcount++; + tc += GET(tc,1); + } + while (*tc != OP_KET); + + /* A DEFINE group is never obeyed inline (the "condition" is always + false). It must have only one branch. Having checked this, change the + opcode to OP_FALSE. */ + + if (code[LINK_SIZE+1] == OP_DEFINE) + { + if (condcount > 1) + { + cb->erroroffset = offset; + *errorcodeptr = ERR54; + return 0; + } + code[LINK_SIZE+1] = OP_FALSE; + bravalue = OP_DEFINE; /* A flag to suppress char handling below */ + } + + /* A "normal" conditional group. If there is just one branch, we must not + make use of its firstcu or reqcu, because this is equivalent to an + empty second branch. Also, it may match an empty string. If there are two + branches, this item must match a character if the group must. */ + + else + { + if (condcount > 2) + { + cb->erroroffset = offset; + *errorcodeptr = ERR27; + return 0; + } + if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE; + else if (group_return > 0) matched_char = TRUE; + } + } + + /* In the pre-compile phase, update the length by the length of the group, + less the brackets at either end. Then reduce the compiled code to just a + set of non-capturing brackets so that it doesn't use much memory if it is + duplicated by a quantifier.*/ + + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; + code++; /* This already contains bravalue */ + PUTINC(code, 0, 1 + LINK_SIZE); + *code++ = OP_KET; + PUTINC(code, 0, 1 + LINK_SIZE); + break; /* No need to waste time with special character handling */ + } + + /* Otherwise update the main code pointer to the end of the group. */ + + code = tempcode; + + /* For a DEFINE group, required and first character settings are not + relevant. */ + + if (bravalue == OP_DEFINE) break; + + /* Handle updating of the required and first code units for other types of + group. Update for normal brackets of all kinds, and conditions with two + branches (see code above). If the bracket is followed by a quantifier with + zero repeat, we have to back off. Hence the definition of zeroreqcu and + zerofirstcu outside the main loop so that they can be accessed for the back + off. */ + + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + groupsetfirstcu = FALSE; + + if (bravalue >= OP_ONCE) /* Not an assertion */ + { + /* If we have not yet set a firstcu in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqcu if necessary. If the subpattern has + no firstcu, set "none" for the whole branch. In both cases, a zero + repeat forces firstcu to "none". */ + + if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET) + { + if (subfirstcuflags >= 0) + { + firstcu = subfirstcu; + firstcuflags = subfirstcuflags; + groupsetfirstcu = TRUE; + } + else firstcuflags = REQ_NONE; + zerofirstcuflags = REQ_NONE; + } + + /* If firstcu was previously set, convert the subpattern's firstcu + into reqcu if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subfirstcuflags >= 0 && subreqcuflags < 0) + { + subreqcu = subfirstcu; + subreqcuflags = subfirstcuflags | tempreqvary; + } + + /* If the subpattern set a required code unit (or set a first code unit + that isn't really the first code unit - see above), set it. */ + + if (subreqcuflags >= 0) + { + reqcu = subreqcu; + reqcuflags = subreqcuflags; + } + } + + /* For a forward assertion, we take the reqcu, if set, provided that the + group has also set a firstcu. This can be helpful if the pattern that + follows the assertion doesn't set a different char. For example, it's + useful for /(?=abcde).+/. We can't set firstcu for an assertion, however + because it leads to incorrect effect for patterns such as /(?=a)a.+/ when + the "real" "a" would then become a reqcu instead of a firstcu. This is + overcome by a scan at the end if there's no firstcu, looking for an + asserted first char. A similar effect for patterns like /(?=.*X)X$/ means + we must only take the reqcu when the group also set a firstcu. Otherwise, + in that example, 'X' ends up set for both. */ + + else if (bravalue == OP_ASSERT && subreqcuflags >= 0 && + subfirstcuflags >= 0) + { + reqcu = subreqcu; + reqcuflags = subreqcuflags; + } + + break; /* End of nested group handling */ + + + /* ===================================================================*/ + /* Handle named backreferences and recursions. */ + + case META_BACKREF_BYNAME: + case META_RECURSE_BYNAME: + { + int count, index; + PCRE2_SPTR name; + BOOL is_dupname = FALSE; + named_group *ng = cb->named_groups; + uint32_t length = *(++pptr); + + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; + + /* In the first pass, the names generated in the pre-pass are available, + but the main name table has not yet been created. Scan the list of names + generated in the pre-pass in order to get a number and whether or not + this name is duplicated. */ + + groupnumber = 0; + for (i = 0; i < cb->names_found; i++, ng++) + { + if (length == ng->length && + PRIV(strncmp)(name, ng->name, length) == 0) + { + is_dupname = ng->isdup; + groupnumber = ng->number; + + /* For a recursion, that's all that is needed. We can now go to + the code above that handles numerical recursion, applying it to + the first group with the given name. */ + + if (meta == META_RECURSE_BYNAME) + { + meta_arg = groupnumber; + goto HANDLE_NUMERICAL_RECURSION; + } + + /* For a back reference, update the back reference map and the + maximum back reference. Then, for each group, we must check to + see if it is recursive, that is, it is inside the group that it + references. A flag is set so that the group can be made atomic. + */ + + cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; + if (groupnumber > cb->top_backref) + cb->top_backref = groupnumber; + + for (oc = cb->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == groupnumber) + { + oc->flag = TRUE; + break; + } + } + } + } + + /* If the name was not found we have a bad reference. */ + + if (groupnumber == 0) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; + } + + /* If a back reference name is not duplicated, we can handle it as + a numerical reference. */ + + if (!is_dupname) + { + meta_arg = groupnumber; + goto HANDLE_SINGLE_REFERENCE; + } + + /* If a back reference name is duplicated, we generate a different + opcode to a numerical back reference. In the second pass we must + search for the index and count in the final name table. */ + + count = 0; /* Values for first pass (avoids compiler warning) */ + index = 0; + if (lengthptr == NULL && !find_dupname_details(name, length, &index, + &count, errorcodeptr, cb)) return 0; + + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF; + PUT2INC(code, 0, index); + PUT2INC(code, 0, count); + } + break; + + + /* ===================================================================*/ + /* Handle a numerical callout. */ + + case META_CALLOUT_NUMBER: + code[0] = OP_CALLOUT; + PUT(code, 1, pptr[1]); /* Offset to next pattern item */ + PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */ + code[1 + 2*LINK_SIZE] = pptr[3]; + pptr += 3; + code += PRIV(OP_lengths)[OP_CALLOUT]; + break; + + + /* ===================================================================*/ + /* Handle a callout with a string argument. In the pre-pass we just compute + the length without generating anything. The length in pptr[3] includes both + delimiters; in the actual compile only the first one is copied, but a + terminating zero is added. Any doubled delimiters within the string make + this an overestimate, but it is not worth bothering about. */ + + case META_CALLOUT_STRING: + if (lengthptr != NULL) + { + *lengthptr += pptr[3] + (1 + 4*LINK_SIZE); + pptr += 3; + SKIPOFFSET(pptr); + } + + /* In the real compile we can copy the string. The starting delimiter is + included so that the client can discover it if they want. We also pass the + start offset to help a script language give better error messages. */ + + else + { + PCRE2_SPTR pp; + uint32_t delimiter; + uint32_t length = pptr[3]; + PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE); + + code[0] = OP_CALLOUT_STR; + PUT(code, 1, pptr[1]); /* Offset to next pattern item */ + PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */ + + pptr += 3; + GETPLUSOFFSET(offset, pptr); /* Offset to string in pattern */ + pp = cb->start_pattern + offset; + delimiter = *callout_string++ = *pp++; + if (delimiter == CHAR_LEFT_CURLY_BRACKET) + delimiter = CHAR_RIGHT_CURLY_BRACKET; + PUT(code, 1 + 3*LINK_SIZE, (int)(offset + 1)); /* One after delimiter */ + + /* The syntax of the pattern was checked in the parsing scan. The length + includes both delimiters, but we have passed the opening one just above, + so we reduce length before testing it. The test is for > 1 because we do + not want to copy the final delimiter. This also ensures that pp[1] is + accessible. */ + + while (--length > 1) + { + if (*pp == delimiter && pp[1] == delimiter) + { + *callout_string++ = delimiter; + pp += 2; + length--; + } + else *callout_string++ = *pp++; + } + *callout_string++ = CHAR_NUL; + + /* Set the length of the entire item, the advance to its end. */ + + PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code)); + code = callout_string; + } + break; + + + /* ===================================================================*/ + /* Handle repetition. The different types are all sorted out in the parsing + pass. */ + + case META_MINMAX_PLUS: + case META_MINMAX_QUERY: + case META_MINMAX: + repeat_min = *(++pptr); + repeat_max = *(++pptr); + goto REPEAT; + + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + repeat_min = 0; + repeat_max = REPEAT_UNLIMITED; + goto REPEAT; + + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + repeat_min = 1; + repeat_max = REPEAT_UNLIMITED; + goto REPEAT; + + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous_matched_char && repeat_min > 0) matched_char = TRUE; + + /* Remember whether this is a variable length repeat, and default to + single-char opcodes. */ + + reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + op_type = 0; + + /* If the repeat is {1} we can ignore it. */ + + if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; + + /* Adjust first and required code units for a zero repeat. */ + + if (repeat_min == 0) + { + firstcu = zerofirstcu; + firstcuflags = zerofirstcuflags; + reqcu = zeroreqcu; + reqcuflags = zeroreqcuflags; + } + + /* Note the greediness and possessiveness. */ + + switch (meta) + { + case META_MINMAX_PLUS: + case META_ASTERISK_PLUS: + case META_PLUS_PLUS: + case META_QUERY_PLUS: + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + break; + + case META_MINMAX_QUERY: + case META_ASTERISK_QUERY: + case META_PLUS_QUERY: + case META_QUERY_QUERY: + repeat_type = greedy_non_default; + possessive_quantifier = FALSE; + break; + + default: + repeat_type = greedy_default; + possessive_quantifier = FALSE; + break; + } + + /* Save start of previous item, in case we have to move it up in order to + insert something before it, and remember what it was. */ + + tempcode = previous; + op_previous = *previous; + + /* Now handle repetition for the different types of item. */ + + switch (op_previous) + { + /* If previous was a character or negated character match, abolish the + item and generate a repeat item instead. If a char item has a minimum of + more than one, ensure that it is set in reqcu - it might not be if a + sequence such as x{3} is the first thing in a branch because the x will + have gone into firstcu instead. */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + op_type = chartypeoffset[op_previous - OP_CHAR]; + + /* Deal with UTF characters that take up more than one code unit. */ + +#ifdef MAYBE_UTF_MULTI + if (utf && NOT_FIRSTCU(code[-1])) + { + PCRE2_UCHAR *lastchar = code - 1; + BACKCHAR(lastchar); + mclength = (uint32_t)(code - lastchar); /* Length of UTF character */ + memcpy(mcbuffer, lastchar, CU2BYTES(mclength)); /* Save the char */ + } + else +#endif /* MAYBE_UTF_MULTI */ + + /* Handle the case of a single code unit - either with no UTF support, or + with UTF disabled, or for a single-code-unit UTF character. */ + { + mcbuffer[0] = code[-1]; + mclength = 1; + if (op_previous <= OP_CHARI && repeat_min > 1) + { + reqcu = mcbuffer[0]; + reqcuflags = req_caseopt | cb->req_varyopt; + } + } + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + + /* If previous was a character class or a back reference, we put the + repeat stuff after it, but just skip the item if the repeat was {0,0}. */ + +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: +#endif + case OP_CLASS: + case OP_NCLASS: + case OP_REF: + case OP_REFI: + case OP_DNREF: + case OP_DNREFI: + + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + if (repeat_min == 0 && repeat_max == REPEAT_UNLIMITED) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == REPEAT_UNLIMITED) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + PUT2INC(code, 0, repeat_min); + if (repeat_max == REPEAT_UNLIMITED) repeat_max = 0; /* 2-byte encoding for max */ + PUT2INC(code, 0, repeat_max); + } + break; + + /* If previous is OP_FAIL, it was generated by an empty class [] + (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be + generated, that is by (*FAIL) or (?!), disallow a quantifier at parse + time. We can just ignore this repeat. */ + + case OP_FAIL: + goto END_REPEAT; + + /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets + because pcre2_match() could not handle backtracking into recursively + called groups. Now that this backtracking is available, we no longer need + to do this. However, we still need to replicate recursions as we do for + groups so as to have independent backtracking points. We can replicate + for the minimum number of repeats directly. For optional repeats we now + wrap the recursion in OP_BRA brackets and make use of the bracket + repetition. */ + + case OP_RECURSE: + + /* Generate unwrapped repeats for a non-zero minimum, except when the + minimum is 1 and the maximum unlimited, because that can be handled with + OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the + minimum, we just need to generate the appropriate additional copies. + Otherwise we need to generate one more, to simulate the situation when + the minimum is zero. */ + + if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED)) + { + int replicate = repeat_min; + if (repeat_min == repeat_max) replicate--; + + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. Do some paranoid checks for + potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit + integer type when available, otherwise double. */ + + if (lengthptr != NULL) + { + PCRE2_SIZE delta = replicate*(1 + LINK_SIZE); + if ((INT64_OR_DOUBLE)replicate* + (INT64_OR_DOUBLE)(1 + LINK_SIZE) > + (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += delta; + } + + else for (i = 0; i < replicate; i++) + { + memcpy(code, previous, CU2BYTES(1 + LINK_SIZE)); + previous = code; + code += 1 + LINK_SIZE; + } + + /* If the number of repeats is fixed, we are done. Otherwise, adjust + the counts and fall through. */ + + if (repeat_min == repeat_max) break; + if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; + repeat_min = 0; + } + + /* Wrap the recursion call in OP_BRA brackets. */ + + (void)memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); + op_previous = *previous = OP_BRA; + PUT(previous, 1, 2 + 2*LINK_SIZE); + previous[2 + 2*LINK_SIZE] = OP_KET; + PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); + code += 2 + 2 * LINK_SIZE; + length_prevgroup = 3 + 3*LINK_SIZE; + group_return = -1; /* Set "may match empty string" */ + + /* Now treat as a repeated OP_BRA. */ + /* Fall through */ + + /* If previous was a bracket group, we may have to replicate it in + certain cases. Note that at this point we can encounter only the "basic" + bracket opcodes such as BRA and CBRA, as this is the place where they get + converted into the more special varieties such as BRAPOS and SBRA. + Originally, PCRE did not allow repetition of assertions, but now it does, + for Perl compatibility. */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_BRA: + case OP_CBRA: + case OP_COND: + { + int len = (int)(code - previous); + PCRE2_UCHAR *bralink = NULL; + PCRE2_UCHAR *brazeroptr = NULL; + + /* Repeating a DEFINE group (or any group where the condition is always + FALSE and there is only one branch) is pointless, but Perl allows the + syntax, so we just ignore the repeat. */ + + if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && + previous[GET(previous, 1)] != OP_ALT) + goto END_REPEAT; + + /* There is no sense in actually repeating assertions. The only + potential use of repetition is in cases when the assertion is optional. + Therefore, if the minimum is greater than zero, just ignore the repeat. + If the maximum is not zero or one, set it to 1. */ + + if (op_previous < OP_ONCE) /* Assertion */ + { + if (repeat_min > 0) goto END_REPEAT; + if (repeat_max > 1) repeat_max = 1; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) + { + /* If the maximum is also zero, we used to just omit the group from + the output altogether, like this: + + ** if (repeat_max == 0) + ** { + ** code = previous; + ** goto END_REPEAT; + ** } + + However, that fails when a group or a subgroup within it is + referenced as a subroutine from elsewhere in the pattern, so now we + stick in OP_SKIPZERO in front of it so that it is skipped on + execution. As we don't have a list of which groups are referenced, we + cannot do this selectively. + + If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. */ + + if (repeat_max <= 1 || repeat_max == REPEAT_UNLIMITED) + { + (void)memmove(previous + 1, previous, CU2BYTES(len)); + code++; + if (repeat_max == 0) + { + *previous++ = OP_SKIPZERO; + goto END_REPEAT; + } + brazeroptr = previous; /* Save for possessive optimizing */ + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We have to + adjust the value or repeat_max, since one less copy is required. */ + + else + { + int linkoffset; + (void)memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len)); + code += 2 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket link offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + linkoffset = (bralink == NULL)? 0 : (int)(previous - bralink); + bralink = previous; + PUTINC(previous, 0, linkoffset); + } + + if (repeat_max != REPEAT_UNLIMITED) repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. */ + + else + { + if (repeat_min > 1) + { + /* In the pre-compile phase, we don't actually do the replication. + We just adjust the length as if we had. Do some paranoid checks for + potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit + integer type when available, otherwise double. */ + + if (lengthptr != NULL) + { + PCRE2_SIZE delta = (repeat_min - 1)*length_prevgroup; + if ((INT64_OR_DOUBLE)(repeat_min - 1)* + (INT64_OR_DOUBLE)length_prevgroup > + (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += delta; + } + + /* This is compiling for real. If there is a set first code unit + for the group, and we have not yet set a "required code unit", set + it. */ + + else + { + if (groupsetfirstcu && reqcuflags < 0) + { + reqcu = firstcu; + reqcuflags = firstcuflags; + } + for (i = 1; (uint32_t)i < repeat_min; i++) + { + memcpy(code, previous, CU2BYTES(len)); + code += len; + } + } + } + + if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero + minimum, the first one was set up above. In all cases the repeat_max + now specifies the number of additional copies needed. Again, we must + remember to replicate entries on the forward reference list. */ + + if (repeat_max != REPEAT_UNLIMITED) + { + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. For each repetition we must add + 1 to the length for BRAZERO and for all but the last repetition we + must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some + paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type + is a 64-bit integer type when available, otherwise double. */ + + if (lengthptr != NULL && repeat_max > 0) + { + PCRE2_SIZE delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - + 2 - 2*LINK_SIZE; /* Last one doesn't nest */ + if ((INT64_OR_DOUBLE)repeat_max * + (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) + > (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += delta; + } + + /* This is compiling for real */ + + else for (i = repeat_max - 1; i >= 0; i--) + { + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) + { + int linkoffset; + *code++ = OP_BRA; + linkoffset = (bralink == NULL)? 0 : (int)(code - bralink); + bralink = code; + PUTINC(code, 0, linkoffset); + } + + memcpy(code, previous, CU2BYTES(len)); + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink != NULL) + { + int oldlinkoffset; + int linkoffset = (int)(code - bralink + 1); + PCRE2_UCHAR *bra = code - linkoffset; + oldlinkoffset = GET(bra, 1); + bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; + *code++ = OP_KET; + PUTINC(code, 0, linkoffset); + PUT(bra, 1, linkoffset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. For + ONCE brackets, that's all we need to do. However, possessively repeated + ONCE brackets can be converted into non-capturing brackets, as the + behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to + deal with possessive ONCEs specially. + + Otherwise, when we are doing the actual compile phase, check to see + whether this group is one that could match an empty string. If so, + convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so + that runtime checking can be done. [This check is also applied to ONCE + groups at runtime, but in a different way.] + + Then, if the quantifier was possessive and the bracket is not a + conditional, we convert the BRA code to the POS form, and the KET code to + KETRPOS. (It turns out to be convenient at runtime to detect this kind of + subpattern at both the start and at the end.) The use of special opcodes + makes it possible to reduce greatly the stack usage in pcre2_match(). If + the group is preceded by OP_BRAZERO, convert this to OP_BRAPOSZERO. + + Then, if the minimum number of matches is 1 or 0, cancel the possessive + flag so that the default action below, of wrapping everything inside + atomic brackets, does not happen. When the minimum is greater than 1, + there will be earlier copies of the group, and so we still have to wrap + the whole thing. */ + + else + { + PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; + PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); + + /* Convert possessive ONCE brackets to non-capturing */ + + if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA; + + /* For non-possessive ONCE brackets, all we need to do is to + set the KET. */ + + if (*bracode == OP_ONCE) *ketcode = OP_KETRMAX + repeat_type; + + /* Handle non-ONCE brackets and possessive ONCEs (which have been + converted to non-capturing above). */ + + else + { + /* In the compile phase, adjust the opcode if the group can match + an empty string. For a conditional group with only one branch, the + value of group_return will not show "could be empty", so we must + check that separately. */ + + if (lengthptr == NULL) + { + if (group_return < 0) *bracode += OP_SBRA - OP_BRA; + if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT) + *bracode = OP_SCOND; + } + + /* Handle possessive quantifiers. */ + + if (possessive_quantifier) + { + /* For COND brackets, we wrap the whole thing in a possessively + repeated non-capturing bracket, because we have not invented POS + versions of the COND opcodes. */ + + if (*bracode == OP_COND || *bracode == OP_SCOND) + { + int nlen = (int)(code - bracode); + (void)memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen)); + code += 1 + LINK_SIZE; + nlen += 1 + LINK_SIZE; + *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS; + *code++ = OP_KETRPOS; + PUTINC(code, 0, nlen); + PUT(bracode, 1, nlen); + } + + /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ + + else + { + *bracode += 1; /* Switch to xxxPOS opcodes */ + *ketcode = OP_KETRPOS; + } + + /* If the minimum is zero, mark it as possessive, then unset the + possessive flag when the minimum is 0 or 1. */ + + if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; + if (repeat_min < 2) possessive_quantifier = FALSE; + } + + /* Non-possessive quantifier */ + + else *ketcode = OP_KETRMAX + repeat_type; + } + } + } + break; + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting op_type to add a suitable offset into repeat_type. + Note the the Unicode property types will be present only when + SUPPORT_UNICODE is defined, but we don't wrap the little bits of code + here because it just makes it horribly messy. */ + + default: + if (op_previous >= OP_EODN) /* Not a character type - internal error */ + { + *errorcodeptr = ERR10; + return 0; + } + else + { + int prop_type, prop_value; + PCRE2_UCHAR *oldcode; + + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + mclength = 0; /* Not a character */ + + if (op_previous == OP_PROP || op_previous == OP_NOTPROP) + { + prop_type = previous[1]; + prop_value = previous[2]; + } + else + { + /* Come here from just above with a character in mcbuffer/mclength. */ + OUTPUT_SINGLE_REPEAT: + prop_type = prop_value = -1; + } + + /* At this point, if prop_type == prop_value == -1 we either have a + character in mcbuffer when mclength is greater than zero, or we have + mclength zero, in which case there is a non-property character type in + op_previous. If prop_type/value are not negative, we have a property + character type in op_previous. */ + + oldcode = code; /* Save where we were */ + code = previous; /* Usually overwrite previous item */ + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) goto END_REPEAT; + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + + /* A repeat minimum of 1 is optimized into some special cases. If the + maximum is unlimited, we use OP_PLUS. Otherwise, the original item is + left in place and, if the maximum is greater than 1, we use OP_UPTO with + one less than the maximum. */ + + else if (repeat_min == 1) + { + if (repeat_max == REPEAT_UNLIMITED) + *code++ = OP_PLUS + repeat_type; + else + { + code = oldcode; /* Leave previous item in place */ + if (repeat_max == 1) goto END_REPEAT; + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max - 1); + } + } + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO or STAR or QUERY. */ + + else + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + PUT2INC(code, 0, repeat_min); + + /* Unless repeat_max equals repeat_min, fill in the data for EXACT, + and then generate the second opcode. For a repeated Unicode property + match, there are two extra values that define the required property, + and mclength is set zero to indicate this. */ + + if (repeat_max != repeat_min) + { + if (mclength > 0) + { + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + } + else + { + *code++ = op_previous; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + } + + /* Now set up the following opcode */ + + if (repeat_max == REPEAT_UNLIMITED) + *code++ = OP_STAR + repeat_type; + else + { + repeat_max -= repeat_min; + if (repeat_max == 1) + { + *code++ = OP_QUERY + repeat_type; + } + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + } + } + + /* Fill in the character or character type for the final opcode. */ + + if (mclength > 0) + { + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + } + else + { + *code++ = op_previous; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + } + } + break; + } /* End of switch on different op_previous values */ + + + /* If the character following a repeat is '+', possessive_quantifier is + TRUE. For some opcodes, there are special alternative opcodes for this + case. For anything else, we wrap the entire repeated item inside OP_ONCE + brackets. Logically, the '+' notation is just syntactic sugar, taken from + Sun's Java package, but the special opcodes can optimize it. + + Some (but not all) possessively repeated subpatterns have already been + completely handled in the code just above. For them, possessive_quantifier + is always FALSE at this stage. Note that the repeated item starts at + tempcode, not at previous, which might be the first part of a string whose + (former) last char we repeated. */ + + if (possessive_quantifier) + { + int len; + + /* Possessifying an EXACT quantifier has no effect, so we can ignore it. + However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6}, + {5,}, or {5,10}). We skip over an EXACT item; if the length of what + remains is greater than zero, there's a further opcode that can be + handled. If not, do nothing, leaving the EXACT alone. */ + + switch(*tempcode) + { + case OP_TYPEEXACT: + tempcode += PRIV(OP_lengths)[*tempcode] + + ((tempcode[1 + IMM2_SIZE] == OP_PROP + || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + break; + + /* CHAR opcodes are used for exacts whose count is 1. */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + tempcode += PRIV(OP_lengths)[*tempcode]; +#ifdef SUPPORT_UNICODE + if (utf && HAS_EXTRALEN(tempcode[-1])) + tempcode += GET_EXTRALEN(tempcode[-1]); +#endif + break; + + /* For the class opcodes, the repeat operator appears at the end; + adjust tempcode to point to it. */ + + case OP_CLASS: + case OP_NCLASS: + tempcode += 1 + 32/sizeof(PCRE2_UCHAR); + break; + +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: + tempcode += GET(tempcode, 1); + break; +#endif + } + + /* If tempcode is equal to code (which points to the end of the repeated + item), it means we have skipped an EXACT item but there is no following + QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In + all other cases, tempcode will be pointing to the repeat opcode, and will + be less than code, so the value of len will be greater than 0. */ + + len = (int)(code - tempcode); + if (len > 0) + { + unsigned int repcode = *tempcode; + + /* There is a table for possessifying opcodes, all of which are less + than OP_CALLOUT. A zero entry means there is no possessified version. + */ + + if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0) + *tempcode = opcode_possessify[repcode]; + + /* For opcode without a special possessified version, wrap the item in + ONCE brackets. */ + + else + { + (void)memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len)); + code += 1 + LINK_SIZE; + len += 1 + LINK_SIZE; + tempcode[0] = OP_ONCE; + *code++ = OP_KET; + PUTINC(code, 0, len); + PUT(tempcode, 1, len); + } + } + } + + /* We set the "follows varying string" flag for subsequently encountered + reqcus if it isn't already set and we have just passed a varying length + item. */ + + END_REPEAT: + cb->req_varyopt |= reqvary; + break; + + + /* ===================================================================*/ + /* Handle a 32-bit data character with a value greater than META_END. */ + + case META_BIGVALUE: + pptr++; + goto NORMAL_CHAR; + + + /* ===============================================================*/ + /* Handle a back reference by number, which is the meta argument. The + pattern offsets for back references to group numbers less than 10 are held + in a special vector, to avoid using more than two parsed pattern elements + in 64-bit environments. We only need the offset to the first occurrence, + because if that doesn't fail, subsequent ones will also be OK. */ + + case META_BACKREF: + if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg]; + else GETPLUSOFFSET(offset, pptr); + + if (meta_arg > cb->bracount) + { + cb->erroroffset = offset; + *errorcodeptr = ERR15; /* Non-existent subpattern */ + return 0; + } + + /* Come here from named backref handling when the reference is to a + single group (that is, not to a duplicated name). The back reference + data will have already been updated. We must disable firstcu if not + set, to cope with cases like (?=(\w+))\1: which would otherwise set ':' + later. */ + + HANDLE_SINGLE_REFERENCE: + if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE; + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF; + PUT2INC(code, 0, meta_arg); + + /* Update the map of back references, and keep the highest one. We + could do this in parse_regex() for numerical back references, but not + for named back references, because we don't know the numbers to which + named back references refer. So we do it all in this function. */ + + cb->backref_map |= (meta_arg < 32)? (1u << meta_arg) : 1; + if (meta_arg > cb->top_backref) cb->top_backref = meta_arg; + + /* Check to see if this back reference is recursive, that it, it + is inside the group that it references. A flag is set so that the + group can be made atomic. */ + + for (oc = cb->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == meta_arg) + { + oc->flag = TRUE; + break; + } + } + break; + + + /* ===============================================================*/ + /* Handle recursion by inserting the number of the called group (which is + the meta argument) after OP_RECURSE. At the end of compiling the pattern is + scanned and these numbers are replaced by offsets within the pattern. It is + done like this to avoid problems with forward references and adjusting + offsets when groups are duplicated and moved (as discovered in previous + implementations). Note that a recursion does not have a set first character + (relevant if it is repeated, because it will then be wrapped with ONCE + brackets). */ + + case META_RECURSE: + GETPLUSOFFSET(offset, pptr); + if (meta_arg > cb->bracount) + { + cb->erroroffset = offset; + *errorcodeptr = ERR15; /* Non-existent subpattern */ + return 0; + } + HANDLE_NUMERICAL_RECURSION: + *code = OP_RECURSE; + PUT(code, 1, meta_arg); + code += 1 + LINK_SIZE; + groupsetfirstcu = FALSE; + cb->had_recurse = TRUE; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + break; + + + /* ===============================================================*/ + /* Handle capturing parentheses; the number is the meta argument. */ + + case META_CAPTURE: + bravalue = OP_CBRA; + skipunits = IMM2_SIZE; + PUT2(code, 1+LINK_SIZE, meta_arg); + cb->lastcapture = meta_arg; + goto GROUP_PROCESS_NOTE_EMPTY; + + + /* ===============================================================*/ + /* Handle escape sequence items. For ones like \d, the ESC_values are + arranged to be the same as the corresponding OP_values in the default case + when PCRE2_UCP is not set (which is the only case in which they will appear + here). + + Note: \Q and \E are never seen here, as they were dealt with in + parse_pattern(). Neither are numerical back references or recursions, which + were turned into META_BACKREF or META_RECURSE items, respectively. \k and + \g, when followed by names, are turned into META_BACKREF_BYNAME or + META_RECURSE_BYNAME. */ + + case META_ESCAPE: + + /* We can test for escape sequences that consume a character because their + values lie between ESC_b and ESC_Z; this may have to change if any new ones + are ever created. For these sequences, we disable the setting of a first + character if it hasn't already been set. */ + + if (meta_arg > ESC_b && meta_arg < ESC_Z) + { + matched_char = TRUE; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + } + + /* Set values to reset to if this is followed by a zero repeat. */ + + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + + /* If Unicode is not supported, \P and \p are not allowed and are + faulted at parse time, so will never appear here. */ + +#ifdef SUPPORT_UNICODE + if (meta_arg == ESC_P || meta_arg == ESC_p) + { + uint32_t ptype = *(++pptr) >> 16; + uint32_t pdata = *pptr & 0xffff; + *code++ = (meta_arg == ESC_p)? OP_PROP : OP_NOTPROP; + *code++ = ptype; + *code++ = pdata; + break; /* End META_ESCAPE */ + } +#endif + + /* For the rest (including \X when Unicode is supported - if not it's + faulted at parse time), the OP value is the escape value when PCRE2_UCP is + not set; if it is set, these escapes do not show up here because they are + converted into Unicode property tests in parse_regex(). Note that \b and \B + do a one-character lookbehind, and \A also behaves as if it does. */ + + if (meta_arg == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */ + if ((meta_arg == ESC_b || meta_arg == ESC_B || meta_arg == ESC_A) && + cb->max_lookbehind == 0) + cb->max_lookbehind = 1; + + /* In non-UTF mode, and for both 32-bit modes, we turn \C into OP_ALLANY + instead of OP_ANYBYTE so that it works in DFA mode and in lookbehinds. */ + +#if PCRE2_CODE_UNIT_WIDTH == 32 + *code++ = (meta_arg == ESC_C)? OP_ALLANY : meta_arg; +#else + *code++ = (!utf && meta_arg == ESC_C)? OP_ALLANY : meta_arg; +#endif + break; /* End META_ESCAPE */ + + + /* ===================================================================*/ + /* Handle an unrecognized meta value. A parsed pattern value less than + META_END is a literal. Otherwise we have a problem. */ + + default: + if (meta >= META_END) + { +#ifdef DEBUG_SHOW_PARSED + fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x\n", *pptr); +#endif + *errorcodeptr = ERR89; /* Internal error - unrecognized. */ + return 0; + } + + /* Handle a literal character. We come here by goto in the case of a + 32-bit, non-UTF character whose value is greater than META_END. */ + + NORMAL_CHAR: + meta = *pptr; /* Get the full 32 bits */ + NORMAL_CHAR_SET: /* Character is already in meta */ + matched_char = TRUE; + + /* For caseless UTF mode, check whether this character has more than one + other case. If so, generate a special OP_PROP item instead of OP_CHARI. */ + +#ifdef SUPPORT_UNICODE + if (utf && (options & PCRE2_CASELESS) != 0) + { + uint32_t caseset = UCD_CASESET(meta); + if (caseset != 0) + { + *code++ = OP_PROP; + *code++ = PT_CLIST; + *code++ = caseset; + if (firstcuflags == REQ_UNSET) + firstcuflags = zerofirstcuflags = REQ_NONE; + break; /* End handling this meta item */ + } + } +#endif + + /* Caseful matches, or not one of the multicase characters. Get the + character's code units into mcbuffer, with the length in mclength. When not + in UTF mode, the length is always 1. */ + +#ifdef SUPPORT_UNICODE + if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else +#endif + { + mclength = 1; + mcbuffer[0] = meta; + } + + /* Generate the appropriate code */ + + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR; + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + + /* Remember if \r or \n were seen */ + + if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) + cb->external_flags |= PCRE2_HASCRORLF; + + /* Set the first and required code units appropriately. If no previous + first code unit, set it from this character, but revert to none on a zero + repeat. Otherwise, leave the firstcu value alone, and don't change it on + a zero repeat. */ + + if (firstcuflags == REQ_UNSET) + { + zerofirstcuflags = REQ_NONE; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + + /* If the character is more than one code unit long, we can set firstcu + only if it is not to be matched caselessly. */ + + if (mclength == 1 || req_caseopt == 0) + { + firstcu = mcbuffer[0]; + firstcuflags = req_caseopt; + if (mclength != 1) + { + reqcu = code[-1]; + reqcuflags = cb->req_varyopt; + } + } + else firstcuflags = reqcuflags = REQ_NONE; + } + + /* firstcu was previously set; we can set reqcu only if the length is + 1 or the matching is caseful. */ + + else + { + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + if (mclength == 1 || req_caseopt == 0) + { + reqcu = code[-1]; + reqcuflags = req_caseopt | cb->req_varyopt; + } + } + break; /* End default meta handling */ + } /* End of big switch */ + } /* End of big loop */ + +/* Control never reaches here. */ +} + + + +/************************************************* +* Compile regex: a sequence of alternatives * +*************************************************/ + +/* On entry, pptr is pointing past the bracket meta, but on return it points to +the closing bracket or META_END. The code variable is pointing at the code unit +into which the BRA operator has been stored. This function is used during the +pre-compile phase when we are trying to find out the amount of memory needed, +as well as during the real compile phase. The value of lengthptr distinguishes +the two phases. + +Arguments: + options option bits, including any changes for this subpattern + codeptr -> the address of the current code pointer + pptrptr -> the address of the current parsed pattern pointer + errorcodeptr -> pointer to error code variable + skipunits skip this many code units at start (for brackets and OP_COND) + firstcuptr place to put the first required code unit + firstcuflagsptr place to put the first code unit flags, or a negative number + reqcuptr place to put the last required code unit + reqcuflagsptr place to put the last required code unit flags, or a negative number + bcptr pointer to the chain of currently open branches + cb points to the data block with tables pointers etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: 0 There has been an error + +1 Success, this group must match at least one character + -1 Success, this group may match an empty string +*/ + +static int +compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, uint32_t **pptrptr, + int *errorcodeptr, uint32_t skipunits, uint32_t *firstcuptr, + int32_t *firstcuflagsptr, uint32_t *reqcuptr,int32_t *reqcuflagsptr, + branch_chain *bcptr, compile_block *cb, PCRE2_SIZE *lengthptr) +{ +PCRE2_UCHAR *code = *codeptr; +PCRE2_UCHAR *last_branch = code; +PCRE2_UCHAR *start_bracket = code; +BOOL lookbehind; +open_capitem capitem; +int capnumber = 0; +int okreturn = 1; +uint32_t *pptr = *pptrptr; +uint32_t firstcu, reqcu; +uint32_t lookbehindlength; +int32_t firstcuflags, reqcuflags; +uint32_t branchfirstcu, branchreqcu; +int32_t branchfirstcuflags, branchreqcuflags; +PCRE2_SIZE length; +branch_chain bc; + +/* If set, call the external function that checks for stack availability. */ + +if (cb->cx->stack_guard != NULL && + cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data)) + { + *errorcodeptr= ERR33; + return 0; + } + +/* Miscellaneous initialization */ + +bc.outer = bcptr; +bc.current_branch = code; + +firstcu = reqcu = 0; +firstcuflags = reqcuflags = REQ_UNSET; + +/* Accumulate the length for use in the pre-compile phase. Start with the +length of the BRA and KET and any extra code units that are required at the +beginning. We accumulate in a local variable to save frequent testing of +lengthptr for NULL. We cannot do this by looking at the value of 'code' at the +start and end of each alternative, because compiled items are discarded during +the pre-compile phase so that the workspace is not exceeded. */ + +length = 2 + 2*LINK_SIZE + skipunits; + +/* Remember if this is a lookbehind assertion, and if it is, save its length +and skip over the pattern offset. */ + +lookbehind = *code == OP_ASSERTBACK || *code == OP_ASSERTBACK_NOT; +if (lookbehind) + { + lookbehindlength = META_DATA(pptr[-1]); + pptr += SIZEOFFSET; + } +else lookbehindlength = 0; + +/* If this is a capturing subpattern, add to the chain of open capturing items +so that we can detect them if (*ACCEPT) is encountered. Note that only OP_CBRA +need be tested here; changing this opcode to one of its variants, e.g. +OP_SCBRAPOS, happens later, after the group has been compiled. */ + +if (*code == OP_CBRA) + { + capnumber = GET2(code, 1 + LINK_SIZE); + capitem.number = capnumber; + capitem.next = cb->open_caps; + capitem.flag = FALSE; + capitem.assert_depth = cb->assert_depth; + cb->open_caps = &capitem; + } + +/* Offset is set zero to mark that this bracket is still open */ + +PUT(code, 1, 0); +code += 1 + LINK_SIZE + skipunits; + +/* Loop for each alternative branch */ + +for (;;) + { + int branch_return; + + /* Insert OP_REVERSE if this is as lookbehind assertion. */ + + if (lookbehind && lookbehindlength > 0) + { + *code++ = OP_REVERSE; + PUTINC(code, 0, lookbehindlength); + length += 1 + LINK_SIZE; + } + + /* Now compile the branch; in the pre-compile phase its length gets added + into the length. */ + + if ((branch_return = + compile_branch(&options, &code, &pptr, errorcodeptr, &branchfirstcu, + &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc, + cb, (lengthptr == NULL)? NULL : &length)) == 0) + return 0; + + /* If a branch can match an empty string, so can the whole group. */ + + if (branch_return < 0) okreturn = -1; + + /* In the real compile phase, there is some post-processing to be done. */ + + if (lengthptr == NULL) + { + /* If this is the first branch, the firstcu and reqcu values for the + branch become the values for the regex. */ + + if (*last_branch != OP_ALT) + { + firstcu = branchfirstcu; + firstcuflags = branchfirstcuflags; + reqcu = branchreqcu; + reqcuflags = branchreqcuflags; + } + + /* If this is not the first branch, the first char and reqcu have to + match the values from all the previous branches, except that if the + previous value for reqcu didn't have REQ_VARY set, it can still match, + and we set REQ_VARY for the regex. */ + + else + { + /* If we previously had a firstcu, but it doesn't match the new branch, + we have to abandon the firstcu for the regex, but if there was + previously no reqcu, it takes on the value of the old firstcu. */ + + if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu) + { + if (firstcuflags >= 0) + { + if (reqcuflags < 0) + { + reqcu = firstcu; + reqcuflags = firstcuflags; + } + } + firstcuflags = REQ_NONE; + } + + /* If we (now or from before) have no firstcu, a firstcu from the + branch becomes a reqcu if there isn't a branch reqcu. */ + + if (firstcuflags < 0 && branchfirstcuflags >= 0 && + branchreqcuflags < 0) + { + branchreqcu = branchfirstcu; + branchreqcuflags = branchfirstcuflags; + } + + /* Now ensure that the reqcus match */ + + if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) || + reqcu != branchreqcu) + reqcuflags = REQ_NONE; + else + { + reqcu = branchreqcu; + reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY */ + } + } + } + + /* Handle reaching the end of the expression, either ')' or end of pattern. + In the real compile phase, go back through the alternative branches and + reverse the chain of offsets, with the field in the BRA item now becoming an + offset to the first alternative. If there are no alternatives, it points to + the end of the group. The length in the terminating ket is always the length + of the whole bracketed item. Return leaving the pointer at the terminating + char. */ + + if (META_CODE(*pptr) != META_ALT) + { + if (lengthptr == NULL) + { + PCRE2_SIZE branch_length = code - last_branch; + do + { + PCRE2_SIZE prev_length = GET(last_branch, 1); + PUT(last_branch, 1, branch_length); + branch_length = prev_length; + last_branch -= branch_length; + } + while (branch_length > 0); + } + + /* Fill in the ket */ + + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + code += 1 + LINK_SIZE; + + /* If it was a capturing subpattern, check to see if it contained any + recursive back references. If so, we must wrap it in atomic brackets. In + any event, remove the block from the chain. */ + + if (capnumber > 0) + { + if (cb->open_caps->flag) + { + (void)memmove(start_bracket + 1 + LINK_SIZE, start_bracket, + CU2BYTES(code - start_bracket)); + *start_bracket = OP_ONCE; + code += 1 + LINK_SIZE; + PUT(start_bracket, 1, (int)(code - start_bracket)); + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + code += 1 + LINK_SIZE; + length += 2 + 2*LINK_SIZE; + } + cb->open_caps = cb->open_caps->next; + } + + /* Set values to pass back */ + + *codeptr = code; + *pptrptr = pptr; + *firstcuptr = firstcu; + *firstcuflagsptr = firstcuflags; + *reqcuptr = reqcu; + *reqcuflagsptr = reqcuflags; + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += length; + } + return okreturn; + } + + /* Another branch follows. In the pre-compile phase, we can move the code + pointer back to where it was for the start of the first branch. (That is, + pretend that each branch is the only one.) + + In the real compile phase, insert an ALT node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + if (lengthptr != NULL) + { + code = *codeptr + 1 + LINK_SIZE + skipunits; + length += 1 + LINK_SIZE; + } + else + { + *code = OP_ALT; + PUT(code, 1, (int)(code - last_branch)); + bc.current_branch = last_branch = code; + code += 1 + LINK_SIZE; + } + + /* Set the lookbehind length (if not in a lookbehind the value will be zero) + and then advance past the vertical bar. */ + + lookbehindlength = META_DATA(*pptr); + pptr++; + } +/* Control never reaches here */ +} + + + +/************************************************* +* Check for anchored pattern * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD will +be found, because ^ generates OP_CIRCM in that mode. + +We can also consider a regex to be anchored if OP_SOM starts all its branches. +This is the code for \G, which means "match at start of match position, taking +into account the match offset". + +A branch is also implicitly anchored if it starts with .* and DOTALL is set, +because that will try the rest of the pattern at all possible matching points, +so there is no point trying again.... er .... + +.... except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. We haven't enough information +to catch that case precisely. + +At first, the best we could do was to detect when .* was in capturing brackets +and the highest back reference was greater than or equal to that level. +However, by keeping a bitmap of the first 31 back references, we can catch some +of the more common cases more precisely. + +... A second exception is when the .* appears inside an atomic group, because +this prevents the number of characters it matches from being adjusted. + +Arguments: + code points to start of the compiled pattern + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + cb points to the compile data block + atomcount atomic group level + inassert TRUE if in an assertion + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, + int atomcount, BOOL inassert) +{ +do { + PCRE2_SPTR scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); + int op = *scode; + + /* Non-capturing brackets */ + + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) + { + if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1u << n) : 1); + if (!is_anchored(scode, new_map, cb, atomcount, inassert)) return FALSE; + } + + /* Positive forward assertion */ + + else if (op == OP_ASSERT) + { + if (!is_anchored(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; + } + + /* Condition. If there is no second branch, it can't be anchored. */ + + else if (op == OP_COND || op == OP_SCOND) + { + if (scode[GET(scode,1)] != OP_ALT) return FALSE; + if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Atomic groups */ + + else if (op == OP_ONCE) + { + if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert)) + return FALSE; + } + + /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and + it isn't in brackets that are or may be referenced or inside an atomic + group or an assertion. Also the pattern must not contain *PRUNE or *SKIP, + because these break the feature. Consider, for example, /(?s).*?(*PRUNE)b/ + with the subject "aab", which matches "b", i.e. not at the start of a line. + There is also an option that disables auto-anchoring. */ + + else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || + op == OP_TYPEPOSSTAR)) + { + if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 || + atomcount > 0 || cb->had_pruneorskip || inassert || + (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) + return FALSE; + } + + /* Check for explicit anchoring */ + + else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; + + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n). As in the case of is_anchored() (see above), we +have to take account of back references to capturing brackets that contain .* +because in that case we can't make the assumption. Also, the appearance of .* +inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE +or *SKIP does not count, because once again the assumption no longer holds. + +Arguments: + code points to start of the compiled pattern or a group + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + cb points to the compile data + atomcount atomic group level + inassert TRUE if in an assertion + +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, + int atomcount, BOOL inassert) +{ +do { + PCRE2_SPTR scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); + int op = *scode; + + /* If we are at the start of a conditional assertion group, *both* the + conditional assertion *and* what follows the condition must satisfy the test + for start of line. Other kinds of condition fail. Note that there may be an + auto-callout at the start of a condition. */ + + if (op == OP_COND) + { + scode += 1 + LINK_SIZE; + + if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; + else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE); + + switch (*scode) + { + case OP_CREF: + case OP_DNCREF: + case OP_RREF: + case OP_DNRREF: + case OP_FAIL: + case OP_FALSE: + case OP_TRUE: + return FALSE; + + default: /* Assertion */ + if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; + do scode += GET(scode, 1); while (*scode == OP_ALT); + scode += 1 + LINK_SIZE; + break; + } + scode = first_significant_code(scode, FALSE); + op = *scode; + } + + /* Non-capturing brackets */ + + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) + { + if (!is_startline(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1u << n) : 1); + if (!is_startline(scode, new_map, cb, atomcount, inassert)) return FALSE; + } + + /* Positive forward assertions */ + + else if (op == OP_ASSERT) + { + if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) + return FALSE; + } + + /* Atomic brackets */ + + else if (op == OP_ONCE) + { + if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert)) + return FALSE; + } + + /* .* means "start at start or after \n" if it isn't in atomic brackets or + brackets that may be referenced or an assertion, and as long as the pattern + does not contain *PRUNE or *SKIP, because these break the feature. Consider, + for example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", + i.e. not at the start of a line. There is also an option that disables this + optimization. */ + + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) + { + if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 || + atomcount > 0 || cb->had_pruneorskip || inassert || + (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) + return FALSE; + } + + /* Check for explicit circumflex; anything else gives a FALSE result. Note + in particular that this includes atomic brackets OP_ONCE because the number + of characters matched by .* cannot be adjusted inside them. */ + + else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; + + /* Move on to the next alternative */ + + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Scan compiled regex for recursion reference * +*************************************************/ + +/* This function scans through a compiled pattern until it finds an instance of +OP_RECURSE. + +Arguments: + code points to start of expression + utf TRUE in UTF mode + +Returns: pointer to the opcode for OP_RECURSE, or NULL if not found +*/ + +static PCRE2_SPTR +find_recurse(PCRE2_SPTR code, BOOL utf) +{ +for (;;) + { + PCRE2_UCHAR c = *code; + if (c == OP_END) return NULL; + if (c == OP_RECURSE) return code; + + /* XCLASS is used for classes that cannot be represented just by a bit map. + This includes negated single high-valued characters. CALLOUT_STR is used for + callouts with string arguments. In both cases the length in the table is + zero; the actual length is stored in the compiled code. */ + + if (c == OP_XCLASS) code += GET(code, 1); + else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE); + + /* Otherwise, we can get the item's length from the table, except that for + repeated character types, we have to test for \p and \P, which have an extra + two code units of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, + we must add in its length. */ + + else + { + switch(c) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + case OP_TYPEPOSUPTO: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) + code += 2; + break; + + case OP_MARK: + case OP_COMMIT_ARG: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + code += code[1]; + break; + } + + /* Add in the fixed length from the table */ + + code += PRIV(OP_lengths)[c]; + + /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may + be followed by a multi-unit character. The length in the table is a + minimum, so we have to arrange to skip the extra units. */ + +#ifdef MAYBE_UTF_MULTI + if (utf) switch(c) + { + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + case OP_UPTO: + case OP_UPTOI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_STAR: + case OP_STARI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); + break; + } +#else + (void)(utf); /* Keep compiler happy by referencing function argument */ +#endif /* MAYBE_UTF_MULTI */ + } + } +} + + + +/************************************************* +* Check for asserted fixed first code unit * +*************************************************/ + +/* During compilation, the "first code unit" settings from forward assertions +are discarded, because they can cause conflicts with actual literals that +follow. However, if we end up without a first code unit setting for an +unanchored pattern, it is worth scanning the regex to see if there is an +initial asserted first code unit. If all branches start with the same asserted +code unit, or with a non-conditional bracket all of whose alternatives start +with the same asserted code unit (recurse ad lib), then we return that code +unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with +REQ_NONE in the flags. + +Arguments: + code points to start of compiled pattern + flags points to the first code unit flags + inassert non-zero if in an assertion + +Returns: the fixed first code unit, or 0 with REQ_NONE in flags +*/ + +static uint32_t +find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, uint32_t inassert) +{ +uint32_t c = 0; +int cflags = REQ_NONE; + +*flags = REQ_NONE; +do { + uint32_t d; + int dflags; + int xl = (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; + PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE); + PCRE2_UCHAR op = *scode; + + switch(op) + { + default: + return 0; + + case OP_BRA: + case OP_BRAPOS: + case OP_CBRA: + case OP_SCBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_ASSERT: + case OP_ONCE: + d = find_firstassertedcu(scode, &dflags, inassert + ((op==OP_ASSERT)?1:0)); + if (dflags < 0) + return 0; + if (cflags < 0) { c = d; cflags = dflags; } + else if (c != d || cflags != dflags) return 0; + break; + + case OP_EXACT: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + if (inassert == 0) return 0; + if (cflags < 0) { c = scode[1]; cflags = 0; } + else if (c != scode[1]) return 0; + break; + + case OP_EXACTI: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + if (inassert == 0) return 0; + if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; } + else if (c != scode[1]) return 0; + break; + } + + code += GET(code, 1); + } +while (*code == OP_ALT); + +*flags = cflags; +return c; +} + + + +/************************************************* +* Add an entry to the name/number table * +*************************************************/ + +/* This function is called between compiling passes to add an entry to the +name/number table, maintaining alphabetical order. Checking for permitted +and forbidden duplicates has already been done. + +Arguments: + cb the compile data block + name the name to add + length the length of the name + groupno the group number + tablecount the count of names in the table so far + +Returns: nothing +*/ + +static void +add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length, + unsigned int groupno, uint32_t tablecount) +{ +uint32_t i; +PCRE2_UCHAR *slot = cb->name_table; + +for (i = 0; i < tablecount; i++) + { + int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length)); + if (crc == 0 && slot[IMM2_SIZE+length] != 0) + crc = -1; /* Current name is a substring */ + + /* Make space in the table and break the loop for an earlier name. For a + duplicate or later name, carry on. We do this for duplicates so that in the + simple case (when ?(| is not used) they are in order of their numbers. In all + cases they are in the order in which they appear in the pattern. */ + + if (crc < 0) + { + (void)memmove(slot + cb->name_entry_size, slot, + CU2BYTES((tablecount - i) * cb->name_entry_size)); + break; + } + + /* Continue the loop for a later or duplicate name */ + + slot += cb->name_entry_size; + } + +PUT2(slot, 0, groupno); +memcpy(slot + IMM2_SIZE, name, CU2BYTES(length)); + +/* Add a terminating zero and fill the rest of the slot with zeroes so that +the memory is all initialized. Otherwise valgrind moans about uninitialized +memory when saving serialized compiled patterns. */ + +memset(slot + IMM2_SIZE + length, 0, + CU2BYTES(cb->name_entry_size - length - IMM2_SIZE)); +} + + + +/************************************************* +* Skip in parsed pattern * +*************************************************/ + +/* This function is called to skip parts of the parsed pattern when finding the +length of a lookbehind branch. It is called after (*ACCEPT) and (*FAIL) to find +the end of the branch, it is called to skip over an internal lookaround, and it +is also called to skip to the end of a class, during which it will never +encounter nested groups (but there's no need to have special code for that). + +When called to find the end of a branch or group, pptr must point to the first +meta code inside the branch, not the branch-starting code. In other cases it +can point to the item that causes the function to be called. + +Arguments: + pptr current pointer to skip from + skiptype PSKIP_CLASS when skipping to end of class + PSKIP_ALT when META_ALT ends the skip + PSKIP_KET when only META_KET ends the skip + +Returns: new value of pptr + NULL if META_END is reached - should never occur + or for an unknown meta value - likewise +*/ + +static uint32_t * +parsed_skip(uint32_t *pptr, uint32_t skiptype) +{ +uint32_t nestlevel = 0; + +for (;; pptr++) + { + uint32_t meta = META_CODE(*pptr); + + switch(meta) + { + default: /* Just skip over most items */ + if (meta < META_END) continue; /* Literal */ + break; + + /* This should never occur. */ + + case META_END: + return NULL; + + /* The data for these items is variable in length. */ + + case META_BACKREF: /* Offset is present only if group >= 10 */ + if (META_DATA(*pptr) >= 10) pptr += SIZEOFFSET; + break; + + case META_ESCAPE: /* A few escapes are followed by data items. */ + switch (META_DATA(*pptr)) + { + case ESC_P: + case ESC_p: + pptr += 1; + break; + + case ESC_g: + case ESC_k: + pptr += 1 + SIZEOFFSET; + break; + } + break; + + case META_MARK: /* Add the length of the name. */ + case META_COMMIT_ARG: + case META_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += pptr[1]; + break; + + /* These are the "active" items in this loop. */ + + case META_CLASS_END: + if (skiptype == PSKIP_CLASS) return pptr; + break; + + case META_ATOMIC: + case META_CAPTURE: + case META_COND_ASSERT: + case META_COND_DEFINE: + case META_COND_NAME: + case META_COND_NUMBER: + case META_COND_RNAME: + case META_COND_RNUMBER: + case META_COND_VERSION: + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + case META_LOOKBEHIND: + case META_LOOKBEHINDNOT: + case META_NOCAPTURE: + nestlevel++; + break; + + case META_ALT: + if (nestlevel == 0 && skiptype == PSKIP_ALT) return pptr; + break; + + case META_KET: + if (nestlevel == 0) return pptr; + nestlevel--; + break; + } + + /* The extra data item length for each meta is in a table. */ + + meta = (meta >> 16) & 0x7fff; + if (meta >= sizeof(meta_extra_lengths)) return NULL; + pptr += meta_extra_lengths[meta]; + } +/* Control never reaches here */ +return pptr; +} + + + +/************************************************* +* Find length of a parsed group * +*************************************************/ + +/* This is called for nested groups within a branch of a lookbehind whose +length is being computed. If all the branches in the nested group have the same +length, that is OK. On entry, the pointer must be at the first element after +the group initializing code. On exit it points to OP_KET. Caching is used to +improve processing speed when the same capturing group occurs many times. + +Arguments: + pptrptr pointer to pointer in the parsed pattern + isinline FALSE if a reference or recursion; TRUE for inline group + errcodeptr pointer to the errorcode + lcptr pointer to the loop counter + group number of captured group or -1 for a non-capturing group + recurses chain of recurse_check to catch mutual recursion + cb pointer to the compile data + +Returns: the group length or a negative number +*/ + +static int +get_grouplength(uint32_t **pptrptr, BOOL isinline, int *errcodeptr, int *lcptr, + int group, parsed_recurse_check *recurses, compile_block *cb) +{ +int branchlength; +int grouplength = -1; + +/* The cache can be used only if there is no possibility of there being two +groups with the same number. We do not need to set the end pointer for a group +that is being processed as a back reference or recursion, but we must do so for +an inline group. */ + +if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0) + { + uint32_t groupinfo = cb->groupinfo[group]; + if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1; + if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) + { + if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET); + return groupinfo & GI_FIXED_LENGTH_MASK; + } + } + +/* Scan the group. In this case we find the end pointer of necessity. */ + +for(;;) + { + branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb); + if (branchlength < 0) goto ISNOTFIXED; + if (grouplength == -1) grouplength = branchlength; + else if (grouplength != branchlength) goto ISNOTFIXED; + if (**pptrptr == META_KET) break; + *pptrptr += 1; /* Skip META_ALT */ + } + +if (group > 0) + cb->groupinfo[group] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength); +return grouplength; + +ISNOTFIXED: +if (group > 0) cb->groupinfo[group] |= GI_NOT_FIXED_LENGTH; +return -1; +} + + + +/************************************************* +* Find length of a parsed branch * +*************************************************/ + +/* Return a fixed length for a branch in a lookbehind, giving an error if the +length is not fixed. If any lookbehinds are encountered on the way, they get +their length set. On entry, *pptrptr points to the first element inside the +branch. On exit it is set to point to the ALT or KET. + +Arguments: + pptrptr pointer to pointer in the parsed pattern + errcodeptr pointer to error code + lcptr pointer to loop counter + recurses chain of recurse_check to catch mutual recursion + cb pointer to compile block + +Returns: the length, or a negative value on error +*/ + +static int +get_branchlength(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + parsed_recurse_check *recurses, compile_block *cb) +{ +int branchlength = 0; +int grouplength; +uint32_t lastitemlength = 0; +uint32_t *pptr = *pptrptr; +PCRE2_SIZE offset; +parsed_recurse_check this_recurse; + +/* A large and/or complex regex can take too long to process. This can happen +more often when (?| groups are present in the pattern because their length +cannot be cached. */ + +if ((*lcptr)++ > 2000) + { + *errcodeptr = ERR35; /* Lookbehind is too complicated */ + return -1; + } + +/* Scan the branch, accumulating the length. */ + +for (;; pptr++) + { + parsed_recurse_check *r; + uint32_t *gptr, *gptrend; + uint32_t escape; + uint32_t group = 0; + uint32_t itemlength = 0; + + if (*pptr < META_END) + { + itemlength = 1; + } + + else switch (META_CODE(*pptr)) + { + case META_KET: + case META_ALT: + goto EXIT; + + /* (*ACCEPT) and (*FAIL) terminate the branch, but we must skip to the + actual termination. */ + + case META_ACCEPT: + case META_FAIL: + pptr = parsed_skip(pptr, PSKIP_ALT); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + goto EXIT; + + case META_MARK: + case META_COMMIT_ARG: + case META_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += pptr[1] + 1; + break; + + case META_CIRCUMFLEX: + case META_COMMIT: + case META_DOLLAR: + case META_PRUNE: + case META_SKIP: + case META_THEN: + break; + + case META_OPTIONS: + pptr += 1; + break; + + case META_BIGVALUE: + itemlength = 1; + pptr += 1; + break; + + case META_CLASS: + case META_CLASS_NOT: + itemlength = 1; + pptr = parsed_skip(pptr, PSKIP_CLASS); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + break; + + case META_CLASS_EMPTY_NOT: + case META_DOT: + itemlength = 1; + break; + + case META_CALLOUT_NUMBER: + pptr += 3; + break; + + case META_CALLOUT_STRING: + pptr += 3 + SIZEOFFSET; + break; + + /* Only some escapes consume a character. Of those, \R and \X are never + allowed because they might match more than character. \C is allowed only in + 32-bit and non-UTF 8/16-bit modes. */ + + case META_ESCAPE: + escape = META_DATA(*pptr); + if (escape == ESC_R || escape == ESC_X) return -1; + if (escape > ESC_b && escape < ESC_Z) + { +#if PCRE2_CODE_UNIT_WIDTH != 32 + if ((cb->external_options & PCRE2_UTF) != 0 && escape == ESC_C) + { + *errcodeptr = ERR36; + return -1; + } +#endif + itemlength = 1; + if (escape == ESC_p || escape == ESC_P) pptr++; /* Skip prop data */ + } + break; + + /* Lookaheads can be ignored, but we must start the skip inside the group + so that it isn't treated as a group within the branch. */ + + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + pptr = parsed_skip(pptr + 1, PSKIP_KET); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + + /* Also ignore any qualifiers that follow a lookahead assertion. */ + + switch (pptr[1]) + { + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + pptr++; + break; + + case META_MINMAX: + case META_MINMAX_PLUS: + case META_MINMAX_QUERY: + pptr += 3; + break; + + default: + break; + } + break; + + /* Lookbehinds can be ignored, but must themselves be checked. */ + + case META_LOOKBEHIND: + case META_LOOKBEHINDNOT: + if (!set_lookbehind_lengths(&pptr, errcodeptr, lcptr, recurses, cb)) + return -1; + break; + + /* Back references and recursions are handled by very similar code. At this + stage, the names generated in the parsing pass are available, but the main + name table has not yet been created. So for the named varieties, scan the + list of names in order to get the number of the first one in the pattern, + and whether or not this name is duplicated. */ + + case META_BACKREF_BYNAME: + if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0) + goto ISNOTFIXED; + /* Fall through */ + + case META_RECURSE_BYNAME: + { + int i; + PCRE2_SPTR name; + BOOL is_dupname = FALSE; + named_group *ng = cb->named_groups; + uint32_t meta_code = META_CODE(*pptr); + uint32_t length = *(++pptr); + + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; + for (i = 0; i < cb->names_found; i++, ng++) + { + if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0) + { + group = ng->number; + is_dupname = ng->isdup; + break; + } + } + + if (group == 0) + { + *errcodeptr = ERR15; /* Non-existent subpattern */ + cb->erroroffset = offset; + return -1; + } + + /* A numerical back reference can be fixed length if duplicate capturing + groups are not being used. A non-duplicate named back reference can also + be handled. */ + + if (meta_code == META_RECURSE_BYNAME || + (!is_dupname && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)) + goto RECURSE_OR_BACKREF_LENGTH; /* Handle as a numbered version. */ + } + goto ISNOTFIXED; /* Duplicate name or number */ + + /* The offset values for back references < 10 are in a separate vector + because otherwise they would use more than two parsed pattern elements on + 64-bit systems. */ + + case META_BACKREF: + if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0 || + (cb->external_flags & PCRE2_DUPCAPUSED) != 0) + goto ISNOTFIXED; + group = META_DATA(*pptr); + if (group < 10) + { + offset = cb->small_ref_offset[group]; + goto RECURSE_OR_BACKREF_LENGTH; + } + + /* Fall through */ + /* For groups >= 10 - picking up group twice does no harm. */ + + /* A true recursion implies not fixed length, but a subroutine call may + be OK. Back reference "recursions" are also failed. */ + + case META_RECURSE: + group = META_DATA(*pptr); + GETPLUSOFFSET(offset, pptr); + + RECURSE_OR_BACKREF_LENGTH: + if (group > cb->bracount) + { + cb->erroroffset = offset; + *errcodeptr = ERR15; /* Non-existent subpattern */ + return -1; + } + if (group == 0) goto ISNOTFIXED; /* Local recursion */ + for (gptr = cb->parsed_pattern; *gptr != META_END; gptr++) + { + if (META_CODE(*gptr) == META_BIGVALUE) gptr++; + else if (*gptr == (META_CAPTURE | group)) break; + } + + /* We must start the search for the end of the group at the first meta code + inside the group. Otherwise it will be treated as an enclosed group. */ + + gptrend = parsed_skip(gptr + 1, PSKIP_KET); + if (gptrend == NULL) goto PARSED_SKIP_FAILED; + if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED; /* Local recursion */ + for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break; + if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */ + this_recurse.prev = recurses; + this_recurse.groupptr = gptr; + + /* We do not need to know the position of the end of the group, that is, + gptr is not used after the call to get_grouplength(). Setting the second + argument FALSE stops it scanning for the end when the length can be found + in the cache. */ + + gptr++; + grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group, + &this_recurse, cb); + if (grouplength < 0) + { + if (*errcodeptr == 0) goto ISNOTFIXED; + return -1; /* Error already set */ + } + itemlength = grouplength; + break; + + /* Check nested groups - advance past the initial data for each type and + then seek a fixed length with get_grouplength(). */ + + case META_COND_NAME: + case META_COND_NUMBER: + case META_COND_RNAME: + case META_COND_RNUMBER: + case META_COND_DEFINE: + pptr += 2 + SIZEOFFSET; + goto CHECK_GROUP; + + case META_COND_ASSERT: + pptr += 1; + goto CHECK_GROUP; + + case META_COND_VERSION: + pptr += 4; + goto CHECK_GROUP; + + case META_CAPTURE: + group = META_DATA(*pptr); + /* Fall through */ + + case META_ATOMIC: + case META_NOCAPTURE: + pptr++; + CHECK_GROUP: + grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group, + recurses, cb); + if (grouplength < 0) return -1; + itemlength = grouplength; + break; + + /* Exact repetition is OK; variable repetition is not. A repetition of zero + must subtract the length that has already been added. */ + + case META_MINMAX: + case META_MINMAX_PLUS: + case META_MINMAX_QUERY: + if (pptr[1] == pptr[2]) + { + if (pptr[1] == 0) branchlength -= lastitemlength; + else itemlength = (pptr[1] - 1) * lastitemlength; + pptr += 2; + break; + } + /* Fall through */ + + /* Any other item means this branch does not have a fixed length. */ + + default: + ISNOTFIXED: + *errcodeptr = ERR25; /* Not fixed length */ + return -1; + } + + /* Add the item length to the branchlength, and save it for use if the next + thing is a quantifier. */ + + branchlength += itemlength; + lastitemlength = itemlength; + + /* Ensure that the length does not overflow the limit. */ + + if (branchlength > LOOKBEHIND_MAX) + { + *errcodeptr = ERR87; + return -1; + } + } + +EXIT: +*pptrptr = pptr; +if (branchlength > cb->max_lookbehind) cb->max_lookbehind = branchlength; +return branchlength; + +PARSED_SKIP_FAILED: +*errcodeptr = ERR90; +return -1; +} + + + +/************************************************* +* Set lengths in a lookbehind * +*************************************************/ + +/* This function is called for each lookbehind, to set the lengths in its +branches. An error occurs if any branch does not have a fixed length that is +less than the maximum (65535). On exit, the pointer must be left on the final +ket. + +Arguments: + pptrptr pointer to pointer in the parsed pattern + errcodeptr pointer to error code + lcptr pointer to loop counter + recurses chain of recurse_check to catch mutual recursion + cb pointer to compile block + +Returns: TRUE if all is well + FALSE otherwise, with error code and offset set +*/ + +static BOOL +set_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + parsed_recurse_check *recurses, compile_block *cb) +{ +PCRE2_SIZE offset; +int branchlength; +uint32_t *bptr = *pptrptr; + +READPLUSOFFSET(offset, bptr); /* Offset for error messages */ +*pptrptr += SIZEOFFSET; + +do + { + *pptrptr += 1; + branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb); + if (branchlength < 0) + { + /* The errorcode and offset may already be set from a nested lookbehind. */ + if (*errcodeptr == 0) *errcodeptr = ERR25; + if (cb->erroroffset == PCRE2_UNSET) cb->erroroffset = offset; + return FALSE; + } + *bptr |= branchlength; /* branchlength never more than 65535 */ + bptr = *pptrptr; + } +while (*bptr == META_ALT); + +return TRUE; +} + + + +/************************************************* +* Check parsed pattern lookbehinds * +*************************************************/ + +/* This function is called at the end of parsing a pattern if any lookbehinds +were encountered. It scans the parsed pattern for them, calling +set_lookbehind_lengths() for each one. At the start, the errorcode is zero and +the error offset is marked unset. The enables the functions above not to +override settings from deeper nestings. + +Arguments cb points to the compile block +Returns: 0 on success, or an errorcode (cb->erroroffset will be set) +*/ + +static int +check_lookbehinds(compile_block *cb) +{ +uint32_t *pptr; +int errorcode = 0; +int loopcount = 0; + +cb->erroroffset = PCRE2_UNSET; + +for (pptr = cb->parsed_pattern; *pptr != META_END; pptr++) + { + if (*pptr < META_END) continue; /* Literal */ + + switch (META_CODE(*pptr)) + { + default: + return ERR70; /* Unrecognized meta code */ + + case META_ESCAPE: + if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p) + pptr += 1; + break; + + case META_ACCEPT: + case META_ALT: + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + case META_ATOMIC: + case META_BACKREF: + case META_CAPTURE: + case META_CIRCUMFLEX: + case META_CLASS: + case META_CLASS_EMPTY: + case META_CLASS_EMPTY_NOT: + case META_CLASS_END: + case META_CLASS_NOT: + case META_COMMIT: + case META_COND_ASSERT: + case META_DOLLAR: + case META_DOT: + case META_FAIL: + case META_KET: + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + case META_NOCAPTURE: + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + case META_PRUNE: + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + case META_RANGE_ESCAPED: + case META_RANGE_LITERAL: + case META_SKIP: + case META_THEN: + break; + + case META_RECURSE: + pptr += SIZEOFFSET; + break; + + case META_BACKREF_BYNAME: + case META_COND_DEFINE: + case META_COND_NAME: + case META_COND_NUMBER: + case META_COND_RNAME: + case META_COND_RNUMBER: + case META_RECURSE_BYNAME: + pptr += 1 + SIZEOFFSET; + break; + + case META_CALLOUT_STRING: + pptr += 3 + SIZEOFFSET; + break; + + case META_BIGVALUE: + case META_OPTIONS: + case META_POSIX: + case META_POSIX_NEG: + pptr += 1; + break; + + case META_MINMAX: + case META_MINMAX_QUERY: + case META_MINMAX_PLUS: + pptr += 2; + break; + + case META_CALLOUT_NUMBER: + case META_COND_VERSION: + pptr += 3; + break; + + case META_MARK: + case META_COMMIT_ARG: + case META_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += 1 + pptr[1]; + break; + + case META_LOOKBEHIND: + case META_LOOKBEHINDNOT: + if (!set_lookbehind_lengths(&pptr, &errorcode, &loopcount, NULL, cb)) + return errorcode; + break; + } + } + +return 0; +} + + + +/************************************************* +* External function to compile a pattern * +*************************************************/ + +/* This function reads a regular expression in the form of a string and returns +a pointer to a block of store holding a compiled version of the expression. + +Arguments: + pattern the regular expression + patlen the length of the pattern, or PCRE2_ZERO_TERMINATED + options option bits + errorptr pointer to errorcode + erroroffset pointer to error offset + ccontext points to a compile context or is NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorcode and erroroffset set +*/ + +PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION +pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options, + int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext) +{ +BOOL utf; /* Set TRUE for UTF mode */ +BOOL has_lookbehind = FALSE; /* Set TRUE if a lookbehind is found */ +BOOL zero_terminated; /* Set TRUE for zero-terminated pattern */ +pcre2_real_code *re = NULL; /* What we will return */ +compile_block cb; /* "Static" compile-time data */ +const uint8_t *tables; /* Char tables base pointer */ + +PCRE2_UCHAR *code; /* Current pointer in compiled code */ +PCRE2_SPTR codestart; /* Start of compiled code */ +PCRE2_SPTR ptr; /* Current pointer in pattern */ +uint32_t *pptr; /* Current pointer in parsed pattern */ + +PCRE2_SIZE length = 1; /* Allow for final END opcode */ +PCRE2_SIZE usedlength; /* Actual length used */ +PCRE2_SIZE re_blocksize; /* Size of memory block */ +PCRE2_SIZE big32count = 0; /* 32-bit literals >= 0x80000000 */ +PCRE2_SIZE parsed_size_needed; /* Needed for parsed pattern */ + +int32_t firstcuflags, reqcuflags; /* Type of first/req code unit */ +uint32_t firstcu, reqcu; /* Value of first/req code unit */ +uint32_t setflags = 0; /* NL and BSR set flags */ + +uint32_t skipatstart; /* When checking (*UTF) etc */ +uint32_t limit_heap = UINT32_MAX; +uint32_t limit_match = UINT32_MAX; /* Unset match limits */ +uint32_t limit_depth = UINT32_MAX; + +int newline = 0; /* Unset; can be set by the pattern */ +int bsr = 0; /* Unset; can be set by the pattern */ +int errorcode = 0; /* Initialize to avoid compiler warn */ +int regexrc; /* Return from compile */ + +uint32_t i; /* Local loop counter */ + +/* Comments at the head of this file explain about these variables. */ + +uint32_t stack_groupinfo[GROUPINFO_DEFAULT_SIZE]; +uint32_t stack_parsed_pattern[PARSED_PATTERN_DEFAULT_SIZE]; +named_group named_groups[NAMED_GROUP_LIST_SIZE]; + +/* The workspace is used in different ways in the different compiling phases. +It needs to be 16-bit aligned for the preliminary parsing scan. */ + +uint32_t c16workspace[C16_WORK_SIZE]; +PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace; + + +/* -------------- Check arguments and set up the pattern ----------------- */ + +/* There must be error code and offset pointers. */ + +if (errorptr == NULL || erroroffset == NULL) return NULL; +*errorptr = ERR0; +*erroroffset = 0; + +/* There must be a pattern! */ + +if (pattern == NULL) + { + *errorptr = ERR16; + return NULL; + } + +/* A NULL compile context means "use a default context" */ + +if (ccontext == NULL) + ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context)); + +/* Check that all undefined public option bits are zero. */ + +if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 || + (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0) + { + *errorptr = ERR17; + return NULL; + } + +if ((options & PCRE2_LITERAL) != 0 && + ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 || + (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0)) + { + *errorptr = ERR92; + return NULL; + } + +/* A zero-terminated pattern is indicated by the special length value +PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */ + +if ((zero_terminated = (patlen == PCRE2_ZERO_TERMINATED))) + patlen = PRIV(strlen)(pattern); + +if (patlen > ccontext->max_pattern_length) + { + *errorptr = ERR88; + return NULL; + } + +/* From here on, all returns from this function should end up going via the +EXIT label. */ + + +/* ------------ Initialize the "static" compile data -------------- */ + +tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables); + +cb.lcc = tables + lcc_offset; /* Individual */ +cb.fcc = tables + fcc_offset; /* character */ +cb.cbits = tables + cbits_offset; /* tables */ +cb.ctypes = tables + ctypes_offset; + +cb.assert_depth = 0; +cb.bracount = 0; +cb.cx = ccontext; +cb.dupnames = FALSE; +cb.end_pattern = pattern + patlen; +cb.erroroffset = 0; +cb.external_flags = 0; +cb.external_options = options; +cb.groupinfo = stack_groupinfo; +cb.had_recurse = FALSE; +cb.lastcapture = 0; +cb.max_lookbehind = 0; +cb.name_entry_size = 0; +cb.name_table = NULL; +cb.named_groups = named_groups; +cb.named_group_list_size = NAMED_GROUP_LIST_SIZE; +cb.names_found = 0; +cb.open_caps = NULL; +cb.parens_depth = 0; +cb.parsed_pattern = stack_parsed_pattern; +cb.req_varyopt = 0; +cb.start_code = cworkspace; +cb.start_pattern = pattern; +cb.start_workspace = cworkspace; +cb.workspace_size = COMPILE_WORK_SIZE; + +/* Maximum back reference and backref bitmap. The bitmap records up to 31 back +references to help in deciding whether (.*) can be treated as anchored or not. +*/ + +cb.top_backref = 0; +cb.backref_map = 0; + +/* Escape sequences \1 to \9 are always back references, but as they are only +two characters long, only two elements can be used in the parsed_pattern +vector. The first contains the reference, and we'd like to use the second to +record the offset in the pattern, so that forward references to non-existent +groups can be diagnosed later with an offset. However, on 64-bit systems, +PCRE2_SIZE won't fit. Instead, we have a vector of offsets for the first +occurrence of \1 to \9, indexed by the second parsed_pattern value. All other +references have enough space for the offset to be put into the parsed pattern. +*/ + +for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET; + + +/* --------------- Start looking at the pattern --------------- */ + +/* Unless PCRE2_LITERAL is set, check for global one-time option settings at +the start of the pattern, and remember the offset to the actual regex. With +valgrind support, make the terminator of a zero-terminated pattern +inaccessible. This catches bugs that would otherwise only show up for +non-zero-terminated patterns. */ + +#ifdef SUPPORT_VALGRIND +if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1)); +#endif + +ptr = pattern; +skipatstart = 0; + +if ((options & PCRE2_LITERAL) == 0) + { + while (patlen - skipatstart >= 2 && + ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && + ptr[skipatstart+1] == CHAR_ASTERISK) + { + for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) + { + uint32_t c, pp; + pso *p = pso_list + i; + + if (patlen - skipatstart - 2 >= p->length && + PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name), + p->length) == 0) + { + skipatstart += p->length + 2; + switch(p->type) + { + case PSO_OPT: + cb.external_options |= p->value; + break; + + case PSO_FLG: + setflags |= p->value; + break; + + case PSO_NL: + newline = p->value; + setflags |= PCRE2_NL_SET; + break; + + case PSO_BSR: + bsr = p->value; + setflags |= PCRE2_BSR_SET; + break; + + case PSO_LIMM: + case PSO_LIMD: + case PSO_LIMH: + c = 0; + pp = skipatstart; + if (!IS_DIGIT(ptr[pp])) + { + errorcode = ERR60; + ptr += pp; + goto HAD_EARLY_ERROR; + } + while (IS_DIGIT(ptr[pp])) + { + if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */ + c = c*10 + (ptr[pp++] - CHAR_0); + } + if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR60; + ptr += pp; + goto HAD_EARLY_ERROR; + } + if (p->type == PSO_LIMH) limit_heap = c; + else if (p->type == PSO_LIMM) limit_match = c; + else limit_depth = c; + skipatstart += pp - skipatstart; + break; + } + break; /* Out of the table scan loop */ + } + } + if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ + } + } + +/* End of pattern-start options; advance to start of real regex. */ + +ptr += skipatstart; + +/* Can't support UTF or UCP unless PCRE2 has been compiled with UTF support. */ + +#ifndef SUPPORT_UNICODE +if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) + { + errorcode = ERR32; + goto HAD_EARLY_ERROR; + } +#endif + +/* Check UTF. We have the original options in 'options', with that value as +modified by (*UTF) etc in cb->external_options. The extra option +PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the +surrogate code points cannot be represented in UTF-16. */ + +utf = (cb.external_options & PCRE2_UTF) != 0; +if (utf) + { + if ((options & PCRE2_NEVER_UTF) != 0) + { + errorcode = ERR74; + goto HAD_EARLY_ERROR; + } + if ((options & PCRE2_NO_UTF_CHECK) == 0 && + (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) + goto HAD_ERROR; /* Offset was set by valid_utf() */ + +#if PCRE2_CODE_UNIT_WIDTH == 16 + if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0) + { + errorcode = ERR91; + goto HAD_EARLY_ERROR; + } +#endif + } + +/* Check UCP lockout. */ + +if ((cb.external_options & (PCRE2_UCP|PCRE2_NEVER_UCP)) == + (PCRE2_UCP|PCRE2_NEVER_UCP)) + { + errorcode = ERR75; + goto HAD_EARLY_ERROR; + } + +/* Process the BSR setting. */ + +if (bsr == 0) bsr = ccontext->bsr_convention; + +/* Process the newline setting. */ + +if (newline == 0) newline = ccontext->newline_convention; +cb.nltype = NLTYPE_FIXED; +switch(newline) + { + case PCRE2_NEWLINE_CR: + cb.nllen = 1; + cb.nl[0] = CHAR_CR; + break; + + case PCRE2_NEWLINE_LF: + cb.nllen = 1; + cb.nl[0] = CHAR_NL; + break; + + case PCRE2_NEWLINE_NUL: + cb.nllen = 1; + cb.nl[0] = CHAR_NUL; + break; + + case PCRE2_NEWLINE_CRLF: + cb.nllen = 2; + cb.nl[0] = CHAR_CR; + cb.nl[1] = CHAR_NL; + break; + + case PCRE2_NEWLINE_ANY: + cb.nltype = NLTYPE_ANY; + break; + + case PCRE2_NEWLINE_ANYCRLF: + cb.nltype = NLTYPE_ANYCRLF; + break; + + default: + errorcode = ERR56; + goto HAD_EARLY_ERROR; + } + +/* Pre-scan the pattern to do two things: (1) Discover the named groups and +their numerical equivalents, so that this information is always available for +the remaining processing. (2) At the same time, parse the pattern and put a +processed version into the parsed_pattern vector. This has escapes interpreted +and comments removed (amongst other things). + +In all but one case, when PCRE2_AUTO_CALLOUT is not set, the number of unsigned +32-bit ints in the parsed pattern is bounded by the length of the pattern plus +one (for the terminator) plus four if PCRE2_EXTRA_WORD or PCRE2_EXTRA_LINE is +set. The exceptional case is when running in 32-bit, non-UTF mode, when literal +characters greater than META_END (0x80000000) have to be coded as two units. In +this case, therefore, we scan the pattern to check for such values. */ + +#if PCRE2_CODE_UNIT_WIDTH == 32 +if (!utf) + { + PCRE2_SPTR p; + for (p = ptr; p < cb.end_pattern; p++) if (*p >= META_END) big32count++; + } +#endif + +/* Ensure that the parsed pattern buffer is big enough. When PCRE2_AUTO_CALLOUT +is set we have to assume a numerical callout (4 elements) for each character +plus one at the end. This is overkill, but memory is plentiful these days. For +many smaller patterns the vector on the stack (which was set up above) can be +used. */ + +parsed_size_needed = patlen - skipatstart + big32count; + +if ((ccontext->extra_options & + (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0) + parsed_size_needed += 4; + +if ((options & PCRE2_AUTO_CALLOUT) != 0) + parsed_size_needed = (parsed_size_needed + 1) * 5; + +if (parsed_size_needed >= PARSED_PATTERN_DEFAULT_SIZE) + { + uint32_t *heap_parsed_pattern = ccontext->memctl.malloc( + (parsed_size_needed + 1) * sizeof(uint32_t), ccontext->memctl.memory_data); + if (heap_parsed_pattern == NULL) + { + *errorptr = ERR21; + goto EXIT; + } + cb.parsed_pattern = heap_parsed_pattern; + } +cb.parsed_pattern_end = cb.parsed_pattern + parsed_size_needed + 1; + +/* Do the parsing scan. */ + +errorcode = parse_regex(ptr, cb.external_options, &has_lookbehind, &cb); +if (errorcode != 0) goto HAD_CB_ERROR; + +/* Workspace is needed to remember information about numbered groups: whether a +group can match an empty string and what its fixed length is. This is done to +avoid the possibility of recursive references causing very long compile times +when checking these features. Unnumbered groups do not have this exposure since +they cannot be referenced. We use an indexed vector for this purpose. If there +are sufficiently few groups, the default vector on the stack, as set up above, +can be used. Otherwise we have to get/free a special vector. The vector must be +initialized to zero. */ + +if (cb.bracount >= GROUPINFO_DEFAULT_SIZE) + { + cb.groupinfo = ccontext->memctl.malloc( + (cb.bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data); + if (cb.groupinfo == NULL) + { + errorcode = ERR21; + cb.erroroffset = 0; + goto HAD_CB_ERROR; + } + } +memset(cb.groupinfo, 0, (cb.bracount + 1) * sizeof(uint32_t)); + +/* If there were any lookbehinds, scan the parsed pattern to figure out their +lengths. */ + +if (has_lookbehind) + { + errorcode = check_lookbehinds(&cb); + if (errorcode != 0) goto HAD_CB_ERROR; + } + +/* For debugging, there is a function that shows the parsed data vector. */ + +#ifdef DEBUG_SHOW_PARSED +fprintf(stderr, "+++ Pre-scan complete:\n"); +show_parsed(&cb); +#endif + +/* For debugging capturing information this code can be enabled. */ + +#ifdef DEBUG_SHOW_CAPTURES + { + named_group *ng = cb.named_groups; + fprintf(stderr, "+++Captures: %d\n", cb.bracount); + for (i = 0; i < cb.names_found; i++, ng++) + { + fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name); + } + } +#endif + +/* Pretend to compile the pattern while actually just accumulating the amount +of memory required in the 'length' variable. This behaviour is triggered by +passing a non-NULL final argument to compile_regex(). We pass a block of +workspace (cworkspace) for it to compile parts of the pattern into; the +compiled code is discarded when it is no longer needed, so hopefully this +workspace will never overflow, though there is a test for its doing so. + +On error, errorcode will be set non-zero, so we don't need to look at the +result of the function. The initial options have been put into the cb block, +but we still have to pass a separate options variable (the first argument) +because the options may change as the pattern is processed. */ + +cb.erroroffset = patlen; /* For any subsequent errors that do not set it */ +pptr = cb.parsed_pattern; +code = cworkspace; +*code = OP_BRA; + +(void)compile_regex(cb.external_options, &code, &pptr, &errorcode, 0, &firstcu, + &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, &length); + +if (errorcode != 0) goto HAD_CB_ERROR; /* Offset is in cb.erroroffset */ + +/* This should be caught in compile_regex(), but just in case... */ + +if (length > MAX_PATTERN_SIZE) + { + errorcode = ERR20; + goto HAD_CB_ERROR; + } + +/* Compute the size of, and then get and initialize, the data block for storing +the compiled pattern and names table. Integer overflow should no longer be +possible because nowadays we limit the maximum value of cb.names_found and +cb.name_entry_size. */ + +re_blocksize = sizeof(pcre2_real_code) + + CU2BYTES(length + + (PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size); +re = (pcre2_real_code *) + ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data); +if (re == NULL) + { + errorcode = ERR21; + goto HAD_CB_ERROR; + } + +/* The compiler may put padding at the end of the pcre2_real_code structure in +order to round it up to a multiple of 4 or 8 bytes. This means that when a +compiled pattern is copied (for example, when serialized) undefined bytes are +read, and this annoys debuggers such as valgrind. To avoid this, we explicitly +write to the last 8 bytes of the structure before setting the fields. */ + +memset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8); +re->memctl = ccontext->memctl; +re->tables = tables; +re->executable_jit = NULL; +memset(re->start_bitmap, 0, 32 * sizeof(uint8_t)); +re->blocksize = re_blocksize; +re->magic_number = MAGIC_NUMBER; +re->compile_options = options; +re->overall_options = cb.external_options; +re->extra_options = ccontext->extra_options; +re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags; +re->limit_heap = limit_heap; +re->limit_match = limit_match; +re->limit_depth = limit_depth; +re->first_codeunit = 0; +re->last_codeunit = 0; +re->bsr_convention = bsr; +re->newline_convention = newline; +re->max_lookbehind = 0; +re->minlength = 0; +re->top_bracket = 0; +re->top_backref = 0; +re->name_entry_size = cb.name_entry_size; +re->name_count = cb.names_found; + +/* The basic block is immediately followed by the name table, and the compiled +code follows after that. */ + +codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) + + re->name_entry_size * re->name_count; + +/* Update the compile data block for the actual compile. The starting points of +the name/number translation table and of the code are passed around in the +compile data block. The start/end pattern and initial options are already set +from the pre-compile phase, as is the name_entry_size field. */ + +cb.parens_depth = 0; +cb.assert_depth = 0; +cb.lastcapture = 0; +cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); +cb.start_code = codestart; +cb.req_varyopt = 0; +cb.had_accept = FALSE; +cb.had_pruneorskip = FALSE; +cb.open_caps = NULL; + +/* If any named groups were found, create the name/number table from the list +created in the pre-pass. */ + +if (cb.names_found > 0) + { + named_group *ng = cb.named_groups; + for (i = 0; i < cb.names_found; i++, ng++) + add_name_to_table(&cb, ng->name, ng->length, ng->number, i); + } + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, errorcode will be set non-zero, so we don't need to look at the result +of the function here. */ + +pptr = cb.parsed_pattern; +code = (PCRE2_UCHAR *)codestart; +*code = OP_BRA; +regexrc = compile_regex(re->overall_options, &code, &pptr, &errorcode, 0, + &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL); +if (regexrc < 0) re->flags |= PCRE2_MATCH_EMPTY; +re->top_bracket = cb.bracount; +re->top_backref = cb.top_backref; +re->max_lookbehind = cb.max_lookbehind; + +if (cb.had_accept) + { + reqcu = 0; /* Must disable after (*ACCEPT) */ + reqcuflags = REQ_NONE; + } + +/* Fill in the final opcode and check for disastrous overflow. If no overflow, +but the estimated length exceeds the really used length, adjust the value of +re->blocksize, and if valgrind support is configured, mark the extra allocated +memory as unaddressable, so that any out-of-bound reads can be detected. */ + +*code++ = OP_END; +usedlength = code - codestart; +if (usedlength > length) errorcode = ERR23; else + { + re->blocksize -= CU2BYTES(length - usedlength); +#ifdef SUPPORT_VALGRIND + VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength)); +#endif + } + +/* Scan the pattern for recursion/subroutine calls and convert the group +numbers into offsets. Maintain a small cache so that repeated groups containing +recursions are efficiently handled. */ + +#define RSCAN_CACHE_SIZE 8 + +if (errorcode == 0 && cb.had_recurse) + { + PCRE2_UCHAR *rcode; + PCRE2_SPTR rgroup; + unsigned int ccount = 0; + int start = RSCAN_CACHE_SIZE; + recurse_cache rc[RSCAN_CACHE_SIZE]; + + for (rcode = (PCRE2_UCHAR *)find_recurse(codestart, utf); + rcode != NULL; + rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf)) + { + int p, groupnumber; + + groupnumber = (int)GET(rcode, 1); + if (groupnumber == 0) rgroup = codestart; else + { + PCRE2_SPTR search_from = codestart; + rgroup = NULL; + for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7) + { + if (groupnumber == rc[p].groupnumber) + { + rgroup = rc[p].group; + break; + } + + /* Group n+1 must always start to the right of group n, so we can save + search time below when the new group number is greater than any of the + previously found groups. */ + + if (groupnumber > rc[p].groupnumber) search_from = rc[p].group; + } + + if (rgroup == NULL) + { + rgroup = PRIV(find_bracket)(search_from, utf, groupnumber); + if (rgroup == NULL) + { + errorcode = ERR53; + break; + } + if (--start < 0) start = RSCAN_CACHE_SIZE - 1; + rc[start].groupnumber = groupnumber; + rc[start].group = rgroup; + if (ccount < RSCAN_CACHE_SIZE) ccount++; + } + } + + PUT(rcode, 1, rgroup - codestart); + } + } + +/* In rare debugging situations we sometimes need to look at the compiled code +at this stage. */ + +#ifdef DEBUG_CALL_PRINTINT +pcre2_printint(re, stderr, TRUE); +fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength); +#endif + +/* Unless disabled, check whether any single character iterators can be +auto-possessified. The function overwrites the appropriate opcode values, so +the type of the pointer must be cast. NOTE: the intermediate variable "temp" is +used in this code because at least one compiler gives a warning about loss of +"const" attribute if the cast (PCRE2_UCHAR *)codestart is used directly in the +function call. */ + +if (errorcode == 0 && (re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0) + { + PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart; + if (PRIV(auto_possessify)(temp, utf, &cb) != 0) errorcode = ERR80; + } + +/* Failed to compile, or error while post-processing. */ + +if (errorcode != 0) goto HAD_CB_ERROR; + +/* Successful compile. If the anchored option was not passed, set it if +we can determine that the pattern is anchored by virtue of ^ characters or \A +or anything else, such as starting with non-atomic .* when DOTALL is set and +there are no occurrences of *PRUNE or *SKIP (though there is an option to +disable this case). */ + +if ((re->overall_options & PCRE2_ANCHORED) == 0 && + is_anchored(codestart, 0, &cb, 0, FALSE)) + re->overall_options |= PCRE2_ANCHORED; + +/* Set up the first code unit or startline flag, the required code unit, and +then study the pattern. This code need not be obeyed if PCRE2_NO_START_OPTIMIZE +is set, as the data it would create will not be used. Note that a first code +unit (but not the startline flag) is useful for anchored patterns because it +can still give a quick "no match" and also avoid searching for a last code +unit. */ + +if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) + { + /* If we do not have a first code unit, see if there is one that is asserted + (these are not saved during the compile because they can cause conflicts with + actual literals that follow). */ + + if (firstcuflags < 0) + firstcu = find_firstassertedcu(codestart, &firstcuflags, 0); + + /* Save the data for a first code unit. */ + + if (firstcuflags >= 0) + { + re->first_codeunit = firstcu; + re->flags |= PCRE2_FIRSTSET; + + /* Handle caseless first code units. */ + + if ((firstcuflags & REQ_CASELESS) != 0) + { + if (firstcu < 128 || (!utf && firstcu < 255)) + { + if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS; + } + + /* The first code unit is > 128 in UTF mode, or > 255 otherwise. In + 8-bit UTF mode, codepoints in the range 128-255 are introductory code + points and cannot have another case. In 16-bit and 32-bit modes, we can + check wide characters when UTF (and therefore UCP) is supported. */ + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 + else if (firstcu <= MAX_UTF_CODE_POINT && + UCD_OTHERCASE(firstcu) != firstcu) + re->flags |= PCRE2_FIRSTCASELESS; +#endif + } + } + + /* When there is no first code unit, for non-anchored patterns, see if we can + set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all + branches start with ^ and also when all branches start with non-atomic .* for + non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option + that disables this case.) */ + + else if ((re->overall_options & PCRE2_ANCHORED) == 0 && + is_startline(codestart, 0, &cb, 0, FALSE)) + re->flags |= PCRE2_STARTLINE; + + /* Handle the "required code unit", if one is set. In the case of an anchored + pattern, do this only if it follows a variable length item in the pattern. */ + + if (reqcuflags >= 0 && + ((re->overall_options & PCRE2_ANCHORED) == 0 || + (reqcuflags & REQ_VARY) != 0)) + { + re->last_codeunit = reqcu; + re->flags |= PCRE2_LASTSET; + + /* Handle caseless required code units as for first code units (above). */ + + if ((reqcuflags & REQ_CASELESS) != 0) + { + if (reqcu < 128 || (!utf && reqcu < 255)) + { + if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS; + } +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 + else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu) + re->flags |= PCRE2_LASTCASELESS; +#endif + } + } + + /* Finally, study the compiled pattern to set up information such as a bitmap + of starting code units and a minimum matching length. */ + + if (PRIV(study)(re) != 0) + { + errorcode = ERR31; + goto HAD_CB_ERROR; + } + } /* End of start-of-match optimizations. */ + +/* Control ends up here in all cases. When running under valgrind, make a +pattern's terminating zero defined again. If memory was obtained for the parsed +version of the pattern, free it before returning. Also free the list of named +groups if a larger one had to be obtained, and likewise the group information +vector. */ + +EXIT: +#ifdef SUPPORT_VALGRIND +if (zero_terminated) VALGRIND_MAKE_MEM_DEFINED(pattern + patlen, CU2BYTES(1)); +#endif +if (cb.parsed_pattern != stack_parsed_pattern) + ccontext->memctl.free(cb.parsed_pattern, ccontext->memctl.memory_data); +if (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE) + ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data); +if (cb.groupinfo != stack_groupinfo) + ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data); +return re; /* Will be NULL after an error */ + +/* Errors discovered in parse_regex() set the offset value in the compile +block. Errors discovered before it is called must compute it from the ptr +value. After parse_regex() is called, the offset in the compile block is set to +the end of the pattern, but certain errors in compile_regex() may reset it if +an offset is available in the parsed pattern. */ + +HAD_CB_ERROR: +ptr = pattern + cb.erroroffset; + +HAD_EARLY_ERROR: +*erroroffset = ptr - pattern; + +HAD_ERROR: +*errorptr = errorcode; +pcre2_code_free(re); +re = NULL; +goto EXIT; +} + +/* End of pcre2_compile.c */ diff --git a/ProcessHacker/pcre/pcre2_config.c b/ProcessHacker/pcre/pcre2_config.c index 89223ae957bc..e487b1022070 100644 --- a/ProcessHacker/pcre/pcre2_config.c +++ b/ProcessHacker/pcre/pcre2_config.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -85,13 +84,16 @@ if (where == NULL) /* Requests a length */ return PCRE2_ERROR_BADOPTION; case PCRE2_CONFIG_BSR: + case PCRE2_CONFIG_COMPILED_WIDTHS: + case PCRE2_CONFIG_DEPTHLIMIT: + case PCRE2_CONFIG_HEAPLIMIT: case PCRE2_CONFIG_JIT: case PCRE2_CONFIG_LINKSIZE: case PCRE2_CONFIG_MATCHLIMIT: + case PCRE2_CONFIG_NEVER_BACKSLASH_C: case PCRE2_CONFIG_NEWLINE: case PCRE2_CONFIG_PARENSLIMIT: - case PCRE2_CONFIG_RECURSIONLIMIT: - case PCRE2_CONFIG_STACKRECURSE: + case PCRE2_CONFIG_STACKRECURSE: /* Obsolete */ case PCRE2_CONFIG_UNICODE: return sizeof(uint32_t); @@ -117,6 +119,28 @@ switch (what) #endif break; + case PCRE2_CONFIG_COMPILED_WIDTHS: + *((uint32_t *)where) = 0 +#ifdef SUPPORT_PCRE2_8 + + 1 +#endif +#ifdef SUPPORT_PCRE2_16 + + 2 +#endif +#ifdef SUPPORT_PCRE2_32 + + 4 +#endif + ; + break; + + case PCRE2_CONFIG_DEPTHLIMIT: + *((uint32_t *)where) = MATCH_LIMIT_DEPTH; + break; + + case PCRE2_CONFIG_HEAPLIMIT: + *((uint32_t *)where) = HEAP_LIMIT; + break; + case PCRE2_CONFIG_JIT: #ifdef SUPPORT_JIT *((uint32_t *)where) = 1; @@ -148,20 +172,23 @@ switch (what) *((uint32_t *)where) = NEWLINE_DEFAULT; break; + case PCRE2_CONFIG_NEVER_BACKSLASH_C: +#ifdef NEVER_BACKSLASH_C + *((uint32_t *)where) = 1; +#else + *((uint32_t *)where) = 0; +#endif + break; + case PCRE2_CONFIG_PARENSLIMIT: *((uint32_t *)where) = PARENS_NEST_LIMIT; break; - case PCRE2_CONFIG_RECURSIONLIMIT: - *((uint32_t *)where) = MATCH_LIMIT_RECURSION; - break; + /* This is now obsolete. The stack is no longer used via recursion for + handling backtracking in pcre2_match(). */ case PCRE2_CONFIG_STACKRECURSE: -#ifdef HEAP_MATCH_RECURSE *((uint32_t *)where) = 0; -#else - *((uint32_t *)where) = 1; -#endif break; case PCRE2_CONFIG_UNICODE_VERSION: diff --git a/ProcessHacker/pcre/pcre2_context.c b/ProcessHacker/pcre/pcre2_context.c index 096e8ee2f725..88909136efe6 100644 --- a/ProcessHacker/pcre/pcre2_context.c +++ b/ProcessHacker/pcre/pcre2_context.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -40,7 +40,6 @@ POSSIBILITY OF SUCH DAMAGE. #include -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -88,7 +87,7 @@ extern void * PRIV(memctl_malloc)(size_t size, pcre2_memctl *memctl) { pcre2_memctl *newmemctl; -void *yield = (memctl == NULL)? malloc(size) : +void *yield = (memctl == NULL)? PhAllocateSafe(size) : memctl->malloc(size, memctl->memory_data); if (yield == NULL) return NULL; newmemctl = (pcre2_memctl *)yield; @@ -140,7 +139,8 @@ const pcre2_compile_context PRIV(default_compile_context) = { PCRE2_UNSET, /* Max pattern length */ BSR_DEFAULT, /* Backslash R default */ NEWLINE_DEFAULT, /* Newline convention */ - PARENS_NEST_LIMIT }; /* As it says */ + PARENS_NEST_LIMIT, /* As it says */ + 0 }; /* Extra options */ /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ @@ -163,9 +163,6 @@ when no context is supplied to a match function. */ const pcre2_match_context PRIV(default_match_context) = { { default_malloc, default_free, NULL }, -#ifdef HEAP_MATCH_RECURSE - { default_malloc, default_free, NULL }, -#endif #ifdef SUPPORT_JIT NULL, NULL, @@ -173,8 +170,9 @@ const pcre2_match_context PRIV(default_match_context) = { NULL, NULL, PCRE2_UNSET, /* Offset limit */ + HEAP_LIMIT, MATCH_LIMIT, - MATCH_LIMIT_RECURSION }; + MATCH_LIMIT_DEPTH }; /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ @@ -192,6 +190,36 @@ return mcontext; } +/* A default convert context is set up to save having to initialize at run time +when no context is supplied to the convert function. */ + +const pcre2_convert_context PRIV(default_convert_context) = { + { default_malloc, default_free, NULL }, /* Default memory handling */ +#ifdef _WIN32 + CHAR_BACKSLASH, /* Default path separator */ + CHAR_GRAVE_ACCENT /* Default escape character */ +#else /* Not Windows */ + CHAR_SLASH, /* Default path separator */ + CHAR_BACKSLASH /* Default escape character */ +#endif + }; + +/* The create function copies the default into the new memory, but must +override the default memory handling functions if a gcontext was provided. */ + +PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION +pcre2_convert_context_create(pcre2_general_context *gcontext) +{ +pcre2_convert_context *ccontext = PRIV(memctl_malloc)( + sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext); +if (ccontext == NULL) return NULL; +*ccontext = PRIV(default_convert_context); +if (gcontext != NULL) + *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext); +return ccontext; +} + + /************************************************* * Context copy functions * *************************************************/ @@ -233,11 +261,22 @@ return new; +PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION +pcre2_convert_context_copy(pcre2_convert_context *ccontext) +{ +pcre2_convert_context *new = + ccontext->memctl.malloc(sizeof(pcre2_real_convert_context), + ccontext->memctl.memory_data); +if (new == NULL) return NULL; +memcpy(new, ccontext, sizeof(pcre2_real_convert_context)); +return new; +} + + /************************************************* * Context free functions * *************************************************/ - PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_general_context_free(pcre2_general_context *gcontext) { @@ -262,6 +301,12 @@ if (mcontext != NULL) } +PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION +pcre2_convert_context_free(pcre2_convert_context *ccontext) +{ +if (ccontext != NULL) + ccontext->memctl.free(ccontext, ccontext->memctl.memory_data); +} /************************************************* @@ -273,7 +318,7 @@ data is given. Only some of the functions are able to test the validity of the data. */ -/* ------------ Compile contexts ------------ */ +/* ------------ Compile context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_character_tables(pcre2_compile_context *ccontext, @@ -315,6 +360,7 @@ switch(newline) case PCRE2_NEWLINE_CRLF: case PCRE2_NEWLINE_ANY: case PCRE2_NEWLINE_ANYCRLF: + case PCRE2_NEWLINE_NUL: ccontext->newline_convention = newline; return 0; @@ -330,6 +376,13 @@ ccontext->parens_nest_limit = limit; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options) +{ +ccontext->extra_options = options; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext, int (*guard)(uint32_t, void *), void *user_data) @@ -340,7 +393,7 @@ return 0; } -/* ------------ Match contexts ------------ */ +/* ------------ Match context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_callout(pcre2_match_context *mcontext, @@ -351,6 +404,13 @@ mcontext->callout_data = callout_data; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit) +{ +mcontext->heap_limit = limit; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit) { @@ -358,6 +418,13 @@ mcontext->match_limit = limit; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit) +{ +mcontext->depth_limit = limit; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit) { @@ -365,11 +432,13 @@ mcontext->offset_limit = limit; return 0; } +/* This function became obsolete at release 10.30. It is kept as a synonym for +backwards compatibility. */ + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit) { -mcontext->recursion_limit = limit; -return 0; +return pcre2_set_depth_limit(mcontext, limit); } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION @@ -377,17 +446,32 @@ pcre2_set_recursion_memory_management(pcre2_match_context *mcontext, void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *), void *mydata) { -#ifdef HEAP_MATCH_RECURSE -mcontext->stack_memctl.malloc = mymalloc; -mcontext->stack_memctl.free = myfree; -mcontext->stack_memctl.memory_data = mydata; -#else (void)mcontext; (void)mymalloc; (void)myfree; (void)mydata; -#endif +return 0; +} + +/* ------------ Convert context ------------ */ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator) +{ +if (separator != CHAR_SLASH && separator != CHAR_BACKSLASH && + separator != CHAR_DOT) return PCRE2_ERROR_BADDATA; +ccontext->glob_separator = separator; +return 0; +} + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape) +{ +if (escape > 255 || (escape != 0 && !ispunct(escape))) + return PCRE2_ERROR_BADDATA; +ccontext->glob_escape = escape; return 0; } /* End of pcre2_context.c */ + diff --git a/ProcessHacker/pcre/pcre2_convert.c b/ProcessHacker/pcre/pcre2_convert.c new file mode 100644 index 000000000000..1dd5c337dc3c --- /dev/null +++ b/ProcessHacker/pcre/pcre2_convert.c @@ -0,0 +1,1182 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre2_internal.h" + +#define TYPE_OPTIONS (PCRE2_CONVERT_GLOB| \ + PCRE2_CONVERT_POSIX_BASIC|PCRE2_CONVERT_POSIX_EXTENDED) + +#define ALL_OPTIONS (PCRE2_CONVERT_UTF|PCRE2_CONVERT_NO_UTF_CHECK| \ + PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR| \ + PCRE2_CONVERT_GLOB_NO_STARSTAR| \ + TYPE_OPTIONS) + +#define DUMMY_BUFFER_SIZE 100 + +/* Generated pattern fragments */ + +#define STR_BACKSLASH_A STR_BACKSLASH STR_A +#define STR_BACKSLASH_z STR_BACKSLASH STR_z +#define STR_COLON_RIGHT_SQUARE_BRACKET STR_COLON STR_RIGHT_SQUARE_BRACKET +#define STR_DOT_STAR_LOOKBEHIND STR_DOT STR_ASTERISK STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_LESS_THAN_SIGN STR_EQUALS_SIGN +#define STR_LOOKAHEAD_NOT_DOT STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_EXCLAMATION_MARK STR_BACKSLASH STR_DOT STR_RIGHT_PARENTHESIS +#define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS +#define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS + +/* States for range and POSIX processing */ + +enum { RANGE_NOT_STARTED, RANGE_STARTING, RANGE_STARTED }; +enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET, + POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED }; + +/* Macro to add a character string to the output buffer, checking for overflow. */ + +#define PUTCHARS(string) \ + { \ + for (s = (char *)(string); *s != 0; s++) \ + { \ + if (p >= endp) return PCRE2_ERROR_NOMEMORY; \ + *p++ = *s; \ + } \ + } + +/* Literals that must be escaped: \ ? * + | . ^ $ { } [ ] ( ) */ + +static const char *pcre2_escaped_literals = + STR_BACKSLASH STR_QUESTION_MARK STR_ASTERISK STR_PLUS + STR_VERTICAL_LINE STR_DOT STR_CIRCUMFLEX_ACCENT STR_DOLLAR_SIGN + STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET + STR_LEFT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET + STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS; + +/* Recognized escaped metacharacters in POSIX basic patterns. */ + +static const char *posix_meta_escapes = + STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS + STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET + STR_1 STR_2 STR_3 STR_4 STR_5 STR_6 STR_7 STR_8 STR_9; + + + +/************************************************* +* Convert a POSIX pattern * +*************************************************/ + +/* This function handles both basic and extended POSIX patterns. + +Arguments: + pattype the pattern type + pattern the pattern + plength length in code units + utf TRUE if UTF + use_buffer where to put the output + use_length length of use_buffer + bufflenptr where to put the used length + dummyrun TRUE if a dummy run + ccontext the convert context + +Returns: 0 => success + !0 => error code +*/ + +static int +convert_posix(uint32_t pattype, PCRE2_SPTR pattern, PCRE2_SIZE plength, + BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length, + PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext) +{ +char *s; +PCRE2_SPTR posix = pattern; +PCRE2_UCHAR *p = use_buffer; +PCRE2_UCHAR *pp = p; +PCRE2_UCHAR *endp = p + use_length - 1; /* Allow for trailing zero */ +PCRE2_SIZE convlength = 0; + +uint32_t bracount = 0; +uint32_t posix_state = POSIX_START_REGEX; +uint32_t lastspecial = 0; +BOOL extended = (pattype & PCRE2_CONVERT_POSIX_EXTENDED) != 0; +BOOL nextisliteral = FALSE; + +(void)utf; /* Not used when Unicode not supported */ +(void)ccontext; /* Not currently used */ + +/* Initialize default for error offset as end of input. */ + +*bufflenptr = plength; +PUTCHARS(STR_STAR_NUL); + +/* Now scan the input. */ + +while (plength > 0) + { + uint32_t c, sc; + int clength = 1; + + /* Add in the length of the last item, then, if in the dummy run, pull the + pointer back to the start of the (temporary) buffer and then remember the + start of the next item. */ + + convlength += p - pp; + if (dummyrun) p = use_buffer; + pp = p; + + /* Pick up the next character */ + +#ifndef SUPPORT_UNICODE + c = *posix; +#else + GETCHARLENTEST(c, posix, clength); +#endif + posix += clength; + plength -= clength; + + sc = nextisliteral? 0 : c; + nextisliteral = FALSE; + + /* Handle a character within a class. */ + + if (posix_state >= POSIX_CLASS_NOT_STARTED) + { + if (c == CHAR_RIGHT_SQUARE_BRACKET) + { + PUTCHARS(STR_RIGHT_SQUARE_BRACKET); + posix_state = POSIX_NOT_BRACKET; + } + + /* Not the end of the class */ + + else + { + switch (posix_state) + { + case POSIX_CLASS_STARTED: + if (c <= 127 && islower(c)) break; /* Remain in started state */ + posix_state = POSIX_CLASS_NOT_STARTED; + if (c == CHAR_COLON && plength > 0 && + *posix == CHAR_RIGHT_SQUARE_BRACKET) + { + PUTCHARS(STR_COLON_RIGHT_SQUARE_BRACKET); + plength--; + posix++; + continue; /* With next character after :] */ + } + /* Fall through */ + + case POSIX_CLASS_NOT_STARTED: + if (c == CHAR_LEFT_SQUARE_BRACKET) + posix_state = POSIX_CLASS_STARTING; + break; + + case POSIX_CLASS_STARTING: + if (c == CHAR_COLON) posix_state = POSIX_CLASS_STARTED; + break; + } + + if (c == CHAR_BACKSLASH) PUTCHARS(STR_BACKSLASH); + if (p + clength > endp) return PCRE2_ERROR_NOMEMORY; + memcpy(p, posix - clength, CU2BYTES(clength)); + p += clength; + } + } + + /* Handle a character not within a class. */ + + else switch(sc) + { + case CHAR_LEFT_SQUARE_BRACKET: + PUTCHARS(STR_LEFT_SQUARE_BRACKET); + +#ifdef NEVER + /* We could handle special cases [[:<:]] and [[:>:]] (which PCRE does + support) but they are not part of POSIX 1003.1. */ + + if (plength >= 6) + { + if (posix[0] == CHAR_LEFT_SQUARE_BRACKET && + posix[1] == CHAR_COLON && + (posix[2] == CHAR_LESS_THAN_SIGN || + posix[2] == CHAR_GREATER_THAN_SIGN) && + posix[3] == CHAR_COLON && + posix[4] == CHAR_RIGHT_SQUARE_BRACKET && + posix[5] == CHAR_RIGHT_SQUARE_BRACKET) + { + if (p + 6 > endp) return PCRE2_ERROR_NOMEMORY; + memcpy(p, posix, CU2BYTES(6)); + p += 6; + posix += 6; + plength -= 6; + continue; /* With next character */ + } + } +#endif + + /* Handle start of "normal" character classes */ + + posix_state = POSIX_CLASS_NOT_STARTED; + + /* Handle ^ and ] as first characters */ + + if (plength > 0) + { + if (*posix == CHAR_CIRCUMFLEX_ACCENT) + { + posix++; + plength--; + PUTCHARS(STR_CIRCUMFLEX_ACCENT); + } + if (plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET) + { + posix++; + plength--; + PUTCHARS(STR_RIGHT_SQUARE_BRACKET); + } + } + break; + + case CHAR_BACKSLASH: + if (plength <= 0) return PCRE2_ERROR_END_BACKSLASH; + if (extended) nextisliteral = TRUE; else + { + if (*posix < 127 && strchr(posix_meta_escapes, *posix) != NULL) + { + if (isdigit(*posix)) PUTCHARS(STR_BACKSLASH); + if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY; + lastspecial = *p++ = *posix++; + plength--; + } + else nextisliteral = TRUE; + } + break; + + case CHAR_RIGHT_PARENTHESIS: + if (!extended || bracount == 0) goto ESCAPE_LITERAL; + bracount--; + goto COPY_SPECIAL; + + case CHAR_LEFT_PARENTHESIS: + bracount++; + /* Fall through */ + + case CHAR_QUESTION_MARK: + case CHAR_PLUS: + case CHAR_LEFT_CURLY_BRACKET: + case CHAR_RIGHT_CURLY_BRACKET: + case CHAR_VERTICAL_LINE: + if (!extended) goto ESCAPE_LITERAL; + /* Fall through */ + + case CHAR_DOT: + case CHAR_DOLLAR_SIGN: + posix_state = POSIX_NOT_BRACKET; + COPY_SPECIAL: + lastspecial = c; + if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY; + *p++ = c; + break; + + case CHAR_ASTERISK: + if (lastspecial != CHAR_ASTERISK) + { + if (!extended && (posix_state < POSIX_NOT_BRACKET || + lastspecial == CHAR_LEFT_PARENTHESIS)) + goto ESCAPE_LITERAL; + goto COPY_SPECIAL; + } + break; /* Ignore second and subsequent asterisks */ + + case CHAR_CIRCUMFLEX_ACCENT: + if (extended) goto COPY_SPECIAL; + if (posix_state == POSIX_START_REGEX || + lastspecial == CHAR_LEFT_PARENTHESIS) + { + posix_state = POSIX_ANCHORED; + goto COPY_SPECIAL; + } + /* Fall through */ + + default: + if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL) + { + ESCAPE_LITERAL: + PUTCHARS(STR_BACKSLASH); + } + lastspecial = 0xff; /* Indicates nothing special */ + if (p + clength > endp) return PCRE2_ERROR_NOMEMORY; + memcpy(p, posix - clength, CU2BYTES(clength)); + p += clength; + posix_state = POSIX_NOT_BRACKET; + break; + } + } + +if (posix_state >= POSIX_CLASS_NOT_STARTED) + return PCRE2_ERROR_MISSING_SQUARE_BRACKET; +convlength += p - pp; /* Final segment */ +*bufflenptr = convlength; +*p++ = 0; +return 0; +} + + +/************************************************* +* Convert a glob pattern * +*************************************************/ + +/* Context for writing the output into a buffer. */ + +typedef struct pcre2_output_context { + PCRE2_UCHAR *output; /* current output position */ + PCRE2_SPTR output_end; /* output end */ + PCRE2_SIZE output_size; /* size of the output */ + uint8_t out_str[8]; /* string copied to the output */ +} pcre2_output_context; + + +/* Write a character into the output. + +Arguments: + out output context + chr the next character +*/ + +static void +convert_glob_write(pcre2_output_context *out, PCRE2_UCHAR chr) +{ +out->output_size++; + +if (out->output < out->output_end) + *out->output++ = chr; +} + + +/* Write a string into the output. + +Arguments: + out output context + length length of out->out_str +*/ + +static void +convert_glob_write_str(pcre2_output_context *out, PCRE2_SIZE length) +{ +uint8_t *out_str = out->out_str; +PCRE2_UCHAR *output = out->output; +PCRE2_SPTR output_end = out->output_end; +PCRE2_SIZE output_size = out->output_size; + +do + { + output_size++; + + if (output < output_end) + *output++ = *out_str++; + } +while (--length != 0); + +out->output = output; +out->output_size = output_size; +} + + +/* Prints the separator into the output. + +Arguments: + out output context + separator glob separator + with_escape backslash is needed before separator +*/ + +static void +convert_glob_print_separator(pcre2_output_context *out, + PCRE2_UCHAR separator, BOOL with_escape) +{ +if (with_escape) + convert_glob_write(out, CHAR_BACKSLASH); + +convert_glob_write(out, separator); +} + + +/* Prints a wildcard into the output. + +Arguments: + out output context + separator glob separator + with_escape backslash is needed before separator +*/ + +static void +convert_glob_print_wildcard(pcre2_output_context *out, + PCRE2_UCHAR separator, BOOL with_escape) +{ +out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET; +out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT; +convert_glob_write_str(out, 2); + +convert_glob_print_separator(out, separator, with_escape); + +convert_glob_write(out, CHAR_RIGHT_SQUARE_BRACKET); +} + + +/* Parse a posix class. + +Arguments: + from starting point of scanning the range + pattern_end end of pattern + out output context + +Returns: >0 => class index + 0 => malformed class +*/ + +static int +convert_glob_parse_class(PCRE2_SPTR *from, PCRE2_SPTR pattern_end, + pcre2_output_context *out) +{ +static const char *posix_classes = "alnum:alpha:ascii:blank:cntrl:digit:" + "graph:lower:print:punct:space:upper:word:xdigit:"; +PCRE2_SPTR start = *from + 1; +PCRE2_SPTR pattern = start; +const char *class_ptr; +PCRE2_UCHAR c; +int class_index; + +while (TRUE) + { + if (pattern >= pattern_end) return 0; + + c = *pattern++; + + if (c < CHAR_a || c > CHAR_z) break; + } + +if (c != CHAR_COLON || pattern >= pattern_end || + *pattern != CHAR_RIGHT_SQUARE_BRACKET) + return 0; + +class_ptr = posix_classes; +class_index = 1; + +while (TRUE) + { + if (*class_ptr == CHAR_NUL) return 0; + + pattern = start; + + while (*pattern == (PCRE2_UCHAR) *class_ptr) + { + if (*pattern == CHAR_COLON) + { + pattern += 2; + start -= 2; + + do convert_glob_write(out, *start++); while (start < pattern); + + *from = pattern; + return class_index; + } + pattern++; + class_ptr++; + } + + while (*class_ptr != CHAR_COLON) class_ptr++; + class_ptr++; + class_index++; + } +} + +/* Checks whether the character is in the class. + +Arguments: + class_index class index + c character + +Returns: !0 => character is found in the class + 0 => otherwise +*/ + +static BOOL +convert_glob_char_in_class(int class_index, PCRE2_UCHAR c) +{ +switch (class_index) + { + case 1: return isalnum(c); + case 2: return isalpha(c); + case 3: return 1; + case 4: return c == CHAR_HT || c == CHAR_SPACE; + case 5: return iscntrl(c); + case 6: return isdigit(c); + case 7: return isgraph(c); + case 8: return islower(c); + case 9: return isprint(c); + case 10: return ispunct(c); + case 11: return isspace(c); + case 12: return isupper(c); + case 13: return isalnum(c) || c == CHAR_UNDERSCORE; + default: return isxdigit(c); + } +} + +/* Parse a range of characters. + +Arguments: + from starting point of scanning the range + pattern_end end of pattern + out output context + separator glob separator + with_escape backslash is needed before separator + +Returns: 0 => success + !0 => error code +*/ + +static int +convert_glob_parse_range(PCRE2_SPTR *from, PCRE2_SPTR pattern_end, + pcre2_output_context *out, BOOL utf, PCRE2_UCHAR separator, + BOOL with_escape, PCRE2_UCHAR escape, BOOL no_wildsep) +{ +BOOL is_negative = FALSE; +BOOL separator_seen = FALSE; +BOOL has_prev_c; +PCRE2_SPTR pattern = *from; +PCRE2_SPTR char_start = NULL; +uint32_t c, prev_c; +int len, class_index; + +(void)utf; /* Avoid compiler warning. */ + +if (pattern >= pattern_end) + { + *from = pattern; + return PCRE2_ERROR_MISSING_SQUARE_BRACKET; + } + +if (*pattern == CHAR_EXCLAMATION_MARK + || *pattern == CHAR_CIRCUMFLEX_ACCENT) + { + pattern++; + + if (pattern >= pattern_end) + { + *from = pattern; + return PCRE2_ERROR_MISSING_SQUARE_BRACKET; + } + + is_negative = TRUE; + + out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET; + out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT; + len = 2; + + if (!no_wildsep) + { + if (with_escape) + { + out->out_str[len] = CHAR_BACKSLASH; + len++; + } + out->out_str[len] = (uint8_t) separator; + } + + convert_glob_write_str(out, len + 1); + } +else + convert_glob_write(out, CHAR_LEFT_SQUARE_BRACKET); + +has_prev_c = FALSE; +prev_c = 0; + +if (*pattern == CHAR_RIGHT_SQUARE_BRACKET) + { + out->out_str[0] = CHAR_BACKSLASH; + out->out_str[1] = CHAR_RIGHT_SQUARE_BRACKET; + convert_glob_write_str(out, 2); + has_prev_c = TRUE; + prev_c = CHAR_RIGHT_SQUARE_BRACKET; + pattern++; + } + +while (pattern < pattern_end) + { + char_start = pattern; + GETCHARINCTEST(c, pattern); + + if (c == CHAR_RIGHT_SQUARE_BRACKET) + { + convert_glob_write(out, c); + + if (!is_negative && !no_wildsep && separator_seen) + { + out->out_str[0] = CHAR_LEFT_PARENTHESIS; + out->out_str[1] = CHAR_QUESTION_MARK; + out->out_str[2] = CHAR_LESS_THAN_SIGN; + out->out_str[3] = CHAR_EXCLAMATION_MARK; + convert_glob_write_str(out, 4); + + convert_glob_print_separator(out, separator, with_escape); + convert_glob_write(out, CHAR_RIGHT_PARENTHESIS); + } + + *from = pattern; + return 0; + } + + if (pattern >= pattern_end) break; + + if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON) + { + *from = pattern; + class_index = convert_glob_parse_class(from, pattern_end, out); + + if (class_index != 0) + { + pattern = *from; + + has_prev_c = FALSE; + prev_c = 0; + + if (!is_negative && + convert_glob_char_in_class (class_index, separator)) + separator_seen = TRUE; + continue; + } + } + else if (c == CHAR_MINUS && has_prev_c && + *pattern != CHAR_RIGHT_SQUARE_BRACKET) + { + convert_glob_write(out, CHAR_MINUS); + + char_start = pattern; + GETCHARINCTEST(c, pattern); + + if (pattern >= pattern_end) break; + + if (escape != 0 && c == escape) + { + char_start = pattern; + GETCHARINCTEST(c, pattern); + } + else if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON) + { + *from = pattern; + return PCRE2_ERROR_CONVERT_SYNTAX; + } + + if (prev_c > c) + { + *from = pattern; + return PCRE2_ERROR_CONVERT_SYNTAX; + } + + if (prev_c < separator && separator < c) separator_seen = TRUE; + + has_prev_c = FALSE; + prev_c = 0; + } + else + { + if (escape != 0 && c == escape) + { + char_start = pattern; + GETCHARINCTEST(c, pattern); + + if (pattern >= pattern_end) break; + } + + has_prev_c = TRUE; + prev_c = c; + } + + if (c == CHAR_LEFT_SQUARE_BRACKET || c == CHAR_RIGHT_SQUARE_BRACKET || + c == CHAR_BACKSLASH || c == CHAR_MINUS) + convert_glob_write(out, CHAR_BACKSLASH); + + if (c == separator) separator_seen = TRUE; + + do convert_glob_write(out, *char_start++); while (char_start < pattern); + } + +*from = pattern; +return PCRE2_ERROR_MISSING_SQUARE_BRACKET; +} + + +/* Prints a (*COMMIT) into the output. + +Arguments: + out output context +*/ + +static void +convert_glob_print_commit(pcre2_output_context *out) +{ +out->out_str[0] = CHAR_LEFT_PARENTHESIS; +out->out_str[1] = CHAR_ASTERISK; +out->out_str[2] = CHAR_C; +out->out_str[3] = CHAR_O; +out->out_str[4] = CHAR_M; +out->out_str[5] = CHAR_M; +out->out_str[6] = CHAR_I; +out->out_str[7] = CHAR_T; +convert_glob_write_str(out, 8); +convert_glob_write(out, CHAR_RIGHT_PARENTHESIS); +} + + +/* Bash glob converter. + +Arguments: + pattype the pattern type + pattern the pattern + plength length in code units + utf TRUE if UTF + use_buffer where to put the output + use_length length of use_buffer + bufflenptr where to put the used length + dummyrun TRUE if a dummy run + ccontext the convert context + +Returns: 0 => success + !0 => error code +*/ + +static int +convert_glob(uint32_t options, PCRE2_SPTR pattern, PCRE2_SIZE plength, + BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length, + PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext) +{ +pcre2_output_context out; +PCRE2_SPTR pattern_start = pattern; +PCRE2_SPTR pattern_end = pattern + plength; +PCRE2_UCHAR separator = ccontext->glob_separator; +PCRE2_UCHAR escape = ccontext->glob_escape; +PCRE2_UCHAR c; +BOOL no_wildsep = (options & PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR) != 0; +BOOL no_starstar = (options & PCRE2_CONVERT_GLOB_NO_STARSTAR) != 0; +BOOL in_atomic = FALSE; +BOOL after_starstar = FALSE; +BOOL no_slash_z = FALSE; +BOOL with_escape, is_start, after_separator; +int result = 0; + +(void)utf; /* Avoid compiler warning. */ + +#ifdef SUPPORT_UNICODE +if (utf && (separator >= 128 || escape >= 128)) + { + /* Currently only ASCII characters are supported. */ + *bufflenptr = 0; + return PCRE2_ERROR_CONVERT_SYNTAX; + } +#endif + +with_escape = strchr(pcre2_escaped_literals, separator) != NULL; + +/* Initialize default for error offset as end of input. */ +out.output = use_buffer; +out.output_end = use_buffer + use_length; +out.output_size = 0; + +out.out_str[0] = CHAR_LEFT_PARENTHESIS; +out.out_str[1] = CHAR_QUESTION_MARK; +out.out_str[2] = CHAR_s; +out.out_str[3] = CHAR_RIGHT_PARENTHESIS; +convert_glob_write_str(&out, 4); + +is_start = TRUE; + +if (pattern < pattern_end && pattern[0] == CHAR_ASTERISK) + { + if (no_wildsep) + is_start = FALSE; + else if (!no_starstar && pattern + 1 < pattern_end && + pattern[1] == CHAR_ASTERISK) + is_start = FALSE; + } + +if (is_start) + { + out.out_str[0] = CHAR_BACKSLASH; + out.out_str[1] = CHAR_A; + convert_glob_write_str(&out, 2); + } + +while (pattern < pattern_end) + { + c = *pattern++; + + if (c == CHAR_ASTERISK) + { + is_start = pattern == pattern_start + 1; + + if (in_atomic) + { + convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); + in_atomic = FALSE; + } + + if (!no_starstar && pattern < pattern_end && *pattern == CHAR_ASTERISK) + { + after_separator = is_start || (pattern[-2] == separator); + + do pattern++; while (pattern < pattern_end && + *pattern == CHAR_ASTERISK); + + if (pattern >= pattern_end) + { + no_slash_z = TRUE; + break; + } + + after_starstar = TRUE; + + if (after_separator && escape != 0 && *pattern == escape && + pattern + 1 < pattern_end && pattern[1] == separator) + pattern++; + + if (is_start) + { + if (*pattern != separator) continue; + + out.out_str[0] = CHAR_LEFT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_COLON; + out.out_str[3] = CHAR_BACKSLASH; + out.out_str[4] = CHAR_A; + out.out_str[5] = CHAR_VERTICAL_LINE; + convert_glob_write_str(&out, 6); + + convert_glob_print_separator(&out, separator, with_escape); + convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); + + pattern++; + continue; + } + + convert_glob_print_commit(&out); + + if (!after_separator || *pattern != separator) + { + out.out_str[0] = CHAR_DOT; + out.out_str[1] = CHAR_ASTERISK; + out.out_str[2] = CHAR_QUESTION_MARK; + convert_glob_write_str(&out, 3); + continue; + } + + out.out_str[0] = CHAR_LEFT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_COLON; + out.out_str[3] = CHAR_DOT; + out.out_str[4] = CHAR_ASTERISK; + out.out_str[5] = CHAR_QUESTION_MARK; + + convert_glob_write_str(&out, 6); + + convert_glob_print_separator(&out, separator, with_escape); + + out.out_str[0] = CHAR_RIGHT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_QUESTION_MARK; + convert_glob_write_str(&out, 3); + + pattern++; + continue; + } + + if (pattern < pattern_end && *pattern == CHAR_ASTERISK) + { + do pattern++; while (pattern < pattern_end && + *pattern == CHAR_ASTERISK); + } + + if (no_wildsep) + { + if (pattern >= pattern_end) + { + no_slash_z = TRUE; + break; + } + + /* Start check must be after the end check. */ + if (is_start) continue; + } + + if (!is_start) + { + if (after_starstar) + { + out.out_str[0] = CHAR_LEFT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_GREATER_THAN_SIGN; + convert_glob_write_str(&out, 3); + in_atomic = TRUE; + } + else + convert_glob_print_commit(&out); + } + + if (no_wildsep) + convert_glob_write(&out, CHAR_DOT); + else + convert_glob_print_wildcard(&out, separator, with_escape); + + out.out_str[0] = CHAR_ASTERISK; + out.out_str[1] = CHAR_QUESTION_MARK; + if (pattern >= pattern_end) + out.out_str[1] = CHAR_PLUS; + convert_glob_write_str(&out, 2); + continue; + } + + if (c == CHAR_QUESTION_MARK) + { + if (no_wildsep) + convert_glob_write(&out, CHAR_DOT); + else + convert_glob_print_wildcard(&out, separator, with_escape); + continue; + } + + if (c == CHAR_LEFT_SQUARE_BRACKET) + { + result = convert_glob_parse_range(&pattern, pattern_end, + &out, utf, separator, with_escape, escape, no_wildsep); + if (result != 0) break; + continue; + } + + if (escape != 0 && c == escape) + { + if (pattern >= pattern_end) + { + result = PCRE2_ERROR_CONVERT_SYNTAX; + break; + } + c = *pattern++; + } + + if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL) + convert_glob_write(&out, CHAR_BACKSLASH); + + convert_glob_write(&out, c); + } + +if (result == 0) + { + if (!no_slash_z) + { + out.out_str[0] = CHAR_BACKSLASH; + out.out_str[1] = CHAR_z; + convert_glob_write_str(&out, 2); + } + + if (in_atomic) + convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); + + convert_glob_write(&out, CHAR_NUL); + + if (!dummyrun && out.output_size != (PCRE2_SIZE) (out.output - use_buffer)) + result = PCRE2_ERROR_NOMEMORY; + } + +if (result != 0) + { + *bufflenptr = pattern - pattern_start; + return result; + } + +*bufflenptr = out.output_size - 1; +return 0; +} + + +/************************************************* +* Convert pattern * +*************************************************/ + +/* This is the external-facing function for converting other forms of pattern +into PCRE2 regular expression patterns. On error, the bufflenptr argument is +used to return an offset in the original pattern. + +Arguments: + pattern the input pattern + plength length of input, or PCRE2_ZERO_TERMINATED + options options bits + buffptr pointer to pointer to output buffer + bufflenptr pointer to length of output buffer + ccontext convert context or NULL + +Returns: 0 for success, else an error code (+ve or -ve) +*/ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_pattern_convert(PCRE2_SPTR pattern, PCRE2_SIZE plength, uint32_t options, + PCRE2_UCHAR **buffptr, PCRE2_SIZE *bufflenptr, + pcre2_convert_context *ccontext) +{ +int i, rc; +PCRE2_UCHAR dummy_buffer[DUMMY_BUFFER_SIZE]; +PCRE2_UCHAR *use_buffer = dummy_buffer; +PCRE2_SIZE use_length = DUMMY_BUFFER_SIZE; +BOOL utf = (options & PCRE2_CONVERT_UTF) != 0; +uint32_t pattype = options & TYPE_OPTIONS; + +if (pattern == NULL || bufflenptr == NULL) return PCRE2_ERROR_NULL; + +if ((options & ~ALL_OPTIONS) != 0 || /* Undefined bit set */ + (pattype & (~pattype+1)) != pattype || /* More than one type set */ + pattype == 0) /* No type set */ + { + *bufflenptr = 0; /* Error offset */ + return PCRE2_ERROR_BADOPTION; + } + +if (plength == PCRE2_ZERO_TERMINATED) plength = PRIV(strlen)(pattern); +if (ccontext == NULL) ccontext = + (pcre2_convert_context *)(&PRIV(default_convert_context)); + +/* Check UTF if required. */ + +#ifndef SUPPORT_UNICODE +if (utf) + { + *bufflenptr = 0; /* Error offset */ + return PCRE2_ERROR_UNICODE_NOT_SUPPORTED; + } +#else +if (utf && (options & PCRE2_CONVERT_NO_UTF_CHECK) == 0) + { + PCRE2_SIZE erroroffset; + rc = PRIV(valid_utf)(pattern, plength, &erroroffset); + if (rc != 0) + { + *bufflenptr = erroroffset; + return rc; + } + } +#endif + +/* If buffptr is not NULL, and what it points to is not NULL, we are being +provided with a buffer and a length, so set them as the buffer to use. */ + +if (buffptr != NULL && *buffptr != NULL) + { + use_buffer = *buffptr; + use_length = *bufflenptr; + } + +/* Call an individual converter, either just once (if a buffer was provided or +just the length is needed), or twice (if a memory allocation is required). */ + +for (i = 0; i < 2; i++) + { + PCRE2_UCHAR *allocated; + BOOL dummyrun = buffptr == NULL || *buffptr == NULL; + + switch(pattype) + { + case PCRE2_CONVERT_GLOB: + rc = convert_glob(options & ~PCRE2_CONVERT_GLOB, pattern, plength, utf, + use_buffer, use_length, bufflenptr, dummyrun, ccontext); + break; + + case PCRE2_CONVERT_POSIX_BASIC: + case PCRE2_CONVERT_POSIX_EXTENDED: + rc = convert_posix(pattype, pattern, plength, utf, use_buffer, use_length, + bufflenptr, dummyrun, ccontext); + break; + + default: + *bufflenptr = 0; /* Error offset */ + return PCRE2_ERROR_INTERNAL; + } + + if (rc != 0 || /* Error */ + buffptr == NULL || /* Just the length is required */ + *buffptr != NULL) /* Buffer was provided or allocated */ + return rc; + + /* Allocate memory for the buffer, with hidden space for an allocator at + the start. The next time round the loop runs the conversion for real. */ + + allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) + + (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext); + if (allocated == NULL) return PCRE2_ERROR_NOMEMORY; + *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl)); + + use_buffer = *buffptr; + use_length = *bufflenptr + 1; + } + +/* Control should never get here. */ + +return PCRE2_ERROR_INTERNAL; +} + + +/************************************************* +* Free converted pattern * +*************************************************/ + +/* This frees a converted pattern that was put in newly-allocated memory. + +Argument: the converted pattern +Returns: nothing +*/ + +PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION +pcre2_converted_pattern_free(PCRE2_UCHAR *converted) +{ +if (converted != NULL) + { + pcre2_memctl *memctl = + (pcre2_memctl *)((char *)converted - sizeof(pcre2_memctl)); + memctl->free(memctl, memctl->memory_data); + } +} + +/* End of pcre2_convert.c */ diff --git a/ProcessHacker/pcre/pcre2_dfa_match.c b/ProcessHacker/pcre/pcre2_dfa_match.c index c8ad26beef45..9b43237da750 100644 --- a/ProcessHacker/pcre/pcre2_dfa_match.c +++ b/ProcessHacker/pcre/pcre2_dfa_match.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -71,7 +71,7 @@ only once - I suspect this was the cause of the problems with the tests.) Overall, I concluded that the gains in some cases did not outweigh the losses in others, so I abandoned this code. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -83,7 +83,7 @@ in others, so I abandoned this code. */ #include "pcre2_internal.h" #define PUBLIC_DFA_MATCH_OPTIONS \ - (PCRE2_ANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ + (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ PCRE2_PARTIAL_SOFT|PCRE2_DFA_SHORTEST|PCRE2_DFA_RESTART) @@ -172,7 +172,7 @@ static const uint8_t coptable[] = { 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ - 0, 0, /* ONCE, ONCE_NC */ + 0, /* ONCE */ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ 0, 0, /* CREF, DNCREF */ @@ -181,7 +181,8 @@ static const uint8_t coptable[] = { 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ - 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0, /* COMMIT, COMMIT_ARG */ + 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */ }; @@ -245,7 +246,7 @@ static const uint8_t poptable[] = { 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ - 0, 0, /* ONCE, ONCE_NC */ + 0, /* ONCE */ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ 0, 0, /* CREF, DNCREF */ @@ -254,7 +255,8 @@ static const uint8_t poptable[] = { 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ - 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0, /* COMMIT, COMMIT_ARG */ + 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */ }; @@ -292,6 +294,150 @@ typedef struct stateblock { #define INTS_PER_STATEBLOCK (int)(sizeof(stateblock)/sizeof(int)) +/* Before version 10.32 the recursive calls of internal_dfa_match() were passed +local working space and output vectors that were created on the stack. This has +caused issues for some patterns, especially in small-stack environments such as +Windows. A new scheme is now in use which sets up a vector on the stack, but if +this is too small, heap memory is used, up to the heap_limit. The main +parameters are all numbers of ints because the workspace is a vector of ints. + +The size of the starting stack vector, DFA_START_RWS_SIZE, is in bytes, and is +defined in pcre2_internal.h so as to be available to pcre2test when it is +finding the minimum heap requirement for a match. */ + +#define OVEC_UNIT (sizeof(PCRE2_SIZE)/sizeof(int)) + +#define RWS_BASE_SIZE (DFA_START_RWS_SIZE/sizeof(int)) /* Stack vector */ +#define RWS_RSIZE 1000 /* Work size for recursion */ +#define RWS_OVEC_RSIZE (1000*OVEC_UNIT) /* Ovector for recursion */ +#define RWS_OVEC_OSIZE (2*OVEC_UNIT) /* Ovector in other cases */ + +/* This structure is at the start of each workspace block. */ + +typedef struct RWS_anchor { + struct RWS_anchor *next; + unsigned int size; /* Number of ints */ + unsigned int free; /* Number of ints */ +} RWS_anchor; + +#define RWS_ANCHOR_SIZE (sizeof(RWS_anchor)/sizeof(int)) + + + +/************************************************* +* Process a callout * +*************************************************/ + +/* This function is called to perform a callout. + +Arguments: + code current code pointer + offsets points to current capture offsets + current_subject start of current subject match + ptr current position in subject + mb the match block + extracode extra code offset when called from condition + lengthptr where to return the callout length + +Returns: the return from the callout +*/ + +static int +do_callout(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject, + PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode, + PCRE2_SIZE *lengthptr) +{ +pcre2_callout_block *cb = mb->cb; + +*lengthptr = (code[extracode] == OP_CALLOUT)? + (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] : + (PCRE2_SIZE)GET(code, 1 + 2*LINK_SIZE + extracode); + +if (mb->callout == NULL) return 0; /* No callout provided */ + +/* Fixed fields in the callout block are set once and for all at the start of +matching. */ + +cb->offset_vector = offsets; +cb->start_match = (PCRE2_SIZE)(current_subject - mb->start_subject); +cb->current_position = (PCRE2_SIZE)(ptr - mb->start_subject); +cb->pattern_position = GET(code, 1 + extracode); +cb->next_item_length = GET(code, 1 + LINK_SIZE + extracode); + +if (code[extracode] == OP_CALLOUT) + { + cb->callout_number = code[1 + 2*LINK_SIZE + extracode]; + cb->callout_string_offset = 0; + cb->callout_string = NULL; + cb->callout_string_length = 0; + } +else + { + cb->callout_number = 0; + cb->callout_string_offset = GET(code, 1 + 3*LINK_SIZE + extracode); + cb->callout_string = code + (1 + 4*LINK_SIZE + extracode) + 1; + cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2; + } + +return (mb->callout)(cb, mb->callout_data); +} + + + +/************************************************* +* Expand local workspace memory * +*************************************************/ + +/* This function is called when internal_dfa_match() is about to be called +recursively and there is insufficient working space left in the current +workspace block. If there's an existing next block, use it; otherwise get a new +block unless the heap limit is reached. + +Arguments: + rwsptr pointer to block pointer (updated) + ovecsize space needed for an ovector + mb the match block + +Returns: 0 rwsptr has been updated + !0 an error code +*/ + +static int +more_workspace(RWS_anchor **rwsptr, unsigned int ovecsize, dfa_match_block *mb) +{ +RWS_anchor *rws = *rwsptr; +RWS_anchor *new; + +if (rws->next != NULL) + { + new = rws->next; + } + +/* All sizes are in units of sizeof(int), except for mb->heaplimit, which is in +kibibytes. */ + +else + { + unsigned int newsize = rws->size * 2; + unsigned int heapleft = (unsigned int) + (((1024/sizeof(int))*mb->heap_limit - mb->heap_used)); + if (newsize > heapleft) newsize = heapleft; + if (newsize < RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE) + return PCRE2_ERROR_HEAPLIMIT; + new = mb->memctl.malloc(newsize*sizeof(int), mb->memctl.memory_data); + if (new == NULL) return PCRE2_ERROR_NOMEMORY; + mb->heap_used += newsize; + new->next = NULL; + new->size = newsize; + rws->next = new; + } + +new->free = new->size - RWS_ANCHOR_SIZE; +*rwsptr = new; +return 0; +} + + /************************************************* * Match a Regular Expression - DFA engine * @@ -371,18 +517,15 @@ internal_dfa_match( uint32_t offsetcount, int *workspace, int wscount, - int rlevel) + uint32_t rlevel, + int *RWS) { stateblock *active_states, *new_states, *temp_states; stateblock *next_active_state, *next_new_state; - const uint8_t *ctypes, *lcc, *fcc; PCRE2_SPTR ptr; PCRE2_SPTR end_code; -PCRE2_SPTR first_op; - dfa_recursion_info new_recursive; - int active_count, new_count, match_count; /* Some fields in the mb block are frequently referenced, so we load them into @@ -400,7 +543,8 @@ BOOL utf = FALSE; BOOL reset_could_continue = FALSE; -rlevel++; +if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT; +if (rlevel++ > mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT; offsetcount &= (uint32_t)(-2); /* Round down */ wscount -= 2; @@ -417,21 +561,15 @@ active_states = (stateblock *)(workspace + 2); next_new_state = new_states = active_states + wscount; new_count = 0; -first_op = this_start_code + 1 + LINK_SIZE + - ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || - *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) - ? IMM2_SIZE:0); - /* The first thing in any (sub) pattern is a bracket of some sort. Push all the alternative states onto the list, and find out where the end is. This makes is possible to use this function recursively, when we want to stop at a matching internal ket rather than at the end. -If the first opcode in the first alternative is OP_REVERSE, we are dealing with -a backward assertion. In that case, we have to find out the maximum amount to -move back, and set up each alternative appropriately. */ +If we are dealing with a backward assertion we have to find out the maximum +amount to move back, and set up each alternative appropriately. */ -if (*first_op == OP_REVERSE) +if (*this_start_code == OP_ASSERTBACK || *this_start_code == OP_ASSERTBACK_NOT) { size_t max_back = 0; size_t gone_back; @@ -457,7 +595,8 @@ if (*first_op == OP_REVERSE) { if (current_subject <= start_subject) break; current_subject--; - ACROSSCHAR(current_subject > start_subject, *current_subject, current_subject--); + ACROSSCHAR(current_subject > start_subject, current_subject, + current_subject--); } } else @@ -476,15 +615,17 @@ if (*first_op == OP_REVERSE) if (current_subject < mb->start_used_ptr) mb->start_used_ptr = current_subject; - /* Now we can process the individual branches. */ + /* Now we can process the individual branches. There will be an OP_REVERSE at + the start of each branch, except when the length of the branch is zero. */ end_code = this_start_code; do { - size_t back = (size_t)GET(end_code, 2+LINK_SIZE); + uint32_t revlen = (end_code[1+LINK_SIZE] == OP_REVERSE)? 1 + LINK_SIZE : 0; + size_t back = (revlen == 0)? 0 : (size_t)GET(end_code, 2+LINK_SIZE); if (back <= gone_back) { - int bstate = (int)(end_code - start_code + 2 + 2*LINK_SIZE); + int bstate = (int)(end_code - start_code + 1 + LINK_SIZE + revlen); ADD_NEW_DATA(-bstate, 0, (int)(gone_back - back)); } end_code += GET(end_code, 1); @@ -697,7 +838,7 @@ for (;;) case OP_TABLE_LENGTH + ((sizeof(coptable) == OP_TABLE_LENGTH) && (sizeof(poptable) == OP_TABLE_LENGTH)): - break; + return 0; /* ========================================================================== */ /* Reached a closing bracket. If not at the end of the pattern, carry @@ -734,7 +875,7 @@ for (;;) else if (match_count > 0 && ++match_count * 2 > (int)offsetcount) match_count = 0; count = ((match_count == 0)? (int)offsetcount : match_count * 2) - 2; - if (count > 0) memmove(offsets + 2, offsets, + if (count > 0) (void)memmove(offsets + 2, offsets, (size_t)count * sizeof(PCRE2_SIZE)); if (offsetcount >= 2) { @@ -1371,25 +1512,14 @@ for (;;) if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; int ncount = 0; if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, + &ncount); count++; ADD_NEW_DATA(-state_offset, count, ncount); } @@ -1632,8 +1762,6 @@ for (;;) ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY) @@ -1641,17 +1769,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, + &ncount); ADD_NEW_DATA(-(state_offset + count), 0, ncount); } break; @@ -1904,25 +2023,15 @@ for (;;) count = current_state->count; /* Number already matched */ if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; + PCRE2_SPTR nptr; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, + &ncount); if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; if (++count >= (int)GET2(code, 1)) @@ -2099,20 +2208,9 @@ for (;;) case OP_EXTUNI: if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; int ncount = 0; - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + PCRE2_SPTR nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, + end_subject, utf, &ncount); if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; ADD_NEW_DATA(-(state_offset + 1), 0, ncount); @@ -2136,6 +2234,7 @@ for (;;) case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break; + /* Fall through */ case CHAR_LF: ADD_NEW(state_offset + 1, 0); @@ -2225,7 +2324,7 @@ for (;;) case OP_NOTI: if (clen > 0) { - unsigned int otherd; + uint32_t otherd; #ifdef SUPPORT_UNICODE if (utf && d >= 128) otherd = UCD_OTHERCASE(d); @@ -2539,11 +2638,13 @@ for (;;) if (isinclass) { int max = (int)GET2(ecode, 1 + IMM2_SIZE); - if (*ecode == OP_CRPOSRANGE) + + if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1)) { active_count--; /* Remove non-match possibility */ next_active_state--; } + if (++count >= max && max != 0) /* Max 0 => no limit */ { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } else @@ -2573,10 +2674,22 @@ for (;;) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: { - PCRE2_SPTR endasscode = code + GET(code, 1); - PCRE2_SIZE local_offsets[2]; int rc; - int local_workspace[1000]; + int *local_workspace; + PCRE2_SIZE *local_offsets; + PCRE2_SPTR endasscode = code + GET(code, 1); + RWS_anchor *rws = (RWS_anchor *)RWS; + + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); @@ -2586,12 +2699,15 @@ for (;;) ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ - if (rc == PCRE2_ERROR_DFA_UITEM) return rc; + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; + + if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc; if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK)) { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } } @@ -2601,8 +2717,6 @@ for (;;) case OP_COND: case OP_SCOND: { - PCRE2_SIZE local_offsets[1000]; - int local_workspace[1000]; int codelink = (int)GET(code, 1); PCRE2_UCHAR condcode; @@ -2613,45 +2727,10 @@ for (;;) if (code[LINK_SIZE + 1] == OP_CALLOUT || code[LINK_SIZE + 1] == OP_CALLOUT_STR) { - PCRE2_SIZE callout_length = (code[LINK_SIZE + 1] == OP_CALLOUT)? - (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] : - (PCRE2_SIZE)GET(code, 2 + 3*LINK_SIZE); - - rrc = 0; - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.capture_top = 1; - cb.capture_last = 0; - cb.offset_vector = offsets; - cb.mark = NULL; /* No (*MARK) support */ - cb.subject = start_subject; - cb.subject_length = (PCRE2_SIZE)(end_subject - start_subject); - cb.start_match = (PCRE2_SIZE)(current_subject - start_subject); - cb.current_position = (PCRE2_SIZE)(ptr - start_subject); - cb.pattern_position = GET(code, LINK_SIZE + 2); - cb.next_item_length = GET(code, LINK_SIZE + 2 + LINK_SIZE); - - if (code[LINK_SIZE + 1] == OP_CALLOUT) - { - cb.callout_number = code[2 + 3*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(code, 2 + 4*LINK_SIZE); - cb.callout_string = code + (2 + 5*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } - - if ((rrc = (mb->callout)(&cb, mb->callout_data)) < 0) - return rrc; /* Abandon */ - } + PCRE2_SIZE callout_length; + rrc = do_callout(code, offsets, current_subject, ptr, mb, + 1 + LINK_SIZE, &callout_length); + if (rrc < 0) return rrc; /* Abandon */ if (rrc > 0) break; /* Fail this thread */ code += callout_length; /* Skip callout data */ } @@ -2694,8 +2773,22 @@ for (;;) else { int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; PCRE2_SPTR asscode = code + LINK_SIZE + 1; PCRE2_SPTR endasscode = asscode + GET(asscode, 1); + RWS_anchor *rws = (RWS_anchor *)RWS; + + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); @@ -2705,12 +2798,15 @@ for (;;) ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ - if (rc == PCRE2_ERROR_DFA_UITEM) return rc; + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; + + if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc; if ((rc >= 0) == (condcode == OP_ASSERT || condcode == OP_ASSERTBACK)) { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } @@ -2723,13 +2819,25 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_RECURSE: { + int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; + RWS_anchor *rws = (RWS_anchor *)RWS; dfa_recursion_info *ri; - PCRE2_SIZE local_offsets[1000]; - int local_workspace[1000]; PCRE2_SPTR callpat = start_code + GET(code, 1); uint32_t recno = (callpat == mb->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE); - int rc; + + if (rws->free < RWS_RSIZE + RWS_OVEC_RSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_RSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_RSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_RSIZE; /* Check for repeating a recursion without advancing the subject pointer. This should catch convoluted mutual recursions. (Some simple @@ -2753,11 +2861,13 @@ for (;;) ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_RSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ + rws->free += RWS_RSIZE + RWS_OVEC_RSIZE; mb->recursive = new_recursive.prevrec; /* Done this recursion */ /* Ran out of internal offsets */ @@ -2803,10 +2913,25 @@ for (;;) case OP_SCBRAPOS: case OP_BRAPOSZERO: { + int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; PCRE2_SIZE charcount, matched_count; PCRE2_SPTR local_ptr = ptr; + RWS_anchor *rws = (RWS_anchor *)RWS; BOOL allow_zero; + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; + if (codevalue == OP_BRAPOSZERO) { allow_zero = TRUE; @@ -2819,19 +2944,17 @@ for (;;) for (matched_count = 0;; matched_count++) { - PCRE2_SIZE local_offsets[2]; - int local_workspace[1000]; - - int rc = internal_dfa_match( + rc = internal_dfa_match( mb, /* fixed match data */ code, /* this subexpression's code */ local_ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ /* Failed to match */ @@ -2848,6 +2971,8 @@ for (;;) local_ptr += charcount; /* Advance temporary position ptr */ } + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; + /* At this point we have matched the subpattern matched_count times, and local_ptr is pointing to the character after the end of the last match. */ @@ -2889,21 +3014,36 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_ONCE: - case OP_ONCE_NC: { - PCRE2_SIZE local_offsets[2]; - int local_workspace[1000]; + int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; + RWS_anchor *rws = (RWS_anchor *)RWS; - int rc = internal_dfa_match( + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; + + rc = internal_dfa_match( mb, /* fixed match data */ code, /* this subexpression's code */ ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ + + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; if (rc >= 0) { @@ -2984,44 +3124,10 @@ for (;;) case OP_CALLOUT: case OP_CALLOUT_STR: { - unsigned int callout_length = (*code == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(code, 1 + 2*LINK_SIZE); - rrc = 0; - - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.capture_top = 1; - cb.capture_last = 0; - cb.offset_vector = offsets; - cb.mark = NULL; /* No (*MARK) support */ - cb.subject = start_subject; - cb.subject_length = (PCRE2_SIZE)(end_subject - start_subject); - cb.start_match = (PCRE2_SIZE)(current_subject - start_subject); - cb.current_position = (PCRE2_SIZE)(ptr - start_subject); - cb.pattern_position = GET(code, 1); - cb.next_item_length = GET(code, 1 + LINK_SIZE); - - if (*code == OP_CALLOUT) - { - cb.callout_number = code[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(code, 1 + 3*LINK_SIZE); - cb.callout_string = code + (1 + 4*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } - - if ((rrc = (mb->callout)(&cb, mb->callout_data)) < 0) - return rrc; /* Abandon */ - } + PCRE2_SIZE callout_length; + rrc = do_callout(code, offsets, current_subject, ptr, mb, 0, + &callout_length); + if (rrc < 0) return rrc; /* Abandon */ if (rrc == 0) { ADD_ACTIVE(state_offset + (int)callout_length, 0); } } @@ -3069,7 +3175,7 @@ for (;;) ) ) match_count = PCRE2_ERROR_PARTIAL; - break; /* In effect, "return", but see the comment below */ + break; /* Exit from loop along the subject string */ } /* One or more states are active for the next character. */ @@ -3077,11 +3183,13 @@ for (;;) ptr += clen; /* Advance to next subject character */ } /* Loop to move along the subject string */ -/* Control gets here from "break" a few lines above. We do it this way because -if we use "return" above, we have compiler trouble. Some compilers warn if -there's nothing here because they think the function doesn't return a value. On -the other hand, if we put a dummy statement here, some more clever compilers -complain that it can't be reached. Sigh. */ +/* Control gets here from "break" a few lines above. If we have a match and +PCRE2_ENDANCHORED is set, the match fails. */ + +if (match_count >= 0 && + ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0 && + ptr < end_subject) + match_count = PCRE2_ERROR_NOMATCH; return match_count; } @@ -3115,8 +3223,9 @@ Returns: > 0 => number of match offset pairs placed in offsets PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_dfa_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data, - pcre2_match_context *mcontext, int *workspace, size_t wscount) + pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount) { +int rc; const pcre2_real_code *re = (const pcre2_real_code *)code; PCRE2_SPTR start_match; @@ -3125,9 +3234,9 @@ PCRE2_SPTR bumpalong_limit; PCRE2_SPTR req_cu_ptr; BOOL utf, anchored, startline, firstline; - BOOL has_first_cu = FALSE; BOOL has_req_cu = FALSE; + PCRE2_UCHAR first_cu = 0; PCRE2_UCHAR first_cu2 = 0; PCRE2_UCHAR req_cu = 0; @@ -3138,9 +3247,21 @@ const uint8_t *start_bits = NULL; /* We need to have mb pointing to a match block, because the IS_NEWLINE macro is used below, and it expects NLBLOCK to be defined as a pointer. */ +pcre2_callout_block cb; dfa_match_block actual_match_block; dfa_match_block *mb = &actual_match_block; +/* Set up a starting block of memory for use during recursive calls to +internal_dfa_match(). By putting this on the stack, it minimizes resource use +in the case when it is not needed. If this is too small, more memory is +obtained from the heap. At the start of each block is an anchor structure.*/ + +int base_recursion_workspace[RWS_BASE_SIZE]; +RWS_anchor *rws = (RWS_anchor *)base_recursion_workspace; +rws->next = NULL; +rws->size = RWS_BASE_SIZE; +rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE; + /* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated subject string. */ @@ -3154,6 +3275,13 @@ if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL) if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE; if (start_offset > length) return PCRE2_ERROR_BADOFFSET; +/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same +time. */ + +if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 && + ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0) + return PCRE2_ERROR_BADOPTION; + /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ @@ -3208,14 +3336,29 @@ startline = (re->flags & PCRE2_STARTLINE) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; bumpalong_limit = end_subject; -/* Get data from the match context, if present, and fill in the fields in the -match block. It is an error to set an offset limit without setting the flag at -compile time. */ +/* Initialize and set up the fixed fields in the callout block, with a pointer +in the match block. */ + +mb->cb = &cb; +cb.version = 2; +cb.subject = subject; +cb.subject_length = (PCRE2_SIZE)(end_subject - subject); +cb.callout_flags = 0; +cb.capture_top = 1; /* No capture support */ +cb.capture_last = 0; +cb.mark = NULL; /* No (*MARK) support */ + +/* Get data from the match context, if present, and fill in the remaining +fields in the match block. It is an error to set an offset limit without +setting the flag at compile time. */ if (mcontext == NULL) { mb->callout = NULL; mb->memctl = re->memctl; + mb->match_limit = PRIV(default_match_context).match_limit; + mb->match_limit_depth = PRIV(default_match_context).depth_limit; + mb->heap_limit = PRIV(default_match_context).heap_limit; } else { @@ -3228,8 +3371,20 @@ else mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->memctl = mcontext->memctl; + mb->match_limit = mcontext->match_limit; + mb->match_limit_depth = mcontext->depth_limit; + mb->heap_limit = mcontext->heap_limit; } +if (mb->match_limit > re->limit_match) + mb->match_limit = re->limit_match; + +if (mb->match_limit_depth > re->limit_depth) + mb->match_limit_depth = re->limit_depth; + +if (mb->heap_limit > re->limit_heap) + mb->heap_limit = re->limit_heap; + mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_count * re->name_entry_size; mb->tables = re->tables; @@ -3238,6 +3393,8 @@ mb->end_subject = end_subject; mb->start_offset = start_offset; mb->moptions = options; mb->poptions = re->overall_options; +mb->match_call_count = 0; +mb->heap_used = 0; /* Process the \R and newline settings. */ @@ -3255,6 +3412,11 @@ switch(re->newline_convention) mb->nl[0] = CHAR_NL; break; + case PCRE2_NEWLINE_NUL: + mb->nllen = 1; + mb->nl[0] = CHAR_NUL; + break; + case PCRE2_NEWLINE_CRLF: mb->nllen = 2; mb->nl[0] = CHAR_CR; @@ -3321,34 +3483,27 @@ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) } #endif /* SUPPORT_UNICODE */ -/* Set up the first code unit to match, if available. The first_codeunit value -is never set for an anchored regular expression, but the anchoring may be -forced at run time, so we have to test for anchoring. The first code unit may -be unset for an unanchored pattern, of course. If there's no first code unit -there may be a bitmap of possible first characters. */ +/* Set up the first code unit to match, if available. If there's no first code +unit there may be a bitmap of possible first characters. */ -if (!anchored) +if ((re->flags & PCRE2_FIRSTSET) != 0) { - if ((re->flags & PCRE2_FIRSTSET) != 0) + has_first_cu = TRUE; + first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); + if ((re->flags & PCRE2_FIRSTCASELESS) != 0) { - has_first_cu = TRUE; - first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); - if ((re->flags & PCRE2_FIRSTCASELESS) != 0) - { - first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu); + first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - if (utf && first_cu > 127) - first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu); + if (utf && first_cu > 127) + first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu); #endif - } } - else - if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) - start_bits = re->start_bitmap; } +else + if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) + start_bits = re->start_bitmap; -/* For anchored or unanchored matches, there may be a "last known required -character" set. */ +/* There may be a "last known required code unit" set. */ if ((re->flags & PCRE2_LASTSET) != 0) { @@ -3376,8 +3531,6 @@ a match. */ for (;;) { - int rc; - /* ----------------- Start of match optimizations ---------------- */ /* There are some optimizations that avoid running the match if a known @@ -3389,13 +3542,13 @@ for (;;) if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && (options & PCRE2_DFA_RESTART) == 0) { - PCRE2_SPTR save_end_subject = end_subject; - /* If firstline is TRUE, the start of the match is constrained to the first line of a multiline string. That is, the match must be before or at the - first newline. Implement this by temporarily adjusting end_subject so that - we stop the optimization scans at a newline. If the match fails at the - newline, later code breaks this loop. */ + first newline following the start of matching. Temporarily adjust + end_subject so that we stop the optimization scans for a first code unit + immediately after the first character of a newline (the first code unit can + legitimately be a newline). If the match fails at the newline, later code + breaks this loop. */ if (firstline) { @@ -3403,85 +3556,162 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - while (t < mb->end_subject && !IS_NEWLINE(t)) + while (t < end_subject && !IS_NEWLINE(t)) { t++; - ACROSSCHAR(t < end_subject, *t, t++); + ACROSSCHAR(t < end_subject, t, t++); } } else #endif - while (t < mb->end_subject && !IS_NEWLINE(t)) t++; + while (t < end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } - /* Advance to a unique first code unit if there is one. */ + /* Anchored: check the first code unit if one is recorded. This may seem + pointless but it can help in detecting a no match case without scanning for + the required code unit. */ - if (has_first_cu) + if (anchored) { - PCRE2_UCHAR smc; - if (first_cu != first_cu2) - while (start_match < end_subject && - (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2) - start_match++; - else - while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu) - start_match++; + if (has_first_cu || start_bits != NULL) + { + BOOL ok = start_match < end_subject; + if (ok) + { + PCRE2_UCHAR c = UCHAR21TEST(start_match); + ok = has_first_cu && (c == first_cu || c == first_cu2); + if (!ok && start_bits != NULL) + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (c > 255) c = 255; +#endif + ok = (start_bits[c/8] & (1 << (c&7))) != 0; + } + } + if (!ok) break; + } } - /* Or to just after a linebreak for a multiline match */ + /* Not anchored. Advance to a unique first code unit if there is one. In + 8-bit mode, the use of memchr() gives a big speed up, even though we have + to call it twice in caseless mode, in order to find the earliest occurrence + of the character in either of its cases. */ - else if (startline) + else { - if (start_match > mb->start_subject + start_offset) + if (has_first_cu) { -#ifdef SUPPORT_UNICODE - if (utf) + if (first_cu != first_cu2) /* Caseless */ { - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - { +#if PCRE2_CODE_UNIT_WIDTH != 8 + PCRE2_UCHAR smc; + while (start_match < end_subject && + (smc = UCHAR21TEST(start_match)) != first_cu && + smc != first_cu2) start_match++; - ACROSSCHAR(start_match < end_subject, *start_match, - start_match++); - } +#else /* 8-bit code units */ + PCRE2_SPTR pp1 = + memchr(start_match, first_cu, end_subject-start_match); + PCRE2_SPTR pp2 = + memchr(start_match, first_cu2, end_subject-start_match); + if (pp1 == NULL) + start_match = (pp2 == NULL)? end_subject : pp2; + else + start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2; +#endif } + + /* The caseful case */ + else + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + while (start_match < end_subject && UCHAR21TEST(start_match) != + first_cu) + start_match++; +#else + start_match = memchr(start_match, first_cu, end_subject - start_match); + if (start_match == NULL) start_match = end_subject; #endif - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - start_match++; + } - /* If we have just passed a CR and the newline option is ANY or - ANYCRLF, and we are now at a LF, advance the match position by one more - code unit. */ + /* If we can't find the required code unit, having reached the true end + of the subject, break the bumpalong loop, to force a match failure, + except when doing partial matching, when we let the next cycle run at + the end of the subject. To see why, consider the pattern /(?<=abc)def/, + which partially matches "abc", even though the string does not contain + the starting character "d". If we have not reached the true end of the + subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified) + we also let the cycle run, because the matching string is legitimately + allowed to start with the first code unit of a newline. */ - if (start_match[-1] == CHAR_CR && - (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && - start_match < end_subject && - UCHAR21TEST(start_match) == CHAR_NL) - start_match++; + if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 && + start_match >= mb->end_subject) + break; } - } - /* Or to a non-unique first code unit if any have been identified. The - bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all - code units greater than 254 set the 255 bit. */ + /* If there's no first code unit, advance to just after a linebreak for a + multiline match if required. */ - else if (start_bits != NULL) - { - while (start_match < end_subject) + else if (startline) + { + if (start_match > mb->start_subject + start_offset) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + { + start_match++; + ACROSSCHAR(start_match < end_subject, start_match, start_match++); + } + } + else +#endif + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + start_match++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one + more code unit. */ + + if (start_match[-1] == CHAR_CR && + (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + UCHAR21TEST(start_match) == CHAR_NL) + start_match++; + } + } + + /* If there's no first code unit or a requirement for a multiline line + start, advance to a non-unique first code unit if any have been + identified. The bitmap contains only 256 bits. When code units are 16 or + 32 bits wide, all code units greater than 254 set the 255 bit. */ + + else if (start_bits != NULL) { - register uint32_t c = UCHAR21TEST(start_match); + while (start_match < end_subject) + { + uint32_t c = UCHAR21TEST(start_match); #if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) c = 255; + if (c > 255) c = 255; #endif - if ((start_bits[c/8] & (1 << (c&7))) != 0) break; - start_match++; + if ((start_bits[c/8] & (1 << (c&7))) != 0) break; + start_match++; + } + + /* See comment above in first_cu checking about the next line. */ + + if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 && + start_match >= mb->end_subject) + break; } - } + } /* End of first code unit handling */ /* Restore fudged end_subject */ - end_subject = save_end_subject; + end_subject = mb->end_subject; /* The following two optimizations are disabled for partial matching. */ @@ -3492,7 +3722,7 @@ for (;;) in characters, we treat it as code units to avoid spending too much time in this optimization. */ - if (end_subject - start_match < re->minlength) return PCRE2_ERROR_NOMATCH; + if (end_subject - start_match < re->minlength) goto NOMATCH_EXIT; /* If req_cu is set, we know that that code unit must appear in the subject for the match to succeed. If the first code unit is set, req_cu @@ -3510,7 +3740,7 @@ for (;;) if (has_req_cu && end_subject - start_match < REQ_CU_MAX) { - register PCRE2_SPTR p = start_match + (has_first_cu? 1:0); + PCRE2_SPTR p = start_match + (has_first_cu? 1:0); /* We don't need to repeat the search if we haven't yet reached the place we found it at last time. */ @@ -3521,7 +3751,7 @@ for (;;) { while (p < end_subject) { - register uint32_t pp = UCHAR21INCTEST(p); + uint32_t pp = UCHAR21INCTEST(p); if (pp == req_cu || pp == req_cu2) { p--; break; } } } @@ -3569,7 +3799,8 @@ for (;;) (uint32_t)match_data->oveccount * 2, /* actual size of same */ workspace, /* workspace vector */ (int)wscount, /* size of same */ - 0); /* function recurse level */ + 0, /* function recurse level */ + base_recursion_workspace); /* initial workspace for recursion */ /* Anything other than "no match" means we are done, always; otherwise, carry on only if not anchored. */ @@ -3585,7 +3816,7 @@ for (;;) match_data->rightchar = (PCRE2_SIZE)( mb->last_used_ptr - subject); match_data->startchar = (PCRE2_SIZE)(start_match - subject); match_data->rc = rc; - return rc; + goto EXIT; } /* Advance to the next subject character unless we are at the end of a line @@ -3596,8 +3827,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - ACROSSCHAR(start_match < end_subject, *start_match, - start_match++); + ACROSSCHAR(start_match < end_subject, start_match, start_match++); } #endif if (start_match > end_subject) break; @@ -3617,8 +3847,18 @@ for (;;) } /* "Bumpalong" loop */ +NOMATCH_EXIT: +rc = PCRE2_ERROR_NOMATCH; + +EXIT: +while (rws->next != NULL) + { + RWS_anchor *next = rws->next; + rws->next = next->next; + mb->memctl.free(next, mb->memctl.memory_data); + } -return PCRE2_ERROR_NOMATCH; +return rc; } /* End of pcre2_dfa_match.c */ diff --git a/ProcessHacker/pcre/pcre2_error.c b/ProcessHacker/pcre/pcre2_error.c index dcb03e1fbb3c..4b3b3f1bc0d2 100644 --- a/ProcessHacker/pcre/pcre2_error.c +++ b/ProcessHacker/pcre/pcre2_error.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -91,13 +91,13 @@ static const unsigned char compile_error_texts[] = "failed to allocate heap memory\0" "unmatched closing parenthesis\0" "internal error: code overflow\0" - "letter or underscore expected after (?< or (?'\0" + "missing closing parenthesis for condition\0" /* 25 */ "lookbehind assertion is not fixed length\0" - "malformed number or name after (?(\0" + "a relative value of zero is not allowed\0" "conditional group contains more than two branches\0" "assertion expected after (?( or (?(?C)\0" - "(?R or (?[+-]digits must be followed by )\0" + "digit expected after (?+ or (?-\0" /* 30 */ "unknown POSIX class name\0" "internal error in pcre2_study(): should not occur\0" @@ -105,9 +105,9 @@ static const unsigned char compile_error_texts[] = "parentheses are too deeply nested (stack check)\0" "character code point value in \\x{} or \\o{} is too large\0" /* 35 */ - "invalid condition (?(0)\0" + "lookbehind is too complicated\0" "\\C is not allowed in a lookbehind assertion in UTF-" XSTRING(PCRE2_CODE_UNIT_WIDTH) " mode\0" - "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u\0" + "PCRE2 does not support \\F, \\L, \\l, \\N{name}, \\U, or \\u\0" "number after (?C is greater than 255\0" "closing parenthesis for (?C expected\0" /* 40 */ @@ -132,13 +132,14 @@ static const unsigned char compile_error_texts[] = "missing opening brace after \\o\0" "internal error: unknown newline setting\0" "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0" - "a numbered reference must not be zero\0" - "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" + "(?R (recursive pattern call) must be followed by a closing parenthesis\0" + /* "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" */ + "obsolete error (should not occur)\0" /* Was the above */ /* 60 */ "(*VERB) not recognized or malformed\0" - "number is too big\0" + "group number is too big\0" "subpattern name expected\0" - "digit expected after (?+\0" + "internal error: parsed pattern overflow\0" "non-octal character in \\o{} (closing brace missing?)\0" /* 65 */ "different names for subpatterns of the same number are not allowed\0" @@ -151,17 +152,17 @@ static const unsigned char compile_error_texts[] = #endif "\\k is not followed by a braced, angle-bracketed, or quoted name\0" /* 70 */ - "internal error: unknown opcode in find_fixedlength()\0" + "internal error: unknown meta code in check_lookbehinds()\0" "\\N is not supported in a class\0" - "SPARE ERROR\0" + "callout string is too long\0" "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0" "using UTF is disabled by the application\0" /* 75 */ "using UCP is disabled by the application\0" "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\0" "character code point value in \\u.... sequence is too large\0" - "digits missing in \\x{} or \\o{}\0" - "syntax error in (?(VERSION condition\0" + "digits missing in \\x{} or \\o{} or \\N{U+}\0" + "syntax error or number too big in (?(VERSION condition\0" /* 80 */ "internal error: unknown opcode in auto_possessify()\0" "missing terminating delimiter for callout with string argument\0" @@ -173,6 +174,13 @@ static const unsigned char compile_error_texts[] = "regular expression is too complicated\0" "lookbehind assertion is too long\0" "pattern string is longer than the limit set by the application\0" + "internal error: unknown code in parsed pattern\0" + /* 90 */ + "internal error: bad code value in parsed_skip()\0" + "PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\0" + "invalid option bits with PCRE2_LITERAL\0" + "\\N{U+dddd} is supported only in Unicode (UTF) mode\0" + "invalid hyphen in option setting\0" ; /* Match-time and UTF error texts are in the same format. */ @@ -241,7 +249,7 @@ static const unsigned char match_error_texts[] = "non-unique substring name\0" "NULL argument passed\0" "nested recursion at the same subject position\0" - "recursion limit exceeded\0" + "matching depth limit exceeded\0" "requested value is not available\0" /* 55 */ "requested value is not set\0" @@ -250,9 +258,13 @@ static const unsigned char match_error_texts[] = "expected closing curly bracket in replacement string\0" "bad substitution in replacement string\0" /* 60 */ - "match with end before start is not supported\0" + "match with end before start or start moved backwards is not supported\0" "too many replacements (more than INT_MAX)\0" "bad serialized data\0" + "heap limit exceeded\0" + "invalid syntax\0" + /* 65 */ + "internal error - duplicate substitution match\0" ; @@ -268,17 +280,17 @@ distinct. Arguments: enumber error number buffer where to put the message (zero terminated) - size size of the buffer + size size of the buffer in code units Returns: length of message if all is well negative on error */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION -pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, size_t size) +pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, PCRE2_SIZE size) { const unsigned char *message; -size_t i; +PCRE2_SIZE i; int n; if (size == 0) return PCRE2_ERROR_NOMEMORY; @@ -301,8 +313,8 @@ else /* Invalid error number */ for (; n > 0; n--) { - while (*message++ != CHAR_NULL) {}; - if (*message == CHAR_NULL) return PCRE2_ERROR_BADDATA; + while (*message++ != CHAR_NUL) {}; + if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA; } for (i = 0; *message != 0; i++) diff --git a/ProcessHacker/pcre/pcre2_extuni.c b/ProcessHacker/pcre/pcre2_extuni.c new file mode 100644 index 000000000000..237211abf7f9 --- /dev/null +++ b/ProcessHacker/pcre/pcre2_extuni.c @@ -0,0 +1,148 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains an internal function that is used to match a Unicode +extended grapheme sequence. It is used by both pcre2_match() and +pcre2_def_match(). However, it is called only when Unicode support is being +compiled. Nevertheless, we provide a dummy function when there is no Unicode +support, because some compilers do not like functionless source files. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include "pcre2_internal.h" + + +/* Dummy function */ + +#ifndef SUPPORT_UNICODE +PCRE2_SPTR +PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject, + PCRE2_SPTR end_subject, BOOL utf, int *xcount) +{ +(void)c; +(void)eptr; +(void)start_subject; +(void)end_subject; +(void)utf; +(void)xcount; +return NULL; +} +#else + + +/************************************************* +* Match an extended grapheme sequence * +*************************************************/ + +/* +Arguments: + c the first character + eptr pointer to next character + start_subject pointer to start of subject + end_subject pointer to end of subject + utf TRUE if in UTF mode + xcount pointer to count of additional characters, + or NULL if count not needed + +Returns: pointer after the end of the sequence +*/ + +PCRE2_SPTR +PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject, + PCRE2_SPTR end_subject, BOOL utf, int *xcount) +{ +int lgb = UCD_GRAPHBREAK(c); + +while (eptr < end_subject) + { + int rgb; + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + rgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ + + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + int ricount = 0; + PCRE2_SPTR bptr = eptr - 1; + if (utf) BACKCHAR(bptr); + + /* bptr is pointing to the left-hand character */ + + while (bptr > start_subject) + { + bptr--; + if (utf) + { + BACKCHAR(bptr); + GETCHAR(c, bptr); + } + else + c = *bptr; + if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this + allows any number of them before a following Extended_Pictographic. */ + + if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || + lgb != ucp_gbExtended_Pictographic) + lgb = rgb; + + eptr += len; + if (xcount != NULL) *xcount += 1; + } + +return eptr; +} + +#endif /* SUPPORT_UNICODE */ + +/* End of pcre2_extuni.c */ diff --git a/ProcessHacker/pcre/pcre2_find_bracket.c b/ProcessHacker/pcre/pcre2_find_bracket.c index 30d407aba5e9..70baa1394f57 100644 --- a/ProcessHacker/pcre/pcre2_find_bracket.c +++ b/ProcessHacker/pcre/pcre2_find_bracket.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -45,7 +45,7 @@ negative, an instance of OP_REVERSE for a lookbehind. The function is called from pcre2_compile.c and also from pcre2_study.c when finding the minimum matching length. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -71,7 +71,7 @@ PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number) { for (;;) { - register PCRE2_UCHAR c = *code; + PCRE2_UCHAR c = *code; if (c == OP_END) return NULL; @@ -131,6 +131,7 @@ for (;;) break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/ProcessHacker/pcre/pcre2_fuzzsupport.c b/ProcessHacker/pcre/pcre2_fuzzsupport.c new file mode 100644 index 000000000000..48781ffc0996 --- /dev/null +++ b/ProcessHacker/pcre/pcre2_fuzzsupport.c @@ -0,0 +1,365 @@ +/*************************************************************************** +Fuzzer driver for PCRE2. Given an arbitrary string of bytes and a length, it +tries to compile and match it, deriving options from the string itself. If +STANDALONE is defined, a main program that calls the driver with the contents +of specified files is compiled, and commentary on what is happening is output. +If an argument starts with '=' the rest of it it is taken as a literal string +rather than a file name. This allows easy testing of short strings. + +Written by Philip Hazel, October 2016 +***************************************************************************/ + +#include +#include +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" + +#define MAX_MATCH_SIZE 1000 + +#define DFA_WORKSPACE_COUNT 100 + +#define ALLOWED_COMPILE_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ + PCRE2_ALT_VERBNAMES|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_DOLLAR_ENDONLY| \ + PCRE2_DOTALL|PCRE2_DUPNAMES|PCRE2_ENDANCHORED|PCRE2_EXTENDED|PCRE2_FIRSTLINE| \ + PCRE2_MATCH_UNSET_BACKREF|PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C| \ + PCRE2_NO_AUTO_CAPTURE| \ + PCRE2_NO_AUTO_POSSESS|PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_NO_START_OPTIMIZE| \ + PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_USE_OFFSET_LIMIT| \ + PCRE2_UTF) + +#define ALLOWED_MATCH_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ + PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_HARD| \ + PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT) + +/* This is the callout function. Its only purpose is to halt matching if there +are more than 100 callouts, as one way of stopping too much time being spent on +fruitless matches. The callout data is a pointer to the counter. */ + +static int callout_function(pcre2_callout_block *cb, void *callout_data) +{ +(void)cb; /* Avoid unused parameter warning */ +*((uint32_t *)callout_data) += 1; +return (*((uint32_t *)callout_data) > 100)? PCRE2_ERROR_CALLOUT : 0; +} + +/* Putting in this apparently unnecessary prototype prevents gcc from giving a +"no previous prototype" warning when compiling at high warning level. */ + +int LLVMFuzzerTestOneInput(const unsigned char *, size_t); + +/* Here's the driving function. */ + +int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) +{ +uint32_t compile_options; +uint32_t match_options; +pcre2_match_data *match_data = NULL; +pcre2_match_context *match_context = NULL; +size_t match_size; +int dfa_workspace[DFA_WORKSPACE_COUNT]; +int r1, r2; +int i; + +if (size < 1) return 0; + +/* Limiting the length of the subject for matching stops fruitless searches +in large trees taking too much time. */ + +match_size = (size > MAX_MATCH_SIZE)? MAX_MATCH_SIZE : size; + +/* Figure out some options to use. Initialize the random number to ensure +repeatability. Ensure that we get a 32-bit unsigned random number for testing +options. (RAND_MAX is required to be at least 32767, but is commonly +2147483647, which excludes the top bit.) */ + +srand((unsigned int)(data[size/2])); +r1 = rand(); +r2 = rand(); + +/* Ensure that all undefined option bits are zero (waste of time trying them) +and also that PCRE2_NO_UTF_CHECK is unset, as there is no guarantee that the +input is UTF-8. Also unset PCRE2_NEVER_UTF and PCRE2_NEVER_UCP as there is no +reason to disallow UTF and UCP. Force PCRE2_NEVER_BACKSLASH_C to be set because +\C in random patterns is highly likely to cause a crash. */ + +compile_options = + ((((uint32_t)r1 << 16) | ((uint32_t)r2 & 0xffff)) & ALLOWED_COMPILE_OPTIONS) | + PCRE2_NEVER_BACKSLASH_C; + +match_options = + ((((uint32_t)r1 << 16) | ((uint32_t)r2 & 0xffff)) & ALLOWED_MATCH_OPTIONS); + +/* Discard partial matching if PCRE2_ENDANCHORED is set, because they are not +allowed together and just give an immediate error return. */ + +if (((compile_options|match_options) & PCRE2_ENDANCHORED) != 0) + match_options &= ~(PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT); + +/* Do the compile with and without the options, and after a successful compile, +likewise do the match with and without the options. */ + +for (i = 0; i < 2; i++) + { + uint32_t callout_count; + int errorcode; + PCRE2_SIZE erroroffset; + pcre2_code *code; + +#ifdef STANDALONE + printf("Compile options %.8x never_backslash_c", compile_options); + printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + ((compile_options & PCRE2_ALT_BSUX) != 0)? ",alt_bsux" : "", + ((compile_options & PCRE2_ALT_CIRCUMFLEX) != 0)? ",alt_circumflex" : "", + ((compile_options & PCRE2_ALT_VERBNAMES) != 0)? ",alt_verbnames" : "", + ((compile_options & PCRE2_ALLOW_EMPTY_CLASS) != 0)? ",allow_empty_class" : "", + ((compile_options & PCRE2_ANCHORED) != 0)? ",anchored" : "", + ((compile_options & PCRE2_AUTO_CALLOUT) != 0)? ",auto_callout" : "", + ((compile_options & PCRE2_CASELESS) != 0)? ",caseless" : "", + ((compile_options & PCRE2_DOLLAR_ENDONLY) != 0)? ",dollar_endonly" : "", + ((compile_options & PCRE2_DOTALL) != 0)? ",dotall" : "", + ((compile_options & PCRE2_DUPNAMES) != 0)? ",dupnames" : "", + ((compile_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "", + ((compile_options & PCRE2_EXTENDED) != 0)? ",extended" : "", + ((compile_options & PCRE2_FIRSTLINE) != 0)? ",firstline" : "", + ((compile_options & PCRE2_MATCH_UNSET_BACKREF) != 0)? ",match_unset_backref" : "", + ((compile_options & PCRE2_MULTILINE) != 0)? ",multiline" : "", + ((compile_options & PCRE2_NEVER_UCP) != 0)? ",never_ucp" : "", + ((compile_options & PCRE2_NEVER_UTF) != 0)? ",never_utf" : "", + ((compile_options & PCRE2_NO_AUTO_CAPTURE) != 0)? ",no_auto_capture" : "", + ((compile_options & PCRE2_NO_AUTO_POSSESS) != 0)? ",no_auto_possess" : "", + ((compile_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)? ",no_dotstar_anchor" : "", + ((compile_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "", + ((compile_options & PCRE2_NO_START_OPTIMIZE) != 0)? ",no_start_optimize" : "", + ((compile_options & PCRE2_UCP) != 0)? ",ucp" : "", + ((compile_options & PCRE2_UNGREEDY) != 0)? ",ungreedy" : "", + ((compile_options & PCRE2_USE_OFFSET_LIMIT) != 0)? ",use_offset_limit" : "", + ((compile_options & PCRE2_UTF) != 0)? ",utf" : ""); +#endif + + code = pcre2_compile((PCRE2_SPTR)data, (PCRE2_SIZE)size, compile_options, + &errorcode, &erroroffset, NULL); + + /* Compilation succeeded */ + + if (code != NULL) + { + int j; + uint32_t save_match_options = match_options; + + /* Create match data and context blocks only when we first need them. Set + low match and depth limits to avoid wasting too much searching large + pattern trees. Almost all matches are going to fail. */ + + if (match_data == NULL) + { + match_data = pcre2_match_data_create(32, NULL); + if (match_data == NULL) + { +#ifdef STANDALONE + printf("** Failed to create match data block\n"); +#endif + return 0; + } + } + + if (match_context == NULL) + { + match_context = pcre2_match_context_create(NULL); + if (match_context == NULL) + { +#ifdef STANDALONE + printf("** Failed to create match context block\n"); +#endif + return 0; + } + (void)pcre2_set_match_limit(match_context, 100); + (void)pcre2_set_depth_limit(match_context, 100); + (void)pcre2_set_callout(match_context, callout_function, &callout_count); + } + + /* Match twice, with and without options. */ + + for (j = 0; j < 2; j++) + { +#ifdef STANDALONE + printf("Match options %.8x", match_options); + printf("%s%s%s%s%s%s%s%s%s%s\n", + ((match_options & PCRE2_ANCHORED) != 0)? ",anchored" : "", + ((match_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "", + ((match_options & PCRE2_NO_JIT) != 0)? ",no_jit" : "", + ((match_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "", + ((match_options & PCRE2_NOTBOL) != 0)? ",notbol" : "", + ((match_options & PCRE2_NOTEMPTY) != 0)? ",notempty" : "", + ((match_options & PCRE2_NOTEMPTY_ATSTART) != 0)? ",notempty_atstart" : "", + ((match_options & PCRE2_NOTEOL) != 0)? ",noteol" : "", + ((match_options & PCRE2_PARTIAL_HARD) != 0)? ",partial_hard" : "", + ((match_options & PCRE2_PARTIAL_SOFT) != 0)? ",partial_soft" : ""); +#endif + + callout_count = 0; + errorcode = pcre2_match(code, (PCRE2_SPTR)data, (PCRE2_SIZE)match_size, 0, + match_options, match_data, match_context); + +#ifdef STANDALONE + if (errorcode >= 0) printf("Match returned %d\n", errorcode); else + { + unsigned char buffer[256]; + pcre2_get_error_message(errorcode, buffer, 256); + printf("Match failed: error %d: %s\n", errorcode, buffer); + } +#endif + + match_options = 0; /* For second time */ + } + + /* Match with DFA twice, with and without options. */ + + match_options = save_match_options & ~PCRE2_NO_JIT; /* Not valid for DFA */ + + for (j = 0; j < 2; j++) + { +#ifdef STANDALONE + printf("DFA match options %.8x", match_options); + printf("%s%s%s%s%s%s%s%s%s\n", + ((match_options & PCRE2_ANCHORED) != 0)? ",anchored" : "", + ((match_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "", + ((match_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "", + ((match_options & PCRE2_NOTBOL) != 0)? ",notbol" : "", + ((match_options & PCRE2_NOTEMPTY) != 0)? ",notempty" : "", + ((match_options & PCRE2_NOTEMPTY_ATSTART) != 0)? ",notempty_atstart" : "", + ((match_options & PCRE2_NOTEOL) != 0)? ",noteol" : "", + ((match_options & PCRE2_PARTIAL_HARD) != 0)? ",partial_hard" : "", + ((match_options & PCRE2_PARTIAL_SOFT) != 0)? ",partial_soft" : ""); +#endif + + callout_count = 0; + errorcode = pcre2_dfa_match(code, (PCRE2_SPTR)data, + (PCRE2_SIZE)match_size, 0, match_options, match_data, match_context, + dfa_workspace, DFA_WORKSPACE_COUNT); + +#ifdef STANDALONE + if (errorcode >= 0) printf("Match returned %d\n", errorcode); else + { + unsigned char buffer[256]; + pcre2_get_error_message(errorcode, buffer, 256); + printf("Match failed: error %d: %s\n", errorcode, buffer); + } +#endif + + match_options = 0; /* For second time */ + } + + match_options = save_match_options; /* Reset for the second compile */ + pcre2_code_free(code); + } + + /* Compilation failed */ + + else + { + unsigned char buffer[256]; + pcre2_get_error_message(errorcode, buffer, 256); +#ifdef STANDALONE + printf("Error %d at offset %lu: %s\n", errorcode, erroroffset, buffer); +#else + if (strstr((const char *)buffer, "internal error") != NULL) abort(); +#endif + } + + compile_options = PCRE2_NEVER_BACKSLASH_C; /* For second time */ + } + +if (match_data != NULL) pcre2_match_data_free(match_data); +if (match_context != NULL) pcre2_match_context_free(match_context); + +return 0; +} + + +/* Optional main program. */ + +#ifdef STANDALONE +int main(int argc, char **argv) +{ +int i; + +if (argc < 2) + { + printf("** No arguments given\n"); + return 0; + } + +for (i = 1; i < argc; i++) + { + size_t filelen; + size_t readsize; + unsigned char *buffer; + FILE *f; + + /* Handle a literal string. Copy to an exact size buffer so that checks for + overrunning work. */ + + if (argv[i][0] == '=') + { + readsize = strlen(argv[i]) - 1; + printf("------ ------\n"); + printf("Length = %lu\n", readsize); + printf("%.*s\n", (int)readsize, argv[i]+1); + buffer = (unsigned char *)malloc(readsize); + if (buffer == NULL) + printf("** Failed to allocate %lu bytes of memory\n", readsize); + else + { + memcpy(buffer, argv[i]+1, readsize); + LLVMFuzzerTestOneInput(buffer, readsize); + free(buffer); + } + continue; + } + + /* Handle a string given in a file */ + + f = fopen(argv[i], "rb"); + if (f == NULL) + { + printf("** Failed to open %s: %s\n", argv[i], strerror(errno)); + continue; + } + + printf("------ %s ------\n", argv[i]); + + fseek(f, 0, SEEK_END); + filelen = ftell(f); + fseek(f, 0, SEEK_SET); + + buffer = (unsigned char *)malloc(filelen); + if (buffer == NULL) + { + printf("** Failed to allocate %lu bytes of memory\n", filelen); + fclose(f); + continue; + } + + readsize = fread(buffer, 1, filelen, f); + fclose(f); + + if (readsize != filelen) + printf("** File size is %lu but fread() returned %lu\n", filelen, readsize); + else + { + printf("Length = %lu\n", filelen); + LLVMFuzzerTestOneInput(buffer, filelen); + } + free(buffer); + } + +return 0; +} +#endif /* STANDALONE */ + +/* End */ diff --git a/ProcessHacker/pcre/pcre2_internal.h b/ProcessHacker/pcre/pcre2_internal.h index 56908708aa13..8750f2f17468 100644 --- a/ProcessHacker/pcre/pcre2_internal.h +++ b/ProcessHacker/pcre/pcre2_internal.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,6 +38,9 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ +#ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD +#define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD + /* We do not support both EBCDIC and Unicode at the same time. The "configure" script prevents both being selected, but not everybody uses "configure". EBCDIC is only supported for the 8-bit library, but the check for this has to be later @@ -142,20 +145,6 @@ pcre2_match() because of the way it backtracks. */ #define PCRE2_SPTR CUSTOM_SUBJECT_PTR #endif -/* When compiling with the MSVC compiler, it is sometimes necessary to include -a "calling convention" before exported function names. (This is secondhand -information; I know nothing about MSVC myself). For example, something like - - void __cdecl function(....) - -might be needed. In order so make this easy, all the exported functions have -PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not -set, we ensure here that it has no effect. */ - -#ifndef PCRE2_CALL_CONVENTION -#define PCRE2_CALL_CONVENTION -#endif - /* When checking for integer overflow in pcre2_compile(), we need to handle large integers. If a 64-bit integer type is available, we can use that. Otherwise we have to cast to double, which of course requires floating point @@ -176,6 +165,16 @@ by "configure". */ #define INT64_OR_DOUBLE double #endif +/* External (in the C sense) functions and tables that are private to the +libraries are always referenced using the PRIV macro. This makes it possible +for pcre2test.c to include some of the source files from the libraries using a +different PRIV definition to avoid name clashes. It also makes it clear in the +code that a non-static object is being referenced. */ + +#ifndef PRIV +#define PRIV(name) _pcre2_##name +#endif + /* When compiling for use with the Virtual Pascal compiler, these functions need to have their names changed. PCRE2 must be compiled with the -DVPCOMPAT option on the command line. */ @@ -189,50 +188,15 @@ option on the command line. */ #define memset(s,c,n) _memset(s,c,n) #else /* VPCOMPAT */ -/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), -define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY -is set. Otherwise, include an emulating function for those systems that have -neither (there some non-Unix environments where this is the case). */ +/* Otherwise, to cope with SunOS4 and other systems that lack memmove(), define +a macro that calls an emulating function. */ #ifndef HAVE_MEMMOVE -#undef memmove /* some systems may have a macro */ -#ifdef HAVE_BCOPY -#define memmove(a, b, c) bcopy(b, a, c) -#else /* HAVE_BCOPY */ -static void * -pcre2_memmove(void *d, const void *s, size_t n) -{ -size_t i; -unsigned char *dest = (unsigned char *)d; -const unsigned char *src = (const unsigned char *)s; -if (dest > src) - { - dest += n; - src += n; - for (i = 0; i < n; ++i) *(--dest) = *(--src); - return (void *)dest; - } -else - { - for (i = 0; i < n; ++i) *dest++ = *src++; - return (void *)(dest - n); - } -} -#define memmove(a, b, c) pcre2_memmove(a, b, c) -#endif /* not HAVE_BCOPY */ +#undef memmove /* Some systems may have a macro */ +#define memmove(a, b, c) PRIV(memmove)(a, b, c) #endif /* not HAVE_MEMMOVE */ #endif /* not VPCOMPAT */ -/* External (in the C sense) functions and tables that are private to the -libraries are always referenced using the PRIV macro. This makes it possible -for pcre2test.c to include some of the source files from the libraries using a -different PRIV definition to avoid name clashes. It also makes it clear in the -code that a non-static object is being referenced. */ - -#ifndef PRIV -#define PRIV(name) _pcre2_##name -#endif - /* This is an unsigned int value that no UTF character can ever have, as Unicode doesn't go beyond 0x0010ffff. */ @@ -254,6 +218,21 @@ not rely on this. */ #define COMPILE_ERROR_BASE 100 +/* The initial frames vector for remembering backtracking points in +pcre2_match() is allocated on the system stack, of this size (bytes). The size +must be a multiple of sizeof(PCRE2_SPTR) in all environments, so making it a +multiple of 8 is best. Typical frame sizes are a few hundred bytes (it depends +on the number of capturing parentheses) so 20KiB handles quite a few frames. A +larger vector on the heap is obtained for patterns that need more frames. The +maximum size of this can be limited. */ + +#define START_FRAMES_SIZE 20480 + +/* Similarly, for DFA matching, an initial internal workspace vector is +allocated on the stack. */ + +#define DFA_START_RWS_SIZE 30720 + /* Define the default BSR convention. */ #ifdef BSR_ANYCRLF @@ -561,9 +540,14 @@ enum { PCRE2_MATCHEDBY_INTERPRETER, /* pcre2_match() */ #define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ /* The maximum remaining length of subject we are prepared to search for a -req_unit match. */ +req_unit match. In 8-bit mode, memchr() is used and is much faster than the +search loop that has to be used in 16-bit and 32-bit modes. */ +#if PCRE2_CODE_UNIT_WIDTH == 8 +#define REQ_CU_MAX 2000 +#else #define REQ_CU_MAX 1000 +#endif /* Offsets for the bitmap tables in the cbits set of tables. Each table contains a set of bits for a class map. Some classes are built by combining @@ -581,14 +565,15 @@ these tables. */ #define cbit_cntrl 288 /* [:cntrl:] */ #define cbit_length 320 /* Length of the cbits table */ -/* Bit definitions for entries in the ctypes table. */ +/* Bit definitions for entries in the ctypes table. Do not change these values +without checking pcre2_jit_compile.c, which has an assertion to ensure that +ctype_word has the value 16. */ #define ctype_space 0x01 #define ctype_letter 0x02 #define ctype_digit 0x04 -#define ctype_xdigit 0x08 +#define ctype_xdigit 0x08 /* not actually used any more */ #define ctype_word 0x10 /* alphanumeric or '_' */ -#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ /* Offsets of the various tables from the base tables pointer, and total length of the tables. */ @@ -682,7 +667,7 @@ a positive value. */ /* The remaining definitions work in both environments. */ -#define CHAR_NULL '\0' +#define CHAR_NUL '\0' #define CHAR_HT '\t' #define CHAR_VT '\v' #define CHAR_FF '\f' @@ -923,6 +908,7 @@ a positive value. */ #define STRING_CRLF_RIGHTPAR "CRLF)" #define STRING_ANY_RIGHTPAR "ANY)" #define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)" +#define STRING_NUL_RIGHTPAR "NUL)" #define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)" #define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)" #define STRING_UTF8_RIGHTPAR "UTF8)" @@ -936,7 +922,9 @@ a positive value. */ #define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)" #define STRING_NOTEMPTY_RIGHTPAR "NOTEMPTY)" #define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)" +#define STRING_LIMIT_HEAP_EQ "LIMIT_HEAP=" #define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH=" +#define STRING_LIMIT_DEPTH_EQ "LIMIT_DEPTH=" #define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION=" #define STRING_MARK "MARK" @@ -958,7 +946,7 @@ only. */ #define CHAR_ESC '\033' #define CHAR_DEL '\177' -#define CHAR_NULL '\0' +#define CHAR_NUL '\0' #define CHAR_SPACE '\040' #define CHAR_EXCLAMATION_MARK '\041' #define CHAR_QUOTATION_MARK '\042' @@ -1196,6 +1184,7 @@ only. */ #define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS #define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_NUL_RIGHTPAR STR_N STR_U STR_L STR_RIGHT_PARENTHESIS #define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS #define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS @@ -1209,7 +1198,9 @@ only. */ #define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS #define STRING_NOTEMPTY_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS #define STRING_NOTEMPTY_ATSTART_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS +#define STRING_LIMIT_HEAP_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN #define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN +#define STRING_LIMIT_DEPTH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T STR_H STR_EQUALS_SIGN #define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN #define STRING_MARK STR_M STR_A STR_R STR_K @@ -1257,36 +1248,6 @@ contain characters with values greater than 255. */ #define XCL_PROP 3 /* Unicode property (2-byte property code follows) */ #define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ -/* Escape items that are just an encoding of a particular data value. These -appear in the escapes[] table in pcre2_compile.c as positive numbers. */ - -#ifndef ESC_a -#define ESC_a CHAR_BEL -#endif - -#ifndef ESC_e -#define ESC_e CHAR_ESC -#endif - -#ifndef ESC_f -#define ESC_f CHAR_FF -#endif - -#ifndef ESC_n -#define ESC_n CHAR_LF -#endif - -#ifndef ESC_r -#define ESC_r CHAR_CR -#endif - -/* We can't officially use ESC_t because it is a POSIX reserved identifier -(presumably because of all the others like size_t). */ - -#ifndef ESC_tee -#define ESC_tee CHAR_HT -#endif - /* These are escaped items that aren't just an encoding of a particular data value such as \n. They must have non-zero values, as check_escape() returns 0 for a data character. In the escapes[] table in pcre2_compile.c their values @@ -1298,23 +1259,16 @@ mode rather than an escape sequence. It is also used for [^] in JavaScript compatibility mode, and for \C in non-utf mode. In non-DOTALL mode, "." behaves like \N. -The special values ESC_DU, ESC_du, etc. are used instead of ESC_D, ESC_d, etc. -when PCRE2_UCP is set and replacement of \d etc by \p sequences is required. -They must be contiguous, and remain in order so that the replacements can be -looked up from a table. - Negative numbers are used to encode a backreference (\1, \2, \3, etc.) in -check_escape(). There are two tests in the code for an escape -greater than ESC_b and less than ESC_Z to detect the types that may be -repeated. These are the types that consume characters. If any new escapes are -put in between that don't consume a character, that code will have to change. -*/ +check_escape(). There are tests in the code for an escape greater than ESC_b +and less than ESC_Z to detect the types that may be repeated. These are the +types that consume characters. If any new escapes are put in between that don't +consume a character, that code will have to change. */ enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, - ESC_E, ESC_Q, ESC_g, ESC_k, - ESC_DU, ESC_du, ESC_SU, ESC_su, ESC_WU, ESC_wu }; + ESC_E, ESC_Q, ESC_g, ESC_k }; /********************** Opcode definitions ******************/ @@ -1380,7 +1334,8 @@ enum { OP_CIRC, /* 27 Start of line - not multiline */ OP_CIRCM, /* 28 Start of line - multiline */ - /* Single characters; caseful must precede the caseless ones */ + /* Single characters; caseful must precede the caseless ones, and these + must remain in this order, and adjacent. */ OP_CHAR, /* 29 Match one character, casefully */ OP_CHARI, /* 30 Match one character, caselessly */ @@ -1530,53 +1485,55 @@ enum { OP_ASSERTBACK, /* 128 Positive lookbehind */ OP_ASSERTBACK_NOT, /* 129 Negative lookbehind */ - /* ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately - after the assertions, with ONCE first, as there's a test for >= ONCE for a - subpattern that isn't an assertion. The POS versions must immediately follow - the non-POS versions in each case. */ + /* ONCE, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately after the + assertions, with ONCE first, as there's a test for >= ONCE for a subpattern + that isn't an assertion. The POS versions must immediately follow the non-POS + versions in each case. */ OP_ONCE, /* 130 Atomic group, contains captures */ - OP_ONCE_NC, /* 131 Atomic group containing no captures */ - OP_BRA, /* 132 Start of non-capturing bracket */ - OP_BRAPOS, /* 133 Ditto, with unlimited, possessive repeat */ - OP_CBRA, /* 134 Start of capturing bracket */ - OP_CBRAPOS, /* 135 Ditto, with unlimited, possessive repeat */ - OP_COND, /* 136 Conditional group */ + OP_BRA, /* 131 Start of non-capturing bracket */ + OP_BRAPOS, /* 132 Ditto, with unlimited, possessive repeat */ + OP_CBRA, /* 133 Start of capturing bracket */ + OP_CBRAPOS, /* 134 Ditto, with unlimited, possessive repeat */ + OP_COND, /* 135 Conditional group */ /* These five must follow the previous five, in the same order. There's a check for >= SBRA to distinguish the two sets. */ - OP_SBRA, /* 137 Start of non-capturing bracket, check empty */ - OP_SBRAPOS, /* 138 Ditto, with unlimited, possessive repeat */ - OP_SCBRA, /* 139 Start of capturing bracket, check empty */ - OP_SCBRAPOS, /* 140 Ditto, with unlimited, possessive repeat */ - OP_SCOND, /* 141 Conditional group, check empty */ + OP_SBRA, /* 136 Start of non-capturing bracket, check empty */ + OP_SBRAPOS, /* 137 Ditto, with unlimited, possessive repeat */ + OP_SCBRA, /* 138 Start of capturing bracket, check empty */ + OP_SCBRAPOS, /* 139 Ditto, with unlimited, possessive repeat */ + OP_SCOND, /* 140 Conditional group, check empty */ /* The next two pairs must (respectively) be kept together. */ - OP_CREF, /* 142 Used to hold a capture number as condition */ - OP_DNCREF, /* 143 Used to point to duplicate names as a condition */ - OP_RREF, /* 144 Used to hold a recursion number as condition */ - OP_DNRREF, /* 145 Used to point to duplicate names as a condition */ - OP_FALSE, /* 146 Always false (used by DEFINE and VERSION) */ - OP_TRUE, /* 147 Always true (used by VERSION) */ + OP_CREF, /* 141 Used to hold a capture number as condition */ + OP_DNCREF, /* 142 Used to point to duplicate names as a condition */ + OP_RREF, /* 143 Used to hold a recursion number as condition */ + OP_DNRREF, /* 144 Used to point to duplicate names as a condition */ + OP_FALSE, /* 145 Always false (used by DEFINE and VERSION) */ + OP_TRUE, /* 146 Always true (used by VERSION) */ - OP_BRAZERO, /* 148 These two must remain together and in this */ - OP_BRAMINZERO, /* 149 order. */ - OP_BRAPOSZERO, /* 150 */ + OP_BRAZERO, /* 147 These two must remain together and in this */ + OP_BRAMINZERO, /* 148 order. */ + OP_BRAPOSZERO, /* 149 */ /* These are backtracking control verbs */ - OP_MARK, /* 151 always has an argument */ - OP_PRUNE, /* 152 */ - OP_PRUNE_ARG, /* 153 same, but with argument */ - OP_SKIP, /* 154 */ - OP_SKIP_ARG, /* 155 same, but with argument */ - OP_THEN, /* 156 */ - OP_THEN_ARG, /* 157 same, but with argument */ - OP_COMMIT, /* 158 */ + OP_MARK, /* 150 always has an argument */ + OP_PRUNE, /* 151 */ + OP_PRUNE_ARG, /* 152 same, but with argument */ + OP_SKIP, /* 153 */ + OP_SKIP_ARG, /* 154 same, but with argument */ + OP_THEN, /* 155 */ + OP_THEN_ARG, /* 156 same, but with argument */ + OP_COMMIT, /* 157 */ + OP_COMMIT_ARG, /* 158 same, but with argument */ - /* These are forced failure and success verbs */ + /* These are forced failure and success verbs. FAIL and ACCEPT do accept an + argument, but these cases can be compiled as, for example, (*MARK:X)(*FAIL) + without the need for a special opcode. */ OP_FAIL, /* 159 */ OP_ACCEPT, /* 160 */ @@ -1638,7 +1595,7 @@ some cases doesn't actually use these names at all). */ "Recurse", "Callout", "CalloutStr", \ "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \ "Reverse", "Assert", "Assert not", "AssertB", "AssertB not", \ - "Once", "Once_NC", \ + "Once", \ "Bra", "BraPos", "CBra", "CBraPos", \ "Cond", \ "SBra", "SBraPos", "SCBra", "SCBraPos", \ @@ -1647,7 +1604,7 @@ some cases doesn't actually use these names at all). */ "Cond false", "Cond true", \ "Brazero", "Braminzero", "Braposzero", \ "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \ - "*THEN", "*THEN", "*COMMIT", "*FAIL", \ + "*THEN", "*THEN", "*COMMIT", "*COMMIT", "*FAIL", \ "*ACCEPT", "*ASSERT_ACCEPT", \ "Close", "Skip zero", "Define" @@ -1722,7 +1679,6 @@ in UTF-8 mode. The code that uses this table must know about such things. */ 1+LINK_SIZE, /* Assert behind */ \ 1+LINK_SIZE, /* Assert behind not */ \ 1+LINK_SIZE, /* ONCE */ \ - 1+LINK_SIZE, /* ONCE_NC */ \ 1+LINK_SIZE, /* BRA */ \ 1+LINK_SIZE, /* BRAPOS */ \ 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \ @@ -1740,7 +1696,8 @@ in UTF-8 mode. The code that uses this table must know about such things. */ 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \ 1, 3, /* SKIP, SKIP_ARG */ \ 1, 3, /* THEN, THEN_ARG */ \ - 1, 1, 1, 1, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ \ + 1, 3, /* COMMIT, COMMIT_ARG */ \ + 1, 1, 1, /* FAIL, ACCEPT, ASSERT_ACCEPT */ \ 1+IMM2_SIZE, 1, /* CLOSE, SKIPZERO */ \ 1 /* DEFINE */ @@ -1768,6 +1725,7 @@ typedef struct open_capitem { struct open_capitem *next; /* Chain link */ uint16_t number; /* Capture number */ uint16_t flag; /* Set TRUE if recursive back ref */ + uint16_t assert_depth; /* Assertion depth when opened */ } open_capitem; /* Layout of the UCP type table that translates property names into types and @@ -1794,10 +1752,17 @@ typedef struct { /* UCD access macros */ #define UCD_BLOCK_SIZE 128 -#define GET_UCD(ch) (PRIV(ucd_records) + \ +#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \ PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \ UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE]) +#if PCRE2_CODE_UNIT_WIDTH == 32 +#define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \ + PRIV(dummy_ucd_record) : REAL_GET_UCD(ch)) +#else +#define GET_UCD(ch) REAL_GET_UCD(ch) +#endif + #define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype #define UCD_SCRIPT(ch) GET_UCD(ch)->script #define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)] @@ -1852,8 +1817,12 @@ extern const uint8_t PRIV(utf8_table4)[]; #define _pcre2_callout_end_delims PCRE2_SUFFIX(_pcre2_callout_end_delims_) #define _pcre2_callout_start_delims PCRE2_SUFFIX(_pcre2_callout_start_delims_) #define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_) +#define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_) #define _pcre2_default_match_context PCRE2_SUFFIX(_pcre2_default_match_context_) #define _pcre2_default_tables PCRE2_SUFFIX(_pcre2_default_tables_) +#if PCRE2_CODE_UNIT_WIDTH == 32 +#define _pcre2_dummy_ucd_record PCRE2_SUFFIX(_pcre2_dummy_ucd_record_) +#endif #define _pcre2_hspace_list PCRE2_SUFFIX(_pcre2_hspace_list_) #define _pcre2_vspace_list PCRE2_SUFFIX(_pcre2_vspace_list_) #define _pcre2_ucd_caseless_sets PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_) @@ -1872,13 +1841,17 @@ extern const uint8_t PRIV(OP_lengths)[]; extern const uint32_t PRIV(callout_end_delims)[]; extern const uint32_t PRIV(callout_start_delims)[]; extern const pcre2_compile_context PRIV(default_compile_context); +extern const pcre2_convert_context PRIV(default_convert_context); extern const pcre2_match_context PRIV(default_match_context); extern const uint8_t PRIV(default_tables)[]; extern const uint32_t PRIV(hspace_list)[]; extern const uint32_t PRIV(vspace_list)[]; extern const uint32_t PRIV(ucd_caseless_sets)[]; extern const ucd_record PRIV(ucd_records)[]; -extern const uint8_t PRIV(ucd_stage1)[]; +#if PCRE2_CODE_UNIT_WIDTH == 32 +extern const ucd_record PRIV(dummy_ucd_record)[]; +#endif +extern const uint16_t PRIV(ucd_stage1)[]; extern const uint16_t PRIV(ucd_stage2)[]; extern const uint32_t PRIV(ucp_gbtable)[]; extern const uint32_t PRIV(ucp_gentype)[]; @@ -1912,6 +1885,7 @@ is available. */ #define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_) #define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_) +#define _pcre2_extuni PCRE2_SUFFIX(_pcre2_extuni_) #define _pcre2_find_bracket PCRE2_SUFFIX(_pcre2_find_bracket_) #define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_) #define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_) @@ -1935,6 +1909,8 @@ extern int _pcre2_auto_possessify(PCRE2_UCHAR *, BOOL, const compile_block *); extern int _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *, int *, uint32_t, BOOL, compile_block *); +extern PCRE2_SPTR _pcre2_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR, + BOOL, int *); extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int); extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); @@ -1955,6 +1931,15 @@ extern int _pcre2_valid_utf(PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE *); extern BOOL _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); extern BOOL _pcre2_xclass(uint32_t, PCRE2_SPTR, BOOL); + +/* This function is needed only when memmove() is not available. */ + +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +#define _pcre2_memmove PCRE2_SUFFIX(_pcre2_memmove) +extern void * _pcre2_memmove(void *, const void *, size_t); +#endif + #endif /* PCRE2_CODE_UNIT_WIDTH */ +#endif /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */ /* End of pcre2_internal.h */ diff --git a/ProcessHacker/pcre/pcre2_intmodedep.h b/ProcessHacker/pcre/pcre2_intmodedep.h index 596d62cfdcff..62626d0a8a74 100644 --- a/ProcessHacker/pcre/pcre2_intmodedep.h +++ b/ProcessHacker/pcre/pcre2_intmodedep.h @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -54,6 +54,7 @@ just to undefine them all. */ #undef ACROSSCHAR #undef BACKCHAR #undef BYTES2CU +#undef CHMAX_255 #undef CU2BYTES #undef FORWARDCHAR #undef FORWARDCHARTEST @@ -140,7 +141,7 @@ values of 3 or 4 are also supported. */ #undef LINK_SIZE #define LINK_SIZE 1 #define PUT(a,n,d) \ - (a[n] = (d)) + (a[n] = (PCRE2_UCHAR)(d)) #define GET(a,n) \ (a[n]) #define MAX_PATTERN_SIZE (1 << 16) @@ -200,21 +201,26 @@ arithmetic results in a signed value. Hence the cast. */ #endif /* Other macros that are different for 8-bit mode. The MAX_255 macro checks -whether its argument is less than 256. The maximum length of a MARK name must -fit in one code unit; currently it is set to 255 or 65535. The TABLE_GET macro -is used to access elements of tables containing exactly 256 items. When code -points can be greater than 255, a check is needed before accessing these -tables. */ +whether its argument, which is assumed to be one code unit, is less than 256. +The CHMAX_255 macro does not assume one code unit. The maximum length of a MARK +name must fit in one code unit; currently it is set to 255 or 65535. The +TABLE_GET macro is used to access elements of tables containing exactly 256 +items. When code points can be greater than 255, a check is needed before +accessing these tables. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define MAX_255(c) TRUE #define MAX_MARK ((1u << 8) - 1) #ifdef SUPPORT_UNICODE #define SUPPORT_WIDE_CHARS +#define CHMAX_255(c) ((c) <= 255u) +#else +#define CHMAX_255(c) TRUE #endif /* SUPPORT_UNICODE */ #define TABLE_GET(c, table, default) ((table)[c]) #else /* Code units are 16 or 32 bits */ +#define CHMAX_255(c) ((c) <= 255u) #define MAX_255(c) ((c) <= 255u) #define MAX_MARK ((1u << 16) - 1) #define SUPPORT_WIDE_CHARS @@ -345,7 +351,7 @@ because almost all calls are already within a block of UTF-8 only code. */ /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) \ - while((condition) && ((eptr) & 0xc0u) == 0x80u) action + while((condition) && ((*eptr) & 0xc0u) == 0x80u) action /* Deposit a character into memory, returning the number of code units. */ @@ -451,7 +457,7 @@ code. */ /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) \ - if ((condition) && ((eptr) & 0xfc00u) == 0xdc00u) action + if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action /* Deposit a character into memory, returning the number of code units. */ @@ -566,15 +572,13 @@ typedef struct pcre2_real_compile_context { uint16_t bsr_convention; uint16_t newline_convention; uint32_t parens_nest_limit; + uint32_t extra_options; } pcre2_real_compile_context; /* The real match context structure. */ typedef struct pcre2_real_match_context { pcre2_memctl memctl; -#ifdef HEAP_MATCH_RECURSE - pcre2_memctl stack_memctl; -#endif #ifdef SUPPORT_JIT pcre2_jit_callback jit_callback; void *jit_callback_data; @@ -582,10 +586,19 @@ typedef struct pcre2_real_match_context { int (*callout)(pcre2_callout_block *, void *); void *callout_data; PCRE2_SIZE offset_limit; + uint32_t heap_limit; uint32_t match_limit; - uint32_t recursion_limit; + uint32_t depth_limit; } pcre2_real_match_context; +/* The real convert context structure. */ + +typedef struct pcre2_real_convert_context { + pcre2_memctl memctl; + uint32_t glob_separator; + uint32_t glob_escape; +} pcre2_real_convert_context; + /* The real compiled code structure. The type for the blocksize field is defined specially because it is required in pcre2_serialize_decode() when copying the size from possibly unaligned memory into a variable of the same @@ -610,9 +623,11 @@ typedef struct pcre2_real_code { uint32_t magic_number; /* Paranoid and endianness check */ uint32_t compile_options; /* Options passed to pcre2_compile() */ uint32_t overall_options; /* Options after processing the pattern */ + uint32_t extra_options; /* Taken from compile_context */ uint32_t flags; /* Various state flags */ + uint32_t limit_heap; /* Limit set in the pattern */ uint32_t limit_match; /* Limit set in the pattern */ - uint32_t limit_recursion; /* Limit set in the pattern */ + uint32_t limit_depth; /* Limit set in the pattern */ uint32_t first_codeunit; /* Starting code unit */ uint32_t last_codeunit; /* This codeunit must be seen */ uint16_t bsr_convention; /* What \R matches */ @@ -625,7 +640,13 @@ typedef struct pcre2_real_code { uint16_t name_count; /* Number of name entries in the table */ } pcre2_real_code; -/* The real match data structure. */ +/* The real match data structure. Define ovector as large as it can ever +actually be so that array bound checkers don't grumble. Memory for this +structure is obtained by calling pcre2_match_data_create(), which sets the size +as the offset of ovector plus a pair of elements for each capturable string, so +the size varies from call to call. As the maximum number of capturing +subpatterns is 65535 we must allow for 65536 strings to include the overall +match. (See also the heapframe structure below.) */ typedef struct pcre2_real_match_data { pcre2_memctl memctl; @@ -638,7 +659,7 @@ typedef struct pcre2_real_match_data { uint16_t matchedby; /* Type of match (normal, JIT, DFA) */ uint16_t oveccount; /* Number of pairs */ int rc; /* The return code from the match */ - PCRE2_SIZE ovector[1]; /* The first field */ + PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ } pcre2_real_match_data; @@ -648,18 +669,24 @@ typedef struct pcre2_real_match_data { #ifndef PCRE2_PCRE2TEST -/* Structure for checking for mutual recursion when scanning compiled code. */ +/* Structures for checking for mutual recursion when scanning compiled or +parsed code. */ typedef struct recurse_check { struct recurse_check *prev; PCRE2_SPTR group; } recurse_check; +typedef struct parsed_recurse_check { + struct parsed_recurse_check *prev; + uint32_t *groupptr; +} parsed_recurse_check; + /* Structure for building a cache when filling in recursion offsets. */ typedef struct recurse_cache { PCRE2_SPTR group; - int recno; + int groupnumber; } recurse_cache; /* Structure for maintaining a chain of pointers to the currently incomplete @@ -693,34 +720,37 @@ typedef struct compile_block { PCRE2_SPTR start_code; /* The start of the compiled code */ PCRE2_SPTR start_pattern; /* The start of the pattern */ PCRE2_SPTR end_pattern; /* The end of the pattern */ - PCRE2_SPTR nestptr[2]; /* Pointer(s) saved for string substitution */ PCRE2_UCHAR *name_table; /* The name/number table */ - size_t workspace_size; /* Size of workspace */ + PCRE2_SIZE workspace_size; /* Size of workspace */ + PCRE2_SIZE small_ref_offset[10]; /* Offsets for \1 to \9 */ + PCRE2_SIZE erroroffset; /* Offset of error in pattern */ uint16_t names_found; /* Number of entries so far */ uint16_t name_entry_size; /* Size of each entry */ + uint16_t parens_depth; /* Depth of nested parentheses */ + uint16_t assert_depth; /* Depth of nested assertions */ open_capitem *open_caps; /* Chain of open capture items */ named_group *named_groups; /* Points to vector in pre-compile */ uint32_t named_group_list_size; /* Number of entries in the list */ uint32_t external_options; /* External (initial) options */ uint32_t external_flags; /* External flag bits to be set */ - uint32_t bracount; /* Count of capturing parens as we compile */ - uint32_t final_bracount; /* Saved value after first pass */ + uint32_t bracount; /* Count of capturing parentheses */ + uint32_t lastcapture; /* Last capture encountered */ + uint32_t *parsed_pattern; /* Parsed pattern buffer */ + uint32_t *parsed_pattern_end; /* Parsed pattern should not get here */ uint32_t *groupinfo; /* Group info vector */ uint32_t top_backref; /* Maximum back reference */ uint32_t backref_map; /* Bitmap of low back refs */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ + uint32_t class_range_start; /* Overall class range start */ + uint32_t class_range_end; /* Overall class range end */ PCRE2_UCHAR nl[4]; /* Newline string when fixed length */ int max_lookbehind; /* Maximum lookbehind (characters) */ - int parens_depth; /* Depth of nested parentheses */ - int assert_depth; /* Depth of nested assertions */ int req_varyopt; /* "After variable item" flag for reqbyte */ BOOL had_accept; /* (*ACCEPT) encountered */ BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */ BOOL had_recurse; /* Had a recursion or subroutine call */ - BOOL check_lookbehind; /* Lookbehinds need later checking */ BOOL dupnames; /* Duplicate names exist */ - BOOL iscondassert; /* Next assert is a condition */ } compile_block; /* Structure for keeping the properties of the in-memory stack used @@ -731,27 +761,8 @@ typedef struct pcre2_real_jit_stack { void* stack; } pcre2_real_jit_stack; -/* Structure for keeping a chain of heap blocks used for saving ovectors -during pattern recursion when the ovector is larger than can be saved on -the system stack. */ - -typedef struct ovecsave_frame { - struct ovecsave_frame *next; /* Next frame on free chain */ - PCRE2_SIZE saved_ovec[1]; /* First vector element */ -} ovecsave_frame; - /* Structure for items in a linked list that represents an explicit recursive -call within the pattern; used by pcre_match(). */ - -typedef struct recursion_info { - struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ - unsigned int group_num; /* Number of group that was called */ - PCRE2_SIZE *ovec_save; /* Pointer to saved ovector frame */ - uint32_t saved_capture_last; /* Last capture number */ - PCRE2_SPTR subject_position; /* Position at start of recursion */ -} recursion_info; - -/* A similar structure for pcre_dfa_match(). */ +call within the pattern when running pcre_dfa_match(). */ typedef struct dfa_recursion_info { struct dfa_recursion_info *prevrec; @@ -759,35 +770,90 @@ typedef struct dfa_recursion_info { uint32_t group_num; } dfa_recursion_info; -/* Structure for building a chain of data for holding the values of the subject -pointer at the start of each subpattern, so as to detect when an empty string -has been matched by a subpattern - to break infinite loops; used by -pcre2_match(). */ +/* Structure for "stack" frames that are used for remembering backtracking +positions during matching. As these are used in a vector, with the ovector item +being extended, the size of the structure must be a multiple of PCRE2_SIZE. The +only way to check this at compile time is to force an error by generating an +array with a negative size. By putting this in a typedef (which is never used), +we don't generate any code when all is well. */ + +typedef struct heapframe { + + /* The first set of fields are variables that have to be preserved over calls + to RRMATCH(), but which do not need to be copied to new frames. */ + + PCRE2_SPTR ecode; /* The current position in the pattern */ + PCRE2_SPTR temp_sptr[2]; /* Used for short-term PCRE_SPTR values */ + PCRE2_SIZE length; /* Used for character, string, or code lengths */ + PCRE2_SIZE back_frame; /* Amount to subtract on RRETURN */ + PCRE2_SIZE temp_size; /* Used for short-term PCRE2_SIZE values */ + uint32_t rdepth; /* "Recursion" depth */ + uint32_t group_frame_type; /* Type information for group frames */ + uint32_t temp_32[4]; /* Used for short-term 32-bit or BOOL values */ + uint8_t return_id; /* Where to go on in internal "return" */ + uint8_t op; /* Processing opcode */ + + /* At this point, the structure is 16-bit aligned. On most architectures + the alignment requirement for a pointer will ensure that the eptr field below + is 32-bit or 64-bit aligned. However, on m68k it is fine to have a pointer + that is 16-bit aligned. We must therefore ensure that what comes between here + and eptr is an odd multiple of 16 bits so as to get back into 32-bit + alignment. This happens naturally when PCRE2_UCHAR is 8 bits wide, but needs + fudges in the other cases. In the 32-bit case the padding comes first so that + the occu field itself is 32-bit aligned. Without the padding, this structure + is no longer a multiple of PCRE2_SIZE on m68k, and the check below fails. */ -typedef struct eptrblock { - struct eptrblock *epb_prev; - PCRE2_SPTR epb_saved_eptr; -} eptrblock; +#if PCRE2_CODE_UNIT_WIDTH == 8 + PCRE2_UCHAR occu[6]; /* Used for other case code units */ +#elif PCRE2_CODE_UNIT_WIDTH == 16 + PCRE2_UCHAR occu[2]; /* Used for other case code units */ + uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */ +#else + uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */ + PCRE2_UCHAR occu[1]; /* Used for other case code units */ +#endif + + /* The rest have to be copied from the previous frame whenever a new frame + becomes current. The final field is specified as a large vector so that + runtime array bound checks don't catch references to it. However, for any + specific call to pcre2_match() the memory allocated for each frame structure + allows for exactly the right size ovector for the number of capturing + parentheses. (See also the comment for pcre2_real_match_data above.) */ + + PCRE2_SPTR eptr; /* MUST BE FIRST */ + PCRE2_SPTR start_match; /* Can be adjusted by \K */ + PCRE2_SPTR mark; /* Most recent mark on the success path */ + uint32_t current_recurse; /* Current (deepest) recursion number */ + uint32_t capture_last; /* Most recent capture */ + PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */ + PCRE2_SIZE offset_top; /* Offset after highest capture */ + PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ +} heapframe; + +/* This typedef is a check that the size of the heapframe structure is a +multiple of PCRE2_SIZE. See various comments above. */ + +typedef char check_heapframe_size[ + ((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0)? (+1):(-1)]; /* Structure for passing "static" information around between the functions doing traditional NFA matching (pcre2_match() and friends). */ typedef struct match_block { pcre2_memctl memctl; /* For general use */ -#ifdef HEAP_MATCH_RECURSE - pcre2_memctl stack_memctl; /* For "stack" frames */ -#endif - uint32_t match_call_count; /* As it says */ + PCRE2_SIZE frame_vector_size; /* Size of a backtracking frame */ + heapframe *match_frames; /* Points to vector of frames */ + heapframe *match_frames_top; /* Points after the end of the vector */ + heapframe *stack_frames; /* The original vector on the stack */ + PCRE2_SIZE heap_limit; /* As it says */ uint32_t match_limit; /* As it says */ - uint32_t match_limit_recursion; /* As it says */ + uint32_t match_limit_depth; /* As it says */ + uint32_t match_call_count; /* Number of times a new frame is created */ BOOL hitend; /* Hit the end of the subject at some point */ BOOL hasthen; /* Pattern contains (*THEN) */ const uint8_t *lcc; /* Points to lower casing table */ const uint8_t *fcc; /* Points to case-flipping table */ const uint8_t *ctypes; /* Points to table of type maps */ - PCRE2_SIZE *ovector; /* Pointer to the offset vector */ - PCRE2_SIZE offset_end; /* One past the end */ - PCRE2_SIZE offset_max; /* The maximum usable for return data */ PCRE2_SIZE start_offset; /* The start offset value */ PCRE2_SIZE end_offset_top; /* Highwater mark at end of match */ uint16_t partial; /* PARTIAL options */ @@ -798,30 +864,24 @@ typedef struct match_block { PCRE2_SPTR start_code; /* For use when recursing */ PCRE2_SPTR start_subject; /* Start of the subject string */ PCRE2_SPTR end_subject; /* End of the subject string */ - PCRE2_SPTR start_match_ptr; /* Start of matched string */ PCRE2_SPTR end_match_ptr; /* Subject position at end match */ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */ PCRE2_SPTR last_used_ptr; /* Latest consulted character */ PCRE2_SPTR mark; /* Mark pointer to pass back on success */ PCRE2_SPTR nomatch_mark; /* Mark pointer to pass back on failure */ - PCRE2_SPTR once_target; /* Where to back up to for atomic groups */ + PCRE2_SPTR verb_ecode_ptr; /* For passing back info */ + PCRE2_SPTR verb_skip_ptr; /* For passing back a (*SKIP) name */ + uint32_t verb_current_recurse; /* Current recurse when (*VERB) happens */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ - uint32_t capture_last; /* Most recent capture number + overflow flag */ uint32_t skip_arg_count; /* For counting SKIP_ARGs */ uint32_t ignore_skip_arg; /* For re-run when SKIP arg name not found */ - uint32_t match_function_type; /* Set for certain special calls of match() */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ PCRE2_UCHAR nl[4]; /* Newline string when fixed */ - eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */ - recursion_info *recursive; /* Linked list of recursion data */ - ovecsave_frame *ovecsave_chain; /* Linked list of free ovecsave blocks */ + pcre2_callout_block *cb; /* Points to a callout block */ void *callout_data; /* To pass back to callouts */ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */ -#ifdef HEAP_MATCH_RECURSE - void *match_frames_base; /* For remembering malloc'd frames */ -#endif } match_block; /* A similar structure is used for the same purpose by the DFA matching @@ -836,12 +896,18 @@ typedef struct dfa_match_block { PCRE2_SPTR last_used_ptr; /* Latest consulted character */ const uint8_t *tables; /* Character tables */ PCRE2_SIZE start_offset; /* The start offset value */ + PCRE2_SIZE heap_limit; /* As it says */ + PCRE2_SIZE heap_used; /* As it says */ + uint32_t match_limit; /* As it says */ + uint32_t match_limit_depth; /* As it says */ + uint32_t match_call_count; /* Number of calls of internal function */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ PCRE2_UCHAR nl[4]; /* Newline string when fixed */ uint16_t bsr_convention; /* \R interpretation */ + pcre2_callout_block *cb; /* Points to a callout block */ void *callout_data; /* To pass back to callouts */ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */ dfa_recursion_info *recursive; /* Linked list of recursion data */ diff --git a/ProcessHacker/pcre/pcre2_jit_compile.c b/ProcessHacker/pcre/pcre2_jit_compile.c index 0d3baa714e4a..32e985b79385 100644 --- a/ProcessHacker/pcre/pcre2_jit_compile.c +++ b/ProcessHacker/pcre/pcre2_jit_compile.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -229,7 +228,7 @@ enum control_types { type_then_trap = 1 }; -typedef int (SLJIT_CALL *jit_function)(jit_arguments *args); +typedef int (SLJIT_FUNC *jit_function)(jit_arguments *args); /* The following structure is the key data type for the recursive code generator. It is allocated by compile_matchingpath, and contains @@ -314,16 +313,25 @@ typedef struct ref_iterator_backtrack { typedef struct recurse_entry { struct recurse_entry *next; - /* Contains the function entry. */ - struct sljit_label *entry; - /* Collects the calls until the function is not created. */ - jump_list *calls; + /* Contains the function entry label. */ + struct sljit_label *entry_label; + /* Contains the function entry label. */ + struct sljit_label *backtrack_label; + /* Collects the entry calls until the function is not created. */ + jump_list *entry_calls; + /* Collects the backtrack calls until the function is not created. */ + jump_list *backtrack_calls; /* Points to the starting opcode. */ sljit_sw start; } recurse_entry; typedef struct recurse_backtrack { backtrack_common common; + /* Return to the matching path. */ + struct sljit_label *matchingpath; + /* Recursive pattern. */ + recurse_entry *entry; + /* Pattern is inlined. */ BOOL inlined_pattern; } recurse_backtrack; @@ -342,11 +350,26 @@ typedef struct then_trap_backtrack { int framesize; } then_trap_backtrack; -#define MAX_RANGE_SIZE 4 +#define MAX_N_CHARS 12 +#define MAX_DIFF_CHARS 5 + +typedef struct fast_forward_char_data { + /* Number of characters in the chars array, 255 for any character. */ + sljit_u8 count; + /* Number of last UTF-8 characters in the chars array. */ + sljit_u8 last_count; + /* Available characters in the current position. */ + PCRE2_UCHAR chars[MAX_DIFF_CHARS]; +} fast_forward_char_data; + +#define MAX_CLASS_RANGE_SIZE 4 +#define MAX_CLASS_CHARS_SIZE 3 typedef struct compiler_common { /* The sljit ceneric compiler. */ struct sljit_compiler *compiler; + /* Compiled regular expression. */ + pcre2_real_code *re; /* First byte code. */ PCRE2_SPTR start; /* Maps private data offset to each opcode. */ @@ -403,10 +426,10 @@ typedef struct compiler_common { BOOL has_then; /* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */ BOOL has_skip_in_assert_back; - /* Currently in recurse or negative assert. */ - BOOL local_exit; - /* Currently in a positive assert. */ - BOOL positive_assert; + /* Quit is redirected by recurse, negative assertion, or positive assertion in conditional block. */ + BOOL local_quit_available; + /* Currently in a positive assertion. */ + BOOL in_positive_assertion; /* Newline control. */ int nltype; sljit_u32 nlmax; @@ -427,7 +450,7 @@ typedef struct compiler_common { /* Labels and jump lists. */ struct sljit_label *partialmatchlabel; struct sljit_label *quit_label; - struct sljit_label *forced_quit_label; + struct sljit_label *abort_label; struct sljit_label *accept_label; struct sljit_label *ff_newline_shortcut; stub_list *stubs; @@ -436,8 +459,9 @@ typedef struct compiler_common { recurse_entry *currententry; jump_list *partialmatch; jump_list *quit; - jump_list *positive_assert_quit; - jump_list *forced_quit; + jump_list *positive_assertion_quit; + jump_list *abort; + jump_list *failed_match; jump_list *accept; jump_list *calllimit; jump_list *stackalloc; @@ -501,14 +525,29 @@ typedef struct compare_context { #undef CMP /* Used for accessing the elements of the stack. */ -#define STACK(i) ((-(i) - 1) * (int)sizeof(sljit_sw)) +#define STACK(i) ((i) * (int)sizeof(sljit_sw)) + +#ifdef SLJIT_PREF_SHIFT_REG +#if SLJIT_PREF_SHIFT_REG == SLJIT_R2 +/* Nothing. */ +#elif SLJIT_PREF_SHIFT_REG == SLJIT_R3 +#define SHIFT_REG_IS_R3 +#else +#error "Unsupported shift register" +#endif +#endif #define TMP1 SLJIT_R0 +#ifdef SHIFT_REG_IS_R3 +#define TMP2 SLJIT_R3 +#define TMP3 SLJIT_R2 +#else #define TMP2 SLJIT_R2 #define TMP3 SLJIT_R3 -#define STR_PTR SLJIT_S0 -#define STR_END SLJIT_S1 -#define STACK_TOP SLJIT_R1 +#endif +#define STR_PTR SLJIT_R1 +#define STR_END SLJIT_S0 +#define STACK_TOP SLJIT_S1 #define STACK_LIMIT SLJIT_S2 #define COUNT_MATCH SLJIT_S3 #define ARGUMENTS SLJIT_S4 @@ -534,16 +573,13 @@ the start pointers when the end of the capturing group has not yet reached. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define MOV_UCHAR SLJIT_MOV_U8 -#define MOVU_UCHAR SLJIT_MOVU_U8 #define IN_UCHARS(x) (x) #elif PCRE2_CODE_UNIT_WIDTH == 16 #define MOV_UCHAR SLJIT_MOV_U16 -#define MOVU_UCHAR SLJIT_MOVU_U16 #define UCHAR_SHIFT (1) #define IN_UCHARS(x) ((x) * 2) #elif PCRE2_CODE_UNIT_WIDTH == 32 #define MOV_UCHAR SLJIT_MOV_U32 -#define MOVU_UCHAR SLJIT_MOVU_U32 #define UCHAR_SHIFT (2) #define IN_UCHARS(x) ((x) * 4) #else @@ -571,13 +607,17 @@ the start pointers when the end of the capturing group has not yet reached. */ sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)) #define CMPTO(type, src1, src1w, src2, src2w, label) \ sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label)) -#define OP_FLAGS(op, dst, dstw, src, srcw, type) \ - sljit_emit_op_flags(compiler, (op), (dst), (dstw), (src), (srcw), (type)) +#define OP_FLAGS(op, dst, dstw, type) \ + sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type)) +#define CMOV(type, dst_reg, src, srcw) \ + sljit_emit_cmov(compiler, (type), (dst_reg), (src), (srcw)) #define GET_LOCAL_BASE(dst, dstw, offset) \ sljit_get_local_base(compiler, (dst), (dstw), (offset)) #define READ_CHAR_MAX 0x7fffffff +#define INVALID_UTF_CHAR 888 + static PCRE2_SPTR bracketend(PCRE2_SPTR cc) { SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND)); @@ -607,8 +647,8 @@ return count; set_private_data_ptrs get_framesize init_frame - get_private_data_copy_length - copy_private_data + get_recurse_data_length + copy_recurse_data compile_matchingpath compile_backtrackingpath */ @@ -676,7 +716,6 @@ switch(*cc) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_BRAPOS: case OP_CBRA: @@ -800,6 +839,7 @@ switch(*cc) #endif case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: @@ -807,7 +847,7 @@ switch(*cc) default: /* All opcodes are supported now! */ - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } } @@ -900,6 +940,7 @@ while (cc < ccend) common->control_head_ptr = 1; /* Fall through. */ + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_MARK: if (common->mark_ptr == 0) @@ -1305,7 +1346,7 @@ while (cc < ccend) if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE) break; - if (repeat_check && (*cc == OP_ONCE || *cc == OP_ONCE_NC || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND)) + if (repeat_check && (*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND)) { if (detect_repeat(common, cc)) { @@ -1334,7 +1375,6 @@ while (cc < ccend) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: @@ -1515,6 +1555,7 @@ while (cc < ccend) break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); @@ -1655,11 +1696,11 @@ if (length > 0) return stack_restore ? no_frame : no_stack; } -static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop, BOOL recursive) +static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop) { DEFINE_COMPILER; -BOOL setsom_found = recursive; -BOOL setmark_found = recursive; +BOOL setsom_found = FALSE; +BOOL setmark_found = FALSE; /* The last capture is a local variable even for recursions. */ BOOL capture_last_found = FALSE; int offset; @@ -1672,7 +1713,7 @@ stackpos = STACK(stackpos); if (ccend == NULL) { ccend = bracketend(cc) - (1 + LINK_SIZE); - if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)) + if (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS) cc = next_opcode(common, cc); } @@ -1686,15 +1727,16 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setsom_found = TRUE; } cc += 1; break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); @@ -1702,9 +1744,9 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setmark_found = TRUE; } cc += 1 + 2 + cc[1]; @@ -1715,27 +1757,27 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setsom_found = TRUE; } if (common->mark_ptr != 0 && !setmark_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setmark_found = TRUE; } if (common->capture_last_ptr != 0 && !capture_last_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); capture_last_found = TRUE; } cc += 1 + LINK_SIZE; @@ -1749,20 +1791,20 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); capture_last_found = TRUE; } offset = (GET2(cc, 1 + LINK_SIZE)) << 1; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset)); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); cc += 1 + LINK_SIZE + IMM2_SIZE; break; @@ -1777,21 +1819,127 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0); SLJIT_ASSERT(stackpos == STACK(stacktop)); } -static SLJIT_INLINE int get_private_data_copy_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL needs_control_head) +#define RECURSE_TMP_REG_COUNT 3 + +typedef struct delayed_mem_copy_status { + struct sljit_compiler *compiler; + int store_bases[RECURSE_TMP_REG_COUNT]; + int store_offsets[RECURSE_TMP_REG_COUNT]; + int tmp_regs[RECURSE_TMP_REG_COUNT]; + int saved_tmp_regs[RECURSE_TMP_REG_COUNT]; + int next_tmp_reg; +} delayed_mem_copy_status; + +static void delayed_mem_copy_init(delayed_mem_copy_status *status, compiler_common *common) +{ +int i; + +for (i = 0; i < RECURSE_TMP_REG_COUNT; i++) + { + SLJIT_ASSERT(status->tmp_regs[i] >= 0); + SLJIT_ASSERT(sljit_get_register_index(status->saved_tmp_regs[i]) < 0 || status->tmp_regs[i] == status->saved_tmp_regs[i]); + + status->store_bases[i] = -1; + } +status->next_tmp_reg = 0; +status->compiler = common->compiler; +} + +static void delayed_mem_copy_move(delayed_mem_copy_status *status, int load_base, sljit_sw load_offset, + int store_base, sljit_sw store_offset) +{ +struct sljit_compiler *compiler = status->compiler; +int next_tmp_reg = status->next_tmp_reg; +int tmp_reg = status->tmp_regs[next_tmp_reg]; + +SLJIT_ASSERT(load_base > 0 && store_base > 0); + +if (status->store_bases[next_tmp_reg] == -1) + { + /* Preserve virtual registers. */ + if (sljit_get_register_index(status->saved_tmp_regs[next_tmp_reg]) < 0) + OP1(SLJIT_MOV, status->saved_tmp_regs[next_tmp_reg], 0, tmp_reg, 0); + } +else + OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0); + +OP1(SLJIT_MOV, tmp_reg, 0, SLJIT_MEM1(load_base), load_offset); +status->store_bases[next_tmp_reg] = store_base; +status->store_offsets[next_tmp_reg] = store_offset; + +status->next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT; +} + +static void delayed_mem_copy_finish(delayed_mem_copy_status *status) { -int private_data_length = needs_control_head ? 3 : 2; +struct sljit_compiler *compiler = status->compiler; +int next_tmp_reg = status->next_tmp_reg; +int tmp_reg, saved_tmp_reg, i; + +for (i = 0; i < RECURSE_TMP_REG_COUNT; i++) + { + if (status->store_bases[next_tmp_reg] != -1) + { + tmp_reg = status->tmp_regs[next_tmp_reg]; + saved_tmp_reg = status->saved_tmp_regs[next_tmp_reg]; + + OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0); + + /* Restore virtual registers. */ + if (sljit_get_register_index(saved_tmp_reg) < 0) + OP1(SLJIT_MOV, tmp_reg, 0, saved_tmp_reg, 0); + } + + next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT; + } +} + +#undef RECURSE_TMP_REG_COUNT + +static int get_recurse_data_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, + BOOL *needs_control_head, BOOL *has_quit, BOOL *has_accept) +{ +int length = 1; int size; PCRE2_SPTR alternative; +BOOL quit_found = FALSE; +BOOL accept_found = FALSE; +BOOL setsom_found = FALSE; +BOOL setmark_found = FALSE; +BOOL capture_last_found = FALSE; +BOOL control_head_found = FALSE; + +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +SLJIT_ASSERT(common->control_head_ptr != 0); +control_head_found = TRUE; +#endif + /* Calculate the sum of the private machine words. */ while (cc < ccend) { size = 0; switch(*cc) { + case OP_SET_SOM: + SLJIT_ASSERT(common->has_set_som); + setsom_found = TRUE; + cc += 1; + break; + + case OP_RECURSE: + if (common->has_set_som) + setsom_found = TRUE; + if (common->mark_ptr != 0) + setmark_found = TRUE; + if (common->capture_last_ptr != 0) + capture_last_found = TRUE; + cc += 1 + LINK_SIZE; + break; + case OP_KET: if (PRIVATE_DATA(cc) != 0) { - private_data_length++; + length++; SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); cc += PRIVATE_DATA(cc + 1); } @@ -1803,26 +1951,30 @@ while (cc < ccend) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: case OP_SCOND: - private_data_length++; + length++; SLJIT_ASSERT(PRIVATE_DATA(cc) != 0); cc += 1 + LINK_SIZE; break; case OP_CBRA: case OP_SCBRA: + length += 2; + if (common->capture_last_ptr != 0) + capture_last_found = TRUE; if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) - private_data_length++; + length++; cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: - private_data_length += 2; + length += 2 + 2; + if (common->capture_last_ptr != 0) + capture_last_found = TRUE; cc += 1 + LINK_SIZE + IMM2_SIZE; break; @@ -1830,13 +1982,13 @@ while (cc < ccend) /* Might be a hidden SCOND. */ alternative = cc + GET(cc, 1); if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) - private_data_length++; + length++; cc += 1 + LINK_SIZE; break; CASE_ITERATOR_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - private_data_length++; + if (PRIVATE_DATA(cc) != 0) + length++; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); @@ -1844,8 +1996,8 @@ while (cc < ccend) break; CASE_ITERATOR_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); @@ -1853,8 +2005,8 @@ while (cc < ccend) break; CASE_ITERATOR_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 2 + IMM2_SIZE; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); @@ -1862,20 +2014,20 @@ while (cc < ccend) break; CASE_ITERATOR_TYPE_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - private_data_length++; + if (PRIVATE_DATA(cc) != 0) + length++; cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 1 + IMM2_SIZE; break; @@ -1887,11 +2039,52 @@ while (cc < ccend) #else size = 1 + 32 / (int)sizeof(PCRE2_UCHAR); #endif - if (PRIVATE_DATA(cc)) - private_data_length += get_class_iterator_size(cc + size); + if (PRIVATE_DATA(cc) != 0) + length += get_class_iterator_size(cc + size); cc += size; break; + case OP_MARK: + case OP_COMMIT_ARG: + case OP_PRUNE_ARG: + case OP_THEN_ARG: + SLJIT_ASSERT(common->mark_ptr != 0); + if (!setmark_found) + setmark_found = TRUE; + if (common->control_head_ptr != 0) + control_head_found = TRUE; + if (*cc != OP_MARK) + quit_found = TRUE; + + cc += 1 + 2 + cc[1]; + break; + + case OP_PRUNE: + case OP_SKIP: + case OP_COMMIT: + quit_found = TRUE; + cc++; + break; + + case OP_SKIP_ARG: + quit_found = TRUE; + cc += 1 + 2 + cc[1]; + break; + + case OP_THEN: + SLJIT_ASSERT(common->control_head_ptr != 0); + quit_found = TRUE; + if (!control_head_found) + control_head_found = TRUE; + cc++; + break; + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + accept_found = TRUE; + cc++; + break; + default: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); @@ -1899,329 +2092,447 @@ while (cc < ccend) } } SLJIT_ASSERT(cc == ccend); -return private_data_length; + +if (control_head_found) + length++; +if (capture_last_found) + length++; +if (quit_found) + { + if (setsom_found) + length++; + if (setmark_found) + length++; + } + +*needs_control_head = control_head_found; +*has_quit = quit_found; +*has_accept = accept_found; +return length; } -static void copy_private_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, - BOOL save, int stackptr, int stacktop, BOOL needs_control_head) +enum copy_recurse_data_types { + recurse_copy_from_global, + recurse_copy_private_to_global, + recurse_copy_shared_to_global, + recurse_copy_kept_shared_to_global, + recurse_swap_global +}; + +static void copy_recurse_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, + int type, int stackptr, int stacktop, BOOL has_quit) { -DEFINE_COMPILER; -int srcw[2]; -int count, size; -BOOL tmp1next = TRUE; -BOOL tmp1empty = TRUE; -BOOL tmp2empty = TRUE; +delayed_mem_copy_status status; PCRE2_SPTR alternative; -enum { - start, - loop, - end -} status; +sljit_sw private_srcw[2]; +sljit_sw shared_srcw[3]; +sljit_sw kept_shared_srcw[2]; +int private_count, shared_count, kept_shared_count; +int from_sp, base_reg, offset, i; +BOOL setsom_found = FALSE; +BOOL setmark_found = FALSE; +BOOL capture_last_found = FALSE; +BOOL control_head_found = FALSE; -status = save ? start : loop; -stackptr = STACK(stackptr - 2); -stacktop = STACK(stacktop - 1); +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +SLJIT_ASSERT(common->control_head_ptr != 0); +control_head_found = TRUE; +#endif -if (!save) +switch (type) { - stackptr += (needs_control_head ? 2 : 1) * sizeof(sljit_sw); - if (stackptr < stacktop) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - tmp1empty = FALSE; - } - if (stackptr < stacktop) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - tmp2empty = FALSE; - } - /* The tmp1next must be TRUE in either way. */ - } + case recurse_copy_from_global: + from_sp = TRUE; + base_reg = STACK_TOP; + break; -do - { - count = 0; - switch(status) - { - case start: - SLJIT_ASSERT(save && common->recursive_head_ptr != 0); - count = 1; - srcw[0] = common->recursive_head_ptr; - if (needs_control_head) - { - SLJIT_ASSERT(common->control_head_ptr != 0); - count = 2; - srcw[1] = common->control_head_ptr; - } - status = loop; - break; + case recurse_copy_private_to_global: + case recurse_copy_shared_to_global: + case recurse_copy_kept_shared_to_global: + from_sp = FALSE; + base_reg = STACK_TOP; + break; - case loop: - if (cc >= ccend) - { - status = end; - break; - } + default: + SLJIT_ASSERT(type == recurse_swap_global); + from_sp = FALSE; + base_reg = TMP2; + break; + } - switch(*cc) - { - case OP_KET: - if (PRIVATE_DATA(cc) != 0) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); - cc += PRIVATE_DATA(cc + 1); - } - cc += 1 + LINK_SIZE; - break; +stackptr = STACK(stackptr); +stacktop = STACK(stacktop); - case OP_ASSERT: - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - case OP_ONCE: - case OP_ONCE_NC: - case OP_BRAPOS: - case OP_SBRA: - case OP_SBRAPOS: - case OP_SCOND: - count = 1; - srcw[0] = PRIVATE_DATA(cc); - SLJIT_ASSERT(srcw[0] != 0); - cc += 1 + LINK_SIZE; - break; +status.tmp_regs[0] = TMP1; +status.saved_tmp_regs[0] = TMP1; - case OP_CBRA: - case OP_SCBRA: - if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) - { - count = 1; - srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); - } - cc += 1 + LINK_SIZE + IMM2_SIZE; - break; +if (base_reg != TMP2) + { + status.tmp_regs[1] = TMP2; + status.saved_tmp_regs[1] = TMP2; + } +else + { + status.saved_tmp_regs[1] = RETURN_ADDR; + if (sljit_get_register_index (RETURN_ADDR) == -1) + status.tmp_regs[1] = STR_PTR; + else + status.tmp_regs[1] = RETURN_ADDR; + } - case OP_CBRAPOS: - case OP_SCBRAPOS: - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); - SLJIT_ASSERT(srcw[0] != 0 && srcw[1] != 0); - cc += 1 + LINK_SIZE + IMM2_SIZE; - break; +status.saved_tmp_regs[2] = TMP3; +if (sljit_get_register_index (TMP3) == -1) + status.tmp_regs[2] = STR_END; +else + status.tmp_regs[2] = TMP3; - case OP_COND: - /* Might be a hidden SCOND. */ - alternative = cc + GET(cc, 1); - if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - SLJIT_ASSERT(srcw[0] != 0); - } - cc += 1 + LINK_SIZE; - break; +delayed_mem_copy_init(&status, common); - CASE_ITERATOR_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - } - cc += 2; -#ifdef SUPPORT_UNICODE - if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; +if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global) + { + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global); - CASE_ITERATOR_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); - } - cc += 2; -#ifdef SUPPORT_UNICODE - if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->recursive_head_ptr); - CASE_ITERATOR_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); - } - cc += 2 + IMM2_SIZE; -#ifdef SUPPORT_UNICODE - if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, common->recursive_head_ptr, base_reg, stackptr); + } - CASE_ITERATOR_TYPE_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - } - cc += 1; - break; +stackptr += sizeof(sljit_sw); - CASE_ITERATOR_TYPE_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = srcw[0] + sizeof(sljit_sw); - } - cc += 1; - break; +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +if (type != recurse_copy_shared_to_global) + { + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->control_head_ptr); - CASE_ITERATOR_TYPE_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = srcw[0] + sizeof(sljit_sw); - } - cc += 1 + IMM2_SIZE; - break; + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, common->control_head_ptr, base_reg, stackptr); + } - case OP_CLASS: - case OP_NCLASS: -#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 - case OP_XCLASS: - size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR); -#else - size = 1 + 32 / (int)sizeof(PCRE2_UCHAR); +stackptr += sizeof(sljit_sw); #endif - if (PRIVATE_DATA(cc)) - switch(get_class_iterator_size(cc + size)) - { - case 1: - count = 1; - srcw[0] = PRIVATE_DATA(cc); - break; - - case 2: - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = srcw[0] + sizeof(sljit_sw); - break; - default: - SLJIT_ASSERT_STOP(); - break; - } - cc += size; - break; +while (cc < ccend) + { + private_count = 0; + shared_count = 0; + kept_shared_count = 0; - default: - cc = next_opcode(common, cc); - SLJIT_ASSERT(cc != NULL); - break; + switch(*cc) + { + case OP_SET_SOM: + SLJIT_ASSERT(common->has_set_som); + if (has_quit && !setsom_found) + { + kept_shared_srcw[0] = OVECTOR(0); + kept_shared_count = 1; + setsom_found = TRUE; } + cc += 1; break; - case end: - SLJIT_ASSERT_STOP(); - break; - } - - while (count > 0) - { - count--; - if (save) + case OP_RECURSE: + if (has_quit) { - if (tmp1next) + if (common->has_set_som && !setsom_found) { - if (!tmp1empty) - { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); - stackptr += sizeof(sljit_sw); - } - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), srcw[count]); - tmp1empty = FALSE; - tmp1next = FALSE; + kept_shared_srcw[0] = OVECTOR(0); + kept_shared_count = 1; + setsom_found = TRUE; } - else + if (common->mark_ptr != 0 && !setmark_found) { - if (!tmp2empty) - { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); - stackptr += sizeof(sljit_sw); - } - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), srcw[count]); - tmp2empty = FALSE; - tmp1next = TRUE; + kept_shared_srcw[kept_shared_count] = common->mark_ptr; + kept_shared_count++; + setmark_found = TRUE; } } - else + if (common->capture_last_ptr != 0 && !capture_last_found) { - if (tmp1next) - { - SLJIT_ASSERT(!tmp1empty); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), srcw[count], TMP1, 0); - tmp1empty = stackptr >= stacktop; - if (!tmp1empty) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - } - tmp1next = FALSE; - } - else - { - SLJIT_ASSERT(!tmp2empty); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), srcw[count], TMP2, 0); - tmp2empty = stackptr >= stacktop; - if (!tmp2empty) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - } - tmp1next = TRUE; - } + shared_srcw[0] = common->capture_last_ptr; + shared_count = 1; + capture_last_found = TRUE; } - } - } -while (status != end); + cc += 1 + LINK_SIZE; + break; -if (save) - { - if (tmp1next) - { - if (!tmp1empty) + case OP_KET: + if (PRIVATE_DATA(cc) != 0) { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); - stackptr += sizeof(sljit_sw); + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); + cc += PRIVATE_DATA(cc + 1); + } + cc += 1 + LINK_SIZE; + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + offset = (GET2(cc, 1 + LINK_SIZE)) << 1; + shared_srcw[0] = OVECTOR(offset); + shared_srcw[1] = OVECTOR(offset + 1); + shared_count = 2; + + if (common->capture_last_ptr != 0 && !capture_last_found) + { + shared_srcw[2] = common->capture_last_ptr; + shared_count = 3; + capture_last_found = TRUE; + } + + if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) + { + private_count = 1; + private_srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + } + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + offset = (GET2(cc, 1 + LINK_SIZE)) << 1; + shared_srcw[0] = OVECTOR(offset); + shared_srcw[1] = OVECTOR(offset + 1); + shared_count = 2; + + if (common->capture_last_ptr != 0 && !capture_last_found) + { + shared_srcw[2] = common->capture_last_ptr; + shared_count = 3; + capture_last_found = TRUE; + } + + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + } + cc += 1 + LINK_SIZE; + break; + + CASE_ITERATOR_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + } + cc += 2; +#ifdef SUPPORT_UNICODE + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); + } + cc += 2; +#ifdef SUPPORT_UNICODE + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); + } + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UNICODE + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + } + cc += 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); + } + cc += 1; + break; + + CASE_ITERATOR_TYPE_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); + } + cc += 1 + IMM2_SIZE; + break; + + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 + case OP_XCLASS: + i = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR); +#else + i = 1 + 32 / (int)sizeof(PCRE2_UCHAR); +#endif + if (PRIVATE_DATA(cc) != 0) + switch(get_class_iterator_size(cc + i)) + { + case 1: + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + break; + + case 2: + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); + break; + + default: + SLJIT_UNREACHABLE(); + break; + } + cc += i; + break; + + case OP_MARK: + case OP_COMMIT_ARG: + case OP_PRUNE_ARG: + case OP_THEN_ARG: + SLJIT_ASSERT(common->mark_ptr != 0); + if (has_quit && !setmark_found) + { + kept_shared_srcw[0] = common->mark_ptr; + kept_shared_count = 1; + setmark_found = TRUE; + } + if (common->control_head_ptr != 0 && !control_head_found) + { + shared_srcw[0] = common->control_head_ptr; + shared_count = 1; + control_head_found = TRUE; + } + cc += 1 + 2 + cc[1]; + break; + + case OP_THEN: + SLJIT_ASSERT(common->control_head_ptr != 0); + if (!control_head_found) + { + shared_srcw[0] = common->control_head_ptr; + shared_count = 1; + control_head_found = TRUE; } - if (!tmp2empty) + cc++; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + + if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global) + { + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global); + + for (i = 0; i < private_count; i++) { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + SLJIT_ASSERT(private_srcw[i] != 0); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, private_srcw[i]); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, private_srcw[i], base_reg, stackptr); + stackptr += sizeof(sljit_sw); } } else + stackptr += sizeof(sljit_sw) * private_count; + + if (type != recurse_copy_private_to_global && type != recurse_copy_kept_shared_to_global) { - if (!tmp2empty) + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_swap_global); + + for (i = 0; i < shared_count; i++) { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + SLJIT_ASSERT(shared_srcw[i] != 0); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, shared_srcw[i]); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, shared_srcw[i], base_reg, stackptr); + stackptr += sizeof(sljit_sw); } - if (!tmp1empty) + } + else + stackptr += sizeof(sljit_sw) * shared_count; + + if (type != recurse_copy_private_to_global && type != recurse_swap_global) + { + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_copy_kept_shared_to_global); + + for (i = 0; i < kept_shared_count; i++) { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + SLJIT_ASSERT(kept_shared_srcw[i] != 0); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, kept_shared_srcw[i]); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, kept_shared_srcw[i], base_reg, stackptr); + stackptr += sizeof(sljit_sw); } } + else + stackptr += sizeof(sljit_sw) * kept_shared_count; } -SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty))); + +SLJIT_ASSERT(cc == ccend && stackptr == stacktop); + +delayed_mem_copy_finish(&status); } static SLJIT_INLINE PCRE2_SPTR set_then_offsets(compiler_common *common, PCRE2_SPTR cc, sljit_u8 *current_offset) @@ -2338,7 +2649,7 @@ static SLJIT_INLINE void count_match(compiler_common *common) { DEFINE_COMPILER; -OP2(SLJIT_SUB | SLJIT_SET_E, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1); +OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1); add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO)); } @@ -2348,7 +2659,7 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size) DEFINE_COMPILER; SLJIT_ASSERT(size > 0); -OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); #ifdef DESTROY_REGISTERS OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345); OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); @@ -2356,7 +2667,7 @@ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0); #endif -add_stub(common, CMP(SLJIT_GREATER, STACK_TOP, 0, STACK_LIMIT, 0)); +add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0)); } static SLJIT_INLINE void free_stack(compiler_common *common, int size) @@ -2364,7 +2675,7 @@ static SLJIT_INLINE void free_stack(compiler_common *common, int size) DEFINE_COMPILER; SLJIT_ASSERT(size > 0); -OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); } static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size) @@ -2404,12 +2715,25 @@ if (length < 8) } else { - GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); - OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); - loop = LABEL(); - OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw), SLJIT_R0, 0); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); - JUMPTO(SLJIT_NOT_ZERO, loop); + if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); + loop = LABEL(); + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } + else + { + GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START + sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); + loop = LABEL(); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R0, 0); + OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } } } @@ -2442,12 +2766,25 @@ if (length < 8) } else { - GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); - loop = LABEL(); - OP1(SLJIT_MOVU, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); - OP2(SLJIT_SUB | SLJIT_SET_E, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); - JUMPTO(SLJIT_NOT_ZERO, loop); + if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); + loop = LABEL(); + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } + else + { + GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); + loop = LABEL(); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); + OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } } OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0); @@ -2457,37 +2794,38 @@ if (common->control_head_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, base)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, end)); } -static sljit_sw SLJIT_CALL do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg) +static sljit_sw SLJIT_FUNC do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg) { while (current != NULL) { - switch (current[-2]) + switch (current[1]) { case type_then_trap: break; case type_mark: - if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[-3]) == 0) - return current[-4]; + if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[2]) == 0) + return current[3]; break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } - SLJIT_ASSERT(current > (sljit_sw*)current[-1]); - current = (sljit_sw*)current[-1]; + SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]); + current = (sljit_sw*)current[0]; } -return -1; +return 0; } static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket) { DEFINE_COMPILER; struct sljit_label *loop; +BOOL has_pre; /* At this point we can freely use all registers. */ OP1(SLJIT_MOV, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); @@ -2504,36 +2842,62 @@ if (common->mark_ptr != 0) OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, match_data), SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE)); -GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START); +has_pre = sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; + +GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0)); OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, begin)); loop = LABEL(); -OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0, SLJIT_R0, 0); -OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw)); + +if (has_pre) + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); +else + { + OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0); + OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw)); + } + +OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(PCRE2_SIZE)); +OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0); /* Copy the integer value to the output buffer */ #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif + SLJIT_ASSERT(sizeof(PCRE2_SIZE) == 4 || sizeof(PCRE2_SIZE) == 8); -if (sizeof(PCRE2_SIZE) == 4) - OP1(SLJIT_MOVU_U32, SLJIT_MEM1(SLJIT_R2), sizeof(PCRE2_SIZE), SLJIT_S1, 0); -else - OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R2), sizeof(PCRE2_SIZE), SLJIT_S1, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); +OP1(((sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV), SLJIT_MEM1(SLJIT_R2), 0, SLJIT_S1, 0); + +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); /* Calculate the return value, which is the maximum ovector value. */ if (topbracket > 1) { - GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); - OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); + if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); - /* OVECTOR(0) is never equal to SLJIT_S2. */ - loop = LABEL(); - OP1(SLJIT_MOVU, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); - OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); - CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); - OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); + /* OVECTOR(0) is never equal to SLJIT_S2. */ + loop = LABEL(); + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); + OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); + } + else + { + GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + (topbracket - 1) * 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); + + /* OVECTOR(0) is never equal to SLJIT_S2. */ + loop = LABEL(); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0); + OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * (sljit_sw)sizeof(sljit_sw)); + OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); + } } else OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); @@ -2544,7 +2908,7 @@ static SLJIT_INLINE void return_with_partial_match(compiler_common *common, stru DEFINE_COMPILER; sljit_s32 mov_opcode; -SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S1, str_end_must_be_saved_reg2); +SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S0, str_end_must_be_saved_reg0); SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0 && (common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start != 0 : common->hit_start == 0)); @@ -2554,19 +2918,19 @@ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_PARTIAL); /* Store match begin and end. */ -OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, begin)); +OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, begin)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_R2, 0); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, match_data)); mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV; -OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S0, 0); +OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S1, 0); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector), SLJIT_R2, 0); -OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S0, 0); +OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S1, 0); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, STR_END, 0, STR_END, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -3105,8 +3469,8 @@ if (common->utf) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); /* Skip low surrogate if necessary. */ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); return; @@ -3125,6 +3489,7 @@ struct sljit_jump *jump; if (nltype == NLTYPE_ANY) { add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO)); } else if (nltype == NLTYPE_ANYCRLF) @@ -3166,7 +3531,7 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Searching for the first zero. */ -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); @@ -3180,7 +3545,7 @@ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000); jump = JUMP(SLJIT_NOT_ZERO); /* Three byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); @@ -3214,15 +3579,15 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Searching for the first zero. */ -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); JUMPHERE(jump); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_NOT_ZERO); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_NOT_ZERO); /* This code runs only in 8 bit mode. No need to shift the value. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); @@ -3245,7 +3610,7 @@ struct sljit_jump *compare; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); @@ -3282,12 +3647,33 @@ static void do_getucd(compiler_common *common) /* Search the UCD record for the character comes in TMP1. Returns chartype in TMP1 and UCD offset in TMP2. */ DEFINE_COMPILER; +#if PCRE2_CODE_UNIT_WIDTH == 32 +struct sljit_jump *jump; +#endif + +#if defined SLJIT_DEBUG && SLJIT_DEBUG +/* dummy_ucd_record */ +const ucd_record *record = GET_UCD(INVALID_UTF_CHAR); +SLJIT_ASSERT(record->script == ucp_Common && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther); +SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0); +#endif SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8); sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); + +#if PCRE2_CODE_UNIT_WIDTH == 32 +if (!common->utf) + { + jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); + JUMPHERE(jump); + } +#endif + OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); -OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); +OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); @@ -3300,7 +3686,7 @@ sljit_emit_fast_return(compiler, RETURN_ADDR, 0); #endif /* SUPPORT_UNICODE */ -static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, sljit_u32 overall_options) +static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *mainloop; @@ -3312,6 +3698,8 @@ struct sljit_jump *end2 = NULL; struct sljit_jump *singlechar; #endif jump_list *newline = NULL; +sljit_u32 overall_options = common->re->overall_options; +BOOL hascrorlf = (common->re->flags & PCRE2_HASCRORLF) != 0; BOOL newlinecheck = FALSE; BOOL readuchar = FALSE; @@ -3319,7 +3707,7 @@ if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0) && (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) newlinecheck = TRUE; -SLJIT_ASSERT(common->forced_quit_label == NULL); +SLJIT_ASSERT(common->abort_label == NULL); if ((overall_options & PCRE2_FIRSTLINE) != 0) { @@ -3376,7 +3764,7 @@ else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 0) OP1(SLJIT_MOV, TMP2, 0, STR_END, 0); JUMPHERE(end2); OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); - add_jump(compiler, &common->forced_quit, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0)); + add_jump(compiler, &common->abort, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0)); JUMPHERE(end); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0); } @@ -3389,8 +3777,8 @@ if (newlinecheck) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -3427,8 +3815,8 @@ if (common->utf) { singlechar = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); JUMPHERE(singlechar); @@ -3446,40 +3834,42 @@ if (newlinecheck) return mainloop; } -#define MAX_N_CHARS 16 -#define MAX_DIFF_CHARS 6 -static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, PCRE2_UCHAR *chars) +static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, fast_forward_char_data *chars, BOOL last) { -PCRE2_UCHAR i, len; +sljit_u32 i, count = chars->count; -len = chars[0]; -if (len == 255) +if (count == 255) return; -if (len == 0) +if (count == 0) { - chars[0] = 1; - chars[1] = chr; + chars->count = 1; + chars->chars[0] = chr; + + if (last) + chars->last_count = 1; return; } -for (i = len; i > 0; i--) - if (chars[i] == chr) +for (i = 0; i < count; i++) + if (chars->chars[i] == chr) return; -if (len >= MAX_DIFF_CHARS - 1) +if (count >= MAX_DIFF_CHARS) { - chars[0] = 255; + chars->count = 255; return; } -len++; -chars[len] = chr; -chars[0] = len; +chars->chars[count] = chr; +chars->count = count + 1; + +if (last) + chars->last_count++; } -static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *chars, int max_chars, sljit_u32 *rec_count) +static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, fast_forward_char_data *chars, int max_chars, sljit_u32 *rec_count) { /* Recursive function, which scans prefix literals. */ BOOL last, any, class, caseless; @@ -3488,7 +3878,7 @@ sljit_u32 chr; /* Any unicode character. */ sljit_u8 *bytes, *bytes_end, byte; PCRE2_SPTR alternative, cc_save, oc; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 -PCRE2_UCHAR othercase[8]; +PCRE2_UCHAR othercase[4]; #elif defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 PCRE2_UCHAR othercase[2]; #else @@ -3511,6 +3901,7 @@ while (TRUE) { case OP_CHARI: caseless = TRUE; + /* Fall through */ case OP_CHAR: last = FALSE; cc++; @@ -3542,6 +3933,7 @@ while (TRUE) case OP_MINPLUSI: case OP_POSPLUSI: caseless = TRUE; + /* Fall through */ case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: @@ -3550,6 +3942,7 @@ while (TRUE) case OP_EXACTI: caseless = TRUE; + /* Fall through */ case OP_EXACT: repeat = GET2(cc, 1); last = FALSE; @@ -3560,6 +3953,7 @@ while (TRUE) case OP_MINQUERYI: case OP_POSQUERYI: caseless = TRUE; + /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: @@ -3583,7 +3977,6 @@ while (TRUE) continue; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_BRAPOS: case OP_CBRA: @@ -3704,12 +4097,12 @@ while (TRUE) { do { - chars[0] = 255; + chars->count = 255; consumed++; if (--max_chars == 0) return consumed; - chars += MAX_DIFF_CHARS; + chars++; } while (--repeat > 0); @@ -3753,8 +4146,8 @@ while (TRUE) do { if (bytes[31] & 0x80) - chars[0] = 255; - else if (chars[0] != 255) + chars->count = 255; + else if (chars->count != 255) { bytes_end = bytes + 32; chr = 0; @@ -3769,7 +4162,7 @@ while (TRUE) do { if ((byte & 0x1) != 0) - add_prefix_char(chr, chars); + add_prefix_char(chr, chars, TRUE); byte >>= 1; chr++; } @@ -3777,14 +4170,14 @@ while (TRUE) chr = (chr + 7) & ~7; } } - while (chars[0] != 255 && bytes < bytes_end); + while (chars->count != 255 && bytes < bytes_end); bytes = bytes_end - 32; } consumed++; if (--max_chars == 0) return consumed; - chars += MAX_DIFF_CHARS; + chars++; } while (--repeat > 0); @@ -3848,17 +4241,18 @@ while (TRUE) oc = othercase; do { + len--; + consumed++; + chr = *cc; - add_prefix_char(*cc, chars); + add_prefix_char(*cc, chars, len == 0); if (caseless) - add_prefix_char(*oc, chars); + add_prefix_char(*oc, chars, len == 0); - len--; - consumed++; if (--max_chars == 0) return consumed; - chars += MAX_DIFF_CHARS; + chars++; cc++; oc++; } @@ -3877,7 +4271,37 @@ while (TRUE) } } -#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +static void jumpto_if_not_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg, struct sljit_label *label) +{ +#if PCRE2_CODE_UNIT_WIDTH == 8 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0); +CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0x80, label); +#elif PCRE2_CODE_UNIT_WIDTH == 16 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00); +CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00, label); +#else +#error "Unknown code width" +#endif +} +#endif + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +static struct sljit_jump *jump_if_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg) +{ +#if PCRE2_CODE_UNIT_WIDTH == 8 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0); +return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0x80); +#elif PCRE2_CODE_UNIT_WIDTH == 16 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00); +return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00); +#else +#error "Unknown code width" +#endif +} +#endif static sljit_s32 character_to_int32(PCRE2_UCHAR chr) { @@ -3896,39 +4320,140 @@ return value; #endif } -static SLJIT_INLINE void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2) +static void load_from_mem_sse2(struct sljit_compiler *compiler, sljit_s32 dst_xmm_reg, sljit_s32 src_general_reg) { -DEFINE_COMPILER; -struct sljit_label *start; -struct sljit_jump *quit[3]; -struct sljit_jump *nomatch; -sljit_u8 instruction[8]; -sljit_s32 tmp1_ind = sljit_get_register_index(TMP1); -sljit_s32 tmp2_ind = sljit_get_register_index(TMP2); -sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR); -BOOL load_twice = FALSE; -PCRE2_UCHAR bit; - -bit = char1 ^ char2; -if (!is_powerof2(bit)) - bit = 0; - -if ((char1 != char2) && bit == 0) - load_twice = TRUE; - -quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +sljit_u8 instruction[5]; +#else +sljit_u8 instruction[4]; +#endif -/* First part (unaligned start) */ +SLJIT_ASSERT(dst_xmm_reg < 8); + +/* MOVDQA xmm1, xmm2/m128 */ +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +if (src_general_reg < 8) + { + instruction[0] = 0x66; + instruction[1] = 0x0f; + instruction[2] = 0x6f; + instruction[3] = (dst_xmm_reg << 3) | src_general_reg; + sljit_emit_op_custom(compiler, instruction, 4); + } +else + { + instruction[0] = 0x66; + instruction[1] = 0x41; + instruction[2] = 0x0f; + instruction[3] = 0x6f; + instruction[4] = (dst_xmm_reg << 3) | (src_general_reg & 0x7); + sljit_emit_op_custom(compiler, instruction, 4); + } +#else +instruction[0] = 0x66; +instruction[1] = 0x0f; +instruction[2] = 0x6f; +instruction[3] = (dst_xmm_reg << 3) | src_general_reg; +sljit_emit_op_custom(compiler, instruction, 4); +#endif +} + +static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, PCRE2_UCHAR char1, PCRE2_UCHAR char2, + sljit_u32 bit, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind) +{ +sljit_u8 instruction[4]; +instruction[0] = 0x66; +instruction[1] = 0x0f; + +if (char1 == char2 || bit != 0) + { + if (bit != 0) + { + /* POR xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0xeb; + instruction[3] = 0xc0 | (dst_ind << 3) | cmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + + /* PCMPEQB/W/D xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; + instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } +else + { + /* MOVDQA xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0x6f; + instruction[3] = 0xc0 | (tmp_ind << 3) | dst_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + /* PCMPEQB/W/D xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; + instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + instruction[3] = 0xc0 | (tmp_ind << 3) | cmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + /* POR xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0xeb; + instruction[3] = 0xc0 | (dst_ind << 3) | tmp_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } +} + +static void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset) +{ +DEFINE_COMPILER; +struct sljit_label *start; +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +struct sljit_label *restart; +#endif +struct sljit_jump *quit; +struct sljit_jump *partial_quit[2]; +sljit_u8 instruction[8]; +sljit_s32 tmp1_ind = sljit_get_register_index(TMP1); +sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR); +sljit_s32 data_ind = 0; +sljit_s32 tmp_ind = 1; +sljit_s32 cmp1_ind = 2; +sljit_s32 cmp2_ind = 3; +sljit_u32 bit = 0; + +SLJIT_UNUSED_ARG(offset); + +if (char1 != char2) + { + bit = char1 ^ char2; + if (!is_powerof2(bit)) + bit = 0; + } + +partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit[0]); + +/* First part (unaligned start) */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit)); -SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1); +SLJIT_ASSERT(tmp1_ind < 8); /* MOVD xmm, r/m32 */ instruction[0] = 0x66; instruction[1] = 0x0f; instruction[2] = 0x6e; -instruction[3] = 0xc0 | (2 << 3) | tmp1_ind; +instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); if (char1 != char2) @@ -3936,224 +4461,521 @@ if (char1 != char2) OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2)); /* MOVD xmm, r/m32 */ - instruction[3] = 0xc0 | (3 << 3) | tmp1_ind; + instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); } +OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); + /* PSHUFD xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ instruction[2] = 0x70; -instruction[3] = 0xc0 | (2 << 3) | 2; +instruction[3] = 0xc0 | (cmp1_ind << 3) | 2; instruction[4] = 0; sljit_emit_op_custom(compiler, instruction, 5); if (char1 != char2) { /* PSHUFD xmm1, xmm2/m128, imm8 */ - instruction[3] = 0xc0 | (3 << 3) | 3; - instruction[4] = 0; + instruction[3] = 0xc0 | (cmp2_ind << 3) | 3; sljit_emit_op_custom(compiler, instruction, 5); } -OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 0xf); +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +restart = LABEL(); +#endif OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf); -/* MOVDQA xmm1, xmm2/m128 */ -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - -if (str_ptr_ind < 8) - { - instruction[2] = 0x6f; - instruction[3] = (0 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); +load_from_mem_sse2(compiler, data_ind, str_ptr_ind); +fast_forward_char_pair_sse2_compare(compiler, char1, char2, bit, data_ind, cmp1_ind, cmp2_ind, tmp_ind); - if (load_twice) - { - instruction[3] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); - } - } -else - { - instruction[1] = 0x41; - instruction[2] = 0x0f; - instruction[3] = 0x6f; - instruction[4] = (0 << 3) | (str_ptr_ind & 0x7); - sljit_emit_op_custom(compiler, instruction, 5); +/* PMOVMSKB reg, xmm */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xd7; +instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; +sljit_emit_op_custom(compiler, instruction, 4); - if (load_twice) - { - instruction[4] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 5); - } - instruction[1] = 0x0f; - } +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0); -#else +/* BSF r32, r/m32 */ +instruction[0] = 0x0f; +instruction[1] = 0xbc; +instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 3); +sljit_set_current_flags(compiler, SLJIT_SET_Z); -instruction[2] = 0x6f; -instruction[3] = (0 << 3) | str_ptr_ind; -sljit_emit_op_custom(compiler, instruction, 4); +quit = JUMP(SLJIT_NOT_ZERO); -if (load_twice) - { - instruction[3] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); - } +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); -#endif +start = LABEL(); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16); -if (bit != 0) - { - /* POR xmm1, xmm2/m128 */ - instruction[2] = 0xeb; - instruction[3] = 0xc0 | (0 << 3) | 3; - sljit_emit_op_custom(compiler, instruction, 4); - } +partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit[1]); -/* PCMPEQB/W/D xmm1, xmm2/m128 */ -instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; -instruction[3] = 0xc0 | (0 << 3) | 2; -sljit_emit_op_custom(compiler, instruction, 4); +/* Second part (aligned) */ -if (load_twice) - { - instruction[3] = 0xc0 | (1 << 3) | 3; - sljit_emit_op_custom(compiler, instruction, 4); - } +load_from_mem_sse2(compiler, 0, str_ptr_ind); +fast_forward_char_pair_sse2_compare(compiler, char1, char2, bit, data_ind, cmp1_ind, cmp2_ind, tmp_ind); /* PMOVMSKB reg, xmm */ +instruction[0] = 0x66; +instruction[1] = 0x0f; instruction[2] = 0xd7; instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; sljit_emit_op_custom(compiler, instruction, 4); -if (load_twice) - { - OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0); - instruction[3] = 0xc0 | (tmp2_ind << 3) | 1; - sljit_emit_op_custom(compiler, instruction, 4); - - OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); - OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0); - } - -OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0); - /* BSF r32, r/m32 */ instruction[0] = 0x0f; instruction[1] = 0xbc; instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 3); +sljit_set_current_flags(compiler, SLJIT_SET_Z); -nomatch = JUMP(SLJIT_ZERO); +JUMPTO(SLJIT_ZERO, start); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +JUMPHERE(quit); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); -quit[1] = JUMP(SLJIT_JUMP); -JUMPHERE(nomatch); +if (common->mode != PCRE2_JIT_COMPLETE) + { + JUMPHERE(partial_quit[0]); + JUMPHERE(partial_quit[1]); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); + CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0); + } +else + add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); -start = LABEL(); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16); -quit[2] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf && offset > 0) + { + SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); -/* Second part (aligned) */ + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset)); -instruction[0] = 0x66; -instruction[1] = 0x0f; + quit = jump_if_utf_char_start(compiler, TMP1); -/* MOVDQA xmm1, xmm2/m128 */ -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); + JUMPTO(SLJIT_JUMP, restart); + + JUMPHERE(quit); + } +#endif +} + +#ifndef _WIN64 + +static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_sse2_offset(void) +{ +#if PCRE2_CODE_UNIT_WIDTH == 8 +return 15; +#elif PCRE2_CODE_UNIT_WIDTH == 16 +return 7; +#elif PCRE2_CODE_UNIT_WIDTH == 32 +return 3; +#else +#error "Unsupported unit width" +#endif +} + +static void fast_forward_char_pair_sse2(compiler_common *common, sljit_s32 offs1, + PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b) +{ +DEFINE_COMPILER; +sljit_u32 bit1 = 0; +sljit_u32 bit2 = 0; +sljit_u32 diff = IN_UCHARS(offs1 - offs2); +sljit_s32 tmp1_ind = sljit_get_register_index(TMP1); +sljit_s32 tmp2_ind = sljit_get_register_index(TMP2); +sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR); +sljit_s32 data1_ind = 0; +sljit_s32 data2_ind = 1; +sljit_s32 tmp_ind = 2; +sljit_s32 cmp1a_ind = 3; +sljit_s32 cmp1b_ind = 4; +sljit_s32 cmp2a_ind = 5; +sljit_s32 cmp2b_ind = 6; +struct sljit_label *start; +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +struct sljit_label *restart; +#endif +struct sljit_jump *jump[2]; + +sljit_u8 instruction[8]; -if (str_ptr_ind < 8) +SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2); +SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_sse2_offset())); +SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1); + +/* Initialize. */ +if (common->match_end_ptr != 0) { - instruction[2] = 0x6f; - instruction[3] = (0 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1)); - if (load_twice) + OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, STR_END, 0); + CMOV(SLJIT_LESS, STR_END, TMP1, 0); + } + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1)); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +/* MOVD xmm, r/m32 */ +instruction[0] = 0x66; +instruction[1] = 0x0f; +instruction[2] = 0x6e; + +if (char1a == char1b) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a)); +else + { + bit1 = char1a ^ char1b; + if (is_powerof2(bit1)) { - instruction[3] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 4); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a | bit1)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit1)); + } + else + { + bit1 = 0; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char1b)); } } -else + +instruction[3] = 0xc0 | (cmp1a_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +if (char1a != char1b) { - instruction[1] = 0x41; - instruction[2] = 0x0f; - instruction[3] = 0x6f; - instruction[4] = (0 << 3) | (str_ptr_ind & 0x7); - sljit_emit_op_custom(compiler, instruction, 5); + instruction[3] = 0xc0 | (cmp1b_ind << 3) | tmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } - if (load_twice) +if (char2a == char2b) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a)); +else + { + bit2 = char2a ^ char2b; + if (is_powerof2(bit2)) { - instruction[4] = (1 << 3) | str_ptr_ind; - sljit_emit_op_custom(compiler, instruction, 5); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a | bit2)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit2)); + } + else + { + bit2 = 0; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char2b)); } - instruction[1] = 0x0f; } -#else - -instruction[2] = 0x6f; -instruction[3] = (0 << 3) | str_ptr_ind; +instruction[3] = 0xc0 | (cmp2a_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); -if (load_twice) +if (char2a != char2b) { - instruction[3] = (1 << 3) | str_ptr_ind; + instruction[3] = 0xc0 | (cmp2b_ind << 3) | tmp2_ind; sljit_emit_op_custom(compiler, instruction, 4); } -#endif +/* PSHUFD xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x70; +instruction[4] = 0; -if (bit != 0) +instruction[3] = 0xc0 | (cmp1a_ind << 3) | cmp1a_ind; +sljit_emit_op_custom(compiler, instruction, 5); + +if (char1a != char1b) { - /* POR xmm1, xmm2/m128 */ - instruction[2] = 0xeb; - instruction[3] = 0xc0 | (0 << 3) | 3; - sljit_emit_op_custom(compiler, instruction, 4); + instruction[3] = 0xc0 | (cmp1b_ind << 3) | cmp1b_ind; + sljit_emit_op_custom(compiler, instruction, 5); } -/* PCMPEQB/W/D xmm1, xmm2/m128 */ -instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; -instruction[3] = 0xc0 | (0 << 3) | 2; -sljit_emit_op_custom(compiler, instruction, 4); +instruction[3] = 0xc0 | (cmp2a_ind << 3) | cmp2a_ind; +sljit_emit_op_custom(compiler, instruction, 5); -if (load_twice) +if (char2a != char2b) { - instruction[3] = 0xc0 | (1 << 3) | 3; - sljit_emit_op_custom(compiler, instruction, 4); + instruction[3] = 0xc0 | (cmp2b_ind << 3) | cmp2b_ind; + sljit_emit_op_custom(compiler, instruction, 5); } +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +restart = LABEL(); +#endif + +OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1 - offs2)); +OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); +OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, ~0xf); + +load_from_mem_sse2(compiler, data1_ind, str_ptr_ind); + +jump[0] = CMP(SLJIT_EQUAL, STR_PTR, 0, TMP1, 0); + +load_from_mem_sse2(compiler, data2_ind, tmp1_ind); + +/* MOVDQA xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x6f; +instruction[3] = 0xc0 | (tmp_ind << 3) | data1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PSLLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (7 << 3) | tmp_ind; +instruction[4] = diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* PSRLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +/* instruction[2] = 0x73; */ +instruction[3] = 0xc0 | (3 << 3) | data2_ind; +instruction[4] = 16 - diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* POR xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xeb; +instruction[3] = 0xc0 | (data2_ind << 3) | tmp_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +jump[1] = JUMP(SLJIT_JUMP); + +JUMPHERE(jump[0]); + +/* MOVDQA xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x6f; +instruction[3] = 0xc0 | (data2_ind << 3) | data1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PSLLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (7 << 3) | data2_ind; +instruction[4] = diff; +sljit_emit_op_custom(compiler, instruction, 5); + +JUMPHERE(jump[1]); + +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf); + +fast_forward_char_pair_sse2_compare(compiler, char2a, char2b, bit2, data2_ind, cmp2a_ind, cmp2b_ind, tmp_ind); +fast_forward_char_pair_sse2_compare(compiler, char1a, char1b, bit1, data1_ind, cmp1a_ind, cmp1b_ind, tmp_ind); + +/* PAND xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xdb; +instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind; +sljit_emit_op_custom(compiler, instruction, 4); + /* PMOVMSKB reg, xmm */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ instruction[2] = 0xd7; instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; sljit_emit_op_custom(compiler, instruction, 4); -if (load_twice) - { - instruction[3] = 0xc0 | (tmp2_ind << 3) | 1; - sljit_emit_op_custom(compiler, instruction, 4); +/* Ignore matches before the first STR_PTR. */ +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0); - OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); - } +/* BSF r32, r/m32 */ +instruction[0] = 0x0f; +instruction[1] = 0xbc; +instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 3); +sljit_set_current_flags(compiler, SLJIT_SET_Z); + +jump[0] = JUMP(SLJIT_NOT_ZERO); + +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + +/* Main loop. */ +instruction[0] = 0x66; +instruction[1] = 0x0f; + +start = LABEL(); + +load_from_mem_sse2(compiler, data2_ind, str_ptr_ind); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +load_from_mem_sse2(compiler, data1_ind, str_ptr_ind); + +/* PSRLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (3 << 3) | data2_ind; +instruction[4] = 16 - diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* MOVDQA xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x6f; +instruction[3] = 0xc0 | (tmp_ind << 3) | data1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PSLLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (7 << 3) | tmp_ind; +instruction[4] = diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* POR xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xeb; +instruction[3] = 0xc0 | (data2_ind << 3) | tmp_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +fast_forward_char_pair_sse2_compare(compiler, char1a, char1b, bit1, data1_ind, cmp1a_ind, cmp1b_ind, tmp_ind); +fast_forward_char_pair_sse2_compare(compiler, char2a, char2b, bit2, data2_ind, cmp2a_ind, cmp2b_ind, tmp_ind); + +/* PAND xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xdb; +instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PMOVMSKB reg, xmm */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xd7; +instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; +sljit_emit_op_custom(compiler, instruction, 4); /* BSF r32, r/m32 */ instruction[0] = 0x0f; instruction[1] = 0xbc; instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 3); +sljit_set_current_flags(compiler, SLJIT_SET_Z); JUMPTO(SLJIT_ZERO, start); +JUMPHERE(jump[0]); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); -start = LABEL(); -SET_LABEL(quit[0], start); -SET_LABEL(quit[1], start); -SET_LABEL(quit[2], start); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +if (common->match_end_ptr != 0) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1)); + + jump[0] = jump_if_utf_char_start(compiler, TMP1); + + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, restart); + + add_jump(compiler, &common->failed_match, JUMP(SLJIT_JUMP)); + + JUMPHERE(jump[0]); + } +#endif + +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1)); + +if (common->match_end_ptr != 0) + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); +} + +static BOOL check_fast_forward_char_pair_sse2(compiler_common *common, fast_forward_char_data *chars, int max) +{ +sljit_s32 i, j, priority, count; +sljit_u32 priorities; +PCRE2_UCHAR a1, a2, b1, b2; + +priorities = 0; + +count = 0; +for (i = 0; i < max; i++) + { + if (chars[i].last_count > 2) + { + SLJIT_ASSERT(chars[i].last_count <= 7); + + priorities |= (1 << chars[i].last_count); + count++; + } + } + +if (count < 2) + return FALSE; + +for (priority = 7; priority > 2; priority--) + { + if ((priorities & (1 << priority)) == 0) + continue; + + for (i = max - 1; i >= 1; i--) + if (chars[i].last_count >= priority) + { + SLJIT_ASSERT(chars[i].count <= 2 && chars[i].count >= 1); + + a1 = chars[i].chars[0]; + a2 = chars[i].chars[1]; + + j = i - max_fast_forward_char_pair_sse2_offset(); + if (j < 0) + j = 0; + + while (j < i) + { + if (chars[j].last_count >= priority) + { + b1 = chars[j].chars[0]; + b2 = chars[j].chars[1]; + + if (a1 != b1 && a1 != b2 && a2 != b1 && a2 != b2) + { + fast_forward_char_pair_sse2(common, i, a1, a2, j, b1, b2); + return TRUE; + } + } + j++; + } + } + } + +return FALSE; } +#endif + #undef SSE2_COMPARE_TYPE_INDEX #endif @@ -4162,15 +4984,16 @@ static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1, { DEFINE_COMPILER; struct sljit_label *start; -struct sljit_jump *quit; -struct sljit_jump *found; +struct sljit_jump *match; +struct sljit_jump *partial_quit; PCRE2_UCHAR mask; -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 -struct sljit_label *utf_start = NULL; -struct sljit_jump *utf_quit = NULL; -#endif BOOL has_match_end = (common->match_end_ptr != 0); +SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0); + +if (has_match_end) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + if (offset > 0) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); @@ -4178,76 +5001,21 @@ if (has_match_end) { OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); - OP2(SLJIT_ADD, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, SLJIT_IMM, IN_UCHARS(offset + 1)); -#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) - if (sljit_x86_is_cmov_available()) - { - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_END, 0, TMP3, 0); - sljit_x86_emit_cmov(compiler, SLJIT_GREATER, STR_END, TMP3, 0); - } -#endif - { - quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP3, 0); - OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); - JUMPHERE(quit); - } + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offset + 1)); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); + CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 -if (common->utf && offset > 0) - utf_start = LABEL(); -#endif - -#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) /* SSE2 accelerated first character search. */ -if (sljit_x86_is_sse2_available()) +if (sljit_has_cpu_feature(SLJIT_HAS_SSE2)) { - fast_forward_first_char2_sse2(common, char1, char2); - - SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0); - if (common->mode == PCRE2_JIT_COMPLETE) - { - /* In complete mode, we don't need to run a match when STR_PTR == STR_END. */ - SLJIT_ASSERT(common->forced_quit_label == NULL); - OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); - add_jump(compiler, &common->forced_quit, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); - -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 - if (common->utf && offset > 0) - { - SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); + fast_forward_first_char2_sse2(common, char1, char2, offset); - OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset)); - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -#if PCRE2_CODE_UNIT_WIDTH == 8 - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); - CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start); -#elif PCRE2_CODE_UNIT_WIDTH == 16 - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start); -#else -#error "Unknown code width" -#endif - OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); - } -#endif - - if (offset > 0) - OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); - } - else if (sljit_x86_is_cmov_available()) - { - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); - sljit_x86_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0); - } - else - { - quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_PTR, 0, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0); - JUMPHERE(quit); - } + if (offset > 0) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); if (has_match_end) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); @@ -4256,85 +5024,56 @@ if (sljit_x86_is_sse2_available()) #endif -quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); - start = LABEL(); + +partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (char1 == char2) - found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1, start); else { mask = char1 ^ char2; if (is_powerof2(mask)) { OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask); - found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask, start); } else { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char1); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char2); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - found = JUMP(SLJIT_NOT_ZERO); + match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char2, start); + JUMPHERE(match); } } -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, start); - -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 -if (common->utf && offset > 0) - utf_quit = JUMP(SLJIT_JUMP); -#endif - -JUMPHERE(found); - #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 if (common->utf && offset > 0) { - OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset)); - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -#if PCRE2_CODE_UNIT_WIDTH == 8 - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); - CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start); -#elif PCRE2_CODE_UNIT_WIDTH == 16 - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start); -#else -#error "Unknown code width" -#endif - OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); - JUMPHERE(utf_quit); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-(offset + 1))); + jumpto_if_not_utf_char_start(compiler, TMP1, start); } #endif -JUMPHERE(quit); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset + 1)); + +if (common->mode != PCRE2_JIT_COMPLETE) + JUMPHERE(partial_quit); if (has_match_end) - { - quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); - if (offset > 0) - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); - JUMPHERE(quit); OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); - } - -if (offset > 0) - OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); } static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *start; -struct sljit_jump *quit; struct sljit_jump *match; -/* bytes[0] represent the number of characters between 0 -and MAX_N_BYTES - 1, 255 represents any character. */ -PCRE2_UCHAR chars[MAX_N_CHARS * MAX_DIFF_CHARS]; +fast_forward_char_data chars[MAX_N_CHARS]; sljit_s32 offset; PCRE2_UCHAR mask; PCRE2_UCHAR *char_set, *char_set_end; @@ -4345,7 +5084,10 @@ BOOL in_range; sljit_u32 rec_count; for (i = 0; i < MAX_N_CHARS; i++) - chars[i * MAX_DIFF_CHARS] = 0; + { + chars[i].count = 0; + chars[i].last_count = 0; + } rec_count = 10000; max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count); @@ -4353,21 +5095,50 @@ max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count); if (max < 1) return FALSE; +/* Convert last_count to priority. */ +for (i = 0; i < max; i++) + { + SLJIT_ASSERT(chars[i].count > 0 && chars[i].last_count <= chars[i].count); + + if (chars[i].count == 1) + { + chars[i].last_count = (chars[i].last_count == 1) ? 7 : 5; + /* Simplifies algorithms later. */ + chars[i].chars[1] = chars[i].chars[0]; + } + else if (chars[i].count == 2) + { + SLJIT_ASSERT(chars[i].chars[0] != chars[i].chars[1]); + + if (is_powerof2(chars[i].chars[0] ^ chars[i].chars[1])) + chars[i].last_count = (chars[i].last_count == 2) ? 6 : 4; + else + chars[i].last_count = (chars[i].last_count == 2) ? 3 : 2; + } + else + chars[i].last_count = (chars[i].count == 255) ? 0 : 1; + } + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) && !(defined _WIN64) +if (check_fast_forward_char_pair_sse2(common, chars, max)) + return TRUE; +#endif + in_range = FALSE; /* Prevent compiler "uninitialized" warning */ from = 0; range_len = 4 /* minimum length */ - 1; for (i = 0; i <= max; i++) { - if (in_range && (i - from) > range_len && (chars[(i - 1) * MAX_DIFF_CHARS] < 255)) + if (in_range && (i - from) > range_len && (chars[i - 1].count < 255)) { range_len = i - from; range_right = i - 1; } - if (i < max && chars[i * MAX_DIFF_CHARS] < 255) + if (i < max && chars[i].count < 255) { - SLJIT_ASSERT(chars[i * MAX_DIFF_CHARS] > 0); + SLJIT_ASSERT(chars[i].count > 0); if (!in_range) { in_range = TRUE; @@ -4387,16 +5158,17 @@ if (range_right >= 0) for (i = 0; i < range_len; i++) { - char_set = chars + ((range_right - i) * MAX_DIFF_CHARS); - SLJIT_ASSERT(char_set[0] > 0 && char_set[0] < 255); - char_set_end = char_set + char_set[0]; - char_set++; - while (char_set <= char_set_end) + SLJIT_ASSERT(chars[range_right - i].count > 0 && chars[range_right - i].count < 255); + + char_set = chars[range_right - i].chars; + char_set_end = char_set + chars[range_right - i].count; + do { if (update_table[(*char_set) & 0xff] > IN_UCHARS(i)) update_table[(*char_set) & 0xff] = IN_UCHARS(i); char_set++; } + while (char_set < char_set_end); } } @@ -4404,54 +5176,38 @@ offset = -1; /* Scan forward. */ for (i = 0; i < max; i++) { + if (range_right == i) + continue; + if (offset == -1) { - if (chars[i * MAX_DIFF_CHARS] <= 2) - offset = i; - } - else if (chars[offset * MAX_DIFF_CHARS] == 2 && chars[i * MAX_DIFF_CHARS] <= 2) - { - if (chars[i * MAX_DIFF_CHARS] == 1) + if (chars[i].last_count >= 2) offset = i; - else - { - mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2]; - if (!is_powerof2(mask)) - { - mask = chars[i * MAX_DIFF_CHARS + 1] ^ chars[i * MAX_DIFF_CHARS + 2]; - if (is_powerof2(mask)) - offset = i; - } - } } + else if (chars[offset].last_count < chars[i].last_count) + offset = i; } +SLJIT_ASSERT(offset == -1 || (chars[offset].count >= 1 && chars[offset].count <= 2)); + if (range_right < 0) { if (offset < 0) return FALSE; - SLJIT_ASSERT(chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2); /* Works regardless the value is 1 or 2. */ - mask = chars[offset * MAX_DIFF_CHARS + chars[offset * MAX_DIFF_CHARS]]; - fast_forward_first_char2(common, chars[offset * MAX_DIFF_CHARS + 1], mask, offset); + fast_forward_first_char2(common, chars[offset].chars[0], chars[offset].chars[1], offset); return TRUE; } -if (range_right == offset) - offset = -1; +SLJIT_ASSERT(range_right != offset); -SLJIT_ASSERT(offset == -1 || (chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2)); - -max -= 1; -SLJIT_ASSERT(max > 0); if (common->match_end_ptr != 0) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); - quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP1, 0); - OP1(SLJIT_MOV, STR_END, 0, TMP1, 0); - JUMPHERE(quit); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); + CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } else OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); @@ -4463,7 +5219,7 @@ OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table); #endif start = LABEL(); -quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0)); #if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right)); @@ -4484,20 +5240,20 @@ if (offset >= 0) OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset)); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); - if (chars[offset * MAX_DIFF_CHARS] == 1) - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1], start); + if (chars[offset].count == 1) + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0], start); else { - mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2]; + mask = chars[offset].chars[0] ^ chars[offset].chars[1]; if (is_powerof2(mask)) { OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask); - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1] | mask, start); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0] | mask, start); } else { - match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1]); - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 2], start); + match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0]); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[1], start); JUMPHERE(match); } } @@ -4513,15 +5269,9 @@ if (common->utf && offset != 0) } else OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); -#if PCRE2_CODE_UNIT_WIDTH == 8 - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); - CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, start); -#elif PCRE2_CODE_UNIT_WIDTH == 16 - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, start); -#else -#error "Unknown code width" -#endif + + jumpto_if_not_utf_char_start(compiler, TMP1, start); + if (offset < 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } @@ -4530,33 +5280,20 @@ if (common->utf && offset != 0) if (offset >= 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -JUMPHERE(quit); - if (common->match_end_ptr != 0) - { - if (range_right >= 0) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); - if (range_right >= 0) - { - quit = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP1, 0); - OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); - JUMPHERE(quit); - } - } else OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); return TRUE; } -#undef MAX_N_CHARS - -static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, PCRE2_UCHAR first_char, BOOL caseless) +static SLJIT_INLINE void fast_forward_first_char(compiler_common *common) { +PCRE2_UCHAR first_char = (PCRE2_UCHAR)(common->re->first_codeunit); PCRE2_UCHAR oc; oc = first_char; -if (caseless) +if ((common->re->flags & PCRE2_FIRSTCASELESS) != 0) { oc = TABLE_GET(first_char, common->fcc, first_char); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 @@ -4594,8 +5331,8 @@ if (common->nltype == NLTYPE_FIXED && common->newline > 255) firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2)); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -4639,8 +5376,8 @@ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) JUMPHERE(foundcr); notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -4655,79 +5392,75 @@ if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); } -static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks); +static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks); -static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, const sljit_u8 *start_bits) +static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common) { DEFINE_COMPILER; +const sljit_u8 *start_bits = common->re->start_bitmap; struct sljit_label *start; -struct sljit_jump *quit; -struct sljit_jump *found = NULL; -jump_list *matches = NULL; +struct sljit_jump *partial_quit; #if PCRE2_CODE_UNIT_WIDTH != 8 -struct sljit_jump *jump; +struct sljit_jump *found = NULL; #endif +jump_list *matches = NULL; if (common->match_end_ptr != 0) { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); + CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } start = LABEL(); -quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + +partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); -#ifdef SUPPORT_UNICODE -if (common->utf) - OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); -#endif +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -if (!check_class_ranges(common, start_bits, (start_bits[31] & 0x80) != 0, TRUE, &matches)) +if (!optimize_class(common, start_bits, (start_bits[31] & 0x80) != 0, FALSE, &matches)) { #if PCRE2_CODE_UNIT_WIDTH != 8 - jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 255); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 255); - JUMPHERE(jump); + if ((start_bits[31] & 0x80) != 0) + found = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255); + else + CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255, start); +#elif defined SUPPORT_UNICODE + if (common->utf && is_char7_bitset(start_bits, FALSE)) + CMPTO(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 127, start); #endif OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits); - OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); - found = JUMP(SLJIT_NOT_ZERO); + if (sljit_get_register_index(TMP3) >= 0) + { + OP2(SLJIT_SHL, TMP3, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP3, 0); + } + else + { + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + } + JUMPTO(SLJIT_ZERO, start); } +else + set_jumps(matches, start); -#ifdef SUPPORT_UNICODE -if (common->utf) - OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); -#endif -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -#ifdef SUPPORT_UNICODE -#if PCRE2_CODE_UNIT_WIDTH == 8 -if (common->utf) - { - CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start); - OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); - } -#elif PCRE2_CODE_UNIT_WIDTH == 16 -if (common->utf) - { - CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start); - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); - } -#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */ -#endif /* SUPPORT_UNICODE */ -JUMPTO(SLJIT_JUMP, start); +#if PCRE2_CODE_UNIT_WIDTH != 8 if (found != NULL) JUMPHERE(found); -if (matches != NULL) - set_jumps(matches, LABEL()); -JUMPHERE(quit); +#endif + +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (common->mode != PCRE2_JIT_COMPLETE) + JUMPHERE(partial_quit); if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0); @@ -4803,31 +5536,50 @@ struct sljit_jump *jump; struct sljit_label *mainloop; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP1(SLJIT_MOV, TMP1, 0, STACK_TOP, 0); -GET_LOCAL_BASE(TMP3, 0, 0); +GET_LOCAL_BASE(TMP1, 0, 0); /* Drop frames until we reach STACK_TOP. */ mainloop = LABEL(); -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), 0); -OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0); -jump = JUMP(SLJIT_SIG_LESS_EQUAL); - -OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw)); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(TMP1), 2 * sizeof(sljit_sw)); -OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -sizeof(sljit_sw)); +jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0); + +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); +if (sljit_get_register_index (TMP3) < 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); + } +else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); + GET_LOCAL_BASE(TMP1, 0, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0); + } JUMPTO(SLJIT_JUMP, mainloop); JUMPHERE(jump); -jump = JUMP(SLJIT_SIG_LESS); -/* End of dropping frames. */ +jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0); +/* End of reverting values. */ sljit_emit_fast_return(compiler, RETURN_ADDR, 0); JUMPHERE(jump); OP1(SLJIT_NEG, TMP2, 0, TMP2, 0); -OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw)); -OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); +if (sljit_get_register_index (TMP3) < 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); + } +else + { + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0); + } JUMPTO(SLJIT_JUMP, mainloop); } @@ -4860,11 +5612,11 @@ if (common->use_ucp) jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); JUMPHERE(jump); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0); } @@ -4904,11 +5656,11 @@ if (common->use_ucp) jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); JUMPHERE(jump); } else @@ -4936,15 +5688,15 @@ else } set_jumps(skipread_list, LABEL()); -OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); +OP2(SLJIT_XOR | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); } -static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) +static BOOL optimize_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) { /* May destroy TMP1. */ DEFINE_COMPILER; -int ranges[MAX_RANGE_SIZE]; +int ranges[MAX_CLASS_RANGE_SIZE]; sljit_u8 bit, cbit, all; int i, byte, length = 0; @@ -4962,7 +5714,7 @@ for (i = 0; i < 256; ) cbit = (bits[byte] >> (i & 0x7)) & 0x1; if (cbit != bit) { - if (length >= MAX_RANGE_SIZE) + if (length >= MAX_CLASS_RANGE_SIZE) return FALSE; ranges[length] = i; length++; @@ -4975,7 +5727,7 @@ for (i = 0; i < 256; ) if (((bit == 0) && nclass) || ((bit == 1) && !nclass)) { - if (length >= MAX_RANGE_SIZE) + if (length >= MAX_CLASS_RANGE_SIZE) return FALSE; ranges[length] = 256; length++; @@ -5087,9 +5839,118 @@ switch(length) return TRUE; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); + return FALSE; + } +} + +static BOOL optimize_class_chars(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) +{ +/* May destroy TMP1. */ +DEFINE_COMPILER; +uint16_t char_list[MAX_CLASS_CHARS_SIZE]; +uint8_t byte; +sljit_s32 type; +int i, j, k, len, c; + +if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV)) return FALSE; + +if (invert) + nclass = !nclass; + +len = 0; + +for (i = 0; i < 32; i++) + { + byte = bits[i]; + + if (nclass) + byte = ~byte; + + j = 0; + while (byte != 0) + { + if (byte & 0x1) + { + c = i * 8 + j; + + k = len; + + if ((c & 0x20) != 0) + { + for (k = 0; k < len; k++) + if (char_list[k] == c - 0x20) + { + char_list[k] |= 0x120; + break; + } + } + + if (k == len) + { + if (len >= MAX_CLASS_CHARS_SIZE) + return FALSE; + + char_list[len++] = (uint16_t) c; + } + } + + byte >>= 1; + j++; + } + } + +if (len == 0) return FALSE; /* Should never occur, but stops analyzers complaining. */ + +i = 0; +j = 0; + +if (char_list[0] == 0) + { + i++; + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_ZERO); + } +else + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + +while (i < len) + { + if ((char_list[i] & 0x100) != 0) + j++; + else + { + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i]); + CMOV(SLJIT_ZERO, TMP2, TMP1, 0); + } + i++; + } + +if (j != 0) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x20); + + for (i = 0; i < len; i++) + if ((char_list[i] & 0x100) != 0) + { + j--; + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i] & 0xff); + CMOV(SLJIT_ZERO, TMP2, TMP1, 0); + } } + +type = nclass ? SLJIT_NOT_EQUAL : SLJIT_EQUAL; +add_jump(compiler, backtracks, CMP(type, TMP2, 0, SLJIT_IMM, 0)); +return TRUE; +} + +static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) +{ +/* May destroy TMP1. */ +if (optimize_class_ranges(common, bits, nclass, invert, backtracks)) + return TRUE; +return optimize_class_chars(common, bits, nclass, invert, backtracks); } static void check_anynewline(compiler_common *common) @@ -5100,22 +5961,22 @@ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); -OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ -OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); +OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); } @@ -5126,34 +5987,34 @@ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); -OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); +OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ -OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); +OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); } @@ -5166,113 +6027,210 @@ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); -OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ -OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); +OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); } -#define CHAR1 STR_END -#define CHAR2 STACK_TOP - static void do_casefulcmp(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_label *label; +int char1_reg; +int char2_reg; -sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +if (sljit_get_register_index(TMP3) < 0) + { + char1_reg = STR_END; + char2_reg = STACK_TOP; + } +else + { + char1_reg = TMP3; + char2_reg = RETURN_ADDR; + } + +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); -OP1(SLJIT_MOV, TMP3, 0, CHAR1, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, CHAR2, 0); -OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); -OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -label = LABEL(); -OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); -OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); -jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); -JUMPTO(SLJIT_NOT_ZERO, label); +if (char1_reg == STR_END) + { + OP1(SLJIT_MOV, TMP3, 0, char1_reg, 0); + OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0); + } -JUMPHERE(jump); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -OP1(SLJIT_MOV, CHAR1, 0, TMP3, 0); -OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + { + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); + + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + } +else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + { + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); + + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } +else + { + label = LABEL(); + OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0); + OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); + + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + } + +if (char1_reg == STR_END) + { + OP1(SLJIT_MOV, char1_reg, 0, TMP3, 0); + OP1(SLJIT_MOV, char2_reg, 0, RETURN_ADDR, 0); + } + +sljit_emit_fast_return(compiler, TMP1, 0); } -#define LCC_TABLE STACK_LIMIT +static void do_caselesscmp(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *label; +int char1_reg = STR_END; +int char2_reg; +int lcc_table; +int opt_type = 0; + +if (sljit_get_register_index(TMP3) < 0) + { + char2_reg = STACK_TOP; + lcc_table = STACK_LIMIT; + } +else + { + char2_reg = RETURN_ADDR; + lcc_table = TMP3; + } + +if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + opt_type = 1; +else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + opt_type = 2; + +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, char1_reg, 0); + +if (char2_reg == STACK_TOP) + { + OP1(SLJIT_MOV, TMP3, 0, char2_reg, 0); + OP1(SLJIT_MOV, RETURN_ADDR, 0, lcc_table, 0); + } -static void do_caselesscmp(compiler_common *common) -{ -DEFINE_COMPILER; -struct sljit_jump *jump; -struct sljit_label *label; +OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc); -sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +if (opt_type == 1) + { + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + } +else if (opt_type == 2) + { + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -OP1(SLJIT_MOV, TMP3, 0, LCC_TABLE, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, CHAR1, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, CHAR2, 0); -OP1(SLJIT_MOV, LCC_TABLE, 0, SLJIT_IMM, common->lcc); -OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); -OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + } +else + { + label = LABEL(); + OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0); + OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + } -label = LABEL(); -OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); -OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); #if PCRE2_CODE_UNIT_WIDTH != 8 -jump = CMP(SLJIT_GREATER, CHAR1, 0, SLJIT_IMM, 255); +jump = CMP(SLJIT_GREATER, char1_reg, 0, SLJIT_IMM, 255); #endif -OP1(SLJIT_MOV_U8, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0); +OP1(SLJIT_MOV_U8, char1_reg, 0, SLJIT_MEM2(lcc_table, char1_reg), 0); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); -jump = CMP(SLJIT_GREATER, CHAR2, 0, SLJIT_IMM, 255); +jump = CMP(SLJIT_GREATER, char2_reg, 0, SLJIT_IMM, 255); #endif -OP1(SLJIT_MOV_U8, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0); +OP1(SLJIT_MOV_U8, char2_reg, 0, SLJIT_MEM2(lcc_table, char2_reg), 0); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); #endif -jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (opt_type == 0) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); +OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -OP1(SLJIT_MOV, LCC_TABLE, 0, TMP3, 0); -OP1(SLJIT_MOV, CHAR1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); -sljit_emit_fast_return(compiler, RETURN_ADDR, 0); -} +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + +if (opt_type == 2) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (char2_reg == STACK_TOP) + { + OP1(SLJIT_MOV, char2_reg, 0, TMP3, 0); + OP1(SLJIT_MOV, lcc_table, 0, RETURN_ADDR, 0); + } -#undef LCC_TABLE -#undef CHAR1 -#undef CHAR2 +OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); +sljit_emit_fast_return(compiler, TMP1, 0); +} #if defined SUPPORT_UNICODE -static PCRE2_SPTR SLJIT_CALL do_utf_caselesscmp(PCRE2_SPTR src1, jit_arguments *args, PCRE2_SPTR end1) +static PCRE2_SPTR SLJIT_FUNC do_utf_caselesscmp(PCRE2_SPTR src1, PCRE2_SPTR src2, PCRE2_SPTR end1, PCRE2_SPTR end2) { /* This function would be ineffective to do in JIT level. */ sljit_u32 c1, c2; -PCRE2_SPTR src2 = args->startchar_ptr; -PCRE2_SPTR end2 = args->end; const ucd_record *ur; const sljit_u32 *pp; @@ -5417,7 +6375,7 @@ do #endif default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } context->ucharptr = 0; @@ -5592,7 +6550,7 @@ while (*cc != XCL_END) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } cc += 2; @@ -5610,13 +6568,13 @@ if ((cc[-1] & XCL_HASPROP) == 0) if ((cc[-1] & XCL_MAP) != 0) { jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); - if (!check_class_ranges(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found)) + if (!optimize_class(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found)) { OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO)); } @@ -5637,7 +6595,7 @@ else if ((cc[-1] & XCL_MAP) != 0) #ifdef SUPPORT_UNICODE charsaved = TRUE; #endif - if (!check_class_ranges(common, (const sljit_u8 *)cc, FALSE, TRUE, list)) + if (!optimize_class(common, (const sljit_u8 *)cc, FALSE, TRUE, list)) { #if PCRE2_CODE_UNIT_WIDTH == 8 jump = NULL; @@ -5649,7 +6607,7 @@ else if ((cc[-1] & XCL_MAP) != 0) OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO)); #if PCRE2_CODE_UNIT_WIDTH == 8 @@ -5668,8 +6626,18 @@ if (needstype || needsscript) if (needschar && !charsaved) OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); +#if PCRE2_CODE_UNIT_WIDTH == 32 + if (!common->utf) + { + jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); + JUMPHERE(jump); + } +#endif + OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); - OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); @@ -5759,14 +6727,14 @@ while (*cc != XCL_END) if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL); numberofcmps++; } else if (numberofcmps > 0) { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); numberofcmps = 0; } @@ -5785,14 +6753,14 @@ while (*cc != XCL_END) if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) { - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); numberofcmps++; } else if (numberofcmps > 0) { - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); numberofcmps = 0; } @@ -5817,12 +6785,12 @@ while (*cc != XCL_END) break; case PT_LAMP: - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; @@ -5844,33 +6812,33 @@ while (*cc != XCL_END) case PT_SPACE: case PT_PXSPACE: SET_CHAR_OFFSET(9); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); SET_TYPE_OFFSET(ucp_Zl); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_WORD: - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset)); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); /* Fall through. */ case PT_ALNUM: SET_TYPE_OFFSET(ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); - OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, (*cc == PT_ALNUM) ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Nd); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; @@ -5892,8 +6860,8 @@ while (*cc != XCL_END) OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); } - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); other_cases += 2; } else if (is_powerof2(other_cases[2] ^ other_cases[1])) @@ -5905,63 +6873,63 @@ while (*cc != XCL_END) OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); } - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset)); - OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset)); + OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL); other_cases += 3; } else { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); } while (*other_cases != NOTACHAR) { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); - OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); + OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL); } jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_UCNC: - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset)); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset)); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset)); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset)); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset)); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); SET_CHAR_OFFSET(0xa0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset)); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset)); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_GREATER_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_PXGRAPH: /* C and Z groups are the farthest two groups. */ SET_TYPE_OFFSET(ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER); jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll); /* In case of ucp_Cf, we overwrite the result. */ SET_CHAR_OFFSET(0x2066); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); JUMPHERE(jump); jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0); @@ -5970,21 +6938,21 @@ while (*cc != XCL_END) case PT_PXPRINT: /* C and Z groups are the farthest two groups. */ SET_TYPE_OFFSET(ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll); - OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll); + OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL); jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll); /* In case of ucp_Cf, we overwrite the result. */ SET_CHAR_OFFSET(0x2066); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); JUMPHERE(jump); jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0); @@ -5992,21 +6960,21 @@ while (*cc != XCL_END) case PT_PXPUNCT: SET_TYPE_OFFSET(ucp_Sc); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f); - OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f); + OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Pc); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } cc += 2; @@ -6052,6 +7020,7 @@ switch(type) case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; @@ -6067,10 +7036,10 @@ switch(type) else { jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL)); check_partial(common, TRUE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); @@ -6092,9 +7061,9 @@ switch(type) OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); jump[2] = JUMP(SLJIT_GREATER); - add_jump(compiler, backtracks, JUMP(SLJIT_LESS)); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */); /* Equal. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); @@ -6113,6 +7082,7 @@ switch(type) read_char_range(common, common->nlmin, common->nlmax, TRUE); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(SLJIT_ZERO)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); } @@ -6130,8 +7100,8 @@ switch(type) case OP_DOLL: OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); if (!common->endonly) compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks); @@ -6145,8 +7115,8 @@ switch(type) case OP_DOLLM: jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); check_partial(common, FALSE); jump[0] = JUMP(SLJIT_JUMP); JUMPHERE(jump[1]); @@ -6183,16 +7153,16 @@ switch(type) OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0)); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); return cc; case OP_CIRCM: OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); jump[0] = JUMP(SLJIT_JUMP); JUMPHERE(jump[1]); @@ -6230,7 +7200,7 @@ switch(type) label = LABEL(); add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP3, 0)); skip_char_back(common); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -6243,10 +7213,128 @@ switch(type) check_start_used_ptr(common); return cc + LINK_SIZE; } -SLJIT_ASSERT_STOP(); +SLJIT_UNREACHABLE(); +return cc; +} + +#ifdef SUPPORT_UNICODE + +#if PCRE2_CODE_UNIT_WIDTH != 32 + +static PCRE2_SPTR SLJIT_FUNC do_extuni_utf(jit_arguments *args, PCRE2_SPTR cc) +{ +PCRE2_SPTR start_subject = args->begin; +PCRE2_SPTR end_subject = args->end; +int lgb, rgb, len, ricount; +PCRE2_SPTR prevcc, bptr; +uint32_t c; + +prevcc = cc; +GETCHARINC(c, cc); +lgb = UCD_GRAPHBREAK(c); + +while (cc < end_subject) + { + len = 1; + GETCHARLEN(c, cc, len); + rgb = UCD_GRAPHBREAK(c); + + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ + + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + ricount = 0; + bptr = prevcc; + + /* bptr is pointing to the left-hand character */ + while (bptr > start_subject) + { + bptr--; + BACKCHAR(bptr); + GETCHAR(c, bptr); + + if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; + + ricount++; + } + + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this + allows any number of them before a following Extended_Pictographic. */ + + if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || + lgb != ucp_gbExtended_Pictographic) + lgb = rgb; + + prevcc = cc; + cc += len; + } + +return cc; +} + +#endif + +static PCRE2_SPTR SLJIT_FUNC do_extuni_no_utf(jit_arguments *args, PCRE2_SPTR cc) +{ +PCRE2_SPTR start_subject = args->begin; +PCRE2_SPTR end_subject = args->end; +int lgb, rgb, ricount; +PCRE2_SPTR bptr; +uint32_t c; + +GETCHARINC(c, cc); +lgb = UCD_GRAPHBREAK(c); + +while (cc < end_subject) + { + c = *cc; + rgb = UCD_GRAPHBREAK(c); + + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ + + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + ricount = 0; + bptr = cc - 1; + + /* bptr is pointing to the left-hand character */ + while (bptr > start_subject) + { + bptr--; + c = *bptr; + + if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; + + ricount++; + } + + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this + allows any number of them before a following Extended_Pictographic. */ + + if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || + lgb != ucp_gbExtended_Pictographic) + lgb = rgb; + + cc++; + } + return cc; } +#endif + static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr) { DEFINE_COMPILER; @@ -6256,7 +7344,6 @@ compare_context context; struct sljit_jump *jump[3]; jump_list *end_list; #ifdef SUPPORT_UNICODE -struct sljit_label *label; PCRE2_UCHAR propdata[5]; #endif /* SUPPORT_UNICODE */ @@ -6274,7 +7361,7 @@ switch(type) #endif read_char8_type(common, type == OP_NOT_DIGIT); /* Flip the starting bit in the negative case. */ - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; @@ -6288,7 +7375,7 @@ switch(type) else #endif read_char8_type(common, type == OP_NOT_WHITESPACE); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; @@ -6302,7 +7389,7 @@ switch(type) else #endif read_char8_type(common, type == OP_NOT_WORDCHAR); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; @@ -6344,8 +7431,8 @@ switch(type) #elif PCRE2_CODE_UNIT_WIDTH == 16 jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); #endif @@ -6405,6 +7492,7 @@ switch(type) detect_partial_match(common, backtracks); read_char_range(common, 0x9, 0x3000, type == OP_NOT_HSPACE); add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; @@ -6414,6 +7502,7 @@ switch(type) detect_partial_match(common, backtracks); read_char_range(common, 0xa, 0x2029, type == OP_NOT_VSPACE); add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; @@ -6421,35 +7510,22 @@ switch(type) case OP_EXTUNI: if (check_str_ptr) detect_partial_match(common, backtracks); - read_char(common); - add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop)); - /* Optimize register allocation: use a real register. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); - OP1(SLJIT_MOV_U8, STACK_TOP, 0, SLJIT_MEM2(TMP1, TMP2), 3); - label = LABEL(); - jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); - OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); - read_char(common); - add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop)); - OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM2(TMP1, TMP2), 3); + SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); + OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); - OP2(SLJIT_SHL, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2); - OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable)); - OP1(SLJIT_MOV, STACK_TOP, 0, TMP2, 0); - OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); - JUMPTO(SLJIT_NOT_ZERO, label); +#if PCRE2_CODE_UNIT_WIDTH != 32 + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, + common->utf ? SLJIT_FUNC_OFFSET(do_extuni_utf) : SLJIT_FUNC_OFFSET(do_extuni_no_utf)); +#else + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_extuni_no_utf)); +#endif - OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); - JUMPHERE(jump[0]); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); if (common->mode == PCRE2_JIT_PARTIAL_HARD) { - jump[0] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); + jump[0] = CMP(SLJIT_LESS, SLJIT_RETURN_REG, 0, STR_END, 0); /* Since we successfully read a char above, partial matching must occure. */ check_partial(common, TRUE); JUMPHERE(jump[0]); @@ -6583,7 +7659,7 @@ switch(type) read_char_range(common, 0, 255, type == OP_NCLASS); #endif - if (check_class_ranges(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks)) + if (optimize_class(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks)) return cc + 32 / sizeof(PCRE2_UCHAR); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -6610,7 +7686,7 @@ switch(type) OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, backtracks, JUMP(SLJIT_ZERO)); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 @@ -6627,7 +7703,7 @@ switch(type) return cc + GET(cc, 0) - 1; #endif } -SLJIT_ASSERT_STOP(); +SLJIT_UNREACHABLE(); return cc; } @@ -6782,40 +7858,42 @@ else #if defined SUPPORT_UNICODE if (common->utf && *cc == OP_REFI) { - SLJIT_ASSERT(TMP1 == SLJIT_R0 && STACK_TOP == SLJIT_R1 && TMP2 == SLJIT_R2); + SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); if (ref) - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); else - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); if (withchecks) - jump = CMP(SLJIT_EQUAL, TMP1, 0, TMP2, 0); - - /* Needed to save important temporary registers. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); - OP1(SLJIT_MOV, SLJIT_R1, 0, ARGUMENTS, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), STR_PTR, 0); - sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp)); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_R2, 0); + /* No free saved registers so save data on stack. */ + + OP1(SLJIT_MOV, SLJIT_R3, 0, STR_END, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW) | SLJIT_ARG4(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); + if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1)); else { - add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); - nopartial = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_LESS, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); + + add_jump(compiler, backtracks, JUMP(SLJIT_LESS)); + + nopartial = JUMP(SLJIT_NOT_EQUAL); + OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0); check_partial(common, FALSE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(nopartial); } - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); } else #endif /* SUPPORT_UNICODE */ { if (ref) - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0); else - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); if (withchecks) jump = JUMP(SLJIT_ZERO); @@ -6906,7 +7984,7 @@ switch(type) cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE; break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -6920,7 +7998,7 @@ if (!minimize) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); /* Temporary release of STR_PTR. */ - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); /* Handles both invalid and empty cases. Since the minimum repeat, is zero the invalid case is basically the same as an empty case. */ if (ref) @@ -6933,7 +8011,7 @@ if (!minimize) zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); } /* Restore if not zero length. */ - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); } else { @@ -7097,8 +8175,10 @@ if (entry == NULL) if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; entry->next = NULL; - entry->entry = NULL; - entry->calls = NULL; + entry->entry_label = NULL; + entry->backtrack_label = NULL; + entry->entry_calls = NULL; + entry->backtrack_calls = NULL; entry->start = start; if (prev != NULL) @@ -7107,71 +8187,74 @@ if (entry == NULL) common->entries = entry; } -if (common->has_set_som && common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); - allocate_stack(common, 2); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); - } -else if (common->has_set_som || common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr); - allocate_stack(common, 1); - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); - } +BACKTRACK_AS(recurse_backtrack)->entry = entry; -if (entry->entry == NULL) - add_jump(compiler, &entry->calls, JUMP(SLJIT_FAST_CALL)); +if (entry->entry_label == NULL) + add_jump(compiler, &entry->entry_calls, JUMP(SLJIT_FAST_CALL)); else - JUMPTO(SLJIT_FAST_CALL, entry->entry); + JUMPTO(SLJIT_FAST_CALL, entry->entry_label); /* Leave if the match is failed. */ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0)); +BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL(); return cc + 1 + LINK_SIZE; } -static int SLJIT_CALL do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) +static sljit_s32 SLJIT_FUNC do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) { -PCRE2_SPTR begin = arguments->begin; -PCRE2_SIZE *ovector = arguments->match_data->ovector; -sljit_u32 oveccount = arguments->oveccount; -sljit_u32 i; +PCRE2_SPTR begin; +PCRE2_SIZE *ovector; +sljit_u32 oveccount, capture_top; if (arguments->callout == NULL) return 0; -callout_block->version = 1; +SLJIT_COMPILE_ASSERT(sizeof (PCRE2_SIZE) <= sizeof (sljit_sw), pcre2_size_must_be_lower_than_sljit_sw_size); + +begin = arguments->begin; +ovector = (PCRE2_SIZE*)(callout_block + 1); +oveccount = callout_block->capture_top; + +SLJIT_ASSERT(oveccount >= 1); + +callout_block->version = 2; +callout_block->callout_flags = 0; /* Offsets in subject. */ callout_block->subject_length = arguments->end - arguments->begin; -callout_block->start_match = (PCRE2_SPTR)callout_block->subject - arguments->begin; -callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - arguments->begin; +callout_block->start_match = jit_ovector[0] - begin; +callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - begin; callout_block->subject = begin; /* Convert and copy the JIT offset vector to the ovector array. */ -callout_block->capture_top = 0; +callout_block->capture_top = 1; callout_block->offset_vector = ovector; -for (i = 2; i < oveccount; i += 2) - { - ovector[i] = jit_ovector[i] - begin; - ovector[i + 1] = jit_ovector[i + 1] - begin; - if (jit_ovector[i] >= begin) - callout_block->capture_top = i; - } -callout_block->capture_top = (callout_block->capture_top >> 1) + 1; ovector[0] = PCRE2_UNSET; ovector[1] = PCRE2_UNSET; +ovector += 2; +jit_ovector += 2; +capture_top = 1; + +/* Convert pointers to sizes. */ +while (--oveccount != 0) + { + capture_top++; + + ovector[0] = (PCRE2_SIZE)(jit_ovector[0] - begin); + ovector[1] = (PCRE2_SIZE)(jit_ovector[1] - begin); + + if (ovector[0] != PCRE2_UNSET) + callout_block->capture_top = capture_top; + + ovector += 2; + jit_ovector += 2; + } + return (arguments->callout)(callout_block, arguments->callout_data); } -/* Aligning to 8 byte. */ -#define CALLOUT_ARG_SIZE \ - (((int)sizeof(pcre2_callout_block) + 7) & ~7) - #define CALLOUT_ARG_OFFSET(arg) \ - (-CALLOUT_ARG_SIZE + SLJIT_OFFSETOF(pcre2_callout_block, arg)) + SLJIT_OFFSETOF(pcre2_callout_block, arg) static SLJIT_INLINE PCRE2_SPTR compile_callout_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { @@ -7183,10 +8266,13 @@ unsigned int callout_length = (*cc == OP_CALLOUT) sljit_sw value1; sljit_sw value2; sljit_sw value3; +sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * sizeof(sljit_sw); PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); -allocate_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw)); +callout_arg_size = (sizeof(pcre2_callout_block) + callout_arg_size + sizeof(sljit_sw) - 1) / sizeof(sljit_sw); + +allocate_stack(common, callout_arg_size); SLJIT_ASSERT(common->capture_last_ptr != 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); @@ -7194,11 +8280,10 @@ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); value1 = (*cc == OP_CALLOUT) ? cc[1 + 2 * LINK_SIZE] : 0; OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, value1); OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0); +OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_top), SLJIT_IMM, common->re->top_bracket + 1); /* These pointer sized fields temporarly stores internal variables. */ -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(subject), TMP2, 0); if (common->mark_ptr != 0) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr)); @@ -7224,22 +8309,24 @@ OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_length) OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_offset), SLJIT_IMM, value3); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0); +SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); + /* Needed to save important temporary registers. */ -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); -OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_TOP, 0, SLJIT_IMM, CALLOUT_ARG_SIZE); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0); +/* SLJIT_R0 = arguments */ +OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0); GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START); -sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout)); -OP1(SLJIT_MOV_S32, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -free_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw)); +sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(S32) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout)); +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); +free_stack(common, callout_arg_size); /* Check return value. */ -OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); -add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER)); -if (common->forced_quit_label == NULL) - add_jump(compiler, &common->forced_quit, JUMP(SLJIT_SIG_LESS)); +OP2(SLJIT_SUB32 | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER32)); +if (common->abort_label == NULL) + add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL32) /* SIG_LESS */); else - JUMPTO(SLJIT_SIG_LESS, common->forced_quit_label); + JUMPTO(SLJIT_NOT_EQUAL32 /* SIG_LESS */, common->abort_label); return cc + callout_length; } @@ -7281,6 +8368,7 @@ static PCRE2_SPTR compile_assert_matchingpath(compiler_common *common, PCRE2_SPT DEFINE_COMPILER; int framesize; int extrasize; +BOOL local_quit_available = FALSE; BOOL needs_control_head; int private_data_ptr; backtrack_common altbacktrack; @@ -7291,13 +8379,13 @@ jump_list *tmp = NULL; jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks; jump_list **found; /* Saving previous accept variables. */ -BOOL save_local_exit = common->local_exit; -BOOL save_positive_assert = common->positive_assert; +BOOL save_local_quit_available = common->local_quit_available; +BOOL save_in_positive_assertion = common->in_positive_assertion; then_trap_backtrack *save_then_trap = common->then_trap; struct sljit_label *save_quit_label = common->quit_label; struct sljit_label *save_accept_label = common->accept_label; jump_list *save_quit = common->quit; -jump_list *save_positive_assert_quit = common->positive_assert_quit; +jump_list *save_positive_assertion_quit = common->positive_assertion_quit; jump_list *save_accept = common->accept; struct sljit_jump *jump; struct sljit_jump *brajump = NULL; @@ -7364,7 +8452,7 @@ else allocate_stack(common, framesize + extrasize); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); + OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); @@ -7379,21 +8467,21 @@ else else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); - init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize, FALSE); + init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize); } memset(&altbacktrack, 0, sizeof(backtrack_common)); -if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) +if (conditional || (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)) { - /* Negative assert is stronger than positive assert. */ - common->local_exit = TRUE; + /* Control verbs cannot escape from these asserts. */ + local_quit_available = TRUE; + common->local_quit_available = TRUE; common->quit_label = NULL; common->quit = NULL; - common->positive_assert = FALSE; } -else - common->positive_assert = TRUE; -common->positive_assert_quit = NULL; + +common->in_positive_assertion = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK); +common->positive_assertion_quit = NULL; while (1) { @@ -7409,16 +8497,16 @@ while (1) compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { - if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + if (local_quit_available) { - common->local_exit = save_local_exit; + common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } - common->positive_assert = save_positive_assert; + common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; - common->positive_assert_quit = save_positive_assert_quit; + common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return NULL; } @@ -7435,23 +8523,24 @@ while (1) free_stack(common, extrasize); if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } else { if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional) { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } else { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2)); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw)); } } @@ -7461,25 +8550,25 @@ while (1) if (conditional) { if (extrasize > 0) - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1)); } else if (bra == OP_BRAZERO) { if (framesize < 0) - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize)); else { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (framesize + extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else if (framesize >= 0) { /* For OP_BRA and OP_BRAMINZERO. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1)); } } add_jump(compiler, found, JUMP(SLJIT_JUMP)); @@ -7487,16 +8576,16 @@ while (1) compile_backtrackingpath(common, altbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { - if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + if (local_quit_available) { - common->local_exit = save_local_exit; + common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } - common->positive_assert = save_positive_assert; + common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; - common->positive_assert_quit = save_positive_assert_quit; + common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return NULL; } @@ -7509,26 +8598,26 @@ while (1) cc += GET(cc, 1); } -if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) +if (local_quit_available) { - SLJIT_ASSERT(common->positive_assert_quit == NULL); + SLJIT_ASSERT(common->positive_assertion_quit == NULL); /* Makes the check less complicated below. */ - common->positive_assert_quit = common->quit; + common->positive_assertion_quit = common->quit; } /* None of them matched. */ -if (common->positive_assert_quit != NULL) +if (common->positive_assertion_quit != NULL) { jump = JUMP(SLJIT_JUMP); - set_jumps(common->positive_assert_quit, LABEL()); + set_jumps(common->positive_assertion_quit, LABEL()); SLJIT_ASSERT(framesize != no_stack); if (framesize < 0) - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw)); else { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (extrasize + 1) * sizeof(sljit_sw)); } JUMPHERE(jump); } @@ -7577,18 +8666,18 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) { /* We know that STR_PTR was stored on the top of the stack. */ if (extrasize > 0) - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize)); /* Keep the STR_PTR on the top of the stack. */ if (bra == OP_BRAZERO) { - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); if (extrasize == 2) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } else if (bra == OP_BRAMINZERO) { - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } } @@ -7597,13 +8686,13 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) if (bra == OP_BRA) { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 2) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1)); } else { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw)); if (extrasize == 2) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); @@ -7631,7 +8720,9 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } set_jumps(backtrack->common.topbacktracks, LABEL()); } @@ -7684,16 +8775,16 @@ else } } -if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) +if (local_quit_available) { - common->local_exit = save_local_exit; + common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } -common->positive_assert = save_positive_assert; +common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; -common->positive_assert_quit = save_positive_assert_quit; +common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return cc + 1 + LINK_SIZE; } @@ -7718,23 +8809,23 @@ if (framesize < 0) } if (needs_control_head) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? sizeof(sljit_sw) : 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1)); /* TMP2 which is set here used by OP_KETRMAX below. */ if (ket == OP_KETRMAX) - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); else if (ket == OP_KETRMIN) { /* Move the STR_PTR to the private_data_ptr. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } } else { stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1; - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw)); if (needs_control_head) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); if (ket == OP_KETRMAX) { @@ -7821,7 +8912,6 @@ return stacksize; (|) OP_*BRA | OP_ALT ... M A (?()|) OP_*COND | OP_ALT M A (?>|) OP_ONCE | OP_ALT ... [stack trace] M A - (?>|) OP_ONCE_NC | OP_ALT ... [stack trace] M A Or nothing, if trace is unnecessary */ @@ -7889,8 +8979,6 @@ if (SLJIT_UNLIKELY(opcode == OP_COND || opcode == OP_SCOND)) if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) opcode = OP_SCOND; -if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) - opcode = OP_ONCE; if (opcode == OP_CBRA || opcode == OP_SCBRA) { @@ -7967,7 +9055,7 @@ if (bra == OP_BRAMINZERO) { /* Except when the whole stack frame must be saved. */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (BACKTRACK_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw)); + braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2)); } JUMPHERE(skip); } @@ -8040,7 +9128,7 @@ if (opcode == OP_ONCE) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame) - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw)); + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0); } else if (ket == OP_KETRMAX || has_alternatives) @@ -8058,7 +9146,7 @@ if (opcode == OP_ONCE) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); stacksize = needs_control_head ? 1 : 0; if (ket != OP_KET || has_alternatives) @@ -8073,7 +9161,7 @@ if (opcode == OP_ONCE) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); } - init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1, FALSE); + init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1); } } else if (opcode == OP_CBRA || opcode == OP_SCBRA) @@ -8130,13 +9218,13 @@ if (opcode == OP_COND || opcode == OP_SCOND) slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size; OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); slot += common->name_entry_size; i--; while (i-- > 0) { OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); - OP2(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, STR_PTR, 0); + OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0); slot += common->name_entry_size; } OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); @@ -8289,7 +9377,7 @@ if (ket == OP_KETRMAX) { if (has_alternatives) BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, rmax_label); /* Drop STR_PTR for greedy plus quantifier. */ if (opcode != OP_ONCE) @@ -8319,7 +9407,7 @@ if (ket == OP_KETRMAX) if (repeat_type == OP_EXACT) { count_match(common); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, rmax_label); } else if (repeat_type == OP_UPTO) @@ -8347,6 +9435,7 @@ if (bra == OP_BRAMINZERO) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (BACKTRACK_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw)); } else if (ket == OP_KETRMIN && opcode != OP_ONCE) free_stack(common, 1); @@ -8419,7 +9508,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -8497,7 +9586,7 @@ else OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, -STACK(stacksize - 1)); + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); stack = 0; if (!zero) @@ -8516,7 +9605,7 @@ else stack++; } OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0); - init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize, FALSE); + init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize); stack -= 1 + (offset == 0); } @@ -8569,7 +9658,7 @@ while (*cc != OP_KETRPOS) { if (offset != 0) { - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0); @@ -8580,10 +9669,10 @@ while (*cc != OP_KETRPOS) else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP2(SLJIT_ADD, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); if (opcode == OP_SBRAPOS) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw)); - OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw), STR_PTR, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0); } /* Even if the match is empty, we need to reset the control head. */ @@ -8629,7 +9718,7 @@ while (*cc != OP_KETRPOS) else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2)); } } @@ -8646,7 +9735,7 @@ if (!zero) if (framesize < 0) add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0)); else /* TMP2 is set to [private_data_ptr] above. */ - add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), (stacksize - 1) * sizeof(sljit_sw), SLJIT_IMM, 0)); + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0)); } /* None of them matched. */ @@ -8869,7 +9958,7 @@ if (exact > 1) OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact); label = LABEL(); compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -8877,7 +9966,7 @@ if (exact > 1) OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact); label = LABEL(); compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } } @@ -8907,7 +9996,7 @@ switch(opcode) if (opcode == OP_UPTO) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); jump = JUMP(SLJIT_ZERO); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0); } @@ -8969,7 +10058,7 @@ switch(opcode) label = LABEL(); if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO)); } compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); @@ -8989,7 +10078,7 @@ switch(opcode) OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &no_match, JUMP(SLJIT_ZERO)); } @@ -9016,7 +10105,7 @@ switch(opcode) if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -9045,7 +10134,7 @@ switch(opcode) if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -9071,7 +10160,7 @@ switch(opcode) compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } @@ -9158,7 +10247,7 @@ switch(opcode) label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_match, TRUE); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1); @@ -9169,7 +10258,7 @@ switch(opcode) label = LABEL(); detect_partial_match(common, &no_match); compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_char1_match, LABEL()); @@ -9187,7 +10276,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -9208,6 +10297,9 @@ if (*cc == OP_FAIL) return cc + 1; } +if (*cc == OP_ACCEPT && common->currententry == NULL && (common->re->overall_options & PCRE2_ENDANCHORED) != 0) + add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); + if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL || !common->might_be_empty) { /* No need to check notempty conditions. */ @@ -9224,9 +10316,9 @@ else CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label); OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options)); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_NOT_ZERO)); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); if (common->accept_label == NULL) add_jump(compiler, &common->accept, JUMP(SLJIT_ZERO)); else @@ -9266,7 +10358,8 @@ backtrack_common *backtrack; PCRE2_UCHAR opcode = *cc; PCRE2_SPTR ccend = cc + 1; -if (opcode == OP_PRUNE_ARG || opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG) +if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || + opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG) ccend += 2 + cc[1]; PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); @@ -9278,7 +10371,7 @@ if (opcode == OP_SKIP) return ccend; } -if (opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG) +if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2)); @@ -9310,7 +10403,7 @@ size = 3 + (size < 0 ? 0 : size); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); allocate_stack(common, size); if (size > 3) - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw)); + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw)); else OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start); @@ -9319,7 +10412,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0); size = BACKTRACK_AS(then_trap_backtrack)->framesize; if (size >= 0) - init_frame(common, cc, ccend, size - 1, 0, FALSE); + init_frame(common, cc, ccend, size - 1, 0); } static void compile_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent) @@ -9541,7 +10634,6 @@ while (cc < ccend) break; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -9598,6 +10690,7 @@ while (cc < ccend) case OP_THEN: case OP_THEN_ARG: case OP_COMMIT: + case OP_COMMIT_ARG: cc = compile_control_verb_matchingpath(common, cc, parent); break; @@ -9616,7 +10709,7 @@ while (cc < ccend) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return; } if (cc == NULL) @@ -9724,7 +10817,7 @@ switch(opcode) case OP_MINUPTO: OP1(SLJIT_MOV, TMP1, 0, base, offset1); OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO)); OP1(SLJIT_MOV, base, offset1, TMP1, 0); @@ -9770,7 +10863,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -9805,27 +10898,21 @@ free_stack(common, ref ? 2 : 3); static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; +recurse_entry *entry; -if (CURRENT_AS(recurse_backtrack)->inlined_pattern) - compile_backtrackingpath(common, current->top); -set_jumps(current->topbacktracks, LABEL()); -if (CURRENT_AS(recurse_backtrack)->inlined_pattern) - return; - -if (common->has_set_som && common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); - free_stack(common, 2); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), TMP2, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP1, 0); - } -else if (common->has_set_som || common->mark_ptr != 0) +if (!CURRENT_AS(recurse_backtrack)->inlined_pattern) { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); - free_stack(common, 1); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr, TMP2, 0); + entry = CURRENT_AS(recurse_backtrack)->entry; + if (entry->backtrack_label == NULL) + add_jump(compiler, &entry->backtrack_calls, JUMP(SLJIT_FAST_CALL)); + else + JUMPTO(SLJIT_FAST_CALL, entry->backtrack_label); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(recurse_backtrack)->matchingpath); } +else + compile_backtrackingpath(common, current->top); + +set_jumps(current->topbacktracks, LABEL()); } static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current) @@ -9878,7 +10965,9 @@ if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(assert_backtrack)->framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(assert_backtrack)->framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, TMP1, 0); set_jumps(current->topbacktracks, LABEL()); } @@ -9888,7 +10977,7 @@ else if (bra == OP_BRAZERO) { /* We know there is enough place on the stack. */ - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath); JUMPHERE(brajump); @@ -9948,8 +11037,6 @@ if (opcode == OP_CBRA || opcode == OP_SCBRA) offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1; if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) opcode = OP_SCOND; -if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) - opcode = OP_ONCE; alt_max = has_alternatives ? no_alternatives(ccbegin) : 0; @@ -10001,7 +11088,7 @@ else if (ket == OP_KETRMIN) else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (CURRENT_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw), CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath); } /* Drop STR_PTR for non-greedy plus quantifier. */ if (opcode != OP_ONCE) @@ -10055,6 +11142,7 @@ if (SLJIT_UNLIKELY(opcode == OP_ONCE)) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw)); } once = JUMP(SLJIT_JUMP); } @@ -10107,7 +11195,9 @@ if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0); } cond = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL()); @@ -10248,7 +11338,9 @@ if (has_alternatives) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0); } JUMPHERE(cond); } @@ -10303,7 +11395,7 @@ else if (opcode == OP_ONCE) JUMPHERE(once); /* Restore previous private_data_ptr */ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracket_backtrack)->u.framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1)); else if (ket == OP_KETRMIN) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); @@ -10384,6 +11476,7 @@ if (CURRENT_AS(bracketpos_backtrack)->framesize < 0) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracketpos_backtrack)->framesize - 1) * sizeof(sljit_sw)); if (current->topbacktracks) { @@ -10393,7 +11486,7 @@ if (current->topbacktracks) free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize); JUMPHERE(jump); } -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_backtrack)->framesize * sizeof(sljit_sw)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1)); } static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current) @@ -10439,22 +11532,23 @@ if (opcode == OP_THEN || opcode == OP_THEN_ARG) jump = JUMP(SLJIT_JUMP); loop = LABEL(); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), -(int)sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); JUMPHERE(jump); - CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(2 * sizeof(sljit_sw)), TMP1, 0, loop); - CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(3 * sizeof(sljit_sw)), TMP2, 0, loop); + CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop); + CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop); add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP)); return; } - else if (common->positive_assert) + else if (!common->local_quit_available && common->in_positive_assertion) { - add_jump(compiler, &common->positive_assert_quit, JUMP(SLJIT_JUMP)); + add_jump(compiler, &common->positive_assertion_quit, JUMP(SLJIT_JUMP)); return; } } -if (common->local_exit) +if (common->local_quit_available) { + /* Abort match with a fail. */ if (common->quit_label == NULL) add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); else @@ -10464,15 +11558,13 @@ if (common->local_exit) if (opcode == OP_SKIP_ARG) { - SLJIT_ASSERT(common->control_head_ptr != 0); + SLJIT_ASSERT(common->control_head_ptr != 0 && TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2)); - sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark)); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2)); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark)); - OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); - add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, -1)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_R0, 0); + add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0)); return; } @@ -10505,7 +11597,10 @@ jump = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL()); /* STACK_TOP is set by THEN. */ if (CURRENT_AS(then_trap_backtrack)->framesize >= 0) + { add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(then_trap_backtrack)->framesize - 1) * sizeof(sljit_sw)); + } OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 3); @@ -10622,7 +11717,6 @@ while (current) break; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -10671,7 +11765,8 @@ while (current) break; case OP_COMMIT: - if (!common->local_exit) + case OP_COMMIT_ARG: + if (!common->local_quit_available) OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); if (common->quit_label == NULL) add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); @@ -10693,7 +11788,7 @@ while (current) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } current = current->prev; @@ -10708,38 +11803,52 @@ PCRE2_SPTR cc = common->start + common->currententry->start; PCRE2_SPTR ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE); PCRE2_SPTR ccend = bracketend(cc) - (1 + LINK_SIZE); BOOL needs_control_head; -int framesize = get_framesize(common, cc, NULL, TRUE, &needs_control_head); -int private_data_size = get_private_data_copy_length(common, ccbegin, ccend, needs_control_head); -int alternativesize; -BOOL needs_frame; +BOOL has_quit; +BOOL has_accept; +int private_data_size = get_recurse_data_length(common, ccbegin, ccend, &needs_control_head, &has_quit, &has_accept); +int alt_count, alt_max, local_size; backtrack_common altbacktrack; -struct sljit_jump *jump; +jump_list *match = NULL; +sljit_uw *next_update_addr = NULL; +struct sljit_jump *alt1 = NULL; +struct sljit_jump *alt2 = NULL; +struct sljit_jump *accept_exit = NULL; +struct sljit_label *quit; /* Recurse captures then. */ common->then_trap = NULL; SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS); -needs_frame = framesize >= 0; -if (!needs_frame) - framesize = 0; -alternativesize = *(cc + GET(cc, 1)) == OP_ALT ? 1 : 0; -SLJIT_ASSERT(common->currententry->entry == NULL && common->recursive_head_ptr != 0); -common->currententry->entry = LABEL(); -set_jumps(common->currententry->calls, common->currententry->entry); +alt_max = no_alternatives(cc); +alt_count = 0; + +/* Matching path. */ +SLJIT_ASSERT(common->currententry->entry_label == NULL && common->recursive_head_ptr != 0); +common->currententry->entry_label = LABEL(); +set_jumps(common->currententry->entry_calls, common->currententry->entry_label); sljit_emit_fast_enter(compiler, TMP2, 0); count_match(common); -allocate_stack(common, private_data_size + framesize + alternativesize); -OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(private_data_size + framesize + alternativesize - 1), TMP2, 0); -copy_private_data(common, ccbegin, ccend, TRUE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head); + +local_size = (alt_max > 1) ? 2 : 1; + +/* (Reversed) stack layout: + [private data][return address][optional: str ptr] ... [optional: alternative index][recursive_head_ptr] */ + +allocate_stack(common, private_data_size + local_size); +/* Save return address. */ +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP2, 0); + +copy_recurse_data(common, ccbegin, ccend, recurse_copy_from_global, local_size, private_data_size + local_size, has_quit); + +/* This variable is saved and restored all time when we enter or exit from a recursive context. */ +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0); + if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0); -if (needs_frame) - init_frame(common, cc, NULL, framesize + alternativesize - 1, alternativesize, TRUE); -if (alternativesize > 0) +if (alt_max > 1) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); memset(&altbacktrack, 0, sizeof(backtrack_common)); @@ -10761,7 +11870,75 @@ while (1) if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return; - add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); + allocate_stack(common, (alt_max > 1 || has_accept) ? 2 : 1); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); + + if (alt_max > 1 || has_accept) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count); + + add_jump(compiler, &match, JUMP(SLJIT_JUMP)); + + if (alt_count == 0) + { + /* Backtracking path entry. */ + SLJIT_ASSERT(common->currententry->backtrack_label == NULL); + common->currententry->backtrack_label = LABEL(); + set_jumps(common->currententry->backtrack_calls, common->currententry->backtrack_label); + + sljit_emit_fast_enter(compiler, TMP1, 0); + + if (has_accept) + accept_exit = CMP(SLJIT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_max * sizeof (sljit_sw)); + + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + /* Save return address. */ + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(local_size - 1), TMP1, 0); + + copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit); + + if (alt_max > 1) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + free_stack(common, 2); + + if (alt_max > 4) + { + /* Table jump if alt_max is greater than 4. */ + next_update_addr = allocate_read_only_data(common, alt_max * sizeof(sljit_uw)); + if (SLJIT_UNLIKELY(next_update_addr == NULL)) + return; + sljit_emit_ijump(compiler, SLJIT_JUMP, SLJIT_MEM1(TMP1), (sljit_sw)next_update_addr); + add_label_addr(common, next_update_addr++); + } + else + { + if (alt_max == 4) + alt2 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_uw)); + alt1 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, sizeof(sljit_uw)); + } + } + else + free_stack(common, has_accept ? 2 : 1); + } + else if (alt_max > 4) + add_label_addr(common, next_update_addr++); + else + { + if (alt_count != 2 * sizeof(sljit_uw)) + { + JUMPHERE(alt1); + if (alt_max == 3 && alt_count == sizeof(sljit_uw)) + alt2 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_uw)); + } + else + { + JUMPHERE(alt2); + if (alt_max == 4) + alt1 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_uw)); + } + } + + alt_count += sizeof(sljit_uw); compile_backtrackingpath(common, altbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) @@ -10775,55 +11952,65 @@ while (1) cc += GET(cc, 1); } -/* None of them matched. */ -OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); -jump = JUMP(SLJIT_JUMP); +/* No alternative is matched. */ + +quit = LABEL(); + +copy_recurse_data(common, ccbegin, ccend, recurse_copy_private_to_global, local_size, private_data_size + local_size, has_quit); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1)); +free_stack(common, private_data_size + local_size); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +sljit_emit_fast_return(compiler, TMP2, 0); if (common->quit != NULL) { + SLJIT_ASSERT(has_quit); + set_jumps(common->quit, LABEL()); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); - if (needs_frame) - { - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - } - OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); - common->quit = NULL; - add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); + copy_recurse_data(common, ccbegin, ccend, recurse_copy_shared_to_global, local_size, private_data_size + local_size, has_quit); + JUMPTO(SLJIT_JUMP, quit); } -set_jumps(common->accept, LABEL()); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); -if (needs_frame) +if (has_accept) { - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - } -OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 1); + JUMPHERE(accept_exit); + free_stack(common, 2); -JUMPHERE(jump); -if (common->quit != NULL) - set_jumps(common->quit, LABEL()); -copy_private_data(common, ccbegin, ccend, FALSE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head); -free_stack(common, private_data_size + framesize + alternativesize); -if (needs_control_head) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 2 * sizeof(sljit_sw)); - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP1, 0); - OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0); + /* Save return address. */ + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP1, 0); + + copy_recurse_data(common, ccbegin, ccend, recurse_copy_kept_shared_to_global, local_size, private_data_size + local_size, has_quit); + + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1)); + free_stack(common, private_data_size + local_size); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); + sljit_emit_fast_return(compiler, TMP2, 0); } -else + +if (common->accept != NULL) { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw)); - OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP2, 0); + SLJIT_ASSERT(has_accept); + + set_jumps(common->accept, LABEL()); + + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); + OP1(SLJIT_MOV, TMP2, 0, STACK_TOP, 0); + + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count); } -sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), 0); + +set_jumps(match, LABEL()); + +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + +copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), STACK(local_size - 1)); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); +sljit_emit_fast_return(compiler, TMP2, 0); } #undef COMPILE_BACKTRACKINGPATH @@ -10855,11 +12042,13 @@ struct sljit_jump *jump; struct sljit_jump *minlength_check_failed = NULL; struct sljit_jump *reqbyte_notfound = NULL; struct sljit_jump *empty_match = NULL; +struct sljit_jump *end_anchor_failed = NULL; SLJIT_ASSERT(tables); memset(&rootbacktrack, 0, sizeof(backtrack_common)); memset(common, 0, sizeof(compiler_common)); +common->re = re; common->name_table = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)); rootbacktrack.cc = common->name_table + re->name_count * re->name_entry_size; @@ -11046,7 +12235,7 @@ if (!compiler) common->compiler = compiler; /* Main pcre_jit_exec entry. */ -sljit_emit_enter(compiler, 0, 1, 5, 5, 0, 0, private_data_size); +sljit_emit_enter(compiler, 0, SLJIT_ARG1(SW), 5, 5, 0, 0, private_data_size); /* Register init. */ reset_ovector(common, (re->top_bracket + 1) * 2); @@ -11059,8 +12248,8 @@ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)) OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match)); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, base)); -OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, limit)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, end)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, start)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0); @@ -11077,7 +12266,7 @@ if (common->control_head_ptr != 0) /* Main part of the matching */ if ((re->overall_options & PCRE2_ANCHORED) == 0) { - mainloop_label = mainloop_entry(common, (re->flags & PCRE2_HASCRORLF) != 0, re->overall_options); + mainloop_label = mainloop_entry(common); continue_match_label = LABEL(); /* Forward search if possible. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) @@ -11085,11 +12274,11 @@ if ((re->overall_options & PCRE2_ANCHORED) == 0) if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common)) ; else if ((re->flags & PCRE2_FIRSTSET) != 0) - fast_forward_first_char(common, (PCRE2_UCHAR)(re->first_codeunit), (re->flags & PCRE2_FIRSTCASELESS) != 0); + fast_forward_first_char(common); else if ((re->flags & PCRE2_STARTLINE) != 0) fast_forward_newline(common); else if ((re->flags & PCRE2_FIRSTMAPSET) != 0) - fast_forward_start_bits(common, re->start_bitmap); + fast_forward_start_bits(common); } } else @@ -11136,6 +12325,9 @@ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return PCRE2_ERROR_NOMEMORY; } +if ((re->overall_options & PCRE2_ENDANCHORED) != 0) + end_anchor_failed = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0); + if (common->might_be_empty) { empty_match = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); @@ -11148,15 +12340,26 @@ if (common->accept != NULL) /* This means we have a match. Update the ovector. */ copy_ovector(common, re->top_bracket + 1); -common->quit_label = common->forced_quit_label = LABEL(); +common->quit_label = common->abort_label = LABEL(); if (common->quit != NULL) set_jumps(common->quit, common->quit_label); -if (common->forced_quit != NULL) - set_jumps(common->forced_quit, common->forced_quit_label); +if (common->abort != NULL) + set_jumps(common->abort, common->abort_label); if (minlength_check_failed != NULL) - SET_LABEL(minlength_check_failed, common->forced_quit_label); + SET_LABEL(minlength_check_failed, common->abort_label); sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0); +if (common->failed_match != NULL) + { + SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); + set_jumps(common->failed_match, LABEL()); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); + JUMPTO(SLJIT_JUMP, common->abort_label); + } + +if ((re->overall_options & PCRE2_ENDANCHORED) != 0) + JUMPHERE(end_anchor_failed); + if (mode != PCRE2_JIT_COMPLETE) { common->partialmatchlabel = LABEL(); @@ -11237,9 +12440,9 @@ if (common->might_be_empty) JUMPHERE(empty_match); OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options)); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); JUMPTO(SLJIT_NOT_ZERO, empty_match_backtrack_label); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); JUMPTO(SLJIT_ZERO, empty_match_found_label); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label); @@ -11250,7 +12453,7 @@ common->fast_forward_bc_ptr = NULL; common->fast_fail_start_ptr = 0; common->fast_fail_end_ptr = 0; common->currententry = common->entries; -common->local_exit = TRUE; +common->local_quit_available = TRUE; quit_label = common->quit_label; while (common->currententry != NULL) { @@ -11267,7 +12470,7 @@ while (common->currententry != NULL) flush_stubs(common); common->currententry = common->currententry->next; } -common->local_exit = FALSE; +common->local_quit_available = FALSE; common->quit_label = quit_label; /* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */ @@ -11275,20 +12478,23 @@ common->quit_label = quit_label; set_jumps(common->stackalloc, LABEL()); /* RETURN_ADDR is not a saved register. */ sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0); -OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); -OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top), STACK_TOP, 0); -OP2(SLJIT_ADD, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE); -sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); -jump = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); -OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); -OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top)); -OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit)); -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); -sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); +SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); + +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0); +OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); +OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE); +OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0); + +sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); + +jump = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +OP1(SLJIT_MOV, TMP2, 0, STACK_LIMIT, 0); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_RETURN_REG, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); +sljit_emit_fast_return(compiler, TMP1, 0); /* Allocation failed. */ JUMPHERE(jump); diff --git a/ProcessHacker/pcre/pcre2_jit_match.c b/ProcessHacker/pcre/pcre2_jit_match.c index a323971ff3ea..5a66545bae34 100644 --- a/ProcessHacker/pcre/pcre2_jit_match.c +++ b/ProcessHacker/pcre/pcre2_jit_match.c @@ -49,10 +49,10 @@ static SLJIT_NOINLINE int jit_machine_stack_exec(jit_arguments *arguments, jit_f sljit_u8 local_space[MACHINE_STACK_SIZE]; struct sljit_stack local_stack; -local_stack.top = (sljit_sw)&local_space; -local_stack.base = local_stack.top; -local_stack.limit = local_stack.base + MACHINE_STACK_SIZE; -local_stack.max_limit = local_stack.limit; +local_stack.min_start = local_space; +local_stack.start = local_space; +local_stack.end = local_space + MACHINE_STACK_SIZE; +local_stack.top = local_space + MACHINE_STACK_SIZE; arguments->stack = &local_stack; return executable_func(arguments); } @@ -118,7 +118,7 @@ if ((options & PCRE2_PARTIAL_HARD) != 0) else if ((options & PCRE2_PARTIAL_SOFT) != 0) index = 1; -if (functions->executable_funcs[index] == NULL) +if (functions == NULL || functions->executable_funcs[index] == NULL) return PCRE2_ERROR_JIT_BADOPTION; /* Sanity checks should be handled by pcre_exec. */ diff --git a/ProcessHacker/pcre/pcre2_jit_test.c b/ProcessHacker/pcre/pcre2_jit_test.c index 705ba181eb3c..a28e9a0b183e 100644 --- a/ProcessHacker/pcre/pcre2_jit_test.c +++ b/ProcessHacker/pcre/pcre2_jit_test.c @@ -179,10 +179,12 @@ static struct regression_test_case regression_test_cases[] = { { PCRE2_CASELESS, 0, 0, 0, "\xff#a", "\xff#\xff\xfe##\xff#A" }, { PCRE2_CASELESS, 0, 0, 0, "\xfe", "\xff\xfc#\xfe\xfe" }, { PCRE2_CASELESS, 0, 0, 0, "a1", "Aa1" }, +#ifndef NEVER_BACKSLASH_C { M, A, 0, 0, "\\Ca", "cda" }, { CM, A, 0, 0, "\\Ca", "CDA" }, { M, A, 0, 0 | F_NOMATCH, "\\Cx", "cda" }, { CM, A, 0, 0 | F_NOMATCH, "\\Cx", "CDA" }, +#endif { CMUP, A, 0, 0, "\xf0\x90\x90\x80\xf0\x90\x90\xa8", "\xf0\x90\x90\xa8\xf0\x90\x90\x80" }, { CMUP, A, 0, 0, "\xf0\x90\x90\x80{2}", "\xf0\x90\x90\x80#\xf0\x90\x90\xa8\xf0\x90\x90\x80" }, { CMUP, A, 0, 0, "\xf0\x90\x90\xa8{2}", "\xf0\x90\x90\x80#\xf0\x90\x90\xa8\xf0\x90\x90\x80" }, @@ -258,6 +260,8 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0, "=\xc7\x82|#\xc6\x82", "\xf1\x83\x82\x82=\xc7\x82\xc7\x83" }, { MU, A, 0, 0, "\xc7\x82\xc7\x83|\xc6\x82\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" }, { MU, A, 0, 0, "\xc6\x82\xc6\x82|\xc7\x83\xc7\x83|\xc8\x84\xc8\x84", "\xf1\x83\x82\x82\xc8\x84\xc8\x84" }, + { U, A, 0, 0, "\xe1\x81\x80|\xe2\x82\x80|\xe4\x84\x80", "\xdf\xbf\xc2\x80\xe4\x84\x80" }, + { U, A, 0, 0, "(?:\xe1\x81\x80|\xe2\x82\x80|\xe4\x84\x80)#", "\xdf\xbf\xc2\x80#\xe4\x84\x80#" }, /* Greedy and non-greedy ? operators. */ { MU, A, 0, 0, "(?:a)?a", "laab" }, @@ -707,7 +711,7 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0, "(?1)(((a(*ACCEPT)))b)", "axaa" }, { MU, A, 0, 0, "(?1)(?(DEFINE) (((ac(*ACCEPT)))b) )", "akaac" }, { MU, A, 0, 0, "(a+)b(?1)b\\1", "abaaabaaaaa" }, - { MU, A, 0, 0 | F_NOMATCH, "(?(DEFINE)(aa|a))(?1)ab", "aab" }, + { MU, A, 0, 0, "(?(DEFINE)(aa|a))(?1)ab", "aab" }, { MU, A, 0, 0, "(?(DEFINE)(a\\Kb))(?1)+ababc", "abababxabababc" }, { MU, A, 0, 0, "(a\\Kb)(?1)+ababc", "abababxababababc" }, { MU, A, 0, 0 | F_NOMATCH, "(a\\Kb)(?1)+ababc", "abababxababababxc" }, @@ -724,6 +728,8 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0, "((?:(?(R)a|(?1))){3})", "XaaaaaaaaaX" }, { MU, A, 0, 0, "((?(R)a|(?1)){1,3})aaaaaa", "aaaaaaaaXaaaaaaaaa" }, { MU, A, 0, 0, "((?(R)a|(?1)){1,3}?)M", "aaaM" }, + { MU, A, 0, 0, "((.)(?:.|\\2(?1))){0}#(?1)#", "#aabbccdde# #aabbccddee#" }, + { MU, A, 0, 0, "((.)(?:\\2|\\2{4}b)){0}#(?:(?1))+#", "#aaaab# #aaaaab#" }, /* 16 bit specific tests. */ { CM, A, 0, 0 | F_FORCECONV, "\xc3\xa1", "\xc3\x81\xc3\xa1" }, @@ -842,13 +848,23 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0 | F_NOMATCH, "(?(?=a)a(*THEN)b|ad)", "ad" }, { MU, A, 0, 0, "(?!(?(?=a)ab|b(*THEN)d))bn|bnn", "bnn" }, + /* Recurse and control verbs. */ + { MU, A, 0, 0, "(a(*ACCEPT)b){0}a(?1)b", "aacaabb" }, + { MU, A, 0, 0, "((a)\\2(*ACCEPT)b){0}a(?1)b", "aaacaaabb" }, + { MU, A, 0, 0, "((ab|a(*ACCEPT)x)+|ababababax){0}_(?1)_", "_ababababax_ _ababababa_" }, + { MU, A, 0, 0, "((.)(?:A(*ACCEPT)|(?1)\\2)){0}_(?1)_", "_bcdaAdcb_bcdaAdcb_" }, + { MU, A, 0, 0, "((*MARK:m)(?:a|a(*COMMIT)b|aa)){0}_(?1)_", "_ab_" }, + { MU, A, 0, 0, "((*MARK:m)(?:a|a(*COMMIT)b|aa)){0}_(?1)_|(_aa_)", "_aa_" }, + { MU, A, 0, 0, "(a(*COMMIT)(?:b|bb)|c(*ACCEPT)d|dd){0}_(?1)+_", "_ax_ _cd_ _abbb_ _abcd_ _abbcdd_" }, + { MU, A, 0, 0, "((.)(?:.|(*COMMIT)\\2{3}(*ACCEPT).*|.*)){0}_(?1){0,4}_", "_aaaabbbbccccddd_ _aaaabbbbccccdddd_" }, + /* Deep recursion. */ { MU, A, 0, 0, "((((?:(?:(?:\\w)+)?)*|(?>\\w)+?)+|(?>\\w)?\?)*)?\\s", "aaaaa+ " }, { MU, A, 0, 0, "(?:((?:(?:(?:\\w*?)+)??|(?>\\w)?|\\w*+)*)+)+?\\s", "aa+ " }, { MU, A, 0, 0, "((a?)+)+b", "aaaaaaaaaaaa b" }, /* Deep recursion: Stack limit reached. */ - { M, A, 0, 0 | F_NOMATCH, "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaa" }, + { M, A, 0, 0 | F_NOMATCH, "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaa" }, { M, A, 0, 0 | F_NOMATCH, "(?:a+)+b", "aaaaaaaaaaaaaaaaaaaaaaaa b" }, { M, A, 0, 0 | F_NOMATCH, "(?:a+?)+?b", "aaaaaaaaaaaaaaaaaaaaaaaa b" }, { M, A, 0, 0 | F_NOMATCH, "(?:a*)*b", "aaaaaaaaaaaaaaaaaaaaaaaa b" }, @@ -1309,14 +1325,15 @@ static int regression_tests(void) } else { ovector8_1 = pcre2_get_ovector_pointer_8(mdata8_1); ovector8_2 = pcre2_get_ovector_pointer_8(mdata8_2); - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector8_1[i] = -2; - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector8_2[i] = -2; } if (re8) { + (void)pcre2_set_match_limit_8(mcontext8, 10000000); return_value8[1] = pcre2_match_8(re8, (PCRE2_SPTR8)current->input, strlen(current->input), - current->start_offset & OFFSET_MASK, current->match_options, mdata8_2, NULL); + current->start_offset & OFFSET_MASK, current->match_options, mdata8_2, mcontext8); if (pcre2_jit_compile_8(re8, jit_compile_mode)) { printf("\n8 bit: JIT compiler does not support \"%s\"\n", current->pattern); @@ -1348,9 +1365,9 @@ static int regression_tests(void) } else { ovector16_1 = pcre2_get_ovector_pointer_16(mdata16_1); ovector16_2 = pcre2_get_ovector_pointer_16(mdata16_2); - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector16_1[i] = -2; - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector16_2[i] = -2; } if (re16) { @@ -1359,8 +1376,9 @@ static int regression_tests(void) else length16 = copy_char8_to_char16((PCRE2_SPTR8)current->input, regtest_buf16, REGTEST_MAX_LENGTH16); + (void)pcre2_set_match_limit_16(mcontext16, 10000000); return_value16[1] = pcre2_match_16(re16, regtest_buf16, length16, - current->start_offset & OFFSET_MASK, current->match_options, mdata16_2, NULL); + current->start_offset & OFFSET_MASK, current->match_options, mdata16_2, mcontext16); if (pcre2_jit_compile_16(re16, jit_compile_mode)) { printf("\n16 bit: JIT compiler does not support \"%s\"\n", current->pattern); @@ -1392,9 +1410,9 @@ static int regression_tests(void) } else { ovector32_1 = pcre2_get_ovector_pointer_32(mdata32_1); ovector32_2 = pcre2_get_ovector_pointer_32(mdata32_2); - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector32_1[i] = -2; - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector32_2[i] = -2; } if (re32) { @@ -1403,8 +1421,9 @@ static int regression_tests(void) else length32 = copy_char8_to_char32((PCRE2_SPTR8)current->input, regtest_buf32, REGTEST_MAX_LENGTH32); + (void)pcre2_set_match_limit_32(mcontext32, 10000000); return_value32[1] = pcre2_match_32(re32, regtest_buf32, length32, - current->start_offset & OFFSET_MASK, current->match_options, mdata32_2, NULL); + current->start_offset & OFFSET_MASK, current->match_options, mdata32_2, mcontext32); if (pcre2_jit_compile_32(re32, jit_compile_mode)) { printf("\n32 bit: JIT compiler does not support \"%s\"\n", current->pattern); diff --git a/ProcessHacker/pcre/pcre2_maketables.c b/ProcessHacker/pcre/pcre2_maketables.c index dbabdb246c30..342248340823 100644 --- a/ProcessHacker/pcre/pcre2_maketables.c +++ b/ProcessHacker/pcre/pcre2_maketables.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,13 +38,13 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ +#include /* This module contains the external function pcre2_maketables(), which builds character tables for PCRE2 in the current locale. The file is compiled on its own as part of the PCRE2 library. However, it is also included in the compilation of dftables.c, in which case the macro DFTABLES is defined. */ -#define HAVE_CONFIG_H #ifndef DFTABLES # ifdef HAVE_CONFIG_H # include "config.h" @@ -82,7 +82,7 @@ pcre2_maketables(pcre2_general_context *gcontext) { uint8_t *yield = (uint8_t *)((gcontext != NULL)? gcontext->memctl.malloc(tables_length, gcontext->memctl.memory_data) : - malloc(tables_length)); + PhAllocateSafe(tables_length)); #endif /* DFTABLES */ int i; @@ -142,13 +142,6 @@ for (i = 0; i < 256; i++) if (isdigit(i)) x += ctype_digit; if (isxdigit(i)) x += ctype_xdigit; if (isalnum(i) || i == '_') x += ctype_word; - - /* Note: strchr includes the terminating zero in the characters it considers. - In this instance, that is ok because we want binary zero to be flagged as a - meta-character, which in this sense is any character that terminates a run - of data characters. */ - - if (strchr("\\*+?{^.$|()[", i) != 0) x += ctype_meta; *p++ = x; } diff --git a/ProcessHacker/pcre/pcre2_match.c b/ProcessHacker/pcre/pcre2_match.c index 9a8a472e7361..8741e1432d61 100644 --- a/ProcessHacker/pcre/pcre2_match.c +++ b/ProcessHacker/pcre/pcre2_match.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2015-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,26 +38,36 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -// dmex: Disable warnings -#pragma warning(push) -#pragma warning(disable : 4267) -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif -#define NLBLOCK mb /* Block containing newline information */ -#define PSSTART start_subject /* Field containing processed string start */ -#define PSEND end_subject /* Field containing processed string end */ +/* These defines enable debugging code */ + +/* #define DEBUG_FRAMES_DISPLAY */ +/* #define DEBUG_SHOW_OPS */ +/* #define DEBUG_SHOW_RMATCH */ + +#ifdef DEBUG_FRAME_DISPLAY +#include +#endif + +/* These defines identify the name of the block containing "static" +information, and fields within it. */ + +#define NLBLOCK mb /* Block containing newline information */ +#define PSSTART start_subject /* Field containing processed string start */ +#define PSEND end_subject /* Field containing processed string end */ #include "pcre2_internal.h" -/* Masks for identifying the public options that are permitted at match time. -*/ +#define RECURSE_UNSET 0xffffffffu /* Bigger than max group number */ + +/* Masks for identifying the public options that are permitted at match time. */ #define PUBLIC_MATCH_OPTIONS \ - (PCRE2_ANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ + (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT) @@ -65,60 +75,255 @@ POSSIBILITY OF SUCH DAMAGE. (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\ PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD) -/* The mb->capture_last field uses the lower 16 bits for the last captured -substring (which can never be greater than 65535) and a bit in the top half -to mean "capture vector overflowed". This odd way of doing things was -implemented when it was realized that preserving and restoring the overflow bit -whenever the last capture number was saved/restored made for a neater -interface, and doing it this way saved on (a) another variable, which would -have increased the stack frame size (a big NO-NO in PCRE) and (b) another -separate set of save/restore instructions. The following defines are used in -implementing this. */ - -#define CAPLMASK 0x0000ffff /* The bits used for last_capture */ -#define OVFLMASK 0xffff0000 /* The bits used for the overflow flag */ -#define OVFLBIT 0x00010000 /* The bit that is set for overflow */ - -/* Bits for setting in mb->match_function_type to indicate two special types -of call to match(). We do it this way to save on using another stack variable, -as stack usage is to be discouraged. */ - -#define MATCH_CONDASSERT 1 /* Called to check a condition assertion */ -#define MATCH_CBEGROUP 2 /* Could-be-empty unlimited repeat group */ - -/* Non-error returns from the match() function. Error returns are externally -defined PCRE2_ERROR_xxx codes, which are all negative. */ +/* Non-error returns from and within the match() function. Error returns are +externally defined PCRE2_ERROR_xxx codes, which are all negative. */ #define MATCH_MATCH 1 #define MATCH_NOMATCH 0 -/* Special internal returns from the match() function. Make them sufficiently -negative to avoid the external error codes. */ +/* Special internal returns used in the match() function. Make them +sufficiently negative to avoid the external error codes. */ #define MATCH_ACCEPT (-999) #define MATCH_KETRPOS (-998) -#define MATCH_ONCE (-997) /* The next 5 must be kept together and in sequence so that a test that checks for any one of them can use a range. */ -#define MATCH_COMMIT (-996) -#define MATCH_PRUNE (-995) -#define MATCH_SKIP (-994) -#define MATCH_SKIP_ARG (-993) -#define MATCH_THEN (-992) +#define MATCH_COMMIT (-997) +#define MATCH_PRUNE (-996) +#define MATCH_SKIP (-995) +#define MATCH_SKIP_ARG (-994) +#define MATCH_THEN (-993) #define MATCH_BACKTRACK_MAX MATCH_THEN #define MATCH_BACKTRACK_MIN MATCH_COMMIT -/* Min and max values for the common repeats; for the maxima, 0 => infinity */ +/* Group frame type values. Zero means the frame is not a group frame. The +lower 16 bits are used for data (e.g. the capture number). Group frames are +used for most groups so that information about the start is easily available at +the end without having to scan back through intermediate frames (backtrack +points). */ + +#define GF_CAPTURE 0x00010000u +#define GF_NOCAPTURE 0x00020000u +#define GF_CONDASSERT 0x00030000u +#define GF_RECURSE 0x00040000u + +/* Masks for the identity and data parts of the group frame type. */ + +#define GF_IDMASK(a) ((a) & 0xffff0000u) +#define GF_DATAMASK(a) ((a) & 0x0000ffffu) + +/* Repetition types */ + +enum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS }; + +/* Min and max values for the common repeats; a maximum of UINT32_MAX => +infinity. */ + +static const uint32_t rep_min[] = { + 0, 0, /* * and *? */ + 1, 1, /* + and +? */ + 0, 0, /* ? and ?? */ + 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */ + 0, 1, 0 }; /* OP_CRPOS{STAR, PLUS, QUERY} */ + +static const uint32_t rep_max[] = { + UINT32_MAX, UINT32_MAX, /* * and *? */ + UINT32_MAX, UINT32_MAX, /* + and +? */ + 1, 1, /* ? and ?? */ + 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */ + UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */ + +/* Repetition types - must include OP_CRPOSRANGE (not needed above) */ + +static const uint32_t rep_typ[] = { + REPTYPE_MAX, REPTYPE_MIN, /* * and *? */ + REPTYPE_MAX, REPTYPE_MIN, /* + and +? */ + REPTYPE_MAX, REPTYPE_MIN, /* ? and ?? */ + REPTYPE_MAX, REPTYPE_MIN, /* OP_CRRANGE and OP_CRMINRANGE */ + REPTYPE_POS, REPTYPE_POS, /* OP_CRPOSSTAR, OP_CRPOSPLUS */ + REPTYPE_POS, REPTYPE_POS }; /* OP_CRPOSQUERY, OP_CRPOSRANGE */ + +/* Numbers for RMATCH calls at backtracking points. When these lists are +changed, the code at RETURN_SWITCH below must be updated in sync. */ + +enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, + RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, + RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, + RM31, RM32, RM33, RM34, RM35, RM36 }; + +#ifdef SUPPORT_WIDE_CHARS +enum { RM100=100, RM101 }; +#endif + +#ifdef SUPPORT_UNICODE +enum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207, + RM208, RM209, RM210, RM211, RM212, RM213, RM214, RM215, + RM216, RM217, RM218, RM219, RM220, RM221, RM222 }; +#endif + +/* Define short names for general fields in the current backtrack frame, which +is always pointed to by the F variable. Occasional references to fields in +other frames are written out explicitly. There are also some fields in the +current frame whose names start with "temp" that are used for short-term, +localised backtracking memory. These are #defined with Lxxx names at the point +of use and undefined afterwards. */ + +#define Fback_frame F->back_frame +#define Fcapture_last F->capture_last +#define Fcurrent_recurse F->current_recurse +#define Fecode F->ecode +#define Feptr F->eptr +#define Fgroup_frame_type F->group_frame_type +#define Flast_group_offset F->last_group_offset +#define Flength F->length +#define Fmark F->mark +#define Frdepth F->rdepth +#define Fstart_match F->start_match +#define Foffset_top F->offset_top +#define Foccu F->occu +#define Fop F->op +#define Fovector F->ovector +#define Freturn_id F->return_id + + +#ifdef DEBUG_FRAMES_DISPLAY +/************************************************* +* Display current frames and contents * +*************************************************/ + +/* This debugging function displays the current set of frames and their +contents. It is not called automatically from anywhere, the intention being +that calls can be inserted where necessary when debugging frame-related +problems. + +Arguments: + f the file to write to + F the current top frame + P a previous frame of interest + frame_size the frame size + mb points to the match block + s identification text + +Returns: nothing +*/ + +static void +display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size, + match_block *mb, const char *s, ...) +{ +uint32_t i; +heapframe *Q; +va_list ap; +va_start(ap, s); + +fprintf(f, "FRAMES "); +vfprintf(f, s, ap); +va_end(ap); + +if (P != NULL) fprintf(f, " P=%lu", + ((char *)P - (char *)(mb->match_frames))/frame_size); +fprintf(f, "\n"); + +for (i = 0, Q = mb->match_frames; + Q <= F; + i++, Q = (heapframe *)((char *)Q + frame_size)) + { + fprintf(f, "Frame %d type=%x subj=%lu code=%d back=%lu id=%d", + i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode), + Q->back_frame, Q->return_id); + + if (Q->last_group_offset == PCRE2_UNSET) + fprintf(f, " lgoffset=unset\n"); + else + fprintf(f, " lgoffset=%lu\n", Q->last_group_offset/frame_size); + } +} + +#endif + + + +/************************************************* +* Process a callout * +*************************************************/ + +/* This function is called for all callouts, whether "standalone" or at the +start of a conditional group. Feptr will be pointing to either OP_CALLOUT or +OP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized +with fixed values. + +Arguments: + F points to the current backtracking frame + mb points to the match block + lengthptr where to return the length of the callout item -static const char rep_min[] = { 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, }; -static const char rep_max[] = { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, }; +Returns: the return from the callout + or 0 if no callout function exists +*/ -/* Maximum number of ovector elements that can be saved on the system stack -when processing OP_RECURSE in non-HEAP_MATCH_RECURSE mode. If the ovector is -bigger, malloc() is used. This value should be a multiple of 3, because the -ovector length is always a multiple of 3. */ +static int +do_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr) +{ +int rc; +PCRE2_SIZE save0, save1; +PCRE2_SIZE *callout_ovector; +pcre2_callout_block *cb; + +*lengthptr = (*Fecode == OP_CALLOUT)? + PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE); + +if (mb->callout == NULL) return 0; /* No callout function provided */ + +/* The original matching code (pre 10.30) worked directly with the ovector +passed by the user, and this was passed to callouts. Now that the working +ovector is in the backtracking frame, it no longer needs to reserve space for +the overall match offsets (which would waste space in the frame). For backward +compatibility, however, we pass capture_top and offset_vector to the callout as +if for the extended ovector, and we ensure that the first two slots are unset +by preserving and restoring their current contents. Picky compilers complain if +references such as Fovector[-2] are use directly, so we set up a separate +pointer. */ + +callout_ovector = (PCRE2_SIZE *)(Fovector) - 2; + +/* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields +are set externally. The first 3 never change; the last is updated for each +bumpalong. */ + +cb = mb->cb; +cb->capture_top = (uint32_t)Foffset_top/2 + 1; +cb->capture_last = Fcapture_last; +cb->offset_vector = callout_ovector; +cb->mark = mb->nomatch_mark; +cb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject); +cb->pattern_position = GET(Fecode, 1); +cb->next_item_length = GET(Fecode, 1 + LINK_SIZE); + +if (*Fecode == OP_CALLOUT) /* Numerical callout */ + { + cb->callout_number = Fecode[1 + 2*LINK_SIZE]; + cb->callout_string_offset = 0; + cb->callout_string = NULL; + cb->callout_string_length = 0; + } +else /* String callout */ + { + cb->callout_number = 0; + cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE); + cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1; + cb->callout_string_length = + *lengthptr - (1 + 4*LINK_SIZE) - 2; + } -#define OP_RECURSE_STACK_SAVE_MAX 45 +save0 = callout_ovector[0]; +save1 = callout_ovector[1]; +callout_ovector[0] = callout_ovector[1] = PCRE2_UNSET; +rc = mb->callout(cb, mb->callout_data); +callout_ovector[0] = save0; +callout_ovector[1] = save1; +cb->callout_flags = 0; +return rc; +} @@ -134,10 +339,9 @@ seems unlikely.) Arguments: offset index into the offset vector - offset_top top of the used offset vector - eptr pointer into the subject - mb points to match block caseless TRUE if caseless + F the current backtracking frame pointer + mb points to match block lengthptr pointer for returning the length matched Returns: = 0 sucessful match; number of code units matched is set @@ -146,21 +350,18 @@ Returns: = 0 sucessful match; number of code units matched is set */ static int -match_ref(PCRE2_SIZE offset, PCRE2_SIZE offset_top, register PCRE2_SPTR eptr, - match_block *mb, BOOL caseless, PCRE2_SIZE *lengthptr) +match_ref(PCRE2_SIZE offset, BOOL caseless, heapframe *F, match_block *mb, + PCRE2_SIZE *lengthptr) { -#if defined SUPPORT_UNICODE -BOOL utf = (mb->poptions & PCRE2_UTF) != 0; -#endif - -register PCRE2_SPTR p; +PCRE2_SPTR p; PCRE2_SIZE length; -PCRE2_SPTR eptr_start = eptr; +PCRE2_SPTR eptr; +PCRE2_SPTR eptr_start; /* Deal with an unset group. The default is no match, but there is an option to match an empty string. */ -if (offset >= offset_top || mb->ovector[offset] == PCRE2_UNSET) +if (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET) { if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) { @@ -172,19 +373,20 @@ if (offset >= offset_top || mb->ovector[offset] == PCRE2_UNSET) /* Separate the caseless and UTF cases for speed. */ -p = mb->start_subject + mb->ovector[offset]; -length = mb->ovector[offset+1] - mb->ovector[offset]; +eptr = eptr_start = Feptr; +p = mb->start_subject + Fovector[offset]; +length = Fovector[offset+1] - Fovector[offset]; if (caseless) { #if defined SUPPORT_UNICODE - if (utf) + if ((mb->poptions & PCRE2_UTF) != 0) { /* Match characters up to the end of the reference. NOTE: the number of code units matched may differ, because in UTF-8 there are some characters - whose upper and lower case versions code have different numbers of bytes. - For example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 - (3 bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a + whose upper and lower case codes have different numbers of bytes. For + example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3 + bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a sequence of two of the latter. It is important, therefore, to check the length along the reference, not along the subject (earlier code did this wrong). */ @@ -230,14 +432,26 @@ if (caseless) } /* In the caseful case, we can just compare the code units, whether or not we -are in UTF mode. */ +are in UTF mode. When partial matching, we have to do this unit-by-unit. */ else { - for (; length > 0; length--) + if (mb->partial != 0) + { + for (; length > 0; length--) + { + if (eptr >= mb->end_subject) return 1; /* Partial match */ + if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /* No match */ + } + } + + /* Not partial matching */ + + else { - if (eptr >= mb->end_subject) return 1; /* Partial match */ - if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /*No match */ + if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */ + if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1; /* No match */ + eptr += length; } } @@ -247,281 +461,73 @@ return 0; /* Match */ -/*************************************************************************** -**************************************************************************** - RECURSION IN THE match() FUNCTION - -The match() function is highly recursive, though not every recursive call -increases the recursion depth. Nevertheless, some regular expressions can cause -it to recurse to a great depth. I was writing for Unix, so I just let it call -itself recursively. This uses the stack for saving everything that has to be -saved for a recursive call. On Unix, the stack can be large, and this works -fine. - -It turns out that on some non-Unix-like systems there are problems with -programs that use a lot of stack. (This despite the fact that every last chip -has oodles of memory these days, and techniques for extending the stack have -been known for decades.) So.... - -There is a fudge, triggered by defining HEAP_MATCH_RECURSE, which avoids -recursive calls by keeping local variables that need to be preserved in blocks -of memory on the heap instead instead of on the stack. Macros are used to -achieve this so that the actual code doesn't look very different to what it -always used to. - -The original heap-recursive code used longjmp(). However, it seems that this -can be very slow on some operating systems. Following a suggestion from Stan -Switzer, the use of longjmp() has been abolished, at the cost of having to -provide a unique number for each call to RMATCH. There is no way of generating -a sequence of numbers at compile time in C. I have given them names, to make -them stand out more clearly. - -Crude tests on x86 Linux show a small speedup of around 5-8%. However, on -FreeBSD, avoiding longjmp() more than halves the time taken to run the standard -tests. Furthermore, not using longjmp() means that local dynamic variables -don't have indeterminate values; this has meant that the frame size can be -reduced because the result can be "passed back" by straight setting of the -variable instead of being passed in the frame. -**************************************************************************** -***************************************************************************/ - -/* Numbers for RMATCH calls. When this list is changed, the code at HEAP_RETURN -below must be updated in sync. */ - -enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, - RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, - RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, - RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40, - RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50, - RM51, RM52, RM53, RM54, RM55, RM56, RM57, RM58, RM59, RM60, - RM61, RM62, RM63, RM64, RM65, RM66, RM67, RM68 }; - -/* These versions of the macros use the stack, as normal. Note that the "rw" -argument of RMATCH isn't actually used in this definition. */ - -#ifndef HEAP_MATCH_RECURSE -#define REGISTER register -#define RMATCH(ra,rb,rc,rd,re,rw) \ - rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1) -#define RRETURN(ra) return ra -#else - -/* These versions of the macros manage a private stack on the heap. Note that -the "rd" argument of RMATCH isn't actually used in this definition. It's the mb -argument of match(), which never changes. */ - -#define REGISTER - -#define RMATCH(ra,rb,rc,rd,re,rw)\ - {\ - heapframe *newframe = frame->Xnextframe;\ - if (newframe == NULL)\ - {\ - newframe = (heapframe *)(mb->stack_memctl.malloc)\ - (sizeof(heapframe), mb->stack_memctl.memory_data);\ - if (newframe == NULL) RRETURN(PCRE2_ERROR_NOMEMORY);\ - newframe->Xnextframe = NULL;\ - frame->Xnextframe = newframe;\ - }\ - frame->Xwhere = rw;\ - newframe->Xeptr = ra;\ - newframe->Xecode = rb;\ - newframe->Xmstart = mstart;\ - newframe->Xoffset_top = rc;\ - newframe->Xeptrb = re;\ - newframe->Xrdepth = frame->Xrdepth + 1;\ - newframe->Xprevframe = frame;\ - frame = newframe;\ - goto HEAP_RECURSE;\ - L_##rw:;\ - } - -#define RRETURN(ra)\ - {\ - heapframe *oldframe = frame;\ - frame = oldframe->Xprevframe;\ - if (frame != NULL)\ - {\ - rrc = ra;\ - goto HEAP_RETURN;\ - }\ - return ra;\ - } - - -/* Structure for remembering the local variables in a private frame. Arrange it -so as to minimize the number of holes. */ - -typedef struct heapframe { - struct heapframe *Xprevframe; - struct heapframe *Xnextframe; - -#ifdef SUPPORT_UNICODE - PCRE2_SPTR Xcharptr; -#endif - PCRE2_SPTR Xeptr; - PCRE2_SPTR Xecode; - PCRE2_SPTR Xmstart; - PCRE2_SPTR Xcallpat; - PCRE2_SPTR Xdata; - PCRE2_SPTR Xnext_ecode; - PCRE2_SPTR Xpp; - PCRE2_SPTR Xprev; - PCRE2_SPTR Xsaved_eptr; - - eptrblock *Xeptrb; - - PCRE2_SIZE Xlength; - PCRE2_SIZE Xoffset; - PCRE2_SIZE Xoffset_top; - PCRE2_SIZE Xsave_offset1, Xsave_offset2, Xsave_offset3; - - uint32_t Xfc; - uint32_t Xnumber; - uint32_t Xrdepth; - uint32_t Xop; - uint32_t Xsave_capture_last; - -#ifdef SUPPORT_UNICODE - uint32_t Xprop_value; - int Xprop_type; - int Xprop_fail_result; - int Xoclength; -#endif - - int Xcodelink; - int Xctype; - int Xfi; - int Xmax; - int Xmin; - int Xwhere; /* Where to jump back to */ - - BOOL Xcondition; - BOOL Xcur_is_word; - BOOL Xprev_is_word; - - eptrblock Xnewptrb; - recursion_info Xnew_recursive; - -#ifdef SUPPORT_UNICODE - PCRE2_UCHAR Xocchars[6]; -#endif -} heapframe; - -#endif - - -/*************************************************************************** -***************************************************************************/ +/****************************************************************************** +******************************************************************************* + "Recursion" in the match() function +The original match() function was highly recursive, but this proved to be the +source of a number of problems over the years, mostly because of the relatively +small system stacks that are commonly found. As new features were added to +patterns, various kludges were invented to reduce the amount of stack used, +making the code hard to understand in places. -/* When HEAP_MATCH_RECURSE is not defined, the match() function implements -backtrack points by calling itself recursively in all but one case. The one -special case is when processing OP_RECURSE, which specifies recursion in the -pattern. The entire ovector must be saved and restored while processing -OP_RECURSE. If the ovector is small enough, instead of calling match() -directly, op_recurse_ovecsave() is called. This function uses the system stack -to save the ovector while calling match() to process the pattern recursion. */ +A version did exist that used individual frames on the heap instead of calling +match() recursively, but this ran substantially slower. The current version is +a refactoring that uses a vector of frames to remember backtracking points. +This runs no slower, and possibly even a bit faster than the original recursive +implementation. An initial vector of size START_FRAMES_SIZE (enough for maybe +50 frames) is allocated on the system stack. If this is not big enough, the +heap is used for a larger vector. -#ifndef HEAP_MATCH_RECURSE +******************************************************************************* +******************************************************************************/ -/* We need a prototype for match() because it is mutually recursive with -op_recurse_ovecsave(). */ -static int -match(REGISTER PCRE2_SPTR eptr, REGISTER PCRE2_SPTR ecode, PCRE2_SPTR mstart, - PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth); /************************************************* -* Process OP_RECURSE, stacking ovector * +* Macros for the match() function * *************************************************/ -/* When this function is called, mb->recursive has already been updated to -point to a new recursion data block, and all its fields other than ovec_save -have been set. - -This function exists so that the local vector variable ovecsave is no longer -defined in the match() function, as it was in PCRE1. It is used only when there -is recursion in the pattern, so it wastes a lot of stack to have it defined for -every call of match(). We now use this function as an indirect way of calling -match() only in the case when ovecsave is needed. (David Wheeler used to say -"All problems in computer science can be solved by another level of -indirection.") - -HOWEVER: when this file is compiled by gcc in an optimizing mode, because this -function is called only once, and only from within match(), gcc will "inline" -it - that is, move it inside match() - and this completely negates its reason -for existence. Therefore, we mark it as non-inline when gcc is in use. - -Arguments: - eptr pointer to current character in subject - callpat the recursion point in the pattern - mstart pointer to the current match start position (can be modified - by encountering \K) - offset_top current top pointer (highest ovector offset used + 1) - mb pointer to "static" info block for the match - eptrb pointer to chain of blocks containing eptr at start of - brackets - for testing for empty matches - rdepth the recursion depth - -Returns: a match() return code -*/ - -static int -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) -__attribute__ ((noinline)) -#endif -op_recurse_ovecsave(REGISTER PCRE2_SPTR eptr, PCRE2_SPTR callpat, - PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, - uint32_t rdepth) -{ -register int rrc; -BOOL cbegroup = *callpat >= OP_SBRA; -recursion_info *new_recursive = mb->recursive; -PCRE2_SIZE ovecsave[OP_RECURSE_STACK_SAVE_MAX]; +/* These macros pack up tests that are used for partial matching several times +in the code. We set the "hit end" flag if the pointer is at the end of the +subject and also past the earliest inspected character (i.e. something has been +matched, even if not part of the actual matched string). For hard partial +matching, we then return immediately. The second one is used when we already +know we are past the end of the subject. */ -/* Save the ovector */ +#define CHECK_PARTIAL()\ + if (mb->partial != 0 && Feptr >= mb->end_subject && \ + Feptr > mb->start_used_ptr) \ + { \ + mb->hitend = TRUE; \ + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \ + } -new_recursive->ovec_save = ovecsave; -memcpy(ovecsave, mb->ovector, mb->offset_end * sizeof(PCRE2_SIZE)); +#define SCHECK_PARTIAL()\ + if (mb->partial != 0 && Feptr > mb->start_used_ptr) \ + { \ + mb->hitend = TRUE; \ + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \ + } -/* Do the recursion. After processing each alternative, restore the ovector -data and the last captured value. */ +/* These macros are used to implement backtracking. They simulate a recursive +call to the match() function by means of a local vector of frames which +remember the backtracking points. */ -do - { - if (cbegroup) mb->match_function_type |= MATCH_CBEGROUP; - rrc = match(eptr, callpat + PRIV(OP_lengths)[*callpat], mstart, offset_top, - mb, eptrb, rdepth + 1); - memcpy(mb->ovector, new_recursive->ovec_save, - mb->offset_end * sizeof(PCRE2_SIZE)); - mb->capture_last = new_recursive->saved_capture_last; - if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) return rrc; - - /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a - recursion; they cause a NOMATCH for the entire recursion. These codes - are defined in a range that can be tested for. */ - - if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX) - return MATCH_NOMATCH; - - /* Any return code other than NOMATCH is an error. Otherwise, advance to the - next alternative or to the end of the recursing subpattern. If there were - nested recursions, mb->recursive might be changed, so reset it before - looping. */ - - if (rrc != MATCH_NOMATCH) return rrc; - mb->recursive = new_recursive; - callpat += GET(callpat, 1); +#define RMATCH(ra,rb)\ + {\ + start_ecode = ra;\ + Freturn_id = rb;\ + goto MATCH_RECURSE;\ + L_##rb:;\ } -while (*callpat == OP_ALT); /* Loop for the alternatives */ -/* None of the alternatives matched. */ - -return MATCH_NOMATCH; -} -#endif /* HEAP_MATCH_RECURSE */ +#define RRETURN(ra)\ + {\ + rrc = ra;\ + goto RETURN_SWITCH;\ + } @@ -529,2022 +535,1721 @@ return MATCH_NOMATCH; * Match from current position * *************************************************/ -/* This function is called recursively in many circumstances. Whenever it -returns a negative (error) response, the outer incarnation must also return the -same response. */ - -/* These macros pack up tests that are used for partial matching, and which -appear several times in the code. We set the "hit end" flag if the pointer is -at the end of the subject and also past the earliest inspected character (i.e. -something has been matched, even if not part of the actual matched string). For -hard partial matching, we then return immediately. The second one is used when -we already know we are past the end of the subject. */ - -#define CHECK_PARTIAL()\ - if (mb->partial != 0 && eptr >= mb->end_subject && \ - eptr > mb->start_used_ptr) \ - { \ - mb->hitend = TRUE; \ - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); \ - } - -#define SCHECK_PARTIAL()\ - if (mb->partial != 0 && eptr > mb->start_used_ptr) \ - { \ - mb->hitend = TRUE; \ - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); \ - } - +/* This function is called to run one match attempt at a single starting point +in the subject. -/* Performance note: It might be tempting to extract commonly used fields from -the mb structure (e.g. utf, end_subject) into individual variables to improve +Performance note: It might be tempting to extract commonly used fields from the +mb structure (e.g. end_subject) into individual variables to improve performance. Tests using gcc on a SPARC disproved this; in the first case, it made performance worse. Arguments: - eptr pointer to current character in subject - ecode pointer to current position in compiled code - mstart pointer to the current match start position (can be modified - by encountering \K) - offset_top current top pointer (highest ovector offset used + 1) - mb pointer to "static" info block for the match - eptrb pointer to chain of blocks containing eptr at start of - brackets - for testing for empty matches - rdepth the recursion depth - -Returns: MATCH_MATCH if matched ) these values are >= 0 - MATCH_NOMATCH if failed to match ) - a negative MATCH_xxx value for PRUNE, SKIP, etc - a negative PCRE2_ERROR_xxx value if aborted by an error condition - (e.g. stopped by repeated call or recursion limit) + start_eptr starting character in subject + start_ecode starting position in compiled code + ovector pointer to the final output vector + oveccount number of pairs in ovector + top_bracket number of capturing parentheses in the pattern + frame_size size of each backtracking frame + mb pointer to "static" variables block + +Returns: MATCH_MATCH if matched ) these values are >= 0 + MATCH_NOMATCH if failed to match ) + negative MATCH_xxx value for PRUNE, SKIP, etc + negative PCRE2_ERROR_xxx value if aborted by an error condition + (e.g. stopped by repeated call or depth limit) */ static int -match(REGISTER PCRE2_SPTR eptr, REGISTER PCRE2_SPTR ecode, PCRE2_SPTR mstart, - PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth) +match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, PCRE2_SIZE *ovector, + uint16_t oveccount, uint16_t top_bracket, PCRE2_SIZE frame_size, + match_block *mb) { -/* These variables do not need to be preserved over recursion in this function, -so they can be ordinary variables in all cases. Mark some of them with -"register" because they are used a lot in loops. */ - -register int rrc; /* Returns from recursive calls */ -register int i; /* Used for loops not involving calls to RMATCH() */ -register uint32_t c; /* Character values not kept over RMATCH() calls */ -register BOOL utf; /* Local copy of UTF flag for speed */ +/* Frame-handling variables */ -BOOL minimize, possessive; /* Quantifier options */ -BOOL caseless; -int condcode; +heapframe *F; /* Current frame pointer */ +heapframe *N = NULL; /* Temporary frame pointers */ +heapframe *P = NULL; +heapframe *assert_accept_frame; /* For passing back the frame with captures */ +PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */ -/* When recursion is not being used, all "local" variables that have to be -preserved over calls to RMATCH() are part of a "frame". We set up the top-level -frame on the stack here; subsequent instantiations are obtained from the heap -whenever RMATCH() does a "recursion". See the macro definitions above. Putting -the top-level on the stack rather than malloc-ing them all gives a performance -boost in many cases where there is not much "recursion". */ +/* Local variables that do not need to be preserved over calls to RRMATCH(). */ -#ifdef HEAP_MATCH_RECURSE -heapframe *frame = (heapframe *)mb->match_frames_base; +PCRE2_SPTR bracode; /* Temp pointer to start of group */ +PCRE2_SIZE offset; /* Used for group offsets */ +PCRE2_SIZE length; /* Used for various length calculations */ -/* Copy in the original argument variables */ - -frame->Xeptr = eptr; -frame->Xecode = ecode; -frame->Xmstart = mstart; -frame->Xoffset_top = offset_top; -frame->Xeptrb = eptrb; -frame->Xrdepth = rdepth; - -/* This is where control jumps back to to effect "recursion" */ - -HEAP_RECURSE: +int rrc; /* Return from functions & backtracking "recursions" */ +#ifdef SUPPORT_UNICODE +int proptype; /* Type of character property */ +#endif -/* Macros make the argument variables come from the current frame */ +uint32_t i; /* Used for local loops */ +uint32_t fc; /* Character values */ +uint32_t number; /* Used for group and other numbers */ +uint32_t reptype = 0; /* Type of repetition (0 to avoid compiler warning) */ +uint32_t group_frame_type; /* Specifies type for new group frames */ -#define eptr frame->Xeptr -#define ecode frame->Xecode -#define mstart frame->Xmstart -#define offset_top frame->Xoffset_top -#define eptrb frame->Xeptrb -#define rdepth frame->Xrdepth +BOOL condition; /* Used in conditional groups */ +BOOL cur_is_word; /* Used in "word" tests */ +BOOL prev_is_word; /* Used in "word" tests */ -/* Ditto for the local variables */ +/* UTF flag */ #ifdef SUPPORT_UNICODE -#define charptr frame->Xcharptr -#define prop_value frame->Xprop_value -#define prop_type frame->Xprop_type -#define prop_fail_result frame->Xprop_fail_result -#define oclength frame->Xoclength -#define occhars frame->Xocchars +BOOL utf = (mb->poptions & PCRE2_UTF) != 0; +#else +BOOL utf = FALSE; #endif +/* This is the length of the last part of a backtracking frame that must be +copied when a new frame is created. */ -#define callpat frame->Xcallpat -#define codelink frame->Xcodelink -#define data frame->Xdata -#define next_ecode frame->Xnext_ecode -#define pp frame->Xpp -#define prev frame->Xprev -#define saved_eptr frame->Xsaved_eptr - -#define new_recursive frame->Xnew_recursive - -#define ctype frame->Xctype -#define fc frame->Xfc -#define fi frame->Xfi -#define length frame->Xlength -#define max frame->Xmax -#define min frame->Xmin -#define number frame->Xnumber -#define offset frame->Xoffset -#define op frame->Xop -#define save_capture_last frame->Xsave_capture_last -#define save_offset1 frame->Xsave_offset1 -#define save_offset2 frame->Xsave_offset2 -#define save_offset3 frame->Xsave_offset3 - -#define condition frame->Xcondition -#define cur_is_word frame->Xcur_is_word -#define prev_is_word frame->Xprev_is_word - -#define newptrb frame->Xnewptrb - -/* When normal stack-based recursion is being used for match(), local variables -are allocated on the stack and get preserved during recursion in the usual way. -In this environment, fi and i, and fc and c, can be the same variables. */ - -#else /* HEAP_MATCH_RECURSE not defined */ -#define fi i -#define fc c - -/* Many of the following variables are used only in small blocks of the code. -My normal style of coding would have declared them within each of those blocks. -However, in order to accommodate the version of this code that uses an external -"stack" implemented on the heap, it is easier to declare them all here, so the -declarations can be cut out in a block. The only declarations within blocks -below are for variables that do not have to be preserved over a recursive call -to RMATCH(). */ - -#ifdef SUPPORT_UNICODE -PCRE2_SPTR charptr; -#endif -PCRE2_SPTR callpat; -PCRE2_SPTR data; -PCRE2_SPTR next_ecode; -PCRE2_SPTR pp; -PCRE2_SPTR prev; -PCRE2_SPTR saved_eptr; +frame_copy_size = frame_size - offsetof(heapframe, eptr); -PCRE2_SIZE length; -PCRE2_SIZE offset; -PCRE2_SIZE save_offset1, save_offset2, save_offset3; +/* Set up the first current frame at the start of the vector, and initialize +fields that are not reset for new frames. */ -uint32_t number; -uint32_t op; -uint32_t save_capture_last; +F = mb->match_frames; +Frdepth = 0; /* "Recursion" depth */ +Fcapture_last = 0; /* Number of most recent capture */ +Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */ +Fstart_match = Feptr = start_eptr; /* Current data pointer and start match */ +Fmark = NULL; /* Most recent mark */ +Foffset_top = 0; /* End of captures within the frame */ +Flast_group_offset = PCRE2_UNSET; /* Saved frame of most recent group */ +group_frame_type = 0; /* Not a start of group frame */ +goto NEW_FRAME; /* Start processing with this frame */ -#ifdef SUPPORT_UNICODE -uint32_t prop_value; -int prop_type; -int prop_fail_result; -int oclength; -PCRE2_UCHAR occhars[6]; -#endif +/* Come back here when we want to create a new frame for remembering a +backtracking point. */ -int codelink; -int ctype; -int max; -int min; +MATCH_RECURSE: -BOOL condition; -BOOL cur_is_word; -BOOL prev_is_word; +/* Set up a new backtracking frame. If the vector is full, get a new one +on the heap, doubling the size, but constrained by the heap limit. */ -eptrblock newptrb; -recursion_info new_recursive; -#endif /* HEAP_MATCH_RECURSE not defined */ +N = (heapframe *)((char *)F + frame_size); +if (N >= mb->match_frames_top) + { + PCRE2_SIZE newsize = mb->frame_vector_size * 2; + heapframe *new; -/* To save space on the stack and in the heap frame, I have doubled up on some -of the local variables that are used only in localised parts of the code, but -still need to be preserved over recursive calls of match(). These macros define -the alternative names that are used. */ + if ((newsize / 1024) > mb->heap_limit) + { + PCRE2_SIZE maxsize = ((mb->heap_limit * 1024)/frame_size) * frame_size; + if (mb->frame_vector_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT; + newsize = maxsize; + } -#define allow_zero cur_is_word -#define cbegroup condition -#define code_offset codelink -#define condassert condition -#define foc number -#define matched_once prev_is_word -#define save_mark data + new = mb->memctl.malloc(newsize, mb->memctl.memory_data); + if (new == NULL) return PCRE2_ERROR_NOMEMORY; + memcpy(new, mb->match_frames, mb->frame_vector_size); -/* These statements are here to stop the compiler complaining about unitialized -variables. */ + F = (heapframe *)((char *)new + ((char *)F - (char *)mb->match_frames)); + N = (heapframe *)((char *)F + frame_size); -#ifdef SUPPORT_UNICODE -prop_value = 0; -prop_fail_result = 0; -#endif + if (mb->match_frames != mb->stack_frames) + mb->memctl.free(mb->match_frames, mb->memctl.memory_data); + mb->match_frames = new; + mb->match_frames_top = (heapframe *)((char *)mb->match_frames + newsize); + mb->frame_vector_size = newsize; + } +#ifdef DEBUG_SHOW_RMATCH +fprintf(stderr, "++ RMATCH %2d frame=%d", Freturn_id, Frdepth + 1); +if (group_frame_type != 0) + { + fprintf(stderr, " type=%x ", group_frame_type); + switch (GF_IDMASK(group_frame_type)) + { + case GF_CAPTURE: + fprintf(stderr, "capture=%d", GF_DATAMASK(group_frame_type)); + break; -/* This label is used for tail recursion, which is used in a few cases even -when HEAP_MATCH_RECURSE is not defined, in order to reduce the amount of stack -that is used. Thanks to Ian Taylor for noticing this possibility and sending -the original patch. */ + case GF_NOCAPTURE: + fprintf(stderr, "nocapture op=%d", GF_DATAMASK(group_frame_type)); + break; -TAIL_RECURSE: + case GF_CONDASSERT: + fprintf(stderr, "condassert op=%d", GF_DATAMASK(group_frame_type)); + break; -/* OK, now we can get on with the real code of the function. Recursive calls -are specified by the macro RMATCH and RRETURN is used to return. When -HEAP_MATCH_RECURSE is *not* defined, these just turn into a recursive call to -match() and a "return", respectively. However, RMATCH isn't like a function -call because it's quite a complicated macro. It has to be used in one -particular way. This shouldn't, however, impact performance when true recursion -is being used. */ + case GF_RECURSE: + fprintf(stderr, "recurse=%d", GF_DATAMASK(group_frame_type)); + break; -#ifdef SUPPORT_UNICODE -utf = (mb->poptions & PCRE2_UTF) != 0; -#else -utf = FALSE; + default: + fprintf(stderr, "*** unknown ***"); + break; + } + } +fprintf(stderr, "\n"); #endif -/* First check that we haven't called match() too many times, or that we -haven't exceeded the recursive call limit. */ +/* Copy those fields that must be copied into the new frame, increase the +"recursion" depth (i.e. the new frame's index) and then make the new frame +current. */ + +memcpy((char *)N + offsetof(heapframe, eptr), + (char *)F + offsetof(heapframe, eptr), + frame_copy_size); -if (mb->match_call_count++ >= mb->match_limit) RRETURN(PCRE2_ERROR_MATCHLIMIT); -if (rdepth >= mb->match_limit_recursion) RRETURN(PCRE2_ERROR_RECURSIONLIMIT); +N->rdepth = Frdepth + 1; +F = N; -/* At the start of a group with an unlimited repeat that may match an empty -string, the variable mb->match_function_type contains the MATCH_CBEGROUP bit. -It is done this way to save having to use another function argument, which -would take up space on the stack. See also MATCH_CONDASSERT below. +/* Carry on processing with a new frame. */ -When MATCH_CBEGROUP is set, add the current subject pointer to the chain of -such remembered pointers, to be checked when we hit the closing ket, in order -to break infinite loops that match no characters. When match() is called in -other circumstances, don't add to the chain. The MATCH_CBEGROUP feature must -NOT be used with tail recursion, because the memory block that is used is on -the stack, so a new one may be required for each match(). */ +NEW_FRAME: +Fgroup_frame_type = group_frame_type; +Fecode = start_ecode; /* Starting code pointer */ +Fback_frame = frame_size; /* Default is go back one frame */ -if ((mb->match_function_type & MATCH_CBEGROUP) != 0) +/* If this is a special type of group frame, remember its offset for quick +access at the end of the group. If this is a recursion, set a new current +recursion value. */ + +if (group_frame_type != 0) { - newptrb.epb_saved_eptr = eptr; - newptrb.epb_prev = eptrb; - eptrb = &newptrb; - mb->match_function_type &= ~MATCH_CBEGROUP; + Flast_group_offset = (char *)F - (char *)mb->match_frames; + if (GF_IDMASK(group_frame_type) == GF_RECURSE) + Fcurrent_recurse = GF_DATAMASK(group_frame_type); + group_frame_type = 0; } -/* Now, at last, we can start processing the opcodes. */ + +/* ========================================================================= */ +/* This is the main processing loop. First check that we haven't recorded too +many backtracks (search tree is too large), or that we haven't exceeded the +recursive depth limit (used too many backtracking frames). If not, process the +opcodes. */ + +if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT; +if (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT; for (;;) { - minimize = possessive = FALSE; - op = *ecode; +#ifdef DEBUG_SHOW_OPS +fprintf(stderr, "++ op=%d\n", *Fecode); +#endif - switch(op) + Fop = (uint8_t)(*Fecode); /* Cast needed for 16-bit and 32-bit modes */ + switch(Fop) { - case OP_MARK: - mb->nomatch_mark = ecode + 2; - mb->mark = NULL; /* In case previously set by assertion */ - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb, - eptrb, RM55); - if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && - mb->mark == NULL) mb->mark = ecode + 2; - - /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an - argument, and we must check whether that argument matches this MARK's - argument. It is passed back in mb->start_match_ptr (an overloading of that - variable). If it does match, we reset that variable to the current subject - position and return MATCH_SKIP. Otherwise, pass back the return code - unaltered. */ + /* ===================================================================== */ + /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close + any currently open capturing brackets. Unlike reaching the end of a group, + where we know the starting frame is at the top of the chained frames, in + this case we have to search back for the relevant frame in case other types + of group that use chained frames have intervened. Multiple OP_CLOSEs always + come innermost first, which matches the chain order. We can ignore this in + a recursion, because captures are not passed out of recursions. */ - else if (rrc == MATCH_SKIP_ARG && - PRIV(strcmp)(ecode + 2, mb->start_match_ptr) == 0) + case OP_CLOSE: + if (Fcurrent_recurse == RECURSE_UNSET) { - mb->start_match_ptr = eptr; - RRETURN(MATCH_SKIP); + number = GET2(Fecode, 1); + offset = Flast_group_offset; + for(;;) + { + if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; + N = (heapframe *)((char *)mb->match_frames + offset); + P = (heapframe *)((char *)N - frame_size); + if (N->group_frame_type == (GF_CAPTURE | number)) break; + offset = P->last_group_offset; + } + offset = (number << 1) - 2; + Fcapture_last = number; + Fovector[offset] = P->eptr - mb->start_subject; + Fovector[offset+1] = Feptr - mb->start_subject; + if (offset >= Foffset_top) Foffset_top = offset + 2; } - RRETURN(rrc); + Fecode += PRIV(OP_lengths)[*Fecode]; + break; - case OP_FAIL: - RRETURN(MATCH_NOMATCH); - case OP_COMMIT: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM52); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_COMMIT); + /* ===================================================================== */ + /* Real or forced end of the pattern, assertion, or recursion. In an + assertion ACCEPT, update the last used pointer and remember the current + frame so that the captures and mark can be fished out of it. */ - case OP_PRUNE: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM51); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_PRUNE); + case OP_ASSERT_ACCEPT: + if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; + assert_accept_frame = F; + RRETURN(MATCH_ACCEPT); - case OP_PRUNE_ARG: - mb->nomatch_mark = ecode + 2; - mb->mark = NULL; /* In case previously set by assertion */ - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb, - eptrb, RM56); - if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && - mb->mark == NULL) mb->mark = ecode + 2; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_PRUNE); + /* If recursing, we have to find the most recent recursion. */ - case OP_SKIP: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM53); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->start_match_ptr = eptr; /* Pass back current position */ - RRETURN(MATCH_SKIP); + case OP_ACCEPT: + case OP_END: - /* Note that, for Perl compatibility, SKIP with an argument does NOT set - nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was - not a matching mark, we have to re-run the match, ignoring the SKIP_ARG - that failed and any that precede it (either they also failed, or were not - triggered). To do this, we maintain a count of executed SKIP_ARGs. If a - SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg - set to the count of the one that failed. */ + /* Handle end of a recursion. */ - case OP_SKIP_ARG: - mb->skip_arg_count++; - if (mb->skip_arg_count <= mb->ignore_skip_arg) + if (Fcurrent_recurse != RECURSE_UNSET) { - ecode += PRIV(OP_lengths)[*ecode] + ecode[1]; - break; - } - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb, - eptrb, RM57); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); + offset = Flast_group_offset; + for(;;) + { + if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; + N = (heapframe *)((char *)mb->match_frames + offset); + P = (heapframe *)((char *)N - frame_size); + if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break; + offset = P->last_group_offset; + } - /* Pass back the current skip name by overloading mb->start_match_ptr and - returning the special MATCH_SKIP_ARG return code. This will either be - caught by a matching MARK, or get to the top, where it causes a rematch - with mb->ignore_skip_arg set to the value of mb->skip_arg_count. */ + /* N is now the frame of the recursion; the previous frame is at the + OP_RECURSE position. Go back there, copying the current subject position + and mark, and move on past the OP_RECURSE. */ - mb->start_match_ptr = ecode + 2; - RRETURN(MATCH_SKIP_ARG); + P->eptr = Feptr; + P->mark = Fmark; + F = P; + Fecode += 1 + LINK_SIZE; + continue; + } - /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that - the branch in which it occurs can be determined. Overload the start of - match pointer to do this. */ + /* Not a recursion. Fail for an empty string match if either PCRE2_NOTEMPTY + is set, or if PCRE2_NOTEMPTY_ATSTART is set and we have matched at the + start of the subject. In both cases, backtracking will then try other + alternatives, if any. */ - case OP_THEN: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM54); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->start_match_ptr = ecode; - RRETURN(MATCH_THEN); + if (Feptr == Fstart_match && + ((mb->moptions & PCRE2_NOTEMPTY) != 0 || + ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 && + Fstart_match == mb->start_subject + mb->start_offset))) + RRETURN(MATCH_NOMATCH); - case OP_THEN_ARG: - mb->nomatch_mark = ecode + 2; - mb->mark = NULL; /* In case previously set by assertion */ - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, - mb, eptrb, RM58); - if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && - mb->mark == NULL) mb->mark = ecode + 2; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->start_match_ptr = ecode; - RRETURN(MATCH_THEN); + /* Also fail if PCRE2_ENDANCHORED is set and the end of the match is not + the end of the subject. After (*ACCEPT) we fail the entire match (at this + position) but backtrack on reaching the end of the pattern. */ - /* Handle an atomic group that does not contain any capturing parentheses. - This can be handled like an assertion. Prior to 8.13, all atomic groups - were handled this way. In 8.13, the code was changed as below for ONCE, so - that backups pass through the group and thereby reset captured values. - However, this uses a lot more stack, so in 8.20, atomic groups that do not - contain any captures generate OP_ONCE_NC, which can be handled in the old, - less stack intensive way. - - Check the alternative branches in turn - the matching won't pass the KET - for this kind of subpattern. If any one branch matches, we carry on as at - the end of a normal bracket, leaving the subject pointer, but resetting - the start-of-match value in case it was changed by \K. */ - - case OP_ONCE_NC: - prev = ecode; - saved_eptr = eptr; - save_mark = mb->mark; - do + if (Feptr < mb->end_subject && + ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0) { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM64); - if (rrc == MATCH_MATCH) /* Note: _not_ MATCH_ACCEPT */ - { - mstart = mb->start_match_ptr; - break; - } - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += GET(ecode,1); - mb->mark = save_mark; + if (Fop == OP_END) RRETURN(MATCH_NOMATCH); + return MATCH_NOMATCH; } - while (*ecode == OP_ALT); - /* If hit the end of the group (which could be repeated), fail */ + /* We have a successful match of the whole pattern. Record the result and + then do a direct return from the function. If there is space in the offset + vector, set any pairs that follow the highest-numbered captured string but + are less than the number of capturing groups in the pattern to PCRE2_UNSET. + It is documented that this happens. "Gaps" are set to PCRE2_UNSET + dynamically. It is only those at the end that need setting here. */ - if (*ecode != OP_ONCE_NC && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH); + mb->end_match_ptr = Feptr; /* Record where we ended */ + mb->end_offset_top = Foffset_top; /* and how many extracts were taken */ + mb->mark = Fmark; /* and the last success mark */ + if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; - /* Continue as from after the group, updating the offsets high water - mark, since extracts may have been taken. */ + ovector[0] = Fstart_match - mb->start_subject; + ovector[1] = Feptr - mb->start_subject; - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); + /* Set i to the smaller of the sizes of the external and frame ovectors. */ - offset_top = mb->end_offset_top; - eptr = mb->end_match_ptr; + i = 2 * ((top_bracket + 1 > oveccount)? oveccount : top_bracket + 1); + memcpy(ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE)); + while (--i >= Foffset_top + 2) ovector[i] = PCRE2_UNSET; + return MATCH_MATCH; /* Note: NOT RRETURN */ - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. */ - if (*ecode == OP_KET || eptr == saved_eptr) - { - ecode += 1+LINK_SIZE; - break; - } + /*===================================================================== */ + /* Match any single character type except newline; have to take care with + CRLF newlines and partial matching. */ - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. The second "call" of match() - uses tail recursion, to avoid using another stack frame. */ - - if (*ecode == OP_KETRMIN) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM65); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode = prev; - goto TAIL_RECURSE; - } - else /* OP_KETRMAX */ + case OP_ANY: + if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + if (mb->partial != 0 && + Feptr == mb->end_subject - 1 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM66); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += 1 + LINK_SIZE; - goto TAIL_RECURSE; + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } - /* Control never gets here */ - - /* Handle a capturing bracket, other than those that are possessive with an - unlimited repeat. If there is space in the offset vector, save the current - subject position in the working slot at the top of the vector. We mustn't - change the current values of the data slot, because they may be set from a - previous iteration of this group, and be referred to by a reference inside - the group. A failure to match might occur after the group has succeeded, - if something later on doesn't match. For this reason, we need to restore - the working value and also the values of the final offsets, in case they - were set by a previous iteration of the same bracket. - - If there isn't enough space in the offset vector, treat this as if it were - a non-capturing bracket. Don't worry about setting the flag for the error - case here; that is handled in the code for KET. */ - - case OP_CBRA: - case OP_SCBRA: - number = GET2(ecode, 1+LINK_SIZE); - offset = number << 1; - - if (offset < mb->offset_max) - { - save_offset1 = mb->ovector[offset]; - save_offset2 = mb->ovector[offset+1]; - save_offset3 = mb->ovector[mb->offset_end - number]; - save_capture_last = mb->capture_last; - save_mark = mb->mark; - - mb->ovector[mb->offset_end - number] = eptr - mb->start_subject; - - for (;;) - { - if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM1); - if (rrc == MATCH_ONCE) break; /* Backing up through an atomic group */ - - /* If we backed up to a THEN, check whether it is within the current - branch by comparing the address of the THEN that is passed back with - the end of the branch. If it is within the current branch, and the - branch is one of two or more alternatives (it either starts or ends - with OP_ALT), we have reached the limit of THEN's action, so convert - the return code to NOMATCH, which will cause normal backtracking to - happen from now on. Otherwise, THEN is passed back to an outer - alternative. This implements Perl's treatment of parenthesized groups, - where a group not containing | does not affect the current alternative, - that is, (X) is NOT the same as (X|(*F)). */ - - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - /* Anything other than NOMATCH is passed back. */ - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->capture_last = save_capture_last; - ecode += GET(ecode, 1); - mb->mark = save_mark; - if (*ecode != OP_ALT) break; - } - - mb->ovector[offset] = save_offset1; - mb->ovector[offset+1] = save_offset2; - mb->ovector[mb->offset_end - number] = save_offset3; + /* Fall through */ - /* At this point, rrc will be one of MATCH_ONCE or MATCH_NOMATCH. */ + /* Match any single character whatsoever. */ - RRETURN(rrc); + case OP_ALLANY: + if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + Feptr++; +#ifdef SUPPORT_UNICODE + if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); +#endif + Fecode++; + break; - /* FALL THROUGH ... Insufficient room for saving captured contents. Treat - as a non-capturing bracket. */ - - /* VVVVVVVVVVVVVVVVVVVVVVVVV */ - /* VVVVVVVVVVVVVVVVVVVVVVVVV */ - - /* Non-capturing or atomic group, except for possessive with unlimited - repeat and ONCE group with no captures. Loop for all the alternatives. - When we get to the final alternative within the brackets, we used to return - the result of a recursive call to match() whatever happened so it was - possible to reduce stack usage by turning this into a tail recursion, - except in the case of a possibly empty group. However, now that there is - the possiblity of (*THEN) occurring in the final alternative, this - optimization is no longer always possible. + /* ===================================================================== */ + /* Match a single code unit, even in UTF mode. This opcode really does + match any code unit, even newline. (It really should be called ANYCODEUNIT, + of course - the byte name is from pre-16 bit days.) */ - We can optimize if we know there are no (*THEN)s in the pattern; at present - this is the best that can be done. + case OP_ANYBYTE: + if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr++; + Fecode++; + break; - MATCH_ONCE is returned when the end of an atomic group is successfully - reached, but subsequent matching fails. It passes back up the tree (causing - captured values to be reset) until the original atomic group level is - reached. This is tested by comparing mb->once_target with the start of the - group. At this point, the return is converted into MATCH_NOMATCH so that - previous backup points can be taken. */ - case OP_ONCE: - case OP_BRA: - case OP_SBRA: + /* ===================================================================== */ + /* Match a single character, casefully */ - for (;;) + case OP_CHAR: +#ifdef SUPPORT_UNICODE + if (utf) { - if (op >= OP_SBRA || op == OP_ONCE) - mb->match_function_type |= MATCH_CBEGROUP; - - /* If this is not a possibly empty group, and there are no (*THEN)s in - the pattern, and this is the final alternative, optimize as described - above. */ - - else if (!mb->hasthen && ecode[GET(ecode, 1)] != OP_ALT) + Flength = 1; + Fecode++; + GETCHARLEN(fc, Fecode, Flength); + if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr)) { - ecode += PRIV(OP_lengths)[*ecode]; - goto TAIL_RECURSE; + CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); } - - /* In all other cases, we have to make another call to match(). */ - - save_mark = mb->mark; - save_capture_last = mb->capture_last; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, eptrb, - RM2); - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) + for (; Flength > 0; Flength--) { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; + if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH); } - - if (rrc != MATCH_NOMATCH) + } + else +#endif + /* Not UTF mode */ + { + if (mb->end_subject - Feptr < 1) { - if (rrc == MATCH_ONCE) - { - PCRE2_SPTR scode = ecode; - if (*scode != OP_ONCE) /* If not at start, find it */ - { - while (*scode == OP_ALT) scode += GET(scode, 1); - scode -= GET(scode, 1); - } - if (mb->once_target == scode) rrc = MATCH_NOMATCH; - } - RRETURN(rrc); + SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); } - ecode += GET(ecode, 1); - mb->mark = save_mark; - if (*ecode != OP_ALT) break; - mb->capture_last = save_capture_last; + if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH); + Fecode += 2; } + break; - RRETURN(MATCH_NOMATCH); - - /* Handle possessive capturing brackets with an unlimited repeat. We come - here from BRAZERO with allow_zero set TRUE. The ovector values are - handled similarly to the normal case above. However, the matching is - different. The end of these brackets will always be OP_KETRPOS, which - returns MATCH_KETRPOS without going further in the pattern. By this means - we can handle the group by iteration rather than recursion, thereby - reducing the amount of stack needed. If the ovector is too small for - capturing, treat as non-capturing. */ - case OP_CBRAPOS: - case OP_SCBRAPOS: - allow_zero = FALSE; + /* ===================================================================== */ + /* Match a single character, caselessly. If we are at the end of the + subject, give up immediately. We get here only when the pattern character + has at most one other case. Characters with more than two cases are coded + as OP_PROP with the pseudo-property PT_CLIST. */ - POSSESSIVE_CAPTURE: - number = GET2(ecode, 1+LINK_SIZE); - offset = number << 1; - if (offset >= mb->offset_max) goto POSSESSIVE_NON_CAPTURE; - - matched_once = FALSE; - code_offset = (int)(ecode - mb->start_code); - - save_offset1 = mb->ovector[offset]; - save_offset2 = mb->ovector[offset+1]; - save_offset3 = mb->ovector[mb->offset_end - number]; - save_capture_last = mb->capture_last; - - /* Each time round the loop, save the current subject position for use - when the group matches. For MATCH_MATCH, the group has matched, so we - restart it with a new subject starting position, remembering that we had - at least one match. For MATCH_NOMATCH, carry on with the alternatives, as - usual. If we haven't matched any alternatives in any iteration, check to - see if a previous iteration matched. If so, the group has matched; - continue from afterwards. Otherwise it has failed; restore the previous - capture values before returning NOMATCH. */ + case OP_CHARI: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } - for (;;) +#ifdef SUPPORT_UNICODE + if (utf) { - mb->ovector[mb->offset_end - number] = eptr - mb->start_subject; - if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM63); - if (rrc == MATCH_KETRPOS) - { - offset_top = mb->end_offset_top; - ecode = mb->start_code + code_offset; - save_capture_last = mb->capture_last; - matched_once = TRUE; - mstart = mb->start_match_ptr; /* In case \K changed it */ - if (eptr == mb->end_match_ptr) /* Matched an empty string */ - { - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); - break; - } - eptr = mb->end_match_ptr; - continue; - } + Flength = 1; + Fecode++; + GETCHARLEN(fc, Fecode, Flength); - /* See comment in the code for capturing groups above about handling - THEN. */ + /* If the pattern character's value is < 128, we know that its other case + (if any) is also < 128 (and therefore only one code unit long in all + code-unit widths), so we can use the fast lookup table. We checked above + that there is at least one character left in the subject. */ - if (rrc == MATCH_THEN) + if (fc < 128) { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; + uint32_t cc = UCHAR21(Feptr); + if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH); + Fecode++; + Feptr++; } - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->capture_last = save_capture_last; - ecode += GET(ecode, 1); - if (*ecode != OP_ALT) break; - } + /* Otherwise we must pick up the subject character and use Unicode + property support to test its other case. Note that we cannot use the + value of "Flength" to check for sufficient bytes left, because the other + case of the character may have more or fewer code units. */ - if (!matched_once) - { - mb->ovector[offset] = save_offset1; - mb->ovector[offset+1] = save_offset2; - mb->ovector[mb->offset_end - number] = save_offset3; + else + { + uint32_t dc; + GETCHARINC(dc, Feptr); + Fecode += Flength; + if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH); + } } + else +#endif /* SUPPORT_UNICODE */ - if (allow_zero || matched_once) + /* Not UTF mode; use the table for characters < 256. */ { - ecode += 1 + LINK_SIZE; - break; + if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1]) + != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH); + Feptr++; + Fecode += 2; } - RRETURN(MATCH_NOMATCH); - - /* Non-capturing possessive bracket with unlimited repeat. We come here - from BRAZERO with allow_zero = TRUE. The code is similar to the above, - without the capturing complication. It is written out separately for speed - and cleanliness. */ + break; - case OP_BRAPOS: - case OP_SBRAPOS: - allow_zero = FALSE; - POSSESSIVE_NON_CAPTURE: - matched_once = FALSE; - code_offset = (int)(ecode - mb->start_code); - save_capture_last = mb->capture_last; + /* ===================================================================== */ + /* Match not a single character. */ - for (;;) + case OP_NOT: + case OP_NOTI: + if (Feptr >= mb->end_subject) { - if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM48); - if (rrc == MATCH_KETRPOS) + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t ch; + Fecode++; + GETCHARINC(ch, Fecode); + GETCHARINC(fc, Feptr); + if (ch == fc) { - offset_top = mb->end_offset_top; - ecode = mb->start_code + code_offset; - matched_once = TRUE; - mstart = mb->start_match_ptr; /* In case \K reset it */ - if (eptr == mb->end_match_ptr) /* Matched an empty string */ - { - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); - break; - } - eptr = mb->end_match_ptr; - continue; + RRETURN(MATCH_NOMATCH); /* Caseful match */ } - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) + else if (Fop == OP_NOTI) /* If caseless */ { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; + if (ch > 127) + ch = UCD_OTHERCASE(ch); + else + ch = TABLE_GET(ch, mb->fcc, ch); + if (ch == fc) RRETURN(MATCH_NOMATCH); } - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += GET(ecode, 1); - if (*ecode != OP_ALT) break; - mb->capture_last = save_capture_last; } - - if (matched_once || allow_zero) + else +#endif /* SUPPORT_UNICODE */ { - ecode += 1 + LINK_SIZE; - break; + uint32_t ch = Fecode[1]; + fc = *Feptr++; + if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc)) + RRETURN(MATCH_NOMATCH); + Fecode += 2; } - RRETURN(MATCH_NOMATCH); + break; - /* Control never reaches here. */ - /* Conditional group: compilation checked that there are no more than two - branches. If the condition is false, skipping the first branch takes us - past the end of the item if there is only one branch, but that's exactly - what we want. */ + /* ===================================================================== */ + /* Match a single character repeatedly. */ - case OP_COND: - case OP_SCOND: +#define Loclength F->temp_size +#define Lstart_eptr F->temp_sptr[0] +#define Lcharptr F->temp_sptr[1] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lc F->temp_32[2] +#define Loc F->temp_32[3] - /* The variable codelink will be added to ecode when the condition is - false, to get to the second branch. Setting it to the offset to the ALT - or KET, then incrementing ecode achieves this effect. We now have ecode - pointing to the condition or callout. */ + case OP_EXACT: + case OP_EXACTI: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; - codelink = GET(ecode, 1); /* Offset to the second branch */ - ecode += 1 + LINK_SIZE; /* From this opcode */ + case OP_POSUPTO: + case OP_POSUPTOI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; - /* Because of the way auto-callout works during compile, a callout item is - inserted between OP_COND and an assertion condition. */ + case OP_UPTO: + case OP_UPTOI: + reptype = REPTYPE_MAX; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; - if (*ecode == OP_CALLOUT || *ecode == OP_CALLOUT_STR) - { - unsigned int callout_length = (*ecode == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE); + case OP_MINUPTO: + case OP_MINUPTOI: + reptype = REPTYPE_MIN; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.capture_top = offset_top/2; - cb.capture_last = mb->capture_last & CAPLMASK; - cb.offset_vector = mb->ovector; - cb.mark = mb->nomatch_mark; - cb.subject = mb->start_subject; - cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject); - cb.start_match = (PCRE2_SIZE)(mstart - mb->start_subject); - cb.current_position = (PCRE2_SIZE)(eptr - mb->start_subject); - cb.pattern_position = GET(ecode, 1); - cb.next_item_length = GET(ecode, 1 + LINK_SIZE); - - if (*ecode == OP_CALLOUT) - { - cb.callout_number = ecode[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(ecode, 1 + 3*LINK_SIZE); - cb.callout_string = ecode + (1 + 4*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } + case OP_POSSTAR: + case OP_POSSTARI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATCHAR; - if ((rrc = mb->callout(&cb, mb->callout_data)) > 0) - RRETURN(MATCH_NOMATCH); - if (rrc < 0) RRETURN(rrc); - } + case OP_POSPLUS: + case OP_POSPLUSI: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATCHAR; - /* Advance ecode past the callout, so it now points to the condition. We - must adjust codelink so that the value of ecode+codelink is unchanged. */ + case OP_POSQUERY: + case OP_POSQUERYI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATCHAR; - ecode += callout_length; - codelink -= callout_length; - } + case OP_STAR: + case OP_STARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI); + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; - /* Test the various possible conditions */ + /* Common code for all repeated single-character matches. We first check + for the minimum number of characters. If the minimum equals the maximum, we + are done. Otherwise, if minimizing, check the rest of the pattern for a + match; if there isn't one, advance up to the maximum, one character at a + time. - condition = FALSE; - switch(condcode = *ecode) + If maximizing, advance up to the maximum number of matching characters, + until Feptr is past the end of the maximum run. If possessive, we are + then done (no backing up). Otherwise, match at this position; anything + other than no match is immediately returned. For nomatch, back up one + character, unless we are matching \R and the last thing matched was + \r\n, in which case, back up two code units until we reach the first + optional character position. + + The various UTF/non-UTF and caseful/caseless cases are handled separately, + for speed. */ + + REPEATCHAR: +#ifdef SUPPORT_UNICODE + if (utf) { - case OP_RREF: /* Numbered group recursion test */ - if (mb->recursive != NULL) /* Not recursing => FALSE */ - { - uint32_t recno = GET2(ecode, 1); /* Recursion group number*/ - condition = (recno == RREF_ANY || recno == mb->recursive->group_num); - } - break; + Flength = 1; + Lcharptr = Fecode; + GETCHARLEN(fc, Fecode, Flength); + Fecode += Flength; - case OP_DNRREF: /* Duplicate named group recursion test */ - if (mb->recursive != NULL) + /* Handle multi-code-unit character matching, caseful and caseless. */ + + if (Flength > 1) { - int count = GET2(ecode, 1 + IMM2_SIZE); - PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size; - while (count-- > 0) - { - uint32_t recno = GET2(slot, 0); - condition = recno == mb->recursive->group_num; - if (condition) break; - slot += mb->name_entry_size; - } - } - break; + uint32_t othercase; - case OP_CREF: /* Numbered group used test */ - offset = GET2(ecode, 1) << 1; /* Doubled ref number */ - condition = offset < offset_top && - mb->ovector[offset] != PCRE2_UNSET; - break; + if (Fop >= OP_STARI && /* Caseless */ + (othercase = UCD_OTHERCASE(fc)) != fc) + Loclength = PRIV(ord2utf)(othercase, Foccu); + else Loclength = 0; - case OP_DNCREF: /* Duplicate named group used test */ - { - int count = GET2(ecode, 1 + IMM2_SIZE); - PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size; - while (count-- > 0) + for (i = 1; i <= Lmin; i++) { - offset = GET2(slot, 0) << 1; - condition = offset < offset_top && - mb->ovector[offset] != PCRE2_UNSET; - if (condition) break; - slot += mb->name_entry_size; + if (Feptr <= mb->end_subject - Flength && + memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; + else if (Loclength > 0 && + Feptr <= mb->end_subject - Loclength && + memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) + Feptr += Loclength; + else + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } } - } - break; - - case OP_FALSE: - case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */ - break; - - case OP_TRUE: - condition = TRUE; - break; - /* The condition is an assertion. Call match() to evaluate it - setting - the MATCH_CONDASSERT bit in mb->match_function_type causes it to stop at - the end of an assertion. */ + if (Lmin == Lmax) continue; - default: - mb->match_function_type |= MATCH_CONDASSERT; - RMATCH(eptr, ecode, offset_top, mb, NULL, RM3); - if (rrc == MATCH_MATCH) - { - if (mb->end_offset_top > offset_top) - offset_top = mb->end_offset_top; /* Captures may have happened */ - condition = TRUE; - - /* Advance ecode past the assertion to the start of the first branch, - but adjust it so that the general choosing code below works. If the - assertion has a quantifier that allows zero repeats we must skip over - the BRAZERO. This is a lunatic thing to do, but somebody did! */ - - if (*ecode == OP_BRAZERO) ecode++; - ecode += GET(ecode, 1); - while (*ecode == OP_ALT) ecode += GET(ecode, 1); - ecode += 1 + LINK_SIZE - PRIV(OP_lengths)[condcode]; - } + if (reptype == REPTYPE_MIN) + { + for (;;) + { + RMATCH(Fecode, RM202); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr <= mb->end_subject - Flength && + memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; + else if (Loclength > 0 && + Feptr <= mb->end_subject - Loclength && + memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) + Feptr += Loclength; + else + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + else /* Maximize */ + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + if (Feptr <= mb->end_subject - Flength && + memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) + Feptr += Flength; + else if (Loclength > 0 && + Feptr <= mb->end_subject - Loclength && + memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) + Feptr += Loclength; + else + { + CHECK_PARTIAL(); + break; + } + } - /* PCRE doesn't allow the effect of (*THEN) to escape beyond an - assertion; it is therefore treated as NOMATCH. Any other return is an - error. */ + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ - else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) - { - RRETURN(rrc); /* Need braces because of following else */ + if (reptype != REPTYPE_POS) for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM203); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + } + } + break; /* End of repeated wide character handling */ } - break; + + /* Length of UTF character is 1. Put it into the preserved variable and + fall through to the non-UTF code. */ + + Lc = fc; } + else +#endif /* SUPPORT_UNICODE */ - /* Choose branch according to the condition */ + /* When not in UTF mode, load a single-code-unit character. Then proceed as + above. */ - ecode += condition? PRIV(OP_lengths)[condcode] : codelink; + Lc = *Fecode++; - /* We are now at the branch that is to be obeyed. As there is only one, we - can use tail recursion to avoid using another stack frame, except when - there is unlimited repeat of a possibly empty group. In the latter case, a - recursive call to match() is always required, unless the second alternative - doesn't exist, in which case we can just plough on. Note that, for - compatibility with Perl, the | in a conditional group is NOT treated as - creating two alternatives. If a THEN is encountered in the branch, it - propagates out to the enclosing alternative (unless nested in a deeper set - of alternatives, of course). */ + /* Caseless comparison */ - if (condition || ecode[-(1+LINK_SIZE)] == OP_ALT) + if (Fop >= OP_STARI) { - if (op != OP_SCOND) +#if PCRE2_CODE_UNIT_WIDTH == 8 + /* Lc must be < 128 in UTF-8 mode. */ + Loc = mb->fcc[Lc]; +#else /* 16-bit & 32-bit */ +#ifdef SUPPORT_UNICODE + if (utf && Lc > 127) Loc = UCD_OTHERCASE(Lc); + else +#endif /* SUPPORT_UNICODE */ + Loc = TABLE_GET(Lc, mb->fcc, Lc); +#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + + for (i = 1; i <= Lmin; i++) { - goto TAIL_RECURSE; + uint32_t cc; /* Faster than PCRE2_UCHAR */ + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21TEST(Feptr); + if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH); + Feptr++; } + if (Lmin == Lmax) continue; - mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM49); - RRETURN(rrc); + if (reptype == REPTYPE_MIN) + { + for (;;) + { + uint32_t cc; /* Faster than PCRE2_UCHAR */ + RMATCH(Fecode, RM25); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21TEST(Feptr); + if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH); + Feptr++; + } + /* Control never gets here */ + } + + else /* Maximize */ + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + uint32_t cc; /* Faster than PCRE2_UCHAR */ + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + cc = UCHAR21TEST(Feptr); + if (Lc != cc && Loc != cc) break; + Feptr++; + } + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM26); + Feptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + } } - /* Condition false & no alternative; continue after the group. */ + /* Caseful comparisons (includes all multi-byte characters) */ else { - } - break; - + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH); + } - /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, - to close any currently open capturing brackets. */ + if (Lmin == Lmax) continue; - case OP_CLOSE: - number = GET2(ecode, 1); /* Must be less than 65536 */ - offset = number << 1; - mb->capture_last = (mb->capture_last & OVFLMASK) | number; - if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else - { - mb->ovector[offset] = - mb->ovector[mb->offset_end - number]; - mb->ovector[offset+1] = eptr - mb->start_subject; + if (reptype == REPTYPE_MIN) + { + for (;;) + { + RMATCH(Fecode, RM27); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + else /* Maximize */ + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } - /* If this group is at or above the current highwater mark, ensure that - any groups between the current high water mark and this group are marked - unset and then update the high water mark. */ + if (Lc != UCHAR21TEST(Feptr)) break; + Feptr++; + } - if (offset >= offset_top) - { - register PCRE2_SIZE *iptr = mb->ovector + offset_top; - register PCRE2_SIZE *iend = mb->ovector + offset; - while (iptr < iend) *iptr++ = PCRE2_UNSET; - offset_top = offset + 2; + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM28); + Feptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } } } - ecode += 1 + IMM2_SIZE; break; +#undef Loclength +#undef Lstart_eptr +#undef Lcharptr +#undef Lmin +#undef Lmax +#undef Lc +#undef Loc - /* End of the pattern, either real or forced. In an assertion ACCEPT, - update the last used pointer. */ - case OP_ASSERT_ACCEPT: - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; + /* ===================================================================== */ + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ - case OP_ACCEPT: - case OP_END: +#define Lstart_eptr F->temp_sptr[0] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lc F->temp_32[2] +#define Loc F->temp_32[3] - /* If we have matched an empty string, fail if not in an assertion and not - in a recursion if either PCRE2_NOTEMPTY is set, or if PCRE2_NOTEMPTY_ATSTART - is set and we have matched at the start of the subject. In both cases, - backtracking will then try other alternatives, if any. */ + case OP_NOTEXACT: + case OP_NOTEXACTI: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; - if (eptr == mstart && op != OP_ASSERT_ACCEPT && - mb->recursive == NULL && - ((mb->moptions & PCRE2_NOTEMPTY) != 0 || - ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 && - mstart == mb->start_subject + mb->start_offset))) - RRETURN(MATCH_NOMATCH); + case OP_NOTUPTO: + case OP_NOTUPTOI: + Lmin = 0; + Lmax = GET2(Fecode, 1); + reptype = REPTYPE_MAX; + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + Lmin = 0; + Lmax = GET2(Fecode, 1); + reptype = REPTYPE_MIN; + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; - /* Otherwise, we have a match. */ + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATNOTCHAR; - mb->end_match_ptr = eptr; /* Record where we ended */ - mb->end_offset_top = offset_top; /* and how many extracts were taken */ - mb->start_match_ptr = mstart; /* and the start (\K can modify) */ + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATNOTCHAR; - /* For some reason, the macros don't work properly if an expression is - given as the argument to RRETURN when the heap is in use. */ + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; - rrc = (op == OP_END)? MATCH_MATCH : MATCH_ACCEPT; - RRETURN(rrc); + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; - /* Assertion brackets. Check the alternative branches in turn - the - matching won't pass the KET for an assertion. If any one branch matches, - the assertion is true. Lookbehind assertions have an OP_REVERSE item at the - start of each branch to move the current point backwards, so the code at - this level is identical to the lookahead case. When the assertion is part - of a condition, we want to return immediately afterwards. The caller of - this incarnation of the match() function will have set MATCH_CONDASSERT in - mb->match_function type, and one of these opcodes will be the first opcode - that is processed. We use a local variable that is preserved over calls to - match() to remember this case. */ + /* Common code for all repeated single-character non-matches. */ - case OP_ASSERT: - case OP_ASSERTBACK: - save_mark = mb->mark; - if ((mb->match_function_type & MATCH_CONDASSERT) != 0) - { - condassert = TRUE; - mb->match_function_type &= ~MATCH_CONDASSERT; - } - else condassert = FALSE; + REPEATNOTCHAR: + GETCHARINCTEST(Lc, Fecode); - /* Loop for each branch */ + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If Lmin = Lmax, we are done. + Otherwise, if minimizing, keep trying the rest of the expression and + advancing one matching character if failing, up to the maximum. + Alternatively, if maximizing, find the maximum number of characters and + work backwards. */ - do + if (Fop >= OP_NOTSTARI) /* Caseless */ { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, NULL, RM4); +#ifdef SUPPORT_UNICODE + if (utf && Lc > 127) + Loc = UCD_OTHERCASE(Lc); + else +#endif /* SUPPORT_UNICODE */ - /* A match means that the assertion is true; break out of the loop - that matches its alternatives. */ + Loc = TABLE_GET(Lc, mb->fcc, Lc); /* Other case from table */ - if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) +#ifdef SUPPORT_UNICODE + if (utf) { - mstart = mb->start_match_ptr; /* In case \K reset it */ - break; + uint32_t d; + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH); + } } + else +#endif /* SUPPORT_UNICODE */ - /* If not matched, restore the previous mark setting. */ - - mb->mark = save_mark; - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) + /* Not UTF mode */ { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH); + Feptr++; + } } - /* Anything other than NOMATCH causes the entire assertion to fail, - passing back the return code. This includes COMMIT, SKIP, PRUNE and an - uncaptured THEN, which means they take their normal effect. This - consistent approach does not always have exactly the same effect as in - Perl. */ - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += GET(ecode, 1); - } - while (*ecode == OP_ALT); /* Continue for next alternative */ + if (Lmin == Lmax) continue; /* Finished for exact count */ - /* If we have tried all the alternative branches, the assertion has - failed. If not, we broke out after a match. */ + if (reptype == REPTYPE_MIN) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (;;) + { + RMATCH(Fecode, RM204); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif /*SUPPORT_UNICODE */ - if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH); + /* Not UTF mode */ + { + for (;;) + { + RMATCH(Fecode, RM29); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH); + Feptr++; + } + } + /* Control never gets here */ + } - /* If checking an assertion for a condition, return MATCH_MATCH. */ + /* Maximize case */ - if (condassert) RRETURN(MATCH_MATCH); + else + { + Lstart_eptr = Feptr; - /* Continue from after a successful assertion, updating the offsets high - water mark, since extracts may have been taken during the assertion. */ +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(d, Feptr, len); + if (Lc == d || Loc == d) break; + Feptr += len; + } - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - ecode += 1 + LINK_SIZE; - offset_top = mb->end_offset_top; - continue; + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ - /* Negative assertion: all branches must fail to match for the assertion to - succeed. */ + if (reptype != REPTYPE_POS) for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM205); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + } + } + else +#endif /* SUPPORT_UNICODE */ - case OP_ASSERT_NOT: - case OP_ASSERTBACK_NOT: - save_mark = mb->mark; - if ((mb->match_function_type & MATCH_CONDASSERT) != 0) - { - condassert = TRUE; - mb->match_function_type &= ~MATCH_CONDASSERT; + /* Not UTF mode */ + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (Lc == *Feptr || Loc == *Feptr) break; + Feptr++; + } + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM30); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } + } + } } - else condassert = FALSE; - /* Loop for each alternative branch. */ + /* Caseful comparisons */ - do + else { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, NULL, RM5); - mb->mark = save_mark; /* Always restore the mark setting */ - - switch(rrc) +#ifdef SUPPORT_UNICODE + if (utf) { - case MATCH_MATCH: /* A successful match means */ - case MATCH_ACCEPT: /* the assertion has failed. */ - RRETURN(MATCH_NOMATCH); - - case MATCH_NOMATCH: /* Carry on with next branch */ - break; - - /* See comment in the code for capturing groups above about handling - THEN. */ - - case MATCH_THEN: - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) + uint32_t d; + for (i = 1; i <= Lmin; i++) { - rrc = MATCH_NOMATCH; - break; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d) RRETURN(MATCH_NOMATCH); } - /* Otherwise fall through. */ - - /* COMMIT, SKIP, PRUNE, and an uncaptured THEN cause the whole - assertion to fail to match, without considering any more alternatives. - Failing to match means the assertion is true. This is a consistent - approach, but does not always have the same effect as in Perl. */ - - case MATCH_COMMIT: - case MATCH_SKIP: - case MATCH_SKIP_ARG: - case MATCH_PRUNE: - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - goto NEG_ASSERT_TRUE; /* Break out of alternation loop */ - - /* Anything else is an error */ - - default: - RRETURN(rrc); } - - /* Continue with next branch */ - - ecode += GET(ecode,1); - } - while (*ecode == OP_ALT); - - /* All branches in the assertion failed to match. */ - - NEG_ASSERT_TRUE: - if (condassert) RRETURN(MATCH_MATCH); /* Condition assertion */ - ecode += 1 + LINK_SIZE; /* Continue with current branch */ - continue; - - /* Move the subject pointer back. This occurs only at the start of - each branch of a lookbehind assertion. If we are too close to the start to - move back, this match function fails. When working with UTF-8 we move - back a number of characters, not bytes. */ - - case OP_REVERSE: - i = GET(ecode, 1); -#ifdef SUPPORT_UNICODE - if (utf) - { - while (i-- > 0) + else +#endif + /* Not UTF mode */ { - if (eptr <= mb->start_subject) RRETURN(MATCH_NOMATCH); - eptr--; - BACKCHAR(eptr); + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH); + } } - } - else -#endif - /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ + if (Lmin == Lmax) continue; - { - if (i > eptr - mb->start_subject) RRETURN(MATCH_NOMATCH); - eptr -= i; - } + if (reptype == REPTYPE_MIN) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (;;) + { + RMATCH(Fecode, RM206); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (;;) + { + RMATCH(Fecode, RM31); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } - /* Save the earliest consulted character, then skip to next op code */ + /* Maximize case */ - if (eptr < mb->start_used_ptr) mb->start_used_ptr = eptr; - ecode += 1 + LINK_SIZE; - break; + else + { + Lstart_eptr = Feptr; - /* The callout item calls an external function, if one is provided, passing - details of the match so far. This is mainly for debugging, though the - function is able to force a failure. */ +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(d, Feptr, len); + if (Lc == d) break; + Feptr += len; + } - case OP_CALLOUT: - case OP_CALLOUT_STR: - { - unsigned int callout_length = (*ecode == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE); + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.callout_number = ecode[LINK_SIZE + 1]; - cb.capture_top = offset_top/2; - cb.capture_last = mb->capture_last & CAPLMASK; - cb.offset_vector = mb->ovector; - cb.mark = mb->nomatch_mark; - cb.subject = mb->start_subject; - cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject); - cb.start_match = (PCRE2_SIZE)(mstart - mb->start_subject); - cb.current_position = (PCRE2_SIZE)(eptr - mb->start_subject); - cb.pattern_position = GET(ecode, 1); - cb.next_item_length = GET(ecode, 1 + LINK_SIZE); - - if (*ecode == OP_CALLOUT) - { - cb.callout_number = ecode[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; + if (reptype != REPTYPE_POS) for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM207); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + } } else +#endif + /* Not UTF mode */ { - cb.callout_number = 0; - cb.callout_string_offset = GET(ecode, 1 + 3*LINK_SIZE); - cb.callout_string = ecode + (1 + 4*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (Lc == *Feptr) break; + Feptr++; + } + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM32); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } } - - if ((rrc = mb->callout(&cb, mb->callout_data)) > 0) - RRETURN(MATCH_NOMATCH); - if (rrc < 0) RRETURN(rrc); } - ecode += callout_length; } break; - /* Recursion either matches the current regex, or some subexpression. The - offset data is the offset to the starting bracket from the start of the - whole pattern. (This is so that it works from duplicated subpatterns.) - - The state of the capturing groups is preserved over recursion, and - re-instated afterwards. We don't know how many are started and not yet - finished (offset_top records the completed total) so we just have to save - all the potential data. There may be up to 65535 such values, which is too - large to put on the stack, but using malloc for small numbers seems - expensive. As a compromise, the stack is used when there are no more than - OP_RECURSE_STACK_SAVE_MAX values to store; otherwise malloc is used. - - There are also other values that have to be saved. We use a chained - sequence of blocks that actually live on the stack. Thanks to Robin Houston - for the original version of this logic. It has, however, been hacked around - a lot, so he is not to blame for the current way it works. */ - - case OP_RECURSE: - { - ovecsave_frame *fr; - recursion_info *ri; - uint32_t recno; +#undef Lstart_eptr +#undef Lmin +#undef Lmax +#undef Lc +#undef Loc - callpat = mb->start_code + GET(ecode, 1); - recno = (callpat == mb->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE); - /* Check for repeating a pattern recursion without advancing the subject - pointer. This should catch convoluted mutual recursions. (Some simple - cases are caught at compile time.) */ - - for (ri = mb->recursive; ri != NULL; ri = ri->prevrec) - if (recno == ri->group_num && eptr == ri->subject_position) - RRETURN(PCRE2_ERROR_RECURSELOOP); + /* ===================================================================== */ + /* Match a bit-mapped character class, possibly repeatedly. These opcodes + are used when all the characters in the class have values in the range + 0-255, and either the matching is caseful, or the characters are in the + range 0-127 when UTF processing is enabled. The only difference between + OP_CLASS and OP_NCLASS occurs when a data character outside the range is + encountered. */ - /* Add to "recursing stack" */ +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lstart_eptr F->temp_sptr[0] +#define Lbyte_map_address F->temp_sptr[1] +#define Lbyte_map ((unsigned char *)Lbyte_map_address) - new_recursive.group_num = recno; - new_recursive.saved_capture_last = mb->capture_last; - new_recursive.subject_position = eptr; - new_recursive.prevrec = mb->recursive; - mb->recursive = &new_recursive; + case OP_NCLASS: + case OP_CLASS: + { + Lbyte_map_address = Fecode + 1; /* Save for matching */ + Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */ - /* Where to continue from afterwards */ + /* Look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats. */ - ecode += 1 + LINK_SIZE; + switch (*Fecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRPOSSTAR: + case OP_CRPOSPLUS: + case OP_CRPOSQUERY: + fc = *Fecode++ - OP_CRSTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + break; - /* When we are using the system stack for match() recursion we can call a - function that uses the system stack for preserving the ovector while - processing the pattern recursion, but only if the ovector is small - enough. */ + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CRPOSRANGE: + Lmin = GET2(Fecode, 1); + Lmax = GET2(Fecode, 1 + IMM2_SIZE); + if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ + reptype = rep_typ[*Fecode - OP_CRSTAR]; + Fecode += 1 + 2 * IMM2_SIZE; + break; -#ifndef HEAP_MATCH_RECURSE - if (mb->offset_end <= OP_RECURSE_STACK_SAVE_MAX) - { - rrc = op_recurse_ovecsave(eptr, callpat, mstart, offset_top, mb, - eptrb, rdepth); - mb->recursive = new_recursive.prevrec; - if (rrc != MATCH_MATCH && rrc != MATCH_ACCEPT) RRETURN(rrc); - - /* Set where we got to in the subject, and reset the start, in case - it was changed by \K. This *is* propagated back out of a recursion, - for Perl compatibility. */ - - eptr = mb->end_match_ptr; - mstart = mb->start_match_ptr; - break; /* End of processing OP_RECURSE */ + default: /* No repeat follows */ + Lmin = Lmax = 1; + break; } -#endif - /* If the ovector is too big, or if we are using the heap for match() - recursion, we have to use the heap for saving the ovector. Used ovecsave - frames are kept on a chain and re-used. This makes a small improvement in - execution time on Linux. */ - if (mb->ovecsave_chain != NULL) + /* First, ensure the minimum number of matches are present. */ + +#ifdef SUPPORT_UNICODE + if (utf) { - new_recursive.ovec_save = mb->ovecsave_chain->saved_ovec; - mb->ovecsave_chain = mb->ovecsave_chain->next; + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } } else +#endif + /* Not UTF mode */ { - fr = (ovecsave_frame *)(mb->memctl.malloc(sizeof(ovecsave_frame *) + - mb->offset_end * sizeof(PCRE2_SIZE), mb->memctl.memory_data)); - if (fr == NULL) RRETURN(PCRE2_ERROR_NOMEMORY); - new_recursive.ovec_save = fr->saved_ovec; + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + fc = *Feptr++; +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } } - memcpy(new_recursive.ovec_save, mb->ovector, - mb->offset_end * sizeof(PCRE2_SIZE)); + /* If Lmax == Lmin we are done. Continue with main loop. */ - /* Do the recursion. After processing each alternative, restore the - ovector data and the last captured value. This code has the same overall - logic as the code in the op_recurse_ovecsave() function, but is adapted - to use RMATCH/RRETURN and to release the heap block containing the saved - ovector. */ + if (Lmin == Lmax) continue; - cbegroup = (*callpat >= OP_SBRA); - do + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (reptype == REPTYPE_MIN) { - if (cbegroup) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, callpat + PRIV(OP_lengths)[*callpat], offset_top, - mb, eptrb, RM6); - memcpy(mb->ovector, new_recursive.ovec_save, - mb->offset_end * sizeof(PCRE2_SIZE)); - mb->capture_last = new_recursive.saved_capture_last; - mb->recursive = new_recursive.prevrec; - - if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) +#ifdef SUPPORT_UNICODE + if (utf) { - fr = (ovecsave_frame *) - ((uint8_t *)new_recursive.ovec_save - sizeof(ovecsave_frame *)); - fr->next = mb->ovecsave_chain; - mb->ovecsave_chain = fr; - - /* Set where we got to in the subject, and reset the start, in case - it was changed by \K. This *is* propagated back out of a recursion, - for Perl compatibility. */ - - eptr = mb->end_match_ptr; - mstart = mb->start_match_ptr; - goto RECURSION_MATCHED; /* Exit loop; end processing */ + for (;;) + { + RMATCH(Fecode, RM200); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } } - - /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a - recursion; they cause a NOMATCH for the entire recursion. These codes - are defined in a range that can be tested for. */ - - if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX) + else +#endif + /* Not UTF mode */ { - rrc = MATCH_NOMATCH; - goto RECURSION_RETURN; + for (;;) + { + RMATCH(Fecode, RM23); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + fc = *Feptr++; +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } } + /* Control never gets here */ + } - /* Any return code other than NOMATCH is an error. */ + /* If maximizing, find the longest possible run, then work backwards. */ - if (rrc != MATCH_NOMATCH) goto RECURSION_RETURN; - mb->recursive = &new_recursive; - callpat += GET(callpat, 1); - } - while (*callpat == OP_ALT); - - RECURSION_RETURN: - mb->recursive = new_recursive.prevrec; - fr = (ovecsave_frame *) - ((uint8_t *)new_recursive.ovec_save - sizeof(ovecsave_frame *)); - fr->next = mb->ovecsave_chain; - mb->ovecsave_chain = fr; - RRETURN(rrc); - } + else + { + Lstart_eptr = Feptr; - RECURSION_MATCHED: - break; +#ifdef SUPPORT_UNICODE + if (utf) + { + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc > 255) + { + if (Fop == OP_CLASS) break; + } + else + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) break; + Feptr += len; + } - /* An alternation is the end of a branch; scan along to find the end of the - bracketed group and go to there. */ + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - case OP_ALT: - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - break; + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ - /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a bracket group, - indicating that it may occur zero times. It may repeat infinitely, or not - at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets - with fixed upper repeat limits are compiled as a number of copies, with the - optional ones preceded by BRAZERO or BRAMINZERO. */ + for (;;) + { + RMATCH(Fecode, RM201); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */ + BACKCHAR(Feptr); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + fc = *Feptr; +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (fc > 255) + { + if (Fop == OP_CLASS) break; + } + else +#endif + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) break; + Feptr++; + } - case OP_BRAZERO: - next_ecode = ecode + 1; - RMATCH(eptr, next_ecode, offset_top, mb, eptrb, RM10); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - do next_ecode += GET(next_ecode, 1); while (*next_ecode == OP_ALT); - ecode = next_ecode + 1 + LINK_SIZE; - break; + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - case OP_BRAMINZERO: - next_ecode = ecode + 1; - do next_ecode += GET(next_ecode, 1); while (*next_ecode == OP_ALT); - RMATCH(eptr, next_ecode + 1+LINK_SIZE, offset_top, mb, eptrb, RM11); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode++; - break; - - case OP_SKIPZERO: - next_ecode = ecode+1; - do next_ecode += GET(next_ecode,1); while (*next_ecode == OP_ALT); - ecode = next_ecode + 1 + LINK_SIZE; - break; - - /* BRAPOSZERO occurs before a possessive bracket group. Don't do anything - here; just jump to the group, with allow_zero set TRUE. */ - - case OP_BRAPOSZERO: - op = *(++ecode); - allow_zero = TRUE; - if (op == OP_CBRAPOS || op == OP_SCBRAPOS) goto POSSESSIVE_CAPTURE; - goto POSSESSIVE_NON_CAPTURE; - - /* End of a group, repeated or non-repeating. */ - - case OP_KET: - case OP_KETRMIN: - case OP_KETRMAX: - case OP_KETRPOS: - prev = ecode - GET(ecode, 1); - - /* If this was a group that remembered the subject start, in order to break - infinite repeats of empty string matches, retrieve the subject start from - the chain. Otherwise, set it NULL. */ - - if (*prev >= OP_SBRA || *prev == OP_ONCE) - { - saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */ - eptrb = eptrb->epb_prev; /* Backup to previous group */ - } - else saved_eptr = NULL; - - /* If we are at the end of an assertion group or a non-capturing atomic - group, stop matching and return MATCH_MATCH, but record the current high - water mark for use by positive assertions. We also need to record the match - start in case it was changed by \K. */ - - if ((*prev >= OP_ASSERT && *prev <= OP_ASSERTBACK_NOT) || - *prev == OP_ONCE_NC) - { - mb->end_match_ptr = eptr; /* For ONCE_NC */ - mb->end_offset_top = offset_top; - mb->start_match_ptr = mstart; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_MATCH); /* Sets mb->mark */ - } - - /* For capturing groups we have to check the group number back at the start - and if necessary complete handling an extraction by setting the offsets and - bumping the high water mark. Whole-pattern recursion is coded as a recurse - into group 0, so it won't be picked up here. Instead, we catch it when the - OP_END is reached. Other recursion is handled here. We just have to record - the current subject position and start match pointer and give a MATCH - return. */ - - if (*prev == OP_CBRA || *prev == OP_SCBRA || - *prev == OP_CBRAPOS || *prev == OP_SCBRAPOS) - { - number = GET2(prev, 1+LINK_SIZE); - offset = number << 1; - - /* Handle a recursively called group. */ - - if (mb->recursive != NULL && mb->recursive->group_num == number) - { - mb->end_match_ptr = eptr; - mb->start_match_ptr = mstart; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_MATCH); - } - - /* Deal with capturing */ - - mb->capture_last = (mb->capture_last & OVFLMASK) | number; - if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else - { - /* If offset is greater than offset_top, it means that we are - "skipping" a capturing group, and that group's offsets must be marked - unset. In earlier versions of PCRE, all the offsets were unset at the - start of matching, but this doesn't work because atomic groups and - assertions can cause a value to be set that should later be unset. - Example: matching /(?>(a))b|(a)c/ against "ac". This sets group 1 as - part of the atomic group, but this is not on the final matching path, - so must be unset when 2 is set. (If there is no group 2, there is no - problem, because offset_top will then be 2, indicating no capture.) */ - - if (offset > offset_top) - { - register PCRE2_SIZE *iptr = mb->ovector + offset_top; - register PCRE2_SIZE *iend = mb->ovector + offset; - while (iptr < iend) *iptr++ = PCRE2_UNSET; + while (Feptr >= Lstart_eptr) + { + RMATCH(Fecode, RM24); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } } - /* Now make the extraction */ - - mb->ovector[offset] = mb->ovector[mb->offset_end - number]; - mb->ovector[offset+1] = eptr - mb->start_subject; - if (offset_top <= offset) offset_top = offset + 2; - } - } - - /* OP_KETRPOS is a possessive repeating ket. Remember the current position, - and return the MATCH_KETRPOS. This makes it possible to do the repeats one - at a time from the outer level, thus saving stack. This must precede the - empty string test - in this case that test is done at the outer level. */ - - if (*ecode == OP_KETRPOS) - { - mb->start_match_ptr = mstart; /* In case \K reset it */ - mb->end_match_ptr = eptr; - mb->end_offset_top = offset_top; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_KETRPOS); - } - - /* For an ordinary non-repeating ket, just continue at this level. This - also happens for a repeating ket if no characters were matched in the - group. This is the forcible breaking of infinite loops as implemented in - Perl 5.005. For a non-repeating atomic group that includes captures, - establish a backup point by processing the rest of the pattern at a lower - level. If this results in a NOMATCH return, pass MATCH_ONCE back to the - original OP_ONCE level, thereby bypassing intermediate backup points, but - resetting any captures that happened along the way. */ - - if (*ecode == OP_KET || eptr == saved_eptr) - { - if (*prev == OP_ONCE) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM12); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ - RRETURN(MATCH_ONCE); - } - ecode += 1 + LINK_SIZE; /* Carry on at this level */ - break; - } - - /* The normal repeating kets try the rest of the pattern or restart from - the preceding bracket, in the appropriate order. In the second case, we can - use tail recursion to avoid using another stack frame, unless we have an - an atomic group or an unlimited repeat of a group that can match an empty - string. */ - - if (*ecode == OP_KETRMIN) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM7); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (*prev == OP_ONCE) - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM8); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ - RRETURN(MATCH_ONCE); - } - if (*prev >= OP_SBRA) /* Could match an empty string */ - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM50); - RRETURN(rrc); - } - ecode = prev; - goto TAIL_RECURSE; - } - else /* OP_KETRMAX */ - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM13); - if (rrc == MATCH_ONCE && mb->once_target == prev) rrc = MATCH_NOMATCH; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (*prev == OP_ONCE) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM9); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; - RRETURN(MATCH_ONCE); + RRETURN(MATCH_NOMATCH); } - ecode += 1 + LINK_SIZE; - goto TAIL_RECURSE; } /* Control never gets here */ - /* Not multiline mode: start of subject assertion, unless notbol. */ - - case OP_CIRC: - if ((mb->moptions & PCRE2_NOTBOL) != 0 && eptr == mb->start_subject) - RRETURN(MATCH_NOMATCH); - - /* Start of subject assertion */ +#undef Lbyte_map_address +#undef Lbyte_map +#undef Lstart_eptr +#undef Lmin +#undef Lmax - case OP_SOD: - if (eptr != mb->start_subject) RRETURN(MATCH_NOMATCH); - ecode++; - break; - /* Multiline mode: start of subject unless notbol, or after any newline - except for one at the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */ + /* ===================================================================== */ + /* Match an extended character class. In the 8-bit library, this opcode is + encountered only when UTF-8 mode mode is supported. In the 16-bit and + 32-bit libraries, codepoints greater than 255 may be encountered even when + UTF is not supported. */ - case OP_CIRCM: - if ((mb->moptions & PCRE2_NOTBOL) != 0 && eptr == mb->start_subject) - RRETURN(MATCH_NOMATCH); - if (eptr != mb->start_subject && - ((eptr == mb->end_subject && - (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) || - !WAS_NEWLINE(eptr))) - RRETURN(MATCH_NOMATCH); - ecode++; - break; +#define Lstart_eptr F->temp_sptr[0] +#define Lxclass_data F->temp_sptr[1] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] - /* Start of match assertion */ +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: + { + Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */ + Fecode += GET(Fecode, 1); /* Advance past the item */ - case OP_SOM: - if (eptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH); - ecode++; - break; + switch (*Fecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRPOSSTAR: + case OP_CRPOSPLUS: + case OP_CRPOSQUERY: + fc = *Fecode++ - OP_CRSTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + break; - /* Reset the start of match point */ + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CRPOSRANGE: + Lmin = GET2(Fecode, 1); + Lmax = GET2(Fecode, 1 + IMM2_SIZE); + if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ + reptype = rep_typ[*Fecode - OP_CRSTAR]; + Fecode += 1 + 2 * IMM2_SIZE; + break; - case OP_SET_SOM: - mstart = eptr; - ecode++; - break; + default: /* No repeat follows */ + Lmin = Lmax = 1; + break; + } - /* Multiline mode: assert before any newline, or before end of subject - unless noteol is set. */ + /* First, ensure the minimum number of matches are present. */ - case OP_DOLLM: - if (eptr < mb->end_subject) - { - if (!IS_NEWLINE(eptr)) + for (i = 1; i <= Lmin; i++) { - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21TEST(eptr) == NLBLOCK->nl[0]) + if (Feptr >= mb->end_subject) { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - RRETURN(MATCH_NOMATCH); - } - } - else - { - if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); - SCHECK_PARTIAL(); - } - ecode++; - break; - - /* Not multiline mode: assert before a terminating newline or before end of - subject unless noteol is set. */ - - case OP_DOLL: - if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); - if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS; - - /* ... else fall through for endonly */ - - /* End of subject assertion (\z) */ - - case OP_EOD: - if (eptr < mb->end_subject) RRETURN(MATCH_NOMATCH); - SCHECK_PARTIAL(); - ecode++; - break; - - /* End of subject or ending \n assertion (\Z) */ - - case OP_EODN: - ASSERT_NL_OR_EOS: - if (eptr < mb->end_subject && - (!IS_NEWLINE(eptr) || eptr != mb->end_subject - mb->nllen)) - { - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21TEST(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + GETCHARINCTEST(fc, Feptr); + if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); } - RRETURN(MATCH_NOMATCH); - } - - /* Either at end of string or \n before end. */ - - SCHECK_PARTIAL(); - ecode++; - break; - /* Word boundary assertions */ + /* If Lmax == Lmin we can just continue with the main loop. */ - case OP_NOT_WORD_BOUNDARY: - case OP_WORD_BOUNDARY: - { + if (Lmin == Lmax) continue; - /* Find out if the previous and current characters are "word" characters. - It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to - be "non-word" characters. Remember the earliest consulted character for - partial matching. */ + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ -#ifdef SUPPORT_UNICODE - if (utf) + if (reptype == REPTYPE_MIN) { - /* Get status of previous character */ - - if (eptr == mb->start_subject) prev_is_word = FALSE; else - { - PCRE2_SPTR lastptr = eptr - 1; - BACKCHAR(lastptr); - if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr; - GETCHAR(c, lastptr); - if ((mb->poptions & PCRE2_UCP) != 0) - { - if (c == '_') prev_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - prev_is_word = (cat == ucp_L || cat == ucp_N); - } - } - else - prev_is_word = c < 256 && (mb->ctypes[c] & ctype_word) != 0; - } - - /* Get status of next character */ - - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - cur_is_word = FALSE; - } - else + for (;;) { - PCRE2_SPTR nextptr = eptr + 1; - FORWARDCHARTEST(nextptr, mb->end_subject); - if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr; - GETCHAR(c, eptr); - if ((mb->poptions & PCRE2_UCP) != 0) + RMATCH(Fecode, RM100); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { - if (c == '_') cur_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - cur_is_word = (cat == ucp_L || cat == ucp_N); - } + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - else - cur_is_word = c < 256 && (mb->ctypes[c] & ctype_word) != 0; + GETCHARINCTEST(fc, Feptr); + if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); } + /* Control never gets here */ } - else -#endif /* SUPPORT UTF */ - /* Not in UTF-8 mode, but we may still have PCRE2_UCP set, and for - consistency with the behaviour of \w we do use it in this case. */ + /* If maximizing, find the longest possible run, then work backwards. */ + else { - /* Get status of previous character */ - - if (eptr == mb->start_subject) prev_is_word = FALSE; else + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) { - if (eptr <= mb->start_used_ptr) mb->start_used_ptr = eptr - 1; -#ifdef SUPPORT_UNICODE - if ((mb->poptions & PCRE2_UCP) != 0) + int len = 1; + if (Feptr >= mb->end_subject) { - c = eptr[-1]; - if (c == '_') prev_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - prev_is_word = (cat == ucp_L || cat == ucp_N); - } + SCHECK_PARTIAL(); + break; } - else +#ifdef SUPPORT_UNICODE + GETCHARLENTEST(fc, Feptr, len); +#else + fc = *Feptr; #endif - prev_is_word = MAX_255(eptr[-1]) - && ((mb->ctypes[eptr[-1]] & ctype_word) != 0); + if (!PRIV(xclass)(fc, Lxclass_data, utf)) break; + Feptr += len; } - /* Get status of next character */ + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - cur_is_word = FALSE; - } - else + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + for(;;) { - if (eptr >= mb->last_used_ptr) mb->last_used_ptr = eptr + 1; + RMATCH(Fecode, RM101); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */ #ifdef SUPPORT_UNICODE - if ((mb->poptions & PCRE2_UCP) != 0) - { - c = *eptr; - if (c == '_') cur_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - cur_is_word = (cat == ucp_L || cat == ucp_N); - } - } - else + if (utf) BACKCHAR(Feptr); #endif - cur_is_word = MAX_255(*eptr) - && ((mb->ctypes[*eptr] & ctype_word) != 0); } - } - - /* Now see if the situation is what we want */ - - if ((*ecode++ == OP_WORD_BOUNDARY)? - cur_is_word == prev_is_word : cur_is_word != prev_is_word) RRETURN(MATCH_NOMATCH); - } - break; - - /* Match any single character type except newline; have to take care with - CRLF newlines and partial matching. */ + } - case OP_ANY: - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21TEST(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + /* Control never gets here */ } +#endif /* SUPPORT_WIDE_CHARS: end of XCLASS */ - /* Fall through */ - - /* Match any single character whatsoever. */ - - case OP_ALLANY: - if (eptr >= mb->end_subject) /* DO NOT merge the eptr++ here; it must */ - { /* not be updated before SCHECK_PARTIAL. */ - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr++; -#ifdef SUPPORT_UNICODE - if (utf) ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); -#endif - ecode++; - break; +#undef Lstart_eptr +#undef Lxclass_data +#undef Lmin +#undef Lmax - /* Match a single code unit, even in UTF-8 mode. This opcode really does - match any code unit, even newline. (It really should be called ANYCODEUNIT, - of course - the byte name is from pre-16 bit days.) */ - case OP_ANYBYTE: - if (eptr >= mb->end_subject) /* DO NOT merge the eptr++ here; it must */ - { /* not be updated before SCHECK_PARTIAL. */ - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr++; - ecode++; - break; + /* ===================================================================== */ + /* Match various character types when PCRE2_UCP is not set. These opcodes + are not generated when PCRE2_UCP is set - instead appropriate property + tests are compiled. */ case OP_NOT_DIGIT: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c < 256 && -#endif - (mb->ctypes[c] & ctype_digit) != 0 - ) + GETCHARINCTEST(fc, Feptr); + if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); - ecode++; + Fecode++; break; case OP_DIGIT: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c > 255 || -#endif - (mb->ctypes[c] & ctype_digit) == 0 - ) + GETCHARINCTEST(fc, Feptr); + if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); - ecode++; + Fecode++; break; case OP_NOT_WHITESPACE: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c < 256 && -#endif - (mb->ctypes[c] & ctype_space) != 0 - ) + GETCHARINCTEST(fc, Feptr); + if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); - ecode++; + Fecode++; break; case OP_WHITESPACE: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c > 255 || -#endif - (mb->ctypes[c] & ctype_space) == 0 - ) + GETCHARINCTEST(fc, Feptr); + if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); - ecode++; + Fecode++; break; case OP_NOT_WORDCHAR: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c < 256 && -#endif - (mb->ctypes[c] & ctype_word) != 0 - ) + GETCHARINCTEST(fc, Feptr); + if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); - ecode++; + Fecode++; break; case OP_WORDCHAR: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c > 255 || -#endif - (mb->ctypes[c] & ctype_word) == 0 - ) + GETCHARINCTEST(fc, Feptr); + if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); - ecode++; + Fecode++; break; case OP_ANYNL: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARINCTEST(fc, Feptr); + switch(fc) { default: RRETURN(MATCH_NOMATCH); case CHAR_CR: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); } - else if (UCHAR21TEST(eptr) == CHAR_LF) eptr++; + else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++; break; case CHAR_LF: @@ -2560,110 +2265,113 @@ for (;;) if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); break; } - ecode++; + Fecode++; break; case OP_NOT_HSPACE: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARINCTEST(fc, Feptr); + switch(fc) { HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ default: break; } - ecode++; + Fecode++; break; case OP_HSPACE: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARINCTEST(fc, Feptr); + switch(fc) { HSPACE_CASES: break; /* Byte and multibyte cases */ default: RRETURN(MATCH_NOMATCH); } - ecode++; + Fecode++; break; case OP_NOT_VSPACE: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARINCTEST(fc, Feptr); + switch(fc) { VSPACE_CASES: RRETURN(MATCH_NOMATCH); default: break; } - ecode++; + Fecode++; break; case OP_VSPACE: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARINCTEST(fc, Feptr); + switch(fc) { VSPACE_CASES: break; default: RRETURN(MATCH_NOMATCH); } - ecode++; + Fecode++; break; + #ifdef SUPPORT_UNICODE + + /* ===================================================================== */ /* Check the next character by Unicode property. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ case OP_PROP: case OP_NOTPROP: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); + GETCHARINCTEST(fc, Feptr); { const uint32_t *cp; - const ucd_record *prop = GET_UCD(c); + const ucd_record *prop = GET_UCD(fc); - switch(ecode[1]) + switch(Fecode[1]) { case PT_ANY: - if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; case PT_LAMP: if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || - prop->chartype == ucp_Lt) == (op == OP_NOTPROP)) + prop->chartype == ucp_Lt) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; case PT_GC: - if ((ecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (op == OP_PROP)) + if ((Fecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (Fop == OP_PROP)) RRETURN(MATCH_NOMATCH); break; case PT_PC: - if ((ecode[2] != prop->chartype) == (op == OP_PROP)) + if ((Fecode[2] != prop->chartype) == (Fop == OP_PROP)) RRETURN(MATCH_NOMATCH); break; case PT_SC: - if ((ecode[2] != prop->script) == (op == OP_PROP)) + if ((Fecode[2] != prop->script) == (Fop == OP_PROP)) RRETURN(MATCH_NOMATCH); break; @@ -2671,7 +2379,7 @@ for (;;) case PT_ALNUM: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || - PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (op == OP_NOTPROP)) + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; @@ -2681,16 +2389,16 @@ for (;;) case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ - switch(c) + switch(fc) { HSPACE_CASES: VSPACE_CASES: - if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; default: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == - (op == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); + (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; } break; @@ -2698,1652 +2406,984 @@ for (;;) case PT_WORD: if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || PRIV(ucp_gentype)[prop->chartype] == ucp_N || - c == CHAR_UNDERSCORE) == (op == OP_NOTPROP)) + fc == CHAR_UNDERSCORE) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; case PT_CLIST: - cp = PRIV(ucd_caseless_sets) + ecode[2]; + cp = PRIV(ucd_caseless_sets) + Fecode[2]; for (;;) { - if (c < *cp) - { if (op == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; } - if (c == *cp++) - { if (op == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } } + if (fc < *cp) + { if (Fop == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; } + if (fc == *cp++) + { if (Fop == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } } } break; case PT_UCNC: - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == (op == OP_NOTPROP)) + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; /* This should never occur */ default: - RRETURN(PCRE2_ERROR_INTERNAL); + return PCRE2_ERROR_INTERNAL; } - ecode += 3; + Fecode += 3; } break; + + /* ===================================================================== */ /* Match an extended Unicode sequence. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ case OP_EXTUNI: - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } else { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf, + NULL); } CHECK_PARTIAL(); - ecode++; + Fecode++; break; + #endif /* SUPPORT_UNICODE */ - /* Match a back reference, possibly repeatedly. Look past the end of the - item to see if there is repeat information following. + /* ===================================================================== */ + /* Match a single character type repeatedly. Note that the property type + does not need to be in a stack frame as it is not used within an RMATCH() + loop. */ - The OP_REF and OP_REFI opcodes are used for a reference to a numbered group - or to a non-duplicated named group. For a duplicated named group, OP_DNREF - and OP_DNREFI are used. In this case we must scan the list of groups to - which the name refers, and use the first one that is set. */ +#define Lstart_eptr F->temp_sptr[0] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lctype F->temp_32[2] +#define Lpropvalue F->temp_32[3] - case OP_DNREF: - case OP_DNREFI: - caseless = op == OP_DNREFI; - { - int count = GET2(ecode, 1+IMM2_SIZE); - PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size; - ecode += 1 + 2*IMM2_SIZE; + case OP_TYPEEXACT: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; - /* Initializing 'offset' avoids a compiler warning in the REF_REPEAT - code. */ + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + Lmin = 0; + Lmax = GET2(Fecode, 1); + reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX; + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; - offset = 0; - while (count-- > 0) - { - offset = GET2(slot, 0) << 1; - if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET) break; - slot += mb->name_entry_size; - } - } - goto REF_REPEAT; + case OP_TYPEPOSSTAR: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATTYPE; - case OP_REF: - case OP_REFI: - caseless = op == OP_REFI; - offset = GET2(ecode, 1) << 1; /* Doubled ref number */ - ecode += 1 + IMM2_SIZE; + case OP_TYPEPOSPLUS: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATTYPE; - /* Set up for repetition, or handle the non-repeated case */ + case OP_TYPEPOSQUERY: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATTYPE; - REF_REPEAT: - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - c = *ecode++ - OP_CRSTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; + case OP_TYPEPOSUPTO: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*ecode == OP_CRMINRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 1 + IMM2_SIZE); - if (max == 0) max = INT_MAX; - ecode += 1 + 2 * IMM2_SIZE; - break; + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + fc = *Fecode++ - OP_TYPESTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; - default: /* No repeat follows */ - { - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &length); - if (rc != 0) - { - if (rc > 0) eptr = mb->end_subject; /* Partial match */ - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - } - eptr += length; - continue; /* With the main loop */ - } + /* Common code for all repeated character type matches. */ - /* Handle repeated back references. If a set group has length zero, just - continue with the main loop, because it matches however many times. For an - unset reference, if the minimum is zero, we can also just continue. We an - also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset - group be have as a zero-length group. For any other unset cases, carrying - on will result in NOMATCH. */ + REPEATTYPE: + Lctype = *Fecode++; /* Code for the character type */ - if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET) - { - if (mb->ovector[offset] == mb->ovector[offset + 1]) continue; - } - else /* Group is not set */ +#ifdef SUPPORT_UNICODE + if (Lctype == OP_PROP || Lctype == OP_NOTPROP) { - if (min == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) - continue; + proptype = *Fecode++; + Lpropvalue = *Fecode++; } + else proptype = -1; +#endif - /* First, ensure the minimum number of matches are present. We get back - the length of the reference string explicitly rather than passing the - address of eptr, so that eptr can be a register variable. */ + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). The code for UTF mode is separated out for + tidiness, except for Unicode property tests. */ - for (i = 1; i <= min; i++) + if (Lmin > 0) { - PCRE2_SIZE slength; - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); - if (rc != 0) +#ifdef SUPPORT_UNICODE + if (proptype >= 0) /* Property tests in all modes */ { - if (rc > 0) eptr = mb->end_subject; /* Partial match */ - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += slength; - } + switch(proptype) + { + case PT_ANY: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + } + break; - /* If min = max, continue at the same level without recursion. - They are not both allowed to be zero. */ + case PT_LAMP: + for (i = 1; i <= Lmin; i++) + { + int chartype; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + chartype = UCD_CHARTYPE(fc); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; - if (min == max) continue; + case PT_GC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; - /* If minimizing, keep trying and advancing the pointer */ + case PT_PC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; - if (minimize) - { - for (fi = min;; fi++) - { - int rc; - PCRE2_SIZE slength; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM14); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); - if (rc != 0) - { - if (rc > 0) eptr = mb->end_subject; /* Partial match */ - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += slength; - } - /* Control never gets here */ - } + case PT_SC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; - /* If maximizing, find the longest string and work backwards, as long as - the matched lengths for each iteration are the same. */ + case PT_ALNUM: + for (i = 1; i <= Lmin; i++) + { + int category; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; - else - { - BOOL samelengths = TRUE; - pp = eptr; - length = mb->ovector[offset+1] - mb->ovector[offset]; + /* Perl space used to exclude VT, but from Perl 5.18 it is included, + which means that Perl space and POSIX space are now identical. PCRE + was changed at release 8.34. */ - for (i = min; i < max; i++) - { - PCRE2_SIZE slength; - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); + case PT_SPACE: /* Perl space */ + case PT_PXSPACE: /* POSIX space */ + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + HSPACE_CASES: + VSPACE_CASES: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; - if (rc != 0) - { - /* Can't use CHECK_PARTIAL because we don't want to update eptr in - the soft partial matching case. */ + default: + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + } + } + break; - if (rc > 0 && mb->partial != 0 && - mb->end_subject > mb->start_used_ptr) + case PT_WORD: + for (i = 1; i <= Lmin; i++) { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + int category; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N || + fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } break; - } - if (slength != length) samelengths = FALSE; - eptr += slength; - } + case PT_CLIST: + for (i = 1; i <= Lmin; i++) + { + const uint32_t *cp; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; + for (;;) + { + if (fc < *cp) + { + if (Lctype == OP_NOTPROP) break; + RRETURN(MATCH_NOMATCH); + } + if (fc == *cp++) + { + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + } + } + } + break; - /* If the length matched for each repetition is the same as the length of - the captured group, we can easily work backwards. This is the normal - case. However, in caseless UTF-8 mode there are pairs of case-equivalent - characters whose lengths (in terms of code units) differ. However, this - is very rare, so we handle it by re-matching fewer and fewer times. */ + case PT_UCNC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; - if (samelengths) - { - while (eptr >= pp) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM15); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr -= length; + /* This should not occur */ + + default: + return PCRE2_ERROR_INTERNAL; } } - /* The rare case of non-matching lengths. Re-scan the repetition for each - iteration. We know that match_ref() will succeed every time. */ + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ - else + else if (Lctype == OP_EXTUNI) { - max = i; - for (;;) + for (i = 1; i <= Lmin; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM68); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr == pp) break; /* Failed after minimal repetition */ - eptr = pp; - max--; - for (i = min; i < max; i++) + if (Feptr >= mb->end_subject) { - PCRE2_SIZE slength; - (void)match_ref(offset, offset_top, eptr, mb, caseless, &slength); - eptr += slength; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + else + { + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, + mb->end_subject, utf, NULL); + } + CHECK_PARTIAL(); } } + else +#endif /* SUPPORT_UNICODE */ - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - /* Match a bit-mapped character class, possibly repeatedly. This op code is - used when all the characters in the class have values in the range 0-255, - and either the matching is caseful, or the characters are in the range - 0-127 when UTF-8 processing is enabled. The only difference between - OP_CLASS and OP_NCLASS occurs when a data character outside the range is - encountered. - - First, look past the end of the item to see if there is repeat information - following. Then obey similar code to character type repeats - written out - again for speed. */ - - case OP_NCLASS: - case OP_CLASS: - { - /* The data variable is saved across frames, so the byte map needs to - be stored there. */ -#define BYTE_MAP ((uint8_t *)data) - data = ecode + 1; /* Save for matching */ - ecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */ +/* Handle all other cases in UTF mode */ - switch (*ecode) +#ifdef SUPPORT_UNICODE + if (utf) switch(Lctype) { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSPLUS: - case OP_CRPOSQUERY: - c = *ecode++ - OP_CRSTAR; - if (c < OP_CRPOSSTAR - OP_CRSTAR) minimize = (c & 1) != 0; - else possessive = TRUE; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; + case OP_ANY: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } break; - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - minimize = (*ecode == OP_CRMINRANGE); - possessive = (*ecode == OP_CRPOSRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 1 + IMM2_SIZE); - if (max == 0) max = INT_MAX; - ecode += 1 + 2 * IMM2_SIZE; + case OP_ALLANY: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } break; - default: /* No repeat follows */ - min = max = 1; + case OP_ANYBYTE: + if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH); + Feptr += Lmin; break; - } - /* First, ensure the minimum number of matches are present. */ - -#ifdef SUPPORT_UNICODE - if (utf) - { - for (i = 1; i <= min; i++) + case OP_ANYNL: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINC(c, eptr); - if (c > 255) + GETCHARINC(fc, Feptr); + switch(fc) { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + default: RRETURN(MATCH_NOMATCH); + + case CHAR_CR: + if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC + case 0x2028: + case 0x2029: +#endif /* Not EBCDIC */ + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + break; } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); } - } - else -#endif - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - c = *eptr++; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) + GETCHARINC(fc, Feptr); + switch(fc) { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + HSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); } - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == max) continue; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ + break; - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) + case OP_HSPACE: + for (i = 1; i <= Lmin; i++) { - for (fi = min;; fi++) + if (Feptr >= mb->end_subject) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM16); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - if (c > 255) - { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); - } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) + GETCHARINC(fc, Feptr); + switch(fc) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM17); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - c = *eptr++; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) - { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); - } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + HSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); } } - /* Control never gets here */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ - - else - { - pp = eptr; + break; -#ifdef SUPPORT_UNICODE - if (utf) + case OP_NOT_VSPACE: + for (i = 1; i <= Lmin; i++) { - for (i = min; i < max; i++) + if (Feptr >= mb->end_subject) { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c > 255) - { - if (op == OP_CLASS) break; - } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; - eptr += len; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - - if (possessive) continue; /* No backtracking */ - - for (;;) + GETCHARINC(fc, Feptr); + switch(fc) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM18); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ - BACKCHAR(eptr); + VSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; } } - else -#endif - /* Not UTF mode */ + break; + + case OP_VSPACE: + for (i = 1; i <= Lmin; i++) { - for (i = min; i < max; i++) + if (Feptr >= mb->end_subject) { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - c = *eptr; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) - { - if (op == OP_CLASS) break; - } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; - eptr++; + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } - - if (possessive) continue; /* No backtracking */ - - while (eptr >= pp) + GETCHARINC(fc, Feptr); + switch(fc) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM19); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; + VSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); } } - - RRETURN(MATCH_NOMATCH); - } -#undef BYTE_MAP - } - /* Control never gets here */ - - - /* Match an extended character class. In the 8-bit library, this opcode is - encountered only when UTF-8 mode mode is supported. In the 16-bit and - 32-bit libraries, codepoints greater than 255 may be encountered even when - UTF is not supported. */ - -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - { - data = ecode + 1 + LINK_SIZE; /* Save for matching */ - ecode += GET(ecode, 1); /* Advance past the item */ - - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSPLUS: - case OP_CRPOSQUERY: - c = *ecode++ - OP_CRSTAR; - if (c < OP_CRPOSSTAR - OP_CRSTAR) minimize = (c & 1) != 0; - else possessive = TRUE; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; break; - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - minimize = (*ecode == OP_CRMINRANGE); - possessive = (*ecode == OP_CRPOSRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 1 + IMM2_SIZE); - if (max == 0) max = INT_MAX; - ecode += 1 + 2 * IMM2_SIZE; + case OP_NOT_DIGIT: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + } break; - default: /* No repeat follows */ - min = max = 1; + case OP_DIGIT: + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + /* No need to skip more code units - we know it has only one. */ + } break; - } - - /* First, ensure the minimum number of matches are present. */ - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) + case OP_NOT_WHITESPACE: + for (i = 1; i <= Lmin; i++) { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } - GETCHARINCTEST(c, eptr); - if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == max) continue; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ + break; - if (minimize) - { - for (fi = min;; fi++) + case OP_WHITESPACE: + for (i = 1; i <= Lmin; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM20); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + uint32_t cc; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); + cc = UCHAR21(Feptr); + if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + /* No need to skip more code units - we know it has only one. */ } - /* Control never gets here */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ + break; - else - { - pp = eptr; - for (i = min; i < max; i++) + case OP_NOT_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - int len = 1; - if (eptr >= mb->end_subject) + uint32_t cc; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } -#ifdef SUPPORT_UNICODE - GETCHARLENTEST(c, eptr, len); -#else - c = *eptr; -#endif - if (!PRIV(xclass)(c, data, utf)) break; - eptr += len; + cc = UCHAR21(Feptr); + if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); } + break; - if (possessive) continue; /* No backtracking */ - - for(;;) + case OP_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM21); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ -#ifdef SUPPORT_UNICODE - if (utf) BACKCHAR(eptr); -#endif - } - RRETURN(MATCH_NOMATCH); - } - - /* Control never gets here */ - } -#endif /* End of XCLASS */ - - /* Match a single character, casefully */ - - case OP_CHAR: -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - ecode++; - GETCHARLEN(fc, ecode, length); - if (length > (PCRE2_SIZE)(mb->end_subject - eptr)) - { - CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ - RRETURN(MATCH_NOMATCH); - } - for (; length > 0; length--) - { - if (*ecode++ != UCHAR21INC(eptr)) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - if (mb->end_subject - eptr < 1) - { - SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ - RRETURN(MATCH_NOMATCH); - } - if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH); - ecode += 2; - } - break; - - /* Match a single character, caselessly. If we are at the end of the - subject, give up immediately. */ - - case OP_CHARI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - ecode++; - GETCHARLEN(fc, ecode, length); - - /* If the pattern character's value is < 128, we have only one byte, and - we know that its other case must also be one byte long, so we can use the - fast lookup table. We know that there is at least one byte left in the - subject. */ - - if (fc < 128) - { - uint32_t cc = UCHAR21(eptr); - if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH); - ecode++; - eptr++; - } - - /* Otherwise we must pick up the subject character. Note that we cannot - use the value of "length" to check for sufficient bytes left, because the - other case of the character may have more or fewer bytes. */ - - else - { - uint32_t dc; - GETCHARINC(dc, eptr); - ecode += length; - - /* If we have Unicode property support, we can use it to test the other - case of the character, if there is one. */ - - if (fc != dc) - { -#ifdef SUPPORT_UNICODE - if (dc != UCD_OTHERCASE(fc)) -#endif + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); + Feptr++; + /* No need to skip more code units - we know it has only one. */ } - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Not UTF mode */ - { - if (TABLE_GET(ecode[1], mb->lcc, ecode[1]) - != TABLE_GET(*eptr, mb->lcc, *eptr)) RRETURN(MATCH_NOMATCH); - eptr++; - ecode += 2; - } - break; - - /* Match a single character repeatedly. */ - - case OP_EXACT: - case OP_EXACTI: - min = max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATCHAR; - - case OP_POSUPTO: - case OP_POSUPTOI: - possessive = TRUE; - /* Fall through */ - - case OP_UPTO: - case OP_UPTOI: - case OP_MINUPTO: - case OP_MINUPTOI: - min = 0; - max = GET2(ecode, 1); - minimize = *ecode == OP_MINUPTO || *ecode == OP_MINUPTOI; - ecode += 1 + IMM2_SIZE; - goto REPEATCHAR; - - case OP_POSSTAR: - case OP_POSSTARI: - possessive = TRUE; - min = 0; - max = INT_MAX; - ecode++; - goto REPEATCHAR; - - case OP_POSPLUS: - case OP_POSPLUSI: - possessive = TRUE; - min = 1; - max = INT_MAX; - ecode++; - goto REPEATCHAR; - - case OP_POSQUERY: - case OP_POSQUERYI: - possessive = TRUE; - min = 0; - max = 1; - ecode++; - goto REPEATCHAR; - - case OP_STAR: - case OP_STARI: - case OP_MINSTAR: - case OP_MINSTARI: - case OP_PLUS: - case OP_PLUSI: - case OP_MINPLUS: - case OP_MINPLUSI: - case OP_QUERY: - case OP_QUERYI: - case OP_MINQUERY: - case OP_MINQUERYI: - c = *ecode++ - ((op < OP_STARI)? OP_STAR : OP_STARI); - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single-character matches. We first check - for the minimum number of characters. If the minimum equals the maximum, we - are done. Otherwise, if minimizing, check the rest of the pattern for a - match; if there isn't one, advance up to the maximum, one character at a - time. - - If maximizing, advance up to the maximum number of matching characters, - until eptr is past the end of the maximum run. If possessive, we are - then done (no backing up). Otherwise, match at this position; anything - other than no match is immediately returned. For nomatch, back up one - character, unless we are matching \R and the last thing matched was - \r\n, in which case, back up two bytes. When we reach the first optional - character position, we can save stack by doing a tail recurse. + break; - The various UTF/non-UTF and caseful/caseless cases are handled separately, - for speed. */ + default: + return PCRE2_ERROR_INTERNAL; + } /* End switch(Lctype) */ - REPEATCHAR: -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - charptr = ecode; - GETCHARLEN(fc, ecode, length); - ecode += length; + else +#endif /* SUPPORT_UNICODE */ - /* Handle multibyte character matching specially here. There is - support for caseless matching if UCP support is present. */ + /* Code for the non-UTF case for minimum matching of operators other + than OP_PROP and OP_NOTPROP. */ - if (length > 1) + switch(Lctype) { - uint32_t othercase; - if (op >= OP_STARI && /* Caseless */ - (othercase = UCD_OTHERCASE(fc)) != fc) - oclength = PRIV(ord2utf)(othercase, occhars); - else oclength = 0; - - for (i = 1; i <= min; i++) + case OP_ANY: + for (i = 1; i <= Lmin; i++) { - if (eptr <= mb->end_subject - length && - memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length; - else if (oclength > 0 && - eptr <= mb->end_subject - oclength && - memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength; - else + if (Feptr >= mb->end_subject) { - CHECK_PARTIAL(); + SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } + if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + *Feptr == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + Feptr++; } + break; - if (min == max) continue; - - if (minimize) + case OP_ALLANY: + if (Feptr > mb->end_subject - Lmin) { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM22); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr <= mb->end_subject - length && - memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length; - else if (oclength > 0 && - eptr <= mb->end_subject - oclength && - memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength; - else - { - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - } - /* Control never gets here */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + Feptr += Lmin; + break; - else /* Maximize */ + /* This OP_ANYBYTE case will never be reached because \C gets turned + into OP_ALLANY in non-UTF mode. Cut out the code so that coverage + reports don't complain about it's never being used. */ + +/* case OP_ANYBYTE: +* if (Feptr > mb->end_subject - Lmin) +* { +* SCHECK_PARTIAL(); +* RRETURN(MATCH_NOMATCH); +* } +* Feptr += Lmin; +* break; +*/ + case OP_ANYNL: + for (i = 1; i <= Lmin; i++) { - pp = eptr; - for (i = min; i < max; i++) - { - if (eptr <= mb->end_subject - length && - memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length; - else if (oclength > 0 && - eptr <= mb->end_subject - oclength && - memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength; - else - { - CHECK_PARTIAL(); - break; - } + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); } + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); - if (possessive) continue; /* No backtracking */ + case CHAR_CR: + if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; + break; - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ + case CHAR_LF: + break; - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM23); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#if PCRE2_CODE_UNIT_WIDTH != 8 + case 0x2028: + case 0x2029: +#endif + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + break; } } - /* Control never gets here */ - } - - /* If the length of a UTF-8 character is 1, we fall through here, and - obey the code as for non-UTF-8 characters below, though in this case the - value of fc will always be < 128. */ - } - else -#endif /* SUPPORT_UNICODE */ - - /* When not in UTF-8 mode, load a single-byte character. */ - fc = *ecode++; - - /* The value of fc at this point is always one character, though we may - or may not be in UTF mode. The code is duplicated for the caseless and - caseful cases, for speed, since matching characters is likely to be quite - common. First, ensure the minimum number of matches are present. If min = - max, continue at the same level without recursing. Otherwise, if - minimizing, keep trying the rest of the expression and advancing one - matching character if failing, up to the maximum. Alternatively, if - maximizing, find the maximum number of characters and work backwards. */ - - if (op >= OP_STARI) /* Caseless */ - { -#if PCRE2_CODE_UNIT_WIDTH == 8 - /* fc must be < 128 if UTF is enabled. */ - foc = mb->fcc[fc]; -#else -#ifdef SUPPORT_UNICODE - if (utf && fc > 127) - foc = UCD_OTHERCASE(fc); - else -#endif /* SUPPORT_UNICODE */ - foc = TABLE_GET(fc, mb->fcc, fc); -#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + break; - for (i = 1; i <= min; i++) - { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); - eptr++; - } - if (min == max) continue; - if (minimize) - { - for (fi = min;; fi++) + case OP_NOT_HSPACE: + for (i = 1; i <= Lmin; i++) { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM24); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); - eptr++; + switch(*Feptr++) + { + default: break; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + } } - /* Control never gets here */ - } - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) + break; + + case OP_HSPACE: + for (i = 1; i <= Lmin; i++) { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif break; } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) break; - eptr++; - } - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM25); - eptr--; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); } - /* Control never gets here */ - } - } - - /* Caseful comparisons (includes all multi-byte characters) */ + break; - else - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) + case OP_NOT_VSPACE: + for (i = 1; i <= Lmin; i++) { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + default: break; + } } - if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH); - } - - if (min == max) continue; + break; - if (minimize) - { - for (fi = min;; fi++) + case OP_VSPACE: + for (i = 1; i <= Lmin; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM26); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH); + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + break; + } } - /* Control never gets here */ - } - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } - if (fc != UCHAR21TEST(eptr)) break; - eptr++; + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - if (possessive) continue; /* No backtracking */ - for (;;) + break; + + case OP_DIGIT: + for (i = 1; i <= Lmin; i++) { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM27); - eptr--; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - /* Control never gets here */ - } - } - /* Control never gets here */ - - /* Match a negated single one-byte character. The character we are - checking can be multibyte. */ - - case OP_NOT: - case OP_NOTI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t ch, och; - - ecode++; - GETCHARINC(ch, ecode); - GETCHARINC(c, eptr); - - if (op == OP_NOT) - { - if (ch == c) RRETURN(MATCH_NOMATCH); - } - else - { - if (ch > 127) - och = UCD_OTHERCASE(ch); - else - och = TABLE_GET(ch, mb->fcc, ch); - if (ch == c || och == c) RRETURN(MATCH_NOMATCH); - } - } - else -#endif /* SUPPORT_UNICODE */ - { - register uint32_t ch = ecode[1]; - c = *eptr++; - if (ch == c || (op == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == c)) - RRETURN(MATCH_NOMATCH); - ecode += 2; - } - break; - - /* Match a negated single one-byte character repeatedly. This is almost a - repeat of the code for a repeated single character, but I haven't found a - nice way of commoning these up that doesn't require a test of the - positive/negative option for each character match. Maybe that wouldn't add - very much to the time taken, but character matching *is* what this is all - about... */ - - case OP_NOTEXACT: - case OP_NOTEXACTI: - min = max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATNOTCHAR; - - case OP_NOTUPTO: - case OP_NOTUPTOI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - min = 0; - max = GET2(ecode, 1); - minimize = *ecode == OP_NOTMINUPTO || *ecode == OP_NOTMINUPTOI; - ecode += 1 + IMM2_SIZE; - goto REPEATNOTCHAR; - - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - possessive = TRUE; - min = 0; - max = INT_MAX; - ecode++; - goto REPEATNOTCHAR; - - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - possessive = TRUE; - min = 1; - max = INT_MAX; - ecode++; - goto REPEATNOTCHAR; - - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - possessive = TRUE; - min = 0; - max = 1; - ecode++; - goto REPEATNOTCHAR; - - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - possessive = TRUE; - min = 0; - max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATNOTCHAR; - - case OP_NOTSTAR: - case OP_NOTSTARI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - c = *ecode++ - ((op >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single-byte matches. */ - - REPEATNOTCHAR: - GETCHARINCTEST(fc, ecode); + break; - /* The code is duplicated for the caseless and caseful cases, for speed, - since matching characters is likely to be quite common. First, ensure the - minimum number of matches are present. If min = max, continue at the same - level without recursing. Otherwise, if minimizing, keep trying the rest of - the expression and advancing one matching character if failing, up to the - maximum. Alternatively, if maximizing, find the maximum number of - characters and work backwards. */ + case OP_NOT_WHITESPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; - if (op >= OP_NOTSTARI) /* Caseless */ - { -#ifdef SUPPORT_UNICODE - if (utf && fc > 127) - foc = UCD_OTHERCASE(fc); - else -#endif /* SUPPORT_UNICODE */ - foc = TABLE_GET(fc, mb->fcc, fc); + case OP_WHITESPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = 1; i <= min; i++) + case OP_NOT_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINC(d, eptr); - if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH); + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } - } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) + break; + + case OP_WORDCHAR: + for (i = 1; i <= Lmin; i++) { - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); - eptr++; + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; } + break; + + default: + return PCRE2_ERROR_INTERNAL; } + } - if (min == max) continue; + /* If Lmin = Lmax we are done. Continue with the main loop. */ - if (minimize) - { + if (Lmin == Lmax) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. */ + + if (reptype == REPTYPE_MIN) + { #ifdef SUPPORT_UNICODE - if (utf) + if (proptype >= 0) + { + switch(proptype) { - register uint32_t d; - for (fi = min;; fi++) + case PT_ANY: + for (;;) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM28); + RMATCH(Fecode, RM208); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINC(d, eptr); - if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(fc, Feptr); + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); } - } - else -#endif /*SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (fi = min;; fi++) + /* Control never gets here */ + + case PT_LAMP: + for (;;) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM29); + int chartype; + RMATCH(Fecode, RM209); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); - eptr++; + GETCHARINCTEST(fc, Feptr); + chartype = UCD_CHARTYPE(fc); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - } - /* Control never gets here */ - } - - /* Maximize case */ - - else - { - pp = eptr; + /* Control never gets here */ -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = min; i < max; i++) + case PT_GC: + for (;;) { - int len = 1; - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM210); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } - GETCHARLEN(d, eptr, len); - if (fc == d || (uint32_t)foc == d) break; - eptr += len; + GETCHARINCTEST(fc, Feptr); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ + /* Control never gets here */ - for(;;) + case PT_PC: + for (;;) { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM30); + RMATCH(Fecode, RM211); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (i = min; i < max; i++) + /* Control never gets here */ + + case PT_SC: + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM212); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - break; + RRETURN(MATCH_NOMATCH); } - if (fc == *eptr || foc == *eptr) break; - eptr++; + GETCHARINCTEST(fc, Feptr); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - if (possessive) continue; /* No backtracking */ + /* Control never gets here */ + + case PT_ALNUM: for (;;) { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM31); + int category; + RMATCH(Fecode, RM213); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == + (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); } - } - /* Control never gets here */ - } - } - - /* Caseful comparisons */ - - else - { -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(d, eptr); - if (fc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc == *eptr++) RRETURN(MATCH_NOMATCH); - } - } - - if (min == max) continue; - - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM32); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(d, eptr); - if (fc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM33); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc == *eptr++) RRETURN(MATCH_NOMATCH); - } - } - /* Control never gets here */ - } - - /* Maximize case */ - - else - { - pp = eptr; - -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(d, eptr, len); - if (fc == d) break; - eptr += len; - } - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM34); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (fc == *eptr) break; - eptr++; - } - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM35); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - } - } - /* Control never gets here */ - } - } - /* Control never gets here */ - - /* Match a single character type repeatedly; several different opcodes - share code. This is very similar to the code for single characters, but we - repeat it in the interests of efficiency. */ - - case OP_TYPEEXACT: - min = max = GET2(ecode, 1); - minimize = TRUE; - ecode += 1 + IMM2_SIZE; - goto REPEATTYPE; - - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - min = 0; - max = GET2(ecode, 1); - minimize = *ecode == OP_TYPEMINUPTO; - ecode += 1 + IMM2_SIZE; - goto REPEATTYPE; - - case OP_TYPEPOSSTAR: - possessive = TRUE; - min = 0; - max = INT_MAX; - ecode++; - goto REPEATTYPE; - - case OP_TYPEPOSPLUS: - possessive = TRUE; - min = 1; - max = INT_MAX; - ecode++; - goto REPEATTYPE; - - case OP_TYPEPOSQUERY: - possessive = TRUE; - min = 0; - max = 1; - ecode++; - goto REPEATTYPE; - - case OP_TYPEPOSUPTO: - possessive = TRUE; - min = 0; - max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATTYPE; - - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - c = *ecode++ - OP_TYPESTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single character type matches. Note that - in UTF-8 mode, '.' matches a character of any length, but for the other - character types, the valid characters are all one-byte long. */ - - REPEATTYPE: - ctype = *ecode++; /* Code for the character type */ - -#ifdef SUPPORT_UNICODE - if (ctype == OP_PROP || ctype == OP_NOTPROP) - { - prop_fail_result = ctype == OP_NOTPROP; - prop_type = *ecode++; - prop_value = *ecode++; - } - else prop_type = -1; -#endif - - /* First, ensure the minimum number of matches are present. Use inline - code for maximizing the speed, and do the type test once at the start - (i.e. keep it out of the loop). Separate the UTF-8 code completely as that - is tidier. Also separate the UCP code, which can be the same for both UTF-8 - and single-bytes. */ - - if (min > 0) - { -#ifdef SUPPORT_UNICODE - if (prop_type >= 0) - { - switch(prop_type) - { - case PT_ANY: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - } - break; - - case PT_LAMP: - for (i = 1; i <= min; i++) - { - int chartype; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - chartype = UCD_CHARTYPE(c); - if ((chartype == ucp_Lu || - chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_GC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_PC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_SC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_ALNUM: - for (i = 1; i <= min; i++) - { - int category; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; + /* Control never gets here */ /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE @@ -4351,744 +3391,522 @@ for (;;) case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM214); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARINCTEST(fc, Feptr); + switch(fc) { HSPACE_CASES: VSPACE_CASES: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); break; default: - if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result) + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); break; } } - break; + /* Control never gets here */ case PT_WORD: - for (i = 1; i <= min; i++) + for (;;) { int category; - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM215); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N || c == CHAR_UNDERSCORE) - == prop_fail_result) + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || + category == ucp_N || + fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } - break; + /* Control never gets here */ case PT_CLIST: - for (i = 1; i <= min; i++) + for (;;) { const uint32_t *cp; - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM216); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - cp = PRIV(ucd_caseless_sets) + prop_value; + GETCHARINCTEST(fc, Feptr); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; for (;;) { - if (c < *cp) - { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } } - if (c == *cp++) - { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; } + if (fc < *cp) + { + if (Lctype == OP_NOTPROP) break; + RRETURN(MATCH_NOMATCH); + } + if (fc == *cp++) + { + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + } } } - break; + /* Control never gets here */ case PT_UCNC: - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM217); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - GETCHARINCTEST(c, eptr); - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == prop_fail_result) + GETCHARINCTEST(fc, Feptr); + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Lctype == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); } - break; - - /* This should not occur */ + /* Control never gets here */ + /* This should never occur */ default: - RRETURN(PCRE2_ERROR_INTERNAL); + return PCRE2_ERROR_INTERNAL; } } /* Match extended Unicode sequences. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ - else if (ctype == OP_EXTUNI) + else if (Lctype == OP_EXTUNI) { - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM218); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } else { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, + utf, NULL); } CHECK_PARTIAL(); } } - else #endif /* SUPPORT_UNICODE */ -/* Handle all other cases when the coding is UTF-8 */ + /* UTF mode for non-property testing character types. */ #ifdef SUPPORT_UNICODE - if (utf) switch(ctype) + if (utf) { - case OP_ANY: - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM219); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21(eptr) == NLBLOCK->nl[0]) + if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + GETCHARINC(fc, Feptr); + switch(Lctype) { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; + case OP_ANY: /* This is the non-NL case */ + if (mb->partial != 0 && /* Take care with CRLF partial */ + Feptr >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + fc == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + break; - case OP_ALLANY: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; - - case OP_ANYBYTE: - if (eptr > mb->end_subject - min) RRETURN(MATCH_NOMATCH); - eptr += min; - break; + case OP_ALLANY: + case OP_ANYBYTE: + break; - case OP_ANYNL: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - default: RRETURN(MATCH_NOMATCH); + case OP_ANYNL: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); - case CHAR_CR: - if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++; - break; + case CHAR_CR: + if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; + break; - case CHAR_LF: - break; + case CHAR_LF: + break; - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: #ifndef EBCDIC - case 0x2028: - case 0x2029: + case 0x2028: + case 0x2029: #endif /* Not EBCDIC */ - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) + RRETURN(MATCH_NOMATCH); + break; + } break; - } - } - break; - case OP_NOT_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ - default: break; - } - } - break; + case OP_NOT_HSPACE: + switch(fc) + { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + break; - case OP_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - HSPACE_CASES: break; /* Byte and multibyte cases */ - default: RRETURN(MATCH_NOMATCH); - } - } - break; + case OP_HSPACE: + switch(fc) + { + HSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + break; - case OP_NOT_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - VSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - } - break; + case OP_NOT_VSPACE: + switch(fc) + { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + break; - case OP_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - VSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); - } - } - break; + case OP_VSPACE: + switch(fc) + { + VSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + break; - case OP_NOT_DIGIT: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - if (c < 128 && (mb->ctypes[c] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - } - break; + case OP_NOT_DIGIT: + if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_DIGIT: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - /* No need to skip more bytes - we know it's a 1-byte character */ - } - break; + case OP_DIGIT: + if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_NOT_WHITESPACE: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; + case OP_NOT_WHITESPACE: + if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_WHITESPACE: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - /* No need to skip more bytes - we know it's a 1-byte character */ - } - break; + case OP_WHITESPACE: + if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_NOT_WORDCHAR: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; + case OP_NOT_WORDCHAR: + if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_WORDCHAR: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + case OP_WORDCHAR: + if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + return PCRE2_ERROR_INTERNAL; } - cc = UCHAR21(eptr); - if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - /* No need to skip more bytes - we know it's a 1-byte character */ } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } /* End switch(ctype) */ - + } else -#endif /* SUPPORT_UNICODE */ - - /* Code for the non-UTF-8 case for minimum matching of operators other - than OP_PROP and OP_NOTPROP. */ +#endif /* SUPPORT_UNICODE */ - switch(ctype) + /* Not UTF mode */ { - case OP_ANY: - for (i = 1; i <= min; i++) + for (;;) { - if (eptr >= mb->end_subject) + RMATCH(Fecode, RM33); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); RRETURN(MATCH_NOMATCH); } - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - *eptr == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - } - break; - - case OP_ALLANY: - if (eptr > mb->end_subject - min) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += min; - break; - - case OP_ANYBYTE: - if (eptr > mb->end_subject - min) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += min; - break; - - case OP_ANYNL: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); + if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) + fc = *Feptr++; + switch(Lctype) { - default: RRETURN(MATCH_NOMATCH); - - case CHAR_CR: - if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++; + case OP_ANY: /* This is the non-NL case */ + if (mb->partial != 0 && /* Take care with CRLF partial */ + Feptr >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + fc == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } break; - case CHAR_LF: + case OP_ALLANY: + case OP_ANYBYTE: break; - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#if PCRE2_CODE_UNIT_WIDTH != 8 - case 0x2028: - case 0x2029: -#endif - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); - break; - } - } - break; + case OP_ANYNL: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); - case OP_NOT_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: break; - HSPACE_BYTE_CASES: + case CHAR_CR: + if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: #if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: + case 0x2028: + case 0x2029: #endif - RRETURN(MATCH_NOMATCH); - } - } - break; + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) + RRETURN(MATCH_NOMATCH); + break; + } + break; - case OP_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); - HSPACE_BYTE_CASES: + case OP_NOT_HSPACE: + switch(fc) + { + default: break; + HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: + HSPACE_MULTIBYTE_CASES: #endif + RRETURN(MATCH_NOMATCH); + } break; - } - } - break; - case OP_NOT_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - VSPACE_BYTE_CASES: + case OP_HSPACE: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: + HSPACE_MULTIBYTE_CASES: #endif - RRETURN(MATCH_NOMATCH); - default: break; - } - } - break; + break; + } + break; - case OP_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); - VSPACE_BYTE_CASES: + case OP_NOT_VSPACE: + switch(fc) + { + default: break; + VSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: + VSPACE_MULTIBYTE_CASES: #endif + RRETURN(MATCH_NOMATCH); + } break; - } - } - break; - case OP_NOT_DIGIT: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; + case OP_VSPACE: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + break; + } + break; - case OP_DIGIT: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; + case OP_NOT_DIGIT: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_NOT_WHITESPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; + case OP_DIGIT: + if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_WHITESPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; + case OP_NOT_WHITESPACE: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_NOT_WORDCHAR: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; + case OP_WHITESPACE: + if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + break; - case OP_WORDCHAR: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + case OP_NOT_WORDCHAR: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + return PCRE2_ERROR_INTERNAL; } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); } + /* Control never gets here */ } - /* If min = max, continue at the same level without recursing */ - - if (min == max) continue; - - /* If minimizing, we have to test the rest of the pattern before each - subsequent match. Again, separate the UTF-8 case for speed, and also - separate the UCP cases. */ + /* If maximizing, it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ - if (minimize) + else { + Lstart_eptr = Feptr; /* Remember where we started */ + #ifdef SUPPORT_UNICODE - if (prop_type >= 0) + if (proptype >= 0) { - switch(prop_type) + switch(proptype) { case PT_ANY: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM36); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - if (prop_fail_result) RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + if (Lctype == OP_NOTPROP) break; + Feptr+= len; } - /* Control never gets here */ + break; case PT_LAMP: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { int chartype; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM37); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - chartype = UCD_CHARTYPE(c); + GETCHARLENTEST(fc, Feptr, len); + chartype = UCD_CHARTYPE(fc); if ((chartype == ucp_Lu || chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; } - /* Control never gets here */ + break; case PT_GC: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM38); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; } - /* Control never gets here */ + break; case PT_PC: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM39); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; } - /* Control never gets here */ + break; case PT_SC: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM40); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; } - /* Control never gets here */ + break; case PT_ALNUM: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { int category; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM59); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == + (Lctype == OP_NOTPROP)) + break; + Feptr+= len; } - /* Control never gets here */ + break; /* Perl space used to exclude VT, but from Perl 5.18 it is included, which means that Perl space and POSIX space are now identical. PCRE @@ -5096,1326 +3914,2060 @@ for (;;) case PT_SPACE: /* Perl space */ case PT_PXSPACE: /* POSIX space */ - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM61); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - switch(c) + GETCHARLENTEST(fc, Feptr, len); + switch(fc) { HSPACE_CASES: VSPACE_CASES: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); + if (Lctype == OP_NOTPROP) goto ENDLOOP99; /* Break the loop */ break; default: - if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) + goto ENDLOOP99; /* Break the loop */ break; } + Feptr+= len; } - /* Control never gets here */ + ENDLOOP99: + break; case PT_WORD: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { int category; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM62); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || - category == ucp_N || - c == CHAR_UNDERSCORE) - == prop_fail_result) - RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N || + fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; } - /* Control never gets here */ + break; case PT_CLIST: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { const uint32_t *cp; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM67); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - cp = PRIV(ucd_caseless_sets) + prop_value; + GETCHARLENTEST(fc, Feptr, len); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; for (;;) { - if (c < *cp) - { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } } - if (c == *cp++) - { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; } + if (fc < *cp) + { if (Lctype == OP_NOTPROP) break; else goto GOT_MAX; } + if (fc == *cp++) + { if (Lctype == OP_NOTPROP) goto GOT_MAX; else break; } } + Feptr += len; } - /* Control never gets here */ + GOT_MAX: + break; case PT_UCNC: - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM60); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + int len = 1; + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } - GETCHARINCTEST(c, eptr); - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == prop_fail_result) - RRETURN(MATCH_NOMATCH); + GETCHARLENTEST(fc, Feptr, len); + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Lctype == OP_NOTPROP)) + break; + Feptr += len; } - /* Control never gets here */ + break; - /* This should never occur */ default: - RRETURN(PCRE2_ERROR_INTERNAL); + return PCRE2_ERROR_INTERNAL; + } + + /* Feptr is now past the end of the maximum run */ + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM222); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + if (utf) BACKCHAR(Feptr); } } - /* Match extended Unicode sequences. We will get here only if the + /* Match extended Unicode grapheme clusters. We will get here only if the support is in the binary; otherwise a compile-time error occurs. */ - else if (ctype == OP_EXTUNI) + else if (Lctype == OP_EXTUNI) { - for (fi = min;; fi++) + for (i = Lmin; i < Lmax; i++) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM41); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); + break; } else { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, + utf, NULL); + } + CHECK_PARTIAL(); + } + + /* Feptr is now past the end of the maximum run */ + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start + of the run while backtracking because the use of \C in UTF mode can + cause BACKCHAR to move back past Lstart_eptr. This is just palliative; + the use of \C in UTF mode is fraught with danger. */ + + for(;;) + { + int lgb, rgb; + PCRE2_SPTR fptr; + + if (Feptr <= Lstart_eptr) break; /* At start of char run */ + RMATCH(Fecode, RM220); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + + /* Backtracking over an extended grapheme cluster involves inspecting + the previous two characters (if present) to see if a break is + permitted between them. */ + + Feptr--; + if (!utf) fc = *Feptr; else + { + BACKCHAR(Feptr); + GETCHAR(fc, Feptr); + } + rgb = UCD_GRAPHBREAK(fc); + + for (;;) + { + if (Feptr <= Lstart_eptr) break; /* At start of char run */ + fptr = Feptr - 1; + if (!utf) fc = *fptr; else { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; + BACKCHAR(fptr); + GETCHAR(fc, fptr); } + lgb = UCD_GRAPHBREAK(fc); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + Feptr = fptr; + rgb = lgb; } - CHECK_PARTIAL(); } } + else -#endif /* SUPPORT_UNICODE */ +#endif /* SUPPORT_UNICODE */ #ifdef SUPPORT_UNICODE if (utf) { - for (fi = min;; fi++) + switch(Lctype) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM42); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (ctype == OP_ANY && IS_NEWLINE(eptr)) - RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - switch(ctype) + case OP_ANY: + for (i = Lmin; i < Lmax; i++) { - case OP_ANY: /* This is the non-NL case */ + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(Feptr)) break; if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr >= mb->end_subject && + Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && - c == NLBLOCK->nl[0]) + UCHAR21(Feptr) == NLBLOCK->nl[0]) { mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } - break; - - case OP_ALLANY: - case OP_ANYBYTE: - break; + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + break; - case OP_ANYNL: - switch(c) + case OP_ALLANY: + if (Lmax < UINT32_MAX) + { + for (i = Lmin; i < Lmax; i++) { - default: RRETURN(MATCH_NOMATCH); - case CHAR_CR: - if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++; - break; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + } + else + { + Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */ + SCHECK_PARTIAL(); + } + break; - case CHAR_LF: - break; + /* The "byte" (i.e. "code unit") case is the same as non-UTF */ - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: + case OP_ANYBYTE: + fc = Lmax - Lmin; + if (fc > (uint32_t)(mb->end_subject - Feptr)) + { + Feptr = mb->end_subject; + SCHECK_PARTIAL(); + } + else Feptr += fc; + break; + + case OP_ANYNL: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc == CHAR_CR) + { + if (++Feptr >= mb->end_subject) break; + if (UCHAR21(Feptr) == CHAR_LF) Feptr++; + } + else + { + if (fc != CHAR_LF && + (mb->bsr_convention == PCRE2_BSR_ANYCRLF || + (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL #ifndef EBCDIC - case 0x2028: - case 0x2029: + && fc != 0x2028 && fc != 0x2029 #endif /* Not EBCDIC */ - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + ))) + break; + Feptr += len; + } + } + break; + + case OP_NOT_HSPACE: + case OP_HSPACE: + for (i = Lmin; i < Lmax; i++) + { + BOOL gotspace; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); break; } - break; + GETCHARLEN(fc, Feptr, len); + switch(fc) + { + HSPACE_CASES: gotspace = TRUE; break; + default: gotspace = FALSE; break; + } + if (gotspace == (Lctype == OP_NOT_HSPACE)) break; + Feptr += len; + } + break; - case OP_NOT_HSPACE: - switch(c) + case OP_NOT_VSPACE: + case OP_VSPACE: + for (i = Lmin; i < Lmax; i++) + { + BOOL gotspace; + int len = 1; + if (Feptr >= mb->end_subject) { - HSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; + SCHECK_PARTIAL(); + break; } - break; + GETCHARLEN(fc, Feptr, len); + switch(fc) + { + VSPACE_CASES: gotspace = TRUE; break; + default: gotspace = FALSE; break; + } + if (gotspace == (Lctype == OP_NOT_VSPACE)) break; + Feptr += len; + } + break; - case OP_HSPACE: - switch(c) + case OP_NOT_DIGIT: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) { - HSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + break; } - break; + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break; + Feptr+= len; + } + break; - case OP_NOT_VSPACE: - switch(c) + case OP_DIGIT: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) { - VSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; + SCHECK_PARTIAL(); + break; } - break; + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break; + Feptr+= len; + } + break; - case OP_VSPACE: - switch(c) + case OP_NOT_WHITESPACE: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) { - VSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + break; } - break; + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break; + Feptr+= len; + } + break; - case OP_NOT_DIGIT: - if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - break; + case OP_WHITESPACE: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break; + Feptr+= len; + } + break; - case OP_DIGIT: - if (c >= 256 || (mb->ctypes[c] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - break; + case OP_NOT_WORDCHAR: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break; + Feptr+= len; + } + break; - case OP_NOT_WHITESPACE: - if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - break; + case OP_WORDCHAR: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break; + Feptr+= len; + } + break; - case OP_WHITESPACE: - if (c >= 256 || (mb->ctypes[c] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - break; + default: + return PCRE2_ERROR_INTERNAL; + } - case OP_NOT_WORDCHAR: - if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - break; + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - case OP_WORDCHAR: - if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - break; + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go + too far. */ - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } + for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM221); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && + UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR) + Feptr--; } } else -#endif +#endif /* SUPPORT_UNICODE */ + /* Not UTF mode */ { - for (fi = min;; fi++) + switch(Lctype) { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM43); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (ctype == OP_ANY && IS_NEWLINE(eptr)) - RRETURN(MATCH_NOMATCH); - c = *eptr++; - switch(ctype) + case OP_ANY: + for (i = Lmin; i < Lmax; i++) { - case OP_ANY: /* This is the non-NL case */ + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(Feptr)) break; if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr >= mb->end_subject && + Feptr + 1 >= mb->end_subject && NLBLOCK->nltype == NLTYPE_FIXED && NLBLOCK->nllen == 2 && - c == NLBLOCK->nl[0]) + *Feptr == NLBLOCK->nl[0]) { mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } - break; + Feptr++; + } + break; - case OP_ALLANY: - case OP_ANYBYTE: - break; + case OP_ALLANY: + case OP_ANYBYTE: + fc = Lmax - Lmin; + if (fc > (uint32_t)(mb->end_subject - Feptr)) + { + Feptr = mb->end_subject; + SCHECK_PARTIAL(); + } + else Feptr += fc; + break; - case OP_ANYNL: - switch(c) + case OP_ANYNL: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) { - default: RRETURN(MATCH_NOMATCH); - case CHAR_CR: - if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; - - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#if PCRE2_CODE_UNIT_WIDTH != 8 - case 0x2028: - case 0x2029: -#endif - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); break; } - break; - - case OP_NOT_HSPACE: - switch(c) + fc = *Feptr; + if (fc == CHAR_CR) { - default: break; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); + if (++Feptr >= mb->end_subject) break; + if (*Feptr == CHAR_LF) Feptr++; } - break; - - case OP_HSPACE: - switch(c) + else { - default: RRETURN(MATCH_NOMATCH); - HSPACE_BYTE_CASES: + if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF || + (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL #if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: + && fc != 0x2028 && fc != 0x2029 #endif - break; + ))) break; + Feptr++; } - break; + } + break; - case OP_NOT_VSPACE: - switch(c) + case OP_NOT_HSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) { - default: break; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + break; } - break; - - case OP_VSPACE: - switch(c) + switch(*Feptr) { - default: RRETURN(MATCH_NOMATCH); - VSPACE_BYTE_CASES: + default: Feptr++; break; + HSPACE_BYTE_CASES: #if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: + HSPACE_MULTIBYTE_CASES: #endif - break; + goto ENDLOOP00; } - break; - - case OP_NOT_DIGIT: - if (MAX_255(c) && (mb->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_DIGIT: - if (!MAX_255(c) || (mb->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WHITESPACE: - if (MAX_255(c) && (mb->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_WHITESPACE: - if (!MAX_255(c) || (mb->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WORDCHAR: - if (MAX_255(c) && (mb->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_WORDCHAR: - if (!MAX_255(c) || (mb->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); } - } - } - /* Control never gets here */ - } - - /* If maximizing, it is worth using inline code for speed, doing the type - test once at the start (i.e. keep it out of the loop). Again, keep the - UTF-8 and UCP stuff separate. */ - - else - { - pp = eptr; /* Remember where we started */ + ENDLOOP00: + break; -#ifdef SUPPORT_UNICODE - if (prop_type >= 0) - { - switch(prop_type) - { - case PT_ANY: - for (i = min; i < max; i++) + case OP_HSPACE: + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - if (prop_fail_result) break; - eptr+= len; - } - break; - - case PT_LAMP: - for (i = min; i < max; i++) - { - int chartype; - int len = 1; - if (eptr >= mb->end_subject) + switch(*Feptr) { - SCHECK_PARTIAL(); - break; + default: goto ENDLOOP01; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + Feptr++; break; } - GETCHARLENTEST(c, eptr, len); - chartype = UCD_CHARTYPE(c); - if ((chartype == ucp_Lu || - chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) - break; - eptr+= len; } + ENDLOOP01: break; - case PT_GC: - for (i = min; i < max; i++) + case OP_NOT_VSPACE: + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) break; - eptr+= len; + switch(*Feptr) + { + default: Feptr++; break; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + goto ENDLOOP02; + } } + ENDLOOP02: break; - case PT_PC: - for (i = min; i < max; i++) + case OP_VSPACE: + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) break; - eptr+= len; + switch(*Feptr) + { + default: goto ENDLOOP03; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + Feptr++; break; + } } + ENDLOOP03: break; - case PT_SC: - for (i = min; i < max; i++) + case OP_NOT_DIGIT: + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) break; - eptr+= len; + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) + break; + Feptr++; } break; - case PT_ALNUM: - for (i = min; i < max; i++) + case OP_DIGIT: + for (i = Lmin; i < Lmax; i++) { - int category; - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N) == prop_fail_result) + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) break; - eptr+= len; + Feptr++; } break; - /* Perl space used to exclude VT, but from Perl 5.18 it is included, - which means that Perl space and POSIX space are now identical. PCRE - was changed at release 8.34. */ - - case PT_SPACE: /* Perl space */ - case PT_PXSPACE: /* POSIX space */ - for (i = min; i < max; i++) + case OP_NOT_WHITESPACE: + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - switch(c) - { - HSPACE_CASES: - VSPACE_CASES: - if (prop_fail_result) goto ENDLOOP99; /* Break the loop */ - break; - - default: - if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result) - goto ENDLOOP99; /* Break the loop */ + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) break; - } - eptr+= len; + Feptr++; } - ENDLOOP99: break; - case PT_WORD: - for (i = min; i < max; i++) + case OP_WHITESPACE: + for (i = Lmin; i < Lmax; i++) { - int category; - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N || - c == CHAR_UNDERSCORE) == prop_fail_result) + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) break; - eptr+= len; + Feptr++; } break; - case PT_CLIST: - for (i = min; i < max; i++) + case OP_NOT_WORDCHAR: + for (i = Lmin; i < Lmax; i++) { - const uint32_t *cp; - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - cp = PRIV(ucd_caseless_sets) + prop_value; - for (;;) - { - if (c < *cp) - { if (prop_fail_result) break; else goto GOT_MAX; } - if (c == *cp++) - { if (prop_fail_result) goto GOT_MAX; else break; } - } - eptr += len; + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) + break; + Feptr++; } - GOT_MAX: break; - case PT_UCNC: - for (i = min; i < max; i++) + case OP_WORDCHAR: + for (i = Lmin; i < Lmax; i++) { - int len = 1; - if (eptr >= mb->end_subject) + if (Feptr >= mb->end_subject) { SCHECK_PARTIAL(); break; } - GETCHARLENTEST(c, eptr, len); - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == prop_fail_result) + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) break; - eptr += len; + Feptr++; } break; default: - RRETURN(PCRE2_ERROR_INTERNAL); + return PCRE2_ERROR_INTERNAL; } - /* eptr is now past the end of the maximum run */ + if (reptype == REPTYPE_POS) continue; /* No backtracking */ - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) + for (;;) { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM44); + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM34); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - if (utf) BACKCHAR(eptr); + Feptr--; + if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF && + Feptr[-1] == CHAR_CR) Feptr--; } } + } + break; /* End of repeat character type processing */ - /* Match extended Unicode grapheme clusters. We will get here only if the - support is in the binary; otherwise a compile-time error occurs. */ +#undef Lstart_eptr +#undef Lmin +#undef Lmax +#undef Lctype +#undef Lpropvalue - else if (ctype == OP_EXTUNI) - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - else - { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } - } - CHECK_PARTIAL(); - } - /* eptr is now past the end of the maximum run */ + /* ===================================================================== */ + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The OP_REF and + OP_REFI opcodes are used for a reference to a numbered group or to a + non-duplicated named group. For a duplicated named group, OP_DNREF and + OP_DNREFI are used. In this case we must scan the list of groups to which + the name refers, and use the first one that is set. */ + +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lcaseless F->temp_32[2] +#define Lstart F->temp_sptr[0] +#define Loffset F->temp_size - if (possessive) continue; /* No backtracking */ + case OP_DNREF: + case OP_DNREFI: + Lcaseless = (Fop == OP_DNREFI); + { + int count = GET2(Fecode, 1+IMM2_SIZE); + PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; + Fecode += 1 + 2*IMM2_SIZE; - /* We use <= pp rather than == pp to detect the start of the run while - backtracking because the use of \C in UTF mode can cause BACKCHAR to - move back past pp. This is just palliative; the use of \C in UTF mode - is fraught with danger. */ + while (count-- > 0) + { + Loffset = (GET2(slot, 0) << 1) - 2; + if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break; + slot += mb->name_entry_size; + } + } + goto REF_REPEAT; - for(;;) - { - int lgb, rgb; - PCRE2_SPTR fptr; + case OP_REF: + case OP_REFI: + Lcaseless = (Fop == OP_REFI); + Loffset = (GET2(Fecode, 1) << 1) - 2; + Fecode += 1 + IMM2_SIZE; - if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */ - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM45); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); + /* Set up for repetition, or handle the non-repeated case. The maximum and + minimum must be in the heap frame, but as they are short-term values, we + use temporary fields. */ - /* Backtracking over an extended grapheme cluster involves inspecting - the previous two characters (if present) to see if a break is - permitted between them. */ + REF_REPEAT: + switch (*Fecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + fc = *Fecode++ - OP_CRSTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + break; - eptr--; - if (!utf) c = *eptr; else - { - BACKCHAR(eptr); - GETCHAR(c, eptr); - } - rgb = UCD_GRAPHBREAK(c); + case OP_CRRANGE: + case OP_CRMINRANGE: + Lmin = GET2(Fecode, 1); + Lmax = GET2(Fecode, 1 + IMM2_SIZE); + reptype = rep_typ[*Fecode - OP_CRSTAR]; + if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ + Fecode += 1 + 2 * IMM2_SIZE; + break; - for (;;) - { - if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */ - fptr = eptr - 1; - if (!utf) c = *fptr; else - { - BACKCHAR(fptr); - GETCHAR(c, fptr); - } - lgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - eptr = fptr; - rgb = lgb; - } - } - } - - else -#endif /* SUPPORT_UNICODE */ - -#ifdef SUPPORT_UNICODE - if (utf) + default: /* No repeat follows */ { - switch(ctype) + rrc = match_ref(Loffset, Lcaseless, F, mb, &length); + if (rrc != 0) { - case OP_ANY: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (IS_NEWLINE(eptr)) break; - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + Feptr += length; + continue; /* With the main loop */ + } - case OP_ALLANY: - if (max < INT_MAX) - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - } - else - { - eptr = mb->end_subject; /* Unlimited UTF-8 repeat */ - SCHECK_PARTIAL(); - } - break; + /* Handle repeated back references. If a set group has length zero, just + continue with the main loop, because it matches however many times. For an + unset reference, if the minimum is zero, we can also just continue. We can + also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset + group behave as a zero-length group. For any other unset cases, carrying + on will result in NOMATCH. */ - /* The byte case is the same as non-UTF8 */ + if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) + { + if (Fovector[Loffset] == Fovector[Loffset + 1]) continue; + } + else /* Group is not set */ + { + if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) + continue; + } - case OP_ANYBYTE: - c = max - min; - if (c > (uint32_t)(mb->end_subject - eptr)) - { - eptr = mb->end_subject; - SCHECK_PARTIAL(); - } - else eptr += c; - break; + /* First, ensure the minimum number of matches are present. */ - case OP_ANYNL: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c == CHAR_CR) - { - if (++eptr >= mb->end_subject) break; - if (UCHAR21(eptr) == CHAR_LF) eptr++; - } - else - { - if (c != CHAR_LF && - (mb->bsr_convention == PCRE2_BSR_ANYCRLF || - (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL -#ifndef EBCDIC - && c != 0x2028 && c != 0x2029 -#endif /* Not EBCDIC */ - ))) - break; - eptr += len; - } - } - break; + for (i = 1; i <= Lmin; i++) + { + PCRE2_SIZE slength; + rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); + if (rrc != 0) + { + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr += slength; + } - case OP_NOT_HSPACE: - case OP_HSPACE: - for (i = min; i < max; i++) - { - BOOL gotspace; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - switch(c) - { - HSPACE_CASES: gotspace = TRUE; break; - default: gotspace = FALSE; break; - } - if (gotspace == (ctype == OP_NOT_HSPACE)) break; - eptr += len; - } - break; + /* If min = max, we are done. They are not both allowed to be zero. */ - case OP_NOT_VSPACE: - case OP_VSPACE: - for (i = min; i < max; i++) - { - BOOL gotspace; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - switch(c) - { - VSPACE_CASES: gotspace = TRUE; break; - default: gotspace = FALSE; break; - } - if (gotspace == (ctype == OP_NOT_VSPACE)) break; - eptr += len; - } - break; + if (Lmin == Lmax) continue; - case OP_NOT_DIGIT: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) break; - eptr+= len; - } - break; + /* If minimizing, keep trying and advancing the pointer. */ - case OP_DIGIT: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c >= 256 ||(mb->ctypes[c] & ctype_digit) == 0) break; - eptr+= len; - } - break; + if (reptype == REPTYPE_MIN) + { + for (;;) + { + PCRE2_SIZE slength; + RMATCH(Fecode, RM20); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); + if (rrc != 0) + { + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr += slength; + } + /* Control never gets here */ + } - case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) break; - eptr+= len; - } - break; + /* If maximizing, find the longest string and work backwards, as long as + the matched lengths for each iteration are the same. */ - case OP_WHITESPACE: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c >= 256 ||(mb->ctypes[c] & ctype_space) == 0) break; - eptr+= len; - } - break; + else + { + BOOL samelengths = TRUE; + Lstart = Feptr; /* Starting position */ + Flength = Fovector[Loffset+1] - Fovector[Loffset]; - case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) break; - eptr+= len; - } - break; + for (i = Lmin; i < Lmax; i++) + { + PCRE2_SIZE slength; + rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); + if (rrc != 0) + { + /* Can't use CHECK_PARTIAL because we don't want to update Feptr in + the soft partial matching case. */ - case OP_WORDCHAR: - for (i = min; i < max; i++) + if (rrc > 0 && mb->partial != 0 && + mb->end_subject > mb->start_used_ptr) { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) break; - eptr+= len; + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; } break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); } - if (possessive) continue; /* No backtracking */ + if (slength != Flength) samelengths = FALSE; + Feptr += slength; + } - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ + /* If the length matched for each repetition is the same as the length of + the captured group, we can easily work backwards. This is the normal + case. However, in caseless UTF-8 mode there are pairs of case-equivalent + characters whose lengths (in terms of code units) differ. However, this + is very rare, so we handle it by re-matching fewer and fewer times. */ - for(;;) + if (samelengths) + { + while (Feptr >= Lstart) { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM46); + RMATCH(Fecode, RM21); if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); - if (ctype == OP_ANYNL && eptr > pp && UCHAR21(eptr) == CHAR_NL && - UCHAR21(eptr - 1) == CHAR_CR) eptr--; + Feptr -= Flength; } } + + /* The rare case of non-matching lengths. Re-scan the repetition for each + iteration. We know that match_ref() will succeed every time. */ + else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ { - switch(ctype) + Lmax = i; + for (;;) { - case OP_ANY: - for (i = min; i < max; i++) + RMATCH(Fecode, RM22); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr == Lstart) break; /* Failed after minimal repetition */ + Feptr = Lstart; + Lmax--; + for (i = Lmin; i < Lmax; i++) { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (IS_NEWLINE(eptr)) break; - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - *eptr == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; + PCRE2_SIZE slength; + (void)match_ref(Loffset, Lcaseless, F, mb, &slength); + Feptr += slength; } - break; + } + } - case OP_ALLANY: - case OP_ANYBYTE: - c = max - min; - if (c > (uint32_t)(mb->end_subject - eptr)) - { - eptr = mb->end_subject; - SCHECK_PARTIAL(); - } - else eptr += c; - break; + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ - case OP_ANYNL: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - c = *eptr; - if (c == CHAR_CR) - { - if (++eptr >= mb->end_subject) break; - if (*eptr == CHAR_LF) eptr++; - } - else - { - if (c != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF || - (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL -#if PCRE2_CODE_UNIT_WIDTH != 8 - && c != 0x2028 && c != 0x2029 -#endif - ))) break; - eptr++; - } - } - break; +#undef Lcaseless +#undef Lmin +#undef Lmax +#undef Lstart +#undef Loffset - case OP_NOT_HSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: eptr++; break; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - goto ENDLOOP00; - } - } - ENDLOOP00: - break; - case OP_HSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: goto ENDLOOP01; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - eptr++; break; - } - } - ENDLOOP01: - break; - case OP_NOT_VSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: eptr++; break; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - goto ENDLOOP02; - } - } - ENDLOOP02: - break; +/* ========================================================================= */ +/* Opcodes for the start of various parenthesized items */ +/* ========================================================================= */ - case OP_VSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: goto ENDLOOP03; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - eptr++; break; - } - } - ENDLOOP03: - break; + /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the + (*THEN) is within the current branch by comparing the address of OP_THEN + that is passed back with the end of the branch. If (*THEN) is within the + current branch, and the branch is one of two or more alternatives (it + either starts or ends with OP_ALT), we have reached the limit of THEN's + action, so convert the return code to NOMATCH, which will cause normal + backtracking to happen from now on. Otherwise, THEN is passed back to an + outer alternative. This implements Perl's treatment of parenthesized + groups, where a group not containing | does not affect the current + alternative, that is, (X) is NOT the same as (X|(*F)). */ - case OP_NOT_DIGIT: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0) break; - eptr++; - } + + /* ===================================================================== */ + /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive + bracket group, indicating that it may occur zero times. It may repeat + infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in + the pattern. Brackets with fixed upper repeat limits are compiled as a + number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO. + Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */ + +#define Lnext_ecode F->temp_sptr[0] + + case OP_BRAZERO: + Lnext_ecode = Fecode + 1; + RMATCH(Lnext_ecode, RM9); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT); + Fecode = Lnext_ecode + 1 + LINK_SIZE; + break; + + case OP_BRAMINZERO: + Lnext_ecode = Fecode + 1; + do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT); + RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode++; + break; + +#undef Lnext_ecode + + case OP_SKIPZERO: + Fecode++; + do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT); + Fecode += 1 + LINK_SIZE; + break; + + + /* ===================================================================== */ + /* Handle possessive brackets with an unlimited repeat. The end of these + brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without + going further in the pattern. */ + +#define Lframe_type F->temp_32[0] +#define Lmatched_once F->temp_32[1] +#define Lzero_allowed F->temp_32[2] +#define Lstart_eptr F->temp_sptr[0] +#define Lstart_group F->temp_sptr[1] + + case OP_BRAPOSZERO: + Lzero_allowed = TRUE; /* Zero repeat is allowed */ + Fecode += 1; + if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS) + goto POSSESSIVE_CAPTURE; + goto POSSESSIVE_NON_CAPTURE; + + case OP_BRAPOS: + case OP_SBRAPOS: + Lzero_allowed = FALSE; /* Zero repeat not allowed */ + + POSSESSIVE_NON_CAPTURE: + Lframe_type = GF_NOCAPTURE; /* Remembered frame type */ + goto POSSESSIVE_GROUP; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + Lzero_allowed = FALSE; /* Zero repeat not allowed */ + + POSSESSIVE_CAPTURE: + number = GET2(Fecode, 1+LINK_SIZE); + Lframe_type = GF_CAPTURE | number; /* Remembered frame type */ + + POSSESSIVE_GROUP: + Lmatched_once = FALSE; /* Never matched */ + Lstart_group = Fecode; /* Start of this group */ + + for (;;) + { + Lstart_eptr = Feptr; /* Position at group start */ + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8); + if (rrc == MATCH_KETRPOS) + { + Lmatched_once = TRUE; /* Matched at least once */ + if (Feptr == Lstart_eptr) /* Empty match; skip to end */ + { + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); break; + } - case OP_DIGIT: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0) break; - eptr++; - } + Fecode = Lstart_group; + continue; + } + + /* See comment above about handling THEN. */ + + if (rrc == MATCH_THEN) + { + PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1); + if (mb->verb_ecode_ptr < next_ecode && + (*Fecode == OP_ALT || *next_ecode == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) break; + } + + /* Success if matched something or zero repeat allowed */ + + if (Lmatched_once || Lzero_allowed) + { + Fecode += 1 + LINK_SIZE; + break; + } + + RRETURN(MATCH_NOMATCH); + +#undef Lmatched_once +#undef Lzero_allowed +#undef Lframe_type +#undef Lstart_eptr +#undef Lstart_group + + + /* ===================================================================== */ + /* Handle non-capturing brackets that cannot match an empty string. When we + get to the final alternative within the brackets, as long as there are no + THEN's in the pattern, we can optimize by not recording a new backtracking + point. (Ideally we should test for a THEN within this group, but we don't + have that information.) Don't do this if we are at the very top level, + however, because that would make handling assertions and once-only brackets + messier when there is nothing to go back to. */ + +#define Lframe_type F->temp_32[0] /* Set for all that use GROUPLOOP */ +#define Lnext_branch F->temp_sptr[0] /* Used only in OP_BRA handling */ + + case OP_BRA: + if (mb->hasthen || Frdepth == 0) + { + Lframe_type = 0; + goto GROUPLOOP; + } + + for (;;) + { + Lnext_branch = Fecode + GET(Fecode, 1); + if (*Lnext_branch != OP_ALT) break; + + /* This is never the final branch. We do not need to test for MATCH_THEN + here because this code is not used when there is a THEN in the pattern. */ + + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode = Lnext_branch; + } + + /* Hit the start of the final branch. Continue at this level. */ + + Fecode += PRIV(OP_lengths)[*Fecode]; + break; + +#undef Lnext_branch + + + /* ===================================================================== */ + /* Handle a capturing bracket, other than those that are possessive with an + unlimited repeat. */ + + case OP_CBRA: + case OP_SCBRA: + Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE); + goto GROUPLOOP; + + + /* ===================================================================== */ + /* Atomic groups and non-capturing brackets that can match an empty string + must record a backtracking point and also set up a chained frame. */ + + case OP_ONCE: + case OP_SBRA: + Lframe_type = GF_NOCAPTURE | Fop; + + GROUPLOOP: + for (;;) + { + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2); + if (rrc == MATCH_THEN) + { + PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1); + if (mb->verb_ecode_ptr < next_ecode && + (*Fecode == OP_ALT || *next_ecode == OP_ALT)) + rrc = MATCH_NOMATCH; + } + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH); + } + /* Control never reaches here. */ + +#undef Lframe_type + + + /* ===================================================================== */ + /* Recursion either matches the current regex, or some subexpression. The + offset data is the offset to the starting bracket from the start of the + whole pattern. (This is so that it works from duplicated subpatterns.) */ + +#define Lframe_type F->temp_32[0] +#define Lstart_branch F->temp_sptr[0] + + case OP_RECURSE: + bracode = mb->start_code + GET(Fecode, 1); + number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE); + + /* If we are already in a recursion, check for repeating the same one + without advancing the subject pointer. This should catch convoluted mutual + recursions. (Some simple cases are caught at compile time.) */ + + if (Fcurrent_recurse != RECURSE_UNSET) + { + offset = Flast_group_offset; + while (offset != PCRE2_UNSET) + { + N = (heapframe *)((char *)mb->match_frames + offset); + P = (heapframe *)((char *)N - frame_size); + if (N->group_frame_type == (GF_RECURSE | number)) + { + if (Feptr == P->eptr) return PCRE2_ERROR_RECURSELOOP; break; + } + offset = P->last_group_offset; + } + } + + /* Now run the recursion, branch by branch. */ + + Lstart_branch = bracode; + Lframe_type = GF_RECURSE | number; + + for (;;) + { + PCRE2_SPTR next_ecode; + + group_frame_type = Lframe_type; + RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11); + next_ecode = Lstart_branch + GET(Lstart_branch,1); + + /* Handle backtracking verbs, which are defined in a range that can + easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to + escape beyond a recursion; they cause a NOMATCH for the entire recursion. + + When one of these verbs triggers, the current recursion group number is + recorded. If it matches the recursion we are processing, the verb + happened within the recursion and we must deal with it. Otherwise it must + have happened after the recursion completed, and so has to be passed + back. See comment above about handling THEN. */ + + if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX && + mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE)) + { + if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode && + (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT)) + rrc = MATCH_NOMATCH; + else RRETURN(MATCH_NOMATCH); + } + + /* Note that carrying on after (*ACCEPT) in a recursion is handled in the + OP_ACCEPT code. Nothing needs to be done here. */ + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Lstart_branch = next_ecode; + if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH); + } + /* Control never reaches here. */ + +#undef Lframe_type +#undef Lstart_branch + + + /* ===================================================================== */ + /* Positive assertions are like other groups except that PCRE doesn't allow + the effect of (*THEN) to escape beyond an assertion; it is therefore + treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its + captures and mark retained. Any other return is an error. */ + +#define Lframe_type F->temp_32[0] + + case OP_ASSERT: + case OP_ASSERTBACK: + Lframe_type = GF_NOCAPTURE | Fop; + for (;;) + { + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3); + if (rrc == MATCH_ACCEPT) + { + memcpy(Fovector, + (char *)assert_accept_frame + offsetof(heapframe, ovector), + assert_accept_frame->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top = assert_accept_frame->offset_top; + Fmark = assert_accept_frame->mark; + break; + } + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH); + } + + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + Fecode += 1 + LINK_SIZE; + break; + +#undef Lframe_type + + + /* ===================================================================== */ + /* Handle negative assertions. Loop for each non-matching branch as for + positive assertions. */ + +#define Lframe_type F->temp_32[0] + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + Lframe_type = GF_NOCAPTURE | Fop; + + for (;;) + { + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4); + switch(rrc) + { + case MATCH_ACCEPT: /* Assertion matched, therefore it fails. */ + case MATCH_MATCH: + RRETURN (MATCH_NOMATCH); + + case MATCH_NOMATCH: /* Branch failed, try next if present. */ + case MATCH_THEN: + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED; + break; + + case MATCH_COMMIT: /* Assertion forced to fail, therefore continue. */ + case MATCH_SKIP: + case MATCH_PRUNE: + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + goto ASSERT_NOT_FAILED; + + default: /* Pass back any other return */ + RRETURN(rrc); + } + } + + /* None of the branches have matched or there was a backtrack to (*COMMIT), + (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a + negative assertion, so carry on. */ + + ASSERT_NOT_FAILED: + Fecode += 1 + LINK_SIZE; + break; + +#undef Lframe_type + + + /* ===================================================================== */ + /* The callout item calls an external function, if one is provided, passing + details of the match so far. This is mainly for debugging, though the + function is able to force a failure. */ + + case OP_CALLOUT: + case OP_CALLOUT_STR: + rrc = do_callout(F, mb, &length); + if (rrc > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + Fecode += length; + break; + + + /* ===================================================================== */ + /* Conditional group: compilation checked that there are no more than two + branches. If the condition is false, skipping the first branch takes us + past the end of the item if there is only one branch, but that's exactly + what we want. */ + + case OP_COND: + case OP_SCOND: + + /* The variable Flength will be added to Fecode when the condition is + false, to get to the second branch. Setting it to the offset to the ALT or + KET, then incrementing Fecode achieves this effect. However, if the second + branch is non-existent, we must point to the KET so that the end of the + group is correctly processed. We now have Fecode pointing to the condition + or callout. */ + + Flength = GET(Fecode, 1); /* Offset to the second branch */ + if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE; + Fecode += 1 + LINK_SIZE; /* From this opcode */ + + /* Because of the way auto-callout works during compile, a callout item is + inserted between OP_COND and an assertion condition. Such a callout can + also be inserted manually. */ + + if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR) + { + rrc = do_callout(F, mb, &length); + if (rrc > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + + /* Advance Fecode past the callout, so it now points to the condition. We + must adjust Flength so that the value of Fecode+Flength is unchanged. */ + + Fecode += length; + Flength -= length; + } + + /* Test the various possible conditions */ + + condition = FALSE; + switch(*Fecode) + { + case OP_RREF: /* Group recursion test */ + if (Fcurrent_recurse != RECURSE_UNSET) + { + number = GET2(Fecode, 1); + condition = (number == RREF_ANY || number == Fcurrent_recurse); + } + break; + + case OP_DNRREF: /* Duplicate named group recursion test */ + if (Fcurrent_recurse != RECURSE_UNSET) + { + int count = GET2(Fecode, 1 + IMM2_SIZE); + PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; + while (count-- > 0) + { + number = GET2(slot, 0); + condition = number == Fcurrent_recurse; + if (condition) break; + slot += mb->name_entry_size; + } + } + break; + + case OP_CREF: /* Numbered group used test */ + offset = (GET2(Fecode, 1) << 1) - 2; /* Doubled ref number */ + condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET; + break; + + case OP_DNCREF: /* Duplicate named group used test */ + { + int count = GET2(Fecode, 1 + IMM2_SIZE); + PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; + while (count-- > 0) + { + offset = (GET2(slot, 0) << 1) - 2; + condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET; + if (condition) break; + slot += mb->name_entry_size; + } + } + break; + + case OP_FALSE: + case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */ + break; + + case OP_TRUE: + condition = TRUE; + break; + + /* The condition is an assertion. Run code similar to the assertion code + above. */ + +#define Lpositive F->temp_32[0] +#define Lstart_branch F->temp_sptr[0] + + default: + Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK); + Lstart_branch = Fecode; + + for (;;) + { + group_frame_type = GF_CONDASSERT | *Fecode; + RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5); + + switch(rrc) + { + case MATCH_ACCEPT: /* Save captures */ + memcpy(Fovector, + (char *)assert_accept_frame + offsetof(heapframe, ovector), + assert_accept_frame->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top = assert_accept_frame->offset_top; + + /* Fall through */ + /* In the case of a match, the captures have already been put into + the current frame. */ + + case MATCH_MATCH: + condition = Lpositive; /* TRUE for positive assertion */ + break; + + /* PCRE doesn't allow the effect of (*THEN) to escape beyond an + assertion; it is therefore always treated as NOMATCH. */ + + case MATCH_NOMATCH: + case MATCH_THEN: + Lstart_branch += GET(Lstart_branch, 1); + if (*Lstart_branch == OP_ALT) continue; /* Try next branch */ + condition = !Lpositive; /* TRUE for negative assertion */ + break; + + /* These force no match without checking other branches. */ + + case MATCH_COMMIT: + case MATCH_SKIP: + case MATCH_PRUNE: + condition = !Lpositive; + break; + + default: + RRETURN(rrc); + } + break; /* Out of the branch loop */ + } + + /* If the condition is true, find the end of the assertion so that + advancing past it gets us to the start of the first branch. */ + + if (condition) + { + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + } + break; /* End of assertion condition */ + } + +#undef Lpositive +#undef Lstart_branch + + /* Choose branch according to the condition. */ + + Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength; + + /* If the opcode is OP_SCOND it means we are at a repeated conditional + group that might match an empty string. We must therefore descend a level + so that the start is remembered for checking. For OP_COND we can just + continue at this level. */ + + if (Fop == OP_SCOND) + { + group_frame_type = GF_NOCAPTURE | Fop; + RMATCH(Fecode, RM35); + RRETURN(rrc); + } + break; + + + +/* ========================================================================= */ +/* End of start of parenthesis opcodes */ +/* ========================================================================= */ + + + /* ===================================================================== */ + /* Move the subject pointer back. This occurs only at the start of each + branch of a lookbehind assertion. If we are too close to the start to move + back, fail. When working with UTF-8 we move back a number of characters, + not bytes. */ + + case OP_REVERSE: + number = GET(Fecode, 1); +#ifdef SUPPORT_UNICODE + if (utf) + { + while (number-- > 0) + { + if (Feptr <= mb->start_subject) RRETURN(MATCH_NOMATCH); + Feptr--; + BACKCHAR(Feptr); + } + } + else +#endif + + /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ + + { + if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH); + Feptr -= number; + } + + /* Save the earliest consulted character, then skip to next opcode */ + + if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr; + Fecode += 1 + LINK_SIZE; + break; + + + /* ===================================================================== */ + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group. */ + + case OP_ALT: + do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT); + break; + + + /* ===================================================================== */ + /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the + starting frame was added to the chained frames in order to remember the + starting subject position for the group. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + case OP_KETRPOS: + + bracode = Fecode - GET(Fecode, 1); + + /* Point N to the frame at the start of the most recent group. + Remember the subject pointer at the start of the group. */ + + if (*bracode != OP_BRA && *bracode != OP_COND) + { + N = (heapframe *)((char *)mb->match_frames + Flast_group_offset); + P = (heapframe *)((char *)N - frame_size); + Flast_group_offset = P->last_group_offset; + +#ifdef DEBUG_SHOW_RMATCH + fprintf(stderr, "++ KET for frame=%d type=%x prev char offset=%lu\n", + N->rdepth, N->group_frame_type, + (char *)P->eptr - (char *)mb->start_subject); +#endif + + /* If we are at the end of an assertion that is a condition, return a + match, discarding any intermediate backtracking points. Copy back the + captures into the frame before N so that they are set on return. Doing + this for all assertions, both positive and negative, seems to match what + Perl does. */ + + if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT) + { + memcpy((char *)P + offsetof(heapframe, ovector), Fovector, + Foffset_top * sizeof(PCRE2_SIZE)); + P->offset_top = Foffset_top; + Fback_frame = (char *)F - (char *)P; + RRETURN(MATCH_MATCH); + } + } + else P = NULL; /* Indicates starting frame not recorded */ + + /* The group was not a conditional assertion. */ + + switch (*bracode) + { + case OP_BRA: /* No need to do anything for these */ + case OP_COND: + case OP_SCOND: + break; + + /* Positive assertions are like OP_ONCE, except that in addition the + subject pointer must be put back to where it was at the start of the + assertion. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; + Feptr = P->eptr; + /* Fall through */ + + /* For an atomic group, discard internal backtracking points. We must + also ensure that any remaining branches within the top-level of the group + are not tried. Do this by adjusting the code pointer within the backtrack + frame so that it points to the final branch. */ + + case OP_ONCE: + Fback_frame = ((char *)F - (char *)P); + for (;;) + { + uint32_t y = GET(P->ecode,1); + if ((P->ecode)[y] != OP_ALT) break; + P->ecode += y; + } + break; + + /* A matching negative assertion returns MATCH, which is turned into + NOMATCH at the assertion level. */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + RRETURN(MATCH_MATCH); + + /* Whole-pattern recursion is coded as a recurse into group 0, so it + won't be picked up here. Instead, we catch it when the OP_END is reached. + Other recursion is handled here. */ + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + number = GET2(bracode, 1+LINK_SIZE); + + /* Handle a recursively called group. We reinstate the previous set of + captures and then carry on after the recursion call. */ + + if (Fcurrent_recurse == number) + { + P = (heapframe *)((char *)N - frame_size); + memcpy((char *)F + offsetof(heapframe, ovector), P->ovector, + P->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top = P->offset_top; + Fcapture_last = P->capture_last; + Fcurrent_recurse = P->current_recurse; + Fecode = P->ecode + 1 + LINK_SIZE; + continue; /* With next opcode */ + } + + /* Deal with actual capturing. */ + + offset = (number << 1) - 2; + Fcapture_last = number; + Fovector[offset] = P->eptr - mb->start_subject; + Fovector[offset+1] = Feptr - mb->start_subject; + if (offset >= Foffset_top) Foffset_top = offset + 2; + break; + } /* End actions relating to the starting opcode */ + + /* OP_KETRPOS is a possessive repeating ket. Remember the current position, + and return the MATCH_KETRPOS. This makes it possible to do the repeats one + at a time from the outer level. This must precede the empty string test - + in this case that test is done at the outer level. */ + + if (*Fecode == OP_KETRPOS) + { + memcpy((char *)P + offsetof(heapframe, eptr), + (char *)F + offsetof(heapframe, eptr), + frame_copy_size); + RRETURN(MATCH_KETRPOS); + } + + /* Handle the different kinds of closing brackets. A non-repeating ket + needs no special action, just continuing at this level. This also happens + for the repeating kets if the group matched no characters, in order to + forcibly break infinite loops. Otherwise, the repeating kets try the rest + of the pattern or restart from the preceding bracket, in the appropriate + order. */ + + if (Fop != OP_KET && (P == NULL || Feptr != P->eptr)) + { + if (Fop == OP_KETRMIN) + { + RMATCH(Fecode + 1 + LINK_SIZE, RM6); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode -= GET(Fecode, 1); + break; /* End of ket processing */ + } + + /* Repeat the maximum number of times (KETRMAX) */ + + RMATCH(bracode, RM7); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + + /* Carry on at this level for a non-repeating ket, or after matching an + empty string, or after repeating for a maximum number of times. */ + + Fecode += 1 + LINK_SIZE; + break; + + + /* ===================================================================== */ + /* Start and end of line assertions, not multiline mode. */ + + case OP_CIRC: /* Start of line, unless PCRE2_NOTBOL is set. */ + if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_SOD: /* Unconditional start of subject */ + if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + /* When PCRE2_NOTEOL is unset, assert before the subject end, or a + terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */ + + case OP_DOLL: + if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); + if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS; + + /* Fall through */ + /* Unconditional end of subject assertion (\z) */ + + case OP_EOD: + if (Feptr < mb->end_subject) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + Fecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + ASSERT_NL_OR_EOS: + if (Feptr < mb->end_subject && + (!IS_NEWLINE(Feptr) || Feptr != mb->end_subject - mb->nllen)) + { + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + RRETURN(MATCH_NOMATCH); + } + + /* Either at end of string or \n before end. */ + + SCHECK_PARTIAL(); + Fecode++; + break; + + + /* ===================================================================== */ + /* Start and end of line assertions, multiline mode. */ + + /* Start of subject unless notbol, or after any newline except for one at + the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */ + + case OP_CIRCM: + if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject) + RRETURN(MATCH_NOMATCH); + if (Feptr != mb->start_subject && + ((Feptr == mb->end_subject && + (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) || + !WAS_NEWLINE(Feptr))) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + /* Assert before any newline, or before end of subject unless noteol is + set. */ + + case OP_DOLLM: + if (Feptr < mb->end_subject) + { + if (!IS_NEWLINE(Feptr)) + { + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + RRETURN(MATCH_NOMATCH); + } + } + else + { + if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + } + Fecode++; + break; + + + /* ===================================================================== */ + /* Start of match assertion */ + + case OP_SOM: + if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + + /* ===================================================================== */ + /* Reset the start of match point */ + + case OP_SET_SOM: + Fstart_match = Feptr; + Fecode++; + break; + + + /* ===================================================================== */ + /* Word boundary assertions. Find out if the previous and current + characters are "word" characters. It takes a bit more work in UTF mode. + Characters > 255 are assumed to be "non-word" characters when PCRE2_UCP is + not set. When it is set, use Unicode properties if available, even when not + in UTF mode. Remember the earliest and latest consulted characters. */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + if (Feptr == mb->start_subject) prev_is_word = FALSE; else + { + PCRE2_SPTR lastptr = Feptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(lastptr); + GETCHAR(fc, lastptr); + } + else +#endif /* SUPPORT_UNICODE */ + fc = *lastptr; + if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr; +#ifdef SUPPORT_UNICODE + if ((mb->poptions & PCRE2_UCP) != 0) + { + if (fc == '_') prev_is_word = TRUE; else + { + int cat = UCD_CATEGORY(fc); + prev_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif /* SUPPORT_UNICODE */ + prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0; + } + + /* Get status of next character */ + + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + cur_is_word = FALSE; + } + else + { + PCRE2_SPTR nextptr = Feptr + 1; +#ifdef SUPPORT_UNICODE + if (utf) + { + FORWARDCHARTEST(nextptr, mb->end_subject); + GETCHAR(fc, Feptr); + } + else +#endif /* SUPPORT_UNICODE */ + fc = *Feptr; + if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr; +#ifdef SUPPORT_UNICODE + if ((mb->poptions & PCRE2_UCP) != 0) + { + if (fc == '_') cur_is_word = TRUE; else + { + int cat = UCD_CATEGORY(fc); + cur_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif /* SUPPORT_UNICODE */ + cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0; + } + + /* Now see if the situation is what we want */ + + if ((*Fecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + RRETURN(MATCH_NOMATCH); + break; + + + /* ===================================================================== */ + /* Backtracking (*VERB)s, with and without arguments. Note that if the + pattern is successfully matched, we do not come back from RMATCH. */ + + case OP_MARK: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12); + + /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an + argument, and we must check whether that argument matches this MARK's + argument. It is passed back in mb->verb_skip_ptr. If it does match, we + return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject + position that corresponds to this mark. Otherwise, pass back the return + code unaltered. */ + + if (rrc == MATCH_SKIP_ARG && + PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0) + { + mb->verb_skip_ptr = Feptr; /* Pass back current position */ + RRETURN(MATCH_SKIP); + } + RRETURN(rrc); + + case OP_FAIL: + RRETURN(MATCH_NOMATCH); + + /* Record the current recursing group number in mb->verb_current_recurse + when a backtracking return such as MATCH_COMMIT is given. This enables the + recurse processing to catch verbs from within the recursion. */ + + case OP_COMMIT: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_COMMIT); + + case OP_COMMIT_ARG: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM36); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_COMMIT); + + case OP_PRUNE: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_PRUNE); + + case OP_PRUNE_ARG: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_PRUNE); + + case OP_SKIP: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_skip_ptr = Feptr; /* Pass back current position */ + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_SKIP); + + /* Note that, for Perl compatibility, SKIP with an argument does NOT set + nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was + not a matching mark, we have to re-run the match, ignoring the SKIP_ARG + that failed and any that precede it (either they also failed, or were not + triggered). To do this, we maintain a count of executed SKIP_ARGs. If a + SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg + set to the count of the one that failed. */ - case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_space) != 0) break; - eptr++; - } - break; + case OP_SKIP_ARG: + mb->skip_arg_count++; + if (mb->skip_arg_count <= mb->ignore_skip_arg) + { + Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1]; + break; + } + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); - case OP_WHITESPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_space) == 0) break; - eptr++; - } - break; + /* Pass back the current skip name and return the special MATCH_SKIP_ARG + return code. This will either be caught by a matching MARK, or get to the + top, where it causes a rematch with mb->ignore_skip_arg set to the value of + mb->skip_arg_count. */ - case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_word) != 0) break; - eptr++; - } - break; + mb->verb_skip_ptr = Fecode + 2; + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_SKIP_ARG); - case OP_WORDCHAR: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_word) == 0) break; - eptr++; - } - break; + /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that + the branch in which it occurs can be determined. */ - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } + case OP_THEN: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_ecode_ptr = Fecode; + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_THEN); - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM47); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - if (ctype == OP_ANYNL && eptr > pp && *eptr == CHAR_LF && - eptr[-1] == CHAR_CR) eptr--; - } - } + case OP_THEN_ARG: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_ecode_ptr = Fecode; + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_THEN); - /* Control never gets here */ - } + /* ===================================================================== */ /* There's been some horrible disaster. Arrival here can only mean there is something seriously wrong in the code above or the OP_xxx definitions. */ default: - RRETURN(PCRE2_ERROR_INTERNAL); + return PCRE2_ERROR_INTERNAL; } - /* Do not stick any code in here without much thought; it is assumed + /* Do not insert any code in here without much thought; it is assumed that "continue" in the code above comes out to here to repeat the main loop. */ - } /* End of main loop */ + } /* End of main loop */ /* Control never reaches here */ -/* When compiling to use the heap rather than the stack for recursive calls to -match(), the RRETURN() macro jumps here. The number that is saved in -frame->Xwhere indicates which label we actually want to return to. */ +/* ========================================================================= */ +/* The RRETURN() macro jumps here. The number that is saved in Freturn_id +indicates which label we actually want to return to. The value in Frdepth is +the index number of the frame in the vector. The return value has been placed +in rrc. */ -#ifdef HEAP_MATCH_RECURSE #define LBL(val) case val: goto L_RM##val; -HEAP_RETURN: -switch (frame->Xwhere) + +RETURN_SWITCH: +if (Frdepth == 0) return rrc; /* Exit from the top level */ +F = (heapframe *)((char *)F - Fback_frame); /* Backtrack */ +mb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */ + +#ifdef DEBUG_SHOW_RMATCH +fprintf(stderr, "++ RETURN %d to %d\n", rrc, Freturn_id); +#endif + +switch (Freturn_id) { LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8) - LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17) - LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33) - LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52) - LBL(53) LBL(54) LBL(55) LBL(56) LBL(57) LBL(58) LBL(63) LBL(64) - LBL(65) LBL(66) LBL(68) + LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16) + LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24) + LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32) + LBL(33) LBL(34) LBL(35) LBL(36) + #ifdef SUPPORT_WIDE_CHARS - LBL(20) LBL(21) + LBL(100) LBL(101) #endif + #ifdef SUPPORT_UNICODE - LBL(16) LBL(18) - LBL(22) LBL(23) LBL(28) LBL(30) - LBL(32) LBL(34) LBL(42) LBL(46) - LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45) - LBL(59) LBL(60) LBL(61) LBL(62) LBL(67) -#endif /* SUPPORT_UNICODE */ + LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206) + LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213) + LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220) + LBL(221) LBL(222) +#endif + default: return PCRE2_ERROR_INTERNAL; } #undef LBL -#endif /* HEAP_MATCH_RECURSE */ -} - - -/*************************************************************************** -**************************************************************************** - RECURSION IN THE match() FUNCTION - -Undefine all the macros that were defined above to handle this. */ - -#ifdef HEAP_MATCH_RECURSE -#undef eptr -#undef ecode -#undef mstart -#undef offset_top -#undef eptrb -#undef flags - -#undef callpat -#undef charptr -#undef data -#undef next_ecode -#undef pp -#undef prev -#undef saved_eptr - -#undef new_recursive - -#undef cur_is_word -#undef condition -#undef prev_is_word - -#undef ctype -#undef length -#undef max -#undef min -#undef number -#undef offset -#undef op -#undef save_capture_last -#undef save_offset1 -#undef save_offset2 -#undef save_offset3 - -#undef newptrb -#endif /* HEAP_MATCH_RECURSE */ - -/* These two are defined as macros in both cases */ - -#undef fc -#undef fi - -/*************************************************************************** -***************************************************************************/ - - -#ifdef HEAP_MATCH_RECURSE -/************************************************* -* Release allocated heap frames * -*************************************************/ - -/* This function releases all the allocated frames. The base frame is on the -machine stack, and so must not be freed. - -Argument: - frame_base the address of the base frame - mb the match block - -Returns: nothing -*/ - -static void -release_match_heapframes (heapframe *frame_base, match_block *mb) -{ -heapframe *nextframe = frame_base->Xnextframe; -while (nextframe != NULL) - { - heapframe *oldframe = nextframe; - nextframe = nextframe->Xnextframe; - mb->stack_memctl.free(oldframe, mb->stack_memctl.memory_data); - } } -#endif /* HEAP_MATCH_RECURSE */ - /************************************************* @@ -6448,8 +6000,6 @@ pcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, pcre2_match_context *mcontext) { int rc; -int ocount; - const uint8_t *start_bits = NULL; const pcre2_real_code *re = (const pcre2_real_code *)code; @@ -6459,7 +6009,6 @@ BOOL firstline; BOOL has_first_cu = FALSE; BOOL has_req_cu = FALSE; BOOL startline; -BOOL using_temporary_offsets = FALSE; BOOL utf; PCRE2_UCHAR first_cu = 0; @@ -6474,18 +6023,22 @@ PCRE2_SPTR req_cu_ptr = start_match - 1; PCRE2_SPTR start_partial = NULL; PCRE2_SPTR match_partial = NULL; -/* We need to have mb pointing to a match block, because the IS_NEWLINE macro -is used below, and it expects NLBLOCK to be defined as a pointer. */ +PCRE2_SIZE frame_size; + +/* We need to have mb as a pointer to a match block, because the IS_NEWLINE +macro is used below, and it expects NLBLOCK to be defined as a pointer. */ +pcre2_callout_block cb; match_block actual_match_block; match_block *mb = &actual_match_block; -#ifdef HEAP_MATCH_RECURSE -heapframe frame_zero; -frame_zero.Xprevframe = NULL; /* Marks the top level */ -frame_zero.Xnextframe = NULL; /* None are allocated yet */ -mb->match_frames_base = &frame_zero; -#endif +/* Allocate an initial vector of backtracking frames on the stack. If this +proves to be too small, it is replaced by a larger one on the heap. To get a +vector of the size required that is aligned for pointers, allocate it as a +vector of pointers. */ + +PCRE2_SPTR stack_frames_vector[START_FRAMES_SIZE/sizeof(PCRE2_SPTR)]; +mb->stack_frames = (heapframe *)stack_frames_vector; /* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated subject string. */ @@ -6514,8 +6067,8 @@ options variable for this function. Users of PCRE2 who are not calling the function directly would like to have a way of setting these flags, in the same way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and -(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which can now be -transferred to the options for this function. The bits are guaranteed to be +(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which we now +transfer to the options for this function. The bits are guaranteed to be adjacent, but do not have the same values. This bit of Boolean trickery assumes that the match-time bits are not more significant than the flag bits. If by accident this is not the case, a compile-time division by zero error will @@ -6527,20 +6080,22 @@ options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); #undef FF #undef OO -/* A NULL match context means "use a default context" */ - -if (mcontext == NULL) - mcontext = (pcre2_match_context *)(&PRIV(default_match_context)); - /* These two settings are used in the code for checking a UTF string that follows immediately afterwards. Other values in the mb block are used only -during interpretive pcre_match() processing, not when the JIT support is in -use, so they are set up later. */ +during interpretive processing, not when the JIT support is in use, so they are +set up later. */ utf = (re->overall_options & PCRE2_UTF) != 0; mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 : ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0; +/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same +time. */ + +if (mb->partial != 0 && + ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0) + return PCRE2_ERROR_BADOPTION; + /* Check a UTF string for validity if required. For 8-bit and 16-bit strings, we must also check that a starting offset does not point into the middle of a multiunit character. We check only the portion of the subject that is going to @@ -6599,7 +6154,7 @@ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) /* It is an error to set an offset limit without setting the flag at compile time. */ -if (mcontext->offset_limit != PCRE2_UNSET && +if (mcontext != NULL && mcontext->offset_limit != PCRE2_UNSET && (re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0) return PCRE2_ERROR_BADOFFSETLIMIT; @@ -6618,7 +6173,15 @@ if (re->executable_jit != NULL && (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0) } #endif -/* Carry on with non-JIT matching. */ +/* Carry on with non-JIT matching. A NULL match context means "use a default +context", but we take the memory control functions from the pattern. */ + +if (mcontext == NULL) + { + mcontext = (pcre2_match_context *)(&PRIV(default_match_context)); + mb->memctl = re->memctl; + } +else mb->memctl = mcontext->memctl; anchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; @@ -6626,14 +6189,19 @@ startline = (re->flags & PCRE2_STARTLINE) != 0; bumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)? end_subject : subject + mcontext->offset_limit; -/* Fill in the fields in the match block. */ +/* Initialize and set up the fixed fields in the callout block, with a pointer +in the match block. */ + +mb->cb = &cb; +cb.version = 2; +cb.subject = subject; +cb.subject_length = (PCRE2_SIZE)(end_subject - subject); +cb.callout_flags = 0; + +/* Fill in the remaining fields in the match block. */ mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; -mb->memctl = mcontext->memctl; -#ifdef HEAP_MATCH_RECURSE -mb->stack_memctl = mcontext->stack_memctl; -#endif mb->start_subject = subject; mb->start_offset = start_offset; @@ -6645,8 +6213,6 @@ mb->poptions = re->overall_options; /* Pattern options */ mb->ignore_skip_arg = 0; mb->mark = mb->nomatch_mark = NULL; /* In case never set */ -mb->recursive = NULL; /* No recursion at top level */ -mb->ovecsave_chain = NULL; /* No ovecsave blocks yet */ mb->hitend = FALSE; /* The name table is needed for finding all the numbers associated with a @@ -6657,20 +6223,6 @@ mb->name_count = re->name_count; mb->name_entry_size = re->name_entry_size; mb->start_code = mb->name_table + re->name_count * re->name_entry_size; -/* Limits set in the pattern override the match context only if they are -smaller. */ - -mb->match_limit = (mcontext->match_limit < re->limit_match)? - mcontext->match_limit : re->limit_match; -mb->match_limit_recursion = (mcontext->recursion_limit < re->limit_recursion)? - mcontext->recursion_limit : re->limit_recursion; - -/* Pointers to the individual character tables */ - -mb->lcc = re->tables + lcc_offset; -mb->fcc = re->tables + fcc_offset; -mb->ctypes = re->tables + ctypes_offset; - /* Process the \R and newline settings. */ mb->bsr_convention = re->bsr_convention; @@ -6687,6 +6239,11 @@ switch(re->newline_convention) mb->nl[0] = CHAR_NL; break; + case PCRE2_NEWLINE_NUL: + mb->nllen = 1; + mb->nl[0] = CHAR_NUL; + break; + case PCRE2_NEWLINE_CRLF: mb->nllen = 2; mb->nl[0] = CHAR_CR; @@ -6704,71 +6261,91 @@ switch(re->newline_convention) default: return PCRE2_ERROR_INTERNAL; } -/* If the expression has got more back references than the offsets supplied can -hold, we get a temporary chunk of memory to use during the matching. Otherwise, -we can use the vector supplied. The size of the ovector is three times the -value in the oveccount field. Two-thirds of it is pairs for storing matching -offsets, and the top third is working space. */ +/* The backtracking frames have fixed data at the front, and a PCRE2_SIZE +vector at the end, whose size depends on the number of capturing parentheses in +the pattern. It is not used at all if there are no capturing parentheses. + + frame_size is the total size of each frame + mb->frame_vector_size is the total usable size of the vector (rounded down + to a whole number of frames) + +The last of these is changed within the match() function if the frame vector +has to be expanded. We therefore put it into the match block so that it is +correct when calling match() more than once for non-anchored patterns. */ + +frame_size = offsetof(heapframe, ovector) + + re->top_bracket * 2 * sizeof(PCRE2_SIZE); -if (re->top_backref >= match_data->oveccount) +/* Limits set in the pattern override the match context only if they are +smaller. */ + +mb->heap_limit = (mcontext->heap_limit < re->limit_heap)? + mcontext->heap_limit : re->limit_heap; + +mb->match_limit = (mcontext->match_limit < re->limit_match)? + mcontext->match_limit : re->limit_match; + +mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)? + mcontext->depth_limit : re->limit_depth; + +/* If a pattern has very many capturing parentheses, the frame size may be very +large. Ensure that there are at least 10 available frames by getting an initial +vector on the heap if necessary, except when the heap limit prevents this. Get +fewer if possible. (The heap limit is in kibibytes.) */ + +if (frame_size <= START_FRAMES_SIZE/10) { - ocount = re->top_backref * 3 + 3; - mb->ovector = (PCRE2_SIZE *)(mb->memctl.malloc(ocount * sizeof(PCRE2_SIZE), - mb->memctl.memory_data)); - if (mb->ovector == NULL) return PCRE2_ERROR_NOMEMORY; - using_temporary_offsets = TRUE; + mb->match_frames = mb->stack_frames; /* Initial frame vector on the stack */ + mb->frame_vector_size = ((START_FRAMES_SIZE/frame_size) * frame_size); } else { - ocount = 3 * match_data->oveccount; - mb->ovector = match_data->ovector; + mb->frame_vector_size = frame_size * 10; + if ((mb->frame_vector_size / 1024) > mb->heap_limit) + { + if (frame_size > mb->heap_limit * 1024) return PCRE2_ERROR_HEAPLIMIT; + mb->frame_vector_size = ((mb->heap_limit * 1024)/frame_size) * frame_size; + } + mb->match_frames = mb->memctl.malloc(mb->frame_vector_size, + mb->memctl.memory_data); + if (mb->match_frames == NULL) return PCRE2_ERROR_NOMEMORY; } -mb->offset_end = ocount; -mb->offset_max = (2*ocount)/3; +mb->match_frames_top = + (heapframe *)((char *)mb->match_frames + mb->frame_vector_size); -/* Reset the working variable associated with each extraction. These should -never be used unless previously set, but they get saved and restored, and so we -initialize them to avoid reading uninitialized locations. Also, unset the -offsets for the matched string. This is really just for tidiness with callouts, -in case they inspect these fields. */ +/* Write to the ovector within the first frame to mark every capture unset and +to avoid uninitialized memory read errors when it is copied to a new frame. */ -if (ocount > 0) - { - register PCRE2_SIZE *iptr = mb->ovector + ocount; - register PCRE2_SIZE *iend = iptr - re->top_bracket; - if (iend < mb->ovector + 2) iend = mb->ovector + 2; - while (--iptr >= iend) *iptr = PCRE2_UNSET; - mb->ovector[0] = mb->ovector[1] = PCRE2_UNSET; - } +memset((char *)(mb->match_frames) + offsetof(heapframe, ovector), 0xff, + re->top_bracket * 2 * sizeof(PCRE2_SIZE)); + +/* Pointers to the individual character tables */ + +mb->lcc = re->tables + lcc_offset; +mb->fcc = re->tables + fcc_offset; +mb->ctypes = re->tables + ctypes_offset; -/* Set up the first code unit to match, if available. The first_codeunit value -is never set for an anchored regular expression, but the anchoring may be -forced at run time, so we have to test for anchoring. The first code unit may -be unset for an unanchored pattern, of course. If there's no first code unit -there may be a bitmap of possible first characters. */ +/* Set up the first code unit to match, if available. If there's no first code +unit there may be a bitmap of possible first characters. */ -if (!anchored) +if ((re->flags & PCRE2_FIRSTSET) != 0) { - if ((re->flags & PCRE2_FIRSTSET) != 0) + has_first_cu = TRUE; + first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); + if ((re->flags & PCRE2_FIRSTCASELESS) != 0) { - has_first_cu = TRUE; - first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); - if ((re->flags & PCRE2_FIRSTCASELESS) != 0) - { - first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu); + first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - if (utf && first_cu > 127) first_cu2 = UCD_OTHERCASE(first_cu); + if (utf && first_cu > 127) first_cu2 = UCD_OTHERCASE(first_cu); #endif - } } - else - if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) - start_bits = re->start_bitmap; } +else + if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) + start_bits = re->start_bitmap; -/* For anchored or unanchored matches, there may be a "last known required -character" set. */ +/* There may also be a "last known required character" set. */ if ((re->flags & PCRE2_LASTSET) != 0) { @@ -6792,7 +6369,6 @@ the loop runs just once. */ for(;;) { PCRE2_SPTR new_start_match; - mb->capture_last = 0; /* ----------------- Start of match optimizations ---------------- */ @@ -6803,13 +6379,11 @@ for(;;) if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) { - PCRE2_SPTR save_end_subject = end_subject; - /* If firstline is TRUE, the start of the match is constrained to the first line of a multiline string. That is, the match must be before or at the - first newline. Implement this by temporarily adjusting end_subject so that - we stop the optimization scans at a newline. If the match fails at the - newline, later code breaks this loop. */ + first newline following the start of matching. Temporarily adjust + end_subject so that we stop the scans for a first code unit at a newline. + If the match fails at the newline, later code breaks the loop. */ if (firstline) { @@ -6817,102 +6391,179 @@ for(;;) #ifdef SUPPORT_UNICODE if (utf) { - while (t < mb->end_subject && !IS_NEWLINE(t)) + while (t < end_subject && !IS_NEWLINE(t)) { t++; - ACROSSCHAR(t < end_subject, *t, t++); + ACROSSCHAR(t < end_subject, t, t++); } } else #endif - while (t < mb->end_subject && !IS_NEWLINE(t)) t++; + while (t < end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } - /* Advance to a unique first code unit if there is one. In 8-bit mode, the - use of memchr() gives a big speed up. */ + /* Anchored: check the first code unit if one is recorded. This may seem + pointless but it can help in detecting a no match case without scanning for + the required code unit. */ - if (has_first_cu) + if (anchored) { - PCRE2_UCHAR smc; - if (first_cu != first_cu2) - while (start_match < end_subject && - (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2) - start_match++; - else + if (has_first_cu || start_bits != NULL) { + BOOL ok = start_match < end_subject; + if (ok) + { + PCRE2_UCHAR c = UCHAR21TEST(start_match); + ok = has_first_cu && (c == first_cu || c == first_cu2); + if (!ok && start_bits != NULL) + { #if PCRE2_CODE_UNIT_WIDTH != 8 - while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu) - start_match++; -#else - start_match = memchr(start_match, first_cu, end_subject - start_match); - if (start_match == NULL) start_match = end_subject; + if (c > 255) c = 255; #endif + ok = (start_bits[c/8] & (1 << (c&7))) != 0; + } + } + if (!ok) + { + rc = MATCH_NOMATCH; + break; + } } } - /* Or to just after a linebreak for a multiline match */ + /* Not anchored. Advance to a unique first code unit if there is one. In + 8-bit mode, the use of memchr() gives a big speed up, even though we have + to call it twice in caseless mode, in order to find the earliest occurrence + of the character in either of its cases. */ - else if (startline) + else { - if (start_match > mb->start_subject + start_offset) + if (has_first_cu) { -#ifdef SUPPORT_UNICODE - if (utf) + if (first_cu != first_cu2) /* Caseless */ { - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - { +#if PCRE2_CODE_UNIT_WIDTH != 8 + PCRE2_UCHAR smc; + while (start_match < end_subject && + (smc = UCHAR21TEST(start_match)) != first_cu && + smc != first_cu2) start_match++; - ACROSSCHAR(start_match < end_subject, *start_match, - start_match++); - } +#else /* 8-bit code units */ + PCRE2_SPTR pp1 = + memchr(start_match, first_cu, end_subject-start_match); + PCRE2_SPTR pp2 = + memchr(start_match, first_cu2, end_subject-start_match); + if (pp1 == NULL) + start_match = (pp2 == NULL)? end_subject : pp2; + else + start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2; +#endif } + + /* The caseful case */ + else + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + while (start_match < end_subject && UCHAR21TEST(start_match) != + first_cu) + start_match++; +#else + start_match = memchr(start_match, first_cu, end_subject - start_match); + if (start_match == NULL) start_match = end_subject; #endif - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - start_match++; + } - /* If we have just passed a CR and the newline option is ANY or - ANYCRLF, and we are now at a LF, advance the match position by one more - code unit. */ + /* If we can't find the required code unit, having reached the true end + of the subject, break the bumpalong loop, to force a match failure, + except when doing partial matching, when we let the next cycle run at + the end of the subject. To see why, consider the pattern /(?<=abc)def/, + which partially matches "abc", even though the string does not contain + the starting character "d". If we have not reached the true end of the + subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified) + we also let the cycle run, because the matching string is legitimately + allowed to start with the first code unit of a newline. */ + + if (!mb->partial && start_match >= mb->end_subject) + { + rc = MATCH_NOMATCH; + break; + } + } - if (start_match[-1] == CHAR_CR && - (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && - start_match < end_subject && - UCHAR21TEST(start_match) == CHAR_NL) - start_match++; + /* If there's no first code unit, advance to just after a linebreak for a + multiline match if required. */ + + else if (startline) + { + if (start_match > mb->start_subject + start_offset) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + { + start_match++; + ACROSSCHAR(start_match < end_subject, start_match, start_match++); + } + } + else +#endif + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + start_match++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one + more code unit. */ + + if (start_match[-1] == CHAR_CR && + (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + UCHAR21TEST(start_match) == CHAR_NL) + start_match++; + } } - } - /* Or to a non-unique first code unit if any have been identified. The - bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all - code units greater than 254 set the 255 bit. */ + /* If there's no first code unit or a requirement for a multiline line + start, advance to a non-unique first code unit if any have been + identified. The bitmap contains only 256 bits. When code units are 16 or + 32 bits wide, all code units greater than 254 set the 255 bit. */ - else if (start_bits != NULL) - { - while (start_match < end_subject) + else if (start_bits != NULL) { - register uint32_t c = UCHAR21TEST(start_match); + while (start_match < end_subject) + { + uint32_t c = UCHAR21TEST(start_match); #if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) c = 255; + if (c > 255) c = 255; #endif - if ((start_bits[c/8] & (1 << (c&7))) != 0) break; - start_match++; + if ((start_bits[c/8] & (1 << (c&7))) != 0) break; + start_match++; + } + + /* See comment above in first_cu checking about the next few lines. */ + + if (!mb->partial && start_match >= mb->end_subject) + { + rc = MATCH_NOMATCH; + break; + } } - } + } /* End first code unit handling */ /* Restore fudged end_subject */ - end_subject = save_end_subject; + end_subject = mb->end_subject; - /* The following two optimizations are disabled for partial matching. */ + /* The following two optimizations must be disabled for partial matching. */ if (!mb->partial) { - /* The minimum matching length is a lower bound; no actual string of that - length may actually match the pattern. Although the value is, strictly, - in characters, we treat it as code units to avoid spending too much time - in this optimization. */ + /* The minimum matching length is a lower bound; no string of that length + may actually match the pattern. Although the value is, strictly, in + characters, we treat it as code units to avoid spending too much time in + this optimization. */ if (end_subject - start_match < re->minlength) { @@ -6921,12 +6572,16 @@ for(;;) } /* If req_cu is set, we know that that code unit must appear in the - subject for the match to succeed. If the first code unit is set, req_cu - must be later in the subject; otherwise the test starts at the match - point. This optimization can save a huge amount of backtracking in - patterns with nested unlimited repeats that aren't going to match. - Writing separate code for cased/caseless versions makes it go faster, as - does using an autoincrement and backing off on a match. + subject for the (non-partial) match to succeed. If the first code unit is + set, req_cu must be later in the subject; otherwise the test starts at + the match point. This optimization can save a huge amount of backtracking + in patterns with nested unlimited repeats that aren't going to match. + Writing separate code for caseful/caseless versions makes it go faster, + as does using an autoincrement and backing off on a match. As in the case + of the first code unit, using memchr() in the 8-bit library gives a big + speed up. Unlike the first_cu check above, we do not need to call + memchr() twice in the caseless case because we only need to check for the + presence of the character in either case, not find the first occurrence. HOWEVER: when the subject string is very, very long, searching to its end can take a long time, and give bad performance on quite ordinary @@ -6936,30 +6591,55 @@ for(;;) if (has_req_cu && end_subject - start_match < REQ_CU_MAX) { - register PCRE2_SPTR p = start_match + (has_first_cu? 1:0); + PCRE2_SPTR p = start_match + (has_first_cu? 1:0); /* We don't need to repeat the search if we haven't yet reached the - place we found it at last time. */ + place we found it last time round the bumpalong loop. */ if (p > req_cu_ptr) { - if (req_cu != req_cu2) + if (p < end_subject) { - while (p < end_subject) + if (req_cu != req_cu2) /* Caseless */ { - register uint32_t pp = UCHAR21INCTEST(p); - if (pp == req_cu || pp == req_cu2) { p--; break; } +#if PCRE2_CODE_UNIT_WIDTH != 8 + do + { + uint32_t pp = UCHAR21INCTEST(p); + if (pp == req_cu || pp == req_cu2) { p--; break; } + } + while (p < end_subject); + +#else /* 8-bit code units */ + PCRE2_SPTR pp = p; + p = memchr(pp, req_cu, end_subject - pp); + if (p == NULL) + { + p = memchr(pp, req_cu2, end_subject - pp); + if (p == NULL) p = end_subject; + } +#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */ } - } - else - { - while (p < end_subject) + + /* The caseful case */ + + else { - if (UCHAR21INCTEST(p) == req_cu) { p--; break; } +#if PCRE2_CODE_UNIT_WIDTH != 8 + do + { + if (UCHAR21INCTEST(p) == req_cu) { p--; break; } + } + while (p < end_subject); + +#else /* 8-bit code units */ + p = memchr(p, req_cu, end_subject - p); + if (p == NULL) p = end_subject; +#endif } } - /* If we can't find the required code unit, break the matching loop, + /* If we can't find the required code unit, break the bumpalong loop, forcing a match failure. */ if (p >= end_subject) @@ -6969,8 +6649,8 @@ for(;;) } /* If we have found the required code unit, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this code unit yet. */ + found it, so that we don't search again next time round the bumpalong + loop if the start hasn't yet passed this code unit. */ req_cu_ptr = p; } @@ -6991,14 +6671,17 @@ for(;;) /* OK, we can now run the match. If "hitend" is set afterwards, remember the first starting point for which a partial match was found. */ - mb->start_match_ptr = start_match; + cb.start_match = (PCRE2_SIZE)(start_match - subject); + cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH; + mb->start_used_ptr = start_match; mb->last_used_ptr = start_match; mb->match_call_count = 0; - mb->match_function_type = 0; mb->end_offset_top = 0; mb->skip_arg_count = 0; - rc = match(start_match, mb->start_code, start_match, 2, mb, NULL, 0); + + rc = match(start_match, mb->start_code, match_data->ovector, + match_data->oveccount, re->top_bracket, frame_size, mb); if (mb->hitend && start_partial == NULL) { @@ -7024,9 +6707,9 @@ for(;;) greater than the match we have just done, treat it as NOMATCH. */ case MATCH_SKIP: - if (mb->start_match_ptr > start_match) + if (mb->verb_skip_ptr > start_match) { - new_start_match = mb->start_match_ptr; + new_start_match = mb->verb_skip_ptr; break; } /* Fall through */ @@ -7041,7 +6724,7 @@ for(;;) new_start_match = start_match + 1; #ifdef SUPPORT_UNICODE if (utf) - ACROSSCHAR(new_start_match < end_subject, *new_start_match, + ACROSSCHAR(new_start_match < end_subject, new_start_match, new_start_match++); #endif break; @@ -7100,11 +6783,11 @@ for(;;) /* ==========================================================================*/ -/* When we reach here, one of the stopping conditions is true: +/* When we reach here, one of the following stopping conditions is true: (1) The match succeeded, either completely, or partially; -(2) The pattern is anchored or the match was failed by (*COMMIT); +(2) The pattern is anchored or the match was failed after (*COMMIT); (3) We are past the end of the subject or the bumpalong limit; @@ -7118,18 +6801,10 @@ for(;;) ENDLOOP: -#ifdef HEAP_MATCH_RECURSE -release_match_heapframes(&frame_zero, mb); -#endif - -/* Release any frames that were saved from recursions. */ +/* Release an enlarged frame vector that is on the heap. */ -while (mb->ovecsave_chain != NULL) - { - ovecsave_frame *this = mb->ovecsave_chain; - mb->ovecsave_chain = this->next; - mb->memctl.free(this, mb->memctl.memory_data); - } +if (mb->match_frames != mb->stack_frames) + mb->memctl.free(mb->match_frames, mb->memctl.memory_data); /* Fill in fields that are always returned in the match data. */ @@ -7138,68 +6813,14 @@ match_data->subject = subject; match_data->mark = mb->mark; match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER; -/* Handle a fully successful match. */ +/* Handle a fully successful match. Set the return code to the number of +captured strings, or 0 if there were too many to fit into the ovector, and then +set the remaining returned values before returning. */ -if (rc == MATCH_MATCH || rc == MATCH_ACCEPT) +if (rc == MATCH_MATCH) { - uint32_t arg_offset_max = 2 * match_data->oveccount; - - /* When the offset vector is big enough to deal with any backreferences, - captured substring offsets will already be set up. In the case where we had - to get some local memory to hold offsets for backreference processing, copy - those that we can. In this case there need not be overflow if certain parts - of the pattern were not used, even though there are more capturing - parentheses than vector slots. */ - - if (using_temporary_offsets) - { - if (arg_offset_max >= 4) - { - memcpy(match_data->ovector + 2, mb->ovector + 2, - (arg_offset_max - 2) * sizeof(PCRE2_SIZE)); - } - if (mb->end_offset_top > arg_offset_max) mb->capture_last |= OVFLBIT; - mb->memctl.free(mb->ovector, mb->memctl.memory_data); - } - - /* Set the return code to the number of captured strings, or 0 if there were - too many to fit into the ovector. */ - - match_data->rc = ((mb->capture_last & OVFLBIT) != 0)? - 0 : mb->end_offset_top/2; - - /* If there is space in the offset vector, set any pairs that follow the - highest-numbered captured string but are less than the number of capturing - groups in the pattern (and are within the ovector) to PCRE2_UNSET. It is - documented that this happens. In earlier versions, the whole set of potential - capturing offsets was initialized each time round the loop, but this is - handled differently now. "Gaps" are set to PCRE2_UNSET dynamically instead - (this fixed a bug). Thus, it is only those at the end that need setting here. - We can't just mark them all unset at the start of the whole thing because - they may get set in one branch that is not the final matching branch. */ - - if (mb->end_offset_top/2 <= re->top_bracket) - { - register PCRE2_SIZE *iptr, *iend; - int resetcount = re->top_bracket + 1; - if (resetcount > match_data->oveccount) resetcount = match_data->oveccount; - iptr = match_data->ovector + mb->end_offset_top; - iend = match_data->ovector + 2 * resetcount; - while (iptr < iend) *iptr++ = PCRE2_UNSET; - } - - /* If there is space, set up the whole thing as substring 0. The value of - mb->start_match_ptr might be modified if \K was encountered on the success - matching path. */ - - if (match_data->oveccount < 1) rc = 0; else - { - match_data->ovector[0] = mb->start_match_ptr - mb->start_subject; - match_data->ovector[1] = mb->end_match_ptr - mb->start_subject; - } - - /* Set the remaining returned values */ - + match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)? + 0 : (int)mb->end_offset_top/2 + 1; match_data->startchar = start_match - subject; match_data->leftchar = mb->start_used_ptr - subject; match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)? @@ -7215,18 +6836,14 @@ match_data->mark = mb->nomatch_mark; /* For anything other than nomatch or partial match, just return the code. */ -if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) - match_data->rc = rc; +if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc; -/* Else handle a partial match. */ +/* Handle a partial match. */ else if (match_partial != NULL) { - if (match_data->oveccount > 0) - { - match_data->ovector[0] = match_partial - subject; - match_data->ovector[1] = end_subject - subject; - } + match_data->ovector[0] = match_partial - subject; + match_data->ovector[1] = end_subject - subject; match_data->startchar = match_partial - subject; match_data->leftchar = start_partial - subject; match_data->rightchar = end_subject - subject; @@ -7237,13 +6854,7 @@ else if (match_partial != NULL) else match_data->rc = PCRE2_ERROR_NOMATCH; -/* Free any temporary offsets. */ - -if (using_temporary_offsets) - mb->memctl.free(mb->ovector, mb->memctl.memory_data); return match_data->rc; } /* End of pcre2_match.c */ - -#pragma warning(pop) \ No newline at end of file diff --git a/ProcessHacker/pcre/pcre2_match_data.c b/ProcessHacker/pcre/pcre2_match_data.c index 8b3ffcd05034..b297f326b556 100644 --- a/ProcessHacker/pcre/pcre2_match_data.c +++ b/ProcessHacker/pcre/pcre2_match_data.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -51,7 +51,7 @@ POSSIBILITY OF SUCH DAMAGE. * Create a match data block given ovector size * *************************************************/ -/* A minimum of 1 is imposed on the number of ovector triplets. */ +/* A minimum of 1 is imposed on the number of ovector pairs. */ PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext) @@ -59,7 +59,7 @@ pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext) pcre2_match_data *yield; if (oveccount < 1) oveccount = 1; yield = PRIV(memctl_malloc)( - sizeof(pcre2_match_data) + 3*oveccount*sizeof(PCRE2_SIZE), + offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE), (pcre2_memctl *)gcontext); if (yield == NULL) return NULL; yield->oveccount = oveccount; diff --git a/ProcessHacker/pcre/pcre2_newline.c b/ProcessHacker/pcre/pcre2_newline.c index 2aab03a294fd..6e9366db93c6 100644 --- a/ProcessHacker/pcre/pcre2_newline.c +++ b/ProcessHacker/pcre/pcre2_newline.c @@ -47,7 +47,7 @@ only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF, and NLTYPE_ANY. The full list of Unicode newline characters is taken from http://unicode.org/unicode/reports/tr18/. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2_ord2utf.c b/ProcessHacker/pcre/pcre2_ord2utf.c index 482e30a16b01..1403730996d6 100644 --- a/ProcessHacker/pcre/pcre2_ord2utf.c +++ b/ProcessHacker/pcre/pcre2_ord2utf.c @@ -42,7 +42,7 @@ POSSIBILITY OF SUCH DAMAGE. /* This file contains a function that converts a Unicode character code point into a UTF string. The behaviour is different for each code unit width. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -83,7 +83,7 @@ PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer) /* Convert to UTF-8 */ #if PCRE2_CODE_UNIT_WIDTH == 8 -register int i, j; +int i, j; for (i = 0; i < PRIV(utf8_table1_size); i++) if ((int)cvalue <= PRIV(utf8_table1)[i]) break; buffer += i; diff --git a/ProcessHacker/pcre/pcre2_pattern_info.c b/ProcessHacker/pcre/pcre2_pattern_info.c index 39c1162e8ab3..a29f5eff673b 100644 --- a/ProcessHacker/pcre/pcre2_pattern_info.c +++ b/ProcessHacker/pcre/pcre2_pattern_info.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -75,10 +75,13 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_BACKREFMAX: case PCRE2_INFO_BSR: case PCRE2_INFO_CAPTURECOUNT: + case PCRE2_INFO_DEPTHLIMIT: + case PCRE2_INFO_EXTRAOPTIONS: case PCRE2_INFO_FIRSTCODETYPE: case PCRE2_INFO_FIRSTCODEUNIT: case PCRE2_INFO_HASBACKSLASHC: case PCRE2_INFO_HASCRORLF: + case PCRE2_INFO_HEAPLIMIT: case PCRE2_INFO_JCHANGED: case PCRE2_INFO_LASTCODETYPE: case PCRE2_INFO_LASTCODEUNIT: @@ -89,7 +92,6 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_NAMEENTRYSIZE: case PCRE2_INFO_NAMECOUNT: case PCRE2_INFO_NEWLINE: - case PCRE2_INFO_RECURSIONLIMIT: return sizeof(uint32_t); case PCRE2_INFO_FIRSTBITMAP: @@ -97,6 +99,7 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_JITSIZE: case PCRE2_INFO_SIZE: + case PCRE2_INFO_FRAMESIZE: return sizeof(size_t); case PCRE2_INFO_NAMETABLE: @@ -137,6 +140,15 @@ switch(what) *((uint32_t *)where) = re->top_bracket; break; + case PCRE2_INFO_DEPTHLIMIT: + *((uint32_t *)where) = re->limit_depth; + if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET; + break; + + case PCRE2_INFO_EXTRAOPTIONS: + *((uint32_t *)where) = re->extra_options; + break; + case PCRE2_INFO_FIRSTCODETYPE: *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 : ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0; @@ -152,6 +164,11 @@ switch(what) &(re->start_bitmap[0]) : NULL; break; + case PCRE2_INFO_FRAMESIZE: + *((size_t *)where) = offsetof(heapframe, ovector) + + re->top_bracket * 2 * sizeof(PCRE2_SIZE); + break; + case PCRE2_INFO_HASBACKSLASHC: *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0; break; @@ -160,6 +177,11 @@ switch(what) *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0; break; + case PCRE2_INFO_HEAPLIMIT: + *((uint32_t *)where) = re->limit_heap; + if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET; + break; + case PCRE2_INFO_JCHANGED: *((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0; break; @@ -215,11 +237,6 @@ switch(what) *((uint32_t *)where) = re->newline_convention; break; - case PCRE2_INFO_RECURSIONLIMIT: - *((uint32_t *)where) = re->limit_recursion; - if (re->limit_recursion == UINT32_MAX) return PCRE2_ERROR_UNSET; - break; - case PCRE2_INFO_SIZE: *((size_t *)where) = re->blocksize; break; @@ -255,11 +272,15 @@ pcre2_real_code *re = (pcre2_real_code *)code; pcre2_callout_enumerate_block cb; PCRE2_SPTR cc; #ifdef SUPPORT_UNICODE -BOOL utf = (re->overall_options & PCRE2_UTF) != 0; +BOOL utf; #endif if (re == NULL) return PCRE2_ERROR_NULL; +#ifdef SUPPORT_UNICODE +utf = (re->overall_options & PCRE2_UTF) != 0; +#endif + /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ @@ -369,6 +390,7 @@ while (TRUE) #endif case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/ProcessHacker/pcre/pcre2_printint.c b/ProcessHacker/pcre/pcre2_printint.c index 2d30926a744e..bd10c6b1da3d 100644 --- a/ProcessHacker/pcre/pcre2_printint.c +++ b/ProcessHacker/pcre/pcre2_printint.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -206,7 +206,7 @@ print_custring(FILE *f, PCRE2_SPTR ptr) { while (*ptr != '\0') { - register uint32_t c = *ptr++; + uint32_t c = *ptr++; if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c); } } @@ -216,7 +216,7 @@ print_custring_bylen(FILE *f, PCRE2_SPTR ptr, PCRE2_UCHAR len) { for (; len > 0; len--) { - register uint32_t c = *ptr++; + uint32_t c = *ptr++; if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c); } } @@ -340,7 +340,7 @@ for(;;) case OP_TABLE_LENGTH + ((sizeof(OP_names)/sizeof(const char *) == OP_TABLE_LENGTH) && (sizeof(OP_lengths) == OP_TABLE_LENGTH)): - break; + return; /* ========================================================================== */ case OP_END: @@ -393,7 +393,6 @@ for(;;) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_COND: case OP_SCOND: case OP_REVERSE: @@ -800,6 +799,7 @@ for(;;) break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/ProcessHacker/pcre/pcre2_serialize.c b/ProcessHacker/pcre/pcre2_serialize.c index 1aa3c54bd901..cec1a035d19d 100644 --- a/ProcessHacker/pcre/pcre2_serialize.c +++ b/ProcessHacker/pcre/pcre2_serialize.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ POSSIBILITY OF SUCH DAMAGE. /* This module contains functions for serializing and deserializing a sequence of compiled codes. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -127,7 +127,25 @@ dst_bytes += tables_length; for (i = 0; i < number_of_codes; i++) { re = (const pcre2_real_code *)(codes[i]); - memcpy(dst_bytes, (char *)re, re->blocksize); + (void)memcpy(dst_bytes, (char *)re, re->blocksize); + + /* Certain fields in the compiled code block are re-set during + deserialization. In order to ensure that the serialized data stream is always + the same for the same pattern, set them to zero here. We can't assume the + copy of the pattern is correctly aligned for accessing the fields as part of + a structure. Note the use of sizeof(void *) in the second of these, to + specify the size of a pointer. If sizeof(uint8_t *) is used (tables is a + pointer to uint8_t), gcc gives a warning because the first argument is also a + pointer to uint8_t. Casting the first argument to (void *) can stop this, but + it didn't stop Coverity giving the same complaint. */ + + (void)memset(dst_bytes + offsetof(pcre2_real_code, memctl), 0, + sizeof(pcre2_memctl)); + (void)memset(dst_bytes + offsetof(pcre2_real_code, tables), 0, + sizeof(void *)); + (void)memset(dst_bytes + offsetof(pcre2_real_code, executable_jit), 0, + sizeof(void *)); + dst_bytes += re->blocksize; } @@ -214,7 +232,10 @@ for (i = 0; i < number_of_codes; i++) if (dst_re->magic_number != MAGIC_NUMBER || dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 || dst_re->name_count > MAX_NAME_COUNT) + { + memctl->free(dst_re, memctl->memory_data); return PCRE2_ERROR_BADSERIALIZEDDATA; + } /* At the moment only one table is supported. */ diff --git a/ProcessHacker/pcre/pcre2_string_utils.c b/ProcessHacker/pcre/pcre2_string_utils.c index feaa09894c48..d6be01acf55a 100644 --- a/ProcessHacker/pcre/pcre2_string_utils.c +++ b/ProcessHacker/pcre/pcre2_string_utils.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -42,7 +42,7 @@ POSSIBILITY OF SUCH DAMAGE. of strings. These are used instead of strcmp() etc because the standard functions work only on 8-bit data. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -50,6 +50,42 @@ functions work only on 8-bit data. */ #include "pcre2_internal.h" +/************************************************* +* Emulated memmove() for systems without it * +*************************************************/ + +/* This function can make use of bcopy() if it is available. Otherwise do it by +steam, as there some non-Unix environments that lack both memmove() and +bcopy(). */ + +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +void * +PRIV(memmove)(void *d, const void *s, size_t n) +{ +#ifdef HAVE_BCOPY +bcopy(s, d, n); +return d; +#else +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +#endif /* not HAVE_BCOPY */ +} +#endif /* not VPCOMPAT && not HAVE_MEMMOVE */ + + /************************************************* * Compare two zero-terminated PCRE2 strings * *************************************************/ diff --git a/ProcessHacker/pcre/pcre2_study.c b/ProcessHacker/pcre/pcre2_study.c index 9bd0667e69fc..acbf98b41b51 100644 --- a/ProcessHacker/pcre/pcre2_study.c +++ b/ProcessHacker/pcre/pcre2_study.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -41,14 +41,16 @@ POSSIBILITY OF SUCH DAMAGE. /* This module contains functions for scanning a compiled pattern and collecting data (e.g. minimum matching length). */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif - #include "pcre2_internal.h" +/* The maximum remembered capturing brackets minimum. */ + +#define MAX_CACHE_BACKREF 128 /* Set a bit in the starting code unit bit map. */ @@ -71,6 +73,12 @@ length is 16-bits long (on the grounds that anything longer than that is pathological), so we give up when we reach that amount. This also means that integer overflow for really crazy patterns cannot happen. +Backreference minimum lengths are cached to speed up multiple references. This +function is called only when the highest back reference in the pattern is less +than or equal to MAX_CACHE_BACKREF, which is one less than the size of the +caching vector. The zeroth element contains the number of the highest set +value. + Arguments: re compiled pattern block code pointer to start of group (the bracket) @@ -78,6 +86,7 @@ integer overflow for really crazy patterns cannot happen. utf UTF flag recurses chain of recurse_check to catch mutual recursion countptr pointer to call count (to catch over complexity) + backref_cache vector for caching back references. Returns: the minimum length -1 \C in UTF-8 mode @@ -90,7 +99,8 @@ Returns: the minimum length static int find_minlength(const pcre2_real_code *re, PCRE2_SPTR code, - PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr) + PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr, + int *backref_cache) { int length = -1; int prev_cap_recno = -1; @@ -101,8 +111,8 @@ uint32_t once_fudge = 0; BOOL had_recurse = FALSE; BOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0; recurse_check this_recurse; -register int branchlength = 0; -register PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE; +int branchlength = 0; +PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE; /* If this is a "could be empty" group, its minimum length is 0. */ @@ -124,7 +134,7 @@ for (;;) { int d, min, recno; PCRE2_UCHAR *cs, *ce; - register PCRE2_UCHAR op = *cc; + PCRE2_UCHAR op = *cc; if (branchlength >= UINT16_MAX) return UINT16_MAX; @@ -146,12 +156,12 @@ for (;;) } goto PROCESS_NON_CAPTURE; - /* There's a special case of OP_ONCE, when it is wrapped round an + case OP_BRA: + /* There's a special case of OP_BRA, when it is wrapped round a repeated OP_RECURSE. We'd like to process the latter at this level so that remembering the value works for repeated cases. So we do nothing, but set a fudge value to skip over the OP_KET after the recurse. */ - case OP_ONCE: if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET) { once_fudge = 1 + LINK_SIZE; @@ -160,13 +170,13 @@ for (;;) } /* Fall through */ - case OP_ONCE_NC: - case OP_BRA: + case OP_ONCE: case OP_SBRA: case OP_BRAPOS: case OP_SBRAPOS: PROCESS_NON_CAPTURE: - d = find_minlength(re, cc, startcode, utf, recurses, countptr); + d = find_minlength(re, cc, startcode, utf, recurses, countptr, + backref_cache); if (d < 0) return d; branchlength += d; do cc += GET(cc, 1); while (*cc == OP_ALT); @@ -182,11 +192,12 @@ for (;;) case OP_SCBRA: case OP_CBRAPOS: case OP_SCBRAPOS: - recno = dupcapused? prev_cap_recno - 1 : (int)GET2(cc, 1+LINK_SIZE); - if (recno != prev_cap_recno) + recno = (int)GET2(cc, 1+LINK_SIZE); + if (dupcapused || recno != prev_cap_recno) { prev_cap_recno = recno; - prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr); + prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr, + backref_cache); if (prev_cap_d < 0) return prev_cap_d; } branchlength += prev_cap_d; @@ -456,38 +467,52 @@ for (;;) d = INT_MAX; - /* Scan all groups with the same name */ + /* Scan all groups with the same name; find the shortest. */ while (count-- > 0) { - ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, GET2(slot, 0)); - if (cs == NULL) return -2; - do ce += GET(ce, 1); while (*ce == OP_ALT); - if (cc > cs && cc < ce) /* Simple recursion */ - { - d = 0; - had_recurse = TRUE; - break; - } + int dd, i; + recno = GET2(slot, 0); + + if (recno <= backref_cache[0] && backref_cache[recno] >= 0) + dd = backref_cache[recno]; else { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) /* Mutual recursion */ + ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) /* Simple recursion */ { - d = 0; + dd = 0; had_recurse = TRUE; - break; } else { - int dd; - this_recurse.prev = recurses; - this_recurse.group = cs; - dd = find_minlength(re, cs, startcode, utf, &this_recurse, countptr); - if (dd < d) d = dd; + recurse_check *r = recurses; + for (r = recurses; r != NULL; r = r->prev) + if (r->group == cs) break; + if (r != NULL) /* Mutual recursion */ + { + dd = 0; + had_recurse = TRUE; + } + else + { + this_recurse.prev = recurses; + this_recurse.group = cs; + dd = find_minlength(re, cs, startcode, utf, &this_recurse, + countptr, backref_cache); + if (dd < 0) return dd; + } } + + backref_cache[recno] = dd; + for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1; + backref_cache[0] = recno; } + + if (dd < d) d = dd; + if (d <= 0) break; /* No point looking at any more */ slot += re->name_entry_size; } } @@ -501,34 +526,48 @@ for (;;) case OP_REF: case OP_REFI: if (dupcapused) return -1; - if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) + recno = GET2(cc, 1); + if (recno <= backref_cache[0] && backref_cache[recno] >= 0) + d = backref_cache[recno]; + else { - ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1)); - if (cs == NULL) return -2; - do ce += GET(ce, 1); while (*ce == OP_ALT); - if (cc > cs && cc < ce) /* Simple recursion */ + int i; + if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) { - d = 0; - had_recurse = TRUE; - } - else - { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) /* Mutual recursion */ + ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) /* Simple recursion */ { d = 0; had_recurse = TRUE; } else { - this_recurse.prev = recurses; - this_recurse.group = cs; - d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr); + recurse_check *r = recurses; + for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; + if (r != NULL) /* Mutual recursion */ + { + d = 0; + had_recurse = TRUE; + } + else + { + this_recurse.prev = recurses; + this_recurse.group = cs; + d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr, + backref_cache); + if (d < 0) return d; + } } } + else d = 0; + + backref_cache[recno] = d; + for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1; + backref_cache[0] = recno; } - else d = 0; + cc += 1 + IMM2_SIZE; /* Handle repeated back references */ @@ -601,7 +640,7 @@ for (;;) this_recurse.prev = recurses; this_recurse.group = cs; prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse, - countptr); + countptr, backref_cache); if (prev_recurse_d < 0) return prev_recurse_d; prev_recurse_recno = recno; branchlength += prev_recurse_d; @@ -668,6 +707,7 @@ for (;;) /* Skip these, but we need to add in the name length. */ case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: @@ -747,6 +787,7 @@ if (utf) if (caseless) { +#ifdef SUPPORT_UNICODE if (utf) { #if PCRE2_CODE_UNIT_WIDTH == 8 @@ -759,10 +800,12 @@ if (caseless) if (c > 0xff) SET_BIT(0xff); else SET_BIT(c); #endif } + else +#endif /* SUPPORT_UNICODE */ /* Not UTF */ - else if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]); + if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]); } return p; @@ -792,7 +835,7 @@ Returns: nothing static void set_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit) { -register uint32_t c; +uint32_t c; for (c = 0; c < table_limit; c++) re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type]; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -833,7 +876,7 @@ Returns: nothing static void set_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit) { -register uint32_t c; +uint32_t c; for (c = 0; c < table_limit; c++) re->start_bitmap[c] |= ~(re->tables[c+cbits_offset+cbit_type]); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -873,7 +916,7 @@ Returns: SSB_FAIL => Failed to find any starting code units static int set_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf) { -register uint32_t c; +uint32_t c; int yield = SSB_DONE; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -911,10 +954,10 @@ do case OP_ALLANY: case OP_ANY: case OP_ANYBYTE: - case OP_CIRC: case OP_CIRCM: case OP_CLOSE: case OP_COMMIT: + case OP_COMMIT_ARG: case OP_COND: case OP_CREF: case OP_FALSE: @@ -979,6 +1022,13 @@ do case OP_THEN_ARG: return SSB_FAIL; + /* OP_CIRC happens only at the start of an anchored branch (multiline ^ + uses OP_CIRCM). Skip over it. */ + + case OP_CIRC: + tcode += PRIV(OP_lengths)[OP_CIRC]; + break; + /* A "real" property test implies no starting bits, but the fake property PT_CLIST identifies a list of characters. These lists are short, as they are used for characters with more than one "other case", so there is no @@ -1025,7 +1075,6 @@ do case OP_CBRAPOS: case OP_SCBRAPOS: case OP_ONCE: - case OP_ONCE_NC: case OP_ASSERT: rc = set_start_bits(re, tcode, utf); if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; @@ -1227,7 +1276,7 @@ do break; /* Single character types set the bits and stop. Note that if PCRE2_UCP - is set, we do not see these op codes because \d etc are converted to + is set, we do not see these opcodes because \d etc are converted to properties. Therefore, these apply in the case when only characters less than 256 are recognized to match the types. */ @@ -1407,6 +1456,10 @@ do classmap = ((tcode[1 + LINK_SIZE] & XCL_MAP) == 0)? NULL : (uint8_t *)(tcode + 1 + LINK_SIZE + 1); #endif + /* It seems that the fall through comment must be outside the #ifdef if + it is to avoid the gcc compiler warning. */ + + /* Fall through */ /* Enter here for a negative non-XCLASS. In the 8-bit library, if we are in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter @@ -1534,24 +1587,31 @@ BOOL utf = (re->overall_options & PCRE2_UTF) != 0; code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_entry_size * re->name_count; -/* For an anchored pattern, or an unanchored pattern that has a first code -unit, or a multiline pattern that matches only at "line start", there is no -point in seeking a list of starting code units. */ +/* For a pattern that has a first code unit, or a multiline pattern that +matches only at "line start", there is no point in seeking a list of starting +code units. */ -if ((re->overall_options & PCRE2_ANCHORED) == 0 && - (re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0) +if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0) { int rc = set_start_bits(re, code, utf); if (rc == SSB_UNKNOWN) return 1; if (rc == SSB_DONE) re->flags |= PCRE2_FIRSTMAPSET; } -/* Find the minimum length of subject string. If it can match an empty string, -the minimum length is already known. */ +/* Find the minimum length of subject string. If the pattern can match an empty +string, the minimum length is already known. If there are more back references +than the size of the vector we are going to cache them in, do nothing. A +pattern that complicated will probably take a long time to analyze and may in +any case turn out to be too complicated. Note that back reference minima are +held as 16-bit numbers. */ -if ((re->flags & PCRE2_MATCH_EMPTY) == 0) +if ((re->flags & PCRE2_MATCH_EMPTY) == 0 && + re->top_backref <= MAX_CACHE_BACKREF) { - switch(min = find_minlength(re, code, code, utf, NULL, &count)) + int backref_cache[MAX_CACHE_BACKREF+1]; + backref_cache[0] = 0; /* Highest one that is set */ + min = find_minlength(re, code, code, utf, NULL, &count, backref_cache); + switch(min) { case -1: /* \C in UTF mode or (*ACCEPT) or over-complex regex */ break; /* Leave minlength unchanged (will be zero) */ diff --git a/ProcessHacker/pcre/pcre2_substitute.c b/ProcessHacker/pcre/pcre2_substitute.c index a5d0c84e98c4..ab8d10908aa2 100644 --- a/ProcessHacker/pcre/pcre2_substitute.c +++ b/ProcessHacker/pcre/pcre2_substitute.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -114,7 +114,7 @@ for (; ptr < ptrend; ptr++) else if (*ptr == CHAR_BACKSLASH) { int erc; - int errorcode = 0; + int errorcode; uint32_t ch; if (ptr < ptrend - 1) switch (ptr[1]) @@ -127,8 +127,10 @@ for (; ptr < ptrend; ptr++) continue; } + ptr += 1; /* Must point after \ */ erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode, code->overall_options, FALSE, NULL); + ptr -= 1; /* Back to last code unit of escape */ if (errorcode != 0) { rc = errorcode; @@ -236,10 +238,12 @@ PCRE2_SPTR repend; PCRE2_SIZE extra_needed = 0; PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength; PCRE2_SIZE *ovector; +PCRE2_SIZE ovecsave[3]; buff_offset = 0; lengthleft = buff_length = *blength; *blength = PCRE2_UNSET; +ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET; /* Partial matching is not valid. */ @@ -287,6 +291,12 @@ options &= ~SUBSTITUTE_OPTIONS; /* Copy up to the start offset */ +if (start_offset > length) + { + match_data->leftchar = 0; + rc = PCRE2_ERROR_BADOFFSET; + goto EXIT; + } CHECKMEMCPY(subject, start_offset); /* Loop for global substituting. */ @@ -353,13 +363,33 @@ do } /* Handle a successful match. Matches that use \K to end before they start - are not supported. */ - - if (ovector[1] < ovector[0]) + or start before the current point in the subject are not supported. */ + + if (ovector[1] < ovector[0] || ovector[0] < start_offset) { rc = PCRE2_ERROR_BADSUBSPATTERN; goto EXIT; } + + /* Check for the same match as previous. This is legitimate after matching an + empty string that starts after the initial match offset. We have tried again + at the match point in case the pattern is one like /(?<=\G.)/ which can never + match at its starting point, so running the match achieves the bumpalong. If + we do get the same (null) match at the original match point, it isn't such a + pattern, so we now do the empty string magic. In all other cases, a repeat + match should never occur. */ + + if (ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1]) + { + if (ovector[0] == ovector[1] && ovecsave[2] != start_offset) + { + goptions = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + ovecsave[2] = start_offset; + continue; /* Back to the top of the loop */ + } + rc = PCRE2_ERROR_INTERNAL_DUPMATCH; + goto EXIT; + } /* Count substitutions with a paranoid check for integer overflow; surely no real call to this function would ever hit this! */ @@ -698,7 +728,7 @@ do else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 && *ptr == CHAR_BACKSLASH) { - int errorcode = 0; + int errorcode; if (ptr < repend - 1) switch (ptr[1]) { @@ -728,10 +758,10 @@ do break; } + ptr++; /* Point after \ */ rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode, code->overall_options, FALSE, NULL); if (errorcode != 0) goto BADESCAPE; - ptr++; switch(rc) { @@ -791,13 +821,18 @@ do } /* End handling a literal code unit */ } /* End of loop for scanning the replacement. */ - /* The replacement has been copied to the output. Update the start offset to - point to the rest of the subject string. If we matched an empty string, - do the magic for global matches. */ - - start_offset = ovector[1]; - goptions = (ovector[0] != ovector[1])? 0 : + /* The replacement has been copied to the output. Save the details of this + match. See above for how this data is used. If we matched an empty string, do + the magic for global matches. Finally, update the start offset to point to + the rest of the subject string. */ + + ovecsave[0] = ovector[0]; + ovecsave[1] = ovector[1]; + ovecsave[2] = start_offset; + + goptions = (ovector[0] != ovector[1] || ovector[0] > start_offset)? 0 : PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART; + start_offset = ovector[1]; } while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0); /* Repeat "do" loop */ /* Copy the rest of the subject. */ diff --git a/ProcessHacker/pcre/pcre2_substring.c b/ProcessHacker/pcre/pcre2_substring.c index bee84b1d8e73..ddf5774e1501 100644 --- a/ProcessHacker/pcre/pcre2_substring.c +++ b/ProcessHacker/pcre/pcre2_substring.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -414,7 +414,12 @@ else for (i = 0; i < count2; i += 2) { size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0; - memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size)); + + /* Size == 0 includes the case when the capture is unset. Avoid adding + PCRE2_UNSET to match_data->subject because it overflows, even though with + zero size calling memcpy() is harmless. */ + + if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size)); *listp++ = sp; if (lensp != NULL) *lensp++ = size; sp += size; diff --git a/ProcessHacker/pcre/pcre2_tables.c b/ProcessHacker/pcre/pcre2_tables.c index a9e6bc01c521..83d6f9de55ed 100644 --- a/ProcessHacker/pcre/pcre2_tables.c +++ b/ProcessHacker/pcre/pcre2_tables.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -39,13 +39,12 @@ POSSIBILITY OF SUCH DAMAGE. */ /* This module contains some fixed tables that are used by more than one of the -PCRE code modules. The tables are also #included by the pcre2test program, +PCRE2 code modules. The tables are also #included by the pcre2test program, which uses macros to change their names from _pcre2_xxx to xxxx, thereby avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is defined. */ #ifndef PCRE2_PCRE2TEST /* We're compiling the library */ -#define HAVE_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -138,9 +137,10 @@ const uint32_t PRIV(ucp_gentype)[] = { /* This table encodes the rules for finding the end of an extended grapheme cluster. Every code point has a grapheme break property which is one of the -ucp_gbXX values defined in pcre2_ucp.h. The 2-dimensional table is indexed by -the properties of two adjacent code points. The left property selects a word -from the table, and the right property selects a bit from that word like this: +ucp_gbXX values defined in pcre2_ucp.h. These changed between Unicode versions +10 and 11. The 2-dimensional table is indexed by the properties of two adjacent +code points. The left property selects a word from the table, and the right +property selects a bit from that word like this: PRIV(ucp_gbtable)[left-property] & (1 << right-property) @@ -149,7 +149,7 @@ two code points. The breaking rules are as follows: 1. Break at the start and end of text (pretty obviously). -2. Do not break between a CR and LF; otherwise, break before and after +2. Do not break between a CR and LF; otherwise, break before and after controls. 3. Do not break Hangul syllable sequences, the rules for which are: @@ -158,44 +158,54 @@ two code points. The breaking rules are as follows: LV or V may be followed by V or T LVT or T may be followed by T -4. Do not break before extending characters. +4. Do not break before extending characters or zero-width-joiner (ZWJ). -The next two rules are only for extended grapheme clusters (but that's what we +The following rules are only for extended grapheme clusters (but that's what we are implementing). 5. Do not break before SpacingMarks. 6. Do not break after Prepend characters. -7. Otherwise, break everywhere. +7. Do not break within emoji modifier sequences or emoji zwj sequences. That + is, do not break between characters with the Extended_Pictographic property. + Extend and ZWJ characters are allowed between the characters; this cannot be + represented in this table, the code has to deal with it. + +8. Do not break within emoji flag sequences. That is, do not break between + regional indicator (RI) symbols if there are an odd number of RI characters + before the break point. This table encodes "join RI characters"; the code + has to deal with checking for previous adjoining RIs. + +9. Otherwise, break everywhere. */ +#define ESZ (1< 0; p++) { - register uint32_t ab, d; + uint32_t ab, d; c = *p; length--; @@ -143,20 +142,20 @@ for (p = string; length > 0; p++) if (c < 0xc0) /* Isolated 10xx xxxx byte */ { - *erroroffset = (int)(p - string); + *erroroffset = (PCRE2_SIZE)(p - string); return PCRE2_ERROR_UTF8_ERR20; } if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */ { - *erroroffset = (int)(p - string); + *erroroffset = (PCRE2_SIZE)(p - string); return PCRE2_ERROR_UTF8_ERR21; } ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes (1-5) */ if (length < ab) /* Missing bytes */ { - *erroroffset = (int)(p - string); + *erroroffset = (PCRE2_SIZE)(p - string); switch(ab - length) { case 1: return PCRE2_ERROR_UTF8_ERR1; diff --git a/ProcessHacker/pcre/pcre2_xclass.c b/ProcessHacker/pcre/pcre2_xclass.c index ccff77056946..407d3f5b8796 100644 --- a/ProcessHacker/pcre/pcre2_xclass.c +++ b/ProcessHacker/pcre/pcre2_xclass.c @@ -42,7 +42,7 @@ POSSIBILITY OF SUCH DAMAGE. class. It is used by pcre2_auto_possessify() and by both pcre2_match() and pcre2_def_match(). */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ProcessHacker/pcre/pcre2demo.c b/ProcessHacker/pcre/pcre2demo.c new file mode 100644 index 000000000000..5d9b3217bb99 --- /dev/null +++ b/ProcessHacker/pcre/pcre2demo.c @@ -0,0 +1,488 @@ +/************************************************* +* PCRE2 DEMONSTRATION PROGRAM * +*************************************************/ + +/* This is a demonstration program to illustrate a straightforward way of +using the PCRE2 regular expression library from a C program. See the +pcre2sample documentation for a short discussion ("man pcre2sample" if you have +the PCRE2 man pages installed). PCRE2 is a revised API for the library, and is +incompatible with the original PCRE API. + +There are actually three libraries, each supporting a different code unit +width. This demonstration program uses the 8-bit library. The default is to +process each code unit as a separate character, but if the pattern begins with +"(*UTF)", both it and the subject are treated as UTF-8 strings, where +characters may occupy multiple code units. + +In Unix-like environments, if PCRE2 is installed in your standard system +libraries, you should be able to compile this program using this command: + +cc -Wall pcre2demo.c -lpcre2-8 -o pcre2demo + +If PCRE2 is not installed in a standard place, it is likely to be installed +with support for the pkg-config mechanism. If you have pkg-config, you can +compile this program using this command: + +cc -Wall pcre2demo.c `pkg-config --cflags --libs libpcre2-8` -o pcre2demo + +If you do not have pkg-config, you may have to use something like this: + +cc -Wall pcre2demo.c -I/usr/local/include -L/usr/local/lib \ + -R/usr/local/lib -lpcre2-8 -o pcre2demo + +Replace "/usr/local/include" and "/usr/local/lib" with wherever the include and +library files for PCRE2 are installed on your system. Only some operating +systems (Solaris is one) use the -R option. + +Building under Windows: + +If you want to statically link this program against a non-dll .a file, you must +define PCRE2_STATIC before including pcre2.h, so in this environment, uncomment +the following line. */ + +/* #define PCRE2_STATIC */ + +/* The PCRE2_CODE_UNIT_WIDTH macro must be defined before including pcre2.h. +For a program that uses only one code unit width, setting it to 8, 16, or 32 +makes it possible to use generic function names such as pcre2_compile(). Note +that just changing 8 to 16 (for example) is not sufficient to convert this +program to process 16-bit characters. Even in a fully 16-bit environment, where +string-handling functions such as strcmp() and printf() work with 16-bit +characters, the code for handling the table of named substrings will still need +to be modified. */ + +#define PCRE2_CODE_UNIT_WIDTH 8 + +#include +#include +#include + + +/************************************************************************** +* Here is the program. The API includes the concept of "contexts" for * +* setting up unusual interface requirements for compiling and matching, * +* such as custom memory managers and non-standard newline definitions. * +* This program does not do any of this, so it makes no use of contexts, * +* always passing NULL where a context could be given. * +**************************************************************************/ + +int main(int argc, char **argv) +{ +pcre2_code *re; +PCRE2_SPTR pattern; /* PCRE2_SPTR is a pointer to unsigned code units of */ +PCRE2_SPTR subject; /* the appropriate width (in this case, 8 bits). */ +PCRE2_SPTR name_table; + +int crlf_is_newline; +int errornumber; +int find_all; +int i; +int rc; +int utf8; + +uint32_t option_bits; +uint32_t namecount; +uint32_t name_entry_size; +uint32_t newline; + +PCRE2_SIZE erroroffset; +PCRE2_SIZE *ovector; + +size_t subject_length; +pcre2_match_data *match_data; + + + +/************************************************************************** +* First, sort out the command line. There is only one possible option at * +* the moment, "-g" to request repeated matching to find all occurrences, * +* like Perl's /g option. We set the variable find_all to a non-zero value * +* if the -g option is present. * +**************************************************************************/ + +find_all = 0; +for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "-g") == 0) find_all = 1; + else if (argv[i][0] == '-') + { + printf("Unrecognised option %s\n", argv[i]); + return 1; + } + else break; + } + +/* After the options, we require exactly two arguments, which are the pattern, +and the subject string. */ + +if (argc - i != 2) + { + printf("Exactly two arguments required: a regex and a subject string\n"); + return 1; + } + +/* As pattern and subject are char arguments, they can be straightforwardly +cast to PCRE2_SPTR as we are working in 8-bit code units. */ + +pattern = (PCRE2_SPTR)argv[i]; +subject = (PCRE2_SPTR)argv[i+1]; +subject_length = strlen((char *)subject); + + +/************************************************************************* +* Now we are going to compile the regular expression pattern, and handle * +* any errors that are detected. * +*************************************************************************/ + +re = pcre2_compile( + pattern, /* the pattern */ + PCRE2_ZERO_TERMINATED, /* indicates pattern is zero-terminated */ + 0, /* default options */ + &errornumber, /* for error number */ + &erroroffset, /* for error offset */ + NULL); /* use default compile context */ + +/* Compilation failed: print the error message and exit. */ + +if (re == NULL) + { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(errornumber, buffer, sizeof(buffer)); + printf("PCRE2 compilation failed at offset %d: %s\n", (int)erroroffset, + buffer); + return 1; + } + + +/************************************************************************* +* If the compilation succeeded, we call PCRE again, in order to do a * +* pattern match against the subject string. This does just ONE match. If * +* further matching is needed, it will be done below. Before running the * +* match we must set up a match_data block for holding the result. * +*************************************************************************/ + +/* Using this function ensures that the block is exactly the right size for +the number of capturing parentheses in the pattern. */ + +match_data = pcre2_match_data_create_from_pattern(re, NULL); + +rc = pcre2_match( + re, /* the compiled pattern */ + subject, /* the subject string */ + subject_length, /* the length of the subject */ + 0, /* start at offset 0 in the subject */ + 0, /* default options */ + match_data, /* block for storing the result */ + NULL); /* use default match context */ + +/* Matching failed: handle error cases */ + +if (rc < 0) + { + switch(rc) + { + case PCRE2_ERROR_NOMATCH: printf("No match\n"); break; + /* + Handle other special cases if you like + */ + default: printf("Matching error %d\n", rc); break; + } + pcre2_match_data_free(match_data); /* Release memory used for the match */ + pcre2_code_free(re); /* data and the compiled pattern. */ + return 1; + } + +/* Match succeded. Get a pointer to the output vector, where string offsets are +stored. */ + +ovector = pcre2_get_ovector_pointer(match_data); +printf("Match succeeded at offset %d\n", (int)ovector[0]); + + +/************************************************************************* +* We have found the first match within the subject string. If the output * +* vector wasn't big enough, say so. Then output any substrings that were * +* captured. * +*************************************************************************/ + +/* The output vector wasn't big enough. This should not happen, because we used +pcre2_match_data_create_from_pattern() above. */ + +if (rc == 0) + printf("ovector was not big enough for all the captured substrings\n"); + +/* We must guard against patterns such as /(?=.\K)/ that use \K in an assertion +to set the start of a match later than its end. In this demonstration program, +we just detect this case and give up. */ + +if (ovector[0] > ovector[1]) + { + printf("\\K was used in an assertion to set the match start after its end.\n" + "From end to start the match was: %.*s\n", (int)(ovector[0] - ovector[1]), + (char *)(subject + ovector[1])); + printf("Run abandoned\n"); + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return 1; + } + +/* Show substrings stored in the output vector by number. Obviously, in a real +application you might want to do things other than print them. */ + +for (i = 0; i < rc; i++) + { + PCRE2_SPTR substring_start = subject + ovector[2*i]; + size_t substring_length = ovector[2*i+1] - ovector[2*i]; + printf("%2d: %.*s\n", i, (int)substring_length, (char *)substring_start); + } + + +/************************************************************************** +* That concludes the basic part of this demonstration program. We have * +* compiled a pattern, and performed a single match. The code that follows * +* shows first how to access named substrings, and then how to code for * +* repeated matches on the same subject. * +**************************************************************************/ + +/* See if there are any named substrings, and if so, show them by name. First +we have to extract the count of named parentheses from the pattern. */ + +(void)pcre2_pattern_info( + re, /* the compiled pattern */ + PCRE2_INFO_NAMECOUNT, /* get the number of named substrings */ + &namecount); /* where to put the answer */ + +if (namecount == 0) printf("No named substrings\n"); else + { + PCRE2_SPTR tabptr; + printf("Named substrings\n"); + + /* Before we can access the substrings, we must extract the table for + translating names to numbers, and the size of each entry in the table. */ + + (void)pcre2_pattern_info( + re, /* the compiled pattern */ + PCRE2_INFO_NAMETABLE, /* address of the table */ + &name_table); /* where to put the answer */ + + (void)pcre2_pattern_info( + re, /* the compiled pattern */ + PCRE2_INFO_NAMEENTRYSIZE, /* size of each entry in the table */ + &name_entry_size); /* where to put the answer */ + + /* Now we can scan the table and, for each entry, print the number, the name, + and the substring itself. In the 8-bit library the number is held in two + bytes, most significant first. */ + + tabptr = name_table; + for (i = 0; i < namecount; i++) + { + int n = (tabptr[0] << 8) | tabptr[1]; + printf("(%d) %*s: %.*s\n", n, name_entry_size - 3, tabptr + 2, + (int)(ovector[2*n+1] - ovector[2*n]), subject + ovector[2*n]); + tabptr += name_entry_size; + } + } + + +/************************************************************************* +* If the "-g" option was given on the command line, we want to continue * +* to search for additional matches in the subject string, in a similar * +* way to the /g option in Perl. This turns out to be trickier than you * +* might think because of the possibility of matching an empty string. * +* What happens is as follows: * +* * +* If the previous match was NOT for an empty string, we can just start * +* the next match at the end of the previous one. * +* * +* If the previous match WAS for an empty string, we can't do that, as it * +* would lead to an infinite loop. Instead, a call of pcre2_match() is * +* made with the PCRE2_NOTEMPTY_ATSTART and PCRE2_ANCHORED flags set. The * +* first of these tells PCRE2 that an empty string at the start of the * +* subject is not a valid match; other possibilities must be tried. The * +* second flag restricts PCRE2 to one match attempt at the initial string * +* position. If this match succeeds, an alternative to the empty string * +* match has been found, and we can print it and proceed round the loop, * +* advancing by the length of whatever was found. If this match does not * +* succeed, we still stay in the loop, advancing by just one character. * +* In UTF-8 mode, which can be set by (*UTF) in the pattern, this may be * +* more than one byte. * +* * +* However, there is a complication concerned with newlines. When the * +* newline convention is such that CRLF is a valid newline, we must * +* advance by two characters rather than one. The newline convention can * +* be set in the regex by (*CR), etc.; if not, we must find the default. * +*************************************************************************/ + +if (!find_all) /* Check for -g */ + { + pcre2_match_data_free(match_data); /* Release the memory that was used */ + pcre2_code_free(re); /* for the match data and the pattern. */ + return 0; /* Exit the program. */ + } + +/* Before running the loop, check for UTF-8 and whether CRLF is a valid newline +sequence. First, find the options with which the regex was compiled and extract +the UTF state. */ + +(void)pcre2_pattern_info(re, PCRE2_INFO_ALLOPTIONS, &option_bits); +utf8 = (option_bits & PCRE2_UTF) != 0; + +/* Now find the newline convention and see whether CRLF is a valid newline +sequence. */ + +(void)pcre2_pattern_info(re, PCRE2_INFO_NEWLINE, &newline); +crlf_is_newline = newline == PCRE2_NEWLINE_ANY || + newline == PCRE2_NEWLINE_CRLF || + newline == PCRE2_NEWLINE_ANYCRLF; + +/* Loop for second and subsequent matches */ + +for (;;) + { + uint32_t options = 0; /* Normally no options */ + PCRE2_SIZE start_offset = ovector[1]; /* Start at end of previous match */ + + /* If the previous match was for an empty string, we are finished if we are + at the end of the subject. Otherwise, arrange to run another match at the + same point to see if a non-empty match can be found. */ + + if (ovector[0] == ovector[1]) + { + if (ovector[0] == subject_length) break; + options = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + } + + /* If the previous match was not an empty string, there is one tricky case to + consider. If a pattern contains \K within a lookbehind assertion at the + start, the end of the matched string can be at the offset where the match + started. Without special action, this leads to a loop that keeps on matching + the same substring. We must detect this case and arrange to move the start on + by one character. The pcre2_get_startchar() function returns the starting + offset that was passed to pcre2_match(). */ + + else + { + PCRE2_SIZE startchar = pcre2_get_startchar(match_data); + if (start_offset <= startchar) + { + if (startchar >= subject_length) break; /* Reached end of subject. */ + start_offset = startchar + 1; /* Advance by one character. */ + if (utf8) /* If UTF-8, it may be more */ + { /* than one code unit. */ + for (; start_offset < subject_length; start_offset++) + if ((subject[start_offset] & 0xc0) != 0x80) break; + } + } + } + + /* Run the next matching operation */ + + rc = pcre2_match( + re, /* the compiled pattern */ + subject, /* the subject string */ + subject_length, /* the length of the subject */ + start_offset, /* starting offset in the subject */ + options, /* options */ + match_data, /* block for storing the result */ + NULL); /* use default match context */ + + /* This time, a result of NOMATCH isn't an error. If the value in "options" + is zero, it just means we have found all possible matches, so the loop ends. + Otherwise, it means we have failed to find a non-empty-string match at a + point where there was a previous empty-string match. In this case, we do what + Perl does: advance the matching position by one character, and continue. We + do this by setting the "end of previous match" offset, because that is picked + up at the top of the loop as the point at which to start again. + + There are two complications: (a) When CRLF is a valid newline sequence, and + the current position is just before it, advance by an extra byte. (b) + Otherwise we must ensure that we skip an entire UTF character if we are in + UTF mode. */ + + if (rc == PCRE2_ERROR_NOMATCH) + { + if (options == 0) break; /* All matches found */ + ovector[1] = start_offset + 1; /* Advance one code unit */ + if (crlf_is_newline && /* If CRLF is a newline & */ + start_offset < subject_length - 1 && /* we are at CRLF, */ + subject[start_offset] == '\r' && + subject[start_offset + 1] == '\n') + ovector[1] += 1; /* Advance by one more. */ + else if (utf8) /* Otherwise, ensure we */ + { /* advance a whole UTF-8 */ + while (ovector[1] < subject_length) /* character. */ + { + if ((subject[ovector[1]] & 0xc0) != 0x80) break; + ovector[1] += 1; + } + } + continue; /* Go round the loop again */ + } + + /* Other matching errors are not recoverable. */ + + if (rc < 0) + { + printf("Matching error %d\n", rc); + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return 1; + } + + /* Match succeded */ + + printf("\nMatch succeeded again at offset %d\n", (int)ovector[0]); + + /* The match succeeded, but the output vector wasn't big enough. This + should not happen. */ + + if (rc == 0) + printf("ovector was not big enough for all the captured substrings\n"); + + /* We must guard against patterns such as /(?=.\K)/ that use \K in an + assertion to set the start of a match later than its end. In this + demonstration program, we just detect this case and give up. */ + + if (ovector[0] > ovector[1]) + { + printf("\\K was used in an assertion to set the match start after its end.\n" + "From end to start the match was: %.*s\n", (int)(ovector[0] - ovector[1]), + (char *)(subject + ovector[1])); + printf("Run abandoned\n"); + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return 1; + } + + /* As before, show substrings stored in the output vector by number, and then + also any named substrings. */ + + for (i = 0; i < rc; i++) + { + PCRE2_SPTR substring_start = subject + ovector[2*i]; + size_t substring_length = ovector[2*i+1] - ovector[2*i]; + printf("%2d: %.*s\n", i, (int)substring_length, (char *)substring_start); + } + + if (namecount == 0) printf("No named substrings\n"); else + { + PCRE2_SPTR tabptr = name_table; + printf("Named substrings\n"); + for (i = 0; i < namecount; i++) + { + int n = (tabptr[0] << 8) | tabptr[1]; + printf("(%d) %*s: %.*s\n", n, name_entry_size - 3, tabptr + 2, + (int)(ovector[2*n+1] - ovector[2*n]), subject + ovector[2*n]); + tabptr += name_entry_size; + } + } + } /* End of loop to find second and subsequent matches */ + +printf("\n"); +pcre2_match_data_free(match_data); +pcre2_code_free(re); +return 0; +} + +/* End of pcre2demo.c */ diff --git a/ProcessHacker/pcre/pcre2grep.c b/ProcessHacker/pcre/pcre2grep.c new file mode 100644 index 000000000000..d5f34c81fb29 --- /dev/null +++ b/ProcessHacker/pcre/pcre2grep.c @@ -0,0 +1,4340 @@ +/************************************************* +* pcre2grep program * +*************************************************/ + +/* This is a grep program that uses the 8-bit PCRE regular expression library +via the PCRE2 updated API to do its pattern matching. On Unix-like, Windows, +and native z/OS systems it can recurse into directories, and in z/OS it can +handle PDS files. + +Note that for native z/OS, in addition to defining the NATIVE_ZOS macro, an +additional header is required. That header is not included in the main PCRE2 +distribution because other apparatus is needed to compile pcre2grep for z/OS. +The header can be found in the special z/OS distribution, which is available +from www.zaconsultants.net or from www.cbttape.org. + + Copyright (c) 1997-2018 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if (defined _WIN32 || (defined HAVE_WINDOWS_H && HAVE_WINDOWS_H)) \ + && !defined WIN32 && !defined(__CYGWIN__) +#define WIN32 +#endif + +/* Some cmake's define it still */ +#if defined(__CYGWIN__) && defined(WIN32) +#undef WIN32 +#endif + +#ifdef WIN32 +#include /* For _setmode() */ +#include /* For _O_BINARY */ +#endif + +#ifdef SUPPORT_PCRE2GREP_CALLOUT +#ifdef WIN32 +#include +#else +#include +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef SUPPORT_LIBZ +#include +#endif + +#ifdef SUPPORT_LIBBZ2 +#include +#endif + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" + +/* Older versions of MSVC lack snprintf(). This define allows for +warning/error-free compilation and testing with MSVC compilers back to at least +MSVC 10/2010. Except for VC6 (which is missing some fundamentals and fails). */ + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif + +#define FALSE 0 +#define TRUE 1 + +typedef int BOOL; + +#define OFFSET_SIZE 33 + +#if BUFSIZ > 8192 +#define MAXPATLEN BUFSIZ +#else +#define MAXPATLEN 8192 +#endif + +#define FNBUFSIZ 2048 +#define ERRBUFSIZ 256 + +/* Values for the "filenames" variable, which specifies options for file name +output. The order is important; it is assumed that a file name is wanted for +all values greater than FN_DEFAULT. */ + +enum { FN_NONE, FN_DEFAULT, FN_MATCH_ONLY, FN_NOMATCH_ONLY, FN_FORCE }; + +/* File reading styles */ + +enum { FR_PLAIN, FR_LIBZ, FR_LIBBZ2 }; + +/* Actions for the -d and -D options */ + +enum { dee_READ, dee_SKIP, dee_RECURSE }; +enum { DEE_READ, DEE_SKIP }; + +/* Actions for special processing options (flag bits) */ + +#define PO_WORD_MATCH 0x0001 +#define PO_LINE_MATCH 0x0002 +#define PO_FIXED_STRINGS 0x0004 + +/* Binary file options */ + +enum { BIN_BINARY, BIN_NOMATCH, BIN_TEXT }; + +/* In newer versions of gcc, with FORTIFY_SOURCE set (the default in some +environments), a warning is issued if the value of fwrite() is ignored. +Unfortunately, casting to (void) does not suppress the warning. To get round +this, we use a macro that compiles a fudge. Oddly, this does not also seem to +apply to fprintf(). */ + +#define FWRITE_IGNORE(a,b,c,d) if (fwrite(a,b,c,d)) {} + +/* Under Windows, we have to set stdout to be binary, so that it does not +convert \r\n at the ends of output lines to \r\r\n. However, that means that +any messages written to stdout must have \r\n as their line terminator. This is +handled by using STDOUT_NL as the newline string. We also use a normal double +quote for the example, as single quotes aren't usually available. */ + +#ifdef WIN32 +#define STDOUT_NL "\r\n" +#define QUOT "\"" +#else +#define STDOUT_NL "\n" +#define QUOT "'" +#endif + + + +/************************************************* +* Global variables * +*************************************************/ + +/* Jeffrey Friedl has some debugging requirements that are not part of the +regular code. */ + +#ifdef JFRIEDL_DEBUG +static int S_arg = -1; +static unsigned int jfriedl_XR = 0; /* repeat regex attempt this many times */ +static unsigned int jfriedl_XT = 0; /* replicate text this many times */ +static const char *jfriedl_prefix = ""; +static const char *jfriedl_postfix = ""; +#endif + +static const char *colour_string = "1;31"; +static const char *colour_option = NULL; +static const char *dee_option = NULL; +static const char *DEE_option = NULL; +static const char *locale = NULL; +static const char *newline_arg = NULL; +static const char *om_separator = NULL; +static const char *stdin_name = "(standard input)"; +static const char *output_text = NULL; + +static char *main_buffer = NULL; + +static int after_context = 0; +static int before_context = 0; +static int binary_files = BIN_BINARY; +static int both_context = 0; +static int bufthird = PCRE2GREP_BUFSIZE; +static int max_bufthird = PCRE2GREP_MAX_BUFSIZE; +static int bufsize = 3*PCRE2GREP_BUFSIZE; +static int endlinetype; + +static unsigned long int total_count = 0; +static unsigned long int counts_printed = 0; + +#ifdef WIN32 +static int dee_action = dee_SKIP; +#else +static int dee_action = dee_READ; +#endif + +static int DEE_action = DEE_READ; +static int error_count = 0; +static int filenames = FN_DEFAULT; + +#ifdef SUPPORT_PCRE2GREP_JIT +static BOOL use_jit = TRUE; +#else +static BOOL use_jit = FALSE; +#endif + +static const uint8_t *character_tables = NULL; + +static uint32_t pcre2_options = 0; +static uint32_t extra_options = 0; +static PCRE2_SIZE heap_limit = PCRE2_UNSET; +static uint32_t match_limit = 0; +static uint32_t depth_limit = 0; + +static pcre2_compile_context *compile_context; +static pcre2_match_context *match_context; +static pcre2_match_data *match_data; +static PCRE2_SIZE *offsets; + +static BOOL count_only = FALSE; +static BOOL do_colour = FALSE; +#ifdef WIN32 +static BOOL do_ansi = FALSE; +#endif +static BOOL file_offsets = FALSE; +static BOOL hyphenpending = FALSE; +static BOOL invert = FALSE; +static BOOL line_buffered = FALSE; +static BOOL line_offsets = FALSE; +static BOOL multiline = FALSE; +static BOOL number = FALSE; +static BOOL omit_zero_count = FALSE; +static BOOL resource_error = FALSE; +static BOOL quiet = FALSE; +static BOOL show_total_count = FALSE; +static BOOL silent = FALSE; +static BOOL utf = FALSE; + +/* Structure for list of --only-matching capturing numbers. */ + +typedef struct omstr { + struct omstr *next; + int groupnum; +} omstr; + +static omstr *only_matching = NULL; +static omstr *only_matching_last = NULL; +static int only_matching_count; + +/* Structure for holding the two variables that describe a number chain. */ + +typedef struct omdatastr { + omstr **anchor; + omstr **lastptr; +} omdatastr; + +static omdatastr only_matching_data = { &only_matching, &only_matching_last }; + +/* Structure for list of file names (for -f and --{in,ex}clude-from) */ + +typedef struct fnstr { + struct fnstr *next; + char *name; +} fnstr; + +static fnstr *exclude_from = NULL; +static fnstr *exclude_from_last = NULL; +static fnstr *include_from = NULL; +static fnstr *include_from_last = NULL; + +static fnstr *file_lists = NULL; +static fnstr *file_lists_last = NULL; +static fnstr *pattern_files = NULL; +static fnstr *pattern_files_last = NULL; + +/* Structure for holding the two variables that describe a file name chain. */ + +typedef struct fndatastr { + fnstr **anchor; + fnstr **lastptr; +} fndatastr; + +static fndatastr exclude_from_data = { &exclude_from, &exclude_from_last }; +static fndatastr include_from_data = { &include_from, &include_from_last }; +static fndatastr file_lists_data = { &file_lists, &file_lists_last }; +static fndatastr pattern_files_data = { &pattern_files, &pattern_files_last }; + +/* Structure for pattern and its compiled form; used for matching patterns and +also for include/exclude patterns. */ + +typedef struct patstr { + struct patstr *next; + char *string; + PCRE2_SIZE length; + pcre2_code *compiled; +} patstr; + +static patstr *patterns = NULL; +static patstr *patterns_last = NULL; +static patstr *include_patterns = NULL; +static patstr *include_patterns_last = NULL; +static patstr *exclude_patterns = NULL; +static patstr *exclude_patterns_last = NULL; +static patstr *include_dir_patterns = NULL; +static patstr *include_dir_patterns_last = NULL; +static patstr *exclude_dir_patterns = NULL; +static patstr *exclude_dir_patterns_last = NULL; + +/* Structure holding the two variables that describe a pattern chain. A pointer +to such structures is used for each appropriate option. */ + +typedef struct patdatastr { + patstr **anchor; + patstr **lastptr; +} patdatastr; + +static patdatastr match_patdata = { &patterns, &patterns_last }; +static patdatastr include_patdata = { &include_patterns, &include_patterns_last }; +static patdatastr exclude_patdata = { &exclude_patterns, &exclude_patterns_last }; +static patdatastr include_dir_patdata = { &include_dir_patterns, &include_dir_patterns_last }; +static patdatastr exclude_dir_patdata = { &exclude_dir_patterns, &exclude_dir_patterns_last }; + +static patstr **incexlist[4] = { &include_patterns, &exclude_patterns, + &include_dir_patterns, &exclude_dir_patterns }; + +static const char *incexname[4] = { "--include", "--exclude", + "--include-dir", "--exclude-dir" }; + +/* Structure for options and list of them */ + +enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_U32NUMBER, OP_SIZE, + OP_OP_NUMBER, OP_OP_NUMBERS, OP_PATLIST, OP_FILELIST, OP_BINFILES }; + +typedef struct option_item { + int type; + int one_char; + void *dataptr; + const char *long_name; + const char *help_text; +} option_item; + +/* Options without a single-letter equivalent get a negative value. This can be +used to identify them. */ + +#define N_COLOUR (-1) +#define N_EXCLUDE (-2) +#define N_EXCLUDE_DIR (-3) +#define N_HELP (-4) +#define N_INCLUDE (-5) +#define N_INCLUDE_DIR (-6) +#define N_LABEL (-7) +#define N_LOCALE (-8) +#define N_NULL (-9) +#define N_LOFFSETS (-10) +#define N_FOFFSETS (-11) +#define N_LBUFFER (-12) +#define N_H_LIMIT (-13) +#define N_M_LIMIT (-14) +#define N_M_LIMIT_DEP (-15) +#define N_BUFSIZE (-16) +#define N_NOJIT (-17) +#define N_FILE_LIST (-18) +#define N_BINARY_FILES (-19) +#define N_EXCLUDE_FROM (-20) +#define N_INCLUDE_FROM (-21) +#define N_OM_SEPARATOR (-22) +#define N_MAX_BUFSIZE (-23) + +static option_item optionlist[] = { + { OP_NODATA, N_NULL, NULL, "", "terminate options" }, + { OP_NODATA, N_HELP, NULL, "help", "display this help and exit" }, + { OP_NUMBER, 'A', &after_context, "after-context=number", "set number of following context lines" }, + { OP_NODATA, 'a', NULL, "text", "treat binary files as text" }, + { OP_NUMBER, 'B', &before_context, "before-context=number", "set number of prior context lines" }, + { OP_BINFILES, N_BINARY_FILES, NULL, "binary-files=word", "set treatment of binary files" }, + { OP_NUMBER, N_BUFSIZE,&bufthird, "buffer-size=number", "set processing buffer starting size" }, + { OP_NUMBER, N_MAX_BUFSIZE,&max_bufthird, "max-buffer-size=number", "set processing buffer maximum size" }, + { OP_OP_STRING, N_COLOUR, &colour_option, "color=option", "matched text color option" }, + { OP_OP_STRING, N_COLOUR, &colour_option, "colour=option", "matched text colour option" }, + { OP_NUMBER, 'C', &both_context, "context=number", "set number of context lines, before & after" }, + { OP_NODATA, 'c', NULL, "count", "print only a count of matching lines per FILE" }, + { OP_STRING, 'D', &DEE_option, "devices=action","how to handle devices, FIFOs, and sockets" }, + { OP_STRING, 'd', &dee_option, "directories=action", "how to handle directories" }, + { OP_PATLIST, 'e', &match_patdata, "regex(p)=pattern", "specify pattern (may be used more than once)" }, + { OP_NODATA, 'F', NULL, "fixed-strings", "patterns are sets of newline-separated strings" }, + { OP_FILELIST, 'f', &pattern_files_data, "file=path", "read patterns from file" }, + { OP_FILELIST, N_FILE_LIST, &file_lists_data, "file-list=path","read files to search from file" }, + { OP_NODATA, N_FOFFSETS, NULL, "file-offsets", "output file offsets, not text" }, + { OP_NODATA, 'H', NULL, "with-filename", "force the prefixing filename on output" }, + { OP_NODATA, 'h', NULL, "no-filename", "suppress the prefixing filename on output" }, + { OP_NODATA, 'I', NULL, "", "treat binary files as not matching (ignore)" }, + { OP_NODATA, 'i', NULL, "ignore-case", "ignore case distinctions" }, + { OP_NODATA, 'l', NULL, "files-with-matches", "print only FILE names containing matches" }, + { OP_NODATA, 'L', NULL, "files-without-match","print only FILE names not containing matches" }, + { OP_STRING, N_LABEL, &stdin_name, "label=name", "set name for standard input" }, + { OP_NODATA, N_LBUFFER, NULL, "line-buffered", "use line buffering" }, + { OP_NODATA, N_LOFFSETS, NULL, "line-offsets", "output line numbers and offsets, not text" }, + { OP_STRING, N_LOCALE, &locale, "locale=locale", "use the named locale" }, + { OP_SIZE, N_H_LIMIT, &heap_limit, "heap-limit=number", "set PCRE2 heap limit option (kibibytes)" }, + { OP_U32NUMBER, N_M_LIMIT, &match_limit, "match-limit=number", "set PCRE2 match limit option" }, + { OP_U32NUMBER, N_M_LIMIT_DEP, &depth_limit, "depth-limit=number", "set PCRE2 depth limit option" }, + { OP_U32NUMBER, N_M_LIMIT_DEP, &depth_limit, "recursion-limit=number", "obsolete synonym for depth-limit" }, + { OP_NODATA, 'M', NULL, "multiline", "run in multiline mode" }, + { OP_STRING, 'N', &newline_arg, "newline=type", "set newline type (CR, LF, CRLF, ANYCRLF, ANY, or NUL)" }, + { OP_NODATA, 'n', NULL, "line-number", "print line number with output lines" }, +#ifdef SUPPORT_PCRE2GREP_JIT + { OP_NODATA, N_NOJIT, NULL, "no-jit", "do not use just-in-time compiler optimization" }, +#else + { OP_NODATA, N_NOJIT, NULL, "no-jit", "ignored: this pcre2grep does not support JIT" }, +#endif + { OP_STRING, 'O', &output_text, "output=text", "show only this text (possibly expanded)" }, + { OP_OP_NUMBERS, 'o', &only_matching_data, "only-matching=n", "show only the part of the line that matched" }, + { OP_STRING, N_OM_SEPARATOR, &om_separator, "om-separator=text", "set separator for multiple -o output" }, + { OP_NODATA, 'q', NULL, "quiet", "suppress output, just set return code" }, + { OP_NODATA, 'r', NULL, "recursive", "recursively scan sub-directories" }, + { OP_PATLIST, N_EXCLUDE,&exclude_patdata, "exclude=pattern","exclude matching files when recursing" }, + { OP_PATLIST, N_INCLUDE,&include_patdata, "include=pattern","include matching files when recursing" }, + { OP_PATLIST, N_EXCLUDE_DIR,&exclude_dir_patdata, "exclude-dir=pattern","exclude matching directories when recursing" }, + { OP_PATLIST, N_INCLUDE_DIR,&include_dir_patdata, "include-dir=pattern","include matching directories when recursing" }, + { OP_FILELIST, N_EXCLUDE_FROM,&exclude_from_data, "exclude-from=path", "read exclude list from file" }, + { OP_FILELIST, N_INCLUDE_FROM,&include_from_data, "include-from=path", "read include list from file" }, +#ifdef JFRIEDL_DEBUG + { OP_OP_NUMBER, 'S', &S_arg, "jeffS", "replace matched (sub)string with X" }, +#endif + { OP_NODATA, 's', NULL, "no-messages", "suppress error messages" }, + { OP_NODATA, 't', NULL, "total-count", "print total count of matching lines" }, + { OP_NODATA, 'u', NULL, "utf", "use UTF mode" }, + { OP_NODATA, 'V', NULL, "version", "print version information and exit" }, + { OP_NODATA, 'v', NULL, "invert-match", "select non-matching lines" }, + { OP_NODATA, 'w', NULL, "word-regex(p)", "force patterns to match only as words" }, + { OP_NODATA, 'x', NULL, "line-regex(p)", "force patterns to match only whole lines" }, + { OP_NODATA, 0, NULL, NULL, NULL } +}; + +/* Table of names for newline types. Must be kept in step with the definitions +of PCRE2_NEWLINE_xx in pcre2.h. */ + +static const char *newlines[] = { + "DEFAULT", "CR", "LF", "CRLF", "ANY", "ANYCRLF", "NUL" }; + +/* UTF-8 tables - used only when the newline setting is "any". */ + +const int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +const char utf8_table4[] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; + + +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +/************************************************* +* Emulated memmove() for systems without it * +*************************************************/ + +/* This function can make use of bcopy() if it is available. Otherwise do it by +steam, as there are some non-Unix environments that lack both memmove() and +bcopy(). */ + +static void * +emulated_memmove(void *d, const void *s, size_t n) +{ +#ifdef HAVE_BCOPY +bcopy(s, d, n); +return d; +#else +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +#endif /* not HAVE_BCOPY */ +} +#undef memmove +#define memmove(d,s,n) emulated_memmove(d,s,n) +#endif /* not VPCOMPAT && not HAVE_MEMMOVE */ + + +/************************************************* +* Case-independent string compare * +*************************************************/ + +static int +strcmpic(const char *str1, const char *str2) +{ +unsigned int c1, c2; +while (*str1 != '\0' || *str2 != '\0') + { + c1 = tolower(*str1++); + c2 = tolower(*str2++); + if (c1 != c2) return ((c1 > c2) << 1) - 1; + } +return 0; +} + + +/************************************************* +* Parse GREP_COLORS * +*************************************************/ + +/* Extract ms or mt from GREP_COLORS. + +Argument: the string, possibly NULL +Returns: the value of ms or mt, or NULL if neither present +*/ + +static char * +parse_grep_colors(const char *gc) +{ +static char seq[16]; +char *col; +uint32_t len; +if (gc == NULL) return NULL; +col = strstr(gc, "ms="); +if (col == NULL) col = strstr(gc, "mt="); +if (col == NULL) return NULL; +len = 0; +col += 3; +while (*col != ':' && *col != 0 && len < sizeof(seq)-1) + seq[len++] = *col++; +seq[len] = 0; +return seq; +} + + +/************************************************* +* Exit from the program * +*************************************************/ + +/* If there has been a resource error, give a suitable message. + +Argument: the return code +Returns: does not return +*/ + +static void +pcre2grep_exit(int rc) +{ +/* VMS does exit codes differently: both exit(1) and exit(0) return with a +status of 1, which is not helpful. To help with this problem, define a symbol +(akin to an environment variable) called "PCRE2GREP_RC" and put the exit code +therein. */ + +#ifdef __VMS +#include descrip +#include lib$routines + char val_buf[4]; + $DESCRIPTOR(sym_nam, "PCRE2GREP_RC"); + $DESCRIPTOR(sym_val, val_buf); + sprintf(val_buf, "%d", rc); + sym_val.dsc$w_length = strlen(val_buf); + lib$set_symbol(&sym_nam, &sym_val); +#endif + +if (resource_error) + { + fprintf(stderr, "pcre2grep: Error %d, %d, %d or %d means that a resource " + "limit was exceeded.\n", PCRE2_ERROR_JIT_STACKLIMIT, PCRE2_ERROR_MATCHLIMIT, + PCRE2_ERROR_DEPTHLIMIT, PCRE2_ERROR_HEAPLIMIT); + fprintf(stderr, "pcre2grep: Check your regex for nested unlimited loops.\n"); + } +exit(rc); +} + + +/************************************************* +* Add item to chain of patterns * +*************************************************/ + +/* Used to add an item onto a chain, or just return an unconnected item if the +"after" argument is NULL. + +Arguments: + s pattern string to add + patlen length of pattern + after if not NULL points to item to insert after + +Returns: new pattern block or NULL on error +*/ + +static patstr * +add_pattern(char *s, PCRE2_SIZE patlen, patstr *after) +{ +patstr *p = (patstr *)malloc(sizeof(patstr)); +if (p == NULL) + { + fprintf(stderr, "pcre2grep: malloc failed\n"); + pcre2grep_exit(2); + } +if (patlen > MAXPATLEN) + { + fprintf(stderr, "pcre2grep: pattern is too long (limit is %d bytes)\n", + MAXPATLEN); + free(p); + return NULL; + } +p->next = NULL; +p->string = s; +p->length = patlen; +p->compiled = NULL; + +if (after != NULL) + { + p->next = after->next; + after->next = p; + } +return p; +} + + +/************************************************* +* Free chain of patterns * +*************************************************/ + +/* Used for several chains of patterns. + +Argument: pointer to start of chain +Returns: nothing +*/ + +static void +free_pattern_chain(patstr *pc) +{ +while (pc != NULL) + { + patstr *p = pc; + pc = p->next; + if (p->compiled != NULL) pcre2_code_free(p->compiled); + free(p); + } +} + + +/************************************************* +* Free chain of file names * +*************************************************/ + +/* +Argument: pointer to start of chain +Returns: nothing +*/ + +static void +free_file_chain(fnstr *fn) +{ +while (fn != NULL) + { + fnstr *f = fn; + fn = f->next; + free(f); + } +} + + +/************************************************* +* OS-specific functions * +*************************************************/ + +/* These definitions are needed in all Windows environments, even those where +Unix-style directory scanning can be used (see below). */ + +#ifdef WIN32 + +#ifndef STRICT +# define STRICT +#endif +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#include + +#define iswild(name) (strpbrk(name, "*?") != NULL) + +/* Convert ANSI BGR format to RGB used by Windows */ +#define BGR_RGB(x) ((x & 1 ? 4 : 0) | (x & 2) | (x & 4 ? 1 : 0)) + +static HANDLE hstdout; +static CONSOLE_SCREEN_BUFFER_INFO csbi; +static WORD match_colour; + +static WORD +decode_ANSI_colour(const char *cs) +{ +WORD result = csbi.wAttributes; +while (*cs) + { + if (isdigit(*cs)) + { + int code = atoi(cs); + if (code == 1) result |= 0x08; + else if (code == 4) result |= 0x8000; + else if (code == 5) result |= 0x80; + else if (code >= 30 && code <= 37) result = (result & 0xF8) | BGR_RGB(code - 30); + else if (code == 39) result = (result & 0xF0) | (csbi.wAttributes & 0x0F); + else if (code >= 40 && code <= 47) result = (result & 0x8F) | (BGR_RGB(code - 40) << 4); + else if (code == 49) result = (result & 0x0F) | (csbi.wAttributes & 0xF0); + /* aixterm high intensity colour codes */ + else if (code >= 90 && code <= 97) result = (result & 0xF0) | BGR_RGB(code - 90) | 0x08; + else if (code >= 100 && code <= 107) result = (result & 0x0F) | (BGR_RGB(code - 100) << 4) | 0x80; + + while (isdigit(*cs)) cs++; + } + if (*cs) cs++; + } +return result; +} + + +static void +init_colour_output() +{ +if (do_colour) + { + hstdout = GetStdHandle(STD_OUTPUT_HANDLE); + /* This fails when redirected to con; try again if so. */ + if (!GetConsoleScreenBufferInfo(hstdout, &csbi) && !do_ansi) + { + HANDLE hcon = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + GetConsoleScreenBufferInfo(hcon, &csbi); + CloseHandle(hcon); + } + match_colour = decode_ANSI_colour(colour_string); + /* No valid colour found - turn off colouring */ + if (!match_colour) do_colour = FALSE; + } +} + +#endif /* WIN32 */ + + +/* The following sets of functions are defined so that they can be made system +specific. At present there are versions for Unix-style environments, Windows, +native z/OS, and "no support". */ + + +/************* Directory scanning Unix-style and z/OS ***********/ + +#if (defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H) || defined NATIVE_ZOS +#include +#include +#include + +#if defined NATIVE_ZOS +/************* Directory and PDS/E scanning for z/OS ***********/ +/************* z/OS looks mostly like Unix with USS ************/ +/* However, z/OS needs the #include statements in this header */ +#include "pcrzosfs.h" +/* That header is not included in the main PCRE distribution because + other apparatus is needed to compile pcre2grep for z/OS. The header + can be found in the special z/OS distribution, which is available + from www.zaconsultants.net or from www.cbttape.org. */ +#endif + +typedef DIR directory_type; +#define FILESEP '/' + +static int +isdirectory(char *filename) +{ +struct stat statbuf; +if (stat(filename, &statbuf) < 0) + return 0; /* In the expectation that opening as a file will fail */ +return S_ISDIR(statbuf.st_mode); +} + +static directory_type * +opendirectory(char *filename) +{ +return opendir(filename); +} + +static char * +readdirectory(directory_type *dir) +{ +for (;;) + { + struct dirent *dent = readdir(dir); + if (dent == NULL) return NULL; + if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) + return dent->d_name; + } +/* Control never reaches here */ +} + +static void +closedirectory(directory_type *dir) +{ +closedir(dir); +} + + +/************* Test for regular file, Unix-style **********/ + +static int +isregfile(char *filename) +{ +struct stat statbuf; +if (stat(filename, &statbuf) < 0) + return 1; /* In the expectation that opening as a file will fail */ +return S_ISREG(statbuf.st_mode); +} + + +#if defined NATIVE_ZOS +/************* Test for a terminal in z/OS **********/ +/* isatty() does not work in a TSO environment, so always give FALSE.*/ + +static BOOL +is_stdout_tty(void) +{ +return FALSE; +} + +static BOOL +is_file_tty(FILE *f) +{ +return FALSE; +} + + +/************* Test for a terminal, Unix-style **********/ + +#else +static BOOL +is_stdout_tty(void) +{ +return isatty(fileno(stdout)); +} + +static BOOL +is_file_tty(FILE *f) +{ +return isatty(fileno(f)); +} +#endif + + +/************* Print optionally coloured match Unix-style and z/OS **********/ + +static void +print_match(const void *buf, int length) +{ +if (length == 0) return; +if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string); +FWRITE_IGNORE(buf, 1, length, stdout); +if (do_colour) fprintf(stdout, "%c[0m", 0x1b); +} + +/* End of Unix-style or native z/OS environment functions. */ + + +/************* Directory scanning in Windows ***********/ + +/* I (Philip Hazel) have no means of testing this code. It was contributed by +Lionel Fourquaux. David Burgess added a patch to define INVALID_FILE_ATTRIBUTES +when it did not exist. David Byron added a patch that moved the #include of + to before the INVALID_FILE_ATTRIBUTES definition rather than after. +*/ + +#elif defined WIN32 + +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF +#endif + +typedef struct directory_type +{ +HANDLE handle; +BOOL first; +WIN32_FIND_DATA data; +} directory_type; + +#define FILESEP '/' + +int +isdirectory(char *filename) +{ +DWORD attr = GetFileAttributes(filename); +if (attr == INVALID_FILE_ATTRIBUTES) + return 0; +return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +directory_type * +opendirectory(char *filename) +{ +size_t len; +char *pattern; +directory_type *dir; +DWORD err; +len = strlen(filename); +pattern = (char *)malloc(len + 3); +dir = (directory_type *)malloc(sizeof(*dir)); +if ((pattern == NULL) || (dir == NULL)) + { + fprintf(stderr, "pcre2grep: malloc failed\n"); + pcre2grep_exit(2); + } +memcpy(pattern, filename, len); +if (iswild(filename)) + pattern[len] = 0; +else + memcpy(&(pattern[len]), "\\*", 3); +dir->handle = FindFirstFile(pattern, &(dir->data)); +if (dir->handle != INVALID_HANDLE_VALUE) + { + free(pattern); + dir->first = TRUE; + return dir; + } +err = GetLastError(); +free(pattern); +free(dir); +errno = (err == ERROR_ACCESS_DENIED) ? EACCES : ENOENT; +return NULL; +} + +char * +readdirectory(directory_type *dir) +{ +for (;;) + { + if (!dir->first) + { + if (!FindNextFile(dir->handle, &(dir->data))) + return NULL; + } + else + { + dir->first = FALSE; + } + if (strcmp(dir->data.cFileName, ".") != 0 && strcmp(dir->data.cFileName, "..") != 0) + return dir->data.cFileName; + } +#ifndef _MSC_VER +return NULL; /* Keep compiler happy; never executed */ +#endif +} + +void +closedirectory(directory_type *dir) +{ +FindClose(dir->handle); +free(dir); +} + + +/************* Test for regular file in Windows **********/ + +/* I don't know how to do this, or if it can be done; assume all paths are +regular if they are not directories. */ + +int isregfile(char *filename) +{ +return !isdirectory(filename); +} + + +/************* Test for a terminal in Windows **********/ + +static BOOL +is_stdout_tty(void) +{ +return _isatty(_fileno(stdout)); +} + +static BOOL +is_file_tty(FILE *f) +{ +return _isatty(_fileno(f)); +} + + +/************* Print optionally coloured match in Windows **********/ + +static void +print_match(const void *buf, int length) +{ +if (length == 0) return; +if (do_colour) + { + if (do_ansi) fprintf(stdout, "%c[%sm", 0x1b, colour_string); + else SetConsoleTextAttribute(hstdout, match_colour); + } +FWRITE_IGNORE(buf, 1, length, stdout); +if (do_colour) + { + if (do_ansi) fprintf(stdout, "%c[0m", 0x1b); + else SetConsoleTextAttribute(hstdout, csbi.wAttributes); + } +} + +/* End of Windows functions */ + + +/************* Directory scanning when we can't do it ***********/ + +/* The type is void, and apart from isdirectory(), the functions do nothing. */ + +#else + +#define FILESEP 0 +typedef void directory_type; + +int isdirectory(char *filename) { return 0; } +directory_type * opendirectory(char *filename) { return (directory_type*)0;} +char *readdirectory(directory_type *dir) { return (char*)0;} +void closedirectory(directory_type *dir) {} + + +/************* Test for regular file when we can't do it **********/ + +/* Assume all files are regular. */ + +int isregfile(char *filename) { return 1; } + + +/************* Test for a terminal when we can't do it **********/ + +static BOOL +is_stdout_tty(void) +{ +return FALSE; +} + +static BOOL +is_file_tty(FILE *f) +{ +return FALSE; +} + + +/************* Print optionally coloured match when we can't do it **********/ + +static void +print_match(const void *buf, int length) +{ +if (length == 0) return; +FWRITE_IGNORE(buf, 1, length, stdout); +} + +#endif /* End of system-specific functions */ + + + +#ifndef HAVE_STRERROR +/************************************************* +* Provide strerror() for non-ANSI libraries * +*************************************************/ + +/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror() +in their libraries, but can provide the same facility by this simple +alternative function. */ + +extern int sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int n) +{ +if (n < 0 || n >= sys_nerr) return "unknown error number"; +return sys_errlist[n]; +} +#endif /* HAVE_STRERROR */ + + + +/************************************************* +* Usage function * +*************************************************/ + +static int +usage(int rc) +{ +option_item *op; +fprintf(stderr, "Usage: pcre2grep [-"); +for (op = optionlist; op->one_char != 0; op++) + { + if (op->one_char > 0) fprintf(stderr, "%c", op->one_char); + } +fprintf(stderr, "] [long options] [pattern] [files]\n"); +fprintf(stderr, "Type \"pcre2grep --help\" for more information and the long " + "options.\n"); +return rc; +} + + + +/************************************************* +* Help function * +*************************************************/ + +static void +help(void) +{ +option_item *op; + +printf("Usage: pcre2grep [OPTION]... [PATTERN] [FILE1 FILE2 ...]" STDOUT_NL); +printf("Search for PATTERN in each FILE or standard input." STDOUT_NL); +printf("PATTERN must be present if neither -e nor -f is used." STDOUT_NL); + +#ifdef SUPPORT_PCRE2GREP_CALLOUT +printf("Callout scripts in patterns are supported." STDOUT_NL); +#else +printf("Callout scripts are not supported in this pcre2grep." STDOUT_NL); +#endif + +printf("\"-\" can be used as a file name to mean STDIN." STDOUT_NL); + +#ifdef SUPPORT_LIBZ +printf("Files whose names end in .gz are read using zlib." STDOUT_NL); +#endif + +#ifdef SUPPORT_LIBBZ2 +printf("Files whose names end in .bz2 are read using bzlib2." STDOUT_NL); +#endif + +#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 +printf("Other files and the standard input are read as plain files." STDOUT_NL STDOUT_NL); +#else +printf("All files are read as plain files, without any interpretation." STDOUT_NL STDOUT_NL); +#endif + +printf("Example: pcre2grep -i " QUOT "hello.*world" QUOT " menu.h main.c" STDOUT_NL STDOUT_NL); +printf("Options:" STDOUT_NL); + +for (op = optionlist; op->one_char != 0; op++) + { + int n; + char s[4]; + + if (op->one_char > 0 && (op->long_name)[0] == 0) + n = 31 - printf(" -%c", op->one_char); + else + { + if (op->one_char > 0) sprintf(s, "-%c,", op->one_char); + else strcpy(s, " "); + n = 31 - printf(" %s --%s", s, op->long_name); + } + + if (n < 1) n = 1; + printf("%.*s%s" STDOUT_NL, n, " ", op->help_text); + } + +printf(STDOUT_NL "Numbers may be followed by K or M, e.g. --max-buffer-size=100K." STDOUT_NL); +printf("The default value for --buffer-size is %d." STDOUT_NL, PCRE2GREP_BUFSIZE); +printf("The default value for --max-buffer-size is %d." STDOUT_NL, PCRE2GREP_MAX_BUFSIZE); +printf("When reading patterns or file names from a file, trailing white" STDOUT_NL); +printf("space is removed and blank lines are ignored." STDOUT_NL); +printf("The maximum size of any pattern is %d bytes." STDOUT_NL, MAXPATLEN); + +printf(STDOUT_NL "With no FILEs, read standard input. If fewer than two FILEs given, assume -h." STDOUT_NL); +printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble." STDOUT_NL); +} + + + +/************************************************* +* Test exclude/includes * +*************************************************/ + +/* If any exclude pattern matches, the path is excluded. Otherwise, unless +there are no includes, the path must match an include pattern. + +Arguments: + path the path to be matched + ip the chain of include patterns + ep the chain of exclude patterns + +Returns: TRUE if the path is not excluded +*/ + +static BOOL +test_incexc(char *path, patstr *ip, patstr *ep) +{ +int plen = strlen((const char *)path); + +for (; ep != NULL; ep = ep->next) + { + if (pcre2_match(ep->compiled, (PCRE2_SPTR)path, plen, 0, 0, match_data, NULL) >= 0) + return FALSE; + } + +if (ip == NULL) return TRUE; + +for (; ip != NULL; ip = ip->next) + { + if (pcre2_match(ip->compiled, (PCRE2_SPTR)path, plen, 0, 0, match_data, NULL) >= 0) + return TRUE; + } + +return FALSE; +} + + + +/************************************************* +* Decode integer argument value * +*************************************************/ + +/* Integer arguments can be followed by K or M. Avoid the use of strtoul() +because SunOS4 doesn't have it. This is used only for unpicking arguments, so +just keep it simple. + +Arguments: + option_data the option data string + op the option item (for error messages) + longop TRUE if option given in long form + +Returns: a long integer +*/ + +static long int +decode_number(char *option_data, option_item *op, BOOL longop) +{ +unsigned long int n = 0; +char *endptr = option_data; +while (*endptr != 0 && isspace((unsigned char)(*endptr))) endptr++; +while (isdigit((unsigned char)(*endptr))) + n = n * 10 + (int)(*endptr++ - '0'); +if (toupper(*endptr) == 'K') + { + n *= 1024; + endptr++; + } +else if (toupper(*endptr) == 'M') + { + n *= 1024*1024; + endptr++; + } + +if (*endptr != 0) /* Error */ + { + if (longop) + { + char *equals = strchr(op->long_name, '='); + int nlen = (equals == NULL)? (int)strlen(op->long_name) : + (int)(equals - op->long_name); + fprintf(stderr, "pcre2grep: Malformed number \"%s\" after --%.*s\n", + option_data, nlen, op->long_name); + } + else + fprintf(stderr, "pcre2grep: Malformed number \"%s\" after -%c\n", + option_data, op->one_char); + pcre2grep_exit(usage(2)); + } + +return n; +} + + + +/************************************************* +* Add item to a chain of numbers * +*************************************************/ + +/* Used to add an item onto a chain, or just return an unconnected item if the +"after" argument is NULL. + +Arguments: + n the number to add + after if not NULL points to item to insert after + +Returns: new number block +*/ + +static omstr * +add_number(int n, omstr *after) +{ +omstr *om = (omstr *)malloc(sizeof(omstr)); + +if (om == NULL) + { + fprintf(stderr, "pcre2grep: malloc failed\n"); + pcre2grep_exit(2); + } +om->next = NULL; +om->groupnum = n; + +if (after != NULL) + { + om->next = after->next; + after->next = om; + } +return om; +} + + + +/************************************************* +* Read one line of input * +*************************************************/ + +/* Normally, input that is to be scanned is read using fread() (or gzread, or +BZ2_read) into a large buffer, so many lines may be read at once. However, +doing this for tty input means that no output appears until a lot of input has +been typed. Instead, tty input is handled line by line. We cannot use fgets() +for this, because it does not stop at a binary zero, and therefore there is no +way of telling how many characters it has read, because there may be binary +zeros embedded in the data. This function is also used for reading patterns +from files (the -f option). + +Arguments: + buffer the buffer to read into + length the maximum number of characters to read + f the file + +Returns: the number of characters read, zero at end of file +*/ + +static PCRE2_SIZE +read_one_line(char *buffer, int length, FILE *f) +{ +int c; +int yield = 0; +while ((c = fgetc(f)) != EOF) + { + buffer[yield++] = c; + if (c == '\n' || yield >= length) break; + } +return yield; +} + + + +/************************************************* +* Find end of line * +*************************************************/ + +/* The length of the endline sequence that is found is set via lenptr. This may +be zero at the very end of the file if there is no line-ending sequence there. + +Arguments: + p current position in line + endptr end of available data + lenptr where to put the length of the eol sequence + +Returns: pointer after the last byte of the line, + including the newline byte(s) +*/ + +static char * +end_of_line(char *p, char *endptr, int *lenptr) +{ +switch(endlinetype) + { + default: /* Just in case */ + case PCRE2_NEWLINE_LF: + while (p < endptr && *p != '\n') p++; + if (p < endptr) + { + *lenptr = 1; + return p + 1; + } + *lenptr = 0; + return endptr; + + case PCRE2_NEWLINE_CR: + while (p < endptr && *p != '\r') p++; + if (p < endptr) + { + *lenptr = 1; + return p + 1; + } + *lenptr = 0; + return endptr; + + case PCRE2_NEWLINE_NUL: + while (p < endptr && *p != '\0') p++; + if (p < endptr) + { + *lenptr = 1; + return p + 1; + } + *lenptr = 0; + return endptr; + + case PCRE2_NEWLINE_CRLF: + for (;;) + { + while (p < endptr && *p != '\r') p++; + if (++p >= endptr) + { + *lenptr = 0; + return endptr; + } + if (*p == '\n') + { + *lenptr = 2; + return p + 1; + } + } + break; + + case PCRE2_NEWLINE_ANYCRLF: + while (p < endptr) + { + int extra = 0; + int c = *((unsigned char *)p); + + if (utf && c >= 0xc0) + { + int gcii, gcss; + extra = utf8_table4[c & 0x3f]; /* Number of additional bytes */ + gcss = 6*extra; + c = (c & utf8_table3[extra]) << gcss; + for (gcii = 1; gcii <= extra; gcii++) + { + gcss -= 6; + c |= (p[gcii] & 0x3f) << gcss; + } + } + + p += 1 + extra; + + switch (c) + { + case '\n': + *lenptr = 1; + return p; + + case '\r': + if (p < endptr && *p == '\n') + { + *lenptr = 2; + p++; + } + else *lenptr = 1; + return p; + + default: + break; + } + } /* End of loop for ANYCRLF case */ + + *lenptr = 0; /* Must have hit the end */ + return endptr; + + case PCRE2_NEWLINE_ANY: + while (p < endptr) + { + int extra = 0; + int c = *((unsigned char *)p); + + if (utf && c >= 0xc0) + { + int gcii, gcss; + extra = utf8_table4[c & 0x3f]; /* Number of additional bytes */ + gcss = 6*extra; + c = (c & utf8_table3[extra]) << gcss; + for (gcii = 1; gcii <= extra; gcii++) + { + gcss -= 6; + c |= (p[gcii] & 0x3f) << gcss; + } + } + + p += 1 + extra; + + switch (c) + { + case '\n': /* LF */ + case '\v': /* VT */ + case '\f': /* FF */ + *lenptr = 1; + return p; + + case '\r': /* CR */ + if (p < endptr && *p == '\n') + { + *lenptr = 2; + p++; + } + else *lenptr = 1; + return p; + +#ifndef EBCDIC + case 0x85: /* Unicode NEL */ + *lenptr = utf? 2 : 1; + return p; + + case 0x2028: /* Unicode LS */ + case 0x2029: /* Unicode PS */ + *lenptr = 3; + return p; +#endif /* Not EBCDIC */ + + default: + break; + } + } /* End of loop for ANY case */ + + *lenptr = 0; /* Must have hit the end */ + return endptr; + } /* End of overall switch */ +} + + + +/************************************************* +* Find start of previous line * +*************************************************/ + +/* This is called when looking back for before lines to print. + +Arguments: + p start of the subsequent line + startptr start of available data + +Returns: pointer to the start of the previous line +*/ + +static char * +previous_line(char *p, char *startptr) +{ +switch(endlinetype) + { + default: /* Just in case */ + case PCRE2_NEWLINE_LF: + p--; + while (p > startptr && p[-1] != '\n') p--; + return p; + + case PCRE2_NEWLINE_CR: + p--; + while (p > startptr && p[-1] != '\n') p--; + return p; + + case PCRE2_NEWLINE_NUL: + p--; + while (p > startptr && p[-1] != '\0') p--; + return p; + + case PCRE2_NEWLINE_CRLF: + for (;;) + { + p -= 2; + while (p > startptr && p[-1] != '\n') p--; + if (p <= startptr + 1 || p[-2] == '\r') return p; + } + /* Control can never get here */ + + case PCRE2_NEWLINE_ANY: + case PCRE2_NEWLINE_ANYCRLF: + if (*(--p) == '\n' && p > startptr && p[-1] == '\r') p--; + if (utf) while ((*p & 0xc0) == 0x80) p--; + + while (p > startptr) + { + unsigned int c; + char *pp = p - 1; + + if (utf) + { + int extra = 0; + while ((*pp & 0xc0) == 0x80) pp--; + c = *((unsigned char *)pp); + if (c >= 0xc0) + { + int gcii, gcss; + extra = utf8_table4[c & 0x3f]; /* Number of additional bytes */ + gcss = 6*extra; + c = (c & utf8_table3[extra]) << gcss; + for (gcii = 1; gcii <= extra; gcii++) + { + gcss -= 6; + c |= (pp[gcii] & 0x3f) << gcss; + } + } + } + else c = *((unsigned char *)pp); + + if (endlinetype == PCRE2_NEWLINE_ANYCRLF) switch (c) + { + case '\n': /* LF */ + case '\r': /* CR */ + return p; + + default: + break; + } + + else switch (c) + { + case '\n': /* LF */ + case '\v': /* VT */ + case '\f': /* FF */ + case '\r': /* CR */ +#ifndef EBCDIC + case 0x85: /* Unicode NEL */ + case 0x2028: /* Unicode LS */ + case 0x2029: /* Unicode PS */ +#endif /* Not EBCDIC */ + return p; + + default: + break; + } + + p = pp; /* Back one character */ + } /* End of loop for ANY case */ + + return startptr; /* Hit start of data */ + } /* End of overall switch */ +} + + + +/************************************************* +* Print the previous "after" lines * +*************************************************/ + +/* This is called if we are about to lose said lines because of buffer filling, +and at the end of the file. The data in the line is written using fwrite() so +that a binary zero does not terminate it. + +Arguments: + lastmatchnumber the number of the last matching line, plus one + lastmatchrestart where we restarted after the last match + endptr end of available data + printname filename for printing + +Returns: nothing +*/ + +static void +do_after_lines(unsigned long int lastmatchnumber, char *lastmatchrestart, + char *endptr, const char *printname) +{ +if (after_context > 0 && lastmatchnumber > 0) + { + int count = 0; + while (lastmatchrestart < endptr && count < after_context) + { + int ellength; + char *pp = end_of_line(lastmatchrestart, endptr, &ellength); + if (ellength == 0 && pp == main_buffer + bufsize) break; + if (printname != NULL) fprintf(stdout, "%s-", printname); + if (number) fprintf(stdout, "%lu-", lastmatchnumber++); + FWRITE_IGNORE(lastmatchrestart, 1, pp - lastmatchrestart, stdout); + lastmatchrestart = pp; + count++; + } + if (count > 0) hyphenpending = TRUE; + } +} + + + +/************************************************* +* Apply patterns to subject till one matches * +*************************************************/ + +/* This function is called to run through all patterns, looking for a match. It +is used multiple times for the same subject when colouring is enabled, in order +to find all possible matches. + +Arguments: + matchptr the start of the subject + length the length of the subject to match + options options for pcre_exec + startoffset where to start matching + mrc address of where to put the result of pcre2_match() + +Returns: TRUE if there was a match + FALSE if there was no match + invert if there was a non-fatal error +*/ + +static BOOL +match_patterns(char *matchptr, PCRE2_SIZE length, unsigned int options, + PCRE2_SIZE startoffset, int *mrc) +{ +int i; +PCRE2_SIZE slen = length; +patstr *p = patterns; +const char *msg = "this text:\n\n"; + +if (slen > 200) + { + slen = 200; + msg = "text that starts:\n\n"; + } +for (i = 1; p != NULL; p = p->next, i++) + { + *mrc = pcre2_match(p->compiled, (PCRE2_SPTR)matchptr, (int)length, + startoffset, options, match_data, match_context); + if (*mrc >= 0) return TRUE; + if (*mrc == PCRE2_ERROR_NOMATCH) continue; + fprintf(stderr, "pcre2grep: pcre2_match() gave error %d while matching ", *mrc); + if (patterns->next != NULL) fprintf(stderr, "pattern number %d to ", i); + fprintf(stderr, "%s", msg); + FWRITE_IGNORE(matchptr, 1, slen, stderr); /* In case binary zero included */ + fprintf(stderr, "\n\n"); + if (*mrc == PCRE2_ERROR_MATCHLIMIT || *mrc == PCRE2_ERROR_DEPTHLIMIT || + *mrc == PCRE2_ERROR_HEAPLIMIT || *mrc == PCRE2_ERROR_JIT_STACKLIMIT) + resource_error = TRUE; + if (error_count++ > 20) + { + fprintf(stderr, "pcre2grep: Too many errors - abandoned.\n"); + pcre2grep_exit(2); + } + return invert; /* No more matching; don't show the line again */ + } + +return FALSE; /* No match, no errors */ +} + + +/************************************************* +* Check output text for errors * +*************************************************/ + +static BOOL +syntax_check_output_text(PCRE2_SPTR string, BOOL callout) +{ +PCRE2_SPTR begin = string; +for (; *string != 0; string++) + { + if (*string == '$') + { + PCRE2_SIZE capture_id = 0; + BOOL brace = FALSE; + + string++; + + /* Syntax error: a character must be present after $. */ + if (*string == 0) + { + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "no character after $"); + return FALSE; + } + + if (*string == '{') + { + /* Must be a decimal number in braces, e.g: {5} or {38} */ + string++; + + brace = TRUE; + } + + if ((*string >= '1' && *string <= '9') || (!callout && *string == '0')) + { + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + } + while (*string >= '0' && *string <= '9'); + + if (brace) + { + /* Syntax error: closing brace is missing. */ + if (*string != '}') + { + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "missing closing brace"); + return FALSE; + } + } + else + { + /* To negate the effect of the for. */ + string--; + } + } + else if (brace) + { + /* Syntax error: a decimal number required. */ + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "decimal number expected"); + return FALSE; + } + else if (*string == 'o') + { + string++; + + if (*string < '0' || *string > '7') + { + /* Syntax error: an octal number required. */ + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "octal number expected"); + return FALSE; + } + } + else if (*string == 'x') + { + string++; + + if (!isxdigit((unsigned char)*string)) + { + /* Syntax error: a hexdecimal number required. */ + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "hexadecimal number expected"); + return FALSE; + } + } + } + } + + return TRUE; +} + + +/************************************************* +* Display output text * +*************************************************/ + +/* Display the output text, which is assumed to have already been syntax +checked. Output may contain escape sequences started by the dollar sign. The +escape sequences are substituted as follows: + + $ or ${} is replaced by the captured substring of the given + decimal number; zero will substitute the whole match. If the number is + greater than the number of capturing substrings, or if the capture is unset, + the replacement is empty. + + $a is replaced by bell. + $b is replaced by backspace. + $e is replaced by escape. + $f is replaced by form feed. + $n is replaced by newline. + $r is replaced by carriage return. + $t is replaced by tab. + $v is replaced by vertical tab. + + $o is replaced by the character represented by the given octal + number; up to three digits are processed. + + $x is replaced by the character represented by the given hexadecimal + number; up to two digits are processed. + + Any other character is substituted by itself. E.g: $$ is replaced by a single + dollar. + +Arguments: + string: the output text + callout: TRUE for the builtin callout, FALSE for --output + subject the start of the subject + ovector: capture offsets + capture_top: number of captures + +Returns: TRUE if something was output, other than newline + FALSE if nothing was output, or newline was last output +*/ + +static BOOL +display_output_text(PCRE2_SPTR string, BOOL callout, PCRE2_SPTR subject, + PCRE2_SIZE *ovector, PCRE2_SIZE capture_top) +{ +BOOL printed = FALSE; + +for (; *string != 0; string++) + { + int ch = EOF; + if (*string == '$') + { + PCRE2_SIZE capture_id = 0; + BOOL brace = FALSE; + + string++; + + if (*string == '{') + { + /* Must be a decimal number in braces, e.g: {5} or {38} */ + string++; + + brace = TRUE; + } + + if ((*string >= '1' && *string <= '9') || (!callout && *string == '0')) + { + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + } + while (*string >= '0' && *string <= '9'); + + if (!brace) + { + /* To negate the effect of the for. */ + string--; + } + + if (capture_id < capture_top) + { + PCRE2_SIZE capturesize; + capture_id *= 2; + + capturesize = ovector[capture_id + 1] - ovector[capture_id]; + if (capturesize > 0) + { + print_match(subject + ovector[capture_id], capturesize); + printed = TRUE; + } + } + } + else if (*string == 'a') ch = '\a'; + else if (*string == 'b') ch = '\b'; +#ifndef EBCDIC + else if (*string == 'e') ch = '\033'; +#else + else if (*string == 'e') ch = '\047'; +#endif + else if (*string == 'f') ch = '\f'; + else if (*string == 'r') ch = '\r'; + else if (*string == 't') ch = '\t'; + else if (*string == 'v') ch = '\v'; + else if (*string == 'n') + { + fprintf(stdout, STDOUT_NL); + printed = FALSE; + } + else if (*string == 'o') + { + string++; + + ch = *string - '0'; + if (string[1] >= '0' && string[1] <= '7') + { + string++; + ch = ch * 8 + (*string - '0'); + } + if (string[1] >= '0' && string[1] <= '7') + { + string++; + ch = ch * 8 + (*string - '0'); + } + } + else if (*string == 'x') + { + string++; + + if (*string >= '0' && *string <= '9') + ch = *string - '0'; + else + ch = (*string | 0x20) - 'a' + 10; + if (isxdigit((unsigned char)string[1])) + { + string++; + ch *= 16; + if (*string >= '0' && *string <= '9') + ch += *string - '0'; + else + ch += (*string | 0x20) - 'a' + 10; + } + } + else + { + ch = *string; + } + } + else + { + ch = *string; + } + if (ch != EOF) + { + fprintf(stdout, "%c", ch); + printed = TRUE; + } + } + +return printed; +} + + +#ifdef SUPPORT_PCRE2GREP_CALLOUT + +/************************************************* +* Parse and execute callout scripts * +*************************************************/ + +/* This function parses a callout string block and executes the +program specified by the string. The string is a list of substrings +separated by pipe characters. The first substring represents the +executable name, and the following substrings specify the arguments: + + program_name|param1|param2|... + +Any substring (including the program name) can contain escape sequences +started by the dollar character. The escape sequences are substituted as +follows: + + $ or ${} is replaced by the captured substring of the given + decimal number, which must be greater than zero. If the number is greater + than the number of capturing substrings, or if the capture is unset, the + replacement is empty. + + Any other character is substituted by itself. E.g: $$ is replaced by a single + dollar or $| replaced by a pipe character. + +Alternatively, if string starts with pipe, the remainder is taken as an output +string, same as --output. In this case, --om-separator is used to separate each +callout, defaulting to newline. + +Example: + + echo -e "abcde\n12345" | pcre2grep \ + '(.)(..(.))(?C"/bin/echo|Arg1: [$1] [$2] [$3]|Arg2: $|${1}$| ($4)")()' - + + Output: + + Arg1: [a] [bcd] [d] Arg2: |a| () + abcde + Arg1: [1] [234] [4] Arg2: |1| () + 12345 + +Arguments: + blockptr the callout block + +Returns: currently it always returns with 0 +*/ + +static int +pcre2grep_callout(pcre2_callout_block *calloutptr, void *unused) +{ +PCRE2_SIZE length = calloutptr->callout_string_length; +PCRE2_SPTR string = calloutptr->callout_string; +PCRE2_SPTR subject = calloutptr->subject; +PCRE2_SIZE *ovector = calloutptr->offset_vector; +PCRE2_SIZE capture_top = calloutptr->capture_top; +PCRE2_SIZE argsvectorlen = 2; +PCRE2_SIZE argslen = 1; +char *args; +char *argsptr; +char **argsvector; +char **argsvectorptr; +#ifndef WIN32 +pid_t pid; +#endif +int result = 0; + +(void)unused; /* Avoid compiler warning */ + +/* Only callout with strings are supported. */ +if (string == NULL || length == 0) return 0; + +/* If there's no command, output the remainder directly. */ + +if (*string == '|') + { + string++; + if (!syntax_check_output_text(string, TRUE)) return 0; + (void)display_output_text(string, TRUE, subject, ovector, capture_top); + return 0; + } + +/* Checking syntax and compute the number of string fragments. Callout strings +are ignored in case of a syntax error. */ + +while (length > 0) + { + if (*string == '|') + { + argsvectorlen++; + + /* Maximum 10000 arguments allowed. */ + if (argsvectorlen > 10000) return 0; + } + else if (*string == '$') + { + PCRE2_SIZE capture_id = 0; + + string++; + length--; + + /* Syntax error: a character must be present after $. */ + if (length == 0) return 0; + + if (*string >= '1' && *string <= '9') + { + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + length--; + } + while (length > 0 && *string >= '0' && *string <= '9'); + + /* To negate the effect of string++ below. */ + string--; + length++; + } + else if (*string == '{') + { + /* Must be a decimal number in braces, e.g: {5} or {38} */ + string++; + length--; + + /* Syntax error: a decimal number required. */ + if (length == 0) return 0; + if (*string < '1' || *string > '9') return 0; + + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + length--; + + /* Syntax error: no more characters */ + if (length == 0) return 0; + } + while (*string >= '0' && *string <= '9'); + + /* Syntax error: closing brace is missing. */ + if (*string != '}') return 0; + } + + if (capture_id > 0) + { + if (capture_id < capture_top) + { + capture_id *= 2; + argslen += ovector[capture_id + 1] - ovector[capture_id]; + } + + /* To negate the effect of argslen++ below. */ + argslen--; + } + } + + string++; + length--; + argslen++; + } + +args = (char*)malloc(argslen); +if (args == NULL) return 0; + +argsvector = (char**)malloc(argsvectorlen * sizeof(char*)); +if (argsvector == NULL) + { + free(args); + return 0; + } + +argsptr = args; +argsvectorptr = argsvector; + +*argsvectorptr++ = argsptr; + +length = calloutptr->callout_string_length; +string = calloutptr->callout_string; + +while (length > 0) + { + if (*string == '|') + { + *argsptr++ = '\0'; + *argsvectorptr++ = argsptr; + } + else if (*string == '$') + { + string++; + length--; + + if ((*string >= '1' && *string <= '9') || *string == '{') + { + PCRE2_SIZE capture_id = 0; + + if (*string != '{') + { + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + length--; + } + while (length > 0 && *string >= '0' && *string <= '9'); + + /* To negate the effect of string++ below. */ + string--; + length++; + } + else + { + string++; + length--; + + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + length--; + } + while (*string != '}'); + } + + if (capture_id < capture_top) + { + PCRE2_SIZE capturesize; + capture_id *= 2; + + capturesize = ovector[capture_id + 1] - ovector[capture_id]; + memcpy(argsptr, subject + ovector[capture_id], capturesize); + argsptr += capturesize; + } + } + else + { + *argsptr++ = *string; + } + } + else + { + *argsptr++ = *string; + } + + string++; + length--; + } + +*argsptr++ = '\0'; +*argsvectorptr = NULL; + +#ifdef WIN32 +result = _spawnvp(_P_WAIT, argsvector[0], (const char * const *)argsvector); +#else +pid = fork(); + +if (pid == 0) + { + (void)execv(argsvector[0], argsvector); + /* Control gets here if there is an error, e.g. a non-existent program */ + exit(1); + } +else if (pid > 0) + (void)waitpid(pid, &result, 0); +#endif + +free(args); +free(argsvector); + +/* Currently negative return values are not supported, only zero (match +continues) or non-zero (match fails). */ + +return result != 0; +} + +#endif + + + +/************************************************* +* Read a portion of the file into buffer * +*************************************************/ + +static int +fill_buffer(void *handle, int frtype, char *buffer, int length, + BOOL input_line_buffered) +{ +(void)frtype; /* Avoid warning when not used */ + +#ifdef SUPPORT_LIBZ +if (frtype == FR_LIBZ) + return gzread((gzFile)handle, buffer, length); +else +#endif + +#ifdef SUPPORT_LIBBZ2 +if (frtype == FR_LIBBZ2) + return BZ2_bzread((BZFILE *)handle, buffer, length); +else +#endif + +return (input_line_buffered ? + read_one_line(buffer, length, (FILE *)handle) : + fread(buffer, 1, length, (FILE *)handle)); +} + + + +/************************************************* +* Grep an individual file * +*************************************************/ + +/* This is called from grep_or_recurse() below. It uses a buffer that is three +times the value of bufthird. The matching point is never allowed to stray into +the top third of the buffer, thus keeping more of the file available for +context printing or for multiline scanning. For large files, the pointer will +be in the middle third most of the time, so the bottom third is available for +"before" context printing. + +Arguments: + handle the fopened FILE stream for a normal file + the gzFile pointer when reading is via libz + the BZFILE pointer when reading is via libbz2 + frtype FR_PLAIN, FR_LIBZ, or FR_LIBBZ2 + filename the file name or NULL (for errors) + printname the file name if it is to be printed for each match + or NULL if the file name is not to be printed + it cannot be NULL if filenames[_nomatch]_only is set + +Returns: 0 if there was at least one match + 1 otherwise (no matches) + 2 if an overlong line is encountered + 3 if there is a read error on a .bz2 file +*/ + +static int +pcre2grep(void *handle, int frtype, const char *filename, const char *printname) +{ +int rc = 1; +int filepos = 0; +unsigned long int linenumber = 1; +unsigned long int lastmatchnumber = 0; +unsigned long int count = 0; +char *lastmatchrestart = main_buffer; +char *ptr = main_buffer; +char *endptr; +PCRE2_SIZE bufflength; +BOOL binary = FALSE; +BOOL endhyphenpending = FALSE; +BOOL input_line_buffered = line_buffered; +FILE *in = NULL; /* Ensure initialized */ + +/* Do the first read into the start of the buffer and set up the pointer to end +of what we have. In the case of libz, a non-zipped .gz file will be read as a +plain file. However, if a .bz2 file isn't actually bzipped, the first read will +fail. */ + +if (frtype != FR_LIBZ && frtype != FR_LIBBZ2) + { + in = (FILE *)handle; + if (is_file_tty(in)) input_line_buffered = TRUE; + } +else input_line_buffered = FALSE; + +bufflength = fill_buffer(handle, frtype, main_buffer, bufsize, + input_line_buffered); + +#ifdef SUPPORT_LIBBZ2 +if (frtype == FR_LIBBZ2 && (int)bufflength < 0) return 2; /* Gotcha: bufflength is PCRE2_SIZE; */ +#endif + +endptr = main_buffer + bufflength; + +/* Unless binary-files=text, see if we have a binary file. This uses the same +rule as GNU grep, namely, a search for a binary zero byte near the start of the +file. However, when the newline convention is binary zero, we can't do this. */ + +if (binary_files != BIN_TEXT) + { + if (endlinetype != PCRE2_NEWLINE_NUL) + binary = memchr(main_buffer, 0, (bufflength > 1024)? 1024 : bufflength) + != NULL; + if (binary && binary_files == BIN_NOMATCH) return 1; + } + +/* Loop while the current pointer is not at the end of the file. For large +files, endptr will be at the end of the buffer when we are in the middle of the +file, but ptr will never get there, because as soon as it gets over 2/3 of the +way, the buffer is shifted left and re-filled. */ + +while (ptr < endptr) + { + int endlinelength; + int mrc = 0; + unsigned int options = 0; + BOOL match; + char *t = ptr; + PCRE2_SIZE length, linelength; + PCRE2_SIZE startoffset = 0; + + /* At this point, ptr is at the start of a line. We need to find the length + of the subject string to pass to pcre2_match(). In multiline mode, it is the + length remainder of the data in the buffer. Otherwise, it is the length of + the next line, excluding the terminating newline. After matching, we always + advance by the length of the next line. In multiline mode the PCRE2_FIRSTLINE + option is used for compiling, so that any match is constrained to be in the + first line. */ + + t = end_of_line(t, endptr, &endlinelength); + linelength = t - ptr - endlinelength; + length = multiline? (PCRE2_SIZE)(endptr - ptr) : linelength; + + /* Check to see if the line we are looking at extends right to the very end + of the buffer without a line terminator. This means the line is too long to + handle at the current buffer size. Until the buffer reaches its maximum size, + try doubling it and reading more data. */ + + if (endlinelength == 0 && t == main_buffer + bufsize) + { + if (bufthird < max_bufthird) + { + char *new_buffer; + int new_bufthird = 2*bufthird; + + if (new_bufthird > max_bufthird) new_bufthird = max_bufthird; + new_buffer = (char *)malloc(3*new_bufthird); + + if (new_buffer == NULL) + { + fprintf(stderr, + "pcre2grep: line %lu%s%s is too long for the internal buffer\n" + "pcre2grep: not enough memory to increase the buffer size to %d\n", + linenumber, + (filename == NULL)? "" : " of file ", + (filename == NULL)? "" : filename, + new_bufthird); + return 2; + } + + /* Copy the data and adjust pointers to the new buffer location. */ + + memcpy(new_buffer, main_buffer, bufsize); + bufthird = new_bufthird; + bufsize = 3*bufthird; + ptr = new_buffer + (ptr - main_buffer); + lastmatchrestart = new_buffer + (lastmatchrestart - main_buffer); + free(main_buffer); + main_buffer = new_buffer; + + /* Read more data into the buffer and then try to find the line ending + again. */ + + bufflength += fill_buffer(handle, frtype, main_buffer + bufflength, + bufsize - bufflength, input_line_buffered); + endptr = main_buffer + bufflength; + continue; + } + else + { + fprintf(stderr, + "pcre2grep: line %lu%s%s is too long for the internal buffer\n" + "pcre2grep: the maximum buffer size is %d\n" + "pcre2grep: use the --max-buffer-size option to change it\n", + linenumber, + (filename == NULL)? "" : " of file ", + (filename == NULL)? "" : filename, + bufthird); + return 2; + } + } + + /* Extra processing for Jeffrey Friedl's debugging. */ + +#ifdef JFRIEDL_DEBUG + if (jfriedl_XT || jfriedl_XR) + { +# include +# include + struct timeval start_time, end_time; + struct timezone dummy; + int i; + + if (jfriedl_XT) + { + unsigned long newlen = length * jfriedl_XT + strlen(jfriedl_prefix) + strlen(jfriedl_postfix); + const char *orig = ptr; + ptr = malloc(newlen + 1); + if (!ptr) { + printf("out of memory"); + pcre2grep_exit(2); + } + endptr = ptr; + strcpy(endptr, jfriedl_prefix); endptr += strlen(jfriedl_prefix); + for (i = 0; i < jfriedl_XT; i++) { + strncpy(endptr, orig, length); + endptr += length; + } + strcpy(endptr, jfriedl_postfix); endptr += strlen(jfriedl_postfix); + length = newlen; + } + + if (gettimeofday(&start_time, &dummy) != 0) + perror("bad gettimeofday"); + + + for (i = 0; i < jfriedl_XR; i++) + match = (pcre_exec(patterns->compiled, patterns->hint, ptr, length, 0, + PCRE2_NOTEMPTY, offsets, OFFSET_SIZE) >= 0); + + if (gettimeofday(&end_time, &dummy) != 0) + perror("bad gettimeofday"); + + double delta = ((end_time.tv_sec + (end_time.tv_usec / 1000000.0)) + - + (start_time.tv_sec + (start_time.tv_usec / 1000000.0))); + + printf("%s TIMER[%.4f]\n", match ? "MATCH" : "FAIL", delta); + return 0; + } +#endif + + /* We come back here after a match when only_matching_count is non-zero, in + order to find any further matches in the same line. This applies to + --only-matching, --file-offsets, and --line-offsets. */ + + ONLY_MATCHING_RESTART: + + /* Run through all the patterns until one matches or there is an error other + than NOMATCH. This code is in a subroutine so that it can be re-used for + finding subsequent matches when colouring matched lines. After finding one + match, set PCRE2_NOTEMPTY to disable any further matches of null strings in + this line. */ + + match = match_patterns(ptr, length, options, startoffset, &mrc); + options = PCRE2_NOTEMPTY; + + /* If it's a match or a not-match (as required), do what's wanted. NOTE: Use + only FWRITE_IGNORE() - which is just a packaged fwrite() that ignores its + return code - to output data lines, so that binary zeroes are treated as just + another data character. */ + + if (match != invert) + { + BOOL hyphenprinted = FALSE; + + /* We've failed if we want a file that doesn't have any matches. */ + + if (filenames == FN_NOMATCH_ONLY) return 1; + + /* If all we want is a yes/no answer, we can return immediately. */ + + if (quiet) return 0; + + /* Just count if just counting is wanted. */ + + else if (count_only || show_total_count) count++; + + /* When handling a binary file and binary-files==binary, the "binary" + variable will be set true (it's false in all other cases). In this + situation we just want to output the file name. No need to scan further. */ + + else if (binary) + { + fprintf(stdout, "Binary file %s matches" STDOUT_NL, filename); + return 0; + } + + /* Likewise, if all we want is a file name, there is no need to scan any + more lines in the file. */ + + else if (filenames == FN_MATCH_ONLY) + { + fprintf(stdout, "%s" STDOUT_NL, printname); + return 0; + } + + /* The --only-matching option prints just the substring that matched, + and/or one or more captured portions of it, as long as these strings are + not empty. The --file-offsets and --line-offsets options output offsets for + the matching substring (all three set only_matching_count non-zero). None + of these mutually exclusive options prints any context. Afterwards, adjust + the start and then jump back to look for further matches in the same line. + If we are in invert mode, however, nothing is printed and we do not restart + - this could still be useful because the return code is set. */ + + else if (only_matching_count != 0) + { + if (!invert) + { + PCRE2_SIZE oldstartoffset; + + if (printname != NULL) fprintf(stdout, "%s:", printname); + if (number) fprintf(stdout, "%lu:", linenumber); + + /* Handle --line-offsets */ + + if (line_offsets) + fprintf(stdout, "%d,%d" STDOUT_NL, (int)(ptr + offsets[0] - ptr), + (int)(offsets[1] - offsets[0])); + + /* Handle --file-offsets */ + + else if (file_offsets) + fprintf(stdout, "%d,%d" STDOUT_NL, + (int)(filepos + ptr + offsets[0] - ptr), + (int)(offsets[1] - offsets[0])); + + /* Handle --output (which has already been syntax checked) */ + + else if (output_text != NULL) + { + if (display_output_text((PCRE2_SPTR)output_text, FALSE, + (PCRE2_SPTR)ptr, offsets, mrc) || printname != NULL || + number) + fprintf(stdout, STDOUT_NL); + } + + /* Handle --only-matching, which may occur many times */ + + else + { + BOOL printed = FALSE; + omstr *om; + + for (om = only_matching; om != NULL; om = om->next) + { + int n = om->groupnum; + if (n < mrc) + { + int plen = offsets[2*n + 1] - offsets[2*n]; + if (plen > 0) + { + if (printed && om_separator != NULL) + fprintf(stdout, "%s", om_separator); + print_match(ptr + offsets[n*2], plen); + printed = TRUE; + } + } + } + + if (printed || printname != NULL || number) + fprintf(stdout, STDOUT_NL); + } + + /* Prepare to repeat to find the next match in the line. */ + + match = FALSE; + if (line_buffered) fflush(stdout); + rc = 0; /* Had some success */ + + /* If the pattern contained a lookbehind that included \K, it is + possible that the end of the match might be at or before the actual + starting offset we have just used. In this case, start one character + further on. */ + + startoffset = offsets[1]; /* Restart after the match */ + oldstartoffset = pcre2_get_startchar(match_data); + if (startoffset <= oldstartoffset) + { + if (startoffset >= length) goto END_ONE_MATCH; /* Were at end */ + startoffset = oldstartoffset + 1; + if (utf) while ((ptr[startoffset] & 0xc0) == 0x80) startoffset++; + } + + /* If the current match ended past the end of the line (only possible + in multiline mode), we must move on to the line in which it did end + before searching for more matches. */ + + while (startoffset > linelength) + { + ptr += linelength + endlinelength; + filepos += (int)(linelength + endlinelength); + linenumber++; + startoffset -= (int)(linelength + endlinelength); + t = end_of_line(ptr, endptr, &endlinelength); + linelength = t - ptr - endlinelength; + length = (PCRE2_SIZE)(endptr - ptr); + } + + goto ONLY_MATCHING_RESTART; + } + } + + /* This is the default case when none of the above options is set. We print + the matching lines(s), possibly preceded and/or followed by other lines of + context. */ + + else + { + /* See if there is a requirement to print some "after" lines from a + previous match. We never print any overlaps. */ + + if (after_context > 0 && lastmatchnumber > 0) + { + int ellength; + int linecount = 0; + char *p = lastmatchrestart; + + while (p < ptr && linecount < after_context) + { + p = end_of_line(p, ptr, &ellength); + linecount++; + } + + /* It is important to advance lastmatchrestart during this printing so + that it interacts correctly with any "before" printing below. Print + each line's data using fwrite() in case there are binary zeroes. */ + + while (lastmatchrestart < p) + { + char *pp = lastmatchrestart; + if (printname != NULL) fprintf(stdout, "%s-", printname); + if (number) fprintf(stdout, "%lu-", lastmatchnumber++); + pp = end_of_line(pp, endptr, &ellength); + FWRITE_IGNORE(lastmatchrestart, 1, pp - lastmatchrestart, stdout); + lastmatchrestart = pp; + } + if (lastmatchrestart != ptr) hyphenpending = TRUE; + } + + /* If there were non-contiguous lines printed above, insert hyphens. */ + + if (hyphenpending) + { + fprintf(stdout, "--" STDOUT_NL); + hyphenpending = FALSE; + hyphenprinted = TRUE; + } + + /* See if there is a requirement to print some "before" lines for this + match. Again, don't print overlaps. */ + + if (before_context > 0) + { + int linecount = 0; + char *p = ptr; + + while (p > main_buffer && (lastmatchnumber == 0 || p > lastmatchrestart) && + linecount < before_context) + { + linecount++; + p = previous_line(p, main_buffer); + } + + if (lastmatchnumber > 0 && p > lastmatchrestart && !hyphenprinted) + fprintf(stdout, "--" STDOUT_NL); + + while (p < ptr) + { + int ellength; + char *pp = p; + if (printname != NULL) fprintf(stdout, "%s-", printname); + if (number) fprintf(stdout, "%lu-", linenumber - linecount--); + pp = end_of_line(pp, endptr, &ellength); + FWRITE_IGNORE(p, 1, pp - p, stdout); + p = pp; + } + } + + /* Now print the matching line(s); ensure we set hyphenpending at the end + of the file if any context lines are being output. */ + + if (after_context > 0 || before_context > 0) + endhyphenpending = TRUE; + + if (printname != NULL) fprintf(stdout, "%s:", printname); + if (number) fprintf(stdout, "%lu:", linenumber); + + /* This extra option, for Jeffrey Friedl's debugging requirements, + replaces the matched string, or a specific captured string if it exists, + with X. When this happens, colouring is ignored. */ + +#ifdef JFRIEDL_DEBUG + if (S_arg >= 0 && S_arg < mrc) + { + int first = S_arg * 2; + int last = first + 1; + FWRITE_IGNORE(ptr, 1, offsets[first], stdout); + fprintf(stdout, "X"); + FWRITE_IGNORE(ptr + offsets[last], 1, linelength - offsets[last], stdout); + } + else +#endif + + /* In multiline mode, or if colouring, we have to split the line(s) up + and search for further matches, but not of course if the line is a + non-match. In multiline mode this is necessary in case there is another + match that spans the end of the current line. When colouring we want to + colour all matches. */ + + if ((multiline || do_colour) && !invert) + { + int plength; + PCRE2_SIZE endprevious; + + /* The use of \K may make the end offset earlier than the start. In + this situation, swap them round. */ + + if (offsets[0] > offsets[1]) + { + PCRE2_SIZE temp = offsets[0]; + offsets[0] = offsets[1]; + offsets[1] = temp; + } + + FWRITE_IGNORE(ptr, 1, offsets[0], stdout); + print_match(ptr + offsets[0], offsets[1] - offsets[0]); + + for (;;) + { + PCRE2_SIZE oldstartoffset = pcre2_get_startchar(match_data); + + endprevious = offsets[1]; + startoffset = endprevious; /* Advance after previous match. */ + + /* If the pattern contained a lookbehind that included \K, it is + possible that the end of the match might be at or before the actual + starting offset we have just used. In this case, start one character + further on. */ + + if (startoffset <= oldstartoffset) + { + startoffset = oldstartoffset + 1; + if (utf) while ((ptr[startoffset] & 0xc0) == 0x80) startoffset++; + } + + /* If the current match ended past the end of the line (only possible + in multiline mode), we must move on to the line in which it did end + before searching for more matches. Because the PCRE2_FIRSTLINE option + is set, the start of the match will always be before the first + newline sequence. */ + + while (startoffset > linelength + endlinelength) + { + ptr += linelength + endlinelength; + filepos += (int)(linelength + endlinelength); + linenumber++; + startoffset -= (int)(linelength + endlinelength); + endprevious -= (int)(linelength + endlinelength); + t = end_of_line(ptr, endptr, &endlinelength); + linelength = t - ptr - endlinelength; + length = (PCRE2_SIZE)(endptr - ptr); + } + + /* If startoffset is at the exact end of the line it means this + complete line was the final part of the match, so there is nothing + more to do. */ + + if (startoffset == linelength + endlinelength) break; + + /* Otherwise, run a match from within the final line, and if found, + loop for any that may follow. */ + + if (!match_patterns(ptr, length, options, startoffset, &mrc)) break; + + /* The use of \K may make the end offset earlier than the start. In + this situation, swap them round. */ + + if (offsets[0] > offsets[1]) + { + PCRE2_SIZE temp = offsets[0]; + offsets[0] = offsets[1]; + offsets[1] = temp; + } + + FWRITE_IGNORE(ptr + endprevious, 1, offsets[0] - endprevious, stdout); + print_match(ptr + offsets[0], offsets[1] - offsets[0]); + } + + /* In multiline mode, we may have already printed the complete line + and its line-ending characters (if they matched the pattern), so there + may be no more to print. */ + + plength = (int)((linelength + endlinelength) - endprevious); + if (plength > 0) FWRITE_IGNORE(ptr + endprevious, 1, plength, stdout); + } + + /* Not colouring or multiline; no need to search for further matches. */ + + else FWRITE_IGNORE(ptr, 1, linelength + endlinelength, stdout); + } + + /* End of doing what has to be done for a match. If --line-buffered was + given, flush the output. */ + + if (line_buffered) fflush(stdout); + rc = 0; /* Had some success */ + + /* Remember where the last match happened for after_context. We remember + where we are about to restart, and that line's number. */ + + lastmatchrestart = ptr + linelength + endlinelength; + lastmatchnumber = linenumber + 1; + } + + /* For a match in multiline inverted mode (which of course did not cause + anything to be printed), we have to move on to the end of the match before + proceeding. */ + + if (multiline && invert && match) + { + int ellength; + char *endmatch = ptr + offsets[1]; + t = ptr; + while (t < endmatch) + { + t = end_of_line(t, endptr, &ellength); + if (t <= endmatch) linenumber++; else break; + } + endmatch = end_of_line(endmatch, endptr, &ellength); + linelength = endmatch - ptr - ellength; + } + + /* Advance to after the newline and increment the line number. The file + offset to the current line is maintained in filepos. */ + + END_ONE_MATCH: + ptr += linelength + endlinelength; + filepos += (int)(linelength + endlinelength); + linenumber++; + + /* If input is line buffered, and the buffer is not yet full, read another + line and add it into the buffer. */ + + if (input_line_buffered && bufflength < (PCRE2_SIZE)bufsize) + { + int add = read_one_line(ptr, bufsize - (int)(ptr - main_buffer), in); + bufflength += add; + endptr += add; + } + + /* If we haven't yet reached the end of the file (the buffer is full), and + the current point is in the top 1/3 of the buffer, slide the buffer down by + 1/3 and refill it. Before we do this, if some unprinted "after" lines are + about to be lost, print them. */ + + if (bufflength >= (PCRE2_SIZE)bufsize && ptr > main_buffer + 2*bufthird) + { + if (after_context > 0 && + lastmatchnumber > 0 && + lastmatchrestart < main_buffer + bufthird) + { + do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname); + lastmatchnumber = 0; /* Indicates no after lines pending */ + } + + /* Now do the shuffle */ + + (void)memmove(main_buffer, main_buffer + bufthird, 2*bufthird); + ptr -= bufthird; + + bufflength = 2*bufthird + fill_buffer(handle, frtype, + main_buffer + 2*bufthird, bufthird, input_line_buffered); + endptr = main_buffer + bufflength; + + /* Adjust any last match point */ + + if (lastmatchnumber > 0) lastmatchrestart -= bufthird; + } + } /* Loop through the whole file */ + +/* End of file; print final "after" lines if wanted; do_after_lines sets +hyphenpending if it prints something. */ + +if (only_matching_count == 0 && !(count_only|show_total_count)) + { + do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname); + hyphenpending |= endhyphenpending; + } + +/* Print the file name if we are looking for those without matches and there +were none. If we found a match, we won't have got this far. */ + +if (filenames == FN_NOMATCH_ONLY) + { + fprintf(stdout, "%s" STDOUT_NL, printname); + return 0; + } + +/* Print the match count if wanted */ + +if (count_only && !quiet) + { + if (count > 0 || !omit_zero_count) + { + if (printname != NULL && filenames != FN_NONE) + fprintf(stdout, "%s:", printname); + fprintf(stdout, "%lu" STDOUT_NL, count); + counts_printed++; + } + } + +total_count += count; /* Can be set without count_only */ +return rc; +} + + + +/************************************************* +* Grep a file or recurse into a directory * +*************************************************/ + +/* Given a path name, if it's a directory, scan all the files if we are +recursing; if it's a file, grep it. + +Arguments: + pathname the path to investigate + dir_recurse TRUE if recursing is wanted (-r or -drecurse) + only_one_at_top TRUE if the path is the only one at toplevel + +Returns: -1 the file/directory was skipped + 0 if there was at least one match + 1 if there were no matches + 2 there was some kind of error + +However, file opening failures are suppressed if "silent" is set. +*/ + +static int +grep_or_recurse(char *pathname, BOOL dir_recurse, BOOL only_one_at_top) +{ +int rc = 1; +int frtype; +void *handle; +char *lastcomp; +FILE *in = NULL; /* Ensure initialized */ + +#ifdef SUPPORT_LIBZ +gzFile ingz = NULL; +#endif + +#ifdef SUPPORT_LIBBZ2 +BZFILE *inbz2 = NULL; +#endif + +#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 +int pathlen; +#endif + +#if defined NATIVE_ZOS +int zos_type; +FILE *zos_test_file; +#endif + +/* If the file name is "-" we scan stdin */ + +if (strcmp(pathname, "-") == 0) + { + return pcre2grep(stdin, FR_PLAIN, stdin_name, + (filenames > FN_DEFAULT || (filenames == FN_DEFAULT && !only_one_at_top))? + stdin_name : NULL); + } + +/* Inclusion and exclusion: --include-dir and --exclude-dir apply only to +directories, whereas --include and --exclude apply to everything else. The test +is against the final component of the path. */ + +lastcomp = strrchr(pathname, FILESEP); +lastcomp = (lastcomp == NULL)? pathname : lastcomp + 1; + +/* If the file is a directory, skip if not recursing or if explicitly excluded. +Otherwise, scan the directory and recurse for each path within it. The scanning +code is localized so it can be made system-specific. */ + + +/* For z/OS, determine the file type. */ + +#if defined NATIVE_ZOS +zos_test_file = fopen(pathname,"rb"); + +if (zos_test_file == NULL) + { + if (!silent) fprintf(stderr, "pcre2grep: failed to test next file %s\n", + pathname, strerror(errno)); + return -1; + } +zos_type = identifyzosfiletype (zos_test_file); +fclose (zos_test_file); + +/* Handle a PDS in separate code */ + +if (zos_type == __ZOS_PDS || zos_type == __ZOS_PDSE) + { + return travelonpdsdir (pathname, only_one_at_top); + } + +/* Deal with regular files in the normal way below. These types are: + zos_type == __ZOS_PDS_MEMBER + zos_type == __ZOS_PS + zos_type == __ZOS_VSAM_KSDS + zos_type == __ZOS_VSAM_ESDS + zos_type == __ZOS_VSAM_RRDS +*/ + +/* Handle a z/OS directory using common code. */ + +else if (zos_type == __ZOS_HFS) + { +#endif /* NATIVE_ZOS */ + + +/* Handle directories: common code for all OS */ + +if (isdirectory(pathname)) + { + if (dee_action == dee_SKIP || + !test_incexc(lastcomp, include_dir_patterns, exclude_dir_patterns)) + return -1; + + if (dee_action == dee_RECURSE) + { + char buffer[FNBUFSIZ]; + char *nextfile; + directory_type *dir = opendirectory(pathname); + + if (dir == NULL) + { + if (!silent) + fprintf(stderr, "pcre2grep: Failed to open directory %s: %s\n", pathname, + strerror(errno)); + return 2; + } + + while ((nextfile = readdirectory(dir)) != NULL) + { + int frc; + int fnlength = strlen(pathname) + strlen(nextfile) + 2; + if (fnlength > FNBUFSIZ) + { + fprintf(stderr, "pcre2grep: recursive filename is too long\n"); + rc = 2; + break; + } + sprintf(buffer, "%s%c%s", pathname, FILESEP, nextfile); + frc = grep_or_recurse(buffer, dir_recurse, FALSE); + if (frc > 1) rc = frc; + else if (frc == 0 && rc == 1) rc = 0; + } + + closedirectory(dir); + return rc; + } + } + +#ifdef WIN32 +if (iswild(pathname)) + { + char buffer[1024]; + char *nextfile; + char *name; + directory_type *dir = opendirectory(pathname); + + if (dir == NULL) + return 0; + + for (nextfile = name = pathname; *nextfile != 0; nextfile++) + if (*nextfile == '/' || *nextfile == '\\') + name = nextfile + 1; + *name = 0; + + while ((nextfile = readdirectory(dir)) != NULL) + { + int frc; + sprintf(buffer, "%.512s%.128s", pathname, nextfile); + frc = grep_or_recurse(buffer, dir_recurse, FALSE); + if (frc > 1) rc = frc; + else if (frc == 0 && rc == 1) rc = 0; + } + + closedirectory(dir); + return rc; + } +#endif + +#if defined NATIVE_ZOS + } +#endif + +/* If the file is not a directory, check for a regular file, and if it is not, +skip it if that's been requested. Otherwise, check for an explicit inclusion or +exclusion. */ + +else if ( +#if defined NATIVE_ZOS + (zos_type == __ZOS_NOFILE && DEE_action == DEE_SKIP) || +#else /* all other OS */ + (!isregfile(pathname) && DEE_action == DEE_SKIP) || +#endif + !test_incexc(lastcomp, include_patterns, exclude_patterns)) + return -1; /* File skipped */ + +/* Control reaches here if we have a regular file, or if we have a directory +and recursion or skipping was not requested, or if we have anything else and +skipping was not requested. The scan proceeds. If this is the first and only +argument at top level, we don't show the file name, unless we are only showing +the file name, or the filename was forced (-H). */ + +#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 +pathlen = (int)(strlen(pathname)); +#endif + +/* Open using zlib if it is supported and the file name ends with .gz. */ + +#ifdef SUPPORT_LIBZ +if (pathlen > 3 && strcmp(pathname + pathlen - 3, ".gz") == 0) + { + ingz = gzopen(pathname, "rb"); + if (ingz == NULL) + { + if (!silent) + fprintf(stderr, "pcre2grep: Failed to open %s: %s\n", pathname, + strerror(errno)); + return 2; + } + handle = (void *)ingz; + frtype = FR_LIBZ; + } +else +#endif + +/* Otherwise open with bz2lib if it is supported and the name ends with .bz2. */ + +#ifdef SUPPORT_LIBBZ2 +if (pathlen > 4 && strcmp(pathname + pathlen - 4, ".bz2") == 0) + { + inbz2 = BZ2_bzopen(pathname, "rb"); + handle = (void *)inbz2; + frtype = FR_LIBBZ2; + } +else +#endif + +/* Otherwise use plain fopen(). The label is so that we can come back here if +an attempt to read a .bz2 file indicates that it really is a plain file. */ + +#ifdef SUPPORT_LIBBZ2 +PLAIN_FILE: +#endif + { + in = fopen(pathname, "rb"); + handle = (void *)in; + frtype = FR_PLAIN; + } + +/* All the opening methods return errno when they fail. */ + +if (handle == NULL) + { + if (!silent) + fprintf(stderr, "pcre2grep: Failed to open %s: %s\n", pathname, + strerror(errno)); + return 2; + } + +/* Now grep the file */ + +rc = pcre2grep(handle, frtype, pathname, (filenames > FN_DEFAULT || + (filenames == FN_DEFAULT && !only_one_at_top))? pathname : NULL); + +/* Close in an appropriate manner. */ + +#ifdef SUPPORT_LIBZ +if (frtype == FR_LIBZ) + gzclose(ingz); +else +#endif + +/* If it is a .bz2 file and the result is 3, it means that the first attempt to +read failed. If the error indicates that the file isn't in fact bzipped, try +again as a normal file. */ + +#ifdef SUPPORT_LIBBZ2 +if (frtype == FR_LIBBZ2) + { + if (rc == 3) + { + int errnum; + const char *err = BZ2_bzerror(inbz2, &errnum); + if (errnum == BZ_DATA_ERROR_MAGIC) + { + BZ2_bzclose(inbz2); + goto PLAIN_FILE; + } + else if (!silent) + fprintf(stderr, "pcre2grep: Failed to read %s using bzlib: %s\n", + pathname, err); + rc = 2; /* The normal "something went wrong" code */ + } + BZ2_bzclose(inbz2); + } +else +#endif + +/* Normal file close */ + +fclose(in); + +/* Pass back the yield from pcre2grep(). */ + +return rc; +} + + + +/************************************************* +* Handle a single-letter, no data option * +*************************************************/ + +static int +handle_option(int letter, int options) +{ +switch(letter) + { + case N_FOFFSETS: file_offsets = TRUE; break; + case N_HELP: help(); pcre2grep_exit(0); break; /* Stops compiler warning */ + case N_LBUFFER: line_buffered = TRUE; break; + case N_LOFFSETS: line_offsets = number = TRUE; break; + case N_NOJIT: use_jit = FALSE; break; + case 'a': binary_files = BIN_TEXT; break; + case 'c': count_only = TRUE; break; + case 'F': options |= PCRE2_LITERAL; break; + case 'H': filenames = FN_FORCE; break; + case 'I': binary_files = BIN_NOMATCH; break; + case 'h': filenames = FN_NONE; break; + case 'i': options |= PCRE2_CASELESS; break; + case 'l': omit_zero_count = TRUE; filenames = FN_MATCH_ONLY; break; + case 'L': filenames = FN_NOMATCH_ONLY; break; + case 'M': multiline = TRUE; options |= PCRE2_MULTILINE|PCRE2_FIRSTLINE; break; + case 'n': number = TRUE; break; + + case 'o': + only_matching_last = add_number(0, only_matching_last); + if (only_matching == NULL) only_matching = only_matching_last; + break; + + case 'q': quiet = TRUE; break; + case 'r': dee_action = dee_RECURSE; break; + case 's': silent = TRUE; break; + case 't': show_total_count = TRUE; break; + case 'u': options |= PCRE2_UTF; utf = TRUE; break; + case 'v': invert = TRUE; break; + case 'w': extra_options |= PCRE2_EXTRA_MATCH_WORD; break; + case 'x': extra_options |= PCRE2_EXTRA_MATCH_LINE; break; + + case 'V': + { + unsigned char buffer[128]; + (void)pcre2_config(PCRE2_CONFIG_VERSION, buffer); + fprintf(stdout, "pcre2grep version %s" STDOUT_NL, buffer); + } + pcre2grep_exit(0); + break; + + default: + fprintf(stderr, "pcre2grep: Unknown option -%c\n", letter); + pcre2grep_exit(usage(2)); + } + +return options; +} + + + +/************************************************* +* Construct printed ordinal * +*************************************************/ + +/* This turns a number into "1st", "3rd", etc. */ + +static char * +ordin(int n) +{ +static char buffer[14]; +char *p = buffer; +sprintf(p, "%d", n); +while (*p != 0) p++; +n %= 100; +if (n >= 11 && n <= 13) n = 0; +switch (n%10) + { + case 1: strcpy(p, "st"); break; + case 2: strcpy(p, "nd"); break; + case 3: strcpy(p, "rd"); break; + default: strcpy(p, "th"); break; + } +return buffer; +} + + + +/************************************************* +* Compile a single pattern * +*************************************************/ + +/* Do nothing if the pattern has already been compiled. This is the case for +include/exclude patterns read from a file. + +When the -F option has been used, each "pattern" may be a list of strings, +separated by line breaks. They will be matched literally. We split such a +string and compile the first substring, inserting an additional block into the +pattern chain. + +Arguments: + p points to the pattern block + options the PCRE options + fromfile TRUE if the pattern was read from a file + fromtext file name or identifying text (e.g. "include") + count 0 if this is the only command line pattern, or + number of the command line pattern, or + linenumber for a pattern from a file + +Returns: TRUE on success, FALSE after an error +*/ + +static BOOL +compile_pattern(patstr *p, int options, int fromfile, const char *fromtext, + int count) +{ +char *ps; +int errcode; +PCRE2_SIZE patlen, erroffset; +PCRE2_UCHAR errmessbuffer[ERRBUFSIZ]; + +if (p->compiled != NULL) return TRUE; +ps = p->string; +patlen = p->length; + +if ((options & PCRE2_LITERAL) != 0) + { + int ellength; + char *eop = ps + patlen; + char *pe = end_of_line(ps, eop, &ellength); + + if (ellength != 0) + { + patlen = pe - ps - ellength; + if (add_pattern(pe, p->length-patlen-ellength, p) == NULL) return FALSE; + } + } + +p->compiled = pcre2_compile((PCRE2_SPTR)ps, patlen, options, &errcode, + &erroffset, compile_context); + +/* Handle successful compile. Try JIT-compiling if supported and enabled. We +ignore any JIT compiler errors, relying falling back to interpreting if +anything goes wrong with JIT. */ + +if (p->compiled != NULL) + { +#ifdef SUPPORT_PCRE2GREP_JIT + if (use_jit) (void)pcre2_jit_compile(p->compiled, PCRE2_JIT_COMPLETE); +#endif + return TRUE; + } + +/* Handle compile errors */ + +if (erroffset > patlen) erroffset = patlen; +pcre2_get_error_message(errcode, errmessbuffer, sizeof(errmessbuffer)); + +if (fromfile) + { + fprintf(stderr, "pcre2grep: Error in regex in line %d of %s " + "at offset %d: %s\n", count, fromtext, (int)erroffset, errmessbuffer); + } +else + { + if (count == 0) + fprintf(stderr, "pcre2grep: Error in %s regex at offset %d: %s\n", + fromtext, (int)erroffset, errmessbuffer); + else + fprintf(stderr, "pcre2grep: Error in %s %s regex at offset %d: %s\n", + ordin(count), fromtext, (int)erroffset, errmessbuffer); + } + +return FALSE; +} + + + +/************************************************* +* Read and compile a file of patterns * +*************************************************/ + +/* This is used for --filelist, --include-from, and --exclude-from. + +Arguments: + name the name of the file; "-" is stdin + patptr pointer to the pattern chain anchor + patlastptr pointer to the last pattern pointer + +Returns: TRUE if all went well +*/ + +static BOOL +read_pattern_file(char *name, patstr **patptr, patstr **patlastptr) +{ +int linenumber = 0; +PCRE2_SIZE patlen; +FILE *f; +const char *filename; +char buffer[MAXPATLEN+20]; + +if (strcmp(name, "-") == 0) + { + f = stdin; + filename = stdin_name; + } +else + { + f = fopen(name, "r"); + if (f == NULL) + { + fprintf(stderr, "pcre2grep: Failed to open %s: %s\n", name, strerror(errno)); + return FALSE; + } + filename = name; + } + +while ((patlen = read_one_line(buffer, sizeof(buffer), f)) > 0) + { + while (patlen > 0 && isspace((unsigned char)(buffer[patlen-1]))) patlen--; + linenumber++; + if (patlen == 0) continue; /* Skip blank lines */ + + /* Note: this call to add_pattern() puts a pointer to the local variable + "buffer" into the pattern chain. However, that pointer is used only when + compiling the pattern, which happens immediately below, so we flatten it + afterwards, as a precaution against any later code trying to use it. */ + + *patlastptr = add_pattern(buffer, patlen, *patlastptr); + if (*patlastptr == NULL) + { + if (f != stdin) fclose(f); + return FALSE; + } + if (*patptr == NULL) *patptr = *patlastptr; + + /* This loop is needed because compiling a "pattern" when -F is set may add + on additional literal patterns if the original contains a newline. In the + common case, it never will, because read_one_line() stops at a newline. + However, the -N option can be used to give pcre2grep a different newline + setting. */ + + for(;;) + { + if (!compile_pattern(*patlastptr, pcre2_options, TRUE, filename, + linenumber)) + { + if (f != stdin) fclose(f); + return FALSE; + } + (*patlastptr)->string = NULL; /* Insurance */ + if ((*patlastptr)->next == NULL) break; + *patlastptr = (*patlastptr)->next; + } + } + +if (f != stdin) fclose(f); +return TRUE; +} + + + +/************************************************* +* Main program * +*************************************************/ + +/* Returns 0 if something matched, 1 if nothing matched, 2 after an error. */ + +int +main(int argc, char **argv) +{ +int i, j; +int rc = 1; +BOOL only_one_at_top; +patstr *cp; +fnstr *fn; +const char *locale_from = "--locale"; + +#ifdef SUPPORT_PCRE2GREP_JIT +pcre2_jit_stack *jit_stack = NULL; +#endif + +/* In Windows, stdout is set up as a text stream, which means that \n is +converted to \r\n. This causes output lines that are copied from the input to +change from ....\r\n to ....\r\r\n, which is not right. We therefore ensure +that stdout is a binary stream. Note that this means all other output to stdout +must use STDOUT_NL to terminate lines. */ + +#ifdef WIN32 +_setmode(_fileno(stdout), _O_BINARY); +#endif + +/* Set up a default compile and match contexts and a match data block. */ + +compile_context = pcre2_compile_context_create(NULL); +match_context = pcre2_match_context_create(NULL); +match_data = pcre2_match_data_create(OFFSET_SIZE, NULL); +offsets = pcre2_get_ovector_pointer(match_data); + +/* If string (script) callouts are supported, set up the callout processing +function. */ + +#ifdef SUPPORT_PCRE2GREP_CALLOUT +pcre2_set_callout(match_context, pcre2grep_callout, NULL); +#endif + +/* Process the options */ + +for (i = 1; i < argc; i++) + { + option_item *op = NULL; + char *option_data = (char *)""; /* default to keep compiler happy */ + BOOL longop; + BOOL longopwasequals = FALSE; + + if (argv[i][0] != '-') break; + + /* If we hit an argument that is just "-", it may be a reference to STDIN, + but only if we have previously had -e or -f to define the patterns. */ + + if (argv[i][1] == 0) + { + if (pattern_files != NULL || patterns != NULL) break; + else pcre2grep_exit(usage(2)); + } + + /* Handle a long name option, or -- to terminate the options */ + + if (argv[i][1] == '-') + { + char *arg = argv[i] + 2; + char *argequals = strchr(arg, '='); + + if (*arg == 0) /* -- terminates options */ + { + i++; + break; /* out of the options-handling loop */ + } + + longop = TRUE; + + /* Some long options have data that follows after =, for example file=name. + Some options have variations in the long name spelling: specifically, we + allow "regexp" because GNU grep allows it, though I personally go along + with Jeffrey Friedl and Larry Wall in preferring "regex" without the "p". + These options are entered in the table as "regex(p)". Options can be in + both these categories. */ + + for (op = optionlist; op->one_char != 0; op++) + { + char *opbra = strchr(op->long_name, '('); + char *equals = strchr(op->long_name, '='); + + /* Handle options with only one spelling of the name */ + + if (opbra == NULL) /* Does not contain '(' */ + { + if (equals == NULL) /* Not thing=data case */ + { + if (strcmp(arg, op->long_name) == 0) break; + } + else /* Special case xxx=data */ + { + int oplen = (int)(equals - op->long_name); + int arglen = (argequals == NULL)? + (int)strlen(arg) : (int)(argequals - arg); + if (oplen == arglen && strncmp(arg, op->long_name, oplen) == 0) + { + option_data = arg + arglen; + if (*option_data == '=') + { + option_data++; + longopwasequals = TRUE; + } + break; + } + } + } + + /* Handle options with an alternate spelling of the name */ + + else + { + char buff1[24]; + char buff2[24]; + int ret; + + int baselen = (int)(opbra - op->long_name); + int fulllen = (int)(strchr(op->long_name, ')') - op->long_name + 1); + int arglen = (argequals == NULL || equals == NULL)? + (int)strlen(arg) : (int)(argequals - arg); + + if ((ret = snprintf(buff1, sizeof(buff1), "%.*s", baselen, op->long_name), + ret < 0 || ret > (int)sizeof(buff1)) || + (ret = snprintf(buff2, sizeof(buff2), "%s%.*s", buff1, + fulllen - baselen - 2, opbra + 1), + ret < 0 || ret > (int)sizeof(buff2))) + { + fprintf(stderr, "pcre2grep: Buffer overflow when parsing %s option\n", + op->long_name); + pcre2grep_exit(2); + } + + if (strncmp(arg, buff1, arglen) == 0 || + strncmp(arg, buff2, arglen) == 0) + { + if (equals != NULL && argequals != NULL) + { + option_data = argequals; + if (*option_data == '=') + { + option_data++; + longopwasequals = TRUE; + } + } + break; + } + } + } + + if (op->one_char == 0) + { + fprintf(stderr, "pcre2grep: Unknown option %s\n", argv[i]); + pcre2grep_exit(usage(2)); + } + } + + /* Jeffrey Friedl's debugging harness uses these additional options which + are not in the right form for putting in the option table because they use + only one hyphen, yet are more than one character long. By putting them + separately here, they will not get displayed as part of the help() output, + but I don't think Jeffrey will care about that. */ + +#ifdef JFRIEDL_DEBUG + else if (strcmp(argv[i], "-pre") == 0) { + jfriedl_prefix = argv[++i]; + continue; + } else if (strcmp(argv[i], "-post") == 0) { + jfriedl_postfix = argv[++i]; + continue; + } else if (strcmp(argv[i], "-XT") == 0) { + sscanf(argv[++i], "%d", &jfriedl_XT); + continue; + } else if (strcmp(argv[i], "-XR") == 0) { + sscanf(argv[++i], "%d", &jfriedl_XR); + continue; + } +#endif + + + /* One-char options; many that have no data may be in a single argument; we + continue till we hit the last one or one that needs data. */ + + else + { + char *s = argv[i] + 1; + longop = FALSE; + + while (*s != 0) + { + for (op = optionlist; op->one_char != 0; op++) + { + if (*s == op->one_char) break; + } + if (op->one_char == 0) + { + fprintf(stderr, "pcre2grep: Unknown option letter '%c' in \"%s\"\n", + *s, argv[i]); + pcre2grep_exit(usage(2)); + } + + option_data = s+1; + + /* Break out if this is the last character in the string; it's handled + below like a single multi-char option. */ + + if (*option_data == 0) break; + + /* Check for a single-character option that has data: OP_OP_NUMBER(S) + are used for ones that either have a numerical number or defaults, i.e. + the data is optional. If a digit follows, there is data; if not, carry on + with other single-character options in the same string. */ + + if (op->type == OP_OP_NUMBER || op->type == OP_OP_NUMBERS) + { + if (isdigit((unsigned char)s[1])) break; + } + else /* Check for an option with data */ + { + if (op->type != OP_NODATA) break; + } + + /* Handle a single-character option with no data, then loop for the + next character in the string. */ + + pcre2_options = handle_option(*s++, pcre2_options); + } + } + + /* At this point we should have op pointing to a matched option. If the type + is NO_DATA, it means that there is no data, and the option might set + something in the PCRE options. */ + + if (op->type == OP_NODATA) + { + pcre2_options = handle_option(op->one_char, pcre2_options); + continue; + } + + /* If the option type is OP_OP_STRING or OP_OP_NUMBER(S), it's an option that + either has a value or defaults to something. It cannot have data in a + separate item. At the moment, the only such options are "colo(u)r", + "only-matching", and Jeffrey Friedl's special -S debugging option. */ + + if (*option_data == 0 && + (op->type == OP_OP_STRING || op->type == OP_OP_NUMBER || + op->type == OP_OP_NUMBERS)) + { + switch (op->one_char) + { + case N_COLOUR: + colour_option = "auto"; + break; + + case 'o': + only_matching_last = add_number(0, only_matching_last); + if (only_matching == NULL) only_matching = only_matching_last; + break; + +#ifdef JFRIEDL_DEBUG + case 'S': + S_arg = 0; + break; +#endif + } + continue; + } + + /* Otherwise, find the data string for the option. */ + + if (*option_data == 0) + { + if (i >= argc - 1 || longopwasequals) + { + fprintf(stderr, "pcre2grep: Data missing after %s\n", argv[i]); + pcre2grep_exit(usage(2)); + } + option_data = argv[++i]; + } + + /* If the option type is OP_OP_NUMBERS, the value is a number that is to be + added to a chain of numbers. */ + + if (op->type == OP_OP_NUMBERS) + { + unsigned long int n = decode_number(option_data, op, longop); + omdatastr *omd = (omdatastr *)op->dataptr; + *(omd->lastptr) = add_number((int)n, *(omd->lastptr)); + if (*(omd->anchor) == NULL) *(omd->anchor) = *(omd->lastptr); + } + + /* If the option type is OP_PATLIST, it's the -e option, or one of the + include/exclude options, which can be called multiple times to create lists + of patterns. */ + + else if (op->type == OP_PATLIST) + { + patdatastr *pd = (patdatastr *)op->dataptr; + *(pd->lastptr) = add_pattern(option_data, (PCRE2_SIZE)strlen(option_data), + *(pd->lastptr)); + if (*(pd->lastptr) == NULL) goto EXIT2; + if (*(pd->anchor) == NULL) *(pd->anchor) = *(pd->lastptr); + } + + /* If the option type is OP_FILELIST, it's one of the options that names a + file. */ + + else if (op->type == OP_FILELIST) + { + fndatastr *fd = (fndatastr *)op->dataptr; + fn = (fnstr *)malloc(sizeof(fnstr)); + if (fn == NULL) + { + fprintf(stderr, "pcre2grep: malloc failed\n"); + goto EXIT2; + } + fn->next = NULL; + fn->name = option_data; + if (*(fd->anchor) == NULL) + *(fd->anchor) = fn; + else + (*(fd->lastptr))->next = fn; + *(fd->lastptr) = fn; + } + + /* Handle OP_BINARY_FILES */ + + else if (op->type == OP_BINFILES) + { + if (strcmp(option_data, "binary") == 0) + binary_files = BIN_BINARY; + else if (strcmp(option_data, "without-match") == 0) + binary_files = BIN_NOMATCH; + else if (strcmp(option_data, "text") == 0) + binary_files = BIN_TEXT; + else + { + fprintf(stderr, "pcre2grep: unknown value \"%s\" for binary-files\n", + option_data); + pcre2grep_exit(usage(2)); + } + } + + /* Otherwise, deal with a single string or numeric data value. */ + + else if (op->type != OP_NUMBER && op->type != OP_U32NUMBER && + op->type != OP_OP_NUMBER && op->type != OP_SIZE) + { + *((char **)op->dataptr) = option_data; + } + else + { + unsigned long int n = decode_number(option_data, op, longop); + if (op->type == OP_U32NUMBER) *((uint32_t *)op->dataptr) = n; + else if (op->type == OP_SIZE) *((PCRE2_SIZE *)op->dataptr) = n; + else *((int *)op->dataptr) = n; + } + } + +/* Options have been decoded. If -C was used, its value is used as a default +for -A and -B. */ + +if (both_context > 0) + { + if (after_context == 0) after_context = both_context; + if (before_context == 0) before_context = both_context; + } + +/* Only one of --only-matching, --output, --file-offsets, or --line-offsets is +permitted. They display, each in their own way, only the data that has matched. +*/ + +only_matching_count = (only_matching != NULL) + (output_text != NULL) + + file_offsets + line_offsets; + +if (only_matching_count > 1) + { + fprintf(stderr, "pcre2grep: Cannot mix --only-matching, --output, " + "--file-offsets and/or --line-offsets\n"); + pcre2grep_exit(usage(2)); + } + +/* Check the text supplied to --output for errors. */ + +if (output_text != NULL && + !syntax_check_output_text((PCRE2_SPTR)output_text, FALSE)) + goto EXIT2; + +/* Put limits into the match data block. */ + +if (heap_limit != PCRE2_UNSET) pcre2_set_heap_limit(match_context, heap_limit); +if (match_limit > 0) pcre2_set_match_limit(match_context, match_limit); +if (depth_limit > 0) pcre2_set_depth_limit(match_context, depth_limit); + +/* If a locale has not been provided as an option, see if the LC_CTYPE or +LC_ALL environment variable is set, and if so, use it. */ + +if (locale == NULL) + { + locale = getenv("LC_ALL"); + locale_from = "LC_ALL"; + } + +if (locale == NULL) + { + locale = getenv("LC_CTYPE"); + locale_from = "LC_CTYPE"; + } + +/* If a locale is set, use it to generate the tables the PCRE needs. Passing +NULL to pcre2_maketables() means that malloc() is used to get the memory. */ + +if (locale != NULL) + { + if (setlocale(LC_CTYPE, locale) == NULL) + { + fprintf(stderr, "pcre2grep: Failed to set locale %s (obtained from %s)\n", + locale, locale_from); + goto EXIT2; + } + character_tables = pcre2_maketables(NULL); + pcre2_set_character_tables(compile_context, character_tables); + } + +/* Sort out colouring */ + +if (colour_option != NULL && strcmp(colour_option, "never") != 0) + { + if (strcmp(colour_option, "always") == 0) +#ifdef WIN32 + do_ansi = !is_stdout_tty(), +#endif + do_colour = TRUE; + else if (strcmp(colour_option, "auto") == 0) do_colour = is_stdout_tty(); + else + { + fprintf(stderr, "pcre2grep: Unknown colour setting \"%s\"\n", + colour_option); + goto EXIT2; + } + if (do_colour) + { + char *cs = getenv("PCRE2GREP_COLOUR"); + if (cs == NULL) cs = getenv("PCRE2GREP_COLOR"); + if (cs == NULL) cs = getenv("PCREGREP_COLOUR"); + if (cs == NULL) cs = getenv("PCREGREP_COLOR"); + if (cs == NULL) cs = parse_grep_colors(getenv("GREP_COLORS")); + if (cs == NULL) cs = getenv("GREP_COLOR"); + if (cs != NULL) + { + if (strspn(cs, ";0123456789") == strlen(cs)) colour_string = cs; + } +#ifdef WIN32 + init_colour_output(); +#endif + } + } + +/* Sort out a newline setting. */ + +if (newline_arg != NULL) + { + for (endlinetype = 1; endlinetype < (int)(sizeof(newlines)/sizeof(char *)); + endlinetype++) + { + if (strcmpic(newline_arg, newlines[endlinetype]) == 0) break; + } + if (endlinetype < (int)(sizeof(newlines)/sizeof(char *))) + pcre2_set_newline(compile_context, endlinetype); + else + { + fprintf(stderr, "pcre2grep: Invalid newline specifier \"%s\"\n", + newline_arg); + goto EXIT2; + } + } + +/* Find default newline convention */ + +else + { + (void)pcre2_config(PCRE2_CONFIG_NEWLINE, &endlinetype); + } + +/* Interpret the text values for -d and -D */ + +if (dee_option != NULL) + { + if (strcmp(dee_option, "read") == 0) dee_action = dee_READ; + else if (strcmp(dee_option, "recurse") == 0) dee_action = dee_RECURSE; + else if (strcmp(dee_option, "skip") == 0) dee_action = dee_SKIP; + else + { + fprintf(stderr, "pcre2grep: Invalid value \"%s\" for -d\n", dee_option); + goto EXIT2; + } + } + +if (DEE_option != NULL) + { + if (strcmp(DEE_option, "read") == 0) DEE_action = DEE_READ; + else if (strcmp(DEE_option, "skip") == 0) DEE_action = DEE_SKIP; + else + { + fprintf(stderr, "pcre2grep: Invalid value \"%s\" for -D\n", DEE_option); + goto EXIT2; + } + } + +/* Set the extra options */ + +(void)pcre2_set_compile_extra_options(compile_context, extra_options); + +/* Check the values for Jeffrey Friedl's debugging options. */ + +#ifdef JFRIEDL_DEBUG +if (S_arg > 9) + { + fprintf(stderr, "pcre2grep: bad value for -S option\n"); + return 2; + } +if (jfriedl_XT != 0 || jfriedl_XR != 0) + { + if (jfriedl_XT == 0) jfriedl_XT = 1; + if (jfriedl_XR == 0) jfriedl_XR = 1; + } +#endif + +/* If use_jit is set, check whether JIT is available. If not, do not try +to use JIT. */ + +if (use_jit) + { + uint32_t answer; + (void)pcre2_config(PCRE2_CONFIG_JIT, &answer); + if (!answer) use_jit = FALSE; + } + +/* Get memory for the main buffer. */ + +if (bufthird <= 0) + { + fprintf(stderr, "pcre2grep: --buffer-size must be greater than zero\n"); + goto EXIT2; + } + +bufsize = 3*bufthird; +main_buffer = (char *)malloc(bufsize); + +if (main_buffer == NULL) + { + fprintf(stderr, "pcre2grep: malloc failed\n"); + goto EXIT2; + } + +/* If no patterns were provided by -e, and there are no files provided by -f, +the first argument is the one and only pattern, and it must exist. */ + +if (patterns == NULL && pattern_files == NULL) + { + if (i >= argc) return usage(2); + patterns = patterns_last = add_pattern(argv[i], (PCRE2_SIZE)strlen(argv[i]), + NULL); + i++; + if (patterns == NULL) goto EXIT2; + } + +/* Compile the patterns that were provided on the command line, either by +multiple uses of -e or as a single unkeyed pattern. We cannot do this until +after all the command-line options are read so that we know which PCRE options +to use. When -F is used, compile_pattern() may add another block into the +chain, so we must not access the next pointer till after the compile. */ + +for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next) + { + if (!compile_pattern(cp, pcre2_options, FALSE, "command-line", + (j == 1 && patterns->next == NULL)? 0 : j)) + goto EXIT2; + } + +/* Read and compile the regular expressions that are provided in files. */ + +for (fn = pattern_files; fn != NULL; fn = fn->next) + { + if (!read_pattern_file(fn->name, &patterns, &patterns_last)) goto EXIT2; + } + +/* Unless JIT has been explicitly disabled, arrange a stack for it to use. */ + +#ifdef SUPPORT_PCRE2GREP_JIT +if (use_jit) + { + jit_stack = pcre2_jit_stack_create(32*1024, 1024*1024, NULL); + if (jit_stack != NULL ) + pcre2_jit_stack_assign(match_context, NULL, jit_stack); + } +#endif + +/* -F, -w, and -x do not apply to include or exclude patterns, so we must +adjust the options. */ + +pcre2_options &= ~PCRE2_LITERAL; +(void)pcre2_set_compile_extra_options(compile_context, 0); + +/* If there are include or exclude patterns read from the command line, compile +them. */ + +for (j = 0; j < 4; j++) + { + int k; + for (k = 1, cp = *(incexlist[j]); cp != NULL; k++, cp = cp->next) + { + if (!compile_pattern(cp, pcre2_options, FALSE, incexname[j], + (k == 1 && cp->next == NULL)? 0 : k)) + goto EXIT2; + } + } + +/* Read and compile include/exclude patterns from files. */ + +for (fn = include_from; fn != NULL; fn = fn->next) + { + if (!read_pattern_file(fn->name, &include_patterns, &include_patterns_last)) + goto EXIT2; + } + +for (fn = exclude_from; fn != NULL; fn = fn->next) + { + if (!read_pattern_file(fn->name, &exclude_patterns, &exclude_patterns_last)) + goto EXIT2; + } + +/* If there are no files that contain lists of files to search, and there are +no file arguments, search stdin, and then exit. */ + +if (file_lists == NULL && i >= argc) + { + rc = pcre2grep(stdin, FR_PLAIN, stdin_name, + (filenames > FN_DEFAULT)? stdin_name : NULL); + goto EXIT; + } + +/* If any files that contains a list of files to search have been specified, +read them line by line and search the given files. */ + +for (fn = file_lists; fn != NULL; fn = fn->next) + { + char buffer[FNBUFSIZ]; + FILE *fl; + if (strcmp(fn->name, "-") == 0) fl = stdin; else + { + fl = fopen(fn->name, "rb"); + if (fl == NULL) + { + fprintf(stderr, "pcre2grep: Failed to open %s: %s\n", fn->name, + strerror(errno)); + goto EXIT2; + } + } + while (fgets(buffer, sizeof(buffer), fl) != NULL) + { + int frc; + char *end = buffer + (int)strlen(buffer); + while (end > buffer && isspace(end[-1])) end--; + *end = 0; + if (*buffer != 0) + { + frc = grep_or_recurse(buffer, dee_action == dee_RECURSE, FALSE); + if (frc > 1) rc = frc; + else if (frc == 0 && rc == 1) rc = 0; + } + } + if (fl != stdin) fclose(fl); + } + +/* After handling file-list, work through remaining arguments. Pass in the fact +that there is only one argument at top level - this suppresses the file name if +the argument is not a directory and filenames are not otherwise forced. */ + +only_one_at_top = i == argc - 1 && file_lists == NULL; + +for (; i < argc; i++) + { + int frc = grep_or_recurse(argv[i], dee_action == dee_RECURSE, + only_one_at_top); + if (frc > 1) rc = frc; + else if (frc == 0 && rc == 1) rc = 0; + } + +#ifdef SUPPORT_PCRE2GREP_CALLOUT +/* If separating builtin echo callouts by implicit newline, add one more for +the final item. */ + +if (om_separator != NULL && strcmp(om_separator, STDOUT_NL) == 0) + fprintf(stdout, STDOUT_NL); +#endif + +/* Show the total number of matches if requested, but not if only one file's +count was printed. */ + +if (show_total_count && counts_printed != 1 && filenames != FN_NOMATCH_ONLY) + { + if (counts_printed != 0 && filenames >= FN_DEFAULT) + fprintf(stdout, "TOTAL:"); + fprintf(stdout, "%lu" STDOUT_NL, total_count); + } + +EXIT: +#ifdef SUPPORT_PCRE2GREP_JIT +if (jit_stack != NULL) pcre2_jit_stack_free(jit_stack); +#endif + +free(main_buffer); +free((void *)character_tables); + +pcre2_compile_context_free(compile_context); +pcre2_match_context_free(match_context); +pcre2_match_data_free(match_data); + +free_pattern_chain(patterns); +free_pattern_chain(include_patterns); +free_pattern_chain(include_dir_patterns); +free_pattern_chain(exclude_patterns); +free_pattern_chain(exclude_dir_patterns); + +free_file_chain(exclude_from); +free_file_chain(include_from); +free_file_chain(pattern_files); +free_file_chain(file_lists); + +while (only_matching != NULL) + { + omstr *this = only_matching; + only_matching = this->next; + free(this); + } + +pcre2grep_exit(rc); + +EXIT2: +rc = 2; +goto EXIT; +} + +/* End of pcre2grep */ diff --git a/ProcessHacker/pcre/pcre2posix.c b/ProcessHacker/pcre/pcre2posix.c index 6a71969340d4..7b9f4774227e 100644 --- a/ProcessHacker/pcre/pcre2posix.c +++ b/ProcessHacker/pcre/pcre2posix.c @@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel Original API code Copyright (c) 1997-2012 University of Cambridge - New API code Copyright (c) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,14 +38,11 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -// dmex: Disable warnings -#pragma warning(push) -#pragma warning(disable : 4267) /* This module is a wrapper that provides a POSIX API to the underlying PCRE2 functions. */ -#define HAVE_CONFIG_H + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -96,7 +93,7 @@ information; I know nothing about MSVC myself). For example, something like void __cdecl function(....) -might be needed. In order so make this easy, all the exported functions have +might be needed. In order to make this easy, all the exported functions have PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not set, we ensure here that it has no effect. */ @@ -145,6 +142,7 @@ static const int eint2[] = { 32, REG_INVARG, /* this version of PCRE2 does not have Unicode support */ 37, REG_EESCAPE, /* PCRE2 does not support \L, \l, \N{name}, \U, or \u */ 56, REG_INVARG, /* internal error: unknown newline setting */ + 92, REG_INVARG, /* invalid option bits with PCRE2_LITERAL */ }; /* Table of texts corresponding to POSIX error codes */ @@ -234,20 +232,25 @@ PCRE2POSIX_EXP_DEFN int PCRE2_CALL_CONVENTION regcomp(regex_t *preg, const char *pattern, int cflags) { PCRE2_SIZE erroffset; +PCRE2_SIZE patlen; int errorcode; int options = 0; int re_nsub = 0; +patlen = ((cflags & REG_PEND) != 0)? (PCRE2_SIZE)(preg->re_endp - pattern) : + PCRE2_ZERO_TERMINATED; + if ((cflags & REG_ICASE) != 0) options |= PCRE2_CASELESS; if ((cflags & REG_NEWLINE) != 0) options |= PCRE2_MULTILINE; if ((cflags & REG_DOTALL) != 0) options |= PCRE2_DOTALL; +if ((cflags & REG_NOSPEC) != 0) options |= PCRE2_LITERAL; if ((cflags & REG_UTF) != 0) options |= PCRE2_UTF; if ((cflags & REG_UCP) != 0) options |= PCRE2_UCP; if ((cflags & REG_UNGREEDY) != 0) options |= PCRE2_UNGREEDY; preg->re_cflags = cflags; -preg->re_pcre2_code = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, - options, &errorcode, &erroffset, NULL); +preg->re_pcre2_code = pcre2_compile((PCRE2_SPTR)pattern, patlen, options, + &errorcode, &erroffset, NULL); preg->re_erroffset = erroffset; if (preg->re_pcre2_code == NULL) @@ -262,7 +265,7 @@ if (preg->re_pcre2_code == NULL) if (errorcode < (int)(sizeof(eint1)/sizeof(const int))) return eint1[errorcode]; - for (i = 0; i < sizeof(eint2)/(2*sizeof(const int)); i += 2) + for (i = 0; i < sizeof(eint2)/sizeof(const int); i += 2) if (errorcode == eint2[i]) return eint2[i+1]; return REG_BADPAT; } @@ -289,8 +292,7 @@ return 0; /* A suitable match_data block, large enough to hold all possible captures, was obtained when the pattern was compiled, to save having to allocate and free it -for each match. If REG_NOSUB was specified at compile time, the -PCRE_NO_AUTO_CAPTURE flag will be set. When this is the case, the nmatch and +for each match. If REG_NOSUB was specified at compile time, the nmatch and pmatch arguments are ignored, and the only result is yes/no/error. */ PCRE2POSIX_EXP_DEFN int PCRE2_CALL_CONVENTION @@ -342,8 +344,10 @@ if (rc >= 0) if ((size_t)rc > nmatch) rc = (int)nmatch; for (i = 0; i < (size_t)rc; i++) { - pmatch[i].rm_so = ovector[i*2]; - pmatch[i].rm_eo = ovector[i*2+1]; + pmatch[i].rm_so = (ovector[i*2] == PCRE2_UNSET)? -1 : + (int)(ovector[i*2] + so); + pmatch[i].rm_eo = (ovector[i*2+1] == PCRE2_UNSET)? -1 : + (int)(ovector[i*2+1] + so); } for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; return 0; @@ -369,5 +373,3 @@ switch(rc) } /* End of pcre2posix.c */ - -#pragma warning(pop) \ No newline at end of file diff --git a/ProcessHacker/pcre/pcre2posix.h b/ProcessHacker/pcre/pcre2posix.h index 5f2906a4f32c..4ae1d3c2a051 100644 --- a/ProcessHacker/pcre/pcre2posix.h +++ b/ProcessHacker/pcre/pcre2posix.h @@ -56,12 +56,14 @@ extern "C" { #define REG_NOTBOL 0x0004 /* Maps to PCRE2_NOTBOL */ #define REG_NOTEOL 0x0008 /* Maps to PCRE2_NOTEOL */ #define REG_DOTALL 0x0010 /* NOT defined by POSIX; maps to PCRE2_DOTALL */ -#define REG_NOSUB 0x0020 /* Maps to PCRE2_NO_AUTO_CAPTURE */ +#define REG_NOSUB 0x0020 /* Do not report what was matched */ #define REG_UTF 0x0040 /* NOT defined by POSIX; maps to PCRE2_UTF */ #define REG_STARTEND 0x0080 /* BSD feature: pass subject string by so,eo */ #define REG_NOTEMPTY 0x0100 /* NOT defined by POSIX; maps to PCRE2_NOTEMPTY */ #define REG_UNGREEDY 0x0200 /* NOT defined by POSIX; maps to PCRE2_UNGREEDY */ #define REG_UCP 0x0400 /* NOT defined by POSIX; maps to PCRE2_UCP */ +#define REG_PEND 0x0800 /* GNU feature: pass end pattern by re_endp */ +#define REG_NOSPEC 0x1000 /* Maps to PCRE2_LITERAL */ /* This is not used by PCRE2, but by defining it we make it easier to slot PCRE2 into existing programs that make POSIX calls. */ @@ -91,11 +93,13 @@ enum { }; -/* The structure representing a compiled regular expression. */ +/* The structure representing a compiled regular expression. It is also used +for passing the pattern end pointer when REG_PEND is set. */ typedef struct { void *re_pcre2_code; void *re_match_data; + const char *re_endp; size_t re_nsub; size_t re_erroffset; int re_cflags; diff --git a/ProcessHacker/pcre/pcre2test.c b/ProcessHacker/pcre/pcre2test.c new file mode 100644 index 000000000000..8cfb8e919e1e --- /dev/null +++ b/ProcessHacker/pcre/pcre2test.c @@ -0,0 +1,8789 @@ +/************************************************* +* PCRE2 testing program * +*************************************************/ + +/* PCRE2 is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. In 2014 +the API was completely revised and '2' was added to the name, because the old +API, which had lasted for 16 years, could not accommodate new requirements. At +the same time, this testing program was re-designed because its original +hacked-up (non-) design had also run out of steam. + + Written by Philip Hazel + Original code Copyright (c) 1997-2012 University of Cambridge + Rewritten code Copyright (c) 2016-2018 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This program supports testing of the 8-bit, 16-bit, and 32-bit PCRE2 +libraries in a single program, though its input and output are always 8-bit. +It is different from modules such as pcre2_compile.c in the library itself, +which are compiled separately for each code unit width. If two widths are +enabled, for example, pcre2_compile.c is compiled twice. In contrast, +pcre2test.c is compiled only once, and linked with all the enabled libraries. +Therefore, it must not make use of any of the macros from pcre2.h or +pcre2_internal.h that depend on PCRE2_CODE_UNIT_WIDTH. It does, however, make +use of SUPPORT_PCRE2_8, SUPPORT_PCRE2_16, and SUPPORT_PCRE2_32, to ensure that +it references only the enabled library functions. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if defined NATIVE_ZOS +#include "pcrzoscs.h" +/* That header is not included in the main PCRE2 distribution because other +apparatus is needed to compile pcre2test for z/OS. The header can be found in +the special z/OS distribution, which is available from www.zaconsultants.net or +from www.cbttape.org. */ +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +/* Debugging code enabler */ + +/* #define DEBUG_SHOW_MALLOC_ADDRESSES */ + +/* Both libreadline and libedit are optionally supported. The user-supplied +original patch uses readline/readline.h for libedit, but in at least one system +it is installed as editline/readline.h, so the configuration code now looks for +that first, falling back to readline/readline.h. */ + +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) +#if defined(SUPPORT_LIBREADLINE) +#include +#include +#else +#if defined(HAVE_EDITLINE_READLINE_H) +#include +#else +#include +#endif +#endif +#endif + +/* Put the test for interactive input into a macro so that it can be changed if +required for different environments. */ + +#define INTERACTIVE(f) isatty(fileno(f)) + + +/* ---------------------- System-specific definitions ---------------------- */ + +/* A number of things vary for Windows builds. Originally, pcretest opened its +input and output without "b"; then I was told that "b" was needed in some +environments, so it was added for release 5.0 to both the input and output. (It +makes no difference on Unix-like systems.) Later I was told that it is wrong +for the input on Windows. I've now abstracted the modes into macros that are +set here, to make it easier to fiddle with them, and removed "b" from the input +mode under Windows. The BINARY versions are used when saving/restoring compiled +patterns. */ + +#if defined(_WIN32) || defined(WIN32) +#include /* For _setmode() */ +#include /* For _O_BINARY */ +#define INPUT_MODE "r" +#define OUTPUT_MODE "wb" +#define BINARY_INPUT_MODE "rb" +#define BINARY_OUTPUT_MODE "wb" + +#ifndef isatty +#define isatty _isatty /* This is what Windows calls them, I'm told, */ +#endif /* though in some environments they seem to */ + /* be already defined, hence the #ifndefs. */ +#ifndef fileno +#define fileno _fileno +#endif + +/* A user sent this fix for Borland Builder 5 under Windows. */ + +#ifdef __BORLANDC__ +#define _setmode(handle, mode) setmode(handle, mode) +#endif + +/* Not Windows */ + +#else +#include /* These two includes are needed */ +#include /* for setrlimit(). */ +#if defined NATIVE_ZOS /* z/OS uses non-binary I/O */ +#define INPUT_MODE "r" +#define OUTPUT_MODE "w" +#define BINARY_INPUT_MODE "rb" +#define BINARY_OUTPUT_MODE "wb" +#else +#define INPUT_MODE "rb" +#define OUTPUT_MODE "wb" +#define BINARY_INPUT_MODE "rb" +#define BINARY_OUTPUT_MODE "wb" +#endif +#endif + +#ifdef __VMS +#include +void vms_setsymbol( char *, char *, int ); +#endif + +/* VC and older compilers don't support %td or %zu. */ + +#if defined(_MSC_VER) || !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L +#define PTR_FORM "lu" +#define SIZ_FORM "lu" +#define SIZ_CAST (unsigned long int) +#else +#define PTR_FORM "td" +#define SIZ_FORM "zu" +#define SIZ_CAST +#endif + +/* ------------------End of system-specific definitions -------------------- */ + +/* Glueing macros that are used in several places below. */ + +#define glue(a,b) a##b +#define G(a,b) glue(a,b) + +/* Miscellaneous parameters and manifests */ + +#ifndef CLOCKS_PER_SEC +#ifdef CLK_TCK +#define CLOCKS_PER_SEC CLK_TCK +#else +#define CLOCKS_PER_SEC 100 +#endif +#endif + +#define CFORE_UNSET UINT32_MAX /* Unset value for startend/cfail/cerror fields */ +#define CONVERT_UNSET UINT32_MAX /* Unset value for convert_type field */ +#define DFA_WS_DIMENSION 1000 /* Size of DFA workspace */ +#define DEFAULT_OVECCOUNT 15 /* Default ovector count */ +#define JUNK_OFFSET 0xdeadbeef /* For initializing ovector */ +#define LOCALESIZE 32 /* Size of locale name */ +#define LOOPREPEAT 500000 /* Default loop count for timing */ +#define MALLOCLISTSIZE 20 /* For remembering mallocs */ +#define PARENS_NEST_DEFAULT 220 /* Default parentheses nest limit */ +#define PATSTACKSIZE 20 /* Pattern stack for save/restore testing */ +#define REPLACE_MODSIZE 100 /* Field for reading 8-bit replacement */ +#define VERSION_SIZE 64 /* Size of buffer for the version strings */ + +/* Make sure the buffer into which replacement strings are copied is big enough +to hold them as 32-bit code units. */ + +#define REPLACE_BUFFSIZE 1024 /* This is a byte value */ + +/* Execution modes */ + +#define PCRE8_MODE 8 +#define PCRE16_MODE 16 +#define PCRE32_MODE 32 + +/* Processing returns */ + +enum { PR_OK, PR_SKIP, PR_ABEND }; + +/* The macro PRINTABLE determines whether to print an output character as-is or +as a hex value when showing compiled patterns. is We use it in cases when the +locale has not been explicitly changed, so as to get consistent output from +systems that differ in their output from isprint() even in the "C" locale. */ + +#ifdef EBCDIC +#define PRINTABLE(c) ((c) >= 64 && (c) < 255) +#else +#define PRINTABLE(c) ((c) >= 32 && (c) < 127) +#endif + +#define PRINTOK(c) ((use_tables != NULL && c < 256)? isprint(c) : PRINTABLE(c)) + +/* We have to include some of the library source files because we need +to use some of the macros, internal structure definitions, and other internal +values - pcre2test has "inside information" compared to an application program +that strictly follows the PCRE2 API. + +Before including pcre2_internal.h we define PRIV so that it does not get +defined therein. This ensures that PRIV names in the included files do not +clash with those in the libraries. Also, although pcre2_internal.h does itself +include pcre2.h, we explicitly include it beforehand, along with pcre2posix.h, +so that the PCRE2_EXP_xxx macros get set appropriately for an application, not +for building the library. */ + +#define PRIV(name) name +#define PCRE2_CODE_UNIT_WIDTH 0 +#include "pcre2.h" +#include "pcre2posix.h" +#include "pcre2_internal.h" + +/* We need access to some of the data tables that PCRE2 uses. Defining +PCRE2_PCRETEST makes some minor changes in the files. The previous definition +of PRIV avoids name clashes. */ + +#define PCRE2_PCRE2TEST +#include "pcre2_tables.c" +#include "pcre2_ucd.c" + +/* 32-bit integer values in the input are read by strtoul() or strtol(). The +check needed for overflow depends on whether long ints are in fact longer than +ints. They are defined not to be shorter. */ + +#if ULONG_MAX > UINT32_MAX +#define U32OVERFLOW(x) (x > UINT32_MAX) +#else +#define U32OVERFLOW(x) (x == UINT32_MAX) +#endif + +#if LONG_MAX > INT32_MAX +#define S32OVERFLOW(x) (x > INT32_MAX || x < INT32_MIN) +#else +#define S32OVERFLOW(x) (x == INT32_MAX || x == INT32_MIN) +#endif + +/* When PCRE2_CODE_UNIT_WIDTH is zero, pcre2_internal.h does not include +pcre2_intmodedep.h, which is where mode-dependent macros and structures are +defined. We can now include it for each supported code unit width. Because +PCRE2_CODE_UNIT_WIDTH was defined as zero before including pcre2.h, it will +have left PCRE2_SUFFIX defined as a no-op. We must re-define it appropriately +while including these files, and then restore it to a no-op. Because LINK_SIZE +may be changed in 16-bit mode and forced to 1 in 32-bit mode, the order of +these inclusions should not be changed. */ + +#undef PCRE2_SUFFIX +#undef PCRE2_CODE_UNIT_WIDTH + +#ifdef SUPPORT_PCRE2_8 +#define PCRE2_CODE_UNIT_WIDTH 8 +#define PCRE2_SUFFIX(a) G(a,8) +#include "pcre2_intmodedep.h" +#include "pcre2_printint.c" +#undef PCRE2_CODE_UNIT_WIDTH +#undef PCRE2_SUFFIX +#endif /* SUPPORT_PCRE2_8 */ + +#ifdef SUPPORT_PCRE2_16 +#define PCRE2_CODE_UNIT_WIDTH 16 +#define PCRE2_SUFFIX(a) G(a,16) +#include "pcre2_intmodedep.h" +#include "pcre2_printint.c" +#undef PCRE2_CODE_UNIT_WIDTH +#undef PCRE2_SUFFIX +#endif /* SUPPORT_PCRE2_16 */ + +#ifdef SUPPORT_PCRE2_32 +#define PCRE2_CODE_UNIT_WIDTH 32 +#define PCRE2_SUFFIX(a) G(a,32) +#include "pcre2_intmodedep.h" +#include "pcre2_printint.c" +#undef PCRE2_CODE_UNIT_WIDTH +#undef PCRE2_SUFFIX +#endif /* SUPPORT_PCRE2_32 */ + +#define PCRE2_SUFFIX(a) a + +/* We need to be able to check input text for UTF-8 validity, whatever code +widths are actually available, because the input to pcre2test is always in +8-bit code units. So we include the UTF validity checking function for 8-bit +code units. */ + +extern int valid_utf(PCRE2_SPTR8, PCRE2_SIZE, PCRE2_SIZE *); + +#define PCRE2_CODE_UNIT_WIDTH 8 +#undef PCRE2_SPTR +#define PCRE2_SPTR PCRE2_SPTR8 +#include "pcre2_valid_utf.c" +#undef PCRE2_CODE_UNIT_WIDTH +#undef PCRE2_SPTR + +/* If we have 8-bit support, default to it; if there is also 16-or 32-bit +support, it can be selected by a command-line option. If there is no 8-bit +support, there must be 16-bit or 32-bit support, so default to one of them. The +config function, JIT stack, contexts, and version string are the same in all +modes, so use the form of the first that is available. */ + +#if defined SUPPORT_PCRE2_8 +#define DEFAULT_TEST_MODE PCRE8_MODE +#define VERSION_TYPE PCRE2_UCHAR8 +#define PCRE2_CONFIG pcre2_config_8 +#define PCRE2_JIT_STACK pcre2_jit_stack_8 +#define PCRE2_REAL_GENERAL_CONTEXT pcre2_real_general_context_8 +#define PCRE2_REAL_COMPILE_CONTEXT pcre2_real_compile_context_8 +#define PCRE2_REAL_CONVERT_CONTEXT pcre2_real_convert_context_8 +#define PCRE2_REAL_MATCH_CONTEXT pcre2_real_match_context_8 + +#elif defined SUPPORT_PCRE2_16 +#define DEFAULT_TEST_MODE PCRE16_MODE +#define VERSION_TYPE PCRE2_UCHAR16 +#define PCRE2_CONFIG pcre2_config_16 +#define PCRE2_JIT_STACK pcre2_jit_stack_16 +#define PCRE2_REAL_GENERAL_CONTEXT pcre2_real_general_context_16 +#define PCRE2_REAL_COMPILE_CONTEXT pcre2_real_compile_context_16 +#define PCRE2_REAL_CONVERT_CONTEXT pcre2_real_convert_context_16 +#define PCRE2_REAL_MATCH_CONTEXT pcre2_real_match_context_16 + +#elif defined SUPPORT_PCRE2_32 +#define DEFAULT_TEST_MODE PCRE32_MODE +#define VERSION_TYPE PCRE2_UCHAR32 +#define PCRE2_CONFIG pcre2_config_32 +#define PCRE2_JIT_STACK pcre2_jit_stack_32 +#define PCRE2_REAL_GENERAL_CONTEXT pcre2_real_general_context_32 +#define PCRE2_REAL_COMPILE_CONTEXT pcre2_real_compile_context_32 +#define PCRE2_REAL_CONVERT_CONTEXT pcre2_real_convert_context_32 +#define PCRE2_REAL_MATCH_CONTEXT pcre2_real_match_context_32 +#endif + +/* ------------- Structure and table for handling #-commands ------------- */ + +typedef struct cmdstruct { + const char *name; + int value; +} cmdstruct; + +enum { CMD_FORBID_UTF, CMD_LOAD, CMD_NEWLINE_DEFAULT, CMD_PATTERN, + CMD_PERLTEST, CMD_POP, CMD_POPCOPY, CMD_SAVE, CMD_SUBJECT, CMD_UNKNOWN }; + +static cmdstruct cmdlist[] = { + { "forbid_utf", CMD_FORBID_UTF }, + { "load", CMD_LOAD }, + { "newline_default", CMD_NEWLINE_DEFAULT }, + { "pattern", CMD_PATTERN }, + { "perltest", CMD_PERLTEST }, + { "pop", CMD_POP }, + { "popcopy", CMD_POPCOPY }, + { "save", CMD_SAVE }, + { "subject", CMD_SUBJECT }}; + +#define cmdlistcount (sizeof(cmdlist)/sizeof(cmdstruct)) + +/* ------------- Structures and tables for handling modifiers -------------- */ + +/* Table of names for newline types. Must be kept in step with the definitions +of PCRE2_NEWLINE_xx in pcre2.h. */ + +static const char *newlines[] = { + "DEFAULT", "CR", "LF", "CRLF", "ANY", "ANYCRLF", "NUL" }; + +/* Structure and table for handling pattern conversion types. */ + +typedef struct convertstruct { + const char *name; + uint32_t option; +} convertstruct; + +static convertstruct convertlist[] = { + { "glob", PCRE2_CONVERT_GLOB }, + { "glob_no_starstar", PCRE2_CONVERT_GLOB_NO_STARSTAR }, + { "glob_no_wild_separator", PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR }, + { "posix_basic", PCRE2_CONVERT_POSIX_BASIC }, + { "posix_extended", PCRE2_CONVERT_POSIX_EXTENDED }, + { "unset", CONVERT_UNSET }}; + +#define convertlistcount (sizeof(convertlist)/sizeof(convertstruct)) + +/* Modifier types and applicability */ + +enum { MOD_CTC, /* Applies to a compile context */ + MOD_CTM, /* Applies to a match context */ + MOD_PAT, /* Applies to a pattern */ + MOD_PATP, /* Ditto, OK for Perl test */ + MOD_DAT, /* Applies to a data line */ + MOD_PD, /* Applies to a pattern or a data line */ + MOD_PDP, /* As MOD_PD, OK for Perl test */ + MOD_PND, /* As MOD_PD, but not for a default pattern */ + MOD_PNDP, /* As MOD_PND, OK for Perl test */ + MOD_CHR, /* Is a single character */ + MOD_CON, /* Is a "convert" type/options list */ + MOD_CTL, /* Is a control bit */ + MOD_BSR, /* Is a BSR value */ + MOD_IN2, /* Is one or two unsigned integers */ + MOD_INS, /* Is a signed integer */ + MOD_INT, /* Is an unsigned integer */ + MOD_IND, /* Is an unsigned integer, but no value => default */ + MOD_NL, /* Is a newline value */ + MOD_NN, /* Is a number or a name; more than one may occur */ + MOD_OPT, /* Is an option bit */ + MOD_SIZ, /* Is a PCRE2_SIZE value */ + MOD_STR }; /* Is a string */ + +/* Control bits. Some apply to compiling, some to matching, but some can be set +either on a pattern or a data line, so they must all be distinct. There are now +so many of them that they are split into two fields. */ + +#define CTL_AFTERTEXT 0x00000001u +#define CTL_ALLAFTERTEXT 0x00000002u +#define CTL_ALLCAPTURES 0x00000004u +#define CTL_ALLUSEDTEXT 0x00000008u +#define CTL_ALTGLOBAL 0x00000010u +#define CTL_BINCODE 0x00000020u +#define CTL_CALLOUT_CAPTURE 0x00000040u +#define CTL_CALLOUT_INFO 0x00000080u +#define CTL_CALLOUT_NONE 0x00000100u +#define CTL_DFA 0x00000200u +#define CTL_EXPAND 0x00000400u +#define CTL_FINDLIMITS 0x00000800u +#define CTL_FRAMESIZE 0x00001000u +#define CTL_FULLBINCODE 0x00002000u +#define CTL_GETALL 0x00004000u +#define CTL_GLOBAL 0x00008000u +#define CTL_HEXPAT 0x00010000u /* Same word as USE_LENGTH */ +#define CTL_INFO 0x00020000u +#define CTL_JITFAST 0x00040000u +#define CTL_JITVERIFY 0x00080000u +#define CTL_MARK 0x00100000u +#define CTL_MEMORY 0x00200000u +#define CTL_NULLCONTEXT 0x00400000u +#define CTL_POSIX 0x00800000u +#define CTL_POSIX_NOSUB 0x01000000u +#define CTL_PUSH 0x02000000u /* These three must be */ +#define CTL_PUSHCOPY 0x04000000u /* all in the same */ +#define CTL_PUSHTABLESCOPY 0x08000000u /* word. */ +#define CTL_STARTCHAR 0x10000000u +#define CTL_USE_LENGTH 0x20000000u /* Same word as HEXPAT */ +#define CTL_UTF8_INPUT 0x40000000u +#define CTL_ZERO_TERMINATE 0x80000000u + +/* Combinations */ + +#define CTL_DEBUG (CTL_FULLBINCODE|CTL_INFO) /* For setting */ +#define CTL_ANYINFO (CTL_DEBUG|CTL_BINCODE|CTL_CALLOUT_INFO) +#define CTL_ANYGLOB (CTL_ALTGLOBAL|CTL_GLOBAL) + +/* Second control word */ + +#define CTL2_SUBSTITUTE_EXTENDED 0x00000001u +#define CTL2_SUBSTITUTE_OVERFLOW_LENGTH 0x00000002u +#define CTL2_SUBSTITUTE_UNKNOWN_UNSET 0x00000004u +#define CTL2_SUBSTITUTE_UNSET_EMPTY 0x00000008u +#define CTL2_SUBJECT_LITERAL 0x00000010u +#define CTL2_CALLOUT_NO_WHERE 0x00000020u +#define CTL2_CALLOUT_EXTRA 0x00000040u + +#define CTL2_NL_SET 0x40000000u /* Informational */ +#define CTL2_BSR_SET 0x80000000u /* Informational */ + +/* These are the matching controls that may be set either on a pattern or on a +data line. They are copied from the pattern controls as initial settings for +data line controls. Note that CTL_MEMORY is not included here, because it does +different things in the two cases. */ + +#define CTL_ALLPD (CTL_AFTERTEXT|\ + CTL_ALLAFTERTEXT|\ + CTL_ALLCAPTURES|\ + CTL_ALLUSEDTEXT|\ + CTL_ALTGLOBAL|\ + CTL_GLOBAL|\ + CTL_MARK|\ + CTL_STARTCHAR|\ + CTL_UTF8_INPUT) + +#define CTL2_ALLPD (CTL2_SUBSTITUTE_EXTENDED|\ + CTL2_SUBSTITUTE_OVERFLOW_LENGTH|\ + CTL2_SUBSTITUTE_UNKNOWN_UNSET|\ + CTL2_SUBSTITUTE_UNSET_EMPTY) + +/* Structures for holding modifier information for patterns and subject strings +(data). Fields containing modifiers that can be set either for a pattern or a +subject must be at the start and in the same order in both cases so that the +same offset in the big table below works for both. */ + +typedef struct patctl { /* Structure for pattern modifiers. */ + uint32_t options; /* Must be in same position as datctl */ + uint32_t control; /* Must be in same position as datctl */ + uint32_t control2; /* Must be in same position as datctl */ + uint32_t jitstack; /* Must be in same position as datctl */ + uint8_t replacement[REPLACE_MODSIZE]; /* So must this */ + uint32_t jit; + uint32_t stackguard_test; + uint32_t tables_id; + uint32_t convert_type; + uint32_t convert_length; + uint32_t convert_glob_escape; + uint32_t convert_glob_separator; + uint32_t regerror_buffsize; + uint8_t locale[LOCALESIZE]; +} patctl; + +#define MAXCPYGET 10 +#define LENCPYGET 64 + +typedef struct datctl { /* Structure for data line modifiers. */ + uint32_t options; /* Must be in same position as patctl */ + uint32_t control; /* Must be in same position as patctl */ + uint32_t control2; /* Must be in same position as patctl */ + uint32_t jitstack; /* Must be in same position as patctl */ + uint8_t replacement[REPLACE_MODSIZE]; /* So must this */ + uint32_t startend[2]; + uint32_t cerror[2]; + uint32_t cfail[2]; + int32_t callout_data; + int32_t copy_numbers[MAXCPYGET]; + int32_t get_numbers[MAXCPYGET]; + uint32_t oveccount; + uint32_t offset; + uint8_t copy_names[LENCPYGET]; + uint8_t get_names[LENCPYGET]; +} datctl; + +/* Ids for which context to modify. */ + +enum { CTX_PAT, /* Active pattern context */ + CTX_POPPAT, /* Ditto, for a popped pattern */ + CTX_DEFPAT, /* Default pattern context */ + CTX_DAT, /* Active data (match) context */ + CTX_DEFDAT }; /* Default data (match) context */ + +/* Macros to simplify the big table below. */ + +#define CO(name) offsetof(PCRE2_REAL_COMPILE_CONTEXT, name) +#define MO(name) offsetof(PCRE2_REAL_MATCH_CONTEXT, name) +#define PO(name) offsetof(patctl, name) +#define PD(name) PO(name) +#define DO(name) offsetof(datctl, name) + +/* Table of all long-form modifiers. Must be in collating sequence of modifier +name because it is searched by binary chop. */ + +typedef struct modstruct { + const char *name; + uint16_t which; + uint16_t type; + uint32_t value; + PCRE2_SIZE offset; +} modstruct; + +static modstruct modlist[] = { + { "aftertext", MOD_PNDP, MOD_CTL, CTL_AFTERTEXT, PO(control) }, + { "allaftertext", MOD_PNDP, MOD_CTL, CTL_ALLAFTERTEXT, PO(control) }, + { "allcaptures", MOD_PND, MOD_CTL, CTL_ALLCAPTURES, PO(control) }, + { "allow_empty_class", MOD_PAT, MOD_OPT, PCRE2_ALLOW_EMPTY_CLASS, PO(options) }, + { "allow_surrogate_escapes", MOD_CTC, MOD_OPT, PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES, CO(extra_options) }, + { "allusedtext", MOD_PNDP, MOD_CTL, CTL_ALLUSEDTEXT, PO(control) }, + { "alt_bsux", MOD_PAT, MOD_OPT, PCRE2_ALT_BSUX, PO(options) }, + { "alt_circumflex", MOD_PAT, MOD_OPT, PCRE2_ALT_CIRCUMFLEX, PO(options) }, + { "alt_verbnames", MOD_PAT, MOD_OPT, PCRE2_ALT_VERBNAMES, PO(options) }, + { "altglobal", MOD_PND, MOD_CTL, CTL_ALTGLOBAL, PO(control) }, + { "anchored", MOD_PD, MOD_OPT, PCRE2_ANCHORED, PD(options) }, + { "auto_callout", MOD_PAT, MOD_OPT, PCRE2_AUTO_CALLOUT, PO(options) }, + { "bad_escape_is_literal", MOD_CTC, MOD_OPT, PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL, CO(extra_options) }, + { "bincode", MOD_PAT, MOD_CTL, CTL_BINCODE, PO(control) }, + { "bsr", MOD_CTC, MOD_BSR, 0, CO(bsr_convention) }, + { "callout_capture", MOD_DAT, MOD_CTL, CTL_CALLOUT_CAPTURE, DO(control) }, + { "callout_data", MOD_DAT, MOD_INS, 0, DO(callout_data) }, + { "callout_error", MOD_DAT, MOD_IN2, 0, DO(cerror) }, + { "callout_extra", MOD_DAT, MOD_CTL, CTL2_CALLOUT_EXTRA, DO(control2) }, + { "callout_fail", MOD_DAT, MOD_IN2, 0, DO(cfail) }, + { "callout_info", MOD_PAT, MOD_CTL, CTL_CALLOUT_INFO, PO(control) }, + { "callout_no_where", MOD_DAT, MOD_CTL, CTL2_CALLOUT_NO_WHERE, DO(control2) }, + { "callout_none", MOD_DAT, MOD_CTL, CTL_CALLOUT_NONE, DO(control) }, + { "caseless", MOD_PATP, MOD_OPT, PCRE2_CASELESS, PO(options) }, + { "convert", MOD_PAT, MOD_CON, 0, PO(convert_type) }, + { "convert_glob_escape", MOD_PAT, MOD_CHR, 0, PO(convert_glob_escape) }, + { "convert_glob_separator", MOD_PAT, MOD_CHR, 0, PO(convert_glob_separator) }, + { "convert_length", MOD_PAT, MOD_INT, 0, PO(convert_length) }, + { "copy", MOD_DAT, MOD_NN, DO(copy_numbers), DO(copy_names) }, + { "debug", MOD_PAT, MOD_CTL, CTL_DEBUG, PO(control) }, + { "depth_limit", MOD_CTM, MOD_INT, 0, MO(depth_limit) }, + { "dfa", MOD_DAT, MOD_CTL, CTL_DFA, DO(control) }, + { "dfa_restart", MOD_DAT, MOD_OPT, PCRE2_DFA_RESTART, DO(options) }, + { "dfa_shortest", MOD_DAT, MOD_OPT, PCRE2_DFA_SHORTEST, DO(options) }, + { "dollar_endonly", MOD_PAT, MOD_OPT, PCRE2_DOLLAR_ENDONLY, PO(options) }, + { "dotall", MOD_PATP, MOD_OPT, PCRE2_DOTALL, PO(options) }, + { "dupnames", MOD_PATP, MOD_OPT, PCRE2_DUPNAMES, PO(options) }, + { "endanchored", MOD_PD, MOD_OPT, PCRE2_ENDANCHORED, PD(options) }, + { "expand", MOD_PAT, MOD_CTL, CTL_EXPAND, PO(control) }, + { "extended", MOD_PATP, MOD_OPT, PCRE2_EXTENDED, PO(options) }, + { "extended_more", MOD_PATP, MOD_OPT, PCRE2_EXTENDED_MORE, PO(options) }, + { "find_limits", MOD_DAT, MOD_CTL, CTL_FINDLIMITS, DO(control) }, + { "firstline", MOD_PAT, MOD_OPT, PCRE2_FIRSTLINE, PO(options) }, + { "framesize", MOD_PAT, MOD_CTL, CTL_FRAMESIZE, PO(control) }, + { "fullbincode", MOD_PAT, MOD_CTL, CTL_FULLBINCODE, PO(control) }, + { "get", MOD_DAT, MOD_NN, DO(get_numbers), DO(get_names) }, + { "getall", MOD_DAT, MOD_CTL, CTL_GETALL, DO(control) }, + { "global", MOD_PNDP, MOD_CTL, CTL_GLOBAL, PO(control) }, + { "heap_limit", MOD_CTM, MOD_INT, 0, MO(heap_limit) }, + { "hex", MOD_PAT, MOD_CTL, CTL_HEXPAT, PO(control) }, + { "info", MOD_PAT, MOD_CTL, CTL_INFO, PO(control) }, + { "jit", MOD_PAT, MOD_IND, 7, PO(jit) }, + { "jitfast", MOD_PAT, MOD_CTL, CTL_JITFAST, PO(control) }, + { "jitstack", MOD_PNDP, MOD_INT, 0, PO(jitstack) }, + { "jitverify", MOD_PAT, MOD_CTL, CTL_JITVERIFY, PO(control) }, + { "literal", MOD_PAT, MOD_OPT, PCRE2_LITERAL, PO(options) }, + { "locale", MOD_PAT, MOD_STR, LOCALESIZE, PO(locale) }, + { "mark", MOD_PNDP, MOD_CTL, CTL_MARK, PO(control) }, + { "match_limit", MOD_CTM, MOD_INT, 0, MO(match_limit) }, + { "match_line", MOD_CTC, MOD_OPT, PCRE2_EXTRA_MATCH_LINE, CO(extra_options) }, + { "match_unset_backref", MOD_PAT, MOD_OPT, PCRE2_MATCH_UNSET_BACKREF, PO(options) }, + { "match_word", MOD_CTC, MOD_OPT, PCRE2_EXTRA_MATCH_WORD, CO(extra_options) }, + { "max_pattern_length", MOD_CTC, MOD_SIZ, 0, CO(max_pattern_length) }, + { "memory", MOD_PD, MOD_CTL, CTL_MEMORY, PD(control) }, + { "multiline", MOD_PATP, MOD_OPT, PCRE2_MULTILINE, PO(options) }, + { "never_backslash_c", MOD_PAT, MOD_OPT, PCRE2_NEVER_BACKSLASH_C, PO(options) }, + { "never_ucp", MOD_PAT, MOD_OPT, PCRE2_NEVER_UCP, PO(options) }, + { "never_utf", MOD_PAT, MOD_OPT, PCRE2_NEVER_UTF, PO(options) }, + { "newline", MOD_CTC, MOD_NL, 0, CO(newline_convention) }, + { "no_auto_capture", MOD_PAT, MOD_OPT, PCRE2_NO_AUTO_CAPTURE, PO(options) }, + { "no_auto_possess", MOD_PATP, MOD_OPT, PCRE2_NO_AUTO_POSSESS, PO(options) }, + { "no_dotstar_anchor", MOD_PAT, MOD_OPT, PCRE2_NO_DOTSTAR_ANCHOR, PO(options) }, + { "no_jit", MOD_DAT, MOD_OPT, PCRE2_NO_JIT, DO(options) }, + { "no_start_optimize", MOD_PATP, MOD_OPT, PCRE2_NO_START_OPTIMIZE, PO(options) }, + { "no_utf_check", MOD_PD, MOD_OPT, PCRE2_NO_UTF_CHECK, PD(options) }, + { "notbol", MOD_DAT, MOD_OPT, PCRE2_NOTBOL, DO(options) }, + { "notempty", MOD_DAT, MOD_OPT, PCRE2_NOTEMPTY, DO(options) }, + { "notempty_atstart", MOD_DAT, MOD_OPT, PCRE2_NOTEMPTY_ATSTART, DO(options) }, + { "noteol", MOD_DAT, MOD_OPT, PCRE2_NOTEOL, DO(options) }, + { "null_context", MOD_PD, MOD_CTL, CTL_NULLCONTEXT, PO(control) }, + { "offset", MOD_DAT, MOD_INT, 0, DO(offset) }, + { "offset_limit", MOD_CTM, MOD_SIZ, 0, MO(offset_limit)}, + { "ovector", MOD_DAT, MOD_INT, 0, DO(oveccount) }, + { "parens_nest_limit", MOD_CTC, MOD_INT, 0, CO(parens_nest_limit) }, + { "partial_hard", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, + { "partial_soft", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, + { "ph", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, + { "posix", MOD_PAT, MOD_CTL, CTL_POSIX, PO(control) }, + { "posix_nosub", MOD_PAT, MOD_CTL, CTL_POSIX|CTL_POSIX_NOSUB, PO(control) }, + { "posix_startend", MOD_DAT, MOD_IN2, 0, DO(startend) }, + { "ps", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, + { "push", MOD_PAT, MOD_CTL, CTL_PUSH, PO(control) }, + { "pushcopy", MOD_PAT, MOD_CTL, CTL_PUSHCOPY, PO(control) }, + { "pushtablescopy", MOD_PAT, MOD_CTL, CTL_PUSHTABLESCOPY, PO(control) }, + { "recursion_limit", MOD_CTM, MOD_INT, 0, MO(depth_limit) }, /* Obsolete synonym */ + { "regerror_buffsize", MOD_PAT, MOD_INT, 0, PO(regerror_buffsize) }, + { "replace", MOD_PND, MOD_STR, REPLACE_MODSIZE, PO(replacement) }, + { "stackguard", MOD_PAT, MOD_INT, 0, PO(stackguard_test) }, + { "startchar", MOD_PND, MOD_CTL, CTL_STARTCHAR, PO(control) }, + { "startoffset", MOD_DAT, MOD_INT, 0, DO(offset) }, + { "subject_literal", MOD_PATP, MOD_CTL, CTL2_SUBJECT_LITERAL, PO(control2) }, + { "substitute_extended", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_EXTENDED, PO(control2) }, + { "substitute_overflow_length", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_OVERFLOW_LENGTH, PO(control2) }, + { "substitute_unknown_unset", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_UNKNOWN_UNSET, PO(control2) }, + { "substitute_unset_empty", MOD_PND, MOD_CTL, CTL2_SUBSTITUTE_UNSET_EMPTY, PO(control2) }, + { "tables", MOD_PAT, MOD_INT, 0, PO(tables_id) }, + { "ucp", MOD_PATP, MOD_OPT, PCRE2_UCP, PO(options) }, + { "ungreedy", MOD_PAT, MOD_OPT, PCRE2_UNGREEDY, PO(options) }, + { "use_length", MOD_PAT, MOD_CTL, CTL_USE_LENGTH, PO(control) }, + { "use_offset_limit", MOD_PAT, MOD_OPT, PCRE2_USE_OFFSET_LIMIT, PO(options) }, + { "utf", MOD_PATP, MOD_OPT, PCRE2_UTF, PO(options) }, + { "utf8_input", MOD_PAT, MOD_CTL, CTL_UTF8_INPUT, PO(control) }, + { "zero_terminate", MOD_DAT, MOD_CTL, CTL_ZERO_TERMINATE, DO(control) } +}; + +#define MODLISTCOUNT sizeof(modlist)/sizeof(modstruct) + +/* Controls and options that are supported for use with the POSIX interface. */ + +#define POSIX_SUPPORTED_COMPILE_OPTIONS ( \ + PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_LITERAL|PCRE2_MULTILINE|PCRE2_UCP| \ + PCRE2_UTF|PCRE2_UNGREEDY) + +#define POSIX_SUPPORTED_COMPILE_EXTRA_OPTIONS (0) + +#define POSIX_SUPPORTED_COMPILE_CONTROLS ( \ + CTL_AFTERTEXT|CTL_ALLAFTERTEXT|CTL_EXPAND|CTL_HEXPAT|CTL_POSIX| \ + CTL_POSIX_NOSUB|CTL_USE_LENGTH) + +#define POSIX_SUPPORTED_COMPILE_CONTROLS2 (0) + +#define POSIX_SUPPORTED_MATCH_OPTIONS ( \ + PCRE2_NOTBOL|PCRE2_NOTEMPTY|PCRE2_NOTEOL) + +#define POSIX_SUPPORTED_MATCH_CONTROLS (CTL_AFTERTEXT|CTL_ALLAFTERTEXT) +#define POSIX_SUPPORTED_MATCH_CONTROLS2 (0) + +/* Control bits that are not ignored with 'push'. */ + +#define PUSH_SUPPORTED_COMPILE_CONTROLS ( \ + CTL_BINCODE|CTL_CALLOUT_INFO|CTL_FULLBINCODE|CTL_HEXPAT|CTL_INFO| \ + CTL_JITVERIFY|CTL_MEMORY|CTL_FRAMESIZE|CTL_PUSH|CTL_PUSHCOPY| \ + CTL_PUSHTABLESCOPY|CTL_USE_LENGTH) + +#define PUSH_SUPPORTED_COMPILE_CONTROLS2 (CTL2_BSR_SET|CTL2_NL_SET) + +/* Controls that apply only at compile time with 'push'. */ + +#define PUSH_COMPILE_ONLY_CONTROLS CTL_JITVERIFY +#define PUSH_COMPILE_ONLY_CONTROLS2 (0) + +/* Controls that are forbidden with #pop or #popcopy. */ + +#define NOTPOP_CONTROLS (CTL_HEXPAT|CTL_POSIX|CTL_POSIX_NOSUB|CTL_PUSH| \ + CTL_PUSHCOPY|CTL_PUSHTABLESCOPY|CTL_USE_LENGTH) + +/* Pattern controls that are mutually exclusive. At present these are all in +the first control word. Note that CTL_POSIX_NOSUB is always accompanied by +CTL_POSIX, so it doesn't need its own entries. */ + +static uint32_t exclusive_pat_controls[] = { + CTL_POSIX | CTL_PUSH, + CTL_POSIX | CTL_PUSHCOPY, + CTL_POSIX | CTL_PUSHTABLESCOPY, + CTL_PUSH | CTL_PUSHCOPY, + CTL_PUSH | CTL_PUSHTABLESCOPY, + CTL_PUSHCOPY | CTL_PUSHTABLESCOPY, + CTL_EXPAND | CTL_HEXPAT }; + +/* Data controls that are mutually exclusive. At present these are all in the +first control word. */ + +static uint32_t exclusive_dat_controls[] = { + CTL_ALLUSEDTEXT | CTL_STARTCHAR, + CTL_FINDLIMITS | CTL_NULLCONTEXT }; + +/* Table of single-character abbreviated modifiers. The index field is +initialized to -1, but the first time the modifier is encountered, it is filled +in with the index of the full entry in modlist, to save repeated searching when +processing multiple test items. This short list is searched serially, so its +order does not matter. */ + +typedef struct c1modstruct { + const char *fullname; + uint32_t onechar; + int index; +} c1modstruct; + +static c1modstruct c1modlist[] = { + { "bincode", 'B', -1 }, + { "info", 'I', -1 }, + { "global", 'g', -1 }, + { "caseless", 'i', -1 }, + { "multiline", 'm', -1 }, + { "no_auto_capture", 'n', -1 }, + { "dotall", 's', -1 }, + { "extended", 'x', -1 } +}; + +#define C1MODLISTCOUNT sizeof(c1modlist)/sizeof(c1modstruct) + +/* Table of arguments for the -C command line option. Use macros to make the +table itself easier to read. */ + +#if defined SUPPORT_PCRE2_8 +#define SUPPORT_8 1 +#endif +#if defined SUPPORT_PCRE2_16 +#define SUPPORT_16 1 +#endif +#if defined SUPPORT_PCRE2_32 +#define SUPPORT_32 1 +#endif + +#ifndef SUPPORT_8 +#define SUPPORT_8 0 +#endif +#ifndef SUPPORT_16 +#define SUPPORT_16 0 +#endif +#ifndef SUPPORT_32 +#define SUPPORT_32 0 +#endif + +#ifdef EBCDIC +#define SUPPORT_EBCDIC 1 +#define EBCDIC_NL CHAR_LF +#else +#define SUPPORT_EBCDIC 0 +#define EBCDIC_NL 0 +#endif + +#ifdef NEVER_BACKSLASH_C +#define BACKSLASH_C 0 +#else +#define BACKSLASH_C 1 +#endif + +typedef struct coptstruct { + const char *name; + uint32_t type; + uint32_t value; +} coptstruct; + +enum { CONF_BSR, + CONF_FIX, + CONF_FIZ, + CONF_INT, + CONF_NL +}; + +static coptstruct coptlist[] = { + { "backslash-C", CONF_FIX, BACKSLASH_C }, + { "bsr", CONF_BSR, PCRE2_CONFIG_BSR }, + { "ebcdic", CONF_FIX, SUPPORT_EBCDIC }, + { "ebcdic-nl", CONF_FIZ, EBCDIC_NL }, + { "jit", CONF_INT, PCRE2_CONFIG_JIT }, + { "linksize", CONF_INT, PCRE2_CONFIG_LINKSIZE }, + { "newline", CONF_NL, PCRE2_CONFIG_NEWLINE }, + { "pcre2-16", CONF_FIX, SUPPORT_16 }, + { "pcre2-32", CONF_FIX, SUPPORT_32 }, + { "pcre2-8", CONF_FIX, SUPPORT_8 }, + { "unicode", CONF_INT, PCRE2_CONFIG_UNICODE } +}; + +#define COPTLISTCOUNT sizeof(coptlist)/sizeof(coptstruct) + +#undef SUPPORT_8 +#undef SUPPORT_16 +#undef SUPPORT_32 +#undef SUPPORT_EBCDIC + + +/* ----------------------- Static variables ------------------------ */ + +static FILE *infile; +static FILE *outfile; + +static const void *last_callout_mark; +static PCRE2_JIT_STACK *jit_stack = NULL; +static size_t jit_stack_size = 0; + +static BOOL first_callout; +static BOOL jit_was_used; +static BOOL restrict_for_perl_test = FALSE; +static BOOL show_memory = FALSE; + +static int code_unit_size; /* Bytes */ +static int jitrc; /* Return from JIT compile */ +static int test_mode = DEFAULT_TEST_MODE; +static int timeit = 0; +static int timeitm = 0; + +clock_t total_compile_time = 0; +clock_t total_jit_compile_time = 0; +clock_t total_match_time = 0; + +static uint32_t dfa_matched; +static uint32_t forbid_utf = 0; +static uint32_t maxlookbehind; +static uint32_t max_oveccount; +static uint32_t callout_count; + +static uint16_t local_newline_default = 0; + +static VERSION_TYPE jittarget[VERSION_SIZE]; +static VERSION_TYPE version[VERSION_SIZE]; +static VERSION_TYPE uversion[VERSION_SIZE]; + +static patctl def_patctl; +static patctl pat_patctl; +static datctl def_datctl; +static datctl dat_datctl; + +static void *patstack[PATSTACKSIZE]; +static int patstacknext = 0; + +static void *malloclist[MALLOCLISTSIZE]; +static PCRE2_SIZE malloclistlength[MALLOCLISTSIZE]; +static uint32_t malloclistptr = 0; + +#ifdef SUPPORT_PCRE2_8 +static regex_t preg = { NULL, NULL, 0, 0, 0, 0 }; +#endif + +static int *dfa_workspace = NULL; +static const uint8_t *locale_tables = NULL; +static const uint8_t *use_tables = NULL; +static uint8_t locale_name[32]; + +/* We need buffers for building 16/32-bit strings; 8-bit strings don't need +rebuilding, but set up the same naming scheme for use in macros. The "buffer" +buffer is where all input lines are read. Its size is the same as pbuffer8. +Pattern lines are always copied to pbuffer8 for use in callouts, even if they +are actually compiled from pbuffer16 or pbuffer32. */ + +static size_t pbuffer8_size = 50000; /* Initial size, bytes */ +static uint8_t *pbuffer8 = NULL; +static uint8_t *buffer = NULL; + +/* The dbuffer is where all processed data lines are put. In non-8-bit modes it +is cast as needed. For long data lines it grows as necessary. */ + +static size_t dbuffer_size = 1u << 14; /* Initial size, bytes */ +static uint8_t *dbuffer = NULL; + + +/* ---------------- Mode-dependent variables -------------------*/ + +#ifdef SUPPORT_PCRE2_8 +static pcre2_code_8 *compiled_code8; +static pcre2_general_context_8 *general_context8, *general_context_copy8; +static pcre2_compile_context_8 *pat_context8, *default_pat_context8; +static pcre2_convert_context_8 *con_context8, *default_con_context8; +static pcre2_match_context_8 *dat_context8, *default_dat_context8; +static pcre2_match_data_8 *match_data8; +#endif + +#ifdef SUPPORT_PCRE2_16 +static pcre2_code_16 *compiled_code16; +static pcre2_general_context_16 *general_context16, *general_context_copy16; +static pcre2_compile_context_16 *pat_context16, *default_pat_context16; +static pcre2_convert_context_16 *con_context16, *default_con_context16; +static pcre2_match_context_16 *dat_context16, *default_dat_context16; +static pcre2_match_data_16 *match_data16; +static PCRE2_SIZE pbuffer16_size = 0; /* Set only when needed */ +static uint16_t *pbuffer16 = NULL; +#endif + +#ifdef SUPPORT_PCRE2_32 +static pcre2_code_32 *compiled_code32; +static pcre2_general_context_32 *general_context32, *general_context_copy32; +static pcre2_compile_context_32 *pat_context32, *default_pat_context32; +static pcre2_convert_context_32 *con_context32, *default_con_context32; +static pcre2_match_context_32 *dat_context32, *default_dat_context32; +static pcre2_match_data_32 *match_data32; +static PCRE2_SIZE pbuffer32_size = 0; /* Set only when needed */ +static uint32_t *pbuffer32 = NULL; +#endif + + +/* ---------------- Macros that work in all modes ----------------- */ + +#define CAST8VAR(x) CASTVAR(uint8_t *, x) +#define SET(x,y) SETOP(x,y,=) +#define SETPLUS(x,y) SETOP(x,y,+=) +#define strlen8(x) strlen((char *)x) + + +/* ---------------- Mode-dependent, runtime-testing macros ------------------*/ + +/* Define macros for variables and functions that must be selected dynamically +depending on the mode setting (8, 16, 32). These are dependent on which modes +are supported. */ + +#if (defined (SUPPORT_PCRE2_8) + defined (SUPPORT_PCRE2_16) + \ + defined (SUPPORT_PCRE2_32)) >= 2 + +/* ----- All three modes supported ----- */ + +#if defined(SUPPORT_PCRE2_8) && defined(SUPPORT_PCRE2_16) && defined(SUPPORT_PCRE2_32) + +#define CASTFLD(t,a,b) ((test_mode == PCRE8_MODE)? (t)(G(a,8)->b) : \ + (test_mode == PCRE16_MODE)? (t)(G(a,16)->b) : (t)(G(a,32)->b)) + +#define CASTVAR(t,x) ( \ + (test_mode == PCRE8_MODE)? (t)G(x,8) : \ + (test_mode == PCRE16_MODE)? (t)G(x,16) : (t)G(x,32)) + +#define CODE_UNIT(a,b) ( \ + (test_mode == PCRE8_MODE)? (uint32_t)(((PCRE2_SPTR8)(a))[b]) : \ + (test_mode == PCRE16_MODE)? (uint32_t)(((PCRE2_SPTR16)(a))[b]) : \ + (uint32_t)(((PCRE2_SPTR32)(a))[b])) + +#define CONCTXCPY(a,b) \ + if (test_mode == PCRE8_MODE) \ + memcpy(G(a,8),G(b,8),sizeof(pcre2_convert_context_8)); \ + else if (test_mode == PCRE16_MODE) \ + memcpy(G(a,16),G(b,16),sizeof(pcre2_convert_context_16)); \ + else memcpy(G(a,32),G(b,32),sizeof(pcre2_convert_context_32)) + +#define CONVERT_COPY(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + memcpy(G(a,8),(char *)b,c); \ + else if (test_mode == PCRE16_MODE) \ + memcpy(G(a,16),(char *)b,(c)*2); \ + else if (test_mode == PCRE32_MODE) \ + memcpy(G(a,32),(char *)b,(c)*4) + +#define DATCTXCPY(a,b) \ + if (test_mode == PCRE8_MODE) \ + memcpy(G(a,8),G(b,8),sizeof(pcre2_match_context_8)); \ + else if (test_mode == PCRE16_MODE) \ + memcpy(G(a,16),G(b,16),sizeof(pcre2_match_context_16)); \ + else memcpy(G(a,32),G(b,32),sizeof(pcre2_match_context_32)) + +#define FLD(a,b) ((test_mode == PCRE8_MODE)? G(a,8)->b : \ + (test_mode == PCRE16_MODE)? G(a,16)->b : G(a,32)->b) + +#define PATCTXCPY(a,b) \ + if (test_mode == PCRE8_MODE) \ + memcpy(G(a,8),G(b,8),sizeof(pcre2_compile_context_8)); \ + else if (test_mode == PCRE16_MODE) \ + memcpy(G(a,16),G(b,16),sizeof(pcre2_compile_context_16)); \ + else memcpy(G(a,32),G(b,32),sizeof(pcre2_compile_context_32)) + +#define PCHARS(lv, p, offset, len, utf, f) \ + if (test_mode == PCRE32_MODE) \ + lv = pchars32((PCRE2_SPTR32)(p)+offset, len, utf, f); \ + else if (test_mode == PCRE16_MODE) \ + lv = pchars16((PCRE2_SPTR16)(p)+offset, len, utf, f); \ + else \ + lv = pchars8((PCRE2_SPTR8)(p)+offset, len, utf, f) + +#define PCHARSV(p, offset, len, utf, f) \ + if (test_mode == PCRE32_MODE) \ + (void)pchars32((PCRE2_SPTR32)(p)+offset, len, utf, f); \ + else if (test_mode == PCRE16_MODE) \ + (void)pchars16((PCRE2_SPTR16)(p)+offset, len, utf, f); \ + else \ + (void)pchars8((PCRE2_SPTR8)(p)+offset, len, utf, f) + +#define PCRE2_CALLOUT_ENUMERATE(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_callout_enumerate_8(compiled_code8, \ + (int (*)(struct pcre2_callout_enumerate_block_8 *, void *))b,c); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_callout_enumerate_16(compiled_code16, \ + (int(*)(struct pcre2_callout_enumerate_block_16 *, void *))b,c); \ + else \ + a = pcre2_callout_enumerate_32(compiled_code32, \ + (int (*)(struct pcre2_callout_enumerate_block_32 *, void *))b,c) + +#define PCRE2_CODE_COPY_FROM_VOID(a,b) \ + if (test_mode == PCRE8_MODE) \ + G(a,8) = pcre2_code_copy_8(b); \ + else if (test_mode == PCRE16_MODE) \ + G(a,16) = pcre2_code_copy_16(b); \ + else \ + G(a,32) = pcre2_code_copy_32(b) + +#define PCRE2_CODE_COPY_TO_VOID(a,b) \ + if (test_mode == PCRE8_MODE) \ + a = (void *)pcre2_code_copy_8(G(b,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = (void *)pcre2_code_copy_16(G(b,16)); \ + else \ + a = (void *)pcre2_code_copy_32(G(b,32)) + +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) \ + if (test_mode == PCRE8_MODE) \ + a = (void *)pcre2_code_copy_with_tables_8(G(b,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = (void *)pcre2_code_copy_with_tables_16(G(b,16)); \ + else \ + a = (void *)pcre2_code_copy_with_tables_32(G(b,32)) + +#define PCRE2_COMPILE(a,b,c,d,e,f,g) \ + if (test_mode == PCRE8_MODE) \ + G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,g); \ + else if (test_mode == PCRE16_MODE) \ + G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,g); \ + else \ + G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,g) + +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + if (test_mode == PCRE8_MODE) pcre2_converted_pattern_free_8((PCRE2_UCHAR8 *)a); \ + else if (test_mode == PCRE16_MODE) pcre2_converted_pattern_free_16((PCRE2_UCHAR16 *)a); \ + else pcre2_converted_pattern_free_32((PCRE2_UCHAR32 *)a) + +#define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_dfa_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h,i,j); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_dfa_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h,i,j); \ + else \ + a = pcre2_dfa_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h,i,j) + +#define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_get_error_message_8(a,G(b,8),G(G(b,8),_size)); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_get_error_message_16(a,G(b,16),G(G(b,16),_size/2)); \ + else \ + r = pcre2_get_error_message_32(a,G(b,32),G(G(b,32),_size/4)) + +#define PCRE2_GET_OVECTOR_COUNT(a,b) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_get_ovector_count_8(G(b,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_get_ovector_count_16(G(b,16)); \ + else \ + a = pcre2_get_ovector_count_32(G(b,32)) + +#define PCRE2_GET_STARTCHAR(a,b) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_get_startchar_8(G(b,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_get_startchar_16(G(b,16)); \ + else \ + a = pcre2_get_startchar_32(G(b,32)) + +#define PCRE2_JIT_COMPILE(r,a,b) \ + if (test_mode == PCRE8_MODE) r = pcre2_jit_compile_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) r = pcre2_jit_compile_16(G(a,16),b); \ + else r = pcre2_jit_compile_32(G(a,32),b) + +#define PCRE2_JIT_FREE_UNUSED_MEMORY(a) \ + if (test_mode == PCRE8_MODE) pcre2_jit_free_unused_memory_8(G(a,8)); \ + else if (test_mode == PCRE16_MODE) pcre2_jit_free_unused_memory_16(G(a,16)); \ + else pcre2_jit_free_unused_memory_32(G(a,32)) + +#define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_jit_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_jit_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h); \ + else \ + a = pcre2_jit_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) + +#define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ + if (test_mode == PCRE8_MODE) \ + a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_8(b,c,d); \ + else if (test_mode == PCRE16_MODE) \ + a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_16(b,c,d); \ + else \ + a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_32(b,c,d); + +#define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + pcre2_jit_stack_assign_8(G(a,8),(pcre2_jit_callback_8)b,c); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_jit_stack_assign_16(G(a,16),(pcre2_jit_callback_16)b,c); \ + else \ + pcre2_jit_stack_assign_32(G(a,32),(pcre2_jit_callback_32)b,c); + +#define PCRE2_JIT_STACK_FREE(a) \ + if (test_mode == PCRE8_MODE) \ + pcre2_jit_stack_free_8((pcre2_jit_stack_8 *)a); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_jit_stack_free_16((pcre2_jit_stack_16 *)a); \ + else \ + pcre2_jit_stack_free_32((pcre2_jit_stack_32 *)a); + +#define PCRE2_MAKETABLES(a) \ + if (test_mode == PCRE8_MODE) a = pcre2_maketables_8(NULL); \ + else if (test_mode == PCRE16_MODE) a = pcre2_maketables_16(NULL); \ + else a = pcre2_maketables_32(NULL) + +#define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h); \ + else \ + a = pcre2_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) + +#define PCRE2_MATCH_DATA_CREATE(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + G(a,8) = pcre2_match_data_create_8(b,c); \ + else if (test_mode == PCRE16_MODE) \ + G(a,16) = pcre2_match_data_create_16(b,c); \ + else \ + G(a,32) = pcre2_match_data_create_32(b,c) + +#define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + G(a,8) = pcre2_match_data_create_from_pattern_8(G(b,8),c); \ + else if (test_mode == PCRE16_MODE) \ + G(a,16) = pcre2_match_data_create_from_pattern_16(G(b,16),c); \ + else \ + G(a,32) = pcre2_match_data_create_from_pattern_32(G(b,32),c) + +#define PCRE2_MATCH_DATA_FREE(a) \ + if (test_mode == PCRE8_MODE) \ + pcre2_match_data_free_8(G(a,8)); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_match_data_free_16(G(a,16)); \ + else \ + pcre2_match_data_free_32(G(a,32)) + +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_pattern_convert_8(G(b,8),c,d,(PCRE2_UCHAR8 **)e,f,G(g,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_pattern_convert_16(G(b,16),c,d,(PCRE2_UCHAR16 **)e,f,G(g,16)); \ + else \ + a = pcre2_pattern_convert_32(G(b,32),c,d,(PCRE2_UCHAR32 **)e,f,G(g,32)) + +#define PCRE2_PATTERN_INFO(a,b,c,d) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_pattern_info_8(G(b,8),c,d); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_pattern_info_16(G(b,16),c,d); \ + else \ + a = pcre2_pattern_info_32(G(b,32),c,d) + +#define PCRE2_PRINTINT(a) \ + if (test_mode == PCRE8_MODE) \ + pcre2_printint_8(compiled_code8,outfile,a); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_printint_16(compiled_code16,outfile,a); \ + else \ + pcre2_printint_32(compiled_code32,outfile,a) + +#define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_serialize_decode_8((pcre2_code_8 **)a,b,c,G(d,8)); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_serialize_decode_16((pcre2_code_16 **)a,b,c,G(d,16)); \ + else \ + r = pcre2_serialize_decode_32((pcre2_code_32 **)a,b,c,G(d,32)) + +#define PCRE2_SERIALIZE_ENCODE(r,a,b,c,d,e) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_serialize_encode_8((const pcre2_code_8 **)a,b,c,d,G(e,8)); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_serialize_encode_16((const pcre2_code_16 **)a,b,c,d,G(e,16)); \ + else \ + r = pcre2_serialize_encode_32((const pcre2_code_32 **)a,b,c,d,G(e,32)) + +#define PCRE2_SERIALIZE_FREE(a) \ + if (test_mode == PCRE8_MODE) \ + pcre2_serialize_free_8(a); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_serialize_free_16(a); \ + else \ + pcre2_serialize_free_32(a) + +#define PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(r,a) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_serialize_get_number_of_codes_8(a); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_serialize_get_number_of_codes_16(a); \ + else \ + r = pcre2_serialize_get_number_of_codes_32(a); \ + +#define PCRE2_SET_CALLOUT(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_callout_8(G(a,8),(int (*)(pcre2_callout_block_8 *, void *))b,c); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_callout_16(G(a,16),(int (*)(pcre2_callout_block_16 *, void *))b,c); \ + else \ + pcre2_set_callout_32(G(a,32),(int (*)(pcre2_callout_block_32 *, void *))b,c); + +#define PCRE2_SET_CHARACTER_TABLES(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_character_tables_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_character_tables_16(G(a,16),b); \ + else \ + pcre2_set_character_tables_32(G(a,32),b) + +#define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_compile_recursion_guard_8(G(a,8),b,c); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_compile_recursion_guard_16(G(a,16),b,c); \ + else \ + pcre2_set_compile_recursion_guard_32(G(a,32),b,c) + +#define PCRE2_SET_DEPTH_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_depth_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_depth_limit_16(G(a,16),b); \ + else \ + pcre2_set_depth_limit_32(G(a,32),b) + +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_set_glob_separator_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_set_glob_separator_16(G(a,16),b); \ + else \ + r = pcre2_set_glob_separator_32(G(a,32),b) + +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_set_glob_escape_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_set_glob_escape_16(G(a,16),b); \ + else \ + r = pcre2_set_glob_escape_32(G(a,32),b) + +#define PCRE2_SET_HEAP_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_heap_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_heap_limit_16(G(a,16),b); \ + else \ + pcre2_set_heap_limit_32(G(a,32),b) + +#define PCRE2_SET_MATCH_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_match_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_match_limit_16(G(a,16),b); \ + else \ + pcre2_set_match_limit_32(G(a,32),b) + +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_max_pattern_length_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_max_pattern_length_16(G(a,16),b); \ + else \ + pcre2_set_max_pattern_length_32(G(a,32),b) + +#define PCRE2_SET_OFFSET_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_offset_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_offset_limit_16(G(a,16),b); \ + else \ + pcre2_set_offset_limit_32(G(a,32),b) + +#define PCRE2_SET_PARENS_NEST_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_parens_nest_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_parens_nest_limit_16(G(a,16),b); \ + else \ + pcre2_set_parens_nest_limit_32(G(a,32),b) + +#define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substitute_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8), \ + (PCRE2_SPTR8)i,j,(PCRE2_UCHAR8 *)k,l); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substitute_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16), \ + (PCRE2_SPTR16)i,j,(PCRE2_UCHAR16 *)k,l); \ + else \ + a = pcre2_substitute_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32), \ + (PCRE2_SPTR32)i,j,(PCRE2_UCHAR32 *)k,l) + +#define PCRE2_SUBSTRING_COPY_BYNAME(a,b,c,d,e) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_copy_byname_8(G(b,8),G(c,8),(PCRE2_UCHAR8 *)d,e); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_copy_byname_16(G(b,16),G(c,16),(PCRE2_UCHAR16 *)d,e); \ + else \ + a = pcre2_substring_copy_byname_32(G(b,32),G(c,32),(PCRE2_UCHAR32 *)d,e) + +#define PCRE2_SUBSTRING_COPY_BYNUMBER(a,b,c,d,e) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_copy_bynumber_8(G(b,8),c,(PCRE2_UCHAR8 *)d,e); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_copy_bynumber_16(G(b,16),c,(PCRE2_UCHAR16 *)d,e); \ + else \ + a = pcre2_substring_copy_bynumber_32(G(b,32),c,(PCRE2_UCHAR32 *)d,e) + +#define PCRE2_SUBSTRING_FREE(a) \ + if (test_mode == PCRE8_MODE) pcre2_substring_free_8((PCRE2_UCHAR8 *)a); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_substring_free_16((PCRE2_UCHAR16 *)a); \ + else pcre2_substring_free_32((PCRE2_UCHAR32 *)a) + +#define PCRE2_SUBSTRING_GET_BYNAME(a,b,c,d,e) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_get_byname_8(G(b,8),G(c,8),(PCRE2_UCHAR8 **)d,e); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_get_byname_16(G(b,16),G(c,16),(PCRE2_UCHAR16 **)d,e); \ + else \ + a = pcre2_substring_get_byname_32(G(b,32),G(c,32),(PCRE2_UCHAR32 **)d,e) + +#define PCRE2_SUBSTRING_GET_BYNUMBER(a,b,c,d,e) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_get_bynumber_8(G(b,8),c,(PCRE2_UCHAR8 **)d,e); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_get_bynumber_16(G(b,16),c,(PCRE2_UCHAR16 **)d,e); \ + else \ + a = pcre2_substring_get_bynumber_32(G(b,32),c,(PCRE2_UCHAR32 **)d,e) + +#define PCRE2_SUBSTRING_LENGTH_BYNAME(a,b,c,d) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_length_byname_8(G(b,8),G(c,8),d); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_length_byname_16(G(b,16),G(c,16),d); \ + else \ + a = pcre2_substring_length_byname_32(G(b,32),G(c,32),d) + +#define PCRE2_SUBSTRING_LENGTH_BYNUMBER(a,b,c,d) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_length_bynumber_8(G(b,8),c,d); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_length_bynumber_16(G(b,16),c,d); \ + else \ + a = pcre2_substring_length_bynumber_32(G(b,32),c,d) + +#define PCRE2_SUBSTRING_LIST_GET(a,b,c,d) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_list_get_8(G(b,8),(PCRE2_UCHAR8 ***)c,d); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_list_get_16(G(b,16),(PCRE2_UCHAR16 ***)c,d); \ + else \ + a = pcre2_substring_list_get_32(G(b,32),(PCRE2_UCHAR32 ***)c,d) + +#define PCRE2_SUBSTRING_LIST_FREE(a) \ + if (test_mode == PCRE8_MODE) \ + pcre2_substring_list_free_8((PCRE2_SPTR8 *)a); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_substring_list_free_16((PCRE2_SPTR16 *)a); \ + else \ + pcre2_substring_list_free_32((PCRE2_SPTR32 *)a) + +#define PCRE2_SUBSTRING_NUMBER_FROM_NAME(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_substring_number_from_name_8(G(b,8),G(c,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_substring_number_from_name_16(G(b,16),G(c,16)); \ + else \ + a = pcre2_substring_number_from_name_32(G(b,32),G(c,32)) + +#define PTR(x) ( \ + (test_mode == PCRE8_MODE)? (void *)G(x,8) : \ + (test_mode == PCRE16_MODE)? (void *)G(x,16) : \ + (void *)G(x,32)) + +#define SETFLD(x,y,z) \ + if (test_mode == PCRE8_MODE) G(x,8)->y = z; \ + else if (test_mode == PCRE16_MODE) G(x,16)->y = z; \ + else G(x,32)->y = z + +#define SETFLDVEC(x,y,v,z) \ + if (test_mode == PCRE8_MODE) G(x,8)->y[v] = z; \ + else if (test_mode == PCRE16_MODE) G(x,16)->y[v] = z; \ + else G(x,32)->y[v] = z + +#define SETOP(x,y,z) \ + if (test_mode == PCRE8_MODE) G(x,8) z y; \ + else if (test_mode == PCRE16_MODE) G(x,16) z y; \ + else G(x,32) z y + +#define SETCASTPTR(x,y) \ + if (test_mode == PCRE8_MODE) \ + G(x,8) = (uint8_t *)(y); \ + else if (test_mode == PCRE16_MODE) \ + G(x,16) = (uint16_t *)(y); \ + else \ + G(x,32) = (uint32_t *)(y) + +#define STRLEN(p) ((test_mode == PCRE8_MODE)? ((int)strlen((char *)p)) : \ + (test_mode == PCRE16_MODE)? ((int)strlen16((PCRE2_SPTR16)p)) : \ + ((int)strlen32((PCRE2_SPTR32)p))) + +#define SUB1(a,b) \ + if (test_mode == PCRE8_MODE) G(a,8)(G(b,8)); \ + else if (test_mode == PCRE16_MODE) G(a,16)(G(b,16)); \ + else G(a,32)(G(b,32)) + +#define SUB2(a,b,c) \ + if (test_mode == PCRE8_MODE) G(a,8)(G(b,8),G(c,8)); \ + else if (test_mode == PCRE16_MODE) G(a,16)(G(b,16),G(c,16)); \ + else G(a,32)(G(b,32),G(c,32)) + +#define TEST(x,r,y) ( \ + (test_mode == PCRE8_MODE && G(x,8) r (y)) || \ + (test_mode == PCRE16_MODE && G(x,16) r (y)) || \ + (test_mode == PCRE32_MODE && G(x,32) r (y))) + +#define TESTFLD(x,f,r,y) ( \ + (test_mode == PCRE8_MODE && G(x,8)->f r (y)) || \ + (test_mode == PCRE16_MODE && G(x,16)->f r (y)) || \ + (test_mode == PCRE32_MODE && G(x,32)->f r (y))) + + +/* ----- Two out of three modes are supported ----- */ + +#else + +/* We can use some macro trickery to make a single set of definitions work in +the three different cases. */ + +/* ----- 32-bit and 16-bit but not 8-bit supported ----- */ + +#if defined(SUPPORT_PCRE2_32) && defined(SUPPORT_PCRE2_16) +#define BITONE 32 +#define BITTWO 16 + +/* ----- 32-bit and 8-bit but not 16-bit supported ----- */ + +#elif defined(SUPPORT_PCRE2_32) && defined(SUPPORT_PCRE2_8) +#define BITONE 32 +#define BITTWO 8 + +/* ----- 16-bit and 8-bit but not 32-bit supported ----- */ + +#else +#define BITONE 16 +#define BITTWO 8 +#endif + + +/* ----- Common macros for two-mode cases ----- */ + +#define BYTEONE (BITONE/8) +#define BYTETWO (BITTWO/8) + +#define CASTFLD(t,a,b) \ + ((test_mode == G(G(PCRE,BITONE),_MODE))? (t)(G(a,BITONE)->b) : \ + (t)(G(a,BITTWO)->b)) + +#define CASTVAR(t,x) ( \ + (test_mode == G(G(PCRE,BITONE),_MODE))? \ + (t)G(x,BITONE) : (t)G(x,BITTWO)) + +#define CODE_UNIT(a,b) ( \ + (test_mode == G(G(PCRE,BITONE),_MODE))? \ + (uint32_t)(((G(PCRE2_SPTR,BITONE))(a))[b]) : \ + (uint32_t)(((G(PCRE2_SPTR,BITTWO))(a))[b])) + +#define CONCTXCPY(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + memcpy(G(a,BITONE),G(b,BITONE),sizeof(G(pcre2_convert_context_,BITONE))); \ + else \ + memcpy(G(a,BITTWO),G(b,BITTWO),sizeof(G(pcre2_convert_context_,BITTWO))) + +#define CONVERT_COPY(a,b,c) \ + (test_mode == G(G(PCRE,BITONE),_MODE))? \ + memcpy(G(a,BITONE),(char *)b,(c)*BYTEONE) : \ + memcpy(G(a,BITTWO),(char *)b,(c)*BYTETWO) + +#define DATCTXCPY(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + memcpy(G(a,BITONE),G(b,BITONE),sizeof(G(pcre2_match_context_,BITONE))); \ + else \ + memcpy(G(a,BITTWO),G(b,BITTWO),sizeof(G(pcre2_match_context_,BITTWO))) + +#define FLD(a,b) \ + ((test_mode == G(G(PCRE,BITONE),_MODE))? G(a,BITONE)->b : G(a,BITTWO)->b) + +#define PATCTXCPY(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + memcpy(G(a,BITONE),G(b,BITONE),sizeof(G(pcre2_compile_context_,BITONE))); \ + else \ + memcpy(G(a,BITTWO),G(b,BITTWO),sizeof(G(pcre2_compile_context_,BITTWO))) + +#define PCHARS(lv, p, offset, len, utf, f) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + lv = G(pchars,BITONE)((G(PCRE2_SPTR,BITONE))(p)+offset, len, utf, f); \ + else \ + lv = G(pchars,BITTWO)((G(PCRE2_SPTR,BITTWO))(p)+offset, len, utf, f) + +#define PCHARSV(p, offset, len, utf, f) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + (void)G(pchars,BITONE)((G(PCRE2_SPTR,BITONE))(p)+offset, len, utf, f); \ + else \ + (void)G(pchars,BITTWO)((G(PCRE2_SPTR,BITTWO))(p)+offset, len, utf, f) + +#define PCRE2_CALLOUT_ENUMERATE(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_callout_enumerate,BITONE)(G(compiled_code,BITONE), \ + (int (*)(struct G(pcre2_callout_enumerate_block_,BITONE) *, void *))b,c); \ + else \ + a = G(pcre2_callout_enumerate,BITTWO)(G(compiled_code,BITTWO), \ + (int (*)(struct G(pcre2_callout_enumerate_block_,BITTWO) *, void *))b,c) + +#define PCRE2_CODE_COPY_FROM_VOID(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(a,BITONE) = G(pcre2_code_copy_,BITONE)(b); \ + else \ + G(a,BITTWO) = G(pcre2_code_copy_,BITTWO)(b) + +#define PCRE2_CODE_COPY_TO_VOID(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = (void *)G(pcre2_code_copy_,BITONE)(G(b,BITONE)); \ + else \ + a = (void *)G(pcre2_code_copy_,BITTWO)(G(b,BITTWO)) + +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = (void *)G(pcre2_code_copy_with_tables_,BITONE)(G(b,BITONE)); \ + else \ + a = (void *)G(pcre2_code_copy_with_tables_,BITTWO)(G(b,BITTWO)) + +#define PCRE2_COMPILE(a,b,c,d,e,f,g) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(a,BITONE) = G(pcre2_compile_,BITONE)(G(b,BITONE),c,d,e,f,g); \ + else \ + G(a,BITTWO) = G(pcre2_compile_,BITTWO)(G(b,BITTWO),c,d,e,f,g) + +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_converted_pattern_free_,BITONE)((G(PCRE2_UCHAR,BITONE) *)a); \ + else \ + G(pcre2_converted_pattern_free_,BITTWO)((G(PCRE2_UCHAR,BITTWO) *)a) + +#define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_dfa_match_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ + G(g,BITONE),h,i,j); \ + else \ + a = G(pcre2_dfa_match_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ + G(g,BITTWO),h,i,j) + +#define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_get_error_message_,BITONE)(a,G(b,BITONE),G(G(b,BITONE),_size/BYTEONE)); \ + else \ + r = G(pcre2_get_error_message_,BITTWO)(a,G(b,BITTWO),G(G(b,BITTWO),_size/BYTETWO)) + +#define PCRE2_GET_OVECTOR_COUNT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_get_ovector_count_,BITONE)(G(b,BITONE)); \ + else \ + a = G(pcre2_get_ovector_count_,BITTWO)(G(b,BITTWO)) + +#define PCRE2_GET_STARTCHAR(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_get_startchar_,BITONE)(G(b,BITONE)); \ + else \ + a = G(pcre2_get_startchar_,BITTWO)(G(b,BITTWO)) + +#define PCRE2_JIT_COMPILE(r,a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_jit_compile_,BITONE)(G(a,BITONE),b); \ + else \ + r = G(pcre2_jit_compile_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_JIT_FREE_UNUSED_MEMORY(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_jit_free_unused_memory_,BITONE)(G(a,BITONE)); \ + else \ + G(pcre2_jit_free_unused_memory_,BITTWO)(G(a,BITTWO)) + +#define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_jit_match_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ + G(g,BITONE),h); \ + else \ + a = G(pcre2_jit_match_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ + G(g,BITTWO),h) + +#define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = (PCRE2_JIT_STACK *)G(pcre2_jit_stack_create_,BITONE)(b,c,d); \ + else \ + a = (PCRE2_JIT_STACK *)G(pcre2_jit_stack_create_,BITTWO)(b,c,d); \ + +#define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_jit_stack_assign_,BITONE)(G(a,BITONE),(G(pcre2_jit_callback_,BITONE))b,c); \ + else \ + G(pcre2_jit_stack_assign_,BITTWO)(G(a,BITTWO),(G(pcre2_jit_callback_,BITTWO))b,c); + +#define PCRE2_JIT_STACK_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_jit_stack_free_,BITONE)((G(pcre2_jit_stack_,BITONE) *)a); \ + else \ + G(pcre2_jit_stack_free_,BITTWO)((G(pcre2_jit_stack_,BITTWO) *)a); + +#define PCRE2_MAKETABLES(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_maketables_,BITONE)(NULL); \ + else \ + a = G(pcre2_maketables_,BITTWO)(NULL) + +#define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_match_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ + G(g,BITONE),h); \ + else \ + a = G(pcre2_match_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ + G(g,BITTWO),h) + +#define PCRE2_MATCH_DATA_CREATE(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(a,BITONE) = G(pcre2_match_data_create_,BITONE)(b,c); \ + else \ + G(a,BITTWO) = G(pcre2_match_data_create_,BITTWO)(b,c) + +#define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(a,BITONE) = G(pcre2_match_data_create_from_pattern_,BITONE)(G(b,BITONE),c); \ + else \ + G(a,BITTWO) = G(pcre2_match_data_create_from_pattern_,BITTWO)(G(b,BITTWO),c) + +#define PCRE2_MATCH_DATA_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_match_data_free_,BITONE)(G(a,BITONE)); \ + else \ + G(pcre2_match_data_free_,BITTWO)(G(a,BITTWO)) + +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_pattern_convert_,BITONE)(G(b,BITONE),c,d,(G(PCRE2_UCHAR,BITONE) **)e,f,G(g,BITONE)); \ + else \ + a = G(pcre2_pattern_convert_,BITTWO)(G(b,BITTWO),c,d,(G(PCRE2_UCHAR,BITTWO) **)e,f,G(g,BITTWO)) + +#define PCRE2_PATTERN_INFO(a,b,c,d) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_pattern_info_,BITONE)(G(b,BITONE),c,d); \ + else \ + a = G(pcre2_pattern_info_,BITTWO)(G(b,BITTWO),c,d) + +#define PCRE2_PRINTINT(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_printint_,BITONE)(G(compiled_code,BITONE),outfile,a); \ + else \ + G(pcre2_printint_,BITTWO)(G(compiled_code,BITTWO),outfile,a) + +#define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_serialize_decode_,BITONE)((G(pcre2_code_,BITONE) **)a,b,c,G(d,BITONE)); \ + else \ + r = G(pcre2_serialize_decode_,BITTWO)((G(pcre2_code_,BITTWO) **)a,b,c,G(d,BITTWO)) + +#define PCRE2_SERIALIZE_ENCODE(r,a,b,c,d,e) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_serialize_encode_,BITONE)((G(const pcre2_code_,BITONE) **)a,b,c,d,G(e,BITONE)); \ + else \ + r = G(pcre2_serialize_encode_,BITTWO)((G(const pcre2_code_,BITTWO) **)a,b,c,d,G(e,BITTWO)) + +#define PCRE2_SERIALIZE_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_serialize_free_,BITONE)(a); \ + else \ + G(pcre2_serialize_free_,BITTWO)(a) + +#define PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(r,a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_serialize_get_number_of_codes_,BITONE)(a); \ + else \ + r = G(pcre2_serialize_get_number_of_codes_,BITTWO)(a) + +#define PCRE2_SET_CALLOUT(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_callout_,BITONE)(G(a,BITONE), \ + (int (*)(G(pcre2_callout_block_,BITONE) *, void *))b,c); \ + else \ + G(pcre2_set_callout_,BITTWO)(G(a,BITTWO), \ + (int (*)(G(pcre2_callout_block_,BITTWO) *, void *))b,c); + +#define PCRE2_SET_CHARACTER_TABLES(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_character_tables_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_character_tables_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_compile_recursion_guard_,BITONE)(G(a,BITONE),b,c); \ + else \ + G(pcre2_set_compile_recursion_guard_,BITTWO)(G(a,BITTWO),b,c) + +#define PCRE2_SET_DEPTH_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_depth_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_depth_limit_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_set_glob_escape_,BITONE)(G(a,BITONE),b); \ + else \ + r = G(pcre2_set_glob_escape_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_set_glob_separator_,BITONE)(G(a,BITONE),b); \ + else \ + r = G(pcre2_set_glob_separator_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_HEAP_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_heap_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_heap_limit_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_MATCH_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_match_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_match_limit_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_max_pattern_length_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_max_pattern_length_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_OFFSET_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_offset_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_offset_limit_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_PARENS_NEST_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_parens_nest_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_parens_nest_limit_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substitute_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ + G(g,BITONE),G(h,BITONE),(G(PCRE2_SPTR,BITONE))i,j, \ + (G(PCRE2_UCHAR,BITONE) *)k,l); \ + else \ + a = G(pcre2_substitute_,BITTWO)(G(b,BITTWO),(G(PCRE2_SPTR,BITTWO))c,d,e,f, \ + G(g,BITTWO),G(h,BITTWO),(G(PCRE2_SPTR,BITTWO))i,j, \ + (G(PCRE2_UCHAR,BITTWO) *)k,l) + +#define PCRE2_SUBSTRING_COPY_BYNAME(a,b,c,d,e) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_copy_byname_,BITONE)(G(b,BITONE),G(c,BITONE),\ + (G(PCRE2_UCHAR,BITONE) *)d,e); \ + else \ + a = G(pcre2_substring_copy_byname_,BITTWO)(G(b,BITTWO),G(c,BITTWO),\ + (G(PCRE2_UCHAR,BITTWO) *)d,e) + +#define PCRE2_SUBSTRING_COPY_BYNUMBER(a,b,c,d,e) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_copy_bynumber_,BITONE)(G(b,BITONE),c,\ + (G(PCRE2_UCHAR,BITONE) *)d,e); \ + else \ + a = G(pcre2_substring_copy_bynumber_,BITTWO)(G(b,BITTWO),c,\ + (G(PCRE2_UCHAR,BITTWO) *)d,e) + +#define PCRE2_SUBSTRING_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_substring_free_,BITONE)((G(PCRE2_UCHAR,BITONE) *)a); \ + else G(pcre2_substring_free_,BITTWO)((G(PCRE2_UCHAR,BITTWO) *)a) + +#define PCRE2_SUBSTRING_GET_BYNAME(a,b,c,d,e) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_get_byname_,BITONE)(G(b,BITONE),G(c,BITONE),\ + (G(PCRE2_UCHAR,BITONE) **)d,e); \ + else \ + a = G(pcre2_substring_get_byname_,BITTWO)(G(b,BITTWO),G(c,BITTWO),\ + (G(PCRE2_UCHAR,BITTWO) **)d,e) + +#define PCRE2_SUBSTRING_GET_BYNUMBER(a,b,c,d,e) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_get_bynumber_,BITONE)(G(b,BITONE),c,\ + (G(PCRE2_UCHAR,BITONE) **)d,e); \ + else \ + a = G(pcre2_substring_get_bynumber_,BITTWO)(G(b,BITTWO),c,\ + (G(PCRE2_UCHAR,BITTWO) **)d,e) + +#define PCRE2_SUBSTRING_LENGTH_BYNAME(a,b,c,d) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_length_byname_,BITONE)(G(b,BITONE),G(c,BITONE),d); \ + else \ + a = G(pcre2_substring_length_byname_,BITTWO)(G(b,BITTWO),G(c,BITTWO),d) + +#define PCRE2_SUBSTRING_LENGTH_BYNUMBER(a,b,c,d) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_length_bynumber_,BITONE)(G(b,BITONE),c,d); \ + else \ + a = G(pcre2_substring_length_bynumber_,BITTWO)(G(b,BITTWO),c,d) + +#define PCRE2_SUBSTRING_LIST_GET(a,b,c,d) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_list_get_,BITONE)(G(b,BITONE), \ + (G(PCRE2_UCHAR,BITONE) ***)c,d); \ + else \ + a = G(pcre2_substring_list_get_,BITTWO)(G(b,BITTWO), \ + (G(PCRE2_UCHAR,BITTWO) ***)c,d) + +#define PCRE2_SUBSTRING_LIST_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_substring_list_free_,BITONE)((G(PCRE2_SPTR,BITONE) *)a); \ + else \ + G(pcre2_substring_list_free_,BITTWO)((G(PCRE2_SPTR,BITTWO) *)a) + +#define PCRE2_SUBSTRING_NUMBER_FROM_NAME(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_substring_number_from_name_,BITONE)(G(b,BITONE),G(c,BITONE)); \ + else \ + a = G(pcre2_substring_number_from_name_,BITTWO)(G(b,BITTWO),G(c,BITTWO)) + +#define PTR(x) ( \ + (test_mode == G(G(PCRE,BITONE),_MODE))? (void *)G(x,BITONE) : \ + (void *)G(x,BITTWO)) + +#define SETFLD(x,y,z) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) G(x,BITONE)->y = z; \ + else G(x,BITTWO)->y = z + +#define SETFLDVEC(x,y,v,z) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) G(x,BITONE)->y[v] = z; \ + else G(x,BITTWO)->y[v] = z + +#define SETOP(x,y,z) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) G(x,BITONE) z y; \ + else G(x,BITTWO) z y + +#define SETCASTPTR(x,y) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(x,BITONE) = (G(G(uint,BITONE),_t) *)(y); \ + else \ + G(x,BITTWO) = (G(G(uint,BITTWO),_t) *)(y) + +#define STRLEN(p) ((test_mode == G(G(PCRE,BITONE),_MODE))? \ + G(strlen,BITONE)((G(PCRE2_SPTR,BITONE))p) : \ + G(strlen,BITTWO)((G(PCRE2_SPTR,BITTWO))p)) + +#define SUB1(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(a,BITONE)(G(b,BITONE)); \ + else \ + G(a,BITTWO)(G(b,BITTWO)) + +#define SUB2(a,b,c) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(a,BITONE))(G(b,BITONE),G(c,BITONE)); \ + else \ + G(a,BITTWO))(G(b,BITTWO),G(c,BITTWO)) + +#define TEST(x,r,y) ( \ + (test_mode == G(G(PCRE,BITONE),_MODE) && G(x,BITONE) r (y)) || \ + (test_mode == G(G(PCRE,BITTWO),_MODE) && G(x,BITTWO) r (y))) + +#define TESTFLD(x,f,r,y) ( \ + (test_mode == G(G(PCRE,BITONE),_MODE) && G(x,BITONE)->f r (y)) || \ + (test_mode == G(G(PCRE,BITTWO),_MODE) && G(x,BITTWO)->f r (y))) + + +#endif /* Two out of three modes */ + +/* ----- End of cases where more than one mode is supported ----- */ + + +/* ----- Only 8-bit mode is supported ----- */ + +#elif defined SUPPORT_PCRE2_8 +#define CASTFLD(t,a,b) (t)(G(a,8)->b) +#define CASTVAR(t,x) (t)G(x,8) +#define CODE_UNIT(a,b) (uint32_t)(((PCRE2_SPTR8)(a))[b]) +#define CONCTXCPY(a,b) memcpy(G(a,8),G(b,8),sizeof(pcre2_convert_context_8)) +#define CONVERT_COPY(a,b,c) memcpy(G(a,8),(char *)b, c) +#define DATCTXCPY(a,b) memcpy(G(a,8),G(b,8),sizeof(pcre2_match_context_8)) +#define FLD(a,b) G(a,8)->b +#define PATCTXCPY(a,b) memcpy(G(a,8),G(b,8),sizeof(pcre2_compile_context_8)) +#define PCHARS(lv, p, offset, len, utf, f) \ + lv = pchars8((PCRE2_SPTR8)(p)+offset, len, utf, f) +#define PCHARSV(p, offset, len, utf, f) \ + (void)pchars8((PCRE2_SPTR8)(p)+offset, len, utf, f) +#define PCRE2_CALLOUT_ENUMERATE(a,b,c) \ + a = pcre2_callout_enumerate_8(compiled_code8, \ + (int (*)(struct pcre2_callout_enumerate_block_8 *, void *))b,c) +#define PCRE2_CODE_COPY_FROM_VOID(a,b) G(a,8) = pcre2_code_copy_8(b) +#define PCRE2_CODE_COPY_TO_VOID(a,b) a = (void *)pcre2_code_copy_8(G(b,8)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) a = (void *)pcre2_code_copy_with_tables_8(G(b,8)) +#define PCRE2_COMPILE(a,b,c,d,e,f,g) \ + G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + pcre2_converted_pattern_free_8((PCRE2_UCHAR8 *)a) +#define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ + a = pcre2_dfa_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h,i,j) +#define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ + r = pcre2_get_error_message_8(a,G(b,8),G(G(b,8),_size)) +#define PCRE2_GET_OVECTOR_COUNT(a,b) a = pcre2_get_ovector_count_8(G(b,8)) +#define PCRE2_GET_STARTCHAR(a,b) a = pcre2_get_startchar_8(G(b,8)) +#define PCRE2_JIT_COMPILE(r,a,b) r = pcre2_jit_compile_8(G(a,8),b) +#define PCRE2_JIT_FREE_UNUSED_MEMORY(a) pcre2_jit_free_unused_memory_8(G(a,8)) +#define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ + a = pcre2_jit_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h) +#define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ + a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_8(b,c,d); +#define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ + pcre2_jit_stack_assign_8(G(a,8),(pcre2_jit_callback_8)b,c); +#define PCRE2_JIT_STACK_FREE(a) pcre2_jit_stack_free_8((pcre2_jit_stack_8 *)a); +#define PCRE2_MAKETABLES(a) a = pcre2_maketables_8(NULL) +#define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ + a = pcre2_match_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),h) +#define PCRE2_MATCH_DATA_CREATE(a,b,c) G(a,8) = pcre2_match_data_create_8(b,c) +#define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ + G(a,8) = pcre2_match_data_create_from_pattern_8(G(b,8),c) +#define PCRE2_MATCH_DATA_FREE(a) pcre2_match_data_free_8(G(a,8)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) a = pcre2_pattern_convert_8(G(b,8),c,d,(PCRE2_UCHAR8 **)e,f,G(g,8)) +#define PCRE2_PATTERN_INFO(a,b,c,d) a = pcre2_pattern_info_8(G(b,8),c,d) +#define PCRE2_PRINTINT(a) pcre2_printint_8(compiled_code8,outfile,a) +#define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ + r = pcre2_serialize_decode_8((pcre2_code_8 **)a,b,c,G(d,8)) +#define PCRE2_SERIALIZE_ENCODE(r,a,b,c,d,e) \ + r = pcre2_serialize_encode_8((const pcre2_code_8 **)a,b,c,d,G(e,8)) +#define PCRE2_SERIALIZE_FREE(a) pcre2_serialize_free_8(a) +#define PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(r,a) \ + r = pcre2_serialize_get_number_of_codes_8(a) +#define PCRE2_SET_CALLOUT(a,b,c) \ + pcre2_set_callout_8(G(a,8),(int (*)(pcre2_callout_block_8 *, void *))b,c) +#define PCRE2_SET_CHARACTER_TABLES(a,b) pcre2_set_character_tables_8(G(a,8),b) +#define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ + pcre2_set_compile_recursion_guard_8(G(a,8),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) pcre2_set_depth_limit_8(G(a,8),b) +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) r = pcre2_set_glob_escape_8(G(a,8),b) +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) r = pcre2_set_glob_separator_8(G(a,8),b) +#define PCRE2_SET_HEAP_LIMIT(a,b) pcre2_set_heap_limit_8(G(a,8),b) +#define PCRE2_SET_MATCH_LIMIT(a,b) pcre2_set_match_limit_8(G(a,8),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) pcre2_set_max_pattern_length_8(G(a,8),b) +#define PCRE2_SET_OFFSET_LIMIT(a,b) pcre2_set_offset_limit_8(G(a,8),b) +#define PCRE2_SET_PARENS_NEST_LIMIT(a,b) pcre2_set_parens_nest_limit_8(G(a,8),b) +#define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ + a = pcre2_substitute_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8), \ + (PCRE2_SPTR8)i,j,(PCRE2_UCHAR8 *)k,l) +#define PCRE2_SUBSTRING_COPY_BYNAME(a,b,c,d,e) \ + a = pcre2_substring_copy_byname_8(G(b,8),G(c,8),(PCRE2_UCHAR8 *)d,e) +#define PCRE2_SUBSTRING_COPY_BYNUMBER(a,b,c,d,e) \ + a = pcre2_substring_copy_bynumber_8(G(b,8),c,(PCRE2_UCHAR8 *)d,e) +#define PCRE2_SUBSTRING_FREE(a) pcre2_substring_free_8((PCRE2_UCHAR8 *)a) +#define PCRE2_SUBSTRING_GET_BYNAME(a,b,c,d,e) \ + a = pcre2_substring_get_byname_8(G(b,8),G(c,8),(PCRE2_UCHAR8 **)d,e) +#define PCRE2_SUBSTRING_GET_BYNUMBER(a,b,c,d,e) \ + a = pcre2_substring_get_bynumber_8(G(b,8),c,(PCRE2_UCHAR8 **)d,e) +#define PCRE2_SUBSTRING_LENGTH_BYNAME(a,b,c,d) \ + a = pcre2_substring_length_byname_8(G(b,8),G(c,8),d) +#define PCRE2_SUBSTRING_LENGTH_BYNUMBER(a,b,c,d) \ + a = pcre2_substring_length_bynumber_8(G(b,8),c,d) +#define PCRE2_SUBSTRING_LIST_GET(a,b,c,d) \ + a = pcre2_substring_list_get_8(G(b,8),(PCRE2_UCHAR8 ***)c,d) +#define PCRE2_SUBSTRING_LIST_FREE(a) \ + pcre2_substring_list_free_8((PCRE2_SPTR8 *)a) +#define PCRE2_SUBSTRING_NUMBER_FROM_NAME(a,b,c) \ + a = pcre2_substring_number_from_name_8(G(b,8),G(c,8)); +#define PTR(x) (void *)G(x,8) +#define SETFLD(x,y,z) G(x,8)->y = z +#define SETFLDVEC(x,y,v,z) G(x,8)->y[v] = z +#define SETOP(x,y,z) G(x,8) z y +#define SETCASTPTR(x,y) G(x,8) = (uint8_t *)(y) +#define STRLEN(p) (int)strlen((char *)p) +#define SUB1(a,b) G(a,8)(G(b,8)) +#define SUB2(a,b,c) G(a,8)(G(b,8),G(c,8)) +#define TEST(x,r,y) (G(x,8) r (y)) +#define TESTFLD(x,f,r,y) (G(x,8)->f r (y)) + + +/* ----- Only 16-bit mode is supported ----- */ + +#elif defined SUPPORT_PCRE2_16 +#define CASTFLD(t,a,b) (t)(G(a,16)->b) +#define CASTVAR(t,x) (t)G(x,16) +#define CODE_UNIT(a,b) (uint32_t)(((PCRE2_SPTR16)(a))[b]) +#define CONCTXCPY(a,b) memcpy(G(a,16),G(b,16),sizeof(pcre2_convert_context_16)) +#define CONVERT_COPY(a,b,c) memcpy(G(a,16),(char *)b, (c)*2) +#define DATCTXCPY(a,b) memcpy(G(a,16),G(b,16),sizeof(pcre2_match_context_16)) +#define FLD(a,b) G(a,16)->b +#define PATCTXCPY(a,b) memcpy(G(a,16),G(b,16),sizeof(pcre2_compile_context_16)) +#define PCHARS(lv, p, offset, len, utf, f) \ + lv = pchars16((PCRE2_SPTR16)(p)+offset, len, utf, f) +#define PCHARSV(p, offset, len, utf, f) \ + (void)pchars16((PCRE2_SPTR16)(p)+offset, len, utf, f) +#define PCRE2_CALLOUT_ENUMERATE(a,b,c) \ + a = pcre2_callout_enumerate_16(compiled_code16, \ + (int (*)(struct pcre2_callout_enumerate_block_16 *, void *))b,c) +#define PCRE2_CODE_COPY_FROM_VOID(a,b) G(a,16) = pcre2_code_copy_16(b) +#define PCRE2_CODE_COPY_TO_VOID(a,b) a = (void *)pcre2_code_copy_16(G(b,16)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) a = (void *)pcre2_code_copy_with_tables_16(G(b,16)) +#define PCRE2_COMPILE(a,b,c,d,e,f,g) \ + G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + pcre2_converted_pattern_free_16((PCRE2_UCHAR16 *)a) +#define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ + a = pcre2_dfa_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h,i,j) +#define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ + r = pcre2_get_error_message_16(a,G(b,16),G(G(b,16),_size/2)) +#define PCRE2_GET_OVECTOR_COUNT(a,b) a = pcre2_get_ovector_count_16(G(b,16)) +#define PCRE2_GET_STARTCHAR(a,b) a = pcre2_get_startchar_16(G(b,16)) +#define PCRE2_JIT_COMPILE(r,a,b) r = pcre2_jit_compile_16(G(a,16),b) +#define PCRE2_JIT_FREE_UNUSED_MEMORY(a) pcre2_jit_free_unused_memory_16(G(a,16)) +#define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ + a = pcre2_jit_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h) +#define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ + a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_16(b,c,d); +#define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ + pcre2_jit_stack_assign_16(G(a,16),(pcre2_jit_callback_16)b,c); +#define PCRE2_JIT_STACK_FREE(a) pcre2_jit_stack_free_16((pcre2_jit_stack_16 *)a); +#define PCRE2_MAKETABLES(a) a = pcre2_maketables_16(NULL) +#define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ + a = pcre2_match_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),h) +#define PCRE2_MATCH_DATA_CREATE(a,b,c) G(a,16) = pcre2_match_data_create_16(b,c) +#define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ + G(a,16) = pcre2_match_data_create_from_pattern_16(G(b,16),c) +#define PCRE2_MATCH_DATA_FREE(a) pcre2_match_data_free_16(G(a,16)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) a = pcre2_pattern_convert_16(G(b,16),c,d,(PCRE2_UCHAR16 **)e,f,G(g,16)) +#define PCRE2_PATTERN_INFO(a,b,c,d) a = pcre2_pattern_info_16(G(b,16),c,d) +#define PCRE2_PRINTINT(a) pcre2_printint_16(compiled_code16,outfile,a) +#define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ + r = pcre2_serialize_decode_16((pcre2_code_16 **)a,b,c,G(d,16)) +#define PCRE2_SERIALIZE_ENCODE(r,a,b,c,d,e) \ + r = pcre2_serialize_encode_16((const pcre2_code_16 **)a,b,c,d,G(e,16)) +#define PCRE2_SERIALIZE_FREE(a) pcre2_serialize_free_16(a) +#define PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(r,a) \ + r = pcre2_serialize_get_number_of_codes_16(a) +#define PCRE2_SET_CALLOUT(a,b,c) \ + pcre2_set_callout_16(G(a,16),(int (*)(pcre2_callout_block_16 *, void *))b,c); +#define PCRE2_SET_CHARACTER_TABLES(a,b) pcre2_set_character_tables_16(G(a,16),b) +#define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ + pcre2_set_compile_recursion_guard_16(G(a,16),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) pcre2_set_depth_limit_16(G(a,16),b) +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) r = pcre2_set_glob_escape_16(G(a,16),b) +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) r = pcre2_set_glob_separator_16(G(a,16),b) +#define PCRE2_SET_HEAP_LIMIT(a,b) pcre2_set_heap_limit_16(G(a,16),b) +#define PCRE2_SET_MATCH_LIMIT(a,b) pcre2_set_match_limit_16(G(a,16),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) pcre2_set_max_pattern_length_16(G(a,16),b) +#define PCRE2_SET_OFFSET_LIMIT(a,b) pcre2_set_offset_limit_16(G(a,16),b) +#define PCRE2_SET_PARENS_NEST_LIMIT(a,b) pcre2_set_parens_nest_limit_16(G(a,16),b) +#define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ + a = pcre2_substitute_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16), \ + (PCRE2_SPTR16)i,j,(PCRE2_UCHAR16 *)k,l) +#define PCRE2_SUBSTRING_COPY_BYNAME(a,b,c,d,e) \ + a = pcre2_substring_copy_byname_16(G(b,16),G(c,16),(PCRE2_UCHAR16 *)d,e) +#define PCRE2_SUBSTRING_COPY_BYNUMBER(a,b,c,d,e) \ + a = pcre2_substring_copy_bynumber_16(G(b,16),c,(PCRE2_UCHAR16 *)d,e) +#define PCRE2_SUBSTRING_FREE(a) pcre2_substring_free_16((PCRE2_UCHAR16 *)a) +#define PCRE2_SUBSTRING_GET_BYNAME(a,b,c,d,e) \ + a = pcre2_substring_get_byname_16(G(b,16),G(c,16),(PCRE2_UCHAR16 **)d,e) +#define PCRE2_SUBSTRING_GET_BYNUMBER(a,b,c,d,e) \ + a = pcre2_substring_get_bynumber_16(G(b,16),c,(PCRE2_UCHAR16 **)d,e) +#define PCRE2_SUBSTRING_LENGTH_BYNAME(a,b,c,d) \ + a = pcre2_substring_length_byname_16(G(b,16),G(c,16),d) +#define PCRE2_SUBSTRING_LENGTH_BYNUMBER(a,b,c,d) \ + a = pcre2_substring_length_bynumber_16(G(b,16),c,d) +#define PCRE2_SUBSTRING_LIST_GET(a,b,c,d) \ + a = pcre2_substring_list_get_16(G(b,16),(PCRE2_UCHAR16 ***)c,d) +#define PCRE2_SUBSTRING_LIST_FREE(a) \ + pcre2_substring_list_free_16((PCRE2_SPTR16 *)a) +#define PCRE2_SUBSTRING_NUMBER_FROM_NAME(a,b,c) \ + a = pcre2_substring_number_from_name_16(G(b,16),G(c,16)); +#define PTR(x) (void *)G(x,16) +#define SETFLD(x,y,z) G(x,16)->y = z +#define SETFLDVEC(x,y,v,z) G(x,16)->y[v] = z +#define SETOP(x,y,z) G(x,16) z y +#define SETCASTPTR(x,y) G(x,16) = (uint16_t *)(y) +#define STRLEN(p) (int)strlen16((PCRE2_SPTR16)p) +#define SUB1(a,b) G(a,16)(G(b,16)) +#define SUB2(a,b,c) G(a,16)(G(b,16),G(c,16)) +#define TEST(x,r,y) (G(x,16) r (y)) +#define TESTFLD(x,f,r,y) (G(x,16)->f r (y)) + + +/* ----- Only 32-bit mode is supported ----- */ + +#elif defined SUPPORT_PCRE2_32 +#define CASTFLD(t,a,b) (t)(G(a,32)->b) +#define CASTVAR(t,x) (t)G(x,32) +#define CODE_UNIT(a,b) (uint32_t)(((PCRE2_SPTR32)(a))[b]) +#define CONCTXCPY(a,b) memcpy(G(a,32),G(b,32),sizeof(pcre2_convert_context_32)) +#define CONVERT_COPY(a,b,c) memcpy(G(a,32),(char *)b, (c)*4) +#define DATCTXCPY(a,b) memcpy(G(a,32),G(b,32),sizeof(pcre2_match_context_32)) +#define FLD(a,b) G(a,32)->b +#define PATCTXCPY(a,b) memcpy(G(a,32),G(b,32),sizeof(pcre2_compile_context_32)) +#define PCHARS(lv, p, offset, len, utf, f) \ + lv = pchars32((PCRE2_SPTR32)(p)+offset, len, utf, f) +#define PCHARSV(p, offset, len, utf, f) \ + (void)pchars32((PCRE2_SPTR32)(p)+offset, len, utf, f) +#define PCRE2_CALLOUT_ENUMERATE(a,b,c) \ + a = pcre2_callout_enumerate_32(compiled_code32, \ + (int (*)(struct pcre2_callout_enumerate_block_32 *, void *))b,c) +#define PCRE2_CODE_COPY_FROM_VOID(a,b) G(a,32) = pcre2_code_copy_32(b) +#define PCRE2_CODE_COPY_TO_VOID(a,b) a = (void *)pcre2_code_copy_32(G(b,32)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) a = (void *)pcre2_code_copy_with_tables_32(G(b,32)) +#define PCRE2_COMPILE(a,b,c,d,e,f,g) \ + G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + pcre2_converted_pattern_free_32((PCRE2_UCHAR32 *)a) +#define PCRE2_DFA_MATCH(a,b,c,d,e,f,g,h,i,j) \ + a = pcre2_dfa_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h,i,j) +#define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ + r = pcre2_get_error_message_32(a,G(b,32),G(G(b,32),_size/4)) +#define PCRE2_GET_OVECTOR_COUNT(a,b) a = pcre2_get_ovector_count_32(G(b,32)) +#define PCRE2_GET_STARTCHAR(a,b) a = pcre2_get_startchar_32(G(b,32)) +#define PCRE2_JIT_COMPILE(r,a,b) r = pcre2_jit_compile_32(G(a,32),b) +#define PCRE2_JIT_FREE_UNUSED_MEMORY(a) pcre2_jit_free_unused_memory_32(G(a,32)) +#define PCRE2_JIT_MATCH(a,b,c,d,e,f,g,h) \ + a = pcre2_jit_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) +#define PCRE2_JIT_STACK_CREATE(a,b,c,d) \ + a = (PCRE2_JIT_STACK *)pcre2_jit_stack_create_32(b,c,d); +#define PCRE2_JIT_STACK_ASSIGN(a,b,c) \ + pcre2_jit_stack_assign_32(G(a,32),(pcre2_jit_callback_32)b,c); +#define PCRE2_JIT_STACK_FREE(a) pcre2_jit_stack_free_32((pcre2_jit_stack_32 *)a); +#define PCRE2_MAKETABLES(a) a = pcre2_maketables_32(NULL) +#define PCRE2_MATCH(a,b,c,d,e,f,g,h) \ + a = pcre2_match_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),h) +#define PCRE2_MATCH_DATA_CREATE(a,b,c) G(a,32) = pcre2_match_data_create_32(b,c) +#define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ + G(a,32) = pcre2_match_data_create_from_pattern_32(G(b,32),c) +#define PCRE2_MATCH_DATA_FREE(a) pcre2_match_data_free_32(G(a,32)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) a = pcre2_pattern_convert_32(G(b,32),c,d,(PCRE2_UCHAR32 **)e,f,G(g,32)) +#define PCRE2_PATTERN_INFO(a,b,c,d) a = pcre2_pattern_info_32(G(b,32),c,d) +#define PCRE2_PRINTINT(a) pcre2_printint_32(compiled_code32,outfile,a) +#define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ + r = pcre2_serialize_decode_32((pcre2_code_32 **)a,b,c,G(d,32)) +#define PCRE2_SERIALIZE_ENCODE(r,a,b,c,d,e) \ + r = pcre2_serialize_encode_32((const pcre2_code_32 **)a,b,c,d,G(e,32)) +#define PCRE2_SERIALIZE_FREE(a) pcre2_serialize_free_32(a) +#define PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(r,a) \ + r = pcre2_serialize_get_number_of_codes_32(a) +#define PCRE2_SET_CALLOUT(a,b,c) \ + pcre2_set_callout_32(G(a,32),(int (*)(pcre2_callout_block_32 *, void *))b,c); +#define PCRE2_SET_CHARACTER_TABLES(a,b) pcre2_set_character_tables_32(G(a,32),b) +#define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ + pcre2_set_compile_recursion_guard_32(G(a,32),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) pcre2_set_depth_limit_32(G(a,32),b) +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) r = pcre2_set_glob_escape_32(G(a,32),b) +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) r = pcre2_set_glob_separator_32(G(a,32),b) +#define PCRE2_SET_HEAP_LIMIT(a,b) pcre2_set_heap_limit_32(G(a,32),b) +#define PCRE2_SET_MATCH_LIMIT(a,b) pcre2_set_match_limit_32(G(a,32),b) +#define PCRE2_SET_MAX_PATTERN_LENGTH(a,b) pcre2_set_max_pattern_length_32(G(a,32),b) +#define PCRE2_SET_OFFSET_LIMIT(a,b) pcre2_set_offset_limit_32(G(a,32),b) +#define PCRE2_SET_PARENS_NEST_LIMIT(a,b) pcre2_set_parens_nest_limit_32(G(a,32),b) +#define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ + a = pcre2_substitute_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32), \ + (PCRE2_SPTR32)i,j,(PCRE2_UCHAR32 *)k,l) +#define PCRE2_SUBSTRING_COPY_BYNAME(a,b,c,d,e) \ + a = pcre2_substring_copy_byname_32(G(b,32),G(c,32),(PCRE2_UCHAR32 *)d,e) +#define PCRE2_SUBSTRING_COPY_BYNUMBER(a,b,c,d,e) \ + a = pcre2_substring_copy_bynumber_32(G(b,32),c,(PCRE2_UCHAR32 *)d,e); +#define PCRE2_SUBSTRING_FREE(a) pcre2_substring_free_32((PCRE2_UCHAR32 *)a) +#define PCRE2_SUBSTRING_GET_BYNAME(a,b,c,d,e) \ + a = pcre2_substring_get_byname_32(G(b,32),G(c,32),(PCRE2_UCHAR32 **)d,e) +#define PCRE2_SUBSTRING_GET_BYNUMBER(a,b,c,d,e) \ + a = pcre2_substring_get_bynumber_32(G(b,32),c,(PCRE2_UCHAR32 **)d,e) +#define PCRE2_SUBSTRING_LENGTH_BYNAME(a,b,c,d) \ + a = pcre2_substring_length_byname_32(G(b,32),G(c,32),d) +#define PCRE2_SUBSTRING_LENGTH_BYNUMBER(a,b,c,d) \ + a = pcre2_substring_length_bynumber_32(G(b,32),c,d) +#define PCRE2_SUBSTRING_LIST_GET(a,b,c,d) \ + a = pcre2_substring_list_get_32(G(b,32),(PCRE2_UCHAR32 ***)c,d) +#define PCRE2_SUBSTRING_LIST_FREE(a) \ + pcre2_substring_list_free_32((PCRE2_SPTR32 *)a) +#define PCRE2_SUBSTRING_NUMBER_FROM_NAME(a,b,c) \ + a = pcre2_substring_number_from_name_32(G(b,32),G(c,32)); +#define PTR(x) (void *)G(x,32) +#define SETFLD(x,y,z) G(x,32)->y = z +#define SETFLDVEC(x,y,v,z) G(x,32)->y[v] = z +#define SETOP(x,y,z) G(x,32) z y +#define SETCASTPTR(x,y) G(x,32) = (uint32_t *)(y) +#define STRLEN(p) (int)strlen32((PCRE2_SPTR32)p) +#define SUB1(a,b) G(a,32)(G(b,32)) +#define SUB2(a,b,c) G(a,32)(G(b,32),G(c,32)) +#define TEST(x,r,y) (G(x,32) r (y)) +#define TESTFLD(x,f,r,y) (G(x,32)->f r (y)) + +#endif + +/* ----- End of mode-specific function call macros ----- */ + + + + +/************************************************* +* Alternate character tables * +*************************************************/ + +/* By default, the "tables" pointer in the compile context when calling +pcre2_compile() is not set (= NULL), thereby using the default tables of the +library. However, the tables modifier can be used to select alternate sets of +tables, for different kinds of testing. Note that the locale modifier also +adjusts the tables. */ + +/* This is the set of tables distributed as default with PCRE2. It recognizes +only ASCII characters. */ + +static const uint8_t tables1[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table contains bit maps for various character classes. Each map is 32 +bytes long and the bits run from the least significant end of each byte. The +classes that have their own maps are: space, xdigit, digit, upper, lower, word, +graph, print, punct, and cntrl. Other classes are built from combinations. */ + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, + 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* This is a set of tables that came originally from a Windows user. It seems +to be at least an approximation of ISO 8859. In particular, there are +characters greater than 128 that are marked as spaces, letters, etc. */ + +static const uint8_t tables2[] = { +0,1,2,3,4,5,6,7, +8,9,10,11,12,13,14,15, +16,17,18,19,20,21,22,23, +24,25,26,27,28,29,30,31, +32,33,34,35,36,37,38,39, +40,41,42,43,44,45,46,47, +48,49,50,51,52,53,54,55, +56,57,58,59,60,61,62,63, +64,97,98,99,100,101,102,103, +104,105,106,107,108,109,110,111, +112,113,114,115,116,117,118,119, +120,121,122,91,92,93,94,95, +96,97,98,99,100,101,102,103, +104,105,106,107,108,109,110,111, +112,113,114,115,116,117,118,119, +120,121,122,123,124,125,126,127, +128,129,130,131,132,133,134,135, +136,137,138,139,140,141,142,143, +144,145,146,147,148,149,150,151, +152,153,154,155,156,157,158,159, +160,161,162,163,164,165,166,167, +168,169,170,171,172,173,174,175, +176,177,178,179,180,181,182,183, +184,185,186,187,188,189,190,191, +224,225,226,227,228,229,230,231, +232,233,234,235,236,237,238,239, +240,241,242,243,244,245,246,215, +248,249,250,251,252,253,254,223, +224,225,226,227,228,229,230,231, +232,233,234,235,236,237,238,239, +240,241,242,243,244,245,246,247, +248,249,250,251,252,253,254,255, +0,1,2,3,4,5,6,7, +8,9,10,11,12,13,14,15, +16,17,18,19,20,21,22,23, +24,25,26,27,28,29,30,31, +32,33,34,35,36,37,38,39, +40,41,42,43,44,45,46,47, +48,49,50,51,52,53,54,55, +56,57,58,59,60,61,62,63, +64,97,98,99,100,101,102,103, +104,105,106,107,108,109,110,111, +112,113,114,115,116,117,118,119, +120,121,122,91,92,93,94,95, +96,65,66,67,68,69,70,71, +72,73,74,75,76,77,78,79, +80,81,82,83,84,85,86,87, +88,89,90,123,124,125,126,127, +128,129,130,131,132,133,134,135, +136,137,138,139,140,141,142,143, +144,145,146,147,148,149,150,151, +152,153,154,155,156,157,158,159, +160,161,162,163,164,165,166,167, +168,169,170,171,172,173,174,175, +176,177,178,179,180,181,182,183, +184,185,186,187,188,189,190,191, +224,225,226,227,228,229,230,231, +232,233,234,235,236,237,238,239, +240,241,242,243,244,245,246,215, +248,249,250,251,252,253,254,223, +192,193,194,195,196,197,198,199, +200,201,202,203,204,205,206,207, +208,209,210,211,212,213,214,247, +216,217,218,219,220,221,222,255, +0,62,0,0,1,0,0,0, +0,0,0,0,0,0,0,0, +32,0,0,0,1,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,255,3, +126,0,0,0,126,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,255,3, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,12,2, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +254,255,255,7,0,0,0,0, +0,0,0,0,0,0,0,0, +255,255,127,127,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,254,255,255,7, +0,0,0,0,0,4,32,4, +0,0,0,128,255,255,127,255, +0,0,0,0,0,0,255,3, +254,255,255,135,254,255,255,7, +0,0,0,0,0,4,44,6, +255,255,127,255,255,255,127,255, +0,0,0,0,254,255,255,255, +255,255,255,255,255,255,255,127, +0,0,0,0,254,255,255,255, +255,255,255,255,255,255,255,255, +0,2,0,0,255,255,255,255, +255,255,255,255,255,255,255,127, +0,0,0,0,255,255,255,255, +255,255,255,255,255,255,255,255, +0,0,0,0,254,255,0,252, +1,0,0,248,1,0,0,120, +0,0,0,0,254,255,255,255, +0,0,128,0,0,0,128,0, +255,255,255,255,0,0,0,0, +0,0,0,0,0,0,0,128, +255,255,255,255,0,0,0,0, +0,0,0,0,0,0,0,0, +128,0,0,0,0,0,0,0, +0,1,1,0,1,1,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +1,0,0,0,128,0,0,0, +128,128,128,128,0,0,128,0, +28,28,28,28,28,28,28,28, +28,28,0,0,0,0,0,128, +0,26,26,26,26,26,26,18, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18, +18,18,18,128,128,0,128,16, +0,26,26,26,26,26,26,18, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18, +18,18,18,128,128,0,0,0, +0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0, +0,0,18,0,0,0,0,0, +0,0,20,20,0,18,0,0, +0,20,18,0,0,0,0,0, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,0, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,0, +18,18,18,18,18,18,18,18 +}; + + + +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +/************************************************* +* Emulated memmove() for systems without it * +*************************************************/ + +/* This function can make use of bcopy() if it is available. Otherwise do it by +steam, as there are some non-Unix environments that lack both memmove() and +bcopy(). */ + +static void * +emulated_memmove(void *d, const void *s, size_t n) +{ +#ifdef HAVE_BCOPY +bcopy(s, d, n); +return d; +#else +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +#endif /* not HAVE_BCOPY */ +} +#undef memmove +#define memmove(d,s,n) emulated_memmove(d,s,n) +#endif /* not VPCOMPAT && not HAVE_MEMMOVE */ + + + +#ifndef HAVE_STRERROR +/************************************************* +* Provide strerror() for non-ANSI libraries * +*************************************************/ + +/* Some old-fashioned systems (e.g. SunOS4) didn't have strerror() in their +libraries. They may no longer be around, but just in case, we can try to +provide the same facility by this simple alternative function. */ + +extern int sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int n) +{ +if (n < 0 || n >= sys_nerr) return "unknown error number"; +return sys_errlist[n]; +} +#endif /* HAVE_STRERROR */ + + + +/************************************************* +* Local memory functions * +*************************************************/ + +/* Alternative memory functions, to test functionality. */ + +static void *my_malloc(PCRE2_SIZE size, void *data) +{ +void *block = malloc(size); +(void)data; +if (show_memory) + { + if (block == NULL) + { + fprintf(outfile, "** malloc() failed for %" SIZ_FORM "\n", SIZ_CAST size); + } + else + { + fprintf(outfile, "malloc %5" SIZ_FORM, SIZ_CAST size); +#ifdef DEBUG_SHOW_MALLOC_ADDRESSES + fprintf(outfile, " %p", block); /* Not portable */ +#endif + if (malloclistptr < MALLOCLISTSIZE) + { + malloclist[malloclistptr] = block; + malloclistlength[malloclistptr++] = size; + } + else + fprintf(outfile, " (not remembered)"); + fprintf(outfile, "\n"); + } + } +return block; +} + +static void my_free(void *block, void *data) +{ +(void)data; +if (show_memory) + { + uint32_t i, j; + BOOL found = FALSE; + + fprintf(outfile, "free"); + for (i = 0; i < malloclistptr; i++) + { + if (block == malloclist[i]) + { + fprintf(outfile, " %5" SIZ_FORM, SIZ_CAST malloclistlength[i]); + malloclistptr--; + for (j = i; j < malloclistptr; j++) + { + malloclist[j] = malloclist[j+1]; + malloclistlength[j] = malloclistlength[j+1]; + } + found = TRUE; + break; + } + } + if (!found) fprintf(outfile, " unremembered block"); +#ifdef DEBUG_SHOW_MALLOC_ADDRESSES + fprintf(outfile, " %p", block); /* Not portable */ +#endif + fprintf(outfile, "\n"); + } +free(block); +} + + + +/************************************************* +* Callback function for stack guard * +*************************************************/ + +/* This is set up to be called from pcre2_compile() when the stackguard=n +modifier sets a value greater than zero. The test we do is whether the +parenthesis nesting depth is greater than the value set by the modifier. + +Argument: the current parenthesis nesting depth +Returns: non-zero to kill the compilation +*/ + +static int +stack_guard(uint32_t depth, void *user_data) +{ +(void)user_data; +return depth > pat_patctl.stackguard_test; +} + + +/************************************************* +* JIT memory callback * +*************************************************/ + +static PCRE2_JIT_STACK* +jit_callback(void *arg) +{ +jit_was_used = TRUE; +return (PCRE2_JIT_STACK *)arg; +} + + +/************************************************* +* Convert UTF-8 character to code point * +*************************************************/ + +/* This function reads one or more bytes that represent a UTF-8 character, +and returns the codepoint of that character. Note that the function supports +the original UTF-8 definition of RFC 2279, allowing for values in the range 0 +to 0x7fffffff, up to 6 bytes long. This makes it possible to generate +codepoints greater than 0x10ffff which are useful for testing PCRE2's error +checking, and also for generating 32-bit non-UTF data values above the UTF +limit. + +Argument: + utf8bytes a pointer to the byte vector + vptr a pointer to an int to receive the value + +Returns: > 0 => the number of bytes consumed + -6 to 0 => malformed UTF-8 character at offset = (-return) +*/ + +static int +utf82ord(PCRE2_SPTR8 utf8bytes, uint32_t *vptr) +{ +uint32_t c = *utf8bytes++; +uint32_t d = c; +int i, j, s; + +for (i = -1; i < 6; i++) /* i is number of additional bytes */ + { + if ((d & 0x80) == 0) break; + d <<= 1; + } + +if (i == -1) { *vptr = c; return 1; } /* ascii character */ +if (i == 0 || i == 6) return 0; /* invalid UTF-8 */ + +/* i now has a value in the range 1-5 */ + +s = 6*i; +d = (c & utf8_table3[i]) << s; + +for (j = 0; j < i; j++) + { + c = *utf8bytes++; + if ((c & 0xc0) != 0x80) return -(j+1); + s -= 6; + d |= (c & 0x3f) << s; + } + +/* Check that encoding was the correct unique one */ + +for (j = 0; j < utf8_table1_size; j++) + if (d <= (uint32_t)utf8_table1[j]) break; +if (j != i) return -(i+1); + +/* Valid value */ + +*vptr = d; +return i+1; +} + + + +/************************************************* +* Print one character * +*************************************************/ + +/* Print a single character either literally, or as a hex escape, and count how +many printed characters are used. + +Arguments: + c the character + utf TRUE in UTF mode + f the FILE to print to, or NULL just to count characters + +Returns: number of characters written +*/ + +static int +pchar(uint32_t c, BOOL utf, FILE *f) +{ +int n = 0; +char tempbuffer[16]; + +if (PRINTOK(c)) + { + if (f != NULL) fprintf(f, "%c", c); + return 1; + } + +if (c < 0x100) + { + if (utf) + { + if (f != NULL) fprintf(f, "\\x{%02x}", c); + return 6; + } + else + { + if (f != NULL) fprintf(f, "\\x%02x", c); + return 4; + } + } + +if (f != NULL) n = fprintf(f, "\\x{%02x}", c); + else n = sprintf(tempbuffer, "\\x{%02x}", c); + +return n >= 0 ? n : 0; +} + + + +#ifdef SUPPORT_PCRE2_16 +/************************************************* +* Find length of 0-terminated 16-bit string * +*************************************************/ + +static size_t strlen16(PCRE2_SPTR16 p) +{ +PCRE2_SPTR16 pp = p; +while (*pp != 0) pp++; +return (int)(pp - p); +} +#endif /* SUPPORT_PCRE2_16 */ + + + +#ifdef SUPPORT_PCRE2_32 +/************************************************* +* Find length of 0-terminated 32-bit string * +*************************************************/ + +static size_t strlen32(PCRE2_SPTR32 p) +{ +PCRE2_SPTR32 pp = p; +while (*pp != 0) pp++; +return (int)(pp - p); +} +#endif /* SUPPORT_PCRE2_32 */ + + +#ifdef SUPPORT_PCRE2_8 +/************************************************* +* Print 8-bit character string * +*************************************************/ + +/* Must handle UTF-8 strings in utf8 mode. Yields number of characters printed. +For printing *MARK strings, a negative length is given. If handed a NULL file, +just counts chars without printing (because pchar() does that). */ + +static int pchars8(PCRE2_SPTR8 p, int length, BOOL utf, FILE *f) +{ +uint32_t c = 0; +int yield = 0; + +if (length < 0) length = p[-1]; +while (length-- > 0) + { + if (utf) + { + int rc = utf82ord(p, &c); + if (rc > 0 && rc <= length + 1) /* Mustn't run over the end */ + { + length -= rc - 1; + p += rc; + yield += pchar(c, utf, f); + continue; + } + } + c = *p++; + yield += pchar(c, utf, f); + } + +return yield; +} +#endif + + +#ifdef SUPPORT_PCRE2_16 +/************************************************* +* Print 16-bit character string * +*************************************************/ + +/* Must handle UTF-16 strings in utf mode. Yields number of characters printed. +For printing *MARK strings, a negative length is given. If handed a NULL file, +just counts chars without printing. */ + +static int pchars16(PCRE2_SPTR16 p, int length, BOOL utf, FILE *f) +{ +int yield = 0; +if (length < 0) length = p[-1]; +while (length-- > 0) + { + uint32_t c = *p++ & 0xffff; + if (utf && c >= 0xD800 && c < 0xDC00 && length > 0) + { + int d = *p & 0xffff; + if (d >= 0xDC00 && d <= 0xDFFF) + { + c = ((c & 0x3ff) << 10) + (d & 0x3ff) + 0x10000; + length--; + p++; + } + } + yield += pchar(c, utf, f); + } +return yield; +} +#endif /* SUPPORT_PCRE2_16 */ + + + +#ifdef SUPPORT_PCRE2_32 +/************************************************* +* Print 32-bit character string * +*************************************************/ + +/* Must handle UTF-32 strings in utf mode. Yields number of characters printed. +For printing *MARK strings, a negative length is given. If handed a NULL file, +just counts chars without printing. */ + +static int pchars32(PCRE2_SPTR32 p, int length, BOOL utf, FILE *f) +{ +int yield = 0; +(void)(utf); /* Avoid compiler warning */ + +if (length < 0) length = p[-1]; +while (length-- > 0) + { + uint32_t c = *p++; + yield += pchar(c, utf, f); + } +return yield; +} +#endif /* SUPPORT_PCRE2_32 */ + + + + +#ifdef SUPPORT_PCRE2_8 +/************************************************* +* Convert character value to UTF-8 * +*************************************************/ + +/* This function takes an integer value in the range 0 - 0x7fffffff +and encodes it as a UTF-8 character in 0 to 6 bytes. + +Arguments: + cvalue the character value + utf8bytes pointer to buffer for result - at least 6 bytes long + +Returns: number of characters placed in the buffer +*/ + +static int +ord2utf8(uint32_t cvalue, uint8_t *utf8bytes) +{ +int i, j; +if (cvalue > 0x7fffffffu) + return -1; +for (i = 0; i < utf8_table1_size; i++) + if (cvalue <= (uint32_t)utf8_table1[i]) break; +utf8bytes += i; +for (j = i; j > 0; j--) + { + *utf8bytes-- = 0x80 | (cvalue & 0x3f); + cvalue >>= 6; + } +*utf8bytes = utf8_table2[i] | cvalue; +return i + 1; +} +#endif /* SUPPORT_PCRE2_8 */ + + + +#ifdef SUPPORT_PCRE2_16 +/************************************************* +* Convert string to 16-bit * +*************************************************/ + +/* In UTF mode the input is always interpreted as a string of UTF-8 bytes using +the original UTF-8 definition of RFC 2279, which allows for up to 6 bytes, and +code values from 0 to 0x7fffffff. However, values greater than the later UTF +limit of 0x10ffff cause an error. In non-UTF mode the input is interpreted as +UTF-8 if the utf8_input modifier is set, but an error is generated for values +greater than 0xffff. + +If all the input bytes are ASCII, the space needed for a 16-bit string is +exactly double the 8-bit size. Otherwise, the size needed for a 16-bit string +is no more than double, because up to 0xffff uses no more than 3 bytes in UTF-8 +but possibly 4 in UTF-16. Higher values use 4 bytes in UTF-8 and up to 4 bytes +in UTF-16. The result is always left in pbuffer16. Impose a minimum size to +save repeated re-sizing. + +Note that this function does not object to surrogate values. This is +deliberate; it makes it possible to construct UTF-16 strings that are invalid, +for the purpose of testing that they are correctly faulted. + +Arguments: + p points to a byte string + utf true in UTF mode + lenptr points to number of bytes in the string (excluding trailing zero) + +Returns: 0 on success, with the length updated to the number of 16-bit + data items used (excluding the trailing zero) + OR -1 if a UTF-8 string is malformed + OR -2 if a value > 0x10ffff is encountered in UTF mode + OR -3 if a value > 0xffff is encountered when not in UTF mode +*/ + +static PCRE2_SIZE +to16(uint8_t *p, int utf, PCRE2_SIZE *lenptr) +{ +uint16_t *pp; +PCRE2_SIZE len = *lenptr; + +if (pbuffer16_size < 2*len + 2) + { + if (pbuffer16 != NULL) free(pbuffer16); + pbuffer16_size = 2*len + 2; + if (pbuffer16_size < 4096) pbuffer16_size = 4096; + pbuffer16 = (uint16_t *)malloc(pbuffer16_size); + if (pbuffer16 == NULL) + { + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer16\n", + SIZ_CAST pbuffer16_size); + exit(1); + } + } + +pp = pbuffer16; +if (!utf && (pat_patctl.control & CTL_UTF8_INPUT) == 0) + { + for (; len > 0; len--) *pp++ = *p++; + } +else while (len > 0) + { + uint32_t c; + int chlen = utf82ord(p, &c); + if (chlen <= 0) return -1; + if (!utf && c > 0xffff) return -3; + if (c > 0x10ffff) return -2; + p += chlen; + len -= chlen; + if (c < 0x10000) *pp++ = c; else + { + c -= 0x10000; + *pp++ = 0xD800 | (c >> 10); + *pp++ = 0xDC00 | (c & 0x3ff); + } + } + +*pp = 0; +*lenptr = pp - pbuffer16; +return 0; +} +#endif + + + +#ifdef SUPPORT_PCRE2_32 +/************************************************* +* Convert string to 32-bit * +*************************************************/ + +/* In UTF mode the input is always interpreted as a string of UTF-8 bytes using +the original UTF-8 definition of RFC 2279, which allows for up to 6 bytes, and +code values from 0 to 0x7fffffff. However, values greater than the later UTF +limit of 0x10ffff cause an error. + +In non-UTF mode the input is interpreted as UTF-8 if the utf8_input modifier +is set, and no limit is imposed. There is special interpretation of the 0xff +byte (which is illegal in UTF-8) in this case: it causes the top bit of the +next character to be set. This provides a way of generating 32-bit characters +greater than 0x7fffffff. + +If all the input bytes are ASCII, the space needed for a 32-bit string is +exactly four times the 8-bit size. Otherwise, the size needed for a 32-bit +string is no more than four times, because the number of characters must be +less than the number of bytes. The result is always left in pbuffer32. Impose a +minimum size to save repeated re-sizing. + +Note that this function does not object to surrogate values. This is +deliberate; it makes it possible to construct UTF-32 strings that are invalid, +for the purpose of testing that they are correctly faulted. + +Arguments: + p points to a byte string + utf true in UTF mode + lenptr points to number of bytes in the string (excluding trailing zero) + +Returns: 0 on success, with the length updated to the number of 32-bit + data items used (excluding the trailing zero) + OR -1 if a UTF-8 string is malformed + OR -2 if a value > 0x10ffff is encountered in UTF mode +*/ + +static PCRE2_SIZE +to32(uint8_t *p, int utf, PCRE2_SIZE *lenptr) +{ +uint32_t *pp; +PCRE2_SIZE len = *lenptr; + +if (pbuffer32_size < 4*len + 4) + { + if (pbuffer32 != NULL) free(pbuffer32); + pbuffer32_size = 4*len + 4; + if (pbuffer32_size < 8192) pbuffer32_size = 8192; + pbuffer32 = (uint32_t *)malloc(pbuffer32_size); + if (pbuffer32 == NULL) + { + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer32\n", + SIZ_CAST pbuffer32_size); + exit(1); + } + } + +pp = pbuffer32; + +if (!utf && (pat_patctl.control & CTL_UTF8_INPUT) == 0) + { + for (; len > 0; len--) *pp++ = *p++; + } + +else while (len > 0) + { + int chlen; + uint32_t c; + uint32_t topbit = 0; + if (!utf && *p == 0xff && len > 1) + { + topbit = 0x80000000u; + p++; + len--; + } + chlen = utf82ord(p, &c); + if (chlen <= 0) return -1; + if (utf && c > 0x10ffff) return -2; + p += chlen; + len -= chlen; + *pp++ = c | topbit; + } + +*pp = 0; +*lenptr = pp - pbuffer32; +return 0; +} +#endif /* SUPPORT_PCRE2_32 */ + + + +/************************************************* +* Move back by so many characters * +*************************************************/ + +/* Given a code unit offset in a subject string, move backwards by a number of +characters, and return the resulting offset. + +Arguments: + subject pointer to the string + offset start offset + count count to move back by + utf TRUE if in UTF mode + +Returns: a possibly changed offset +*/ + +static PCRE2_SIZE +backchars(uint8_t *subject, PCRE2_SIZE offset, uint32_t count, BOOL utf) +{ +if (!utf || test_mode == PCRE32_MODE) + return (count >= offset)? 0 : (offset - count); + +else if (test_mode == PCRE8_MODE) + { + PCRE2_SPTR8 pp = (PCRE2_SPTR8)subject + offset; + for (; count > 0 && pp > (PCRE2_SPTR8)subject; count--) + { + pp--; + while ((*pp & 0xc0) == 0x80) pp--; + } + return pp - (PCRE2_SPTR8)subject; + } + +else /* 16-bit mode */ + { + PCRE2_SPTR16 pp = (PCRE2_SPTR16)subject + offset; + for (; count > 0 && pp > (PCRE2_SPTR16)subject; count--) + { + pp--; + if ((*pp & 0xfc00) == 0xdc00) pp--; + } + return pp - (PCRE2_SPTR16)subject; + } +} + + + +/************************************************* +* Expand input buffers * +*************************************************/ + +/* This function doubles the size of the input buffer and the buffer for +keeping an 8-bit copy of patterns (pbuffer8), and copies the current buffers to +the new ones. + +Arguments: none +Returns: nothing (aborts if malloc() fails) +*/ + +static void +expand_input_buffers(void) +{ +int new_pbuffer8_size = 2*pbuffer8_size; +uint8_t *new_buffer = (uint8_t *)malloc(new_pbuffer8_size); +uint8_t *new_pbuffer8 = (uint8_t *)malloc(new_pbuffer8_size); + +if (new_buffer == NULL || new_pbuffer8 == NULL) + { + fprintf(stderr, "pcre2test: malloc(%d) failed\n", new_pbuffer8_size); + exit(1); + } + +memcpy(new_buffer, buffer, pbuffer8_size); +memcpy(new_pbuffer8, pbuffer8, pbuffer8_size); + +pbuffer8_size = new_pbuffer8_size; + +free(buffer); +free(pbuffer8); + +buffer = new_buffer; +pbuffer8 = new_pbuffer8; +} + + + +/************************************************* +* Read or extend an input line * +*************************************************/ + +/* Input lines are read into buffer, but both patterns and data lines can be +continued over multiple input lines. In addition, if the buffer fills up, we +want to automatically expand it so as to be able to handle extremely large +lines that are needed for certain stress tests, although this is less likely +now that there are repetition features for both patterns and data. When the +input buffer is expanded, the other two buffers must also be expanded likewise, +and the contents of pbuffer, which are a copy of the input for callouts, must +be preserved (for when expansion happens for a data line). This is not the most +optimal way of handling this, but hey, this is just a test program! + +Arguments: + f the file to read + start where in buffer to start (this *must* be within buffer) + prompt for stdin or readline() + +Returns: pointer to the start of new data + could be a copy of start, or could be moved + NULL if no data read and EOF reached +*/ + +static uint8_t * +extend_inputline(FILE *f, uint8_t *start, const char *prompt) +{ +uint8_t *here = start; + +for (;;) + { + size_t rlen = (size_t)(pbuffer8_size - (here - buffer)); + + if (rlen > 1000) + { + size_t dlen; + + /* If libreadline or libedit support is required, use readline() to read a + line if the input is a terminal. Note that readline() removes the trailing + newline, so we must put it back again, to be compatible with fgets(). */ + +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) + if (INTERACTIVE(f)) + { + size_t len; + char *s = readline(prompt); + if (s == NULL) return (here == start)? NULL : start; + len = strlen(s); + if (len > 0) add_history(s); + if (len > rlen - 1) len = rlen - 1; + memcpy(here, s, len); + here[len] = '\n'; + here[len+1] = 0; + free(s); + } + else +#endif + + /* Read the next line by normal means, prompting if the file is a tty. */ + + { + if (INTERACTIVE(f)) printf("%s", prompt); + if (fgets((char *)here, rlen, f) == NULL) + return (here == start)? NULL : start; + } + + dlen = strlen((char *)here); + here += dlen; + + /* Check for end of line reached. Take care not to read data from before + start (dlen will be zero for a file starting with a binary zero). */ + + if (here > start && here[-1] == '\n') return start; + + /* If we have not read a newline when reading a file, we have either filled + the buffer or reached the end of the file. We can detect the former by + checking that the string fills the buffer, and the latter by feof(). If + neither of these is true, it means we read a binary zero which has caused + strlen() to give a short length. This is a hard error because pcre2test + expects to work with C strings. */ + + if (!INTERACTIVE(f) && dlen < rlen - 1 && !feof(f)) + { + fprintf(outfile, "** Binary zero encountered in input\n"); + fprintf(outfile, "** pcre2test run abandoned\n"); + exit(1); + } + } + + else + { + size_t start_offset = start - buffer; + size_t here_offset = here - buffer; + expand_input_buffers(); + start = buffer + start_offset; + here = buffer + here_offset; + } + } + +/* Control never gets here */ +} + + + +/************************************************* +* Case-independent strncmp() function * +*************************************************/ + +/* +Arguments: + s first string + t second string + n number of characters to compare + +Returns: < 0, = 0, or > 0, according to the comparison +*/ + +static int +strncmpic(const uint8_t *s, const uint8_t *t, int n) +{ +while (n--) + { + int c = tolower(*s++) - tolower(*t++); + if (c != 0) return c; + } +return 0; +} + + + +/************************************************* +* Scan the main modifier list * +*************************************************/ + +/* This function searches the modifier list for a long modifier name. + +Argument: + p start of the name + lenp length of the name + +Returns: an index in the modifier list, or -1 on failure +*/ + +static int +scan_modifiers(const uint8_t *p, unsigned int len) +{ +int bot = 0; +int top = MODLISTCOUNT; + +while (top > bot) + { + int mid = (bot + top)/2; + unsigned int mlen = strlen(modlist[mid].name); + int c = strncmp((char *)p, modlist[mid].name, (len < mlen)? len : mlen); + if (c == 0) + { + if (len == mlen) return mid; + c = (int)len - (int)mlen; + } + if (c > 0) bot = mid + 1; else top = mid; + } + +return -1; + +} + + + +/************************************************* +* Check a modifer and find its field * +*************************************************/ + +/* This function is called when a modifier has been identified. We check that +it is allowed here and find the field that is to be changed. + +Arguments: + m the modifier list entry + ctx CTX_PAT => pattern context + CTX_POPPAT => pattern context for popped pattern + CTX_DEFPAT => default pattern context + CTX_DAT => data context + CTX_DEFDAT => default data context + pctl point to pattern control block + dctl point to data control block + c a single character or 0 + +Returns: a field pointer or NULL +*/ + +static void * +check_modifier(modstruct *m, int ctx, patctl *pctl, datctl *dctl, uint32_t c) +{ +void *field = NULL; +PCRE2_SIZE offset = m->offset; + +if (restrict_for_perl_test) switch(m->which) + { + case MOD_PNDP: + case MOD_PATP: + case MOD_PDP: + break; + + default: + fprintf(outfile, "** '%s' is not allowed in a Perl-compatible test\n", + m->name); + return NULL; + } + +switch (m->which) + { + case MOD_CTC: /* Compile context modifier */ + if (ctx == CTX_DEFPAT) field = PTR(default_pat_context); + else if (ctx == CTX_PAT) field = PTR(pat_context); + break; + + case MOD_CTM: /* Match context modifier */ + if (ctx == CTX_DEFDAT) field = PTR(default_dat_context); + else if (ctx == CTX_DAT) field = PTR(dat_context); + break; + + case MOD_DAT: /* Data line modifier */ + if (dctl != NULL) field = dctl; + break; + + case MOD_PAT: /* Pattern modifier */ + case MOD_PATP: /* Allowed for Perl test */ + if (pctl != NULL) field = pctl; + break; + + case MOD_PD: /* Pattern or data line modifier */ + case MOD_PDP: /* Ditto, allowed for Perl test */ + case MOD_PND: /* Ditto, but not default pattern */ + case MOD_PNDP: /* Ditto, allowed for Perl test */ + if (dctl != NULL) field = dctl; + else if (pctl != NULL && (m->which == MOD_PD || m->which == MOD_PDP || + ctx != CTX_DEFPAT)) + field = pctl; + break; + } + +if (field == NULL) + { + if (c == 0) + fprintf(outfile, "** '%s' is not valid here\n", m->name); + else + fprintf(outfile, "** /%c is not valid here\n", c); + return NULL; + } + +return (char *)field + offset; +} + + + +/************************************************* +* Decode a modifier list * +*************************************************/ + +/* A pointer to a control block is NULL when called in cases when that block is +not relevant. They are never all relevant in one call. At least one of patctl +and datctl is NULL. The second argument specifies which context to use for +modifiers that apply to contexts. + +Arguments: + p point to modifier string + ctx CTX_PAT => pattern context + CTX_POPPAT => pattern context for popped pattern + CTX_DEFPAT => default pattern context + CTX_DAT => data context + CTX_DEFDAT => default data context + pctl point to pattern control block + dctl point to data control block + +Returns: TRUE if successful decode, FALSE otherwise +*/ + +static BOOL +decode_modifiers(uint8_t *p, int ctx, patctl *pctl, datctl *dctl) +{ +uint8_t *ep, *pp; +long li; +unsigned long uli; +BOOL first = TRUE; + +for (;;) + { + void *field; + modstruct *m; + BOOL off = FALSE; + unsigned int i, len; + int index; + char *endptr; + + /* Skip white space and commas. */ + + while (isspace(*p) || *p == ',') p++; + if (*p == 0) break; + + /* Find the end of the item; lose trailing whitespace at end of line. */ + + for (ep = p; *ep != 0 && *ep != ','; ep++); + if (*ep == 0) + { + while (ep > p && isspace(ep[-1])) ep--; + *ep = 0; + } + + /* Remember if the first character is '-'. */ + + if (*p == '-') + { + off = TRUE; + p++; + } + + /* Find the length of a full-length modifier name, and scan for it. */ + + pp = p; + while (pp < ep && *pp != '=') pp++; + index = scan_modifiers(p, pp - p); + + /* If the first modifier is unrecognized, try to interpret it as a sequence + of single-character abbreviated modifiers. None of these modifiers have any + associated data. They just set options or control bits. */ + + if (index < 0) + { + uint32_t cc; + uint8_t *mp = p; + + if (!first) + { + fprintf(outfile, "** Unrecognized modifier '%.*s'\n", (int)(ep-p), p); + if (ep - p == 1) + fprintf(outfile, "** Single-character modifiers must come first\n"); + return FALSE; + } + + for (cc = *p; cc != ',' && cc != '\n' && cc != 0; cc = *(++p)) + { + for (i = 0; i < C1MODLISTCOUNT; i++) + if (cc == c1modlist[i].onechar) break; + + if (i >= C1MODLISTCOUNT) + { + fprintf(outfile, "** Unrecognized modifier '%c' in '%.*s'\n", + *p, (int)(ep-mp), mp); + return FALSE; + } + + if (c1modlist[i].index >= 0) + { + index = c1modlist[i].index; + } + + else + { + index = scan_modifiers((uint8_t *)(c1modlist[i].fullname), + strlen(c1modlist[i].fullname)); + if (index < 0) + { + fprintf(outfile, "** Internal error: single-character equivalent " + "modifier '%s' not found\n", c1modlist[i].fullname); + return FALSE; + } + c1modlist[i].index = index; /* Cache for next time */ + } + + field = check_modifier(modlist + index, ctx, pctl, dctl, *p); + if (field == NULL) return FALSE; + + /* /x is a special case; a second appearance changes PCRE2_EXTENDED to + PCRE2_EXTENDED_MORE. */ + + if (cc == 'x' && (*((uint32_t *)field) & PCRE2_EXTENDED) != 0) + { + *((uint32_t *)field) &= ~PCRE2_EXTENDED; + *((uint32_t *)field) |= PCRE2_EXTENDED_MORE; + } + else + *((uint32_t *)field) |= modlist[index].value; + } + + continue; /* With tne next (fullname) modifier */ + } + + /* We have a match on a full-name modifier. Check for the existence of data + when needed. */ + + m = modlist + index; /* Save typing */ + if (m->type != MOD_CTL && m->type != MOD_OPT && + (m->type != MOD_IND || *pp == '=')) + { + if (*pp++ != '=') + { + fprintf(outfile, "** '=' expected after '%s'\n", m->name); + return FALSE; + } + if (off) + { + fprintf(outfile, "** '-' is not valid for '%s'\n", m->name); + return FALSE; + } + } + + /* These on/off types have no data. */ + + else if (*pp != ',' && *pp != '\n' && *pp != ' ' && *pp != 0) + { + fprintf(outfile, "** Unrecognized modifier '%.*s'\n", (int)(ep-p), p); + return FALSE; + } + + /* Set the data length for those types that have data. Then find the field + that is to be set. If check_modifier() returns NULL, it has already output an + error message. */ + + len = ep - pp; + field = check_modifier(m, ctx, pctl, dctl, 0); + if (field == NULL) return FALSE; + + /* Process according to data type. */ + + switch (m->type) + { + case MOD_CTL: + case MOD_OPT: + if (off) *((uint32_t *)field) &= ~m->value; + else *((uint32_t *)field) |= m->value; + break; + + case MOD_BSR: + if (len == 7 && strncmpic(pp, (const uint8_t *)"default", 7) == 0) + { +#ifdef BSR_ANYCRLF + *((uint16_t *)field) = PCRE2_BSR_ANYCRLF; +#else + *((uint16_t *)field) = PCRE2_BSR_UNICODE; +#endif + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 &= ~CTL2_BSR_SET; + else dctl->control2 &= ~CTL2_BSR_SET; + } + else + { + if (len == 7 && strncmpic(pp, (const uint8_t *)"anycrlf", 7) == 0) + *((uint16_t *)field) = PCRE2_BSR_ANYCRLF; + else if (len == 7 && strncmpic(pp, (const uint8_t *)"unicode", 7) == 0) + *((uint16_t *)field) = PCRE2_BSR_UNICODE; + else goto INVALID_VALUE; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 |= CTL2_BSR_SET; + else dctl->control2 |= CTL2_BSR_SET; + } + pp = ep; + break; + + case MOD_CHR: /* A single character */ + *((uint32_t *)field) = *pp++; + break; + + case MOD_CON: /* A convert type/options list */ + for (;; pp++) + { + uint8_t *colon = (uint8_t *)strchr((const char *)pp, ':'); + len = ((colon != NULL && colon < ep)? colon:ep) - pp; + for (i = 0; i < convertlistcount; i++) + { + if (strncmpic(pp, (const uint8_t *)convertlist[i].name, len) == 0) + { + if (*((uint32_t *)field) == CONVERT_UNSET) + *((uint32_t *)field) = convertlist[i].option; + else + *((uint32_t *)field) |= convertlist[i].option; + break; + } + } + if (i >= convertlistcount) goto INVALID_VALUE; + pp += len; + if (*pp != ':') break; + } + break; + + case MOD_IN2: /* One or two unsigned integers */ + if (!isdigit(*pp)) goto INVALID_VALUE; + uli = strtoul((const char *)pp, &endptr, 10); + if (U32OVERFLOW(uli)) goto INVALID_VALUE; + ((uint32_t *)field)[0] = (uint32_t)uli; + if (*endptr == ':') + { + uli = strtoul((const char *)endptr+1, &endptr, 10); + if (U32OVERFLOW(uli)) goto INVALID_VALUE; + ((uint32_t *)field)[1] = (uint32_t)uli; + } + else ((uint32_t *)field)[1] = 0; + pp = (uint8_t *)endptr; + break; + + /* PCRE2_SIZE_MAX is usually SIZE_MAX, which may be greater, equal to, or + less than ULONG_MAX. So first test for overflowing the long int, and then + test for overflowing PCRE2_SIZE_MAX if it is smaller than ULONG_MAX. */ + + case MOD_SIZ: /* PCRE2_SIZE value */ + if (!isdigit(*pp)) goto INVALID_VALUE; + uli = strtoul((const char *)pp, &endptr, 10); + if (uli == ULONG_MAX) goto INVALID_VALUE; +#if ULONG_MAX > PCRE2_SIZE_MAX + if (uli > PCRE2_SIZE_MAX) goto INVALID_VALUE; +#endif + *((PCRE2_SIZE *)field) = (PCRE2_SIZE)uli; + pp = (uint8_t *)endptr; + break; + + case MOD_IND: /* Unsigned integer with default */ + if (len == 0) + { + *((uint32_t *)field) = (uint32_t)(m->value); + break; + } + /* Fall through */ + + case MOD_INT: /* Unsigned integer */ + if (!isdigit(*pp)) goto INVALID_VALUE; + uli = strtoul((const char *)pp, &endptr, 10); + if (U32OVERFLOW(uli)) goto INVALID_VALUE; + *((uint32_t *)field) = (uint32_t)uli; + pp = (uint8_t *)endptr; + break; + + case MOD_INS: /* Signed integer */ + if (!isdigit(*pp) && *pp != '-') goto INVALID_VALUE; + li = strtol((const char *)pp, &endptr, 10); + if (S32OVERFLOW(li)) goto INVALID_VALUE; + *((int32_t *)field) = (int32_t)li; + pp = (uint8_t *)endptr; + break; + + case MOD_NL: + for (i = 0; i < sizeof(newlines)/sizeof(char *); i++) + if (len == strlen(newlines[i]) && + strncmpic(pp, (const uint8_t *)newlines[i], len) == 0) break; + if (i >= sizeof(newlines)/sizeof(char *)) goto INVALID_VALUE; + if (i == 0) + { + *((uint16_t *)field) = NEWLINE_DEFAULT; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 &= ~CTL2_NL_SET; + else dctl->control2 &= ~CTL2_NL_SET; + } + else + { + *((uint16_t *)field) = i; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 |= CTL2_NL_SET; + else dctl->control2 |= CTL2_NL_SET; + } + pp = ep; + break; + + case MOD_NN: /* Name or (signed) number; may be several */ + if (isdigit(*pp) || *pp == '-') + { + int ct = MAXCPYGET - 1; + int32_t value; + li = strtol((const char *)pp, &endptr, 10); + if (S32OVERFLOW(li)) goto INVALID_VALUE; + value = (int32_t)li; + field = (char *)field - m->offset + m->value; /* Adjust field ptr */ + if (value >= 0) /* Add new number */ + { + while (*((int32_t *)field) >= 0 && ct-- > 0) /* Skip previous */ + field = (char *)field + sizeof(int32_t); + if (ct <= 0) + { + fprintf(outfile, "** Too many numeric '%s' modifiers\n", m->name); + return FALSE; + } + } + *((int32_t *)field) = value; + if (ct > 0) ((int32_t *)field)[1] = -1; + pp = (uint8_t *)endptr; + } + + /* Multiple strings are put end to end. */ + + else + { + char *nn = (char *)field; + if (len > 0) /* Add new name */ + { + if (len > MAX_NAME_SIZE) + { + fprintf(outfile, "** Group name in '%s' is too long\n", m->name); + return FALSE; + } + while (*nn != 0) nn += strlen(nn) + 1; + if (nn + len + 2 - (char *)field > LENCPYGET) + { + fprintf(outfile, "** Too many characters in named '%s' modifiers\n", + m->name); + return FALSE; + } + memcpy(nn, pp, len); + } + nn[len] = 0 ; + nn[len+1] = 0; + pp = ep; + } + break; + + case MOD_STR: + if (len + 1 > m->value) + { + fprintf(outfile, "** Overlong value for '%s' (max %d code units)\n", + m->name, m->value - 1); + return FALSE; + } + memcpy(field, pp, len); + ((uint8_t *)field)[len] = 0; + pp = ep; + break; + } + + if (*pp != ',' && *pp != '\n' && *pp != ' ' && *pp != 0) + { + fprintf(outfile, "** Comma expected after modifier item '%s'\n", m->name); + return FALSE; + } + + p = pp; + first = FALSE; + + if (ctx == CTX_POPPAT && + (pctl->options != 0 || + pctl->tables_id != 0 || + pctl->locale[0] != 0 || + (pctl->control & NOTPOP_CONTROLS) != 0)) + { + fprintf(outfile, "** '%s' is not valid here\n", m->name); + return FALSE; + } + } + +return TRUE; + +INVALID_VALUE: +fprintf(outfile, "** Invalid value in '%.*s'\n", (int)(ep-p), p); +return FALSE; +} + + +/************************************************* +* Get info from a pattern * +*************************************************/ + +/* A wrapped call to pcre2_pattern_info(), applied to the current compiled +pattern. + +Arguments: + what code for the required information + where where to put the answer + unsetok PCRE2_ERROR_UNSET is an "expected" result + +Returns: the return from pcre2_pattern_info() +*/ + +static int +pattern_info(int what, void *where, BOOL unsetok) +{ +int rc; +PCRE2_PATTERN_INFO(rc, compiled_code, what, NULL); /* Exercise the code */ +PCRE2_PATTERN_INFO(rc, compiled_code, what, where); +if (rc >= 0) return 0; +if (rc != PCRE2_ERROR_UNSET || !unsetok) + { + fprintf(outfile, "Error %d from pcre2_pattern_info_%d(%d)\n", rc, test_mode, + what); + if (rc == PCRE2_ERROR_BADMODE) + fprintf(outfile, "Running in %d-bit mode but pattern was compiled in " + "%d-bit mode\n", test_mode, + 8 * (FLD(compiled_code, flags) & PCRE2_MODE_MASK)); + } +return rc; +} + + + +#ifdef SUPPORT_PCRE2_8 +/************************************************* +* Show something in a list * +*************************************************/ + +/* This function just helps to keep the code that uses it tidier. It's used for +various lists of things where there needs to be introductory text before the +first item. As these calls are all in the POSIX-support code, they happen only +when 8-bit mode is supported. */ + +static void +prmsg(const char **msg, const char *s) +{ +fprintf(outfile, "%s %s", *msg, s); +*msg = ""; +} +#endif /* SUPPORT_PCRE2_8 */ + + + +/************************************************* +* Show control bits * +*************************************************/ + +/* Called for mutually exclusive controls and for unsupported POSIX controls. +Because the bits are unique, this can be used for both pattern and data control +words. + +Arguments: + controls control bits + controls2 more control bits + before text to print before + +Returns: nothing +*/ + +static void +show_controls(uint32_t controls, uint32_t controls2, const char *before) +{ +fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + before, + ((controls & CTL_AFTERTEXT) != 0)? " aftertext" : "", + ((controls & CTL_ALLAFTERTEXT) != 0)? " allaftertext" : "", + ((controls & CTL_ALLCAPTURES) != 0)? " allcaptures" : "", + ((controls & CTL_ALLUSEDTEXT) != 0)? " allusedtext" : "", + ((controls & CTL_ALTGLOBAL) != 0)? " altglobal" : "", + ((controls & CTL_BINCODE) != 0)? " bincode" : "", + ((controls2 & CTL2_BSR_SET) != 0)? " bsr" : "", + ((controls & CTL_CALLOUT_CAPTURE) != 0)? " callout_capture" : "", + ((controls2 & CTL2_CALLOUT_EXTRA) != 0)? " callout_extra" : "", + ((controls & CTL_CALLOUT_INFO) != 0)? " callout_info" : "", + ((controls & CTL_CALLOUT_NONE) != 0)? " callout_none" : "", + ((controls2 & CTL2_CALLOUT_NO_WHERE) != 0)? " callout_no_where" : "", + ((controls & CTL_DFA) != 0)? " dfa" : "", + ((controls & CTL_EXPAND) != 0)? " expand" : "", + ((controls & CTL_FINDLIMITS) != 0)? " find_limits" : "", + ((controls & CTL_FRAMESIZE) != 0)? " framesize" : "", + ((controls & CTL_FULLBINCODE) != 0)? " fullbincode" : "", + ((controls & CTL_GETALL) != 0)? " getall" : "", + ((controls & CTL_GLOBAL) != 0)? " global" : "", + ((controls & CTL_HEXPAT) != 0)? " hex" : "", + ((controls & CTL_INFO) != 0)? " info" : "", + ((controls & CTL_JITFAST) != 0)? " jitfast" : "", + ((controls & CTL_JITVERIFY) != 0)? " jitverify" : "", + ((controls & CTL_MARK) != 0)? " mark" : "", + ((controls & CTL_MEMORY) != 0)? " memory" : "", + ((controls2 & CTL2_NL_SET) != 0)? " newline" : "", + ((controls & CTL_NULLCONTEXT) != 0)? " null_context" : "", + ((controls & CTL_POSIX) != 0)? " posix" : "", + ((controls & CTL_POSIX_NOSUB) != 0)? " posix_nosub" : "", + ((controls & CTL_PUSH) != 0)? " push" : "", + ((controls & CTL_PUSHCOPY) != 0)? " pushcopy" : "", + ((controls & CTL_PUSHTABLESCOPY) != 0)? " pushtablescopy" : "", + ((controls & CTL_STARTCHAR) != 0)? " startchar" : "", + ((controls2 & CTL2_SUBSTITUTE_EXTENDED) != 0)? " substitute_extended" : "", + ((controls2 & CTL2_SUBSTITUTE_OVERFLOW_LENGTH) != 0)? " substitute_overflow_length" : "", + ((controls2 & CTL2_SUBSTITUTE_UNKNOWN_UNSET) != 0)? " substitute_unknown_unset" : "", + ((controls2 & CTL2_SUBSTITUTE_UNSET_EMPTY) != 0)? " substitute_unset_empty" : "", + ((controls & CTL_USE_LENGTH) != 0)? " use_length" : "", + ((controls & CTL_UTF8_INPUT) != 0)? " utf8_input" : "", + ((controls & CTL_ZERO_TERMINATE) != 0)? " zero_terminate" : ""); +} + + + +/************************************************* +* Show compile options * +*************************************************/ + +/* Called from show_pattern_info() and for unsupported POSIX options. + +Arguments: + options an options word + before text to print before + after text to print after + +Returns: nothing +*/ + +static void +show_compile_options(uint32_t options, const char *before, const char *after) +{ +if (options == 0) fprintf(outfile, "%s %s", before, after); +else fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + before, + ((options & PCRE2_ALT_BSUX) != 0)? " alt_bsux" : "", + ((options & PCRE2_ALT_CIRCUMFLEX) != 0)? " alt_circumflex" : "", + ((options & PCRE2_ALT_VERBNAMES) != 0)? " alt_verbnames" : "", + ((options & PCRE2_ALLOW_EMPTY_CLASS) != 0)? " allow_empty_class" : "", + ((options & PCRE2_ANCHORED) != 0)? " anchored" : "", + ((options & PCRE2_AUTO_CALLOUT) != 0)? " auto_callout" : "", + ((options & PCRE2_CASELESS) != 0)? " caseless" : "", + ((options & PCRE2_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "", + ((options & PCRE2_DOTALL) != 0)? " dotall" : "", + ((options & PCRE2_DUPNAMES) != 0)? " dupnames" : "", + ((options & PCRE2_ENDANCHORED) != 0)? " endanchored" : "", + ((options & PCRE2_EXTENDED) != 0)? " extended" : "", + ((options & PCRE2_EXTENDED_MORE) != 0)? " extended_more" : "", + ((options & PCRE2_FIRSTLINE) != 0)? " firstline" : "", + ((options & PCRE2_LITERAL) != 0)? " literal" : "", + ((options & PCRE2_MATCH_UNSET_BACKREF) != 0)? " match_unset_backref" : "", + ((options & PCRE2_MULTILINE) != 0)? " multiline" : "", + ((options & PCRE2_NEVER_BACKSLASH_C) != 0)? " never_backslash_c" : "", + ((options & PCRE2_NEVER_UCP) != 0)? " never_ucp" : "", + ((options & PCRE2_NEVER_UTF) != 0)? " never_utf" : "", + ((options & PCRE2_NO_AUTO_CAPTURE) != 0)? " no_auto_capture" : "", + ((options & PCRE2_NO_AUTO_POSSESS) != 0)? " no_auto_possess" : "", + ((options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)? " no_dotstar_anchor" : "", + ((options & PCRE2_NO_UTF_CHECK) != 0)? " no_utf_check" : "", + ((options & PCRE2_NO_START_OPTIMIZE) != 0)? " no_start_optimize" : "", + ((options & PCRE2_UCP) != 0)? " ucp" : "", + ((options & PCRE2_UNGREEDY) != 0)? " ungreedy" : "", + ((options & PCRE2_USE_OFFSET_LIMIT) != 0)? " use_offset_limit" : "", + ((options & PCRE2_UTF) != 0)? " utf" : "", + after); +} + + +/************************************************* +* Show compile extra options * +*************************************************/ + +/* Called from show_pattern_info() and for unsupported POSIX options. + +Arguments: + options an options word + before text to print before + after text to print after + +Returns: nothing +*/ + +static void +show_compile_extra_options(uint32_t options, const char *before, + const char *after) +{ +if (options == 0) fprintf(outfile, "%s %s", before, after); +else fprintf(outfile, "%s%s%s%s%s%s", + before, + ((options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0)? " allow_surrogate_escapes" : "", + ((options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) != 0)? " bad_escape_is_literal" : "", + ((options & PCRE2_EXTRA_MATCH_WORD) != 0)? " match_word" : "", + ((options & PCRE2_EXTRA_MATCH_LINE) != 0)? " match_line" : "", + after); +} + + + +#ifdef SUPPORT_PCRE2_8 +/************************************************* +* Show match options * +*************************************************/ + +/* Called for unsupported POSIX options. */ + +static void +show_match_options(uint32_t options) +{ +fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s", + ((options & PCRE2_ANCHORED) != 0)? " anchored" : "", + ((options & PCRE2_DFA_RESTART) != 0)? " dfa_restart" : "", + ((options & PCRE2_DFA_SHORTEST) != 0)? " dfa_shortest" : "", + ((options & PCRE2_ENDANCHORED) != 0)? " endanchored" : "", + ((options & PCRE2_NO_UTF_CHECK) != 0)? " no_utf_check" : "", + ((options & PCRE2_NOTBOL) != 0)? " notbol" : "", + ((options & PCRE2_NOTEMPTY) != 0)? " notempty" : "", + ((options & PCRE2_NOTEMPTY_ATSTART) != 0)? " notempty_atstart" : "", + ((options & PCRE2_NOTEOL) != 0)? " noteol" : "", + ((options & PCRE2_PARTIAL_HARD) != 0)? " partial_hard" : "", + ((options & PCRE2_PARTIAL_SOFT) != 0)? " partial_soft" : ""); +} +#endif /* SUPPORT_PCRE2_8 */ + + + +/************************************************* +* Show memory usage info for a pattern * +*************************************************/ + +static void +show_memory_info(void) +{ +uint32_t name_count, name_entry_size; +size_t size, cblock_size; + +/* One of the test_mode values will always be true, but to stop a compiler +warning we must initialize cblock_size. */ + +cblock_size = 0; +#ifdef SUPPORT_PCRE2_8 +if (test_mode == PCRE8_MODE) cblock_size = sizeof(pcre2_real_code_8); +#endif +#ifdef SUPPORT_PCRE2_16 +if (test_mode == PCRE16_MODE) cblock_size = sizeof(pcre2_real_code_16); +#endif +#ifdef SUPPORT_PCRE2_32 +if (test_mode == PCRE32_MODE) cblock_size = sizeof(pcre2_real_code_32); +#endif + +(void)pattern_info(PCRE2_INFO_SIZE, &size, FALSE); +(void)pattern_info(PCRE2_INFO_NAMECOUNT, &name_count, FALSE); +(void)pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &name_entry_size, FALSE); +fprintf(outfile, "Memory allocation (code space): %d\n", + (int)(size - name_count*name_entry_size*code_unit_size - cblock_size)); +if (pat_patctl.jit != 0) + { + (void)pattern_info(PCRE2_INFO_JITSIZE, &size, FALSE); + fprintf(outfile, "Memory allocation (JIT code): %d\n", (int)size); + } +} + + + +/************************************************* +* Show frame size info for a pattern * +*************************************************/ + +static void +show_framesize(void) +{ +size_t frame_size; +(void)pattern_info(PCRE2_INFO_FRAMESIZE, &frame_size, FALSE); +fprintf(outfile, "Frame size for pcre2_match(): %d\n", (int)frame_size); +} + + + +/************************************************* +* Get and output an error message * +*************************************************/ + +static BOOL +print_error_message(int errorcode, const char *before, const char *after) +{ +int len; +PCRE2_GET_ERROR_MESSAGE(len, errorcode, pbuffer); +if (len < 0) + { + fprintf(outfile, "\n** pcre2test internal error: cannot interpret error " + "number\n** Unexpected return (%d) from pcre2_get_error_message()\n", len); + } +else + { + fprintf(outfile, "%s", before); + PCHARSV(CASTVAR(void *, pbuffer), 0, len, FALSE, outfile); + fprintf(outfile, "%s", after); + } +return len >= 0; +} + + +/************************************************* +* Callback function for callout enumeration * +*************************************************/ + +/* The only differences in the callout emumeration block for different code +unit widths are that the pointers to the subject, the most recent MARK, and a +callout argument string point to strings of the appropriate width. Casts can be +used to deal with this. + +Argument: + cb pointer to enumerate block + callout_data user data + +Returns: 0 +*/ + +static int callout_callback(pcre2_callout_enumerate_block_8 *cb, + void *callout_data) +{ +uint32_t i; +BOOL utf = (FLD(compiled_code, overall_options) & PCRE2_UTF) != 0; + +(void)callout_data; /* Not currently displayed */ + +fprintf(outfile, "Callout "); +if (cb->callout_string != NULL) + { + uint32_t delimiter = CODE_UNIT(cb->callout_string, -1); + fprintf(outfile, "%c", delimiter); + PCHARSV(cb->callout_string, 0, + cb->callout_string_length, utf, outfile); + for (i = 0; callout_start_delims[i] != 0; i++) + if (delimiter == callout_start_delims[i]) + { + delimiter = callout_end_delims[i]; + break; + } + fprintf(outfile, "%c ", delimiter); + } +else fprintf(outfile, "%d ", cb->callout_number); + +fprintf(outfile, "%.*s\n", + (int)((cb->next_item_length == 0)? 1 : cb->next_item_length), + pbuffer8 + cb->pattern_position); + +return 0; +} + + + +/************************************************* +* Show information about a pattern * +*************************************************/ + +/* This function is called after a pattern has been compiled if any of the +information-requesting controls have been set. + +Arguments: none + +Returns: PR_OK continue processing next line + PR_SKIP skip to a blank line + PR_ABEND abort the pcre2test run +*/ + +static int +show_pattern_info(void) +{ +uint32_t compile_options, overall_options, extra_options; + +if ((pat_patctl.control & (CTL_BINCODE|CTL_FULLBINCODE)) != 0) + { + fprintf(outfile, "------------------------------------------------------------------\n"); + PCRE2_PRINTINT((pat_patctl.control & CTL_FULLBINCODE) != 0); + } + +if ((pat_patctl.control & CTL_INFO) != 0) + { + int rc; + void *nametable; + uint8_t *start_bits; + BOOL heap_limit_set, match_limit_set, depth_limit_set; + uint32_t backrefmax, bsr_convention, capture_count, first_ctype, first_cunit, + hasbackslashc, hascrorlf, jchanged, last_ctype, last_cunit, match_empty, + depth_limit, heap_limit, match_limit, minlength, nameentrysize, namecount, + newline_convention; + + /* Exercise the error route. */ + + PCRE2_PATTERN_INFO(rc, compiled_code, 999, NULL); + (void)rc; + + /* These info requests may return PCRE2_ERROR_UNSET. */ + + switch(pattern_info(PCRE2_INFO_HEAPLIMIT, &heap_limit, TRUE)) + { + case 0: + heap_limit_set = TRUE; + break; + + case PCRE2_ERROR_UNSET: + heap_limit_set = FALSE; + break; + + default: + return PR_ABEND; + } + + switch(pattern_info(PCRE2_INFO_MATCHLIMIT, &match_limit, TRUE)) + { + case 0: + match_limit_set = TRUE; + break; + + case PCRE2_ERROR_UNSET: + match_limit_set = FALSE; + break; + + default: + return PR_ABEND; + } + + switch(pattern_info(PCRE2_INFO_DEPTHLIMIT, &depth_limit, TRUE)) + { + case 0: + depth_limit_set = TRUE; + break; + + case PCRE2_ERROR_UNSET: + depth_limit_set = FALSE; + break; + + default: + return PR_ABEND; + } + + /* These info requests should always succeed. */ + + if (pattern_info(PCRE2_INFO_BACKREFMAX, &backrefmax, FALSE) + + pattern_info(PCRE2_INFO_BSR, &bsr_convention, FALSE) + + pattern_info(PCRE2_INFO_CAPTURECOUNT, &capture_count, FALSE) + + pattern_info(PCRE2_INFO_FIRSTBITMAP, &start_bits, FALSE) + + pattern_info(PCRE2_INFO_FIRSTCODEUNIT, &first_cunit, FALSE) + + pattern_info(PCRE2_INFO_FIRSTCODETYPE, &first_ctype, FALSE) + + pattern_info(PCRE2_INFO_HASBACKSLASHC, &hasbackslashc, FALSE) + + pattern_info(PCRE2_INFO_HASCRORLF, &hascrorlf, FALSE) + + pattern_info(PCRE2_INFO_JCHANGED, &jchanged, FALSE) + + pattern_info(PCRE2_INFO_LASTCODEUNIT, &last_cunit, FALSE) + + pattern_info(PCRE2_INFO_LASTCODETYPE, &last_ctype, FALSE) + + pattern_info(PCRE2_INFO_MATCHEMPTY, &match_empty, FALSE) + + pattern_info(PCRE2_INFO_MINLENGTH, &minlength, FALSE) + + pattern_info(PCRE2_INFO_NAMECOUNT, &namecount, FALSE) + + pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &nameentrysize, FALSE) + + pattern_info(PCRE2_INFO_NAMETABLE, &nametable, FALSE) + + pattern_info(PCRE2_INFO_NEWLINE, &newline_convention, FALSE) + != 0) + return PR_ABEND; + + fprintf(outfile, "Capturing subpattern count = %d\n", capture_count); + + if (backrefmax > 0) + fprintf(outfile, "Max back reference = %d\n", backrefmax); + + if (maxlookbehind > 0) + fprintf(outfile, "Max lookbehind = %d\n", maxlookbehind); + + if (heap_limit_set) + fprintf(outfile, "Heap limit = %u\n", heap_limit); + + if (match_limit_set) + fprintf(outfile, "Match limit = %u\n", match_limit); + + if (depth_limit_set) + fprintf(outfile, "Depth limit = %u\n", depth_limit); + + if (namecount > 0) + { + fprintf(outfile, "Named capturing subpatterns:\n"); + for (; namecount > 0; namecount--) + { + int imm2_size = test_mode == PCRE8_MODE ? 2 : 1; + uint32_t length = (uint32_t)STRLEN(nametable + imm2_size); + fprintf(outfile, " "); + PCHARSV(nametable, imm2_size, length, FALSE, outfile); + while (length++ < nameentrysize - imm2_size) putc(' ', outfile); +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE) + fprintf(outfile, "%3d\n", (int)(((PCRE2_SPTR32)nametable)[0])); +#endif +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE) + fprintf(outfile, "%3d\n", (int)(((PCRE2_SPTR16)nametable)[0])); +#endif +#ifdef SUPPORT_PCRE2_8 + if (test_mode == PCRE8_MODE) + fprintf(outfile, "%3d\n", (int)( + ((((PCRE2_SPTR8)nametable)[0]) << 8) | ((PCRE2_SPTR8)nametable)[1])); +#endif + nametable = (void*)((PCRE2_SPTR8)nametable + nameentrysize * code_unit_size); + } + } + + if (hascrorlf) fprintf(outfile, "Contains explicit CR or LF match\n"); + if (hasbackslashc) fprintf(outfile, "Contains \\C\n"); + if (match_empty) fprintf(outfile, "May match empty string\n"); + + pattern_info(PCRE2_INFO_ARGOPTIONS, &compile_options, FALSE); + pattern_info(PCRE2_INFO_ALLOPTIONS, &overall_options, FALSE); + pattern_info(PCRE2_INFO_EXTRAOPTIONS, &extra_options, FALSE); + + /* Remove UTF/UCP if they were there only because of forbid_utf. This saves + cluttering up the verification output of non-UTF test files. */ + + if ((pat_patctl.options & PCRE2_NEVER_UTF) == 0) + { + compile_options &= ~PCRE2_NEVER_UTF; + overall_options &= ~PCRE2_NEVER_UTF; + } + + if ((pat_patctl.options & PCRE2_NEVER_UCP) == 0) + { + compile_options &= ~PCRE2_NEVER_UCP; + overall_options &= ~PCRE2_NEVER_UCP; + } + + if ((compile_options|overall_options) != 0) + { + if (compile_options == overall_options) + show_compile_options(compile_options, "Options:", "\n"); + else + { + show_compile_options(compile_options, "Compile options:", "\n"); + show_compile_options(overall_options, "Overall options:", "\n"); + } + } + + if (extra_options != 0) + show_compile_extra_options(extra_options, "Extra options:", "\n"); + + if (jchanged) fprintf(outfile, "Duplicate name status changes\n"); + + if ((pat_patctl.control2 & CTL2_BSR_SET) != 0 || + (FLD(compiled_code, flags) & PCRE2_BSR_SET) != 0) + fprintf(outfile, "\\R matches %s\n", (bsr_convention == PCRE2_BSR_UNICODE)? + "any Unicode newline" : "CR, LF, or CRLF"); + + if ((FLD(compiled_code, flags) & PCRE2_NL_SET) != 0) + { + switch (newline_convention) + { + case PCRE2_NEWLINE_CR: + fprintf(outfile, "Forced newline is CR\n"); + break; + + case PCRE2_NEWLINE_LF: + fprintf(outfile, "Forced newline is LF\n"); + break; + + case PCRE2_NEWLINE_CRLF: + fprintf(outfile, "Forced newline is CRLF\n"); + break; + + case PCRE2_NEWLINE_ANYCRLF: + fprintf(outfile, "Forced newline is CR, LF, or CRLF\n"); + break; + + case PCRE2_NEWLINE_ANY: + fprintf(outfile, "Forced newline is any Unicode newline\n"); + break; + + case PCRE2_NEWLINE_NUL: + fprintf(outfile, "Forced newline is NUL\n"); + break; + + default: + break; + } + } + + if (first_ctype == 2) + { + fprintf(outfile, "First code unit at start or follows newline\n"); + } + else if (first_ctype == 1) + { + const char *caseless = + ((FLD(compiled_code, flags) & PCRE2_FIRSTCASELESS) == 0)? + "" : " (caseless)"; + if (PRINTOK(first_cunit)) + fprintf(outfile, "First code unit = \'%c\'%s\n", first_cunit, caseless); + else + { + fprintf(outfile, "First code unit = "); + pchar(first_cunit, FALSE, outfile); + fprintf(outfile, "%s\n", caseless); + } + } + else if (start_bits != NULL) + { + int i; + int c = 24; + fprintf(outfile, "Starting code units: "); + for (i = 0; i < 256; i++) + { + if ((start_bits[i/8] & (1<<(i&7))) != 0) + { + if (c > 75) + { + fprintf(outfile, "\n "); + c = 2; + } + if (PRINTOK(i) && i != ' ') + { + fprintf(outfile, "%c ", i); + c += 2; + } + else + { + fprintf(outfile, "\\x%02x ", i); + c += 5; + } + } + } + fprintf(outfile, "\n"); + } + + if (last_ctype != 0) + { + const char *caseless = + ((FLD(compiled_code, flags) & PCRE2_LASTCASELESS) == 0)? + "" : " (caseless)"; + if (PRINTOK(last_cunit)) + fprintf(outfile, "Last code unit = \'%c\'%s\n", last_cunit, caseless); + else + { + fprintf(outfile, "Last code unit = "); + pchar(last_cunit, FALSE, outfile); + fprintf(outfile, "%s\n", caseless); + } + } + + fprintf(outfile, "Subject length lower bound = %d\n", minlength); + + if (pat_patctl.jit != 0 && (pat_patctl.control & CTL_JITVERIFY) != 0) + { + if (FLD(compiled_code, executable_jit) != NULL) + fprintf(outfile, "JIT compilation was successful\n"); + else + { +#ifdef SUPPORT_JIT + fprintf(outfile, "JIT compilation was not successful"); + if (jitrc != 0 && !print_error_message(jitrc, " (", ")")) + return PR_ABEND; + fprintf(outfile, "\n"); +#else + fprintf(outfile, "JIT support is not available in this version of PCRE2\n"); +#endif + } + } + } + +if ((pat_patctl.control & CTL_CALLOUT_INFO) != 0) + { + int errorcode; + PCRE2_CALLOUT_ENUMERATE(errorcode, callout_callback, 0); + if (errorcode != 0) + { + fprintf(outfile, "Callout enumerate failed: error %d: ", errorcode); + if (errorcode < 0 && !print_error_message(errorcode, "", "\n")) + return PR_ABEND; + return PR_SKIP; + } + } + +return PR_OK; +} + + + +/************************************************* +* Handle serialization error * +*************************************************/ + +/* Print an error message after a serialization failure. + +Arguments: + rc the error code + msg an initial message for what failed + +Returns: FALSE if print_error_message() fails +*/ + +static BOOL +serial_error(int rc, const char *msg) +{ +fprintf(outfile, "%s failed: error %d: ", msg, rc); +return print_error_message(rc, "", "\n"); +} + + + +/************************************************* +* Open file for save/load commands * +*************************************************/ + +/* This function decodes the file name and opens the file. + +Arguments: + buffptr point after the #command + mode open mode + fptr points to the FILE variable + +Returns: PR_OK or PR_ABEND +*/ + +static int +open_file(uint8_t *buffptr, const char *mode, FILE **fptr) +{ +char *endf; +char *filename = (char *)buffptr; +while (isspace(*filename)) filename++; +endf = filename + strlen8(filename); +while (endf > filename && isspace(endf[-1])) endf--; + +if (endf == filename) + { + fprintf(outfile, "** File name expected after #save\n"); + return PR_ABEND; + } + +*endf = 0; +*fptr = fopen((const char *)filename, mode); +if (*fptr == NULL) + { + fprintf(outfile, "** Failed to open '%s': %s\n", filename, strerror(errno)); + return PR_ABEND; + } + +return PR_OK; +} + + + +/************************************************* +* Process command line * +*************************************************/ + +/* This function is called for lines beginning with # and a character that is +not ! or whitespace, when encountered between tests, which means that there is +no compiled pattern (compiled_code is NULL). The line is in buffer. + +Arguments: none + +Returns: PR_OK continue processing next line + PR_SKIP skip to a blank line + PR_ABEND abort the pcre2test run +*/ + +static int +process_command(void) +{ +FILE *f; +PCRE2_SIZE serial_size; +size_t i; +int rc, cmd, cmdlen, yield; +uint16_t first_listed_newline; +const char *cmdname; +uint8_t *argptr, *serial; + +yield = PR_OK; +cmd = CMD_UNKNOWN; +cmdlen = 0; + +for (i = 0; i < cmdlistcount; i++) + { + cmdname = cmdlist[i].name; + cmdlen = strlen(cmdname); + if (strncmp((char *)(buffer+1), cmdname, cmdlen) == 0 && + isspace(buffer[cmdlen+1])) + { + cmd = cmdlist[i].value; + break; + } + } + +argptr = buffer + cmdlen + 1; + +if (restrict_for_perl_test && cmd != CMD_PATTERN && cmd != CMD_SUBJECT) + { + fprintf(outfile, "** #%s is not allowed after #perltest\n", cmdname); + return PR_ABEND; + } + +switch(cmd) + { + case CMD_UNKNOWN: + fprintf(outfile, "** Unknown command: %s", buffer); + break; + + case CMD_FORBID_UTF: + forbid_utf = PCRE2_NEVER_UTF|PCRE2_NEVER_UCP; + break; + + case CMD_PERLTEST: + restrict_for_perl_test = TRUE; + break; + + /* Set default pattern modifiers */ + + case CMD_PATTERN: + (void)decode_modifiers(argptr, CTX_DEFPAT, &def_patctl, NULL); + if (def_patctl.jit == 0 && (def_patctl.control & CTL_JITVERIFY) != 0) + def_patctl.jit = 7; + break; + + /* Set default subject modifiers */ + + case CMD_SUBJECT: + (void)decode_modifiers(argptr, CTX_DEFDAT, NULL, &def_datctl); + break; + + /* Check the default newline, and if not one of those listed, set up the + first one to be forced. An empty list unsets. */ + + case CMD_NEWLINE_DEFAULT: + local_newline_default = 0; /* Unset */ + first_listed_newline = 0; + for (;;) + { + while (isspace(*argptr)) argptr++; + if (*argptr == 0) break; + for (i = 1; i < sizeof(newlines)/sizeof(char *); i++) + { + size_t nlen = strlen(newlines[i]); + if (strncmpic(argptr, (const uint8_t *)newlines[i], nlen) == 0 && + isspace(argptr[nlen])) + { + if (i == NEWLINE_DEFAULT) return PR_OK; /* Default is valid */ + if (first_listed_newline == 0) first_listed_newline = i; + } + } + while (*argptr != 0 && !isspace(*argptr)) argptr++; + } + local_newline_default = first_listed_newline; + break; + + /* Pop or copy a compiled pattern off the stack. Modifiers that do not affect + the compiled pattern (e.g. to give information) are permitted. The default + pattern modifiers are ignored. */ + + case CMD_POP: + case CMD_POPCOPY: + if (patstacknext <= 0) + { + fprintf(outfile, "** Can't pop off an empty stack\n"); + return PR_SKIP; + } + memset(&pat_patctl, 0, sizeof(patctl)); /* Completely unset */ + if (!decode_modifiers(argptr, CTX_POPPAT, &pat_patctl, NULL)) + return PR_SKIP; + + if (cmd == CMD_POP) + { + SET(compiled_code, patstack[--patstacknext]); + } + else + { + PCRE2_CODE_COPY_FROM_VOID(compiled_code, patstack[patstacknext - 1]); + } + + if (pat_patctl.jit != 0) + { + PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit); + } + if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info(); + if ((pat_patctl.control & CTL_FRAMESIZE) != 0) show_framesize(); + if ((pat_patctl.control & CTL_ANYINFO) != 0) + { + rc = show_pattern_info(); + if (rc != PR_OK) return rc; + } + break; + + /* Save the stack of compiled patterns to a file, then empty the stack. */ + + case CMD_SAVE: + if (patstacknext <= 0) + { + fprintf(outfile, "** No stacked patterns to save\n"); + return PR_OK; + } + + rc = open_file(argptr+1, BINARY_OUTPUT_MODE, &f); + if (rc != PR_OK) return rc; + + PCRE2_SERIALIZE_ENCODE(rc, patstack, patstacknext, &serial, &serial_size, + general_context); + if (rc < 0) + { + fclose(f); + if (!serial_error(rc, "Serialization")) return PR_ABEND; + break; + } + + /* Write the length at the start of the file to make it straightforward to + get the right memory when re-loading. This saves having to read the file size + in different operating systems. To allow for different endianness (even + though reloading with the opposite endianness does not work), write the + length byte-by-byte. */ + + for (i = 0; i < 4; i++) fputc((serial_size >> (i*8)) & 255, f); + if (fwrite(serial, 1, serial_size, f) != serial_size) + { + fprintf(outfile, "** Wrong return from fwrite()\n"); + fclose(f); + return PR_ABEND; + } + + fclose(f); + PCRE2_SERIALIZE_FREE(serial); + while(patstacknext > 0) + { + SET(compiled_code, patstack[--patstacknext]); + SUB1(pcre2_code_free, compiled_code); + } + SET(compiled_code, NULL); + break; + + /* Load a set of compiled patterns from a file onto the stack */ + + case CMD_LOAD: + rc = open_file(argptr+1, BINARY_INPUT_MODE, &f); + if (rc != PR_OK) return rc; + + serial_size = 0; + for (i = 0; i < 4; i++) serial_size |= fgetc(f) << (i*8); + + serial = malloc(serial_size); + if (serial == NULL) + { + fprintf(outfile, "** Failed to get memory (size %" SIZ_FORM ") for #load\n", + SIZ_CAST serial_size); + fclose(f); + return PR_ABEND; + } + + i = fread(serial, 1, serial_size, f); + fclose(f); + + if (i != serial_size) + { + fprintf(outfile, "** Wrong return from fread()\n"); + yield = PR_ABEND; + } + else + { + PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(rc, serial); + if (rc < 0) + { + if (!serial_error(rc, "Get number of codes")) yield = PR_ABEND; + } + else + { + if (rc + patstacknext > PATSTACKSIZE) + { + fprintf(outfile, "** Not enough space on pattern stack for %d pattern%s\n", + rc, (rc == 1)? "" : "s"); + rc = PATSTACKSIZE - patstacknext; + fprintf(outfile, "** Decoding %d pattern%s\n", rc, + (rc == 1)? "" : "s"); + } + PCRE2_SERIALIZE_DECODE(rc, patstack + patstacknext, rc, serial, + general_context); + if (rc < 0) + { + if (!serial_error(rc, "Deserialization")) yield = PR_ABEND; + } + else patstacknext += rc; + } + } + + free(serial); + break; + } + +return yield; +} + + + +/************************************************* +* Process pattern line * +*************************************************/ + +/* This function is called when the input buffer contains the start of a +pattern. The first character is known to be a valid delimiter. The pattern is +read, modifiers are interpreted, and a suitable local context is set up for +this test. The pattern is then compiled. + +Arguments: none + +Returns: PR_OK continue processing next line + PR_SKIP skip to a blank line + PR_ABEND abort the pcre2test run +*/ + +static int +process_pattern(void) +{ +BOOL utf; +uint32_t k; +uint8_t *p = buffer; +unsigned int delimiter = *p++; +int errorcode; +void *use_pat_context; +uint32_t use_forbid_utf = forbid_utf; +PCRE2_SIZE patlen; +PCRE2_SIZE valgrind_access_length; +PCRE2_SIZE erroroffset; + +/* Initialize the context and pattern/data controls for this test from the +defaults. */ + +PATCTXCPY(pat_context, default_pat_context); +memcpy(&pat_patctl, &def_patctl, sizeof(patctl)); + +/* Find the end of the pattern, reading more lines if necessary. */ + +for(;;) + { + while (*p != 0) + { + if (*p == '\\' && p[1] != 0) p++; + else if (*p == delimiter) break; + p++; + } + if (*p != 0) break; + if ((p = extend_inputline(infile, p, " > ")) == NULL) + { + fprintf(outfile, "** Unexpected EOF\n"); + return PR_ABEND; + } + if (!INTERACTIVE(infile)) fprintf(outfile, "%s", (char *)p); + } + +/* If the first character after the delimiter is backslash, make the pattern +end with backslash. This is purely to provide a way of testing for the error +message when a pattern ends with backslash. */ + +if (p[1] == '\\') *p++ = '\\'; + +/* Terminate the pattern at the delimiter, and compute the length. */ + +*p++ = 0; +patlen = p - buffer - 2; + +/* Look for modifiers and options after the final delimiter. */ + +if (!decode_modifiers(p, CTX_PAT, &pat_patctl, NULL)) return PR_SKIP; +utf = (pat_patctl.options & PCRE2_UTF) != 0; + +/* The utf8_input modifier is not allowed in 8-bit mode, and is mutually +exclusive with the utf modifier. */ + +if ((pat_patctl.control & CTL_UTF8_INPUT) != 0) + { + if (test_mode == PCRE8_MODE) + { + fprintf(outfile, "** The utf8_input modifier is not allowed in 8-bit mode\n"); + return PR_SKIP; + } + if (utf) + { + fprintf(outfile, "** The utf and utf8_input modifiers are mutually exclusive\n"); + return PR_SKIP; + } + } + +/* The convert and posix modifiers are mutually exclusive. */ + +if (pat_patctl.convert_type != CONVERT_UNSET && + (pat_patctl.control & CTL_POSIX) != 0) + { + fprintf(outfile, "** The convert and posix modifiers are mutually exclusive\n"); + return PR_SKIP; + } + +/* Check for mutually exclusive control modifiers. At present, these are all in +the first control word. */ + +for (k = 0; k < sizeof(exclusive_pat_controls)/sizeof(uint32_t); k++) + { + uint32_t c = pat_patctl.control & exclusive_pat_controls[k]; + if (c != 0 && c != (c & (~c+1))) + { + show_controls(c, 0, "** Not allowed together:"); + fprintf(outfile, "\n"); + return PR_SKIP; + } + } + +/* Assume full JIT compile for jitverify and/or jitfast if nothing else was +specified. */ + +if (pat_patctl.jit == 0 && + (pat_patctl.control & (CTL_JITVERIFY|CTL_JITFAST)) != 0) + pat_patctl.jit = 7; + +/* Now copy the pattern to pbuffer8 for use in 8-bit testing and for reflecting +in callouts. Convert from hex if requested (literal strings in quotes may be +present within the hexadecimal pairs). The result must necessarily be fewer +characters so will always fit in pbuffer8. */ + +if ((pat_patctl.control & CTL_HEXPAT) != 0) + { + uint8_t *pp, *pt; + uint32_t c, d; + + pt = pbuffer8; + for (pp = buffer + 1; *pp != 0; pp++) + { + if (isspace(*pp)) continue; + c = *pp++; + + /* Handle a literal substring */ + + if (c == '\'' || c == '"') + { + uint8_t *pq = pp; + for (;; pp++) + { + d = *pp; + if (d == 0) + { + fprintf(outfile, "** Missing closing quote in hex pattern: " + "opening quote is at offset %" PTR_FORM ".\n", pq - buffer - 2); + return PR_SKIP; + } + if (d == c) break; + *pt++ = d; + } + } + + /* Expect a hex pair */ + + else + { + if (!isxdigit(c)) + { + fprintf(outfile, "** Unexpected non-hex-digit '%c' at offset %" + PTR_FORM " in hex pattern: quote missing?\n", c, pp - buffer - 2); + return PR_SKIP; + } + if (*pp == 0) + { + fprintf(outfile, "** Odd number of digits in hex pattern\n"); + return PR_SKIP; + } + d = *pp; + if (!isxdigit(d)) + { + fprintf(outfile, "** Unexpected non-hex-digit '%c' at offset %" + PTR_FORM " in hex pattern: quote missing?\n", d, pp - buffer - 1); + return PR_SKIP; + } + c = toupper(c); + d = toupper(d); + *pt++ = ((isdigit(c)? (c - '0') : (c - 'A' + 10)) << 4) + + (isdigit(d)? (d - '0') : (d - 'A' + 10)); + } + } + *pt = 0; + patlen = pt - pbuffer8; + } + +/* If not a hex string, process for repetition expansion if requested. */ + +else if ((pat_patctl.control & CTL_EXPAND) != 0) + { + uint8_t *pp, *pt; + + pt = pbuffer8; + for (pp = buffer + 1; *pp != 0; pp++) + { + uint8_t *pc = pp; + uint32_t count = 1; + size_t length = 1; + + /* Check for replication syntax; if not found, the defaults just set will + prevail and one character will be copied. */ + + if (pp[0] == '\\' && pp[1] == '[') + { + uint8_t *pe; + for (pe = pp + 2; *pe != 0; pe++) + { + if (pe[0] == ']' && pe[1] == '{') + { + uint32_t clen = pe - pc - 2; + uint32_t i = 0; + unsigned long uli; + char *endptr; + + pe += 2; + uli = strtoul((const char *)pe, &endptr, 10); + if (U32OVERFLOW(uli)) + { + fprintf(outfile, "** Pattern repeat count too large\n"); + return PR_SKIP; + } + + i = (uint32_t)uli; + pe = (uint8_t *)endptr; + if (*pe == '}') + { + if (i == 0) + { + fprintf(outfile, "** Zero repeat not allowed\n"); + return PR_SKIP; + } + pc += 2; + count = i; + length = clen; + pp = pe; + break; + } + } + } + } + + /* Add to output. If the buffer is too small expand it. The function for + expanding buffers always keeps buffer and pbuffer8 in step as far as their + size goes. */ + + while (pt + count * length > pbuffer8 + pbuffer8_size) + { + size_t pc_offset = pc - buffer; + size_t pp_offset = pp - buffer; + size_t pt_offset = pt - pbuffer8; + expand_input_buffers(); + pc = buffer + pc_offset; + pp = buffer + pp_offset; + pt = pbuffer8 + pt_offset; + } + + for (; count > 0; count--) + { + memcpy(pt, pc, length); + pt += length; + } + } + + *pt = 0; + patlen = pt - pbuffer8; + + if ((pat_patctl.control & CTL_INFO) != 0) + fprintf(outfile, "Expanded: %s\n", pbuffer8); + } + +/* Neither hex nor expanded, just copy the input verbatim. */ + +else + { + strncpy((char *)pbuffer8, (char *)(buffer+1), patlen + 1); + } + +/* Sort out character tables */ + +if (pat_patctl.locale[0] != 0) + { + if (pat_patctl.tables_id != 0) + { + fprintf(outfile, "** 'Locale' and 'tables' must not both be set\n"); + return PR_SKIP; + } + if (setlocale(LC_CTYPE, (const char *)pat_patctl.locale) == NULL) + { + fprintf(outfile, "** Failed to set locale '%s'\n", pat_patctl.locale); + return PR_SKIP; + } + if (strcmp((const char *)pat_patctl.locale, (const char *)locale_name) != 0) + { + strcpy((char *)locale_name, (char *)pat_patctl.locale); + if (locale_tables != NULL) free((void *)locale_tables); + PCRE2_MAKETABLES(locale_tables); + } + use_tables = locale_tables; + } + +else switch (pat_patctl.tables_id) + { + case 0: use_tables = NULL; break; + case 1: use_tables = tables1; break; + case 2: use_tables = tables2; break; + default: + fprintf(outfile, "** 'Tables' must specify 0, 1, or 2.\n"); + return PR_SKIP; + } + +PCRE2_SET_CHARACTER_TABLES(pat_context, use_tables); + +/* Set up for the stackguard test. */ + +if (pat_patctl.stackguard_test != 0) + { + PCRE2_SET_COMPILE_RECURSION_GUARD(pat_context, stack_guard, NULL); + } + +/* Handle compiling via the POSIX interface, which doesn't support the +timing, showing, or debugging options, nor the ability to pass over +local character tables. Neither does it have 16-bit or 32-bit support. */ + +if ((pat_patctl.control & CTL_POSIX) != 0) + { +#ifdef SUPPORT_PCRE2_8 + int rc; + int cflags = 0; + const char *msg = "** Ignored with POSIX interface:"; +#endif + + if (test_mode != PCRE8_MODE) + { + fprintf(outfile, "** The POSIX interface is available only in 8-bit mode\n"); + return PR_SKIP; + } + +#ifdef SUPPORT_PCRE2_8 + /* Check for features that the POSIX interface does not support. */ + + if (pat_patctl.locale[0] != 0) prmsg(&msg, "locale"); + if (pat_patctl.replacement[0] != 0) prmsg(&msg, "replace"); + if (pat_patctl.tables_id != 0) prmsg(&msg, "tables"); + if (pat_patctl.stackguard_test != 0) prmsg(&msg, "stackguard"); + if (timeit > 0) prmsg(&msg, "timing"); + if (pat_patctl.jit != 0) prmsg(&msg, "JIT"); + + if ((pat_patctl.options & ~POSIX_SUPPORTED_COMPILE_OPTIONS) != 0) + { + show_compile_options( + pat_patctl.options & ~POSIX_SUPPORTED_COMPILE_OPTIONS, msg, ""); + msg = ""; + } + + if ((FLD(pat_context, extra_options) & + ~POSIX_SUPPORTED_COMPILE_EXTRA_OPTIONS) != 0) + { + show_compile_extra_options( + FLD(pat_context, extra_options) & ~POSIX_SUPPORTED_COMPILE_EXTRA_OPTIONS, + msg, ""); + msg = ""; + } + + if ((pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS) != 0 || + (pat_patctl.control2 & ~POSIX_SUPPORTED_COMPILE_CONTROLS2) != 0) + { + show_controls(pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS, + pat_patctl.control2 & ~POSIX_SUPPORTED_COMPILE_CONTROLS2, msg); + msg = ""; + } + + if (local_newline_default != 0) prmsg(&msg, "#newline_default"); + if (FLD(pat_context, max_pattern_length) != PCRE2_UNSET) + prmsg(&msg, "max_pattern_length"); + if (FLD(pat_context, parens_nest_limit) != PARENS_NEST_DEFAULT) + prmsg(&msg, "parens_nest_limit"); + + if (msg[0] == 0) fprintf(outfile, "\n"); + + /* Translate PCRE2 options to POSIX options and then compile. */ + + if (utf) cflags |= REG_UTF; + if ((pat_patctl.control & CTL_POSIX_NOSUB) != 0) cflags |= REG_NOSUB; + if ((pat_patctl.options & PCRE2_UCP) != 0) cflags |= REG_UCP; + if ((pat_patctl.options & PCRE2_CASELESS) != 0) cflags |= REG_ICASE; + if ((pat_patctl.options & PCRE2_LITERAL) != 0) cflags |= REG_NOSPEC; + if ((pat_patctl.options & PCRE2_MULTILINE) != 0) cflags |= REG_NEWLINE; + if ((pat_patctl.options & PCRE2_DOTALL) != 0) cflags |= REG_DOTALL; + if ((pat_patctl.options & PCRE2_UNGREEDY) != 0) cflags |= REG_UNGREEDY; + + if ((pat_patctl.control & (CTL_HEXPAT|CTL_USE_LENGTH)) != 0) + { + preg.re_endp = (char *)pbuffer8 + patlen; + cflags |= REG_PEND; + } + + rc = regcomp(&preg, (char *)pbuffer8, cflags); + + /* Compiling failed */ + + if (rc != 0) + { + size_t bsize, usize; + int psize; + + preg.re_pcre2_code = NULL; /* In case something was left in there */ + preg.re_match_data = NULL; + + bsize = (pat_patctl.regerror_buffsize != 0)? + pat_patctl.regerror_buffsize : pbuffer8_size; + if (bsize + 8 < pbuffer8_size) + memcpy(pbuffer8 + bsize, "DEADBEEF", 8); + usize = regerror(rc, &preg, (char *)pbuffer8, bsize); + + /* Inside regerror(), snprintf() is used. If the buffer is too small, some + versions of snprintf() put a zero byte at the end, but others do not. + Therefore, we print a maximum of one less than the size of the buffer. */ + + psize = (int)bsize - 1; + fprintf(outfile, "Failed: POSIX code %d: %.*s\n", rc, psize, pbuffer8); + if (usize > bsize) + { + fprintf(outfile, "** regerror() message truncated\n"); + if (memcmp(pbuffer8 + bsize, "DEADBEEF", 8) != 0) + fprintf(outfile, "** regerror() buffer overflow\n"); + } + return PR_SKIP; + } + + /* Compiling succeeded. Check that the values in the preg block are sensible. + It can happen that pcre2test is accidentally linked with a different POSIX + library which succeeds, but of course puts different things into preg. In + this situation, calling regfree() may cause a segfault (or invalid free() in + valgrind), so ensure that preg.re_pcre2_code is NULL, which suppresses the + calling of regfree() on exit. */ + + if (preg.re_pcre2_code == NULL || + ((pcre2_real_code_8 *)preg.re_pcre2_code)->magic_number != MAGIC_NUMBER || + ((pcre2_real_code_8 *)preg.re_pcre2_code)->top_bracket != preg.re_nsub || + preg.re_match_data == NULL || + preg.re_cflags != cflags) + { + fprintf(outfile, + "** The regcomp() function returned zero (success), but the values set\n" + "** in the preg block are not valid for PCRE2. Check that pcre2test is\n" + "** linked with PCRE2's pcre2posix module (-lpcre2-posix) and not with\n" + "** some other POSIX regex library.\n**\n"); + preg.re_pcre2_code = NULL; + return PR_ABEND; + } + + return PR_OK; +#endif /* SUPPORT_PCRE2_8 */ + } + +/* Handle compiling via the native interface. Controls that act later are +ignored with "push". Replacements are locked out. */ + +if ((pat_patctl.control & (CTL_PUSH|CTL_PUSHCOPY|CTL_PUSHTABLESCOPY)) != 0) + { + if (pat_patctl.replacement[0] != 0) + { + fprintf(outfile, "** Replacement text is not supported with 'push'.\n"); + return PR_OK; + } + if ((pat_patctl.control & ~PUSH_SUPPORTED_COMPILE_CONTROLS) != 0 || + (pat_patctl.control2 & ~PUSH_SUPPORTED_COMPILE_CONTROLS2) != 0) + { + show_controls(pat_patctl.control & ~PUSH_SUPPORTED_COMPILE_CONTROLS, + pat_patctl.control2 & ~PUSH_SUPPORTED_COMPILE_CONTROLS2, + "** Ignored when compiled pattern is stacked with 'push':"); + fprintf(outfile, "\n"); + } + if ((pat_patctl.control & PUSH_COMPILE_ONLY_CONTROLS) != 0 || + (pat_patctl.control2 & PUSH_COMPILE_ONLY_CONTROLS2) != 0) + { + show_controls(pat_patctl.control & PUSH_COMPILE_ONLY_CONTROLS, + pat_patctl.control2 & PUSH_COMPILE_ONLY_CONTROLS2, + "** Applies only to compile when pattern is stacked with 'push':"); + fprintf(outfile, "\n"); + } + } + +/* Convert the input in non-8-bit modes. */ + +errorcode = 0; + +#ifdef SUPPORT_PCRE2_16 +if (test_mode == PCRE16_MODE) errorcode = to16(pbuffer8, utf, &patlen); +#endif + +#ifdef SUPPORT_PCRE2_32 +if (test_mode == PCRE32_MODE) errorcode = to32(pbuffer8, utf, &patlen); +#endif + +switch(errorcode) + { + case -1: + fprintf(outfile, "** Failed: invalid UTF-8 string cannot be " + "converted to %d-bit string\n", (test_mode == PCRE16_MODE)? 16:32); + return PR_SKIP; + + case -2: + fprintf(outfile, "** Failed: character value greater than 0x10ffff " + "cannot be converted to UTF\n"); + return PR_SKIP; + + case -3: + fprintf(outfile, "** Failed: character value greater than 0xffff " + "cannot be converted to 16-bit in non-UTF mode\n"); + return PR_SKIP; + + default: + break; + } + +/* The pattern is now in pbuffer[8|16|32], with the length in code units in +patlen. If it is to be converted, copy the result back afterwards so that it +ends up back in the usual place. */ + +if (pat_patctl.convert_type != CONVERT_UNSET) + { + int rc; + int convert_return = PR_OK; + uint32_t convert_options = pat_patctl.convert_type; + void *converted_pattern; + PCRE2_SIZE converted_length; + + if (pat_patctl.convert_length != 0) + { + converted_length = pat_patctl.convert_length; + converted_pattern = malloc(converted_length * code_unit_size); + if (converted_pattern == NULL) + { + fprintf(outfile, "** Failed: malloc failed for converted pattern\n"); + return PR_SKIP; + } + } + else converted_pattern = NULL; /* Let the library allocate */ + + if (utf) convert_options |= PCRE2_CONVERT_UTF; + if ((pat_patctl.options & PCRE2_NO_UTF_CHECK) != 0) + convert_options |= PCRE2_CONVERT_NO_UTF_CHECK; + + CONCTXCPY(con_context, default_con_context); + + if (pat_patctl.convert_glob_escape != 0) + { + uint32_t escape = (pat_patctl.convert_glob_escape == '0')? 0 : + pat_patctl.convert_glob_escape; + PCRE2_SET_GLOB_ESCAPE(rc, con_context, escape); + if (rc != 0) + { + fprintf(outfile, "** Invalid glob escape '%c'\n", + pat_patctl.convert_glob_escape); + convert_return = PR_SKIP; + goto CONVERT_FINISH; + } + } + + if (pat_patctl.convert_glob_separator != 0) + { + PCRE2_SET_GLOB_SEPARATOR(rc, con_context, pat_patctl.convert_glob_separator); + if (rc != 0) + { + fprintf(outfile, "** Invalid glob separator '%c'\n", + pat_patctl.convert_glob_separator); + convert_return = PR_SKIP; + goto CONVERT_FINISH; + } + } + + PCRE2_PATTERN_CONVERT(rc, pbuffer, patlen, convert_options, + &converted_pattern, &converted_length, con_context); + + if (rc != 0) + { + fprintf(outfile, "** Pattern conversion error at offset %" SIZ_FORM ": ", + SIZ_CAST converted_length); + convert_return = print_error_message(rc, "", "\n")? PR_SKIP:PR_ABEND; + } + + /* Output the converted pattern, then copy it. */ + + else + { + PCHARSV(converted_pattern, 0, converted_length, utf, outfile); + fprintf(outfile, "\n"); + patlen = converted_length; + CONVERT_COPY(pbuffer, converted_pattern, converted_length + 1); + } + + /* Free the converted pattern. */ + + CONVERT_FINISH: + if (pat_patctl.convert_length != 0) + free(converted_pattern); + else + PCRE2_CONVERTED_PATTERN_FREE(converted_pattern); + + /* Return if conversion was unsuccessful. */ + + if (convert_return != PR_OK) return convert_return; + } + +/* By default we pass a zero-terminated pattern, but a length is passed if +"use_length" was specified or this is a hex pattern (which might contain binary +zeros). When valgrind is supported, arrange for the unused part of the buffer +to be marked as no access. */ + +valgrind_access_length = patlen; +if ((pat_patctl.control & (CTL_HEXPAT|CTL_USE_LENGTH)) == 0) + { + patlen = PCRE2_ZERO_TERMINATED; + valgrind_access_length += 1; /* For the terminating zero */ + } + +#ifdef SUPPORT_VALGRIND +#ifdef SUPPORT_PCRE2_8 +if (test_mode == PCRE8_MODE && pbuffer8 != NULL) + { + VALGRIND_MAKE_MEM_NOACCESS(pbuffer8 + valgrind_access_length, + pbuffer8_size - valgrind_access_length); + } +#endif +#ifdef SUPPORT_PCRE2_16 +if (test_mode == PCRE16_MODE && pbuffer16 != NULL) + { + VALGRIND_MAKE_MEM_NOACCESS(pbuffer16 + valgrind_access_length, + pbuffer16_size - valgrind_access_length*sizeof(uint16_t)); + } +#endif +#ifdef SUPPORT_PCRE2_32 +if (test_mode == PCRE32_MODE && pbuffer32 != NULL) + { + VALGRIND_MAKE_MEM_NOACCESS(pbuffer32 + valgrind_access_length, + pbuffer32_size - valgrind_access_length*sizeof(uint32_t)); + } +#endif +#else /* Valgrind not supported */ +(void)valgrind_access_length; /* Avoid compiler warning */ +#endif + +/* If #newline_default has been used and the library was not compiled with an +appropriate default newline setting, local_newline_default will be non-zero. We +use this if there is no explicit newline modifier. */ + +if ((pat_patctl.control2 & CTL2_NL_SET) == 0 && local_newline_default != 0) + { + SETFLD(pat_context, newline_convention, local_newline_default); + } + +/* The null_context modifier is used to test calling pcre2_compile() with a +NULL context. */ + +use_pat_context = ((pat_patctl.control & CTL_NULLCONTEXT) != 0)? + NULL : PTR(pat_context); + +/* If PCRE2_LITERAL is set, set use_forbid_utf zero because PCRE2_NEVER_UTF +and PCRE2_NEVER_UCP are invalid with it. */ + +if ((pat_patctl.options & PCRE2_LITERAL) != 0) use_forbid_utf = 0; + +/* Compile many times when timing. */ + +if (timeit > 0) + { + int i; + clock_t time_taken = 0; + for (i = 0; i < timeit; i++) + { + clock_t start_time = clock(); + PCRE2_COMPILE(compiled_code, pbuffer, patlen, + pat_patctl.options|use_forbid_utf, &errorcode, &erroroffset, + use_pat_context); + time_taken += clock() - start_time; + if (TEST(compiled_code, !=, NULL)) + { SUB1(pcre2_code_free, compiled_code); } + } + total_compile_time += time_taken; + fprintf(outfile, "Compile time %.4f milliseconds\n", + (((double)time_taken * 1000.0) / (double)timeit) / + (double)CLOCKS_PER_SEC); + } + +/* A final compile that is used "for real". */ + +PCRE2_COMPILE(compiled_code, pbuffer, patlen, pat_patctl.options|use_forbid_utf, + &errorcode, &erroroffset, use_pat_context); + +/* Call the JIT compiler if requested. When timing, we must free and recompile +the pattern each time because that is the only way to free the JIT compiled +code. We know that compilation will always succeed. */ + +if (TEST(compiled_code, !=, NULL) && pat_patctl.jit != 0) + { + if (timeit > 0) + { + int i; + clock_t time_taken = 0; + for (i = 0; i < timeit; i++) + { + clock_t start_time; + SUB1(pcre2_code_free, compiled_code); + PCRE2_COMPILE(compiled_code, pbuffer, patlen, + pat_patctl.options|use_forbid_utf, &errorcode, &erroroffset, + use_pat_context); + start_time = clock(); + PCRE2_JIT_COMPILE(jitrc,compiled_code, pat_patctl.jit); + time_taken += clock() - start_time; + } + total_jit_compile_time += time_taken; + fprintf(outfile, "JIT compile %.4f milliseconds\n", + (((double)time_taken * 1000.0) / (double)timeit) / + (double)CLOCKS_PER_SEC); + } + else + { + PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit); + } + } + +/* If valgrind is supported, mark the pbuffer as accessible again. The 16-bit +and 32-bit buffers can be marked completely undefined, but we must leave the +pattern in the 8-bit buffer defined because it may be read from a callout +during matching. */ + +#ifdef SUPPORT_VALGRIND +#ifdef SUPPORT_PCRE2_8 +if (test_mode == PCRE8_MODE) + { + VALGRIND_MAKE_MEM_UNDEFINED(pbuffer8 + valgrind_access_length, + pbuffer8_size - valgrind_access_length); + } +#endif +#ifdef SUPPORT_PCRE2_16 +if (test_mode == PCRE16_MODE) + { + VALGRIND_MAKE_MEM_UNDEFINED(pbuffer16, pbuffer16_size); + } +#endif +#ifdef SUPPORT_PCRE2_32 +if (test_mode == PCRE32_MODE) + { + VALGRIND_MAKE_MEM_UNDEFINED(pbuffer32, pbuffer32_size); + } +#endif +#endif + +/* Compilation failed; go back for another re, skipping to blank line +if non-interactive. */ + +if (TEST(compiled_code, ==, NULL)) + { + fprintf(outfile, "Failed: error %d at offset %d: ", errorcode, + (int)erroroffset); + if (!print_error_message(errorcode, "", "\n")) return PR_ABEND; + return PR_SKIP; + } + +/* If forbid_utf is non-zero, we are running a non-UTF test. UTF and UCP are +locked out at compile time, but we must also check for occurrences of \P, \p, +and \X, which are only supported when Unicode is supported. */ + +if (forbid_utf != 0) + { + if ((FLD(compiled_code, flags) & PCRE2_HASBKPORX) != 0) + { + fprintf(outfile, "** \\P, \\p, and \\X are not allowed after the " + "#forbid_utf command\n"); + return PR_SKIP; + } + } + +/* Remember the maximum lookbehind, for partial matching. */ + +if (pattern_info(PCRE2_INFO_MAXLOOKBEHIND, &maxlookbehind, FALSE) != 0) + return PR_ABEND; + +/* If an explicit newline modifier was given, set the information flag in the +pattern so that it is preserved over push/pop. */ + +if ((pat_patctl.control2 & CTL2_NL_SET) != 0) + { + SETFLD(compiled_code, flags, FLD(compiled_code, flags) | PCRE2_NL_SET); + } + +/* Output code size and other information if requested. */ + +if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info(); +if ((pat_patctl.control & CTL_FRAMESIZE) != 0) show_framesize(); +if ((pat_patctl.control & CTL_ANYINFO) != 0) + { + int rc = show_pattern_info(); + if (rc != PR_OK) return rc; + } + +/* The "push" control requests that the compiled pattern be remembered on a +stack. This is mainly for testing the serialization functionality. */ + +if ((pat_patctl.control & CTL_PUSH) != 0) + { + if (patstacknext >= PATSTACKSIZE) + { + fprintf(outfile, "** Too many pushed patterns (max %d)\n", PATSTACKSIZE); + return PR_ABEND; + } + patstack[patstacknext++] = PTR(compiled_code); + SET(compiled_code, NULL); + } + +/* The "pushcopy" and "pushtablescopy" controls are similar, but push a +copy of the pattern, the latter with a copy of its character tables. This tests +the pcre2_code_copy() and pcre2_code_copy_with_tables() functions. */ + +if ((pat_patctl.control & (CTL_PUSHCOPY|CTL_PUSHTABLESCOPY)) != 0) + { + if (patstacknext >= PATSTACKSIZE) + { + fprintf(outfile, "** Too many pushed patterns (max %d)\n", PATSTACKSIZE); + return PR_ABEND; + } + if ((pat_patctl.control & CTL_PUSHCOPY) != 0) + { + PCRE2_CODE_COPY_TO_VOID(patstack[patstacknext++], compiled_code); + } + else + { + PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(patstack[patstacknext++], + compiled_code); } + } + +return PR_OK; +} + + + +/************************************************* +* Check heap, match or depth limit * +*************************************************/ + +/* This is used for DFA, normal, and JIT fast matching. For DFA matching it +should only be called with the third argument set to PCRE2_ERROR_DEPTHLIMIT. + +Arguments: + pp the subject string + ulen length of subject or PCRE2_ZERO_TERMINATED + errnumber defines which limit to test + msg string to include in final message + +Returns: the return from the final match function call +*/ + +static int +check_match_limit(uint8_t *pp, PCRE2_SIZE ulen, int errnumber, const char *msg) +{ +int capcount; +uint32_t min = 0; +uint32_t mid = 64; +uint32_t max = UINT32_MAX; + +PCRE2_SET_MATCH_LIMIT(dat_context, max); +PCRE2_SET_DEPTH_LIMIT(dat_context, max); +PCRE2_SET_HEAP_LIMIT(dat_context, max); + +for (;;) + { + uint32_t stack_start = 0; + + if (errnumber == PCRE2_ERROR_HEAPLIMIT) + { + PCRE2_SET_HEAP_LIMIT(dat_context, mid); + } + else if (errnumber == PCRE2_ERROR_MATCHLIMIT) + { + PCRE2_SET_MATCH_LIMIT(dat_context, mid); + } + else + { + PCRE2_SET_DEPTH_LIMIT(dat_context, mid); + } + + if ((dat_datctl.control & CTL_DFA) != 0) + { + stack_start = DFA_START_RWS_SIZE/1024; + if (dfa_workspace == NULL) + dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int)); + if (dfa_matched++ == 0) + dfa_workspace[0] = -1; /* To catch bad restart */ + PCRE2_DFA_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, + dat_datctl.options, match_data, + PTR(dat_context), dfa_workspace, DFA_WS_DIMENSION); + } + + else if ((pat_patctl.control & CTL_JITFAST) != 0) + PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, + dat_datctl.options, match_data, PTR(dat_context)); + + else + { + stack_start = START_FRAMES_SIZE/1024; + PCRE2_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, + dat_datctl.options, match_data, PTR(dat_context)); + } + + if (capcount == errnumber) + { + if ((mid & 0x80000000u) != 0) + { + fprintf(outfile, "Can't find minimum %s limit: check pattern for " + "restriction\n", msg); + break; + } + + min = mid; + mid = (mid == max - 1)? max : (max != UINT32_MAX)? (min + max)/2 : mid*2; + } + else if (capcount >= 0 || + capcount == PCRE2_ERROR_NOMATCH || + capcount == PCRE2_ERROR_PARTIAL) + { + /* If we've not hit the error with a heap limit less than the size of the + initial stack frame vector (for pcre2_match()) or the initial stack + workspace vector (for pcre2_dfa_match()), the heap is not being used, so + the minimum limit is zero; there's no need to go on. The other limits are + always greater than zero. */ + + if (errnumber == PCRE2_ERROR_HEAPLIMIT && mid < stack_start) + { + fprintf(outfile, "Minimum %s limit = 0\n", msg); + break; + } + if (mid == min + 1) + { + fprintf(outfile, "Minimum %s limit = %d\n", msg, mid); + break; + } + max = mid; + mid = (min + max)/2; + } + else break; /* Some other error */ + } + +return capcount; +} + + + +/************************************************* +* Callout function * +*************************************************/ + +/* Called from a PCRE2 library as a result of the (?C) item. We print out where +we are in the match (unless suppressed). Yield zero unless more callouts than +the fail count, or the callout data is not zero. The only differences in the +callout block for different code unit widths are that the pointers to the +subject, the most recent MARK, and a callout argument string point to strings +of the appropriate width. Casts can be used to deal with this. + +Argument: a pointer to a callout block +Return: +*/ + +static int +callout_function(pcre2_callout_block_8 *cb, void *callout_data_ptr) +{ +FILE *f, *fdefault; +uint32_t i, pre_start, post_start, subject_length; +PCRE2_SIZE current_position; +BOOL utf = (FLD(compiled_code, overall_options) & PCRE2_UTF) != 0; +BOOL callout_capture = (dat_datctl.control & CTL_CALLOUT_CAPTURE) != 0; +BOOL callout_where = (dat_datctl.control2 & CTL2_CALLOUT_NO_WHERE) == 0; + +/* The FILE f is used for echoing the subject string if it is non-NULL. This +happens only once in simple cases, but we want to repeat after any additional +output caused by CALLOUT_EXTRA. */ + +fdefault = (!first_callout && !callout_capture && cb->callout_string == NULL)? + NULL : outfile; + +if ((dat_datctl.control2 & CTL2_CALLOUT_EXTRA) != 0) + { + f = outfile; + switch (cb->callout_flags) + { + case PCRE2_CALLOUT_BACKTRACK: + fprintf(f, "Backtrack\n"); + break; + + case PCRE2_CALLOUT_STARTMATCH|PCRE2_CALLOUT_BACKTRACK: + fprintf(f, "Backtrack\nNo other matching paths\n"); + /* Fall through */ + + case PCRE2_CALLOUT_STARTMATCH: + fprintf(f, "New match attempt\n"); + break; + + default: + f = fdefault; + break; + } + } +else f = fdefault; + +/* For a callout with a string argument, show the string first because there +isn't a tidy way to fit it in the rest of the data. */ + +if (cb->callout_string != NULL) + { + uint32_t delimiter = CODE_UNIT(cb->callout_string, -1); + fprintf(outfile, "Callout (%" SIZ_FORM "): %c", + SIZ_CAST cb->callout_string_offset, delimiter); + PCHARSV(cb->callout_string, 0, + cb->callout_string_length, utf, outfile); + for (i = 0; callout_start_delims[i] != 0; i++) + if (delimiter == callout_start_delims[i]) + { + delimiter = callout_end_delims[i]; + break; + } + fprintf(outfile, "%c", delimiter); + if (!callout_capture) fprintf(outfile, "\n"); + } + +/* Show captured strings if required */ + +if (callout_capture) + { + if (cb->callout_string == NULL) + fprintf(outfile, "Callout %d:", cb->callout_number); + fprintf(outfile, " last capture = %d\n", cb->capture_last); + for (i = 2; i < cb->capture_top * 2; i += 2) + { + fprintf(outfile, "%2d: ", i/2); + if (cb->offset_vector[i] == PCRE2_UNSET) + fprintf(outfile, ""); + else + { + PCHARSV(cb->subject, cb->offset_vector[i], + cb->offset_vector[i+1] - cb->offset_vector[i], utf, f); + } + fprintf(outfile, "\n"); + } + } + +/* Unless suppressed, re-print the subject in canonical form (with escapes for +non-printing characters), the first time, or if giving full details. On +subsequent calls in the same match, we use PCHARS() just to find the printed +lengths of the substrings. */ + +if (callout_where) + { + if (f != NULL) fprintf(f, "--->"); + + /* The subject before the match start. */ + + PCHARS(pre_start, cb->subject, 0, cb->start_match, utf, f); + + /* If a lookbehind is involved, the current position may be earlier than the + match start. If so, use the match start instead. */ + + current_position = (cb->current_position >= cb->start_match)? + cb->current_position : cb->start_match; + + /* The subject between the match start and the current position. */ + + PCHARS(post_start, cb->subject, cb->start_match, + current_position - cb->start_match, utf, f); + + /* Print from the current position to the end. */ + + PCHARSV(cb->subject, current_position, cb->subject_length - current_position, + utf, f); + + /* Calculate the total subject printed length (no print). */ + + PCHARS(subject_length, cb->subject, 0, cb->subject_length, utf, NULL); + + if (f != NULL) fprintf(f, "\n"); + + /* For automatic callouts, show the pattern offset. Otherwise, for a + numerical callout whose number has not already been shown with captured + strings, show the number here. A callout with a string argument has been + displayed above. */ + + if (cb->callout_number == 255) + { + fprintf(outfile, "%+3d ", (int)cb->pattern_position); + if (cb->pattern_position > 99) fprintf(outfile, "\n "); + } + else + { + if (callout_capture || cb->callout_string != NULL) fprintf(outfile, " "); + else fprintf(outfile, "%3d ", cb->callout_number); + } + + /* Now show position indicators */ + + for (i = 0; i < pre_start; i++) fprintf(outfile, " "); + fprintf(outfile, "^"); + + if (post_start > 0) + { + for (i = 0; i < post_start - 1; i++) fprintf(outfile, " "); + fprintf(outfile, "^"); + } + + for (i = 0; i < subject_length - pre_start - post_start + 4; i++) + fprintf(outfile, " "); + + if (cb->next_item_length != 0) + fprintf(outfile, "%.*s", (int)(cb->next_item_length), + pbuffer8 + cb->pattern_position); + else + fprintf(outfile, "End of pattern"); + + fprintf(outfile, "\n"); + } + +first_callout = FALSE; + +/* Show any mark info */ + +if (cb->mark != last_callout_mark) + { + if (cb->mark == NULL) + fprintf(outfile, "Latest Mark: \n"); + else + { + fprintf(outfile, "Latest Mark: "); + PCHARSV(cb->mark, 0, -1, utf, outfile); + putc('\n', outfile); + } + last_callout_mark = cb->mark; + } + +/* Show callout data */ + +if (callout_data_ptr != NULL) + { + int callout_data = *((int32_t *)callout_data_ptr); + if (callout_data != 0) + { + fprintf(outfile, "Callout data = %d\n", callout_data); + return callout_data; + } + } + +/* Keep count and give the appropriate return code */ + +callout_count++; + +if (cb->callout_number == dat_datctl.cerror[0] && + callout_count >= dat_datctl.cerror[1]) + return PCRE2_ERROR_CALLOUT; + +if (cb->callout_number == dat_datctl.cfail[0] && + callout_count >= dat_datctl.cfail[1]) + return 1; + +return 0; +} + + + +/************************************************* +* Handle *MARK and copy/get tests * +*************************************************/ + +/* This function is called after complete and partial matches. It runs the +tests for substring extraction. + +Arguments: + utf TRUE for utf + capcount return from pcre2_match() + +Returns: FALSE if print_error_message() fails +*/ + +static BOOL +copy_and_get(BOOL utf, int capcount) +{ +int i; +uint8_t *nptr; + +/* Test copy strings by number */ + +for (i = 0; i < MAXCPYGET && dat_datctl.copy_numbers[i] >= 0; i++) + { + int rc; + PCRE2_SIZE length, length2; + uint32_t copybuffer[256]; + uint32_t n = (uint32_t)(dat_datctl.copy_numbers[i]); + length = sizeof(copybuffer)/code_unit_size; + PCRE2_SUBSTRING_COPY_BYNUMBER(rc, match_data, n, copybuffer, &length); + if (rc < 0) + { + fprintf(outfile, "Copy substring %d failed (%d): ", n, rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else + { + PCRE2_SUBSTRING_LENGTH_BYNUMBER(rc, match_data, n, &length2); + if (rc < 0) + { + fprintf(outfile, "Get substring %d length failed (%d): ", n, rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else if (length2 != length) + { + fprintf(outfile, "Mismatched substring lengths: %" + SIZ_FORM " %" SIZ_FORM "\n", SIZ_CAST length, SIZ_CAST length2); + } + fprintf(outfile, "%2dC ", n); + PCHARSV(copybuffer, 0, length, utf, outfile); + fprintf(outfile, " (%" SIZ_FORM ")\n", SIZ_CAST length); + } + } + +/* Test copy strings by name */ + +nptr = dat_datctl.copy_names; +for (;;) + { + int rc; + int groupnumber; + PCRE2_SIZE length, length2; + uint32_t copybuffer[256]; + int namelen = strlen((const char *)nptr); +#if defined SUPPORT_PCRE2_16 || defined SUPPORT_PCRE2_32 + PCRE2_SIZE cnl = namelen; +#endif + if (namelen == 0) break; + +#ifdef SUPPORT_PCRE2_8 + if (test_mode == PCRE8_MODE) strcpy((char *)pbuffer8, (char *)nptr); +#endif +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE)(void)to16(nptr, utf, &cnl); +#endif +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE)(void)to32(nptr, utf, &cnl); +#endif + + PCRE2_SUBSTRING_NUMBER_FROM_NAME(groupnumber, compiled_code, pbuffer); + if (groupnumber < 0 && groupnumber != PCRE2_ERROR_NOUNIQUESUBSTRING) + fprintf(outfile, "Number not found for group '%s'\n", nptr); + + length = sizeof(copybuffer)/code_unit_size; + PCRE2_SUBSTRING_COPY_BYNAME(rc, match_data, pbuffer, copybuffer, &length); + if (rc < 0) + { + fprintf(outfile, "Copy substring '%s' failed (%d): ", nptr, rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else + { + PCRE2_SUBSTRING_LENGTH_BYNAME(rc, match_data, pbuffer, &length2); + if (rc < 0) + { + fprintf(outfile, "Get substring '%s' length failed (%d): ", nptr, rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else if (length2 != length) + { + fprintf(outfile, "Mismatched substring lengths: %" + SIZ_FORM " %" SIZ_FORM "\n", SIZ_CAST length, SIZ_CAST length2); + } + fprintf(outfile, " C "); + PCHARSV(copybuffer, 0, length, utf, outfile); + fprintf(outfile, " (%" SIZ_FORM ") %s", SIZ_CAST length, nptr); + if (groupnumber >= 0) fprintf(outfile, " (group %d)\n", groupnumber); + else fprintf(outfile, " (non-unique)\n"); + } + nptr += namelen + 1; + } + +/* Test get strings by number */ + +for (i = 0; i < MAXCPYGET && dat_datctl.get_numbers[i] >= 0; i++) + { + int rc; + PCRE2_SIZE length; + void *gotbuffer; + uint32_t n = (uint32_t)(dat_datctl.get_numbers[i]); + PCRE2_SUBSTRING_GET_BYNUMBER(rc, match_data, n, &gotbuffer, &length); + if (rc < 0) + { + fprintf(outfile, "Get substring %d failed (%d): ", n, rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else + { + fprintf(outfile, "%2dG ", n); + PCHARSV(gotbuffer, 0, length, utf, outfile); + fprintf(outfile, " (%" SIZ_FORM ")\n", SIZ_CAST length); + PCRE2_SUBSTRING_FREE(gotbuffer); + } + } + +/* Test get strings by name */ + +nptr = dat_datctl.get_names; +for (;;) + { + PCRE2_SIZE length; + void *gotbuffer; + int rc; + int groupnumber; + int namelen = strlen((const char *)nptr); +#if defined SUPPORT_PCRE2_16 || defined SUPPORT_PCRE2_32 + PCRE2_SIZE cnl = namelen; +#endif + if (namelen == 0) break; + +#ifdef SUPPORT_PCRE2_8 + if (test_mode == PCRE8_MODE) strcpy((char *)pbuffer8, (char *)nptr); +#endif +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE)(void)to16(nptr, utf, &cnl); +#endif +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE)(void)to32(nptr, utf, &cnl); +#endif + + PCRE2_SUBSTRING_NUMBER_FROM_NAME(groupnumber, compiled_code, pbuffer); + if (groupnumber < 0 && groupnumber != PCRE2_ERROR_NOUNIQUESUBSTRING) + fprintf(outfile, "Number not found for group '%s'\n", nptr); + + PCRE2_SUBSTRING_GET_BYNAME(rc, match_data, pbuffer, &gotbuffer, &length); + if (rc < 0) + { + fprintf(outfile, "Get substring '%s' failed (%d): ", nptr, rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else + { + fprintf(outfile, " G "); + PCHARSV(gotbuffer, 0, length, utf, outfile); + fprintf(outfile, " (%" SIZ_FORM ") %s", SIZ_CAST length, nptr); + if (groupnumber >= 0) fprintf(outfile, " (group %d)\n", groupnumber); + else fprintf(outfile, " (non-unique)\n"); + PCRE2_SUBSTRING_FREE(gotbuffer); + } + nptr += namelen + 1; + } + +/* Test getting the complete list of captured strings. */ + +if ((dat_datctl.control & CTL_GETALL) != 0) + { + int rc; + void **stringlist; + PCRE2_SIZE *lengths; + PCRE2_SUBSTRING_LIST_GET(rc, match_data, &stringlist, &lengths); + if (rc < 0) + { + fprintf(outfile, "get substring list failed (%d): ", rc); + if (!print_error_message(rc, "", "\n")) return FALSE; + } + else + { + for (i = 0; i < capcount; i++) + { + fprintf(outfile, "%2dL ", i); + PCHARSV(stringlist[i], 0, lengths[i], utf, outfile); + putc('\n', outfile); + } + if (stringlist[i] != NULL) + fprintf(outfile, "string list not terminated by NULL\n"); + PCRE2_SUBSTRING_LIST_FREE(stringlist); + } + } + +return TRUE; +} + + + +/************************************************* +* Process a data line * +*************************************************/ + +/* The line is in buffer; it will not be empty. + +Arguments: none + +Returns: PR_OK continue processing next line + PR_SKIP skip to a blank line + PR_ABEND abort the pcre2test run +*/ + +static int +process_data(void) +{ +PCRE2_SIZE len, ulen, arg_ulen; +uint32_t gmatched; +uint32_t c, k; +uint32_t g_notempty = 0; +uint8_t *p, *pp, *start_rep; +size_t needlen; +void *use_dat_context; +BOOL utf; +BOOL subject_literal; +PCRE2_SIZE ovecsave[3]; + +#ifdef SUPPORT_PCRE2_8 +uint8_t *q8 = NULL; +#endif +#ifdef SUPPORT_PCRE2_16 +uint16_t *q16 = NULL; +#endif +#ifdef SUPPORT_PCRE2_32 +uint32_t *q32 = NULL; +#endif + +subject_literal = (pat_patctl.control2 & CTL2_SUBJECT_LITERAL) != 0; + +/* Copy the default context and data control blocks to the active ones. Then +copy from the pattern the controls that can be set in either the pattern or the +data. This allows them to be overridden in the data line. We do not do this for +options because those that are common apply separately to compiling and +matching. */ + +DATCTXCPY(dat_context, default_dat_context); +memcpy(&dat_datctl, &def_datctl, sizeof(datctl)); +dat_datctl.control |= (pat_patctl.control & CTL_ALLPD); +dat_datctl.control2 |= (pat_patctl.control2 & CTL2_ALLPD); +strcpy((char *)dat_datctl.replacement, (char *)pat_patctl.replacement); +if (dat_datctl.jitstack == 0) dat_datctl.jitstack = pat_patctl.jitstack; + +/* Initialize for scanning the data line. */ + +#ifdef SUPPORT_PCRE2_8 +utf = ((((pat_patctl.control & CTL_POSIX) != 0)? + ((pcre2_real_code_8 *)preg.re_pcre2_code)->overall_options : + FLD(compiled_code, overall_options)) & PCRE2_UTF) != 0; +#else +utf = (FLD(compiled_code, overall_options) & PCRE2_UTF) != 0; +#endif + +start_rep = NULL; +len = strlen((const char *)buffer); +while (len > 0 && isspace(buffer[len-1])) len--; +buffer[len] = 0; +p = buffer; +while (isspace(*p)) p++; + +/* Check that the data is well-formed UTF-8 if we're in UTF mode. To create +invalid input to pcre2_match(), you must use \x?? or \x{} sequences. */ + +if (utf) + { + uint8_t *q; + uint32_t cc; + int n = 1; + for (q = p; n > 0 && *q; q += n) n = utf82ord(q, &cc); + if (n <= 0) + { + fprintf(outfile, "** Failed: invalid UTF-8 string cannot be used as input " + "in UTF mode\n"); + return PR_OK; + } + } + +#ifdef SUPPORT_VALGRIND +/* Mark the dbuffer as addressable but undefined again. */ +if (dbuffer != NULL) + { + VALGRIND_MAKE_MEM_UNDEFINED(dbuffer, dbuffer_size); + } +#endif + +/* Allocate a buffer to hold the data line; len+1 is an upper bound on +the number of code units that will be needed (though the buffer may have to be +extended if replication is involved). */ + +needlen = (size_t)((len+1) * code_unit_size); +if (dbuffer == NULL || needlen >= dbuffer_size) + { + while (needlen >= dbuffer_size) dbuffer_size *= 2; + dbuffer = (uint8_t *)realloc(dbuffer, dbuffer_size); + if (dbuffer == NULL) + { + fprintf(stderr, "pcre2test: realloc(%d) failed\n", (int)dbuffer_size); + exit(1); + } + } +SETCASTPTR(q, dbuffer); /* Sets q8, q16, or q32, as appropriate. */ + +/* Scan the data line, interpreting data escapes, and put the result into a +buffer of the appropriate width. In UTF mode, input is always UTF-8; otherwise, +in 16- and 32-bit modes, it can be forced to UTF-8 by the utf8_input modifier. +*/ + +while ((c = *p++) != 0) + { + int32_t i = 0; + size_t replen; + + /* ] may mark the end of a replicated sequence */ + + if (c == ']' && start_rep != NULL) + { + long li; + char *endptr; + size_t qoffset = CAST8VAR(q) - dbuffer; + size_t rep_offset = start_rep - dbuffer; + + if (*p++ != '{') + { + fprintf(outfile, "** Expected '{' after \\[....]\n"); + return PR_OK; + } + + li = strtol((const char *)p, &endptr, 10); + if (S32OVERFLOW(li)) + { + fprintf(outfile, "** Repeat count too large\n"); + return PR_OK; + } + + p = (uint8_t *)endptr; + if (*p++ != '}') + { + fprintf(outfile, "** Expected '}' after \\[...]{...\n"); + return PR_OK; + } + + i = (int32_t)li; + if (i-- == 0) + { + fprintf(outfile, "** Zero repeat not allowed\n"); + return PR_OK; + } + + replen = CAST8VAR(q) - start_rep; + needlen += replen * i; + + if (needlen >= dbuffer_size) + { + while (needlen >= dbuffer_size) dbuffer_size *= 2; + dbuffer = (uint8_t *)realloc(dbuffer, dbuffer_size); + if (dbuffer == NULL) + { + fprintf(stderr, "pcre2test: realloc(%d) failed\n", (int)dbuffer_size); + exit(1); + } + SETCASTPTR(q, dbuffer + qoffset); + start_rep = dbuffer + rep_offset; + } + + while (i-- > 0) + { + memcpy(CAST8VAR(q), start_rep, replen); + SETPLUS(q, replen/code_unit_size); + } + + start_rep = NULL; + continue; + } + + /* Handle a non-escaped character. In non-UTF 32-bit mode with utf8_input + set, do the fudge for setting the top bit. */ + + if (c != '\\' || subject_literal) + { + uint32_t topbit = 0; + if (test_mode == PCRE32_MODE && c == 0xff && *p != 0) + { + topbit = 0x80000000; + c = *p++; + } + if ((utf || (pat_patctl.control & CTL_UTF8_INPUT) != 0) && + HASUTF8EXTRALEN(c)) { GETUTF8INC(c, p); } + c |= topbit; + } + + /* Handle backslash escapes */ + + else switch ((c = *p++)) + { + case '\\': break; + case 'a': c = CHAR_BEL; break; + case 'b': c = '\b'; break; + case 'e': c = CHAR_ESC; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c -= '0'; + while (i++ < 2 && isdigit(*p) && *p != '8' && *p != '9') + c = c * 8 + *p++ - '0'; + break; + + case 'o': + if (*p == '{') + { + uint8_t *pt = p; + c = 0; + for (pt++; isdigit(*pt) && *pt != '8' && *pt != '9'; pt++) + { + if (++i == 12) + fprintf(outfile, "** Too many octal digits in \\o{...} item; " + "using only the first twelve.\n"); + else c = c * 8 + *pt - '0'; + } + if (*pt == '}') p = pt + 1; + else fprintf(outfile, "** Missing } after \\o{ (assumed)\n"); + } + break; + + case 'x': + if (*p == '{') + { + uint8_t *pt = p; + c = 0; + + /* We used to have "while (isxdigit(*(++pt)))" here, but it fails + when isxdigit() is a macro that refers to its argument more than + once. This is banned by the C Standard, but apparently happens in at + least one MacOS environment. */ + + for (pt++; isxdigit(*pt); pt++) + { + if (++i == 9) + fprintf(outfile, "** Too many hex digits in \\x{...} item; " + "using only the first eight.\n"); + else c = c * 16 + tolower(*pt) - ((isdigit(*pt))? '0' : 'a' - 10); + } + if (*pt == '}') + { + p = pt + 1; + break; + } + /* Not correct form for \x{...}; fall through */ + } + + /* \x without {} always defines just one byte in 8-bit mode. This + allows UTF-8 characters to be constructed byte by byte, and also allows + invalid UTF-8 sequences to be made. Just copy the byte in UTF-8 mode. + Otherwise, pass it down as data. */ + + c = 0; + while (i++ < 2 && isxdigit(*p)) + { + c = c * 16 + tolower(*p) - ((isdigit(*p))? '0' : 'a' - 10); + p++; + } +#if defined SUPPORT_PCRE2_8 + if (utf && (test_mode == PCRE8_MODE)) + { + *q8++ = c; + continue; + } +#endif + break; + + case 0: /* \ followed by EOF allows for an empty line */ + p--; + continue; + + case '=': /* \= terminates the data, starts modifiers */ + goto ENDSTRING; + + case '[': /* \[ introduces a replicated character sequence */ + if (start_rep != NULL) + { + fprintf(outfile, "** Nested replication is not supported\n"); + return PR_OK; + } + start_rep = CAST8VAR(q); + continue; + + default: + if (isalnum(c)) + { + fprintf(outfile, "** Unrecognized escape sequence \"\\%c\"\n", c); + return PR_OK; + } + } + + /* We now have a character value in c that may be greater than 255. + In 8-bit mode we convert to UTF-8 if we are in UTF mode. Values greater + than 127 in UTF mode must have come from \x{...} or octal constructs + because values from \x.. get this far only in non-UTF mode. */ + +#ifdef SUPPORT_PCRE2_8 + if (test_mode == PCRE8_MODE) + { + if (utf) + { + if (c > 0x7fffffff) + { + fprintf(outfile, "** Character \\x{%x} is greater than 0x7fffffff " + "and so cannot be converted to UTF-8\n", c); + return PR_OK; + } + q8 += ord2utf8(c, q8); + } + else + { + if (c > 0xffu) + { + fprintf(outfile, "** Character \\x{%x} is greater than 255 " + "and UTF-8 mode is not enabled.\n", c); + fprintf(outfile, "** Truncation will probably give the wrong " + "result.\n"); + } + *q8++ = c; + } + } +#endif +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE) + { + if (utf) + { + if (c > 0x10ffffu) + { + fprintf(outfile, "** Failed: character \\x{%x} is greater than " + "0x10ffff and so cannot be converted to UTF-16\n", c); + return PR_OK; + } + else if (c >= 0x10000u) + { + c-= 0x10000u; + *q16++ = 0xD800 | (c >> 10); + *q16++ = 0xDC00 | (c & 0x3ff); + } + else + *q16++ = c; + } + else + { + if (c > 0xffffu) + { + fprintf(outfile, "** Character \\x{%x} is greater than 0xffff " + "and UTF-16 mode is not enabled.\n", c); + fprintf(outfile, "** Truncation will probably give the wrong " + "result.\n"); + } + + *q16++ = c; + } + } +#endif +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE) + { + *q32++ = c; + } +#endif + } + +ENDSTRING: +SET(*q, 0); +len = CASTVAR(uint8_t *, q) - dbuffer; /* Length in bytes */ +ulen = len/code_unit_size; /* Length in code units */ +arg_ulen = ulen; /* Value to use in match arg */ + +/* If the string was terminated by \= we must now interpret modifiers. */ + +if (p[-1] != 0 && !decode_modifiers(p, CTX_DAT, NULL, &dat_datctl)) + return PR_OK; + +/* Check for mutually exclusive modifiers. At present, these are all in the +first control word. */ + +for (k = 0; k < sizeof(exclusive_dat_controls)/sizeof(uint32_t); k++) + { + c = dat_datctl.control & exclusive_dat_controls[k]; + if (c != 0 && c != (c & (~c+1))) + { + show_controls(c, 0, "** Not allowed together:"); + fprintf(outfile, "\n"); + return PR_OK; + } + } + +if (pat_patctl.replacement[0] != 0 && + (dat_datctl.control & CTL_NULLCONTEXT) != 0) + { + fprintf(outfile, "** Replacement text is not supported with null_context.\n"); + return PR_OK; + } + +/* We now have the subject in dbuffer, with len containing the byte length, and +ulen containing the code unit length, with a copy in arg_ulen for use in match +function arguments (this gets changed to PCRE2_ZERO_TERMINATED when the +zero_terminate modifier is present). + +Move the data to the end of the buffer so that a read over the end can be +caught by valgrind or other means. If we have explicit valgrind support, mark +the unused start of the buffer unaddressable. If we are using the POSIX +interface, or testing zero-termination, we must include the terminating zero in +the usable data. */ + +c = code_unit_size * (((pat_patctl.control & CTL_POSIX) + + (dat_datctl.control & CTL_ZERO_TERMINATE) != 0)? 1:0); +pp = memmove(dbuffer + dbuffer_size - len - c, dbuffer, len + c); +#ifdef SUPPORT_VALGRIND + VALGRIND_MAKE_MEM_NOACCESS(dbuffer, dbuffer_size - (len + c)); +#endif + +/* Now pp points to the subject string. POSIX matching is only possible in +8-bit mode, and it does not support timing or other fancy features. Some were +checked at compile time, but we need to check the match-time settings here. */ + +#ifdef SUPPORT_PCRE2_8 +if ((pat_patctl.control & CTL_POSIX) != 0) + { + int rc; + int eflags = 0; + regmatch_t *pmatch = NULL; + const char *msg = "** Ignored with POSIX interface:"; + + if (dat_datctl.cerror[0] != CFORE_UNSET || dat_datctl.cerror[1] != CFORE_UNSET) + prmsg(&msg, "callout_error"); + if (dat_datctl.cfail[0] != CFORE_UNSET || dat_datctl.cfail[1] != CFORE_UNSET) + prmsg(&msg, "callout_fail"); + if (dat_datctl.copy_numbers[0] >= 0 || dat_datctl.copy_names[0] != 0) + prmsg(&msg, "copy"); + if (dat_datctl.get_numbers[0] >= 0 || dat_datctl.get_names[0] != 0) + prmsg(&msg, "get"); + if (dat_datctl.jitstack != 0) prmsg(&msg, "jitstack"); + if (dat_datctl.offset != 0) prmsg(&msg, "offset"); + + if ((dat_datctl.options & ~POSIX_SUPPORTED_MATCH_OPTIONS) != 0) + { + fprintf(outfile, "%s", msg); + show_match_options(dat_datctl.options & ~POSIX_SUPPORTED_MATCH_OPTIONS); + msg = ""; + } + if ((dat_datctl.control & ~POSIX_SUPPORTED_MATCH_CONTROLS) != 0 || + (dat_datctl.control2 & ~POSIX_SUPPORTED_MATCH_CONTROLS2) != 0) + { + show_controls(dat_datctl.control & ~POSIX_SUPPORTED_MATCH_CONTROLS, + dat_datctl.control2 & ~POSIX_SUPPORTED_MATCH_CONTROLS2, msg); + msg = ""; + } + + if (msg[0] == 0) fprintf(outfile, "\n"); + + if (dat_datctl.oveccount > 0) + { + pmatch = (regmatch_t *)malloc(sizeof(regmatch_t) * dat_datctl.oveccount); + if (pmatch == NULL) + { + fprintf(outfile, "** Failed to get memory for recording matching " + "information (size set = %du)\n", dat_datctl.oveccount); + return PR_OK; + } + } + + if (dat_datctl.startend[0] != CFORE_UNSET) + { + pmatch[0].rm_so = dat_datctl.startend[0]; + pmatch[0].rm_eo = (dat_datctl.startend[1] != 0)? + dat_datctl.startend[1] : len; + eflags |= REG_STARTEND; + } + + if ((dat_datctl.options & PCRE2_NOTBOL) != 0) eflags |= REG_NOTBOL; + if ((dat_datctl.options & PCRE2_NOTEOL) != 0) eflags |= REG_NOTEOL; + if ((dat_datctl.options & PCRE2_NOTEMPTY) != 0) eflags |= REG_NOTEMPTY; + + rc = regexec(&preg, (const char *)pp, dat_datctl.oveccount, pmatch, eflags); + if (rc != 0) + { + (void)regerror(rc, &preg, (char *)pbuffer8, pbuffer8_size); + fprintf(outfile, "No match: POSIX code %d: %s\n", rc, pbuffer8); + } + else if ((pat_patctl.control & CTL_POSIX_NOSUB) != 0) + fprintf(outfile, "Matched with REG_NOSUB\n"); + else if (dat_datctl.oveccount == 0) + fprintf(outfile, "Matched without capture\n"); + else + { + size_t i, j; + size_t last_printed = (size_t)dat_datctl.oveccount; + for (i = 0; i < (size_t)dat_datctl.oveccount; i++) + { + if (pmatch[i].rm_so >= 0) + { + PCRE2_SIZE start = pmatch[i].rm_so; + PCRE2_SIZE end = pmatch[i].rm_eo; + for (j = last_printed + 1; j < i; j++) + fprintf(outfile, "%2d: \n", (int)j); + last_printed = i; + if (start > end) + { + start = pmatch[i].rm_eo; + end = pmatch[i].rm_so; + fprintf(outfile, "Start of matched string is beyond its end - " + "displaying from end to start.\n"); + } + fprintf(outfile, "%2d: ", (int)i); + PCHARSV(pp, start, end - start, utf, outfile); + fprintf(outfile, "\n"); + + if ((i == 0 && (dat_datctl.control & CTL_AFTERTEXT) != 0) || + (dat_datctl.control & CTL_ALLAFTERTEXT) != 0) + { + fprintf(outfile, "%2d+ ", (int)i); + /* Note: don't use the start/end variables here because we want to + show the text from what is reported as the end. */ + PCHARSV(pp, pmatch[i].rm_eo, len - pmatch[i].rm_eo, utf, outfile); + fprintf(outfile, "\n"); } + } + } + } + free(pmatch); + return PR_OK; + } +#endif /* SUPPORT_PCRE2_8 */ + + /* Handle matching via the native interface. Check for consistency of +modifiers. */ + +if (dat_datctl.startend[0] != CFORE_UNSET) + fprintf(outfile, "** \\=posix_startend ignored for non-POSIX matching\n"); + +/* ALLUSEDTEXT is not supported with JIT, but JIT is not used with DFA +matching, even if the JIT compiler was used. */ + +if ((dat_datctl.control & (CTL_ALLUSEDTEXT|CTL_DFA)) == CTL_ALLUSEDTEXT && + FLD(compiled_code, executable_jit) != NULL) + { + fprintf(outfile, "** Showing all consulted text is not supported by JIT: ignored\n"); + dat_datctl.control &= ~CTL_ALLUSEDTEXT; + } + +/* Handle passing the subject as zero-terminated. */ + +if ((dat_datctl.control & CTL_ZERO_TERMINATE) != 0) + arg_ulen = PCRE2_ZERO_TERMINATED; + +/* The nullcontext modifier is used to test calling pcre2_[jit_]match() with a +NULL context. */ + +use_dat_context = ((dat_datctl.control & CTL_NULLCONTEXT) != 0)? + NULL : PTR(dat_context); + +/* Enable display of malloc/free if wanted. We can do this only if either the +pattern or the subject is processed with a context. */ + +show_memory = (dat_datctl.control & CTL_MEMORY) != 0; + +if (show_memory && + (pat_patctl.control & dat_datctl.control & CTL_NULLCONTEXT) != 0) + fprintf(outfile, "** \\=memory requires either a pattern or a subject " + "context: ignored\n"); + +/* Create and assign a JIT stack if requested. */ + +if (dat_datctl.jitstack != 0) + { + if (dat_datctl.jitstack != jit_stack_size) + { + PCRE2_JIT_STACK_FREE(jit_stack); + PCRE2_JIT_STACK_CREATE(jit_stack, 1, dat_datctl.jitstack * 1024, NULL); + jit_stack_size = dat_datctl.jitstack; + } + PCRE2_JIT_STACK_ASSIGN(dat_context, jit_callback, jit_stack); + } + +/* Or de-assign */ + +else if (jit_stack != NULL) + { + PCRE2_JIT_STACK_ASSIGN(dat_context, NULL, NULL); + PCRE2_JIT_STACK_FREE(jit_stack); + jit_stack = NULL; + jit_stack_size = 0; + } + +/* When no JIT stack is assigned, we must ensure that there is a JIT callback +if we want to verify that JIT was actually used. */ + +if ((pat_patctl.control & CTL_JITVERIFY) != 0 && jit_stack == NULL) + { + PCRE2_JIT_STACK_ASSIGN(dat_context, jit_callback, NULL); + } + +/* Adjust match_data according to size of offsets required. A size of zero +causes a new match data block to be obtained that exactly fits the pattern. */ + +if (dat_datctl.oveccount == 0) + { + PCRE2_MATCH_DATA_FREE(match_data); + PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(match_data, compiled_code, NULL); + PCRE2_GET_OVECTOR_COUNT(max_oveccount, match_data); + } +else if (dat_datctl.oveccount <= max_oveccount) + { + SETFLD(match_data, oveccount, dat_datctl.oveccount); + } +else + { + max_oveccount = dat_datctl.oveccount; + PCRE2_MATCH_DATA_FREE(match_data); + PCRE2_MATCH_DATA_CREATE(match_data, max_oveccount, NULL); + } + +if (CASTVAR(void *, match_data) == NULL) + { + fprintf(outfile, "** Failed to get memory for recording matching " + "information (size requested: %d)\n", dat_datctl.oveccount); + max_oveccount = 0; + return PR_OK; + } + +/* Replacement processing is ignored for DFA matching. */ + +if (dat_datctl.replacement[0] != 0 && (dat_datctl.control & CTL_DFA) != 0) + { + fprintf(outfile, "** Ignored for DFA matching: replace\n"); + dat_datctl.replacement[0] = 0; + } + +/* If a replacement string is provided, call pcre2_substitute() instead of one +of the matching functions. First we have to convert the replacement string to +the appropriate width. */ + +if (dat_datctl.replacement[0] != 0) + { + int rc; + uint8_t *pr; + uint8_t rbuffer[REPLACE_BUFFSIZE]; + uint8_t nbuffer[REPLACE_BUFFSIZE]; + uint32_t xoptions; + PCRE2_SIZE rlen, nsize, erroroffset; + BOOL badutf = FALSE; + +#ifdef SUPPORT_PCRE2_8 + uint8_t *r8 = NULL; +#endif +#ifdef SUPPORT_PCRE2_16 + uint16_t *r16 = NULL; +#endif +#ifdef SUPPORT_PCRE2_32 + uint32_t *r32 = NULL; +#endif + + if (timeitm) + fprintf(outfile, "** Timing is not supported with replace: ignored\n"); + + if ((dat_datctl.control & CTL_ALTGLOBAL) != 0) + fprintf(outfile, "** Altglobal is not supported with replace: ignored\n"); + + xoptions = (((dat_datctl.control & CTL_GLOBAL) == 0)? 0 : + PCRE2_SUBSTITUTE_GLOBAL) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_EXTENDED) == 0)? 0 : + PCRE2_SUBSTITUTE_EXTENDED) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_OVERFLOW_LENGTH) == 0)? 0 : + PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_UNKNOWN_UNSET) == 0)? 0 : + PCRE2_SUBSTITUTE_UNKNOWN_UNSET) | + (((dat_datctl.control2 & CTL2_SUBSTITUTE_UNSET_EMPTY) == 0)? 0 : + PCRE2_SUBSTITUTE_UNSET_EMPTY); + + SETCASTPTR(r, rbuffer); /* Sets r8, r16, or r32, as appropriate. */ + pr = dat_datctl.replacement; + + /* If the replacement starts with '[]' we interpret that as length + value for the replacement buffer. */ + + nsize = REPLACE_BUFFSIZE/code_unit_size; + if (*pr == '[') + { + PCRE2_SIZE n = 0; + while ((c = *(++pr)) >= CHAR_0 && c <= CHAR_9) n = n * 10 + c - CHAR_0; + if (*pr++ != ']') + { + fprintf(outfile, "Bad buffer size in replacement string\n"); + return PR_OK; + } + if (n > nsize) + { + fprintf(outfile, "Replacement buffer setting (%" SIZ_FORM ") is too " + "large (max %" SIZ_FORM ")\n", SIZ_CAST n, SIZ_CAST nsize); + return PR_OK; + } + nsize = n; + } + + /* Now copy the replacement string to a buffer of the appropriate width. No + escape processing is done for replacements. In UTF mode, check for an invalid + UTF-8 input string, and if it is invalid, just copy its code units without + UTF interpretation. This provides a means of checking that an invalid string + is detected. Otherwise, UTF-8 can be used to include wide characters in a + replacement. */ + + if (utf) badutf = valid_utf(pr, strlen((const char *)pr), &erroroffset); + + /* Not UTF or invalid UTF-8: just copy the code units. */ + + if (!utf || badutf) + { + while ((c = *pr++) != 0) + { +#ifdef SUPPORT_PCRE2_8 + if (test_mode == PCRE8_MODE) *r8++ = c; +#endif +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE) *r16++ = c; +#endif +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE) *r32++ = c; +#endif + } + } + + /* Valid UTF-8 replacement string */ + + else while ((c = *pr++) != 0) + { + if (HASUTF8EXTRALEN(c)) { GETUTF8INC(c, pr); } + +#ifdef SUPPORT_PCRE2_8 + if (test_mode == PCRE8_MODE) r8 += ord2utf8(c, r8); +#endif + +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE) + { + if (c >= 0x10000u) + { + c-= 0x10000u; + *r16++ = 0xD800 | (c >> 10); + *r16++ = 0xDC00 | (c & 0x3ff); + } + else *r16++ = c; + } +#endif + +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE) *r32++ = c; +#endif + } + + SET(*r, 0); + if ((dat_datctl.control & CTL_ZERO_TERMINATE) != 0) + rlen = PCRE2_ZERO_TERMINATED; + else + rlen = (CASTVAR(uint8_t *, r) - rbuffer)/code_unit_size; + PCRE2_SUBSTITUTE(rc, compiled_code, pp, arg_ulen, dat_datctl.offset, + dat_datctl.options|xoptions, match_data, dat_context, + rbuffer, rlen, nbuffer, &nsize); + + if (rc < 0) + { + fprintf(outfile, "Failed: error %d", rc); + if (rc != PCRE2_ERROR_NOMEMORY && nsize != PCRE2_UNSET) + fprintf(outfile, " at offset %ld in replacement", (long int)nsize); + fprintf(outfile, ": "); + if (!print_error_message(rc, "", "")) return PR_ABEND; + if (rc == PCRE2_ERROR_NOMEMORY && + (xoptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) != 0) + fprintf(outfile, ": %ld code units are needed", (long int)nsize); + } + else + { + fprintf(outfile, "%2d: ", rc); + PCHARSV(nbuffer, 0, nsize, utf, outfile); + } + + fprintf(outfile, "\n"); + show_memory = FALSE; + return PR_OK; + } /* End of substitution handling */ + +/* When a replacement string is not provided, run a loop for global matching +with one of the basic matching functions. For altglobal (or first time round +the loop), set an "unset" value for the previous match info. */ + +ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET; + +for (gmatched = 0;; gmatched++) + { + PCRE2_SIZE j; + int capcount; + PCRE2_SIZE *ovector; + + ovector = FLD(match_data, ovector); + + /* Fill the ovector with junk to detect elements that do not get set + when they should be. */ + + for (j = 0; j < 2*dat_datctl.oveccount; j++) ovector[j] = JUNK_OFFSET; + + /* When matching is via pcre2_match(), we will detect the use of JIT via the + stack callback function. */ + + jit_was_used = (pat_patctl.control & CTL_JITFAST) != 0; + + /* Do timing if required. */ + + if (timeitm > 0) + { + int i; + clock_t start_time, time_taken; + + if ((dat_datctl.control & CTL_DFA) != 0) + { + if ((dat_datctl.options & PCRE2_DFA_RESTART) != 0) + { + fprintf(outfile, "Timing DFA restarts is not supported\n"); + return PR_OK; + } + if (dfa_workspace == NULL) + dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int)); + start_time = clock(); + for (i = 0; i < timeitm; i++) + { + PCRE2_DFA_MATCH(capcount, compiled_code, pp, arg_ulen, + dat_datctl.offset, dat_datctl.options | g_notempty, match_data, + use_dat_context, dfa_workspace, DFA_WS_DIMENSION); + } + } + + else if ((pat_patctl.control & CTL_JITFAST) != 0) + { + start_time = clock(); + for (i = 0; i < timeitm; i++) + { + PCRE2_JIT_MATCH(capcount, compiled_code, pp, arg_ulen, + dat_datctl.offset, dat_datctl.options | g_notempty, match_data, + use_dat_context); + } + } + + else + { + start_time = clock(); + for (i = 0; i < timeitm; i++) + { + PCRE2_MATCH(capcount, compiled_code, pp, arg_ulen, + dat_datctl.offset, dat_datctl.options | g_notempty, match_data, + use_dat_context); + } + } + total_match_time += (time_taken = clock() - start_time); + fprintf(outfile, "Match time %.4f milliseconds\n", + (((double)time_taken * 1000.0) / (double)timeitm) / + (double)CLOCKS_PER_SEC); + } + + /* Find the heap, match and depth limits if requested. The depth and heap + limits are not relevant for JIT. The return from check_match_limit() is the + return from the final call to pcre2_match() or pcre2_dfa_match(). */ + + if ((dat_datctl.control & CTL_FINDLIMITS) != 0) + { + capcount = 0; /* This stops compiler warnings */ + + if (FLD(compiled_code, executable_jit) == NULL || + (dat_datctl.options & PCRE2_NO_JIT) != 0) + { + (void)check_match_limit(pp, arg_ulen, PCRE2_ERROR_HEAPLIMIT, "heap"); + } + + capcount = check_match_limit(pp, arg_ulen, PCRE2_ERROR_MATCHLIMIT, + "match"); + + if (FLD(compiled_code, executable_jit) == NULL || + (dat_datctl.options & PCRE2_NO_JIT) != 0 || + (dat_datctl.control & CTL_DFA) != 0) + { + capcount = check_match_limit(pp, arg_ulen, PCRE2_ERROR_DEPTHLIMIT, + "depth"); + } + + if (capcount == 0) + { + fprintf(outfile, "Matched, but offsets vector is too small to show all matches\n"); + capcount = dat_datctl.oveccount; + } + } + + /* Otherwise just run a single match, setting up a callout if required (the + default). There is a copy of the pattern in pbuffer8 for use by callouts. */ + + else + { + if ((dat_datctl.control & CTL_CALLOUT_NONE) == 0) + { + PCRE2_SET_CALLOUT(dat_context, callout_function, + (void *)(&dat_datctl.callout_data)); + first_callout = TRUE; + last_callout_mark = NULL; + callout_count = 0; + } + else + { + PCRE2_SET_CALLOUT(dat_context, NULL, NULL); /* No callout */ + } + + /* Run a single DFA or NFA match. */ + + if ((dat_datctl.control & CTL_DFA) != 0) + { + if (dfa_workspace == NULL) + dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int)); + if (dfa_matched++ == 0) + dfa_workspace[0] = -1; /* To catch bad restart */ + PCRE2_DFA_MATCH(capcount, compiled_code, pp, arg_ulen, + dat_datctl.offset, dat_datctl.options | g_notempty, match_data, + use_dat_context, dfa_workspace, DFA_WS_DIMENSION); + if (capcount == 0) + { + fprintf(outfile, "Matched, but offsets vector is too small to show all matches\n"); + capcount = dat_datctl.oveccount; + } + } + else + { + if ((pat_patctl.control & CTL_JITFAST) != 0) + PCRE2_JIT_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, + dat_datctl.options | g_notempty, match_data, use_dat_context); + else + PCRE2_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, + dat_datctl.options | g_notempty, match_data, use_dat_context); + if (capcount == 0) + { + fprintf(outfile, "Matched, but too many substrings\n"); + capcount = dat_datctl.oveccount; + } + } + } + + /* The result of the match is now in capcount. First handle a successful + match. */ + + if (capcount >= 0) + { + int i; + uint32_t oveccount; + + /* This is a check against a lunatic return value. */ + + PCRE2_GET_OVECTOR_COUNT(oveccount, match_data); + if (capcount > (int)oveccount) + { + fprintf(outfile, + "** PCRE2 error: returned count %d is too big for ovector count %d\n", + capcount, oveccount); + capcount = oveccount; + if ((dat_datctl.control & CTL_ANYGLOB) != 0) + { + fprintf(outfile, "** Global loop abandoned\n"); + dat_datctl.control &= ~CTL_ANYGLOB; /* Break g/G loop */ + } + } + + /* If this is not the first time round a global loop, check that the + returned string has changed. If it has not, check for an empty string match + at different starting offset from the previous match. This is a failed test + retry for null-matching patterns that don't match at their starting offset, + for example /(?<=\G.)/. A repeated match at the same point is not such a + pattern, and must be discarded, and we then proceed to seek a non-null + match at the current point. For any other repeated match, there is a bug + somewhere and we must break the loop because it will go on for ever. We + know that there are always at least two elements in the ovector. */ + + if (gmatched > 0 && ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1]) + { + if (ovector[0] == ovector[1] && ovecsave[2] != dat_datctl.offset) + { + g_notempty = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + ovecsave[2] = dat_datctl.offset; + continue; /* Back to the top of the loop */ + } + fprintf(outfile, + "** PCRE2 error: global repeat returned the same string as previous\n"); + fprintf(outfile, "** Global loop abandoned\n"); + dat_datctl.control &= ~CTL_ANYGLOB; /* Break g/G loop */ + } + + /* "allcaptures" requests showing of all captures in the pattern, to check + unset ones at the end. It may be set on the pattern or the data. Implement + by setting capcount to the maximum. This is not relevant for DFA matching, + so ignore it. */ + + if ((dat_datctl.control & CTL_ALLCAPTURES) != 0) + { + uint32_t maxcapcount; + if ((dat_datctl.control & CTL_DFA) != 0) + { + fprintf(outfile, "** Ignored after DFA matching: allcaptures\n"); + } + else + { + if (pattern_info(PCRE2_INFO_CAPTURECOUNT, &maxcapcount, FALSE) < 0) + return PR_SKIP; + capcount = maxcapcount + 1; /* Allow for full match */ + if (capcount > (int)oveccount) capcount = oveccount; + } + } + + /* Output the captured substrings. Note that, for the matched string, + the use of \K in an assertion can make the start later than the end. */ + + for (i = 0; i < 2*capcount; i += 2) + { + PCRE2_SIZE lleft, lmiddle, lright; + PCRE2_SIZE start = ovector[i]; + PCRE2_SIZE end = ovector[i+1]; + + if (start > end) + { + start = ovector[i+1]; + end = ovector[i]; + fprintf(outfile, "Start of matched string is beyond its end - " + "displaying from end to start.\n"); + } + + fprintf(outfile, "%2d: ", i/2); + + /* Check for an unset group */ + + if (start == PCRE2_UNSET) + { + fprintf(outfile, "\n"); + continue; + } + + /* Check for silly offsets, in particular, values that have not been + set when they should have been. */ + + if (start > ulen || end > ulen) + { + fprintf(outfile, "ERROR: bad value(s) for offset(s): 0x%lx 0x%lx\n", + (unsigned long int)start, (unsigned long int)end); + continue; + } + + /* When JIT is not being used, ALLUSEDTEXT may be set. (It if is set with + JIT, it is disabled above, with a comment.) When the match is done by the + interpreter, leftchar and rightchar are available, and if ALLUSEDTEXT is + set, and if the leftmost consulted character is before the start of the + match or the rightmost consulted character is past the end of the match, + we want to show all consulted characters for the main matched string, and + indicate which were lookarounds. */ + + if (i == 0) + { + BOOL showallused; + PCRE2_SIZE leftchar, rightchar; + + if ((dat_datctl.control & CTL_ALLUSEDTEXT) != 0) + { + leftchar = FLD(match_data, leftchar); + rightchar = FLD(match_data, rightchar); + showallused = i == 0 && (leftchar < start || rightchar > end); + } + else showallused = FALSE; + + if (showallused) + { + PCHARS(lleft, pp, leftchar, start - leftchar, utf, outfile); + PCHARS(lmiddle, pp, start, end - start, utf, outfile); + PCHARS(lright, pp, end, rightchar - end, utf, outfile); + if ((pat_patctl.control & CTL_JITVERIFY) != 0 && jit_was_used) + fprintf(outfile, " (JIT)"); + fprintf(outfile, "\n "); + for (j = 0; j < lleft; j++) fprintf(outfile, "<"); + for (j = 0; j < lmiddle; j++) fprintf(outfile, " "); + for (j = 0; j < lright; j++) fprintf(outfile, ">"); + } + + /* When a pattern contains \K, the start of match position may be + different to the start of the matched string. When this is the case, + show it when requested. */ + + else if ((dat_datctl.control & CTL_STARTCHAR) != 0) + { + PCRE2_SIZE startchar; + PCRE2_GET_STARTCHAR(startchar, match_data); + PCHARS(lleft, pp, startchar, start - startchar, utf, outfile); + PCHARSV(pp, start, end - start, utf, outfile); + if ((pat_patctl.control & CTL_JITVERIFY) != 0 && jit_was_used) + fprintf(outfile, " (JIT)"); + if (startchar != start) + { + fprintf(outfile, "\n "); + for (j = 0; j < lleft; j++) fprintf(outfile, "^"); + } + } + + /* Otherwise, just show the matched string. */ + + else + { + PCHARSV(pp, start, end - start, utf, outfile); + if ((pat_patctl.control & CTL_JITVERIFY) != 0 && jit_was_used) + fprintf(outfile, " (JIT)"); + } + } + + /* Not the main matched string. Just show it unadorned. */ + + else + { + PCHARSV(pp, start, end - start, utf, outfile); + } + + fprintf(outfile, "\n"); + + /* Note: don't use the start/end variables here because we want to + show the text from what is reported as the end. */ + + if ((dat_datctl.control & CTL_ALLAFTERTEXT) != 0 || + (i == 0 && (dat_datctl.control & CTL_AFTERTEXT) != 0)) + { + fprintf(outfile, "%2d+ ", i/2); + PCHARSV(pp, ovector[i+1], ulen - ovector[i+1], utf, outfile); + fprintf(outfile, "\n"); + } + } + + /* Output (*MARK) data if requested */ + + if ((dat_datctl.control & CTL_MARK) != 0 && + TESTFLD(match_data, mark, !=, NULL)) + { + fprintf(outfile, "MK: "); + PCHARSV(CASTFLD(void *, match_data, mark), 0, -1, utf, outfile); + fprintf(outfile, "\n"); + } + + /* Process copy/get strings */ + + if (!copy_and_get(utf, capcount)) return PR_ABEND; + + } /* End of handling a successful match */ + + /* There was a partial match. The value of ovector[0] is the bumpalong point, + that is, startchar, not any \K point that might have been passed. */ + + else if (capcount == PCRE2_ERROR_PARTIAL) + { + PCRE2_SIZE poffset; + int backlength; + int rubriclength = 0; + + fprintf(outfile, "Partial match"); + if ((dat_datctl.control & CTL_MARK) != 0 && + TESTFLD(match_data, mark, !=, NULL)) + { + fprintf(outfile, ", mark="); + PCHARS(rubriclength, CASTFLD(void *, match_data, mark), 0, -1, utf, + outfile); + rubriclength += 7; + } + fprintf(outfile, ": "); + rubriclength += 15; + + poffset = backchars(pp, ovector[0], maxlookbehind, utf); + PCHARS(backlength, pp, poffset, ovector[0] - poffset, utf, outfile); + PCHARSV(pp, ovector[0], ulen - ovector[0], utf, outfile); + + if ((pat_patctl.control & CTL_JITVERIFY) != 0 && jit_was_used) + fprintf(outfile, " (JIT)"); + fprintf(outfile, "\n"); + + if (backlength != 0) + { + int i; + for (i = 0; i < rubriclength; i++) fprintf(outfile, " "); + for (i = 0; i < backlength; i++) fprintf(outfile, "<"); + fprintf(outfile, "\n"); + } + + /* Process copy/get strings */ + + if (!copy_and_get(utf, 1)) return PR_ABEND; + + break; /* Out of the /g loop */ + } /* End of handling partial match */ + + /* Failed to match. If this is a /g or /G loop, we might previously have + set g_notempty (to PCRE2_NOTEMPTY_ATSTART|PCRE2_ANCHORED) after a null match. + If that is the case, this is not necessarily the end. We want to advance the + start offset, and continue. We won't be at the end of the string - that was + checked before setting g_notempty. We achieve the effect by pretending that a + single character was matched. + + Complication arises in the case when the newline convention is "any", "crlf", + or "anycrlf". If the previous match was at the end of a line terminated by + CRLF, an advance of one character just passes the CR, whereas we should + prefer the longer newline sequence, as does the code in pcre2_match(). + + Otherwise, in the case of UTF-8 or UTF-16 matching, the advance must be one + character, not one byte. */ + + else if (g_notempty != 0) /* There was a previous null match */ + { + uint16_t nl = FLD(compiled_code, newline_convention); + PCRE2_SIZE start_offset = dat_datctl.offset; /* Where the match was */ + PCRE2_SIZE end_offset = start_offset + 1; + + if ((nl == PCRE2_NEWLINE_CRLF || nl == PCRE2_NEWLINE_ANY || + nl == PCRE2_NEWLINE_ANYCRLF) && + start_offset < ulen - 1 && + CODE_UNIT(pp, start_offset) == '\r' && + CODE_UNIT(pp, end_offset) == '\n') + end_offset++; + + else if (utf && test_mode != PCRE32_MODE) + { + if (test_mode == PCRE8_MODE) + { + for (; end_offset < ulen; end_offset++) + if ((((PCRE2_SPTR8)pp)[end_offset] & 0xc0) != 0x80) break; + } + else /* 16-bit mode */ + { + for (; end_offset < ulen; end_offset++) + if ((((PCRE2_SPTR16)pp)[end_offset] & 0xfc00) != 0xdc00) break; + } + } + + SETFLDVEC(match_data, ovector, 0, start_offset); + SETFLDVEC(match_data, ovector, 1, end_offset); + } /* End of handling null match in a global loop */ + + /* A "normal" match failure. There will be a negative error number in + capcount. */ + + else + { + switch(capcount) + { + case PCRE2_ERROR_NOMATCH: + if (gmatched == 0) + { + fprintf(outfile, "No match"); + if ((dat_datctl.control & CTL_MARK) != 0 && + TESTFLD(match_data, mark, !=, NULL)) + { + fprintf(outfile, ", mark = "); + PCHARSV(CASTFLD(void *, match_data, mark), 0, -1, utf, outfile); + } + if ((pat_patctl.control & CTL_JITVERIFY) != 0 && jit_was_used) + fprintf(outfile, " (JIT)"); + fprintf(outfile, "\n"); + } + break; + + case PCRE2_ERROR_BADUTFOFFSET: + fprintf(outfile, "Error %d (bad UTF-%d offset)\n", capcount, test_mode); + break; + + default: + fprintf(outfile, "Failed: error %d: ", capcount); + if (!print_error_message(capcount, "", "")) return PR_ABEND; + if (capcount <= PCRE2_ERROR_UTF8_ERR1 && + capcount >= PCRE2_ERROR_UTF32_ERR2) + { + PCRE2_SIZE startchar; + PCRE2_GET_STARTCHAR(startchar, match_data); + fprintf(outfile, " at offset %" SIZ_FORM, SIZ_CAST startchar); + } + fprintf(outfile, "\n"); + break; + } + + break; /* Out of the /g loop */ + } /* End of failed match handling */ + + /* Control reaches here in two circumstances: (a) after a match, and (b) + after a non-match that immediately followed a match on an empty string when + doing a global search. Such a match is done with PCRE2_NOTEMPTY_ATSTART and + PCRE2_ANCHORED set in g_notempty. The code above turns it into a fake match + of one character. So effectively we get here only after a match. If we + are not doing a global search, we are done. */ + + if ((dat_datctl.control & CTL_ANYGLOB) == 0) break; else + { + PCRE2_SIZE match_offset = FLD(match_data, ovector)[0]; + PCRE2_SIZE end_offset = FLD(match_data, ovector)[1]; + + /* We must now set up for the next iteration of a global search. If we have + matched an empty string, first check to see if we are at the end of the + subject. If so, the loop is over. Otherwise, mimic what Perl's /g option + does. Set PCRE2_NOTEMPTY_ATSTART and PCRE2_ANCHORED and try the match again + at the same point. If this fails it will be picked up above, where a fake + match is set up so that at this point we advance to the next character. + + However, in order to cope with patterns that never match at their starting + offset (e.g. /(?<=\G.)/) we don't do this when the match offset is greater + than the starting offset. This means there will be a retry with the + starting offset at the match offset. If this returns the same match again, + it is picked up above and ignored, and the special action is then taken. */ + + if (match_offset == end_offset) + { + if (end_offset == ulen) break; /* End of subject */ + if (match_offset <= dat_datctl.offset) + g_notempty = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + } + + /* However, even after matching a non-empty string, there is still one + tricky case. If a pattern contains \K within a lookbehind assertion at the + start, the end of the matched string can be at the offset where the match + started. In the case of a normal /g iteration without special action, this + leads to a loop that keeps on returning the same substring. The loop would + be caught above, but we really want to move on to the next match. */ + + else + { + g_notempty = 0; /* Set for a "normal" repeat */ + if ((dat_datctl.control & CTL_GLOBAL) != 0) + { + PCRE2_SIZE startchar; + PCRE2_GET_STARTCHAR(startchar, match_data); + if (end_offset <= startchar) + { + if (startchar >= ulen) break; /* End of subject */ + end_offset = startchar + 1; + if (utf && test_mode != PCRE32_MODE) + { + if (test_mode == PCRE8_MODE) + { + for (; end_offset < ulen; end_offset++) + if ((((PCRE2_SPTR8)pp)[end_offset] & 0xc0) != 0x80) break; + } + else /* 16-bit mode */ + { + for (; end_offset < ulen; end_offset++) + if ((((PCRE2_SPTR16)pp)[end_offset] & 0xfc00) != 0xdc00) break; + } + } + } + } + } + + /* For a normal global (/g) iteration, save the current ovector[0,1] and + the starting offset so that we can check that they do change each time. + Otherwise a matching bug that returns the same string causes an infinite + loop. It has happened! Then update the start offset, leaving other + parameters alone. */ + + if ((dat_datctl.control & CTL_GLOBAL) != 0) + { + ovecsave[0] = ovector[0]; + ovecsave[1] = ovector[1]; + ovecsave[2] = dat_datctl.offset; + dat_datctl.offset = end_offset; + } + + /* For altglobal, just update the pointer and length. */ + + else + { + pp += end_offset * code_unit_size; + len -= end_offset * code_unit_size; + ulen -= end_offset; + if (arg_ulen != PCRE2_ZERO_TERMINATED) arg_ulen -= end_offset; + } + } + } /* End of global loop */ + +show_memory = FALSE; +return PR_OK; +} + + + + +/************************************************* +* Print PCRE2 version * +*************************************************/ + +static void +print_version(FILE *f) +{ +VERSION_TYPE *vp; +fprintf(f, "PCRE2 version "); +for (vp = version; *vp != 0; vp++) fprintf(f, "%c", *vp); +fprintf(f, "\n"); +} + + + +/************************************************* +* Print Unicode version * +*************************************************/ + +static void +print_unicode_version(FILE *f) +{ +VERSION_TYPE *vp; +fprintf(f, "Unicode version "); +for (vp = uversion; *vp != 0; vp++) fprintf(f, "%c", *vp); +} + + + +/************************************************* +* Print JIT target * +*************************************************/ + +static void +print_jit_target(FILE *f) +{ +VERSION_TYPE *vp; +for (vp = jittarget; *vp != 0; vp++) fprintf(f, "%c", *vp); +} + + + +/************************************************* +* Print newline configuration * +*************************************************/ + +/* Output is always to stdout. + +Arguments: + rc the return code from PCRE2_CONFIG_NEWLINE + isc TRUE if called from "-C newline" +Returns: nothing +*/ + +static void +print_newline_config(uint32_t optval, BOOL isc) +{ +if (!isc) printf(" Newline sequence is "); +if (optval < sizeof(newlines)/sizeof(char *)) + printf("%s\n", newlines[optval]); +else + printf("a non-standard value: %d\n", optval); +} + + + +/************************************************* +* Usage function * +*************************************************/ + +static void +usage(void) +{ +printf("Usage: pcre2test [options] [ []]\n\n"); +printf("Input and output default to stdin and stdout.\n"); +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) +printf("If input is a terminal, readline() is used to read from it.\n"); +#else +printf("This version of pcre2test is not linked with readline().\n"); +#endif +printf("\nOptions:\n"); +#ifdef SUPPORT_PCRE2_8 +printf(" -8 use the 8-bit library\n"); +#endif +#ifdef SUPPORT_PCRE2_16 +printf(" -16 use the 16-bit library\n"); +#endif +#ifdef SUPPORT_PCRE2_32 +printf(" -32 use the 32-bit library\n"); +#endif +printf(" -ac set default pattern modifier PCRE2_AUTO_CALLOUT\n"); +printf(" -AC as -ac, but also set subject 'callout_extra' modifier\n"); +printf(" -b set default pattern modifier 'fullbincode'\n"); +printf(" -C show PCRE2 compile-time options and exit\n"); +printf(" -C arg show a specific compile-time option and exit with its\n"); +printf(" value if numeric (else 0). The arg can be:\n"); +printf(" backslash-C use of \\C is enabled [0, 1]\n"); +printf(" bsr \\R type [ANYCRLF, ANY]\n"); +printf(" ebcdic compiled for EBCDIC character code [0,1]\n"); +printf(" ebcdic-nl NL code if compiled for EBCDIC\n"); +printf(" jit just-in-time compiler supported [0, 1]\n"); +printf(" linksize internal link size [2, 3, 4]\n"); +printf(" newline newline type [CR, LF, CRLF, ANYCRLF, ANY, NUL]\n"); +printf(" pcre2-8 8 bit library support enabled [0, 1]\n"); +printf(" pcre2-16 16 bit library support enabled [0, 1]\n"); +printf(" pcre2-32 32 bit library support enabled [0, 1]\n"); +printf(" unicode Unicode and UTF support enabled [0, 1]\n"); +printf(" -d set default pattern modifier 'debug'\n"); +printf(" -dfa set default subject modifier 'dfa'\n"); +printf(" -error show messages for error numbers, then exit\n"); +printf(" -help show usage information\n"); +printf(" -i set default pattern modifier 'info'\n"); +printf(" -jit set default pattern modifier 'jit'\n"); +printf(" -jitverify set default pattern modifier 'jitverify'\n"); +printf(" -LM list pattern and subject modifiers, then exit\n"); +printf(" -q quiet: do not output PCRE2 version number at start\n"); +printf(" -pattern set default pattern modifier fields\n"); +printf(" -subject set default subject modifier fields\n"); +printf(" -S set stack size to mebibytes\n"); +printf(" -t [] time compilation and execution, repeating times\n"); +printf(" -tm [] time execution (matching) only, repeating times\n"); +printf(" -T same as -t, but show total times at the end\n"); +printf(" -TM same as -tm, but show total time at the end\n"); +printf(" -version show PCRE2 version and exit\n"); +} + + + +/************************************************* +* Handle -C option * +*************************************************/ + +/* This option outputs configuration options and sets an appropriate return +code when asked for a single option. The code is abstracted into a separate +function because of its size. Use whichever pcre2_config() function is +available. + +Argument: an option name or NULL +Returns: the return code +*/ + +static int +c_option(const char *arg) +{ +uint32_t optval; +unsigned int i = COPTLISTCOUNT; +int yield = 0; + +if (arg != NULL && arg[0] != CHAR_MINUS) + { + for (i = 0; i < COPTLISTCOUNT; i++) + if (strcmp(arg, coptlist[i].name) == 0) break; + + if (i >= COPTLISTCOUNT) + { + fprintf(stderr, "** Unknown -C option '%s'\n", arg); + return 0; + } + + switch (coptlist[i].type) + { + case CONF_BSR: + (void)PCRE2_CONFIG(coptlist[i].value, &optval); + printf("%s\n", (optval == PCRE2_BSR_ANYCRLF)? "ANYCRLF" : "ANY"); + break; + + case CONF_FIX: + yield = coptlist[i].value; + printf("%d\n", yield); + break; + + case CONF_FIZ: + optval = coptlist[i].value; + printf("%d\n", optval); + break; + + case CONF_INT: + (void)PCRE2_CONFIG(coptlist[i].value, &yield); + printf("%d\n", yield); + break; + + case CONF_NL: + (void)PCRE2_CONFIG(coptlist[i].value, &optval); + print_newline_config(optval, TRUE); + break; + } + +/* For VMS, return the value by setting a symbol, for certain values only. */ + +#ifdef __VMS + if (copytlist[i].type == CONF_FIX || coptlist[i].type == CONF_INT) + { + char ucname[16]; + strcpy(ucname, coptlist[i].name); + for (i = 0; ucname[i] != 0; i++) ucname[i] = toupper[ucname[i]]; + vms_setsymbol(ucname, 0, optval); + } +#endif + + return yield; + } + +/* No argument for -C: output all configuration information. */ + +print_version(stdout); +printf("Compiled with\n"); + +#ifdef EBCDIC +printf(" EBCDIC code support: LF is 0x%02x\n", CHAR_LF); +#if defined NATIVE_ZOS +printf(" EBCDIC code page %s or similar\n", pcrz_cpversion()); +#endif +#endif + +(void)PCRE2_CONFIG(PCRE2_CONFIG_COMPILED_WIDTHS, &optval); +if (optval & 1) printf(" 8-bit support\n"); +if (optval & 2) printf(" 16-bit support\n"); +if (optval & 4) printf(" 32-bit support\n"); + +#ifdef SUPPORT_VALGRIND +printf(" Valgrind support\n"); +#endif + +(void)PCRE2_CONFIG(PCRE2_CONFIG_UNICODE, &optval); +if (optval != 0) + { + printf(" UTF and UCP support ("); + print_unicode_version(stdout); + printf(")\n"); + } +else printf(" No Unicode support\n"); + +(void)PCRE2_CONFIG(PCRE2_CONFIG_JIT, &optval); +if (optval != 0) + { + printf(" Just-in-time compiler support: "); + print_jit_target(stdout); + printf("\n"); + } +else + { + printf(" No just-in-time compiler support\n"); + } + +(void)PCRE2_CONFIG(PCRE2_CONFIG_NEWLINE, &optval); +print_newline_config(optval, FALSE); +(void)PCRE2_CONFIG(PCRE2_CONFIG_BSR, &optval); +printf(" \\R matches %s\n", + (optval == PCRE2_BSR_ANYCRLF)? "CR, LF, or CRLF only" : + "all Unicode newlines"); +(void)PCRE2_CONFIG(PCRE2_CONFIG_NEVER_BACKSLASH_C, &optval); +printf(" \\C is %ssupported\n", optval? "not ":""); +(void)PCRE2_CONFIG(PCRE2_CONFIG_LINKSIZE, &optval); +printf(" Internal link size = %d\n", optval); +(void)PCRE2_CONFIG(PCRE2_CONFIG_PARENSLIMIT, &optval); +printf(" Parentheses nest limit = %d\n", optval); +(void)PCRE2_CONFIG(PCRE2_CONFIG_HEAPLIMIT, &optval); +printf(" Default heap limit = %d\n", optval); +(void)PCRE2_CONFIG(PCRE2_CONFIG_MATCHLIMIT, &optval); +printf(" Default match limit = %d\n", optval); +(void)PCRE2_CONFIG(PCRE2_CONFIG_DEPTHLIMIT, &optval); +printf(" Default depth limit = %d\n", optval); +return 0; +} + + + +/************************************************* +* Display one modifier * +*************************************************/ + +static void +display_one_modifier(modstruct *m, BOOL for_pattern) +{ +uint32_t c = (!for_pattern && (m->which == MOD_PND || m->which == MOD_PNDP))? + '*' : ' '; +printf("%c%s", c, m->name); +} + + + +/************************************************* +* Display pattern or subject modifiers * +*************************************************/ + +/* In order to print in two columns, first scan without printing to get a list +of the modifiers that are required. + +Arguments: + for_pattern TRUE for pattern modifiers, FALSE for subject modifiers + title string to be used in title + +Returns: nothing +*/ + +static void +display_selected_modifiers(BOOL for_pattern, const char *title) +{ +uint32_t i, j; +uint32_t n = 0; +uint32_t list[MODLISTCOUNT]; + +for (i = 0; i < MODLISTCOUNT; i++) + { + BOOL is_pattern = TRUE; + modstruct *m = modlist + i; + + switch (m->which) + { + case MOD_CTC: /* Compile context */ + case MOD_PAT: /* Pattern */ + case MOD_PATP: /* Pattern, OK for Perl-compatible test */ + break; + + /* The MOD_PND and MOD_PNDP modifiers are precisely those that affect + subjects, but can be given with a pattern. We list them as subject + modifiers, but marked with an asterisk.*/ + + case MOD_CTM: /* Match context */ + case MOD_DAT: /* Subject line */ + case MOD_PND: /* As PD, but not default pattern */ + case MOD_PNDP: /* As PND, OK for Perl-compatible test */ + is_pattern = FALSE; + break; + + default: printf("** Unknown type for modifier '%s'\n", m->name); + /* Fall through */ + case MOD_PD: /* Pattern or subject */ + case MOD_PDP: /* As PD, OK for Perl-compatible test */ + is_pattern = for_pattern; + break; + } + + if (for_pattern == is_pattern) list[n++] = i; + } + +/* Now print from the list in two columns. */ + +printf("-------------- %s MODIFIERS --------------\n", title); + +for (i = 0, j = (n+1)/2; i < (n+1)/2; i++, j++) + { + modstruct *m = modlist + list[i]; + display_one_modifier(m, for_pattern); + if (j < n) + { + uint32_t k = 27 - strlen(m->name); + while (k-- > 0) printf(" "); + display_one_modifier(modlist + list[j], for_pattern); + } + printf("\n"); + } +} + + + +/************************************************* +* Display the list of modifiers * +*************************************************/ + +static void +display_modifiers(void) +{ +printf( + "An asterisk on a subject modifier means that it may be given on a pattern\n" + "line, in order to apply to all subjects matched by that pattern. Modifiers\n" + "that are listed for both patterns and subjects have different effects in\n" + "each case.\n\n"); +display_selected_modifiers(TRUE, "PATTERN"); +printf("\n"); +display_selected_modifiers(FALSE, "SUBJECT"); +} + + + +/************************************************* +* Main Program * +*************************************************/ + +int +main(int argc, char **argv) +{ +uint32_t temp; +uint32_t yield = 0; +uint32_t op = 1; +BOOL notdone = TRUE; +BOOL quiet = FALSE; +BOOL showtotaltimes = FALSE; +BOOL skipping = FALSE; +char *arg_subject = NULL; +char *arg_pattern = NULL; +char *arg_error = NULL; + +/* The offsets to the options and control bits fields of the pattern and data +control blocks must be the same so that common options and controls such as +"anchored" or "memory" can work for either of them from a single table entry. +We cannot test this till runtime because "offsetof" does not work in the +preprocessor. */ + +if (PO(options) != DO(options) || PO(control) != DO(control) || + PO(control2) != DO(control2)) + { + fprintf(stderr, "** Coding error: " + "options and control offsets for pattern and data must be the same.\n"); + return 1; + } + +/* Get the PCRE2 and Unicode version number and JIT target information, at the +same time checking that a request for the length gives the same answer. Also +check lengths for non-string items. */ + +if (PCRE2_CONFIG(PCRE2_CONFIG_VERSION, NULL) != + PCRE2_CONFIG(PCRE2_CONFIG_VERSION, version) || + + PCRE2_CONFIG(PCRE2_CONFIG_UNICODE_VERSION, NULL) != + PCRE2_CONFIG(PCRE2_CONFIG_UNICODE_VERSION, uversion) || + + PCRE2_CONFIG(PCRE2_CONFIG_JITTARGET, NULL) != + PCRE2_CONFIG(PCRE2_CONFIG_JITTARGET, jittarget) || + + PCRE2_CONFIG(PCRE2_CONFIG_UNICODE, NULL) != sizeof(uint32_t) || + PCRE2_CONFIG(PCRE2_CONFIG_MATCHLIMIT, NULL) != sizeof(uint32_t)) + { + fprintf(stderr, "** Error in pcre2_config(): bad length\n"); + return 1; + } + +/* Check that bad options are diagnosed. */ + +if (PCRE2_CONFIG(999, NULL) != PCRE2_ERROR_BADOPTION || + PCRE2_CONFIG(999, &temp) != PCRE2_ERROR_BADOPTION) + { + fprintf(stderr, "** Error in pcre2_config(): bad option not diagnosed\n"); + return 1; + } + +/* This configuration option is now obsolete, but running a quick check ensures +that its code is covered. */ + +(void)PCRE2_CONFIG(PCRE2_CONFIG_STACKRECURSE, &temp); + +/* Get buffers from malloc() so that valgrind will check their misuse when +debugging. They grow automatically when very long lines are read. The 16- +and 32-bit buffers (pbuffer16, pbuffer32) are obtained only if needed. */ + +buffer = (uint8_t *)malloc(pbuffer8_size); +pbuffer8 = (uint8_t *)malloc(pbuffer8_size); + +/* The following _setmode() stuff is some Windows magic that tells its runtime +library to translate CRLF into a single LF character. At least, that's what +I've been told: never having used Windows I take this all on trust. Originally +it set 0x8000, but then I was advised that _O_BINARY was better. */ + +#if defined(_WIN32) || defined(WIN32) +_setmode( _fileno( stdout ), _O_BINARY ); +#endif + +/* Initialization that does not depend on the running mode. */ + +locale_name[0] = 0; + +memset(&def_patctl, 0, sizeof(patctl)); +def_patctl.convert_type = CONVERT_UNSET; + +memset(&def_datctl, 0, sizeof(datctl)); +def_datctl.oveccount = DEFAULT_OVECCOUNT; +def_datctl.copy_numbers[0] = -1; +def_datctl.get_numbers[0] = -1; +def_datctl.startend[0] = def_datctl.startend[1] = CFORE_UNSET; +def_datctl.cerror[0] = def_datctl.cerror[1] = CFORE_UNSET; +def_datctl.cfail[0] = def_datctl.cfail[1] = CFORE_UNSET; + +/* Scan command line options. */ + +while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) + { + char *endptr; + char *arg = argv[op]; + unsigned long uli; + + /* List modifiers and exit. */ + + if (strcmp(arg, "-LM") == 0) + { + display_modifiers(); + goto EXIT; + } + + /* Display and/or set return code for configuration options. */ + + if (strcmp(arg, "-C") == 0) + { + yield = c_option(argv[op + 1]); + goto EXIT; + } + + /* Select operating mode. Ensure that pcre2_config() is called in 16-bit + and 32-bit modes because that won't happen naturally when 8-bit is also + configured. Also call some other functions that are not otherwise used. This + means that a coverage report won't claim there are uncalled functions. */ + + if (strcmp(arg, "-8") == 0) + { +#ifdef SUPPORT_PCRE2_8 + test_mode = PCRE8_MODE; + (void)pcre2_set_bsr_8(pat_context8, 999); + (void)pcre2_set_newline_8(pat_context8, 999); +#else + fprintf(stderr, + "** This version of PCRE2 was built without 8-bit support\n"); + exit(1); +#endif + } + + else if (strcmp(arg, "-16") == 0) + { +#ifdef SUPPORT_PCRE2_16 + test_mode = PCRE16_MODE; + (void)pcre2_config_16(PCRE2_CONFIG_VERSION, NULL); + (void)pcre2_set_bsr_16(pat_context16, 999); + (void)pcre2_set_newline_16(pat_context16, 999); +#else + fprintf(stderr, + "** This version of PCRE2 was built without 16-bit support\n"); + exit(1); +#endif + } + + else if (strcmp(arg, "-32") == 0) + { +#ifdef SUPPORT_PCRE2_32 + test_mode = PCRE32_MODE; + (void)pcre2_config_32(PCRE2_CONFIG_VERSION, NULL); + (void)pcre2_set_bsr_32(pat_context32, 999); + (void)pcre2_set_newline_32(pat_context32, 999); +#else + fprintf(stderr, + "** This version of PCRE2 was built without 32-bit support\n"); + exit(1); +#endif + } + + /* Set quiet (no version verification) */ + + else if (strcmp(arg, "-q") == 0) quiet = TRUE; + + /* Set system stack size */ + + else if (strcmp(arg, "-S") == 0 && argc > 2 && + ((uli = strtoul(argv[op+1], &endptr, 10)), *endptr == 0)) + { +#if defined(_WIN32) || defined(WIN32) || defined(__minix) || defined(NATIVE_ZOS) || defined(__VMS) + fprintf(stderr, "pcre2test: -S is not supported on this OS\n"); + exit(1); +#else + int rc; + uint32_t stack_size; + struct rlimit rlim; + if (U32OVERFLOW(uli)) + { + fprintf(stderr, "** Argument for -S is too big\n"); + exit(1); + } + stack_size = (uint32_t)uli; + getrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = stack_size * 1024 * 1024; + if (rlim.rlim_cur > rlim.rlim_max) + { + fprintf(stderr, + "pcre2test: requested stack size %luMiB is greater than hard limit " + "%luMiB\n", (unsigned long int)stack_size, + (unsigned long int)(rlim.rlim_max)); + exit(1); + } + rc = setrlimit(RLIMIT_STACK, &rlim); + if (rc != 0) + { + fprintf(stderr, "pcre2test: setting stack size %luMiB failed: %s\n", + (unsigned long int)stack_size, strerror(errno)); + exit(1); + } + op++; + argc--; +#endif + } + + /* Set some common pattern and subject controls */ + + else if (strcmp(arg, "-AC") == 0) + { + def_patctl.options |= PCRE2_AUTO_CALLOUT; + def_datctl.control2 |= CTL2_CALLOUT_EXTRA; + } + else if (strcmp(arg, "-ac") == 0) def_patctl.options |= PCRE2_AUTO_CALLOUT; + else if (strcmp(arg, "-b") == 0) def_patctl.control |= CTL_FULLBINCODE; + else if (strcmp(arg, "-d") == 0) def_patctl.control |= CTL_DEBUG; + else if (strcmp(arg, "-dfa") == 0) def_datctl.control |= CTL_DFA; + else if (strcmp(arg, "-i") == 0) def_patctl.control |= CTL_INFO; + else if (strcmp(arg, "-jit") == 0 || strcmp(arg, "-jitverify") == 0) + { + if (arg[4] != 0) def_patctl.control |= CTL_JITVERIFY; + def_patctl.jit = 7; /* full & partial */ +#ifndef SUPPORT_JIT + fprintf(stderr, "** Warning: JIT support is not available: " + "-jit[verify] calls functions that do nothing.\n"); +#endif + } + + /* Set timing parameters */ + + else if (strcmp(arg, "-t") == 0 || strcmp(arg, "-tm") == 0 || + strcmp(arg, "-T") == 0 || strcmp(arg, "-TM") == 0) + { + int both = arg[2] == 0; + showtotaltimes = arg[1] == 'T'; + if (argc > 2 && (uli = strtoul(argv[op+1], &endptr, 10), *endptr == 0)) + { + if (U32OVERFLOW(uli)) + { + fprintf(stderr, "** Argument for %s is too big\n", arg); + exit(1); + } + timeitm = (int)uli; + op++; + argc--; + } + else timeitm = LOOPREPEAT; + if (both) timeit = timeitm; + } + + /* Give help */ + + else if (strcmp(arg, "-help") == 0 || + strcmp(arg, "--help") == 0) + { + usage(); + goto EXIT; + } + + /* Show version */ + + else if (strcmp(arg, "-version") == 0 || + strcmp(arg, "--version") == 0) + { + print_version(stdout); + goto EXIT; + } + + /* The following options save their data for processing once we know what + the running mode is. */ + + else if (strcmp(arg, "-error") == 0) + { + arg_error = argv[op+1]; + goto CHECK_VALUE_EXISTS; + } + + else if (strcmp(arg, "-subject") == 0) + { + arg_subject = argv[op+1]; + goto CHECK_VALUE_EXISTS; + } + + else if (strcmp(arg, "-pattern") == 0) + { + arg_pattern = argv[op+1]; + CHECK_VALUE_EXISTS: + if (argc <= 2) + { + fprintf(stderr, "** Missing value for %s\n", arg); + yield = 1; + goto EXIT; + } + op++; + argc--; + } + + /* Unrecognized option */ + + else + { + fprintf(stderr, "** Unknown or malformed option '%s'\n", arg); + usage(); + yield = 1; + goto EXIT; + } + op++; + argc--; + } + +/* If -error was present, get the error numbers, show the messages, and exit. +We wait to do this until we know which mode we are in. */ + +if (arg_error != NULL) + { + int len; + int errcode; + char *endptr; + +/* Ensure the relevant non-8-bit buffer is available. Ensure that it is at +least 128 code units, because it is used for retrieving error messages. */ + +#ifdef SUPPORT_PCRE2_16 + if (test_mode == PCRE16_MODE) + { + pbuffer16_size = 256; + pbuffer16 = (uint16_t *)malloc(pbuffer16_size); + if (pbuffer16 == NULL) + { + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer16\n", + SIZ_CAST pbuffer16_size); + yield = 1; + goto EXIT; + } + } +#endif + +#ifdef SUPPORT_PCRE2_32 + if (test_mode == PCRE32_MODE) + { + pbuffer32_size = 512; + pbuffer32 = (uint32_t *)malloc(pbuffer32_size); + if (pbuffer32 == NULL) + { + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer32\n", + SIZ_CAST pbuffer32_size); + yield = 1; + goto EXIT; + } + } +#endif + + /* Loop along a list of error numbers. */ + + for (;;) + { + errcode = strtol(arg_error, &endptr, 10); + if (*endptr != 0 && *endptr != CHAR_COMMA) + { + fprintf(stderr, "** '%s' is not a valid error number list\n", arg_error); + yield = 1; + goto EXIT; + } + printf("Error %d: ", errcode); + PCRE2_GET_ERROR_MESSAGE(len, errcode, pbuffer); + if (len < 0) + { + switch (len) + { + case PCRE2_ERROR_BADDATA: + printf("PCRE2_ERROR_BADDATA (unknown error number)"); + break; + + case PCRE2_ERROR_NOMEMORY: + printf("PCRE2_ERROR_NOMEMORY (buffer too small)"); + break; + + default: + printf("Unexpected return (%d) from pcre2_get_error_message()", len); + break; + } + } + else + { + PCHARSV(CASTVAR(void *, pbuffer), 0, len, FALSE, stdout); + } + printf("\n"); + if (*endptr == 0) goto EXIT; + arg_error = endptr + 1; + } + /* Control never reaches here */ + } /* End of -error handling */ + +/* Initialize things that cannot be done until we know which test mode we are +running in. Exercise the general context copying function, which is not +otherwise used. */ + +code_unit_size = test_mode/8; +max_oveccount = DEFAULT_OVECCOUNT; + +/* Use macros to save a lot of duplication. */ + +#define CREATECONTEXTS \ + G(general_context,BITS) = G(pcre2_general_context_create_,BITS)(&my_malloc, &my_free, NULL); \ + G(general_context_copy,BITS) = G(pcre2_general_context_copy_,BITS)(G(general_context,BITS)); \ + G(default_pat_context,BITS) = G(pcre2_compile_context_create_,BITS)(G(general_context,BITS)); \ + G(pat_context,BITS) = G(pcre2_compile_context_copy_,BITS)(G(default_pat_context,BITS)); \ + G(default_dat_context,BITS) = G(pcre2_match_context_create_,BITS)(G(general_context,BITS)); \ + G(dat_context,BITS) = G(pcre2_match_context_copy_,BITS)(G(default_dat_context,BITS)); \ + G(default_con_context,BITS) = G(pcre2_convert_context_create_,BITS)(G(general_context,BITS)); \ + G(con_context,BITS) = G(pcre2_convert_context_copy_,BITS)(G(default_con_context,BITS)); \ + G(match_data,BITS) = G(pcre2_match_data_create_,BITS)(max_oveccount, G(general_context,BITS)) + +#define CONTEXTTESTS \ + (void)G(pcre2_set_compile_extra_options_,BITS)(G(pat_context,BITS), 0); \ + (void)G(pcre2_set_max_pattern_length_,BITS)(G(pat_context,BITS), 0); \ + (void)G(pcre2_set_offset_limit_,BITS)(G(dat_context,BITS), 0); \ + (void)G(pcre2_set_recursion_memory_management_,BITS)(G(dat_context,BITS), my_malloc, my_free, NULL) + +/* Call the appropriate functions for the current mode, and exercise some +functions that are not otherwise called. */ + +#ifdef SUPPORT_PCRE2_8 +#undef BITS +#define BITS 8 +if (test_mode == PCRE8_MODE) + { + CREATECONTEXTS; + CONTEXTTESTS; + } +#endif + +#ifdef SUPPORT_PCRE2_16 +#undef BITS +#define BITS 16 +if (test_mode == PCRE16_MODE) + { + CREATECONTEXTS; + CONTEXTTESTS; + } +#endif + +#ifdef SUPPORT_PCRE2_32 +#undef BITS +#define BITS 32 +if (test_mode == PCRE32_MODE) + { + CREATECONTEXTS; + CONTEXTTESTS; + } +#endif + +/* Set a default parentheses nest limit that is large enough to run the +standard tests (this also exercises the function). */ + +PCRE2_SET_PARENS_NEST_LIMIT(default_pat_context, PARENS_NEST_DEFAULT); + +/* Handle command line modifier settings, sending any error messages to +stderr. We need to know the mode before modifying the context, and it is tidier +to do them all in the same way. */ + +outfile = stderr; +if ((arg_pattern != NULL && + !decode_modifiers((uint8_t *)arg_pattern, CTX_DEFPAT, &def_patctl, NULL)) || + (arg_subject != NULL && + !decode_modifiers((uint8_t *)arg_subject, CTX_DEFDAT, NULL, &def_datctl))) + { + yield = 1; + goto EXIT; + } + +/* Sort out the input and output files, defaulting to stdin/stdout. */ + +infile = stdin; +outfile = stdout; + +if (argc > 1 && strcmp(argv[op], "-") != 0) + { + infile = fopen(argv[op], INPUT_MODE); + if (infile == NULL) + { + printf("** Failed to open '%s': %s\n", argv[op], strerror(errno)); + yield = 1; + goto EXIT; + } + } + +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) +if (INTERACTIVE(infile)) using_history(); +#endif + +if (argc > 2) + { + outfile = fopen(argv[op+1], OUTPUT_MODE); + if (outfile == NULL) + { + printf("** Failed to open '%s': %s\n", argv[op+1], strerror(errno)); + yield = 1; + goto EXIT; + } + } + +/* Output a heading line unless quiet, then process input lines. */ + +if (!quiet) print_version(outfile); + +SET(compiled_code, NULL); + +#ifdef SUPPORT_PCRE2_8 +preg.re_pcre2_code = NULL; +preg.re_match_data = NULL; +#endif + +while (notdone) + { + uint8_t *p; + int rc = PR_OK; + BOOL expectdata = TEST(compiled_code, !=, NULL); +#ifdef SUPPORT_PCRE2_8 + expectdata |= preg.re_pcre2_code != NULL; +#endif + + if (extend_inputline(infile, buffer, expectdata? "data> " : " re> ") == NULL) + break; + if (!INTERACTIVE(infile)) fprintf(outfile, "%s", (char *)buffer); + fflush(outfile); + p = buffer; + + /* If we have a pattern set up for testing, or we are skipping after a + compile failure, a blank line terminates this test. */ + + if (expectdata || skipping) + { + while (isspace(*p)) p++; + if (*p == 0) + { +#ifdef SUPPORT_PCRE2_8 + if (preg.re_pcre2_code != NULL) + { + regfree(&preg); + preg.re_pcre2_code = NULL; + preg.re_match_data = NULL; + } +#endif /* SUPPORT_PCRE2_8 */ + if (TEST(compiled_code, !=, NULL)) + { + SUB1(pcre2_code_free, compiled_code); + SET(compiled_code, NULL); + } + skipping = FALSE; + setlocale(LC_CTYPE, "C"); + } + + /* Otherwise, if we are not skipping, and the line is not a data comment + line starting with "\=", process a data line. */ + + else if (!skipping && !(p[0] == '\\' && p[1] == '=' && isspace(p[2]))) + { + rc = process_data(); + } + } + + /* We do not have a pattern set up for testing. Lines starting with # are + either comments or special commands. Blank lines are ignored. Otherwise, the + line must start with a valid delimiter. It is then processed as a pattern + line. A copy of the pattern is left in pbuffer8 for use by callouts. Under + valgrind, make the unused part of the buffer undefined, to catch overruns. */ + + else if (*p == '#') + { + if (isspace(p[1]) || p[1] == '!' || p[1] == 0) continue; + rc = process_command(); + } + + else if (strchr("/!\"'`%&-=_:;,@~", *p) != NULL) + { + rc = process_pattern(); + dfa_matched = 0; + } + + else + { + while (isspace(*p)) p++; + if (*p != 0) + { + fprintf(outfile, "** Invalid pattern delimiter '%c' (x%x).\n", *buffer, + *buffer); + rc = PR_SKIP; + } + } + + if (rc == PR_SKIP && !INTERACTIVE(infile)) skipping = TRUE; + else if (rc == PR_ABEND) + { + fprintf(outfile, "** pcre2test run abandoned\n"); + yield = 1; + goto EXIT; + } + } + +/* Finish off a normal run. */ + +if (INTERACTIVE(infile)) fprintf(outfile, "\n"); + +if (showtotaltimes) + { + const char *pad = ""; + fprintf(outfile, "--------------------------------------\n"); + if (timeit > 0) + { + fprintf(outfile, "Total compile time %.4f milliseconds\n", + (((double)total_compile_time * 1000.0) / (double)timeit) / + (double)CLOCKS_PER_SEC); + if (total_jit_compile_time > 0) + fprintf(outfile, "Total JIT compile %.4f milliseconds\n", + (((double)total_jit_compile_time * 1000.0) / (double)timeit) / + (double)CLOCKS_PER_SEC); + pad = " "; + } + fprintf(outfile, "Total match time %s%.4f milliseconds\n", pad, + (((double)total_match_time * 1000.0) / (double)timeitm) / + (double)CLOCKS_PER_SEC); + } + + +EXIT: + +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) +if (infile != NULL && INTERACTIVE(infile)) clear_history(); +#endif + +if (infile != NULL && infile != stdin) fclose(infile); +if (outfile != NULL && outfile != stdout) fclose(outfile); + +free(buffer); +free(dbuffer); +free(pbuffer8); +free(dfa_workspace); +free((void *)locale_tables); +PCRE2_MATCH_DATA_FREE(match_data); +SUB1(pcre2_code_free, compiled_code); + +while(patstacknext-- > 0) + { + SET(compiled_code, patstack[patstacknext]); + SUB1(pcre2_code_free, compiled_code); + } + +PCRE2_JIT_FREE_UNUSED_MEMORY(general_context); +if (jit_stack != NULL) + { + PCRE2_JIT_STACK_FREE(jit_stack); + } + +#define FREECONTEXTS \ + G(pcre2_general_context_free_,BITS)(G(general_context,BITS)); \ + G(pcre2_general_context_free_,BITS)(G(general_context_copy,BITS)); \ + G(pcre2_compile_context_free_,BITS)(G(pat_context,BITS)); \ + G(pcre2_compile_context_free_,BITS)(G(default_pat_context,BITS)); \ + G(pcre2_match_context_free_,BITS)(G(dat_context,BITS)); \ + G(pcre2_match_context_free_,BITS)(G(default_dat_context,BITS)); \ + G(pcre2_convert_context_free_,BITS)(G(default_con_context,BITS)); \ + G(pcre2_convert_context_free_,BITS)(G(con_context,BITS)); + +#ifdef SUPPORT_PCRE2_8 +#undef BITS +#define BITS 8 +if (preg.re_pcre2_code != NULL) regfree(&preg); +FREECONTEXTS; +#endif + +#ifdef SUPPORT_PCRE2_16 +#undef BITS +#define BITS 16 +free(pbuffer16); +FREECONTEXTS; +#endif + +#ifdef SUPPORT_PCRE2_32 +#undef BITS +#define BITS 32 +free(pbuffer32); +FREECONTEXTS; +#endif + +#if defined(__VMS) + yield = SS$_NORMAL; /* Return values via DCL symbols */ +#endif + +return yield; +} + +/* End of pcre2test.c */ diff --git a/ProcessHacker/phsvc/clapi.c b/ProcessHacker/phsvc/clapi.c index 3772cf25e837..ae229a216c64 100644 --- a/ProcessHacker/phsvc/clapi.c +++ b/ProcessHacker/phsvc/clapi.c @@ -1,1217 +1,1216 @@ -/* - * Process Hacker - - * phsvc client - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -HANDLE PhSvcClPortHandle; -PVOID PhSvcClPortHeap; -HANDLE PhSvcClServerProcessId; - -NTSTATUS PhSvcConnectToServer( - _In_ PUNICODE_STRING PortName, - _In_opt_ SIZE_T PortSectionSize - ) -{ - NTSTATUS status; - HANDLE sectionHandle; - LARGE_INTEGER sectionSize; - PORT_VIEW clientView; - REMOTE_PORT_VIEW serverView; - SECURITY_QUALITY_OF_SERVICE securityQos; - ULONG maxMessageLength; - PHSVC_API_CONNECTINFO connectInfo; - ULONG connectInfoLength; - - if (PhSvcClPortHandle) - return STATUS_ADDRESS_ALREADY_EXISTS; - - if (PortSectionSize == 0) - PortSectionSize = 512 * 1024; - - // Create the port section and connect to the port. - - sectionSize.QuadPart = PortSectionSize; - status = NtCreateSection( - §ionHandle, - SECTION_ALL_ACCESS, - NULL, - §ionSize, - PAGE_READWRITE, - SEC_COMMIT, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - clientView.Length = sizeof(PORT_VIEW); - clientView.SectionHandle = sectionHandle; - clientView.SectionOffset = 0; - clientView.ViewSize = PortSectionSize; - clientView.ViewBase = NULL; - clientView.ViewRemoteBase = NULL; - - serverView.Length = sizeof(REMOTE_PORT_VIEW); - serverView.ViewSize = 0; - serverView.ViewBase = NULL; - - securityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); - securityQos.ImpersonationLevel = SecurityImpersonation; - securityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; - securityQos.EffectiveOnly = TRUE; - - connectInfoLength = sizeof(PHSVC_API_CONNECTINFO); - - status = NtConnectPort( - &PhSvcClPortHandle, - PortName, - &securityQos, - &clientView, - &serverView, - &maxMessageLength, - &connectInfo, - &connectInfoLength - ); - NtClose(sectionHandle); - - if (!NT_SUCCESS(status)) - return status; - - PhSvcClServerProcessId = UlongToHandle(connectInfo.ServerProcessId); - - // Create the port heap. - - PhSvcClPortHeap = RtlCreateHeap( - HEAP_CLASS_1, - clientView.ViewBase, - clientView.ViewSize, - PAGE_SIZE, - NULL, - NULL - ); - - if (!PhSvcClPortHeap) - { - NtClose(PhSvcClPortHandle); - return STATUS_INSUFFICIENT_RESOURCES; - } - - return status; -} - -VOID PhSvcDisconnectFromServer( - VOID - ) -{ - if (PhSvcClPortHeap) - { - RtlDestroyHeap(PhSvcClPortHeap); - PhSvcClPortHeap = NULL; - } - - if (PhSvcClPortHandle) - { - NtClose(PhSvcClPortHandle); - PhSvcClPortHandle = NULL; - } - - PhSvcClServerProcessId = NULL; -} - -PVOID PhSvcpAllocateHeap( - _In_ SIZE_T Size, - _Out_ PULONG Offset - ) -{ - PVOID memory; - - if (!PhSvcClPortHeap) - return NULL; - - memory = RtlAllocateHeap(PhSvcClPortHeap, 0, Size); - - if (!memory) - return NULL; - - *Offset = (ULONG)((ULONG_PTR)memory - (ULONG_PTR)PhSvcClPortHeap); - - return memory; -} - -VOID PhSvcpFreeHeap( - _In_ PVOID Memory - ) -{ - if (!PhSvcClPortHeap) - return; - - RtlFreeHeap(PhSvcClPortHeap, 0, Memory); -} - -PVOID PhSvcpCreateString( - _In_opt_ PVOID String, - _In_ SIZE_T Length, - _Out_ PPH_RELATIVE_STRINGREF StringRef - ) -{ - PVOID memory; - SIZE_T length; - ULONG offset; - - if (Length != -1) - length = Length; - else - length = PhCountStringZ(String) * sizeof(WCHAR); - - if (length > MAXULONG32) - return NULL; - - memory = PhSvcpAllocateHeap(length, &offset); - - if (!memory) - return NULL; - - if (String) - memcpy(memory, String, length); - - StringRef->Length = (ULONG)length; - StringRef->Offset = offset; - - return memory; -} - -NTSTATUS PhSvcpCallServer( - _Inout_ PPHSVC_API_MSG Message - ) -{ - NTSTATUS status; - - Message->h.u1.s1.DataLength = sizeof(PHSVC_API_MSG) - FIELD_OFFSET(PHSVC_API_MSG, p); - Message->h.u1.s1.TotalLength = sizeof(PHSVC_API_MSG); - Message->h.u2.ZeroInit = 0; - - status = NtRequestWaitReplyPort(PhSvcClPortHandle, &Message->h, &Message->h); - - if (!NT_SUCCESS(status)) - return status; - - return Message->p.ReturnStatus; -} - -NTSTATUS PhSvcCallPlugin( - _In_ PPH_STRINGREF ApiId, - _In_reads_bytes_opt_(InLength) PVOID InBuffer, - _In_ ULONG InLength, - _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, - _In_ ULONG OutLength - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID apiId = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - if (InLength > sizeof(m.p.u.Plugin.i.Data)) - return STATUS_BUFFER_OVERFLOW; - - m.p.ApiNumber = PhSvcPluginApiNumber; - - if (!(apiId = PhSvcpCreateString(ApiId->Buffer, ApiId->Length, &m.p.u.Plugin.i.ApiId))) - return STATUS_NO_MEMORY; - - if (InLength != 0) - memcpy(m.p.u.Plugin.i.Data, InBuffer, InLength); - - status = PhSvcpCallServer(&m); - - if (OutLength != 0) - memcpy(OutBuffer, m.p.u.Plugin.o.Data, min(OutLength, sizeof(m.p.u.Plugin.o.Data))); - - if (apiId) - PhSvcpFreeHeap(apiId); - - return status; -} - -NTSTATUS PhSvcpCallExecuteRunAsCommand( - _In_ PHSVC_API_NUMBER ApiNumber, - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID userName = NULL; - PVOID password = NULL; - ULONG passwordLength; - PVOID currentDirectory = NULL; - PVOID commandLine = NULL; - PVOID fileName = NULL; - PVOID desktopName = NULL; - PVOID serviceName = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = ApiNumber; - - m.p.u.ExecuteRunAsCommand.i.ProcessId = Parameters->ProcessId; - m.p.u.ExecuteRunAsCommand.i.LogonType = Parameters->LogonType; - m.p.u.ExecuteRunAsCommand.i.SessionId = Parameters->SessionId; - m.p.u.ExecuteRunAsCommand.i.UseLinkedToken = Parameters->UseLinkedToken; - - status = STATUS_NO_MEMORY; - - if (Parameters->UserName && !(userName = PhSvcpCreateString(Parameters->UserName, -1, &m.p.u.ExecuteRunAsCommand.i.UserName))) - goto CleanupExit; - - if (Parameters->Password) - { - if (!(password = PhSvcpCreateString(Parameters->Password, -1, &m.p.u.ExecuteRunAsCommand.i.Password))) - goto CleanupExit; - - passwordLength = m.p.u.ExecuteRunAsCommand.i.Password.Length; - } - - if (Parameters->CurrentDirectory && !(currentDirectory = PhSvcpCreateString(Parameters->CurrentDirectory, -1, &m.p.u.ExecuteRunAsCommand.i.CurrentDirectory))) - goto CleanupExit; - if (Parameters->CommandLine && !(commandLine = PhSvcpCreateString(Parameters->CommandLine, -1, &m.p.u.ExecuteRunAsCommand.i.CommandLine))) - goto CleanupExit; - if (Parameters->FileName && !(fileName = PhSvcpCreateString(Parameters->FileName, -1, &m.p.u.ExecuteRunAsCommand.i.FileName))) - goto CleanupExit; - if (Parameters->DesktopName && !(desktopName = PhSvcpCreateString(Parameters->DesktopName, -1, &m.p.u.ExecuteRunAsCommand.i.DesktopName))) - goto CleanupExit; - if (Parameters->ServiceName && !(serviceName = PhSvcpCreateString(Parameters->ServiceName, -1, &m.p.u.ExecuteRunAsCommand.i.ServiceName))) - goto CleanupExit; - - status = PhSvcpCallServer(&m); - -CleanupExit: - if (serviceName) PhSvcpFreeHeap(serviceName); - if (desktopName) PhSvcpFreeHeap(desktopName); - if (fileName) PhSvcpFreeHeap(fileName); - if (commandLine) PhSvcpFreeHeap(commandLine); - if (currentDirectory) PhSvcpFreeHeap(currentDirectory); - - if (password) - { - RtlSecureZeroMemory(password, passwordLength); - PhSvcpFreeHeap(password); - } - - if (userName) PhSvcpFreeHeap(userName); - - return status; -} - -NTSTATUS PhSvcCallExecuteRunAsCommand( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ) -{ - return PhSvcpCallExecuteRunAsCommand(PhSvcExecuteRunAsCommandApiNumber, Parameters); -} - -NTSTATUS PhSvcCallUnloadDriver( - _In_opt_ PVOID BaseAddress, - _In_opt_ PWSTR Name - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID name = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcUnloadDriverApiNumber; - - m.p.u.UnloadDriver.i.BaseAddress = BaseAddress; - - if (Name) - { - name = PhSvcpCreateString(Name, -1, &m.p.u.UnloadDriver.i.Name); - - if (!name) - return STATUS_NO_MEMORY; - } - - status = PhSvcpCallServer(&m); - - if (name) - PhSvcpFreeHeap(name); - - return status; -} - -NTSTATUS PhSvcCallControlProcess( - _In_ HANDLE ProcessId, - _In_ PHSVC_API_CONTROLPROCESS_COMMAND Command, - _In_ ULONG Argument - ) -{ - PHSVC_API_MSG m; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcControlProcessApiNumber; - m.p.u.ControlProcess.i.ProcessId = ProcessId; - m.p.u.ControlProcess.i.Command = Command; - m.p.u.ControlProcess.i.Argument = Argument; - - return PhSvcpCallServer(&m); -} - -NTSTATUS PhSvcCallControlService( - _In_ PWSTR ServiceName, - _In_ PHSVC_API_CONTROLSERVICE_COMMAND Command - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID serviceName; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcControlServiceApiNumber; - - serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ControlService.i.ServiceName); - m.p.u.ControlService.i.Command = Command; - - if (serviceName) - { - status = PhSvcpCallServer(&m); - } - else - { - status = STATUS_NO_MEMORY; - } - - if (serviceName) - PhSvcpFreeHeap(serviceName); - - return status; -} - -NTSTATUS PhSvcCallCreateService( - _In_ PWSTR ServiceName, - _In_opt_ PWSTR DisplayName, - _In_ ULONG ServiceType, - _In_ ULONG StartType, - _In_ ULONG ErrorControl, - _In_opt_ PWSTR BinaryPathName, - _In_opt_ PWSTR LoadOrderGroup, - _Out_opt_ PULONG TagId, - _In_opt_ PWSTR Dependencies, - _In_opt_ PWSTR ServiceStartName, - _In_opt_ PWSTR Password - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID serviceName = NULL; - PVOID displayName = NULL; - PVOID binaryPathName = NULL; - PVOID loadOrderGroup = NULL; - PVOID dependencies = NULL; - PVOID serviceStartName = NULL; - PVOID password = NULL; - ULONG passwordLength; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcCreateServiceApiNumber; - - m.p.u.CreateService.i.ServiceType = ServiceType; - m.p.u.CreateService.i.StartType = StartType; - m.p.u.CreateService.i.ErrorControl = ErrorControl; - m.p.u.CreateService.i.TagIdSpecified = TagId != NULL; - - status = STATUS_NO_MEMORY; - - if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.CreateService.i.ServiceName))) - goto CleanupExit; - if (DisplayName && !(displayName = PhSvcpCreateString(DisplayName, -1, &m.p.u.CreateService.i.DisplayName))) - goto CleanupExit; - if (BinaryPathName && !(binaryPathName = PhSvcpCreateString(BinaryPathName, -1, &m.p.u.CreateService.i.BinaryPathName))) - goto CleanupExit; - if (LoadOrderGroup && !(loadOrderGroup = PhSvcpCreateString(LoadOrderGroup, -1, &m.p.u.CreateService.i.LoadOrderGroup))) - goto CleanupExit; - - if (Dependencies) - { - SIZE_T dependenciesLength; - SIZE_T partCount; - PWSTR part; - - dependenciesLength = sizeof(WCHAR); - part = Dependencies; - - do - { - partCount = PhCountStringZ(part) + 1; - part += partCount; - dependenciesLength += partCount * sizeof(WCHAR); - } while (partCount != 1); // stop at empty dependency part - - if (!(dependencies = PhSvcpCreateString(Dependencies, dependenciesLength, &m.p.u.CreateService.i.Dependencies))) - goto CleanupExit; - } - - if (ServiceStartName && !(serviceStartName = PhSvcpCreateString(ServiceStartName, -1, &m.p.u.CreateService.i.ServiceStartName))) - goto CleanupExit; - - if (Password) - { - if (!(password = PhSvcpCreateString(Password, -1, &m.p.u.CreateService.i.Password))) - goto CleanupExit; - - passwordLength = m.p.u.CreateService.i.Password.Length; - } - - status = PhSvcpCallServer(&m); - - if (NT_SUCCESS(status)) - { - if (TagId) - *TagId = m.p.u.CreateService.o.TagId; - } - -CleanupExit: - if (password) - { - RtlSecureZeroMemory(password, passwordLength); - PhSvcpFreeHeap(password); - } - - if (serviceStartName) PhSvcpFreeHeap(serviceStartName); - if (dependencies) PhSvcpFreeHeap(dependencies); - if (loadOrderGroup) PhSvcpFreeHeap(loadOrderGroup); - if (binaryPathName) PhSvcpFreeHeap(binaryPathName); - if (displayName) PhSvcpFreeHeap(displayName); - if (serviceName) PhSvcpFreeHeap(serviceName); - - return status; -} - -NTSTATUS PhSvcCallChangeServiceConfig( - _In_ PWSTR ServiceName, - _In_ ULONG ServiceType, - _In_ ULONG StartType, - _In_ ULONG ErrorControl, - _In_opt_ PWSTR BinaryPathName, - _In_opt_ PWSTR LoadOrderGroup, - _Out_opt_ PULONG TagId, - _In_opt_ PWSTR Dependencies, - _In_opt_ PWSTR ServiceStartName, - _In_opt_ PWSTR Password, - _In_opt_ PWSTR DisplayName - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID serviceName = NULL; - PVOID binaryPathName = NULL; - PVOID loadOrderGroup = NULL; - PVOID dependencies = NULL; - PVOID serviceStartName = NULL; - PVOID password = NULL; - ULONG passwordLength; - PVOID displayName = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcChangeServiceConfigApiNumber; - - m.p.u.ChangeServiceConfig.i.ServiceType = ServiceType; - m.p.u.ChangeServiceConfig.i.StartType = StartType; - m.p.u.ChangeServiceConfig.i.ErrorControl = ErrorControl; - m.p.u.ChangeServiceConfig.i.TagIdSpecified = TagId != NULL; - - status = STATUS_NO_MEMORY; - - if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ChangeServiceConfig.i.ServiceName))) - goto CleanupExit; - if (BinaryPathName && !(binaryPathName = PhSvcpCreateString(BinaryPathName, -1, &m.p.u.ChangeServiceConfig.i.BinaryPathName))) - goto CleanupExit; - if (LoadOrderGroup && !(loadOrderGroup = PhSvcpCreateString(LoadOrderGroup, -1, &m.p.u.ChangeServiceConfig.i.LoadOrderGroup))) - goto CleanupExit; - - if (Dependencies) - { - SIZE_T dependenciesLength; - SIZE_T partCount; - PWSTR part; - - dependenciesLength = sizeof(WCHAR); - part = Dependencies; - - do - { - partCount = PhCountStringZ(part) + 1; - part += partCount; - dependenciesLength += partCount * sizeof(WCHAR); - } while (partCount != 1); // stop at empty dependency part - - if (!(dependencies = PhSvcpCreateString(Dependencies, dependenciesLength, &m.p.u.ChangeServiceConfig.i.Dependencies))) - goto CleanupExit; - } - - if (ServiceStartName && !(serviceStartName = PhSvcpCreateString(ServiceStartName, -1, &m.p.u.ChangeServiceConfig.i.ServiceStartName))) - goto CleanupExit; - - if (Password) - { - if (!(password = PhSvcpCreateString(Password, -1, &m.p.u.ChangeServiceConfig.i.Password))) - goto CleanupExit; - - passwordLength = m.p.u.ChangeServiceConfig.i.Password.Length; - } - - if (DisplayName && !(displayName = PhSvcpCreateString(DisplayName, -1, &m.p.u.ChangeServiceConfig.i.DisplayName))) - goto CleanupExit; - - status = PhSvcpCallServer(&m); - - if (NT_SUCCESS(status)) - { - if (TagId) - *TagId = m.p.u.ChangeServiceConfig.o.TagId; - } - -CleanupExit: - if (displayName) PhSvcpFreeHeap(displayName); - - if (password) - { - RtlSecureZeroMemory(password, passwordLength); - PhSvcpFreeHeap(password); - } - - if (serviceStartName) PhSvcpFreeHeap(serviceStartName); - if (dependencies) PhSvcpFreeHeap(dependencies); - if (loadOrderGroup) PhSvcpFreeHeap(loadOrderGroup); - if (binaryPathName) PhSvcpFreeHeap(binaryPathName); - if (serviceName) PhSvcpFreeHeap(serviceName); - - return status; -} - -PVOID PhSvcpPackRoot( - _Inout_ PPH_BYTES_BUILDER BytesBuilder, - _In_ PVOID Buffer, - _In_ SIZE_T Length - ) -{ - return PhAppendBytesBuilderEx(BytesBuilder, Buffer, Length, sizeof(ULONG_PTR), NULL); -} - -VOID PhSvcpPackBuffer_V( - _Inout_ PPH_BYTES_BUILDER BytesBuilder, - _Inout_ PVOID *PointerInBytesBuilder, - _In_ SIZE_T Length, - _In_ SIZE_T Alignment, - _In_ ULONG NumberOfPointersToRebase, - _In_ va_list ArgPtr - ) -{ - va_list argptr; - ULONG_PTR oldBase; - SIZE_T oldLength; - ULONG_PTR newBase; - SIZE_T offset; - ULONG i; - PVOID *pointer; - - oldBase = (ULONG_PTR)BytesBuilder->Bytes->Buffer; - oldLength = BytesBuilder->Bytes->Length; - assert((ULONG_PTR)PointerInBytesBuilder >= oldBase && (ULONG_PTR)PointerInBytesBuilder + sizeof(PVOID) <= oldBase + oldLength); - - if (!*PointerInBytesBuilder) - return; - - PhAppendBytesBuilderEx(BytesBuilder, *PointerInBytesBuilder, Length, Alignment, &offset); - newBase = (ULONG_PTR)BytesBuilder->Bytes->Buffer; - - PointerInBytesBuilder = (PVOID *)((ULONG_PTR)PointerInBytesBuilder - oldBase + newBase); - *PointerInBytesBuilder = (PVOID)offset; - - argptr = ArgPtr; - - for (i = 0; i < NumberOfPointersToRebase; i++) - { - pointer = va_arg(argptr, PVOID *); - assert(!*pointer || ((ULONG_PTR)*pointer >= oldBase && (ULONG_PTR)*pointer + sizeof(PVOID) <= oldBase + oldLength)); - - if (*pointer) - *pointer = (PVOID)((ULONG_PTR)*pointer - oldBase + newBase); - } -} - -VOID PhSvcpPackBuffer( - _Inout_ PPH_BYTES_BUILDER BytesBuilder, - _Inout_ PVOID *PointerInBytesBuilder, - _In_ SIZE_T Length, - _In_ SIZE_T Alignment, - _In_ ULONG NumberOfPointersToRebase, - ... - ) -{ - va_list argptr; - - va_start(argptr, NumberOfPointersToRebase); - PhSvcpPackBuffer_V(BytesBuilder, PointerInBytesBuilder, Length, Alignment, NumberOfPointersToRebase, argptr); -} - -SIZE_T PhSvcpBufferLengthStringZ( - _In_opt_ PWSTR String, - _In_ BOOLEAN Multi - ) -{ - SIZE_T length = 0; - - if (String) - { - if (Multi) - { - PWSTR part = String; - SIZE_T partCount; - - while (TRUE) - { - partCount = PhCountStringZ(part); - length += (partCount + 1) * sizeof(WCHAR); - - if (partCount == 0) - break; - - part += partCount + 1; - } - } - else - { - length = (PhCountStringZ(String) + 1) * sizeof(WCHAR); - } - } - - return length; -} - -NTSTATUS PhSvcCallChangeServiceConfig2( - _In_ PWSTR ServiceName, - _In_ ULONG InfoLevel, - _In_ PVOID Info - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID serviceName = NULL; - PVOID info = NULL; - PH_BYTES_BUILDER bb; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcChangeServiceConfig2ApiNumber; - - m.p.u.ChangeServiceConfig2.i.InfoLevel = InfoLevel; - - if (serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ChangeServiceConfig2.i.ServiceName)) - { - switch (InfoLevel) - { - case SERVICE_CONFIG_FAILURE_ACTIONS: - { - LPSERVICE_FAILURE_ACTIONS failureActions = Info; - LPSERVICE_FAILURE_ACTIONS packedFailureActions; - - PhInitializeBytesBuilder(&bb, 200); - packedFailureActions = PhSvcpPackRoot(&bb, failureActions, sizeof(SERVICE_FAILURE_ACTIONS)); - PhSvcpPackBuffer(&bb, &packedFailureActions->lpRebootMsg, PhSvcpBufferLengthStringZ(failureActions->lpRebootMsg, FALSE), sizeof(WCHAR), - 1, &packedFailureActions); - PhSvcpPackBuffer(&bb, &packedFailureActions->lpCommand, PhSvcpBufferLengthStringZ(failureActions->lpCommand, FALSE), sizeof(WCHAR), - 1, &packedFailureActions); - - if (failureActions->cActions != 0 && failureActions->lpsaActions) - { - PhSvcpPackBuffer(&bb, &packedFailureActions->lpsaActions, failureActions->cActions * sizeof(SC_ACTION), __alignof(SC_ACTION), - 1, &packedFailureActions); - } - - info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info); - PhDeleteBytesBuilder(&bb); - } - break; - case SERVICE_CONFIG_DELAYED_AUTO_START_INFO: - info = PhSvcpCreateString(Info, sizeof(SERVICE_DELAYED_AUTO_START_INFO), &m.p.u.ChangeServiceConfig2.i.Info); - break; - case SERVICE_CONFIG_FAILURE_ACTIONS_FLAG: - info = PhSvcpCreateString(Info, sizeof(SERVICE_FAILURE_ACTIONS_FLAG), &m.p.u.ChangeServiceConfig2.i.Info); - break; - case SERVICE_CONFIG_SERVICE_SID_INFO: - info = PhSvcpCreateString(Info, sizeof(SERVICE_SID_INFO), &m.p.u.ChangeServiceConfig2.i.Info); - break; - case SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO: - { - LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo = Info; - LPSERVICE_REQUIRED_PRIVILEGES_INFO packedRequiredPrivilegesInfo; - - PhInitializeBytesBuilder(&bb, 100); - packedRequiredPrivilegesInfo = PhSvcpPackRoot(&bb, requiredPrivilegesInfo, sizeof(SERVICE_REQUIRED_PRIVILEGES_INFO)); - PhSvcpPackBuffer(&bb, &packedRequiredPrivilegesInfo->pmszRequiredPrivileges, PhSvcpBufferLengthStringZ(requiredPrivilegesInfo->pmszRequiredPrivileges, TRUE), sizeof(WCHAR), - 1, &packedRequiredPrivilegesInfo); - - info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info); - PhDeleteBytesBuilder(&bb); - } - break; - case SERVICE_CONFIG_PRESHUTDOWN_INFO: - info = PhSvcpCreateString(Info, sizeof(SERVICE_PRESHUTDOWN_INFO), &m.p.u.ChangeServiceConfig2.i.Info); - break; - case SERVICE_CONFIG_TRIGGER_INFO: - { - PSERVICE_TRIGGER_INFO triggerInfo = Info; - PSERVICE_TRIGGER_INFO packedTriggerInfo; - ULONG i; - PSERVICE_TRIGGER packedTrigger; - ULONG j; - PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM packedDataItem; - ULONG alignment; - - PhInitializeBytesBuilder(&bb, 400); - packedTriggerInfo = PhSvcpPackRoot(&bb, triggerInfo, sizeof(SERVICE_TRIGGER_INFO)); - - if (triggerInfo->cTriggers != 0 && triggerInfo->pTriggers) - { - PhSvcpPackBuffer(&bb, &packedTriggerInfo->pTriggers, triggerInfo->cTriggers * sizeof(SERVICE_TRIGGER), __alignof(SERVICE_TRIGGER), - 1, &packedTriggerInfo); - - for (i = 0; i < triggerInfo->cTriggers; i++) - { - packedTrigger = PhOffsetBytesBuilder(&bb, (SIZE_T)packedTriggerInfo->pTriggers + i * sizeof(SERVICE_TRIGGER)); - - PhSvcpPackBuffer(&bb, &packedTrigger->pTriggerSubtype, sizeof(GUID), __alignof(GUID), - 2, &packedTriggerInfo, &packedTrigger); - - if (packedTrigger->cDataItems != 0 && packedTrigger->pDataItems) - { - PhSvcpPackBuffer(&bb, &packedTrigger->pDataItems, packedTrigger->cDataItems * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), __alignof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), - 2, &packedTriggerInfo, &packedTrigger); - - for (j = 0; j < packedTrigger->cDataItems; j++) - { - packedDataItem = PhOffsetBytesBuilder(&bb, (SIZE_T)packedTrigger->pDataItems + j * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM)); - alignment = 1; - - switch (packedDataItem->dwDataType) - { - case SERVICE_TRIGGER_DATA_TYPE_BINARY: - case SERVICE_TRIGGER_DATA_TYPE_LEVEL: - alignment = sizeof(CHAR); - break; - case SERVICE_TRIGGER_DATA_TYPE_STRING: - alignment = sizeof(WCHAR); - break; - case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY: - case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL: - alignment = sizeof(ULONG64); - break; - } - - PhSvcpPackBuffer(&bb, &packedDataItem->pData, packedDataItem->cbData, alignment, - 3, &packedTriggerInfo, &packedTrigger, &packedDataItem); - } - } - } - } - - info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info); - PhDeleteBytesBuilder(&bb); - } - break; - case SERVICE_CONFIG_LAUNCH_PROTECTED: - info = PhSvcpCreateString(Info, sizeof(SERVICE_LAUNCH_PROTECTED_INFO), &m.p.u.ChangeServiceConfig2.i.Info); - break; - default: - status = STATUS_INVALID_PARAMETER; - break; - } - } - - if (serviceName && info) - { - status = PhSvcpCallServer(&m); - } - else - { - status = STATUS_NO_MEMORY; - } - - if (info) - PhSvcpFreeHeap(info); - if (serviceName) - PhSvcpFreeHeap(serviceName); - - return status; -} - -NTSTATUS PhSvcCallSetTcpEntry( - _In_ PVOID TcpRow - ) -{ - PHSVC_API_MSG m; - struct - { - DWORD dwState; - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwRemoteAddr; - DWORD dwRemotePort; - } *tcpRow = TcpRow; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcSetTcpEntryApiNumber; - - m.p.u.SetTcpEntry.i.State = tcpRow->dwState; - m.p.u.SetTcpEntry.i.LocalAddress = tcpRow->dwLocalAddr; - m.p.u.SetTcpEntry.i.LocalPort = tcpRow->dwLocalPort; - m.p.u.SetTcpEntry.i.RemoteAddress = tcpRow->dwRemoteAddr; - m.p.u.SetTcpEntry.i.RemotePort = tcpRow->dwRemotePort; - - return PhSvcpCallServer(&m); -} - -NTSTATUS PhSvcCallControlThread( - _In_ HANDLE ThreadId, - _In_ PHSVC_API_CONTROLTHREAD_COMMAND Command, - _In_ ULONG Argument - ) -{ - PHSVC_API_MSG m; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcControlThreadApiNumber; - m.p.u.ControlThread.i.ThreadId = ThreadId; - m.p.u.ControlThread.i.Command = Command; - m.p.u.ControlThread.i.Argument = Argument; - - return PhSvcpCallServer(&m); -} - -NTSTATUS PhSvcCallAddAccountRight( - _In_ PSID AccountSid, - _In_ PUNICODE_STRING UserRight - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID accountSid = NULL; - PVOID userRight = NULL; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcAddAccountRightApiNumber; - - status = STATUS_NO_MEMORY; - - if (!(accountSid = PhSvcpCreateString(AccountSid, RtlLengthSid(AccountSid), &m.p.u.AddAccountRight.i.AccountSid))) - goto CleanupExit; - if (!(userRight = PhSvcpCreateString(UserRight->Buffer, UserRight->Length, &m.p.u.AddAccountRight.i.UserRight))) - goto CleanupExit; - - status = PhSvcpCallServer(&m); - -CleanupExit: - if (userRight) PhSvcpFreeHeap(userRight); - if (accountSid) PhSvcpFreeHeap(accountSid); - - return status; -} - -NTSTATUS PhSvcCallInvokeRunAsService( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ) -{ - return PhSvcpCallExecuteRunAsCommand(PhSvcInvokeRunAsServiceApiNumber, Parameters); -} - -NTSTATUS PhSvcCallIssueMemoryListCommand( - _In_ SYSTEM_MEMORY_LIST_COMMAND Command - ) -{ - PHSVC_API_MSG m; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcIssueMemoryListCommandApiNumber; - m.p.u.IssueMemoryListCommand.i.Command = Command; - - return PhSvcpCallServer(&m); -} - -NTSTATUS PhSvcCallPostMessage( - _In_opt_ HWND hWnd, - _In_ UINT Msg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PHSVC_API_MSG m; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcPostMessageApiNumber; - m.p.u.PostMessage.i.hWnd = hWnd; - m.p.u.PostMessage.i.Msg = Msg; - m.p.u.PostMessage.i.wParam = wParam; - m.p.u.PostMessage.i.lParam = lParam; - - return PhSvcpCallServer(&m); -} - -NTSTATUS PhSvcCallSendMessage( - _In_opt_ HWND hWnd, - _In_ UINT Msg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PHSVC_API_MSG m; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcSendMessageApiNumber; - m.p.u.PostMessage.i.hWnd = hWnd; - m.p.u.PostMessage.i.Msg = Msg; - m.p.u.PostMessage.i.wParam = wParam; - m.p.u.PostMessage.i.lParam = lParam; - - return PhSvcpCallServer(&m); -} - -NTSTATUS PhSvcCallCreateProcessIgnoreIfeoDebugger( - _In_ PWSTR FileName - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID fileName = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber; - fileName = PhSvcpCreateString(FileName, -1, &m.p.u.CreateProcessIgnoreIfeoDebugger.i.FileName); - - if (!fileName) - return STATUS_NO_MEMORY; - - status = PhSvcpCallServer(&m); - - if (fileName) - PhSvcpFreeHeap(fileName); - - return status; -} - -PSECURITY_DESCRIPTOR PhpAbsoluteToSelfRelativeSD( - _In_ PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, - _Out_ PULONG BufferSize - ) -{ - NTSTATUS status; - ULONG bufferSize = 0; - PSECURITY_DESCRIPTOR selfRelativeSecurityDescriptor; - - status = RtlAbsoluteToSelfRelativeSD(AbsoluteSecurityDescriptor, NULL, &bufferSize); - - if (status != STATUS_BUFFER_TOO_SMALL) - return NULL; - - selfRelativeSecurityDescriptor = PhAllocate(bufferSize); - status = RtlAbsoluteToSelfRelativeSD(AbsoluteSecurityDescriptor, selfRelativeSecurityDescriptor, &bufferSize); - - if (!NT_SUCCESS(status)) - { - PhFree(selfRelativeSecurityDescriptor); - return NULL; - } - - *BufferSize = bufferSize; - - return selfRelativeSecurityDescriptor; -} - -NTSTATUS PhSvcCallSetServiceSecurity( - _In_ PWSTR ServiceName, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PSECURITY_DESCRIPTOR selfRelativeSecurityDescriptor = NULL; - ULONG bufferSize; - PVOID serviceName = NULL; - PVOID copiedSelfRelativeSecurityDescriptor = NULL; - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - selfRelativeSecurityDescriptor = PhpAbsoluteToSelfRelativeSD(SecurityDescriptor, &bufferSize); - - if (!selfRelativeSecurityDescriptor) - { - status = STATUS_BAD_DESCRIPTOR_FORMAT; - goto CleanupExit; - } - - m.p.ApiNumber = PhSvcSetServiceSecurityApiNumber; - m.p.u.SetServiceSecurity.i.SecurityInformation = SecurityInformation; - status = STATUS_NO_MEMORY; - - if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.SetServiceSecurity.i.ServiceName))) - goto CleanupExit; - if (!(copiedSelfRelativeSecurityDescriptor = PhSvcpCreateString(selfRelativeSecurityDescriptor, bufferSize, &m.p.u.SetServiceSecurity.i.SecurityDescriptor))) - goto CleanupExit; - - status = PhSvcpCallServer(&m); - -CleanupExit: - if (selfRelativeSecurityDescriptor) PhFree(selfRelativeSecurityDescriptor); - if (serviceName) PhSvcpFreeHeap(serviceName); - if (copiedSelfRelativeSecurityDescriptor) PhSvcpFreeHeap(copiedSelfRelativeSecurityDescriptor); - - return status; -} - -NTSTATUS PhSvcCallLoadDbgHelp( - _In_ PWSTR DbgHelpPath - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - PVOID dbgHelpPath = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - m.p.ApiNumber = PhSvcLoadDbgHelpApiNumber; - dbgHelpPath = PhSvcpCreateString(DbgHelpPath, -1, &m.p.u.LoadDbgHelp.i.DbgHelpPath); - - if (!dbgHelpPath) - return STATUS_NO_MEMORY; - - status = PhSvcpCallServer(&m); - - if (dbgHelpPath) - PhSvcpFreeHeap(dbgHelpPath); - - return status; -} - -NTSTATUS PhSvcCallWriteMiniDumpProcess( - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId, - _In_ HANDLE FileHandle, - _In_ ULONG DumpType - ) -{ - NTSTATUS status; - PHSVC_API_MSG m; - HANDLE serverHandle = NULL; - HANDLE remoteProcessHandle = NULL; - HANDLE remoteFileHandle = NULL; - - memset(&m, 0, sizeof(PHSVC_API_MSG)); - - if (!PhSvcClPortHandle) - return STATUS_PORT_DISCONNECTED; - - // For typical uses of this function, the client has more privileges than the server. - // We therefore duplicate our handles into the server's process. - - m.p.ApiNumber = PhSvcWriteMiniDumpProcessApiNumber; - - if (!NT_SUCCESS(status = PhOpenProcess(&serverHandle, PROCESS_DUP_HANDLE, PhSvcClServerProcessId))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(status = NtDuplicateObject(NtCurrentProcess(), ProcessHandle, serverHandle, &remoteProcessHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, 0))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(status = NtDuplicateObject(NtCurrentProcess(), FileHandle, serverHandle, &remoteFileHandle, - FILE_GENERIC_WRITE, 0, 0))) - { - goto CleanupExit; - } - - m.p.u.WriteMiniDumpProcess.i.LocalProcessHandle = HandleToUlong(remoteProcessHandle); - m.p.u.WriteMiniDumpProcess.i.ProcessId = HandleToUlong(ProcessId); - m.p.u.WriteMiniDumpProcess.i.LocalFileHandle = HandleToUlong(remoteFileHandle); - m.p.u.WriteMiniDumpProcess.i.DumpType = DumpType; - - status = PhSvcpCallServer(&m); - -CleanupExit: - if (serverHandle) - { - if (remoteProcessHandle) - NtDuplicateObject(serverHandle, remoteProcessHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); - if (remoteFileHandle) - NtDuplicateObject(serverHandle, remoteFileHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); - - NtClose(serverHandle); - } - - return status; -} +/* + * Process Hacker - + * phsvc client + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +HANDLE PhSvcClPortHandle; +PVOID PhSvcClPortHeap; +HANDLE PhSvcClServerProcessId; + +NTSTATUS PhSvcConnectToServer( + _In_ PUNICODE_STRING PortName, + _In_opt_ SIZE_T PortSectionSize + ) +{ + NTSTATUS status; + HANDLE sectionHandle; + LARGE_INTEGER sectionSize; + PORT_VIEW clientView; + REMOTE_PORT_VIEW serverView; + SECURITY_QUALITY_OF_SERVICE securityQos; + ULONG maxMessageLength; + PHSVC_API_CONNECTINFO connectInfo; + ULONG connectInfoLength; + + if (PhSvcClPortHandle) + return STATUS_ADDRESS_ALREADY_EXISTS; + + if (PortSectionSize == 0) + PortSectionSize = 512 * 1024; + + // Create the port section and connect to the port. + + sectionSize.QuadPart = PortSectionSize; + status = NtCreateSection( + §ionHandle, + SECTION_ALL_ACCESS, + NULL, + §ionSize, + PAGE_READWRITE, + SEC_COMMIT, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + clientView.Length = sizeof(PORT_VIEW); + clientView.SectionHandle = sectionHandle; + clientView.SectionOffset = 0; + clientView.ViewSize = PortSectionSize; + clientView.ViewBase = NULL; + clientView.ViewRemoteBase = NULL; + + serverView.Length = sizeof(REMOTE_PORT_VIEW); + serverView.ViewSize = 0; + serverView.ViewBase = NULL; + + securityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + securityQos.ImpersonationLevel = SecurityImpersonation; + securityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + securityQos.EffectiveOnly = TRUE; + + connectInfoLength = sizeof(PHSVC_API_CONNECTINFO); + + status = NtConnectPort( + &PhSvcClPortHandle, + PortName, + &securityQos, + &clientView, + &serverView, + &maxMessageLength, + &connectInfo, + &connectInfoLength + ); + NtClose(sectionHandle); + + if (!NT_SUCCESS(status)) + return status; + + PhSvcClServerProcessId = UlongToHandle(connectInfo.ServerProcessId); + + // Create the port heap. + + PhSvcClPortHeap = RtlCreateHeap( + HEAP_CLASS_1, + clientView.ViewBase, + clientView.ViewSize, + PAGE_SIZE, + NULL, + NULL + ); + + if (!PhSvcClPortHeap) + { + NtClose(PhSvcClPortHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlSetHeapInformation( + PhSvcClPortHeap, + HeapCompatibilityInformation, + &(ULONG){ HEAP_COMPATIBILITY_LFH }, + sizeof(ULONG) + ); + + return status; +} + +VOID PhSvcDisconnectFromServer( + VOID + ) +{ + if (PhSvcClPortHeap) + { + RtlDestroyHeap(PhSvcClPortHeap); + PhSvcClPortHeap = NULL; + } + + if (PhSvcClPortHandle) + { + NtClose(PhSvcClPortHandle); + PhSvcClPortHandle = NULL; + } + + PhSvcClServerProcessId = NULL; +} + +PVOID PhSvcpAllocateHeap( + _In_ SIZE_T Size, + _Out_ PULONG Offset + ) +{ + PVOID memory; + + if (!PhSvcClPortHeap) + return NULL; + + memory = RtlAllocateHeap(PhSvcClPortHeap, 0, Size); + + if (!memory) + return NULL; + + *Offset = PtrToUlong(PTR_SUB_OFFSET(memory, PhSvcClPortHeap)); + + return memory; +} + +VOID PhSvcpFreeHeap( + _In_ PVOID Memory + ) +{ + if (!PhSvcClPortHeap) + return; + + RtlFreeHeap(PhSvcClPortHeap, 0, Memory); +} + +PVOID PhSvcpCreateString( + _In_opt_ PVOID String, + _In_ SIZE_T Length, + _Out_ PPH_RELATIVE_STRINGREF StringRef + ) +{ + PVOID memory; + SIZE_T length; + ULONG offset; + + if (Length != -1) + length = Length; + else + length = PhCountStringZ(String) * sizeof(WCHAR); + + if (length > MAXULONG32) + return NULL; + + memory = PhSvcpAllocateHeap(length, &offset); + + if (!memory) + return NULL; + + if (String) + memcpy(memory, String, length); + + StringRef->Length = (ULONG)length; + StringRef->Offset = offset; + + return memory; +} + +NTSTATUS PhSvcpCallServer( + _Inout_ PPHSVC_API_MSG Message + ) +{ + NTSTATUS status; + + Message->h.u1.s1.DataLength = sizeof(PHSVC_API_MSG) - FIELD_OFFSET(PHSVC_API_MSG, p); + Message->h.u1.s1.TotalLength = sizeof(PHSVC_API_MSG); + Message->h.u2.ZeroInit = 0; + + status = NtRequestWaitReplyPort(PhSvcClPortHandle, &Message->h, &Message->h); + + if (!NT_SUCCESS(status)) + return status; + + return Message->p.ReturnStatus; +} + +NTSTATUS PhSvcCallPlugin( + _In_ PPH_STRINGREF ApiId, + _In_reads_bytes_opt_(InLength) PVOID InBuffer, + _In_ ULONG InLength, + _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, + _In_ ULONG OutLength + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID apiId = NULL; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + if (InLength > sizeof(m.p.u.Plugin.i.Data)) + return STATUS_BUFFER_OVERFLOW; + + m.p.ApiNumber = PhSvcPluginApiNumber; + + if (!(apiId = PhSvcpCreateString(ApiId->Buffer, ApiId->Length, &m.p.u.Plugin.i.ApiId))) + return STATUS_NO_MEMORY; + + if (InLength != 0) + memcpy(m.p.u.Plugin.i.Data, InBuffer, InLength); + + status = PhSvcpCallServer(&m); + + if (OutLength != 0) + memcpy(OutBuffer, m.p.u.Plugin.o.Data, min(OutLength, sizeof(m.p.u.Plugin.o.Data))); + + if (apiId) + PhSvcpFreeHeap(apiId); + + return status; +} + +NTSTATUS PhSvcpCallExecuteRunAsCommand( + _In_ PHSVC_API_NUMBER ApiNumber, + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID userName = NULL; + PVOID password = NULL; + ULONG passwordLength; + PVOID currentDirectory = NULL; + PVOID commandLine = NULL; + PVOID fileName = NULL; + PVOID desktopName = NULL; + PVOID serviceName = NULL; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = ApiNumber; + + m.p.u.ExecuteRunAsCommand.i.ProcessId = Parameters->ProcessId; + m.p.u.ExecuteRunAsCommand.i.LogonType = Parameters->LogonType; + m.p.u.ExecuteRunAsCommand.i.SessionId = Parameters->SessionId; + m.p.u.ExecuteRunAsCommand.i.UseLinkedToken = Parameters->UseLinkedToken; + m.p.u.ExecuteRunAsCommand.i.CreateSuspendedProcess = Parameters->CreateSuspendedProcess; + + status = STATUS_NO_MEMORY; + + if (Parameters->UserName && !(userName = PhSvcpCreateString(Parameters->UserName, -1, &m.p.u.ExecuteRunAsCommand.i.UserName))) + goto CleanupExit; + + if (Parameters->Password) + { + if (!(password = PhSvcpCreateString(Parameters->Password, -1, &m.p.u.ExecuteRunAsCommand.i.Password))) + goto CleanupExit; + + passwordLength = m.p.u.ExecuteRunAsCommand.i.Password.Length; + } + + if (Parameters->CurrentDirectory && !(currentDirectory = PhSvcpCreateString(Parameters->CurrentDirectory, -1, &m.p.u.ExecuteRunAsCommand.i.CurrentDirectory))) + goto CleanupExit; + if (Parameters->CommandLine && !(commandLine = PhSvcpCreateString(Parameters->CommandLine, -1, &m.p.u.ExecuteRunAsCommand.i.CommandLine))) + goto CleanupExit; + if (Parameters->FileName && !(fileName = PhSvcpCreateString(Parameters->FileName, -1, &m.p.u.ExecuteRunAsCommand.i.FileName))) + goto CleanupExit; + if (Parameters->DesktopName && !(desktopName = PhSvcpCreateString(Parameters->DesktopName, -1, &m.p.u.ExecuteRunAsCommand.i.DesktopName))) + goto CleanupExit; + if (Parameters->ServiceName && !(serviceName = PhSvcpCreateString(Parameters->ServiceName, -1, &m.p.u.ExecuteRunAsCommand.i.ServiceName))) + goto CleanupExit; + + status = PhSvcpCallServer(&m); + +CleanupExit: + if (serviceName) PhSvcpFreeHeap(serviceName); + if (desktopName) PhSvcpFreeHeap(desktopName); + if (fileName) PhSvcpFreeHeap(fileName); + if (commandLine) PhSvcpFreeHeap(commandLine); + if (currentDirectory) PhSvcpFreeHeap(currentDirectory); + + if (password) + { + RtlSecureZeroMemory(password, passwordLength); + PhSvcpFreeHeap(password); + } + + if (userName) PhSvcpFreeHeap(userName); + + return status; +} + +NTSTATUS PhSvcCallExecuteRunAsCommand( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ) +{ + return PhSvcpCallExecuteRunAsCommand(PhSvcExecuteRunAsCommandApiNumber, Parameters); +} + +NTSTATUS PhSvcCallUnloadDriver( + _In_opt_ PVOID BaseAddress, + _In_opt_ PWSTR Name + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID name = NULL; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcUnloadDriverApiNumber; + + m.p.u.UnloadDriver.i.BaseAddress = BaseAddress; + + if (Name) + { + name = PhSvcpCreateString(Name, -1, &m.p.u.UnloadDriver.i.Name); + + if (!name) + return STATUS_NO_MEMORY; + } + + status = PhSvcpCallServer(&m); + + if (name) + PhSvcpFreeHeap(name); + + return status; +} + +NTSTATUS PhSvcCallControlProcess( + _In_ HANDLE ProcessId, + _In_ PHSVC_API_CONTROLPROCESS_COMMAND Command, + _In_ ULONG Argument + ) +{ + PHSVC_API_MSG m; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcControlProcessApiNumber; + m.p.u.ControlProcess.i.ProcessId = ProcessId; + m.p.u.ControlProcess.i.Command = Command; + m.p.u.ControlProcess.i.Argument = Argument; + + return PhSvcpCallServer(&m); +} + +NTSTATUS PhSvcCallControlService( + _In_ PWSTR ServiceName, + _In_ PHSVC_API_CONTROLSERVICE_COMMAND Command + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID serviceName; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcControlServiceApiNumber; + + serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ControlService.i.ServiceName); + m.p.u.ControlService.i.Command = Command; + + if (serviceName) + { + status = PhSvcpCallServer(&m); + } + else + { + status = STATUS_NO_MEMORY; + } + + if (serviceName) + PhSvcpFreeHeap(serviceName); + + return status; +} + +NTSTATUS PhSvcCallCreateService( + _In_ PWSTR ServiceName, + _In_opt_ PWSTR DisplayName, + _In_ ULONG ServiceType, + _In_ ULONG StartType, + _In_ ULONG ErrorControl, + _In_opt_ PWSTR BinaryPathName, + _In_opt_ PWSTR LoadOrderGroup, + _Out_opt_ PULONG TagId, + _In_opt_ PWSTR Dependencies, + _In_opt_ PWSTR ServiceStartName, + _In_opt_ PWSTR Password + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID serviceName = NULL; + PVOID displayName = NULL; + PVOID binaryPathName = NULL; + PVOID loadOrderGroup = NULL; + PVOID dependencies = NULL; + PVOID serviceStartName = NULL; + PVOID password = NULL; + ULONG passwordLength; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcCreateServiceApiNumber; + + m.p.u.CreateService.i.ServiceType = ServiceType; + m.p.u.CreateService.i.StartType = StartType; + m.p.u.CreateService.i.ErrorControl = ErrorControl; + m.p.u.CreateService.i.TagIdSpecified = TagId != NULL; + + status = STATUS_NO_MEMORY; + + if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.CreateService.i.ServiceName))) + goto CleanupExit; + if (DisplayName && !(displayName = PhSvcpCreateString(DisplayName, -1, &m.p.u.CreateService.i.DisplayName))) + goto CleanupExit; + if (BinaryPathName && !(binaryPathName = PhSvcpCreateString(BinaryPathName, -1, &m.p.u.CreateService.i.BinaryPathName))) + goto CleanupExit; + if (LoadOrderGroup && !(loadOrderGroup = PhSvcpCreateString(LoadOrderGroup, -1, &m.p.u.CreateService.i.LoadOrderGroup))) + goto CleanupExit; + + if (Dependencies) + { + SIZE_T dependenciesLength; + SIZE_T partCount; + PWSTR part; + + dependenciesLength = sizeof(WCHAR); + part = Dependencies; + + do + { + partCount = PhCountStringZ(part) + 1; + part += partCount; + dependenciesLength += partCount * sizeof(WCHAR); + } while (partCount != 1); // stop at empty dependency part + + if (!(dependencies = PhSvcpCreateString(Dependencies, dependenciesLength, &m.p.u.CreateService.i.Dependencies))) + goto CleanupExit; + } + + if (ServiceStartName && !(serviceStartName = PhSvcpCreateString(ServiceStartName, -1, &m.p.u.CreateService.i.ServiceStartName))) + goto CleanupExit; + + if (Password) + { + if (!(password = PhSvcpCreateString(Password, -1, &m.p.u.CreateService.i.Password))) + goto CleanupExit; + + passwordLength = m.p.u.CreateService.i.Password.Length; + } + + status = PhSvcpCallServer(&m); + + if (NT_SUCCESS(status)) + { + if (TagId) + *TagId = m.p.u.CreateService.o.TagId; + } + +CleanupExit: + if (password) + { + RtlSecureZeroMemory(password, passwordLength); + PhSvcpFreeHeap(password); + } + + if (serviceStartName) PhSvcpFreeHeap(serviceStartName); + if (dependencies) PhSvcpFreeHeap(dependencies); + if (loadOrderGroup) PhSvcpFreeHeap(loadOrderGroup); + if (binaryPathName) PhSvcpFreeHeap(binaryPathName); + if (displayName) PhSvcpFreeHeap(displayName); + if (serviceName) PhSvcpFreeHeap(serviceName); + + return status; +} + +NTSTATUS PhSvcCallChangeServiceConfig( + _In_ PWSTR ServiceName, + _In_ ULONG ServiceType, + _In_ ULONG StartType, + _In_ ULONG ErrorControl, + _In_opt_ PWSTR BinaryPathName, + _In_opt_ PWSTR LoadOrderGroup, + _Out_opt_ PULONG TagId, + _In_opt_ PWSTR Dependencies, + _In_opt_ PWSTR ServiceStartName, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DisplayName + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID serviceName = NULL; + PVOID binaryPathName = NULL; + PVOID loadOrderGroup = NULL; + PVOID dependencies = NULL; + PVOID serviceStartName = NULL; + PVOID password = NULL; + ULONG passwordLength; + PVOID displayName = NULL; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcChangeServiceConfigApiNumber; + + m.p.u.ChangeServiceConfig.i.ServiceType = ServiceType; + m.p.u.ChangeServiceConfig.i.StartType = StartType; + m.p.u.ChangeServiceConfig.i.ErrorControl = ErrorControl; + m.p.u.ChangeServiceConfig.i.TagIdSpecified = TagId != NULL; + + status = STATUS_NO_MEMORY; + + if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ChangeServiceConfig.i.ServiceName))) + goto CleanupExit; + if (BinaryPathName && !(binaryPathName = PhSvcpCreateString(BinaryPathName, -1, &m.p.u.ChangeServiceConfig.i.BinaryPathName))) + goto CleanupExit; + if (LoadOrderGroup && !(loadOrderGroup = PhSvcpCreateString(LoadOrderGroup, -1, &m.p.u.ChangeServiceConfig.i.LoadOrderGroup))) + goto CleanupExit; + + if (Dependencies) + { + SIZE_T dependenciesLength; + SIZE_T partCount; + PWSTR part; + + dependenciesLength = sizeof(WCHAR); + part = Dependencies; + + do + { + partCount = PhCountStringZ(part) + 1; + part += partCount; + dependenciesLength += partCount * sizeof(WCHAR); + } while (partCount != 1); // stop at empty dependency part + + if (!(dependencies = PhSvcpCreateString(Dependencies, dependenciesLength, &m.p.u.ChangeServiceConfig.i.Dependencies))) + goto CleanupExit; + } + + if (ServiceStartName && !(serviceStartName = PhSvcpCreateString(ServiceStartName, -1, &m.p.u.ChangeServiceConfig.i.ServiceStartName))) + goto CleanupExit; + + if (Password) + { + if (!(password = PhSvcpCreateString(Password, -1, &m.p.u.ChangeServiceConfig.i.Password))) + goto CleanupExit; + + passwordLength = m.p.u.ChangeServiceConfig.i.Password.Length; + } + + if (DisplayName && !(displayName = PhSvcpCreateString(DisplayName, -1, &m.p.u.ChangeServiceConfig.i.DisplayName))) + goto CleanupExit; + + status = PhSvcpCallServer(&m); + + if (NT_SUCCESS(status)) + { + if (TagId) + *TagId = m.p.u.ChangeServiceConfig.o.TagId; + } + +CleanupExit: + if (displayName) PhSvcpFreeHeap(displayName); + + if (password) + { + RtlSecureZeroMemory(password, passwordLength); + PhSvcpFreeHeap(password); + } + + if (serviceStartName) PhSvcpFreeHeap(serviceStartName); + if (dependencies) PhSvcpFreeHeap(dependencies); + if (loadOrderGroup) PhSvcpFreeHeap(loadOrderGroup); + if (binaryPathName) PhSvcpFreeHeap(binaryPathName); + if (serviceName) PhSvcpFreeHeap(serviceName); + + return status; +} + +PVOID PhSvcpPackRoot( + _Inout_ PPH_BYTES_BUILDER BytesBuilder, + _In_ PVOID Buffer, + _In_ SIZE_T Length + ) +{ + return PhAppendBytesBuilderEx(BytesBuilder, Buffer, Length, sizeof(ULONG_PTR), NULL); +} + +VOID PhSvcpPackBuffer_V( + _Inout_ PPH_BYTES_BUILDER BytesBuilder, + _Inout_ PVOID *PointerInBytesBuilder, + _In_ SIZE_T Length, + _In_ SIZE_T Alignment, + _In_ ULONG NumberOfPointersToRebase, + _In_ va_list ArgPtr + ) +{ + va_list argptr; + ULONG_PTR oldBase; + SIZE_T oldLength; + ULONG_PTR newBase; + SIZE_T offset; + ULONG i; + PVOID *pointer; + + oldBase = (ULONG_PTR)BytesBuilder->Bytes->Buffer; + oldLength = BytesBuilder->Bytes->Length; + assert((ULONG_PTR)PointerInBytesBuilder >= oldBase && (ULONG_PTR)PointerInBytesBuilder + sizeof(PVOID) <= oldBase + oldLength); + + if (!*PointerInBytesBuilder) + return; + + PhAppendBytesBuilderEx(BytesBuilder, *PointerInBytesBuilder, Length, Alignment, &offset); + newBase = (ULONG_PTR)BytesBuilder->Bytes->Buffer; + + PointerInBytesBuilder = (PVOID *)((ULONG_PTR)PointerInBytesBuilder - oldBase + newBase); + *PointerInBytesBuilder = (PVOID)offset; + + argptr = ArgPtr; + + for (i = 0; i < NumberOfPointersToRebase; i++) + { + pointer = va_arg(argptr, PVOID *); + assert(!*pointer || ((ULONG_PTR)*pointer >= oldBase && (ULONG_PTR)*pointer + sizeof(PVOID) <= oldBase + oldLength)); + + if (*pointer) + *pointer = (PVOID)((ULONG_PTR)*pointer - oldBase + newBase); + } +} + +VOID PhSvcpPackBuffer( + _Inout_ PPH_BYTES_BUILDER BytesBuilder, + _Inout_ PVOID *PointerInBytesBuilder, + _In_ SIZE_T Length, + _In_ SIZE_T Alignment, + _In_ ULONG NumberOfPointersToRebase, + ... + ) +{ + va_list argptr; + + va_start(argptr, NumberOfPointersToRebase); + PhSvcpPackBuffer_V(BytesBuilder, PointerInBytesBuilder, Length, Alignment, NumberOfPointersToRebase, argptr); +} + +SIZE_T PhSvcpBufferLengthStringZ( + _In_opt_ PWSTR String, + _In_ BOOLEAN Multi + ) +{ + SIZE_T length = 0; + + if (String) + { + if (Multi) + { + PWSTR part = String; + SIZE_T partCount; + + while (TRUE) + { + partCount = PhCountStringZ(part); + length += (partCount + 1) * sizeof(WCHAR); + + if (partCount == 0) + break; + + part += partCount + 1; + } + } + else + { + length = (PhCountStringZ(String) + 1) * sizeof(WCHAR); + } + } + + return length; +} + +NTSTATUS PhSvcCallChangeServiceConfig2( + _In_ PWSTR ServiceName, + _In_ ULONG InfoLevel, + _In_ PVOID Info + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID serviceName = NULL; + PVOID info = NULL; + PH_BYTES_BUILDER bb; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcChangeServiceConfig2ApiNumber; + + m.p.u.ChangeServiceConfig2.i.InfoLevel = InfoLevel; + + if (serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ChangeServiceConfig2.i.ServiceName)) + { + switch (InfoLevel) + { + case SERVICE_CONFIG_FAILURE_ACTIONS: + { + LPSERVICE_FAILURE_ACTIONS failureActions = Info; + LPSERVICE_FAILURE_ACTIONS packedFailureActions; + + PhInitializeBytesBuilder(&bb, 200); + packedFailureActions = PhSvcpPackRoot(&bb, failureActions, sizeof(SERVICE_FAILURE_ACTIONS)); + PhSvcpPackBuffer(&bb, &packedFailureActions->lpRebootMsg, PhSvcpBufferLengthStringZ(failureActions->lpRebootMsg, FALSE), sizeof(WCHAR), + 1, &packedFailureActions); + PhSvcpPackBuffer(&bb, &packedFailureActions->lpCommand, PhSvcpBufferLengthStringZ(failureActions->lpCommand, FALSE), sizeof(WCHAR), + 1, &packedFailureActions); + + if (failureActions->cActions != 0 && failureActions->lpsaActions) + { + PhSvcpPackBuffer(&bb, &packedFailureActions->lpsaActions, failureActions->cActions * sizeof(SC_ACTION), __alignof(SC_ACTION), + 1, &packedFailureActions); + } + + info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info); + PhDeleteBytesBuilder(&bb); + } + break; + case SERVICE_CONFIG_DELAYED_AUTO_START_INFO: + info = PhSvcpCreateString(Info, sizeof(SERVICE_DELAYED_AUTO_START_INFO), &m.p.u.ChangeServiceConfig2.i.Info); + break; + case SERVICE_CONFIG_FAILURE_ACTIONS_FLAG: + info = PhSvcpCreateString(Info, sizeof(SERVICE_FAILURE_ACTIONS_FLAG), &m.p.u.ChangeServiceConfig2.i.Info); + break; + case SERVICE_CONFIG_SERVICE_SID_INFO: + info = PhSvcpCreateString(Info, sizeof(SERVICE_SID_INFO), &m.p.u.ChangeServiceConfig2.i.Info); + break; + case SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO: + { + LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo = Info; + LPSERVICE_REQUIRED_PRIVILEGES_INFO packedRequiredPrivilegesInfo; + + PhInitializeBytesBuilder(&bb, 100); + packedRequiredPrivilegesInfo = PhSvcpPackRoot(&bb, requiredPrivilegesInfo, sizeof(SERVICE_REQUIRED_PRIVILEGES_INFO)); + PhSvcpPackBuffer(&bb, &packedRequiredPrivilegesInfo->pmszRequiredPrivileges, PhSvcpBufferLengthStringZ(requiredPrivilegesInfo->pmszRequiredPrivileges, TRUE), sizeof(WCHAR), + 1, &packedRequiredPrivilegesInfo); + + info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info); + PhDeleteBytesBuilder(&bb); + } + break; + case SERVICE_CONFIG_PRESHUTDOWN_INFO: + info = PhSvcpCreateString(Info, sizeof(SERVICE_PRESHUTDOWN_INFO), &m.p.u.ChangeServiceConfig2.i.Info); + break; + case SERVICE_CONFIG_TRIGGER_INFO: + { + PSERVICE_TRIGGER_INFO triggerInfo = Info; + PSERVICE_TRIGGER_INFO packedTriggerInfo; + ULONG i; + PSERVICE_TRIGGER packedTrigger; + ULONG j; + PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM packedDataItem; + ULONG alignment; + + PhInitializeBytesBuilder(&bb, 400); + packedTriggerInfo = PhSvcpPackRoot(&bb, triggerInfo, sizeof(SERVICE_TRIGGER_INFO)); + + if (triggerInfo->cTriggers != 0 && triggerInfo->pTriggers) + { + PhSvcpPackBuffer(&bb, &packedTriggerInfo->pTriggers, triggerInfo->cTriggers * sizeof(SERVICE_TRIGGER), __alignof(SERVICE_TRIGGER), + 1, &packedTriggerInfo); + + for (i = 0; i < triggerInfo->cTriggers; i++) + { + packedTrigger = PhOffsetBytesBuilder(&bb, (SIZE_T)packedTriggerInfo->pTriggers + i * sizeof(SERVICE_TRIGGER)); + + PhSvcpPackBuffer(&bb, &packedTrigger->pTriggerSubtype, sizeof(GUID), __alignof(GUID), + 2, &packedTriggerInfo, &packedTrigger); + + if (packedTrigger->cDataItems != 0 && packedTrigger->pDataItems) + { + PhSvcpPackBuffer(&bb, &packedTrigger->pDataItems, packedTrigger->cDataItems * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), __alignof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), + 2, &packedTriggerInfo, &packedTrigger); + + for (j = 0; j < packedTrigger->cDataItems; j++) + { + packedDataItem = PhOffsetBytesBuilder(&bb, (SIZE_T)packedTrigger->pDataItems + j * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM)); + alignment = 1; + + switch (packedDataItem->dwDataType) + { + case SERVICE_TRIGGER_DATA_TYPE_BINARY: + case SERVICE_TRIGGER_DATA_TYPE_LEVEL: + alignment = sizeof(CHAR); + break; + case SERVICE_TRIGGER_DATA_TYPE_STRING: + alignment = sizeof(WCHAR); + break; + case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY: + case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL: + alignment = sizeof(ULONG64); + break; + } + + PhSvcpPackBuffer(&bb, &packedDataItem->pData, packedDataItem->cbData, alignment, + 3, &packedTriggerInfo, &packedTrigger, &packedDataItem); + } + } + } + } + + info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info); + PhDeleteBytesBuilder(&bb); + } + break; + case SERVICE_CONFIG_LAUNCH_PROTECTED: + info = PhSvcpCreateString(Info, sizeof(SERVICE_LAUNCH_PROTECTED_INFO), &m.p.u.ChangeServiceConfig2.i.Info); + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } + + if (serviceName && info) + { + status = PhSvcpCallServer(&m); + } + else + { + status = STATUS_NO_MEMORY; + } + + if (info) + PhSvcpFreeHeap(info); + if (serviceName) + PhSvcpFreeHeap(serviceName); + + return status; +} + +NTSTATUS PhSvcCallSetTcpEntry( + _In_ PVOID TcpRow + ) +{ + PHSVC_API_MSG m; + struct + { + ULONG dwState; + ULONG dwLocalAddr; + ULONG dwLocalPort; + ULONG dwRemoteAddr; + ULONG dwRemotePort; + } *tcpRow = TcpRow; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcSetTcpEntryApiNumber; + + m.p.u.SetTcpEntry.i.State = tcpRow->dwState; + m.p.u.SetTcpEntry.i.LocalAddress = tcpRow->dwLocalAddr; + m.p.u.SetTcpEntry.i.LocalPort = tcpRow->dwLocalPort; + m.p.u.SetTcpEntry.i.RemoteAddress = tcpRow->dwRemoteAddr; + m.p.u.SetTcpEntry.i.RemotePort = tcpRow->dwRemotePort; + + return PhSvcpCallServer(&m); +} + +NTSTATUS PhSvcCallControlThread( + _In_ HANDLE ThreadId, + _In_ PHSVC_API_CONTROLTHREAD_COMMAND Command, + _In_ ULONG Argument + ) +{ + PHSVC_API_MSG m; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcControlThreadApiNumber; + m.p.u.ControlThread.i.ThreadId = ThreadId; + m.p.u.ControlThread.i.Command = Command; + m.p.u.ControlThread.i.Argument = Argument; + + return PhSvcpCallServer(&m); +} + +NTSTATUS PhSvcCallAddAccountRight( + _In_ PSID AccountSid, + _In_ PUNICODE_STRING UserRight + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID accountSid = NULL; + PVOID userRight = NULL; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcAddAccountRightApiNumber; + + status = STATUS_NO_MEMORY; + + if (!(accountSid = PhSvcpCreateString(AccountSid, RtlLengthSid(AccountSid), &m.p.u.AddAccountRight.i.AccountSid))) + goto CleanupExit; + if (!(userRight = PhSvcpCreateString(UserRight->Buffer, UserRight->Length, &m.p.u.AddAccountRight.i.UserRight))) + goto CleanupExit; + + status = PhSvcpCallServer(&m); + +CleanupExit: + if (userRight) PhSvcpFreeHeap(userRight); + if (accountSid) PhSvcpFreeHeap(accountSid); + + return status; +} + +NTSTATUS PhSvcCallInvokeRunAsService( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ) +{ + return PhSvcpCallExecuteRunAsCommand(PhSvcInvokeRunAsServiceApiNumber, Parameters); +} + +NTSTATUS PhSvcCallIssueMemoryListCommand( + _In_ SYSTEM_MEMORY_LIST_COMMAND Command + ) +{ + PHSVC_API_MSG m; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcIssueMemoryListCommandApiNumber; + m.p.u.IssueMemoryListCommand.i.Command = Command; + + return PhSvcpCallServer(&m); +} + +NTSTATUS PhSvcCallPostMessage( + _In_opt_ HWND hWnd, + _In_ UINT Msg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PHSVC_API_MSG m; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcPostMessageApiNumber; + m.p.u.PostMessage.i.hWnd = hWnd; + m.p.u.PostMessage.i.Msg = Msg; + m.p.u.PostMessage.i.wParam = wParam; + m.p.u.PostMessage.i.lParam = lParam; + + return PhSvcpCallServer(&m); +} + +NTSTATUS PhSvcCallSendMessage( + _In_opt_ HWND hWnd, + _In_ UINT Msg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PHSVC_API_MSG m; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcSendMessageApiNumber; + m.p.u.PostMessage.i.hWnd = hWnd; + m.p.u.PostMessage.i.Msg = Msg; + m.p.u.PostMessage.i.wParam = wParam; + m.p.u.PostMessage.i.lParam = lParam; + + return PhSvcpCallServer(&m); +} + +NTSTATUS PhSvcCallCreateProcessIgnoreIfeoDebugger( + _In_ PWSTR FileName + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PVOID fileName = NULL; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + m.p.ApiNumber = PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber; + fileName = PhSvcpCreateString(FileName, -1, &m.p.u.CreateProcessIgnoreIfeoDebugger.i.FileName); + + if (!fileName) + return STATUS_NO_MEMORY; + + status = PhSvcpCallServer(&m); + + if (fileName) + PhSvcpFreeHeap(fileName); + + return status; +} + +PSECURITY_DESCRIPTOR PhpAbsoluteToSelfRelativeSD( + _In_ PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, + _Out_ PULONG BufferSize + ) +{ + NTSTATUS status; + ULONG bufferSize = 0; + PSECURITY_DESCRIPTOR selfRelativeSecurityDescriptor; + + status = RtlAbsoluteToSelfRelativeSD(AbsoluteSecurityDescriptor, NULL, &bufferSize); + + if (status != STATUS_BUFFER_TOO_SMALL) + return NULL; + + selfRelativeSecurityDescriptor = PhAllocate(bufferSize); + status = RtlAbsoluteToSelfRelativeSD(AbsoluteSecurityDescriptor, selfRelativeSecurityDescriptor, &bufferSize); + + if (!NT_SUCCESS(status)) + { + PhFree(selfRelativeSecurityDescriptor); + return NULL; + } + + *BufferSize = bufferSize; + + return selfRelativeSecurityDescriptor; +} + +NTSTATUS PhSvcCallSetServiceSecurity( + _In_ PWSTR ServiceName, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + PSECURITY_DESCRIPTOR selfRelativeSecurityDescriptor = NULL; + ULONG bufferSize; + PVOID serviceName = NULL; + PVOID copiedSelfRelativeSecurityDescriptor = NULL; + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + selfRelativeSecurityDescriptor = PhpAbsoluteToSelfRelativeSD(SecurityDescriptor, &bufferSize); + + if (!selfRelativeSecurityDescriptor) + { + status = STATUS_BAD_DESCRIPTOR_FORMAT; + goto CleanupExit; + } + + m.p.ApiNumber = PhSvcSetServiceSecurityApiNumber; + m.p.u.SetServiceSecurity.i.SecurityInformation = SecurityInformation; + status = STATUS_NO_MEMORY; + + if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.SetServiceSecurity.i.ServiceName))) + goto CleanupExit; + if (!(copiedSelfRelativeSecurityDescriptor = PhSvcpCreateString(selfRelativeSecurityDescriptor, bufferSize, &m.p.u.SetServiceSecurity.i.SecurityDescriptor))) + goto CleanupExit; + + status = PhSvcpCallServer(&m); + +CleanupExit: + if (selfRelativeSecurityDescriptor) PhFree(selfRelativeSecurityDescriptor); + if (serviceName) PhSvcpFreeHeap(serviceName); + if (copiedSelfRelativeSecurityDescriptor) PhSvcpFreeHeap(copiedSelfRelativeSecurityDescriptor); + + return status; +} + +NTSTATUS PhSvcCallWriteMiniDumpProcess( + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE FileHandle, + _In_ ULONG DumpType + ) +{ + NTSTATUS status; + PHSVC_API_MSG m; + HANDLE serverHandle = NULL; + HANDLE remoteProcessHandle = NULL; + HANDLE remoteFileHandle = NULL; + + memset(&m, 0, sizeof(PHSVC_API_MSG)); + + if (!PhSvcClPortHandle) + return STATUS_PORT_DISCONNECTED; + + // For typical uses of this function, the client has more privileges than the server. + // We therefore duplicate our handles into the server's process. + + m.p.ApiNumber = PhSvcWriteMiniDumpProcessApiNumber; + + status = PhOpenProcess( + &serverHandle, + PROCESS_DUP_HANDLE, + PhSvcClServerProcessId + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtDuplicateObject( + NtCurrentProcess(), + ProcessHandle, + serverHandle, + &remoteProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtDuplicateObject( + NtCurrentProcess(), + FileHandle, + serverHandle, + &remoteFileHandle, + FILE_GENERIC_WRITE, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + m.p.u.WriteMiniDumpProcess.i.LocalProcessHandle = HandleToUlong(remoteProcessHandle); + m.p.u.WriteMiniDumpProcess.i.ProcessId = HandleToUlong(ProcessId); + m.p.u.WriteMiniDumpProcess.i.LocalFileHandle = HandleToUlong(remoteFileHandle); + m.p.u.WriteMiniDumpProcess.i.DumpType = DumpType; + + status = PhSvcpCallServer(&m); + +CleanupExit: + if (serverHandle) + { + if (remoteProcessHandle) + NtDuplicateObject(serverHandle, remoteProcessHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); + if (remoteFileHandle) + NtDuplicateObject(serverHandle, remoteFileHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); + + NtClose(serverHandle); + } + + return status; +} diff --git a/ProcessHacker/phsvc/svcapi.c b/ProcessHacker/phsvc/svcapi.c index c18ca5045e44..ee88d2632758 100644 --- a/ProcessHacker/phsvc/svcapi.c +++ b/ProcessHacker/phsvc/svcapi.c @@ -1,1446 +1,1423 @@ -/* - * Process Hacker - - * server API - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include - -typedef struct _PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS -{ - PPH_STRING UserName; - PPH_STRING Password; - PPH_STRING CurrentDirectory; - PPH_STRING CommandLine; - PPH_STRING FileName; - PPH_STRING DesktopName; - PPH_STRING ServiceName; -} PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS, *PPHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS; - -PPHSVC_API_PROCEDURE PhSvcApiCallTable[] = -{ - PhSvcApiPlugin, - PhSvcApiExecuteRunAsCommand, - PhSvcApiUnloadDriver, - PhSvcApiControlProcess, - PhSvcApiControlService, - PhSvcApiCreateService, - PhSvcApiChangeServiceConfig, - PhSvcApiChangeServiceConfig2, - PhSvcApiSetTcpEntry, - PhSvcApiControlThread, - PhSvcApiAddAccountRight, - PhSvcApiInvokeRunAsService, - PhSvcApiIssueMemoryListCommand, - PhSvcApiPostMessage, - PhSvcApiSendMessage, - PhSvcApiCreateProcessIgnoreIfeoDebugger, - PhSvcApiSetServiceSecurity, - PhSvcApiLoadDbgHelp, - PhSvcApiWriteMiniDumpProcess -}; -C_ASSERT(sizeof(PhSvcApiCallTable) / sizeof(PPHSVC_API_PROCEDURE) == PhSvcMaximumApiNumber - 1); - -NTSTATUS PhSvcApiInitialization( - VOID - ) -{ - return STATUS_SUCCESS; -} - -VOID PhSvcDispatchApiCall( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload, - _Out_ PHANDLE ReplyPortHandle - ) -{ - NTSTATUS status; - - if ( - Payload->ApiNumber == 0 || - (ULONG)Payload->ApiNumber >= (ULONG)PhSvcMaximumApiNumber || - !PhSvcApiCallTable[Payload->ApiNumber - 1] - ) - { - Payload->ReturnStatus = STATUS_INVALID_SYSTEM_SERVICE; - *ReplyPortHandle = Client->PortHandle; - return; - } - - status = PhSvcApiCallTable[Payload->ApiNumber - 1](Client, Payload); - Payload->ReturnStatus = status; - - *ReplyPortHandle = Client->PortHandle; -} - -PVOID PhSvcValidateString( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ ULONG Alignment - ) -{ - PPHSVC_CLIENT client = PhSvcGetCurrentClient(); - PVOID address; - - address = (PCHAR)client->ClientViewBase + String->Offset; - - if ((ULONG_PTR)address + String->Length < (ULONG_PTR)address || - (ULONG_PTR)address < (ULONG_PTR)client->ClientViewBase || - (ULONG_PTR)address + String->Length > (ULONG_PTR)client->ClientViewLimit) - { - return NULL; - } - - if ((ULONG_PTR)address & (Alignment - 1)) - { - return NULL; - } - - return address; -} - -NTSTATUS PhSvcProbeBuffer( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ ULONG Alignment, - _In_ BOOLEAN AllowNull, - _Out_ PVOID *Pointer - ) -{ - PVOID address; - - if (String->Offset != 0) - { - address = PhSvcValidateString(String, Alignment); - - if (!address) - return STATUS_ACCESS_VIOLATION; - - *Pointer = address; - } - else - { - if (!AllowNull) - return STATUS_ACCESS_VIOLATION; - - *Pointer = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcCaptureBuffer( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PVOID *CapturedBuffer - ) -{ - PVOID address; - PVOID buffer; - - if (String->Offset != 0) - { - address = PhSvcValidateString(String, 1); - - if (!address) - return STATUS_ACCESS_VIOLATION; - - buffer = PhAllocateSafe(String->Length); - - if (!buffer) - return STATUS_NO_MEMORY; - - memcpy(buffer, address, String->Length); - *CapturedBuffer = buffer; - } - else - { - if (!AllowNull) - return STATUS_ACCESS_VIOLATION; - - *CapturedBuffer = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcCaptureString( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PPH_STRING *CapturedString - ) -{ - PVOID address; - - if (String->Length & 1) - return STATUS_INVALID_BUFFER_SIZE; - if (String->Length > 0xfffe) - return STATUS_INVALID_BUFFER_SIZE; - - if (String->Offset != 0) - { - address = PhSvcValidateString(String, sizeof(WCHAR)); - - if (!address) - return STATUS_ACCESS_VIOLATION; - - if (String->Length != 0) - *CapturedString = PhCreateStringEx(address, String->Length); - else - *CapturedString = PhReferenceEmptyString(); - } - else - { - if (!AllowNull) - return STATUS_ACCESS_VIOLATION; - - *CapturedString = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcCaptureSid( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _Out_ PSID *CapturedSid - ) -{ - NTSTATUS status; - PSID sid; - - if (!NT_SUCCESS(status = PhSvcCaptureBuffer(String, AllowNull, &sid))) - return status; - - if (sid) - { - if (String->Length < (ULONG)FIELD_OFFSET(struct _SID, IdentifierAuthority) || - String->Length < RtlLengthRequiredSid(((struct _SID *)sid)->SubAuthorityCount) || - !RtlValidSid(sid)) - { - PhFree(sid); - return STATUS_INVALID_SID; - } - - *CapturedSid = sid; - } - else - { - *CapturedSid = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcCaptureSecurityDescriptor( - _In_ PPH_RELATIVE_STRINGREF String, - _In_ BOOLEAN AllowNull, - _In_ SECURITY_INFORMATION RequiredInformation, - _Out_ PSECURITY_DESCRIPTOR *CapturedSecurityDescriptor - ) -{ - NTSTATUS status; - PSECURITY_DESCRIPTOR securityDescriptor; - ULONG bufferSize; - - if (!NT_SUCCESS(status = PhSvcCaptureBuffer(String, AllowNull, &securityDescriptor))) - return status; - - if (securityDescriptor) - { - if (!RtlValidRelativeSecurityDescriptor(securityDescriptor, String->Length, RequiredInformation)) - { - PhFree(securityDescriptor); - return STATUS_INVALID_SECURITY_DESCR; - } - - bufferSize = String->Length; - status = RtlSelfRelativeToAbsoluteSD2(securityDescriptor, &bufferSize); - - if (status == STATUS_BUFFER_TOO_SMALL) - { - PVOID newBuffer; - - newBuffer = PhAllocate(bufferSize); - memcpy(newBuffer, securityDescriptor, String->Length); - PhFree(securityDescriptor); - securityDescriptor = newBuffer; - - status = RtlSelfRelativeToAbsoluteSD2(securityDescriptor, &bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(securityDescriptor); - return status; - } - - *CapturedSecurityDescriptor = securityDescriptor; - } - else - { - *CapturedSecurityDescriptor = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcApiDefault( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS PhSvcApiPlugin( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING apiId; - PPH_PLUGIN plugin; - PH_STRINGREF pluginName; - PH_PLUGIN_PHSVC_REQUEST request; - - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.Plugin.i.ApiId, FALSE, &apiId))) - { - PH_AUTO(apiId); - - if (PhPluginsEnabled && - PhEmParseCompoundId(&apiId->sr, &pluginName, &request.SubId) && - (plugin = PhFindPlugin2(&pluginName))) - { - request.ReturnStatus = STATUS_NOT_IMPLEMENTED; - request.InBuffer = Payload->u.Plugin.i.Data; - request.InLength = sizeof(Payload->u.Plugin.i.Data); - request.OutBuffer = Payload->u.Plugin.o.Data; - request.OutLength = sizeof(Payload->u.Plugin.o.Data); - - request.ProbeBuffer = PhSvcProbeBuffer; - request.CaptureBuffer = PhSvcCaptureBuffer; - request.CaptureString = PhSvcCaptureString; - - PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackPhSvcRequest), &request); - status = request.ReturnStatus; - } - else - { - status = STATUS_NOT_FOUND; - } - } - - return status; -} - -NTSTATUS PhSvcpCaptureRunAsServiceParameters( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload, - _Out_ PPH_RUNAS_SERVICE_PARAMETERS Parameters, - _Out_ PPHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS CapturedParameters - ) -{ - NTSTATUS status; - - memset(CapturedParameters, 0, sizeof(PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS)); - - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.UserName, TRUE, &CapturedParameters->UserName))) - return status; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.Password, TRUE, &CapturedParameters->Password))) - return status; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.CurrentDirectory, TRUE, &CapturedParameters->CurrentDirectory))) - return status; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.CommandLine, TRUE, &CapturedParameters->CommandLine))) - return status; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.FileName, TRUE, &CapturedParameters->FileName))) - return status; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.DesktopName, TRUE, &CapturedParameters->DesktopName))) - return status; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.ServiceName, TRUE, &CapturedParameters->ServiceName))) - return status; - - Parameters->ProcessId = Payload->u.ExecuteRunAsCommand.i.ProcessId; - Parameters->UserName = PhGetString(CapturedParameters->UserName); - Parameters->Password = PhGetString(CapturedParameters->Password); - Parameters->LogonType = Payload->u.ExecuteRunAsCommand.i.LogonType; - Parameters->SessionId = Payload->u.ExecuteRunAsCommand.i.SessionId; - Parameters->CurrentDirectory = PhGetString(CapturedParameters->CurrentDirectory); - Parameters->CommandLine = PhGetString(CapturedParameters->CommandLine); - Parameters->FileName = PhGetString(CapturedParameters->FileName); - Parameters->DesktopName = PhGetString(CapturedParameters->DesktopName); - Parameters->UseLinkedToken = Payload->u.ExecuteRunAsCommand.i.UseLinkedToken; - Parameters->ServiceName = PhGetString(CapturedParameters->ServiceName); - - return status; -} - -VOID PhSvcpReleaseRunAsServiceParameters( - _In_ PPHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS CapturedParameters - ) -{ - if (CapturedParameters->UserName) - PhDereferenceObject(CapturedParameters->UserName); - - if (CapturedParameters->Password) - { - RtlSecureZeroMemory(CapturedParameters->Password->Buffer, CapturedParameters->Password->Length); - PhDereferenceObject(CapturedParameters->Password); - } - - if (CapturedParameters->CurrentDirectory) - PhDereferenceObject(CapturedParameters->CurrentDirectory); - if (CapturedParameters->CommandLine) - PhDereferenceObject(CapturedParameters->CommandLine); - if (CapturedParameters->FileName) - PhDereferenceObject(CapturedParameters->FileName); - if (CapturedParameters->DesktopName) - PhDereferenceObject(CapturedParameters->DesktopName); - if (CapturedParameters->ServiceName) - PhDereferenceObject(CapturedParameters->ServiceName); -} - -NTSTATUS PhSvcpValidateRunAsServiceParameters( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ) -{ - if ((!Parameters->UserName || !Parameters->Password) && !Parameters->ProcessId) - return STATUS_INVALID_PARAMETER_MIX; - if (!Parameters->FileName && !Parameters->CommandLine) - return STATUS_INVALID_PARAMETER_MIX; - if (!Parameters->ServiceName) - return STATUS_INVALID_PARAMETER; - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcApiExecuteRunAsCommand( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PH_RUNAS_SERVICE_PARAMETERS parameters; - PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS capturedParameters; - - if (NT_SUCCESS(status = PhSvcpCaptureRunAsServiceParameters(Client, Payload, ¶meters, &capturedParameters))) - { - if (NT_SUCCESS(status = PhSvcpValidateRunAsServiceParameters(¶meters))) - { - status = PhExecuteRunAsCommand(¶meters); - } - } - - PhSvcpReleaseRunAsServiceParameters(&capturedParameters); - - return status; -} - -NTSTATUS PhSvcApiUnloadDriver( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING name; - - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.UnloadDriver.i.Name, TRUE, &name))) - { - status = PhUnloadDriver(Payload->u.UnloadDriver.i.BaseAddress, PhGetString(name)); - PhClearReference(&name); - } - - return status; -} - -NTSTATUS PhSvcApiControlProcess( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - HANDLE processId; - HANDLE processHandle; - - processId = Payload->u.ControlProcess.i.ProcessId; - - switch (Payload->u.ControlProcess.i.Command) - { - case PhSvcControlProcessTerminate: - if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_TERMINATE, processId))) - { - status = PhTerminateProcess(processHandle, 1); // see notes in PhUiTerminateProcesses - NtClose(processHandle); - } - break; - case PhSvcControlProcessSuspend: - if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SUSPEND_RESUME, processId))) - { - status = NtSuspendProcess(processHandle); - NtClose(processHandle); - } - break; - case PhSvcControlProcessResume: - if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SUSPEND_RESUME, processId))) - { - status = NtResumeProcess(processHandle); - NtClose(processHandle); - } - break; - case PhSvcControlProcessPriority: - if (processId != SYSTEM_PROCESS_ID) - { - if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SET_INFORMATION, processId))) - { - PROCESS_PRIORITY_CLASS priorityClass; - - priorityClass.Foreground = FALSE; - priorityClass.PriorityClass = (UCHAR)Payload->u.ControlProcess.i.Argument; - status = NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); - - NtClose(processHandle); - } - } - else - { - status = STATUS_UNSUCCESSFUL; - } - break; - case PhSvcControlProcessIoPriority: - if (processId != SYSTEM_PROCESS_ID) - { - if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SET_INFORMATION, processId))) - { - status = PhSetProcessIoPriority(processHandle, Payload->u.ControlProcess.i.Argument); - NtClose(processHandle); - } - } - else - { - status = STATUS_UNSUCCESSFUL; - } - break; - default: - status = STATUS_INVALID_PARAMETER; - break; - } - - return status; -} - -NTSTATUS PhSvcApiControlService( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING serviceName; - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; - - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ControlService.i.ServiceName, FALSE, &serviceName))) - { - PH_AUTO(serviceName); - - switch (Payload->u.ControlService.i.Command) - { - case PhSvcControlServiceStart: - if (serviceHandle = PhOpenService( - serviceName->Buffer, - SERVICE_START - )) - { - if (!StartService(serviceHandle, 0, NULL)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - break; - case PhSvcControlServiceContinue: - if (serviceHandle = PhOpenService( - serviceName->Buffer, - SERVICE_PAUSE_CONTINUE - )) - { - if (!ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - break; - case PhSvcControlServicePause: - if (serviceHandle = PhOpenService( - serviceName->Buffer, - SERVICE_PAUSE_CONTINUE - )) - { - if (!ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - break; - case PhSvcControlServiceStop: - if (serviceHandle = PhOpenService( - serviceName->Buffer, - SERVICE_STOP - )) - { - if (!ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - break; - case PhSvcControlServiceDelete: - if (serviceHandle = PhOpenService( - serviceName->Buffer, - DELETE - )) - { - if (!DeleteService(serviceHandle)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - break; - default: - status = STATUS_INVALID_PARAMETER; - break; - } - } - - return status; -} - -NTSTATUS PhSvcApiCreateService( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING serviceName = NULL; - PPH_STRING displayName = NULL; - PPH_STRING binaryPathName = NULL; - PPH_STRING loadOrderGroup = NULL; - PPH_STRING dependencies = NULL; - PPH_STRING serviceStartName = NULL; - PPH_STRING password = NULL; - ULONG tagId = 0; - SC_HANDLE scManagerHandle; - SC_HANDLE serviceHandle; - - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.ServiceName, FALSE, &serviceName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.DisplayName, TRUE, &displayName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.BinaryPathName, TRUE, &binaryPathName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.LoadOrderGroup, TRUE, &loadOrderGroup))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.Dependencies, TRUE, &dependencies))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.ServiceStartName, TRUE, &serviceStartName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.Password, TRUE, &password))) - goto CleanupExit; - - if (scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) - { - if (serviceHandle = CreateService( - scManagerHandle, - serviceName->Buffer, - PhGetString(displayName), - SERVICE_CHANGE_CONFIG, - Payload->u.CreateService.i.ServiceType, - Payload->u.CreateService.i.StartType, - Payload->u.CreateService.i.ErrorControl, - PhGetString(binaryPathName), - PhGetString(loadOrderGroup), - Payload->u.CreateService.i.TagIdSpecified ? &tagId : NULL, - PhGetString(dependencies), - PhGetString(serviceStartName), - PhGetString(password) - )) - { - Payload->u.CreateService.o.TagId = tagId; - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - - CloseServiceHandle(scManagerHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - -CleanupExit: - if (password) - { - RtlSecureZeroMemory(password->Buffer, password->Length); - PhDereferenceObject(password); - } - - PhClearReference(&serviceStartName); - PhClearReference(&dependencies); - PhClearReference(&loadOrderGroup); - PhClearReference(&binaryPathName); - PhClearReference(&displayName); - PhClearReference(&serviceName); - - return status; -} - -NTSTATUS PhSvcApiChangeServiceConfig( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING serviceName = NULL; - PPH_STRING binaryPathName = NULL; - PPH_STRING loadOrderGroup = NULL; - PPH_STRING dependencies = NULL; - PPH_STRING serviceStartName = NULL; - PPH_STRING password = NULL; - PPH_STRING displayName = NULL; - ULONG tagId = 0; - SC_HANDLE serviceHandle; - - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.ServiceName, FALSE, &serviceName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.BinaryPathName, TRUE, &binaryPathName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.LoadOrderGroup, TRUE, &loadOrderGroup))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.Dependencies, TRUE, &dependencies))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.ServiceStartName, TRUE, &serviceStartName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.Password, TRUE, &password))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.DisplayName, TRUE, &displayName))) - goto CleanupExit; - - if (serviceHandle = PhOpenService(serviceName->Buffer, SERVICE_CHANGE_CONFIG)) - { - if (ChangeServiceConfig( - serviceHandle, - Payload->u.ChangeServiceConfig.i.ServiceType, - Payload->u.ChangeServiceConfig.i.StartType, - Payload->u.ChangeServiceConfig.i.ErrorControl, - PhGetString(binaryPathName), - PhGetString(loadOrderGroup), - Payload->u.ChangeServiceConfig.i.TagIdSpecified ? &tagId : NULL, - PhGetString(dependencies), - PhGetString(serviceStartName), - PhGetString(password), - PhGetString(displayName) - )) - { - Payload->u.ChangeServiceConfig.o.TagId = tagId; - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - -CleanupExit: - PhClearReference(&displayName); - - if (password) - { - RtlSecureZeroMemory(password->Buffer, password->Length); - PhDereferenceObject(password); - } - - PhClearReference(&serviceStartName); - PhClearReference(&dependencies); - PhClearReference(&loadOrderGroup); - PhClearReference(&binaryPathName); - PhClearReference(&serviceName); - - return status; -} - -NTSTATUS PhSvcpUnpackRoot( - _In_ PPH_RELATIVE_STRINGREF PackedData, - _In_ PVOID CapturedBuffer, - _In_ SIZE_T Length, - _Out_ PVOID *ValidatedBuffer - ) -{ - if (Length > PackedData->Length) - return STATUS_ACCESS_VIOLATION; - - *ValidatedBuffer = CapturedBuffer; - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcpUnpackBuffer( - _In_ PPH_RELATIVE_STRINGREF PackedData, - _In_ PVOID CapturedBuffer, - _In_ PVOID *OffsetInBuffer, - _In_ SIZE_T Length, - _In_ ULONG Alignment, - _In_ BOOLEAN AllowNull - ) -{ - SIZE_T offset; - - offset = (SIZE_T)*OffsetInBuffer; - - if (offset == 0) - { - if (AllowNull) - return STATUS_SUCCESS; - else - return STATUS_ACCESS_VIOLATION; - } - - if (offset + Length < offset) - return STATUS_ACCESS_VIOLATION; - if (offset + Length > PackedData->Length) - return STATUS_ACCESS_VIOLATION; - if (offset & (Alignment - 1)) - return STATUS_DATATYPE_MISALIGNMENT; - - *OffsetInBuffer = (PVOID)((ULONG_PTR)CapturedBuffer + offset); - - return STATUS_SUCCESS; -} - -NTSTATUS PhSvcpUnpackStringZ( - _In_ PPH_RELATIVE_STRINGREF PackedData, - _In_ PVOID CapturedBuffer, - _In_ PVOID *OffsetInBuffer, - _In_ BOOLEAN Multi, - _In_ BOOLEAN AllowNull - ) -{ - SIZE_T offset; - PWCHAR start; - PWCHAR end; - PH_STRINGREF remainingPart; - PH_STRINGREF firstPart; - - offset = (SIZE_T)*OffsetInBuffer; - - if (offset == 0) - { - if (AllowNull) - return STATUS_SUCCESS; - else - return STATUS_ACCESS_VIOLATION; - } - - if (offset >= PackedData->Length) - return STATUS_ACCESS_VIOLATION; - if (offset & 1) - return STATUS_DATATYPE_MISALIGNMENT; - - start = (PWCHAR)((ULONG_PTR)CapturedBuffer + offset); - end = (PWCHAR)((ULONG_PTR)CapturedBuffer + (PackedData->Length & -2)); - remainingPart.Buffer = start; - remainingPart.Length = (end - start) * sizeof(WCHAR); - - if (Multi) - { - SIZE_T validatedLength = 0; - - while (PhSplitStringRefAtChar(&remainingPart, 0, &firstPart, &remainingPart)) - { - validatedLength += firstPart.Length + sizeof(WCHAR); - - if (firstPart.Length == 0) - { - *OffsetInBuffer = start; - - return STATUS_SUCCESS; - } - } - } - else - { - if (PhSplitStringRefAtChar(&remainingPart, 0, &firstPart, &remainingPart)) - { - *OffsetInBuffer = start; - - return STATUS_SUCCESS; - } - } - - return STATUS_ACCESS_VIOLATION; -} - -NTSTATUS PhSvcApiChangeServiceConfig2( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING serviceName = NULL; - PVOID info = NULL; - SC_HANDLE serviceHandle = NULL; - PH_RELATIVE_STRINGREF packedData; - PVOID unpackedInfo = NULL; - ACCESS_MASK desiredAccess = SERVICE_CHANGE_CONFIG; - - if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig2.i.ServiceName, FALSE, &serviceName))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcCaptureBuffer(&Payload->u.ChangeServiceConfig2.i.Info, FALSE, &info))) - goto CleanupExit; - - packedData = Payload->u.ChangeServiceConfig2.i.Info; - - switch (Payload->u.ChangeServiceConfig2.i.InfoLevel) - { - case SERVICE_CONFIG_FAILURE_ACTIONS: - { - LPSERVICE_FAILURE_ACTIONS failureActions; - - if (!NT_SUCCESS(status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_FAILURE_ACTIONS), &failureActions))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcpUnpackStringZ(&packedData, info, &failureActions->lpRebootMsg, FALSE, TRUE))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcpUnpackStringZ(&packedData, info, &failureActions->lpCommand, FALSE, TRUE))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &failureActions->lpsaActions, failureActions->cActions * sizeof(SC_ACTION), __alignof(SC_ACTION), TRUE))) - goto CleanupExit; - - if (failureActions->lpsaActions) - { - ULONG i; - - for (i = 0; i < failureActions->cActions; i++) - { - if (failureActions->lpsaActions[i].Type == SC_ACTION_RESTART) - { - desiredAccess |= SERVICE_START; - break; - } - } - } - - unpackedInfo = failureActions; - } - break; - case SERVICE_CONFIG_DELAYED_AUTO_START_INFO: - status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_DELAYED_AUTO_START_INFO), &unpackedInfo); - break; - case SERVICE_CONFIG_FAILURE_ACTIONS_FLAG: - status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_FAILURE_ACTIONS_FLAG), &unpackedInfo); - break; - case SERVICE_CONFIG_SERVICE_SID_INFO: - status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_SID_INFO), &unpackedInfo); - break; - case SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO: - { - LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; - - if (!NT_SUCCESS(status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_REQUIRED_PRIVILEGES_INFO), &requiredPrivilegesInfo))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcpUnpackStringZ(&packedData, info, &requiredPrivilegesInfo->pmszRequiredPrivileges, TRUE, FALSE))) - goto CleanupExit; - - unpackedInfo = requiredPrivilegesInfo; - } - break; - case SERVICE_CONFIG_PRESHUTDOWN_INFO: - status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_PRESHUTDOWN_INFO), &unpackedInfo); - break; - case SERVICE_CONFIG_TRIGGER_INFO: - { - PSERVICE_TRIGGER_INFO triggerInfo; - ULONG i; - PSERVICE_TRIGGER trigger; - ULONG j; - PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem; - ULONG alignment; - - if (!NT_SUCCESS(status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_TRIGGER_INFO), &triggerInfo))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &triggerInfo->pTriggers, triggerInfo->cTriggers * sizeof(SERVICE_TRIGGER), __alignof(SERVICE_TRIGGER), TRUE))) - goto CleanupExit; - - if (triggerInfo->pTriggers) - { - for (i = 0; i < triggerInfo->cTriggers; i++) - { - trigger = &triggerInfo->pTriggers[i]; - - if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &trigger->pTriggerSubtype, sizeof(GUID), __alignof(GUID), TRUE))) - goto CleanupExit; - if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &trigger->pDataItems, trigger->cDataItems * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), __alignof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), TRUE))) - goto CleanupExit; - - if (trigger->pDataItems) - { - for (j = 0; j < trigger->cDataItems; j++) - { - dataItem = &trigger->pDataItems[j]; - alignment = 1; - - switch (dataItem->dwDataType) - { - case SERVICE_TRIGGER_DATA_TYPE_BINARY: - case SERVICE_TRIGGER_DATA_TYPE_LEVEL: - alignment = sizeof(CHAR); - break; - case SERVICE_TRIGGER_DATA_TYPE_STRING: - alignment = sizeof(WCHAR); - break; - case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY: - case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL: - alignment = sizeof(ULONG64); - break; - } - - if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &dataItem->pData, dataItem->cbData, alignment, FALSE))) - goto CleanupExit; - } - } - } - } - - unpackedInfo = triggerInfo; - } - break; - case SERVICE_CONFIG_LAUNCH_PROTECTED: - status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_LAUNCH_PROTECTED_INFO), &unpackedInfo); - break; - default: - status = STATUS_INVALID_PARAMETER; - break; - } - - if (NT_SUCCESS(status)) - { - assert(unpackedInfo); - - if (!(serviceHandle = PhOpenService(serviceName->Buffer, desiredAccess))) - { - status = PhGetLastWin32ErrorAsNtStatus(); - goto CleanupExit; - } - - if (!ChangeServiceConfig2( - serviceHandle, - Payload->u.ChangeServiceConfig2.i.InfoLevel, - unpackedInfo - )) - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - } - -CleanupExit: - if (serviceHandle) - CloseServiceHandle(serviceHandle); - if (info) - PhFree(info); - if (serviceName) - PhDereferenceObject(serviceName); - - return status; -} - -NTSTATUS PhSvcApiSetTcpEntry( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - static PVOID setTcpEntry = NULL; - - ULONG (__stdcall *localSetTcpEntry)(PVOID TcpRow); - struct - { - DWORD dwState; - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwRemoteAddr; - DWORD dwRemotePort; - } tcpRow; - ULONG result; - - localSetTcpEntry = setTcpEntry; - - if (!localSetTcpEntry) - { - HMODULE iphlpapiModule; - - iphlpapiModule = LoadLibrary(L"iphlpapi.dll"); - - if (iphlpapiModule) - { - localSetTcpEntry = PhGetProcedureAddress(iphlpapiModule, "SetTcpEntry", 0); - - if (localSetTcpEntry) - { - if (_InterlockedExchangePointer(&setTcpEntry, localSetTcpEntry) != NULL) - { - // Another thread got the address of SetTcpEntry already. - // Decrement the reference count of iphlpapi.dll. - FreeLibrary(iphlpapiModule); - } - } - } - } - - if (!localSetTcpEntry) - return STATUS_NOT_SUPPORTED; - - tcpRow.dwState = Payload->u.SetTcpEntry.i.State; - tcpRow.dwLocalAddr = Payload->u.SetTcpEntry.i.LocalAddress; - tcpRow.dwLocalPort = Payload->u.SetTcpEntry.i.LocalPort; - tcpRow.dwRemoteAddr = Payload->u.SetTcpEntry.i.RemoteAddress; - tcpRow.dwRemotePort = Payload->u.SetTcpEntry.i.RemotePort; - result = localSetTcpEntry(&tcpRow); - - return NTSTATUS_FROM_WIN32(result); -} - -NTSTATUS PhSvcApiControlThread( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - HANDLE threadId; - HANDLE threadHandle; - - threadId = Payload->u.ControlThread.i.ThreadId; - - switch (Payload->u.ControlThread.i.Command) - { - case PhSvcControlThreadTerminate: - if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_TERMINATE, threadId))) - { - status = NtTerminateThread(threadHandle, STATUS_SUCCESS); - NtClose(threadHandle); - } - break; - case PhSvcControlThreadSuspend: - if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) - { - status = NtSuspendThread(threadHandle, NULL); - NtClose(threadHandle); - } - break; - case PhSvcControlThreadResume: - if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) - { - status = NtResumeThread(threadHandle, NULL); - NtClose(threadHandle); - } - break; - case PhSvcControlThreadIoPriority: - if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_SET_INFORMATION, threadId))) - { - status = PhSetThreadIoPriority(threadHandle, Payload->u.ControlThread.i.Argument); - NtClose(threadHandle); - } - break; - default: - status = STATUS_INVALID_PARAMETER; - break; - } - - return status; -} - -NTSTATUS PhSvcApiAddAccountRight( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PSID accountSid; - PPH_STRING userRight; - LSA_HANDLE policyHandle; - UNICODE_STRING userRightUs; - - if (NT_SUCCESS(status = PhSvcCaptureSid(&Payload->u.AddAccountRight.i.AccountSid, FALSE, &accountSid))) - { - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.AddAccountRight.i.UserRight, FALSE, &userRight))) - { - PH_AUTO(userRight); - - if (NT_SUCCESS(status = PhOpenLsaPolicy(&policyHandle, POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT, NULL))) - { - PhStringRefToUnicodeString(&userRight->sr, &userRightUs); - status = LsaAddAccountRights(policyHandle, accountSid, &userRightUs, 1); - LsaClose(policyHandle); - } - } - - PhFree(accountSid); - } - - return status; -} - -NTSTATUS PhSvcApiInvokeRunAsService( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PH_RUNAS_SERVICE_PARAMETERS parameters; - PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS capturedParameters; - - if (NT_SUCCESS(status = PhSvcpCaptureRunAsServiceParameters(Client, Payload, ¶meters, &capturedParameters))) - { - if (NT_SUCCESS(status = PhSvcpValidateRunAsServiceParameters(¶meters))) - { - status = PhInvokeRunAsService(¶meters); - } - } - - PhSvcpReleaseRunAsServiceParameters(&capturedParameters); - - return status; -} - -NTSTATUS PhSvcApiIssueMemoryListCommand( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - - status = NtSetSystemInformation( - SystemMemoryListInformation, - &Payload->u.IssueMemoryListCommand.i.Command, - sizeof(SYSTEM_MEMORY_LIST_COMMAND) - ); - - return status; -} - -NTSTATUS PhSvcApiPostMessage( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - if (PostMessage( - Payload->u.PostMessage.i.hWnd, - Payload->u.PostMessage.i.Msg, - Payload->u.PostMessage.i.wParam, - Payload->u.PostMessage.i.lParam - )) - { - return STATUS_SUCCESS; - } - else - { - return PhGetLastWin32ErrorAsNtStatus(); - } -} - -NTSTATUS PhSvcApiSendMessage( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - if (SendMessage( - Payload->u.PostMessage.i.hWnd, - Payload->u.PostMessage.i.Msg, - Payload->u.PostMessage.i.wParam, - Payload->u.PostMessage.i.lParam - )) - { - return STATUS_SUCCESS; - } - else - { - return PhGetLastWin32ErrorAsNtStatus(); - } -} - -NTSTATUS PhSvcApiCreateProcessIgnoreIfeoDebugger( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING fileName; - - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateProcessIgnoreIfeoDebugger.i.FileName, FALSE, &fileName))) - { - PH_AUTO(fileName); - - if (!PhCreateProcessIgnoreIfeoDebugger(fileName->Buffer)) - status = STATUS_UNSUCCESSFUL; - } - - return status; -} - -NTSTATUS PhSvcApiSetServiceSecurity( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - NTSTATUS status; - PPH_STRING serviceName; - PSECURITY_DESCRIPTOR securityDescriptor; - ACCESS_MASK desiredAccess; - SC_HANDLE serviceHandle; - - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.SetServiceSecurity.i.ServiceName, FALSE, &serviceName))) - { - PH_AUTO(serviceName); - - if (NT_SUCCESS(status = PhSvcCaptureSecurityDescriptor(&Payload->u.SetServiceSecurity.i.SecurityDescriptor, FALSE, 0, &securityDescriptor))) - { - desiredAccess = 0; - - if ((Payload->u.SetServiceSecurity.i.SecurityInformation & OWNER_SECURITY_INFORMATION) || - (Payload->u.SetServiceSecurity.i.SecurityInformation & GROUP_SECURITY_INFORMATION)) - { - desiredAccess |= WRITE_OWNER; - } - - if (Payload->u.SetServiceSecurity.i.SecurityInformation & DACL_SECURITY_INFORMATION) - { - desiredAccess |= WRITE_DAC; - } - - if (Payload->u.SetServiceSecurity.i.SecurityInformation & SACL_SECURITY_INFORMATION) - { - desiredAccess |= ACCESS_SYSTEM_SECURITY; - } - - if (serviceHandle = PhOpenService(serviceName->Buffer, desiredAccess)) - { - status = PhSetSeObjectSecurity( - serviceHandle, - SE_SERVICE, - Payload->u.SetServiceSecurity.i.SecurityInformation, - securityDescriptor - ); - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - - PhFree(securityDescriptor); - } - } - - return status; -} - -NTSTATUS PhSvcApiLoadDbgHelp( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - static BOOLEAN alreadyLoaded; - - NTSTATUS status; - PPH_STRING dbgHelpPath; - - if (alreadyLoaded) - return STATUS_SOME_NOT_MAPPED; - - if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.LoadDbgHelp.i.DbgHelpPath, FALSE, &dbgHelpPath))) - { - PH_AUTO(dbgHelpPath); - PhLoadDbgHelpFromPath(dbgHelpPath->Buffer); - alreadyLoaded = TRUE; - } - - return status; -} - -NTSTATUS PhSvcApiWriteMiniDumpProcess( - _In_ PPHSVC_CLIENT Client, - _Inout_ PPHSVC_API_PAYLOAD Payload - ) -{ - if (PhWriteMiniDumpProcess( - UlongToHandle(Payload->u.WriteMiniDumpProcess.i.LocalProcessHandle), - UlongToHandle(Payload->u.WriteMiniDumpProcess.i.ProcessId), - UlongToHandle(Payload->u.WriteMiniDumpProcess.i.LocalFileHandle), - Payload->u.WriteMiniDumpProcess.i.DumpType, - NULL, - NULL, - NULL - )) - { - return STATUS_SUCCESS; - } - else - { - ULONG error; - - error = GetLastError(); - - if (error == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)) - return STATUS_INVALID_PARAMETER; - else - return STATUS_UNSUCCESSFUL; - } -} +/* + * Process Hacker - + * server API + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +typedef struct _PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS +{ + PPH_STRING UserName; + PPH_STRING Password; + PPH_STRING CurrentDirectory; + PPH_STRING CommandLine; + PPH_STRING FileName; + PPH_STRING DesktopName; + PPH_STRING ServiceName; +} PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS, *PPHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS; + +PPHSVC_API_PROCEDURE PhSvcApiCallTable[] = +{ + PhSvcApiPlugin, + PhSvcApiExecuteRunAsCommand, + PhSvcApiUnloadDriver, + PhSvcApiControlProcess, + PhSvcApiControlService, + PhSvcApiCreateService, + PhSvcApiChangeServiceConfig, + PhSvcApiChangeServiceConfig2, + PhSvcApiSetTcpEntry, + PhSvcApiControlThread, + PhSvcApiAddAccountRight, + PhSvcApiInvokeRunAsService, + PhSvcApiIssueMemoryListCommand, + PhSvcApiPostMessage, + PhSvcApiSendMessage, + PhSvcApiCreateProcessIgnoreIfeoDebugger, + PhSvcApiSetServiceSecurity, + PhSvcApiWriteMiniDumpProcess +}; +C_ASSERT(sizeof(PhSvcApiCallTable) / sizeof(PPHSVC_API_PROCEDURE) == PhSvcMaximumApiNumber - 1); + +NTSTATUS PhSvcApiInitialization( + VOID + ) +{ + return STATUS_SUCCESS; +} + +VOID PhSvcDispatchApiCall( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload, + _Out_ PHANDLE ReplyPortHandle + ) +{ + NTSTATUS status; + + if ( + Payload->ApiNumber == 0 || + (ULONG)Payload->ApiNumber >= (ULONG)PhSvcMaximumApiNumber || + !PhSvcApiCallTable[Payload->ApiNumber - 1] + ) + { + Payload->ReturnStatus = STATUS_INVALID_SYSTEM_SERVICE; + *ReplyPortHandle = Client->PortHandle; + return; + } + + status = PhSvcApiCallTable[Payload->ApiNumber - 1](Client, Payload); + Payload->ReturnStatus = status; + + *ReplyPortHandle = Client->PortHandle; +} + +PVOID PhSvcValidateString( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ ULONG Alignment + ) +{ + PPHSVC_CLIENT client = PhSvcGetCurrentClient(); + PVOID address; + + address = PTR_ADD_OFFSET(client->ClientViewBase, String->Offset); + + if ((ULONG_PTR)address + String->Length < (ULONG_PTR)address || + (ULONG_PTR)address < (ULONG_PTR)client->ClientViewBase || + (ULONG_PTR)address + String->Length > (ULONG_PTR)client->ClientViewLimit) + { + return NULL; + } + + if ((ULONG_PTR)address & (Alignment - 1)) + { + return NULL; + } + + return address; +} + +NTSTATUS PhSvcProbeBuffer( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ ULONG Alignment, + _In_ BOOLEAN AllowNull, + _Out_ PVOID *Pointer + ) +{ + PVOID address; + + if (String->Offset != 0) + { + address = PhSvcValidateString(String, Alignment); + + if (!address) + return STATUS_ACCESS_VIOLATION; + + *Pointer = address; + } + else + { + if (!AllowNull) + return STATUS_ACCESS_VIOLATION; + + *Pointer = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcCaptureBuffer( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PVOID *CapturedBuffer + ) +{ + PVOID address; + PVOID buffer; + + if (String->Offset != 0) + { + address = PhSvcValidateString(String, 1); + + if (!address) + return STATUS_ACCESS_VIOLATION; + + buffer = PhAllocateSafe(String->Length); + + if (!buffer) + return STATUS_NO_MEMORY; + + memcpy(buffer, address, String->Length); + *CapturedBuffer = buffer; + } + else + { + if (!AllowNull) + return STATUS_ACCESS_VIOLATION; + + *CapturedBuffer = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcCaptureString( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PPH_STRING *CapturedString + ) +{ + PVOID address; + + if (String->Length & 1) + return STATUS_INVALID_BUFFER_SIZE; + if (String->Length > 0xfffe) + return STATUS_INVALID_BUFFER_SIZE; + + if (String->Offset != 0) + { + address = PhSvcValidateString(String, sizeof(WCHAR)); + + if (!address) + return STATUS_ACCESS_VIOLATION; + + if (String->Length != 0) + *CapturedString = PhCreateStringEx(address, String->Length); + else + *CapturedString = PhReferenceEmptyString(); + } + else + { + if (!AllowNull) + return STATUS_ACCESS_VIOLATION; + + *CapturedString = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcCaptureSid( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _Out_ PSID *CapturedSid + ) +{ + NTSTATUS status; + PSID sid; + + if (!NT_SUCCESS(status = PhSvcCaptureBuffer(String, AllowNull, &sid))) + return status; + + if (sid) + { + if (String->Length < UFIELD_OFFSET(struct _SID, IdentifierAuthority) || + String->Length < RtlLengthRequiredSid(((struct _SID *)sid)->SubAuthorityCount) || + !RtlValidSid(sid)) + { + PhFree(sid); + return STATUS_INVALID_SID; + } + + *CapturedSid = sid; + } + else + { + *CapturedSid = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcCaptureSecurityDescriptor( + _In_ PPH_RELATIVE_STRINGREF String, + _In_ BOOLEAN AllowNull, + _In_ SECURITY_INFORMATION RequiredInformation, + _Out_ PSECURITY_DESCRIPTOR *CapturedSecurityDescriptor + ) +{ + NTSTATUS status; + PSECURITY_DESCRIPTOR securityDescriptor; + ULONG bufferSize; + + if (!NT_SUCCESS(status = PhSvcCaptureBuffer(String, AllowNull, &securityDescriptor))) + return status; + + if (securityDescriptor) + { + if (!RtlValidRelativeSecurityDescriptor(securityDescriptor, String->Length, RequiredInformation)) + { + PhFree(securityDescriptor); + return STATUS_INVALID_SECURITY_DESCR; + } + + bufferSize = String->Length; + status = RtlSelfRelativeToAbsoluteSD2(securityDescriptor, &bufferSize); + + if (status == STATUS_BUFFER_TOO_SMALL) + { + PVOID newBuffer; + + newBuffer = PhAllocate(bufferSize); + memcpy(newBuffer, securityDescriptor, String->Length); + PhFree(securityDescriptor); + securityDescriptor = newBuffer; + + status = RtlSelfRelativeToAbsoluteSD2(securityDescriptor, &bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(securityDescriptor); + return status; + } + + *CapturedSecurityDescriptor = securityDescriptor; + } + else + { + *CapturedSecurityDescriptor = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcApiDefault( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS PhSvcApiPlugin( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING apiId; + PPH_PLUGIN plugin; + PH_STRINGREF pluginName; + PH_PLUGIN_PHSVC_REQUEST request; + + if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.Plugin.i.ApiId, FALSE, &apiId))) + { + PH_AUTO(apiId); + + if (PhPluginsEnabled && + PhEmParseCompoundId(&apiId->sr, &pluginName, &request.SubId) && + (plugin = PhFindPlugin2(&pluginName))) + { + request.ReturnStatus = STATUS_NOT_IMPLEMENTED; + request.InBuffer = Payload->u.Plugin.i.Data; + request.InLength = sizeof(Payload->u.Plugin.i.Data); + request.OutBuffer = Payload->u.Plugin.o.Data; + request.OutLength = sizeof(Payload->u.Plugin.o.Data); + + request.ProbeBuffer = PhSvcProbeBuffer; + request.CaptureBuffer = PhSvcCaptureBuffer; + request.CaptureString = PhSvcCaptureString; + + PhInvokeCallback(PhGetPluginCallback(plugin, PluginCallbackPhSvcRequest), &request); + status = request.ReturnStatus; + } + else + { + status = STATUS_NOT_FOUND; + } + } + + return status; +} + +NTSTATUS PhSvcpCaptureRunAsServiceParameters( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload, + _Out_ PPH_RUNAS_SERVICE_PARAMETERS Parameters, + _Out_ PPHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS CapturedParameters + ) +{ + NTSTATUS status; + + memset(CapturedParameters, 0, sizeof(PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS)); + + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.UserName, TRUE, &CapturedParameters->UserName))) + return status; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.Password, TRUE, &CapturedParameters->Password))) + return status; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.CurrentDirectory, TRUE, &CapturedParameters->CurrentDirectory))) + return status; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.CommandLine, TRUE, &CapturedParameters->CommandLine))) + return status; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.FileName, TRUE, &CapturedParameters->FileName))) + return status; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.DesktopName, TRUE, &CapturedParameters->DesktopName))) + return status; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ExecuteRunAsCommand.i.ServiceName, TRUE, &CapturedParameters->ServiceName))) + return status; + + Parameters->ProcessId = Payload->u.ExecuteRunAsCommand.i.ProcessId; + Parameters->UserName = PhGetString(CapturedParameters->UserName); + Parameters->Password = PhGetString(CapturedParameters->Password); + Parameters->LogonType = Payload->u.ExecuteRunAsCommand.i.LogonType; + Parameters->SessionId = Payload->u.ExecuteRunAsCommand.i.SessionId; + Parameters->CurrentDirectory = PhGetString(CapturedParameters->CurrentDirectory); + Parameters->CommandLine = PhGetString(CapturedParameters->CommandLine); + Parameters->FileName = PhGetString(CapturedParameters->FileName); + Parameters->DesktopName = PhGetString(CapturedParameters->DesktopName); + Parameters->UseLinkedToken = Payload->u.ExecuteRunAsCommand.i.UseLinkedToken; + Parameters->ServiceName = PhGetString(CapturedParameters->ServiceName); + Parameters->CreateSuspendedProcess = Payload->u.ExecuteRunAsCommand.i.CreateSuspendedProcess; + + return status; +} + +VOID PhSvcpReleaseRunAsServiceParameters( + _In_ PPHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS CapturedParameters + ) +{ + if (CapturedParameters->UserName) + PhDereferenceObject(CapturedParameters->UserName); + + if (CapturedParameters->Password) + { + RtlSecureZeroMemory(CapturedParameters->Password->Buffer, CapturedParameters->Password->Length); + PhDereferenceObject(CapturedParameters->Password); + } + + if (CapturedParameters->CurrentDirectory) + PhDereferenceObject(CapturedParameters->CurrentDirectory); + if (CapturedParameters->CommandLine) + PhDereferenceObject(CapturedParameters->CommandLine); + if (CapturedParameters->FileName) + PhDereferenceObject(CapturedParameters->FileName); + if (CapturedParameters->DesktopName) + PhDereferenceObject(CapturedParameters->DesktopName); + if (CapturedParameters->ServiceName) + PhDereferenceObject(CapturedParameters->ServiceName); +} + +NTSTATUS PhSvcpValidateRunAsServiceParameters( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ) +{ + if ((!Parameters->UserName || !Parameters->Password) && !Parameters->ProcessId) + return STATUS_INVALID_PARAMETER_MIX; + if (!Parameters->FileName && !Parameters->CommandLine) + return STATUS_INVALID_PARAMETER_MIX; + if (!Parameters->ServiceName) + return STATUS_INVALID_PARAMETER; + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcApiExecuteRunAsCommand( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PH_RUNAS_SERVICE_PARAMETERS parameters; + PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS capturedParameters; + + if (NT_SUCCESS(status = PhSvcpCaptureRunAsServiceParameters(Client, Payload, ¶meters, &capturedParameters))) + { + if (NT_SUCCESS(status = PhSvcpValidateRunAsServiceParameters(¶meters))) + { + status = PhExecuteRunAsCommand(¶meters); + } + } + + PhSvcpReleaseRunAsServiceParameters(&capturedParameters); + + return status; +} + +NTSTATUS PhSvcApiUnloadDriver( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING name; + + if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.UnloadDriver.i.Name, TRUE, &name))) + { + status = PhUnloadDriver(Payload->u.UnloadDriver.i.BaseAddress, PhGetString(name)); + PhClearReference(&name); + } + + return status; +} + +NTSTATUS PhSvcApiControlProcess( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + HANDLE processId; + HANDLE processHandle; + + processId = Payload->u.ControlProcess.i.ProcessId; + + switch (Payload->u.ControlProcess.i.Command) + { + case PhSvcControlProcessTerminate: + if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_TERMINATE, processId))) + { + status = PhTerminateProcess(processHandle, 1); // see notes in PhUiTerminateProcesses + NtClose(processHandle); + } + break; + case PhSvcControlProcessSuspend: + if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SUSPEND_RESUME, processId))) + { + status = NtSuspendProcess(processHandle); + NtClose(processHandle); + } + break; + case PhSvcControlProcessResume: + if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SUSPEND_RESUME, processId))) + { + status = NtResumeProcess(processHandle); + NtClose(processHandle); + } + break; + case PhSvcControlProcessPriority: + if (processId != SYSTEM_PROCESS_ID) + { + if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SET_INFORMATION, processId))) + { + PROCESS_PRIORITY_CLASS priorityClass; + + priorityClass.Foreground = FALSE; + priorityClass.PriorityClass = (UCHAR)Payload->u.ControlProcess.i.Argument; + + status = PhSetProcessPriority(processHandle, priorityClass); + + NtClose(processHandle); + } + } + else + { + status = STATUS_UNSUCCESSFUL; + } + break; + case PhSvcControlProcessIoPriority: + if (processId != SYSTEM_PROCESS_ID) + { + if (NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_SET_INFORMATION, processId))) + { + status = PhSetProcessIoPriority(processHandle, Payload->u.ControlProcess.i.Argument); + NtClose(processHandle); + } + } + else + { + status = STATUS_UNSUCCESSFUL; + } + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + return status; +} + +NTSTATUS PhSvcApiControlService( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING serviceName; + SC_HANDLE serviceHandle; + SERVICE_STATUS serviceStatus; + + if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ControlService.i.ServiceName, FALSE, &serviceName))) + { + PH_AUTO(serviceName); + + switch (Payload->u.ControlService.i.Command) + { + case PhSvcControlServiceStart: + if (serviceHandle = PhOpenService( + serviceName->Buffer, + SERVICE_START + )) + { + if (!StartService(serviceHandle, 0, NULL)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + break; + case PhSvcControlServiceContinue: + if (serviceHandle = PhOpenService( + serviceName->Buffer, + SERVICE_PAUSE_CONTINUE + )) + { + if (!ControlService(serviceHandle, SERVICE_CONTROL_CONTINUE, &serviceStatus)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + break; + case PhSvcControlServicePause: + if (serviceHandle = PhOpenService( + serviceName->Buffer, + SERVICE_PAUSE_CONTINUE + )) + { + if (!ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, &serviceStatus)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + break; + case PhSvcControlServiceStop: + if (serviceHandle = PhOpenService( + serviceName->Buffer, + SERVICE_STOP + )) + { + if (!ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + break; + case PhSvcControlServiceDelete: + if (serviceHandle = PhOpenService( + serviceName->Buffer, + DELETE + )) + { + if (!DeleteService(serviceHandle)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } + + return status; +} + +NTSTATUS PhSvcApiCreateService( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING serviceName = NULL; + PPH_STRING displayName = NULL; + PPH_STRING binaryPathName = NULL; + PPH_STRING loadOrderGroup = NULL; + PPH_STRING dependencies = NULL; + PPH_STRING serviceStartName = NULL; + PPH_STRING password = NULL; + ULONG tagId = 0; + SC_HANDLE scManagerHandle; + SC_HANDLE serviceHandle; + + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.ServiceName, FALSE, &serviceName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.DisplayName, TRUE, &displayName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.BinaryPathName, TRUE, &binaryPathName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.LoadOrderGroup, TRUE, &loadOrderGroup))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.Dependencies, TRUE, &dependencies))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.ServiceStartName, TRUE, &serviceStartName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateService.i.Password, TRUE, &password))) + goto CleanupExit; + + if (scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) + { + if (serviceHandle = CreateService( + scManagerHandle, + serviceName->Buffer, + PhGetString(displayName), + SERVICE_CHANGE_CONFIG, + Payload->u.CreateService.i.ServiceType, + Payload->u.CreateService.i.StartType, + Payload->u.CreateService.i.ErrorControl, + PhGetString(binaryPathName), + PhGetString(loadOrderGroup), + Payload->u.CreateService.i.TagIdSpecified ? &tagId : NULL, + PhGetString(dependencies), + PhGetString(serviceStartName), + PhGetString(password) + )) + { + Payload->u.CreateService.o.TagId = tagId; + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + CloseServiceHandle(scManagerHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + +CleanupExit: + if (password) + { + RtlSecureZeroMemory(password->Buffer, password->Length); + PhDereferenceObject(password); + } + + PhClearReference(&serviceStartName); + PhClearReference(&dependencies); + PhClearReference(&loadOrderGroup); + PhClearReference(&binaryPathName); + PhClearReference(&displayName); + PhClearReference(&serviceName); + + return status; +} + +NTSTATUS PhSvcApiChangeServiceConfig( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING serviceName = NULL; + PPH_STRING binaryPathName = NULL; + PPH_STRING loadOrderGroup = NULL; + PPH_STRING dependencies = NULL; + PPH_STRING serviceStartName = NULL; + PPH_STRING password = NULL; + PPH_STRING displayName = NULL; + ULONG tagId = 0; + SC_HANDLE serviceHandle; + + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.ServiceName, FALSE, &serviceName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.BinaryPathName, TRUE, &binaryPathName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.LoadOrderGroup, TRUE, &loadOrderGroup))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.Dependencies, TRUE, &dependencies))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.ServiceStartName, TRUE, &serviceStartName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.Password, TRUE, &password))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig.i.DisplayName, TRUE, &displayName))) + goto CleanupExit; + + if (serviceHandle = PhOpenService(serviceName->Buffer, SERVICE_CHANGE_CONFIG)) + { + if (ChangeServiceConfig( + serviceHandle, + Payload->u.ChangeServiceConfig.i.ServiceType, + Payload->u.ChangeServiceConfig.i.StartType, + Payload->u.ChangeServiceConfig.i.ErrorControl, + PhGetString(binaryPathName), + PhGetString(loadOrderGroup), + Payload->u.ChangeServiceConfig.i.TagIdSpecified ? &tagId : NULL, + PhGetString(dependencies), + PhGetString(serviceStartName), + PhGetString(password), + PhGetString(displayName) + )) + { + Payload->u.ChangeServiceConfig.o.TagId = tagId; + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + +CleanupExit: + PhClearReference(&displayName); + + if (password) + { + RtlSecureZeroMemory(password->Buffer, password->Length); + PhDereferenceObject(password); + } + + PhClearReference(&serviceStartName); + PhClearReference(&dependencies); + PhClearReference(&loadOrderGroup); + PhClearReference(&binaryPathName); + PhClearReference(&serviceName); + + return status; +} + +NTSTATUS PhSvcpUnpackRoot( + _In_ PPH_RELATIVE_STRINGREF PackedData, + _In_ PVOID CapturedBuffer, + _In_ SIZE_T Length, + _Out_ PVOID *ValidatedBuffer + ) +{ + if (Length > PackedData->Length) + return STATUS_ACCESS_VIOLATION; + + *ValidatedBuffer = CapturedBuffer; + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcpUnpackBuffer( + _In_ PPH_RELATIVE_STRINGREF PackedData, + _In_ PVOID CapturedBuffer, + _In_ PVOID *OffsetInBuffer, + _In_ SIZE_T Length, + _In_ ULONG Alignment, + _In_ BOOLEAN AllowNull + ) +{ + SIZE_T offset; + + offset = (SIZE_T)*OffsetInBuffer; + + if (offset == 0) + { + if (AllowNull) + return STATUS_SUCCESS; + else + return STATUS_ACCESS_VIOLATION; + } + + if (offset + Length < offset) + return STATUS_ACCESS_VIOLATION; + if (offset + Length > PackedData->Length) + return STATUS_ACCESS_VIOLATION; + if (offset & (Alignment - 1)) + return STATUS_DATATYPE_MISALIGNMENT; + + *OffsetInBuffer = PTR_ADD_OFFSET(CapturedBuffer, offset); + + return STATUS_SUCCESS; +} + +NTSTATUS PhSvcpUnpackStringZ( + _In_ PPH_RELATIVE_STRINGREF PackedData, + _In_ PVOID CapturedBuffer, + _In_ PVOID *OffsetInBuffer, + _In_ BOOLEAN Multi, + _In_ BOOLEAN AllowNull + ) +{ + SIZE_T offset; + PWCHAR start; + PWCHAR end; + PH_STRINGREF remainingPart; + PH_STRINGREF firstPart; + + offset = (SIZE_T)*OffsetInBuffer; + + if (offset == 0) + { + if (AllowNull) + return STATUS_SUCCESS; + else + return STATUS_ACCESS_VIOLATION; + } + + if (offset >= PackedData->Length) + return STATUS_ACCESS_VIOLATION; + if (offset & 1) + return STATUS_DATATYPE_MISALIGNMENT; + + start = (PWCHAR)PTR_ADD_OFFSET(CapturedBuffer, offset); + end = (PWCHAR)PTR_ADD_OFFSET(CapturedBuffer, (PackedData->Length & -2)); + remainingPart.Buffer = start; + remainingPart.Length = (end - start) * sizeof(WCHAR); + + if (Multi) + { + SIZE_T validatedLength = 0; + + while (PhSplitStringRefAtChar(&remainingPart, 0, &firstPart, &remainingPart)) + { + validatedLength += firstPart.Length + sizeof(WCHAR); + + if (firstPart.Length == 0) + { + *OffsetInBuffer = start; + + return STATUS_SUCCESS; + } + } + } + else + { + if (PhSplitStringRefAtChar(&remainingPart, 0, &firstPart, &remainingPart)) + { + *OffsetInBuffer = start; + + return STATUS_SUCCESS; + } + } + + return STATUS_ACCESS_VIOLATION; +} + +NTSTATUS PhSvcApiChangeServiceConfig2( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING serviceName = NULL; + PVOID info = NULL; + SC_HANDLE serviceHandle = NULL; + PH_RELATIVE_STRINGREF packedData; + PVOID unpackedInfo = NULL; + ACCESS_MASK desiredAccess = SERVICE_CHANGE_CONFIG; + + if (!NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.ChangeServiceConfig2.i.ServiceName, FALSE, &serviceName))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcCaptureBuffer(&Payload->u.ChangeServiceConfig2.i.Info, FALSE, &info))) + goto CleanupExit; + + packedData = Payload->u.ChangeServiceConfig2.i.Info; + + switch (Payload->u.ChangeServiceConfig2.i.InfoLevel) + { + case SERVICE_CONFIG_FAILURE_ACTIONS: + { + LPSERVICE_FAILURE_ACTIONS failureActions; + + if (!NT_SUCCESS(status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_FAILURE_ACTIONS), &failureActions))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcpUnpackStringZ(&packedData, info, &failureActions->lpRebootMsg, FALSE, TRUE))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcpUnpackStringZ(&packedData, info, &failureActions->lpCommand, FALSE, TRUE))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &failureActions->lpsaActions, failureActions->cActions * sizeof(SC_ACTION), __alignof(SC_ACTION), TRUE))) + goto CleanupExit; + + if (failureActions->lpsaActions) + { + ULONG i; + + for (i = 0; i < failureActions->cActions; i++) + { + if (failureActions->lpsaActions[i].Type == SC_ACTION_RESTART) + { + desiredAccess |= SERVICE_START; + break; + } + } + } + + unpackedInfo = failureActions; + } + break; + case SERVICE_CONFIG_DELAYED_AUTO_START_INFO: + status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_DELAYED_AUTO_START_INFO), &unpackedInfo); + break; + case SERVICE_CONFIG_FAILURE_ACTIONS_FLAG: + status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_FAILURE_ACTIONS_FLAG), &unpackedInfo); + break; + case SERVICE_CONFIG_SERVICE_SID_INFO: + status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_SID_INFO), &unpackedInfo); + break; + case SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO: + { + LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; + + if (!NT_SUCCESS(status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_REQUIRED_PRIVILEGES_INFO), &requiredPrivilegesInfo))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcpUnpackStringZ(&packedData, info, &requiredPrivilegesInfo->pmszRequiredPrivileges, TRUE, FALSE))) + goto CleanupExit; + + unpackedInfo = requiredPrivilegesInfo; + } + break; + case SERVICE_CONFIG_PRESHUTDOWN_INFO: + status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_PRESHUTDOWN_INFO), &unpackedInfo); + break; + case SERVICE_CONFIG_TRIGGER_INFO: + { + PSERVICE_TRIGGER_INFO triggerInfo; + ULONG i; + PSERVICE_TRIGGER trigger; + ULONG j; + PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem; + ULONG alignment; + + if (!NT_SUCCESS(status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_TRIGGER_INFO), &triggerInfo))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &triggerInfo->pTriggers, triggerInfo->cTriggers * sizeof(SERVICE_TRIGGER), __alignof(SERVICE_TRIGGER), TRUE))) + goto CleanupExit; + + if (triggerInfo->pTriggers) + { + for (i = 0; i < triggerInfo->cTriggers; i++) + { + trigger = &triggerInfo->pTriggers[i]; + + if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &trigger->pTriggerSubtype, sizeof(GUID), __alignof(GUID), TRUE))) + goto CleanupExit; + if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &trigger->pDataItems, trigger->cDataItems * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), __alignof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), TRUE))) + goto CleanupExit; + + if (trigger->pDataItems) + { + for (j = 0; j < trigger->cDataItems; j++) + { + dataItem = &trigger->pDataItems[j]; + alignment = 1; + + switch (dataItem->dwDataType) + { + case SERVICE_TRIGGER_DATA_TYPE_BINARY: + case SERVICE_TRIGGER_DATA_TYPE_LEVEL: + alignment = sizeof(CHAR); + break; + case SERVICE_TRIGGER_DATA_TYPE_STRING: + alignment = sizeof(WCHAR); + break; + case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY: + case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL: + alignment = sizeof(ULONG64); + break; + } + + if (!NT_SUCCESS(status = PhSvcpUnpackBuffer(&packedData, info, &dataItem->pData, dataItem->cbData, alignment, FALSE))) + goto CleanupExit; + } + } + } + } + + unpackedInfo = triggerInfo; + } + break; + case SERVICE_CONFIG_LAUNCH_PROTECTED: + status = PhSvcpUnpackRoot(&packedData, info, sizeof(SERVICE_LAUNCH_PROTECTED_INFO), &unpackedInfo); + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + if (NT_SUCCESS(status)) + { + assert(unpackedInfo); + + if (!(serviceHandle = PhOpenService(serviceName->Buffer, desiredAccess))) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + if (!ChangeServiceConfig2( + serviceHandle, + Payload->u.ChangeServiceConfig2.i.InfoLevel, + unpackedInfo + )) + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + } + +CleanupExit: + if (serviceHandle) + CloseServiceHandle(serviceHandle); + if (info) + PhFree(info); + if (serviceName) + PhDereferenceObject(serviceName); + + return status; +} + +NTSTATUS PhSvcApiSetTcpEntry( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + static PVOID setTcpEntry = NULL; + + ULONG (__stdcall *localSetTcpEntry)(PVOID TcpRow); + struct + { + ULONG dwState; + ULONG dwLocalAddr; + ULONG dwLocalPort; + ULONG dwRemoteAddr; + ULONG dwRemotePort; + } tcpRow; + ULONG result; + + localSetTcpEntry = setTcpEntry; + + if (!localSetTcpEntry) + { + HMODULE iphlpapiModule; + + iphlpapiModule = LoadLibrary(L"iphlpapi.dll"); + + if (iphlpapiModule) + { + localSetTcpEntry = PhGetDllBaseProcedureAddress(iphlpapiModule, "SetTcpEntry", 0); + + if (localSetTcpEntry) + { + if (_InterlockedExchangePointer(&setTcpEntry, localSetTcpEntry) != NULL) + { + // Another thread got the address of SetTcpEntry already. + // Decrement the reference count of iphlpapi.dll. + FreeLibrary(iphlpapiModule); + } + } + } + } + + if (!localSetTcpEntry) + return STATUS_NOT_SUPPORTED; + + tcpRow.dwState = Payload->u.SetTcpEntry.i.State; + tcpRow.dwLocalAddr = Payload->u.SetTcpEntry.i.LocalAddress; + tcpRow.dwLocalPort = Payload->u.SetTcpEntry.i.LocalPort; + tcpRow.dwRemoteAddr = Payload->u.SetTcpEntry.i.RemoteAddress; + tcpRow.dwRemotePort = Payload->u.SetTcpEntry.i.RemotePort; + result = localSetTcpEntry(&tcpRow); + + return NTSTATUS_FROM_WIN32(result); +} + +NTSTATUS PhSvcApiControlThread( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + HANDLE threadId; + HANDLE threadHandle; + + threadId = Payload->u.ControlThread.i.ThreadId; + + switch (Payload->u.ControlThread.i.Command) + { + case PhSvcControlThreadTerminate: + if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_TERMINATE, threadId))) + { + status = NtTerminateThread(threadHandle, STATUS_SUCCESS); + NtClose(threadHandle); + } + break; + case PhSvcControlThreadSuspend: + if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) + { + status = NtSuspendThread(threadHandle, NULL); + NtClose(threadHandle); + } + break; + case PhSvcControlThreadResume: + if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_SUSPEND_RESUME, threadId))) + { + status = NtResumeThread(threadHandle, NULL); + NtClose(threadHandle); + } + break; + case PhSvcControlThreadIoPriority: + if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_SET_INFORMATION, threadId))) + { + status = PhSetThreadIoPriority(threadHandle, Payload->u.ControlThread.i.Argument); + NtClose(threadHandle); + } + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + return status; +} + +NTSTATUS PhSvcApiAddAccountRight( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PSID accountSid; + PPH_STRING userRight; + LSA_HANDLE policyHandle; + UNICODE_STRING userRightUs; + + if (NT_SUCCESS(status = PhSvcCaptureSid(&Payload->u.AddAccountRight.i.AccountSid, FALSE, &accountSid))) + { + if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.AddAccountRight.i.UserRight, FALSE, &userRight))) + { + PH_AUTO(userRight); + + if (NT_SUCCESS(status = PhOpenLsaPolicy(&policyHandle, POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT, NULL))) + { + PhStringRefToUnicodeString(&userRight->sr, &userRightUs); + status = LsaAddAccountRights(policyHandle, accountSid, &userRightUs, 1); + LsaClose(policyHandle); + } + } + + PhFree(accountSid); + } + + return status; +} + +NTSTATUS PhSvcApiInvokeRunAsService( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PH_RUNAS_SERVICE_PARAMETERS parameters; + PHSVCP_CAPTURED_RUNAS_SERVICE_PARAMETERS capturedParameters; + + if (NT_SUCCESS(status = PhSvcpCaptureRunAsServiceParameters(Client, Payload, ¶meters, &capturedParameters))) + { + if (NT_SUCCESS(status = PhSvcpValidateRunAsServiceParameters(¶meters))) + { + status = PhInvokeRunAsService(¶meters); + } + } + + PhSvcpReleaseRunAsServiceParameters(&capturedParameters); + + return status; +} + +NTSTATUS PhSvcApiIssueMemoryListCommand( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + + status = NtSetSystemInformation( + SystemMemoryListInformation, + &Payload->u.IssueMemoryListCommand.i.Command, + sizeof(SYSTEM_MEMORY_LIST_COMMAND) + ); + + return status; +} + +NTSTATUS PhSvcApiPostMessage( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + if (PostMessage( + Payload->u.PostMessage.i.hWnd, + Payload->u.PostMessage.i.Msg, + Payload->u.PostMessage.i.wParam, + Payload->u.PostMessage.i.lParam + )) + { + return STATUS_SUCCESS; + } + else + { + return PhGetLastWin32ErrorAsNtStatus(); + } +} + +NTSTATUS PhSvcApiSendMessage( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + if (SendMessage( + Payload->u.PostMessage.i.hWnd, + Payload->u.PostMessage.i.Msg, + Payload->u.PostMessage.i.wParam, + Payload->u.PostMessage.i.lParam + )) + { + return STATUS_SUCCESS; + } + else + { + return PhGetLastWin32ErrorAsNtStatus(); + } +} + +NTSTATUS PhSvcApiCreateProcessIgnoreIfeoDebugger( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING fileName; + + if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.CreateProcessIgnoreIfeoDebugger.i.FileName, FALSE, &fileName))) + { + PH_AUTO(fileName); + + if (!PhCreateProcessIgnoreIfeoDebugger(fileName->Buffer)) + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +NTSTATUS PhSvcApiSetServiceSecurity( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + NTSTATUS status; + PPH_STRING serviceName; + PSECURITY_DESCRIPTOR securityDescriptor; + ACCESS_MASK desiredAccess; + SC_HANDLE serviceHandle; + + if (NT_SUCCESS(status = PhSvcCaptureString(&Payload->u.SetServiceSecurity.i.ServiceName, FALSE, &serviceName))) + { + PH_AUTO(serviceName); + + if (NT_SUCCESS(status = PhSvcCaptureSecurityDescriptor(&Payload->u.SetServiceSecurity.i.SecurityDescriptor, FALSE, 0, &securityDescriptor))) + { + desiredAccess = 0; + + if ((Payload->u.SetServiceSecurity.i.SecurityInformation & OWNER_SECURITY_INFORMATION) || + (Payload->u.SetServiceSecurity.i.SecurityInformation & GROUP_SECURITY_INFORMATION)) + { + desiredAccess |= WRITE_OWNER; + } + + if (Payload->u.SetServiceSecurity.i.SecurityInformation & DACL_SECURITY_INFORMATION) + { + desiredAccess |= WRITE_DAC; + } + + if (Payload->u.SetServiceSecurity.i.SecurityInformation & SACL_SECURITY_INFORMATION) + { + desiredAccess |= ACCESS_SYSTEM_SECURITY; + } + + if (serviceHandle = PhOpenService(serviceName->Buffer, desiredAccess)) + { + status = PhSetSeObjectSecurity( + serviceHandle, + SE_SERVICE, + Payload->u.SetServiceSecurity.i.SecurityInformation, + securityDescriptor + ); + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + PhFree(securityDescriptor); + } + } + + return status; +} + +NTSTATUS PhSvcApiWriteMiniDumpProcess( + _In_ PPHSVC_CLIENT Client, + _Inout_ PPHSVC_API_PAYLOAD Payload + ) +{ + if (PhWriteMiniDumpProcess( + UlongToHandle(Payload->u.WriteMiniDumpProcess.i.LocalProcessHandle), + UlongToHandle(Payload->u.WriteMiniDumpProcess.i.ProcessId), + UlongToHandle(Payload->u.WriteMiniDumpProcess.i.LocalFileHandle), + Payload->u.WriteMiniDumpProcess.i.DumpType, + NULL, + NULL, + NULL + )) + { + return STATUS_SUCCESS; + } + else + { + ULONG error; + + error = GetLastError(); + + if (error == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)) + return STATUS_INVALID_PARAMETER; + else + return STATUS_UNSUCCESSFUL; + } +} diff --git a/ProcessHacker/phsvc/svcapiport.c b/ProcessHacker/phsvc/svcapiport.c index 7eae7d306460..62278ed630cf 100644 --- a/ProcessHacker/phsvc/svcapiport.c +++ b/ProcessHacker/phsvc/svcapiport.c @@ -1,318 +1,314 @@ -/* - * Process Hacker - - * server API port - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -NTSTATUS PhSvcApiRequestThreadStart( - _In_ PVOID Parameter - ); - -extern HANDLE PhSvcTimeoutStandbyEventHandle; -extern HANDLE PhSvcTimeoutCancelEventHandle; - -ULONG PhSvcApiThreadContextTlsIndex; -HANDLE PhSvcApiPortHandle; -ULONG PhSvcApiNumberOfClients = 0; - -NTSTATUS PhSvcApiPortInitialization( - _In_ PUNICODE_STRING PortName - ) -{ - static SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; - - NTSTATUS status; - OBJECT_ATTRIBUTES objectAttributes; - PSECURITY_DESCRIPTOR securityDescriptor; - ULONG sdAllocationLength; - UCHAR administratorsSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2]; - PSID administratorsSid; - PACL dacl; - ULONG i; - HANDLE threadHandle; - - // Create the API port. - - administratorsSid = (PSID)administratorsSidBuffer; - RtlInitializeSid(administratorsSid, &ntAuthority, 2); - *RtlSubAuthoritySid(administratorsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID; - *RtlSubAuthoritySid(administratorsSid, 1) = DOMAIN_ALIAS_RID_ADMINS; - - sdAllocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + - (ULONG)sizeof(ACL) + - (ULONG)sizeof(ACCESS_ALLOWED_ACE) + - RtlLengthSid(administratorsSid) + - (ULONG)sizeof(ACCESS_ALLOWED_ACE) + - RtlLengthSid(&PhSeEveryoneSid); - - securityDescriptor = PhAllocate(sdAllocationLength); - dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); - - RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); - RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); - RtlAddAccessAllowedAce(dacl, ACL_REVISION, PORT_ALL_ACCESS, administratorsSid); - RtlAddAccessAllowedAce(dacl, ACL_REVISION, PORT_CONNECT, &PhSeEveryoneSid); - RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE); - - InitializeObjectAttributes( - &objectAttributes, - PortName, - OBJ_CASE_INSENSITIVE, - NULL, - securityDescriptor - ); - - status = NtCreatePort( - &PhSvcApiPortHandle, - &objectAttributes, - sizeof(PHSVC_API_CONNECTINFO), - PhIsExecutingInWow64() ? sizeof(PHSVC_API_MSG64) : sizeof(PHSVC_API_MSG), - 0 - ); - PhFree(securityDescriptor); - - if (!NT_SUCCESS(status)) - return status; - - // Start the API threads. - - PhSvcApiThreadContextTlsIndex = TlsAlloc(); - - for (i = 0; i < 2; i++) - { - threadHandle = PhCreateThread(0, PhSvcApiRequestThreadStart, NULL); - - if (threadHandle) - NtClose(threadHandle); - } - - return status; -} - -PPHSVC_THREAD_CONTEXT PhSvcGetCurrentThreadContext( - VOID - ) -{ - return (PPHSVC_THREAD_CONTEXT)TlsGetValue(PhSvcApiThreadContextTlsIndex); -} - -NTSTATUS PhSvcApiRequestThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - NTSTATUS status; - PHSVC_THREAD_CONTEXT threadContext; - HANDLE portHandle; - PVOID portContext; - SIZE_T messageSize; - PPORT_MESSAGE receiveMessage; - PPORT_MESSAGE replyMessage; - CSHORT messageType; - PPHSVC_CLIENT client; - PPHSVC_API_PAYLOAD payload; - - PhInitializeAutoPool(&autoPool); - - threadContext.CurrentClient = NULL; - threadContext.OldClient = NULL; - - TlsSetValue(PhSvcApiThreadContextTlsIndex, &threadContext); - - portHandle = PhSvcApiPortHandle; - messageSize = PhIsExecutingInWow64() ? sizeof(PHSVC_API_MSG64) : sizeof(PHSVC_API_MSG); - receiveMessage = PhAllocate(messageSize); - replyMessage = NULL; - - while (TRUE) - { - status = NtReplyWaitReceivePort( - portHandle, - &portContext, - replyMessage, - receiveMessage - ); - - portHandle = PhSvcApiPortHandle; - replyMessage = NULL; - - if (!NT_SUCCESS(status)) - { - // Client probably died. - continue; - } - - messageType = receiveMessage->u2.s2.Type; - - if (messageType == LPC_CONNECTION_REQUEST) - { - PhSvcHandleConnectionRequest(receiveMessage); - continue; - } - - if (!portContext) - continue; - - client = portContext; - threadContext.CurrentClient = client; - PhWaitForEvent(&client->ReadyEvent, NULL); - - if (messageType == LPC_REQUEST) - { - if (PhIsExecutingInWow64()) - payload = &((PPHSVC_API_MSG64)receiveMessage)->p; - else - payload = &((PPHSVC_API_MSG)receiveMessage)->p; - - PhSvcDispatchApiCall(client, payload, &portHandle); - replyMessage = receiveMessage; - } - else if (messageType == LPC_PORT_CLOSED) - { - PhDereferenceObject(client); - - if (_InterlockedDecrement(&PhSvcApiNumberOfClients) == 0) - { - NtSetEvent(PhSvcTimeoutStandbyEventHandle, NULL); - } - } - - assert(!threadContext.OldClient); - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); -} - -VOID PhSvcHandleConnectionRequest( - _In_ PPORT_MESSAGE PortMessage - ) -{ - NTSTATUS status; - PPHSVC_API_MSG message; - PPHSVC_API_MSG64 message64; - CLIENT_ID clientId; - PPHSVC_CLIENT client; - HANDLE portHandle; - REMOTE_PORT_VIEW clientView; - REMOTE_PORT_VIEW64 clientView64; - PREMOTE_PORT_VIEW actualClientView; - - message = (PPHSVC_API_MSG)PortMessage; - message64 = (PPHSVC_API_MSG64)PortMessage; - - if (PhIsExecutingInWow64()) - { - clientId.UniqueProcess = (HANDLE)message64->h.ClientId.UniqueProcess; - clientId.UniqueThread = (HANDLE)message64->h.ClientId.UniqueThread; - } - else - { - PPH_STRING referenceFileName; - PPH_STRING remoteFileName; - - clientId = message->h.ClientId; - - // Make sure that the remote process is Process Hacker itself and not some other program. - - referenceFileName = NULL; - PhGetProcessImageFileNameByProcessId(NtCurrentProcessId(), &referenceFileName); - PH_AUTO(referenceFileName); - - remoteFileName = NULL; - PhGetProcessImageFileNameByProcessId(clientId.UniqueProcess, &remoteFileName); - PH_AUTO(remoteFileName); - - if (!referenceFileName || !remoteFileName || !PhEqualString(referenceFileName, remoteFileName, TRUE)) - { - NtAcceptConnectPort(&portHandle, NULL, PortMessage, FALSE, NULL, NULL); - return; - } - } - - client = PhSvcCreateClient(&clientId); - - if (!client) - { - NtAcceptConnectPort(&portHandle, NULL, PortMessage, FALSE, NULL, NULL); - return; - } - - if (PhIsExecutingInWow64()) - { - message64->p.ConnectInfo.ServerProcessId = HandleToUlong(NtCurrentProcessId()); - - clientView64.Length = sizeof(REMOTE_PORT_VIEW64); - clientView64.ViewSize = 0; - clientView64.ViewBase = 0; - actualClientView = (PREMOTE_PORT_VIEW)&clientView64; - } - else - { - message->p.ConnectInfo.ServerProcessId = HandleToUlong(NtCurrentProcessId()); - - clientView.Length = sizeof(REMOTE_PORT_VIEW); - clientView.ViewSize = 0; - clientView.ViewBase = NULL; - actualClientView = &clientView; - } - - status = NtAcceptConnectPort( - &portHandle, - client, - PortMessage, - TRUE, - NULL, - actualClientView - ); - - if (!NT_SUCCESS(status)) - { - PhDereferenceObject(client); - return; - } - - // IMPORTANT: Since Vista, NtCompleteConnectPort does not do anything and simply returns STATUS_SUCCESS. - // We will call it anyway (for completeness), but we need to use an event to ensure that other threads don't try - // to process requests before we have finished setting up the client object. - - client->PortHandle = portHandle; - - if (PhIsExecutingInWow64()) - { - client->ClientViewBase = (PVOID)clientView64.ViewBase; - client->ClientViewLimit = (PCHAR)clientView64.ViewBase + (ULONG)clientView64.ViewSize; - } - else - { - client->ClientViewBase = clientView.ViewBase; - client->ClientViewLimit = (PCHAR)clientView.ViewBase + clientView.ViewSize; - } - - NtCompleteConnectPort(portHandle); - PhSetEvent(&client->ReadyEvent); - - if (_InterlockedIncrement(&PhSvcApiNumberOfClients) == 1) - { - NtSetEvent(PhSvcTimeoutCancelEventHandle, NULL); - } -} +/* + * Process Hacker - + * server API port + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +NTSTATUS PhSvcApiRequestThreadStart( + _In_ PVOID Parameter + ); + +extern HANDLE PhSvcTimeoutStandbyEventHandle; +extern HANDLE PhSvcTimeoutCancelEventHandle; + +ULONG PhSvcApiThreadContextTlsIndex; +HANDLE PhSvcApiPortHandle; +ULONG PhSvcApiNumberOfClients = 0; + +NTSTATUS PhSvcApiPortInitialization( + _In_ PUNICODE_STRING PortName + ) +{ + static SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; + + NTSTATUS status; + OBJECT_ATTRIBUTES objectAttributes; + PSECURITY_DESCRIPTOR securityDescriptor; + ULONG sdAllocationLength; + UCHAR administratorsSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2]; + PSID administratorsSid; + PACL dacl; + ULONG i; + + // Create the API port. + + administratorsSid = (PSID)administratorsSidBuffer; + RtlInitializeSid(administratorsSid, &ntAuthority, 2); + *RtlSubAuthoritySid(administratorsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID; + *RtlSubAuthoritySid(administratorsSid, 1) = DOMAIN_ALIAS_RID_ADMINS; + + sdAllocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + + (ULONG)sizeof(ACL) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(administratorsSid) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(&PhSeEveryoneSid); + + securityDescriptor = PhAllocate(sdAllocationLength); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); + + RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, PORT_ALL_ACCESS, administratorsSid); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, PORT_CONNECT, &PhSeEveryoneSid); + RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE); + + InitializeObjectAttributes( + &objectAttributes, + PortName, + OBJ_CASE_INSENSITIVE, + NULL, + securityDescriptor + ); + + status = NtCreatePort( + &PhSvcApiPortHandle, + &objectAttributes, + sizeof(PHSVC_API_CONNECTINFO), + PhIsExecutingInWow64() ? sizeof(PHSVC_API_MSG64) : sizeof(PHSVC_API_MSG), + 0 + ); + PhFree(securityDescriptor); + + if (!NT_SUCCESS(status)) + return status; + + // Start the API threads. + + PhSvcApiThreadContextTlsIndex = TlsAlloc(); + + for (i = 0; i < 2; i++) + { + PhCreateThread2(PhSvcApiRequestThreadStart, NULL); + } + + return status; +} + +PPHSVC_THREAD_CONTEXT PhSvcGetCurrentThreadContext( + VOID + ) +{ + return (PPHSVC_THREAD_CONTEXT)TlsGetValue(PhSvcApiThreadContextTlsIndex); +} + +NTSTATUS PhSvcApiRequestThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + NTSTATUS status; + PHSVC_THREAD_CONTEXT threadContext; + HANDLE portHandle; + PVOID portContext; + SIZE_T messageSize; + PPORT_MESSAGE receiveMessage; + PPORT_MESSAGE replyMessage; + CSHORT messageType; + PPHSVC_CLIENT client; + PPHSVC_API_PAYLOAD payload; + + PhInitializeAutoPool(&autoPool); + + threadContext.CurrentClient = NULL; + threadContext.OldClient = NULL; + + TlsSetValue(PhSvcApiThreadContextTlsIndex, &threadContext); + + portHandle = PhSvcApiPortHandle; + messageSize = PhIsExecutingInWow64() ? sizeof(PHSVC_API_MSG64) : sizeof(PHSVC_API_MSG); + receiveMessage = PhAllocate(messageSize); + replyMessage = NULL; + + while (TRUE) + { + status = NtReplyWaitReceivePort( + portHandle, + &portContext, + replyMessage, + receiveMessage + ); + + portHandle = PhSvcApiPortHandle; + replyMessage = NULL; + + if (!NT_SUCCESS(status)) + { + // Client probably died. + continue; + } + + messageType = receiveMessage->u2.s2.Type; + + if (messageType == LPC_CONNECTION_REQUEST) + { + PhSvcHandleConnectionRequest(receiveMessage); + continue; + } + + if (!portContext) + continue; + + client = portContext; + threadContext.CurrentClient = client; + PhWaitForEvent(&client->ReadyEvent, NULL); + + if (messageType == LPC_REQUEST) + { + if (PhIsExecutingInWow64()) + payload = &((PPHSVC_API_MSG64)receiveMessage)->p; + else + payload = &((PPHSVC_API_MSG)receiveMessage)->p; + + PhSvcDispatchApiCall(client, payload, &portHandle); + replyMessage = receiveMessage; + } + else if (messageType == LPC_PORT_CLOSED) + { + PhDereferenceObject(client); + + if (_InterlockedDecrement(&PhSvcApiNumberOfClients) == 0) + { + NtSetEvent(PhSvcTimeoutStandbyEventHandle, NULL); + } + } + + assert(!threadContext.OldClient); + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); +} + +VOID PhSvcHandleConnectionRequest( + _In_ PPORT_MESSAGE PortMessage + ) +{ + NTSTATUS status; + PPHSVC_API_MSG message; + PPHSVC_API_MSG64 message64; + CLIENT_ID clientId; + PPHSVC_CLIENT client; + HANDLE portHandle; + REMOTE_PORT_VIEW clientView; + REMOTE_PORT_VIEW64 clientView64; + PREMOTE_PORT_VIEW actualClientView; + + message = (PPHSVC_API_MSG)PortMessage; + message64 = (PPHSVC_API_MSG64)PortMessage; + + if (PhIsExecutingInWow64()) + { + clientId.UniqueProcess = (HANDLE)message64->h.ClientId.UniqueProcess; + clientId.UniqueThread = (HANDLE)message64->h.ClientId.UniqueThread; + } + else + { + PPH_STRING referenceFileName; + PPH_STRING remoteFileName; + + clientId = message->h.ClientId; + + // Make sure that the remote process is Process Hacker itself and not some other program. + + referenceFileName = NULL; + PhGetProcessImageFileNameByProcessId(NtCurrentProcessId(), &referenceFileName); + PH_AUTO(referenceFileName); + + remoteFileName = NULL; + PhGetProcessImageFileNameByProcessId(clientId.UniqueProcess, &remoteFileName); + PH_AUTO(remoteFileName); + + if (!referenceFileName || !remoteFileName || !PhEqualString(referenceFileName, remoteFileName, TRUE)) + { + NtAcceptConnectPort(&portHandle, NULL, PortMessage, FALSE, NULL, NULL); + return; + } + } + + client = PhSvcCreateClient(&clientId); + + if (!client) + { + NtAcceptConnectPort(&portHandle, NULL, PortMessage, FALSE, NULL, NULL); + return; + } + + if (PhIsExecutingInWow64()) + { + message64->p.ConnectInfo.ServerProcessId = HandleToUlong(NtCurrentProcessId()); + + clientView64.Length = sizeof(REMOTE_PORT_VIEW64); + clientView64.ViewSize = 0; + clientView64.ViewBase = 0; + actualClientView = (PREMOTE_PORT_VIEW)&clientView64; + } + else + { + message->p.ConnectInfo.ServerProcessId = HandleToUlong(NtCurrentProcessId()); + + clientView.Length = sizeof(REMOTE_PORT_VIEW); + clientView.ViewSize = 0; + clientView.ViewBase = NULL; + actualClientView = &clientView; + } + + status = NtAcceptConnectPort( + &portHandle, + client, + PortMessage, + TRUE, + NULL, + actualClientView + ); + + if (!NT_SUCCESS(status)) + { + PhDereferenceObject(client); + return; + } + + // IMPORTANT: Since Vista, NtCompleteConnectPort does not do anything and simply returns STATUS_SUCCESS. + // We will call it anyway (for completeness), but we need to use an event to ensure that other threads don't try + // to process requests before we have finished setting up the client object. + + client->PortHandle = portHandle; + + if (PhIsExecutingInWow64()) + { + client->ClientViewBase = (PVOID)clientView64.ViewBase; + client->ClientViewLimit = PTR_ADD_OFFSET(clientView64.ViewBase, clientView64.ViewSize); + } + else + { + client->ClientViewBase = clientView.ViewBase; + client->ClientViewLimit = PTR_ADD_OFFSET(clientView.ViewBase, clientView.ViewSize); + } + + NtCompleteConnectPort(portHandle); + PhSetEvent(&client->ReadyEvent); + + if (_InterlockedIncrement(&PhSvcApiNumberOfClients) == 1) + { + NtSetEvent(PhSvcTimeoutCancelEventHandle, NULL); + } +} diff --git a/ProcessHacker/phsvc/svcclient.c b/ProcessHacker/phsvc/svcclient.c index 537d30440a6d..a8d7a9fbb98c 100644 --- a/ProcessHacker/phsvc/svcclient.c +++ b/ProcessHacker/phsvc/svcclient.c @@ -1,159 +1,156 @@ -/* - * Process Hacker - - * server client - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -VOID NTAPI PhSvcpClientDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -PPH_OBJECT_TYPE PhSvcClientType; -LIST_ENTRY PhSvcClientListHead; -PH_QUEUED_LOCK PhSvcClientListLock = PH_QUEUED_LOCK_INIT; - -NTSTATUS PhSvcClientInitialization( - VOID - ) -{ - PhSvcClientType = PhCreateObjectType(L"Client", 0, PhSvcpClientDeleteProcedure); - InitializeListHead(&PhSvcClientListHead); - - return STATUS_SUCCESS; -} - -PPHSVC_CLIENT PhSvcCreateClient( - _In_opt_ PCLIENT_ID ClientId - ) -{ - PPHSVC_CLIENT client; - - client = PhCreateObject(sizeof(PHSVC_CLIENT), PhSvcClientType); - memset(client, 0, sizeof(PHSVC_CLIENT)); - PhInitializeEvent(&client->ReadyEvent); - - if (ClientId) - client->ClientId = *ClientId; - - PhAcquireQueuedLockExclusive(&PhSvcClientListLock); - InsertTailList(&PhSvcClientListHead, &client->ListEntry); - PhReleaseQueuedLockExclusive(&PhSvcClientListLock); - - return client; -} - -VOID NTAPI PhSvcpClientDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPHSVC_CLIENT client = Object; - - PhAcquireQueuedLockExclusive(&PhSvcClientListLock); - RemoveEntryList(&client->ListEntry); - PhReleaseQueuedLockExclusive(&PhSvcClientListLock); - - if (client->PortHandle) - NtClose(client->PortHandle); -} - -PPHSVC_CLIENT PhSvcReferenceClientByClientId( - _In_ PCLIENT_ID ClientId - ) -{ - PLIST_ENTRY listEntry; - PPHSVC_CLIENT client = NULL; - - PhAcquireQueuedLockShared(&PhSvcClientListLock); - - listEntry = PhSvcClientListHead.Flink; - - while (listEntry != &PhSvcClientListHead) - { - client = CONTAINING_RECORD(listEntry, PHSVC_CLIENT, ListEntry); - - if (ClientId->UniqueThread) - { - if ( - client->ClientId.UniqueProcess == ClientId->UniqueProcess && - client->ClientId.UniqueThread == ClientId->UniqueThread - ) - { - break; - } - } - else - { - if (client->ClientId.UniqueProcess == ClientId->UniqueProcess) - break; - } - - client = NULL; - - listEntry = listEntry->Flink; - } - - if (client) - { - if (!PhReferenceObjectSafe(client)) - client = NULL; - } - - PhReleaseQueuedLockShared(&PhSvcClientListLock); - - return client; -} - -PPHSVC_CLIENT PhSvcGetCurrentClient( - VOID - ) -{ - return PhSvcGetCurrentThreadContext()->CurrentClient; -} - -BOOLEAN PhSvcAttachClient( - _In_ PPHSVC_CLIENT Client - ) -{ - PPHSVC_THREAD_CONTEXT threadContext = PhSvcGetCurrentThreadContext(); - - if (threadContext->OldClient) - return FALSE; - - PhReferenceObject(Client); - threadContext->OldClient = threadContext->CurrentClient; - threadContext->CurrentClient = Client; - - return TRUE; -} - -VOID PhSvcDetachClient( - _In_ PPHSVC_CLIENT Client - ) -{ - PPHSVC_THREAD_CONTEXT threadContext = PhSvcGetCurrentThreadContext(); - - PhDereferenceObject(threadContext->CurrentClient); - threadContext->CurrentClient = threadContext->OldClient; - threadContext->OldClient = NULL; -} +/* + * Process Hacker - + * server client + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +VOID NTAPI PhSvcpClientDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +PPH_OBJECT_TYPE PhSvcClientType = NULL; +LIST_ENTRY PhSvcClientListHead = { &PhSvcClientListHead, &PhSvcClientListHead }; +PH_QUEUED_LOCK PhSvcClientListLock = PH_QUEUED_LOCK_INIT; + +PPHSVC_CLIENT PhSvcCreateClient( + _In_opt_ PCLIENT_ID ClientId + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPHSVC_CLIENT client; + + if (PhBeginInitOnce(&initOnce)) + { + PhSvcClientType = PhCreateObjectType(L"PhSvcClient", 0, PhSvcpClientDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + client = PhCreateObject(sizeof(PHSVC_CLIENT), PhSvcClientType); + memset(client, 0, sizeof(PHSVC_CLIENT)); + PhInitializeEvent(&client->ReadyEvent); + + if (ClientId) + client->ClientId = *ClientId; + + PhAcquireQueuedLockExclusive(&PhSvcClientListLock); + InsertTailList(&PhSvcClientListHead, &client->ListEntry); + PhReleaseQueuedLockExclusive(&PhSvcClientListLock); + + return client; +} + +VOID NTAPI PhSvcpClientDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPHSVC_CLIENT client = Object; + + PhAcquireQueuedLockExclusive(&PhSvcClientListLock); + RemoveEntryList(&client->ListEntry); + PhReleaseQueuedLockExclusive(&PhSvcClientListLock); + + if (client->PortHandle) + NtClose(client->PortHandle); +} + +PPHSVC_CLIENT PhSvcReferenceClientByClientId( + _In_ PCLIENT_ID ClientId + ) +{ + PLIST_ENTRY listEntry; + PPHSVC_CLIENT client = NULL; + + PhAcquireQueuedLockShared(&PhSvcClientListLock); + + listEntry = PhSvcClientListHead.Flink; + + while (listEntry != &PhSvcClientListHead) + { + client = CONTAINING_RECORD(listEntry, PHSVC_CLIENT, ListEntry); + + if (ClientId->UniqueThread) + { + if ( + client->ClientId.UniqueProcess == ClientId->UniqueProcess && + client->ClientId.UniqueThread == ClientId->UniqueThread + ) + { + break; + } + } + else + { + if (client->ClientId.UniqueProcess == ClientId->UniqueProcess) + break; + } + + client = NULL; + + listEntry = listEntry->Flink; + } + + if (client) + { + if (!PhReferenceObjectSafe(client)) + client = NULL; + } + + PhReleaseQueuedLockShared(&PhSvcClientListLock); + + return client; +} + +PPHSVC_CLIENT PhSvcGetCurrentClient( + VOID + ) +{ + return PhSvcGetCurrentThreadContext()->CurrentClient; +} + +BOOLEAN PhSvcAttachClient( + _In_ PPHSVC_CLIENT Client + ) +{ + PPHSVC_THREAD_CONTEXT threadContext = PhSvcGetCurrentThreadContext(); + + if (threadContext->OldClient) + return FALSE; + + PhReferenceObject(Client); + threadContext->OldClient = threadContext->CurrentClient; + threadContext->CurrentClient = Client; + + return TRUE; +} + +VOID PhSvcDetachClient( + _In_ PPHSVC_CLIENT Client + ) +{ + PPHSVC_THREAD_CONTEXT threadContext = PhSvcGetCurrentThreadContext(); + + PhDereferenceObject(threadContext->CurrentClient); + threadContext->CurrentClient = threadContext->OldClient; + threadContext->OldClient = NULL; +} diff --git a/ProcessHacker/phsvc/svcmain.c b/ProcessHacker/phsvc/svcmain.c index dfc9685e4198..d02c423092a3 100644 --- a/ProcessHacker/phsvc/svcmain.c +++ b/ProcessHacker/phsvc/svcmain.c @@ -1,111 +1,103 @@ -/* - * Process Hacker - - * server - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -HANDLE PhSvcTimeoutStandbyEventHandle; -HANDLE PhSvcTimeoutCancelEventHandle; - -NTSTATUS PhSvcMain( - _In_opt_ PUNICODE_STRING PortName, - _In_opt_ PLARGE_INTEGER Timeout, - _Inout_opt_ PPHSVC_STOP Stop - ) -{ - NTSTATUS status; - UNICODE_STRING portName; - LARGE_INTEGER timeout; - - if (!PortName) - { - if (PhIsExecutingInWow64()) - RtlInitUnicodeString(&portName, PHSVC_WOW64_PORT_NAME); - else - RtlInitUnicodeString(&portName, PHSVC_PORT_NAME); - - PortName = &portName; - } - - if (!Timeout) - { - timeout.QuadPart = -15 * PH_TIMEOUT_SEC; - Timeout = &timeout; - } - - if (!NT_SUCCESS(status = PhSvcClientInitialization())) - return status; - if (!NT_SUCCESS(status = PhSvcApiInitialization())) - return status; - if (!NT_SUCCESS(status = PhSvcApiPortInitialization(PortName))) - return status; - - if (!NT_SUCCESS(status = NtCreateEvent(&PhSvcTimeoutStandbyEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, TRUE))) - return status; - - if (!NT_SUCCESS(status = NtCreateEvent(&PhSvcTimeoutCancelEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) - { - NtClose(PhSvcTimeoutStandbyEventHandle); - return status; - } - - if (Stop) - { - Stop->Event1 = PhSvcTimeoutStandbyEventHandle; - Stop->Event2 = PhSvcTimeoutCancelEventHandle; - MemoryBarrier(); - - if (Stop->Stop) - return STATUS_SUCCESS; - } - - while (TRUE) - { - NtWaitForSingleObject(PhSvcTimeoutStandbyEventHandle, FALSE, NULL); - - if (Stop && Stop->Stop) - break; - - status = NtWaitForSingleObject(PhSvcTimeoutCancelEventHandle, FALSE, Timeout); - - if (Stop && Stop->Stop) - break; - if (status == STATUS_TIMEOUT) - break; - - // A client connected, so we wait on the standby event again. - } - - return status; -} - -VOID PhSvcStop( - _Inout_ PPHSVC_STOP Stop - ) -{ - Stop->Stop = TRUE; - - if (Stop->Event1) - NtSetEvent(Stop->Event1, NULL); - if (Stop->Event2) - NtSetEvent(Stop->Event2, NULL); -} +/* + * Process Hacker - + * server + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +HANDLE PhSvcTimeoutStandbyEventHandle = NULL; +HANDLE PhSvcTimeoutCancelEventHandle = NULL; + +NTSTATUS PhSvcMain( + _In_opt_ PPH_STRING PortName, + _Inout_opt_ PPHSVC_STOP Stop + ) +{ + NTSTATUS status; + UNICODE_STRING portName; + LARGE_INTEGER timeout; + + if (PortName) + { + PhStringRefToUnicodeString(&PortName->sr, &portName); + } + else + { + if (PhIsExecutingInWow64()) + RtlInitUnicodeString(&portName, PHSVC_WOW64_PORT_NAME); + else + RtlInitUnicodeString(&portName, PHSVC_PORT_NAME); + } + + if (!NT_SUCCESS(status = PhSvcApiPortInitialization(&portName))) + return status; + + if (!NT_SUCCESS(status = NtCreateEvent(&PhSvcTimeoutStandbyEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, TRUE))) + return status; + + if (!NT_SUCCESS(status = NtCreateEvent(&PhSvcTimeoutCancelEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) + { + NtClose(PhSvcTimeoutStandbyEventHandle); + return status; + } + + if (Stop) + { + Stop->Event1 = PhSvcTimeoutStandbyEventHandle; + Stop->Event2 = PhSvcTimeoutCancelEventHandle; + MemoryBarrier(); + + if (Stop->Stop) + return STATUS_SUCCESS; + } + + while (TRUE) + { + NtWaitForSingleObject(PhSvcTimeoutStandbyEventHandle, FALSE, NULL); + + if (Stop && Stop->Stop) + break; + + status = NtWaitForSingleObject(PhSvcTimeoutCancelEventHandle, FALSE, PhTimeoutFromMilliseconds(&timeout, 10 * 1000)); + + if (Stop && Stop->Stop) + break; + if (status == STATUS_TIMEOUT) + break; + + // A client connected, so we wait on the standby event again. + } + + return status; +} + +VOID PhSvcStop( + _Inout_ PPHSVC_STOP Stop + ) +{ + Stop->Stop = TRUE; + + if (Stop->Event1) + NtSetEvent(Stop->Event1, NULL); + if (Stop->Event2) + NtSetEvent(Stop->Event2, NULL); +} diff --git a/ProcessHacker/plugin.c b/ProcessHacker/plugin.c index 0af42c05aacc..1af19b6397f5 100644 --- a/ProcessHacker/plugin.c +++ b/ProcessHacker/plugin.c @@ -1,1043 +1,1065 @@ -/* - * Process Hacker - - * plugin support - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -typedef struct _PHP_PLUGIN_LOAD_ERROR -{ - PPH_STRING FileName; - PPH_STRING ErrorMessage; -} PHP_PLUGIN_LOAD_ERROR, *PPHP_PLUGIN_LOAD_ERROR; - -typedef struct _PHP_PLUGIN_MENU_HOOK -{ - PPH_PLUGIN Plugin; - PVOID Context; -} PHP_PLUGIN_MENU_HOOK, *PPHP_PLUGIN_MENU_HOOK; - -INT NTAPI PhpPluginsCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ); - -BOOLEAN PhLoadPlugin( - _In_ PPH_STRING FileName - ); - -VOID PhpExecuteCallbackForAllPlugins( - _In_ PH_PLUGIN_CALLBACK Callback, - _In_ BOOLEAN StartupParameters - ); - -PH_AVL_TREE PhPluginsByName = PH_AVL_TREE_INIT(PhpPluginsCompareFunction); - -static PH_CALLBACK GeneralCallbacks[GeneralCallbackMaximum]; -static PPH_STRING PluginsDirectory; -static PPH_LIST LoadErrors; -static ULONG NextPluginId = IDPLUGINS + 1; - -VOID PhPluginsInitialization( - VOID - ) -{ - ULONG i; - - for (i = 0; i < GeneralCallbackMaximum; i++) - PhInitializeCallback(&GeneralCallbacks[i]); -} - -INT NTAPI PhpPluginsCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ) -{ - PPH_PLUGIN plugin1 = CONTAINING_RECORD(Links1, PH_PLUGIN, Links); - PPH_PLUGIN plugin2 = CONTAINING_RECORD(Links2, PH_PLUGIN, Links); - - return PhCompareStringRef(&plugin1->Name, &plugin2->Name, FALSE); -} - -BOOLEAN PhpLocateDisabledPlugin( - _In_ PPH_STRING List, - _In_ PPH_STRINGREF BaseName, - _Out_opt_ PULONG FoundIndex - ) -{ - PH_STRINGREF namePart; - PH_STRINGREF remainingPart; - - remainingPart = List->sr; - - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, '|', &namePart, &remainingPart); - - if (PhEqualStringRef(&namePart, BaseName, TRUE)) - { - if (FoundIndex) - *FoundIndex = (ULONG)(namePart.Buffer - List->Buffer); - - return TRUE; - } - } - - return FALSE; -} - -BOOLEAN PhIsPluginDisabled( - _In_ PPH_STRINGREF BaseName - ) -{ - BOOLEAN found; - PPH_STRING disabled; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - found = PhpLocateDisabledPlugin(disabled, BaseName, NULL); - PhDereferenceObject(disabled); - - return found; -} - -VOID PhSetPluginDisabled( - _In_ PPH_STRINGREF BaseName, - _In_ BOOLEAN Disable - ) -{ - BOOLEAN found; - PPH_STRING disabled; - ULONG foundIndex; - PPH_STRING newDisabled; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - - found = PhpLocateDisabledPlugin(disabled, BaseName, &foundIndex); - - if (Disable && !found) - { - // We need to add the plugin to the disabled list. - - if (disabled->Length != 0) - { - // We have other disabled plugins. Append a pipe character followed by the plugin name. - newDisabled = PhCreateStringEx(NULL, disabled->Length + sizeof(WCHAR) + BaseName->Length); - memcpy(newDisabled->Buffer, disabled->Buffer, disabled->Length); - newDisabled->Buffer[disabled->Length / 2] = '|'; - memcpy(&newDisabled->Buffer[disabled->Length / 2 + 1], BaseName->Buffer, BaseName->Length); - PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); - PhDereferenceObject(newDisabled); - } - else - { - // This is the first disabled plugin. - PhSetStringSetting2(L"DisabledPlugins", BaseName); - } - } - else if (!Disable && found) - { - ULONG removeCount; - - // We need to remove the plugin from the disabled list. - - removeCount = (ULONG)BaseName->Length / 2; - - if (foundIndex + (ULONG)BaseName->Length / 2 < (ULONG)disabled->Length / 2) - { - // Remove the following pipe character as well. - removeCount++; - } - else if (foundIndex != 0) - { - // Remove the preceding pipe character as well. - foundIndex--; - removeCount++; - } - - newDisabled = PhCreateStringEx(NULL, disabled->Length - removeCount * sizeof(WCHAR)); - memcpy(newDisabled->Buffer, disabled->Buffer, foundIndex * sizeof(WCHAR)); - memcpy(&newDisabled->Buffer[foundIndex], &disabled->Buffer[foundIndex + removeCount], - disabled->Length - removeCount * sizeof(WCHAR) - foundIndex * sizeof(WCHAR)); - PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); - PhDereferenceObject(newDisabled); - } - - PhDereferenceObject(disabled); -} - -static BOOLEAN EnumPluginsDirectoryCallback( - _In_ PFILE_DIRECTORY_INFORMATION Information, - _In_opt_ PVOID Context - ) -{ - PH_STRINGREF baseName; - PPH_STRING fileName; - - baseName.Buffer = Information->FileName; - baseName.Length = Information->FileNameLength; - - if (PhEndsWithStringRef2(&baseName, L".dll", TRUE)) - { - if (!PhIsPluginDisabled(&baseName)) - { - fileName = PhCreateStringEx(NULL, PluginsDirectory->Length + Information->FileNameLength); - memcpy(fileName->Buffer, PluginsDirectory->Buffer, PluginsDirectory->Length); - memcpy(&fileName->Buffer[PluginsDirectory->Length / 2], Information->FileName, Information->FileNameLength); - - PhLoadPlugin(fileName); - - PhDereferenceObject(fileName); - } - } - - return TRUE; -} - -/** - * Loads plugins from the default plugins directory. - */ -VOID PhLoadPlugins( - VOID - ) -{ - HANDLE pluginsDirectoryHandle; - PPH_STRING pluginsDirectory; - - pluginsDirectory = PhGetStringSetting(L"PluginsDirectory"); - - if (RtlDetermineDosPathNameType_U(pluginsDirectory->Buffer) == RtlPathTypeRelative) - { - // Not absolute. Make sure it is. - PluginsDirectory = PhConcatStrings(4, PhApplicationDirectory->Buffer, L"\\", pluginsDirectory->Buffer, L"\\"); - PhDereferenceObject(pluginsDirectory); - } - else - { - PluginsDirectory = pluginsDirectory; - } - - if (NT_SUCCESS(PhCreateFileWin32( - &pluginsDirectoryHandle, - PluginsDirectory->Buffer, - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ, - FILE_OPEN, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*.dll"); - - PhEnumDirectoryFile(pluginsDirectoryHandle, &pattern, EnumPluginsDirectoryCallback, NULL); - NtClose(pluginsDirectoryHandle); - } - - // Handle load errors. - // In certain startup modes we want to ignore all plugin load errors. - if (LoadErrors && LoadErrors->Count != 0 && !PhStartupParameters.PhSvc) - { - PH_STRING_BUILDER sb; - ULONG i; - PPHP_PLUGIN_LOAD_ERROR loadError; - PPH_STRING baseName; - - PhInitializeStringBuilder(&sb, 100); - PhAppendStringBuilder2(&sb, L"Unable to load the following plugin(s):\n\n"); - - for (i = 0; i < LoadErrors->Count; i++) - { - loadError = LoadErrors->Items[i]; - baseName = PhGetBaseName(loadError->FileName); - PhAppendFormatStringBuilder(&sb, L"%s: %s\n", - baseName->Buffer, PhGetStringOrDefault(loadError->ErrorMessage, L"An unknown error occurred.")); - PhDereferenceObject(baseName); - } - - PhAppendStringBuilder2(&sb, L"\nDo you want to disable the above plugin(s)?"); - - if (PhShowMessage( - NULL, - MB_ICONERROR | MB_YESNO, - sb.String->Buffer - ) == IDYES) - { - ULONG i; - - for (i = 0; i < LoadErrors->Count; i++) - { - loadError = LoadErrors->Items[i]; - baseName = PhGetBaseName(loadError->FileName); - PhSetPluginDisabled(&baseName->sr, TRUE); - PhDereferenceObject(baseName); - } - } - - PhDeleteStringBuilder(&sb); - } - - // When we loaded settings before, we didn't know about plugin settings, so they - // went into the ignored settings list. Now that they've had a chance to add - // settings, we should scan the ignored settings list and move the settings to - // the right places. - if (PhSettingsFileName) - PhConvertIgnoredSettings(); - - PhpExecuteCallbackForAllPlugins(PluginCallbackLoad, TRUE); -} - -/** - * Notifies all plugins that the program is shutting down. - */ -VOID PhUnloadPlugins( - VOID - ) -{ - PhpExecuteCallbackForAllPlugins(PluginCallbackUnload, FALSE); -} - -/** - * Loads a plugin. - * - * \param FileName The full file name of the plugin. - */ -BOOLEAN PhLoadPlugin( - _In_ PPH_STRING FileName - ) -{ - BOOLEAN success; - PPH_STRING fileName; - PPH_STRING errorMessage; - - fileName = PhGetFullPath(FileName->Buffer, NULL); - - if (!fileName) - PhSetReference(&fileName, FileName); - - success = TRUE; - - if (!LoadLibrary(fileName->Buffer)) - { - success = FALSE; - errorMessage = PhGetWin32Message(GetLastError()); - } - - if (!success) - { - PPHP_PLUGIN_LOAD_ERROR loadError; - - loadError = PhAllocate(sizeof(PHP_PLUGIN_LOAD_ERROR)); - PhSetReference(&loadError->FileName, fileName); - PhSetReference(&loadError->ErrorMessage, errorMessage); - - if (!LoadErrors) - LoadErrors = PhCreateList(2); - - PhAddItemList(LoadErrors, loadError); - - if (errorMessage) - PhDereferenceObject(errorMessage); - } - - PhDereferenceObject(fileName); - - return success; -} - -VOID PhpExecuteCallbackForAllPlugins( - _In_ PH_PLUGIN_CALLBACK Callback, - _In_ BOOLEAN StartupParameters - ) -{ - PPH_AVL_LINKS links; - - for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) - { - PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); - PPH_LIST parameters = NULL; - - // Find relevant startup parameters for this plugin. - if (StartupParameters && PhStartupParameters.PluginParameters) - { - ULONG i; - - for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++) - { - PPH_STRING string = PhStartupParameters.PluginParameters->Items[i]; - PH_STRINGREF pluginName; - PH_STRINGREF parameter; - - if (PhSplitStringRefAtChar(&string->sr, ':', &pluginName, ¶meter) && - PhEqualStringRef(&pluginName, &plugin->Name, FALSE) && - parameter.Length != 0) - { - if (!parameters) - parameters = PhCreateList(3); - - PhAddItemList(parameters, PhCreateString2(¶meter)); - } - } - } - - PhInvokeCallback(PhGetPluginCallback(plugin, Callback), parameters); - - if (parameters) - { - PhDereferenceObjects(parameters->Items, parameters->Count); - PhDereferenceObject(parameters); - } - } -} - -BOOLEAN PhpValidatePluginName( - _In_ PPH_STRINGREF Name - ) -{ - SIZE_T i; - PWSTR buffer; - SIZE_T count; - - buffer = Name->Buffer; - count = Name->Length / sizeof(WCHAR); - - for (i = 0; i < count; i++) - { - if (!iswalnum(buffer[i]) && buffer[i] != ' ' && buffer[i] != '.' && buffer[i] != '_') - { - return FALSE; - } - } - - return TRUE; -} - -/** - * Registers a plugin with the host. - * - * \param Name A unique identifier for the plugin. The function fails - * if another plugin has already been registered with the same name. The - * name must only contain alphanumeric characters, spaces, dots and - * underscores. - * \param DllBase The base address of the plugin DLL. This is passed - * to the DllMain function. - * \param Information A variable which receives a pointer to the - * plugin's additional information block. This should be filled in after - * the function returns. - * - * \return A pointer to the plugin instance structure, or NULL if the - * function failed. - */ -PPH_PLUGIN PhRegisterPlugin( - _In_ PWSTR Name, - _In_ PVOID DllBase, - _Out_opt_ PPH_PLUGIN_INFORMATION *Information - ) -{ - PPH_PLUGIN plugin; - PH_STRINGREF pluginName; - PPH_AVL_LINKS existingLinks; - ULONG i; - PPH_STRING fileName; - - PhInitializeStringRefLongHint(&pluginName, Name); - - if (!PhpValidatePluginName(&pluginName)) - return NULL; - - fileName = PhGetDllFileName(DllBase, NULL); - - if (!fileName) - return NULL; - - plugin = PhAllocate(sizeof(PH_PLUGIN)); - memset(plugin, 0, sizeof(PH_PLUGIN)); - - plugin->Name = pluginName; - plugin->DllBase = DllBase; - - plugin->FileName = fileName; - - existingLinks = PhAddElementAvlTree(&PhPluginsByName, &plugin->Links); - - if (existingLinks) - { - // Another plugin has already been registered with the same name. - PhFree(plugin); - return NULL; - } - - for (i = 0; i < PluginCallbackMaximum; i++) - PhInitializeCallback(&plugin->Callbacks[i]); - - PhEmInitializeAppContext(&plugin->AppContext, &pluginName); - - if (Information) - *Information = &plugin->Information; - - return plugin; -} - -/** - * Locates a plugin instance structure. - * - * \param Name The name of the plugin. - * - * \return A plugin instance structure, or NULL if the plugin was not found. - */ -PPH_PLUGIN PhFindPlugin( - _In_ PWSTR Name - ) -{ - PH_STRINGREF name; - - PhInitializeStringRefLongHint(&name, Name); - - return PhFindPlugin2(&name); -} - -/** - * Locates a plugin instance structure. - * - * \param Name The name of the plugin. - * - * \return A plugin instance structure, or NULL if the plugin was not found. - */ -PPH_PLUGIN PhFindPlugin2( - _In_ PPH_STRINGREF Name - ) -{ - PPH_AVL_LINKS links; - PH_PLUGIN lookupPlugin; - - lookupPlugin.Name = *Name; - links = PhFindElementAvlTree(&PhPluginsByName, &lookupPlugin.Links); - - if (links) - return CONTAINING_RECORD(links, PH_PLUGIN, Links); - else - return NULL; -} - -/** - * Gets a pointer to a plugin's additional information block. - * - * \param Plugin The plugin instance structure. - * - * \return The plugin's additional information block. - */ -PPH_PLUGIN_INFORMATION PhGetPluginInformation( - _In_ PPH_PLUGIN Plugin - ) -{ - return &Plugin->Information; -} - -/** - * Retrieves a pointer to a plugin callback. - * - * \param Plugin A plugin instance structure. - * \param Callback The type of callback. - * - * \remarks The program invokes plugin callbacks for notifications - * specific to a plugin. - */ -PPH_CALLBACK PhGetPluginCallback( - _In_ PPH_PLUGIN Plugin, - _In_ PH_PLUGIN_CALLBACK Callback - ) -{ - if (Callback >= PluginCallbackMaximum) - PhRaiseStatus(STATUS_INVALID_PARAMETER_2); - - return &Plugin->Callbacks[Callback]; -} - -/** - * Retrieves a pointer to a general callback. - * - * \param Callback The type of callback. - * - * \remarks The program invokes general callbacks for system-wide - * notifications. - */ -PPH_CALLBACK PhGetGeneralCallback( - _In_ PH_GENERAL_CALLBACK Callback - ) -{ - if (Callback >= GeneralCallbackMaximum) - PhRaiseStatus(STATUS_INVALID_PARAMETER_2); - - return &GeneralCallbacks[Callback]; -} - -/** - * Reserves unique GUI identifiers. - * - * \param Count The number of identifiers to reserve. - * - * \return The start of the reserved range. - * - * \remarks The identifiers reserved by this function are - * guaranteed to be unique throughout the program. - */ -ULONG PhPluginReserveIds( - _In_ ULONG Count - ) -{ - ULONG nextPluginId; - - nextPluginId = NextPluginId; - NextPluginId += Count; - - return nextPluginId; -} - -/** - * Adds a menu item to the program's main menu. This function is - * deprecated. Use \c GeneralCallbackMainMenuInitializing instead. - * - * \param Plugin A plugin instance structure. - * \param Location A handle to the parent menu, or one of the following: - * \li \c PH_MENU_ITEM_LOCATION_VIEW The "View" menu. - * \li \c PH_MENU_ITEM_LOCATION_TOOLS The "Tools" menu. - * \param InsertAfter The text of the menu item to insert the - * new menu item after. The search is a case-insensitive prefix search - * that ignores prefix characters (ampersands). - * \param Id An identifier for the menu item. This should be unique - * within the plugin. - * \param Text The text of the menu item. - * \param Context A user-defined value for the menu item. - * - * \return TRUE if the function succeeded, otherwise FALSE. - * - * \remarks The \ref PluginCallbackMenuItem callback is invoked when - * the menu item is chosen, and the \ref PH_PLUGIN_MENU_ITEM structure - * will contain the \a Id and \a Context values passed to this function. - */ -ULONG_PTR PhPluginAddMenuItem( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG_PTR Location, - _In_opt_ PWSTR InsertAfter, - _In_ ULONG Id, - _In_ PWSTR Text, - _In_opt_ PVOID Context - ) -{ - PH_ADD_MENU_ITEM addMenuItem; - - addMenuItem.Plugin = Plugin; - addMenuItem.InsertAfter = InsertAfter; - addMenuItem.Text = Text; - addMenuItem.Context = Context; - - if (Location < 0x1000) - { - addMenuItem.Location = (ULONG)Location; - } - else - { - return 0; - } - - addMenuItem.Flags = Id & PH_MENU_ITEM_VALID_FLAGS; - Id &= ~PH_MENU_ITEM_VALID_FLAGS; - addMenuItem.Id = Id; - - return ProcessHacker_AddMenuItem(PhMainWndHandle, &addMenuItem); -} - -/** - * Retrieves current system statistics. - */ -VOID PhPluginGetSystemStatistics( - _Out_ PPH_PLUGIN_SYSTEM_STATISTICS Statistics - ) -{ - Statistics->Performance = &PhPerfInformation; - - Statistics->NumberOfProcesses = PhTotalProcesses; - Statistics->NumberOfThreads = PhTotalThreads; - Statistics->NumberOfHandles = PhTotalHandles; - - Statistics->CpuKernelUsage = PhCpuKernelUsage; - Statistics->CpuUserUsage = PhCpuUserUsage; - - Statistics->IoReadDelta = PhIoReadDelta; - Statistics->IoWriteDelta = PhIoWriteDelta; - Statistics->IoOtherDelta = PhIoOtherDelta; - - Statistics->CommitPages = PhGetItemCircularBuffer_ULONG(&PhCommitHistory, 0); - Statistics->PhysicalPages = PhGetItemCircularBuffer_ULONG(&PhPhysicalHistory, 0); - - Statistics->MaxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); - Statistics->MaxIoProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxIoHistory, 0)); - - Statistics->CpuKernelHistory = &PhCpuKernelHistory; - Statistics->CpuUserHistory = &PhCpuUserHistory; - Statistics->CpusKernelHistory = &PhCpusKernelHistory; - Statistics->CpusUserHistory = &PhCpusUserHistory; - Statistics->IoReadHistory = &PhIoReadHistory; - Statistics->IoWriteHistory = &PhIoWriteHistory; - Statistics->IoOtherHistory = &PhIoOtherHistory; - Statistics->CommitHistory = &PhCommitHistory; - Statistics->PhysicalHistory = &PhPhysicalHistory; - Statistics->MaxCpuHistory = &PhMaxCpuHistory; - Statistics->MaxIoHistory = &PhMaxIoHistory; -#ifdef PH_RECORD_MAX_USAGE - Statistics->MaxCpuUsageHistory = &PhMaxCpuUsageHistory; - Statistics->MaxIoReadOtherHistory = &PhMaxIoReadOtherHistory; - Statistics->MaxIoWriteHistory = &PhMaxIoWriteHistory; -#else - Statistics->MaxCpuUsageHistory = NULL; - Statistics->MaxIoReadOtherHistory = NULL; - Statistics->MaxIoWriteHistory = NULL; -#endif -} - -static VOID NTAPI PhpPluginEMenuItemDeleteFunction( - _In_ PPH_EMENU_ITEM Item - ) -{ - PPH_PLUGIN_MENU_ITEM pluginMenuItem; - - pluginMenuItem = Item->Context; - - if (pluginMenuItem->DeleteFunction) - pluginMenuItem->DeleteFunction(pluginMenuItem); - - PhFree(pluginMenuItem); -} - -/** - * Creates a menu item. - * - * \param Plugin A plugin instance structure. - * \param Flags A combination of flags. - * \param Id An identifier for the menu item. This should be unique - * within the plugin. - * \param Text The text of the menu item. - * \param Context A user-defined value for the menu item. - * - * \return A menu item object. This can then be inserted into another - * menu using PhInsertEMenuItem(). - * - * \remarks The \ref PluginCallbackMenuItem callback is invoked when - * the menu item is chosen, and the \ref PH_PLUGIN_MENU_ITEM structure - * will contain the \a Id and \a Context values passed to this function. - */ -PPH_EMENU_ITEM PhPluginCreateEMenuItem( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG Flags, - _In_ ULONG Id, - _In_ PWSTR Text, - _In_opt_ PVOID Context - ) -{ - PPH_EMENU_ITEM item; - PPH_PLUGIN_MENU_ITEM pluginMenuItem; - - item = PhCreateEMenuItem(Flags, ID_PLUGIN_MENU_ITEM, Text, NULL, NULL); - - pluginMenuItem = PhAllocate(sizeof(PH_PLUGIN_MENU_ITEM)); - memset(pluginMenuItem, 0, sizeof(PH_PLUGIN_MENU_ITEM)); - pluginMenuItem->Plugin = Plugin; - pluginMenuItem->Id = Id; - pluginMenuItem->Context = Context; - - item->Context = pluginMenuItem; - item->DeleteFunction = PhpPluginEMenuItemDeleteFunction; - - return item; -} - -/** - * Adds a menu hook. - * - * \param MenuInfo The plugin menu information structure. - * \param Plugin A plugin instance structure. - * \param Context A user-defined value that is later accessible from the callback. - * - * \remarks The \ref PluginCallbackMenuHook callback is invoked when any menu item - * from the menu is chosen. - */ -BOOLEAN PhPluginAddMenuHook( - _Inout_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_ PPH_PLUGIN Plugin, - _In_opt_ PVOID Context - ) -{ - PPHP_PLUGIN_MENU_HOOK hook; - - if (MenuInfo->Flags & PH_PLUGIN_MENU_DISALLOW_HOOKS) - return FALSE; - - if (!MenuInfo->PluginHookList) - MenuInfo->PluginHookList = PH_AUTO(PhCreateList(2)); - - hook = PH_AUTO(PhCreateAlloc(sizeof(PHP_PLUGIN_MENU_HOOK))); - hook->Plugin = Plugin; - hook->Context = Context; - PhAddItemList(MenuInfo->PluginHookList, hook); - - return TRUE; -} - -/** - * Initializes a plugin menu information structure. - * - * \param MenuInfo The structure to initialize. - * \param Menu The menu being shown. - * \param OwnerWindow The window that owns the menu. - * \param Flags Additional flags. - * - * \remarks This function is reserved for internal use. - */ -VOID PhPluginInitializeMenuInfo( - _Out_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_opt_ PPH_EMENU Menu, - _In_ HWND OwnerWindow, - _In_ ULONG Flags - ) -{ - memset(MenuInfo, 0, sizeof(PH_PLUGIN_MENU_INFORMATION)); - MenuInfo->Menu = Menu; - MenuInfo->OwnerWindow = OwnerWindow; - MenuInfo->Flags = Flags; -} - -/** - * Triggers a plugin menu item. - * - * \param MenuInfo The plugin menu information structure. - * \param Item The menu item chosen by the user. - * - * \remarks This function is reserved for internal use. - */ -BOOLEAN PhPluginTriggerEMenuItem( - _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_ PPH_EMENU_ITEM Item - ) -{ - PPH_PLUGIN_MENU_ITEM pluginMenuItem; - ULONG i; - PPHP_PLUGIN_MENU_HOOK hook; - PH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo; - - if (MenuInfo->PluginHookList) - { - for (i = 0; i < MenuInfo->PluginHookList->Count; i++) - { - hook = MenuInfo->PluginHookList->Items[i]; - menuHookInfo.MenuInfo = MenuInfo; - menuHookInfo.SelectedItem = Item; - menuHookInfo.Context = hook->Context; - menuHookInfo.Handled = FALSE; - PhInvokeCallback(PhGetPluginCallback(hook->Plugin, PluginCallbackMenuHook), &menuHookInfo); - - if (menuHookInfo.Handled) - return TRUE; - } - } - - if (Item->Id != ID_PLUGIN_MENU_ITEM) - return FALSE; - - pluginMenuItem = Item->Context; - - pluginMenuItem->OwnerWindow = MenuInfo->OwnerWindow; - - PhInvokeCallback(PhGetPluginCallback(pluginMenuItem->Plugin, PluginCallbackMenuItem), pluginMenuItem); - - return TRUE; -} - -/** - * Adds a column to a tree new control. - * - * \param Plugin A plugin instance structure. - * \param CmData The CmData value from the \ref PH_PLUGIN_TREENEW_INFORMATION - * structure. - * \param Column The column properties. - * \param SubId An identifier for the column. This should be unique within the - * plugin. - * \param Context A user-defined value. - * \param SortFunction The sort function for the column. - */ -BOOLEAN PhPluginAddTreeNewColumn( - _In_ PPH_PLUGIN Plugin, - _In_ PVOID CmData, - _In_ PPH_TREENEW_COLUMN Column, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_opt_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction - ) -{ - return !!PhCmCreateColumn( - CmData, - Column, - Plugin, - SubId, - Context, - SortFunction - ); -} - -/** - * Sets the object extension size and callbacks for an object type. - * - * \param Plugin A plugin instance structure. - * \param ObjectType The type of object for which the extension is being registered. - * \param ExtensionSize The size of the extension, in bytes. - * \param CreateCallback The object creation callback. - * \param DeleteCallback The object deletion callback. - */ -VOID PhPluginSetObjectExtension( - _In_ PPH_PLUGIN Plugin, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ ULONG ExtensionSize, - _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, - _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback - ) -{ - PhEmSetObjectExtension( - &Plugin->AppContext, - ObjectType, - ExtensionSize, - CreateCallback, - DeleteCallback - ); -} - -/** - * Gets the object extension for an object. - * - * \param Plugin A plugin instance structure. - * \param Object The object. - * \param ObjectType The type of object for which an extension has been registered. - */ -PVOID PhPluginGetObjectExtension( - _In_ PPH_PLUGIN Plugin, - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType - ) -{ - return PhEmGetObjectExtension( - &Plugin->AppContext, - ObjectType, - Object - ); -} - -/** - * Creates a notification icon. - * - * \param Plugin A plugin instance structure. - * \param SubId An identifier for the column. This should be unique within the - * plugin. - * \param Context A user-defined value. - * \param Text A string describing the notification icon. - * \param Flags A combination of flags. - * \li \c PH_NF_ICON_UNAVAILABLE The notification icon is currently unavailable. - * \param RegistrationData A \ref PH_NF_ICON_REGISTRATION_DATA structure that - * contains registration information. - */ -struct _PH_NF_ICON *PhPluginRegisterIcon( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG SubId, - _In_opt_ PVOID Context, - _In_ PWSTR Text, - _In_ ULONG Flags, - _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData - ) -{ - return PhNfRegisterIcon( - Plugin, - SubId, - Context, - Text, - Flags, - RegistrationData->UpdateCallback, - RegistrationData->MessageCallback - ); -} - -/** - * Allows a plugin to receive all treenew messages, not just column-related ones. - * - * \param Plugin A plugin instance structure. - * \param CmData The CmData value from the \ref PH_PLUGIN_TREENEW_INFORMATION - * structure. - */ -VOID PhPluginEnableTreeNewNotify( - _In_ PPH_PLUGIN Plugin, - _In_ PVOID CmData - ) -{ - PhCmSetNotifyPlugin(CmData, Plugin); -} - -BOOLEAN PhPluginQueryPhSvc( - _Out_ PPH_PLUGIN_PHSVC_CLIENT Client - ) -{ - if (!PhSvcClServerProcessId) - return FALSE; - - Client->ServerProcessId = PhSvcClServerProcessId; - Client->FreeHeap = PhSvcpFreeHeap; - Client->CreateString = PhSvcpCreateString; - - return TRUE; -} - -NTSTATUS PhPluginCallPhSvc( - _In_ PPH_PLUGIN Plugin, - _In_ ULONG SubId, - _In_reads_bytes_opt_(InLength) PVOID InBuffer, - _In_ ULONG InLength, - _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, - _In_ ULONG OutLength - ) -{ - NTSTATUS status; - PPH_STRING apiId; - PH_FORMAT format[4]; - - PhInitFormatC(&format[0], '+'); - PhInitFormatSR(&format[1], Plugin->Name); - PhInitFormatC(&format[2], '+'); - PhInitFormatU(&format[3], SubId); - apiId = PhFormat(format, 4, 50); - - status = PhSvcCallPlugin(&apiId->sr, InBuffer, InLength, OutBuffer, OutLength); - PhDereferenceObject(apiId); - - return status; -} +/* + * Process Hacker - + * plugin support + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PHP_PLUGIN_LOAD_ERROR +{ + PPH_STRING FileName; + PPH_STRING ErrorMessage; +} PHP_PLUGIN_LOAD_ERROR, *PPHP_PLUGIN_LOAD_ERROR; + +typedef struct _PHP_PLUGIN_MENU_HOOK +{ + PPH_PLUGIN Plugin; + PVOID Context; +} PHP_PLUGIN_MENU_HOOK, *PPHP_PLUGIN_MENU_HOOK; + +INT NTAPI PhpPluginsCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ); + +NTSTATUS PhLoadPlugin( + _In_ PPH_STRING FileName + ); + +VOID PhpExecuteCallbackForAllPlugins( + _In_ PH_PLUGIN_CALLBACK Callback, + _In_ BOOLEAN StartupParameters + ); + +PH_AVL_TREE PhPluginsByName = PH_AVL_TREE_INIT(PhpPluginsCompareFunction); +static PH_CALLBACK GeneralCallbacks[GeneralCallbackMaximum]; +static PPH_STRING PluginsDirectory; +static ULONG NextPluginId = IDPLUGINS + 1; + +INT NTAPI PhpPluginsCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ) +{ + PPH_PLUGIN plugin1 = CONTAINING_RECORD(Links1, PH_PLUGIN, Links); + PPH_PLUGIN plugin2 = CONTAINING_RECORD(Links2, PH_PLUGIN, Links); + + return PhCompareStringRef(&plugin1->Name, &plugin2->Name, FALSE); +} + +BOOLEAN PhpLocateDisabledPlugin( + _In_ PPH_STRING List, + _In_ PPH_STRINGREF BaseName, + _Out_opt_ PULONG FoundIndex + ) +{ + PH_STRINGREF namePart; + PH_STRINGREF remainingPart; + + remainingPart = List->sr; + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, '|', &namePart, &remainingPart); + + if (PhEqualStringRef(&namePart, BaseName, TRUE)) + { + if (FoundIndex) + *FoundIndex = (ULONG)(namePart.Buffer - List->Buffer); + + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN PhIsPluginDisabled( + _In_ PPH_STRINGREF BaseName + ) +{ + BOOLEAN found; + PPH_STRING disabled; + + disabled = PhGetStringSetting(L"DisabledPlugins"); + found = PhpLocateDisabledPlugin(disabled, BaseName, NULL); + PhDereferenceObject(disabled); + + return found; +} + +VOID PhSetPluginDisabled( + _In_ PPH_STRINGREF BaseName, + _In_ BOOLEAN Disable + ) +{ + BOOLEAN found; + PPH_STRING disabled; + ULONG foundIndex; + PPH_STRING newDisabled; + + disabled = PhGetStringSetting(L"DisabledPlugins"); + + found = PhpLocateDisabledPlugin(disabled, BaseName, &foundIndex); + + if (Disable && !found) + { + // We need to add the plugin to the disabled list. + + if (disabled->Length != 0) + { + // We have other disabled plugins. Append a pipe character followed by the plugin name. + newDisabled = PhCreateStringEx(NULL, disabled->Length + sizeof(WCHAR) + BaseName->Length); + memcpy(newDisabled->Buffer, disabled->Buffer, disabled->Length); + newDisabled->Buffer[disabled->Length / sizeof(WCHAR)] = '|'; + memcpy(&newDisabled->Buffer[disabled->Length / sizeof(WCHAR) + 1], BaseName->Buffer, BaseName->Length); + PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); + PhDereferenceObject(newDisabled); + } + else + { + // This is the first disabled plugin. + PhSetStringSetting2(L"DisabledPlugins", BaseName); + } + } + else if (!Disable && found) + { + ULONG removeCount; + + // We need to remove the plugin from the disabled list. + + removeCount = (ULONG)BaseName->Length / sizeof(WCHAR); + + if (foundIndex + (ULONG)BaseName->Length / sizeof(WCHAR) < (ULONG)disabled->Length / sizeof(WCHAR)) + { + // Remove the following pipe character as well. + removeCount++; + } + else if (foundIndex != 0) + { + // Remove the preceding pipe character as well. + foundIndex--; + removeCount++; + } + + newDisabled = PhCreateStringEx(NULL, disabled->Length - removeCount * sizeof(WCHAR)); + memcpy(newDisabled->Buffer, disabled->Buffer, foundIndex * sizeof(WCHAR)); + memcpy(&newDisabled->Buffer[foundIndex], &disabled->Buffer[foundIndex + removeCount], + disabled->Length - removeCount * sizeof(WCHAR) - foundIndex * sizeof(WCHAR)); + PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); + PhDereferenceObject(newDisabled); + } + + PhDereferenceObject(disabled); +} + +static BOOLEAN EnumPluginsDirectoryCallback( + _In_ PFILE_NAMES_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + static PH_STRINGREF PhpPluginExtension = PH_STRINGREF_INIT(L".dll"); + static PH_STRINGREF PhpPluginBlocklist[] = + { + PH_STRINGREF_INIT(L"CommonUtil.dll"), + PH_STRINGREF_INIT(L"ExtraPlugins.dll"), + PH_STRINGREF_INIT(L"SbieSupport.dll"), + PH_STRINGREF_INIT(L"HexPidPlugin.dll") + }; + BOOLEAN blocklistedPlugin = FALSE; + PH_STRINGREF baseName; + PPH_STRING fileName; + + baseName.Buffer = Information->FileName; + baseName.Length = Information->FileNameLength; + + // Note: The *.dll pattern passed to NtQueryDirectoryFile includes extensions other than dll (For example: *.dll* or .dllmanifest). (dmex) + if (!PhEndsWithStringRef(&baseName, &PhpPluginExtension, FALSE)) + return TRUE; + + for (ULONG i = 0; i < RTL_NUMBER_OF(PhpPluginBlocklist); i++) + { + if (PhEndsWithStringRef(&baseName, &PhpPluginBlocklist[i], TRUE)) + { + blocklistedPlugin = TRUE; + break; + } + } + + fileName = PhConcatStringRef2(&PluginsDirectory->sr, &baseName); + + if (blocklistedPlugin) + { + PhDeleteFileWin32(fileName->Buffer); + } + else if (!PhIsPluginDisabled(&baseName)) + { + NTSTATUS status; + + status = PhLoadPlugin(fileName); + + if (!NT_SUCCESS(status)) + { + PPH_LIST pluginLoadErrors = Context; + PPHP_PLUGIN_LOAD_ERROR loadError; + PPH_STRING errorMessage; + + loadError = PhAllocateZero(sizeof(PHP_PLUGIN_LOAD_ERROR)); + PhSetReference(&loadError->FileName, fileName); + + if (errorMessage = PhGetNtMessage(status)) + { + PhSetReference(&loadError->ErrorMessage, errorMessage); + PhDereferenceObject(errorMessage); + } + + PhAddItemList(pluginLoadErrors, loadError); + } + } + + PhDereferenceObject(fileName); + + return TRUE; +} + +/** + * Loads plugins from the default plugins directory. + */ +VOID PhLoadPlugins( + VOID + ) +{ + ULONG i; + HANDLE pluginsDirectoryHandle; + PPH_STRING pluginsDirectory; + PPH_LIST pluginLoadErrors; + + pluginLoadErrors = PhCreateList(1); + pluginsDirectory = PhGetStringSetting(L"PluginsDirectory"); + + if (RtlDetermineDosPathNameType_U(pluginsDirectory->Buffer) == RtlPathTypeRelative) + { + PPH_STRING applicationDirectory; + + if (applicationDirectory = PhGetApplicationDirectory()) + { + PhMoveReference(&pluginsDirectory, PhConcatStrings( // Not absolute. Make sure it is. + 4, + applicationDirectory->Buffer, + L"\\", + pluginsDirectory->Buffer, + L"\\" + )); + + PhDereferenceObject(applicationDirectory); + } + } + + if (PhIsNullOrEmptyString(PluginsDirectory)) + { + PhMoveReference(&PluginsDirectory, pluginsDirectory); + } + + if (NT_SUCCESS(PhCreateFileWin32( + &pluginsDirectoryHandle, + PhGetString(PluginsDirectory), + FILE_LIST_DIRECTORY | SYNCHRONIZE, + FILE_ATTRIBUTE_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*.dll"); + + if (!NT_SUCCESS(PhEnumDirectoryFileEx( + pluginsDirectoryHandle, + FileNamesInformation, + FALSE, + &pattern, + EnumPluginsDirectoryCallback, + pluginLoadErrors + ))) + { + // Note: The MUP devices for Virtualbox and VMware improperly truncate + // data returned by NtQueryDirectoryFile when ReturnSingleEntry=FALSE and also have + // various other bugs and issues for information classes other than FileNamesInformation. (dmex) + PhEnumDirectoryFileEx( + pluginsDirectoryHandle, + FileNamesInformation, + TRUE, + &pattern, + EnumPluginsDirectoryCallback, + pluginLoadErrors + ); + } + + NtClose(pluginsDirectoryHandle); + } + + // Handle load errors. + // In certain startup modes we want to ignore all plugin load errors. + if (PhGetIntegerSetting(L"ShowPluginLoadErrors") && pluginLoadErrors->Count != 0 && !PhStartupParameters.PhSvc) + { + PH_STRING_BUILDER stringBuilder; + PPHP_PLUGIN_LOAD_ERROR loadError; + PPH_STRING baseName; + TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; + TASKDIALOG_BUTTON buttons[2]; + INT result; + + PhInitializeStringBuilder(&stringBuilder, 100); + + for (i = 0; i < pluginLoadErrors->Count; i++) + { + loadError = pluginLoadErrors->Items[i]; + baseName = PhGetBaseName(loadError->FileName); + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%s: %s\n", + baseName->Buffer, + PhGetStringOrDefault(loadError->ErrorMessage, L"An unknown error occurred.") + ); + + PhDereferenceObject(baseName); + } + + if (PhEndsWithStringRef2(&stringBuilder.String->sr, L"\n", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.pszWindowTitle = PhApplicationName; + config.pszMainIcon = TD_ERROR_ICON; + config.pszMainInstruction = L"Unable to load the following plugin(s)"; + config.pszContent = PhGetString(PhFinalStringBuilderString(&stringBuilder)); + + buttons[0].nButtonID = IDYES; + buttons[0].pszButtonText = L"Delete plugins"; + buttons[1].nButtonID = IDNO; + buttons[1].pszButtonText = L"Disable plugins"; + + config.cButtons = 2; + config.pButtons = buttons; + config.nDefaultButton = IDCANCEL; + + if (TaskDialogIndirect( + &config, + &result, + NULL, + NULL + ) == S_OK) + { + switch (result) + { + case IDNO: + for (i = 0; i < pluginLoadErrors->Count; i++) + { + loadError = pluginLoadErrors->Items[i]; + baseName = PhGetBaseName(loadError->FileName); + + PhSetPluginDisabled(&baseName->sr, TRUE); + + PhDereferenceObject(baseName); + } + break; + case IDYES: + for (i = 0; i < pluginLoadErrors->Count; i++) + { + loadError = pluginLoadErrors->Items[i]; + + PhDeleteFileWin32(loadError->FileName->Buffer); + } + break; + } + } + + PhDeleteStringBuilder(&stringBuilder); + } + + // When we loaded settings before, we didn't know about plugin settings, so they + // went into the ignored settings list. Now that they've had a chance to add + // settings, we should scan the ignored settings list and move the settings to + // the right places. + if (PhSettingsFileName) + PhConvertIgnoredSettings(); + + PhpExecuteCallbackForAllPlugins(PluginCallbackLoad, TRUE); + + for (i = 0; i < pluginLoadErrors->Count; i++) + { + PPHP_PLUGIN_LOAD_ERROR loadError; + + loadError = pluginLoadErrors->Items[i]; + + if (loadError->FileName) + PhDereferenceObject(loadError->FileName); + if (loadError->ErrorMessage) + PhDereferenceObject(loadError->ErrorMessage); + + PhFree(loadError); + } + + PhDereferenceObject(pluginLoadErrors); +} + +/** + * Notifies all plugins that the program is shutting down. + */ +VOID PhUnloadPlugins( + VOID + ) +{ + PhpExecuteCallbackForAllPlugins(PluginCallbackUnload, FALSE); +} + +/** + * Loads a plugin. + * + * \param FileName The full file name of the plugin. + */ +NTSTATUS PhLoadPlugin( + _In_ PPH_STRING FileName + ) +{ + NTSTATUS status; + PPH_STRING fileName; + + status = PhGetFullPathEx( + FileName->Buffer, + NULL, + &fileName + ); + + if (NT_SUCCESS(status)) + { + status = PhLoadPluginImage(fileName, NULL); + + PhDereferenceObject(fileName); + } + + return status; +} + +VOID PhpExecuteCallbackForAllPlugins( + _In_ PH_PLUGIN_CALLBACK Callback, + _In_ BOOLEAN StartupParameters + ) +{ + PPH_AVL_LINKS links; + + for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) + { + PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); + PPH_LIST parameters = NULL; + + // Find relevant startup parameters for this plugin. + if (StartupParameters && PhStartupParameters.PluginParameters) + { + ULONG i; + + for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++) + { + PPH_STRING string = PhStartupParameters.PluginParameters->Items[i]; + PH_STRINGREF pluginName; + PH_STRINGREF parameter; + + if (PhSplitStringRefAtChar(&string->sr, ':', &pluginName, ¶meter) && + PhEqualStringRef(&pluginName, &plugin->Name, FALSE) && + parameter.Length != 0) + { + if (!parameters) + parameters = PhCreateList(3); + + PhAddItemList(parameters, PhCreateString2(¶meter)); + } + } + } + + PhInvokeCallback(PhGetPluginCallback(plugin, Callback), parameters); + + if (parameters) + { + PhDereferenceObjects(parameters->Items, parameters->Count); + PhDereferenceObject(parameters); + } + } +} + +BOOLEAN PhpValidatePluginName( + _In_ PPH_STRINGREF Name + ) +{ + SIZE_T i; + PWSTR buffer; + SIZE_T count; + + buffer = Name->Buffer; + count = Name->Length / sizeof(WCHAR); + + for (i = 0; i < count; i++) + { + if (!iswalnum(buffer[i]) && buffer[i] != ' ' && buffer[i] != '.' && buffer[i] != '_') + { + return FALSE; + } + } + + return TRUE; +} + +/** + * Registers a plugin with the host. + * + * \param Name A unique identifier for the plugin. The function fails + * if another plugin has already been registered with the same name. The + * name must only contain alphanumeric characters, spaces, dots and + * underscores. + * \param DllBase The base address of the plugin DLL. This is passed + * to the DllMain function. + * \param Information A variable which receives a pointer to the + * plugin's additional information block. This should be filled in after + * the function returns. + * + * \return A pointer to the plugin instance structure, or NULL if the + * function failed. + */ +PPH_PLUGIN PhRegisterPlugin( + _In_ PWSTR Name, + _In_ PVOID DllBase, + _Out_opt_ PPH_PLUGIN_INFORMATION *Information + ) +{ + PPH_PLUGIN plugin; + PH_STRINGREF pluginName; + PPH_AVL_LINKS existingLinks; + ULONG i; + PPH_STRING fileName; + + PhInitializeStringRefLongHint(&pluginName, Name); + + if (!PhpValidatePluginName(&pluginName)) + return NULL; + + fileName = PhGetDllFileName(DllBase, NULL); + + if (!fileName) + return NULL; + + plugin = PhAllocateZero(sizeof(PH_PLUGIN)); + plugin->Name = pluginName; + plugin->DllBase = DllBase; + plugin->FileName = fileName; + + existingLinks = PhAddElementAvlTree(&PhPluginsByName, &plugin->Links); + + if (existingLinks) + { + // Another plugin has already been registered with the same name. + PhFree(plugin); + return NULL; + } + + for (i = 0; i < PluginCallbackMaximum; i++) + PhInitializeCallback(&plugin->Callbacks[i]); + + PhEmInitializeAppContext(&plugin->AppContext, &pluginName); + + if (Information) + *Information = &plugin->Information; + + return plugin; +} + +/** + * Locates a plugin instance structure. + * + * \param Name The name of the plugin. + * + * \return A plugin instance structure, or NULL if the plugin was not found. + */ +PPH_PLUGIN PhFindPlugin( + _In_ PWSTR Name + ) +{ + PH_STRINGREF name; + + PhInitializeStringRefLongHint(&name, Name); + + return PhFindPlugin2(&name); +} + +/** + * Locates a plugin instance structure. + * + * \param Name The name of the plugin. + * + * \return A plugin instance structure, or NULL if the plugin was not found. + */ +PPH_PLUGIN PhFindPlugin2( + _In_ PPH_STRINGREF Name + ) +{ + PPH_AVL_LINKS links; + PH_PLUGIN lookupPlugin; + + lookupPlugin.Name = *Name; + links = PhFindElementAvlTree(&PhPluginsByName, &lookupPlugin.Links); + + if (links) + return CONTAINING_RECORD(links, PH_PLUGIN, Links); + else + return NULL; +} + +/** + * Gets a pointer to a plugin's additional information block. + * + * \param Plugin The plugin instance structure. + * + * \return The plugin's additional information block. + */ +PPH_PLUGIN_INFORMATION PhGetPluginInformation( + _In_ PPH_PLUGIN Plugin + ) +{ + return &Plugin->Information; +} + +/** + * Retrieves a pointer to a plugin callback. + * + * \param Plugin A plugin instance structure. + * \param Callback The type of callback. + * + * \remarks The program invokes plugin callbacks for notifications + * specific to a plugin. + */ +PPH_CALLBACK PhGetPluginCallback( + _In_ PPH_PLUGIN Plugin, + _In_ PH_PLUGIN_CALLBACK Callback + ) +{ + if (Callback >= PluginCallbackMaximum) + PhRaiseStatus(STATUS_INVALID_PARAMETER_2); + + return &Plugin->Callbacks[Callback]; +} + +VOID PhInitializeCallbacks( // HACK (dmex) + VOID + ) +{ + for (ULONG i = 0; i < GeneralCallbackMaximum; i++) + PhInitializeCallback(&GeneralCallbacks[i]); +} + +/** + * Retrieves a pointer to a general callback. + * + * \param Callback The type of callback. + * + * \remarks The program invokes general callbacks for system-wide + * notifications. + */ +PPH_CALLBACK PhGetGeneralCallback( + _In_ PH_GENERAL_CALLBACK Callback + ) +{ + if (Callback >= GeneralCallbackMaximum) + PhRaiseStatus(STATUS_INVALID_PARAMETER_2); + + return &GeneralCallbacks[Callback]; +} + +/** + * Reserves unique GUI identifiers. + * + * \param Count The number of identifiers to reserve. + * + * \return The start of the reserved range. + * + * \remarks The identifiers reserved by this function are + * guaranteed to be unique throughout the program. + */ +ULONG PhPluginReserveIds( + _In_ ULONG Count + ) +{ + ULONG nextPluginId; + + nextPluginId = NextPluginId; + NextPluginId += Count; + + return nextPluginId; +} + +/** + * Retrieves current system statistics. + */ +VOID PhPluginGetSystemStatistics( + _Out_ PPH_PLUGIN_SYSTEM_STATISTICS Statistics + ) +{ + Statistics->Performance = &PhPerfInformation; + + Statistics->NumberOfProcesses = PhTotalProcesses; + Statistics->NumberOfThreads = PhTotalThreads; + Statistics->NumberOfHandles = PhTotalHandles; + + Statistics->CpuKernelUsage = PhCpuKernelUsage; + Statistics->CpuUserUsage = PhCpuUserUsage; + + Statistics->IoReadDelta = PhIoReadDelta; + Statistics->IoWriteDelta = PhIoWriteDelta; + Statistics->IoOtherDelta = PhIoOtherDelta; + + Statistics->CommitPages = PhGetItemCircularBuffer_ULONG(&PhCommitHistory, 0); + Statistics->PhysicalPages = PhGetItemCircularBuffer_ULONG(&PhPhysicalHistory, 0); + + Statistics->MaxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); + Statistics->MaxIoProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxIoHistory, 0)); + + Statistics->CpuKernelHistory = &PhCpuKernelHistory; + Statistics->CpuUserHistory = &PhCpuUserHistory; + Statistics->CpusKernelHistory = &PhCpusKernelHistory; + Statistics->CpusUserHistory = &PhCpusUserHistory; + Statistics->IoReadHistory = &PhIoReadHistory; + Statistics->IoWriteHistory = &PhIoWriteHistory; + Statistics->IoOtherHistory = &PhIoOtherHistory; + Statistics->CommitHistory = &PhCommitHistory; + Statistics->PhysicalHistory = &PhPhysicalHistory; + Statistics->MaxCpuHistory = &PhMaxCpuHistory; + Statistics->MaxIoHistory = &PhMaxIoHistory; +#ifdef PH_RECORD_MAX_USAGE + Statistics->MaxCpuUsageHistory = &PhMaxCpuUsageHistory; + Statistics->MaxIoReadOtherHistory = &PhMaxIoReadOtherHistory; + Statistics->MaxIoWriteHistory = &PhMaxIoWriteHistory; +#else + Statistics->MaxCpuUsageHistory = NULL; + Statistics->MaxIoReadOtherHistory = NULL; + Statistics->MaxIoWriteHistory = NULL; +#endif +} + +static VOID NTAPI PhpPluginEMenuItemDeleteFunction( + _In_ PPH_EMENU_ITEM Item + ) +{ + PPH_PLUGIN_MENU_ITEM pluginMenuItem; + + pluginMenuItem = Item->Context; + + if (pluginMenuItem->DeleteFunction) + pluginMenuItem->DeleteFunction(pluginMenuItem); + + PhFree(pluginMenuItem); +} + +/** + * Creates a menu item. + * + * \param Plugin A plugin instance structure. + * \param Flags A combination of flags. + * \param Id An identifier for the menu item. This should be unique + * within the plugin. + * \param Text The text of the menu item. + * \param Context A user-defined value for the menu item. + * + * \return A menu item object. This can then be inserted into another + * menu using PhInsertEMenuItem(). + * + * \remarks The \ref PluginCallbackMenuItem callback is invoked when + * the menu item is chosen, and the \ref PH_PLUGIN_MENU_ITEM structure + * will contain the \a Id and \a Context values passed to this function. + */ +PPH_EMENU_ITEM PhPluginCreateEMenuItem( + _In_ PPH_PLUGIN Plugin, + _In_ ULONG Flags, + _In_ ULONG Id, + _In_ PWSTR Text, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM pluginMenuItem; + PPH_EMENU_ITEM item; + + pluginMenuItem = PhAllocateZero(sizeof(PH_PLUGIN_MENU_ITEM)); + pluginMenuItem->Plugin = Plugin; + pluginMenuItem->Id = Id; + pluginMenuItem->Context = Context; + + item = PhCreateEMenuItem(Flags, ID_PLUGIN_MENU_ITEM, Text, NULL, pluginMenuItem); + item->DeleteFunction = PhpPluginEMenuItemDeleteFunction; + + return item; +} + +/** + * Adds a menu hook. + * + * \param MenuInfo The plugin menu information structure. + * \param Plugin A plugin instance structure. + * \param Context A user-defined value that is later accessible from the callback. + * + * \remarks The \ref PluginCallbackMenuHook callback is invoked when any menu item + * from the menu is chosen. + */ +BOOLEAN PhPluginAddMenuHook( + _Inout_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_ PPH_PLUGIN Plugin, + _In_opt_ PVOID Context + ) +{ + PPHP_PLUGIN_MENU_HOOK hook; + + if (MenuInfo->Flags & PH_PLUGIN_MENU_DISALLOW_HOOKS) + return FALSE; + + if (!MenuInfo->PluginHookList) + MenuInfo->PluginHookList = PH_AUTO(PhCreateList(2)); + + hook = PH_AUTO(PhCreateAlloc(sizeof(PHP_PLUGIN_MENU_HOOK))); + hook->Plugin = Plugin; + hook->Context = Context; + PhAddItemList(MenuInfo->PluginHookList, hook); + + return TRUE; +} + +/** + * Initializes a plugin menu information structure. + * + * \param MenuInfo The structure to initialize. + * \param Menu The menu being shown. + * \param OwnerWindow The window that owns the menu. + * \param Flags Additional flags. + * + * \remarks This function is reserved for internal use. + */ +VOID PhPluginInitializeMenuInfo( + _Out_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_opt_ PPH_EMENU Menu, + _In_ HWND OwnerWindow, + _In_ ULONG Flags + ) +{ + memset(MenuInfo, 0, sizeof(PH_PLUGIN_MENU_INFORMATION)); + MenuInfo->Menu = Menu; + MenuInfo->OwnerWindow = OwnerWindow; + MenuInfo->Flags = Flags; +} + +/** + * Triggers a plugin menu item. + * + * \param MenuInfo The plugin menu information structure. + * \param Item The menu item chosen by the user. + * + * \remarks This function is reserved for internal use. + */ +BOOLEAN PhPluginTriggerEMenuItem( + _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_ PPH_EMENU_ITEM Item + ) +{ + PPH_PLUGIN_MENU_ITEM pluginMenuItem; + ULONG i; + PPHP_PLUGIN_MENU_HOOK hook; + PH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo; + + if (MenuInfo->PluginHookList) + { + for (i = 0; i < MenuInfo->PluginHookList->Count; i++) + { + hook = MenuInfo->PluginHookList->Items[i]; + menuHookInfo.MenuInfo = MenuInfo; + menuHookInfo.SelectedItem = Item; + menuHookInfo.Context = hook->Context; + menuHookInfo.Handled = FALSE; + PhInvokeCallback(PhGetPluginCallback(hook->Plugin, PluginCallbackMenuHook), &menuHookInfo); + + if (menuHookInfo.Handled) + return TRUE; + } + } + + if (Item->Id != ID_PLUGIN_MENU_ITEM) + return FALSE; + + pluginMenuItem = Item->Context; + + pluginMenuItem->OwnerWindow = MenuInfo->OwnerWindow; + + PhInvokeCallback(PhGetPluginCallback(pluginMenuItem->Plugin, PluginCallbackMenuItem), pluginMenuItem); + + return TRUE; +} + +/** + * Adds a column to a tree new control. + * + * \param Plugin A plugin instance structure. + * \param CmData The CmData value from the \ref PH_PLUGIN_TREENEW_INFORMATION + * structure. + * \param Column The column properties. + * \param SubId An identifier for the column. This should be unique within the + * plugin. + * \param Context A user-defined value. + * \param SortFunction The sort function for the column. + */ +BOOLEAN PhPluginAddTreeNewColumn( + _In_ PPH_PLUGIN Plugin, + _In_ PVOID CmData, + _In_ PPH_TREENEW_COLUMN Column, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_opt_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction + ) +{ + return !!PhCmCreateColumn( + CmData, + Column, + Plugin, + SubId, + Context, + SortFunction + ); +} + +/** + * Sets the object extension size and callbacks for an object type. + * + * \param Plugin A plugin instance structure. + * \param ObjectType The type of object for which the extension is being registered. + * \param ExtensionSize The size of the extension, in bytes. + * \param CreateCallback The object creation callback. + * \param DeleteCallback The object deletion callback. + */ +VOID PhPluginSetObjectExtension( + _In_ PPH_PLUGIN Plugin, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ ULONG ExtensionSize, + _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, + _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback + ) +{ + PhEmSetObjectExtension( + &Plugin->AppContext, + ObjectType, + ExtensionSize, + CreateCallback, + DeleteCallback + ); +} + +/** + * Gets the object extension for an object. + * + * \param Plugin A plugin instance structure. + * \param Object The object. + * \param ObjectType The type of object for which an extension has been registered. + */ +PVOID PhPluginGetObjectExtension( + _In_ PPH_PLUGIN Plugin, + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType + ) +{ + return PhEmGetObjectExtension( + &Plugin->AppContext, + ObjectType, + Object + ); +} + +/** + * Allows a plugin to receive all treenew messages, not just column-related ones. + * + * \param Plugin A plugin instance structure. + * \param CmData The CmData value from the \ref PH_PLUGIN_TREENEW_INFORMATION + * structure. + */ +VOID PhPluginEnableTreeNewNotify( + _In_ PPH_PLUGIN Plugin, + _In_ PVOID CmData + ) +{ + PhCmSetNotifyPlugin(CmData, Plugin); +} + +BOOLEAN PhPluginQueryPhSvc( + _Out_ PPH_PLUGIN_PHSVC_CLIENT Client + ) +{ + if (!PhSvcClServerProcessId) + return FALSE; + + Client->ServerProcessId = PhSvcClServerProcessId; + Client->FreeHeap = PhSvcpFreeHeap; + Client->CreateString = PhSvcpCreateString; + + return TRUE; +} + +NTSTATUS PhPluginCallPhSvc( + _In_ PPH_PLUGIN Plugin, + _In_ ULONG SubId, + _In_reads_bytes_opt_(InLength) PVOID InBuffer, + _In_ ULONG InLength, + _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, + _In_ ULONG OutLength + ) +{ + NTSTATUS status; + PPH_STRING apiId; + PH_FORMAT format[4]; + + PhInitFormatC(&format[0], '+'); + PhInitFormatSR(&format[1], Plugin->Name); + PhInitFormatC(&format[2], '+'); + PhInitFormatU(&format[3], SubId); + apiId = PhFormat(format, 4, 50); + + status = PhSvcCallPlugin(&apiId->sr, InBuffer, InLength, OutBuffer, OutLength); + PhDereferenceObject(apiId); + + return status; +} + +PPH_STRING PhGetPluginName( + _In_ PPH_PLUGIN Plugin + ) +{ + return PhCreateString2(&Plugin->Name); +} diff --git a/ProcessHacker/plugman.c b/ProcessHacker/plugman.c index 680a7dcfce9c..665ed05c5a29 100644 --- a/ProcessHacker/plugman.c +++ b/ProcessHacker/plugman.c @@ -1,445 +1,1195 @@ -/* - * Process Hacker - - * plugins - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include -#include - -#define IS_PLUGIN_LOADED(Plugin) (!!(Plugin)->AppContext.AppName.Buffer) -#define STR_OR_DEFAULT(String, Default) ((String) ? (String) : (Default)) - -static HWND PluginsLv; -static PPH_PLUGIN SelectedPlugin; -static PPH_LIST DisabledPluginInstances; // fake PH_PLUGIN structures for disabled plugins -static PPH_HASHTABLE DisabledPluginLookup; // list of all disabled plugins (including fake structures) by PH_PLUGIN address - -INT_PTR CALLBACK PhpPluginsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowPluginsDialog( - _In_ HWND ParentWindowHandle - ) -{ - if (PhPluginsEnabled) - { - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PLUGINS), - ParentWindowHandle, - PhpPluginsDlgProc - ); - } - else - { - PhShowInformation(ParentWindowHandle, - L"Plugins are not enabled. To use plugins enable them in Options and restart Process Hacker."); - } -} - -PWSTR PhpGetPluginBaseName( - _In_ PPH_PLUGIN Plugin - ) -{ - if (Plugin->FileName) - { - PH_STRINGREF pathNamePart; - PH_STRINGREF baseNamePart; - - if (PhSplitStringRefAtLastChar(&Plugin->FileName->sr, '\\', &pathNamePart, &baseNamePart)) - return baseNamePart.Buffer; - else - return Plugin->FileName->Buffer; - } - else - { - // Fake disabled plugin. - return Plugin->Name.Buffer; - } -} - -PWSTR PhpGetPluginDisableButtonText( - _In_ PWSTR BaseName - ) -{ - PH_STRINGREF baseName; - - PhInitializeStringRefLongHint(&baseName, BaseName); - - if (PhIsPluginDisabled(&baseName)) - return L"Enable"; - else - return L"Disable"; -} - -VOID PhpRefreshPluginDetails( - _In_ HWND hwndDlg - ) -{ - PPH_STRING fileName; - PH_IMAGE_VERSION_INFO versionInfo; - - if (SelectedPlugin && SelectedPlugin->FileName) // if there's no FileName, then it's a fake disabled plugin instance - { - fileName = SelectedPlugin->FileName; - - SetDlgItemText(hwndDlg, IDC_NAME, SelectedPlugin->Information.DisplayName ? SelectedPlugin->Information.DisplayName : L"(unnamed)"); - SetDlgItemText(hwndDlg, IDC_INTERNALNAME, SelectedPlugin->Name.Buffer); - SetDlgItemText(hwndDlg, IDC_AUTHOR, SelectedPlugin->Information.Author); - SetDlgItemText(hwndDlg, IDC_FILENAME, fileName->Buffer); - SetDlgItemText(hwndDlg, IDC_DESCRIPTION, SelectedPlugin->Information.Description); - SetDlgItemText(hwndDlg, IDC_URL, SelectedPlugin->Information.Url); - - if (PhInitializeImageVersionInfo(&versionInfo, fileName->Buffer)) - { - SetDlgItemText(hwndDlg, IDC_VERSION, PhGetStringOrDefault(versionInfo.FileVersion, L"Unknown")); - PhDeleteImageVersionInfo(&versionInfo); - } - else - { - SetDlgItemText(hwndDlg, IDC_VERSION, L"Unknown"); - } - - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENURL), SelectedPlugin->Information.Url ? SW_SHOW : SW_HIDE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), TRUE); - SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(PhpGetPluginBaseName(SelectedPlugin))); - EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), SelectedPlugin->Information.HasOptions); - } - else - { - SetDlgItemText(hwndDlg, IDC_NAME, L"N/A"); - SetDlgItemText(hwndDlg, IDC_VERSION, L"N/A"); - SetDlgItemText(hwndDlg, IDC_INTERNALNAME, L"N/A"); - SetDlgItemText(hwndDlg, IDC_AUTHOR, L"N/A"); - SetDlgItemText(hwndDlg, IDC_URL, L"N/A"); - SetDlgItemText(hwndDlg, IDC_FILENAME, L"N/A"); - SetDlgItemText(hwndDlg, IDC_DESCRIPTION, L"N/A"); - - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENURL), SW_HIDE); - - if (SelectedPlugin) - { - // This is a disabled plugin. - EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), TRUE); - SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(SelectedPlugin->Name.Buffer)); - } - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), FALSE); - SetDlgItemText(hwndDlg, IDC_DISABLE, L"Disable"); - } - - EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), FALSE); - } -} - -BOOLEAN PhpIsPluginLoadedByBaseName( - _In_ PPH_STRINGREF BaseName - ) -{ - PPH_AVL_LINKS links; - - // Extremely inefficient code follows. - // TODO: Make this better. - - for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) - { - PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); - PH_STRINGREF pluginBaseName; - - PhInitializeStringRefLongHint(&pluginBaseName, PhpGetPluginBaseName(plugin)); - - if (PhEqualStringRef(&pluginBaseName, BaseName, TRUE)) - return TRUE; - } - - return FALSE; -} - -PPH_PLUGIN PhpCreateDisabledPlugin( - _In_ PPH_STRINGREF BaseName - ) -{ - PPH_PLUGIN plugin; - - plugin = PhAllocate(sizeof(PH_PLUGIN)); - memset(plugin, 0, sizeof(PH_PLUGIN)); - - plugin->Name.Length = BaseName->Length; - plugin->Name.Buffer = PhAllocate(BaseName->Length + sizeof(WCHAR)); - memcpy(plugin->Name.Buffer, BaseName->Buffer, BaseName->Length); - plugin->Name.Buffer[BaseName->Length / 2] = 0; - - return plugin; -} - -VOID PhpFreeDisabledPlugin( - _In_ PPH_PLUGIN Plugin - ) -{ - PhFree(Plugin->Name.Buffer); - PhFree(Plugin); -} - -VOID PhpAddDisabledPlugins( - VOID - ) -{ - PPH_STRING disabled; - PH_STRINGREF remainingPart; - PH_STRINGREF part; - PPH_PLUGIN disabledPlugin; - PPH_STRING displayText; - INT lvItemIndex; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - remainingPart = disabled->sr; - - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length != 0) - { - if (!PhpIsPluginLoadedByBaseName(&part)) - { - disabledPlugin = PhpCreateDisabledPlugin(&part); - PhAddItemList(DisabledPluginInstances, disabledPlugin); - PhAddItemSimpleHashtable(DisabledPluginLookup, disabledPlugin, NULL); - - displayText = PhCreateString2(&part); - lvItemIndex = PhAddListViewItem(PluginsLv, MAXINT, displayText->Buffer, disabledPlugin); - PhDereferenceObject(displayText); - } - } - } - - PhDereferenceObject(disabled); -} - -VOID PhpUpdateDisabledPlugin( - _In_ HWND hwndDlg, - _In_ INT ItemIndex, - _In_ PPH_PLUGIN Plugin, - _In_ BOOLEAN NewDisabledState - ) -{ - if (NewDisabledState) - { - PhAddItemSimpleHashtable(DisabledPluginLookup, Plugin, NULL); - } - else - { - PhRemoveItemSimpleHashtable(DisabledPluginLookup, Plugin); - } - - if (!IS_PLUGIN_LOADED(Plugin)) - { - assert(!NewDisabledState); - ListView_DeleteItem(PluginsLv, ItemIndex); - } - - InvalidateRect(PluginsLv, NULL, TRUE); - - ShowWindow(GetDlgItem(hwndDlg, IDC_INSTRUCTION), SW_SHOW); -} - -static COLORREF PhpPluginColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN plugin = Param; - - if (PhFindItemSimpleHashtable(DisabledPluginLookup, plugin)) - return RGB(0x77, 0x77, 0x77); // fake disabled plugin - - return GetSysColor(COLOR_WINDOW); -} - -INT_PTR CALLBACK PhpPluginsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_AVL_LINKS links; - - PhCenterWindow(hwndDlg, PhMainWndHandle); - - PluginsLv = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(PluginsLv, FALSE, TRUE); - PhSetControlTheme(PluginsLv, L"explorer"); - PhAddListViewColumn(PluginsLv, 0, 0, 0, LVCFMT_LEFT, 280, L"Name"); - PhAddListViewColumn(PluginsLv, 1, 1, 1, LVCFMT_LEFT, 100, L"Author"); - PhSetExtendedListView(PluginsLv); - ExtendedListView_SetItemColorFunction(PluginsLv, PhpPluginColorFunction); - - DisabledPluginLookup = PhCreateSimpleHashtable(10); - - for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) - { - PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); - INT lvItemIndex; - PH_STRINGREF baseNameSr; - - lvItemIndex = PhAddListViewItem(PluginsLv, MAXINT, plugin->Information.DisplayName ? plugin->Information.DisplayName : plugin->Name.Buffer, plugin); - - if (plugin->Information.Author) - PhSetListViewSubItem(PluginsLv, lvItemIndex, 1, plugin->Information.Author); - - PhInitializeStringRefLongHint(&baseNameSr, PhpGetPluginBaseName(plugin)); - - if (PhIsPluginDisabled(&baseNameSr)) - PhAddItemSimpleHashtable(DisabledPluginLookup, plugin, NULL); - } - - DisabledPluginInstances = PhCreateList(10); - PhpAddDisabledPlugins(); - - ExtendedListView_SortItems(PluginsLv); - - SelectedPlugin = NULL; - PhpRefreshPluginDetails(hwndDlg); - } - break; - case WM_DESTROY: - { - ULONG i; - - for (i = 0; i < DisabledPluginInstances->Count; i++) - PhpFreeDisabledPlugin(DisabledPluginInstances->Items[i]); - - PhDereferenceObject(DisabledPluginInstances); - PhDereferenceObject(DisabledPluginLookup); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_DISABLE: - { - if (SelectedPlugin) - { - PWSTR baseName; - PH_STRINGREF baseNameRef; - BOOLEAN newDisabledState; - - baseName = PhpGetPluginBaseName(SelectedPlugin); - PhInitializeStringRef(&baseNameRef, baseName); - newDisabledState = !PhIsPluginDisabled(&baseNameRef); - PhSetPluginDisabled(&baseNameRef, newDisabledState); - PhpUpdateDisabledPlugin(hwndDlg, PhFindListViewItemByFlags(PluginsLv, -1, LVNI_SELECTED), SelectedPlugin, newDisabledState); - - SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(baseName)); - } - } - break; - case IDC_OPTIONS: - { - if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) - { - PhInvokeCallback(PhGetPluginCallback(SelectedPlugin, PluginCallbackShowOptions), hwndDlg); - } - } - break; - case IDC_CLEANUP: - { - if (PhShowMessage(hwndDlg, MB_ICONQUESTION | MB_YESNO, - L"Do you want to clean up unused plugin settings?") == IDYES) - { - PhClearIgnoredSettings(); - } - } - break; - case IDC_OPENURL: - { - NOTHING; - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == PluginsLv) - { - if (ListView_GetSelectedCount(PluginsLv) == 1) - SelectedPlugin = PhGetSelectedListViewItemParam(PluginsLv); - else - SelectedPlugin = NULL; - - PhpRefreshPluginDetails(hwndDlg); - } - } - break; - case NM_CLICK: - { - if (header->hwndFrom == GetDlgItem(hwndDlg, IDC_OPENURL)) - { - if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) - PhShellExecute(hwndDlg, SelectedPlugin->Information.Url, NULL); - } - } - break; - case NM_DBLCLK: - { - if (header->hwndFrom == PluginsLv) - { - if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin)) - { - PhInvokeCallback(PhGetPluginCallback(SelectedPlugin, PluginCallbackShowOptions), hwndDlg); - } - } - } - break; - } - } - break; - } - - REFLECT_MESSAGE_DLG(hwndDlg, PluginsLv, uMsg, wParam, lParam); - - return FALSE; -} +/* + * Process Hacker - + * plugins + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define WM_PH_PLUGINS_SHOWDIALOG (WM_APP + 401) +#define WM_PH_PLUGINS_SHOWPROPERTIES (WM_APP + 402) + +static HANDLE PhPluginsThreadHandle = NULL; +static HWND PhPluginsWindowHandle = NULL; +static PH_EVENT PhPluginsInitializedEvent = PH_EVENT_INIT; + +typedef struct _PH_PLUGMAN_CONTEXT +{ + PH_LAYOUT_MANAGER LayoutManager; + RECT MinimumSize; + + HFONT NormalFontHandle; + HFONT TitleFontHandle; + + HWND WindowHandle; + HWND TreeNewHandle; + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; +} PH_PLUGMAN_CONTEXT, *PPH_PLUGMAN_CONTEXT; + +typedef enum _PH_PLUGIN_TREE_ITEM_MENU +{ + PH_PLUGIN_TREE_ITEM_MENU_UNINSTALL, + PH_PLUGIN_TREE_ITEM_MENU_DISABLE, + PH_PLUGIN_TREE_ITEM_MENU_PROPERTIES +} PH_PLUGIN_TREE_ITEM_MENU; + +typedef enum _PH_PLUGIN_TREE_COLUMN_ITEM +{ + PH_PLUGIN_TREE_COLUMN_ITEM_NAME, + PH_PLUGIN_TREE_COLUMN_ITEM_AUTHOR, + PH_PLUGIN_TREE_COLUMN_ITEM_VERSION, + PH_PLUGIN_TREE_COLUMN_ITEM_MAXIMUM +} PH_PLUGIN_TREE_COLUMN_ITEM; + +typedef struct _PH_PLUGIN_TREE_ROOT_NODE +{ + PH_TREENEW_NODE Node; + + BOOLEAN PluginOptions; + PPH_PLUGIN PluginInstance; + + PPH_STRING InternalName; + PPH_STRING Name; + PPH_STRING Version; + PPH_STRING Author; + PPH_STRING Description; + + PH_STRINGREF TextCache[PH_PLUGIN_TREE_COLUMN_ITEM_MAXIMUM]; +} PH_PLUGIN_TREE_ROOT_NODE, *PPH_PLUGIN_TREE_ROOT_NODE; + +INT_PTR CALLBACK PhpPluginPropertiesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +ULONG PhpDisabledPluginsCount( + VOID + ); + +INT_PTR CALLBACK PhpPluginsDisabledDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#pragma region Plugin TreeList + +#define SORT_FUNCTION(Column) PluginsTreeNewCompare##Column +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PluginsTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_PLUGIN_TREE_ROOT_NODE node1 = *(PPH_PLUGIN_TREE_ROOT_NODE*)_elem1; \ + PPH_PLUGIN_TREE_ROOT_NODE node2 = *(PPH_PLUGIN_TREE_ROOT_NODE*)_elem2; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ + \ + return PhModifySort(sortResult, ((PPH_PLUGMAN_CONTEXT)_context)->TreeNewSortOrder); \ +} + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareString(node1->Name, node2->Name, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Author) +{ + sortResult = PhCompareString(node1->Author, node2->Author, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Version) +{ + sortResult = PhCompareString(node1->Version, node2->Version, TRUE); +} +END_SORT_FUNCTION + +VOID PluginsLoadSettingsTreeList( + _Inout_ PPH_PLUGMAN_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhGetStringSetting(L"PluginManagerTreeListColumns"); + PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); + PhDereferenceObject(settings); +} + +VOID PluginsSaveSettingsTreeList( + _Inout_ PPH_PLUGMAN_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhCmSaveSettings(Context->TreeNewHandle); + PhSetStringSetting2(L"PluginManagerTreeListColumns", &settings->sr); + PhDereferenceObject(settings); +} + +BOOLEAN PluginsNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_PLUGIN_TREE_ROOT_NODE node1 = *(PPH_PLUGIN_TREE_ROOT_NODE *)Entry1; + PPH_PLUGIN_TREE_ROOT_NODE node2 = *(PPH_PLUGIN_TREE_ROOT_NODE *)Entry2; + + return PhEqualString(node1->InternalName, node2->InternalName, TRUE); +} + +ULONG PluginsNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashStringRef(&(*(PPH_PLUGIN_TREE_ROOT_NODE*)Entry)->InternalName->sr, TRUE); +} + +VOID DestroyPluginsNode( + _In_ PPH_PLUGIN_TREE_ROOT_NODE Node + ) +{ + PhClearReference(&Node->InternalName); + PhClearReference(&Node->Name); + PhClearReference(&Node->Version); + PhClearReference(&Node->Author); + PhClearReference(&Node->Description); + + PhFree(Node); +} + +PPH_PLUGIN_TREE_ROOT_NODE AddPluginsNode( + _Inout_ PPH_PLUGMAN_CONTEXT Context, + _In_ PPH_PLUGIN Plugin + ) +{ + PPH_PLUGIN_TREE_ROOT_NODE pluginNode; + PH_IMAGE_VERSION_INFO versionInfo; + + pluginNode = PhAllocate(sizeof(PH_PLUGIN_TREE_ROOT_NODE)); + memset(pluginNode, 0, sizeof(PH_PLUGIN_TREE_ROOT_NODE)); + + PhInitializeTreeNewNode(&pluginNode->Node); + + memset(pluginNode->TextCache, 0, sizeof(PH_STRINGREF) * PH_PLUGIN_TREE_COLUMN_ITEM_MAXIMUM); + pluginNode->Node.TextCache = pluginNode->TextCache; + pluginNode->Node.TextCacheSize = PH_PLUGIN_TREE_COLUMN_ITEM_MAXIMUM; + + pluginNode->PluginInstance = Plugin; + pluginNode->PluginOptions = Plugin->Information.HasOptions; + pluginNode->InternalName = PhCreateString2(&Plugin->Name); + pluginNode->Name = PhCreateString(Plugin->Information.DisplayName); + pluginNode->Author = PhCreateString(Plugin->Information.Author); + pluginNode->Description = PhCreateString(Plugin->Information.Description); + + if (PhInitializeImageVersionInfo(&versionInfo, Plugin->FileName->Buffer)) + { + pluginNode->Version = PhReferenceObject(versionInfo.FileVersion); + PhDeleteImageVersionInfo(&versionInfo); + } + + PhAddEntryHashtable(Context->NodeHashtable, &pluginNode); + PhAddItemList(Context->NodeList, pluginNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); + + return pluginNode; +} + +PPH_PLUGIN_TREE_ROOT_NODE FindPluginsNode( + _In_ PPH_PLUGMAN_CONTEXT Context, + _In_ PPH_STRING InternalName + ) +{ + PH_PLUGIN_TREE_ROOT_NODE lookupPluginsNode; + PPH_PLUGIN_TREE_ROOT_NODE lookupPluginsNodePtr = &lookupPluginsNode; + PPH_PLUGIN_TREE_ROOT_NODE *pluginsNode; + + lookupPluginsNode.InternalName = InternalName; + + pluginsNode = (PPH_PLUGIN_TREE_ROOT_NODE*)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupPluginsNodePtr + ); + + if (pluginsNode) + return *pluginsNode; + else + return NULL; +} + +VOID RemovePluginsNode( + _In_ PPH_PLUGMAN_CONTEXT Context, + _In_ PPH_PLUGIN_TREE_ROOT_NODE Node + ) +{ + ULONG index = 0; + + PhRemoveEntryHashtable(Context->NodeHashtable, &Node); + + if ((index = PhFindItemList(Context->NodeList, Node)) != ULONG_MAX) + { + PhRemoveItemList(Context->NodeList, index); + } + + DestroyPluginsNode(Node); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID UpdatePluginsNode( + _In_ PPH_PLUGMAN_CONTEXT Context, + _In_ PPH_PLUGIN_TREE_ROOT_NODE Node + ) +{ + memset(Node->TextCache, 0, sizeof(PH_STRINGREF) * PH_PLUGIN_TREE_COLUMN_ITEM_MAXIMUM); + + PhInvalidateTreeNewNode(&Node->Node, TN_CACHE_COLOR); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +BOOLEAN NTAPI PluginsTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGMAN_CONTEXT context = Context; + PPH_PLUGIN_TREE_ROOT_NODE node; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + node = (PPH_PLUGIN_TREE_ROOT_NODE)getChildren->Node; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Name), + SORT_FUNCTION(Author), + SORT_FUNCTION(Version) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortColumn < PH_PLUGIN_TREE_COLUMN_ITEM_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1; + node = (PPH_PLUGIN_TREE_ROOT_NODE)isLeaf->Node; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1; + node = (PPH_PLUGIN_TREE_ROOT_NODE)getCellText->Node; + + switch (getCellText->Id) + { + case PH_PLUGIN_TREE_COLUMN_ITEM_NAME: + getCellText->Text = PhGetStringRef(node->Name); + break; + case PH_PLUGIN_TREE_COLUMN_ITEM_AUTHOR: + getCellText->Text = PhGetStringRef(node->Author); + break; + case PH_PLUGIN_TREE_COLUMN_ITEM_VERSION: + getCellText->Text = PhGetStringRef(node->Version); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + node = (PPH_PLUGIN_TREE_ROOT_NODE)getNodeColor->Node; + + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(context->TreeNewHandle, 0, -1); + break; + case VK_DELETE: + SendMessage(context->WindowHandle, WM_COMMAND, ID_OBJECT_CLOSE, 0); + break; + } + } + return TRUE; + case TreeNewLeftDoubleClick: + { + PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + + SendMessage(context->WindowHandle, WM_COMMAND, WM_PH_PLUGINS_SHOWPROPERTIES, (LPARAM)mouseEvent); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + SendMessage(context->WindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenuEvent); + } + return TRUE; + //case TreeNewHeaderRightClick: + // { + // PH_TN_COLUMN_MENU_DATA data; + // + // data.TreeNewHandle = hwnd; + // data.MouseEvent = Parameter1; + // data.DefaultSortColumn = 0; + // data.DefaultSortOrder = AscendingSortOrder; + // PhInitializeTreeNewColumnMenu(&data); + // + // data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + // PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + // PhHandleTreeNewColumnMenu(&data); + // PhDeleteTreeNewColumnMenu(&data); + // } + // return TRUE; + case TreeNewCustomDraw: + { + PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; + RECT rect = customDraw->CellRect; + node = (PPH_PLUGIN_TREE_ROOT_NODE)customDraw->Node; + + switch (customDraw->Column->Id) + { + case PH_PLUGIN_TREE_COLUMN_ITEM_NAME: + { + PH_STRINGREF text; + SIZE nameSize; + SIZE textSize; + + rect.left += PH_SCALE_DPI(15); + rect.top += PH_SCALE_DPI(5); + rect.right -= PH_SCALE_DPI(5); + rect.bottom -= PH_SCALE_DPI(8); + + // top + SetTextColor(customDraw->Dc, RGB(0x0, 0x0, 0x0)); + SelectFont(customDraw->Dc, context->TitleFontHandle); + text = PhIsNullOrEmptyString(node->Name) ? PhGetStringRef(node->InternalName) : PhGetStringRef(node->Name); + GetTextExtentPoint32(customDraw->Dc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &nameSize); + DrawText(customDraw->Dc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &rect, DT_TOP | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE); + + // bottom + SetTextColor(customDraw->Dc, RGB(0x64, 0x64, 0x64)); + SelectFont(customDraw->Dc, context->NormalFontHandle); + text = PhGetStringRef(node->Description); + GetTextExtentPoint32(customDraw->Dc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textSize); + DrawText( + customDraw->Dc, + text.Buffer, + (ULONG)text.Length / sizeof(WCHAR), + &rect, + DT_BOTTOM | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE + ); + } + break; + } + } + return TRUE; + } + + return FALSE; +} + +VOID ClearPluginsTree( + _In_ PPH_PLUGMAN_CONTEXT Context + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyPluginsNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +PPH_PLUGIN_TREE_ROOT_NODE GetSelectedPluginsNode( + _In_ PPH_PLUGMAN_CONTEXT Context + ) +{ + PPH_PLUGIN_TREE_ROOT_NODE windowNode = NULL; + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + windowNode = Context->NodeList->Items[i]; + + if (windowNode->Node.Selected) + return windowNode; + } + + return NULL; +} + +VOID GetSelectedPluginsNodes( + _In_ PPH_PLUGMAN_CONTEXT Context, + _Out_ PPH_PLUGIN_TREE_ROOT_NODE **PluginsNodes, + _Out_ PULONG NumberOfPluginsNodes + ) +{ + PPH_LIST list; + + list = PhCreateList(2); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PPH_PLUGIN_TREE_ROOT_NODE node = (PPH_PLUGIN_TREE_ROOT_NODE)Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *PluginsNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfPluginsNodes = list->Count; + + PhDereferenceObject(list); +} + +VOID InitializePluginsTree( + _Inout_ PPH_PLUGMAN_CONTEXT Context + ) +{ + Context->NodeList = PhCreateList(100); + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_PLUGIN_TREE_ROOT_NODE), + PluginsNodeHashtableEqualFunction, + PluginsNodeHashtableHashFunction, + 100 + ); + + Context->NormalFontHandle = PhCreateCommonFont(-10, FW_NORMAL, NULL); + Context->TitleFontHandle = PhCreateCommonFont(-14, FW_BOLD, NULL); + + PhSetControlTheme(Context->TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(Context->TreeNewHandle, PluginsTreeNewCallback, Context); + TreeNew_SetRowHeight(Context->TreeNewHandle, PH_SCALE_DPI(48)); + + PhAddTreeNewColumnEx2(Context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TRUE, L"Plugin", 80, PH_ALIGN_LEFT, 0, 0, TN_COLUMN_FLAG_CUSTOMDRAW); + PhAddTreeNewColumnEx2(Context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_AUTHOR, TRUE, L"Author", 80, PH_ALIGN_LEFT, 1, 0, 0); + PhAddTreeNewColumnEx2(Context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_VERSION, TRUE, L"Version", 80, PH_ALIGN_CENTER, 2, DT_CENTER, 0); + + TreeNew_SetTriState(Context->TreeNewHandle, TRUE); + + PluginsLoadSettingsTreeList(Context); +} + +VOID DeletePluginsTree( + _In_ PPH_PLUGMAN_CONTEXT Context + ) +{ + if (Context->TitleFontHandle) + DeleteFont(Context->TitleFontHandle); + if (Context->NormalFontHandle) + DeleteFont(Context->NormalFontHandle); + + PluginsSaveSettingsTreeList(Context); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyPluginsNode(Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +#pragma endregion + +PWSTR PhpGetPluginBaseName( + _In_ PPH_PLUGIN Plugin + ) +{ + if (Plugin->FileName) + { + PH_STRINGREF pathNamePart; + PH_STRINGREF baseNamePart; + + if (PhSplitStringRefAtLastChar(&Plugin->FileName->sr, OBJ_NAME_PATH_SEPARATOR, &pathNamePart, &baseNamePart)) + return baseNamePart.Buffer; + else + return Plugin->FileName->Buffer; + } + else + { + // Fake disabled plugin. + return Plugin->Name.Buffer; + } +} + +VOID PhpEnumerateLoadedPlugins( + _In_ PPH_PLUGMAN_CONTEXT Context + ) +{ + PPH_AVL_LINKS links; + + for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) + { + PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); + PH_STRINGREF pluginBaseName; + + PhInitializeStringRefLongHint(&pluginBaseName, PhpGetPluginBaseName(plugin)); + + if (PhIsPluginDisabled(&pluginBaseName)) + continue; + + AddPluginsNode(Context, plugin); + } +} + +INT_PTR CALLBACK PhpPluginsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_PLUGMAN_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(PH_PLUGMAN_CONTEXT)); + memset(context, 0, sizeof(PH_PLUGMAN_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + context->WindowHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_PLUGINTREE); + + InitializePluginsTree(context); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_DISABLED), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + + if (PhGetIntegerPairSetting(L"PluginManagerWindowPosition").X != 0) + PhLoadWindowPlacementFromSetting(L"PluginManagerWindowPosition", L"PluginManagerWindowSize", hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + + context->MinimumSize.left = 0; + context->MinimumSize.top = 0; + context->MinimumSize.right = 300; + context->MinimumSize.bottom = 100; + MapDialogRect(hwndDlg, &context->MinimumSize); + + PhpEnumerateLoadedPlugins(context); + TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + PhSetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhpDisabledPluginsCount())->Buffer); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(L"PluginManagerWindowPosition", L"PluginManagerWindowSize", hwndDlg); + + PhDeleteLayoutManager(&context->LayoutManager); + + DeletePluginsTree(context); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhFree(context); + + PostQuitMessage(0); + } + break; + case WM_PH_PLUGINS_SHOWDIALOG: + { + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); + + SetForegroundWindow(hwndDlg); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDOK: + case IDCANCEL: + { + DestroyWindow(hwndDlg); + } + break; + case IDC_DISABLED: + { + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PLUGINSDISABLED), + hwndDlg, + PhpPluginsDisabledDlgProc + ); + + ClearPluginsTree(context); + PhpEnumerateLoadedPlugins(context); + TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + PhSetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhpDisabledPluginsCount())->Buffer); + } + break; + case ID_SHOWCONTEXTMENU: + { + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + //PPH_EMENU_ITEM uninstallItem; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PPH_PLUGIN_TREE_ROOT_NODE selectedNode = (PPH_PLUGIN_TREE_ROOT_NODE)contextMenuEvent->Node; + + if (!selectedNode) + break; + + menu = PhCreateEMenu(); + //PhInsertEMenuItem(menu, uninstallItem = PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_UNINSTALL, L"Uninstall", NULL, NULL), ULONG_MAX); + //PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_DISABLE, L"Disable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_PLUGIN_TREE_ITEM_MENU_PROPERTIES, L"Properties", NULL, NULL), ULONG_MAX); + + //if (!PhGetOwnTokenAttributes().Elevated) + //{ + // HBITMAP shieldBitmap; + // + // if (shieldBitmap = PhGetShieldBitmap()) + // uninstallItem->Bitmap = shieldBitmap; + //} + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + switch (selectedItem->Id) + { + case PH_PLUGIN_TREE_ITEM_MENU_UNINSTALL: + { + //if (PhShowConfirmMessage( + // hwndDlg, + // L"Uninstall", + // PhGetString(selectedNode->Name), + // L"Changes may require a restart to take effect...", + // TRUE + // )) + //{ + // + //} + } + break; + case PH_PLUGIN_TREE_ITEM_MENU_DISABLE: + { + PWSTR baseName; + PH_STRINGREF baseNameRef; + + baseName = PhpGetPluginBaseName(selectedNode->PluginInstance); + PhInitializeStringRef(&baseNameRef, baseName); + + PhSetPluginDisabled(&baseNameRef, TRUE); + + RemovePluginsNode(context, selectedNode); + + PhSetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhpDisabledPluginsCount())->Buffer); + } + break; + case PH_PLUGIN_TREE_ITEM_MENU_PROPERTIES: + { + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PLUGINPROPERTIES), + hwndDlg, + PhpPluginPropertiesDlgProc, + (LPARAM)selectedNode->PluginInstance + ); + } + break; + } + } + } + break; + case WM_PH_PLUGINS_SHOWPROPERTIES: + { + PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)lParam; + PPH_PLUGIN_TREE_ROOT_NODE selectedNode = (PPH_PLUGIN_TREE_ROOT_NODE)mouseEvent->Node; + + if (!selectedNode) + break; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PLUGINPROPERTIES), + hwndDlg, + PhpPluginPropertiesDlgProc, + (LPARAM)selectedNode->PluginInstance + ); + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + TreeNew_AutoSizeColumn(context->TreeNewHandle, PH_PLUGIN_TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, context->MinimumSize.right, context->MinimumSize.bottom); + } + break; + } + + return FALSE; +} + +NTSTATUS PhpPluginsDialogThreadStart( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + PhPluginsWindowHandle = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PLUGINS), + NULL, + PhpPluginsDlgProc + ); + + PhSetEvent(&PhPluginsInitializedEvent); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(PhPluginsWindowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + PhResetEvent(&PhPluginsInitializedEvent); + + if (PhPluginsThreadHandle) + { + NtClose(PhPluginsThreadHandle); + PhPluginsThreadHandle = NULL; + } + + return STATUS_SUCCESS; +} + +VOID PhShowPluginsDialog( + _In_ HWND ParentWindowHandle + ) +{ + if (PhPluginsEnabled) + { + if (!PhPluginsThreadHandle) + { + if (!NT_SUCCESS(PhCreateThreadEx(&PhPluginsThreadHandle, PhpPluginsDialogThreadStart, NULL))) + { + PhShowError(PhMainWndHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&PhPluginsInitializedEvent, NULL); + } + + PostMessage(PhPluginsWindowHandle, WM_PH_PLUGINS_SHOWDIALOG, 0, 0); + } + else + { + PhShowInformation2( + ParentWindowHandle, + L"Plugins are not enabled.", + L"To use plugins enable them in Options and restart Process Hacker." + ); + } +} + +VOID PhpRefreshPluginDetails( + _In_ HWND hwndDlg, + _In_ PPH_PLUGIN SelectedPlugin + ) +{ + PPH_STRING fileName; + PH_IMAGE_VERSION_INFO versionInfo; + + fileName = SelectedPlugin->FileName; + + PhSetDialogItemText(hwndDlg, IDC_NAME, SelectedPlugin->Information.DisplayName ? SelectedPlugin->Information.DisplayName : L"(unnamed)"); + PhSetDialogItemText(hwndDlg, IDC_INTERNALNAME, SelectedPlugin->Name.Buffer); + PhSetDialogItemText(hwndDlg, IDC_AUTHOR, SelectedPlugin->Information.Author); + PhSetDialogItemText(hwndDlg, IDC_FILENAME, PH_AUTO_T(PH_STRING, PhGetBaseName(fileName))->Buffer); + PhSetDialogItemText(hwndDlg, IDC_DESCRIPTION, SelectedPlugin->Information.Description); + PhSetDialogItemText(hwndDlg, IDC_URL, SelectedPlugin->Information.Url); + + if (PhInitializeImageVersionInfo(&versionInfo, fileName->Buffer)) + { + PhSetDialogItemText(hwndDlg, IDC_VERSION, PhGetStringOrDefault(versionInfo.FileVersion, L"Unknown")); + PhDeleteImageVersionInfo(&versionInfo); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_VERSION, L"Unknown"); + } + + ShowWindow(GetDlgItem(hwndDlg, IDC_OPENURL), SelectedPlugin->Information.Url ? SW_SHOW : SW_HIDE); + EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), SelectedPlugin->Information.HasOptions); +} + +INT_PTR CALLBACK PhpPluginPropertiesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_PLUGIN selectedPlugin = NULL; + + if (uMsg == WM_INITDIALOG) + { + selectedPlugin = (PPH_PLUGIN)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, selectedPlugin); + } + else + { + selectedPlugin = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (selectedPlugin == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhpRefreshPluginDetails(hwndDlg, selectedPlugin); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_OPTIONS: + { + PhInvokeCallback(PhGetPluginCallback(selectedPlugin, PluginCallbackShowOptions), hwndDlg); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_CLICK: + { + if (header->hwndFrom == GetDlgItem(hwndDlg, IDC_OPENURL)) + { + PhShellExecute(hwndDlg, selectedPlugin->Information.Url, NULL); + } + } + break; + } + } + break; + } + + return FALSE; +} + +typedef struct _PLUGIN_DISABLED_CONTEXT +{ + PH_QUEUED_LOCK ListLock; + HWND DialogHandle; + HWND ListViewHandle; +} PLUGIN_DISABLED_CONTEXT, *PPLUGIN_DISABLED_CONTEXT; + +VOID PhpAddDisabledPlugins( + _In_ PPLUGIN_DISABLED_CONTEXT Context + ) +{ + PPH_STRING disabled; + PH_STRINGREF remainingPart; + PH_STRINGREF part; + PPH_STRING displayText; + INT lvItemIndex; + + disabled = PhGetStringSetting(L"DisabledPlugins"); + remainingPart = disabled->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + displayText = PhCreateString2(&part); + + PhAcquireQueuedLockExclusive(&Context->ListLock); + lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, PhGetString(displayText), displayText); + PhReleaseQueuedLockExclusive(&Context->ListLock); + + ListView_SetCheckState(Context->ListViewHandle, lvItemIndex, TRUE); + } + } + + PhDereferenceObject(disabled); +} + +ULONG PhpDisabledPluginsCount( + VOID + ) +{ + PPH_STRING disabled; + PH_STRINGREF remainingPart; + PH_STRINGREF part; + ULONG count = 0; + + disabled = PhGetStringSetting(L"DisabledPlugins"); + remainingPart = disabled->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + count++; + } + + PhDereferenceObject(disabled); + + return count; +} + +INT_PTR CALLBACK PhpPluginsDisabledDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPLUGIN_DISABLED_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(PLUGIN_DISABLED_CONTEXT)); + memset(context, 0, sizeof(PLUGIN_DISABLED_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); + } + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + context->DialogHandle = hwndDlg; + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST_DISABLED); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + ListView_SetExtendedListViewStyleEx(context->ListViewHandle, + LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER, + LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 400, L"Property"); + PhSetExtendedListView(context->ListViewHandle); + + PhpAddDisabledPlugins(context); + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + if (header->code == LVN_ITEMCHANGED) + { + LPNM_LISTVIEW listView = (LPNM_LISTVIEW)lParam; + + if (!PhTryAcquireReleaseQueuedLockExclusive(&context->ListLock)) + break; + + if (listView->uChanged & LVIF_STATE) + { + switch (listView->uNewState & LVIS_STATEIMAGEMASK) + { + case INDEXTOSTATEIMAGEMASK(2): // checked + { + PPH_STRING param = (PPH_STRING)listView->lParam; + + PhSetPluginDisabled(¶m->sr, TRUE); + } + break; + case INDEXTOSTATEIMAGEMASK(1): // unchecked + { + PPH_STRING param = (PPH_STRING)listView->lParam; + + PhSetPluginDisabled(¶m->sr, FALSE); + } + break; + } + } + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/procgrp.c b/ProcessHacker/procgrp.c index 87f76ababcaa..fa3c40d06f28 100644 --- a/ProcessHacker/procgrp.c +++ b/ProcessHacker/procgrp.c @@ -1,343 +1,343 @@ -/* - * Process Hacker - - * process grouping - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -typedef struct _PHP_PROCESS_DATA -{ - PPH_PROCESS_NODE Process; - LIST_ENTRY ListEntry; - HWND WindowHandle; -} PHP_PROCESS_DATA, *PPHP_PROCESS_DATA; - -PPH_LIST PhpCreateProcessDataList( - _In_ PPH_LIST Processes - ) -{ - PPH_LIST processDataList; - ULONG i; - - processDataList = PhCreateList(Processes->Count); - - for (i = 0; i < Processes->Count; i++) - { - PPH_PROCESS_NODE process = Processes->Items[i]; - PPHP_PROCESS_DATA processData; - - if (PH_IS_FAKE_PROCESS_ID(process->ProcessId) || process->ProcessId == SYSTEM_IDLE_PROCESS_ID) - continue; - - processData = PhAllocate(sizeof(PHP_PROCESS_DATA)); - memset(processData, 0, sizeof(PHP_PROCESS_DATA)); - processData->Process = process; - PhAddItemList(processDataList, processData); - } - - return processDataList; -} - -VOID PhpDestroyProcessDataList( - _In_ PPH_LIST List - ) -{ - ULONG i; - - for (i = 0; i < List->Count; i++) - { - PPHP_PROCESS_DATA processData = List->Items[i]; - PhFree(processData); - } - - PhDereferenceObject(List); -} - -VOID PhpProcessDataListToLinkedList( - _In_ PPH_LIST List, - _Out_ PLIST_ENTRY ListHead - ) -{ - ULONG i; - - InitializeListHead(ListHead); - - for (i = 0; i < List->Count; i++) - { - PPHP_PROCESS_DATA processData = List->Items[i]; - InsertTailList(ListHead, &processData->ListEntry); - } -} - -VOID PhpProcessDataListToHashtable( - _In_ PPH_LIST List, - _Out_ PPH_HASHTABLE *Hashtable - ) -{ - PPH_HASHTABLE hashtable; - ULONG i; - - hashtable = PhCreateSimpleHashtable(List->Count); - - for (i = 0; i < List->Count; i++) - { - PPHP_PROCESS_DATA processData = List->Items[i]; - PhAddItemSimpleHashtable(hashtable, processData->Process->ProcessId, processData); - } - - *Hashtable = hashtable; -} - -typedef struct _QUERY_WINDOWS_CONTEXT -{ - PPH_HASHTABLE ProcessDataHashtable; -} QUERY_WINDOWS_CONTEXT, *PQUERY_WINDOWS_CONTEXT; - -BOOL CALLBACK PhpQueryWindowsEnumWindowsProc( - _In_ HWND hwnd, - _In_ LPARAM lParam - ) -{ - PQUERY_WINDOWS_CONTEXT context = (PQUERY_WINDOWS_CONTEXT)lParam; - ULONG processId; - PPHP_PROCESS_DATA processData; - HWND parentWindow; - - if (!IsWindowVisible(hwnd)) - return TRUE; - - GetWindowThreadProcessId(hwnd, &processId); - processData = PhFindItemSimpleHashtable2(context->ProcessDataHashtable, UlongToHandle(processId)); - - if (!processData || processData->WindowHandle) - return TRUE; - - if (!((parentWindow = GetParent(hwnd)) && IsWindowVisible(parentWindow)) && // Skip windows with a visible parent - PhGetWindowTextEx(hwnd, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0) // Skip windows with no title - { - processData->WindowHandle = hwnd; - } - - return TRUE; -} - -PPH_STRING PhpGetRelevantFileName( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG Flags - ) -{ - if (Flags & PH_GROUP_PROCESSES_FILE_PATH) - return ProcessItem->FileName; - else - return ProcessItem->ProcessName; -} - -BOOLEAN PhpEqualFileNameAndUserName( - _In_ PPH_STRING FileName, - _In_ PPH_STRING UserName, - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG Flags - ) -{ - PPH_STRING otherFileName; - PPH_STRING otherUserName; - - otherFileName = PhpGetRelevantFileName(ProcessItem, Flags); - otherUserName = ProcessItem->UserName; - - return - otherFileName && PhEqualString(otherFileName, FileName, TRUE) && - otherUserName && PhEqualString(otherUserName, UserName, TRUE); -} - -PPHP_PROCESS_DATA PhpFindGroupRoot( - _In_ PPHP_PROCESS_DATA ProcessData, - _In_ PPH_HASHTABLE ProcessDataHashtable, - _In_ ULONG Flags - ) -{ - PPH_PROCESS_NODE root; - PPHP_PROCESS_DATA rootProcessData; - PPH_PROCESS_NODE parent; - PPHP_PROCESS_DATA processData; - PPH_STRING fileName; - PPH_STRING userName; - - root = ProcessData->Process; - rootProcessData = ProcessData; - fileName = PhpGetRelevantFileName(ProcessData->Process->ProcessItem, Flags); - userName = ProcessData->Process->ProcessItem->UserName; - - if (ProcessData->WindowHandle) - return rootProcessData; - - while (parent = root->Parent) - { - if ((processData = PhFindItemSimpleHashtable2(ProcessDataHashtable, parent->ProcessId)) && - PhpEqualFileNameAndUserName(fileName, userName, parent->ProcessItem, Flags)) - { - root = parent; - rootProcessData = processData; - - if (processData->WindowHandle) - break; - } - else - { - break; - } - } - - return rootProcessData; -} - -VOID PhpAddGroupMember( - _In_ PPHP_PROCESS_DATA ProcessData, - _Inout_ PPH_LIST List - ) -{ - PhReferenceObject(ProcessData->Process->ProcessItem); - PhAddItemList(List, ProcessData->Process->ProcessItem); - RemoveEntryList(&ProcessData->ListEntry); -} - -VOID PhpAddGroupMembersFromRoot( - _In_ PPHP_PROCESS_DATA ProcessData, - _Inout_ PPH_LIST List, - _In_ PPH_HASHTABLE ProcessDataHashtable, - _In_ ULONG Flags - ) -{ - PPH_STRING fileName; - PPH_STRING userName; - ULONG i; - - PhpAddGroupMember(ProcessData, List); - fileName = PhpGetRelevantFileName(ProcessData->Process->ProcessItem, Flags); - userName = ProcessData->Process->ProcessItem->UserName; - - for (i = 0; i < ProcessData->Process->Children->Count; i++) - { - PPH_PROCESS_NODE node = ProcessData->Process->Children->Items[i]; - PPHP_PROCESS_DATA processData; - - if ((processData = PhFindItemSimpleHashtable2(ProcessDataHashtable, node->ProcessId)) && - PhpEqualFileNameAndUserName(fileName, userName, node->ProcessItem, Flags) && - node->ProcessItem->UserName && PhEqualString(node->ProcessItem->UserName, userName, TRUE) && - !processData->WindowHandle) - { - PhpAddGroupMembersFromRoot(processData, List, ProcessDataHashtable, Flags); - } - } -} - -PPH_LIST PhCreateProcessGroupList( - _In_opt_ PPH_SORT_LIST_FUNCTION SortListFunction, - _In_opt_ PVOID Context, - _In_ ULONG MaximumGroups, - _In_ ULONG Flags - ) -{ - PPH_LIST processList; - PPH_LIST processDataList; - LIST_ENTRY processDataListHead; // We will be removing things from this list as we group processes together - PPH_HASHTABLE processDataHashtable; // Process ID to process data hashtable - QUERY_WINDOWS_CONTEXT queryWindowsContext; - PPH_LIST processGroupList; - - // We group together processes that share a common ancestor and have the same file name, where - // the ancestor must have a visible window and all other processes in the group do not have a - // visible window. All processes in the group must have the same user name. All ancestors up to - // the lowest common ancestor must have the same file name and user name. - // - // The current algorithm is greedy and may not detect groups that have many processes, each with - // a small usage amount. - - processList = PhDuplicateProcessNodeList(); - - if (SortListFunction) - SortListFunction(processList, Context); - - processDataList = PhpCreateProcessDataList(processList); - PhDereferenceObject(processList); - PhpProcessDataListToLinkedList(processDataList, &processDataListHead); - PhpProcessDataListToHashtable(processDataList, &processDataHashtable); - - queryWindowsContext.ProcessDataHashtable = processDataHashtable; - PhEnumChildWindows(NULL, 0x800, PhpQueryWindowsEnumWindowsProc, (LPARAM)&queryWindowsContext); - - processGroupList = PhCreateList(10); - - while (processDataListHead.Flink != &processDataListHead && processGroupList->Count < MaximumGroups) - { - PPHP_PROCESS_DATA processData = CONTAINING_RECORD(processDataListHead.Flink, PHP_PROCESS_DATA, ListEntry); - PPH_PROCESS_GROUP processGroup; - PPH_STRING fileName; - PPH_STRING userName; - - processGroup = PhAllocate(sizeof(PH_PROCESS_GROUP)); - processGroup->Processes = PhCreateList(4); - fileName = PhpGetRelevantFileName(processData->Process->ProcessItem, Flags); - userName = processData->Process->ProcessItem->UserName; - - if (!fileName || !userName || (Flags & PH_GROUP_PROCESSES_DONT_GROUP)) - { - processGroup->Representative = processData->Process->ProcessItem; - PhpAddGroupMember(processData, processGroup->Processes); - } - else - { - processData = PhpFindGroupRoot(processData, processDataHashtable, Flags); - processGroup->Representative = processData->Process->ProcessItem; - PhpAddGroupMembersFromRoot(processData, processGroup->Processes, processDataHashtable, Flags); - } - - processGroup->WindowHandle = processData->WindowHandle; - - PhAddItemList(processGroupList, processGroup); - } - - PhDereferenceObject(processDataHashtable); - PhpDestroyProcessDataList(processDataList); - - return processGroupList; -} - -VOID PhFreeProcessGroupList( - _In_ PPH_LIST List - ) -{ - ULONG i; - - for (i = 0; i < List->Count; i++) - { - PPH_PROCESS_GROUP processGroup = List->Items[i]; - - PhDereferenceObjects(processGroup->Processes->Items, processGroup->Processes->Count); - PhDereferenceObject(processGroup->Processes); - PhFree(processGroup); - } - - PhDereferenceObject(List); -} +/* + * Process Hacker - + * process grouping + * + * Copyright (C) 2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +typedef struct _PHP_PROCESS_DATA +{ + PPH_PROCESS_NODE Process; + LIST_ENTRY ListEntry; + HWND WindowHandle; +} PHP_PROCESS_DATA, *PPHP_PROCESS_DATA; + +PPH_LIST PhpCreateProcessDataList( + _In_ PPH_LIST Processes + ) +{ + PPH_LIST processDataList; + ULONG i; + + processDataList = PhCreateList(Processes->Count); + + for (i = 0; i < Processes->Count; i++) + { + PPH_PROCESS_NODE process = Processes->Items[i]; + PPHP_PROCESS_DATA processData; + + if (PH_IS_FAKE_PROCESS_ID(process->ProcessId) || process->ProcessId == SYSTEM_IDLE_PROCESS_ID) + continue; + + processData = PhAllocate(sizeof(PHP_PROCESS_DATA)); + memset(processData, 0, sizeof(PHP_PROCESS_DATA)); + processData->Process = process; + PhAddItemList(processDataList, processData); + } + + return processDataList; +} + +VOID PhpDestroyProcessDataList( + _In_ PPH_LIST List + ) +{ + ULONG i; + + for (i = 0; i < List->Count; i++) + { + PPHP_PROCESS_DATA processData = List->Items[i]; + PhFree(processData); + } + + PhDereferenceObject(List); +} + +VOID PhpProcessDataListToLinkedList( + _In_ PPH_LIST List, + _Out_ PLIST_ENTRY ListHead + ) +{ + ULONG i; + + InitializeListHead(ListHead); + + for (i = 0; i < List->Count; i++) + { + PPHP_PROCESS_DATA processData = List->Items[i]; + InsertTailList(ListHead, &processData->ListEntry); + } +} + +VOID PhpProcessDataListToHashtable( + _In_ PPH_LIST List, + _Out_ PPH_HASHTABLE *Hashtable + ) +{ + PPH_HASHTABLE hashtable; + ULONG i; + + hashtable = PhCreateSimpleHashtable(List->Count); + + for (i = 0; i < List->Count; i++) + { + PPHP_PROCESS_DATA processData = List->Items[i]; + PhAddItemSimpleHashtable(hashtable, processData->Process->ProcessId, processData); + } + + *Hashtable = hashtable; +} + +typedef struct _QUERY_WINDOWS_CONTEXT +{ + PPH_HASHTABLE ProcessDataHashtable; +} QUERY_WINDOWS_CONTEXT, *PQUERY_WINDOWS_CONTEXT; + +BOOLEAN CALLBACK PhpQueryWindowsEnumWindowsProc( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ) +{ + PQUERY_WINDOWS_CONTEXT context = (PQUERY_WINDOWS_CONTEXT)Context; + ULONG processId; + PPHP_PROCESS_DATA processData; + HWND parentWindow; + + if (!IsWindowVisible(WindowHandle)) + return TRUE; + + GetWindowThreadProcessId(WindowHandle, &processId); + processData = PhFindItemSimpleHashtable2(context->ProcessDataHashtable, UlongToHandle(processId)); + + if (!processData || processData->WindowHandle) + return TRUE; + + if (!((parentWindow = GetParent(WindowHandle)) && IsWindowVisible(parentWindow)) && // Skip windows with a visible parent + PhGetWindowTextEx(WindowHandle, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0) // Skip windows with no title + { + processData->WindowHandle = WindowHandle; + } + + return TRUE; +} + +PPH_STRING PhpGetRelevantFileName( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ ULONG Flags + ) +{ + if (Flags & PH_GROUP_PROCESSES_FILE_PATH) + return ProcessItem->FileName; + else + return ProcessItem->ProcessName; +} + +BOOLEAN PhpEqualFileNameAndUserName( + _In_ PPH_STRING FileName, + _In_ PSID UserSid, + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ ULONG Flags + ) +{ + PPH_STRING otherFileName; + PSID otherUserSid; + + otherFileName = PhpGetRelevantFileName(ProcessItem, Flags); + otherUserSid = ProcessItem->Sid; + + return + otherFileName && PhEqualString(otherFileName, FileName, TRUE) && + otherUserSid && RtlEqualSid(otherUserSid, UserSid); +} + +PPHP_PROCESS_DATA PhpFindGroupRoot( + _In_ PPHP_PROCESS_DATA ProcessData, + _In_ PPH_HASHTABLE ProcessDataHashtable, + _In_ ULONG Flags + ) +{ + PPH_PROCESS_NODE root; + PPHP_PROCESS_DATA rootProcessData; + PPH_PROCESS_NODE parent; + PPHP_PROCESS_DATA processData; + PPH_STRING fileName; + PPH_STRING userSid; + + root = ProcessData->Process; + rootProcessData = ProcessData; + fileName = PhpGetRelevantFileName(ProcessData->Process->ProcessItem, Flags); + userSid = ProcessData->Process->ProcessItem->Sid; + + if (ProcessData->WindowHandle) + return rootProcessData; + + while (parent = root->Parent) + { + if ((processData = PhFindItemSimpleHashtable2(ProcessDataHashtable, parent->ProcessId)) && + PhpEqualFileNameAndUserName(fileName, userSid, parent->ProcessItem, Flags)) + { + root = parent; + rootProcessData = processData; + + if (processData->WindowHandle) + break; + } + else + { + break; + } + } + + return rootProcessData; +} + +VOID PhpAddGroupMember( + _In_ PPHP_PROCESS_DATA ProcessData, + _Inout_ PPH_LIST List + ) +{ + PhReferenceObject(ProcessData->Process->ProcessItem); + PhAddItemList(List, ProcessData->Process->ProcessItem); + RemoveEntryList(&ProcessData->ListEntry); +} + +VOID PhpAddGroupMembersFromRoot( + _In_ PPHP_PROCESS_DATA ProcessData, + _Inout_ PPH_LIST List, + _In_ PPH_HASHTABLE ProcessDataHashtable, + _In_ ULONG Flags + ) +{ + PPH_STRING fileName; + PSID userSid; + ULONG i; + + PhpAddGroupMember(ProcessData, List); + fileName = PhpGetRelevantFileName(ProcessData->Process->ProcessItem, Flags); + userSid = ProcessData->Process->ProcessItem->Sid; + + for (i = 0; i < ProcessData->Process->Children->Count; i++) + { + PPH_PROCESS_NODE node = ProcessData->Process->Children->Items[i]; + PPHP_PROCESS_DATA processData; + + if ((processData = PhFindItemSimpleHashtable2(ProcessDataHashtable, node->ProcessId)) && + PhpEqualFileNameAndUserName(fileName, userSid, node->ProcessItem, Flags) && + node->ProcessItem->Sid && RtlEqualSid(node->ProcessItem->Sid, userSid) && + !processData->WindowHandle) + { + PhpAddGroupMembersFromRoot(processData, List, ProcessDataHashtable, Flags); + } + } +} + +PPH_LIST PhCreateProcessGroupList( + _In_opt_ PPH_SORT_LIST_FUNCTION SortListFunction, + _In_opt_ PVOID Context, + _In_ ULONG MaximumGroups, + _In_ ULONG Flags + ) +{ + PPH_LIST processList; + PPH_LIST processDataList; + LIST_ENTRY processDataListHead; // We will be removing things from this list as we group processes together + PPH_HASHTABLE processDataHashtable; // Process ID to process data hashtable + QUERY_WINDOWS_CONTEXT queryWindowsContext; + PPH_LIST processGroupList; + + // We group together processes that share a common ancestor and have the same file name, where + // the ancestor must have a visible window and all other processes in the group do not have a + // visible window. All processes in the group must have the same user name. All ancestors up to + // the lowest common ancestor must have the same file name and user name. + // + // The current algorithm is greedy and may not detect groups that have many processes, each with + // a small usage amount. + + processList = PhDuplicateProcessNodeList(); + + if (SortListFunction) + SortListFunction(processList, Context); + + processDataList = PhpCreateProcessDataList(processList); + PhDereferenceObject(processList); + PhpProcessDataListToLinkedList(processDataList, &processDataListHead); + PhpProcessDataListToHashtable(processDataList, &processDataHashtable); + + queryWindowsContext.ProcessDataHashtable = processDataHashtable; + PhEnumChildWindows(NULL, 0x800, PhpQueryWindowsEnumWindowsProc, &queryWindowsContext); + + processGroupList = PhCreateList(10); + + while (processDataListHead.Flink != &processDataListHead && processGroupList->Count < MaximumGroups) + { + PPHP_PROCESS_DATA processData = CONTAINING_RECORD(processDataListHead.Flink, PHP_PROCESS_DATA, ListEntry); + PPH_PROCESS_GROUP processGroup; + PPH_STRING fileName; + PSID userSid; + + processGroup = PhAllocate(sizeof(PH_PROCESS_GROUP)); + processGroup->Processes = PhCreateList(4); + fileName = PhpGetRelevantFileName(processData->Process->ProcessItem, Flags); + userSid = processData->Process->ProcessItem->Sid; + + if (!fileName || !userSid || (Flags & PH_GROUP_PROCESSES_DONT_GROUP)) + { + processGroup->Representative = processData->Process->ProcessItem; + PhpAddGroupMember(processData, processGroup->Processes); + } + else + { + processData = PhpFindGroupRoot(processData, processDataHashtable, Flags); + processGroup->Representative = processData->Process->ProcessItem; + PhpAddGroupMembersFromRoot(processData, processGroup->Processes, processDataHashtable, Flags); + } + + processGroup->WindowHandle = processData->WindowHandle; + + PhAddItemList(processGroupList, processGroup); + } + + PhDereferenceObject(processDataHashtable); + PhpDestroyProcessDataList(processDataList); + + return processGroupList; +} + +VOID PhFreeProcessGroupList( + _In_ PPH_LIST List + ) +{ + ULONG i; + + for (i = 0; i < List->Count; i++) + { + PPH_PROCESS_GROUP processGroup = List->Items[i]; + + PhDereferenceObjects(processGroup->Processes->Items, processGroup->Processes->Count); + PhDereferenceObject(processGroup->Processes); + PhFree(processGroup); + } + + PhDereferenceObject(List); +} diff --git a/ProcessHacker/procmtgn.c b/ProcessHacker/procmtgn.c index 26b9915e41e2..b1e9a6ce75ed 100644 --- a/ProcessHacker/procmtgn.c +++ b/ProcessHacker/procmtgn.c @@ -3,6 +3,7 @@ * process mitigation information * * Copyright (C) 2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -51,7 +52,7 @@ NTSTATUS PhpCopyProcessMitigationPolicy( return status; } - memcpy(Destination, (PCHAR)&policyInfo + Offset, Size); + memcpy(Destination, PTR_ADD_OFFSET(&policyInfo, Offset), Size); *Status = STATUS_SUCCESS; return status; @@ -99,7 +100,7 @@ NTSTATUS PhGetProcessMitigationPolicy( #define COPY_PROCESS_MITIGATION_POLICY(PolicyName, StructName) \ if (NT_SUCCESS(PhpCopyProcessMitigationPolicy(&status, ProcessHandle, Process##PolicyName##Policy, \ - FIELD_OFFSET(PROCESS_MITIGATION_POLICY_INFORMATION, PolicyName##Policy), \ + UFIELD_OFFSET(PROCESS_MITIGATION_POLICY_INFORMATION, PolicyName##Policy), \ sizeof(StructName), \ &Information->PolicyName##Policy))) \ { \ @@ -115,6 +116,10 @@ NTSTATUS PhGetProcessMitigationPolicy( COPY_PROCESS_MITIGATION_POLICY(Signature, PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY); COPY_PROCESS_MITIGATION_POLICY(FontDisable, PROCESS_MITIGATION_FONT_DISABLE_POLICY); COPY_PROCESS_MITIGATION_POLICY(ImageLoad, PROCESS_MITIGATION_IMAGE_LOAD_POLICY); + COPY_PROCESS_MITIGATION_POLICY(SystemCallFilter, PROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY); // REDSTONE3 + COPY_PROCESS_MITIGATION_POLICY(PayloadRestriction, PROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY); + COPY_PROCESS_MITIGATION_POLICY(ChildProcess, PROCESS_MITIGATION_CHILD_PROCESS_POLICY); + COPY_PROCESS_MITIGATION_POLICY(SideChannelIsolation, PROCESS_MITIGATION_SIDE_CHANNEL_ISOLATION_POLICY); return status; } @@ -173,6 +178,7 @@ BOOLEAN PhDescribeProcessMitigationPolicy( PhAppendStringBuilder2(&sb, L" ("); if (data->EnableHighEntropy) PhAppendStringBuilder2(&sb, L"high entropy, "); if (data->EnableForceRelocateImages) PhAppendStringBuilder2(&sb, L"force relocate, "); + if (data->DisallowStrippedImages) PhAppendStringBuilder2(&sb, L"disallow stripped, "); if (PhEndsWithStringRef2(&sb.String->sr, L", ", FALSE)) PhRemoveEndStringBuilder(&sb, 2); PhAppendCharStringBuilder(&sb, ')'); } @@ -219,6 +225,17 @@ BOOLEAN PhDescribeProcessMitigationPolicy( result = TRUE; } + + if (data->AllowRemoteDowngrade) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Dynamic code downgradable"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Allow non-AppContainer processes to modify all of the dynamic code settings for the calling process, including relaxing dynamic code restrictions after they have been set.\r\n"); + + result = TRUE; + } } break; case ProcessStrictHandleCheckPolicy: @@ -251,6 +268,17 @@ BOOLEAN PhDescribeProcessMitigationPolicy( result = TRUE; } + + if (data->AuditDisallowWin32kSystemCalls) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Win32k system calls (Audit)"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Win32k (GDI/USER) system calls will trigger an ETW event.\r\n"); + + result = TRUE; + } } break; case ProcessExtensionPointDisablePolicy: @@ -276,10 +304,21 @@ BOOLEAN PhDescribeProcessMitigationPolicy( if (data->EnableControlFlowGuard) { if (ShortDescription) - *ShortDescription = PhCreateString(L"CF Guard"); + { + PhInitializeStringBuilder(&sb, 50); + if (data->StrictMode) PhAppendStringBuilder2(&sb, L"Strict "); + PhAppendStringBuilder2(&sb, L"CF Guard"); + *ShortDescription = PhFinalStringBuilderString(&sb); + } if (LongDescription) - *LongDescription = PhCreateString(L"Control Flow Guard (CFG) is enabled for the process.\r\n"); + { + PhInitializeStringBuilder(&sb, 100); + PhAppendStringBuilder2(&sb, L"Control Flow Guard (CFG) is enabled for the process.\r\n"); + if (data->StrictMode) PhAppendStringBuilder2(&sb, L"Strict CFG : only CFG modules can be loaded.\r\n"); + if (data->EnableExportSuppression) PhAppendStringBuilder2(&sb, L"Dll Exports can be marked as CFG invalid targets.\r\n"); + *LongDescription = PhFinalStringBuilderString(&sb); + } result = TRUE; } @@ -327,7 +366,12 @@ BOOLEAN PhDescribeProcessMitigationPolicy( *ShortDescription = PhCreateString(L"Non-system fonts disabled"); if (LongDescription) - *LongDescription = PhCreateString(L"Non-system fonts cannot be used in this process.\r\n"); + { + PhInitializeStringBuilder(&sb, 100); + PhAppendStringBuilder2(&sb, L"Non-system fonts cannot be used in this process.\r\n"); + if (data->AuditNonSystemFontLoading) PhAppendStringBuilder2(&sb, L"Loading a non-system font in this process will trigger an ETW event.\r\n"); + *LongDescription = PhFinalStringBuilderString(&sb); + } result = TRUE; } @@ -375,9 +419,166 @@ BOOLEAN PhDescribeProcessMitigationPolicy( } } break; + case ProcessSystemCallFilterPolicy: + { + PPROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY data = Data; + + if (data->FilterId) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"System call filtering"); + + if (LongDescription) + *LongDescription = PhCreateString(L"System call filtering is active.\r\n"); + + result = TRUE; + } + } + break; + case ProcessPayloadRestrictionPolicy: + { + PPROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY data = Data; + + if (data->EnableExportAddressFilter || data->EnableExportAddressFilterPlus || + data->EnableImportAddressFilter || data->EnableRopStackPivot || + data->EnableRopCallerCheck || data->EnableRopSimExec) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Payload restrictions"); + + if (LongDescription) + { + PhInitializeStringBuilder(&sb, 100); + PhAppendStringBuilder2(&sb, L"Payload restrictions are enabled for this process.\r\n"); + if (data->EnableExportAddressFilter) PhAppendStringBuilder2(&sb, L"Export Address Filtering is enabled.\r\n"); + if (data->EnableExportAddressFilterPlus) PhAppendStringBuilder2(&sb, L"Export Address Filtering (Plus) is enabled.\r\n"); + if (data->EnableImportAddressFilter) PhAppendStringBuilder2(&sb, L"Import Address Filtering is enabled.\r\n"); + if (data->EnableRopStackPivot) PhAppendStringBuilder2(&sb, L"StackPivot is enabled.\r\n"); + if (data->EnableRopCallerCheck) PhAppendStringBuilder2(&sb, L"CallerCheck is enabled.\r\n"); + if (data->EnableRopSimExec) PhAppendStringBuilder2(&sb, L"SimExec is enabled.\r\n"); + *LongDescription = PhFinalStringBuilderString(&sb); + } + + result = TRUE; + } + } + break; + case ProcessChildProcessPolicy: + { + PPROCESS_MITIGATION_CHILD_PROCESS_POLICY data = Data; + + if (data->NoChildProcessCreation) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Child process creation disabled"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Child processes cannot be created by this process.\r\n"); + + result = TRUE; + } + } + break; + case ProcessSideChannelIsolationPolicy: + { + PPROCESS_MITIGATION_SIDE_CHANNEL_ISOLATION_POLICY data = Data; + + if (data->SmtBranchTargetIsolation) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"SMT-thread branch target isolation"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Branch target pollution cross-SMT-thread in user mode is enabled.\r\n"); + + result = TRUE; + } + + if (data->IsolateSecurityDomain) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Distinct security domain"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Isolated security domain is enabled.\r\n"); + + result = TRUE; + } + + if (data->DisablePageCombine) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Restricted page combining"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Disables all page combining for this process.\r\n"); + + result = TRUE; + } + + if (data->SpeculativeStoreBypassDisable) + { + if (ShortDescription) + *ShortDescription = PhCreateString(L"Restricted page combining"); + + if (LongDescription) + *LongDescription = PhCreateString(L"Memory Disambiguation is enabled for this process.\r\n"); + + result = TRUE; + } + } + break; default: result = FALSE; } return result; } + +NTSTATUS PhGetProcessSystemDllInitBlock( + _In_ HANDLE ProcessHandle, + _Out_ PPS_SYSTEM_DLL_INIT_BLOCK *SystemDllInitBlock + ) +{ + NTSTATUS status; + PH_STRINGREF systemRoot; + PVOID ldrInitBlockBaseAddress = NULL; + PPH_STRING ntdllFileName; + + PhGetSystemRoot(&systemRoot); + ntdllFileName = PhConcatStringRefZ(&systemRoot, L"\\System32\\ntdll.dll"); + + status = PhGetProcedureAddressRemote( + ProcessHandle, + ntdllFileName->Buffer, + "LdrSystemDllInitBlock", + 0, + &ldrInitBlockBaseAddress, + NULL + ); + + PhDereferenceObject(ntdllFileName); + + if (NT_SUCCESS(status) && ldrInitBlockBaseAddress) + { + PPS_SYSTEM_DLL_INIT_BLOCK ldrInitBlock; + + ldrInitBlock = PhAllocate(sizeof(PS_SYSTEM_DLL_INIT_BLOCK)); + memset(ldrInitBlock, 0, sizeof(PS_SYSTEM_DLL_INIT_BLOCK)); + + status = NtReadVirtualMemory( + ProcessHandle, + ldrInitBlockBaseAddress, + ldrInitBlock, + sizeof(PS_SYSTEM_DLL_INIT_BLOCK), + NULL + ); + + if (NT_SUCCESS(status)) + *SystemDllInitBlock = ldrInitBlock; + else + PhFree(ldrInitBlock); + } + + return status; +} diff --git a/ProcessHacker/procprp.c b/ProcessHacker/procprp.c index b106b68a795b..d44f6c24c1dd 100644 --- a/ProcessHacker/procprp.c +++ b/ProcessHacker/procprp.c @@ -1,724 +1,746 @@ -/* - * Process Hacker - - * Process properties - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include - -#include - -#include - -#include -#include -#include - -PPH_OBJECT_TYPE PhpProcessPropContextType; -PPH_OBJECT_TYPE PhpProcessPropPageContextType; -PH_STRINGREF PhpLoadingText = PH_STRINGREF_INIT(L"Loading..."); - -static RECT MinimumSize = { -1, -1, -1, -1 }; - -BOOLEAN PhProcessPropInitialization( - VOID - ) -{ - PhpProcessPropContextType = PhCreateObjectType(L"ProcessPropContext", 0, PhpProcessPropContextDeleteProcedure); - PhpProcessPropPageContextType = PhCreateObjectType(L"ProcessPropPageContext", 0, PhpProcessPropPageContextDeleteProcedure); - - return TRUE; -} - -PPH_PROCESS_PROPCONTEXT PhCreateProcessPropContext( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PPH_PROCESS_PROPCONTEXT propContext; - PROPSHEETHEADER propSheetHeader; - - propContext = PhCreateObject(sizeof(PH_PROCESS_PROPCONTEXT), PhpProcessPropContextType); - memset(propContext, 0, sizeof(PH_PROCESS_PROPCONTEXT)); - - propContext->PropSheetPages = PhAllocate(sizeof(HPROPSHEETPAGE) * PH_PROCESS_PROPCONTEXT_MAXPAGES); - - if (!PH_IS_FAKE_PROCESS_ID(ProcessItem->ProcessId)) - { - propContext->Title = PhFormatString( - L"%s (%u)", - ProcessItem->ProcessName->Buffer, - HandleToUlong(ProcessItem->ProcessId) - ); - } - else - { - PhSetReference(&propContext->Title, ProcessItem->ProcessName); - } - - memset(&propSheetHeader, 0, sizeof(PROPSHEETHEADER)); - propSheetHeader.dwSize = sizeof(PROPSHEETHEADER); - propSheetHeader.dwFlags = - PSH_MODELESS | - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE | - PSH_USECALLBACK | - PSH_USEHICON; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.hIcon = ProcessItem->SmallIcon; - propSheetHeader.pszCaption = propContext->Title->Buffer; - propSheetHeader.pfnCallback = PhpPropSheetProc; - - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = propContext->PropSheetPages; - - if (PhCsForceNoParent) - propSheetHeader.hwndParent = NULL; - - memcpy(&propContext->PropSheetHeader, &propSheetHeader, sizeof(PROPSHEETHEADER)); - - PhSetReference(&propContext->ProcessItem, ProcessItem); - PhInitializeEvent(&propContext->CreatedEvent); - - return propContext; -} - -VOID NTAPI PhpProcessPropContextDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_PROCESS_PROPCONTEXT propContext = (PPH_PROCESS_PROPCONTEXT)Object; - - PhFree(propContext->PropSheetPages); - PhDereferenceObject(propContext->Title); - PhDereferenceObject(propContext->ProcessItem); -} - -VOID PhRefreshProcessPropContext( - _Inout_ PPH_PROCESS_PROPCONTEXT PropContext - ) -{ - PropContext->PropSheetHeader.hIcon = PropContext->ProcessItem->SmallIcon; -} - -VOID PhSetSelectThreadIdProcessPropContext( - _Inout_ PPH_PROCESS_PROPCONTEXT PropContext, - _In_ HANDLE ThreadId - ) -{ - PropContext->SelectThreadId = ThreadId; -} - -INT CALLBACK PhpPropSheetProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam - ) -{ -#define PROPSHEET_ADD_STYLE (WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME); - - switch (uMsg) - { - case PSCB_PRECREATE: - { - if (lParam) - { - if (((DLGTEMPLATEEX *)lParam)->signature == 0xffff) - { - ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; - } - else - { - ((DLGTEMPLATE *)lParam)->style |= PROPSHEET_ADD_STYLE; - } - } - } - break; - case PSCB_INITIALIZED: - { - PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; - - propSheetContext = PhAllocate(sizeof(PH_PROCESS_PROPSHEETCONTEXT)); - memset(propSheetContext, 0, sizeof(PH_PROCESS_PROPSHEETCONTEXT)); - - PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)propSheetContext); - SetWindowSubclass(hwndDlg, PhpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); - - if (MinimumSize.left == -1) - { - RECT rect; - - rect.left = 0; - rect.top = 0; - rect.right = 290; - rect.bottom = 320; - MapDialogRect(hwndDlg, &rect); - MinimumSize = rect; - MinimumSize.left = 0; - } - } - break; - } - - return 0; -} - -PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( - _In_ HWND hwnd - ) -{ - return (PPH_PROCESS_PROPSHEETCONTEXT)GetProp(hwnd, PhMakeContextAtom()); -} - -LRESULT CALLBACK PhpPropSheetWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_PROCESS_PROPSHEETCONTEXT propSheetContext = (PPH_PROCESS_PROPSHEETCONTEXT)dwRefData; - - switch (uMsg) - { - case WM_DESTROY: - { - HWND tabControl; - TCITEM tabItem; - WCHAR text[128]; - - // Save the window position and size. - - PhSaveWindowPlacementToSetting(L"ProcPropPosition", L"ProcPropSize", hwnd); - - // Save the selected tab. - - tabControl = PropSheet_GetTabControl(hwnd); - - tabItem.mask = TCIF_TEXT; - tabItem.pszText = text; - tabItem.cchTextMax = sizeof(text) / 2 - 1; - - if (TabCtrl_GetItem(tabControl, TabCtrl_GetCurSel(tabControl), &tabItem)) - { - PhSetStringSetting(L"ProcPropPage", text); - } - } - break; - case WM_NCDESTROY: - { - RemoveWindowSubclass(hwnd, PhpPropSheetWndProc, uIdSubclass); - RemoveProp(hwnd, PhMakeContextAtom()); - - PhDeleteLayoutManager(&propSheetContext->LayoutManager); - PhFree(propSheetContext); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDOK: - // Prevent the OK button from working (even though - // it's already hidden). This prevents the Enter - // key from closing the dialog box. - return 0; - } - } - break; - case WM_SIZE: - { - if (!IsIconic(hwnd)) - { - PhLayoutManagerLayout(&propSheetContext->LayoutManager); - } - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -BOOLEAN PhpInitializePropSheetLayoutStage1( - _In_ PPH_PROCESS_PROPSHEETCONTEXT Context, - _In_ HWND hwnd - ) -{ - if (!Context->LayoutInitialized) - { - HWND tabControlHandle; - PPH_LAYOUT_ITEM tabControlItem; - PPH_LAYOUT_ITEM tabPageItem; - - tabControlHandle = PropSheet_GetTabControl(hwnd); - tabControlItem = PhAddLayoutItem(&Context->LayoutManager, tabControlHandle, - NULL, PH_ANCHOR_ALL | PH_LAYOUT_IMMEDIATE_RESIZE); - tabPageItem = PhAddLayoutItem(&Context->LayoutManager, tabControlHandle, - NULL, PH_LAYOUT_TAB_CONTROL); // dummy item to fix multiline tab control - - Context->TabPageItem = tabPageItem; - - PhAddLayoutItem(&Context->LayoutManager, GetDlgItem(hwnd, IDCANCEL), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - // Hide the OK button. - ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE); - // Set the Cancel button's text to "Close". - SetDlgItemText(hwnd, IDCANCEL, L"Close"); - - Context->LayoutInitialized = TRUE; - - return TRUE; - } - - return FALSE; -} - -VOID PhpInitializePropSheetLayoutStage2( - _In_ HWND hwnd - ) -{ - PH_RECTANGLE windowRectangle; - - windowRectangle.Position = PhGetIntegerPairSetting(L"ProcPropPosition"); - windowRectangle.Size = PhGetScalableIntegerPairSetting(L"ProcPropSize", TRUE).Pair; - - if (windowRectangle.Size.X < MinimumSize.right) - windowRectangle.Size.X = MinimumSize.right; - if (windowRectangle.Size.Y < MinimumSize.bottom) - windowRectangle.Size.Y = MinimumSize.bottom; - - PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); - - MoveWindow(hwnd, windowRectangle.Left, windowRectangle.Top, - windowRectangle.Width, windowRectangle.Height, FALSE); - - // Implement cascading by saving an offsetted rectangle. - windowRectangle.Left += 20; - windowRectangle.Top += 20; - - PhSetIntegerPairSetting(L"ProcPropPosition", windowRectangle.Position); -} - -BOOLEAN PhAddProcessPropPage( - _Inout_ PPH_PROCESS_PROPCONTEXT PropContext, - _In_ _Assume_refs_(1) PPH_PROCESS_PROPPAGECONTEXT PropPageContext - ) -{ - HPROPSHEETPAGE propSheetPageHandle; - - if (PropContext->PropSheetHeader.nPages == PH_PROCESS_PROPCONTEXT_MAXPAGES) - return FALSE; - - propSheetPageHandle = CreatePropertySheetPage( - &PropPageContext->PropSheetPage - ); - // CreatePropertySheetPage would have sent PSPCB_ADDREF, - // which would have added a reference. - PhDereferenceObject(PropPageContext); - - PropPageContext->PropContext = PropContext; - PhReferenceObject(PropContext); - - PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = - propSheetPageHandle; - PropContext->PropSheetHeader.nPages++; - - return TRUE; -} - -BOOLEAN PhAddProcessPropPage2( - _Inout_ PPH_PROCESS_PROPCONTEXT PropContext, - _In_ HPROPSHEETPAGE PropSheetPageHandle - ) -{ - if (PropContext->PropSheetHeader.nPages == PH_PROCESS_PROPCONTEXT_MAXPAGES) - return FALSE; - - PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = - PropSheetPageHandle; - PropContext->PropSheetHeader.nPages++; - - return TRUE; -} - -PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContext( - _In_ LPCWSTR Template, - _In_ DLGPROC DlgProc, - _In_opt_ PVOID Context - ) -{ - return PhCreateProcessPropPageContextEx(NULL, Template, DlgProc, Context); -} - -PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContextEx( - _In_opt_ PVOID InstanceHandle, - _In_ LPCWSTR Template, - _In_ DLGPROC DlgProc, - _In_opt_ PVOID Context - ) -{ - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - - propPageContext = PhCreateObject(sizeof(PH_PROCESS_PROPPAGECONTEXT), PhpProcessPropPageContextType); - memset(propPageContext, 0, sizeof(PH_PROCESS_PROPPAGECONTEXT)); - - propPageContext->PropSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propPageContext->PropSheetPage.dwFlags = - PSP_USECALLBACK; - propPageContext->PropSheetPage.hInstance = InstanceHandle; - propPageContext->PropSheetPage.pszTemplate = Template; - propPageContext->PropSheetPage.pfnDlgProc = DlgProc; - propPageContext->PropSheetPage.lParam = (LPARAM)propPageContext; - propPageContext->PropSheetPage.pfnCallback = PhpStandardPropPageProc; - - propPageContext->Context = Context; - - return propPageContext; -} - -VOID NTAPI PhpProcessPropPageContextDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_PROCESS_PROPPAGECONTEXT propPageContext = (PPH_PROCESS_PROPPAGECONTEXT)Object; - - if (propPageContext->PropContext) - PhDereferenceObject(propPageContext->PropContext); -} - -INT CALLBACK PhpStandardPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ) -{ - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - - propPageContext = (PPH_PROCESS_PROPPAGECONTEXT)ppsp->lParam; - - if (uMsg == PSPCB_ADDREF) - PhReferenceObject(propPageContext); - else if (uMsg == PSPCB_RELEASE) - PhDereferenceObject(propPageContext); - - return 1; -} - -BOOLEAN PhPropPageDlgProcHeader( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam, - _Out_ LPPROPSHEETPAGE *PropSheetPage, - _Out_ PPH_PROCESS_PROPPAGECONTEXT *PropPageContext, - _Out_ PPH_PROCESS_ITEM *ProcessItem - ) -{ - return PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, PropSheetPage, PropPageContext, ProcessItem); -} - -VOID PhPropPageDlgProcDestroy( - _In_ HWND hwndDlg - ) -{ - PhpPropPageDlgProcDestroy(hwndDlg); -} - -PPH_LAYOUT_ITEM PhAddPropPageLayoutItem( - _In_ HWND hwnd, - _In_ HWND Handle, - _In_ PPH_LAYOUT_ITEM ParentItem, - _In_ ULONG Anchor - ) -{ - HWND parent; - PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; - PPH_LAYOUT_MANAGER layoutManager; - PPH_LAYOUT_ITEM realParentItem; - BOOLEAN doLayoutStage2; - PPH_LAYOUT_ITEM item; - - parent = GetParent(hwnd); - propSheetContext = PhpGetPropSheetContext(parent); - layoutManager = &propSheetContext->LayoutManager; - - doLayoutStage2 = PhpInitializePropSheetLayoutStage1(propSheetContext, parent); - - if (ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT) - realParentItem = ParentItem; - else - realParentItem = propSheetContext->TabPageItem; - - // Use the HACK if the control is a direct child of the dialog. - if (ParentItem && ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT && - // We detect if ParentItem is the layout item for the dialog - // by looking at its parent. - (ParentItem->ParentItem == &layoutManager->RootItem || - (ParentItem->ParentItem->Anchor & PH_LAYOUT_TAB_CONTROL))) - { - RECT dialogRect; - RECT dialogSize; - RECT margin; - - // MAKE SURE THESE NUMBERS ARE CORRECT. - dialogSize.right = 260; - dialogSize.bottom = 260; - MapDialogRect(hwnd, &dialogSize); - - // Get the original dialog rectangle. - GetWindowRect(hwnd, &dialogRect); - dialogRect.right = dialogRect.left + dialogSize.right; - dialogRect.bottom = dialogRect.top + dialogSize.bottom; - - // Calculate the margin from the original rectangle. - GetWindowRect(Handle, &margin); - margin = PhMapRect(margin, dialogRect); - PhConvertRect(&margin, &dialogRect); - - item = PhAddLayoutItemEx(layoutManager, Handle, realParentItem, Anchor, margin); - } - else - { - item = PhAddLayoutItem(layoutManager, Handle, realParentItem, Anchor); - } - - if (doLayoutStage2) - PhpInitializePropSheetLayoutStage2(parent); - - return item; -} - -VOID PhDoPropPageLayout( - _In_ HWND hwnd - ) -{ - HWND parent; - PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; - - parent = GetParent(hwnd); - propSheetContext = PhpGetPropSheetContext(parent); - PhLayoutManagerLayout(&propSheetContext->LayoutManager); -} - -NTSTATUS PhpProcessPropertiesThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - PPH_PROCESS_PROPCONTEXT PropContext = (PPH_PROCESS_PROPCONTEXT)Parameter; - PPH_PROCESS_PROPPAGECONTEXT newPage; - PPH_STRING startPage; - HWND hwnd; - BOOL result; - MSG message; - - PhInitializeAutoPool(&autoPool); - - // Wait for stage 1 to be processed. - PhWaitForEvent(&PropContext->ProcessItem->Stage1Event, NULL); - // Refresh the icon which may have been updated due to - // stage 1. - PhRefreshProcessPropContext(PropContext); - - // Add the pages... - - // General - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCGENERAL), - PhpProcessGeneralDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Statistics - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCSTATISTICS), - PhpProcessStatisticsDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Performance - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCPERFORMANCE), - PhpProcessPerformanceDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Threads - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCTHREADS), - PhpProcessThreadsDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Token - PhAddProcessPropPage2( - PropContext, - PhCreateTokenPage(PhpOpenProcessTokenForPage, (PVOID)PropContext->ProcessItem->ProcessId, PhpProcessTokenHookProc) - ); - - // Modules - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCMODULES), - PhpProcessModulesDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Memory - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCMEMORY), - PhpProcessMemoryDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Environment - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCENVIRONMENT), - PhpProcessEnvironmentDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Handles - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCHANDLES), - PhpProcessHandlesDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - - // Job - if ( - PropContext->ProcessItem->IsInJob && - // There's no way the job page can function without KPH since it needs - // to open a handle to the job. - KphIsConnected() - ) - { - PhAddProcessPropPage2( - PropContext, - PhCreateJobPage(PhpOpenProcessJobForPage, (PVOID)PropContext->ProcessItem->ProcessId, PhpProcessJobHookProc) - ); - } - - // Services - if (PropContext->ProcessItem->ServiceList && PropContext->ProcessItem->ServiceList->Count != 0) - { - newPage = PhCreateProcessPropPageContext( - MAKEINTRESOURCE(IDD_PROCSERVICES), - PhpProcessServicesDlgProc, - NULL - ); - PhAddProcessPropPage(PropContext, newPage); - } - - // Plugin-supplied pages - if (PhPluginsEnabled) - { - PH_PLUGIN_PROCESS_PROPCONTEXT pluginProcessPropContext; - - pluginProcessPropContext.PropContext = PropContext; - pluginProcessPropContext.ProcessItem = PropContext->ProcessItem; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), &pluginProcessPropContext); - } - - // Create the property sheet - - if (PropContext->SelectThreadId) - PhSetStringSetting(L"ProcPropPage", L"Threads"); - - startPage = PhGetStringSetting(L"ProcPropPage"); - PropContext->PropSheetHeader.dwFlags |= PSH_USEPSTARTPAGE; - PropContext->PropSheetHeader.pStartPage = startPage->Buffer; - - hwnd = (HWND)PropertySheet(&PropContext->PropSheetHeader); - - PhDereferenceObject(startPage); - - PropContext->WindowHandle = hwnd; - PhSetEvent(&PropContext->CreatedEvent); - - // Main event loop - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (!PropSheet_IsDialogMessage(hwnd, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&autoPool); - - if (!PropSheet_GetCurrentPageHwnd(hwnd)) - break; - } - - DestroyWindow(hwnd); - PhDereferenceObject(PropContext); - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -BOOLEAN PhShowProcessProperties( - _In_ PPH_PROCESS_PROPCONTEXT Context - ) -{ - HANDLE threadHandle; - - PhReferenceObject(Context); - threadHandle = PhCreateThread(0, PhpProcessPropertiesThreadStart, Context); - - if (threadHandle) - { - NtClose(threadHandle); - return TRUE; - } - else - { - PhDereferenceObject(Context); - return FALSE; - } -} +/* + * Process Hacker - + * Process properties + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +PPH_OBJECT_TYPE PhpProcessPropContextType = NULL; +PPH_OBJECT_TYPE PhpProcessPropPageContextType = NULL; +PH_STRINGREF PhpLoadingText = PH_STRINGREF_INIT(L"Loading..."); +static RECT MinimumSize = { -1, -1, -1, -1 }; + +PPH_PROCESS_PROPCONTEXT PhCreateProcessPropContext( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_PROCESS_PROPCONTEXT propContext; + PROPSHEETHEADER propSheetHeader; + + if (PhBeginInitOnce(&initOnce)) + { + PhpProcessPropContextType = PhCreateObjectType(L"ProcessPropContext", 0, PhpProcessPropContextDeleteProcedure); + PhpProcessPropPageContextType = PhCreateObjectType(L"ProcessPropPageContext", 0, PhpProcessPropPageContextDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + propContext = PhCreateObjectZero(sizeof(PH_PROCESS_PROPCONTEXT), PhpProcessPropContextType); + propContext->PropSheetPages = PhAllocateZero(sizeof(HPROPSHEETPAGE) * PH_PROCESS_PROPCONTEXT_MAXPAGES); + + if (!PH_IS_FAKE_PROCESS_ID(ProcessItem->ProcessId)) + { + propContext->Title = PhFormatString( + L"%s (%u)", + ProcessItem->ProcessName->Buffer, + HandleToUlong(ProcessItem->ProcessId) + ); + } + else + { + PhSetReference(&propContext->Title, ProcessItem->ProcessName); + } + + memset(&propSheetHeader, 0, sizeof(PROPSHEETHEADER)); + propSheetHeader.dwSize = sizeof(PROPSHEETHEADER); + propSheetHeader.dwFlags = + PSH_MODELESS | + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE | + PSH_USECALLBACK | + PSH_USEHICON; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.hwndParent = ParentWindowHandle; + propSheetHeader.hIcon = ProcessItem->SmallIcon; + propSheetHeader.pszCaption = propContext->Title->Buffer; + propSheetHeader.pfnCallback = PhpPropSheetProc; + + propSheetHeader.nPages = 0; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = propContext->PropSheetPages; + + if (PhCsForceNoParent) + propSheetHeader.hwndParent = NULL; + + memcpy(&propContext->PropSheetHeader, &propSheetHeader, sizeof(PROPSHEETHEADER)); + + PhSetReference(&propContext->ProcessItem, ProcessItem); + + return propContext; +} + +VOID NTAPI PhpProcessPropContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_PROCESS_PROPCONTEXT propContext = (PPH_PROCESS_PROPCONTEXT)Object; + + PhFree(propContext->PropSheetPages); + PhDereferenceObject(propContext->Title); + PhDereferenceObject(propContext->ProcessItem); +} + +VOID PhRefreshProcessPropContext( + _Inout_ PPH_PROCESS_PROPCONTEXT PropContext + ) +{ + if (PropContext->ProcessItem->SmallIcon) + { + PropContext->PropSheetHeader.hIcon = PropContext->ProcessItem->SmallIcon; + } + else + { + HICON iconSmall; + + PhGetStockApplicationIcon(&iconSmall, NULL); + + PropContext->PropSheetHeader.hIcon = iconSmall; + } +} + +VOID PhSetSelectThreadIdProcessPropContext( + _Inout_ PPH_PROCESS_PROPCONTEXT PropContext, + _In_ HANDLE ThreadId + ) +{ + PropContext->SelectThreadId = ThreadId; +} + +INT CALLBACK PhpPropSheetProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam + ) +{ +#define PROPSHEET_ADD_STYLE (WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME); + + switch (uMsg) + { + case PSCB_PRECREATE: + { + if (lParam) + { + if (((DLGTEMPLATEEX *)lParam)->signature == USHRT_MAX) + { + ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; + } + else + { + ((DLGTEMPLATE *)lParam)->style |= PROPSHEET_ADD_STYLE; + } + } + } + break; + case PSCB_INITIALIZED: + { + PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; + + propSheetContext = PhAllocate(sizeof(PH_PROCESS_PROPSHEETCONTEXT)); + memset(propSheetContext, 0, sizeof(PH_PROCESS_PROPSHEETCONTEXT)); + + PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, propSheetContext); + + propSheetContext->PropSheetWindowHookProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, 0xF, propSheetContext); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PhpPropSheetWndProc); + + if (PhEnableThemeSupport) // NOTE: Required for compatibility. (dmex) + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + + PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 290; + rect.bottom = 320; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + } + break; + } + + return 0; +} + +PPH_PROCESS_PROPSHEETCONTEXT PhpGetPropSheetContext( + _In_ HWND hwnd + ) +{ + return PhGetWindowContext(hwnd, PH_WINDOW_CONTEXT_DEFAULT); +} + +LRESULT CALLBACK PhpPropSheetWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; + WNDPROC oldWndProc; + + propSheetContext = PhGetWindowContext(hwnd, 0xF); + + if (!propSheetContext) + return 0; + + oldWndProc = propSheetContext->PropSheetWindowHookProc; + + switch (uMsg) + { + case WM_DESTROY: + { + HWND tabControl; + TCITEM tabItem; + WCHAR text[128]; + + // Save the window position and size. + + PhSaveWindowPlacementToSetting(L"ProcPropPosition", L"ProcPropSize", hwnd); + + // Save the selected tab. + + tabControl = PropSheet_GetTabControl(hwnd); + + tabItem.mask = TCIF_TEXT; + tabItem.pszText = text; + tabItem.cchTextMax = RTL_NUMBER_OF(text) - 1; + + if (TabCtrl_GetItem(tabControl, TabCtrl_GetCurSel(tabControl), &tabItem)) + { + PhSetStringSetting(L"ProcPropPage", text); + } + } + break; + case WM_NCDESTROY: + { + PhUnregisterWindowCallback(hwnd); + + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + + PhDeleteLayoutManager(&propSheetContext->LayoutManager); + PhFree(propSheetContext); + } + break; + case WM_SYSCOMMAND: + { + // Note: Clicking the X on the taskbar window thumbnail preview doens't close modeless property sheets + // when there are more than 1 window and the window doesn't have focus... The MFC, ATL and WTL libraries + // check if the propsheet is modeless and SendMessage WM_CLOSE and so we'll implement the same solution. (dmex) + switch (wParam & 0xFFF0) + { + case SC_CLOSE: + { + PostMessage(hwnd, WM_CLOSE, 0, 0); + //SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE); + //return TRUE; + } + break; + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDOK: + // Prevent the OK button from working (even though + // it's already hidden). This prevents the Enter + // key from closing the dialog box. + return 0; + } + } + break; + case WM_SIZE: + { + if (!IsMinimized(hwnd)) + { + PhLayoutManagerLayout(&propSheetContext->LayoutManager); + } + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + } + + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); +} + +BOOLEAN PhpInitializePropSheetLayoutStage1( + _In_ PPH_PROCESS_PROPSHEETCONTEXT Context, + _In_ HWND hwnd + ) +{ + if (!Context->LayoutInitialized) + { + HWND tabControlHandle; + PPH_LAYOUT_ITEM tabControlItem; + PPH_LAYOUT_ITEM tabPageItem; + + tabControlHandle = PropSheet_GetTabControl(hwnd); + tabControlItem = PhAddLayoutItem(&Context->LayoutManager, tabControlHandle, + NULL, PH_ANCHOR_ALL | PH_LAYOUT_IMMEDIATE_RESIZE); + tabPageItem = PhAddLayoutItem(&Context->LayoutManager, tabControlHandle, + NULL, PH_LAYOUT_TAB_CONTROL); // dummy item to fix multiline tab control + + Context->TabPageItem = tabPageItem; + + PhAddLayoutItem(&Context->LayoutManager, GetDlgItem(hwnd, IDCANCEL), + NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + // Hide the OK button. + ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE); + // Set the Cancel button's text to "Close". + PhSetDialogItemText(hwnd, IDCANCEL, L"Close"); + + Context->LayoutInitialized = TRUE; + + return TRUE; + } + + return FALSE; +} + +VOID PhpInitializePropSheetLayoutStage2( + _In_ HWND hwnd + ) +{ + PH_RECTANGLE windowRectangle; + + windowRectangle.Position = PhGetIntegerPairSetting(L"ProcPropPosition"); + windowRectangle.Size = PhGetScalableIntegerPairSetting(L"ProcPropSize", TRUE).Pair; + + if (windowRectangle.Size.X < MinimumSize.right) + windowRectangle.Size.X = MinimumSize.right; + if (windowRectangle.Size.Y < MinimumSize.bottom) + windowRectangle.Size.Y = MinimumSize.bottom; + + PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); + + MoveWindow(hwnd, windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, FALSE); + + // Implement cascading by saving an offsetted rectangle. + windowRectangle.Left += 20; + windowRectangle.Top += 20; + + PhSetIntegerPairSetting(L"ProcPropPosition", windowRectangle.Position); +} + +BOOLEAN PhAddProcessPropPage( + _Inout_ PPH_PROCESS_PROPCONTEXT PropContext, + _In_ _Assume_refs_(1) PPH_PROCESS_PROPPAGECONTEXT PropPageContext + ) +{ + HPROPSHEETPAGE propSheetPageHandle; + + if (PropContext->PropSheetHeader.nPages == PH_PROCESS_PROPCONTEXT_MAXPAGES) + return FALSE; + + propSheetPageHandle = CreatePropertySheetPage( + &PropPageContext->PropSheetPage + ); + // CreatePropertySheetPage would have sent PSPCB_ADDREF, + // which would have added a reference. + PhDereferenceObject(PropPageContext); + + PhSetReference(&PropPageContext->PropContext, PropContext); + + PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = propSheetPageHandle; + PropContext->PropSheetHeader.nPages++; + + return TRUE; +} + +BOOLEAN PhAddProcessPropPage2( + _Inout_ PPH_PROCESS_PROPCONTEXT PropContext, + _In_ HPROPSHEETPAGE PropSheetPageHandle + ) +{ + if (PropContext->PropSheetHeader.nPages == PH_PROCESS_PROPCONTEXT_MAXPAGES) + return FALSE; + + PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = PropSheetPageHandle; + PropContext->PropSheetHeader.nPages++; + + return TRUE; +} + +PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContext( + _In_ LPCWSTR Template, + _In_ DLGPROC DlgProc, + _In_opt_ PVOID Context + ) +{ + return PhCreateProcessPropPageContextEx(PhInstanceHandle, Template, DlgProc, Context); +} + +PPH_PROCESS_PROPPAGECONTEXT PhCreateProcessPropPageContextEx( + _In_opt_ PVOID InstanceHandle, + _In_ LPCWSTR Template, + _In_ DLGPROC DlgProc, + _In_opt_ PVOID Context + ) +{ + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + + propPageContext = PhCreateObjectZero(sizeof(PH_PROCESS_PROPPAGECONTEXT), PhpProcessPropPageContextType); + propPageContext->PropSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propPageContext->PropSheetPage.dwFlags = PSP_USECALLBACK; + propPageContext->PropSheetPage.hInstance = InstanceHandle; + propPageContext->PropSheetPage.pszTemplate = Template; + propPageContext->PropSheetPage.pfnDlgProc = DlgProc; + propPageContext->PropSheetPage.lParam = (LPARAM)propPageContext; + propPageContext->PropSheetPage.pfnCallback = PhpStandardPropPageProc; + + propPageContext->Context = Context; + + return propPageContext; +} + +VOID NTAPI PhpProcessPropPageContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_PROCESS_PROPPAGECONTEXT propPageContext = (PPH_PROCESS_PROPPAGECONTEXT)Object; + + if (propPageContext->PropContext) + PhDereferenceObject(propPageContext->PropContext); +} + +INT CALLBACK PhpStandardPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) +{ + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + + propPageContext = (PPH_PROCESS_PROPPAGECONTEXT)ppsp->lParam; + + if (uMsg == PSPCB_ADDREF) + PhReferenceObject(propPageContext); + else if (uMsg == PSPCB_RELEASE) + PhDereferenceObject(propPageContext); + + return 1; +} + +BOOLEAN PhPropPageDlgProcHeader( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam, + _Out_opt_ LPPROPSHEETPAGE *PropSheetPage, + _Out_opt_ PPH_PROCESS_PROPPAGECONTEXT *PropPageContext, + _Out_opt_ PPH_PROCESS_ITEM *ProcessItem + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + + if (uMsg == WM_INITDIALOG) + { + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, (PVOID)lParam); + } + + propSheetPage = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (!propSheetPage) + return FALSE; + + propPageContext = (PPH_PROCESS_PROPPAGECONTEXT)propSheetPage->lParam; + + if (PropSheetPage) + *PropSheetPage = propSheetPage; + if (PropPageContext) + *PropPageContext = propPageContext; + if (ProcessItem) + *ProcessItem = propPageContext->PropContext->ProcessItem; + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + return TRUE; +} + +PPH_LAYOUT_ITEM PhAddPropPageLayoutItem( + _In_ HWND hwnd, + _In_ HWND Handle, + _In_ PPH_LAYOUT_ITEM ParentItem, + _In_ ULONG Anchor + ) +{ + HWND parent; + PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; + PPH_LAYOUT_MANAGER layoutManager; + PPH_LAYOUT_ITEM realParentItem; + BOOLEAN doLayoutStage2; + PPH_LAYOUT_ITEM item; + + parent = GetParent(hwnd); + propSheetContext = PhpGetPropSheetContext(parent); + layoutManager = &propSheetContext->LayoutManager; + + doLayoutStage2 = PhpInitializePropSheetLayoutStage1(propSheetContext, parent); + + if (ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT) + realParentItem = ParentItem; + else + realParentItem = propSheetContext->TabPageItem; + + // Use the HACK if the control is a direct child of the dialog. + if (ParentItem && ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT && + // We detect if ParentItem is the layout item for the dialog + // by looking at its parent. + (ParentItem->ParentItem == &layoutManager->RootItem || + (ParentItem->ParentItem->Anchor & PH_LAYOUT_TAB_CONTROL))) + { + RECT dialogRect; + RECT dialogSize; + RECT margin; + + // MAKE SURE THESE NUMBERS ARE CORRECT. + dialogSize.right = 260; + dialogSize.bottom = 260; + MapDialogRect(hwnd, &dialogSize); + + // Get the original dialog rectangle. + GetWindowRect(hwnd, &dialogRect); + dialogRect.right = dialogRect.left + dialogSize.right; + dialogRect.bottom = dialogRect.top + dialogSize.bottom; + + // Calculate the margin from the original rectangle. + GetWindowRect(Handle, &margin); + margin = PhMapRect(margin, dialogRect); + PhConvertRect(&margin, &dialogRect); + + item = PhAddLayoutItemEx(layoutManager, Handle, realParentItem, Anchor, margin); + } + else + { + item = PhAddLayoutItem(layoutManager, Handle, realParentItem, Anchor); + } + + if (doLayoutStage2) + PhpInitializePropSheetLayoutStage2(parent); + + return item; +} + +VOID PhDoPropPageLayout( + _In_ HWND hwnd + ) +{ + HWND parent; + PPH_PROCESS_PROPSHEETCONTEXT propSheetContext; + + parent = GetParent(hwnd); + propSheetContext = PhpGetPropSheetContext(parent); + PhLayoutManagerLayout(&propSheetContext->LayoutManager); +} + +NTSTATUS PhpProcessPropertiesThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + PPH_PROCESS_PROPCONTEXT PropContext = (PPH_PROCESS_PROPCONTEXT)Parameter; + PPH_PROCESS_PROPPAGECONTEXT newPage; + PPH_STRING startPage; + + PhInitializeAutoPool(&autoPool); + + // Wait for stage 1 to be processed. + PhWaitForEvent(&PropContext->ProcessItem->Stage1Event, NULL); + // Refresh the icon which may have been updated due to + // stage 1. + PhRefreshProcessPropContext(PropContext); + + // Add the pages... + + // General + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCGENERAL), + PhpProcessGeneralDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Statistics + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCSTATISTICS), + PhpProcessStatisticsDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Performance + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCPERFORMANCE), + PhpProcessPerformanceDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Threads + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCTHREADS), + PhpProcessThreadsDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Token + PhAddProcessPropPage2( + PropContext, + PhCreateTokenPage(PhpOpenProcessTokenForPage, PropContext->ProcessItem->ProcessId, (PVOID)PropContext->ProcessItem->ProcessId, PhpProcessTokenHookProc) + ); + + // Modules + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCMODULES), + PhpProcessModulesDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Memory + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCMEMORY), + PhpProcessMemoryDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Environment + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCENVIRONMENT), + PhpProcessEnvironmentDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Handles + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCHANDLES), + PhpProcessHandlesDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + + // Job + if ( + PropContext->ProcessItem->IsInJob && + // There's no way the job page can function without KPH since it needs + // to open a handle to the job. + KphIsConnected() + ) + { + PhAddProcessPropPage2( + PropContext, + PhCreateJobPage(PhpOpenProcessJobForPage, (PVOID)PropContext->ProcessItem->ProcessId, PhpProcessJobHookProc) + ); + } + + // Services + if (PropContext->ProcessItem->ServiceList && PropContext->ProcessItem->ServiceList->Count != 0) + { + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCSERVICES), + PhpProcessServicesDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + } + + // WMI Provider Host + if ((PropContext->ProcessItem->KnownProcessType & KnownProcessTypeMask) == WmiProviderHostType) + { + newPage = PhCreateProcessPropPageContext( + MAKEINTRESOURCE(IDD_PROCWMIPROVIDERS), + PhpProcessWmiProvidersDlgProc, + NULL + ); + PhAddProcessPropPage(PropContext, newPage); + } + + // Plugin-supplied pages + if (PhPluginsEnabled) + { + PH_PLUGIN_PROCESS_PROPCONTEXT pluginProcessPropContext; + + pluginProcessPropContext.PropContext = PropContext; + pluginProcessPropContext.ProcessItem = PropContext->ProcessItem; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), &pluginProcessPropContext); + } + + // Create the property sheet + + if (PropContext->SelectThreadId) + PhSetStringSetting(L"ProcPropPage", L"Threads"); + + startPage = PhGetStringSetting(L"ProcPropPage"); + PropContext->PropSheetHeader.dwFlags |= PSH_USEPSTARTPAGE; + PropContext->PropSheetHeader.pStartPage = startPage->Buffer; + + PhModalPropertySheet(&PropContext->PropSheetHeader); + + PhDereferenceObject(startPage); + PhDereferenceObject(PropContext); + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + +VOID PhShowProcessProperties( + _In_ PPH_PROCESS_PROPCONTEXT Context + ) +{ + PhReferenceObject(Context); + PhCreateThread2(PhpProcessPropertiesThreadStart, Context); +} diff --git a/ProcessHacker/procprv.c b/ProcessHacker/procprv.c index 5640344ef0d5..25cab7dff5b2 100644 --- a/ProcessHacker/procprv.c +++ b/ProcessHacker/procprv.c @@ -1,2979 +1,2967 @@ -/* - * Process Hacker - - * process provider - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This provider module handles the collection of process information and system-wide statistics. A - * list of all running processes is kept and periodically scanned to detect new and terminated - * processes. - * - * The retrieval of certain information is delayed in order to improve performance. This includes - * things such as file icons, version information, digital signature verification, and packed - * executable detection. These requests are handed to worker threads which then post back - * information to a S-list. - * - * Also contained in this module is the storage of process records, which contain static information - * about processes. Unlike process items which are removed as soon as their corresponding process - * exits, process records remain as long as they are needed by the statistics system, and more - * specifically the max. CPU and I/O history buffers. In PH 1.x, a new formatted string was created - * at each update containing information about the maximum-usage process within that interval. Here - * we use a much more storage-efficient method, where the raw maximum-usage PIDs are stored for each - * interval, and the process record list is searched when the name of a process is needed. - * - * The process record list is stored as a list of records sorted by process creation time. If two or - * more processes have the same creation time, they are added to a doubly-linked list. This - * structure allows for fast searching in the typical scenario where we know the PID of a process - * and a specific time in which the process was running. In this case a binary search is used and - * then the list is traversed backwards until the process is found. Binary search is similarly used - * for insertion and removal. - * - * On Windows 7 and above, CPU usage can be calculated from cycle time. However, cycle time cannot - * be split into kernel/user components, and cycle time is not available for DPCs and Interrupts - * separately (only a "system" cycle time). - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define PROCESS_ID_BUCKETS 64 -#define PROCESS_ID_TO_BUCKET_INDEX(ProcessId) ((HandleToUlong(ProcessId) / 4) & (PROCESS_ID_BUCKETS - 1)) - -typedef struct _PH_PROCESS_QUERY_DATA -{ - SLIST_ENTRY ListEntry; - ULONG Stage; - PPH_PROCESS_ITEM ProcessItem; -} PH_PROCESS_QUERY_DATA, *PPH_PROCESS_QUERY_DATA; - -typedef struct _PH_PROCESS_QUERY_S1_DATA -{ - PH_PROCESS_QUERY_DATA Header; - - PPH_STRING CommandLine; - - HICON SmallIcon; - HICON LargeIcon; - PH_IMAGE_VERSION_INFO VersionInfo; - - TOKEN_ELEVATION_TYPE ElevationType; - MANDATORY_LEVEL IntegrityLevel; - PWSTR IntegrityString; - - PPH_STRING JobName; - HANDLE ConsoleHostProcessId; - PPH_STRING PackageFullName; - - union - { - ULONG Flags; - struct - { - ULONG IsDotNet : 1; - ULONG IsElevated : 1; - ULONG IsInJob : 1; - ULONG IsInSignificantJob : 1; - ULONG IsWow64 : 1; - ULONG IsWow64Valid : 1; - ULONG IsProtectedProcess : 1; - ULONG IsSecureProcess : 1; - ULONG IsSubsystemProcess : 1; - - ULONG Spare : 23; - }; - }; -} PH_PROCESS_QUERY_S1_DATA, *PPH_PROCESS_QUERY_S1_DATA; - -typedef struct _PH_PROCESS_QUERY_S2_DATA -{ - PH_PROCESS_QUERY_DATA Header; - - VERIFY_RESULT VerifyResult; - PPH_STRING VerifySignerName; - - BOOLEAN IsPacked; - ULONG ImportFunctions; - ULONG ImportModules; -} PH_PROCESS_QUERY_S2_DATA, *PPH_PROCESS_QUERY_S2_DATA; - -typedef struct _PH_VERIFY_CACHE_ENTRY -{ - PH_AVL_LINKS Links; - - PPH_STRING FileName; - VERIFY_RESULT VerifyResult; - PPH_STRING VerifySignerName; -} PH_VERIFY_CACHE_ENTRY, *PPH_VERIFY_CACHE_ENTRY; - -typedef struct _PH_SID_FULL_NAME_CACHE_ENTRY -{ - PSID Sid; - PPH_STRING FullName; -} PH_SID_FULL_NAME_CACHE_ENTRY, *PPH_SID_FULL_NAME_CACHE_ENTRY; - -VOID NTAPI PhpProcessItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -INT NTAPI PhpVerifyCacheCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ); - -VOID PhpQueueProcessQueryStage1( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -VOID PhpQueueProcessQueryStage2( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -PPH_PROCESS_RECORD PhpCreateProcessRecord( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -VOID PhpAddProcessRecord( - _Inout_ PPH_PROCESS_RECORD ProcessRecord - ); - -VOID PhpRemoveProcessRecord( - _Inout_ PPH_PROCESS_RECORD ProcessRecord - ); - -PPH_OBJECT_TYPE PhProcessItemType; - -PPH_HASH_ENTRY PhProcessHashSet[256] = PH_HASH_SET_INIT; -ULONG PhProcessHashSetCount = 0; -PH_QUEUED_LOCK PhProcessHashSetLock = PH_QUEUED_LOCK_INIT; - -SLIST_HEADER PhProcessQueryDataListHead; - -PHAPPAPI PH_CALLBACK_DECLARE(PhProcessAddedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhProcessModifiedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhProcessRemovedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhProcessesUpdatedEvent); - -PPH_LIST PhProcessRecordList; -PH_QUEUED_LOCK PhProcessRecordListLock = PH_QUEUED_LOCK_INIT; - -ULONG PhStatisticsSampleCount = 512; -BOOLEAN PhEnableProcessQueryStage2 = FALSE; -BOOLEAN PhEnablePurgeProcessRecords = TRUE; -BOOLEAN PhEnableCycleCpuUsage = TRUE; - -PVOID PhProcessInformation; // only can be used if running on same thread as process provider -SYSTEM_PERFORMANCE_INFORMATION PhPerfInformation; -PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuInformation; -SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuTotals; -ULONG PhTotalProcesses; -ULONG PhTotalThreads; -ULONG PhTotalHandles; - -SYSTEM_PROCESS_INFORMATION PhDpcsProcessInformation; -SYSTEM_PROCESS_INFORMATION PhInterruptsProcessInformation; - -ULONG64 PhCpuTotalCycleDelta; // real cycle time delta for this period -PLARGE_INTEGER PhCpuIdleCycleTime; // cycle time for Idle -PLARGE_INTEGER PhCpuSystemCycleTime; // cycle time for DPCs and Interrupts -PH_UINT64_DELTA PhCpuIdleCycleDelta; -PH_UINT64_DELTA PhCpuSystemCycleDelta; -//PPH_UINT64_DELTA PhCpusIdleCycleDelta; - -FLOAT PhCpuKernelUsage; -FLOAT PhCpuUserUsage; -PFLOAT PhCpusKernelUsage; -PFLOAT PhCpusUserUsage; - -PH_UINT64_DELTA PhCpuKernelDelta; -PH_UINT64_DELTA PhCpuUserDelta; -PH_UINT64_DELTA PhCpuIdleDelta; - -PPH_UINT64_DELTA PhCpusKernelDelta; -PPH_UINT64_DELTA PhCpusUserDelta; -PPH_UINT64_DELTA PhCpusIdleDelta; - -PH_UINT64_DELTA PhIoReadDelta; -PH_UINT64_DELTA PhIoWriteDelta; -PH_UINT64_DELTA PhIoOtherDelta; - -static BOOLEAN PhProcessStatisticsInitialized = FALSE; -static ULONG PhTimeSequenceNumber = 0; -static PH_CIRCULAR_BUFFER_ULONG PhTimeHistory; - -PH_CIRCULAR_BUFFER_FLOAT PhCpuKernelHistory; -PH_CIRCULAR_BUFFER_FLOAT PhCpuUserHistory; -//PH_CIRCULAR_BUFFER_FLOAT PhCpuOtherHistory; - -PPH_CIRCULAR_BUFFER_FLOAT PhCpusKernelHistory; -PPH_CIRCULAR_BUFFER_FLOAT PhCpusUserHistory; -//PPH_CIRCULAR_BUFFER_FLOAT PhCpusOtherHistory; - -PH_CIRCULAR_BUFFER_ULONG64 PhIoReadHistory; -PH_CIRCULAR_BUFFER_ULONG64 PhIoWriteHistory; -PH_CIRCULAR_BUFFER_ULONG64 PhIoOtherHistory; - -PH_CIRCULAR_BUFFER_ULONG PhCommitHistory; -PH_CIRCULAR_BUFFER_ULONG PhPhysicalHistory; - -PH_CIRCULAR_BUFFER_ULONG PhMaxCpuHistory; // ID of max. CPU process -PH_CIRCULAR_BUFFER_ULONG PhMaxIoHistory; // ID of max. I/O process -#ifdef PH_RECORD_MAX_USAGE -PH_CIRCULAR_BUFFER_FLOAT PhMaxCpuUsageHistory; -PH_CIRCULAR_BUFFER_ULONG64 PhMaxIoReadOtherHistory; -PH_CIRCULAR_BUFFER_ULONG64 PhMaxIoWriteHistory; -#endif - -static PTS_ALL_PROCESSES_INFO PhpTsProcesses = NULL; -static ULONG PhpTsNumberOfProcesses; - -#ifdef PH_ENABLE_VERIFY_CACHE -static PH_AVL_TREE PhpVerifyCacheSet = PH_AVL_TREE_INIT(PhpVerifyCacheCompareFunction); -static PH_QUEUED_LOCK PhpVerifyCacheLock = PH_QUEUED_LOCK_INIT; -#endif - -static PPH_HASHTABLE PhpSidFullNameCacheHashtable; - -BOOLEAN PhProcessProviderInitialization( - VOID - ) -{ - PFLOAT usageBuffer; - PPH_UINT64_DELTA deltaBuffer; - PPH_CIRCULAR_BUFFER_FLOAT historyBuffer; - - PhProcessItemType = PhCreateObjectType(L"ProcessItem", 0, PhpProcessItemDeleteProcedure); - - RtlInitializeSListHead(&PhProcessQueryDataListHead); - - PhProcessRecordList = PhCreateList(40); - - RtlInitUnicodeString( - &PhDpcsProcessInformation.ImageName, - L"DPCs" - ); - PhDpcsProcessInformation.UniqueProcessId = DPCS_PROCESS_ID; - PhDpcsProcessInformation.InheritedFromUniqueProcessId = SYSTEM_IDLE_PROCESS_ID; - - RtlInitUnicodeString( - &PhInterruptsProcessInformation.ImageName, - L"Interrupts" - ); - PhInterruptsProcessInformation.UniqueProcessId = INTERRUPTS_PROCESS_ID; - PhInterruptsProcessInformation.InheritedFromUniqueProcessId = SYSTEM_IDLE_PROCESS_ID; - - PhCpuInformation = PhAllocate( - sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - - PhCpuIdleCycleTime = PhAllocate( - sizeof(LARGE_INTEGER) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - PhCpuSystemCycleTime = PhAllocate( - sizeof(LARGE_INTEGER) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - - usageBuffer = PhAllocate( - sizeof(FLOAT) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors * - 2 - ); - deltaBuffer = PhAllocate( - sizeof(PH_UINT64_DELTA) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors * - 3 // 4 for PhCpusIdleCycleDelta - ); - historyBuffer = PhAllocate( - sizeof(PH_CIRCULAR_BUFFER_FLOAT) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors * - 2 - ); - - PhCpusKernelUsage = usageBuffer; - PhCpusUserUsage = PhCpusKernelUsage + (ULONG)PhSystemBasicInformation.NumberOfProcessors; - - PhCpusKernelDelta = deltaBuffer; - PhCpusUserDelta = PhCpusKernelDelta + (ULONG)PhSystemBasicInformation.NumberOfProcessors; - PhCpusIdleDelta = PhCpusUserDelta + (ULONG)PhSystemBasicInformation.NumberOfProcessors; - //PhCpusIdleCycleDelta = PhCpusIdleDelta + (ULONG)PhSystemBasicInformation.NumberOfProcessors; - - PhCpusKernelHistory = historyBuffer; - PhCpusUserHistory = PhCpusKernelHistory + (ULONG)PhSystemBasicInformation.NumberOfProcessors; - - memset(deltaBuffer, 0, sizeof(PH_UINT64_DELTA) * (ULONG)PhSystemBasicInformation.NumberOfProcessors); - - return TRUE; -} - -PPH_STRING PhGetClientIdName( - _In_ PCLIENT_ID ClientId - ) -{ - PPH_STRING name; - PPH_PROCESS_ITEM processItem; - - processItem = PhReferenceProcessItem(ClientId->UniqueProcess); - - if (processItem) - { - name = PhGetClientIdNameEx(ClientId, processItem->ProcessName); - PhDereferenceObject(processItem); - } - else - { - name = PhGetClientIdNameEx(ClientId, NULL); - } - - return name; -} - -PPH_STRING PhGetClientIdNameEx( - _In_ PCLIENT_ID ClientId, - _In_opt_ PPH_STRING ProcessName - ) -{ - PPH_STRING name; - PH_FORMAT format[5]; - - if (ClientId->UniqueThread) - { - if (ProcessName) - { - PhInitFormatSR(&format[0], ProcessName->sr); - PhInitFormatS(&format[1], L" ("); - PhInitFormatIU(&format[2], (ULONG_PTR)ClientId->UniqueProcess); - PhInitFormatS(&format[3], L"): "); - PhInitFormatIU(&format[4], (ULONG_PTR)ClientId->UniqueThread); - - name = PhFormat(format, 5, ProcessName->Length + 16 * sizeof(WCHAR)); - } - else - { - PhInitFormatS(&format[0], L"Non-existent process ("); - PhInitFormatIU(&format[1], (ULONG_PTR)ClientId->UniqueProcess); - PhInitFormatS(&format[2], L"): "); - PhInitFormatIU(&format[3], (ULONG_PTR)ClientId->UniqueThread); - - name = PhFormat(format, 4, 0); - } - } - else - { - if (ProcessName) - { - PhInitFormatSR(&format[0], ProcessName->sr); - PhInitFormatS(&format[1], L" ("); - PhInitFormatIU(&format[2], (ULONG_PTR)ClientId->UniqueProcess); - PhInitFormatC(&format[3], ')'); - - name = PhFormat(format, 4, 0); - } - else - { - PhInitFormatS(&format[0], L"Non-existent process ("); - PhInitFormatIU(&format[1], (ULONG_PTR)ClientId->UniqueProcess); - PhInitFormatC(&format[2], ')'); - - name = PhFormat(format, 3, 0); - } - } - - return name; -} - -PWSTR PhGetProcessPriorityClassString( - _In_ ULONG PriorityClass - ) -{ - switch (PriorityClass) - { - case PROCESS_PRIORITY_CLASS_REALTIME: - return L"Real time"; - case PROCESS_PRIORITY_CLASS_HIGH: - return L"High"; - case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: - return L"Above normal"; - case PROCESS_PRIORITY_CLASS_NORMAL: - return L"Normal"; - case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: - return L"Below normal"; - case PROCESS_PRIORITY_CLASS_IDLE: - return L"Idle"; - default: - return L"Unknown"; - } -} - -/** - * Creates a process item. - */ -PPH_PROCESS_ITEM PhCreateProcessItem( - _In_ HANDLE ProcessId - ) -{ - PPH_PROCESS_ITEM processItem; - - processItem = PhCreateObject( - PhEmGetObjectSize(EmProcessItemType, sizeof(PH_PROCESS_ITEM)), - PhProcessItemType - ); - memset(processItem, 0, sizeof(PH_PROCESS_ITEM)); - PhInitializeEvent(&processItem->Stage1Event); - PhInitializeQueuedLock(&processItem->ServiceListLock); - - processItem->ProcessId = ProcessId; - - if (!PH_IS_FAKE_PROCESS_ID(ProcessId)) - PhPrintUInt32(processItem->ProcessIdString, HandleToUlong(ProcessId)); - - // Create the statistics buffers. - PhInitializeCircularBuffer_FLOAT(&processItem->CpuKernelHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_FLOAT(&processItem->CpuUserHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&processItem->IoReadHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&processItem->IoWriteHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&processItem->IoOtherHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, PhStatisticsSampleCount); - //PhInitializeCircularBuffer_SIZE_T(&processItem->WorkingSetHistory, PhStatisticsSampleCount); - - PhEmCallObjectOperation(EmProcessItemType, processItem, EmObjectCreate); - - return processItem; -} - -VOID PhpProcessItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Object; - ULONG i; - - PhEmCallObjectOperation(EmProcessItemType, processItem, EmObjectDelete); - - PhDeleteCircularBuffer_FLOAT(&processItem->CpuKernelHistory); - PhDeleteCircularBuffer_FLOAT(&processItem->CpuUserHistory); - PhDeleteCircularBuffer_ULONG64(&processItem->IoReadHistory); - PhDeleteCircularBuffer_ULONG64(&processItem->IoWriteHistory); - PhDeleteCircularBuffer_ULONG64(&processItem->IoOtherHistory); - PhDeleteCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory); - //PhDeleteCircularBuffer_SIZE_T(&processItem->WorkingSetHistory); - - if (processItem->ServiceList) - { - PPH_SERVICE_ITEM serviceItem; - - i = 0; - - while (PhEnumPointerList(processItem->ServiceList, &i, &serviceItem)) - PhDereferenceObject(serviceItem); - - PhDereferenceObject(processItem->ServiceList); - } - - if (processItem->ProcessName) PhDereferenceObject(processItem->ProcessName); - if (processItem->FileName) PhDereferenceObject(processItem->FileName); - if (processItem->CommandLine) PhDereferenceObject(processItem->CommandLine); - if (processItem->SmallIcon) DestroyIcon(processItem->SmallIcon); - if (processItem->LargeIcon) DestroyIcon(processItem->LargeIcon); - PhDeleteImageVersionInfo(&processItem->VersionInfo); - if (processItem->UserName) PhDereferenceObject(processItem->UserName); - if (processItem->JobName) PhDereferenceObject(processItem->JobName); - if (processItem->VerifySignerName) PhDereferenceObject(processItem->VerifySignerName); - if (processItem->PackageFullName) PhDereferenceObject(processItem->PackageFullName); - - if (processItem->QueryHandle) NtClose(processItem->QueryHandle); - - if (processItem->Record) PhDereferenceProcessRecord(processItem->Record); -} - -FORCEINLINE BOOLEAN PhCompareProcessItem( - _In_ PPH_PROCESS_ITEM Value1, - _In_ PPH_PROCESS_ITEM Value2 - ) -{ - return Value1->ProcessId == Value2->ProcessId; -} - -FORCEINLINE ULONG PhHashProcessItem( - _In_ PPH_PROCESS_ITEM Value - ) -{ - return HandleToUlong(Value->ProcessId) / 4; -} - -/** - * Finds a process item in the hash set. - * - * \param ProcessId The process ID of the process item. - * - * \remarks The hash set must be locked before calling this function. The reference count of the - * found process item is not incremented. - */ -PPH_PROCESS_ITEM PhpLookupProcessItem( - _In_ HANDLE ProcessId - ) -{ - PH_PROCESS_ITEM lookupProcessItem; - PPH_HASH_ENTRY entry; - PPH_PROCESS_ITEM processItem; - - lookupProcessItem.ProcessId = ProcessId; - entry = PhFindEntryHashSet( - PhProcessHashSet, - PH_HASH_SET_SIZE(PhProcessHashSet), - PhHashProcessItem(&lookupProcessItem) - ); - - for (; entry; entry = entry->Next) - { - processItem = CONTAINING_RECORD(entry, PH_PROCESS_ITEM, HashEntry); - - if (PhCompareProcessItem(&lookupProcessItem, processItem)) - return processItem; - } - - return NULL; -} - -/** - * Finds and references a process item. - * - * \param ProcessId The process ID of the process item. - * - * \return The found process item. - */ -PPH_PROCESS_ITEM PhReferenceProcessItem( - _In_ HANDLE ProcessId - ) -{ - PPH_PROCESS_ITEM processItem; - - PhAcquireQueuedLockShared(&PhProcessHashSetLock); - - processItem = PhpLookupProcessItem(ProcessId); - - if (processItem) - PhReferenceObject(processItem); - - PhReleaseQueuedLockShared(&PhProcessHashSetLock); - - return processItem; -} - -/** - * Enumerates the process items. - * - * \param ProcessItems A variable which receives an array of pointers to process items. You must - * free the buffer with PhFree() when you no longer need it. - * \param NumberOfProcessItems A variable which receives the number of process items returned in - * \a ProcessItems. - */ -VOID PhEnumProcessItems( - _Out_opt_ PPH_PROCESS_ITEM **ProcessItems, - _Out_ PULONG NumberOfProcessItems - ) -{ - PPH_PROCESS_ITEM *processItems; - ULONG numberOfProcessItems; - ULONG count = 0; - ULONG i; - PPH_HASH_ENTRY entry; - PPH_PROCESS_ITEM processItem; - - if (!ProcessItems) - { - *NumberOfProcessItems = PhProcessHashSetCount; - return; - } - - PhAcquireQueuedLockShared(&PhProcessHashSetLock); - - numberOfProcessItems = PhProcessHashSetCount; - processItems = PhAllocate(sizeof(PPH_PROCESS_ITEM) * numberOfProcessItems); - - for (i = 0; i < PH_HASH_SET_SIZE(PhProcessHashSet); i++) - { - for (entry = PhProcessHashSet[i]; entry; entry = entry->Next) - { - processItem = CONTAINING_RECORD(entry, PH_PROCESS_ITEM, HashEntry); - PhReferenceObject(processItem); - processItems[count++] = processItem; - } - } - - PhReleaseQueuedLockShared(&PhProcessHashSetLock); - - *ProcessItems = processItems; - *NumberOfProcessItems = numberOfProcessItems; -} - -VOID PhpAddProcessItem( - _In_ _Assume_refs_(1) PPH_PROCESS_ITEM ProcessItem - ) -{ - PhAddEntryHashSet( - PhProcessHashSet, - PH_HASH_SET_SIZE(PhProcessHashSet), - &ProcessItem->HashEntry, - PhHashProcessItem(ProcessItem) - ); - PhProcessHashSetCount++; -} - -VOID PhpRemoveProcessItem( - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PhRemoveEntryHashSet(PhProcessHashSet, PH_HASH_SET_SIZE(PhProcessHashSet), &ProcessItem->HashEntry); - PhProcessHashSetCount--; - PhDereferenceObject(ProcessItem); -} - -INT NTAPI PhpVerifyCacheCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ) -{ - PPH_VERIFY_CACHE_ENTRY entry1 = CONTAINING_RECORD(Links1, PH_VERIFY_CACHE_ENTRY, Links); - PPH_VERIFY_CACHE_ENTRY entry2 = CONTAINING_RECORD(Links2, PH_VERIFY_CACHE_ENTRY, Links); - - return PhCompareString(entry1->FileName, entry2->FileName, TRUE); -} - -VERIFY_RESULT PhVerifyFileWithAdditionalCatalog( - _In_ PPH_VERIFY_FILE_INFO Information, - _In_opt_ PWSTR PackageFullName, - _Out_opt_ PPH_STRING *SignerName - ) -{ - static PH_STRINGREF codeIntegrityFileName = PH_STRINGREF_INIT(L"\\AppxMetadata\\CodeIntegrity.cat"); - - VERIFY_RESULT result; - PPH_STRING additionalCatalogFileName = NULL; - PCERT_CONTEXT *signatures; - ULONG numberOfSignatures; - - if (PackageFullName) - { - PACKAGE_ID *packageId; - PPH_STRING packagePath; - - if (packageId = PhPackageIdFromFullName(PackageFullName)) - { - if (packagePath = PhGetPackagePath(packageId)) - { - additionalCatalogFileName = PhConcatStringRef2(&packagePath->sr, &codeIntegrityFileName); - PhDereferenceObject(packagePath); - } - - PhFree(packageId); - } - } - - if (additionalCatalogFileName) - { - Information->NumberOfCatalogFileNames = 1; - Information->CatalogFileNames = &additionalCatalogFileName->Buffer; - } - - if (!NT_SUCCESS(PhVerifyFileEx(Information, &result, &signatures, &numberOfSignatures))) - { - result = VrNoSignature; - signatures = NULL; - numberOfSignatures = 0; - } - - if (additionalCatalogFileName) - PhDereferenceObject(additionalCatalogFileName); - - if (SignerName) - { - if (numberOfSignatures != 0) - *SignerName = PhGetSignerNameFromCertificate(signatures[0]); - else - *SignerName = NULL; - } - - PhFreeVerifySignatures(signatures, numberOfSignatures); - - return result; -} - -/** - * Verifies a file's digital signature, using a cached result if possible. - * - * \param FileName A file name. - * \param ProcessItem An associated process item. - * \param SignerName A variable which receives a pointer to a string containing the signer name. You - * must free the string using PhDereferenceObject() when you no longer need it. Note that the signer - * name may be NULL if it is not valid. - * \param CachedOnly Specify TRUE to fail the function when no cached result exists. - * - * \return A VERIFY_RESULT value. - */ -VERIFY_RESULT PhVerifyFileCached( - _In_ PPH_STRING FileName, - _In_opt_ PWSTR PackageFullName, - _Out_opt_ PPH_STRING *SignerName, - _In_ BOOLEAN CachedOnly - ) -{ -#ifdef PH_ENABLE_VERIFY_CACHE - PPH_AVL_LINKS links; - PPH_VERIFY_CACHE_ENTRY entry; - PH_VERIFY_CACHE_ENTRY lookupEntry; - - lookupEntry.FileName = FileName; - - PhAcquireQueuedLockShared(&PhpVerifyCacheLock); - links = PhFindElementAvlTree(&PhpVerifyCacheSet, &lookupEntry.Links); - PhReleaseQueuedLockShared(&PhpVerifyCacheLock); - - if (links) - { - entry = CONTAINING_RECORD(links, PH_VERIFY_CACHE_ENTRY, Links); - - if (SignerName) - PhSetReference(SignerName, entry->VerifySignerName); - - return entry->VerifyResult; - } - else - { - VERIFY_RESULT result; - PPH_STRING signerName; - - if (!CachedOnly) - { - PH_VERIFY_FILE_INFO info; - - memset(&info, 0, sizeof(PH_VERIFY_FILE_INFO)); - info.FileName = FileName->Buffer; - info.Flags = PH_VERIFY_PREVENT_NETWORK_ACCESS; - result = PhVerifyFileWithAdditionalCatalog(&info, PackageFullName, &signerName); - - if (result != VrTrusted) - PhClearReference(&signerName); - } - else - { - result = VrUnknown; - signerName = NULL; - } - - if (result != VrUnknown) - { - entry = PhAllocate(sizeof(PH_VERIFY_CACHE_ENTRY)); - entry->FileName = FileName; - entry->VerifyResult = result; - entry->VerifySignerName = signerName; - - PhAcquireQueuedLockExclusive(&PhpVerifyCacheLock); - links = PhAddElementAvlTree(&PhpVerifyCacheSet, &entry->Links); - PhReleaseQueuedLockExclusive(&PhpVerifyCacheLock); - - if (!links) - { - // We successfully added the cache entry. Add references. - - PhReferenceObject(entry->FileName); - - if (entry->VerifySignerName) - PhReferenceObject(entry->VerifySignerName); - } - else - { - // Entry already exists. - PhFree(entry); - } - } - - if (SignerName) - { - *SignerName = signerName; - } - else - { - if (signerName) - PhDereferenceObject(signerName); - } - - return result; - } -#else - VERIFY_RESULT result; - PPH_STRING signerName; - PH_VERIFY_FILE_INFO info; - - memset(&info, 0, sizeof(PH_VERIFY_FILE_INFO)); - info.FileName = FileName->Buffer; - info.Flags = PH_VERIFY_PREVENT_NETWORK_ACCESS; - result = PhVerifyFileWithAdditionalCatalog(&info, PackageFullName, &signerName); - - if (result != VrTrusted) - PhClearReference(&signerName); - - if (SignerName) - { - *SignerName = signerName; - } - else - { - if (signerName) - PhDereferenceObject(signerName); - } - - return result; -#endif -} - -BOOLEAN PhpSidFullNameCacheHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_SID_FULL_NAME_CACHE_ENTRY entry1 = Entry1; - PPH_SID_FULL_NAME_CACHE_ENTRY entry2 = Entry2; - - return RtlEqualSid(entry1->Sid, entry2->Sid); -} - -ULONG PhpSidFullNameCacheHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPH_SID_FULL_NAME_CACHE_ENTRY entry = Entry; - - return PhHashBytes(entry->Sid, RtlLengthSid(entry->Sid)); -} - -PPH_STRING PhpGetSidFullNameCached( - _In_ PSID Sid - ) -{ - PPH_STRING fullName; - PH_SID_FULL_NAME_CACHE_ENTRY newEntry; - - if (PhpSidFullNameCacheHashtable) - { - PPH_SID_FULL_NAME_CACHE_ENTRY entry; - PH_SID_FULL_NAME_CACHE_ENTRY lookupEntry; - - lookupEntry.Sid = Sid; - entry = PhFindEntryHashtable(PhpSidFullNameCacheHashtable, &lookupEntry); - - if (entry) - return PhReferenceObject(entry->FullName); - } - - fullName = PhGetSidFullName(Sid, TRUE, NULL); - - if (!fullName) - return NULL; - - if (!PhpSidFullNameCacheHashtable) - { - PhpSidFullNameCacheHashtable = PhCreateHashtable( - sizeof(PH_SID_FULL_NAME_CACHE_ENTRY), - PhpSidFullNameCacheHashtableEqualFunction, - PhpSidFullNameCacheHashtableHashFunction, - 16 - ); - } - - newEntry.Sid = PhAllocateCopy(Sid, RtlLengthSid(Sid)); - newEntry.FullName = PhReferenceObject(fullName); - PhAddEntryHashtable(PhpSidFullNameCacheHashtable, &newEntry); - - return fullName; -} - -VOID PhpFlushSidFullNameCache( - VOID - ) -{ - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_SID_FULL_NAME_CACHE_ENTRY entry; - - if (!PhpSidFullNameCacheHashtable) - return; - - PhBeginEnumHashtable(PhpSidFullNameCacheHashtable, &enumContext); - - while (entry = PhNextEnumHashtable(&enumContext)) - { - PhFree(entry->Sid); - PhDereferenceObject(entry->FullName); - } - - PhClearReference(&PhpSidFullNameCacheHashtable); -} - -VOID PhpProcessQueryStage1( - _Inout_ PPH_PROCESS_QUERY_S1_DATA Data - ) -{ - NTSTATUS status; - PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; - HANDLE processId = processItem->ProcessId; - HANDLE processHandleLimited = NULL; - - PhOpenProcess(&processHandleLimited, ProcessQueryAccess, processId); - - if (processItem->FileName) - { - // Small icon, large icon. - if (ExtractIconEx( - processItem->FileName->Buffer, - 0, - &Data->LargeIcon, - &Data->SmallIcon, - 1 - ) == 0) - { - Data->LargeIcon = NULL; - Data->SmallIcon = NULL; - } - - // Version info. - PhInitializeImageVersionInfo(&Data->VersionInfo, processItem->FileName->Buffer); - } - - // Use the default EXE icon if we didn't get the file's icon. - { - if (!Data->SmallIcon || !Data->LargeIcon) - { - if (Data->SmallIcon) - { - DestroyIcon(Data->SmallIcon); - Data->SmallIcon = NULL; - } - else if (Data->LargeIcon) - { - DestroyIcon(Data->LargeIcon); - Data->LargeIcon = NULL; - } - - PhGetStockApplicationIcon(&Data->SmallIcon, &Data->LargeIcon); - Data->SmallIcon = CopyIcon(Data->SmallIcon); - Data->LargeIcon = CopyIcon(Data->LargeIcon); - } - } - - // Process flags - if (processHandleLimited) - { - PROCESS_EXTENDED_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(processHandleLimited, &basicInfo))) - { - Data->IsProtectedProcess = basicInfo.IsProtectedProcess; - Data->IsSecureProcess = basicInfo.IsSecureProcess; - Data->IsSubsystemProcess = basicInfo.IsSubsystemProcess; - Data->IsWow64 = basicInfo.IsWow64Process; - Data->IsWow64Valid = TRUE; - } - } - - // Command line, .NET - { - HANDLE processHandle; - BOOLEAN queryAccess = FALSE; - - status = PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_VM_READ, - processId - ); - - if (!NT_SUCCESS(status) && WindowsVersion >= WINDOWS_8_1) - { - queryAccess = TRUE; - status = PhOpenProcess( - &processHandle, - ProcessQueryAccess, - processId - ); - } - - if (NT_SUCCESS(status)) - { - BOOLEAN isDotNet = FALSE; - PPH_STRING commandLine; - ULONG i; - - if (NT_SUCCESS(status = PhGetProcessCommandLine(processHandle, &commandLine))) - { - // Some command lines (e.g. from taskeng.exe) have nulls in them. Since Windows - // can't display them, we'll replace them with spaces. - for (i = 0; i < (ULONG)commandLine->Length / 2; i++) - { - if (commandLine->Buffer[i] == 0) - commandLine->Buffer[i] = ' '; - } - } - - if (NT_SUCCESS(status)) - { - Data->CommandLine = commandLine; - } - - if (!queryAccess) - { - PhGetProcessIsDotNetEx( - processId, - processHandle, -#ifdef _WIN64 - PH_CLR_NO_WOW64_CHECK | (Data->IsWow64 ? PH_CLR_KNOWN_IS_WOW64 : 0), -#else - 0, -#endif - &isDotNet, - NULL - ); - Data->IsDotNet = isDotNet; - } - - NtClose(processHandle); - } - } - - // Token information - if (processHandleLimited) - { - if (WINDOWS_HAS_UAC) - { - HANDLE tokenHandle; - - status = PhOpenProcessToken(processHandleLimited, TOKEN_QUERY, &tokenHandle); - - if (NT_SUCCESS(status)) - { - // Elevation - if (NT_SUCCESS(PhGetTokenElevationType( - tokenHandle, - &Data->ElevationType - ))) - { - Data->IsElevated = Data->ElevationType == TokenElevationTypeFull; - } - - // Integrity - PhGetTokenIntegrityLevel( - tokenHandle, - &Data->IntegrityLevel, - &Data->IntegrityString - ); - - NtClose(tokenHandle); - } - } - } - - // Job - if (processHandleLimited) - { - if (KphIsConnected()) - { - HANDLE jobHandle = NULL; - - status = KphOpenProcessJob( - processHandleLimited, - JOB_OBJECT_QUERY, - &jobHandle - ); - - if (NT_SUCCESS(status) && status != STATUS_PROCESS_NOT_IN_JOB && jobHandle) - { - JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimits; - - Data->IsInJob = TRUE; - - PhGetHandleInformation( - NtCurrentProcess(), - jobHandle, - -1, - NULL, - NULL, - NULL, - &Data->JobName - ); - - // Process Explorer only recognizes processes as being in jobs if they don't have - // the silent-breakaway-OK limit as their only limit. Emulate this behaviour. - if (NT_SUCCESS(PhGetJobBasicLimits(jobHandle, &basicLimits))) - { - Data->IsInSignificantJob = - basicLimits.LimitFlags != JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; - } - - NtClose(jobHandle); - } - } - else - { - // KProcessHacker is not available. We can determine if the process is in a job, but we - // can't get a handle to the job. - - status = NtIsProcessInJob(processHandleLimited, NULL); - - if (NT_SUCCESS(status)) - Data->IsInJob = status == STATUS_PROCESS_IN_JOB; - } - } - - // Console host process - if (processHandleLimited && WINDOWS_HAS_CONSOLE_HOST) - { - PhGetProcessConsoleHostProcessId(processHandleLimited, &Data->ConsoleHostProcessId); - } - - // Package full name - if (processHandleLimited && WINDOWS_HAS_IMMERSIVE) - { - Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); - } - - if (processHandleLimited) - NtClose(processHandleLimited); - - PhpQueueProcessQueryStage2(processItem); -} - -VOID PhpProcessQueryStage2( - _Inout_ PPH_PROCESS_QUERY_S2_DATA Data - ) -{ - NTSTATUS status; - PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; - - if (PhEnableProcessQueryStage2 && processItem->FileName) - { - PPH_STRING packageFullName = NULL; - - if (processItem->QueryHandle) - packageFullName = PhGetProcessPackageFullName(processItem->QueryHandle); - - Data->VerifyResult = PhVerifyFileCached( - processItem->FileName, - PhGetString(packageFullName), - &Data->VerifySignerName, - FALSE - ); - - if (packageFullName) - PhDereferenceObject(packageFullName); - - status = PhIsExecutablePacked( - processItem->FileName->Buffer, - &Data->IsPacked, - &Data->ImportModules, - &Data->ImportFunctions - ); - - // If we got an image-related error, the image is packed. - if ( - status == STATUS_INVALID_IMAGE_NOT_MZ || - status == STATUS_INVALID_IMAGE_FORMAT || - status == STATUS_ACCESS_VIOLATION - ) - { - Data->IsPacked = TRUE; - Data->ImportModules = -1; - Data->ImportFunctions = -1; - } - } -} - -NTSTATUS PhpProcessQueryStage1Worker( - _In_ PVOID Parameter - ) -{ - PPH_PROCESS_QUERY_S1_DATA data; - PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Parameter; - - data = PhAllocate(sizeof(PH_PROCESS_QUERY_S1_DATA)); - memset(data, 0, sizeof(PH_PROCESS_QUERY_S1_DATA)); - data->Header.Stage = 1; - data->Header.ProcessItem = processItem; - - PhpProcessQueryStage1(data); - - RtlInterlockedPushEntrySList(&PhProcessQueryDataListHead, &data->Header.ListEntry); - - return STATUS_SUCCESS; -} - -NTSTATUS PhpProcessQueryStage2Worker( - _In_ PVOID Parameter - ) -{ - PPH_PROCESS_QUERY_S2_DATA data; - PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Parameter; - - data = PhAllocate(sizeof(PH_PROCESS_QUERY_S2_DATA)); - memset(data, 0, sizeof(PH_PROCESS_QUERY_S2_DATA)); - data->Header.Stage = 2; - data->Header.ProcessItem = processItem; - - PhpProcessQueryStage2(data); - - RtlInterlockedPushEntrySList(&PhProcessQueryDataListHead, &data->Header.ListEntry); - - return STATUS_SUCCESS; -} - -VOID PhpQueueProcessQueryStage1( - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PH_WORK_QUEUE_ENVIRONMENT environment; - - // Ref: dereferenced when the provider update function removes the item from the queue. - PhReferenceObject(ProcessItem); - - PhInitializeWorkQueueEnvironment(&environment); - environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; - - PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpProcessQueryStage1Worker, ProcessItem, - NULL, &environment); -} - -VOID PhpQueueProcessQueryStage2( - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PH_WORK_QUEUE_ENVIRONMENT environment; - - if (!PhEnableProcessQueryStage2) - return; - - PhReferenceObject(ProcessItem); - - PhInitializeWorkQueueEnvironment(&environment); - environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; - environment.IoPriority = IoPriorityVeryLow; - environment.PagePriority = MEMORY_PRIORITY_VERY_LOW; - - PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpProcessQueryStage2Worker, ProcessItem, - NULL, &environment); -} - -VOID PhpFillProcessItemStage1( - _In_ PPH_PROCESS_QUERY_S1_DATA Data - ) -{ - PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; - - processItem->CommandLine = Data->CommandLine; - processItem->SmallIcon = Data->SmallIcon; - processItem->LargeIcon = Data->LargeIcon; - memcpy(&processItem->VersionInfo, &Data->VersionInfo, sizeof(PH_IMAGE_VERSION_INFO)); - processItem->ElevationType = Data->ElevationType; - processItem->IntegrityLevel = Data->IntegrityLevel; - processItem->IntegrityString = Data->IntegrityString; - processItem->JobName = Data->JobName; - processItem->ConsoleHostProcessId = Data->ConsoleHostProcessId; - processItem->PackageFullName = Data->PackageFullName; - processItem->IsDotNet = Data->IsDotNet; - processItem->IsElevated = Data->IsElevated; - processItem->IsInJob = Data->IsInJob; - processItem->IsInSignificantJob = Data->IsInSignificantJob; - processItem->IsWow64 = Data->IsWow64; - processItem->IsWow64Valid = Data->IsWow64Valid; - processItem->IsProtectedProcess = Data->IsProtectedProcess; - processItem->IsSecureProcess = Data->IsSecureProcess; - processItem->IsSubsystemProcess = Data->IsSubsystemProcess; - - PhSwapReference(&processItem->Record->CommandLine, processItem->CommandLine); -} - -VOID PhpFillProcessItemStage2( - _In_ PPH_PROCESS_QUERY_S2_DATA Data - ) -{ - PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; - - processItem->VerifyResult = Data->VerifyResult; - processItem->VerifySignerName = Data->VerifySignerName; - processItem->IsPacked = Data->IsPacked; - processItem->ImportFunctions = Data->ImportFunctions; - processItem->ImportModules = Data->ImportModules; -} - -VOID PhpFillProcessItem( - _Inout_ PPH_PROCESS_ITEM ProcessItem, - _In_ PSYSTEM_PROCESS_INFORMATION Process - ) -{ - NTSTATUS status; - HANDLE processHandle = NULL; - - ProcessItem->ParentProcessId = Process->InheritedFromUniqueProcessId; - ProcessItem->SessionId = Process->SessionId; - ProcessItem->CreateTime = Process->CreateTime; - - if (ProcessItem->ProcessId != SYSTEM_IDLE_PROCESS_ID) - ProcessItem->ProcessName = PhCreateStringFromUnicodeString(&Process->ImageName); - else - ProcessItem->ProcessName = PhCreateString(SYSTEM_IDLE_PROCESS_NAME); - - PhPrintUInt32(ProcessItem->ParentProcessIdString, HandleToUlong(ProcessItem->ParentProcessId)); - PhPrintUInt32(ProcessItem->SessionIdString, ProcessItem->SessionId); - - PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessItem->ProcessId); - - // Process information - { - // If we're dealing with System (PID 4), we need to get the - // kernel file name. Otherwise, get the image file name. - - if (ProcessItem->ProcessId != SYSTEM_PROCESS_ID) - { - PPH_STRING fileName; - - if (WindowsVersion >= WINDOWS_VISTA) - { - if (processHandle) - { - PhGetProcessImageFileNameWin32(processHandle, &ProcessItem->FileName); - } - else - { - if (NT_SUCCESS(PhGetProcessImageFileNameByProcessId(ProcessItem->ProcessId, &fileName))) - { - ProcessItem->FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - } - } - } - else - { - if (processHandle && NT_SUCCESS(PhGetProcessImageFileName(processHandle, &fileName))) - { - ProcessItem->FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - } - } - } - else - { - PPH_STRING fileName; - - fileName = PhGetKernelFileName(); - - if (fileName) - { - ProcessItem->FileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - } - } - } - - // Token-related information - if ( - processHandle && - ProcessItem->ProcessId != SYSTEM_PROCESS_ID // Token of System process can't be opened sometimes - ) - { - HANDLE tokenHandle; - - status = PhOpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle); - - if (NT_SUCCESS(status)) - { - // User name - { - PTOKEN_USER user; - - status = PhGetTokenUser(tokenHandle, &user); - - if (NT_SUCCESS(status)) - { - ProcessItem->UserName = PhpGetSidFullNameCached(user->User.Sid); - PhFree(user); - } - } - - NtClose(tokenHandle); - } - } - else - { - if ( - ProcessItem->ProcessId == SYSTEM_IDLE_PROCESS_ID || - ProcessItem->ProcessId == SYSTEM_PROCESS_ID // System token can't be opened on XP - ) - { - PhSetReference(&ProcessItem->UserName, PhLocalSystemName); - } - } - - if (!ProcessItem->UserName && WindowsVersion <= WINDOWS_XP) - { - // In some cases we can get the user SID using WTS (only works on XP and below). - - if (!PhpTsProcesses) - { - WinStationGetAllProcesses( - NULL, - 0, - &PhpTsNumberOfProcesses, - &PhpTsProcesses - ); - } - - if (PhpTsProcesses) - { - ULONG i; - - for (i = 0; i < PhpTsNumberOfProcesses; i++) - { - if (UlongToHandle(PhpTsProcesses[i].pTsProcessInfo->UniqueProcessId) == ProcessItem->ProcessId) - { - ProcessItem->UserName = PhpGetSidFullNameCached(PhpTsProcesses[i].pSid); - break; - } - } - } - } - - NtClose(processHandle); -} - -FORCEINLINE VOID PhpUpdateDynamicInfoProcessItem( - _Inout_ PPH_PROCESS_ITEM ProcessItem, - _In_ PSYSTEM_PROCESS_INFORMATION Process - ) -{ - ProcessItem->BasePriority = Process->BasePriority; - - if (ProcessItem->QueryHandle) - { - PROCESS_PRIORITY_CLASS priorityClass; - - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessPriorityClass, - &priorityClass, - sizeof(PROCESS_PRIORITY_CLASS), - NULL - ))) - { - ProcessItem->PriorityClass = priorityClass.PriorityClass; - } - } - else - { - ProcessItem->PriorityClass = 0; - } - - ProcessItem->KernelTime = Process->KernelTime; - ProcessItem->UserTime = Process->UserTime; - ProcessItem->NumberOfHandles = Process->HandleCount; - ProcessItem->NumberOfThreads = Process->NumberOfThreads; - ProcessItem->WorkingSetPrivateSize = (SIZE_T)Process->WorkingSetPrivateSize.QuadPart; - ProcessItem->PeakNumberOfThreads = Process->NumberOfThreadsHighWatermark; - ProcessItem->HardFaultCount = Process->HardFaultCount; - - // Update VM and I/O counters. - ProcessItem->VmCounters = *(PVM_COUNTERS_EX)&Process->PeakVirtualSize; - ProcessItem->IoCounters = *(PIO_COUNTERS)&Process->ReadOperationCount; -} - -VOID PhpUpdatePerfInformation( - VOID - ) -{ - NtQuerySystemInformation( - SystemPerformanceInformation, - &PhPerfInformation, - sizeof(SYSTEM_PERFORMANCE_INFORMATION), - NULL - ); - - PhUpdateDelta(&PhIoReadDelta, PhPerfInformation.IoReadTransferCount.QuadPart); - PhUpdateDelta(&PhIoWriteDelta, PhPerfInformation.IoWriteTransferCount.QuadPart); - PhUpdateDelta(&PhIoOtherDelta, PhPerfInformation.IoOtherTransferCount.QuadPart); -} - -VOID PhpUpdateCpuInformation( - _In_ BOOLEAN SetCpuUsage, - _Out_ PULONG64 TotalTime - ) -{ - ULONG i; - ULONG64 totalTime; - - NtQuerySystemInformation( - SystemProcessorPerformanceInformation, - PhCpuInformation, - sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, - NULL - ); - - // Zero the CPU totals. - memset(&PhCpuTotals, 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); - - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - { - PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION cpuInfo = - &PhCpuInformation[i]; - - // KernelTime includes IdleTime. - cpuInfo->KernelTime.QuadPart -= cpuInfo->IdleTime.QuadPart; - - PhCpuTotals.DpcTime.QuadPart += cpuInfo->DpcTime.QuadPart; - PhCpuTotals.IdleTime.QuadPart += cpuInfo->IdleTime.QuadPart; - PhCpuTotals.InterruptCount += cpuInfo->InterruptCount; - PhCpuTotals.InterruptTime.QuadPart += cpuInfo->InterruptTime.QuadPart; - PhCpuTotals.KernelTime.QuadPart += cpuInfo->KernelTime.QuadPart; - PhCpuTotals.UserTime.QuadPart += cpuInfo->UserTime.QuadPart; - - PhUpdateDelta(&PhCpusKernelDelta[i], cpuInfo->KernelTime.QuadPart); - PhUpdateDelta(&PhCpusUserDelta[i], cpuInfo->UserTime.QuadPart); - PhUpdateDelta(&PhCpusIdleDelta[i], cpuInfo->IdleTime.QuadPart); - - if (SetCpuUsage) - { - totalTime = PhCpusKernelDelta[i].Delta + PhCpusUserDelta[i].Delta + PhCpusIdleDelta[i].Delta; - - if (totalTime != 0) - { - PhCpusKernelUsage[i] = (FLOAT)PhCpusKernelDelta[i].Delta / totalTime; - PhCpusUserUsage[i] = (FLOAT)PhCpusUserDelta[i].Delta / totalTime; - } - else - { - PhCpusKernelUsage[i] = 0; - PhCpusUserUsage[i] = 0; - } - } - } - - PhUpdateDelta(&PhCpuKernelDelta, PhCpuTotals.KernelTime.QuadPart); - PhUpdateDelta(&PhCpuUserDelta, PhCpuTotals.UserTime.QuadPart); - PhUpdateDelta(&PhCpuIdleDelta, PhCpuTotals.IdleTime.QuadPart); - - totalTime = PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta + PhCpuIdleDelta.Delta; - - if (SetCpuUsage) - { - if (totalTime != 0) - { - PhCpuKernelUsage = (FLOAT)PhCpuKernelDelta.Delta / totalTime; - PhCpuUserUsage = (FLOAT)PhCpuUserDelta.Delta / totalTime; - } - else - { - PhCpuKernelUsage = 0; - PhCpuUserUsage = 0; - } - } - - *TotalTime = totalTime; -} - -VOID PhpUpdateCpuCycleInformation( - _Out_ PULONG64 IdleCycleTime - ) -{ - ULONG i; - ULONG64 total; - - // Idle - - // We need to query this separately because the idle cycle time in SYSTEM_PROCESS_INFORMATION - // doesn't give us data for individual processors. - - NtQuerySystemInformation( - SystemProcessorIdleCycleTimeInformation, - PhCpuIdleCycleTime, - sizeof(LARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, - NULL - ); - - total = 0; - - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - { - //PhUpdateDelta(&PhCpusIdleCycleDelta[i], PhCpuIdleCycleTime[i].QuadPart); - total += PhCpuIdleCycleTime[i].QuadPart; - } - - PhUpdateDelta(&PhCpuIdleCycleDelta, total); - *IdleCycleTime = PhCpuIdleCycleDelta.Delta; - - // System - - NtQuerySystemInformation( - SystemProcessorCycleTimeInformation, - PhCpuSystemCycleTime, - sizeof(LARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, - NULL - ); - - total = 0; - - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - { - total += PhCpuSystemCycleTime[i].QuadPart; - } - - PhUpdateDelta(&PhCpuSystemCycleDelta, total); -} - -VOID PhpUpdateCpuCycleUsageInformation( - _In_ ULONG64 TotalCycleTime, - _In_ ULONG64 IdleCycleTime - ) -{ - ULONG i; - FLOAT baseCpuUsage; - FLOAT totalTimeDelta; - ULONG64 totalTime; - - // Cycle time is not only lacking for kernel/user components, but also for individual - // processors. We can get the total idle cycle time for individual processors but - // without knowing the total cycle time for individual processors, this information - // is useless. - // - // We'll start by calculating the total CPU usage, then we'll calculate the kernel/user - // components. In the event that the corresponding CPU time deltas are zero, we'll split - // the CPU usage evenly across the kernel/user components. CPU usage for individual - // processors is left untouched, because it's too difficult to provide an estimate. - // - // Let I_1, I_2, ..., I_n be the idle cycle times and T_1, T_2, ..., T_n be the - // total cycle times. Let I'_1, I'_2, ..., I'_n be the idle CPU times and T'_1, T'_2, ..., - // T'_n be the total CPU times. - // We know all I'_n, T'_n and I_n, but we only know sigma(T). The "real" total CPU usage is - // sigma(I)/sigma(T), and the "real" individual CPU usage is I_n/T_n. The problem is that - // we don't know T_n; we only know sigma(T). Hence we need to find values i_1, i_2, ..., i_n - // and t_1, t_2, ..., t_n such that: - // sigma(i)/sigma(t) ~= sigma(I)/sigma(T), and - // i_n/t_n ~= I_n/T_n - // - // Solution 1: Set i_n = I_n and t_n = sigma(T)*T'_n/sigma(T'). Then: - // sigma(i)/sigma(t) = sigma(I)/(sigma(T)*sigma(T')/sigma(T')) = sigma(I)/sigma(T), and - // i_n/t_n = I_n/T'_n*sigma(T')/sigma(T) ~= I_n/T_n since I_n/T'_n ~= I_n/T_n and sigma(T')/sigma(T) ~= 1. - // However, it is not guaranteed that i_n/t_n <= 1, which may lead to CPU usages over 100% being displayed. - // - // Solution 2: Set i_n = I'_n and t_n = T'_n. Then: - // sigma(i)/sigma(t) = sigma(I')/sigma(T') ~= sigma(I)/sigma(T) since I'_n ~= I_n and T'_n ~= T_n. - // i_n/t_n = I'_n/T'_n ~= I_n/T_n as above. - // Not scaling at all is currently the best solution, since it's fast, simple and guarantees that i_n/t_n <= 1. - - baseCpuUsage = 1 - (FLOAT)IdleCycleTime / TotalCycleTime; - totalTimeDelta = (FLOAT)(PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta); - - if (totalTimeDelta != 0) - { - PhCpuKernelUsage = baseCpuUsage * ((FLOAT)PhCpuKernelDelta.Delta / totalTimeDelta); - PhCpuUserUsage = baseCpuUsage * ((FLOAT)PhCpuUserDelta.Delta / totalTimeDelta); - } - else - { - PhCpuKernelUsage = baseCpuUsage / 2; - PhCpuUserUsage = baseCpuUsage / 2; - } - - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - { - totalTime = PhCpusKernelDelta[i].Delta + PhCpusUserDelta[i].Delta + PhCpusIdleDelta[i].Delta; - - if (totalTime != 0) - { - PhCpusKernelUsage[i] = (FLOAT)PhCpusKernelDelta[i].Delta / totalTime; - PhCpusUserUsage[i] = (FLOAT)PhCpusUserDelta[i].Delta / totalTime; - } - else - { - PhCpusKernelUsage[i] = 0; - PhCpusUserUsage[i] = 0; - } - } -} - -VOID PhpInitializeProcessStatistics( - VOID - ) -{ - ULONG i; - - PhInitializeCircularBuffer_ULONG(&PhTimeHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_FLOAT(&PhCpuKernelHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_FLOAT(&PhCpuUserHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&PhIoReadHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&PhIoWriteHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&PhIoOtherHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG(&PhCommitHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG(&PhPhysicalHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG(&PhMaxCpuHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG(&PhMaxIoHistory, PhStatisticsSampleCount); -#ifdef PH_RECORD_MAX_USAGE - PhInitializeCircularBuffer_FLOAT(&PhMaxCpuUsageHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&PhMaxIoReadOtherHistory, PhStatisticsSampleCount); - PhInitializeCircularBuffer_ULONG64(&PhMaxIoWriteHistory, PhStatisticsSampleCount); -#endif - - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - { - PhInitializeCircularBuffer_FLOAT(&PhCpusKernelHistory[i], PhStatisticsSampleCount); - PhInitializeCircularBuffer_FLOAT(&PhCpusUserHistory[i], PhStatisticsSampleCount); - } -} - -VOID PhpUpdateSystemHistory( - VOID - ) -{ - ULONG i; - LARGE_INTEGER systemTime; - ULONG secondsSince1980; - - // CPU - PhAddItemCircularBuffer_FLOAT(&PhCpuKernelHistory, PhCpuKernelUsage); - PhAddItemCircularBuffer_FLOAT(&PhCpuUserHistory, PhCpuUserUsage); - - // CPUs - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - { - PhAddItemCircularBuffer_FLOAT(&PhCpusKernelHistory[i], PhCpusKernelUsage[i]); - PhAddItemCircularBuffer_FLOAT(&PhCpusUserHistory[i], PhCpusUserUsage[i]); - } - - // I/O - PhAddItemCircularBuffer_ULONG64(&PhIoReadHistory, PhIoReadDelta.Delta); - PhAddItemCircularBuffer_ULONG64(&PhIoWriteHistory, PhIoWriteDelta.Delta); - PhAddItemCircularBuffer_ULONG64(&PhIoOtherHistory, PhIoOtherDelta.Delta); - - // Memory - PhAddItemCircularBuffer_ULONG(&PhCommitHistory, PhPerfInformation.CommittedPages); - PhAddItemCircularBuffer_ULONG(&PhPhysicalHistory, - PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages - ); - - // Time - PhQuerySystemTime(&systemTime); - RtlTimeToSecondsSince1980(&systemTime, &secondsSince1980); - PhAddItemCircularBuffer_ULONG(&PhTimeHistory, secondsSince1980); -} - -/** - * Retrieves a time value recorded by the statistics system. - * - * \param ProcessItem A process item to synchronize with, or NULL if no synchronization is - * necessary. - * \param Index The history index. - * \param Time A variable which receives the time at \a Index. - * - * \return TRUE if the function succeeded, otherwise FALSE if \a ProcessItem was specified and - * \a Index is too far into the past for that process item. - */ -BOOLEAN PhGetStatisticsTime( - _In_opt_ PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG Index, - _Out_ PLARGE_INTEGER Time - ) -{ - ULONG secondsSince1980; - ULONG index; - LARGE_INTEGER time; - - if (ProcessItem) - { - // The sequence number is used to synchronize statistics when a process exits, since that - // process' history is not updated anymore. - index = PhTimeSequenceNumber - ProcessItem->SequenceNumber + Index; - - if (index >= PhTimeHistory.Count) - { - // The data point is too far into the past. - return FALSE; - } - } - else - { - // Assume the index is valid. - index = Index; - } - - secondsSince1980 = PhGetItemCircularBuffer_ULONG(&PhTimeHistory, index); - RtlSecondsSince1980ToTime(secondsSince1980, &time); - - *Time = time; - - return TRUE; -} - -PPH_STRING PhGetStatisticsTimeString( - _In_opt_ PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG Index - ) -{ - LARGE_INTEGER time; - SYSTEMTIME systemTime; - - if (PhGetStatisticsTime(ProcessItem, Index, &time)) - { - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - - return PhFormatDateTime(&systemTime); - } - else - { - return PhCreateString(L"Unknown time"); - } -} - -VOID PhFlushProcessQueryData( - _In_ BOOLEAN SendModifiedEvent - ) -{ - PSLIST_ENTRY entry; - PPH_PROCESS_QUERY_DATA data; - BOOLEAN processed; - - if (!RtlFirstEntrySList(&PhProcessQueryDataListHead)) - return; - - entry = RtlInterlockedFlushSList(&PhProcessQueryDataListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_PROCESS_QUERY_DATA, ListEntry); - entry = entry->Next; - processed = FALSE; - - if (data->Stage == 1) - { - PhpFillProcessItemStage1((PPH_PROCESS_QUERY_S1_DATA)data); - PhSetEvent(&data->ProcessItem->Stage1Event); - processed = TRUE; - } - else if (data->Stage == 2) - { - PhpFillProcessItemStage2((PPH_PROCESS_QUERY_S2_DATA)data); - processed = TRUE; - } - - if (processed) - { - // Invoke the modified event only if the main provider has sent the added event already. - if (SendModifiedEvent && data->ProcessItem->AddedEventSent) - { - // Since this may be executing on a thread other than the main provider thread, we - // need to check whether the process has been removed already. If we don't do this - // then users may get a modified event after a removed event for the same process, - // which will lead to very bad things happening. - PhAcquireQueuedLockExclusive(&data->ProcessItem->RemoveLock); - if (!(data->ProcessItem->State & PH_PROCESS_ITEM_REMOVED)) - PhInvokeCallback(&PhProcessModifiedEvent, data->ProcessItem); - PhReleaseQueuedLockExclusive(&data->ProcessItem->RemoveLock); - } - else - { - data->ProcessItem->JustProcessed = 1; - } - } - - PhDereferenceObject(data->ProcessItem); - PhFree(data); - } -} - -VOID PhpGetProcessThreadInformation( - _In_ PSYSTEM_PROCESS_INFORMATION Process, - _Out_opt_ PBOOLEAN IsSuspended, - _Out_opt_ PBOOLEAN IsPartiallySuspended, - _Out_opt_ PULONG ContextSwitches - ) -{ - ULONG i; - BOOLEAN isSuspended; - BOOLEAN isPartiallySuspended; - ULONG contextSwitches; - - isSuspended = PH_IS_REAL_PROCESS_ID(Process->UniqueProcessId); - isPartiallySuspended = FALSE; - contextSwitches = 0; - - for (i = 0; i < Process->NumberOfThreads; i++) - { - if (Process->Threads[i].ThreadState != Waiting || - Process->Threads[i].WaitReason != Suspended) - { - isSuspended = FALSE; - } - else - { - isPartiallySuspended = TRUE; - } - - contextSwitches += Process->Threads[i].ContextSwitches; - } - - if (IsSuspended) - *IsSuspended = isSuspended; - if (IsPartiallySuspended) - *IsPartiallySuspended = isPartiallySuspended; - if (ContextSwitches) - *ContextSwitches = contextSwitches; -} - -VOID PhProcessProviderUpdate( - _In_ PVOID Object - ) -{ - static ULONG runCount = 0; - static PSYSTEM_PROCESS_INFORMATION pidBuckets[PROCESS_ID_BUCKETS]; - - // Note about locking: - // - // Since this is the only function that is allowed to modify the process hashtable, locking is - // not needed for shared accesses. However, exclusive accesses need locking. - - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - ULONG bucketIndex; - - BOOLEAN isCycleCpuUsageEnabled = FALSE; - - ULONG64 sysTotalTime; // total time for this update period - ULONG64 sysTotalCycleTime = 0; // total cycle time for this update period - ULONG64 sysIdleCycleTime = 0; // total idle cycle time for this update period - FLOAT maxCpuValue = 0; - PPH_PROCESS_ITEM maxCpuProcessItem = NULL; - ULONG64 maxIoValue = 0; - PPH_PROCESS_ITEM maxIoProcessItem = NULL; - - // Pre-update tasks - - if (runCount % 5 == 0) - { - PhUpdateDosDevicePrefixes(); - } - - if (runCount % 512 == 0) // yes, a very long time - { - if (PhEnablePurgeProcessRecords) - PhPurgeProcessRecords(); - } - - isCycleCpuUsageEnabled = WindowsVersion >= WINDOWS_7 && PhEnableCycleCpuUsage; - - if (!PhProcessStatisticsInitialized) - { - PhpInitializeProcessStatistics(); - PhProcessStatisticsInitialized = TRUE; - } - - PhpUpdatePerfInformation(); - - if (isCycleCpuUsageEnabled) - { - PhpUpdateCpuInformation(FALSE, &sysTotalTime); - PhpUpdateCpuCycleInformation(&sysIdleCycleTime); - } - else - { - PhpUpdateCpuInformation(TRUE, &sysTotalTime); - } - - if (runCount != 0) - { - PhTimeSequenceNumber++; - } - - // Get the process list. - - PhTotalProcesses = 0; - PhTotalThreads = 0; - PhTotalHandles = 0; - - if (!NT_SUCCESS(PhEnumProcesses(&processes))) - return; - - // Notes on cycle-based CPU usage: - // - // Cycle-based CPU usage is a bit tricky to calculate because we cannot get the total number of - // cycles consumed by all processes since system startup - we can only get total number of - // cycles per process. This means there are two ways to calculate the system-wide cycle time - // delta: - // - // 1. Each update, sum the cycle times of all processes, and calculate the system-wide delta - // from this. Process Explorer seems to do this. - // 2. Each update, calculate the cycle time delta for each individual process, and sum these - // deltas to create the system-wide delta. We use this here. - // - // The first method is simpler but has a problem when a process exits and its cycle time is no - // longer counted in the system-wide total. This may cause the delta to be negative and all - // other calculations to become invalid. Process Explorer simply ignored this fact and treated - // the system-wide delta as unsigned (and therefore huge when negative), leading to all CPU - // usages being displayed as "< 0.01". - // - // The second method is used here, but the adjustments must be done before the main new/modified - // pass. We need take into account new, existing and terminated processes. - - // Create the PID hash set. This contains the process information structures returned by - // PhEnumProcesses, distinct from the process item hash set. Note that we use the - // UniqueProcessKey field as the next node pointer to avoid having to allocate extra memory. - - memset(pidBuckets, 0, sizeof(pidBuckets)); - - process = PH_FIRST_PROCESS(processes); - - do - { - PhTotalProcesses++; - PhTotalThreads += process->NumberOfThreads; - PhTotalHandles += process->HandleCount; - - if (process->UniqueProcessId == SYSTEM_IDLE_PROCESS_ID) - { - process->CycleTime = PhCpuIdleCycleDelta.Value; - process->KernelTime = PhCpuTotals.IdleTime; - } - - bucketIndex = PROCESS_ID_TO_BUCKET_INDEX(process->UniqueProcessId); - process->UniqueProcessKey = (ULONG_PTR)pidBuckets[bucketIndex]; - pidBuckets[bucketIndex] = process; - - if (isCycleCpuUsageEnabled) - { - PPH_PROCESS_ITEM processItem; - - if ((processItem = PhpLookupProcessItem(process->UniqueProcessId)) && processItem->CreateTime.QuadPart == process->CreateTime.QuadPart) - sysTotalCycleTime += process->CycleTime - processItem->CycleTimeDelta.Value; // existing process - else - sysTotalCycleTime += process->CycleTime; // new process - } - } while (process = PH_NEXT_PROCESS(process)); - - // Add the fake processes to the PID list. - // - // On Windows 7 the two fake processes are merged into "Interrupts" since we can only get cycle - // time information both DPCs and Interrupts combined. - - if (isCycleCpuUsageEnabled) - { - PhInterruptsProcessInformation.KernelTime.QuadPart = PhCpuTotals.DpcTime.QuadPart + PhCpuTotals.InterruptTime.QuadPart; - PhInterruptsProcessInformation.CycleTime = PhCpuSystemCycleDelta.Value; - sysTotalCycleTime += PhCpuSystemCycleDelta.Delta; - } - else - { - PhDpcsProcessInformation.KernelTime = PhCpuTotals.DpcTime; - PhInterruptsProcessInformation.KernelTime = PhCpuTotals.InterruptTime; - } - - // Look for dead processes. - { - PPH_LIST processesToRemove = NULL; - ULONG i; - PPH_HASH_ENTRY entry; - PPH_PROCESS_ITEM processItem; - PSYSTEM_PROCESS_INFORMATION processEntry; - - for (i = 0; i < PH_HASH_SET_SIZE(PhProcessHashSet); i++) - { - for (entry = PhProcessHashSet[i]; entry; entry = entry->Next) - { - processItem = CONTAINING_RECORD(entry, PH_PROCESS_ITEM, HashEntry); - - // Check if the process still exists. Note that we take into account PID re-use by - // checking CreateTime as well. - - if (processItem->ProcessId == DPCS_PROCESS_ID) - { - processEntry = &PhDpcsProcessInformation; - } - else if (processItem->ProcessId == INTERRUPTS_PROCESS_ID) - { - processEntry = &PhInterruptsProcessInformation; - } - else - { - processEntry = pidBuckets[PROCESS_ID_TO_BUCKET_INDEX(processItem->ProcessId)]; - - while (processEntry && processEntry->UniqueProcessId != processItem->ProcessId) - processEntry = (PSYSTEM_PROCESS_INFORMATION)processEntry->UniqueProcessKey; - } - - if (!processEntry || processEntry->CreateTime.QuadPart != processItem->CreateTime.QuadPart) - { - LARGE_INTEGER exitTime; - - processItem->State |= PH_PROCESS_ITEM_REMOVED; - exitTime.QuadPart = 0; - - if (processItem->QueryHandle) - { - KERNEL_USER_TIMES times; - ULONG64 finalCycleTime; - - if (NT_SUCCESS(PhGetProcessTimes(processItem->QueryHandle, ×))) - { - exitTime = times.ExitTime; - } - - if (isCycleCpuUsageEnabled) - { - if (NT_SUCCESS(PhGetProcessCycleTime(processItem->QueryHandle, &finalCycleTime))) - { - // Adjust deltas for the terminated process because this doesn't get - // picked up anywhere else. - // - // Note that if we don't have sufficient access to the process, the - // worst that will happen is that the CPU usages of other processes - // will get inflated. (See above; if we were using the first - // technique, we could get negative deltas, which is much worse.) - sysTotalCycleTime += finalCycleTime - processItem->CycleTimeDelta.Value; - } - } - } - - // If we don't have a valid exit time, use the current time. - if (exitTime.QuadPart == 0) - PhQuerySystemTime(&exitTime); - - processItem->Record->Flags |= PH_PROCESS_RECORD_DEAD; - processItem->Record->ExitTime = exitTime; - - // Raise the process removed event. - // See PhFlushProcessQueryData for why we need to lock here. - PhAcquireQueuedLockExclusive(&processItem->RemoveLock); - PhInvokeCallback(&PhProcessRemovedEvent, processItem); - PhReleaseQueuedLockExclusive(&processItem->RemoveLock); - - if (!processesToRemove) - processesToRemove = PhCreateList(2); - - PhAddItemList(processesToRemove, processItem); - } - } - } - - // Lock only if we have something to do. - if (processesToRemove) - { - PhAcquireQueuedLockExclusive(&PhProcessHashSetLock); - - for (i = 0; i < processesToRemove->Count; i++) - { - PhpRemoveProcessItem((PPH_PROCESS_ITEM)processesToRemove->Items[i]); - } - - PhReleaseQueuedLockExclusive(&PhProcessHashSetLock); - PhDereferenceObject(processesToRemove); - } - } - - // Go through the queued process query data. - PhFlushProcessQueryData(FALSE); - - if (sysTotalTime == 0) - sysTotalTime = -1; // max. value - if (sysTotalCycleTime == 0) - sysTotalCycleTime = -1; - - PhCpuTotalCycleDelta = sysTotalCycleTime; - - // Look for new processes and update existing ones. - process = PH_FIRST_PROCESS(processes); - - while (process) - { - PPH_PROCESS_ITEM processItem; - - processItem = PhpLookupProcessItem(process->UniqueProcessId); - - if (!processItem) - { - PPH_PROCESS_RECORD processRecord; - BOOLEAN isSuspended; - BOOLEAN isPartiallySuspended; - ULONG contextSwitches; - - // Create the process item and fill in basic information. - processItem = PhCreateProcessItem(process->UniqueProcessId); - PhpFillProcessItem(processItem, process); - processItem->SequenceNumber = PhTimeSequenceNumber; - - processRecord = PhpCreateProcessRecord(processItem); - PhpAddProcessRecord(processRecord); - processItem->Record = processRecord; - - // Open a handle to the process for later usage. - // - // Don't try to do this if the process has no threads. On Windows 8.1, processes without - // threads are probably reflected processes which will not terminate if we have a handle - // open. - if (process->NumberOfThreads != 0) - { - PhOpenProcess(&processItem->QueryHandle, PROCESS_QUERY_INFORMATION, processItem->ProcessId); - - if (WINDOWS_HAS_LIMITED_ACCESS && !processItem->QueryHandle) - PhOpenProcess(&processItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, processItem->ProcessId); - } - - PhpGetProcessThreadInformation(process, &isSuspended, &isPartiallySuspended, &contextSwitches); - PhpUpdateDynamicInfoProcessItem(processItem, process); - - // Initialize the deltas. - PhUpdateDelta(&processItem->CpuKernelDelta, process->KernelTime.QuadPart); - PhUpdateDelta(&processItem->CpuUserDelta, process->UserTime.QuadPart); - PhUpdateDelta(&processItem->IoReadDelta, process->ReadTransferCount.QuadPart); - PhUpdateDelta(&processItem->IoWriteDelta, process->WriteTransferCount.QuadPart); - PhUpdateDelta(&processItem->IoOtherDelta, process->OtherTransferCount.QuadPart); - PhUpdateDelta(&processItem->IoReadCountDelta, process->ReadOperationCount.QuadPart); - PhUpdateDelta(&processItem->IoWriteCountDelta, process->WriteOperationCount.QuadPart); - PhUpdateDelta(&processItem->IoOtherCountDelta, process->OtherOperationCount.QuadPart); - PhUpdateDelta(&processItem->ContextSwitchesDelta, contextSwitches); - PhUpdateDelta(&processItem->PageFaultsDelta, process->PageFaultCount); - PhUpdateDelta(&processItem->CycleTimeDelta, process->CycleTime); - PhUpdateDelta(&processItem->PrivateBytesDelta, process->PagefileUsage); - - processItem->IsSuspended = isSuspended; - processItem->IsPartiallySuspended = isPartiallySuspended; - - // If this is the first run of the provider, queue the - // process query tasks. Otherwise, perform stage 1 - // processing now and queue stage 2 processing. - if (runCount > 0) - { - PH_PROCESS_QUERY_S1_DATA data; - - memset(&data, 0, sizeof(PH_PROCESS_QUERY_S1_DATA)); - data.Header.Stage = 1; - data.Header.ProcessItem = processItem; - PhpProcessQueryStage1(&data); - PhpFillProcessItemStage1(&data); - PhSetEvent(&processItem->Stage1Event); - } - else - { - PhpQueueProcessQueryStage1(processItem); - } - - // Add pending service items to the process item. - PhUpdateProcessItemServices(processItem); - - // Add the process item to the hashtable. - PhAcquireQueuedLockExclusive(&PhProcessHashSetLock); - PhpAddProcessItem(processItem); - PhReleaseQueuedLockExclusive(&PhProcessHashSetLock); - - // Raise the process added event. - PhInvokeCallback(&PhProcessAddedEvent, processItem); - processItem->AddedEventSent = TRUE; - - // (Ref: for the process item being in the hashtable.) - // Instead of referencing then dereferencing we simply don't do anything. - // Dereferenced in PhpRemoveProcessItem. - } - else - { - BOOLEAN modified = FALSE; - BOOLEAN isSuspended; - BOOLEAN isPartiallySuspended; - ULONG contextSwitches; - FLOAT newCpuUsage; - FLOAT kernelCpuUsage; - FLOAT userCpuUsage; - - PhpGetProcessThreadInformation(process, &isSuspended, &isPartiallySuspended, &contextSwitches); - PhpUpdateDynamicInfoProcessItem(processItem, process); - - // Update the deltas. - PhUpdateDelta(&processItem->CpuKernelDelta, process->KernelTime.QuadPart); - PhUpdateDelta(&processItem->CpuUserDelta, process->UserTime.QuadPart); - PhUpdateDelta(&processItem->IoReadDelta, process->ReadTransferCount.QuadPart); - PhUpdateDelta(&processItem->IoWriteDelta, process->WriteTransferCount.QuadPart); - PhUpdateDelta(&processItem->IoOtherDelta, process->OtherTransferCount.QuadPart); - PhUpdateDelta(&processItem->IoReadCountDelta, process->ReadOperationCount.QuadPart); - PhUpdateDelta(&processItem->IoWriteCountDelta, process->WriteOperationCount.QuadPart); - PhUpdateDelta(&processItem->IoOtherCountDelta, process->OtherOperationCount.QuadPart); - PhUpdateDelta(&processItem->ContextSwitchesDelta, contextSwitches); - PhUpdateDelta(&processItem->PageFaultsDelta, process->PageFaultCount); - PhUpdateDelta(&processItem->CycleTimeDelta, process->CycleTime); - PhUpdateDelta(&processItem->PrivateBytesDelta, process->PagefileUsage); - - processItem->SequenceNumber++; - PhAddItemCircularBuffer_ULONG64(&processItem->IoReadHistory, processItem->IoReadDelta.Delta); - PhAddItemCircularBuffer_ULONG64(&processItem->IoWriteHistory, processItem->IoWriteDelta.Delta); - PhAddItemCircularBuffer_ULONG64(&processItem->IoOtherHistory, processItem->IoOtherDelta.Delta); - - PhAddItemCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, processItem->VmCounters.PagefileUsage); - //PhAddItemCircularBuffer_SIZE_T(&processItem->WorkingSetHistory, processItem->VmCounters.WorkingSetSize); - - if (InterlockedExchange(&processItem->JustProcessed, 0) != 0) - modified = TRUE; - - if (isCycleCpuUsageEnabled) - { - FLOAT totalDelta; - - newCpuUsage = (FLOAT)processItem->CycleTimeDelta.Delta / sysTotalCycleTime; - - // Calculate the kernel/user CPU usage based on the kernel/user time. If the kernel - // and user deltas are both zero, we'll just have to use an estimate. Currently, we - // split the CPU usage evenly across the kernel and user components, except when the - // total user time is zero, in which case we assign it all to the kernel component. - - totalDelta = (FLOAT)(processItem->CpuKernelDelta.Delta + processItem->CpuUserDelta.Delta); - - if (totalDelta != 0) - { - kernelCpuUsage = newCpuUsage * ((FLOAT)processItem->CpuKernelDelta.Delta / totalDelta); - userCpuUsage = newCpuUsage * ((FLOAT)processItem->CpuUserDelta.Delta / totalDelta); - } - else - { - if (processItem->UserTime.QuadPart != 0) - { - kernelCpuUsage = newCpuUsage / 2; - userCpuUsage = newCpuUsage / 2; - } - else - { - kernelCpuUsage = newCpuUsage; - userCpuUsage = 0; - } - } - } - else - { - kernelCpuUsage = (FLOAT)processItem->CpuKernelDelta.Delta / sysTotalTime; - userCpuUsage = (FLOAT)processItem->CpuUserDelta.Delta / sysTotalTime; - newCpuUsage = kernelCpuUsage + userCpuUsage; - } - - processItem->CpuUsage = newCpuUsage; - processItem->CpuKernelUsage = kernelCpuUsage; - processItem->CpuUserUsage = userCpuUsage; - - PhAddItemCircularBuffer_FLOAT(&processItem->CpuKernelHistory, kernelCpuUsage); - PhAddItemCircularBuffer_FLOAT(&processItem->CpuUserHistory, userCpuUsage); - - // Max. values - - if (processItem->ProcessId != NULL) - { - if (maxCpuValue < newCpuUsage) - { - maxCpuValue = newCpuUsage; - maxCpuProcessItem = processItem; - } - - // I/O for Other is not included because it is too generic. - if (maxIoValue < processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta) - { - maxIoValue = processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta; - maxIoProcessItem = processItem; - } - } - - // Debugged - if (processItem->QueryHandle) - { - BOOLEAN isBeingDebugged; - - if (NT_SUCCESS(PhGetProcessIsBeingDebugged( - processItem->QueryHandle, - &isBeingDebugged - )) && processItem->IsBeingDebugged != isBeingDebugged) - { - processItem->IsBeingDebugged = isBeingDebugged; - modified = TRUE; - } - } - - // Suspended - if (processItem->IsSuspended != isSuspended) - { - processItem->IsSuspended = isSuspended; - modified = TRUE; - } - - processItem->IsPartiallySuspended = isPartiallySuspended; - - // .NET - if (processItem->UpdateIsDotNet) - { - BOOLEAN isDotNet; - - if (NT_SUCCESS(PhGetProcessIsDotNet(processItem->ProcessId, &isDotNet))) - { - processItem->IsDotNet = isDotNet; - modified = TRUE; - } - - processItem->UpdateIsDotNet = FALSE; - } - - // Immersive - if (processItem->QueryHandle && IsImmersiveProcess_I) - { - BOOLEAN isImmersive; - - isImmersive = !!IsImmersiveProcess_I(processItem->QueryHandle); - - if (processItem->IsImmersive != isImmersive) - { - processItem->IsImmersive = isImmersive; - modified = TRUE; - } - } - - if (modified) - { - PhInvokeCallback(&PhProcessModifiedEvent, processItem); - } - - // No reference added by PhpLookupProcessItem. - } - - // Trick ourselves into thinking that the fake processes - // are on the list. - if (process == &PhInterruptsProcessInformation) - { - process = NULL; - } - else if (process == &PhDpcsProcessInformation) - { - process = &PhInterruptsProcessInformation; - } - else - { - process = PH_NEXT_PROCESS(process); - - if (process == NULL) - { - if (isCycleCpuUsageEnabled) - process = &PhInterruptsProcessInformation; - else - process = &PhDpcsProcessInformation; - } - } - } - - if (PhProcessInformation) - PhFree(PhProcessInformation); - - PhProcessInformation = processes; - - if (PhpTsProcesses) - { - WinStationFreeGAPMemory(0, PhpTsProcesses, PhpTsNumberOfProcesses); - PhpTsProcesses = NULL; - } - - PhpFlushSidFullNameCache(); - - // History cannot be updated on the first run because the deltas are invalid. For example, the - // I/O "deltas" will be huge because they are currently the raw accumulated values. - if (runCount != 0) - { - if (isCycleCpuUsageEnabled) - PhpUpdateCpuCycleUsageInformation(sysTotalCycleTime, sysIdleCycleTime); - - PhpUpdateSystemHistory(); - - // Note that we need to add a reference to the records of these processes, to make it - // possible for others to get the name of a max. CPU or I/O process. - - if (maxCpuProcessItem) - { - PhAddItemCircularBuffer_ULONG(&PhMaxCpuHistory, HandleToUlong(maxCpuProcessItem->ProcessId)); -#ifdef PH_RECORD_MAX_USAGE - PhAddItemCircularBuffer_FLOAT(&PhMaxCpuUsageHistory, maxCpuProcessItem->CpuUsage); -#endif - - if (!(maxCpuProcessItem->Record->Flags & PH_PROCESS_RECORD_STAT_REF)) - { - PhReferenceProcessRecord(maxCpuProcessItem->Record); - maxCpuProcessItem->Record->Flags |= PH_PROCESS_RECORD_STAT_REF; - } - } - else - { - PhAddItemCircularBuffer_ULONG(&PhMaxCpuHistory, PtrToUlong(NULL)); -#ifdef PH_RECORD_MAX_USAGE - PhAddItemCircularBuffer_FLOAT(&PhMaxCpuUsageHistory, 0); -#endif - } - - if (maxIoProcessItem) - { - PhAddItemCircularBuffer_ULONG(&PhMaxIoHistory, HandleToUlong(maxIoProcessItem->ProcessId)); -#ifdef PH_RECORD_MAX_USAGE - PhAddItemCircularBuffer_ULONG64(&PhMaxIoReadOtherHistory, - maxIoProcessItem->IoReadDelta.Delta + maxIoProcessItem->IoOtherDelta.Delta); - PhAddItemCircularBuffer_ULONG64(&PhMaxIoWriteHistory, maxIoProcessItem->IoWriteDelta.Delta); -#endif - - if (!(maxIoProcessItem->Record->Flags & PH_PROCESS_RECORD_STAT_REF)) - { - PhReferenceProcessRecord(maxIoProcessItem->Record); - maxIoProcessItem->Record->Flags |= PH_PROCESS_RECORD_STAT_REF; - } - } - else - { - PhAddItemCircularBuffer_ULONG(&PhMaxIoHistory, PtrToUlong(NULL)); -#ifdef PH_RECORD_MAX_USAGE - PhAddItemCircularBuffer_ULONG64(&PhMaxIoReadOtherHistory, 0); - PhAddItemCircularBuffer_ULONG64(&PhMaxIoWriteHistory, 0); -#endif - } - } - - PhInvokeCallback(&PhProcessesUpdatedEvent, NULL); - runCount++; -} - -PPH_PROCESS_RECORD PhpCreateProcessRecord( - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PPH_PROCESS_RECORD processRecord; - - processRecord = PhAllocate(sizeof(PH_PROCESS_RECORD)); - memset(processRecord, 0, sizeof(PH_PROCESS_RECORD)); - - InitializeListHead(&processRecord->ListEntry); - processRecord->RefCount = 1; - - processRecord->ProcessId = ProcessItem->ProcessId; - processRecord->ParentProcessId = ProcessItem->ParentProcessId; - processRecord->SessionId = ProcessItem->SessionId; - processRecord->CreateTime = ProcessItem->CreateTime; - - PhSetReference(&processRecord->ProcessName, ProcessItem->ProcessName); - PhSetReference(&processRecord->FileName, ProcessItem->FileName); - PhSetReference(&processRecord->CommandLine, ProcessItem->CommandLine); - //PhSetReference(&processRecord->UserName, ProcessItem->UserName); - - return processRecord; -} - -PPH_PROCESS_RECORD PhpSearchProcessRecordList( - _In_ PLARGE_INTEGER Time, - _Out_opt_ PULONG Index, - _Out_opt_ PULONG InsertIndex - ) -{ - PPH_PROCESS_RECORD processRecord; - LONG low; - LONG high; - LONG i; - BOOLEAN found = FALSE; - INT comparison; - - if (PhProcessRecordList->Count == 0) - { - if (Index) - *Index = 0; - if (InsertIndex) - *InsertIndex = 0; - - return NULL; - } - - low = 0; - high = PhProcessRecordList->Count - 1; - - // Do a binary search. - do - { - i = (low + high) / 2; - processRecord = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i]; - - comparison = uint64cmp(Time->QuadPart, processRecord->CreateTime.QuadPart); - - if (comparison == 0) - { - found = TRUE; - break; - } - else if (comparison < 0) - { - high = i - 1; - } - else - { - low = i + 1; - } - } while (low <= high); - - if (Index) - *Index = i; - - if (found) - { - return processRecord; - } - else - { - if (InsertIndex) - *InsertIndex = i + (comparison > 0); - - return NULL; - } -} - -VOID PhpAddProcessRecord( - _Inout_ PPH_PROCESS_RECORD ProcessRecord - ) -{ - PPH_PROCESS_RECORD processRecord; - ULONG insertIndex; - - PhAcquireQueuedLockExclusive(&PhProcessRecordListLock); - - processRecord = PhpSearchProcessRecordList(&ProcessRecord->CreateTime, NULL, &insertIndex); - - if (!processRecord) - { - // Insert the process record, keeping the list sorted. - PhInsertItemList(PhProcessRecordList, insertIndex, ProcessRecord); - } - else - { - // Link the process record with the existing one that we found. - InsertTailList(&processRecord->ListEntry, &ProcessRecord->ListEntry); - } - - PhReleaseQueuedLockExclusive(&PhProcessRecordListLock); -} - -VOID PhpRemoveProcessRecord( - _Inout_ PPH_PROCESS_RECORD ProcessRecord - ) -{ - ULONG i; - PPH_PROCESS_RECORD headProcessRecord; - - PhAcquireQueuedLockExclusive(&PhProcessRecordListLock); - - headProcessRecord = PhpSearchProcessRecordList(&ProcessRecord->CreateTime, &i, NULL); - assert(headProcessRecord); - - // Unlink the record from the list. - RemoveEntryList(&ProcessRecord->ListEntry); - - if (ProcessRecord == headProcessRecord) - { - // Remove the slot completely, or choose a new head record. - if (IsListEmpty(&headProcessRecord->ListEntry)) - PhRemoveItemList(PhProcessRecordList, i); - else - PhProcessRecordList->Items[i] = CONTAINING_RECORD(headProcessRecord->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); - } - - PhReleaseQueuedLockExclusive(&PhProcessRecordListLock); -} - -VOID PhReferenceProcessRecord( - _In_ PPH_PROCESS_RECORD ProcessRecord - ) -{ - _InterlockedIncrement(&ProcessRecord->RefCount); -} - -BOOLEAN PhReferenceProcessRecordSafe( - _In_ PPH_PROCESS_RECORD ProcessRecord - ) -{ - return _InterlockedIncrementNoZero(&ProcessRecord->RefCount); -} - -VOID PhReferenceProcessRecordForStatistics( - _In_ PPH_PROCESS_RECORD ProcessRecord - ) -{ - if (!(ProcessRecord->Flags & PH_PROCESS_RECORD_STAT_REF)) - { - PhReferenceProcessRecord(ProcessRecord); - ProcessRecord->Flags |= PH_PROCESS_RECORD_STAT_REF; - } -} - -VOID PhDereferenceProcessRecord( - _In_ PPH_PROCESS_RECORD ProcessRecord - ) -{ - if (_InterlockedDecrement(&ProcessRecord->RefCount) == 0) - { - PhpRemoveProcessRecord(ProcessRecord); - - PhDereferenceObject(ProcessRecord->ProcessName); - if (ProcessRecord->FileName) PhDereferenceObject(ProcessRecord->FileName); - if (ProcessRecord->CommandLine) PhDereferenceObject(ProcessRecord->CommandLine); - /*if (ProcessRecord->UserName) PhDereferenceObject(ProcessRecord->UserName);*/ - PhFree(ProcessRecord); - } -} - -PPH_PROCESS_RECORD PhpFindProcessRecord( - _In_ PPH_PROCESS_RECORD ProcessRecord, - _In_ HANDLE ProcessId - ) -{ - PPH_PROCESS_RECORD startProcessRecord; - - startProcessRecord = ProcessRecord; - - do - { - if (ProcessRecord->ProcessId == ProcessId) - return ProcessRecord; - - ProcessRecord = CONTAINING_RECORD(ProcessRecord->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); - } while (ProcessRecord != startProcessRecord); - - return NULL; -} - -/** - * Finds a process record. - * - * \param ProcessId The ID of the process. - * \param Time A time in which the process was active. - * - * \return The newest record older than \a Time, or NULL - * if the record could not be found. You must call - * PhDereferenceProcessRecord() when you no longer need - * the record. - */ -PPH_PROCESS_RECORD PhFindProcessRecord( - _In_opt_ HANDLE ProcessId, - _In_ PLARGE_INTEGER Time - ) -{ - PPH_PROCESS_RECORD processRecord; - ULONG i; - BOOLEAN found; - - if (PhProcessRecordList->Count == 0) - return NULL; - - PhAcquireQueuedLockShared(&PhProcessRecordListLock); - - processRecord = PhpSearchProcessRecordList(Time, &i, NULL); - - if (!processRecord) - { - // This is expected. Now we search backwards to find the newest matching element older than - // the given time. - - found = FALSE; - - while (TRUE) - { - processRecord = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i]; - - if (processRecord->CreateTime.QuadPart < Time->QuadPart) - { - if (ProcessId) - { - processRecord = PhpFindProcessRecord(processRecord, ProcessId); - - if (processRecord) - found = TRUE; - } - else - { - found = TRUE; - } - - if (found) - break; - } - - if (i == 0) - break; - - i--; - } - } - else - { - if (ProcessId) - { - processRecord = PhpFindProcessRecord(processRecord, ProcessId); - - if (processRecord) - found = TRUE; - else - found = FALSE; - } - else - { - found = TRUE; - } - } - - if (found) - { - // The record might have had its last reference just cleared but it hasn't been removed from - // the list yet. - if (!PhReferenceProcessRecordSafe(processRecord)) - found = FALSE; - } - - PhReleaseQueuedLockShared(&PhProcessRecordListLock); - - if (found) - return processRecord; - else - return NULL; -} - -/** - * Deletes unused process records. - */ -VOID PhPurgeProcessRecords( - VOID - ) -{ - PPH_PROCESS_RECORD processRecord; - PPH_PROCESS_RECORD startProcessRecord; - ULONG i; - LARGE_INTEGER threshold; - PPH_LIST derefList = NULL; - - if (PhProcessRecordList->Count == 0) - return; - - // Get the oldest statistics time. - PhGetStatisticsTime(NULL, PhTimeHistory.Count - 1, &threshold); - - PhAcquireQueuedLockShared(&PhProcessRecordListLock); - - for (i = 0; i < PhProcessRecordList->Count; i++) - { - processRecord = PhProcessRecordList->Items[i]; - startProcessRecord = processRecord; - - do - { - ULONG requiredFlags; - - requiredFlags = PH_PROCESS_RECORD_DEAD | PH_PROCESS_RECORD_STAT_REF; - - if ((processRecord->Flags & requiredFlags) == requiredFlags) - { - // Check if the process exit time is before the oldest statistics time. If so we can - // dereference the process record. - if (processRecord->ExitTime.QuadPart < threshold.QuadPart) - { - // Clear the stat ref bit; this is to make sure we don't try to dereference the - // record twice (e.g. if someone else currently holds a reference to the record - // and it doesn't get removed immediately). - processRecord->Flags &= ~PH_PROCESS_RECORD_STAT_REF; - - if (!derefList) - derefList = PhCreateList(2); - - PhAddItemList(derefList, processRecord); - } - } - - processRecord = CONTAINING_RECORD(processRecord->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); - } while (processRecord != startProcessRecord); - } - - PhReleaseQueuedLockShared(&PhProcessRecordListLock); - - if (derefList) - { - for (i = 0; i < derefList->Count; i++) - { - PhDereferenceProcessRecord(derefList->Items[i]); - } - - PhDereferenceObject(derefList); - } -} - -PPH_PROCESS_ITEM PhReferenceProcessItemForParent( - _In_ HANDLE ParentProcessId, - _In_ HANDLE ProcessId, - _In_ PLARGE_INTEGER CreateTime - ) -{ - PPH_PROCESS_ITEM processItem; - - if (ParentProcessId == ProcessId) // for cases where the parent PID = PID (e.g. System Idle Process) - return NULL; - - PhAcquireQueuedLockShared(&PhProcessHashSetLock); - - processItem = PhpLookupProcessItem(ParentProcessId); - - // We make sure that the process item we found is actually the parent process - its start time - // must not be larger than the supplied time. - if (processItem && processItem->CreateTime.QuadPart <= CreateTime->QuadPart) - PhReferenceObject(processItem); - else - processItem = NULL; - - PhReleaseQueuedLockShared(&PhProcessHashSetLock); - - return processItem; -} - -PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( - _In_ PPH_PROCESS_RECORD Record - ) -{ - PPH_PROCESS_ITEM processItem; - - PhAcquireQueuedLockShared(&PhProcessHashSetLock); - - processItem = PhpLookupProcessItem(Record->ProcessId); - - if (processItem && processItem->CreateTime.QuadPart == Record->CreateTime.QuadPart) - PhReferenceObject(processItem); - else - processItem = NULL; - - PhReleaseQueuedLockShared(&PhProcessHashSetLock); - - return processItem; -} +/* + * Process Hacker - + * process provider + * + * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This provider module handles the collection of process information and system-wide statistics. A + * list of all running processes is kept and periodically scanned to detect new and terminated + * processes. + * + * The retrieval of certain information is delayed in order to improve performance. This includes + * things such as file icons, version information, digital signature verification, and packed + * executable detection. These requests are handed to worker threads which then post back + * information to a S-list. + * + * Also contained in this module is the storage of process records, which contain static information + * about processes. Unlike process items which are removed as soon as their corresponding process + * exits, process records remain as long as they are needed by the statistics system, and more + * specifically the max. CPU and I/O history buffers. In PH 1.x, a new formatted string was created + * at each update containing information about the maximum-usage process within that interval. Here + * we use a much more storage-efficient method, where the raw maximum-usage PIDs are stored for each + * interval, and the process record list is searched when the name of a process is needed. + * + * The process record list is stored as a list of records sorted by process creation time. If two or + * more processes have the same creation time, they are added to a doubly-linked list. This + * structure allows for fast searching in the typical scenario where we know the PID of a process + * and a specific time in which the process was running. In this case a binary search is used and + * then the list is traversed backwards until the process is found. Binary search is similarly used + * for insertion and removal. + * + * On Windows 7 and above, CPU usage can be calculated from cycle time. However, cycle time cannot + * be split into kernel/user components, and cycle time is not available for DPCs and Interrupts + * separately (only a "system" cycle time). + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PROCESS_ID_BUCKETS 64 +#define PROCESS_ID_TO_BUCKET_INDEX(ProcessId) ((HandleToUlong(ProcessId) / 4) & (PROCESS_ID_BUCKETS - 1)) + +typedef struct _PH_PROCESS_QUERY_DATA +{ + SLIST_ENTRY ListEntry; + ULONG Stage; + PPH_PROCESS_ITEM ProcessItem; +} PH_PROCESS_QUERY_DATA, *PPH_PROCESS_QUERY_DATA; + +typedef struct _PH_PROCESS_QUERY_S1_DATA +{ + PH_PROCESS_QUERY_DATA Header; + + PPH_STRING CommandLine; + + HICON SmallIcon; + HICON LargeIcon; + PH_IMAGE_VERSION_INFO VersionInfo; + + HANDLE ConsoleHostProcessId; + PPH_STRING PackageFullName; + PPH_STRING UserName; + + union + { + ULONG Flags; + struct + { + ULONG IsDotNet : 1; + ULONG Spare1 : 1; + ULONG IsInJob : 1; + ULONG IsInSignificantJob : 1; + ULONG IsBeingDebugged : 1; + ULONG IsImmersive : 1; + ULONG IsFilteredHandle : 1; + ULONG Spare : 25; + }; + }; +} PH_PROCESS_QUERY_S1_DATA, *PPH_PROCESS_QUERY_S1_DATA; + +typedef struct _PH_PROCESS_QUERY_S2_DATA +{ + PH_PROCESS_QUERY_DATA Header; + + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; + + BOOLEAN IsPacked; + ULONG ImportFunctions; + ULONG ImportModules; + PH_IMAGE_VERSION_INFO VersionInfo; // LXSS only +} PH_PROCESS_QUERY_S2_DATA, *PPH_PROCESS_QUERY_S2_DATA; + +typedef struct _PH_SID_FULL_NAME_CACHE_ENTRY +{ + PSID Sid; + PPH_STRING FullName; +} PH_SID_FULL_NAME_CACHE_ENTRY, *PPH_SID_FULL_NAME_CACHE_ENTRY; + +VOID NTAPI PhpProcessItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID PhpQueueProcessQueryStage1( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +VOID PhpQueueProcessQueryStage2( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +PPH_PROCESS_RECORD PhpCreateProcessRecord( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +VOID PhpAddProcessRecord( + _Inout_ PPH_PROCESS_RECORD ProcessRecord + ); + +VOID PhpRemoveProcessRecord( + _Inout_ PPH_PROCESS_RECORD ProcessRecord + ); + +PPH_OBJECT_TYPE PhProcessItemType = NULL; + +PPH_HASH_ENTRY PhProcessHashSet[256] = PH_HASH_SET_INIT; +ULONG PhProcessHashSetCount = 0; +PH_QUEUED_LOCK PhProcessHashSetLock = PH_QUEUED_LOCK_INIT; + +SLIST_HEADER PhProcessQueryDataListHead; + +PPH_LIST PhProcessRecordList = NULL; +PH_QUEUED_LOCK PhProcessRecordListLock = PH_QUEUED_LOCK_INIT; + +ULONG PhStatisticsSampleCount = 512; +BOOLEAN PhEnablePurgeProcessRecords = TRUE; +BOOLEAN PhEnableCycleCpuUsage = TRUE; + +PVOID PhProcessInformation = NULL; // only can be used if running on same thread as process provider +SYSTEM_PERFORMANCE_INFORMATION PhPerfInformation; +PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuInformation = NULL; +SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuTotals; +ULONG PhTotalProcesses = 0; +ULONG PhTotalThreads = 0; +ULONG PhTotalHandles = 0; + +PSYSTEM_PROCESS_INFORMATION PhDpcsProcessInformation = NULL; +PSYSTEM_PROCESS_INFORMATION PhInterruptsProcessInformation = NULL; + +ULONG64 PhCpuTotalCycleDelta = 0; // real cycle time delta for this period +PLARGE_INTEGER PhCpuIdleCycleTime = NULL; // cycle time for Idle +PLARGE_INTEGER PhCpuSystemCycleTime = NULL; // cycle time for DPCs and Interrupts +PH_UINT64_DELTA PhCpuIdleCycleDelta; +PH_UINT64_DELTA PhCpuSystemCycleDelta; +//PPH_UINT64_DELTA PhCpusIdleCycleDelta; + +FLOAT PhCpuKernelUsage = 0.0f; +FLOAT PhCpuUserUsage = 0.0f; +PFLOAT PhCpusKernelUsage = NULL; +PFLOAT PhCpusUserUsage = NULL; + +PH_UINT64_DELTA PhCpuKernelDelta; +PH_UINT64_DELTA PhCpuUserDelta; +PH_UINT64_DELTA PhCpuIdleDelta; + +PPH_UINT64_DELTA PhCpusKernelDelta = NULL; +PPH_UINT64_DELTA PhCpusUserDelta = NULL; +PPH_UINT64_DELTA PhCpusIdleDelta = NULL; + +PH_UINT64_DELTA PhIoReadDelta; +PH_UINT64_DELTA PhIoWriteDelta; +PH_UINT64_DELTA PhIoOtherDelta; + +static BOOLEAN PhProcessStatisticsInitialized = FALSE; +static ULONG PhTimeSequenceNumber = 0; +static PH_CIRCULAR_BUFFER_ULONG PhTimeHistory; + +PH_CIRCULAR_BUFFER_FLOAT PhCpuKernelHistory; +PH_CIRCULAR_BUFFER_FLOAT PhCpuUserHistory; +//PH_CIRCULAR_BUFFER_FLOAT PhCpuOtherHistory; + +PPH_CIRCULAR_BUFFER_FLOAT PhCpusKernelHistory; +PPH_CIRCULAR_BUFFER_FLOAT PhCpusUserHistory; +//PPH_CIRCULAR_BUFFER_FLOAT PhCpusOtherHistory; + +PH_CIRCULAR_BUFFER_ULONG64 PhIoReadHistory; +PH_CIRCULAR_BUFFER_ULONG64 PhIoWriteHistory; +PH_CIRCULAR_BUFFER_ULONG64 PhIoOtherHistory; + +PH_CIRCULAR_BUFFER_ULONG PhCommitHistory; +PH_CIRCULAR_BUFFER_ULONG PhPhysicalHistory; + +PH_CIRCULAR_BUFFER_ULONG PhMaxCpuHistory; // ID of max. CPU process +PH_CIRCULAR_BUFFER_ULONG PhMaxIoHistory; // ID of max. I/O process +#ifdef PH_RECORD_MAX_USAGE +PH_CIRCULAR_BUFFER_FLOAT PhMaxCpuUsageHistory; +PH_CIRCULAR_BUFFER_ULONG64 PhMaxIoReadOtherHistory; +PH_CIRCULAR_BUFFER_ULONG64 PhMaxIoWriteHistory; +#endif + +static PPH_HASHTABLE PhpSidFullNameCacheHashtable = NULL; + +BOOLEAN PhProcessProviderInitialization( + VOID + ) +{ + PFLOAT usageBuffer; + PPH_UINT64_DELTA deltaBuffer; + PPH_CIRCULAR_BUFFER_FLOAT historyBuffer; + + PhProcessItemType = PhCreateObjectType(L"ProcessItem", 0, PhpProcessItemDeleteProcedure); + + RtlInitializeSListHead(&PhProcessQueryDataListHead); + + PhProcessRecordList = PhCreateList(40); + + PhDpcsProcessInformation = PhAllocateZero(sizeof(SYSTEM_PROCESS_INFORMATION) + sizeof(SYSTEM_PROCESS_INFORMATION_EXTENSION)); + RtlInitUnicodeString(&PhDpcsProcessInformation->ImageName, L"DPCs"); + PhDpcsProcessInformation->UniqueProcessId = DPCS_PROCESS_ID; + PhDpcsProcessInformation->InheritedFromUniqueProcessId = SYSTEM_IDLE_PROCESS_ID; + + PhInterruptsProcessInformation = PhAllocateZero(sizeof(SYSTEM_PROCESS_INFORMATION) + sizeof(SYSTEM_PROCESS_INFORMATION_EXTENSION)); + RtlInitUnicodeString(&PhInterruptsProcessInformation->ImageName, L"Interrupts"); + PhInterruptsProcessInformation->UniqueProcessId = INTERRUPTS_PROCESS_ID; + PhInterruptsProcessInformation->InheritedFromUniqueProcessId = SYSTEM_IDLE_PROCESS_ID; + + PhCpuInformation = PhAllocate( + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors + ); + + PhCpuIdleCycleTime = PhAllocate( + sizeof(LARGE_INTEGER) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors + ); + PhCpuSystemCycleTime = PhAllocate( + sizeof(LARGE_INTEGER) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors + ); + + usageBuffer = PhAllocate( + sizeof(FLOAT) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors * + 2 + ); + deltaBuffer = PhAllocate( + sizeof(PH_UINT64_DELTA) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors * + 3 // 4 for PhCpusIdleCycleDelta + ); + historyBuffer = PhAllocate( + sizeof(PH_CIRCULAR_BUFFER_FLOAT) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors * + 2 + ); + + PhCpusKernelUsage = usageBuffer; + PhCpusUserUsage = PhCpusKernelUsage + (ULONG)PhSystemBasicInformation.NumberOfProcessors; + + PhCpusKernelDelta = deltaBuffer; + PhCpusUserDelta = PhCpusKernelDelta + (ULONG)PhSystemBasicInformation.NumberOfProcessors; + PhCpusIdleDelta = PhCpusUserDelta + (ULONG)PhSystemBasicInformation.NumberOfProcessors; + //PhCpusIdleCycleDelta = PhCpusIdleDelta + (ULONG)PhSystemBasicInformation.NumberOfProcessors; + + PhCpusKernelHistory = historyBuffer; + PhCpusUserHistory = PhCpusKernelHistory + (ULONG)PhSystemBasicInformation.NumberOfProcessors; + + memset(deltaBuffer, 0, sizeof(PH_UINT64_DELTA) * (ULONG)PhSystemBasicInformation.NumberOfProcessors); + + return TRUE; +} + +PPH_STRING PhGetClientIdName( + _In_ PCLIENT_ID ClientId + ) +{ + PPH_STRING name; + PPH_PROCESS_ITEM processItem; + + processItem = PhReferenceProcessItem(ClientId->UniqueProcess); + + if (processItem) + { + name = PhGetClientIdNameEx(ClientId, processItem->ProcessName); + PhDereferenceObject(processItem); + } + else + { + // HACK + // Workaround race condition with newly created process handles showing as 'Non-existent process' -dmex + name = PhStdGetClientIdName(ClientId); + } + + return name; +} + +PPH_STRING PhGetClientIdNameEx( + _In_ PCLIENT_ID ClientId, + _In_opt_ PPH_STRING ProcessName + ) +{ + PPH_STRING name; + PH_FORMAT format[5]; + + if (ClientId->UniqueThread) + { + if (ProcessName) + { + PhInitFormatSR(&format[0], ProcessName->sr); + PhInitFormatS(&format[1], L" ("); + PhInitFormatIU(&format[2], (ULONG_PTR)ClientId->UniqueProcess); + PhInitFormatS(&format[3], L"): "); + PhInitFormatIU(&format[4], (ULONG_PTR)ClientId->UniqueThread); + + name = PhFormat(format, 5, ProcessName->Length + 16 * sizeof(WCHAR)); + } + else + { + PhInitFormatS(&format[0], L"Non-existent process ("); + PhInitFormatIU(&format[1], (ULONG_PTR)ClientId->UniqueProcess); + PhInitFormatS(&format[2], L"): "); + PhInitFormatIU(&format[3], (ULONG_PTR)ClientId->UniqueThread); + + name = PhFormat(format, 4, 0); + } + } + else + { + if (ProcessName) + { + PhInitFormatSR(&format[0], ProcessName->sr); + PhInitFormatS(&format[1], L" ("); + PhInitFormatIU(&format[2], (ULONG_PTR)ClientId->UniqueProcess); + PhInitFormatC(&format[3], ')'); + + name = PhFormat(format, 4, 0); + } + else + { + PhInitFormatS(&format[0], L"Non-existent process ("); + PhInitFormatIU(&format[1], (ULONG_PTR)ClientId->UniqueProcess); + PhInitFormatC(&format[2], ')'); + + name = PhFormat(format, 3, 0); + } + } + + return name; +} + +PWSTR PhGetProcessPriorityClassString( + _In_ ULONG PriorityClass + ) +{ + switch (PriorityClass) + { + case PROCESS_PRIORITY_CLASS_REALTIME: + return L"Real time"; + case PROCESS_PRIORITY_CLASS_HIGH: + return L"High"; + case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: + return L"Above normal"; + case PROCESS_PRIORITY_CLASS_NORMAL: + return L"Normal"; + case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: + return L"Below normal"; + case PROCESS_PRIORITY_CLASS_IDLE: + return L"Idle"; + default: + return L"Unknown"; + } +} + +/** + * Creates a process item. + */ +PPH_PROCESS_ITEM PhCreateProcessItem( + _In_ HANDLE ProcessId + ) +{ + PPH_PROCESS_ITEM processItem; + + processItem = PhCreateObject( + PhEmGetObjectSize(EmProcessItemType, sizeof(PH_PROCESS_ITEM)), + PhProcessItemType + ); + memset(processItem, 0, sizeof(PH_PROCESS_ITEM)); + PhInitializeEvent(&processItem->Stage1Event); + PhInitializeQueuedLock(&processItem->ServiceListLock); + + processItem->ProcessId = ProcessId; + + // Create the statistics buffers. + PhInitializeCircularBuffer_FLOAT(&processItem->CpuKernelHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_FLOAT(&processItem->CpuUserHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&processItem->IoReadHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&processItem->IoWriteHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&processItem->IoOtherHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, PhStatisticsSampleCount); + //PhInitializeCircularBuffer_SIZE_T(&processItem->WorkingSetHistory, PhStatisticsSampleCount); + + PhEmCallObjectOperation(EmProcessItemType, processItem, EmObjectCreate); + + return processItem; +} + +VOID PhpProcessItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Object; + ULONG i; + + PhEmCallObjectOperation(EmProcessItemType, processItem, EmObjectDelete); + + PhDeleteCircularBuffer_FLOAT(&processItem->CpuKernelHistory); + PhDeleteCircularBuffer_FLOAT(&processItem->CpuUserHistory); + PhDeleteCircularBuffer_ULONG64(&processItem->IoReadHistory); + PhDeleteCircularBuffer_ULONG64(&processItem->IoWriteHistory); + PhDeleteCircularBuffer_ULONG64(&processItem->IoOtherHistory); + PhDeleteCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory); + //PhDeleteCircularBuffer_SIZE_T(&processItem->WorkingSetHistory); + + if (processItem->ServiceList) + { + PPH_SERVICE_ITEM serviceItem; + + i = 0; + + while (PhEnumPointerList(processItem->ServiceList, &i, &serviceItem)) + PhDereferenceObject(serviceItem); + + PhDereferenceObject(processItem->ServiceList); + } + + if (processItem->ProcessName) PhDereferenceObject(processItem->ProcessName); + if (processItem->FileName) PhDereferenceObject(processItem->FileName); + if (processItem->CommandLine) PhDereferenceObject(processItem->CommandLine); + if (processItem->SmallIcon) DestroyIcon(processItem->SmallIcon); + if (processItem->LargeIcon) DestroyIcon(processItem->LargeIcon); + PhDeleteImageVersionInfo(&processItem->VersionInfo); + if (processItem->Sid) PhFree(processItem->Sid); + if (processItem->VerifySignerName) PhDereferenceObject(processItem->VerifySignerName); + if (processItem->PackageFullName) PhDereferenceObject(processItem->PackageFullName); + if (processItem->UserName) PhDereferenceObject(processItem->UserName); + + if (processItem->QueryHandle) NtClose(processItem->QueryHandle); + + if (processItem->Record) PhDereferenceProcessRecord(processItem->Record); +} + +FORCEINLINE BOOLEAN PhCompareProcessItem( + _In_ PPH_PROCESS_ITEM Value1, + _In_ PPH_PROCESS_ITEM Value2 + ) +{ + return Value1->ProcessId == Value2->ProcessId; +} + +FORCEINLINE ULONG PhHashProcessItem( + _In_ PPH_PROCESS_ITEM Value + ) +{ + return HandleToUlong(Value->ProcessId) / 4; +} + +/** + * Finds a process item in the hash set. + * + * \param ProcessId The process ID of the process item. + * + * \remarks The hash set must be locked before calling this function. The reference count of the + * found process item is not incremented. + */ +PPH_PROCESS_ITEM PhpLookupProcessItem( + _In_ HANDLE ProcessId + ) +{ + PH_PROCESS_ITEM lookupProcessItem; + PPH_HASH_ENTRY entry; + PPH_PROCESS_ITEM processItem; + + lookupProcessItem.ProcessId = ProcessId; + entry = PhFindEntryHashSet( + PhProcessHashSet, + PH_HASH_SET_SIZE(PhProcessHashSet), + PhHashProcessItem(&lookupProcessItem) + ); + + for (; entry; entry = entry->Next) + { + processItem = CONTAINING_RECORD(entry, PH_PROCESS_ITEM, HashEntry); + + if (PhCompareProcessItem(&lookupProcessItem, processItem)) + return processItem; + } + + return NULL; +} + +/** + * Finds and references a process item. + * + * \param ProcessId The process ID of the process item. + * + * \return The found process item. + */ +PPH_PROCESS_ITEM PhReferenceProcessItem( + _In_ HANDLE ProcessId + ) +{ + PPH_PROCESS_ITEM processItem; + + PhAcquireQueuedLockShared(&PhProcessHashSetLock); + + processItem = PhpLookupProcessItem(ProcessId); + + if (processItem) + PhReferenceObject(processItem); + + PhReleaseQueuedLockShared(&PhProcessHashSetLock); + + return processItem; +} + +/** + * Enumerates the process items. + * + * \param ProcessItems A variable which receives an array of pointers to process items. You must + * free the buffer with PhFree() when you no longer need it. + * \param NumberOfProcessItems A variable which receives the number of process items returned in + * \a ProcessItems. + */ +VOID PhEnumProcessItems( + _Out_opt_ PPH_PROCESS_ITEM **ProcessItems, + _Out_ PULONG NumberOfProcessItems + ) +{ + PPH_PROCESS_ITEM *processItems; + ULONG numberOfProcessItems; + ULONG count = 0; + ULONG i; + PPH_HASH_ENTRY entry; + PPH_PROCESS_ITEM processItem; + + if (!ProcessItems) + { + *NumberOfProcessItems = PhProcessHashSetCount; + return; + } + + PhAcquireQueuedLockShared(&PhProcessHashSetLock); + + numberOfProcessItems = PhProcessHashSetCount; + processItems = PhAllocate(sizeof(PPH_PROCESS_ITEM) * numberOfProcessItems); + + for (i = 0; i < PH_HASH_SET_SIZE(PhProcessHashSet); i++) + { + for (entry = PhProcessHashSet[i]; entry; entry = entry->Next) + { + processItem = CONTAINING_RECORD(entry, PH_PROCESS_ITEM, HashEntry); + PhReferenceObject(processItem); + processItems[count++] = processItem; + } + } + + PhReleaseQueuedLockShared(&PhProcessHashSetLock); + + *ProcessItems = processItems; + *NumberOfProcessItems = numberOfProcessItems; +} + +VOID PhpAddProcessItem( + _In_ _Assume_refs_(1) PPH_PROCESS_ITEM ProcessItem + ) +{ + PhAddEntryHashSet( + PhProcessHashSet, + PH_HASH_SET_SIZE(PhProcessHashSet), + &ProcessItem->HashEntry, + PhHashProcessItem(ProcessItem) + ); + PhProcessHashSetCount++; +} + +VOID PhpRemoveProcessItem( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PhRemoveEntryHashSet(PhProcessHashSet, PH_HASH_SET_SIZE(PhProcessHashSet), &ProcessItem->HashEntry); + PhProcessHashSetCount--; + PhDereferenceObject(ProcessItem); +} + +BOOLEAN PhpSidFullNameCacheHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_SID_FULL_NAME_CACHE_ENTRY entry1 = Entry1; + PPH_SID_FULL_NAME_CACHE_ENTRY entry2 = Entry2; + + return RtlEqualSid(entry1->Sid, entry2->Sid); +} + +ULONG PhpSidFullNameCacheHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_SID_FULL_NAME_CACHE_ENTRY entry = Entry; + + return PhHashBytes(entry->Sid, RtlLengthSid(entry->Sid)); +} + +PPH_STRING PhpGetSidFullNameCachedSlow( + _In_ PSID Sid + ) +{ + PPH_STRING fullName; + PH_SID_FULL_NAME_CACHE_ENTRY newEntry; + + if (PhpSidFullNameCacheHashtable) + { + PPH_SID_FULL_NAME_CACHE_ENTRY entry; + PH_SID_FULL_NAME_CACHE_ENTRY lookupEntry; + + lookupEntry.Sid = Sid; + entry = PhFindEntryHashtable(PhpSidFullNameCacheHashtable, &lookupEntry); + + if (entry) + return PhReferenceObject(entry->FullName); + } + + fullName = PhGetSidFullName(Sid, TRUE, NULL); + + if (!fullName) + return NULL; + + if (!PhpSidFullNameCacheHashtable) + { + PhpSidFullNameCacheHashtable = PhCreateHashtable( + sizeof(PH_SID_FULL_NAME_CACHE_ENTRY), + PhpSidFullNameCacheHashtableEqualFunction, + PhpSidFullNameCacheHashtableHashFunction, + 16 + ); + } + + newEntry.Sid = PhAllocateCopy(Sid, RtlLengthSid(Sid)); + newEntry.FullName = PhReferenceObject(fullName); + PhAddEntryHashtable(PhpSidFullNameCacheHashtable, &newEntry); + + return fullName; +} + +PPH_STRING PhpGetSidFullNameCached( + _In_ PSID Sid + ) +{ + //if (!PhpSidFullNameCacheHashtable) + //{ + // PhpSidFullNameCacheHashtable = PhCreateHashtable( + // sizeof(PH_SID_FULL_NAME_CACHE_ENTRY), + // PhpSidFullNameCacheHashtableEqualFunction, + // PhpSidFullNameCacheHashtableHashFunction, + // 16 + // ); + // // HACK pre-cache local SIDs (dmex) + // PhpGetSidFullNameCachedSlow(&PhSeLocalSystemSid); + // PhpGetSidFullNameCachedSlow(&PhSeLocalServiceSid); + // PhpGetSidFullNameCachedSlow(&PhSeNetworkServiceSid); + //} + + if (PhpSidFullNameCacheHashtable) + { + PPH_SID_FULL_NAME_CACHE_ENTRY entry; + PH_SID_FULL_NAME_CACHE_ENTRY lookupEntry; + + lookupEntry.Sid = Sid; + entry = PhFindEntryHashtable(PhpSidFullNameCacheHashtable, &lookupEntry); + + if (entry) + return PhReferenceObject(entry->FullName); + } + + return NULL; +} + +VOID PhpFlushSidFullNameCache( + VOID + ) +{ + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_SID_FULL_NAME_CACHE_ENTRY entry; + + if (!PhpSidFullNameCacheHashtable) + return; + + PhBeginEnumHashtable(PhpSidFullNameCacheHashtable, &enumContext); + + while (entry = PhNextEnumHashtable(&enumContext)) + { + PhFree(entry->Sid); + PhDereferenceObject(entry->FullName); + } + + PhClearReference(&PhpSidFullNameCacheHashtable); +} + +VOID PhpProcessQueryStage1( + _Inout_ PPH_PROCESS_QUERY_S1_DATA Data + ) +{ + NTSTATUS status; + PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; + HANDLE processId = processItem->ProcessId; + HANDLE processHandleLimited = processItem->QueryHandle; + + if (processItem->FileName && !processItem->IsSubsystemProcess) + { + if (!PhExtractIcon( + processItem->FileName->Buffer, + &Data->LargeIcon, + &Data->SmallIcon + )) + { + Data->LargeIcon = NULL; + Data->SmallIcon = NULL; + } + + // Version info. + PhInitializeImageVersionInfoCached(&Data->VersionInfo, processItem->FileName, FALSE); + } + + // Command line, .NET + if (processHandleLimited && !processItem->IsSubsystemProcess) + { + BOOLEAN isDotNet = FALSE; + HANDLE processHandle = NULL; + ULONG processQueryFlags = 0; + + if (WindowsVersion >= WINDOWS_8_1) + { + processHandle = processHandleLimited; + processQueryFlags |= PH_CLR_USE_SECTION_CHECK; + status = STATUS_SUCCESS; + } + else + { + status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + processId + ); + } + + if (NT_SUCCESS(status)) + { + PPH_STRING commandLine; + + if (NT_SUCCESS(PhGetProcessCommandLine(processHandle, &commandLine))) + { + // Some command lines (e.g. from taskeng.exe) have nulls in them. Since Windows + // can't display them, we'll replace them with spaces. + for (ULONG i = 0; i < (ULONG)commandLine->Length / sizeof(WCHAR); i++) + { + if (commandLine->Buffer[i] == UNICODE_NULL) + commandLine->Buffer[i] = ' '; + } + + Data->CommandLine = commandLine; + } + } + + if (NT_SUCCESS(status)) + { + PhGetProcessIsDotNetEx( + processId, + processHandle, +#ifdef _WIN64 + processQueryFlags | PH_CLR_NO_WOW64_CHECK | (processItem->IsWow64 ? PH_CLR_KNOWN_IS_WOW64 : 0), +#else + processQueryFlags, +#endif + &isDotNet, + NULL + ); + Data->IsDotNet = isDotNet; + } + + if (!(processQueryFlags & PH_CLR_USE_SECTION_CHECK) && processHandle) + NtClose(processHandle); + } + + // Job + if (processHandleLimited) + { + if (KphIsConnected()) + { + HANDLE jobHandle = NULL; + + status = KphOpenProcessJob( + processHandleLimited, + JOB_OBJECT_QUERY, + &jobHandle + ); + + if (NT_SUCCESS(status) && status != STATUS_PROCESS_NOT_IN_JOB) + { + JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimits; + + Data->IsInJob = TRUE; + + // Process Explorer only recognizes processes as being in jobs if they don't have + // the silent-breakaway-OK limit as their only limit. Emulate this behaviour. + if (NT_SUCCESS(PhGetJobBasicLimits(jobHandle, &basicLimits))) + { + Data->IsInSignificantJob = + basicLimits.LimitFlags != JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; + } + } + + if (jobHandle) + NtClose(jobHandle); + } + else + { + // KProcessHacker is not available. We can determine if the process is in a job, but we + // can't get a handle to the job. + + status = NtIsProcessInJob(processHandleLimited, NULL); + + if (NT_SUCCESS(status)) + Data->IsInJob = status == STATUS_PROCESS_IN_JOB; + } + } + + // Console host process + if (processHandleLimited) + { + PhGetProcessConsoleHostProcessId(processHandleLimited, &Data->ConsoleHostProcessId); + } + + // Immersive + if (processHandleLimited && WINDOWS_HAS_IMMERSIVE && IsImmersiveProcess && !processItem->IsSubsystemProcess) + { + Data->IsImmersive = !!IsImmersiveProcess(processHandleLimited); + } + + // Package full name + if (processHandleLimited && WINDOWS_HAS_IMMERSIVE && Data->IsImmersive) + { + Data->PackageFullName = PhGetProcessPackageFullName(processHandleLimited); + } + + if (processHandleLimited && processItem->IsHandleValid) + { + OBJECT_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetHandleInformationEx( + NtCurrentProcess(), + processHandleLimited, + ULONG_MAX, + 0, + NULL, + &basicInfo, + NULL, + NULL, + NULL, + NULL + ))) + { + if ((basicInfo.GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION) + Data->IsFilteredHandle = TRUE; + } + else + { + Data->IsFilteredHandle = TRUE; + } + } + + // Debugged + if (processHandleLimited && !processItem->IsSubsystemProcess && !Data->IsFilteredHandle) // Don't query the debug object if the handle was filtered (dmex) + { + BOOLEAN isBeingDebugged; + + if (NT_SUCCESS(PhGetProcessIsBeingDebugged(processHandleLimited, &isBeingDebugged))) + { + Data->IsBeingDebugged = isBeingDebugged; + } + } + + if (processItem->Sid) + { + // Note: We delay resolving the SID name because the local LSA cache might still be + // initializing for users on domain networks with slow links (e.g. VPNs). This can block + // for a very long time depending on server/network conditions. (dmex) + // TODO: This might need to be moved to Stage2... + PhMoveReference(&Data->UserName, PhpGetSidFullNameCachedSlow(processItem->Sid)); + } +} + +VOID PhpProcessQueryStage2( + _Inout_ PPH_PROCESS_QUERY_S2_DATA Data + ) +{ + PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; + + if (PhEnableProcessQueryStage2 && processItem->FileName && !processItem->IsSubsystemProcess) + { + NTSTATUS status; + + Data->VerifyResult = PhVerifyFileCached( + processItem->FileName, + processItem->PackageFullName, + &Data->VerifySignerName, + FALSE + ); + + status = PhIsExecutablePacked( + processItem->FileName->Buffer, + &Data->IsPacked, + &Data->ImportModules, + &Data->ImportFunctions + ); + + // If we got an image-related error, the image is packed. + if ( + status == STATUS_INVALID_IMAGE_NOT_MZ || + status == STATUS_INVALID_IMAGE_FORMAT || + status == STATUS_ACCESS_VIOLATION + ) + { + Data->IsPacked = TRUE; + Data->ImportModules = ULONG_MAX; + Data->ImportFunctions = ULONG_MAX; + } + } + + if (PhEnableLinuxSubsystemSupport && processItem->FileName && processItem->IsSubsystemProcess) + { + PhInitializeImageVersionInfoCached(&Data->VersionInfo, processItem->FileName, TRUE); + } +} + +NTSTATUS PhpProcessQueryStage1Worker( + _In_ PVOID Parameter + ) +{ + PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Parameter; + PPH_PROCESS_QUERY_S1_DATA data; + + data = PhAllocateZero(sizeof(PH_PROCESS_QUERY_S1_DATA)); + data->Header.Stage = 1; + data->Header.ProcessItem = processItem; + + PhpProcessQueryStage1(data); + + RtlInterlockedPushEntrySList(&PhProcessQueryDataListHead, &data->Header.ListEntry); + + return STATUS_SUCCESS; +} + +NTSTATUS PhpProcessQueryStage2Worker( + _In_ PVOID Parameter + ) +{ + PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)Parameter; + PPH_PROCESS_QUERY_S2_DATA data; + + data = PhAllocateZero(sizeof(PH_PROCESS_QUERY_S2_DATA)); + data->Header.Stage = 2; + data->Header.ProcessItem = processItem; + + PhpProcessQueryStage2(data); + + RtlInterlockedPushEntrySList(&PhProcessQueryDataListHead, &data->Header.ListEntry); + + return STATUS_SUCCESS; +} + +VOID PhpQueueProcessQueryStage1( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PH_WORK_QUEUE_ENVIRONMENT environment; + + // Ref: dereferenced when the provider update function removes the item from the queue. + PhReferenceObject(ProcessItem); + + PhInitializeWorkQueueEnvironment(&environment); + environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; + + PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpProcessQueryStage1Worker, ProcessItem, NULL, &environment); +} + +VOID PhpQueueProcessQueryStage2( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PH_WORK_QUEUE_ENVIRONMENT environment; + + PhReferenceObject(ProcessItem); + + PhInitializeWorkQueueEnvironment(&environment); + environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; + environment.IoPriority = IoPriorityVeryLow; + environment.PagePriority = MEMORY_PRIORITY_VERY_LOW; + + PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpProcessQueryStage2Worker, ProcessItem, NULL, &environment); +} + +VOID PhpFillProcessItemStage1( + _In_ PPH_PROCESS_QUERY_S1_DATA Data + ) +{ + PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; + + processItem->CommandLine = Data->CommandLine; + processItem->SmallIcon = Data->SmallIcon; + processItem->LargeIcon = Data->LargeIcon; + memcpy(&processItem->VersionInfo, &Data->VersionInfo, sizeof(PH_IMAGE_VERSION_INFO)); + processItem->ConsoleHostProcessId = Data->ConsoleHostProcessId; + processItem->PackageFullName = Data->PackageFullName; + processItem->IsDotNet = Data->IsDotNet; + processItem->IsInJob = Data->IsInJob; + processItem->IsInSignificantJob = Data->IsInSignificantJob; + processItem->IsBeingDebugged = Data->IsBeingDebugged; + processItem->IsImmersive = Data->IsImmersive; + processItem->IsProtectedHandle = Data->IsFilteredHandle; + + PhSwapReference(&processItem->Record->CommandLine, processItem->CommandLine); + + // Note: We might have referenced the cached username so don't overwrite the previous data. (dmex) + if (!processItem->UserName) + processItem->UserName = Data->UserName; + else if (Data->UserName) + PhDereferenceObject(Data->UserName); + + // Note: Queue stage 2 processing after filling stage1 process data. + PhpQueueProcessQueryStage2(processItem); +} + +VOID PhpFillProcessItemStage2( + _In_ PPH_PROCESS_QUERY_S2_DATA Data + ) +{ + PPH_PROCESS_ITEM processItem = Data->Header.ProcessItem; + + processItem->VerifyResult = Data->VerifyResult; + processItem->VerifySignerName = Data->VerifySignerName; + processItem->IsPacked = Data->IsPacked; + processItem->ImportFunctions = Data->ImportFunctions; + processItem->ImportModules = Data->ImportModules; + + // Note: We query Win32 processes in stage1 so don't overwrite the previous data. (dmex) + if (processItem->IsSubsystemProcess) + { + memcpy(&processItem->VersionInfo, &Data->VersionInfo, sizeof(PH_IMAGE_VERSION_INFO)); + } +} + +VOID PhpFillProcessItemExtension( + _Inout_ PPH_PROCESS_ITEM ProcessItem, + _In_ PSYSTEM_PROCESS_INFORMATION Process + ) +{ + PSYSTEM_PROCESS_INFORMATION_EXTENSION processExtension; + + if (WindowsVersion < WINDOWS_10_RS3 || PhIsExecutingInWow64()) + return; + + processExtension = PH_PROCESS_EXTENSION(Process); + + ProcessItem->JobObjectId = processExtension->JobObjectId; + ProcessItem->ProcessSequenceNumber = processExtension->ProcessSequenceNumber; +} + +VOID PhpFillProcessItem( + _Inout_ PPH_PROCESS_ITEM ProcessItem, + _In_ PSYSTEM_PROCESS_INFORMATION Process + ) +{ + ProcessItem->ParentProcessId = Process->InheritedFromUniqueProcessId; + ProcessItem->SessionId = Process->SessionId; + ProcessItem->CreateTime = Process->CreateTime; + + if (ProcessItem->ProcessId != SYSTEM_IDLE_PROCESS_ID) + ProcessItem->ProcessName = PhCreateStringFromUnicodeString(&Process->ImageName); + else + ProcessItem->ProcessName = PhCreateString(SYSTEM_IDLE_PROCESS_NAME); + + if (PH_IS_REAL_PROCESS_ID(ProcessItem->ProcessId)) + { + PhPrintUInt32(ProcessItem->ProcessIdString, HandleToUlong(ProcessItem->ProcessId)); + PhPrintUInt32(ProcessItem->ParentProcessIdString, HandleToUlong(ProcessItem->ParentProcessId)); + PhPrintUInt32(ProcessItem->SessionIdString, ProcessItem->SessionId); + } + + // Open a handle to the process for later usage. + if (PH_IS_REAL_PROCESS_ID(ProcessItem->ProcessId)) + { + if (NT_SUCCESS(PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_INFORMATION, ProcessItem->ProcessId))) + { + ProcessItem->IsHandleValid = TRUE; + } + + if (!ProcessItem->QueryHandle) + { + PhOpenProcess(&ProcessItem->QueryHandle, PROCESS_QUERY_LIMITED_INFORMATION, ProcessItem->ProcessId); + } + } + + // Process flags + if (ProcessItem->QueryHandle) + { + PROCESS_EXTENDED_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetProcessExtendedBasicInformation(ProcessItem->QueryHandle, &basicInfo))) + { + ProcessItem->IsProtectedProcess = basicInfo.IsProtectedProcess; + ProcessItem->IsSecureProcess = basicInfo.IsSecureProcess; + ProcessItem->IsSubsystemProcess = basicInfo.IsSubsystemProcess; + ProcessItem->IsWow64 = basicInfo.IsWow64Process; + ProcessItem->IsWow64Valid = TRUE; + } + } + + // Process information + { + // If we're dealing with System (PID 4), we need to get the + // kernel file name. Otherwise, get the image file name. (wj32) + + if (ProcessItem->ProcessId != SYSTEM_PROCESS_ID) + { + NTSTATUS status = STATUS_UNSUCCESSFUL; + PPH_STRING fileName; + + if (ProcessItem->QueryHandle && !ProcessItem->IsSubsystemProcess) + { + status = PhGetProcessImageFileNameWin32(ProcessItem->QueryHandle, &fileName); + } + + if (!NT_SUCCESS(status)) + { + status = PhGetProcessImageFileNameByProcessId(ProcessItem->ProcessId, &fileName); + } + + if (NT_SUCCESS(status)) + { + ProcessItem->FileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + } + } + else + { + PPH_STRING fileName; + + fileName = PhGetKernelFileName(); + + if (fileName) + { + ProcessItem->FileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + } + } + } + + // Token information + if ( + ProcessItem->QueryHandle && + ProcessItem->ProcessId != SYSTEM_PROCESS_ID // System token can't be opened (dmex) + ) + { + HANDLE tokenHandle; + + if (NT_SUCCESS(PhOpenProcessToken( + ProcessItem->QueryHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + PTOKEN_USER tokenUser; + TOKEN_ELEVATION_TYPE elevationType; + MANDATORY_LEVEL integrityLevel; + PWSTR integrityString; + + // User + if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) + { + ProcessItem->Sid = PhAllocateCopy(tokenUser->User.Sid, RtlLengthSid(tokenUser->User.Sid)); + ProcessItem->UserName = PhpGetSidFullNameCached(tokenUser->User.Sid); + + PhFree(tokenUser); + } + + // Elevation + if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) + { + ProcessItem->ElevationType = elevationType; + ProcessItem->IsElevated = elevationType == TokenElevationTypeFull; + } + + // Integrity + if (NT_SUCCESS(PhGetTokenIntegrityLevel(tokenHandle, &integrityLevel, &integrityString))) + { + ProcessItem->IntegrityLevel = integrityLevel; + ProcessItem->IntegrityString = integrityString; + } + + NtClose(tokenHandle); + } + } + else + { + if (ProcessItem->ProcessId == SYSTEM_IDLE_PROCESS_ID || + ProcessItem->ProcessId == SYSTEM_PROCESS_ID) // System token can't be opened on XP (wj32) + { + ProcessItem->Sid = PhAllocateCopy(&PhSeLocalSystemSid, RtlLengthSid(&PhSeLocalSystemSid)); + ProcessItem->UserName = PhpGetSidFullNameCached(&PhSeLocalSystemSid); + } + } + + // Known Process Type + { + ProcessItem->KnownProcessType = PhGetProcessKnownTypeEx( + ProcessItem->ProcessId, + ProcessItem->FileName + ); + } + + // Protection + if (ProcessItem->QueryHandle) + { + if (WindowsVersion >= WINDOWS_8_1) + { + PS_PROTECTION protection; + + if (NT_SUCCESS(PhGetProcessProtection(ProcessItem->QueryHandle, &protection))) + { + ProcessItem->Protection.Level = protection.Level; + } + } + else + { + // HACK: 'emulate' the PS_PROTECTION info for older OSes. (ge0rdi) + if (ProcessItem->IsProtectedProcess) + ProcessItem->Protection.Type = PsProtectedTypeProtected; + } + } + else + { + // Signalize that we weren't able to get protection info with a special value. + // Note: We use this value to determine if we should show protection information. (ge0rdi) + ProcessItem->Protection.Level = UCHAR_MAX; + } + + // Control Flow Guard + if (WindowsVersion >= WINDOWS_8_1 && ProcessItem->QueryHandle) + { + BOOLEAN cfguardEnabled; + + if (NT_SUCCESS(PhGetProcessIsCFGuardEnabled(ProcessItem->QueryHandle, &cfguardEnabled))) + { + ProcessItem->IsControlFlowGuardEnabled = cfguardEnabled; + } + } + + // On Windows 8.1 and above, processes without threads are reflected processes + // which will not terminate if we have a handle open. (wj32) + if (Process->NumberOfThreads == 0 && ProcessItem->QueryHandle) + { + NtClose(ProcessItem->QueryHandle); + ProcessItem->QueryHandle = NULL; + } +} + +FORCEINLINE VOID PhpUpdateDynamicInfoProcessItem( + _Inout_ PPH_PROCESS_ITEM ProcessItem, + _In_ PSYSTEM_PROCESS_INFORMATION Process + ) +{ + ProcessItem->BasePriority = Process->BasePriority; + + if (ProcessItem->QueryHandle) + { + PROCESS_PRIORITY_CLASS priorityClass; + + if (NT_SUCCESS(PhGetProcessPriority(ProcessItem->QueryHandle, &priorityClass))) + { + ProcessItem->PriorityClass = priorityClass.PriorityClass; + } + } + else + { + ProcessItem->PriorityClass = 0; + } + + ProcessItem->KernelTime = Process->KernelTime; + ProcessItem->UserTime = Process->UserTime; + ProcessItem->NumberOfHandles = Process->HandleCount; + ProcessItem->NumberOfThreads = Process->NumberOfThreads; + ProcessItem->WorkingSetPrivateSize = (SIZE_T)Process->WorkingSetPrivateSize.QuadPart; + ProcessItem->PeakNumberOfThreads = Process->NumberOfThreadsHighWatermark; + ProcessItem->HardFaultCount = Process->HardFaultCount; + + // Update VM and I/O counters. + ProcessItem->VmCounters = *(PVM_COUNTERS_EX)&Process->PeakVirtualSize; + ProcessItem->IoCounters = *(PIO_COUNTERS)&Process->ReadOperationCount; +} + +VOID PhpUpdatePerfInformation( + VOID + ) +{ + NtQuerySystemInformation( + SystemPerformanceInformation, + &PhPerfInformation, + sizeof(SYSTEM_PERFORMANCE_INFORMATION), + NULL + ); + + PhUpdateDelta(&PhIoReadDelta, PhPerfInformation.IoReadTransferCount.QuadPart); + PhUpdateDelta(&PhIoWriteDelta, PhPerfInformation.IoWriteTransferCount.QuadPart); + PhUpdateDelta(&PhIoOtherDelta, PhPerfInformation.IoOtherTransferCount.QuadPart); +} + +VOID PhpUpdateCpuInformation( + _In_ BOOLEAN SetCpuUsage, + _Out_ PULONG64 TotalTime + ) +{ + ULONG i; + ULONG64 totalTime; + + NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + PhCpuInformation, + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, + NULL + ); + + // Zero the CPU totals. + memset(&PhCpuTotals, 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + + for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) + { + PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION cpuInfo = &PhCpuInformation[i]; + + // KernelTime includes IdleTime. + cpuInfo->KernelTime.QuadPart -= cpuInfo->IdleTime.QuadPart; + + PhCpuTotals.DpcTime.QuadPart += cpuInfo->DpcTime.QuadPart; + PhCpuTotals.IdleTime.QuadPart += cpuInfo->IdleTime.QuadPart; + PhCpuTotals.InterruptCount += cpuInfo->InterruptCount; + PhCpuTotals.InterruptTime.QuadPart += cpuInfo->InterruptTime.QuadPart; + PhCpuTotals.KernelTime.QuadPart += cpuInfo->KernelTime.QuadPart; + PhCpuTotals.UserTime.QuadPart += cpuInfo->UserTime.QuadPart; + + PhUpdateDelta(&PhCpusKernelDelta[i], cpuInfo->KernelTime.QuadPart); + PhUpdateDelta(&PhCpusUserDelta[i], cpuInfo->UserTime.QuadPart); + PhUpdateDelta(&PhCpusIdleDelta[i], cpuInfo->IdleTime.QuadPart); + + if (SetCpuUsage) + { + totalTime = PhCpusKernelDelta[i].Delta + PhCpusUserDelta[i].Delta + PhCpusIdleDelta[i].Delta; + + if (totalTime != 0) + { + PhCpusKernelUsage[i] = (FLOAT)PhCpusKernelDelta[i].Delta / totalTime; + PhCpusUserUsage[i] = (FLOAT)PhCpusUserDelta[i].Delta / totalTime; + } + else + { + PhCpusKernelUsage[i] = 0.0f; + PhCpusUserUsage[i] = 0.0f; + } + } + } + + PhUpdateDelta(&PhCpuKernelDelta, PhCpuTotals.KernelTime.QuadPart); + PhUpdateDelta(&PhCpuUserDelta, PhCpuTotals.UserTime.QuadPart); + PhUpdateDelta(&PhCpuIdleDelta, PhCpuTotals.IdleTime.QuadPart); + + totalTime = PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta + PhCpuIdleDelta.Delta; + + if (SetCpuUsage) + { + if (totalTime != 0) + { + PhCpuKernelUsage = (FLOAT)PhCpuKernelDelta.Delta / totalTime; + PhCpuUserUsage = (FLOAT)PhCpuUserDelta.Delta / totalTime; + } + else + { + PhCpuKernelUsage = 0.0f; + PhCpuUserUsage = 0.0f; + } + } + + *TotalTime = totalTime; +} + +VOID PhpUpdateCpuCycleInformation( + _Out_ PULONG64 IdleCycleTime + ) +{ + ULONG i; + ULONG64 total; + + // Idle + + // We need to query this separately because the idle cycle time in SYSTEM_PROCESS_INFORMATION + // doesn't give us data for individual processors. + + NtQuerySystemInformation( + SystemProcessorIdleCycleTimeInformation, + PhCpuIdleCycleTime, + sizeof(LARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, + NULL + ); + + total = 0; + + for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) + { + //PhUpdateDelta(&PhCpusIdleCycleDelta[i], PhCpuIdleCycleTime[i].QuadPart); + total += PhCpuIdleCycleTime[i].QuadPart; + } + + PhUpdateDelta(&PhCpuIdleCycleDelta, total); + *IdleCycleTime = PhCpuIdleCycleDelta.Delta; + + // System + + NtQuerySystemInformation( + SystemProcessorCycleTimeInformation, + PhCpuSystemCycleTime, + sizeof(LARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, + NULL + ); + + total = 0; + + for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) + { + total += PhCpuSystemCycleTime[i].QuadPart; + } + + PhUpdateDelta(&PhCpuSystemCycleDelta, total); +} + +VOID PhpUpdateCpuCycleUsageInformation( + _In_ ULONG64 TotalCycleTime, + _In_ ULONG64 IdleCycleTime + ) +{ + ULONG i; + FLOAT baseCpuUsage; + FLOAT totalTimeDelta; + ULONG64 totalTime; + + // Cycle time is not only lacking for kernel/user components, but also for individual + // processors. We can get the total idle cycle time for individual processors but + // without knowing the total cycle time for individual processors, this information + // is useless. + // + // We'll start by calculating the total CPU usage, then we'll calculate the kernel/user + // components. In the event that the corresponding CPU time deltas are zero, we'll split + // the CPU usage evenly across the kernel/user components. CPU usage for individual + // processors is left untouched, because it's too difficult to provide an estimate. + // + // Let I_1, I_2, ..., I_n be the idle cycle times and T_1, T_2, ..., T_n be the + // total cycle times. Let I'_1, I'_2, ..., I'_n be the idle CPU times and T'_1, T'_2, ..., + // T'_n be the total CPU times. + // We know all I'_n, T'_n and I_n, but we only know sigma(T). The "real" total CPU usage is + // sigma(I)/sigma(T), and the "real" individual CPU usage is I_n/T_n. The problem is that + // we don't know T_n; we only know sigma(T). Hence we need to find values i_1, i_2, ..., i_n + // and t_1, t_2, ..., t_n such that: + // sigma(i)/sigma(t) ~= sigma(I)/sigma(T), and + // i_n/t_n ~= I_n/T_n + // + // Solution 1: Set i_n = I_n and t_n = sigma(T)*T'_n/sigma(T'). Then: + // sigma(i)/sigma(t) = sigma(I)/(sigma(T)*sigma(T')/sigma(T')) = sigma(I)/sigma(T), and + // i_n/t_n = I_n/T'_n*sigma(T')/sigma(T) ~= I_n/T_n since I_n/T'_n ~= I_n/T_n and sigma(T')/sigma(T) ~= 1. + // However, it is not guaranteed that i_n/t_n <= 1, which may lead to CPU usages over 100% being displayed. + // + // Solution 2: Set i_n = I'_n and t_n = T'_n. Then: + // sigma(i)/sigma(t) = sigma(I')/sigma(T') ~= sigma(I)/sigma(T) since I'_n ~= I_n and T'_n ~= T_n. + // i_n/t_n = I'_n/T'_n ~= I_n/T_n as above. + // Not scaling at all is currently the best solution, since it's fast, simple and guarantees that i_n/t_n <= 1. + + baseCpuUsage = 1 - (FLOAT)IdleCycleTime / TotalCycleTime; + totalTimeDelta = (FLOAT)(PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta); + + if (totalTimeDelta != 0) + { + PhCpuKernelUsage = baseCpuUsage * ((FLOAT)PhCpuKernelDelta.Delta / totalTimeDelta); + PhCpuUserUsage = baseCpuUsage * ((FLOAT)PhCpuUserDelta.Delta / totalTimeDelta); + } + else + { + PhCpuKernelUsage = baseCpuUsage / 2; + PhCpuUserUsage = baseCpuUsage / 2; + } + + for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) + { + totalTime = PhCpusKernelDelta[i].Delta + PhCpusUserDelta[i].Delta + PhCpusIdleDelta[i].Delta; + + if (totalTime != 0) + { + PhCpusKernelUsage[i] = (FLOAT)PhCpusKernelDelta[i].Delta / totalTime; + PhCpusUserUsage[i] = (FLOAT)PhCpusUserDelta[i].Delta / totalTime; + } + else + { + PhCpusKernelUsage[i] = 0; + PhCpusUserUsage[i] = 0; + } + } +} + +VOID PhpInitializeProcessStatistics( + VOID + ) +{ + ULONG i; + + PhInitializeCircularBuffer_ULONG(&PhTimeHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_FLOAT(&PhCpuKernelHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_FLOAT(&PhCpuUserHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&PhIoReadHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&PhIoWriteHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&PhIoOtherHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG(&PhCommitHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG(&PhPhysicalHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG(&PhMaxCpuHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG(&PhMaxIoHistory, PhStatisticsSampleCount); +#ifdef PH_RECORD_MAX_USAGE + PhInitializeCircularBuffer_FLOAT(&PhMaxCpuUsageHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&PhMaxIoReadOtherHistory, PhStatisticsSampleCount); + PhInitializeCircularBuffer_ULONG64(&PhMaxIoWriteHistory, PhStatisticsSampleCount); +#endif + + for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) + { + PhInitializeCircularBuffer_FLOAT(&PhCpusKernelHistory[i], PhStatisticsSampleCount); + PhInitializeCircularBuffer_FLOAT(&PhCpusUserHistory[i], PhStatisticsSampleCount); + } +} + +VOID PhpUpdateSystemHistory( + VOID + ) +{ + ULONG i; + LARGE_INTEGER systemTime; + ULONG secondsSince1980; + + // CPU + PhAddItemCircularBuffer_FLOAT(&PhCpuKernelHistory, PhCpuKernelUsage); + PhAddItemCircularBuffer_FLOAT(&PhCpuUserHistory, PhCpuUserUsage); + + // CPUs + for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) + { + PhAddItemCircularBuffer_FLOAT(&PhCpusKernelHistory[i], PhCpusKernelUsage[i]); + PhAddItemCircularBuffer_FLOAT(&PhCpusUserHistory[i], PhCpusUserUsage[i]); + } + + // I/O + PhAddItemCircularBuffer_ULONG64(&PhIoReadHistory, PhIoReadDelta.Delta); + PhAddItemCircularBuffer_ULONG64(&PhIoWriteHistory, PhIoWriteDelta.Delta); + PhAddItemCircularBuffer_ULONG64(&PhIoOtherHistory, PhIoOtherDelta.Delta); + + // Memory + PhAddItemCircularBuffer_ULONG(&PhCommitHistory, PhPerfInformation.CommittedPages); + PhAddItemCircularBuffer_ULONG(&PhPhysicalHistory, + PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages + ); + + // Time + PhQuerySystemTime(&systemTime); + RtlTimeToSecondsSince1980(&systemTime, &secondsSince1980); + PhAddItemCircularBuffer_ULONG(&PhTimeHistory, secondsSince1980); +} + +/** + * Retrieves a time value recorded by the statistics system. + * + * \param ProcessItem A process item to synchronize with, or NULL if no synchronization is + * necessary. + * \param Index The history index. + * \param Time A variable which receives the time at \a Index. + * + * \return TRUE if the function succeeded, otherwise FALSE if \a ProcessItem was specified and + * \a Index is too far into the past for that process item. + */ +BOOLEAN PhGetStatisticsTime( + _In_opt_ PPH_PROCESS_ITEM ProcessItem, + _In_ ULONG Index, + _Out_ PLARGE_INTEGER Time + ) +{ + ULONG secondsSince1980; + ULONG index; + LARGE_INTEGER time; + + if (ProcessItem) + { + // The sequence number is used to synchronize statistics when a process exits, since that + // process' history is not updated anymore. + index = PhTimeSequenceNumber - ProcessItem->TimeSequenceNumber + Index; + + if (index >= PhTimeHistory.Count) + { + // The data point is too far into the past. + return FALSE; + } + } + else + { + // Assume the index is valid. + index = Index; + } + + secondsSince1980 = PhGetItemCircularBuffer_ULONG(&PhTimeHistory, index); + RtlSecondsSince1980ToTime(secondsSince1980, &time); + + *Time = time; + + return TRUE; +} + +PPH_STRING PhGetStatisticsTimeString( + _In_opt_ PPH_PROCESS_ITEM ProcessItem, + _In_ ULONG Index + ) +{ + LARGE_INTEGER time; + SYSTEMTIME systemTime; + + if (PhGetStatisticsTime(ProcessItem, Index, &time)) + { + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + + return PhFormatDateTime(&systemTime); + } + else + { + return PhCreateString(L"Unknown time"); + } +} + +VOID PhFlushProcessQueryData( + VOID + ) +{ + PSLIST_ENTRY entry; + PPH_PROCESS_QUERY_DATA data; + + if (!RtlFirstEntrySList(&PhProcessQueryDataListHead)) + return; + + entry = RtlInterlockedFlushSList(&PhProcessQueryDataListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_PROCESS_QUERY_DATA, ListEntry); + entry = entry->Next; + + if (data->Stage == 1) + { + PhpFillProcessItemStage1((PPH_PROCESS_QUERY_S1_DATA)data); + PhSetEvent(&data->ProcessItem->Stage1Event); + } + else if (data->Stage == 2) + { + PhpFillProcessItemStage2((PPH_PROCESS_QUERY_S2_DATA)data); + } + + data->ProcessItem->JustProcessed = TRUE; + + PhDereferenceObject(data->ProcessItem); + PhFree(data); + } +} + +VOID PhpGetProcessThreadInformation( + _In_ PSYSTEM_PROCESS_INFORMATION Process, + _Out_opt_ PBOOLEAN IsSuspended, + _Out_opt_ PBOOLEAN IsPartiallySuspended, + _Out_opt_ PULONG ContextSwitches + ) +{ + ULONG i; + BOOLEAN isSuspended; + BOOLEAN isPartiallySuspended; + ULONG contextSwitches; + + isSuspended = PH_IS_REAL_PROCESS_ID(Process->UniqueProcessId); + isPartiallySuspended = FALSE; + contextSwitches = 0; + + for (i = 0; i < Process->NumberOfThreads; i++) + { + if (Process->Threads[i].ThreadState != Waiting || + Process->Threads[i].WaitReason != Suspended) + { + isSuspended = FALSE; + } + else + { + isPartiallySuspended = TRUE; + } + + contextSwitches += Process->Threads[i].ContextSwitches; + } + + // HACK: Minimal/Reflected processes don't have threads (TODO: Use PhGetProcessIsSuspended instead). + if (Process->NumberOfThreads == 0) + { + isSuspended = FALSE; + isPartiallySuspended = FALSE; + } + + if (IsSuspended) + *IsSuspended = isSuspended; + if (IsPartiallySuspended) + *IsPartiallySuspended = isPartiallySuspended; + if (ContextSwitches) + *ContextSwitches = contextSwitches; +} + +VOID PhProcessProviderUpdate( + _In_ PVOID Object + ) +{ + static ULONG runCount = 0; + static PSYSTEM_PROCESS_INFORMATION pidBuckets[PROCESS_ID_BUCKETS]; + + // Note about locking: + // + // Since this is the only function that is allowed to modify the process hashtable, locking is + // not needed for shared accesses. However, exclusive accesses need locking. + + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + ULONG bucketIndex; + + ULONG64 sysTotalTime; // total time for this update period + ULONG64 sysTotalCycleTime = 0; // total cycle time for this update period + ULONG64 sysIdleCycleTime = 0; // total idle cycle time for this update period + FLOAT maxCpuValue = 0; + PPH_PROCESS_ITEM maxCpuProcessItem = NULL; + ULONG64 maxIoValue = 0; + PPH_PROCESS_ITEM maxIoProcessItem = NULL; + + // Pre-update tasks + + if (runCount % 512 == 0) // yes, a very long time + { + if (PhEnablePurgeProcessRecords) + PhPurgeProcessRecords(); + + PhpFlushSidFullNameCache(); + + PhFlushImageVersionInfoCache(); + } + + if (!PhProcessStatisticsInitialized) + { + PhpInitializeProcessStatistics(); + PhProcessStatisticsInitialized = TRUE; + } + + PhpUpdatePerfInformation(); + + if (PhEnableCycleCpuUsage) + { + PhpUpdateCpuInformation(FALSE, &sysTotalTime); + PhpUpdateCpuCycleInformation(&sysIdleCycleTime); + } + else + { + PhpUpdateCpuInformation(TRUE, &sysTotalTime); + } + + if (runCount != 0) + { + PhTimeSequenceNumber++; + } + + // Get the process list. + + PhTotalProcesses = 0; + PhTotalThreads = 0; + PhTotalHandles = 0; + + if (!NT_SUCCESS(PhEnumProcesses(&processes))) + return; + + // Notes on cycle-based CPU usage: + // + // Cycle-based CPU usage is a bit tricky to calculate because we cannot get the total number of + // cycles consumed by all processes since system startup - we can only get total number of + // cycles per process. This means there are two ways to calculate the system-wide cycle time + // delta: + // + // 1. Each update, sum the cycle times of all processes, and calculate the system-wide delta + // from this. Process Explorer seems to do this. + // 2. Each update, calculate the cycle time delta for each individual process, and sum these + // deltas to create the system-wide delta. We use this here. + // + // The first method is simpler but has a problem when a process exits and its cycle time is no + // longer counted in the system-wide total. This may cause the delta to be negative and all + // other calculations to become invalid. Process Explorer simply ignored this fact and treated + // the system-wide delta as unsigned (and therefore huge when negative), leading to all CPU + // usages being displayed as "< 0.01". + // + // The second method is used here, but the adjustments must be done before the main new/modified + // pass. We need take into account new, existing and terminated processes. + + // Create the PID hash set. This contains the process information structures returned by + // PhEnumProcesses, distinct from the process item hash set. Note that we use the + // UniqueProcessKey field as the next node pointer to avoid having to allocate extra memory. + + memset(pidBuckets, 0, sizeof(pidBuckets)); + + process = PH_FIRST_PROCESS(processes); + + do + { + PhTotalProcesses++; + PhTotalThreads += process->NumberOfThreads; + PhTotalHandles += process->HandleCount; + + if (process->UniqueProcessId == SYSTEM_IDLE_PROCESS_ID) + { + process->CycleTime = PhCpuIdleCycleDelta.Value; + process->KernelTime = PhCpuTotals.IdleTime; + } + + bucketIndex = PROCESS_ID_TO_BUCKET_INDEX(process->UniqueProcessId); + process->UniqueProcessKey = (ULONG_PTR)pidBuckets[bucketIndex]; + pidBuckets[bucketIndex] = process; + + if (PhEnableCycleCpuUsage) + { + PPH_PROCESS_ITEM processItem; + + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) + { + if ((processItem = PhpLookupProcessItem(process->UniqueProcessId)) && processItem->ProcessSequenceNumber == PH_PROCESS_EXTENSION(process)->ProcessSequenceNumber) + sysTotalCycleTime += process->CycleTime - processItem->CycleTimeDelta.Value; // existing process + else + sysTotalCycleTime += process->CycleTime; // new process + } + else + { + if ((processItem = PhpLookupProcessItem(process->UniqueProcessId)) && processItem->CreateTime.QuadPart == process->CreateTime.QuadPart) + sysTotalCycleTime += process->CycleTime - processItem->CycleTimeDelta.Value; // existing process + else + sysTotalCycleTime += process->CycleTime; // new process + } + } + } while (process = PH_NEXT_PROCESS(process)); + + // Add the fake processes to the PID list. + // + // On Windows 7 the two fake processes are merged into "Interrupts" since we can only get cycle + // time information both DPCs and Interrupts combined. + + if (PhEnableCycleCpuUsage) + { + PhInterruptsProcessInformation->KernelTime.QuadPart = PhCpuTotals.DpcTime.QuadPart + PhCpuTotals.InterruptTime.QuadPart; + PhInterruptsProcessInformation->CycleTime = PhCpuSystemCycleDelta.Value; + sysTotalCycleTime += PhCpuSystemCycleDelta.Delta; + } + else + { + PhDpcsProcessInformation->KernelTime = PhCpuTotals.DpcTime; + PhInterruptsProcessInformation->KernelTime = PhCpuTotals.InterruptTime; + } + + // Look for dead processes. + { + PPH_LIST processesToRemove = NULL; + ULONG i; + PPH_HASH_ENTRY entry; + PPH_PROCESS_ITEM processItem; + PSYSTEM_PROCESS_INFORMATION processEntry; + + for (i = 0; i < PH_HASH_SET_SIZE(PhProcessHashSet); i++) + { + for (entry = PhProcessHashSet[i]; entry; entry = entry->Next) + { + BOOLEAN processRemoved = FALSE; + + processItem = CONTAINING_RECORD(entry, PH_PROCESS_ITEM, HashEntry); + + // Check if the process still exists. Note that we take into account PID re-use by + // checking CreateTime as well. + + if (processItem->ProcessId == DPCS_PROCESS_ID) + { + processEntry = PhDpcsProcessInformation; + } + else if (processItem->ProcessId == INTERRUPTS_PROCESS_ID) + { + processEntry = PhInterruptsProcessInformation; + } + else + { + processEntry = pidBuckets[PROCESS_ID_TO_BUCKET_INDEX(processItem->ProcessId)]; + + while (processEntry && processEntry->UniqueProcessId != processItem->ProcessId) + processEntry = (PSYSTEM_PROCESS_INFORMATION)processEntry->UniqueProcessKey; + } + + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) + { + if (!processEntry || PH_PROCESS_EXTENSION(processEntry)->ProcessSequenceNumber != processItem->ProcessSequenceNumber) + processRemoved = TRUE; + } + else + { + if (!processEntry || processEntry->CreateTime.QuadPart != processItem->CreateTime.QuadPart) + processRemoved = TRUE; + } + + if (processRemoved) + { + LARGE_INTEGER exitTime; + + processItem->State |= PH_PROCESS_ITEM_REMOVED; + exitTime.QuadPart = 0; + + if (processItem->QueryHandle) + { + KERNEL_USER_TIMES times; + ULONG64 finalCycleTime; + + if (NT_SUCCESS(PhGetProcessTimes(processItem->QueryHandle, ×))) + { + exitTime = times.ExitTime; + } + + if (PhEnableCycleCpuUsage) + { + if (NT_SUCCESS(PhGetProcessCycleTime(processItem->QueryHandle, &finalCycleTime))) + { + // Adjust deltas for the terminated process because this doesn't get + // picked up anywhere else. + // + // Note that if we don't have sufficient access to the process, the + // worst that will happen is that the CPU usages of other processes + // will get inflated. (See above; if we were using the first + // technique, we could get negative deltas, which is much worse.) + sysTotalCycleTime += finalCycleTime - processItem->CycleTimeDelta.Value; + } + } + } + + // If we don't have a valid exit time, use the current time. + if (exitTime.QuadPart == 0) + PhQuerySystemTime(&exitTime); + + processItem->Record->Flags |= PH_PROCESS_RECORD_DEAD; + processItem->Record->ExitTime = exitTime; + + // Raise the process removed event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderRemovedEvent), processItem); + + if (!processesToRemove) + processesToRemove = PhCreateList(2); + + PhAddItemList(processesToRemove, processItem); + } + } + } + + // Lock only if we have something to do. + if (processesToRemove) + { + PhAcquireQueuedLockExclusive(&PhProcessHashSetLock); + + for (i = 0; i < processesToRemove->Count; i++) + { + PhpRemoveProcessItem((PPH_PROCESS_ITEM)processesToRemove->Items[i]); + } + + PhReleaseQueuedLockExclusive(&PhProcessHashSetLock); + PhDereferenceObject(processesToRemove); + } + } + + // Go through the queued process query data. + PhFlushProcessQueryData(); + + if (sysTotalTime == 0) + sysTotalTime = -1; // max. value + if (sysTotalCycleTime == 0) + sysTotalCycleTime = -1; + + PhCpuTotalCycleDelta = sysTotalCycleTime; + + // Look for new processes and update existing ones. + process = PH_FIRST_PROCESS(processes); + + while (process) + { + PPH_PROCESS_ITEM processItem; + + processItem = PhpLookupProcessItem(process->UniqueProcessId); + + if (!processItem) + { + PPH_PROCESS_RECORD processRecord; + BOOLEAN isSuspended; + BOOLEAN isPartiallySuspended; + ULONG contextSwitches; + + // Create the process item and fill in basic information. + processItem = PhCreateProcessItem(process->UniqueProcessId); + PhpFillProcessItem(processItem, process); + PhpFillProcessItemExtension(processItem, process); + processItem->TimeSequenceNumber = PhTimeSequenceNumber; + + processRecord = PhpCreateProcessRecord(processItem); + PhpAddProcessRecord(processRecord); + processItem->Record = processRecord; + + PhpGetProcessThreadInformation(process, &isSuspended, &isPartiallySuspended, &contextSwitches); + PhpUpdateDynamicInfoProcessItem(processItem, process); + + // Initialize the deltas. + PhUpdateDelta(&processItem->CpuKernelDelta, process->KernelTime.QuadPart); + PhUpdateDelta(&processItem->CpuUserDelta, process->UserTime.QuadPart); + PhUpdateDelta(&processItem->IoReadDelta, process->ReadTransferCount.QuadPart); + PhUpdateDelta(&processItem->IoWriteDelta, process->WriteTransferCount.QuadPart); + PhUpdateDelta(&processItem->IoOtherDelta, process->OtherTransferCount.QuadPart); + PhUpdateDelta(&processItem->IoReadCountDelta, process->ReadOperationCount.QuadPart); + PhUpdateDelta(&processItem->IoWriteCountDelta, process->WriteOperationCount.QuadPart); + PhUpdateDelta(&processItem->IoOtherCountDelta, process->OtherOperationCount.QuadPart); + PhUpdateDelta(&processItem->ContextSwitchesDelta, contextSwitches); + PhUpdateDelta(&processItem->PageFaultsDelta, process->PageFaultCount); + PhUpdateDelta(&processItem->CycleTimeDelta, process->CycleTime); + PhUpdateDelta(&processItem->PrivateBytesDelta, process->PagefileUsage); + + processItem->IsSuspended = isSuspended; + processItem->IsPartiallySuspended = isPartiallySuspended; + + // If this is the first run of the provider, queue the + // process query tasks. Otherwise, perform stage 1 + // processing now and queue stage 2 processing. + if (runCount > 0) + { + PH_PROCESS_QUERY_S1_DATA data; + + memset(&data, 0, sizeof(PH_PROCESS_QUERY_S1_DATA)); + data.Header.Stage = 1; + data.Header.ProcessItem = processItem; + PhpProcessQueryStage1(&data); + PhpFillProcessItemStage1(&data); + PhSetEvent(&processItem->Stage1Event); + } + else + { + PhpQueueProcessQueryStage1(processItem); + } + + // Add pending service items to the process item. + PhUpdateProcessItemServices(processItem); + + // Add the process item to the hashtable. + PhAcquireQueuedLockExclusive(&PhProcessHashSetLock); + PhpAddProcessItem(processItem); + PhReleaseQueuedLockExclusive(&PhProcessHashSetLock); + + // Raise the process added event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderAddedEvent), processItem); + + // (Ref: for the process item being in the hashtable.) + // Instead of referencing then dereferencing we simply don't do anything. + // Dereferenced in PhpRemoveProcessItem. + } + else + { + BOOLEAN modified = FALSE; + BOOLEAN isSuspended; + BOOLEAN isPartiallySuspended; + ULONG contextSwitches; + FLOAT newCpuUsage; + FLOAT kernelCpuUsage; + FLOAT userCpuUsage; + + PhpGetProcessThreadInformation(process, &isSuspended, &isPartiallySuspended, &contextSwitches); + PhpUpdateDynamicInfoProcessItem(processItem, process); + PhpFillProcessItemExtension(processItem, process); + + // Update the deltas. + PhUpdateDelta(&processItem->CpuKernelDelta, process->KernelTime.QuadPart); + PhUpdateDelta(&processItem->CpuUserDelta, process->UserTime.QuadPart); + PhUpdateDelta(&processItem->IoReadDelta, process->ReadTransferCount.QuadPart); + PhUpdateDelta(&processItem->IoWriteDelta, process->WriteTransferCount.QuadPart); + PhUpdateDelta(&processItem->IoOtherDelta, process->OtherTransferCount.QuadPart); + PhUpdateDelta(&processItem->IoReadCountDelta, process->ReadOperationCount.QuadPart); + PhUpdateDelta(&processItem->IoWriteCountDelta, process->WriteOperationCount.QuadPart); + PhUpdateDelta(&processItem->IoOtherCountDelta, process->OtherOperationCount.QuadPart); + PhUpdateDelta(&processItem->ContextSwitchesDelta, contextSwitches); + PhUpdateDelta(&processItem->PageFaultsDelta, process->PageFaultCount); + PhUpdateDelta(&processItem->CycleTimeDelta, process->CycleTime); + PhUpdateDelta(&processItem->PrivateBytesDelta, process->PagefileUsage); + + processItem->TimeSequenceNumber++; + PhAddItemCircularBuffer_ULONG64(&processItem->IoReadHistory, processItem->IoReadDelta.Delta); + PhAddItemCircularBuffer_ULONG64(&processItem->IoWriteHistory, processItem->IoWriteDelta.Delta); + PhAddItemCircularBuffer_ULONG64(&processItem->IoOtherHistory, processItem->IoOtherDelta.Delta); + + PhAddItemCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, processItem->VmCounters.PagefileUsage); + //PhAddItemCircularBuffer_SIZE_T(&processItem->WorkingSetHistory, processItem->VmCounters.WorkingSetSize); + + if (InterlockedExchange(&processItem->JustProcessed, 0) != 0) + modified = TRUE; + + if (PhEnableCycleCpuUsage) + { + FLOAT totalDelta; + + newCpuUsage = (FLOAT)processItem->CycleTimeDelta.Delta / sysTotalCycleTime; + + // Calculate the kernel/user CPU usage based on the kernel/user time. If the kernel + // and user deltas are both zero, we'll just have to use an estimate. Currently, we + // split the CPU usage evenly across the kernel and user components, except when the + // total user time is zero, in which case we assign it all to the kernel component. + + totalDelta = (FLOAT)(processItem->CpuKernelDelta.Delta + processItem->CpuUserDelta.Delta); + + if (totalDelta != 0) + { + kernelCpuUsage = newCpuUsage * ((FLOAT)processItem->CpuKernelDelta.Delta / totalDelta); + userCpuUsage = newCpuUsage * ((FLOAT)processItem->CpuUserDelta.Delta / totalDelta); + } + else + { + if (processItem->UserTime.QuadPart != 0) + { + kernelCpuUsage = newCpuUsage / 2; + userCpuUsage = newCpuUsage / 2; + } + else + { + kernelCpuUsage = newCpuUsage; + userCpuUsage = 0; + } + } + } + else + { + kernelCpuUsage = (FLOAT)processItem->CpuKernelDelta.Delta / sysTotalTime; + userCpuUsage = (FLOAT)processItem->CpuUserDelta.Delta / sysTotalTime; + newCpuUsage = kernelCpuUsage + userCpuUsage; + } + + processItem->CpuUsage = newCpuUsage; + processItem->CpuKernelUsage = kernelCpuUsage; + processItem->CpuUserUsage = userCpuUsage; + + PhAddItemCircularBuffer_FLOAT(&processItem->CpuKernelHistory, kernelCpuUsage); + PhAddItemCircularBuffer_FLOAT(&processItem->CpuUserHistory, userCpuUsage); + + // Max. values + + if (processItem->ProcessId) + { + if (maxCpuValue < newCpuUsage) + { + maxCpuValue = newCpuUsage; + maxCpuProcessItem = processItem; + } + + // I/O for Other is not included because it is too generic. + if (maxIoValue < processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta) + { + maxIoValue = processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta; + maxIoProcessItem = processItem; + } + } + + // Token information + if ( + processItem->QueryHandle && + processItem->ProcessId != SYSTEM_PROCESS_ID // System token can't be opened (dmex) + ) + { + HANDLE tokenHandle; + + if (NT_SUCCESS(PhOpenProcessToken( + processItem->QueryHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + PTOKEN_USER tokenUser; + TOKEN_ELEVATION_TYPE elevationType; + MANDATORY_LEVEL integrityLevel; + PWSTR integrityString; + + // User + if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) + { + if (!RtlEqualSid(processItem->Sid, tokenUser->User.Sid)) + { + PSID processSid; + + // HACK (dmex) + processSid = processItem->Sid; + processItem->Sid = PhAllocateCopy(tokenUser->User.Sid, RtlLengthSid(tokenUser->User.Sid)); + PhFree(processSid); + + PhMoveReference(&processItem->UserName, PhpGetSidFullNameCachedSlow(processItem->Sid)); + + modified = TRUE; + } + + PhFree(tokenUser); + } + + // Elevation + if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) + { + if (processItem->ElevationType != elevationType) + { + processItem->ElevationType = elevationType; + processItem->IsElevated = elevationType == TokenElevationTypeFull; + modified = TRUE; + } + } + + // Integrity + if (NT_SUCCESS(PhGetTokenIntegrityLevel(tokenHandle, &integrityLevel, &integrityString))) + { + if (processItem->IntegrityLevel != integrityLevel) + { + processItem->IntegrityLevel = integrityLevel; + processItem->IntegrityString = integrityString; + modified = TRUE; + } + } + + NtClose(tokenHandle); + } + } + + // Job + if (processItem->QueryHandle) + { + NTSTATUS status; + BOOLEAN isInSignificantJob = FALSE; + BOOLEAN isInJob = FALSE; + + if (KphIsConnected()) + { + HANDLE jobHandle = NULL; + + status = KphOpenProcessJob( + processItem->QueryHandle, + JOB_OBJECT_QUERY, + &jobHandle + ); + + if (NT_SUCCESS(status) && status != STATUS_PROCESS_NOT_IN_JOB) + { + JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimits; + + isInJob = TRUE; + + if (NT_SUCCESS(PhGetJobBasicLimits(jobHandle, &basicLimits))) + { + isInSignificantJob = basicLimits.LimitFlags != JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; + } + } + + if (jobHandle) + NtClose(jobHandle); + } + else + { + status = NtIsProcessInJob(processItem->QueryHandle, NULL); + + if (NT_SUCCESS(status)) + isInJob = status == STATUS_PROCESS_IN_JOB; + } + + if (processItem->IsInSignificantJob != isInSignificantJob) + { + processItem->IsInSignificantJob = isInSignificantJob; + modified = TRUE; + } + + if (processItem->IsInJob != isInJob) + { + processItem->IsInJob = isInJob; + modified = TRUE; + } + } + + // Debugged + if (processItem->QueryHandle && !processItem->IsSubsystemProcess && !processItem->IsProtectedHandle) + { + BOOLEAN isBeingDebugged = FALSE; + + PhGetProcessIsBeingDebugged(processItem->QueryHandle, &isBeingDebugged); + + if (processItem->IsBeingDebugged != isBeingDebugged) + { + processItem->IsBeingDebugged = isBeingDebugged; + modified = TRUE; + } + } + + // Suspended + if (processItem->IsSuspended != isSuspended) + { + processItem->IsSuspended = isSuspended; + modified = TRUE; + } + + processItem->IsPartiallySuspended = isPartiallySuspended; + + // .NET + if (processItem->UpdateIsDotNet) + { + BOOLEAN isDotNet; + ULONG flags = 0; + + if (NT_SUCCESS(PhGetProcessIsDotNetEx(processItem->ProcessId, NULL, 0, &isDotNet, &flags))) + { + processItem->IsDotNet = isDotNet; + + // This check is needed for the DotNetTools plugin. (dmex) + if (!isDotNet && (flags & PH_CLR_JIT_PRESENT)) + processItem->IsDotNet = TRUE; + + modified = TRUE; + } + + processItem->UpdateIsDotNet = FALSE; + } + + // Immersive + if (processItem->QueryHandle && WINDOWS_HAS_IMMERSIVE && IsImmersiveProcess && !processItem->IsSubsystemProcess) + { + BOOLEAN isImmersive; + + isImmersive = !!IsImmersiveProcess(processItem->QueryHandle); + + if (processItem->IsImmersive != isImmersive) + { + processItem->IsImmersive = isImmersive; + modified = TRUE; + } + } + + if (processItem->QueryHandle && processItem->IsHandleValid) + { + OBJECT_BASIC_INFORMATION basicInfo; + BOOLEAN filteredHandle = FALSE; + + if (NT_SUCCESS(PhGetHandleInformationEx( + NtCurrentProcess(), + processItem->QueryHandle, + ULONG_MAX, + 0, + NULL, + &basicInfo, + NULL, + NULL, + NULL, + NULL + ))) + { + if ((basicInfo.GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION) + { + filteredHandle = TRUE; + } + } + else + { + filteredHandle = TRUE; + } + + if (processItem->IsProtectedHandle != filteredHandle) + { + processItem->IsProtectedHandle = filteredHandle; + modified = TRUE; + } + } + + if (modified) + { + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderModifiedEvent), processItem); + } + + // No reference added by PhpLookupProcessItem. + } + + // Trick ourselves into thinking that the fake processes + // are on the list. + if (process == PhInterruptsProcessInformation) + { + process = NULL; + } + else if (process == PhDpcsProcessInformation) + { + process = PhInterruptsProcessInformation; + } + else + { + process = PH_NEXT_PROCESS(process); + + if (process == NULL) + { + if (PhEnableCycleCpuUsage) + process = PhInterruptsProcessInformation; + else + process = PhDpcsProcessInformation; + } + } + } + + if (PhProcessInformation) + PhFree(PhProcessInformation); + + PhProcessInformation = processes; + + // History cannot be updated on the first run because the deltas are invalid. For example, the + // I/O "deltas" will be huge because they are currently the raw accumulated values. + if (runCount != 0) + { + if (PhEnableCycleCpuUsage) + PhpUpdateCpuCycleUsageInformation(sysTotalCycleTime, sysIdleCycleTime); + + PhpUpdateSystemHistory(); + + // Note that we need to add a reference to the records of these processes, to make it + // possible for others to get the name of a max. CPU or I/O process. + + if (maxCpuProcessItem) + { + PhAddItemCircularBuffer_ULONG(&PhMaxCpuHistory, HandleToUlong(maxCpuProcessItem->ProcessId)); +#ifdef PH_RECORD_MAX_USAGE + PhAddItemCircularBuffer_FLOAT(&PhMaxCpuUsageHistory, maxCpuProcessItem->CpuUsage); +#endif + + if (!(maxCpuProcessItem->Record->Flags & PH_PROCESS_RECORD_STAT_REF)) + { + PhReferenceProcessRecord(maxCpuProcessItem->Record); + maxCpuProcessItem->Record->Flags |= PH_PROCESS_RECORD_STAT_REF; + } + } + else + { + PhAddItemCircularBuffer_ULONG(&PhMaxCpuHistory, PtrToUlong(NULL)); +#ifdef PH_RECORD_MAX_USAGE + PhAddItemCircularBuffer_FLOAT(&PhMaxCpuUsageHistory, 0); +#endif + } + + if (maxIoProcessItem) + { + PhAddItemCircularBuffer_ULONG(&PhMaxIoHistory, HandleToUlong(maxIoProcessItem->ProcessId)); +#ifdef PH_RECORD_MAX_USAGE + PhAddItemCircularBuffer_ULONG64(&PhMaxIoReadOtherHistory, + maxIoProcessItem->IoReadDelta.Delta + maxIoProcessItem->IoOtherDelta.Delta); + PhAddItemCircularBuffer_ULONG64(&PhMaxIoWriteHistory, maxIoProcessItem->IoWriteDelta.Delta); +#endif + + if (!(maxIoProcessItem->Record->Flags & PH_PROCESS_RECORD_STAT_REF)) + { + PhReferenceProcessRecord(maxIoProcessItem->Record); + maxIoProcessItem->Record->Flags |= PH_PROCESS_RECORD_STAT_REF; + } + } + else + { + PhAddItemCircularBuffer_ULONG(&PhMaxIoHistory, PtrToUlong(NULL)); +#ifdef PH_RECORD_MAX_USAGE + PhAddItemCircularBuffer_ULONG64(&PhMaxIoReadOtherHistory, 0); + PhAddItemCircularBuffer_ULONG64(&PhMaxIoWriteHistory, 0); +#endif + } + } + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), NULL); + runCount++; +} + +PPH_PROCESS_RECORD PhpCreateProcessRecord( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PPH_PROCESS_RECORD processRecord; + + processRecord = PhAllocate(sizeof(PH_PROCESS_RECORD)); + memset(processRecord, 0, sizeof(PH_PROCESS_RECORD)); + + InitializeListHead(&processRecord->ListEntry); + processRecord->RefCount = 1; + + processRecord->ProcessId = ProcessItem->ProcessId; + processRecord->ParentProcessId = ProcessItem->ParentProcessId; + processRecord->SessionId = ProcessItem->SessionId; + processRecord->ProcessSequenceNumber = ProcessItem->ProcessSequenceNumber; + processRecord->CreateTime = ProcessItem->CreateTime; + + PhSetReference(&processRecord->ProcessName, ProcessItem->ProcessName); + PhSetReference(&processRecord->FileName, ProcessItem->FileName); + PhSetReference(&processRecord->CommandLine, ProcessItem->CommandLine); + //PhSetReference(&processRecord->UserName, ProcessItem->UserName); + + return processRecord; +} + +PPH_PROCESS_RECORD PhpSearchProcessRecordList( + _In_ PLARGE_INTEGER Time, + _Out_opt_ PULONG Index, + _Out_opt_ PULONG InsertIndex + ) +{ + PPH_PROCESS_RECORD processRecord; + LONG low; + LONG high; + LONG i; + BOOLEAN found = FALSE; + INT comparison; + + if (PhProcessRecordList->Count == 0) + { + if (Index) + *Index = 0; + if (InsertIndex) + *InsertIndex = 0; + + return NULL; + } + + low = 0; + high = PhProcessRecordList->Count - 1; + + // Do a binary search. + do + { + i = (low + high) / 2; + processRecord = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i]; + + comparison = uint64cmp(Time->QuadPart, processRecord->CreateTime.QuadPart); + + if (comparison == 0) + { + found = TRUE; + break; + } + else if (comparison < 0) + { + high = i - 1; + } + else + { + low = i + 1; + } + } while (low <= high); + + if (Index) + *Index = i; + + if (found) + { + return processRecord; + } + else + { + if (InsertIndex) + *InsertIndex = i + (comparison > 0); + + return NULL; + } +} + +VOID PhpAddProcessRecord( + _Inout_ PPH_PROCESS_RECORD ProcessRecord + ) +{ + PPH_PROCESS_RECORD processRecord; + ULONG insertIndex; + + PhAcquireQueuedLockExclusive(&PhProcessRecordListLock); + + processRecord = PhpSearchProcessRecordList(&ProcessRecord->CreateTime, NULL, &insertIndex); + + if (!processRecord) + { + // Insert the process record, keeping the list sorted. + PhInsertItemList(PhProcessRecordList, insertIndex, ProcessRecord); + } + else + { + // Link the process record with the existing one that we found. + InsertTailList(&processRecord->ListEntry, &ProcessRecord->ListEntry); + } + + PhReleaseQueuedLockExclusive(&PhProcessRecordListLock); +} + +VOID PhpRemoveProcessRecord( + _Inout_ PPH_PROCESS_RECORD ProcessRecord + ) +{ + ULONG i; + PPH_PROCESS_RECORD headProcessRecord; + + PhAcquireQueuedLockExclusive(&PhProcessRecordListLock); + + headProcessRecord = PhpSearchProcessRecordList(&ProcessRecord->CreateTime, &i, NULL); + assert(headProcessRecord); + + // Unlink the record from the list. + RemoveEntryList(&ProcessRecord->ListEntry); + + if (ProcessRecord == headProcessRecord) + { + // Remove the slot completely, or choose a new head record. + if (IsListEmpty(&headProcessRecord->ListEntry)) + PhRemoveItemList(PhProcessRecordList, i); + else + PhProcessRecordList->Items[i] = CONTAINING_RECORD(headProcessRecord->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); + } + + PhReleaseQueuedLockExclusive(&PhProcessRecordListLock); +} + +VOID PhReferenceProcessRecord( + _In_ PPH_PROCESS_RECORD ProcessRecord + ) +{ + _InterlockedIncrement(&ProcessRecord->RefCount); +} + +BOOLEAN PhReferenceProcessRecordSafe( + _In_ PPH_PROCESS_RECORD ProcessRecord + ) +{ + return _InterlockedIncrementNoZero(&ProcessRecord->RefCount); +} + +VOID PhReferenceProcessRecordForStatistics( + _In_ PPH_PROCESS_RECORD ProcessRecord + ) +{ + if (!(ProcessRecord->Flags & PH_PROCESS_RECORD_STAT_REF)) + { + PhReferenceProcessRecord(ProcessRecord); + ProcessRecord->Flags |= PH_PROCESS_RECORD_STAT_REF; + } +} + +VOID PhDereferenceProcessRecord( + _In_ PPH_PROCESS_RECORD ProcessRecord + ) +{ + if (_InterlockedDecrement(&ProcessRecord->RefCount) == 0) + { + PhpRemoveProcessRecord(ProcessRecord); + + PhDereferenceObject(ProcessRecord->ProcessName); + if (ProcessRecord->FileName) PhDereferenceObject(ProcessRecord->FileName); + if (ProcessRecord->CommandLine) PhDereferenceObject(ProcessRecord->CommandLine); + /*if (ProcessRecord->UserName) PhDereferenceObject(ProcessRecord->UserName);*/ + PhFree(ProcessRecord); + } +} + +PPH_PROCESS_RECORD PhpFindProcessRecord( + _In_ PPH_PROCESS_RECORD ProcessRecord, + _In_ HANDLE ProcessId + ) +{ + PPH_PROCESS_RECORD startProcessRecord; + + startProcessRecord = ProcessRecord; + + do + { + if (ProcessRecord->ProcessId == ProcessId) + return ProcessRecord; + + ProcessRecord = CONTAINING_RECORD(ProcessRecord->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); + } while (ProcessRecord != startProcessRecord); + + return NULL; +} + +/** + * Finds a process record. + * + * \param ProcessId The ID of the process. + * \param Time A time in which the process was active. + * + * \return The newest record older than \a Time, or NULL + * if the record could not be found. You must call + * PhDereferenceProcessRecord() when you no longer need + * the record. + */ +PPH_PROCESS_RECORD PhFindProcessRecord( + _In_opt_ HANDLE ProcessId, + _In_ PLARGE_INTEGER Time + ) +{ + PPH_PROCESS_RECORD processRecord; + ULONG i; + BOOLEAN found; + + if (PhProcessRecordList->Count == 0) + return NULL; + + PhAcquireQueuedLockShared(&PhProcessRecordListLock); + + processRecord = PhpSearchProcessRecordList(Time, &i, NULL); + + if (!processRecord) + { + // This is expected. Now we search backwards to find the newest matching element older than + // the given time. + + found = FALSE; + + while (TRUE) + { + processRecord = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i]; + + if (processRecord->CreateTime.QuadPart < Time->QuadPart) + { + if (ProcessId) + { + processRecord = PhpFindProcessRecord(processRecord, ProcessId); + + if (processRecord) + found = TRUE; + } + else + { + found = TRUE; + } + + if (found) + break; + } + + if (i == 0) + break; + + i--; + } + } + else + { + if (ProcessId) + { + processRecord = PhpFindProcessRecord(processRecord, ProcessId); + + if (processRecord) + found = TRUE; + else + found = FALSE; + } + else + { + found = TRUE; + } + } + + if (found) + { + // The record might have had its last reference just cleared but it hasn't been removed from + // the list yet. + if (!PhReferenceProcessRecordSafe(processRecord)) + found = FALSE; + } + + PhReleaseQueuedLockShared(&PhProcessRecordListLock); + + if (found) + return processRecord; + else + return NULL; +} + +/** + * Deletes unused process records. + */ +VOID PhPurgeProcessRecords( + VOID + ) +{ + PPH_PROCESS_RECORD processRecord; + PPH_PROCESS_RECORD startProcessRecord; + ULONG i; + LARGE_INTEGER threshold; + PPH_LIST derefList = NULL; + + if (PhProcessRecordList->Count == 0) + return; + + // Get the oldest statistics time. + PhGetStatisticsTime(NULL, PhTimeHistory.Count - 1, &threshold); + + PhAcquireQueuedLockShared(&PhProcessRecordListLock); + + for (i = 0; i < PhProcessRecordList->Count; i++) + { + processRecord = PhProcessRecordList->Items[i]; + startProcessRecord = processRecord; + + do + { + ULONG requiredFlags; + + requiredFlags = PH_PROCESS_RECORD_DEAD | PH_PROCESS_RECORD_STAT_REF; + + if ((processRecord->Flags & requiredFlags) == requiredFlags) + { + // Check if the process exit time is before the oldest statistics time. If so we can + // dereference the process record. + if (processRecord->ExitTime.QuadPart < threshold.QuadPart) + { + // Clear the stat ref bit; this is to make sure we don't try to dereference the + // record twice (e.g. if someone else currently holds a reference to the record + // and it doesn't get removed immediately). + processRecord->Flags &= ~PH_PROCESS_RECORD_STAT_REF; + + if (!derefList) + derefList = PhCreateList(2); + + PhAddItemList(derefList, processRecord); + } + } + + processRecord = CONTAINING_RECORD(processRecord->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry); + } while (processRecord != startProcessRecord); + } + + PhReleaseQueuedLockShared(&PhProcessRecordListLock); + + if (derefList) + { + for (i = 0; i < derefList->Count; i++) + { + PhDereferenceProcessRecord(derefList->Items[i]); + } + + PhDereferenceObject(derefList); + } +} + +PPH_PROCESS_ITEM PhReferenceProcessItemForParent( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PPH_PROCESS_ITEM parentProcessItem; + + if (ProcessItem->ParentProcessId == ProcessItem->ProcessId) // for cases where the parent PID = PID (e.g. System Idle Process) + return NULL; + + PhAcquireQueuedLockShared(&PhProcessHashSetLock); + + parentProcessItem = PhpLookupProcessItem(ProcessItem->ParentProcessId); + + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) + { + // We make sure that the process item we found is actually the parent process - its sequence number + // must not be higher than the supplied sequence. + if (parentProcessItem && parentProcessItem->ProcessSequenceNumber <= ProcessItem->ProcessSequenceNumber) + PhReferenceObject(parentProcessItem); + else + parentProcessItem = NULL; + } + else + { + // We make sure that the process item we found is actually the parent process - its start time + // must not be larger than the supplied time. + if (parentProcessItem && parentProcessItem->CreateTime.QuadPart <= ProcessItem->CreateTime.QuadPart) + PhReferenceObject(parentProcessItem); + else + parentProcessItem = NULL; + } + + PhReleaseQueuedLockShared(&PhProcessHashSetLock); + + return parentProcessItem; +} + +PPH_PROCESS_ITEM PhReferenceProcessItemForRecord( + _In_ PPH_PROCESS_RECORD Record + ) +{ + PPH_PROCESS_ITEM processItem; + + PhAcquireQueuedLockShared(&PhProcessHashSetLock); + + processItem = PhpLookupProcessItem(Record->ProcessId); + + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) + { + if (processItem && processItem->ProcessSequenceNumber == Record->ProcessSequenceNumber) + PhReferenceObject(processItem); + else + processItem = NULL; + } + else + { + if (processItem && processItem->CreateTime.QuadPart == Record->CreateTime.QuadPart) + PhReferenceObject(processItem); + else + processItem = NULL; + } + + PhReleaseQueuedLockShared(&PhProcessHashSetLock); + + return processItem; +} + diff --git a/ProcessHacker/procrec.c b/ProcessHacker/procrec.c index 77d407ef28c9..4a2a2cc093db 100644 --- a/ProcessHacker/procrec.c +++ b/ProcessHacker/procrec.c @@ -1,262 +1,280 @@ -/* - * Process Hacker - - * process record properties - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -typedef struct _PROCESS_RECORD_CONTEXT -{ - PPH_PROCESS_RECORD Record; - HICON FileIcon; -} PROCESS_RECORD_CONTEXT, *PPROCESS_RECORD_CONTEXT; - -INT_PTR CALLBACK PhpProcessRecordDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowProcessRecordDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_RECORD Record - ) -{ - PROCESS_RECORD_CONTEXT context; - - context.Record = Record; - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PROCRECORD), - ParentWindowHandle, - PhpProcessRecordDlgProc, - (LPARAM)&context - ); -} - -PPH_STRING PhpaGetRelativeTimeString( - _In_ PLARGE_INTEGER Time - ) -{ - LARGE_INTEGER time; - LARGE_INTEGER currentTime; - SYSTEMTIME timeFields; - PPH_STRING timeRelativeString; - PPH_STRING timeString; - - time = *Time; - PhQuerySystemTime(¤tTime); - timeRelativeString = PH_AUTO(PhFormatTimeSpanRelative(currentTime.QuadPart - time.QuadPart)); - - PhLargeIntegerToLocalSystemTime(&timeFields, &time); - timeString = PhaFormatDateTime(&timeFields); - - return PhaFormatString(L"%s ago (%s)", timeRelativeString->Buffer, timeString->Buffer); -} - -FORCEINLINE PWSTR PhpGetStringOrNa( - _In_ PPH_STRING String - ) -{ - if (String) - return String->Buffer; - else - return L"N/A"; -} - -INT_PTR CALLBACK PhpProcessRecordDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPROCESS_RECORD_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PPROCESS_RECORD_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - } - else - { - context = (PPROCESS_RECORD_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (uMsg == WM_DESTROY) - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PH_IMAGE_VERSION_INFO versionInfo; - BOOLEAN versionInfoInitialized; - PPH_STRING processNameString; - PPH_PROCESS_ITEM processItem; - - if (!PH_IS_FAKE_PROCESS_ID(context->Record->ProcessId)) - { - processNameString = PhaFormatString(L"%s (%u)", - context->Record->ProcessName->Buffer, HandleToUlong(context->Record->ProcessId)); - } - else - { - processNameString = context->Record->ProcessName; - } - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); - SetWindowText(hwndDlg, processNameString->Buffer); - - SetDlgItemText(hwndDlg, IDC_PROCESSNAME, processNameString->Buffer); - - if (processItem = PhReferenceProcessItemForRecord(context->Record)) - { - PPH_PROCESS_ITEM parentProcess; - - if (parentProcess = PhReferenceProcessItemForParent( - processItem->ParentProcessId, - processItem->ProcessId, - &processItem->CreateTime - )) - { - CLIENT_ID clientId; - - clientId.UniqueProcess = parentProcess->ProcessId; - clientId.UniqueThread = NULL; - - SetDlgItemText(hwndDlg, IDC_PARENT, - PH_AUTO_T(PH_STRING, PhGetClientIdNameEx(&clientId, parentProcess->ProcessName))->Buffer); - - PhDereferenceObject(parentProcess); - } - else - { - SetDlgItemText(hwndDlg, IDC_PARENT, PhaFormatString(L"Non-existent process (%u)", - HandleToUlong(context->Record->ParentProcessId))->Buffer); - } - - PhDereferenceObject(processItem); - } - else - { - SetDlgItemText(hwndDlg, IDC_PARENT, PhaFormatString(L"Unknown process (%u)", - HandleToUlong(context->Record->ParentProcessId))->Buffer); - - EnableWindow(GetDlgItem(hwndDlg, IDC_PROPERTIES), FALSE); - } - - memset(&versionInfo, 0, sizeof(PH_IMAGE_VERSION_INFO)); - versionInfoInitialized = FALSE; - - if (context->Record->FileName) - { - if (PhInitializeImageVersionInfo(&versionInfo, context->Record->FileName->Buffer)) - versionInfoInitialized = TRUE; - } - - context->FileIcon = PhGetFileShellIcon(PhGetString(context->Record->FileName), L".exe", TRUE); - - SendMessage(GetDlgItem(hwndDlg, IDC_OPENFILENAME), BM_SETIMAGE, IMAGE_ICON, - (LPARAM)PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER))); - SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, - (WPARAM)context->FileIcon, 0); - - SetDlgItemText(hwndDlg, IDC_NAME, PhpGetStringOrNa(versionInfo.FileDescription)); - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, PhpGetStringOrNa(versionInfo.CompanyName)); - SetDlgItemText(hwndDlg, IDC_VERSION, PhpGetStringOrNa(versionInfo.FileVersion)); - SetDlgItemText(hwndDlg, IDC_FILENAME, PhpGetStringOrNa(context->Record->FileName)); - - if (versionInfoInitialized) - PhDeleteImageVersionInfo(&versionInfo); - - if (!context->Record->FileName) - EnableWindow(GetDlgItem(hwndDlg, IDC_OPENFILENAME), FALSE); - - SetDlgItemText(hwndDlg, IDC_CMDLINE, PhpGetStringOrNa(context->Record->CommandLine)); - - if (context->Record->CreateTime.QuadPart != 0) - SetDlgItemText(hwndDlg, IDC_STARTED, PhpaGetRelativeTimeString(&context->Record->CreateTime)->Buffer); - else - SetDlgItemText(hwndDlg, IDC_STARTED, L"N/A"); - - if (context->Record->ExitTime.QuadPart != 0) - SetDlgItemText(hwndDlg, IDC_TERMINATED, PhpaGetRelativeTimeString(&context->Record->ExitTime)->Buffer); - else - SetDlgItemText(hwndDlg, IDC_TERMINATED, L"N/A"); - - SetDlgItemInt(hwndDlg, IDC_SESSIONID, context->Record->SessionId, FALSE); - } - break; - case WM_DESTROY: - { - if (context->FileIcon) - DestroyIcon(context->FileIcon); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - { - EndDialog(hwndDlg, IDOK); - } - break; - case IDC_OPENFILENAME: - { - if (context->Record->FileName) - PhShellExploreFile(hwndDlg, context->Record->FileName->Buffer); - } - break; - case IDC_PROPERTIES: - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItemForRecord(context->Record)) - { - ProcessHacker_ShowProcessProperties(PhMainWndHandle, processItem); - PhDereferenceObject(processItem); - } - else - { - PhShowError(hwndDlg, L"The process has already terminated; only the process record is available."); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * process record properties + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include + +typedef struct _PROCESS_RECORD_CONTEXT +{ + PPH_PROCESS_RECORD Record; + HICON FileIcon; +} PROCESS_RECORD_CONTEXT, *PPROCESS_RECORD_CONTEXT; + +INT_PTR CALLBACK PhpProcessRecordDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowProcessRecordDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_RECORD Record + ) +{ + PROCESS_RECORD_CONTEXT context; + + context.Record = Record; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_PROCRECORD), + ParentWindowHandle, + PhpProcessRecordDlgProc, + (LPARAM)&context + ); +} + +PPH_STRING PhpaGetRelativeTimeString( + _In_ PLARGE_INTEGER Time + ) +{ + LARGE_INTEGER time; + LARGE_INTEGER currentTime; + SYSTEMTIME timeFields; + PPH_STRING timeRelativeString; + PPH_STRING timeString; + + time = *Time; + PhQuerySystemTime(¤tTime); + timeRelativeString = PH_AUTO(PhFormatTimeSpanRelative(currentTime.QuadPart - time.QuadPart)); + + PhLargeIntegerToLocalSystemTime(&timeFields, &time); + timeString = PhaFormatDateTime(&timeFields); + + return PhaFormatString(L"%s ago (%s)", timeRelativeString->Buffer, timeString->Buffer); +} + +FORCEINLINE PWSTR PhpGetStringOrNa( + _In_ _Maybenull_ PPH_STRING String + ) +{ + if (String) + return String->Buffer; + else + return L"N/A"; +} + +INT_PTR CALLBACK PhpProcessRecordDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPROCESS_RECORD_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PPROCESS_RECORD_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PH_IMAGE_VERSION_INFO versionInfo; + BOOLEAN versionInfoInitialized; + PPH_STRING processNameString; + PPH_PROCESS_ITEM processItem; + + if (!PH_IS_FAKE_PROCESS_ID(context->Record->ProcessId)) + { + processNameString = PhaFormatString(L"%s (%u)", + context->Record->ProcessName->Buffer, HandleToUlong(context->Record->ProcessId)); + } + else + { + processNameString = context->Record->ProcessName; + } + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + PhSetWindowText(hwndDlg, processNameString->Buffer); + + PhSetDialogItemText(hwndDlg, IDC_PROCESSNAME, processNameString->Buffer); + + if (processItem = PhReferenceProcessItemForRecord(context->Record)) + { + PPH_PROCESS_ITEM parentProcess; + + if (parentProcess = PhReferenceProcessItemForParent(processItem)) + { + CLIENT_ID clientId; + + clientId.UniqueProcess = parentProcess->ProcessId; + clientId.UniqueThread = NULL; + + PhSetDialogItemText(hwndDlg, IDC_PARENT, + PH_AUTO_T(PH_STRING, PhGetClientIdNameEx(&clientId, parentProcess->ProcessName))->Buffer); + + PhDereferenceObject(parentProcess); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_PARENT, PhaFormatString(L"Non-existent process (%u)", + HandleToUlong(context->Record->ParentProcessId))->Buffer); + } + + PhDereferenceObject(processItem); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_PARENT, PhaFormatString(L"Unknown process (%u)", + HandleToUlong(context->Record->ParentProcessId))->Buffer); + + EnableWindow(GetDlgItem(hwndDlg, IDC_PROPERTIES), FALSE); + } + + memset(&versionInfo, 0, sizeof(PH_IMAGE_VERSION_INFO)); + versionInfoInitialized = FALSE; + + if (context->Record->FileName) + { + PhExtractIcon( + context->Record->FileName->Buffer, + &context->FileIcon, + NULL + ); + + if (PhInitializeImageVersionInfo(&versionInfo, context->Record->FileName->Buffer)) + versionInfoInitialized = TRUE; + } + + if (context->FileIcon) + { + SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)context->FileIcon, 0); + } + else + { + HICON largeIcon; + + PhGetStockApplicationIcon(NULL, &largeIcon); + SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)largeIcon, 0); + } + + SendMessage(GetDlgItem(hwndDlg, IDC_OPENFILENAME), BM_SETIMAGE, IMAGE_ICON, + (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_FOLDER))); + + PhSetDialogItemText(hwndDlg, IDC_NAME, PhpGetStringOrNa(versionInfo.FileDescription)); + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, PhpGetStringOrNa(versionInfo.CompanyName)); + PhSetDialogItemText(hwndDlg, IDC_VERSION, PhpGetStringOrNa(versionInfo.FileVersion)); + PhSetDialogItemText(hwndDlg, IDC_FILENAME, PhpGetStringOrNa(context->Record->FileName)); + + if (versionInfoInitialized) + PhDeleteImageVersionInfo(&versionInfo); + + if (!context->Record->FileName) + EnableWindow(GetDlgItem(hwndDlg, IDC_OPENFILENAME), FALSE); + + PhSetDialogItemText(hwndDlg, IDC_CMDLINE, PhpGetStringOrNa(context->Record->CommandLine)); + + if (context->Record->CreateTime.QuadPart != 0) + PhSetDialogItemText(hwndDlg, IDC_STARTED, PhpaGetRelativeTimeString(&context->Record->CreateTime)->Buffer); + else + PhSetDialogItemText(hwndDlg, IDC_STARTED, L"N/A"); + + if (context->Record->ExitTime.QuadPart != 0) + PhSetDialogItemText(hwndDlg, IDC_TERMINATED, PhpaGetRelativeTimeString(&context->Record->ExitTime)->Buffer); + else + PhSetDialogItemText(hwndDlg, IDC_TERMINATED, L"N/A"); + + PhSetDialogItemValue(hwndDlg, IDC_SESSIONID, context->Record->SessionId, FALSE); + } + break; + case WM_DESTROY: + { + if (context->FileIcon) + DestroyIcon(context->FileIcon); + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + case IDOK: + { + EndDialog(hwndDlg, IDOK); + } + break; + case IDC_OPENFILENAME: + { + if (context->Record->FileName) + { + PhShellExecuteUserString( + PhMainWndHandle, + L"FileBrowseExecutable", + context->Record->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } + } + break; + case IDC_PROPERTIES: + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItemForRecord(context->Record)) + { + ProcessHacker_ShowProcessProperties(PhMainWndHandle, processItem); + PhDereferenceObject(processItem); + } + else + { + PhShowError(hwndDlg, L"The process has already terminated; only the process record is available."); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/proctree.c b/ProcessHacker/proctree.c index 0bd39228551e..82f6501e23cc 100644 --- a/ProcessHacker/proctree.c +++ b/ProcessHacker/proctree.c @@ -1,3508 +1,3715 @@ -/* - * Process Hacker - - * process tree list - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The process tree list manages a list of tree nodes and handles callback events generated by the - * underlying treenew control. Retrieval of certain types of process information is also performed - * here, on the GUI thread (see PH_PROCESS_NODE.ValidMask). This is done for columns that require - * data not supplied by the process provider. - */ - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -typedef enum _PHP_AGGREGATE_TYPE -{ - AggregateTypeFloat, - AggregateTypeInt32, - AggregateTypeInt64, - AggregateTypeIntPtr -} PHP_AGGREGATE_TYPE; - -typedef enum _PHP_AGGREGATE_LOCATION -{ - AggregateLocationProcessNode, - AggregateLocationProcessItem -} PHP_AGGREGATE_LOCATION; - -VOID PhpRemoveProcessNode( - _In_ PPH_PROCESS_NODE ProcessNode - ); - -VOID PhpUpdateNeedCyclesInformation( - VOID - ); - -VOID PhpUpdateProcessNodeCycles( - _Inout_ PPH_PROCESS_NODE ProcessNode - ); - -LONG PhpProcessTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpProcessTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -static HWND ProcessTreeListHandle; -static ULONG ProcessTreeListSortColumn; -static PH_SORT_ORDER ProcessTreeListSortOrder; -static PH_CM_MANAGER ProcessTreeListCm; - -static PPH_HASH_ENTRY ProcessNodeHashSet[256] = PH_HASH_SET_INIT; // hashtable of all nodes -static PPH_LIST ProcessNodeList; // list of all nodes, used when sorting is enabled -static PPH_LIST ProcessNodeRootList; // list of root nodes - -BOOLEAN PhProcessTreeListStateHighlighting = TRUE; -static PPH_POINTER_LIST ProcessNodeStateList = NULL; // list of nodes which need to be processed - -static PH_TN_FILTER_SUPPORT FilterSupport; -static BOOLEAN NeedCyclesInformation = FALSE; - -static HDC GraphContext = NULL; -static ULONG GraphContextWidth = 0; -static ULONG GraphContextHeight = 0; -static HBITMAP GraphOldBitmap; -static HBITMAP GraphBitmap = NULL; -static PVOID GraphBits = NULL; - -VOID PhProcessTreeListInitialization( - VOID - ) -{ - ProcessNodeList = PhCreateList(40); - ProcessNodeRootList = PhCreateList(10); -} - -VOID PhInitializeProcessTreeList( - _In_ HWND hwnd - ) -{ - ProcessTreeListHandle = hwnd; - PhSetControlTheme(ProcessTreeListHandle, L"explorer"); - TreeNew_SetExtendedFlags(hwnd, TN_FLAG_ITEM_DRAG_SELECT, TN_FLAG_ITEM_DRAG_SELECT); - SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); - - TreeNew_SetCallback(hwnd, PhpProcessTreeNewCallback, NULL); - - TreeNew_SetMaxId(hwnd, PHPRTLC_MAXIMUM - 1); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHPRTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, -2, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_PID, TRUE, L"PID", 50, PH_ALIGN_RIGHT, 0, DT_RIGHT); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOTOTALRATE, TRUE, L"I/O total rate", 70, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTES, TRUE, L"Private bytes", 70, PH_ALIGN_RIGHT, 3, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_USERNAME, TRUE, L"User name", 140, PH_ALIGN_LEFT, 4, DT_PATH_ELLIPSIS); - PhAddTreeNewColumn(hwnd, PHPRTLC_DESCRIPTION, TRUE, L"Description", 180, PH_ALIGN_LEFT, 5, 0); - - // Customizable columns (1) - PhAddTreeNewColumn(hwnd, PHPRTLC_COMPANYNAME, FALSE, L"Company name", 180, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_VERSION, FALSE, L"Version", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_FILENAME, FALSE, L"File name", 180, PH_ALIGN_LEFT, -1, DT_PATH_ELLIPSIS); - PhAddTreeNewColumn(hwnd, PHPRTLC_COMMANDLINE, FALSE, L"Command line", 180, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPRIVATEBYTES, FALSE, L"Peak private bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_WORKINGSET, FALSE, L"Working set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKWORKINGSET, FALSE, L"Peak working set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEWS, FALSE, L"Private WS", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREDWS, FALSE, L"Shared WS", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREABLEWS, FALSE, L"Shareable WS", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALSIZE, FALSE, L"Virtual size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKVIRTUALSIZE, FALSE, L"Peak virtual size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTS, FALSE, L"Page faults", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_SESSIONID, FALSE, L"Session ID", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIORITYCLASS, FALSE, L"Priority class", 100, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_BASEPRIORITY, FALSE, L"Base priority", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - - // Customizable columns (2) - PhAddTreeNewColumnEx(hwnd, PHPRTLC_THREADS, FALSE, L"Threads", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_HANDLES, FALSE, L"Handles", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_GDIHANDLES, FALSE, L"GDI handles", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERHANDLES, FALSE, L"USER handles", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IORORATE, FALSE, L"I/O read+other rate", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRATE, FALSE, L"I/O write rate", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_INTEGRITY, FALSE, L"Integrity", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOPRIORITY, FALSE, L"I/O priority", 70, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEPRIORITY, FALSE, L"Page priority", 45, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_STARTTIME, FALSE, L"Start time", 100, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_TOTALCPUTIME, FALSE, L"Total CPU time", 90, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_KERNELCPUTIME, FALSE, L"Kernel CPU time", 90, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERCPUTIME, FALSE, L"User CPU time", 90, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_ASLR, FALSE, L"ASLR", 50, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_RELATIVESTARTTIME, FALSE, L"Relative start time", 180, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_BITS, FALSE, L"Bits", 50, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_ELEVATION, FALSE, L"Elevation", 60, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_WINDOWTITLE, FALSE, L"Window title", 120, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_WINDOWSTATUS, FALSE, L"Window status", 60, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLES, FALSE, L"Cycles", 110, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLESDELTA, FALSE, L"Cycles delta", 90, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx2(hwnd, PHPRTLC_CPUHISTORY, FALSE, L"CPU history", 100, PH_ALIGN_LEFT, -1, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING); - PhAddTreeNewColumnEx2(hwnd, PHPRTLC_PRIVATEBYTESHISTORY, FALSE, L"Private bytes history", 100, PH_ALIGN_LEFT, -1, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING); - PhAddTreeNewColumnEx2(hwnd, PHPRTLC_IOHISTORY, FALSE, L"I/O history", 100, PH_ALIGN_LEFT, -1, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING); - PhAddTreeNewColumn(hwnd, PHPRTLC_DEP, FALSE, L"DEP", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALIZED, FALSE, L"Virtualized", 80, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHES, FALSE, L"Context switches", 100, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHESDELTA, FALSE, L"Context switches delta", 80, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTSDELTA, FALSE, L"Page faults delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - - // I/O group columns - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADS, FALSE, L"I/O reads", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITES, FALSE, L"I/O writes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHER, FALSE, L"I/O other", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADBYTES, FALSE, L"I/O read bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITEBYTES, FALSE, L"I/O write bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERBYTES, FALSE, L"I/O other bytes", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADSDELTA, FALSE, L"I/O reads delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITESDELTA, FALSE, L"I/O writes delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERDELTA, FALSE, L"I/O other delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - - // Customizable columns (3) - PhAddTreeNewColumn(hwnd, PHPRTLC_OSCONTEXT, FALSE, L"OS context", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEDPOOL, FALSE, L"Paged pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPAGEDPOOL, FALSE, L"Peak paged pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_NONPAGEDPOOL, FALSE, L"Non-paged pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKNONPAGEDPOOL, FALSE, L"Peak non-paged pool", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_MINIMUMWORKINGSET, FALSE, L"Minimum working set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_MAXIMUMWORKINGSET, FALSE, L"Maximum working set", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTESDELTA, FALSE, L"Private bytes delta", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHPRTLC_SUBSYSTEM, FALSE, L"Subsystem", 110, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_PACKAGENAME, FALSE, L"Package name", 160, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_APPID, FALSE, L"App ID", 160, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHPRTLC_DPIAWARENESS, FALSE, L"DPI awareness", 110, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_CFGUARD, FALSE, L"CF Guard", 70, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_TIMESTAMP, FALSE, L"Time stamp", 140, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILEMODIFIEDTIME, FALSE, L"File modified time", 140, PH_ALIGN_LEFT, -1, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILESIZE, FALSE, L"File size", 70, PH_ALIGN_RIGHT, -1, DT_RIGHT, TRUE); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetTriState(hwnd, TRUE); - TreeNew_SetSort(hwnd, 0, NoSortOrder); - - PhCmInitializeManager(&ProcessTreeListCm, hwnd, PHPRTLC_MAXIMUM, PhpProcessTreeNewPostSortFunction); - - if (PhPluginsEnabled) - { - PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; - - treeNewInfo.TreeNewHandle = hwnd; - treeNewInfo.CmData = &ProcessTreeListCm; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), &treeNewInfo); - } - - PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ProcessNodeList); -} - -VOID PhLoadSettingsProcessTreeList( - VOID - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhGetStringSetting(L"ProcessTreeListColumns"); - sortSettings = PhGetStringSetting(L"ProcessTreeListSort"); - PhCmLoadSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); - - if (PhGetIntegerSetting(L"EnableInstantTooltips")) - { - SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0); - } - - PhpUpdateNeedCyclesInformation(); -} - -VOID PhSaveSettingsProcessTreeList( - VOID - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &sortSettings); - PhSetStringSetting2(L"ProcessTreeListColumns", &settings->sr); - PhSetStringSetting2(L"ProcessTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhReloadSettingsProcessTreeList( - VOID - ) -{ - SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, - PhGetIntegerSetting(L"EnableInstantTooltips") ? 0 : -1); -} - -struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportProcessTreeList( - VOID - ) -{ - return &FilterSupport; -} - -FORCEINLINE BOOLEAN PhCompareProcessNode( - _In_ PPH_PROCESS_NODE Value1, - _In_ PPH_PROCESS_NODE Value2 - ) -{ - return Value1->ProcessId == Value2->ProcessId; -} - -FORCEINLINE ULONG PhHashProcessNode( - _In_ PPH_PROCESS_NODE Value - ) -{ - return HandleToUlong(Value->ProcessId) / 4; -} - -FORCEINLINE BOOLEAN PhpValidateParentCreateTime( - _In_ PPH_PROCESS_NODE Child, - _In_ PPH_PROCESS_NODE Parent - ) -{ - return - PH_IS_FAKE_PROCESS_ID(Child->ProcessId) || - Parent->ProcessItem->CreateTime.QuadPart <= Child->ProcessItem->CreateTime.QuadPart; -} - -PPH_PROCESS_NODE PhAddProcessNode( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG RunId - ) -{ - PPH_PROCESS_NODE processNode; - PPH_PROCESS_NODE parentNode; - ULONG i; - - processNode = PhAllocate(PhEmGetObjectSize(EmProcessNodeType, sizeof(PH_PROCESS_NODE))); - memset(processNode, 0, sizeof(PH_PROCESS_NODE)); - PhInitializeTreeNewNode(&processNode->Node); - - if (PhProcessTreeListStateHighlighting && RunId != 1) - { - PhChangeShStateTn( - &processNode->Node, - &processNode->ShState, - &ProcessNodeStateList, - NewItemState, - PhCsColorNew, - NULL - ); - } - - processNode->ProcessId = ProcessItem->ProcessId; - processNode->ProcessItem = ProcessItem; - PhReferenceObject(ProcessItem); - - memset(processNode->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM); - processNode->Node.TextCache = processNode->TextCache; - processNode->Node.TextCacheSize = PHPRTLC_MAXIMUM; - - processNode->Children = PhCreateList(1); - - // Find this process' parent and add the process to it if we found it. - if ( - (parentNode = PhFindProcessNode(ProcessItem->ParentProcessId)) && - PhpValidateParentCreateTime(processNode, parentNode) - ) - { - PhAddItemList(parentNode->Children, processNode); - processNode->Parent = parentNode; - } - else - { - // No parent, add to root list. - processNode->Parent = NULL; - PhAddItemList(ProcessNodeRootList, processNode); - } - - // Find this process' children and move them to this node. - - for (i = 0; i < ProcessNodeRootList->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNodeRootList->Items[i]; - - if ( - node != processNode && // for cases where the parent PID = PID (e.g. System Idle Process) - node->ProcessItem->ParentProcessId == ProcessItem->ProcessId && - PhpValidateParentCreateTime(node, processNode) - ) - { - node->Parent = processNode; - PhAddItemList(processNode->Children, node); - } - } - - for (i = 0; i < processNode->Children->Count; i++) - { - PhRemoveItemList( - ProcessNodeRootList, - PhFindItemList(ProcessNodeRootList, processNode->Children->Items[i]) - ); - } - - PhAddEntryHashSet( - ProcessNodeHashSet, - PH_HASH_SET_SIZE(ProcessNodeHashSet), - &processNode->HashEntry, - PhHashProcessNode(processNode) - ); - PhAddItemList(ProcessNodeList, processNode); - - if (PhCsCollapseServicesOnStart) - { - static PH_STRINGREF servicesBaseName = PH_STRINGREF_INIT(L"\\services.exe"); - static BOOLEAN servicesFound = FALSE; - static PPH_STRING servicesFileName = NULL; - - if (!servicesFound) - { - if (!servicesFileName) - { - PPH_STRING systemDirectory; - - systemDirectory = PhGetSystemDirectory(); - servicesFileName = PhConcatStringRef2(&systemDirectory->sr, &servicesBaseName); - PhDereferenceObject(systemDirectory); - } - - // If this process is services.exe, collapse the node and free the string. - if ( - ProcessItem->FileName && - PhEqualString(ProcessItem->FileName, servicesFileName, TRUE) - ) - { - processNode->Node.Expanded = FALSE; - PhDereferenceObject(servicesFileName); - servicesFileName = NULL; - servicesFound = TRUE; - } - } - } - - if (WindowsVersion >= WINDOWS_7 && PhEnableCycleCpuUsage && ProcessItem->ProcessId == INTERRUPTS_PROCESS_ID) - PhInitializeStringRef(&processNode->DescriptionText, L"Interrupts and DPCs"); - - if (FilterSupport.FilterList) - processNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &processNode->Node); - - PhEmCallObjectOperation(EmProcessNodeType, processNode, EmObjectCreate); - - TreeNew_NodesStructured(ProcessTreeListHandle); - - return processNode; -} - -PPH_PROCESS_NODE PhFindProcessNode( - _In_ HANDLE ProcessId - ) -{ - PH_PROCESS_NODE lookupNode; - PPH_HASH_ENTRY entry; - PPH_PROCESS_NODE node; - - lookupNode.ProcessId = ProcessId; - entry = PhFindEntryHashSet( - ProcessNodeHashSet, - PH_HASH_SET_SIZE(ProcessNodeHashSet), - PhHashProcessNode(&lookupNode) - ); - - for (; entry; entry = entry->Next) - { - node = CONTAINING_RECORD(entry, PH_PROCESS_NODE, HashEntry); - - if (PhCompareProcessNode(&lookupNode, node)) - return node; - } - - return NULL; -} - -VOID PhRemoveProcessNode( - _In_ PPH_PROCESS_NODE ProcessNode - ) -{ - // Remove from the hashtable here to avoid problems in case the key is re-used. - PhRemoveEntryHashSet(ProcessNodeHashSet, PH_HASH_SET_SIZE(ProcessNodeHashSet), &ProcessNode->HashEntry); - - if (PhProcessTreeListStateHighlighting) - { - PhChangeShStateTn( - &ProcessNode->Node, - &ProcessNode->ShState, - &ProcessNodeStateList, - RemovingItemState, - PhCsColorRemoved, - ProcessTreeListHandle - ); - } - else - { - PhpRemoveProcessNode(ProcessNode); - } -} - -VOID PhpRemoveProcessNode( - _In_ PPH_PROCESS_NODE ProcessNode - ) -{ - ULONG index; - ULONG i; - - PhEmCallObjectOperation(EmProcessNodeType, ProcessNode, EmObjectDelete); - - if (ProcessNode->Parent) - { - // Remove the node from its parent. - - if ((index = PhFindItemList(ProcessNode->Parent->Children, ProcessNode)) != -1) - PhRemoveItemList(ProcessNode->Parent->Children, index); - } - else - { - // Remove the node from the root list. - - if ((index = PhFindItemList(ProcessNodeRootList, ProcessNode)) != -1) - PhRemoveItemList(ProcessNodeRootList, index); - } - - // Move the node's children to the root list. - for (i = 0; i < ProcessNode->Children->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNode->Children->Items[i]; - - node->Parent = NULL; - PhAddItemList(ProcessNodeRootList, node); - } - - // Remove from list and cleanup. - - if ((index = PhFindItemList(ProcessNodeList, ProcessNode)) != -1) - PhRemoveItemList(ProcessNodeList, index); - - PhDereferenceObject(ProcessNode->Children); - - PhClearReference(&ProcessNode->WindowText); - PhClearReference(&ProcessNode->AppIdText); - - PhClearReference(&ProcessNode->TooltipText); - - PhClearReference(&ProcessNode->IoTotalRateText); - PhClearReference(&ProcessNode->PrivateBytesText); - PhClearReference(&ProcessNode->PeakPrivateBytesText); - PhClearReference(&ProcessNode->WorkingSetText); - PhClearReference(&ProcessNode->PeakWorkingSetText); - PhClearReference(&ProcessNode->PrivateWsText); - PhClearReference(&ProcessNode->SharedWsText); - PhClearReference(&ProcessNode->ShareableWsText); - PhClearReference(&ProcessNode->VirtualSizeText); - PhClearReference(&ProcessNode->PeakVirtualSizeText); - PhClearReference(&ProcessNode->PageFaultsText); - PhClearReference(&ProcessNode->IoRoRateText); - PhClearReference(&ProcessNode->IoWRateText); - PhClearReference(&ProcessNode->StartTimeText); - PhClearReference(&ProcessNode->TotalCpuTimeText); - PhClearReference(&ProcessNode->KernelCpuTimeText); - PhClearReference(&ProcessNode->UserCpuTimeText); - PhClearReference(&ProcessNode->RelativeStartTimeText); - PhClearReference(&ProcessNode->WindowTitleText); - PhClearReference(&ProcessNode->CyclesText); - PhClearReference(&ProcessNode->CyclesDeltaText); - PhClearReference(&ProcessNode->ContextSwitchesText); - PhClearReference(&ProcessNode->ContextSwitchesDeltaText); - PhClearReference(&ProcessNode->PageFaultsDeltaText); - - for (i = 0; i < PHPRTLC_IOGROUP_COUNT; i++) - PhClearReference(&ProcessNode->IoGroupText[i]); - - PhClearReference(&ProcessNode->PagedPoolText); - PhClearReference(&ProcessNode->PeakPagedPoolText); - PhClearReference(&ProcessNode->NonPagedPoolText); - PhClearReference(&ProcessNode->PeakNonPagedPoolText); - PhClearReference(&ProcessNode->MinimumWorkingSetText); - PhClearReference(&ProcessNode->MaximumWorkingSetText); - PhClearReference(&ProcessNode->PrivateBytesDeltaText); - PhClearReference(&ProcessNode->TimeStampText); - PhClearReference(&ProcessNode->FileModifiedTimeText); - PhClearReference(&ProcessNode->FileSizeText); - - PhDeleteGraphBuffers(&ProcessNode->CpuGraphBuffers); - PhDeleteGraphBuffers(&ProcessNode->PrivateGraphBuffers); - PhDeleteGraphBuffers(&ProcessNode->IoGraphBuffers); - - PhDereferenceObject(ProcessNode->ProcessItem); - - PhFree(ProcessNode); - - TreeNew_NodesStructured(ProcessTreeListHandle); -} - -VOID PhUpdateProcessNode( - _In_ PPH_PROCESS_NODE ProcessNode - ) -{ - memset(ProcessNode->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM); - - if (ProcessNode->TooltipText) - { - PhDereferenceObject(ProcessNode->TooltipText); - ProcessNode->TooltipText = NULL; - } - - PhInvalidateTreeNewNode(&ProcessNode->Node, TN_CACHE_COLOR | TN_CACHE_ICON); - TreeNew_InvalidateNode(ProcessTreeListHandle, &ProcessNode->Node); -} - -VOID PhTickProcessNodes( - VOID - ) -{ - ULONG i; - PH_TREENEW_VIEW_PARTS viewParts; - BOOLEAN fullyInvalidated; - RECT rect; - - // Text invalidation, node updates - - for (i = 0; i < ProcessNodeList->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; - - // The name and PID never change, so we don't invalidate that. - memset(&node->TextCache[2], 0, sizeof(PH_STRINGREF) * (PHPRTLC_MAXIMUM - 2)); - node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS; // Items that always remain valid - - // Invalidate graph buffers. - node->CpuGraphBuffers.Valid = FALSE; - node->PrivateGraphBuffers.Valid = FALSE; - node->IoGraphBuffers.Valid = FALSE; - - // Updates cycles if necessary. - if (NeedCyclesInformation) - PhpUpdateProcessNodeCycles(node); - } - - fullyInvalidated = FALSE; - - if (ProcessTreeListSortOrder != NoSortOrder) - { - // Force a rebuild to sort the items. - TreeNew_NodesStructured(ProcessTreeListHandle); - fullyInvalidated = TRUE; - } - - // State highlighting - PH_TICK_SH_STATE_TN(PH_PROCESS_NODE, ShState, ProcessNodeStateList, PhpRemoveProcessNode, PhCsHighlightingDuration, ProcessTreeListHandle, TRUE, &fullyInvalidated); - - if (!fullyInvalidated) - { - // The first column doesn't need to be invalidated because the process name never changes, and - // icon changes are handled by the modified event. This small optimization can save more than - // 10 million cycles per update (on my machine). - TreeNew_GetViewParts(ProcessTreeListHandle, &viewParts); - rect.left = viewParts.NormalLeft; - rect.top = viewParts.HeaderHeight; - rect.right = viewParts.ClientRect.right - viewParts.VScrollWidth; - rect.bottom = viewParts.ClientRect.bottom; - InvalidateRect(ProcessTreeListHandle, &rect, FALSE); - } -} - -static VOID PhpNeedGraphContext( - _In_ HDC hdc, - _In_ ULONG Width, - _In_ ULONG Height - ) -{ - BITMAPINFOHEADER header; - - // If we already have a graph context and it's the right size, then return immediately. - if (GraphContextWidth == Width && GraphContextHeight == Height) - return; - - if (GraphContext) - { - // The original bitmap must be selected back into the context, otherwise - // the bitmap can't be deleted. - SelectObject(GraphContext, GraphBitmap); - DeleteObject(GraphBitmap); - DeleteDC(GraphContext); - - GraphContext = NULL; - GraphBitmap = NULL; - GraphBits = NULL; - } - - GraphContext = CreateCompatibleDC(hdc); - - memset(&header, 0, sizeof(BITMAPINFOHEADER)); - header.biSize = sizeof(BITMAPINFOHEADER); - header.biWidth = Width; - header.biHeight = Height; - header.biPlanes = 1; - header.biBitCount = 32; - GraphBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &GraphBits, NULL, 0); - GraphOldBitmap = SelectObject(GraphContext, GraphBitmap); -} - -static BOOLEAN PhpFormatInt32GroupDigits( - _In_ ULONG Value, - _Out_writes_bytes_(BufferLength) PWCHAR Buffer, - _In_ ULONG BufferLength, - _Out_opt_ PPH_STRINGREF String - ) -{ - PH_FORMAT format; - SIZE_T returnLength; - - PhInitFormatU(&format, Value); - format.Type |= FormatGroupDigits; - - if (PhFormatToBuffer(&format, 1, Buffer, BufferLength, &returnLength)) - { - if (String) - { - String->Buffer = Buffer; - String->Length = returnLength - sizeof(WCHAR); - } - - return TRUE; - } - else - { - return FALSE; - } -} - -FORCEINLINE PVOID PhpFieldForAggregate( - _In_ PPH_PROCESS_NODE ProcessNode, - _In_ PHP_AGGREGATE_LOCATION Location, - _In_ SIZE_T FieldOffset - ) -{ - PVOID object; - - switch (Location) - { - case AggregateLocationProcessNode: - object = ProcessNode; - break; - case AggregateLocationProcessItem: - object = ProcessNode->ProcessItem; - break; - default: - PhRaiseStatus(STATUS_INVALID_PARAMETER); - } - - return PTR_ADD_OFFSET(object, FieldOffset); -} - -FORCEINLINE VOID PhpAccumulateField( - _Inout_ PVOID Accumulator, - _In_ PVOID Value, - _In_ PHP_AGGREGATE_TYPE Type - ) -{ - switch (Type) - { - case AggregateTypeFloat: - *(PFLOAT)Accumulator += *(PFLOAT)Value; - break; - case AggregateTypeInt32: - *(PULONG)Accumulator += *(PULONG)Value; - break; - case AggregateTypeInt64: - *(PULONG64)Accumulator += *(PULONG64)Value; - break; - case AggregateTypeIntPtr: - *(PULONG_PTR)Accumulator += *(PULONG_PTR)Value; - break; - } -} - -static VOID PhpAggregateField( - _In_ PPH_PROCESS_NODE ProcessNode, - _In_ PHP_AGGREGATE_TYPE Type, - _In_ PHP_AGGREGATE_LOCATION Location, - _In_ SIZE_T FieldOffset, - _Inout_ PVOID AggregatedValue - ) -{ - ULONG i; - - PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type); - - for (i = 0; i < ProcessNode->Children->Count; i++) - { - PhpAggregateField(ProcessNode->Children->Items[i], Type, Location, FieldOffset, AggregatedValue); - } -} - -static VOID PhpAggregateFieldIfNeeded( - _In_ PPH_PROCESS_NODE ProcessNode, - _In_ PHP_AGGREGATE_TYPE Type, - _In_ PHP_AGGREGATE_LOCATION Location, - _In_ SIZE_T FieldOffset, - _Inout_ PVOID AggregatedValue - ) -{ - if (!PhCsPropagateCpuUsage || ProcessNode->Node.Expanded || ProcessTreeListSortOrder != NoSortOrder) - { - PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type); - } - else - { - PhpAggregateField(ProcessNode, Type, Location, FieldOffset, AggregatedValue); - } -} - -static VOID PhpUpdateProcessNodeWsCounters( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_WSCOUNTERS)) - { - BOOLEAN success = FALSE; - HANDLE processHandle; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION, - ProcessNode->ProcessItem->ProcessId - ))) - { - if (NT_SUCCESS(PhGetProcessWsCounters( - processHandle, - &ProcessNode->WsCounters - ))) - success = TRUE; - - NtClose(processHandle); - } - - if (!success) - memset(&ProcessNode->WsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS)); - - ProcessNode->ValidMask |= PHPN_WSCOUNTERS; - } -} - -static VOID PhpUpdateProcessNodeGdiUserHandles( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_GDIUSERHANDLES)) - { - if (ProcessNode->ProcessItem->QueryHandle) - { - ProcessNode->GdiHandles = GetGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_GDIOBJECTS); - ProcessNode->UserHandles = GetGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_USEROBJECTS); - } - else - { - ProcessNode->GdiHandles = 0; - ProcessNode->UserHandles = 0; - } - - ProcessNode->ValidMask |= PHPN_GDIUSERHANDLES; - } -} - -static VOID PhpUpdateProcessNodeIoPagePriority( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_IOPAGEPRIORITY)) - { - if (ProcessNode->ProcessItem->QueryHandle) - { - if (!NT_SUCCESS(PhGetProcessIoPriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->IoPriority))) - ProcessNode->IoPriority = -1; - if (!NT_SUCCESS(PhGetProcessPagePriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->PagePriority))) - ProcessNode->PagePriority = -1; - } - else - { - ProcessNode->IoPriority = -1; - ProcessNode->PagePriority = -1; - } - - ProcessNode->ValidMask |= PHPN_IOPAGEPRIORITY; - } -} - -static VOID PhpUpdateProcessNodeWindow( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_WINDOW)) - { - ProcessNode->WindowHandle = PhGetProcessMainWindow(ProcessNode->ProcessId, ProcessNode->ProcessItem->QueryHandle); - - PhClearReference(&ProcessNode->WindowText); - - if (ProcessNode->WindowHandle) - { - PhGetWindowTextEx(ProcessNode->WindowHandle, PH_GET_WINDOW_TEXT_INTERNAL, &ProcessNode->WindowText); - ProcessNode->WindowHung = !!IsHungAppWindow(ProcessNode->WindowHandle); - } - - ProcessNode->ValidMask |= PHPN_WINDOW; - } -} - -static VOID PhpUpdateProcessNodeDepStatus( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_DEPSTATUS)) - { - HANDLE processHandle; - ULONG depStatus; - - depStatus = 0; - -#ifdef _WIN64 - if (ProcessNode->ProcessItem->IsWow64) -#else - if (TRUE) -#endif - { - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION, - ProcessNode->ProcessItem->ProcessId - ))) - { - PhGetProcessDepStatus(processHandle, &depStatus); - NtClose(processHandle); - } - } - else - { - if (ProcessNode->ProcessItem->QueryHandle) - depStatus = PH_PROCESS_DEP_ENABLED | PH_PROCESS_DEP_PERMANENT; - } - - ProcessNode->DepStatus = depStatus; - - ProcessNode->ValidMask |= PHPN_DEPSTATUS; - } -} - -static VOID PhpUpdateProcessNodeToken( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_TOKEN)) - { - HANDLE tokenHandle; - - ProcessNode->VirtualizationAllowed = FALSE; - ProcessNode->VirtualizationEnabled = FALSE; - - if (WINDOWS_HAS_UAC && ProcessNode->ProcessItem->QueryHandle) - { - if (NT_SUCCESS(PhOpenProcessToken( - ProcessNode->ProcessItem->QueryHandle, - TOKEN_QUERY, - &tokenHandle - ))) - { - if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &ProcessNode->VirtualizationAllowed)) && - ProcessNode->VirtualizationAllowed) - { - if (!NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &ProcessNode->VirtualizationEnabled))) - { - ProcessNode->VirtualizationAllowed = FALSE; // display N/A on error - } - } - - NtClose(tokenHandle); - } - } - - ProcessNode->ValidMask |= PHPN_TOKEN; - } -} - -static VOID PhpUpdateProcessOsContext( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_OSCONTEXT)) - { - HANDLE processHandle; - - if (WindowsVersion >= WINDOWS_7) - { - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) - { - if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid))) - { - if (memcmp(&ProcessNode->OsContextGuid, &WINTHRESHOLD_CONTEXT_GUID, sizeof(GUID)) == 0) - ProcessNode->OsContextVersion = WINDOWS_10; - else if (memcmp(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID, sizeof(GUID)) == 0) - ProcessNode->OsContextVersion = WINDOWS_8_1; - else if (memcmp(&ProcessNode->OsContextGuid, &WIN8_CONTEXT_GUID, sizeof(GUID)) == 0) - ProcessNode->OsContextVersion = WINDOWS_8; - else if (memcmp(&ProcessNode->OsContextGuid, &WIN7_CONTEXT_GUID, sizeof(GUID)) == 0) - ProcessNode->OsContextVersion = WINDOWS_7; - else if (memcmp(&ProcessNode->OsContextGuid, &VISTA_CONTEXT_GUID, sizeof(GUID)) == 0) - ProcessNode->OsContextVersion = WINDOWS_VISTA; - else if (memcmp(&ProcessNode->OsContextGuid, &XP_CONTEXT_GUID, sizeof(GUID)) == 0) - ProcessNode->OsContextVersion = WINDOWS_XP; - } - - NtClose(processHandle); - } - } - - ProcessNode->ValidMask |= PHPN_OSCONTEXT; - } -} - -static VOID PhpUpdateProcessNodeQuotaLimits( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_QUOTALIMITS)) - { - QUOTA_LIMITS quotaLimits; - - if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(NtQueryInformationProcess( - ProcessNode->ProcessItem->QueryHandle, - ProcessQuotaLimits, - "aLimits, - sizeof(QUOTA_LIMITS), - NULL - ))) - { - ProcessNode->MinimumWorkingSetSize = quotaLimits.MinimumWorkingSetSize; - ProcessNode->MaximumWorkingSetSize = quotaLimits.MaximumWorkingSetSize; - } - else - { - ProcessNode->MinimumWorkingSetSize = 0; - ProcessNode->MaximumWorkingSetSize = 0; - } - - ProcessNode->ValidMask |= PHPN_QUOTALIMITS; - } -} - -static VOID PhpUpdateProcessNodeImage( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_IMAGE)) - { - HANDLE processHandle; - PROCESS_BASIC_INFORMATION basicInfo; - PVOID imageBaseAddress; - PH_REMOTE_MAPPED_IMAGE mappedImage; - - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) - { - if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo))) - { - if (NT_SUCCESS(NtReadVirtualMemory( - processHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ImageBaseAddress)), - &imageBaseAddress, - sizeof(PVOID), - NULL - ))) - { - if (NT_SUCCESS(PhLoadRemoteMappedImage(processHandle, imageBaseAddress, &mappedImage))) - { - ProcessNode->ImageTimeDateStamp = mappedImage.NtHeaders->FileHeader.TimeDateStamp; - ProcessNode->ImageCharacteristics = mappedImage.NtHeaders->FileHeader.Characteristics; - - if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->Subsystem; - ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; - } - else if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) - { - ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader)->Subsystem; - ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; - } - - PhUnloadRemoteMappedImage(&mappedImage); - } - } - } - - NtClose(processHandle); - } - - ProcessNode->ValidMask |= PHPN_IMAGE; - } -} - -static VOID PhpUpdateProcessNodeAppId( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_APPID)) - { - HANDLE processHandle; - ULONG windowFlags; - PPH_STRING windowTitle; - - PhClearReference(&ProcessNode->AppIdText); - - if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessNode->ProcessId))) - { - if (WindowsVersion >= WINDOWS_7) - { - if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessNode->ProcessId))) - goto Done; - } - else - { - goto Done; - } - } - - if (NT_SUCCESS(PhGetProcessWindowTitle( - processHandle, - &windowFlags, - &windowTitle - ))) - { - if (windowFlags & STARTF_TITLEISAPPID) - ProcessNode->AppIdText = windowTitle; - else - PhDereferenceObject(windowTitle); - } - - NtClose(processHandle); - -Done: - ProcessNode->ValidMask |= PHPN_APPID; - } -} - -static VOID PhpUpdateProcessNodeDpiAwareness( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static BOOL (WINAPI *getProcessDpiAwarenessInternal)( - _In_ HANDLE hprocess, - _Out_ ULONG *value - ); - - if (PhBeginInitOnce(&initOnce)) - { - getProcessDpiAwarenessInternal = PhGetModuleProcAddress(L"user32.dll", "GetProcessDpiAwarenessInternal"); - PhEndInitOnce(&initOnce); - } - - if (!getProcessDpiAwarenessInternal) - return; - - if (!(ProcessNode->ValidMask & PHPN_DPIAWARENESS)) - { - if (ProcessNode->ProcessItem->QueryHandle) - { - ULONG dpiAwareness; - - if (getProcessDpiAwarenessInternal(ProcessNode->ProcessItem->QueryHandle, &dpiAwareness)) - ProcessNode->DpiAwareness = dpiAwareness + 1; - } - - ProcessNode->ValidMask |= PHPN_DPIAWARENESS; - } -} - -static VOID PhpUpdateProcessNodeFileAttributes( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (!(ProcessNode->ValidMask & PHPN_FILEATTRIBUTES)) - { - FILE_NETWORK_OPEN_INFORMATION networkOpenInfo; - - if (ProcessNode->ProcessItem->FileName && NT_SUCCESS(PhQueryFullAttributesFileWin32( - ProcessNode->ProcessItem->FileName->Buffer, - &networkOpenInfo - ))) - { - ProcessNode->FileLastWriteTime = networkOpenInfo.LastWriteTime; - ProcessNode->FileEndOfFile = networkOpenInfo.EndOfFile; - } - else - { - ProcessNode->FileEndOfFile.QuadPart = -1; - } - - ProcessNode->ValidMask |= PHPN_FILEATTRIBUTES; - } -} - -static VOID PhpUpdateNeedCyclesInformation( - VOID - ) -{ - PH_TREENEW_COLUMN column; - - NeedCyclesInformation = FALSE; - - // Before Windows Vista, there is no cycle time measurement. - // On Windows 7 and above, cycle time information is available directly from the process item. - // We only need to query cycle time separately for Windows Vista. - if (WindowsVersion != WINDOWS_VISTA) - return; - - TreeNew_GetColumn(ProcessTreeListHandle, PHPRTLC_CYCLES, &column); - - if (column.Visible) - { - NeedCyclesInformation = TRUE; - return; - } - - TreeNew_GetColumn(ProcessTreeListHandle, PHPRTLC_CYCLESDELTA, &column); - - if (column.Visible) - { - NeedCyclesInformation = TRUE; - return; - } -} - -static VOID PhpUpdateProcessNodeCycles( - _Inout_ PPH_PROCESS_NODE ProcessNode - ) -{ - if (ProcessNode->ProcessId == SYSTEM_IDLE_PROCESS_ID) - { - PULARGE_INTEGER idleThreadCycleTimes; - ULONG64 cycleTime; - ULONG i; - - // System Idle Process requires special treatment. - - idleThreadCycleTimes = PhAllocate( - sizeof(ULARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - - if (NT_SUCCESS(NtQuerySystemInformation( - SystemProcessorIdleCycleTimeInformation, - idleThreadCycleTimes, - sizeof(ULARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, - NULL - ))) - { - cycleTime = 0; - - for (i = 0; i < (ULONG)PhSystemBasicInformation.NumberOfProcessors; i++) - cycleTime += idleThreadCycleTimes[i].QuadPart; - - PhUpdateDelta(&ProcessNode->CyclesDelta, cycleTime); - } - - PhFree(idleThreadCycleTimes); - } - else if (ProcessNode->ProcessItem->QueryHandle) - { - ULONG64 cycleTime; - - if (NT_SUCCESS(PhGetProcessCycleTime(ProcessNode->ProcessItem->QueryHandle, &cycleTime))) - { - PhUpdateDelta(&ProcessNode->CyclesDelta, cycleTime); - } - } - - if (ProcessNode->CyclesDelta.Value != 0) - PhMoveReference(&ProcessNode->CyclesText, PhFormatUInt64(ProcessNode->CyclesDelta.Value, TRUE)); - else - PhClearReference(&ProcessNode->CyclesText); - - if (ProcessNode->CyclesDelta.Delta != 0) - PhMoveReference(&ProcessNode->CyclesDeltaText, PhFormatUInt64(ProcessNode->CyclesDelta.Delta, TRUE)); - else - PhClearReference(&ProcessNode->CyclesDeltaText); -} - -#define SORT_FUNCTION(Column) PhpProcessTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpProcessTreeNewCompare##Column( \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)_elem1; \ - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)_elem2; \ - PPH_PROCESS_ITEM processItem1 = node1->ProcessItem; \ - PPH_PROCESS_ITEM processItem2 = node2->ProcessItem; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId); \ - \ - return PhModifySort(sortResult, ProcessTreeListSortOrder); \ -} - -LONG PhpProcessTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - if (Result == 0) - Result = intptrcmp((LONG_PTR)((PPH_PROCESS_NODE)Node1)->ProcessItem->ProcessId, (LONG_PTR)((PPH_PROCESS_NODE)Node2)->ProcessItem->ProcessId); - - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(Name) -{ - sortResult = PhCompareString(processItem1->ProcessName, processItem2->ProcessName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Pid) -{ - // Use signed int so DPCs and Interrupts are placed above System Idle Process. - sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Cpu) -{ - sortResult = singlecmp(processItem1->CpuUsage, processItem2->CpuUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoTotalRate) -{ - sortResult = uint64cmp( - processItem1->IoReadDelta.Delta + processItem1->IoWriteDelta.Delta + processItem1->IoOtherDelta.Delta, - processItem2->IoReadDelta.Delta + processItem2->IoWriteDelta.Delta + processItem2->IoOtherDelta.Delta - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PrivateBytes) -{ - sortResult = uintptrcmp(processItem1->VmCounters.PagefileUsage, processItem2->VmCounters.PagefileUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(UserName) -{ - sortResult = PhCompareStringWithNull(processItem1->UserName, processItem2->UserName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Description) -{ - PH_STRINGREF sr1; - PH_STRINGREF sr2; - - sr1 = processItem1->VersionInfo.FileDescription ? processItem1->VersionInfo.FileDescription->sr : node1->DescriptionText; - sr2 = processItem2->VersionInfo.FileDescription ? processItem2->VersionInfo.FileDescription->sr : node2->DescriptionText; - - sortResult = PhCompareStringRef(&sr1, &sr2, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CompanyName) -{ - sortResult = PhCompareStringWithNull( - processItem1->VersionInfo.CompanyName, - processItem2->VersionInfo.CompanyName, - TRUE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Version) -{ - sortResult = PhCompareStringWithNull( - processItem1->VersionInfo.FileVersion, - processItem2->VersionInfo.FileVersion, - TRUE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileName) -{ - sortResult = PhCompareStringWithNull( - processItem1->FileName, - processItem2->FileName, - TRUE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CommandLine) -{ - sortResult = PhCompareStringWithNull( - processItem1->CommandLine, - processItem2->CommandLine, - TRUE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PeakPrivateBytes) -{ - sortResult = uintptrcmp(processItem1->VmCounters.PeakPagefileUsage, processItem2->VmCounters.PeakPagefileUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(WorkingSet) -{ - sortResult = uintptrcmp(processItem1->VmCounters.WorkingSetSize, processItem2->VmCounters.WorkingSetSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PeakWorkingSet) -{ - sortResult = uintptrcmp(processItem1->VmCounters.PeakWorkingSetSize, processItem2->VmCounters.PeakWorkingSetSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PrivateWs) -{ - PhpUpdateProcessNodeWsCounters(node1); - PhpUpdateProcessNodeWsCounters(node2); - sortResult = uintptrcmp(node1->WsCounters.NumberOfPrivatePages, node2->WsCounters.NumberOfPrivatePages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PrivateWsWin7) -{ - sortResult = uintptrcmp(processItem1->WorkingSetPrivateSize, processItem2->WorkingSetPrivateSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(SharedWs) -{ - PhpUpdateProcessNodeWsCounters(node1); - PhpUpdateProcessNodeWsCounters(node2); - sortResult = uintptrcmp(node1->WsCounters.NumberOfSharedPages, node2->WsCounters.NumberOfSharedPages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ShareableWs) -{ - PhpUpdateProcessNodeWsCounters(node1); - PhpUpdateProcessNodeWsCounters(node2); - sortResult = uintptrcmp(node1->WsCounters.NumberOfShareablePages, node2->WsCounters.NumberOfShareablePages); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(VirtualSize) -{ - sortResult = uintptrcmp(processItem1->VmCounters.VirtualSize, processItem2->VmCounters.VirtualSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PeakVirtualSize) -{ - sortResult = uintptrcmp(processItem1->VmCounters.PeakVirtualSize, processItem2->VmCounters.PeakVirtualSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PageFaults) -{ - sortResult = uintcmp(processItem1->VmCounters.PageFaultCount, processItem2->VmCounters.PageFaultCount); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(SessionId) -{ - sortResult = uintcmp(processItem1->SessionId, processItem2->SessionId); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(BasePriority) -{ - sortResult = intcmp(processItem1->BasePriority, processItem2->BasePriority); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Threads) -{ - sortResult = uintcmp(processItem1->NumberOfThreads, processItem2->NumberOfThreads); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Handles) -{ - sortResult = uintcmp(processItem1->NumberOfHandles, processItem2->NumberOfHandles); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(GdiHandles) -{ - sortResult = uintcmp(node1->GdiHandles, node2->GdiHandles); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(UserHandles) -{ - sortResult = uintcmp(node1->UserHandles, node2->UserHandles); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoRoRate) -{ - sortResult = uint64cmp( - processItem1->IoReadDelta.Delta + processItem1->IoOtherDelta.Delta, - processItem2->IoReadDelta.Delta + processItem2->IoOtherDelta.Delta - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoWRate) -{ - sortResult = uint64cmp( - processItem1->IoWriteDelta.Delta, - processItem2->IoWriteDelta.Delta - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Integrity) -{ - sortResult = uintcmp(processItem1->IntegrityLevel, processItem2->IntegrityLevel); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoPriority) -{ - sortResult = uintcmp(node1->IoPriority, node2->IoPriority); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PagePriority) -{ - sortResult = uintcmp(node1->PagePriority, node2->PagePriority); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(StartTime) -{ - sortResult = int64cmp(processItem1->CreateTime.QuadPart, processItem2->CreateTime.QuadPart); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(TotalCpuTime) -{ - sortResult = uint64cmp( - processItem1->KernelTime.QuadPart + processItem1->UserTime.QuadPart, - processItem2->KernelTime.QuadPart + processItem2->UserTime.QuadPart - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(KernelCpuTime) -{ - sortResult = uint64cmp( - processItem1->KernelTime.QuadPart, - processItem2->KernelTime.QuadPart - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(UserCpuTime) -{ - sortResult = uint64cmp( - processItem1->UserTime.QuadPart, - processItem2->UserTime.QuadPart - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(VerificationStatus) -{ - sortResult = intcmp(processItem1->VerifyResult, processItem2->VerifyResult); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(VerifiedSigner) -{ - sortResult = PhCompareStringWithNull( - processItem1->VerifySignerName, - processItem2->VerifySignerName, - TRUE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Aslr) -{ - PhpUpdateProcessNodeImage(node1); - PhpUpdateProcessNodeImage(node2); - sortResult = intcmp( - node1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, - node2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(RelativeStartTime) -{ - sortResult = -int64cmp(processItem1->CreateTime.QuadPart, processItem2->CreateTime.QuadPart); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Bits) -{ - sortResult = intcmp(processItem1->IsWow64Valid, processItem2->IsWow64Valid); - - if (sortResult == 0) - sortResult = intcmp(processItem1->IsWow64, processItem2->IsWow64); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Elevation) -{ - ULONG key1; - ULONG key2; - - switch (processItem1->ElevationType) - { - case TokenElevationTypeFull: - key1 = 2; - break; - case TokenElevationTypeLimited: - key1 = 1; - break; - default: - key1 = 0; - break; - } - - switch (processItem2->ElevationType) - { - case TokenElevationTypeFull: - key2 = 2; - break; - case TokenElevationTypeLimited: - key2 = 1; - break; - default: - key2 = 0; - break; - } - - sortResult = intcmp(key1, key2); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(WindowTitle) -{ - PhpUpdateProcessNodeWindow(node1); - PhpUpdateProcessNodeWindow(node2); - sortResult = PhCompareStringWithNull(node1->WindowText, node2->WindowText, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(WindowStatus) -{ - PhpUpdateProcessNodeWindow(node1); - PhpUpdateProcessNodeWindow(node2); - sortResult = intcmp(node1->WindowHung, node2->WindowHung); - - // Make sure all processes with windows get grouped together. - if (sortResult == 0) - sortResult = intcmp(!!node1->WindowHandle, !!node2->WindowHandle); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Cycles) -{ - sortResult = uint64cmp(node1->CyclesDelta.Value, node2->CyclesDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CyclesWin7) -{ - sortResult = uint64cmp(processItem1->CycleTimeDelta.Value, processItem2->CycleTimeDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CyclesDelta) -{ - sortResult = uint64cmp(node1->CyclesDelta.Delta, node2->CyclesDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CyclesDeltaWin7) -{ - sortResult = uint64cmp(processItem1->CycleTimeDelta.Delta, processItem2->CycleTimeDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Dep) -{ - PhpUpdateProcessNodeDepStatus(node1); - PhpUpdateProcessNodeDepStatus(node2); - sortResult = uintcmp(node1->DepStatus, node2->DepStatus); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Virtualized) -{ - PhpUpdateProcessNodeToken(node1); - PhpUpdateProcessNodeToken(node2); - sortResult = intcmp(node1->VirtualizationEnabled, node2->VirtualizationEnabled); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ContextSwitches) -{ - sortResult = uintcmp(processItem1->ContextSwitchesDelta.Value, processItem2->ContextSwitchesDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ContextSwitchesDelta) -{ - sortResult = intcmp((LONG)processItem1->ContextSwitchesDelta.Delta, (LONG)processItem2->ContextSwitchesDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PageFaultsDelta) -{ - sortResult = uintcmp(processItem1->PageFaultsDelta.Delta, processItem2->PageFaultsDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoReads) -{ - sortResult = uint64cmp(processItem1->IoReadCountDelta.Value, processItem2->IoReadCountDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoWrites) -{ - sortResult = uint64cmp(processItem1->IoWriteCountDelta.Value, processItem2->IoWriteCountDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoOther) -{ - sortResult = uint64cmp(processItem1->IoOtherCountDelta.Value, processItem2->IoOtherCountDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoReadBytes) -{ - sortResult = uint64cmp(processItem1->IoReadDelta.Value, processItem2->IoReadDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoWriteBytes) -{ - sortResult = uint64cmp(processItem1->IoWriteDelta.Value, processItem2->IoWriteDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoOtherBytes) -{ - sortResult = uint64cmp(processItem1->IoOtherDelta.Value, processItem2->IoOtherDelta.Value); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoReadsDelta) -{ - sortResult = uint64cmp(processItem1->IoReadCountDelta.Delta, processItem2->IoReadCountDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoWritesDelta) -{ - sortResult = uint64cmp(processItem1->IoWriteCountDelta.Delta, processItem2->IoWriteCountDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoOtherDelta) -{ - sortResult = uint64cmp(processItem1->IoOtherCountDelta.Delta, processItem2->IoOtherCountDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(OsContext) -{ - PhpUpdateProcessOsContext(node1); - PhpUpdateProcessOsContext(node2); - sortResult = uintcmp(node1->OsContextVersion, node2->OsContextVersion); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PagedPool) -{ - sortResult = uintptrcmp(processItem1->VmCounters.QuotaPagedPoolUsage, processItem2->VmCounters.QuotaPagedPoolUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PeakPagedPool) -{ - sortResult = uintptrcmp(processItem1->VmCounters.QuotaPeakPagedPoolUsage, processItem2->VmCounters.QuotaPeakPagedPoolUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(NonPagedPool) -{ - sortResult = uintptrcmp(processItem1->VmCounters.QuotaNonPagedPoolUsage, processItem2->VmCounters.QuotaNonPagedPoolUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PeakNonPagedPool) -{ - sortResult = uintptrcmp(processItem1->VmCounters.QuotaPeakNonPagedPoolUsage, processItem2->VmCounters.QuotaPeakNonPagedPoolUsage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(MinimumWorkingSet) -{ - PhpUpdateProcessNodeQuotaLimits(node1); - PhpUpdateProcessNodeQuotaLimits(node2); - sortResult = uintptrcmp(node1->MinimumWorkingSetSize, node2->MinimumWorkingSetSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(MaximumWorkingSet) -{ - PhpUpdateProcessNodeQuotaLimits(node1); - PhpUpdateProcessNodeQuotaLimits(node2); - sortResult = uintptrcmp(node1->MaximumWorkingSetSize, node2->MaximumWorkingSetSize); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PrivateBytesDelta) -{ - sortResult = intptrcmp(processItem1->PrivateBytesDelta.Delta, processItem2->PrivateBytesDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Subsystem) -{ - PhpUpdateProcessNodeImage(node1); - PhpUpdateProcessNodeImage(node2); - sortResult = intcmp(node1->ImageSubsystem, node2->ImageSubsystem); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(PackageName) -{ - sortResult = PhCompareStringWithNull(processItem1->PackageFullName, processItem2->PackageFullName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(AppId) -{ - PhpUpdateProcessNodeAppId(node1); - PhpUpdateProcessNodeAppId(node2); - sortResult = PhCompareStringWithNull(node1->AppIdText, node2->AppIdText, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(DpiAwareness) -{ - PhpUpdateProcessNodeDpiAwareness(node1); - PhpUpdateProcessNodeDpiAwareness(node2); - sortResult = uintcmp(node1->DpiAwareness, node2->DpiAwareness); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CfGuard) -{ - PhpUpdateProcessNodeImage(node1); - PhpUpdateProcessNodeImage(node2); - sortResult = intcmp( - node1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF, - node2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF - ); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(TimeStamp) -{ - PhpUpdateProcessNodeImage(node1); - PhpUpdateProcessNodeImage(node2); - sortResult = uintcmp(node1->ImageTimeDateStamp, node2->ImageTimeDateStamp); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileModifiedTime) -{ - PhpUpdateProcessNodeFileAttributes(node1); - PhpUpdateProcessNodeFileAttributes(node2); - sortResult = int64cmp(node1->FileLastWriteTime.QuadPart, node2->FileLastWriteTime.QuadPart); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(FileSize) -{ - PhpUpdateProcessNodeFileAttributes(node1); - PhpUpdateProcessNodeFileAttributes(node2); - sortResult = int64cmp(node1->FileEndOfFile.QuadPart, node2->FileEndOfFile.QuadPart); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpProcessTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_PROCESS_NODE node; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &ProcessTreeListCm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - node = (PPH_PROCESS_NODE)getChildren->Node; - - if (ProcessTreeListSortOrder == NoSortOrder) - { - if (!node) - { - getChildren->Children = (PPH_TREENEW_NODE *)ProcessNodeRootList->Items; - getChildren->NumberOfChildren = ProcessNodeRootList->Count; - } - else - { - getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; - getChildren->NumberOfChildren = node->Children->Count; - } - } - else - { - if (!node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Name), - SORT_FUNCTION(Pid), - SORT_FUNCTION(Cpu), - SORT_FUNCTION(IoTotalRate), - SORT_FUNCTION(PrivateBytes), - SORT_FUNCTION(UserName), - SORT_FUNCTION(Description), - SORT_FUNCTION(CompanyName), - SORT_FUNCTION(Version), - SORT_FUNCTION(FileName), - SORT_FUNCTION(CommandLine), - SORT_FUNCTION(PeakPrivateBytes), - SORT_FUNCTION(WorkingSet), - SORT_FUNCTION(PeakWorkingSet), - SORT_FUNCTION(PrivateWs), - SORT_FUNCTION(SharedWs), - SORT_FUNCTION(ShareableWs), - SORT_FUNCTION(VirtualSize), - SORT_FUNCTION(PeakVirtualSize), - SORT_FUNCTION(PageFaults), - SORT_FUNCTION(SessionId), - SORT_FUNCTION(BasePriority), // Priority Class - SORT_FUNCTION(BasePriority), - SORT_FUNCTION(Threads), - SORT_FUNCTION(Handles), - SORT_FUNCTION(GdiHandles), - SORT_FUNCTION(UserHandles), - SORT_FUNCTION(IoRoRate), - SORT_FUNCTION(IoWRate), - SORT_FUNCTION(Integrity), - SORT_FUNCTION(IoPriority), - SORT_FUNCTION(PagePriority), - SORT_FUNCTION(StartTime), - SORT_FUNCTION(TotalCpuTime), - SORT_FUNCTION(KernelCpuTime), - SORT_FUNCTION(UserCpuTime), - SORT_FUNCTION(VerificationStatus), - SORT_FUNCTION(VerifiedSigner), - SORT_FUNCTION(Aslr), - SORT_FUNCTION(RelativeStartTime), - SORT_FUNCTION(Bits), - SORT_FUNCTION(Elevation), - SORT_FUNCTION(WindowTitle), - SORT_FUNCTION(WindowStatus), - SORT_FUNCTION(Cycles), - SORT_FUNCTION(CyclesDelta), - SORT_FUNCTION(Cpu), // CPU History - SORT_FUNCTION(PrivateBytes), // Private Bytes History - SORT_FUNCTION(IoTotalRate), // I/O History - SORT_FUNCTION(Dep), - SORT_FUNCTION(Virtualized), - SORT_FUNCTION(ContextSwitches), - SORT_FUNCTION(ContextSwitchesDelta), - SORT_FUNCTION(PageFaultsDelta), - SORT_FUNCTION(IoReads), - SORT_FUNCTION(IoWrites), - SORT_FUNCTION(IoOther), - SORT_FUNCTION(IoReadBytes), - SORT_FUNCTION(IoWriteBytes), - SORT_FUNCTION(IoOtherBytes), - SORT_FUNCTION(IoReadsDelta), - SORT_FUNCTION(IoWritesDelta), - SORT_FUNCTION(IoOtherDelta), - SORT_FUNCTION(OsContext), - SORT_FUNCTION(PagedPool), - SORT_FUNCTION(PeakPagedPool), - SORT_FUNCTION(NonPagedPool), - SORT_FUNCTION(PeakNonPagedPool), - SORT_FUNCTION(MinimumWorkingSet), - SORT_FUNCTION(MaximumWorkingSet), - SORT_FUNCTION(PrivateBytesDelta), - SORT_FUNCTION(Subsystem), - SORT_FUNCTION(PackageName), - SORT_FUNCTION(AppId), - SORT_FUNCTION(DpiAwareness), - SORT_FUNCTION(CfGuard), - SORT_FUNCTION(TimeStamp), - SORT_FUNCTION(FileModifiedTime), - SORT_FUNCTION(FileSize) - }; - static PH_INITONCE initOnce = PH_INITONCE_INIT; - int (__cdecl *sortFunction)(const void *, const void *); - - if (PhBeginInitOnce(&initOnce)) - { - if (WindowsVersion >= WINDOWS_7) - { - sortFunctions[PHPRTLC_PRIVATEWS] = SORT_FUNCTION(PrivateWsWin7); - sortFunctions[PHPRTLC_CYCLES] = SORT_FUNCTION(CyclesWin7); - sortFunctions[PHPRTLC_CYCLESDELTA] = SORT_FUNCTION(CyclesDeltaWin7); - } - - PhEndInitOnce(&initOnce); - } - - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)ProcessNodeList->Items, - ProcessNodeList->Count, - ProcessTreeListSortColumn, - ProcessTreeListSortOrder, - &ProcessTreeListCm - )) - { - if (ProcessTreeListSortColumn < PHPRTLC_MAXIMUM) - sortFunction = sortFunctions[ProcessTreeListSortColumn]; - else - sortFunction = NULL; - - if (sortFunction) - { - qsort(ProcessNodeList->Items, ProcessNodeList->Count, sizeof(PVOID), sortFunction); - } - } - - getChildren->Children = (PPH_TREENEW_NODE *)ProcessNodeList->Items; - getChildren->NumberOfChildren = ProcessNodeList->Count; - } - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - node = (PPH_PROCESS_NODE)isLeaf->Node; - - if (ProcessTreeListSortOrder == NoSortOrder) - isLeaf->IsLeaf = node->Children->Count == 0; - else - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_PROCESS_ITEM processItem; - - node = (PPH_PROCESS_NODE)getCellText->Node; - processItem = node->ProcessItem; - - switch (getCellText->Id) - { - case PHPRTLC_NAME: - getCellText->Text = processItem->ProcessName->sr; - break; - case PHPRTLC_PID: - PhInitializeStringRefLongHint(&getCellText->Text, processItem->ProcessIdString); - break; - case PHPRTLC_CPU: - { - FLOAT cpuUsage = 0; - - PhpAggregateFieldIfNeeded(node, AggregateTypeFloat, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUsage), &cpuUsage); - cpuUsage *= 100; - - if (cpuUsage >= 0.01) - { - PH_FORMAT format; - SIZE_T returnLength; - - PhInitFormatF(&format, cpuUsage, 2); - - if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) - { - getCellText->Text.Buffer = node->CpuUsageText; - getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator - } - } - else if (cpuUsage != 0 && PhCsShowCpuBelow001) - { - PH_FORMAT format[2]; - SIZE_T returnLength; - - PhInitFormatS(&format[0], L"< "); - PhInitFormatF(&format[1], 0.01, 2); - - if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) - { - getCellText->Text.Buffer = node->CpuUsageText; - getCellText->Text.Length = returnLength - sizeof(WCHAR); - } - } - } - break; - case PHPRTLC_IOTOTALRATE: - { - ULONG64 number = 0; - - if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) // delta is wrong on first run of process provider - { - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number); - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number); - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number); - number *= 1000; - number /= PhCsUpdateInterval; - } - - if (number != 0) - { - PH_FORMAT format[2]; - - PhInitFormatSize(&format[0], number); - PhInitFormatS(&format[1], L"/s"); - PhMoveReference(&node->IoTotalRateText, PhFormat(format, 2, 0)); - getCellText->Text = node->IoTotalRateText->sr; - } - } - break; - case PHPRTLC_PRIVATEBYTES: - { - SIZE_T value = 0; - PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PagefileUsage), &value); - PhMoveReference(&node->PrivateBytesText, PhFormatSize(value, -1)); - getCellText->Text = node->PrivateBytesText->sr; - } - break; - case PHPRTLC_USERNAME: - getCellText->Text = PhGetStringRef(processItem->UserName); - break; - case PHPRTLC_DESCRIPTION: - if (processItem->VersionInfo.FileDescription) - getCellText->Text = processItem->VersionInfo.FileDescription->sr; - else - getCellText->Text = node->DescriptionText; - break; - case PHPRTLC_COMPANYNAME: - getCellText->Text = PhGetStringRef(processItem->VersionInfo.CompanyName); - break; - case PHPRTLC_VERSION: - getCellText->Text = PhGetStringRef(processItem->VersionInfo.FileVersion); - break; - case PHPRTLC_FILENAME: - getCellText->Text = PhGetStringRef(processItem->FileName); - break; - case PHPRTLC_COMMANDLINE: - getCellText->Text = PhGetStringRef(processItem->CommandLine); - break; - case PHPRTLC_PEAKPRIVATEBYTES: - PhMoveReference(&node->PeakPrivateBytesText, PhFormatSize(processItem->VmCounters.PeakPagefileUsage, -1)); - getCellText->Text = node->PeakPrivateBytesText->sr; - break; - case PHPRTLC_WORKINGSET: - { - SIZE_T value = 0; - PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.WorkingSetSize), &value); - PhMoveReference(&node->WorkingSetText, PhFormatSize(value, -1)); - getCellText->Text = node->WorkingSetText->sr; - } - break; - case PHPRTLC_PEAKWORKINGSET: - PhMoveReference(&node->PeakWorkingSetText, PhFormatSize(processItem->VmCounters.PeakWorkingSetSize, -1)); - getCellText->Text = node->PeakWorkingSetText->sr; - break; - case PHPRTLC_PRIVATEWS: - if (WindowsVersion >= WINDOWS_7) - { - PhMoveReference(&node->PrivateWsText, PhFormatSize(processItem->WorkingSetPrivateSize, -1)); - } - else - { - PhpUpdateProcessNodeWsCounters(node); - PhMoveReference(&node->PrivateWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfPrivatePages * PAGE_SIZE, -1)); - } - getCellText->Text = node->PrivateWsText->sr; - break; - case PHPRTLC_SHAREDWS: - PhpUpdateProcessNodeWsCounters(node); - PhMoveReference(&node->SharedWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfSharedPages * PAGE_SIZE, -1)); - getCellText->Text = node->SharedWsText->sr; - break; - case PHPRTLC_SHAREABLEWS: - PhpUpdateProcessNodeWsCounters(node); - PhMoveReference(&node->ShareableWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfShareablePages * PAGE_SIZE, -1)); - getCellText->Text = node->ShareableWsText->sr; - break; - case PHPRTLC_VIRTUALSIZE: - PhMoveReference(&node->VirtualSizeText, PhFormatSize(processItem->VmCounters.VirtualSize, -1)); - getCellText->Text = node->VirtualSizeText->sr; - break; - case PHPRTLC_PEAKVIRTUALSIZE: - PhMoveReference(&node->PeakVirtualSizeText, PhFormatSize(processItem->VmCounters.PeakVirtualSize, -1)); - getCellText->Text = node->PeakVirtualSizeText->sr; - break; - case PHPRTLC_PAGEFAULTS: - PhMoveReference(&node->PageFaultsText, PhFormatUInt64(processItem->VmCounters.PageFaultCount, TRUE)); - getCellText->Text = node->PageFaultsText->sr; - break; - case PHPRTLC_SESSIONID: - PhInitializeStringRefLongHint(&getCellText->Text, processItem->SessionIdString); - break; - case PHPRTLC_PRIORITYCLASS: - PhInitializeStringRefLongHint(&getCellText->Text, PhGetProcessPriorityClassString(processItem->PriorityClass)); - break; - case PHPRTLC_BASEPRIORITY: - PhPrintInt32(node->BasePriorityText, processItem->BasePriority); - PhInitializeStringRefLongHint(&getCellText->Text, node->BasePriorityText); - break; - case PHPRTLC_THREADS: - { - ULONG value = 0; - PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfThreads), &value); - PhpFormatInt32GroupDigits(value, node->ThreadsText, sizeof(node->ThreadsText), &getCellText->Text); - } - break; - case PHPRTLC_HANDLES: - { - ULONG value = 0; - PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfHandles), &value); - PhpFormatInt32GroupDigits(value, node->HandlesText, sizeof(node->HandlesText), &getCellText->Text); - } - break; - case PHPRTLC_GDIHANDLES: - PhpUpdateProcessNodeGdiUserHandles(node); - PhpFormatInt32GroupDigits(node->GdiHandles, node->GdiHandlesText, sizeof(node->GdiHandlesText), &getCellText->Text); - break; - case PHPRTLC_USERHANDLES: - PhpUpdateProcessNodeGdiUserHandles(node); - PhpFormatInt32GroupDigits(node->UserHandles, node->UserHandlesText, sizeof(node->UserHandlesText), &getCellText->Text); - break; - case PHPRTLC_IORORATE: - { - ULONG64 number = 0; - - if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) - { - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number); - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number); - number *= 1000; - number /= PhCsUpdateInterval; - } - - if (number != 0) - { - PH_FORMAT format[2]; - - PhInitFormatSize(&format[0], number); - PhInitFormatS(&format[1], L"/s"); - PhMoveReference(&node->IoRoRateText, PhFormat(format, 2, 0)); - getCellText->Text = node->IoRoRateText->sr; - } - } - break; - case PHPRTLC_IOWRATE: - { - ULONG64 number = 0; - - if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) - { - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number); - number *= 1000; - number /= PhCsUpdateInterval; - } - - if (number != 0) - { - PH_FORMAT format[2]; - - PhInitFormatSize(&format[0], number); - PhInitFormatS(&format[1], L"/s"); - PhMoveReference(&node->IoWRateText, PhFormat(format, 2, 0)); - getCellText->Text = node->IoWRateText->sr; - } - } - break; - case PHPRTLC_INTEGRITY: - if (processItem->IntegrityString) - PhInitializeStringRefLongHint(&getCellText->Text, processItem->IntegrityString); - break; - case PHPRTLC_IOPRIORITY: - PhpUpdateProcessNodeIoPagePriority(node); - if (node->IoPriority != -1 && node->IoPriority < MaxIoPriorityTypes) - PhInitializeStringRefLongHint(&getCellText->Text, PhIoPriorityHintNames[node->IoPriority]); - break; - case PHPRTLC_PAGEPRIORITY: - PhpUpdateProcessNodeIoPagePriority(node); - if (node->PagePriority != -1 && node->PagePriority <= MEMORY_PRIORITY_NORMAL) - PhInitializeStringRefLongHint(&getCellText->Text, PhPagePriorityNames[node->PagePriority]); - break; - case PHPRTLC_STARTTIME: - if (processItem->CreateTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - - PhLargeIntegerToLocalSystemTime(&systemTime, &processItem->CreateTime); - PhMoveReference(&node->StartTimeText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->StartTimeText->sr; - } - break; - case PHPRTLC_TOTALCPUTIME: - PhMoveReference(&node->TotalCpuTimeText, PhFormatTimeSpan( - processItem->KernelTime.QuadPart + processItem->UserTime.QuadPart, - PH_TIMESPAN_HMSM - )); - getCellText->Text = node->TotalCpuTimeText->sr; - break; - case PHPRTLC_KERNELCPUTIME: - PhMoveReference(&node->KernelCpuTimeText, PhFormatTimeSpan( - processItem->KernelTime.QuadPart, - PH_TIMESPAN_HMSM - )); - getCellText->Text = node->KernelCpuTimeText->sr; - break; - case PHPRTLC_USERCPUTIME: - PhMoveReference(&node->UserCpuTimeText, PhFormatTimeSpan( - processItem->UserTime.QuadPart, - PH_TIMESPAN_HMSM - )); - getCellText->Text = node->UserCpuTimeText->sr; - break; - case PHPRTLC_VERIFICATIONSTATUS: - if (processItem->VerifyResult == VrTrusted) - PhInitializeStringRef(&getCellText->Text, L"Trusted"); - break; - case PHPRTLC_VERIFIEDSIGNER: - getCellText->Text = PhGetStringRef(processItem->VerifySignerName); - break; - case PHPRTLC_ASLR: - PhpUpdateProcessNodeImage(node); - - if (WindowsVersion >= WINDOWS_VISTA) - { - if (node->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) - PhInitializeStringRef(&getCellText->Text, L"ASLR"); - } - else - { - PhInitializeStringRef(&getCellText->Text, L"N/A"); - } - break; - case PHPRTLC_RELATIVESTARTTIME: - { - if (processItem->CreateTime.QuadPart != 0) - { - LARGE_INTEGER currentTime; - PPH_STRING startTimeString; - - PhQuerySystemTime(¤tTime); - startTimeString = PhFormatTimeSpanRelative(currentTime.QuadPart - processItem->CreateTime.QuadPart); - PhMoveReference(&node->RelativeStartTimeText, PhConcatStrings2(startTimeString->Buffer, L" ago")); - PhDereferenceObject(startTimeString); - getCellText->Text = node->RelativeStartTimeText->sr; - } - } - break; - case PHPRTLC_BITS: -#ifdef _WIN64 - if (processItem->IsWow64Valid) - PhInitializeStringRef(&getCellText->Text, processItem->IsWow64 ? L"32" : L"64"); -#else - PhInitializeStringRef(&getCellText->Text, L"32"); -#endif - break; - case PHPRTLC_ELEVATION: - { - PWSTR type; - - if (WINDOWS_HAS_UAC) - { - switch (processItem->ElevationType) - { - case TokenElevationTypeDefault: - type = L"N/A"; - break; - case TokenElevationTypeLimited: - type = L"Limited"; - break; - case TokenElevationTypeFull: - type = L"Full"; - break; - default: - type = L"N/A"; - break; - } - } - else - { - type = L""; - } - - PhInitializeStringRefLongHint(&getCellText->Text, type); - } - break; - case PHPRTLC_WINDOWTITLE: - PhpUpdateProcessNodeWindow(node); - PhSwapReference(&node->WindowTitleText, node->WindowText); - getCellText->Text = PhGetStringRef(node->WindowTitleText); - break; - case PHPRTLC_WINDOWSTATUS: - PhpUpdateProcessNodeWindow(node); - - if (node->WindowHandle) - PhInitializeStringRef(&getCellText->Text, node->WindowHung ? L"Not responding" : L"Running"); - - break; - case PHPRTLC_CYCLES: - if (WindowsVersion >= WINDOWS_7) - { - ULONG64 value = 0; - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Value), &value); - - if (value != 0) - { - PhMoveReference(&node->CyclesText, PhFormatUInt64(value, TRUE)); - getCellText->Text = node->CyclesText->sr; - } - } - else - { - getCellText->Text = PhGetStringRef(node->CyclesText); - } - break; - case PHPRTLC_CYCLESDELTA: - if (WindowsVersion >= WINDOWS_7) - { - ULONG64 value = 0; - PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Delta), &value); - - if (value != 0) - { - PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(value, TRUE)); - getCellText->Text = node->CyclesDeltaText->sr; - } - } - else - { - getCellText->Text = PhGetStringRef(node->CyclesDeltaText); - } - break; - case PHPRTLC_DEP: - PhpUpdateProcessNodeDepStatus(node); - - if (node->DepStatus & PH_PROCESS_DEP_ENABLED) - { - if (node->DepStatus & PH_PROCESS_DEP_PERMANENT) - PhInitializeStringRef(&getCellText->Text, L"DEP (permanent)"); - else - PhInitializeStringRef(&getCellText->Text, L"DEP"); - } - - break; - case PHPRTLC_VIRTUALIZED: - PhpUpdateProcessNodeToken(node); - - if (node->VirtualizationEnabled) - PhInitializeStringRef(&getCellText->Text, L"Virtualized"); - - break; - case PHPRTLC_CONTEXTSWITCHES: - if (processItem->ContextSwitchesDelta.Value != 0) - { - PhMoveReference(&node->ContextSwitchesText, PhFormatUInt64(processItem->ContextSwitchesDelta.Value, TRUE)); - getCellText->Text = node->ContextSwitchesText->sr; - } - break; - case PHPRTLC_CONTEXTSWITCHESDELTA: - if ((LONG)processItem->ContextSwitchesDelta.Delta > 0) // the delta may be negative if a thread exits - just don't show anything - { - PhMoveReference(&node->ContextSwitchesDeltaText, PhFormatUInt64(processItem->ContextSwitchesDelta.Delta, TRUE)); - getCellText->Text = node->ContextSwitchesDeltaText->sr; - } - break; - case PHPRTLC_PAGEFAULTSDELTA: - if (processItem->PageFaultsDelta.Delta != 0) - { - PhMoveReference(&node->PageFaultsDeltaText, PhFormatUInt64(processItem->PageFaultsDelta.Delta, TRUE)); - getCellText->Text = node->PageFaultsDeltaText->sr; - } - break; - case PHPRTLC_IOREADS: - if (processItem->IoReadCountDelta.Value != 0) - { - PhMoveReference(&node->IoGroupText[0], PhFormatUInt64(processItem->IoReadCountDelta.Value, TRUE)); - getCellText->Text = node->IoGroupText[0]->sr; - } - break; - case PHPRTLC_IOWRITES: - if (processItem->IoWriteCountDelta.Value != 0) - { - PhMoveReference(&node->IoGroupText[1], PhFormatUInt64(processItem->IoWriteCountDelta.Value, TRUE)); - getCellText->Text = node->IoGroupText[1]->sr; - } - break; - case PHPRTLC_IOOTHER: - if (processItem->IoOtherCountDelta.Value != 0) - { - PhMoveReference(&node->IoGroupText[2], PhFormatUInt64(processItem->IoOtherCountDelta.Value, TRUE)); - getCellText->Text = node->IoGroupText[2]->sr; - } - break; - case PHPRTLC_IOREADBYTES: - if (processItem->IoReadDelta.Value != 0) - { - PhMoveReference(&node->IoGroupText[3], PhFormatSize(processItem->IoReadDelta.Value, -1)); - getCellText->Text = node->IoGroupText[3]->sr; - } - break; - case PHPRTLC_IOWRITEBYTES: - if (processItem->IoWriteDelta.Value != 0) - { - PhMoveReference(&node->IoGroupText[4], PhFormatSize(processItem->IoWriteDelta.Value, -1)); - getCellText->Text = node->IoGroupText[4]->sr; - } - break; - case PHPRTLC_IOOTHERBYTES: - if (processItem->IoOtherDelta.Value != 0) - { - PhMoveReference(&node->IoGroupText[5], PhFormatSize(processItem->IoOtherDelta.Value, -1)); - getCellText->Text = node->IoGroupText[5]->sr; - } - break; - case PHPRTLC_IOREADSDELTA: - if (processItem->IoReadCountDelta.Delta != 0) - { - PhMoveReference(&node->IoGroupText[6], PhFormatUInt64(processItem->IoReadCountDelta.Delta, TRUE)); - getCellText->Text = node->IoGroupText[6]->sr; - } - break; - case PHPRTLC_IOWRITESDELTA: - if (processItem->IoWriteCountDelta.Delta != 0) - { - PhMoveReference(&node->IoGroupText[7], PhFormatUInt64(processItem->IoWriteCountDelta.Delta, TRUE)); - getCellText->Text = node->IoGroupText[7]->sr; - } - break; - case PHPRTLC_IOOTHERDELTA: - if (processItem->IoOtherCountDelta.Delta != 0) - { - PhMoveReference(&node->IoGroupText[8], PhFormatUInt64(processItem->IoOtherCountDelta.Delta, TRUE)); - getCellText->Text = node->IoGroupText[8]->sr; - } - break; - case PHPRTLC_OSCONTEXT: - PhpUpdateProcessOsContext(node); - - if (WindowsVersion >= WINDOWS_7) - { - switch (node->OsContextVersion) - { - case WINDOWS_10: - PhInitializeStringRef(&getCellText->Text, L"Windows 10"); - break; - case WINDOWS_8_1: - PhInitializeStringRef(&getCellText->Text, L"Windows 8.1"); - break; - case WINDOWS_8: - PhInitializeStringRef(&getCellText->Text, L"Windows 8"); - break; - case WINDOWS_7: - PhInitializeStringRef(&getCellText->Text, L"Windows 7"); - break; - case WINDOWS_VISTA: - PhInitializeStringRef(&getCellText->Text, L"Windows Vista"); - break; - case WINDOWS_XP: - PhInitializeStringRef(&getCellText->Text, L"Windows XP"); - break; - } - } - else - { - PhInitializeStringRef(&getCellText->Text, L"N/A"); - } - break; - case PHPRTLC_PAGEDPOOL: - PhMoveReference(&node->PagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPagedPoolUsage, -1)); - getCellText->Text = node->PagedPoolText->sr; - break; - case PHPRTLC_PEAKPAGEDPOOL: - PhMoveReference(&node->PeakPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakPagedPoolUsage, -1)); - getCellText->Text = node->PeakPagedPoolText->sr; - break; - case PHPRTLC_NONPAGEDPOOL: - PhMoveReference(&node->NonPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaNonPagedPoolUsage, -1)); - getCellText->Text = node->NonPagedPoolText->sr; - break; - case PHPRTLC_PEAKNONPAGEDPOOL: - PhMoveReference(&node->PeakNonPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakNonPagedPoolUsage, -1)); - getCellText->Text = node->PeakNonPagedPoolText->sr; - break; - case PHPRTLC_MINIMUMWORKINGSET: - PhpUpdateProcessNodeQuotaLimits(node); - PhMoveReference(&node->MinimumWorkingSetText, PhFormatSize(node->MinimumWorkingSetSize, -1)); - getCellText->Text = node->MinimumWorkingSetText->sr; - break; - case PHPRTLC_MAXIMUMWORKINGSET: - PhpUpdateProcessNodeQuotaLimits(node); - PhMoveReference(&node->MaximumWorkingSetText, PhFormatSize(node->MaximumWorkingSetSize, -1)); - getCellText->Text = node->MaximumWorkingSetText->sr; - break; - case PHPRTLC_PRIVATEBYTESDELTA: - { - LONG_PTR delta; - - delta = processItem->PrivateBytesDelta.Delta; - - if (delta != 0) - { - PH_FORMAT format[2]; - - if (delta > 0) - { - PhInitFormatC(&format[0], '+'); - } - else - { - PhInitFormatC(&format[0], '-'); - delta = -delta; - } - - format[1].Type = SizeFormatType | FormatUseRadix; - format[1].Radix = (UCHAR)PhMaxSizeUnit; - format[1].u.Size = delta; - - PhMoveReference(&node->PrivateBytesDeltaText, PhFormat(format, 2, 0)); - getCellText->Text = node->PrivateBytesDeltaText->sr; - } - } - break; - case PHPRTLC_SUBSYSTEM: - PhpUpdateProcessNodeImage(node); - - switch (node->ImageSubsystem) - { - case 0: - break; - case IMAGE_SUBSYSTEM_NATIVE: - PhInitializeStringRef(&getCellText->Text, L"Native"); - break; - case IMAGE_SUBSYSTEM_WINDOWS_GUI: - PhInitializeStringRef(&getCellText->Text, L"Windows"); - break; - case IMAGE_SUBSYSTEM_WINDOWS_CUI: - PhInitializeStringRef(&getCellText->Text, L"Windows console"); - break; - case IMAGE_SUBSYSTEM_OS2_CUI: - PhInitializeStringRef(&getCellText->Text, L"OS/2"); - break; - case IMAGE_SUBSYSTEM_POSIX_CUI: - PhInitializeStringRef(&getCellText->Text, L"POSIX"); - break; - default: - PhInitializeStringRef(&getCellText->Text, L"Unknown"); - break; - } - break; - case PHPRTLC_PACKAGENAME: - getCellText->Text = PhGetStringRef(processItem->PackageFullName); - break; - case PHPRTLC_APPID: - PhpUpdateProcessNodeAppId(node); - getCellText->Text = PhGetStringRef(node->AppIdText); - break; - case PHPRTLC_DPIAWARENESS: - PhpUpdateProcessNodeDpiAwareness(node); - - switch (node->DpiAwareness) - { - case 0: - break; - case 1: - PhInitializeStringRef(&getCellText->Text, L"Unaware"); - break; - case 2: - PhInitializeStringRef(&getCellText->Text, L"System aware"); - break; - case 3: - PhInitializeStringRef(&getCellText->Text, L"Per-monitor aware"); - break; - } - break; - case PHPRTLC_CFGUARD: - PhpUpdateProcessNodeImage(node); - - if (WindowsVersion >= WINDOWS_8_1) - { - if (node->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) - PhInitializeStringRef(&getCellText->Text, L"CF Guard"); - } - else - { - PhInitializeStringRef(&getCellText->Text, L"N/A"); - } - break; - case PHPRTLC_TIMESTAMP: - PhpUpdateProcessNodeImage(node); - - if (node->ImageTimeDateStamp != 0) - { - LARGE_INTEGER time; - SYSTEMTIME systemTime; - - RtlSecondsSince1970ToTime(node->ImageTimeDateStamp, &time); - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - - PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->TimeStampText->sr; - } - break; - case PHPRTLC_FILEMODIFIEDTIME: - PhpUpdateProcessNodeFileAttributes(node); - - if (node->FileLastWriteTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - - PhLargeIntegerToLocalSystemTime(&systemTime, &node->FileLastWriteTime); - PhMoveReference(&node->FileModifiedTimeText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->FileModifiedTimeText->sr; - } - break; - case PHPRTLC_FILESIZE: - PhpUpdateProcessNodeFileAttributes(node); - - if (node->FileEndOfFile.QuadPart != -1) - { - PhMoveReference(&node->FileSizeText, PhFormatSize(node->FileEndOfFile.QuadPart, -1)); - getCellText->Text = node->FileSizeText->sr; - } - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; - PPH_PROCESS_ITEM processItem; - - node = (PPH_PROCESS_NODE)getNodeColor->Node; - processItem = node->ProcessItem; - - if (PhPluginsEnabled) - { - PH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor; - - getHighlightingColor.Parameter = processItem; - getHighlightingColor.BackColor = RGB(0xff, 0xff, 0xff); - getHighlightingColor.Handled = FALSE; - getHighlightingColor.Cache = FALSE; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), &getHighlightingColor); - - if (getHighlightingColor.Handled) - { - getNodeColor->BackColor = getHighlightingColor.BackColor; - getNodeColor->Flags = TN_AUTO_FORECOLOR; - - if (getHighlightingColor.Cache) - getNodeColor->Flags |= TN_CACHE; - - return TRUE; - } - } - - if (!processItem) - ; // Dummy - else if (PhCsUseColorDebuggedProcesses && processItem->IsBeingDebugged) - getNodeColor->BackColor = PhCsColorDebuggedProcesses; - else if (PhCsUseColorSuspended && processItem->IsSuspended) - getNodeColor->BackColor = PhCsColorSuspended; - else if (PhCsUseColorElevatedProcesses && processItem->IsElevated) - getNodeColor->BackColor = PhCsColorElevatedProcesses; - else if (PhCsUseColorPicoProcesses && processItem->IsSubsystemProcess) - getNodeColor->BackColor = PhCsColorPicoProcesses; - else if (PhCsUseColorImmersiveProcesses && processItem->IsImmersive) - getNodeColor->BackColor = PhCsColorImmersiveProcesses; - else if (PhCsUseColorDotNet && processItem->IsDotNet) - getNodeColor->BackColor = PhCsColorDotNet; - else if (PhCsUseColorPacked && processItem->IsPacked) - getNodeColor->BackColor = PhCsColorPacked; - else if (PhCsUseColorWow64Processes && processItem->IsWow64) - getNodeColor->BackColor = PhCsColorWow64Processes; - else if (PhCsUseColorJobProcesses && processItem->IsInSignificantJob) - getNodeColor->BackColor = PhCsColorJobProcesses; - else if (PhCsUseColorServiceProcesses && processItem->ServiceList && processItem->ServiceList->Count != 0) - getNodeColor->BackColor = PhCsColorServiceProcesses; - else if ( - PhCsUseColorSystemProcesses && - processItem->UserName && - PhEqualString(processItem->UserName, PhLocalSystemName, TRUE) - ) - getNodeColor->BackColor = PhCsColorSystemProcesses; - else if ( - PhCsUseColorOwnProcesses && - processItem->UserName && - PhCurrentUserName && - PhEqualString(processItem->UserName, PhCurrentUserName, TRUE) - ) - getNodeColor->BackColor = PhCsColorOwnProcesses; - - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; - } - return TRUE; - case TreeNewGetNodeIcon: - { - PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; - - node = (PPH_PROCESS_NODE)getNodeIcon->Node; - - if (node->ProcessItem->SmallIcon) - { - getNodeIcon->Icon = node->ProcessItem->SmallIcon; - } - else - { - PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL); - } - - getNodeIcon->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - ULONG tickCount; - - node = (PPH_PROCESS_NODE)getCellTooltip->Node; - - if (getCellTooltip->Column->Id != 0) - return FALSE; - - tickCount = GetTickCount(); - - if ((LONG)(node->TooltipTextValidToTickCount - tickCount) < 0) - PhClearReference(&node->TooltipText); - if (!node->TooltipText) - node->TooltipText = PhGetProcessTooltipText(node->ProcessItem, &node->TooltipTextValidToTickCount); - - if (!PhIsNullOrEmptyString(node->TooltipText)) - { - getCellTooltip->Text = node->TooltipText->sr; - getCellTooltip->Unfolding = FALSE; - getCellTooltip->MaximumWidth = -1; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewCustomDraw: - { - PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; - PPH_PROCESS_ITEM processItem; - RECT rect; - PH_GRAPH_DRAW_INFO drawInfo; - - node = (PPH_PROCESS_NODE)customDraw->Node; - processItem = node->ProcessItem; - rect = customDraw->CellRect; - - if (rect.right - rect.left <= 1) - break; // nothing to draw - - // Generic graph pre-processing - switch (customDraw->Column->Id) - { - case PHPRTLC_CPUHISTORY: - case PHPRTLC_PRIVATEBYTESHISTORY: - case PHPRTLC_IOHISTORY: - memset(&drawInfo, 0, sizeof(PH_GRAPH_DRAW_INFO)); - drawInfo.Width = rect.right - rect.left - 1; // leave a small gap - drawInfo.Height = rect.bottom - rect.top - 1; // leave a small gap - drawInfo.Step = 2; - drawInfo.BackColor = RGB(0x00, 0x00, 0x00); - break; - } - - // Specific graph processing - switch (customDraw->Column->Id) - { - case PHPRTLC_CPUHISTORY: - { - drawInfo.Flags = PH_GRAPH_USE_LINE_2; - drawInfo.LineColor1 = PhCsColorCpuKernel; - drawInfo.LineColor2 = PhCsColorCpuUser; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorCpuKernel); - drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorCpuUser); - - PhGetDrawInfoGraphBuffers( - &node->CpuGraphBuffers, - &drawInfo, - processItem->CpuKernelHistory.Count - ); - - if (!node->CpuGraphBuffers.Valid) - { - PhCopyCircularBuffer_FLOAT(&processItem->CpuKernelHistory, - node->CpuGraphBuffers.Data1, drawInfo.LineDataCount); - PhCopyCircularBuffer_FLOAT(&processItem->CpuUserHistory, - node->CpuGraphBuffers.Data2, drawInfo.LineDataCount); - node->CpuGraphBuffers.Valid = TRUE; - } - } - break; - case PHPRTLC_PRIVATEBYTESHISTORY: - { - drawInfo.Flags = 0; - drawInfo.LineColor1 = PhCsColorPrivate; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPrivate); - - PhGetDrawInfoGraphBuffers( - &node->PrivateGraphBuffers, - &drawInfo, - processItem->PrivateBytesHistory.Count - ); - - if (!node->PrivateGraphBuffers.Valid) - { - ULONG i; - FLOAT total; - FLOAT max; - - for (i = 0; i < drawInfo.LineDataCount; i++) - { - node->PrivateGraphBuffers.Data1[i] = - (FLOAT)PhGetItemCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, i); - } - - // This makes it easier for the user to see what processes are hogging memory. - // Scaling is still *not* consistent across all graphs. - total = (FLOAT)PhPerfInformation.CommittedPages * PAGE_SIZE / 4; // divide by 4 to make the scaling a bit better - max = (FLOAT)processItem->VmCounters.PeakPagefileUsage; - - if (max < total) - max = total; - - if (max != 0) - { - // Scale the data. - PhDivideSinglesBySingle( - node->PrivateGraphBuffers.Data1, - max, - drawInfo.LineDataCount - ); - } - - node->PrivateGraphBuffers.Valid = TRUE; - } - } - break; - case PHPRTLC_IOHISTORY: - { - drawInfo.Flags = PH_GRAPH_USE_LINE_2; - drawInfo.LineColor1 = PhCsColorIoReadOther; - drawInfo.LineColor2 = PhCsColorIoWrite; - drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorIoReadOther); - drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorIoWrite); - - PhGetDrawInfoGraphBuffers( - &node->IoGraphBuffers, - &drawInfo, - processItem->IoReadHistory.Count - ); - - if (!node->IoGraphBuffers.Valid) - { - ULONG i; - FLOAT total; - FLOAT max = 0; - - for (i = 0; i < drawInfo.LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - node->IoGraphBuffers.Data1[i] = data1 = - (FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoReadHistory, i) + - (FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoOtherHistory, i); - node->IoGraphBuffers.Data2[i] = data2 = - (FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoWriteHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - // Make the scaling a bit more consistent across the processes. - // It does *not* scale all graphs using the same maximum. - total = (FLOAT)(PhIoReadDelta.Delta + PhIoWriteDelta.Delta + PhIoOtherDelta.Delta); - - if (max < total) - max = total; - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - node->IoGraphBuffers.Data1, - max, - drawInfo.LineDataCount - ); - PhDivideSinglesBySingle( - node->IoGraphBuffers.Data2, - max, - drawInfo.LineDataCount - ); - } - - node->IoGraphBuffers.Valid = TRUE; - } - } - break; - } - - // Draw the graph. - switch (customDraw->Column->Id) - { - case PHPRTLC_CPUHISTORY: - case PHPRTLC_PRIVATEBYTESHISTORY: - case PHPRTLC_IOHISTORY: - PhpNeedGraphContext(customDraw->Dc, drawInfo.Width, drawInfo.Height); - - if (GraphBits) - { - PhDrawGraphDirect(GraphContext, GraphBits, &drawInfo); - BitBlt( - customDraw->Dc, - rect.left, - rect.top + 1, // + 1 for small gap - drawInfo.Width, - drawInfo.Height, - GraphContext, - 0, - 0, - SRCCOPY - ); - } - - break; - } - } - return TRUE; - case TreeNewColumnResized: - { - PPH_TREENEW_COLUMN column = Parameter1; - ULONG i; - - if (column->Id == PHPRTLC_CPUHISTORY || column->Id == PHPRTLC_IOHISTORY || column->Id == PHPRTLC_PRIVATEBYTESHISTORY) - { - for (i = 0; i < ProcessNodeList->Count; i++) - { - node = ProcessNodeList->Items[i]; - - if (column->Id == PHPRTLC_CPUHISTORY) - node->CpuGraphBuffers.Valid = FALSE; - if (column->Id == PHPRTLC_IOHISTORY) - node->IoGraphBuffers.Valid = FALSE; - if (column->Id == PHPRTLC_PRIVATEBYTESHISTORY) - node->PrivateGraphBuffers.Valid = FALSE; - } - } - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &ProcessTreeListSortColumn, &ProcessTreeListSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(ProcessTreeListHandle, 0, -1); - break; - case VK_DELETE: - if (GetKeyState(VK_SHIFT) >= 0) - SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_TERMINATE, 0); - else - SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_TERMINATETREE, 0); - break; - case VK_RETURN: - if (GetKeyState(VK_CONTROL) >= 0) - SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_PROPERTIES, 0); - else - SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_OPENFILELOCATION, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = NoSortOrder; - PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - - if (data.ProcessedId == PH_TN_COLUMN_MENU_HIDE_COLUMN_ID || data.ProcessedId == PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID) - PhpUpdateNeedCyclesInformation(); - - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_PROPERTIES, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - PhShowProcessContextMenu(contextMenu); - } - return TRUE; - case TreeNewNodeExpanding: - { - node = Parameter1; - - if (PhCsPropagateCpuUsage) - PhUpdateProcessNode(node); - } - return TRUE; - } - - return FALSE; -} - -PPH_PROCESS_ITEM PhGetSelectedProcessItem( - VOID - ) -{ - PPH_PROCESS_ITEM processItem = NULL; - ULONG i; - - for (i = 0; i < ProcessNodeList->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; - - if (node->Node.Selected) - { - processItem = node->ProcessItem; - break; - } - } - - return processItem; -} - -VOID PhGetSelectedProcessItems( - _Out_ PPH_PROCESS_ITEM **Processes, - _Out_ PULONG NumberOfProcesses - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - for (i = 0; i < ProcessNodeList->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node->ProcessItem); - } - - *NumberOfProcesses = (ULONG)array.Count; - *Processes = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllProcessNodes( - VOID - ) -{ - TreeNew_DeselectRange(ProcessTreeListHandle, 0, -1); -} - -VOID PhExpandAllProcessNodes( - _In_ BOOLEAN Expand - ) -{ - ULONG i; - BOOLEAN needsRestructure = FALSE; - - for (i = 0; i < ProcessNodeList->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; - - if (node->Children->Count != 0 && node->Node.Expanded != Expand) - { - node->Node.Expanded = Expand; - needsRestructure = TRUE; - } - } - - if (needsRestructure) - TreeNew_NodesStructured(ProcessTreeListHandle); -} - -VOID PhInvalidateAllProcessNodes( - VOID - ) -{ - ULONG i; - - for (i = 0; i < ProcessNodeList->Count; i++) - { - PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; - - memset(node->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM); - PhInvalidateTreeNewNode(&node->Node, TN_CACHE_COLOR); - node->ValidMask = 0; - - // Invalidate graph buffers. - node->CpuGraphBuffers.Valid = FALSE; - node->PrivateGraphBuffers.Valid = FALSE; - node->IoGraphBuffers.Valid = FALSE; - } - - InvalidateRect(ProcessTreeListHandle, NULL, FALSE); -} - -VOID PhSelectAndEnsureVisibleProcessNode( - _In_ PPH_PROCESS_NODE ProcessNode - ) -{ - PhSelectAndEnsureVisibleProcessNodes(&ProcessNode, 1); -} - -VOID PhSelectAndEnsureVisibleProcessNodes( - _In_ PPH_PROCESS_NODE *ProcessNodes, - _In_ ULONG NumberOfProcessNodes - ) -{ - ULONG i; - PPH_PROCESS_NODE leader = NULL; - PPH_PROCESS_NODE node; - BOOLEAN needsRestructure = FALSE; - - PhDeselectAllProcessNodes(); - - for (i = 0; i < NumberOfProcessNodes; i++) - { - if (ProcessNodes[i]->Node.Visible) - { - leader = ProcessNodes[i]; - break; - } - } - - if (!leader) - return; - - // Expand recursively upwards, and select the nodes. - - for (i = 0; i < NumberOfProcessNodes; i++) - { - if (!ProcessNodes[i]->Node.Visible) - continue; - - node = ProcessNodes[i]->Parent; - - while (node) - { - if (!node->Node.Expanded) - needsRestructure = TRUE; - - node->Node.Expanded = TRUE; - node = node->Parent; - } - - ProcessNodes[i]->Node.Selected = TRUE; - } - - if (needsRestructure) - TreeNew_NodesStructured(ProcessTreeListHandle); - - TreeNew_SetFocusNode(ProcessTreeListHandle, &leader->Node); - TreeNew_SetMarkNode(ProcessTreeListHandle, &leader->Node); - TreeNew_EnsureVisible(ProcessTreeListHandle, &leader->Node); - TreeNew_InvalidateNode(ProcessTreeListHandle, &leader->Node); -} - -VOID PhpPopulateTableWithProcessNodes( - _In_ HWND TreeListHandle, - _In_ PPH_PROCESS_NODE Node, - _In_ ULONG Level, - _In_ PPH_STRING **Table, - _Inout_ PULONG Index, - _In_ PULONG DisplayToId, - _In_ ULONG Columns - ) -{ - ULONG i; - - for (i = 0; i < Columns; i++) - { - PH_TREENEW_GET_CELL_TEXT getCellText; - PPH_STRING text; - - getCellText.Node = &Node->Node; - getCellText.Id = DisplayToId[i]; - PhInitializeEmptyStringRef(&getCellText.Text); - TreeNew_GetCellText(TreeListHandle, &getCellText); - - if (i != 0) - { - text = PhaCreateStringEx(getCellText.Text.Buffer, getCellText.Text.Length); - } - else - { - // If this is the first column in the row, add some indentation. - text = PhaCreateStringEx( - NULL, - getCellText.Text.Length + Level * 2 * sizeof(WCHAR) - ); - wmemset(text->Buffer, ' ', Level * 2); - memcpy(&text->Buffer[Level * 2], getCellText.Text.Buffer, getCellText.Text.Length); - } - - Table[*Index][i] = text; - } - - (*Index)++; - - // Process the children. - for (i = 0; i < Node->Children->Count; i++) - { - PhpPopulateTableWithProcessNodes( - TreeListHandle, - Node->Children->Items[i], - Level + 1, - Table, - Index, - DisplayToId, - Columns - ); - } -} - -PPH_LIST PhGetProcessTreeListLines( - _In_ HWND TreeListHandle, - _In_ ULONG NumberOfNodes, - _In_ PPH_LIST RootNodes, - _In_ ULONG Mode - ) -{ - PH_AUTO_POOL autoPool; - PPH_LIST lines; - // The number of rows in the table (including +1 for the column headers). - ULONG rows; - // The number of columns. - ULONG columns; - // A column display index to ID map. - PULONG displayToId; - // A column display index to text map. - PWSTR *displayToText; - // The actual string table. - PPH_STRING **table; - ULONG i; - ULONG j; - - // Use a local auto-pool to make memory mangement a bit less painful. - PhInitializeAutoPool(&autoPool); - - rows = NumberOfNodes + 1; - - // Create the display index to ID map. - PhMapDisplayIndexTreeNew(TreeListHandle, &displayToId, &displayToText, &columns); - - PhaCreateTextTable(&table, rows, columns); - - // Populate the first row with the column headers. - for (i = 0; i < columns; i++) - { - table[0][i] = PhaCreateString(displayToText[i]); - } - - // Go through the nodes in the process tree and populate each cell of the table. - - j = 1; // index starts at one because the first row contains the column headers. - - for (i = 0; i < RootNodes->Count; i++) - { - PhpPopulateTableWithProcessNodes( - TreeListHandle, - RootNodes->Items[i], - 0, - table, - &j, - displayToId, - columns - ); - } - - PhFree(displayToId); - PhFree(displayToText); - - lines = PhaFormatTextTable(table, rows, columns, Mode); - - PhDeleteAutoPool(&autoPool); - - return lines; -} - -VOID PhCopyProcessTree( - VOID - ) -{ - PPH_STRING text; - - text = PhGetTreeNewText(ProcessTreeListHandle, 0); - PhSetClipboardString(ProcessTreeListHandle, &text->sr); - PhDereferenceObject(text); -} - -VOID PhWriteProcessTree( - _Inout_ PPH_FILE_STREAM FileStream, - _In_ ULONG Mode - ) -{ - PPH_LIST lines; - ULONG i; - - lines = PhGetProcessTreeListLines( - ProcessTreeListHandle, - ProcessNodeList->Count, - ProcessNodeRootList, - Mode - ); - - for (i = 0; i < lines->Count; i++) - { - PPH_STRING line; - - line = lines->Items[i]; - PhWriteStringAsUtf8FileStream(FileStream, &line->sr); - PhDereferenceObject(line); - PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); - } - - PhDereferenceObject(lines); -} - -PPH_LIST PhDuplicateProcessNodeList( - VOID - ) -{ - PPH_LIST newList; - - newList = PhCreateList(ProcessNodeList->Count); - PhInsertItemsList(newList, 0, ProcessNodeList->Items, ProcessNodeList->Count); - - return newList; -} +/* + * Process Hacker - + * process tree list + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2016-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The process tree list manages a list of tree nodes and handles callback events generated by the + * underlying treenew control. Retrieval of certain types of process information is also performed + * here, on the GUI thread (see PH_PROCESS_NODE.ValidMask). This is done for columns that require + * data not supplied by the process provider. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef enum _PHP_AGGREGATE_TYPE +{ + AggregateTypeFloat, + AggregateTypeInt32, + AggregateTypeInt64, + AggregateTypeIntPtr +} PHP_AGGREGATE_TYPE; + +typedef enum _PHP_AGGREGATE_LOCATION +{ + AggregateLocationProcessNode, + AggregateLocationProcessItem +} PHP_AGGREGATE_LOCATION; + +VOID PhpRemoveProcessNode( + _In_ PPH_PROCESS_NODE ProcessNode + ); + +LONG PhpProcessTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpProcessTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +static HWND ProcessTreeListHandle; +static ULONG ProcessTreeListSortColumn; +static PH_SORT_ORDER ProcessTreeListSortOrder; +static PH_CM_MANAGER ProcessTreeListCm; + +static PPH_HASH_ENTRY ProcessNodeHashSet[256] = PH_HASH_SET_INIT; // hashtable of all nodes +static PPH_LIST ProcessNodeList; // list of all nodes, used when sorting is enabled +static PPH_LIST ProcessNodeRootList; // list of root nodes +static PH_TN_FILTER_SUPPORT FilterSupport; + +BOOLEAN PhProcessTreeListStateHighlighting = TRUE; +static PPH_POINTER_LIST ProcessNodeStateList = NULL; // list of nodes which need to be processed + +static HDC GraphContext = NULL; +static ULONG GraphContextWidth = 0; +static ULONG GraphContextHeight = 0; +static HBITMAP GraphOldBitmap; +static HBITMAP GraphBitmap = NULL; +static PVOID GraphBits = NULL; + +VOID PhProcessTreeListInitialization( + VOID + ) +{ + ProcessNodeList = PhCreateList(40); + ProcessNodeRootList = PhCreateList(10); +} + +VOID PhInitializeProcessTreeList( + _In_ HWND hwnd + ) +{ + ProcessTreeListHandle = hwnd; + PhSetControlTheme(ProcessTreeListHandle, L"explorer"); + TreeNew_SetExtendedFlags(hwnd, TN_FLAG_ITEM_DRAG_SELECT, TN_FLAG_ITEM_DRAG_SELECT); + SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); + + TreeNew_SetCallback(hwnd, PhpProcessTreeNewCallback, NULL); + + TreeNew_SetMaxId(hwnd, PHPRTLC_MAXIMUM - 1); + + TreeNew_SetRedraw(hwnd, FALSE); + + // Default columns + PhAddTreeNewColumn(hwnd, PHPRTLC_NAME, TRUE, L"Name", 200, PH_ALIGN_LEFT, -2, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_PID, TRUE, L"PID", 50, PH_ALIGN_RIGHT, 0, DT_RIGHT); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOTOTALRATE, TRUE, L"I/O total rate", 70, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTES, TRUE, L"Private bytes", 70, PH_ALIGN_RIGHT, 3, DT_RIGHT, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_USERNAME, TRUE, L"User name", 140, PH_ALIGN_LEFT, 4, DT_PATH_ELLIPSIS); + PhAddTreeNewColumn(hwnd, PHPRTLC_DESCRIPTION, TRUE, L"Description", 180, PH_ALIGN_LEFT, 5, 0); + + // Customizable columns (1) + PhAddTreeNewColumn(hwnd, PHPRTLC_COMPANYNAME, FALSE, L"Company name", 180, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_VERSION, FALSE, L"Version", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_FILENAME, FALSE, L"File name", 180, PH_ALIGN_LEFT, ULONG_MAX, DT_PATH_ELLIPSIS); + PhAddTreeNewColumn(hwnd, PHPRTLC_COMMANDLINE, FALSE, L"Command line", 180, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPRIVATEBYTES, FALSE, L"Peak private bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_WORKINGSET, FALSE, L"Working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKWORKINGSET, FALSE, L"Peak working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEWS, FALSE, L"Private WS", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREDWS, FALSE, L"Shared WS", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_SHAREABLEWS, FALSE, L"Shareable WS", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALSIZE, FALSE, L"Virtual size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKVIRTUALSIZE, FALSE, L"Peak virtual size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTS, FALSE, L"Page faults", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_SESSIONID, FALSE, L"Session ID", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIORITYCLASS, FALSE, L"Priority class", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_BASEPRIORITY, FALSE, L"Base priority", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + + // Customizable columns (2) + PhAddTreeNewColumnEx(hwnd, PHPRTLC_THREADS, FALSE, L"Threads", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_HANDLES, FALSE, L"Handles", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_GDIHANDLES, FALSE, L"GDI handles", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERHANDLES, FALSE, L"USER handles", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IORORATE, FALSE, L"I/O read+other rate", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRATE, FALSE, L"I/O write rate", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_INTEGRITY, FALSE, L"Integrity", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOPRIORITY, FALSE, L"I/O priority", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEPRIORITY, FALSE, L"Page priority", 45, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_STARTTIME, FALSE, L"Start time", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_TOTALCPUTIME, FALSE, L"Total CPU time", 90, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_KERNELCPUTIME, FALSE, L"Kernel CPU time", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_USERCPUTIME, FALSE, L"User CPU time", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_ASLR, FALSE, L"ASLR", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_RELATIVESTARTTIME, FALSE, L"Relative start time", 180, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_BITS, FALSE, L"Bits", 50, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_ELEVATION, FALSE, L"Elevation", 60, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_WINDOWTITLE, FALSE, L"Window title", 120, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_WINDOWSTATUS, FALSE, L"Window status", 60, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLES, FALSE, L"Cycles", 110, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CYCLESDELTA, FALSE, L"Cycles delta", 90, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx2(hwnd, PHPRTLC_CPUHISTORY, FALSE, L"CPU history", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING); + PhAddTreeNewColumnEx2(hwnd, PHPRTLC_PRIVATEBYTESHISTORY, FALSE, L"Private bytes history", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING); + PhAddTreeNewColumnEx2(hwnd, PHPRTLC_IOHISTORY, FALSE, L"I/O history", 100, PH_ALIGN_LEFT, ULONG_MAX, 0, TN_COLUMN_FLAG_CUSTOMDRAW | TN_COLUMN_FLAG_SORTDESCENDING); + PhAddTreeNewColumn(hwnd, PHPRTLC_DEP, FALSE, L"DEP", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_VIRTUALIZED, FALSE, L"Virtualized", 80, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHES, FALSE, L"Context switches", 100, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CONTEXTSWITCHESDELTA, FALSE, L"Context switches delta", 80, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEFAULTSDELTA, FALSE, L"Page faults delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + + // I/O group columns + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADS, FALSE, L"I/O reads", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITES, FALSE, L"I/O writes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHER, FALSE, L"I/O other", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADBYTES, FALSE, L"I/O read bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITEBYTES, FALSE, L"I/O write bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERBYTES, FALSE, L"I/O other bytes", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOREADSDELTA, FALSE, L"I/O reads delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOWRITESDELTA, FALSE, L"I/O writes delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_IOOTHERDELTA, FALSE, L"I/O other delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + + // Customizable columns (3) + PhAddTreeNewColumn(hwnd, PHPRTLC_OSCONTEXT, FALSE, L"OS context", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PAGEDPOOL, FALSE, L"Paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKPAGEDPOOL, FALSE, L"Peak paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_NONPAGEDPOOL, FALSE, L"Non-paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PEAKNONPAGEDPOOL, FALSE, L"Peak non-paged pool", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_MINIMUMWORKINGSET, FALSE, L"Minimum working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_MAXIMUMWORKINGSET, FALSE, L"Maximum working set", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PRIVATEBYTESDELTA, FALSE, L"Private bytes delta", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumn(hwnd, PHPRTLC_SUBSYSTEM, FALSE, L"Subsystem", 110, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_PACKAGENAME, FALSE, L"Package name", 160, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_APPID, FALSE, L"App ID", 160, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHPRTLC_DPIAWARENESS, FALSE, L"DPI awareness", 110, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CFGUARD, FALSE, L"CF Guard", 70, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_TIMESTAMP, FALSE, L"Time stamp", 140, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILEMODIFIEDTIME, FALSE, L"File modified time", 140, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_FILESIZE, FALSE, L"File size", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_SUBPROCESSCOUNT, FALSE, L"Subprocesses", 70, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_JOBOBJECTID, FALSE, L"Job Object ID", 50, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PROTECTION, FALSE, L"Protection", 105, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_DESKTOP, FALSE, L"Desktop", 80, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_CRITICAL, FALSE, L"Critical", 80, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHPRTLC_PIDHEX, FALSE, L"PID (Hex)", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT, TRUE); + + TreeNew_SetRedraw(hwnd, TRUE); + + TreeNew_SetTriState(hwnd, TRUE); + TreeNew_SetSort(hwnd, 0, NoSortOrder); + + PhCmInitializeManager(&ProcessTreeListCm, hwnd, PHPRTLC_MAXIMUM, PhpProcessTreeNewPostSortFunction); + + if (PhPluginsEnabled) + { + PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; + + treeNewInfo.TreeNewHandle = hwnd; + treeNewInfo.CmData = &ProcessTreeListCm; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), &treeNewInfo); + } + + PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ProcessNodeList); +} + +VOID PhLoadSettingsProcessTreeList( + VOID + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhGetStringSetting(L"ProcessTreeListColumns"); + sortSettings = PhGetStringSetting(L"ProcessTreeListSort"); + PhCmLoadSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &settings->sr, &sortSettings->sr); + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); + + if (PhGetIntegerSetting(L"EnableInstantTooltips")) + { + SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0); + } +} + +VOID PhSaveSettingsProcessTreeList( + VOID + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &sortSettings); + PhSetStringSetting2(L"ProcessTreeListColumns", &settings->sr); + PhSetStringSetting2(L"ProcessTreeListSort", &sortSettings->sr); + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhLoadSettingsProcessTreeListEx( + _In_ PPH_STRING TreeListSettings, + _In_ PPH_STRING TreeSortSettings + ) +{ + PhCmLoadSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &TreeListSettings->sr, &TreeSortSettings->sr); + + if (PhGetIntegerSetting(L"EnableInstantTooltips")) + { + SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, 0); + } +} + +VOID PhSaveSettingsProcessTreeListEx( + _Out_ PPH_STRING *TreeListSettings, + _Out_ PPH_STRING *TreeSortSettings + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(ProcessTreeListHandle, &ProcessTreeListCm, 0, &sortSettings); + + *TreeListSettings = settings; + *TreeSortSettings = sortSettings; +} + +VOID PhReloadSettingsProcessTreeList( + VOID + ) +{ + SendMessage(TreeNew_GetTooltips(ProcessTreeListHandle), TTM_SETDELAYTIME, TTDT_INITIAL, + PhGetIntegerSetting(L"EnableInstantTooltips") ? 0 : -1); +} + +struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportProcessTreeList( + VOID + ) +{ + return &FilterSupport; +} + +FORCEINLINE BOOLEAN PhCompareProcessNode( + _In_ PPH_PROCESS_NODE Value1, + _In_ PPH_PROCESS_NODE Value2 + ) +{ + return Value1->ProcessId == Value2->ProcessId; +} + +FORCEINLINE ULONG PhHashProcessNode( + _In_ PPH_PROCESS_NODE Value + ) +{ + return HandleToUlong(Value->ProcessId) / 4; +} + +FORCEINLINE BOOLEAN PhpValidateParentProcessNode( + _In_ PPH_PROCESS_NODE Child, + _In_ PPH_PROCESS_NODE Parent + ) +{ + if (WindowsVersion >= WINDOWS_10_RS3) + { + return PH_IS_FAKE_PROCESS_ID(Child->ProcessId) || + Parent->ProcessItem->ProcessSequenceNumber <= Child->ProcessItem->ProcessSequenceNumber; + } + else + { + return PH_IS_FAKE_PROCESS_ID(Child->ProcessId) || + Parent->ProcessItem->CreateTime.QuadPart <= Child->ProcessItem->CreateTime.QuadPart; + } +} + +PPH_PROCESS_NODE PhAddProcessNode( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ ULONG RunId + ) +{ + PPH_PROCESS_NODE processNode; + PPH_PROCESS_NODE parentNode; + ULONG i; + + processNode = PhAllocate(PhEmGetObjectSize(EmProcessNodeType, sizeof(PH_PROCESS_NODE))); + memset(processNode, 0, sizeof(PH_PROCESS_NODE)); + PhInitializeTreeNewNode(&processNode->Node); + + if (PhProcessTreeListStateHighlighting && RunId != 1) + { + PhChangeShStateTn( + &processNode->Node, + &processNode->ShState, + &ProcessNodeStateList, + NewItemState, + PhCsColorNew, + NULL + ); + } + + processNode->ProcessId = ProcessItem->ProcessId; + processNode->ProcessItem = ProcessItem; + PhReferenceObject(ProcessItem); + + memset(processNode->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM); + processNode->Node.TextCache = processNode->TextCache; + processNode->Node.TextCacheSize = PHPRTLC_MAXIMUM; + + processNode->Children = PhCreateList(1); + + // Find this process' parent and add the process to it if we found it. + if ( + (parentNode = PhFindProcessNode(ProcessItem->ParentProcessId)) && + PhpValidateParentProcessNode(processNode, parentNode) + ) + { + PhAddItemList(parentNode->Children, processNode); + processNode->Parent = parentNode; + } + else + { + // No parent, add to root list. + processNode->Parent = NULL; + PhAddItemList(ProcessNodeRootList, processNode); + } + + // Find this process' children and move them to this node. + + for (i = 0; i < ProcessNodeRootList->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNodeRootList->Items[i]; + + if ( + node != processNode && // for cases where the parent PID = PID (e.g. System Idle Process) + node->ProcessItem->ParentProcessId == ProcessItem->ProcessId && + PhpValidateParentProcessNode(node, processNode) + ) + { + node->Parent = processNode; + PhAddItemList(processNode->Children, node); + } + } + + for (i = 0; i < processNode->Children->Count; i++) + { + PhRemoveItemList( + ProcessNodeRootList, + PhFindItemList(ProcessNodeRootList, processNode->Children->Items[i]) + ); + } + + PhAddEntryHashSet( + ProcessNodeHashSet, + PH_HASH_SET_SIZE(ProcessNodeHashSet), + &processNode->HashEntry, + PhHashProcessNode(processNode) + ); + PhAddItemList(ProcessNodeList, processNode); + + if (PhCsCollapseServicesOnStart) + { + static PH_STRINGREF servicesBaseName = PH_STRINGREF_INIT(L"\\services.exe"); + static BOOLEAN servicesFound = FALSE; + static PPH_STRING servicesFileName = NULL; + + if (!servicesFound) + { + if (!servicesFileName) + { + PPH_STRING systemDirectory; + + systemDirectory = PhGetSystemDirectory(); + servicesFileName = PhConcatStringRef2(&systemDirectory->sr, &servicesBaseName); + PhDereferenceObject(systemDirectory); + } + + // If this process is services.exe, collapse the node and free the string. + if ( + ProcessItem->FileName && + PhEqualString(ProcessItem->FileName, servicesFileName, TRUE) + ) + { + processNode->Node.Expanded = FALSE; + PhDereferenceObject(servicesFileName); + servicesFileName = NULL; + servicesFound = TRUE; + } + } + } + + if (PhEnableCycleCpuUsage && ProcessItem->ProcessId == INTERRUPTS_PROCESS_ID) + PhInitializeStringRef(&processNode->DescriptionText, L"Interrupts and DPCs"); + + if (FilterSupport.FilterList) + processNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &processNode->Node); + + PhEmCallObjectOperation(EmProcessNodeType, processNode, EmObjectCreate); + + TreeNew_NodesStructured(ProcessTreeListHandle); + + return processNode; +} + +PPH_PROCESS_NODE PhFindProcessNode( + _In_ HANDLE ProcessId + ) +{ + PH_PROCESS_NODE lookupNode; + PPH_HASH_ENTRY entry; + PPH_PROCESS_NODE node; + + lookupNode.ProcessId = ProcessId; + entry = PhFindEntryHashSet( + ProcessNodeHashSet, + PH_HASH_SET_SIZE(ProcessNodeHashSet), + PhHashProcessNode(&lookupNode) + ); + + for (; entry; entry = entry->Next) + { + node = CONTAINING_RECORD(entry, PH_PROCESS_NODE, HashEntry); + + if (PhCompareProcessNode(&lookupNode, node)) + return node; + } + + return NULL; +} + +VOID PhRemoveProcessNode( + _In_ PPH_PROCESS_NODE ProcessNode + ) +{ + // Remove from the hashtable here to avoid problems in case the key is re-used. + PhRemoveEntryHashSet(ProcessNodeHashSet, PH_HASH_SET_SIZE(ProcessNodeHashSet), &ProcessNode->HashEntry); + + if (PhProcessTreeListStateHighlighting) + { + PhChangeShStateTn( + &ProcessNode->Node, + &ProcessNode->ShState, + &ProcessNodeStateList, + RemovingItemState, + PhCsColorRemoved, + ProcessTreeListHandle + ); + } + else + { + PhpRemoveProcessNode(ProcessNode); + } +} + +VOID PhpRemoveProcessNode( + _In_ PPH_PROCESS_NODE ProcessNode + ) +{ + ULONG index; + ULONG i; + + PhEmCallObjectOperation(EmProcessNodeType, ProcessNode, EmObjectDelete); + + if (ProcessNode->Parent) + { + // Remove the node from its parent. + + if ((index = PhFindItemList(ProcessNode->Parent->Children, ProcessNode)) != ULONG_MAX) + PhRemoveItemList(ProcessNode->Parent->Children, index); + } + else + { + // Remove the node from the root list. + + if ((index = PhFindItemList(ProcessNodeRootList, ProcessNode)) != ULONG_MAX) + PhRemoveItemList(ProcessNodeRootList, index); + } + + // Move the node's children to the root list. + for (i = 0; i < ProcessNode->Children->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNode->Children->Items[i]; + + node->Parent = NULL; + PhAddItemList(ProcessNodeRootList, node); + } + + // Remove from list and cleanup. + + if ((index = PhFindItemList(ProcessNodeList, ProcessNode)) != ULONG_MAX) + PhRemoveItemList(ProcessNodeList, index); + + PhDereferenceObject(ProcessNode->Children); + + PhClearReference(&ProcessNode->WindowText); + PhClearReference(&ProcessNode->AppIdText); + + PhClearReference(&ProcessNode->TooltipText); + + PhClearReference(&ProcessNode->PeakPrivateBytesText); + PhClearReference(&ProcessNode->WorkingSetText); + PhClearReference(&ProcessNode->PeakWorkingSetText); + PhClearReference(&ProcessNode->SharedWsText); + PhClearReference(&ProcessNode->ShareableWsText); + PhClearReference(&ProcessNode->VirtualSizeText); + PhClearReference(&ProcessNode->PeakVirtualSizeText); + PhClearReference(&ProcessNode->PageFaultsText); + PhClearReference(&ProcessNode->IoRoRateText); + PhClearReference(&ProcessNode->IoWRateText); + PhClearReference(&ProcessNode->StartTimeText); + PhClearReference(&ProcessNode->TotalCpuTimeText); + PhClearReference(&ProcessNode->KernelCpuTimeText); + PhClearReference(&ProcessNode->UserCpuTimeText); + PhClearReference(&ProcessNode->RelativeStartTimeText); + PhClearReference(&ProcessNode->WindowTitleText); + PhClearReference(&ProcessNode->CyclesText); + PhClearReference(&ProcessNode->CyclesDeltaText); + PhClearReference(&ProcessNode->ContextSwitchesText); + PhClearReference(&ProcessNode->ContextSwitchesDeltaText); + PhClearReference(&ProcessNode->PageFaultsDeltaText); + + for (i = 0; i < PHPRTLC_IOGROUP_COUNT; i++) + PhClearReference(&ProcessNode->IoGroupText[i]); + + PhClearReference(&ProcessNode->PagedPoolText); + PhClearReference(&ProcessNode->PeakPagedPoolText); + PhClearReference(&ProcessNode->NonPagedPoolText); + PhClearReference(&ProcessNode->PeakNonPagedPoolText); + PhClearReference(&ProcessNode->MinimumWorkingSetText); + PhClearReference(&ProcessNode->MaximumWorkingSetText); + PhClearReference(&ProcessNode->PrivateBytesDeltaText); + PhClearReference(&ProcessNode->TimeStampText); + PhClearReference(&ProcessNode->FileModifiedTimeText); + PhClearReference(&ProcessNode->FileSizeText); + PhClearReference(&ProcessNode->SubprocessCountText); + PhClearReference(&ProcessNode->ProtectionText); + PhClearReference(&ProcessNode->DesktopInfoText); + + PhDeleteGraphBuffers(&ProcessNode->CpuGraphBuffers); + PhDeleteGraphBuffers(&ProcessNode->PrivateGraphBuffers); + PhDeleteGraphBuffers(&ProcessNode->IoGraphBuffers); + + PhDereferenceObject(ProcessNode->ProcessItem); + + PhFree(ProcessNode); + + TreeNew_NodesStructured(ProcessTreeListHandle); +} + +VOID PhUpdateProcessNode( + _In_ PPH_PROCESS_NODE ProcessNode + ) +{ + memset(ProcessNode->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM); + + PhClearReference(&ProcessNode->TooltipText); + + PhInvalidateTreeNewNode(&ProcessNode->Node, TN_CACHE_COLOR | TN_CACHE_ICON); + TreeNew_InvalidateNode(ProcessTreeListHandle, &ProcessNode->Node); +} + +VOID PhTickProcessNodes( + VOID + ) +{ + ULONG i; + PH_TREENEW_VIEW_PARTS viewParts; + BOOLEAN fullyInvalidated; + RECT rect; + + // Text invalidation, node updates + + for (i = 0; i < ProcessNodeList->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; + + // The name and PID never change, so we don't invalidate that. + memset(&node->TextCache[2], 0, sizeof(PH_STRINGREF) * (PHPRTLC_MAXIMUM - 2)); + node->ValidMask &= PHPN_OSCONTEXT | PHPN_IMAGE | PHPN_DPIAWARENESS | PHPN_APPID | PHPN_DESKTOPINFO; // Items that always remain valid + + // The DPI awareness defaults to unaware if not set or declared in the manifest in which case + // it can be changed once, so we can only be sure that it won't be changed again if it is different + // from Unaware (poizan42). + if (node->DpiAwareness != 1) + node->ValidMask &= ~PHPN_DPIAWARENESS; + + // Invalidate graph buffers. + node->CpuGraphBuffers.Valid = FALSE; + node->PrivateGraphBuffers.Valid = FALSE; + node->IoGraphBuffers.Valid = FALSE; + } + + fullyInvalidated = FALSE; + + if (ProcessTreeListSortOrder != NoSortOrder) + { + // Force a rebuild to sort the items. + TreeNew_NodesStructured(ProcessTreeListHandle); + fullyInvalidated = TRUE; + } + + // State highlighting + PH_TICK_SH_STATE_TN(PH_PROCESS_NODE, ShState, ProcessNodeStateList, PhpRemoveProcessNode, PhCsHighlightingDuration, ProcessTreeListHandle, TRUE, &fullyInvalidated); + + if (!fullyInvalidated) + { + // The first column doesn't need to be invalidated because the process name never changes, and + // icon changes are handled by the modified event. This small optimization can save more than + // 10 million cycles per update (on my machine). + TreeNew_GetViewParts(ProcessTreeListHandle, &viewParts); + rect.left = viewParts.NormalLeft; + rect.top = viewParts.HeaderHeight; + rect.right = viewParts.ClientRect.right - viewParts.VScrollWidth; + rect.bottom = viewParts.ClientRect.bottom; + InvalidateRect(ProcessTreeListHandle, &rect, FALSE); + } +} + +static VOID PhpNeedGraphContext( + _In_ HDC hdc, + _In_ ULONG Width, + _In_ ULONG Height + ) +{ + BITMAPINFOHEADER header; + + // If we already have a graph context and it's the right size, then return immediately. + if (GraphContextWidth == Width && GraphContextHeight == Height) + return; + + if (GraphContext) + { + // The original bitmap must be selected back into the context, otherwise + // the bitmap can't be deleted. + SelectBitmap(GraphContext, GraphBitmap); + DeleteBitmap(GraphBitmap); + DeleteDC(GraphContext); + + GraphContext = NULL; + GraphBitmap = NULL; + GraphBits = NULL; + } + + GraphContext = CreateCompatibleDC(hdc); + + memset(&header, 0, sizeof(BITMAPINFOHEADER)); + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = Width; + header.biHeight = Height; + header.biPlanes = 1; + header.biBitCount = 32; + GraphBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &GraphBits, NULL, 0); + GraphOldBitmap = SelectBitmap(GraphContext, GraphBitmap); +} + +static BOOLEAN PhpFormatInt32GroupDigits( + _In_ ULONG Value, + _Out_writes_bytes_(BufferLength) PWCHAR Buffer, + _In_ ULONG BufferLength, + _Out_opt_ PPH_STRINGREF String + ) +{ + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatU(&format, Value); + format.Type |= FormatGroupDigits; + + if (PhFormatToBuffer(&format, 1, Buffer, BufferLength, &returnLength)) + { + if (String) + { + String->Buffer = Buffer; + String->Length = returnLength - sizeof(UNICODE_NULL); + } + + return TRUE; + } + else + { + return FALSE; + } +} + +FORCEINLINE PVOID PhpFieldForAggregate( + _In_ PPH_PROCESS_NODE ProcessNode, + _In_ PHP_AGGREGATE_LOCATION Location, + _In_ SIZE_T FieldOffset + ) +{ + PVOID object; + + switch (Location) + { + case AggregateLocationProcessNode: + object = ProcessNode; + break; + case AggregateLocationProcessItem: + object = ProcessNode->ProcessItem; + break; + default: + PhRaiseStatus(STATUS_INVALID_PARAMETER); + } + + return PTR_ADD_OFFSET(object, FieldOffset); +} + +FORCEINLINE VOID PhpAccumulateField( + _Inout_ PVOID Accumulator, + _In_ PVOID Value, + _In_ PHP_AGGREGATE_TYPE Type + ) +{ + switch (Type) + { + case AggregateTypeFloat: + *(PFLOAT)Accumulator += *(PFLOAT)Value; + break; + case AggregateTypeInt32: + *(PULONG)Accumulator += *(PULONG)Value; + break; + case AggregateTypeInt64: + *(PULONG64)Accumulator += *(PULONG64)Value; + break; + case AggregateTypeIntPtr: + *(PULONG_PTR)Accumulator += *(PULONG_PTR)Value; + break; + } +} + +static VOID PhpAggregateField( + _In_ PPH_PROCESS_NODE ProcessNode, + _In_ PHP_AGGREGATE_TYPE Type, + _In_ PHP_AGGREGATE_LOCATION Location, + _In_ SIZE_T FieldOffset, + _Inout_ PVOID AggregatedValue + ) +{ + ULONG i; + + PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type); + + for (i = 0; i < ProcessNode->Children->Count; i++) + { + PhpAggregateField(ProcessNode->Children->Items[i], Type, Location, FieldOffset, AggregatedValue); + } +} + +static VOID PhpAggregateFieldIfNeeded( + _In_ PPH_PROCESS_NODE ProcessNode, + _In_ PHP_AGGREGATE_TYPE Type, + _In_ PHP_AGGREGATE_LOCATION Location, + _In_ SIZE_T FieldOffset, + _Inout_ PVOID AggregatedValue + ) +{ + if (!PhCsPropagateCpuUsage || ProcessNode->Node.Expanded || ProcessTreeListSortOrder != NoSortOrder) + { + PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type); + } + else + { + PhpAggregateField(ProcessNode, Type, Location, FieldOffset, AggregatedValue); + } +} + +static VOID PhpUpdateProcessNodeWsCounters( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_WSCOUNTERS)) + { + BOOLEAN success = FALSE; + HANDLE processHandle; + + if (PH_IS_REAL_PROCESS_ID(ProcessNode->ProcessItem->ProcessId)) + { + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_INFORMATION, ProcessNode->ProcessItem->ProcessId))) + { + if (NT_SUCCESS(PhGetProcessWsCounters(processHandle, &ProcessNode->WsCounters))) + success = TRUE; + + NtClose(processHandle); + } + } + + if (!success) + memset(&ProcessNode->WsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS)); + + ProcessNode->ValidMask |= PHPN_WSCOUNTERS; + } +} + +static VOID PhpUpdateProcessNodeGdiUserHandles( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_GDIUSERHANDLES)) + { + if (ProcessNode->ProcessItem->QueryHandle) + { + ProcessNode->GdiHandles = GetGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_GDIOBJECTS); + ProcessNode->UserHandles = GetGuiResources(ProcessNode->ProcessItem->QueryHandle, GR_USEROBJECTS); + } + else + { + ProcessNode->GdiHandles = 0; + ProcessNode->UserHandles = 0; + } + + ProcessNode->ValidMask |= PHPN_GDIUSERHANDLES; + } +} + +static VOID PhpUpdateProcessNodeIoPagePriority( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_IOPAGEPRIORITY)) + { + if (ProcessNode->ProcessItem->QueryHandle) + { + if (!NT_SUCCESS(PhGetProcessIoPriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->IoPriority))) + ProcessNode->IoPriority = ULONG_MAX; + if (!NT_SUCCESS(PhGetProcessPagePriority(ProcessNode->ProcessItem->QueryHandle, &ProcessNode->PagePriority))) + ProcessNode->PagePriority = ULONG_MAX; + } + else + { + ProcessNode->IoPriority = ULONG_MAX; + ProcessNode->PagePriority = ULONG_MAX; + } + + ProcessNode->ValidMask |= PHPN_IOPAGEPRIORITY; + } +} + +static VOID PhpUpdateProcessNodeWindow( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_WINDOW)) + { + PhClearReference(&ProcessNode->WindowText); + + if (ProcessNode->ProcessItem->QueryHandle && !ProcessNode->ProcessItem->IsSubsystemProcess) + { + ProcessNode->WindowHandle = PhGetProcessMainWindow( + ProcessNode->ProcessId, + ProcessNode->ProcessItem->QueryHandle + ); + + if (ProcessNode->WindowHandle) + { + PhGetWindowTextEx( + ProcessNode->WindowHandle, + PH_GET_WINDOW_TEXT_INTERNAL, + &ProcessNode->WindowText + ); + + ProcessNode->WindowHung = !!IsHungAppWindow(ProcessNode->WindowHandle); + } + } + + ProcessNode->ValidMask |= PHPN_WINDOW; + } +} + +static VOID PhpUpdateProcessNodeDepStatus( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_DEPSTATUS)) + { + HANDLE processHandle; + ULONG depStatus; + + depStatus = 0; + +#ifdef _WIN64 + if (ProcessNode->ProcessItem->IsWow64) +#else + if (TRUE) +#endif + { + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_INFORMATION, + ProcessNode->ProcessItem->ProcessId + ))) + { + PhGetProcessDepStatus(processHandle, &depStatus); + NtClose(processHandle); + } + } + else + { + if (ProcessNode->ProcessItem->QueryHandle) + depStatus = PH_PROCESS_DEP_ENABLED | PH_PROCESS_DEP_PERMANENT; + } + + ProcessNode->DepStatus = depStatus; + + ProcessNode->ValidMask |= PHPN_DEPSTATUS; + } +} + +static VOID PhpUpdateProcessNodeToken( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_TOKEN)) + { + HANDLE tokenHandle; + + ProcessNode->VirtualizationAllowed = FALSE; + ProcessNode->VirtualizationEnabled = FALSE; + + if (ProcessNode->ProcessItem->QueryHandle) + { + if (NT_SUCCESS(PhOpenProcessToken( + ProcessNode->ProcessItem->QueryHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &ProcessNode->VirtualizationAllowed)) && + ProcessNode->VirtualizationAllowed) + { + if (!NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &ProcessNode->VirtualizationEnabled))) + { + ProcessNode->VirtualizationAllowed = FALSE; // display N/A on error + } + } + + NtClose(tokenHandle); + } + } + + ProcessNode->ValidMask |= PHPN_TOKEN; + } +} + +static VOID PhpUpdateProcessOsContext( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_OSCONTEXT)) + { + HANDLE processHandle; + + if (ProcessNode->ProcessItem->IsSubsystemProcess) + { + NOTHING; + } + else if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, ProcessNode->ProcessId))) + { + if (NT_SUCCESS(PhGetProcessSwitchContext(processHandle, &ProcessNode->OsContextGuid))) + { + if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN10_CONTEXT_GUID)) + ProcessNode->OsContextVersion = WINDOWS_10; + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WINBLUE_CONTEXT_GUID)) + ProcessNode->OsContextVersion = WINDOWS_8_1; + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN8_CONTEXT_GUID)) + ProcessNode->OsContextVersion = WINDOWS_8; + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &WIN7_CONTEXT_GUID)) + ProcessNode->OsContextVersion = WINDOWS_7; + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &VISTA_CONTEXT_GUID)) + ProcessNode->OsContextVersion = WINDOWS_VISTA; + else if (IsEqualGUID(&ProcessNode->OsContextGuid, &XP_CONTEXT_GUID)) + ProcessNode->OsContextVersion = WINDOWS_XP; + } + + NtClose(processHandle); + } + + ProcessNode->ValidMask |= PHPN_OSCONTEXT; + } +} + +static VOID PhpUpdateProcessNodeQuotaLimits( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_QUOTALIMITS)) + { + QUOTA_LIMITS quotaLimits; + + if (ProcessNode->ProcessItem->QueryHandle && NT_SUCCESS(PhGetProcessQuotaLimits( + ProcessNode->ProcessItem->QueryHandle, + "aLimits + ))) + { + ProcessNode->MinimumWorkingSetSize = quotaLimits.MinimumWorkingSetSize; + ProcessNode->MaximumWorkingSetSize = quotaLimits.MaximumWorkingSetSize; + } + else + { + ProcessNode->MinimumWorkingSetSize = 0; + ProcessNode->MaximumWorkingSetSize = 0; + } + + ProcessNode->ValidMask |= PHPN_QUOTALIMITS; + } +} + +static VOID PhpUpdateProcessNodeImage( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_IMAGE)) + { + if (ProcessNode->ProcessItem->IsSubsystemProcess) + { + ProcessNode->ImageSubsystem = IMAGE_SUBSYSTEM_POSIX_CUI; + } + else + { + HANDLE processHandle; + PROCESS_BASIC_INFORMATION basicInfo; + PVOID imageBaseAddress; + PH_REMOTE_MAPPED_IMAGE mappedImage; + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, ProcessNode->ProcessId))) + { + if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo)) && basicInfo.PebBaseAddress != 0) + { + if (NT_SUCCESS(NtReadVirtualMemory( + processHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ImageBaseAddress)), + &imageBaseAddress, + sizeof(PVOID), + NULL + ))) + { + if (NT_SUCCESS(PhLoadRemoteMappedImage(processHandle, imageBaseAddress, &mappedImage))) + { + ProcessNode->ImageTimeDateStamp = mappedImage.NtHeaders->FileHeader.TimeDateStamp; + ProcessNode->ImageCharacteristics = mappedImage.NtHeaders->FileHeader.Characteristics; + + if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->Subsystem; + ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; + } + else if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + ProcessNode->ImageSubsystem = ((PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader)->Subsystem; + ProcessNode->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&mappedImage.NtHeaders->OptionalHeader)->DllCharacteristics; + } + + PhUnloadRemoteMappedImage(&mappedImage); + } + } + } + + NtClose(processHandle); + } + } + + ProcessNode->ValidMask |= PHPN_IMAGE; + } +} + +static VOID PhpUpdateProcessNodeAppId( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_APPID)) + { + ULONG windowFlags; + PPH_STRING applicationUserModelId; + + PhClearReference(&ProcessNode->AppIdText); + + if (ProcessNode->ProcessItem->IsSubsystemProcess) + { + NOTHING; + } + else if (PhAppResolverGetAppIdForProcess(ProcessNode->ProcessItem->ProcessId, &applicationUserModelId)) + { + ProcessNode->AppIdText = applicationUserModelId; + } + else + { + if (ProcessNode->ProcessItem->QueryHandle) + { + if (NT_SUCCESS(PhGetProcessWindowTitle( + ProcessNode->ProcessItem->QueryHandle, + &windowFlags, + &applicationUserModelId + ))) + { + if (windowFlags & STARTF_TITLEISAPPID) + ProcessNode->AppIdText = applicationUserModelId; + else + PhDereferenceObject(applicationUserModelId); + } + + //if (WindowsVersion >= WINDOWS_8 && ProcessNode->ProcessItem->IsImmersive) + //{ + // HANDLE tokenHandle; + // PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; + // + // if (NT_SUCCESS(PhOpenProcessToken( + // ProcessNode->ProcessItem->QueryHandle, + // TOKEN_QUERY, + // &tokenHandle + // ))) + // { + // // rev from GetApplicationUserModelId + // if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenSecurityAttributes, &info))) + // { + // for (ULONG i = 0; i < info->AttributeCount; i++) + // { + // static UNICODE_STRING attributeNameUs = RTL_CONSTANT_STRING(L"WIN://SYSAPPID"); + // PTOKEN_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; + // + // if (RtlEqualUnicodeString(&attribute->Name, &attributeNameUs, FALSE)) + // { + // if (attribute->ValueType == TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING) + // { + // PPH_STRING attributeValue1; + // PPH_STRING attributeValue2; + // + // attributeValue1 = PH_AUTO(PhCreateStringFromUnicodeString(&attribute->Values.pString[1])); + // attributeValue2 = PH_AUTO(PhCreateStringFromUnicodeString(&attribute->Values.pString[2])); + // + // ProcessNode->AppIdText = PhConcatStrings( + // 3, + // attributeValue2->Buffer, + // L"!", + // attributeValue1->Buffer + // ); + // + // break; + // } + // } + // } + // + // PhFree(info); + // } + // + // NtClose(tokenHandle); + // } + //} + } + } + + ProcessNode->ValidMask |= PHPN_APPID; + } +} + +static VOID PhpUpdateProcessNodeDpiAwareness( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static BOOL (WINAPI *getProcessDpiAwarenessInternal)( + _In_ HANDLE hprocess, + _Out_ ULONG *value + ); + + if (PhBeginInitOnce(&initOnce)) + { + getProcessDpiAwarenessInternal = PhGetDllProcedureAddress(L"user32.dll", "GetProcessDpiAwarenessInternal", 0); + PhEndInitOnce(&initOnce); + } + + if (!getProcessDpiAwarenessInternal) + return; + + if (!(ProcessNode->ValidMask & PHPN_DPIAWARENESS)) + { + if (ProcessNode->ProcessItem->QueryHandle) + { + ULONG dpiAwareness; + + if (getProcessDpiAwarenessInternal(ProcessNode->ProcessItem->QueryHandle, &dpiAwareness)) + ProcessNode->DpiAwareness = dpiAwareness + 1; + } + + ProcessNode->ValidMask |= PHPN_DPIAWARENESS; + } +} + +static VOID PhpUpdateProcessNodeFileAttributes( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_FILEATTRIBUTES)) + { + FILE_NETWORK_OPEN_INFORMATION networkOpenInfo; + + if (ProcessNode->ProcessItem->FileName && NT_SUCCESS(PhQueryFullAttributesFileWin32( + ProcessNode->ProcessItem->FileName->Buffer, + &networkOpenInfo + ))) + { + ProcessNode->FileLastWriteTime = networkOpenInfo.LastWriteTime; + ProcessNode->FileEndOfFile = networkOpenInfo.EndOfFile; + } + else + { + ProcessNode->FileEndOfFile.QuadPart = -1; + } + + ProcessNode->ValidMask |= PHPN_FILEATTRIBUTES; + } +} + +static VOID PhpUpdateProcessNodeDesktopInfo( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_DESKTOPINFO)) + { + HANDLE processHandle; + + PhClearReference(&ProcessNode->DesktopInfoText); + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + ProcessNode->ProcessId + ))) + { + PPH_STRING desktopinfo; + + if (NT_SUCCESS(PhGetProcessDesktopInfo(processHandle, &desktopinfo))) + { + ProcessNode->DesktopInfoText = desktopinfo; + } + + NtClose(processHandle); + } + + ProcessNode->ValidMask |= PHPN_DESKTOPINFO; + } +} + +static VOID PhpUpdateProcessBreakOnTermination( + _Inout_ PPH_PROCESS_NODE ProcessNode + ) +{ + if (!(ProcessNode->ValidMask & PHPN_CRITICAL)) + { + ProcessNode->BreakOnTerminationEnabled = FALSE; + + if (ProcessNode->ProcessItem->QueryHandle) + { + BOOLEAN breakOnTermination; + + if (NT_SUCCESS(PhGetProcessBreakOnTermination( + ProcessNode->ProcessItem->QueryHandle, + &breakOnTermination + ))) + { + ProcessNode->BreakOnTerminationEnabled = breakOnTermination; + } + } + + ProcessNode->ValidMask |= PHPN_CRITICAL; + } +} + +#define SORT_FUNCTION(Column) PhpProcessTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpProcessTreeNewCompare##Column( \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)_elem1; \ + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)_elem2; \ + PPH_PROCESS_ITEM processItem1 = node1->ProcessItem; \ + PPH_PROCESS_ITEM processItem2 = node2->ProcessItem; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId); \ + \ + return PhModifySort(sortResult, ProcessTreeListSortOrder); \ +} + +LONG PhpProcessTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = intptrcmp((LONG_PTR)((PPH_PROCESS_NODE)Node1)->ProcessItem->ProcessId, (LONG_PTR)((PPH_PROCESS_NODE)Node2)->ProcessItem->ProcessId); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareString(processItem1->ProcessName, processItem2->ProcessName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Pid) +{ + // Use signed int so DPCs and Interrupts are placed above System Idle Process. + sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Cpu) +{ + sortResult = singlecmp(processItem1->CpuUsage, processItem2->CpuUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoTotalRate) +{ + sortResult = uint64cmp( + processItem1->IoReadDelta.Delta + processItem1->IoWriteDelta.Delta + processItem1->IoOtherDelta.Delta, + processItem2->IoReadDelta.Delta + processItem2->IoWriteDelta.Delta + processItem2->IoOtherDelta.Delta + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PrivateBytes) +{ + sortResult = uintptrcmp(processItem1->VmCounters.PagefileUsage, processItem2->VmCounters.PagefileUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(UserName) +{ + sortResult = PhCompareStringWithNull(processItem1->UserName, processItem2->UserName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Description) +{ + PH_STRINGREF sr1; + PH_STRINGREF sr2; + + sr1 = processItem1->VersionInfo.FileDescription ? processItem1->VersionInfo.FileDescription->sr : node1->DescriptionText; + sr2 = processItem2->VersionInfo.FileDescription ? processItem2->VersionInfo.FileDescription->sr : node2->DescriptionText; + + sortResult = PhCompareStringRef(&sr1, &sr2, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CompanyName) +{ + sortResult = PhCompareStringWithNull( + processItem1->VersionInfo.CompanyName, + processItem2->VersionInfo.CompanyName, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Version) +{ + sortResult = PhCompareStringWithNull( + processItem1->VersionInfo.FileVersion, + processItem2->VersionInfo.FileVersion, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileName) +{ + sortResult = PhCompareStringWithNull( + processItem1->FileName, + processItem2->FileName, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CommandLine) +{ + sortResult = PhCompareStringWithNull( + processItem1->CommandLine, + processItem2->CommandLine, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PeakPrivateBytes) +{ + sortResult = uintptrcmp(processItem1->VmCounters.PeakPagefileUsage, processItem2->VmCounters.PeakPagefileUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(WorkingSet) +{ + sortResult = uintptrcmp(processItem1->VmCounters.WorkingSetSize, processItem2->VmCounters.WorkingSetSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PeakWorkingSet) +{ + sortResult = uintptrcmp(processItem1->VmCounters.PeakWorkingSetSize, processItem2->VmCounters.PeakWorkingSetSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PrivateWs) +{ + sortResult = uintptrcmp(processItem1->WorkingSetPrivateSize, processItem2->WorkingSetPrivateSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(SharedWs) +{ + PhpUpdateProcessNodeWsCounters(node1); + PhpUpdateProcessNodeWsCounters(node2); + sortResult = uintptrcmp(node1->WsCounters.NumberOfSharedPages, node2->WsCounters.NumberOfSharedPages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ShareableWs) +{ + PhpUpdateProcessNodeWsCounters(node1); + PhpUpdateProcessNodeWsCounters(node2); + sortResult = uintptrcmp(node1->WsCounters.NumberOfShareablePages, node2->WsCounters.NumberOfShareablePages); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VirtualSize) +{ + sortResult = uintptrcmp(processItem1->VmCounters.VirtualSize, processItem2->VmCounters.VirtualSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PeakVirtualSize) +{ + sortResult = uintptrcmp(processItem1->VmCounters.PeakVirtualSize, processItem2->VmCounters.PeakVirtualSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PageFaults) +{ + sortResult = uintcmp(processItem1->VmCounters.PageFaultCount, processItem2->VmCounters.PageFaultCount); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(SessionId) +{ + sortResult = uintcmp(processItem1->SessionId, processItem2->SessionId); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(BasePriority) +{ + sortResult = intcmp(processItem1->BasePriority, processItem2->BasePriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Threads) +{ + sortResult = uintcmp(processItem1->NumberOfThreads, processItem2->NumberOfThreads); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Handles) +{ + sortResult = uintcmp(processItem1->NumberOfHandles, processItem2->NumberOfHandles); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(GdiHandles) +{ + sortResult = uintcmp(node1->GdiHandles, node2->GdiHandles); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(UserHandles) +{ + sortResult = uintcmp(node1->UserHandles, node2->UserHandles); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoRoRate) +{ + sortResult = uint64cmp( + processItem1->IoReadDelta.Delta + processItem1->IoOtherDelta.Delta, + processItem2->IoReadDelta.Delta + processItem2->IoOtherDelta.Delta + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoWRate) +{ + sortResult = uint64cmp( + processItem1->IoWriteDelta.Delta, + processItem2->IoWriteDelta.Delta + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Integrity) +{ + sortResult = uintcmp(processItem1->IntegrityLevel, processItem2->IntegrityLevel); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoPriority) +{ + sortResult = uintcmp(node1->IoPriority, node2->IoPriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PagePriority) +{ + sortResult = uintcmp(node1->PagePriority, node2->PagePriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StartTime) +{ + sortResult = int64cmp(processItem1->CreateTime.QuadPart, processItem2->CreateTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TotalCpuTime) +{ + sortResult = uint64cmp( + processItem1->KernelTime.QuadPart + processItem1->UserTime.QuadPart, + processItem2->KernelTime.QuadPart + processItem2->UserTime.QuadPart + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(KernelCpuTime) +{ + sortResult = uint64cmp( + processItem1->KernelTime.QuadPart, + processItem2->KernelTime.QuadPart + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(UserCpuTime) +{ + sortResult = uint64cmp( + processItem1->UserTime.QuadPart, + processItem2->UserTime.QuadPart + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerificationStatus) +{ + sortResult = intcmp(processItem1->VerifyResult, processItem2->VerifyResult); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerifiedSigner) +{ + sortResult = PhCompareStringWithNull( + processItem1->VerifySignerName, + processItem2->VerifySignerName, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Aslr) +{ + PhpUpdateProcessNodeImage(node1); + PhpUpdateProcessNodeImage(node2); + sortResult = intcmp( + node1->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, + node2->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(RelativeStartTime) +{ + sortResult = -int64cmp(processItem1->CreateTime.QuadPart, processItem2->CreateTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Bits) +{ + sortResult = intcmp(processItem1->IsWow64Valid, processItem2->IsWow64Valid); + + if (sortResult == 0) + sortResult = intcmp(processItem1->IsWow64, processItem2->IsWow64); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Elevation) +{ + ULONG key1; + ULONG key2; + + switch (processItem1->ElevationType) + { + case TokenElevationTypeFull: + key1 = 2; + break; + case TokenElevationTypeLimited: + key1 = 1; + break; + default: + key1 = 0; + break; + } + + switch (processItem2->ElevationType) + { + case TokenElevationTypeFull: + key2 = 2; + break; + case TokenElevationTypeLimited: + key2 = 1; + break; + default: + key2 = 0; + break; + } + + sortResult = intcmp(key1, key2); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(WindowTitle) +{ + PhpUpdateProcessNodeWindow(node1); + PhpUpdateProcessNodeWindow(node2); + sortResult = PhCompareStringWithNull(node1->WindowText, node2->WindowText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(WindowStatus) +{ + PhpUpdateProcessNodeWindow(node1); + PhpUpdateProcessNodeWindow(node2); + sortResult = intcmp(node1->WindowHung, node2->WindowHung); + + // Make sure all processes with windows get grouped together. + if (sortResult == 0) + sortResult = intcmp(!!node1->WindowHandle, !!node2->WindowHandle); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Cycles) +{ + sortResult = uint64cmp(processItem1->CycleTimeDelta.Value, processItem2->CycleTimeDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CyclesDelta) +{ + sortResult = uint64cmp(processItem1->CycleTimeDelta.Delta, processItem2->CycleTimeDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Dep) +{ + PhpUpdateProcessNodeDepStatus(node1); + PhpUpdateProcessNodeDepStatus(node2); + sortResult = uintcmp(node1->DepStatus, node2->DepStatus); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Virtualized) +{ + PhpUpdateProcessNodeToken(node1); + PhpUpdateProcessNodeToken(node2); + sortResult = ucharcmp(node1->VirtualizationEnabled, node2->VirtualizationEnabled); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ContextSwitches) +{ + sortResult = uintcmp(processItem1->ContextSwitchesDelta.Value, processItem2->ContextSwitchesDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ContextSwitchesDelta) +{ + sortResult = intcmp((LONG)processItem1->ContextSwitchesDelta.Delta, (LONG)processItem2->ContextSwitchesDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PageFaultsDelta) +{ + sortResult = uintcmp(processItem1->PageFaultsDelta.Delta, processItem2->PageFaultsDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoReads) +{ + sortResult = uint64cmp(processItem1->IoReadCountDelta.Value, processItem2->IoReadCountDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoWrites) +{ + sortResult = uint64cmp(processItem1->IoWriteCountDelta.Value, processItem2->IoWriteCountDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoOther) +{ + sortResult = uint64cmp(processItem1->IoOtherCountDelta.Value, processItem2->IoOtherCountDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoReadBytes) +{ + sortResult = uint64cmp(processItem1->IoReadDelta.Value, processItem2->IoReadDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoWriteBytes) +{ + sortResult = uint64cmp(processItem1->IoWriteDelta.Value, processItem2->IoWriteDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoOtherBytes) +{ + sortResult = uint64cmp(processItem1->IoOtherDelta.Value, processItem2->IoOtherDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoReadsDelta) +{ + sortResult = uint64cmp(processItem1->IoReadCountDelta.Delta, processItem2->IoReadCountDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoWritesDelta) +{ + sortResult = uint64cmp(processItem1->IoWriteCountDelta.Delta, processItem2->IoWriteCountDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoOtherDelta) +{ + sortResult = uint64cmp(processItem1->IoOtherCountDelta.Delta, processItem2->IoOtherCountDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(OsContext) +{ + PhpUpdateProcessOsContext(node1); + PhpUpdateProcessOsContext(node2); + sortResult = uintcmp(node1->OsContextVersion, node2->OsContextVersion); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PagedPool) +{ + sortResult = uintptrcmp(processItem1->VmCounters.QuotaPagedPoolUsage, processItem2->VmCounters.QuotaPagedPoolUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PeakPagedPool) +{ + sortResult = uintptrcmp(processItem1->VmCounters.QuotaPeakPagedPoolUsage, processItem2->VmCounters.QuotaPeakPagedPoolUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(NonPagedPool) +{ + sortResult = uintptrcmp(processItem1->VmCounters.QuotaNonPagedPoolUsage, processItem2->VmCounters.QuotaNonPagedPoolUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PeakNonPagedPool) +{ + sortResult = uintptrcmp(processItem1->VmCounters.QuotaPeakNonPagedPoolUsage, processItem2->VmCounters.QuotaPeakNonPagedPoolUsage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(MinimumWorkingSet) +{ + PhpUpdateProcessNodeQuotaLimits(node1); + PhpUpdateProcessNodeQuotaLimits(node2); + sortResult = uintptrcmp(node1->MinimumWorkingSetSize, node2->MinimumWorkingSetSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(MaximumWorkingSet) +{ + PhpUpdateProcessNodeQuotaLimits(node1); + PhpUpdateProcessNodeQuotaLimits(node2); + sortResult = uintptrcmp(node1->MaximumWorkingSetSize, node2->MaximumWorkingSetSize); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PrivateBytesDelta) +{ + sortResult = intptrcmp(processItem1->PrivateBytesDelta.Delta, processItem2->PrivateBytesDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Subsystem) +{ + PhpUpdateProcessNodeImage(node1); + PhpUpdateProcessNodeImage(node2); + sortResult = ushortcmp(node1->ImageSubsystem, node2->ImageSubsystem); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PackageName) +{ + sortResult = PhCompareStringWithNullSortOrder(processItem1->PackageFullName, processItem2->PackageFullName, ProcessTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(AppId) +{ + PhpUpdateProcessNodeAppId(node1); + PhpUpdateProcessNodeAppId(node2); + sortResult = PhCompareStringWithNullSortOrder(node1->AppIdText, node2->AppIdText, ProcessTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(DpiAwareness) +{ + PhpUpdateProcessNodeDpiAwareness(node1); + PhpUpdateProcessNodeDpiAwareness(node2); + sortResult = uintcmp(node1->DpiAwareness, node2->DpiAwareness); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CfGuard) +{ + sortResult = uintcmp(node1->ProcessItem->IsControlFlowGuardEnabled, node2->ProcessItem->IsControlFlowGuardEnabled); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TimeStamp) +{ + PhpUpdateProcessNodeImage(node1); + PhpUpdateProcessNodeImage(node2); + sortResult = uintcmp(node1->ImageTimeDateStamp, node2->ImageTimeDateStamp); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileModifiedTime) +{ + PhpUpdateProcessNodeFileAttributes(node1); + PhpUpdateProcessNodeFileAttributes(node2); + sortResult = int64cmp(node1->FileLastWriteTime.QuadPart, node2->FileLastWriteTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileSize) +{ + PhpUpdateProcessNodeFileAttributes(node1); + PhpUpdateProcessNodeFileAttributes(node2); + sortResult = int64cmp(node1->FileEndOfFile.QuadPart, node2->FileEndOfFile.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Subprocesses) +{ + sortResult = int64cmp(node1->Children->Count, node2->Children->Count); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(JobObjectId) +{ + sortResult = int64cmp(processItem1->JobObjectId, processItem2->JobObjectId); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Protection) +{ + // Use signed char so processes that we were unable to query (e.g. indicated by UCHAR_MAX) + // are placed below processes we are able to query (e.g. 0 and above). + sortResult = charcmp((CHAR)processItem1->Protection.Level, (CHAR)processItem2->Protection.Level); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(DesktopInfo) +{ + PhpUpdateProcessNodeDesktopInfo(node1); + PhpUpdateProcessNodeDesktopInfo(node2); + sortResult = PhCompareStringWithNullSortOrder(node1->DesktopInfoText, node2->DesktopInfoText, ProcessTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Critical) +{ + PhpUpdateProcessBreakOnTermination(node1); + PhpUpdateProcessBreakOnTermination(node2); + sortResult = ucharcmp(node1->BreakOnTerminationEnabled, node2->BreakOnTerminationEnabled); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(HexPid) +{ + sortResult = intptrcmp((LONG_PTR)processItem1->ProcessId, (LONG_PTR)processItem2->ProcessId); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpProcessTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_PROCESS_NODE node; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &ProcessTreeListCm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + node = (PPH_PROCESS_NODE)getChildren->Node; + + if (ProcessTreeListSortOrder == NoSortOrder) + { + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)ProcessNodeRootList->Items; + getChildren->NumberOfChildren = ProcessNodeRootList->Count; + } + else + { + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + } + else + { + if (!node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Name), + SORT_FUNCTION(Pid), + SORT_FUNCTION(Cpu), + SORT_FUNCTION(IoTotalRate), + SORT_FUNCTION(PrivateBytes), + SORT_FUNCTION(UserName), + SORT_FUNCTION(Description), + SORT_FUNCTION(CompanyName), + SORT_FUNCTION(Version), + SORT_FUNCTION(FileName), + SORT_FUNCTION(CommandLine), + SORT_FUNCTION(PeakPrivateBytes), + SORT_FUNCTION(WorkingSet), + SORT_FUNCTION(PeakWorkingSet), + SORT_FUNCTION(PrivateWs), + SORT_FUNCTION(SharedWs), + SORT_FUNCTION(ShareableWs), + SORT_FUNCTION(VirtualSize), + SORT_FUNCTION(PeakVirtualSize), + SORT_FUNCTION(PageFaults), + SORT_FUNCTION(SessionId), + SORT_FUNCTION(BasePriority), // Priority Class + SORT_FUNCTION(BasePriority), + SORT_FUNCTION(Threads), + SORT_FUNCTION(Handles), + SORT_FUNCTION(GdiHandles), + SORT_FUNCTION(UserHandles), + SORT_FUNCTION(IoRoRate), + SORT_FUNCTION(IoWRate), + SORT_FUNCTION(Integrity), + SORT_FUNCTION(IoPriority), + SORT_FUNCTION(PagePriority), + SORT_FUNCTION(StartTime), + SORT_FUNCTION(TotalCpuTime), + SORT_FUNCTION(KernelCpuTime), + SORT_FUNCTION(UserCpuTime), + SORT_FUNCTION(VerificationStatus), + SORT_FUNCTION(VerifiedSigner), + SORT_FUNCTION(Aslr), + SORT_FUNCTION(RelativeStartTime), + SORT_FUNCTION(Bits), + SORT_FUNCTION(Elevation), + SORT_FUNCTION(WindowTitle), + SORT_FUNCTION(WindowStatus), + SORT_FUNCTION(Cycles), + SORT_FUNCTION(CyclesDelta), + SORT_FUNCTION(Cpu), // CPU History + SORT_FUNCTION(PrivateBytes), // Private Bytes History + SORT_FUNCTION(IoTotalRate), // I/O History + SORT_FUNCTION(Dep), + SORT_FUNCTION(Virtualized), + SORT_FUNCTION(ContextSwitches), + SORT_FUNCTION(ContextSwitchesDelta), + SORT_FUNCTION(PageFaultsDelta), + SORT_FUNCTION(IoReads), + SORT_FUNCTION(IoWrites), + SORT_FUNCTION(IoOther), + SORT_FUNCTION(IoReadBytes), + SORT_FUNCTION(IoWriteBytes), + SORT_FUNCTION(IoOtherBytes), + SORT_FUNCTION(IoReadsDelta), + SORT_FUNCTION(IoWritesDelta), + SORT_FUNCTION(IoOtherDelta), + SORT_FUNCTION(OsContext), + SORT_FUNCTION(PagedPool), + SORT_FUNCTION(PeakPagedPool), + SORT_FUNCTION(NonPagedPool), + SORT_FUNCTION(PeakNonPagedPool), + SORT_FUNCTION(MinimumWorkingSet), + SORT_FUNCTION(MaximumWorkingSet), + SORT_FUNCTION(PrivateBytesDelta), + SORT_FUNCTION(Subsystem), + SORT_FUNCTION(PackageName), + SORT_FUNCTION(AppId), + SORT_FUNCTION(DpiAwareness), + SORT_FUNCTION(CfGuard), + SORT_FUNCTION(TimeStamp), + SORT_FUNCTION(FileModifiedTime), + SORT_FUNCTION(FileSize), + SORT_FUNCTION(Subprocesses), + SORT_FUNCTION(JobObjectId), + SORT_FUNCTION(Protection), + SORT_FUNCTION(DesktopInfo), + SORT_FUNCTION(Critical), + SORT_FUNCTION(HexPid), + }; + int (__cdecl *sortFunction)(const void *, const void *); + + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)ProcessNodeList->Items, + ProcessNodeList->Count, + ProcessTreeListSortColumn, + ProcessTreeListSortOrder, + &ProcessTreeListCm + )) + { + if (ProcessTreeListSortColumn < PHPRTLC_MAXIMUM) + sortFunction = sortFunctions[ProcessTreeListSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort(ProcessNodeList->Items, ProcessNodeList->Count, sizeof(PVOID), sortFunction); + } + } + + getChildren->Children = (PPH_TREENEW_NODE *)ProcessNodeList->Items; + getChildren->NumberOfChildren = ProcessNodeList->Count; + } + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + node = (PPH_PROCESS_NODE)isLeaf->Node; + + if (ProcessTreeListSortOrder == NoSortOrder) + isLeaf->IsLeaf = node->Children->Count == 0; + else + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_PROCESS_ITEM processItem; + + node = (PPH_PROCESS_NODE)getCellText->Node; + processItem = node->ProcessItem; + + switch (getCellText->Id) + { + case PHPRTLC_NAME: + getCellText->Text = processItem->ProcessName->sr; + break; + case PHPRTLC_PID: + PhInitializeStringRefLongHint(&getCellText->Text, processItem->ProcessIdString); + break; + case PHPRTLC_CPU: + { + FLOAT cpuUsage = 0; + + PhpAggregateFieldIfNeeded(node, AggregateTypeFloat, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CpuUsage), &cpuUsage); + cpuUsage *= 100; + + if (cpuUsage >= 0.01) + { + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatF(&format, cpuUsage, 2); + + if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) + { + getCellText->Text.Buffer = node->CpuUsageText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); // minus null terminator + } + } + else if (cpuUsage != 0 && PhCsShowCpuBelow001) + { + PH_FORMAT format[2]; + SIZE_T returnLength; + + PhInitFormatS(&format[0], L"< "); + PhInitFormatF(&format[1], 0.01, 2); + + if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) + { + getCellText->Text.Buffer = node->CpuUsageText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + } + break; + case PHPRTLC_IOTOTALRATE: + { + ULONG64 number = 0; + + if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) // delta is wrong on first run of process provider + { + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number); + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number); + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number); + number *= 1000; + number /= PhCsUpdateInterval; + } + + if (number != 0) + { + SIZE_T returnLength; + PH_FORMAT format[2]; + + PhInitFormatSize(&format[0], number); + PhInitFormatS(&format[1], L"/s"); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), node->IoTotalRateText, sizeof(node->IoTotalRateText), &returnLength)) + { + getCellText->Text.Buffer = node->IoTotalRateText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + } + break; + case PHPRTLC_PRIVATEBYTES: + { + SIZE_T value = 0; + SIZE_T returnLength; + PH_FORMAT format[1]; + + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PagefileUsage), &value); + PhInitFormatSize(&format[0], value); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), node->PrivateBytesText, sizeof(node->PrivateBytesText), &returnLength)) + { + getCellText->Text.Buffer = node->PrivateBytesText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + case PHPRTLC_USERNAME: + getCellText->Text = PhGetStringRef(processItem->UserName); + break; + case PHPRTLC_DESCRIPTION: + if (processItem->VersionInfo.FileDescription) + getCellText->Text = processItem->VersionInfo.FileDescription->sr; + else + getCellText->Text = node->DescriptionText; + break; + case PHPRTLC_COMPANYNAME: + getCellText->Text = PhGetStringRef(processItem->VersionInfo.CompanyName); + break; + case PHPRTLC_VERSION: + getCellText->Text = PhGetStringRef(processItem->VersionInfo.FileVersion); + break; + case PHPRTLC_FILENAME: + getCellText->Text = PhGetStringRef(processItem->FileName); + break; + case PHPRTLC_COMMANDLINE: + getCellText->Text = PhGetStringRef(processItem->CommandLine); + break; + case PHPRTLC_PEAKPRIVATEBYTES: + PhMoveReference(&node->PeakPrivateBytesText, PhFormatSize(processItem->VmCounters.PeakPagefileUsage, ULONG_MAX)); + getCellText->Text = node->PeakPrivateBytesText->sr; + break; + case PHPRTLC_WORKINGSET: + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.WorkingSetSize), &value); + PhMoveReference(&node->WorkingSetText, PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->WorkingSetText->sr; + } + break; + case PHPRTLC_PEAKWORKINGSET: + PhMoveReference(&node->PeakWorkingSetText, PhFormatSize(processItem->VmCounters.PeakWorkingSetSize, ULONG_MAX)); + getCellText->Text = node->PeakWorkingSetText->sr; + break; + case PHPRTLC_PRIVATEWS: + { + SIZE_T value = 0; + SIZE_T returnLength; + PH_FORMAT format[1]; + + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, WorkingSetPrivateSize), &value); + PhInitFormatSize(&format[0], value); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), node->PrivateWsText, sizeof(node->PrivateWsText), &returnLength)) + { + getCellText->Text.Buffer = node->PrivateWsText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + case PHPRTLC_SHAREDWS: + PhpUpdateProcessNodeWsCounters(node); + PhMoveReference(&node->SharedWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfSharedPages * PAGE_SIZE, ULONG_MAX)); + getCellText->Text = node->SharedWsText->sr; + break; + case PHPRTLC_SHAREABLEWS: + PhpUpdateProcessNodeWsCounters(node); + PhMoveReference(&node->ShareableWsText, PhFormatSize((ULONG64)node->WsCounters.NumberOfShareablePages * PAGE_SIZE, ULONG_MAX)); + getCellText->Text = node->ShareableWsText->sr; + break; + case PHPRTLC_VIRTUALSIZE: + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.VirtualSize), &value); + PhMoveReference(&node->VirtualSizeText, PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->VirtualSizeText->sr; + } + break; + case PHPRTLC_PEAKVIRTUALSIZE: + PhMoveReference(&node->PeakVirtualSizeText, PhFormatSize(processItem->VmCounters.PeakVirtualSize, ULONG_MAX)); + getCellText->Text = node->PeakVirtualSizeText->sr; + break; + case PHPRTLC_PAGEFAULTS: + { + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.PageFaultCount), &value); + PhMoveReference(&node->PageFaultsText, PhFormatUInt64(value, TRUE)); + getCellText->Text = node->PageFaultsText->sr; + } + break; + case PHPRTLC_SESSIONID: + PhInitializeStringRefLongHint(&getCellText->Text, processItem->SessionIdString); + break; + case PHPRTLC_PRIORITYCLASS: + PhInitializeStringRefLongHint(&getCellText->Text, PhGetProcessPriorityClassString(processItem->PriorityClass)); + break; + case PHPRTLC_BASEPRIORITY: + PhPrintInt32(node->BasePriorityText, processItem->BasePriority); + PhInitializeStringRefLongHint(&getCellText->Text, node->BasePriorityText); + break; + case PHPRTLC_THREADS: + { + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfThreads), &value); + PhpFormatInt32GroupDigits(value, node->ThreadsText, sizeof(node->ThreadsText), &getCellText->Text); + } + break; + case PHPRTLC_HANDLES: + { + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, NumberOfHandles), &value); + PhpFormatInt32GroupDigits(value, node->HandlesText, sizeof(node->HandlesText), &getCellText->Text); + } + break; + case PHPRTLC_GDIHANDLES: + { + PhpUpdateProcessNodeGdiUserHandles(node); + + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, GdiHandles), &value); + PhpFormatInt32GroupDigits(value, node->GdiHandlesText, sizeof(node->GdiHandlesText), &getCellText->Text); + } + break; + case PHPRTLC_USERHANDLES: + { + PhpUpdateProcessNodeGdiUserHandles(node); + + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, UserHandles), &value); + PhpFormatInt32GroupDigits(value, node->UserHandlesText, sizeof(node->UserHandlesText), &getCellText->Text); + } + break; + case PHPRTLC_IORORATE: + { + ULONG64 number = 0; + + if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) + { + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Delta), &number); + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Delta), &number); + number *= 1000; + number /= PhCsUpdateInterval; + } + + if (number != 0) + { + PH_FORMAT format[2]; + + PhInitFormatSize(&format[0], number); + PhInitFormatS(&format[1], L"/s"); + PhMoveReference(&node->IoRoRateText, PhFormat(format, 2, 0)); + getCellText->Text = node->IoRoRateText->sr; + } + } + break; + case PHPRTLC_IOWRATE: + { + ULONG64 number = 0; + + if (processItem->IoReadDelta.Delta != processItem->IoReadDelta.Value) + { + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Delta), &number); + number *= 1000; + number /= PhCsUpdateInterval; + } + + if (number != 0) + { + PH_FORMAT format[2]; + + PhInitFormatSize(&format[0], number); + PhInitFormatS(&format[1], L"/s"); + PhMoveReference(&node->IoWRateText, PhFormat(format, 2, 0)); + getCellText->Text = node->IoWRateText->sr; + } + } + break; + case PHPRTLC_INTEGRITY: + if (processItem->IntegrityString) + PhInitializeStringRefLongHint(&getCellText->Text, processItem->IntegrityString); + break; + case PHPRTLC_IOPRIORITY: + PhpUpdateProcessNodeIoPagePriority(node); + if (node->IoPriority != ULONG_MAX && node->IoPriority < MaxIoPriorityTypes) + PhInitializeStringRefLongHint(&getCellText->Text, PhIoPriorityHintNames[node->IoPriority]); + break; + case PHPRTLC_PAGEPRIORITY: + PhpUpdateProcessNodeIoPagePriority(node); + if (node->PagePriority != ULONG_MAX && node->PagePriority <= MEMORY_PRIORITY_NORMAL) + PhInitializeStringRefLongHint(&getCellText->Text, PhPagePriorityNames[node->PagePriority]); + break; + case PHPRTLC_STARTTIME: + if (processItem->CreateTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + + PhLargeIntegerToLocalSystemTime(&systemTime, &processItem->CreateTime); + PhMoveReference(&node->StartTimeText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->StartTimeText->sr; + } + break; + case PHPRTLC_TOTALCPUTIME: + PhMoveReference(&node->TotalCpuTimeText, PhFormatTimeSpan( + processItem->KernelTime.QuadPart + processItem->UserTime.QuadPart, + PH_TIMESPAN_HMSM + )); + getCellText->Text = node->TotalCpuTimeText->sr; + break; + case PHPRTLC_KERNELCPUTIME: + PhMoveReference(&node->KernelCpuTimeText, PhFormatTimeSpan( + processItem->KernelTime.QuadPart, + PH_TIMESPAN_HMSM + )); + getCellText->Text = node->KernelCpuTimeText->sr; + break; + case PHPRTLC_USERCPUTIME: + PhMoveReference(&node->UserCpuTimeText, PhFormatTimeSpan( + processItem->UserTime.QuadPart, + PH_TIMESPAN_HMSM + )); + getCellText->Text = node->UserCpuTimeText->sr; + break; + case PHPRTLC_VERIFICATIONSTATUS: + if (processItem->VerifyResult == VrTrusted) + PhInitializeStringRef(&getCellText->Text, L"Trusted"); + break; + case PHPRTLC_VERIFIEDSIGNER: + getCellText->Text = PhGetStringRef(processItem->VerifySignerName); + break; + case PHPRTLC_ASLR: + PhpUpdateProcessNodeImage(node); + if (node->ImageDllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) + PhInitializeStringRef(&getCellText->Text, L"ASLR"); + break; + case PHPRTLC_RELATIVESTARTTIME: + { + if (processItem->CreateTime.QuadPart != 0) + { + LARGE_INTEGER currentTime; + PPH_STRING startTimeString; + + PhQuerySystemTime(¤tTime); + startTimeString = PhFormatTimeSpanRelative(currentTime.QuadPart - processItem->CreateTime.QuadPart); + PhMoveReference(&node->RelativeStartTimeText, PhConcatStrings2(startTimeString->Buffer, L" ago")); + PhDereferenceObject(startTimeString); + getCellText->Text = node->RelativeStartTimeText->sr; + } + } + break; + case PHPRTLC_BITS: +#ifdef _WIN64 + if (processItem->IsWow64Valid) + PhInitializeStringRef(&getCellText->Text, processItem->IsWow64 ? L"32" : L"64"); +#else + PhInitializeStringRef(&getCellText->Text, L"32"); +#endif + break; + case PHPRTLC_ELEVATION: + { + PWSTR type; + + switch (processItem->ElevationType) + { + case TokenElevationTypeDefault: + type = L"N/A"; + break; + case TokenElevationTypeLimited: + type = L"Limited"; + break; + case TokenElevationTypeFull: + type = L"Full"; + break; + default: + type = L"N/A"; + break; + } + + PhInitializeStringRefLongHint(&getCellText->Text, type); + } + break; + case PHPRTLC_WINDOWTITLE: + PhpUpdateProcessNodeWindow(node); + PhSwapReference(&node->WindowTitleText, node->WindowText); + getCellText->Text = PhGetStringRef(node->WindowTitleText); + break; + case PHPRTLC_WINDOWSTATUS: + PhpUpdateProcessNodeWindow(node); + + if (node->WindowHandle) + PhInitializeStringRef(&getCellText->Text, node->WindowHung ? L"Not responding" : L"Running"); + + break; + case PHPRTLC_CYCLES: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->CyclesText, PhFormatUInt64(value, TRUE)); + getCellText->Text = node->CyclesText->sr; + } + } + break; + case PHPRTLC_CYCLESDELTA: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, CycleTimeDelta.Delta), &value); + + if (value != 0) + { + PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(value, TRUE)); + getCellText->Text = node->CyclesDeltaText->sr; + } + } + break; + case PHPRTLC_DEP: + PhpUpdateProcessNodeDepStatus(node); + + if (node->DepStatus & PH_PROCESS_DEP_ENABLED) + { + if (node->DepStatus & PH_PROCESS_DEP_PERMANENT) + PhInitializeStringRef(&getCellText->Text, L"DEP (permanent)"); + else + PhInitializeStringRef(&getCellText->Text, L"DEP"); + } + + break; + case PHPRTLC_VIRTUALIZED: + PhpUpdateProcessNodeToken(node); + + if (node->VirtualizationEnabled) + PhInitializeStringRef(&getCellText->Text, L"Virtualized"); + + break; + case PHPRTLC_CONTEXTSWITCHES: + { + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, ContextSwitchesDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->ContextSwitchesText, PhFormatUInt64(value, TRUE)); + getCellText->Text = node->ContextSwitchesText->sr; + } + } + break; + case PHPRTLC_CONTEXTSWITCHESDELTA: + if ((LONG)processItem->ContextSwitchesDelta.Delta >= 0) // the delta may be negative if a thread exits - just don't show anything + { + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, ContextSwitchesDelta.Delta), &value); + + if (value != 0) + { + PhMoveReference(&node->ContextSwitchesDeltaText, PhFormatUInt64(value, TRUE)); + getCellText->Text = node->ContextSwitchesDeltaText->sr; + } + } + break; + case PHPRTLC_PAGEFAULTSDELTA: + { + ULONG value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt32, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, PageFaultsDelta.Delta), &value); + + if (value != 0) + { + PhMoveReference(&node->PageFaultsDeltaText, PhFormatUInt64(value, TRUE)); + getCellText->Text = node->PageFaultsDeltaText->sr; + } + } + break; + case PHPRTLC_IOREADS: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadCountDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[0], PhFormatUInt64(value, TRUE)); + getCellText->Text = node->IoGroupText[0]->sr; + } + } + break; + case PHPRTLC_IOWRITES: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteCountDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[1], PhFormatUInt64(value, TRUE)); + getCellText->Text = node->IoGroupText[1]->sr; + } + } + break; + case PHPRTLC_IOOTHER: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherCountDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[2], PhFormatUInt64(value, TRUE)); + getCellText->Text = node->IoGroupText[2]->sr; + } + } + break; + case PHPRTLC_IOREADBYTES: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[3], PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->IoGroupText[3]->sr; + } + } + break; + case PHPRTLC_IOWRITEBYTES: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[4], PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->IoGroupText[4]->sr; + } + } + break; + case PHPRTLC_IOOTHERBYTES: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherDelta.Value), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[5], PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->IoGroupText[5]->sr; + } + } + break; + case PHPRTLC_IOREADSDELTA: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoReadCountDelta.Delta), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[6], PhFormatUInt64(value, TRUE)); + getCellText->Text = node->IoGroupText[6]->sr; + } + } + break; + case PHPRTLC_IOWRITESDELTA: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoWriteCountDelta.Delta), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[7], PhFormatUInt64(value, TRUE)); + getCellText->Text = node->IoGroupText[7]->sr; + } + } + break; + case PHPRTLC_IOOTHERDELTA: + { + ULONG64 value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, IoOtherCountDelta.Delta), &value); + + if (value != 0) + { + PhMoveReference(&node->IoGroupText[8], PhFormatUInt64(value, TRUE)); + getCellText->Text = node->IoGroupText[8]->sr; + } + } + break; + case PHPRTLC_OSCONTEXT: + PhpUpdateProcessOsContext(node); + switch (node->OsContextVersion) + { + case WINDOWS_10: + PhInitializeStringRef(&getCellText->Text, L"Windows 10"); + break; + case WINDOWS_8_1: + PhInitializeStringRef(&getCellText->Text, L"Windows 8.1"); + break; + case WINDOWS_8: + PhInitializeStringRef(&getCellText->Text, L"Windows 8"); + break; + case WINDOWS_7: + PhInitializeStringRef(&getCellText->Text, L"Windows 7"); + break; + case WINDOWS_VISTA: + PhInitializeStringRef(&getCellText->Text, L"Windows Vista"); + break; + case WINDOWS_XP: + PhInitializeStringRef(&getCellText->Text, L"Windows XP"); + break; + } + break; + case PHPRTLC_PAGEDPOOL: + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaPagedPoolUsage), &value); + PhMoveReference(&node->PagedPoolText, PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->PagedPoolText->sr; + } + break; + case PHPRTLC_PEAKPAGEDPOOL: + PhMoveReference(&node->PeakPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakPagedPoolUsage, ULONG_MAX)); + getCellText->Text = node->PeakPagedPoolText->sr; + break; + case PHPRTLC_NONPAGEDPOOL: + { + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, VmCounters.QuotaNonPagedPoolUsage), &value); + PhMoveReference(&node->NonPagedPoolText, PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->NonPagedPoolText->sr; + } + break; + case PHPRTLC_PEAKNONPAGEDPOOL: + PhMoveReference(&node->PeakNonPagedPoolText, PhFormatSize(processItem->VmCounters.QuotaPeakNonPagedPoolUsage, ULONG_MAX)); + getCellText->Text = node->PeakNonPagedPoolText->sr; + break; + case PHPRTLC_MINIMUMWORKINGSET: + { + PhpUpdateProcessNodeQuotaLimits(node); + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, MinimumWorkingSetSize), &value); + PhMoveReference(&node->MinimumWorkingSetText, PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->MinimumWorkingSetText->sr; + } + break; + case PHPRTLC_MAXIMUMWORKINGSET: + { + PhpUpdateProcessNodeQuotaLimits(node); + SIZE_T value = 0; + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessNode, FIELD_OFFSET(PH_PROCESS_NODE, MaximumWorkingSetSize), &value); + PhMoveReference(&node->MaximumWorkingSetText, PhFormatSize(value, ULONG_MAX)); + getCellText->Text = node->MaximumWorkingSetText->sr; + } + break; + case PHPRTLC_PRIVATEBYTESDELTA: + { + LONG_PTR delta = 0; + + PhpAggregateFieldIfNeeded(node, AggregateTypeIntPtr, AggregateLocationProcessItem, FIELD_OFFSET(PH_PROCESS_ITEM, PrivateBytesDelta.Delta), &delta); + + if (delta != 0) + { + PH_FORMAT format[2]; + + if (delta > 0) + { + PhInitFormatC(&format[0], '+'); + } + else + { + PhInitFormatC(&format[0], '-'); + delta = -delta; + } + + format[1].Type = SizeFormatType | FormatUseRadix; + format[1].Radix = (UCHAR)PhMaxSizeUnit; + format[1].u.Size = delta; + + PhMoveReference(&node->PrivateBytesDeltaText, PhFormat(format, 2, 0)); + getCellText->Text = node->PrivateBytesDeltaText->sr; + } + } + break; + case PHPRTLC_SUBSYSTEM: + PhpUpdateProcessNodeImage(node); + + switch (node->ImageSubsystem) + { + case 0: + break; + case IMAGE_SUBSYSTEM_NATIVE: + PhInitializeStringRef(&getCellText->Text, L"Native"); + break; + case IMAGE_SUBSYSTEM_WINDOWS_GUI: + PhInitializeStringRef(&getCellText->Text, L"Windows"); + break; + case IMAGE_SUBSYSTEM_WINDOWS_CUI: + PhInitializeStringRef(&getCellText->Text, L"Windows console"); + break; + case IMAGE_SUBSYSTEM_OS2_CUI: + PhInitializeStringRef(&getCellText->Text, L"OS/2"); + break; + case IMAGE_SUBSYSTEM_POSIX_CUI: + PhInitializeStringRef(&getCellText->Text, L"POSIX"); + break; + default: + PhInitializeStringRef(&getCellText->Text, L"Unknown"); + break; + } + break; + case PHPRTLC_PACKAGENAME: + getCellText->Text = PhGetStringRef(processItem->PackageFullName); + break; + case PHPRTLC_APPID: + PhpUpdateProcessNodeAppId(node); + getCellText->Text = PhGetStringRef(node->AppIdText); + break; + case PHPRTLC_DPIAWARENESS: + PhpUpdateProcessNodeDpiAwareness(node); + + switch (node->DpiAwareness) + { + case 0: + break; + case 1: + PhInitializeStringRef(&getCellText->Text, L"Unaware"); + break; + case 2: + PhInitializeStringRef(&getCellText->Text, L"System aware"); + break; + case 3: + PhInitializeStringRef(&getCellText->Text, L"Per-monitor aware"); + break; + } + break; + case PHPRTLC_CFGUARD: + if (processItem->IsControlFlowGuardEnabled) + PhInitializeStringRef(&getCellText->Text, L"CF Guard"); + break; + case PHPRTLC_TIMESTAMP: + PhpUpdateProcessNodeImage(node); + + if (node->ImageTimeDateStamp != 0) + { + LARGE_INTEGER time; + SYSTEMTIME systemTime; + + RtlSecondsSince1970ToTime(node->ImageTimeDateStamp, &time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + + PhMoveReference(&node->TimeStampText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->TimeStampText->sr; + } + break; + case PHPRTLC_FILEMODIFIEDTIME: + PhpUpdateProcessNodeFileAttributes(node); + + if (node->FileLastWriteTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + + PhLargeIntegerToLocalSystemTime(&systemTime, &node->FileLastWriteTime); + PhMoveReference(&node->FileModifiedTimeText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->FileModifiedTimeText->sr; + } + break; + case PHPRTLC_FILESIZE: + PhpUpdateProcessNodeFileAttributes(node); + + if (node->FileEndOfFile.QuadPart != -1) + { + PhMoveReference(&node->FileSizeText, PhFormatSize(node->FileEndOfFile.QuadPart, ULONG_MAX)); + getCellText->Text = node->FileSizeText->sr; + } + break; + case PHPRTLC_SUBPROCESSCOUNT: + { + PhMoveReference(&node->SubprocessCountText, PhFormatUInt64(node->Children->Count, TRUE)); + getCellText->Text = node->SubprocessCountText->sr; + } + break; + case PHPRTLC_JOBOBJECTID: + { + if (processItem->JobObjectId != 0) + { + PhPrintInt32(node->JobObjectIdText, processItem->JobObjectId); + PhInitializeStringRefLongHint(&getCellText->Text, node->JobObjectIdText); + } + } + break; + case PHPRTLC_PROTECTION: + { + if (processItem->Protection.Level != 0 && processItem->Protection.Level != UCHAR_MAX) + { + PhMoveReference(&node->ProtectionText, PhGetProcessItemProtectionText(processItem)); + getCellText->Text = node->ProtectionText->sr; + } + } + break; + case PHPRTLC_DESKTOP: + { + PhpUpdateProcessNodeDesktopInfo(node); + getCellText->Text = PhGetStringRef(node->DesktopInfoText); + } + break; + case PHPRTLC_CRITICAL: + { + PhpUpdateProcessBreakOnTermination(node); + + if (node->BreakOnTerminationEnabled) + PhInitializeStringRef(&getCellText->Text, L"Critical"); + } + break; + case PHPRTLC_PIDHEX: + { + if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId)) + { + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatIX(&format, HandleToUlong(processItem->ProcessId)); + + if (PhFormatToBuffer(&format, 1, node->PidHexText, sizeof(node->PidHexText), &returnLength)) + { + getCellText->Text.Buffer = node->PidHexText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PPH_PROCESS_ITEM processItem; + + node = (PPH_PROCESS_NODE)getNodeColor->Node; + processItem = node->ProcessItem; + + if (PhPluginsEnabled) + { + PH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor; + + getHighlightingColor.Parameter = processItem; + getHighlightingColor.BackColor = RGB(0xff, 0xff, 0xff); + getHighlightingColor.ForeColor = RGB(0x00, 0x00, 0x00); + getHighlightingColor.Handled = FALSE; + getHighlightingColor.Cache = FALSE; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), &getHighlightingColor); + + if (getHighlightingColor.Handled) + { + getNodeColor->BackColor = getHighlightingColor.BackColor; + getNodeColor->ForeColor = getHighlightingColor.ForeColor; + + if (!getNodeColor->ForeColor) + getNodeColor->Flags |= TN_AUTO_FORECOLOR; + + if (getHighlightingColor.Cache) + getNodeColor->Flags |= TN_CACHE; + + return TRUE; + } + } + + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + + if (!processItem) + ; // Dummy + else if (PhCsUseColorDebuggedProcesses && processItem->IsBeingDebugged) + getNodeColor->BackColor = PhCsColorDebuggedProcesses; + else if (PhCsUseColorSuspended && processItem->IsSuspended) + getNodeColor->BackColor = PhCsColorSuspended; + else if (PhCsUseColorHandleFiltered && processItem->IsProtectedHandle) + getNodeColor->BackColor = PhCsColorHandleFiltered; + else if (PhCsUseColorElevatedProcesses && processItem->IsElevated) + getNodeColor->BackColor = PhCsColorElevatedProcesses; + else if (PhCsUseColorPicoProcesses && processItem->IsSubsystemProcess) + getNodeColor->BackColor = PhCsColorPicoProcesses; + else if (PhCsUseColorImmersiveProcesses && processItem->IsImmersive) + getNodeColor->BackColor = PhCsColorImmersiveProcesses; + else if (PhCsUseColorDotNet && processItem->IsDotNet) + getNodeColor->BackColor = PhCsColorDotNet; + else if (PhCsUseColorPacked && processItem->IsPacked) + getNodeColor->BackColor = PhCsColorPacked; + else if (PhCsUseColorWow64Processes && processItem->IsWow64) + getNodeColor->BackColor = PhCsColorWow64Processes; + else if (PhCsUseColorJobProcesses && processItem->IsInSignificantJob) + getNodeColor->BackColor = PhCsColorJobProcesses; + else if ( + PhCsUseColorServiceProcesses && + ((processItem->ServiceList && processItem->ServiceList->Count != 0) || + (processItem->Sid && RtlEqualSid(processItem->Sid, &PhSeServiceSid)) || + (processItem->Sid && RtlEqualSid(processItem->Sid, &PhSeLocalServiceSid)) || + (processItem->Sid && RtlEqualSid(processItem->Sid, &PhSeNetworkServiceSid)) + )) + getNodeColor->BackColor = PhCsColorServiceProcesses; + else if ( + PhCsUseColorSystemProcesses && + ((processItem->Sid && RtlEqualSid(processItem->Sid, &PhSeLocalSystemSid)) || + PH_IS_FAKE_PROCESS_ID(processItem->ProcessId))) + getNodeColor->BackColor = PhCsColorSystemProcesses; + else if ( + PhCsUseColorOwnProcesses && + processItem->Sid && + RtlEqualSid(processItem->Sid, PhGetOwnTokenAttributes().TokenSid) + ) + getNodeColor->BackColor = PhCsColorOwnProcesses; + } + return TRUE; + case TreeNewGetNodeIcon: + { + PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; + + node = (PPH_PROCESS_NODE)getNodeIcon->Node; + + if (node->ProcessItem->SmallIcon) + { + getNodeIcon->Icon = node->ProcessItem->SmallIcon; + } + else + { + PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL); + } + + getNodeIcon->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + ULONG64 tickCount; + + node = (PPH_PROCESS_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0) + return FALSE; + + tickCount = NtGetTickCount64(); + + if ((LONG64)(node->TooltipTextValidToTickCount - tickCount) < 0) + PhClearReference(&node->TooltipText); + + if (PhEnableTooltipSupport && !node->TooltipText) + node->TooltipText = PhGetProcessTooltipText(node->ProcessItem, &node->TooltipTextValidToTickCount); + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->MaximumWidth = ULONG_MAX; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewCustomDraw: + { + PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; + PPH_PROCESS_ITEM processItem; + RECT rect; + PH_GRAPH_DRAW_INFO drawInfo; + + node = (PPH_PROCESS_NODE)customDraw->Node; + processItem = node->ProcessItem; + rect = customDraw->CellRect; + + if (rect.right - rect.left <= 1) + break; // nothing to draw + + // Generic graph pre-processing + switch (customDraw->Column->Id) + { + case PHPRTLC_CPUHISTORY: + case PHPRTLC_PRIVATEBYTESHISTORY: + case PHPRTLC_IOHISTORY: + memset(&drawInfo, 0, sizeof(PH_GRAPH_DRAW_INFO)); + drawInfo.Width = rect.right - rect.left - 1; // leave a small gap + drawInfo.Height = rect.bottom - rect.top - 1; // leave a small gap + drawInfo.Step = 2; + drawInfo.BackColor = RGB(0x00, 0x00, 0x00); + break; + } + + // Specific graph processing + switch (customDraw->Column->Id) + { + case PHPRTLC_CPUHISTORY: + { + drawInfo.Flags = PH_GRAPH_USE_LINE_2; + drawInfo.LineColor1 = PhCsColorCpuKernel; + drawInfo.LineColor2 = PhCsColorCpuUser; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorCpuKernel); + drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorCpuUser); + + PhGetDrawInfoGraphBuffers( + &node->CpuGraphBuffers, + &drawInfo, + processItem->CpuKernelHistory.Count + ); + + if (!node->CpuGraphBuffers.Valid) + { + PhCopyCircularBuffer_FLOAT(&processItem->CpuKernelHistory, + node->CpuGraphBuffers.Data1, drawInfo.LineDataCount); + PhCopyCircularBuffer_FLOAT(&processItem->CpuUserHistory, + node->CpuGraphBuffers.Data2, drawInfo.LineDataCount); + node->CpuGraphBuffers.Valid = TRUE; + } + } + break; + case PHPRTLC_PRIVATEBYTESHISTORY: + { + drawInfo.Flags = 0; + drawInfo.LineColor1 = PhCsColorPrivate; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorPrivate); + + PhGetDrawInfoGraphBuffers( + &node->PrivateGraphBuffers, + &drawInfo, + processItem->PrivateBytesHistory.Count + ); + + if (!node->PrivateGraphBuffers.Valid) + { + ULONG i; + FLOAT total; + FLOAT max; + + for (i = 0; i < drawInfo.LineDataCount; i++) + { + node->PrivateGraphBuffers.Data1[i] = + (FLOAT)PhGetItemCircularBuffer_SIZE_T(&processItem->PrivateBytesHistory, i); + } + + // This makes it easier for the user to see what processes are hogging memory. + // Scaling is still *not* consistent across all graphs. + total = (FLOAT)PhPerfInformation.CommittedPages * PAGE_SIZE / 4; // divide by 4 to make the scaling a bit better + max = (FLOAT)processItem->VmCounters.PeakPagefileUsage; + + if (max < total) + max = total; + + if (max != 0) + { + // Scale the data. + PhDivideSinglesBySingle( + node->PrivateGraphBuffers.Data1, + max, + drawInfo.LineDataCount + ); + } + + node->PrivateGraphBuffers.Valid = TRUE; + } + } + break; + case PHPRTLC_IOHISTORY: + { + drawInfo.Flags = PH_GRAPH_USE_LINE_2; + drawInfo.LineColor1 = PhCsColorIoReadOther; + drawInfo.LineColor2 = PhCsColorIoWrite; + drawInfo.LineBackColor1 = PhHalveColorBrightness(PhCsColorIoReadOther); + drawInfo.LineBackColor2 = PhHalveColorBrightness(PhCsColorIoWrite); + + PhGetDrawInfoGraphBuffers( + &node->IoGraphBuffers, + &drawInfo, + processItem->IoReadHistory.Count + ); + + if (!node->IoGraphBuffers.Valid) + { + ULONG i; + FLOAT total; + FLOAT max = 0; + + for (i = 0; i < drawInfo.LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + node->IoGraphBuffers.Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoReadHistory, i) + + (FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoOtherHistory, i); + node->IoGraphBuffers.Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG64(&processItem->IoWriteHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + // Make the scaling a bit more consistent across the processes. + // It does *not* scale all graphs using the same maximum. + total = (FLOAT)(PhIoReadDelta.Delta + PhIoWriteDelta.Delta + PhIoOtherDelta.Delta); + + if (max < total) + max = total; + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + node->IoGraphBuffers.Data1, + max, + drawInfo.LineDataCount + ); + PhDivideSinglesBySingle( + node->IoGraphBuffers.Data2, + max, + drawInfo.LineDataCount + ); + } + + node->IoGraphBuffers.Valid = TRUE; + } + } + break; + } + + // Draw the graph. + switch (customDraw->Column->Id) + { + case PHPRTLC_CPUHISTORY: + case PHPRTLC_PRIVATEBYTESHISTORY: + case PHPRTLC_IOHISTORY: + PhpNeedGraphContext(customDraw->Dc, drawInfo.Width, drawInfo.Height); + + if (GraphBits) + { + PhDrawGraphDirect(GraphContext, GraphBits, &drawInfo); + BitBlt( + customDraw->Dc, + rect.left, + rect.top + 1, // + 1 for small gap + drawInfo.Width, + drawInfo.Height, + GraphContext, + 0, + 0, + SRCCOPY + ); + } + + break; + } + } + return TRUE; + case TreeNewColumnResized: + { + PPH_TREENEW_COLUMN column = Parameter1; + ULONG i; + + if (column->Id == PHPRTLC_CPUHISTORY || column->Id == PHPRTLC_IOHISTORY || column->Id == PHPRTLC_PRIVATEBYTESHISTORY) + { + for (i = 0; i < ProcessNodeList->Count; i++) + { + node = ProcessNodeList->Items[i]; + + if (column->Id == PHPRTLC_CPUHISTORY) + node->CpuGraphBuffers.Valid = FALSE; + if (column->Id == PHPRTLC_IOHISTORY) + node->IoGraphBuffers.Valid = FALSE; + if (column->Id == PHPRTLC_PRIVATEBYTESHISTORY) + node->PrivateGraphBuffers.Valid = FALSE; + } + } + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &ProcessTreeListSortColumn, &ProcessTreeListSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(ProcessTreeListHandle, 0, -1); + break; + case VK_DELETE: + if (GetKeyState(VK_SHIFT) >= 0) + SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_TERMINATE, 0); + else + SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_TERMINATETREE, 0); + break; + case VK_RETURN: + if (GetKeyState(VK_CONTROL) >= 0) + SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_PROPERTIES, 0); + else + SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_OPENFILELOCATION, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = NoSortOrder; + PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(PhMainWndHandle, WM_COMMAND, ID_PROCESS_PROPERTIES, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + PhShowProcessContextMenu(contextMenu); + } + return TRUE; + case TreeNewNodeExpanding: + { + node = Parameter1; + + if (PhCsPropagateCpuUsage) + PhUpdateProcessNode(node); + } + return TRUE; + } + + return FALSE; +} + +PPH_PROCESS_ITEM PhGetSelectedProcessItem( + VOID + ) +{ + PPH_PROCESS_ITEM processItem = NULL; + ULONG i; + + for (i = 0; i < ProcessNodeList->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; + + if (node->Node.Selected) + { + processItem = node->ProcessItem; + break; + } + } + + return processItem; +} + +VOID PhGetSelectedProcessItems( + _Out_ PPH_PROCESS_ITEM **Processes, + _Out_ PULONG NumberOfProcesses + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + for (i = 0; i < ProcessNodeList->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; + + // HACK workaround issue with multiple select->search->termination and Searchbox->PhApplyTreeNewFilters (dmex) + if (!node->Node.Visible) + continue; + + if (node->Node.Selected) + PhAddItemArray(&array, &node->ProcessItem); + } + + *NumberOfProcesses = (ULONG)array.Count; + *Processes = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllProcessNodes( + VOID + ) +{ + TreeNew_DeselectRange(ProcessTreeListHandle, 0, -1); +} + +VOID PhExpandAllProcessNodes( + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < ProcessNodeList->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; + + if (node->Children->Count != 0 && node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(ProcessTreeListHandle); +} + +VOID PhInvalidateAllProcessNodes( + VOID + ) +{ + ULONG i; + + for (i = 0; i < ProcessNodeList->Count; i++) + { + PPH_PROCESS_NODE node = ProcessNodeList->Items[i]; + + memset(node->TextCache, 0, sizeof(PH_STRINGREF) * PHPRTLC_MAXIMUM); + PhInvalidateTreeNewNode(&node->Node, TN_CACHE_COLOR); + node->ValidMask = 0; + + // Invalidate graph buffers. + node->CpuGraphBuffers.Valid = FALSE; + node->PrivateGraphBuffers.Valid = FALSE; + node->IoGraphBuffers.Valid = FALSE; + } + + InvalidateRect(ProcessTreeListHandle, NULL, FALSE); +} + +VOID PhSelectAndEnsureVisibleProcessNode( + _In_ PPH_PROCESS_NODE ProcessNode + ) +{ + PhSelectAndEnsureVisibleProcessNodes(&ProcessNode, 1); +} + +VOID PhSelectAndEnsureVisibleProcessNodes( + _In_ PPH_PROCESS_NODE *ProcessNodes, + _In_ ULONG NumberOfProcessNodes + ) +{ + ULONG i; + PPH_PROCESS_NODE leader = NULL; + PPH_PROCESS_NODE node; + BOOLEAN needsRestructure = FALSE; + + PhDeselectAllProcessNodes(); + + for (i = 0; i < NumberOfProcessNodes; i++) + { + if (ProcessNodes[i]->Node.Visible) + { + leader = ProcessNodes[i]; + break; + } + } + + if (!leader) + return; + + // Expand recursively upwards, and select the nodes. + + for (i = 0; i < NumberOfProcessNodes; i++) + { + if (!ProcessNodes[i]->Node.Visible) + continue; + + node = ProcessNodes[i]->Parent; + + while (node) + { + if (!node->Node.Expanded) + needsRestructure = TRUE; + + node->Node.Expanded = TRUE; + node = node->Parent; + } + + ProcessNodes[i]->Node.Selected = TRUE; + } + + if (needsRestructure) + TreeNew_NodesStructured(ProcessTreeListHandle); + + TreeNew_SetFocusNode(ProcessTreeListHandle, &leader->Node); + TreeNew_SetMarkNode(ProcessTreeListHandle, &leader->Node); + TreeNew_EnsureVisible(ProcessTreeListHandle, &leader->Node); + TreeNew_InvalidateNode(ProcessTreeListHandle, &leader->Node); +} + +VOID PhpPopulateTableWithProcessNodes( + _In_ HWND TreeListHandle, + _In_ PPH_PROCESS_NODE Node, + _In_ ULONG Level, + _In_ PPH_STRING **Table, + _Inout_ PULONG Index, + _In_ PULONG DisplayToId, + _In_ ULONG Columns + ) +{ + ULONG i; + + for (i = 0; i < Columns; i++) + { + PH_TREENEW_GET_CELL_TEXT getCellText; + PPH_STRING text; + + getCellText.Node = &Node->Node; + getCellText.Id = DisplayToId[i]; + PhInitializeEmptyStringRef(&getCellText.Text); + TreeNew_GetCellText(TreeListHandle, &getCellText); + + if (i != 0) + { + if (getCellText.Text.Length == 0) + text = PhReferenceEmptyString(); + else + text = PhaCreateStringEx(getCellText.Text.Buffer, getCellText.Text.Length); + } + else + { + // If this is the first column in the row, add some indentation. + text = PhaCreateStringEx( + NULL, + getCellText.Text.Length + Level * sizeof(WCHAR) * sizeof(WCHAR) + ); + wmemset(text->Buffer, ' ', Level * sizeof(WCHAR)); + memcpy(&text->Buffer[Level * sizeof(WCHAR)], getCellText.Text.Buffer, getCellText.Text.Length); + } + + Table[*Index][i] = text; + } + + (*Index)++; + + // Process the children. + for (i = 0; i < Node->Children->Count; i++) + { + PhpPopulateTableWithProcessNodes( + TreeListHandle, + Node->Children->Items[i], + Level + 1, + Table, + Index, + DisplayToId, + Columns + ); + } +} + +PPH_LIST PhGetProcessTreeListLines( + _In_ HWND TreeListHandle, + _In_ ULONG NumberOfNodes, + _In_ PPH_LIST RootNodes, + _In_ ULONG Mode + ) +{ + PH_AUTO_POOL autoPool; + PPH_LIST lines; + // The number of rows in the table (including +1 for the column headers). + ULONG rows; + // The number of columns. + ULONG columns; + // A column display index to ID map. + PULONG displayToId; + // A column display index to text map. + PWSTR *displayToText; + // The actual string table. + PPH_STRING **table; + ULONG i; + ULONG j; + + // Use a local auto-pool to make memory mangement a bit less painful. + PhInitializeAutoPool(&autoPool); + + rows = NumberOfNodes + 1; + + // Create the display index to ID map. + PhMapDisplayIndexTreeNew(TreeListHandle, &displayToId, &displayToText, &columns); + + PhaCreateTextTable(&table, rows, columns); + + // Populate the first row with the column headers. + for (i = 0; i < columns; i++) + { + table[0][i] = PhaCreateString(displayToText[i]); + } + + // Go through the nodes in the process tree and populate each cell of the table. + + j = 1; // index starts at one because the first row contains the column headers. + + for (i = 0; i < RootNodes->Count; i++) + { + PhpPopulateTableWithProcessNodes( + TreeListHandle, + RootNodes->Items[i], + 0, + table, + &j, + displayToId, + columns + ); + } + + PhFree(displayToId); + PhFree(displayToText); + + lines = PhaFormatTextTable(table, rows, columns, Mode); + + PhDeleteAutoPool(&autoPool); + + return lines; +} + +VOID PhCopyProcessTree( + VOID + ) +{ + PPH_STRING text; + + text = PhGetTreeNewText(ProcessTreeListHandle, 0); + PhSetClipboardString(ProcessTreeListHandle, &text->sr); + PhDereferenceObject(text); +} + +VOID PhWriteProcessTree( + _Inout_ PPH_FILE_STREAM FileStream, + _In_ ULONG Mode + ) +{ + PPH_LIST lines; + ULONG i; + + lines = PhGetProcessTreeListLines( + ProcessTreeListHandle, + ProcessNodeList->Count, + ProcessNodeRootList, + Mode + ); + + for (i = 0; i < lines->Count; i++) + { + PPH_STRING line; + + line = lines->Items[i]; + PhWriteStringAsUtf8FileStream(FileStream, &line->sr); + PhDereferenceObject(line); + PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); + } + + PhDereferenceObject(lines); +} + +PPH_LIST PhDuplicateProcessNodeList( + VOID + ) +{ + PPH_LIST newList; + + newList = PhCreateList(ProcessNodeList->Count); + PhInsertItemsList(newList, 0, ProcessNodeList->Items, ProcessNodeList->Count); + + return newList; +} diff --git a/ProcessHacker/prpgenv.c b/ProcessHacker/prpgenv.c index 46f6d895bd5b..7771b6b76c92 100644 --- a/ProcessHacker/prpgenv.c +++ b/ProcessHacker/prpgenv.c @@ -3,6 +3,7 @@ * Process properties: Environment page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2018-2019 dmex * * This file is part of Process Hacker. * @@ -21,15 +22,98 @@ */ #include +#include +#include +#include + +#include #include #include +#include +#include +#include -#include +typedef enum _ENVIRONMENT_TREE_MENU_ITEM +{ + ENVIRONMENT_TREE_MENU_ITEM_HIDE_PROCESS_TYPE = 1, + ENVIRONMENT_TREE_MENU_ITEM_HIDE_USER_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_HIDE_SYSTEM_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_HIDE_CMD_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_PROCESS_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_USER_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_SYSTEM_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_CMD_TYPE, + ENVIRONMENT_TREE_MENU_ITEM_NEW_ENVIRONMENT_VARIABLE, + ENVIRONMENT_TREE_MENU_ITEM_MAXIMUM +} ENVIRONMENT_TREE_MENU_ITEM; + +typedef enum _ENVIRONMENT_TREE_COLUMN_MENU_ITEM +{ + ENVIRONMENT_TREE_COLUMN_MENU_ITEM_EDIT = 1, + ENVIRONMENT_TREE_COLUMN_MENU_ITEM_DELETE, + ENVIRONMENT_TREE_COLUMN_MENU_ITEM_COPY, + ENVIRONMENT_TREE_COLUMN_MENU_ITEM_MAXIMUM +} ENVIRONMENT_TREE_COLUMN_MENU_ITEM; -#include -#include +typedef enum _ENVIRONMENT_TREE_COLUMN_ITEM_NAME +{ + ENVIRONMENT_COLUMN_ITEM_NAME, + ENVIRONMENT_COLUMN_ITEM_VALUE, + ENVIRONMENT_COLUMN_ITEM_MAXIMUM +} ENVIRONMENT_TREE_COLUMN_ITEM_NAME; -#include +typedef enum _PROCESS_ENVIRONMENT_TREENODE_TYPE +{ + PROCESS_ENVIRONMENT_TREENODE_TYPE_GROUP, + PROCESS_ENVIRONMENT_TREENODE_TYPE_PROCESS, + PROCESS_ENVIRONMENT_TREENODE_TYPE_USER, + PROCESS_ENVIRONMENT_TREENODE_TYPE_SYSTEM +} PROCESS_ENVIRONMENT_TREENODE_TYPE; + +typedef struct _PHP_PROCESS_ENVIRONMENT_TREENODE +{ + PH_TREENEW_NODE Node; + PH_STRINGREF TextCache[ENVIRONMENT_COLUMN_ITEM_MAXIMUM]; + + struct _PHP_PROCESS_ENVIRONMENT_TREENODE* Parent; + PPH_LIST Children; + BOOLEAN HasChildren; + + ULONG Id; + PROCESS_ENVIRONMENT_TREENODE_TYPE Type; + PPH_STRING NameText; + PPH_STRING ValueText; + union + { + BOOLEAN Flags; + struct + { + BOOLEAN IsCmdVariable : 1; + BOOLEAN Spare : 7; + }; + }; +} PHP_PROCESS_ENVIRONMENT_TREENODE, *PPHP_PROCESS_ENVIRONMENT_TREENODE; + +PPHP_PROCESS_ENVIRONMENT_TREENODE PhpAddEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_opt_ PPHP_PROCESS_ENVIRONMENT_TREENODE ParentNode, + _In_ PROCESS_ENVIRONMENT_TREENODE_TYPE Type, + _In_ PPH_STRING Name, + _In_opt_ PPH_STRING Value + ); + +PPHP_PROCESS_ENVIRONMENT_TREENODE PhpFindEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ PWSTR KeyPath + ); + +VOID PhpClearEnvironmentTree( + _In_ PPH_ENVIRONMENT_CONTEXT Context + ); + +PPHP_PROCESS_ENVIRONMENT_TREENODE PhpGetSelectedEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context + ); typedef struct _EDIT_ENV_DIALOG_CONTEXT { @@ -37,7 +121,6 @@ typedef struct _EDIT_ENV_DIALOG_CONTEXT PWSTR Name; PWSTR Value; BOOLEAN Refresh; - PH_LAYOUT_MANAGER LayoutManager; RECT MinimumSize; } EDIT_ENV_DIALOG_CONTEXT, *PEDIT_ENV_DIALOG_CONTEXT; @@ -57,7 +140,7 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( _In_ LPARAM lParam ); -static VOID PhpClearEnvironmentItems( +VOID PhpClearEnvironmentItems( _Inout_ PPH_ENVIRONMENT_CONTEXT Context ) { @@ -67,31 +150,52 @@ static VOID PhpClearEnvironmentItems( for (i = 0; i < Context->Items.Count; i++) { item = PhItemArray(&Context->Items, i); - PhDereferenceObject(item->Name); - PhDereferenceObject(item->Value); + + if (item->Name) + PhDereferenceObject(item->Name); + if (item->Value) + PhDereferenceObject(item->Value); } PhClearArray(&Context->Items); } -static VOID PhpRefreshEnvironment( +VOID PhpRefreshEnvironmentList( _In_ HWND hwndDlg, _Inout_ PPH_ENVIRONMENT_CONTEXT Context, _In_ PPH_PROCESS_ITEM ProcessItem ) { - PVOID selectedIndex; HANDLE processHandle; PVOID environment; ULONG environmentLength; ULONG enumerationKey; PH_ENVIRONMENT_VARIABLE variable; + PPH_ENVIRONMENT_ITEM item; + PPHP_PROCESS_ENVIRONMENT_TREENODE processRootNode; + PPHP_PROCESS_ENVIRONMENT_TREENODE userRootNode; + PPHP_PROCESS_ENVIRONMENT_TREENODE systemRootNode; + ULONG i; - ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); + PhpClearEnvironmentTree(Context); + processRootNode = PhpAddEnvironmentNode(Context, NULL, PROCESS_ENVIRONMENT_TREENODE_TYPE_GROUP, PhaCreateString(L"Process"), NULL); + userRootNode = PhpAddEnvironmentNode(Context, NULL, PROCESS_ENVIRONMENT_TREENODE_TYPE_GROUP, PhaCreateString(L"User"), NULL); + systemRootNode = PhpAddEnvironmentNode(Context, NULL, PROCESS_ENVIRONMENT_TREENODE_TYPE_GROUP, PhaCreateString(L"System"), NULL); - selectedIndex = PhGetSelectedListViewItemParam(Context->ListViewHandle); - ListView_DeleteAllItems(Context->ListViewHandle); - PhpClearEnvironmentItems(Context); + if (DestroyEnvironmentBlock) + { + if (Context->SystemDefaultEnvironment) + { + DestroyEnvironmentBlock(Context->SystemDefaultEnvironment); + Context->SystemDefaultEnvironment = NULL; + } + + if (Context->UserDefaultEnvironment) + { + DestroyEnvironmentBlock(Context->UserDefaultEnvironment); + Context->UserDefaultEnvironment = NULL; + } + } if (NT_SUCCESS(PhOpenProcess( &processHandle, @@ -99,9 +203,23 @@ static VOID PhpRefreshEnvironment( ProcessItem->ProcessId ))) { - ULONG flags; + HANDLE tokenHandle; + ULONG flags = 0; + + if (CreateEnvironmentBlock) + { + CreateEnvironmentBlock(&Context->SystemDefaultEnvironment, NULL, FALSE); - flags = 0; + if (NT_SUCCESS(PhOpenProcessToken( + processHandle, + TOKEN_QUERY | TOKEN_DUPLICATE, + &tokenHandle + ))) + { + CreateEnvironmentBlock(&Context->UserDefaultEnvironment, tokenHandle, FALSE); + NtClose(tokenHandle); + } + } #ifdef _WIN64 if (ProcessItem->IsWow64) @@ -120,20 +238,15 @@ static VOID PhpRefreshEnvironment( while (PhEnumProcessEnvironmentVariables(environment, environmentLength, &enumerationKey, &variable)) { PH_ENVIRONMENT_ITEM item; - INT lvItemIndex; - // Don't display pairs with no name. - if (variable.Name.Length == 0) - continue; + // Remove the most confusing item. Some say it's just a weird per-drive current directory + // with a colon used as a drive letter for some reason. It should not be here. (diversenok) + //if (PhEqualStringRef2(&variable.Name, L"=::", FALSE) && PhEqualStringRef2(&variable.Value, L"::\\", FALSE)) + // continue; - // The strings are not guaranteed to be null-terminated, so we need to create some temporary strings. item.Name = PhCreateString2(&variable.Name); item.Value = PhCreateString2(&variable.Value); - lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, item.Name->Buffer, - UlongToPtr((ULONG)Context->Items.Count + 1)); - PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, item.Value->Buffer); - PhAddItemArray(&Context->Items, &item); } @@ -143,283 +256,84 @@ static VOID PhpRefreshEnvironment( NtClose(processHandle); } - if (selectedIndex) - { - ListView_SetItemState(Context->ListViewHandle, PtrToUlong(selectedIndex) - 1, - LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); - ListView_EnsureVisible(Context->ListViewHandle, PtrToUlong(selectedIndex) - 1, FALSE); - } - - ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); -} - -INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - PPH_ENVIRONMENT_CONTEXT environmentContext; - HWND lvHandle; - - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) - { - environmentContext = propPageContext->Context; - - if (environmentContext) - lvHandle = environmentContext->ListViewHandle; - } - else - { - return FALSE; - } - - switch (uMsg) + for (i = 0; i < Context->Items.Count; i++) { - case WM_INITDIALOG: - { - environmentContext = propPageContext->Context = PhAllocate(sizeof(PH_ENVIRONMENT_CONTEXT)); - memset(environmentContext, 0, sizeof(PH_ENVIRONMENT_CONTEXT)); - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - environmentContext->ListViewHandle = lvHandle; - PhInitializeArray(&environmentContext->Items, sizeof(PH_ENVIRONMENT_ITEM), 100); - - PhSetListViewStyle(lvHandle, TRUE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 200, L"Value"); - - PhSetExtendedListView(lvHandle); - PhLoadListViewColumnsFromSetting(L"EnvironmentListViewColumns", lvHandle); - - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - - PhpRefreshEnvironment(hwndDlg, environmentContext, processItem); - } - break; - case WM_DESTROY: - { - PhSaveListViewColumnsToSetting(L"EnvironmentListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + PPHP_PROCESS_ENVIRONMENT_TREENODE node; + PPHP_PROCESS_ENVIRONMENT_TREENODE parentNode; + PROCESS_ENVIRONMENT_TREENODE_TYPE nodeType; + PPH_STRING variableValue; - PhpClearEnvironmentItems(environmentContext); - PhDeleteArray(&environmentContext->Items); - - PhFree(environmentContext); + item = PhItemArray(&Context->Items, i); - PhpPropPageDlgProcDestroy(hwndDlg); - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_NEW), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_EDIT), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_DELETE), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_COMMAND: + if (Context->SystemDefaultEnvironment && PhQueryEnvironmentVariable( + Context->SystemDefaultEnvironment, + &item->Name->sr, + NULL + ) == STATUS_BUFFER_TOO_SMALL) { - switch (LOWORD(wParam)) + nodeType = PROCESS_ENVIRONMENT_TREENODE_TYPE_SYSTEM; + parentNode = systemRootNode; + + if (NT_SUCCESS(PhQueryEnvironmentVariable( + Context->SystemDefaultEnvironment, + &item->Name->sr, + &variableValue + ))) { - case IDC_NEW: - { - BOOLEAN refresh; - - if (PhpShowEditEnvDialog(hwndDlg, processItem, L"", NULL, &refresh) == IDOK && - refresh) - { - PhpRefreshEnvironment(hwndDlg, environmentContext, processItem); - } - } - break; - case IDC_EDIT: - case ID_ENVIRONMENT_EDIT: + if (!PhEqualString(variableValue, item->Value, FALSE)) { - PVOID index = PhGetSelectedListViewItemParam(lvHandle); - - if (index) - { - PPH_ENVIRONMENT_ITEM item = PhItemArray(&environmentContext->Items, PtrToUlong(index) - 1); - BOOLEAN refresh; - - if (PhpShowEditEnvDialog(hwndDlg, processItem, item->Name->Buffer, - item->Value->Buffer, &refresh) == IDOK && refresh) - { - PhpRefreshEnvironment(hwndDlg, environmentContext, processItem); - } - } + nodeType = PROCESS_ENVIRONMENT_TREENODE_TYPE_PROCESS; + parentNode = processRootNode; } - break; - case IDC_DELETE: - case ID_ENVIRONMENT_DELETE: - { - NTSTATUS status; - PVOID *indices; - ULONG numberOfIndices; - HANDLE processHandle; - LARGE_INTEGER timeout; - ULONG i; - PPH_ENVIRONMENT_ITEM item; - - PhGetSelectedListViewItemParams(lvHandle, &indices, &numberOfIndices); - - if (numberOfIndices != 0 && PhShowConfirmMessage( - hwndDlg, - L"delete", - numberOfIndices != 1 ? L"the selected environment variables" : L"the selected environment variable", - NULL, - FALSE)) - { - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - ProcessQueryAccess | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | - PROCESS_VM_READ | PROCESS_VM_WRITE, - processItem->ProcessId - ))) - { - timeout.QuadPart = -10 * PH_TIMEOUT_SEC; - - for (i = 0; i < numberOfIndices; i++) - { - item = PhItemArray(&environmentContext->Items, PtrToUlong(indices[i]) - 1); - PhSetEnvironmentVariableRemote(processHandle, &item->Name->sr, NULL, &timeout); - } - - NtClose(processHandle); - - PhpRefreshEnvironment(hwndDlg, environmentContext, processItem); - } - else - { - PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); - } - } - PhFree(indices); - } - break; - case ID_ENVIRONMENT_COPY: - { - PhCopyListView(lvHandle); - } - break; + PhDereferenceObject(variableValue); } } - break; - case WM_NOTIFY: + else if (Context->UserDefaultEnvironment && PhQueryEnvironmentVariable( + Context->UserDefaultEnvironment, + &item->Name->sr, + NULL + ) == STATUS_BUFFER_TOO_SMALL) { - LPNMHDR header = (LPNMHDR)lParam; - - PhHandleListViewNotifyBehaviors(lParam, lvHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - - switch (header->code) + nodeType = PROCESS_ENVIRONMENT_TREENODE_TYPE_USER; + parentNode = userRootNode; + + if (NT_SUCCESS(PhQueryEnvironmentVariable( + Context->UserDefaultEnvironment, + &item->Name->sr, + &variableValue + ))) { - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == lvHandle) - { - ULONG selectedCount; - - selectedCount = ListView_GetSelectedCount(lvHandle); - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), selectedCount == 1); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), selectedCount >= 1); - } - } - break; - case NM_DBLCLK: + if (!PhEqualString(variableValue, item->Value, FALSE)) { - if (header->hwndFrom == lvHandle) - { - SendMessage(hwndDlg, WM_COMMAND, ID_ENVIRONMENT_EDIT, 0); - } + nodeType = PROCESS_ENVIRONMENT_TREENODE_TYPE_PROCESS; + parentNode = processRootNode; } - break; - case LVN_KEYDOWN: - { - if (header->hwndFrom == lvHandle) - { - LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)header; - switch (keyDown->wVKey) - { - case VK_DELETE: - SendMessage(hwndDlg, WM_COMMAND, ID_ENVIRONMENT_DELETE, 0); - break; - } - } - } - break; + PhDereferenceObject(variableValue); } } - break; - case WM_CONTEXTMENU: + else { - if ((HWND)wParam == lvHandle) - { - POINT point; - PVOID *indices; - ULONG numberOfIndices; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); - - if (point.x == -1 && point.y == -1) - PhGetListViewContextMenuPoint((HWND)wParam, &point); - - PhGetSelectedListViewItemParams(lvHandle, &indices, &numberOfIndices); - - if (numberOfIndices != 0) - { - PPH_EMENU menu; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_ENVIRONMENT), 0); - PhSetFlagsEMenuItem(menu, ID_ENVIRONMENT_EDIT, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); - - if (numberOfIndices > 1) - PhEnableEMenuItem(menu, ID_ENVIRONMENT_EDIT, FALSE); + nodeType = PROCESS_ENVIRONMENT_TREENODE_TYPE_PROCESS; + parentNode = processRootNode; + } - PhShowEMenu( - menu, - hwndDlg, - PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - point.x, - point.y - ); - PhDestroyEMenu(menu); - } + node = PhpAddEnvironmentNode( + Context, + parentNode, + nodeType, + item->Name, + item->Value + ); - PhFree(indices); - } + if (item->Name && item->Name->Buffer[0] == L'=') + { + node->IsCmdVariable = TRUE; } - break; } - return FALSE; + PhApplyTreeNewFilters(&Context->TreeFilterSupport); } INT_PTR CALLBACK PhpEditEnvDlgProc( @@ -434,15 +348,15 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( if (uMsg == WM_INITDIALOG) { context = (PEDIT_ENV_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PEDIT_ENV_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -453,17 +367,16 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( { case WM_INITDIALOG: { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_NAME), NULL, - PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_VALUE), NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, - PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_NAME), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_VALUE), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhLayoutManagerLayout(&context->LayoutManager); context->MinimumSize.left = 0; @@ -472,13 +385,12 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( context->MinimumSize.bottom = 140; MapDialogRect(hwndDlg, &context->MinimumSize); - SetDlgItemText(hwndDlg, IDC_NAME, context->Name); - SetDlgItemText(hwndDlg, IDC_VALUE, context->Value ? context->Value : L""); + PhSetDialogItemText(hwndDlg, IDC_NAME, context->Name); + PhSetDialogItemText(hwndDlg, IDC_VALUE, context->Value ? context->Value : L""); - if (context->Value) - { - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_VALUE), TRUE); - } + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDCANCEL)); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: @@ -488,7 +400,7 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: { @@ -503,6 +415,31 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( HANDLE processHandle; LARGE_INTEGER timeout; + if (PhGetIntegerSetting(L"EnableWarnings") && !PhShowConfirmMessage( + hwndDlg, + L"edit", + L"the selected environment variable", + L"Some programs may restrict access or ban your account when editing the environment variable(s) of the process.", + FALSE + )) + { + break; + } + + if (PhIsProcessSuspended(context->ProcessItem->ProcessId)) + { + if (PhGetIntegerSetting(L"EnableWarnings") && PhShowMessage2( + hwndDlg, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_WARNING_ICON, + L"The process is suspended.", + L"Editing environment variable(s) of suspended processes is not supported.\n\nAre you sure you want to continue?" + ) == IDNO) + { + break; + } + } + if (!PhIsNullOrEmptyString(name)) { if (!PhEqualString2(name, context->Name, FALSE) || @@ -510,12 +447,12 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( { if (NT_SUCCESS(status = PhOpenProcess( &processHandle, - ProcessQueryAccess | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, context->ProcessItem->ProcessId ))) { - timeout.QuadPart = -10 * PH_TIMEOUT_SEC; + timeout.QuadPart = -(LONGLONG)UInt32x32To64(10, PH_TIMEOUT_SEC); // Delete the old environment variable if necessary. if (!PhEqualStringZ(context->Name, L"", FALSE) && @@ -544,7 +481,12 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( if (!NT_SUCCESS(status)) { - PhShowStatus(hwndDlg, L"Unable to set the environment variable", status, 0); + PhShowStatus(hwndDlg, L"Unable to set the environment variable.", status, 0); + break; + } + else if (status == STATUS_TIMEOUT) + { + PhShowStatus(hwndDlg, L"Unable to delete the environment variable.", 0, WAIT_TIMEOUT); break; } @@ -557,9 +499,9 @@ INT_PTR CALLBACK PhpEditEnvDlgProc( break; case IDC_NAME: { - if (HIWORD(wParam) == EN_CHANGE) + if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) { - EnableWindow(GetDlgItem(hwndDlg, IDOK), GetWindowTextLength(GetDlgItem(hwndDlg, IDC_NAME)) > 0); + EnableWindow(GetDlgItem(hwndDlg, IDOK), PhGetWindowTextLength(GetDlgItem(hwndDlg, IDC_NAME)) > 0); } } break; @@ -610,3 +552,978 @@ INT_PTR PhpShowEditEnvDialog( return result; } + +VOID PhpShowEnvironmentNodeContextMenu( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent + ) +{ + PPHP_PROCESS_ENVIRONMENT_TREENODE node; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + + if (!(node = PhpGetSelectedEnvironmentNode(Context))) + return; + + if (node->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_GROUP) + return; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ENVIRONMENT_TREE_COLUMN_MENU_ITEM_EDIT, L"Edit", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ENVIRONMENT_TREE_COLUMN_MENU_ITEM_DELETE, L"Delete", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ENVIRONMENT_TREE_COLUMN_MENU_ITEM_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, 3, Context->TreeNewHandle, ContextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + Context->WindowHandle, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + ContextMenuEvent->Location.x, + ContextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != -1) + { + if (!PhHandleCopyCellEMenuItem(selectedItem)) + { + switch (selectedItem->Id) + { + case ENVIRONMENT_TREE_COLUMN_MENU_ITEM_EDIT: + { + //PPH_ENVIRONMENT_ITEM item = PhItemArray(&Context->Items, node->Id); + BOOLEAN refresh; + + if (PhpShowEditEnvDialog( + Context->WindowHandle, + Context->ProcessItem, + node->NameText->Buffer, + node->ValueText->Buffer, + &refresh + ) == IDOK && refresh) + { + PhpRefreshEnvironmentList(Context->WindowHandle, Context, Context->ProcessItem); + } + } + break; + case ENVIRONMENT_TREE_COLUMN_MENU_ITEM_DELETE: + { + //PPH_ENVIRONMENT_ITEM item = PhItemArray(&Context->Items, node->Id); + NTSTATUS status; + HANDLE processHandle; + + if (PhGetIntegerSetting(L"EnableWarnings") && !PhShowConfirmMessage( + Context->WindowHandle, + L"delete", + L"the selected environment variable", + L"Some programs may restrict access or ban your account when editing the environment variable(s) of the process.", + FALSE + )) + { + break; + } + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | + PROCESS_VM_READ | PROCESS_VM_WRITE, + Context->ProcessItem->ProcessId + ))) + { + LARGE_INTEGER timeout; + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(10, PH_TIMEOUT_SEC); + status = PhSetEnvironmentVariableRemote( + processHandle, + &node->NameText->sr, + NULL, + &timeout + ); + NtClose(processHandle); + + PhpRefreshEnvironmentList(Context->WindowHandle, Context, Context->ProcessItem); + + if (status == STATUS_TIMEOUT) + { + PhShowStatus(Context->WindowHandle, L"Unable to delete the environment variable.", 0, WAIT_TIMEOUT); + } + else if (!NT_SUCCESS(status)) + { + PhShowStatus(Context->WindowHandle, L"Unable to delete the environment variable.", status, 0); + } + } + else + { + PhShowStatus(Context->WindowHandle, L"Unable to open the process.", status, 0); + } + } + break; + case ENVIRONMENT_TREE_COLUMN_MENU_ITEM_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(Context->TreeNewHandle, 0); + PhSetClipboardString(Context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + } + + PhDestroyEMenu(menu); +} + +static BOOLEAN PhpWordMatchEnvironmentStringRef( + _In_ PPH_STRINGREF SearchText, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = *SearchText; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +static BOOLEAN PhpWordMatchEnvironmentStringZ( + _In_ PPH_STRING SearchText, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + + return PhpWordMatchEnvironmentStringRef(&SearchText->sr, &text); +} + +VOID PhLoadSettingsEnvironmentList( + _Inout_ PPH_ENVIRONMENT_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhGetStringSetting(L"EnvironmentTreeListColumns"); + sortSettings = PhGetStringSetting(L"EnvironmentTreeListSort"); + Context->Flags = PhGetIntegerSetting(L"EnvironmentTreeListFlags"); + + PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSaveSettingsEnvironmentList( + _Inout_ PPH_ENVIRONMENT_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); + + PhSetIntegerSetting(L"EnvironmentTreeListFlags", Context->Flags); + PhSetStringSetting2(L"EnvironmentTreeListColumns", &settings->sr); + PhSetStringSetting2(L"EnvironmentTreeListSort", &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSetOptionsEnvironmentList( + _Inout_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ ULONG Options + ) +{ + switch (Options) + { + case ENVIRONMENT_TREE_MENU_ITEM_HIDE_PROCESS_TYPE: + Context->HideProcessEnvironment = !Context->HideProcessEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIDE_USER_TYPE: + Context->HideUserEnvironment = !Context->HideUserEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIDE_SYSTEM_TYPE: + Context->HideSystemEnvironment = !Context->HideSystemEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIDE_CMD_TYPE: + Context->HideCmdTypeEnvironment = !Context->HideCmdTypeEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_PROCESS_TYPE: + Context->HighlightProcessEnvironment = !Context->HighlightProcessEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_USER_TYPE: + Context->HighlightUserEnvironment = !Context->HighlightUserEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_SYSTEM_TYPE: + Context->HighlightSystemEnvironment = !Context->HighlightSystemEnvironment; + break; + case ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_CMD_TYPE: + Context->HighlightCmdEnvironment = !Context->HighlightCmdEnvironment; + break; + } +} + +BOOLEAN PhpEnvironmentNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPHP_PROCESS_ENVIRONMENT_TREENODE poolTagNode1 = *(PPHP_PROCESS_ENVIRONMENT_TREENODE *)Entry1; + PPHP_PROCESS_ENVIRONMENT_TREENODE poolTagNode2 = *(PPHP_PROCESS_ENVIRONMENT_TREENODE *)Entry2; + + return PhEqualStringRef(&poolTagNode1->NameText->sr, &poolTagNode2->NameText->sr, TRUE); +} + +ULONG PhpEnvironmentNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashStringRef(&(*(PPHP_PROCESS_ENVIRONMENT_TREENODE*)Entry)->NameText->sr, TRUE); +} + +VOID PhpDestroyEnvironmentNode( + _In_ PPHP_PROCESS_ENVIRONMENT_TREENODE Node + ) +{ + if (Node->NameText) + PhDereferenceObject(Node->NameText); + if (Node->ValueText) + PhDereferenceObject(Node->ValueText); + + PhFree(Node); +} + +PPHP_PROCESS_ENVIRONMENT_TREENODE PhpAddEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_opt_ PPHP_PROCESS_ENVIRONMENT_TREENODE ParentNode, + _In_ PROCESS_ENVIRONMENT_TREENODE_TYPE Type, + _In_ PPH_STRING Name, + _In_opt_ PPH_STRING Value + ) +{ + PPHP_PROCESS_ENVIRONMENT_TREENODE node; + + node = PhAllocateZero(sizeof(PHP_PROCESS_ENVIRONMENT_TREENODE)); + PhInitializeTreeNewNode(&node->Node); + + memset(node->TextCache, 0, sizeof(PH_STRINGREF) * ENVIRONMENT_COLUMN_ITEM_MAXIMUM); + node->Node.TextCache = node->TextCache; + node->Node.TextCacheSize = ENVIRONMENT_COLUMN_ITEM_MAXIMUM; + node->Children = PhCreateList(1); + + node->Type = Type; + PhSetReference(&node->NameText, Name); + if (Value) PhSetReference(&node->ValueText, Value); + + PhAddEntryHashtable(Context->NodeHashtable, &node); + PhAddItemList(Context->NodeList, node); + + if (Context->TreeFilterSupport.FilterList) + node->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->TreeFilterSupport, &node->Node); + + if (ParentNode) + { + node->HasChildren = FALSE; + + // This is a child node. + node->Parent = ParentNode; + PhAddItemList(ParentNode->Children, node); + } + else + { + node->HasChildren = TRUE; + node->Node.Expanded = TRUE; + + // This is a root node. + PhAddItemList(Context->NodeRootList, node); + } + + return node; +} + +PPHP_PROCESS_ENVIRONMENT_TREENODE PhpFindEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ PWSTR KeyPath + ) +{ + PHP_PROCESS_ENVIRONMENT_TREENODE lookupWindowNode; + PPHP_PROCESS_ENVIRONMENT_TREENODE lookupWindowNodePtr = &lookupWindowNode; + PPHP_PROCESS_ENVIRONMENT_TREENODE *windowNode; + + PhInitializeStringRefLongHint(&lookupWindowNode.NameText->sr, KeyPath); + + windowNode = (PPHP_PROCESS_ENVIRONMENT_TREENODE*)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupWindowNodePtr + ); + + if (windowNode) + return *windowNode; + else + return NULL; +} + +VOID PhpRemoveEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ PPHP_PROCESS_ENVIRONMENT_TREENODE Node + ) +{ + ULONG index = 0; + + // Remove from hashtable/list and cleanup. + PhRemoveEntryHashtable(Context->NodeHashtable, &Node); + + if ((index = PhFindItemList(Context->NodeList, Node)) != -1) + { + PhRemoveItemList(Context->NodeList, index); + } + + PhpDestroyEnvironmentNode(Node); + //TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhpUpdateEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ PPHP_PROCESS_ENVIRONMENT_TREENODE Node + ) +{ + memset(Node->TextCache, 0, sizeof(PH_STRINGREF) * ENVIRONMENT_COLUMN_ITEM_MAXIMUM); + + PhInvalidateTreeNewNode(&Node->Node, TN_CACHE_COLOR); + //TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhpExpandAllEnvironmentNodes( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_MODULE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +#define SORT_FUNCTION(Column) PhpEnvironmentTreeNewCompare##Column +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpEnvironmentTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPHP_PROCESS_ENVIRONMENT_TREENODE node1 = *(PPHP_PROCESS_ENVIRONMENT_TREENODE*)_elem1; \ + PPHP_PROCESS_ENVIRONMENT_TREENODE node2 = *(PPHP_PROCESS_ENVIRONMENT_TREENODE*)_elem2; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ + \ + return PhModifySort(sortResult, ((PPH_ENVIRONMENT_CONTEXT)_context)->TreeNewSortOrder); \ +} + +LONG PhpEnvironmentTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = uintptrcmp((ULONG_PTR)((PPHP_PROCESS_ENVIRONMENT_TREENODE)Node1)->Node.Index, (ULONG_PTR)((PPHP_PROCESS_ENVIRONMENT_TREENODE)Node2)->Node.Index); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareStringWithNull(node1->NameText, node2->NameText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Value) +{ + sortResult = PhCompareStringWithNull(node1->ValueText, node2->ValueText, FALSE); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpEnvironmentTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context +) +{ + PPH_ENVIRONMENT_CONTEXT context; + PPHP_PROCESS_ENVIRONMENT_TREENODE node; + + context = Context; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + node = (PPHP_PROCESS_ENVIRONMENT_TREENODE)getChildren->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + { + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items; + getChildren->NumberOfChildren = context->NodeRootList->Count; + } + else + { + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + } + else + { + if (!node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Name), + SORT_FUNCTION(Value) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortColumn < ENVIRONMENT_COLUMN_ITEM_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + node = (PPHP_PROCESS_ENVIRONMENT_TREENODE)isLeaf->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + isLeaf->IsLeaf = !node->HasChildren; + else + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1; + node = (PPHP_PROCESS_ENVIRONMENT_TREENODE)getCellText->Node; + + switch (getCellText->Id) + { + case ENVIRONMENT_COLUMN_ITEM_NAME: + getCellText->Text = PhGetStringRef(node->NameText); + break; + case ENVIRONMENT_COLUMN_ITEM_VALUE: + getCellText->Text = PhGetStringRef(node->ValueText); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = (PPH_TREENEW_GET_NODE_COLOR)Parameter1; + node = (PPHP_PROCESS_ENVIRONMENT_TREENODE)getNodeColor->Node; + + if (node->HasChildren) + { + NOTHING; + } + else + { + if (context->HighlightCmdEnvironment && node->IsCmdVariable) + getNodeColor->BackColor = PhCsColorDebuggedProcesses; + else if (context->HighlightProcessEnvironment && node->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_PROCESS) + getNodeColor->BackColor = PhCsColorServiceProcesses; + else if (context->HighlightUserEnvironment && node->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_USER) + getNodeColor->BackColor = PhCsColorOwnProcesses; + else if (context->HighlightSystemEnvironment && node->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_SYSTEM) + getNodeColor->BackColor = PhCsColorSystemProcesses; + } + + getNodeColor->Flags = TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + + // HACK + if (context->TreeFilterSupport.FilterList) + PhApplyTreeNewFilters(&context->TreeFilterSupport); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + PhpShowEnvironmentNodeContextMenu(context, contextMenuEvent); + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu( + data.Menu, + hwnd, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + data.MouseEvent->ScreenLocation.x, + data.MouseEvent->ScreenLocation.y + ); + + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(context->WindowHandle, WM_COMMAND, WM_PH_SET_LIST_VIEW_SETTINGS, 0); // HACK + } + return TRUE; + } + + return FALSE; +} + +VOID PhpClearEnvironmentTree( + _In_ PPH_ENVIRONMENT_CONTEXT Context +) +{ + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + PhpDestroyEnvironmentNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); + PhClearList(Context->NodeRootList); + + PhpClearEnvironmentItems(Context); +} + +PPHP_PROCESS_ENVIRONMENT_TREENODE PhpGetSelectedEnvironmentNode( + _In_ PPH_ENVIRONMENT_CONTEXT Context + ) +{ + PPHP_PROCESS_ENVIRONMENT_TREENODE windowNode = NULL; + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + { + windowNode = Context->NodeList->Items[i]; + + if (windowNode->Node.Selected) + return windowNode; + } + + return NULL; +} + +VOID PhpGetSelectedEnvironmentNodes( + _In_ PPH_ENVIRONMENT_CONTEXT Context, + _Out_ PPHP_PROCESS_ENVIRONMENT_TREENODE **PoolTags, + _Out_ PULONG NumberOfPoolTags + ) +{ + PPH_LIST list; + ULONG i; + + list = PhCreateList(2); + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPHP_PROCESS_ENVIRONMENT_TREENODE node = (PPHP_PROCESS_ENVIRONMENT_TREENODE)Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *PoolTags = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfPoolTags = list->Count; + + PhDereferenceObject(list); +} + +VOID PhpInitializeEnvironmentTree( + _Inout_ PPH_ENVIRONMENT_CONTEXT Context + ) +{ + Context->NodeList = PhCreateList(100); + Context->NodeRootList = PhCreateList(30); + Context->NodeHashtable = PhCreateHashtable( + sizeof(PHP_PROCESS_ENVIRONMENT_TREENODE), + PhpEnvironmentNodeHashtableEqualFunction, + PhpEnvironmentNodeHashtableHashFunction, + 100 + ); + + PhSetControlTheme(Context->TreeNewHandle, L"explorer"); + TreeNew_SetCallback(Context->TreeNewHandle, PhpEnvironmentTreeNewCallback, Context); + + PhAddTreeNewColumn(Context->TreeNewHandle, ENVIRONMENT_COLUMN_ITEM_NAME, TRUE, L"Name", 250, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, ENVIRONMENT_COLUMN_ITEM_VALUE, TRUE, L"Value", 250, PH_ALIGN_LEFT, 1, 0); + + TreeNew_SetTriState(Context->TreeNewHandle, TRUE); + TreeNew_SetSort(Context->TreeNewHandle, ENVIRONMENT_COLUMN_ITEM_NAME, NoSortOrder); + + PhCmInitializeManager(&Context->Cm, Context->TreeNewHandle, PHMOTLC_MAXIMUM, PhpEnvironmentTreeNewPostSortFunction); + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, Context->TreeNewHandle, Context->NodeList); +} + +VOID PhpDeleteEnvironmentTree( + _In_ PPH_ENVIRONMENT_CONTEXT Context + ) +{ + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PhpDestroyEnvironmentNode(Context->NodeList->Items[i]); + } + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +BOOLEAN PhpProcessEnvironmentTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPH_ENVIRONMENT_CONTEXT context = Context; + PPHP_PROCESS_ENVIRONMENT_TREENODE environmentNode = (PPHP_PROCESS_ENVIRONMENT_TREENODE)Node; + + if (!environmentNode->Parent && environmentNode->Children && environmentNode->Children->Count == 0) + return FALSE; + if (context->TreeNewSortOrder != NoSortOrder && environmentNode->HasChildren) + return FALSE; + + if (context->HideProcessEnvironment && environmentNode->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_PROCESS) + return FALSE; + if (context->HideUserEnvironment && environmentNode->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_USER) + return FALSE; + if (context->HideSystemEnvironment && environmentNode->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_SYSTEM) + return FALSE; + if (context->HideCmdTypeEnvironment && environmentNode->IsCmdVariable) + return FALSE; + + if (PhIsNullOrEmptyString(context->SearchboxText)) + return TRUE; + + if (!PhIsNullOrEmptyString(environmentNode->NameText)) + { + if (PhpWordMatchEnvironmentStringRef(&context->SearchboxText->sr, &environmentNode->NameText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(environmentNode->ValueText)) + { + if (PhpWordMatchEnvironmentStringRef(&context->SearchboxText->sr, &environmentNode->ValueText->sr)) + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK PhpProcessEnvironmentDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PPH_ENVIRONMENT_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + context = propPageContext->Context = PhAllocateZero(sizeof(PH_ENVIRONMENT_CONTEXT)); + context->WindowHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->SearchWindowHandle = GetDlgItem(hwndDlg, IDC_SEARCH); + context->SearchboxText = PhReferenceEmptyString(); + context->ProcessItem = processItem; + + PhCreateSearchControl(hwndDlg, context->SearchWindowHandle, L"Search Environment (Ctrl+K)"); + Edit_SetSel(context->SearchWindowHandle, 0, -1); + PhpInitializeEnvironmentTree(context); + + PhInitializeArray(&context->Items, sizeof(PH_ENVIRONMENT_ITEM), 100); + context->TreeFilterEntry = PhAddTreeNewFilter(&context->TreeFilterSupport, PhpProcessEnvironmentTreeFilterCallback, context); + + PhMoveReference(&context->StatusMessage, PhCreateString(L"There are no environment variables to display.")); + TreeNew_SetEmptyText(context->TreeNewHandle, &context->StatusMessage->sr, 0); + PhLoadSettingsEnvironmentList(context); + + PhpRefreshEnvironmentList(hwndDlg, context, processItem); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhRemoveTreeNewFilter(&context->TreeFilterSupport, context->TreeFilterEntry); + if (context->SearchboxText) PhDereferenceObject(context->SearchboxText); + + PhSaveSettingsEnvironmentList(context); + PhpDeleteEnvironmentTree(context); + + PhpClearEnvironmentItems(context); + PhDeleteArray(&context->Items); + PhClearReference(&context->StatusMessage); + + if (DestroyEnvironmentBlock) + { + if (context->SystemDefaultEnvironment) + DestroyEnvironmentBlock(context->SystemDefaultEnvironment); + if (context->UserDefaultEnvironment) + DestroyEnvironmentBlock(context->UserDefaultEnvironment); + } + + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + PPH_LAYOUT_ITEM dialogItem; + + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, context->SearchWindowHandle, dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); + PhAddPropPageLayoutItem(hwndDlg, context->TreeNewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != context->SearchWindowHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchWindowHandle)); + + if (!PhEqualString(context->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhSwapReference(&context->SearchboxText, newSearchboxText); + + // Expand any hidden nodes to make search results visible. + PhpExpandAllEnvironmentNodes(context, TRUE); + + PhApplyTreeNewFilters(&context->TreeFilterSupport); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_OPTIONS: + { + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM processMenuItem; + PPH_EMENU_ITEM userMenuItem; + PPH_EMENU_ITEM systemMenuItem; + PPH_EMENU_ITEM cmdMenuItem; + PPH_EMENU_ITEM highlightProcessMenuItem; + PPH_EMENU_ITEM highlightUserMenuItem; + PPH_EMENU_ITEM highlightSystemMenuItem; + PPH_EMENU_ITEM highlightCmdMenuItem; + PPH_EMENU_ITEM newProcessMenuItem; + PPH_EMENU_ITEM selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_OPTIONS), &rect); + + processMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIDE_PROCESS_TYPE, L"Hide process", NULL, NULL); + userMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIDE_USER_TYPE, L"Hide user", NULL, NULL); + systemMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIDE_SYSTEM_TYPE, L"Hide system", NULL, NULL); + cmdMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIDE_CMD_TYPE, L"Hide cmd", NULL, NULL); + highlightProcessMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_PROCESS_TYPE, L"Highlight process", NULL, NULL); + highlightUserMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_USER_TYPE, L"Highlight user", NULL, NULL); + highlightSystemMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_SYSTEM_TYPE, L"Highlight system", NULL, NULL); + highlightCmdMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_HIGHLIGHT_CMD_TYPE, L"Highlight cmd", NULL, NULL); + newProcessMenuItem = PhCreateEMenuItem(0, ENVIRONMENT_TREE_MENU_ITEM_NEW_ENVIRONMENT_VARIABLE, L"New variable...", NULL, NULL); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, processMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, userMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, systemMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, cmdMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, highlightProcessMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, highlightUserMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, highlightSystemMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, highlightCmdMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, newProcessMenuItem, ULONG_MAX); + + if (context->HideProcessEnvironment) + processMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HideUserEnvironment) + userMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HideSystemEnvironment) + systemMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HideCmdTypeEnvironment) + cmdMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HighlightProcessEnvironment) + highlightProcessMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HighlightUserEnvironment) + highlightUserMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HighlightSystemEnvironment) + highlightSystemMenuItem->Flags |= PH_EMENU_CHECKED; + if (context->HighlightCmdEnvironment) + highlightCmdMenuItem->Flags |= PH_EMENU_CHECKED; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id) + { + if (selectedItem->Id == ENVIRONMENT_TREE_MENU_ITEM_NEW_ENVIRONMENT_VARIABLE) + { + BOOLEAN refresh; + + if (PhpShowEditEnvDialog(hwndDlg, processItem, L"", NULL, &refresh) == IDOK && refresh) + { + PhpRefreshEnvironmentList(hwndDlg, context, processItem); + } + } + else + { + PhSetOptionsEnvironmentList(context, selectedItem->Id); + PhSaveSettingsEnvironmentList(context); + PhApplyTreeNewFilters(&context->TreeFilterSupport); + } + } + + PhDestroyEMenu(menu); + } + break; + case IDC_REFRESH: + { + PhpRefreshEnvironmentList(hwndDlg, context, processItem); + } + break; + case WM_PH_SET_LIST_VIEW_SETTINGS: // HACK + { + PPHP_PROCESS_ENVIRONMENT_TREENODE item = PhpGetSelectedEnvironmentNode(context); + BOOLEAN refresh; + + if (!item || item->Type == PROCESS_ENVIRONMENT_TREENODE_TYPE_GROUP) + break; + + if (PhpShowEditEnvDialog( + hwndDlg, + context->ProcessItem, + item->NameText->Buffer, + item->ValueText->Buffer, + &refresh + ) == IDOK && refresh) + { + PhpRefreshEnvironmentList(hwndDlg, context, context->ProcessItem); + } + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_REFRESH)); + return TRUE; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/prpggen.c b/ProcessHacker/prpggen.c index 30b2115d2695..d84de296043e 100644 --- a/ProcessHacker/prpggen.c +++ b/ProcessHacker/prpggen.c @@ -24,19 +24,63 @@ #include #include -#include - #include #include #include #include +#include #include #include #include +#include + static PWSTR ProtectedSignerStrings[] = - { L"", L" (Authenticode)", L" (CodeGen)", L" (Antimalware)", L" (Lsa)", L" (Windows)", L" (WinTcb)", L" (WinSystem)" }; + { L"", L" (Authenticode)", L" (CodeGen)", L" (Antimalware)", L" (Lsa)", L" (Windows)", L" (WinTcb)", L" (WinSystem)", L" (StoreApp)" }; + +PPH_STRING PhGetProcessItemProtectionText( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + if (ProcessItem->Protection.Level != UCHAR_MAX) + { + if (WindowsVersion >= WINDOWS_8_1) + { + PWSTR type; + PWSTR signer; + + switch (ProcessItem->Protection.Type) + { + case PsProtectedTypeNone: + type = L"None"; + break; + case PsProtectedTypeProtectedLight: + type = L"Light"; + break; + case PsProtectedTypeProtected: + type = L"Full"; + break; + default: + type = L"Unknown"; + break; + } + + if (ProcessItem->Protection.Signer < sizeof(ProtectedSignerStrings) / sizeof(PWSTR)) + signer = ProtectedSignerStrings[ProcessItem->Protection.Signer]; + else + signer = L""; + + return PhConcatStrings2(type, signer); + } + else + { + return PhCreateString(ProcessItem->IsProtectedProcess ? L"Yes" : L"None"); + } + } + + return PhCreateString(L"N/A"); +} NTSTATUS PhpProcessGeneralOpenProcess( _Out_ PHANDLE Handle, @@ -66,7 +110,7 @@ VOID PhpUpdateProcessMitigationPolicies( HANDLE processHandle; PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION information; - SetDlgItemText(hwndDlg, IDC_MITIGATION, L"N/A"); + PhSetDialogItemText(hwndDlg, IDC_MITIGATION, L"N/A"); if (NT_SUCCESS(status = PhOpenProcess( &processHandle, @@ -100,11 +144,11 @@ VOID PhpUpdateProcessMitigationPolicies( if (sb.String->Length != 0) { PhRemoveEndStringBuilder(&sb, 2); - SetDlgItemText(hwndDlg, IDC_MITIGATION, sb.String->Buffer); + PhSetDialogItemText(hwndDlg, IDC_MITIGATION, sb.String->Buffer); } else { - SetDlgItemText(hwndDlg, IDC_MITIGATION, L"None"); + PhSetDialogItemText(hwndDlg, IDC_MITIGATION, L"None"); } PhDeleteStringBuilder(&sb); @@ -128,8 +172,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( PPH_PROCESS_PROPPAGECONTEXT propPageContext; PPH_PROCESS_ITEM processItem; - if (!PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (!PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) return FALSE; switch (uMsg) @@ -147,8 +190,8 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( HICON folder; HICON magnifier; - folder = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER)); - magnifier = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_MAGNIFIER)); + folder = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_FOLDER)); + magnifier = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_MAGNIFIER)); SET_BUTTON_ICON(IDC_INSPECT, magnifier); SET_BUTTON_ICON(IDC_OPENFILENAME, folder); @@ -157,30 +200,50 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( // File - SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, - (WPARAM)processItem->LargeIcon, 0); + if (processItem->LargeIcon) + { + Static_SetIcon(GetDlgItem(hwndDlg, IDC_FILEICON), processItem->LargeIcon); + } + else + { + HICON iconLarge; + + PhGetStockApplicationIcon(NULL, &iconLarge); + Static_SetIcon(GetDlgItem(hwndDlg, IDC_FILEICON), iconLarge); + } if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId)) { - SetDlgItemText(hwndDlg, IDC_NAME, PhpGetStringOrNa(processItem->VersionInfo.FileDescription)); + PhSetDialogItemText(hwndDlg, IDC_NAME, PhpGetStringOrNa(processItem->VersionInfo.FileDescription)); } else { - SetDlgItemText(hwndDlg, IDC_NAME, processItem->ProcessName->Buffer); + PhSetDialogItemText(hwndDlg, IDC_NAME, processItem->ProcessName->Buffer); } - SetDlgItemText(hwndDlg, IDC_VERSION, PhpGetStringOrNa(processItem->VersionInfo.FileVersion)); - SetDlgItemText(hwndDlg, IDC_FILENAME, PhpGetStringOrNa(processItem->FileName)); - - if (!processItem->FileName) - EnableWindow(GetDlgItem(hwndDlg, IDC_OPENFILENAME), FALSE); + PhSetDialogItemText(hwndDlg, IDC_VERSION, PhpGetStringOrNa(processItem->VersionInfo.FileVersion)); + PhSetDialogItemText(hwndDlg, IDC_FILENAME, PhpGetStringOrNa(processItem->FileName)); { PPH_STRING inspectExecutables; + if (PhIsNullOrEmptyString(processItem->FileName)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_OPENFILENAME), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_INSPECT), FALSE); + } + else + { + if (!PhDoesFileExistsWin32(PhGetString(processItem->FileName))) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_OPENFILENAME), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_INSPECT), FALSE); + } + } + inspectExecutables = PhaGetStringSetting(L"ProgramInspectExecutables"); - if (!processItem->FileName || inspectExecutables->Length == 0) + if (PhIsNullOrEmptyString(inspectExecutables)) { EnableWindow(GetDlgItem(hwndDlg, IDC_INSPECT), FALSE); } @@ -190,14 +253,14 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( { if (processItem->VerifySignerName) { - SetDlgItemText(hwndDlg, IDC_COMPANYNAME_LINK, + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME_LINK, PhaFormatString(L"(Verified) %s", processItem->VerifySignerName->Buffer)->Buffer); ShowWindow(GetDlgItem(hwndDlg, IDC_COMPANYNAME), SW_HIDE); ShowWindow(GetDlgItem(hwndDlg, IDC_COMPANYNAME_LINK), SW_SHOW); } else { - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, PhaConcatStrings2( L"(Verified) ", PhGetStringOrEmpty(processItem->VersionInfo.CompanyName) @@ -206,7 +269,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( } else if (processItem->VerifyResult != VrUnknown) { - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, PhaConcatStrings2( L"(UNVERIFIED) ", PhGetStringOrEmpty(processItem->VersionInfo.CompanyName) @@ -214,13 +277,13 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( } else { - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, PhpGetStringOrNa(processItem->VersionInfo.CompanyName)); } // Command Line - SetDlgItemText(hwndDlg, IDC_CMDLINE, PhpGetStringOrNa(processItem->CommandLine)); + PhSetDialogItemText(hwndDlg, IDC_CMDLINE, PhpGetStringOrNa(processItem->CommandLine)); if (!processItem->CommandLine) EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWCOMMANDLINE), FALSE); @@ -229,7 +292,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess | PROCESS_VM_READ, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, processItem->ProcessId ))) { @@ -255,7 +318,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( processHandle = NULL; } - SetDlgItemText(hwndDlg, IDC_CURDIR, PhpGetStringOrNa(curDir)); + PhSetDialogItemText(hwndDlg, IDC_CURDIR, PhpGetStringOrNa(curDir)); // Started @@ -274,33 +337,29 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( PhLargeIntegerToLocalSystemTime(&startTimeFields, &startTime); startTimeString = PhaFormatDateTime(&startTimeFields); - SetDlgItemText(hwndDlg, IDC_STARTED, + PhSetDialogItemText(hwndDlg, IDC_STARTED, PhaFormatString(L"%s ago (%s)", startTimeRelativeString->Buffer, startTimeString->Buffer)->Buffer); } else { - SetDlgItemText(hwndDlg, IDC_STARTED, L"N/A"); + PhSetDialogItemText(hwndDlg, IDC_STARTED, L"N/A"); } // Parent - if (parentProcess = PhReferenceProcessItemForParent( - processItem->ParentProcessId, - processItem->ProcessId, - &processItem->CreateTime - )) + if (parentProcess = PhReferenceProcessItemForParent(processItem)) { clientId.UniqueProcess = parentProcess->ProcessId; clientId.UniqueThread = NULL; - SetDlgItemText(hwndDlg, IDC_PARENTPROCESS, + PhSetDialogItemText(hwndDlg, IDC_PARENTPROCESS, PH_AUTO_T(PH_STRING, PhGetClientIdNameEx(&clientId, parentProcess->ProcessName))->Buffer); PhDereferenceObject(parentProcess); } else { - SetDlgItemText(hwndDlg, IDC_PARENTPROCESS, + PhSetDialogItemText(hwndDlg, IDC_PARENTPROCESS, PhaFormatString(L"Non-existent process (%u)", HandleToUlong(processItem->ParentProcessId))->Buffer); EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWPARENTPROCESS), FALSE); } @@ -311,176 +370,93 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( // PEB address - SetDlgItemText(hwndDlg, IDC_PEBADDRESS, L"N/A"); + PhSetDialogItemText(hwndDlg, IDC_PEBADDRESS, L"N/A"); - PhOpenProcess( + if (NT_SUCCESS(PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, processItem->ProcessId - ); - - if (processHandle) + ))) { PhGetProcessBasicInformation(processHandle, &basicInfo); #ifdef _WIN64 if (processItem->IsWow64) { PhGetProcessPeb32(processHandle, &peb32); - SetDlgItemText(hwndDlg, IDC_PEBADDRESS, + PhSetDialogItemText(hwndDlg, IDC_PEBADDRESS, PhaFormatString(L"0x%Ix (32-bit: 0x%x)", basicInfo.PebBaseAddress, PtrToUlong(peb32))->Buffer); } else { #endif - SetDlgItemText(hwndDlg, IDC_PEBADDRESS, + PhSetDialogItemText(hwndDlg, IDC_PEBADDRESS, PhaFormatString(L"0x%Ix", basicInfo.PebBaseAddress)->Buffer); #ifdef _WIN64 } #endif + NtClose(processHandle); + processHandle = NULL; } // Protection - SetDlgItemText(hwndDlg, IDC_PROTECTION, L"N/A"); - - if (WINDOWS_HAS_LIMITED_ACCESS && processHandle) - { - if (WindowsVersion >= WINDOWS_8_1) - { - PS_PROTECTION protection; - - if (NT_SUCCESS(NtQueryInformationProcess( - processHandle, - ProcessProtectionInformation, - &protection, - sizeof(PS_PROTECTION), - NULL - ))) - { - PWSTR type; - PWSTR signer; - - switch (protection.Type) - { - case PsProtectedTypeNone: - type = L"None"; - break; - case PsProtectedTypeProtectedLight: - type = L"Light"; - break; - case PsProtectedTypeProtected: - type = L"Full"; - break; - default: - type = L"Unknown"; - break; - } - - if (protection.Signer < sizeof(ProtectedSignerStrings) / sizeof(PWSTR)) - signer = ProtectedSignerStrings[protection.Signer]; - else - signer = L""; - - SetDlgItemText(hwndDlg, IDC_PROTECTION, PhaConcatStrings2(type, signer)->Buffer); - } - } - else - { - SetDlgItemText(hwndDlg, IDC_PROTECTION, processItem->IsProtectedProcess ? L"Yes" : L"None"); - } - } - - if (processHandle) - NtClose(processHandle); + PhSetDialogItemText(hwndDlg, IDC_PROTECTION, PH_AUTO_T(PH_STRING, PhGetProcessItemProtectionText(processItem))->Buffer); #ifdef _WIN64 if (processItem->IsWow64Valid) { if (processItem->IsWow64) - SetDlgItemText(hwndDlg, IDC_PROCESSTYPETEXT, L"32-bit"); + PhSetDialogItemText(hwndDlg, IDC_PROCESSTYPETEXT, L"32-bit"); else - SetDlgItemText(hwndDlg, IDC_PROCESSTYPETEXT, L"64-bit"); + PhSetDialogItemText(hwndDlg, IDC_PROCESSTYPETEXT, L"64-bit"); } else { - SetDlgItemText(hwndDlg, IDC_PROCESSTYPETEXT, L"N/A"); + PhSetDialogItemText(hwndDlg, IDC_PROCESSTYPETEXT, L"N/A"); } ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESSTYPELABEL), SW_SHOW); ShowWindow(GetDlgItem(hwndDlg, IDC_PROCESSTYPETEXT), SW_SHOW); #endif - } - break; - case WM_DESTROY: - { - PhpPropPageDlgProcDestroy(hwndDlg); + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) + PPH_LAYOUT_ITEM dialogItem; + + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILE), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_NAME), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMPANYNAME), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMPANYNAME_LINK), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VERSION), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PROCESSTYPELABEL), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PROCESSTYPETEXT), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILENAME), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_INSPECT), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_OPENFILENAME), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_CMDLINE), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIEWCOMMANDLINE), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_CURDIR), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_STARTED), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PEBADDRESS), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PARENTPROCESS), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIEWPARENTPROCESS), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_MITIGATION), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIEWMITIGATION), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_TERMINATE), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PERMISSIONS), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PROCESS), - dialogItem, PH_ANCHOR_ALL); - - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILE), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_NAME), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMPANYNAME), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMPANYNAME_LINK), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VERSION), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PROCESSTYPELABEL), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PROCESSTYPETEXT), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILENAME), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_INSPECT), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_OPENFILENAME), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_CMDLINE), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIEWCOMMANDLINE), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_CURDIR), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_STARTED), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PEBADDRESS), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PARENTPROCESS), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIEWPARENTPROCESS), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_MITIGATION), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIEWMITIGATION), dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_TERMINATE), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PERMISSIONS), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_TOP); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PROCESS), dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); } } break; case WM_COMMAND: { - INT id = LOWORD(wParam); - - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_INSPECT: { @@ -499,13 +475,51 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( case IDC_OPENFILENAME: { if (processItem->FileName) - PhShellExploreFile(hwndDlg, processItem->FileName->Buffer); + { + PhShellExecuteUserString( + hwndDlg, + L"FileBrowseExecutable", + processItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } } break; case IDC_VIEWCOMMANDLINE: { if (processItem->CommandLine) - PhShowInformationDialog(hwndDlg, processItem->CommandLine->Buffer, 0); + { + PPH_STRING commandLineString; + INT stringArgCount; + PWSTR* stringArgList; + + if (stringArgList = CommandLineToArgvW(processItem->CommandLine->Buffer, &stringArgCount)) + { + PH_STRING_BUILDER sb; + + PhInitializeStringBuilder(&sb, 260); + + for (INT i = 0; i < stringArgCount; i++) + { + PhAppendFormatStringBuilder(&sb, L"[%d] %s\r\n\r\n", i, stringArgList[i]); + } + + PhAppendFormatStringBuilder(&sb, L"[FULL] %s\r\n", processItem->CommandLine->Buffer); + + commandLineString = PhFinalStringBuilderString(&sb); + + LocalFree(stringArgList); + } + else + { + commandLineString = PhReferenceObject(processItem->CommandLine); + } + + PhShowInformationDialog(hwndDlg, commandLineString->Buffer, 0); + + PhDereferenceObject(commandLineString); + } } break; case IDC_VIEWPARENTPROCESS: @@ -525,27 +539,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( break; case IDC_VIEWMITIGATION: { - NTSTATUS status; - HANDLE processHandle; - PH_PROCESS_MITIGATION_POLICY_ALL_INFORMATION information; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_QUERY_INFORMATION, - processItem->ProcessId - ))) - { - if (NT_SUCCESS(PhGetProcessMitigationPolicy(processHandle, &information))) - { - PhShowProcessMitigationPolicyDialog(hwndDlg, &information); - } - - NtClose(processHandle); - } - else - { - PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); - } + PhShowProcessMitigationPolicyDialog(hwndDlg, processItem->ProcessId); } break; case IDC_TERMINATE: @@ -559,27 +553,14 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( break; case IDC_PERMISSIONS: { - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - - stdObjectSecurity.OpenObject = PhpProcessGeneralOpenProcess; - stdObjectSecurity.ObjectType = L"Process"; - stdObjectSecurity.Context = processItem->ProcessId; - - if (PhGetAccessEntries(L"Process", &accessEntries, &numberOfAccessEntries)) - { - PhEditSecurity( - hwndDlg, - processItem->ProcessName->Buffer, - PhStdGetObjectSecurity, - PhStdSetObjectSecurity, - &stdObjectSecurity, - accessEntries, - numberOfAccessEntries - ); - PhFree(accessEntries); - } + PhEditSecurity( + PhCsForceNoParent ? NULL : hwndDlg, + PhGetStringOrEmpty(processItem->ProcessName), + L"Process", + PhpProcessGeneralOpenProcess, + NULL, + processItem->ProcessId + ); } break; } @@ -607,7 +588,7 @@ INT_PTR CALLBACK PhpProcessGeneralDlgProc( info.hWnd = hwndDlg; PhVerifyFileWithAdditionalCatalog( &info, - PhGetString(processItem->PackageFullName), + processItem->PackageFullName, NULL ); } diff --git a/ProcessHacker/prpghndl.c b/ProcessHacker/prpghndl.c index fe7dc80849fb..8a7034aaa5f3 100644 --- a/ProcessHacker/prpghndl.c +++ b/ProcessHacker/prpghndl.c @@ -3,6 +3,7 @@ * Process properties: Handles page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -24,10 +25,9 @@ #include #include -#include - #include #include +#include #include #include @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -208,6 +209,133 @@ VOID PhShowHandleContextMenu( PhFree(handles); } +static BOOLEAN PhpWordMatchHandleStringRef( + _In_ PPH_STRING SearchText, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = SearchText->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +static BOOLEAN PhpWordMatchHandleStringZ( + _In_ PPH_STRING SearchText, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + + return PhpWordMatchHandleStringRef(SearchText, &text); +} + +BOOLEAN PhpHandleTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPH_HANDLES_CONTEXT handlesContext = Context; + PPH_HANDLE_NODE handleNode = (PPH_HANDLE_NODE)Node; + PPH_HANDLE_ITEM handleItem = handleNode->HandleItem; + + if (handlesContext->ListContext.HideUnnamedHandles && PhIsNullOrEmptyString(handleItem->BestObjectName)) + return FALSE; + + if (handlesContext->ListContext.HideEtwHandles) + { + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static ULONG eventTraceTypeIndex = ULONG_MAX; + + // HACK: lazy init the etw object type index (dmex) + if (PhBeginInitOnce(&initOnce)) + { + UNICODE_STRING etwTypeName = RTL_CONSTANT_STRING(L"EtwRegistration"); + + eventTraceTypeIndex = PhGetObjectTypeNumber(&etwTypeName); + + PhEndInitOnce(&initOnce); + } + + if (handleItem->TypeIndex == eventTraceTypeIndex) + return FALSE; + } + + if (PhIsNullOrEmptyString(handlesContext->SearchboxText)) + return TRUE; + + // handle properties + + if (!PhIsNullOrEmptyString(handleItem->TypeName)) + { + if (PhpWordMatchHandleStringRef(handlesContext->SearchboxText, &handleItem->TypeName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(handleItem->ObjectName)) + { + if (PhpWordMatchHandleStringRef(handlesContext->SearchboxText, &handleItem->ObjectName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(handleItem->BestObjectName)) + { + if (PhpWordMatchHandleStringRef(handlesContext->SearchboxText, &handleItem->BestObjectName->sr)) + return TRUE; + } + + if (handleItem->HandleString[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleItem->HandleString)) + return TRUE; + } + + if (handleItem->GrantedAccessString[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleItem->GrantedAccessString)) + return TRUE; + } + + // TODO: Add search for handleItem->Attributes + + // node properties + + if (handleNode->ObjectString[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleNode->ObjectString)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(handleNode->GrantedAccessSymbolicText)) + { + if (PhpWordMatchHandleStringRef(handlesContext->SearchboxText, &handleNode->GrantedAccessSymbolicText->sr)) + return TRUE; + } + + if (handleNode->FileShareAccessText[0]) + { + if (PhpWordMatchHandleStringZ(handlesContext->SearchboxText, handleNode->FileShareAccessText)) + return TRUE; + } + + return FALSE; +} + INT_PTR CALLBACK PhpProcessHandlesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -215,16 +343,14 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( _In_ LPARAM lParam ) { - LPPROPSHEETPAGE propSheetPage; PPH_PROCESS_PROPPAGECONTEXT propPageContext; PPH_PROCESS_ITEM processItem; PPH_HANDLES_CONTEXT handlesContext; HWND tnHandle; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &propPageContext, &processItem)) { - handlesContext = (PPH_HANDLES_CONTEXT)propPageContext->Context; + handlesContext = propPageContext->Context; if (handlesContext) tnHandle = handlesContext->ListContext.TreeNewHandle; @@ -238,8 +364,8 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( { case WM_INITDIALOG: { - handlesContext = propPageContext->Context = - PhAllocate(PhEmGetObjectSize(EmHandlesContextType, sizeof(PH_HANDLES_CONTEXT))); + handlesContext = propPageContext->Context = PhAllocate(PhEmGetObjectSize(EmHandlesContextType, sizeof(PH_HANDLES_CONTEXT))); + memset(handlesContext, 0, sizeof(PH_HANDLES_CONTEXT)); handlesContext->Provider = PhCreateHandleProvider( processItem->ProcessId @@ -274,16 +400,21 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( handlesContext, &handlesContext->UpdatedEventRegistration ); + handlesContext->WindowHandle = hwndDlg; + handlesContext->SearchboxHandle = GetDlgItem(hwndDlg, IDC_HANDLESEARCH); + + PhCreateSearchControl(hwndDlg, handlesContext->SearchboxHandle, L"Search Handles (Ctrl+K)"); // Initialize the list. tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - BringWindowToTop(tnHandle); PhInitializeHandleList(hwndDlg, tnHandle, &handlesContext->ListContext); TreeNew_SetEmptyText(tnHandle, &PhpLoadingText, 0); PhInitializeProviderEventQueue(&handlesContext->EventQueue, 100); handlesContext->LastRunStatus = -1; handlesContext->ErrorMessage = NULL; + handlesContext->SearchboxText = PhReferenceEmptyString(); + handlesContext->FilterEntry = PhAddTreeNewFilter(&handlesContext->ListContext.TreeFilterSupport, PhpHandleTreeFilterCallback, handlesContext); PhEmCallObjectOperation(EmHandlesContextType, handlesContext, EmObjectCreate); @@ -299,16 +430,17 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( PhLoadSettingsHandleList(&handlesContext->ListContext); - PhSetOptionsHandleList(&handlesContext->ListContext, !!PhGetIntegerSetting(L"HideUnnamedHandles")); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_HIDEUNNAMEDHANDLES), - handlesContext->ListContext.HideUnnamedHandles ? BST_CHECKED : BST_UNCHECKED); - PhSetEnabledProvider(&handlesContext->ProviderRegistration, TRUE); PhBoostProvider(&handlesContext->ProviderRegistration, NULL); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: { + PhRemoveTreeNewFilter(&handlesContext->ListContext.TreeFilterSupport, handlesContext->FilterEntry); + if (handlesContext->SearchboxText) PhDereferenceObject(handlesContext->SearchboxText); + PhEmCallObjectOperation(EmHandlesContextType, handlesContext, EmObjectDelete); PhUnregisterCallback( @@ -344,32 +476,51 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( PhDeleteHandleList(&handlesContext->ListContext); PhFree(handlesContext); - - PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - - PhDoPropPageLayout(hwndDlg); + PPH_LAYOUT_ITEM dialogItem; - propPageContext->LayoutInitialized = TRUE; + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, handlesContext->SearchboxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); } } break; case WM_COMMAND: { - INT id = LOWORD(wParam); + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != handlesContext->SearchboxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(handlesContext->SearchboxHandle)); - switch (id) + if (!PhEqualString(handlesContext->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhSwapReference(&handlesContext->SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(handlesContext->SearchboxText)) + { + // Expand any hidden nodes to make search results visible. + PhExpandAllHandleNodes(&handlesContext->ListContext, TRUE); + } + + PhApplyTreeNewFilters(&handlesContext->ListContext.TreeFilterSupport); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_SHOWCONTEXTMENU: { @@ -409,9 +560,9 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( // Toggle the appropriate bit. - if (id == ID_HANDLE_PROTECTED) + if (GET_WM_COMMAND_ID(wParam, lParam) == ID_HANDLE_PROTECTED) attributes ^= OBJ_PROTECT_CLOSE; - else if (id == ID_HANDLE_INHERIT) + else if (GET_WM_COMMAND_ID(wParam, lParam) == ID_HANDLE_INHERIT) attributes ^= OBJ_INHERIT; PhReferenceObject(handleItem); @@ -434,7 +585,7 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( info.TypeName = handleItem->TypeName; info.BestObjectName = handleItem->BestObjectName; - if (id == ID_HANDLE_OBJECTPROPERTIES1) + if (GET_WM_COMMAND_ID(wParam, lParam) == ID_HANDLE_OBJECTPROPERTIES1) PhShowHandleObjectProperties1(hwndDlg, &info); else PhShowHandleObjectProperties2(hwndDlg, &info); @@ -462,14 +613,43 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( PhDereferenceObject(text); } break; - case IDC_HIDEUNNAMEDHANDLES: + case IDC_OPTIONS: { - BOOLEAN hide; + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM unnamedMenuItem; + PPH_EMENU_ITEM etwMenuItem; + PPH_EMENU_ITEM selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_OPTIONS), &rect); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, unnamedMenuItem = PhCreateEMenuItem(0, PH_HANDLE_TREE_MENUITEM_HIDEUNNAMEDHANDLES, L"Hide unnamed handles", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, etwMenuItem = PhCreateEMenuItem(0, PH_HANDLE_TREE_MENUITEM_HIDEETWHANDLES, L"Hide etw handles", NULL, NULL), ULONG_MAX); + + if (handlesContext->ListContext.HideUnnamedHandles) + unnamedMenuItem->Flags |= PH_EMENU_CHECKED; + + if (handlesContext->ListContext.HideEtwHandles) + etwMenuItem->Flags |= PH_EMENU_CHECKED; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id) + { + PhSetOptionsHandleList(&handlesContext->ListContext, selectedItem->Id); + PhSaveSettingsHandleList(&handlesContext->ListContext); + PhApplyTreeNewFilters(&handlesContext->ListContext.TreeFilterSupport); + } - hide = Button_GetCheck(GetDlgItem(hwndDlg, IDC_HIDEUNNAMEDHANDLES)) == BST_CHECKED; - - PhSetIntegerSetting(L"HideUnnamedHandles", hide); - PhSetOptionsHandleList(&handlesContext->ListContext, hide); + PhDestroyEMenu(menu); } break; } @@ -487,6 +667,9 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( case PSN_KILLACTIVE: PhSetEnabledProvider(&handlesContext->ProviderRegistration, FALSE); break; + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_LIST)); + return TRUE; } } break; @@ -554,6 +737,9 @@ INT_PTR CALLBACK PhpProcessHandlesDlgProc( InvalidateRect(tnHandle, NULL, FALSE); } + // Refresh the visible nodes. + PhApplyTreeNewFilters(&handlesContext->ListContext.TreeFilterSupport); + if (count != 0) TreeNew_SetRedraw(tnHandle, TRUE); } diff --git a/ProcessHacker/prpgjob.c b/ProcessHacker/prpgjob.c index a6858385e81e..6cdf17d9f82d 100644 --- a/ProcessHacker/prpgjob.c +++ b/ProcessHacker/prpgjob.c @@ -38,7 +38,7 @@ NTSTATUS NTAPI PhpOpenProcessJobForPage( if (!NT_SUCCESS(status = PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, (HANDLE)Context ))) return status; @@ -69,12 +69,12 @@ INT_PTR CALLBACK PhpProcessJobHookProc( { case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } break; case WM_SHOWWINDOW: { - if (!GetProp(hwndDlg, PhMakeContextAtom())) // LayoutInitialized + if (!PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT)) // LayoutInitialized { PPH_LAYOUT_ITEM dialogItem; @@ -97,7 +97,7 @@ INT_PTR CALLBACK PhpProcessJobHookProc( PhDoPropPageLayout(hwndDlg); - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)TRUE); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr(TRUE)); } } break; diff --git a/ProcessHacker/prpgmem.c b/ProcessHacker/prpgmem.c index 1449dcebe0ed..29196be6efed 100644 --- a/ProcessHacker/prpgmem.c +++ b/ProcessHacker/prpgmem.c @@ -3,6 +3,7 @@ * Process properties: Memory page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -24,8 +25,6 @@ #include #include -#include - #include #include @@ -35,23 +34,18 @@ #include #include #include +#include #include #include +static PPH_OBJECT_TYPE PhMemoryContextType = NULL; static PH_STRINGREF EmptyMemoryText = PH_STRINGREF_INIT(L"There are no memory regions to display."); -VOID PhpRefreshProcessMemoryList( - _In_ HWND hwndDlg, - _In_ PPH_PROCESS_PROPPAGECONTEXT PropPageContext +NTSTATUS PhpRefreshProcessMemoryThread( + _In_ PVOID Context ) { - PPH_MEMORY_CONTEXT memoryContext = PropPageContext->Context; - - if (memoryContext->MemoryItemListValid) - { - PhDeleteMemoryItemList(&memoryContext->MemoryItemList); - memoryContext->MemoryItemListValid = FALSE; - } + PPH_MEMORY_CONTEXT memoryContext = Context; memoryContext->LastRunStatus = PhQueryMemoryItemList( memoryContext->ProcessId, @@ -72,20 +66,29 @@ VOID PhpRefreshProcessMemoryList( } memoryContext->MemoryItemListValid = TRUE; - TreeNew_SetEmptyText(memoryContext->ListContext.TreeNewHandle, &EmptyMemoryText, 0); - PhReplaceMemoryList(&memoryContext->ListContext, &memoryContext->MemoryItemList); } - else - { - PPH_STRING message; - message = PhGetStatusMessage(memoryContext->LastRunStatus, 0); - PhMoveReference(&memoryContext->ErrorMessage, PhFormatString(L"Unable to query memory information:\n%s", PhGetStringOrDefault(message, L"Unknown error."))); - PhClearReference(&message); - TreeNew_SetEmptyText(memoryContext->ListContext.TreeNewHandle, &memoryContext->ErrorMessage->sr, 0); + PostMessage(memoryContext->WindowHandle, WM_PH_INVOKE, 0, 0); - PhReplaceMemoryList(&memoryContext->ListContext, NULL); + PhDereferenceObject(memoryContext); + + return STATUS_SUCCESS; +} + +VOID PhpRefreshProcessMemoryList( + _In_ PPH_PROCESS_PROPPAGECONTEXT PropPageContext + ) +{ + PPH_MEMORY_CONTEXT memoryContext = PropPageContext->Context; + + if (memoryContext->MemoryItemListValid) + { + PhDeleteMemoryItemList(&memoryContext->MemoryItemList); + memoryContext->MemoryItemListValid = FALSE; } + + PhReferenceObject(memoryContext); + PhCreateThread2(PhpRefreshProcessMemoryThread, memoryContext); } VOID PhpInitializeMemoryMenu( @@ -129,8 +132,6 @@ VOID PhpInitializeMemoryMenu( if (numberOfAllocationBase == 0 || numberOfAllocationBase == NumberOfMemoryNodes) PhEnableEMenuItem(Menu, ID_MEMORY_SAVE, TRUE); } - - PhEnableEMenuItem(Menu, ID_MEMORY_READWRITEADDRESS, TRUE); } VOID PhShowMemoryContextMenu( @@ -196,6 +197,165 @@ VOID PhShowMemoryContextMenu( PhFree(memoryNodes); } +static BOOLEAN PhpWordMatchHandleStringRef( + _In_ PPH_STRING SearchText, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = SearchText->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +static BOOLEAN PhpWordMatchHandleStringZ( + _In_ PPH_STRING SearchText, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + + return PhpWordMatchHandleStringRef(SearchText, &text); +} + +BOOLEAN PhpMemoryTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPH_MEMORY_CONTEXT memoryContext = Context; + PPH_MEMORY_NODE memoryNode = (PPH_MEMORY_NODE)Node; + PPH_MEMORY_ITEM memoryItem = memoryNode->MemoryItem; + PPH_STRING useText; + PWSTR tempString; + + if (memoryContext->ListContext.HideFreeRegions && memoryItem->State & MEM_FREE) + return FALSE; + if (memoryContext->ListContext.HideGuardRegions && memoryItem->Protect & PAGE_GUARD) + return FALSE; + + if (memoryContext->ListContext.HideReservedRegions && + (memoryItem->Type & MEM_PRIVATE || memoryItem->Type & MEM_MAPPED || memoryItem->Type & MEM_IMAGE) && + memoryItem->State & MEM_RESERVE && + memoryItem->AllocationBaseItem // Ignore root nodes + ) + { + return FALSE; + } + + if (PhIsNullOrEmptyString(memoryContext->SearchboxText)) + return TRUE; + + if (memoryContext->UseSearchPointer) + { + // Show all the nodes for which the specified pointer is the allocation base + if ((ULONG64)memoryNode->MemoryItem->AllocationBase == memoryContext->SearchPointer) + { + return TRUE; + } + + // Show the AllocationBaseNode for which the specified pointer is within its range + if ( + memoryNode->IsAllocationBase && + (ULONG64)memoryNode->MemoryItem->AllocationBase <= memoryContext->SearchPointer && + (ULONG64)memoryNode->MemoryItem->AllocationBase + (ULONG64)memoryNode->MemoryItem->RegionSize > memoryContext->SearchPointer + ) + { + return TRUE; + } + + // Show the RegionNode for which the specified pointer is within its range + if ( + (ULONG64)memoryNode->MemoryItem->BaseAddress <= memoryContext->SearchPointer && + (ULONG64)memoryNode->MemoryItem->BaseAddress + (ULONG64)memoryNode->MemoryItem->RegionSize > memoryContext->SearchPointer + ) + { + return TRUE; + } + } + + if (memoryNode->BaseAddressText[0]) + { + if (PhpWordMatchHandleStringZ(memoryContext->SearchboxText, memoryNode->BaseAddressText)) + return TRUE; + } + + useText = PH_AUTO(PhGetMemoryRegionUseText(memoryItem)); + if (!PhIsNullOrEmptyString(useText)) + { + if (PhpWordMatchHandleStringRef(memoryContext->SearchboxText, &useText->sr)) + return TRUE; + } + + tempString = PhGetMemoryTypeString(memoryItem->Type); + if (tempString[0]) + { + if (PhpWordMatchHandleStringZ(memoryContext->SearchboxText, tempString)) + return TRUE; + } + + tempString = PhGetMemoryStateString(memoryItem->State); + if (tempString[0]) + { + if (PhpWordMatchHandleStringZ(memoryContext->SearchboxText, tempString)) + return TRUE; + } + + return FALSE; +} + +VOID PhpMemoryContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_MEMORY_CONTEXT memoryContext = (PPH_MEMORY_CONTEXT)Object; + + PhDeleteMemoryList(&memoryContext->ListContext); + + if (memoryContext->MemoryItemListValid) + PhDeleteMemoryItemList(&memoryContext->MemoryItemList); + + PhClearReference(&memoryContext->ErrorMessage); +} + +PPH_MEMORY_CONTEXT PhCreateMemoryContext( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_MEMORY_CONTEXT memoryContext; + + if (PhBeginInitOnce(&initOnce)) + { + PhMemoryContextType = PhCreateObjectType(L"ProcessMemoryContext", 0, PhpMemoryContextDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + memoryContext = PhCreateObject( + PhEmGetObjectSize(EmMemoryContextType, sizeof(PH_MEMORY_CONTEXT)), + PhMemoryContextType + ); + memset(memoryContext, 0, sizeof(PH_MEMORY_CONTEXT)); + + return memoryContext; +} + INT_PTR CALLBACK PhpProcessMemoryDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -209,8 +369,7 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( PPH_MEMORY_CONTEXT memoryContext; HWND tnHandle; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { memoryContext = (PPH_MEMORY_CONTEXT)propPageContext->Context; @@ -226,18 +385,22 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( { case WM_INITDIALOG: { - memoryContext = propPageContext->Context = - PhAllocate(PhEmGetObjectSize(EmMemoryContextType, sizeof(PH_MEMORY_CONTEXT))); - memset(memoryContext, 0, sizeof(PH_MEMORY_CONTEXT)); + memoryContext = propPageContext->Context = PhCreateMemoryContext(); + memoryContext->WindowHandle = hwndDlg; memoryContext->ProcessId = processItem->ProcessId; + memoryContext->SearchboxHandle = GetDlgItem(hwndDlg, IDC_SEARCH); + + PhCreateSearchControl(hwndDlg, memoryContext->SearchboxHandle, L"Search Memory (Ctrl+K)"); // Initialize the list. tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - BringWindowToTop(tnHandle); PhInitializeMemoryList(hwndDlg, tnHandle, &memoryContext->ListContext); TreeNew_SetEmptyText(tnHandle, &PhpLoadingText, 0); memoryContext->LastRunStatus = -1; memoryContext->ErrorMessage = NULL; + memoryContext->SearchboxText = PhReferenceEmptyString(); + memoryContext->AllocationFilterEntry = PhAddTreeNewFilter(&memoryContext->ListContext.AllocationTreeFilterSupport, PhpMemoryTreeFilterCallback, memoryContext); + memoryContext->FilterEntry = PhAddTreeNewFilter(&memoryContext->ListContext.TreeFilterSupport, PhpMemoryTreeFilterCallback, memoryContext); PhEmCallObjectOperation(EmMemoryContextType, memoryContext, EmObjectCreate); @@ -252,15 +415,18 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( } PhLoadSettingsMemoryList(&memoryContext->ListContext); - PhSetOptionsMemoryList(&memoryContext->ListContext, !!PhGetIntegerSetting(L"HideFreeRegions")); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_HIDEFREEREGIONS), - memoryContext->ListContext.HideFreeRegions ? BST_CHECKED : BST_UNCHECKED); - PhpRefreshProcessMemoryList(hwndDlg, propPageContext); + PhpRefreshProcessMemoryList(propPageContext); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: { + PhRemoveTreeNewFilter(&memoryContext->ListContext.TreeFilterSupport, memoryContext->FilterEntry); + PhRemoveTreeNewFilter(&memoryContext->ListContext.TreeFilterSupport, memoryContext->AllocationFilterEntry); + if (memoryContext->SearchboxText) PhDereferenceObject(memoryContext->SearchboxText); + PhEmCallObjectOperation(EmMemoryContextType, memoryContext, EmObjectDelete); if (PhPluginsEnabled) @@ -273,41 +439,54 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( } PhSaveSettingsMemoryList(&memoryContext->ListContext); - PhDeleteMemoryList(&memoryContext->ListContext); - if (memoryContext->MemoryItemListValid) - PhDeleteMemoryItemList(&memoryContext->MemoryItemList); - - PhClearReference(&memoryContext->ErrorMessage); - PhFree(memoryContext); - - PhpPropPageDlgProcDestroy(hwndDlg); + PhDereferenceObject(memoryContext); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; + PPH_LAYOUT_ITEM dialogItem; - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_STRINGS), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_REFRESH), - dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, memoryContext->ListContext.TreeNewHandle, - dialogItem, PH_ANCHOR_ALL); - - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, memoryContext->SearchboxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, memoryContext->ListContext.TreeNewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); } } break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != memoryContext->SearchboxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(memoryContext->SearchboxHandle)); + + if (!PhEqualString(memoryContext->SearchboxText, newSearchboxText, FALSE)) + { + // Try to get a search pointer from the search string. + memoryContext->UseSearchPointer = PhStringToInteger64(&newSearchboxText->sr, 0, &memoryContext->SearchPointer); + + // Cache the current search text for our callback. + PhSwapReference(&memoryContext->SearchboxText, newSearchboxText); + + // Expand any hidden nodes to make search results visible. + PhExpandAllMemoryNodes(&memoryContext->ListContext, TRUE); + + PhApplyTreeNewFilters(&memoryContext->ListContext.AllocationTreeFilterSupport); + PhApplyTreeNewFilters(&memoryContext->ListContext.TreeFilterSupport); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_SHOWCONTEXTMENU: { @@ -320,22 +499,19 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( if (memoryNode && !memoryNode->IsAllocationBase) { - if (memoryNode->MemoryItem->State & MEM_COMMIT) - { - PPH_SHOW_MEMORY_EDITOR showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); - - memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); - showMemoryEditor->ProcessId = processItem->ProcessId; - showMemoryEditor->BaseAddress = memoryNode->MemoryItem->BaseAddress; - showMemoryEditor->RegionSize = memoryNode->MemoryItem->RegionSize; - showMemoryEditor->SelectOffset = -1; - showMemoryEditor->SelectLength = 0; - ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); - } - else - { - PhShowError(hwndDlg, L"Unable to edit the memory region because it is not committed."); - } + PPH_SHOW_MEMORY_EDITOR showMemoryEditor; + + showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); + memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); + + showMemoryEditor->OwnerWindow = hwndDlg; + showMemoryEditor->ProcessId = processItem->ProcessId; + showMemoryEditor->BaseAddress = memoryNode->MemoryItem->BaseAddress; + showMemoryEditor->RegionSize = memoryNode->MemoryItem->RegionSize; + showMemoryEditor->SelectOffset = -1; + showMemoryEditor->SelectLength = 0; + + ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); } } break; @@ -474,84 +650,197 @@ INT_PTR CALLBACK PhpProcessMemoryDlgProc( } } break; - case ID_MEMORY_READWRITEADDRESS: + case ID_MEMORY_COPY: { - PPH_STRING selectedChoice = NULL; - - if (!memoryContext->MemoryItemListValid) - break; + PPH_STRING text; - while (PhaChoiceDialog( - hwndDlg, - L"Read/Write Address", - L"Enter an address:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - L"MemoryReadWriteAddressChoices" - )) + text = PhGetTreeNewText(tnHandle, 0); + PhSetClipboardString(tnHandle, &text->sr); + PhDereferenceObject(text); + } + break; + case IDC_REFRESH: + PhpRefreshProcessMemoryList(propPageContext); + break; + case IDC_FILTEROPTIONS: + { + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM freeItem; + PPH_EMENU_ITEM reservedItem; + PPH_EMENU_ITEM guardItem; + PPH_EMENU_ITEM privateItem; + PPH_EMENU_ITEM systemItem; + PPH_EMENU_ITEM cfgItem; + PPH_EMENU_ITEM typeItem; + PPH_EMENU_ITEM selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_FILTEROPTIONS), &rect); + + typedef enum _PH_MEMORY_FILTER_MENU_ITEM { - ULONG64 address64; - PVOID address; - - if (selectedChoice->Length == 0) - continue; + PH_MEMORY_FILTER_MENU_HIDE_FREE = 1, + PH_MEMORY_FILTER_MENU_HIDE_RESERVED, + PH_MEMORY_FILTER_MENU_HIGHLIGHT_PRIVATE, + PH_MEMORY_FILTER_MENU_HIGHLIGHT_SYSTEM, + PH_MEMORY_FILTER_MENU_HIGHLIGHT_CFG, + PH_MEMORY_FILTER_MENU_HIGHLIGHT_EXECUTE, + PH_MEMORY_FILTER_MENU_HIDE_GUARD, + // Non-standard PH_MEMORY_FLAG options. + PH_MEMORY_FILTER_MENU_READ_ADDRESS, + PH_MEMORY_FILTER_MENU_STRINGS, + } PH_MEMORY_FILTER_MENU_ITEM; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, freeItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_FREE, L"Hide free pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, reservedItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_RESERVED, L"Hide reserved pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, guardItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIDE_GUARD, L"Hide guard pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, privateItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_PRIVATE, L"Highlight private pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, systemItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_SYSTEM, L"Highlight system pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, cfgItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_CFG, L"Highlight CFG pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, typeItem = PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_HIGHLIGHT_EXECUTE, L"Highlight executable pages", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_READ_ADDRESS, L"Read/Write &address...", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MEMORY_FILTER_MENU_STRINGS, L"Strings...", NULL, NULL), ULONG_MAX); + + if (memoryContext->ListContext.HideFreeRegions) + freeItem->Flags |= PH_EMENU_CHECKED; + if (memoryContext->ListContext.HideReservedRegions) + reservedItem->Flags |= PH_EMENU_CHECKED; + if (memoryContext->ListContext.HideGuardRegions) + guardItem->Flags |= PH_EMENU_CHECKED; + if (memoryContext->ListContext.HighlightPrivatePages) + privateItem->Flags |= PH_EMENU_CHECKED; + if (memoryContext->ListContext.HighlightSystemPages) + systemItem->Flags |= PH_EMENU_CHECKED; + if (memoryContext->ListContext.HighlightCfgPages) + cfgItem->Flags |= PH_EMENU_CHECKED; + if (memoryContext->ListContext.HighlightExecutePages) + typeItem->Flags |= PH_EMENU_CHECKED; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); - if (PhStringToInteger64(&selectedChoice->sr, 0, &address64)) + if (selectedItem && selectedItem->Id) + { + if (selectedItem->Id == PH_MEMORY_FILTER_MENU_HIDE_FREE || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIDE_RESERVED || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIDE_GUARD || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIGHLIGHT_PRIVATE || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIGHLIGHT_SYSTEM || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIGHLIGHT_CFG || + selectedItem->Id == PH_MEMORY_FILTER_MENU_HIGHLIGHT_EXECUTE) { - PPH_MEMORY_ITEM memoryItem; + PhSetOptionsMemoryList(&memoryContext->ListContext, selectedItem->Id); + PhSaveSettingsMemoryList(&memoryContext->ListContext); - address = (PVOID)address64; - memoryItem = PhLookupMemoryItemList(&memoryContext->MemoryItemList, address); + PhApplyTreeNewFilters(&memoryContext->ListContext.AllocationTreeFilterSupport); + PhApplyTreeNewFilters(&memoryContext->ListContext.TreeFilterSupport); + } + else if (selectedItem->Id == PH_MEMORY_FILTER_MENU_STRINGS) + { + PhShowMemoryStringDialog(hwndDlg, processItem); + } + else if (selectedItem->Id == PH_MEMORY_FILTER_MENU_READ_ADDRESS) + { + PPH_STRING selectedChoice = NULL; - if (memoryItem) - { - PPH_SHOW_MEMORY_EDITOR showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); - - memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); - showMemoryEditor->ProcessId = processItem->ProcessId; - showMemoryEditor->BaseAddress = memoryItem->BaseAddress; - showMemoryEditor->RegionSize = memoryItem->RegionSize; - showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)address - (ULONG_PTR)memoryItem->BaseAddress); - showMemoryEditor->SelectLength = 0; - ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); + if (!memoryContext->MemoryItemListValid) break; - } - else + + while (PhaChoiceDialog( + hwndDlg, + L"Read/Write Address", + L"Enter an address:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + L"MemoryReadWriteAddressChoices" + )) { - PhShowError(hwndDlg, L"Unable to find the memory region for the selected address."); + ULONG64 address64; + PVOID address; + + if (selectedChoice->Length == 0) + continue; + + if (PhStringToInteger64(&selectedChoice->sr, 0, &address64)) + { + PPH_MEMORY_ITEM memoryItem; + + address = (PVOID)address64; + memoryItem = PhLookupMemoryItemList(&memoryContext->MemoryItemList, address); + + if (memoryItem) + { + PPH_SHOW_MEMORY_EDITOR showMemoryEditor = PhAllocate(sizeof(PH_SHOW_MEMORY_EDITOR)); + + memset(showMemoryEditor, 0, sizeof(PH_SHOW_MEMORY_EDITOR)); + showMemoryEditor->ProcessId = processItem->ProcessId; + showMemoryEditor->BaseAddress = memoryItem->BaseAddress; + showMemoryEditor->RegionSize = memoryItem->RegionSize; + showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)address - (ULONG_PTR)memoryItem->BaseAddress); + showMemoryEditor->SelectLength = 0; + ProcessHacker_ShowMemoryEditor(PhMainWndHandle, showMemoryEditor); + break; + } + else + { + PhShowError(hwndDlg, L"Unable to find the memory region for the selected address."); + } + } } } } - } - break; - case ID_MEMORY_COPY: - { - PPH_STRING text; - text = PhGetTreeNewText(tnHandle, 0); - PhSetClipboardString(tnHandle, &text->sr); - PhDereferenceObject(text); + PhDestroyEMenu(menu); } break; - case IDC_HIDEFREEREGIONS: - { - BOOLEAN hide; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; - hide = Button_GetCheck(GetDlgItem(hwndDlg, IDC_HIDEFREEREGIONS)) == BST_CHECKED; - PhSetIntegerSetting(L"HideFreeRegions", hide); - PhSetOptionsMemoryList(&memoryContext->ListContext, hide); - } - break; - case IDC_STRINGS: - PhShowMemoryStringDialog(hwndDlg, processItem); - break; - case IDC_REFRESH: - PhpRefreshProcessMemoryList(hwndDlg, propPageContext); - break; + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_LIST)); + return TRUE; + } + } + break; + case WM_PH_INVOKE: + { + if (memoryContext->MemoryItemListValid) + { + TreeNew_SetEmptyText(memoryContext->ListContext.TreeNewHandle, &EmptyMemoryText, 0); + + PhReplaceMemoryList(&memoryContext->ListContext, &memoryContext->MemoryItemList); + } + else + { + PPH_STRING message; + + message = PhGetStatusMessage(memoryContext->LastRunStatus, 0); + PhMoveReference(&memoryContext->ErrorMessage, PhFormatString(L"Unable to query memory information:\n%s", PhGetStringOrDefault(message, L"Unknown error."))); + TreeNew_SetEmptyText(memoryContext->ListContext.TreeNewHandle, &memoryContext->ErrorMessage->sr, 0); + + PhReplaceMemoryList(&memoryContext->ListContext, NULL); + + PhClearReference(&message); } } break; diff --git a/ProcessHacker/prpgmod.c b/ProcessHacker/prpgmod.c index d7e49874ca36..afee83ac64f4 100644 --- a/ProcessHacker/prpgmod.c +++ b/ProcessHacker/prpgmod.c @@ -3,6 +3,7 @@ * Process properties: Modules page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -24,9 +25,6 @@ #include #include -#include -#include - #include #include @@ -35,8 +33,10 @@ #include #include #include +#include #include #include +#include static PH_STRINGREF EmptyModulesText = PH_STRINGREF_INIT(L"There are no modules to display."); @@ -178,6 +178,232 @@ VOID PhShowModuleContextMenu( PhFree(modules); } +static BOOLEAN PhpWordMatchHandleStringRef( + _In_ PPH_STRING SearchText, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = SearchText->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +static BOOLEAN PhpWordMatchHandleStringZ( + _In_ PPH_STRING SearchText, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + + return PhpWordMatchHandleStringRef(SearchText, &text); +} + +BOOLEAN PhpModulesTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PPH_MODULES_CONTEXT Context + ) +{ + PPH_MODULE_NODE moduleNode = (PPH_MODULE_NODE)Node; + PPH_MODULE_ITEM moduleItem = moduleNode->ModuleItem; + + switch (moduleItem->LoadReason) + { + case LoadReasonStaticDependency: + case LoadReasonStaticForwarderDependency: + { + if (Context->ListContext.HideStaticModules) + return FALSE; + } + break; + case LoadReasonDynamicForwarderDependency: + case LoadReasonDynamicLoad: + { + if (Context->ListContext.HideDynamicModules) + return FALSE; + } + break; + } + + switch (moduleItem->Type) + { + case PH_MODULE_TYPE_MAPPED_FILE: + case PH_MODULE_TYPE_MAPPED_IMAGE: + { + if (Context->ListContext.HideMappedModules) + return FALSE; + } + break; + } + + if (Context->ListContext.HideSignedModules && moduleItem->VerifyResult == VrTrusted) + return FALSE; + + if ( + PhEnableProcessQueryStage2 && + Context->ListContext.HideSystemModules && + moduleItem->VerifyResult == VrTrusted && + PhEqualStringRef2(&moduleItem->VerifySignerName->sr, L"Microsoft Windows", TRUE) + ) + { + return FALSE; + } + + if (PhIsNullOrEmptyString(Context->SearchboxText)) + return TRUE; + + // (dmex) TODO: Add search support for the following fields: + //ULONG Flags; + //ULONG Type; + //USHORT ImageCharacteristics; + //USHORT ImageDllCharacteristics; + + // module properties + + if (!PhIsNullOrEmptyString(moduleItem->Name)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->Name->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(moduleItem->FileName)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->FileName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(moduleItem->VerifySignerName)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->VerifySignerName->sr)) + return TRUE; + } + + if (moduleItem->BaseAddressString[0]) + { + if (PhpWordMatchHandleStringZ(Context->SearchboxText, moduleItem->BaseAddressString)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(moduleItem->VersionInfo.CompanyName)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->VersionInfo.CompanyName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(moduleItem->VersionInfo.FileDescription)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->VersionInfo.FileDescription->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(moduleItem->VersionInfo.FileVersion)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->VersionInfo.FileVersion->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(moduleItem->VersionInfo.ProductName)) + { + if (PhpWordMatchHandleStringRef(Context->SearchboxText, &moduleItem->VersionInfo.ProductName->sr)) + return TRUE; + } + + if (moduleItem->EntryPointAddressString[0]) + { + if (PhpWordMatchHandleStringZ(Context->SearchboxText, moduleItem->EntryPointAddressString)) + return TRUE; + } + + if (moduleItem->ParentBaseAddressString[0]) + { + if (PhpWordMatchHandleStringZ(Context->SearchboxText, moduleItem->ParentBaseAddressString)) + return TRUE; + } + + switch (moduleItem->LoadReason) + { + case LoadReasonStaticDependency: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Static dependency")) + return TRUE; + break; + case LoadReasonStaticForwarderDependency: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Static forwarder dependency")) + return TRUE; + break; + case LoadReasonDynamicForwarderDependency: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Dynamic forwarder dependency")) + return TRUE; + break; + case LoadReasonDelayloadDependency: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Delay load dependency")) + return TRUE; + break; + case LoadReasonDynamicLoad: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Dynamic")) + return TRUE; + break; + case LoadReasonAsImageLoad: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Image")) + return TRUE; + break; + case LoadReasonAsDataLoad: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Data")) + return TRUE; + break; + } + + switch (moduleItem->VerifyResult) + { + case VrNoSignature: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"No Signature")) + return TRUE; + break; + case VrExpired: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Expired")) + return TRUE; + break; + case VrRevoked: + case VrDistrust: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Revoked")) + return TRUE; + break; + } + + switch (moduleItem->VerifyResult) + { + case VrTrusted: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Trusted")) + return TRUE; + break; + case VrNoSignature: + case VrExpired: + case VrRevoked: + case VrDistrust: + case VrUnknown: + case VrBadSignature: + if (PhpWordMatchHandleStringZ(Context->SearchboxText, L"Bad")) + return TRUE; + break; + } + + return FALSE; +} + INT_PTR CALLBACK PhpProcessModulesDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -191,8 +417,7 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PPH_MODULES_CONTEXT modulesContext; HWND tnHandle; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { modulesContext = (PPH_MODULES_CONTEXT)propPageContext->Context; @@ -208,10 +433,8 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( { case WM_INITDIALOG: { - // Lots of boilerplate code... - - modulesContext = propPageContext->Context = - PhAllocate(PhEmGetObjectSize(EmModulesContextType, sizeof(PH_MODULES_CONTEXT))); + modulesContext = propPageContext->Context = PhAllocate(PhEmGetObjectSize(EmModulesContextType, sizeof(PH_MODULES_CONTEXT))); + memset(modulesContext, 0, sizeof(PH_MODULES_CONTEXT)); modulesContext->Provider = PhCreateModuleProvider( processItem->ProcessId @@ -248,14 +471,18 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( ); modulesContext->WindowHandle = hwndDlg; + modulesContext->SearchboxHandle = GetDlgItem(hwndDlg, IDC_SEARCH); + PhCreateSearchControl(hwndDlg, modulesContext->SearchboxHandle, L"Search Modules (Ctrl+K)"); + // Initialize the list. tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - BringWindowToTop(tnHandle); PhInitializeModuleList(hwndDlg, tnHandle, &modulesContext->ListContext); TreeNew_SetEmptyText(tnHandle, &PhpLoadingText, 0); PhInitializeProviderEventQueue(&modulesContext->EventQueue, 100); modulesContext->LastRunStatus = -1; modulesContext->ErrorMessage = NULL; + modulesContext->SearchboxText = PhReferenceEmptyString(); + modulesContext->FilterEntry = PhAddTreeNewFilter(&modulesContext->ListContext.TreeFilterSupport, PhpModulesTreeFilterCallback, modulesContext); PhEmCallObjectOperation(EmModulesContextType, modulesContext, EmObjectCreate); @@ -274,11 +501,14 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhSetEnabledProvider(&modulesContext->ProviderRegistration, TRUE); PhBoostProvider(&modulesContext->ProviderRegistration, NULL); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: { + PhRemoveTreeNewFilter(&modulesContext->ListContext.TreeFilterSupport, modulesContext->FilterEntry); + if (modulesContext->SearchboxText) PhDereferenceObject(modulesContext->SearchboxText); + PhEmCallObjectOperation(EmModulesContextType, modulesContext, EmObjectDelete); PhUnregisterCallback( @@ -315,29 +545,47 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhClearReference(&modulesContext->ErrorMessage); PhFree(modulesContext); - - PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, modulesContext->ListContext.TreeNewHandle, - dialogItem, PH_ANCHOR_ALL); + PPH_LAYOUT_ITEM dialogItem; - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, modulesContext->SearchboxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, modulesContext->ListContext.TreeNewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); } } break; case WM_COMMAND: { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != modulesContext->SearchboxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(modulesContext->SearchboxHandle)); + + if (!PhEqualString(modulesContext->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhSwapReference(&modulesContext->SearchboxText, newSearchboxText); + + // Expand any hidden nodes to make search results visible. + PhExpandAllModuleNodes(&modulesContext->ListContext, TRUE); + + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + } + } + break; + } + switch (LOWORD(wParam)) { case ID_SHOWCONTEXTMENU: @@ -392,7 +640,13 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( if (moduleItem) { - PhShellExploreFile(hwndDlg, moduleItem->FileName->Buffer); + PhShellExecuteUserString( + hwndDlg, + L"FileBrowseExecutable", + moduleItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); } } break; @@ -415,6 +669,105 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( PhDereferenceObject(text); } break; + case IDC_FILTEROPTIONS: + { + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM dynamicItem; + PPH_EMENU_ITEM mappedItem; + PPH_EMENU_ITEM staticItem; + PPH_EMENU_ITEM verifiedItem; + PPH_EMENU_ITEM systemItem; + PPH_EMENU_ITEM untrustedItem; + PPH_EMENU_ITEM systemHighlightItem; + PPH_EMENU_ITEM dotnetItem; + PPH_EMENU_ITEM immersiveItem; + PPH_EMENU_ITEM relocatedItem; + PPH_EMENU_ITEM selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_FILTEROPTIONS), &rect); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, dynamicItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_DYNAMIC_OPTION, L"Hide dynamic", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, mappedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MAPPED_OPTION, L"Hide mapped", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, staticItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_STATIC_OPTION, L"Hide static", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, verifiedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_SIGNED_OPTION, L"Hide verified", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, systemItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_SYSTEM_OPTION, L"Hide system", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, dotnetItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_DOTNET_OPTION, L"Highlight .NET modules", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, immersiveItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_IMMERSIVE_OPTION, L"Highlight immersive modules", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, relocatedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_RELOCATED_OPTION, L"Highlight relocated modules", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, untrustedItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_UNSIGNED_OPTION, L"Highlight untrusted modules", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, systemHighlightItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_HIGHLIGHT_SYSTEM_OPTION, L"Highlight system modules", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PH_MODULE_FLAGS_LOAD_MODULE_OPTION, L"Load module...", NULL, NULL), ULONG_MAX); + //PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + //PhInsertEMenuItem(menu, stringsItem = PhCreateEMenuItem(0, PH_MODULE_FLAGS_MODULE_STRINGS_OPTION, L"Strings...", NULL, NULL), ULONG_MAX); + + if (modulesContext->ListContext.HideDynamicModules) + dynamicItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HideMappedModules) + mappedItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HideStaticModules) + staticItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HideSignedModules) + verifiedItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HideSystemModules) + systemItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HighlightDotNetModules) + dotnetItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HighlightImmersiveModules) + immersiveItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HighlightRelocatedModules) + relocatedItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HighlightUntrustedModules) + untrustedItem->Flags |= PH_EMENU_CHECKED; + if (modulesContext->ListContext.HighlightSystemModules) + systemHighlightItem->Flags |= PH_EMENU_CHECKED; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id) + { + if (selectedItem->Id == PH_MODULE_FLAGS_LOAD_MODULE_OPTION) + { + if (PhGetIntegerSetting(L"EnableWarnings") && !PhShowConfirmMessage( + hwndDlg, + L"load", + L"a module", + L"Some programs may restrict access or ban your account when loading modules into the process.", + FALSE + )) + { + break; + } + + PhReferenceObject(processItem); + PhUiLoadDllProcess(hwndDlg, processItem); + PhDereferenceObject(processItem); + } + else if (selectedItem->Id == PH_MODULE_FLAGS_MODULE_STRINGS_OPTION) + { + // TODO + } + else + { + PhSetOptionsModuleList(&modulesContext->ListContext, selectedItem->Id); + PhSaveSettingsModuleList(&modulesContext->ListContext); + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + } + } + + PhDestroyEMenu(menu); + } + break; } } break; @@ -430,6 +783,9 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( case PSN_KILLACTIVE: PhSetEnabledProvider(&modulesContext->ProviderRegistration, FALSE); break; + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_LIST)); + return TRUE; } } break; @@ -497,6 +853,9 @@ INT_PTR CALLBACK PhpProcessModulesDlgProc( InvalidateRect(tnHandle, NULL, FALSE); } + // Refresh the visible nodes. + PhApplyTreeNewFilters(&modulesContext->ListContext.TreeFilterSupport); + if (count != 0) TreeNew_SetRedraw(tnHandle, TRUE); } diff --git a/ProcessHacker/prpgperf.c b/ProcessHacker/prpgperf.c index cfa46d60d7c3..eb60d02f1c05 100644 --- a/ProcessHacker/prpgperf.c +++ b/ProcessHacker/prpgperf.c @@ -21,14 +21,17 @@ */ #include +#include #include #include #include +#include #include #include #include +#include static VOID NTAPI PerformanceUpdateHandler( _In_opt_ PVOID Parameter, @@ -52,8 +55,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PPH_PROCESS_ITEM processItem; PPH_PERFORMANCE_CONTEXT performanceContext; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { performanceContext = (PPH_PERFORMANCE_CONTEXT)propPageContext->Context; } @@ -66,13 +68,13 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( { case WM_INITDIALOG: { - performanceContext = propPageContext->Context = - PhAllocate(sizeof(PH_PERFORMANCE_CONTEXT)); + performanceContext = propPageContext->Context = PhAllocate(sizeof(PH_PERFORMANCE_CONTEXT)); + memset(performanceContext, 0, sizeof(PH_PERFORMANCE_CONTEXT)); performanceContext->WindowHandle = hwndDlg; PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), PerformanceUpdateHandler, performanceContext, &performanceContext->ProcessesUpdatedRegistration @@ -102,6 +104,8 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhSetWindowStyle(performanceContext->IoGraphHandle, WS_BORDER, WS_BORDER); Graph_SetTooltip(performanceContext->IoGraphHandle, TRUE); BringWindowToTop(performanceContext->IoGraphHandle); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: @@ -111,26 +115,19 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhDeleteGraphState(&performanceContext->IoGraphState); PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &performanceContext->ProcessesUpdatedRegistration ); PhFree(performanceContext); - - PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; + PPH_LAYOUT_ITEM dialogItem; - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhEndPropPageLayout(hwndDlg, propPageContext); } } break; @@ -224,7 +221,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhMoveReference(&performanceContext->PrivateGraphState.Text, PhConcatStrings2( L"Private bytes: ", - PhaFormatSize(processItem->VmCounters.PagefileUsage, -1)->Buffer + PhaFormatSize(processItem->VmCounters.PagefileUsage, ULONG_MAX)->Buffer )); hdc = Graph_GetBufferedContext(performanceContext->PrivateGraphHandle); @@ -296,8 +293,8 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhMoveReference(&performanceContext->IoGraphState.Text, PhFormatString( L"R+O: %s, W: %s", - PhaFormatSize(processItem->IoReadDelta.Delta + processItem->IoOtherDelta.Delta, -1)->Buffer, - PhaFormatSize(processItem->IoWriteDelta.Delta, -1)->Buffer + PhaFormatSize(processItem->IoReadDelta.Delta + processItem->IoOtherDelta.Delta, ULONG_MAX)->Buffer, + PhaFormatSize(processItem->IoWriteDelta.Delta, ULONG_MAX)->Buffer )); hdc = Graph_GetBufferedContext(performanceContext->IoGraphHandle); @@ -350,7 +347,7 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhMoveReference(&performanceContext->PrivateGraphState.TooltipText, PhFormatString( L"Private bytes: %s\n%s", - PhaFormatSize(privateBytes, -1)->Buffer, + PhaFormatSize(privateBytes, ULONG_MAX)->Buffer, PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(processItem, getTooltipText->Index))->Buffer )); } @@ -374,9 +371,9 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( PhMoveReference(&performanceContext->IoGraphState.TooltipText, PhFormatString( L"R: %s\nW: %s\nO: %s\n%s", - PhaFormatSize(ioRead, -1)->Buffer, - PhaFormatSize(ioWrite, -1)->Buffer, - PhaFormatSize(ioOther, -1)->Buffer, + PhaFormatSize(ioRead, ULONG_MAX)->Buffer, + PhaFormatSize(ioWrite, ULONG_MAX)->Buffer, + PhaFormatSize(ioOther, ULONG_MAX)->Buffer, PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(processItem, getTooltipText->Index))->Buffer )); } @@ -402,15 +399,15 @@ INT_PTR CALLBACK PhpProcessPerformanceDlgProc( LONG height; performanceContext->CpuGraphState.Valid = FALSE; - performanceContext->CpuGraphState.TooltipIndex = -1; + performanceContext->CpuGraphState.TooltipIndex = ULONG_MAX; performanceContext->PrivateGraphState.Valid = FALSE; - performanceContext->PrivateGraphState.TooltipIndex = -1; + performanceContext->PrivateGraphState.TooltipIndex = ULONG_MAX; performanceContext->IoGraphState.Valid = FALSE; - performanceContext->IoGraphState.TooltipIndex = -1; + performanceContext->IoGraphState.TooltipIndex = ULONG_MAX; GetClientRect(hwndDlg, &clientRect); width = clientRect.right - margin.left - margin.right; - height = (clientRect.bottom - margin.top - margin.bottom - between * 2) / 3; + height = (clientRect.bottom - margin.top - margin.bottom - between * sizeof(WCHAR)) / 3; deferHandle = BeginDeferWindowPos(6); diff --git a/ProcessHacker/prpgsrv.c b/ProcessHacker/prpgsrv.c index 19b74bc130a9..e62e6c3d548c 100644 --- a/ProcessHacker/prpgsrv.c +++ b/ProcessHacker/prpgsrv.c @@ -21,9 +21,9 @@ */ #include +#include #include #include - #include static VOID PhpLayoutServiceListControl( @@ -53,15 +53,11 @@ INT_PTR CALLBACK PhpProcessServicesDlgProc( _In_ LPARAM lParam ) { - LPPROPSHEETPAGE propSheetPage; PPH_PROCESS_PROPPAGECONTEXT propPageContext; PPH_PROCESS_ITEM processItem; - if (!PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) - { + if (!PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &propPageContext, &processItem)) return FALSE; - } switch (uMsg) { @@ -103,28 +99,20 @@ INT_PTR CALLBACK PhpProcessServicesDlgProc( ShowWindow(serviceListHandle, SW_SHOW); propPageContext->Context = serviceListHandle; - } - break; - case WM_DESTROY: - { - PhpPropPageDlgProcDestroy(hwndDlg); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; + PPH_LAYOUT_ITEM dialogItem; - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), - dialogItem, PH_ANCHOR_ALL); + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); - PhDoPropPageLayout(hwndDlg); PhpLayoutServiceListControl(hwndDlg, (HWND)propPageContext->Context); - - propPageContext->LayoutInitialized = TRUE; } } break; diff --git a/ProcessHacker/prpgstat.c b/ProcessHacker/prpgstat.c index 97b0127b4ea9..7fa296c15c23 100644 --- a/ProcessHacker/prpgstat.c +++ b/ProcessHacker/prpgstat.c @@ -3,6 +3,7 @@ * Process properties: Statistics page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2019 dmex * * This file is part of Process Hacker. * @@ -21,69 +22,288 @@ */ #include +#include +#include #include #include - #include -static VOID NTAPI StatisticsUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context +#include +#include + +typedef enum _PH_PROCESS_STATISTICS_CATEGORY +{ + PH_PROCESS_STATISTICS_CATEGORY_CPU, + PH_PROCESS_STATISTICS_CATEGORY_MEMORY, + PH_PROCESS_STATISTICS_CATEGORY_IO, + PH_PROCESS_STATISTICS_CATEGORY_OTHER, + PH_PROCESS_STATISTICS_CATEGORY_EXTENSION +} PH_PROCESS_STATISTICS_CATEGORY; + +typedef enum _PH_PROCESS_STATISTICS_INDEX +{ + PH_PROCESS_STATISTICS_INDEX_PRIORITY, + PH_PROCESS_STATISTICS_INDEX_CYCLES, + PH_PROCESS_STATISTICS_INDEX_CYCLESDELTA, + PH_PROCESS_STATISTICS_INDEX_KERNELTIME, + PH_PROCESS_STATISTICS_INDEX_KERNELDELTA, + PH_PROCESS_STATISTICS_INDEX_USERTIME, + PH_PROCESS_STATISTICS_INDEX_USERDELTA, + PH_PROCESS_STATISTICS_INDEX_TOTALTIME, + PH_PROCESS_STATISTICS_INDEX_TOTALDELTA, + PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTES, + PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTESDELTA, + + PH_PROCESS_STATISTICS_INDEX_PEAKPRIVATEBYTES, + PH_PROCESS_STATISTICS_INDEX_VIRTUALSIZE, + PH_PROCESS_STATISTICS_INDEX_PEAKVIRTUALSIZE, + PH_PROCESS_STATISTICS_INDEX_PAGEFAULTS, + PH_PROCESS_STATISTICS_INDEX_PAGEFAULTSDELTA, + PH_PROCESS_STATISTICS_INDEX_WORKINGSET, + PH_PROCESS_STATISTICS_INDEX_PEAKWORKINGSET, + PH_PROCESS_STATISTICS_INDEX_PRIVATEWS, + PH_PROCESS_STATISTICS_INDEX_SHAREABLEWS, + PH_PROCESS_STATISTICS_INDEX_SHAREDWS, + PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, + + PH_PROCESS_STATISTICS_INDEX_READS, + PH_PROCESS_STATISTICS_INDEX_READSDELTA, + PH_PROCESS_STATISTICS_INDEX_READBYTES, + PH_PROCESS_STATISTICS_INDEX_READBYTESDELTA, + PH_PROCESS_STATISTICS_INDEX_WRITES, + PH_PROCESS_STATISTICS_INDEX_WRITESDELTA, + PH_PROCESS_STATISTICS_INDEX_WRITEBYTES, + PH_PROCESS_STATISTICS_INDEX_WRITEBYTESDELTA, + PH_PROCESS_STATISTICS_INDEX_OTHER, + PH_PROCESS_STATISTICS_INDEX_OTHERDELTA, + PH_PROCESS_STATISTICS_INDEX_OTHERBYTES, + PH_PROCESS_STATISTICS_INDEX_OTHERBYTESDELTA, + PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, + + PH_PROCESS_STATISTICS_INDEX_HANDLES, + PH_PROCESS_STATISTICS_INDEX_PEAKHANDLES, + PH_PROCESS_STATISTICS_INDEX_GDIHANDLES, + PH_PROCESS_STATISTICS_INDEX_USERHANDLES, + + PH_PROCESS_STATISTICS_INDEX_PAGEDPOOL, + PH_PROCESS_STATISTICS_INDEX_PEAKPAGEDPOOL, + PH_PROCESS_STATISTICS_INDEX_NONPAGED, + PH_PROCESS_STATISTICS_INDEX_PEAKNONPAGED, + + PH_PROCESS_STATISTICS_INDEX_RUNNINGTIME, + PH_PROCESS_STATISTICS_INDEX_SUSPENDEDTIME, + PH_PROCESS_STATISTICS_INDEX_HANGCOUNT, + PH_PROCESS_STATISTICS_INDEX_GHOSTCOUNT, + + PH_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES, + PH_PROCESS_STATISTICS_INDEX_DISKREAD, + PH_PROCESS_STATISTICS_INDEX_DISKWRITE, + PH_PROCESS_STATISTICS_INDEX_NETWORKTXRXBYTES, + PH_PROCESS_STATISTICS_INDEX_MBBTXRXBYTES +} PH_PROCESS_STATISTICS_INDEX; + +VOID PhpUpdateStatisticsAddListViewGroups( + _In_ PPH_STATISTICS_CONTEXT Context ) { - PPH_STATISTICS_CONTEXT statisticsContext = (PPH_STATISTICS_CONTEXT)Context; + ListView_EnableGroupView(Context->ListViewHandle, TRUE); + + PhAddListViewGroup(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, L"CPU"); + PhAddListViewGroup(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, L"Memory"); + PhAddListViewGroup(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, L"I/O"); + PhAddListViewGroup(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, L"Other"); + PhAddListViewGroup(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, L"Extension"); + + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_PRIORITY, L"Priority", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_CYCLES, L"Cycles", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_CYCLESDELTA, L"Cycles delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_KERNELTIME, L"Kernel time", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_KERNELDELTA, L"Kernel delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_USERTIME, L"User time", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_USERDELTA, L"User delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_TOTALTIME, L"Total time", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_CPU, PH_PROCESS_STATISTICS_INDEX_TOTALDELTA, L"Total delta", NULL); + + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTES, L"Private bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTESDELTA, L"Private bytes delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKPRIVATEBYTES, L"Peak private bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_VIRTUALSIZE, L"Virtual size", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKVIRTUALSIZE, L"Peak virtual size", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PAGEFAULTS, L"Page faults", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PAGEFAULTSDELTA, L"Page faults delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_WORKINGSET, L"Working set", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKWORKINGSET, L"Peak working set", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PRIVATEWS, L"Private WS", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_SHAREABLEWS, L"Shareable WS", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_SHAREDWS, L"Shared WS", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PAGEDPOOL, L"Paged pool bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKPAGEDPOOL, L"Peak paged pool bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_NONPAGED, L"Nonpaged pool bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PEAKNONPAGED, L"Peak nonpaged pool bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_MEMORY, PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, L"Page priority", NULL); + + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_READS, L"Reads", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_READSDELTA, L"Reads delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_READBYTES, L"Read bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_READBYTESDELTA, L"Read bytes delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_WRITES, L"Writes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_WRITESDELTA, L"Writes delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_WRITEBYTES, L"Write bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_WRITEBYTESDELTA, L"Write bytes delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_OTHER, L"Other", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_OTHERDELTA, L"Other delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_OTHERBYTES, L"Other bytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_OTHERBYTESDELTA, L"Other bytes delta", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_IO, PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, L"I/O priority", NULL); + + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_HANDLES, L"Handles", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_PEAKHANDLES, L"Peak handles", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_GDIHANDLES, L"GDI handles", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_USERHANDLES, L"USER handles", NULL); + + if (WindowsVersion >= WINDOWS_10_RS3) + { + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_RUNNINGTIME, L"Running time", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_SUSPENDEDTIME, L"Suspended time", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_HANGCOUNT, L"Hang count", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_OTHER, PH_PROCESS_STATISTICS_INDEX_GHOSTCOUNT, L"Ghost count", NULL); + } - if (statisticsContext->Enabled) - PostMessage(statisticsContext->WindowHandle, WM_PH_STATISTICS_UPDATE, 0, 0); + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) + { + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES, L"ContextSwitches", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_DISKREAD, L"BytesRead", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_DISKWRITE, L"BytesWritten", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_NETWORKTXRXBYTES, L"NetworkTxRxBytes", NULL); + PhAddListViewGroupItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_CATEGORY_EXTENSION, PH_PROCESS_STATISTICS_INDEX_MBBTXRXBYTES, L"MBBTxRxBytes", NULL); + } + + if (PhPluginsEnabled) + { + PH_PLUGIN_PROCESS_STATS_EVENT notifyEvent; + + notifyEvent.Version = 0; + notifyEvent.Type = 1; + notifyEvent.ProcessItem = Context->ProcessItem; + notifyEvent.Parameter = Context->ListViewHandle; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessStatsNotifyEvent), ¬ifyEvent); + } +} + +VOID PhpUpdateProcessStatisticDelta( + _In_ PPH_STATISTICS_CONTEXT Context, + _In_ INT Index, + _In_ ULONG_PTR Delta + ) +{ + LONG_PTR delta = (LONG_PTR)Delta; + + if (delta != 0) + { + PH_FORMAT format[2]; + + if (delta > 0) + { + PhInitFormatC(&format[0], '+'); + } + else + { + PhInitFormatC(&format[0], '-'); + delta = -delta; + } + + format[1].Type = SizeFormatType | FormatUseRadix; + format[1].Radix = (UCHAR)PhMaxSizeUnit; + format[1].u.Size = delta; + + PhSetListViewSubItem(Context->ListViewHandle, Index, 1, PH_AUTO_T(PH_STRING, PhFormat(format, 2, 0))->Buffer); + } + else + { + PhSetListViewSubItem(Context->ListViewHandle, Index, 1, L"0"); + } +} + +VOID PhpUpdateProcessStatisticDeltaBytes( + _In_ PPH_STATISTICS_CONTEXT Context, + _In_ INT Index, + _In_ PH_UINT64_DELTA DeltaBuffer + ) +{ + ULONG64 number = 0; + + if (DeltaBuffer.Delta != DeltaBuffer.Value) + { + number = DeltaBuffer.Delta; + number *= 1000; + number /= PhCsUpdateInterval; + } + + if (number != 0) + { + PH_FORMAT format[2]; + + PhInitFormatSize(&format[0], number); + PhInitFormatS(&format[1], L"/s"); + PhSetListViewSubItem(Context->ListViewHandle, Index, 1, PH_AUTO_T(PH_STRING, PhFormat(format, 2, 0))->Buffer); + } + else + { + PhSetListViewSubItem(Context->ListViewHandle, Index, 1, L"0"); + } } VOID PhpUpdateProcessStatistics( - _In_ HWND hwndDlg, _In_ PPH_PROCESS_ITEM ProcessItem, _In_ PPH_STATISTICS_CONTEXT Context ) { - WCHAR timeSpan[PH_TIMESPAN_STR_LEN_1]; - - SetDlgItemInt(hwndDlg, IDC_ZPRIORITY_V, ProcessItem->BasePriority, TRUE); // priority - PhPrintTimeSpan(timeSpan, ProcessItem->KernelTime.QuadPart, PH_TIMESPAN_HMSM); // kernel time - SetDlgItemText(hwndDlg, IDC_ZKERNELTIME_V, timeSpan); - PhPrintTimeSpan(timeSpan, ProcessItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); // user time - SetDlgItemText(hwndDlg, IDC_ZUSERTIME_V, timeSpan); - PhPrintTimeSpan(timeSpan, - ProcessItem->KernelTime.QuadPart + ProcessItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); // total time - SetDlgItemText(hwndDlg, IDC_ZTOTALTIME_V, timeSpan); - - SetDlgItemText(hwndDlg, IDC_ZPRIVATEBYTES_V, - PhaFormatSize(ProcessItem->VmCounters.PagefileUsage, -1)->Buffer); // private bytes (same as PrivateUsage) - SetDlgItemText(hwndDlg, IDC_ZPEAKPRIVATEBYTES_V, - PhaFormatSize(ProcessItem->VmCounters.PeakPagefileUsage, -1)->Buffer); // peak private bytes - SetDlgItemText(hwndDlg, IDC_ZVIRTUALSIZE_V, - PhaFormatSize(ProcessItem->VmCounters.VirtualSize, -1)->Buffer); // virtual size - SetDlgItemText(hwndDlg, IDC_ZPEAKVIRTUALSIZE_V, - PhaFormatSize(ProcessItem->VmCounters.PeakVirtualSize, -1)->Buffer); // peak virtual size - SetDlgItemText(hwndDlg, IDC_ZPAGEFAULTS_V, - PhaFormatUInt64(ProcessItem->VmCounters.PageFaultCount, TRUE)->Buffer); // page faults - SetDlgItemText(hwndDlg, IDC_ZWORKINGSET_V, - PhaFormatSize(ProcessItem->VmCounters.WorkingSetSize, -1)->Buffer); // working set - SetDlgItemText(hwndDlg, IDC_ZPEAKWORKINGSET_V, - PhaFormatSize(ProcessItem->VmCounters.PeakWorkingSetSize, -1)->Buffer); // peak working set - - SetDlgItemText(hwndDlg, IDC_ZIOREADS_V, - PhaFormatUInt64(ProcessItem->IoCounters.ReadOperationCount, TRUE)->Buffer); // reads - SetDlgItemText(hwndDlg, IDC_ZIOREADBYTES_V, - PhaFormatSize(ProcessItem->IoCounters.ReadTransferCount, -1)->Buffer); // read bytes - SetDlgItemText(hwndDlg, IDC_ZIOWRITES_V, - PhaFormatUInt64(ProcessItem->IoCounters.WriteOperationCount, TRUE)->Buffer); // writes - SetDlgItemText(hwndDlg, IDC_ZIOWRITEBYTES_V, - PhaFormatSize(ProcessItem->IoCounters.WriteTransferCount, -1)->Buffer); // write bytes - SetDlgItemText(hwndDlg, IDC_ZIOOTHER_V, - PhaFormatUInt64(ProcessItem->IoCounters.OtherOperationCount, TRUE)->Buffer); // other - SetDlgItemText(hwndDlg, IDC_ZIOOTHERBYTES_V, - PhaFormatSize(ProcessItem->IoCounters.OtherTransferCount, -1)->Buffer); // read bytes - - SetDlgItemText(hwndDlg, IDC_ZHANDLES_V, - PhaFormatUInt64(ProcessItem->NumberOfHandles, TRUE)->Buffer); // handles + WCHAR priority[PH_INT32_STR_LEN_1] = L""; + WCHAR timeSpan[PH_TIMESPAN_STR_LEN_1] = L""; + + PhPrintInt32(priority, ProcessItem->BasePriority); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PRIORITY, 1, priority); + PhPrintTimeSpan(timeSpan, ProcessItem->KernelTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_KERNELTIME, 1, timeSpan); + PhPrintTimeSpan(timeSpan, ProcessItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_USERTIME, 1, timeSpan); + PhPrintTimeSpan(timeSpan, ProcessItem->KernelTime.QuadPart + ProcessItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_TOTALTIME, 1, timeSpan); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_CYCLESDELTA, 1, PhaFormatUInt64(ProcessItem->CycleTimeDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_KERNELDELTA, 1, PhaFormatUInt64(ProcessItem->CpuKernelDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_USERDELTA, 1, PhaFormatUInt64(ProcessItem->CpuUserDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_TOTALDELTA, 1, PhaFormatUInt64(ProcessItem->CpuKernelDelta.Delta + ProcessItem->CpuUserDelta.Delta, TRUE)->Buffer); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTES, 1, PhaFormatSize(ProcessItem->VmCounters.PagefileUsage, ULONG_MAX)->Buffer); + PhpUpdateProcessStatisticDelta(Context, PH_PROCESS_STATISTICS_INDEX_PRIVATEBYTESDELTA, ProcessItem->PrivateBytesDelta.Delta); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKPRIVATEBYTES, 1, PhaFormatSize(ProcessItem->VmCounters.PeakPagefileUsage, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_VIRTUALSIZE, 1, PhaFormatSize(ProcessItem->VmCounters.VirtualSize, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKVIRTUALSIZE, 1, PhaFormatSize(ProcessItem->VmCounters.PeakVirtualSize, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEFAULTS, 1, PhaFormatUInt64(ProcessItem->VmCounters.PageFaultCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEFAULTSDELTA, 1, PhaFormatUInt64(ProcessItem->PageFaultsDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WORKINGSET, 1, PhaFormatSize(ProcessItem->VmCounters.WorkingSetSize, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKWORKINGSET, 1, PhaFormatSize(ProcessItem->VmCounters.PeakWorkingSetSize, ULONG_MAX)->Buffer); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEDPOOL, 1, PhaFormatSize(ProcessItem->VmCounters.QuotaPagedPoolUsage, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKPAGEDPOOL, 1, PhaFormatSize(ProcessItem->VmCounters.QuotaPeakPagedPoolUsage, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_NONPAGED, 1, PhaFormatSize(ProcessItem->VmCounters.QuotaNonPagedPoolUsage, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKNONPAGED, 1, PhaFormatSize(ProcessItem->VmCounters.QuotaPeakNonPagedPoolUsage, ULONG_MAX)->Buffer); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_READS, 1, PhaFormatUInt64(ProcessItem->IoCounters.ReadOperationCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_READSDELTA, 1, PhaFormatUInt64(ProcessItem->IoReadCountDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_READBYTES, 1, PhaFormatSize(ProcessItem->IoCounters.ReadTransferCount, ULONG_MAX)->Buffer); + PhpUpdateProcessStatisticDeltaBytes(Context, PH_PROCESS_STATISTICS_INDEX_READBYTESDELTA, ProcessItem->IoReadDelta); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WRITES, 1, PhaFormatUInt64(ProcessItem->IoCounters.WriteOperationCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WRITESDELTA, 1, PhaFormatUInt64(ProcessItem->IoWriteCountDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_WRITEBYTES, 1, PhaFormatSize(ProcessItem->IoCounters.WriteTransferCount, ULONG_MAX)->Buffer); + PhpUpdateProcessStatisticDeltaBytes(Context, PH_PROCESS_STATISTICS_INDEX_WRITEBYTESDELTA, ProcessItem->IoWriteDelta); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_OTHER, 1, PhaFormatUInt64(ProcessItem->IoCounters.OtherOperationCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_OTHERDELTA, 1, PhaFormatUInt64(ProcessItem->IoOtherCountDelta.Delta, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_OTHERBYTES, 1, PhaFormatSize(ProcessItem->IoCounters.OtherTransferCount, ULONG_MAX)->Buffer); + PhpUpdateProcessStatisticDeltaBytes(Context, PH_PROCESS_STATISTICS_INDEX_OTHERBYTESDELTA, ProcessItem->IoOtherDelta); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_HANDLES, 1, PhaFormatUInt64(ProcessItem->NumberOfHandles, TRUE)->Buffer); // Optional information if (!PH_IS_FAKE_PROCESS_ID(ProcessItem->ProcessId)) @@ -92,48 +312,50 @@ VOID PhpUpdateProcessStatistics( PPH_STRING gdiHandles = NULL; PPH_STRING userHandles = NULL; PPH_STRING cycles = NULL; - ULONG pagePriority = -1; - IO_PRIORITY_HINT ioPriority = -1; + ULONG pagePriority = ULONG_MAX; + IO_PRIORITY_HINT ioPriority = ULONG_MAX; PPH_STRING privateWs = NULL; PPH_STRING shareableWs = NULL; PPH_STRING sharedWs = NULL; BOOLEAN gotCycles = FALSE; BOOLEAN gotWsCounters = FALSE; + BOOLEAN gotUptime = FALSE; + ULONG hangCount = 0; + ULONG ghostCount = 0; + ULONGLONG runningTime = 0; + ULONGLONG suspendedTime = 0; + WCHAR timeSpan[PH_TIMESPAN_STR_LEN_1] = L""; if (ProcessItem->QueryHandle) { ULONG64 cycleTime; + PROCESS_HANDLE_INFORMATION handleInfo; + PROCESS_UPTIME_INFORMATION uptimeInfo; - if (WindowsVersion >= WINDOWS_7) + if (NT_SUCCESS(PhGetProcessHandleCount(ProcessItem->QueryHandle, &handleInfo))) { - PROCESS_HANDLE_INFORMATION handleInfo; - - if (NT_SUCCESS(NtQueryInformationProcess( - ProcessItem->QueryHandle, - ProcessHandleCount, - &handleInfo, - sizeof(PROCESS_HANDLE_INFORMATION), - NULL - ))) - { - peakHandles = PhaFormatUInt64(handleInfo.HandleCountHighWatermark, TRUE); - } + peakHandles = PhaFormatUInt64(handleInfo.HandleCountHighWatermark, TRUE); } gdiHandles = PhaFormatUInt64(GetGuiResources(ProcessItem->QueryHandle, GR_GDIOBJECTS), TRUE); // GDI handles userHandles = PhaFormatUInt64(GetGuiResources(ProcessItem->QueryHandle, GR_USEROBJECTS), TRUE); // USER handles - if (WINDOWS_HAS_CYCLE_TIME && - NT_SUCCESS(PhGetProcessCycleTime(ProcessItem->QueryHandle, &cycleTime))) + if (NT_SUCCESS(PhGetProcessCycleTime(ProcessItem->QueryHandle, &cycleTime))) { cycles = PhaFormatUInt64(cycleTime, TRUE); gotCycles = TRUE; } - if (WindowsVersion >= WINDOWS_VISTA) + PhGetProcessPagePriority(ProcessItem->QueryHandle, &pagePriority); + PhGetProcessIoPriority(ProcessItem->QueryHandle, &ioPriority); + + if (WindowsVersion >= WINDOWS_10_RS3 && NT_SUCCESS(PhGetProcessUptime(ProcessItem->QueryHandle, &uptimeInfo))) { - PhGetProcessPagePriority(ProcessItem->QueryHandle, &pagePriority); - PhGetProcessIoPriority(ProcessItem->QueryHandle, &ioPriority); + runningTime = uptimeInfo.Uptime; + suspendedTime = uptimeInfo.SuspendedTime; + hangCount = uptimeInfo.HangCount; + ghostCount = uptimeInfo.GhostCount; + gotUptime = TRUE; } } @@ -143,67 +365,96 @@ VOID PhpUpdateProcessStatistics( if (NT_SUCCESS(PhGetProcessWsCounters(Context->ProcessHandle, &wsCounters))) { - privateWs = PhaFormatSize((ULONG64)wsCounters.NumberOfPrivatePages * PAGE_SIZE, -1); - shareableWs = PhaFormatSize((ULONG64)wsCounters.NumberOfShareablePages * PAGE_SIZE, -1); - sharedWs = PhaFormatSize((ULONG64)wsCounters.NumberOfSharedPages * PAGE_SIZE, -1); + privateWs = PhaFormatSize((ULONG64)wsCounters.NumberOfPrivatePages * PAGE_SIZE, ULONG_MAX); + shareableWs = PhaFormatSize((ULONG64)wsCounters.NumberOfShareablePages * PAGE_SIZE, ULONG_MAX); + sharedWs = PhaFormatSize((ULONG64)wsCounters.NumberOfSharedPages * PAGE_SIZE, ULONG_MAX); gotWsCounters = TRUE; } } - if (WindowsVersion >= WINDOWS_7) - { - if (!gotCycles) - cycles = PhaFormatUInt64(ProcessItem->CycleTimeDelta.Value, TRUE); - if (!gotWsCounters) - privateWs = PhaFormatSize(ProcessItem->WorkingSetPrivateSize, -1); - } + if (!gotCycles) + cycles = PhaFormatUInt64(ProcessItem->CycleTimeDelta.Value, TRUE); + if (!gotWsCounters) + privateWs = PhaFormatSize(ProcessItem->WorkingSetPrivateSize, ULONG_MAX); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_CYCLES, 1, PhGetStringOrDefault(cycles, L"N/A")); - if (WindowsVersion >= WINDOWS_7) - SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, PhGetStringOrDefault(peakHandles, L"Unknown")); + if (pagePriority != ULONG_MAX && pagePriority <= MEMORY_PRIORITY_NORMAL) + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, 1, PhPagePriorityNames[pagePriority]); else - SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, L"N/A"); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PAGEPRIORITY, 1, L"N/A"); + + if (ioPriority != ULONG_MAX && ioPriority < MaxIoPriorityTypes) + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, 1, PhIoPriorityHintNames[ioPriority]); + else + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_IOPRIORITY, 1, L"N/A"); + + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PRIVATEWS, 1, PhGetStringOrDefault(privateWs, L"N/A")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_SHAREABLEWS, 1, PhGetStringOrDefault(shareableWs, L"N/A")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_SHAREDWS, 1, PhGetStringOrDefault(sharedWs, L"N/A")); - SetDlgItemText(hwndDlg, IDC_ZGDIHANDLES_V, PhGetStringOrDefault(gdiHandles, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_ZUSERHANDLES_V, PhGetStringOrDefault(userHandles, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_ZCYCLES_V, - PhGetStringOrDefault(cycles, WINDOWS_HAS_CYCLE_TIME ? L"Unknown" : L"N/A")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_PEAKHANDLES, 1, PhGetStringOrDefault(peakHandles, L"N/A")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_GDIHANDLES, 1, PhGetStringOrDefault(gdiHandles, L"N/A")); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_USERHANDLES, 1, PhGetStringOrDefault(userHandles, L"N/A")); - if (WindowsVersion >= WINDOWS_VISTA) + if (WindowsVersion >= WINDOWS_10_RS3) { - if (pagePriority != -1 && pagePriority <= MEMORY_PRIORITY_NORMAL) - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, PhPagePriorityNames[pagePriority]); - else - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, L"Unknown"); - - if (ioPriority != -1 && ioPriority < MaxIoPriorityTypes) - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, PhIoPriorityHintNames[ioPriority]); - else - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, L"Unknown"); + PhPrintTimeSpan(timeSpan, runningTime, PH_TIMESPAN_HMSM); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_RUNNINGTIME, 1, timeSpan); + PhPrintTimeSpan(timeSpan, suspendedTime, PH_TIMESPAN_HMSM); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_SUSPENDEDTIME, 1, timeSpan); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_HANGCOUNT, 1, PhaFormatUInt64(hangCount, TRUE)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_GHOSTCOUNT, 1, PhaFormatUInt64(ghostCount, TRUE)->Buffer); } - else + } + + if (WindowsVersion >= WINDOWS_10_RS3 && !PhIsExecutingInWow64()) + { + PVOID processes; + PSYSTEM_PROCESS_INFORMATION processInfo; + PSYSTEM_PROCESS_INFORMATION_EXTENSION processExtension; + + if (NT_SUCCESS(PhEnumProcesses(&processes))) { - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, L"N/A"); - } + processInfo = PhFindProcessInformation(processes, ProcessItem->ProcessId); + + if (processInfo && (processExtension = PH_PROCESS_EXTENSION(processInfo))) + { + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES, 1, PhaFormatUInt64(processExtension->ContextSwitches, TRUE)->Buffer); // TODO: ContextSwitchesDelta + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_DISKREAD, 1, PhaFormatSize(processExtension->DiskCounters.BytesRead, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_DISKWRITE, 1, PhaFormatSize(processExtension->DiskCounters.BytesWritten, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_NETWORKTXRXBYTES, 1, PhaFormatSize(processExtension->EnergyValues.NetworkTxRxBytes, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, PH_PROCESS_STATISTICS_INDEX_MBBTXRXBYTES, 1, PhaFormatSize(processExtension->EnergyValues.MBBTxRxBytes, ULONG_MAX)->Buffer); + } - SetDlgItemText(hwndDlg, IDC_ZPRIVATEWS_V, PhGetStringOrDefault(privateWs, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_ZSHAREABLEWS_V, PhGetStringOrDefault(shareableWs, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_ZSHAREDWS_V, PhGetStringOrDefault(sharedWs, L"Unknown")); + PhFree(processes); + } } - else + + if (PhPluginsEnabled) { - SetDlgItemText(hwndDlg, IDC_ZPEAKHANDLES_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZGDIHANDLES_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZUSERHANDLES_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZCYCLES_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZPAGEPRIORITY_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZIOPRIORITY_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZPRIVATEWS_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZSHAREABLEWS_V, L"N/A"); - SetDlgItemText(hwndDlg, IDC_ZSHAREDWS_V, L"N/A"); + PH_PLUGIN_PROCESS_STATS_EVENT notifyEvent; + + notifyEvent.Version = 0; + notifyEvent.Type = 2; + notifyEvent.ProcessItem = Context->ProcessItem; + notifyEvent.Parameter = Context->ListViewHandle; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackProcessStatsNotifyEvent), ¬ifyEvent); } } +static VOID NTAPI PhpStatisticsUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_STATISTICS_CONTEXT statisticsContext = (PPH_STATISTICS_CONTEXT)Context; + + if (statisticsContext->Enabled) + PostMessage(statisticsContext->WindowHandle, WM_PH_STATISTICS_UPDATE, 0, 0); +} + INT_PTR CALLBACK PhpProcessStatisticsDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -216,8 +467,7 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( PPH_PROCESS_ITEM processItem; PPH_STATISTICS_CONTEXT statisticsContext; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { statisticsContext = (PPH_STATISTICS_CONTEXT)propPageContext->Context; } @@ -230,35 +480,49 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( { case WM_INITDIALOG: { - statisticsContext = propPageContext->Context = - PhAllocate(sizeof(PH_STATISTICS_CONTEXT)); - + statisticsContext = propPageContext->Context = PhAllocateZero(sizeof(PH_STATISTICS_CONTEXT)); statisticsContext->WindowHandle = hwndDlg; + statisticsContext->ListViewHandle = GetDlgItem(hwndDlg, IDC_STATISTICS_LIST); + statisticsContext->ProcessItem = processItem; statisticsContext->Enabled = TRUE; - statisticsContext->ProcessHandle = NULL; - - // Try to open a process handle with PROCESS_QUERY_INFORMATION access for - // WS information. - PhOpenProcess( - &statisticsContext->ProcessHandle, - PROCESS_QUERY_INFORMATION, - processItem->ProcessId - ); + + // Try to open a process handle with PROCESS_QUERY_INFORMATION access for WS information. + if (PH_IS_REAL_PROCESS_ID(processItem->ProcessId)) + { + PhOpenProcess( + &statisticsContext->ProcessHandle, + PROCESS_QUERY_INFORMATION, + processItem->ProcessId + ); + } + + PhSetListViewStyle(statisticsContext->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(statisticsContext->ListViewHandle, L"explorer"); + PhAddListViewColumn(statisticsContext->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 135, L"Property"); + PhAddListViewColumn(statisticsContext->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"Value"); + PhSetExtendedListView(statisticsContext->ListViewHandle); + + PhpUpdateStatisticsAddListViewGroups(statisticsContext); + PhLoadListViewGroupStatesFromSetting(L"ProcStatPropPageGroupStates", statisticsContext->ListViewHandle); PhRegisterCallback( - &PhProcessesUpdatedEvent, - StatisticsUpdateHandler, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + PhpStatisticsUpdateHandler, statisticsContext, &statisticsContext->ProcessesUpdatedRegistration ); - PhpUpdateProcessStatistics(hwndDlg, processItem, statisticsContext); + PhpUpdateProcessStatistics(processItem, statisticsContext); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: { + PhSaveListViewGroupStatesToSetting(L"ProcStatPropPageGroupStates", statisticsContext->ListViewHandle); + PhUnregisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &statisticsContext->ProcessesUpdatedRegistration ); @@ -266,41 +530,27 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( NtClose(statisticsContext->ProcessHandle); PhFree(statisticsContext); - - PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; + PPH_LAYOUT_ITEM dialogItem; - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) { - case IDC_DETAILS: - { - PhShowHandleStatisticsDialog(hwndDlg, processItem->ProcessId); - } - break; + PhAddPropPageLayoutItem(hwndDlg, statisticsContext->ListViewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); } + + ExtendedListView_SetColumnWidth(statisticsContext->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; + PhHandleListViewNotifyBehaviors(lParam, statisticsContext->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + switch (header->code) { case PSN_SETACTIVE: @@ -314,7 +564,75 @@ INT_PTR CALLBACK PhpProcessStatisticsDlgProc( break; case WM_PH_STATISTICS_UPDATE: { - PhpUpdateProcessStatistics(hwndDlg, processItem, statisticsContext); + PhpUpdateProcessStatistics(processItem, statisticsContext); + } + break; + case WM_SIZE: + { + ExtendedListView_SetColumnWidth(statisticsContext->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == statisticsContext->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(statisticsContext->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, statisticsContext->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(statisticsContext->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } } break; } diff --git a/ProcessHacker/prpgthrd.c b/ProcessHacker/prpgthrd.c index 53e2cf7797e5..b3697eb542e1 100644 --- a/ProcessHacker/prpgthrd.c +++ b/ProcessHacker/prpgthrd.c @@ -3,6 +3,7 @@ * Process properties: Threads page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2018 dmex * * This file is part of Process Hacker. * @@ -27,10 +28,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -102,8 +105,6 @@ VOID PhpInitializeThreadMenu( _In_ ULONG NumberOfThreads ) { - PPH_EMENU_ITEM item; - if (NumberOfThreads == 0) { PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); @@ -133,62 +134,64 @@ VOID PhpInitializeThreadMenu( } } - // Remove irrelevant menu items. - - if (WindowsVersion < WINDOWS_VISTA) - { - // Remove I/O priority. - if (item = PhFindEMenuItem(Menu, 0, L"I/O Priority", 0)) - PhDestroyEMenuItem(item); - // Remove page priority. - if (item = PhFindEMenuItem(Menu, 0, L"Page Priority", 0)) - PhDestroyEMenuItem(item); - } - PhEnableEMenuItem(Menu, ID_THREAD_TOKEN, FALSE); - // Priority + // Critical if (NumberOfThreads == 1) { HANDLE threadHandle; - ULONG threadPriority = THREAD_PRIORITY_ERROR_RETURN; - IO_PRIORITY_HINT ioPriority = -1; - ULONG pagePriority = -1; - ULONG id = 0; if (NT_SUCCESS(PhOpenThread( &threadHandle, - ThreadQueryAccess, + THREAD_QUERY_INFORMATION, Threads[0]->ThreadId ))) { - THREAD_BASIC_INFORMATION basicInfo; + BOOLEAN breakOnTermination; - if (NT_SUCCESS(PhGetThreadBasicInformation(threadHandle, &basicInfo))) + if (NT_SUCCESS(PhGetThreadBreakOnTermination( + threadHandle, + &breakOnTermination + ))) { - threadPriority = basicInfo.BasePriority; + if (breakOnTermination) + { + PhSetFlagsEMenuItem(Menu, ID_THREAD_CRITICAL, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + } } + } + } - if (WindowsVersion >= WINDOWS_VISTA) - { - PhGetThreadIoPriority(threadHandle, &ioPriority); - PhGetThreadPagePriority(threadHandle, &pagePriority); - } + // Priority + if (NumberOfThreads == 1) + { + HANDLE threadHandle; + ULONG threadPriority = THREAD_PRIORITY_ERROR_RETURN; + IO_PRIORITY_HINT ioPriority = ULONG_MAX; + ULONG pagePriority = ULONG_MAX; + ULONG id = 0; - // Token + if (NT_SUCCESS(PhOpenThread( + &threadHandle, + THREAD_QUERY_LIMITED_INFORMATION, + Threads[0]->ThreadId + ))) + { + HANDLE tokenHandle; + + PhGetThreadBasePriority(threadHandle, &threadPriority); + PhGetThreadIoPriority(threadHandle, &ioPriority); + PhGetThreadPagePriority(threadHandle, &pagePriority); + + if (NT_SUCCESS(NtOpenThreadToken( + threadHandle, + TOKEN_QUERY, + TRUE, + &tokenHandle + ))) { - HANDLE tokenHandle; - - if (NT_SUCCESS(NtOpenThreadToken( - threadHandle, - TOKEN_QUERY, - TRUE, - &tokenHandle - ))) - { - PhEnableEMenuItem(Menu, ID_THREAD_TOKEN, TRUE); - NtClose(tokenHandle); - } + PhEnableEMenuItem(Menu, ID_THREAD_TOKEN, TRUE); + NtClose(tokenHandle); } NtClose(threadHandle); @@ -228,7 +231,7 @@ VOID PhpInitializeThreadMenu( PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK); } - if (ioPriority != -1) + if (ioPriority != ULONG_MAX) { id = 0; @@ -256,7 +259,7 @@ VOID PhpInitializeThreadMenu( } } - if (pagePriority != -1) + if (pagePriority != ULONG_MAX) { id = 0; @@ -312,138 +315,139 @@ static NTSTATUS NTAPI PhpOpenThreadTokenObject( ); } -VOID PhpUpdateThreadDetails( - _In_ HWND hwndDlg, - _In_ PPH_THREADS_CONTEXT Context, - _In_ BOOLEAN Force +static BOOLEAN PhpWordMatchThreadStringRef( + _In_ PPH_STRING SearchText, + _In_ PPH_STRINGREF Text ) { - PPH_THREAD_ITEM *threads; - ULONG numberOfThreads; - PPH_THREAD_ITEM threadItem; - PPH_STRING startModule = NULL; - PPH_STRING started = NULL; - WCHAR kernelTime[PH_TIMESPAN_STR_LEN_1] = L"N/A"; - WCHAR userTime[PH_TIMESPAN_STR_LEN_1] = L"N/A"; - PPH_STRING contextSwitches = NULL; - PPH_STRING cycles = NULL; - PPH_STRING state = NULL; - WCHAR priority[PH_INT32_STR_LEN_1] = L"N/A"; - WCHAR basePriority[PH_INT32_STR_LEN_1] = L"N/A"; - PWSTR ioPriority = L"N/A"; - PWSTR pagePriority = L"N/A"; - WCHAR idealProcessor[PH_INT32_STR_LEN + 1 + PH_INT32_STR_LEN + 1] = L"N/A"; - HANDLE threadHandle; - SYSTEMTIME time; - IO_PRIORITY_HINT ioPriorityInteger; - ULONG pagePriorityInteger; - PROCESSOR_NUMBER idealProcessorNumber; - ULONG suspendCount; + PH_STRINGREF part; + PH_STRINGREF remainingPart; - PhGetSelectedThreadItems(&Context->ListContext, &threads, &numberOfThreads); + remainingPart = SearchText->sr; - if (numberOfThreads == 1) - threadItem = threads[0]; - else - threadItem = NULL; + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - PhFree(threads); + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } - if (numberOfThreads != 1 && !Force) - return; + return FALSE; +} - if (numberOfThreads == 1) - { - startModule = threadItem->StartAddressFileName; +static BOOLEAN PhpWordMatchThreadStringZ( + _In_ PPH_STRING SearchText, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; - PhLargeIntegerToLocalSystemTime(&time, &threadItem->CreateTime); - started = PhaFormatDateTime(&time); + PhInitializeStringRef(&text, Text); - PhPrintTimeSpan(kernelTime, threadItem->KernelTime.QuadPart, PH_TIMESPAN_HMSM); - PhPrintTimeSpan(userTime, threadItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); + return PhpWordMatchThreadStringRef(SearchText, &text); +} - contextSwitches = PhaFormatUInt64(threadItem->ContextSwitchesDelta.Value, TRUE); +BOOLEAN PhpThreadTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PPH_THREADS_CONTEXT Context + ) +{ + PPH_THREAD_NODE threadNode = (PPH_THREAD_NODE)Node; + PPH_THREAD_ITEM threadItem = threadNode->ThreadItem; - if (WINDOWS_HAS_CYCLE_TIME) - cycles = PhaFormatUInt64(threadItem->CyclesDelta.Value, TRUE); + if (Context->ListContext.HideSuspended && threadItem->WaitReason == Suspended) + return FALSE; + if (Context->ListContext.HideGuiThreads && threadItem->IsGuiThread) + return FALSE; - if (threadItem->State != Waiting) - { - if ((ULONG)threadItem->State < MaximumThreadState) - state = PhaCreateString(PhKThreadStateNames[(ULONG)threadItem->State]); - else - state = PhaCreateString(L"Unknown"); - } - else - { - if ((ULONG)threadItem->WaitReason < MaximumWaitReason) - state = PhaConcatStrings2(L"Wait:", PhKWaitReasonNames[(ULONG)threadItem->WaitReason]); - else - state = PhaCreateString(L"Waiting"); - } + if (PhIsNullOrEmptyString(Context->SearchboxText)) + return TRUE; - PhPrintInt32(priority, threadItem->Priority); - PhPrintInt32(basePriority, threadItem->BasePriority); + // thread properties - if (NT_SUCCESS(PhOpenThread(&threadHandle, ThreadQueryAccess, threadItem->ThreadId))) - { - if (NT_SUCCESS(PhGetThreadIoPriority(threadHandle, &ioPriorityInteger)) && - ioPriorityInteger < MaxIoPriorityTypes) - { - ioPriority = PhIoPriorityHintNames[ioPriorityInteger]; - } + if (threadNode->ThreadIdText[0]) + { + if (PhpWordMatchThreadStringZ(Context->SearchboxText, threadNode->ThreadIdText)) + return TRUE; + } - if (NT_SUCCESS(PhGetThreadPagePriority(threadHandle, &pagePriorityInteger)) && - pagePriorityInteger <= MEMORY_PRIORITY_NORMAL) - { - pagePriority = PhPagePriorityNames[pagePriorityInteger]; - } + if (threadNode->PriorityText[0]) + { + if (PhpWordMatchThreadStringZ(Context->SearchboxText, threadNode->PriorityText)) + return TRUE; + } - if (NT_SUCCESS(NtQueryInformationThread(threadHandle, ThreadIdealProcessorEx, &idealProcessorNumber, sizeof(PROCESSOR_NUMBER), NULL))) - { - PH_FORMAT format[3]; + if (threadNode->BasePriorityText[0]) + { + if (PhpWordMatchThreadStringZ(Context->SearchboxText, threadNode->BasePriorityText)) + return TRUE; + } - PhInitFormatU(&format[0], idealProcessorNumber.Group); - PhInitFormatC(&format[1], ':'); - PhInitFormatU(&format[2], idealProcessorNumber.Number); - PhFormatToBuffer(format, 3, idealProcessor, sizeof(idealProcessor), NULL); - } + if (threadNode->IdealProcessorText[0]) + { + if (PhpWordMatchThreadStringZ(Context->SearchboxText, threadNode->IdealProcessorText)) + return TRUE; + } - if (threadItem->WaitReason == Suspended && NT_SUCCESS(NtQueryInformationThread(threadHandle, ThreadSuspendCount, &suspendCount, sizeof(ULONG), NULL))) - { - PH_FORMAT format[4]; + if (threadNode->ThreadIdHexText[0]) + { + if (PhpWordMatchThreadStringZ(Context->SearchboxText, threadNode->ThreadIdHexText)) + return TRUE; + } - PhInitFormatSR(&format[0], state->sr); - PhInitFormatS(&format[1], L" ("); - PhInitFormatU(&format[2], suspendCount); - PhInitFormatS(&format[3], L")"); - state = PH_AUTO(PhFormat(format, 4, 30)); - } + if (!PhIsNullOrEmptyString(threadNode->StartAddressText)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->StartAddressText->sr)) + return TRUE; + } - NtClose(threadHandle); - } + if (!PhIsNullOrEmptyString(threadNode->PrioritySymbolicText)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->PrioritySymbolicText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(threadNode->CreatedText)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->CreatedText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(threadNode->NameText)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->NameText->sr)) + return TRUE; } - if (Force) + if (!PhIsNullOrEmptyString(threadNode->StateText)) { - // These don't change... + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->StateText->sr)) + return TRUE; + } - SetDlgItemText(hwndDlg, IDC_STARTMODULE, PhGetStringOrEmpty(startModule)); - EnableWindow(GetDlgItem(hwndDlg, IDC_OPENSTARTMODULE), !!startModule); + if (!PhIsNullOrEmptyString(threadNode->ThreadItem->ServiceName)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->ThreadItem->ServiceName->sr)) + return TRUE; + } - SetDlgItemText(hwndDlg, IDC_STARTED, PhGetStringOrDefault(started, L"N/A")); + if (!PhIsNullOrEmptyString(threadNode->ThreadItem->StartAddressString)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->ThreadItem->StartAddressString->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(threadNode->ThreadItem->StartAddressFileName)) + { + if (PhpWordMatchThreadStringRef(Context->SearchboxText, &threadNode->ThreadItem->StartAddressFileName->sr)) + return TRUE; } - SetDlgItemText(hwndDlg, IDC_KERNELTIME, kernelTime); - SetDlgItemText(hwndDlg, IDC_USERTIME, userTime); - SetDlgItemText(hwndDlg, IDC_CONTEXTSWITCHES, PhGetStringOrDefault(contextSwitches, L"N/A")); - SetDlgItemText(hwndDlg, IDC_CYCLES, PhGetStringOrDefault(cycles, L"N/A")); - SetDlgItemText(hwndDlg, IDC_STATE, PhGetStringOrDefault(state, L"N/A")); - SetDlgItemText(hwndDlg, IDC_PRIORITY, priority); - SetDlgItemText(hwndDlg, IDC_BASEPRIORITY, basePriority); - SetDlgItemText(hwndDlg, IDC_IOPRIORITY, ioPriority); - SetDlgItemText(hwndDlg, IDC_PAGEPRIORITY, pagePriority); - SetDlgItemText(hwndDlg, IDC_IDEALPROCESSOR, idealProcessor); + return FALSE; } VOID PhShowThreadContextMenu( @@ -522,8 +526,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( PPH_THREADS_CONTEXT threadsContext; HWND tnHandle; - if (PhpPropPageDlgProcHeader(hwndDlg, uMsg, lParam, - &propSheetPage, &propPageContext, &processItem)) + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { threadsContext = (PPH_THREADS_CONTEXT)propPageContext->Context; @@ -539,8 +542,8 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { case WM_INITDIALOG: { - threadsContext = propPageContext->Context = - PhAllocate(PhEmGetObjectSize(EmThreadsContextType, sizeof(PH_THREADS_CONTEXT))); + threadsContext = propPageContext->Context = PhAllocate(PhEmGetObjectSize(EmThreadsContextType, sizeof(PH_THREADS_CONTEXT))); + memset(threadsContext, 0, sizeof(PH_THREADS_CONTEXT)); // The thread provider has a special registration mechanism. threadsContext->Provider = PhCreateThreadProvider( @@ -577,24 +580,27 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( &threadsContext->LoadingStateChangedEventRegistration ); threadsContext->WindowHandle = hwndDlg; + threadsContext->SearchboxHandle = GetDlgItem(hwndDlg, IDC_SEARCH); + + PhCreateSearchControl(hwndDlg, threadsContext->SearchboxHandle, L"Search Threads (Ctrl+K)"); // Initialize the list. tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - BringWindowToTop(tnHandle); PhInitializeThreadList(hwndDlg, tnHandle, &threadsContext->ListContext); TreeNew_SetEmptyText(tnHandle, &EmptyThreadsText, 0); PhInitializeProviderEventQueue(&threadsContext->EventQueue, 100); + threadsContext->SearchboxText = PhReferenceEmptyString(); + threadsContext->FilterEntry = PhAddTreeNewFilter(&threadsContext->ListContext.TreeFilterSupport, PhpThreadTreeFilterCallback, threadsContext); // Use Cycles instead of Context Switches on Vista and above, but only when we can // open the process, since cycle time information requires sufficient access to the - // threads. - if (WINDOWS_HAS_CYCLE_TIME) + // threads. (wj32) { HANDLE processHandle; // We make a distinction between PROCESS_QUERY_INFORMATION and PROCESS_QUERY_LIMITED_INFORMATION since // the latter can be used when opening audiodg.exe even though we can't access its threads using - // THREAD_QUERY_LIMITED_INFORMATION. + // THREAD_QUERY_LIMITED_INFORMATION. (wj32) if (processItem->ProcessId == SYSTEM_IDLE_PROCESS_ID) { @@ -609,7 +615,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { threadsContext->ListContext.UseCycleTime = TRUE; - // We can't use cycle time for protected processes (without KProcessHacker). + // We can't use cycle time for protected processes (without KProcessHacker). (wj32) if (processItem->IsProtectedProcess) { threadsContext->ListContext.UseCycleTime = FALSE; @@ -619,7 +625,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( } } - if (processItem->ServiceList && processItem->ServiceList->Count != 0 && WINDOWS_HAS_SERVICE_TAGS) + if (processItem->ServiceList && processItem->ServiceList->Count != 0) threadsContext->ListContext.HasServices = TRUE; PhEmCallObjectOperation(EmThreadsContextType, threadsContext, EmObjectCreate); @@ -639,11 +645,14 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( PhThreadProviderInitialUpdate(threadsContext->Provider); PhRegisterThreadProvider(threadsContext->Provider, &threadsContext->ProviderRegistration); - SET_BUTTON_ICON(IDC_OPENSTARTMODULE, PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_FOLDER))); + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); } break; case WM_DESTROY: { + PhRemoveTreeNewFilter(&threadsContext->ListContext.TreeFilterSupport, threadsContext->FilterEntry); + if (threadsContext->SearchboxText) PhDereferenceObject(threadsContext->SearchboxText); + PhEmCallObjectOperation(EmThreadsContextType, threadsContext, EmObjectDelete); PhUnregisterCallback( @@ -684,62 +693,45 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( PhDeleteThreadList(&threadsContext->ListContext); PhFree(threadsContext); - - PhpPropPageDlgProcDestroy(hwndDlg); } break; case WM_SHOWWINDOW: { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; + PPH_LAYOUT_ITEM dialogItem; - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, threadsContext->SearchboxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, tnHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; -#define ADD_BL_ITEM(Id) \ - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, Id), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM) + if (GET_WM_COMMAND_HWND(wParam, lParam) != threadsContext->SearchboxHandle) + break; - // Thread details area - { - ULONG id; + newSearchboxText = PH_AUTO(PhGetWindowText(threadsContext->SearchboxHandle)); - for (id = IDC_STATICBL1; id <= IDC_STATICBL11; id++) - ADD_BL_ITEM(id); + if (!PhEqualString(threadsContext->SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhSwapReference(&threadsContext->SearchboxText, newSearchboxText); - // Not in sequence - ADD_BL_ITEM(IDC_STATICBL12); + PhApplyTreeNewFilters(&threadsContext->ListContext.TreeFilterSupport); + } } - - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_STARTMODULE), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_OPENSTARTMODULE), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - ADD_BL_ITEM(IDC_STARTED); - ADD_BL_ITEM(IDC_KERNELTIME); - ADD_BL_ITEM(IDC_USERTIME); - ADD_BL_ITEM(IDC_CONTEXTSWITCHES); - ADD_BL_ITEM(IDC_CYCLES); - ADD_BL_ITEM(IDC_STATE); - ADD_BL_ITEM(IDC_PRIORITY); - ADD_BL_ITEM(IDC_BASEPRIORITY); - ADD_BL_ITEM(IDC_IOPRIORITY); - ADD_BL_ITEM(IDC_PAGEPRIORITY); - ADD_BL_ITEM(IDC_IDEALPROCESSOR); - - PhDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; + break; } - } - break; - case WM_COMMAND: - { - INT id = LOWORD(wParam); - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_SHOWCONTEXTMENU: { @@ -814,35 +806,80 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( } } break; - case ID_THREAD_PERMISSIONS: + case ID_THREAD_CRITICAL: { PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; if (threadItem) { - stdObjectSecurity.OpenObject = PhpThreadPermissionsOpenThread; - stdObjectSecurity.ObjectType = L"Thread"; - stdObjectSecurity.Context = threadItem->ThreadId; + NTSTATUS status; + HANDLE threadHandle; + BOOLEAN breakOnTermination; + + status = PhOpenThread( + &threadHandle, + THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION, + threadItem->ThreadId + ); - if (PhGetAccessEntries(L"Thread", &accessEntries, &numberOfAccessEntries)) + if (NT_SUCCESS(status)) { - PhEditSecurity( - hwndDlg, - PhaFormatString(L"Thread %u", HandleToUlong(threadItem->ThreadId))->Buffer, - PhStdGetObjectSecurity, - PhStdSetObjectSecurity, - &stdObjectSecurity, - accessEntries, - numberOfAccessEntries + status = PhGetThreadBreakOnTermination( + threadHandle, + &breakOnTermination ); - PhFree(accessEntries); + + if (NT_SUCCESS(status)) + { + if (!breakOnTermination && (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + hwndDlg, + L"enable", + L"critical status on the thread", + L"If the process ends, the operating system will shut down immediately.", + TRUE + ))) + { + status = PhSetThreadBreakOnTermination(threadHandle, TRUE); + } + else if (breakOnTermination && (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + hwndDlg, + L"disable", + L"critical status on the thread", + NULL, + FALSE + ))) + { + status = PhSetThreadBreakOnTermination(threadHandle, FALSE); + } + } + + NtClose(threadHandle); + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus(hwndDlg, L"Unable to change the thread critical status.", status, 0); } } } break; + case ID_THREAD_PERMISSIONS: + { + PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); + + if (threadItem) + { + PhEditSecurity( + PhCsForceNoParent ? NULL : hwndDlg, + PhaFormatString(L"Thread %u", HandleToUlong(threadItem->ThreadId))->Buffer, + L"Thread", + PhpThreadPermissionsOpenThread, + NULL, + threadItem->ThreadId + ); + } + } + break; case ID_THREAD_TOKEN: { NTSTATUS status; @@ -853,13 +890,14 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { if (NT_SUCCESS(status = PhOpenThread( &threadHandle, - ThreadQueryAccess, + THREAD_QUERY_LIMITED_INFORMATION, threadItem->ThreadId ))) { PhShowTokenProperties( hwndDlg, PhpOpenThreadTokenObject, + threadsContext->Provider->ProcessId, (PVOID)threadHandle, NULL ); @@ -904,7 +942,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { ULONG threadPriorityWin32; - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_PRIORITY_TIMECRITICAL: threadPriorityWin32 = THREAD_PRIORITY_TIME_CRITICAL; @@ -946,7 +984,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { IO_PRIORITY_HINT ioPriority; - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_IOPRIORITY_VERYLOW: ioPriority = IoPriorityVeryLow; @@ -980,7 +1018,7 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( { ULONG pagePriority; - switch (id) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case ID_PAGEPRIORITY_VERYLOW: pagePriority = MEMORY_PRIORITY_VERY_LOW; @@ -1014,14 +1052,73 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( PhDereferenceObject(text); } break; - case IDC_OPENSTARTMODULE: + //case IDC_OPENSTARTMODULE: + // { + // PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); + // + // if (threadItem && threadItem->StartAddressFileName) + // { + // PhShellExecuteUserString( + // hwndDlg, + // L"FileBrowseExecutable", + // threadItem->StartAddressFileName->Buffer, + // FALSE, + // L"Make sure the Explorer executable file is present." + // ); + // } + // } + // break; + case IDC_OPTIONS: { - PPH_THREAD_ITEM threadItem = PhGetSelectedThreadItem(&threadsContext->ListContext); + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM hideSuspendedMenuItem; + PPH_EMENU_ITEM hideGuiMenuItem; + PPH_EMENU_ITEM highlightSuspendedMenuItem; + PPH_EMENU_ITEM highlightGuiMenuItem; + PPH_EMENU_ITEM selectedItem; + + if (!GetWindowRect(GetDlgItem(hwndDlg, IDC_OPTIONS), &rect)) + break; - if (threadItem && threadItem->StartAddressFileName) + hideSuspendedMenuItem = PhCreateEMenuItem(0, PH_THREAD_TREELIST_MENUITEM_HIDE_SUSPENDED, L"Hide suspended", NULL, NULL); + hideGuiMenuItem = PhCreateEMenuItem(0, PH_THREAD_TREELIST_MENUITEM_HIDE_GUITHREADS, L"Hide gui", NULL, NULL); + highlightSuspendedMenuItem = PhCreateEMenuItem(0, PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_SUSPENDED, L"Highlight suspended", NULL, NULL); + highlightGuiMenuItem = PhCreateEMenuItem(0, PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_GUITHREADS, L"Highlight gui", NULL, NULL); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, hideSuspendedMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, hideGuiMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, highlightSuspendedMenuItem, ULONG_MAX); + PhInsertEMenuItem(menu, highlightGuiMenuItem, ULONG_MAX); + + if (threadsContext->ListContext.HideSuspended) + hideSuspendedMenuItem->Flags |= PH_EMENU_CHECKED; + if (threadsContext->ListContext.HideGuiThreads) + hideGuiMenuItem->Flags |= PH_EMENU_CHECKED; + if (threadsContext->ListContext.HighlightSuspended) + highlightSuspendedMenuItem->Flags |= PH_EMENU_CHECKED; + if (threadsContext->ListContext.HighlightGuiThreads) + highlightGuiMenuItem->Flags |= PH_EMENU_CHECKED; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id) { - PhShellExploreFile(hwndDlg, threadItem->StartAddressFileName->Buffer); + PhSetOptionsThreadList(&threadsContext->ListContext, selectedItem->Id); + PhSaveSettingsThreadList(&threadsContext->ListContext); + PhApplyTreeNewFilters(&threadsContext->ListContext.TreeFilterSupport); } + + PhDestroyEMenu(menu); } break; } @@ -1038,6 +1135,9 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( case PSN_KILLACTIVE: // Can't disable, it screws up the deltas. break; + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)tnHandle); + return TRUE; } } break; @@ -1100,13 +1200,6 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( propPageContext->PropContext->SelectThreadId = NULL; } - - PhpUpdateThreadDetails(hwndDlg, threadsContext, FALSE); - } - break; - case WM_PH_THREAD_SELECTION_CHANGED: - { - PhpUpdateThreadDetails(hwndDlg, threadsContext, TRUE); } break; } diff --git a/ProcessHacker/prpgtok.c b/ProcessHacker/prpgtok.c index 2f8ec1cfe583..3baa10d2733f 100644 --- a/ProcessHacker/prpgtok.c +++ b/ProcessHacker/prpgtok.c @@ -3,6 +3,7 @@ * Process properties: Token page * * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2018 dmex * * This file is part of Process Hacker. * @@ -24,6 +25,8 @@ #include #include +#include + NTSTATUS NTAPI PhpOpenProcessTokenForPage( _Out_ PHANDLE Handle, _In_ ACCESS_MASK DesiredAccess, @@ -35,12 +38,24 @@ NTSTATUS NTAPI PhpOpenProcessTokenForPage( if (!NT_SUCCESS(status = PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, (HANDLE)Context ))) return status; - status = PhOpenProcessToken(processHandle, DesiredAccess, Handle); + if (!NT_SUCCESS(status = PhOpenProcessToken( + processHandle, + DesiredAccess | TOKEN_READ | TOKEN_ADJUST_DEFAULT | READ_CONTROL, // HACK: Add extra access_masks for querying default token. (dmex) + Handle + ))) + { + status = PhOpenProcessToken( + processHandle, + DesiredAccess, + Handle + ); + } + NtClose(processHandle); return status; @@ -57,59 +72,31 @@ INT_PTR CALLBACK PhpProcessTokenHookProc( { case WM_DESTROY: { - RemoveProp(hwndDlg, PhMakeContextAtom()); + if (PhGetWindowContext(hwndDlg, 0xD)) + PhRemoveWindowContext(hwndDlg, 0xD); } break; case WM_SHOWWINDOW: { - if (!GetProp(hwndDlg, PhMakeContextAtom())) // LayoutInitialized + if (!PhGetWindowContext(hwndDlg, 0xD)) // LayoutInitialized { PPH_LAYOUT_ITEM dialogItem; - HWND groupsLv; - HWND privilegesLv; // This is a big violation of abstraction... - dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_USER), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_USERSID), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIRTUALIZED), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_APPCONTAINERSID), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_GROUPS), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PRIVILEGES), - dialogItem, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_INSTRUCTION), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_INTEGRITY), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_ADVANCED), - dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + dialogItem = PhAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_USER), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_USERSID), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_VIRTUALIZED), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_GROUPS), dialogItem, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_DEFAULTPERM), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_PERMISSIONS), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_INTEGRITY), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_ADVANCED), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhDoPropPageLayout(hwndDlg); - groupsLv = GetDlgItem(hwndDlg, IDC_GROUPS); - privilegesLv = GetDlgItem(hwndDlg, IDC_PRIVILEGES); - - if (ListView_GetItemCount(groupsLv) != 0) - { - ListView_SetColumnWidth(groupsLv, 0, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(groupsLv, 1, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - - if (ListView_GetItemCount(privilegesLv) != 0) - { - ListView_SetColumnWidth(privilegesLv, 0, LVSCW_AUTOSIZE); - ListView_SetColumnWidth(privilegesLv, 1, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(privilegesLv, 2, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)TRUE); + PhSetWindowContext(hwndDlg, 0xD, UlongToPtr(TRUE)); } } break; diff --git a/ProcessHacker/prpgwmi.c b/ProcessHacker/prpgwmi.c new file mode 100644 index 000000000000..f4be2fe003ef --- /dev/null +++ b/ProcessHacker/prpgwmi.c @@ -0,0 +1,773 @@ +/* + * Process Hacker - + * Process properties: WMI Providor page + * + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define WM_PH_WMI_UPDATE (WM_APP + 251) + +typedef struct _PH_WMI_CONTEXT +{ + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + HWND WindowHandle; + HWND ListViewHandle; + BOOLEAN Enabled; + PPH_LIST WmiProviderList; +} PH_WMI_CONTEXT, *PPH_WMI_CONTEXT; + +typedef struct _PH_WMI_ENTRY +{ + PPH_STRING ProviderName; + PPH_STRING NamespacePath; + PPH_STRING FileName; + PPH_STRING UserName; +} PH_WMI_ENTRY, *PPH_WMI_ENTRY; + +HRESULT PhpWmiProviderExecMethod( + _In_ PWSTR Method, + _In_ PWSTR ProcessIdString, + _In_ PPH_WMI_ENTRY Entry + ) +{ + HRESULT status; + PPH_STRING queryString = NULL; + IWbemLocator* wbemLocator = NULL; + IWbemServices* wbemServices = NULL; + IEnumWbemClassObject* wbemEnumerator = NULL; + IWbemClassObject *wbemClassObject; + + if (FAILED(status = CoCreateInstance( + &CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + &wbemLocator + ))) + { + goto CleanupExit; + } + + if (FAILED(status = IWbemLocator_ConnectServer( + wbemLocator, + L"root\\CIMV2", + NULL, + NULL, + NULL, + 0, + 0, + NULL, + &wbemServices + ))) + { + goto CleanupExit; + } + + queryString = PhConcatStrings2(L"SELECT Namespace,Provider,User,__PATH FROM Msft_Providers WHERE HostProcessIdentifier = ", ProcessIdString); + + if (FAILED(status = IWbemServices_ExecQuery( + wbemServices, + L"WQL", + queryString->Buffer, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &wbemEnumerator + ))) + { + goto CleanupExit; + } + + while (TRUE) + { + PPH_STRING namespacePath = NULL; + PPH_STRING providerName = NULL; + PPH_STRING userName = NULL; + PPH_STRING instancePath = NULL; + ULONG count = 0; + VARIANT variant; + + if (FAILED(IEnumWbemClassObject_Next(wbemEnumerator, WBEM_INFINITE, 1, &wbemClassObject, &count))) + break; + + if (count == 0) + break; + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"Namespace", 0, &variant, 0, 0))) + { + namespacePath = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"Provider", 0, &variant, 0, 0))) + { + providerName = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"User", 0, &variant, 0, 0))) + { + userName = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"__PATH", 0, &variant, 0, 0))) + { + instancePath = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + if (namespacePath && providerName && userName && instancePath) + { + if ( + PhEqualString(Entry->NamespacePath, namespacePath, FALSE) && + PhEqualString(Entry->ProviderName, providerName, FALSE) && + PhEqualString(Entry->UserName, userName, FALSE) + ) + { + status = IWbemServices_ExecMethod( + wbemServices, + instancePath->Buffer, + Method, + 0, + NULL, + wbemClassObject, + NULL, + NULL + ); + } + } + + if (instancePath) + PhDereferenceObject(instancePath); + if (userName) + PhDereferenceObject(userName); + if (providerName) + PhDereferenceObject(providerName); + if (namespacePath) + PhDereferenceObject(namespacePath); + + IWbemClassObject_Release(wbemClassObject); + } + +CleanupExit: + if (queryString) + PhDereferenceObject(queryString); + if (wbemEnumerator) + IEnumWbemClassObject_Release(wbemEnumerator); + if (wbemServices) + IWbemServices_Release(wbemServices); + if (wbemLocator) + IWbemLocator_Release(wbemLocator); + + return status; +} + +HRESULT PhpQueryWmiProviderFileName( + _In_ PPH_STRING ProviderNameSpace, + _In_ PPH_STRING ProviderName, + _Out_ PPH_STRING *FileName + ) +{ + HRESULT status; + PPH_STRING fileName = NULL; + PPH_STRING queryString = NULL; + PPH_STRING clsidString = NULL; + IWbemLocator* wbemLocator = NULL; + IWbemServices* wbemServices = NULL; + IEnumWbemClassObject* wbemEnumerator = NULL; + IWbemClassObject *wbemClassObject = NULL; + ULONG count = 0; + + if (FAILED(status = CoCreateInstance( + &CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + &wbemLocator + ))) + { + goto CleanupExit; + } + + if (FAILED(status = IWbemLocator_ConnectServer( + wbemLocator, + ProviderNameSpace->Buffer, + NULL, + NULL, + NULL, + 0, + 0, + NULL, + &wbemServices + ))) + { + goto CleanupExit; + } + + queryString = PhFormatString(L"SELECT clsid FROM __Win32Provider WHERE Name = '%s'", ProviderName->Buffer); + + if (FAILED(status = IWbemServices_ExecQuery( + wbemServices, + L"WQL", + queryString->Buffer, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &wbemEnumerator + ))) + { + goto CleanupExit; + } + + if (SUCCEEDED(status = IEnumWbemClassObject_Next( + wbemEnumerator, + WBEM_INFINITE, + 1, + &wbemClassObject, + &count + ))) + { + VARIANT variant; + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"CLSID", 0, &variant, 0, 0))) + { + if (variant.bstrVal) // returns NULL for some host processes (dmex) + { + clsidString = PhCreateString(variant.bstrVal); + } + + VariantClear(&variant); + } + + IWbemClassObject_Release(wbemClassObject); + } + + // Lookup the GUID in the registry to determine the name and file name. + + if (!PhIsNullOrEmptyString(clsidString)) + { + HANDLE keyHandle; + PPH_STRING keyPath; + + // Note: String pooling optimization. + keyPath = PhConcatStrings( + 4, + L"CLSID\\", + clsidString->Buffer, + L"\\", + L"InprocServer32" + ); + + if (SUCCEEDED(status = HRESULT_FROM_NT(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CLASSES_ROOT, + &keyPath->sr, + 0 + )))) + { + if (fileName = PhQueryRegistryString(keyHandle, NULL)) + { + PPH_STRING expandedString; + + if (expandedString = PhExpandEnvironmentStrings(&fileName->sr)) + { + PhMoveReference(&fileName, expandedString); + } + } + + NtClose(keyHandle); + } + + PhDereferenceObject(keyPath); + } + +CleanupExit: + if (clsidString) + PhDereferenceObject(clsidString); + if (queryString) + PhDereferenceObject(queryString); + if (wbemEnumerator) + IEnumWbemClassObject_Release(wbemEnumerator); + if (wbemServices) + IWbemServices_Release(wbemServices); + if (wbemLocator) + IWbemLocator_Release(wbemLocator); + + if (SUCCEEDED(status)) + *FileName = fileName; + + return status; +} + +PPH_LIST PhpQueryWmiProviderHostProcess( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + HRESULT status; + PPH_LIST providerList; + PPH_STRING queryString = NULL; + IWbemLocator* wbemLocator = NULL; + IWbemServices* wbemServices = NULL; + IEnumWbemClassObject* wbemEnumerator = NULL; + IWbemClassObject *wbemClassObject; + + if (FAILED(status = CoCreateInstance( + &CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + &wbemLocator + ))) + { + goto CleanupExit; + } + + if (FAILED(status = IWbemLocator_ConnectServer( + wbemLocator, + L"root\\CIMV2", + NULL, + NULL, + NULL, + 0, + 0, + NULL, + &wbemServices + ))) + { + goto CleanupExit; + } + + queryString = PhConcatStrings2(L"SELECT Namespace,Provider,User FROM Msft_Providers WHERE HostProcessIdentifier = ", ProcessItem->ProcessIdString); + + if (FAILED(status = IWbemServices_ExecQuery( + wbemServices, + L"WQL", + queryString->Buffer, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &wbemEnumerator + ))) + { + goto CleanupExit; + } + + providerList = PhCreateList(1); + + while (TRUE) + { + ULONG count = 0; + VARIANT variant; + PPH_WMI_ENTRY entry; + + if (FAILED(IEnumWbemClassObject_Next(wbemEnumerator, WBEM_INFINITE, 1, &wbemClassObject, &count))) + break; + + if (count == 0) + break; + + entry = PhAllocate(sizeof(PH_WMI_ENTRY)); + memset(entry, 0, sizeof(PH_WMI_ENTRY)); + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"Namespace", 0, &variant, 0, 0))) + { + entry->NamespacePath = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"Provider", 0, &variant, 0, 0))) + { + entry->ProviderName = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + if (SUCCEEDED(IWbemClassObject_Get(wbemClassObject, L"User", 0, &variant, 0, 0))) + { + entry->UserName = PhCreateString(variant.bstrVal); + VariantClear(&variant); + } + + IWbemClassObject_Release(wbemClassObject); + + if (entry->NamespacePath && entry->ProviderName) + { + PPH_STRING fileName; + + if (SUCCEEDED(PhpQueryWmiProviderFileName(entry->NamespacePath, entry->ProviderName, &fileName))) + { + entry->FileName = fileName; + } + } + + PhAddItemList(providerList, entry); + } + +CleanupExit: + if (queryString) + PhDereferenceObject(queryString); + if (wbemEnumerator) + IEnumWbemClassObject_Release(wbemEnumerator); + if (wbemServices) + IWbemServices_Release(wbemServices); + if (wbemLocator) + IWbemLocator_Release(wbemLocator); + + return providerList; +} + +// HACK: Move to itemtips.c +VOID PhQueryWmiHostProcessString( + _In_ PPH_PROCESS_ITEM ProcessItem, + _Inout_ PPH_STRING_BUILDER Providers + ) +{ + PPH_LIST providerList; + + if (providerList = PhpQueryWmiProviderHostProcess(ProcessItem)) + { + for (ULONG i = 0; i < providerList->Count; i++) + { + PPH_WMI_ENTRY entry = providerList->Items[i]; + + PhAppendFormatStringBuilder( + Providers, + L" %s (%s)\n", + PhGetStringOrEmpty(entry->ProviderName), + PhGetStringOrEmpty(entry->FileName) + ); + + if (entry->NamespacePath) + PhDereferenceObject(entry->NamespacePath); + if (entry->ProviderName) + PhDereferenceObject(entry->ProviderName); + if (entry->FileName) + PhDereferenceObject(entry->FileName); + if (entry->UserName) + PhDereferenceObject(entry->UserName); + + PhFree(entry); + } + + PhDereferenceObject(providerList); + } +} + +static VOID NTAPI PhpWmiProviderUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_WMI_CONTEXT context = (PPH_WMI_CONTEXT)Context; + + PostMessage(context->WindowHandle, WM_PH_WMI_UPDATE, 0, 0); +} + +VOID PhpClearWmiProviderItems( + _Inout_ PPH_WMI_CONTEXT Context + ) +{ + ULONG i; + PPH_WMI_ENTRY entry; + + for (i = 0; i < Context->WmiProviderList->Count; i++) + { + entry = Context->WmiProviderList->Items[i]; + + if (entry->NamespacePath) + PhDereferenceObject(entry->NamespacePath); + if (entry->ProviderName) + PhDereferenceObject(entry->ProviderName); + if (entry->FileName) + PhDereferenceObject(entry->FileName); + if (entry->UserName) + PhDereferenceObject(entry->UserName); + + PhFree(entry); + } + + PhClearList(Context->WmiProviderList); +} + +VOID PhpRefreshWmiProviders( + _In_ HWND hwndDlg, + _Inout_ PPH_WMI_CONTEXT Context, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PVOID selectedIndex; + PPH_LIST providerList; + + ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); + + selectedIndex = PhGetSelectedListViewItemParam(Context->ListViewHandle); + ListView_DeleteAllItems(Context->ListViewHandle); + PhpClearWmiProviderItems(Context); + + if (providerList = PhpQueryWmiProviderHostProcess(ProcessItem)) + { + for (ULONG i = 0; i < providerList->Count; i++) + { + PPH_WMI_ENTRY entry = providerList->Items[i]; + INT lvItemIndex; + + lvItemIndex = PhAddListViewItem( + Context->ListViewHandle, + MAXINT, + PhGetStringOrEmpty(entry->ProviderName), + UlongToPtr(Context->WmiProviderList->Count + 1) + ); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, PhGetStringOrEmpty(entry->NamespacePath)); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, PhGetStringOrEmpty(entry->FileName)); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 3, PhGetStringOrEmpty(entry->UserName)); + + PhAddItemList(Context->WmiProviderList, entry); + } + + PhDereferenceObject(providerList); + } + + if (selectedIndex) + { + ListView_SetItemState(Context->ListViewHandle, PtrToUlong(selectedIndex) - 1, + LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); + ListView_EnsureVisible(Context->ListViewHandle, PtrToUlong(selectedIndex) - 1, FALSE); + } + + ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); +} + +INT_PTR CALLBACK PhpProcessWmiProvidersDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PPH_WMI_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = (PPH_WMI_CONTEXT)propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + context = propPageContext->Context = PhAllocateZero(sizeof(PH_WMI_CONTEXT)); + context->WindowHandle = hwndDlg; + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->Enabled = TRUE; + context->WmiProviderList = PhCreateList(1); + + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Provider"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 180, L"Namespace"); + PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 260, L"File name"); + PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"User"); + PhSetExtendedListView(context->ListViewHandle); + PhLoadListViewColumnsFromSetting(L"WmiProviderListViewColumns", context->ListViewHandle); + + PhpRefreshWmiProviders(hwndDlg, context, processItem); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + PhpWmiProviderUpdateHandler, + context, + &context->ProcessesUpdatedRegistration + ); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &context->ProcessesUpdatedRegistration); + + PhSaveListViewColumnsToSetting(L"WmiProviderListViewColumns", context->ListViewHandle); + + PhpClearWmiProviderItems(context); + PhDereferenceObject(context->WmiProviderList); + + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + PPH_LAYOUT_ITEM dialogItem; + + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, context->ListViewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_SETACTIVE: + context->Enabled = TRUE; + break; + case PSN_KILLACTIVE: + context->Enabled = FALSE; + break; + } + + PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PVOID index; + PPH_EMENU_ITEM selectedItem; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + if (index = PhGetSelectedListViewItemParam(context->ListViewHandle)) + { + PPH_EMENU menu; + + menu = PhCreateEMenu(); + if (PhGetIntegerSetting(L"WmiProviderEnableHiddenMenu")) + { + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"&Suspend", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 2, L"Res&ume", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 3, L"Un&load", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), -1); + } + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 4, L"Open &file location", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), -1); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + PhDestroyEMenu(menu); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + PPH_WMI_ENTRY entry; + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(selectedItem); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (handled) + break; + + entry = context->WmiProviderList->Items[PtrToUlong(index) - 1]; + + switch (selectedItem->Id) + { + case 1: + PhpWmiProviderExecMethod(L"Suspend", processItem->ProcessIdString, entry); + break; + case 2: + PhpWmiProviderExecMethod(L"Resume", processItem->ProcessIdString, entry); + break; + case 3: + PhpWmiProviderExecMethod(L"Unload", processItem->ProcessIdString, entry); + break; + case 4: + { + if (!PhIsNullOrEmptyString(entry->FileName) && PhDoesFileExistsWin32(entry->FileName->Buffer)) + { + PhShellExecuteUserString( + hwndDlg, + L"FileBrowseExecutable", + processItem->FileName->Buffer, + FALSE, + L"Make sure the Explorer executable file is present." + ); + } + } + break; + case 5: + { + PPH_STRING string; + + string = PhFormatString( + L"%s, %s, %s, %s", + PhGetStringOrDefault(entry->ProviderName, L"N/A"), + PhGetStringOrDefault(entry->NamespacePath, L"N/A"), + PhGetStringOrDefault(entry->FileName, L"N/A"), + PhGetStringOrDefault(entry->UserName, L"N/A") + ); + + PhSetClipboardString(hwndDlg, &string->sr); + PhDereferenceObject(string); + + PhSetDialogFocus(hwndDlg, context->ListViewHandle); + } + break; + case IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + } + } + break; + case WM_PH_WMI_UPDATE: + { + PhpRefreshWmiProviders(hwndDlg, context, processItem); + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/resource.h b/ProcessHacker/resource.h index cd3680fce5b1..45f750f01572 100644 --- a/ProcessHacker/resource.h +++ b/ProcessHacker/resource.h @@ -1,746 +1,768 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by ProcessHacker.rc -// -#define IDR_RT_MANIFEST 1 -#define IDI_PROCESSHACKER 101 -#define IDR_MAINWND 102 -#define IDR_MAINWND_ACCEL 102 -#define IDD_PROCGENERAL 103 -#define IDD_PROCMODULES 104 -#define IDD_PROCTHREADS 105 -#define IDD_PROCHANDLES 106 -#define IDD_PROCENVIRONMENT 107 -#define IDD_THRDSTACK 108 -#define IDR_THREAD 110 -#define IDR_HANDLE 111 -#define IDR_MODULE 112 -#define IDC_CPU 112 -#define IDC_PRIVATEBYTES 113 -#define IDC_IO 114 -#define IDB_CROSS 117 -#define IDB_TICK 118 -#define IDC_PHYSICAL 119 -#define IDR_COMPUTER 120 -#define IDC_MEMORY 120 -#define IDD_ABOUT 121 -#define IDC_NEWOBJECTS 121 -#define IDC_REMOVEDOBJECTS 122 -#define IDR_PROCESS 123 -#define IDC_CPUUSER 123 -#define IDR_SERVICE 124 -#define IDC_CPUKERNEL 124 -#define IDC_IORO 125 -#define IDD_SRVLIST 125 -#define IDD_SRVGENERAL 126 -#define IDC_IOW 126 -#define IDD_HNDLGENERAL 128 -#define IDD_INFORMATION 129 -#define IDD_FINDOBJECTS 130 -#define IDD_OBJTOKEN 131 -#define ID_PLUGIN_MENU_ITEM 131 -#define ID_SHOWCONTEXTMENU 132 -#define IDR_PRIVILEGE 133 -#define IDC_SEPARATOR 133 -#define IDR_FINDOBJ 134 -#define IDC_COMMIT 134 -#define IDD_HIDDENPROCESSES 135 -#define ID_TRAYICONS_REGISTERED 135 -#define IDD_RUNAS 136 -#define ID_COPY_CELL 136 -#define IDD_PROGRESS 137 -#define IDD_PAGEFILES 138 -#define IDD_TOKGENERAL 139 -#define IDD_TOKADVANCED 140 -#define IDD_OBJJOB 141 -#define IDD_OBJEVENT 142 -#define IDD_OBJMUTANT 143 -#define IDD_OBJSEMAPHORE 144 -#define IDD_OBJTIMER 145 -#define IDD_JOBSTATISTICS 146 -#define IDD_OBJEVENTPAIR 147 -#define IDD_OBJSECTION 148 -#define IDD_AFFINITY 149 -#define IDD_SYSINFO 150 -#define IDR_USER 151 -#define IDD_EDITMESSAGE 152 -#define IDD_SESSION 153 -#define IDD_PROCMEMORY 154 -#define IDD_CHOOSE 155 -#define IDD_OPTGENERAL 162 -#define IDD_OPTHIGHLIGHTING 163 -#define IDR_NETWORK 164 -#define IDD_CHOOSECOLUMNS 166 -#define IDD_NETSTACK 167 -#define IDD_CREATESERVICE 168 -#define IDD_PROCPERFORMANCE 169 -#define IDD_PROCSTATISTICS 170 -#define IDD_OPTADVANCED 171 -#define IDR_ICON 173 -#define IDD_GDIHANDLES 175 -#define IDD_LOG 178 -#define IDD_OPTSYMBOLS 179 -#define IDD_MEMEDIT 180 -#define IDR_MEMORY 181 -#define IDD_MEMPROTECT 182 -#define IDD_MEMRESULTS 183 -#define IDR_MEMFILTER 184 -#define IDD_MEMSTRING 185 -#define IDD_OPTGRAPHS 186 -#define IDD_PLUGINS 187 -#define IDD_HANDLESTATS 188 -#define IDD_PROCRECORD 189 -#define IDD_CHOOSEPROCESS 190 -#define IDD_PROCSERVICES 191 -#define IDI_PHAPPLICATION 191 -#define IDI_COG 192 -#define IDD_SHADOWSESSION 193 -#define IDI_PHAPPLICATIONGO 194 -#define IDD_TOKCAPABILITIES 194 -#define IDI_COGGO 195 -#define IDD_TOKATTRIBUTES 195 -#define IDD_SYSINFO_CPU 196 -#define IDD_SYSINFO_CPUPANEL 197 -#define IDD_SYSINFO_MEMPANEL 198 -#define IDR_SYSINFO_ACCEL 198 -#define IDD_SYSINFO_MEM 199 -#define IDD_SYSINFO_IO 200 -#define IDD_SYSINFO_IOPANEL 201 -#define IDD_MEMLISTS 202 -#define IDR_EMPTYMEMLISTS 204 -#define IDD_CONTAINER 205 -#define IDD_SYSINFO_MEMPANELXP 206 -#define IDD_MINIINFO 207 -#define IDD_MINIINFO_LIST 210 -#define IDR_MINIINFO 211 -#define IDR_MINIINFO_PROCESS 212 -#define IDR_ENVIRONMENT 214 -#define IDD_MITIGATION 215 -#define IDI_PIN 216 -#define IDI_FOLDER 217 -#define IDI_PENCIL 218 -#define IDI_MAGNIFIER 219 -#define IDD_EDITENV 221 -#define IDC_TERMINATE 1003 -#define IDC_FILEICON 1005 -#define IDC_FILE 1006 -#define IDC_PROCESS 1007 -#define IDC_COMPANYNAME 1009 -#define IDC_VERSION 1010 -#define IDC_REFRESH 1015 -#define IDC_PERMISSIONS 1018 -#define IDC_FILENAME 1020 -#define IDC_CMDLINE 1021 -#define IDC_URL 1021 -#define IDC_RUNSELECTED 1021 -#define IDC_CURDIR 1022 -#define IDC_STARTED 1023 -#define IDC_ABOUT_NAME 1024 -#define IDC_PEBADDRESS 1024 -#define IDC_TERMINATED 1024 -#define IDC_PARENTPROCESS 1025 -#define IDC_MITIGATION 1026 -#define IDC_PAUSE 1027 -#define IDC_PROTECTION 1027 -#define IDC_START 1028 -#define IDC_DESCRIPTION 1029 -#define IDC_TYPE 1032 -#define IDC_STARTTYPE 1033 -#define IDC_ADDRESS 1033 -#define IDC_IMPERSONATIONLEVEL 1033 -#define IDC_ERRORCONTROL 1034 -#define IDC_GRANTED_ACCESS 1034 -#define IDC_TOKENLUID 1034 -#define IDC_GROUP 1035 -#define IDC_AUTHENTICATIONLUID 1035 -#define IDC_BINARYPATH 1036 -#define IDC_MEMORYUSED 1036 -#define IDC_USERACCOUNT 1037 -#define IDC_MEMORYAVAILABLE 1037 -#define IDC_PASSWORD 1040 -#define IDC_PASSWORDCHECK 1041 -#define IDC_SERVICEDLL 1042 -#define IDC_NAME 1044 -#define IDC_REFERENCES 1045 -#define IDC_INTERNALNAME 1045 -#define IDC_HANDLES 1046 -#define IDC_PROCESSTYPELABEL 1046 -#define IDC_AUTHOR 1046 -#define IDC_PAGED 1047 -#define IDC_PROCESSTYPETEXT 1047 -#define IDC_NONPAGED 1048 -#define IDC_TEXT 1048 -#define IDC_DIAGNOSTICS 1049 -#define IDC_FILTER 1050 -#define IDC_RESULTS 1051 -#define IDC_USER 1052 -#define IDC_USERSID 1053 -#define IDC_ADVANCED 1054 -#define IDC_OWNER 1054 -#define IDC_GROUPS 1055 -#define IDC_PRIMARYGROUP 1055 -#define IDC_PRIVILEGES 1056 -#define IDC_VIRTUALIZATION 1056 -#define IDC_SESSIONID 1057 -#define IDC_ELEVATED 1058 -#define IDC_VIRTUALIZED 1059 -#define IDC_SOURCENAME 1059 -#define IDC_PROPERTIES 1060 -#define IDC_SOURCELUID 1060 -#define IDC_APPCONTAINERSID 1060 -#define IDC_LIST 1061 -#define IDC_PROCESSES 1063 -#define IDC_SCAN 1064 -#define IDC_SAVE 1065 -#define IDC_METHOD 1067 -#define IDC_INTRO 1068 -#define IDC_PROGRAM 1069 -#define IDC_BROWSE 1070 -#define IDC_USERNAME 1071 -#define IDC_SESSIONS 1074 -#define IDC_DESKTOPS 1075 -#define IDC_PROGRESS 1076 -#define IDC_PROGRESSTEXT 1077 -#define IDC_LINKEDTOKEN 1079 -#define IDC_DISABLEALL 1079 -#define IDC_MOVEUP 1079 -#define IDC_CHANGE 1079 -#define IDC_CLEAR 1079 -#define IDC_GOTO 1079 -#define IDC_OPTIONS 1079 -#define IDC_DETAILS 1079 -#define IDC_ADD 1079 -#define IDC_FONT 1079 -#define IDC_INTEGRITY 1079 -#define IDC_MORE 1079 -#define IDC_VIEWMITIGATION 1080 -#define IDC_VIEWPARENTPROCESS 1081 -#define IDC_OPENFILENAME 1082 -#define IDC_LIMITS 1083 -#define IDC_SIGNALED 1084 -#define IDC_SET 1085 -#define IDC_RESET 1086 -#define IDC_PULSE 1087 -#define IDC_COUNT 1088 -#define IDC_ABANDONED 1089 -#define IDC_CURRENTCOUNT 1090 -#define IDC_MAXIMUMCOUNT 1091 -#define IDC_ACQUIRE 1092 -#define IDC_RELEASE 1093 -#define IDC_CANCEL 1094 -#define IDC_OWNERLABEL 1095 -#define IDC_SETLOW 1096 -#define IDC_SETHIGH 1097 -#define IDC_SIZE_ 1098 -#define IDC_CPU0 1099 -#define IDC_CPU1 1100 -#define IDC_CPU2 1101 -#define IDC_CPU3 1102 -#define IDC_CPU4 1103 -#define IDC_CPU5 1104 -#define IDC_CPU6 1105 -#define IDC_CPU7 1106 -#define IDC_CPU8 1107 -#define IDC_TITLE 1107 -#define IDC_CPU9 1108 -#define IDC_CPU10 1109 -#define IDC_TIMEOUT 1109 -#define IDC_CPU11 1110 -#define IDC_CPU12 1111 -#define IDC_STATE 1111 -#define IDC_CPU13 1112 -#define IDC_CLIENTNAME 1112 -#define IDC_CPU14 1113 -#define IDC_CLIENTADDRESS 1113 -#define IDC_CPU15 1114 -#define IDC_CLIENTDISPLAY 1114 -#define IDC_CPU16 1115 -#define IDC_CHOICE 1115 -#define IDC_CPU17 1116 -#define IDC_OPTION 1116 -#define IDC_CPU18 1117 -#define IDC_CHOICEUSER 1117 -#define IDC_CPU19 1118 -#define IDC_HIDEUNNAMEDHANDLES 1118 -#define IDC_CPU20 1119 -#define IDC_CPU21 1120 -#define IDC_STARTMODULE 1120 -#define IDC_CPU22 1121 -#define IDC_OPENSTARTMODULE 1121 -#define IDC_CPU23 1122 -#define IDC_KERNELTIME 1122 -#define IDC_CPU24 1123 -#define IDC_USERTIME 1123 -#define IDC_CPU25 1124 -#define IDC_CONTEXTSWITCHES 1124 -#define IDC_CPU26 1125 -#define IDC_CYCLES 1125 -#define IDC_CPU27 1126 -#define IDC_PRIORITY 1126 -#define IDC_CPU28 1127 -#define IDC_BASEPRIORITY 1127 -#define IDC_CPU29 1128 -#define IDC_IOPRIORITY 1128 -#define IDC_CPU30 1129 -#define IDC_PAGEPRIORITY 1129 -#define IDC_CPU31 1130 -#define IDC_STATICBL1 1130 -#define IDC_STATICBL2 1131 -#define IDC_CPU32 1131 -#define IDC_STATICBL3 1132 -#define IDC_CPU33 1132 -#define IDC_STATICBL4 1133 -#define IDC_CPU34 1133 -#define IDC_STATICBL5 1134 -#define IDC_CPU35 1134 -#define IDC_STATICBL6 1135 -#define IDC_CPU36 1135 -#define IDC_STATICBL7 1136 -#define IDC_CPU37 1136 -#define IDC_STATICBL8 1137 -#define IDC_CPU38 1137 -#define IDC_STATICBL9 1138 -#define IDC_CPU39 1138 -#define IDC_STATICBL10 1139 -#define IDC_CPU40 1139 -#define IDC_STATICBL11 1140 -#define IDC_CPU41 1140 -#define IDC_CHOICESIMPLE 1141 -#define IDC_CPU42 1141 -#define IDC_MESSAGE 1142 -#define IDC_CPU43 1142 -#define IDC_SEARCHENGINE 1143 -#define IDC_CPU44 1143 -#define IDC_MAXSIZEUNIT 1144 -#define IDC_CPU45 1144 -#define IDC_ALLOWONLYONEINSTANCE 1145 -#define IDC_CPU46 1145 -#define IDC_HIDEONCLOSE 1146 -#define IDC_CPU47 1146 -#define IDC_ENABLEWARNINGS 1147 -#define IDC_HIDEONMINIMIZE 1147 -#define IDC_CPU48 1147 -#define IDC_STARTHIDDEN 1148 -#define IDC_CPU49 1148 -#define IDC_ENABLEKERNELMODEDRIVER 1149 -#define IDC_CPU50 1149 -#define IDC_CPU51 1150 -#define IDC_PEVIEWER 1151 -#define IDC_CPU52 1151 -#define IDC_CPU53 1152 -#define IDC_CPU54 1153 -#define IDC_CPU55 1154 -#define IDC_HIGHLIGHTINGDURATION 1155 -#define IDC_CPU56 1155 -#define IDC_CPU57 1156 -#define IDC_ENABLEALL 1157 -#define IDC_CPU58 1157 -#define IDC_ZACTIVEPROCESSES_V 1158 -#define IDC_CPU59 1158 -#define IDC_ZTOTALPROCESSES_V 1159 -#define IDC_CPU60 1159 -#define IDC_ZTERMINATEDPROCESSES_V 1160 -#define IDC_CPU61 1160 -#define IDC_ZUSERTIME_V 1161 -#define IDC_CPU62 1161 -#define IDC_ZKERNELTIME_V 1162 -#define IDC_CPU63 1162 -#define IDC_ZUSERTIMEPERIOD_V 1163 -#define IDC_ZKERNELTIMEPERIOD_V 1164 -#define IDC_ZPAGEFAULTS_V 1165 -#define IDC_ZPEAKPROCESSUSAGE_V 1166 -#define IDC_ZPEAKJOBUSAGE_V 1167 -#define IDC_ZIOREADS_V 1168 -#define IDC_ZIOREADBYTES_V 1169 -#define IDC_ZIOWRITES_V 1170 -#define IDC_ZIOWRITEBYTES_V 1171 -#define IDC_ZIOOTHER_V 1172 -#define IDC_ZIOOTHERBYTES_V 1173 -#define IDC_MOVEDOWN 1176 -#define IDC_DISPLAYNAME 1179 -#define IDC_ZPRIORITY_V 1181 -#define IDC_ZCYCLES_V 1182 -#define IDC_ZTOTALTIME_V 1185 -#define IDC_ZPRIVATEBYTES_V 1186 -#define IDC_ZWORKINGSET_V 1187 -#define IDC_ZPEAKWORKINGSET_V 1188 -#define IDC_ZVIRTUALSIZE_V 1189 -#define IDC_ZPEAKVIRTUALSIZE_V 1190 -#define IDC_ZPEAKPRIVATEBYTES_V 1191 -#define IDC_ZPAGEPRIORITY_V 1192 -#define IDC_ZIOPRIORITY_V 1194 -#define IDC_ZHANDLES_V 1195 -#define IDC_ZGDIHANDLES_V 1196 -#define IDC_ZOTHERDELTA_V 1196 -#define IDC_ZUSERHANDLES_V 1197 -#define IDC_ZOTHER_V 1197 -#define IDC_GROUPCPU 1198 -#define IDC_GROUPPRIVATEBYTES 1199 -#define IDC_GROUPIO 1200 -#define IDC_ONEGRAPHPERCPU 1202 -#define IDC_ALWAYSONTOP 1203 -#define IDC_COPY 1206 -#define IDC_INACTIVE 1207 -#define IDC_ACTIVE 1208 -#define IDC_SHOW 1209 -#define IDC_HIDE 1210 -#define IDC_REPLACETASKMANAGER 1211 -#define IDC_AUTOSCROLL 1215 -#define IDC_UNDECORATESYMBOLS 1216 -#define IDC_DBGHELPPATH 1217 -#define IDC_DBGHELPSEARCHPATH 1218 -#define IDC_COLLAPSESERVICES 1219 -#define IDC_ICONSINGLECLICK 1220 -#define IDC_DESKTOP 1221 -#define IDC_REREAD 1224 -#define IDC_WRITE 1225 -#define IDC_VALUE 1230 -#define IDC_DELAYEDSTART 1234 -#define IDC_MINIMUMLENGTH 1236 -#define IDC_DETECTUNICODE 1237 -#define IDC_PRIVATE 1238 -#define IDC_IMAGE 1239 -#define IDC_MAPPED 1240 -#define IDC_STRINGS 1242 -#define IDC_SHOWTEXT 1245 -#define IDC_ICONPROCESSES 1248 -#define IDC_CLEANUP 1251 -#define IDC_ENABLESTAGE2 1253 -#define IDC_TOGGLEELEVATION 1254 -#define IDC_ENABLEPLUGINS 1258 -#define IDC_PARENT 1263 -#define IDC_PROCESSNAME 1264 -#define IDC_SERVICES_LAYOUT 1266 -#define IDC_LINK_SF 1267 -#define IDC_CREDITS 1270 -#define IDC_ENABLENETWORKRESOLVE 1271 -#define IDC_LOGONTIME 1272 -#define IDC_ZPEAKHANDLES_V 1273 -#define IDC_PROPAGATECPUUSAGE 1274 -#define IDC_SHIFT 1274 -#define IDC_USEOLDCOLORS 1274 -#define IDC_ICONTOGGLESVISIBILITY 1274 -#define IDC_OPENURL 1279 -#define IDC_COMPANYNAME_LINK 1279 -#define IDC_SAMPLECOUNT 1280 -#define IDC_SAMPLECOUNTLABEL 1281 -#define IDC_DISABLE 1282 -#define IDC_VIRTUALKEY 1283 -#define IDC_CTRL 1284 -#define IDC_ALT 1285 -#define IDC_CONNECTTIME 1286 -#define IDC_DISCONNECTTIME 1287 -#define IDC_LASTINPUTTIME 1288 -#define IDC_ZPRIVATEWS_V 1289 -#define IDC_ZSHAREABLEWS_V 1290 -#define IDC_ZSHAREDWS_V 1291 -#define IDC_ENABLEINSTANTTOOLTIPS 1292 -#define IDC_ENABLECYCLECPUUSAGE 1293 -#define IDC_IDEALPROCESSOR 1294 -#define IDC_STATICBL12 1295 -#define IDC_BASICINFORMATION 1296 -#define IDC_INSTRUCTION 1298 -#define IDC_CPUNAME 1299 -#define IDC_LAYOUT 1300 -#define IDC_UTILIZATION 1301 -#define IDC_SPEED 1302 -#define IDC_ZPROCESSES_V 1303 -#define IDC_ZTHREADS_V 1304 -#define IDC_ZCONTEXTSWITCHESDELTA_V 1307 -#define IDC_ZINTERRUPTSDELTA_V 1308 -#define IDC_ZDPCSDELTA_V 1309 -#define IDC_ZSYSTEMCALLSDELTA_V 1310 -#define IDC_GRAPH_LAYOUT 1311 -#define IDC_TOTALPHYSICAL 1312 -#define IDC_ZCOMMITCURRENT_V 1314 -#define IDC_ZCOMMITPEAK_V 1315 -#define IDC_ZCOMMITLIMIT_V 1316 -#define IDC_ZPHYSICALCURRENT_V 1317 -#define IDC_ZPHYSICALTOTAL_V 1318 -#define IDC_ZPHYSICALCACHEWS_V 1319 -#define IDC_ZPHYSICALKERNELWS_V 1320 -#define IDC_ZPHYSICALDRIVERWS_V 1321 -#define IDC_ZPAGEDWORKINGSET_V 1322 -#define IDC_ZPAGEDVIRTUALSIZE_V 1323 -#define IDC_ZPAGEDLIMIT_V 1324 -#define IDC_ZPAGEDALLOCSDELTA_V 1325 -#define IDC_ZPAGEDFREESDELTA_V 1326 -#define IDC_ZNONPAGEDUSAGE_V 1327 -#define IDC_ZNONPAGEDLIMIT_V 1328 -#define IDC_ZNONPAGEDALLOCSDELTA_V 1329 -#define IDC_ZNONPAGEDFREESDELTA_V 1330 -#define IDC_ZPHYSICALRESERVED_V 1331 -#define IDC_ZLISTZEROED_V 1332 -#define IDC_ZLISTFREE_V 1333 -#define IDC_ZLISTMODIFIED_V 1334 -#define IDC_ZLISTMODIFIEDNOWRITE_V 1335 -#define IDC_ZLISTSTANDBY_V 1337 -#define IDC_COMMIT_L 1339 -#define IDC_PHYSICAL_L 1340 -#define IDC_ZUPTIME_V 1341 -#define IDC_ZOTHERBYTESDELTA_V 1342 -#define IDC_ZREADSDELTA_V 1343 -#define IDC_ZREADBYTESDELTA_V 1344 -#define IDC_ZWRITESDELTA_V 1345 -#define IDC_ZLISTSTANDBY0_V 1346 -#define IDC_ZWRITEBYTESDELTA_V 1346 -#define IDC_ZLISTSTANDBY1_V 1347 -#define IDC_ZREADS_V 1347 -#define IDC_ZLISTSTANDBY2_V 1348 -#define IDC_ZREADBYTES_V 1348 -#define IDC_ZLISTSTANDBY3_V 1349 -#define IDC_ZWRITES_V 1349 -#define IDC_ZLISTBAD_V 1350 -#define IDC_ZWRITEBYTES_V 1350 -#define IDC_ZLISTREPURPOSED_V 1351 -#define IDC_ZOTHERBYTES_V 1351 -#define IDC_ZLISTREPURPOSED0_V 1352 -#define IDC_ZLISTREPURPOSED1_V 1353 -#define IDC_ZLISTREPURPOSED2_V 1354 -#define IDC_ZLISTREPURPOSED3_V 1355 -#define IDC_ZLISTREPURPOSED4_V 1356 -#define IDC_ZLISTREPURPOSED5_V 1357 -#define IDC_ZLISTREPURPOSED6_V 1358 -#define IDC_ZLISTREPURPOSED7_V 1359 -#define IDC_EMPTY 1360 -#define IDC_SAMPLECOUNTAUTOMATIC 1361 -#define IDC_SHOWCOMMITINSUMMARY 1361 -#define IDC_ZLISTSTANDBY4_V 1364 -#define IDC_ZLISTSTANDBY5_V 1365 -#define IDC_SELECTALL 1365 -#define IDC_ZLISTSTANDBY6_V 1366 -#define IDC_DESELECTALL 1366 -#define IDC_ZLISTSTANDBY7_V 1367 -#define IDC_INSPECT 1367 -#define IDC_ZPAGINGPAGEFAULTSDELTA_V 1368 -#define IDC_HIDEFREEREGIONS 1368 -#define IDC_ZPAGINGPAGEREADSDELTA_V 1369 -#define IDC_BYTESPERROW 1369 -#define IDC_ZPAGINGPAGEFILEWRITESDELTA_V 1370 -#define IDC_PIN 1370 -#define IDC_ZPAGINGMAPPEDWRITESDELTA_V 1371 -#define IDC_ZLISTMODIFIEDPAGEFILE_V 1373 -#define IDC_SECTION 1375 -#define IDC_REGEX 1377 -#define IDC_DESCRIPTIONLABEL 1378 -#define IDC_STARTATLOGON 1380 -#define IDC_VIEWCOMMANDLINE 1381 -#define IDC_DELETE 1382 -#define IDC_EDIT 1383 -#define IDC_NEW 1384 -#define ID_MAINWND_PROCESSTL 2001 -#define ID_MAINWND_SERVICETL 2002 -#define ID_MAINWND_NETWORKTL 2003 -#define ID_MAINWND_PROCESSLV 2004 -#define ID_HACKER_EXIT 40001 -#define ID_PROCESS_PROPERTIES 40006 -#define ID_PROCESS_TERMINATE 40007 -#define ID_PROCESS_SUSPEND 40008 -#define ID_PROCESS_RESUME 40009 -#define ID_HACKER_SAVE 40010 -#define ID_THREAD_TERMINATE 40011 -#define ID_THREAD_SUSPEND 40012 -#define ID_THREAD_RESUME 40013 -#define ID_THREAD_PERMISSIONS 40015 -#define ID_THREAD_TOKEN 40016 -#define ID_ANALYZE_WAIT 40018 -#define ID_PRIORITY_TIMECRITICAL 40020 -#define ID_PRIORITY_HIGHEST 40021 -#define ID_PRIORITY_ABOVENORMAL 40022 -#define ID_PRIORITY_NORMAL 40023 -#define ID_PRIORITY_BELOWNORMAL 40024 -#define ID_PRIORITY_LOWEST 40025 -#define ID_PRIORITY_IDLE 40026 -#define ID_IOPRIORITY_VERYLOW 40028 -#define ID_IOPRIORITY_LOW 40029 -#define ID_IOPRIORITY_NORMAL 40030 -#define ID_IOPRIORITY_HIGH 40031 -#define ID_PROCESS_RESTART 40032 -#define ID_PROCESS_VIRTUALIZATION 40034 -#define ID_PROCESS_AFFINITY 40035 -#define ID_PROCESS_CREATEDUMPFILE 40036 -#define ID_MISCELLANEOUS_DETACHFROMDEBUGGER 40039 -#define ID_MISCELLANEOUS_HEAPS 40040 -#define ID_MISCELLANEOUS_INJECTDLL 40041 -#define ID_PRIORITY_REALTIME 40048 -#define ID_PRIORITY_HIGH 40049 -#define ID_WINDOW_BRINGTOFRONT 40055 -#define ID_WINDOW_RESTORE 40056 -#define ID_WINDOW_MINIMIZE 40057 -#define ID_WINDOW_MAXIMIZE 40058 -#define ID_WINDOW_CLOSE 40059 -#define ID_PROCESS_SEARCHONLINE 40060 -#define ID_HANDLE_CLOSE 40061 -#define ID_HANDLE_PROTECTED 40062 -#define ID_HANDLE_INHERIT 40063 -#define ID_HANDLE_PROPERTIES 40064 -#define ID_MODULE_UNLOAD 40066 -#define ID_MODULE_PROPERTIES 40068 -#define ID_PROCESS_TERMINATETREE 40069 -#define ID_THREAD_INSPECT 40075 -#define ID_HACKER_RUN 40076 -#define ID_HACKER_RUNASADMINISTRATOR 40077 -#define ID_HACKER_RUNAS 40078 -#define ID_HACKER_SHOWDETAILSFORALLPROCESSES 40079 -#define ID_HACKER_FINDHANDLESORDLLS 40082 -#define ID_HACKER_OPTIONS 40083 -#define ID_VIEW_SYSTEMINFORMATION 40091 -#define ID_TRAYICONS_CPUHISTORY 40093 -#define ID_TRAYICONS_CPUUSAGE 40094 -#define ID_TRAYICONS_COMMITHISTORY 40096 -#define ID_TRAYICONS_PHYSICALMEMORYHISTORY 40097 -#define ID_VIEW_REFRESH 40098 -#define ID_TOOLS_CREATESERVICE 40101 -#define ID_TOOLS_HIDDENPROCESSES 40102 -#define ID_TOOLS_INSPECTEXECUTABLEFILE 40103 -#define ID_HELP_LOG 40104 -#define ID_HELP_DONATE 40105 -#define ID_HELP_ABOUT 40106 -#define ID_SERVICE_GOTOPROCESS 40107 -#define ID_SERVICE_START 40108 -#define ID_SERVICE_CONTINUE 40109 -#define ID_SERVICE_PAUSE 40110 -#define ID_SERVICE_STOP 40111 -#define ID_SERVICE_DELETE 40112 -#define ID_SERVICE_PROPERTIES 40113 -#define ID_SERVICE_OPENKEY 40114 -#define ID_PRIVILEGE_ENABLE 40116 -#define ID_PRIVILEGE_DISABLE 40117 -#define ID_PRIVILEGE_REMOVE 40118 -#define ID_OBJECT_CLOSE 40119 -#define ID_OBJECT_PROPERTIES 40120 -#define ID_HELP_DEBUGCONSOLE 40122 -#define ID_MODULE_SEARCHONLINE 40125 -#define ID_TOOLS_PAGEFILES 40126 -#define ID_USER_DISCONNECT 40127 -#define ID_USER_LOGOFF 40128 -#define ID_USER_SENDMESSAGE 40129 -#define ID_USER_PROPERTIES 40130 -#define ID_USERS_DUMMY 40131 -#define ID_MODULE_INSPECT 40132 -#define ID_PROCESS_DEBUG 40133 -#define ID_USER_CONNECT 40138 -#define ID_NETWORK_GOTOPROCESS 40139 -#define ID_NETWORK_CLOSE 40140 -#define ID_OPACITY_10 40142 -#define ID_OPACITY_20 40143 -#define ID_OPACITY_30 40144 -#define ID_OPACITY_40 40145 -#define ID_OPACITY_50 40146 -#define ID_OPACITY_60 40147 -#define ID_OPACITY_70 40148 -#define ID_OPACITY_80 40149 -#define ID_OPACITY_90 40150 -#define ID_OPACITY_OPAQUE 40151 -#define ID_VIEW_ALWAYSONTOP 40153 -#define ID_UPDATEINTERVAL_FAST 40155 -#define ID_UPDATEINTERVAL_NORMAL 40156 -#define ID_UPDATEINTERVAL_BELOWNORMAL 40157 -#define ID_UPDATEINTERVAL_SLOW 40158 -#define ID_UPDATEINTERVAL_VERYSLOW 40159 -#define ID_COMPUTER_LOCK 40160 -#define ID_COMPUTER_LOGOFF 40161 -#define ID_COMPUTER_SLEEP 40162 -#define ID_COMPUTER_HIBERNATE 40163 -#define ID_COMPUTER_RESTART 40164 -#define ID_COMPUTER_SHUTDOWN 40165 -#define ID_NETWORK_VIEWSTACK 40167 -#define ID_TRAYICONS_IOHISTORY 40168 -#define ID_ICON_EXIT 40169 -#define ID_ICON_SHOWHIDEPROCESSHACKER 40171 -#define ID_ICON_SYSTEMINFORMATION 40172 -#define ID_PROCESSES_DUMMY 40177 -#define ID_NOTIFICATIONS_ENABLEALL 40178 -#define ID_NOTIFICATIONS_DISABLEALL 40179 -#define ID_NOTIFICATIONS_NEWPROCESSES 40180 -#define ID_NOTIFICATIONS_TERMINATEDPROCESSES 40181 -#define ID_NOTIFICATIONS_NEWSERVICES 40182 -#define ID_NOTIFICATIONS_STARTEDSERVICES 40183 -#define ID_NOTIFICATIONS_STOPPEDSERVICES 40184 -#define ID_NOTIFICATIONS_DELETEDSERVICES 40185 -#define ID_MISCELLANEOUS_GDIHANDLES 40188 -#define ID_ESC_EXIT 40190 -#define ID_PROCESS_COPY 40194 -#define ID_THREAD_COPY 40195 -#define ID_PRIVILEGE_COPY 40196 -#define ID_NETWORK_COPY 40197 -#define ID_SERVICE_COPY 40198 -#define ID_MODULE_COPY 40199 -#define ID_HANDLE_COPY 40200 -#define ID_OBJECT_COPY 40201 -#define ID_MEMORY_SAVE 40208 -#define ID_MEMORY_CHANGEPROTECTION 40209 -#define ID_MEMORY_FREE 40210 -#define ID_MEMORY_DECOMMIT 40211 -#define ID_MEMORY_COPY 40213 -#define ID_MEMORY_READWRITEMEMORY 40214 -#define ID_MEMORY_READWRITEADDRESS 40215 -#define ID_FILTER_CONTAINS 40216 -#define ID_FILTER_CONTAINS_CASEINSENSITIVE 40218 -#define ID_FILTER_REGEX 40219 -#define ID_FILTER_REGEX_CASEINSENSITIVE 40221 -#define ID_TAB_NEXT 40223 -#define ID_TAB_PREV 40224 -#define ID_MISCELLANEOUS_RUNAS 40229 -#define ID_MISCELLANEOUS_RUNASTHISUSER 40230 -#define ID_HACKER_PLUGINS 40231 -#define ID_VIEW_HIDEPROCESSESFROMOTHERUSERS 40232 -#define ID_THREAD_AFFINITY 40233 -#define ID_VIEW_HIDESIGNEDPROCESSES 40234 -#define ID_VIEW_UPDATEAUTOMATICALLY 40235 -#define ID_HACKER_RUNASLIMITEDUSER 40236 -#define ID_USER_REMOTECONTROL 40237 -#define ID_PAGEPRIORITY_NORMAL 40239 -#define ID_PAGEPRIORITY_BELOWNORMAL 40240 -#define ID_PAGEPRIORITY_MEDIUM 40241 -#define ID_PAGEPRIORITY_LOW 40242 -#define ID_PAGEPRIORITY_VERYLOW 40243 -#define ID_VIEW_SHOWCPUBELOW001 40246 -#define ID_MODULE_OPENFILELOCATION 40247 -#define ID_PROCESS_OPENFILELOCATION 40248 -#define ID_MISCELLANEOUS_REDUCEWORKINGSET 40249 -#define ID_EMPTY_EMPTYWORKINGSETS 40250 -#define ID_EMPTY_EMPTYMODIFIEDPAGELIST 40251 -#define ID_EMPTY_EMPTYSTANDBYLIST 40252 -#define ID_EMPTY_EMPTYPRIORITY0STANDBYLIST 40253 -#define IDC_BACK 40255 -#define ID_DIGIT1 40263 -#define ID_DIGIT2 40264 -#define ID_DIGIT3 40265 -#define ID_DIGIT4 40266 -#define ID_DIGIT5 40267 -#define ID_DIGIT6 40268 -#define ID_DIGIT7 40269 -#define ID_DIGIT8 40270 -#define ID_DIGIT9 40271 -#define ID_VIEW_HIDEDRIVERSERVICES 40273 -#define ID_VIEW_SECTIONPLACEHOLDER 40274 -#define ID_VIEW_SCROLLTONEWPROCESSES 40275 -#define ID_TOOLS_STARTTASKMANAGER 40277 -#define ID_COMPUTER_SHUTDOWNHYBRID 40278 -#define ID_COMPUTER_RESTARTBOOTOPTIONS 40280 -#define ID_HANDLE_OBJECTPROPERTIES1 40282 -#define ID_HANDLE_OBJECTPROPERTIES2 40283 -#define ID_OBJECT_GOTOOWNINGPROCESS 40284 -#define ID_NETWORK_GOTOSERVICE 40285 -#define ID_SERVICE_OPENFILELOCATION 40286 -#define ID_PROCESS_GOTOPROCESS 40287 -#define ID_MINIINFO_REFRESH 40288 -#define ID_MINIINFO_REFRESHAUTOMATICALLY 40289 -#define ID_ENVIRONMENT_EDIT 40290 -#define ID_ENVIRONMENT_COPY 40291 -#define ID_ENVIRONMENT_DELETE 40292 -#define IDC_MAXSCREEN 40293 -#define IDDYNAMIC 50000 -#define IDPLUGINS 55000 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 223 -#define _APS_NEXT_COMMAND_VALUE 40295 -#define _APS_NEXT_CONTROL_VALUE 1385 -#define _APS_NEXT_SYMED_VALUE 169 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ProcessHacker.rc +// +#define IDR_RT_MANIFEST 1 +#define IDI_PROCESSHACKER 101 +#define IDR_MAINWND 102 +#define IDR_MAINWND_ACCEL 102 +#define IDD_PROCGENERAL 103 +#define IDD_PROCMODULES 104 +#define IDD_PROCTHREADS 105 +#define IDD_PROCHANDLES 106 +#define IDD_PROCENVIRONMENT 107 +#define IDD_THRDSTACK 108 +#define IDR_THREAD 110 +#define IDR_HANDLE 111 +#define IDR_MODULE 112 +#define IDC_CPU 112 +#define IDC_PRIVATEBYTES 113 +#define IDC_IO 114 +#define IDB_CROSS 117 +#define IDB_TICK 118 +#define IDC_PHYSICAL 119 +#define IDR_COMPUTER 120 +#define IDC_MEMORY 120 +#define IDD_ABOUT 121 +#define IDC_NEWOBJECTS 121 +#define IDC_REMOVEDOBJECTS 122 +#define IDR_PROCESS 123 +#define IDC_CPUUSER 123 +#define IDR_SERVICE 124 +#define IDC_CPUKERNEL 124 +#define IDC_IORO 125 +#define IDD_SRVLIST 125 +#define IDD_SRVGENERAL 126 +#define IDC_IOW 126 +#define IDD_HNDLGENERAL 128 +#define IDD_INFORMATION 129 +#define IDD_FINDOBJECTS 130 +#define IDD_OBJTOKEN 131 +#define ID_PLUGIN_MENU_ITEM 131 +#define IDC_SEPARATOR 133 +#define IDR_FINDOBJ 134 +#define IDD_HIDDENPROCESSES 135 +#define ID_TRAYICONS_REGISTERED 135 +#define IDD_RUNAS 136 +#define ID_COPY_CELL 136 +#define IDD_PROGRESS 137 +#define IDD_PAGEFILES 138 +#define IDD_TOKGENERAL 139 +#define IDD_TOKADVANCED 140 +#define IDD_OBJJOB 141 +#define IDD_OBJEVENT 142 +#define IDD_OBJMUTANT 143 +#define IDD_OBJSEMAPHORE 144 +#define IDD_OBJTIMER 145 +#define IDD_JOBSTATISTICS 146 +#define IDD_OBJEVENTPAIR 147 +#define IDD_OBJSECTION 148 +#define IDD_AFFINITY 149 +#define IDD_SYSINFO 150 +#define IDR_USER 151 +#define IDD_EDITMESSAGE 152 +#define IDD_SESSION 153 +#define IDD_PROCMEMORY 154 +#define IDD_CHOOSE 155 +#define IDD_OPTGENERAL 162 +#define IDD_OPTHIGHLIGHTING 163 +#define IDR_NETWORK 164 +#define IDD_CHOOSECOLUMNS 166 +#define IDD_NETSTACK 167 +#define IDD_CREATESERVICE 168 +#define IDD_PROCPERFORMANCE 169 +#define ID_SHOWCONTEXTMENU 169 +#define IDD_PROCSTATISTICS 170 +#define IDD_OPTADVANCED 171 +#define IDR_ICON 173 +#define IDD_GDIHANDLES 175 +#define IDD_LOG 178 +#define IDD_MEMEDIT 180 +#define IDR_MEMORY 181 +#define IDD_MEMPROTECT 182 +#define IDD_MEMRESULTS 183 +#define IDR_MEMFILTER 184 +#define IDD_MEMSTRING 185 +#define IDD_OPTGRAPHS 186 +#define IDD_PLUGINS 187 +#define IDD_PLUGINMANAGER 187 +#define IDD_HANDLESTATS 188 +#define IDD_PROCRECORD 189 +#define IDD_CHOOSEPROCESS 190 +#define IDD_PROCSERVICES 191 +#define IDI_PHAPPLICATION 191 +#define IDI_COG 192 +#define IDD_SHADOWSESSION 193 +#define IDI_PHAPPLICATIONGO 194 +#define IDD_TOKCAPABILITIES 194 +#define IDI_COGGO 195 +#define IDD_TOKATTRIBUTES 195 +#define IDD_SYSINFO_CPU 196 +#define IDD_SYSINFO_CPUPANEL 197 +#define IDD_SYSINFO_MEMPANEL 198 +#define IDR_SYSINFO_ACCEL 198 +#define IDD_SYSINFO_MEM 199 +#define IDD_SYSINFO_IO 200 +#define IDD_SYSINFO_IOPANEL 201 +#define IDD_MEMLISTS 202 +#define IDR_EMPTYMEMLISTS 204 +#define IDD_CONTAINER 205 +#define IDD_MINIINFO 207 +#define IDD_MINIINFO_LIST 210 +#define IDR_MINIINFO 211 +#define IDR_MINIINFO_PROCESS 212 +#define IDD_MITIGATION 215 +#define IDI_PIN 216 +#define IDI_FOLDER 217 +#define IDI_PENCIL 218 +#define IDI_MAGNIFIER 219 +#define IDD_EDITENV 221 +#define IDB_SEARCH_ACTIVE 223 +#define IDB_SEARCH_INACTIVE 224 +#define IDB_SEARCH_ACTIVE_BMP 225 +#define IDB_SEARCH_INACTIVE_BMP 226 +#define IDD_OPTIONS 227 +#define IDD_PLUGINPROPERTIES 228 +#define IDD_PLUGINSDISABLED 241 +#define IDD_PROCWMIPROVIDERS 242 +#define IDD_COLUMNSETS 243 +#define ID_VIEW_TRAYICONS 244 +#define IDD_OBJFILE 249 +#define IDD_RUNFILEDLG 253 +#define IDC_TERMINATE 1003 +#define IDC_FILEICON 1005 +#define IDC_FILE 1006 +#define IDC_PROCESS 1007 +#define IDC_COMPANYNAME 1009 +#define IDC_VERSION 1010 +#define IDC_REFRESH 1015 +#define IDC_PERMISSIONS 1018 +#define IDC_FILENAME 1020 +#define IDC_CMDLINE 1021 +#define IDC_URL 1021 +#define IDC_RUNSELECTED 1021 +#define IDC_CURDIR 1022 +#define IDC_STARTED 1023 +#define IDC_ABOUT_NAME 1024 +#define IDC_PEBADDRESS 1024 +#define IDC_TERMINATED 1024 +#define IDC_PARENTPROCESS 1025 +#define IDC_MITIGATION 1026 +#define IDC_PAUSE 1027 +#define IDC_PROTECTION 1027 +#define IDC_START 1028 +#define IDC_DESCRIPTION 1029 +#define IDC_TYPE 1032 +#define IDC_STARTTYPE 1033 +#define IDC_ADDRESS 1033 +#define IDC_IMPERSONATIONLEVEL 1033 +#define IDC_ERRORCONTROL 1034 +#define IDC_GRANTED_ACCESS 1034 +#define IDC_TOKENLUID 1034 +#define IDC_GROUP 1035 +#define IDC_AUTHENTICATIONLUID 1035 +#define IDC_BINARYPATH 1036 +#define IDC_MEMORYUSED 1036 +#define IDC_USERACCOUNT 1037 +#define IDC_MEMORYAVAILABLE 1037 +#define IDC_PASSWORD 1040 +#define IDC_PASSWORDCHECK 1041 +#define IDC_SERVICEDLL 1042 +#define IDC_NAME 1044 +#define IDC_REFERENCES 1045 +#define IDC_INTERNALNAME 1045 +#define IDC_HANDLES 1046 +#define IDC_PROCESSTYPELABEL 1046 +#define IDC_AUTHOR 1046 +#define IDC_PAGED 1047 +#define IDC_PROCESSTYPETEXT 1047 +#define IDC_NONPAGED 1048 +#define IDC_TEXT 1048 +#define IDC_DIAGNOSTICS 1049 +#define IDC_FILTER 1050 +#define IDC_RESULTS 1051 +#define IDC_USER 1052 +#define IDC_USERSID 1053 +#define IDC_ADVANCED 1054 +#define IDC_OWNER 1054 +#define IDC_GROUPS 1055 +#define IDC_PRIMARYGROUP 1055 +#define IDC_VIRTUALIZATION 1056 +#define IDC_SESSIONID 1057 +#define IDC_ELEVATED 1058 +#define IDC_VIRTUALIZED 1059 +#define IDC_SOURCENAME 1059 +#define IDC_PROPERTIES 1060 +#define IDC_SOURCELUID 1060 +#define IDC_APPCONTAINERSID 1060 +#define IDC_LIST 1061 +#define IDC_TOKENUSER 1061 +#define IDC_UIACCESS 1061 +#define IDC_TOKENSID 1062 +#define IDC_PROCESSES 1063 +#define IDC_SCAN 1064 +#define IDC_SAVE 1065 +#define IDC_METHOD 1067 +#define IDC_INTRO 1068 +#define IDC_PROGRAM 1069 +#define IDC_BROWSE 1070 +#define IDC_USERNAME 1071 +#define IDC_SESSIONCOMBO 1072 +#define IDC_DESKTOPCOMBO 1073 +#define IDC_SESSIONS 1074 +#define IDC_PROGRAMCOMBO 1074 +#define IDC_DESKTOPS 1075 +#define IDC_PROGRESS 1076 +#define IDC_PROGRESSTEXT 1077 +#define IDC_LINKEDTOKEN 1079 +#define IDC_DISABLEALL 1079 +#define IDC_MOVEUP 1079 +#define IDC_CLEAR 1079 +#define IDC_GOTO 1079 +#define IDC_OPTIONS 1079 +#define IDC_DETAILS 1079 +#define IDC_ADD 1079 +#define IDC_FONT 1079 +#define IDC_INTEGRITY 1079 +#define IDC_MORE 1079 +#define IDC_VIEWMITIGATION 1080 +#define IDC_REPLACETASKMANAGER 1080 +#define IDC_RENAME 1080 +#define IDC_VIEWPARENTPROCESS 1081 +#define IDC_OPENFILENAME 1082 +#define IDC_LIMITS 1083 +#define IDC_SIGNALED 1084 +#define IDC_SET 1085 +#define IDC_RESET 1086 +#define IDC_PULSE 1087 +#define IDC_APPLY 1087 +#define IDC_COUNT 1088 +#define IDC_ABANDONED 1089 +#define IDC_CURRENTCOUNT 1090 +#define IDC_MAXIMUMCOUNT 1091 +#define IDC_ACQUIRE 1092 +#define IDC_RELEASE 1093 +#define IDC_CANCEL 1094 +#define IDC_OWNERLABEL 1095 +#define IDC_SETLOW 1096 +#define IDC_SETHIGH 1097 +#define IDC_SIZE_ 1098 +#define IDC_CPU0 1099 +#define IDC_CPU1 1100 +#define IDC_CPU2 1101 +#define IDC_CPU3 1102 +#define IDC_CPU4 1103 +#define IDC_CPU5 1104 +#define IDC_CPU6 1105 +#define IDC_CPU7 1106 +#define IDC_CPU8 1107 +#define IDC_TITLE 1107 +#define IDC_CPU9 1108 +#define IDC_CPU10 1109 +#define IDC_TIMEOUT 1109 +#define IDC_CPU11 1110 +#define IDC_CPU12 1111 +#define IDC_STATE 1111 +#define IDC_CPU13 1112 +#define IDC_CLIENTNAME 1112 +#define IDC_CPU14 1113 +#define IDC_CLIENTADDRESS 1113 +#define IDC_CPU15 1114 +#define IDC_CLIENTDISPLAY 1114 +#define IDC_CPU16 1115 +#define IDC_CHOICE 1115 +#define IDC_CPU17 1116 +#define IDC_OPTION 1116 +#define IDC_CPU18 1117 +#define IDC_CHOICEUSER 1117 +#define IDC_CPU19 1118 +#define IDC_HIDEUNNAMEDHANDLES 1118 +#define IDC_CPU20 1119 +#define IDC_CPU21 1120 +#define IDC_STARTMODULE 1120 +#define IDC_CPU22 1121 +#define IDC_OPENSTARTMODULE 1121 +#define IDC_CPU23 1122 +#define IDC_KERNELTIME 1122 +#define IDC_CPU24 1123 +#define IDC_USERTIME 1123 +#define IDC_CPU25 1124 +#define IDC_CONTEXTSWITCHES 1124 +#define IDC_CPU26 1125 +#define IDC_CYCLES 1125 +#define IDC_CPU27 1126 +#define IDC_PRIORITY 1126 +#define IDC_CPU28 1127 +#define IDC_BASEPRIORITY 1127 +#define IDC_CPU29 1128 +#define IDC_IOPRIORITY 1128 +#define IDC_CPU30 1129 +#define IDC_PAGEPRIORITY 1129 +#define IDC_CPU31 1130 +#define IDC_STATICBL1 1130 +#define IDC_STATICBL2 1131 +#define IDC_CPU32 1131 +#define IDC_STATICBL3 1132 +#define IDC_CPU33 1132 +#define IDC_STATICBL4 1133 +#define IDC_CPU34 1133 +#define IDC_STATICBL5 1134 +#define IDC_CPU35 1134 +#define IDC_STATICBL6 1135 +#define IDC_CPU36 1135 +#define IDC_STATICBL7 1136 +#define IDC_CPU37 1136 +#define IDC_STATICBL8 1137 +#define IDC_CPU38 1137 +#define IDC_STATICBL9 1138 +#define IDC_CPU39 1138 +#define IDC_STATICBL10 1139 +#define IDC_CPU40 1139 +#define IDC_STATICBL11 1140 +#define IDC_CPU41 1140 +#define IDC_CHOICESIMPLE 1141 +#define IDC_CPU42 1141 +#define IDC_MESSAGE 1142 +#define IDC_CPU43 1142 +#define IDC_SEARCHENGINE 1143 +#define IDC_CPU44 1143 +#define IDC_MAXSIZEUNIT 1144 +#define IDC_CPU45 1144 +#define IDC_CPU46 1145 +#define IDC_CPU47 1146 +#define IDC_CPU48 1147 +#define IDC_CPU49 1148 +#define IDC_CPU50 1149 +#define IDC_CPU51 1150 +#define IDC_PEVIEWER 1151 +#define IDC_CPU52 1151 +#define IDC_CPU53 1152 +#define IDC_CPU54 1153 +#define IDC_CPU55 1154 +#define IDC_HIGHLIGHTINGDURATION 1155 +#define IDC_CPU56 1155 +#define IDC_CPU57 1156 +#define IDC_ENABLEALL 1157 +#define IDC_CPU58 1157 +#define IDC_ZACTIVEPROCESSES_V 1158 +#define IDC_CPU59 1158 +#define IDC_ZTOTALPROCESSES_V 1159 +#define IDC_CPU60 1159 +#define IDC_ZTERMINATEDPROCESSES_V 1160 +#define IDC_CPU61 1160 +#define IDC_ZUSERTIME_V 1161 +#define IDC_CPU62 1161 +#define IDC_ZKERNELTIME_V 1162 +#define IDC_CPU63 1162 +#define IDC_ZUSERTIMEPERIOD_V 1163 +#define IDC_ZKERNELTIMEPERIOD_V 1164 +#define IDC_ZPAGEFAULTS_V 1165 +#define IDC_ZPEAKPROCESSUSAGE_V 1166 +#define IDC_ZPEAKJOBUSAGE_V 1167 +#define IDC_ZIOREADS_V 1168 +#define IDC_ZIOREADBYTES_V 1169 +#define IDC_ZIOWRITES_V 1170 +#define IDC_ZIOWRITEBYTES_V 1171 +#define IDC_ZIOOTHER_V 1172 +#define IDC_ZIOOTHERBYTES_V 1173 +#define IDC_MOVEDOWN 1176 +#define IDC_REMOVE 1177 +#define IDC_DISPLAYNAME 1179 +#define IDC_ZPRIORITY_V 1181 +#define IDC_ZCYCLES_V 1182 +#define IDC_ZTOTALTIME_V 1185 +#define IDC_ZPRIVATEBYTES_V 1186 +#define IDC_ZWORKINGSET_V 1187 +#define IDC_ZPEAKWORKINGSET_V 1188 +#define IDC_ZVIRTUALSIZE_V 1189 +#define IDC_ZPEAKVIRTUALSIZE_V 1190 +#define IDC_ZPEAKPRIVATEBYTES_V 1191 +#define IDC_ZPAGEPRIORITY_V 1192 +#define IDC_ZIOPRIORITY_V 1194 +#define IDC_ZHANDLES_V 1195 +#define IDC_ZGDIHANDLES_V 1196 +#define IDC_ZOTHERDELTA_V 1196 +#define IDC_ZUSERHANDLES_V 1197 +#define IDC_ZOTHER_V 1197 +#define IDC_GROUPCPU 1198 +#define IDC_GROUPPRIVATEBYTES 1199 +#define IDC_GROUPIO 1200 +#define IDC_ONEGRAPHPERCPU 1202 +#define IDC_ALWAYSONTOP 1203 +#define IDC_COPY 1206 +#define IDC_INACTIVE 1207 +#define IDC_ACTIVE 1208 +#define IDC_SHOW 1209 +#define IDC_HIDE 1210 +#define IDC_AUTOSCROLL 1215 +#define IDC_UNDECORATESYMBOLS 1216 +#define IDC_DBGHELPPATH 1217 +#define IDC_DBGHELPSEARCHPATH 1218 +#define IDC_DESKTOP 1221 +#define IDC_REREAD 1224 +#define IDC_WRITE 1225 +#define IDC_VALUE 1230 +#define IDC_DELAYEDSTART 1234 +#define IDC_MINIMUMLENGTH 1236 +#define IDC_DETECTUNICODE 1237 +#define IDC_PRIVATE 1238 +#define IDC_IMAGE 1239 +#define IDC_MAPPED 1240 +#define IDC_EXTENDEDUNICODE 1241 +#define IDC_SHOWTEXT 1245 +#define IDC_ICONPROCESSES 1248 +#define IDC_CLEANUP 1251 +#define IDC_TOGGLEELEVATION 1254 +#define IDC_TOGGLESUSPENDED 1255 +#define IDC_PARENT 1263 +#define IDC_PROCESSNAME 1264 +#define IDC_SERVICES_LAYOUT 1266 +#define IDC_LINK_SF 1267 +#define IDC_CREDITS 1270 +#define IDC_LOGONTIME 1272 +#define IDC_ZPEAKHANDLES_V 1273 +#define IDC_SHIFT 1274 +#define IDC_USEOLDCOLORS 1274 +#define IDC_OPENURL 1279 +#define IDC_COMPANYNAME_LINK 1279 +#define IDC_SAMPLECOUNT 1280 +#define IDC_SAMPLECOUNTLABEL 1281 +#define IDC_VIRTUALKEY 1283 +#define IDC_CTRL 1284 +#define IDC_ALT 1285 +#define IDC_CONNECTTIME 1286 +#define IDC_DISCONNECTTIME 1287 +#define IDC_LASTINPUTTIME 1288 +#define IDC_ZPRIVATEWS_V 1289 +#define IDC_ZSHAREABLEWS_V 1290 +#define IDC_ZSHAREDWS_V 1291 +#define IDC_IDEALPROCESSOR 1294 +#define IDC_STATICBL12 1295 +#define IDC_BASICINFORMATION 1296 +#define IDC_INSTRUCTION 1298 +#define IDC_CPUNAME 1299 +#define IDC_LAYOUT 1300 +#define IDC_UTILIZATION 1301 +#define IDC_SPEED 1302 +#define IDC_ZPROCESSES_V 1303 +#define IDC_ZTHREADS_V 1304 +#define IDC_ZCONTEXTSWITCHESDELTA_V 1307 +#define IDC_ZINTERRUPTSDELTA_V 1308 +#define IDC_ZDPCSDELTA_V 1309 +#define IDC_ZSYSTEMCALLSDELTA_V 1310 +#define IDC_GRAPH_LAYOUT 1311 +#define IDC_TOTALPHYSICAL 1312 +#define IDC_ZCOMMITCURRENT_V 1314 +#define IDC_ZCOMMITPEAK_V 1315 +#define IDC_ZCOMMITLIMIT_V 1316 +#define IDC_ZPHYSICALCURRENT_V 1317 +#define IDC_ZPHYSICALTOTAL_V 1318 +#define IDC_ZPHYSICALCACHEWS_V 1319 +#define IDC_ZPHYSICALKERNELWS_V 1320 +#define IDC_ZPHYSICALDRIVERWS_V 1321 +#define IDC_ZPAGEDWORKINGSET_V 1322 +#define IDC_ZPAGEDVIRTUALSIZE_V 1323 +#define IDC_ZPAGEDLIMIT_V 1324 +#define IDC_ZPAGEDALLOCSDELTA_V 1325 +#define IDC_ZPAGEDFREESDELTA_V 1326 +#define IDC_ZNONPAGEDUSAGE_V 1327 +#define IDC_ZNONPAGEDLIMIT_V 1328 +#define IDC_ZNONPAGEDALLOCSDELTA_V 1329 +#define IDC_ZNONPAGEDFREESDELTA_V 1330 +#define IDC_ZPHYSICALRESERVED_V 1331 +#define IDC_ZLISTZEROED_V 1332 +#define IDC_ZLISTFREE_V 1333 +#define IDC_ZLISTMODIFIED_V 1334 +#define IDC_ZLISTMODIFIEDNOWRITE_V 1335 +#define IDC_ZLISTSTANDBY_V 1337 +#define IDC_COMMIT_L 1339 +#define IDC_PHYSICAL_L 1340 +#define IDC_ZUPTIME_V 1341 +#define IDC_ZOTHERBYTESDELTA_V 1342 +#define IDC_ZREADSDELTA_V 1343 +#define IDC_ZREADBYTESDELTA_V 1344 +#define IDC_ZWRITESDELTA_V 1345 +#define IDC_ZLISTSTANDBY0_V 1346 +#define IDC_ZWRITEBYTESDELTA_V 1346 +#define IDC_ZLISTSTANDBY1_V 1347 +#define IDC_ZREADS_V 1347 +#define IDC_ZLISTSTANDBY2_V 1348 +#define IDC_ZREADBYTES_V 1348 +#define IDC_ZLISTSTANDBY3_V 1349 +#define IDC_ZWRITES_V 1349 +#define IDC_ZLISTBAD_V 1350 +#define IDC_ZWRITEBYTES_V 1350 +#define IDC_ZLISTREPURPOSED_V 1351 +#define IDC_ZOTHERBYTES_V 1351 +#define IDC_ZLISTREPURPOSED0_V 1352 +#define IDC_ZLISTREPURPOSED1_V 1353 +#define IDC_ZLISTREPURPOSED2_V 1354 +#define IDC_ZLISTREPURPOSED3_V 1355 +#define IDC_ZLISTREPURPOSED4_V 1356 +#define IDC_ZLISTREPURPOSED5_V 1357 +#define IDC_ZLISTREPURPOSED6_V 1358 +#define IDC_ZLISTREPURPOSED7_V 1359 +#define IDC_EMPTY 1360 +#define IDC_SAMPLECOUNTAUTOMATIC 1361 +#define IDC_SHOWCOMMITINSUMMARY 1361 +#define IDC_ZLISTSTANDBY4_V 1364 +#define IDC_ZLISTSTANDBY5_V 1365 +#define IDC_SELECTALL 1365 +#define IDC_ZLISTSTANDBY6_V 1366 +#define IDC_DESELECTALL 1366 +#define IDC_ZLISTSTANDBY7_V 1367 +#define IDC_INSPECT 1367 +#define IDC_ZPAGINGPAGEFAULTSDELTA_V 1368 +#define IDC_ZPAGINGPAGEREADSDELTA_V 1369 +#define IDC_BYTESPERROW 1369 +#define IDC_ZPAGINGPAGEFILEWRITESDELTA_V 1370 +#define IDC_PINWINDOW 1370 +#define IDC_ZPAGINGMAPPEDWRITESDELTA_V 1371 +#define IDC_ZMAPPEDREADIO 1372 +#define IDC_ZLISTMODIFIEDPAGEFILE_V 1373 +#define IDC_ZMAPPEDWRITEIO 1374 +#define IDC_SECTION 1375 +#define IDC_REGEX 1377 +#define IDC_DESCRIPTIONLABEL 1378 +#define IDC_VIEWCOMMANDLINE 1381 +#define IDC_DELETE 1382 +#define IDC_EDIT 1383 +#define IDC_NEW 1384 +#define IDC_HANDLESEARCH 1385 +#define IDC_SEARCH 1387 +#define IDC_FILTEROPTIONS 1389 +#define IDC_FILTERTYPE 1390 +#define IDC_TREELIST 1391 +#define IDC_SECTIONTREE 1393 +#define IDC_INFO 1396 +#define IDC_DEFSTATE 1398 +#define IDC_SETTINGS 1399 +#define IDC_PLUGINTREE 1400 +#define IDC_DISABLED 1401 +#define IDC_LIST_DISABLED 1402 +#define IDC_COLUMNSETLIST 1403 +#define IDC_STATISTICS_LIST 1404 +#define IDC_FLUSH 1406 +#define IDC_POSITION 1407 +#define IDC_FILESIZE 1408 +#define IDC_FILETYPE 1409 +#define IDC_FILEMODE 1410 +#define IDC_DEFAULTPERM 1410 +#define ID_HACKER_EXIT 40001 +#define ID_PROCESS_PROPERTIES 40006 +#define ID_PROCESS_TERMINATE 40007 +#define ID_PROCESS_SUSPEND 40008 +#define ID_PROCESS_RESUME 40009 +#define ID_HACKER_SAVE 40010 +#define ID_THREAD_TERMINATE 40011 +#define ID_THREAD_SUSPEND 40012 +#define ID_THREAD_RESUME 40013 +#define ID_THREAD_PERMISSIONS 40015 +#define ID_THREAD_TOKEN 40016 +#define ID_ANALYZE_WAIT 40018 +#define ID_PRIORITY_TIMECRITICAL 40020 +#define ID_PRIORITY_HIGHEST 40021 +#define ID_PRIORITY_ABOVENORMAL 40022 +#define ID_PRIORITY_NORMAL 40023 +#define ID_PRIORITY_BELOWNORMAL 40024 +#define ID_PRIORITY_LOWEST 40025 +#define ID_PRIORITY_IDLE 40026 +#define ID_IOPRIORITY_VERYLOW 40028 +#define ID_IOPRIORITY_LOW 40029 +#define ID_IOPRIORITY_NORMAL 40030 +#define ID_IOPRIORITY_HIGH 40031 +#define ID_PROCESS_RESTART 40032 +#define ID_PROCESS_VIRTUALIZATION 40034 +#define ID_PROCESS_AFFINITY 40035 +#define ID_PROCESS_CREATEDUMPFILE 40036 +#define ID_MISCELLANEOUS_SETCRITICAL 40038 +#define ID_MISCELLANEOUS_DETACHFROMDEBUGGER 40039 +#define ID_PRIORITY_REALTIME 40048 +#define ID_PRIORITY_HIGH 40049 +#define ID_WINDOW_BRINGTOFRONT 40055 +#define ID_WINDOW_RESTORE 40056 +#define ID_WINDOW_MINIMIZE 40057 +#define ID_WINDOW_MAXIMIZE 40058 +#define ID_WINDOW_CLOSE 40059 +#define ID_PROCESS_SEARCHONLINE 40060 +#define ID_HANDLE_CLOSE 40061 +#define ID_HANDLE_PROTECTED 40062 +#define ID_HANDLE_INHERIT 40063 +#define ID_HANDLE_PROPERTIES 40064 +#define ID_MODULE_UNLOAD 40066 +#define ID_MODULE_PROPERTIES 40068 +#define ID_PROCESS_TERMINATETREE 40069 +#define ID_THREAD_INSPECT 40075 +#define ID_HACKER_RUN 40076 +#define ID_HACKER_RUNASADMINISTRATOR 40077 +#define ID_HACKER_RUNAS 40078 +#define ID_HACKER_SHOWDETAILSFORALLPROCESSES 40079 +#define ID_HACKER_FINDHANDLESORDLLS 40082 +#define ID_HACKER_OPTIONS 40083 +#define ID_VIEW_SYSTEMINFORMATION 40091 +#define ID_TRAYICONS_CPUHISTORY 40093 +#define ID_TRAYICONS_CPUUSAGE 40094 +#define ID_TRAYICONS_COMMITHISTORY 40096 +#define ID_TRAYICONS_PHYSICALMEMORYHISTORY 40097 +#define ID_VIEW_REFRESH 40098 +#define ID_TOOLS_CREATESERVICE 40101 +#define ID_TOOLS_HIDDENPROCESSES 40102 +#define ID_TOOLS_INSPECTEXECUTABLEFILE 40103 +#define ID_HELP_LOG 40104 +#define ID_HELP_DONATE 40105 +#define ID_HELP_ABOUT 40106 +#define ID_SERVICE_GOTOPROCESS 40107 +#define ID_SERVICE_START 40108 +#define ID_SERVICE_CONTINUE 40109 +#define ID_SERVICE_PAUSE 40110 +#define ID_SERVICE_STOP 40111 +#define ID_SERVICE_DELETE 40112 +#define ID_SERVICE_PROPERTIES 40113 +#define ID_SERVICE_OPENKEY 40114 +#define ID_PRIVILEGE_ENABLE 40116 +#define ID_PRIVILEGE_DISABLE 40117 +#define ID_PRIVILEGE_REMOVE 40118 +#define ID_OBJECT_CLOSE 40119 +#define ID_OBJECT_PROPERTIES 40120 +#define ID_HELP_DEBUGCONSOLE 40122 +#define ID_MODULE_SEARCHONLINE 40125 +#define ID_TOOLS_PAGEFILES 40126 +#define ID_USER_DISCONNECT 40127 +#define ID_USER_LOGOFF 40128 +#define ID_USER_SENDMESSAGE 40129 +#define ID_USER_PROPERTIES 40130 +#define ID_USERS_DUMMY 40131 +#define ID_MODULE_INSPECT 40132 +#define ID_PROCESS_DEBUG 40133 +#define ID_USER_CONNECT 40138 +#define ID_NETWORK_GOTOPROCESS 40139 +#define ID_NETWORK_CLOSE 40140 +#define ID_OPACITY_10 40142 +#define ID_OPACITY_20 40143 +#define ID_OPACITY_30 40144 +#define ID_OPACITY_40 40145 +#define ID_OPACITY_50 40146 +#define ID_OPACITY_60 40147 +#define ID_OPACITY_70 40148 +#define ID_OPACITY_80 40149 +#define ID_OPACITY_90 40150 +#define ID_OPACITY_OPAQUE 40151 +#define ID_VIEW_ALWAYSONTOP 40153 +#define ID_UPDATEINTERVAL_FAST 40155 +#define ID_UPDATEINTERVAL_NORMAL 40156 +#define ID_UPDATEINTERVAL_BELOWNORMAL 40157 +#define ID_UPDATEINTERVAL_SLOW 40158 +#define ID_UPDATEINTERVAL_VERYSLOW 40159 +#define ID_COMPUTER_LOCK 40160 +#define ID_COMPUTER_LOGOFF 40161 +#define ID_COMPUTER_SLEEP 40162 +#define ID_COMPUTER_HIBERNATE 40163 +#define ID_COMPUTER_RESTART 40164 +#define ID_COMPUTER_SHUTDOWN 40165 +#define ID_TRAYICONS_IOHISTORY 40168 +#define ID_ICON_EXIT 40169 +#define ID_ICON_SHOWHIDEPROCESSHACKER 40171 +#define ID_ICON_SYSTEMINFORMATION 40172 +#define ID_PROCESSES_DUMMY 40177 +#define ID_NOTIFICATIONS_ENABLEALL 40178 +#define ID_NOTIFICATIONS_DISABLEALL 40179 +#define ID_NOTIFICATIONS_NEWPROCESSES 40180 +#define ID_NOTIFICATIONS_TERMINATEDPROCESSES 40181 +#define ID_NOTIFICATIONS_NEWSERVICES 40182 +#define ID_NOTIFICATIONS_STARTEDSERVICES 40183 +#define ID_NOTIFICATIONS_STOPPEDSERVICES 40184 +#define ID_NOTIFICATIONS_DELETEDSERVICES 40185 +#define ID_MISCELLANEOUS_GDIHANDLES 40188 +#define ID_ESC_EXIT 40190 +#define ID_PROCESS_COPY 40194 +#define ID_THREAD_COPY 40195 +#define ID_NETWORK_COPY 40197 +#define ID_SERVICE_COPY 40198 +#define ID_MODULE_COPY 40199 +#define ID_HANDLE_COPY 40200 +#define ID_OBJECT_COPY 40201 +#define ID_MEMORY_SAVE 40208 +#define ID_MEMORY_CHANGEPROTECTION 40209 +#define ID_MEMORY_FREE 40210 +#define ID_MEMORY_DECOMMIT 40211 +#define ID_MEMORY_COPY 40213 +#define ID_MEMORY_READWRITEMEMORY 40214 +#define ID_FILTER_CONTAINS 40216 +#define ID_FILTER_CONTAINS_CASEINSENSITIVE 40218 +#define ID_FILTER_REGEX 40219 +#define ID_FILTER_REGEX_CASEINSENSITIVE 40221 +#define ID_TAB_NEXT 40223 +#define ID_TAB_PREV 40224 +#define ID_MISCELLANEOUS_RUNAS 40229 +#define ID_MISCELLANEOUS_RUNASTHISUSER 40230 +#define ID_HACKER_PLUGINS 40231 +#define ID_VIEW_HIDEPROCESSESFROMOTHERUSERS 40232 +#define ID_THREAD_AFFINITY 40233 +#define ID_VIEW_HIDESIGNEDPROCESSES 40234 +#define ID_VIEW_UPDATEAUTOMATICALLY 40235 +#define ID_HACKER_RUNASLIMITEDUSER 40236 +#define ID_USER_REMOTECONTROL 40237 +#define ID_PAGEPRIORITY_NORMAL 40239 +#define ID_PAGEPRIORITY_BELOWNORMAL 40240 +#define ID_PAGEPRIORITY_MEDIUM 40241 +#define ID_PAGEPRIORITY_LOW 40242 +#define ID_PAGEPRIORITY_VERYLOW 40243 +#define ID_VIEW_SHOWCPUBELOW001 40246 +#define ID_MODULE_OPENFILELOCATION 40247 +#define ID_PROCESS_OPENFILELOCATION 40248 +#define ID_MISCELLANEOUS_REDUCEWORKINGSET 40249 +#define ID_EMPTY_EMPTYWORKINGSETS 40250 +#define ID_EMPTY_EMPTYMODIFIEDPAGELIST 40251 +#define ID_EMPTY_EMPTYSTANDBYLIST 40252 +#define ID_EMPTY_EMPTYPRIORITY0STANDBYLIST 40253 +#define IDC_BACK 40255 +#define ID_VIEW_ORGANIZECOLUMNSETS 40256 +#define ID_VIEW_SAVECOLUMNSET 40257 +#define ID_VIEW_LOADCOLUMNSET 40258 +#define ID_DIGIT1 40263 +#define ID_DIGIT2 40264 +#define ID_DIGIT3 40265 +#define ID_DIGIT4 40266 +#define ID_DIGIT5 40267 +#define ID_DIGIT6 40268 +#define ID_DIGIT7 40269 +#define ID_DIGIT8 40270 +#define ID_DIGIT9 40271 +#define ID_VIEW_HIDEWAITINGCONNECTIONS 40272 +#define ID_VIEW_HIDEDRIVERSERVICES 40273 +#define ID_VIEW_SECTIONPLACEHOLDER 40274 +#define ID_VIEW_SCROLLTONEWPROCESSES 40275 +#define ID_TOOLS_STARTTASKMANAGER 40277 +#define ID_COMPUTER_SHUTDOWNHYBRID 40278 +#define ID_COMPUTER_RESTARTBOOTOPTIONS 40280 +#define ID_HANDLE_OBJECTPROPERTIES1 40282 +#define ID_HANDLE_OBJECTPROPERTIES2 40283 +#define ID_OBJECT_GOTOOWNINGPROCESS 40284 +#define ID_NETWORK_GOTOSERVICE 40285 +#define ID_SERVICE_OPENFILELOCATION 40286 +#define ID_PROCESS_GOTOPROCESS 40287 +#define ID_MINIINFO_REFRESH 40288 +#define ID_MINIINFO_REFRESHAUTOMATICALLY 40289 +#define IDC_MAXSCREEN 40293 +#define ID_EMPTY_COMBINEMEMORYLISTS 40295 +#define ID_PRIVILEGE_RESET 40296 +#define ID_GROUP_ENABLE 40297 +#define ID_THREAD_CRITICAL 40297 +#define ID_GROUP_DISABLE 40298 +#define ID_GROUP_RESET 40299 +#define ID_TOOLS_SCM_PERMISSIONS 40300 +#define ID_TOOLS_RDP_PERMISSIONS 40301 +#define ID_UIACCESS_REMOVE 40302 +#define IDDYNAMIC 50000 +#define IDPLUGINS 55000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 254 +#define _APS_NEXT_COMMAND_VALUE 40298 +#define _APS_NEXT_CONTROL_VALUE 1410 +#define _APS_NEXT_SYMED_VALUE 170 +#endif +#endif diff --git a/plugins/CommonUtil/resources/active_search.bmp b/ProcessHacker/resources/active_search.bmp similarity index 100% rename from plugins/CommonUtil/resources/active_search.bmp rename to ProcessHacker/resources/active_search.bmp diff --git a/plugins/CommonUtil/resources/active_search.png b/ProcessHacker/resources/active_search.png similarity index 100% rename from plugins/CommonUtil/resources/active_search.png rename to ProcessHacker/resources/active_search.png diff --git a/ProcessHacker/resources/application_go.ico b/ProcessHacker/resources/application_go.ico deleted file mode 100644 index 0a72a48bd77e..000000000000 Binary files a/ProcessHacker/resources/application_go.ico and /dev/null differ diff --git a/ProcessHacker/resources/capslist.txt b/ProcessHacker/resources/capslist.txt new file mode 100644 index 000000000000..d910fe7785a4 --- /dev/null +++ b/ProcessHacker/resources/capslist.txt @@ -0,0 +1,895 @@ +ID_CAP_ACCESSIBILITY_CLIENT +ID_CAP_ACCESS_FAMILY_NOTES_API +ID_CAP_ACCESS_REMINDER_IMAGES +ID_CAP_ACC_MGR_ADMIN +ID_CAP_ADAPTIVE_BRIGHTNESS_CONTROL +ID_CAP_ADVERTISING_CONFIG +ID_CAP_AIS_TOKEN_MANAGER +ID_CAP_APPCONTAINER_PACKAGE_CERTIFICATES +ID_CAP_APPOINTMENTS +ID_CAP_APPPREINSTALL_DIRECTORY +ID_CAP_APPPREINSTALL_EVENTS +ID_CAP_APPRESOLVER +ID_CAP_APPX_EXECUTION +ID_CAP_APP_STORE_PURCHASE_HISTORY +ID_CAP_AUDIO_EVENTSND +ID_CAP_AUDIO_INTERNAL +ID_CAP_AUDIO_ROUTING_CONTROLLER +ID_CAP_AUDIO_SETTINGS +ID_CAP_AUTHHOST +ID_CAP_BACKGROUND_EXECUTION_MANAGEMENT +ID_CAP_BACKGROUND_SETTINGS_MANAGEMENT +ID_CAP_BACKGROUND_WORKER +ID_CAP_BACKUPROAMRESTORE +ID_CAP_BASEOS_UPDATEAPI +ID_CAP_BINGCLIENT_BINGCONFIGURATION +ID_CAP_BINGCLIENT_CA_INTERESTEXTRACTION +ID_CAP_BINGCLIENT_DDC +ID_CAP_BINGCLIENT_IDENTITY +ID_CAP_BINGCLIENT_OSS +ID_CAP_BINGCLIENT_SUGGESTSHIGHLIGHTS +ID_CAP_BINGCLIENT_TEE +ID_CAP_BLUETOOTH +ID_CAP_BLUETOOTH_ADMIN +ID_CAP_BMR2_MONITOR_SERVICE +ID_CAP_BMR_CONFIGURATION +ID_CAP_BMR_SYNC +ID_CAP_BROKER_NAVIGATION +ID_CAP_BROWSER_ACCESSIBILITY_SETTINGS +ID_CAP_BROWSER_FEATURE_CONTROL_KEYS +ID_CAP_BSS_AIM_INTERFACE +ID_CAP_BSS_NABSYNC_INTERFACE +ID_CAP_BSS_PUSH_INTERFACE +ID_CAP_BSS_REMINDER_INTERFACE +ID_CAP_BUILTIN_BASEPRIORITY +ID_CAP_BUILTIN_CREATEGLOBAL +ID_CAP_BUILTIN_CREATEPERMANENT +ID_CAP_BUILTIN_DEFAULT +ID_CAP_BUILTIN_IMPERSONATE +ID_CAP_BUILTIN_PROFILE +ID_CAP_BUILTIN_SETTIME +ID_CAP_BUILTIN_SHUTDOWN +ID_CAP_BUILTIN_SYMBOLICLINK +ID_CAP_BUILTIN_TCB +ID_CAP_BUTTONS +ID_CAP_CALLMESSAGING_FILTER +ID_CAP_CAMERA +ID_CAP_CA_ACTIONURI_TEST_EVENT +ID_CAP_CA_ADMIN +ID_CAP_CA_BACKGROUND_PROCESSOR +ID_CAP_CA_BACKGROUND_PROCESSOR_ADMIN +ID_CAP_CA_CONTEXT +ID_CAP_CA_DND_MANAGER +ID_CAP_CA_ENABLED +ID_CAP_CA_HISTORY +ID_CAP_CA_RULES +ID_CAP_CA_SIGNALS_GENERIC +ID_CAP_CA_SIGNALS_MANAGER +ID_CAP_CA_SIGNALS_MANAGER_ADMIN +ID_CAP_CA_UPLOAD_FOLDER +ID_CAP_CELLUX_CONFIG_READ +ID_CAP_CELL_API_ADMIN +ID_CAP_CELL_API_COMMON +ID_CAP_CELL_API_LOCATION +ID_CAP_CELL_API_MESSAGING +ID_CAP_CELL_API_MODEMLOGGING +ID_CAP_CELL_API_OEM_PASSTHROUGH +ID_CAP_CELL_API_TELEPHONY +ID_CAP_CELL_API_UICC +ID_CAP_CELL_API_UICC_LOWLEVEL +ID_CAP_CELL_CELLMGR +ID_CAP_CELL_CMCSPWWAN_PLUS +ID_CAP_CELL_MSFT_UICC_DATASTORE +ID_CAP_CELL_OEM_UICC_DATASTORE +ID_CAP_CELL_RCSPRESENCE +ID_CAP_CELL_VIDEOTELEPHONY +ID_CAP_CELL_WNF +ID_CAP_CELL_WNF_ADMIN +ID_CAP_CELL_WNF_PII +ID_CAP_CHAMBER_PROFILE_CODE_INSTALLTEMP_RWD +ID_CAP_CHAMBER_PROFILE_CODE_NITEMP_RW +ID_CAP_CHAMBER_PROFILE_CODE_R +ID_CAP_CHAMBER_PROFILE_CODE_RW +ID_CAP_CHAMBER_PROFILE_DATA_LIVETILES_RWD +ID_CAP_CHAMBER_PROFILE_DATA_MEDIA_RWD +ID_CAP_CHAMBER_PROFILE_DATA_PLATFORMDATA_ALL +ID_CAP_CHAMBER_PROFILE_DATA_R +ID_CAP_CHAMBER_PROFILE_DATA_RW +ID_CAP_CHAMBER_PROFILE_DATA_SHELLCONTENT_R +ID_CAP_CHAMBER_PROFILE_DATA_SHELLCONTENT_RWD +ID_CAP_CLIPBOARD +ID_CAP_COMMANDCHANNEL +ID_CAP_COMMS_APPLICATIONS +ID_CAP_COMMS_COMMON +ID_CAP_COMMS_SERVICES +ID_CAP_COMMS_SETTINGS +ID_CAP_CONTACTS +ID_CAP_CONTENTSHARING +ID_CAP_CORTANA_RULES_DB +ID_CAP_CREATE_PROCESS_IN_CHAMBER +ID_CAP_CREDENTIAL_COLLECTION_UI +ID_CAP_CRITICAL_DATA +ID_CAP_CSP_BMR_PROVISION +ID_CAP_CSP_DMCLIENT +ID_CAP_CSP_FOUNDATION +ID_CAP_CSP_LOCATION +ID_CAP_CSP_MAIL +ID_CAP_CSP_NODECACHE +ID_CAP_CSP_OEM +ID_CAP_CSP_PHONE +ID_CAP_CSP_W4_APPLICATION +ID_CAP_CSP_WIFI_HOTSPOT +ID_CAP_DATACOLLECTION_ACTIVITY +ID_CAP_DATACOLLECTION_COLLECTOR +ID_CAP_DATACOLLECTION_RAWETW +ID_CAP_DATAPLANUSAGE +ID_CAP_DATAPLANUSAGE_ADMIN +ID_CAP_DCP +ID_CAP_DEBUG +ID_CAP_DEBUG_FOLDERS +ID_CAP_DEBUG_NAVIGATION +ID_CAP_DEVELOPERUNLOCK +ID_CAP_DEVELOPERUNLOCK_API +ID_CAP_DEVELOPERUNLOCK_CODEDUI +ID_CAP_DEVICE_LOCK +ID_CAP_DEVICE_LOCK_ADMIN +ID_CAP_DEVICE_MANAGEMENT +ID_CAP_DEVICE_MANAGEMENT_ADMIN +ID_CAP_DEVICE_MANAGEMENT_BOOTSTRAP +ID_CAP_DEVICE_MANAGEMENT_SECURITY_POLICIES +ID_CAP_DIAGNOSTIC_CLIENT +ID_CAP_DISPLAY_CONTROL +ID_CAP_DMCLIENT_APPMGMT +ID_CAP_DO_NOT_DISTURB +ID_CAP_DRIVE_MODE_ADMIN +ID_CAP_DUASVC +ID_CAP_DU_AGENT +ID_CAP_DU_CORE_API +ID_CAP_DU_CSP +ID_CAP_DU_MIGRATION_MANAGER_STATUS +ID_CAP_DU_MIGRATION_WNF_EVENTS +ID_CAP_DU_MIGRATOR_PROVISIONING_STATUS_MICROSOFT +ID_CAP_DU_MIGRATOR_PROVISIONING_STATUS_OEM +ID_CAP_DU_MIGRATOR_STATUS_MICROSOFT +ID_CAP_DU_MIGRATOR_STATUS_OEM +ID_CAP_DU_PROVISIONING +ID_CAP_DU_SHARED_DATA +ID_CAP_DU_USS +ID_CAP_DU_UX +ID_CAP_DU_UX_FEATURE_DISCOVERY +ID_CAP_EAS_CREDENTIALS +ID_CAP_EDM_CACHE_RWDELETE +ID_CAP_EDM_CACHE_WRITE +ID_CAP_ENDPOINTDISCOVERY +ID_CAP_ENROLLMENT +ID_CAP_ENROLLMENT_ADMIN +ID_CAP_ENROLLMENT_POLL +ID_CAP_ENROLLMENT_RENEW +ID_CAP_ENTERPRISERESOURCESTORE +ID_CAP_ENTERPRISE_AUTHENTICATION +ID_CAP_ENTERPRISE_ENROLLMENT +ID_CAP_ENTERPRISE_SERVICE +ID_CAP_ENTERPRISE_SHARED_DATA +ID_CAP_ETW_PROFILER +ID_CAP_EVERYONE +ID_CAP_EVERYONE_INROM +ID_CAP_EXTERNAL_DISPLAY +ID_CAP_FAILURE_REPORT_CONTENT_PROVIDER +ID_CAP_FINDMYPHONE +ID_CAP_FOREGROUND_TASK_MANAGER +ID_CAP_GAMERSERVICES +ID_CAP_GLOBALIZATION_SETTINGS +ID_CAP_HTTP_ACCEPT_LANGUAGE_HEADER +ID_CAP_ICS_RO +ID_CAP_ICS_RW +ID_CAP_IDENTITY_DEVICE +ID_CAP_IDENTITY_DEVICE_1ST_PARTY +ID_CAP_IDENTITY_USER +ID_CAP_IDENTITY_USER_1ST_PARTY +ID_CAP_IDM_IMAGE_CACHE +ID_CAP_IMMERSIVE_SHELL +ID_CAP_INPUT_CORE +ID_CAP_INPUT_FEATURES +ID_CAP_INPUT_INJECTION +ID_CAP_INPUT_LOCALES +ID_CAP_INPUT_SERVICE +ID_CAP_INSTALL_CERTIFICATES +ID_CAP_INTENTTEXTRACTION_OPTIN +ID_CAP_INTERNAL_DEPLOYMENT +ID_CAP_INTERNET_EXPLORER_BROWSER_HISTORY +ID_CAP_INTERNET_EXPLORER_DATA_OPTIMIZATION +ID_CAP_INTERNET_EXPLORER_FAVORITES +ID_CAP_INTERNET_EXPLORER_HKCU_WRITE +ID_CAP_INTERNET_EXPLORER_HKLM_SECURITY_SETTINGS +ID_CAP_INTERNET_EXPLORER_INTRANET_ZONE_SETTINGS +ID_CAP_INTERNET_EXPLORER_REMOTEDEBUGGING +ID_CAP_INTERNET_EXPLORER_ROAMING +ID_CAP_INTERNET_EXPLORER_SEARCH_PROVIDER_KEYS_HKCU +ID_CAP_INTEROPSERVICES +ID_CAP_ISV_CAMERA +ID_CAP_KEYBOARD +ID_CAP_KIDZONE_ADMIN +ID_CAP_KIDZONE_CUSTOMIZATION +ID_CAP_LANGUAGEUNDERSTANDING +ID_CAP_LASS_ADMIN +ID_CAP_LASS_REMOTELOCK +ID_CAP_LEGACY_VOICEMAIL_HANDLER +ID_CAP_LEXICONUPDATE +ID_CAP_LIVEID +ID_CAP_LIVETOKEN_WNF_EVENTS +ID_CAP_LOCATION +ID_CAP_LOCATION_ADMIN +ID_CAP_LOCATION_BTPOLICY +ID_CAP_LOCATION_GNSSDRIVER +ID_CAP_MAP +ID_CAP_MAP_ADMIN +ID_CAP_MAP_WRITE +ID_CAP_MEDIALIB +ID_CAP_MEDIALIB_AUDIO +ID_CAP_MEDIALIB_INT +ID_CAP_MEDIALIB_PHOTO +ID_CAP_MEDIALIB_PHOTO_FULL +ID_CAP_MEDIALIB_PLAYBACK +ID_CAP_MEDIALIB_VIDEO +ID_CAP_MEDIASERVICE_VOLUMELIMIT_INT +ID_CAP_MICMUTEPOLICY_BYPASS +ID_CAP_MICROPHONE +ID_CAP_MONITOR_NAVIGATION +ID_CAP_MOUSE +ID_CAP_MO_CLOUDMESSAGING +ID_CAP_MSS_BYTESTREAM_RPC +ID_CAP_MULTIMEDIA_ENCODER_HARDWARE +ID_CAP_MULTIVARIANT +ID_CAP_MULTIVARIANT_INSTALL_DATA +ID_CAP_NARRATOR_SETTINGS +ID_CAP_NATIVE_NETWORK_REPLACEMENT +ID_CAP_NAVIGATIONBAR_ADMIN +ID_CAP_NETWORKING +ID_CAP_NETWORKING_ADMIN +ID_CAP_NETWORKING_INTERNET_CLIENT +ID_CAP_NETWORKING_INTERNET_CLIENT_SERVER +ID_CAP_NETWORKING_PRIVATE_NETWORK_CLIENT_SERVER +ID_CAP_NETWORKING_VPN_ADMIN +ID_CAP_NETWORKING_VPN_PROVIDER +ID_CAP_NETWORKING_VPN_SERVICES +ID_CAP_NFC_ADMIN +ID_CAP_NOCENTER_SOUNDS +ID_CAP_NTSERVICES +ID_CAP_NVREAD +ID_CAP_NVREADWRITE +ID_CAP_O365_DISCOVERY +ID_CAP_OEMPUBLICDIRECTORY +ID_CAP_OEM_ADC +ID_CAP_OEM_CUSTOM +ID_CAP_OEM_DEPLOYMENT +ID_CAP_OFFICE_LAUNCH_URL +ID_CAP_OFFICE_MSDRM_HKCU +ID_CAP_ONENOTE_EVENTS +ID_CAP_OOBE_PRIVATE +ID_CAP_ORIENTATION_LOCK +ID_CAP_PEOPLE_EXTENSION +ID_CAP_PEOPLE_EXTENSION_IM +ID_CAP_PEOPLE_EXTENSION_MOBILE +ID_CAP_PERSONA +ID_CAP_PERSONAL_INFORMATION_IMPORT +ID_CAP_PHONEBROKER_INTERFACE +ID_CAP_PHONEDIALER +ID_CAP_PHONEPROVISIONER_DEVICEUPDATE +ID_CAP_PHONEPROVISIONER_EVENTS +ID_CAP_PHONE_2ND_PARTY +ID_CAP_PHONE_ADMIN +ID_CAP_PHONE_INTERNAL +ID_CAP_PHOTOS_SETTINGS_R +ID_CAP_PHOTOS_SETTINGS_RW +ID_CAP_PICKER_CONTRACT_UI +ID_CAP_PLATFORM_EXTENSIBILITY +ID_CAP_PLAYREADY +ID_CAP_PLAYREADY_ADMIN +ID_CAP_PM_1ST_PARTY +ID_CAP_PM_BSS +ID_CAP_PM_INSTALL +ID_CAP_POIDATASTORE +ID_CAP_POIDATASTORE_ADMIN +ID_CAP_POLICY_MANAGER +ID_CAP_POLICY_MANAGER_READONLY +ID_CAP_POWERNOTIF_USER +ID_CAP_PRESERVED_DATA +ID_CAP_PRIV_ABOUTCPL +ID_CAP_PRIV_ACCESSIBILITYCPL +ID_CAP_PRIV_ACCESSORIESCPL +ID_CAP_PRIV_ACCESSORYMGRSVC +ID_CAP_PRIV_ACCOUNTPROVSVC +ID_CAP_PRIV_ACTIONURIHOST +ID_CAP_PRIV_ADVERTISINGIDCPL +ID_CAP_PRIV_ALARMS +ID_CAP_PRIV_APHCHECK +ID_CAP_PRIV_APMUX +ID_CAP_PRIV_APPCORNER +ID_CAP_PRIV_APPPREINSTALLER +ID_CAP_PRIV_APPRESOLVERUI +ID_CAP_PRIV_APPSDATAMIGRATOR +ID_CAP_PRIV_APPXEXECUTIONSVC +ID_CAP_PRIV_AUTHHOST_MSA +ID_CAP_PRIV_AUTHHOST_WAB_A +ID_CAP_PRIV_AUTHHOST_WAB_B +ID_CAP_PRIV_AUTHHOST_WAB_C +ID_CAP_PRIV_AUTHHOST_WAB_ENTERPRISE +ID_CAP_PRIV_AUTHHOST_WAB_SSO +ID_CAP_PRIV_AUTHHOST_WAB_SSO_ENTERPRISE +ID_CAP_PRIV_AUTOTIMEUPDATE +ID_CAP_PRIV_BATTERYSAVERCPL +ID_CAP_PRIV_BLUETOOTHPBAPSVC +ID_CAP_PRIV_BMR2MONITORSVC +ID_CAP_PRIV_BMR2SCHEDULETRIGGER +ID_CAP_PRIV_BMRCPL +ID_CAP_PRIV_BMRSCHEDULETRIGGER +ID_CAP_PRIV_BRIGHTNESSCPL +ID_CAP_PRIV_BSSVC +ID_CAP_PRIV_BTAGSERVICE +ID_CAP_PRIV_BTCONNMGR +ID_CAP_PRIV_BTHAVCTPSVC +ID_CAP_PRIV_BTSERV +ID_CAP_PRIV_BTUXCPL +ID_CAP_PRIV_CALC7 +ID_CAP_PRIV_CAPTURESVC +ID_CAP_PRIV_CASVCSHARED3 +ID_CAP_PRIV_CELLMANAGER +ID_CAP_PRIV_CELLULARDATACOLLECTOR +ID_CAP_PRIV_CELLUX +ID_CAP_PRIV_CERTINSTALLER +ID_CAP_PRIV_CFMSVC +ID_CAP_PRIV_CGSVC +ID_CAP_PRIV_CLOUDSTORAGECPL +ID_CAP_PRIV_CMSERVICE +ID_CAP_PRIV_COMMANDCHANNEL +ID_CAP_PRIV_COMMSAPHOST +ID_CAP_PRIV_COMMSAPPLICATIONS +ID_CAP_PRIV_COMMSCERTINSTSVC +ID_CAP_PRIV_COMMSMESSAGESVC +ID_CAP_PRIV_COMMSMMSTRANSPORT +ID_CAP_PRIV_CONTACTSTOKENSVC +ID_CAP_PRIV_CONTENTSHARESVC +ID_CAP_PRIV_CONTENTSHARINGAPP +ID_CAP_PRIV_COREUIREGISTRAR +ID_CAP_PRIV_DACCERTINSTSVC +ID_CAP_PRIV_DATACOLLECTION +ID_CAP_PRIV_DATASENSESVC +ID_CAP_PRIV_DATASMART +ID_CAP_PRIV_DATETIMECPL +ID_CAP_PRIV_DCPSVC +ID_CAP_PRIV_DEBUGGERMUXNOTIFY +ID_CAP_PRIV_DIAGNOSTICSVC +ID_CAP_PRIV_DMCFGHOST +ID_CAP_PRIV_DMOMACPNETWMO +ID_CAP_PRIV_DMOMACPUSERMO +ID_CAP_PRIV_DMWAPPUSHSVC +ID_CAP_PRIV_DRIVINGMODEMANAGER +ID_CAP_PRIV_DRIVINGMODESETTINGS +ID_CAP_PRIV_DSSVC +ID_CAP_PRIV_DSTOKENCLEAN +ID_CAP_PRIV_DUACALLBACK +ID_CAP_PRIV_DUACLIENT +ID_CAP_PRIV_DUCLEANUPMIGRATOR +ID_CAP_PRIV_DUFEATUREDISCOVERY +ID_CAP_PRIV_DUMIGRATIONMANAGER +ID_CAP_PRIV_DUMIGRATIONPROVISIONERMICROSOFT +ID_CAP_PRIV_DUMIGRATIONPROVISIONEROEM +ID_CAP_PRIV_DUPOSTUPDATEUX +ID_CAP_PRIV_DUSTARTINGMIGRATOR +ID_CAP_PRIV_DUSVC +ID_CAP_PRIV_ENROLLMENTCLIENT +ID_CAP_PRIV_ENTAPPSERVICE +ID_CAP_PRIV_ENTERPRISEINSTALL +ID_CAP_PRIV_ENTERPRISEMGMSVC +ID_CAP_PRIV_ENTERPRISERING +ID_CAP_PRIV_ENTERPRISEVALIDATION +ID_CAP_PRIV_EXECMANSERVICE +ID_CAP_PRIV_FINDMYPHONE +ID_CAP_PRIV_FLYOUTDATAMIGRATOR +ID_CAP_PRIV_GROVELER +ID_CAP_PRIV_GWPCENROLLSVC +ID_CAP_PRIV_HFA +ID_CAP_PRIV_HOTSPOTHOST +ID_CAP_PRIV_HUBTILERESTOREHOST +ID_CAP_PRIV_ICSENTITLEMENTHOST +ID_CAP_PRIV_ICSSVC +ID_CAP_PRIV_INPUTSERVICE +ID_CAP_PRIV_INSTALLERWORKER +ID_CAP_PRIV_INTERNETEXPLORER +ID_CAP_PRIV_IPOVERUSB +ID_CAP_PRIV_KEYBOARDCPL +ID_CAP_PRIV_KIDZONECONFIGURATION +ID_CAP_PRIV_KIDZONECUSTOMIZATION +ID_CAP_PRIV_LASSCREDENTIALEXPIRATIONCHECK +ID_CAP_PRIV_LAUNCHAPPSVC +ID_CAP_PRIV_LEXICONUPDATE +ID_CAP_PRIV_LFSVC +ID_CAP_PRIV_LIVETOKEN +ID_CAP_PRIV_LOCATIONUXCPL +ID_CAP_PRIV_LOCKANDWALLPAPER +ID_CAP_PRIV_MEDIA +ID_CAP_PRIV_MEDIASERVICE +ID_CAP_PRIV_MIRRORCPL +ID_CAP_PRIV_MOBILEUI +ID_CAP_PRIV_MOSHOST +ID_CAP_PRIV_MSATICKETSVC +ID_CAP_PRIV_MSGIMTRANSPORT +ID_CAP_PRIV_MSGSMSTRANSPORT +ID_CAP_PRIV_MTP +ID_CAP_PRIV_MVPROVISIONHOST +ID_CAP_PRIV_MVUX +ID_CAP_PRIV_NABSYNC +ID_CAP_PRIV_NOCENTERSETTINGSCPL +ID_CAP_PRIV_NOTIFICATIONPLATFORMMIGRATOR +ID_CAP_PRIV_NOTIFSVC +ID_CAP_PRIV_OFFICE +ID_CAP_PRIV_OMADMCLIENT_ENTERPRISE +ID_CAP_PRIV_OMADMCLIENT_MOBILE_OPERATOR +ID_CAP_PRIV_OMADMPRC +ID_CAP_PRIV_OOBE +ID_CAP_PRIV_ORIENTSRV +ID_CAP_PRIV_PACMANSERVICE +ID_CAP_PRIV_PHONEAUDIOSRV +ID_CAP_PRIV_PHONEPROVISIONER +ID_CAP_PRIV_PHONEPROVISIONER_OEM +ID_CAP_PRIV_PHONESVCSG +ID_CAP_PRIV_PHOTOS +ID_CAP_PRIV_PHOTOSSVC +ID_CAP_PRIV_PIMIDXMAINT +ID_CAP_PRIV_PLACESSVC +ID_CAP_PRIV_POSTDUAPPMIGRATOR +ID_CAP_PRIV_POWERNOTIF +ID_CAP_PRIV_PROXIMITYSVC +ID_CAP_PRIV_PROXYSVC +ID_CAP_PRIV_REALWORLD-BINGCLIENT +ID_CAP_PRIV_REALWORLD-INTERESTEXTRACTION +ID_CAP_PRIV_REBOOTDEVICE +ID_CAP_PRIV_REGIONCPL +ID_CAP_PRIV_REMEMBER +ID_CAP_PRIV_RETAILDEMO +ID_CAP_PRIV_RETAILDEMOERROR +ID_CAP_PRIV_RETAILDEMOGLOB +ID_CAP_PRIV_RETAILDEMOUI +ID_CAP_PRIV_RILADAPTATION +ID_CAP_PRIV_RINGTONESANDSOUNDS +ID_CAP_PRIV_ROAMINGCPL +ID_CAP_PRIV_ROTATIONLOCKCPL +ID_CAP_PRIV_SAPISVR +ID_CAP_PRIV_SECMIGRATOR +ID_CAP_PRIV_SEMGRSVC +ID_CAP_PRIV_SETTINGS +ID_CAP_PRIV_SHELLDATAMIGRATOR +ID_CAP_PRIV_SIREPSVC +ID_CAP_PRIV_SOFTAPUX +ID_CAP_PRIV_SPEECHCPL +ID_CAP_PRIV_SPEECHUPDATE +ID_CAP_PRIV_START +ID_CAP_PRIV_STORAGESENSE +ID_CAP_PRIV_STORAGESVC +ID_CAP_PRIV_STOREDATAMIGRATOR +ID_CAP_PRIV_TASKSCHEDULERSVC +ID_CAP_PRIV_TELCPL +ID_CAP_PRIV_TELREPSVC +ID_CAP_PRIV_THEMECPL +ID_CAP_PRIV_TILEMIGRATOR +ID_CAP_PRIV_TIMEBROKER +ID_CAP_PRIV_UPDATEMGRSVC +ID_CAP_PRIV_USBCPL +ID_CAP_PRIV_USERDATASERVICE +ID_CAP_PRIV_USSREPORTING +ID_CAP_PRIV_UTKSERVICE +ID_CAP_PRIV_UTKUX +ID_CAP_PRIV_VPNUX +ID_CAP_PRIV_WALLET +ID_CAP_PRIV_WALLETSVC +ID_CAP_PRIV_WEHCSPHELPER +ID_CAP_PRIV_WEHSTART +ID_CAP_PRIV_WIFICONNSVC +ID_CAP_PRIV_WIFICPASSIST +ID_CAP_PRIV_WIFICPBROWSERUX +ID_CAP_PRIV_WIFIUDPTEST +ID_CAP_PRIV_WIFIUXBLUE +ID_CAP_PRIV_WLID2MSA +ID_CAP_PRIV_WPABSVC +ID_CAP_PRIV_WPNARRATOR +ID_CAP_PRIV_WPNCERTINSTSVC +ID_CAP_PRIV_WPTOOLS +ID_CAP_PRIV_WPTPMVSCMGRSVC +ID_CAP_PRIV_WPUITESTTOOLS +ID_CAP_PRIV_ZMEDIAQUEUESVC +ID_CAP_PRIV_ZMF +ID_CAP_PRIV_ZMF_SERVICE +ID_CAP_PROVISIONWPCERTIFICATE +ID_CAP_PROXIMITY +ID_CAP_PUBLIC_FOLDER_FULL +ID_CAP_PUBLISH_ALARM_STATE +ID_CAP_PUBLISH_OOBE_STATE +ID_CAP_PUSHROUTER +ID_CAP_PUSH_NOTIFICATION +ID_CAP_PUSH_SERVER +ID_CAP_QUICK_SETTINGS +ID_CAP_READGWPCERTIFICATE +ID_CAP_REBOOT_FLASHING_MODE +ID_CAP_REMEMBER_ADMIN +ID_CAP_REMEMBER_API +ID_CAP_REMOVABLE_STORAGE +ID_CAP_RESET_PHONE +ID_CAP_RESOURCE_MANAGER +ID_CAP_RETAILDEMO_BACKGROUNDIMAGE +ID_CAP_RETAILDEMO_CLIENT +ID_CAP_RETAILDEMO_GLOB_REGISTRY +ID_CAP_RETAILDEMO_OFFLINECONTENT +ID_CAP_RINGTONE_ADD +ID_CAP_ROAMING_CONFIGURATION +ID_CAP_ROTATION_MANAGER +ID_CAP_RUNTIME_CONFIG +ID_CAP_SCREENCAPTURE +ID_CAP_SCREEN_RECORDER +ID_CAP_SCREEN_RECORDER_BKG +ID_CAP_SEARCHMAPS_SHAREDCONFIG +ID_CAP_SEND_TO_ONENOTE +ID_CAP_SENSORS +ID_CAP_SETDEVICENAME +ID_CAP_SETTINGSYNC +ID_CAP_SETTINGSYNC_CONFIGURATION +ID_CAP_SETTINGS_MANAGEMENT_PROVIDER +ID_CAP_SHARED_OBJECT_DIRECTORY +ID_CAP_SHARED_USER_CERTIFICATES +ID_CAP_SHARE_DELEGATE +ID_CAP_SHELL_DEVICE_LOCK_UI_API +ID_CAP_SHELL_LAUNCH_SESSION +ID_CAP_SHELL_NAVIGATION +ID_CAP_SHELL_NOTIFICATION_CLIENT +ID_CAP_SHELL_OEM_ADMIN +ID_CAP_SHELL_RESET_NAVIGATION +ID_CAP_SHELL_TEST_CLIENT +ID_CAP_SHOW_VOLUME_CONTROL +ID_CAP_SMS +ID_CAP_SMS_COMPANION +ID_CAP_SMS_INTERCEPT_AGENT +ID_CAP_SMS_INTERCEPT_RECIPIENT +ID_CAP_SOUND_CONTROL +ID_CAP_SPEECH_GRAMMARS +ID_CAP_SPEECH_RECOGNITION +ID_CAP_SPEECH_RECOGNITION_SYSTEM +ID_CAP_SPEECH_SETTINGS +ID_CAP_SPEECH_UPDATE +ID_CAP_SPLASH_CONFIG +ID_CAP_STARTMENU_CONFIG +ID_CAP_STORAGE_MANAGEMENT +ID_CAP_SUPPRESS_MSA_CONNECT_ARD +ID_CAP_SYNC_EXTENSION +ID_CAP_SYSTEMTRAY_ADMIN +ID_CAP_SYSTEM_ALLOC_WINDOWID +ID_CAP_SYSTEM_COMPOSITOR +ID_CAP_SYSTEM_COUNTERS +ID_CAP_SYSTEM_REGISTRAR +ID_CAP_SYSTEM_WAITCURSOR +ID_CAP_TELEMETRY_ADMIN +ID_CAP_TELEMETRY_CONFIGURE +ID_CAP_TELEMETRY_STUDY +ID_CAP_TEST_NAVIGATION +ID_CAP_TILERESTOREDATA +ID_CAP_TOUCH +ID_CAP_TOUCH_TEST +ID_CAP_TPM_VSCMANAGER +ID_CAP_TS_SCHEDULES_ALL +ID_CAP_USB +ID_CAP_USER_ACTIVITY +ID_CAP_VIDEOSINK_INTERNAL +ID_CAP_VOICEMAIL +ID_CAP_VOIP +ID_CAP_VOIP_CALL_CONTROLLER +ID_CAP_VSTEST_INSTALL_FOLDER +ID_CAP_W32TIME_API +ID_CAP_WAB_RESOURCES +ID_CAP_WALLET +ID_CAP_WALLET_ADMIN +ID_CAP_WALLET_DEALS +ID_CAP_WALLET_PAYMENTINSTRUMENTS +ID_CAP_WALLET_SECUREELEMENT +ID_CAP_WALLPAPER_ADMIN +ID_CAP_WBOEXT +ID_CAP_WEBBROWSERCOMPONENT +ID_CAP_WEB_CREDENTIALS +ID_CAP_WIFI_ADMIN +ID_CAP_WIFI_BASIC +ID_CAP_WIFI_BROWSER +ID_CAP_WIFI_HOTSPOT_HOST +ID_CAP_WIFI_PROFILE_ADMIN +ID_CAP_WIFI_TILE_MANAGER +ID_CAP_WPN_PLATFORM +ID_CAP_WPN_PLATFORM_REG_KEY +ID_CAP_WPTOOLS_INSTALL_FOLDER +ID_CAP_ZMFSERVICES +ID_CAP_ZTRACE +Microsoft.firmwareRead_cw5n1h2txyewy +Microsoft.firmwareWrite_cw5n1h2txyewy +accessoryManager +activateAsUser +activity +activityData +activitySystem +allAppMods +allJoyn +allowElevation +appBroadcast +appBroadcastServices +appBroadcastSettings +appCaptureServices +appCaptureSettings +appDiagnostics +appLicensing +appManagementSystem +applicationDefaults +applicationViewActivation +appointments +appointmentsSystem +audioDeviceConfiguration +backgroundMediaPlayback +backgroundMediaRecording +backgroundVoIP +biometricSystem +blockedChatMessages +bluetooth +bluetooth.genericAttributeProfile +bluetooth.rfcomm +bluetoothAdapter +bluetoothDeviceSettings +bluetoothSync +broadFileSystemAccess +browserAppList +browserCredentials +cameraProcessingExtension +capabilityAccessConsentDeviceSettings +cellularData +cellularDeviceControl +cellularDeviceIdentity +cellularMessaging +chat +chatSystem +childWebContent +cloudExperienceHost +cloudStore +codeGeneration +comPort +componentUiInWebContent +confirmAppClose +constrainedImpersonation +contacts +contactsSystem +contentDeliveryManagerSettings +contentRestrictions +coreShell +cortanaPermissions +cortanaSettings +cortanaSpeechAccessory +cortanaSurface +curatedTileCollections +dateAndTimeDeviceSettings +developerSettings +developmentModeNetwork +deviceEncryptionManagement +deviceIdentityManagement +deviceLockManagement +deviceManagementAdministrator +deviceManagementDeviceLockPolicies +deviceManagementDmAccount +deviceManagementEmailAccount +deviceManagementFoundation +deviceManagementRegistration +deviceManagementWapSecurityPolicies +devicePortalProvider +deviceProvisioningAdministrator +deviceUnlock +diagnostics +displayDeviceSettings +documentsLibrary +dualSimTiles +email +emailSystem +enterpriseAuthentication +enterpriseCloudSSO +enterpriseDataPolicy +enterpriseDeviceLockdown +eraApplication +exclusiveResource +expandedResources +extendedBackgroundTaskTime +extendedExecutionBackgroundAudio +extendedExecutionCritical +extendedExecutionUnconstrained +featureStagingInfo +feedbackLogCollection +firstSignInSettings +flashPlayerSupport +fullFileSystemAccess +gameBarServices +gameConfigStoreManagement +gameList +gameMonitor +gazeInput +globalMediaControl +graphicsCapture +hevcPlayback +hfxSystem +hidTelephony +holographicCompositor +holographicCompositorSystem +humanInterfaceDevice +imeSystem +inProcessMediaExtension +indexedContent +inputForegroundObservation +inputInjection +inputInjectionBrokered +inputObservation +inputSettings +inputSuppression +internetClient +internetClientServer +interopServices +keyboardDeviceSettings +kinectAudio +kinectExpressions +kinectFace +kinectGamechat +kinectRequired +kinectVideo +kinectVision +languageAndRegionDeviceSettings +languageSettings +liveIdService +localExperienceInternal +location +locationHistory +locationSystem +lockScreenCreatives +lowLevel +lowLevelDevices +lpacAppExperience +lpacClipboard +lpacCom +lpacCryptoServices +lpacEnterprisePolicyChangeNotifications +lpacIME +lpacIdentityServices +lpacInstrumentation +lpacMedia +lpacPackageManagerOperation +lpacPayments +lpacPnPNotifications +lpacPrinting +lpacServicesManagement +lpacSessionManagement +lpacWebPlatform +microphone +microsoftEdgeRemoteDebugging +microsoft.eSIMManagement_8wekyb3d8bbwe +mixedRealityEnvironmentInternal +mmsTransportSystem +multiplaneOverlay +muma +musicLibrary +networkConnectionManagerProvisioning +networkDataPlanProvisioning +networkDataUsageManagement +networkDeviceSettings +networkDiagnostics +networkingVpnProvider +nfcSystem +notificationsDeviceSettings +objects3D +oemDeployment +oemPublicDirectory +offlineMapsManagement +oneProcessVoIP +optical +packageContents +packageManagement +packagePolicySystem +packageQuery +perceptionMonitoring +perceptionSensorsExperimental +perceptionSystem +personalizationDeviceSettings +phoneCall +phoneCallHistory +phoneCallHistoryPublic +phoneCallHistorySystem +phoneCallSystem +picturesLibrary +pointOfService +powerDeviceSettings +preemptiveCamera +previewHfx +previewInkWorkspace +previewPenWorkspace +previewStore +previewUiComposition +privateNetworkClientServer +projectionDeviceSettings +protectedApp +proximity +radios +recordedCallsFolder +regionSettings +registryRead +relatedPackages +remoteFileAccess +remotePassportAuthentication +remoteSystem +removableStorage +resetPhone +runFullTrust +screenDuplication +secondaryAuthenticationFactor +secureAssessment +sensors.custom +serialCommunication +sessionImpersonation +settingSyncConfiguration +sharedUserCertificates +shellDisplayManagement +shellExperience +shellExperienceComposer +slapiQueryLicenseValue +smbios +sms +smsSend +smsSystem +smsTransportSystem +spatialPerception +startScreenManagement +storeAppInstall +storeAppInstallation +storeConfiguration +storeLicenseManagement +storeOptionalPackageInstallManagement +systemDialog +systemDialogEmergency +systemManagement +systemRegistrar +targetedContent +targetedContentSubscription +teamEditionExperience +telemetryData +terminalPowerManagement +thumbnailCache +timezone +uiAutomationSystem +unzipFile +updateAndSecurityDeviceSettings +usb +userAccountInformation +userDataAccountSetup +userDataAccountsProvider +userDataSystem +userDataTasks +userDataTasksSystem +userManagementSystem +userNotificationListener +userPrincipalName +userSigninSupport +userSystemId +userWebAccounts +videosLibrary +visualElementsSystem +visualVoiceMail +vmWorkerProcess +voipCall +walletSystem +webPlatformMediaExtension +webcam +wiFiControl +wiFiDirect +windowManagement +windowManagementSystem +windowsHelloCredentialAccess +windowsPerformanceCounters +xboxAccessoryManagement +xboxBroadcaster +xboxGameSpeechWindow +xboxLiveAuthenticationProvider +xboxSystemApplicationClipQuery +xboxTrackingStream diff --git a/ProcessHacker/resources/cog_go.ico b/ProcessHacker/resources/cog_go.ico deleted file mode 100644 index d679437a07c4..000000000000 Binary files a/ProcessHacker/resources/cog_go.ico and /dev/null differ diff --git a/plugins/CommonUtil/resources/inactive_search.bmp b/ProcessHacker/resources/inactive_search.bmp similarity index 100% rename from plugins/CommonUtil/resources/inactive_search.bmp rename to ProcessHacker/resources/inactive_search.bmp diff --git a/plugins/CommonUtil/resources/inactive_search.png b/ProcessHacker/resources/inactive_search.png similarity index 100% rename from plugins/CommonUtil/resources/inactive_search.png rename to ProcessHacker/resources/inactive_search.png diff --git a/ProcessHacker/runas.c b/ProcessHacker/runas.c index fb71c6fe94f7..31a935072595 100644 --- a/ProcessHacker/runas.c +++ b/ProcessHacker/runas.c @@ -1,1167 +1,2591 @@ -/* - * Process Hacker - - * run as dialog - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The run-as mechanism has three stages: - * 1. The user enters the information into the dialog box. Here it is decided whether the run-as - * service is needed. If it is not, PhCreateProcessAsUser is called directly. Otherwise, - * PhExecuteRunAsCommand2 is called for stage 2. - * 2. PhExecuteRunAsCommand2 creates a random service name and tries to create the service and - * execute it (using PhExecuteRunAsCommand). If the process has insufficient permissions, an - * elevated instance of phsvc is started and PhSvcCallExecuteRunAsCommand is called. - * 3. The service is started, and sets up an instance of phsvc with the same random service name as - * its port name. Either the original or elevated Process Hacker instance then calls - * PhSvcCallInvokeRunAsService to complete the operation. - */ - -/* - * - * ProcessHacker.exe (user, limited privileges) - * * | ^ - * | | | phsvc API (LPC) - * | | | - * | v | - * ProcessHacker.exe (user, full privileges) - * | ^ | ^ - * | | SCM API (RPC) | | - * | | | | - * v | | | phsvc API (LPC) - * services.exe | | - * * | | - * | | | - * | | | - * | v | - * ProcessHacker.exe (NT AUTHORITY\SYSTEM) - * * - * | - * | - * | - * program.exe - */ - -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -typedef struct _RUNAS_DIALOG_CONTEXT -{ - HANDLE ProcessId; - PPH_LIST DesktopList; - PPH_STRING CurrentWinStaName; -} RUNAS_DIALOG_CONTEXT, *PRUNAS_DIALOG_CONTEXT; - -INT_PTR CALLBACK PhpRunAsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhSetDesktopWinStaAccess( - VOID - ); - -VOID PhpSplitUserName( - _In_ PWSTR UserName, - _Out_ PPH_STRING *DomainPart, - _Out_ PPH_STRING *UserPart - ); - -#define SIP(String, Integer) { (String), (PVOID)(Integer) } - -static PH_KEY_VALUE_PAIR PhpLogonTypePairs[] = -{ - SIP(L"Batch", LOGON32_LOGON_BATCH), - SIP(L"Interactive", LOGON32_LOGON_INTERACTIVE), - SIP(L"Network", LOGON32_LOGON_NETWORK), - SIP(L"New credentials", LOGON32_LOGON_NEW_CREDENTIALS), - SIP(L"Service", LOGON32_LOGON_SERVICE) -}; - -static WCHAR RunAsOldServiceName[32] = L""; -static PH_QUEUED_LOCK RunAsOldServiceLock = PH_QUEUED_LOCK_INIT; - -static PPH_STRING RunAsServiceName; -static SERVICE_STATUS_HANDLE RunAsServiceStatusHandle; -static PHSVC_STOP RunAsServiceStop; - -VOID PhShowRunAsDialog( - _In_ HWND ParentWindowHandle, - _In_opt_ HANDLE ProcessId - ) -{ - RUNAS_DIALOG_CONTEXT context; - - context.ProcessId = ProcessId; - context.DesktopList = NULL; - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_RUNAS), - ParentWindowHandle, - PhpRunAsDlgProc, - (LPARAM)&context - ); -} - -static VOID PhpAddAccountsToComboBox( - _In_ HWND ComboBoxHandle - ) -{ - LSA_HANDLE policyHandle; - LSA_ENUMERATION_HANDLE enumerationContext = 0; - PLSA_ENUMERATION_INFORMATION buffer; - ULONG count; - ULONG i; - PPH_STRING name; - SID_NAME_USE nameUse; - - if (NT_SUCCESS(PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL))) - { - while (NT_SUCCESS(LsaEnumerateAccounts( - policyHandle, - &enumerationContext, - &buffer, - 0x100, - &count - ))) - { - for (i = 0; i < count; i++) - { - name = PhGetSidFullName(buffer[i].Sid, TRUE, &nameUse); - - if (name) - { - if (nameUse == SidTypeUser) - ComboBox_AddString(ComboBoxHandle, name->Buffer); - - PhDereferenceObject(name); - } - } - - LsaFreeMemory(buffer); - } - - LsaClose(policyHandle); - } -} - -static BOOLEAN IsServiceAccount( - _In_ PPH_STRING UserName - ) -{ - if ( - PhEqualString2(UserName, L"NT AUTHORITY\\LOCAL SERVICE", TRUE) || - PhEqualString2(UserName, L"NT AUTHORITY\\NETWORK SERVICE", TRUE) || - PhEqualString2(UserName, L"NT AUTHORITY\\SYSTEM", TRUE) - ) - { - return TRUE; - } - else - { - return FALSE; - } -} - -static PPH_STRING GetCurrentWinStaName( - VOID - ) -{ - PPH_STRING string; - - string = PhCreateStringEx(NULL, 0x200); - - if (GetUserObjectInformation( - GetProcessWindowStation(), - UOI_NAME, - string->Buffer, - (ULONG)string->Length + 2, - NULL - )) - { - PhTrimToNullTerminatorString(string); - return string; - } - else - { - PhDereferenceObject(string); - return PhCreateString(L"WinSta0"); // assume the current window station is WinSta0 - } -} - -static BOOL CALLBACK EnumDesktopsCallback( - _In_ PWSTR DesktopName, - _In_ LPARAM Context - ) -{ - PRUNAS_DIALOG_CONTEXT context = (PRUNAS_DIALOG_CONTEXT)Context; - - PhAddItemList(context->DesktopList, PhConcatStrings( - 3, - context->CurrentWinStaName->Buffer, - L"\\", - DesktopName - )); - - return TRUE; -} - -INT_PTR CALLBACK PhpRunAsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PRUNAS_DIALOG_CONTEXT context; - - if (uMsg != WM_INITDIALOG) - { - context = (PRUNAS_DIALOG_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - } - else - { - context = (PRUNAS_DIALOG_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND typeComboBoxHandle = GetDlgItem(hwndDlg, IDC_TYPE); - HWND userNameComboBoxHandle = GetDlgItem(hwndDlg, IDC_USERNAME); - ULONG sessionId; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - if (SHAutoComplete_I) - { - SHAutoComplete_I( - GetDlgItem(hwndDlg, IDC_PROGRAM), - SHACF_AUTOAPPEND_FORCE_ON | SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_ONLY - ); - } - - ComboBox_AddString(typeComboBoxHandle, L"Batch"); - ComboBox_AddString(typeComboBoxHandle, L"Interactive"); - ComboBox_AddString(typeComboBoxHandle, L"Network"); - ComboBox_AddString(typeComboBoxHandle, L"New credentials"); - ComboBox_AddString(typeComboBoxHandle, L"Service"); - PhSelectComboBoxString(typeComboBoxHandle, L"Interactive", FALSE); - - ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\SYSTEM"); - ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\LOCAL SERVICE"); - ComboBox_AddString(userNameComboBoxHandle, L"NT AUTHORITY\\NETWORK SERVICE"); - - PhpAddAccountsToComboBox(userNameComboBoxHandle); - - if (NT_SUCCESS(PhGetProcessSessionId(NtCurrentProcess(), &sessionId))) - SetDlgItemInt(hwndDlg, IDC_SESSIONID, sessionId, FALSE); - - SetDlgItemText(hwndDlg, IDC_DESKTOP, L"WinSta0\\Default"); - SetDlgItemText(hwndDlg, IDC_PROGRAM, PhaGetStringSetting(L"RunAsProgram")->Buffer); - - if (!context->ProcessId) - { - SetDlgItemText(hwndDlg, IDC_USERNAME, - PH_AUTO_T(PH_STRING, PhGetStringSetting(L"RunAsUserName"))->Buffer); - - // Fire the user name changed event so we can fix the logon type. - SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_USERNAME, CBN_EDITCHANGE), 0); - } - else - { - HANDLE processHandle; - HANDLE tokenHandle; - PTOKEN_USER user; - PPH_STRING userName; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - ProcessQueryAccess, - context->ProcessId - ))) - { - if (NT_SUCCESS(PhOpenProcessToken( - processHandle, - TOKEN_QUERY, - &tokenHandle - ))) - { - if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &user))) - { - if (userName = PhGetSidFullName(user->User.Sid, TRUE, NULL)) - { - SetDlgItemText(hwndDlg, IDC_USERNAME, userName->Buffer); - PhDereferenceObject(userName); - } - - PhFree(user); - } - - NtClose(tokenHandle); - } - - NtClose(processHandle); - } - - EnableWindow(GetDlgItem(hwndDlg, IDC_USERNAME), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_TYPE), FALSE); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_PROGRAM), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_PROGRAM), 0, -1); - - //if (!PhGetOwnTokenAttributes().Elevated) - // SendMessage(GetDlgItem(hwndDlg, IDOK), BCM_SETSHIELD, 0, TRUE); - - if (!WINDOWS_HAS_UAC) - ShowWindow(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION), SW_HIDE); - } - break; - case WM_DESTROY: - { - if (context->DesktopList) - PhDereferenceObject(context->DesktopList); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - NTSTATUS status; - PPH_STRING program; - PPH_STRING userName; - PPH_STRING password; - PPH_STRING logonTypeString; - ULONG logonType; - ULONG sessionId; - PPH_STRING desktopName; - BOOLEAN useLinkedToken; - - program = PhaGetDlgItemText(hwndDlg, IDC_PROGRAM); - userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME); - logonTypeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE); - - // Fix up the user name if it doesn't have a domain. - if (PhFindCharInString(userName, 0, '\\') == -1) - { - PSID sid; - PPH_STRING newUserName; - - if (NT_SUCCESS(PhLookupName(&userName->sr, &sid, NULL, NULL))) - { - if (newUserName = PH_AUTO(PhGetSidFullName(sid, TRUE, NULL))) - userName = newUserName; - - PhFree(sid); - } - } - - if (!IsServiceAccount(userName)) - password = PhGetWindowText(GetDlgItem(hwndDlg, IDC_PASSWORD)); - else - password = NULL; - - sessionId = GetDlgItemInt(hwndDlg, IDC_SESSIONID, NULL, FALSE); - desktopName = PhaGetDlgItemText(hwndDlg, IDC_DESKTOP); - - if (WINDOWS_HAS_UAC) - useLinkedToken = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION)) == BST_CHECKED; - else - useLinkedToken = FALSE; - - if (PhFindIntegerSiKeyValuePairs( - PhpLogonTypePairs, - sizeof(PhpLogonTypePairs), - logonTypeString->Buffer, - &logonType - )) - { - if ( - logonType == LOGON32_LOGON_INTERACTIVE && - !context->ProcessId && - sessionId == NtCurrentPeb()->SessionId && - !useLinkedToken - ) - { - // We are eligible to load the user profile. - // This must be done here, not in the service, because - // we need to be in the target session. - - PH_CREATE_PROCESS_AS_USER_INFO createInfo; - PPH_STRING domainPart; - PPH_STRING userPart; - - PhpSplitUserName(userName->Buffer, &domainPart, &userPart); - - memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); - createInfo.CommandLine = program->Buffer; - createInfo.UserName = userPart->Buffer; - createInfo.DomainName = domainPart->Buffer; - createInfo.Password = PhGetStringOrEmpty(password); - - // Whenever we can, try not to set the desktop name; it breaks a lot of things. - // Note that on XP we must set it, otherwise the program doesn't display correctly. - if (WindowsVersion < WINDOWS_VISTA || (desktopName->Length != 0 && !PhEqualString2(desktopName, L"WinSta0\\Default", TRUE))) - createInfo.DesktopName = desktopName->Buffer; - - PhSetDesktopWinStaAccess(); - - status = PhCreateProcessAsUser( - &createInfo, - PH_CREATE_PROCESS_WITH_PROFILE, - NULL, - NULL, - NULL - ); - - if (domainPart) PhDereferenceObject(domainPart); - if (userPart) PhDereferenceObject(userPart); - } - else - { - status = PhExecuteRunAsCommand2( - hwndDlg, - program->Buffer, - userName->Buffer, - PhGetStringOrEmpty(password), - logonType, - context->ProcessId, - sessionId, - desktopName->Buffer, - useLinkedToken - ); - } - } - else - { - status = STATUS_INVALID_PARAMETER; - } - - if (password) - { - RtlSecureZeroMemory(password->Buffer, password->Length); - PhDereferenceObject(password); - } - - if (!NT_SUCCESS(status)) - { - if (status != STATUS_CANCELLED) - PhShowStatus(hwndDlg, L"Unable to start the program", status, 0); - } - else if (status != STATUS_TIMEOUT) - { - PhSetStringSetting2(L"RunAsProgram", &program->sr); - PhSetStringSetting2(L"RunAsUserName", &userName->sr); - EndDialog(hwndDlg, IDOK); - } - } - break; - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Programs (*.exe;*.pif;*.com;*.bat)", L"*.exe;*.pif;*.com;*.bat" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - PhSetFileDialogFileName(fileDialog, PhaGetDlgItemText(hwndDlg, IDC_PROGRAM)->Buffer); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - PPH_STRING fileName; - - fileName = PhGetFileDialogFileName(fileDialog); - SetDlgItemText(hwndDlg, IDC_PROGRAM, fileName->Buffer); - PhDereferenceObject(fileName); - } - - PhFreeFileDialog(fileDialog); - } - break; - case IDC_USERNAME: - { - PPH_STRING userName = NULL; - - if (!context->ProcessId && HIWORD(wParam) == CBN_SELCHANGE) - { - userName = PH_AUTO(PhGetComboBoxString(GetDlgItem(hwndDlg, IDC_USERNAME), -1)); - } - else if (!context->ProcessId && ( - HIWORD(wParam) == CBN_EDITCHANGE || - HIWORD(wParam) == CBN_CLOSEUP - )) - { - userName = PhaGetDlgItemText(hwndDlg, IDC_USERNAME); - } - - if (userName) - { - if (IsServiceAccount(userName)) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE); - - // Hack for Windows XP - if ( - PhEqualString2(userName, L"NT AUTHORITY\\SYSTEM", TRUE) && - WindowsVersion <= WINDOWS_XP - ) - { - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"New credentials", FALSE); - } - else - { - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Service", FALSE); - } - } - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), TRUE); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Interactive", FALSE); - } - } - } - break; - case IDC_SESSIONS: - { - PPH_EMENU sessionsMenu; - PSESSIONIDW sessions; - ULONG numberOfSessions; - ULONG i; - RECT buttonRect; - PPH_EMENU_ITEM selectedItem; - - sessionsMenu = PhCreateEMenu(); - - if (WinStationEnumerateW(NULL, &sessions, &numberOfSessions)) - { - for (i = 0; i < numberOfSessions; i++) - { - PPH_STRING menuString; - WINSTATIONINFORMATION winStationInfo; - ULONG returnLength; - - if (!WinStationQueryInformationW( - NULL, - sessions[i].SessionId, - WinStationInformation, - &winStationInfo, - sizeof(WINSTATIONINFORMATION), - &returnLength - )) - { - winStationInfo.Domain[0] = 0; - winStationInfo.UserName[0] = 0; - } - - if ( - winStationInfo.UserName[0] != 0 && - sessions[i].WinStationName[0] != 0 - ) - { - menuString = PhaFormatString( - L"%u: %s (%s\\%s)", - sessions[i].SessionId, - sessions[i].WinStationName, - winStationInfo.Domain, - winStationInfo.UserName - ); - } - else if (winStationInfo.UserName[0] != 0) - { - menuString = PhaFormatString( - L"%u: %s\\%s", - sessions[i].SessionId, - winStationInfo.Domain, - winStationInfo.UserName - ); - } - else if (sessions[i].WinStationName[0] != 0) - { - menuString = PhaFormatString( - L"%u: %s", - sessions[i].SessionId, - sessions[i].WinStationName - ); - } - else - { - menuString = PhaFormatString(L"%u", sessions[i].SessionId); - } - - PhInsertEMenuItem(sessionsMenu, - PhCreateEMenuItem(0, 0, menuString->Buffer, NULL, UlongToPtr(sessions[i].SessionId)), -1); - } - - WinStationFreeMemory(sessions); - - GetWindowRect(GetDlgItem(hwndDlg, IDC_SESSIONS), &buttonRect); - - selectedItem = PhShowEMenu( - sessionsMenu, - hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - buttonRect.right, - buttonRect.top - ); - - if (selectedItem) - { - SetDlgItemInt( - hwndDlg, - IDC_SESSIONID, - PtrToUlong(selectedItem->Context), - FALSE - ); - } - - PhDestroyEMenu(sessionsMenu); - } - } - break; - case IDC_DESKTOPS: - { - PPH_EMENU desktopsMenu; - ULONG i; - RECT buttonRect; - PPH_EMENU_ITEM selectedItem; - - desktopsMenu = PhCreateEMenu(); - - if (!context->DesktopList) - context->DesktopList = PhCreateList(10); - - context->CurrentWinStaName = GetCurrentWinStaName(); - - EnumDesktops(GetProcessWindowStation(), EnumDesktopsCallback, (LPARAM)context); - - for (i = 0; i < context->DesktopList->Count; i++) - { - PhInsertEMenuItem( - desktopsMenu, - PhCreateEMenuItem(0, 0, ((PPH_STRING)context->DesktopList->Items[i])->Buffer, NULL, NULL), - -1 - ); - } - - GetWindowRect(GetDlgItem(hwndDlg, IDC_DESKTOPS), &buttonRect); - - selectedItem = PhShowEMenu( - desktopsMenu, - hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - buttonRect.right, - buttonRect.top - ); - - if (selectedItem) - { - SetDlgItemText( - hwndDlg, - IDC_DESKTOP, - selectedItem->Text - ); - } - - for (i = 0; i < context->DesktopList->Count; i++) - PhDereferenceObject(context->DesktopList->Items[i]); - - PhClearList(context->DesktopList); - PhDereferenceObject(context->CurrentWinStaName); - PhDestroyEMenu(desktopsMenu); - } - break; - } - } - break; - } - - return FALSE; -} - -/** - * Sets the access control lists of the current window station - * and desktop to allow all access. - */ -VOID PhSetDesktopWinStaAccess( - VOID - ) -{ - static SID_IDENTIFIER_AUTHORITY appPackageAuthority = SECURITY_APP_PACKAGE_AUTHORITY; - - HWINSTA wsHandle; - HDESK desktopHandle; - ULONG allocationLength; - PSECURITY_DESCRIPTOR securityDescriptor; - PACL dacl; - CHAR allAppPackagesSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2]; - PSID allAppPackagesSid; - - // TODO: Set security on the correct window station and desktop. - - allAppPackagesSid = (PISID)allAppPackagesSidBuffer; - RtlInitializeSid(allAppPackagesSid, &appPackageAuthority, SECURITY_BUILTIN_APP_PACKAGE_RID_COUNT); - *RtlSubAuthoritySid(allAppPackagesSid, 0) = SECURITY_APP_PACKAGE_BASE_RID; - *RtlSubAuthoritySid(allAppPackagesSid, 1) = SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE; - - // We create a DACL that allows everyone to access everything. - - allocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + - (ULONG)sizeof(ACL) + - (ULONG)sizeof(ACCESS_ALLOWED_ACE) + - RtlLengthSid(&PhSeEveryoneSid) + - (ULONG)sizeof(ACCESS_ALLOWED_ACE) + - RtlLengthSid(allAppPackagesSid); - securityDescriptor = PhAllocate(allocationLength); - dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); - - RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); - - RtlCreateAcl(dacl, allocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); - RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, &PhSeEveryoneSid); - - if (WindowsVersion >= WINDOWS_8) - { - RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, allAppPackagesSid); - } - - RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE); - - if (wsHandle = OpenWindowStation( - L"WinSta0", - FALSE, - WRITE_DAC - )) - { - PhSetObjectSecurity(wsHandle, DACL_SECURITY_INFORMATION, securityDescriptor); - CloseWindowStation(wsHandle); - } - - if (desktopHandle = OpenDesktop( - L"Default", - 0, - FALSE, - WRITE_DAC | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS - )) - { - PhSetObjectSecurity(desktopHandle, DACL_SECURITY_INFORMATION, securityDescriptor); - CloseDesktop(desktopHandle); - } - - PhFree(securityDescriptor); -} - -/** - * Executes the run-as service. - * - * \param Parameters The run-as parameters. - * - * \remarks This function requires administrator-level access. - */ -NTSTATUS PhExecuteRunAsCommand( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ) -{ - NTSTATUS status; - ULONG win32Result; - PPH_STRING commandLine; - SC_HANDLE scManagerHandle; - SC_HANDLE serviceHandle; - PPH_STRING portName; - UNICODE_STRING portNameUs; - ULONG attempts; - LARGE_INTEGER interval; - - if (!(scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE))) - return PhGetLastWin32ErrorAsNtStatus(); - - commandLine = PhFormatString(L"\"%s\" -ras \"%s\"", PhApplicationFileName->Buffer, Parameters->ServiceName); - - serviceHandle = CreateService( - scManagerHandle, - Parameters->ServiceName, - Parameters->ServiceName, - SERVICE_ALL_ACCESS, - SERVICE_WIN32_OWN_PROCESS, - SERVICE_DEMAND_START, - SERVICE_ERROR_IGNORE, - commandLine->Buffer, - NULL, - NULL, - NULL, - L"LocalSystem", - L"" - ); - win32Result = GetLastError(); - - PhDereferenceObject(commandLine); - - CloseServiceHandle(scManagerHandle); - - if (!serviceHandle) - { - return NTSTATUS_FROM_WIN32(win32Result); - } - - PhSetDesktopWinStaAccess(); - - StartService(serviceHandle, 0, NULL); - DeleteService(serviceHandle); - - portName = PhConcatStrings2(L"\\BaseNamedObjects\\", Parameters->ServiceName); - PhStringRefToUnicodeString(&portName->sr, &portNameUs); - attempts = 10; - - // Try to connect several times because the server may take - // a while to initialize. - do - { - status = PhSvcConnectToServer(&portNameUs, 0); - - if (NT_SUCCESS(status)) - break; - - interval.QuadPart = -50 * PH_TIMEOUT_MS; - NtDelayExecution(FALSE, &interval); - } while (--attempts != 0); - - PhDereferenceObject(portName); - - if (NT_SUCCESS(status)) - { - status = PhSvcCallInvokeRunAsService(Parameters); - PhSvcDisconnectFromServer(); - } - - if (serviceHandle) - CloseServiceHandle(serviceHandle); - - return status; -} - -/** - * Starts a program as another user. - * - * \param hWnd A handle to the parent window. - * \param Program The command line of the program to start. - * \param UserName The user to start the program as. The user - * name should be specified as: domain\\name. This parameter - * can be NULL if \a ProcessIdWithToken is specified. - * \param Password The password for the specified user. If there - * is no password, specify an empty string. This parameter - * can be NULL if \a ProcessIdWithToken is specified. - * \param LogonType The logon type for the specified user. This - * parameter can be 0 if \a ProcessIdWithToken is specified. - * \param ProcessIdWithToken The ID of a process from which - * to duplicate the token. - * \param SessionId The ID of the session to run the program - * under. - * \param DesktopName The window station and desktop to run the - * program under. - * \param UseLinkedToken Uses the linked token if possible. - * - * \retval STATUS_CANCELLED The user cancelled the operation. - * - * \remarks This function will cause another instance of - * Process Hacker to be executed if the current security context - * does not have sufficient system access. This is done - * through a UAC elevation prompt. - */ -NTSTATUS PhExecuteRunAsCommand2( - _In_ HWND hWnd, - _In_ PWSTR Program, - _In_opt_ PWSTR UserName, - _In_opt_ PWSTR Password, - _In_opt_ ULONG LogonType, - _In_opt_ HANDLE ProcessIdWithToken, - _In_ ULONG SessionId, - _In_ PWSTR DesktopName, - _In_ BOOLEAN UseLinkedToken - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PH_RUNAS_SERVICE_PARAMETERS parameters; - WCHAR serviceName[32]; - PPH_STRING portName; - UNICODE_STRING portNameUs; - - memset(¶meters, 0, sizeof(PH_RUNAS_SERVICE_PARAMETERS)); - parameters.ProcessId = HandleToUlong(ProcessIdWithToken); - parameters.UserName = UserName; - parameters.Password = Password; - parameters.LogonType = LogonType; - parameters.SessionId = SessionId; - parameters.CommandLine = Program; - parameters.DesktopName = DesktopName; - parameters.UseLinkedToken = UseLinkedToken; - - // Try to use an existing instance of the service if possible. - if (RunAsOldServiceName[0] != 0) - { - PhAcquireQueuedLockExclusive(&RunAsOldServiceLock); - - portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsOldServiceName); - PhStringRefToUnicodeString(&portName->sr, &portNameUs); - - if (NT_SUCCESS(PhSvcConnectToServer(&portNameUs, 0))) - { - parameters.ServiceName = RunAsOldServiceName; - status = PhSvcCallInvokeRunAsService(¶meters); - PhSvcDisconnectFromServer(); - - PhDereferenceObject(portName); - PhReleaseQueuedLockExclusive(&RunAsOldServiceLock); - - return status; - } - - PhDereferenceObject(portName); - PhReleaseQueuedLockExclusive(&RunAsOldServiceLock); - } - - // An existing instance was not available. Proceed normally. - - memcpy(serviceName, L"ProcessHacker", 13 * sizeof(WCHAR)); - PhGenerateRandomAlphaString(&serviceName[13], 16); - PhAcquireQueuedLockExclusive(&RunAsOldServiceLock); - memcpy(RunAsOldServiceName, serviceName, sizeof(serviceName)); - PhReleaseQueuedLockExclusive(&RunAsOldServiceLock); - - parameters.ServiceName = serviceName; - - if (PhGetOwnTokenAttributes().Elevated) - { - status = PhExecuteRunAsCommand(¶meters); - } - else - { - if (PhUiConnectToPhSvc(hWnd, FALSE)) - { - status = PhSvcCallExecuteRunAsCommand(¶meters); - PhUiDisconnectFromPhSvc(); - } - else - { - status = STATUS_CANCELLED; - } - } - - return status; -} - -static VOID PhpSplitUserName( - _In_ PWSTR UserName, - _Out_ PPH_STRING *DomainPart, - _Out_ PPH_STRING *UserPart - ) -{ - PH_STRINGREF userName; - PH_STRINGREF domainPart; - PH_STRINGREF userPart; - - PhInitializeStringRefLongHint(&userName, UserName); - - if (PhSplitStringRefAtChar(&userName, '\\', &domainPart, &userPart)) - { - *DomainPart = PhCreateString2(&domainPart); - *UserPart = PhCreateString2(&userPart); - } - else - { - *DomainPart = NULL; - *UserPart = PhCreateString2(&userName); - } -} - -static VOID SetRunAsServiceStatus( - _In_ ULONG State - ) -{ - SERVICE_STATUS status; - - memset(&status, 0, sizeof(SERVICE_STATUS)); - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - status.dwCurrentState = State; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP; - - SetServiceStatus(RunAsServiceStatusHandle, &status); -} - -static DWORD WINAPI RunAsServiceHandlerEx( - _In_ DWORD dwControl, - _In_ DWORD dwEventType, - _In_ LPVOID lpEventData, - _In_ LPVOID lpContext - ) -{ - switch (dwControl) - { - case SERVICE_CONTROL_STOP: - PhSvcStop(&RunAsServiceStop); - return NO_ERROR; - case SERVICE_CONTROL_INTERROGATE: - return NO_ERROR; - } - - return ERROR_CALL_NOT_IMPLEMENTED; -} - -static VOID WINAPI RunAsServiceMain( - _In_ DWORD dwArgc, - _In_ LPTSTR *lpszArgv - ) -{ - PPH_STRING portName; - UNICODE_STRING portNameUs; - LARGE_INTEGER timeout; - - memset(&RunAsServiceStop, 0, sizeof(PHSVC_STOP)); - RunAsServiceStatusHandle = RegisterServiceCtrlHandlerEx(RunAsServiceName->Buffer, RunAsServiceHandlerEx, NULL); - SetRunAsServiceStatus(SERVICE_RUNNING); - - portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsServiceName->Buffer); - PhStringRefToUnicodeString(&portName->sr, &portNameUs); - // Use a shorter timeout value to reduce the time spent running as SYSTEM. - timeout.QuadPart = -5 * PH_TIMEOUT_SEC; - - PhSvcMain(&portNameUs, &timeout, &RunAsServiceStop); - - SetRunAsServiceStatus(SERVICE_STOPPED); -} - -NTSTATUS PhRunAsServiceStart( - _In_ PPH_STRING ServiceName - ) -{ - HANDLE tokenHandle; - SERVICE_TABLE_ENTRY entry; - - // Enable some required privileges. - - if (NT_SUCCESS(NtOpenProcessToken( - NtCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES, - &tokenHandle - ))) - { - PhSetTokenPrivilege(tokenHandle, L"SeAssignPrimaryTokenPrivilege", NULL, SE_PRIVILEGE_ENABLED); - PhSetTokenPrivilege(tokenHandle, L"SeBackupPrivilege", NULL, SE_PRIVILEGE_ENABLED); - PhSetTokenPrivilege(tokenHandle, L"SeImpersonatePrivilege", NULL, SE_PRIVILEGE_ENABLED); - PhSetTokenPrivilege(tokenHandle, L"SeIncreaseQuotaPrivilege", NULL, SE_PRIVILEGE_ENABLED); - PhSetTokenPrivilege(tokenHandle, L"SeRestorePrivilege", NULL, SE_PRIVILEGE_ENABLED); - NtClose(tokenHandle); - } - - RunAsServiceName = ServiceName; - - entry.lpServiceName = ServiceName->Buffer; - entry.lpServiceProc = RunAsServiceMain; - - StartServiceCtrlDispatcher(&entry); - - return STATUS_SUCCESS; -} - -NTSTATUS PhInvokeRunAsService( - _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters - ) -{ - NTSTATUS status; - PPH_STRING domainName; - PPH_STRING userName; - PH_CREATE_PROCESS_AS_USER_INFO createInfo; - ULONG flags; - - if (Parameters->UserName) - { - PhpSplitUserName(Parameters->UserName, &domainName, &userName); - } - else - { - domainName = NULL; - userName = NULL; - } - - memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); - createInfo.ApplicationName = Parameters->FileName; - createInfo.CommandLine = Parameters->CommandLine; - createInfo.CurrentDirectory = Parameters->CurrentDirectory; - createInfo.DomainName = PhGetString(domainName); - createInfo.UserName = PhGetString(userName); - createInfo.Password = Parameters->Password; - createInfo.LogonType = Parameters->LogonType; - createInfo.SessionId = Parameters->SessionId; - createInfo.DesktopName = Parameters->DesktopName; - - flags = PH_CREATE_PROCESS_SET_SESSION_ID; - - if (Parameters->ProcessId) - { - createInfo.ProcessIdWithToken = UlongToHandle(Parameters->ProcessId); - flags |= PH_CREATE_PROCESS_USE_PROCESS_TOKEN; - } - - if (Parameters->UseLinkedToken) - flags |= PH_CREATE_PROCESS_USE_LINKED_TOKEN; - - status = PhCreateProcessAsUser( - &createInfo, - flags, - NULL, - NULL, - NULL - ); - - if (domainName) PhDereferenceObject(domainName); - if (userName) PhDereferenceObject(userName); - - return status; -} +/* + * Process Hacker - + * run as dialog + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The run-as mechanism has three stages: + * 1. The user enters the information into the dialog box. Here it is decided whether the run-as + * service is needed. If it is not, PhCreateProcessAsUser is called directly. Otherwise, + * PhExecuteRunAsCommand2 is called for stage 2. + * 2. PhExecuteRunAsCommand2 creates a random service name and tries to create the service and + * execute it (using PhExecuteRunAsCommand). If the process has insufficient permissions, an + * elevated instance of phsvc is started and PhSvcCallExecuteRunAsCommand is called. + * 3. The service is started, and sets up an instance of phsvc with the same random service name as + * its port name. Either the original or elevated Process Hacker instance then calls + * PhSvcCallInvokeRunAsService to complete the operation. + */ + +/* + * + * ProcessHacker.exe (user, limited privileges) + * * | ^ + * | | | phsvc API (LPC) + * | | | + * | v | + * ProcessHacker.exe (user, full privileges) + * | ^ | ^ + * | | SCM API (RPC) | | + * | | | | + * v | | | phsvc API (LPC) + * services.exe | | + * * | | + * | | | + * | | | + * | v | + * ProcessHacker.exe (NT AUTHORITY\SYSTEM) + * * + * | + * | + * | + * program.exe + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct _RUNAS_DIALOG_CONTEXT +{ + HWND ProgramComboBoxWindowHandle; + HWND UserComboBoxWindowHandle; + HWND TypeComboBoxWindowHandle; + HWND PasswordEditWindowHandle; + HWND SessionEditWindowHandle; + HWND DesktopEditWindowHandle; + HANDLE ProcessId; + PPH_STRING CurrentWinStaName; +} RUNAS_DIALOG_CONTEXT, *PRUNAS_DIALOG_CONTEXT; + +typedef struct _PH_RUNAS_SESSION_ITEM +{ + ULONG SessionId; + PPH_STRING SessionName; +} PH_RUNAS_SESSION_ITEM, *PPH_RUNAS_SESSION_ITEM; + +typedef struct _PH_RUNAS_DESKTOP_ITEM +{ + PPH_STRING DesktopName; +} PH_RUNAS_DESKTOP_ITEM, *PPH_RUNAS_DESKTOP_ITEM; + +typedef INT (CALLBACK *MRUSTRINGCMPPROC)(PCWSTR pString1, PCWSTR pString2); +typedef INT (CALLBACK *MRUINARYCMPPROC)(LPCVOID pString1, LPCVOID pString2, ULONG length); + +#define MRU_STRING 0x0000 // list will contain strings. +#define MRU_BINARY 0x0001 // list will contain binary data. +#define MRU_CACHEWRITE 0x0002 // only save list order to reg. is FreeMRUList. + +typedef struct _MRUINFO +{ + ULONG cbSize; + UINT uMaxItems; + UINT uFlags; + HKEY hKey; + LPCTSTR lpszSubKey; + MRUSTRINGCMPPROC lpfnCompare; +} MRUINFO, *PMRUINFO; + +static ULONG (WINAPI *NetUserEnum_I)( + _In_ PCWSTR servername, + _In_ ULONG level, + _In_ ULONG filter, + _Out_ PVOID *bufptr, + _In_ ULONG prefmaxlen, + _Out_ PULONG entriesread, + _Out_ PULONG totalentries, + _Inout_ PULONG resume_handle + ); + +static ULONG (WINAPI *NetApiBufferFree_I)( + _Frees_ptr_opt_ PVOID Buffer + ); + +static HANDLE (WINAPI *CreateMRUList_I)( + _In_ PMRUINFO lpmi + ); +static INT (WINAPI *AddMRUString_I)( + _In_ HANDLE hMRU, + _In_ PWSTR szString + ); +static INT (WINAPI *EnumMRUList_I)( + _In_ HANDLE hMRU, + _In_ INT nItem, + _Out_ PVOID lpData, + _In_ UINT uLen + ); +static INT (WINAPI *FreeMRUList_I)( + _In_ HANDLE hMRU + ); + +INT_PTR CALLBACK PhpRunAsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpRunFileWndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhSetDesktopWinStaAccess( + VOID + ); + +VOID PhpSplitUserName( + _In_ PWSTR UserName, + _Out_opt_ PPH_STRING* DomainPart, + _Out_opt_ PPH_STRING* UserPart + ); + +#define SIP(String, Integer) { (String), (PVOID)(Integer) } + +static PH_KEY_VALUE_PAIR PhpLogonTypePairs[] = +{ + SIP(L"Batch", LOGON32_LOGON_BATCH), + SIP(L"Interactive", LOGON32_LOGON_INTERACTIVE), + SIP(L"Network", LOGON32_LOGON_NETWORK), + SIP(L"New credentials", LOGON32_LOGON_NEW_CREDENTIALS), + SIP(L"Service", LOGON32_LOGON_SERVICE) +}; + +static WCHAR RunAsOldServiceName[32] = L""; +static PH_QUEUED_LOCK RunAsOldServiceLock = PH_QUEUED_LOCK_INIT; + +static PPH_STRING RunAsServiceName; +static SERVICE_STATUS_HANDLE RunAsServiceStatusHandle; +static PHSVC_STOP RunAsServiceStop; + +VOID PhShowRunAsDialog( + _In_ HWND ParentWindowHandle, + _In_opt_ HANDLE ProcessId + ) +{ + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_RUNAS), + PhCsForceNoParent ? NULL : ParentWindowHandle, + PhpRunAsDlgProc, + (LPARAM)ProcessId + ); +} + +BOOLEAN PhShowRunFileDialog( + _In_ HWND ParentWindowHandle + ) +{ + if (DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_RUNFILEDLG), + ParentWindowHandle, + PhpRunFileWndProc + ) == IDOK) + { + return TRUE; + } + + return FALSE; + + // Removed from guisup.c (dmex) + //BOOL (WINAPI *RunFileDlg_I)( + // _In_ HWND hwndOwner, + // _In_opt_ HICON hIcon, + // _In_opt_ LPCWSTR lpszDirectory, + // _In_opt_ LPCWSTR lpszTitle, + // _In_opt_ LPCWSTR lpszDescription, + // _In_ ULONG uFlags + // ); + //PVOID shell32Handle; + // + //if (shell32Handle = LoadLibrary(L"shell32.dll")) + //{ + // if (RunFileDlg_I = PhGetDllBaseProcedureAddress(shell32Handle, NULL, 61)) + // { + // result = !!RunFileDlg_I( + // WindowHandle, + // WindowIcon, + // WorkingDirectory, + // WindowTitle, + // WindowDescription, + // Flags + // ); + // } + // + // FreeLibrary(shell32Handle); + //} +} + +BOOLEAN IsServiceAccount( + _In_ PPH_STRING UserName + ) +{ + BOOLEAN serviceAccount = FALSE; + PPH_STRING localSystemSidName; + PPH_STRING localServiceSidName; + PPH_STRING localNetworkSidName; + + localSystemSidName = PhGetSidFullName(&PhSeLocalSystemSid, TRUE, NULL); + localServiceSidName = PhGetSidFullName(&PhSeLocalServiceSid, TRUE, NULL); + localNetworkSidName = PhGetSidFullName(&PhSeNetworkServiceSid, TRUE, NULL); + + if ( + PhEqualString(localSystemSidName, UserName, TRUE) || + PhEqualString(localServiceSidName, UserName, TRUE) || + PhEqualString(localNetworkSidName, UserName, TRUE) + ) + { + serviceAccount = TRUE; + } + + PhDereferenceObject(localNetworkSidName); + PhDereferenceObject(localServiceSidName); + PhDereferenceObject(localSystemSidName); + + return serviceAccount; +} + +BOOLEAN IsCurrentUserAccount( + _In_ PPH_STRING UserName + ) +{ + PPH_STRING userName; + + if (userName = PhGetTokenUserString(PhGetOwnTokenAttributes().TokenHandle, TRUE)) + { + if (PhEndsWithString(userName, UserName, TRUE)) + { + PhDereferenceObject(userName); + return TRUE; + } + + PhDereferenceObject(userName); + } + + return FALSE; +} + +PPH_STRING GetCurrentWinStaName( + VOID + ) +{ + PPH_STRING string; + + string = PhCreateStringEx(NULL, 0x200); + + if (GetUserObjectInformation( + GetProcessWindowStation(), + UOI_NAME, + string->Buffer, + (ULONG)string->Length + sizeof(UNICODE_NULL), + NULL + )) + { + PhTrimToNullTerminatorString(string); + return string; + } + else + { + PhDereferenceObject(string); + return PhCreateString(L"WinSta0"); // assume the current window station is WinSta0 + } +} + +PPH_STRING GetCurrentDesktopName( + VOID + ) +{ + PPH_STRING string; + + string = PhCreateStringEx(NULL, 0x200); + + if (GetUserObjectInformation( + GetThreadDesktop(HandleToUlong(NtCurrentThreadId())), + UOI_NAME, + string->Buffer, + (ULONG)string->Length + sizeof(UNICODE_NULL), + NULL + )) + { + PhTrimToNullTerminatorString(string); + return string; + } + else + { + PhDereferenceObject(string); + return PhCreateString(L"Default"); + } +} + +PPH_STRING PhpGetCurrentDesktopInfo( + VOID + ) +{ + static PH_STRINGREF seperator = PH_STRINGREF_INIT(L"\\"); // OBJ_NAME_PATH_SEPARATOR + PPH_STRING desktopInfo = NULL; + PPH_STRING winstationName = NULL; + PPH_STRING desktopName = NULL; + + winstationName = GetCurrentWinStaName(); + desktopName = GetCurrentDesktopName(); + + if (winstationName && desktopName) + { + desktopInfo = PhConcatStringRef3(&winstationName->sr, &seperator, &desktopName->sr); + } + + if (PhIsNullOrEmptyString(desktopInfo)) + { + PH_STRINGREF desktopInfoSr; + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->DesktopInfo, &desktopInfoSr); + + PhMoveReference(&desktopInfo, PhCreateString2(&desktopInfoSr)); + } + + if (winstationName) + PhDereferenceObject(winstationName); + if (desktopName) + PhDereferenceObject(desktopName); + + return desktopInfo; +} + +BOOLEAN PhpInitializeNetApi(VOID) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PVOID netapiModuleHandle = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + if (netapiModuleHandle = LoadLibrary(L"netapi32.dll")) + { + NetUserEnum_I = PhGetDllBaseProcedureAddress(netapiModuleHandle, "NetUserEnum", 0); + NetApiBufferFree_I = PhGetDllBaseProcedureAddress(netapiModuleHandle, "NetApiBufferFree", 0); + } + + if (netapiModuleHandle && !NetUserEnum_I && !NetApiBufferFree_I) + { + FreeLibrary(netapiModuleHandle); + netapiModuleHandle = NULL; + } + + PhEndInitOnce(&initOnce); + } + + if (netapiModuleHandle) + return TRUE; + + return FALSE; +} + +BOOLEAN PhpInitializeMRUList(VOID) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PVOID comctl32ModuleHandle = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + if (comctl32ModuleHandle = LoadLibrary(L"comctl32.dll")) + { + CreateMRUList_I = PhGetDllBaseProcedureAddress(comctl32ModuleHandle, "CreateMRUListW", 0); + AddMRUString_I = PhGetDllBaseProcedureAddress(comctl32ModuleHandle, "AddMRUStringW", 0); + EnumMRUList_I = PhGetDllBaseProcedureAddress(comctl32ModuleHandle, "EnumMRUListW", 0); + FreeMRUList_I = PhGetDllBaseProcedureAddress(comctl32ModuleHandle, "FreeMRUList", 0); + } + + if (!CreateMRUList_I && !AddMRUString_I && !EnumMRUList_I && !FreeMRUList_I && comctl32ModuleHandle) + { + FreeLibrary(comctl32ModuleHandle); + comctl32ModuleHandle = NULL; + } + + PhEndInitOnce(&initOnce); + } + + if (comctl32ModuleHandle) + return TRUE; + + return FALSE; +} + +static HANDLE PhpCreateRunMRUList( + VOID + ) +{ + MRUINFO info; + + if (!CreateMRUList_I) + return NULL; + + memset(&info, 0, sizeof(MRUINFO)); + info.cbSize = sizeof(MRUINFO); + info.uMaxItems = UINT_MAX; + info.hKey = HKEY_CURRENT_USER; + info.lpszSubKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"; + + return CreateMRUList_I(&info); +} + +static VOID PhpAddRunMRUListEntry( + _In_ PPH_STRING CommandLine + ) +{ + static PH_STRINGREF prefixSr = PH_STRINGREF_INIT(L"\\1"); + HANDLE listHandle; + PPH_STRING commandString; + + if (!(listHandle = PhpCreateRunMRUList())) + return; + + commandString = PhConcatStringRef2(&CommandLine->sr, &prefixSr); + AddMRUString_I(listHandle, commandString->Buffer); + PhDereferenceObject(commandString); + + FreeMRUList_I(listHandle); +} + +static VOID PhpAddProgramsToComboBox( + _In_ HWND ComboBoxHandle + ) +{ + static PH_STRINGREF prefixSr = PH_STRINGREF_INIT(L"\\1"); + HANDLE listHandle; + INT listCount; + + if (!PhpInitializeMRUList()) + return; + if (!(listHandle = PhpCreateRunMRUList())) + return; + + listCount = EnumMRUList_I( + listHandle, + MAXINT, + NULL, + 0 + ); + + for (INT i = 0; i < listCount; i++) + { + PPH_STRING programName; + PH_STRINGREF nameSr; + PH_STRINGREF firstPart; + PH_STRINGREF remainingPart; + WCHAR entry[MAX_PATH]; + + if (!EnumMRUList_I( + listHandle, + i, + entry, + RTL_NUMBER_OF(entry) + )) + { + break; + } + + PhInitializeStringRefLongHint(&nameSr, entry); + + if (!PhSplitStringRefAtString(&nameSr, &prefixSr, TRUE, &firstPart, &remainingPart)) + { + ComboBox_AddString(ComboBoxHandle, entry); + continue; + } + + programName = PhCreateString2(&firstPart); + ComboBox_AddString(ComboBoxHandle, PhGetString(programName)); + PhDereferenceObject(programName); + } + + FreeMRUList_I(listHandle); +} + +VOID PhpFreeProgramsComboBox( + _In_ HWND ComboBoxHandle + ) +{ + ULONG total; + + if ((total = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + return; + + for (ULONG i = 0; i < total; i++) + { + ComboBox_DeleteString(ComboBoxHandle, i); + } +} + +static VOID PhpFreeAccountsComboBox( + _In_ HWND ComboBoxHandle + ) +{ + ULONG total; + + if ((total = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + return; + + for (ULONG i = 0; i < total; i++) + { + ComboBox_DeleteString(ComboBoxHandle, i); + } + + ComboBox_ResetContent(ComboBoxHandle); +} + +static VOID PhpAddAccountsToComboBox( + _In_ HWND ComboBoxHandle + ) +{ + NET_API_STATUS status; + LPUSER_INFO_0 userinfoArray = NULL; + ULONG userinfoEntriesRead = 0; + ULONG userinfoTotalEntries = 0; + + PhpFreeAccountsComboBox(ComboBoxHandle); + + ComboBox_AddString(ComboBoxHandle, PH_AUTO_T(PH_STRING, PhGetSidFullName(&PhSeLocalSystemSid, TRUE, NULL))->Buffer); + ComboBox_AddString(ComboBoxHandle, PH_AUTO_T(PH_STRING, PhGetSidFullName(&PhSeLocalServiceSid, TRUE, NULL))->Buffer); + ComboBox_AddString(ComboBoxHandle, PH_AUTO_T(PH_STRING, PhGetSidFullName(&PhSeNetworkServiceSid, TRUE, NULL))->Buffer); + + if (!PhpInitializeNetApi()) + return; + + NetUserEnum_I( + NULL, + 0, + FILTER_NORMAL_ACCOUNT, + &userinfoArray, + MAX_PREFERRED_LENGTH, + &userinfoEntriesRead, + &userinfoTotalEntries, + NULL + ); + + if (userinfoArray) + { + NetApiBufferFree_I(userinfoArray); + userinfoArray = NULL; + } + + status = NetUserEnum_I( + NULL, + 0, + FILTER_NORMAL_ACCOUNT, + &userinfoArray, + MAX_PREFERRED_LENGTH, + &userinfoEntriesRead, + &userinfoTotalEntries, + NULL + ); + + if (status == NERR_Success) + { + PPH_STRING username; + PPH_STRING userDomainName = NULL; + + if (username = PhGetSidFullName(PhGetOwnTokenAttributes().TokenSid, TRUE, NULL)) + { + PhpSplitUserName(username->Buffer, &userDomainName, NULL); + PhDereferenceObject(username); + } + + for (ULONG i = 0; i < userinfoEntriesRead; i++) + { + LPUSER_INFO_0 entry = PTR_ADD_OFFSET(userinfoArray, sizeof(USER_INFO_0) * i); + + if (entry->usri0_name) + { + if (userDomainName) + { + PPH_STRING usernameString; + + usernameString = PhConcatStrings( + 3, + userDomainName->Buffer, + L"\\", + entry->usri0_name + ); + + ComboBox_AddString(ComboBoxHandle, usernameString->Buffer); + PhDereferenceObject(usernameString); + } + else + { + ComboBox_AddString(ComboBoxHandle, entry->usri0_name); + } + } + } + + if (userDomainName) + PhDereferenceObject(userDomainName); + } + + if (userinfoArray) + NetApiBufferFree_I(userinfoArray); + + //LSA_HANDLE policyHandle; + //LSA_ENUMERATION_HANDLE enumerationContext = 0; + //PLSA_ENUMERATION_INFORMATION buffer; + //ULONG count; + //PPH_STRING name; + //SID_NAME_USE nameUse; + // + //if (NT_SUCCESS(PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL))) + //{ + // while (NT_SUCCESS(LsaEnumerateAccounts( + // policyHandle, + // &enumerationContext, + // &buffer, + // 0x100, + // &count + // ))) + // { + // for (i = 0; i < count; i++) + // { + // name = PhGetSidFullName(buffer[i].Sid, TRUE, &nameUse); + // if (name) + // { + // if (nameUse == SidTypeUser) + // ComboBox_AddString(ComboBoxHandle, name->Buffer); + // PhDereferenceObject(name); + // } + // } + // LsaFreeMemory(buffer); + // } + // + // LsaClose(policyHandle); + //} +} + +static VOID PhpFreeSessionsComboBox( + _In_ HWND ComboBoxHandle + ) +{ + PPH_RUNAS_SESSION_ITEM entry; + ULONG total; + ULONG i; + + if ((total = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + return; + + for (i = 0; i < total; i++) + { + entry = (PPH_RUNAS_SESSION_ITEM)ComboBox_GetItemData(ComboBoxHandle, i); + + if (entry->SessionName) + PhDereferenceObject(entry->SessionName); + + PhFree(entry); + } + + ComboBox_ResetContent(ComboBoxHandle); +} + +static VOID PhpAddSessionsToComboBox( + _In_ HWND ComboBoxHandle + ) +{ + PSESSIONIDW sessions; + ULONG numberOfSessions; + ULONG i; + + PhpFreeSessionsComboBox(ComboBoxHandle); + + if (WinStationEnumerateW(NULL, &sessions, &numberOfSessions)) + { + for (i = 0; i < numberOfSessions; i++) + { + PPH_STRING menuString; + WINSTATIONINFORMATION winStationInfo; + ULONG returnLength; + + if (!WinStationQueryInformationW( + NULL, + sessions[i].SessionId, + WinStationInformation, + &winStationInfo, + sizeof(WINSTATIONINFORMATION), + &returnLength + )) + { + winStationInfo.Domain[0] = UNICODE_NULL; + winStationInfo.UserName[0] = UNICODE_NULL; + } + + if ( + winStationInfo.UserName[0] != UNICODE_NULL && + sessions[i].WinStationName[0] != UNICODE_NULL + ) + { + menuString = PhFormatString(L"%lu: %s (%s\\%s)", + sessions[i].SessionId, + sessions[i].WinStationName, + winStationInfo.Domain, + winStationInfo.UserName + ); + } + else if (winStationInfo.UserName[0] != UNICODE_NULL) + { + menuString = PhFormatString(L"%lu: %s\\%s", + sessions[i].SessionId, + winStationInfo.Domain, + winStationInfo.UserName + ); + } + else if (sessions[i].WinStationName[0] != UNICODE_NULL) + { + menuString = PhFormatString(L"%lu: %s", + sessions[i].SessionId, + sessions[i].WinStationName + ); + } + else + { + menuString = PhFormatString(L"%lu", sessions[i].SessionId); + } + + { + PPH_RUNAS_SESSION_ITEM entry; + INT itemIndex; + + entry = PhAllocate(sizeof(PH_RUNAS_SESSION_ITEM)); + entry->SessionId = sessions[i].SessionId; + entry->SessionName = menuString; + + if ((itemIndex = ComboBox_AddString(ComboBoxHandle, menuString->Buffer)) != CB_ERR) + { + ComboBox_SetItemData(ComboBoxHandle, itemIndex, entry); + } + } + } + + WinStationFreeMemory(sessions); + } +} + +typedef struct _RUNAS_DIALOG_DESKTOP_CALLBACK +{ + PPH_LIST DesktopList; + PPH_STRING WinStaName; +} RUNAS_DIALOG_DESKTOP_CALLBACK, *PRUNAS_DIALOG_DESKTOP_CALLBACK; + +static BOOL CALLBACK EnumDesktopsCallback( + _In_ PWSTR DesktopName, + _In_ LPARAM Context + ) +{ + PRUNAS_DIALOG_DESKTOP_CALLBACK context = (PRUNAS_DIALOG_DESKTOP_CALLBACK)Context; + + PhAddItemList(context->DesktopList, PhConcatStrings( + 3, + PhGetString(context->WinStaName), + L"\\", + DesktopName + )); + + return TRUE; +} + +static VOID PhpFreeDesktopsComboBox( + _In_ HWND ComboBoxHandle + ) +{ + PPH_RUNAS_DESKTOP_ITEM entry; + ULONG total; + ULONG i; + + if ((total = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + return; + + for (i = 0; i < total; i++) + { + entry = (PPH_RUNAS_DESKTOP_ITEM)ComboBox_GetItemData(ComboBoxHandle, i); + + if (entry->DesktopName) + PhDereferenceObject(entry->DesktopName); + + PhFree(entry); + } + + ComboBox_ResetContent(ComboBoxHandle); +} + +static VOID PhpAddDesktopsToComboBox( + _In_ HWND ComboBoxHandle + ) +{ + ULONG i; + RUNAS_DIALOG_DESKTOP_CALLBACK callback; + + PhpFreeDesktopsComboBox(ComboBoxHandle); + + callback.DesktopList = PhCreateList(10); + callback.WinStaName = GetCurrentWinStaName(); + + EnumDesktops(GetProcessWindowStation(), EnumDesktopsCallback, (LPARAM)&callback); + + for (i = 0; i < callback.DesktopList->Count; i++) + { + INT itemIndex = ComboBox_AddString( + ComboBoxHandle, + PhGetString(callback.DesktopList->Items[i]) + ); + + if (itemIndex != CB_ERR) + { + PPH_RUNAS_DESKTOP_ITEM entry; + + entry = PhAllocate(sizeof(PH_RUNAS_DESKTOP_ITEM)); + entry->DesktopName = callback.DesktopList->Items[i]; + + ComboBox_SetItemData(ComboBoxHandle, itemIndex, entry); + } + } + + PhDereferenceObject(callback.DesktopList); + PhDereferenceObject(callback.WinStaName); +} + +VOID SetDefaultProgramEntry( + _In_ HWND ComboBoxHandle + ) +{ + //Edit_SetText(ComboBoxHandle, PhaGetStringSetting(L"RunAsProgram")->Buffer); + ComboBox_SetCurSel(ComboBoxHandle, 0); +} + +VOID SetDefaultSessionEntry( + _In_ HWND ComboBoxHandle + ) +{ + INT sessionCount; + ULONG currentSessionId = 0; + + if (!NT_SUCCESS(PhGetProcessSessionId(NtCurrentProcess(), ¤tSessionId))) + return; + + if ((sessionCount = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + return; + + for (INT i = 0; i < sessionCount; i++) + { + PPH_RUNAS_SESSION_ITEM entry = (PPH_RUNAS_SESSION_ITEM)ComboBox_GetItemData(ComboBoxHandle, i); + + if (entry && entry->SessionId == currentSessionId) + { + ComboBox_SetCurSel(ComboBoxHandle, i); + break; + } + } +} + +VOID SetDefaultDesktopEntry( + _In_ PRUNAS_DIALOG_CONTEXT Context, + _In_ HWND ComboBoxHandle + ) +{ + INT sessionCount; + PPH_STRING desktopName; + + if (!(desktopName = PhpGetCurrentDesktopInfo())) + return; + + if ((sessionCount = ComboBox_GetCount(ComboBoxHandle)) == CB_ERR) + { + PhClearReference(&desktopName); + return; + } + + for (INT i = 0; i < sessionCount; i++) + { + PPH_RUNAS_DESKTOP_ITEM entry = (PPH_RUNAS_DESKTOP_ITEM)ComboBox_GetItemData(ComboBoxHandle, i); + + if (PhEqualStringRef(&entry->DesktopName->sr, &desktopName->sr, TRUE)) + { + ComboBox_SetCurSel(ComboBoxHandle, i); + break; + } + } + + PhClearReference(&desktopName); +} + +INT_PTR CALLBACK PhpRunAsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PRUNAS_DIALOG_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocateZero(sizeof(RUNAS_DIALOG_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + context->ProgramComboBoxWindowHandle = GetDlgItem(hwndDlg, IDC_PROGRAMCOMBO); + context->SessionEditWindowHandle = GetDlgItem(hwndDlg, IDC_SESSIONCOMBO); + context->DesktopEditWindowHandle = GetDlgItem(hwndDlg, IDC_DESKTOPCOMBO); + context->TypeComboBoxWindowHandle = GetDlgItem(hwndDlg, IDC_TYPE); + context->UserComboBoxWindowHandle = GetDlgItem(hwndDlg, IDC_USERNAME); + context->PasswordEditWindowHandle = GetDlgItem(hwndDlg, IDC_PASSWORD); + context->ProcessId = (HANDLE)lParam; + + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); + + { + COMBOBOXINFO info = { sizeof(COMBOBOXINFO) }; + + if (SendMessage(context->ProgramComboBoxWindowHandle, CB_GETCOMBOBOXINFO, 0, (LPARAM)&info)) + { + if (SHAutoComplete) + SHAutoComplete(info.hwndItem, SHACF_DEFAULT); + } + } + + ComboBox_AddString(context->TypeComboBoxWindowHandle, L"Batch"); + ComboBox_AddString(context->TypeComboBoxWindowHandle, L"Interactive"); + ComboBox_AddString(context->TypeComboBoxWindowHandle, L"Network"); + ComboBox_AddString(context->TypeComboBoxWindowHandle, L"New credentials"); + ComboBox_AddString(context->TypeComboBoxWindowHandle, L"Service"); + PhSelectComboBoxString(context->TypeComboBoxWindowHandle, L"Interactive", FALSE); + + PhpAddProgramsToComboBox(context->ProgramComboBoxWindowHandle); + PhpAddAccountsToComboBox(context->UserComboBoxWindowHandle); + PhpAddSessionsToComboBox(context->SessionEditWindowHandle); + PhpAddDesktopsToComboBox(context->DesktopEditWindowHandle); + + SetDefaultProgramEntry(context->ProgramComboBoxWindowHandle); + SetDefaultSessionEntry(context->SessionEditWindowHandle); + SetDefaultDesktopEntry(context, context->DesktopEditWindowHandle); + + if (!context->ProcessId) + { + PPH_STRING runAsUserName = PhaGetStringSetting(L"RunAsUserName"); + INT runAsUserNameIndex = CB_ERR; + + // Fire the user name changed event so we can fix the logon type. + if (!PhIsNullOrEmptyString(runAsUserName)) + { + runAsUserNameIndex = ComboBox_FindString( + context->UserComboBoxWindowHandle, + 0, + PhGetString(runAsUserName) + ); + } + + if (runAsUserNameIndex != CB_ERR) + ComboBox_SetCurSel(context->UserComboBoxWindowHandle, runAsUserNameIndex); + else + ComboBox_SetCurSel(context->UserComboBoxWindowHandle, 0); + + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_USERNAME, CBN_EDITCHANGE), 0); + } + else + { + HANDLE processHandle; + HANDLE tokenHandle; + PPH_STRING userName; + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + context->ProcessId + ))) + { + if (NT_SUCCESS(PhOpenProcessToken( + processHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + if (userName = PhGetTokenUserString(tokenHandle, TRUE)) + { + PhSetWindowText(context->UserComboBoxWindowHandle, userName->Buffer); + PhDereferenceObject(userName); + } + + NtClose(tokenHandle); + } + + NtClose(processHandle); + } + + EnableWindow(context->UserComboBoxWindowHandle, FALSE); + EnableWindow(context->PasswordEditWindowHandle, FALSE); + EnableWindow(context->TypeComboBoxWindowHandle, FALSE); + } + + PhSetDialogFocus(hwndDlg, context->ProgramComboBoxWindowHandle); + Edit_SetSel(context->ProgramComboBoxWindowHandle, -1, -1); + + //if (!PhGetOwnTokenAttributes().Elevated) + // Button_SetElevationRequiredState(GetDlgItem(hwndDlg, IDOK), TRUE); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhpFreeDesktopsComboBox(context->DesktopEditWindowHandle); + PhpFreeSessionsComboBox(context->SessionEditWindowHandle); + PhpFreeAccountsComboBox(context->UserComboBoxWindowHandle); + PhpFreeProgramsComboBox(context->ProgramComboBoxWindowHandle); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case CBN_DROPDOWN: + { + if (GET_WM_COMMAND_HWND(wParam, lParam) == context->UserComboBoxWindowHandle) + { + //PhpAddAccountsToComboBox(context->UserComboBoxWindowHandle); + } + + if (GET_WM_COMMAND_HWND(wParam, lParam) == context->SessionEditWindowHandle) + { + PhpAddSessionsToComboBox(context->SessionEditWindowHandle); + SetDefaultSessionEntry(context->SessionEditWindowHandle); + } + + if (GET_WM_COMMAND_HWND(wParam, lParam) == context->DesktopEditWindowHandle) + { + PhpAddDesktopsToComboBox(context->DesktopEditWindowHandle); + SetDefaultDesktopEntry(context, context->DesktopEditWindowHandle); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + NTSTATUS status; + BOOLEAN useLinkedToken = FALSE; + BOOLEAN createSuspended = FALSE; + ULONG logonType = ULONG_MAX; + ULONG sessionId = ULONG_MAX; + PPH_STRING program = NULL; + PPH_STRING username = NULL; + PPH_STRING password = NULL; + PPH_STRING logonTypeString; + PPH_STRING desktopName = NULL; + INT selectionIndex = CB_ERR; + + program = PH_AUTO(PhGetWindowText(context->ProgramComboBoxWindowHandle)); + username = PH_AUTO(PhGetWindowText(context->UserComboBoxWindowHandle)); + logonTypeString = PH_AUTO(PhGetWindowText(context->TypeComboBoxWindowHandle)); + useLinkedToken = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION)) == BST_CHECKED; + createSuspended = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TOGGLESUSPENDED)) == BST_CHECKED; + + if (PhIsNullOrEmptyString(program)) + break; + + if ((selectionIndex = ComboBox_GetCurSel(context->SessionEditWindowHandle)) != CB_ERR) + { + PPH_RUNAS_SESSION_ITEM sessionEntry; + + if (sessionEntry = (PPH_RUNAS_SESSION_ITEM)ComboBox_GetItemData(context->SessionEditWindowHandle, selectionIndex)) + { + sessionId = sessionEntry->SessionId; + } + } + + if ((selectionIndex = ComboBox_GetCurSel(context->DesktopEditWindowHandle)) != CB_ERR) + { + PPH_RUNAS_DESKTOP_ITEM desktopEntry; + + if (desktopEntry = (PPH_RUNAS_DESKTOP_ITEM)ComboBox_GetItemData(context->DesktopEditWindowHandle, selectionIndex)) + { + desktopName = desktopEntry->DesktopName; + } + } + + if (selectionIndex == CB_ERR) + break; + if (sessionId == ULONG_MAX) + break; + + // Fix up the user name if it doesn't have a domain. + if (PhFindCharInString(username, 0, '\\') == -1) + { + PSID sid; + PPH_STRING newUserName; + + if (NT_SUCCESS(PhLookupName(&username->sr, &sid, NULL, NULL))) + { + if (newUserName = PH_AUTO(PhGetSidFullName(sid, TRUE, NULL))) + PhSwapReference(&username, newUserName); + + PhFree(sid); + } + } + + if (!IsServiceAccount(username)) + { + password = PhGetWindowText(context->PasswordEditWindowHandle); + PhSetWindowText(context->PasswordEditWindowHandle, L""); + } + + //if (IsCurrentUserAccount(username)) + //{ + // status = PhCreateProcessWin32( + // NULL, + // program->Buffer, + // NULL, + // NULL, + // 0, + // NULL, + // NULL, + // NULL + // ); + //} + + if (PhFindIntegerSiKeyValuePairs( + PhpLogonTypePairs, + sizeof(PhpLogonTypePairs), + logonTypeString->Buffer, + &logonType + )) + { + ULONG currentSessionId = ULONG_MAX; + + PhGetProcessSessionId(NtCurrentProcess(), ¤tSessionId); + + if ( + logonType == LOGON32_LOGON_INTERACTIVE && + !context->ProcessId && + sessionId == currentSessionId && + !useLinkedToken + ) + { + // We are eligible to load the user profile. + // This must be done here, not in the service, because + // we need to be in the target session. + + PH_CREATE_PROCESS_AS_USER_INFO createInfo; + PPH_STRING domainPart = NULL; + PPH_STRING userPart = NULL; + + PhpSplitUserName(username->Buffer, &domainPart, &userPart); + + memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); + createInfo.CommandLine = PhGetString(program); + createInfo.UserName = PhGetString(userPart); + createInfo.DomainName = PhGetString(domainPart); + createInfo.Password = PhGetStringOrEmpty(password); + + // Whenever we can, try not to set the desktop name; it breaks a lot of things. + if (!PhIsNullOrEmptyString(desktopName) && !PhEqualString2(desktopName, L"WinSta0\\Default", TRUE)) + createInfo.DesktopName = PhGetString(desktopName); + + PhSetDesktopWinStaAccess(); + + status = PhCreateProcessAsUser( + &createInfo, + PH_CREATE_PROCESS_WITH_PROFILE | (createSuspended ? PH_CREATE_PROCESS_SUSPENDED : 0), + NULL, + NULL, + NULL + ); + + if (domainPart) PhDereferenceObject(domainPart); + if (userPart) PhDereferenceObject(userPart); + } + else + { + if (context->ProcessId) + { + HANDLE processHandle = NULL; + HANDLE newProcessHandle; + STARTUPINFOEX startupInfo; + SIZE_T attributeListLength = 0; + PSECURITY_DESCRIPTOR processSecurityDescriptor = NULL; + PSECURITY_DESCRIPTOR tokenSecurityDescriptor = NULL; + PVOID environment = NULL; + HANDLE tokenHandle; + ULONG flags = 0; + + memset(&startupInfo, 0, sizeof(STARTUPINFOEX)); + startupInfo.StartupInfo.cb = sizeof(STARTUPINFOEX); + startupInfo.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + startupInfo.StartupInfo.wShowWindow = SW_SHOWNORMAL; + + status = PhOpenProcess( + &processHandle, + PROCESS_CREATE_PROCESS | (PhGetOwnTokenAttributes().Elevated ? PROCESS_QUERY_LIMITED_INFORMATION | READ_CONTROL : 0), + context->ProcessId + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + if (!InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + startupInfo.lpAttributeList = PhAllocate(attributeListLength); + + if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, &attributeListLength)) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &processHandle, sizeof(HANDLE), NULL, NULL)) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + if (PhGetOwnTokenAttributes().Elevated) + { + PhGetObjectSecurity( + processHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + &processSecurityDescriptor + ); + } + + if (NT_SUCCESS(PhOpenProcessToken( + processHandle, + TOKEN_QUERY | (PhGetOwnTokenAttributes().Elevated ? READ_CONTROL : 0), + &tokenHandle + ))) + { + if (PhGetOwnTokenAttributes().Elevated) + { + PhGetObjectSecurity( + tokenHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + &tokenSecurityDescriptor + ); + } + + if (CreateEnvironmentBlock && CreateEnvironmentBlock(&environment, tokenHandle, FALSE)) + { + flags |= PH_CREATE_PROCESS_UNICODE_ENVIRONMENT; + } + + NtClose(tokenHandle); + } + + status = PhCreateProcessWin32Ex( + NULL, + PhGetString(program), + environment, + NULL, + &startupInfo.StartupInfo, + PH_CREATE_PROCESS_SUSPENDED | PH_CREATE_PROCESS_NEW_CONSOLE | PH_CREATE_PROCESS_EXTENDED_STARTUPINFO | flags, + NULL, + NULL, + &newProcessHandle, + NULL + ); + + if (NT_SUCCESS(status)) + { + PROCESS_BASIC_INFORMATION basicInfo; + + if (PhGetOwnTokenAttributes().Elevated) + { + // Note: This is needed to workaround a severe bug with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS + // where the process and token security descriptors are created without an ACE for the current user, + // owned by the wrong user and with a High-IL when the process token is Medium-IL + // preventing the new process from accessing user/system resources above Low-IL. (dmex) + + if (processSecurityDescriptor) + { + PhSetObjectSecurity( + newProcessHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + processSecurityDescriptor + ); + } + + if (tokenSecurityDescriptor && NT_SUCCESS(PhOpenProcessToken( + newProcessHandle, + WRITE_DAC | WRITE_OWNER, + &tokenHandle + ))) + { + PhSetObjectSecurity( + tokenHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + tokenSecurityDescriptor + ); + NtClose(tokenHandle); + } + } + + if (NT_SUCCESS(PhGetProcessBasicInformation(newProcessHandle, &basicInfo))) + { + AllowSetForegroundWindow(HandleToUlong(basicInfo.UniqueProcessId)); + } + + NtResumeProcess(newProcessHandle); + NtClose(newProcessHandle); + } + + CleanupExit: + + if (environment && DestroyEnvironmentBlock) + { + DestroyEnvironmentBlock(environment); + } + + if (tokenSecurityDescriptor) + { + PhFree(tokenSecurityDescriptor); + } + + if (processSecurityDescriptor) + { + PhFree(processSecurityDescriptor); + } + + if (startupInfo.lpAttributeList) + { + DeleteProcThreadAttributeList(startupInfo.lpAttributeList); + PhFree(startupInfo.lpAttributeList); + } + + if (processHandle) + { + NtClose(processHandle); + } + } + else + { + status = PhExecuteRunAsCommand3( + hwndDlg, + PhGetString(program), + PhGetString(username), + PhGetStringOrEmpty(password), + logonType, + context->ProcessId, + sessionId, + PhGetString(desktopName), + useLinkedToken, + createSuspended + ); + } + } + } + else + { + status = STATUS_INVALID_PARAMETER; + } + + if (password) + { + RtlSecureZeroMemory(password->Buffer, password->Length); + PhDereferenceObject(password); + } + + if (!NT_SUCCESS(status)) + { + if (status != STATUS_CANCELLED) + PhShowStatus(hwndDlg, L"Unable to start the program.", status, 0); + } + else if (status != STATUS_TIMEOUT) + { + PhpAddRunMRUListEntry(program); + + //PhSetStringSetting2(L"RunAsProgram", &program->sr); + PhSetStringSetting2(L"RunAsUserName", &username->sr); + EndDialog(hwndDlg, IDOK); + } + } + break; + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Programs (*.exe;*.pif;*.com;*.bat)", L"*.exe;*.pif;*.com;*.bat" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + PhSetFileDialogFileName(fileDialog, PH_AUTO_T(PH_STRING, PhGetWindowText(context->ProgramComboBoxWindowHandle))->Buffer); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + PPH_STRING fileName; + + fileName = PhGetFileDialogFileName(fileDialog); + PhSetWindowText(context->ProgramComboBoxWindowHandle, fileName->Buffer); + PhDereferenceObject(fileName); + } + + PhFreeFileDialog(fileDialog); + } + break; + case IDC_USERNAME: + { + PPH_STRING username = NULL; + + if (!context->ProcessId && GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + { + username = PH_AUTO(PhGetComboBoxString(context->UserComboBoxWindowHandle, -1)); + } + else if (!context->ProcessId && ( + GET_WM_COMMAND_CMD(wParam, lParam) == CBN_EDITCHANGE || + GET_WM_COMMAND_CMD(wParam, lParam) == CBN_CLOSEUP + )) + { + username = PH_AUTO(PhGetWindowText(context->UserComboBoxWindowHandle)); + } + + if (username) + { + if (IsServiceAccount(username)) + { + EnableWindow(context->PasswordEditWindowHandle, FALSE); + PhSelectComboBoxString(context->TypeComboBoxWindowHandle, L"Service", FALSE); + } + else + { + EnableWindow(context->PasswordEditWindowHandle, TRUE); + PhSelectComboBoxString(context->TypeComboBoxWindowHandle, L"Interactive", FALSE); + } + } + } + break; + } + } + break; + } + + return FALSE; +} + +/** + * Sets the access control lists of the current window station + * and desktop to allow all access. + */ +VOID PhSetDesktopWinStaAccess( + VOID + ) +{ + static SID_IDENTIFIER_AUTHORITY appPackageAuthority = SECURITY_APP_PACKAGE_AUTHORITY; + + HWINSTA wsHandle; + HDESK desktopHandle; + ULONG allocationLength; + PSECURITY_DESCRIPTOR securityDescriptor; + PACL dacl; + CHAR allAppPackagesSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2]; + PSID allAppPackagesSid; + + // TODO: Set security on the correct window station and desktop. + + allAppPackagesSid = (PISID)allAppPackagesSidBuffer; + RtlInitializeSid(allAppPackagesSid, &appPackageAuthority, SECURITY_BUILTIN_APP_PACKAGE_RID_COUNT); + *RtlSubAuthoritySid(allAppPackagesSid, 0) = SECURITY_APP_PACKAGE_BASE_RID; + *RtlSubAuthoritySid(allAppPackagesSid, 1) = SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE; + + // We create a DACL that allows everyone to access everything. + + allocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + + (ULONG)sizeof(ACL) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(&PhSeEveryoneSid) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(allAppPackagesSid); + securityDescriptor = PhAllocate(allocationLength); + dacl = PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); + + RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlCreateAcl(dacl, allocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, &PhSeEveryoneSid); + + if (WindowsVersion >= WINDOWS_8) + { + RtlAddAccessAllowedAce(dacl, ACL_REVISION, GENERIC_ALL, allAppPackagesSid); + } + + RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE); + + if (wsHandle = OpenWindowStation( + L"WinSta0", + FALSE, + WRITE_DAC + )) + { + PhSetObjectSecurity(wsHandle, DACL_SECURITY_INFORMATION, securityDescriptor); + CloseWindowStation(wsHandle); + } + + if (desktopHandle = OpenDesktop( + L"Default", + 0, + FALSE, + WRITE_DAC | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS + )) + { + PhSetObjectSecurity(desktopHandle, DACL_SECURITY_INFORMATION, securityDescriptor); + CloseDesktop(desktopHandle); + } + + PhFree(securityDescriptor); +} + +/** + * Executes the run-as service. + * + * \param Parameters The run-as parameters. + * + * \remarks This function requires administrator-level access. + */ +NTSTATUS PhExecuteRunAsCommand( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ) +{ + NTSTATUS status; + ULONG win32Result; + PPH_STRING applicationFileName; + PPH_STRING commandLine; + SC_HANDLE scManagerHandle; + SC_HANDLE serviceHandle; + PPH_STRING portName; + UNICODE_STRING portNameUs; + ULONG attempts; + + if (!(scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE))) + return PhGetLastWin32ErrorAsNtStatus(); + + if (!(applicationFileName = PhGetApplicationFileName())) + return STATUS_FAIL_CHECK; + + commandLine = PhFormatString(L"\"%s\" -ras \"%s\"", applicationFileName->Buffer, Parameters->ServiceName); + + serviceHandle = CreateService( + scManagerHandle, + Parameters->ServiceName, + Parameters->ServiceName, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_IGNORE, + commandLine->Buffer, + NULL, + NULL, + NULL, + L"LocalSystem", + L"" + ); + win32Result = GetLastError(); + + PhDereferenceObject(commandLine); + PhDereferenceObject(applicationFileName); + + CloseServiceHandle(scManagerHandle); + + if (!serviceHandle) + { + return NTSTATUS_FROM_WIN32(win32Result); + } + + PhSetDesktopWinStaAccess(); + + StartService(serviceHandle, 0, NULL); + DeleteService(serviceHandle); + + portName = PhConcatStrings2(L"\\BaseNamedObjects\\", Parameters->ServiceName); + PhStringRefToUnicodeString(&portName->sr, &portNameUs); + attempts = 50; + + // Try to connect several times because the server may take + // a while to initialize. + do + { + status = PhSvcConnectToServer(&portNameUs, 0); + + if (NT_SUCCESS(status)) + break; + + PhDelayExecution(100); + + } while (--attempts != 0); + + PhDereferenceObject(portName); + + if (NT_SUCCESS(status)) + { + status = PhSvcCallInvokeRunAsService(Parameters); + PhSvcDisconnectFromServer(); + } + + if (serviceHandle) + CloseServiceHandle(serviceHandle); + + return status; +} + +/** + * Starts a program as another user. + * + * \param hWnd A handle to the parent window. + * \param Program The command line of the program to start. + * \param UserName The user to start the program as. The user + * name should be specified as: domain\\name. This parameter + * can be NULL if \a ProcessIdWithToken is specified. + * \param Password The password for the specified user. If there + * is no password, specify an empty string. This parameter + * can be NULL if \a ProcessIdWithToken is specified. + * \param LogonType The logon type for the specified user. This + * parameter can be 0 if \a ProcessIdWithToken is specified. + * \param ProcessIdWithToken The ID of a process from which + * to duplicate the token. + * \param SessionId The ID of the session to run the program + * under. + * \param DesktopName The window station and desktop to run the + * program under. + * \param UseLinkedToken Uses the linked token if possible. + * + * \retval STATUS_CANCELLED The user cancelled the operation. + * + * \remarks This function will cause another instance of + * Process Hacker to be executed if the current security context + * does not have sufficient system access. This is done + * through a UAC elevation prompt. + */ +NTSTATUS PhExecuteRunAsCommand2( + _In_ HWND hWnd, + _In_ PWSTR Program, + _In_opt_ PWSTR UserName, + _In_opt_ PWSTR Password, + _In_opt_ ULONG LogonType, + _In_opt_ HANDLE ProcessIdWithToken, + _In_ ULONG SessionId, + _In_ PWSTR DesktopName, + _In_ BOOLEAN UseLinkedToken + ) +{ + return PhExecuteRunAsCommand3(hWnd, Program, UserName, Password, LogonType, ProcessIdWithToken, SessionId, DesktopName, UseLinkedToken, FALSE); +} + +NTSTATUS PhExecuteRunAsCommand3( + _In_ HWND hWnd, + _In_ PWSTR Program, + _In_opt_ PWSTR UserName, + _In_opt_ PWSTR Password, + _In_opt_ ULONG LogonType, + _In_opt_ HANDLE ProcessIdWithToken, + _In_ ULONG SessionId, + _In_ PWSTR DesktopName, + _In_ BOOLEAN UseLinkedToken, + _In_ BOOLEAN CreateSuspendedProcess + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PH_RUNAS_SERVICE_PARAMETERS parameters; + WCHAR serviceName[32]; + PPH_STRING portName; + UNICODE_STRING portNameUs; + + memset(¶meters, 0, sizeof(PH_RUNAS_SERVICE_PARAMETERS)); + parameters.ProcessId = HandleToUlong(ProcessIdWithToken); + parameters.UserName = UserName; + parameters.Password = Password; + parameters.LogonType = LogonType; + parameters.SessionId = SessionId; + parameters.CommandLine = Program; + parameters.DesktopName = DesktopName; + parameters.UseLinkedToken = UseLinkedToken; + parameters.CreateSuspendedProcess = CreateSuspendedProcess; + + // Try to use an existing instance of the service if possible. + if (RunAsOldServiceName[0] != UNICODE_NULL) + { + PhAcquireQueuedLockExclusive(&RunAsOldServiceLock); + + portName = PhConcatStrings2(L"\\BaseNamedObjects\\", RunAsOldServiceName); + PhStringRefToUnicodeString(&portName->sr, &portNameUs); + + if (NT_SUCCESS(PhSvcConnectToServer(&portNameUs, 0))) + { + parameters.ServiceName = RunAsOldServiceName; + status = PhSvcCallInvokeRunAsService(¶meters); + PhSvcDisconnectFromServer(); + + PhDereferenceObject(portName); + PhReleaseQueuedLockExclusive(&RunAsOldServiceLock); + + return status; + } + + PhDereferenceObject(portName); + PhReleaseQueuedLockExclusive(&RunAsOldServiceLock); + } + + // An existing instance was not available. Proceed normally. + + memcpy(serviceName, L"ProcessHacker", 13 * sizeof(WCHAR)); + PhGenerateRandomAlphaString(&serviceName[13], 16); + PhAcquireQueuedLockExclusive(&RunAsOldServiceLock); + memcpy(RunAsOldServiceName, serviceName, sizeof(serviceName)); + PhReleaseQueuedLockExclusive(&RunAsOldServiceLock); + + parameters.ServiceName = serviceName; + + if (PhGetOwnTokenAttributes().Elevated) + { + status = PhExecuteRunAsCommand(¶meters); + } + else + { + if (PhUiConnectToPhSvc(hWnd, FALSE)) + { + status = PhSvcCallExecuteRunAsCommand(¶meters); + PhUiDisconnectFromPhSvc(); + } + else + { + status = STATUS_CANCELLED; + } + } + + return status; +} + +static VOID PhpSplitUserName( + _In_ PWSTR UserName, + _Out_opt_ PPH_STRING *DomainPart, + _Out_opt_ PPH_STRING *UserPart + ) +{ + PH_STRINGREF userName; + PH_STRINGREF domainPart; + PH_STRINGREF userPart; + + PhInitializeStringRefLongHint(&userName, UserName); + + if (PhSplitStringRefAtChar(&userName, OBJ_NAME_PATH_SEPARATOR, &domainPart, &userPart)) + { + if (DomainPart) + *DomainPart = PhCreateString2(&domainPart); + if (UserPart) + *UserPart = PhCreateString2(&userPart); + } + else + { + if (DomainPart) + *DomainPart = NULL; + if (UserPart) + *UserPart = PhCreateString2(&userName); + } +} + +static VOID SetRunAsServiceStatus( + _In_ ULONG State + ) +{ + SERVICE_STATUS status; + + memset(&status, 0, sizeof(SERVICE_STATUS)); + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = State; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + SetServiceStatus(RunAsServiceStatusHandle, &status); +} + +static ULONG WINAPI RunAsServiceHandlerEx( + _In_ ULONG dwControl, + _In_ ULONG dwEventType, + _In_ PVOID lpEventData, + _In_ PVOID lpContext + ) +{ + switch (dwControl) + { + case SERVICE_CONTROL_STOP: + PhSvcStop(&RunAsServiceStop); + return NO_ERROR; + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + } + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +static VOID WINAPI RunAsServiceMain( + _In_ ULONG dwArgc, + _In_ PWSTR *lpszArgv + ) +{ + PPH_STRING portName; + + memset(&RunAsServiceStop, 0, sizeof(PHSVC_STOP)); + + RunAsServiceStatusHandle = RegisterServiceCtrlHandlerEx(RunAsServiceName->Buffer, RunAsServiceHandlerEx, NULL); + SetRunAsServiceStatus(SERVICE_RUNNING); + + portName = PhConcatStrings2( + L"\\BaseNamedObjects\\", + RunAsServiceName->Buffer + ); + + PhSvcMain(portName, &RunAsServiceStop); + + SetRunAsServiceStatus(SERVICE_STOPPED); +} + +NTSTATUS PhRunAsServiceStart( + _In_ PPH_STRING ServiceName + ) +{ + HANDLE tokenHandle; + SERVICE_TABLE_ENTRY entry; + + // Enable some required privileges. + + if (NT_SUCCESS(PhOpenProcessToken( + NtCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES, + &tokenHandle + ))) + { + PhSetTokenPrivilege2(tokenHandle, SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, SE_PRIVILEGE_ENABLED); + PhSetTokenPrivilege2(tokenHandle, SE_INCREASE_QUOTA_PRIVILEGE, SE_PRIVILEGE_ENABLED); + PhSetTokenPrivilege2(tokenHandle, SE_BACKUP_PRIVILEGE, SE_PRIVILEGE_ENABLED); + PhSetTokenPrivilege2(tokenHandle, SE_RESTORE_PRIVILEGE, SE_PRIVILEGE_ENABLED); + PhSetTokenPrivilege2(tokenHandle, SE_IMPERSONATE_PRIVILEGE, SE_PRIVILEGE_ENABLED); + NtClose(tokenHandle); + } + + RunAsServiceName = ServiceName; + + entry.lpServiceName = ServiceName->Buffer; + entry.lpServiceProc = RunAsServiceMain; + + StartServiceCtrlDispatcher(&entry); + + return STATUS_SUCCESS; +} + +NTSTATUS PhInvokeRunAsService( + _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters + ) +{ + NTSTATUS status; + PPH_STRING domainName; + PPH_STRING userName; + PH_CREATE_PROCESS_AS_USER_INFO createInfo; + ULONG flags; + + if (Parameters->UserName) + { + PhpSplitUserName(Parameters->UserName, &domainName, &userName); + } + else + { + domainName = NULL; + userName = NULL; + } + + memset(&createInfo, 0, sizeof(PH_CREATE_PROCESS_AS_USER_INFO)); + createInfo.ApplicationName = Parameters->FileName; + createInfo.CommandLine = Parameters->CommandLine; + createInfo.CurrentDirectory = Parameters->CurrentDirectory; + createInfo.DomainName = PhGetString(domainName); + createInfo.UserName = PhGetString(userName); + createInfo.Password = Parameters->Password; + createInfo.LogonType = Parameters->LogonType; + createInfo.SessionId = Parameters->SessionId; + createInfo.DesktopName = Parameters->DesktopName; + + flags = PH_CREATE_PROCESS_SET_SESSION_ID; + + if (Parameters->ProcessId) + { + createInfo.ProcessIdWithToken = UlongToHandle(Parameters->ProcessId); + flags |= PH_CREATE_PROCESS_USE_PROCESS_TOKEN; + } + + if (Parameters->UseLinkedToken) + flags |= PH_CREATE_PROCESS_USE_LINKED_TOKEN; + if (Parameters->CreateSuspendedProcess) + flags |= PH_CREATE_PROCESS_SUSPENDED; + + status = PhCreateProcessAsUser( + &createInfo, + flags, + NULL, + NULL, + NULL + ); + + if (domainName) PhDereferenceObject(domainName); + if (userName) PhDereferenceObject(userName); + + return status; +} + +typedef struct _PHP_RUNFILEDLG +{ + HWND WindowHandle; + HWND ComboBoxHandle; + HWND RunAsCheckboxHandle; +} PHP_RUNFILEDLG, *PPHP_RUNFILEDLG; + +PPH_STRING PhpQueryRunFileParentDirectory( + _In_ BOOLEAN Elevated + ) +{ + // Note: Explorer creates new processes with the parent directory as SystemRoot when elevated or + // the below environment variables when not elevated. (dmex) + if (!Elevated) + { + static PH_STRINGREF homeDriveNameSr = PH_STRINGREF_INIT(L"HOMEDRIVE"); + static PH_STRINGREF homePathNameSr = PH_STRINGREF_INIT(L"HOMEPATH"); + PPH_STRING parentDirectoryString = NULL; + PPH_STRING homeDriveNameString = NULL; + PPH_STRING homePathNameString = NULL; + + PhQueryEnvironmentVariable(NULL, &homeDriveNameSr, &homeDriveNameString); + PhQueryEnvironmentVariable(NULL, &homePathNameSr, &homePathNameString); + + if (homeDriveNameString && homePathNameString) + { + parentDirectoryString = PhConcatStringRef2( + &homeDriveNameString->sr, + &homePathNameString->sr + ); + } + + if (homeDriveNameString) + PhDereferenceObject(homeDriveNameString); + if (homePathNameString) + PhDereferenceObject(homePathNameString); + + return parentDirectoryString; + } + else + { + return PhGetSystemDirectory(); + } +} + +NTSTATUS PhpCustomShellExecute( + _In_ HWND hWnd, + _In_ PWSTR FileName, + _In_opt_ PWSTR Parameters, + _In_ BOOLEAN Elevated + ) +{ + NTSTATUS status; + PPH_STRING parentDirectory = NULL; + SHELLEXECUTEINFO info; + + parentDirectory = PhpQueryRunFileParentDirectory(Elevated); + + memset(&info, 0, sizeof(SHELLEXECUTEINFO)); + info.cbSize = sizeof(SHELLEXECUTEINFO); + info.lpFile = FileName; + info.lpParameters = Parameters; + info.lpDirectory = PhGetString(parentDirectory); + info.fMask = SEE_MASK_FLAG_NO_UI; + info.nShow = SW_SHOWNORMAL; + info.hwnd = hWnd; + + if (Elevated) + info.lpVerb = L"runas"; + + if (ShellExecuteEx(&info)) + { + if (info.hProcess) + NtClose(info.hProcess); + + status = STATUS_SUCCESS; + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + if (parentDirectory) + PhDereferenceObject(parentDirectory); + + return status; +} + +BOOLEAN PhpRunFileAsInteractiveUser( + _In_ PPHP_RUNFILEDLG Context, + _In_ PPH_STRING Command + ) +{ + ULONG (WINAPI *WdcRunTaskAsInteractiveUser_I)( + _In_ PWSTR CommandLine, + _In_ PWSTR CurrentDirectory, + _In_ ULONG Reserved + ) = NULL; + BOOLEAN success = FALSE; + PVOID wdcLibraryHandle; + PPH_STRING executeString = NULL; + INT cmdlineArgCount; + PWSTR* cmdlineArgList; + + if (!(wdcLibraryHandle = LoadLibrary(L"wdc.dll"))) + return FALSE; + + if (!(WdcRunTaskAsInteractiveUser_I = PhGetDllBaseProcedureAddress(wdcLibraryHandle, "WdcRunTaskAsInteractiveUser", 0))) + { + FreeLibrary(wdcLibraryHandle); + return FALSE; + } + + // Extract the filename. + if (cmdlineArgList = CommandLineToArgvW(Command->Buffer, &cmdlineArgCount)) + { + PPH_STRING fileName = PhCreateString(cmdlineArgList[0]); + + if (fileName && !PhDoesFileExistsWin32(fileName->Buffer)) + { + PPH_STRING filePathString; + + // The user typed a name without a path so attempt to locate the executable. + if (filePathString = PhSearchFilePath(fileName->Buffer, L".exe")) + PhMoveReference(&fileName, filePathString); + else + PhClearReference(&fileName); + } + + if (fileName) + { + // Escape the filename. + PhMoveReference(&fileName, PhConcatStrings(3, L"\"", fileName->Buffer, L"\"")); + + if (cmdlineArgCount == 2) + { + PPH_STRING fileArgs = PhCreateString(cmdlineArgList[1]); + + // Escape the parameters. + PhMoveReference(&fileArgs, PhConcatStrings(3, L"\"", fileArgs->Buffer, L"\"")); + + // Create the escaped execute string. + PhMoveReference(&executeString, PhConcatStrings(3, fileName->Buffer, L" ", fileArgs->Buffer)); + + // Cleanup. + PhDereferenceObject(fileArgs); + } + else + { + // Create the escaped execute string. + executeString = PhReferenceObject(fileName); + } + + PhDereferenceObject(fileName); + } + + LocalFree(cmdlineArgList); + } + + if (!PhIsNullOrEmptyString(executeString)) + { + PPH_STRING parentDirectory = PhpQueryRunFileParentDirectory(FALSE); + + if (WdcRunTaskAsInteractiveUser_I(executeString->Buffer, PhGetString(parentDirectory), 0) == 0) + { + success = TRUE; + } + + if (parentDirectory) + { + PhDereferenceObject(parentDirectory); + } + } + + if (executeString) PhDereferenceObject(executeString); + FreeLibrary(wdcLibraryHandle); + + return success; +} + +NTSTATUS PhpRunFileProgram( + _In_ PPHP_RUNFILEDLG Context, + _In_ PPH_STRING Command + ) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PPH_STRING commandString = NULL; + PPH_STRING fullFileName = NULL; + PPH_STRING argumentsString = NULL; + PH_STRINGREF fileName; + PH_STRINGREF arguments; + FILE_BASIC_INFORMATION basicInfo; + BOOLEAN isDirectory = FALSE; + + if (PhIsNullOrEmptyString(Command)) + return STATUS_UNSUCCESSFUL; + + if (!(commandString = PhExpandEnvironmentStrings(&Command->sr))) + commandString = PhCreateString2(&Command->sr); + + PhParseCommandLineFuzzy(&commandString->sr, &fileName, &arguments, &fullFileName); + + if (PhIsNullOrEmptyString(fullFileName)) + PhMoveReference(&fullFileName, PhCreateString2(&fileName)); + + if (PhIsNullOrEmptyString(fullFileName)) + { + if (fullFileName) + PhDereferenceObject(fullFileName); + + return STATUS_UNSUCCESSFUL; + } + + if (arguments.Length) + { + argumentsString = PhCreateString2(&arguments); + } + + if (NT_SUCCESS(PhQueryAttributesFileWin32(fullFileName->Buffer, &basicInfo))) + { + isDirectory = !!(basicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); + } + + // If the file doesn't exist its probably a URL with http, https, www (dmex) + if (isDirectory || !PhDoesFileExistsWin32(fullFileName->Buffer)) + { + status = PhpCustomShellExecute( + Context->WindowHandle, + commandString->Buffer, + NULL, + FALSE + ); + } + else if (Button_GetCheck(Context->RunAsCheckboxHandle) == BST_CHECKED || + // The explorer runas dialog executes programs as administrator when holding ctrl/shift keys + // and clicking the OK button, so we'll implement the same functionality. (dmex) + (!!(GetKeyState(VK_CONTROL) < 0 && !!(GetKeyState(VK_SHIFT) < 0)))) + { + status = PhpCustomShellExecute( + Context->WindowHandle, + commandString->Buffer, + NULL, + TRUE + ); + } + else + { + ULONG processId = ULONG_MAX; + PPH_STRING parentDirectory = NULL; + HANDLE processHandle = NULL; + HANDLE newProcessHandle; + HANDLE tokenHandle; + HWND shellWindow; + STARTUPINFOEX startupInfo; + SIZE_T attributeListLength = 0; + PSECURITY_DESCRIPTOR processSecurityDescriptor = NULL; + PSECURITY_DESCRIPTOR tokenSecurityDescriptor = NULL; + PVOID environment = NULL; + ULONG flags = 0; + + memset(&startupInfo, 0, sizeof(STARTUPINFOEX)); + startupInfo.StartupInfo.cb = sizeof(STARTUPINFOEX); + startupInfo.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + startupInfo.StartupInfo.wShowWindow = SW_SHOWNORMAL; + parentDirectory = PhpQueryRunFileParentDirectory(FALSE); + + if (!(shellWindow = GetShellWindow())) + { + if (PhpRunFileAsInteractiveUser(Context, commandString)) + status = STATUS_SUCCESS; + + goto CleanupExit; + } + + GetWindowThreadProcessId(shellWindow, &processId); + + if (processId == ULONG_MAX) + { + status = STATUS_UNSUCCESSFUL; + goto CleanupExit; + } + + status = PhOpenProcess( + &processHandle, + PROCESS_CREATE_PROCESS | (PhGetOwnTokenAttributes().Elevated ? PROCESS_QUERY_LIMITED_INFORMATION | READ_CONTROL : 0), + UlongToHandle(processId) + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + if (!InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + startupInfo.lpAttributeList = PhAllocate(attributeListLength); + + if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, &attributeListLength)) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &processHandle, sizeof(HANDLE), NULL, NULL)) + { + status = PhGetLastWin32ErrorAsNtStatus(); + goto CleanupExit; + } + + if (PhGetOwnTokenAttributes().Elevated) + { + PhGetObjectSecurity( + processHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + &processSecurityDescriptor + ); + } + + if (NT_SUCCESS(PhOpenProcessToken( + processHandle, + TOKEN_QUERY | (PhGetOwnTokenAttributes().Elevated ? READ_CONTROL : 0), + &tokenHandle + ))) + { + if (PhGetOwnTokenAttributes().Elevated) + { + PhGetObjectSecurity( + tokenHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + &tokenSecurityDescriptor + ); + } + + if (CreateEnvironmentBlock && CreateEnvironmentBlock(&environment, tokenHandle, FALSE)) + { + flags |= PH_CREATE_PROCESS_UNICODE_ENVIRONMENT; + } + + NtClose(tokenHandle); + } + + status = PhCreateProcessWin32Ex( + fullFileName->Buffer, + PhGetString(argumentsString), + environment, + PhGetString(parentDirectory), + &startupInfo.StartupInfo, + PH_CREATE_PROCESS_SUSPENDED | PH_CREATE_PROCESS_NEW_CONSOLE | PH_CREATE_PROCESS_EXTENDED_STARTUPINFO | flags, + NULL, + NULL, + &newProcessHandle, + NULL + ); + + if (NT_SUCCESS(status)) + { + PROCESS_BASIC_INFORMATION basicInfo; + + if (PhGetOwnTokenAttributes().Elevated) + { + // Note: This is needed to workaround a severe bug with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS + // where the process and token security descriptors are created without an ACE for the current user, + // owned by the wrong user and with a High-IL when the process token is Medium-IL + // preventing the new process from accessing user/system resources above Low-IL. (dmex) + + if (processSecurityDescriptor) + { + PhSetObjectSecurity( + newProcessHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + processSecurityDescriptor + ); + } + + if (tokenSecurityDescriptor && NT_SUCCESS(PhOpenProcessToken( + newProcessHandle, + WRITE_DAC | WRITE_OWNER, + &tokenHandle + ))) + { + PhSetObjectSecurity( + tokenHandle, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, + tokenSecurityDescriptor + ); + NtClose(tokenHandle); + } + } + + if (NT_SUCCESS(PhGetProcessBasicInformation(newProcessHandle, &basicInfo))) + { + AllowSetForegroundWindow(HandleToUlong(basicInfo.UniqueProcessId)); + } + + NtResumeProcess(newProcessHandle); + + NtClose(newProcessHandle); + } + + CleanupExit: + + if (environment && DestroyEnvironmentBlock) + { + DestroyEnvironmentBlock(environment); + } + + if (tokenSecurityDescriptor) + { + PhFree(tokenSecurityDescriptor); + } + + if (processSecurityDescriptor) + { + PhFree(processSecurityDescriptor); + } + + if (startupInfo.lpAttributeList) + { + DeleteProcThreadAttributeList(startupInfo.lpAttributeList); + PhFree(startupInfo.lpAttributeList); + } + + if (processHandle) + { + NtClose(processHandle); + } + + if (parentDirectory) + { + PhDereferenceObject(parentDirectory); + } + } + + if (fullFileName) PhDereferenceObject(fullFileName); + if (argumentsString) PhDereferenceObject(argumentsString); + if (commandString) PhDereferenceObject(commandString); + + return status; +} + +INT_PTR CALLBACK PhpRunFileWndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_RUNFILEDLG context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocateZero(sizeof(PHP_RUNFILEDLG)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + context->WindowHandle = hwndDlg; + context->ComboBoxHandle = GetDlgItem(hwndDlg, IDC_PROGRAMCOMBO); + context->RunAsCheckboxHandle = GetDlgItem(hwndDlg, IDC_TOGGLEELEVATION); + + PhpAddProgramsToComboBox(context->ComboBoxHandle); + ComboBox_SetCurSel(context->ComboBoxHandle, 0); + + { + COMBOBOXINFO info = { sizeof(COMBOBOXINFO) }; + + if (SendMessage(context->ComboBoxHandle, CB_GETCOMBOBOXINFO, 0, (LPARAM)& info)) + { + if (SHAutoComplete) + SHAutoComplete(info.hwndItem, SHACF_DEFAULT); + } + } + + Button_SetCheck(context->RunAsCheckboxHandle, PhGetIntegerSetting(L"RunFileDlgState") ? TRUE : FALSE); + } + break; + case WM_DESTROY: + { + PhSetIntegerSetting(L"RunFileDlgState", Button_GetCheck(context->RunAsCheckboxHandle) == BST_CHECKED); + + PhFree(context); + } + break; + case WM_CTLCOLORSTATIC: + { + HDC hdc = (HDC)wParam; + + SetBkMode(hdc, TRANSPARENT); + + return (INT_PTR)GetStockBrush(WHITE_BRUSH); + } + break; + case WM_ERASEBKGND: + { + HDC hdc = (HDC)wParam; + RECT clientRect; + + if (!GetClientRect(hwndDlg, &clientRect)) + break; + + SetBkMode(hdc, TRANSPARENT); + + clientRect.bottom -= PH_SCALE_DPI(60); + FillRect(hdc, &clientRect, GetSysColorBrush(COLOR_WINDOW)); + + clientRect.top = clientRect.bottom; + clientRect.bottom = clientRect.top + PH_SCALE_DPI(60); + FillRect(hdc, &clientRect, GetSysColorBrush(COLOR_3DFACE)); + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + } + return TRUE; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + NTSTATUS status; + PPH_STRING commandString; + + if (commandString = PhGetWindowText(context->ComboBoxHandle)) + { + status = PhpRunFileProgram( + context, + commandString + ); + + if (NT_SUCCESS(status)) + { + PhpAddRunMRUListEntry(commandString); + + EndDialog(hwndDlg, IDOK); + } + else + { + if (!(NT_NTWIN32(status) && WIN32_FROM_NTSTATUS(status) == ERROR_CANCELLED)) + { + PhShowStatus(hwndDlg, L"Unable to execute the command.", status, 0); + } + } + + PhDereferenceObject(commandString); + } + } + break; + case IDC_BROWSE: + { + PH_FILETYPE_FILTER filters[] = + { + { L"Executable files (*.exe;*.pif;*.com;*.bat;*.cmd)", L"*.exe;*.pif;*.com;*.bat;*.cmd" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog = PhCreateOpenFileDialog(); + + PhSetFileDialogFilter(fileDialog, filters, RTL_NUMBER_OF(filters)); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + PPH_STRING fileName; + + if (fileName = PhGetFileDialogFileName(fileDialog)) + { + ComboBox_SetText(context->ComboBoxHandle, PhGetString(fileName)); + PhDereferenceObject(fileName); + } + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/sdk/phdk.h b/ProcessHacker/sdk/phdk.h index ddecc15ff419..59303ee4416a 100644 --- a/ProcessHacker/sdk/phdk.h +++ b/ProcessHacker/sdk/phdk.h @@ -1,26 +1,26 @@ -#ifndef _PH_PHDK_H -#define _PH_PHDK_H - -#pragma once - -#define PHNT_VERSION PHNT_WIN7 -#define PHAPPAPI __declspec(dllimport) - -#include "ph.h" -#include "phnet.h" -#include "provider.h" -#include "filestream.h" -#include "fastlock.h" -#include "lsasup.h" -#include "svcsup.h" -#include "circbuf.h" -#include "dltmgr.h" -#include "guisup.h" -#include "treenew.h" -#include "graph.h" -#include "emenu.h" -#include "cpysave.h" - -#include "phapppub.h" - -#endif +#ifndef _PH_PHDK_H +#define _PH_PHDK_H + +#pragma once + +#define PHNT_VERSION PHNT_WIN7 +#define PHAPPAPI __declspec(dllimport) + +#include "ph.h" +#include "phnet.h" +#include "provider.h" +#include "filestream.h" +#include "fastlock.h" +#include "lsasup.h" +#include "svcsup.h" +#include "circbuf.h" +#include "dltmgr.h" +#include "guisup.h" +#include "treenew.h" +#include "graph.h" +#include "emenu.h" +#include "cpysave.h" + +#include "phapppub.h" + +#endif diff --git a/ProcessHacker/sdk/readme.txt b/ProcessHacker/sdk/readme.txt index deae177e4c41..abff41352718 100644 --- a/ProcessHacker/sdk/readme.txt +++ b/ProcessHacker/sdk/readme.txt @@ -1,37 +1,37 @@ -This SDK allows you to create plugins for Process Hacker. - -Doxygen output is supplied in the doc\doxygen directory. -Header files are supplied in the include directory. -Import libraries are supplied in the lib directory. -Samples are supplied in the samples directory. - -The latest version of the Windows SDK is required to build -plugins. - -Add the include directory to your compiler's include paths, -and add the lib\ directory to your compiler's library -paths. Your plugin must be a DLL, and should at minimum use the -libraries ProcessHacker.lib and ntdll.lib. Your plugin should -include the header file in order to gain access to: - -* phlib functions -* Process Hacker application functions -* The Native API - -Some functions are not exported by Process Hacker. If you -receive linker errors, check if the relevant function is -marked with PHLIBAPI or PHAPPAPI; if not, the function -cannot be used by your plugin. - -If you wish to use Native API functions available only on -platforms newer than Windows 7, set PHNT_VERSION to the -appropriate value before including : - - #define PHNT_VERSION PHNT_WIN8 // or PHNT_WIN10 - #include - -To load a plugin, create a directory named "plugins" in the -same directory as ProcessHacker.exe and copy the plugin DLL -file into that directory. Then enable plugins in Options and -restart Process Hacker. Note that plugins will only work if -Process Hacker's executable file is named ProcessHacker.exe. +This SDK allows you to create plugins for Process Hacker. + +Doxygen output is supplied in the doc\doxygen directory. +Header files are supplied in the include directory. +Import libraries are supplied in the lib directory. +Samples are supplied in the samples directory. + +The latest version of the Windows SDK is required to build +plugins. + +Add the include directory to your compiler's include paths, +and add the lib\ directory to your compiler's library +paths. Your plugin must be a DLL, and should at minimum use the +libraries ProcessHacker.lib and ntdll.lib. Your plugin should +include the header file in order to gain access to: + +* phlib functions +* Process Hacker application functions +* The Native API + +Some functions are not exported by Process Hacker. If you +receive linker errors, check if the relevant function is +marked with PHLIBAPI or PHAPPAPI; if not, the function +cannot be used by your plugin. + +If you wish to use Native API functions available only on +platforms newer than Windows 7, set PHNT_VERSION to the +appropriate value before including : + + #define PHNT_VERSION PHNT_WIN8 // or PHNT_WIN10 + #include + +To load a plugin, create a directory named "plugins" in the +same directory as ProcessHacker.exe and copy the plugin DLL +file into that directory. Then enable plugins in Options and +restart Process Hacker. Note that plugins will only work if +Process Hacker's executable file is named ProcessHacker.exe. diff --git a/ProcessHacker/searchbox.c b/ProcessHacker/searchbox.c new file mode 100644 index 000000000000..8dd22570470f --- /dev/null +++ b/ProcessHacker/searchbox.c @@ -0,0 +1,830 @@ +/* + * Process Hacker - + * Searchbox control + * + * Copyright (C) 2012-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + * + */ + +#include +#include +#include +#include +#include + +typedef struct _EDIT_CONTEXT +{ + union + { + ULONG Flags; + struct + { + ULONG ThemeSupport : 1; + ULONG Hot : 1; + ULONG Pushed : 1; + ULONG ColorMode : 8; + ULONG Spare : 21; + }; + }; + + LONG CXWidth; + INT CXBorder; + INT ImageWidth; + INT ImageHeight; + HWND WindowHandle; + WNDPROC DefaultWindowProc; + HFONT WindowFont; + HIMAGELIST ImageListHandle; + HBRUSH BrushNormal; + HBRUSH BrushPushed; + HBRUSH BrushHot; +} EDIT_CONTEXT, *PEDIT_CONTEXT; + +HICON PhpSearchBitmapToIcon( + _In_ HBITMAP BitmapHandle, + _In_ INT Width, + _In_ INT Height + ); + +VOID PhpSearchFreeTheme( + _Inout_ PEDIT_CONTEXT Context + ) +{ + if (Context->BrushNormal) + DeleteBrush(Context->BrushNormal); + if (Context->BrushHot) + DeleteBrush(Context->BrushHot); + if (Context->BrushPushed) + DeleteBrush(Context->BrushPushed); +} + +VOID PhpSearchInitializeFont( + _Inout_ PEDIT_CONTEXT Context + ) +{ + if (Context->WindowFont) + DeleteFont(Context->WindowFont); + + Context->WindowFont = PhCreateCommonFont(10, FW_MEDIUM, Context->WindowHandle); +} + +VOID PhpSearchInitializeTheme( + _Inout_ PEDIT_CONTEXT Context + ) +{ + Context->CXWidth = PH_SCALE_DPI(20); + Context->BrushNormal = GetSysColorBrush(COLOR_WINDOW); + Context->BrushHot = CreateSolidBrush(RGB(205, 232, 255)); + Context->BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); + Context->ColorMode = PhGetIntegerSetting(L"GraphColorMode"); + + if (IsThemeActive()) + { + HTHEME themeDataHandle; + + if (themeDataHandle = OpenThemeData(Context->WindowHandle, VSCLASS_EDIT)) + { + //IsThemePartDefined_I(themeDataHandle, EP_EDITBORDER_NOSCROLL, EPSHV_NORMAL); + + if (!SUCCEEDED(GetThemeInt( + themeDataHandle, + EP_EDITBORDER_NOSCROLL, + EPSHV_NORMAL, + TMT_BORDERSIZE, + &Context->CXBorder + ))) + { + Context->CXBorder = GetSystemMetrics(SM_CXBORDER) * 2; + } + + CloseThemeData(themeDataHandle); + } + else + { + Context->CXBorder = GetSystemMetrics(SM_CXBORDER) * 2; + } + } + else + { + Context->CXBorder = GetSystemMetrics(SM_CXBORDER) * 2; + } +} + +VOID PhpSearchInitializeImages( + _Inout_ PEDIT_CONTEXT Context + ) +{ + HBITMAP bitmap; + + Context->ImageWidth = GetSystemMetrics(SM_CXSMICON) + 4; + Context->ImageHeight = GetSystemMetrics(SM_CYSMICON) + 4; + Context->ImageListHandle = ImageList_Create( + Context->ImageWidth, + Context->ImageHeight, + ILC_COLOR32, + 2, + 0 + ); + ImageList_SetImageCount(Context->ImageListHandle, 2); + + if (bitmap = PhLoadPngImageFromResource(PhInstanceHandle, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE), TRUE)) + { + ImageList_Replace(Context->ImageListHandle, 0, bitmap, NULL); + DeleteBitmap(bitmap); + } + else + { + PhSetImageListBitmap(Context->ImageListHandle, 0, PhInstanceHandle, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE_BMP)); + } + + if (bitmap = PhLoadPngImageFromResource(PhInstanceHandle, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE), TRUE)) + { + ImageList_Replace(Context->ImageListHandle, 1, bitmap, NULL); + DeleteBitmap(bitmap); + } + else + { + PhSetImageListBitmap(Context->ImageListHandle, 1, PhInstanceHandle, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE_BMP)); + } +} + +VOID PhpSearchGetButtonRect( + _Inout_ PEDIT_CONTEXT Context, + _Inout_ PRECT ButtonRect + ) +{ + ButtonRect->left = (ButtonRect->right - Context->CXWidth) - Context->CXBorder - 1; // offset left border by 1 + ButtonRect->bottom -= Context->CXBorder; + ButtonRect->right -= Context->CXBorder; + ButtonRect->top += Context->CXBorder; +} + +VOID PhpSearchDrawButton( + _Inout_ PEDIT_CONTEXT Context, + _In_ RECT WindowRect, + _In_ RECT ButtonRect + ) +{ + HDC hdc; + HDC bufferDc; + HBITMAP bufferBitmap; + HBITMAP oldBufferBitmap; + RECT bufferRect = + { + 0, 0, + ButtonRect.right - ButtonRect.left, + ButtonRect.bottom - ButtonRect.top + }; + + if (!(hdc = GetWindowDC(Context->WindowHandle))) + return; + + bufferDc = CreateCompatibleDC(hdc); + bufferBitmap = CreateCompatibleBitmap(hdc, bufferRect.right, bufferRect.bottom); + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); + + if (Context->Pushed) + { + if (Context->ThemeSupport) + { + switch (Context->ColorMode) + { + case 0: // New colors + FillRect(bufferDc, &bufferRect, Context->BrushPushed); + break; + case 1: // Old colors + SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(bufferDc, RGB(99, 99, 99)); + FillRect(bufferDc, &bufferRect, GetStockBrush(DC_BRUSH)); + break; + } + } + else + { + FillRect(bufferDc, &bufferRect, Context->BrushPushed); + //FrameRect(bufferDc, &bufferRect, CreateSolidBrush(RGB(0xff, 0, 0))); + } + } + else if (Context->Hot) + { + if (Context->ThemeSupport) + { + switch (Context->ColorMode) + { + case 0: // New colors + FillRect(bufferDc, &bufferRect, Context->BrushHot); + break; + case 1: // Old colors + SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(bufferDc, RGB(78, 78, 78)); + FillRect(bufferDc, &bufferRect, GetStockBrush(DC_BRUSH)); + break; + } + } + else + { + FillRect(bufferDc, &bufferRect, Context->BrushHot); + //FrameRect(bufferDc, &bufferRect, CreateSolidBrush(RGB(38, 160, 218))); + } + } + else + { + if (Context->ThemeSupport) + { + switch (Context->ColorMode) + { + case 0: // New colors + FillRect(bufferDc, &bufferRect, Context->BrushNormal); + break; + case 1: // Old colors + SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(bufferDc, RGB(60, 60, 60)); + FillRect(bufferDc, &bufferRect, GetStockBrush(DC_BRUSH)); + break; + } + + } + else + { + FillRect(bufferDc, &bufferRect, Context->BrushNormal); + } + } + + if (Edit_GetTextLength(Context->WindowHandle) > 0) + { + ImageList_Draw( + Context->ImageListHandle, + 0, + bufferDc, + bufferRect.left + 1, // offset + bufferRect.top, + ILD_TRANSPARENT + ); + } + else + { + ImageList_Draw( + Context->ImageListHandle, + 1, + bufferDc, + bufferRect.left + 2, // offset + bufferRect.top + 1, // offset + ILD_TRANSPARENT + ); + } + + BitBlt(hdc, ButtonRect.left, ButtonRect.top, ButtonRect.right, ButtonRect.bottom, bufferDc, 0, 0, SRCCOPY); + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); + DeleteDC(bufferDc); + + if (Context->ThemeSupport) // HACK + { + if (GetFocus() == Context->WindowHandle) + { + switch (Context->ColorMode) + { + case 0: // New colors + SetDCBrushColor(hdc, RGB(0, 0, 0)); + break; + case 1: // Old colors + SetDCBrushColor(hdc, RGB(65, 65, 65)); + break; + } + + SelectBrush(hdc, GetStockBrush(DC_BRUSH)); + PatBlt(hdc, WindowRect.left, WindowRect.top, 1, WindowRect.bottom - WindowRect.top, PATCOPY); + PatBlt(hdc, WindowRect.right - 1, WindowRect.top, 1, WindowRect.bottom - WindowRect.top, PATCOPY); + PatBlt(hdc, WindowRect.left, WindowRect.top, WindowRect.right - WindowRect.left, 1, PATCOPY); + PatBlt(hdc, WindowRect.left, WindowRect.bottom - 1, WindowRect.right - WindowRect.left, 1, PATCOPY); + + switch (Context->ColorMode) + { + case 0: // New colors + SetDCBrushColor(hdc, RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SetDCBrushColor(hdc, RGB(60, 60, 60)); + break; + } + + SelectBrush(hdc, GetStockBrush(DC_BRUSH)); + PatBlt(hdc, WindowRect.left + 1, WindowRect.top + 1, 1, WindowRect.bottom - WindowRect.top - 2, PATCOPY); + PatBlt(hdc, WindowRect.right - 2, WindowRect.top + 1, 1, WindowRect.bottom - WindowRect.top - 2, PATCOPY); + PatBlt(hdc, WindowRect.left + 1, WindowRect.top + 1, WindowRect.right - WindowRect.left - 2, 1, PATCOPY); + PatBlt(hdc, WindowRect.left + 1, WindowRect.bottom - 2, WindowRect.right - WindowRect.left - 2, 1, PATCOPY); + } + else + { + switch (Context->ColorMode) + { + case 0: // New colors + SetDCBrushColor(hdc, RGB(0, 0, 0)); + break; + case 1: // Old colors + SetDCBrushColor(hdc, RGB(65, 65, 65)); + break; + } + + SelectBrush(hdc, GetStockBrush(DC_BRUSH)); + PatBlt(hdc, WindowRect.left, WindowRect.top, 1, WindowRect.bottom - WindowRect.top, PATCOPY); + PatBlt(hdc, WindowRect.right - 1, WindowRect.top, 1, WindowRect.bottom - WindowRect.top, PATCOPY); + PatBlt(hdc, WindowRect.left, WindowRect.top, WindowRect.right - WindowRect.left, 1, PATCOPY); + PatBlt(hdc, WindowRect.left, WindowRect.bottom - 1, WindowRect.right - WindowRect.left, 1, PATCOPY); + + switch (Context->ColorMode) + { + case 0: // New colors + SetDCBrushColor(hdc, RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SetDCBrushColor(hdc, RGB(60, 60, 60)); + break; + } + + SelectBrush(hdc, GetStockBrush(DC_BRUSH)); + PatBlt(hdc, WindowRect.left + 1, WindowRect.top + 1, 1, WindowRect.bottom - WindowRect.top - 2, PATCOPY); + PatBlt(hdc, WindowRect.right - 2, WindowRect.top + 1, 1, WindowRect.bottom - WindowRect.top - 2, PATCOPY); + PatBlt(hdc, WindowRect.left + 1, WindowRect.top + 1, WindowRect.right - WindowRect.left - 2, 1, PATCOPY); + PatBlt(hdc, WindowRect.left + 1, WindowRect.bottom - 2, WindowRect.right - WindowRect.left - 2, 1, PATCOPY); + } + } + + ReleaseDC(Context->WindowHandle, hdc); +} + +LRESULT CALLBACK PhpSearchWndSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PEDIT_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hWnd, SHRT_MAX))) + return 0; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + PhpSearchFreeTheme(context); + + if (context->WindowFont) + { + DeleteFont(context->WindowFont); + context->WindowFont = NULL; + } + + if (context->ImageListHandle) + { + ImageList_Destroy(context->ImageListHandle); + context->ImageListHandle = NULL; + } + + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, SHRT_MAX); + PhFree(context); + } + break; + case WM_ERASEBKGND: + return 1; + case WM_NCCALCSIZE: + { + LPNCCALCSIZE_PARAMS ncCalcSize = (NCCALCSIZE_PARAMS*)lParam; + + // Let Windows handle the non-client defaults. + CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); + + // Deflate the client area to accommodate the custom button. + ncCalcSize->rgrc[0].right -= context->CXWidth; + } + return 0; + case WM_NCPAINT: + { + RECT windowRect; + RECT buttonRect; + + // Let Windows handle the non-client defaults. + CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); + + // Get the screen coordinates of the window. + GetWindowRect(hWnd, &windowRect); + + // Adjust the coordinates (start from 0,0). + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + buttonRect = windowRect; + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &buttonRect); + + // Draw the button. + PhpSearchDrawButton(context, windowRect, buttonRect); + } + return 0; + case WM_NCHITTEST: + { + POINT windowPoint; + RECT windowRect; + + // Get the screen coordinates of the mouse. + if (!GetCursorPos(&windowPoint)) + break; + + // Get the screen coordinates of the window. + GetWindowRect(hWnd, &windowRect); + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &windowRect); + + // Check that the mouse is within the inserted button. + if (PtInRect(&windowRect, windowPoint)) + { + return HTBORDER; + } + } + break; + case WM_NCLBUTTONDOWN: + { + POINT windowPoint; + RECT windowRect; + + // Get the screen coordinates of the mouse. + if (!GetCursorPos(&windowPoint)) + break; + + // Get the screen coordinates of the window. + GetWindowRect(hWnd, &windowRect); + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &windowRect); + + // Check that the mouse is within the inserted button. + if (PtInRect(&windowRect, windowPoint)) + { + context->Pushed = TRUE; + + SetCapture(hWnd); + + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + } + } + break; + case WM_LBUTTONUP: + { + POINT windowPoint; + RECT windowRect; + + // Get the screen coordinates of the mouse. + if (!GetCursorPos(&windowPoint)) + break; + + // Get the screen coordinates of the window. + GetWindowRect(hWnd, &windowRect); + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &windowRect); + + // Check that the mouse is within the inserted button. + if (PtInRect(&windowRect, windowPoint)) + { + // Forward click notification. + //SendMessage(GetParent(context->WindowHandle), WM_COMMAND, MAKEWPARAM(context->CommandID, BN_CLICKED), 0); + + SetFocus(hWnd); + Static_SetText(hWnd, L""); + } + + if (GetCapture() == hWnd) + { + context->Pushed = FALSE; + ReleaseCapture(); + } + + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + } + break; + case WM_CUT: + case WM_CLEAR: + case WM_PASTE: + case WM_UNDO: + case WM_KEYUP: + case WM_SETTEXT: + case WM_KILLFOCUS: + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + break; + case WM_SETTINGCHANGE: + case WM_SYSCOLORCHANGE: + case WM_THEMECHANGED: + { + PhpSearchFreeTheme(context); + PhpSearchInitializeTheme(context); + PhpSearchInitializeFont(context); + + // Reset the client area margins. + SendMessage(hWnd, EM_SETMARGINS, EC_LEFTMARGIN, MAKELPARAM(0, 0)); + + // Refresh the non-client area. + SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + + // Force the edit control to update its non-client area. + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + } + break; + case WM_NCMOUSEMOVE: + { + POINT windowPoint; + RECT windowRect; + + // Get the screen coordinates of the mouse. + if (!GetCursorPos(&windowPoint)) + break; + + // Get the screen coordinates of the window. + GetWindowRect(hWnd, &windowRect); + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &windowRect); + + // Check that the mouse is within the inserted button. + if (PtInRect(&windowRect, windowPoint) && !context->Hot) + { + TRACKMOUSEEVENT trackMouseEvent; + + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE | TME_NONCLIENT; + trackMouseEvent.hwndTrack = hWnd; + trackMouseEvent.dwHoverTime = 0; + + context->Hot = TRUE; + + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + + TrackMouseEvent(&trackMouseEvent); + } + } + break; + case WM_NCMOUSELEAVE: + { + if (context->Hot) + { + context->Hot = FALSE; + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + } + } + break; + case WM_MOUSEMOVE: + { + if ((wParam & MK_LBUTTON) && GetCapture() == hWnd) + { + POINT windowPoint; + RECT windowRect; + + // Get the screen coordinates of the mouse. + if (!GetCursorPos(&windowPoint)) + break; + + // Get the screen coordinates of the window. + GetWindowRect(hWnd, &windowRect); + + // Get the position of the inserted button. + PhpSearchGetButtonRect(context, &windowRect); + + // Check that the mouse is within the inserted button. + context->Pushed = PtInRect(&windowRect, windowPoint); + + RedrawWindow(hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE); + } + } + break; + } + + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); +} + +HICON PhpSearchBitmapToIcon( + _In_ HBITMAP BitmapHandle, + _In_ INT Width, + _In_ INT Height + ) +{ + HICON icon; + HDC screenDc; + HBITMAP screenBitmap; + ICONINFO iconInfo = { 0 }; + + screenDc = GetDC(NULL); + screenBitmap = CreateCompatibleBitmap(screenDc, Width, Height); + + iconInfo.fIcon = TRUE; + iconInfo.hbmColor = BitmapHandle; + iconInfo.hbmMask = screenBitmap; + + icon = CreateIconIndirect(&iconInfo); + + DeleteBitmap(screenBitmap); + ReleaseDC(NULL, screenDc); + + return icon; +} + +VOID PhCreateSearchControl( + _In_ HWND Parent, + _In_ HWND WindowHandle, + _In_opt_ PWSTR BannerText + ) +{ + PEDIT_CONTEXT context; + + context = (PEDIT_CONTEXT)PhAllocate(sizeof(EDIT_CONTEXT)); + memset(context, 0, sizeof(EDIT_CONTEXT)); + + context->WindowHandle = WindowHandle; + context->ThemeSupport = !!PhGetIntegerSetting(L"EnableThemeSupport"); // HACK + context->ColorMode = PhGetIntegerSetting(L"GraphColorMode"); + + //PhpSearchInitializeTheme(context); + PhpSearchInitializeImages(context); + + // Set initial text + if (BannerText) + Edit_SetCueBannerText(WindowHandle, BannerText); + + // Subclass the Edit control window procedure. + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); + PhSetWindowContext(WindowHandle, SHRT_MAX, context); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhpSearchWndSubclassProc); + + // Initialize the theme parameters. + SendMessage(WindowHandle, WM_THEMECHANGED, 0, 0); +} + +HBITMAP PhLoadPngImageFromResource( + _In_ PVOID DllBase, + _In_ UINT Width, + _In_ UINT Height, + _In_ PCWSTR Name, + _In_ BOOLEAN RGBAImage + ) +{ + BOOLEAN success = FALSE; + UINT frameCount = 0; + ULONG resourceLength = 0; + WICInProcPointer resourceBuffer = NULL; + HDC screenHdc = NULL; + HDC bufferDc = NULL; + BITMAPINFO bitmapInfo = { 0 }; + HBITMAP bitmapHandle = NULL; + PVOID bitmapBuffer = NULL; + IWICStream* wicStream = NULL; + IWICBitmapSource* wicBitmapSource = NULL; + IWICBitmapDecoder* wicDecoder = NULL; + IWICBitmapFrameDecode* wicFrame = NULL; + IWICImagingFactory* wicFactory = NULL; + IWICBitmapScaler* wicScaler = NULL; + WICPixelFormatGUID pixelFormat; + WICRect rect = { 0, 0, Width, Height }; + + // Create the ImagingFactory + if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, &wicFactory))) + goto CleanupExit; + + // Load the resource + if (!PhLoadResource(DllBase, Name, L"PNG", &resourceLength, &resourceBuffer)) + goto CleanupExit; + + // Create the Stream + if (FAILED(IWICImagingFactory_CreateStream(wicFactory, &wicStream))) + goto CleanupExit; + + // Initialize the Stream from Memory + if (FAILED(IWICStream_InitializeFromMemory(wicStream, resourceBuffer, resourceLength))) + goto CleanupExit; + + if (FAILED(IWICImagingFactory_CreateDecoder(wicFactory, &GUID_ContainerFormatPng, NULL, &wicDecoder))) + goto CleanupExit; + + if (FAILED(IWICBitmapDecoder_Initialize(wicDecoder, (IStream*)wicStream, WICDecodeMetadataCacheOnLoad))) + goto CleanupExit; + + // Get the Frame count + if (FAILED(IWICBitmapDecoder_GetFrameCount(wicDecoder, &frameCount)) || frameCount < 1) + goto CleanupExit; + + // Get the Frame + if (FAILED(IWICBitmapDecoder_GetFrame(wicDecoder, 0, &wicFrame))) + goto CleanupExit; + + // Get the WicFrame image format + if (FAILED(IWICBitmapFrameDecode_GetPixelFormat(wicFrame, &pixelFormat))) + goto CleanupExit; + + // Check if the image format is supported: + if (IsEqualGUID(&pixelFormat, RGBAImage ? &GUID_WICPixelFormat32bppPRGBA : &GUID_WICPixelFormat32bppPBGRA)) + { + wicBitmapSource = (IWICBitmapSource*)wicFrame; + } + else + { + IWICFormatConverter* wicFormatConverter = NULL; + + if (FAILED(IWICImagingFactory_CreateFormatConverter(wicFactory, &wicFormatConverter))) + goto CleanupExit; + + if (FAILED(IWICFormatConverter_Initialize( + wicFormatConverter, + (IWICBitmapSource*)wicFrame, + RGBAImage ? &GUID_WICPixelFormat32bppPRGBA : &GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, + NULL, + 0.0, + WICBitmapPaletteTypeCustom + ))) + { + IWICFormatConverter_Release(wicFormatConverter); + goto CleanupExit; + } + + // Convert the image to the correct format: + IWICFormatConverter_QueryInterface(wicFormatConverter, &IID_IWICBitmapSource, &wicBitmapSource); + + // Cleanup the converter. + IWICFormatConverter_Release(wicFormatConverter); + + // Dispose the old frame now that the converted frame is in wicBitmapSource. + IWICBitmapFrameDecode_Release(wicFrame); + } + + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = rect.Width; + bitmapInfo.bmiHeader.biHeight = -((LONG)rect.Height); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + screenHdc = GetDC(NULL); + bufferDc = CreateCompatibleDC(screenHdc); + bitmapHandle = CreateDIBSection(screenHdc, &bitmapInfo, DIB_RGB_COLORS, &bitmapBuffer, NULL, 0); + + // Check if it's the same rect as the requested size. + //if (width != rect.Width || height != rect.Height) + if (FAILED(IWICImagingFactory_CreateBitmapScaler(wicFactory, &wicScaler))) + goto CleanupExit; + if (FAILED(IWICBitmapScaler_Initialize(wicScaler, wicBitmapSource, rect.Width, rect.Height, WICBitmapInterpolationModeFant))) + goto CleanupExit; + if (FAILED(IWICBitmapScaler_CopyPixels(wicScaler, &rect, rect.Width * 4, rect.Width * rect.Height * 4, (PBYTE)bitmapBuffer))) + goto CleanupExit; + + success = TRUE; + +CleanupExit: + + if (wicScaler) + IWICBitmapScaler_Release(wicScaler); + + if (bufferDc) + DeleteDC(bufferDc); + + if (screenHdc) + ReleaseDC(NULL, screenHdc); + + if (wicBitmapSource) + IWICBitmapSource_Release(wicBitmapSource); + + if (wicStream) + IWICStream_Release(wicStream); + + if (wicDecoder) + IWICBitmapDecoder_Release(wicDecoder); + + if (wicFactory) + IWICImagingFactory_Release(wicFactory); + + if (resourceBuffer) + PhFree(resourceBuffer); + + if (success) + return bitmapHandle; + + DeleteBitmap(bitmapHandle); + return NULL; +} diff --git a/ProcessHacker/sessmsg.c b/ProcessHacker/sessmsg.c index 776f8d1995d5..053e2751c88e 100644 --- a/ProcessHacker/sessmsg.c +++ b/ProcessHacker/sessmsg.c @@ -1,160 +1,163 @@ -/* - * Process Hacker - - * send message window - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -#define SIP(String, Integer) { (String), (PVOID)(Integer) } - -static PH_KEY_VALUE_PAIR PhpMessageBoxIconPairs[] = -{ - SIP(L"None", 0), - SIP(L"Information", MB_ICONINFORMATION), - SIP(L"Warning", MB_ICONWARNING), - SIP(L"Error", MB_ICONERROR), - SIP(L"Question", MB_ICONQUESTION) -}; - -INT_PTR CALLBACK PhpSessionSendMessageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowSessionSendMessageDialog( - _In_ HWND ParentWindowHandle, - _In_ ULONG SessionId - ) -{ - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_EDITMESSAGE), - ParentWindowHandle, - PhpSessionSendMessageDlgProc, - (LPARAM)SessionId - ); -} - -INT_PTR CALLBACK PhpSessionSendMessageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND iconComboBox; - - SetProp(hwndDlg, L"SessionId", UlongToHandle((ULONG)lParam)); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - iconComboBox = GetDlgItem(hwndDlg, IDC_TYPE); - - ComboBox_AddString(iconComboBox, L"None"); - ComboBox_AddString(iconComboBox, L"Information"); - ComboBox_AddString(iconComboBox, L"Warning"); - ComboBox_AddString(iconComboBox, L"Error"); - ComboBox_AddString(iconComboBox, L"Question"); - PhSelectComboBoxString(iconComboBox, L"None", FALSE); - - if (PhCurrentUserName) - { - SetDlgItemText( - hwndDlg, - IDC_TITLE, - PhaFormatString(L"Message from %s", PhCurrentUserName->Buffer)->Buffer - ); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_TEXT), TRUE); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, L"SessionId"); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - ULONG sessionId = HandleToUlong(GetProp(hwndDlg, L"SessionId")); - PPH_STRING title; - PPH_STRING text; - ULONG icon = 0; - ULONG64 timeout = 0; - ULONG response; - - title = PhaGetDlgItemText(hwndDlg, IDC_TITLE); - text = PhaGetDlgItemText(hwndDlg, IDC_TEXT); - - PhFindIntegerSiKeyValuePairs( - PhpMessageBoxIconPairs, - sizeof(PhpMessageBoxIconPairs), - PhaGetDlgItemText(hwndDlg, IDC_TYPE)->Buffer, - &icon - ); - PhStringToInteger64( - &PhaGetDlgItemText(hwndDlg, IDC_TIMEOUT)->sr, - 10, - &timeout - ); - - if (WinStationSendMessageW( - NULL, - sessionId, - title->Buffer, - (ULONG)title->Length, - text->Buffer, - (ULONG)text->Length, - icon, - (ULONG)timeout, - &response, - TRUE - )) - { - EndDialog(hwndDlg, IDOK); - } - else - { - PhShowStatus(hwndDlg, L"Unable to send the message", 0, GetLastError()); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * send message window + * + * Copyright (C) 2010-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#define SIP(String, Integer) { (String), (PVOID)(Integer) } + +static PH_KEY_VALUE_PAIR PhpMessageBoxIconPairs[] = +{ + SIP(L"None", 0), + SIP(L"Information", MB_ICONINFORMATION), + SIP(L"Warning", MB_ICONWARNING), + SIP(L"Error", MB_ICONERROR), + SIP(L"Question", MB_ICONQUESTION) +}; + +INT_PTR CALLBACK PhpSessionSendMessageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowSessionSendMessageDialog( + _In_ HWND ParentWindowHandle, + _In_ ULONG SessionId + ) +{ + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_EDITMESSAGE), + ParentWindowHandle, + PhpSessionSendMessageDlgProc, + (LPARAM)SessionId + ); +} + +INT_PTR CALLBACK PhpSessionSendMessageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND iconComboBox; + PPH_STRING currentUserName; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr((ULONG)lParam)); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + iconComboBox = GetDlgItem(hwndDlg, IDC_TYPE); + + ComboBox_AddString(iconComboBox, L"None"); + ComboBox_AddString(iconComboBox, L"Information"); + ComboBox_AddString(iconComboBox, L"Warning"); + ComboBox_AddString(iconComboBox, L"Error"); + ComboBox_AddString(iconComboBox, L"Question"); + PhSelectComboBoxString(iconComboBox, L"None", FALSE); + + if (currentUserName = PhGetTokenUserString(PhGetOwnTokenAttributes().TokenHandle, TRUE)) + { + PhSetDialogItemText( + hwndDlg, + IDC_TITLE, + PhaFormatString(L"Message from %s", currentUserName->Buffer)->Buffer + ); + PhDereferenceObject(currentUserName); + } + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_TEXT)); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + ULONG sessionId = PtrToUlong(PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT)); + PPH_STRING title; + PPH_STRING text; + ULONG icon = 0; + ULONG64 timeout = 0; + ULONG response; + + title = PhaGetDlgItemText(hwndDlg, IDC_TITLE); + text = PhaGetDlgItemText(hwndDlg, IDC_TEXT); + + PhFindIntegerSiKeyValuePairs( + PhpMessageBoxIconPairs, + sizeof(PhpMessageBoxIconPairs), + PhaGetDlgItemText(hwndDlg, IDC_TYPE)->Buffer, + &icon + ); + PhStringToInteger64( + &PhaGetDlgItemText(hwndDlg, IDC_TIMEOUT)->sr, + 10, + &timeout + ); + + if (WinStationSendMessageW( + NULL, + sessionId, + title->Buffer, + (ULONG)title->Length, + text->Buffer, + (ULONG)text->Length, + icon, + (ULONG)timeout, + &response, + TRUE + )) + { + EndDialog(hwndDlg, IDOK); + } + else + { + PhShowStatus(hwndDlg, L"Unable to send the message", 0, GetLastError()); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/sessprp.c b/ProcessHacker/sessprp.c index 60a1aee807f2..b90e878587a2 100644 --- a/ProcessHacker/sessprp.c +++ b/ProcessHacker/sessprp.c @@ -1,237 +1,231 @@ -/* - * Process Hacker - - * session properties - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -INT_PTR CALLBACK PhpSessionPropertiesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -#define SIP(String, Integer) { (String), (PVOID)(Integer) } - -static PH_KEY_VALUE_PAIR PhpConnectStatePairs[] = -{ - SIP(L"Active", State_Active), - SIP(L"Connected", State_Connected), - SIP(L"ConnectQuery", State_ConnectQuery), - SIP(L"Shadow", State_Shadow), - SIP(L"Disconnected", State_Disconnected), - SIP(L"Idle", State_Idle), - SIP(L"Listen", State_Listen), - SIP(L"Reset", State_Reset), - SIP(L"Down", State_Down), - SIP(L"Init", State_Init) -}; - -VOID PhShowSessionProperties( - _In_ HWND ParentWindowHandle, - _In_ ULONG SessionId - ) -{ - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_SESSION), - ParentWindowHandle, - PhpSessionPropertiesDlgProc, - (LPARAM)SessionId - ); -} - -INT_PTR CALLBACK PhpSessionPropertiesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG sessionId = (ULONG)lParam; - WINSTATIONINFORMATION winStationInfo; - BOOLEAN haveWinStationInfo; - WINSTATIONCLIENT clientInfo; - BOOLEAN haveClientInfo; - ULONG returnLength; - PWSTR stateString; - - SetProp(hwndDlg, L"SessionId", UlongToHandle(sessionId)); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - // Query basic session information - - haveWinStationInfo = WinStationQueryInformationW( - NULL, - sessionId, - WinStationInformation, - &winStationInfo, - sizeof(WINSTATIONINFORMATION), - &returnLength - ); - - // Query client information - - haveClientInfo = WinStationQueryInformationW( - NULL, - sessionId, - WinStationClient, - &clientInfo, - sizeof(WINSTATIONCLIENT), - &returnLength - ); - - if (haveWinStationInfo) - { - SetDlgItemText(hwndDlg, IDC_USERNAME, - PhaFormatString(L"%s\\%s", winStationInfo.Domain, winStationInfo.UserName)->Buffer); - } - - SetDlgItemInt(hwndDlg, IDC_SESSIONID, sessionId, FALSE); - - if (haveWinStationInfo) - { - if (PhFindStringSiKeyValuePairs( - PhpConnectStatePairs, - sizeof(PhpConnectStatePairs), - winStationInfo.ConnectState, - &stateString - )) - { - SetDlgItemText(hwndDlg, IDC_STATE, stateString); - } - } - - if (haveWinStationInfo && winStationInfo.LogonTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - PPH_STRING time; - - PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.LogonTime); - time = PhFormatDateTime(&systemTime); - SetDlgItemText(hwndDlg, IDC_LOGONTIME, time->Buffer); - PhDereferenceObject(time); - } - - if (haveWinStationInfo && winStationInfo.ConnectTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - PPH_STRING time; - - PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.ConnectTime); - time = PhFormatDateTime(&systemTime); - SetDlgItemText(hwndDlg, IDC_CONNECTTIME, time->Buffer); - PhDereferenceObject(time); - } - - if (haveWinStationInfo && winStationInfo.DisconnectTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - PPH_STRING time; - - PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.DisconnectTime); - time = PhFormatDateTime(&systemTime); - SetDlgItemText(hwndDlg, IDC_DISCONNECTTIME, time->Buffer); - PhDereferenceObject(time); - } - - if (haveWinStationInfo && winStationInfo.LastInputTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - PPH_STRING time; - - PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.LastInputTime); - time = PhFormatDateTime(&systemTime); - SetDlgItemText(hwndDlg, IDC_LASTINPUTTIME, time->Buffer); - PhDereferenceObject(time); - } - - if (haveClientInfo && clientInfo.ClientName[0] != 0) - { - WCHAR addressString[65]; - - SetDlgItemText(hwndDlg, IDC_CLIENTNAME, clientInfo.ClientName); - - if (clientInfo.ClientAddressFamily == AF_INET6) - { - struct in6_addr address; - ULONG i; - PUSHORT in; - PUSHORT out; - - // IPv6 is special - the client address data is a reversed version of - // the real address. - - in = (PUSHORT)clientInfo.ClientAddress; - out = (PUSHORT)address.u.Word; - - for (i = 8; i != 0; i--) - { - *out = _byteswap_ushort(*in); - in++; - out++; - } - - RtlIpv6AddressToString(&address, addressString); - } - else - { - wcscpy_s(addressString, 65, clientInfo.ClientAddress); - } - - SetDlgItemText(hwndDlg, IDC_CLIENTADDRESS, addressString); - - SetDlgItemText(hwndDlg, IDC_CLIENTDISPLAY, - PhaFormatString(L"%ux%u@%u", clientInfo.HRes, - clientInfo.VRes, clientInfo.ColorDepth)->Buffer - ); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, L"SessionId"); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * session properties + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include + +INT_PTR CALLBACK PhpSessionPropertiesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#define SIP(String, Integer) { (String), (PVOID)(Integer) } + +static PH_KEY_VALUE_PAIR PhpConnectStatePairs[] = +{ + SIP(L"Active", State_Active), + SIP(L"Connected", State_Connected), + SIP(L"ConnectQuery", State_ConnectQuery), + SIP(L"Shadow", State_Shadow), + SIP(L"Disconnected", State_Disconnected), + SIP(L"Idle", State_Idle), + SIP(L"Listen", State_Listen), + SIP(L"Reset", State_Reset), + SIP(L"Down", State_Down), + SIP(L"Init", State_Init) +}; + +VOID PhShowSessionProperties( + _In_ HWND ParentWindowHandle, + _In_ ULONG SessionId + ) +{ + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_SESSION), + ParentWindowHandle, + PhpSessionPropertiesDlgProc, + (LPARAM)SessionId + ); +} + +INT_PTR CALLBACK PhpSessionPropertiesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG sessionId = (ULONG)lParam; + WINSTATIONINFORMATION winStationInfo; + BOOLEAN haveWinStationInfo; + WINSTATIONCLIENT clientInfo; + BOOLEAN haveClientInfo; + ULONG returnLength; + PWSTR stateString; + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + // Query basic session information + + haveWinStationInfo = WinStationQueryInformationW( + NULL, + sessionId, + WinStationInformation, + &winStationInfo, + sizeof(WINSTATIONINFORMATION), + &returnLength + ); + + // Query client information + + haveClientInfo = WinStationQueryInformationW( + NULL, + sessionId, + WinStationClient, + &clientInfo, + sizeof(WINSTATIONCLIENT), + &returnLength + ); + + if (haveWinStationInfo) + { + PhSetDialogItemText(hwndDlg, IDC_USERNAME, + PhaFormatString(L"%s\\%s", winStationInfo.Domain, winStationInfo.UserName)->Buffer); + } + + PhSetDialogItemValue(hwndDlg, IDC_SESSIONID, sessionId, FALSE); + + if (haveWinStationInfo) + { + if (PhFindStringSiKeyValuePairs( + PhpConnectStatePairs, + sizeof(PhpConnectStatePairs), + winStationInfo.ConnectState, + &stateString + )) + { + PhSetDialogItemText(hwndDlg, IDC_STATE, stateString); + } + } + + if (haveWinStationInfo && winStationInfo.LogonTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + PPH_STRING time; + + PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.LogonTime); + time = PhFormatDateTime(&systemTime); + PhSetDialogItemText(hwndDlg, IDC_LOGONTIME, time->Buffer); + PhDereferenceObject(time); + } + + if (haveWinStationInfo && winStationInfo.ConnectTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + PPH_STRING time; + + PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.ConnectTime); + time = PhFormatDateTime(&systemTime); + PhSetDialogItemText(hwndDlg, IDC_CONNECTTIME, time->Buffer); + PhDereferenceObject(time); + } + + if (haveWinStationInfo && winStationInfo.DisconnectTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + PPH_STRING time; + + PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.DisconnectTime); + time = PhFormatDateTime(&systemTime); + PhSetDialogItemText(hwndDlg, IDC_DISCONNECTTIME, time->Buffer); + PhDereferenceObject(time); + } + + if (haveWinStationInfo && winStationInfo.LastInputTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + PPH_STRING time; + + PhLargeIntegerToLocalSystemTime(&systemTime, &winStationInfo.LastInputTime); + time = PhFormatDateTime(&systemTime); + PhSetDialogItemText(hwndDlg, IDC_LASTINPUTTIME, time->Buffer); + PhDereferenceObject(time); + } + + if (haveClientInfo && clientInfo.ClientName[0] != UNICODE_NULL) + { + WCHAR addressString[65]; + + PhSetDialogItemText(hwndDlg, IDC_CLIENTNAME, clientInfo.ClientName); + + if (clientInfo.ClientAddressFamily == AF_INET6) + { + struct in6_addr address; + ULONG i; + PUSHORT in; + PUSHORT out; + + // IPv6 is special - the client address data is a reversed version of + // the real address. + + in = (PUSHORT)clientInfo.ClientAddress; + out = (PUSHORT)address.u.Word; + + for (i = 8; i != 0; i--) + { + *out = _byteswap_ushort(*in); + in++; + out++; + } + + RtlIpv6AddressToString(&address, addressString); + } + else + { + wcscpy_s(addressString, 65, clientInfo.ClientAddress); + } + + PhSetDialogItemText(hwndDlg, IDC_CLIENTADDRESS, addressString); + + PhSetDialogItemText(hwndDlg, IDC_CLIENTDISPLAY, + PhaFormatString(L"%ux%u@%u", clientInfo.HRes, + clientInfo.VRes, clientInfo.ColorDepth)->Buffer + ); + } + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDOK)); + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/sessshad.c b/ProcessHacker/sessshad.c index 78e4377fbf10..ec891b65c8e2 100644 --- a/ProcessHacker/sessshad.c +++ b/ProcessHacker/sessshad.c @@ -1,239 +1,242 @@ -/* - * Process Hacker - - * session shadow configuration - * - * Copyright (C) 2011-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -#include - -#define SIP(String, Integer) { (String), (PVOID)(Integer) } - -static PH_KEY_VALUE_PAIR VirtualKeyPairs[] = -{ - SIP(L"0", '0'), - SIP(L"1", '1'), - SIP(L"2", '2'), - SIP(L"3", '3'), - SIP(L"4", '4'), - SIP(L"5", '5'), - SIP(L"6", '6'), - SIP(L"7", '7'), - SIP(L"8", '8'), - SIP(L"9", '9'), - SIP(L"A", 'A'), - SIP(L"B", 'B'), - SIP(L"C", 'C'), - SIP(L"D", 'D'), - SIP(L"E", 'E'), - SIP(L"F", 'F'), - SIP(L"G", 'G'), - SIP(L"H", 'H'), - SIP(L"I", 'I'), - SIP(L"J", 'J'), - SIP(L"K", 'K'), - SIP(L"L", 'L'), - SIP(L"M", 'M'), - SIP(L"N", 'N'), - SIP(L"O", 'O'), - SIP(L"P", 'P'), - SIP(L"Q", 'Q'), - SIP(L"R", 'R'), - SIP(L"S", 'S'), - SIP(L"T", 'T'), - SIP(L"U", 'U'), - SIP(L"V", 'V'), - SIP(L"W", 'W'), - SIP(L"X", 'X'), - SIP(L"Y", 'Y'), - SIP(L"Z", 'Z'), - SIP(L"{backspace}", VK_BACK), - SIP(L"{delete}", VK_DELETE), - SIP(L"{down}", VK_DOWN), - SIP(L"{end}", VK_END), - SIP(L"{enter}", VK_RETURN), - SIP(L"{F2}", VK_F2), - SIP(L"{F3}", VK_F3), - SIP(L"{F4}", VK_F4), - SIP(L"{F5}", VK_F5), - SIP(L"{F6}", VK_F6), - SIP(L"{F7}", VK_F7), - SIP(L"{F8}", VK_F8), - SIP(L"{F9}", VK_F9), - SIP(L"{F10}", VK_F10), - SIP(L"{F11}", VK_F11), - SIP(L"{F12}", VK_F12), - SIP(L"{home}", VK_HOME), - SIP(L"{insert}", VK_INSERT), - SIP(L"{left}", VK_LEFT), - SIP(L"{-}", VK_SUBTRACT), - SIP(L"{pagedown}", VK_NEXT), - SIP(L"{pageup}", VK_PRIOR), - SIP(L"{+}", VK_ADD), - SIP(L"{prtscrn}", VK_SNAPSHOT), - SIP(L"{right}", VK_RIGHT), - SIP(L"{spacebar}", VK_SPACE), - SIP(L"{*}", VK_MULTIPLY), - SIP(L"{tab}", VK_TAB), - SIP(L"{up}", VK_UP) -}; - -INT_PTR CALLBACK PhpSessionShadowDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowSessionShadowDialog( - _In_ HWND ParentWindowHandle, - _In_ ULONG SessionId - ) -{ - if (SessionId == NtCurrentPeb()->SessionId) - { - PhShowError(ParentWindowHandle, L"You cannot remote control the current session."); - return; - } - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_SHADOWSESSION), - ParentWindowHandle, - PhpSessionShadowDlgProc, - (LPARAM)SessionId - ); -} - -INT_PTR CALLBACK PhpSessionShadowDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND virtualKeyComboBox; - PH_INTEGER_PAIR hotkey; - ULONG i; - PWSTR stringToSelect; - - SetProp(hwndDlg, L"SessionId", UlongToHandle((ULONG)lParam)); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - hotkey = PhGetIntegerPairSetting(L"SessionShadowHotkey"); - - // Set up the hotkeys. - - virtualKeyComboBox = GetDlgItem(hwndDlg, IDC_VIRTUALKEY); - stringToSelect = L"{*}"; - - for (i = 0; i < sizeof(VirtualKeyPairs) / sizeof(PH_KEY_VALUE_PAIR); i++) - { - ComboBox_AddString(virtualKeyComboBox, VirtualKeyPairs[i].Key); - - if (PtrToUlong(VirtualKeyPairs[i].Value) == (ULONG)hotkey.X) - { - stringToSelect = VirtualKeyPairs[i].Key; - } - } - - PhSelectComboBoxString(virtualKeyComboBox, stringToSelect, FALSE); - - // Set up the modifiers. - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHIFT), hotkey.Y & KBDSHIFT); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_CTRL), hotkey.Y & KBDCTRL); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ALT), hotkey.Y & KBDALT); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, L"SessionId"); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - ULONG sessionId = HandleToUlong(GetProp(hwndDlg, L"SessionId")); - ULONG virtualKey; - ULONG modifiers; - WCHAR computerName[64]; - ULONG computerNameLength = 64; - - virtualKey = VK_MULTIPLY; - PhFindIntegerSiKeyValuePairs( - VirtualKeyPairs, - sizeof(VirtualKeyPairs), - PhaGetDlgItemText(hwndDlg, IDC_VIRTUALKEY)->Buffer, - &virtualKey - ); - - modifiers = 0; - - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHIFT)) == BST_CHECKED) - modifiers |= KBDSHIFT; - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_CTRL)) == BST_CHECKED) - modifiers |= KBDCTRL; - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_ALT)) == BST_CHECKED) - modifiers |= KBDALT; - - if (GetComputerName(computerName, &computerNameLength)) - { - if (WinStationShadow(NULL, computerName, sessionId, (UCHAR)virtualKey, (USHORT)modifiers)) - { - PH_INTEGER_PAIR hotkey; - - hotkey.X = virtualKey; - hotkey.Y = modifiers; - PhSetIntegerPairSetting(L"SessionShadowHotkey", hotkey); - - EndDialog(hwndDlg, IDOK); - } - else - { - PhShowStatus(hwndDlg, L"Unable to remote control the session", 0, GetLastError()); - } - } - else - { - PhShowError(hwndDlg, L"The computer name is too long."); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * session shadow configuration + * + * Copyright (C) 2011-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#define SIP(String, Integer) { (String), (PVOID)(Integer) } + +static PH_KEY_VALUE_PAIR VirtualKeyPairs[] = +{ + SIP(L"0", '0'), + SIP(L"1", '1'), + SIP(L"2", '2'), + SIP(L"3", '3'), + SIP(L"4", '4'), + SIP(L"5", '5'), + SIP(L"6", '6'), + SIP(L"7", '7'), + SIP(L"8", '8'), + SIP(L"9", '9'), + SIP(L"A", 'A'), + SIP(L"B", 'B'), + SIP(L"C", 'C'), + SIP(L"D", 'D'), + SIP(L"E", 'E'), + SIP(L"F", 'F'), + SIP(L"G", 'G'), + SIP(L"H", 'H'), + SIP(L"I", 'I'), + SIP(L"J", 'J'), + SIP(L"K", 'K'), + SIP(L"L", 'L'), + SIP(L"M", 'M'), + SIP(L"N", 'N'), + SIP(L"O", 'O'), + SIP(L"P", 'P'), + SIP(L"Q", 'Q'), + SIP(L"R", 'R'), + SIP(L"S", 'S'), + SIP(L"T", 'T'), + SIP(L"U", 'U'), + SIP(L"V", 'V'), + SIP(L"W", 'W'), + SIP(L"X", 'X'), + SIP(L"Y", 'Y'), + SIP(L"Z", 'Z'), + SIP(L"{backspace}", VK_BACK), + SIP(L"{delete}", VK_DELETE), + SIP(L"{down}", VK_DOWN), + SIP(L"{end}", VK_END), + SIP(L"{enter}", VK_RETURN), + SIP(L"{F2}", VK_F2), + SIP(L"{F3}", VK_F3), + SIP(L"{F4}", VK_F4), + SIP(L"{F5}", VK_F5), + SIP(L"{F6}", VK_F6), + SIP(L"{F7}", VK_F7), + SIP(L"{F8}", VK_F8), + SIP(L"{F9}", VK_F9), + SIP(L"{F10}", VK_F10), + SIP(L"{F11}", VK_F11), + SIP(L"{F12}", VK_F12), + SIP(L"{home}", VK_HOME), + SIP(L"{insert}", VK_INSERT), + SIP(L"{left}", VK_LEFT), + SIP(L"{-}", VK_SUBTRACT), + SIP(L"{pagedown}", VK_NEXT), + SIP(L"{pageup}", VK_PRIOR), + SIP(L"{+}", VK_ADD), + SIP(L"{prtscrn}", VK_SNAPSHOT), + SIP(L"{right}", VK_RIGHT), + SIP(L"{spacebar}", VK_SPACE), + SIP(L"{*}", VK_MULTIPLY), + SIP(L"{tab}", VK_TAB), + SIP(L"{up}", VK_UP) +}; + +INT_PTR CALLBACK PhpSessionShadowDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowSessionShadowDialog( + _In_ HWND ParentWindowHandle, + _In_ ULONG SessionId + ) +{ + ULONG sessionId = ULONG_MAX; + + PhGetProcessSessionId(NtCurrentProcess(), &sessionId); + + if (SessionId == sessionId) + { + PhShowError2(ParentWindowHandle, L"Unable to shadow session.", L"You cannot remote control the current session."); + return; + } + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_SHADOWSESSION), + ParentWindowHandle, + PhpSessionShadowDlgProc, + (LPARAM)SessionId + ); +} + +INT_PTR CALLBACK PhpSessionShadowDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND virtualKeyComboBox; + PH_INTEGER_PAIR hotkey; + ULONG i; + PWSTR stringToSelect; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, UlongToPtr((ULONG)lParam)); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + hotkey = PhGetIntegerPairSetting(L"SessionShadowHotkey"); + + // Set up the hotkeys. + + virtualKeyComboBox = GetDlgItem(hwndDlg, IDC_VIRTUALKEY); + stringToSelect = L"{*}"; + + for (i = 0; i < sizeof(VirtualKeyPairs) / sizeof(PH_KEY_VALUE_PAIR); i++) + { + ComboBox_AddString(virtualKeyComboBox, VirtualKeyPairs[i].Key); + + if (PtrToUlong(VirtualKeyPairs[i].Value) == (ULONG)hotkey.X) + { + stringToSelect = VirtualKeyPairs[i].Key; + } + } + + PhSelectComboBoxString(virtualKeyComboBox, stringToSelect, FALSE); + + // Set up the modifiers. + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHIFT), hotkey.Y & KBDSHIFT); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_CTRL), hotkey.Y & KBDCTRL); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ALT), hotkey.Y & KBDALT); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + ULONG sessionId = PtrToUlong(PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT)); + ULONG virtualKey; + ULONG modifiers; + WCHAR computerName[64]; + ULONG computerNameLength = 64; + + virtualKey = VK_MULTIPLY; + PhFindIntegerSiKeyValuePairs( + VirtualKeyPairs, + sizeof(VirtualKeyPairs), + PhaGetDlgItemText(hwndDlg, IDC_VIRTUALKEY)->Buffer, + &virtualKey + ); + + modifiers = 0; + + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHIFT)) == BST_CHECKED) + modifiers |= KBDSHIFT; + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_CTRL)) == BST_CHECKED) + modifiers |= KBDCTRL; + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_ALT)) == BST_CHECKED) + modifiers |= KBDALT; + + if (GetComputerName(computerName, &computerNameLength)) + { + if (WinStationShadow(NULL, computerName, sessionId, (UCHAR)virtualKey, (USHORT)modifiers)) + { + PH_INTEGER_PAIR hotkey; + + hotkey.X = virtualKey; + hotkey.Y = modifiers; + PhSetIntegerPairSetting(L"SessionShadowHotkey", hotkey); + + EndDialog(hwndDlg, IDOK); + } + else + { + PhShowStatus(hwndDlg, L"Unable to remote control the session", 0, GetLastError()); + } + } + else + { + PhShowError(hwndDlg, L"The computer name is too long."); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/settings.c b/ProcessHacker/settings.c index ddc8f441af47..965abfde667e 100644 --- a/ProcessHacker/settings.c +++ b/ProcessHacker/settings.c @@ -1,1198 +1,322 @@ -/* - * Process Hacker - - * program settings - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This file contains a program-specific settings system. All possible - * settings are defined at program startup and added to a hashtable. - * The values of these settings can then be read in from a XML file or - * saved to a XML file at any time. Settings which are not recognized - * are added to a list of "ignored settings"; this is necessary to - * support plugin settings, as we don't want their settings to get - * deleted whenever the plugins are disabled. - * - * The get/set functions are very strict. If the wrong function is used - * (the get-integer-setting function is used on a string setting) or - * the setting does not exist, an exception will be raised. - */ - -#include -#define PH_SETTINGS_PRIVATE -#include -#include - -#include "mxml/mxml.h" - -PPH_HASHTABLE PhSettingsHashtable; -PH_QUEUED_LOCK PhSettingsLock = PH_QUEUED_LOCK_INIT; - -PPH_LIST PhIgnoredSettings; - -// These macros make sure the C strings can be seamlessly converted into -// PH_STRINGREFs at compile time, for a small speed boost. - -#define ADD_SETTING_WRAPPER(Type, Name, DefaultValue) \ - { \ - static PH_STRINGREF name = PH_STRINGREF_INIT(Name); \ - static PH_STRINGREF defaultValue = PH_STRINGREF_INIT(DefaultValue); \ - PhpAddSetting(Type, &name, &defaultValue); \ - } - -#define PhpAddStringSetting(A, B) ADD_SETTING_WRAPPER(StringSettingType, A, B) -#define PhpAddIntegerSetting(A, B) ADD_SETTING_WRAPPER(IntegerSettingType, A, B) -#define PhpAddIntegerPairSetting(A, B) ADD_SETTING_WRAPPER(IntegerPairSettingType, A, B) -#define PhpAddScalableIntegerPairSetting(A, B) ADD_SETTING_WRAPPER(ScalableIntegerPairSettingType, A, B) - -VOID PhSettingsInitialization( - VOID - ) -{ - PhSettingsHashtable = PhCreateHashtable( - sizeof(PH_SETTING), - PhpSettingsHashtableEqualFunction, - PhpSettingsHashtableHashFunction, - 256 - ); - PhIgnoredSettings = PhCreateList(4); - - PhpAddIntegerSetting(L"AllowOnlyOneInstance", L"1"); - PhpAddIntegerSetting(L"CloseOnEscape", L"0"); - PhpAddIntegerSetting(L"CollapseServicesOnStart", L"0"); - PhpAddStringSetting(L"DbgHelpPath", L"dbghelp.dll"); - PhpAddStringSetting(L"DbgHelpSearchPath", L""); - PhpAddIntegerSetting(L"DbgHelpUndecorate", L"1"); - PhpAddStringSetting(L"DisabledPlugins", L""); - PhpAddIntegerSetting(L"ElevationLevel", L"1"); // PromptElevateAction - PhpAddIntegerSetting(L"EnableCycleCpuUsage", L"1"); - PhpAddIntegerSetting(L"EnableInstantTooltips", L"0"); - PhpAddIntegerSetting(L"EnableKph", L"1"); - PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); - PhpAddIntegerSetting(L"EnablePlugins", L"1"); - PhpAddIntegerSetting(L"EnableServiceNonPoll", L"1"); - PhpAddIntegerSetting(L"EnableStage2", L"1"); - PhpAddIntegerSetting(L"EnableWarnings", L"1"); - PhpAddStringSetting(L"EnvironmentListViewColumns", L""); - PhpAddIntegerSetting(L"FindObjRegex", L"0"); - PhpAddStringSetting(L"FindObjListViewColumns", L""); - PhpAddIntegerPairSetting(L"FindObjWindowPosition", L"350,350"); - PhpAddScalableIntegerPairSetting(L"FindObjWindowSize", L"@96|550,420"); - PhpAddIntegerSetting(L"FirstRun", L"1"); - PhpAddStringSetting(L"Font", L""); // null - PhpAddIntegerSetting(L"ForceNoParent", L"0"); - PhpAddStringSetting(L"HandleTreeListColumns", L""); - PhpAddStringSetting(L"HandleTreeListSort", L"0,1"); // 0, AscendingSortOrder - PhpAddStringSetting(L"HiddenProcessesListViewColumns", L""); - PhpAddIntegerPairSetting(L"HiddenProcessesWindowPosition", L"400,400"); - PhpAddScalableIntegerPairSetting(L"HiddenProcessesWindowSize", L"@96|520,400"); - PhpAddIntegerSetting(L"HideDriverServices", L"0"); - PhpAddIntegerSetting(L"HideFreeRegions", L"1"); - PhpAddIntegerSetting(L"HideOnClose", L"0"); - PhpAddIntegerSetting(L"HideOnMinimize", L"0"); - PhpAddIntegerSetting(L"HideOtherUserProcesses", L"0"); - PhpAddIntegerSetting(L"HideSignedProcesses", L"0"); - PhpAddIntegerSetting(L"HideUnnamedHandles", L"1"); - PhpAddIntegerSetting(L"HighlightingDuration", L"3e8"); // 1000ms - PhpAddIntegerSetting(L"IconMask", L"1"); // PH_ICON_CPU_HISTORY - PhpAddStringSetting(L"IconMaskList", L""); - PhpAddIntegerSetting(L"IconNotifyMask", L"c"); // PH_NOTIFY_SERVICE_CREATE | PH_NOTIFY_SERVICE_DELETE - PhpAddIntegerSetting(L"IconProcesses", L"f"); // 15 - PhpAddIntegerSetting(L"IconSingleClick", L"0"); - PhpAddIntegerSetting(L"IconTogglesVisibility", L"1"); - PhpAddStringSetting(L"JobListViewColumns", L""); - PhpAddIntegerSetting(L"KphUnloadOnShutdown", L"0"); - PhpAddIntegerSetting(L"LogEntries", L"200"); // 512 - PhpAddStringSetting(L"LogListViewColumns", L""); - PhpAddIntegerPairSetting(L"LogWindowPosition", L"300,300"); - PhpAddScalableIntegerPairSetting(L"LogWindowSize", L"@96|450,500"); - PhpAddIntegerSetting(L"MainWindowAlwaysOnTop", L"0"); - PhpAddIntegerSetting(L"MainWindowOpacity", L"0"); // means 100% - PhpAddIntegerPairSetting(L"MainWindowPosition", L"100,100"); - PhpAddScalableIntegerPairSetting(L"MainWindowSize", L"@96|800,600"); - PhpAddIntegerSetting(L"MainWindowState", L"1"); - PhpAddIntegerSetting(L"MaxSizeUnit", L"6"); - PhpAddIntegerSetting(L"MemEditBytesPerRow", L"10"); // 16 - PhpAddStringSetting(L"MemEditGotoChoices", L""); - PhpAddIntegerPairSetting(L"MemEditPosition", L"450,450"); - PhpAddScalableIntegerPairSetting(L"MemEditSize", L"@96|600,500"); - PhpAddStringSetting(L"MemFilterChoices", L""); - PhpAddStringSetting(L"MemResultsListViewColumns", L""); - PhpAddIntegerPairSetting(L"MemResultsPosition", L"300,300"); - PhpAddScalableIntegerPairSetting(L"MemResultsSize", L"@96|500,520"); - PhpAddStringSetting(L"MemoryTreeListColumns", L""); - PhpAddStringSetting(L"MemoryTreeListSort", L"0,0"); // 0, NoSortOrder - PhpAddIntegerPairSetting(L"MemoryListsWindowPosition", L"400,400"); - PhpAddStringSetting(L"MemoryReadWriteAddressChoices", L""); - PhpAddIntegerSetting(L"MiniInfoWindowEnabled", L"1"); - PhpAddIntegerSetting(L"MiniInfoWindowOpacity", L"0"); // means 100% - PhpAddIntegerSetting(L"MiniInfoWindowPinned", L"0"); - PhpAddIntegerPairSetting(L"MiniInfoWindowPosition", L"200,200"); - PhpAddIntegerSetting(L"MiniInfoWindowRefreshAutomatically", L"1"); - PhpAddScalableIntegerPairSetting(L"MiniInfoWindowSize", L"@96|10,200"); - PhpAddStringSetting(L"ModuleTreeListColumns", L""); - PhpAddStringSetting(L"ModuleTreeListSort", L"0,0"); // 0, NoSortOrder - PhpAddStringSetting(L"NetworkTreeListColumns", L""); - PhpAddStringSetting(L"NetworkTreeListSort", L"0,1"); // 0, AscendingSortOrder - PhpAddIntegerSetting(L"NoPurgeProcessRecords", L"0"); - PhpAddStringSetting(L"PluginsDirectory", L"plugins"); - PhpAddStringSetting(L"ProcessServiceListViewColumns", L""); - PhpAddStringSetting(L"ProcessTreeListColumns", L""); - PhpAddStringSetting(L"ProcessTreeListSort", L"0,0"); // 0, NoSortOrder - PhpAddStringSetting(L"ProcPropPage", L"General"); - PhpAddIntegerPairSetting(L"ProcPropPosition", L"200,200"); - PhpAddScalableIntegerPairSetting(L"ProcPropSize", L"@96|460,580"); - PhpAddStringSetting(L"ProgramInspectExecutables", L"peview.exe \"%s\""); - PhpAddIntegerSetting(L"PropagateCpuUsage", L"0"); - PhpAddStringSetting(L"RunAsProgram", L""); - PhpAddStringSetting(L"RunAsUserName", L""); - PhpAddIntegerSetting(L"SampleCount", L"200"); // 512 - PhpAddIntegerSetting(L"SampleCountAutomatic", L"1"); - PhpAddIntegerSetting(L"ScrollToNewProcesses", L"0"); - PhpAddStringSetting(L"SearchEngine", L"http://www.google.com/search?q=\"%s\""); - PhpAddStringSetting(L"ServiceListViewColumns", L""); - PhpAddStringSetting(L"ServiceTreeListColumns", L""); - PhpAddStringSetting(L"ServiceTreeListSort", L"0,1"); // 0, AscendingSortOrder - PhpAddIntegerPairSetting(L"SessionShadowHotkey", L"106,2"); // VK_MULTIPLY,KBDCTRL - PhpAddIntegerSetting(L"ShowCommitInSummary", L"1"); - PhpAddIntegerSetting(L"ShowCpuBelow001", L"0"); - PhpAddIntegerSetting(L"StartHidden", L"0"); - PhpAddIntegerSetting(L"SysInfoWindowAlwaysOnTop", L"0"); - PhpAddIntegerSetting(L"SysInfoWindowOneGraphPerCpu", L"0"); - PhpAddIntegerPairSetting(L"SysInfoWindowPosition", L"200,200"); - PhpAddStringSetting(L"SysInfoWindowSection", L""); - PhpAddScalableIntegerPairSetting(L"SysInfoWindowSize", L"@96|620,590"); - PhpAddIntegerSetting(L"SysInfoWindowState", L"1"); - PhpAddIntegerSetting(L"ThinRows", L"0"); - PhpAddStringSetting(L"ThreadTreeListColumns", L""); - PhpAddStringSetting(L"ThreadTreeListSort", L"1,2"); // 1, DescendingSortOrder - PhpAddStringSetting(L"ThreadStackListViewColumns", L""); - PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,380"); - PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms - - // Colors are specified with R in the lowest byte, then G, then B. - // So: bbggrr. - PhpAddIntegerSetting(L"ColorNew", L"00ff7f"); // Chartreuse - PhpAddIntegerSetting(L"ColorRemoved", L"283cff"); - PhpAddIntegerSetting(L"UseColorOwnProcesses", L"1"); - PhpAddIntegerSetting(L"ColorOwnProcesses", L"aaffff"); - PhpAddIntegerSetting(L"UseColorSystemProcesses", L"1"); - PhpAddIntegerSetting(L"ColorSystemProcesses", L"ffccaa"); - PhpAddIntegerSetting(L"UseColorServiceProcesses", L"1"); - PhpAddIntegerSetting(L"ColorServiceProcesses", L"ffffcc"); - PhpAddIntegerSetting(L"UseColorJobProcesses", L"0"); - PhpAddIntegerSetting(L"ColorJobProcesses", L"3f85cd"); // Peru - PhpAddIntegerSetting(L"UseColorWow64Processes", L"0"); - PhpAddIntegerSetting(L"ColorWow64Processes", L"8f8fbc"); // Rosy Brown - PhpAddIntegerSetting(L"UseColorDebuggedProcesses", L"1"); - PhpAddIntegerSetting(L"ColorDebuggedProcesses", L"ffbbcc"); - PhpAddIntegerSetting(L"UseColorElevatedProcesses", L"1"); - PhpAddIntegerSetting(L"ColorElevatedProcesses", L"00aaff"); - PhpAddIntegerSetting(L"UseColorPicoProcesses", L"1"); - PhpAddIntegerSetting(L"ColorPicoProcesses", L"ff8000"); // Blue - PhpAddIntegerSetting(L"UseColorImmersiveProcesses", L"1"); - PhpAddIntegerSetting(L"ColorImmersiveProcesses", L"cbc0ff"); // Pink - PhpAddIntegerSetting(L"UseColorSuspended", L"1"); - PhpAddIntegerSetting(L"ColorSuspended", L"777777"); - PhpAddIntegerSetting(L"UseColorDotNet", L"1"); - PhpAddIntegerSetting(L"ColorDotNet", L"00ffde"); - PhpAddIntegerSetting(L"UseColorPacked", L"1"); - PhpAddIntegerSetting(L"ColorPacked", L"9314ff"); // Deep Pink - PhpAddIntegerSetting(L"UseColorGuiThreads", L"1"); - PhpAddIntegerSetting(L"ColorGuiThreads", L"77ffff"); - PhpAddIntegerSetting(L"UseColorRelocatedModules", L"1"); - PhpAddIntegerSetting(L"ColorRelocatedModules", L"80c0ff"); - PhpAddIntegerSetting(L"UseColorProtectedHandles", L"1"); - PhpAddIntegerSetting(L"ColorProtectedHandles", L"777777"); - PhpAddIntegerSetting(L"UseColorInheritHandles", L"1"); - PhpAddIntegerSetting(L"ColorInheritHandles", L"ffff77"); - - PhpAddIntegerSetting(L"GraphShowText", L"1"); - PhpAddIntegerSetting(L"GraphColorMode", L"0"); - PhpAddIntegerSetting(L"ColorCpuKernel", L"00ff00"); - PhpAddIntegerSetting(L"ColorCpuUser", L"0000ff"); - PhpAddIntegerSetting(L"ColorIoReadOther", L"00ffff"); - PhpAddIntegerSetting(L"ColorIoWrite", L"ff0077"); - PhpAddIntegerSetting(L"ColorPrivate", L"0077ff"); - PhpAddIntegerSetting(L"ColorPhysical", L"ffff00"); - - PhUpdateCachedSettings(); -} - -VOID PhUpdateCachedSettings( - VOID - ) -{ -#define UPDATE_INTEGER_CS(Name) (PhCs##Name = PhGetIntegerSetting(L#Name)) - - UPDATE_INTEGER_CS(CollapseServicesOnStart); - UPDATE_INTEGER_CS(ForceNoParent); - UPDATE_INTEGER_CS(HighlightingDuration); - UPDATE_INTEGER_CS(PropagateCpuUsage); - UPDATE_INTEGER_CS(ScrollToNewProcesses); - UPDATE_INTEGER_CS(ShowCpuBelow001); - UPDATE_INTEGER_CS(UpdateInterval); - - UPDATE_INTEGER_CS(ColorNew); - UPDATE_INTEGER_CS(ColorRemoved); - UPDATE_INTEGER_CS(UseColorOwnProcesses); - UPDATE_INTEGER_CS(ColorOwnProcesses); - UPDATE_INTEGER_CS(UseColorSystemProcesses); - UPDATE_INTEGER_CS(ColorSystemProcesses); - UPDATE_INTEGER_CS(UseColorServiceProcesses); - UPDATE_INTEGER_CS(ColorServiceProcesses); - UPDATE_INTEGER_CS(UseColorJobProcesses); - UPDATE_INTEGER_CS(ColorJobProcesses); - UPDATE_INTEGER_CS(UseColorWow64Processes); - UPDATE_INTEGER_CS(ColorWow64Processes); - UPDATE_INTEGER_CS(UseColorDebuggedProcesses); - UPDATE_INTEGER_CS(ColorDebuggedProcesses); - UPDATE_INTEGER_CS(UseColorElevatedProcesses); - UPDATE_INTEGER_CS(ColorElevatedProcesses); - UPDATE_INTEGER_CS(UseColorPicoProcesses); - UPDATE_INTEGER_CS(ColorPicoProcesses); - UPDATE_INTEGER_CS(UseColorImmersiveProcesses); - UPDATE_INTEGER_CS(ColorImmersiveProcesses); - UPDATE_INTEGER_CS(UseColorSuspended); - UPDATE_INTEGER_CS(ColorSuspended); - UPDATE_INTEGER_CS(UseColorDotNet); - UPDATE_INTEGER_CS(ColorDotNet); - UPDATE_INTEGER_CS(UseColorPacked); - UPDATE_INTEGER_CS(ColorPacked); - UPDATE_INTEGER_CS(UseColorGuiThreads); - UPDATE_INTEGER_CS(ColorGuiThreads); - UPDATE_INTEGER_CS(UseColorRelocatedModules); - UPDATE_INTEGER_CS(ColorRelocatedModules); - UPDATE_INTEGER_CS(UseColorProtectedHandles); - UPDATE_INTEGER_CS(ColorProtectedHandles); - UPDATE_INTEGER_CS(UseColorInheritHandles); - UPDATE_INTEGER_CS(ColorInheritHandles); - UPDATE_INTEGER_CS(GraphShowText); - UPDATE_INTEGER_CS(GraphColorMode); - UPDATE_INTEGER_CS(ColorCpuKernel); - UPDATE_INTEGER_CS(ColorCpuUser); - UPDATE_INTEGER_CS(ColorIoReadOther); - UPDATE_INTEGER_CS(ColorIoWrite); - UPDATE_INTEGER_CS(ColorPrivate); - UPDATE_INTEGER_CS(ColorPhysical); -} - -BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_SETTING setting1 = (PPH_SETTING)Entry1; - PPH_SETTING setting2 = (PPH_SETTING)Entry2; - - return PhEqualStringRef(&setting1->Name, &setting2->Name, FALSE); -} - -ULONG NTAPI PhpSettingsHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPH_SETTING setting = (PPH_SETTING)Entry; - - return PhHashBytes((PUCHAR)setting->Name.Buffer, setting->Name.Length); -} - -static VOID PhpAddSetting( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_STRINGREF Name, - _In_ PPH_STRINGREF DefaultValue - ) -{ - PH_SETTING setting; - - setting.Type = Type; - setting.Name = *Name; - setting.DefaultValue = *DefaultValue; - memset(&setting.u, 0, sizeof(setting.u)); - - PhpSettingFromString(Type, &setting.DefaultValue, NULL, &setting); - - PhAddEntryHashtable(PhSettingsHashtable, &setting); -} - -static ULONG PhpGetCurrentScale( - VOID - ) -{ - static PH_INITONCE initOnce; - static ULONG dpi = 96; - - if (PhBeginInitOnce(&initOnce)) - { - HDC hdc; - - if (hdc = GetDC(NULL)) - { - dpi = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - } - - PhEndInitOnce(&initOnce); - } - - return dpi; -} - -static PPH_STRING PhpSettingToString( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_SETTING Setting - ) -{ - switch (Type) - { - case StringSettingType: - { - if (!Setting->u.Pointer) - return PhReferenceEmptyString(); - - PhReferenceObject(Setting->u.Pointer); - - return (PPH_STRING)Setting->u.Pointer; - } - case IntegerSettingType: - { - return PhFormatString(L"%x", Setting->u.Integer); - } - case IntegerPairSettingType: - { - PPH_INTEGER_PAIR integerPair = &Setting->u.IntegerPair; - - return PhFormatString(L"%d,%d", integerPair->X, integerPair->Y); - } - case ScalableIntegerPairSettingType: - { - PPH_SCALABLE_INTEGER_PAIR scalableIntegerPair = Setting->u.Pointer; - - if (!scalableIntegerPair) - return PhReferenceEmptyString(); - - return PhFormatString(L"@%u|%d,%d", scalableIntegerPair->Scale, scalableIntegerPair->X, scalableIntegerPair->Y); - } - } - - return PhReferenceEmptyString(); -} - -static BOOLEAN PhpSettingFromString( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_STRINGREF StringRef, - _In_opt_ PPH_STRING String, - _Inout_ PPH_SETTING Setting - ) -{ - switch (Type) - { - case StringSettingType: - { - if (String) - { - PhSetReference(&Setting->u.Pointer, String); - } - else - { - Setting->u.Pointer = PhCreateString2(StringRef); - } - - return TRUE; - } - case IntegerSettingType: - { - ULONG64 integer; - - if (PhStringToInteger64(StringRef, 16, &integer)) - { - Setting->u.Integer = (ULONG)integer; - return TRUE; - } - else - { - return FALSE; - } - } - case IntegerPairSettingType: - { - LONG64 x; - LONG64 y; - PH_STRINGREF xString; - PH_STRINGREF yString; - - if (!PhSplitStringRefAtChar(StringRef, ',', &xString, &yString)) - return FALSE; - - if (PhStringToInteger64(&xString, 10, &x) && PhStringToInteger64(&yString, 10, &y)) - { - Setting->u.IntegerPair.X = (LONG)x; - Setting->u.IntegerPair.Y = (LONG)y; - return TRUE; - } - else - { - return FALSE; - } - } - case ScalableIntegerPairSettingType: - { - ULONG64 scale; - LONG64 x; - LONG64 y; - PH_STRINGREF stringRef; - PH_STRINGREF firstPart; - PH_STRINGREF secondPart; - PPH_SCALABLE_INTEGER_PAIR scalableIntegerPair; - - stringRef = *StringRef; - - if (stringRef.Length != 0 && stringRef.Buffer[0] == '@') - { - PhSkipStringRef(&stringRef, sizeof(WCHAR)); - - if (!PhSplitStringRefAtChar(&stringRef, '|', &firstPart, &stringRef)) - return FALSE; - if (!PhStringToInteger64(&firstPart, 10, &scale)) - return FALSE; - } - else - { - scale = PhpGetCurrentScale(); - } - - if (!PhSplitStringRefAtChar(&stringRef, ',', &firstPart, &secondPart)) - return FALSE; - - if (PhStringToInteger64(&firstPart, 10, &x) && PhStringToInteger64(&secondPart, 10, &y)) - { - scalableIntegerPair = PhAllocate(sizeof(PH_SCALABLE_INTEGER_PAIR)); - scalableIntegerPair->X = (LONG)x; - scalableIntegerPair->Y = (LONG)y; - scalableIntegerPair->Scale = (ULONG)scale; - Setting->u.Pointer = scalableIntegerPair; - return TRUE; - } - else - { - return FALSE; - } - } - } - - return FALSE; -} - -static VOID PhpFreeSettingValue( - _In_ PH_SETTING_TYPE Type, - _In_ PPH_SETTING Setting - ) -{ - switch (Type) - { - case StringSettingType: - PhClearReference(&Setting->u.Pointer); - break; - case ScalableIntegerPairSettingType: - PhFree(Setting->u.Pointer); - Setting->u.Pointer = NULL; - break; - } -} - -static PVOID PhpLookupSetting( - _In_ PPH_STRINGREF Name - ) -{ - PH_SETTING lookupSetting; - PPH_SETTING setting; - - lookupSetting.Name = *Name; - setting = (PPH_SETTING)PhFindEntryHashtable( - PhSettingsHashtable, - &lookupSetting - ); - - return setting; -} - -_May_raise_ ULONG PhGetIntegerSetting( - _In_ PWSTR Name - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - ULONG value; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockShared(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == IntegerSettingType) - { - value = setting->u.Integer; - } - else - { - setting = NULL; - } - - PhReleaseQueuedLockShared(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); - - return value; -} - -_May_raise_ PH_INTEGER_PAIR PhGetIntegerPairSetting( - _In_ PWSTR Name - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - PH_INTEGER_PAIR value; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockShared(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == IntegerPairSettingType) - { - value = setting->u.IntegerPair; - } - else - { - setting = NULL; - } - - PhReleaseQueuedLockShared(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); - - return value; -} - -_May_raise_ PH_SCALABLE_INTEGER_PAIR PhGetScalableIntegerPairSetting( - _In_ PWSTR Name, - _In_ BOOLEAN ScaleToCurrent - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - PH_SCALABLE_INTEGER_PAIR value; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockShared(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == ScalableIntegerPairSettingType) - { - value = *(PPH_SCALABLE_INTEGER_PAIR)setting->u.Pointer; - } - else - { - setting = NULL; - } - - PhReleaseQueuedLockShared(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); - - if (ScaleToCurrent) - { - ULONG currentScale; - - currentScale = PhpGetCurrentScale(); - - if (value.Scale != currentScale && value.Scale != 0) - { - value.X = PhMultiplyDivideSigned(value.X, currentScale, value.Scale); - value.Y = PhMultiplyDivideSigned(value.Y, currentScale, value.Scale); - value.Scale = currentScale; - } - } - - return value; -} - -_May_raise_ PPH_STRING PhGetStringSetting( - _In_ PWSTR Name - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - PPH_STRING value; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockShared(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == StringSettingType) - { - if (setting->u.Pointer) - { - PhSetReference(&value, setting->u.Pointer); - } - else - { - // Set to NULL, create an empty string - // outside of the lock. - value = NULL; - } - } - else - { - setting = NULL; - } - - PhReleaseQueuedLockShared(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); - - if (!value) - value = PhReferenceEmptyString(); - - return value; -} - -_May_raise_ VOID PhSetIntegerSetting( - _In_ PWSTR Name, - _In_ ULONG Value - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == IntegerSettingType) - { - setting->u.Integer = Value; - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); -} - -_May_raise_ VOID PhSetIntegerPairSetting( - _In_ PWSTR Name, - _In_ PH_INTEGER_PAIR Value - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == IntegerPairSettingType) - { - setting->u.IntegerPair = Value; - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); -} - -_May_raise_ VOID PhSetScalableIntegerPairSetting( - _In_ PWSTR Name, - _In_ PH_SCALABLE_INTEGER_PAIR Value - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == ScalableIntegerPairSettingType) - { - PhpFreeSettingValue(ScalableIntegerPairSettingType, setting); - setting->u.Pointer = PhAllocateCopy(&Value, sizeof(PH_SCALABLE_INTEGER_PAIR)); - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); -} - -_May_raise_ VOID PhSetScalableIntegerPairSetting2( - _In_ PWSTR Name, - _In_ PH_INTEGER_PAIR Value - ) -{ - PH_SCALABLE_INTEGER_PAIR scalableIntegerPair; - - scalableIntegerPair.Pair = Value; - scalableIntegerPair.Scale = PhpGetCurrentScale(); - PhSetScalableIntegerPairSetting(Name, scalableIntegerPair); -} - -_May_raise_ VOID PhSetStringSetting( - _In_ PWSTR Name, - _In_ PWSTR Value - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == StringSettingType) - { - PhpFreeSettingValue(StringSettingType, setting); - setting->u.Pointer = PhCreateString(Value); - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); -} - -_May_raise_ VOID PhSetStringSetting2( - _In_ PWSTR Name, - _In_ PPH_STRINGREF Value - ) -{ - PPH_SETTING setting; - PH_STRINGREF name; - - PhInitializeStringRef(&name, Name); - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - setting = PhpLookupSetting(&name); - - if (setting && setting->Type == StringSettingType) - { - PhpFreeSettingValue(StringSettingType, setting); - setting->u.Pointer = PhCreateString2(Value); - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); - - if (!setting) - PhRaiseStatus(STATUS_NOT_FOUND); -} - -VOID PhpFreeIgnoredSetting( - _In_ PPH_SETTING Setting - ) -{ - PhFree(Setting->Name.Buffer); - PhDereferenceObject(Setting->u.Pointer); - - PhFree(Setting); -} - -VOID PhpClearIgnoredSettings( - VOID - ) -{ - ULONG i; - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - for (i = 0; i < PhIgnoredSettings->Count; i++) - { - PhpFreeIgnoredSetting(PhIgnoredSettings->Items[i]); - } - - PhClearList(PhIgnoredSettings); - - PhReleaseQueuedLockExclusive(&PhSettingsLock); -} - -VOID PhClearIgnoredSettings( - VOID - ) -{ - PhpClearIgnoredSettings(); -} - -VOID PhConvertIgnoredSettings( - VOID - ) -{ - ULONG i; - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - for (i = 0; i < PhIgnoredSettings->Count; i++) - { - PPH_SETTING ignoredSetting = PhIgnoredSettings->Items[i]; - PPH_SETTING setting; - - setting = PhpLookupSetting(&ignoredSetting->Name); - - if (setting) - { - PhpFreeSettingValue(setting->Type, setting); - - if (!PhpSettingFromString( - setting->Type, - &((PPH_STRING)ignoredSetting->u.Pointer)->sr, - ignoredSetting->u.Pointer, - setting - )) - { - PhpSettingFromString( - setting->Type, - &setting->DefaultValue, - NULL, - setting - ); - } - - PhpFreeIgnoredSetting(ignoredSetting); - - PhRemoveItemList(PhIgnoredSettings, i); - i--; - } - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); -} - -NTSTATUS PhLoadSettings( - _In_ PWSTR FileName - ) -{ - NTSTATUS status; - HANDLE fileHandle; - LARGE_INTEGER fileSize; - mxml_node_t *topNode; - mxml_node_t *currentNode; - - PhpClearIgnoredSettings(); - - status = PhCreateFileWin32( - &fileHandle, - FileName, - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - return status; - - if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) - { - // A blank file is OK. There are no settings to load. - NtClose(fileHandle); - return status; - } - - topNode = mxmlLoadFd(NULL, fileHandle, MXML_OPAQUE_CALLBACK); - NtClose(fileHandle); - - if (!topNode) - return STATUS_FILE_CORRUPT_ERROR; - - if (topNode->type != MXML_ELEMENT) - { - mxmlDelete(topNode); - return STATUS_FILE_CORRUPT_ERROR; - } - - currentNode = topNode->child; - - while (currentNode) - { - PPH_STRING settingName = NULL; - - if ( - currentNode->type == MXML_ELEMENT && - currentNode->value.element.num_attrs >= 1 && - _stricmp(currentNode->value.element.attrs[0].name, "name") == 0 - ) - { - settingName = PhConvertUtf8ToUtf16(currentNode->value.element.attrs[0].value); - } - - if (settingName) - { - PPH_STRING settingValue; - - settingValue = PhGetOpaqueXmlNodeText(currentNode); - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - { - PPH_SETTING setting; - - setting = PhpLookupSetting(&settingName->sr); - - if (setting) - { - PhpFreeSettingValue(setting->Type, setting); - - if (!PhpSettingFromString( - setting->Type, - &settingValue->sr, - settingValue, - setting - )) - { - PhpSettingFromString( - setting->Type, - &setting->DefaultValue, - NULL, - setting - ); - } - } - else - { - setting = PhAllocate(sizeof(PH_SETTING)); - setting->Name.Buffer = PhAllocateCopy(settingName->Buffer, settingName->Length + sizeof(WCHAR)); - setting->Name.Length = settingName->Length; - PhReferenceObject(settingValue); - setting->u.Pointer = settingValue; - - PhAddItemList(PhIgnoredSettings, setting); - } - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); - - PhDereferenceObject(settingValue); - PhDereferenceObject(settingName); - } - - currentNode = currentNode->next; - } - - mxmlDelete(topNode); - - PhUpdateCachedSettings(); - - return STATUS_SUCCESS; -} - -char *PhpSettingsSaveCallback( - _In_ mxml_node_t *node, - _In_ int position - ) -{ - if (PhEqualBytesZ(node->value.element.name, "setting", TRUE)) - { - if (position == MXML_WS_BEFORE_OPEN) - return " "; - else if (position == MXML_WS_AFTER_CLOSE) - return "\r\n"; - } - else if (PhEqualBytesZ(node->value.element.name, "settings", TRUE)) - { - if (position == MXML_WS_AFTER_OPEN) - return "\r\n"; - } - - return NULL; -} - -mxml_node_t *PhpCreateSettingElement( - _Inout_ mxml_node_t *ParentNode, - _In_ PPH_STRINGREF SettingName, - _In_ PPH_STRINGREF SettingValue - ) -{ - mxml_node_t *settingNode; - mxml_node_t *textNode; - PPH_BYTES settingNameUtf8; - PPH_BYTES settingValueUtf8; - - // Create the setting element. - - settingNode = mxmlNewElement(ParentNode, "setting"); - - settingNameUtf8 = PhConvertUtf16ToUtf8Ex(SettingName->Buffer, SettingName->Length); - mxmlElementSetAttr(settingNode, "name", settingNameUtf8->Buffer); - PhDereferenceObject(settingNameUtf8); - - // Set the value. - - settingValueUtf8 = PhConvertUtf16ToUtf8Ex(SettingValue->Buffer, SettingValue->Length); - textNode = mxmlNewOpaque(settingNode, settingValueUtf8->Buffer); - PhDereferenceObject(settingValueUtf8); - - return settingNode; -} - -NTSTATUS PhSaveSettings( - _In_ PWSTR FileName - ) -{ - NTSTATUS status; - HANDLE fileHandle; - mxml_node_t *topNode; - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_SETTING setting; - - topNode = mxmlNewElement(MXML_NO_PARENT, "settings"); - - PhAcquireQueuedLockShared(&PhSettingsLock); - - PhBeginEnumHashtable(PhSettingsHashtable, &enumContext); - - while (setting = PhNextEnumHashtable(&enumContext)) - { - PPH_STRING settingValue; - - settingValue = PhpSettingToString(setting->Type, setting); - PhpCreateSettingElement(topNode, &setting->Name, &settingValue->sr); - PhDereferenceObject(settingValue); - } - - // Write the ignored settings. - { - ULONG i; - - for (i = 0; i < PhIgnoredSettings->Count; i++) - { - PPH_STRING settingValue; - - setting = PhIgnoredSettings->Items[i]; - settingValue = setting->u.Pointer; - PhpCreateSettingElement(topNode, &setting->Name, &settingValue->sr); - } - } - - PhReleaseQueuedLockShared(&PhSettingsLock); - - // Create the directory if it does not exist. - { - PPH_STRING fullPath; - ULONG indexOfFileName; - PPH_STRING directoryName; - - fullPath = PhGetFullPath(FileName, &indexOfFileName); - - if (fullPath) - { - if (indexOfFileName != -1) - { - directoryName = PhSubstring(fullPath, 0, indexOfFileName); - SHCreateDirectoryEx(NULL, directoryName->Buffer, NULL); - PhDereferenceObject(directoryName); - } - - PhDereferenceObject(fullPath); - } - } - - status = PhCreateFileWin32( - &fileHandle, - FileName, - FILE_GENERIC_WRITE, - 0, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - { - mxmlDelete(topNode); - return status; - } - - mxmlSaveFd(topNode, fileHandle, PhpSettingsSaveCallback); - mxmlDelete(topNode); - NtClose(fileHandle); - - return STATUS_SUCCESS; -} - -VOID PhResetSettings( - VOID - ) -{ - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_SETTING setting; - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - PhBeginEnumHashtable(PhSettingsHashtable, &enumContext); - - while (setting = PhNextEnumHashtable(&enumContext)) - { - PhpFreeSettingValue(setting->Type, setting); - PhpSettingFromString(setting->Type, &setting->DefaultValue, NULL, setting); - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); -} - -VOID PhAddSettings( - _In_ PPH_SETTING_CREATE Settings, - _In_ ULONG NumberOfSettings - ) -{ - ULONG i; - - PhAcquireQueuedLockExclusive(&PhSettingsLock); - - for (i = 0; i < NumberOfSettings; i++) - { - PH_STRINGREF name; - PH_STRINGREF defaultValue; - - PhInitializeStringRefLongHint(&name, Settings[i].Name); - PhInitializeStringRefLongHint(&defaultValue, Settings[i].DefaultValue); - PhpAddSetting(Settings[i].Type, &name, &defaultValue); - } - - PhReleaseQueuedLockExclusive(&PhSettingsLock); -} +/* + * Process Hacker - + * program settings cache + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#define PH_SETTINGS_PRIVATE +#include + +#define PH_UPDATE_SETTING(Name) \ + (PhCs##Name = PhGetIntegerSetting(L#Name)) + +VOID PhAddDefaultSettings( + VOID + ) +{ + PhpAddIntegerSetting(L"AllowOnlyOneInstance", L"1"); + PhpAddIntegerSetting(L"CloseOnEscape", L"0"); + PhpAddIntegerSetting(L"CollapseServicesOnStart", L"0"); + PhpAddStringSetting(L"DbgHelpSearchPath", L"SRV*C:\\Symbols*https://msdl.microsoft.com/download/symbols"); + PhpAddIntegerSetting(L"DbgHelpUndecorate", L"1"); + PhpAddStringSetting(L"DisabledPlugins", L""); + PhpAddIntegerSetting(L"ElevationLevel", L"1"); // PromptElevateAction + PhpAddIntegerSetting(L"EnableCycleCpuUsage", L"1"); + PhpAddIntegerSetting(L"EnableInstantTooltips", L"0"); + PhpAddIntegerSetting(L"EnableKph", L"1"); + PhpAddIntegerSetting(L"EnableKphWarnings", L"1"); + PhpAddIntegerSetting(L"EnableHandleSnapshot", L"1"); + PhpAddIntegerSetting(L"EnableNetworkResolve", L"1"); + PhpAddIntegerSetting(L"EnablePlugins", L"1"); + PhpAddIntegerSetting(L"EnableServiceNonPoll", L"0"); + PhpAddIntegerSetting(L"EnableStage2", L"1"); + PhpAddIntegerSetting(L"EnableServiceStage2", L"0"); + PhpAddIntegerSetting(L"EnableWarnings", L"1"); + PhpAddIntegerSetting(L"EnableWindowText", L"1"); + PhpAddIntegerSetting(L"EnableThemeSupport", L"0"); + PhpAddIntegerSetting(L"EnableTooltipSupport", L"1"); + PhpAddIntegerSetting(L"EnableSecurityAdvancedDialog", L"1"); + PhpAddIntegerSetting(L"EnableLinuxSubsystemSupport", L"0"); + PhpAddStringSetting(L"EnvironmentTreeListColumns", L""); + PhpAddStringSetting(L"EnvironmentTreeListSort", L"0,0"); // 0, NoSortOrder + PhpAddIntegerSetting(L"EnvironmentTreeListFlags", L"0"); + PhpAddIntegerSetting(L"FindObjRegex", L"0"); + PhpAddStringSetting(L"FindObjTreeListColumns", L""); + PhpAddIntegerPairSetting(L"FindObjWindowPosition", L"0,0"); + PhpAddScalableIntegerPairSetting(L"FindObjWindowSize", L"@96|550,420"); + PhpAddStringSetting(L"FileBrowseExecutable", L"%SystemRoot%\\explorer.exe /select,\"%s\""); + PhpAddIntegerSetting(L"FirstRun", L"1"); + PhpAddStringSetting(L"Font", L""); // null + PhpAddIntegerSetting(L"ForceNoParent", L"1"); + PhpAddIntegerSetting(L"KphBuildNumber", L"0"); + PhpAddStringSetting(L"HandleTreeListColumns", L""); + PhpAddStringSetting(L"HandleTreeListSort", L"0,1"); // 0, AscendingSortOrder + PhpAddIntegerSetting(L"HandleTreeListFlags", L"3"); + PhpAddIntegerPairSetting(L"HandlePropertiesWindowPosition", L"0,0"); + PhpAddScalableIntegerPairSetting(L"HandlePropertiesWindowSize", L"@96|260,260"); + PhpAddIntegerSetting(L"HiddenProcessesMenuEnabled", L"0"); + PhpAddStringSetting(L"HiddenProcessesListViewColumns", L""); + PhpAddIntegerPairSetting(L"HiddenProcessesWindowPosition", L"400,400"); + PhpAddScalableIntegerPairSetting(L"HiddenProcessesWindowSize", L"@96|520,400"); + PhpAddIntegerSetting(L"HideDriverServices", L"0"); + PhpAddIntegerSetting(L"HideFreeRegions", L"1"); + PhpAddIntegerSetting(L"HideOnClose", L"0"); + PhpAddIntegerSetting(L"HideOnMinimize", L"0"); + PhpAddIntegerSetting(L"HideOtherUserProcesses", L"0"); + PhpAddIntegerSetting(L"HideSignedProcesses", L"0"); + PhpAddIntegerSetting(L"HideWaitingConnections", L"0"); + PhpAddIntegerSetting(L"HighlightingDuration", L"3e8"); // 1000ms + PhpAddStringSetting(L"IconGuids", L""); + PhpAddStringSetting(L"IconSettings", L"1|1"); + PhpAddIntegerSetting(L"IconNotifyMask", L"c"); // PH_NOTIFY_SERVICE_CREATE | PH_NOTIFY_SERVICE_DELETE + PhpAddIntegerSetting(L"IconProcesses", L"f"); // 15 + PhpAddIntegerSetting(L"IconSingleClick", L"0"); + PhpAddIntegerSetting(L"IconTogglesVisibility", L"1"); + PhpAddStringSetting(L"JobListViewColumns", L""); + //PhpAddIntegerSetting(L"KphUnloadOnShutdown", L"0"); + PhpAddIntegerSetting(L"LogEntries", L"200"); // 512 + PhpAddStringSetting(L"LogListViewColumns", L""); + PhpAddIntegerPairSetting(L"LogWindowPosition", L"0,0"); + PhpAddScalableIntegerPairSetting(L"LogWindowSize", L"@96|450,500"); + PhpAddIntegerSetting(L"MainWindowAlwaysOnTop", L"0"); + PhpAddStringSetting(L"MainWindowClassName", L"MainWindowClassName"); + PhpAddIntegerSetting(L"MainWindowOpacity", L"0"); // means 100% + PhpAddIntegerPairSetting(L"MainWindowPosition", L"100,100"); + PhpAddScalableIntegerPairSetting(L"MainWindowSize", L"@96|800,600"); + PhpAddIntegerSetting(L"MainWindowState", L"1"); + PhpAddIntegerSetting(L"MainWindowTabRestoreEnabled", L"0"); + PhpAddIntegerSetting(L"MainWindowTabRestoreIndex", L"0"); + PhpAddIntegerSetting(L"MaxSizeUnit", L"6"); + PhpAddIntegerSetting(L"MemEditBytesPerRow", L"10"); // 16 + PhpAddStringSetting(L"MemEditGotoChoices", L""); + PhpAddIntegerPairSetting(L"MemEditPosition", L"450,450"); + PhpAddScalableIntegerPairSetting(L"MemEditSize", L"@96|600,500"); + PhpAddStringSetting(L"MemFilterChoices", L""); + PhpAddStringSetting(L"MemResultsListViewColumns", L""); + PhpAddIntegerPairSetting(L"MemResultsPosition", L"300,300"); + PhpAddScalableIntegerPairSetting(L"MemResultsSize", L"@96|500,520"); + PhpAddIntegerSetting(L"MemoryListFlags", L"3"); + PhpAddStringSetting(L"MemoryTreeListColumns", L""); + PhpAddStringSetting(L"MemoryTreeListSort", L"0,0"); // 0, NoSortOrder + PhpAddIntegerPairSetting(L"MemoryListsWindowPosition", L"400,400"); + PhpAddStringSetting(L"MemoryReadWriteAddressChoices", L""); + PhpAddStringSetting(L"MiniInfoWindowClassName", L"MiniInfoWindowClassName"); + PhpAddIntegerSetting(L"MiniInfoWindowEnabled", L"1"); + PhpAddIntegerSetting(L"MiniInfoWindowOpacity", L"0"); // means 100% + PhpAddIntegerSetting(L"MiniInfoWindowPinned", L"0"); + PhpAddIntegerPairSetting(L"MiniInfoWindowPosition", L"200,200"); + PhpAddIntegerSetting(L"MiniInfoWindowRefreshAutomatically", L"1"); + PhpAddScalableIntegerPairSetting(L"MiniInfoWindowSize", L"@96|10,200"); + PhpAddIntegerSetting(L"ModuleTreeListFlags", L"1"); + PhpAddStringSetting(L"ModuleTreeListColumns", L""); + PhpAddStringSetting(L"ModuleTreeListSort", L"0,0"); // 0, NoSortOrder + PhpAddStringSetting(L"NetworkTreeListColumns", L""); + PhpAddStringSetting(L"NetworkTreeListSort", L"0,1"); // 0, AscendingSortOrder + PhpAddIntegerSetting(L"NoPurgeProcessRecords", L"0"); + PhpAddIntegerPairSetting(L"OptionsWindowPosition", L"0,0"); + PhpAddScalableIntegerPairSetting(L"OptionsWindowSize", L"@96|900,590"); + PhpAddIntegerPairSetting(L"PluginManagerWindowPosition", L"0,0"); + PhpAddScalableIntegerPairSetting(L"PluginManagerWindowSize", L"@96|900,590"); + PhpAddStringSetting(L"PluginManagerTreeListColumns", L""); + PhpAddStringSetting(L"PluginsDirectory", L"plugins"); + PhpAddStringSetting(L"ProcessServiceListViewColumns", L""); + PhpAddStringSetting(L"ProcessTreeColumnSetConfig", L""); + PhpAddStringSetting(L"ProcessTreeListColumns", L""); + PhpAddStringSetting(L"ProcessTreeListSort", L"0,0"); // 0, NoSortOrder + PhpAddStringSetting(L"ProcPropPage", L"General"); + PhpAddIntegerPairSetting(L"ProcPropPosition", L"200,200"); + PhpAddScalableIntegerPairSetting(L"ProcPropSize", L"@96|460,580"); + PhpAddStringSetting(L"ProcStatPropPageGroupStates", L""); + PhpAddStringSetting(L"ProgramInspectExecutables", L"peview.exe \"%s\""); + PhpAddIntegerSetting(L"PropagateCpuUsage", L"0"); + PhpAddStringSetting(L"RunAsProgram", L""); + PhpAddStringSetting(L"RunAsUserName", L""); + PhpAddIntegerSetting(L"RunFileDlgState", L"0"); + PhpAddIntegerSetting(L"SampleCount", L"200"); // 512 + PhpAddIntegerSetting(L"SampleCountAutomatic", L"1"); + PhpAddIntegerSetting(L"ScrollToNewProcesses", L"0"); + PhpAddStringSetting(L"SearchEngine", L"https://www.google.com/search?q=\"%s\""); + PhpAddIntegerPairSetting(L"ServiceWindowPosition", L"0,0"); + PhpAddStringSetting(L"ServiceListViewColumns", L""); + PhpAddStringSetting(L"ServiceTreeListColumns", L""); + PhpAddStringSetting(L"ServiceTreeListSort", L"0,1"); // 0, AscendingSortOrder + PhpAddIntegerPairSetting(L"SessionShadowHotkey", L"106,2"); // VK_MULTIPLY,KBDCTRL + PhpAddIntegerSetting(L"ShowPluginLoadErrors", L"0"); + PhpAddIntegerSetting(L"ShowCommitInSummary", L"1"); + PhpAddIntegerSetting(L"ShowCpuBelow001", L"0"); + PhpAddIntegerSetting(L"ShowHexId", L"0"); + PhpAddIntegerSetting(L"StartHidden", L"0"); + PhpAddIntegerSetting(L"SysInfoWindowAlwaysOnTop", L"0"); + PhpAddIntegerSetting(L"SysInfoWindowOneGraphPerCpu", L"0"); + PhpAddIntegerPairSetting(L"SysInfoWindowPosition", L"200,200"); + PhpAddStringSetting(L"SysInfoWindowSection", L""); + PhpAddScalableIntegerPairSetting(L"SysInfoWindowSize", L"@96|620,590"); + PhpAddIntegerSetting(L"SysInfoWindowState", L"1"); + PhpAddIntegerSetting(L"ThinRows", L"0"); + PhpAddStringSetting(L"ThreadTreeListColumns", L""); + PhpAddStringSetting(L"ThreadTreeListSort", L"1,2"); // 1, DescendingSortOrder + PhpAddIntegerSetting(L"ThreadTreeListFlags", L"0"); + PhpAddStringSetting(L"ThreadStackTreeListColumns", L""); + PhpAddScalableIntegerPairSetting(L"ThreadStackWindowSize", L"@96|420,400"); + PhpAddStringSetting(L"TokenGroupsListViewColumns", L""); + PhpAddStringSetting(L"TokenGroupsListViewStates", L""); + PhpAddStringSetting(L"TokenGroupsListViewSort", L"1,2"); + PhpAddIntegerSetting(L"TokenSplitterEnable", L"0"); + PhpAddIntegerSetting(L"TokenSplitterPosition", L"150"); + PhpAddStringSetting(L"TokenPrivilegesListViewColumns", L""); + PhpAddIntegerSetting(L"TreeListBorderEnable", L"0"); + PhpAddIntegerSetting(L"TreeListCustomColorsEnable", L"0"); + PhpAddIntegerSetting(L"TreeListCustomColorText", L"0"); + PhpAddIntegerSetting(L"TreeListCustomColorFocus", L"0"); + PhpAddIntegerSetting(L"TreeListCustomColorSelection", L"0"); + PhpAddIntegerSetting(L"UpdateInterval", L"3e8"); // 1000ms + PhpAddIntegerSetting(L"WmiProviderEnableHiddenMenu", L"0"); + PhpAddStringSetting(L"WmiProviderListViewColumns", L""); + + // Colors are specified with R in the lowest byte, then G, then B. So: bbggrr. + PhpAddIntegerSetting(L"ColorNew", L"00ff7f"); // Chartreuse + PhpAddIntegerSetting(L"ColorRemoved", L"283cff"); + PhpAddIntegerSetting(L"UseColorOwnProcesses", L"1"); + PhpAddIntegerSetting(L"ColorOwnProcesses", L"aaffff"); + PhpAddIntegerSetting(L"UseColorSystemProcesses", L"1"); + PhpAddIntegerSetting(L"ColorSystemProcesses", L"ffccaa"); + PhpAddIntegerSetting(L"UseColorServiceProcesses", L"1"); + PhpAddIntegerSetting(L"ColorServiceProcesses", L"ffffcc"); + PhpAddIntegerSetting(L"UseColorJobProcesses", L"0"); + PhpAddIntegerSetting(L"ColorJobProcesses", L"3f85cd"); // Peru + PhpAddIntegerSetting(L"UseColorWow64Processes", L"0"); + PhpAddIntegerSetting(L"ColorWow64Processes", L"8f8fbc"); // Rosy Brown + PhpAddIntegerSetting(L"UseColorDebuggedProcesses", L"1"); + PhpAddIntegerSetting(L"ColorDebuggedProcesses", L"ffbbcc"); + PhpAddIntegerSetting(L"UseColorElevatedProcesses", L"1"); + PhpAddIntegerSetting(L"ColorElevatedProcesses", L"00aaff"); + PhpAddIntegerSetting(L"UseColorHandleFiltered", L"1"); + PhpAddIntegerSetting(L"ColorHandleFiltered", L"000000"); // Black + PhpAddIntegerSetting(L"UseColorImmersiveProcesses", L"1"); + PhpAddIntegerSetting(L"ColorImmersiveProcesses", L"cbc0ff"); // Pink + PhpAddIntegerSetting(L"UseColorPicoProcesses", L"1"); + PhpAddIntegerSetting(L"ColorPicoProcesses", L"ff8000"); // Blue + PhpAddIntegerSetting(L"UseColorSuspended", L"1"); + PhpAddIntegerSetting(L"ColorSuspended", L"777777"); + PhpAddIntegerSetting(L"UseColorDotNet", L"1"); + PhpAddIntegerSetting(L"ColorDotNet", L"00ffde"); + PhpAddIntegerSetting(L"UseColorPacked", L"1"); + PhpAddIntegerSetting(L"ColorPacked", L"9314ff"); // Deep Pink + PhpAddIntegerSetting(L"UseColorGuiThreads", L"1"); + PhpAddIntegerSetting(L"ColorGuiThreads", L"77ffff"); + PhpAddIntegerSetting(L"UseColorRelocatedModules", L"1"); + PhpAddIntegerSetting(L"ColorRelocatedModules", L"80c0ff"); + PhpAddIntegerSetting(L"UseColorProtectedHandles", L"1"); + PhpAddIntegerSetting(L"ColorProtectedHandles", L"777777"); + PhpAddIntegerSetting(L"UseColorInheritHandles", L"1"); + PhpAddIntegerSetting(L"ColorInheritHandles", L"ffff77"); + PhpAddIntegerSetting(L"UseColorServiceDisabled", L"1"); + PhpAddIntegerSetting(L"ColorServiceDisabled", L"6d6d6d"); // Dark grey + PhpAddIntegerSetting(L"UseColorServiceStop", L"1"); + PhpAddIntegerSetting(L"ColorServiceStop", L"6d6d6d"); // Dark grey + PhpAddIntegerSetting(L"UseColorUnknown", L"1"); + PhpAddIntegerSetting(L"ColorUnknown", L"8080ff"); // Light Red + + PhpAddIntegerSetting(L"UseColorSystemThreadStack", L"0"); + PhpAddIntegerSetting(L"ColorSystemThreadStack", L"ffccaa"); + PhpAddIntegerSetting(L"UseColorUserThreadStack", L"0"); + PhpAddIntegerSetting(L"ColorUserThreadStack", L"aaffff"); + + PhpAddIntegerSetting(L"GraphShowText", L"1"); + PhpAddIntegerSetting(L"GraphColorMode", L"0"); + PhpAddIntegerSetting(L"ColorCpuKernel", L"00ff00"); + PhpAddIntegerSetting(L"ColorCpuUser", L"0000ff"); + PhpAddIntegerSetting(L"ColorIoReadOther", L"00ffff"); + PhpAddIntegerSetting(L"ColorIoWrite", L"ff0077"); + PhpAddIntegerSetting(L"ColorPrivate", L"0077ff"); + PhpAddIntegerSetting(L"ColorPhysical", L"ff8000"); // Blue +} + +VOID PhUpdateCachedSettings( + VOID + ) +{ + PH_UPDATE_SETTING(CollapseServicesOnStart); + PH_UPDATE_SETTING(ForceNoParent); + PH_UPDATE_SETTING(HighlightingDuration); + PH_UPDATE_SETTING(PropagateCpuUsage); + PH_UPDATE_SETTING(ScrollToNewProcesses); + PH_UPDATE_SETTING(ShowCpuBelow001); + PH_UPDATE_SETTING(UpdateInterval); + + PH_UPDATE_SETTING(ColorNew); + PH_UPDATE_SETTING(ColorRemoved); + PH_UPDATE_SETTING(UseColorOwnProcesses); + PH_UPDATE_SETTING(ColorOwnProcesses); + PH_UPDATE_SETTING(UseColorSystemProcesses); + PH_UPDATE_SETTING(ColorSystemProcesses); + PH_UPDATE_SETTING(UseColorServiceProcesses); + PH_UPDATE_SETTING(ColorServiceProcesses); + PH_UPDATE_SETTING(UseColorJobProcesses); + PH_UPDATE_SETTING(ColorJobProcesses); + PH_UPDATE_SETTING(UseColorWow64Processes); + PH_UPDATE_SETTING(ColorWow64Processes); + PH_UPDATE_SETTING(UseColorDebuggedProcesses); + PH_UPDATE_SETTING(ColorDebuggedProcesses); + PH_UPDATE_SETTING(UseColorHandleFiltered); + PH_UPDATE_SETTING(ColorHandleFiltered); + PH_UPDATE_SETTING(UseColorElevatedProcesses); + PH_UPDATE_SETTING(ColorElevatedProcesses); + PH_UPDATE_SETTING(UseColorPicoProcesses); + PH_UPDATE_SETTING(ColorPicoProcesses); + PH_UPDATE_SETTING(UseColorImmersiveProcesses); + PH_UPDATE_SETTING(ColorImmersiveProcesses); + PH_UPDATE_SETTING(UseColorSuspended); + PH_UPDATE_SETTING(ColorSuspended); + PH_UPDATE_SETTING(UseColorDotNet); + PH_UPDATE_SETTING(ColorDotNet); + PH_UPDATE_SETTING(UseColorPacked); + PH_UPDATE_SETTING(ColorPacked); + PH_UPDATE_SETTING(UseColorGuiThreads); + PH_UPDATE_SETTING(ColorGuiThreads); + PH_UPDATE_SETTING(UseColorRelocatedModules); + PH_UPDATE_SETTING(ColorRelocatedModules); + PH_UPDATE_SETTING(UseColorProtectedHandles); + PH_UPDATE_SETTING(ColorProtectedHandles); + PH_UPDATE_SETTING(UseColorInheritHandles); + PH_UPDATE_SETTING(ColorInheritHandles); + PH_UPDATE_SETTING(UseColorServiceDisabled); + PH_UPDATE_SETTING(ColorServiceDisabled); + PH_UPDATE_SETTING(UseColorServiceStop); + PH_UPDATE_SETTING(ColorServiceStop); + PH_UPDATE_SETTING(UseColorUnknown); + PH_UPDATE_SETTING(ColorUnknown); + + PH_UPDATE_SETTING(UseColorSystemThreadStack); + PH_UPDATE_SETTING(ColorSystemThreadStack); + PH_UPDATE_SETTING(UseColorUserThreadStack); + PH_UPDATE_SETTING(ColorUserThreadStack); + + PH_UPDATE_SETTING(GraphShowText); + PH_UPDATE_SETTING(GraphColorMode); + PH_UPDATE_SETTING(ColorCpuKernel); + PH_UPDATE_SETTING(ColorCpuUser); + PH_UPDATE_SETTING(ColorIoReadOther); + PH_UPDATE_SETTING(ColorIoWrite); + PH_UPDATE_SETTING(ColorPrivate); + PH_UPDATE_SETTING(ColorPhysical); +} diff --git a/ProcessHacker/srvcr.c b/ProcessHacker/srvcr.c index 5ad4500bd80c..e582d69fc301 100644 --- a/ProcessHacker/srvcr.c +++ b/ProcessHacker/srvcr.c @@ -1,226 +1,226 @@ -/* - * Process Hacker - - * service creation dialog - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -#include -#include - -INT_PTR CALLBACK PhpCreateServiceDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowCreateServiceDialog( - _In_ HWND ParentWindowHandle - ) -{ - DialogBox( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_CREATESERVICE), - ParentWindowHandle, - PhpCreateServiceDlgProc - ); -} - -INT_PTR CALLBACK PhpCreateServiceDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_TYPE), PhServiceTypeStrings, - sizeof(PhServiceTypeStrings) / sizeof(WCHAR *)); - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_STARTTYPE), PhServiceStartTypeStrings, - sizeof(PhServiceStartTypeStrings) / sizeof(WCHAR *)); - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), PhServiceErrorControlStrings, - sizeof(PhServiceErrorControlStrings) / sizeof(WCHAR *)); - - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Own Process", FALSE); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_STARTTYPE), L"Demand Start", FALSE); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), L"Ignore", FALSE); - - if (!PhGetOwnTokenAttributes().Elevated) - { - SendMessage(GetDlgItem(hwndDlg, IDOK), BCM_SETSHIELD, 0, TRUE); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_NAME), TRUE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - EndDialog(hwndDlg, IDCANCEL); - } - break; - case IDOK: - { - NTSTATUS status = 0; - BOOLEAN success = FALSE; - SC_HANDLE scManagerHandle; - SC_HANDLE serviceHandle; - ULONG win32Result = 0; - PPH_STRING serviceName; - PPH_STRING serviceDisplayName; - PPH_STRING serviceTypeString; - PPH_STRING serviceStartTypeString; - PPH_STRING serviceErrorControlString; - ULONG serviceType; - ULONG serviceStartType; - ULONG serviceErrorControl; - PPH_STRING serviceBinaryPath; - - serviceName = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_NAME))); - serviceDisplayName = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_DISPLAYNAME))); - - serviceTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_TYPE))); - serviceStartTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_STARTTYPE))); - serviceErrorControlString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_ERRORCONTROL))); - serviceType = PhGetServiceTypeInteger(serviceTypeString->Buffer); - serviceStartType = PhGetServiceStartTypeInteger(serviceStartTypeString->Buffer); - serviceErrorControl = PhGetServiceErrorControlInteger(serviceErrorControlString->Buffer); - - serviceBinaryPath = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_BINARYPATH))); - - if (PhGetOwnTokenAttributes().Elevated) - { - if (scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) - { - if (serviceHandle = CreateService( - scManagerHandle, - serviceName->Buffer, - serviceDisplayName->Buffer, - SERVICE_CHANGE_CONFIG, - serviceType, - serviceStartType, - serviceErrorControl, - serviceBinaryPath->Buffer, - NULL, - NULL, - NULL, - NULL, - L"" - )) - { - EndDialog(hwndDlg, IDOK); - CloseServiceHandle(serviceHandle); - success = TRUE; - } - else - { - win32Result = GetLastError(); - } - - CloseServiceHandle(scManagerHandle); - } - else - { - win32Result = GetLastError(); - } - } - else - { - if (PhUiConnectToPhSvc(hwndDlg, FALSE)) - { - status = PhSvcCallCreateService( - serviceName->Buffer, - serviceDisplayName->Buffer, - serviceType, - serviceStartType, - serviceErrorControl, - serviceBinaryPath->Buffer, - NULL, - NULL, - NULL, - NULL, - L"" - ); - PhUiDisconnectFromPhSvc(); - - if (NT_SUCCESS(status)) - { - EndDialog(hwndDlg, IDOK); - success = TRUE; - } - } - else - { - // User cancelled elevation. - success = TRUE; - } - } - - if (!success) - PhShowStatus(hwndDlg, L"Unable to create the service", status, win32Result); - } - break; - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Executable files (*.exe;*.sys)", L"*.exe;*.sys" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - fileName = PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_BINARYPATH)); - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - PhDereferenceObject(fileName); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PhGetFileDialogFileName(fileDialog); - SetDlgItemText(hwndDlg, IDC_BINARYPATH, fileName->Buffer); - PhDereferenceObject(fileName); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * service creation dialog + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#include +#include + +INT_PTR CALLBACK PhpCreateServiceDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhShowCreateServiceDialog( + _In_ HWND ParentWindowHandle + ) +{ + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_CREATESERVICE), + PhCsForceNoParent ? NULL : ParentWindowHandle, + PhpCreateServiceDlgProc + ); +} + +INT_PTR CALLBACK PhpCreateServiceDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_TYPE), PhServiceTypeStrings, RTL_NUMBER_OF(PhServiceTypeStrings)); + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_STARTTYPE), PhServiceStartTypeStrings, RTL_NUMBER_OF(PhServiceStartTypeStrings)); + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), PhServiceErrorControlStrings, RTL_NUMBER_OF(PhServiceErrorControlStrings)); + + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), L"Own Process", FALSE); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_STARTTYPE), L"Demand Start", FALSE); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), L"Ignore", FALSE); + + if (!PhGetOwnTokenAttributes().Elevated) + { + Button_SetElevationRequiredState(GetDlgItem(hwndDlg, IDOK), TRUE); + } + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_NAME)); + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + { + EndDialog(hwndDlg, IDCANCEL); + } + break; + case IDOK: + { + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN success = FALSE; + SC_HANDLE scManagerHandle; + SC_HANDLE serviceHandle; + ULONG win32Result = ERROR_SUCCESS; + PPH_STRING serviceName; + PPH_STRING serviceDisplayName; + PPH_STRING serviceTypeString; + PPH_STRING serviceStartTypeString; + PPH_STRING serviceErrorControlString; + ULONG serviceType; + ULONG serviceStartType; + ULONG serviceErrorControl; + PPH_STRING serviceBinaryPath; + + serviceName = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_NAME))); + serviceDisplayName = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_DISPLAYNAME))); + + serviceTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_TYPE))); + serviceStartTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_STARTTYPE))); + serviceErrorControlString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_ERRORCONTROL))); + serviceType = PhGetServiceTypeInteger(serviceTypeString->Buffer); + serviceStartType = PhGetServiceStartTypeInteger(serviceStartTypeString->Buffer); + serviceErrorControl = PhGetServiceErrorControlInteger(serviceErrorControlString->Buffer); + + serviceBinaryPath = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_BINARYPATH))); + + if (PhGetOwnTokenAttributes().Elevated) + { + if (scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) + { + if (serviceHandle = CreateService( + scManagerHandle, + serviceName->Buffer, + serviceDisplayName->Buffer, + SERVICE_CHANGE_CONFIG, + serviceType, + serviceStartType, + serviceErrorControl, + serviceBinaryPath->Buffer, + NULL, + NULL, + NULL, + NULL, + L"" + )) + { + EndDialog(hwndDlg, IDOK); + CloseServiceHandle(serviceHandle); + success = TRUE; + } + else + { + win32Result = GetLastError(); + } + + CloseServiceHandle(scManagerHandle); + } + else + { + win32Result = GetLastError(); + } + } + else + { + if (PhUiConnectToPhSvc(hwndDlg, FALSE)) + { + status = PhSvcCallCreateService( + serviceName->Buffer, + serviceDisplayName->Buffer, + serviceType, + serviceStartType, + serviceErrorControl, + serviceBinaryPath->Buffer, + NULL, + NULL, + NULL, + NULL, + L"" + ); + PhUiDisconnectFromPhSvc(); + + if (NT_SUCCESS(status)) + { + EndDialog(hwndDlg, IDOK); + success = TRUE; + } + } + else + { + // User cancelled elevation. + success = TRUE; + } + } + + if (!success) + PhShowStatus(hwndDlg, L"Unable to create the service", status, win32Result); + } + break; + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Executable files (*.exe;*.sys)", L"*.exe;*.sys" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + fileName = PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_BINARYPATH)); + PhSetFileDialogFileName(fileDialog, fileName->Buffer); + PhDereferenceObject(fileName); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + fileName = PhGetFileDialogFileName(fileDialog); + PhSetDialogItemText(hwndDlg, IDC_BINARYPATH, fileName->Buffer); + PhDereferenceObject(fileName); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/srvctl.c b/ProcessHacker/srvctl.c index 29b39231266a..7536a0ba5a03 100644 --- a/ProcessHacker/srvctl.c +++ b/ProcessHacker/srvctl.c @@ -1,412 +1,496 @@ -/* - * Process Hacker - - * service list control - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include - -typedef struct _PH_SERVICES_CONTEXT -{ - PPH_SERVICE_ITEM *Services; - ULONG NumberOfServices; - PH_CALLBACK_REGISTRATION ModifiedEventRegistration; - - PWSTR ListViewSettingName; - - PH_LAYOUT_MANAGER LayoutManager; - HWND WindowHandle; -} PH_SERVICES_CONTEXT, *PPH_SERVICES_CONTEXT; - -VOID NTAPI ServiceModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -INT_PTR CALLBACK PhpServicesPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -/** - * Creates a service list property page. - * - * \param ParentWindowHandle The parent of the service list. - * \param Services An array of service items. Each - * service item must have a reference that is transferred - * to this function. The array must be allocated using - * PhAllocate() and must not be freed by the caller; it - * will be freed automatically when no longer needed. - * \param NumberOfServices The number of service items - * in \a Services. - */ -HWND PhCreateServiceListControl( - _In_ HWND ParentWindowHandle, - _In_ PPH_SERVICE_ITEM *Services, - _In_ ULONG NumberOfServices - ) -{ - HWND windowHandle; - PPH_SERVICES_CONTEXT servicesContext; - - servicesContext = PhAllocate(sizeof(PH_SERVICES_CONTEXT)); - - memset(servicesContext, 0, sizeof(PH_SERVICES_CONTEXT)); - servicesContext->Services = Services; - servicesContext->NumberOfServices = NumberOfServices; - - windowHandle = CreateDialogParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_SRVLIST), - ParentWindowHandle, - PhpServicesPageProc, - (LPARAM)servicesContext - ); - - if (!windowHandle) - { - PhFree(servicesContext); - return windowHandle; - } - - return windowHandle; -} - -static VOID NTAPI ServiceModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_SERVICE_MODIFIED_DATA serviceModifiedData = (PPH_SERVICE_MODIFIED_DATA)Parameter; - PPH_SERVICES_CONTEXT servicesContext = (PPH_SERVICES_CONTEXT)Context; - PPH_SERVICE_MODIFIED_DATA copy; - - copy = PhAllocateCopy(serviceModifiedData, sizeof(PH_SERVICE_MODIFIED_DATA)); - - PostMessage(servicesContext->WindowHandle, WM_PH_SERVICE_MODIFIED, 0, (LPARAM)copy); -} - -VOID PhpFixProcessServicesControls( - _In_ HWND hWnd, - _In_opt_ PPH_SERVICE_ITEM ServiceItem - ) -{ - HWND startButton; - HWND pauseButton; - HWND descriptionLabel; - - startButton = GetDlgItem(hWnd, IDC_START); - pauseButton = GetDlgItem(hWnd, IDC_PAUSE); - descriptionLabel = GetDlgItem(hWnd, IDC_DESCRIPTION); - - if (ServiceItem) - { - SC_HANDLE serviceHandle; - PPH_STRING description; - - switch (ServiceItem->State) - { - case SERVICE_RUNNING: - { - SetWindowText(startButton, L"S&top"); - SetWindowText(pauseButton, L"&Pause"); - EnableWindow(startButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); - EnableWindow(pauseButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); - } - break; - case SERVICE_PAUSED: - { - SetWindowText(startButton, L"S&top"); - SetWindowText(pauseButton, L"C&ontinue"); - EnableWindow(startButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); - EnableWindow(pauseButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); - } - break; - case SERVICE_STOPPED: - { - SetWindowText(startButton, L"&Start"); - SetWindowText(pauseButton, L"&Pause"); - EnableWindow(startButton, TRUE); - EnableWindow(pauseButton, FALSE); - } - break; - case SERVICE_START_PENDING: - case SERVICE_CONTINUE_PENDING: - case SERVICE_PAUSE_PENDING: - case SERVICE_STOP_PENDING: - { - SetWindowText(startButton, L"&Start"); - SetWindowText(pauseButton, L"&Pause"); - EnableWindow(startButton, FALSE); - EnableWindow(pauseButton, FALSE); - } - break; - } - - if (serviceHandle = PhOpenService( - ServiceItem->Name->Buffer, - SERVICE_QUERY_CONFIG - )) - { - if (description = PhGetServiceDescription(serviceHandle)) - { - SetWindowText(descriptionLabel, description->Buffer); - PhDereferenceObject(description); - } - - CloseServiceHandle(serviceHandle); - } - } - else - { - SetWindowText(startButton, L"&Start"); - SetWindowText(pauseButton, L"&Pause"); - EnableWindow(startButton, FALSE); - EnableWindow(pauseButton, FALSE); - SetWindowText(descriptionLabel, L""); - } -} - -INT_PTR CALLBACK PhpServicesPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPH_SERVICES_CONTEXT servicesContext; - HWND lvHandle; - - if (uMsg == WM_INITDIALOG) - { - servicesContext = (PPH_SERVICES_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)servicesContext); - } - else - { - servicesContext = (PPH_SERVICES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (uMsg == WM_DESTROY) - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - } - - if (!servicesContext) - return FALSE; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG i; - - PhRegisterCallback( - &PhServiceModifiedEvent, - ServiceModifiedHandler, - servicesContext, - &servicesContext->ModifiedEventRegistration - ); - - servicesContext->WindowHandle = hwndDlg; - - // Initialize the list. - PhSetListViewStyle(lvHandle, TRUE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 220, L"Display name"); - - PhSetExtendedListView(lvHandle); - - for (i = 0; i < servicesContext->NumberOfServices; i++) - { - PPH_SERVICE_ITEM serviceItem; - INT lvItemIndex; - - serviceItem = servicesContext->Services[i]; - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, serviceItem->Name->Buffer, serviceItem); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, serviceItem->DisplayName->Buffer); - } - - ExtendedListView_SortItems(lvHandle); - - PhpFixProcessServicesControls(hwndDlg, NULL); - - PhInitializeLayoutManager(&servicesContext->LayoutManager, hwndDlg); - PhAddLayoutItem(&servicesContext->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), - NULL, PH_ANCHOR_ALL); - PhAddLayoutItem(&servicesContext->LayoutManager, GetDlgItem(hwndDlg, IDC_DESCRIPTION), - NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&servicesContext->LayoutManager, GetDlgItem(hwndDlg, IDC_START), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&servicesContext->LayoutManager, GetDlgItem(hwndDlg, IDC_PAUSE), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - } - break; - case WM_DESTROY: - { - ULONG i; - - for (i = 0; i < servicesContext->NumberOfServices; i++) - PhDereferenceObject(servicesContext->Services[i]); - - PhFree(servicesContext->Services); - - PhUnregisterCallback( - &PhServiceModifiedEvent, - &servicesContext->ModifiedEventRegistration - ); - - if (servicesContext->ListViewSettingName) - PhSaveListViewColumnsToSetting(servicesContext->ListViewSettingName, lvHandle); - - PhDeleteLayoutManager(&servicesContext->LayoutManager); - - PhFree(servicesContext); - } - break; - case WM_COMMAND: - { - INT id = LOWORD(wParam); - - switch (id) - { - case IDC_START: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedListViewItemParam(lvHandle); - - if (serviceItem) - { - switch (serviceItem->State) - { - case SERVICE_RUNNING: - PhUiStopService(hwndDlg, serviceItem); - break; - case SERVICE_PAUSED: - PhUiStopService(hwndDlg, serviceItem); - break; - case SERVICE_STOPPED: - PhUiStartService(hwndDlg, serviceItem); - break; - } - } - } - break; - case IDC_PAUSE: - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedListViewItemParam(lvHandle); - - if (serviceItem) - { - switch (serviceItem->State) - { - case SERVICE_RUNNING: - PhUiPauseService(hwndDlg, serviceItem); - break; - case SERVICE_PAUSED: - PhUiContinueService(hwndDlg, serviceItem); - break; - } - } - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - PhHandleListViewNotifyBehaviors(lParam, lvHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - - switch (header->code) - { - case NM_DBLCLK: - { - if (header->hwndFrom == lvHandle) - { - PPH_SERVICE_ITEM serviceItem = PhGetSelectedListViewItemParam(lvHandle); - - if (serviceItem) - { - PhShowServiceProperties(hwndDlg, serviceItem); - } - } - } - break; - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == lvHandle) - { - //LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; - PPH_SERVICE_ITEM serviceItem = NULL; - - if (ListView_GetSelectedCount(lvHandle) == 1) - serviceItem = PhGetSelectedListViewItemParam(lvHandle); - - PhpFixProcessServicesControls(hwndDlg, serviceItem); - } - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&servicesContext->LayoutManager); - } - break; - case WM_PH_SERVICE_MODIFIED: - { - PPH_SERVICE_MODIFIED_DATA serviceModifiedData = (PPH_SERVICE_MODIFIED_DATA)lParam; - PPH_SERVICE_ITEM serviceItem = NULL; - - if (ListView_GetSelectedCount(lvHandle) == 1) - serviceItem = PhGetSelectedListViewItemParam(lvHandle); - - if (serviceModifiedData->Service == serviceItem) - { - PhpFixProcessServicesControls(hwndDlg, serviceItem); - } - - PhFree(serviceModifiedData); - } - break; - case WM_PH_SET_LIST_VIEW_SETTINGS: - { - PWSTR settingName = (PWSTR)lParam; - - servicesContext->ListViewSettingName = settingName; - PhLoadListViewColumnsFromSetting(settingName, lvHandle); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * service list control + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct _PH_SERVICES_CONTEXT +{ + PPH_SERVICE_ITEM *Services; + ULONG NumberOfServices; + PH_CALLBACK_REGISTRATION ModifiedEventRegistration; + + PWSTR ListViewSettingName; + + PH_LAYOUT_MANAGER LayoutManager; + HWND WindowHandle; + HWND ListViewHandle; +} PH_SERVICES_CONTEXT, *PPH_SERVICES_CONTEXT; + +#define WM_PH_SERVICE_PAGE_MODIFIED (WM_APP + 106) + +VOID NTAPI ServiceModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +INT_PTR CALLBACK PhpServicesPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +/** + * Creates a service list property page. + * + * \param ParentWindowHandle The parent of the service list. + * \param Services An array of service items. Each + * service item must have a reference that is transferred + * to this function. The array must be allocated using + * PhAllocate() and must not be freed by the caller; it + * will be freed automatically when no longer needed. + * \param NumberOfServices The number of service items + * in \a Services. + */ +HWND PhCreateServiceListControl( + _In_ HWND ParentWindowHandle, + _In_ PPH_SERVICE_ITEM *Services, + _In_ ULONG NumberOfServices + ) +{ + HWND windowHandle; + PPH_SERVICES_CONTEXT servicesContext; + + servicesContext = PhAllocateZero(sizeof(PH_SERVICES_CONTEXT)); + servicesContext->Services = Services; + servicesContext->NumberOfServices = NumberOfServices; + + windowHandle = CreateDialogParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_SRVLIST), + ParentWindowHandle, + PhpServicesPageProc, + (LPARAM)servicesContext + ); + + if (!windowHandle) + { + PhFree(servicesContext); + return windowHandle; + } + + return windowHandle; +} + +static VOID NTAPI ServiceModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_SERVICE_MODIFIED_DATA serviceModifiedData = (PPH_SERVICE_MODIFIED_DATA)Parameter; + PPH_SERVICES_CONTEXT servicesContext = (PPH_SERVICES_CONTEXT)Context; + PPH_SERVICE_MODIFIED_DATA copy; + + copy = PhAllocateCopy(serviceModifiedData, sizeof(PH_SERVICE_MODIFIED_DATA)); + + PostMessage(servicesContext->WindowHandle, WM_PH_SERVICE_PAGE_MODIFIED, 0, (LPARAM)copy); +} + +VOID PhpFixProcessServicesControls( + _In_ HWND hWnd, + _In_opt_ PPH_SERVICE_ITEM ServiceItem + ) +{ + HWND startButton; + HWND pauseButton; + HWND descriptionLabel; + + startButton = GetDlgItem(hWnd, IDC_START); + pauseButton = GetDlgItem(hWnd, IDC_PAUSE); + descriptionLabel = GetDlgItem(hWnd, IDC_DESCRIPTION); + + if (ServiceItem) + { + SC_HANDLE serviceHandle; + PPH_STRING description; + + switch (ServiceItem->State) + { + case SERVICE_RUNNING: + { + PhSetWindowText(startButton, L"S&top"); + PhSetWindowText(pauseButton, L"&Pause"); + EnableWindow(startButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); + EnableWindow(pauseButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); + } + break; + case SERVICE_PAUSED: + { + PhSetWindowText(startButton, L"S&top"); + PhSetWindowText(pauseButton, L"C&ontinue"); + EnableWindow(startButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); + EnableWindow(pauseButton, ServiceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); + } + break; + case SERVICE_STOPPED: + { + PhSetWindowText(startButton, L"&Start"); + PhSetWindowText(pauseButton, L"&Pause"); + EnableWindow(startButton, TRUE); + EnableWindow(pauseButton, FALSE); + } + break; + case SERVICE_START_PENDING: + case SERVICE_CONTINUE_PENDING: + case SERVICE_PAUSE_PENDING: + case SERVICE_STOP_PENDING: + { + PhSetWindowText(startButton, L"&Start"); + PhSetWindowText(pauseButton, L"&Pause"); + EnableWindow(startButton, FALSE); + EnableWindow(pauseButton, FALSE); + } + break; + } + + if (serviceHandle = PhOpenService( + ServiceItem->Name->Buffer, + SERVICE_QUERY_CONFIG + )) + { + if (description = PhGetServiceDescription(serviceHandle)) + { + PhSetWindowText(descriptionLabel, description->Buffer); + PhDereferenceObject(description); + } + + CloseServiceHandle(serviceHandle); + } + } + else + { + PhSetWindowText(startButton, L"&Start"); + PhSetWindowText(pauseButton, L"&Pause"); + EnableWindow(startButton, FALSE); + EnableWindow(pauseButton, FALSE); + PhSetWindowText(descriptionLabel, L""); + } +} + +INT_PTR CALLBACK PhpServicesPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_SERVICES_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PPH_SERVICES_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + context->WindowHandle = hwndDlg; + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceProviderModifiedEvent), + ServiceModifiedHandler, + context, + &context->ModifiedEventRegistration + ); + + // Initialize the list. + PhSetListViewStyle(context->ListViewHandle, TRUE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 220, L"Display name"); + PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 220, L"File name"); + + PhSetExtendedListView(context->ListViewHandle); + + for (ULONG i = 0; i < context->NumberOfServices; i++) + { + SC_HANDLE serviceHandle; + PPH_SERVICE_ITEM serviceItem; + INT lvItemIndex; + + serviceItem = context->Services[i]; + lvItemIndex = PhAddListViewItem(context->ListViewHandle, MAXINT, serviceItem->Name->Buffer, serviceItem); + PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 1, serviceItem->DisplayName->Buffer); + + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + PPH_STRING fileName; + + if (fileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle)) + { + PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, 2, PhGetStringOrEmpty(fileName)); + PhDereferenceObject(fileName); + } + + CloseServiceHandle(serviceHandle); + } + } + + ExtendedListView_SortItems(context->ListViewHandle); + + if (context->NumberOfServices > 0) + { + SetFocus(context->ListViewHandle); + ListView_SetItemState(context->ListViewHandle, 0, LVNI_SELECTED, LVNI_SELECTED); + ListView_EnsureVisible(context->ListViewHandle, 0, FALSE); + PhpFixProcessServicesControls(hwndDlg, context->Services[0]); + } + else + { + PhpFixProcessServicesControls(hwndDlg, NULL); + } + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_START), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PAUSE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + ULONG i; + + PhUnregisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceProviderModifiedEvent), + &context->ModifiedEventRegistration + ); + + for (i = 0; i < context->NumberOfServices; i++) + PhDereferenceObject(context->Services[i]); + + PhFree(context->Services); + + if (context->ListViewSettingName) + PhSaveListViewColumnsToSetting(context->ListViewSettingName, context->ListViewHandle); + + PhDeleteLayoutManager(&context->LayoutManager); + + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_START: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedListViewItemParam(context->ListViewHandle); + + if (serviceItem) + { + switch (serviceItem->State) + { + case SERVICE_RUNNING: + PhUiStopService(hwndDlg, serviceItem); + break; + case SERVICE_PAUSED: + PhUiStopService(hwndDlg, serviceItem); + break; + case SERVICE_STOPPED: + PhUiStartService(hwndDlg, serviceItem); + break; + } + } + } + break; + case IDC_PAUSE: + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedListViewItemParam(context->ListViewHandle); + + if (serviceItem) + { + switch (serviceItem->State) + { + case SERVICE_RUNNING: + PhUiPauseService(hwndDlg, serviceItem); + break; + case SERVICE_PAUSED: + PhUiContinueService(hwndDlg, serviceItem); + break; + } + } + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + + switch (header->code) + { + case NM_DBLCLK: + { + if (header->hwndFrom == context->ListViewHandle) + { + PPH_SERVICE_ITEM serviceItem = PhGetSelectedListViewItemParam(context->ListViewHandle); + + if (serviceItem) + { + PhShowServiceProperties(hwndDlg, serviceItem); + } + } + } + break; + case LVN_ITEMCHANGED: + { + if (header->hwndFrom == context->ListViewHandle) + { + //LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header; + PPH_SERVICE_ITEM serviceItem = NULL; + + if (ListView_GetSelectedCount(context->ListViewHandle) == 1) + serviceItem = PhGetSelectedListViewItemParam(context->ListViewHandle); + + PhpFixProcessServicesControls(hwndDlg, serviceItem); + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_PH_SERVICE_PAGE_MODIFIED: + { + PPH_SERVICE_MODIFIED_DATA serviceModifiedData = (PPH_SERVICE_MODIFIED_DATA)lParam; + PPH_SERVICE_ITEM serviceItem = NULL; + + if (ListView_GetSelectedCount(context->ListViewHandle) == 1) + serviceItem = PhGetSelectedListViewItemParam(context->ListViewHandle); + + if (serviceModifiedData->Service == serviceItem) + { + PhpFixProcessServicesControls(hwndDlg, serviceItem); + } + + PhFree(serviceModifiedData); + } + break; + case WM_PH_SET_LIST_VIEW_SETTINGS: + { + PWSTR settingName = (PWSTR)lParam; + + context->ListViewSettingName = settingName; + PhLoadListViewColumnsFromSetting(settingName, context->ListViewHandle); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID* listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/srvlist.c b/ProcessHacker/srvlist.c index f39fb8ad392b..ccaa88dc1180 100644 --- a/ProcessHacker/srvlist.c +++ b/ProcessHacker/srvlist.c @@ -1,904 +1,1014 @@ -/* - * Process Hacker - - * service list - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -BOOLEAN PhpServiceNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG PhpServiceNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpRemoveServiceNode( - _In_ PPH_SERVICE_NODE ServiceNode - ); - -LONG PhpServiceTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpServiceTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -static HWND ServiceTreeListHandle; -static ULONG ServiceTreeListSortColumn; -static PH_SORT_ORDER ServiceTreeListSortOrder; -static PH_CM_MANAGER ServiceTreeListCm; - -static PPH_HASHTABLE ServiceNodeHashtable; // hashtable of all nodes -static PPH_LIST ServiceNodeList; // list of all nodes - -static PH_TN_FILTER_SUPPORT FilterSupport; - -static BOOLEAN ServiceIconsLoaded = FALSE; -static HICON ServiceApplicationIcon; -static HICON ServiceApplicationGoIcon; -static HICON ServiceCogIcon; -static HICON ServiceCogGoIcon; - -BOOLEAN PhServiceTreeListStateHighlighting = TRUE; -static PPH_POINTER_LIST ServiceNodeStateList = NULL; // list of nodes which need to be processed - -VOID PhServiceTreeListInitialization( - VOID - ) -{ - ServiceNodeHashtable = PhCreateHashtable( - sizeof(PPH_SERVICE_NODE), - PhpServiceNodeHashtableEqualFunction, - PhpServiceNodeHashtableHashFunction, - 100 - ); - ServiceNodeList = PhCreateList(100); -} - -BOOLEAN PhpServiceNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_SERVICE_NODE serviceNode1 = *(PPH_SERVICE_NODE *)Entry1; - PPH_SERVICE_NODE serviceNode2 = *(PPH_SERVICE_NODE *)Entry2; - - return serviceNode1->ServiceItem == serviceNode2->ServiceItem; -} - -ULONG PhpServiceNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashIntPtr((ULONG_PTR)(*(PPH_SERVICE_NODE *)Entry)->ServiceItem); -} - -VOID PhInitializeServiceTreeList( - _In_ HWND hwnd - ) -{ - ServiceTreeListHandle = hwnd; - PhSetControlTheme(ServiceTreeListHandle, L"explorer"); - SendMessage(TreeNew_GetTooltips(ServiceTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); - - TreeNew_SetCallback(hwnd, PhpServiceTreeNewCallback, NULL); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHSVTLC_NAME, TRUE, L"Name", 140, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_DISPLAYNAME, TRUE, L"Display name", 220, PH_ALIGN_LEFT, 1, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 2, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_STATUS, TRUE, L"Status", 70, PH_ALIGN_LEFT, 3, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_STARTTYPE, TRUE, L"Start type", 130, PH_ALIGN_LEFT, 4, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_PID, TRUE, L"PID", 50, PH_ALIGN_RIGHT, 5, DT_RIGHT); - - PhAddTreeNewColumn(hwnd, PHSVTLC_BINARYPATH, FALSE, L"Binary path", 180, PH_ALIGN_LEFT, -1, DT_PATH_ELLIPSIS); - PhAddTreeNewColumn(hwnd, PHSVTLC_ERRORCONTROL, FALSE, L"Error control", 70, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_GROUP, FALSE, L"Group", 100, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumn(hwnd, PHSVTLC_DESCRIPTION, FALSE, L"Description", 200, PH_ALIGN_LEFT, -1, 0); - PhAddTreeNewColumnEx(hwnd, PHSVTLC_KEYMODIFIEDTIME, FALSE, L"Key modified time", 140, PH_ALIGN_LEFT, -1, 0, TRUE); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetSort(hwnd, 0, AscendingSortOrder); - - PhCmInitializeManager(&ServiceTreeListCm, hwnd, PHSVTLC_MAXIMUM, PhpServiceTreeNewPostSortFunction); - - if (PhPluginsEnabled) - { - PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; - - treeNewInfo.TreeNewHandle = hwnd; - treeNewInfo.CmData = &ServiceTreeListCm; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), &treeNewInfo); - } - - PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ServiceNodeList); -} - -VOID PhLoadSettingsServiceTreeList( - VOID - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhGetStringSetting(L"ServiceTreeListColumns"); - sortSettings = PhGetStringSetting(L"ServiceTreeListSort"); - PhCmLoadSettingsEx(ServiceTreeListHandle, &ServiceTreeListCm, 0, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -VOID PhSaveSettingsServiceTreeList( - VOID - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(ServiceTreeListHandle, &ServiceTreeListCm, 0, &sortSettings); - PhSetStringSetting2(L"ServiceTreeListColumns", &settings->sr); - PhSetStringSetting2(L"ServiceTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportServiceTreeList( - VOID - ) -{ - return &FilterSupport; -} - -PPH_SERVICE_NODE PhAddServiceNode( - _In_ PPH_SERVICE_ITEM ServiceItem, - _In_ ULONG RunId - ) -{ - PPH_SERVICE_NODE serviceNode; - - serviceNode = PhAllocate(PhEmGetObjectSize(EmServiceNodeType, sizeof(PH_SERVICE_NODE))); - memset(serviceNode, 0, sizeof(PH_SERVICE_NODE)); - PhInitializeTreeNewNode(&serviceNode->Node); - - if (PhServiceTreeListStateHighlighting && RunId != 1) - { - PhChangeShStateTn( - &serviceNode->Node, - &serviceNode->ShState, - &ServiceNodeStateList, - NewItemState, - PhCsColorNew, - NULL - ); - } - - serviceNode->ServiceItem = ServiceItem; - PhReferenceObject(ServiceItem); - - memset(serviceNode->TextCache, 0, sizeof(PH_STRINGREF) * PHSVTLC_MAXIMUM); - serviceNode->Node.TextCache = serviceNode->TextCache; - serviceNode->Node.TextCacheSize = PHSVTLC_MAXIMUM; - - PhAddEntryHashtable(ServiceNodeHashtable, &serviceNode); - PhAddItemList(ServiceNodeList, serviceNode); - - if (FilterSupport.FilterList) - serviceNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &serviceNode->Node); - - PhEmCallObjectOperation(EmServiceNodeType, serviceNode, EmObjectCreate); - - TreeNew_NodesStructured(ServiceTreeListHandle); - - return serviceNode; -} - -PPH_SERVICE_NODE PhFindServiceNode( - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - PH_SERVICE_NODE lookupServiceNode; - PPH_SERVICE_NODE lookupServiceNodePtr = &lookupServiceNode; - PPH_SERVICE_NODE *serviceNode; - - lookupServiceNode.ServiceItem = ServiceItem; - - serviceNode = (PPH_SERVICE_NODE *)PhFindEntryHashtable( - ServiceNodeHashtable, - &lookupServiceNodePtr - ); - - if (serviceNode) - return *serviceNode; - else - return NULL; -} - -VOID PhRemoveServiceNode( - _In_ PPH_SERVICE_NODE ServiceNode - ) -{ - // Remove from the hashtable here to avoid problems in case the key is re-used. - PhRemoveEntryHashtable(ServiceNodeHashtable, &ServiceNode); - - if (PhServiceTreeListStateHighlighting) - { - PhChangeShStateTn( - &ServiceNode->Node, - &ServiceNode->ShState, - &ServiceNodeStateList, - RemovingItemState, - PhCsColorRemoved, - ServiceTreeListHandle - ); - } - else - { - PhpRemoveServiceNode(ServiceNode); - } -} - -VOID PhpRemoveServiceNode( - _In_ PPH_SERVICE_NODE ServiceNode - ) -{ - ULONG index; - - PhEmCallObjectOperation(EmServiceNodeType, ServiceNode, EmObjectDelete); - - // Remove from list and cleanup. - - if ((index = PhFindItemList(ServiceNodeList, ServiceNode)) != -1) - PhRemoveItemList(ServiceNodeList, index); - - PhClearReference(&ServiceNode->BinaryPath); - PhClearReference(&ServiceNode->LoadOrderGroup); - PhClearReference(&ServiceNode->Description); - - PhClearReference(&ServiceNode->TooltipText); - - PhClearReference(&ServiceNode->KeyModifiedTimeText); - - PhDereferenceObject(ServiceNode->ServiceItem); - - PhFree(ServiceNode); - - TreeNew_NodesStructured(ServiceTreeListHandle); -} - -VOID PhUpdateServiceNode( - _In_ PPH_SERVICE_NODE ServiceNode - ) -{ - memset(ServiceNode->TextCache, 0, sizeof(PH_STRINGREF) * PHSVTLC_MAXIMUM); - PhClearReference(&ServiceNode->TooltipText); - - ServiceNode->ValidMask = 0; - PhInvalidateTreeNewNode(&ServiceNode->Node, TN_CACHE_ICON); - TreeNew_NodesStructured(ServiceTreeListHandle); -} - -VOID PhTickServiceNodes( - VOID - ) -{ - if (ServiceTreeListSortOrder != NoSortOrder && ServiceTreeListSortColumn >= PHSVTLC_MAXIMUM) - { - // Sorting is on, but it's not one of our columns. Force a rebuild. (If it was one of our - // columns, the restructure would have been handled in PhUpdateServiceNode.) - TreeNew_NodesStructured(ServiceTreeListHandle); - } - - PH_TICK_SH_STATE_TN(PH_SERVICE_NODE, ShState, ServiceNodeStateList, PhpRemoveServiceNode, PhCsHighlightingDuration, ServiceTreeListHandle, TRUE, NULL); -} - -static VOID PhpUpdateServiceNodeConfig( - _Inout_ PPH_SERVICE_NODE ServiceNode - ) -{ - if (!(ServiceNode->ValidMask & PHSN_CONFIG)) - { - SC_HANDLE serviceHandle; - LPQUERY_SERVICE_CONFIG serviceConfig; - - if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) - { - if (serviceConfig = PhGetServiceConfig(serviceHandle)) - { - if (serviceConfig->lpBinaryPathName) - PhMoveReference(&ServiceNode->BinaryPath, PhCreateString(serviceConfig->lpBinaryPathName)); - if (serviceConfig->lpLoadOrderGroup) - PhMoveReference(&ServiceNode->LoadOrderGroup, PhCreateString(serviceConfig->lpLoadOrderGroup)); - - PhFree(serviceConfig); - } - - CloseServiceHandle(serviceHandle); - } - - ServiceNode->ValidMask |= PHSN_CONFIG; - } -} - -static VOID PhpUpdateServiceNodeDescription( - _Inout_ PPH_SERVICE_NODE ServiceNode - ) -{ - if (!(ServiceNode->ValidMask & PHSN_DESCRIPTION)) - { - SC_HANDLE serviceHandle; - - if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) - { - PhMoveReference(&ServiceNode->Description, PhGetServiceDescription(serviceHandle)); - - CloseServiceHandle(serviceHandle); - } - - ServiceNode->ValidMask |= PHSN_DESCRIPTION; - } -} - -static VOID PhpUpdateServiceNodeKey( - _Inout_ PPH_SERVICE_NODE ServiceNode - ) -{ - if (!(ServiceNode->ValidMask & PHSN_KEY)) - { - static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); - - HANDLE keyHandle; - PPH_STRING keyName; - - keyName = PhConcatStringRef2(&servicesKeyName, &ServiceNode->ServiceItem->Name->sr); - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName->sr, - 0 - ))) - { - PKEY_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhQueryKey(keyHandle, KeyBasicInformation, &basicInfo))) - { - ServiceNode->KeyLastWriteTime = basicInfo->LastWriteTime; - PhFree(basicInfo); - } - - NtClose(keyHandle); - } - - PhDereferenceObject(keyName); - - ServiceNode->ValidMask |= PHSN_KEY; - } -} - -#define SORT_FUNCTION(Column) PhpServiceTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpServiceTreeNewCompare##Column( \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_SERVICE_NODE node1 = *(PPH_SERVICE_NODE *)_elem1; \ - PPH_SERVICE_NODE node2 = *(PPH_SERVICE_NODE *)_elem2; \ - PPH_SERVICE_ITEM serviceItem1 = node1->ServiceItem; \ - PPH_SERVICE_ITEM serviceItem2 = node2->ServiceItem; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - /* if (sortResult == 0) */ \ - /* sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); */ \ - \ - return PhModifySort(sortResult, ServiceTreeListSortOrder); \ -} - -LONG PhpServiceTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(Name) -{ - sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(DisplayName) -{ - sortResult = PhCompareStringWithNull(serviceItem1->DisplayName, serviceItem2->DisplayName, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Type) -{ - sortResult = intcmp(serviceItem1->Type, serviceItem2->Type); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Status) -{ - sortResult = intcmp(serviceItem1->State, serviceItem2->State); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(StartType) -{ - sortResult = intcmp(serviceItem1->StartType, serviceItem2->StartType); - - if (sortResult == 0) - sortResult = intcmp(serviceItem1->DelayedStart, serviceItem2->DelayedStart); - if (sortResult == 0) - sortResult = intcmp(serviceItem1->HasTriggers, serviceItem2->HasTriggers); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Pid) -{ - sortResult = uintptrcmp((ULONG_PTR)serviceItem1->ProcessId, (ULONG_PTR)serviceItem2->ProcessId); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(BinaryPath) -{ - PhpUpdateServiceNodeConfig(node1); - PhpUpdateServiceNodeConfig(node2); - sortResult = PhCompareStringWithNull(node1->BinaryPath, node2->BinaryPath, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ErrorControl) -{ - sortResult = intcmp(serviceItem1->ErrorControl, serviceItem2->ErrorControl); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Group) -{ - PhpUpdateServiceNodeConfig(node1); - PhpUpdateServiceNodeConfig(node2); - sortResult = PhCompareStringWithNull(node1->LoadOrderGroup, node2->LoadOrderGroup, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Description) -{ - PhpUpdateServiceNodeDescription(node1); - PhpUpdateServiceNodeDescription(node2); - sortResult = PhCompareStringWithNull(node1->Description, node2->Description, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(KeyModifiedTime) -{ - PhpUpdateServiceNodeKey(node1); - PhpUpdateServiceNodeKey(node2); - sortResult = int64cmp(node1->KeyLastWriteTime.QuadPart, node2->KeyLastWriteTime.QuadPart); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpServiceTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_SERVICE_NODE node; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &ServiceTreeListCm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Name), - SORT_FUNCTION(DisplayName), - SORT_FUNCTION(Type), - SORT_FUNCTION(Status), - SORT_FUNCTION(StartType), - SORT_FUNCTION(Pid), - SORT_FUNCTION(BinaryPath), - SORT_FUNCTION(ErrorControl), - SORT_FUNCTION(Group), - SORT_FUNCTION(Description), - SORT_FUNCTION(KeyModifiedTime) - }; - int (__cdecl *sortFunction)(const void *, const void *); - - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)ServiceNodeList->Items, - ServiceNodeList->Count, - ServiceTreeListSortColumn, - ServiceTreeListSortOrder, - &ServiceTreeListCm - )) - { - if (ServiceTreeListSortColumn < PHSVTLC_MAXIMUM) - sortFunction = sortFunctions[ServiceTreeListSortColumn]; - else - sortFunction = NULL; - - if (sortFunction) - { - qsort(ServiceNodeList->Items, ServiceNodeList->Count, sizeof(PVOID), sortFunction); - } - } - - getChildren->Children = (PPH_TREENEW_NODE *)ServiceNodeList->Items; - getChildren->NumberOfChildren = ServiceNodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_SERVICE_ITEM serviceItem; - - node = (PPH_SERVICE_NODE)getCellText->Node; - serviceItem = node->ServiceItem; - - switch (getCellText->Id) - { - case PHSVTLC_NAME: - getCellText->Text = serviceItem->Name->sr; - break; - case PHSVTLC_DISPLAYNAME: - getCellText->Text = serviceItem->DisplayName->sr; - break; - case PHSVTLC_TYPE: - PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceTypeString(serviceItem->Type)); - break; - case PHSVTLC_STATUS: - PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceStateString(serviceItem->State)); - break; - case PHSVTLC_STARTTYPE: - { - PH_FORMAT format[2]; - PWSTR additional = NULL; - SIZE_T returnLength; - - PhInitFormatS(&format[0], PhGetServiceStartTypeString(serviceItem->StartType)); - - if (serviceItem->DelayedStart && serviceItem->HasTriggers) - additional = L" (delayed, trigger)"; - else if (serviceItem->DelayedStart) - additional = L" (delayed)"; - else if (serviceItem->HasTriggers) - additional = L" (trigger)"; - - if (additional) - PhInitFormatS(&format[1], additional); - - if (PhFormatToBuffer(format, 1 + (additional ? 1 : 0), node->StartTypeText, - sizeof(node->StartTypeText), &returnLength)) - { - getCellText->Text.Buffer = node->StartTypeText; - getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator - } - } - break; - case PHSVTLC_PID: - PhInitializeStringRefLongHint(&getCellText->Text, serviceItem->ProcessIdString); - break; - case PHSVTLC_BINARYPATH: - PhpUpdateServiceNodeConfig(node); - getCellText->Text = PhGetStringRef(node->BinaryPath); - break; - case PHSVTLC_ERRORCONTROL: - PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceErrorControlString(serviceItem->ErrorControl)); - break; - case PHSVTLC_GROUP: - PhpUpdateServiceNodeConfig(node); - getCellText->Text = PhGetStringRef(node->LoadOrderGroup); - break; - case PHSVTLC_DESCRIPTION: - PhpUpdateServiceNodeDescription(node); - getCellText->Text = PhGetStringRef(node->Description); - break; - case PHSVTLC_KEYMODIFIEDTIME: - PhpUpdateServiceNodeKey(node); - - if (node->KeyLastWriteTime.QuadPart != 0) - { - SYSTEMTIME systemTime; - - PhLargeIntegerToLocalSystemTime(&systemTime, &node->KeyLastWriteTime); - PhMoveReference(&node->KeyModifiedTimeText, PhFormatDateTime(&systemTime)); - getCellText->Text = node->KeyModifiedTimeText->sr; - } - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeIcon: - { - PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; - - node = (PPH_SERVICE_NODE)getNodeIcon->Node; - - if (!ServiceIconsLoaded) - { - ServiceApplicationIcon = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PHAPPLICATION)); - ServiceApplicationGoIcon = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_PHAPPLICATIONGO)); - ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_COG)); - ServiceCogGoIcon = PH_LOAD_SHARED_ICON_SMALL(MAKEINTRESOURCE(IDI_COGGO)); - - ServiceIconsLoaded = TRUE; - } - - if (node->ServiceItem->Type == SERVICE_KERNEL_DRIVER || node->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) - { - if (node->ServiceItem->State == SERVICE_RUNNING) - getNodeIcon->Icon = ServiceCogGoIcon; - else - getNodeIcon->Icon = ServiceCogIcon; - } - else - { - if (node->ServiceItem->State == SERVICE_RUNNING) - getNodeIcon->Icon = ServiceApplicationGoIcon; - else - getNodeIcon->Icon = ServiceApplicationIcon; - } - - getNodeIcon->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - - node = (PPH_SERVICE_NODE)getCellTooltip->Node; - - if (getCellTooltip->Column->Id != 0) - return FALSE; - - if (!node->TooltipText) - node->TooltipText = PhGetServiceTooltipText(node->ServiceItem); - - if (!PhIsNullOrEmptyString(node->TooltipText)) - { - getCellTooltip->Text = node->TooltipText->sr; - getCellTooltip->Unfolding = FALSE; - getCellTooltip->MaximumWidth = 550; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &ServiceTreeListSortColumn, &ServiceTreeListSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(ServiceTreeListHandle, 0, -1); - break; - case VK_DELETE: - SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_DELETE, 0); - break; - case VK_RETURN: - if (GetKeyState(VK_CONTROL) >= 0) - SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_PROPERTIES, 0); - else - SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_OPENFILELOCATION, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = AscendingSortOrder; - PhInitializeTreeNewColumnMenu(&data); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_PROPERTIES, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - PhShowServiceContextMenu(contextMenu); - } - return TRUE; - } - - return FALSE; -} - -PPH_SERVICE_ITEM PhGetSelectedServiceItem( - VOID - ) -{ - PPH_SERVICE_ITEM serviceItem = NULL; - ULONG i; - - for (i = 0; i < ServiceNodeList->Count; i++) - { - PPH_SERVICE_NODE node = ServiceNodeList->Items[i]; - - if (node->Node.Selected) - { - serviceItem = node->ServiceItem; - break; - } - } - - return serviceItem; -} - -VOID PhGetSelectedServiceItems( - _Out_ PPH_SERVICE_ITEM **Services, - _Out_ PULONG NumberOfServices - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - for (i = 0; i < ServiceNodeList->Count; i++) - { - PPH_SERVICE_NODE node = ServiceNodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node->ServiceItem); - } - - *NumberOfServices = (ULONG)array.Count; - *Services = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllServiceNodes( - VOID - ) -{ - TreeNew_DeselectRange(ServiceTreeListHandle, 0, -1); -} - -VOID PhSelectAndEnsureVisibleServiceNode( - _In_ PPH_SERVICE_NODE ServiceNode - ) -{ - PhDeselectAllServiceNodes(); - - if (!ServiceNode->Node.Visible) - return; - - TreeNew_SetFocusNode(ServiceTreeListHandle, &ServiceNode->Node); - TreeNew_SetMarkNode(ServiceTreeListHandle, &ServiceNode->Node); - TreeNew_SelectRange(ServiceTreeListHandle, ServiceNode->Node.Index, ServiceNode->Node.Index); - TreeNew_EnsureVisible(ServiceTreeListHandle, &ServiceNode->Node); -} - -VOID PhCopyServiceList( - VOID - ) -{ - PPH_STRING text; - - text = PhGetTreeNewText(ServiceTreeListHandle, 0); - PhSetClipboardString(ServiceTreeListHandle, &text->sr); - PhDereferenceObject(text); -} - -VOID PhWriteServiceList( - _Inout_ PPH_FILE_STREAM FileStream, - _In_ ULONG Mode - ) -{ - PPH_LIST lines; - ULONG i; - - lines = PhGetGenericTreeNewLines(ServiceTreeListHandle, Mode); - - for (i = 0; i < lines->Count; i++) - { - PPH_STRING line; - - line = lines->Items[i]; - PhWriteStringAsUtf8FileStream(FileStream, &line->sr); - PhDereferenceObject(line); - PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); - } - - PhDereferenceObject(lines); -} +/* + * Process Hacker - + * service list + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +BOOLEAN PhpServiceNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG PhpServiceNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpRemoveServiceNode( + _In_ PPH_SERVICE_NODE ServiceNode + ); + +LONG PhpServiceTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpServiceTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +static HWND ServiceTreeListHandle; +static ULONG ServiceTreeListSortColumn; +static PH_SORT_ORDER ServiceTreeListSortOrder; +static PH_CM_MANAGER ServiceTreeListCm; + +static PPH_HASHTABLE ServiceNodeHashtable; // hashtable of all nodes +static PPH_LIST ServiceNodeList; // list of all nodes + +static PH_TN_FILTER_SUPPORT FilterSupport; + +static BOOLEAN ServiceIconsLoaded = FALSE; +static HICON ServiceApplicationIcon; +static HICON ServiceCogIcon; + +BOOLEAN PhServiceTreeListStateHighlighting = TRUE; +static PPH_POINTER_LIST ServiceNodeStateList = NULL; // list of nodes which need to be processed + +VOID PhServiceTreeListInitialization( + VOID + ) +{ + ServiceNodeHashtable = PhCreateHashtable( + sizeof(PPH_SERVICE_NODE), + PhpServiceNodeHashtableEqualFunction, + PhpServiceNodeHashtableHashFunction, + 100 + ); + ServiceNodeList = PhCreateList(100); +} + +BOOLEAN PhpServiceNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_SERVICE_NODE serviceNode1 = *(PPH_SERVICE_NODE *)Entry1; + PPH_SERVICE_NODE serviceNode2 = *(PPH_SERVICE_NODE *)Entry2; + + return serviceNode1->ServiceItem == serviceNode2->ServiceItem; +} + +ULONG PhpServiceNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PPH_SERVICE_NODE *)Entry)->ServiceItem); +} + +VOID PhInitializeServiceTreeList( + _In_ HWND hwnd + ) +{ + ServiceTreeListHandle = hwnd; + PhSetControlTheme(ServiceTreeListHandle, L"explorer"); + SendMessage(TreeNew_GetTooltips(ServiceTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); + + TreeNew_SetCallback(hwnd, PhpServiceTreeNewCallback, NULL); + + TreeNew_SetRedraw(hwnd, FALSE); + + // Default columns + PhAddTreeNewColumn(hwnd, PHSVTLC_NAME, TRUE, L"Name", 140, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_DISPLAYNAME, TRUE, L"Display name", 220, PH_ALIGN_LEFT, 1, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 2, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_STATUS, TRUE, L"Status", 70, PH_ALIGN_LEFT, 3, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_STARTTYPE, TRUE, L"Start type", 130, PH_ALIGN_LEFT, 4, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_PID, TRUE, L"PID", 50, PH_ALIGN_RIGHT, 5, DT_RIGHT); + + PhAddTreeNewColumn(hwnd, PHSVTLC_BINARYPATH, FALSE, L"Binary path", 180, PH_ALIGN_LEFT, ULONG_MAX, DT_PATH_ELLIPSIS); + PhAddTreeNewColumn(hwnd, PHSVTLC_ERRORCONTROL, FALSE, L"Error control", 70, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_GROUP, FALSE, L"Group", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_DESCRIPTION, FALSE, L"Description", 200, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumnEx(hwnd, PHSVTLC_KEYMODIFIEDTIME, FALSE, L"Key modified time", 140, PH_ALIGN_LEFT, ULONG_MAX, 0, TRUE); + PhAddTreeNewColumn(hwnd, PHSVTLC_VERIFICATIONSTATUS, FALSE, L"Verification status", 70, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_VERIFIEDSIGNER, FALSE, L"Verified signer", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(hwnd, PHSVTLC_FILENAME, FALSE, L"File name", 100, PH_ALIGN_LEFT, ULONG_MAX, DT_PATH_ELLIPSIS); + + TreeNew_SetRedraw(hwnd, TRUE); + + TreeNew_SetSort(hwnd, 0, AscendingSortOrder); + + PhCmInitializeManager(&ServiceTreeListCm, hwnd, PHSVTLC_MAXIMUM, PhpServiceTreeNewPostSortFunction); + + if (PhPluginsEnabled) + { + PH_PLUGIN_TREENEW_INFORMATION treeNewInfo; + + treeNewInfo.TreeNewHandle = hwnd; + treeNewInfo.CmData = &ServiceTreeListCm; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), &treeNewInfo); + } + + PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ServiceNodeList); +} + +VOID PhLoadSettingsServiceTreeList( + VOID + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhGetStringSetting(L"ServiceTreeListColumns"); + sortSettings = PhGetStringSetting(L"ServiceTreeListSort"); + PhCmLoadSettingsEx(ServiceTreeListHandle, &ServiceTreeListCm, 0, &settings->sr, &sortSettings->sr); + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSaveSettingsServiceTreeList( + VOID + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(ServiceTreeListHandle, &ServiceTreeListCm, 0, &sortSettings); + PhSetStringSetting2(L"ServiceTreeListColumns", &settings->sr); + PhSetStringSetting2(L"ServiceTreeListSort", &sortSettings->sr); + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +struct _PH_TN_FILTER_SUPPORT *PhGetFilterSupportServiceTreeList( + VOID + ) +{ + return &FilterSupport; +} + +PPH_SERVICE_NODE PhAddServiceNode( + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ ULONG RunId + ) +{ + PPH_SERVICE_NODE serviceNode; + + serviceNode = PhAllocate(PhEmGetObjectSize(EmServiceNodeType, sizeof(PH_SERVICE_NODE))); + memset(serviceNode, 0, sizeof(PH_SERVICE_NODE)); + PhInitializeTreeNewNode(&serviceNode->Node); + + if (PhServiceTreeListStateHighlighting && RunId != 1) + { + PhChangeShStateTn( + &serviceNode->Node, + &serviceNode->ShState, + &ServiceNodeStateList, + NewItemState, + PhCsColorNew, + NULL + ); + } + + serviceNode->ServiceItem = ServiceItem; + PhReferenceObject(ServiceItem); + + memset(serviceNode->TextCache, 0, sizeof(PH_STRINGREF) * PHSVTLC_MAXIMUM); + serviceNode->Node.TextCache = serviceNode->TextCache; + serviceNode->Node.TextCacheSize = PHSVTLC_MAXIMUM; + + PhAddEntryHashtable(ServiceNodeHashtable, &serviceNode); + PhAddItemList(ServiceNodeList, serviceNode); + + if (FilterSupport.FilterList) + serviceNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &serviceNode->Node); + + PhEmCallObjectOperation(EmServiceNodeType, serviceNode, EmObjectCreate); + + TreeNew_NodesStructured(ServiceTreeListHandle); + + return serviceNode; +} + +PPH_SERVICE_NODE PhFindServiceNode( + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PH_SERVICE_NODE lookupServiceNode; + PPH_SERVICE_NODE lookupServiceNodePtr = &lookupServiceNode; + PPH_SERVICE_NODE *serviceNode; + + lookupServiceNode.ServiceItem = ServiceItem; + + serviceNode = (PPH_SERVICE_NODE *)PhFindEntryHashtable( + ServiceNodeHashtable, + &lookupServiceNodePtr + ); + + if (serviceNode) + return *serviceNode; + else + return NULL; +} + +VOID PhRemoveServiceNode( + _In_ PPH_SERVICE_NODE ServiceNode + ) +{ + // Remove from the hashtable here to avoid problems in case the key is re-used. + PhRemoveEntryHashtable(ServiceNodeHashtable, &ServiceNode); + + if (PhServiceTreeListStateHighlighting) + { + PhChangeShStateTn( + &ServiceNode->Node, + &ServiceNode->ShState, + &ServiceNodeStateList, + RemovingItemState, + PhCsColorRemoved, + ServiceTreeListHandle + ); + } + else + { + PhpRemoveServiceNode(ServiceNode); + } +} + +VOID PhpRemoveServiceNode( + _In_ PPH_SERVICE_NODE ServiceNode + ) +{ + ULONG index; + + PhEmCallObjectOperation(EmServiceNodeType, ServiceNode, EmObjectDelete); + + // Remove from list and cleanup. + + if ((index = PhFindItemList(ServiceNodeList, ServiceNode)) != ULONG_MAX) + PhRemoveItemList(ServiceNodeList, index); + + PhClearReference(&ServiceNode->BinaryPath); + PhClearReference(&ServiceNode->LoadOrderGroup); + PhClearReference(&ServiceNode->Description); + PhClearReference(&ServiceNode->TooltipText); + PhClearReference(&ServiceNode->KeyModifiedTimeText); + + PhDereferenceObject(ServiceNode->ServiceItem); + + PhFree(ServiceNode); + + TreeNew_NodesStructured(ServiceTreeListHandle); +} + +VOID PhUpdateServiceNode( + _In_ PPH_SERVICE_NODE ServiceNode + ) +{ + memset(ServiceNode->TextCache, 0, sizeof(PH_STRINGREF) * PHSVTLC_MAXIMUM); + PhClearReference(&ServiceNode->TooltipText); + + ServiceNode->ValidMask = 0; + PhInvalidateTreeNewNode(&ServiceNode->Node, TN_CACHE_ICON); + TreeNew_NodesStructured(ServiceTreeListHandle); +} + +VOID PhTickServiceNodes( + VOID + ) +{ + if (ServiceTreeListSortOrder != NoSortOrder && ServiceTreeListSortColumn >= PHSVTLC_MAXIMUM) + { + // Sorting is on, but it's not one of our columns. Force a rebuild. (If it was one of our + // columns, the restructure would have been handled in PhUpdateServiceNode.) + TreeNew_NodesStructured(ServiceTreeListHandle); + } + + PH_TICK_SH_STATE_TN(PH_SERVICE_NODE, ShState, ServiceNodeStateList, PhpRemoveServiceNode, PhCsHighlightingDuration, ServiceTreeListHandle, TRUE, NULL); +} + +static VOID PhpUpdateServiceNodeConfig( + _Inout_ PPH_SERVICE_NODE ServiceNode + ) +{ + if (!(ServiceNode->ValidMask & PHSN_CONFIG)) + { + SC_HANDLE serviceHandle; + LPQUERY_SERVICE_CONFIG serviceConfig; + + if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + if (serviceConfig = PhGetServiceConfig(serviceHandle)) + { + if (serviceConfig->lpBinaryPathName) + PhMoveReference(&ServiceNode->BinaryPath, PhCreateString(serviceConfig->lpBinaryPathName)); + if (serviceConfig->lpLoadOrderGroup) + PhMoveReference(&ServiceNode->LoadOrderGroup, PhCreateString(serviceConfig->lpLoadOrderGroup)); + + PhFree(serviceConfig); + } + + CloseServiceHandle(serviceHandle); + } + + ServiceNode->ValidMask |= PHSN_CONFIG; + } +} + +static VOID PhpUpdateServiceNodeDescription( + _Inout_ PPH_SERVICE_NODE ServiceNode + ) +{ + if (!(ServiceNode->ValidMask & PHSN_DESCRIPTION)) + { + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + HANDLE keyHandle; + PPH_STRING keyName; + + keyName = PhConcatStringRef2(&servicesKeyName, &ServiceNode->ServiceItem->Name->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_QUERY_VALUE, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) + { + PPH_STRING descriptionString; + PPH_STRING serviceDescriptionString; + + if (descriptionString = PhQueryRegistryString(keyHandle, L"Description")) + { + if (serviceDescriptionString = PhLoadIndirectString(descriptionString->Buffer)) + PhMoveReference(&ServiceNode->Description, serviceDescriptionString); + else + PhSwapReference(&ServiceNode->Description, descriptionString); + + PhDereferenceObject(descriptionString); + } + + NtClose(keyHandle); + } + + PhDereferenceObject(keyName); + + ServiceNode->ValidMask |= PHSN_DESCRIPTION; + } + + // NOTE: Querying the service description via RPC is extremely slow. (dmex) + //SC_HANDLE serviceHandle; + // + //if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + //{ + // PhMoveReference(&ServiceNode->Description, PhGetServiceDescription(serviceHandle)); + // CloseServiceHandle(serviceHandle); + //} +} + +static VOID PhpUpdateServiceNodeKey( + _Inout_ PPH_SERVICE_NODE ServiceNode + ) +{ + if (!(ServiceNode->ValidMask & PHSN_KEY)) + { + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + + HANDLE keyHandle; + PPH_STRING keyName; + + keyName = PhConcatStringRef2(&servicesKeyName, &ServiceNode->ServiceItem->Name->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) + { + PKEY_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhQueryKey(keyHandle, KeyBasicInformation, &basicInfo))) + { + ServiceNode->KeyLastWriteTime = basicInfo->LastWriteTime; + PhFree(basicInfo); + } + + NtClose(keyHandle); + } + + PhDereferenceObject(keyName); + + ServiceNode->ValidMask |= PHSN_KEY; + } +} + +#define SORT_FUNCTION(Column) PhpServiceTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpServiceTreeNewCompare##Column( \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_SERVICE_NODE node1 = *(PPH_SERVICE_NODE *)_elem1; \ + PPH_SERVICE_NODE node2 = *(PPH_SERVICE_NODE *)_elem2; \ + PPH_SERVICE_ITEM serviceItem1 = node1->ServiceItem; \ + PPH_SERVICE_ITEM serviceItem2 = node2->ServiceItem; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); \ + \ + return PhModifySort(sortResult, ServiceTreeListSortOrder); \ +} + +LONG PhpServiceTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + PPH_SERVICE_NODE node1 = (PPH_SERVICE_NODE)Node1; + PPH_SERVICE_NODE node2 = (PPH_SERVICE_NODE)Node2; + PPH_SERVICE_ITEM serviceItem1 = node1->ServiceItem; + PPH_SERVICE_ITEM serviceItem2 = node2->ServiceItem; + + if (Result == 0) + Result = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(DisplayName) +{ + sortResult = PhCompareStringWithNull(serviceItem1->DisplayName, serviceItem2->DisplayName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Type) +{ + sortResult = uintcmp(serviceItem1->Type, serviceItem2->Type); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Status) +{ + sortResult = uintcmp(serviceItem1->State, serviceItem2->State); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StartType) +{ + sortResult = uintcmp(serviceItem1->StartType, serviceItem2->StartType); + + if (sortResult == 0) + sortResult = ucharcmp(serviceItem1->DelayedStart, serviceItem2->DelayedStart); + if (sortResult == 0) + sortResult = ucharcmp(serviceItem1->HasTriggers, serviceItem2->HasTriggers); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Pid) +{ + sortResult = uintptrcmp((ULONG_PTR)serviceItem1->ProcessId, (ULONG_PTR)serviceItem2->ProcessId); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(BinaryPath) +{ + PhpUpdateServiceNodeConfig(node1); + PhpUpdateServiceNodeConfig(node2); + sortResult = PhCompareStringWithNullSortOrder(node1->BinaryPath, node2->BinaryPath, ServiceTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ErrorControl) +{ + sortResult = uintcmp(serviceItem1->ErrorControl, serviceItem2->ErrorControl); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Group) +{ + PhpUpdateServiceNodeConfig(node1); + PhpUpdateServiceNodeConfig(node2); + sortResult = PhCompareStringWithNullSortOrder(node1->LoadOrderGroup, node2->LoadOrderGroup, ServiceTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Description) +{ + PhpUpdateServiceNodeDescription(node1); + PhpUpdateServiceNodeDescription(node2); + sortResult = PhCompareStringWithNullSortOrder(node1->Description, node2->Description, ServiceTreeListSortOrder, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(KeyModifiedTime) +{ + PhpUpdateServiceNodeKey(node1); + PhpUpdateServiceNodeKey(node2); + sortResult = int64cmp(node1->KeyLastWriteTime.QuadPart, node2->KeyLastWriteTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerificationStatus) +{ + sortResult = uintcmp(serviceItem1->VerifyResult, serviceItem2->VerifyResult); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VerifiedSigner) +{ + sortResult = PhCompareStringWithNullSortOrder( + serviceItem1->VerifySignerName, + serviceItem2->VerifySignerName, + ServiceTreeListSortOrder, + TRUE + ); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FileName) +{ + sortResult = PhCompareStringWithNullSortOrder( + serviceItem1->FileName, + serviceItem2->FileName, + ServiceTreeListSortOrder, + TRUE + ); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpServiceTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_SERVICE_NODE node; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &ServiceTreeListCm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Name), + SORT_FUNCTION(DisplayName), + SORT_FUNCTION(Type), + SORT_FUNCTION(Status), + SORT_FUNCTION(StartType), + SORT_FUNCTION(Pid), + SORT_FUNCTION(BinaryPath), + SORT_FUNCTION(ErrorControl), + SORT_FUNCTION(Group), + SORT_FUNCTION(Description), + SORT_FUNCTION(KeyModifiedTime), + SORT_FUNCTION(VerificationStatus), + SORT_FUNCTION(VerifiedSigner), + SORT_FUNCTION(FileName) + }; + int (__cdecl *sortFunction)(const void *, const void *); + + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)ServiceNodeList->Items, + ServiceNodeList->Count, + ServiceTreeListSortColumn, + ServiceTreeListSortOrder, + &ServiceTreeListCm + )) + { + if (ServiceTreeListSortColumn < PHSVTLC_MAXIMUM) + sortFunction = sortFunctions[ServiceTreeListSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort(ServiceNodeList->Items, ServiceNodeList->Count, sizeof(PVOID), sortFunction); + } + } + + getChildren->Children = (PPH_TREENEW_NODE *)ServiceNodeList->Items; + getChildren->NumberOfChildren = ServiceNodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_SERVICE_ITEM serviceItem; + + node = (PPH_SERVICE_NODE)getCellText->Node; + serviceItem = node->ServiceItem; + + switch (getCellText->Id) + { + case PHSVTLC_NAME: + getCellText->Text = PhGetStringRef(serviceItem->Name); + break; + case PHSVTLC_DISPLAYNAME: + getCellText->Text = PhGetStringRef(serviceItem->DisplayName); + break; + case PHSVTLC_TYPE: + PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceTypeString(serviceItem->Type)); + break; + case PHSVTLC_STATUS: + PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceStateString(serviceItem->State)); + break; + case PHSVTLC_STARTTYPE: + { + PH_FORMAT format[2]; + PWSTR additional = NULL; + SIZE_T returnLength; + + PhInitFormatS(&format[0], PhGetServiceStartTypeString(serviceItem->StartType)); + + if (serviceItem->DelayedStart && serviceItem->HasTriggers) + additional = L" (delayed, trigger)"; + else if (serviceItem->DelayedStart) + additional = L" (delayed)"; + else if (serviceItem->HasTriggers) + additional = L" (trigger)"; + + if (additional) + PhInitFormatS(&format[1], additional); + + if (PhFormatToBuffer(format, 1 + (additional ? 1 : 0), node->StartTypeText, + sizeof(node->StartTypeText), &returnLength)) + { + getCellText->Text.Buffer = node->StartTypeText; + getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator + } + } + break; + case PHSVTLC_PID: + PhInitializeStringRefLongHint(&getCellText->Text, serviceItem->ProcessIdString); + break; + case PHSVTLC_BINARYPATH: + PhpUpdateServiceNodeConfig(node); + getCellText->Text = PhGetStringRef(node->BinaryPath); + break; + case PHSVTLC_ERRORCONTROL: + PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceErrorControlString(serviceItem->ErrorControl)); + break; + case PHSVTLC_GROUP: + PhpUpdateServiceNodeConfig(node); + getCellText->Text = PhGetStringRef(node->LoadOrderGroup); + break; + case PHSVTLC_DESCRIPTION: + PhpUpdateServiceNodeDescription(node); + getCellText->Text = PhGetStringRef(node->Description); + break; + case PHSVTLC_KEYMODIFIEDTIME: + PhpUpdateServiceNodeKey(node); + + if (node->KeyLastWriteTime.QuadPart != 0) + { + SYSTEMTIME systemTime; + + PhLargeIntegerToLocalSystemTime(&systemTime, &node->KeyLastWriteTime); + PhMoveReference(&node->KeyModifiedTimeText, PhFormatDateTime(&systemTime)); + getCellText->Text = node->KeyModifiedTimeText->sr; + } + break; + case PHSVTLC_VERIFICATIONSTATUS: + PhInitializeStringRef(&getCellText->Text, + serviceItem->VerifyResult == VrTrusted ? L"Trusted" : L"Not trusted"); + break; + case PHSVTLC_VERIFIEDSIGNER: + getCellText->Text = PhGetStringRef(serviceItem->VerifySignerName); + break; + case PHSVTLC_FILENAME: + getCellText->Text = PhGetStringRef(serviceItem->FileName); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeIcon: + { + PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; + + node = (PPH_SERVICE_NODE)getNodeIcon->Node; + + if (!ServiceIconsLoaded) + { + HICON icon; + + PhGetStockApplicationIcon(&icon, NULL); + + ServiceApplicationIcon = icon; + ServiceCogIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); + + ServiceIconsLoaded = TRUE; + } + + if (node->ServiceItem->SmallIcon) + getNodeIcon->Icon = node->ServiceItem->SmallIcon; + else + { + if (node->ServiceItem->Type == SERVICE_KERNEL_DRIVER || node->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) + getNodeIcon->Icon = ServiceCogIcon; + else + getNodeIcon->Icon = ServiceApplicationIcon; + } + + getNodeIcon->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PPH_SERVICE_ITEM serviceItem; + + node = (PPH_SERVICE_NODE)getNodeColor->Node; + serviceItem = node->ServiceItem; + + if (!serviceItem) + ; // Dummy + else if (PhEnableServiceQueryStage2 && PhCsUseColorUnknown && serviceItem->VerifyResult != VrTrusted) + { + getNodeColor->BackColor = PhCsColorUnknown; + getNodeColor->Flags |= TN_AUTO_FORECOLOR; + } + else if (PhCsUseColorServiceDisabled && serviceItem->State == SERVICE_STOPPED && serviceItem->StartType == SERVICE_DISABLED) + { + getNodeColor->BackColor = PhCsColorServiceDisabled; + getNodeColor->Flags |= TN_AUTO_FORECOLOR; + } + else if (PhCsUseColorServiceStop && serviceItem->State == SERVICE_STOPPED) + { + getNodeColor->ForeColor = PhCsColorServiceStop; + } + else + { + getNodeColor->Flags |= TN_AUTO_FORECOLOR; + } + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + + node = (PPH_SERVICE_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0) + return FALSE; + + if (!node->TooltipText) + node->TooltipText = PhGetServiceTooltipText(node->ServiceItem); + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->MaximumWidth = 550; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &ServiceTreeListSortColumn, &ServiceTreeListSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(ServiceTreeListHandle, 0, -1); + break; + case VK_DELETE: + SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_DELETE, 0); + break; + case VK_RETURN: + if (GetKeyState(VK_CONTROL) >= 0) + SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_PROPERTIES, 0); + else + SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_OPENFILELOCATION, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_PROPERTIES, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + PhShowServiceContextMenu(contextMenu); + } + return TRUE; + } + + return FALSE; +} + +PPH_SERVICE_ITEM PhGetSelectedServiceItem( + VOID + ) +{ + PPH_SERVICE_ITEM serviceItem = NULL; + ULONG i; + + for (i = 0; i < ServiceNodeList->Count; i++) + { + PPH_SERVICE_NODE node = ServiceNodeList->Items[i]; + + if (node->Node.Selected) + { + serviceItem = node->ServiceItem; + break; + } + } + + return serviceItem; +} + +VOID PhGetSelectedServiceItems( + _Out_ PPH_SERVICE_ITEM **Services, + _Out_ PULONG NumberOfServices + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + for (i = 0; i < ServiceNodeList->Count; i++) + { + PPH_SERVICE_NODE node = ServiceNodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node->ServiceItem); + } + + *NumberOfServices = (ULONG)array.Count; + *Services = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllServiceNodes( + VOID + ) +{ + TreeNew_DeselectRange(ServiceTreeListHandle, 0, -1); +} + +VOID PhSelectAndEnsureVisibleServiceNode( + _In_ PPH_SERVICE_NODE ServiceNode + ) +{ + PhDeselectAllServiceNodes(); + + if (!ServiceNode->Node.Visible) + return; + + TreeNew_SetFocusNode(ServiceTreeListHandle, &ServiceNode->Node); + TreeNew_SetMarkNode(ServiceTreeListHandle, &ServiceNode->Node); + TreeNew_SelectRange(ServiceTreeListHandle, ServiceNode->Node.Index, ServiceNode->Node.Index); + TreeNew_EnsureVisible(ServiceTreeListHandle, &ServiceNode->Node); +} + +VOID PhCopyServiceList( + VOID + ) +{ + PPH_STRING text; + + text = PhGetTreeNewText(ServiceTreeListHandle, 0); + PhSetClipboardString(ServiceTreeListHandle, &text->sr); + PhDereferenceObject(text); +} + +VOID PhWriteServiceList( + _Inout_ PPH_FILE_STREAM FileStream, + _In_ ULONG Mode + ) +{ + PPH_LIST lines; + ULONG i; + + lines = PhGetGenericTreeNewLines(ServiceTreeListHandle, Mode); + + for (i = 0; i < lines->Count; i++) + { + PPH_STRING line; + + line = lines->Items[i]; + PhWriteStringAsUtf8FileStream(FileStream, &line->sr); + PhDereferenceObject(line); + PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); + } + + PhDereferenceObject(lines); +} diff --git a/ProcessHacker/srvprp.c b/ProcessHacker/srvprp.c index fe193f2dd90a..287f85a3632f 100644 --- a/ProcessHacker/srvprp.c +++ b/ProcessHacker/srvprp.c @@ -1,574 +1,628 @@ -/* - * Process Hacker - - * service properties - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include - -#include -#include -#include -#include - -typedef struct _SERVICE_PROPERTIES_CONTEXT -{ - PPH_SERVICE_ITEM ServiceItem; - BOOLEAN Ready; - BOOLEAN Dirty; - - BOOLEAN OldDelayedStart; -} SERVICE_PROPERTIES_CONTEXT, *PSERVICE_PROPERTIES_CONTEXT; - -INT_PTR CALLBACK PhpServiceGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static NTSTATUS PhpOpenService( - _Out_ PHANDLE Handle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ PVOID Context - ) -{ - SC_HANDLE serviceHandle; - - if (!(serviceHandle = PhOpenService( - ((PPH_SERVICE_ITEM)Context)->Name->Buffer, - DesiredAccess - ))) - return PhGetLastWin32ErrorAsNtStatus(); - - *Handle = serviceHandle; - - return STATUS_SUCCESS; -} - -static _Callback_ NTSTATUS PhpSetServiceSecurity( - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PPH_STD_OBJECT_SECURITY stdObjectSecurity; - - stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context; - - status = PhStdSetObjectSecurity(SecurityDescriptor, SecurityInformation, Context); - - if ((status == STATUS_ACCESS_DENIED || status == NTSTATUS_FROM_WIN32(ERROR_ACCESS_DENIED)) && !PhGetOwnTokenAttributes().Elevated) - { - // Elevate using phsvc. - if (PhUiConnectToPhSvc(NULL, FALSE)) - { - status = PhSvcCallSetServiceSecurity( - ((PPH_SERVICE_ITEM)stdObjectSecurity->Context)->Name->Buffer, - SecurityInformation, - SecurityDescriptor - ); - PhUiDisconnectFromPhSvc(); - } - } - - return status; -} - -VOID PhShowServiceProperties( - _In_ HWND ParentWindowHandle, - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[32]; - SERVICE_PROPERTIES_CONTEXT context; - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = ServiceItem->Name->Buffer; - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - // General - - memset(&context, 0, sizeof(SERVICE_PROPERTIES_CONTEXT)); - context.ServiceItem = ServiceItem; - context.Ready = FALSE; - context.Dirty = FALSE; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVGENERAL); - propSheetPage.pfnDlgProc = PhpServiceGeneralDlgProc; - propSheetPage.lParam = (LPARAM)&context; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - // Security - - stdObjectSecurity.OpenObject = PhpOpenService; - stdObjectSecurity.ObjectType = L"Service"; - stdObjectSecurity.Context = ServiceItem; - - if (PhGetAccessEntries(L"Service", &accessEntries, &numberOfAccessEntries)) - { - pages[propSheetHeader.nPages++] = PhCreateSecurityPage( - ServiceItem->Name->Buffer, - PhStdGetObjectSecurity, - PhpSetServiceSecurity, - &stdObjectSecurity, - accessEntries, - numberOfAccessEntries - ); - PhFree(accessEntries); - } - - if (PhPluginsEnabled) - { - PH_PLUGIN_OBJECT_PROPERTIES objectProperties; - - objectProperties.Parameter = ServiceItem; - objectProperties.NumberOfPages = propSheetHeader.nPages; - objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); - objectProperties.Pages = pages; - - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), &objectProperties); - - propSheetHeader.nPages = objectProperties.NumberOfPages; - } - - PhModalPropertySheet(&propSheetHeader); -} - -static VOID PhpRefreshControls( - _In_ HWND hwndDlg - ) -{ - if ( - WindowsVersion >= WINDOWS_VISTA && - PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_STARTTYPE), L"Auto start", FALSE) - ) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), TRUE); - } - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), FALSE); - } -} - -INT_PTR CALLBACK PhpServiceGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PSERVICE_PROPERTIES_CONTEXT context = (PSERVICE_PROPERTIES_CONTEXT)propSheetPage->lParam; - PPH_SERVICE_ITEM serviceItem = context->ServiceItem; - SC_HANDLE serviceHandle; - ULONG startType; - ULONG errorControl; - PPH_STRING serviceDll; - - // HACK - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); - - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_TYPE), PhServiceTypeStrings, - sizeof(PhServiceTypeStrings) / sizeof(WCHAR *)); - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_STARTTYPE), PhServiceStartTypeStrings, - sizeof(PhServiceStartTypeStrings) / sizeof(WCHAR *)); - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), PhServiceErrorControlStrings, - sizeof(PhServiceErrorControlStrings) / sizeof(WCHAR *)); - - SetDlgItemText(hwndDlg, IDC_DESCRIPTION, serviceItem->DisplayName->Buffer); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), - PhGetServiceTypeString(serviceItem->Type), FALSE); - - startType = serviceItem->StartType; - errorControl = serviceItem->ErrorControl; - serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG); - - if (serviceHandle) - { - LPQUERY_SERVICE_CONFIG config; - PPH_STRING description; - BOOLEAN delayedStart; - - if (config = PhGetServiceConfig(serviceHandle)) - { - SetDlgItemText(hwndDlg, IDC_GROUP, config->lpLoadOrderGroup); - SetDlgItemText(hwndDlg, IDC_BINARYPATH, config->lpBinaryPathName); - SetDlgItemText(hwndDlg, IDC_USERACCOUNT, config->lpServiceStartName); - - if (startType != config->dwStartType || errorControl != config->dwErrorControl) - { - startType = config->dwStartType; - errorControl = config->dwErrorControl; - PhMarkNeedsConfigUpdateServiceItem(serviceItem); - } - - PhFree(config); - } - - if (description = PhGetServiceDescription(serviceHandle)) - { - SetDlgItemText(hwndDlg, IDC_DESCRIPTION, description->Buffer); - PhDereferenceObject(description); - } - - if ( - WindowsVersion >= WINDOWS_VISTA && - PhGetServiceDelayedAutoStart(serviceHandle, &delayedStart) - ) - { - context->OldDelayedStart = delayedStart; - - if (delayedStart) - Button_SetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), BST_CHECKED); - } - - CloseServiceHandle(serviceHandle); - } - - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_STARTTYPE), - PhGetServiceStartTypeString(startType), FALSE); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), - PhGetServiceErrorControlString(errorControl), FALSE); - - SetDlgItemText(hwndDlg, IDC_PASSWORD, L"password"); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK), BST_UNCHECKED); - - if (NT_SUCCESS(PhGetServiceDllParameter(&serviceItem->Name->sr, &serviceDll))) - { - SetDlgItemText(hwndDlg, IDC_SERVICEDLL, serviceDll->Buffer); - PhDereferenceObject(serviceDll); - } - else - { - SetDlgItemText(hwndDlg, IDC_SERVICEDLL, L"N/A"); - } - - PhpRefreshControls(hwndDlg); - - context->Ready = TRUE; - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - PSERVICE_PROPERTIES_CONTEXT context = - (PSERVICE_PROPERTIES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - // Workaround for property sheet + multiline edit: http://support.microsoft.com/kb/130765 - - SendMessage(GetParent(hwndDlg), uMsg, wParam, lParam); - } - break; - case IDC_PASSWORD: - { - if (HIWORD(wParam) == EN_CHANGE) - { - Button_SetCheck(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK), BST_CHECKED); - } - } - break; - case IDC_DELAYEDSTART: - { - context->Dirty = TRUE; - } - break; - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Executable files (*.exe;*.sys)", L"*.exe;*.sys" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING commandLine; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - commandLine = PhaGetDlgItemText(hwndDlg, IDC_BINARYPATH); - - if (context->ServiceItem->Type & SERVICE_WIN32) - { - PH_STRINGREF dummyFileName; - PH_STRINGREF dummyArguments; - - if (!PhParseCommandLineFuzzy(&commandLine->sr, &dummyFileName, &dummyArguments, &fileName)) - fileName = NULL; - - if (!fileName) - PhSwapReference(&fileName, commandLine); - } - else - { - fileName = PhGetFileName(commandLine); - } - - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - PhDereferenceObject(fileName); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PhGetFileDialogFileName(fileDialog); - SetDlgItemText(hwndDlg, IDC_BINARYPATH, fileName->Buffer); - PhDereferenceObject(fileName); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - - switch (HIWORD(wParam)) - { - case EN_CHANGE: - case CBN_SELCHANGE: - { - PhpRefreshControls(hwndDlg); - - if (context->Ready) - context->Dirty = TRUE; - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_STARTTYPE)); - } - return TRUE; - case PSN_KILLACTIVE: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); - } - return TRUE; - case PSN_APPLY: - { - NTSTATUS status; - PSERVICE_PROPERTIES_CONTEXT context = - (PSERVICE_PROPERTIES_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - PPH_SERVICE_ITEM serviceItem = context->ServiceItem; - SC_HANDLE serviceHandle; - PPH_STRING newServiceTypeString; - PPH_STRING newServiceStartTypeString; - PPH_STRING newServiceErrorControlString; - ULONG newServiceType; - ULONG newServiceStartType; - ULONG newServiceErrorControl; - PPH_STRING newServiceGroup = NULL; - PPH_STRING newServiceBinaryPath = NULL; - PPH_STRING newServiceUserAccount = NULL; - PPH_STRING newServicePassword = NULL; - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - - if (!context->Dirty) - { - return TRUE; - } - - newServiceTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_TYPE))); - newServiceStartTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_STARTTYPE))); - newServiceErrorControlString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_ERRORCONTROL))); - newServiceType = PhGetServiceTypeInteger(newServiceTypeString->Buffer); - newServiceStartType = PhGetServiceStartTypeInteger(newServiceStartTypeString->Buffer); - newServiceErrorControl = PhGetServiceErrorControlInteger(newServiceErrorControlString->Buffer); - - if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_GROUP))) - newServiceGroup = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_GROUP))); - if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_BINARYPATH))) - newServiceBinaryPath = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_BINARYPATH))); - if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_USERACCOUNT))) - newServiceUserAccount = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_USERACCOUNT))); - - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK)) == BST_CHECKED) - newServicePassword = PhGetWindowText(GetDlgItem(hwndDlg, IDC_PASSWORD)); - - serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_CHANGE_CONFIG); - - if (serviceHandle) - { - if (ChangeServiceConfig( - serviceHandle, - newServiceType, - newServiceStartType, - newServiceErrorControl, - PhGetString(newServiceBinaryPath), - PhGetString(newServiceGroup), - NULL, - NULL, - PhGetString(newServiceUserAccount), - PhGetString(newServicePassword), - NULL - )) - { - if (WindowsVersion >= WINDOWS_VISTA) - { - BOOLEAN newDelayedStart; - - newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; - - if (newDelayedStart != context->OldDelayedStart) - { - PhSetServiceDelayedAutoStart(serviceHandle, newDelayedStart); - } - } - - PhMarkNeedsConfigUpdateServiceItem(serviceItem); - - CloseServiceHandle(serviceHandle); - } - else - { - CloseServiceHandle(serviceHandle); - goto ErrorCase; - } - } - else - { - if (GetLastError() == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) - { - // Elevate using phsvc. - if (PhUiConnectToPhSvc(hwndDlg, FALSE)) - { - if (NT_SUCCESS(status = PhSvcCallChangeServiceConfig( - serviceItem->Name->Buffer, - newServiceType, - newServiceStartType, - newServiceErrorControl, - PhGetString(newServiceBinaryPath), - PhGetString(newServiceGroup), - NULL, - NULL, - PhGetString(newServiceUserAccount), - PhGetString(newServicePassword), - NULL - ))) - { - if (WindowsVersion >= WINDOWS_VISTA) - { - BOOLEAN newDelayedStart; - - newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; - - if (newDelayedStart != context->OldDelayedStart) - { - SERVICE_DELAYED_AUTO_START_INFO info; - - info.fDelayedAutostart = newDelayedStart; - PhSvcCallChangeServiceConfig2( - serviceItem->Name->Buffer, - SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - &info - ); - } - } - - PhMarkNeedsConfigUpdateServiceItem(serviceItem); - } - - PhUiDisconnectFromPhSvc(); - - if (!NT_SUCCESS(status)) - { - SetLastError(PhNtStatusToDosError(status)); - goto ErrorCase; - } - } - else - { - // User cancelled elevation. - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } - } - else - { - goto ErrorCase; - } - } - - goto Cleanup; -ErrorCase: - if (PhShowMessage( - hwndDlg, - MB_ICONERROR | MB_RETRYCANCEL, - L"Unable to change service configuration: %s", - PH_AUTO_T(PH_STRING, PhGetWin32Message(GetLastError()))->Buffer - ) == IDRETRY) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } - -Cleanup: - if (newServicePassword) - { - RtlSecureZeroMemory(newServicePassword->Buffer, newServicePassword->Length); - PhDereferenceObject(newServicePassword); - } - } - return TRUE; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * service properties + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef struct _SERVICE_PROPERTIES_CONTEXT +{ + PPH_SERVICE_ITEM ServiceItem; + BOOLEAN Ready; + BOOLEAN Dirty; + + BOOLEAN OldDelayedStart; +} SERVICE_PROPERTIES_CONTEXT, *PSERVICE_PROPERTIES_CONTEXT; + +INT_PTR CALLBACK PhpServiceGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static NTSTATUS PhpOpenService( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ) +{ + SC_HANDLE serviceHandle; + PPH_SERVICE_ITEM serviceItem; + + serviceItem = ((PPH_SERVICE_ITEM)Context); + + if (serviceHandle = PhOpenService( + serviceItem->Name->Buffer, + DesiredAccess + )) + { + *Handle = serviceHandle; + return STATUS_SUCCESS; + } + + return PhGetLastWin32ErrorAsNtStatus(); +} + +NTSTATUS PhpCloseServiceCallback( + _In_opt_ PVOID Context + ) +{ + PPH_SERVICE_ITEM serviceItem; + + serviceItem = ((PPH_SERVICE_ITEM)Context); + PhDereferenceObject(serviceItem); + + return STATUS_SUCCESS; +} + +static _Callback_ NTSTATUS PhpSetServiceSecurity( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PPH_STD_OBJECT_SECURITY stdObjectSecurity; + + stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context; + + status = PhStdSetObjectSecurity(SecurityDescriptor, SecurityInformation, Context); + + if ((status == STATUS_ACCESS_DENIED || status == NTSTATUS_FROM_WIN32(ERROR_ACCESS_DENIED)) && !PhGetOwnTokenAttributes().Elevated) + { + // Elevate using phsvc. + if (PhUiConnectToPhSvc(NULL, FALSE)) + { + status = PhSvcCallSetServiceSecurity( + ((PPH_SERVICE_ITEM)stdObjectSecurity->Context)->Name->Buffer, + SecurityInformation, + SecurityDescriptor + ); + PhUiDisconnectFromPhSvc(); + } + } + + return status; +} + +NTSTATUS PhpShowServicePropertiesThread( + _In_opt_ PVOID Context + ) +{ + PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; + PROPSHEETPAGE propSheetPage; + HPROPSHEETPAGE pages[32]; + SERVICE_PROPERTIES_CONTEXT context; + PPH_SERVICE_ITEM serviceItem; + + serviceItem = ((PPH_SERVICE_ITEM)Context); + + propSheetHeader.dwFlags = + PSH_MODELESS | + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE | + //PSH_USECALLBACK | + PSH_USEHICON; + propSheetHeader.hInstance = PhInstanceHandle; + //propSheetHeader.hwndParent = ParentWindowHandle; + propSheetHeader.pszCaption = PhGetString(serviceItem->Name); + propSheetHeader.nPages = 0; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = pages; + + { + if (serviceItem->SmallIcon) + propSheetHeader.hIcon = serviceItem->SmallIcon; + else + { + if (serviceItem->Type == SERVICE_KERNEL_DRIVER || serviceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) + propSheetHeader.hIcon = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_COG)); + else + { + HICON iconSmall; + PhGetStockApplicationIcon(&iconSmall, NULL); + propSheetHeader.hIcon = iconSmall; + } + } + } + + // General + + memset(&context, 0, sizeof(SERVICE_PROPERTIES_CONTEXT)); + context.ServiceItem = serviceItem; + context.Ready = FALSE; + context.Dirty = FALSE; + + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVGENERAL); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = PhpServiceGeneralDlgProc; + propSheetPage.lParam = (LPARAM)&context; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + if (PhPluginsEnabled) + { + PH_PLUGIN_OBJECT_PROPERTIES objectProperties; + + objectProperties.Parameter = serviceItem; + objectProperties.NumberOfPages = propSheetHeader.nPages; + objectProperties.MaximumNumberOfPages = sizeof(pages) / sizeof(HPROPSHEETPAGE); + objectProperties.Pages = pages; + + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), &objectProperties); + + propSheetHeader.nPages = objectProperties.NumberOfPages; + } + + PhModalPropertySheet(&propSheetHeader); + + PhDereferenceObject(serviceItem); + + return STATUS_SUCCESS; +} + +VOID PhShowServiceProperties( + _In_ HWND ParentWindowHandle, + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PhReferenceObject(ServiceItem); + PhCreateThread2(PhpShowServicePropertiesThread, ServiceItem); +} + +static VOID PhpRefreshControls( + _In_ HWND hwndDlg + ) +{ + if (PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_STARTTYPE), L"Auto start", FALSE)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), TRUE); + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), FALSE); + } + + if (PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_TYPE), L"Driver", FALSE) || + PhEqualString2(PhaGetDlgItemText(hwndDlg, IDC_TYPE), L"FS driver", FALSE)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_USERACCOUNT), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_SERVICEDLL), FALSE); + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORD), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_SERVICEDLL), TRUE); + } +} + +INT_PTR CALLBACK PhpServiceGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_PROPERTIES_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + context = (PSERVICE_PROPERTIES_CONTEXT)propSheetPage->lParam; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_SERVICE_ITEM serviceItem = context->ServiceItem; + SC_HANDLE serviceHandle; + ULONG startType; + ULONG errorControl; + PPH_STRING serviceDll; + + // HACK + if (PhGetIntegerPairSetting(L"ServiceWindowPosition").X == 0) + PhCenterWindow(GetParent(hwndDlg), PhMainWndHandle); + else + PhLoadWindowPlacementFromSetting(L"ServiceWindowPosition", NULL, GetParent(hwndDlg)); + + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_TYPE), PhServiceTypeStrings, RTL_NUMBER_OF(PhServiceTypeStrings)); + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_STARTTYPE), PhServiceStartTypeStrings, RTL_NUMBER_OF(PhServiceStartTypeStrings)); + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), PhServiceErrorControlStrings, RTL_NUMBER_OF(PhServiceErrorControlStrings)); + + PhSetDialogItemText(hwndDlg, IDC_DESCRIPTION, PhGetStringOrEmpty(serviceItem->DisplayName)); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_TYPE), PhGetServiceTypeString(serviceItem->Type), FALSE); + + startType = serviceItem->StartType; + errorControl = serviceItem->ErrorControl; + serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG); + + if (serviceHandle) + { + LPQUERY_SERVICE_CONFIG config; + PPH_STRING description; + BOOLEAN delayedStart; + + if (config = PhGetServiceConfig(serviceHandle)) + { + PhSetDialogItemText(hwndDlg, IDC_GROUP, config->lpLoadOrderGroup); + PhSetDialogItemText(hwndDlg, IDC_BINARYPATH, config->lpBinaryPathName); + PhSetDialogItemText(hwndDlg, IDC_USERACCOUNT, config->lpServiceStartName); + + if (startType != config->dwStartType || errorControl != config->dwErrorControl) + { + startType = config->dwStartType; + errorControl = config->dwErrorControl; + PhMarkNeedsConfigUpdateServiceItem(serviceItem); + } + + PhFree(config); + } + + if (description = PhGetServiceDescription(serviceHandle)) + { + PhSetDialogItemText(hwndDlg, IDC_DESCRIPTION, description->Buffer); + PhDereferenceObject(description); + } + + if (PhGetServiceDelayedAutoStart(serviceHandle, &delayedStart)) + { + context->OldDelayedStart = delayedStart; + + if (delayedStart) + Button_SetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART), BST_CHECKED); + } + + CloseServiceHandle(serviceHandle); + } + + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_STARTTYPE), PhGetServiceStartTypeString(startType), FALSE); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_ERRORCONTROL), PhGetServiceErrorControlString(errorControl), FALSE); + + PhSetDialogItemText(hwndDlg, IDC_PASSWORD, L"password"); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK), BST_UNCHECKED); + + if (NT_SUCCESS(PhGetServiceDllParameter(serviceItem->Type, &serviceItem->Name->sr, &serviceDll))) + { + PhSetDialogItemText(hwndDlg, IDC_SERVICEDLL, serviceDll->Buffer); + PhDereferenceObject(serviceDll); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_SERVICEDLL, L"N/A"); + } + + PhpRefreshControls(hwndDlg); + + context->Ready = TRUE; + + if (PhEnableThemeSupport) // TODO: Required for compat (dmex) + PhInitializeWindowTheme(GetParent(hwndDlg), PhEnableThemeSupport); // HACK (GetParent) + else + PhInitializeWindowTheme(hwndDlg, FALSE); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(L"ServiceWindowPosition", NULL, GetParent(hwndDlg)); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + { + // Workaround for property sheet + multiline edit: http://support.microsoft.com/kb/130765 + SendMessage(GetParent(hwndDlg), uMsg, wParam, lParam); + } + break; + case IDC_PASSWORD: + { + if (HIWORD(wParam) == EN_CHANGE) + { + Button_SetCheck(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK), BST_CHECKED); + } + } + break; + case IDC_DELAYEDSTART: + { + context->Dirty = TRUE; + } + break; + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Executable files (*.exe;*.sys)", L"*.exe;*.sys" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING commandLine; + PPH_STRING fileName; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + commandLine = PhaGetDlgItemText(hwndDlg, IDC_BINARYPATH); + + if (context->ServiceItem->Type & SERVICE_WIN32) + { + PH_STRINGREF dummyFileName; + PH_STRINGREF dummyArguments; + + if (!PhParseCommandLineFuzzy(&commandLine->sr, &dummyFileName, &dummyArguments, &fileName)) + fileName = NULL; + + if (!fileName) + PhSwapReference(&fileName, commandLine); + } + else + { + fileName = PhGetFileName(commandLine); + } + + PhSetFileDialogFileName(fileDialog, fileName->Buffer); + PhDereferenceObject(fileName); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + fileName = PhGetFileDialogFileName(fileDialog); + PhSetDialogItemText(hwndDlg, IDC_BINARYPATH, fileName->Buffer); + PhDereferenceObject(fileName); + } + + PhFreeFileDialog(fileDialog); + } + break; + case IDC_PERMISSIONS: + { + PhReferenceObject(context->ServiceItem); + + PhEditSecurity( + PhCsForceNoParent ? NULL : hwndDlg, + PhGetStringOrEmpty(context->ServiceItem->DisplayName), + L"Service", + PhpOpenService, + PhpCloseServiceCallback, + context->ServiceItem + ); + } + break; + } + + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + case CBN_SELCHANGE: + { + PhpRefreshControls(hwndDlg); + + if (context->Ready) + context->Dirty = TRUE; + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_STARTTYPE)); + } + return TRUE; + //case PSN_KILLACTIVE: + // { + // SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); + // } + // return TRUE; + case PSN_APPLY: + { + NTSTATUS status; + PPH_SERVICE_ITEM serviceItem = context->ServiceItem; + SC_HANDLE serviceHandle; + PPH_STRING newServiceTypeString; + PPH_STRING newServiceStartTypeString; + PPH_STRING newServiceErrorControlString; + ULONG newServiceType; + ULONG newServiceStartType; + ULONG newServiceErrorControl; + PPH_STRING newServiceGroup = NULL; + PPH_STRING newServiceBinaryPath = NULL; + PPH_STRING newServiceUserAccount = NULL; + PPH_STRING newServicePassword = NULL; + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + + if (!context->Dirty) + { + return TRUE; + } + + newServiceTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_TYPE))); + newServiceStartTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_STARTTYPE))); + newServiceErrorControlString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_ERRORCONTROL))); + newServiceType = PhGetServiceTypeInteger(newServiceTypeString->Buffer); + newServiceStartType = PhGetServiceStartTypeInteger(newServiceStartTypeString->Buffer); + newServiceErrorControl = PhGetServiceErrorControlInteger(newServiceErrorControlString->Buffer); + + if (PhGetWindowTextLength(GetDlgItem(hwndDlg, IDC_GROUP))) + newServiceGroup = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_GROUP))); + if (PhGetWindowTextLength(GetDlgItem(hwndDlg, IDC_BINARYPATH))) + newServiceBinaryPath = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_BINARYPATH))); + if (PhGetWindowTextLength(GetDlgItem(hwndDlg, IDC_USERACCOUNT))) + newServiceUserAccount = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_USERACCOUNT))); + + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_PASSWORDCHECK)) == BST_CHECKED) + newServicePassword = PhGetWindowText(GetDlgItem(hwndDlg, IDC_PASSWORD)); + + serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_CHANGE_CONFIG); + + if (serviceHandle) + { + if (ChangeServiceConfig( + serviceHandle, + newServiceType, + newServiceStartType, + newServiceErrorControl, + PhGetString(newServiceBinaryPath), + PhGetString(newServiceGroup), + NULL, + NULL, + PhGetString(newServiceUserAccount), + PhGetString(newServicePassword), + NULL + )) + { + BOOLEAN newDelayedStart; + + newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; + + if (newDelayedStart != context->OldDelayedStart) + { + PhSetServiceDelayedAutoStart(serviceHandle, newDelayedStart); + } + + PhMarkNeedsConfigUpdateServiceItem(serviceItem); + + CloseServiceHandle(serviceHandle); + } + else + { + CloseServiceHandle(serviceHandle); + goto ErrorCase; + } + } + else + { + if (GetLastError() == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) + { + // Elevate using phsvc. + if (PhUiConnectToPhSvc(hwndDlg, FALSE)) + { + if (NT_SUCCESS(status = PhSvcCallChangeServiceConfig( + serviceItem->Name->Buffer, + newServiceType, + newServiceStartType, + newServiceErrorControl, + PhGetString(newServiceBinaryPath), + PhGetString(newServiceGroup), + NULL, + NULL, + PhGetString(newServiceUserAccount), + PhGetString(newServicePassword), + NULL + ))) + { + BOOLEAN newDelayedStart; + + newDelayedStart = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DELAYEDSTART)) == BST_CHECKED; + + if (newDelayedStart != context->OldDelayedStart) + { + SERVICE_DELAYED_AUTO_START_INFO info; + + info.fDelayedAutostart = newDelayedStart; + PhSvcCallChangeServiceConfig2( + serviceItem->Name->Buffer, + SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + &info + ); + } + + PhMarkNeedsConfigUpdateServiceItem(serviceItem); + } + + PhUiDisconnectFromPhSvc(); + + if (!NT_SUCCESS(status)) + { + SetLastError(PhNtStatusToDosError(status)); + goto ErrorCase; + } + } + else + { + // User cancelled elevation. + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + } + else + { + goto ErrorCase; + } + } + + goto Cleanup; +ErrorCase: + + PhShowStatus(hwndDlg, L"Unable to change service configuration.", 0, GetLastError()); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + +Cleanup: + if (newServicePassword) + { + RtlSecureZeroMemory(newServicePassword->Buffer, newServicePassword->Length); + PhDereferenceObject(newServicePassword); + } + } + return TRUE; + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/srvprv.c b/ProcessHacker/srvprv.c index c7f9d45bbc7f..98163cfe2923 100644 --- a/ProcessHacker/srvprv.c +++ b/ProcessHacker/srvprv.c @@ -1,1041 +1,1370 @@ -/* - * Process Hacker - - * service provider - * - * Copyright (C) 2009-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#include -#include - -typedef DWORD (WINAPI *_NotifyServiceStatusChangeW)( - _In_ SC_HANDLE hService, - _In_ DWORD dwNotifyMask, - _In_ PSERVICE_NOTIFYW pNotifyBuffer - ); - -typedef struct _PHP_SERVICE_NAME_ENTRY -{ - PH_HASH_ENTRY HashEntry; - PH_STRINGREF Name; - ENUM_SERVICE_STATUS_PROCESS *ServiceEntry; -} PHP_SERVICE_NAME_ENTRY, *PPHP_SERVICE_NAME_ENTRY; - -typedef enum _PHP_SERVICE_NOTIFY_STATE -{ - SnNone, - SnAdding, - SnRemoving, - SnNotify -} PHP_SERVICE_NOTIFY_STATE; - -typedef struct _PHP_SERVICE_NOTIFY_CONTEXT -{ - LIST_ENTRY ListEntry; - SC_HANDLE ServiceHandle; - PPH_STRING ServiceName; // Valid only when adding - BOOLEAN IsServiceManager; - PHP_SERVICE_NOTIFY_STATE State; - SERVICE_NOTIFY Buffer; -} PHP_SERVICE_NOTIFY_CONTEXT, *PPHP_SERVICE_NOTIFY_CONTEXT; - -VOID NTAPI PhpServiceItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -BOOLEAN NTAPI PhpServiceHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI PhpServiceHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpAddProcessItemService( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ PPH_SERVICE_ITEM ServiceItem - ); - -VOID PhpRemoveProcessItemService( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ PPH_SERVICE_ITEM ServiceItem - ); - -VOID PhpInitializeServiceNonPoll( - VOID - ); - -PPH_OBJECT_TYPE PhServiceItemType; - -PPH_HASHTABLE PhServiceHashtable; -PH_QUEUED_LOCK PhServiceHashtableLock = PH_QUEUED_LOCK_INIT; - -PHAPPAPI PH_CALLBACK_DECLARE(PhServiceAddedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhServiceModifiedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhServiceRemovedEvent); -PHAPPAPI PH_CALLBACK_DECLARE(PhServicesUpdatedEvent); - -BOOLEAN PhEnableServiceNonPoll = FALSE; -static BOOLEAN PhpNonPollInitialized = FALSE; -static BOOLEAN PhpNonPollActive = FALSE; -static HANDLE PhpNonPollThreadHandle; -static ULONG PhpNonPollGate; -static _NotifyServiceStatusChangeW NotifyServiceStatusChangeW_I; -static HANDLE PhpNonPollEventHandle; -static PH_QUEUED_LOCK PhpNonPollServiceListLock = PH_QUEUED_LOCK_INIT; -static LIST_ENTRY PhpNonPollServiceListHead; -static LIST_ENTRY PhpNonPollServicePendingListHead; - -BOOLEAN PhServiceProviderInitialization( - VOID - ) -{ - PhServiceItemType = PhCreateObjectType(L"ServiceItem", 0, PhpServiceItemDeleteProcedure); - PhServiceHashtable = PhCreateHashtable( - sizeof(PPH_SERVICE_ITEM), - PhpServiceHashtableEqualFunction, - PhpServiceHashtableHashFunction, - 40 - ); - - return TRUE; -} - -PPH_SERVICE_ITEM PhCreateServiceItem( - _In_opt_ LPENUM_SERVICE_STATUS_PROCESS Information - ) -{ - PPH_SERVICE_ITEM serviceItem; - - serviceItem = PhCreateObject( - PhEmGetObjectSize(EmServiceItemType, sizeof(PH_SERVICE_ITEM)), - PhServiceItemType - ); - memset(serviceItem, 0, sizeof(PH_SERVICE_ITEM)); - - if (Information) - { - serviceItem->Name = PhCreateString(Information->lpServiceName); - serviceItem->Key = serviceItem->Name->sr; - serviceItem->DisplayName = PhCreateString(Information->lpDisplayName); - serviceItem->Type = Information->ServiceStatusProcess.dwServiceType; - serviceItem->State = Information->ServiceStatusProcess.dwCurrentState; - serviceItem->ControlsAccepted = Information->ServiceStatusProcess.dwControlsAccepted; - serviceItem->Flags = Information->ServiceStatusProcess.dwServiceFlags; - serviceItem->ProcessId = UlongToHandle(Information->ServiceStatusProcess.dwProcessId); - - if (serviceItem->ProcessId) - PhPrintUInt32(serviceItem->ProcessIdString, HandleToUlong(serviceItem->ProcessId)); - } - - PhEmCallObjectOperation(EmServiceItemType, serviceItem, EmObjectCreate); - - return serviceItem; -} - -VOID PhpServiceItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Object; - - PhEmCallObjectOperation(EmServiceItemType, serviceItem, EmObjectDelete); - - if (serviceItem->Name) PhDereferenceObject(serviceItem->Name); - if (serviceItem->DisplayName) PhDereferenceObject(serviceItem->DisplayName); -} - -BOOLEAN PhpServiceHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)Entry1; - PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)Entry2; - - return PhEqualStringRef(&serviceItem1->Key, &serviceItem2->Key, TRUE); -} - -ULONG PhpServiceHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPH_SERVICE_ITEM serviceItem = *(PPH_SERVICE_ITEM *)Entry; - - return PhHashStringRef(&serviceItem->Key, TRUE); -} - -PPH_SERVICE_ITEM PhpLookupServiceItem( - _In_ PPH_STRINGREF Name - ) -{ - PH_SERVICE_ITEM lookupServiceItem; - PPH_SERVICE_ITEM lookupServiceItemPtr = &lookupServiceItem; - PPH_SERVICE_ITEM *serviceItem; - - // Construct a temporary service item for the lookup. - lookupServiceItem.Key = *Name; - - serviceItem = (PPH_SERVICE_ITEM *)PhFindEntryHashtable( - PhServiceHashtable, - &lookupServiceItemPtr - ); - - if (serviceItem) - return *serviceItem; - else - return NULL; -} - -PPH_SERVICE_ITEM PhReferenceServiceItem( - _In_ PWSTR Name - ) -{ - PPH_SERVICE_ITEM serviceItem; - PH_STRINGREF key; - - PhInitializeStringRef(&key, Name); - - PhAcquireQueuedLockShared(&PhServiceHashtableLock); - - serviceItem = PhpLookupServiceItem(&key); - - if (serviceItem) - PhReferenceObject(serviceItem); - - PhReleaseQueuedLockShared(&PhServiceHashtableLock); - - return serviceItem; -} - -VOID PhMarkNeedsConfigUpdateServiceItem( - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - ServiceItem->NeedsConfigUpdate = TRUE; - - if (PhEnableServiceNonPoll) - PhpNonPollGate = 1; -} - -VOID PhpRemoveServiceItem( - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - PhRemoveEntryHashtable(PhServiceHashtable, &ServiceItem); - PhDereferenceObject(ServiceItem); -} - -PH_SERVICE_CHANGE PhGetServiceChange( - _In_ PPH_SERVICE_MODIFIED_DATA Data - ) -{ - if ( - ( - Data->OldService.State == SERVICE_STOPPED || - Data->OldService.State == SERVICE_START_PENDING - ) && - Data->Service->State == SERVICE_RUNNING - ) - { - return ServiceStarted; - } - - if ( - ( - Data->OldService.State == SERVICE_PAUSED || - Data->OldService.State == SERVICE_CONTINUE_PENDING - ) && - Data->Service->State == SERVICE_RUNNING - ) - { - return ServiceContinued; - } - - if ( - ( - Data->OldService.State == SERVICE_RUNNING || - Data->OldService.State == SERVICE_PAUSE_PENDING - ) && - Data->Service->State == SERVICE_PAUSED - ) - { - return ServicePaused; - } - - if ( - ( - Data->OldService.State == SERVICE_RUNNING || - Data->OldService.State == SERVICE_STOP_PENDING - ) && - Data->Service->State == SERVICE_STOPPED - ) - { - return ServiceStopped; - } - - return -1; -} - -VOID PhUpdateProcessItemServices( - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_SERVICE_ITEM *serviceItem; - - // We don't need to lock as long as the service provider - // never runs concurrently with the process provider. This - // is currently true. - - PhBeginEnumHashtable(PhServiceHashtable, &enumContext); - - while (serviceItem = PhNextEnumHashtable(&enumContext)) - { - if ( - (*serviceItem)->PendingProcess && - (*serviceItem)->ProcessId == ProcessItem->ProcessId - ) - { - PhpAddProcessItemService(ProcessItem, *serviceItem); - } - } -} - -VOID PhpAddProcessItemService( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - PhAcquireQueuedLockExclusive(&ProcessItem->ServiceListLock); - - if (!ProcessItem->ServiceList) - ProcessItem->ServiceList = PhCreatePointerList(2); - - if (!PhFindItemPointerList(ProcessItem->ServiceList, ServiceItem)) - { - PhReferenceObject(ServiceItem); - PhAddItemPointerList(ProcessItem->ServiceList, ServiceItem); - } - - PhReleaseQueuedLockExclusive(&ProcessItem->ServiceListLock); - - ServiceItem->PendingProcess = FALSE; - ProcessItem->JustProcessed = 1; -} - -VOID PhpRemoveProcessItemService( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - HANDLE pointerHandle; - - if (!ProcessItem->ServiceList) - return; - - PhAcquireQueuedLockExclusive(&ProcessItem->ServiceListLock); - - if (pointerHandle = PhFindItemPointerList(ProcessItem->ServiceList, ServiceItem)) - { - PhRemoveItemPointerList(ProcessItem->ServiceList, pointerHandle); - PhDereferenceObject(ServiceItem); - } - - PhReleaseQueuedLockExclusive(&ProcessItem->ServiceListLock); - - ProcessItem->JustProcessed = 1; -} - -VOID PhpUpdateServiceItemConfig( - _In_ SC_HANDLE ScManagerHandle, - _In_ PPH_SERVICE_ITEM ServiceItem - ) -{ - SC_HANDLE serviceHandle; - - serviceHandle = OpenService(ScManagerHandle, ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG); - - if (serviceHandle) - { - LPQUERY_SERVICE_CONFIG config; - SERVICE_DELAYED_AUTO_START_INFO delayedAutoStartInfo; - ULONG returnLength; - PSERVICE_TRIGGER_INFO triggerInfo; - - config = PhGetServiceConfig(serviceHandle); - - if (config) - { - ServiceItem->StartType = config->dwStartType; - ServiceItem->ErrorControl = config->dwErrorControl; - - PhFree(config); - } - - if (QueryServiceConfig2( - serviceHandle, - SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - (BYTE *)&delayedAutoStartInfo, - sizeof(SERVICE_DELAYED_AUTO_START_INFO), - &returnLength - )) - { - ServiceItem->DelayedStart = delayedAutoStartInfo.fDelayedAutostart; - } - else - { - ServiceItem->DelayedStart = FALSE; - } - - if (triggerInfo = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO)) - { - ServiceItem->HasTriggers = triggerInfo->cTriggers != 0; - PhFree(triggerInfo); - } - else - { - ServiceItem->HasTriggers = FALSE; - } - - CloseServiceHandle(serviceHandle); - } -} - -static BOOLEAN PhpCompareServiceNameEntry( - _In_ PPHP_SERVICE_NAME_ENTRY Value1, - _In_ PPHP_SERVICE_NAME_ENTRY Value2 - ) -{ - return PhEqualStringRef(&Value1->Name, &Value2->Name, TRUE); -} - -static ULONG PhpHashServiceNameEntry( - _In_ PPHP_SERVICE_NAME_ENTRY Value - ) -{ - return PhHashStringRef(&Value->Name, TRUE); -} - -VOID PhServiceProviderUpdate( - _In_ PVOID Object - ) -{ - static SC_HANDLE scManagerHandle = NULL; - static ULONG runCount = 0; - - static PPH_HASH_ENTRY nameHashSet[256]; - static PPHP_SERVICE_NAME_ENTRY nameEntries = NULL; - static ULONG nameEntriesCount; - static ULONG nameEntriesAllocated = 0; - - LPENUM_SERVICE_STATUS_PROCESS services; - ULONG numberOfServices; - ULONG i; - PPH_HASH_ENTRY hashEntry; - - // We always execute the first run, and we only initialize non-polling after the first run. - if (PhEnableServiceNonPoll && runCount != 0) - { - if (!PhpNonPollInitialized) - { - if (WindowsVersion >= WINDOWS_VISTA) - { - PhpInitializeServiceNonPoll(); - } - - PhpNonPollInitialized = TRUE; - } - - if (PhpNonPollActive) - { - if (InterlockedExchange(&PhpNonPollGate, 0) == 0) - { - // Non-poll gate is closed; skip all processing. - goto UpdateEnd; - } - } - } - - if (!scManagerHandle) - { - scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); - - if (!scManagerHandle) - return; - } - - services = PhEnumServices(scManagerHandle, 0, 0, &numberOfServices); - - if (!services) - return; - - // Build a hash set containing the service names. - - // This has caused a massive decrease in background CPU usage, and - // is certainly much better than the quadratic-time string comparisons - // we were doing before (in the "Look for dead services" section). - - nameEntriesCount = 0; - - if (nameEntriesAllocated < numberOfServices) - { - nameEntriesAllocated = numberOfServices + 32; - - if (nameEntries) PhFree(nameEntries); - nameEntries = PhAllocate(sizeof(PHP_SERVICE_NAME_ENTRY) * nameEntriesAllocated); - } - - PhInitializeHashSet(nameHashSet, PH_HASH_SET_SIZE(nameHashSet)); - - for (i = 0; i < numberOfServices; i++) - { - PPHP_SERVICE_NAME_ENTRY entry; - - entry = &nameEntries[nameEntriesCount++]; - PhInitializeStringRefLongHint(&entry->Name, services[i].lpServiceName); - entry->ServiceEntry = &services[i]; - PhAddEntryHashSet( - nameHashSet, - PH_HASH_SET_SIZE(nameHashSet), - &entry->HashEntry, - PhpHashServiceNameEntry(entry) - ); - } - - // Look for dead services. - { - PPH_LIST servicesToRemove = NULL; - PH_HASHTABLE_ENUM_CONTEXT enumContext; - PPH_SERVICE_ITEM *serviceItem; - - PhBeginEnumHashtable(PhServiceHashtable, &enumContext); - - while (serviceItem = PhNextEnumHashtable(&enumContext)) - { - BOOLEAN found = FALSE; - PHP_SERVICE_NAME_ENTRY lookupNameEntry; - - // Check if the service still exists. - - lookupNameEntry.Name = (*serviceItem)->Name->sr; - hashEntry = PhFindEntryHashSet( - nameHashSet, - PH_HASH_SET_SIZE(nameHashSet), - PhpHashServiceNameEntry(&lookupNameEntry) - ); - - for (; hashEntry; hashEntry = hashEntry->Next) - { - PPHP_SERVICE_NAME_ENTRY nameEntry; - - nameEntry = CONTAINING_RECORD(hashEntry, PHP_SERVICE_NAME_ENTRY, HashEntry); - - if (PhpCompareServiceNameEntry(&lookupNameEntry, nameEntry)) - { - found = TRUE; - break; - } - } - - if (!found) - { - // Remove the service from its process. - if ((*serviceItem)->ProcessId) - { - PPH_PROCESS_ITEM processItem; - - processItem = PhReferenceProcessItem((HANDLE)(*serviceItem)->ProcessId); - - if (processItem) - { - PhpRemoveProcessItemService(processItem, *serviceItem); - PhDereferenceObject(processItem); - } - } - - // Raise the service removed event. - PhInvokeCallback(&PhServiceRemovedEvent, *serviceItem); - - if (!servicesToRemove) - servicesToRemove = PhCreateList(2); - - PhAddItemList(servicesToRemove, *serviceItem); - } - } - - if (servicesToRemove) - { - PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); - - for (i = 0; i < servicesToRemove->Count; i++) - { - PhpRemoveServiceItem((PPH_SERVICE_ITEM)servicesToRemove->Items[i]); - } - - PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); - PhDereferenceObject(servicesToRemove); - } - } - - // Look for new services and update existing ones. - for (i = 0; i < PH_HASH_SET_SIZE(nameHashSet); i++) - { - for (hashEntry = nameHashSet[i]; hashEntry; hashEntry = hashEntry->Next) - { - PPH_SERVICE_ITEM serviceItem; - PPHP_SERVICE_NAME_ENTRY nameEntry; - ENUM_SERVICE_STATUS_PROCESS *serviceEntry; - - nameEntry = CONTAINING_RECORD(hashEntry, PHP_SERVICE_NAME_ENTRY, HashEntry); - serviceEntry = nameEntry->ServiceEntry; - serviceItem = PhpLookupServiceItem(&nameEntry->Name); - - if (!serviceItem) - { - // Create the service item and fill in basic information. - - serviceItem = PhCreateServiceItem(serviceEntry); - - PhpUpdateServiceItemConfig(scManagerHandle, serviceItem); - - // Add the service to its process, if appropriate. - if ( - ( - serviceItem->State == SERVICE_RUNNING || - serviceItem->State == SERVICE_PAUSED - ) && - serviceItem->ProcessId - ) - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(serviceItem->ProcessId)) - { - PhpAddProcessItemService(processItem, serviceItem); - PhDereferenceObject(processItem); - } - else - { - // The process doesn't exist yet (to us). Set the pending - // flag and when the process is added this will be - // fixed. - serviceItem->PendingProcess = TRUE; - } - } - - // Add the service item to the hashtable. - PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); - PhAddEntryHashtable(PhServiceHashtable, &serviceItem); - PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); - - // Raise the service added event. - PhInvokeCallback(&PhServiceAddedEvent, serviceItem); - } - else - { - if ( - serviceItem->Type != serviceEntry->ServiceStatusProcess.dwServiceType || - serviceItem->State != serviceEntry->ServiceStatusProcess.dwCurrentState || - serviceItem->ControlsAccepted != serviceEntry->ServiceStatusProcess.dwControlsAccepted || - serviceItem->ProcessId != UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId) || - serviceItem->NeedsConfigUpdate - ) - { - PH_SERVICE_MODIFIED_DATA serviceModifiedData; - PH_SERVICE_CHANGE serviceChange; - - // The service has been "modified". - - serviceModifiedData.Service = serviceItem; - memset(&serviceModifiedData.OldService, 0, sizeof(PH_SERVICE_ITEM)); - serviceModifiedData.OldService.Type = serviceItem->Type; - serviceModifiedData.OldService.State = serviceItem->State; - serviceModifiedData.OldService.ControlsAccepted = serviceItem->ControlsAccepted; - serviceModifiedData.OldService.ProcessId = serviceItem->ProcessId; - - // Update the service item. - serviceItem->Type = serviceEntry->ServiceStatusProcess.dwServiceType; - serviceItem->State = serviceEntry->ServiceStatusProcess.dwCurrentState; - serviceItem->ControlsAccepted = serviceEntry->ServiceStatusProcess.dwControlsAccepted; - serviceItem->ProcessId = UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId); - - if (serviceItem->ProcessId) - PhPrintUInt32(serviceItem->ProcessIdString, HandleToUlong(serviceItem->ProcessId)); - else - serviceItem->ProcessIdString[0] = 0; - - // Add/remove the service from its process. - - serviceChange = PhGetServiceChange(&serviceModifiedData); - - if ( - (serviceChange == ServiceStarted && serviceItem->ProcessId) || - (serviceChange == ServiceStopped && serviceModifiedData.OldService.ProcessId) - ) - { - PPH_PROCESS_ITEM processItem; - - if (serviceChange == ServiceStarted) - processItem = PhReferenceProcessItem(serviceItem->ProcessId); - else - processItem = PhReferenceProcessItem(serviceModifiedData.OldService.ProcessId); - - if (processItem) - { - if (serviceChange == ServiceStarted) - PhpAddProcessItemService(processItem, serviceItem); - else - PhpRemoveProcessItemService(processItem, serviceItem); - - PhDereferenceObject(processItem); - } - else - { - if (serviceChange == ServiceStarted) - serviceItem->PendingProcess = TRUE; - else - serviceItem->PendingProcess = FALSE; - } - } - else if ( - serviceItem->State == SERVICE_RUNNING && - serviceItem->ProcessId != serviceModifiedData.OldService.ProcessId && - serviceItem->ProcessId - ) - { - PPH_PROCESS_ITEM processItem; - - // The service stopped and started, and the only change we have detected - // is in the process ID. - - if (processItem = PhReferenceProcessItem(serviceModifiedData.OldService.ProcessId)) - { - PhpRemoveProcessItemService(processItem, serviceItem); - PhDereferenceObject(processItem); - } - - if (processItem = PhReferenceProcessItem(serviceItem->ProcessId)) - { - PhpAddProcessItemService(processItem, serviceItem); - PhDereferenceObject(processItem); - } - else - { - serviceItem->PendingProcess = TRUE; - } - } - - // Do a config update if necessary. - if (serviceItem->NeedsConfigUpdate) - { - PhpUpdateServiceItemConfig(scManagerHandle, serviceItem); - serviceItem->NeedsConfigUpdate = FALSE; - } - - // Raise the service modified event. - PhInvokeCallback(&PhServiceModifiedEvent, &serviceModifiedData); - } - } - } - } - - PhFree(services); - -UpdateEnd: - PhInvokeCallback(&PhServicesUpdatedEvent, NULL); - runCount++; -} - -VOID CALLBACK PhpServiceNonPollScNotifyCallback( - _In_ PVOID pParameter - ) -{ - PSERVICE_NOTIFYW notifyBuffer = pParameter; - PPHP_SERVICE_NOTIFY_CONTEXT notifyContext = notifyBuffer->pContext; - - if (notifyBuffer->dwNotificationStatus == ERROR_SUCCESS) - { - if ((notifyBuffer->dwNotificationTriggered & (SERVICE_NOTIFY_CREATED | SERVICE_NOTIFY_DELETED)) && - notifyBuffer->pszServiceNames) - { - PWSTR name; - SIZE_T nameLength; - - name = notifyBuffer->pszServiceNames; - - while (TRUE) - { - nameLength = PhCountStringZ(name); - - if (nameLength == 0) - break; - - if (name[0] == '/') - { - PPHP_SERVICE_NOTIFY_CONTEXT newNotifyContext; - - // Service creation - newNotifyContext = PhAllocate(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); - memset(newNotifyContext, 0, sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); - newNotifyContext->State = SnAdding; - newNotifyContext->ServiceName = PhCreateString(name + 1); - InsertTailList(&PhpNonPollServicePendingListHead, &newNotifyContext->ListEntry); - } - - name += nameLength + 1; - } - - LocalFree(notifyBuffer->pszServiceNames); - } - - notifyContext->State = SnNotify; - RemoveEntryList(¬ifyContext->ListEntry); - InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); - } - else if (notifyBuffer->dwNotificationStatus == ERROR_SERVICE_MARKED_FOR_DELETE) - { - if (!notifyContext->IsServiceManager) - { - notifyContext->State = SnRemoving; - RemoveEntryList(¬ifyContext->ListEntry); - InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); - } - } - else - { - notifyContext->State = SnNotify; - RemoveEntryList(¬ifyContext->ListEntry); - InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); - } - - PhpNonPollGate = 1; - NtSetEvent(PhpNonPollEventHandle, NULL); -} - -VOID PhpDestroyServiceNotifyContext( - _In_ PPHP_SERVICE_NOTIFY_CONTEXT NotifyContext - ) -{ - if (NotifyContext->Buffer.pszServiceNames) - LocalFree(NotifyContext->Buffer.pszServiceNames); - - CloseServiceHandle(NotifyContext->ServiceHandle); - PhClearReference(&NotifyContext->ServiceName); - PhFree(NotifyContext); -} - -NTSTATUS PhpServiceNonPollThreadStart( - _In_ PVOID Parameter - ) -{ - ULONG result; - SC_HANDLE scManagerHandle; - LPENUM_SERVICE_STATUS_PROCESS services; - ULONG numberOfServices; - ULONG i; - PLIST_ENTRY listEntry; - PPHP_SERVICE_NOTIFY_CONTEXT notifyContext; - - if (!NT_SUCCESS(NtCreateEvent(&PhpNonPollEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) - { - PhpNonPollActive = FALSE; - PhpNonPollGate = 1; - return STATUS_UNSUCCESSFUL; - } - - while (TRUE) - { - scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); - - if (!scManagerHandle) - goto ErrorExit; - - InitializeListHead(&PhpNonPollServiceListHead); - InitializeListHead(&PhpNonPollServicePendingListHead); - - if (!(services = PhEnumServices(scManagerHandle, 0, 0, &numberOfServices))) - goto ErrorExit; - - for (i = 0; i < numberOfServices; i++) - { - SC_HANDLE serviceHandle; - - if (serviceHandle = OpenService(scManagerHandle, services[i].lpServiceName, SERVICE_QUERY_STATUS)) - { - notifyContext = PhAllocate(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); - memset(notifyContext, 0, sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); - notifyContext->ServiceHandle = serviceHandle; - notifyContext->State = SnNotify; - InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); - } - } - - PhFree(services); - - notifyContext = PhAllocate(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); - memset(notifyContext, 0, sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); - notifyContext->ServiceHandle = scManagerHandle; - notifyContext->IsServiceManager = TRUE; - notifyContext->State = SnNotify; - InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); - - while (TRUE) - { - BOOLEAN lagging = FALSE; - - listEntry = PhpNonPollServicePendingListHead.Flink; - - while (listEntry != &PhpNonPollServicePendingListHead) - { - notifyContext = CONTAINING_RECORD(listEntry, PHP_SERVICE_NOTIFY_CONTEXT, ListEntry); - listEntry = listEntry->Flink; - - switch (notifyContext->State) - { - case SnNone: - break; - case SnAdding: - notifyContext->ServiceHandle = - OpenService(scManagerHandle, notifyContext->ServiceName->Buffer, SERVICE_QUERY_STATUS); - - if (!notifyContext->ServiceHandle) - { - RemoveEntryList(¬ifyContext->ListEntry); - PhpDestroyServiceNotifyContext(notifyContext); - continue; - } - - PhClearReference(¬ifyContext->ServiceName); - notifyContext->State = SnNotify; - goto NotifyCase; - case SnRemoving: - RemoveEntryList(¬ifyContext->ListEntry); - PhpDestroyServiceNotifyContext(notifyContext); - break; - case SnNotify: -NotifyCase: - memset(¬ifyContext->Buffer, 0, sizeof(SERVICE_NOTIFY)); - notifyContext->Buffer.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; - notifyContext->Buffer.pfnNotifyCallback = PhpServiceNonPollScNotifyCallback; - notifyContext->Buffer.pContext = notifyContext; - result = NotifyServiceStatusChangeW_I( - notifyContext->ServiceHandle, - notifyContext->IsServiceManager - ? (SERVICE_NOTIFY_CREATED | SERVICE_NOTIFY_DELETED) - : (SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_START_PENDING | SERVICE_NOTIFY_STOP_PENDING | - SERVICE_NOTIFY_RUNNING | SERVICE_NOTIFY_CONTINUE_PENDING | SERVICE_NOTIFY_PAUSE_PENDING | - SERVICE_NOTIFY_PAUSED | SERVICE_NOTIFY_DELETE_PENDING), - ¬ifyContext->Buffer - ); - - switch (result) - { - case ERROR_SUCCESS: - notifyContext->State = SnNone; - RemoveEntryList(¬ifyContext->ListEntry); - InsertTailList(&PhpNonPollServiceListHead, ¬ifyContext->ListEntry); - break; - case ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: - // We are lagging behind. Re-open the handle to the SCM as soon as possible. - lagging = TRUE; - break; - case ERROR_SERVICE_MARKED_FOR_DELETE: - default: - RemoveEntryList(¬ifyContext->ListEntry); - PhpDestroyServiceNotifyContext(notifyContext); - break; - } - - break; - } - } - - while (NtWaitForSingleObject(PhpNonPollEventHandle, TRUE, NULL) != STATUS_WAIT_0) - NOTHING; - - if (lagging) - break; - } - - // Execute all pending callbacks. - NtTestAlert(); - - listEntry = PhpNonPollServiceListHead.Flink; - - while (listEntry != &PhpNonPollServiceListHead) - { - notifyContext = CONTAINING_RECORD(listEntry, PHP_SERVICE_NOTIFY_CONTEXT, ListEntry); - listEntry = listEntry->Flink; - PhpDestroyServiceNotifyContext(notifyContext); - } - - listEntry = PhpNonPollServicePendingListHead.Flink; - - while (listEntry != &PhpNonPollServicePendingListHead) - { - notifyContext = CONTAINING_RECORD(listEntry, PHP_SERVICE_NOTIFY_CONTEXT, ListEntry); - listEntry = listEntry->Flink; - PhpDestroyServiceNotifyContext(notifyContext); - } - - CloseServiceHandle(scManagerHandle); - } - - NtClose(PhpNonPollEventHandle); - - return STATUS_SUCCESS; - -ErrorExit: - PhpNonPollActive = FALSE; - PhpNonPollGate = 1; - NtClose(PhpNonPollEventHandle); - return STATUS_UNSUCCESSFUL; -} - -VOID PhpInitializeServiceNonPoll( - VOID - ) -{ - // Dynamically import the required functions. - - NotifyServiceStatusChangeW_I = PhGetModuleProcAddress(L"advapi32.dll", "NotifyServiceStatusChangeW"); - - if (!NotifyServiceStatusChangeW_I) - return; - - PhpNonPollActive = TRUE; - PhpNonPollGate = 1; // initially the gate should be open since we only just initialized everything - - PhpNonPollThreadHandle = PhCreateThread(0, PhpServiceNonPollThreadStart, NULL); - - if (!PhpNonPollThreadHandle) - { - PhpNonPollActive = FALSE; - return; - } -} +/* + * Process Hacker - + * service provider + * + * Copyright (C) 2009-2015 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +typedef ULONG (WINAPI *_SubscribeServiceChangeNotifications)( + _In_ SC_HANDLE hService, + _In_ SC_EVENT_TYPE eEventType, + _In_ PSC_NOTIFICATION_CALLBACK pCallback, + _In_opt_ PVOID pCallbackContext, + _Out_ PSC_NOTIFICATION_REGISTRATION* pSubscription + ); + +typedef VOID (WINAPI *_UnsubscribeServiceChangeNotifications)( + _In_ PSC_NOTIFICATION_REGISTRATION pSubscription + ); + +typedef struct _PHP_SERVICE_NAME_ENTRY +{ + PH_HASH_ENTRY HashEntry; + PH_STRINGREF Name; + ENUM_SERVICE_STATUS_PROCESS *ServiceEntry; +} PHP_SERVICE_NAME_ENTRY, *PPHP_SERVICE_NAME_ENTRY; + +typedef enum _PHP_SERVICE_NOTIFY_STATE +{ + SnNone, + SnAdding, + SnRemoving, + SnNotify +} PHP_SERVICE_NOTIFY_STATE; + +typedef struct _PHP_SERVICE_NOTIFY_CONTEXT +{ + LIST_ENTRY ListEntry; + SC_HANDLE ServiceHandle; + PPH_STRING ServiceName; + union + { + BOOLEAN Flags; + struct + { + BOOLEAN IsServiceManager : 1; + BOOLEAN JustAddedNotifyRegistration : 1; + BOOLEAN Spare : 6; + }; + }; + PHP_SERVICE_NOTIFY_STATE State; + SERVICE_NOTIFY Buffer; + PSC_NOTIFICATION_REGISTRATION NotifyRegistration; +} PHP_SERVICE_NOTIFY_CONTEXT, *PPHP_SERVICE_NOTIFY_CONTEXT; + +typedef struct _PH_SERVICE_QUERY_DATA +{ + SLIST_ENTRY ListEntry; + ULONG Stage; + PPH_SERVICE_ITEM ServiceItem; +} PH_SERVICE_QUERY_DATA, *PPH_SERVICE_QUERY_DATA; + +typedef struct _PH_SERVICE_QUERY_S1_DATA +{ + PH_SERVICE_QUERY_DATA Header; + + PPH_STRING FileName; + HICON SmallIcon; + HICON LargeIcon; +} PH_SERVICE_QUERY_S1_DATA, *PPH_SERVICE_QUERY_S1_DATA; + +typedef struct _PH_SERVICE_QUERY_S2_DATA +{ + PH_SERVICE_QUERY_DATA Header; + + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; +} PH_SERVICE_QUERY_S2_DATA, *PPH_SERVICE_QUERY_S2_DATA; + +VOID NTAPI PhpServiceItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +BOOLEAN NTAPI PhpServiceHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpServiceHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpAddProcessItemService( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ PPH_SERVICE_ITEM ServiceItem + ); + +VOID PhpRemoveProcessItemService( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ PPH_SERVICE_ITEM ServiceItem + ); + +VOID PhpInitializeServiceNonPoll( + VOID + ); + +VOID PhpWorkaroundWindows10ServiceTypeBug( + _Inout_ LPENUM_SERVICE_STATUS_PROCESS ServieEntry + ); + +PPH_OBJECT_TYPE PhServiceItemType = NULL; +PPH_HASHTABLE PhServiceHashtable = NULL; +PH_QUEUED_LOCK PhServiceHashtableLock = PH_QUEUED_LOCK_INIT; + +BOOLEAN PhEnableServiceNonPoll = FALSE; +static BOOLEAN PhpNonPollInitialized = FALSE; +static BOOLEAN PhpNonPollActive = FALSE; +static ULONG PhpNonPollGate = 0; +static HANDLE PhpNonPollEventHandle = NULL; +static PH_QUEUED_LOCK PhpNonPollServiceListLock = PH_QUEUED_LOCK_INIT; +static LIST_ENTRY PhpNonPollServiceListHead = { &PhpNonPollServiceListHead, &PhpNonPollServiceListHead }; +static LIST_ENTRY PhpNonPollServicePendingListHead = { &PhpNonPollServicePendingListHead, &PhpNonPollServicePendingListHead }; +static SLIST_HEADER PhpServiceQueryDataListHead; +static _SubscribeServiceChangeNotifications SubscribeServiceChangeNotifications_I = NULL; +static _UnsubscribeServiceChangeNotifications UnsubscribeServiceChangeNotifications_I = NULL; + +BOOLEAN PhServiceProviderInitialization( + VOID + ) +{ + PhServiceItemType = PhCreateObjectType(L"ServiceItem", 0, PhpServiceItemDeleteProcedure); + PhServiceHashtable = PhCreateHashtable( + sizeof(PPH_SERVICE_ITEM), + PhpServiceHashtableEqualFunction, + PhpServiceHashtableHashFunction, + 40 + ); + + RtlInitializeSListHead(&PhpServiceQueryDataListHead); + + if (WindowsVersion >= WINDOWS_8) + { + SubscribeServiceChangeNotifications_I = PhGetDllProcedureAddress(L"sechost.dll", "SubscribeServiceChangeNotifications", 0); + UnsubscribeServiceChangeNotifications_I = PhGetDllProcedureAddress(L"sechost.dll", "UnsubscribeServiceChangeNotifications", 0); + } + + return TRUE; +} + +PPH_SERVICE_ITEM PhCreateServiceItem( + _In_opt_ LPENUM_SERVICE_STATUS_PROCESS Information + ) +{ + PPH_SERVICE_ITEM serviceItem; + + serviceItem = PhCreateObject( + PhEmGetObjectSize(EmServiceItemType, sizeof(PH_SERVICE_ITEM)), + PhServiceItemType + ); + memset(serviceItem, 0, sizeof(PH_SERVICE_ITEM)); + + if (Information) + { + serviceItem->Name = PhCreateString(Information->lpServiceName); + serviceItem->Key = serviceItem->Name->sr; + serviceItem->DisplayName = PhCreateString(Information->lpDisplayName); + serviceItem->Type = Information->ServiceStatusProcess.dwServiceType; + serviceItem->State = Information->ServiceStatusProcess.dwCurrentState; + serviceItem->ControlsAccepted = Information->ServiceStatusProcess.dwControlsAccepted; + serviceItem->Flags = Information->ServiceStatusProcess.dwServiceFlags; + serviceItem->ProcessId = UlongToHandle(Information->ServiceStatusProcess.dwProcessId); + + if (serviceItem->ProcessId) + PhPrintUInt32(serviceItem->ProcessIdString, HandleToUlong(serviceItem->ProcessId)); + } + + PhEmCallObjectOperation(EmServiceItemType, serviceItem, EmObjectCreate); + + return serviceItem; +} + +VOID PhpServiceItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Object; + + PhEmCallObjectOperation(EmServiceItemType, serviceItem, EmObjectDelete); + + if (serviceItem->Name) PhDereferenceObject(serviceItem->Name); + if (serviceItem->DisplayName) PhDereferenceObject(serviceItem->DisplayName); + if (serviceItem->FileName) PhDereferenceObject(serviceItem->FileName); + if (serviceItem->VerifySignerName) PhDereferenceObject(serviceItem->VerifySignerName); + if (serviceItem->SmallIcon) DestroyIcon(serviceItem->SmallIcon); + if (serviceItem->LargeIcon) DestroyIcon(serviceItem->LargeIcon); + //PhDeleteImageVersionInfo(&serviceItem->VersionInfo); +} + +BOOLEAN PhpServiceHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)Entry1; + PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)Entry2; + + return PhEqualStringRef(&serviceItem1->Key, &serviceItem2->Key, TRUE); +} + +ULONG PhpServiceHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_SERVICE_ITEM serviceItem = *(PPH_SERVICE_ITEM *)Entry; + + return PhHashStringRef(&serviceItem->Key, TRUE); +} + +PPH_SERVICE_ITEM PhpLookupServiceItem( + _In_ PPH_STRINGREF Name + ) +{ + PH_SERVICE_ITEM lookupServiceItem; + PPH_SERVICE_ITEM lookupServiceItemPtr = &lookupServiceItem; + PPH_SERVICE_ITEM *serviceItem; + + // Construct a temporary service item for the lookup. + lookupServiceItem.Key = *Name; + + serviceItem = (PPH_SERVICE_ITEM *)PhFindEntryHashtable( + PhServiceHashtable, + &lookupServiceItemPtr + ); + + if (serviceItem) + return *serviceItem; + else + return NULL; +} + +PPH_SERVICE_ITEM PhReferenceServiceItem( + _In_ PWSTR Name + ) +{ + PPH_SERVICE_ITEM serviceItem; + PH_STRINGREF key; + + PhInitializeStringRef(&key, Name); + + PhAcquireQueuedLockShared(&PhServiceHashtableLock); + + serviceItem = PhpLookupServiceItem(&key); + + if (serviceItem) + PhReferenceObject(serviceItem); + + PhReleaseQueuedLockShared(&PhServiceHashtableLock); + + return serviceItem; +} + +VOID PhpResetServiceNonPollGate( + VOID + ) +{ + if (PhEnableServiceNonPoll) + InterlockedExchange(&PhpNonPollGate, 1); +} + +VOID PhMarkNeedsConfigUpdateServiceItem( + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + ServiceItem->NeedsConfigUpdate = TRUE; + + PhpResetServiceNonPollGate(); +} + +VOID PhpRemoveServiceItem( + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PhRemoveEntryHashtable(PhServiceHashtable, &ServiceItem); + PhDereferenceObject(ServiceItem); +} + +PH_SERVICE_CHANGE PhGetServiceChange( + _In_ PPH_SERVICE_MODIFIED_DATA Data + ) +{ + if ( + ( + Data->OldService.State == SERVICE_STOPPED || + Data->OldService.State == SERVICE_START_PENDING + ) && + Data->Service->State == SERVICE_RUNNING + ) + { + return ServiceStarted; + } + + if ( + ( + Data->OldService.State == SERVICE_PAUSED || + Data->OldService.State == SERVICE_CONTINUE_PENDING + ) && + Data->Service->State == SERVICE_RUNNING + ) + { + return ServiceContinued; + } + + if ( + ( + Data->OldService.State == SERVICE_RUNNING || + Data->OldService.State == SERVICE_PAUSE_PENDING + ) && + Data->Service->State == SERVICE_PAUSED + ) + { + return ServicePaused; + } + + if ( + ( + Data->OldService.State == SERVICE_RUNNING || + Data->OldService.State == SERVICE_STOP_PENDING + ) && + Data->Service->State == SERVICE_STOPPED + ) + { + return ServiceStopped; + } + + return -1; +} + +VOID PhUpdateProcessItemServices( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_SERVICE_ITEM *serviceItem; + + // We don't need to lock as long as the service provider + // never runs concurrently with the process provider. This + // is currently true. + + PhBeginEnumHashtable(PhServiceHashtable, &enumContext); + + while (serviceItem = PhNextEnumHashtable(&enumContext)) + { + if ( + (*serviceItem)->PendingProcess && + (*serviceItem)->ProcessId == ProcessItem->ProcessId + ) + { + PhpAddProcessItemService(ProcessItem, *serviceItem); + } + } +} + +VOID PhpAddProcessItemService( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PhAcquireQueuedLockExclusive(&ProcessItem->ServiceListLock); + + if (!ProcessItem->ServiceList) + ProcessItem->ServiceList = PhCreatePointerList(2); + + if (!PhFindItemPointerList(ProcessItem->ServiceList, ServiceItem)) + { + PhReferenceObject(ServiceItem); + PhAddItemPointerList(ProcessItem->ServiceList, ServiceItem); + } + + PhReleaseQueuedLockExclusive(&ProcessItem->ServiceListLock); + + ServiceItem->PendingProcess = FALSE; + ProcessItem->JustProcessed = 1; +} + +VOID PhpRemoveProcessItemService( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + HANDLE pointerHandle; + + if (!ProcessItem->ServiceList) + return; + + PhAcquireQueuedLockExclusive(&ProcessItem->ServiceListLock); + + if (pointerHandle = PhFindItemPointerList(ProcessItem->ServiceList, ServiceItem)) + { + PhRemoveItemPointerList(ProcessItem->ServiceList, pointerHandle); + PhDereferenceObject(ServiceItem); + } + + PhReleaseQueuedLockExclusive(&ProcessItem->ServiceListLock); + + ProcessItem->JustProcessed = 1; +} + +VOID PhpUpdateServiceItemConfig( + _In_ SC_HANDLE ScManagerHandle, + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + SC_HANDLE serviceHandle; + + serviceHandle = OpenService(ScManagerHandle, ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG); + + if (serviceHandle) + { + LPQUERY_SERVICE_CONFIG config; + SERVICE_DELAYED_AUTO_START_INFO delayedAutoStartInfo; + ULONG returnLength; + PSERVICE_TRIGGER_INFO triggerInfo; + + config = PhGetServiceConfig(serviceHandle); + + if (config) + { + ServiceItem->StartType = config->dwStartType; + ServiceItem->ErrorControl = config->dwErrorControl; + + PhFree(config); + } + + if (QueryServiceConfig2( + serviceHandle, + SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + (BYTE *)&delayedAutoStartInfo, + sizeof(SERVICE_DELAYED_AUTO_START_INFO), + &returnLength + )) + { + ServiceItem->DelayedStart = delayedAutoStartInfo.fDelayedAutostart; + } + else + { + ServiceItem->DelayedStart = FALSE; + } + + if (triggerInfo = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO)) + { + ServiceItem->HasTriggers = triggerInfo->cTriggers != 0; + PhFree(triggerInfo); + } + else + { + ServiceItem->HasTriggers = FALSE; + } + + CloseServiceHandle(serviceHandle); + } +} + +static BOOLEAN PhpCompareServiceNameEntry( + _In_ PPHP_SERVICE_NAME_ENTRY Value1, + _In_ PPHP_SERVICE_NAME_ENTRY Value2 + ) +{ + return PhEqualStringRef(&Value1->Name, &Value2->Name, TRUE); +} + +static ULONG PhpHashServiceNameEntry( + _In_ PPHP_SERVICE_NAME_ENTRY Value + ) +{ + return PhHashStringRef(&Value->Name, TRUE); +} + +VOID PhpServiceQueryStage1( + _Inout_ PPH_SERVICE_QUERY_S1_DATA Data + ) +{ + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; + SC_HANDLE serviceHandle; + + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + Data->FileName = PhGetServiceRelevantFileName(&serviceItem->Name->sr, serviceHandle); + CloseServiceHandle(serviceHandle); + } + + if (Data->FileName) + { + if (!PhExtractIcon(Data->FileName->Buffer, &Data->LargeIcon, &Data->SmallIcon)) + { + Data->LargeIcon = NULL; + Data->SmallIcon = NULL; + } + + // Version info. + //PhInitializeImageVersionInfo(&Data->VersionInfo, Data->FileName->Buffer); + } + + PhpResetServiceNonPollGate(); // HACK (dmex) +} + +VOID PhpServiceQueryStage2( + _Inout_ PPH_SERVICE_QUERY_S2_DATA Data + ) +{ + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; + + if (serviceItem->FileName) + { + Data->VerifyResult = PhVerifyFileCached( + serviceItem->FileName, + NULL, + &Data->VerifySignerName, + FALSE + ); + } + + PhpResetServiceNonPollGate(); // HACK (dmex) +} + +NTSTATUS PhpServiceQueryStage1Worker( + _In_ PVOID Parameter + ) +{ + PPH_SERVICE_QUERY_S1_DATA data; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Parameter; + + data = PhAllocateZero(sizeof(PH_SERVICE_QUERY_S1_DATA)); + data->Header.Stage = 1; + data->Header.ServiceItem = serviceItem; + + PhpServiceQueryStage1(data); + + RtlInterlockedPushEntrySList(&PhpServiceQueryDataListHead, &data->Header.ListEntry); + + return STATUS_SUCCESS; +} + +NTSTATUS PhpServiceQueryStage2Worker( + _In_ PVOID Parameter + ) +{ + PPH_SERVICE_QUERY_S2_DATA data; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)Parameter; + + data = PhAllocateZero(sizeof(PH_SERVICE_QUERY_S2_DATA)); + data->Header.Stage = 2; + data->Header.ServiceItem = serviceItem; + + PhpServiceQueryStage2(data); + + RtlInterlockedPushEntrySList(&PhpServiceQueryDataListHead, &data->Header.ListEntry); + + return STATUS_SUCCESS; +} + +VOID PhpQueueServiceQueryStage1( + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PH_WORK_QUEUE_ENVIRONMENT environment; + + PhReferenceObject(ServiceItem); + + PhInitializeWorkQueueEnvironment(&environment); + environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; + environment.IoPriority = IoPriorityLow; + environment.PagePriority = MEMORY_PRIORITY_LOW; + + PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpServiceQueryStage1Worker, ServiceItem, NULL, &environment); +} + +VOID PhQueueServiceQueryStage2( + _In_ PPH_SERVICE_ITEM ServiceItem + ) +{ + PH_WORK_QUEUE_ENVIRONMENT environment; + + if (!PhEnableServiceQueryStage2) + return; + + PhReferenceObject(ServiceItem); + + PhInitializeWorkQueueEnvironment(&environment); + environment.BasePriority = THREAD_PRIORITY_BELOW_NORMAL; + environment.IoPriority = IoPriorityVeryLow; + environment.PagePriority = MEMORY_PRIORITY_VERY_LOW; + + PhQueueItemWorkQueueEx(PhGetGlobalWorkQueue(), PhpServiceQueryStage2Worker, ServiceItem, NULL, &environment); +} + +VOID PhpFillServiceItemStage1( + _In_ PPH_SERVICE_QUERY_S1_DATA Data + ) +{ + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; + + serviceItem->FileName = Data->FileName; + serviceItem->SmallIcon = Data->SmallIcon; + serviceItem->LargeIcon = Data->LargeIcon; + //memcpy(&processItem->VersionInfo, &Data->VersionInfo, sizeof(PH_IMAGE_VERSION_INFO)); + + // Note: Queue stage 2 processing after filling stage1 process data. + PhQueueServiceQueryStage2(serviceItem); +} + +VOID PhpFillServiceItemStage2( + _In_ PPH_SERVICE_QUERY_S2_DATA Data + ) +{ + PPH_SERVICE_ITEM serviceItem = Data->Header.ServiceItem; + + serviceItem->VerifyResult = Data->VerifyResult; + serviceItem->VerifySignerName = Data->VerifySignerName; +} + +VOID PhFlushServiceQueryData( + VOID + ) +{ + PSLIST_ENTRY entry; + PPH_SERVICE_QUERY_DATA data; + + if (!RtlFirstEntrySList(&PhpServiceQueryDataListHead)) + return; + + entry = RtlInterlockedFlushSList(&PhpServiceQueryDataListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_SERVICE_QUERY_DATA, ListEntry); + entry = entry->Next; + + if (data->Stage == 1) + { + PhpFillServiceItemStage1((PPH_SERVICE_QUERY_S1_DATA)data); + } + else if (data->Stage == 2) + { + PhpFillServiceItemStage2((PPH_SERVICE_QUERY_S2_DATA)data); + } + + data->ServiceItem->JustProcessed = TRUE; + + PhDereferenceObject(data->ServiceItem); + PhFree(data); + } +} + +VOID PhServiceProviderUpdate( + _In_ PVOID Object + ) +{ + static SC_HANDLE scManagerHandle = NULL; + static ULONG runCount = 0; + static PPH_HASH_ENTRY nameHashSet[256]; + static PPHP_SERVICE_NAME_ENTRY nameEntries = NULL; + static ULONG nameEntriesCount; + static ULONG nameEntriesAllocated = 0; + LPENUM_SERVICE_STATUS_PROCESS services; + ULONG numberOfServices; + ULONG i; + PPH_HASH_ENTRY hashEntry; + + // We always execute the first run, and we only initialize non-polling after the first run. + if (PhEnableServiceNonPoll && runCount != 0) + { + if (!PhpNonPollInitialized) + { + PhpInitializeServiceNonPoll(); + PhpNonPollInitialized = TRUE; + } + + if (PhpNonPollActive) + { + if (InterlockedExchange(&PhpNonPollGate, 0) == 0) + { + // Non-poll gate is closed; skip all processing. + goto UpdateEnd; + } + } + } + + if (!scManagerHandle) + { + scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + + if (!scManagerHandle) + return; + } + + services = PhEnumServices(scManagerHandle, 0, 0, &numberOfServices); + + if (!services) + return; + + // Build a hash set containing the service names. + + // This has caused a massive decrease in background CPU usage, and + // is certainly much better than the quadratic-time string comparisons + // we were doing before (in the "Look for dead services" section). (wj32) + + nameEntriesCount = 0; + + if (nameEntriesAllocated < numberOfServices) + { + nameEntriesAllocated = numberOfServices + 32; + + if (nameEntries) PhFree(nameEntries); + nameEntries = PhAllocate(sizeof(PHP_SERVICE_NAME_ENTRY) * nameEntriesAllocated); + } + + PhInitializeHashSet(nameHashSet, PH_HASH_SET_SIZE(nameHashSet)); + + for (i = 0; i < numberOfServices; i++) + { + PPHP_SERVICE_NAME_ENTRY entry; + + entry = &nameEntries[nameEntriesCount++]; + PhInitializeStringRefLongHint(&entry->Name, services[i].lpServiceName); + entry->ServiceEntry = &services[i]; + + if (WindowsVersion >= WINDOWS_10_RS2) + PhpWorkaroundWindows10ServiceTypeBug(entry->ServiceEntry); + + PhAddEntryHashSet( + nameHashSet, + PH_HASH_SET_SIZE(nameHashSet), + &entry->HashEntry, + PhpHashServiceNameEntry(entry) + ); + } + + // Go through the queued services query data. + PhFlushServiceQueryData(); + + // Look for dead services. + { + PPH_LIST servicesToRemove = NULL; + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_SERVICE_ITEM *serviceItem; + + PhBeginEnumHashtable(PhServiceHashtable, &enumContext); + + while (serviceItem = PhNextEnumHashtable(&enumContext)) + { + BOOLEAN found = FALSE; + PHP_SERVICE_NAME_ENTRY lookupNameEntry; + + // Check if the service still exists. + + lookupNameEntry.Name = (*serviceItem)->Name->sr; + hashEntry = PhFindEntryHashSet( + nameHashSet, + PH_HASH_SET_SIZE(nameHashSet), + PhpHashServiceNameEntry(&lookupNameEntry) + ); + + for (; hashEntry; hashEntry = hashEntry->Next) + { + PPHP_SERVICE_NAME_ENTRY nameEntry; + + nameEntry = CONTAINING_RECORD(hashEntry, PHP_SERVICE_NAME_ENTRY, HashEntry); + + if (PhpCompareServiceNameEntry(&lookupNameEntry, nameEntry)) + { + found = TRUE; + break; + } + } + + if (!found) + { + // Remove the service from its process. + if ((*serviceItem)->ProcessId) + { + PPH_PROCESS_ITEM processItem; + + processItem = PhReferenceProcessItem((HANDLE)(*serviceItem)->ProcessId); + + if (processItem) + { + PhpRemoveProcessItemService(processItem, *serviceItem); + PhDereferenceObject(processItem); + } + } + + // Raise the service removed event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderRemovedEvent), *serviceItem); + + if (!servicesToRemove) + servicesToRemove = PhCreateList(2); + + PhAddItemList(servicesToRemove, *serviceItem); + } + } + + if (servicesToRemove) + { + PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); + + for (i = 0; i < servicesToRemove->Count; i++) + { + PhpRemoveServiceItem((PPH_SERVICE_ITEM)servicesToRemove->Items[i]); + } + + PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); + PhDereferenceObject(servicesToRemove); + } + } + + // Look for new services and update existing ones. + for (i = 0; i < PH_HASH_SET_SIZE(nameHashSet); i++) + { + for (hashEntry = nameHashSet[i]; hashEntry; hashEntry = hashEntry->Next) + { + PPH_SERVICE_ITEM serviceItem; + PPHP_SERVICE_NAME_ENTRY nameEntry; + ENUM_SERVICE_STATUS_PROCESS *serviceEntry; + + nameEntry = CONTAINING_RECORD(hashEntry, PHP_SERVICE_NAME_ENTRY, HashEntry); + serviceEntry = nameEntry->ServiceEntry; + serviceItem = PhpLookupServiceItem(&nameEntry->Name); + + if (!serviceItem) + { + // Create the service item and fill in basic information. + + serviceItem = PhCreateServiceItem(serviceEntry); + + PhpUpdateServiceItemConfig(scManagerHandle, serviceItem); + + // Add the service to its process, if appropriate. + if ( + ( + serviceItem->State == SERVICE_RUNNING || + serviceItem->State == SERVICE_PAUSED + ) && + serviceItem->ProcessId + ) + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(serviceItem->ProcessId)) + { + PhpAddProcessItemService(processItem, serviceItem); + PhDereferenceObject(processItem); + } + else + { + // The process doesn't exist yet (to us). Set the pending + // flag and when the process is added this will be + // fixed. (wj32) + serviceItem->PendingProcess = TRUE; + } + } + + // If this is the first run of the provider, queue the + // process query tasks. Otherwise, perform stage 1 + // processing now and queue stage 2 processing. (wj32) + if (runCount > 0) + { + PH_SERVICE_QUERY_S1_DATA data; + + memset(&data, 0, sizeof(PH_SERVICE_QUERY_S1_DATA)); + data.Header.Stage = 1; + data.Header.ServiceItem = serviceItem; + PhpServiceQueryStage1(&data); + PhpFillServiceItemStage1(&data); + } + else + { + PhpQueueServiceQueryStage1(serviceItem); + } + + // Add the service item to the hashtable. + PhAcquireQueuedLockExclusive(&PhServiceHashtableLock); + PhAddEntryHashtable(PhServiceHashtable, &serviceItem); + PhReleaseQueuedLockExclusive(&PhServiceHashtableLock); + + // Raise the service added event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderAddedEvent), serviceItem); + } + else + { + if ( + serviceItem->Type != serviceEntry->ServiceStatusProcess.dwServiceType || + serviceItem->State != serviceEntry->ServiceStatusProcess.dwCurrentState || + serviceItem->ControlsAccepted != serviceEntry->ServiceStatusProcess.dwControlsAccepted || + serviceItem->ProcessId != UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId) || + serviceItem->NeedsConfigUpdate || + serviceItem->JustProcessed + ) + { + PH_SERVICE_MODIFIED_DATA serviceModifiedData; + PH_SERVICE_CHANGE serviceChange; + + // The service has been "modified". + + serviceModifiedData.Service = serviceItem; + memset(&serviceModifiedData.OldService, 0, sizeof(PH_SERVICE_ITEM)); + serviceModifiedData.OldService.Type = serviceItem->Type; + serviceModifiedData.OldService.State = serviceItem->State; + serviceModifiedData.OldService.ControlsAccepted = serviceItem->ControlsAccepted; + serviceModifiedData.OldService.ProcessId = serviceItem->ProcessId; + + // Update the service item. + serviceItem->Type = serviceEntry->ServiceStatusProcess.dwServiceType; + serviceItem->State = serviceEntry->ServiceStatusProcess.dwCurrentState; + serviceItem->ControlsAccepted = serviceEntry->ServiceStatusProcess.dwControlsAccepted; + serviceItem->ProcessId = UlongToHandle(serviceEntry->ServiceStatusProcess.dwProcessId); + + if (serviceItem->ProcessId) + PhPrintUInt32(serviceItem->ProcessIdString, HandleToUlong(serviceItem->ProcessId)); + else + serviceItem->ProcessIdString[0] = UNICODE_NULL; + + // Add/remove the service from its process. + + serviceChange = PhGetServiceChange(&serviceModifiedData); + + if ( + (serviceChange == ServiceStarted && serviceItem->ProcessId) || + (serviceChange == ServiceStopped && serviceModifiedData.OldService.ProcessId) + ) + { + PPH_PROCESS_ITEM processItem; + + if (serviceChange == ServiceStarted) + processItem = PhReferenceProcessItem(serviceItem->ProcessId); + else + processItem = PhReferenceProcessItem(serviceModifiedData.OldService.ProcessId); + + if (processItem) + { + if (serviceChange == ServiceStarted) + PhpAddProcessItemService(processItem, serviceItem); + else + PhpRemoveProcessItemService(processItem, serviceItem); + + PhDereferenceObject(processItem); + } + else + { + if (serviceChange == ServiceStarted) + serviceItem->PendingProcess = TRUE; + else + serviceItem->PendingProcess = FALSE; + } + } + else if ( + serviceItem->State == SERVICE_RUNNING && + serviceItem->ProcessId != serviceModifiedData.OldService.ProcessId && + serviceItem->ProcessId + ) + { + PPH_PROCESS_ITEM processItem; + + // The service stopped and started, and the only change we have detected + // is in the process ID. (wj32) + + if (processItem = PhReferenceProcessItem(serviceModifiedData.OldService.ProcessId)) + { + PhpRemoveProcessItemService(processItem, serviceItem); + PhDereferenceObject(processItem); + } + + if (processItem = PhReferenceProcessItem(serviceItem->ProcessId)) + { + PhpAddProcessItemService(processItem, serviceItem); + PhDereferenceObject(processItem); + } + else + { + serviceItem->PendingProcess = TRUE; + } + } + + // Do a config update if necessary. + if (serviceItem->NeedsConfigUpdate) + { + PhpUpdateServiceItemConfig(scManagerHandle, serviceItem); + serviceItem->NeedsConfigUpdate = FALSE; + } + + if (serviceItem->JustProcessed) // HACK (dmex) + serviceItem->JustProcessed = FALSE; + + // Raise the service modified event. + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderModifiedEvent), &serviceModifiedData); + } + } + } + } + + PhFree(services); + +UpdateEnd: + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackServiceProviderUpdatedEvent), NULL); + runCount++; +} + +VOID CALLBACK PhpServiceNonPollScNotifyCallback( + _In_ PVOID pParameter + ) +{ + PSERVICE_NOTIFY notifyBuffer = pParameter; + PPHP_SERVICE_NOTIFY_CONTEXT notifyContext = notifyBuffer->pContext; + + if (notifyBuffer->dwNotificationStatus == ERROR_SUCCESS) + { + if ((notifyBuffer->dwNotificationTriggered & (SERVICE_NOTIFY_CREATED | SERVICE_NOTIFY_DELETED)) && + notifyBuffer->pszServiceNames) + { + PWSTR name; + SIZE_T nameLength; + + name = notifyBuffer->pszServiceNames; + + while (TRUE) + { + nameLength = PhCountStringZ(name); + + if (nameLength == 0) + break; + + if (name[0] == '/') + { + PPHP_SERVICE_NOTIFY_CONTEXT newNotifyContext; + + // Service creation + newNotifyContext = PhAllocateZero(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); + newNotifyContext->State = SnAdding; + newNotifyContext->ServiceName = PhCreateString(name + 1); + InsertTailList(&PhpNonPollServicePendingListHead, &newNotifyContext->ListEntry); + } + + name += nameLength + 1; + } + + LocalFree(notifyBuffer->pszServiceNames); + } + + notifyContext->State = SnNotify; + RemoveEntryList(¬ifyContext->ListEntry); + InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); + } + else if (notifyBuffer->dwNotificationStatus == ERROR_SERVICE_MARKED_FOR_DELETE) + { + if (!notifyContext->IsServiceManager) + { + notifyContext->State = SnRemoving; + RemoveEntryList(¬ifyContext->ListEntry); + InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); + } + } + else + { + notifyContext->State = SnNotify; + RemoveEntryList(¬ifyContext->ListEntry); + InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); + } + + PhpNonPollGate = 1; + NtSetEvent(PhpNonPollEventHandle, NULL); +} + +VOID PhpDestroyServiceNotifyContext( + _In_ PPHP_SERVICE_NOTIFY_CONTEXT NotifyContext + ) +{ + if (UnsubscribeServiceChangeNotifications_I && NotifyContext->NotifyRegistration) + UnsubscribeServiceChangeNotifications_I(NotifyContext->NotifyRegistration); + + if (NotifyContext->Buffer.pszServiceNames) + LocalFree(NotifyContext->Buffer.pszServiceNames); + + CloseServiceHandle(NotifyContext->ServiceHandle); + PhClearReference(&NotifyContext->ServiceName); + PhFree(NotifyContext); +} + +VOID CALLBACK PhpServicePropertyChangeNotifyCallback( + _In_ ULONG ServiceNotifyFlags, + _In_opt_ PVOID Context + ) +{ + PPHP_SERVICE_NOTIFY_CONTEXT notifyContext = Context; + PPH_SERVICE_ITEM serviceItem; + + // Note: Ignore deleted nofications since we handle this elsewhere and our + // notify context gets destroyed before services.exe invokes this callback. (dmex) + if (ServiceNotifyFlags == SERVICE_NOTIFY_DELETED) + return; + + if (notifyContext->JustAddedNotifyRegistration) + { + notifyContext->JustAddedNotifyRegistration = FALSE; + return; + } + + if (!PhIsNullOrEmptyString(notifyContext->ServiceName)) + { + if (serviceItem = PhReferenceServiceItem(notifyContext->ServiceName->Buffer)) + { + PhMarkNeedsConfigUpdateServiceItem(serviceItem); + + PhDereferenceObject(serviceItem); + } + } +} + +NTSTATUS PhpServiceNonPollThreadStart( + _In_ PVOID Parameter + ) +{ + ULONG result; + SC_HANDLE scManagerHandle; + LPENUM_SERVICE_STATUS_PROCESS services; + ULONG numberOfServices; + ULONG i; + PLIST_ENTRY listEntry; + PPHP_SERVICE_NOTIFY_CONTEXT notifyContext; + + if (!NT_SUCCESS(NtCreateEvent(&PhpNonPollEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) + { + PhpNonPollActive = FALSE; + PhpNonPollGate = 1; + return STATUS_UNSUCCESSFUL; + } + + while (TRUE) + { + scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + + if (!scManagerHandle) + goto ErrorExit; + + if (!(services = PhEnumServices(scManagerHandle, 0, 0, &numberOfServices))) + goto ErrorExit; + + for (i = 0; i < numberOfServices; i++) + { + SC_HANDLE serviceHandle; + + if (!(serviceHandle = OpenService(scManagerHandle, services[i].lpServiceName, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG))) + serviceHandle = OpenService(scManagerHandle, services[i].lpServiceName, SERVICE_QUERY_STATUS); + + if (serviceHandle) + { + notifyContext = PhAllocateZero(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); + notifyContext->ServiceHandle = serviceHandle; + notifyContext->State = SnNotify; + notifyContext->ServiceName = PhCreateString(services[i].lpServiceName); + InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); + } + } + + PhFree(services); + + notifyContext = PhAllocateZero(sizeof(PHP_SERVICE_NOTIFY_CONTEXT)); + notifyContext->ServiceHandle = scManagerHandle; + notifyContext->IsServiceManager = TRUE; + notifyContext->State = SnNotify; + InsertTailList(&PhpNonPollServicePendingListHead, ¬ifyContext->ListEntry); + + while (TRUE) + { + BOOLEAN lagging = FALSE; + + listEntry = PhpNonPollServicePendingListHead.Flink; + + while (listEntry != &PhpNonPollServicePendingListHead) + { + notifyContext = CONTAINING_RECORD(listEntry, PHP_SERVICE_NOTIFY_CONTEXT, ListEntry); + listEntry = listEntry->Flink; + + switch (notifyContext->State) + { + case SnRemoving: + RemoveEntryList(¬ifyContext->ListEntry); + PhpDestroyServiceNotifyContext(notifyContext); + break; + case SnAdding: + { + notifyContext->ServiceHandle = OpenService( + scManagerHandle, + notifyContext->ServiceName->Buffer, + SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG + ); + + if (!notifyContext->ServiceHandle) + { + notifyContext->ServiceHandle = OpenService( + scManagerHandle, + notifyContext->ServiceName->Buffer, + SERVICE_QUERY_STATUS + ); + } + + if (!notifyContext->ServiceHandle) + { + RemoveEntryList(¬ifyContext->ListEntry); + PhpDestroyServiceNotifyContext(notifyContext); + continue; + } + + notifyContext->State = SnNotify; + } + __fallthrough; + case SnNotify: + { + if (notifyContext->NotifyRegistration) + { + if (UnsubscribeServiceChangeNotifications_I && notifyContext->NotifyRegistration) + UnsubscribeServiceChangeNotifications_I(notifyContext->NotifyRegistration); + + notifyContext->JustAddedNotifyRegistration = FALSE; + notifyContext->NotifyRegistration = NULL; + } + + if (SubscribeServiceChangeNotifications_I && !notifyContext->IsServiceManager) + { + PSC_NOTIFICATION_REGISTRATION serviceNotifyRegistration; + + if (SubscribeServiceChangeNotifications_I( + notifyContext->ServiceHandle, + SC_EVENT_PROPERTY_CHANGE, // TODO: SC_EVENT_STATUS_CHANGE (dmex) + PhpServicePropertyChangeNotifyCallback, + notifyContext, + &serviceNotifyRegistration + ) == ERROR_SUCCESS) + { + notifyContext->JustAddedNotifyRegistration = TRUE; + notifyContext->NotifyRegistration = serviceNotifyRegistration; + } + } + + memset(¬ifyContext->Buffer, 0, sizeof(SERVICE_NOTIFY)); + notifyContext->Buffer.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; + notifyContext->Buffer.pfnNotifyCallback = PhpServiceNonPollScNotifyCallback; + notifyContext->Buffer.pContext = notifyContext; + + result = NotifyServiceStatusChange( + notifyContext->ServiceHandle, + notifyContext->IsServiceManager + ? (SERVICE_NOTIFY_CREATED | SERVICE_NOTIFY_DELETED) + : (SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_START_PENDING | SERVICE_NOTIFY_STOP_PENDING | + SERVICE_NOTIFY_RUNNING | SERVICE_NOTIFY_CONTINUE_PENDING | SERVICE_NOTIFY_PAUSE_PENDING | + SERVICE_NOTIFY_PAUSED | SERVICE_NOTIFY_DELETE_PENDING), + ¬ifyContext->Buffer + ); + + switch (result) + { + case ERROR_SUCCESS: + notifyContext->State = SnNone; + RemoveEntryList(¬ifyContext->ListEntry); + InsertTailList(&PhpNonPollServiceListHead, ¬ifyContext->ListEntry); + break; + case ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: + // We are lagging behind. Re-open the handle to the SCM as soon as possible. (wj32) + lagging = TRUE; + break; + case ERROR_SERVICE_MARKED_FOR_DELETE: + default: + RemoveEntryList(¬ifyContext->ListEntry); + PhpDestroyServiceNotifyContext(notifyContext); + break; + } + } + break; + } + } + + while (NtWaitForSingleObject(PhpNonPollEventHandle, TRUE, NULL) != STATUS_WAIT_0) + NOTHING; + + if (lagging) + break; + } + + // Execute all pending callbacks. + NtTestAlert(); + + listEntry = PhpNonPollServiceListHead.Flink; + + while (listEntry != &PhpNonPollServiceListHead) + { + notifyContext = CONTAINING_RECORD(listEntry, PHP_SERVICE_NOTIFY_CONTEXT, ListEntry); + listEntry = listEntry->Flink; + PhpDestroyServiceNotifyContext(notifyContext); + } + + listEntry = PhpNonPollServicePendingListHead.Flink; + + while (listEntry != &PhpNonPollServicePendingListHead) + { + notifyContext = CONTAINING_RECORD(listEntry, PHP_SERVICE_NOTIFY_CONTEXT, ListEntry); + listEntry = listEntry->Flink; + PhpDestroyServiceNotifyContext(notifyContext); + } + + CloseServiceHandle(scManagerHandle); + } + + NtClose(PhpNonPollEventHandle); + + return STATUS_SUCCESS; + +ErrorExit: + PhpNonPollActive = FALSE; + PhpNonPollGate = 1; + NtClose(PhpNonPollEventHandle); + return STATUS_UNSUCCESSFUL; +} + +VOID PhpInitializeServiceNonPoll( + VOID + ) +{ + PhpNonPollActive = TRUE; + PhpNonPollGate = 1; // initially the gate should be open since we only just initialized everything (wj32) + + PhCreateThread2(PhpServiceNonPollThreadStart, NULL); +} + +VOID PhpWorkaroundWindows10ServiceTypeBug( + _Inout_ LPENUM_SERVICE_STATUS_PROCESS ServieEntry + ) +{ + // https://github.com/processhacker2/processhacker/issues/120 (dmex) + if (ServieEntry->ServiceStatusProcess.dwServiceType == SERVICE_WIN32) + ServieEntry->ServiceStatusProcess.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + if (ServieEntry->ServiceStatusProcess.dwServiceType == (SERVICE_WIN32 | SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE)) + ServieEntry->ServiceStatusProcess.dwServiceType = SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE; +} diff --git a/ProcessHacker/sysinfo.c b/ProcessHacker/sysinfo.c index 709a126b2f5c..91afaab549db 100644 --- a/ProcessHacker/sysinfo.c +++ b/ProcessHacker/sysinfo.c @@ -1,2054 +1,2290 @@ -/* - * Process Hacker - - * System Information window - * - * Copyright (C) 2011-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This file contains the System Information framework. The "framework" handles creation, layout and - * events for the top-level window itself. It manages the list of sections. - * - * A section is an object that provides information to the user about a type of system resource. The - * CPU, Memory and I/O sections are added automatically to every System Information window. Plugins - * can also add sections to the window. There are two views: summary and section. In summary view, - * rows of graphs are displayed in the window, one graph for each section. The section is - * responsible for providing the graph data and any text to draw on the left-hand side of the graph. - * In section view, the graphs become mini-graphs on the left-hand side of the window. The section - * displays its own embedded dialog in the remaining space. Any controls contained in this dialog, - * including graphs, are the responsibility of the section. - * - * Users can enter section view by: - * * Clicking on a graph or mini-graph. - * * Pressing a number from 1 to 9. - * * Using the tab or arrow keys to select a graph and pressing space or enter. - * - * Users can return to summary view by: - * * Clicking "Back" on the left-hand side of the window. - * * Pressing Backspace. - * * Using the tab or arrow keys to select "Back" and pressing space or enter. - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -static HANDLE PhSipThread = NULL; -HWND PhSipWindow = NULL; -static PPH_LIST PhSipDialogList = NULL; -static PH_EVENT InitializedEvent = PH_EVENT_INIT; -static PWSTR InitialSectionName; -static PH_LAYOUT_MANAGER WindowLayoutManager; -static RECT MinimumSize; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - -static PPH_LIST SectionList; -static PH_SYSINFO_PARAMETERS CurrentParameters; -static PH_SYSINFO_VIEW_TYPE CurrentView; -static PPH_SYSINFO_SECTION CurrentSection; -static HWND ContainerControl; -static HWND SeparatorControl; -static HWND RestoreSummaryControl; -static BOOLEAN RestoreSummaryControlHot; -static BOOLEAN RestoreSummaryControlHasFocus; - -static HTHEME ThemeData; -static BOOLEAN ThemeHasItemBackground; - -static BOOLEAN AlwaysOnTop; - -VOID PhShowSystemInformationDialog( - _In_opt_ PWSTR SectionName - ) -{ - InitialSectionName = SectionName; - - if (!PhSipWindow) - { - if (!(PhSipThread = PhCreateThread(0, PhSipSysInfoThreadStart, NULL))) - { - PhShowStatus(PhMainWndHandle, L"Unable to create the system information window", 0, GetLastError()); - return; - } - - PhWaitForEvent(&InitializedEvent, NULL); - } - - SendMessage(PhSipWindow, SI_MSG_SYSINFO_ACTIVATE, (WPARAM)SectionName, 0); -} - -NTSTATUS PhSipSysInfoThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - BOOL result; - MSG message; - HACCEL acceleratorTable; - BOOLEAN processed; - - PhInitializeAutoPool(&autoPool); - - PhSipWindow = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_SYSINFO), - NULL, - PhSipSysInfoDialogProc - ); - - PhSetEvent(&InitializedEvent); - - acceleratorTable = LoadAccelerators(PhInstanceHandle, MAKEINTRESOURCE(IDR_SYSINFO_ACCEL)); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - processed = FALSE; - - if ( - message.hwnd == PhSipWindow || - IsChild(PhSipWindow, message.hwnd) - ) - { - if (TranslateAccelerator(PhSipWindow, acceleratorTable, &message)) - processed = TRUE; - } - - if (!processed && PhSipDialogList) - { - ULONG i; - - for (i = 0; i < PhSipDialogList->Count; i++) - { - if (IsDialogMessage((HWND)PhSipDialogList->Items[i], &message)) - { - processed = TRUE; - break; - } - } - } - - if (!processed) - { - if (!IsDialogMessage(PhSipWindow, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - } - - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); - PhResetEvent(&InitializedEvent); - NtClose(PhSipThread); - - PhSipWindow = NULL; - PhSipThread = NULL; - - if (PhSipDialogList) - { - PhDereferenceObject(PhSipDialogList); - PhSipDialogList = NULL; - } - - return STATUS_SUCCESS; -} - -INT_PTR CALLBACK PhSipSysInfoDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhSipWindow = hwndDlg; - PhSipOnInitDialog(); - } - break; - case WM_DESTROY: - { - PhSipOnDestroy(); - } - break; - case WM_NCDESTROY: - { - PhSipOnNcDestroy(); - } - break; - case WM_SHOWWINDOW: - { - PhSipOnShowWindow(!!wParam, (ULONG)lParam); - } - break; - case WM_SYSCOMMAND: - { - if (PhSipOnSysCommand((ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))) - return 0; - } - break; - case WM_SIZE: - { - PhSipOnSize(); - } - break; - case WM_SIZING: - { - PhSipOnSizing((ULONG)wParam, (PRECT)lParam); - } - break; - case WM_THEMECHANGED: - { - PhSipOnThemeChanged(); - } - break; - case WM_COMMAND: - { - PhSipOnCommand(LOWORD(wParam), HIWORD(wParam)); - } - break; - case WM_NOTIFY: - { - LRESULT result; - - if (PhSipOnNotify((NMHDR *)lParam, &result)) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, result); - return TRUE; - } - } - break; - case WM_DRAWITEM: - { - if (PhSipOnDrawItem(wParam, (DRAWITEMSTRUCT *)lParam)) - return TRUE; - } - break; - } - - if (uMsg >= SI_MSG_SYSINFO_FIRST && uMsg <= SI_MSG_SYSINFO_LAST) - { - PhSipOnUserMessage(uMsg, wParam, lParam); - } - - return FALSE; -} - -INT_PTR CALLBACK PhSipContainerDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return FALSE; -} - -VOID PhSipOnInitDialog( - VOID - ) -{ - PhInitializeLayoutManager(&WindowLayoutManager, PhSipWindow); - - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhSipWindow, IDC_INSTRUCTION), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhSipWindow, IDC_ALWAYSONTOP), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(PhSipWindow, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - PhSipSysInfoUpdateHandler, - NULL, - &ProcessesUpdatedRegistration - ); - - ContainerControl = CreateDialog( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_CONTAINER), - PhSipWindow, - PhSipContainerDialogProc - ); - - PhSetControlTheme(PhSipWindow, L"explorer"); - PhSipUpdateThemeData(); -} - -VOID PhSipOnDestroy( - VOID - ) -{ - PhUnregisterCallback( - &PhProcessesUpdatedEvent, - &ProcessesUpdatedRegistration - ); - - if (CurrentSection) - PhSetStringSetting2(L"SysInfoWindowSection", &CurrentSection->Name); - else - PhSetStringSetting(L"SysInfoWindowSection", L""); - - PhSetIntegerSetting(L"SysInfoWindowAlwaysOnTop", AlwaysOnTop); - - PhSaveWindowPlacementToSetting(L"SysInfoWindowPosition", L"SysInfoWindowSize", PhSipWindow); - PhSipSaveWindowState(); -} - -VOID PhSipOnNcDestroy( - VOID - ) -{ - ULONG i; - PPH_SYSINFO_SECTION section; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - PhSipDestroySection(section); - } - - PhDereferenceObject(SectionList); - SectionList = NULL; - PhSipDeleteParameters(); - - if (ThemeData) - { - CloseThemeData(ThemeData); - ThemeData = NULL; - } - - PhDeleteLayoutManager(&WindowLayoutManager); - PostQuitMessage(0); -} - -VOID PhSipOnShowWindow( - _In_ BOOLEAN Showing, - _In_ ULONG State - ) -{ - RECT buttonRect; - RECT clientRect; - PH_STRINGREF sectionName; - PPH_SYSINFO_SECTION section; - - if (SectionList) - return; - - SectionList = PhCreateList(8); - PhSipInitializeParameters(); - CurrentView = SysInfoSummaryView; - CurrentSection = NULL; - - PhSipCreateInternalSection(L"CPU", 0, PhSipCpuSectionCallback); - PhSipCreateInternalSection(L"Memory", 0, PhSipMemorySectionCallback); - PhSipCreateInternalSection(L"I/O", 0, PhSipIoSectionCallback); - - if (PhPluginsEnabled) - { - PH_PLUGIN_SYSINFO_POINTERS pointers; - - pointers.WindowHandle = PhSipWindow; - pointers.CreateSection = PhSipCreateSection; - pointers.FindSection = PhSipFindSection; - pointers.EnterSectionView = PhSipEnterSectionView; - pointers.RestoreSummaryView = PhSipRestoreSummaryView; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackSystemInformationInitializing), &pointers); - } - - SeparatorControl = CreateWindow( - L"STATIC", - NULL, - WS_CHILD | SS_OWNERDRAW, - 0, - 0, - 3, - 3, - PhSipWindow, - (HMENU)IDC_SEPARATOR, - PhInstanceHandle, - NULL - ); - - RestoreSummaryControl = CreateWindow( - L"STATIC", - NULL, - WS_CHILD | WS_TABSTOP | SS_OWNERDRAW | SS_NOTIFY, - 0, - 0, - 3, - 3, - PhSipWindow, - (HMENU)IDC_RESET, - PhInstanceHandle, - NULL - ); - - SetWindowSubclass(RestoreSummaryControl, PhSipPanelHookWndProc, 0, 0); - RestoreSummaryControlHot = FALSE; - - EnableThemeDialogTexture(ContainerControl, ETDT_ENABLETAB); - - GetWindowRect(GetDlgItem(PhSipWindow, IDOK), &buttonRect); - MapWindowPoints(NULL, PhSipWindow, (POINT *)&buttonRect, 2); - GetClientRect(PhSipWindow, &clientRect); - - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = WindowsVersion >= WINDOWS_VISTA ? 430 : 370; // XP doesn't have the Memory Lists group - MinimumSize.bottom = 290; - MapDialogRect(PhSipWindow, &MinimumSize); - - MinimumSize.right += CurrentParameters.PanelWidth; - MinimumSize.right += GetSystemMetrics(SM_CXFRAME) * 2; - MinimumSize.bottom += GetSystemMetrics(SM_CYFRAME) * 2; - - if (SectionList->Count != 0) - { - ULONG newMinimumHeight; - - newMinimumHeight = - GetSystemMetrics(SM_CYCAPTION) + - CurrentParameters.WindowPadding + - CurrentParameters.MinimumGraphHeight * SectionList->Count + - CurrentParameters.MinimumGraphHeight + // Back button - CurrentParameters.GraphPadding * SectionList->Count + - CurrentParameters.WindowPadding + - clientRect.bottom - buttonRect.top + - GetSystemMetrics(SM_CYFRAME) * 2; - - if (newMinimumHeight > (ULONG)MinimumSize.bottom) - MinimumSize.bottom = newMinimumHeight; - } - - PhLoadWindowPlacementFromSetting(L"SysInfoWindowPosition", L"SysInfoWindowSize", PhSipWindow); - - if (PhGetIntegerSetting(L"SysInfoWindowState") == SW_MAXIMIZE) - ShowWindow(PhSipWindow, SW_MAXIMIZE); - - if (InitialSectionName) - PhInitializeStringRefLongHint(§ionName, InitialSectionName); - else - sectionName = PhaGetStringSetting(L"SysInfoWindowSection")->sr; - - if (sectionName.Length != 0 && (section = PhSipFindSection(§ionName))) - { - PhSipEnterSectionView(section); - } - - AlwaysOnTop = (BOOLEAN)PhGetIntegerSetting(L"SysInfoWindowAlwaysOnTop"); - Button_SetCheck(GetDlgItem(PhSipWindow, IDC_ALWAYSONTOP), AlwaysOnTop ? BST_CHECKED : BST_UNCHECKED); - PhSipSetAlwaysOnTop(); - - PhSipOnSize(); - PhSipOnUserMessage(SI_MSG_SYSINFO_UPDATE, 0, 0); -} - -BOOLEAN PhSipOnSysCommand( - _In_ ULONG Type, - _In_ LONG CursorScreenX, - _In_ LONG CursorScreenY - ) -{ - switch (Type) - { - case SC_MINIMIZE: - { - // Save the current window state because we may not have a chance to later. - PhSipSaveWindowState(); - } - break; - } - - return FALSE; -} - -VOID PhSipOnSize( - VOID - ) -{ - PhLayoutManagerLayout(&WindowLayoutManager); - - if (SectionList && SectionList->Count != 0) - { - if (CurrentView == SysInfoSummaryView) - PhSipLayoutSummaryView(); - else if (CurrentView == SysInfoSectionView) - PhSipLayoutSectionView(); - } -} - -VOID PhSipOnSizing( - _In_ ULONG Edge, - _In_ PRECT DragRectangle - ) -{ - PhResizingMinimumSize(DragRectangle, Edge, MinimumSize.right, MinimumSize.bottom); -} - -VOID PhSipOnThemeChanged( - VOID - ) -{ - PhSipUpdateThemeData(); -} - -VOID PhSipOnCommand( - _In_ ULONG Id, - _In_ ULONG Code - ) -{ - switch (Id) - { - case IDCANCEL: - case IDOK: - DestroyWindow(PhSipWindow); - break; - case IDC_ALWAYSONTOP: - { - AlwaysOnTop = Button_GetCheck(GetDlgItem(PhSipWindow, IDC_ALWAYSONTOP)) == BST_CHECKED; - PhSipSetAlwaysOnTop(); - } - break; - case IDC_RESET: - { - if (Code == STN_CLICKED) - { - PhSipRestoreSummaryView(); - } - } - break; - case IDC_BACK: - { - PhSipRestoreSummaryView(); - } - break; - case IDC_REFRESH: - { - ProcessHacker_Refresh(PhMainWndHandle); - } - break; - case IDC_PAUSE: - { - ProcessHacker_SetUpdateAutomatically(PhMainWndHandle, !ProcessHacker_GetUpdateAutomatically(PhMainWndHandle)); - } - break; - case IDC_MAXSCREEN: - { - static WINDOWPLACEMENT windowLayout = { sizeof(WINDOWPLACEMENT) }; - ULONG windowStyle = (ULONG)GetWindowLongPtr(PhSipWindow, GWL_STYLE); - - if (windowStyle & WS_OVERLAPPEDWINDOW) - { - MONITORINFO info = { sizeof(MONITORINFO) }; - - if ( - GetWindowPlacement(PhSipWindow, &windowLayout) && - GetMonitorInfo(MonitorFromWindow(PhSipWindow, MONITOR_DEFAULTTOPRIMARY), &info) - ) - { - ULONG padding = CurrentParameters.WindowPadding / 2; - PhSetWindowStyle(PhSipWindow, WS_OVERLAPPEDWINDOW, 0); - SetWindowPos( - PhSipWindow, - HWND_TOPMOST, - info.rcMonitor.left - padding, - info.rcMonitor.top - padding, - (info.rcMonitor.right - info.rcMonitor.left) + padding * 2, - (info.rcMonitor.bottom - info.rcMonitor.top) + padding * 2, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED - ); - } - } - else - { - PhSetWindowStyle(PhSipWindow, WS_OVERLAPPEDWINDOW, WS_OVERLAPPEDWINDOW); - SetWindowPlacement(PhSipWindow, &windowLayout); - SetWindowPos(PhSipWindow, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - } - } - break; - } - - if (SectionList && Id >= ID_DIGIT1 && Id <= ID_DIGIT9) - { - ULONG index; - - index = Id - ID_DIGIT1; - - if (index < SectionList->Count) - PhSipEnterSectionView(SectionList->Items[index]); - } - - if (SectionList && Code == STN_CLICKED) - { - ULONG i; - PPH_SYSINFO_SECTION section; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (Id == section->PanelId) - { - PhSipEnterSectionView(section); - break; - } - } - } -} - -BOOLEAN PhSipOnNotify( - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - ULONG i; - PPH_SYSINFO_SECTION section; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (getDrawInfo->Header.hwndFrom == section->GraphHandle) - { - section->Callback(section, SysInfoGraphGetDrawInfo, drawInfo, 0); - - if (CurrentView == SysInfoSectionView) - { - drawInfo->Flags &= ~(PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y); - } - else - { - ULONG badWidth = CurrentParameters.PanelWidth; - - // Try not to draw max data point labels that will get covered by the - // fade-out part of the graph. - if (badWidth < drawInfo->Width) - drawInfo->LabelMaxYIndexLimit = (drawInfo->Width - badWidth) / 2; - else - drawInfo->LabelMaxYIndexLimit = -1; - } - - break; - } - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - ULONG i; - PPH_SYSINFO_SECTION section; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (getTooltipText->Header.hwndFrom == section->GraphHandle) - { - PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT graphGetTooltipText; - - graphGetTooltipText.Index = getTooltipText->Index; - PhInitializeEmptyStringRef(&graphGetTooltipText.Text); - - section->Callback(section, SysInfoGraphGetTooltipText, &graphGetTooltipText, 0); - - getTooltipText->Text = graphGetTooltipText.Text; - - break; - } - } - } - } - break; - case GCN_DRAWPANEL: - { - PPH_GRAPH_DRAWPANEL drawPanel = (PPH_GRAPH_DRAWPANEL)Header; - ULONG i; - PPH_SYSINFO_SECTION section; - - if (CurrentView == SysInfoSummaryView) - { - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (drawPanel->Header.hwndFrom == section->GraphHandle) - { - PhSipDrawPanel(section, drawPanel->hdc, &drawPanel->Rect); - break; - } - } - } - } - break; - case GCN_MOUSEEVENT: - { - PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; - ULONG i; - PPH_SYSINFO_SECTION section; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (mouseEvent->Header.hwndFrom == section->GraphHandle) - { - if (mouseEvent->Message == WM_LBUTTONDOWN) - { - PhSipEnterSectionView(section); - } - - break; - } - } - } - break; - } - - return FALSE; -} - -BOOLEAN PhSipOnDrawItem( - _In_ ULONG_PTR Id, - _In_ DRAWITEMSTRUCT *DrawItemStruct - ) -{ - ULONG i; - PPH_SYSINFO_SECTION section; - - if (Id == IDC_RESET) - { - PhSipDrawRestoreSummaryPanel(DrawItemStruct->hDC, &DrawItemStruct->rcItem); - return TRUE; - } - else if (Id == IDC_SEPARATOR) - { - PhSipDrawSeparator(DrawItemStruct->hDC, &DrawItemStruct->rcItem); - return TRUE; - } - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (Id == section->PanelId) - { - PhSipDrawPanel(section, DrawItemStruct->hDC, &DrawItemStruct->rcItem); - return TRUE; - } - } - - return FALSE; -} - -VOID PhSipOnUserMessage( - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ) -{ - switch (Message) - { - case SI_MSG_SYSINFO_ACTIVATE: - { - PWSTR sectionName = (PWSTR)WParam; - - if (SectionList && sectionName) - { - PH_STRINGREF sectionNameSr; - PPH_SYSINFO_SECTION section; - - PhInitializeStringRefLongHint(§ionNameSr, sectionName); - section = PhSipFindSection(§ionNameSr); - - if (section) - PhSipEnterSectionView(section); - else - PhSipRestoreSummaryView(); - } - - if (IsIconic(PhSipWindow)) - ShowWindow(PhSipWindow, SW_RESTORE); - else - ShowWindow(PhSipWindow, SW_SHOW); - - SetForegroundWindow(PhSipWindow); - } - break; - case SI_MSG_SYSINFO_UPDATE: - { - ULONG i; - PPH_SYSINFO_SECTION section; - - if (SectionList) - { - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - section->Callback(section, SysInfoTick, NULL, NULL); - - section->GraphState.Valid = FALSE; - section->GraphState.TooltipIndex = -1; - Graph_MoveGrid(section->GraphHandle, 1); - Graph_Draw(section->GraphHandle); - Graph_UpdateTooltip(section->GraphHandle); - InvalidateRect(section->GraphHandle, NULL, FALSE); - - InvalidateRect(section->PanelHandle, NULL, FALSE); - } - } - } - break; - case SI_MSG_SYSINFO_CHANGE_SETTINGS: - { - ULONG i; - PPH_SYSINFO_SECTION section; - PH_GRAPH_OPTIONS options; - - if (SectionList) - { - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - Graph_GetOptions(section->GraphHandle, &options); - options.FadeOutBackColor = CurrentParameters.GraphBackColor; - Graph_SetOptions(section->GraphHandle, &options); - } - } - - InvalidateRect(PhSipWindow, NULL, TRUE); - } - break; - } -} - -VOID PhSiNotifyChangeSettings( - VOID - ) -{ - HWND window; - - PhSipUpdateColorParameters(); - - window = PhSipWindow; - - if (window) - PostMessage(window, SI_MSG_SYSINFO_CHANGE_SETTINGS, 0, 0); -} - -VOID PhSiSetColorsGraphDrawInfo( - _Out_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ COLORREF Color1, - _In_ COLORREF Color2 - ) -{ - static PH_QUEUED_LOCK lock = PH_QUEUED_LOCK_INIT; - static ULONG lastDpi = -1; - static HFONT iconTitleFont; - - // Get the appropriate fonts. - - if (DrawInfo->Flags & PH_GRAPH_LABEL_MAX_Y) - { - PhAcquireQueuedLockExclusive(&lock); - - if (lastDpi != PhGlobalDpi) - { - LOGFONT logFont; - - if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) - { - logFont.lfHeight += PhMultiplyDivide(1, PhGlobalDpi, 72); - iconTitleFont = CreateFontIndirect(&logFont); - } - - if (!iconTitleFont) - iconTitleFont = PhApplicationFont; - - lastDpi = PhGlobalDpi; - } - - DrawInfo->LabelYFont = iconTitleFont; - - PhReleaseQueuedLockExclusive(&lock); - } - - DrawInfo->TextFont = PhApplicationFont; - - // Set up the colors. - - switch (PhCsGraphColorMode) - { - case 0: // New colors - DrawInfo->BackColor = RGB(0xef, 0xef, 0xef); - DrawInfo->LineColor1 = PhHalveColorBrightness(Color1); - DrawInfo->LineBackColor1 = PhMakeColorBrighter(Color1, 125); - DrawInfo->LineColor2 = PhHalveColorBrightness(Color2); - DrawInfo->LineBackColor2 = PhMakeColorBrighter(Color2, 125); - DrawInfo->GridColor = RGB(0xc7, 0xc7, 0xc7); - DrawInfo->LabelYColor = RGB(0xa0, 0x60, 0x20); - DrawInfo->TextColor = RGB(0x00, 0x00, 0x00); - DrawInfo->TextBoxColor = RGB(0xe7, 0xe7, 0xe7); - break; - case 1: // Old colors - DrawInfo->BackColor = RGB(0x00, 0x00, 0x00); - DrawInfo->LineColor1 = Color1; - DrawInfo->LineBackColor1 = PhHalveColorBrightness(Color1); - DrawInfo->LineColor2 = Color2; - DrawInfo->LineBackColor2 = PhHalveColorBrightness(Color2); - DrawInfo->GridColor = RGB(0x00, 0x57, 0x00); - DrawInfo->LabelYColor = RGB(0xd0, 0xa0, 0x70); - DrawInfo->TextColor = RGB(0x00, 0xff, 0x00); - DrawInfo->TextBoxColor = RGB(0x00, 0x22, 0x00); - break; - } -} - -PPH_STRING PhSiSizeLabelYFunction( - _In_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ ULONG DataIndex, - _In_ FLOAT Value, - _In_ FLOAT Parameter - ) -{ - ULONG64 size; - - size = (ULONG64)(Value * Parameter); - - if (size != 0) - { - PH_FORMAT format; - - format.Type = SizeFormatType | FormatUsePrecision | FormatUseRadix; - format.Precision = 0; - format.Radix = -1; - format.u.Size = size; - - return PhFormat(&format, 1, 0); - } - else - { - return PhReferenceEmptyString(); - } -} - -VOID PhSipRegisterDialog( - _In_ HWND DialogWindowHandle - ) -{ - if (!PhSipDialogList) - PhSipDialogList = PhCreateList(4); - - PhAddItemList(PhSipDialogList, DialogWindowHandle); -} - -VOID PhSipUnregisterDialog( - _In_ HWND DialogWindowHandle - ) -{ - ULONG index; - - if ((index = PhFindItemList(PhSipDialogList, DialogWindowHandle)) != -1) - PhRemoveItemList(PhSipDialogList, index); -} - -VOID PhSipInitializeParameters( - VOID - ) -{ - LOGFONT logFont; - HDC hdc; - TEXTMETRIC textMetrics; - HFONT originalFont; - - memset(&CurrentParameters, 0, sizeof(PH_SYSINFO_PARAMETERS)); - - CurrentParameters.SysInfoWindowHandle = PhSipWindow; - CurrentParameters.ContainerWindowHandle = ContainerControl; - - if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) - { - CurrentParameters.Font = CreateFontIndirect(&logFont); - } - else - { - CurrentParameters.Font = PhApplicationFont; - GetObject(PhApplicationFont, sizeof(LOGFONT), &logFont); - } - - hdc = GetDC(PhSipWindow); - - logFont.lfHeight -= PhMultiplyDivide(3, GetDeviceCaps(hdc, LOGPIXELSY), 72); - CurrentParameters.MediumFont = CreateFontIndirect(&logFont); - - logFont.lfHeight -= PhMultiplyDivide(3, GetDeviceCaps(hdc, LOGPIXELSY), 72); - CurrentParameters.LargeFont = CreateFontIndirect(&logFont); - - PhSipUpdateColorParameters(); - - CurrentParameters.ColorSetupFunction = PhSiSetColorsGraphDrawInfo; - - originalFont = SelectObject(hdc, CurrentParameters.Font); - GetTextMetrics(hdc, &textMetrics); - CurrentParameters.FontHeight = textMetrics.tmHeight; - CurrentParameters.FontAverageWidth = textMetrics.tmAveCharWidth; - - SelectObject(hdc, CurrentParameters.MediumFont); - GetTextMetrics(hdc, &textMetrics); - CurrentParameters.MediumFontHeight = textMetrics.tmHeight; - CurrentParameters.MediumFontAverageWidth = textMetrics.tmAveCharWidth; - - SelectObject(hdc, originalFont); - - // Internal padding and other values - CurrentParameters.PanelPadding = PH_SCALE_DPI(PH_SYSINFO_PANEL_PADDING); - CurrentParameters.WindowPadding = PH_SCALE_DPI(PH_SYSINFO_WINDOW_PADDING); - CurrentParameters.GraphPadding = PH_SCALE_DPI(PH_SYSINFO_GRAPH_PADDING); - CurrentParameters.SmallGraphWidth = PH_SCALE_DPI(PH_SYSINFO_SMALL_GRAPH_WIDTH); - CurrentParameters.SmallGraphPadding = PH_SCALE_DPI(PH_SYSINFO_SMALL_GRAPH_PADDING); - CurrentParameters.SeparatorWidth = PH_SCALE_DPI(PH_SYSINFO_SEPARATOR_WIDTH); - CurrentParameters.CpuPadding = PH_SCALE_DPI(PH_SYSINFO_CPU_PADDING); - CurrentParameters.MemoryPadding = PH_SCALE_DPI(PH_SYSINFO_MEMORY_PADDING); - - CurrentParameters.MinimumGraphHeight = - CurrentParameters.PanelPadding + - CurrentParameters.MediumFontHeight + - CurrentParameters.PanelPadding; - - CurrentParameters.SectionViewGraphHeight = - CurrentParameters.PanelPadding + - CurrentParameters.MediumFontHeight + - CurrentParameters.PanelPadding + - CurrentParameters.FontHeight + - 2 + - CurrentParameters.FontHeight + - CurrentParameters.PanelPadding + - 2; - - CurrentParameters.PanelWidth = CurrentParameters.MediumFontAverageWidth * 10; - - ReleaseDC(PhSipWindow, hdc); -} - -VOID PhSipDeleteParameters( - VOID - ) -{ - if (CurrentParameters.Font) - DeleteObject(CurrentParameters.Font); - if (CurrentParameters.MediumFont) - DeleteObject(CurrentParameters.MediumFont); - if (CurrentParameters.LargeFont) - DeleteObject(CurrentParameters.LargeFont); -} - -VOID PhSipUpdateColorParameters( - VOID - ) -{ - switch (PhCsGraphColorMode) - { - case 0: // New colors - CurrentParameters.GraphBackColor = RGB(0xef, 0xef, 0xef); - CurrentParameters.PanelForeColor = RGB(0x00, 0x00, 0x00); - break; - case 1: // Old colors - CurrentParameters.GraphBackColor = RGB(0x00, 0x00, 0x00); - CurrentParameters.PanelForeColor = RGB(0xff, 0xff, 0xff); - break; - } -} - -PPH_SYSINFO_SECTION PhSipCreateSection( - _In_ PPH_SYSINFO_SECTION Template - ) -{ - PPH_SYSINFO_SECTION section; - PH_GRAPH_OPTIONS options; - - section = PhAllocate(sizeof(PH_SYSINFO_SECTION)); - memset(section, 0, sizeof(PH_SYSINFO_SECTION)); - - section->Name = Template->Name; - section->Flags = Template->Flags; - section->Callback = Template->Callback; - section->Context = Template->Context; - - section->GraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP | GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL, - 0, - 0, - 3, - 3, - PhSipWindow, - NULL, - PhInstanceHandle, - NULL - ); - PhInitializeGraphState(§ion->GraphState); - section->Parameters = &CurrentParameters; - - Graph_GetOptions(section->GraphHandle, &options); - options.FadeOutBackColor = CurrentParameters.GraphBackColor; - options.FadeOutWidth = CurrentParameters.PanelWidth + PH_SYSINFO_FADE_ADD; - options.DefaultCursor = LoadCursor(NULL, IDC_HAND); - Graph_SetOptions(section->GraphHandle, &options); - Graph_SetTooltip(section->GraphHandle, TRUE); - - section->PanelId = IDDYNAMIC + SectionList->Count * 2 + 2; - section->PanelHandle = CreateWindow( - L"STATIC", - NULL, - WS_CHILD | SS_OWNERDRAW | SS_NOTIFY, - 0, - 0, - 3, - 3, - PhSipWindow, - (HMENU)(ULONG_PTR)section->PanelId, - PhInstanceHandle, - NULL - ); - - SetWindowSubclass(section->GraphHandle, PhSipGraphHookWndProc, 0, (ULONG_PTR)section); - SetWindowSubclass(section->PanelHandle, PhSipPanelHookWndProc, 0, (ULONG_PTR)section); - - PhAddItemList(SectionList, section); - - section->Callback(section, SysInfoCreate, NULL, NULL); - - return section; -} - -VOID PhSipDestroySection( - _In_ PPH_SYSINFO_SECTION Section - ) -{ - Section->Callback(Section, SysInfoDestroy, NULL, NULL); - - PhDeleteGraphState(&Section->GraphState); - PhFree(Section); -} - -PPH_SYSINFO_SECTION PhSipFindSection( - _In_ PPH_STRINGREF Name - ) -{ - ULONG i; - PPH_SYSINFO_SECTION section; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (PhEqualStringRef(§ion->Name, Name, TRUE)) - return section; - } - - return NULL; -} - -PPH_SYSINFO_SECTION PhSipCreateInternalSection( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_ PPH_SYSINFO_SECTION_CALLBACK Callback - ) -{ - PH_SYSINFO_SECTION section; - - memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); - PhInitializeStringRef(§ion.Name, Name); - section.Flags = Flags; - section.Callback = Callback; - - return PhSipCreateSection(§ion); -} - -VOID PhSipDrawRestoreSummaryPanel( - _In_ HDC hdc, - _In_ PRECT Rect - ) -{ - FillRect(hdc, Rect, GetSysColorBrush(COLOR_3DFACE)); - - if (RestoreSummaryControlHot || RestoreSummaryControlHasFocus) - { - if (ThemeHasItemBackground) - { - DrawThemeBackground( - ThemeData, - hdc, - TVP_TREEITEM, - TREIS_HOT, - Rect, - Rect - ); - } - else - { - FillRect(hdc, Rect, GetSysColorBrush(COLOR_WINDOW)); - } - } - - SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); - SetBkMode(hdc, TRANSPARENT); - - SelectObject(hdc, CurrentParameters.MediumFont); - DrawText(hdc, L"Back", 4, Rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); -} - -VOID PhSipDrawSeparator( - _In_ HDC hdc, - _In_ PRECT Rect - ) -{ - RECT rect; - - rect = *Rect; - FillRect(hdc, &rect, GetSysColorBrush(COLOR_3DHIGHLIGHT)); - rect.left += 1; - FillRect(hdc, &rect, GetSysColorBrush(COLOR_3DSHADOW)); -} - -VOID PhSipDrawPanel( - _In_ PPH_SYSINFO_SECTION Section, - _In_ HDC hdc, - _In_ PRECT Rect - ) -{ - PH_SYSINFO_DRAW_PANEL sysInfoDrawPanel; - - if (CurrentView == SysInfoSectionView) - FillRect(hdc, Rect, GetSysColorBrush(COLOR_3DFACE)); - - sysInfoDrawPanel.hdc = hdc; - sysInfoDrawPanel.Rect = *Rect; - sysInfoDrawPanel.Rect.right = CurrentParameters.PanelWidth; - sysInfoDrawPanel.CustomDraw = FALSE; - - sysInfoDrawPanel.Title = NULL; - sysInfoDrawPanel.SubTitle = NULL; - sysInfoDrawPanel.SubTitleOverflow = NULL; - - Section->Callback(Section, SysInfoGraphDrawPanel, &sysInfoDrawPanel, NULL); - - if (!sysInfoDrawPanel.CustomDraw) - { - sysInfoDrawPanel.Rect.right = Rect->right; - PhSipDefaultDrawPanel(Section, &sysInfoDrawPanel); - } - - PhClearReference(&sysInfoDrawPanel.Title); - PhClearReference(&sysInfoDrawPanel.SubTitle); - PhClearReference(&sysInfoDrawPanel.SubTitleOverflow); -} - -VOID PhSipDefaultDrawPanel( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PPH_SYSINFO_DRAW_PANEL DrawPanel - ) -{ - HDC hdc; - RECT rect; - ULONG flags; - - hdc = DrawPanel->hdc; - - if (ThemeHasItemBackground) - { - if (CurrentView == SysInfoSectionView) - { - INT stateId; - - stateId = -1; - - if (Section->GraphHot || Section->PanelHot || Section->HasFocus) - { - if (Section == CurrentSection) - stateId = TREIS_HOTSELECTED; - else - stateId = TREIS_HOT; - } - else if (Section == CurrentSection) - { - stateId = TREIS_SELECTED; - } - - if (stateId != -1) - { - RECT themeRect; - - themeRect = DrawPanel->Rect; - themeRect.left -= 2; // remove left edge - - DrawThemeBackground( - ThemeData, - hdc, - TVP_TREEITEM, - stateId, - &themeRect, - &DrawPanel->Rect - ); - } - } - else if (Section->HasFocus) - { - DrawThemeBackground( - ThemeData, - hdc, - TVP_TREEITEM, - TREIS_HOT, - &DrawPanel->Rect, - &DrawPanel->Rect - ); - } - } - else - { - if (CurrentView == SysInfoSectionView) - { - HBRUSH brush; - - brush = NULL; - - if (Section->GraphHot || Section->PanelHot || Section->HasFocus) - { - brush = GetSysColorBrush(COLOR_WINDOW); // TODO: Use a different color - } - else if (Section == CurrentSection) - { - brush = GetSysColorBrush(COLOR_WINDOW); - } - - if (brush) - { - FillRect(hdc, &DrawPanel->Rect, brush); - } - } - } - - if (CurrentView == SysInfoSummaryView) - SetTextColor(hdc, CurrentParameters.PanelForeColor); - else - SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); - - SetBkMode(hdc, TRANSPARENT); - - rect.left = CurrentParameters.SmallGraphPadding + CurrentParameters.PanelPadding; - rect.top = CurrentParameters.PanelPadding; - rect.right = CurrentParameters.PanelWidth; - rect.bottom = DrawPanel->Rect.bottom; - - flags = DT_NOPREFIX; - - if (CurrentView == SysInfoSummaryView) - rect.right = DrawPanel->Rect.right; // allow the text to overflow - else - flags |= DT_END_ELLIPSIS; - - if (DrawPanel->Title) - { - SelectObject(hdc, CurrentParameters.MediumFont); - DrawText(hdc, DrawPanel->Title->Buffer, (ULONG)DrawPanel->Title->Length / 2, &rect, flags | DT_SINGLELINE); - } - - if (DrawPanel->SubTitle) - { - RECT measureRect; - - rect.top += CurrentParameters.MediumFontHeight + CurrentParameters.PanelPadding; - SelectObject(hdc, CurrentParameters.Font); - - measureRect = rect; - DrawText(hdc, DrawPanel->SubTitle->Buffer, (ULONG)DrawPanel->SubTitle->Length / 2, &measureRect, (flags & ~DT_END_ELLIPSIS) | DT_CALCRECT); - - if (measureRect.right <= rect.right || !DrawPanel->SubTitleOverflow) - { - // Text fits; draw normally. - DrawText(hdc, DrawPanel->SubTitle->Buffer, (ULONG)DrawPanel->SubTitle->Length / 2, &rect, flags); - } - else - { - // Text doesn't fit; draw the alternative text. - DrawText(hdc, DrawPanel->SubTitleOverflow->Buffer, (ULONG)DrawPanel->SubTitleOverflow->Length / 2, &rect, flags); - } - } -} - -VOID PhSipLayoutSummaryView( - VOID - ) -{ - RECT buttonRect; - RECT clientRect; - ULONG availableHeight; - ULONG availableWidth; - ULONG graphHeight; - ULONG i; - PPH_SYSINFO_SECTION section; - HDWP deferHandle; - ULONG y; - - GetWindowRect(GetDlgItem(PhSipWindow, IDOK), &buttonRect); - MapWindowPoints(NULL, PhSipWindow, (POINT *)&buttonRect, 2); - GetClientRect(PhSipWindow, &clientRect); - - availableHeight = buttonRect.top - CurrentParameters.WindowPadding * 2; - availableWidth = clientRect.right - CurrentParameters.WindowPadding * 2; - graphHeight = (availableHeight - CurrentParameters.GraphPadding * (SectionList->Count - 1)) / SectionList->Count; - - deferHandle = BeginDeferWindowPos(SectionList->Count); - y = CurrentParameters.WindowPadding; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - deferHandle = DeferWindowPos( - deferHandle, - section->GraphHandle, - NULL, - CurrentParameters.WindowPadding, - y, - availableWidth, - graphHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - y += graphHeight + CurrentParameters.GraphPadding; - - section->GraphState.Valid = FALSE; - } - - EndDeferWindowPos(deferHandle); -} - -VOID PhSipLayoutSectionView( - VOID - ) -{ - RECT buttonRect; - RECT clientRect; - ULONG availableHeight; - ULONG availableWidth; - ULONG graphHeight; - ULONG i; - PPH_SYSINFO_SECTION section; - HDWP deferHandle; - ULONG y; - ULONG containerLeft; - - GetWindowRect(GetDlgItem(PhSipWindow, IDOK), &buttonRect); - MapWindowPoints(NULL, PhSipWindow, (POINT *)&buttonRect, 2); - GetClientRect(PhSipWindow, &clientRect); - - availableHeight = buttonRect.top - CurrentParameters.WindowPadding * 2; - availableWidth = clientRect.right - CurrentParameters.WindowPadding * 2; - graphHeight = (availableHeight - CurrentParameters.SmallGraphPadding * SectionList->Count) / (SectionList->Count + 1); - - if (graphHeight > CurrentParameters.SectionViewGraphHeight) - graphHeight = CurrentParameters.SectionViewGraphHeight; - - deferHandle = BeginDeferWindowPos(SectionList->Count * 2 + 3); - y = CurrentParameters.WindowPadding; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - deferHandle = DeferWindowPos( - deferHandle, - section->GraphHandle, - NULL, - CurrentParameters.WindowPadding, - y, - CurrentParameters.SmallGraphWidth, - graphHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - deferHandle = DeferWindowPos( - deferHandle, - section->PanelHandle, - NULL, - CurrentParameters.WindowPadding + CurrentParameters.SmallGraphWidth, - y, - CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth, - graphHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - InvalidateRect(section->PanelHandle, NULL, TRUE); - - y += graphHeight + CurrentParameters.SmallGraphPadding; - - section->GraphState.Valid = FALSE; - } - - deferHandle = DeferWindowPos( - deferHandle, - RestoreSummaryControl, - NULL, - CurrentParameters.WindowPadding, - y, - CurrentParameters.SmallGraphWidth + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth, - graphHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - InvalidateRect(RestoreSummaryControl, NULL, TRUE); - - deferHandle = DeferWindowPos( - deferHandle, - SeparatorControl, - NULL, - CurrentParameters.WindowPadding + CurrentParameters.SmallGraphWidth + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth + CurrentParameters.WindowPadding - 7, - 0, - CurrentParameters.SeparatorWidth, - CurrentParameters.WindowPadding + availableHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - containerLeft = CurrentParameters.WindowPadding + CurrentParameters.SmallGraphWidth + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth + CurrentParameters.WindowPadding - 7 + CurrentParameters.SeparatorWidth; - deferHandle = DeferWindowPos( - deferHandle, - ContainerControl, - NULL, - containerLeft, - 0, - clientRect.right - containerLeft, - CurrentParameters.WindowPadding + availableHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - EndDeferWindowPos(deferHandle); - - if (CurrentSection && CurrentSection->DialogHandle) - { - SetWindowPos( - CurrentSection->DialogHandle, - NULL, - CurrentParameters.WindowPadding, - CurrentParameters.WindowPadding, - clientRect.right - containerLeft - CurrentParameters.WindowPadding - CurrentParameters.WindowPadding, - availableHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - } -} - -VOID PhSipEnterSectionView( - _In_ PPH_SYSINFO_SECTION NewSection - ) -{ - ULONG i; - PPH_SYSINFO_SECTION section; - BOOLEAN fromSummaryView; - PPH_SYSINFO_SECTION oldSection; - HDWP deferHandle; - HDWP containerDeferHandle; - - if (CurrentSection == NewSection) - return; - - fromSummaryView = CurrentView == SysInfoSummaryView; - CurrentView = SysInfoSectionView; - oldSection = CurrentSection; - CurrentSection = NewSection; - - deferHandle = BeginDeferWindowPos(SectionList->Count + 4); - containerDeferHandle = BeginDeferWindowPos(SectionList->Count); - - PhSipEnterSectionViewInner(NewSection, fromSummaryView, &deferHandle, &containerDeferHandle); - PhSipLayoutSectionView(); - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - if (section != NewSection) - PhSipEnterSectionViewInner(section, fromSummaryView, &deferHandle, &containerDeferHandle); - } - - deferHandle = DeferWindowPos(deferHandle, ContainerControl, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); - deferHandle = DeferWindowPos(deferHandle, RestoreSummaryControl, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); - deferHandle = DeferWindowPos(deferHandle, SeparatorControl, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); - deferHandle = DeferWindowPos(deferHandle, GetDlgItem(PhSipWindow, IDC_INSTRUCTION), NULL, 0, 0, 0, 0, SWP_HIDEWINDOW_ONLY); - - EndDeferWindowPos(deferHandle); - EndDeferWindowPos(containerDeferHandle); - - if (oldSection) - InvalidateRect(oldSection->PanelHandle, NULL, TRUE); - - InvalidateRect(NewSection->PanelHandle, NULL, TRUE); - - if (NewSection->DialogHandle) - RedrawWindow(NewSection->DialogHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW); -} - -VOID PhSipEnterSectionViewInner( - _In_ PPH_SYSINFO_SECTION Section, - _In_ BOOLEAN FromSummaryView, - _Inout_ HDWP *DeferHandle, - _Inout_ HDWP *ContainerDeferHandle - ) -{ - Section->HasFocus = FALSE; - Section->Callback(Section, SysInfoViewChanging, (PVOID)CurrentView, CurrentSection); - - if (FromSummaryView) - { - PhSetWindowStyle(Section->GraphHandle, GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL, 0); - *DeferHandle = DeferWindowPos(*DeferHandle, Section->PanelHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); - } - - if (Section == CurrentSection && !Section->DialogHandle) - PhSipCreateSectionDialog(Section); - - if (Section->DialogHandle) - { - if (Section == CurrentSection) - *ContainerDeferHandle = DeferWindowPos(*ContainerDeferHandle, Section->DialogHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY | SWP_NOREDRAW); - else - *ContainerDeferHandle = DeferWindowPos(*ContainerDeferHandle, Section->DialogHandle, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW_ONLY | SWP_NOREDRAW); - } -} - -VOID PhSipRestoreSummaryView( - VOID - ) -{ - ULONG i; - PPH_SYSINFO_SECTION section; - - if (CurrentView == SysInfoSummaryView) - return; - - CurrentView = SysInfoSummaryView; - CurrentSection = NULL; - - for (i = 0; i < SectionList->Count; i++) - { - section = SectionList->Items[i]; - - section->Callback(section, SysInfoViewChanging, (PVOID)CurrentView, NULL); - - PhSetWindowStyle(section->GraphHandle, GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL, GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL); - ShowWindow(section->PanelHandle, SW_HIDE); - - if (section->DialogHandle) - ShowWindow(section->DialogHandle, SW_HIDE); - } - - ShowWindow(ContainerControl, SW_HIDE); - ShowWindow(RestoreSummaryControl, SW_HIDE); - ShowWindow(SeparatorControl, SW_HIDE); - ShowWindow(GetDlgItem(PhSipWindow, IDC_INSTRUCTION), SW_SHOW); - - PhSipLayoutSummaryView(); -} - -VOID PhSipCreateSectionDialog( - _In_ PPH_SYSINFO_SECTION Section - ) -{ - PH_SYSINFO_CREATE_DIALOG createDialog; - - memset(&createDialog, 0, sizeof(PH_SYSINFO_CREATE_DIALOG)); - - if (Section->Callback(Section, SysInfoCreateDialog, &createDialog, NULL)) - { - if (!createDialog.CustomCreate) - { - Section->DialogHandle = PhCreateDialogFromTemplate( - ContainerControl, - DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, - createDialog.Instance, - createDialog.Template, - createDialog.DialogProc, - createDialog.Parameter - ); - } - } -} - -LRESULT CALLBACK PhSipGraphHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)dwRefData; - - switch (uMsg) - { - case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhSipGraphHookWndProc, uIdSubclass); - break; - case WM_SETFOCUS: - section->HasFocus = TRUE; - - if (CurrentView == SysInfoSummaryView) - { - Graph_Draw(section->GraphHandle); - InvalidateRect(section->GraphHandle, NULL, FALSE); - } - else - { - InvalidateRect(section->PanelHandle, NULL, TRUE); - } - - break; - case WM_KILLFOCUS: - section->HasFocus = FALSE; - - if (CurrentView == SysInfoSummaryView) - { - Graph_Draw(section->GraphHandle); - InvalidateRect(section->GraphHandle, NULL, FALSE); - } - else - { - InvalidateRect(section->PanelHandle, NULL, TRUE); - } - - break; - case WM_GETDLGCODE: - if (wParam == VK_SPACE || wParam == VK_RETURN || - wParam == VK_UP || wParam == VK_DOWN || - wParam == VK_LEFT || wParam == VK_RIGHT) - return DLGC_WANTALLKEYS; - break; - case WM_KEYDOWN: - { - if (wParam == VK_SPACE || wParam == VK_RETURN) - { - PhSipEnterSectionView(section); - } - else if (wParam == VK_UP || wParam == VK_LEFT || - wParam == VK_DOWN || wParam == VK_RIGHT) - { - ULONG i; - - for (i = 0; i < SectionList->Count; i++) - { - if (SectionList->Items[i] == section) - { - if ((wParam == VK_UP || wParam == VK_LEFT) && i > 0) - { - SetFocus(((PPH_SYSINFO_SECTION)SectionList->Items[i - 1])->GraphHandle); - } - else if (wParam == VK_DOWN || wParam == VK_RIGHT) - { - if (i < SectionList->Count - 1) - SetFocus(((PPH_SYSINFO_SECTION)SectionList->Items[i + 1])->GraphHandle); - else - SetFocus(RestoreSummaryControl); - } - - break; - } - } - } - } - break; - case WM_MOUSEMOVE: - { - TRACKMOUSEEVENT trackMouseEvent; - - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); - trackMouseEvent.dwFlags = TME_LEAVE; - trackMouseEvent.hwndTrack = hwnd; - trackMouseEvent.dwHoverTime = 0; - TrackMouseEvent(&trackMouseEvent); - - if (!(section->GraphHot || section->PanelHot)) - { - section->GraphHot = TRUE; - InvalidateRect(section->PanelHandle, NULL, TRUE); - } - else - { - section->GraphHot = TRUE; - } - } - break; - case WM_MOUSELEAVE: - { - if (!section->PanelHot) - { - section->GraphHot = FALSE; - InvalidateRect(section->PanelHandle, NULL, TRUE); - } - else - { - section->GraphHot = FALSE; - } - - section->HasFocus = FALSE; - - if (CurrentView == SysInfoSummaryView) - { - Graph_Draw(section->GraphHandle); - InvalidateRect(section->GraphHandle, NULL, FALSE); - } - } - break; - case WM_NCLBUTTONDOWN: - { - PhSipEnterSectionView(section); - } - break; - case WM_UPDATEUISTATE: - { - switch (LOWORD(wParam)) - { - case UIS_SET: - if (HIWORD(wParam) & UISF_HIDEFOCUS) - section->HideFocus = TRUE; - break; - case UIS_CLEAR: - if (HIWORD(wParam) & UISF_HIDEFOCUS) - section->HideFocus = FALSE; - break; - case UIS_INITIALIZE: - section->HideFocus = !!(HIWORD(wParam) & UISF_HIDEFOCUS); - break; - } - } - break; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -LRESULT CALLBACK PhSipPanelHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_SYSINFO_SECTION section = (PPH_SYSINFO_SECTION)dwRefData; - - switch (uMsg) - { - case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhSipPanelHookWndProc, uIdSubclass); - break; - case WM_SETFOCUS: - { - if (!section) - { - RestoreSummaryControlHasFocus = TRUE; - InvalidateRect(hwnd, NULL, TRUE); - } - } - break; - case WM_KILLFOCUS: - { - if (!section) - { - RestoreSummaryControlHasFocus = FALSE; - InvalidateRect(hwnd, NULL, TRUE); - } - } - break; - case WM_SETCURSOR: - { - SetCursor(LoadCursor(NULL, IDC_HAND)); - } - return TRUE; - case WM_GETDLGCODE: - if (wParam == VK_SPACE || wParam == VK_RETURN || - wParam == VK_UP || wParam == VK_DOWN || - wParam == VK_LEFT || wParam == VK_RIGHT) - return DLGC_WANTALLKEYS; - break; - case WM_KEYDOWN: - { - if (wParam == VK_SPACE || wParam == VK_RETURN) - { - if (section) - PhSipEnterSectionView(section); - else - PhSipRestoreSummaryView(); - } - else if (wParam == VK_UP || wParam == VK_LEFT) - { - if (!section && SectionList->Count != 0) - { - SetFocus(((PPH_SYSINFO_SECTION)SectionList->Items[SectionList->Count - 1])->GraphHandle); - } - } - } - break; - case WM_MOUSEMOVE: - { - TRACKMOUSEEVENT trackMouseEvent; - - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); - trackMouseEvent.dwFlags = TME_LEAVE; - trackMouseEvent.hwndTrack = hwnd; - trackMouseEvent.dwHoverTime = 0; - TrackMouseEvent(&trackMouseEvent); - - if (section) - { - if (!(section->GraphHot || section->PanelHot)) - { - section->PanelHot = TRUE; - InvalidateRect(section->PanelHandle, NULL, TRUE); - } - else - { - section->PanelHot = TRUE; - } - } - else - { - RestoreSummaryControlHot = TRUE; - InvalidateRect(RestoreSummaryControl, NULL, TRUE); - } - } - break; - case WM_MOUSELEAVE: - { - if (section) - { - section->HasFocus = FALSE; - - if (!section->GraphHot) - { - section->PanelHot = FALSE; - InvalidateRect(section->PanelHandle, NULL, TRUE); - } - else - { - section->PanelHot = FALSE; - } - } - else - { - RestoreSummaryControlHasFocus = FALSE; - RestoreSummaryControlHot = FALSE; - InvalidateRect(RestoreSummaryControl, NULL, TRUE); - } - } - break; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -VOID PhSipUpdateThemeData( - VOID - ) -{ - if (ThemeData) - { - CloseThemeData(ThemeData); - ThemeData = NULL; - } - - ThemeData = OpenThemeData(PhSipWindow, L"TREEVIEW"); - - if (ThemeData) - { - ThemeHasItemBackground = !!IsThemePartDefined(ThemeData, TVP_TREEITEM, 0); - } - else - { - ThemeHasItemBackground = FALSE; - } -} - -VOID PhSipSetAlwaysOnTop( - VOID - ) -{ - SetFocus(PhSipWindow); // HACK - SetWindowPos doesn't work properly without this - SetWindowPos(PhSipWindow, AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); -} - -VOID PhSipSaveWindowState( - VOID - ) -{ - WINDOWPLACEMENT placement = { sizeof(placement) }; - - GetWindowPlacement(PhSipWindow, &placement); - - if (placement.showCmd == SW_NORMAL) - PhSetIntegerSetting(L"SysInfoWindowState", SW_NORMAL); - else if (placement.showCmd == SW_MAXIMIZE) - PhSetIntegerSetting(L"SysInfoWindowState", SW_MAXIMIZE); -} - -VOID NTAPI PhSipSysInfoUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PostMessage(PhSipWindow, SI_MSG_SYSINFO_UPDATE, 0, 0); -} - -PPH_STRING PhSipFormatSizeWithPrecision( - _In_ ULONG64 Size, - _In_ USHORT Precision - ) -{ - PH_FORMAT format; - - format.Type = SizeFormatType | FormatUsePrecision; - format.Precision = Precision; - format.u.Size = Size; - - return PH_AUTO(PhFormat(&format, 1, 0)); -} +/* + * Process Hacker - + * System Information window + * + * Copyright (C) 2011-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This file contains the System Information framework. The "framework" handles creation, layout and + * events for the top-level window itself. It manages the list of sections. + * + * A section is an object that provides information to the user about a type of system resource. The + * CPU, Memory and I/O sections are added automatically to every System Information window. Plugins + * can also add sections to the window. There are two views: summary and section. In summary view, + * rows of graphs are displayed in the window, one graph for each section. The section is + * responsible for providing the graph data and any text to draw on the left-hand side of the graph. + * In section view, the graphs become mini-graphs on the left-hand side of the window. The section + * displays its own embedded dialog in the remaining space. Any controls contained in this dialog, + * including graphs, are the responsibility of the section. + * + * Users can enter section view by: + * * Clicking on a graph or mini-graph. + * * Pressing a number from 1 to 9. + * * Using the tab or arrow keys to select a graph and pressing space or enter. + * + * Users can return to summary view by: + * * Clicking "Back" on the left-hand side of the window. + * * Pressing Backspace. + * * Using the tab or arrow keys to select "Back" and pressing space or enter. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +static HANDLE PhSipThread = NULL; +HWND PhSipWindow = NULL; +static PPH_LIST PhSipDialogList = NULL; +static PH_EVENT InitializedEvent = PH_EVENT_INIT; +static PWSTR InitialSectionName; +static RECT MinimumSize; +static PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + +static PPH_LIST SectionList; +static PH_SYSINFO_PARAMETERS CurrentParameters; +static PH_SYSINFO_VIEW_TYPE CurrentView; +static PPH_SYSINFO_SECTION CurrentSection; +static HWND ContainerControl; +static HWND SeparatorControl; +static HWND RestoreSummaryControl; +static WNDPROC RestoreSummaryControlOldWndProc; +static BOOLEAN RestoreSummaryControlHot; +static BOOLEAN RestoreSummaryControlHasFocus; +static HTHEME ThemeData; +static BOOLEAN ThemeHasItemBackground; + +VOID PhShowSystemInformationDialog( + _In_opt_ PWSTR SectionName + ) +{ + InitialSectionName = SectionName; + + if (!PhSipThread) + { + if (!NT_SUCCESS(PhCreateThreadEx(&PhSipThread, PhSipSysInfoThreadStart, NULL))) + { + PhShowError(PhMainWndHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&InitializedEvent, NULL); + } + + SendMessage(PhSipWindow, SI_MSG_SYSINFO_ACTIVATE, (WPARAM)SectionName, 0); +} + +NTSTATUS PhSipSysInfoThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + BOOL result; + MSG message; + HACCEL acceleratorTable; + BOOLEAN processed; + + PhInitializeAutoPool(&autoPool); + + PhSipWindow = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_SYSINFO), + NULL, + PhSipSysInfoDialogProc + ); + + PhSetEvent(&InitializedEvent); + + acceleratorTable = LoadAccelerators(PhInstanceHandle, MAKEINTRESOURCE(IDR_SYSINFO_ACCEL)); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + processed = FALSE; + + if ( + message.hwnd == PhSipWindow || + IsChild(PhSipWindow, message.hwnd) + ) + { + if (TranslateAccelerator(PhSipWindow, acceleratorTable, &message)) + processed = TRUE; + } + + if (!processed && PhSipDialogList) + { + ULONG i; + + for (i = 0; i < PhSipDialogList->Count; i++) + { + if (IsDialogMessage((HWND)PhSipDialogList->Items[i], &message)) + { + processed = TRUE; + break; + } + } + } + + if (!processed) + { + if (!IsDialogMessage(PhSipWindow, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + PhResetEvent(&InitializedEvent); + NtClose(PhSipThread); + + PhSipWindow = NULL; + PhSipThread = NULL; + + if (PhSipDialogList) + { + PhDereferenceObject(PhSipDialogList); + PhSipDialogList = NULL; + } + + return STATUS_SUCCESS; +} + +INT_PTR CALLBACK PhSipSysInfoDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSipWindow = hwndDlg; + PhSipOnInitDialog(); + } + break; + case WM_DESTROY: + { + PhSipOnDestroy(); + } + break; + case WM_NCDESTROY: + { + PhSipOnNcDestroy(); + } + break; + case WM_SYSCOMMAND: + { + if (PhSipOnSysCommand((ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))) + return 0; + } + break; + case WM_SIZE: + { + PhSipOnSize(); + } + break; + case WM_SIZING: + { + PhSipOnSizing((ULONG)wParam, (PRECT)lParam); + } + break; + case WM_THEMECHANGED: + { + PhSipOnThemeChanged(); + } + break; + case WM_COMMAND: + { + PhSipOnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam)); + } + break; + case WM_NOTIFY: + { + LRESULT result; + + if (PhSipOnNotify((NMHDR *)lParam, &result)) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, result); + return TRUE; + } + } + break; + case WM_DRAWITEM: + { + if (PhSipOnDrawItem(wParam, (DRAWITEMSTRUCT *)lParam)) + return TRUE; + } + break; + case WM_CTLCOLORBTN: // TODO: theme subclass sysinfo window. + case WM_CTLCOLORDLG: + case WM_CTLCOLORSTATIC: + { + if (!PhEnableThemeSupport) + break; + + SetBkMode((HDC)wParam, TRANSPARENT); + + switch (PhCsGraphColorMode) + { + case 0: // New colors + SetTextColor((HDC)wParam, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor((HDC)wParam, RGB(0xef, 0xef, 0xef)); // GetSysColor(COLOR_WINDOW) + break; + case 1: // Old colors + SetTextColor((HDC)wParam, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor((HDC)wParam, RGB(30, 30, 30)); + break; + } + + return (INT_PTR)GetStockBrush(DC_BRUSH); + } + break; + } + + if (uMsg >= SI_MSG_SYSINFO_FIRST && uMsg <= SI_MSG_SYSINFO_LAST) + { + PhSipOnUserMessage(uMsg, wParam, lParam); + } + + return FALSE; +} + +INT_PTR CALLBACK PhSipContainerDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_CTLCOLORBTN: // TODO: theme subclass sysinfo window. + case WM_CTLCOLORDLG: + case WM_CTLCOLORSTATIC: + { + SetBkMode((HDC)wParam, TRANSPARENT); + + if (PhEnableThemeSupport) + { + switch (PhCsGraphColorMode) + { + case 0: // New colors + SetTextColor((HDC)wParam, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + break; + case 1: // Old colors + SetTextColor((HDC)wParam, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor((HDC)wParam, RGB(30, 30, 30)); + break; + } + } + else + { + SetTextColor((HDC)wParam, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + } + + return (INT_PTR)GetStockBrush(DC_BRUSH); + } + break; + } + + return FALSE; +} + +VOID PhSipOnInitDialog( + VOID + ) +{ + RECT clientRect; + PH_STRINGREF sectionName; + PPH_SYSINFO_SECTION section; + + PhSetControlTheme(PhSipWindow, L"explorer"); + SendMessage(PhSipWindow, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(PhSipWindow, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + PhSipSysInfoUpdateHandler, + NULL, + &ProcessesUpdatedRegistration + ); + + ContainerControl = CreateDialog( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_CONTAINER), + PhSipWindow, + PhSipContainerDialogProc + ); + + PhSipUpdateThemeData(); + + if (SectionList) // TODO: Remove (dmex) + { + ULONG i; + PPH_SYSINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + PhSipDestroySection(section); + } + + PhDereferenceObject(SectionList); + } + + SectionList = PhCreateList(8); + PhSipInitializeParameters(); + CurrentView = SysInfoSummaryView; + CurrentSection = NULL; + + PhSipCreateInternalSection(L"CPU", 0, PhSipCpuSectionCallback); + PhSipCreateInternalSection(L"Memory", 0, PhSipMemorySectionCallback); + PhSipCreateInternalSection(L"I/O", 0, PhSipIoSectionCallback); + + if (PhPluginsEnabled) + { + PH_PLUGIN_SYSINFO_POINTERS pointers; + + pointers.WindowHandle = PhSipWindow; + pointers.CreateSection = PhSipCreateSection; + pointers.FindSection = PhSipFindSection; + pointers.EnterSectionView = PhSipEnterSectionView; + pointers.RestoreSummaryView = PhSipRestoreSummaryView; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackSystemInformationInitializing), &pointers); + } + + SeparatorControl = CreateWindow( + WC_STATIC, + NULL, + WS_CHILD | SS_OWNERDRAW, + 0, + 0, + 3, + 3, + PhSipWindow, + NULL, + PhInstanceHandle, + NULL + ); + RestoreSummaryControl = CreateWindow( + WC_STATIC, + NULL, + WS_CHILD | WS_TABSTOP | SS_OWNERDRAW | SS_NOTIFY, + 0, + 0, + 3, + 3, + PhSipWindow, + NULL, + PhInstanceHandle, + NULL + ); + + RestoreSummaryControlOldWndProc = (WNDPROC)GetWindowLongPtr(RestoreSummaryControl, GWLP_WNDPROC); + SetWindowLongPtr(RestoreSummaryControl, GWLP_WNDPROC, (LONG_PTR)PhSipPanelHookWndProc); + RestoreSummaryControlHot = FALSE; + + GetClientRect(PhSipWindow, &clientRect); + MinimumSize.left = 0; + MinimumSize.top = 0; + MinimumSize.right = 430; + MinimumSize.bottom = 290; + MapDialogRect(PhSipWindow, &MinimumSize); + + MinimumSize.right += CurrentParameters.PanelWidth; + MinimumSize.right += GetSystemMetrics(SM_CXFRAME) * 2; + MinimumSize.bottom += GetSystemMetrics(SM_CYFRAME) * 2; + + if (SectionList->Count != 0) + { + //ULONG newMinimumHeight; + // + //newMinimumHeight = + // GetSystemMetrics(SM_CYCAPTION) + + // CurrentParameters.WindowPadding + + // CurrentParameters.MinimumGraphHeight * SectionList->Count + + // CurrentParameters.MinimumGraphHeight + // Back button + // CurrentParameters.GraphPadding * SectionList->Count + + // CurrentParameters.WindowPadding + + // clientRect.bottom - buttonRect.top + + // GetSystemMetrics(SM_CYFRAME) * 2; + // + //if (newMinimumHeight > (ULONG)MinimumSize.bottom) + // MinimumSize.bottom = newMinimumHeight; + } + + PhLoadWindowPlacementFromSetting(L"SysInfoWindowPosition", L"SysInfoWindowSize", PhSipWindow); + + if (PhGetIntegerSetting(L"SysInfoWindowState") == SW_MAXIMIZE) + ShowWindow(PhSipWindow, SW_MAXIMIZE); + + if (InitialSectionName) + PhInitializeStringRefLongHint(§ionName, InitialSectionName); + else + sectionName = PhaGetStringSetting(L"SysInfoWindowSection")->sr; + + if (sectionName.Length != 0 && (section = PhSipFindSection(§ionName))) + { + PhSipEnterSectionView(section); + } + + PhRegisterWindowCallback(PhSipWindow, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + PhInitializeThemeWindowFrame(PhSipWindow); + + PhSipOnSize(); + PhSipOnUserMessage(SI_MSG_SYSINFO_UPDATE, 0, 0); +} + +VOID PhSipOnDestroy( + VOID + ) +{ + PhUnregisterWindowCallback(PhSipWindow); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &ProcessesUpdatedRegistration); + + if (CurrentSection) + PhSetStringSetting2(L"SysInfoWindowSection", &CurrentSection->Name); + else + PhSetStringSetting(L"SysInfoWindowSection", L""); + + PhSaveWindowPlacementToSetting(L"SysInfoWindowPosition", L"SysInfoWindowSize", PhSipWindow); + PhSipSaveWindowState(); +} + +VOID PhSipOnNcDestroy( + VOID + ) +{ + ULONG i; + PPH_SYSINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + PhSipDestroySection(section); + } + + PhDereferenceObject(SectionList); + SectionList = NULL; + PhSipDeleteParameters(); + + if (ThemeData) + { + CloseThemeData(ThemeData); + ThemeData = NULL; + } + + PostQuitMessage(0); +} + +BOOLEAN PhSipOnSysCommand( + _In_ ULONG Type, + _In_ LONG CursorScreenX, + _In_ LONG CursorScreenY + ) +{ + switch (Type) + { + case SC_MINIMIZE: + { + // Save the current window state because we may not have a chance to later. + PhSipSaveWindowState(); + } + break; + } + + return FALSE; +} + +VOID PhSipOnSize( + VOID + ) +{ + if (SectionList && SectionList->Count != 0) + { + if (CurrentView == SysInfoSummaryView) + PhSipLayoutSummaryView(); + else if (CurrentView == SysInfoSectionView) + PhSipLayoutSectionView(); + } +} + +VOID PhSipOnSizing( + _In_ ULONG Edge, + _In_ PRECT DragRectangle + ) +{ + PhResizingMinimumSize(DragRectangle, Edge, MinimumSize.right, MinimumSize.bottom); +} + +VOID PhSipOnThemeChanged( + VOID + ) +{ + PhSipUpdateThemeData(); +} + +VOID PhSipOnCommand( + _In_ HWND HwndControl, + _In_ ULONG Id, + _In_ ULONG Code + ) +{ + switch (Id) + { + case IDCANCEL: + DestroyWindow(PhSipWindow); + break; + case IDC_BACK: + { + PhSipRestoreSummaryView(); + } + break; + case IDC_REFRESH: + { + ProcessHacker_Refresh(PhMainWndHandle); + } + break; + case IDC_PAUSE: + { + ProcessHacker_SetUpdateAutomatically(PhMainWndHandle, !ProcessHacker_GetUpdateAutomatically(PhMainWndHandle)); + } + break; + case IDC_MAXSCREEN: + { + static WINDOWPLACEMENT windowLayout = { sizeof(WINDOWPLACEMENT) }; + ULONG windowStyle = (ULONG)GetWindowLongPtr(PhSipWindow, GWL_STYLE); + + if (windowStyle & WS_OVERLAPPEDWINDOW) + { + MONITORINFO info = { sizeof(MONITORINFO) }; + + if ( + GetWindowPlacement(PhSipWindow, &windowLayout) && + GetMonitorInfo(MonitorFromWindow(PhSipWindow, MONITOR_DEFAULTTOPRIMARY), &info) + ) + { + PhSetWindowStyle(PhSipWindow, WS_OVERLAPPEDWINDOW, 0); + SetWindowPos( + PhSipWindow, + HWND_TOPMOST, + info.rcMonitor.left, + info.rcMonitor.top, + (info.rcMonitor.right - info.rcMonitor.left), + (info.rcMonitor.bottom - info.rcMonitor.top), + SWP_NOOWNERZORDER | SWP_FRAMECHANGED + ); + } + } + else + { + PhSetWindowStyle(PhSipWindow, WS_OVERLAPPEDWINDOW, WS_OVERLAPPEDWINDOW); + SetWindowPlacement(PhSipWindow, &windowLayout); + SetWindowPos(PhSipWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } + } + break; + } + + if (SectionList && Id >= ID_DIGIT1 && Id <= ID_DIGIT9) + { + ULONG index; + + index = Id - ID_DIGIT1; + + if (index < SectionList->Count) + PhSipEnterSectionView(SectionList->Items[index]); + } + + if (SectionList && Code == STN_CLICKED) + { + ULONG i; + PPH_SYSINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (Id == section->PanelId) + { + PhSipEnterSectionView(section); + break; + } + } + } + + if (HwndControl == RestoreSummaryControl) + { + if (Code == STN_CLICKED) + { + PhSipRestoreSummaryView(); + } + } +} + +BOOLEAN PhSipOnNotify( + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + ULONG i; + PPH_SYSINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (getDrawInfo->Header.hwndFrom == section->GraphHandle) + { + section->Callback(section, SysInfoGraphGetDrawInfo, drawInfo, 0); + + if (CurrentView == SysInfoSectionView) + { + drawInfo->Flags &= ~(PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y); + } + else + { + ULONG badWidth = CurrentParameters.PanelWidth; + + // Try not to draw max data point labels that will get covered by the + // fade-out part of the graph. + if (badWidth < drawInfo->Width) + drawInfo->LabelMaxYIndexLimit = (drawInfo->Width - badWidth) / 2; + else + drawInfo->LabelMaxYIndexLimit = -1; + } + + break; + } + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + ULONG i; + PPH_SYSINFO_SECTION section; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (getTooltipText->Header.hwndFrom == section->GraphHandle) + { + PH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT graphGetTooltipText; + + graphGetTooltipText.Index = getTooltipText->Index; + PhInitializeEmptyStringRef(&graphGetTooltipText.Text); + + section->Callback(section, SysInfoGraphGetTooltipText, &graphGetTooltipText, 0); + + getTooltipText->Text = graphGetTooltipText.Text; + + break; + } + } + } + } + break; + case GCN_DRAWPANEL: + { + PPH_GRAPH_DRAWPANEL drawPanel = (PPH_GRAPH_DRAWPANEL)Header; + ULONG i; + PPH_SYSINFO_SECTION section; + + if (CurrentView == SysInfoSummaryView) + { + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (drawPanel->Header.hwndFrom == section->GraphHandle) + { + PhSipDrawPanel(section, drawPanel->hdc, &drawPanel->Rect); + break; + } + } + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + ULONG i; + PPH_SYSINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (mouseEvent->Header.hwndFrom == section->GraphHandle) + { + if (mouseEvent->Message == WM_LBUTTONDOWN) + { + PhSipEnterSectionView(section); + } + + break; + } + } + } + break; + } + + return FALSE; +} + +BOOLEAN PhSipOnDrawItem( + _In_ ULONG_PTR Id, + _In_ PDRAWITEMSTRUCT DrawItemStruct + ) +{ + ULONG i; + PPH_SYSINFO_SECTION section; + + if (DrawItemStruct->hwndItem == RestoreSummaryControl) + { + PhSipDrawRestoreSummaryPanel(DrawItemStruct); + return TRUE; + } + else if (DrawItemStruct->hwndItem == SeparatorControl) + { + PhSipDrawSeparator(DrawItemStruct); + return TRUE; + } + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (Id == section->PanelId) + { + PhSipDrawPanel(section, DrawItemStruct->hDC, &DrawItemStruct->rcItem); + return TRUE; + } + } + + return FALSE; +} + +VOID PhSipOnUserMessage( + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ) +{ + switch (Message) + { + case SI_MSG_SYSINFO_ACTIVATE: + { + PWSTR sectionName = (PWSTR)WParam; + + if (SectionList && sectionName) + { + PH_STRINGREF sectionNameSr; + PPH_SYSINFO_SECTION section; + + PhInitializeStringRefLongHint(§ionNameSr, sectionName); + section = PhSipFindSection(§ionNameSr); + + if (section) + PhSipEnterSectionView(section); + else + PhSipRestoreSummaryView(); + } + + if (IsIconic(PhSipWindow)) + ShowWindow(PhSipWindow, SW_RESTORE); + else + ShowWindow(PhSipWindow, SW_SHOW); + + SetForegroundWindow(PhSipWindow); + } + break; + case SI_MSG_SYSINFO_UPDATE: + { + ULONG i; + PPH_SYSINFO_SECTION section; + + if (SectionList) + { + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + section->Callback(section, SysInfoTick, NULL, NULL); + + section->GraphState.Valid = FALSE; + section->GraphState.TooltipIndex = -1; + Graph_MoveGrid(section->GraphHandle, 1); + Graph_Draw(section->GraphHandle); + Graph_UpdateTooltip(section->GraphHandle); + InvalidateRect(section->GraphHandle, NULL, FALSE); + + InvalidateRect(section->PanelHandle, NULL, FALSE); + } + } + } + break; + case SI_MSG_SYSINFO_CHANGE_SETTINGS: + { + ULONG i; + PPH_SYSINFO_SECTION section; + PH_GRAPH_OPTIONS options; + + if (SectionList) + { + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + Graph_GetOptions(section->GraphHandle, &options); + options.FadeOutBackColor = CurrentParameters.GraphBackColor; + Graph_SetOptions(section->GraphHandle, &options); + } + } + + InvalidateRect(PhSipWindow, NULL, TRUE); + } + break; + } +} + +VOID PhSiNotifyChangeSettings( + VOID + ) +{ + HWND window; + + PhSipUpdateColorParameters(); + + window = PhSipWindow; + + if (window) + PostMessage(window, SI_MSG_SYSINFO_CHANGE_SETTINGS, 0, 0); +} + +VOID PhSiSetColorsGraphDrawInfo( + _Out_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ COLORREF Color1, + _In_ COLORREF Color2 + ) +{ + static PH_QUEUED_LOCK lock = PH_QUEUED_LOCK_INIT; + static ULONG lastDpi = ULONG_MAX; + static HFONT iconTitleFont; + + // Get the appropriate fonts. + + if (DrawInfo->Flags & PH_GRAPH_LABEL_MAX_Y) + { + PhAcquireQueuedLockExclusive(&lock); + + if (lastDpi != PhGlobalDpi) + { + LOGFONT logFont; + + if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) + { + logFont.lfHeight += PhMultiplyDivide(1, PhGlobalDpi, 72); + iconTitleFont = CreateFontIndirect(&logFont); + } + + if (!iconTitleFont) + iconTitleFont = PhApplicationFont; + + lastDpi = PhGlobalDpi; + } + + DrawInfo->LabelYFont = iconTitleFont; + + PhReleaseQueuedLockExclusive(&lock); + } + + DrawInfo->TextFont = PhApplicationFont; + + // Set up the colors. + + switch (PhCsGraphColorMode) + { + case 0: // New colors + DrawInfo->BackColor = RGB(0xef, 0xef, 0xef); + DrawInfo->LineColor1 = PhHalveColorBrightness(Color1); + DrawInfo->LineBackColor1 = PhMakeColorBrighter(Color1, 125); + DrawInfo->LineColor2 = PhHalveColorBrightness(Color2); + DrawInfo->LineBackColor2 = PhMakeColorBrighter(Color2, 125); + DrawInfo->GridColor = RGB(0xc7, 0xc7, 0xc7); + DrawInfo->LabelYColor = RGB(0xa0, 0x60, 0x20); + DrawInfo->TextColor = RGB(0x00, 0x00, 0x00); + DrawInfo->TextBoxColor = RGB(0xe7, 0xe7, 0xe7); + break; + case 1: // Old colors + DrawInfo->BackColor = RGB(0x00, 0x00, 0x00); + DrawInfo->LineColor1 = Color1; + DrawInfo->LineBackColor1 = PhHalveColorBrightness(Color1); + DrawInfo->LineColor2 = Color2; + DrawInfo->LineBackColor2 = PhHalveColorBrightness(Color2); + DrawInfo->GridColor = RGB(0x00, 0x57, 0x00); + DrawInfo->LabelYColor = RGB(0xd0, 0xa0, 0x70); + DrawInfo->TextColor = RGB(0x00, 0xff, 0x00); + DrawInfo->TextBoxColor = RGB(0x00, 0x22, 0x00); + break; + } +} + +PPH_STRING PhSiSizeLabelYFunction( + _In_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ ULONG DataIndex, + _In_ FLOAT Value, + _In_ FLOAT Parameter + ) +{ + ULONG64 size; + + size = (ULONG64)(Value * Parameter); + + if (size != 0) + { + PH_FORMAT format; + + format.Type = SizeFormatType | FormatUsePrecision | FormatUseRadix; + format.Precision = 0; + format.Radix = -1; + format.u.Size = size; + + return PhFormat(&format, 1, 0); + } + else + { + return PhReferenceEmptyString(); + } +} + +VOID PhSipRegisterDialog( + _In_ HWND DialogWindowHandle + ) +{ + if (!PhSipDialogList) + PhSipDialogList = PhCreateList(4); + + PhAddItemList(PhSipDialogList, DialogWindowHandle); +} + +VOID PhSipUnregisterDialog( + _In_ HWND DialogWindowHandle + ) +{ + ULONG index; + + if ((index = PhFindItemList(PhSipDialogList, DialogWindowHandle)) != -1) + PhRemoveItemList(PhSipDialogList, index); +} + +VOID PhSipInitializeParameters( + VOID + ) +{ + LOGFONT logFont; + HDC hdc; + TEXTMETRIC textMetrics; + HFONT originalFont; + + memset(&CurrentParameters, 0, sizeof(PH_SYSINFO_PARAMETERS)); + + CurrentParameters.SysInfoWindowHandle = PhSipWindow; + CurrentParameters.ContainerWindowHandle = ContainerControl; + + if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) + { + CurrentParameters.Font = CreateFontIndirect(&logFont); + } + else + { + CurrentParameters.Font = PhApplicationFont; + GetObject(PhApplicationFont, sizeof(LOGFONT), &logFont); + } + + hdc = GetDC(PhSipWindow); + + logFont.lfHeight -= PhMultiplyDivide(3, GetDeviceCaps(hdc, LOGPIXELSY), 72); + CurrentParameters.MediumFont = CreateFontIndirect(&logFont); + + logFont.lfHeight -= PhMultiplyDivide(3, GetDeviceCaps(hdc, LOGPIXELSY), 72); + CurrentParameters.LargeFont = CreateFontIndirect(&logFont); + + PhSipUpdateColorParameters(); + + CurrentParameters.ColorSetupFunction = PhSiSetColorsGraphDrawInfo; + + originalFont = SelectFont(hdc, CurrentParameters.Font); + GetTextMetrics(hdc, &textMetrics); + CurrentParameters.FontHeight = textMetrics.tmHeight; + CurrentParameters.FontAverageWidth = textMetrics.tmAveCharWidth; + + SelectFont(hdc, CurrentParameters.MediumFont); + GetTextMetrics(hdc, &textMetrics); + CurrentParameters.MediumFontHeight = textMetrics.tmHeight; + CurrentParameters.MediumFontAverageWidth = textMetrics.tmAveCharWidth; + + SelectFont(hdc, originalFont); + + // Internal padding and other values + CurrentParameters.PanelPadding = PH_SCALE_DPI(PH_SYSINFO_PANEL_PADDING); + CurrentParameters.WindowPadding = PH_SCALE_DPI(PH_SYSINFO_WINDOW_PADDING); + CurrentParameters.GraphPadding = PH_SCALE_DPI(PH_SYSINFO_GRAPH_PADDING); + CurrentParameters.SmallGraphWidth = PH_SCALE_DPI(PH_SYSINFO_SMALL_GRAPH_WIDTH); + CurrentParameters.SmallGraphPadding = PH_SCALE_DPI(PH_SYSINFO_SMALL_GRAPH_PADDING); + CurrentParameters.SeparatorWidth = PH_SCALE_DPI(PH_SYSINFO_SEPARATOR_WIDTH); + CurrentParameters.CpuPadding = PH_SCALE_DPI(PH_SYSINFO_CPU_PADDING); + CurrentParameters.MemoryPadding = PH_SCALE_DPI(PH_SYSINFO_MEMORY_PADDING); + + CurrentParameters.MinimumGraphHeight = + CurrentParameters.PanelPadding + + CurrentParameters.MediumFontHeight + + CurrentParameters.PanelPadding; + + CurrentParameters.SectionViewGraphHeight = + CurrentParameters.PanelPadding + + CurrentParameters.MediumFontHeight + + CurrentParameters.PanelPadding + + CurrentParameters.FontHeight + + 2 + + CurrentParameters.FontHeight + + CurrentParameters.PanelPadding + + 2; + + CurrentParameters.PanelWidth = CurrentParameters.MediumFontAverageWidth * 10; + + ReleaseDC(PhSipWindow, hdc); +} + +VOID PhSipDeleteParameters( + VOID + ) +{ + if (CurrentParameters.Font) + DeleteFont(CurrentParameters.Font); + if (CurrentParameters.MediumFont) + DeleteFont(CurrentParameters.MediumFont); + if (CurrentParameters.LargeFont) + DeleteFont(CurrentParameters.LargeFont); +} + +VOID PhSipUpdateColorParameters( + VOID + ) +{ + switch (PhCsGraphColorMode) + { + case 0: // New colors + CurrentParameters.GraphBackColor = RGB(0xef, 0xef, 0xef); + CurrentParameters.PanelForeColor = RGB(0x00, 0x00, 0x00); + break; + case 1: // Old colors + CurrentParameters.GraphBackColor = RGB(0x00, 0x00, 0x00); + CurrentParameters.PanelForeColor = RGB(0xff, 0xff, 0xff); + break; + } +} + +PPH_SYSINFO_SECTION PhSipCreateSection( + _In_ PPH_SYSINFO_SECTION Template + ) +{ + PPH_SYSINFO_SECTION section; + PH_GRAPH_OPTIONS options; + + section = PhAllocateZero(sizeof(PH_SYSINFO_SECTION)); + section->Name = Template->Name; + section->Flags = Template->Flags; + section->Callback = Template->Callback; + section->Context = Template->Context; + + section->GraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP | GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL, + 0, + 0, + 3, + 3, + PhSipWindow, + NULL, + PhInstanceHandle, + NULL + ); + PhInitializeGraphState(§ion->GraphState); + section->Parameters = &CurrentParameters; + + Graph_GetOptions(section->GraphHandle, &options); + options.FadeOutBackColor = CurrentParameters.GraphBackColor; + options.FadeOutWidth = CurrentParameters.PanelWidth + PH_SYSINFO_FADE_ADD; + options.DefaultCursor = LoadCursor(NULL, IDC_HAND); + Graph_SetOptions(section->GraphHandle, &options); + if (PhEnableTooltipSupport) Graph_SetTooltip(section->GraphHandle, TRUE); + + section->PanelId = IDDYNAMIC + SectionList->Count * 2 + 2; + section->PanelHandle = CreateWindow( + WC_STATIC, + NULL, + WS_CHILD | SS_OWNERDRAW | SS_NOTIFY, + 0, + 0, + 3, + 3, + PhSipWindow, + UlongToHandle(section->PanelId), + PhInstanceHandle, + NULL + ); + + section->GraphWindowProc = (WNDPROC)GetWindowLongPtr(section->GraphHandle, GWLP_WNDPROC); + section->PanelWindowProc = (WNDPROC)GetWindowLongPtr(section->PanelHandle, GWLP_WNDPROC); + + PhSetWindowContext(section->GraphHandle, 0xF, section); + PhSetWindowContext(section->PanelHandle, 0xF, section); + + SetWindowLongPtr(section->GraphHandle, GWLP_WNDPROC, (LONG_PTR)PhSipGraphHookWndProc); + SetWindowLongPtr(section->PanelHandle, GWLP_WNDPROC, (LONG_PTR)PhSipPanelHookWndProc); + + PhAddItemList(SectionList, section); + + section->Callback(section, SysInfoCreate, NULL, NULL); + + return section; +} + +VOID PhSipDestroySection( + _In_ PPH_SYSINFO_SECTION Section + ) +{ + Section->Callback(Section, SysInfoDestroy, NULL, NULL); + + PhDeleteGraphState(&Section->GraphState); + PhFree(Section); +} + +PPH_SYSINFO_SECTION PhSipFindSection( + _In_ PPH_STRINGREF Name + ) +{ + ULONG i; + PPH_SYSINFO_SECTION section; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (PhEqualStringRef(§ion->Name, Name, TRUE)) + return section; + } + + return NULL; +} + +PPH_SYSINFO_SECTION PhSipCreateInternalSection( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_ PPH_SYSINFO_SECTION_CALLBACK Callback + ) +{ + PH_SYSINFO_SECTION section; + + memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); + PhInitializeStringRef(§ion.Name, Name); + section.Flags = Flags; + section.Callback = Callback; + + return PhSipCreateSection(§ion); +} + +VOID PhSipDrawRestoreSummaryPanel( + _In_ PDRAWITEMSTRUCT DrawItemStruct + ) +{ + HDC hdc; + HDC bufferDc; + HBITMAP bufferBitmap; + HBITMAP oldBufferBitmap; + RECT bufferRect = + { + 0, 0, + DrawItemStruct->rcItem.right - DrawItemStruct->rcItem.left, + DrawItemStruct->rcItem.bottom - DrawItemStruct->rcItem.top + }; + + if (!(hdc = GetWindowDC(DrawItemStruct->hwndItem))) + return; + + bufferDc = CreateCompatibleDC(hdc); + bufferBitmap = CreateCompatibleBitmap(hdc, bufferRect.right, bufferRect.bottom); + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); + + SetBkMode(bufferDc, TRANSPARENT); + + if (PhEnableThemeSupport) + { + switch (PhCsGraphColorMode) + { + case 0: // New colors + SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_3DFACE)); + break; + case 1: // Old colors + SetTextColor(bufferDc, CurrentParameters.PanelForeColor); + SetDCBrushColor(bufferDc, RGB(30, 30, 30)); + FillRect(bufferDc, &bufferRect, GetStockBrush(DC_BRUSH)); + break; + } + } + else + { + SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_3DFACE)); + } + + if (RestoreSummaryControlHot || RestoreSummaryControlHasFocus) + { + if (ThemeHasItemBackground) + { + DrawThemeBackground( + ThemeData, + bufferDc, + TVP_TREEITEM, + TREIS_HOT, + &bufferRect, + &bufferRect + ); + } + else + { + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_WINDOW)); + } + } + + SelectFont(bufferDc, CurrentParameters.MediumFont); + DrawText(bufferDc, L"Back", 4, &bufferRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); + + BitBlt( + hdc, + DrawItemStruct->rcItem.left, + DrawItemStruct->rcItem.top, + DrawItemStruct->rcItem.right, + DrawItemStruct->rcItem.bottom, + bufferDc, + 0, + 0, + SRCCOPY + ); + + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); + DeleteDC(bufferDc); + + ReleaseDC(DrawItemStruct->hwndItem, hdc); +} + +VOID PhSipDrawSeparator( + _In_ PDRAWITEMSTRUCT DrawItemStruct + ) +{ + HDC hdc; + HDC bufferDc; + HBITMAP bufferBitmap; + HBITMAP oldBufferBitmap; + RECT bufferRect = + { + 0, 0, + DrawItemStruct->rcItem.right - DrawItemStruct->rcItem.left, + DrawItemStruct->rcItem.bottom - DrawItemStruct->rcItem.top + }; + + if (!(hdc = GetWindowDC(DrawItemStruct->hwndItem))) + return; + + bufferDc = CreateCompatibleDC(hdc); + bufferBitmap = CreateCompatibleBitmap(hdc, bufferRect.right, bufferRect.bottom); + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); + + SetBkMode(bufferDc, TRANSPARENT); + + if (PhEnableThemeSupport) + { + switch (PhCsGraphColorMode) + { + case 0: // New colors + { + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_3DFACE)); + bufferRect.left += 1; + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_3DSHADOW)); + bufferRect.left -= 1; + } + break; + case 1: // Old colors + { + SetDCBrushColor(bufferDc, RGB(0, 0, 0)); + FillRect(bufferDc, &bufferRect, GetStockBrush(DC_BRUSH)); + } + break; + } + } + else + { + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_3DHIGHLIGHT)); + bufferRect.left += 1; + FillRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_3DSHADOW)); + bufferRect.left -= 1; + } + + BitBlt( + hdc, + DrawItemStruct->rcItem.left, + DrawItemStruct->rcItem.top, + DrawItemStruct->rcItem.right, + DrawItemStruct->rcItem.bottom, + bufferDc, + 0, + 0, + SRCCOPY + ); + + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); + DeleteDC(bufferDc); + + ReleaseDC(DrawItemStruct->hwndItem, hdc); +} + +VOID PhSipDrawPanel( + _In_ PPH_SYSINFO_SECTION Section, + _In_ HDC hdc, + _In_ PRECT Rect + ) +{ + PH_SYSINFO_DRAW_PANEL sysInfoDrawPanel; + + if (CurrentView == SysInfoSectionView) + { + if (PhEnableThemeSupport) + { + switch (PhCsGraphColorMode) + { + case 0: // New colors + FillRect(hdc, Rect, GetSysColorBrush(COLOR_3DFACE)); + break; + case 1: // Old colors + SetDCBrushColor(hdc, RGB(30, 30, 30)); + FillRect(hdc, Rect, GetStockBrush(DC_BRUSH)); + break; + } + } + else + { + FillRect(hdc, Rect, GetSysColorBrush(COLOR_3DFACE)); + } + } + + sysInfoDrawPanel.hdc = hdc; + sysInfoDrawPanel.Rect = *Rect; + sysInfoDrawPanel.Rect.right = CurrentParameters.PanelWidth; + sysInfoDrawPanel.CustomDraw = FALSE; + + sysInfoDrawPanel.Title = NULL; + sysInfoDrawPanel.SubTitle = NULL; + sysInfoDrawPanel.SubTitleOverflow = NULL; + + Section->Callback(Section, SysInfoGraphDrawPanel, &sysInfoDrawPanel, NULL); + + if (!sysInfoDrawPanel.CustomDraw) + { + sysInfoDrawPanel.Rect.right = Rect->right; + PhSipDefaultDrawPanel(Section, &sysInfoDrawPanel); + } + + PhClearReference(&sysInfoDrawPanel.Title); + PhClearReference(&sysInfoDrawPanel.SubTitle); + PhClearReference(&sysInfoDrawPanel.SubTitleOverflow); +} + +VOID PhSipDefaultDrawPanel( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PPH_SYSINFO_DRAW_PANEL DrawPanel + ) +{ + HDC hdc; + RECT rect; + ULONG flags; + + hdc = DrawPanel->hdc; + + if (ThemeHasItemBackground) + { + if (CurrentView == SysInfoSummaryView) + { + //if (Section->GraphHot) + //{ + // DrawThemeBackground( + // ThemeData, + // hdc, + // TVP_TREEITEM, + // TREIS_HOT, + // &DrawPanel->Rect, + // &DrawPanel->Rect + // ); + //} + } + else if (CurrentView == SysInfoSectionView) + { + INT stateId; + + stateId = -1; + + if (Section->GraphHot || Section->PanelHot || Section->HasFocus) + { + if (Section == CurrentSection) + stateId = TREIS_HOTSELECTED; + else + stateId = TREIS_HOT; + } + else if (Section == CurrentSection) + { + stateId = TREIS_SELECTED; + } + + if (stateId != -1) + { + RECT themeRect; + + themeRect = DrawPanel->Rect; + themeRect.left -= 2; // remove left edge + + DrawThemeBackground( + ThemeData, + hdc, + TVP_TREEITEM, + stateId, + &themeRect, + &DrawPanel->Rect + ); + } + } + else if (Section->HasFocus) + { + DrawThemeBackground( + ThemeData, + hdc, + TVP_TREEITEM, + TREIS_HOT, + &DrawPanel->Rect, + &DrawPanel->Rect + ); + } + } + else + { + if (CurrentView == SysInfoSectionView) + { + HBRUSH brush; + + brush = NULL; + + if (Section->GraphHot || Section->PanelHot || Section->HasFocus) + { + brush = GetSysColorBrush(COLOR_WINDOW); // TODO: Use a different color + } + else if (Section == CurrentSection) + { + brush = GetSysColorBrush(COLOR_WINDOW); + } + + if (brush) + { + FillRect(hdc, &DrawPanel->Rect, brush); + } + } + } + + SetBkMode(hdc, TRANSPARENT); + + if (PhEnableThemeSupport) + { + SetTextColor(hdc, CurrentParameters.PanelForeColor); + } + else + { + if (CurrentView == SysInfoSummaryView) + SetTextColor(hdc, CurrentParameters.PanelForeColor); + else + SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); + } + + rect.left = CurrentParameters.SmallGraphPadding + CurrentParameters.PanelPadding; + rect.top = CurrentParameters.PanelPadding; + rect.right = CurrentParameters.PanelWidth; + rect.bottom = DrawPanel->Rect.bottom; + + flags = DT_NOPREFIX; + + if (CurrentView == SysInfoSummaryView) + rect.right = DrawPanel->Rect.right; // allow the text to overflow + else + flags |= DT_END_ELLIPSIS; + + if (DrawPanel->Title) + { + SelectFont(hdc, CurrentParameters.MediumFont); + DrawText(hdc, DrawPanel->Title->Buffer, (ULONG)DrawPanel->Title->Length / sizeof(WCHAR), &rect, flags | DT_SINGLELINE); + } + + if (DrawPanel->SubTitle) + { + RECT measureRect; + SIZE textSize; + LONG lineHeight; + LONG lineWidth; + LONG rectHeight; + LONG rectWidth; + LONG textHeight; + LONG textWidth; + + rect.top += CurrentParameters.MediumFontHeight + CurrentParameters.PanelPadding; + measureRect = rect; + + SelectFont(hdc, CurrentParameters.Font); + GetTextExtentPoint32(hdc, DrawPanel->SubTitle->Buffer, (ULONG)DrawPanel->SubTitle->Length / sizeof(WCHAR), &textSize); + DrawText(hdc, DrawPanel->SubTitle->Buffer, (ULONG)DrawPanel->SubTitle->Length / sizeof(WCHAR), &measureRect, flags | DT_CALCRECT); + + lineHeight = textSize.cy; + lineWidth = textSize.cx; + rectHeight = rect.bottom - rect.top; + rectWidth = rect.right - rect.left; + textHeight = measureRect.bottom - measureRect.top; + textWidth = measureRect.right - measureRect.left; + //dprintf( + // "[rectHeight: %u, rectwidth: %u] [lineHeight: %u, lineWidth: %u] [textHeight: %u, textWidth: %u]\n", + // rectHeight, rectWidth, + // lineHeight, lineWidth, + // textHeight, textWidth + // ); + + if (rectHeight > lineHeight) + { + if (rectHeight > textHeight) + { + if (lineWidth < rectWidth || !DrawPanel->SubTitleOverflow) + { + // Text fits; draw normally. + DrawText(hdc, DrawPanel->SubTitle->Buffer, (ULONG)DrawPanel->SubTitle->Length / sizeof(WCHAR), &rect, flags); + } + else + { + // Text doesn't fit; draw the alternative text. + DrawText(hdc, DrawPanel->SubTitleOverflow->Buffer, (ULONG)DrawPanel->SubTitleOverflow->Length / sizeof(WCHAR), &rect, flags); + } + } + else + { + PH_STRINGREF titlePart; + PH_STRINGREF remainingPart; + + // dmex: Multiline text doesn't fit; split the string and draw the first line. + if (DrawPanel->SubTitleOverflow) + { + if (PhSplitStringRefAtChar(&DrawPanel->SubTitleOverflow->sr, '\n', &titlePart, &remainingPart)) + DrawText(hdc, titlePart.Buffer, (ULONG)titlePart.Length / sizeof(WCHAR), &rect, flags); + else + DrawText(hdc, DrawPanel->SubTitleOverflow->Buffer, (ULONG)DrawPanel->SubTitleOverflow->Length / sizeof(WCHAR), &rect, flags); + } + else + { + if (PhSplitStringRefAtChar(&DrawPanel->SubTitle->sr, '\n', &titlePart, &remainingPart)) + DrawText(hdc, titlePart.Buffer, (ULONG)titlePart.Length / sizeof(WCHAR), &rect, flags); + else + DrawText(hdc, DrawPanel->SubTitle->Buffer, (ULONG)DrawPanel->SubTitle->Length / sizeof(WCHAR), &rect, flags); + } + } + } + } +} + +VOID PhSipLayoutSummaryView( + VOID + ) +{ + RECT clientRect; + ULONG availableHeight; + ULONG availableWidth; + ULONG graphHeight; + ULONG i; + PPH_SYSINFO_SECTION section; + HDWP deferHandle; + ULONG y; + + GetClientRect(PhSipWindow, &clientRect); + + availableHeight = clientRect.bottom - CurrentParameters.WindowPadding * 2; + availableWidth = clientRect.right - CurrentParameters.WindowPadding * 2; + graphHeight = (availableHeight - CurrentParameters.GraphPadding * (SectionList->Count - 1)) / SectionList->Count; + + deferHandle = BeginDeferWindowPos(SectionList->Count); + y = CurrentParameters.WindowPadding; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + deferHandle = DeferWindowPos( + deferHandle, + section->GraphHandle, + NULL, + CurrentParameters.WindowPadding, + y, + availableWidth, + graphHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + y += graphHeight + CurrentParameters.GraphPadding; + + section->GraphState.Valid = FALSE; + } + + EndDeferWindowPos(deferHandle); +} + +VOID PhSipLayoutSectionView( + VOID + ) +{ + RECT clientRect; + ULONG availableHeight; + ULONG availableWidth; + ULONG graphHeight; + ULONG i; + PPH_SYSINFO_SECTION section; + HDWP deferHandle; + ULONG y; + ULONG containerLeft; + + GetClientRect(PhSipWindow, &clientRect); + + availableHeight = clientRect.bottom - CurrentParameters.WindowPadding * 2; + availableWidth = clientRect.right - CurrentParameters.WindowPadding * 2; + graphHeight = (availableHeight - CurrentParameters.SmallGraphPadding * SectionList->Count) / (SectionList->Count + 1); + + if (graphHeight > CurrentParameters.SectionViewGraphHeight) + graphHeight = CurrentParameters.SectionViewGraphHeight; + + deferHandle = BeginDeferWindowPos(SectionList->Count * 2 + 3); + y = CurrentParameters.WindowPadding; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + deferHandle = DeferWindowPos( + deferHandle, + section->GraphHandle, + NULL, + CurrentParameters.WindowPadding, + y, + CurrentParameters.SmallGraphWidth, + graphHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + deferHandle = DeferWindowPos( + deferHandle, + section->PanelHandle, + NULL, + CurrentParameters.WindowPadding + CurrentParameters.SmallGraphWidth, + y, + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth, + graphHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + InvalidateRect(section->PanelHandle, NULL, TRUE); + + y += graphHeight + CurrentParameters.SmallGraphPadding; + + section->GraphState.Valid = FALSE; + } + + deferHandle = DeferWindowPos( + deferHandle, + RestoreSummaryControl, + NULL, + CurrentParameters.WindowPadding, + y, + CurrentParameters.SmallGraphWidth + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth, + graphHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + InvalidateRect(RestoreSummaryControl, NULL, TRUE); + + deferHandle = DeferWindowPos( + deferHandle, + SeparatorControl, + NULL, + CurrentParameters.WindowPadding + CurrentParameters.SmallGraphWidth + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth + CurrentParameters.WindowPadding - 7, + 0, + CurrentParameters.SeparatorWidth, + CurrentParameters.WindowPadding + availableHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + containerLeft = CurrentParameters.WindowPadding + CurrentParameters.SmallGraphWidth + CurrentParameters.SmallGraphPadding + CurrentParameters.PanelWidth + CurrentParameters.WindowPadding - 7 + CurrentParameters.SeparatorWidth; + deferHandle = DeferWindowPos( + deferHandle, + ContainerControl, + NULL, + containerLeft, + 0, + clientRect.right - containerLeft, + CurrentParameters.WindowPadding + availableHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + EndDeferWindowPos(deferHandle); + + if (CurrentSection && CurrentSection->DialogHandle) + { + SetWindowPos( + CurrentSection->DialogHandle, + NULL, + CurrentParameters.WindowPadding, + CurrentParameters.WindowPadding, + clientRect.right - containerLeft - CurrentParameters.WindowPadding - CurrentParameters.WindowPadding, + availableHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + } +} + +VOID PhSipEnterSectionView( + _In_ PPH_SYSINFO_SECTION NewSection + ) +{ + ULONG i; + PPH_SYSINFO_SECTION section; + BOOLEAN fromSummaryView; + PPH_SYSINFO_SECTION oldSection; + HDWP deferHandle; + HDWP containerDeferHandle; + + if (CurrentSection == NewSection) + return; + + fromSummaryView = CurrentView == SysInfoSummaryView; + CurrentView = SysInfoSectionView; + oldSection = CurrentSection; + CurrentSection = NewSection; + + deferHandle = BeginDeferWindowPos(SectionList->Count + 3); + containerDeferHandle = BeginDeferWindowPos(SectionList->Count); + + PhSipEnterSectionViewInner(NewSection, fromSummaryView, &deferHandle, &containerDeferHandle); + PhSipLayoutSectionView(); + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + if (section != NewSection) + PhSipEnterSectionViewInner(section, fromSummaryView, &deferHandle, &containerDeferHandle); + } + + deferHandle = DeferWindowPos(deferHandle, ContainerControl, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); + deferHandle = DeferWindowPos(deferHandle, RestoreSummaryControl, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); + deferHandle = DeferWindowPos(deferHandle, SeparatorControl, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); + + EndDeferWindowPos(deferHandle); + EndDeferWindowPos(containerDeferHandle); + + if (oldSection) + InvalidateRect(oldSection->PanelHandle, NULL, TRUE); + + InvalidateRect(NewSection->PanelHandle, NULL, TRUE); + + if (NewSection->DialogHandle) + RedrawWindow(NewSection->DialogHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW); +} + +VOID PhSipEnterSectionViewInner( + _In_ PPH_SYSINFO_SECTION Section, + _In_ BOOLEAN FromSummaryView, + _Inout_ HDWP *DeferHandle, + _Inout_ HDWP *ContainerDeferHandle + ) +{ + Section->HasFocus = FALSE; + Section->Callback(Section, SysInfoViewChanging, (PVOID)CurrentView, CurrentSection); + + if (FromSummaryView) + { + PhSetWindowStyle(Section->GraphHandle, GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL, 0); + *DeferHandle = DeferWindowPos(*DeferHandle, Section->PanelHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY); + } + + if (Section == CurrentSection && !Section->DialogHandle) + PhSipCreateSectionDialog(Section); + + if (Section->DialogHandle) + { + if (Section == CurrentSection) + *ContainerDeferHandle = DeferWindowPos(*ContainerDeferHandle, Section->DialogHandle, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW_ONLY | SWP_NOREDRAW); + else + *ContainerDeferHandle = DeferWindowPos(*ContainerDeferHandle, Section->DialogHandle, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW_ONLY | SWP_NOREDRAW); + } +} + +VOID PhSipRestoreSummaryView( + VOID + ) +{ + ULONG i; + PPH_SYSINFO_SECTION section; + + if (CurrentView == SysInfoSummaryView) + return; + + CurrentView = SysInfoSummaryView; + CurrentSection = NULL; + + for (i = 0; i < SectionList->Count; i++) + { + section = SectionList->Items[i]; + + section->Callback(section, SysInfoViewChanging, (PVOID)CurrentView, NULL); + + PhSetWindowStyle(section->GraphHandle, GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL, GC_STYLE_FADEOUT | GC_STYLE_DRAW_PANEL); + ShowWindow(section->PanelHandle, SW_HIDE); + + if (section->DialogHandle) + ShowWindow(section->DialogHandle, SW_HIDE); + } + + ShowWindow(ContainerControl, SW_HIDE); + ShowWindow(RestoreSummaryControl, SW_HIDE); + ShowWindow(SeparatorControl, SW_HIDE); + + PhSipLayoutSummaryView(); +} + +VOID PhSipCreateSectionDialog( + _In_ PPH_SYSINFO_SECTION Section + ) +{ + PH_SYSINFO_CREATE_DIALOG createDialog; + + memset(&createDialog, 0, sizeof(PH_SYSINFO_CREATE_DIALOG)); + + if (Section->Callback(Section, SysInfoCreateDialog, &createDialog, NULL)) + { + if (!createDialog.CustomCreate) + { + Section->DialogHandle = PhCreateDialogFromTemplate( + ContainerControl, + DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD, + createDialog.Instance, + createDialog.Template, + createDialog.DialogProc, + createDialog.Parameter + ); + + PhInitializeWindowTheme(Section->DialogHandle, PhEnableThemeSupport); + } + } +} + +LRESULT CALLBACK PhSipGraphHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_SYSINFO_SECTION section; + WNDPROC oldWndProc; + + if (!(section = PhGetWindowContext(hwnd, 0xF))) + return 0; + + oldWndProc = section->GraphWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + } + break; + case WM_SETFOCUS: + section->HasFocus = TRUE; + + if (CurrentView == SysInfoSummaryView) + { + Graph_Draw(section->GraphHandle); + InvalidateRect(section->GraphHandle, NULL, FALSE); + } + else + { + InvalidateRect(section->PanelHandle, NULL, TRUE); + } + + break; + case WM_KILLFOCUS: + section->HasFocus = FALSE; + + if (CurrentView == SysInfoSummaryView) + { + Graph_Draw(section->GraphHandle); + InvalidateRect(section->GraphHandle, NULL, FALSE); + } + else + { + InvalidateRect(section->PanelHandle, NULL, TRUE); + } + + break; + case WM_GETDLGCODE: + if (wParam == VK_SPACE || wParam == VK_RETURN || + wParam == VK_UP || wParam == VK_DOWN || + wParam == VK_LEFT || wParam == VK_RIGHT) + return DLGC_WANTALLKEYS; + break; + case WM_KEYDOWN: + { + if (wParam == VK_SPACE || wParam == VK_RETURN) + { + PhSipEnterSectionView(section); + } + else if (wParam == VK_UP || wParam == VK_LEFT || + wParam == VK_DOWN || wParam == VK_RIGHT) + { + ULONG i; + + for (i = 0; i < SectionList->Count; i++) + { + if (SectionList->Items[i] == section) + { + if ((wParam == VK_UP || wParam == VK_LEFT) && i > 0) + { + SetFocus(((PPH_SYSINFO_SECTION)SectionList->Items[i - 1])->GraphHandle); + } + else if (wParam == VK_DOWN || wParam == VK_RIGHT) + { + if (i < SectionList->Count - 1) + SetFocus(((PPH_SYSINFO_SECTION)SectionList->Items[i + 1])->GraphHandle); + else + SetFocus(RestoreSummaryControl); + } + + break; + } + } + } + } + break; + case WM_MOUSEMOVE: + { + TRACKMOUSEEVENT trackMouseEvent; + + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + trackMouseEvent.dwHoverTime = 0; + TrackMouseEvent(&trackMouseEvent); + + if (!(section->GraphHot || section->PanelHot)) + { + section->GraphHot = TRUE; + } + else + { + section->GraphHot = TRUE; + } + + Graph_Draw(section->GraphHandle); + InvalidateRect(section->GraphHandle, NULL, TRUE); + InvalidateRect(section->PanelHandle, NULL, TRUE); + } + break; + case WM_MOUSELEAVE: + { + if (!section->PanelHot) + { + section->GraphHot = FALSE; + InvalidateRect(section->PanelHandle, NULL, TRUE); + } + else + { + section->GraphHot = FALSE; + } + + section->HasFocus = FALSE; + + if (CurrentView == SysInfoSummaryView) + { + Graph_Draw(section->GraphHandle); + InvalidateRect(section->GraphHandle, NULL, FALSE); + } + } + break; + case WM_NCLBUTTONDOWN: + { + PhSipEnterSectionView(section); + } + break; + case WM_UPDATEUISTATE: + { + switch (LOWORD(wParam)) + { + case UIS_SET: + if (HIWORD(wParam) & UISF_HIDEFOCUS) + section->HideFocus = TRUE; + break; + case UIS_CLEAR: + if (HIWORD(wParam) & UISF_HIDEFOCUS) + section->HideFocus = FALSE; + break; + case UIS_INITIALIZE: + section->HideFocus = !!(HIWORD(wParam) & UISF_HIDEFOCUS); + break; + } + } + break; + } + + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); +} + +LRESULT CALLBACK PhSipPanelHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_SYSINFO_SECTION section; + WNDPROC oldWndProc; + + section = PhGetWindowContext(hwnd, 0xF); + + if (section) + oldWndProc = section->PanelWindowProc; + else + oldWndProc = RestoreSummaryControlOldWndProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + } + break; + case WM_SETFOCUS: + { + if (!section) + { + RestoreSummaryControlHasFocus = TRUE; + InvalidateRect(hwnd, NULL, TRUE); + } + } + break; + case WM_KILLFOCUS: + { + if (!section) + { + RestoreSummaryControlHasFocus = FALSE; + InvalidateRect(hwnd, NULL, TRUE); + } + } + break; + case WM_SETCURSOR: + { + SetCursor(LoadCursor(NULL, IDC_HAND)); + } + return TRUE; + case WM_GETDLGCODE: + if (wParam == VK_SPACE || wParam == VK_RETURN || + wParam == VK_UP || wParam == VK_DOWN || + wParam == VK_LEFT || wParam == VK_RIGHT) + return DLGC_WANTALLKEYS; + break; + case WM_KEYDOWN: + { + if (wParam == VK_SPACE || wParam == VK_RETURN) + { + if (section) + PhSipEnterSectionView(section); + else + PhSipRestoreSummaryView(); + } + else if (wParam == VK_UP || wParam == VK_LEFT) + { + if (!section && SectionList->Count != 0) + { + SetFocus(((PPH_SYSINFO_SECTION)SectionList->Items[SectionList->Count - 1])->GraphHandle); + } + } + } + break; + case WM_MOUSEMOVE: + { + TRACKMOUSEEVENT trackMouseEvent; + + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + trackMouseEvent.dwHoverTime = 0; + TrackMouseEvent(&trackMouseEvent); + + if (section) + { + if (!(section->GraphHot || section->PanelHot)) + { + section->PanelHot = TRUE; + InvalidateRect(section->PanelHandle, NULL, TRUE); + } + else + { + section->PanelHot = TRUE; + } + } + else + { + RestoreSummaryControlHot = TRUE; + InvalidateRect(RestoreSummaryControl, NULL, TRUE); + } + } + break; + case WM_MOUSELEAVE: + { + if (section) + { + section->HasFocus = FALSE; + + if (!section->GraphHot) + { + section->PanelHot = FALSE; + InvalidateRect(section->PanelHandle, NULL, TRUE); + } + else + { + section->PanelHot = FALSE; + } + } + else + { + RestoreSummaryControlHasFocus = FALSE; + RestoreSummaryControlHot = FALSE; + InvalidateRect(RestoreSummaryControl, NULL, TRUE); + } + } + break; + } + + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); +} + +VOID PhSipUpdateThemeData( + VOID + ) +{ + if (ThemeData) + { + CloseThemeData(ThemeData); + ThemeData = NULL; + } + + ThemeData = OpenThemeData(PhSipWindow, VSCLASS_TREEVIEW); + + if (ThemeData) + { + ThemeHasItemBackground = !!IsThemePartDefined(ThemeData, TVP_TREEITEM, 0); + } + else + { + ThemeHasItemBackground = FALSE; + } +} + +VOID PhSipSaveWindowState( + VOID + ) +{ + WINDOWPLACEMENT placement = { sizeof(placement) }; + + GetWindowPlacement(PhSipWindow, &placement); + + if (placement.showCmd == SW_NORMAL) + PhSetIntegerSetting(L"SysInfoWindowState", SW_NORMAL); + else if (placement.showCmd == SW_MAXIMIZE) + PhSetIntegerSetting(L"SysInfoWindowState", SW_MAXIMIZE); +} + +VOID NTAPI PhSipSysInfoUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PostMessage(PhSipWindow, SI_MSG_SYSINFO_UPDATE, 0, 0); +} + +PPH_STRING PhSipFormatSizeWithPrecision( + _In_ ULONG64 Size, + _In_ USHORT Precision + ) +{ + PH_FORMAT format; + + format.Type = SizeFormatType | FormatUsePrecision; + format.Precision = Precision; + format.u.Size = Size; + + return PH_AUTO(PhFormat(&format, 1, 0)); +} diff --git a/ProcessHacker/syssccpu.c b/ProcessHacker/syssccpu.c index 357159c9f330..2a78c5d6fb04 100644 --- a/ProcessHacker/syssccpu.c +++ b/ProcessHacker/syssccpu.c @@ -21,14 +21,14 @@ */ #include +#include #include #include #include -#include #include -#include +#include static PPH_SYSINFO_SECTION CpuSection; static HWND CpuDialog; @@ -176,8 +176,7 @@ VOID PhSipInitializeCpuDialog( CurrentPerformanceDistribution = NULL; PreviousPerformanceDistribution = NULL; - if (WindowsVersion >= WINDOWS_7) - PhSipQueryProcessorPerformanceDistribution(&CurrentPerformanceDistribution); + PhSipQueryProcessorPerformanceDistribution(&CurrentPerformanceDistribution); } VOID PhSipUninitializeCpuDialog( @@ -240,15 +239,12 @@ VOID PhSipTickCpuDialog( memset(PowerInformation, 0, sizeof(PROCESSOR_POWER_INFORMATION) * NumberOfProcessors); } - if (WindowsVersion >= WINDOWS_7) - { - if (PreviousPerformanceDistribution) - PhFree(PreviousPerformanceDistribution); + if (PreviousPerformanceDistribution) + PhFree(PreviousPerformanceDistribution); - PreviousPerformanceDistribution = CurrentPerformanceDistribution; - CurrentPerformanceDistribution = NULL; - PhSipQueryProcessorPerformanceDistribution(&CurrentPerformanceDistribution); - } + PreviousPerformanceDistribution = CurrentPerformanceDistribution; + CurrentPerformanceDistribution = NULL; + PhSipQueryProcessorPerformanceDistribution(&CurrentPerformanceDistribution); CpuTicked++; @@ -283,11 +279,11 @@ INT_PTR CALLBACK PhSipCpuDialogProc( CpuGraphMargin = graphItem->Margin; panelItem = PhAddLayoutItem(&CpuLayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - SendMessage(GetDlgItem(hwndDlg, IDC_TITLE), WM_SETFONT, (WPARAM)CpuSection->Parameters->LargeFont, FALSE); - SendMessage(GetDlgItem(hwndDlg, IDC_CPUNAME), WM_SETFONT, (WPARAM)CpuSection->Parameters->MediumFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_TITLE), CpuSection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_CPUNAME), CpuSection->Parameters->MediumFont, FALSE); PhSipGetCpuBrandString(brandString); - SetDlgItemText(hwndDlg, IDC_CPUNAME, brandString); + PhSetDialogItemText(hwndDlg, IDC_CPUNAME, brandString); CpuPanel = CreateDialog(PhInstanceHandle, MAKEINTRESOURCE(IDD_SYSINFO_CPUPANEL), hwndDlg, PhSipCpuPanelDialogProc); ShowWindow(CpuPanel, SW_SHOW); @@ -330,7 +326,7 @@ INT_PTR CALLBACK PhSipCpuDialogProc( if (header->hwndFrom == CpuGraphHandle) { - PhSipNotifyCpuGraph(-1, header); + PhSipNotifyCpuGraph(ULONG_MAX, header); } else { @@ -361,13 +357,13 @@ INT_PTR CALLBACK PhSipCpuPanelDialogProc( { case WM_INITDIALOG: { - SendMessage(GetDlgItem(hwndDlg, IDC_UTILIZATION), WM_SETFONT, (WPARAM)CpuSection->Parameters->MediumFont, FALSE); - SendMessage(GetDlgItem(hwndDlg, IDC_SPEED), WM_SETFONT, (WPARAM)CpuSection->Parameters->MediumFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_UTILIZATION), CpuSection->Parameters->MediumFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_SPEED), CpuSection->Parameters->MediumFont, FALSE); } break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_ONEGRAPHPERCPU: { @@ -547,7 +543,7 @@ VOID PhSipNotifyCpuGraph( drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_USE_LINE_2; PhSiSetColorsGraphDrawInfo(drawInfo, PhCsColorCpuKernel, PhCsColorCpuUser); - if (Index == -1) + if (Index == ULONG_MAX) { PhGraphStateGetDrawInfo( &CpuGraphState, @@ -585,7 +581,7 @@ VOID PhSipNotifyCpuGraph( if (getTooltipText->Index < getTooltipText->TotalCount) { - if (Index == -1) + if (Index == ULONG_MAX) { if (CpuGraphState.TooltipIndex != getTooltipText->Index) { @@ -659,7 +655,7 @@ VOID PhSipUpdateCpuGraphs( ULONG i; CpuGraphState.Valid = FALSE; - CpuGraphState.TooltipIndex = -1; + CpuGraphState.TooltipIndex = ULONG_MAX; Graph_MoveGrid(CpuGraphHandle, 1); Graph_Draw(CpuGraphHandle); Graph_UpdateTooltip(CpuGraphHandle); @@ -668,7 +664,7 @@ VOID PhSipUpdateCpuGraphs( for (i = 0; i < NumberOfProcessors; i++) { CpusGraphState[i].Valid = FALSE; - CpusGraphState[i].TooltipIndex = -1; + CpusGraphState[i].TooltipIndex = ULONG_MAX; Graph_MoveGrid(CpusGraphHandle[i], 1); Graph_Draw(CpusGraphHandle[i]); Graph_UpdateTooltip(CpusGraphHandle[i]); @@ -687,12 +683,12 @@ VOID PhSipUpdateCpuPanel( SYSTEM_TIMEOFDAY_INFORMATION timeOfDayInfo; WCHAR uptimeString[PH_TIMESPAN_STR_LEN_1] = L"Unknown"; - SetDlgItemText(hwnd, IDC_UTILIZATION, PhaFormatString(L"%.2f%%", (PhCpuUserUsage + PhCpuKernelUsage) * 100)->Buffer); + PhSetDialogItemText(hwnd, IDC_UTILIZATION, PhaFormatString(L"%.2f%%", (PhCpuUserUsage + PhCpuKernelUsage) * 100)->Buffer); cpuGhz = 0; distributionSucceeded = FALSE; - if (WindowsVersion >= WINDOWS_7 && CurrentPerformanceDistribution && PreviousPerformanceDistribution) + if (CurrentPerformanceDistribution && PreviousPerformanceDistribution) { if (PhSipGetCpuFrequencyFromDistribution(&cpuFraction)) { @@ -704,11 +700,11 @@ VOID PhSipUpdateCpuPanel( if (!distributionSucceeded) cpuGhz = (DOUBLE)PowerInformation[0].CurrentMhz / 1000; - SetDlgItemText(hwnd, IDC_SPEED, PhaFormatString(L"%.2f / %.2f GHz", cpuGhz, (DOUBLE)PowerInformation[0].MaxMhz / 1000)->Buffer); + PhSetDialogItemText(hwnd, IDC_SPEED, PhaFormatString(L"%.2f / %.2f GHz", cpuGhz, (DOUBLE)PowerInformation[0].MaxMhz / 1000)->Buffer); - SetDlgItemText(hwnd, IDC_ZPROCESSES_V, PhaFormatUInt64(PhTotalProcesses, TRUE)->Buffer); - SetDlgItemText(hwnd, IDC_ZTHREADS_V, PhaFormatUInt64(PhTotalThreads, TRUE)->Buffer); - SetDlgItemText(hwnd, IDC_ZHANDLES_V, PhaFormatUInt64(PhTotalHandles, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZPROCESSES_V, PhaFormatUInt64(PhTotalProcesses, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZTHREADS_V, PhaFormatUInt64(PhTotalThreads, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZHANDLES_V, PhaFormatUInt64(PhTotalHandles, TRUE)->Buffer); if (NT_SUCCESS(NtQuerySystemInformation( SystemTimeOfDayInformation, @@ -720,27 +716,27 @@ VOID PhSipUpdateCpuPanel( PhPrintTimeSpan(uptimeString, timeOfDayInfo.CurrentTime.QuadPart - timeOfDayInfo.BootTime.QuadPart, PH_TIMESPAN_DHMS); } - SetDlgItemText(hwnd, IDC_ZUPTIME_V, uptimeString); + PhSetDialogItemText(hwnd, IDC_ZUPTIME_V, uptimeString); if (CpuTicked > 1) - SetDlgItemText(hwnd, IDC_ZCONTEXTSWITCHESDELTA_V, PhaFormatUInt64(ContextSwitchesDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZCONTEXTSWITCHESDELTA_V, PhaFormatUInt64(ContextSwitchesDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(hwnd, IDC_ZCONTEXTSWITCHESDELTA_V, L"-"); + PhSetDialogItemText(hwnd, IDC_ZCONTEXTSWITCHESDELTA_V, L"-"); if (CpuTicked > 1) - SetDlgItemText(hwnd, IDC_ZINTERRUPTSDELTA_V, PhaFormatUInt64(InterruptsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZINTERRUPTSDELTA_V, PhaFormatUInt64(InterruptsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(hwnd, IDC_ZINTERRUPTSDELTA_V, L"-"); + PhSetDialogItemText(hwnd, IDC_ZINTERRUPTSDELTA_V, L"-"); if (CpuTicked > 1) - SetDlgItemText(hwnd, IDC_ZDPCSDELTA_V, PhaFormatUInt64(DpcsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZDPCSDELTA_V, PhaFormatUInt64(DpcsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(hwnd, IDC_ZDPCSDELTA_V, L"-"); + PhSetDialogItemText(hwnd, IDC_ZDPCSDELTA_V, L"-"); if (CpuTicked > 1) - SetDlgItemText(hwnd, IDC_ZSYSTEMCALLSDELTA_V, PhaFormatUInt64(SystemCallsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(hwnd, IDC_ZSYSTEMCALLSDELTA_V, PhaFormatUInt64(SystemCallsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(hwnd, IDC_ZSYSTEMCALLSDELTA_V, L"-"); + PhSetDialogItemText(hwnd, IDC_ZSYSTEMCALLSDELTA_V, L"-"); } PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord( @@ -771,7 +767,7 @@ PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord( // * If we call PhFindProcessRecord, it cannot find the process because it was // started at 2.5 seconds, not 2 seconds or older. // - // This mean we must add one second minus one tick (100ns) to the time, giving us + // This means we must add one second minus one tick (100ns) to the time, giving us // 2.9999999 seconds. This will then make sure we find the process. PhGetStatisticsTime(NULL, Index, &time); time.QuadPart += PH_TICKS_PER_SEC - 1; @@ -799,7 +795,7 @@ PPH_STRING PhSipGetMaxCpuString( if (!PH_IS_FAKE_PROCESS_ID(maxProcessRecord->ProcessId)) { maxUsageString = PhaFormatString( - L"\n%s (%u): %.2f%%", + L"\n%s (%lu): %.2f%%", maxProcessRecord->ProcessName->Buffer, HandleToUlong(maxProcessRecord->ProcessId), maxCpuUsage * 100 @@ -827,14 +823,24 @@ VOID PhSipGetCpuBrandString( _Out_writes_(49) PWSTR BrandString ) { - ULONG brandString[4 * 3]; - - __cpuid(&brandString[0], 0x80000002); - __cpuid(&brandString[4], 0x80000003); - __cpuid(&brandString[8], 0x80000004); + // dmex: The __cpuid instruction generates quite a few FPs by security software (malware uses this as an anti-VM trick)... + // TODO: This comment block should be removed if the SystemProcessorBrandString class is more reliable. + //ULONG brandString[4 * 3]; + //__cpuid(&brandString[0], 0x80000002); + //__cpuid(&brandString[4], 0x80000003); + //__cpuid(&brandString[8], 0x80000004); + + CHAR brandString[49]; + + NtQuerySystemInformation( + SystemProcessorBrandString, + brandString, + sizeof(brandString), + NULL + ); - PhZeroExtendToUtf16Buffer((PSTR)brandString, 48, BrandString); - BrandString[48] = 0; + PhZeroExtendToUtf16Buffer(brandString, 48, BrandString); + BrandString[48] = UNICODE_NULL; } BOOLEAN PhSipGetCpuFrequencyFromDistribution( diff --git a/ProcessHacker/sysscio.c b/ProcessHacker/sysscio.c index 2f72b2b37ffa..2ba206292dc8 100644 --- a/ProcessHacker/sysscio.c +++ b/ProcessHacker/sysscio.c @@ -21,11 +21,12 @@ */ #include +#include #include #include #include -#include +#include static PPH_SYSINFO_SECTION IoSection; static HWND IoDialog; @@ -143,9 +144,9 @@ BOOLEAN PhSipIoSectionCallback( PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( L"R: %s\nW: %s\nO: %s%s\n%s", - PhaFormatSize(ioRead, -1)->Buffer, - PhaFormatSize(ioWrite, -1)->Buffer, - PhaFormatSize(ioOther, -1)->Buffer, + PhaFormatSize(ioRead, ULONG_MAX)->Buffer, + PhaFormatSize(ioWrite, ULONG_MAX)->Buffer, + PhaFormatSize(ioOther, ULONG_MAX)->Buffer, PhGetStringOrEmpty(PhSipGetMaxIoString(getTooltipText->Index)), PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer )); @@ -227,7 +228,7 @@ INT_PTR CALLBACK PhSipIoDialogProc( graphItem = PhAddLayoutItem(&IoLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); panelItem = PhAddLayoutItem(&IoLayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - SendMessage(GetDlgItem(hwndDlg, IDC_TITLE), WM_SETFONT, (WPARAM)IoSection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_TITLE), IoSection->Parameters->LargeFont, FALSE); IoPanel = CreateDialog(PhInstanceHandle, MAKEINTRESOURCE(IDD_SYSINFO_IOPANEL), hwndDlg, PhSipIoPanelDialogProc); ShowWindow(IoPanel, SW_SHOW); @@ -242,7 +243,7 @@ INT_PTR CALLBACK PhSipIoDialogProc( 3, 3, IoDialog, - (HMENU)IDC_IO, + NULL, PhInstanceHandle, NULL ); @@ -379,9 +380,9 @@ VOID PhSipNotifyIoGraph( PhMoveReference(&IoGraphState.TooltipText, PhFormatString( L"R: %s\nW: %s\nO: %s%s\n%s", - PhaFormatSize(ioRead, -1)->Buffer, - PhaFormatSize(ioWrite, -1)->Buffer, - PhaFormatSize(ioOther, -1)->Buffer, + PhaFormatSize(ioRead, ULONG_MAX)->Buffer, + PhaFormatSize(ioWrite, ULONG_MAX)->Buffer, + PhaFormatSize(ioOther, ULONG_MAX)->Buffer, PhGetStringOrEmpty(PhSipGetMaxIoString(getTooltipText->Index)), PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer )); @@ -418,7 +419,7 @@ VOID PhSipUpdateIoGraph( ) { IoGraphState.Valid = FALSE; - IoGraphState.TooltipIndex = -1; + IoGraphState.TooltipIndex = ULONG_MAX; Graph_MoveGrid(IoGraphHandle, 1); Graph_Draw(IoGraphHandle); Graph_UpdateTooltip(IoGraphHandle); @@ -433,38 +434,38 @@ VOID PhSipUpdateIoPanel( if (IoTicked > 1) { - SetDlgItemText(IoPanel, IDC_ZREADSDELTA_V, PhaFormatUInt64(IoReadDelta.Delta, TRUE)->Buffer); - SetDlgItemText(IoPanel, IDC_ZWRITESDELTA_V, PhaFormatUInt64(IoWriteDelta.Delta, TRUE)->Buffer); - SetDlgItemText(IoPanel, IDC_ZOTHERDELTA_V, PhaFormatUInt64(IoOtherDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZREADSDELTA_V, PhaFormatUInt64(IoReadDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZWRITESDELTA_V, PhaFormatUInt64(IoWriteDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZOTHERDELTA_V, PhaFormatUInt64(IoOtherDelta.Delta, TRUE)->Buffer); } else { - SetDlgItemText(IoPanel, IDC_ZREADSDELTA_V, L"-"); - SetDlgItemText(IoPanel, IDC_ZWRITESDELTA_V, L"-"); - SetDlgItemText(IoPanel, IDC_ZOTHERDELTA_V, L"-"); + PhSetDialogItemText(IoPanel, IDC_ZREADSDELTA_V, L"-"); + PhSetDialogItemText(IoPanel, IDC_ZWRITESDELTA_V, L"-"); + PhSetDialogItemText(IoPanel, IDC_ZOTHERDELTA_V, L"-"); } if (PhIoReadHistory.Count != 0) { - SetDlgItemText(IoPanel, IDC_ZREADBYTESDELTA_V, PhaFormatSize(PhIoReadDelta.Delta, -1)->Buffer); - SetDlgItemText(IoPanel, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(PhIoWriteDelta.Delta, -1)->Buffer); - SetDlgItemText(IoPanel, IDC_ZOTHERBYTESDELTA_V, PhaFormatSize(PhIoOtherDelta.Delta, -1)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZREADBYTESDELTA_V, PhaFormatSize(PhIoReadDelta.Delta, ULONG_MAX)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(PhIoWriteDelta.Delta, ULONG_MAX)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZOTHERBYTESDELTA_V, PhaFormatSize(PhIoOtherDelta.Delta, ULONG_MAX)->Buffer); } else { - SetDlgItemText(IoPanel, IDC_ZREADBYTESDELTA_V, L"-"); - SetDlgItemText(IoPanel, IDC_ZWRITEBYTESDELTA_V, L"-"); - SetDlgItemText(IoPanel, IDC_ZOTHERBYTESDELTA_V, L"-"); + PhSetDialogItemText(IoPanel, IDC_ZREADBYTESDELTA_V, L"-"); + PhSetDialogItemText(IoPanel, IDC_ZWRITEBYTESDELTA_V, L"-"); + PhSetDialogItemText(IoPanel, IDC_ZOTHERBYTESDELTA_V, L"-"); } // I/O Totals - SetDlgItemText(IoPanel, IDC_ZREADS_V, PhaFormatUInt64(PhPerfInformation.IoReadOperationCount, TRUE)->Buffer); - SetDlgItemText(IoPanel, IDC_ZREADBYTES_V, PhaFormatSize(PhPerfInformation.IoReadTransferCount.QuadPart, -1)->Buffer); - SetDlgItemText(IoPanel, IDC_ZWRITES_V, PhaFormatUInt64(PhPerfInformation.IoWriteOperationCount, TRUE)->Buffer); - SetDlgItemText(IoPanel, IDC_ZWRITEBYTES_V, PhaFormatSize(PhPerfInformation.IoWriteTransferCount.QuadPart, -1)->Buffer); - SetDlgItemText(IoPanel, IDC_ZOTHER_V, PhaFormatUInt64(PhPerfInformation.IoOtherOperationCount, TRUE)->Buffer); - SetDlgItemText(IoPanel, IDC_ZOTHERBYTES_V, PhaFormatSize(PhPerfInformation.IoOtherTransferCount.QuadPart, -1)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZREADS_V, PhaFormatUInt64(PhPerfInformation.IoReadOperationCount, TRUE)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZREADBYTES_V, PhaFormatSize(PhPerfInformation.IoReadTransferCount.QuadPart, ULONG_MAX)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZWRITES_V, PhaFormatUInt64(PhPerfInformation.IoWriteOperationCount, TRUE)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZWRITEBYTES_V, PhaFormatSize(PhPerfInformation.IoWriteTransferCount.QuadPart, ULONG_MAX)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZOTHER_V, PhaFormatUInt64(PhPerfInformation.IoOtherOperationCount, TRUE)->Buffer); + PhSetDialogItemText(IoPanel, IDC_ZOTHERBYTES_V, PhaFormatSize(PhPerfInformation.IoOtherTransferCount.QuadPart, ULONG_MAX)->Buffer); } PPH_PROCESS_RECORD PhSipReferenceMaxIoRecord( @@ -512,8 +513,8 @@ PPH_STRING PhSipGetMaxIoString( L"\n%s (%u): R+O: %s, W: %s", maxProcessRecord->ProcessName->Buffer, HandleToUlong(maxProcessRecord->ProcessId), - PhaFormatSize(maxIoReadOther, -1)->Buffer, - PhaFormatSize(maxIoWrite, -1)->Buffer + PhaFormatSize(maxIoReadOther, ULONG_MAX)->Buffer, + PhaFormatSize(maxIoWrite, ULONG_MAX)->Buffer ); } else @@ -521,8 +522,8 @@ PPH_STRING PhSipGetMaxIoString( maxUsageString = PhaFormatString( L"\n%s: R+O: %s, W: %s", maxProcessRecord->ProcessName->Buffer, - PhaFormatSize(maxIoReadOther, -1)->Buffer, - PhaFormatSize(maxIoWrite, -1)->Buffer + PhaFormatSize(maxIoReadOther, ULONG_MAX)->Buffer, + PhaFormatSize(maxIoWrite, ULONG_MAX)->Buffer ); } #else diff --git a/ProcessHacker/sysscmem.c b/ProcessHacker/sysscmem.c index 9d26867685c1..69f1f255420e 100644 --- a/ProcessHacker/sysscmem.c +++ b/ProcessHacker/sysscmem.c @@ -27,9 +27,10 @@ #include #include #include +#include #include -#include +#include static PPH_SYSINFO_SECTION MemorySection; static HWND MemoryDialog; @@ -49,6 +50,8 @@ static PH_UINT32_DELTA PageFaultsDelta; static PH_UINT32_DELTA PageReadsDelta; static PH_UINT32_DELTA PagefileWritesDelta; static PH_UINT32_DELTA MappedWritesDelta; +static PH_UINT64_DELTA MappedIoReadDelta; +static PH_UINT64_DELTA MappedIoWritesDelta; static BOOLEAN MmAddressesInitialized; static PSIZE_T MmSizeOfPagedPoolInBytes; static PSIZE_T MmMaximumNonPagedPoolInBytes; @@ -164,7 +167,7 @@ BOOLEAN PhSipMemorySectionCallback( PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( L"Commit charge: %s\n%s", - PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), -1)->Buffer, + PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), ULONG_MAX)->Buffer, PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer )); getTooltipText->Text = Section->GraphState.TooltipText->sr; @@ -175,7 +178,7 @@ BOOLEAN PhSipMemorySectionCallback( PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( L"Physical memory: %s\n%s", - PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), -1)->Buffer, + PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), ULONG_MAX)->Buffer, PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer )); getTooltipText->Text = Section->GraphState.TooltipText->sr; @@ -230,6 +233,8 @@ VOID PhSipInitializeMemoryDialog( PhInitializeDelta(&PageReadsDelta); PhInitializeDelta(&PagefileWritesDelta); PhInitializeDelta(&MappedWritesDelta); + PhInitializeDelta(&MappedIoReadDelta); + PhInitializeDelta(&MappedIoWritesDelta); PhInitializeGraphState(&CommitGraphState); PhInitializeGraphState(&PhysicalGraphState); @@ -263,11 +268,11 @@ VOID PhSipTickMemoryDialog( PhUpdateDelta(&PageReadsDelta, PhPerfInformation.PageReadCount); PhUpdateDelta(&PagefileWritesDelta, PhPerfInformation.DirtyPagesWriteCount); PhUpdateDelta(&MappedWritesDelta, PhPerfInformation.MappedPagesWriteCount); + PhUpdateDelta(&MappedIoReadDelta, UInt32x32To64(PhPerfInformation.PageReadCount, PAGE_SIZE)); + PhUpdateDelta(&MappedIoWritesDelta, PhPerfInformation.MappedPagesWriteCount + PhPerfInformation.DirtyPagesWriteCount + PhPerfInformation.CcLazyWritePages * PAGE_SIZE); - MemoryTicked++; - - if (MemoryTicked > 2) - MemoryTicked = 2; + if (MemoryTicked < 2) + MemoryTicked++; PhSipUpdateMemoryGraphs(); PhSipUpdateMemoryPanel(); @@ -295,31 +300,31 @@ INT_PTR CALLBACK PhSipMemoryDialogProc( PhInitializeLayoutManager(&MemoryLayoutManager, hwndDlg); PhAddLayoutItem(&MemoryLayoutManager, GetDlgItem(hwndDlg, IDC_TOTALPHYSICAL), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); graphItem = PhAddLayoutItem(&MemoryLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); - MemoryGraphMargin = graphItem->Margin; panelItem = PhAddLayoutItem(&MemoryLayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + MemoryGraphMargin = graphItem->Margin; - SendMessage(GetDlgItem(hwndDlg, IDC_TITLE), WM_SETFONT, (WPARAM)MemorySection->Parameters->LargeFont, FALSE); - SendMessage(GetDlgItem(hwndDlg, IDC_TOTALPHYSICAL), WM_SETFONT, (WPARAM)MemorySection->Parameters->MediumFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_TITLE), MemorySection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_TOTALPHYSICAL), MemorySection->Parameters->MediumFont, FALSE); if (!getPhysicallyInstalledSystemMemory) - getPhysicallyInstalledSystemMemory = PhGetModuleProcAddress(L"kernel32.dll", "GetPhysicallyInstalledSystemMemory"); + getPhysicallyInstalledSystemMemory = PhGetDllProcedureAddress(L"kernel32.dll", "GetPhysicallyInstalledSystemMemory", 0); InstalledMemory = 0; if (getPhysicallyInstalledSystemMemory && getPhysicallyInstalledSystemMemory(&InstalledMemory)) { - SetDlgItemText(hwndDlg, IDC_TOTALPHYSICAL, - PhaConcatStrings2(PhaFormatSize(InstalledMemory * 1024, -1)->Buffer, L" installed")->Buffer); + PhSetDialogItemText(hwndDlg, IDC_TOTALPHYSICAL, + PhaConcatStrings2(PhaFormatSize(InstalledMemory * 1024, ULONG_MAX)->Buffer, L" installed")->Buffer); } else { - SetDlgItemText(hwndDlg, IDC_TOTALPHYSICAL, - PhaConcatStrings2(PhaFormatSize(UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages, PAGE_SIZE), -1)->Buffer, L" total")->Buffer); + PhSetDialogItemText(hwndDlg, IDC_TOTALPHYSICAL, + PhaConcatStrings2(PhaFormatSize(UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages, PAGE_SIZE), ULONG_MAX)->Buffer, L" total")->Buffer); } MemoryPanel = CreateDialog( PhInstanceHandle, - WindowsVersion >= WINDOWS_VISTA ? MAKEINTRESOURCE(IDD_SYSINFO_MEMPANEL) : MAKEINTRESOURCE(IDD_SYSINFO_MEMPANELXP), + MAKEINTRESOURCE(IDD_SYSINFO_MEMPANEL), hwndDlg, PhSipMemoryPanelDialogProc ); @@ -335,7 +340,7 @@ INT_PTR CALLBACK PhSipMemoryDialogProc( 3, 3, MemoryDialog, - (HMENU)IDC_COMMIT, + NULL, PhInstanceHandle, NULL ); @@ -350,7 +355,7 @@ INT_PTR CALLBACK PhSipMemoryDialogProc( 3, 3, MemoryDialog, - (HMENU)IDC_PHYSICAL, + NULL, PhInstanceHandle, NULL ); @@ -546,7 +551,7 @@ VOID PhSipNotifyCommitGraph( PhMoveReference(&CommitGraphState.TooltipText, PhFormatString( L"Commit charge: %s\n%s", - PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), -1)->Buffer, + PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), ULONG_MAX)->Buffer, PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer )); } @@ -614,7 +619,7 @@ VOID PhSipNotifyPhysicalGraph( PhMoveReference(&PhysicalGraphState.TooltipText, PhFormatString( L"Physical memory: %s\n%s", - PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), -1)->Buffer, + PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), ULONG_MAX)->Buffer, PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer )); } @@ -631,14 +636,14 @@ VOID PhSipUpdateMemoryGraphs( ) { CommitGraphState.Valid = FALSE; - CommitGraphState.TooltipIndex = -1; + CommitGraphState.TooltipIndex = ULONG_MAX; Graph_MoveGrid(CommitGraphHandle, 1); Graph_Draw(CommitGraphHandle); Graph_UpdateTooltip(CommitGraphHandle); InvalidateRect(CommitGraphHandle, NULL, FALSE); PhysicalGraphState.Valid = FALSE; - PhysicalGraphState.TooltipIndex = -1; + PhysicalGraphState.TooltipIndex = ULONG_MAX; Graph_MoveGrid(PhysicalGraphHandle, 1); Graph_Draw(PhysicalGraphHandle); Graph_UpdateTooltip(PhysicalGraphHandle); @@ -655,68 +660,68 @@ VOID PhSipUpdateMemoryPanel( // Commit charge - SetDlgItemText(MemoryPanel, IDC_ZCOMMITCURRENT_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE), -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZCOMMITPEAK_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.PeakCommitment, PAGE_SIZE), -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZCOMMITLIMIT_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.CommitLimit, PAGE_SIZE), -1)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZCOMMITCURRENT_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.CommittedPages, PAGE_SIZE), ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZCOMMITPEAK_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.PeakCommitment, PAGE_SIZE), ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZCOMMITLIMIT_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.CommitLimit, PAGE_SIZE), ULONG_MAX)->Buffer); // Physical memory - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALCURRENT_V, - PhaFormatSize(UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages, PAGE_SIZE), -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALTOTAL_V, - PhaFormatSize(UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages, PAGE_SIZE), -1)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALCURRENT_V, + PhaFormatSize(UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages - PhPerfInformation.AvailablePages, PAGE_SIZE), ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALTOTAL_V, + PhaFormatSize(UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages, PAGE_SIZE), ULONG_MAX)->Buffer); if (InstalledMemory != 0) { - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALRESERVED_V, - PhaFormatSize(InstalledMemory * 1024 - UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages, PAGE_SIZE), -1)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALRESERVED_V, + PhaFormatSize(InstalledMemory * 1024 - UInt32x32To64(PhSystemBasicInformation.NumberOfPhysicalPages, PAGE_SIZE), ULONG_MAX)->Buffer); } else { - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALRESERVED_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALRESERVED_V, L"-"); } - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALCACHEWS_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentSystemCachePage, PAGE_SIZE), -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALKERNELWS_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentSystemCodePage, PAGE_SIZE), -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZPHYSICALDRIVERWS_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentSystemDriverPage, PAGE_SIZE), -1)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALCACHEWS_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentSystemCachePage, PAGE_SIZE), ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALKERNELWS_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentSystemCodePage, PAGE_SIZE), ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPHYSICALDRIVERWS_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentSystemDriverPage, PAGE_SIZE), ULONG_MAX)->Buffer); // Paged pool - SetDlgItemText(MemoryPanel, IDC_ZPAGEDWORKINGSET_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentPagedPoolPage, PAGE_SIZE), -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZPAGEDVIRTUALSIZE_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.PagedPoolPages, PAGE_SIZE), -1)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDWORKINGSET_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.ResidentPagedPoolPage, PAGE_SIZE), ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDVIRTUALSIZE_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.PagedPoolPages, PAGE_SIZE), ULONG_MAX)->Buffer); if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZPAGEDALLOCSDELTA_V, PhaFormatUInt64(PagedAllocsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDALLOCSDELTA_V, PhaFormatUInt64(PagedAllocsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZPAGEDALLOCSDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDALLOCSDELTA_V, L"-"); if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZPAGEDFREESDELTA_V, PhaFormatUInt64(PagedFreesDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDFREESDELTA_V, PhaFormatUInt64(PagedFreesDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZPAGEDFREESDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDFREESDELTA_V, L"-"); // Non-paged pool - SetDlgItemText(MemoryPanel, IDC_ZNONPAGEDUSAGE_V, - PhaFormatSize(UInt32x32To64(PhPerfInformation.NonPagedPoolPages, PAGE_SIZE), -1)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZNONPAGEDUSAGE_V, + PhaFormatSize(UInt32x32To64(PhPerfInformation.NonPagedPoolPages, PAGE_SIZE), ULONG_MAX)->Buffer); if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZNONPAGEDALLOCSDELTA_V, PhaFormatUInt64(PagedAllocsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZNONPAGEDALLOCSDELTA_V, PhaFormatUInt64(PagedAllocsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZNONPAGEDALLOCSDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZNONPAGEDALLOCSDELTA_V, L"-"); if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZNONPAGEDFREESDELTA_V, PhaFormatUInt64(NonPagedFreesDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZNONPAGEDFREESDELTA_V, PhaFormatUInt64(NonPagedFreesDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZNONPAGEDFREESDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZNONPAGEDFREESDELTA_V, L"-"); // Pools (KPH) @@ -728,12 +733,12 @@ VOID PhSipUpdateMemoryPanel( PhSipGetPoolLimits(&paged, &nonPaged); if (paged != -1) - pagedLimit = PhaFormatSize(paged, -1)->Buffer; + pagedLimit = PhaFormatSize(paged, ULONG_MAX)->Buffer; else pagedLimit = L"N/A"; if (nonPaged != -1) - nonPagedLimit = PhaFormatSize(nonPaged, -1)->Buffer; + nonPagedLimit = PhaFormatSize(nonPaged, ULONG_MAX)->Buffer; else nonPagedLimit = L"N/A"; } @@ -749,91 +754,100 @@ VOID PhSipUpdateMemoryPanel( } } - SetDlgItemText(MemoryPanel, IDC_ZPAGEDLIMIT_V, pagedLimit); - SetDlgItemText(MemoryPanel, IDC_ZNONPAGEDLIMIT_V, nonPagedLimit); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGEDLIMIT_V, pagedLimit); + PhSetDialogItemText(MemoryPanel, IDC_ZNONPAGEDLIMIT_V, nonPagedLimit); // Paging if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZPAGINGPAGEFAULTSDELTA_V, PhaFormatUInt64(PageFaultsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGPAGEFAULTSDELTA_V, PhaFormatUInt64(PageFaultsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZPAGINGPAGEFAULTSDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGPAGEFAULTSDELTA_V, L"-"); if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZPAGINGPAGEREADSDELTA_V, PhaFormatUInt64(PageReadsDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGPAGEREADSDELTA_V, PhaFormatUInt64(PageReadsDelta.Delta, TRUE)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZPAGINGPAGEREADSDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGPAGEREADSDELTA_V, L"-"); + + if (MemoryTicked > 1) + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGPAGEFILEWRITESDELTA_V, PhaFormatUInt64(PagefileWritesDelta.Delta, TRUE)->Buffer); + else + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGPAGEFILEWRITESDELTA_V, L"-"); + + if (MemoryTicked > 1) + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGMAPPEDWRITESDELTA_V, PhaFormatUInt64(MappedWritesDelta.Delta, TRUE)->Buffer); + else + PhSetDialogItemText(MemoryPanel, IDC_ZPAGINGMAPPEDWRITESDELTA_V, L"-"); + + // Mapped if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZPAGINGPAGEFILEWRITESDELTA_V, PhaFormatUInt64(PagefileWritesDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZMAPPEDREADIO, PhaFormatSize(MappedIoReadDelta.Delta, ULONG_MAX)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZPAGINGPAGEFILEWRITESDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZMAPPEDREADIO, L"-"); if (MemoryTicked > 1) - SetDlgItemText(MemoryPanel, IDC_ZPAGINGMAPPEDWRITESDELTA_V, PhaFormatUInt64(MappedWritesDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZMAPPEDWRITEIO, PhaFormatSize(MappedIoWritesDelta.Delta, ULONG_MAX)->Buffer); else - SetDlgItemText(MemoryPanel, IDC_ZPAGINGMAPPEDWRITESDELTA_V, L"-"); + PhSetDialogItemText(MemoryPanel, IDC_ZMAPPEDWRITEIO, L"-"); // Memory lists - if (WindowsVersion >= WINDOWS_VISTA) + if (NT_SUCCESS(NtQuerySystemInformation( + SystemMemoryListInformation, + &memoryListInfo, + sizeof(SYSTEM_MEMORY_LIST_INFORMATION), + NULL + ))) { - if (NT_SUCCESS(NtQuerySystemInformation( - SystemMemoryListInformation, - &memoryListInfo, - sizeof(SYSTEM_MEMORY_LIST_INFORMATION), - NULL - ))) - { - ULONG_PTR standbyPageCount; - ULONG_PTR repurposedPageCount; - ULONG i; + ULONG_PTR standbyPageCount; + ULONG_PTR repurposedPageCount; + ULONG i; - standbyPageCount = 0; - repurposedPageCount = 0; + standbyPageCount = 0; + repurposedPageCount = 0; - for (i = 0; i < 8; i++) - { - standbyPageCount += memoryListInfo.PageCountByPriority[i]; - repurposedPageCount += memoryListInfo.RepurposedPagesByPriority[i]; - } - - SetDlgItemText(MemoryPanel, IDC_ZLISTZEROED_V, PhaFormatSize((ULONG64)memoryListInfo.ZeroPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTFREE_V, PhaFormatSize((ULONG64)memoryListInfo.FreePageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIED_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIEDNOWRITE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedNoWritePageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY_V, PhaFormatSize((ULONG64)standbyPageCount * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY0_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[0] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY1_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[1] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY2_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[2] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY3_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[3] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY4_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[4] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY5_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[5] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY6_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[6] * PAGE_SIZE, -1)->Buffer); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY7_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[7] * PAGE_SIZE, -1)->Buffer); - - if (WindowsVersion >= WINDOWS_8) - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIEDPAGEFILE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCountPageFile * PAGE_SIZE, -1)->Buffer); - else - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIEDPAGEFILE_V, L"N/A"); - } - else + for (i = 0; i < 8; i++) { - SetDlgItemText(MemoryPanel, IDC_ZLISTZEROED_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTFREE_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIED_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIEDNOWRITE_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTMODIFIEDPAGEFILE_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY0_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY1_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY2_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY3_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY4_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY5_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY6_V, L"N/A"); - SetDlgItemText(MemoryPanel, IDC_ZLISTSTANDBY7_V, L"N/A"); + standbyPageCount += memoryListInfo.PageCountByPriority[i]; + repurposedPageCount += memoryListInfo.RepurposedPagesByPriority[i]; } + + PhSetDialogItemText(MemoryPanel, IDC_ZLISTZEROED_V, PhaFormatSize((ULONG64)memoryListInfo.ZeroPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTFREE_V, PhaFormatSize((ULONG64)memoryListInfo.FreePageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIED_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIEDNOWRITE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedNoWritePageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY_V, PhaFormatSize((ULONG64)standbyPageCount * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY0_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[0] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY1_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[1] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY2_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[2] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY3_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[3] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY4_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[4] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY5_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[5] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY6_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[6] * PAGE_SIZE, ULONG_MAX)->Buffer); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY7_V, PhaFormatSize((ULONG64)memoryListInfo.PageCountByPriority[7] * PAGE_SIZE, ULONG_MAX)->Buffer); + + if (WindowsVersion >= WINDOWS_8) + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIEDPAGEFILE_V, PhaFormatSize((ULONG64)memoryListInfo.ModifiedPageCountPageFile * PAGE_SIZE, ULONG_MAX)->Buffer); + else + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIEDPAGEFILE_V, L"N/A"); + } + else + { + PhSetDialogItemText(MemoryPanel, IDC_ZLISTZEROED_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTFREE_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIED_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIEDNOWRITE_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTMODIFIEDPAGEFILE_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY0_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY1_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY2_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY3_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY4_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY5_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY6_V, L"N/A"); + PhSetDialogItemText(MemoryPanel, IDC_ZLISTSTANDBY7_V, L"N/A"); } } diff --git a/ProcessHacker/thrdlist.c b/ProcessHacker/thrdlist.c index 1800cbdadfdc..314ba487aaf2 100644 --- a/ProcessHacker/thrdlist.c +++ b/ProcessHacker/thrdlist.c @@ -1,723 +1,1101 @@ -/* - * Process Hacker - - * thread list - * - * Copyright (C) 2011-2012 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -BOOLEAN PhpThreadNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG PhpThreadNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpDestroyThreadNode( - _In_ PPH_THREAD_NODE ThreadNode - ); - -VOID PhpRemoveThreadNode( - _In_ PPH_THREAD_NODE ThreadNode, - _In_ PPH_THREAD_LIST_CONTEXT Context - ); - -LONG PhpThreadTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ); - -BOOLEAN NTAPI PhpThreadTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -VOID PhInitializeThreadList( - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle, - _Out_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - HWND hwnd; - - memset(Context, 0, sizeof(PH_THREAD_LIST_CONTEXT)); - Context->EnableStateHighlighting = TRUE; - - Context->NodeHashtable = PhCreateHashtable( - sizeof(PPH_THREAD_NODE), - PhpThreadNodeHashtableEqualFunction, - PhpThreadNodeHashtableHashFunction, - 100 - ); - Context->NodeList = PhCreateList(100); - - Context->ParentWindowHandle = ParentWindowHandle; - Context->TreeNewHandle = TreeNewHandle; - hwnd = TreeNewHandle; - PhSetControlTheme(hwnd, L"explorer"); - - TreeNew_SetCallback(hwnd, PhpThreadTreeNewCallback, Context); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, PHTHTLC_TID, TRUE, L"TID", 50, PH_ALIGN_RIGHT, 0, DT_RIGHT); - PhAddTreeNewColumnEx(hwnd, PHTHTLC_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, PHTHTLC_CYCLESDELTA, TRUE, L"Cycles delta", 80, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHTHTLC_STARTADDRESS, TRUE, L"Start address", 180, PH_ALIGN_LEFT, 3, 0); - PhAddTreeNewColumnEx(hwnd, PHTHTLC_PRIORITY, TRUE, L"Priority", 80, PH_ALIGN_LEFT, 4, 0, TRUE); - PhAddTreeNewColumn(hwnd, PHTHTLC_SERVICE, FALSE, L"Service", 100, PH_ALIGN_LEFT, -1, 0); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetSort(hwnd, PHTHTLC_CYCLESDELTA, DescendingSortOrder); - - PhCmInitializeManager(&Context->Cm, hwnd, PHTHTLC_MAXIMUM, PhpThreadTreeNewPostSortFunction); -} - -VOID PhDeleteThreadList( - _In_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - ULONG i; - - PhCmDeleteManager(&Context->Cm); - - for (i = 0; i < Context->NodeList->Count; i++) - PhpDestroyThreadNode(Context->NodeList->Items[i]); - - PhDereferenceObject(Context->NodeHashtable); - PhDereferenceObject(Context->NodeList); -} - -BOOLEAN PhpThreadNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_THREAD_NODE threadNode1 = *(PPH_THREAD_NODE *)Entry1; - PPH_THREAD_NODE threadNode2 = *(PPH_THREAD_NODE *)Entry2; - - return threadNode1->ThreadId == threadNode2->ThreadId; -} - -ULONG PhpThreadNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return HandleToUlong((*(PPH_THREAD_NODE *)Entry)->ThreadId) / 4; -} - -VOID PhLoadSettingsThreadList( - _Inout_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - PH_TREENEW_COLUMN column; - ULONG sortColumn; - PH_SORT_ORDER sortOrder; - - if (!Context->UseCycleTime) - { - column.Id = PHTHTLC_CYCLESDELTA; - column.Text = L"Context switches delta"; - TreeNew_SetColumn(Context->TreeNewHandle, TN_COLUMN_TEXT, &column); - } - - if (Context->HasServices) - { - column.Id = PHTHTLC_SERVICE; - column.Visible = TRUE; - TreeNew_SetColumn(Context->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column); - } - - settings = PhGetStringSetting(L"ThreadTreeListColumns"); - sortSettings = PhGetStringSetting(L"ThreadTreeListSort"); - PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, PH_CM_COLUMN_WIDTHS_ONLY, &settings->sr, &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); - - TreeNew_GetSort(Context->TreeNewHandle, &sortColumn, &sortOrder); - - // Make sure we're not sorting by an invisible column. - if (sortOrder != NoSortOrder && !(TreeNew_GetColumn(Context->TreeNewHandle, sortColumn, &column) && column.Visible)) - { - TreeNew_SetSort(Context->TreeNewHandle, PHTHTLC_CYCLESDELTA, DescendingSortOrder); - } -} - -VOID PhSaveSettingsThreadList( - _Inout_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - PPH_STRING settings; - PPH_STRING sortSettings; - - settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, PH_CM_COLUMN_WIDTHS_ONLY, &sortSettings); - PhSetStringSetting2(L"ThreadTreeListColumns", &settings->sr); - PhSetStringSetting2(L"ThreadTreeListSort", &sortSettings->sr); - PhDereferenceObject(settings); - PhDereferenceObject(sortSettings); -} - -PPH_THREAD_NODE PhAddThreadNode( - _Inout_ PPH_THREAD_LIST_CONTEXT Context, - _In_ PPH_THREAD_ITEM ThreadItem, - _In_ BOOLEAN FirstRun - ) -{ - PPH_THREAD_NODE threadNode; - - threadNode = PhAllocate(PhEmGetObjectSize(EmThreadNodeType, sizeof(PH_THREAD_NODE))); - memset(threadNode, 0, sizeof(PH_THREAD_NODE)); - PhInitializeTreeNewNode(&threadNode->Node); - - if (Context->EnableStateHighlighting && !FirstRun) - { - PhChangeShStateTn( - &threadNode->Node, - &threadNode->ShState, - &Context->NodeStateList, - NewItemState, - PhCsColorNew, - NULL - ); - } - - threadNode->ThreadId = ThreadItem->ThreadId; - threadNode->ThreadItem = ThreadItem; - PhReferenceObject(ThreadItem); - - memset(threadNode->TextCache, 0, sizeof(PH_STRINGREF) * PHTHTLC_MAXIMUM); - threadNode->Node.TextCache = threadNode->TextCache; - threadNode->Node.TextCacheSize = PHTHTLC_MAXIMUM; - - PhAddEntryHashtable(Context->NodeHashtable, &threadNode); - PhAddItemList(Context->NodeList, threadNode); - - PhEmCallObjectOperation(EmThreadNodeType, threadNode, EmObjectCreate); - - TreeNew_NodesStructured(Context->TreeNewHandle); - - return threadNode; -} - -PPH_THREAD_NODE PhFindThreadNode( - _In_ PPH_THREAD_LIST_CONTEXT Context, - _In_ HANDLE ThreadId - ) -{ - PH_THREAD_NODE lookupThreadNode; - PPH_THREAD_NODE lookupThreadNodePtr = &lookupThreadNode; - PPH_THREAD_NODE *threadNode; - - lookupThreadNode.ThreadId = ThreadId; - - threadNode = (PPH_THREAD_NODE *)PhFindEntryHashtable( - Context->NodeHashtable, - &lookupThreadNodePtr - ); - - if (threadNode) - return *threadNode; - else - return NULL; -} - -VOID PhRemoveThreadNode( - _In_ PPH_THREAD_LIST_CONTEXT Context, - _In_ PPH_THREAD_NODE ThreadNode - ) -{ - // Remove from the hashtable here to avoid problems in case the key is re-used. - PhRemoveEntryHashtable(Context->NodeHashtable, &ThreadNode); - - if (Context->EnableStateHighlighting) - { - PhChangeShStateTn( - &ThreadNode->Node, - &ThreadNode->ShState, - &Context->NodeStateList, - RemovingItemState, - PhCsColorRemoved, - Context->TreeNewHandle - ); - } - else - { - PhpRemoveThreadNode(ThreadNode, Context); - } -} - -VOID PhpDestroyThreadNode( - _In_ PPH_THREAD_NODE ThreadNode - ) -{ - PhEmCallObjectOperation(EmThreadNodeType, ThreadNode, EmObjectDelete); - - if (ThreadNode->CyclesDeltaText) PhDereferenceObject(ThreadNode->CyclesDeltaText); - if (ThreadNode->StartAddressText) PhDereferenceObject(ThreadNode->StartAddressText); - if (ThreadNode->PriorityText) PhDereferenceObject(ThreadNode->PriorityText); - - PhDereferenceObject(ThreadNode->ThreadItem); - - PhFree(ThreadNode); -} - -VOID PhpRemoveThreadNode( - _In_ PPH_THREAD_NODE ThreadNode, - _In_ PPH_THREAD_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after ThreadNode - ) -{ - ULONG index; - - // Remove from list and cleanup. - - if ((index = PhFindItemList(Context->NodeList, ThreadNode)) != -1) - PhRemoveItemList(Context->NodeList, index); - - PhpDestroyThreadNode(ThreadNode); - - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhUpdateThreadNode( - _In_ PPH_THREAD_LIST_CONTEXT Context, - _In_ PPH_THREAD_NODE ThreadNode - ) -{ - memset(ThreadNode->TextCache, 0, sizeof(PH_STRINGREF) * PHTHTLC_MAXIMUM); - - ThreadNode->ValidMask = 0; - PhInvalidateTreeNewNode(&ThreadNode->Node, TN_CACHE_COLOR); - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID PhTickThreadNodes( - _In_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - PH_TICK_SH_STATE_TN(PH_THREAD_NODE, ShState, Context->NodeStateList, PhpRemoveThreadNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context); -} - -#define SORT_FUNCTION(Column) PhpThreadTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpThreadTreeNewCompare##Column( \ - _In_ void *_context, \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPH_THREAD_NODE node1 = *(PPH_THREAD_NODE *)_elem1; \ - PPH_THREAD_NODE node2 = *(PPH_THREAD_NODE *)_elem2; \ - PPH_THREAD_ITEM threadItem1 = node1->ThreadItem; \ - PPH_THREAD_ITEM threadItem2 = node2->ThreadItem; \ - PPH_THREAD_LIST_CONTEXT context = (PPH_THREAD_LIST_CONTEXT)_context; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId); \ - \ - return PhModifySort(sortResult, context->TreeNewSortOrder); \ -} - -LONG PhpThreadTreeNewPostSortFunction( - _In_ LONG Result, - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ PH_SORT_ORDER SortOrder - ) -{ - if (Result == 0) - Result = uintptrcmp((ULONG_PTR)((PPH_THREAD_NODE)Node1)->ThreadId, (ULONG_PTR)((PPH_THREAD_NODE)Node2)->ThreadId); - - return PhModifySort(Result, SortOrder); -} - -BEGIN_SORT_FUNCTION(Tid) -{ - sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Cpu) -{ - sortResult = singlecmp(threadItem1->CpuUsage, threadItem2->CpuUsage); - - if (sortResult == 0) - { - if (context->UseCycleTime) - sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta); - else - sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta); - } -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(CyclesDelta) -{ - if (context->UseCycleTime) - sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta); - else - sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(StartAddress) -{ - sortResult = PhCompareStringWithNull(threadItem1->StartAddressString, threadItem2->StartAddressString, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Priority) -{ - sortResult = intcmp(threadItem1->BasePriorityIncrement, threadItem2->BasePriorityIncrement); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Service) -{ - sortResult = PhCompareStringWithNull(threadItem1->ServiceName, threadItem2->ServiceName, TRUE); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PhpThreadTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PPH_THREAD_LIST_CONTEXT context; - PPH_THREAD_NODE node; - - context = Context; - - if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) - return TRUE; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Tid), - SORT_FUNCTION(Cpu), - SORT_FUNCTION(CyclesDelta), - SORT_FUNCTION(StartAddress), - SORT_FUNCTION(Priority), - SORT_FUNCTION(Service) - }; - int (__cdecl *sortFunction)(void *, const void *, const void *); - - if (!PhCmForwardSort( - (PPH_TREENEW_NODE *)context->NodeList->Items, - context->NodeList->Count, - context->TreeNewSortColumn, - context->TreeNewSortOrder, - &context->Cm - )) - { - if (context->TreeNewSortColumn < PHTHTLC_MAXIMUM) - sortFunction = sortFunctions[context->TreeNewSortColumn]; - else - sortFunction = NULL; - } - else - { - sortFunction = NULL; - } - - if (sortFunction) - { - qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); - } - - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; - getChildren->NumberOfChildren = context->NodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PPH_THREAD_ITEM threadItem; - - node = (PPH_THREAD_NODE)getCellText->Node; - threadItem = node->ThreadItem; - - switch (getCellText->Id) - { - case PHTHTLC_TID: - PhInitializeStringRefLongHint(&getCellText->Text, threadItem->ThreadIdString); - break; - case PHTHTLC_CPU: - { - FLOAT cpuUsage; - - cpuUsage = threadItem->CpuUsage * 100; - - if (cpuUsage >= 0.01) - { - PH_FORMAT format; - SIZE_T returnLength; - - PhInitFormatF(&format, cpuUsage, 2); - - if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) - { - getCellText->Text.Buffer = node->CpuUsageText; - getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator - } - } - else if (cpuUsage != 0 && PhCsShowCpuBelow001) - { - PH_FORMAT format[2]; - SIZE_T returnLength; - - PhInitFormatS(&format[0], L"< "); - PhInitFormatF(&format[1], 0.01, 2); - - if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) - { - getCellText->Text.Buffer = node->CpuUsageText; - getCellText->Text.Length = returnLength - sizeof(WCHAR); - } - } - } - break; - case PHTHTLC_CYCLESDELTA: - if (context->UseCycleTime) - { - if (threadItem->CyclesDelta.Delta != threadItem->CyclesDelta.Value && threadItem->CyclesDelta.Delta != 0) - { - PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(threadItem->CyclesDelta.Delta, TRUE)); - getCellText->Text = node->CyclesDeltaText->sr; - } - } - else - { - if (threadItem->ContextSwitchesDelta.Delta != threadItem->ContextSwitchesDelta.Value && threadItem->ContextSwitchesDelta.Delta != 0) - { - PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(threadItem->ContextSwitchesDelta.Delta, TRUE)); - getCellText->Text = node->CyclesDeltaText->sr; - } - } - break; - case PHTHTLC_STARTADDRESS: - PhSwapReference(&node->StartAddressText, threadItem->StartAddressString); - getCellText->Text = PhGetStringRef(node->StartAddressText); - break; - case PHTHTLC_PRIORITY: - PhMoveReference(&node->PriorityText, PhGetBasePriorityIncrementString(threadItem->BasePriorityIncrement)); - getCellText->Text = PhGetStringRef(node->PriorityText); - break; - case PHTHTLC_SERVICE: - getCellText->Text = PhGetStringRef(threadItem->ServiceName); - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; - PPH_THREAD_ITEM threadItem; - - node = (PPH_THREAD_NODE)getNodeColor->Node; - threadItem = node->ThreadItem; - - if (!threadItem) - ; // Dummy - else if (PhCsUseColorSuspended && threadItem->WaitReason == Suspended) - getNodeColor->BackColor = PhCsColorSuspended; - else if (PhCsUseColorGuiThreads && threadItem->IsGuiThread) - getNodeColor->BackColor = PhCsColorGuiThreads; - - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewSelectionChanged: - { - SendMessage(context->ParentWindowHandle, WM_PH_THREAD_SELECTION_CHANGED, 0, 0); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_COPY, 0); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(context->TreeNewHandle, 0, -1); - break; - case VK_DELETE: - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_TERMINATE, 0); - break; - case VK_RETURN: - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_INSPECT, 0); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - // Customizable columns are disabled until we can figure out how to make it - // co-operate with the column adjustments (e.g. Cycles Delta vs Context Switches Delta, - // Service column). - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = PHTHTLC_CYCLESDELTA; - data.DefaultSortOrder = DescendingSortOrder; - PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_NO_VISIBILITY); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_INSPECT, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); - } - return TRUE; - case TreeNewGetDialogCode: - { - PULONG code = Parameter2; - - if (PtrToUlong(Parameter1) == VK_RETURN) - { - *code = DLGC_WANTMESSAGE; - return TRUE; - } - } - return FALSE; - } - - return FALSE; -} - -PPH_THREAD_ITEM PhGetSelectedThreadItem( - _In_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - PPH_THREAD_ITEM threadItem = NULL; - ULONG i; - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_THREAD_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - { - threadItem = node->ThreadItem; - break; - } - } - - return threadItem; -} - -VOID PhGetSelectedThreadItems( - _In_ PPH_THREAD_LIST_CONTEXT Context, - _Out_ PPH_THREAD_ITEM **Threads, - _Out_ PULONG NumberOfThreads - ) -{ - PH_ARRAY array; - ULONG i; - - PhInitializeArray(&array, sizeof(PVOID), 2); - - for (i = 0; i < Context->NodeList->Count; i++) - { - PPH_THREAD_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemArray(&array, &node->ThreadItem); - } - - *NumberOfThreads = (ULONG)array.Count; - *Threads = PhFinalArrayItems(&array); -} - -VOID PhDeselectAllThreadNodes( - _In_ PPH_THREAD_LIST_CONTEXT Context - ) -{ - TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); -} +/* + * Process Hacker - + * thread list + * + * Copyright (C) 2011-2012 wj32 + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +BOOLEAN PhpThreadNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG PhpThreadNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpDestroyThreadNode( + _In_ PPH_THREAD_NODE ThreadNode + ); + +VOID PhpRemoveThreadNode( + _In_ PPH_THREAD_NODE ThreadNode, + _In_ PPH_THREAD_LIST_CONTEXT Context + ); + +LONG PhpThreadTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); + +BOOLEAN NTAPI PhpThreadTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +VOID PhInitializeThreadList( + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle, + _Out_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + memset(Context, 0, sizeof(PH_THREAD_LIST_CONTEXT)); + Context->EnableStateHighlighting = TRUE; + + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_THREAD_NODE), + PhpThreadNodeHashtableEqualFunction, + PhpThreadNodeHashtableHashFunction, + 100 + ); + Context->NodeList = PhCreateList(100); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + + PhSetControlTheme(TreeNewHandle, L"explorer"); + TreeNew_SetCallback(TreeNewHandle, PhpThreadTreeNewCallback, Context); + TreeNew_SetRedraw(TreeNewHandle, FALSE); + + // Default columns + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_TID, TRUE, L"TID", 50, PH_ALIGN_RIGHT, 0, DT_RIGHT); + PhAddTreeNewColumnEx(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_CYCLESDELTA, TRUE, L"Cycles delta", 80, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_STARTADDRESS, TRUE, L"Start address", 180, PH_ALIGN_LEFT, 3, 0); + PhAddTreeNewColumnEx(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_PRIORITYSYMBOLIC, TRUE, L"Priority (symbolic)", 80, PH_ALIGN_LEFT, 4, 0, TRUE); + // Available columns + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_SERVICE, FALSE, L"Service", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_NAME, FALSE, L"Name", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_STARTED, FALSE, L"Created", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_STARTMODULE, FALSE, L"Start module", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_CONTEXTSWITCHES, FALSE, L"Context switches", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_PRIORITY, FALSE, L"Priority", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_BASEPRIORITY, FALSE, L"Base priority", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_PAGEPRIORITY, FALSE, L"Page priority", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_IOPRIORITY, FALSE, L"I/O priority", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_CYCLES, FALSE, L"Cycles", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_STATE, FALSE, L"State", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_KERNELTIME, FALSE, L"Kernel time", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_USERTIME, FALSE, L"User time", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_IDEALPROCESSOR, FALSE, L"Ideal processor", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_CRITICAL, FALSE, L"Critical", 80, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(TreeNewHandle, PH_THREAD_TREELIST_COLUMN_TIDHEX, FALSE, L"TID (Hex)", 50, PH_ALIGN_RIGHT, ULONG_MAX, DT_RIGHT); + + TreeNew_SetRedraw(TreeNewHandle, TRUE); + TreeNew_SetTriState(TreeNewHandle, TRUE); + //TreeNew_SetSort(TreeNewHandle, PHTHTLC_CYCLESDELTA, DescendingSortOrder); + + PhCmInitializeManager(&Context->Cm, TreeNewHandle, PH_THREAD_TREELIST_COLUMN_MAXIMUM, PhpThreadTreeNewPostSortFunction); + + PhInitializeTreeNewFilterSupport(&Context->TreeFilterSupport, Context->TreeNewHandle, Context->NodeList); +} + +VOID PhDeleteThreadList( + _In_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + ULONG i; + + PhDeleteTreeNewFilterSupport(&Context->TreeFilterSupport); + + PhCmDeleteManager(&Context->Cm); + + for (i = 0; i < Context->NodeList->Count; i++) + PhpDestroyThreadNode(Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +BOOLEAN PhpThreadNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_THREAD_NODE threadNode1 = *(PPH_THREAD_NODE *)Entry1; + PPH_THREAD_NODE threadNode2 = *(PPH_THREAD_NODE *)Entry2; + + return threadNode1->ThreadId == threadNode2->ThreadId; +} + +ULONG PhpThreadNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return HandleToUlong((*(PPH_THREAD_NODE *)Entry)->ThreadId) / 4; +} + +VOID PhLoadSettingsThreadList( + _Inout_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + PH_TREENEW_COLUMN column; + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + settings = PhGetStringSetting(L"ThreadTreeListColumns"); + sortSettings = PhGetStringSetting(L"ThreadTreeListSort"); + Context->Flags = PhGetIntegerSetting(L"ThreadTreeListFlags"); + + PhCmLoadSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &settings->sr, &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); + + TreeNew_GetSort(Context->TreeNewHandle, &sortColumn, &sortOrder); + + // Make sure we're not sorting by an invisible column. + if (sortOrder != NoSortOrder && !(TreeNew_GetColumn(Context->TreeNewHandle, sortColumn, &column) && column.Visible)) + { + TreeNew_SetSort(Context->TreeNewHandle, PH_THREAD_TREELIST_COLUMN_CYCLESDELTA, DescendingSortOrder); + } +} + +VOID PhSaveSettingsThreadList( + _Inout_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + PPH_STRING settings; + PPH_STRING sortSettings; + + settings = PhCmSaveSettingsEx(Context->TreeNewHandle, &Context->Cm, 0, &sortSettings); + + PhSetIntegerSetting(L"ThreadTreeListFlags", Context->Flags); + PhSetStringSetting2(L"ThreadTreeListColumns", &settings->sr); + PhSetStringSetting2(L"ThreadTreeListSort", &sortSettings->sr); + + PhDereferenceObject(settings); + PhDereferenceObject(sortSettings); +} + +VOID PhSetOptionsThreadList( + _Inout_ PPH_THREAD_LIST_CONTEXT Context, + _In_ ULONG Options + ) +{ + switch (Options) + { + case PH_THREAD_TREELIST_MENUITEM_HIDE_SUSPENDED: + Context->HideSuspended = !Context->HideSuspended; + break; + case PH_THREAD_TREELIST_MENUITEM_HIDE_GUITHREADS: + Context->HideGuiThreads = !Context->HideGuiThreads; + break; + case PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_SUSPENDED: + Context->HighlightSuspended = !Context->HighlightSuspended; + break; + case PH_THREAD_TREELIST_MENUITEM_HIGHLIGHT_GUITHREADS: + Context->HighlightGuiThreads = !Context->HighlightGuiThreads; + break; + } +} + +PPH_THREAD_NODE PhAddThreadNode( + _Inout_ PPH_THREAD_LIST_CONTEXT Context, + _In_ PPH_THREAD_ITEM ThreadItem, + _In_ BOOLEAN FirstRun + ) +{ + PPH_THREAD_NODE threadNode; + + threadNode = PhAllocate(PhEmGetObjectSize(EmThreadNodeType, sizeof(PH_THREAD_NODE))); + memset(threadNode, 0, sizeof(PH_THREAD_NODE)); + PhInitializeTreeNewNode(&threadNode->Node); + + if (Context->EnableStateHighlighting && !FirstRun) + { + PhChangeShStateTn( + &threadNode->Node, + &threadNode->ShState, + &Context->NodeStateList, + NewItemState, + PhCsColorNew, + NULL + ); + } + + PhReferenceObject(ThreadItem); + threadNode->ThreadId = ThreadItem->ThreadId; + threadNode->ThreadItem = ThreadItem; + threadNode->PagePriority = MEMORY_PRIORITY_NORMAL + 1; + threadNode->IoPriority = MaxIoPriorityTypes; + + memset(threadNode->TextCache, 0, sizeof(PH_STRINGREF) * PH_THREAD_TREELIST_COLUMN_MAXIMUM); + threadNode->Node.TextCache = threadNode->TextCache; + threadNode->Node.TextCacheSize = PH_THREAD_TREELIST_COLUMN_MAXIMUM; + + PhAddEntryHashtable(Context->NodeHashtable, &threadNode); + PhAddItemList(Context->NodeList, threadNode); + + PhEmCallObjectOperation(EmThreadNodeType, threadNode, EmObjectCreate); + + TreeNew_NodesStructured(Context->TreeNewHandle); + + return threadNode; +} + +PPH_THREAD_NODE PhFindThreadNode( + _In_ PPH_THREAD_LIST_CONTEXT Context, + _In_ HANDLE ThreadId + ) +{ + PH_THREAD_NODE lookupThreadNode; + PPH_THREAD_NODE lookupThreadNodePtr = &lookupThreadNode; + PPH_THREAD_NODE *threadNode; + + lookupThreadNode.ThreadId = ThreadId; + + threadNode = (PPH_THREAD_NODE *)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupThreadNodePtr + ); + + if (threadNode) + return *threadNode; + else + return NULL; +} + +VOID PhRemoveThreadNode( + _In_ PPH_THREAD_LIST_CONTEXT Context, + _In_ PPH_THREAD_NODE ThreadNode + ) +{ + // Remove from the hashtable here to avoid problems in case the key is re-used. + PhRemoveEntryHashtable(Context->NodeHashtable, &ThreadNode); + + if (Context->EnableStateHighlighting) + { + PhChangeShStateTn( + &ThreadNode->Node, + &ThreadNode->ShState, + &Context->NodeStateList, + RemovingItemState, + PhCsColorRemoved, + Context->TreeNewHandle + ); + } + else + { + PhpRemoveThreadNode(ThreadNode, Context); + } +} + +VOID PhpDestroyThreadNode( + _In_ PPH_THREAD_NODE ThreadNode + ) +{ + PhEmCallObjectOperation(EmThreadNodeType, ThreadNode, EmObjectDelete); + + if (ThreadNode->CyclesDeltaText) PhDereferenceObject(ThreadNode->CyclesDeltaText); + if (ThreadNode->StartAddressText) PhDereferenceObject(ThreadNode->StartAddressText); + if (ThreadNode->PrioritySymbolicText) PhDereferenceObject(ThreadNode->PrioritySymbolicText); + if (ThreadNode->CreatedText) PhDereferenceObject(ThreadNode->CreatedText); + if (ThreadNode->NameText) PhDereferenceObject(ThreadNode->NameText); + if (ThreadNode->StateText) PhDereferenceObject(ThreadNode->StateText); + + PhDereferenceObject(ThreadNode->ThreadItem); + + PhFree(ThreadNode); +} + +VOID PhpRemoveThreadNode( + _In_ PPH_THREAD_NODE ThreadNode, + _In_ PPH_THREAD_LIST_CONTEXT Context // PH_TICK_SH_STATE requires this parameter to be after ThreadNode + ) +{ + ULONG index; + + // Remove from list and cleanup. + + if ((index = PhFindItemList(Context->NodeList, ThreadNode)) != ULONG_MAX) + PhRemoveItemList(Context->NodeList, index); + + PhpDestroyThreadNode(ThreadNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhUpdateThreadNode( + _In_ PPH_THREAD_LIST_CONTEXT Context, + _In_ PPH_THREAD_NODE ThreadNode + ) +{ + memset(ThreadNode->TextCache, 0, sizeof(PH_STRINGREF) * PH_THREAD_TREELIST_COLUMN_MAXIMUM); + + ThreadNode->ValidMask = 0; + PhInvalidateTreeNewNode(&ThreadNode->Node, TN_CACHE_COLOR); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID PhTickThreadNodes( + _In_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + PH_TICK_SH_STATE_TN(PH_THREAD_NODE, ShState, Context->NodeStateList, PhpRemoveThreadNode, PhCsHighlightingDuration, Context->TreeNewHandle, TRUE, NULL, Context); +} + +#define SORT_FUNCTION(Column) PhpThreadTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpThreadTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_THREAD_NODE node1 = *(PPH_THREAD_NODE *)_elem1; \ + PPH_THREAD_NODE node2 = *(PPH_THREAD_NODE *)_elem2; \ + PPH_THREAD_ITEM threadItem1 = node1->ThreadItem; \ + PPH_THREAD_ITEM threadItem2 = node2->ThreadItem; \ + PPH_THREAD_LIST_CONTEXT context = (PPH_THREAD_LIST_CONTEXT)_context; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId); \ + \ + return PhModifySort(sortResult, context->TreeNewSortOrder); \ +} + +LONG PhpThreadTreeNewPostSortFunction( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ) +{ + if (Result == 0) + Result = uintptrcmp((ULONG_PTR)((PPH_THREAD_NODE)Node1)->ThreadId, (ULONG_PTR)((PPH_THREAD_NODE)Node2)->ThreadId); + + return PhModifySort(Result, SortOrder); +} + +BEGIN_SORT_FUNCTION(Tid) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Cpu) +{ + sortResult = singlecmp(threadItem1->CpuUsage, threadItem2->CpuUsage); + + if (sortResult == 0) + { + if (context->UseCycleTime) + sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta); + else + sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta); + } +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(CyclesDelta) +{ + if (context->UseCycleTime) + sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta); + else + sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StartAddress) +{ + sortResult = PhCompareStringWithNull(threadItem1->StartAddressString, threadItem2->StartAddressString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PrioritySymbolic) +{ + sortResult = intcmp(threadItem1->BasePriorityIncrement, threadItem2->BasePriorityIncrement); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Service) +{ + sortResult = PhCompareStringWithNull(threadItem1->ServiceName, threadItem2->ServiceName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Name) +{ + sortResult = PhCompareStringWithNull(node1->NameText, node2->NameText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Created) +{ + sortResult = uint64cmp(threadItem1->CreateTime.QuadPart, threadItem2->CreateTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StartModule) +{ + sortResult = PhCompareStringWithNull(threadItem1->StartAddressFileName, threadItem2->StartAddressFileName, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ContextSwitches) +{ + sortResult = uint64cmp(threadItem1->ContextSwitchesDelta.Value, threadItem2->ContextSwitchesDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Priority) +{ + sortResult = intcmp(threadItem1->Priority, threadItem2->Priority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(BasePriority) +{ + sortResult = intcmp(threadItem1->BasePriority, threadItem2->BasePriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(PagePriority) +{ + sortResult = uintcmp(node1->PagePriority, node2->PagePriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoPriority) +{ + sortResult = uintcmp(node1->IoPriority, node2->IoPriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Cycles) +{ + sortResult = uint64cmp(threadItem1->CyclesDelta.Value, threadItem2->CyclesDelta.Value); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(State) +{ + sortResult = uintcmp(threadItem1->WaitReason, threadItem2->WaitReason); + //sortResult = uintcmp(threadItem1->State, threadItem2->State); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(KernelTime) +{ + sortResult = uint64cmp(threadItem1->KernelTime.QuadPart, threadItem2->KernelTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(UserTime) +{ + sortResult = uint64cmp(threadItem1->UserTime.QuadPart, threadItem2->UserTime.QuadPart); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IdealProcessor) +{ + sortResult = PhCompareStringZ(node1->IdealProcessorText, node2->IdealProcessorText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Critical) +{ + sortResult = ucharcmp(node1->BreakOnTermination, node2->BreakOnTermination); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TidHex) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->ThreadId, (ULONG_PTR)node2->ThreadId); + //sortResult = ucharcmp(node1->ThreadIdHexText, node2->ThreadIdHexText, TRUE); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PhpThreadTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_LIST_CONTEXT context; + PPH_THREAD_NODE node; + + context = Context; + + if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &context->Cm)) + return TRUE; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Tid), + SORT_FUNCTION(Cpu), + SORT_FUNCTION(CyclesDelta), + SORT_FUNCTION(StartAddress), + SORT_FUNCTION(PrioritySymbolic), + SORT_FUNCTION(Service), + SORT_FUNCTION(Name), + SORT_FUNCTION(Created), + SORT_FUNCTION(StartModule), + SORT_FUNCTION(ContextSwitches), + SORT_FUNCTION(Priority), + SORT_FUNCTION(BasePriority), + SORT_FUNCTION(PagePriority), + SORT_FUNCTION(IoPriority), + SORT_FUNCTION(Cycles), + SORT_FUNCTION(State), + SORT_FUNCTION(KernelTime), + SORT_FUNCTION(UserTime), + SORT_FUNCTION(IdealProcessor), + SORT_FUNCTION(Critical), + SORT_FUNCTION(TidHex), + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (!PhCmForwardSort( + (PPH_TREENEW_NODE *)context->NodeList->Items, + context->NodeList->Count, + context->TreeNewSortColumn, + context->TreeNewSortOrder, + &context->Cm + )) + { + if (context->TreeNewSortColumn < PH_THREAD_TREELIST_COLUMN_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + } + else + { + sortFunction = NULL; + } + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PPH_THREAD_ITEM threadItem; + + node = (PPH_THREAD_NODE)getCellText->Node; + threadItem = node->ThreadItem; + + switch (getCellText->Id) + { + case PH_THREAD_TREELIST_COLUMN_TID: + { + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatIU(&format, HandleToUlong(threadItem->ThreadId)); + + if (PhFormatToBuffer(&format, 1, node->ThreadIdText, sizeof(node->ThreadIdText), &returnLength)) + { + getCellText->Text.Buffer = node->ThreadIdText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); // minus null terminator + } + } + break; + case PH_THREAD_TREELIST_COLUMN_CPU: + { + FLOAT cpuUsage; + + cpuUsage = threadItem->CpuUsage * 100; + + if (cpuUsage >= 0.01) + { + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatF(&format, cpuUsage, 2); + + if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) + { + getCellText->Text.Buffer = node->CpuUsageText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + else if (cpuUsage != 0 && PhCsShowCpuBelow001) + { + PH_FORMAT format[2]; + SIZE_T returnLength; + + PhInitFormatS(&format[0], L"< "); + PhInitFormatF(&format[1], 0.01, 2); + + if (PhFormatToBuffer(format, 2, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) + { + getCellText->Text.Buffer = node->CpuUsageText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + } + break; + case PH_THREAD_TREELIST_COLUMN_CYCLESDELTA: + { + if (context->UseCycleTime) + { + if (threadItem->CyclesDelta.Delta != threadItem->CyclesDelta.Value && threadItem->CyclesDelta.Delta != 0) + { + PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(threadItem->CyclesDelta.Delta, TRUE)); + getCellText->Text = node->CyclesDeltaText->sr; + } + } + else + { + if (threadItem->ContextSwitchesDelta.Delta != threadItem->ContextSwitchesDelta.Value && threadItem->ContextSwitchesDelta.Delta != 0) + { + PhMoveReference(&node->CyclesDeltaText, PhFormatUInt64(threadItem->ContextSwitchesDelta.Delta, TRUE)); + getCellText->Text = node->CyclesDeltaText->sr; + } + } + } + break; + case PH_THREAD_TREELIST_COLUMN_STARTADDRESS: + PhSwapReference(&node->StartAddressText, threadItem->StartAddressString); + getCellText->Text = PhGetStringRef(node->StartAddressText); + break; + case PH_THREAD_TREELIST_COLUMN_PRIORITYSYMBOLIC: + PhMoveReference(&node->PrioritySymbolicText, PhGetBasePriorityIncrementString(threadItem->BasePriorityIncrement)); + getCellText->Text = PhGetStringRef(node->PrioritySymbolicText); + break; + case PH_THREAD_TREELIST_COLUMN_SERVICE: + getCellText->Text = PhGetStringRef(threadItem->ServiceName); + break; + case PH_THREAD_TREELIST_COLUMN_NAME: + { + if (threadItem->ThreadHandle && WindowsVersion >= WINDOWS_10_RS1) + { + PPH_STRING threadName; + + if (NT_SUCCESS(PhGetThreadName(threadItem->ThreadHandle, &threadName))) + { + PhMoveReference(&node->NameText, threadName); + } + } + + getCellText->Text = PhGetStringRef(node->NameText); + } + break; + case PH_THREAD_TREELIST_COLUMN_STARTED: + { + SYSTEMTIME time; + + PhLargeIntegerToLocalSystemTime(&time, &threadItem->CreateTime); + PhMoveReference(&node->CreatedText, PhFormatDateTime(&time)); + + getCellText->Text = PhGetStringRef(node->CreatedText); + } + break; + case PH_THREAD_TREELIST_COLUMN_STARTMODULE: + getCellText->Text = PhGetStringRef(threadItem->StartAddressFileName); + break; + case PH_THREAD_TREELIST_COLUMN_CONTEXTSWITCHES: + { + SIZE_T returnLength; + PH_FORMAT format[1]; + + PhInitFormatI64UGroupDigits(&format[0], threadItem->ContextSwitchesDelta.Value); + + if (PhFormatToBuffer(format, 1, node->ContextSwitchesText, sizeof(node->ContextSwitchesText), &returnLength)) + { + getCellText->Text.Buffer = node->ContextSwitchesText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + case PH_THREAD_TREELIST_COLUMN_PRIORITY: + { + SIZE_T returnLength; + PH_FORMAT format[1]; + + PhInitFormatD(&format[0], threadItem->Priority); + + if (PhFormatToBuffer(format, 1, node->PriorityText, sizeof(node->PriorityText), &returnLength)) + { + getCellText->Text.Buffer = node->PriorityText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + case PH_THREAD_TREELIST_COLUMN_BASEPRIORITY: + { + SIZE_T returnLength; + PH_FORMAT format[1]; + + PhInitFormatD(&format[0], threadItem->BasePriority); + + if (PhFormatToBuffer(format, 1, node->BasePriorityText, sizeof(node->BasePriorityText), &returnLength)) + { + getCellText->Text.Buffer = node->BasePriorityText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + case PH_THREAD_TREELIST_COLUMN_PAGEPRIORITY: + { + ULONG pagePriorityInteger = MEMORY_PRIORITY_NORMAL + 1; + PWSTR pagePriority = L"N/A"; + + if (threadItem->ThreadHandle) + { + if (NT_SUCCESS(PhGetThreadPagePriority(threadItem->ThreadHandle, &pagePriorityInteger)) && pagePriorityInteger <= MEMORY_PRIORITY_NORMAL) + { + node->PagePriority = pagePriorityInteger; + pagePriority = PhPagePriorityNames[pagePriorityInteger]; + } + } + + PhInitializeStringRefLongHint(&getCellText->Text, pagePriority); + } + break; + case PH_THREAD_TREELIST_COLUMN_IOPRIORITY: + { + IO_PRIORITY_HINT ioPriorityInteger = MaxIoPriorityTypes; + PWSTR ioPriority = L"N/A"; + + if (threadItem->ThreadHandle) + { + if (NT_SUCCESS(PhGetThreadIoPriority(threadItem->ThreadHandle, &ioPriorityInteger)) && ioPriorityInteger < MaxIoPriorityTypes) + { + node->IoPriority = ioPriorityInteger; + ioPriority = PhIoPriorityHintNames[ioPriorityInteger]; + } + } + + PhInitializeStringRefLongHint(&getCellText->Text, ioPriority); + } + break; + case PH_THREAD_TREELIST_COLUMN_CYCLES: + { + SIZE_T returnLength; + PH_FORMAT format[1]; + + PhInitFormatI64UGroupDigits(&format[0], threadItem->CyclesDelta.Value); + + if (PhFormatToBuffer(format, 1, node->CyclesText, sizeof(node->CyclesText), &returnLength)) + { + getCellText->Text.Buffer = node->CyclesText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + case PH_THREAD_TREELIST_COLUMN_STATE: + { + ULONG suspendCount; + + if (threadItem->State != Waiting) + { + if ((ULONG)threadItem->State < MaximumThreadState) + PhMoveReference(&node->StateText, PhCreateString(PhKThreadStateNames[(ULONG)threadItem->State])); + else + PhMoveReference(&node->StateText, PhCreateString(L"Unknown")); + } + else + { + if ((ULONG)threadItem->WaitReason < MaximumWaitReason) + PhMoveReference(&node->StateText, PhConcatStrings2(L"Wait:", PhKWaitReasonNames[(ULONG)threadItem->WaitReason])); + else + PhMoveReference(&node->StateText, PhCreateString(L"Waiting")); + } + + if (threadItem->ThreadHandle) + { + if (threadItem->WaitReason == Suspended && NT_SUCCESS(PhGetThreadSuspendCount(threadItem->ThreadHandle, &suspendCount))) + { + PH_FORMAT format[4]; + + PhInitFormatSR(&format[0], node->StateText->sr); + PhInitFormatS(&format[1], L" ("); + PhInitFormatU(&format[2], suspendCount); + PhInitFormatS(&format[3], L")"); + + PhMoveReference(&node->StateText, PhFormat(format, 4, 30)); + } + } + + getCellText->Text = PhGetStringRef(node->StateText); + } + break; + case PH_THREAD_TREELIST_COLUMN_KERNELTIME: + { + PhPrintTimeSpan(node->KernelTimeText, threadItem->KernelTime.QuadPart, PH_TIMESPAN_HMSM); + + PhInitializeStringRefLongHint(&getCellText->Text, node->KernelTimeText); + } + break; + case PH_THREAD_TREELIST_COLUMN_USERTIME: + { + PhPrintTimeSpan(node->UserTimeText, threadItem->UserTime.QuadPart, PH_TIMESPAN_HMSM); + + PhInitializeStringRefLongHint(&getCellText->Text, node->UserTimeText); + } + break; + case PH_THREAD_TREELIST_COLUMN_IDEALPROCESSOR: + { + PROCESSOR_NUMBER idealProcessorNumber; + SIZE_T returnLength; + PH_FORMAT format[3]; + + if (threadItem->ThreadHandle) + { + if (NT_SUCCESS(PhGetThreadIdealProcessor(threadItem->ThreadHandle, &idealProcessorNumber))) + { + PhInitFormatU(&format[0], idealProcessorNumber.Group); + PhInitFormatC(&format[1], ':'); + PhInitFormatU(&format[2], idealProcessorNumber.Number); + + if (PhFormatToBuffer(format, 3, node->IdealProcessorText, sizeof(node->IdealProcessorText), &returnLength)) + { + getCellText->Text.Buffer = node->IdealProcessorText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + } + } + break; + case PH_THREAD_TREELIST_COLUMN_CRITICAL: + { + BOOLEAN breakOnTermination; + + if (threadItem->ThreadHandle) + { + if (NT_SUCCESS(PhGetThreadBreakOnTermination(threadItem->ThreadHandle, &breakOnTermination))) + { + if (breakOnTermination) + PhInitializeStringRef(&getCellText->Text, L"Critical"); + } + } + } + break; + case PH_THREAD_TREELIST_COLUMN_TIDHEX: + { + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatIX(&format, HandleToUlong(threadItem->ThreadId)); + + if (PhFormatToBuffer(&format, 1, node->ThreadIdHexText, sizeof(node->ThreadIdHexText), &returnLength)) + { + getCellText->Text.Buffer = node->ThreadIdHexText; + getCellText->Text.Length = returnLength - sizeof(UNICODE_NULL); + } + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PPH_THREAD_ITEM threadItem; + + node = (PPH_THREAD_NODE)getNodeColor->Node; + threadItem = node->ThreadItem; + + if (!threadItem) + NOTHING; + //else if (context->HighlightUnknownStartAddress && threadItem->StartAddressResolveLevel == PhsrlAddress) + // getNodeColor->BackColor = PhCsColorUnknown; + else if (context->HighlightSuspended && threadItem->WaitReason == Suspended) + getNodeColor->BackColor = PhCsColorSuspended; + else if (context->HighlightGuiThreads && threadItem->IsGuiThread) + getNodeColor->BackColor = PhCsColorGuiThreads; + + getNodeColor->Flags = TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_COPY, 0); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(context->TreeNewHandle, 0, -1); + break; + case 'K': + SetFocus(GetDlgItem(context->ParentWindowHandle, IDC_SEARCH)); + break; + case VK_DELETE: + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_TERMINATE, 0); + break; + case VK_RETURN: + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_INSPECT, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + // Customizable columns are disabled until we can figure out how to make it + // co-operate with the column adjustments (e.g. Cycles Delta vs Context Switches Delta, + // Service column). + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = PH_THREAD_TREELIST_COLUMN_CYCLESDELTA; + data.DefaultSortOrder = DescendingSortOrder; + PhInitializeTreeNewColumnMenuEx(&data, PH_TN_COLUMN_MENU_SHOW_RESET_SORT); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_THREAD_INSPECT, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenu); + } + return TRUE; + case TreeNewGetDialogCode: + { + PULONG code = Parameter2; + + if (PtrToUlong(Parameter1) == VK_RETURN) + { + *code = DLGC_WANTMESSAGE; + return TRUE; + } + } + return FALSE; + } + + return FALSE; +} + +PPH_THREAD_ITEM PhGetSelectedThreadItem( + _In_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + PPH_THREAD_ITEM threadItem = NULL; + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_THREAD_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + threadItem = node->ThreadItem; + break; + } + } + + return threadItem; +} + +VOID PhGetSelectedThreadItems( + _In_ PPH_THREAD_LIST_CONTEXT Context, + _Out_ PPH_THREAD_ITEM **Threads, + _Out_ PULONG NumberOfThreads + ) +{ + PH_ARRAY array; + ULONG i; + + PhInitializeArray(&array, sizeof(PVOID), 2); + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_THREAD_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemArray(&array, &node->ThreadItem); + } + + *NumberOfThreads = (ULONG)array.Count; + *Threads = PhFinalArrayItems(&array); +} + +VOID PhDeselectAllThreadNodes( + _In_ PPH_THREAD_LIST_CONTEXT Context + ) +{ + TreeNew_DeselectRange(Context->TreeNewHandle, 0, -1); +} diff --git a/ProcessHacker/thrdprv.c b/ProcessHacker/thrdprv.c index 85f3f6a6c79d..211d50146af2 100644 --- a/ProcessHacker/thrdprv.c +++ b/ProcessHacker/thrdprv.c @@ -1,1115 +1,1109 @@ -/* - * Process Hacker - - * thread provider - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The thread provider is tied to the process provider, and runs by registering a callback for the - * processes-updated event. This is because calculating CPU usage depends on deltas calculated by - * the process provider. However, this does increase the complexity of the thread provider system. - */ - -#include -#include - -#include -#include -#include -#include - -#include -#include - -typedef struct _PH_THREAD_QUERY_DATA -{ - SLIST_ENTRY ListEntry; - PPH_THREAD_PROVIDER ThreadProvider; - PPH_THREAD_ITEM ThreadItem; - ULONG64 RunId; - - PPH_STRING StartAddressString; - PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel; - - PPH_STRING ServiceName; -} PH_THREAD_QUERY_DATA, *PPH_THREAD_QUERY_DATA; - -typedef struct _PH_THREAD_SYMBOL_LOAD_CONTEXT -{ - HANDLE ProcessId; - PPH_THREAD_PROVIDER ThreadProvider; - PPH_SYMBOL_PROVIDER SymbolProvider; -} PH_THREAD_SYMBOL_LOAD_CONTEXT, *PPH_THREAD_SYMBOL_LOAD_CONTEXT; - -VOID NTAPI PhpThreadProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID NTAPI PhpThreadItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -BOOLEAN NTAPI PhpThreadHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI PhpThreadHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID PhpThreadProviderCallbackHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID PhpThreadProviderUpdate( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ PVOID ProcessInformation - ); - -PPH_OBJECT_TYPE PhThreadProviderType; -PPH_OBJECT_TYPE PhThreadItemType; - -PH_WORK_QUEUE PhThreadProviderWorkQueue; -PH_INITONCE PhThreadProviderWorkQueueInitOnce = PH_INITONCE_INIT; - -BOOLEAN PhThreadProviderInitialization( - VOID - ) -{ - PhThreadProviderType = PhCreateObjectType(L"ThreadProvider", 0, PhpThreadProviderDeleteProcedure); - PhThreadItemType = PhCreateObjectType(L"ThreadItem", 0, PhpThreadItemDeleteProcedure); - - return TRUE; -} - -VOID PhpQueueThreadWorkQueueItem( - _In_ PTHREAD_START_ROUTINE Function, - _In_opt_ PVOID Context - ) -{ - if (PhBeginInitOnce(&PhThreadProviderWorkQueueInitOnce)) - { - PhInitializeWorkQueue(&PhThreadProviderWorkQueue, 0, 1, 1000); - PhEndInitOnce(&PhThreadProviderWorkQueueInitOnce); - } - - PhQueueItemWorkQueue(&PhThreadProviderWorkQueue, Function, Context); -} - -PPH_THREAD_PROVIDER PhCreateThreadProvider( - _In_ HANDLE ProcessId - ) -{ - PPH_THREAD_PROVIDER threadProvider; - - threadProvider = PhCreateObject( - PhEmGetObjectSize(EmThreadProviderType, sizeof(PH_THREAD_PROVIDER)), - PhThreadProviderType - ); - memset(threadProvider, 0, sizeof(PH_THREAD_PROVIDER)); - - threadProvider->ThreadHashtable = PhCreateHashtable( - sizeof(PPH_THREAD_ITEM), - PhpThreadHashtableEqualFunction, - PhpThreadHashtableHashFunction, - 20 - ); - PhInitializeFastLock(&threadProvider->ThreadHashtableLock); - - PhInitializeCallback(&threadProvider->ThreadAddedEvent); - PhInitializeCallback(&threadProvider->ThreadModifiedEvent); - PhInitializeCallback(&threadProvider->ThreadRemovedEvent); - PhInitializeCallback(&threadProvider->UpdatedEvent); - PhInitializeCallback(&threadProvider->LoadingStateChangedEvent); - - threadProvider->ProcessId = ProcessId; - threadProvider->SymbolProvider = PhCreateSymbolProvider(ProcessId); - - if (threadProvider->SymbolProvider) - { - if (threadProvider->SymbolProvider->IsRealHandle) - threadProvider->ProcessHandle = threadProvider->SymbolProvider->ProcessHandle; - } - - RtlInitializeSListHead(&threadProvider->QueryListHead); - PhInitializeQueuedLock(&threadProvider->LoadSymbolsLock); - - threadProvider->RunId = 1; - threadProvider->SymbolsLoadedRunId = 0; // Force symbols to be loaded the first time we try to resolve an address - - PhEmCallObjectOperation(EmThreadProviderType, threadProvider, EmObjectCreate); - - return threadProvider; -} - -VOID PhpThreadProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_THREAD_PROVIDER threadProvider = (PPH_THREAD_PROVIDER)Object; - - PhEmCallObjectOperation(EmThreadProviderType, threadProvider, EmObjectDelete); - - // Dereference all thread items (we referenced them - // when we added them to the hashtable). - PhDereferenceAllThreadItems(threadProvider); - - PhDereferenceObject(threadProvider->ThreadHashtable); - PhDeleteFastLock(&threadProvider->ThreadHashtableLock); - PhDeleteCallback(&threadProvider->ThreadAddedEvent); - PhDeleteCallback(&threadProvider->ThreadModifiedEvent); - PhDeleteCallback(&threadProvider->ThreadRemovedEvent); - PhDeleteCallback(&threadProvider->UpdatedEvent); - PhDeleteCallback(&threadProvider->LoadingStateChangedEvent); - - // Destroy all queue items. - { - PSLIST_ENTRY entry; - PPH_THREAD_QUERY_DATA data; - - entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry); - entry = entry->Next; - - PhClearReference(&data->StartAddressString); - PhClearReference(&data->ServiceName); - PhDereferenceObject(data->ThreadItem); - PhFree(data); - } - } - - // We don't close the process handle because it is owned by the symbol provider. - if (threadProvider->SymbolProvider) PhDereferenceObject(threadProvider->SymbolProvider); -} - -VOID PhRegisterThreadProvider( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _Out_ PPH_CALLBACK_REGISTRATION CallbackRegistration - ) -{ - PhReferenceObject(ThreadProvider); - PhRegisterCallback(&PhProcessesUpdatedEvent, PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration); -} - -VOID PhUnregisterThreadProvider( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ PPH_CALLBACK_REGISTRATION CallbackRegistration - ) -{ - PhUnregisterCallback(&PhProcessesUpdatedEvent, CallbackRegistration); - PhDereferenceObject(ThreadProvider); -} - -VOID PhSetTerminatingThreadProvider( - _Inout_ PPH_THREAD_PROVIDER ThreadProvider - ) -{ - ThreadProvider->Terminating = TRUE; -} - -static BOOLEAN LoadSymbolsEnumGenericModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PPH_THREAD_SYMBOL_LOAD_CONTEXT context = Context; - PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider; - - if (context->ThreadProvider->Terminating) - return FALSE; - - // If we're loading kernel module symbols for a process other than System, ignore modules which - // are in user space. This may happen in Windows 7. - if (context->ProcessId == SYSTEM_PROCESS_ID && - context->ThreadProvider->ProcessId != SYSTEM_PROCESS_ID && - (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress) - { - return TRUE; - } - - PhLoadModuleSymbolProvider( - symbolProvider, - Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, - Module->Size - ); - - return TRUE; -} - -static BOOLEAN LoadBasicSymbolsEnumGenericModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PPH_THREAD_SYMBOL_LOAD_CONTEXT context = Context; - PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider; - - if (context->ThreadProvider->Terminating) - return FALSE; - - if (PhEqualString2(Module->Name, L"ntdll.dll", TRUE) || - PhEqualString2(Module->Name, L"kernel32.dll", TRUE)) - { - PhLoadModuleSymbolProvider( - symbolProvider, - Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, - Module->Size - ); - } - - return TRUE; -} - -VOID PhLoadSymbolsThreadProvider( - _In_ PPH_THREAD_PROVIDER ThreadProvider - ) -{ - PH_THREAD_SYMBOL_LOAD_CONTEXT loadContext; - ULONG64 runId; - - loadContext.ThreadProvider = ThreadProvider; - loadContext.SymbolProvider = ThreadProvider->SymbolProvider; - - PhAcquireQueuedLockExclusive(&ThreadProvider->LoadSymbolsLock); - runId = ThreadProvider->RunId; - PhLoadSymbolProviderOptions(ThreadProvider->SymbolProvider); - - if (ThreadProvider->ProcessId != SYSTEM_IDLE_PROCESS_ID) - { - if (ThreadProvider->SymbolProvider->IsRealHandle || - ThreadProvider->ProcessId == SYSTEM_PROCESS_ID) - { - loadContext.ProcessId = ThreadProvider->ProcessId; - PhEnumGenericModules( - ThreadProvider->ProcessId, - ThreadProvider->SymbolProvider->ProcessHandle, - 0, - LoadSymbolsEnumGenericModulesCallback, - &loadContext - ); - } - else - { - // We can't enumerate the process modules. Load symbols for ntdll.dll and kernel32.dll. - loadContext.ProcessId = NtCurrentProcessId(); - PhEnumGenericModules( - NtCurrentProcessId(), - NtCurrentProcess(), - 0, - LoadBasicSymbolsEnumGenericModulesCallback, - &loadContext - ); - } - - // Load kernel module symbols as well. - if (ThreadProvider->ProcessId != SYSTEM_PROCESS_ID) - { - loadContext.ProcessId = SYSTEM_PROCESS_ID; - PhEnumGenericModules( - SYSTEM_PROCESS_ID, - NULL, - 0, - LoadSymbolsEnumGenericModulesCallback, - &loadContext - ); - } - } - else - { - // System Idle Process has one thread for each CPU, each having a start address at - // KiIdleLoop. We need to load symbols for the kernel. - - PRTL_PROCESS_MODULES kernelModules; - - if (NT_SUCCESS(PhEnumKernelModules(&kernelModules))) - { - if (kernelModules->NumberOfModules > 0) - { - PPH_STRING fileName; - PPH_STRING newFileName; - - fileName = PhConvertMultiByteToUtf16(kernelModules->Modules[0].FullPathName); - newFileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - - PhLoadModuleSymbolProvider( - ThreadProvider->SymbolProvider, - newFileName->Buffer, - (ULONG64)kernelModules->Modules[0].ImageBase, - kernelModules->Modules[0].ImageSize - ); - PhDereferenceObject(newFileName); - } - - PhFree(kernelModules); - } - } - - ThreadProvider->SymbolsLoadedRunId = runId; - PhReleaseQueuedLockExclusive(&ThreadProvider->LoadSymbolsLock); -} - -PPH_THREAD_ITEM PhCreateThreadItem( - _In_ HANDLE ThreadId - ) -{ - PPH_THREAD_ITEM threadItem; - - threadItem = PhCreateObject( - PhEmGetObjectSize(EmThreadItemType, sizeof(PH_THREAD_ITEM)), - PhThreadItemType - ); - memset(threadItem, 0, sizeof(PH_THREAD_ITEM)); - threadItem->ThreadId = ThreadId; - PhPrintUInt32(threadItem->ThreadIdString, HandleToUlong(ThreadId)); - - PhEmCallObjectOperation(EmThreadItemType, threadItem, EmObjectCreate); - - return threadItem; -} - -VOID PhpThreadItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_THREAD_ITEM threadItem = (PPH_THREAD_ITEM)Object; - - PhEmCallObjectOperation(EmThreadItemType, threadItem, EmObjectDelete); - - if (threadItem->ThreadHandle) NtClose(threadItem->ThreadHandle); - if (threadItem->StartAddressString) PhDereferenceObject(threadItem->StartAddressString); - if (threadItem->StartAddressFileName) PhDereferenceObject(threadItem->StartAddressFileName); - if (threadItem->ServiceName) PhDereferenceObject(threadItem->ServiceName); -} - -BOOLEAN PhpThreadHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - return - (*(PPH_THREAD_ITEM *)Entry1)->ThreadId == - (*(PPH_THREAD_ITEM *)Entry2)->ThreadId; -} - -ULONG PhpThreadHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return HandleToUlong((*(PPH_THREAD_ITEM *)Entry)->ThreadId) / 4; -} - -PPH_THREAD_ITEM PhReferenceThreadItem( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ HANDLE ThreadId - ) -{ - PH_THREAD_ITEM lookupThreadItem; - PPH_THREAD_ITEM lookupThreadItemPtr = &lookupThreadItem; - PPH_THREAD_ITEM *threadItemPtr; - PPH_THREAD_ITEM threadItem; - - lookupThreadItem.ThreadId = ThreadId; - - PhAcquireFastLockShared(&ThreadProvider->ThreadHashtableLock); - - threadItemPtr = (PPH_THREAD_ITEM *)PhFindEntryHashtable( - ThreadProvider->ThreadHashtable, - &lookupThreadItemPtr - ); - - if (threadItemPtr) - { - threadItem = *threadItemPtr; - PhReferenceObject(threadItem); - } - else - { - threadItem = NULL; - } - - PhReleaseFastLockShared(&ThreadProvider->ThreadHashtableLock); - - return threadItem; -} - -VOID PhDereferenceAllThreadItems( - _In_ PPH_THREAD_PROVIDER ThreadProvider - ) -{ - ULONG enumerationKey = 0; - PPH_THREAD_ITEM *threadItem; - - PhAcquireFastLockExclusive(&ThreadProvider->ThreadHashtableLock); - - while (PhEnumHashtable(ThreadProvider->ThreadHashtable, (PVOID *)&threadItem, &enumerationKey)) - { - PhDereferenceObject(*threadItem); - } - - PhReleaseFastLockExclusive(&ThreadProvider->ThreadHashtableLock); -} - -VOID PhpRemoveThreadItem( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ PPH_THREAD_ITEM ThreadItem - ) -{ - PhRemoveEntryHashtable(ThreadProvider->ThreadHashtable, &ThreadItem); - PhDereferenceObject(ThreadItem); -} - -NTSTATUS PhpThreadQueryWorker( - _In_ PVOID Parameter - ) -{ - PPH_THREAD_QUERY_DATA data = (PPH_THREAD_QUERY_DATA)Parameter; - LONG newSymbolsLoading; - - if (data->ThreadProvider->Terminating) - goto Done; - - newSymbolsLoading = _InterlockedIncrement(&data->ThreadProvider->SymbolsLoading); - - if (newSymbolsLoading == 1) - PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)TRUE); - - if (data->ThreadProvider->SymbolsLoadedRunId == 0) - PhLoadSymbolsThreadProvider(data->ThreadProvider); - - data->StartAddressString = PhGetSymbolFromAddress( - data->ThreadProvider->SymbolProvider, - data->ThreadItem->StartAddress, - &data->StartAddressResolveLevel, - &data->ThreadItem->StartAddressFileName, - NULL, - NULL - ); - - if (data->StartAddressResolveLevel == PhsrlAddress && data->ThreadProvider->SymbolsLoadedRunId < data->RunId) - { - // The process may have loaded new modules, so load symbols for those and try again. - - PhLoadSymbolsThreadProvider(data->ThreadProvider); - - PhClearReference(&data->StartAddressString); - PhClearReference(&data->ThreadItem->StartAddressFileName); - data->StartAddressString = PhGetSymbolFromAddress( - data->ThreadProvider->SymbolProvider, - data->ThreadItem->StartAddress, - &data->StartAddressResolveLevel, - &data->ThreadItem->StartAddressFileName, - NULL, - NULL - ); - } - - newSymbolsLoading = _InterlockedDecrement(&data->ThreadProvider->SymbolsLoading); - - if (newSymbolsLoading == 0) - PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)FALSE); - - // Check if the process has services - we'll need to know before getting service tag/name - // information. - if (WINDOWS_HAS_SERVICE_TAGS && !data->ThreadProvider->HasServicesKnown) - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(data->ThreadProvider->ProcessId)) - { - data->ThreadProvider->HasServices = processItem->ServiceList && processItem->ServiceList->Count != 0; - PhDereferenceObject(processItem); - } - - data->ThreadProvider->HasServicesKnown = TRUE; - } - - // Get the service tag, and the service name. - if (WINDOWS_HAS_SERVICE_TAGS && - data->ThreadProvider->SymbolProvider->IsRealHandle && - data->ThreadItem->ThreadHandle) - { - PVOID serviceTag; - - if (NT_SUCCESS(PhGetThreadServiceTag( - data->ThreadItem->ThreadHandle, - data->ThreadProvider->ProcessHandle, - &serviceTag - ))) - { - data->ServiceName = PhGetServiceNameFromTag( - data->ThreadProvider->ProcessId, - serviceTag - ); - } - } - -Done: - RtlInterlockedPushEntrySList(&data->ThreadProvider->QueryListHead, &data->ListEntry); - PhDereferenceObject(data->ThreadProvider); - - return STATUS_SUCCESS; -} - -VOID PhpQueueThreadQuery( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ PPH_THREAD_ITEM ThreadItem - ) -{ - PPH_THREAD_QUERY_DATA data; - - data = PhAllocate(sizeof(PH_THREAD_QUERY_DATA)); - memset(data, 0, sizeof(PH_THREAD_QUERY_DATA)); - PhSetReference(&data->ThreadProvider, ThreadProvider); - PhSetReference(&data->ThreadItem, ThreadItem); - data->RunId = ThreadProvider->RunId; - - PhpQueueThreadWorkQueueItem(PhpThreadQueryWorker, data); -} - -PPH_STRING PhpGetThreadBasicStartAddress( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ ULONG64 Address, - _Out_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel - ) -{ - ULONG64 modBase; - PPH_STRING fileName = NULL; - PPH_STRING baseName = NULL; - PPH_STRING symbol; - - modBase = PhGetModuleFromAddress( - ThreadProvider->SymbolProvider, - Address, - &fileName - ); - - if (fileName == NULL) - { - *ResolveLevel = PhsrlAddress; - - symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2); - PhPrintPointer(symbol->Buffer, (PVOID)Address); - PhTrimToNullTerminatorString(symbol); - } - else - { - PH_FORMAT format[3]; - - baseName = PhGetBaseName(fileName); - *ResolveLevel = PhsrlModule; - - PhInitFormatSR(&format[0], baseName->sr); - PhInitFormatS(&format[1], L"+0x"); - PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase)); - - symbol = PhFormat(format, 3, baseName->Length + 6 + 32); - } - - if (fileName) - PhDereferenceObject(fileName); - if (baseName) - PhDereferenceObject(baseName); - - return symbol; -} - -static NTSTATUS PhpGetThreadCycleTime( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ PPH_THREAD_ITEM ThreadItem, - _Out_ PULONG64 CycleTime - ) -{ - if (ThreadProvider->ProcessId != SYSTEM_IDLE_PROCESS_ID) - { - return PhGetThreadCycleTime(ThreadItem->ThreadHandle, CycleTime); - } - else - { - if (HandleToUlong(ThreadItem->ThreadId) < (ULONG)PhSystemBasicInformation.NumberOfProcessors) - { - *CycleTime = PhCpuIdleCycleTime[HandleToUlong(ThreadItem->ThreadId)].QuadPart; - return STATUS_SUCCESS; - } - } - - return STATUS_INVALID_PARAMETER; -} - -PPH_STRING PhGetBasePriorityIncrementString( - _In_ LONG Increment - ) -{ - switch (Increment) - { - case THREAD_BASE_PRIORITY_LOWRT + 1: - case THREAD_BASE_PRIORITY_LOWRT: - return PhCreateString(L"Time critical"); - case THREAD_PRIORITY_HIGHEST: - return PhCreateString(L"Highest"); - case THREAD_PRIORITY_ABOVE_NORMAL: - return PhCreateString(L"Above normal"); - case THREAD_PRIORITY_NORMAL: - return PhCreateString(L"Normal"); - case THREAD_PRIORITY_BELOW_NORMAL: - return PhCreateString(L"Below normal"); - case THREAD_PRIORITY_LOWEST: - return PhCreateString(L"Lowest"); - case THREAD_BASE_PRIORITY_IDLE: - case THREAD_BASE_PRIORITY_IDLE - 1: - return PhCreateString(L"Idle"); - case THREAD_PRIORITY_ERROR_RETURN: - return NULL; - default: - return PhFormatString(L"%d", Increment); - } -} - -VOID PhThreadProviderInitialUpdate( - _In_ PPH_THREAD_PROVIDER ThreadProvider - ) -{ - PVOID processes; - - if (NT_SUCCESS(PhEnumProcesses(&processes))) - { - PhpThreadProviderUpdate(ThreadProvider, processes); - PhFree(processes); - } -} - -VOID PhpThreadProviderCallbackHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (PhProcessInformation) - { - PhpThreadProviderUpdate((PPH_THREAD_PROVIDER)Context, PhProcessInformation); - } -} - -VOID PhpThreadProviderUpdate( - _In_ PPH_THREAD_PROVIDER ThreadProvider, - _In_ PVOID ProcessInformation - ) -{ - PPH_THREAD_PROVIDER threadProvider = ThreadProvider; - PSYSTEM_PROCESS_INFORMATION process; - SYSTEM_PROCESS_INFORMATION localProcess; - PSYSTEM_THREAD_INFORMATION threads; - ULONG numberOfThreads; - ULONG i; - - process = PhFindProcessInformation(ProcessInformation, threadProvider->ProcessId); - - if (!process) - { - // The process doesn't exist anymore. Pretend it does but has no threads. - process = &localProcess; - process->NumberOfThreads = 0; - } - - threads = process->Threads; - numberOfThreads = process->NumberOfThreads; - - // System Idle Process has one thread per CPU. They all have a TID of 0. We can't have duplicate - // TIDs, so we'll assign unique TIDs. - if (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID) - { - for (i = 0; i < numberOfThreads; i++) - { - threads[i].ClientId.UniqueThread = UlongToHandle(i); - } - } - - // Look for dead threads. - { - PPH_LIST threadsToRemove = NULL; - ULONG enumerationKey = 0; - PPH_THREAD_ITEM *threadItem; - - while (PhEnumHashtable(threadProvider->ThreadHashtable, (PVOID *)&threadItem, &enumerationKey)) - { - BOOLEAN found = FALSE; - - // Check if the thread still exists. - for (i = 0; i < numberOfThreads; i++) - { - PSYSTEM_THREAD_INFORMATION thread = &threads[i]; - - if ((*threadItem)->ThreadId == thread->ClientId.UniqueThread) - { - found = TRUE; - break; - } - } - - if (!found) - { - // Raise the thread removed event. - PhInvokeCallback(&threadProvider->ThreadRemovedEvent, *threadItem); - - if (!threadsToRemove) - threadsToRemove = PhCreateList(2); - - PhAddItemList(threadsToRemove, *threadItem); - } - } - - if (threadsToRemove) - { - PhAcquireFastLockExclusive(&threadProvider->ThreadHashtableLock); - - for (i = 0; i < threadsToRemove->Count; i++) - { - PhpRemoveThreadItem( - threadProvider, - (PPH_THREAD_ITEM)threadsToRemove->Items[i] - ); - } - - PhReleaseFastLockExclusive(&threadProvider->ThreadHashtableLock); - PhDereferenceObject(threadsToRemove); - } - } - - // Go through the queued thread query data. - { - PSLIST_ENTRY entry; - PPH_THREAD_QUERY_DATA data; - - entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead); - - while (entry) - { - data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry); - entry = entry->Next; - - if (data->StartAddressResolveLevel == PhsrlFunction && data->StartAddressString) - { - PhSwapReference(&data->ThreadItem->StartAddressString, data->StartAddressString); - data->ThreadItem->StartAddressResolveLevel = data->StartAddressResolveLevel; - } - - PhMoveReference(&data->ThreadItem->ServiceName, data->ServiceName); - - data->ThreadItem->JustResolved = TRUE; - - if (data->StartAddressString) PhDereferenceObject(data->StartAddressString); - PhDereferenceObject(data->ThreadItem); - PhFree(data); - } - } - - // Look for new threads and update existing ones. - for (i = 0; i < numberOfThreads; i++) - { - PSYSTEM_THREAD_INFORMATION thread = &threads[i]; - PPH_THREAD_ITEM threadItem; - THREAD_BASIC_INFORMATION basicInfo; - - threadItem = PhReferenceThreadItem(threadProvider, thread->ClientId.UniqueThread); - - if (!threadItem) - { - PVOID startAddress = NULL; - - threadItem = PhCreateThreadItem(thread->ClientId.UniqueThread); - - threadItem->CreateTime = thread->CreateTime; - threadItem->KernelTime = thread->KernelTime; - threadItem->UserTime = thread->UserTime; - - PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches); - threadItem->Priority = thread->Priority; - threadItem->BasePriority = thread->BasePriority; - threadItem->State = (KTHREAD_STATE)thread->ThreadState; - threadItem->WaitReason = thread->WaitReason; - - // Try to open a handle to the thread. - if (!NT_SUCCESS(PhOpenThread( - &threadItem->ThreadHandle, - THREAD_QUERY_INFORMATION, - threadItem->ThreadId - ))) - { - PhOpenThread( - &threadItem->ThreadHandle, - ThreadQueryAccess, - threadItem->ThreadId - ); - } - - // Get the cycle count. - if (WINDOWS_HAS_CYCLE_TIME) - { - ULONG64 cycles; - - if (NT_SUCCESS(PhpGetThreadCycleTime( - threadProvider, - threadItem, - &cycles - ))) - { - PhUpdateDelta(&threadItem->CyclesDelta, cycles); - } - } - - // Initialize the CPU time deltas. - PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); - PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); - - // Try to get the start address. - - if (threadItem->ThreadHandle) - { - NtQueryInformationThread( - threadItem->ThreadHandle, - ThreadQuerySetWin32StartAddress, - &startAddress, - sizeof(PVOID), - NULL - ); - } - - if (!startAddress) - startAddress = thread->StartAddress; - - threadItem->StartAddress = (ULONG64)startAddress; - - // Get the base priority increment (relative to the process priority). - if (threadItem->ThreadHandle && NT_SUCCESS(PhGetThreadBasicInformation( - threadItem->ThreadHandle, - &basicInfo - ))) - { - threadItem->BasePriorityIncrement = basicInfo.BasePriority; - } - else - { - threadItem->BasePriorityIncrement = THREAD_PRIORITY_ERROR_RETURN; - } - - if (threadProvider->SymbolsLoadedRunId != 0) - { - threadItem->StartAddressString = PhpGetThreadBasicStartAddress( - threadProvider, - threadItem->StartAddress, - &threadItem->StartAddressResolveLevel - ); - } - - if (!threadItem->StartAddressString) - { - threadItem->StartAddressResolveLevel = PhsrlAddress; - threadItem->StartAddressString = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2); - PhPrintPointer( - threadItem->StartAddressString->Buffer, - (PVOID)threadItem->StartAddress - ); - PhTrimToNullTerminatorString(threadItem->StartAddressString); - } - - PhpQueueThreadQuery(threadProvider, threadItem); - - // Is it a GUI thread? - { - GUITHREADINFO info = { sizeof(GUITHREADINFO) }; - - threadItem->IsGuiThread = !!GetGUIThreadInfo(HandleToUlong(threadItem->ThreadId), &info); - } - - // Add the thread item to the hashtable. - PhAcquireFastLockExclusive(&threadProvider->ThreadHashtableLock); - PhAddEntryHashtable(threadProvider->ThreadHashtable, &threadItem); - PhReleaseFastLockExclusive(&threadProvider->ThreadHashtableLock); - - // Raise the thread added event. - PhInvokeCallback(&threadProvider->ThreadAddedEvent, threadItem); - } - else - { - BOOLEAN modified = FALSE; - - if (threadItem->JustResolved) - modified = TRUE; - - threadItem->KernelTime = thread->KernelTime; - threadItem->UserTime = thread->UserTime; - - threadItem->Priority = thread->Priority; - threadItem->BasePriority = thread->BasePriority; - - threadItem->State = (KTHREAD_STATE)thread->ThreadState; - - if (threadItem->WaitReason != thread->WaitReason) - { - threadItem->WaitReason = thread->WaitReason; - modified = TRUE; - } - - // If the resolve level is only at address, it probably - // means symbols weren't loaded the last time we - // tried to get the start address. Try again. - if (threadItem->StartAddressResolveLevel == PhsrlAddress) - { - if (threadProvider->SymbolsLoadedRunId != 0) - { - PPH_STRING newStartAddressString; - - newStartAddressString = PhpGetThreadBasicStartAddress( - threadProvider, - threadItem->StartAddress, - &threadItem->StartAddressResolveLevel - ); - - PhMoveReference( - &threadItem->StartAddressString, - newStartAddressString - ); - - modified = TRUE; - } - } - - // If we couldn't resolve the start address to a module+offset, use the StartAddress - // instead of the Win32StartAddress and try again. Note that we check the resolve level - // again because we may have changed it in the previous block. - if (threadItem->JustResolved && - threadItem->StartAddressResolveLevel == PhsrlAddress) - { - if (threadItem->StartAddress != (ULONG64)thread->StartAddress) - { - threadItem->StartAddress = (ULONG64)thread->StartAddress; - PhpQueueThreadQuery(threadProvider, threadItem); - } - } - - // Update the context switch count. - { - ULONG oldDelta; - - oldDelta = threadItem->ContextSwitchesDelta.Delta; - PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches); - - if (threadItem->ContextSwitchesDelta.Delta != oldDelta) - { - modified = TRUE; - } - } - - // Update the cycle count. - if (WINDOWS_HAS_CYCLE_TIME) - { - ULONG64 cycles; - ULONG64 oldDelta; - - oldDelta = threadItem->CyclesDelta.Delta; - - if (NT_SUCCESS(PhpGetThreadCycleTime( - threadProvider, - threadItem, - &cycles - ))) - { - PhUpdateDelta(&threadItem->CyclesDelta, cycles); - - if (threadItem->CyclesDelta.Delta != oldDelta) - { - modified = TRUE; - } - } - } - - // Update the CPU time deltas. - PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); - PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); - - // Update the CPU usage. - // If the cycle time isn't available, we'll fall back to using the CPU time. - if (WINDOWS_HAS_CYCLE_TIME && PhEnableCycleCpuUsage && (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID || threadItem->ThreadHandle)) - { - threadItem->CpuUsage = (FLOAT)threadItem->CyclesDelta.Delta / PhCpuTotalCycleDelta; - } - else - { - threadItem->CpuUsage = (FLOAT)(threadItem->CpuKernelDelta.Delta + threadItem->CpuUserDelta.Delta) / - (PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta + PhCpuIdleDelta.Delta); - } - - // Update the base priority increment. - { - LONG oldBasePriorityIncrement = threadItem->BasePriorityIncrement; - - if (threadItem->ThreadHandle && NT_SUCCESS(PhGetThreadBasicInformation( - threadItem->ThreadHandle, - &basicInfo - ))) - { - threadItem->BasePriorityIncrement = basicInfo.BasePriority; - } - else - { - threadItem->BasePriorityIncrement = THREAD_PRIORITY_ERROR_RETURN; - } - - if (threadItem->BasePriorityIncrement != oldBasePriorityIncrement) - { - modified = TRUE; - } - } - - // Update the GUI thread status. - { - GUITHREADINFO info = { sizeof(GUITHREADINFO) }; - BOOLEAN oldIsGuiThread = threadItem->IsGuiThread; - - threadItem->IsGuiThread = !!GetGUIThreadInfo(HandleToUlong(threadItem->ThreadId), &info); - - if (threadItem->IsGuiThread != oldIsGuiThread) - modified = TRUE; - } - - threadItem->JustResolved = FALSE; - - if (modified) - { - // Raise the thread modified event. - PhInvokeCallback(&threadProvider->ThreadModifiedEvent, threadItem); - } - - PhDereferenceObject(threadItem); - } - } - - PhInvokeCallback(&threadProvider->UpdatedEvent, NULL); - threadProvider->RunId++; -} +/* + * Process Hacker - + * thread provider + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The thread provider is tied to the process provider, and runs by registering a callback for the + * processes-updated event. This is because calculating CPU usage depends on deltas calculated by + * the process provider. However, this does increase the complexity of the thread provider system. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +typedef struct _PH_THREAD_QUERY_DATA +{ + SLIST_ENTRY ListEntry; + PPH_THREAD_PROVIDER ThreadProvider; + PPH_THREAD_ITEM ThreadItem; + ULONG64 RunId; + + PPH_STRING StartAddressString; + PPH_STRING StartAddressFileName; + PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel; + + PPH_STRING ServiceName; +} PH_THREAD_QUERY_DATA, *PPH_THREAD_QUERY_DATA; + +typedef struct _PH_THREAD_SYMBOL_LOAD_CONTEXT +{ + HANDLE ProcessId; + PPH_THREAD_PROVIDER ThreadProvider; + PPH_SYMBOL_PROVIDER SymbolProvider; +} PH_THREAD_SYMBOL_LOAD_CONTEXT, *PPH_THREAD_SYMBOL_LOAD_CONTEXT; + +VOID NTAPI PhpThreadProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID NTAPI PhpThreadItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +BOOLEAN NTAPI PhpThreadHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpThreadHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID PhpThreadProviderCallbackHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID PhpThreadProviderUpdate( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ PVOID ProcessInformation + ); + +PPH_OBJECT_TYPE PhThreadProviderType = NULL; +PPH_OBJECT_TYPE PhThreadItemType = NULL; +PH_WORK_QUEUE PhThreadProviderWorkQueue; +PH_INITONCE PhThreadProviderWorkQueueInitOnce = PH_INITONCE_INIT; + +VOID PhpQueueThreadWorkQueueItem( + _In_ PUSER_THREAD_START_ROUTINE Function, + _In_opt_ PVOID Context + ) +{ + if (PhBeginInitOnce(&PhThreadProviderWorkQueueInitOnce)) + { + PhInitializeWorkQueue(&PhThreadProviderWorkQueue, 0, 1, 1000); + PhEndInitOnce(&PhThreadProviderWorkQueueInitOnce); + } + + PhQueueItemWorkQueue(&PhThreadProviderWorkQueue, Function, Context); +} + +PPH_THREAD_PROVIDER PhCreateThreadProvider( + _In_ HANDLE ProcessId + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPH_THREAD_PROVIDER threadProvider; + + if (PhBeginInitOnce(&initOnce)) + { + PhThreadProviderType = PhCreateObjectType(L"ThreadProvider", 0, PhpThreadProviderDeleteProcedure); + PhThreadItemType = PhCreateObjectType(L"ThreadItem", 0, PhpThreadItemDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + threadProvider = PhCreateObject( + PhEmGetObjectSize(EmThreadProviderType, sizeof(PH_THREAD_PROVIDER)), + PhThreadProviderType + ); + memset(threadProvider, 0, sizeof(PH_THREAD_PROVIDER)); + + threadProvider->ThreadHashtable = PhCreateHashtable( + sizeof(PPH_THREAD_ITEM), + PhpThreadHashtableEqualFunction, + PhpThreadHashtableHashFunction, + 20 + ); + PhInitializeFastLock(&threadProvider->ThreadHashtableLock); + + PhInitializeCallback(&threadProvider->ThreadAddedEvent); + PhInitializeCallback(&threadProvider->ThreadModifiedEvent); + PhInitializeCallback(&threadProvider->ThreadRemovedEvent); + PhInitializeCallback(&threadProvider->UpdatedEvent); + PhInitializeCallback(&threadProvider->LoadingStateChangedEvent); + + threadProvider->ProcessId = ProcessId; + threadProvider->SymbolProvider = PhCreateSymbolProvider(ProcessId); + + if (threadProvider->SymbolProvider) + { + if (threadProvider->SymbolProvider->IsRealHandle) + threadProvider->ProcessHandle = threadProvider->SymbolProvider->ProcessHandle; + } + + RtlInitializeSListHead(&threadProvider->QueryListHead); + PhInitializeQueuedLock(&threadProvider->LoadSymbolsLock); + + threadProvider->RunId = 1; + threadProvider->SymbolsLoadedRunId = 0; // Force symbols to be loaded the first time we try to resolve an address + + PhEmCallObjectOperation(EmThreadProviderType, threadProvider, EmObjectCreate); + + return threadProvider; +} + +VOID PhpThreadProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_THREAD_PROVIDER threadProvider = (PPH_THREAD_PROVIDER)Object; + + PhEmCallObjectOperation(EmThreadProviderType, threadProvider, EmObjectDelete); + + // Dereference all thread items (we referenced them + // when we added them to the hashtable). + PhDereferenceAllThreadItems(threadProvider); + + PhDereferenceObject(threadProvider->ThreadHashtable); + PhDeleteFastLock(&threadProvider->ThreadHashtableLock); + PhDeleteCallback(&threadProvider->ThreadAddedEvent); + PhDeleteCallback(&threadProvider->ThreadModifiedEvent); + PhDeleteCallback(&threadProvider->ThreadRemovedEvent); + PhDeleteCallback(&threadProvider->UpdatedEvent); + PhDeleteCallback(&threadProvider->LoadingStateChangedEvent); + + // Destroy all queue items. + { + PSLIST_ENTRY entry; + PPH_THREAD_QUERY_DATA data; + + entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry); + entry = entry->Next; + + PhClearReference(&data->StartAddressString); + PhClearReference(&data->ServiceName); + PhDereferenceObject(data->ThreadItem); + PhFree(data); + } + } + + // We don't close the process handle because it is owned by the symbol provider. + if (threadProvider->SymbolProvider) PhDereferenceObject(threadProvider->SymbolProvider); +} + +VOID PhRegisterThreadProvider( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _Out_ PPH_CALLBACK_REGISTRATION CallbackRegistration + ) +{ + PhReferenceObject(ThreadProvider); + PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration); +} + +VOID PhUnregisterThreadProvider( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ PPH_CALLBACK_REGISTRATION CallbackRegistration + ) +{ + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), CallbackRegistration); + PhDereferenceObject(ThreadProvider); +} + +VOID PhSetTerminatingThreadProvider( + _Inout_ PPH_THREAD_PROVIDER ThreadProvider + ) +{ + ThreadProvider->Terminating = TRUE; +} + +static BOOLEAN LoadSymbolsEnumGenericModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_SYMBOL_LOAD_CONTEXT context = Context; + PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider; + + if (context->ThreadProvider->Terminating) + return FALSE; + + // If we're loading kernel module symbols for a process other than System, ignore modules which + // are in user space. This may happen in Windows 7. + if (context->ProcessId == SYSTEM_PROCESS_ID && + context->ThreadProvider->ProcessId != SYSTEM_PROCESS_ID && + (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress) + { + return TRUE; + } + + PhLoadModuleSymbolProvider( + symbolProvider, + Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, + Module->Size + ); + + return TRUE; +} + +static BOOLEAN LoadBasicSymbolsEnumGenericModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_SYMBOL_LOAD_CONTEXT context = Context; + PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider; + + if (context->ThreadProvider->Terminating) + return FALSE; + + if (PhEqualString2(Module->Name, L"ntdll.dll", TRUE) || + PhEqualString2(Module->Name, L"kernel32.dll", TRUE)) + { + PhLoadModuleSymbolProvider( + symbolProvider, + Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, + Module->Size + ); + } + + return TRUE; +} + +VOID PhLoadSymbolsThreadProvider( + _In_ PPH_THREAD_PROVIDER ThreadProvider + ) +{ + PH_THREAD_SYMBOL_LOAD_CONTEXT loadContext; + ULONG64 runId; + + loadContext.ThreadProvider = ThreadProvider; + loadContext.SymbolProvider = ThreadProvider->SymbolProvider; + + PhAcquireQueuedLockExclusive(&ThreadProvider->LoadSymbolsLock); + runId = ThreadProvider->RunId; + PhLoadSymbolProviderOptions(ThreadProvider->SymbolProvider); + + if (ThreadProvider->ProcessId != SYSTEM_IDLE_PROCESS_ID) + { + if (ThreadProvider->SymbolProvider->IsRealHandle || + ThreadProvider->ProcessId == SYSTEM_PROCESS_ID) + { + loadContext.ProcessId = ThreadProvider->ProcessId; + PhEnumGenericModules( + ThreadProvider->ProcessId, + ThreadProvider->SymbolProvider->ProcessHandle, + 0, + LoadSymbolsEnumGenericModulesCallback, + &loadContext + ); + } + else + { + // We can't enumerate the process modules. Load symbols for ntdll.dll and kernel32.dll. + loadContext.ProcessId = NtCurrentProcessId(); + PhEnumGenericModules( + NtCurrentProcessId(), + NtCurrentProcess(), + 0, + LoadBasicSymbolsEnumGenericModulesCallback, + &loadContext + ); + } + + // Load kernel module symbols as well. + if (ThreadProvider->ProcessId != SYSTEM_PROCESS_ID) + { + loadContext.ProcessId = SYSTEM_PROCESS_ID; + PhEnumGenericModules( + SYSTEM_PROCESS_ID, + NULL, + 0, + LoadSymbolsEnumGenericModulesCallback, + &loadContext + ); + } + } + else + { + // System Idle Process has one thread for each CPU, each having a start address at + // KiIdleLoop. We need to load symbols for the kernel. + + PRTL_PROCESS_MODULES kernelModules; + + if (NT_SUCCESS(PhEnumKernelModules(&kernelModules))) + { + if (kernelModules->NumberOfModules > 0) + { + PPH_STRING fileName; + PPH_STRING newFileName; + + fileName = PhConvertMultiByteToUtf16(kernelModules->Modules[0].FullPathName); + newFileName = PhGetFileName(fileName); + PhDereferenceObject(fileName); + + PhLoadModuleSymbolProvider( + ThreadProvider->SymbolProvider, + newFileName->Buffer, + (ULONG64)kernelModules->Modules[0].ImageBase, + kernelModules->Modules[0].ImageSize + ); + PhDereferenceObject(newFileName); + } + + PhFree(kernelModules); + } + } + + ThreadProvider->SymbolsLoadedRunId = runId; + PhReleaseQueuedLockExclusive(&ThreadProvider->LoadSymbolsLock); +} + +PPH_THREAD_ITEM PhCreateThreadItem( + _In_ HANDLE ThreadId + ) +{ + PPH_THREAD_ITEM threadItem; + + threadItem = PhCreateObject( + PhEmGetObjectSize(EmThreadItemType, sizeof(PH_THREAD_ITEM)), + PhThreadItemType + ); + memset(threadItem, 0, sizeof(PH_THREAD_ITEM)); + threadItem->ThreadId = ThreadId; + + PhEmCallObjectOperation(EmThreadItemType, threadItem, EmObjectCreate); + + return threadItem; +} + +VOID PhpThreadItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_THREAD_ITEM threadItem = (PPH_THREAD_ITEM)Object; + + PhEmCallObjectOperation(EmThreadItemType, threadItem, EmObjectDelete); + + if (threadItem->ThreadHandle) NtClose(threadItem->ThreadHandle); + if (threadItem->StartAddressString) PhDereferenceObject(threadItem->StartAddressString); + if (threadItem->StartAddressFileName) PhDereferenceObject(threadItem->StartAddressFileName); + if (threadItem->ServiceName) PhDereferenceObject(threadItem->ServiceName); +} + +BOOLEAN PhpThreadHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + return + (*(PPH_THREAD_ITEM *)Entry1)->ThreadId == + (*(PPH_THREAD_ITEM *)Entry2)->ThreadId; +} + +ULONG PhpThreadHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return HandleToUlong((*(PPH_THREAD_ITEM *)Entry)->ThreadId) / 4; +} + +PPH_THREAD_ITEM PhReferenceThreadItem( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ HANDLE ThreadId + ) +{ + PH_THREAD_ITEM lookupThreadItem; + PPH_THREAD_ITEM lookupThreadItemPtr = &lookupThreadItem; + PPH_THREAD_ITEM *threadItemPtr; + PPH_THREAD_ITEM threadItem; + + lookupThreadItem.ThreadId = ThreadId; + + PhAcquireFastLockShared(&ThreadProvider->ThreadHashtableLock); + + threadItemPtr = (PPH_THREAD_ITEM *)PhFindEntryHashtable( + ThreadProvider->ThreadHashtable, + &lookupThreadItemPtr + ); + + if (threadItemPtr) + { + threadItem = *threadItemPtr; + PhReferenceObject(threadItem); + } + else + { + threadItem = NULL; + } + + PhReleaseFastLockShared(&ThreadProvider->ThreadHashtableLock); + + return threadItem; +} + +VOID PhDereferenceAllThreadItems( + _In_ PPH_THREAD_PROVIDER ThreadProvider + ) +{ + ULONG enumerationKey = 0; + PPH_THREAD_ITEM *threadItem; + + PhAcquireFastLockExclusive(&ThreadProvider->ThreadHashtableLock); + + while (PhEnumHashtable(ThreadProvider->ThreadHashtable, (PVOID *)&threadItem, &enumerationKey)) + { + PhDereferenceObject(*threadItem); + } + + PhReleaseFastLockExclusive(&ThreadProvider->ThreadHashtableLock); +} + +VOID PhpRemoveThreadItem( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ PPH_THREAD_ITEM ThreadItem + ) +{ + PhRemoveEntryHashtable(ThreadProvider->ThreadHashtable, &ThreadItem); + PhDereferenceObject(ThreadItem); +} + +NTSTATUS PhpThreadQueryWorker( + _In_ PVOID Parameter + ) +{ + PPH_THREAD_QUERY_DATA data = (PPH_THREAD_QUERY_DATA)Parameter; + LONG newSymbolsLoading; + + if (data->ThreadProvider->Terminating) + goto Done; + + newSymbolsLoading = _InterlockedIncrement(&data->ThreadProvider->SymbolsLoading); + + if (newSymbolsLoading == 1) + PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)TRUE); + + if (data->ThreadProvider->SymbolsLoadedRunId == 0) + PhLoadSymbolsThreadProvider(data->ThreadProvider); + + data->StartAddressString = PhGetSymbolFromAddress( + data->ThreadProvider->SymbolProvider, + data->ThreadItem->StartAddress, + &data->StartAddressResolveLevel, + &data->StartAddressFileName, + NULL, + NULL + ); + + if (data->StartAddressResolveLevel == PhsrlAddress && data->ThreadProvider->SymbolsLoadedRunId < data->RunId) + { + // The process may have loaded new modules, so load symbols for those and try again. + + PhLoadSymbolsThreadProvider(data->ThreadProvider); + + PhClearReference(&data->StartAddressString); + PhClearReference(&data->StartAddressFileName); + data->StartAddressString = PhGetSymbolFromAddress( + data->ThreadProvider->SymbolProvider, + data->ThreadItem->StartAddress, + &data->StartAddressResolveLevel, + &data->StartAddressFileName, + NULL, + NULL + ); + } + + newSymbolsLoading = _InterlockedDecrement(&data->ThreadProvider->SymbolsLoading); + + if (newSymbolsLoading == 0) + PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)FALSE); + + // Check if the process has services - we'll need to know before getting service tag/name + // information. + if (!data->ThreadProvider->HasServicesKnown) + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(data->ThreadProvider->ProcessId)) + { + data->ThreadProvider->HasServices = processItem->ServiceList && processItem->ServiceList->Count != 0; + PhDereferenceObject(processItem); + } + + data->ThreadProvider->HasServicesKnown = TRUE; + } + + // Get the service tag, and the service name. + if ( + data->ThreadProvider->SymbolProvider->IsRealHandle && + data->ThreadItem->ThreadHandle + ) + { + PVOID serviceTag; + + if (NT_SUCCESS(PhGetThreadServiceTag( + data->ThreadItem->ThreadHandle, + data->ThreadProvider->ProcessHandle, + &serviceTag + ))) + { + data->ServiceName = PhGetServiceNameFromTag( + data->ThreadProvider->ProcessId, + serviceTag + ); + } + } + +Done: + RtlInterlockedPushEntrySList(&data->ThreadProvider->QueryListHead, &data->ListEntry); + PhDereferenceObject(data->ThreadProvider); + + return STATUS_SUCCESS; +} + +VOID PhpQueueThreadQuery( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ PPH_THREAD_ITEM ThreadItem + ) +{ + PPH_THREAD_QUERY_DATA data; + + data = PhAllocate(sizeof(PH_THREAD_QUERY_DATA)); + memset(data, 0, sizeof(PH_THREAD_QUERY_DATA)); + PhSetReference(&data->ThreadProvider, ThreadProvider); + PhSetReference(&data->ThreadItem, ThreadItem); + data->RunId = ThreadProvider->RunId; + + PhpQueueThreadWorkQueueItem(PhpThreadQueryWorker, data); +} + +PPH_STRING PhpGetThreadBasicStartAddress( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ ULONG64 Address, + _Out_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel + ) +{ + ULONG64 modBase; + PPH_STRING fileName = NULL; + PPH_STRING baseName = NULL; + PPH_STRING symbol; + + modBase = PhGetModuleFromAddress( + ThreadProvider->SymbolProvider, + Address, + &fileName + ); + + if (fileName == NULL) + { + *ResolveLevel = PhsrlAddress; + + symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * sizeof(WCHAR)); + PhPrintPointer(symbol->Buffer, (PVOID)Address); + PhTrimToNullTerminatorString(symbol); + } + else + { + PH_FORMAT format[3]; + + baseName = PhGetBaseName(fileName); + *ResolveLevel = PhsrlModule; + + PhInitFormatSR(&format[0], baseName->sr); + PhInitFormatS(&format[1], L"+0x"); + PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase)); + + symbol = PhFormat(format, 3, baseName->Length + 6 + 32); + } + + if (fileName) + PhDereferenceObject(fileName); + if (baseName) + PhDereferenceObject(baseName); + + return symbol; +} + +static NTSTATUS PhpGetThreadCycleTime( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ PPH_THREAD_ITEM ThreadItem, + _Out_ PULONG64 CycleTime + ) +{ + if (ThreadProvider->ProcessId != SYSTEM_IDLE_PROCESS_ID) + { + return PhGetThreadCycleTime(ThreadItem->ThreadHandle, CycleTime); + } + else + { + if (HandleToUlong(ThreadItem->ThreadId) < (ULONG)PhSystemBasicInformation.NumberOfProcessors) + { + *CycleTime = PhCpuIdleCycleTime[HandleToUlong(ThreadItem->ThreadId)].QuadPart; + return STATUS_SUCCESS; + } + } + + return STATUS_INVALID_PARAMETER; +} + +PPH_STRING PhGetBasePriorityIncrementString( + _In_ LONG Increment + ) +{ + switch (Increment) + { + case THREAD_BASE_PRIORITY_LOWRT + 1: + case THREAD_BASE_PRIORITY_LOWRT: + return PhCreateString(L"Time critical"); + case THREAD_PRIORITY_HIGHEST: + return PhCreateString(L"Highest"); + case THREAD_PRIORITY_ABOVE_NORMAL: + return PhCreateString(L"Above normal"); + case THREAD_PRIORITY_NORMAL: + return PhCreateString(L"Normal"); + case THREAD_PRIORITY_BELOW_NORMAL: + return PhCreateString(L"Below normal"); + case THREAD_PRIORITY_LOWEST: + return PhCreateString(L"Lowest"); + case THREAD_BASE_PRIORITY_IDLE: + case THREAD_BASE_PRIORITY_IDLE - 1: + return PhCreateString(L"Idle"); + case THREAD_PRIORITY_ERROR_RETURN: + return NULL; + default: + return PhFormatString(L"%ld", Increment); + } +} + +VOID PhThreadProviderInitialUpdate( + _In_ PPH_THREAD_PROVIDER ThreadProvider + ) +{ + PVOID processes; + + if (NT_SUCCESS(PhEnumProcesses(&processes))) + { + PhpThreadProviderUpdate(ThreadProvider, processes); + PhFree(processes); + } +} + +VOID PhpThreadProviderCallbackHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (PhProcessInformation) + { + PhpThreadProviderUpdate((PPH_THREAD_PROVIDER)Context, PhProcessInformation); + } +} + +VOID PhpThreadProviderUpdate( + _In_ PPH_THREAD_PROVIDER ThreadProvider, + _In_ PVOID ProcessInformation + ) +{ + PPH_THREAD_PROVIDER threadProvider = ThreadProvider; + PSYSTEM_PROCESS_INFORMATION process; + SYSTEM_PROCESS_INFORMATION localProcess; + PSYSTEM_THREAD_INFORMATION threads; + ULONG numberOfThreads; + ULONG i; + + process = PhFindProcessInformation(ProcessInformation, threadProvider->ProcessId); + + if (!process) + { + // The process doesn't exist anymore. Pretend it does but has no threads. + process = &localProcess; + process->NumberOfThreads = 0; + } + + threads = process->Threads; + numberOfThreads = process->NumberOfThreads; + + // System Idle Process has one thread per CPU. They all have a TID of 0. We can't have duplicate + // TIDs, so we'll assign unique TIDs. + if (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID) + { + for (i = 0; i < numberOfThreads; i++) + { + threads[i].ClientId.UniqueThread = UlongToHandle(i); + } + } + + // Look for dead threads. + { + PPH_LIST threadsToRemove = NULL; + ULONG enumerationKey = 0; + PPH_THREAD_ITEM *threadItem; + + while (PhEnumHashtable(threadProvider->ThreadHashtable, (PVOID *)&threadItem, &enumerationKey)) + { + BOOLEAN found = FALSE; + + // Check if the thread still exists. + for (i = 0; i < numberOfThreads; i++) + { + PSYSTEM_THREAD_INFORMATION thread = &threads[i]; + + if ((*threadItem)->ThreadId == thread->ClientId.UniqueThread) + { + found = TRUE; + break; + } + } + + if (!found) + { + // Raise the thread removed event. + PhInvokeCallback(&threadProvider->ThreadRemovedEvent, *threadItem); + + if (!threadsToRemove) + threadsToRemove = PhCreateList(2); + + PhAddItemList(threadsToRemove, *threadItem); + } + } + + if (threadsToRemove) + { + PhAcquireFastLockExclusive(&threadProvider->ThreadHashtableLock); + + for (i = 0; i < threadsToRemove->Count; i++) + { + PhpRemoveThreadItem( + threadProvider, + (PPH_THREAD_ITEM)threadsToRemove->Items[i] + ); + } + + PhReleaseFastLockExclusive(&threadProvider->ThreadHashtableLock); + PhDereferenceObject(threadsToRemove); + } + } + + // Go through the queued thread query data. + { + PSLIST_ENTRY entry; + PPH_THREAD_QUERY_DATA data; + + entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead); + + while (entry) + { + data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry); + entry = entry->Next; + + if (data->StartAddressResolveLevel == PhsrlFunction && data->StartAddressString) + { + PhSwapReference(&data->ThreadItem->StartAddressString, data->StartAddressString); + PhSwapReference(&data->ThreadItem->StartAddressFileName, data->StartAddressFileName); + data->ThreadItem->StartAddressResolveLevel = data->StartAddressResolveLevel; + } + + PhMoveReference(&data->ThreadItem->ServiceName, data->ServiceName); + + data->ThreadItem->JustResolved = TRUE; + + if (data->StartAddressString) PhDereferenceObject(data->StartAddressString); + if (data->StartAddressFileName) PhDereferenceObject(data->StartAddressFileName); + PhDereferenceObject(data->ThreadItem); + PhFree(data); + } + } + + // Look for new threads and update existing ones. + for (i = 0; i < numberOfThreads; i++) + { + PSYSTEM_THREAD_INFORMATION thread = &threads[i]; + PPH_THREAD_ITEM threadItem; + THREAD_BASIC_INFORMATION basicInfo; + + threadItem = PhReferenceThreadItem(threadProvider, thread->ClientId.UniqueThread); + + if (!threadItem) + { + PVOID startAddress = NULL; + + threadItem = PhCreateThreadItem(thread->ClientId.UniqueThread); + + threadItem->CreateTime = thread->CreateTime; + threadItem->KernelTime = thread->KernelTime; + threadItem->UserTime = thread->UserTime; + + PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches); + threadItem->Priority = thread->Priority; + threadItem->BasePriority = thread->BasePriority; + threadItem->State = (KTHREAD_STATE)thread->ThreadState; + threadItem->WaitReason = thread->WaitReason; + + // Try to open a handle to the thread. + if (!NT_SUCCESS(PhOpenThread( + &threadItem->ThreadHandle, + THREAD_QUERY_INFORMATION, + threadItem->ThreadId + ))) + { + PhOpenThread( + &threadItem->ThreadHandle, + THREAD_QUERY_LIMITED_INFORMATION, + threadItem->ThreadId + ); + } + + // Get the cycle count. + { + ULONG64 cycles; + + if (NT_SUCCESS(PhpGetThreadCycleTime( + threadProvider, + threadItem, + &cycles + ))) + { + PhUpdateDelta(&threadItem->CyclesDelta, cycles); + } + } + + // Initialize the CPU time deltas. + PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); + PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); + + // Try to get the start address. + + if (threadItem->ThreadHandle) + { + PhGetThreadStartAddress(threadItem->ThreadHandle, &startAddress); + } + + if (!startAddress) + startAddress = thread->StartAddress; + + threadItem->StartAddress = (ULONG64)startAddress; + + // Get the base priority increment (relative to the process priority). + if (threadItem->ThreadHandle && NT_SUCCESS(PhGetThreadBasicInformation( + threadItem->ThreadHandle, + &basicInfo + ))) + { + threadItem->BasePriorityIncrement = basicInfo.BasePriority; + } + else + { + threadItem->BasePriorityIncrement = THREAD_PRIORITY_ERROR_RETURN; + } + + if (threadProvider->SymbolsLoadedRunId != 0) + { + threadItem->StartAddressString = PhpGetThreadBasicStartAddress( + threadProvider, + threadItem->StartAddress, + &threadItem->StartAddressResolveLevel + ); + } + + if (!threadItem->StartAddressString) + { + threadItem->StartAddressResolveLevel = PhsrlAddress; + threadItem->StartAddressString = PhCreateStringEx(NULL, PH_PTR_STR_LEN * sizeof(WCHAR)); + PhPrintPointer( + threadItem->StartAddressString->Buffer, + (PVOID)threadItem->StartAddress + ); + PhTrimToNullTerminatorString(threadItem->StartAddressString); + } + + // Is it a GUI thread? + { + GUITHREADINFO info = { sizeof(GUITHREADINFO) }; + + threadItem->IsGuiThread = !!GetGUIThreadInfo(HandleToUlong(threadItem->ThreadId), &info); + } + + PhpQueueThreadQuery(threadProvider, threadItem); + + // Add the thread item to the hashtable. + PhAcquireFastLockExclusive(&threadProvider->ThreadHashtableLock); + PhAddEntryHashtable(threadProvider->ThreadHashtable, &threadItem); + PhReleaseFastLockExclusive(&threadProvider->ThreadHashtableLock); + + // Raise the thread added event. + PhInvokeCallback(&threadProvider->ThreadAddedEvent, threadItem); + } + else + { + BOOLEAN modified = FALSE; + + if (threadItem->JustResolved) + modified = TRUE; + + threadItem->KernelTime = thread->KernelTime; + threadItem->UserTime = thread->UserTime; + + threadItem->Priority = thread->Priority; + threadItem->BasePriority = thread->BasePriority; + + threadItem->State = (KTHREAD_STATE)thread->ThreadState; + + if (threadItem->WaitReason != thread->WaitReason) + { + threadItem->WaitReason = thread->WaitReason; + modified = TRUE; + } + + // If the resolve level is only at address, it probably + // means symbols weren't loaded the last time we + // tried to get the start address. Try again. + if (threadItem->StartAddressResolveLevel == PhsrlAddress) + { + if (threadProvider->SymbolsLoadedRunId != 0) + { + PPH_STRING newStartAddressString; + + newStartAddressString = PhpGetThreadBasicStartAddress( + threadProvider, + threadItem->StartAddress, + &threadItem->StartAddressResolveLevel + ); + + PhMoveReference( + &threadItem->StartAddressString, + newStartAddressString + ); + + modified = TRUE; + } + } + + // If we couldn't resolve the start address to a module+offset, use the StartAddress + // instead of the Win32StartAddress and try again. Note that we check the resolve level + // again because we may have changed it in the previous block. + if (threadItem->JustResolved && + threadItem->StartAddressResolveLevel == PhsrlAddress) + { + if (threadItem->StartAddress != (ULONG64)thread->StartAddress) + { + threadItem->StartAddress = (ULONG64)thread->StartAddress; + PhpQueueThreadQuery(threadProvider, threadItem); + } + } + + // Update the context switch count. + { + ULONG oldDelta; + + oldDelta = threadItem->ContextSwitchesDelta.Delta; + PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches); + + if (threadItem->ContextSwitchesDelta.Delta != oldDelta) + { + modified = TRUE; + } + } + + // Update the cycle count. + { + ULONG64 cycles; + ULONG64 oldDelta; + + oldDelta = threadItem->CyclesDelta.Delta; + + if (NT_SUCCESS(PhpGetThreadCycleTime( + threadProvider, + threadItem, + &cycles + ))) + { + PhUpdateDelta(&threadItem->CyclesDelta, cycles); + + if (threadItem->CyclesDelta.Delta != oldDelta) + { + modified = TRUE; + } + } + } + + // Update the CPU time deltas. + PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); + PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); + + // Update the CPU usage. + // If the cycle time isn't available, we'll fall back to using the CPU time. + if (PhEnableCycleCpuUsage && (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID || threadItem->ThreadHandle)) + { + threadItem->CpuUsage = (FLOAT)threadItem->CyclesDelta.Delta / PhCpuTotalCycleDelta; + } + else + { + threadItem->CpuUsage = (FLOAT)(threadItem->CpuKernelDelta.Delta + threadItem->CpuUserDelta.Delta) / + (PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta + PhCpuIdleDelta.Delta); + } + + // Update the base priority increment. + { + LONG oldBasePriorityIncrement = threadItem->BasePriorityIncrement; + + if (threadItem->ThreadHandle && NT_SUCCESS(PhGetThreadBasicInformation( + threadItem->ThreadHandle, + &basicInfo + ))) + { + threadItem->BasePriorityIncrement = basicInfo.BasePriority; + } + else + { + threadItem->BasePriorityIncrement = THREAD_PRIORITY_ERROR_RETURN; + } + + if (threadItem->BasePriorityIncrement != oldBasePriorityIncrement) + { + modified = TRUE; + } + } + + // Update the GUI thread status. + { + GUITHREADINFO info = { sizeof(GUITHREADINFO) }; + BOOLEAN oldIsGuiThread = threadItem->IsGuiThread; + + threadItem->IsGuiThread = !!GetGUIThreadInfo(HandleToUlong(threadItem->ThreadId), &info); + + if (threadItem->IsGuiThread != oldIsGuiThread) + modified = TRUE; + } + + threadItem->JustResolved = FALSE; + + if (modified) + { + // Raise the thread modified event. + PhInvokeCallback(&threadProvider->ThreadModifiedEvent, threadItem); + } + + PhDereferenceObject(threadItem); + } + } + + PhInvokeCallback(&threadProvider->UpdatedEvent, NULL); + threadProvider->RunId++; +} diff --git a/ProcessHacker/thrdstk.c b/ProcessHacker/thrdstk.c index 3e64ca2dc9f1..f4b3ecd6ca2c 100644 --- a/ProcessHacker/thrdstk.c +++ b/ProcessHacker/thrdstk.c @@ -1,697 +1,1412 @@ -/* - * Process Hacker - - * thread stack viewer - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include - -#include -#include -#include -#include - -#define WM_PH_COMPLETED (WM_APP + 301) -#define WM_PH_STATUS_UPDATE (WM_APP + 302) - -typedef struct _THREAD_STACK_CONTEXT -{ - HANDLE ProcessId; - HANDLE ThreadId; - HANDLE ThreadHandle; - HWND ListViewHandle; - PPH_THREAD_PROVIDER ThreadProvider; - PPH_SYMBOL_PROVIDER SymbolProvider; - BOOLEAN CustomWalk; - - BOOLEAN StopWalk; - PPH_LIST List; - PPH_LIST NewList; - HWND ProgressWindowHandle; - NTSTATUS WalkStatus; - PPH_STRING StatusMessage; - PH_QUEUED_LOCK StatusLock; -} THREAD_STACK_CONTEXT, *PTHREAD_STACK_CONTEXT; - -typedef struct _THREAD_STACK_ITEM -{ - PH_THREAD_STACK_FRAME StackFrame; - ULONG Index; - PPH_STRING Symbol; -} THREAD_STACK_ITEM, *PTHREAD_STACK_ITEM; - -INT_PTR CALLBACK PhpThreadStackDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhpFreeThreadStackItem( - _In_ PTHREAD_STACK_ITEM StackItem - ); - -NTSTATUS PhpRefreshThreadStack( - _In_ HWND hwnd, - _In_ PTHREAD_STACK_CONTEXT ThreadStackContext - ); - -INT_PTR CALLBACK PhpThreadStackProgressDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static RECT MinimumSize = { -1, -1, -1, -1 }; - -VOID PhShowThreadStackDialog( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId, - _In_ PPH_THREAD_PROVIDER ThreadProvider - ) -{ - NTSTATUS status; - THREAD_STACK_CONTEXT threadStackContext; - HANDLE threadHandle = NULL; - - // If the user is trying to view a system thread stack - // but KProcessHacker is not loaded, show an error message. - if (ProcessId == SYSTEM_PROCESS_ID && !KphIsConnected()) - { - PhShowError(ParentWindowHandle, PH_KPH_ERROR_MESSAGE); - return; - } - - memset(&threadStackContext, 0, sizeof(THREAD_STACK_CONTEXT)); - threadStackContext.ProcessId = ProcessId; - threadStackContext.ThreadId = ThreadId; - threadStackContext.ThreadProvider = ThreadProvider; - threadStackContext.SymbolProvider = ThreadProvider->SymbolProvider; - - if (!NT_SUCCESS(status = PhOpenThread( - &threadHandle, - THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, - ThreadId - ))) - { - if (KphIsConnected()) - { - status = PhOpenThread( - &threadHandle, - ThreadQueryAccess, - ThreadId - ); - } - } - - if (!NT_SUCCESS(status)) - { - PhShowStatus(ParentWindowHandle, L"Unable to open the thread", status, 0); - return; - } - - threadStackContext.ThreadHandle = threadHandle; - threadStackContext.List = PhCreateList(10); - threadStackContext.NewList = PhCreateList(10); - PhInitializeQueuedLock(&threadStackContext.StatusLock); - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_THRDSTACK), - ParentWindowHandle, - PhpThreadStackDlgProc, - (LPARAM)&threadStackContext - ); - - PhClearReference(&threadStackContext.StatusMessage); - PhDereferenceObject(threadStackContext.NewList); - PhDereferenceObject(threadStackContext.List); - - if (threadStackContext.ThreadHandle) - NtClose(threadStackContext.ThreadHandle); -} - -static INT_PTR CALLBACK PhpThreadStackDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - PTHREAD_STACK_CONTEXT threadStackContext; - PPH_STRING title; - HWND lvHandle; - PPH_LAYOUT_MANAGER layoutManager; - - threadStackContext = (PTHREAD_STACK_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext); - - title = PhFormatString(L"Stack - thread %u", HandleToUlong(threadStackContext->ThreadId)); - SetWindowText(hwndDlg, title->Buffer); - PhDereferenceObject(title); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 30, L" "); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 300, L"Name"); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhLoadListViewColumnsFromSetting(L"ThreadStackListViewColumns", lvHandle); - - threadStackContext->ListViewHandle = lvHandle; - - layoutManager = PhAllocate(sizeof(PH_LAYOUT_MANAGER)); - PhInitializeLayoutManager(layoutManager, hwndDlg); - SetProp(hwndDlg, L"LayoutManager", (HANDLE)layoutManager); - - PhAddLayoutItem(layoutManager, lvHandle, NULL, - PH_ANCHOR_ALL); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_COPY), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhAddLayoutItem(layoutManager, GetDlgItem(hwndDlg, IDOK), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - if (MinimumSize.left == -1) - { - RECT rect; - - rect.left = 0; - rect.top = 0; - rect.right = 190; - rect.bottom = 120; - MapDialogRect(hwndDlg, &rect); - MinimumSize = rect; - MinimumSize.left = 0; - } - - PhLoadWindowPlacementFromSetting(NULL, L"ThreadStackWindowSize", hwndDlg); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - if (PhPluginsEnabled) - { - PH_PLUGIN_THREAD_STACK_CONTROL control; - - control.Type = PluginThreadStackInitializing; - control.UniqueKey = threadStackContext; - control.u.Initializing.ProcessId = threadStackContext->ProcessId; - control.u.Initializing.ThreadId = threadStackContext->ThreadId; - control.u.Initializing.ThreadHandle = threadStackContext->ThreadHandle; - control.u.Initializing.SymbolProvider = threadStackContext->SymbolProvider; - control.u.Initializing.CustomWalk = FALSE; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - - threadStackContext->CustomWalk = control.u.Initializing.CustomWalk; - } - - status = PhpRefreshThreadStack(hwndDlg, threadStackContext); - - if (status == STATUS_ABANDONED) - EndDialog(hwndDlg, IDCANCEL); - else if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); - } - break; - case WM_DESTROY: - { - PPH_LAYOUT_MANAGER layoutManager; - PTHREAD_STACK_CONTEXT threadStackContext; - ULONG i; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhDeleteLayoutManager(layoutManager); - PhFree(layoutManager); - - threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (PhPluginsEnabled) - { - PH_PLUGIN_THREAD_STACK_CONTROL control; - - control.Type = PluginThreadStackUninitializing; - control.UniqueKey = threadStackContext; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - } - - for (i = 0; i < threadStackContext->List->Count; i++) - PhpFreeThreadStackItem(threadStackContext->List->Items[i]); - - PhSaveListViewColumnsToSetting(L"ThreadStackListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); - PhSaveWindowPlacementToSetting(NULL, L"ThreadStackWindowSize", hwndDlg); - - RemoveProp(hwndDlg, PhMakeContextAtom()); - RemoveProp(hwndDlg, L"LayoutManager"); - } - break; - case WM_COMMAND: - { - INT id = LOWORD(wParam); - - switch (id) - { - case IDCANCEL: // Esc and X button to close - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_REFRESH: - { - NTSTATUS status; - - if (!NT_SUCCESS(status = PhpRefreshThreadStack( - hwndDlg, - (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()) - ))) - { - PhShowStatus(hwndDlg, L"Unable to load the stack", status, 0); - } - } - break; - case IDC_COPY: - { - HWND lvHandle; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - if (ListView_GetSelectedCount(lvHandle) == 0) - PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED); - - PhCopyListView(lvHandle); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)lvHandle, TRUE); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case LVN_GETINFOTIP: - { - LPNMLVGETINFOTIP getInfoTip = (LPNMLVGETINFOTIP)header; - HWND lvHandle; - PTHREAD_STACK_CONTEXT threadStackContext; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - if (header->hwndFrom == lvHandle) - { - PTHREAD_STACK_ITEM stackItem; - PPH_THREAD_STACK_FRAME stackFrame; - - if (PhGetListViewItemParam(lvHandle, getInfoTip->iItem, &stackItem)) - { - PH_STRING_BUILDER stringBuilder; - PPH_STRING fileName; - PH_SYMBOL_LINE_INFORMATION lineInfo; - - stackFrame = &stackItem->StackFrame; - PhInitializeStringBuilder(&stringBuilder, 40); - - PhAppendFormatStringBuilder( - &stringBuilder, - L"Stack: 0x%Ix, Frame: 0x%Ix\n", - stackFrame->StackAddress, - stackFrame->FrameAddress - ); - - // There are no params for kernel-mode stack traces. - if ((ULONG_PTR)stackFrame->PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress) - { - PhAppendFormatStringBuilder( - &stringBuilder, - L"Parameters: 0x%Ix, 0x%Ix, 0x%Ix, 0x%Ix\n", - stackFrame->Params[0], - stackFrame->Params[1], - stackFrame->Params[2], - stackFrame->Params[3] - ); - } - - if (PhGetLineFromAddress( - threadStackContext->SymbolProvider, - (ULONG64)stackFrame->PcAddress, - &fileName, - NULL, - &lineInfo - )) - { - PhAppendFormatStringBuilder( - &stringBuilder, - L"File: %s: line %u\n", - fileName->Buffer, - lineInfo.LineNumber - ); - PhDereferenceObject(fileName); - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - if (PhPluginsEnabled) - { - PH_PLUGIN_THREAD_STACK_CONTROL control; - - control.Type = PluginThreadStackGetTooltip; - control.UniqueKey = threadStackContext; - control.u.GetTooltip.StackFrame = stackFrame; - control.u.GetTooltip.StringBuilder = &stringBuilder; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - } - - PhCopyListViewInfoTip(getInfoTip, &stringBuilder.String->sr); - PhDeleteStringBuilder(&stringBuilder); - } - } - } - break; - } - } - break; - case WM_SIZE: - { - PPH_LAYOUT_MANAGER layoutManager; - - layoutManager = (PPH_LAYOUT_MANAGER)GetProp(hwndDlg, L"LayoutManager"); - PhLayoutManagerLayout(layoutManager); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - } - - return FALSE; -} - -static VOID PhpFreeThreadStackItem( - _In_ PTHREAD_STACK_ITEM StackItem - ) -{ - PhClearReference(&StackItem->Symbol); - PhFree(StackItem); -} - -static NTSTATUS PhpRefreshThreadStack( - _In_ HWND hwnd, - _In_ PTHREAD_STACK_CONTEXT ThreadStackContext - ) -{ - ULONG i; - - ThreadStackContext->StopWalk = FALSE; - PhMoveReference(&ThreadStackContext->StatusMessage, PhCreateString(L"Loading stack...")); - - DialogBoxParam( - PhInstanceHandle, - MAKEINTRESOURCE(IDD_PROGRESS), - hwnd, - PhpThreadStackProgressDlgProc, - (LPARAM)ThreadStackContext - ); - - if (!ThreadStackContext->StopWalk && NT_SUCCESS(ThreadStackContext->WalkStatus)) - { - for (i = 0; i < ThreadStackContext->List->Count; i++) - PhpFreeThreadStackItem(ThreadStackContext->List->Items[i]); - - PhDereferenceObject(ThreadStackContext->List); - ThreadStackContext->List = ThreadStackContext->NewList; - ThreadStackContext->NewList = PhCreateList(10); - - SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, FALSE, 0); - ListView_DeleteAllItems(ThreadStackContext->ListViewHandle); - - for (i = 0; i < ThreadStackContext->List->Count; i++) - { - PTHREAD_STACK_ITEM item = ThreadStackContext->List->Items[i]; - INT lvItemIndex; - WCHAR integerString[PH_INT32_STR_LEN_1]; - - PhPrintUInt32(integerString, item->Index); - lvItemIndex = PhAddListViewItem(ThreadStackContext->ListViewHandle, MAXINT, integerString, item); - PhSetListViewSubItem(ThreadStackContext->ListViewHandle, lvItemIndex, 1, PhGetStringOrDefault(item->Symbol, L"???")); - } - - SendMessage(ThreadStackContext->ListViewHandle, WM_SETREDRAW, TRUE, 0); - InvalidateRect(ThreadStackContext->ListViewHandle, NULL, FALSE); - } - else - { - for (i = 0; i < ThreadStackContext->NewList->Count; i++) - PhpFreeThreadStackItem(ThreadStackContext->NewList->Items[i]); - - PhClearList(ThreadStackContext->NewList); - } - - if (ThreadStackContext->StopWalk) - return STATUS_ABANDONED; - - return ThreadStackContext->WalkStatus; -} - -static BOOLEAN NTAPI PhpWalkThreadStackCallback( - _In_ PPH_THREAD_STACK_FRAME StackFrame, - _In_opt_ PVOID Context - ) -{ - PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)Context; - PPH_STRING symbol; - PTHREAD_STACK_ITEM item; - - if (threadStackContext->StopWalk) - return FALSE; - - PhAcquireQueuedLockExclusive(&threadStackContext->StatusLock); - PhMoveReference(&threadStackContext->StatusMessage, - PhFormatString(L"Processing frame %u...", threadStackContext->NewList->Count)); - PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock); - PostMessage(threadStackContext->ProgressWindowHandle, WM_PH_STATUS_UPDATE, 0, 0); - - symbol = PhGetSymbolFromAddress( - threadStackContext->SymbolProvider, - (ULONG64)StackFrame->PcAddress, - NULL, - NULL, - NULL, - NULL - ); - - if (symbol && - (StackFrame->Flags & PH_THREAD_STACK_FRAME_I386) && - !(StackFrame->Flags & PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT)) - { - PhMoveReference(&symbol, PhConcatStrings2(symbol->Buffer, L" (No unwind info)")); - } - - item = PhAllocate(sizeof(THREAD_STACK_ITEM)); - item->StackFrame = *StackFrame; - item->Index = threadStackContext->NewList->Count; - - if (PhPluginsEnabled) - { - PH_PLUGIN_THREAD_STACK_CONTROL control; - - control.Type = PluginThreadStackResolveSymbol; - control.UniqueKey = threadStackContext; - control.u.ResolveSymbol.StackFrame = StackFrame; - control.u.ResolveSymbol.Symbol = symbol; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - - symbol = control.u.ResolveSymbol.Symbol; - } - - item->Symbol = symbol; - PhAddItemList(threadStackContext->NewList, item); - - return TRUE; -} - -static NTSTATUS PhpRefreshThreadStackThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - NTSTATUS status; - PTHREAD_STACK_CONTEXT threadStackContext = Parameter; - CLIENT_ID clientId; - BOOLEAN defaultWalk; - - PhInitializeAutoPool(&autoPool); - - PhLoadSymbolsThreadProvider(threadStackContext->ThreadProvider); - - clientId.UniqueProcess = threadStackContext->ProcessId; - clientId.UniqueThread = threadStackContext->ThreadId; - defaultWalk = TRUE; - - if (threadStackContext->CustomWalk) - { - PH_PLUGIN_THREAD_STACK_CONTROL control; - - control.Type = PluginThreadStackWalkStack; - control.UniqueKey = threadStackContext; - control.u.WalkStack.Status = STATUS_UNSUCCESSFUL; - control.u.WalkStack.ThreadHandle = threadStackContext->ThreadHandle; - control.u.WalkStack.ProcessHandle = threadStackContext->SymbolProvider->ProcessHandle; - control.u.WalkStack.ClientId = &clientId; - control.u.WalkStack.Flags = PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK; - control.u.WalkStack.Callback = PhpWalkThreadStackCallback; - control.u.WalkStack.CallbackContext = threadStackContext; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - status = control.u.WalkStack.Status; - - if (NT_SUCCESS(status)) - defaultWalk = FALSE; - } - - if (defaultWalk) - { - PH_PLUGIN_THREAD_STACK_CONTROL control; - - control.UniqueKey = threadStackContext; - - if (PhPluginsEnabled) - { - control.Type = PluginThreadStackBeginDefaultWalkStack; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - } - - status = PhWalkThreadStack( - threadStackContext->ThreadHandle, - threadStackContext->SymbolProvider->ProcessHandle, - &clientId, - threadStackContext->SymbolProvider, - PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK, - PhpWalkThreadStackCallback, - threadStackContext - ); - - if (PhPluginsEnabled) - { - control.Type = PluginThreadStackEndDefaultWalkStack; - PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); - } - } - - if (threadStackContext->NewList->Count != 0) - status = STATUS_SUCCESS; - - threadStackContext->WalkStatus = status; - PostMessage(threadStackContext->ProgressWindowHandle, WM_PH_COMPLETED, 0, 0); - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -static INT_PTR CALLBACK PhpThreadStackProgressDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PTHREAD_STACK_CONTEXT threadStackContext; - HANDLE threadHandle; - - threadStackContext = (PTHREAD_STACK_CONTEXT)lParam; - SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)threadStackContext); - threadStackContext->ProgressWindowHandle = hwndDlg; - - if (threadHandle = PhCreateThread(0, PhpRefreshThreadStackThreadStart, threadStackContext)) - { - NtClose(threadHandle); - } - else - { - threadStackContext->WalkStatus = STATUS_UNSUCCESSFUL; - EndDialog(hwndDlg, IDOK); - break; - } - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); - SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); - SetWindowText(hwndDlg, L"Loading stack..."); - } - break; - case WM_DESTROY: - { - RemoveProp(hwndDlg, PhMakeContextAtom()); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - - EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE); - threadStackContext->StopWalk = TRUE; - } - break; - } - } - break; - case WM_PH_COMPLETED: - { - EndDialog(hwndDlg, IDOK); - } - break; - case WM_PH_STATUS_UPDATE: - { - PTHREAD_STACK_CONTEXT threadStackContext = (PTHREAD_STACK_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom()); - PPH_STRING message; - - PhAcquireQueuedLockExclusive(&threadStackContext->StatusLock); - message = threadStackContext->StatusMessage; - PhReferenceObject(message); - PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock); - - SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, message->Buffer); - PhDereferenceObject(message); - } - break; - } - - return 0; -} +/* + * Process Hacker - + * thread stack viewer + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define WM_PH_COMPLETED (WM_APP + 301) +//#define WM_PH_STATUS_UPDATE (WM_APP + 302) +#define WM_PH_SHOWSTACKMENU (WM_APP + 303) + +static PPH_OBJECT_TYPE PhThreadStackContextType = NULL; +static RECT MinimumSize = { -1, -1, -1, -1 }; + +typedef struct _PH_THREAD_STACK_CONTEXT +{ + HANDLE ProcessId; + HANDLE ThreadId; + HANDLE ThreadHandle; + PPH_THREAD_PROVIDER ThreadProvider; + PPH_SYMBOL_PROVIDER SymbolProvider; + + BOOLEAN CustomWalk; + BOOLEAN StopWalk; + BOOLEAN EnableCloseDialog; + + PPH_LIST List; + PPH_LIST NewList; + + HWND TaskDialogHandle; + + NTSTATUS WalkStatus; + PPH_STRING StatusMessage; + PPH_STRING StatusContent; + PH_QUEUED_LOCK StatusLock; + + PH_LAYOUT_MANAGER LayoutManager; + + HWND WindowHandle; + HWND TreeNewHandle; + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; + PPH_LIST NodeRootList; + WNDPROC ThreadStackStatusDefaultWindowProc; + PH_CALLBACK_REGISTRATION SymbolProviderEventRegistration; +} PH_THREAD_STACK_CONTEXT, *PPH_THREAD_STACK_CONTEXT; + +typedef struct _THREAD_STACK_ITEM +{ + PH_THREAD_STACK_FRAME StackFrame; + ULONG Index; + PPH_STRING Symbol; +} THREAD_STACK_ITEM, *PTHREAD_STACK_ITEM; + +typedef enum _PH_STACK_TREE_COLUMN_ITEM_NAME +{ + PH_STACK_TREE_COLUMN_INDEX, + PH_STACK_TREE_COLUMN_SYMBOL, + PH_STACK_TREE_COLUMN_STACKADDRESS, + PH_STACK_TREE_COLUMN_FRAMEADDRESS, + PH_STACK_TREE_COLUMN_PARAMETER1, + PH_STACK_TREE_COLUMN_PARAMETER2, + PH_STACK_TREE_COLUMN_PARAMETER3, + PH_STACK_TREE_COLUMN_PARAMETER4, + PH_STACK_TREE_COLUMN_CONTROLADDRESS, + PH_STACK_TREE_COLUMN_RETURNADDRESS, + TREE_COLUMN_ITEM_MAXIMUM +} PH_STACK_TREE_COLUMN_ITEM_NAME; + +typedef struct _PH_STACK_TREE_ROOT_NODE +{ + PH_TREENEW_NODE Node; + + PH_THREAD_STACK_FRAME StackFrame; + + ULONG Index; + PPH_STRING TooltipText; + PPH_STRING IndexString; + PPH_STRING SymbolString; + WCHAR StackAddressString[PH_PTR_STR_LEN_1]; + WCHAR FrameAddressString[PH_PTR_STR_LEN_1]; + WCHAR Parameter1String[PH_PTR_STR_LEN_1]; + WCHAR Parameter2String[PH_PTR_STR_LEN_1]; + WCHAR Parameter3String[PH_PTR_STR_LEN_1]; + WCHAR Parameter4String[PH_PTR_STR_LEN_1]; + WCHAR PcAddressString[PH_PTR_STR_LEN_1]; + WCHAR ReturnAddressString[PH_PTR_STR_LEN_1]; + + PH_STRINGREF TextCache[TREE_COLUMN_ITEM_MAXIMUM]; +} PH_STACK_TREE_ROOT_NODE, *PPH_STACK_TREE_ROOT_NODE; + +INT_PTR CALLBACK PhpThreadStackDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhpFreeThreadStackItem( + _In_ PTHREAD_STACK_ITEM StackItem + ); + +NTSTATUS PhpRefreshThreadStack( + _In_ HWND hwnd, + _In_ PPH_THREAD_STACK_CONTEXT ThreadStackContext + ); + +#define SORT_FUNCTION(Column) ThreadStackTreeNewCompare##Column +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl ThreadStackTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPH_STACK_TREE_ROOT_NODE node1 = *(PPH_STACK_TREE_ROOT_NODE*)_elem1; \ + PPH_STACK_TREE_ROOT_NODE node2 = *(PPH_STACK_TREE_ROOT_NODE*)_elem2; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ + \ + return PhModifySort(sortResult, ((PPH_THREAD_STACK_CONTEXT)_context)->TreeNewSortOrder); \ +} + +BEGIN_SORT_FUNCTION(Index) +{ + sortResult = uint64cmp(node1->Index, node2->Index); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Symbol) +{ + sortResult = PhCompareString(node1->SymbolString, node2->SymbolString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StackAddress) +{ + sortResult = PhCompareStringZ(node1->StackAddressString, node2->StackAddressString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(FrameAddress) +{ + sortResult = PhCompareStringZ(node1->FrameAddressString, node2->FrameAddressString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StackParameter1) +{ + sortResult = PhCompareStringZ(node1->Parameter1String, node2->Parameter1String, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StackParameter2) +{ + sortResult = PhCompareStringZ(node1->Parameter2String, node2->Parameter2String, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StackParameter3) +{ + sortResult = PhCompareStringZ(node1->Parameter3String, node2->Parameter3String, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(StackParameter4) +{ + sortResult = PhCompareStringZ(node1->Parameter4String, node2->Parameter4String, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ControlAddress) +{ + sortResult = PhCompareStringZ(node1->PcAddressString, node2->PcAddressString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ReturnAddress) +{ + sortResult = PhCompareStringZ(node1->ReturnAddressString, node2->ReturnAddressString, TRUE); +} +END_SORT_FUNCTION + +VOID ThreadStackLoadSettingsTreeList( + _Inout_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhGetStringSetting(L"ThreadStackTreeListColumns"); + PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); + PhDereferenceObject(settings); +} + +VOID ThreadStackSaveSettingsTreeList( + _Inout_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + PPH_STRING settings; + + settings = PhCmSaveSettings(Context->TreeNewHandle); + PhSetStringSetting2(L"ThreadStackTreeListColumns", &settings->sr); + PhDereferenceObject(settings); +} + +BOOLEAN ThreadStackNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_STACK_TREE_ROOT_NODE node1 = *(PPH_STACK_TREE_ROOT_NODE *)Entry1; + PPH_STACK_TREE_ROOT_NODE node2 = *(PPH_STACK_TREE_ROOT_NODE *)Entry2; + + return node1->Index == node2->Index; +} + +ULONG ThreadStackNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return (*(PPH_STACK_TREE_ROOT_NODE*)Entry)->Index; +} + +VOID DestroyThreadStackNode( + _In_ PPH_STACK_TREE_ROOT_NODE Node + ) +{ + if (Node->TooltipText) + PhDereferenceObject(Node->TooltipText); + if (Node->IndexString) + PhDereferenceObject(Node->IndexString); + if (Node->SymbolString) + PhDereferenceObject(Node->SymbolString); + + PhDereferenceObject(Node); +} + +PPH_STACK_TREE_ROOT_NODE AddThreadStackNode( + _Inout_ PPH_THREAD_STACK_CONTEXT Context, + _In_ ULONG Index + ) +{ + PPH_STACK_TREE_ROOT_NODE threadStackNode; + + threadStackNode = PhCreateAlloc(sizeof(PH_STACK_TREE_ROOT_NODE)); + memset(threadStackNode, 0, sizeof(PH_STACK_TREE_ROOT_NODE)); + + PhInitializeTreeNewNode(&threadStackNode->Node); + + memset(threadStackNode->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM); + threadStackNode->Node.TextCache = threadStackNode->TextCache; + threadStackNode->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM; + + threadStackNode->Index = Index; + + PhAddEntryHashtable(Context->NodeHashtable, &threadStackNode); + PhAddItemList(Context->NodeList, threadStackNode); + + // TreeNew_NodesStructured(Context->TreeNewHandle); + + return threadStackNode; +} + +PPH_STACK_TREE_ROOT_NODE FindThreadStackNode( + _In_ PPH_THREAD_STACK_CONTEXT Context, + _In_ ULONG Index + ) +{ + PH_STACK_TREE_ROOT_NODE lookupThreadStackNode; + PPH_STACK_TREE_ROOT_NODE lookupThreadStackNodePtr = &lookupThreadStackNode; + PPH_STACK_TREE_ROOT_NODE *threadStackNode; + + lookupThreadStackNode.Index = Index; + + threadStackNode = (PPH_STACK_TREE_ROOT_NODE*)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupThreadStackNodePtr + ); + + if (threadStackNode) + return *threadStackNode; + else + return NULL; +} + +VOID RemoveThreadStackNode( + _In_ PPH_THREAD_STACK_CONTEXT Context, + _In_ PPH_STACK_TREE_ROOT_NODE Node +) +{ + ULONG index = 0; + + PhRemoveEntryHashtable(Context->NodeHashtable, &Node); + + if ((index = PhFindItemList(Context->NodeList, Node)) != ULONG_MAX) + { + PhRemoveItemList(Context->NodeList, index); + } + + DestroyThreadStackNode(Node); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID UpdateThreadStackNode( + _In_ PPH_THREAD_STACK_CONTEXT Context, + _In_ PPH_STACK_TREE_ROOT_NODE Node + ) +{ + memset(Node->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM); + + PhInvalidateTreeNewNode(&Node->Node, TN_CACHE_COLOR); + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +BOOLEAN NTAPI ThreadStackTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_STACK_CONTEXT context = Context; + PPH_STACK_TREE_ROOT_NODE node; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + node = (PPH_STACK_TREE_ROOT_NODE)getChildren->Node; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Index), + SORT_FUNCTION(Symbol), + SORT_FUNCTION(StackAddress), + SORT_FUNCTION(FrameAddress), + SORT_FUNCTION(StackParameter1), + SORT_FUNCTION(StackParameter2), + SORT_FUNCTION(StackParameter3), + SORT_FUNCTION(StackParameter4), + SORT_FUNCTION(ControlAddress), + SORT_FUNCTION(ReturnAddress), + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortColumn < TREE_COLUMN_ITEM_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1; + node = (PPH_STACK_TREE_ROOT_NODE)isLeaf->Node; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1; + node = (PPH_STACK_TREE_ROOT_NODE)getCellText->Node; + + switch (getCellText->Id) + { + case PH_STACK_TREE_COLUMN_INDEX: + { + PhMoveReference(&node->IndexString, PhFormatUInt64(node->Index, TRUE)); + getCellText->Text = PhGetStringRef(node->IndexString); + } + break; + case PH_STACK_TREE_COLUMN_SYMBOL: + getCellText->Text = PhGetStringRef(node->SymbolString); + break; + case PH_STACK_TREE_COLUMN_STACKADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, node->StackAddressString); + break; + case PH_STACK_TREE_COLUMN_FRAMEADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, node->FrameAddressString); + break; + case PH_STACK_TREE_COLUMN_PARAMETER1: + PhInitializeStringRefLongHint(&getCellText->Text, node->Parameter1String); + break; + case PH_STACK_TREE_COLUMN_PARAMETER2: + PhInitializeStringRefLongHint(&getCellText->Text, node->Parameter2String); + break; + case PH_STACK_TREE_COLUMN_PARAMETER3: + PhInitializeStringRefLongHint(&getCellText->Text, node->Parameter3String); + break; + case PH_STACK_TREE_COLUMN_PARAMETER4: + PhInitializeStringRefLongHint(&getCellText->Text, node->Parameter4String); + break; + case PH_STACK_TREE_COLUMN_CONTROLADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, node->PcAddressString); + break; + case PH_STACK_TREE_COLUMN_RETURNADDRESS: + PhInitializeStringRefLongHint(&getCellText->Text, node->ReturnAddressString); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + node = (PPH_STACK_TREE_ROOT_NODE)getNodeColor->Node; + + if (PhCsUseColorSystemThreadStack && (ULONG_PTR)node->StackFrame.PcAddress > PhSystemBasicInformation.MaximumUserModeAddress) + { + getNodeColor->BackColor = PhCsColorSystemThreadStack; + } + else if (PhCsUseColorUserThreadStack && (ULONG_PTR)node->StackFrame.PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress) + { + getNodeColor->BackColor = PhCsColorUserThreadStack; + } + + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + SendMessage( + context->WindowHandle, + WM_COMMAND, + WM_PH_SHOWSTACKMENU, + (LPARAM)contextMenuEvent + ); + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + node = (PPH_STACK_TREE_ROOT_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0) + return FALSE; + + if (!node->TooltipText) + { + PH_STRING_BUILDER stringBuilder; + PPH_STRING fileName; + PH_SYMBOL_LINE_INFORMATION lineInfo; + + PhInitializeStringBuilder(&stringBuilder, 40); + + if (PhGetLineFromAddress( + context->SymbolProvider, + (ULONG64)node->StackFrame.PcAddress, + &fileName, + NULL, + &lineInfo + )) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"File: %s: line %lu\n", + fileName->Buffer, + lineInfo.LineNumber + ); + PhDereferenceObject(fileName); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + if (PhPluginsEnabled) + { + PH_PLUGIN_THREAD_STACK_CONTROL control; + + control.Type = PluginThreadStackGetTooltip; + control.UniqueKey = context; + control.u.GetTooltip.StackFrame = &node->StackFrame; + control.u.GetTooltip.StringBuilder = &stringBuilder; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + } + + node->TooltipText = PhFinalStringBuilderString(&stringBuilder); + } + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->Font = NULL; // use default font + getCellTooltip->MaximumWidth = 550; + } + else + { + return FALSE; + } + } + return TRUE; + } + + return FALSE; +} + +VOID ClearThreadStackTree( + _In_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DestroyThreadStackNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); +} + +PPH_STACK_TREE_ROOT_NODE GetSelectedThreadStackNode( + _In_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + PPH_STACK_TREE_ROOT_NODE windowNode = NULL; + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + windowNode = Context->NodeList->Items[i]; + + if (windowNode->Node.Selected) + return windowNode; + } + + return NULL; +} + +VOID GetSelectedThreadStackNodes( + _In_ PPH_THREAD_STACK_CONTEXT Context, + _Out_ PPH_STACK_TREE_ROOT_NODE **ThreadStackNodes, + _Out_ PULONG NumberOfThreadStackNodes + ) +{ + PPH_LIST list; + + list = PhCreateList(2); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PPH_STACK_TREE_ROOT_NODE node = (PPH_STACK_TREE_ROOT_NODE)Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *ThreadStackNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfThreadStackNodes = list->Count; + + PhDereferenceObject(list); +} + +VOID InitializeThreadStackTree( + _Inout_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + Context->NodeList = PhCreateList(100); + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPH_STACK_TREE_ROOT_NODE), + ThreadStackNodeHashtableEqualFunction, + ThreadStackNodeHashtableHashFunction, + 100 + ); + + PhSetControlTheme(Context->TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(Context->TreeNewHandle, ThreadStackTreeNewCallback, Context); + + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_INDEX, TRUE, L"#", 30, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_SYMBOL, TRUE, L"Name", 250, PH_ALIGN_LEFT, 1, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_STACKADDRESS, FALSE, L"Stack address", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_FRAMEADDRESS, FALSE, L"Frame address", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER1, FALSE, L"Stack parameter #1", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER2, FALSE, L"Stack parameter #2", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER3, FALSE, L"Stack parameter #3", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_PARAMETER4, FALSE, L"Stack parameter #4", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_CONTROLADDRESS, FALSE, L"Control address", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + PhAddTreeNewColumn(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_RETURNADDRESS, FALSE, L"Return address", 100, PH_ALIGN_LEFT, ULONG_MAX, 0); + + TreeNew_SetTriState(Context->TreeNewHandle, FALSE); + TreeNew_SetSort(Context->TreeNewHandle, PH_STACK_TREE_COLUMN_INDEX, AscendingSortOrder); + + ThreadStackLoadSettingsTreeList(Context); +} + +VOID DeleteThreadStackTree( + _In_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + ThreadStackSaveSettingsTreeList(Context); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + DestroyThreadStackNode(Context->NodeList->Items[i]); + } + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +VOID NTAPI PhpThreadStackContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_THREAD_STACK_CONTEXT context = (PPH_THREAD_STACK_CONTEXT)Object; + + if (context->StatusMessage) PhDereferenceObject(context->StatusMessage); + if (context->StatusContent) PhDereferenceObject(context->StatusContent); + if (context->NewList) PhDereferenceObject(context->NewList); + if (context->List) PhDereferenceObject(context->List); + + if (context->ThreadHandle) + NtClose(context->ThreadHandle); +} + +VOID PhShowThreadStackDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId, + _In_ PPH_THREAD_PROVIDER ThreadProvider + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + NTSTATUS status; + HANDLE threadHandle; + PPH_THREAD_STACK_CONTEXT context; + + // If the user is trying to view a system thread stack + // but KProcessHacker is not loaded, show an error message. + if (ProcessId == SYSTEM_PROCESS_ID && !KphIsConnected()) + { + PhShowError2(ParentWindowHandle, PH_KPH_ERROR_TITLE, PH_KPH_ERROR_MESSAGE); + return; + } + + if (!NT_SUCCESS(status = PhOpenThread( + &threadHandle, + THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, + ThreadId + ))) + { + if (KphIsConnected()) + { + status = PhOpenThread( + &threadHandle, + THREAD_QUERY_LIMITED_INFORMATION, + ThreadId + ); + } + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus(ParentWindowHandle, L"Unable to open the thread.", status, 0); + return; + } + + if (PhBeginInitOnce(&initOnce)) + { + PhThreadStackContextType = PhCreateObjectType(L"ThreadStackContext", 0, PhpThreadStackContextDeleteProcedure); + PhEndInitOnce(&initOnce); + } + + context = PhCreateObject(sizeof(PH_THREAD_STACK_CONTEXT), PhThreadStackContextType); + memset(context, 0, sizeof(PH_THREAD_STACK_CONTEXT)); + + context->List = PhCreateList(10); + context->NewList = PhCreateList(10); + PhInitializeQueuedLock(&context->StatusLock); + + context->ThreadHandle = threadHandle; + context->ProcessId = ProcessId; + context->ThreadId = ThreadId; + context->ThreadProvider = ThreadProvider; + context->SymbolProvider = ThreadProvider->SymbolProvider; + + DialogBoxParam( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_THRDSTACK), + ParentWindowHandle, + PhpThreadStackDlgProc, + (LPARAM)context + ); +} + +INT_PTR CALLBACK PhpThreadStackDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_THREAD_STACK_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PPH_THREAD_STACK_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + + context->WindowHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_TREELIST); + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + PhSetWindowText(hwndDlg, PhaFormatString(L"Stack - thread %lu", HandleToUlong(context->ThreadId))->Buffer); + + InitializeThreadStackTree(context); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 190; + rect.bottom = 120; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + + PhLoadWindowPlacementFromSetting(NULL, L"ThreadStackWindowSize", hwndDlg); + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + + if (PhPluginsEnabled) + { + PH_PLUGIN_THREAD_STACK_CONTROL control; + + control.Type = PluginThreadStackInitializing; + control.UniqueKey = context; + control.u.Initializing.ProcessId = context->ProcessId; + control.u.Initializing.ThreadId = context->ThreadId; + control.u.Initializing.ThreadHandle = context->ThreadHandle; + control.u.Initializing.SymbolProvider = context->SymbolProvider; + control.u.Initializing.CustomWalk = FALSE; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + + context->CustomWalk = control.u.Initializing.CustomWalk; + } + + status = PhpRefreshThreadStack(hwndDlg, context); + + if (status == STATUS_ABANDONED) + EndDialog(hwndDlg, IDCANCEL); + else if (!NT_SUCCESS(status)) + { + // HACK: Show error dialog on the parent window. + PhShowStatus(GetParent(hwndDlg), L"Unable to load the stack.", status, 0); + EndDialog(hwndDlg, IDCANCEL); + } + } + break; + case WM_DESTROY: + { + context->StopWalk = TRUE; + + // HACK HACK HACK + // PhWalkThreadStack will suspend the thread before walking the stack and download symbols but + // we don't wait for the stack walk / symbol downloading to complete when the user closes the progress dialog + // and so the thread can remain suspended for a long time (in some cases)... For now just resume the thread + // when closing the dialog since those results will be discarded and PhWalkThreadStack will cleanup whenever it completes. (dmex) + NtResumeThread(context->ThreadHandle, NULL); + + DeleteThreadStackTree(context); + + PhDeleteLayoutManager(&context->LayoutManager); + + if (PhPluginsEnabled) + { + PH_PLUGIN_THREAD_STACK_CONTROL control; + control.Type = PluginThreadStackUninitializing; + control.UniqueKey = context; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + } + + for (ULONG i = 0; i < context->List->Count; i++) + PhpFreeThreadStackItem(context->List->Items[i]); + + PhSaveWindowPlacementToSetting(NULL, L"ThreadStackWindowSize", hwndDlg); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhDereferenceObject(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_REFRESH: + { + NTSTATUS status; + + if (!NT_SUCCESS(status = PhpRefreshThreadStack(hwndDlg, context))) + { + PhShowStatus(hwndDlg, L"Unable to refresh the stack.", status, 0); + } + } + break; + case IDC_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; + case WM_PH_SHOWSTACKMENU: + { + PPH_EMENU menu; + PPH_STACK_TREE_ROOT_NODE selectedNode; + PPH_EMENU_ITEM selectedItem; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + + if (selectedNode = GetSelectedThreadStackNode(context)) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, IDC_COPY, context->TreeNewHandle, contextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + + PhDestroyEMenu(menu); + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + } + + return FALSE; +} + +VOID PhpFreeThreadStackItem( + _In_ PTHREAD_STACK_ITEM StackItem + ) +{ + if (StackItem->Symbol) PhDereferenceObject(StackItem->Symbol); + PhFree(StackItem); +} + +BOOLEAN NTAPI PhpWalkThreadStackCallback( + _In_ PPH_THREAD_STACK_FRAME StackFrame, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_STACK_CONTEXT threadStackContext = (PPH_THREAD_STACK_CONTEXT)Context; + PPH_STRING symbol; + PTHREAD_STACK_ITEM item; + + if (threadStackContext->StopWalk) + return FALSE; + + PhAcquireQueuedLockExclusive(&threadStackContext->StatusLock); + PhMoveReference(&threadStackContext->StatusMessage, PhFormatString(L"Processing stack frame %u...", threadStackContext->NewList->Count)); + PhReleaseQueuedLockExclusive(&threadStackContext->StatusLock); + + symbol = PhGetSymbolFromAddress( + threadStackContext->SymbolProvider, + (ULONG64)StackFrame->PcAddress, + NULL, + NULL, + NULL, + NULL + ); + + if (symbol && + (StackFrame->Flags & PH_THREAD_STACK_FRAME_I386) && + !(StackFrame->Flags & PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT)) + { + PhMoveReference(&symbol, PhConcatStrings2(symbol->Buffer, L" (No unwind info)")); + } + + item = PhAllocateZero(sizeof(THREAD_STACK_ITEM)); + item->StackFrame = *StackFrame; + item->Index = threadStackContext->NewList->Count; + + if (PhPluginsEnabled) + { + PH_PLUGIN_THREAD_STACK_CONTROL control; + + control.Type = PluginThreadStackResolveSymbol; + control.UniqueKey = threadStackContext; + control.u.ResolveSymbol.StackFrame = StackFrame; + control.u.ResolveSymbol.Symbol = symbol; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + + symbol = control.u.ResolveSymbol.Symbol; + } + + item->Symbol = symbol; + PhAddItemList(threadStackContext->NewList, item); + + return TRUE; +} + +NTSTATUS PhpRefreshThreadStackThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + NTSTATUS status; + PPH_THREAD_STACK_CONTEXT threadStackContext = Parameter; + CLIENT_ID clientId; + BOOLEAN defaultWalk; + + PhInitializeAutoPool(&autoPool); + + PhLoadSymbolsThreadProvider(threadStackContext->ThreadProvider); + + clientId.UniqueProcess = threadStackContext->ProcessId; + clientId.UniqueThread = threadStackContext->ThreadId; + defaultWalk = TRUE; + + if (threadStackContext->CustomWalk) + { + PH_PLUGIN_THREAD_STACK_CONTROL control; + + control.Type = PluginThreadStackWalkStack; + control.UniqueKey = threadStackContext; + control.u.WalkStack.Status = STATUS_UNSUCCESSFUL; + control.u.WalkStack.ThreadHandle = threadStackContext->ThreadHandle; + control.u.WalkStack.ProcessHandle = threadStackContext->SymbolProvider->ProcessHandle; + control.u.WalkStack.ClientId = &clientId; + control.u.WalkStack.Flags = PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK; + control.u.WalkStack.Callback = PhpWalkThreadStackCallback; + control.u.WalkStack.CallbackContext = threadStackContext; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + status = control.u.WalkStack.Status; + + if (NT_SUCCESS(status)) + defaultWalk = FALSE; + } + + if (defaultWalk) + { + PH_PLUGIN_THREAD_STACK_CONTROL control; + + control.UniqueKey = threadStackContext; + + if (PhPluginsEnabled) + { + control.Type = PluginThreadStackBeginDefaultWalkStack; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + } + + status = PhWalkThreadStack( + threadStackContext->ThreadHandle, + threadStackContext->SymbolProvider->ProcessHandle, + &clientId, + threadStackContext->SymbolProvider, + PH_WALK_I386_STACK | PH_WALK_AMD64_STACK | PH_WALK_KERNEL_STACK, + PhpWalkThreadStackCallback, + threadStackContext + ); + + if (PhPluginsEnabled) + { + control.Type = PluginThreadStackEndDefaultWalkStack; + PhInvokeCallback(PhGetGeneralCallback(GeneralCallbackThreadStackControl), &control); + } + } + + if (threadStackContext->NewList->Count != 0) + status = STATUS_SUCCESS; + + threadStackContext->WalkStatus = status; + PostMessage(threadStackContext->TaskDialogHandle, WM_PH_COMPLETED, 0, 0); + + PhDeleteAutoPool(&autoPool); + PhDereferenceObject(threadStackContext); + + return STATUS_SUCCESS; +} + +LRESULT CALLBACK PhpThreadStackTaskDialogSubclassProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_THREAD_STACK_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hwndDlg, 0xF))) + return 0; + + oldWndProc = context->ThreadStackStatusDefaultWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwndDlg, 0xF); + } + break; + case WM_PH_COMPLETED: + { + context->EnableCloseDialog = TRUE; + SendMessage(hwndDlg, TDM_CLICK_BUTTON, IDOK, 0); + } + break; + } + + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); +} + +VOID PhpSymbolProviderEventCallbackHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_SYMBOL_EVENT_DATA event = Parameter; + PPH_THREAD_STACK_CONTEXT context = Context; + PPH_STRING statusMessage = NULL; + + switch (event->ActionCode) + { + case CBA_DEFERRED_SYMBOL_LOAD_START: + case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: + case CBA_DEFERRED_SYMBOL_LOAD_FAILURE: + case CBA_SYMBOLS_UNLOADED: + { + PIMAGEHLP_DEFERRED_SYMBOL_LOADW64 callbackData = (PIMAGEHLP_DEFERRED_SYMBOL_LOADW64)event->EventData; + PPH_STRING fileName = NULL; + + if (callbackData->FileName[0] != UNICODE_NULL) + { + fileName = PhCreateString(callbackData->FileName); + PhMoveReference(&fileName, PhGetBaseName(fileName)); + } + + switch (event->ActionCode) + { + case CBA_DEFERRED_SYMBOL_LOAD_START: + statusMessage = PhFormatString(L"Loading symbols from %s...", PhGetStringOrEmpty(fileName)); + break; + case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: + statusMessage = PhFormatString(L"Loaded symbols from %s...", PhGetStringOrEmpty(fileName)); + break; + case CBA_DEFERRED_SYMBOL_LOAD_FAILURE: + statusMessage = PhFormatString(L"Failed to load %s...", PhGetStringOrEmpty(fileName)); + break; + case CBA_SYMBOLS_UNLOADED: + statusMessage = PhFormatString(L"Unloading %s...", PhGetStringOrEmpty(fileName)); + break; + } + + if (fileName) + PhDereferenceObject(fileName); + } + break; + case CBA_READ_MEMORY: + { + PIMAGEHLP_CBA_READ_MEMORY callbackEvent = (PIMAGEHLP_CBA_READ_MEMORY)event->EventData; + //statusMessage = PhFormatString(L"Reading %lu bytes of memory from %I64u...", callbackEvent->bytes, callbackEvent->addr); + } + break; + case CBA_EVENT: + { + PIMAGEHLP_CBA_EVENTW callbackEvent = (PIMAGEHLP_CBA_EVENTW)event->EventData; + //statusMessage = PhFormatString(L"%s", callbackEvent->desc); + } + break; + case CBA_DEBUG_INFO: + { + //statusMessage = PhFormatString(L"%s", event->EventData); + } + break; + case CBA_ENGINE_PRESENT: + case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL: + case CBA_DEFERRED_SYMBOL_LOAD_CANCEL: + default: + { + //statusMessage = PhFormatString(L"Unknown: %lu", event->ActionCode); + } + break; + } + + if (statusMessage) + { + //dprintf("%S\r\n", statusMessage->Buffer); + PhAcquireQueuedLockExclusive(&context->StatusLock); + PhMoveReference(&context->StatusContent, statusMessage); + PhReleaseQueuedLockExclusive(&context->StatusLock); + } +} + +HRESULT CALLBACK PhpThreadStackTaskDialogCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PPH_THREAD_STACK_CONTEXT context = (PPH_THREAD_STACK_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_CREATED: + { + context->TaskDialogHandle = hwndDlg; + + PhRegisterCallback( + &PhSymbolEventCallback, + PhpSymbolProviderEventCallbackHandler, + context, + &context->SymbolProviderEventRegistration + ); + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + SendMessage(hwndDlg, TDM_UPDATE_ICON, TDIE_ICON_MAIN, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); + + SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + + context->ThreadStackStatusDefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, 0xF, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PhpThreadStackTaskDialogSubclassProc); + + PhReferenceObject(context); + PhCreateThread2(PhpRefreshThreadStackThreadStart, context); + } + break; + case TDN_DESTROYED: + { + PhUnregisterCallback(&PhSymbolEventCallback, &context->SymbolProviderEventRegistration); + } + break; + case TDN_BUTTON_CLICKED: + { + if ((INT)wParam == IDCANCEL) + { + context->StopWalk = TRUE; + //context->SymbolProvider->Terminating = TRUE; // HACK: Cancel symbol load/download. + } + + //if (!context->EnableCloseDialog) + // return S_FALSE; + } + break; + case TDN_TIMER: + { + PPH_STRING message; + PPH_STRING content; + + PhAcquireQueuedLockExclusive(&context->StatusLock); + + message = context->StatusMessage; + content = context->StatusContent; + + if (message) PhReferenceObject(message); + if (content) PhReferenceObject(content); + + PhReleaseQueuedLockExclusive(&context->StatusLock); + + SendMessage( + context->TaskDialogHandle, + TDM_SET_ELEMENT_TEXT, + TDE_MAIN_INSTRUCTION, + (LPARAM)PhGetStringOrDefault(message, L" ") + ); + + SendMessage( + context->TaskDialogHandle, + TDM_SET_ELEMENT_TEXT, + TDE_CONTENT, + (LPARAM)PhGetStringOrDefault(content, L" ") + ); + + if (message) PhDereferenceObject(message); + if (content) PhDereferenceObject(content); + } + break; + } + + return S_OK; +} + +BOOLEAN PhpShowThreadStackWindow( + _In_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + INT result; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = + TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | + TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SHOW_MARQUEE_PROGRESS_BAR | + TDF_CALLBACK_TIMER; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.pfCallback = PhpThreadStackTaskDialogCallback; + config.lpCallbackData = (LONG_PTR)Context; + config.hwndParent = Context->WindowHandle; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Processing stack frames..."; + config.pszContent = L" "; + config.cxWidth = 200; + + return SUCCEEDED(TaskDialogIndirect(&config, &result, NULL, NULL)) && result != IDCANCEL; +} + +static NTSTATUS PhpRefreshThreadStack( + _In_ HWND hwnd, + _In_ PPH_THREAD_STACK_CONTEXT Context + ) +{ + ULONG i; + + Context->StopWalk = FALSE; + PhMoveReference(&Context->StatusMessage, PhCreateString(L"Processing stack frames...")); + + if (!PhpShowThreadStackWindow(Context)) + { + return STATUS_ABANDONED; + } + + if (!Context->StopWalk && NT_SUCCESS(Context->WalkStatus)) + { + for (i = 0; i < Context->List->Count; i++) + PhpFreeThreadStackItem(Context->List->Items[i]); + + PhDereferenceObject(Context->List); + Context->List = Context->NewList; + Context->NewList = PhCreateList(10); + + ClearThreadStackTree(Context); + + for (i = 0; i < Context->List->Count; i++) + { + PTHREAD_STACK_ITEM item = Context->List->Items[i]; + PPH_STACK_TREE_ROOT_NODE stackNode; + + stackNode = AddThreadStackNode(Context, item->Index); + stackNode->StackFrame = item->StackFrame; + + if (!PhIsNullOrEmptyString(item->Symbol)) + stackNode->SymbolString = PhReferenceObject(item->Symbol); + + if (item->StackFrame.StackAddress) + PhPrintPointer(stackNode->StackAddressString, item->StackFrame.StackAddress); + if (item->StackFrame.FrameAddress) + PhPrintPointer(stackNode->FrameAddressString, item->StackFrame.FrameAddress); + + // There are no params for kernel-mode stack traces. + if ((ULONG_PTR)item->StackFrame.PcAddress <= PhSystemBasicInformation.MaximumUserModeAddress) + { + PhPrintPointer(stackNode->Parameter1String, item->StackFrame.Params[0]); + PhPrintPointer(stackNode->Parameter2String, item->StackFrame.Params[1]); + PhPrintPointer(stackNode->Parameter3String, item->StackFrame.Params[2]); + PhPrintPointer(stackNode->Parameter4String, item->StackFrame.Params[3]); + } + + if (item->StackFrame.PcAddress) + PhPrintPointer(stackNode->PcAddressString, item->StackFrame.PcAddress); + if (item->StackFrame.ReturnAddress) + PhPrintPointer(stackNode->ReturnAddressString, item->StackFrame.ReturnAddress); + + UpdateThreadStackNode(Context, stackNode); + } + + TreeNew_NodesStructured(Context->TreeNewHandle); + } + else + { + for (i = 0; i < Context->NewList->Count; i++) + PhpFreeThreadStackItem(Context->NewList->Items[i]); + + PhClearList(Context->NewList); + } + + if (Context->StopWalk) + return STATUS_ABANDONED; + + return Context->WalkStatus; +} diff --git a/ProcessHacker/tokprp.c b/ProcessHacker/tokprp.c index c973169123ce..be55c1412f83 100644 --- a/ProcessHacker/tokprp.c +++ b/ProcessHacker/tokprp.c @@ -1,1935 +1,3674 @@ -/* - * Process Hacker - - * token properties - * - * Copyright (C) 2010-2012 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include -#include -#include - -typedef struct _ATTRIBUTE_NODE -{ - PH_TREENEW_NODE Node; - PPH_LIST Children; - PPH_STRING Text; -} ATTRIBUTE_NODE, *PATTRIBUTE_NODE; - -typedef struct _ATTRIBUTE_TREE_CONTEXT -{ - PPH_LIST RootList; - PPH_LIST NodeList; -} ATTRIBUTE_TREE_CONTEXT, *PATTRIBUTE_TREE_CONTEXT; - -typedef struct _TOKEN_PAGE_CONTEXT -{ - PPH_OPEN_OBJECT OpenObject; - PVOID Context; - DLGPROC HookProc; - - HWND GroupsListViewHandle; - HWND PrivilegesListViewHandle; - - PTOKEN_GROUPS Groups; - PTOKEN_PRIVILEGES Privileges; - PTOKEN_GROUPS Capabilities; - - ATTRIBUTE_TREE_CONTEXT ClaimsTreeContext; - ATTRIBUTE_TREE_CONTEXT AuthzTreeContext; -} TOKEN_PAGE_CONTEXT, *PTOKEN_PAGE_CONTEXT; - -INT CALLBACK PhpTokenPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ); - -INT_PTR CALLBACK PhpTokenPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhpShowTokenAdvancedProperties( - _In_ HWND ParentWindowHandle, - _In_ PTOKEN_PAGE_CONTEXT Context - ); - -INT_PTR CALLBACK PhpTokenGeneralPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpTokenAdvancedPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpTokenCapabilitiesPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -BOOLEAN NTAPI PhpAttributeTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -INT_PTR CALLBACK PhpTokenClaimsPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PhpTokenAttributesPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhShowTokenProperties( - _In_ HWND ParentWindowHandle, - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ PWSTR Title - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - HPROPSHEETPAGE pages[1]; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = Title ? Title : L"Token"; - propSheetHeader.nPages = 1; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - pages[0] = PhCreateTokenPage(OpenObject, Context, NULL); - - PhModalPropertySheet(&propSheetHeader); -} - -HPROPSHEETPAGE PhCreateTokenPage( - _In_ PPH_OPEN_OBJECT OpenObject, - _In_opt_ PVOID Context, - _In_opt_ DLGPROC HookProc - ) -{ - HPROPSHEETPAGE propSheetPageHandle; - PROPSHEETPAGE propSheetPage; - PTOKEN_PAGE_CONTEXT tokenPageContext; - - tokenPageContext = PhCreateAlloc(sizeof(TOKEN_PAGE_CONTEXT)); - memset(tokenPageContext, 0, sizeof(TOKEN_PAGE_CONTEXT)); - tokenPageContext->OpenObject = OpenObject; - tokenPageContext->Context = Context; - tokenPageContext->HookProc = HookProc; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USECALLBACK; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OBJTOKEN); - propSheetPage.pfnDlgProc = PhpTokenPageProc; - propSheetPage.lParam = (LPARAM)tokenPageContext; - propSheetPage.pfnCallback = PhpTokenPropPageProc; - - propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); - // CreatePropertySheetPage would have sent PSPCB_ADDREF (below), - // which would have added a reference. - PhDereferenceObject(tokenPageContext); - - return propSheetPageHandle; -} - -INT CALLBACK PhpTokenPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - - tokenPageContext = (PTOKEN_PAGE_CONTEXT)ppsp->lParam; - - if (uMsg == PSPCB_ADDREF) - { - PhReferenceObject(tokenPageContext); - } - else if (uMsg == PSPCB_RELEASE) - { - PhDereferenceObject(tokenPageContext); - } - - return 1; -} - -PPH_STRING PhGetGroupAttributesString( - _In_ ULONG Attributes - ) -{ - PWSTR baseString; - PPH_STRING string; - - if (Attributes & SE_GROUP_INTEGRITY) - { - if (Attributes & SE_GROUP_INTEGRITY_ENABLED) - return PhCreateString(L"Integrity"); - else - return PhCreateString(L"Integrity (disabled)"); - } - - if (Attributes & SE_GROUP_LOGON_ID) - baseString = L"Logon ID"; - else if (Attributes & SE_GROUP_MANDATORY) - baseString = L"Mandatory"; - else if (Attributes & SE_GROUP_OWNER) - baseString = L"Owner"; - else if (Attributes & SE_GROUP_RESOURCE) - baseString = L"Resource"; - else if (Attributes & SE_GROUP_USE_FOR_DENY_ONLY) - baseString = L"Use for deny only"; - else - baseString = NULL; - - if (!baseString) - { - if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) - return PhCreateString(L"Default enabled"); - else if (Attributes & SE_GROUP_ENABLED) - return PhReferenceEmptyString(); - else - return PhCreateString(L"Disabled"); - } - - if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) - string = PhConcatStrings2(baseString, L" (default enabled)"); - else if (Attributes & SE_GROUP_ENABLED) - string = PhCreateString(baseString); - else - string = PhConcatStrings2(baseString, L" (disabled)"); - - return string; -} - -COLORREF PhGetGroupAttributesColor( - _In_ ULONG Attributes - ) -{ - if (Attributes & SE_GROUP_INTEGRITY) - { - if (Attributes & SE_GROUP_INTEGRITY_ENABLED) - return RGB(0xe0, 0xf0, 0xe0); - else - return GetSysColor(COLOR_WINDOW); - } - - if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) - return RGB(0xe0, 0xf0, 0xe0); - else if (Attributes & SE_GROUP_ENABLED) - return GetSysColor(COLOR_WINDOW); - else - return RGB(0xf0, 0xe0, 0xe0); -} - -static COLORREF NTAPI PhpTokenGroupColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ) -{ - PSID_AND_ATTRIBUTES sidAndAttributes = Param; - - return PhGetGroupAttributesColor(sidAndAttributes->Attributes); -} - -PWSTR PhGetPrivilegeAttributesString( - _In_ ULONG Attributes - ) -{ - if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) - return L"Default Enabled"; - else if (Attributes & SE_PRIVILEGE_ENABLED) - return L"Enabled"; - else - return L"Disabled"; -} - -COLORREF PhGetPrivilegeAttributesColor( - _In_ ULONG Attributes - ) -{ - if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) - return RGB(0xc0, 0xf0, 0xc0); - else if (Attributes & SE_PRIVILEGE_ENABLED) - return RGB(0xe0, 0xf0, 0xe0); - else - return RGB(0xf0, 0xe0, 0xe0); -} - -static COLORREF NTAPI PhpTokenPrivilegeColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ) -{ - PLUID_AND_ATTRIBUTES luidAndAttributes = Param; - - return PhGetPrivilegeAttributesColor(luidAndAttributes->Attributes); -} - -PWSTR PhGetElevationTypeString( - _In_ TOKEN_ELEVATION_TYPE ElevationType - ) -{ - switch (ElevationType) - { - case TokenElevationTypeFull: - return L"Yes"; - case TokenElevationTypeLimited: - return L"No"; - default: - return L"N/A"; - } -} - -BOOLEAN PhpUpdateTokenGroups( - _In_ HWND hwndDlg, - _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, - _In_ HWND GroupsLv, - _In_ HANDLE TokenHandle - ) -{ - PTOKEN_GROUPS groups; - ULONG i; - - if (!NT_SUCCESS(PhGetTokenGroups(TokenHandle, &groups))) - return FALSE; - - ExtendedListView_SetRedraw(GroupsLv, FALSE); - - ListView_DeleteAllItems(GroupsLv); - - for (i = 0; i < groups->GroupCount; i++) - { - INT lvItemIndex; - PPH_STRING fullName; - PPH_STRING attributesString; - - if (!(fullName = PhGetSidFullName(groups->Groups[i].Sid, TRUE, NULL))) - fullName = PhSidToStringSid(groups->Groups[i].Sid); - - if (fullName) - { - lvItemIndex = PhAddListViewItem(GroupsLv, MAXINT, fullName->Buffer, &groups->Groups[i]); - attributesString = PhGetGroupAttributesString(groups->Groups[i].Attributes); - PhSetListViewSubItem(GroupsLv, lvItemIndex, 1, attributesString->Buffer); - - PhDereferenceObject(attributesString); - PhDereferenceObject(fullName); - } - } - - ExtendedListView_SortItems(GroupsLv); - - ExtendedListView_SetRedraw(GroupsLv, TRUE); - - if (TokenPageContext->Groups) - PhFree(TokenPageContext->Groups); - - TokenPageContext->Groups = groups; - - return TRUE; -} - -FORCEINLINE PTOKEN_PAGE_CONTEXT PhpTokenPageHeader( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return (PTOKEN_PAGE_CONTEXT)PhpGenericPropertyPageHeader( - hwndDlg, uMsg, wParam, lParam, L"TokenPageContext"); -} - -INT_PTR CALLBACK PhpTokenPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - - tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!tokenPageContext) - return FALSE; - - if (tokenPageContext->HookProc) - { - if (tokenPageContext->HookProc(hwndDlg, uMsg, wParam, lParam)) - return TRUE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND groupsLv; - HWND privilegesLv; - HANDLE tokenHandle; - - tokenPageContext->GroupsListViewHandle = groupsLv = GetDlgItem(hwndDlg, IDC_GROUPS); - tokenPageContext->PrivilegesListViewHandle = privilegesLv = GetDlgItem(hwndDlg, IDC_PRIVILEGES); - PhSetListViewStyle(groupsLv, FALSE, TRUE); - PhSetListViewStyle(privilegesLv, FALSE, TRUE); - PhSetControlTheme(groupsLv, L"explorer"); - PhSetControlTheme(privilegesLv, L"explorer"); - - PhAddListViewColumn(groupsLv, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); - PhAddListViewColumn(groupsLv, 1, 1, 1, LVCFMT_LEFT, 200, L"Flags"); - - PhAddListViewColumn(privilegesLv, 0, 0, 0, LVCFMT_LEFT, 100, L"Name"); - PhAddListViewColumn(privilegesLv, 1, 1, 1, LVCFMT_LEFT, 100, L"Status"); - PhAddListViewColumn(privilegesLv, 2, 2, 2, LVCFMT_LEFT, 170, L"Description"); - - PhSetExtendedListView(groupsLv); - ExtendedListView_SetItemColorFunction(groupsLv, PhpTokenGroupColorFunction); - - PhSetExtendedListView(privilegesLv); - ExtendedListView_SetItemColorFunction(privilegesLv, PhpTokenPrivilegeColorFunction); - - SetDlgItemText(hwndDlg, IDC_USER, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_USERSID, L"Unknown"); - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"Unknown"); - - if (!WINDOWS_HAS_UAC) - ShowWindow(GetDlgItem(hwndDlg, IDC_INTEGRITY), SW_HIDE); - - if (NT_SUCCESS(tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - tokenPageContext->Context - ))) - { - PTOKEN_USER tokenUser; - PPH_STRING fullUserName; - PPH_STRING stringUserSid; - ULONG sessionId; - TOKEN_ELEVATION_TYPE elevationType; - BOOLEAN isVirtualizationAllowed; - BOOLEAN isVirtualizationEnabled; - PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; - PPH_STRING appContainerSid; - ULONG i; - - if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) - { - if (fullUserName = PhGetSidFullName(tokenUser->User.Sid, TRUE, NULL)) - { - SetDlgItemText(hwndDlg, IDC_USER, fullUserName->Buffer); - PhDereferenceObject(fullUserName); - } - - if (stringUserSid = PhSidToStringSid(tokenUser->User.Sid)) - { - SetDlgItemText(hwndDlg, IDC_USERSID, stringUserSid->Buffer); - PhDereferenceObject(stringUserSid); - } - - PhFree(tokenUser); - } - - if (NT_SUCCESS(PhGetTokenSessionId(tokenHandle, &sessionId))) - SetDlgItemInt(hwndDlg, IDC_SESSIONID, sessionId, FALSE); - - if (WINDOWS_HAS_UAC) - { - if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) - SetDlgItemText(hwndDlg, IDC_ELEVATED, PhGetElevationTypeString(elevationType)); - - if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) - { - if (isVirtualizationAllowed) - { - if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) - { - SetDlgItemText( - hwndDlg, - IDC_VIRTUALIZED, - isVirtualizationEnabled ? L"Yes" : L"No" - ); - } - } - else - { - SetDlgItemText(hwndDlg, IDC_VIRTUALIZED, L"Not allowed"); - } - } - } - else - { - SetDlgItemText(hwndDlg, IDC_ELEVATED, L"N/A"); - SetDlgItemText(hwndDlg, IDC_VIRTUALIZED, L"N/A"); - } - - if (WINDOWS_HAS_IMMERSIVE) - { - appContainerSid = NULL; - - if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) - { - if (appContainerInfo->TokenAppContainer) - appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); - - PhFree(appContainerInfo); - } - - if (appContainerSid) - { - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, appContainerSid->Buffer); - PhDereferenceObject(appContainerSid); - } - else - { - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"N/A"); - } - } - else - { - SetDlgItemText(hwndDlg, IDC_APPCONTAINERSID, L"N/A"); - } - - // Groups - PhpUpdateTokenGroups(hwndDlg, tokenPageContext, groupsLv, tokenHandle); - - // Privileges - if (NT_SUCCESS(PhGetTokenPrivileges(tokenHandle, &tokenPageContext->Privileges))) - { - for (i = 0; i < tokenPageContext->Privileges->PrivilegeCount; i++) - { - INT lvItemIndex; - PPH_STRING privilegeName; - PPH_STRING privilegeDisplayName; - - if (PhLookupPrivilegeName( - &tokenPageContext->Privileges->Privileges[i].Luid, - &privilegeName - )) - { - privilegeDisplayName = NULL; - PhLookupPrivilegeDisplayName(&privilegeName->sr, &privilegeDisplayName); - - // Name - lvItemIndex = PhAddListViewItem(privilegesLv, MAXINT, privilegeName->Buffer, - &tokenPageContext->Privileges->Privileges[i]); - // Status - PhSetListViewSubItem(privilegesLv, lvItemIndex, 1, - PhGetPrivilegeAttributesString( - tokenPageContext->Privileges->Privileges[i].Attributes)); - // Description - PhSetListViewSubItem(privilegesLv, lvItemIndex, 2, - PhGetString(privilegeDisplayName)); - - if (privilegeDisplayName) PhDereferenceObject(privilegeDisplayName); - PhDereferenceObject(privilegeName); - } - } - - ExtendedListView_SortItems(privilegesLv); - } - - NtClose(tokenHandle); - } - } - break; - case WM_DESTROY: - { - if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); - if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_PRIVILEGE_ENABLE: - case ID_PRIVILEGE_DISABLE: - case ID_PRIVILEGE_REMOVE: - { - NTSTATUS status; - PLUID_AND_ATTRIBUTES *privileges; - ULONG numberOfPrivileges; - HANDLE tokenHandle; - ULONG i; - - if (LOWORD(wParam) == ID_PRIVILEGE_REMOVE) - { - if (!PhShowConfirmMessage( - hwndDlg, - L"remove", - L"the selected privilege(s)", - L"Removing privileges may reduce the functionality of the process, " - L"and is permanent for the lifetime of the process.", - FALSE - )) - break; - } - - PhGetSelectedListViewItemParams( - tokenPageContext->PrivilegesListViewHandle, - &privileges, - &numberOfPrivileges - ); - - status = tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_ADJUST_PRIVILEGES, - tokenPageContext->Context - ); - - if (NT_SUCCESS(status)) - { - ExtendedListView_SetRedraw(tokenPageContext->PrivilegesListViewHandle, FALSE); - - for (i = 0; i < numberOfPrivileges; i++) - { - PPH_STRING privilegeName = NULL; - ULONG newAttributes; - - PhLookupPrivilegeName(&privileges[i]->Luid, &privilegeName); - PH_AUTO(privilegeName); - - switch (LOWORD(wParam)) - { - case ID_PRIVILEGE_ENABLE: - newAttributes = SE_PRIVILEGE_ENABLED; - break; - case ID_PRIVILEGE_DISABLE: - newAttributes = 0; - break; - case ID_PRIVILEGE_REMOVE: - newAttributes = SE_PRIVILEGE_REMOVED; - break; - } - - // Privileges which are enabled by default cannot be - // modified except to remove them. - - if ( - privileges[i]->Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT && - LOWORD(wParam) != ID_PRIVILEGE_REMOVE - ) - { - if (LOWORD(wParam) == ID_PRIVILEGE_DISABLE) - { - if (!PhShowContinueStatus( - hwndDlg, - PhaConcatStrings2(L"Unable to disable ", privilegeName->Buffer)->Buffer, - STATUS_UNSUCCESSFUL, - 0 - )) - break; - } - - continue; - } - - if (PhSetTokenPrivilege( - tokenHandle, - NULL, - &privileges[i]->Luid, - newAttributes - )) - { - INT lvItemIndex = PhFindListViewItemByParam( - tokenPageContext->PrivilegesListViewHandle, - -1, - privileges[i] - ); - - if (LOWORD(wParam) != ID_PRIVILEGE_REMOVE) - { - // Refresh the status text (and background - // color). - privileges[i]->Attributes = newAttributes; - PhSetListViewSubItem( - tokenPageContext->PrivilegesListViewHandle, - lvItemIndex, - 1, - PhGetPrivilegeAttributesString(newAttributes) - ); - } - else - { - ListView_DeleteItem( - tokenPageContext->PrivilegesListViewHandle, - lvItemIndex - ); - } - } - else - { - PWSTR action = L"set"; - - switch (LOWORD(wParam)) - { - case ID_PRIVILEGE_ENABLE: - action = L"enable"; - break; - case ID_PRIVILEGE_DISABLE: - action = L"disable"; - break; - case ID_PRIVILEGE_REMOVE: - action = L"remove"; - break; - } - - if (!PhShowContinueStatus( - hwndDlg, - PhaFormatString(L"Unable to %s %s", action, privilegeName->Buffer)->Buffer, - STATUS_UNSUCCESSFUL, - 0 - )) - break; - } - } - - ExtendedListView_SetRedraw(tokenPageContext->PrivilegesListViewHandle, TRUE); - - NtClose(tokenHandle); - } - else - { - PhShowStatus(hwndDlg, L"Unable to open the token", status, 0); - } - - PhFree(privileges); - - ExtendedListView_SortItems(tokenPageContext->PrivilegesListViewHandle); - } - break; - case ID_PRIVILEGE_COPY: - { - PhCopyListView(tokenPageContext->PrivilegesListViewHandle); - } - break; - case IDC_INTEGRITY: - { - NTSTATUS status; - RECT rect; - PPH_EMENU menu; - HANDLE tokenHandle; - MANDATORY_LEVEL integrityLevel; - PPH_EMENU_ITEM selectedItem; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_INTEGRITY), &rect); - - menu = PhCreateEMenu(); - - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLevelSecureProcess, L"Protected", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLevelSystem, L"System", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLevelHigh, L"High", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLevelMedium, L"Medium", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLevelLow, L"Low", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLevelUntrusted, L"Untrusted", NULL, NULL), -1); - - integrityLevel = -1; - - // Put a radio check on the menu item that corresponds with the current integrity level. - // Also disable menu items which correspond to higher integrity levels since - // NtSetInformationToken doesn't allow integrity levels to be raised. - if (NT_SUCCESS(tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - tokenPageContext->Context - ))) - { - if (NT_SUCCESS(PhGetTokenIntegrityLevel( - tokenHandle, - &integrityLevel, - NULL - ))) - { - ULONG i; - - for (i = 0; i < menu->Items->Count; i++) - { - PPH_EMENU_ITEM menuItem = menu->Items->Items[i]; - - if (menuItem->Id == integrityLevel) - { - menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; - } - else if (menuItem->Id > (ULONG)integrityLevel) - { - menuItem->Flags |= PH_EMENU_DISABLED; - } - } - } - - NtClose(tokenHandle); - } - - selectedItem = PhShowEMenu( - menu, - hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - rect.left, - rect.bottom - ); - - if (selectedItem && selectedItem->Id != integrityLevel) - { - if (PhShowConfirmMessage( - hwndDlg, - L"set", - L"the integrity level", - L"Once lowered, the integrity level of the token cannot be raised again.", - FALSE - )) - { - if (NT_SUCCESS(status = tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, - tokenPageContext->Context - ))) - { - static SID_IDENTIFIER_AUTHORITY mandatoryLabelAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY; - - UCHAR newSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG)]; - PSID newSid; - TOKEN_MANDATORY_LABEL mandatoryLabel; - - newSid = (PSID)newSidBuffer; - RtlInitializeSid(newSid, &mandatoryLabelAuthority, 1); - *RtlSubAuthoritySid(newSid, 0) = MANDATORY_LEVEL_TO_MANDATORY_RID(selectedItem->Id); - mandatoryLabel.Label.Sid = newSid; - mandatoryLabel.Label.Attributes = SE_GROUP_INTEGRITY; - - status = NtSetInformationToken( - tokenHandle, - TokenIntegrityLevel, - &mandatoryLabel, - sizeof(TOKEN_MANDATORY_LABEL) - ); - - if (NT_SUCCESS(status)) - PhpUpdateTokenGroups(hwndDlg, tokenPageContext, GetDlgItem(hwndDlg, IDC_GROUPS), tokenHandle); - - NtClose(tokenHandle); - } - - if (!NT_SUCCESS(status)) - PhShowStatus(hwndDlg, L"Unable to set the integrity level", status, 0); - } - } - - PhDestroyEMenu(menu); - } - break; - case IDC_ADVANCED: - { - PhpShowTokenAdvancedProperties(hwndDlg, tokenPageContext); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_SESSIONID)); - return TRUE; - } - break; - } - - PhHandleListViewNotifyBehaviors(lParam, tokenPageContext->GroupsListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - PhHandleListViewNotifyBehaviors(lParam, tokenPageContext->PrivilegesListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - } - break; - case WM_CONTEXTMENU: - { - if ((HWND)wParam == tokenPageContext->PrivilegesListViewHandle) - { - POINT point; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); - - if (point.x == -1 && point.y == -1) - PhGetListViewContextMenuPoint((HWND)wParam, &point); - - if (ListView_GetSelectedCount(tokenPageContext->PrivilegesListViewHandle) != 0) - { - PPH_EMENU menu; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_PRIVILEGE), 0); - PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); - PhDestroyEMenu(menu); - } - } - } - break; - } - - REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->GroupsListViewHandle, uMsg, wParam, lParam); - REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->PrivilegesListViewHandle, uMsg, wParam, lParam); - - return FALSE; -} - -VOID PhpShowTokenAdvancedProperties( - _In_ HWND ParentWindowHandle, - _In_ PTOKEN_PAGE_CONTEXT Context - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - HPROPSHEETPAGE pages[6]; - PROPSHEETPAGE page; - ULONG numberOfPages; - PH_STD_OBJECT_SECURITY stdObjectSecurity; - PPH_ACCESS_ENTRY accessEntries; - ULONG numberOfAccessEntries; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = ParentWindowHandle; - propSheetHeader.pszCaption = L"Token"; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - numberOfPages = 0; - - // General - - memset(&page, 0, sizeof(PROPSHEETPAGE)); - page.dwSize = sizeof(PROPSHEETPAGE); - page.pszTemplate = MAKEINTRESOURCE(IDD_TOKGENERAL); - page.pfnDlgProc = PhpTokenGeneralPageProc; - page.lParam = (LPARAM)Context; - pages[numberOfPages++] = CreatePropertySheetPage(&page); - - // Advanced - - memset(&page, 0, sizeof(PROPSHEETPAGE)); - page.dwSize = sizeof(PROPSHEETPAGE); - page.pszTemplate = MAKEINTRESOURCE(IDD_TOKADVANCED); - page.pfnDlgProc = PhpTokenAdvancedPageProc; - page.lParam = (LPARAM)Context; - pages[numberOfPages++] = CreatePropertySheetPage(&page); - - if (WindowsVersion >= WINDOWS_8) - { - // Capabilities - - memset(&page, 0, sizeof(PROPSHEETPAGE)); - page.dwSize = sizeof(PROPSHEETPAGE); - page.pszTemplate = MAKEINTRESOURCE(IDD_TOKCAPABILITIES); - page.pfnDlgProc = PhpTokenCapabilitiesPageProc; - page.lParam = (LPARAM)Context; - pages[numberOfPages++] = CreatePropertySheetPage(&page); - - // Claims - - memset(&page, 0, sizeof(PROPSHEETPAGE)); - page.dwSize = sizeof(PROPSHEETPAGE); - page.dwFlags = PSP_USETITLE; - page.pszTemplate = MAKEINTRESOURCE(IDD_TOKATTRIBUTES); - page.pszTitle = L"Claims"; - page.pfnDlgProc = PhpTokenClaimsPageProc; - page.lParam = (LPARAM)Context; - pages[numberOfPages++] = CreatePropertySheetPage(&page); - - // (Token) Attributes - - memset(&page, 0, sizeof(PROPSHEETPAGE)); - page.dwSize = sizeof(PROPSHEETPAGE); - page.dwFlags = PSP_USETITLE; - page.pszTemplate = MAKEINTRESOURCE(IDD_TOKATTRIBUTES); - page.pszTitle = L"Attributes"; - page.pfnDlgProc = PhpTokenAttributesPageProc; - page.lParam = (LPARAM)Context; - pages[numberOfPages++] = CreatePropertySheetPage(&page); - } - - // Security - - stdObjectSecurity.OpenObject = Context->OpenObject; - stdObjectSecurity.ObjectType = L"Token"; - stdObjectSecurity.Context = Context->Context; - - if (PhGetAccessEntries(L"Token", &accessEntries, &numberOfAccessEntries)) - { - pages[numberOfPages++] = PhCreateSecurityPage( - L"Token", - PhStdGetObjectSecurity, - PhStdSetObjectSecurity, - &stdObjectSecurity, - accessEntries, - numberOfAccessEntries - ); - PhFree(accessEntries); - } - - propSheetHeader.nPages = numberOfPages; - PhModalPropertySheet(&propSheetHeader); -} - -static NTSTATUS PhpOpenLinkedToken( - _Out_ PHANDLE Handle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ PVOID Context - ) -{ - return PhGetTokenLinkedToken((HANDLE)Context, Handle); -} - -INT_PTR CALLBACK PhpTokenGeneralPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - - tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!tokenPageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HANDLE tokenHandle; - PPH_STRING tokenUserName = NULL; - PPH_STRING tokenUserSid = NULL; - PPH_STRING tokenOwnerName = NULL; - PPH_STRING tokenPrimaryGroupName = NULL; - ULONG tokenSessionId = -1; - PWSTR tokenElevated = L"N/A"; - BOOLEAN hasLinkedToken = FALSE; - PWSTR tokenVirtualization = L"N/A"; - WCHAR tokenSourceName[TOKEN_SOURCE_LENGTH + 1] = L"Unknown"; - WCHAR tokenSourceLuid[PH_PTR_STR_LEN_1] = L"Unknown"; - - // HACK - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - - if (NT_SUCCESS(tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - tokenPageContext->Context - ))) - { - PTOKEN_USER tokenUser; - PTOKEN_OWNER tokenOwner; - PTOKEN_PRIMARY_GROUP tokenPrimaryGroup; - TOKEN_ELEVATION_TYPE elevationType; - BOOLEAN isVirtualizationAllowed; - BOOLEAN isVirtualizationEnabled; - - if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) - { - tokenUserName = PH_AUTO(PhGetSidFullName(tokenUser->User.Sid, TRUE, NULL)); - tokenUserSid = PH_AUTO(PhSidToStringSid(tokenUser->User.Sid)); - - PhFree(tokenUser); - } - - if (NT_SUCCESS(PhGetTokenOwner(tokenHandle, &tokenOwner))) - { - tokenOwnerName = PH_AUTO(PhGetSidFullName(tokenOwner->Owner, TRUE, NULL)); - PhFree(tokenOwner); - } - - if (NT_SUCCESS(PhGetTokenPrimaryGroup(tokenHandle, &tokenPrimaryGroup))) - { - tokenPrimaryGroupName = PH_AUTO(PhGetSidFullName( - tokenPrimaryGroup->PrimaryGroup, TRUE, NULL)); - PhFree(tokenPrimaryGroup); - } - - PhGetTokenSessionId(tokenHandle, &tokenSessionId); - - if (WINDOWS_HAS_UAC) - { - if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) - { - tokenElevated = PhGetElevationTypeString(elevationType); - hasLinkedToken = elevationType != TokenElevationTypeDefault; - } - - if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) - { - if (isVirtualizationAllowed) - { - if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) - { - tokenVirtualization = isVirtualizationEnabled ? L"Enabled" : L"Disabled"; - } - } - else - { - tokenVirtualization = L"Not Allowed"; - } - } - } - - NtClose(tokenHandle); - } - - if (NT_SUCCESS(tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY_SOURCE, - tokenPageContext->Context - ))) - { - TOKEN_SOURCE tokenSource; - - if (NT_SUCCESS(PhGetTokenSource(tokenHandle, &tokenSource))) - { - PhCopyStringZFromBytes( - tokenSource.SourceName, - TOKEN_SOURCE_LENGTH, - tokenSourceName, - sizeof(tokenSourceName) / 2, - NULL - ); - - PhPrintPointer(tokenSourceLuid, UlongToPtr(tokenSource.SourceIdentifier.LowPart)); - } - - NtClose(tokenHandle); - } - - SetDlgItemText(hwndDlg, IDC_USER, PhGetStringOrDefault(tokenUserName, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_USERSID, PhGetStringOrDefault(tokenUserSid, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_OWNER, PhGetStringOrDefault(tokenOwnerName, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_PRIMARYGROUP, PhGetStringOrDefault(tokenPrimaryGroupName, L"Unknown")); - - if (tokenSessionId != -1) - SetDlgItemInt(hwndDlg, IDC_SESSIONID, tokenSessionId, FALSE); - else - SetDlgItemText(hwndDlg, IDC_SESSIONID, L"Unknown"); - - SetDlgItemText(hwndDlg, IDC_ELEVATED, tokenElevated); - SetDlgItemText(hwndDlg, IDC_VIRTUALIZATION, tokenVirtualization); - SetDlgItemText(hwndDlg, IDC_SOURCENAME, tokenSourceName); - SetDlgItemText(hwndDlg, IDC_SOURCELUID, tokenSourceLuid); - - if (!hasLinkedToken) - ShowWindow(GetDlgItem(hwndDlg, IDC_LINKEDTOKEN), SW_HIDE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_LINKEDTOKEN: - { - NTSTATUS status; - HANDLE tokenHandle; - - if (NT_SUCCESS(status = tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - tokenPageContext->Context - ))) - { - PhShowTokenProperties(hwndDlg, PhpOpenLinkedToken, (PVOID)tokenHandle, L"Linked Token"); - NtClose(tokenHandle); - } - else - { - PhShowStatus(hwndDlg, L"Unable to open the token", status, 0); - } - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_LINKEDTOKEN)); - return TRUE; - } - break; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK PhpTokenAdvancedPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - - tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!tokenPageContext) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HANDLE tokenHandle; - PWSTR tokenType = L"Unknown"; - PWSTR tokenImpersonationLevel = L"Unknown"; - WCHAR tokenLuid[PH_PTR_STR_LEN_1] = L"Unknown"; - WCHAR authenticationLuid[PH_PTR_STR_LEN_1] = L"Unknown"; - PPH_STRING memoryUsed = NULL; - PPH_STRING memoryAvailable = NULL; - - if (NT_SUCCESS(tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - tokenPageContext->Context - ))) - { - TOKEN_STATISTICS statistics; - - if (NT_SUCCESS(PhGetTokenStatistics(tokenHandle, &statistics))) - { - switch (statistics.TokenType) - { - case TokenPrimary: - tokenType = L"Primary"; - break; - case TokenImpersonation: - tokenType = L"Impersonation"; - break; - } - - if (statistics.TokenType == TokenImpersonation) - { - switch (statistics.ImpersonationLevel) - { - case SecurityAnonymous: - tokenImpersonationLevel = L"Anonymous"; - break; - case SecurityIdentification: - tokenImpersonationLevel = L"Identification"; - break; - case SecurityImpersonation: - tokenImpersonationLevel = L"Impersonation"; - break; - case SecurityDelegation: - tokenImpersonationLevel = L"Delegation"; - break; - } - } - else - { - tokenImpersonationLevel = L"N/A"; - } - - PhPrintPointer(tokenLuid, UlongToPtr(statistics.TokenId.LowPart)); - PhPrintPointer(authenticationLuid, UlongToPtr(statistics.AuthenticationId.LowPart)); - - // DynamicCharged contains the number of bytes allocated. - // DynamicAvailable contains the number of bytes free. - memoryUsed = PhaFormatSize(statistics.DynamicCharged - statistics.DynamicAvailable, -1); - memoryAvailable = PhaFormatSize(statistics.DynamicCharged, -1); - } - - NtClose(tokenHandle); - } - - SetDlgItemText(hwndDlg, IDC_TYPE, tokenType); - SetDlgItemText(hwndDlg, IDC_IMPERSONATIONLEVEL, tokenImpersonationLevel); - SetDlgItemText(hwndDlg, IDC_TOKENLUID, tokenLuid); - SetDlgItemText(hwndDlg, IDC_AUTHENTICATIONLUID, authenticationLuid); - SetDlgItemText(hwndDlg, IDC_MEMORYUSED, PhGetStringOrDefault(memoryUsed, L"Unknown")); - SetDlgItemText(hwndDlg, IDC_MEMORYAVAILABLE, PhGetStringOrDefault(memoryAvailable, L"Unknown")); - } - break; - } - - return FALSE; -} - -static COLORREF NTAPI PhpTokenCapabilitiesColorFunction( - _In_ INT Index, - _In_ PVOID Param, - _In_opt_ PVOID Context - ) -{ - PSID_AND_ATTRIBUTES sidAndAttributes = Param; - - return PhGetGroupAttributesColor(sidAndAttributes->Attributes); -} - -INT_PTR CALLBACK PhpTokenCapabilitiesPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - HWND lvHandle; - - tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!tokenPageContext) - return FALSE; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - switch (uMsg) - { - case WM_INITDIALOG: - { - HANDLE tokenHandle; - ULONG i; - - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 200, L"Flags"); - PhSetExtendedListView(lvHandle); - ExtendedListView_SetItemColorFunction(lvHandle, PhpTokenCapabilitiesColorFunction); - - if (NT_SUCCESS(tokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - tokenPageContext->Context - ))) - { - if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenCapabilities, &tokenPageContext->Capabilities))) - { - for (i = 0; i < tokenPageContext->Capabilities->GroupCount; i++) - { - INT lvItemIndex; - PPH_STRING name; - PPH_STRING attributesString; - - name = PhGetSidFullName(tokenPageContext->Capabilities->Groups[i].Sid, TRUE, NULL); - - if (!name) - name = PhSidToStringSid(tokenPageContext->Capabilities->Groups[i].Sid); - - if (name) - { - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, - &tokenPageContext->Capabilities->Groups[i]); - attributesString = PhGetGroupAttributesString( - tokenPageContext->Capabilities->Groups[i].Attributes); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, attributesString->Buffer); - - PhDereferenceObject(attributesString); - PhDereferenceObject(name); - } - } - - if (ListView_GetItemCount(lvHandle) != 0) - { - ListView_SetColumnWidth(lvHandle, 0, LVSCW_AUTOSIZE); - ExtendedListView_SetColumnWidth(lvHandle, 1, ELVSCW_AUTOSIZE_REMAININGSPACE); - } - - ExtendedListView_SortItems(lvHandle); - } - - NtClose(tokenHandle); - } - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_DESTROY: - { - PhFree(tokenPageContext->Capabilities); - tokenPageContext->Capabilities = NULL; - } - break; - case WM_NOTIFY: - { - PhHandleListViewNotifyBehaviors(lParam, lvHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); - } - break; - } - - REFLECT_MESSAGE_DLG(hwndDlg, lvHandle, uMsg, wParam, lParam); - - return FALSE; -} - -BOOLEAN NTAPI PhpAttributeTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PATTRIBUTE_TREE_CONTEXT context; - PATTRIBUTE_NODE node; - - context = Context; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - node = (PATTRIBUTE_NODE)getChildren->Node; - - if (!node) - { - getChildren->Children = (PPH_TREENEW_NODE *)context->RootList->Items; - getChildren->NumberOfChildren = context->RootList->Count; - } - else - { - getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; - getChildren->NumberOfChildren = node->Children->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - node = (PATTRIBUTE_NODE)isLeaf->Node; - - isLeaf->IsLeaf = node->Children->Count == 0; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - - node = (PATTRIBUTE_NODE)getCellText->Node; - - if (getCellText->Id == 0) - getCellText->Text = PhGetStringRef(node->Text); - else - return FALSE; - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - { - PPH_STRING text; - - text = PhGetTreeNewText(hwnd, 0); - PhSetClipboardString(hwnd, &text->sr); - PhDereferenceObject(text); - } - break; - } - } - return TRUE; - } - - return FALSE; -} - -PATTRIBUTE_NODE PhpAddAttributeNode( - _In_ PATTRIBUTE_TREE_CONTEXT Context, - _In_opt_ PATTRIBUTE_NODE Parent, - _In_opt_ _Assume_refs_(1) PPH_STRING Text - ) -{ - PATTRIBUTE_NODE node; - - node = PhAllocate(sizeof(ATTRIBUTE_NODE)); - memset(node, 0, sizeof(ATTRIBUTE_NODE)); - PhInitializeTreeNewNode(&node->Node); - - node->Children = PhCreateList(2); - - PhAddItemList(Context->NodeList, node); - - if (Parent) - PhAddItemList(Parent->Children, node); - else - PhAddItemList(Context->RootList, node); - - PhMoveReference(&node->Text, Text); - - return node; -} - -VOID PhpDestroyAttributeNode( - _In_ PATTRIBUTE_NODE Node - ) -{ - PhDereferenceObject(Node->Children); - PhClearReference(&Node->Text); - PhFree(Node); -} - -VOID PhpInitializeAttributeTreeContext( - _Out_ PATTRIBUTE_TREE_CONTEXT Context, - _In_ HWND TreeNewHandle - ) -{ - PH_TREENEW_VIEW_PARTS parts; - - Context->NodeList = PhCreateList(10); - Context->RootList = PhCreateList(10); - - PhSetControlTheme(TreeNewHandle, L"explorer"); - TreeNew_SetCallback(TreeNewHandle, PhpAttributeTreeNewCallback, Context); - TreeNew_GetViewParts(TreeNewHandle, &parts); - PhAddTreeNewColumnEx2(TreeNewHandle, 0, TRUE, L"Attributes", parts.ClientRect.right - parts.VScrollWidth, PH_ALIGN_LEFT, 0, 0, TN_COLUMN_FLAG_NODPISCALEONADD); -} - -VOID PhpDeleteAttributeTreeContext( - _Inout_ PATTRIBUTE_TREE_CONTEXT Context - ) -{ - ULONG i; - - for (i = 0; i < Context->NodeList->Count; i++) - PhpDestroyAttributeNode(Context->NodeList->Items[i]); - - PhDereferenceObject(Context->NodeList); - PhDereferenceObject(Context->RootList); -} - -PWSTR PhGetSecurityAttributeTypeString( - _In_ USHORT Type - ) -{ - // These types are shared between CLAIM_* and TOKEN_* security attributes. - - switch (Type) - { - case TOKEN_SECURITY_ATTRIBUTE_TYPE_INVALID: - return L"Invalid"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_INT64: - return L"Int64"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_UINT64: - return L"UInt64"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING: - return L"String"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_FQBN: - return L"FQBN"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_SID: - return L"SID"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: - return L"Boolean"; - case TOKEN_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: - return L"Octet string"; - default: - return L"(Unknown)"; - } -} - -PPH_STRING PhGetSecurityAttributeFlagsString( - _In_ ULONG Flags - ) -{ - PH_STRING_BUILDER sb; - - // These flags are shared between CLAIM_* and TOKEN_* security attributes. - - PhInitializeStringBuilder(&sb, 100); - - if (Flags & TOKEN_SECURITY_ATTRIBUTE_MANDATORY) - PhAppendStringBuilder2(&sb, L"Mandatory, "); - if (Flags & TOKEN_SECURITY_ATTRIBUTE_DISABLED) - PhAppendStringBuilder2(&sb, L"Disabled, "); - if (Flags & TOKEN_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT) - PhAppendStringBuilder2(&sb, L"Default disabled, "); - if (Flags & TOKEN_SECURITY_ATTRIBUTE_USE_FOR_DENY_ONLY) - PhAppendStringBuilder2(&sb, L"Use for deny only, "); - if (Flags & TOKEN_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE) - PhAppendStringBuilder2(&sb, L"Case-sensitive, "); - if (Flags & TOKEN_SECURITY_ATTRIBUTE_NON_INHERITABLE) - PhAppendStringBuilder2(&sb, L"Non-inheritable, "); - - if (sb.String->Length != 0) - PhRemoveEndStringBuilder(&sb, 2); - else - PhAppendStringBuilder2(&sb, L"(None)"); - - return PhFinalStringBuilderString(&sb); -} - -PPH_STRING PhFormatClaimSecurityAttributeValue( - _In_ PCLAIM_SECURITY_ATTRIBUTE_V1 Attribute, - _In_ ULONG ValueIndex - ) -{ - PH_FORMAT format; - - switch (Attribute->ValueType) - { - case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64: - PhInitFormatI64D(&format, Attribute->Values.pInt64[ValueIndex]); - return PhFormat(&format, 1, 0); - case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64: - PhInitFormatI64U(&format, Attribute->Values.pUint64[ValueIndex]); - return PhFormat(&format, 1, 0); - case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING: - return PhCreateString(Attribute->Values.ppString[ValueIndex]); - case CLAIM_SECURITY_ATTRIBUTE_TYPE_FQBN: - return PhFormatString(L"Version %I64u: %s", - Attribute->Values.pFqbn[ValueIndex].Version, - Attribute->Values.pFqbn[ValueIndex].Name); - case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID: - { - if (RtlValidSid(Attribute->Values.pOctetString[ValueIndex].pValue)) - { - PPH_STRING name; - - name = PhGetSidFullName(Attribute->Values.pOctetString[ValueIndex].pValue, TRUE, NULL); - - if (name) - return name; - - name = PhSidToStringSid(Attribute->Values.pOctetString[ValueIndex].pValue); - - if (name) - return name; - } - } - return PhCreateString(L"(Invalid SID)"); - case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: - return PhCreateString(Attribute->Values.pInt64[ValueIndex] != 0 ? L"True" : L"False"); - case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: - return PhCreateString(L"(Octet string)"); - default: - return PhCreateString(L"(Unknown)"); - } -} - -PPH_STRING PhFormatTokenSecurityAttributeValue( - _In_ PTOKEN_SECURITY_ATTRIBUTE_V1 Attribute, - _In_ ULONG ValueIndex - ) -{ - PH_FORMAT format; - - switch (Attribute->ValueType) - { - case TOKEN_SECURITY_ATTRIBUTE_TYPE_INT64: - PhInitFormatI64D(&format, Attribute->Values.pInt64[ValueIndex]); - return PhFormat(&format, 1, 0); - case TOKEN_SECURITY_ATTRIBUTE_TYPE_UINT64: - PhInitFormatI64U(&format, Attribute->Values.pUint64[ValueIndex]); - return PhFormat(&format, 1, 0); - case TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING: - return PhCreateStringFromUnicodeString(&Attribute->Values.pString[ValueIndex]); - case TOKEN_SECURITY_ATTRIBUTE_TYPE_FQBN: - return PhFormatString(L"Version %I64u: %.*s", - Attribute->Values.pFqbn[ValueIndex].Version, - Attribute->Values.pFqbn[ValueIndex].Name.Length / sizeof(WCHAR), - Attribute->Values.pFqbn[ValueIndex].Name.Buffer); - case TOKEN_SECURITY_ATTRIBUTE_TYPE_SID: - { - if (RtlValidSid(Attribute->Values.pOctetString[ValueIndex].pValue)) - { - PPH_STRING name; - - name = PhGetSidFullName(Attribute->Values.pOctetString[ValueIndex].pValue, TRUE, NULL); - - if (name) - return name; - - name = PhSidToStringSid(Attribute->Values.pOctetString[ValueIndex].pValue); - - if (name) - return name; - } - } - return PhCreateString(L"(Invalid SID)"); - case TOKEN_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: - return PhCreateString(Attribute->Values.pInt64[ValueIndex] != 0 ? L"True" : L"False"); - case TOKEN_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: - return PhCreateString(L"(Octet string)"); - default: - return PhCreateString(L"(Unknown)"); - } -} - -BOOLEAN PhpAddTokenClaimAttributes( - _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, - _In_ HWND tnHandle, - _In_ BOOLEAN DeviceClaims, - _In_ PATTRIBUTE_NODE Parent - ) -{ - HANDLE tokenHandle; - PCLAIM_SECURITY_ATTRIBUTES_INFORMATION info; - ULONG i; - ULONG j; - - if (!NT_SUCCESS(TokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - TokenPageContext->Context - ))) - return FALSE; - - if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, DeviceClaims ? TokenDeviceClaimAttributes : TokenUserClaimAttributes, &info))) - { - for (i = 0; i < info->AttributeCount; i++) - { - PCLAIM_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; - PATTRIBUTE_NODE node; - PPH_STRING temp; - - // Attribute - node = PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, Parent, PhCreateString(attribute->Name)); - // Type - PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, node, - PhFormatString(L"Type: %s", PhGetSecurityAttributeTypeString(attribute->ValueType))); - // Flags - temp = PhGetSecurityAttributeFlagsString(attribute->Flags); - PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, node, - PhFormatString(L"Flags: %s", temp->Buffer)); - PhDereferenceObject(temp); - - // Values - for (j = 0; j < attribute->ValueCount; j++) - { - temp = PhFormatClaimSecurityAttributeValue(attribute, j); - PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, node, - PhFormatString(L"Value %u: %s", j, temp->Buffer)); - PhDereferenceObject(temp); - } - } - - PhFree(info); - } - - NtClose(tokenHandle); - - TreeNew_NodesStructured(tnHandle); - - return TRUE; -} - -INT_PTR CALLBACK PhpTokenClaimsPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - HWND tnHandle; - - tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!tokenPageContext) - return FALSE; - - tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - - switch (uMsg) - { - case WM_INITDIALOG: - { - PATTRIBUTE_NODE userNode; - PATTRIBUTE_NODE deviceNode; - - PhpInitializeAttributeTreeContext(&tokenPageContext->ClaimsTreeContext, tnHandle); - - TreeNew_SetRedraw(tnHandle, FALSE); - - userNode = PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, NULL, PhCreateString(L"User claims")); - PhpAddTokenClaimAttributes(tokenPageContext, tnHandle, FALSE, userNode); - deviceNode = PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, NULL, PhCreateString(L"Device claims")); - PhpAddTokenClaimAttributes(tokenPageContext, tnHandle, TRUE, deviceNode); - - if (userNode->Children->Count == 0) - PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, userNode, PhCreateString(L"(None)")); - if (deviceNode->Children->Count == 0) - PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, deviceNode, PhCreateString(L"(None)")); - - TreeNew_NodesStructured(tnHandle); - TreeNew_SetRedraw(tnHandle, TRUE); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_DESTROY: - { - PhpDeleteAttributeTreeContext(&tokenPageContext->ClaimsTreeContext); - } - break; - } - - return FALSE; -} - -BOOLEAN PhpAddTokenAttributes( - _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, - _In_ HWND tnHandle - ) -{ - HANDLE tokenHandle; - PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; - ULONG i; - ULONG j; - - if (!NT_SUCCESS(TokenPageContext->OpenObject( - &tokenHandle, - TOKEN_QUERY, - TokenPageContext->Context - ))) - return FALSE; - - if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenSecurityAttributes, &info))) - { - for (i = 0; i < info->AttributeCount; i++) - { - PTOKEN_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; - PATTRIBUTE_NODE node; - PPH_STRING temp; - - // Attribute - node = PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, NULL, - PhCreateStringFromUnicodeString(&attribute->Name)); - // Type - PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, node, - PhFormatString(L"Type: %s", PhGetSecurityAttributeTypeString(attribute->ValueType))); - // Flags - temp = PhGetSecurityAttributeFlagsString(attribute->Flags); - PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, node, - PhFormatString(L"Flags: %s", temp->Buffer)); - PhDereferenceObject(temp); - - // Values - for (j = 0; j < attribute->ValueCount; j++) - { - temp = PhFormatTokenSecurityAttributeValue(attribute, j); - PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, node, - PhFormatString(L"Value %u: %s", j, temp->Buffer)); - PhDereferenceObject(temp); - } - } - - PhFree(info); - } - - NtClose(tokenHandle); - - TreeNew_NodesStructured(tnHandle); - - return TRUE; -} - -INT_PTR CALLBACK PhpTokenAttributesPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PTOKEN_PAGE_CONTEXT tokenPageContext; - HWND tnHandle; - - tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); - - if (!tokenPageContext) - return FALSE; - - tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhpInitializeAttributeTreeContext(&tokenPageContext->AuthzTreeContext, tnHandle); - - TreeNew_SetRedraw(tnHandle, FALSE); - - PhpAddTokenAttributes(tokenPageContext, tnHandle); - - if (tokenPageContext->AuthzTreeContext.RootList->Count == 0) - PhpAddAttributeNode(&tokenPageContext->AuthzTreeContext, NULL, PhCreateString(L"(None)")); - - TreeNew_NodesStructured(tnHandle); - TreeNew_SetRedraw(tnHandle, TRUE); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_DESTROY: - { - PhpDeleteAttributeTreeContext(&tokenPageContext->AuthzTreeContext); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * token properties + * + * Copyright (C) 2010-2012 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef enum _PH_PROCESS_TOKEN_CATEGORY +{ + PH_PROCESS_TOKEN_CATEGORY_DANGEROUS_FLAGS, + PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, + PH_PROCESS_TOKEN_CATEGORY_GROUPS, + PH_PROCESS_TOKEN_CATEGORY_RESTRICTED +} PH_PROCESS_TOKEN_CATEGORY; + +typedef enum _PH_PROCESS_TOKEN_FLAG +{ + PH_PROCESS_TOKEN_FLAG_NO_WRITE_UP, + PH_PROCESS_TOKEN_FLAG_SANDBOX_INERT, + PH_PROCESS_TOKEN_FLAG_UIACCESS +} PH_PROCESS_TOKEN_FLAG; + +typedef enum _PH_PROCESS_TOKEN_INDEX +{ + PH_PROCESS_TOKEN_INDEX_NAME, + PH_PROCESS_TOKEN_INDEX_STATUS, + PH_PROCESS_TOKEN_INDEX_DESCRIPTION, +} PH_PROCESS_TOKEN_INDEX; + +typedef struct _PHP_TOKEN_PAGE_LISTVIEW_ITEM +{ + PH_PROCESS_TOKEN_CATEGORY ItemCategory; + union + { + PSID_AND_ATTRIBUTES TokenGroup; + PLUID_AND_ATTRIBUTES TokenPrivilege; + struct + { + PH_PROCESS_TOKEN_FLAG ItemFlag; + BOOLEAN ItemFlagState; + }; + }; +} PHP_TOKEN_PAGE_LISTVIEW_ITEM, *PPHP_TOKEN_PAGE_LISTVIEW_ITEM; + +typedef struct _PHP_TOKEN_USER_RESOLVE_CONTEXT +{ + HWND WindowHandle; + PSID TokenUserSid; +} PHP_TOKEN_USER_RESOLVE_CONTEXT, *PPHP_TOKEN_USER_RESOLVE_CONTEXT; + +typedef struct _PHP_TOKEN_GROUP_RESOLVE_CONTEXT +{ + HWND ListViewHandle; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM LvItem; + PSID TokenGroupSid; +} PHP_TOKEN_GROUP_RESOLVE_CONTEXT, *PPHP_TOKEN_GROUP_RESOLVE_CONTEXT; + +typedef struct _ATTRIBUTE_NODE +{ + PH_TREENEW_NODE Node; + PPH_LIST Children; + PPH_STRING Text; +} ATTRIBUTE_NODE, *PATTRIBUTE_NODE; + +typedef struct _ATTRIBUTE_TREE_CONTEXT +{ + HWND WindowHandle; + PPH_LIST RootList; + PPH_LIST NodeList; +} ATTRIBUTE_TREE_CONTEXT, *PATTRIBUTE_TREE_CONTEXT; + +typedef struct _TOKEN_PAGE_CONTEXT +{ + PPH_OPEN_OBJECT OpenObject; + PVOID Context; + DLGPROC HookProc; + HANDLE ProcessId; + + HWND ListViewHandle; + HIMAGELIST ListViewImageList; + + PTOKEN_GROUPS Groups; + PTOKEN_GROUPS RestrictedSids; + PTOKEN_PRIVILEGES Privileges; + PTOKEN_GROUPS Capabilities; + + ATTRIBUTE_TREE_CONTEXT CapsTreeContext; + ATTRIBUTE_TREE_CONTEXT ClaimsTreeContext; + ATTRIBUTE_TREE_CONTEXT AuthzTreeContext; +} TOKEN_PAGE_CONTEXT, *PTOKEN_PAGE_CONTEXT; + +PH_ACCESS_ENTRY GroupDescriptionEntries[6] = +{ + { NULL, SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED, FALSE, FALSE, L"Integrity" }, + { NULL, SE_GROUP_LOGON_ID, FALSE, FALSE, L"Logon Id" }, + { NULL, SE_GROUP_OWNER, FALSE, FALSE, L"Owner" }, + { NULL, SE_GROUP_MANDATORY, FALSE, FALSE, L"Mandatory" }, + { NULL, SE_GROUP_USE_FOR_DENY_ONLY, FALSE, FALSE, L"Use for deny only" }, + { NULL, SE_GROUP_RESOURCE, FALSE, FALSE, L"Resource" } +}; + +static PH_STRINGREF PhpEmptyTokenAttributesText = PH_STRINGREF_INIT(L"There are no attributes to display."); +static PH_STRINGREF PhpEmptyTokenClaimsText = PH_STRINGREF_INIT(L"There are no claims to display."); +static PH_STRINGREF PhpEmptyTokenCapabilitiesText = PH_STRINGREF_INIT(L"There are no capabilities to display."); + +INT CALLBACK PhpTokenPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +INT_PTR CALLBACK PhpTokenPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhpShowTokenAdvancedProperties( + _In_ HWND ParentWindowHandle, + _In_ PTOKEN_PAGE_CONTEXT Context, + _In_ BOOLEAN ShowAppContainerPage + ); + +INT_PTR CALLBACK PhpTokenGeneralPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpTokenAdvancedPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpTokenCapabilitiesPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOLEAN NTAPI PhpAttributeTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +INT_PTR CALLBACK PhpTokenClaimsPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpTokenAttributesPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PhpTokenContainerPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +PPH_STRING PhpGetTokenFolderPath( + _In_ HANDLE TokenHandle + ); +PPH_STRING PhpGetTokenRegistryPath( + _In_ HANDLE TokenHandle + ); + +VOID PhShowTokenProperties( + _In_ HWND ParentWindowHandle, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_ HANDLE ProcessId, + _In_opt_ PVOID Context, + _In_opt_ PWSTR Title + ) +{ + PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; + HPROPSHEETPAGE pages[1]; + + propSheetHeader.dwFlags = + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.hwndParent = ParentWindowHandle; + propSheetHeader.pszCaption = Title ? Title : L"Token"; + propSheetHeader.nPages = 1; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = pages; + + pages[0] = PhCreateTokenPage(OpenObject, ProcessId, Context, NULL); + + PhModalPropertySheet(&propSheetHeader); +} + +HPROPSHEETPAGE PhCreateTokenPage( + _In_ PPH_OPEN_OBJECT OpenObject, + _In_ HANDLE ProcessId, + _In_opt_ PVOID Context, + _In_opt_ DLGPROC HookProc + ) +{ + HPROPSHEETPAGE propSheetPageHandle; + PROPSHEETPAGE propSheetPage; + PTOKEN_PAGE_CONTEXT tokenPageContext; + + tokenPageContext = PhCreateAlloc(sizeof(TOKEN_PAGE_CONTEXT)); + memset(tokenPageContext, 0, sizeof(TOKEN_PAGE_CONTEXT)); + tokenPageContext->OpenObject = OpenObject; + tokenPageContext->Context = Context; + tokenPageContext->HookProc = HookProc; + tokenPageContext->ProcessId = ProcessId; + + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USECALLBACK; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_OBJTOKEN); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = PhpTokenPageProc; + propSheetPage.lParam = (LPARAM)tokenPageContext; + propSheetPage.pfnCallback = PhpTokenPropPageProc; + + propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); + // CreatePropertySheetPage would have sent PSPCB_ADDREF (below), + // which would have added a reference. + PhDereferenceObject(tokenPageContext); + + return propSheetPageHandle; +} + +INT CALLBACK PhpTokenPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + + tokenPageContext = (PTOKEN_PAGE_CONTEXT)ppsp->lParam; + + if (uMsg == PSPCB_ADDREF) + { + PhReferenceObject(tokenPageContext); + } + else if (uMsg == PSPCB_RELEASE) + { + PhDereferenceObject(tokenPageContext); + } + + return 1; +} + +PPH_STRING PhGetGroupAttributesString( + _In_ ULONG Attributes, + _In_ BOOLEAN Restricted + ) +{ + PPH_STRING string; + + if (Attributes & (SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED)) + { + if (Attributes & SE_GROUP_ENABLED) + string = PhCreateString(L"Enabled (as a group)"); + else + string = PhReferenceEmptyString(); + } + else + { + if (Attributes & SE_GROUP_ENABLED) + { + if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + string = PhCreateString(L"Enabled"); + else + string = PhCreateString(L"Enabled (modified)"); + } + else + { + if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + string = PhCreateString(L"Disabled (modified)"); + else + string = PhCreateString(L"Disabled"); + } + } + + if (Restricted) + { + PPH_STRING prefixString = string; + string = PhConcatStrings2(prefixString->Buffer, L" (restricted)"); + PhDereferenceObject(prefixString); + } + + return string; +} + +COLORREF PhGetGroupAttributesColor( + _In_ ULONG Attributes + ) +{ + if (Attributes & (SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED)) + { + if (!(Attributes & SE_GROUP_ENABLED)) + return RGB(0xe0, 0xf0, 0xe0); + } + + if (Attributes & SE_GROUP_ENABLED) + { + if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + return RGB(0xe0, 0xf0, 0xe0); + else + return RGB(0xc0, 0xf0, 0xc0); + } + else + { + if (Attributes & SE_GROUP_ENABLED_BY_DEFAULT) + return RGB(0xf0, 0xc0, 0xc0); + else + return RGB(0xf0, 0xe0, 0xe0); + } +} + +COLORREF PhGetPrivilegeAttributesColor( + _In_ ULONG Attributes + ) +{ + if (Attributes & SE_PRIVILEGE_ENABLED) + { + if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) + return RGB(0xe0, 0xf0, 0xe0); + else + return RGB(0xc0, 0xf0, 0xc0); + } + else + { + if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) + return RGB(0xf0, 0xc0, 0xc0); + else + return RGB(0xf0, 0xe0, 0xe0); + } +} + +COLORREF PhGetDangerousFlagColor( + _In_ BOOLEAN FlagState +) +{ + if (FlagState) + return RGB(0xc0, 0xf0, 0xc0); + else + return RGB(0xf0, 0xc0, 0xc0); +} + +static COLORREF NTAPI PhpTokenGroupColorFunction( + _In_ INT Index, + _In_ PVOID Param, + _In_opt_ PVOID Context + ) +{ + PPHP_TOKEN_PAGE_LISTVIEW_ITEM entry = Param; + + if (entry->ItemCategory == PH_PROCESS_TOKEN_CATEGORY_DANGEROUS_FLAGS) + return PhGetDangerousFlagColor(entry->ItemFlagState); + else if (entry->ItemCategory == PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES) + return PhGetPrivilegeAttributesColor(entry->TokenPrivilege->Attributes); + else + return PhGetGroupAttributesColor(entry->TokenGroup->Attributes); +} + +PWSTR PhGetPrivilegeAttributesString( + _In_ ULONG Attributes + ) +{ + if (Attributes & SE_PRIVILEGE_ENABLED) + { + if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) + return L"Enabled"; + else + return L"Enabled (modified)"; + } + else + { + if (Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) + return L"Disabled (modified)"; + else + return L"Disabled"; + } +} + +PWSTR PhGetElevationTypeString( + _In_ TOKEN_ELEVATION_TYPE ElevationType + ) +{ + switch (ElevationType) + { + case TokenElevationTypeFull: + return L"Yes"; + case TokenElevationTypeLimited: + return L"No"; + default: + return L"N/A"; + } +} + +VOID PhpTokenPageFreeListViewEntries( + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext + ) +{ + ULONG index = ULONG_MAX; + + while ((index = PhFindListViewItemByFlags( + TokenPageContext->ListViewHandle, + index, + LVNI_ALL + )) != ULONG_MAX) + { + PPHP_TOKEN_PAGE_LISTVIEW_ITEM entry; + + if (PhGetListViewItemParam(TokenPageContext->ListViewHandle, index, &entry)) + { + PhFree(entry); + } + } +} + +static NTSTATUS NTAPI PhpTokenGroupResolveWorker( + _In_ PVOID ThreadParameter + ) +{ + PPHP_TOKEN_GROUP_RESOLVE_CONTEXT context = ThreadParameter; + PPH_STRING fullUserName; + INT lvItemIndex; + + lvItemIndex = PhFindListViewItemByParam( + context->ListViewHandle, + -1, + context->LvItem + ); + + if (lvItemIndex != -1) + { + if (fullUserName = PhGetSidFullName(context->TokenGroupSid, TRUE, NULL)) + { + PhSetListViewSubItem(context->ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_NAME, PhGetString(fullUserName)); + PhDereferenceObject(fullUserName); + } + } + + PhFree(context->TokenGroupSid); + PhFree(context); + + return STATUS_SUCCESS; +} + +VOID PhpUpdateSidsFromTokenGroups( + _In_ HWND ListViewHandle, + _In_ PTOKEN_GROUPS Groups, + _In_ BOOLEAN Restricted + ) +{ + for (ULONG i = 0; i < Groups->GroupCount; i++) + { + INT lvItemIndex; + PPH_STRING stringUserSid; + PPH_STRING attributesString; + PPH_STRING descriptionString; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM lvitem; + + lvitem = PhAllocateZero(sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + lvitem->ItemCategory = Restricted ? PH_PROCESS_TOKEN_CATEGORY_RESTRICTED : PH_PROCESS_TOKEN_CATEGORY_GROUPS; + lvitem->TokenGroup = &Groups->Groups[i]; + + stringUserSid = PhSidToStringSid(Groups->Groups[i].Sid); + + lvItemIndex = PhAddListViewGroupItem( + ListViewHandle, + lvitem->ItemCategory, + MAXINT, + stringUserSid->Buffer, + lvitem + ); + + if (attributesString = PhGetGroupAttributesString(Groups->Groups[i].Attributes, Restricted)) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_STATUS, PhGetString(attributesString)); + PhDereferenceObject(attributesString); + } + + descriptionString = PhGetAccessString( + Groups->Groups[i].Attributes, + GroupDescriptionEntries, + RTL_NUMBER_OF(GroupDescriptionEntries) + ); + + if (descriptionString) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_DESCRIPTION, PhGetString(descriptionString)); + PhDereferenceObject(descriptionString); + } + + { + PPHP_TOKEN_GROUP_RESOLVE_CONTEXT tokenGroupResolve; + + tokenGroupResolve = PhAllocateZero(sizeof(PHP_TOKEN_GROUP_RESOLVE_CONTEXT)); + tokenGroupResolve->ListViewHandle = ListViewHandle; + tokenGroupResolve->LvItem = lvitem; + tokenGroupResolve->TokenGroupSid = PhAllocateCopy(Groups->Groups[i].Sid, RtlLengthSid(Groups->Groups[i].Sid)); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhpTokenGroupResolveWorker, tokenGroupResolve); + } + + PhDereferenceObject(stringUserSid); + } +} + +BOOLEAN PhpUpdateTokenGroups( + _In_ HWND hwndDlg, + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND ListViewHandle, + _In_ HANDLE TokenHandle + ) +{ + PTOKEN_GROUPS groups = NULL; + PTOKEN_GROUPS restrictedSIDs = NULL; + + if (NT_SUCCESS(PhGetTokenGroups(TokenHandle, &groups))) + { + PhpUpdateSidsFromTokenGroups(ListViewHandle, groups, FALSE); + } + + if (NT_SUCCESS(PhGetTokenRestrictedSids(TokenHandle, &restrictedSIDs))) + { + PhpUpdateSidsFromTokenGroups(ListViewHandle, restrictedSIDs, TRUE); + } + + if (TokenPageContext->RestrictedSids) + PhFree(TokenPageContext->RestrictedSids); + if (TokenPageContext->Groups) + PhFree(TokenPageContext->Groups); + + TokenPageContext->RestrictedSids = restrictedSIDs; + TokenPageContext->Groups = groups; + + return TRUE; +} + +BOOLEAN PhpUpdateTokenPrivileges( + _In_ HWND hwndDlg, + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND ListViewHandle, + _In_ HANDLE TokenHandle + ) +{ + PTOKEN_PRIVILEGES privileges; + ULONG i; + + if (!NT_SUCCESS(PhGetTokenPrivileges(TokenHandle, &privileges))) + return FALSE; + + for (i = 0; i < privileges->PrivilegeCount; i++) + { + INT lvItemIndex; + PPH_STRING privilegeName; + PPH_STRING privilegeDisplayName; + + if (PhLookupPrivilegeName( + &privileges->Privileges[i].Luid, + &privilegeName + )) + { + PPHP_TOKEN_PAGE_LISTVIEW_ITEM lvitem; + + lvitem = PhAllocateZero(sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + lvitem->ItemCategory = PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES; + lvitem->TokenPrivilege = &privileges->Privileges[i]; + + privilegeDisplayName = NULL; + PhLookupPrivilegeDisplayName(&privilegeName->sr, &privilegeDisplayName); + + // Name + lvItemIndex = PhAddListViewGroupItem( + ListViewHandle, + PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, + MAXINT, + privilegeName->Buffer, + lvitem + ); + // Status + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_STATUS, PhGetPrivilegeAttributesString(privileges->Privileges[i].Attributes)); + // Description + PhSetListViewSubItem(ListViewHandle, lvItemIndex, PH_PROCESS_TOKEN_INDEX_DESCRIPTION, PhGetStringOrEmpty(privilegeDisplayName)); + + if (privilegeDisplayName) PhDereferenceObject(privilegeDisplayName); + PhDereferenceObject(privilegeName); + } + } + + if (TokenPageContext->Privileges) + PhFree(TokenPageContext->Privileges); + + TokenPageContext->Privileges = privileges; + + return TRUE; +} + +VOID PhpUpdateTokenDangerousFlagItem( + _In_ HWND ListViewHandle, + _In_ PH_PROCESS_TOKEN_FLAG Flag, + _In_ BOOLEAN State, + _In_ PWSTR Name, + _In_ PWSTR Description + ) +{ + INT lvItemIndex; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM lvitem; + + lvitem = PhAllocateZero(sizeof(PHP_TOKEN_PAGE_LISTVIEW_ITEM)); + lvitem->ItemCategory = PH_PROCESS_TOKEN_CATEGORY_DANGEROUS_FLAGS; + lvitem->ItemFlag = Flag; + lvitem->ItemFlagState = State; + + lvItemIndex = PhAddListViewGroupItem( + ListViewHandle, + lvitem->ItemCategory, + MAXINT, + Name, + lvitem + ); + + PhSetListViewSubItem( + ListViewHandle, + lvItemIndex, + PH_PROCESS_TOKEN_INDEX_STATUS, + State ? L"Enabled (modified)" : L"Disabled (modified)" + ); + + PhSetListViewSubItem( + ListViewHandle, + lvItemIndex, + PH_PROCESS_TOKEN_INDEX_DESCRIPTION, + Description + ); +} + +BOOLEAN PhpUpdateTokenDangerousFlags( + _In_ HWND hwndDlg, + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND ListViewHandle, + _In_ HANDLE TokenHandle + ) +{ + TOKEN_MANDATORY_POLICY mandatoryPolicy; + BOOLEAN isSandboxInert; + BOOLEAN isUIAccess; + + if (NT_SUCCESS(PhGetTokenMandatoryPolicy(TokenHandle, &mandatoryPolicy))) + { + // The disabled no-write-up policy is considered to be dangerous (diversenok) + if ((mandatoryPolicy.Policy & TOKEN_MANDATORY_POLICY_NO_WRITE_UP) == 0) + { + PhpUpdateTokenDangerousFlagItem( + ListViewHandle, + PH_PROCESS_TOKEN_FLAG_NO_WRITE_UP, + FALSE, + L"No-Write-Up Policy", + L"Prevents the process from modifying objects with a higher integrity" + ); + } + } + + if (NT_SUCCESS(PhGetTokenIsSandBoxInert(TokenHandle, &isSandboxInert))) + { + // The presence of SandboxInert flag is considered dangerous (diversenok) + if (isSandboxInert) + { + PhpUpdateTokenDangerousFlagItem( + ListViewHandle, + PH_PROCESS_TOKEN_FLAG_SANDBOX_INERT, + TRUE, + L"Sandbox Inert", + L"Ignore AppLocker rules and Software Restriction Policies" + ); + } + } + + if (NT_SUCCESS(PhGetTokenIsUIAccessEnabled(TokenHandle, &isUIAccess))) + { + // The presence of UIAccess flag is considered dangerous (diversenok) + if (isUIAccess) + { + PhpUpdateTokenDangerousFlagItem( + ListViewHandle, + PH_PROCESS_TOKEN_FLAG_UIACCESS, + TRUE, + L"UIAccess", + L"Ignore User Interface Privilege Isolation" + ); + } + } + + return TRUE; +} + +FORCEINLINE PTOKEN_PAGE_CONTEXT PhpTokenPageHeader( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return PhpGenericPropertyPageHeader(hwndDlg, uMsg, wParam, lParam, 3); +} + +static NTSTATUS NTAPI PhpTokenUserResolveWorker( + _In_ PVOID ThreadParameter + ) +{ + PPHP_TOKEN_USER_RESOLVE_CONTEXT context = ThreadParameter; + PPH_STRING fullUserName; + + if (fullUserName = PhGetSidFullName(context->TokenUserSid, TRUE, NULL)) + { + PhSetWindowText(context->WindowHandle, fullUserName->Buffer); + PhDereferenceObject(fullUserName); + } + + PhFree(context->TokenUserSid); + PhFree(context); + + return STATUS_SUCCESS; +} + +INT_PTR CALLBACK PhpTokenPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + if (tokenPageContext->HookProc) + { + if (tokenPageContext->HookProc(hwndDlg, uMsg, wParam, lParam)) + return TRUE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE tokenHandle; + + tokenPageContext->ListViewHandle = GetDlgItem(hwndDlg, IDC_GROUPS); + tokenPageContext->ListViewImageList = ImageList_Create(2, 20, ILC_COLOR, 1, 1); + + PhSetListViewStyle(tokenPageContext->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(tokenPageContext->ListViewHandle, L"explorer"); + PhAddListViewColumn(tokenPageContext->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 100, L"Name"); + PhAddListViewColumn(tokenPageContext->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Status"); + PhAddListViewColumn(tokenPageContext->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 170, L"Description"); + + PhSetExtendedListView(tokenPageContext->ListViewHandle); + ExtendedListView_SetItemColorFunction(tokenPageContext->ListViewHandle, PhpTokenGroupColorFunction); + ListView_EnableGroupView(tokenPageContext->ListViewHandle, TRUE); + PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_DANGEROUS_FLAGS, L"Dangerous Flags"); + PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES, L"Privileges"); + PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_GROUPS, L"Groups"); + PhAddListViewGroup(tokenPageContext->ListViewHandle, PH_PROCESS_TOKEN_CATEGORY_RESTRICTED, L"Restricting SIDs"); + ListView_SetImageList(tokenPageContext->ListViewHandle, tokenPageContext->ListViewImageList, LVSIL_SMALL); + PhLoadListViewColumnsFromSetting(L"TokenGroupsListViewColumns", tokenPageContext->ListViewHandle); + PhLoadListViewGroupStatesFromSetting(L"TokenGroupsListViewStates", tokenPageContext->ListViewHandle); + PhLoadListViewSortColumnsFromSetting(L"TokenGroupsListViewSort", tokenPageContext->ListViewHandle); + + PhSetDialogItemText(hwndDlg, IDC_USER, L"Unknown"); + PhSetDialogItemText(hwndDlg, IDC_USERSID, L"Unknown"); + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + PTOKEN_USER tokenUser; + PPH_STRING stringUserSid; + ULONG sessionId; + TOKEN_ELEVATION_TYPE elevationType; + BOOLEAN isVirtualizationAllowed; + BOOLEAN isVirtualizationEnabled; + PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; + PPH_STRING appContainerName; + PPH_STRING appContainerSid; + + if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) + { + BOOLEAN tokenIsAppContainer = FALSE; + + PhGetTokenIsAppContainer(tokenHandle, &tokenIsAppContainer); + + if (!tokenIsAppContainer) // HACK (dmex) + { + PPHP_TOKEN_USER_RESOLVE_CONTEXT tokenUserResolve; + + PhSetDialogItemText(hwndDlg, IDC_USER, L"Resolving..."); + + tokenUserResolve = PhAllocateZero(sizeof(PHP_TOKEN_USER_RESOLVE_CONTEXT)); + tokenUserResolve->WindowHandle = GetDlgItem(hwndDlg, IDC_USER); + tokenUserResolve->TokenUserSid = PhAllocateCopy(tokenUser->User.Sid, RtlLengthSid(tokenUser->User.Sid)); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhpTokenUserResolveWorker, tokenUserResolve); + } + + if (stringUserSid = PhSidToStringSid(tokenUser->User.Sid)) + { + PhSetDialogItemText(hwndDlg, IDC_USERSID, stringUserSid->Buffer); + PhDereferenceObject(stringUserSid); + } + + PhFree(tokenUser); + } + + if (NT_SUCCESS(PhGetTokenSessionId(tokenHandle, &sessionId))) + PhSetDialogItemValue(hwndDlg, IDC_SESSIONID, sessionId, FALSE); + + if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) + PhSetDialogItemText(hwndDlg, IDC_ELEVATED, PhGetElevationTypeString(elevationType)); + + if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) + { + if (isVirtualizationAllowed) + { + if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) + { + PhSetDialogItemText( + hwndDlg, + IDC_VIRTUALIZED, + isVirtualizationEnabled ? L"Yes" : L"No" + ); + } + } + else + { + PhSetDialogItemText(hwndDlg, IDC_VIRTUALIZED, L"Not allowed"); + } + } + + if (WINDOWS_HAS_IMMERSIVE) + { + appContainerName = NULL; + appContainerSid = NULL; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) + { + if (appContainerInfo->TokenAppContainer) + { + appContainerName = PhGetAppContainerName(appContainerInfo->TokenAppContainer); + appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); + } + + PhFree(appContainerInfo); + } + + if (appContainerName) + { + PPH_STRING packageFamilyName; + + packageFamilyName = PhConcatStrings2(appContainerName->Buffer, L" (APP_CONTAINER)"); + PhSetDialogItemText(hwndDlg, IDC_USER, packageFamilyName->Buffer); + + PhDereferenceObject(packageFamilyName); + PhDereferenceObject(appContainerName); + } + + if (appContainerSid) + { + PhSetDialogItemText(hwndDlg, IDC_USERSID, appContainerSid->Buffer); + PhDereferenceObject(appContainerSid); + } + } + + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); + PhpTokenPageFreeListViewEntries(tokenPageContext); + ListView_DeleteAllItems(tokenPageContext->ListViewHandle); + + PhpUpdateTokenDangerousFlags(hwndDlg, tokenPageContext, tokenPageContext->ListViewHandle, tokenHandle); + PhpUpdateTokenGroups(hwndDlg, tokenPageContext, tokenPageContext->ListViewHandle, tokenHandle); + PhpUpdateTokenPrivileges(hwndDlg, tokenPageContext, tokenPageContext->ListViewHandle, tokenHandle); + + ExtendedListView_SortItems(tokenPageContext->ListViewHandle); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, TRUE); + + NtClose(tokenHandle); + } + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhSaveListViewSortColumnsToSetting(L"TokenGroupsListViewSort", tokenPageContext->ListViewHandle); + PhSaveListViewGroupStatesToSetting(L"TokenGroupsListViewStates", tokenPageContext->ListViewHandle); + PhSaveListViewColumnsToSetting(L"TokenGroupsListViewColumns", tokenPageContext->ListViewHandle); + + if (tokenPageContext->ListViewImageList) + ImageList_Destroy(tokenPageContext->ListViewImageList); + + PhpTokenPageFreeListViewEntries(tokenPageContext); + + if (tokenPageContext->Groups) PhFree(tokenPageContext->Groups); + if (tokenPageContext->RestrictedSids) PhFree(tokenPageContext->RestrictedSids); + if (tokenPageContext->Privileges) PhFree(tokenPageContext->Privileges); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case ID_PRIVILEGE_ENABLE: + case ID_PRIVILEGE_DISABLE: + case ID_PRIVILEGE_RESET: + case ID_PRIVILEGE_REMOVE: + { + NTSTATUS status; + BOOLEAN listViewGroupItemsValid = TRUE; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM *listViewItems; + ULONG numberOfItems; + HANDLE tokenHandle; + ULONG i; + + PhGetSelectedListViewItemParams( + tokenPageContext->ListViewHandle, + &listViewItems, + &numberOfItems + ); + + for (i = 0; i < numberOfItems; i++) + { + if (listViewItems[i]->ItemCategory != PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES) + { + listViewGroupItemsValid = FALSE; + break; + } + } + + if (!listViewGroupItemsValid) + { + PhFree(listViewItems); + break; + } + + if (GET_WM_COMMAND_ID(wParam, lParam) == ID_PRIVILEGE_REMOVE) + { + if (!PhShowConfirmMessage( + hwndDlg, + L"remove", + L"the selected privilege(s)", + L"Removing privileges may reduce the functionality of the process, " + L"and is permanent for the lifetime of the process.", + FALSE + )) + { + PhFree(listViewItems); + break; + } + } + + status = tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_ADJUST_PRIVILEGES, + tokenPageContext->Context // ProcessId + ); + + if (NT_SUCCESS(status)) + { + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); + + for (i = 0; i < numberOfItems; i++) + { + PPH_STRING privilegeName = NULL; + ULONG newAttributes = listViewItems[i]->TokenPrivilege->Attributes; + + if (PhLookupPrivilegeName(&listViewItems[i]->TokenPrivilege->Luid, &privilegeName)) + { + PH_AUTO(privilegeName); + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case ID_PRIVILEGE_ENABLE: + newAttributes |= SE_PRIVILEGE_ENABLED; + break; + case ID_PRIVILEGE_DISABLE: + newAttributes &= ~SE_PRIVILEGE_ENABLED; + break; + case ID_PRIVILEGE_RESET: + { + if (newAttributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT) + newAttributes |= SE_PRIVILEGE_ENABLED; + else + newAttributes &= ~SE_PRIVILEGE_ENABLED; + } + break; + case ID_PRIVILEGE_REMOVE: + newAttributes = SE_PRIVILEGE_REMOVED; + break; + } + + if (PhSetTokenPrivilege( + tokenHandle, + NULL, + &listViewItems[i]->TokenPrivilege->Luid, + newAttributes + )) + { + INT lvItemIndex = PhFindListViewItemByParam( + tokenPageContext->ListViewHandle, + -1, + listViewItems[i] + ); + + if (lvItemIndex != -1) + { + if (GET_WM_COMMAND_ID(wParam, lParam) != ID_PRIVILEGE_REMOVE) + { + // Refresh the status text (and background color). + listViewItems[i]->TokenPrivilege->Attributes = newAttributes; + PhSetListViewSubItem( + tokenPageContext->ListViewHandle, + lvItemIndex, + PH_PROCESS_TOKEN_INDEX_STATUS, + PhGetPrivilegeAttributesString(newAttributes) + ); + } + else + { + ListView_DeleteItem( + tokenPageContext->ListViewHandle, + lvItemIndex + ); + } + } + } + else + { + PWSTR action = L"set"; + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case ID_PRIVILEGE_ENABLE: + action = L"enable"; + break; + case ID_PRIVILEGE_DISABLE: + action = L"disable"; + break; + case ID_PRIVILEGE_RESET: + action = L"reset"; + break; + case ID_PRIVILEGE_REMOVE: + action = L"remove"; + break; + } + + if (!PhShowContinueStatus( + hwndDlg, + PhaFormatString(L"Unable to %s %s.", action, PhGetStringOrDefault(privilegeName, L"privilege"))->Buffer, + STATUS_UNSUCCESSFUL, + 0 + )) + break; + } + } + + ExtendedListView_SortItems(tokenPageContext->ListViewHandle); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, TRUE); + + NtClose(tokenHandle); + } + else + { + PhShowStatus(hwndDlg, L"Unable to open the token.", status, 0); + } + + PhFree(listViewItems); + } + break; + case ID_GROUP_ENABLE: + case ID_GROUP_DISABLE: + case ID_GROUP_RESET: + { + NTSTATUS status; + BOOLEAN listViewGroupItemsValid = TRUE; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM *listViewItems; + ULONG numberOfItems; + HANDLE tokenHandle; + ULONG i; + + PhGetSelectedListViewItemParams( + tokenPageContext->ListViewHandle, + &listViewItems, + &numberOfItems + ); + + for (i = 0; i < numberOfItems; i++) + { + if (listViewItems[i]->ItemCategory != PH_PROCESS_TOKEN_CATEGORY_GROUPS) + { + listViewGroupItemsValid = FALSE; + break; + } + } + + if (!listViewGroupItemsValid) + { + PhFree(listViewItems); + break; + } + + status = tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_ADJUST_GROUPS, + tokenPageContext->Context // ProcessId + ); + + if (NT_SUCCESS(status)) + { + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); + + for (i = 0; i < numberOfItems; i++) + { + ULONG newAttributes = listViewItems[i]->TokenGroup->Attributes; + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case ID_GROUP_ENABLE: + newAttributes |= SE_GROUP_ENABLED; + break; + case ID_GROUP_DISABLE: + newAttributes &= ~SE_GROUP_ENABLED; + break; + case ID_GROUP_RESET: + { + if (newAttributes & SE_GROUP_ENABLED_BY_DEFAULT) + newAttributes |= SE_GROUP_ENABLED; + else + newAttributes &= ~SE_GROUP_ENABLED; + } + break; + } + + if (NT_SUCCESS(status = PhSetTokenGroups( + tokenHandle, + NULL, + listViewItems[i]->TokenGroup->Sid, + newAttributes + ))) + { + PPH_STRING attributesString; + INT lvItemIndex; + + attributesString = PhGetGroupAttributesString(newAttributes, FALSE); + lvItemIndex = PhFindListViewItemByParam( + tokenPageContext->ListViewHandle, + -1, + listViewItems[i] + ); + + if (lvItemIndex != -1) + { + // Refresh the status text (and background color). + listViewItems[i]->TokenGroup->Attributes = newAttributes; + PhSetListViewSubItem( + tokenPageContext->ListViewHandle, + lvItemIndex, + PH_PROCESS_TOKEN_INDEX_STATUS, + attributesString->Buffer + ); + } + + PhDereferenceObject(attributesString); + } + else + { + PWSTR action = L"set"; + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case ID_GROUP_ENABLE: + action = L"enable"; + break; + case ID_GROUP_DISABLE: + action = L"disable"; + break; + case ID_GROUP_RESET: + action = L"reset"; + break; + } + + if (!PhShowContinueStatus( + hwndDlg, + PhaFormatString(L"Unable to %s %s.", action, L"group")->Buffer, + status, + 0 + )) + break; + } + } + + ExtendedListView_SortItems(tokenPageContext->ListViewHandle); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, TRUE); + + NtClose(tokenHandle); + } + else + { + PhShowStatus(hwndDlg, L"Unable to open the token.", status, 0); + } + + PhFree(listViewItems); + } + break; + case ID_UIACCESS_REMOVE: + { + NTSTATUS status; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM *listViewItems; + ULONG numberOfItems; + HANDLE tokenHandle; + + PhGetSelectedListViewItemParams( + tokenPageContext->ListViewHandle, + &listViewItems, + &numberOfItems + ); + + if (numberOfItems != 1) + { + PhFree(listViewItems); + break; + } + + if ((listViewItems[0]->ItemCategory != PH_PROCESS_TOKEN_CATEGORY_DANGEROUS_FLAGS) || + (listViewItems[0]->ItemFlag != PH_PROCESS_TOKEN_FLAG_UIACCESS)) + { + PhFree(listViewItems); + break; + } + + if (!PhShowConfirmMessage( + hwndDlg, + L"remove", + L"the UIAccess flag", + L"Removing this flag may reduce the functionality of the process " + L"provided it is an accessibility application.", + FALSE + )) + { + PhFree(listViewItems); + break; + } + + status = tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_ADJUST_DEFAULT, + tokenPageContext->Context // ProcessId + ); + + if (NT_SUCCESS(status)) + { + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); + + status = PhSetTokenUIAccessEnabled(tokenHandle, FALSE); + + if (NT_SUCCESS(status)) + { + INT lvItemIndex = PhFindListViewItemByParam( + tokenPageContext->ListViewHandle, + -1, + listViewItems[0] + ); + + if (lvItemIndex != -1) + { + ListView_DeleteItem(tokenPageContext->ListViewHandle, lvItemIndex); + } + } + else + { + PhShowStatus(hwndDlg, L"Unable to disable UIAccess flag.", status, 0); + } + + ExtendedListView_SortItems(tokenPageContext->ListViewHandle); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, TRUE); + + NtClose(tokenHandle); + } + else + { + PhShowStatus(hwndDlg, L"Unable to open the token.", status, 0); + } + + PhFree(listViewItems); + } + break; + case IDC_DEFAULTPERM: + { + PhEditSecurity( + PhCsForceNoParent ? NULL : hwndDlg, + L"Default Token", + L"TokenDefault", + tokenPageContext->OpenObject, + NULL, + tokenPageContext->Context // ProcessId + ); + } + break; + case IDC_PERMISSIONS: + { + PhEditSecurity( + PhCsForceNoParent ? NULL : hwndDlg, + L"Token", + L"Token", + tokenPageContext->OpenObject, + NULL, + tokenPageContext->Context // ProcessId + ); + } + break; + case IDC_INTEGRITY: + { + NTSTATUS status; + RECT rect; + PPH_EMENU menu; + HANDLE tokenHandle; + MANDATORY_LEVEL_RID integrityLevelRID; + PPH_EMENU_ITEM selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_INTEGRITY), &rect); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatorySecureProcessRID, L"Protected", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatorySystemRID, L"System", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryHighRID, L"High", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryMediumRID, L"Medium", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryLowRID, L"Low", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MandatoryUntrustedRID, L"Untrusted", NULL, NULL), ULONG_MAX); + + integrityLevelRID = ULONG_MAX; + + // Put a radio check on the menu item that corresponds with the current integrity level. + // Also disable menu items which correspond to higher integrity levels since + // NtSetInformationToken doesn't allow integrity levels to be raised. + if (NT_SUCCESS(status = tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + if (NT_SUCCESS(status = PhGetTokenIntegrityLevelRID( + tokenHandle, + &integrityLevelRID, + NULL + ))) + { + ULONG customLevelPosition = 0; // Processing atypical integrity levels + + for (ULONG i = 0; i < menu->Items->Count; i++) + { + PPH_EMENU_ITEM menuItem = menu->Items->Items[i]; + + if (menuItem->Id == (ULONG)integrityLevelRID) + { + menuItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; + customLevelPosition = 0; // The integrity level is a well-known one. No need to add a new menu item. + } + else if (menuItem->Id > (ULONG)integrityLevelRID) + { + menuItem->Flags |= PH_EMENU_DISABLED; + customLevelPosition = i + 1; + } + } + + if (customLevelPosition) + { + PPH_EMENU_ITEM unknownIntegrityItem; + + unknownIntegrityItem = PhCreateEMenuItem(0, (ULONG)integrityLevelRID, L"Intermediate level", NULL, NULL); + unknownIntegrityItem->Flags |= PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK; + PhInsertEMenuItem(menu, unknownIntegrityItem, customLevelPosition); + } + } + + NtClose(tokenHandle); + } + + if (!NT_SUCCESS(status)) + { + for (ULONG i = 0; i < menu->Items->Count; i++) + { + PPH_EMENU_ITEM menuItem = menu->Items->Items[i]; + menuItem->Flags |= PH_EMENU_DISABLED; + } + } + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id != integrityLevelRID) + { + if (PhShowConfirmMessage( + hwndDlg, + L"set", + L"the integrity level", + L"Once lowered, the integrity level of the token cannot be raised again.", + FALSE + )) + { + if (NT_SUCCESS(status = tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, + tokenPageContext->Context // ProcessId + ))) + { + static SID_IDENTIFIER_AUTHORITY mandatoryLabelAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY; + + UCHAR newSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG)]; + PSID newSid; + TOKEN_MANDATORY_LABEL mandatoryLabel; + + newSid = (PSID)newSidBuffer; + RtlInitializeSid(newSid, &mandatoryLabelAuthority, 1); + *RtlSubAuthoritySid(newSid, 0) = selectedItem->Id; + mandatoryLabel.Label.Sid = newSid; + mandatoryLabel.Label.Attributes = SE_GROUP_INTEGRITY; + + status = NtSetInformationToken( + tokenHandle, + TokenIntegrityLevel, + &mandatoryLabel, + sizeof(TOKEN_MANDATORY_LABEL) + ); + + if (NT_SUCCESS(status)) + { + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, FALSE); + PhpTokenPageFreeListViewEntries(tokenPageContext); + ListView_DeleteAllItems(tokenPageContext->ListViewHandle); + PhpUpdateTokenGroups(hwndDlg, tokenPageContext, tokenPageContext->ListViewHandle, tokenHandle); + PhpUpdateTokenPrivileges(hwndDlg, tokenPageContext, tokenPageContext->ListViewHandle, tokenHandle); + ExtendedListView_SortItems(tokenPageContext->ListViewHandle); + ExtendedListView_SetRedraw(tokenPageContext->ListViewHandle, TRUE); + } + + NtClose(tokenHandle); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(hwndDlg, L"Unable to set the integrity level", status, 0); + } + } + + PhDestroyEMenu(menu); + } + break; + case IDC_ADVANCED: + { + HANDLE tokenHandle; + BOOLEAN tokenIsAppContainer = FALSE; + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + PhGetTokenIsAppContainer(tokenHandle, &tokenIsAppContainer); + NtClose(tokenHandle); + } + + PhpShowTokenAdvancedProperties(hwndDlg, tokenPageContext, tokenIsAppContainer); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_SESSIONID)); + return TRUE; + } + break; + } + + PhHandleListViewNotifyBehaviors(lParam, tokenPageContext->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == tokenPageContext->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PPHP_TOKEN_PAGE_LISTVIEW_ITEM *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(tokenPageContext->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + BOOLEAN hasMixedCategories = FALSE; + PH_PROCESS_TOKEN_CATEGORY currentCategory = listviewItems[0]->ItemCategory; + ULONG i; + + for (i = 1; i < numberOfItems; i++) + { + if (listviewItems[i]->ItemCategory != currentCategory) + { + hasMixedCategories = TRUE; + break; + } + } + + menu = PhCreateEMenu(); + + if (!hasMixedCategories) + { + switch (currentCategory) + { + case PH_PROCESS_TOKEN_CATEGORY_PRIVILEGES: + { + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_ENABLE, L"&Enable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_DISABLE, L"&Disable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_RESET, L"Re&set", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_PRIVILEGE_REMOVE, L"&Remove", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + } + break; + case PH_PROCESS_TOKEN_CATEGORY_GROUPS: + { + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_GROUP_ENABLE, L"&Enable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_GROUP_DISABLE, L"&Disable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_GROUP_RESET, L"Re&set", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + } + break; + case PH_PROCESS_TOKEN_CATEGORY_DANGEROUS_FLAGS: + { + if ((numberOfItems == 1) && (listviewItems[0]->ItemFlag == PH_PROCESS_TOKEN_FLAG_UIACCESS)) + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_UIACCESS_REMOVE, L"&Remove", NULL, NULL), ULONG_MAX); + } + break; + } + } + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, tokenPageContext->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(tokenPageContext->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + REFLECT_MESSAGE_DLG(hwndDlg, tokenPageContext->ListViewHandle, uMsg, wParam, lParam); + + return FALSE; +} + +VOID PhpShowTokenAdvancedProperties( + _In_ HWND ParentWindowHandle, + _In_ PTOKEN_PAGE_CONTEXT Context, + _In_ BOOLEAN ShowAppContainerPage + ) +{ + PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; + HPROPSHEETPAGE pages[6]; + PROPSHEETPAGE page; + ULONG numberOfPages; + + propSheetHeader.dwFlags = + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.hwndParent = ParentWindowHandle; + propSheetHeader.pszCaption = L"Token"; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = pages; + + numberOfPages = 0; + + // General + + memset(&page, 0, sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.pszTemplate = MAKEINTRESOURCE(IDD_TOKGENERAL); + page.hInstance = PhInstanceHandle; + page.pfnDlgProc = PhpTokenGeneralPageProc; + page.lParam = (LPARAM)Context; + pages[numberOfPages++] = CreatePropertySheetPage(&page); + + // Advanced + + memset(&page, 0, sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.pszTemplate = MAKEINTRESOURCE(IDD_TOKADVANCED); + page.hInstance = PhInstanceHandle; + page.pfnDlgProc = PhpTokenAdvancedPageProc; + page.lParam = (LPARAM)Context; + pages[numberOfPages++] = CreatePropertySheetPage(&page); + + if (WindowsVersion >= WINDOWS_8) + { + if (ShowAppContainerPage) + { + memset(&page, 0, sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.dwFlags = PSP_USETITLE; + page.pszTemplate = MAKEINTRESOURCE(IDD_TOKADVANCED); + page.hInstance = PhInstanceHandle; + page.pszTitle = L"Container"; + page.pfnDlgProc = PhpTokenContainerPageProc; + page.lParam = (LPARAM)Context; + pages[numberOfPages++] = CreatePropertySheetPage(&page); + } + + // Capabilities + + memset(&page, 0, sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.pszTemplate = MAKEINTRESOURCE(IDD_TOKCAPABILITIES); + page.hInstance = PhInstanceHandle; + page.pfnDlgProc = PhpTokenCapabilitiesPageProc; + page.lParam = (LPARAM)Context; + pages[numberOfPages++] = CreatePropertySheetPage(&page); + + // Claims + + memset(&page, 0, sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.dwFlags = PSP_USETITLE; + page.pszTemplate = MAKEINTRESOURCE(IDD_TOKATTRIBUTES); + page.hInstance = PhInstanceHandle; + page.pszTitle = L"Claims"; + page.pfnDlgProc = PhpTokenClaimsPageProc; + page.lParam = (LPARAM)Context; + pages[numberOfPages++] = CreatePropertySheetPage(&page); + + // (Token) Attributes + + memset(&page, 0, sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.dwFlags = PSP_USETITLE; + page.pszTemplate = MAKEINTRESOURCE(IDD_TOKATTRIBUTES); + page.hInstance = PhInstanceHandle; + page.pszTitle = L"Attributes"; + page.pfnDlgProc = PhpTokenAttributesPageProc; + page.lParam = (LPARAM)Context; + pages[numberOfPages++] = CreatePropertySheetPage(&page); + } + + propSheetHeader.nPages = numberOfPages; + PhModalPropertySheet(&propSheetHeader); +} + +static NTSTATUS PhpOpenLinkedToken( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ) +{ + return PhGetTokenLinkedToken((HANDLE)Context, Handle); +} + +INT_PTR CALLBACK PhpTokenGeneralPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE tokenHandle; + PPH_STRING tokenUserName = NULL; + PPH_STRING tokenUserSid = NULL; + PPH_STRING tokenOwnerName = NULL; + PPH_STRING tokenPrimaryGroupName = NULL; + ULONG tokenSessionId = ULONG_MAX; + PWSTR tokenElevated = L"N/A"; + BOOLEAN hasLinkedToken = FALSE; + PWSTR tokenVirtualization = L"N/A"; + PWSTR tokenUIAccess = L"Unknown"; + WCHAR tokenSourceName[TOKEN_SOURCE_LENGTH + 1] = L"Unknown"; + WCHAR tokenSourceLuid[PH_PTR_STR_LEN_1] = L"Unknown"; + + // HACK + PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + PTOKEN_USER tokenUser; + PTOKEN_OWNER tokenOwner; + PTOKEN_PRIMARY_GROUP tokenPrimaryGroup; + TOKEN_ELEVATION_TYPE elevationType; + BOOLEAN isVirtualizationAllowed; + BOOLEAN isVirtualizationEnabled; + BOOLEAN isUIAccessEnabled; + + if (NT_SUCCESS(PhGetTokenUser(tokenHandle, &tokenUser))) + { + tokenUserName = PH_AUTO(PhGetSidFullName(tokenUser->User.Sid, TRUE, NULL)); + tokenUserSid = PH_AUTO(PhSidToStringSid(tokenUser->User.Sid)); + + PhFree(tokenUser); + } + + if (NT_SUCCESS(PhGetTokenOwner(tokenHandle, &tokenOwner))) + { + tokenOwnerName = PH_AUTO(PhGetSidFullName(tokenOwner->Owner, TRUE, NULL)); + PhFree(tokenOwner); + } + + if (NT_SUCCESS(PhGetTokenPrimaryGroup(tokenHandle, &tokenPrimaryGroup))) + { + tokenPrimaryGroupName = PH_AUTO(PhGetSidFullName( + tokenPrimaryGroup->PrimaryGroup, TRUE, NULL)); + PhFree(tokenPrimaryGroup); + } + + PhGetTokenSessionId(tokenHandle, &tokenSessionId); + + if (NT_SUCCESS(PhGetTokenElevationType(tokenHandle, &elevationType))) + { + tokenElevated = PhGetElevationTypeString(elevationType); + hasLinkedToken = elevationType != TokenElevationTypeDefault; + } + + if (NT_SUCCESS(PhGetTokenIsVirtualizationAllowed(tokenHandle, &isVirtualizationAllowed))) + { + if (isVirtualizationAllowed) + { + if (NT_SUCCESS(PhGetTokenIsVirtualizationEnabled(tokenHandle, &isVirtualizationEnabled))) + { + tokenVirtualization = isVirtualizationEnabled ? L"Enabled" : L"Disabled"; + } + } + else + { + tokenVirtualization = L"Not Allowed"; + } + } + + if (NT_SUCCESS(PhGetTokenIsUIAccessEnabled(tokenHandle, &isUIAccessEnabled))) + { + tokenUIAccess = isUIAccessEnabled ? L"Enabled": L"Disabled"; + } + + NtClose(tokenHandle); + } + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY_SOURCE, + tokenPageContext->Context // ProcessId + ))) + { + TOKEN_SOURCE tokenSource; + + if (NT_SUCCESS(PhGetTokenSource(tokenHandle, &tokenSource))) + { + PhCopyStringZFromBytes( + tokenSource.SourceName, + TOKEN_SOURCE_LENGTH, + tokenSourceName, + RTL_NUMBER_OF(tokenSourceName), + NULL + ); + + PhPrintPointer(tokenSourceLuid, UlongToPtr(tokenSource.SourceIdentifier.LowPart)); + } + + NtClose(tokenHandle); + } + + PhSetDialogItemText(hwndDlg, IDC_USER, PhGetStringOrDefault(tokenUserName, L"Unknown")); + PhSetDialogItemText(hwndDlg, IDC_USERSID, PhGetStringOrDefault(tokenUserSid, L"Unknown")); + PhSetDialogItemText(hwndDlg, IDC_OWNER, PhGetStringOrDefault(tokenOwnerName, L"Unknown")); + PhSetDialogItemText(hwndDlg, IDC_PRIMARYGROUP, PhGetStringOrDefault(tokenPrimaryGroupName, L"Unknown")); + + if (tokenSessionId != ULONG_MAX) + PhSetDialogItemValue(hwndDlg, IDC_SESSIONID, tokenSessionId, FALSE); + else + PhSetDialogItemText(hwndDlg, IDC_SESSIONID, L"Unknown"); + + PhSetDialogItemText(hwndDlg, IDC_ELEVATED, tokenElevated); + PhSetDialogItemText(hwndDlg, IDC_VIRTUALIZATION, tokenVirtualization); + PhSetDialogItemText(hwndDlg, IDC_UIACCESS, tokenUIAccess); + PhSetDialogItemText(hwndDlg, IDC_SOURCENAME, tokenSourceName); + PhSetDialogItemText(hwndDlg, IDC_SOURCELUID, tokenSourceLuid); + + if (!hasLinkedToken) + ShowWindow(GetDlgItem(hwndDlg, IDC_LINKEDTOKEN), SW_HIDE); + + if (PhEnableThemeSupport) // TODO: Required for compat (dmex) + PhInitializeWindowTheme(GetParent(hwndDlg), PhEnableThemeSupport); // HACK (GetParent) + else + PhInitializeWindowTheme(hwndDlg, FALSE); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_LINKEDTOKEN: + { + NTSTATUS status; + HANDLE tokenHandle; + + if (NT_SUCCESS(status = tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + PhShowTokenProperties(hwndDlg, PhpOpenLinkedToken, tokenPageContext->ProcessId, (PVOID)tokenHandle, L"Linked Token"); + NtClose(tokenHandle); + } + else + { + PhShowStatus(hwndDlg, L"Unable to open the token", status, 0); + } + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_LINKEDTOKEN)); + return TRUE; + } + break; + } + } + break; + } + + return FALSE; +} + +typedef struct _PHP_TOKEN_ADVANCED_CONTEXT +{ + HWND WindowHandle; + HWND ListViewHandle; + PH_LAYOUT_MANAGER LayoutManager; +} PHP_TOKEN_ADVANCED_CONTEXT, *PPHP_TOKEN_ADVANCED_CONTEXT; + +INT_PTR CALLBACK PhpTokenAdvancedPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + PPHP_TOKEN_ADVANCED_CONTEXT context; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocateZero(sizeof(PHP_TOKEN_ADVANCED_CONTEXT)); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE tokenHandle; + ULONG listViewGroupIndex = 0; + PWSTR tokenType = L"Unknown"; + PWSTR tokenImpersonationLevel = L"Unknown"; + WCHAR tokenLuid[PH_PTR_STR_LEN_1] = L"Unknown"; + WCHAR authenticationLuid[PH_PTR_STR_LEN_1] = L"Unknown"; + WCHAR tokenModifiedLuid[PH_PTR_STR_LEN_1] = L"Unknown"; + WCHAR tokenOriginLogonSession[PH_PTR_STR_LEN_1] = L"Unknown"; + PPH_STRING memoryUsed = NULL; + PPH_STRING memoryAvailable = NULL; + PPH_STRING tokenNamedObjectPathString = NULL; + PPH_STRING tokenSecurityDescriptorString = NULL; + PPH_STRING tokenTrustLevelSidString; + PPH_STRING tokenTrustLevelNameString; + PPH_STRING tokenProfilePathString; + PPH_STRING tokenProfileRegistryString; + + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 280, L"Value"); + PhSetExtendedListView(context->ListViewHandle); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + + ListView_EnableGroupView(context->ListViewHandle, TRUE); + PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"General"); + PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"LUIDs"); + PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"Memory"); + PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"Properties"); + PhAddListViewGroupItem(context->ListViewHandle, 0, MAXINT, L"Type", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 0, MAXINT, L"Impersonation level", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"Token LUID", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"Authentication LUID", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"ModifiedId LUID", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"Origin LUID", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 2, MAXINT, L"Memory used", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 2, MAXINT, L"Memory available", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 3, MAXINT, L"Token object path", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 3, MAXINT, L"Token SDDL", NULL); + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + TOKEN_STATISTICS statistics; + TOKEN_ORIGIN origin; + + if (NT_SUCCESS(PhGetTokenStatistics(tokenHandle, &statistics))) + { + switch (statistics.TokenType) + { + case TokenPrimary: + tokenType = L"Primary"; + break; + case TokenImpersonation: + tokenType = L"Impersonation"; + break; + } + + if (statistics.TokenType == TokenImpersonation) + { + switch (statistics.ImpersonationLevel) + { + case SecurityAnonymous: + tokenImpersonationLevel = L"Anonymous"; + break; + case SecurityIdentification: + tokenImpersonationLevel = L"Identification"; + break; + case SecurityImpersonation: + tokenImpersonationLevel = L"Impersonation"; + break; + case SecurityDelegation: + tokenImpersonationLevel = L"Delegation"; + break; + } + } + else + { + tokenImpersonationLevel = L"N/A"; + } + + PhPrintPointer(tokenLuid, UlongToPtr(statistics.TokenId.LowPart)); + PhPrintPointer(authenticationLuid, UlongToPtr(statistics.AuthenticationId.LowPart)); + PhPrintPointer(tokenModifiedLuid, UlongToPtr(statistics.ModifiedId.LowPart)); + + memoryUsed = PhFormatSize(statistics.DynamicCharged - statistics.DynamicAvailable, ULONG_MAX); // DynamicAvailable contains the number of bytes free. + memoryAvailable = PhFormatSize(statistics.DynamicCharged, ULONG_MAX); // DynamicCharged contains the number of bytes allocated. + } + + if (NT_SUCCESS(PhGetTokenOrigin(tokenHandle, &origin))) + { + PhPrintPointer(tokenOriginLogonSession, UlongToPtr(origin.OriginatingLogonSession.LowPart)); + } + + PhGetTokenNamedObjectPath(tokenHandle, NULL, &tokenNamedObjectPathString); + PhGetObjectSecurityDescriptorAsString(tokenHandle, &tokenSecurityDescriptorString); + + if (NT_SUCCESS(PhGetTokenProcessTrustLevelRID( + tokenHandle, + NULL, + NULL, + &tokenTrustLevelNameString, + &tokenTrustLevelSidString + ))) + { + INT trustLevelGroupIndex; + INT trustLevelSidIndex; + INT trustLevelNameIndex; + + trustLevelGroupIndex = PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"TrustLevel"); + trustLevelSidIndex = PhAddListViewGroupItem(context->ListViewHandle, trustLevelGroupIndex, MAXINT, L"TrustLevel Sid", NULL); + trustLevelNameIndex = PhAddListViewGroupItem(context->ListViewHandle, trustLevelGroupIndex, MAXINT, L"TrustLevel Name", NULL); + + PhSetListViewSubItem(context->ListViewHandle, trustLevelSidIndex, 1, PhGetStringOrDefault(tokenTrustLevelSidString, L"N/A")); + PhSetListViewSubItem(context->ListViewHandle, trustLevelNameIndex, 1, PhGetStringOrDefault(tokenTrustLevelNameString, L"N/A")); + + PhClearReference(&tokenTrustLevelNameString); + PhClearReference(&tokenTrustLevelSidString); + } + + //PTOKEN_GROUPS tokenLogonGroups; + //if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenLogonSid, &tokenLogonGroups))) + //{ + // PPH_STRING tokenLogonName = PhGetSidFullName(tokenLogonGroups->Groups[0].Sid, TRUE, NULL); + // PPH_STRING tokenLogonSid = PhSidToStringSid(tokenLogonGroups->Groups[0].Sid); + // INT tokenLogonGroupIndex = PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"Logon"); + // INT tokenLogonNameIndex = PhAddListViewGroupItem(context->ListViewHandle, tokenLogonGroupIndex, MAXINT, L"Token logon SID", NULL); + // INT tokenLogonSidIndex = PhAddListViewGroupItem(context->ListViewHandle, tokenLogonGroupIndex, MAXINT, L"Token logon Name", NULL); + // PhSetListViewSubItem(context->ListViewHandle, tokenLogonNameIndex, 1, PhGetStringOrDefault(tokenLogonName, L"Unknown")); + // PhSetListViewSubItem(context->ListViewHandle, tokenLogonSidIndex, 1, PhGetStringOrDefault(tokenLogonSid, L"Unknown")); + // PhFree(tokenLogonGroups); + //} + + if (tokenProfilePathString = PhpGetTokenFolderPath(tokenHandle)) + { + INT profileGroupIndex; + INT profileFolderIndex; + INT profileRegistryIndex; + + profileGroupIndex = PhAddListViewGroup(context->ListViewHandle, listViewGroupIndex++, L"Profile"); + profileFolderIndex = PhAddListViewGroupItem(context->ListViewHandle, profileGroupIndex, MAXINT, L"Folder path", NULL); + profileRegistryIndex = PhAddListViewGroupItem(context->ListViewHandle, profileGroupIndex, MAXINT, L"Registry path", NULL); + + PhSetListViewSubItem(context->ListViewHandle, profileFolderIndex, 1, PhGetStringOrDefault(tokenProfilePathString, L"N/A")); + + if (tokenProfileRegistryString = PhpGetTokenRegistryPath(tokenHandle)) + { + PhSetListViewSubItem(context->ListViewHandle, profileRegistryIndex, 1, PhGetStringOrDefault(tokenProfileRegistryString, L"N/A")); + PhDereferenceObject(tokenProfileRegistryString); + } + + PhDereferenceObject(tokenProfilePathString); + } + + NtClose(tokenHandle); + } + + PhSetListViewSubItem(context->ListViewHandle, 0, 1, tokenType); + PhSetListViewSubItem(context->ListViewHandle, 1, 1, tokenImpersonationLevel); + PhSetListViewSubItem(context->ListViewHandle, 2, 1, tokenLuid); + PhSetListViewSubItem(context->ListViewHandle, 3, 1, authenticationLuid); + PhSetListViewSubItem(context->ListViewHandle, 4, 1, tokenModifiedLuid); + PhSetListViewSubItem(context->ListViewHandle, 5, 1, tokenOriginLogonSession); + PhSetListViewSubItem(context->ListViewHandle, 6, 1, PhGetStringOrDefault(memoryUsed, L"Unknown")); + PhSetListViewSubItem(context->ListViewHandle, 7, 1, PhGetStringOrDefault(memoryAvailable, L"Unknown")); + + PhSetListViewSubItem(context->ListViewHandle, 8, 1, PhGetStringOrDefault(tokenNamedObjectPathString, L"Unknown")); + PhSetListViewSubItem(context->ListViewHandle, 9, 1, PhGetStringOrDefault(tokenSecurityDescriptorString, L"Unknown")); + + PhClearReference(&memoryUsed); + PhClearReference(&memoryAvailable); + PhClearReference(&tokenNamedObjectPathString); + PhClearReference(&tokenSecurityDescriptorString); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhDeleteLayoutManager(&context->LayoutManager); + PhFree(context); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID* listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} + +BOOLEAN NTAPI PhpAttributeTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PATTRIBUTE_TREE_CONTEXT context; + PATTRIBUTE_NODE node; + + context = Context; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + node = (PATTRIBUTE_NODE)getChildren->Node; + + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)context->RootList->Items; + getChildren->NumberOfChildren = context->RootList->Count; + } + else + { + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + node = (PATTRIBUTE_NODE)isLeaf->Node; + + isLeaf->IsLeaf = node->Children->Count == 0; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + + node = (PATTRIBUTE_NODE)getCellText->Node; + + if (getCellText->Id == 0) + getCellText->Text = PhGetStringRef(node->Text); + else + return FALSE; + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + { + PPH_STRING text; + + text = PhGetTreeNewText(hwnd, 0); + PhSetClipboardString(hwnd, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + SendMessage( + context->WindowHandle, + WM_CONTEXTMENU, + 0, + (LPARAM)contextMenuEvent + ); + } + return TRUE; + } + + return FALSE; +} + +PATTRIBUTE_NODE PhpAddAttributeNode( + _In_ PATTRIBUTE_TREE_CONTEXT Context, + _In_opt_ PATTRIBUTE_NODE Parent, + _In_opt_ _Assume_refs_(1) PPH_STRING Text + ) +{ + PATTRIBUTE_NODE node; + + node = PhAllocate(sizeof(ATTRIBUTE_NODE)); + memset(node, 0, sizeof(ATTRIBUTE_NODE)); + PhInitializeTreeNewNode(&node->Node); + + node->Children = PhCreateList(2); + + PhAddItemList(Context->NodeList, node); + + if (Parent) + PhAddItemList(Parent->Children, node); + else + PhAddItemList(Context->RootList, node); + + PhMoveReference(&node->Text, Text); + + return node; +} + +VOID PhpDestroyAttributeNode( + _In_ PATTRIBUTE_NODE Node + ) +{ + PhDereferenceObject(Node->Children); + PhClearReference(&Node->Text); + PhFree(Node); +} + +VOID PhpRemoveAttributeNode( + _In_ PATTRIBUTE_TREE_CONTEXT Context, + _In_ PATTRIBUTE_NODE Node + ) +{ + ULONG index; + + if ((index = PhFindItemList(Context->NodeList, Node)) != -1) + PhRemoveItemList(Context->NodeList, index); + if ((index = PhFindItemList(Context->RootList, Node)) != -1) + PhRemoveItemList(Context->RootList, index); + + PhpDestroyAttributeNode(Node); +} + +VOID PhpGetSelectedAttributeTreeNodes( + _Inout_ PATTRIBUTE_TREE_CONTEXT Context, + _Out_ PATTRIBUTE_NODE **AttributeNodes, + _Out_ PULONG NumberOfAttributeNodes + ) +{ + PPH_LIST list; + + list = PhCreateList(2); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PATTRIBUTE_NODE node = (PATTRIBUTE_NODE)Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *AttributeNodes = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfAttributeNodes = list->Count; + + PhDereferenceObject(list); +} + +VOID PhpInitializeAttributeTreeContext( + _Out_ PATTRIBUTE_TREE_CONTEXT Context, + _In_ HWND WindowHandle, + _In_ HWND TreeNewHandle + ) +{ + Context->WindowHandle = WindowHandle; + Context->NodeList = PhCreateList(10); + Context->RootList = PhCreateList(10); + + PhSetControlTheme(TreeNewHandle, L"explorer"); + TreeNew_SetCallback(TreeNewHandle, PhpAttributeTreeNewCallback, Context); + //TreeNew_GetViewParts(TreeNewHandle, &parts); // column width = (parts.ClientRect.right - parts.VScrollWidth) // TODO: VScrollWidth not set during INITDIALOG. (dmex) + PhAddTreeNewColumnEx2(TreeNewHandle, 0, TRUE, L"Attributes", 200, PH_ALIGN_LEFT, 0, 0, TN_COLUMN_FLAG_NODPISCALEONADD); +} + +VOID PhpDeleteAttributeTreeContext( + _Inout_ PATTRIBUTE_TREE_CONTEXT Context + ) +{ + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + PhpDestroyAttributeNode(Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeList); + PhDereferenceObject(Context->RootList); +} + +//static COLORREF NTAPI PhpTokenCapabilitiesColorFunction( +// _In_ INT Index, +// _In_ PVOID Param, +// _In_opt_ PVOID Context +// ) +//{ +// PSID_AND_ATTRIBUTES sidAndAttributes = Param; +// +// return PhGetGroupAttributesColor(sidAndAttributes->Attributes); +//} + +BOOLEAN PhpAddTokenCapabilities( + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND tnHandle + ) +{ + HANDLE tokenHandle; + ULONG i; + + if (!NT_SUCCESS(TokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + TokenPageContext->Context + ))) + return FALSE; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenCapabilities, &TokenPageContext->Capabilities))) + { + for (i = 0; i < TokenPageContext->Capabilities->GroupCount; i++) + { + PATTRIBUTE_NODE node; + PPH_STRING name; + ULONG subAuthoritiesCount; + ULONG subAuthority; + + name = PhSidToStringSid(TokenPageContext->Capabilities->Groups[i].Sid); + node = PhpAddAttributeNode(&TokenPageContext->CapsTreeContext, NULL, name); + + if (name = PhGetSidFullName(TokenPageContext->Capabilities->Groups[i].Sid, TRUE, NULL)) + { + PhpAddAttributeNode(&TokenPageContext->CapsTreeContext, node, PhFormatString(L"FullName: %s", PhGetString(name))); + PhDereferenceObject(name); + } + + if (name = PhGetCapabilitySidName(TokenPageContext->Capabilities->Groups[i].Sid)) + { + PhpAddAttributeNode(&TokenPageContext->CapsTreeContext, node, PhFormatString(L"Capability: %s", PhGetString(name))); + PhDereferenceObject(name); + } + + subAuthoritiesCount = *RtlSubAuthorityCountSid(TokenPageContext->Capabilities->Groups[i].Sid); + subAuthority = *RtlSubAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid, 0); + + // RtlIdentifierAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid) == (BYTE[])SECURITY_APP_PACKAGE_AUTHORITY + if (subAuthority == SECURITY_CAPABILITY_BASE_RID) + { + if (subAuthoritiesCount == SECURITY_APP_PACKAGE_RID_COUNT) + { + PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; + + //if (*RtlSubAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid, 1) == SECURITY_CAPABILITY_APP_RID) + // continue; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) + { + if (appContainerInfo->TokenAppContainer) + { + if (PhIsPackageCapabilitySid(appContainerInfo->TokenAppContainer, TokenPageContext->Capabilities->Groups[i].Sid)) + { + HANDLE processHandle; + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, TokenPageContext->Context))) + { + name = PhGetProcessPackageFullName(processHandle); + PhpAddAttributeNode(&TokenPageContext->CapsTreeContext, node, PhFormatString(L"Package: %s", PhGetString(name))); + PhDereferenceObject(name); + + NtClose(processHandle); + } + } + } + + PhFree(appContainerInfo); + } + } + else if (subAuthoritiesCount == SECURITY_CAPABILITY_RID_COUNT) + { + PPH_STRING capabilityName; + union + { + GUID Guid; + struct + { + ULONG Data1; + ULONG Data2; + ULONG Data3; + ULONG Data4; + }; + } capabilityGuid; + + capabilityGuid.Data1 = *RtlSubAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid, 1); + capabilityGuid.Data2 = *RtlSubAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid, 2); + capabilityGuid.Data3 = *RtlSubAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid, 3); + capabilityGuid.Data4 = *RtlSubAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid, 4); + + if (name = PhFormatGuid(&capabilityGuid.Guid)) + { + PhpAddAttributeNode(&TokenPageContext->CapsTreeContext, node, PhFormatString(L"Guid: %s", PhGetString(name))); + + if (capabilityName = PhGetCapabilityGuidName(name)) + { + PhpAddAttributeNode(&TokenPageContext->CapsTreeContext, node, PhFormatString(L"Capability: %s", PhGetString(capabilityName))); + PhDereferenceObject(capabilityName); + } + + PhDereferenceObject(name); + } + } + } + } + } + + NtClose(tokenHandle); + + return TRUE; +} + +INT_PTR CALLBACK PhpTokenCapabilitiesPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + HWND tnHandle; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + tnHandle = GetDlgItem(hwndDlg, IDC_LIST); + + switch (uMsg) + { + case WM_INITDIALOG: + { + tokenPageContext->CapsTreeContext.WindowHandle = hwndDlg; + tokenPageContext->CapsTreeContext.NodeList = PhCreateList(10); + tokenPageContext->CapsTreeContext.RootList = PhCreateList(10); + + PhSetControlTheme(tnHandle, L"explorer"); + TreeNew_SetCallback(tnHandle, PhpAttributeTreeNewCallback, &tokenPageContext->CapsTreeContext); + PhAddTreeNewColumnEx2(tnHandle, 0, TRUE, L"Capabilities", 200, PH_ALIGN_LEFT, 0, 0, TN_COLUMN_FLAG_NODPISCALEONADD); + + TreeNew_SetEmptyText(tnHandle, &PhpEmptyTokenCapabilitiesText, 0); + TreeNew_SetRedraw(tnHandle, FALSE); + PhpAddTokenCapabilities(tokenPageContext, tnHandle); + TreeNew_NodesStructured(tnHandle); + TreeNew_SetRedraw(tnHandle, TRUE); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhpDeleteAttributeTreeContext(&tokenPageContext->CapsTreeContext); + + if (tokenPageContext->Capabilities) + { + PhFree(tokenPageContext->Capabilities); + tokenPageContext->Capabilities = NULL; + } + } + break; + case WM_SHOWWINDOW: + { + TreeNew_AutoSizeColumn(tnHandle, 0, TN_AUTOSIZE_REMAINING_SPACE); + } + break; + case WM_CONTEXTMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + PATTRIBUTE_NODE *attributeObjectNodes = NULL; + ULONG numberOfAttributeObjectNodes = 0; + + PhpGetSelectedAttributeTreeNodes(&tokenPageContext->CapsTreeContext, &attributeObjectNodes, &numberOfAttributeObjectNodes); + + if (numberOfAttributeObjectNodes != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, IDC_COPY, tnHandle, contextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + if (!PhHandleCopyCellEMenuItem(selectedItem)) + { + switch (selectedItem->Id) + { + case IDC_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(tnHandle, 0); + PhSetClipboardString(tnHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(attributeObjectNodes); + } + break; + } + + return FALSE; +} + +PWSTR PhGetSecurityAttributeTypeString( + _In_ USHORT Type + ) +{ + // These types are shared between CLAIM_* and TOKEN_* security attributes. + + switch (Type) + { + case TOKEN_SECURITY_ATTRIBUTE_TYPE_INVALID: + return L"Invalid"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_INT64: + return L"Int64"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_UINT64: + return L"UInt64"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING: + return L"String"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_FQBN: + return L"FQBN"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_SID: + return L"SID"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: + return L"Boolean"; + case TOKEN_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: + return L"Octet string"; + default: + return L"(Unknown)"; + } +} + +PPH_STRING PhGetSecurityAttributeFlagsString( + _In_ ULONG Flags + ) +{ + PH_STRING_BUILDER sb; + + // These flags are shared between CLAIM_* and TOKEN_* security attributes. + + PhInitializeStringBuilder(&sb, 100); + + if (Flags & TOKEN_SECURITY_ATTRIBUTE_MANDATORY) + PhAppendStringBuilder2(&sb, L"Mandatory, "); + if (Flags & TOKEN_SECURITY_ATTRIBUTE_DISABLED) + PhAppendStringBuilder2(&sb, L"Disabled, "); + if (Flags & TOKEN_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT) + PhAppendStringBuilder2(&sb, L"Default disabled, "); + if (Flags & TOKEN_SECURITY_ATTRIBUTE_USE_FOR_DENY_ONLY) + PhAppendStringBuilder2(&sb, L"Use for deny only, "); + if (Flags & TOKEN_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE) + PhAppendStringBuilder2(&sb, L"Case-sensitive, "); + if (Flags & TOKEN_SECURITY_ATTRIBUTE_NON_INHERITABLE) + PhAppendStringBuilder2(&sb, L"Non-inheritable, "); + if (Flags & TOKEN_SECURITY_ATTRIBUTE_COMPARE_IGNORE) + PhAppendStringBuilder2(&sb, L"Compare-ignore, "); + + if (sb.String->Length != 0) + PhRemoveEndStringBuilder(&sb, 2); + else + PhAppendStringBuilder2(&sb, L"(None)"); + + return PhFinalStringBuilderString(&sb); +} + +PPH_STRING PhFormatClaimSecurityAttributeValue( + _In_ PCLAIM_SECURITY_ATTRIBUTE_V1 Attribute, + _In_ ULONG ValueIndex + ) +{ + PH_FORMAT format; + + switch (Attribute->ValueType) + { + case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64: + PhInitFormatI64D(&format, Attribute->Values.pInt64[ValueIndex]); + return PhFormat(&format, 1, 0); + case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64: + PhInitFormatI64U(&format, Attribute->Values.pUint64[ValueIndex]); + return PhFormat(&format, 1, 0); + case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING: + return PhCreateString(Attribute->Values.ppString[ValueIndex]); + case CLAIM_SECURITY_ATTRIBUTE_TYPE_FQBN: + return PhFormatString(L"Version %I64u: %s", + Attribute->Values.pFqbn[ValueIndex].Version, + Attribute->Values.pFqbn[ValueIndex].Name); + case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID: + { + if (RtlValidSid(Attribute->Values.pOctetString[ValueIndex].pValue)) + { + PPH_STRING name; + + name = PhGetSidFullName(Attribute->Values.pOctetString[ValueIndex].pValue, TRUE, NULL); + + if (name) + return name; + + name = PhSidToStringSid(Attribute->Values.pOctetString[ValueIndex].pValue); + + if (name) + return name; + } + } + return PhCreateString(L"(Invalid SID)"); + case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: + return PhCreateString(Attribute->Values.pInt64[ValueIndex] != 0 ? L"True" : L"False"); + case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: + return PhCreateString(L"(Octet string)"); + default: + return PhCreateString(L"(Unknown)"); + } +} + +PPH_STRING PhFormatTokenSecurityAttributeValue( + _In_ PTOKEN_SECURITY_ATTRIBUTE_V1 Attribute, + _In_ ULONG ValueIndex + ) +{ + PH_FORMAT format; + + switch (Attribute->ValueType) + { + case TOKEN_SECURITY_ATTRIBUTE_TYPE_INT64: + PhInitFormatI64D(&format, Attribute->Values.pInt64[ValueIndex]); + return PhFormat(&format, 1, 0); + case TOKEN_SECURITY_ATTRIBUTE_TYPE_UINT64: + PhInitFormatI64U(&format, Attribute->Values.pUint64[ValueIndex]); + return PhFormat(&format, 1, 0); + case TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING: + return PhCreateStringFromUnicodeString(&Attribute->Values.pString[ValueIndex]); + case TOKEN_SECURITY_ATTRIBUTE_TYPE_FQBN: + return PhFormatString(L"Version %I64u: %.*s", + Attribute->Values.pFqbn[ValueIndex].Version, + Attribute->Values.pFqbn[ValueIndex].Name.Length / sizeof(WCHAR), + Attribute->Values.pFqbn[ValueIndex].Name.Buffer); + case TOKEN_SECURITY_ATTRIBUTE_TYPE_SID: + { + if (RtlValidSid(Attribute->Values.pOctetString[ValueIndex].pValue)) + { + PPH_STRING name; + + name = PhGetSidFullName(Attribute->Values.pOctetString[ValueIndex].pValue, TRUE, NULL); + + if (name) + return name; + + name = PhSidToStringSid(Attribute->Values.pOctetString[ValueIndex].pValue); + + if (name) + return name; + } + } + return PhCreateString(L"(Invalid SID)"); + case TOKEN_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: + return PhCreateString(Attribute->Values.pInt64[ValueIndex] != 0 ? L"True" : L"False"); + case TOKEN_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: + return PhCreateString(L"(Octet string)"); + default: + return PhCreateString(L"(Unknown)"); + } +} + +BOOLEAN PhpAddTokenClaimAttributes( + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND tnHandle, + _In_ BOOLEAN DeviceClaims, + _In_ PATTRIBUTE_NODE Parent + ) +{ + HANDLE tokenHandle; + PCLAIM_SECURITY_ATTRIBUTES_INFORMATION info; + ULONG i; + ULONG j; + + if (!NT_SUCCESS(TokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + TokenPageContext->Context // ProcessId + ))) + return FALSE; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, DeviceClaims ? TokenDeviceClaimAttributes : TokenUserClaimAttributes, &info))) + { + for (i = 0; i < info->AttributeCount; i++) + { + PCLAIM_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; + PATTRIBUTE_NODE node; + PPH_STRING temp; + + // Attribute + node = PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, Parent, PhCreateString(attribute->Name)); + // Type + PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, node, + PhFormatString(L"Type: %s", PhGetSecurityAttributeTypeString(attribute->ValueType))); + // Flags + temp = PhGetSecurityAttributeFlagsString(attribute->Flags); + PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, node, + PhFormatString(L"Flags: %s (0x%lx)", temp->Buffer, attribute->Flags)); + PhDereferenceObject(temp); + + // Values + for (j = 0; j < attribute->ValueCount; j++) + { + temp = PhFormatClaimSecurityAttributeValue(attribute, j); + PhpAddAttributeNode(&TokenPageContext->ClaimsTreeContext, node, + PhFormatString(L"Value %u: %s", j, temp->Buffer)); + PhDereferenceObject(temp); + } + } + + PhFree(info); + } + + NtClose(tokenHandle); + + return TRUE; +} + +INT_PTR CALLBACK PhpTokenClaimsPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + HWND tnHandle; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + tnHandle = GetDlgItem(hwndDlg, IDC_LIST); + + switch (uMsg) + { + case WM_INITDIALOG: + { + PATTRIBUTE_NODE userNode; + PATTRIBUTE_NODE deviceNode; + + PhpInitializeAttributeTreeContext(&tokenPageContext->ClaimsTreeContext, hwndDlg, tnHandle); + + TreeNew_SetEmptyText(tnHandle, &PhpEmptyTokenClaimsText, 0); + TreeNew_SetRedraw(tnHandle, FALSE); + + userNode = PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, NULL, PhCreateString(L"User claims")); + deviceNode = PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, NULL, PhCreateString(L"Device claims")); + + PhpAddTokenClaimAttributes(tokenPageContext, tnHandle, FALSE, userNode); + PhpAddTokenClaimAttributes(tokenPageContext, tnHandle, TRUE, deviceNode); + + if (userNode->Children->Count == 0 && deviceNode->Children->Count == 0) + { + PhpRemoveAttributeNode(&tokenPageContext->ClaimsTreeContext, userNode); + PhpRemoveAttributeNode(&tokenPageContext->ClaimsTreeContext, deviceNode); + } + else + { + if (userNode->Children->Count == 0) + PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, userNode, PhCreateString(L"(None)")); + if (deviceNode->Children->Count == 0) + PhpAddAttributeNode(&tokenPageContext->ClaimsTreeContext, deviceNode, PhCreateString(L"(None)")); + } + + TreeNew_NodesStructured(tnHandle); + TreeNew_SetRedraw(tnHandle, TRUE); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhpDeleteAttributeTreeContext(&tokenPageContext->ClaimsTreeContext); + } + break; + case WM_SHOWWINDOW: + { + TreeNew_AutoSizeColumn(tnHandle, 0, TN_AUTOSIZE_REMAINING_SPACE); + } + break; + case WM_CONTEXTMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + PATTRIBUTE_NODE *attributeObjectNodes = NULL; + ULONG numberOfAttributeObjectNodes = 0; + + PhpGetSelectedAttributeTreeNodes(&tokenPageContext->ClaimsTreeContext, &attributeObjectNodes, &numberOfAttributeObjectNodes); + + if (numberOfAttributeObjectNodes != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, IDC_COPY, tnHandle, contextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + if (!PhHandleCopyCellEMenuItem(selectedItem)) + { + switch (selectedItem->Id) + { + case IDC_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(tnHandle, 0); + PhSetClipboardString(tnHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(attributeObjectNodes); + } + break; + } + + return FALSE; +} + +BOOLEAN PhpAddTokenAttributes( + _In_ PTOKEN_PAGE_CONTEXT TokenPageContext, + _In_ HWND tnHandle + ) +{ + HANDLE tokenHandle; + PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; + ULONG i; + ULONG j; + + if (!NT_SUCCESS(TokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + TokenPageContext->Context // ProcessId + ))) + return FALSE; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenSecurityAttributes, &info))) + { + for (i = 0; i < info->AttributeCount; i++) + { + PTOKEN_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; + PATTRIBUTE_NODE node; + PPH_STRING temp; + + // Attribute + node = PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, NULL, + PhCreateStringFromUnicodeString(&attribute->Name)); + // Type + PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, node, + PhFormatString(L"Type: %s", PhGetSecurityAttributeTypeString(attribute->ValueType))); + // Flags + temp = PhGetSecurityAttributeFlagsString(attribute->Flags); + PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, node, + PhFormatString(L"Flags: %s (0x%lx)", temp->Buffer, attribute->Flags)); + PhDereferenceObject(temp); + + // Values + for (j = 0; j < attribute->ValueCount; j++) + { + temp = PhFormatTokenSecurityAttributeValue(attribute, j); + PhpAddAttributeNode(&TokenPageContext->AuthzTreeContext, node, + PhFormatString(L"Value %u: %s", j, temp->Buffer)); + PhDereferenceObject(temp); + } + } + + PhFree(info); + } + + NtClose(tokenHandle); + + return TRUE; +} + +INT_PTR CALLBACK PhpTokenAttributesPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + HWND tnHandle; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + tnHandle = GetDlgItem(hwndDlg, IDC_LIST); + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhpInitializeAttributeTreeContext(&tokenPageContext->AuthzTreeContext, hwndDlg, tnHandle); + + TreeNew_SetEmptyText(tnHandle, &PhpEmptyTokenAttributesText, 0); + TreeNew_SetRedraw(tnHandle, FALSE); + + PhpAddTokenAttributes(tokenPageContext, tnHandle); + + //if (tokenPageContext->AuthzTreeContext.RootList->Count == 0) + // PhpAddAttributeNode(&tokenPageContext->AuthzTreeContext, NULL, PhCreateString(L"(None)")); + + TreeNew_NodesStructured(tnHandle); + TreeNew_SetRedraw(tnHandle, TRUE); + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhpDeleteAttributeTreeContext(&tokenPageContext->AuthzTreeContext); + } + break; + case WM_SHOWWINDOW: + { + TreeNew_AutoSizeColumn(tnHandle, 0, TN_AUTOSIZE_REMAINING_SPACE); + } + break; + case WM_CONTEXTMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + PATTRIBUTE_NODE *attributeObjectNodes = NULL; + ULONG numberOfAttributeObjectNodes = 0; + + PhpGetSelectedAttributeTreeNodes(&tokenPageContext->AuthzTreeContext, &attributeObjectNodes, &numberOfAttributeObjectNodes); + + if (numberOfAttributeObjectNodes != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, IDC_COPY, tnHandle, contextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + if (!PhHandleCopyCellEMenuItem(selectedItem)) + { + switch (selectedItem->Id) + { + case IDC_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(tnHandle, 0); + PhSetClipboardString(tnHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(attributeObjectNodes); + } + break; + } + + return FALSE; +} + +// rev from GetUserProfileDirectory (dmex) +PPH_STRING PhpGetTokenFolderPath( + _In_ HANDLE TokenHandle + ) +{ + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\"); + PPH_STRING profileFolderPath = NULL; + PPH_STRING profileKeyPath = NULL; + PPH_STRING tokenUserSid; + PTOKEN_USER tokenUser; + + if (NT_SUCCESS(PhGetTokenUser(TokenHandle, &tokenUser))) + { + if (tokenUserSid = PhSidToStringSid(tokenUser->User.Sid)) + { + profileKeyPath = PhConcatStringRef2(&servicesKeyName, &tokenUserSid->sr); + PhDereferenceObject(tokenUserSid); + } + + PhFree(tokenUser); + } + + if (profileKeyPath) + { + HANDLE keyHandle; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &profileKeyPath->sr, + 0 + ))) + { + PPH_STRING profileImagePath; + + if (profileFolderPath = PhQueryRegistryString(keyHandle, L"ProfileImagePath")) + { + if (profileImagePath = PhExpandEnvironmentStrings(&profileFolderPath->sr)) + { + PhMoveReference(&profileFolderPath, profileImagePath); + } + } + + NtClose(keyHandle); + } + + PhDereferenceObject(profileKeyPath); + } + + //ULONG profileFolderLength; + //if (GetUserProfileDirectory) + //{ + // GetUserProfileDirectory(TokenHandle, NULL, &profileFolderLength); + // profileFolderPath = PhCreateStringEx(NULL, profileFolderLength * sizeof(WCHAR)); + // GetUserProfileDirectory(TokenHandle, profileFolderPath->Buffer, &profileFolderLength); + //} + + return profileFolderPath; +} + +PPH_STRING PhpGetTokenRegistryPath( + _In_ HANDLE TokenHandle + ) +{ + PPH_STRING profileRegistryPath = NULL; + PPH_STRING tokenUserSid = NULL; + PTOKEN_USER tokenUser; + + if (NT_SUCCESS(PhGetTokenUser(TokenHandle, &tokenUser))) + { + tokenUserSid = PhSidToStringSid(tokenUser->User.Sid); + PhFree(tokenUser); + } + + if (tokenUserSid) + { + NTSTATUS status; + HANDLE keyHandle = NULL; + + status = PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_USERS, + &tokenUserSid->sr, + 0 + ); + + if (NT_SUCCESS(status) || status == STATUS_ACCESS_DENIED) + { + profileRegistryPath = PhConcatStrings2(L"HKU\\", tokenUserSid->Buffer); + } + + if (keyHandle) + NtClose(keyHandle); + + PhDereferenceObject(tokenUserSid); + } + + return profileRegistryPath; +} + +PPH_STRING PhpGetTokenAppContainerFolderPath( + _In_ PSID TokenAppContainerSid + ) +{ + PPH_STRING appContainerFolderPath = NULL; + PPH_STRING appContainerSid; + PWSTR folderPath; + + appContainerSid = PhSidToStringSid(TokenAppContainerSid); + + if (GetAppContainerFolderPath_Import()) + { + if (SUCCEEDED(GetAppContainerFolderPath_Import()(appContainerSid->Buffer, &folderPath))) + { + appContainerFolderPath = PhCreateString(folderPath); + CoTaskMemFree(folderPath); + } + } + + PhDereferenceObject(appContainerSid); + + return appContainerFolderPath; +} + +PPH_STRING PhpGetTokenAppContainerRegistryPath( + _In_ HANDLE TokenHandle + ) +{ + PPH_STRING appContainerRegistryPath = NULL; + HKEY registryHandle = NULL; + + if (NT_SUCCESS(PhImpersonateToken(NtCurrentThread(), TokenHandle))) + { + if (GetAppContainerRegistryLocation_Import()) + GetAppContainerRegistryLocation_Import()(KEY_READ, ®istryHandle); + + PhRevertImpersonationToken(NtCurrentThread()); + } + + if (registryHandle) + { + PhGetHandleInformation( + NtCurrentProcess(), + registryHandle, + ULONG_MAX, + NULL, + NULL, + NULL, + &appContainerRegistryPath + ); + + NtClose(registryHandle); + } + + return appContainerRegistryPath; +} + +INT_PTR CALLBACK PhpTokenContainerPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PTOKEN_PAGE_CONTEXT tokenPageContext; + PPHP_TOKEN_ADVANCED_CONTEXT context; + + tokenPageContext = PhpTokenPageHeader(hwndDlg, uMsg, wParam, lParam); + + if (!tokenPageContext) + return FALSE; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocateZero(sizeof(PHP_TOKEN_ADVANCED_CONTEXT)); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE tokenHandle; + BOOLEAN isLessPrivilegedAppContainer = FALSE; + WCHAR appContainerNumberString[PH_INT64_STR_LEN_1] = L"Unknown"; + PPH_STRING tokenNamedObjectPathString = NULL; + HANDLE processHandle; + + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Name"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 280, L"Value"); + PhSetExtendedListView(context->ListViewHandle); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + + ListView_EnableGroupView(context->ListViewHandle, TRUE); + PhAddListViewGroup(context->ListViewHandle, 0, L"General"); + PhAddListViewGroup(context->ListViewHandle, 1, L"Properties"); + PhAddListViewGroup(context->ListViewHandle, 2, L"Parent"); + PhAddListViewGroup(context->ListViewHandle, 3, L"Package"); + PhAddListViewGroup(context->ListViewHandle, 4, L"Profile"); + + PhAddListViewGroupItem(context->ListViewHandle, 0, MAXINT, L"Name", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 0, MAXINT, L"Type", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 0, MAXINT, L"SID", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"Number", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"LPAC", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 1, MAXINT, L"Token object path", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 2, MAXINT, L"Name", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 2, MAXINT, L"SID", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 3, MAXINT, L"Name", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 3, MAXINT, L"Path", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 4, MAXINT, L"Folder path", NULL); + PhAddListViewGroupItem(context->ListViewHandle, 4, MAXINT, L"Registry path", NULL); + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY, + tokenPageContext->Context // ProcessId + ))) + { + PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; + APPCONTAINER_SID_TYPE appContainerSidType = InvalidAppContainerSidType; + PSID appContainerSidParent = NULL; + PPH_STRING appContainerName = NULL; + PPH_STRING appContainerSid = NULL; + ULONG appContainerNumber; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) + { + if (appContainerInfo->TokenAppContainer) + { + if (RtlGetAppContainerSidType_Import()) + RtlGetAppContainerSidType_Import()(appContainerInfo->TokenAppContainer, &appContainerSidType); + if (RtlGetAppContainerParent_Import()) + RtlGetAppContainerParent_Import()(appContainerInfo->TokenAppContainer, &appContainerSidParent); + + appContainerName = PhGetAppContainerName(appContainerInfo->TokenAppContainer); + appContainerSid = PhSidToStringSid(appContainerInfo->TokenAppContainer); + } + + PhFree(appContainerInfo); + } + + if (appContainerName) + { + PhSetListViewSubItem(context->ListViewHandle, 0, 1, appContainerName->Buffer); + PhDereferenceObject(appContainerName); + } + + switch (appContainerSidType) + { + case ChildAppContainerSidType: + PhSetListViewSubItem(context->ListViewHandle, 1, 1, L"Child"); + break; + case ParentAppContainerSidType: + PhSetListViewSubItem(context->ListViewHandle, 1, 1, L"Parent"); + break; + default: + PhSetListViewSubItem(context->ListViewHandle, 1, 1, L"Unknown"); + break; + } + + if (appContainerSid) + { + PhSetListViewSubItem(context->ListViewHandle, 2, 1, appContainerSid->Buffer); + PhDereferenceObject(appContainerSid); + } + + if (NT_SUCCESS(PhGetTokenAppContainerNumber(tokenHandle, &appContainerNumber))) + { + PhPrintUInt32(appContainerNumberString, appContainerNumber); + PhSetListViewSubItem(context->ListViewHandle, 3, 1, appContainerNumberString); + } + + // TODO: TokenIsLessPrivilegedAppContainer + { + static UNICODE_STRING attributeNameUs = RTL_CONSTANT_STRING(L"WIN://NOALLAPPPKG"); + PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenSecurityAttributes, &info))) + { + for (ULONG i = 0; i < info->AttributeCount; i++) + { + PTOKEN_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; + + if (RtlEqualUnicodeString(&attribute->Name, &attributeNameUs, FALSE)) + { + if (attribute->ValueType == TOKEN_SECURITY_ATTRIBUTE_TYPE_UINT64) + { + isLessPrivilegedAppContainer = TRUE; // (*attribute->Values.pUint64 == 1); + break; + } + } + } + + PhFree(info); + } + + PhSetListViewSubItem(context->ListViewHandle, 4, 1, isLessPrivilegedAppContainer ? L"True" : L"False"); + } + + if (NT_SUCCESS(PhGetAppContainerNamedObjectPath(tokenHandle, NULL, FALSE, &tokenNamedObjectPathString))) + { + PhSetListViewSubItem(context->ListViewHandle, 5, 1, PhGetStringOrDefault(tokenNamedObjectPathString, L"Unknown")); + PhDereferenceObject(tokenNamedObjectPathString); + } + + if (appContainerSidParent) + { + if (appContainerName = PhGetAppContainerName(appContainerSidParent)) + { + PhSetListViewSubItem(context->ListViewHandle, 6, 1, appContainerName->Buffer); + PhDereferenceObject(appContainerName); + } + + if (appContainerSid = PhSidToStringSid(appContainerSidParent)) + { + PhSetListViewSubItem(context->ListViewHandle, 7, 1, appContainerSid->Buffer); + PhDereferenceObject(appContainerSid); + } + + RtlFreeSid(appContainerSidParent); + } + + NtClose(tokenHandle); + } + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + tokenPageContext->Context // ProcessId + ))) + { + PPH_STRING packageFullName; + PPH_STRING packagePath; + + if (packageFullName = PhGetProcessPackageFullName(processHandle)) + { + PhSetListViewSubItem(context->ListViewHandle, 8, 1, packageFullName->Buffer); + + if (packagePath = PhGetPackagePath(packageFullName)) + { + PhSetListViewSubItem(context->ListViewHandle, 9, 1, packagePath->Buffer); + PhDereferenceObject(packagePath); + } + + PhDereferenceObject(packageFullName); + } + + NtClose(processHandle); + } + + if (NT_SUCCESS(tokenPageContext->OpenObject( + &tokenHandle, + TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, + tokenPageContext->Context // ProcessId + ))) + { + PTOKEN_APPCONTAINER_INFORMATION appContainerInfo; + PPH_STRING appContainerFolderPath = NULL; + PPH_STRING appContainerRegistryPath = NULL; + + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) + { + if (appContainerInfo->TokenAppContainer) + { + appContainerFolderPath = PhpGetTokenAppContainerFolderPath(appContainerInfo->TokenAppContainer); + } + + PhFree(appContainerInfo); + } + + if (appContainerFolderPath) + { + PhSetListViewSubItem(context->ListViewHandle, 10, 1, appContainerFolderPath->Buffer); + PhDereferenceObject(appContainerFolderPath); + } + + if (appContainerRegistryPath = PhpGetTokenAppContainerRegistryPath(tokenHandle)) + { + PhSetListViewSubItem(context->ListViewHandle, 11, 1, appContainerRegistryPath->Buffer); + PhDereferenceObject(appContainerRegistryPath); + } + + NtClose(tokenHandle); + } + + PhInitializeWindowTheme(hwndDlg, PhEnableThemeSupport); + } + break; + case WM_DESTROY: + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + PhDeleteLayoutManager(&context->LayoutManager); + PhFree(context); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID* listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} diff --git a/ProcessHacker/version.rc b/ProcessHacker/version.rc index 719f3d2ab572..d9685eada357 100644 --- a/ProcessHacker/version.rc +++ b/ProcessHacker/version.rc @@ -1,35 +1,101 @@ -#include "winres.h" -#include "include/phappres.h" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION PHAPP_VERSION_NUMBER - PRODUCTVERSION PHAPP_VERSION_NUMBER - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "Process Hacker" - VALUE "FileVersion", PHAPP_VERSION_STRING - VALUE "InternalName", "Process Hacker" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ProcessHacker.exe" - VALUE "ProductName", "Process Hacker" - VALUE "ProductVersion", PHAPP_VERSION_STRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" +#include "include/phappres.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "#include ""include/phappres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PHAPP_VERSION_NUMBER + PRODUCTVERSION PHAPP_VERSION_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "Process Hacker" + VALUE "FileDescription", "Process Hacker" + VALUE "FileVersion", PHAPP_VERSION_STRING + VALUE "InternalName", "ProcessHacker.exe" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3. Copyright (C) 2017" + VALUE "OriginalFilename", "ProcessHacker.exe" + VALUE "ProductName", "Process Hacker" + VALUE "ProductVersion", PHAPP_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/README.md b/README.md index 38037e452c14..83df32910073 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,60 @@ -Process Hacker is a powerful free and open source process viewer. +[![Build status](https://img.shields.io/appveyor/ci/processhacker/processhacker.svg?style=for-the-badge)](https://ci.appveyor.com/project/processhacker/processhacker) +[![Build contributors](https://img.shields.io/github/contributors/processhacker/processhacker.svg?style=for-the-badge)](https://github.com/processhacker/processhacker/graphs/contributors) +[![Licence](https://img.shields.io/badge/license-GPLv3-blue.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0.en.html) +[![Github stats](https://img.shields.io/github/downloads/processhacker/processhacker/total.svg?style=for-the-badge)](https://github.com/processhacker/processhacker/releases) +[![SourceForge stats](https://img.shields.io/sourceforge/dt/processhacker.svg?style=for-the-badge)](https://sourceforge.net/projects/processhacker/) -## Getting started +![Logo](https://raw.githubusercontent.com/processhacker2/processhacker/master/ProcessHacker/resources/ProcessHacker.png) -Simply run ProcessHacker.exe to start Process Hacker. There are two -versions, 32-bit (x86) and 64-bit (x64). If you are not sure which -version to use, open Control Panel > System and check the "System -type". You cannot run the 32-bit version of Process Hacker on a -64-bit system and expect it to work correctly, unlike other programs. +## Process Hacker + +A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware. + +* [Official Website](https://processhacker.sourceforge.io/) +* [Nightly Builds](https://wj32.org/processhacker/nightly.php) ## System requirements Windows 7 or higher, 32-bit or 64-bit. +## Features + +* A detailed overview of system activity with highlighting. +* Graphs and statistics allow you quickly to track down resource hogs and runaway processes. +* Can't edit or delete a file? Discover which processes are using that file. +* See what programs have active network connections, and close them if necessary. +* Get real-time information on disk access. +* View detailed stack traces with kernel-mode, WOW64 and .NET support. +* Go beyond services.msc: create, edit and control services. +* Small, portable and no installation required. +* 100% [Free Software](http://www.gnu.org/philosophy/free-sw.en.html) ([GPL v3](http://www.gnu.org/licenses/gpl-3.0.en.html)) + + +## Building the project + + +Requires Visual Studio (2017 or later). + +Execute `build_release.cmd` located in the `build` directory to compile the project or load the `ProcessHacker.sln` and `Plugins.sln` solutions if you prefer building the project using Visual Studio. + +You can download the free [Visual Studio Community Edition](https://www.visualstudio.com/vs/community/) +to build, run or develop Process Hacker. + +## Additional information + + +You cannot run the 32-bit version of Process Hacker on a +64-bit system and expect it to work correctly, unlike other programs. + + + +## Enhancements/Bugs + + +Please use the [GitHub issue tracker](https://github.com/processhacker2/processhacker/issues) +for reporting problems or suggesting new features. + + ## Settings If you are running Process Hacker from a USB drive, you may want to @@ -32,8 +75,7 @@ Plugins can be configured from Hacker > Plugins. If you experience any crashes involving plugins, make sure they are up to date. -The ExtendedTools plugin is only available for Windows Vista and -above. Disk and Network information provided by this plugin is +Disk and Network information provided by the ExtendedTools plugin is only available when running Process Hacker with administrative rights. @@ -49,8 +91,8 @@ assist with certain functionality. This includes: * Setting handle attributes Note that by default, KProcessHacker only allows connections from -processes with SeDebugPrivilege. To allow Process Hacker to show details -for all processes when it is not running as administrator: +processes with administrative privileges (SeDebugPrivilege). To allow Process Hacker +to show details for all processes when it is not running as administrator: 1. In Registry Editor, navigate to: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KProcessHacker3 diff --git a/README.txt b/README.txt new file mode 100644 index 000000000000..dc20782948e6 --- /dev/null +++ b/README.txt @@ -0,0 +1,61 @@ +Process Hacker is a powerful free and open source process viewer. + +## Getting started + +Simply run ProcessHacker.exe to start Process Hacker. There are two +versions, 32-bit (x86) and 64-bit (x64). If you are not sure which +version to use, open Control Panel > System and check the "System +type". You cannot run the 32-bit version of Process Hacker on a +64-bit system and expect it to work correctly, unlike other programs. + +## System requirements + +Windows 7 or higher, 32-bit or 64-bit. + +## Settings + +If you are running Process Hacker from a USB drive, you may want to +save Process Hacker's settings there as well. To do this, create a +blank file named "ProcessHacker.exe.settings.xml" in the same +directory as ProcessHacker.exe. You can do this using Windows Explorer: + +1. Make sure "Hide extensions for known file types" is unticked in + Tools > Folder options > View. +2. Right-click in the folder and choose New > Text Document. +3. Rename the file to ProcessHacker.exe.settings.xml (delete the ".txt" + extension). + +## Plugins + +Plugins can be configured from Hacker > Plugins. + +If you experience any crashes involving plugins, make sure they +are up to date. + +The ExtendedTools plugin is only available for Windows Vista and +above. Disk and Network information provided by this plugin is +only available when running Process Hacker with administrative +rights. + +## KProcessHacker + +Process Hacker uses a kernel-mode driver, KProcessHacker, to +assist with certain functionality. This includes: + +* Capturing kernel-mode stack traces +* More efficiently enumerating process handles +* Retrieving names for file handles +* Retrieving names for EtwRegistration objects +* Setting handle attributes + +Note that by default, KProcessHacker only allows connections from +processes with SeDebugPrivilege. To allow Process Hacker to show details +for all processes when it is not running as administrator: + +1. In Registry Editor, navigate to: + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KProcessHacker3 +2. Under this key, create a key named Parameters if it does not exist. +3. Create a DWORD value named SecurityLevel and set it to 2. If you are + not using an official build, you may need to set it to 0 instead. +4. Restart the KProcessHacker3 service (sc stop KProcessHacker3, + sc start KProcessHacker3). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..28258c33ff09 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policies and Procedures + +This document outlines security procedures and general policies for the Process Hacker project. + + * [Reporting a Bug](#reporting-a-bug) + * [Disclosure Policy](#disclosure-policy) + * [Comments on this Policy](#comments-on-this-policy) + +## Reporting a Bug + +The Process Hacker team and community take all security bugs very seriously. We appreciate efforts +for responsible disclosure and will make every effort to acknowledge your contributions. + +Report security bugs by emailing the lead maintainers at dmex04@gmail.com and wj32.64@gmail.com. + +The Process Hacker team will acknowledge your email within 48 hours, and will send a +more detailed response indicating the next steps in handling +your report. After the initial reply to your report, the security team will +endeavor to keep you informed of the progress towards a fix and full +announcement, and may ask for additional information or guidance. + +Report security bugs in third-party modules to the person or team maintaining +the module. + +## Disclosure Policy + +When the Process Hacker team receives a security bug report, they will assign it to a +primary developer. This person will coordinate the fix and release process, +involving the following steps: + + * Confirm the problem and determine the affected versions. + * Audit code to find any potential similar problems. + * Seek review from respected third-party security experts. + * Prepare fixes for all releases still under maintenance. These fixes will be + released as fast as possible. + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a +pull request. diff --git a/appveyor.yml b/appveyor.yml index 15728ca977a5..321ce514fe26 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,8 @@ # Build image. -image: Visual Studio 2017 +image: Visual Studio 2019 # Version format. -version: "3.0.{build}" +version: "#{build}" # Only build the master branch. branches: @@ -15,10 +15,10 @@ pull_requests: # Do not start a new build when a new Git tag is created. skip_tags: true - + # Set the clone directory on the buildbot. clone_folder: C:\projects\processhacker - + # Immediately finish build if something fails. matrix: fast_finish: true @@ -28,13 +28,4 @@ test: off # Run custom build script. build_script: -- cmd: .\build\CustomBuildTool.exe - -# Setup build notifications. -notifications: -- provider: Email - to: - - dmex04@gmail.com - on_build_success: false - on_build_failure: true - on_build_status_changed: false \ No newline at end of file +- cmd: tools\CustomBuildTool\bin\Release\CustomBuildTool.exe -appveyor diff --git a/build/build_appx_makecert.cmd b/build/build_appx_makecert.cmd new file mode 100644 index 000000000000..f28f3479e3b5 --- /dev/null +++ b/build/build_appx_makecert.cmd @@ -0,0 +1,7 @@ +@echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" + +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-appxmakecert" + +pause diff --git a/build/build_appx_package.cmd b/build/build_appx_package.cmd new file mode 100644 index 000000000000..06227454c85e --- /dev/null +++ b/build/build_appx_package.cmd @@ -0,0 +1,7 @@ +@echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" + +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-appxbuild" + +pause diff --git a/build/build_binaries.cmd b/build/build_binaries.cmd deleted file mode 100644 index 3fad7f7d524c..000000000000 --- a/build/build_binaries.cmd +++ /dev/null @@ -1,5 +0,0 @@ -@echo off - -CustomBuildTool.exe -bin - -pause diff --git a/build/build_changelog.cmd b/build/build_changelog.cmd new file mode 100644 index 000000000000..3a1df98f5065 --- /dev/null +++ b/build/build_changelog.cmd @@ -0,0 +1,7 @@ +@echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" + +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-graph" + +pause diff --git a/build/build_clean.cmd b/build/build_clean.cmd new file mode 100644 index 000000000000..40de19df02f2 --- /dev/null +++ b/build/build_clean.cmd @@ -0,0 +1,5 @@ +@echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" + +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleanup" diff --git a/build/build_debug.cmd b/build/build_debug.cmd index d11d2ffedd85..8a9027bb842f 100644 --- a/build/build_debug.cmd +++ b/build/build_debug.cmd @@ -1,5 +1,7 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" -CustomBuildTool.exe -debug +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-debug" pause diff --git a/build/build_release.cmd b/build/build_release.cmd index c92eb4847d49..000b928d35a4 100644 --- a/build/build_release.cmd +++ b/build/build_release.cmd @@ -1,17 +1,7 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" - set /p APPVEYOR_BUILD_KEY="APPVEYOR_BUILD_KEY: " - set /p APPVEYOR_BUILD_API="APPVEYOR_BUILD_API: " - set /p VIRUSTOTAL_BUILD_KEY="VIRUSTOTAL_BUILD_KEY: " - set /p KPH_BUILD_KEY="KPH_BUILD_KEY: " - set /p NIGHTLY_BUILD_KEY="NIGHTLY_BUILD_KEY: " - - set APPVEYOR_BUILD_KEY=%APPVEYOR_BUILD_KEY% - set APPVEYOR_BUILD_API=%APPVEYOR_BUILD_API% - set VIRUSTOTAL_BUILD_KEY=%VIRUSTOTAL_BUILD_KEY% - set KPH_BUILD_KEY=%KPH_BUILD_KEY% - set NIGHTLY_BUILD_KEY=%NIGHTLY_BUILD_KEY% - -CustomBuildTool.exe -release +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-release" pause diff --git a/build/build_sdk.cmd b/build/build_sdk.cmd index 31bed7a1b211..7a3a7fe039a6 100644 --- a/build/build_sdk.cmd +++ b/build/build_sdk.cmd @@ -1,3 +1,7 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" -CustomBuildTool.exe -sdk \ No newline at end of file +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-sdk" + +pause diff --git a/build/build_sdk_rebuild.cmd b/build/build_sdk_rebuild.cmd index 8658cf1b1a04..19ad008487ba 100644 --- a/build/build_sdk_rebuild.cmd +++ b/build/build_sdk_rebuild.cmd @@ -1,5 +1,7 @@ @echo off +@setlocal enableextensions +@cd /d "%~dp0\..\" -CustomBuildTool.exe -cleansdk +start /B /W "" "tools\CustomBuildTool\bin\Release\CustomBuildTool.exe" "-cleansdk" pause diff --git a/build/phapppub_options.txt b/build/phapppub_options.txt deleted file mode 100644 index f668a06b10e3..000000000000 --- a/build/phapppub_options.txt +++ /dev/null @@ -1,6 +0,0 @@ -base=ProcessHacker\include -modes=phapppub -in=phapp.h;appsup.h;phfwddef.h;procprv.h;srvprv.h;netprv.h;modprv.h;thrdprv.h;hndlprv.h;memprv.h;phuisup.h;colmgr.h;proctree.h;srvlist.h;netlist.h;thrdlist.h;modlist.h;hndllist.h;memlist.h;extmgr.h;mainwnd.h;notifico.h;settings.h;phplug.h;actions.h;procprp.h;procprpp.h;phsvccl.h;sysinfo.h;procgrp.h;miniinfo.h -out=..\sdk\phapppub.h -header=#ifndef _PH_PHAPPPUB_H\r\n#define _PH_PHAPPPUB_H\r\n\r\n// This file was automatically generated. Do not edit.\r\n\r\n#ifdef __cplusplus\r\nextern "C" {\r\n#endif\r\n -footer=\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n \ No newline at end of file diff --git a/build/plugins.s b/build/plugins.s new file mode 100644 index 000000000000..cf600c72ece7 Binary files /dev/null and b/build/plugins.s differ diff --git a/plugins/OnlineChecks/virustotal.s b/build/virustotal.s similarity index 100% rename from plugins/OnlineChecks/virustotal.s rename to build/virustotal.s diff --git a/phlib/apiimport.c b/phlib/apiimport.c index a538d8359f6b..22158c9933f4 100644 --- a/phlib/apiimport.c +++ b/phlib/apiimport.c @@ -1,68 +1,94 @@ -/* - * Process Hacker - - * procedure import module - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -PVOID PhpImportProcedure( - _Inout_ PVOID *Cache, - _Inout_ PBOOLEAN CacheValid, - _In_ PWSTR ModuleName, - _In_ PSTR ProcedureName - ) -{ - HMODULE module; - PVOID procedure; - - if (*CacheValid) - return *Cache; - - module = GetModuleHandle(ModuleName); - - if (!module) - return NULL; - - procedure = GetProcAddress(module, ProcedureName); - *Cache = procedure; - MemoryBarrier(); - *CacheValid = TRUE; - - return procedure; -} - -#define PH_DEFINE_IMPORT(Module, Name) \ -_##Name Name##_Import(VOID) \ -{ \ - static PVOID cache = NULL; \ - static BOOLEAN cacheValid = FALSE; \ -\ - return (_##Name)PhpImportProcedure(&cache, &cacheValid, Module, #Name); \ -} - -PH_DEFINE_IMPORT(L"comctl32.dll", TaskDialogIndirect); -PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationEnlistment); -PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationResourceManager); -PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationTransaction); -PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationTransactionManager); -PH_DEFINE_IMPORT(L"shell32.dll", SHCreateShellItem); -PH_DEFINE_IMPORT(L"shell32.dll", SHOpenFolderAndSelectItems); -PH_DEFINE_IMPORT(L"shell32.dll", SHParseDisplayName); +/* + * Process Hacker - + * procedure import module + * + * Copyright (C) 2015 wj32 + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +PVOID PhpEncodeDecodePointer( // dmex + _In_ PVOID Pointer + ) +{ + static ULONG cookie = 0; + + if (cookie == 0) + { + RtlRandomEx(&cookie); + } + + return (PVOID)((ULONG_PTR)Pointer ^ cookie); +} + +PVOID PhpImportProcedure( + _Inout_ PVOID *Cache, + _Inout_ PBOOLEAN CacheValid, + _In_ PWSTR ModuleName, + _In_ PSTR ProcedureName + ) +{ + PVOID module; + PVOID procedure; + + if (*CacheValid) + return PhpEncodeDecodePointer(*Cache); + + if (!(module = PhGetLoaderEntryDllBase(ModuleName))) + module = LoadLibrary(ModuleName); // HACK (dmex) + + if (!module) + return NULL; + + procedure = PhGetDllBaseProcedureAddress(module, ProcedureName, 0); + *Cache = PhpEncodeDecodePointer(procedure); + + MemoryBarrier(); + *CacheValid = TRUE; + + return procedure; +} + +#define PH_DEFINE_IMPORT(Module, Name) \ +_##Name Name##_Import(VOID) \ +{ \ + static PVOID cache = NULL; \ + static BOOLEAN cacheValid = FALSE; \ +\ + return (_##Name)PhpImportProcedure(&cache, &cacheValid, Module, #Name); \ +} + +PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationEnlistment); +PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationResourceManager); +PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationTransaction); +PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryInformationTransactionManager); +PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryDefaultLocale); +PH_DEFINE_IMPORT(L"ntdll.dll", NtQueryDefaultUILanguage); + +PH_DEFINE_IMPORT(L"ntdll.dll", RtlGetTokenNamedObjectPath); +PH_DEFINE_IMPORT(L"ntdll.dll", RtlGetAppContainerNamedObjectPath); +PH_DEFINE_IMPORT(L"ntdll.dll", RtlGetAppContainerSidType); +PH_DEFINE_IMPORT(L"ntdll.dll", RtlGetAppContainerParent); +PH_DEFINE_IMPORT(L"ntdll.dll", RtlDeriveCapabilitySidsFromName); + +PH_DEFINE_IMPORT(L"userenv.dll", GetAppContainerRegistryLocation); +PH_DEFINE_IMPORT(L"userenv.dll", GetAppContainerFolderPath); + +PH_DEFINE_IMPORT(L"advapi32.dll", ConvertSecurityDescriptorToStringSecurityDescriptorW); diff --git a/phlib/appresolver.c b/phlib/appresolver.c new file mode 100644 index 000000000000..a3e7d4c073ef --- /dev/null +++ b/phlib/appresolver.c @@ -0,0 +1,669 @@ +/* + * Process Hacker - + * Appmodel support functions + * + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static PVOID PhpQueryAppResolverInterface( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PVOID resolverInterface = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + if (WindowsVersion < WINDOWS_8) + CoCreateInstance(&CLSID_StartMenuCacheAndAppResolver_I, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationResolver61_I, &resolverInterface); + else + CoCreateInstance(&CLSID_StartMenuCacheAndAppResolver_I, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationResolver62_I, &resolverInterface); + + PhEndInitOnce(&initOnce); + } + + return resolverInterface; +} + +static PVOID PhpQueryStartMenuCacheInterface( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PVOID startMenuInterface = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + if (WindowsVersion < WINDOWS_8) + CoCreateInstance(&CLSID_StartMenuCacheAndAppResolver_I, NULL, CLSCTX_INPROC_SERVER, &IID_IStartMenuAppItems61_I, &startMenuInterface); + else + CoCreateInstance(&CLSID_StartMenuCacheAndAppResolver_I, NULL, CLSCTX_INPROC_SERVER, &IID_IStartMenuAppItems62_I, &startMenuInterface); + + PhEndInitOnce(&initOnce); + } + + return startMenuInterface; +} + +static BOOLEAN PhpKernelAppCoreInitialized( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static BOOLEAN kernelAppCoreInitialized = FALSE; + + if (PhBeginInitOnce(&initOnce)) + { + if (WindowsVersion >= WINDOWS_8) + { + PVOID kernelBaseModuleHandle; + + if (kernelBaseModuleHandle = LoadLibrary(L"kernelbase.dll")) // kernel.appcore.dll + { + AppContainerDeriveSidFromMoniker_I = PhGetDllBaseProcedureAddress(kernelBaseModuleHandle, "AppContainerDeriveSidFromMoniker", 0); + AppContainerLookupMoniker_I = PhGetDllBaseProcedureAddress(kernelBaseModuleHandle, "AppContainerLookupMoniker", 0); + AppContainerFreeMemory_I = PhGetDllBaseProcedureAddress(kernelBaseModuleHandle, "AppContainerFreeMemory", 0); + AppContainerRegisterSid_I = PhGetDllBaseProcedureAddress(kernelBaseModuleHandle, "AppContainerRegisterSid", 0); + AppContainerUnregisterSid_I = PhGetDllBaseProcedureAddress(kernelBaseModuleHandle, "AppContainerUnregisterSid", 0); + AppPolicyGetWindowingModel_I = PhGetDllBaseProcedureAddress(kernelBaseModuleHandle, "AppPolicyGetWindowingModel", 0); + } + + if ( + AppContainerDeriveSidFromMoniker_I && + AppContainerLookupMoniker_I && + AppContainerFreeMemory_I && + AppContainerRegisterSid_I && + AppContainerUnregisterSid_I + ) + { + kernelAppCoreInitialized = TRUE; + } + } + + PhEndInitOnce(&initOnce); + } + + return kernelAppCoreInitialized; +} + +BOOLEAN PhAppResolverGetAppIdForProcess( + _In_ HANDLE ProcessId, + _Out_ PPH_STRING *ApplicationUserModelId + ) +{ + PVOID resolverInterface; + PWSTR appIdText = NULL; + + if (!(resolverInterface = PhpQueryAppResolverInterface())) + return FALSE; + + if (WindowsVersion < WINDOWS_8) + { + IApplicationResolver_GetAppIDForProcess( + (IApplicationResolver61*)resolverInterface, + HandleToUlong(ProcessId), + &appIdText, + NULL, + NULL, + NULL + ); + } + else + { + IApplicationResolver2_GetAppIDForProcess( + (IApplicationResolver62*)resolverInterface, + HandleToUlong(ProcessId), + &appIdText, + NULL, + NULL, + NULL + ); + } + + if (appIdText) + { + *ApplicationUserModelId = PhCreateString(appIdText); + return TRUE; + } + + return FALSE; +} + +BOOLEAN PhAppResolverGetAppIdForWindow( + _In_ HWND WindowHandle, + _Out_ PPH_STRING *ApplicationUserModelId + ) +{ + PVOID resolverInterface; + PWSTR appIdText = NULL; + + if (!(resolverInterface = PhpQueryAppResolverInterface())) + return FALSE; + + if (WindowsVersion < WINDOWS_8) + { + IApplicationResolver_GetAppIDForWindow( + (IApplicationResolver61*)resolverInterface, + WindowHandle, + &appIdText, + NULL, + NULL, + NULL + ); + } + else + { + IApplicationResolver_GetAppIDForWindow( + (IApplicationResolver62*)resolverInterface, + WindowHandle, + &appIdText, + NULL, + NULL, + NULL + ); + } + + if (appIdText) + { + *ApplicationUserModelId = PhCreateString(appIdText); + return TRUE; + } + + return FALSE; +} + +HRESULT PhAppResolverActivateAppId( + _In_ PPH_STRING AppUserModelId, + _In_opt_ PWSTR CommandLine, + _Out_opt_ HANDLE *ProcessId + ) +{ + HRESULT status; + ULONG processId = 0; + IApplicationActivationManager* applicationActivationManager; + + status = CoCreateInstance( + &CLSID_ApplicationActivationManager, + NULL, + CLSCTX_LOCAL_SERVER, + &IID_IApplicationActivationManager, + &applicationActivationManager + ); + + if (SUCCEEDED(status)) + { + CoAllowSetForegroundWindow((IUnknown*)applicationActivationManager, NULL); + + status = IApplicationActivationManager_ActivateApplication( + applicationActivationManager, + PhGetString(AppUserModelId), + CommandLine, + AO_NONE, + &processId + ); + + IApplicationActivationManager_Release(applicationActivationManager); + } + + if (SUCCEEDED(status)) + { + if (ProcessId) *ProcessId = UlongToHandle(processId); + } + + return status; +} + +PPH_LIST PhAppResolverEnumeratePackageBackgroundTasks( + _In_ PPH_STRING PackageFullName + ) +{ + HRESULT status; + PPH_LIST packageTasks = NULL; + IPackageDebugSettings* packageDebugSettings; + + status = CoCreateInstance( + &CLSID_PackageDebugSettings, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IPackageDebugSettings, + &packageDebugSettings + ); + + if (SUCCEEDED(status)) + { + ULONG taskCount = 0; + PGUID taskIds = NULL; + PWSTR* taskNames = NULL; + + status = IPackageDebugSettings_EnumerateBackgroundTasks( + packageDebugSettings, + PhGetString(PackageFullName), + &taskCount, + &taskIds, + &taskNames + ); + + if (SUCCEEDED(status)) + { + for (ULONG i = 0; i < taskCount; i++) + { + PPH_PACKAGE_TASK_ENTRY entry; + + entry = PhAllocateZero(sizeof(PH_PACKAGE_TASK_ENTRY)); + entry->TaskGuid = taskIds[i]; + entry->TaskName = PhCreateString(taskNames[i]); + + if (!packageTasks) + packageTasks = PhCreateList(taskCount); + + PhAddItemList(packageTasks, entry); + } + } + + IPackageDebugSettings_Release(packageDebugSettings); + } + + if (packageTasks) + return packageTasks; + else + return NULL; +} + +PPH_STRING PhGetAppContainerName( + _In_ PSID AppContainerSid + ) +{ + HRESULT result; + PPH_STRING appContainerName = NULL; + PWSTR packageMonikerName; + + if (!PhpKernelAppCoreInitialized()) + return NULL; + + // NOTE: The AppContainerLookupMoniker function is not able to lookup the appcontainer names created using the + // CreateAppContainerProfile function from Win32 desktop applications (e.g. Google Chrome). + result = AppContainerLookupMoniker_I( + AppContainerSid, + &packageMonikerName + ); + + if (SUCCEEDED(result)) + { + appContainerName = PhCreateString(packageMonikerName); + AppContainerFreeMemory_I(packageMonikerName); + } + + return appContainerName; +} + +PPH_STRING PhGetAppContainerSidFromName( + _In_ PWSTR AppContainerName + ) +{ + PSID appContainerSid; + PPH_STRING packageSidString = NULL; + + if (!PhpKernelAppCoreInitialized()) + return NULL; + + if (SUCCEEDED(AppContainerDeriveSidFromMoniker_I( + AppContainerName, + &appContainerSid + ))) + { + packageSidString = PhSidToStringSid(appContainerSid); + + RtlFreeSid(appContainerSid); + } + + return packageSidString; +} + +PPH_STRING PhGetAppContainerPackageName( + _In_ PSID Sid + ) +{ + static PH_STRINGREF appcontainerMappings = PH_STRINGREF_INIT(L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Mappings\\"); + HANDLE keyHandle; + PPH_STRING sidString; + PPH_STRING keyPath; + PPH_STRING packageName = NULL; + + sidString = PhSidToStringSid(Sid); + + if (PhEqualString2(sidString, L"S-1-15-3-4096", FALSE)) // HACK + { + PhDereferenceObject(sidString); + return PhCreateString(L"InternetExplorer"); + } + + keyPath = PhConcatStringRef2(&appcontainerMappings, &sidString->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &keyPath->sr, + 0 + ))) + { + PhMoveReference(&packageName, PhQueryRegistryString(keyHandle, L"Moniker")); + NtClose(keyHandle); + } + + PhDereferenceObject(keyPath); + PhDereferenceObject(sidString); + + return packageName; +} + +PPH_STRING PhGetPackagePath( + _In_ PPH_STRING PackageFullName + ) +{ + static PH_STRINGREF storeAppPackages = PH_STRINGREF_INIT(L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\Repository\\Packages\\"); + HANDLE keyHandle; + PPH_STRING keyPath; + PPH_STRING packagePath = NULL; + + keyPath = PhConcatStringRef2(&storeAppPackages, &PackageFullName->sr); + + // rev from GetPackagePath + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &keyPath->sr, + 0 + ))) + { + packagePath = PhQueryRegistryString(keyHandle, L"PackageRootFolder"); + NtClose(keyHandle); + } + + PhDereferenceObject(keyPath); + + return packagePath; +} + +PPH_STRING PhGetPackageAppDataPath( + _In_ HANDLE ProcessHandle + ) +{ + static UNICODE_STRING attributeNameUs = RTL_CONSTANT_STRING(L"WIN://SYSAPPID"); + static PH_STRINGREF appdataPackages = PH_STRINGREF_INIT(L"%APPDATALOCAL%\\Packages\\"); + HANDLE tokenHandle; + PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; + PPH_STRING packageAppDataPath = NULL; + + if (NT_SUCCESS(PhOpenProcessToken( + ProcessHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenSecurityAttributes, &info))) + { + for (ULONG i = 0; i < info->AttributeCount; i++) + { + PTOKEN_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; + + if (attribute->ValueType == TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING) + { + if (RtlEqualUnicodeString(&attribute->Name, &attributeNameUs, FALSE)) + { + PPH_STRING attributeValue; + PPH_STRING attributePath; + + attributeValue = PhCreateStringFromUnicodeString(&attribute->Values.pString[2]); + + if (attributePath = PhExpandEnvironmentStrings(&appdataPackages)) + { + packageAppDataPath = PhConcatStringRef2(&attributePath->sr, &attributeValue->sr); + + PhDereferenceObject(attributePath); + PhDereferenceObject(attributeValue); + break; + } + + PhDereferenceObject(attributeValue); + } + } + } + + PhFree(info); + } + + NtClose(tokenHandle); + } + + return packageAppDataPath; +} + +PPH_STRING PhGetProcessPackageFullName( + _In_ HANDLE ProcessHandle + ) +{ + static UNICODE_STRING attributeNameUs = RTL_CONSTANT_STRING(L"WIN://SYSAPPID"); + HANDLE tokenHandle; + PTOKEN_SECURITY_ATTRIBUTES_INFORMATION info; + PPH_STRING packageName = NULL; + + if (NT_SUCCESS(PhOpenProcessToken( + ProcessHandle, + TOKEN_QUERY, + &tokenHandle + ))) + { + // rev from PackageIdFromFullName + if (NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenSecurityAttributes, &info))) + { + for (ULONG i = 0; i < info->AttributeCount; i++) + { + PTOKEN_SECURITY_ATTRIBUTE_V1 attribute = &info->Attribute.pAttributeV1[i]; + + if (attribute->ValueType == TOKEN_SECURITY_ATTRIBUTE_TYPE_STRING) + { + if (RtlEqualUnicodeString(&attribute->Name, &attributeNameUs, FALSE)) + { + packageName = PhCreateStringFromUnicodeString(&attribute->Values.pString[0]); + break; + } + } + } + + PhFree(info); + } + + NtClose(tokenHandle); + } + + return packageName; +} + +BOOLEAN PhIsPackageCapabilitySid( + _In_ PSID AppContainerSid, + _In_ PSID Sid + ) +{ + BOOLEAN isPackageCapability = TRUE; + + for (ULONG i = 1; i < SECURITY_APP_PACKAGE_RID_COUNT - 1; i++) + { + if ( + *RtlSubAuthoritySid(AppContainerSid, i) != + *RtlSubAuthoritySid(Sid, i) + ) + { + isPackageCapability = FALSE; + break; + } + } + + return isPackageCapability; +} + +BOOLEAN PhGetAppWindowingModel( + _In_ HANDLE ProcessTokenHandle, + _Out_ AppPolicyWindowingModel *ProcessWindowingModelPolicy + ) +{ + if (!PhpKernelAppCoreInitialized() && !AppPolicyGetWindowingModel_I) + return FALSE; + + return SUCCEEDED(AppPolicyGetWindowingModel_I(ProcessTokenHandle, ProcessWindowingModelPolicy)); +} + +PPH_LIST PhGetPackageAssetsFromResourceFile( + _In_ PWSTR FilePath + ) +{ + IMrtResourceManager* resourceManager = NULL; + IResourceMap* resourceMap = NULL; + PPH_LIST resourceList = NULL; + ULONG resourceCount = 0; + + if (FAILED(CoCreateInstance( + &CLSID_MrtResourceManager_I, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IMrtResourceManager_I, + &resourceManager + ))) + { + return FALSE; + } + + if (FAILED(IMrtResourceManager_InitializeForFile(resourceManager, FilePath))) + goto CleanupExit; + + if (FAILED(IMrtResourceManager_GetMainResourceMap(resourceManager, &IID_IResourceMap_I, &resourceMap))) + goto CleanupExit; + + if (FAILED(IResourceMap_GetNamedResourceCount(resourceMap, &resourceCount))) + goto CleanupExit; + + resourceList = PhCreateList(10); + + for (ULONG i = 0; i < resourceCount; i++) + { + PWSTR resourceName; + + if (SUCCEEDED(IResourceMap_GetNamedResourceUri(resourceMap, i, &resourceName))) + { + PhAddItemList(resourceList, PhCreateString(resourceName)); + } + } + +CleanupExit: + + if (resourceMap) + IResourceMap_Release(resourceMap); + + if (resourceManager) + IMrtResourceManager_Release(resourceManager); + + return resourceList; +} + +// TODO: FIXME +//HICON PhAppResolverGetPackageIcon( +// _In_ HANDLE ProcessId, +// _In_ PPH_STRING PackageFullName +// ) +//{ +// PVOID startMenuInterface; +// PPH_STRING applicationUserModelId; +// IPropertyStore* propStoreInterface; +// HICON packageIcon = NULL; +// +// if (!(startMenuInterface = PhpQueryStartMenuCacheInterface())) +// return NULL; +// +// if (!PhAppResolverGetAppIdForProcess(ProcessId, &applicationUserModelId)) +// return NULL; +// +// if (WindowsVersion < WINDOWS_8) +// { +// IStartMenuAppItems_GetItem( +// (IStartMenuAppItems61*)startMenuInterface, +// SMAIF_DEFAULT, +// applicationUserModelId->Buffer, +// &IID_IPropertyStore, +// &propStoreInterface +// ); +// } +// else +// { +// IStartMenuAppItems2_GetItem( +// (IStartMenuAppItems62*)startMenuInterface, +// SMAIF_DEFAULT, +// applicationUserModelId->Buffer, +// &IID_IPropertyStore, +// &propStoreInterface +// ); +// } +// +// if (propStoreInterface) +// { +// IMrtResourceManager* mrtResourceManager; +// IResourceMap* resourceMap; +// PROPVARIANT propVar; +// PWSTR filePath; +// +// IPropertyStore_GetValue(propStoreInterface, &PKEY_Tile_Background, &propVar); +// IPropertyStore_GetValue(propStoreInterface, &PKEY_Tile_SmallLogoPath, &propVar); +// +// CoCreateInstance( +// &CLSID_MrtResourceManager_I, +// NULL, +// CLSCTX_INPROC_SERVER, +// &IID_IMrtResourceManager_I, +// &mrtResourceManager +// ); +// +// IMrtResourceManager_InitializeForPackage(mrtResourceManager, PackageFullName->Buffer); +// IMrtResourceManager_GetMainResourceMap(mrtResourceManager, &IID_IResourceMap_I, &resourceMap); +// IResourceMap_GetFilePath(resourceMap, propVar.pwszVal, &filePath); +// +// //HBITMAP bitmap = PhLoadImageFromFile(filePath, 32, 32); +// //packageIcon = PhBitmapToIcon(bitmap, 32, 32); +// +// IResourceMap_Release(resourceMap); +// IMrtResourceManager_Release(mrtResourceManager); +// PropVariantClear(&propVar); +// +// IPropertyStore_Release(propStoreInterface); +// } +// +// PhDereferenceObject(applicationUserModelId); +// +// return packageIcon; +//} diff --git a/phlib/basesup.c b/phlib/basesup.c index e93241089a70..ab0c3f8082a8 100644 --- a/phlib/basesup.c +++ b/phlib/basesup.c @@ -1,5985 +1,6108 @@ -/* - * Process Hacker - - * base support functions - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This file contains basic low-level code as well as general algorithms and data structures. - * - * Memory allocation. PhAllocate is a wrapper around RtlAllocateHeap, and always allocates from the - * phlib heap. PhAllocatePage is a wrapper around NtAllocateVirtualMemory and allocates pages. - * - * Null-terminated strings. The Ph*StringZ functions manipulate null-terminated strings. The copying - * functions provide a simple way to copy strings which may not be null-terminated, but have a - * specified limit. - * - * String. The design of the string object was chosen for maximum compatibility. As such each string - * buffer must be null-terminated, and each object contains an embedded PH_STRINGREF structure. Note - * that efficient sub-string creation (no copying, only references the parent string object) could - * not be implemented due to the mandatory null-termination. String objects must be regarded as - * immutable (for thread-safety reasons) unless the object has just been created and no references - * have been shared. - * - * String builder. This is a set of functions which allow for efficient modification of strings. For - * performance reasons, these functions modify string objects directly, even though they are - * normally immutable. - * - * List. A simple PVOID list that resizes itself when needed. - * - * Pointer list. Similar to the normal list object, but uses a free list in order to support - * constant time insertion and deletion. In order for the free list to work, normal entries have - * their lowest bit clear while free entries have their lowest bit set, with the index of the next - * free entry in the upper bits. - * - * Hashtable. A hashtable with power-of-two bucket sizes and with all entries stored in a single - * array. This improves locality but may be inefficient when resizing the hashtable. It is a good - * idea to store pointers to objects as entries, as opposed to the objects themselves. - * - * Simple hashtable. A wrapper around the normal hashtable, with PVOID keys and PVOID values. - * - * Free list. A thread-safe memory allocation method where freed blocks are stored in a S-list, and - * allocations are made from this list whenever possible. - * - * Callback. A thread-safe notification mechanism where clients can register callback functions - * which are then invoked by other code. - */ - -#include - -#include -#include - -#include - -#define PH_VECTOR_LEVEL_NONE 0 -#define PH_VECTOR_LEVEL_SSE2 1 -#define PH_VECTOR_LEVEL_AVX 2 - -typedef struct _PHP_BASE_THREAD_CONTEXT -{ - PUSER_THREAD_START_ROUTINE StartAddress; - PVOID Parameter; -} PHP_BASE_THREAD_CONTEXT, *PPHP_BASE_THREAD_CONTEXT; - -VOID NTAPI PhpListDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID NTAPI PhpPointerListDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID NTAPI PhpQueueDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID NTAPI PhpHashtableDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -// Types - -PPH_OBJECT_TYPE PhStringType; -PPH_OBJECT_TYPE PhBytesType; -PPH_OBJECT_TYPE PhListType; -PPH_OBJECT_TYPE PhPointerListType; -PPH_OBJECT_TYPE PhHashtableType; - -// Misc. - -static BOOLEAN PhpVectorLevel = PH_VECTOR_LEVEL_NONE; -static PPH_STRING PhSharedEmptyString = NULL; - -// Threads - -static PH_FREE_LIST PhpBaseThreadContextFreeList; -#ifdef DEBUG -ULONG PhDbgThreadDbgTlsIndex; -LIST_ENTRY PhDbgThreadListHead; -PH_QUEUED_LOCK PhDbgThreadListLock = PH_QUEUED_LOCK_INIT; -#endif - -// Data - -static ULONG PhpPrimeNumbers[] = -{ - 0x3, 0x7, 0xb, 0x11, 0x17, 0x1d, 0x25, 0x2f, 0x3b, 0x47, 0x59, 0x6b, 0x83, - 0xa3, 0xc5, 0xef, 0x125, 0x161, 0x1af, 0x209, 0x277, 0x2f9, 0x397, 0x44f, - 0x52f, 0x63d, 0x78b, 0x91d, 0xaf1, 0xd2b, 0xfd1, 0x12fd, 0x16cf, 0x1b65, - 0x20e3, 0x2777, 0x2f6f, 0x38ff, 0x446f, 0x521f, 0x628d, 0x7655, 0x8e01, - 0xaa6b, 0xcc89, 0xf583, 0x126a7, 0x1619b, 0x1a857, 0x1fd3b, 0x26315, 0x2dd67, - 0x3701b, 0x42023, 0x4f361, 0x5f0ed, 0x72125, 0x88e31, 0xa443b, 0xc51eb, - 0xec8c1, 0x11bdbf, 0x154a3f, 0x198c4f, 0x1ea867, 0x24ca19, 0x2c25c1, 0x34fa1b, - 0x3f928f, 0x4c4987, 0x5b8b6f, 0x6dda89 -}; - -/** - * Initializes the base support module. - */ -BOOLEAN PhBaseInitialization( - VOID - ) -{ - PH_OBJECT_TYPE_PARAMETERS parameters; - - // The following relies on the (technically undefined) value of XState being zero before Windows 7 SP1. - // NOTE: This is unused for now. - /*if (USER_SHARED_DATA->XState.EnabledFeatures & XSTATE_MASK_AVX) - PhpVectorLevel = PH_VECTOR_LEVEL_AVX; - else*/ if (USER_SHARED_DATA->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE]) - PhpVectorLevel = PH_VECTOR_LEVEL_SSE2; - - PhStringType = PhCreateObjectType(L"String", 0, NULL); - PhBytesType = PhCreateObjectType(L"Bytes", 0, NULL); - - parameters.FreeListSize = sizeof(PH_LIST); - parameters.FreeListCount = 128; - - PhListType = PhCreateObjectTypeEx(L"List", PH_OBJECT_TYPE_USE_FREE_LIST, PhpListDeleteProcedure, ¶meters); - PhPointerListType = PhCreateObjectType(L"PointerList", 0, PhpPointerListDeleteProcedure); - - parameters.FreeListSize = sizeof(PH_HASHTABLE); - parameters.FreeListCount = 64; - - PhHashtableType = PhCreateObjectTypeEx(L"Hashtable", PH_OBJECT_TYPE_USE_FREE_LIST, PhpHashtableDeleteProcedure, ¶meters); - - PhInitializeFreeList(&PhpBaseThreadContextFreeList, sizeof(PHP_BASE_THREAD_CONTEXT), 16); - -#ifdef DEBUG - PhDbgThreadDbgTlsIndex = TlsAlloc(); - InitializeListHead(&PhDbgThreadListHead); -#endif - - return TRUE; -} - -NTSTATUS PhpBaseThreadStart( - _In_ PVOID Parameter - ) -{ - NTSTATUS status; - HRESULT result; - PHP_BASE_THREAD_CONTEXT context; -#ifdef DEBUG - PHP_BASE_THREAD_DBG dbg; -#endif - - context = *(PPHP_BASE_THREAD_CONTEXT)Parameter; - PhFreeToFreeList(&PhpBaseThreadContextFreeList, Parameter); - -#ifdef DEBUG - dbg.ClientId = NtCurrentTeb()->ClientId; - - dbg.StartAddress = context.StartAddress; - dbg.Parameter = context.Parameter; - dbg.CurrentAutoPool = NULL; - - PhAcquireQueuedLockExclusive(&PhDbgThreadListLock); - InsertTailList(&PhDbgThreadListHead, &dbg.ListEntry); - PhReleaseQueuedLockExclusive(&PhDbgThreadListLock); - - TlsSetValue(PhDbgThreadDbgTlsIndex, &dbg); -#endif - - // Initialization code - - result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - - // Call the user-supplied function. - status = context.StartAddress(context.Parameter); - - // De-initialization code - - if (result == S_OK || result == S_FALSE) - CoUninitialize(); - -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&PhDbgThreadListLock); - RemoveEntryList(&dbg.ListEntry); - PhReleaseQueuedLockExclusive(&PhDbgThreadListLock); -#endif - - return status; -} - -/** - * Creates a thread. - * - * \param StackSize The initial stack size of the thread. - * \param StartAddress The function to execute in the thread. - * \param Parameter A user-defined value to pass to the function. - */ -HANDLE PhCreateThread( - _In_opt_ SIZE_T StackSize, - _In_ PUSER_THREAD_START_ROUTINE StartAddress, - _In_opt_ PVOID Parameter - ) -{ - HANDLE threadHandle; - PPHP_BASE_THREAD_CONTEXT context; - - context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList); - context->StartAddress = StartAddress; - context->Parameter = Parameter; - - threadHandle = CreateThread( - NULL, - StackSize, - PhpBaseThreadStart, - context, - 0, - NULL - ); - - if (threadHandle) - { - PHLIB_INC_STATISTIC(BaseThreadsCreated); - return threadHandle; - } - else - { - PHLIB_INC_STATISTIC(BaseThreadsCreateFailed); - PhFreeToFreeList(&PhpBaseThreadContextFreeList, context); - return NULL; - } -} - -/** - * Gets the current system time (UTC). - * - * \remarks Use this function instead of NtQuerySystemTime() because no system calls are involved. - */ -VOID PhQuerySystemTime( - _Out_ PLARGE_INTEGER SystemTime - ) -{ - do - { - SystemTime->HighPart = USER_SHARED_DATA->SystemTime.High1Time; - SystemTime->LowPart = USER_SHARED_DATA->SystemTime.LowPart; - } while (SystemTime->HighPart != USER_SHARED_DATA->SystemTime.High2Time); -} - -/** - * Gets the offset of the current time zone from UTC. - */ -VOID PhQueryTimeZoneBias( - _Out_ PLARGE_INTEGER TimeZoneBias - ) -{ - do - { - TimeZoneBias->HighPart = USER_SHARED_DATA->TimeZoneBias.High1Time; - TimeZoneBias->LowPart = USER_SHARED_DATA->TimeZoneBias.LowPart; - } while (TimeZoneBias->HighPart != USER_SHARED_DATA->TimeZoneBias.High2Time); -} - -/** - * Converts system time to local time. - * - * \param SystemTime A UTC time value. - * \param LocalTime A variable which receives the local time value. This may be the same variable as - * \a SystemTime. - * - * \remarks Use this function instead of RtlSystemTimeToLocalTime() because no system calls are - * involved. - */ -VOID PhSystemTimeToLocalTime( - _In_ PLARGE_INTEGER SystemTime, - _Out_ PLARGE_INTEGER LocalTime - ) -{ - LARGE_INTEGER timeZoneBias; - - PhQueryTimeZoneBias(&timeZoneBias); - LocalTime->QuadPart = SystemTime->QuadPart - timeZoneBias.QuadPart; -} - -/** - * Converts local time to system time. - * - * \param LocalTime A local time value. - * \param SystemTime A variable which receives the UTC time value. This may be the same variable as - * \a LocalTime. - * - * \remarks Use this function instead of RtlLocalTimeToSystemTime() because no system calls are - * involved. - */ -VOID PhLocalTimeToSystemTime( - _In_ PLARGE_INTEGER LocalTime, - _Out_ PLARGE_INTEGER SystemTime - ) -{ - LARGE_INTEGER timeZoneBias; - - PhQueryTimeZoneBias(&timeZoneBias); - SystemTime->QuadPart = LocalTime->QuadPart + timeZoneBias.QuadPart; -} - -/** - * Allocates a block of memory. - * - * \param Size The number of bytes to allocate. - * - * \return A pointer to the allocated block of memory. - * - * \remarks If the function fails to allocate the block of memory, it raises an exception. The block - * is guaranteed to be aligned at MEMORY_ALLOCATION_ALIGNMENT bytes. - */ -_May_raise_ -_Check_return_ -_Ret_notnull_ -_Post_writable_byte_size_(Size) -PVOID PhAllocate( - _In_ SIZE_T Size - ) -{ - return RtlAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Size); -} - -/** - * Allocates a block of memory. - * - * \param Size The number of bytes to allocate. - * - * \return A pointer to the allocated block of memory, or NULL if the block could not be allocated. - */ -PVOID PhAllocateSafe( - _In_ SIZE_T Size - ) -{ - return RtlAllocateHeap(PhHeapHandle, 0, Size); -} - -/** - * Allocates a block of memory. - * - * \param Size The number of bytes to allocate. - * \param Flags Flags controlling the allocation. - * - * \return A pointer to the allocated block of memory, or NULL if the block could not be allocated. - */ -PVOID PhAllocateExSafe( - _In_ SIZE_T Size, - _In_ ULONG Flags - ) -{ - return RtlAllocateHeap(PhHeapHandle, Flags, Size); -} - -/** - * Frees a block of memory allocated with PhAllocate(). - * - * \param Memory A pointer to a block of memory. - */ -VOID PhFree( - _Frees_ptr_opt_ PVOID Memory - ) -{ - RtlFreeHeap(PhHeapHandle, 0, Memory); -} - -/** - * Re-allocates a block of memory originally allocated with PhAllocate(). - * - * \param Memory A pointer to a block of memory. - * \param Size The new size of the memory block, in bytes. - * - * \return A pointer to the new block of memory. The existing contents of the memory block are - * copied to the new block. - * - * \remarks If the function fails to allocate the block of memory, it raises an exception. - */ -_May_raise_ -_Post_writable_byte_size_(Size) -PVOID PhReAllocate( - _Frees_ptr_opt_ PVOID Memory, - _In_ SIZE_T Size - ) -{ - return RtlReAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Memory, Size); -} - -/** - * Re-allocates a block of memory originally allocated with PhAllocate(). - * - * \param Memory A pointer to a block of memory. - * \param Size The new size of the memory block, in bytes. - * - * \return A pointer to the new block of memory, or NULL if the block could not be allocated. The - * existing contents of the memory block are copied to the new block. - */ -PVOID PhReAllocateSafe( - _In_ PVOID Memory, - _In_ SIZE_T Size - ) -{ - return RtlReAllocateHeap(PhHeapHandle, 0, Memory, Size); -} - -/** - * Allocates pages of memory. - * - * \param Size The number of bytes to allocate. The number of pages allocated will be large enough - * to contain \a Size bytes. - * \param NewSize The number of bytes actually allocated. This is \a Size rounded up to the next - * multiple of PAGE_SIZE. - * - * \return A pointer to the allocated block of memory, or NULL if the block could not be allocated. - */ -_Check_return_ -_Ret_maybenull_ -PVOID PhAllocatePage( - _In_ SIZE_T Size, - _Out_opt_ PSIZE_T NewSize - ) -{ - PVOID baseAddress; - - baseAddress = NULL; - - if (NT_SUCCESS(NtAllocateVirtualMemory( - NtCurrentProcess(), - &baseAddress, - 0, - &Size, - MEM_COMMIT, - PAGE_READWRITE - ))) - { - if (NewSize) - *NewSize = Size; - - return baseAddress; - } - else - { - return NULL; - } -} - -/** - * Frees pages of memory allocated with PhAllocatePage(). - * - * \param Memory A pointer to a block of memory. - */ -VOID PhFreePage( - _Frees_ptr_opt_ PVOID Memory - ) -{ - SIZE_T size; - - size = 0; - - NtFreeVirtualMemory( - NtCurrentProcess(), - &Memory, - &size, - MEM_RELEASE - ); -} - -/** - * Determines the length of the specified string, in characters. - * - * \param String The string. - */ -SIZE_T PhCountStringZ( - _In_ PWSTR String - ) -{ - if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) - { - PWSTR p; - ULONG unaligned; - __m128i b; - __m128i z; - ULONG mask; - ULONG index; - - p = (PWSTR)((ULONG_PTR)String & ~0xe); // String should be 2 byte aligned - unaligned = PtrToUlong(String) & 0xf; - z = _mm_setzero_si128(); - - if (unaligned != 0) - { - b = _mm_load_si128((__m128i *)p); - b = _mm_cmpeq_epi16(b, z); - mask = _mm_movemask_epi8(b) >> unaligned; - - if (_BitScanForward(&index, mask)) - return index / sizeof(WCHAR); - - p += 16 / sizeof(WCHAR); - } - - while (TRUE) - { - b = _mm_load_si128((__m128i *)p); - b = _mm_cmpeq_epi16(b, z); - mask = _mm_movemask_epi8(b); - - if (_BitScanForward(&index, mask)) - return (SIZE_T)(p - String) + index / sizeof(WCHAR); - - p += 16 / sizeof(WCHAR); - } - } - else - { - return wcslen(String); - } -} - -/** - * Allocates space for and copies a byte string. - * - * \param String The string to duplicate. - * - * \return The new string, which can be freed using PhFree(). - */ -PSTR PhDuplicateBytesZ( - _In_ PSTR String - ) -{ - PSTR newString; - SIZE_T length; - - length = strlen(String) + 1; // include the null terminator - - newString = PhAllocate(length); - memcpy(newString, String, length); - - return newString; -} - -/** - * Allocates space for and copies a byte string. - * - * \param String The string to duplicate. - * - * \return The new string, which can be freed using PhFree(), or NULL if storage could not be - * allocated. - */ -PSTR PhDuplicateBytesZSafe( - _In_ PSTR String - ) -{ - PSTR newString; - SIZE_T length; - - length = strlen(String) + 1; // include the null terminator - - newString = PhAllocateSafe(length); - - if (!newString) - return NULL; - - memcpy(newString, String, length); - - return newString; -} - -/** - * Allocates space for and copies a 16-bit string. - * - * \param String The string to duplicate. - * - * \return The new string, which can be freed using PhFree(). - */ -PWSTR PhDuplicateStringZ( - _In_ PWSTR String - ) -{ - PWSTR newString; - SIZE_T length; - - length = (PhCountStringZ(String) + 1) * sizeof(WCHAR); // include the null terminator - - newString = PhAllocate(length); - memcpy(newString, String, length); - - return newString; -} - -/** - * Copies a string with optional null termination and a maximum number of characters. - * - * \param InputBuffer A pointer to the input string. - * \param InputCount The maximum number of characters to copy, not including the null terminator. - * Specify -1 for no limit. - * \param OutputBuffer A pointer to the output buffer. - * \param OutputCount The number of characters in the output buffer, including the null terminator. - * \param ReturnCount A variable which receives the number of characters required to contain the - * input string, including the null terminator. If the function returns TRUE, this variable contains - * the number of characters written to the output buffer. - * - * \return TRUE if the input string was copied to the output string, otherwise FALSE. - * - * \remarks The function stops copying when it encounters the first null character in the input - * string. It will also stop copying when it reaches the character count specified in \a InputCount, - * if \a InputCount is not -1. - */ -BOOLEAN PhCopyBytesZ( - _In_ PSTR InputBuffer, - _In_ SIZE_T InputCount, - _Out_writes_opt_z_(OutputCount) PSTR OutputBuffer, - _In_ SIZE_T OutputCount, - _Out_opt_ PSIZE_T ReturnCount - ) -{ - SIZE_T i; - BOOLEAN copied; - - // Determine the length of the input string. - - if (InputCount != -1) - { - i = 0; - - while (i < InputCount && InputBuffer[i]) - i++; - } - else - { - i = strlen(InputBuffer); - } - - // Copy the string if there is enough room. - - if (OutputBuffer && OutputCount >= i + 1) // need one character for null terminator - { - memcpy(OutputBuffer, InputBuffer, i); - OutputBuffer[i] = 0; - copied = TRUE; - } - else - { - copied = FALSE; - } - - if (ReturnCount) - *ReturnCount = i + 1; - - return copied; -} - -/** - * Copies a string with optional null termination and a maximum number of characters. - * - * \param InputBuffer A pointer to the input string. - * \param InputCount The maximum number of characters to copy, not including the null terminator. - * Specify -1 for no limit. - * \param OutputBuffer A pointer to the output buffer. - * \param OutputCount The number of characters in the output buffer, including the null terminator. - * \param ReturnCount A variable which receives the number of characters required to contain the - * input string, including the null terminator. If the function returns TRUE, this variable contains - * the number of characters written to the output buffer. - * - * \return TRUE if the input string was copied to the output string, otherwise FALSE. - * - * \remarks The function stops copying when it encounters the first null character in the input - * string. It will also stop copying when it reaches the character count specified in \a InputCount, - * if \a InputCount is not -1. - */ -BOOLEAN PhCopyStringZ( - _In_ PWSTR InputBuffer, - _In_ SIZE_T InputCount, - _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer, - _In_ SIZE_T OutputCount, - _Out_opt_ PSIZE_T ReturnCount - ) -{ - SIZE_T i; - BOOLEAN copied; - - // Determine the length of the input string. - - if (InputCount != -1) - { - i = 0; - - while (i < InputCount && InputBuffer[i]) - i++; - } - else - { - i = PhCountStringZ(InputBuffer); - } - - // Copy the string if there is enough room. - - if (OutputBuffer && OutputCount >= i + 1) // need one character for null terminator - { - memcpy(OutputBuffer, InputBuffer, i * sizeof(WCHAR)); - OutputBuffer[i] = 0; - copied = TRUE; - } - else - { - copied = FALSE; - } - - if (ReturnCount) - *ReturnCount = i + 1; - - return copied; -} - -/** - * Copies a string with optional null termination and a maximum number of characters. - * - * \param InputBuffer A pointer to the input string. - * \param InputCount The maximum number of characters to copy, not including the null terminator. - * Specify -1 for no limit. - * \param OutputBuffer A pointer to the output buffer. - * \param OutputCount The number of characters in the output buffer, including the null terminator. - * \param ReturnCount A variable which receives the number of characters required to contain the - * input string, including the null terminator. If the function returns TRUE, this variable contains - * the number of characters written to the output buffer. - * - * \return TRUE if the input string was copied to the output string, otherwise FALSE. - * - * \remarks The function stops copying when it encounters the first null character in the input - * string. It will also stop copying when it reaches the character count specified in \a InputCount, - * if \a InputCount is not -1. - */ -BOOLEAN PhCopyStringZFromBytes( - _In_ PSTR InputBuffer, - _In_ SIZE_T InputCount, - _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer, - _In_ SIZE_T OutputCount, - _Out_opt_ PSIZE_T ReturnCount - ) -{ - SIZE_T i; - BOOLEAN copied; - - // Determine the length of the input string. - - if (InputCount != -1) - { - i = 0; - - while (i < InputCount && InputBuffer[i]) - i++; - } - else - { - i = strlen(InputBuffer); - } - - // Copy the string if there is enough room. - - if (OutputBuffer && OutputCount >= i + 1) // need one character for null terminator - { - PhZeroExtendToUtf16Buffer(InputBuffer, i, OutputBuffer); - OutputBuffer[i] = 0; - copied = TRUE; - } - else - { - copied = FALSE; - } - - if (ReturnCount) - *ReturnCount = i + 1; - - return copied; -} - -/** - * Copies a string with optional null termination and a maximum number of characters. - * - * \param InputBuffer A pointer to the input string. - * \param InputCount The maximum number of characters to copy, not including the null terminator. - * Specify -1 for no limit. - * \param OutputBuffer A pointer to the output buffer. - * \param OutputCount The number of characters in the output buffer, including the null terminator. - * \param ReturnCount A variable which receives the number of characters required to contain the - * input string, including the null terminator. If the function returns TRUE, this variable contains - * the number of characters written to the output buffer. - * - * \return TRUE if the input string was copied to the output string, otherwise FALSE. - * - * \remarks The function stops copying when it encounters the first null character in the input - * string. It will also stop copying when it reaches the character count specified in \a InputCount, - * if \a InputCount is not -1. - */ -BOOLEAN PhCopyStringZFromMultiByte( - _In_ PSTR InputBuffer, - _In_ SIZE_T InputCount, - _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer, - _In_ SIZE_T OutputCount, - _Out_opt_ PSIZE_T ReturnCount - ) -{ - NTSTATUS status; - SIZE_T i; - ULONG unicodeBytes; - BOOLEAN copied; - - // Determine the length of the input string. - - if (InputCount != -1) - { - i = 0; - - while (i < InputCount && InputBuffer[i]) - i++; - } - else - { - i = strlen(InputBuffer); - } - - // Determine the length of the output string. - - status = RtlMultiByteToUnicodeSize( - &unicodeBytes, - InputBuffer, - (ULONG)i - ); - - if (!NT_SUCCESS(status)) - { - if (ReturnCount) - *ReturnCount = -1; - - return FALSE; - } - - // Convert the string to Unicode if there is enough room. - - if (OutputBuffer && OutputCount >= unicodeBytes / sizeof(WCHAR) + 1) - { - status = RtlMultiByteToUnicodeN( - OutputBuffer, - unicodeBytes, - NULL, - InputBuffer, - (ULONG)i - ); - - if (NT_SUCCESS(status)) - { - // RtlMultiByteToUnicodeN doesn't null terminate the string. - *(PWCHAR)((PCHAR)OutputBuffer + unicodeBytes) = 0; - copied = TRUE; - } - else - { - copied = FALSE; - } - } - else - { - copied = FALSE; - } - - if (ReturnCount) - *ReturnCount = unicodeBytes / sizeof(WCHAR) + 1; - - return copied; -} - -FORCEINLINE LONG PhpCompareRightNatural( - _In_ PWSTR A, - _In_ PWSTR B - ) -{ - LONG bias = 0; - - for (; ; A++, B++) - { - if (!PhIsDigitCharacter(*A) && !PhIsDigitCharacter(*B)) - { - return bias; - } - else if (!PhIsDigitCharacter(*A)) - { - return -1; - } - else if (!PhIsDigitCharacter(*B)) - { - return 1; - } - else if (*A < *B) - { - if (bias == 0) - bias = -1; - } - else if (*A > *B) - { - if (bias == 0) - bias = 1; - } - else if (!*A && !*B) - { - return bias; - } - } - - return 0; -} - -FORCEINLINE LONG PhpCompareLeftNatural( - _In_ PWSTR A, - _In_ PWSTR B - ) -{ - for (; ; A++, B++) - { - if (!PhIsDigitCharacter(*A) && !PhIsDigitCharacter(*B)) - { - return 0; - } - else if (!PhIsDigitCharacter(*A)) - { - return -1; - } - else if (!PhIsDigitCharacter(*B)) - { - return 1; - } - else if (*A < *B) - { - return -1; - } - else if (*A > *B) - { - return 1; - } - } - - return 0; -} - -FORCEINLINE LONG PhpCompareStringZNatural( - _In_ PWSTR A, - _In_ PWSTR B, - _In_ BOOLEAN IgnoreCase - ) -{ - /* strnatcmp.c -- Perform 'natural order' comparisons of strings in C. - Copyright (C) 2000, 2004 by Martin Pool - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - This code has been modified for Process Hacker. - */ - - ULONG ai, bi; - WCHAR ca, cb; - LONG result; - BOOLEAN fractional; - - ai = 0; - bi = 0; - - while (TRUE) - { - ca = A[ai]; - cb = B[bi]; - - /* Skip over leading spaces or zeros. */ - - while (ca == ' ') - ca = A[++ai]; - - while (cb == ' ') - cb = B[++bi]; - - /* Process run of digits. */ - if (PhIsDigitCharacter(ca) && PhIsDigitCharacter(cb)) - { - fractional = (ca == '0' || cb == '0'); - - if (fractional) - { - if ((result = PhpCompareLeftNatural(A + ai, B + bi)) != 0) - return result; - } - else - { - if ((result = PhpCompareRightNatural(A + ai, B + bi)) != 0) - return result; - } - } - - if (!ca && !cb) - { - /* Strings are considered the same. */ - return 0; - } - - if (IgnoreCase) - { - ca = towupper(ca); - cb = towupper(cb); - } - - if (ca < cb) - return -1; - else if (ca > cb) - return 1; - - ai++; - bi++; - } -} - -/** - * Compares two strings in natural sort order. - * - * \param A The first string. - * \param B The second string. - * \param IgnoreCase Whether to ignore character cases. - */ -LONG PhCompareStringZNatural( - _In_ PWSTR A, - _In_ PWSTR B, - _In_ BOOLEAN IgnoreCase - ) -{ - if (!IgnoreCase) - return PhpCompareStringZNatural(A, B, FALSE); - else - return PhpCompareStringZNatural(A, B, TRUE); -} - -/** - * Compares two strings. - * - * \param String1 The first string. - * \param String2 The second string. - * \param IgnoreCase TRUE to perform a case-insensitive comparison, otherwise FALSE. - */ -LONG PhCompareStringRef( - _In_ PPH_STRINGREF String1, - _In_ PPH_STRINGREF String2, - _In_ BOOLEAN IgnoreCase - ) -{ - SIZE_T l1; - SIZE_T l2; - PWCHAR s1; - PWCHAR s2; - WCHAR c1; - WCHAR c2; - PWCHAR end; - - // Note: this function assumes that the difference between the lengths of the two strings can - // fit inside a LONG. - - l1 = String1->Length; - l2 = String2->Length; - assert(!(l1 & 1)); - assert(!(l2 & 1)); - s1 = String1->Buffer; - s2 = String2->Buffer; - - end = (PWCHAR)((PCHAR)s1 + (l1 <= l2 ? l1 : l2)); - - if (!IgnoreCase) - { - while (s1 != end) - { - c1 = *s1; - c2 = *s2; - - if (c1 != c2) - return (LONG)c1 - (LONG)c2; - - s1++; - s2++; - } - } - else - { - while (s1 != end) - { - c1 = *s1; - c2 = *s2; - - if (c1 != c2) - { - c1 = RtlUpcaseUnicodeChar(c1); - c2 = RtlUpcaseUnicodeChar(c2); - - if (c1 != c2) - return (LONG)c1 - (LONG)c2; - } - - s1++; - s2++; - } - } - - return (LONG)(l1 - l2); -} - -/** - * Determines if two strings are equal. - * - * \param String1 The first string. - * \param String2 The second string. - * \param IgnoreCase TRUE to perform a case-insensitive comparison, otherwise FALSE. - */ -BOOLEAN PhEqualStringRef( - _In_ PPH_STRINGREF String1, - _In_ PPH_STRINGREF String2, - _In_ BOOLEAN IgnoreCase - ) -{ - SIZE_T l1; - SIZE_T l2; - PWSTR s1; - PWSTR s2; - WCHAR c1; - WCHAR c2; - SIZE_T length; - - l1 = String1->Length; - l2 = String2->Length; - assert(!(l1 & 1)); - assert(!(l2 & 1)); - - if (l1 != l2) - return FALSE; - - s1 = String1->Buffer; - s2 = String2->Buffer; - - if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) - { - length = l1 / 16; - - if (length != 0) - { - __m128i b1; - __m128i b2; - - do - { - b1 = _mm_loadu_si128((__m128i *)s1); - b2 = _mm_loadu_si128((__m128i *)s2); - b1 = _mm_cmpeq_epi32(b1, b2); - - if (_mm_movemask_epi8(b1) != 0xffff) - { - if (!IgnoreCase) - { - return FALSE; - } - else - { - // Compare character-by-character to ignore case. - l1 = length * 16 + (l1 & 15); - l1 /= sizeof(WCHAR); - goto CompareCharacters; - } - } - - s1 += 16 / sizeof(WCHAR); - s2 += 16 / sizeof(WCHAR); - } while (--length != 0); - } - - // Compare character-by-character because we have no more 16-byte blocks to compare. - l1 = (l1 & 15) / sizeof(WCHAR); - } - else - { - length = l1 / sizeof(ULONG_PTR); - - if (length != 0) - { - do - { - if (*(PULONG_PTR)s1 != *(PULONG_PTR)s2) - { - if (!IgnoreCase) - { - return FALSE; - } - else - { - // Compare character-by-character to ignore case. - l1 = length * sizeof(ULONG_PTR) + (l1 & (sizeof(ULONG_PTR) - 1)); - l1 /= sizeof(WCHAR); - goto CompareCharacters; - } - } - - s1 += sizeof(ULONG_PTR) / sizeof(WCHAR); - s2 += sizeof(ULONG_PTR) / sizeof(WCHAR); - } while (--length != 0); - } - - // Compare character-by-character because we have no more ULONG_PTR blocks to compare. - l1 = (l1 & (sizeof(ULONG_PTR) - 1)) / sizeof(WCHAR); - } - -CompareCharacters: - if (l1 != 0) - { - if (!IgnoreCase) - { - do - { - c1 = *s1; - c2 = *s2; - - if (c1 != c2) - return FALSE; - - s1++; - s2++; - } while (--l1 != 0); - } - else - { - do - { - c1 = *s1; - c2 = *s2; - - if (c1 != c2) - { - c1 = RtlUpcaseUnicodeChar(c1); - c2 = RtlUpcaseUnicodeChar(c2); - - if (c1 != c2) - return FALSE; - } - - s1++; - s2++; - } while (--l1 != 0); - } - } - - return TRUE; -} - -/** - * Locates a character in a string. - * - * \param String The string to search. - * \param Character The character to search for. - * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. - * - * \return The index, in characters, of the first occurrence of \a Character in \a String1. If - * \a Character was not found, -1 is returned. - */ -ULONG_PTR PhFindCharInStringRef( - _In_ PPH_STRINGREF String, - _In_ WCHAR Character, - _In_ BOOLEAN IgnoreCase - ) -{ - PWSTR buffer; - SIZE_T length; - - buffer = String->Buffer; - length = String->Length / sizeof(WCHAR); - - if (!IgnoreCase) - { - if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) - { - SIZE_T length16; - - length16 = String->Length / 16; - length &= 7; - - if (length16 != 0) - { - __m128i pattern; - __m128i block; - ULONG mask; - ULONG index; - - pattern = _mm_set1_epi16(Character); - - do - { - block = _mm_loadu_si128((__m128i *)buffer); - block = _mm_cmpeq_epi16(block, pattern); - mask = _mm_movemask_epi8(block); - - if (_BitScanForward(&index, mask)) - return (String->Length - length16 * 16) / sizeof(WCHAR) - length + index / 2; - - buffer += 16 / sizeof(WCHAR); - } while (--length16 != 0); - } - } - - if (length != 0) - { - do - { - if (*buffer == Character) - return String->Length / sizeof(WCHAR) - length; - - buffer++; - } while (--length != 0); - } - } - else - { - if (length != 0) - { - WCHAR c; - - c = RtlUpcaseUnicodeChar(Character); - - do - { - if (RtlUpcaseUnicodeChar(*buffer) == c) - return String->Length / sizeof(WCHAR) - length; - - buffer++; - } while (--length != 0); - } - } - - return -1; -} - -/** - * Locates a character in a string, searching backwards. - * - * \param String The string to search. - * \param Character The character to search for. - * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. - * - * \return The index, in characters, of the last occurrence of \a Character in \a String1. If - * \a Character was not found, -1 is returned. - */ -ULONG_PTR PhFindLastCharInStringRef( - _In_ PPH_STRINGREF String, - _In_ WCHAR Character, - _In_ BOOLEAN IgnoreCase - ) -{ - PWCHAR buffer; - SIZE_T length; - - buffer = (PWCHAR)((PCHAR)String->Buffer + String->Length); - length = String->Length / sizeof(WCHAR); - - if (!IgnoreCase) - { - if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) - { - SIZE_T length16; - - length16 = String->Length / 16; - length &= 7; - - if (length16 != 0) - { - __m128i pattern; - __m128i block; - ULONG mask; - ULONG index; - - pattern = _mm_set1_epi16(Character); - buffer -= 16 / sizeof(WCHAR); - - do - { - block = _mm_loadu_si128((__m128i *)buffer); - block = _mm_cmpeq_epi16(block, pattern); - mask = _mm_movemask_epi8(block); - - if (_BitScanReverse(&index, mask)) - return (length16 - 1) * 16 / sizeof(WCHAR) + length + index / 2; - - buffer -= 16 / sizeof(WCHAR); - } while (--length16 != 0); - - buffer += 16 / sizeof(WCHAR); - } - } - - if (length != 0) - { - buffer--; - - do - { - if (*buffer == Character) - return length - 1; - - buffer--; - } while (--length != 0); - } - } - else - { - if (length != 0) - { - WCHAR c; - - c = RtlUpcaseUnicodeChar(Character); - buffer--; - - do - { - if (RtlUpcaseUnicodeChar(*buffer) == c) - return length - 1; - - buffer--; - } while (--length != 0); - } - } - - return -1; -} - -/** - * Locates a string in a string. - * - * \param String The string to search. - * \param SubString The string to search for. - * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. - * - * \return The index, in characters, of the first occurrence of \a SubString in \a String. If - * \a SubString was not found, -1 is returned. - */ -ULONG_PTR PhFindStringInStringRef( - _In_ PPH_STRINGREF String, - _In_ PPH_STRINGREF SubString, - _In_ BOOLEAN IgnoreCase - ) -{ - SIZE_T length1; - SIZE_T length2; - PH_STRINGREF sr1; - PH_STRINGREF sr2; - WCHAR c; - SIZE_T i; - - length1 = String->Length / sizeof(WCHAR); - length2 = SubString->Length / sizeof(WCHAR); - - // Can't be a substring if it's bigger than the first string. - if (length2 > length1) - return -1; - // We always get a match if the substring is zero-length. - if (length2 == 0) - return 0; - - sr1.Buffer = String->Buffer; - sr1.Length = SubString->Length - sizeof(WCHAR); - sr2.Buffer = SubString->Buffer; - sr2.Length = SubString->Length - sizeof(WCHAR); - - if (!IgnoreCase) - { - c = *sr2.Buffer++; - - for (i = length1 - length2 + 1; i != 0; i--) - { - if (*sr1.Buffer++ == c && PhEqualStringRef(&sr1, &sr2, FALSE)) - { - goto FoundUString; - } - } - } - else - { - c = RtlUpcaseUnicodeChar(*sr2.Buffer++); - - for (i = length1 - length2 + 1; i != 0; i--) - { - if (RtlUpcaseUnicodeChar(*sr1.Buffer++) == c && PhEqualStringRef(&sr1, &sr2, TRUE)) - { - goto FoundUString; - } - } - } - - return -1; -FoundUString: - return (ULONG_PTR)(sr1.Buffer - String->Buffer - 1); -} - -/** - * Splits a string. - * - * \param Input The input string. - * \param Separator The character to split at. - * \param FirstPart A variable which receives the part of \a Input before the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * \a Input. - * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * an empty string. - * - * \return TRUE if \a Separator was found in \a Input, otherwise FALSE. - */ -BOOLEAN PhSplitStringRefAtChar( - _In_ PPH_STRINGREF Input, - _In_ WCHAR Separator, - _Out_ PPH_STRINGREF FirstPart, - _Out_ PPH_STRINGREF SecondPart - ) -{ - PH_STRINGREF input; - ULONG_PTR index; - - input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input - index = PhFindCharInStringRef(Input, Separator, FALSE); - - if (index == -1) - { - // The separator was not found. - - FirstPart->Buffer = Input->Buffer; - FirstPart->Length = Input->Length; - SecondPart->Buffer = NULL; - SecondPart->Length = 0; - - return FALSE; - } - - FirstPart->Buffer = input.Buffer; - FirstPart->Length = index * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + sizeof(WCHAR)); - SecondPart->Length = input.Length - index * sizeof(WCHAR) - sizeof(WCHAR); - - return TRUE; -} - -/** - * Splits a string at the last occurrence of a character. - * - * \param Input The input string. - * \param Separator The character to split at. - * \param FirstPart A variable which receives the part of \a Input before the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * \a Input. - * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * an empty string. - * - * \return TRUE if \a Separator was found in \a Input, otherwise FALSE. - */ -BOOLEAN PhSplitStringRefAtLastChar( - _In_ PPH_STRINGREF Input, - _In_ WCHAR Separator, - _Out_ PPH_STRINGREF FirstPart, - _Out_ PPH_STRINGREF SecondPart - ) -{ - PH_STRINGREF input; - ULONG_PTR index; - - input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input - index = PhFindLastCharInStringRef(Input, Separator, FALSE); - - if (index == -1) - { - // The separator was not found. - - FirstPart->Buffer = Input->Buffer; - FirstPart->Length = Input->Length; - SecondPart->Buffer = NULL; - SecondPart->Length = 0; - - return FALSE; - } - - FirstPart->Buffer = input.Buffer; - FirstPart->Length = index * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + sizeof(WCHAR)); - SecondPart->Length = input.Length - index * sizeof(WCHAR) - sizeof(WCHAR); - - return TRUE; -} - -/** - * Splits a string. - * - * \param Input The input string. - * \param Separator The string to split at. - * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. - * \param FirstPart A variable which receives the part of \a Input before the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * \a Input. - * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * an empty string. - * - * \return TRUE if \a Separator was found in \a Input, otherwise FALSE. - */ -BOOLEAN PhSplitStringRefAtString( - _In_ PPH_STRINGREF Input, - _In_ PPH_STRINGREF Separator, - _In_ BOOLEAN IgnoreCase, - _Out_ PPH_STRINGREF FirstPart, - _Out_ PPH_STRINGREF SecondPart - ) -{ - PH_STRINGREF input; - ULONG_PTR index; - - input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input - index = PhFindStringInStringRef(Input, Separator, IgnoreCase); - - if (index == -1) - { - // The separator was not found. - - FirstPart->Buffer = Input->Buffer; - FirstPart->Length = Input->Length; - SecondPart->Buffer = NULL; - SecondPart->Length = 0; - - return FALSE; - } - - FirstPart->Buffer = input.Buffer; - FirstPart->Length = index * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + Separator->Length); - SecondPart->Length = input.Length - index * sizeof(WCHAR) - Separator->Length; - - return TRUE; -} - -/** - * Splits a string. - * - * \param Input The input string. - * \param Separator The character set, string or range to split at. - * \param Flags A combination of flags. - * \li \c PH_SPLIT_AT_CHAR_SET \a Separator specifies a character set. \a Input will be split at a - * character which is contained in \a Separator. - * \li \c PH_SPLIT_AT_STRING \a Separator specifies a string. \a Input will be split an occurrence - * of \a Separator. - * \li \c PH_SPLIT_AT_RANGE \a Separator specifies a range. The \a Buffer field contains a character - * index cast to \c PWSTR and the \a Length field contains the length of the range, in bytes. - * \li \c PH_SPLIT_CASE_INSENSITIVE Specifies a case-insensitive search. - * \li \c PH_SPLIT_COMPLEMENT_CHAR_SET If used with \c PH_SPLIT_AT_CHAR_SET, the separator is a - * character which is not contained in \a Separator. - * \li \c PH_SPLIT_START_AT_END If used with \c PH_SPLIT_AT_CHAR_SET, the search is performed - * starting from the end of the string. - * \li \c PH_SPLIT_CHAR_SET_IS_UPPERCASE If used with \c PH_SPLIT_CASE_INSENSITIVE, specifies that - * the character set in \a Separator contains only uppercase characters. - * \param FirstPart A variable which receives the part of \a Input before the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * \a Input. - * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be - * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to - * an empty string. - * \param SeparatorPart A variable which receives the part of \a Input that is the separator. If the - * separator is not found in \a Input, this variable is set to an empty string. - * - * \return TRUE if a separator was found in \a Input, otherwise FALSE. - */ -BOOLEAN PhSplitStringRefEx( - _In_ PPH_STRINGREF Input, - _In_ PPH_STRINGREF Separator, - _In_ ULONG Flags, - _Out_ PPH_STRINGREF FirstPart, - _Out_ PPH_STRINGREF SecondPart, - _Out_opt_ PPH_STRINGREF SeparatorPart - ) -{ - PH_STRINGREF input; - SIZE_T separatorIndex; - SIZE_T separatorLength; - PWCHAR charSet; - SIZE_T charSetCount; - BOOLEAN charSetTable[256]; - BOOLEAN charSetTableComplete; - SIZE_T i; - SIZE_T j; - USHORT c; - PWCHAR s; - LONG_PTR direction; - - input = *Input; // Get a copy of the input because FirstPart/SecondPart/SeparatorPart may alias Input - - if (Flags & PH_SPLIT_AT_RANGE) - { - separatorIndex = (SIZE_T)Separator->Buffer; - separatorLength = Separator->Length; - - if (separatorIndex == -1) - goto SeparatorNotFound; - - goto SeparatorFound; - } - else if (Flags & PH_SPLIT_AT_STRING) - { - if (Flags & PH_SPLIT_START_AT_END) - { - // not implemented - goto SeparatorNotFound; - } - - separatorIndex = PhFindStringInStringRef(Input, Separator, !!(Flags & PH_SPLIT_CASE_INSENSITIVE)); - - if (separatorIndex == -1) - goto SeparatorNotFound; - - separatorLength = Separator->Length; - goto SeparatorFound; - } - - // Special case for character sets with only one character. - if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET) && Separator->Length == sizeof(WCHAR)) - { - if (!(Flags & PH_SPLIT_START_AT_END)) - separatorIndex = PhFindCharInStringRef(Input, Separator->Buffer[0], !!(Flags & PH_SPLIT_CASE_INSENSITIVE)); - else - separatorIndex = PhFindLastCharInStringRef(Input, Separator->Buffer[0], !!(Flags & PH_SPLIT_CASE_INSENSITIVE)); - - if (separatorIndex == -1) - goto SeparatorNotFound; - - separatorLength = sizeof(WCHAR); - goto SeparatorFound; - } - - if (input.Length == 0) - goto SeparatorNotFound; - - // Build the character set lookup table. - - charSet = Separator->Buffer; - charSetCount = Separator->Length / sizeof(WCHAR); - memset(charSetTable, 0, sizeof(charSetTable)); - charSetTableComplete = TRUE; - - for (i = 0; i < charSetCount; i++) - { - c = charSet[i]; - - if (Flags & PH_SPLIT_CASE_INSENSITIVE) - c = RtlUpcaseUnicodeChar(c); - - charSetTable[c & 0xff] = TRUE; - - if (c >= 256) - charSetTableComplete = FALSE; - } - - // Perform the search. - - i = input.Length / sizeof(WCHAR); - separatorLength = sizeof(WCHAR); - - if (!(Flags & PH_SPLIT_START_AT_END)) - { - s = input.Buffer; - direction = 1; - } - else - { - s = (PWCHAR)((PCHAR)input.Buffer + input.Length - sizeof(WCHAR)); - direction = -1; - } - - do - { - c = *s; - - if (Flags & PH_SPLIT_CASE_INSENSITIVE) - c = RtlUpcaseUnicodeChar(c); - - if (c < 256 && charSetTableComplete) - { - if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET)) - { - if (charSetTable[c]) - goto CharFound; - } - else - { - if (!charSetTable[c]) - goto CharFound; - } - } - else - { - if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET)) - { - if (charSetTable[c & 0xff]) - { - if (!(Flags & PH_SPLIT_CASE_INSENSITIVE) || (Flags & PH_SPLIT_CHAR_SET_IS_UPPERCASE)) - { - for (j = 0; j < charSetCount; j++) - { - if (charSet[j] == c) - goto CharFound; - } - } - else - { - for (j = 0; j < charSetCount; j++) - { - if (RtlUpcaseUnicodeChar(charSet[j]) == c) - goto CharFound; - } - } - } - } - else - { - if (charSetTable[c & 0xff]) - { - if (!(Flags & PH_SPLIT_CASE_INSENSITIVE) || (Flags & PH_SPLIT_CHAR_SET_IS_UPPERCASE)) - { - for (j = 0; j < charSetCount; j++) - { - if (charSet[j] == c) - break; - } - } - else - { - for (j = 0; j < charSetCount; j++) - { - if (RtlUpcaseUnicodeChar(charSet[j]) == c) - break; - } - } - - if (j == charSetCount) - goto CharFound; - } - else - { - goto CharFound; - } - } - } - - s += direction; - } while (--i != 0); - - goto SeparatorNotFound; - -CharFound: - separatorIndex = s - input.Buffer; - -SeparatorFound: - FirstPart->Buffer = input.Buffer; - FirstPart->Length = separatorIndex * sizeof(WCHAR); - SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + separatorIndex * sizeof(WCHAR) + separatorLength); - SecondPart->Length = input.Length - separatorIndex * sizeof(WCHAR) - separatorLength; - - if (SeparatorPart) - { - SeparatorPart->Buffer = input.Buffer + separatorIndex; - SeparatorPart->Length = separatorLength; - } - - return TRUE; - -SeparatorNotFound: - FirstPart->Buffer = input.Buffer; - FirstPart->Length = input.Length; - SecondPart->Buffer = NULL; - SecondPart->Length = 0; - - if (SeparatorPart) - { - SeparatorPart->Buffer = NULL; - SeparatorPart->Length = 0; - } - - return FALSE; -} - -VOID PhTrimStringRef( - _Inout_ PPH_STRINGREF String, - _In_ PPH_STRINGREF CharSet, - _In_ ULONG Flags - ) -{ - PWCHAR charSet; - SIZE_T charSetCount; - BOOLEAN charSetTable[256]; - BOOLEAN charSetTableComplete; - SIZE_T i; - SIZE_T j; - USHORT c; - SIZE_T trimCount; - SIZE_T count; - PWCHAR s; - - if (String->Length == 0 || CharSet->Length == 0) - return; - - if (CharSet->Length == sizeof(WCHAR)) - { - c = CharSet->Buffer[0]; - - if (!(Flags & PH_TRIM_END_ONLY)) - { - trimCount = 0; - count = String->Length / sizeof(WCHAR); - s = String->Buffer; - - while (count-- != 0) - { - if (*s++ != c) - break; - - trimCount++; - } - - PhSkipStringRef(String, trimCount * sizeof(WCHAR)); - } - - if (!(Flags & PH_TRIM_START_ONLY)) - { - trimCount = 0; - count = String->Length / sizeof(WCHAR); - s = (PWCHAR)((PCHAR)String->Buffer + String->Length - sizeof(WCHAR)); - - while (count-- != 0) - { - if (*s-- != c) - break; - - trimCount++; - } - - String->Length -= trimCount * sizeof(WCHAR); - } - - return; - } - - // Build the character set lookup table. - - charSet = CharSet->Buffer; - charSetCount = CharSet->Length / sizeof(WCHAR); - memset(charSetTable, 0, sizeof(charSetTable)); - charSetTableComplete = TRUE; - - for (i = 0; i < charSetCount; i++) - { - c = charSet[i]; - charSetTable[c & 0xff] = TRUE; - - if (c >= 256) - charSetTableComplete = FALSE; - } - - // Trim the string. - - if (!(Flags & PH_TRIM_END_ONLY)) - { - trimCount = 0; - count = String->Length / sizeof(WCHAR); - s = String->Buffer; - - while (count-- != 0) - { - c = *s++; - - if (!charSetTable[c & 0xff]) - break; - - if (!charSetTableComplete) - { - for (j = 0; j < charSetCount; j++) - { - if (charSet[j] == c) - goto CharFound; - } - - break; - } - -CharFound: - trimCount++; - } - - PhSkipStringRef(String, trimCount * sizeof(WCHAR)); - } - - if (!(Flags & PH_TRIM_START_ONLY)) - { - trimCount = 0; - count = String->Length / sizeof(WCHAR); - s = (PWCHAR)((PCHAR)String->Buffer + String->Length - sizeof(WCHAR)); - - while (count-- != 0) - { - c = *s--; - - if (!charSetTable[c & 0xff]) - break; - - if (!charSetTableComplete) - { - for (j = 0; j < charSetCount; j++) - { - if (charSet[j] == c) - goto CharFound2; - } - - break; - } - -CharFound2: - trimCount++; - } - - String->Length -= trimCount * sizeof(WCHAR); - } -} - -/** - * Creates a string object from an existing null-terminated string. - * - * \param Buffer A null-terminated Unicode string. - */ -PPH_STRING PhCreateString( - _In_ PWSTR Buffer - ) -{ - return PhCreateStringEx(Buffer, wcslen(Buffer) * sizeof(WCHAR)); -} - -/** - * Creates a string object using a specified length. - * - * \param Buffer A null-terminated Unicode string. - * \param Length The length, in bytes, of the string. - */ -PPH_STRING PhCreateStringEx( - _In_opt_ PWCHAR Buffer, - _In_ SIZE_T Length - ) -{ - PPH_STRING string; - - string = PhCreateObject( - FIELD_OFFSET(PH_STRING, Data) + Length + sizeof(WCHAR), // Null terminator - PhStringType - ); - - assert(!(Length & 1)); - string->Length = Length; - string->Buffer = string->Data; - *(PWCHAR)((PCHAR)string->Buffer + Length) = 0; - - if (Buffer) - { - memcpy(string->Buffer, Buffer, Length); - } - - return string; -} - -/** - * Obtains a reference to a zero-length string. - */ -PPH_STRING PhReferenceEmptyString( - VOID - ) -{ - PPH_STRING string; - PPH_STRING newString; - - string = PhSharedEmptyString; - - if (!string) - { - newString = PhCreateStringEx(NULL, 0); - - string = _InterlockedCompareExchangePointer( - &PhSharedEmptyString, - newString, - NULL - ); - - if (!string) - { - string = newString; // success - } - else - { - PhDereferenceObject(newString); - } - } - - return PhReferenceObject(string); -} - -/** - * Concatenates multiple strings. - * - * \param Count The number of strings to concatenate. - */ -PPH_STRING PhConcatStrings( - _In_ ULONG Count, - ... - ) -{ - va_list argptr; - - va_start(argptr, Count); - - return PhConcatStrings_V(Count, argptr); -} - -/** - * Concatenates multiple strings. - * - * \param Count The number of strings to concatenate. - * \param ArgPtr A pointer to an array of strings. - */ -PPH_STRING PhConcatStrings_V( - _In_ ULONG Count, - _In_ va_list ArgPtr - ) -{ - va_list argptr; - ULONG i; - SIZE_T totalLength = 0; - SIZE_T stringLength; - SIZE_T cachedLengths[PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE]; - PWSTR arg; - PPH_STRING string; - - // Compute the total length, in bytes, of the strings. - - argptr = ArgPtr; - - for (i = 0; i < Count; i++) - { - arg = va_arg(argptr, PWSTR); - stringLength = PhCountStringZ(arg) * sizeof(WCHAR); - totalLength += stringLength; - - if (i < PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE) - cachedLengths[i] = stringLength; - } - - // Create the string. - - string = PhCreateStringEx(NULL, totalLength); - totalLength = 0; - - // Append the strings one by one. - - argptr = ArgPtr; - - for (i = 0; i < Count; i++) - { - arg = va_arg(argptr, PWSTR); - - if (i < PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE) - stringLength = cachedLengths[i]; - else - stringLength = PhCountStringZ(arg) * sizeof(WCHAR); - - memcpy( - (PCHAR)string->Buffer + totalLength, - arg, - stringLength - ); - totalLength += stringLength; - } - - return string; -} - -/** - * Concatenates two strings. - * - * \param String1 The first string. - * \param String2 The second string. - */ -PPH_STRING PhConcatStrings2( - _In_ PWSTR String1, - _In_ PWSTR String2 - ) -{ - PPH_STRING string; - SIZE_T length1; - SIZE_T length2; - - length1 = PhCountStringZ(String1) * sizeof(WCHAR); - length2 = PhCountStringZ(String2) * sizeof(WCHAR); - string = PhCreateStringEx(NULL, length1 + length2); - memcpy( - string->Buffer, - String1, - length1 - ); - memcpy( - (PCHAR)string->Buffer + length1, - String2, - length2 - ); - - return string; -} - -/** - * Concatenates two strings. - * - * \param String1 The first string. - * \param String2 The second string. - */ -PPH_STRING PhConcatStringRef2( - _In_ PPH_STRINGREF String1, - _In_ PPH_STRINGREF String2 - ) -{ - PPH_STRING string; - - assert(!(String1->Length & 1)); - assert(!(String2->Length & 1)); - - string = PhCreateStringEx(NULL, String1->Length + String2->Length); - memcpy(string->Buffer, String1->Buffer, String1->Length); - memcpy((PCHAR)string->Buffer + String1->Length, String2->Buffer, String2->Length); - - return string; -} - -/** - * Concatenates three strings. - * - * \param String1 The first string. - * \param String2 The second string. - * \param String3 The third string. - */ -PPH_STRING PhConcatStringRef3( - _In_ PPH_STRINGREF String1, - _In_ PPH_STRINGREF String2, - _In_ PPH_STRINGREF String3 - ) -{ - PPH_STRING string; - PCHAR buffer; - - assert(!(String1->Length & 1)); - assert(!(String2->Length & 1)); - assert(!(String3->Length & 1)); - - string = PhCreateStringEx(NULL, String1->Length + String2->Length + String3->Length); - - buffer = (PCHAR)string->Buffer; - memcpy(buffer, String1->Buffer, String1->Length); - - buffer += String1->Length; - memcpy(buffer, String2->Buffer, String2->Length); - - buffer += String2->Length; - memcpy(buffer, String3->Buffer, String3->Length); - - return string; -} - -/** - * Creates a string using format specifiers. - * - * \param Format The format-control string. - */ -PPH_STRING PhFormatString( - _In_ _Printf_format_string_ PWSTR Format, - ... - ) -{ - va_list argptr; - - va_start(argptr, Format); - - return PhFormatString_V(Format, argptr); -} - -/** - * Creates a string using format specifiers. - * - * \param Format The format-control string. - * \param ArgPtr A pointer to the list of arguments. - */ -PPH_STRING PhFormatString_V( - _In_ _Printf_format_string_ PWSTR Format, - _In_ va_list ArgPtr - ) -{ - PPH_STRING string; - int length; - - length = _vscwprintf(Format, ArgPtr); - - if (length == -1) - return NULL; - - string = PhCreateStringEx(NULL, length * sizeof(WCHAR)); - _vsnwprintf(string->Buffer, length, Format, ArgPtr); - - return string; -} - -/** - * Creates a bytes object from an existing null-terminated string of bytes. - * - * \param Buffer A null-terminated byte string. - */ -PPH_BYTES PhCreateBytes( - _In_ PSTR Buffer - ) -{ - return PhCreateBytesEx(Buffer, strlen(Buffer) * sizeof(CHAR)); -} - -/** - * Creates a bytes object. - * - * \param Buffer An array of bytes. - * \param Length The length of \a Buffer, in bytes. - */ -PPH_BYTES PhCreateBytesEx( - _In_opt_ PCHAR Buffer, - _In_ SIZE_T Length - ) -{ - PPH_BYTES bytes; - - bytes = PhCreateObject( - FIELD_OFFSET(PH_BYTES, Data) + Length + sizeof(CHAR), // Null terminator for compatibility - PhBytesType - ); - - bytes->Length = Length; - bytes->Buffer = bytes->Data; - bytes->Buffer[Length] = 0; - - if (Buffer) - { - memcpy(bytes->Buffer, Buffer, Length); - } - - return bytes; -} - -BOOLEAN PhWriteUnicodeDecoder( - _Inout_ PPH_UNICODE_DECODER Decoder, - _In_ ULONG CodeUnit - ) -{ - switch (Decoder->Encoding) - { - case PH_UNICODE_UTF8: - if (Decoder->InputCount >= 4) - return FALSE; - Decoder->u.Utf8.Input[Decoder->InputCount] = (UCHAR)CodeUnit; - Decoder->InputCount++; - return TRUE; - case PH_UNICODE_UTF16: - if (Decoder->InputCount >= 2) - return FALSE; - Decoder->u.Utf16.Input[Decoder->InputCount] = (USHORT)CodeUnit; - Decoder->InputCount++; - return TRUE; - case PH_UNICODE_UTF32: - if (Decoder->InputCount >= 1) - return FALSE; - Decoder->u.Utf32.Input = CodeUnit; - Decoder->InputCount = 1; - return TRUE; - default: - PhRaiseStatus(STATUS_UNSUCCESSFUL); - } -} - -BOOLEAN PhpReadUnicodeDecoder( - _Inout_ PPH_UNICODE_DECODER Decoder, - _Out_ PULONG CodeUnit - ) -{ - switch (Decoder->Encoding) - { - case PH_UNICODE_UTF8: - if (Decoder->InputCount == 0) - return FALSE; - *CodeUnit = Decoder->u.Utf8.Input[0]; - Decoder->u.Utf8.Input[0] = Decoder->u.Utf8.Input[1]; - Decoder->u.Utf8.Input[1] = Decoder->u.Utf8.Input[2]; - Decoder->u.Utf8.Input[2] = Decoder->u.Utf8.Input[3]; - Decoder->InputCount--; - return TRUE; - case PH_UNICODE_UTF16: - if (Decoder->InputCount == 0) - return FALSE; - *CodeUnit = Decoder->u.Utf16.Input[0]; - Decoder->u.Utf16.Input[0] = Decoder->u.Utf16.Input[1]; - Decoder->InputCount--; - return TRUE; - case PH_UNICODE_UTF32: - if (Decoder->InputCount == 0) - return FALSE; - *CodeUnit = Decoder->u.Utf32.Input; - Decoder->InputCount--; - return TRUE; - default: - PhRaiseStatus(STATUS_UNSUCCESSFUL); - } -} - -VOID PhpUnreadUnicodeDecoder( - _Inout_ PPH_UNICODE_DECODER Decoder, - _In_ ULONG CodeUnit - ) -{ - switch (Decoder->Encoding) - { - case PH_UNICODE_UTF8: - if (Decoder->InputCount >= 4) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - Decoder->u.Utf8.Input[3] = Decoder->u.Utf8.Input[2]; - Decoder->u.Utf8.Input[2] = Decoder->u.Utf8.Input[1]; - Decoder->u.Utf8.Input[1] = Decoder->u.Utf8.Input[0]; - Decoder->u.Utf8.Input[0] = (UCHAR)CodeUnit; - Decoder->InputCount++; - break; - case PH_UNICODE_UTF16: - if (Decoder->InputCount >= 2) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - Decoder->u.Utf16.Input[1] = Decoder->u.Utf16.Input[0]; - Decoder->u.Utf16.Input[0] = (USHORT)CodeUnit; - Decoder->InputCount++; - break; - case PH_UNICODE_UTF32: - if (Decoder->InputCount >= 1) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - Decoder->u.Utf32.Input = CodeUnit; - Decoder->InputCount = 1; - break; - default: - PhRaiseStatus(STATUS_UNSUCCESSFUL); - } -} - -BOOLEAN PhpDecodeUtf8Error( - _Inout_ PPH_UNICODE_DECODER Decoder, - _Out_ PULONG CodePoint, - _In_ ULONG Which - ) -{ - if (Which >= 4) - PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit4); - if (Which >= 3) - PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit3); - if (Which >= 2) - PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit2); - - *CodePoint = (ULONG)Decoder->u.Utf8.CodeUnit1 + 0xdc00; - Decoder->State = 0; - - return TRUE; -} - -BOOLEAN PhDecodeUnicodeDecoder( - _Inout_ PPH_UNICODE_DECODER Decoder, - _Out_ PULONG CodePoint - ) -{ - ULONG codeUnit; - - while (TRUE) - { - switch (Decoder->Encoding) - { - case PH_UNICODE_UTF8: - if (!PhpReadUnicodeDecoder(Decoder, &codeUnit)) - return FALSE; - - switch (Decoder->State) - { - case 0: - Decoder->u.Utf8.CodeUnit1 = (UCHAR)codeUnit; - - if (codeUnit < 0x80) - { - *CodePoint = codeUnit; - return TRUE; - } - else if (codeUnit < 0xc2) - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 1); - } - else if (codeUnit < 0xe0) - { - Decoder->State = 1; // 2 byte sequence - continue; - } - else if (codeUnit < 0xf0) - { - Decoder->State = 2; // 3 byte sequence - continue; - } - else if (codeUnit < 0xf5) - { - Decoder->State = 3; // 4 byte sequence - continue; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 1); - } - - break; - case 1: // 2 byte sequence - Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit; - - if ((codeUnit & 0xc0) == 0x80) - { - *CodePoint = ((ULONG)Decoder->u.Utf8.CodeUnit1 << 6) + codeUnit - 0x3080; - Decoder->State = 0; - return TRUE; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 2); - } - - break; - case 2: // 3 byte sequence (1) - Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit; - - if (((codeUnit & 0xc0) == 0x80) && - (Decoder->u.Utf8.CodeUnit1 != 0xe0 || codeUnit >= 0xa0)) - { - Decoder->State = 4; // 3 byte sequence (2) - continue; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 2); - } - - break; - case 3: // 4 byte sequence (1) - Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit; - - if (((codeUnit & 0xc0) == 0x80) && - (Decoder->u.Utf8.CodeUnit1 != 0xf0 || codeUnit >= 0x90) && - (Decoder->u.Utf8.CodeUnit1 != 0xf4 || codeUnit < 0x90)) - { - Decoder->State = 5; // 4 byte sequence (2) - continue; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 2); - } - - break; - case 4: // 3 byte sequence (2) - Decoder->u.Utf8.CodeUnit3 = (UCHAR)codeUnit; - - if ((codeUnit & 0xc0) == 0x80) - { - *CodePoint = - ((ULONG)Decoder->u.Utf8.CodeUnit1 << 12) + - ((ULONG)Decoder->u.Utf8.CodeUnit2 << 6) + - codeUnit - 0xe2080; - Decoder->State = 0; - return TRUE; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 3); - } - - break; - case 5: // 4 byte sequence (2) - Decoder->u.Utf8.CodeUnit3 = (UCHAR)codeUnit; - - if ((codeUnit & 0xc0) == 0x80) - { - Decoder->State = 6; // 4 byte sequence (3) - continue; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 3); - } - - break; - case 6: // 4 byte sequence (3) - Decoder->u.Utf8.CodeUnit4 = (UCHAR)codeUnit; - - if ((codeUnit & 0xc0) == 0x80) - { - *CodePoint = - ((ULONG)Decoder->u.Utf8.CodeUnit1 << 18) + - ((ULONG)Decoder->u.Utf8.CodeUnit2 << 12) + - ((ULONG)Decoder->u.Utf8.CodeUnit3 << 6) + - codeUnit - 0x3c82080; - Decoder->State = 0; - return TRUE; - } - else - { - return PhpDecodeUtf8Error(Decoder, CodePoint, 4); - } - - break; - } - - return FALSE; - case PH_UNICODE_UTF16: - if (!PhpReadUnicodeDecoder(Decoder, &codeUnit)) - return FALSE; - - switch (Decoder->State) - { - case 0: - if (PH_UNICODE_UTF16_IS_HIGH_SURROGATE(codeUnit)) - { - Decoder->u.Utf16.CodeUnit = (USHORT)codeUnit; - Decoder->State = 1; - continue; - } - else - { - *CodePoint = codeUnit; - return TRUE; - } - break; - case 1: - if (PH_UNICODE_UTF16_IS_LOW_SURROGATE(codeUnit)) - { - *CodePoint = PH_UNICODE_UTF16_TO_CODE_POINT(Decoder->u.Utf16.CodeUnit, codeUnit); - Decoder->State = 0; - return TRUE; - } - else - { - *CodePoint = Decoder->u.Utf16.CodeUnit; - PhpUnreadUnicodeDecoder(Decoder, codeUnit); - Decoder->State = 0; - return TRUE; - } - break; - } - - return FALSE; - case PH_UNICODE_UTF32: - if (PhpReadUnicodeDecoder(Decoder, CodePoint)) - return TRUE; - return FALSE; - default: - return FALSE; - } - } -} - -BOOLEAN PhEncodeUnicode( - _In_ UCHAR Encoding, - _In_ ULONG CodePoint, - _Out_opt_ PVOID CodeUnits, - _Out_ PULONG NumberOfCodeUnits - ) -{ - switch (Encoding) - { - case PH_UNICODE_UTF8: - { - PUCHAR codeUnits = CodeUnits; - - if (CodePoint < 0x80) - { - *NumberOfCodeUnits = 1; - - if (codeUnits) - codeUnits[0] = (UCHAR)CodePoint; - } - else if (CodePoint <= 0x7ff) - { - *NumberOfCodeUnits = 2; - - if (codeUnits) - { - codeUnits[0] = (UCHAR)(CodePoint >> 6) + 0xc0; - codeUnits[1] = (UCHAR)(CodePoint & 0x3f) + 0x80; - } - } - else if (CodePoint <= 0xffff) - { - *NumberOfCodeUnits = 3; - - if (codeUnits) - { - codeUnits[0] = (UCHAR)(CodePoint >> 12) + 0xe0; - codeUnits[1] = (UCHAR)((CodePoint >> 6) & 0x3f) + 0x80; - codeUnits[2] = (UCHAR)(CodePoint & 0x3f) + 0x80; - } - } - else if (CodePoint <= PH_UNICODE_MAX_CODE_POINT) - { - *NumberOfCodeUnits = 4; - - if (codeUnits) - { - codeUnits[0] = (UCHAR)(CodePoint >> 18) + 0xf0; - codeUnits[1] = (UCHAR)((CodePoint >> 12) & 0x3f) + 0x80; - codeUnits[2] = (UCHAR)((CodePoint >> 6) & 0x3f) + 0x80; - codeUnits[3] = (UCHAR)(CodePoint & 0x3f) + 0x80; - } - } - else - { - return FALSE; - } - } - return TRUE; - case PH_UNICODE_UTF16: - { - PUSHORT codeUnits = CodeUnits; - - if (CodePoint < 0x10000) - { - *NumberOfCodeUnits = 1; - - if (codeUnits) - codeUnits[0] = (USHORT)CodePoint; - } - else if (CodePoint <= PH_UNICODE_MAX_CODE_POINT) - { - *NumberOfCodeUnits = 2; - - if (codeUnits) - { - codeUnits[0] = PH_UNICODE_UTF16_TO_HIGH_SURROGATE(CodePoint); - codeUnits[1] = PH_UNICODE_UTF16_TO_LOW_SURROGATE(CodePoint); - } - } - else - { - return FALSE; - } - } - return TRUE; - case PH_UNICODE_UTF32: - *NumberOfCodeUnits = 1; - if (CodeUnits) - *(PULONG)CodeUnits = CodePoint; - return TRUE; - default: - return FALSE; - } -} - -/** - * Converts an ASCII string to a UTF-16 string by zero-extending each byte. - * - * \param Input The original ASCII string. - * \param InputLength The length of \a Input. - * \param Output A buffer which will contain the converted string. - */ -VOID PhZeroExtendToUtf16Buffer( - _In_reads_bytes_(InputLength) PCH Input, - _In_ SIZE_T InputLength, - _Out_writes_bytes_(InputLength * sizeof(WCHAR)) PWCH Output - ) -{ - SIZE_T inputLength; - - inputLength = InputLength & -4; - - if (inputLength) - { - do - { - Output[0] = C_1uTo2(Input[0]); - Output[1] = C_1uTo2(Input[1]); - Output[2] = C_1uTo2(Input[2]); - Output[3] = C_1uTo2(Input[3]); - Input += 4; - Output += 4; - inputLength -= 4; - } while (inputLength != 0); - } - - switch (InputLength & 3) - { - case 3: - *Output++ = C_1uTo2(*Input++); - case 2: - *Output++ = C_1uTo2(*Input++); - case 1: - *Output++ = C_1uTo2(*Input++); - } -} - -PPH_STRING PhZeroExtendToUtf16Ex( - _In_reads_bytes_(InputLength) PCH Input, - _In_ SIZE_T InputLength - ) -{ - PPH_STRING string; - - string = PhCreateStringEx(NULL, InputLength * sizeof(WCHAR)); - PhZeroExtendToUtf16Buffer(Input, InputLength, string->Buffer); - - return string; -} - -PPH_BYTES PhConvertUtf16ToAsciiEx( - _In_ PWCH Buffer, - _In_ SIZE_T Length, - _In_opt_ CHAR Replacement - ) -{ - PPH_BYTES bytes; - PH_UNICODE_DECODER decoder; - PWCH in; - SIZE_T inRemaining; - PCH out; - SIZE_T outLength; - ULONG codePoint; - - bytes = PhCreateBytesEx(NULL, Length / sizeof(WCHAR)); - PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF16); - in = Buffer; - inRemaining = Length / sizeof(WCHAR); - out = bytes->Buffer; - outLength = 0; - - while (inRemaining != 0) - { - PhWriteUnicodeDecoder(&decoder, (USHORT)*in); - in++; - inRemaining--; - - while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) - { - if (codePoint < 0x80) - { - *out++ = (CHAR)codePoint; - outLength++; - } - else if (Replacement) - { - *out++ = Replacement; - outLength++; - } - } - } - - bytes->Length = outLength; - bytes->Buffer[outLength] = 0; - - return bytes; -} - -/** - * Creates a string object from an existing null-terminated multi-byte string. - * - * \param Buffer A null-terminated multi-byte string. - */ -PPH_STRING PhConvertMultiByteToUtf16( - _In_ PSTR Buffer - ) -{ - return PhConvertMultiByteToUtf16Ex( - Buffer, - strlen(Buffer) - ); -} - -/** - * Creates a string object from an existing null-terminated multi-byte string. - * - * \param Buffer A null-terminated multi-byte string. - * \param Length The number of bytes to use. - */ -PPH_STRING PhConvertMultiByteToUtf16Ex( - _In_ PCHAR Buffer, - _In_ SIZE_T Length - ) -{ - NTSTATUS status; - PPH_STRING string; - ULONG unicodeBytes; - - status = RtlMultiByteToUnicodeSize( - &unicodeBytes, - Buffer, - (ULONG)Length - ); - - if (!NT_SUCCESS(status)) - return NULL; - - string = PhCreateStringEx(NULL, unicodeBytes); - status = RtlMultiByteToUnicodeN( - string->Buffer, - (ULONG)string->Length, - NULL, - Buffer, - (ULONG)Length - ); - - if (!NT_SUCCESS(status)) - { - PhDereferenceObject(string); - return NULL; - } - - return string; -} - -/** - * Creates a multi-byte string from an existing null-terminated UTF-16 string. - * - * \param Buffer A null-terminated UTF-16 string. - */ -PPH_BYTES PhConvertUtf16ToMultiByte( - _In_ PWSTR Buffer - ) -{ - return PhConvertUtf16ToMultiByteEx( - Buffer, - PhCountStringZ(Buffer) * sizeof(WCHAR) - ); -} - -/** - * Creates a multi-byte string from an existing null-terminated UTF-16 string. - * - * \param Buffer A null-terminated UTF-16 string. - * \param Length The number of bytes to use. - */ -PPH_BYTES PhConvertUtf16ToMultiByteEx( - _In_ PWCHAR Buffer, - _In_ SIZE_T Length - ) -{ - NTSTATUS status; - PPH_BYTES bytes; - ULONG multiByteLength; - - status = RtlUnicodeToMultiByteSize( - &multiByteLength, - Buffer, - (ULONG)Length - ); - - if (!NT_SUCCESS(status)) - return NULL; - - bytes = PhCreateBytesEx(NULL, multiByteLength); - status = RtlUnicodeToMultiByteN( - bytes->Buffer, - (ULONG)bytes->Length, - NULL, - Buffer, - (ULONG)Length - ); - - if (!NT_SUCCESS(status)) - { - PhDereferenceObject(bytes); - return NULL; - } - - return bytes; -} - -BOOLEAN PhConvertUtf8ToUtf16Size( - _Out_ PSIZE_T BytesInUtf16String, - _In_reads_bytes_(BytesInUtf8String) PCH Utf8String, - _In_ SIZE_T BytesInUtf8String - ) -{ - BOOLEAN result; - PH_UNICODE_DECODER decoder; - PCH in; - SIZE_T inRemaining; - SIZE_T bytesInUtf16String; - ULONG codePoint; - ULONG numberOfCodeUnits; - - result = TRUE; - PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF8); - in = Utf8String; - inRemaining = BytesInUtf8String; - bytesInUtf16String = 0; - - while (inRemaining != 0) - { - PhWriteUnicodeDecoder(&decoder, (UCHAR)*in); - in++; - inRemaining--; - - while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) - { - if (PhEncodeUnicode(PH_UNICODE_UTF16, codePoint, NULL, &numberOfCodeUnits)) - bytesInUtf16String += numberOfCodeUnits * sizeof(WCHAR); - else - result = FALSE; - } - } - - *BytesInUtf16String = bytesInUtf16String; - - return result; -} - -BOOLEAN PhConvertUtf8ToUtf16Buffer( - _Out_writes_bytes_to_(MaxBytesInUtf16String, *BytesInUtf16String) PWCH Utf16String, - _In_ SIZE_T MaxBytesInUtf16String, - _Out_opt_ PSIZE_T BytesInUtf16String, - _In_reads_bytes_(BytesInUtf8String) PCH Utf8String, - _In_ SIZE_T BytesInUtf8String - ) -{ - BOOLEAN result; - PH_UNICODE_DECODER decoder; - PCH in; - SIZE_T inRemaining; - PWCH out; - SIZE_T outRemaining; - SIZE_T bytesInUtf16String; - ULONG codePoint; - USHORT codeUnits[2]; - ULONG numberOfCodeUnits; - - result = TRUE; - PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF8); - in = Utf8String; - inRemaining = BytesInUtf8String; - out = Utf16String; - outRemaining = MaxBytesInUtf16String / sizeof(WCHAR); - bytesInUtf16String = 0; - - while (inRemaining != 0) - { - PhWriteUnicodeDecoder(&decoder, (UCHAR)*in); - in++; - inRemaining--; - - while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) - { - if (PhEncodeUnicode(PH_UNICODE_UTF16, codePoint, codeUnits, &numberOfCodeUnits)) - { - bytesInUtf16String += numberOfCodeUnits * sizeof(WCHAR); - - if (outRemaining >= numberOfCodeUnits) - { - *out++ = codeUnits[0]; - - if (numberOfCodeUnits >= 2) - *out++ = codeUnits[1]; - - outRemaining -= numberOfCodeUnits; - } - else - { - result = FALSE; - } - } - else - { - result = FALSE; - } - } - } - - if (BytesInUtf16String) - *BytesInUtf16String = bytesInUtf16String; - - return result; -} - -PPH_STRING PhConvertUtf8ToUtf16( - _In_ PSTR Buffer - ) -{ - return PhConvertUtf8ToUtf16Ex( - Buffer, - strlen(Buffer) - ); -} - -PPH_STRING PhConvertUtf8ToUtf16Ex( - _In_ PCHAR Buffer, - _In_ SIZE_T Length - ) -{ - PPH_STRING string; - SIZE_T utf16Bytes; - - if (!PhConvertUtf8ToUtf16Size( - &utf16Bytes, - Buffer, - Length - )) - { - return NULL; - } - - string = PhCreateStringEx(NULL, utf16Bytes); - - if (!PhConvertUtf8ToUtf16Buffer( - string->Buffer, - string->Length, - NULL, - Buffer, - Length - )) - { - PhDereferenceObject(string); - return NULL; - } - - return string; -} - -BOOLEAN PhConvertUtf16ToUtf8Size( - _Out_ PSIZE_T BytesInUtf8String, - _In_reads_bytes_(BytesInUtf16String) PWCH Utf16String, - _In_ SIZE_T BytesInUtf16String - ) -{ - BOOLEAN result; - PH_UNICODE_DECODER decoder; - PWCH in; - SIZE_T inRemaining; - SIZE_T bytesInUtf8String; - ULONG codePoint; - ULONG numberOfCodeUnits; - - result = TRUE; - PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF16); - in = Utf16String; - inRemaining = BytesInUtf16String / sizeof(WCHAR); - bytesInUtf8String = 0; - - while (inRemaining != 0) - { - PhWriteUnicodeDecoder(&decoder, (USHORT)*in); - in++; - inRemaining--; - - while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) - { - if (PhEncodeUnicode(PH_UNICODE_UTF8, codePoint, NULL, &numberOfCodeUnits)) - bytesInUtf8String += numberOfCodeUnits; - else - result = FALSE; - } - } - - *BytesInUtf8String = bytesInUtf8String; - - return result; -} - -BOOLEAN PhConvertUtf16ToUtf8Buffer( - _Out_writes_bytes_to_(MaxBytesInUtf8String, *BytesInUtf8String) PCH Utf8String, - _In_ SIZE_T MaxBytesInUtf8String, - _Out_opt_ PSIZE_T BytesInUtf8String, - _In_reads_bytes_(BytesInUtf16String) PWCH Utf16String, - _In_ SIZE_T BytesInUtf16String - ) -{ - BOOLEAN result; - PH_UNICODE_DECODER decoder; - PWCH in; - SIZE_T inRemaining; - PCH out; - SIZE_T outRemaining; - SIZE_T bytesInUtf8String; - ULONG codePoint; - UCHAR codeUnits[4]; - ULONG numberOfCodeUnits; - - result = TRUE; - PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF16); - in = Utf16String; - inRemaining = BytesInUtf16String / sizeof(WCHAR); - out = Utf8String; - outRemaining = MaxBytesInUtf8String; - bytesInUtf8String = 0; - - while (inRemaining != 0) - { - PhWriteUnicodeDecoder(&decoder, (USHORT)*in); - in++; - inRemaining--; - - while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) - { - if (PhEncodeUnicode(PH_UNICODE_UTF8, codePoint, codeUnits, &numberOfCodeUnits)) - { - bytesInUtf8String += numberOfCodeUnits; - - if (outRemaining >= numberOfCodeUnits) - { - *out++ = codeUnits[0]; - - if (numberOfCodeUnits >= 2) - *out++ = codeUnits[1]; - if (numberOfCodeUnits >= 3) - *out++ = codeUnits[2]; - if (numberOfCodeUnits >= 4) - *out++ = codeUnits[3]; - - outRemaining -= numberOfCodeUnits; - } - else - { - result = FALSE; - } - } - else - { - result = FALSE; - } - } - } - - if (BytesInUtf8String) - *BytesInUtf8String = bytesInUtf8String; - - return result; -} - -PPH_BYTES PhConvertUtf16ToUtf8( - _In_ PWSTR Buffer - ) -{ - return PhConvertUtf16ToUtf8Ex( - Buffer, - PhCountStringZ(Buffer) * sizeof(WCHAR) - ); -} - -PPH_BYTES PhConvertUtf16ToUtf8Ex( - _In_ PWCHAR Buffer, - _In_ SIZE_T Length - ) -{ - PPH_BYTES bytes; - SIZE_T utf8Bytes; - - if (!PhConvertUtf16ToUtf8Size( - &utf8Bytes, - Buffer, - Length - )) - { - return NULL; - } - - bytes = PhCreateBytesEx(NULL, utf8Bytes); - - if (!PhConvertUtf16ToUtf8Buffer( - bytes->Buffer, - bytes->Length, - NULL, - Buffer, - Length - )) - { - PhDereferenceObject(bytes); - return NULL; - } - - return bytes; -} - -/** - * Initializes a string builder object. - * - * \param StringBuilder A string builder object. - * \param InitialCapacity The number of bytes to allocate initially. - */ -VOID PhInitializeStringBuilder( - _Out_ PPH_STRING_BUILDER StringBuilder, - _In_ SIZE_T InitialCapacity - ) -{ - // Make sure the initial capacity is even, as required for all string objects. - if (InitialCapacity & 1) - InitialCapacity++; - - StringBuilder->AllocatedLength = InitialCapacity; - - // Allocate a PH_STRING for the string builder. - // We will dereference it and allocate a new one when we need to resize the string. - - StringBuilder->String = PhCreateStringEx(NULL, StringBuilder->AllocatedLength); - - // We will keep modifying the Length field of the string so that: - // 1. We know how much of the string is used, and - // 2. The user can simply get a reference to the string and use it as-is. - - StringBuilder->String->Length = 0; - - // Write the null terminator. - StringBuilder->String->Buffer[0] = 0; - - PHLIB_INC_STATISTIC(BaseStringBuildersCreated); -} - -/** - * Frees resources used by a string builder object. - * - * \param StringBuilder A string builder object. - */ -VOID PhDeleteStringBuilder( - _Inout_ PPH_STRING_BUILDER StringBuilder - ) -{ - PhDereferenceObject(StringBuilder->String); -} - -/** - * Obtains a reference to the string constructed by a string builder object and frees resources used - * by the object. - * - * \param StringBuilder A string builder object. - * - * \return A pointer to a string. You must free the string using PhDereferenceObject() when you no - * longer need it. - */ -PPH_STRING PhFinalStringBuilderString( - _Inout_ PPH_STRING_BUILDER StringBuilder - ) -{ - return StringBuilder->String; -} - -VOID PhpResizeStringBuilder( - _In_ PPH_STRING_BUILDER StringBuilder, - _In_ SIZE_T NewCapacity - ) -{ - PPH_STRING newString; - - // Double the string size. If that still isn't enough room, just use the new length. - - StringBuilder->AllocatedLength *= 2; - - if (StringBuilder->AllocatedLength < NewCapacity) - StringBuilder->AllocatedLength = NewCapacity; - - // Allocate a new string. - newString = PhCreateStringEx(NULL, StringBuilder->AllocatedLength); - - // Copy the old string to the new string. - memcpy( - newString->Buffer, - StringBuilder->String->Buffer, - StringBuilder->String->Length + sizeof(WCHAR) // Include null terminator - ); - - // Copy the old string length. - newString->Length = StringBuilder->String->Length; - - // Dereference the old string and replace it with the new string. - PhMoveReference(&StringBuilder->String, newString); - - PHLIB_INC_STATISTIC(BaseStringBuildersResized); -} - -FORCEINLINE VOID PhpWriteNullTerminatorStringBuilder( - _In_ PPH_STRING_BUILDER StringBuilder - ) -{ - assert(!(StringBuilder->String->Length & 1)); - *(PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length) = 0; -} - -/** - * Appends a string to the end of a string builder string. - * - * \param StringBuilder A string builder object. - * \param String The string to append. - */ -VOID PhAppendStringBuilder( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ PPH_STRINGREF String - ) -{ - PhAppendStringBuilderEx( - StringBuilder, - String->Buffer, - String->Length - ); -} - -/** - * Appends a string to the end of a string builder string. - * - * \param StringBuilder A string builder object. - * \param String The string to append. - */ -VOID PhAppendStringBuilder2( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ PWSTR String - ) -{ - PhAppendStringBuilderEx( - StringBuilder, - String, - PhCountStringZ(String) * sizeof(WCHAR) - ); -} - -/** - * Appends a string to the end of a string builder string. - * - * \param StringBuilder A string builder object. - * \param String The string to append. Specify NULL to simply reserve \a Length bytes. - * \param Length The number of bytes to append. - */ -VOID PhAppendStringBuilderEx( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_opt_ PWCHAR String, - _In_ SIZE_T Length - ) -{ - if (Length == 0) - return; - - // See if we need to re-allocate the string. - if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Length) - { - PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Length); - } - - // Copy the string, add the length, then write the null terminator. - - if (String) - { - memcpy( - (PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length, - String, - Length - ); - } - - StringBuilder->String->Length += Length; - PhpWriteNullTerminatorStringBuilder(StringBuilder); -} - -/** - * Appends a character to the end of a string builder string. - * - * \param StringBuilder A string builder object. - * \param Character The character to append. - */ -VOID PhAppendCharStringBuilder( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ WCHAR Character - ) -{ - if (StringBuilder->AllocatedLength < StringBuilder->String->Length + sizeof(WCHAR)) - { - PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + sizeof(WCHAR)); - } - - *(PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length) = Character; - StringBuilder->String->Length += sizeof(WCHAR); - PhpWriteNullTerminatorStringBuilder(StringBuilder); -} - -/** - * Appends a number of characters to the end of a string builder string. - * - * \param StringBuilder A string builder object. - * \param Character The character to append. - * \param Count The number of times to append the character. - */ -VOID PhAppendCharStringBuilder2( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ WCHAR Character, - _In_ SIZE_T Count - ) -{ - if (Count == 0) - return; - - // See if we need to re-allocate the string. - if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Count * sizeof(WCHAR)) - { - PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Count * sizeof(WCHAR)); - } - - wmemset( - (PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length), - Character, - Count - ); - - StringBuilder->String->Length += Count * sizeof(WCHAR); - PhpWriteNullTerminatorStringBuilder(StringBuilder); -} - -/** - * Appends a formatted string to the end of a string builder string. - * - * \param StringBuilder A string builder object. - * \param Format The format-control string. - */ -VOID PhAppendFormatStringBuilder( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ _Printf_format_string_ PWSTR Format, - ... - ) -{ - va_list argptr; - - va_start(argptr, Format); - PhAppendFormatStringBuilder_V(StringBuilder, Format, argptr); -} - -VOID PhAppendFormatStringBuilder_V( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ _Printf_format_string_ PWSTR Format, - _In_ va_list ArgPtr - ) -{ - int length; - SIZE_T lengthInBytes; - - length = _vscwprintf(Format, ArgPtr); - - if (length == -1 || length == 0) - return; - - lengthInBytes = length * sizeof(WCHAR); - - if (StringBuilder->AllocatedLength < StringBuilder->String->Length + lengthInBytes) - PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + lengthInBytes); - - _vsnwprintf( - (PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length), - length, - Format, - ArgPtr - ); - - StringBuilder->String->Length += lengthInBytes; - PhpWriteNullTerminatorStringBuilder(StringBuilder); -} - -/** - * Inserts a string into a string builder string. - * - * \param StringBuilder A string builder object. - * \param Index The index, in characters, at which to insert the string. - * \param String The string to insert. - */ -VOID PhInsertStringBuilder( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ SIZE_T Index, - _In_ PPH_STRINGREF String - ) -{ - PhInsertStringBuilderEx( - StringBuilder, - Index, - String->Buffer, - String->Length - ); -} - -/** - * Inserts a string into a string builder string. - * - * \param StringBuilder A string builder object. - * \param Index The index, in characters, at which to insert the string. - * \param String The string to insert. - */ -VOID PhInsertStringBuilder2( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ SIZE_T Index, - _In_ PWSTR String - ) -{ - PhInsertStringBuilderEx( - StringBuilder, - Index, - String, - PhCountStringZ(String) * sizeof(WCHAR) - ); -} - -/** - * Inserts a string into a string builder string. - * - * \param StringBuilder A string builder object. - * \param Index The index, in characters, at which to insert the string. - * \param String The string to insert. Specify NULL to simply reserve \a Length bytes at \a Index. - * \param Length The number of bytes to insert. - */ -VOID PhInsertStringBuilderEx( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ SIZE_T Index, - _In_opt_ PWCHAR String, - _In_ SIZE_T Length - ) -{ - if (Length == 0) - return; - - // See if we need to re-allocate the string. - if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Length) - { - PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Length); - } - - if (Index * sizeof(WCHAR) < StringBuilder->String->Length) - { - // Create some space for the string. - memmove( - &StringBuilder->String->Buffer[Index + Length / sizeof(WCHAR)], - &StringBuilder->String->Buffer[Index], - StringBuilder->String->Length - Index * sizeof(WCHAR) - ); - } - - if (String) - { - // Copy the new string. - memcpy( - &StringBuilder->String->Buffer[Index], - String, - Length - ); - } - - StringBuilder->String->Length += Length; - PhpWriteNullTerminatorStringBuilder(StringBuilder); -} - -/** - * Removes characters from a string builder string. - * - * \param StringBuilder A string builder object. - * \param StartIndex The index, in characters, at which to begin removing characters. - * \param Count The number of characters to remove. - */ -VOID PhRemoveStringBuilder( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ SIZE_T StartIndex, - _In_ SIZE_T Count - ) -{ - // Overwrite the removed part with the part - // behind it. - - memmove( - &StringBuilder->String->Buffer[StartIndex], - &StringBuilder->String->Buffer[StartIndex + Count], - StringBuilder->String->Length - (Count + StartIndex) * sizeof(WCHAR) - ); - StringBuilder->String->Length -= Count * sizeof(WCHAR); - PhpWriteNullTerminatorStringBuilder(StringBuilder); -} - -/** - * Initializes a byte string builder object. - * - * \param BytesBuilder A byte string builder object. - * \param InitialCapacity The number of bytes to allocate initially. - */ -VOID PhInitializeBytesBuilder( - _Out_ PPH_BYTES_BUILDER BytesBuilder, - _In_ SIZE_T InitialCapacity - ) -{ - BytesBuilder->AllocatedLength = InitialCapacity; - BytesBuilder->Bytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength); - BytesBuilder->Bytes->Length = 0; - BytesBuilder->Bytes->Buffer[0] = 0; -} - -/** - * Frees resources used by a byte string builder object. - * - * \param BytesBuilder A byte string builder object. - */ -VOID PhDeleteBytesBuilder( - _Inout_ PPH_BYTES_BUILDER BytesBuilder - ) -{ - PhDereferenceObject(BytesBuilder->Bytes); -} - -/** - * Obtains a reference to the byte string constructed by a byte string builder object and frees - * resources used by the object. - * - * \param BytesBuilder A byte string builder object. - * - * \return A pointer to a byte string. You must free the byte string using PhDereferenceObject() - * when you no longer need it. - */ -PPH_BYTES PhFinalBytesBuilderBytes( - _Inout_ PPH_BYTES_BUILDER BytesBuilder - ) -{ - return BytesBuilder->Bytes; -} - -VOID PhpResizeBytesBuilder( - _In_ PPH_BYTES_BUILDER BytesBuilder, - _In_ SIZE_T NewCapacity - ) -{ - PPH_BYTES newBytes; - - // Double the byte string size. If that still isn't enough room, just use the new length. - - BytesBuilder->AllocatedLength *= 2; - - if (BytesBuilder->AllocatedLength < NewCapacity) - BytesBuilder->AllocatedLength = NewCapacity; - - // Allocate a new byte string. - newBytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength); - - // Copy the old byte string to the new byte string. - memcpy( - newBytes->Buffer, - BytesBuilder->Bytes->Buffer, - BytesBuilder->Bytes->Length + sizeof(CHAR) // Include null terminator - ); - - // Copy the old byte string length. - newBytes->Length = BytesBuilder->Bytes->Length; - - // Dereference the old byte string and replace it with the new byte string. - PhMoveReference(&BytesBuilder->Bytes, newBytes); -} - -FORCEINLINE VOID PhpWriteNullTerminatorBytesBuilder( - _In_ PPH_BYTES_BUILDER BytesBuilder - ) -{ - BytesBuilder->Bytes->Buffer[BytesBuilder->Bytes->Length] = 0; -} - -/** - * Appends a byte string to the end of a byte string builder string. - * - * \param BytesBuilder A byte string builder object. - * \param Bytes The byte string to append. - */ -VOID PhAppendBytesBuilder( - _Inout_ PPH_BYTES_BUILDER BytesBuilder, - _In_ PPH_BYTESREF Bytes - ) -{ - PhAppendBytesBuilderEx( - BytesBuilder, - Bytes->Buffer, - Bytes->Length, - 0, - NULL - ); -} - -/** - * Appends a byte string to the end of a byte string builder string. - * - * \param BytesBuilder A byte string builder object. - * \param Bytes The byte string to append. - */ -VOID PhAppendBytesBuilder2( - _Inout_ PPH_BYTES_BUILDER BytesBuilder, - _In_ PCHAR Bytes - ) -{ - PhAppendBytesBuilderEx( - BytesBuilder, - Bytes, - strlen(Bytes), - 0, - NULL - ); -} - -/** - * Appends a byte string to the end of a byte string builder string. - * - * \param BytesBuilder A byte string builder object. - * \param Buffer The byte string to append. Specify NULL to simply reserve \a Length bytes. - * \param Length The number of bytes to append. - * \param Alignment The required alignment. This should not be greater than 8. - * \param Offset A variable which receives the byte offset of the appended or reserved bytes in the - * byte string builder string. - * - * \return A pointer to the appended or reserved bytes. - */ -PVOID PhAppendBytesBuilderEx( - _Inout_ PPH_BYTES_BUILDER BytesBuilder, - _In_opt_ PVOID Buffer, - _In_ SIZE_T Length, - _In_opt_ SIZE_T Alignment, - _Out_opt_ PSIZE_T Offset - ) -{ - SIZE_T currentLength; - - currentLength = BytesBuilder->Bytes->Length; - - if (Length == 0) - goto Done; - - if (Alignment) - currentLength = ALIGN_UP_BY(currentLength, Alignment); - - // See if we need to re-allocate the byte string. - if (BytesBuilder->AllocatedLength < currentLength + Length) - PhpResizeBytesBuilder(BytesBuilder, currentLength + Length); - - // Copy the byte string, add the length, then write the null terminator. - - if (Buffer) - memcpy(BytesBuilder->Bytes->Buffer + currentLength, Buffer, Length); - - BytesBuilder->Bytes->Length = currentLength + Length; - PhpWriteNullTerminatorBytesBuilder(BytesBuilder); - -Done: - if (Offset) - *Offset = currentLength; - - return BytesBuilder->Bytes->Buffer + currentLength; -} - -/** - * Creates an array object. - * - * \param Array An array object. - * \param ItemSize The size of each item, in bytes. - * \param InitialCapacity The number of elements to allocate storage for, initially. - */ -VOID PhInitializeArray( - _Out_ PPH_ARRAY Array, - _In_ SIZE_T ItemSize, - _In_ SIZE_T InitialCapacity - ) -{ - // Initial capacity of 0 is not allowed. - if (InitialCapacity == 0) - InitialCapacity = 1; - - Array->Count = 0; - Array->AllocatedCount = InitialCapacity; - Array->ItemSize = ItemSize; - Array->Items = PhAllocate(Array->AllocatedCount * ItemSize); -} - -/** - * Frees resources used by an array object. - * - * \param Array An array object. - */ -VOID PhDeleteArray( - _Inout_ PPH_ARRAY Array - ) -{ - PhFree(Array->Items); -} - -/** - * Obtains a copy of the array constructed by an array object and frees resources used by the - * object. - * - * \param Array An array object. - * - * \return The array buffer. - */ -PVOID PhFinalArrayItems( - _Inout_ PPH_ARRAY Array - ) -{ - return Array->Items; -} - -/** - * Resizes an array. - * - * \param Array An array object. - * \param NewCapacity The new required number of elements for which storage has been reserved. This - * must not be smaller than the current number of items in the array. - */ -VOID PhResizeArray( - _Inout_ PPH_ARRAY Array, - _In_ SIZE_T NewCapacity - ) -{ - if (Array->Count > NewCapacity) - PhRaiseStatus(STATUS_INVALID_PARAMETER_2); - - Array->AllocatedCount = NewCapacity; - Array->Items = PhReAllocate(Array->Items, Array->AllocatedCount * Array->ItemSize); -} - -/** - * Adds an item to an array. - * - * \param Array An array object. - * \param Item The item to add. - */ -VOID PhAddItemArray( - _Inout_ PPH_ARRAY Array, - _In_ PVOID Item - ) -{ - // See if we need to resize the list. - if (Array->Count == Array->AllocatedCount) - { - Array->AllocatedCount *= 2; - Array->Items = PhReAllocate(Array->Items, Array->AllocatedCount * Array->ItemSize); - } - - memcpy(PhItemArray(Array, Array->Count), Item, Array->ItemSize); - Array->Count++; -} - -/** - * Adds items to an array. - * - * \param Array An array object. - * \param Items An array containing the items to add. - * \param Count The number of items to add. - */ -VOID PhAddItemsArray( - _Inout_ PPH_ARRAY Array, - _In_ PVOID Items, - _In_ SIZE_T Count - ) -{ - // See if we need to resize the list. - if (Array->AllocatedCount < Array->Count + Count) - { - Array->AllocatedCount *= 2; - - if (Array->AllocatedCount < Array->Count + Count) - Array->AllocatedCount = Array->Count + Count; - - Array->Items = PhReAllocate(Array->Items, Array->AllocatedCount * Array->ItemSize); - } - - memcpy( - PhItemArray(Array, Array->Count), - Items, - Count * Array->ItemSize - ); - Array->Count += Count; -} - -/** - * Clears an array. - * - * \param Array An array object. - */ -VOID PhClearArray( - _Inout_ PPH_ARRAY Array - ) -{ - Array->Count = 0; -} - -/** - * Removes an item from an array. - * - * \param Array An array object. - * \param Index The index of the item. - */ -VOID PhRemoveItemArray( - _Inout_ PPH_ARRAY Array, - _In_ SIZE_T Index - ) -{ - PhRemoveItemsArray(Array, Index, 1); -} - -/** - * Removes items from an array. - * - * \param Array An array object. - * \param StartIndex The index at which to begin removing items. - * \param Count The number of items to remove. - */ -VOID PhRemoveItemsArray( - _Inout_ PPH_ARRAY Array, - _In_ SIZE_T StartIndex, - _In_ SIZE_T Count - ) -{ - // Shift the items after the items forward. - memmove( - PhItemArray(Array, StartIndex), - PhItemArray(Array, StartIndex + Count), - (Array->Count - StartIndex - Count) * Array->ItemSize - ); - Array->Count -= Count; -} - -/** - * Creates a list object. - * - * \param InitialCapacity The number of elements to allocate storage for, initially. - */ -PPH_LIST PhCreateList( - _In_ ULONG InitialCapacity - ) -{ - PPH_LIST list; - - list = PhCreateObject(sizeof(PH_LIST), PhListType); - - // Initial capacity of 0 is not allowed. - if (InitialCapacity == 0) - InitialCapacity = 1; - - list->Count = 0; - list->AllocatedCount = InitialCapacity; - list->Items = PhAllocate(list->AllocatedCount * sizeof(PVOID)); - - return list; -} - -VOID PhpListDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_LIST list = (PPH_LIST)Object; - - PhFree(list->Items); -} - -/** - * Resizes a list. - * - * \param List A list object. - * \param NewCapacity The new required number of elements for which storage has been reserved. This - * must not be smaller than the current number of items in the list. - */ -VOID PhResizeList( - _Inout_ PPH_LIST List, - _In_ ULONG NewCapacity - ) -{ - if (List->Count > NewCapacity) - PhRaiseStatus(STATUS_INVALID_PARAMETER_2); - - List->AllocatedCount = NewCapacity; - List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); -} - -/** - * Adds an item to a list. - * - * \param List A list object. - * \param Item The item to add. - */ -VOID PhAddItemList( - _Inout_ PPH_LIST List, - _In_ PVOID Item - ) -{ - // See if we need to resize the list. - if (List->Count == List->AllocatedCount) - { - List->AllocatedCount *= 2; - List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); - } - - List->Items[List->Count++] = Item; -} - -/** - * Adds items to a list. - * - * \param List A list object. - * \param Items An array containing the items to add. - * \param Count The number of items to add. - */ -VOID PhAddItemsList( - _Inout_ PPH_LIST List, - _In_ PVOID *Items, - _In_ ULONG Count - ) -{ - // See if we need to resize the list. - if (List->AllocatedCount < List->Count + Count) - { - List->AllocatedCount *= 2; - - if (List->AllocatedCount < List->Count + Count) - List->AllocatedCount = List->Count + Count; - - List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); - } - - memcpy( - &List->Items[List->Count], - Items, - Count * sizeof(PVOID) - ); - List->Count += Count; -} - -/** - * Clears a list. - * - * \param List A list object. - */ -VOID PhClearList( - _Inout_ PPH_LIST List - ) -{ - List->Count = 0; -} - -_Success_(return != -1) -/** - * Locates an item in a list. - * - * \param List A list object. - * \param Item The item to search for. - * - * \return The index of the item. If the - * item was not found, -1 is returned. - */ -ULONG PhFindItemList( - _In_ PPH_LIST List, - _In_ PVOID Item - ) -{ - ULONG i; - - for (i = 0; i < List->Count; i++) - { - if (List->Items[i] == Item) - return i; - } - - return -1; -} - -/** - * Inserts an item into a list. - * - * \param List A list object. - * \param Index The index at which to insert the item. - * \param Item The item to add. - */ -VOID PhInsertItemList( - _Inout_ PPH_LIST List, - _In_ ULONG Index, - _In_ PVOID Item - ) -{ - PhInsertItemsList(List, Index, &Item, 1); -} - -/** - * Inserts items into a list. - * - * \param List A list object. - * \param Index The index at which to insert the items. - * \param Items An array containing the items to add. - * \param Count The number of items to add. - */ -VOID PhInsertItemsList( - _Inout_ PPH_LIST List, - _In_ ULONG Index, - _In_ PVOID *Items, - _In_ ULONG Count - ) -{ - // See if we need to resize the list. - if (List->AllocatedCount < List->Count + Count) - { - List->AllocatedCount *= 2; - - if (List->AllocatedCount < List->Count + Count) - List->AllocatedCount = List->Count + Count; - - List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); - } - - if (Index < List->Count) - { - // Shift the existing items backward. - memmove( - &List->Items[Index + Count], - &List->Items[Index], - (List->Count - Index) * sizeof(PVOID) - ); - } - - // Copy the new items into the list. - memcpy( - &List->Items[Index], - Items, - Count * sizeof(PVOID) - ); - - List->Count += Count; -} - -/** - * Removes an item from a list. - * - * \param List A list object. - * \param Index The index of the item. - */ -VOID PhRemoveItemList( - _Inout_ PPH_LIST List, - _In_ ULONG Index - ) -{ - PhRemoveItemsList(List, Index, 1); -} - -/** - * Removes items from a list. - * - * \param List A list object. - * \param StartIndex The index at which to begin removing items. - * \param Count The number of items to remove. - */ -VOID PhRemoveItemsList( - _Inout_ PPH_LIST List, - _In_ ULONG StartIndex, - _In_ ULONG Count - ) -{ - // Shift the items after the items forward. - memmove( - &List->Items[StartIndex], - &List->Items[StartIndex + Count], - (List->Count - StartIndex - Count) * sizeof(PVOID) - ); - List->Count -= Count; -} - -/** - * Creates a pointer list object. - * - * \param InitialCapacity The number of elements to allocate storage for initially. - */ -PPH_POINTER_LIST PhCreatePointerList( - _In_ ULONG InitialCapacity - ) -{ - PPH_POINTER_LIST pointerList; - - pointerList = PhCreateObject(sizeof(PH_POINTER_LIST), PhPointerListType); - - // Initial capacity of 0 is not allowed. - if (InitialCapacity == 0) - InitialCapacity = 1; - - pointerList->Count = 0; - pointerList->AllocatedCount = InitialCapacity; - pointerList->FreeEntry = -1; - pointerList->NextEntry = 0; - pointerList->Items = PhAllocate(pointerList->AllocatedCount * sizeof(PVOID)); - - return pointerList; -} - -VOID NTAPI PhpPointerListDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_POINTER_LIST pointerList = (PPH_POINTER_LIST)Object; - - PhFree(pointerList->Items); -} - -/** - * Decodes an index stored in a free entry. - */ -FORCEINLINE ULONG PhpDecodePointerListIndex( - _In_ PVOID Index - ) -{ - // At least with Microsoft's compiler, shift right on a signed value preserves the sign. This is - // important because we want decode(encode(-1)) = ((-1 << 1) | 1) >> 1 = -1. - return (ULONG)((LONG_PTR)Index >> 1); -} - -/** - * Encodes an index for storage in a free entry. - */ -FORCEINLINE PVOID PhpEncodePointerListIndex( - _In_ ULONG Index - ) -{ - return (PVOID)(((ULONG_PTR)Index << 1) | 0x1); -} - -FORCEINLINE HANDLE PhpPointerListIndexToHandle( - _In_ ULONG Index - ) -{ - // Add one to allow NULL handles to indicate failure/an invalid index. - return UlongToHandle(Index + 1); -} - -FORCEINLINE ULONG PhpPointerListHandleToIndex( - _In_ HANDLE Handle - ) -{ - return HandleToUlong(Handle) - 1; -} - -/** - * Adds a pointer to a pointer list. - * - * \param PointerList A pointer list object. - * \param Pointer The pointer to add. The pointer must be at least 2 byte aligned. - * - * \return A handle to the pointer, valid until the pointer is removed from the pointer list. - */ -HANDLE PhAddItemPointerList( - _Inout_ PPH_POINTER_LIST PointerList, - _In_ PVOID Pointer - ) -{ - ULONG index; - - assert(PH_IS_LIST_POINTER_VALID(Pointer)); - - // Use a free entry if possible. - if (PointerList->FreeEntry != -1) - { - PVOID oldPointer; - - index = PointerList->FreeEntry; - oldPointer = PointerList->Items[index]; - PointerList->Items[index] = Pointer; - PointerList->FreeEntry = PhpDecodePointerListIndex(oldPointer); - } - else - { - // Use the next entry. - if (PointerList->NextEntry == PointerList->AllocatedCount) - { - PointerList->AllocatedCount *= 2; - PointerList->Items = PhReAllocate(PointerList->Items, PointerList->AllocatedCount * sizeof(PVOID)); - } - - index = PointerList->NextEntry++; - PointerList->Items[index] = Pointer; - } - - PointerList->Count++; - - return PhpPointerListIndexToHandle(index); -} - -BOOLEAN PhEnumPointerListEx( - _In_ PPH_POINTER_LIST PointerList, - _Inout_ PULONG EnumerationKey, - _Out_ PVOID *Pointer, - _Out_ PHANDLE PointerHandle - ) -{ - ULONG index; - - while ((index = *EnumerationKey) < PointerList->NextEntry) - { - PVOID pointer = PointerList->Items[index]; - - (*EnumerationKey)++; - - if (PH_IS_LIST_POINTER_VALID(pointer)) - { - *Pointer = pointer; - *PointerHandle = PhpPointerListIndexToHandle(index); - - return TRUE; - } - } - - return FALSE; -} - -/** - * Locates a pointer in a pointer list. - * - * \param PointerList A pointer list object. - * \param Pointer The pointer to find. The pointer must be at least 2 byte aligned. - * - * \return A handle to the pointer, valid until the pointer is removed from the pointer list. If the - * pointer is not contained in the pointer list, NULL is returned. - */ -HANDLE PhFindItemPointerList( - _In_ PPH_POINTER_LIST PointerList, - _In_ PVOID Pointer - ) -{ - ULONG i; - - assert(PH_IS_LIST_POINTER_VALID(Pointer)); - - for (i = 0; i < PointerList->NextEntry; i++) - { - if (PointerList->Items[i] == Pointer) - return PhpPointerListIndexToHandle(i); - } - - return NULL; -} - -/** - * Removes a pointer from a pointer list. - * - * \param PointerList A pointer list object. - * \param PointerHandle A handle to the pointer to remove. - * - * \remarks No checking is performed on the pointer handle. Make sure the handle is valid before - * calling the function. - */ -VOID PhRemoveItemPointerList( - _Inout_ PPH_POINTER_LIST PointerList, - _In_ HANDLE PointerHandle - ) -{ - ULONG index; - - assert(PointerHandle); - - index = PhpPointerListHandleToIndex(PointerHandle); - - PointerList->Items[index] = PhpEncodePointerListIndex(PointerList->FreeEntry); - PointerList->FreeEntry = index; - - PointerList->Count--; -} - -FORCEINLINE ULONG PhpValidateHash( - _In_ ULONG Hash - ) -{ - // No point in using a full hash when we're going to AND with size minus one anyway. -#if defined(PH_HASHTABLE_FULL_HASH) && !defined(PH_HASHTABLE_POWER_OF_TWO_SIZE) - if (Hash != -1) - return Hash; - else - return 0; -#else - return Hash & MAXLONG; -#endif -} - -FORCEINLINE ULONG PhpIndexFromHash( - _In_ PPH_HASHTABLE Hashtable, - _In_ ULONG Hash - ) -{ -#ifdef PH_HASHTABLE_POWER_OF_TWO_SIZE - return Hash & (Hashtable->AllocatedBuckets - 1); -#else - return Hash % Hashtable->AllocatedBuckets; -#endif -} - -FORCEINLINE ULONG PhpGetNumberOfBuckets( - _In_ ULONG Capacity - ) -{ -#ifdef PH_HASHTABLE_POWER_OF_TWO_SIZE - return PhRoundUpToPowerOfTwo(Capacity); -#else - return PhGetPrimeNumber(Capacity); -#endif -} - -/** - * Creates a hashtable object. - * - * \param EntrySize The size of each hashtable entry, in bytes. - * \param EqualFunction A comparison function that is executed to compare two hashtable entries. - * \param HashFunction A hash function that is executed to generate a hash code for a hashtable - * entry. - * \param InitialCapacity The number of entries to allocate storage for initially. - */ -PPH_HASHTABLE PhCreateHashtable( - _In_ ULONG EntrySize, - _In_ PPH_HASHTABLE_EQUAL_FUNCTION EqualFunction, - _In_ PPH_HASHTABLE_HASH_FUNCTION HashFunction, - _In_ ULONG InitialCapacity - ) -{ - PPH_HASHTABLE hashtable; - - hashtable = PhCreateObject(sizeof(PH_HASHTABLE), PhHashtableType); - - // Initial capacity of 0 is not allowed. - if (InitialCapacity == 0) - InitialCapacity = 1; - - hashtable->EntrySize = EntrySize; - hashtable->EqualFunction = EqualFunction; - hashtable->HashFunction = HashFunction; - - // Allocate the buckets. - hashtable->AllocatedBuckets = PhpGetNumberOfBuckets(InitialCapacity); - hashtable->Buckets = PhAllocate(sizeof(ULONG) * hashtable->AllocatedBuckets); - // Set all bucket values to -1. - memset(hashtable->Buckets, 0xff, sizeof(ULONG) * hashtable->AllocatedBuckets); - - // Allocate the entries. - hashtable->AllocatedEntries = hashtable->AllocatedBuckets; - hashtable->Entries = PhAllocate(PH_HASHTABLE_ENTRY_SIZE(EntrySize) * hashtable->AllocatedEntries); - - hashtable->Count = 0; - hashtable->FreeEntry = -1; - hashtable->NextEntry = 0; - - return hashtable; -} - -VOID PhpHashtableDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_HASHTABLE hashtable = (PPH_HASHTABLE)Object; - - PhFree(hashtable->Buckets); - PhFree(hashtable->Entries); -} - -VOID PhpResizeHashtable( - _Inout_ PPH_HASHTABLE Hashtable, - _In_ ULONG NewCapacity - ) -{ - PPH_HASHTABLE_ENTRY entry; - ULONG i; - - // Re-allocate the buckets. Note that we don't need to keep the contents. - Hashtable->AllocatedBuckets = PhpGetNumberOfBuckets(NewCapacity); - PhFree(Hashtable->Buckets); - Hashtable->Buckets = PhAllocate(sizeof(ULONG) * Hashtable->AllocatedBuckets); - // Set all bucket values to -1. - memset(Hashtable->Buckets, 0xff, sizeof(ULONG) * Hashtable->AllocatedBuckets); - - // Re-allocate the entries. - Hashtable->AllocatedEntries = Hashtable->AllocatedBuckets; - Hashtable->Entries = PhReAllocate( - Hashtable->Entries, - PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize) * Hashtable->AllocatedEntries - ); - - // Re-distribute the entries among the buckets. - - // PH_HASHTABLE_GET_ENTRY is quite slow (it involves a multiply), so we use a pointer here. - entry = Hashtable->Entries; - - for (i = 0; i < Hashtable->NextEntry; i++) - { - if (entry->HashCode != -1) - { - ULONG index = PhpIndexFromHash(Hashtable, entry->HashCode); - - entry->Next = Hashtable->Buckets[index]; - Hashtable->Buckets[index] = i; - } - - entry = (PPH_HASHTABLE_ENTRY)((ULONG_PTR)entry + PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize)); - } -} - -FORCEINLINE PVOID PhpAddEntryHashtable( - _Inout_ PPH_HASHTABLE Hashtable, - _In_ PVOID Entry, - _In_ BOOLEAN CheckForDuplicate, - _Out_opt_ PBOOLEAN Added - ) -{ - ULONG hashCode; // hash code of the new entry - ULONG index; // bucket index of the new entry - ULONG freeEntry; // index of new entry in entry array - PPH_HASHTABLE_ENTRY entry; // pointer to new entry in entry array - - hashCode = PhpValidateHash(Hashtable->HashFunction(Entry)); - index = PhpIndexFromHash(Hashtable, hashCode); - - if (CheckForDuplicate) - { - ULONG i; - - for (i = Hashtable->Buckets[index]; i != -1; i = entry->Next) - { - entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i); - - if (entry->HashCode == hashCode && Hashtable->EqualFunction(&entry->Body, Entry)) - { - if (Added) - *Added = FALSE; - - return &entry->Body; - } - } - } - - // Use a free entry if possible. - if (Hashtable->FreeEntry != -1) - { - freeEntry = Hashtable->FreeEntry; - entry = PH_HASHTABLE_GET_ENTRY(Hashtable, freeEntry); - Hashtable->FreeEntry = entry->Next; - } - else - { - // Use the next entry in the entry array. - - if (Hashtable->NextEntry == Hashtable->AllocatedEntries) - { - // Resize the hashtable. - PhpResizeHashtable(Hashtable, Hashtable->AllocatedBuckets * 2); - index = PhpIndexFromHash(Hashtable, hashCode); - } - - freeEntry = Hashtable->NextEntry++; - entry = PH_HASHTABLE_GET_ENTRY(Hashtable, freeEntry); - } - - // Initialize the entry. - entry->HashCode = hashCode; - entry->Next = Hashtable->Buckets[index]; - Hashtable->Buckets[index] = freeEntry; - // Copy the user-supplied data to the entry. - memcpy(&entry->Body, Entry, Hashtable->EntrySize); - - Hashtable->Count++; - - if (Added) - *Added = TRUE; - - return &entry->Body; -} - -/** - * Adds an entry to a hashtable. - * - * \param Hashtable A hashtable object. - * \param Entry The entry to add. - * - * \return A pointer to the entry as stored in the hashtable. This pointer is valid until the - * hashtable is modified. If the hashtable already contained an equal entry, NULL is returned. - * - * \remarks Entries are only guaranteed to be 8 byte aligned, even on 64-bit systems. - */ -PVOID PhAddEntryHashtable( - _Inout_ PPH_HASHTABLE Hashtable, - _In_ PVOID Entry - ) -{ - PVOID entry; - BOOLEAN added; - - entry = PhpAddEntryHashtable(Hashtable, Entry, TRUE, &added); - - if (added) - return entry; - else - return NULL; -} - -/** - * Adds an entry to a hashtable or returns an existing one. - * - * \param Hashtable A hashtable object. - * \param Entry The entry to add. - * \param Added A variable which receives TRUE if a new entry was created, and FALSE if an existing - * entry was returned. - * - * \return A pointer to the entry as stored in the hashtable. This pointer is valid until the - * hashtable is modified. If the hashtable already contained an equal entry, the existing entry is - * returned. Check the value of \a Added to determine whether the returned entry is new or existing. - * - * \remarks Entries are only guaranteed to be 8 byte aligned, even on 64-bit systems. - */ -PVOID PhAddEntryHashtableEx( - _Inout_ PPH_HASHTABLE Hashtable, - _In_ PVOID Entry, - _Out_opt_ PBOOLEAN Added - ) -{ - return PhpAddEntryHashtable(Hashtable, Entry, TRUE, Added); -} - -/** - * Clears a hashtable. - * - * \param Hashtable A hashtable object. - */ -VOID PhClearHashtable( - _Inout_ PPH_HASHTABLE Hashtable - ) -{ - if (Hashtable->Count > 0) - { - memset(Hashtable->Buckets, 0xff, sizeof(ULONG) * Hashtable->AllocatedBuckets); - Hashtable->Count = 0; - Hashtable->FreeEntry = -1; - Hashtable->NextEntry = 0; - } -} - -/** - * Enumerates the entries in a hashtable. - * - * \param Hashtable A hashtable object. - * \param Entry A variable which receives a pointer to the hashtable entry. The pointer is valid - * until the hashtable is modified. - * \param EnumerationKey A variable which is initialized to 0 before first calling this function. - * - * \return TRUE if an entry pointer was stored in \a Entry, FALSE if there are no more entries. - * - * \remarks Do not modify the hashtable while the hashtable is being enumerated (between calls to - * this function). Otherwise, the function may behave unexpectedly. You may reset the - * \a EnumerationKey variable to 0 if you wish to restart the enumeration. - */ -BOOLEAN PhEnumHashtable( - _In_ PPH_HASHTABLE Hashtable, - _Out_ PVOID *Entry, - _Inout_ PULONG EnumerationKey - ) -{ - while (*EnumerationKey < Hashtable->NextEntry) - { - PPH_HASHTABLE_ENTRY entry = PH_HASHTABLE_GET_ENTRY(Hashtable, *EnumerationKey); - - (*EnumerationKey)++; - - if (entry->HashCode != -1) - { - *Entry = &entry->Body; - return TRUE; - } - } - - return FALSE; -} - -/** - * Locates an entry in a hashtable. - * - * \param Hashtable A hashtable object. - * \param Entry An entry representing the entry to find. - * - * \return A pointer to the entry as stored in the hashtable. This pointer is valid until the - * hashtable is modified. If the entry could not be found, NULL is returned. - * - * \remarks The entry specified in \a Entry can be a partial entry that is filled in enough so that - * the comparison and hash functions can work with them. - */ -PVOID PhFindEntryHashtable( - _In_ PPH_HASHTABLE Hashtable, - _In_ PVOID Entry - ) -{ - ULONG hashCode; - ULONG index; - ULONG i; - PPH_HASHTABLE_ENTRY entry; - - hashCode = PhpValidateHash(Hashtable->HashFunction(Entry)); - index = PhpIndexFromHash(Hashtable, hashCode); - - for (i = Hashtable->Buckets[index]; i != -1; i = entry->Next) - { - entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i); - - if (entry->HashCode == hashCode && Hashtable->EqualFunction(&entry->Body, Entry)) - { - return &entry->Body; - } - } - - return NULL; -} - -/** - * Removes an entry from a hashtable. - * - * \param Hashtable A hashtable object. - * \param Entry The entry to remove. - * - * \return TRUE if the entry was removed, FALSE if the entry could not be found. - * - * \remarks The entry specified in \a Entry can be an actual entry pointer returned by - * PhFindEntryHashtable, or a partial entry. - */ -BOOLEAN PhRemoveEntryHashtable( - _Inout_ PPH_HASHTABLE Hashtable, - _In_ PVOID Entry - ) -{ - ULONG hashCode; - ULONG index; - ULONG i; - ULONG previousIndex; - PPH_HASHTABLE_ENTRY entry; - - hashCode = PhpValidateHash(Hashtable->HashFunction(Entry)); - index = PhpIndexFromHash(Hashtable, hashCode); - previousIndex = -1; - - for (i = Hashtable->Buckets[index]; i != -1; i = entry->Next) - { - entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i); - - if (entry->HashCode == hashCode && Hashtable->EqualFunction(&entry->Body, Entry)) - { - // Unlink the entry from the bucket. - if (previousIndex == -1) - { - Hashtable->Buckets[index] = entry->Next; - } - else - { - PH_HASHTABLE_GET_ENTRY(Hashtable, previousIndex)->Next = entry->Next; - } - - entry->HashCode = -1; // indicates the entry is not being used - entry->Next = Hashtable->FreeEntry; - Hashtable->FreeEntry = i; - - Hashtable->Count--; - - return TRUE; - } - - previousIndex = i; - } - - return FALSE; -} - -/** - * Generates a hash code for a sequence of bytes. - * - * \param Bytes A pointer to a byte array. - * \param Length The number of bytes to hash. - */ -ULONG PhHashBytes( - _In_reads_(Length) PUCHAR Bytes, - _In_ SIZE_T Length - ) -{ - ULONG hash = 0; - - if (Length == 0) - return hash; - - // FNV-1a algorithm: http://www.isthe.com/chongo/src/fnv/hash_32a.c - - do - { - hash ^= *Bytes++; - hash *= 0x01000193; - } while (--Length != 0); - - return hash; -} - -/** - * Generates a hash code for a string. - * - * \param String The string to hash. - * \param IgnoreCase TRUE for a case-insensitive hash function, otherwise FALSE. - */ -ULONG PhHashStringRef( - _In_ PPH_STRINGREF String, - _In_ BOOLEAN IgnoreCase - ) -{ - ULONG hash = 0; - SIZE_T count; - PWCHAR p; - - if (String->Length == 0) - return 0; - - count = String->Length / sizeof(WCHAR); - p = String->Buffer; - - if (!IgnoreCase) - { - return PhHashBytes((PUCHAR)String->Buffer, String->Length); - } - else - { - do - { - hash ^= (USHORT)RtlUpcaseUnicodeChar(*p++); - hash *= 0x01000193; - } while (--count != 0); - } - - return hash; -} - -BOOLEAN NTAPI PhpSimpleHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPH_KEY_VALUE_PAIR entry1 = Entry1; - PPH_KEY_VALUE_PAIR entry2 = Entry2; - - return entry1->Key == entry2->Key; -} - -ULONG NTAPI PhpSimpleHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPH_KEY_VALUE_PAIR entry = Entry; - - return PhHashIntPtr((ULONG_PTR)entry->Key); -} - -PPH_HASHTABLE PhCreateSimpleHashtable( - _In_ ULONG InitialCapacity - ) -{ - return PhCreateHashtable( - sizeof(PH_KEY_VALUE_PAIR), - PhpSimpleHashtableEqualFunction, - PhpSimpleHashtableHashFunction, - InitialCapacity - ); -} - -PVOID PhAddItemSimpleHashtable( - _Inout_ PPH_HASHTABLE SimpleHashtable, - _In_opt_ PVOID Key, - _In_opt_ PVOID Value - ) -{ - PH_KEY_VALUE_PAIR entry; - - entry.Key = Key; - entry.Value = Value; - - if (PhAddEntryHashtable(SimpleHashtable, &entry)) - return Value; - else - return NULL; -} - -PVOID *PhFindItemSimpleHashtable( - _In_ PPH_HASHTABLE SimpleHashtable, - _In_opt_ PVOID Key - ) -{ - PH_KEY_VALUE_PAIR lookupEntry; - PPH_KEY_VALUE_PAIR entry; - - lookupEntry.Key = Key; - entry = PhFindEntryHashtable(SimpleHashtable, &lookupEntry); - - if (entry) - return &entry->Value; - else - return NULL; -} - -BOOLEAN PhRemoveItemSimpleHashtable( - _Inout_ PPH_HASHTABLE SimpleHashtable, - _In_opt_ PVOID Key - ) -{ - PH_KEY_VALUE_PAIR lookupEntry; - - lookupEntry.Key = Key; - - return PhRemoveEntryHashtable(SimpleHashtable, &lookupEntry); -} - -/** - * Initializes a free list object. - * - * \param FreeList A pointer to the free list object. - * \param Size The number of bytes in each allocation. - * \param MaximumCount The number of unused allocations to store. - */ -VOID PhInitializeFreeList( - _Out_ PPH_FREE_LIST FreeList, - _In_ SIZE_T Size, - _In_ ULONG MaximumCount - ) -{ - RtlInitializeSListHead(&FreeList->ListHead); - FreeList->Count = 0; - FreeList->MaximumCount = MaximumCount; - FreeList->Size = Size; -} - -/** - * Frees resources used by a free list object. - * - * \param FreeList A pointer to the free list object. - */ -VOID PhDeleteFreeList( - _Inout_ PPH_FREE_LIST FreeList - ) -{ - PPH_FREE_LIST_ENTRY entry; - PSLIST_ENTRY listEntry; - - listEntry = RtlInterlockedFlushSList(&FreeList->ListHead); - - while (listEntry) - { - entry = CONTAINING_RECORD(listEntry, PH_FREE_LIST_ENTRY, ListEntry); - listEntry = listEntry->Next; - PhFree(entry); - } -} - -/** - * Allocates a block of memory from a free list. - * - * \param FreeList A pointer to a free list object. - * - * \return A pointer to the allocated block of memory. The memory must be freed using - * PhFreeToFreeList(). The block is guaranteed to be aligned at MEMORY_ALLOCATION_ALIGNMENT bytes. - */ -PVOID PhAllocateFromFreeList( - _Inout_ PPH_FREE_LIST FreeList - ) -{ - PPH_FREE_LIST_ENTRY entry; - PSLIST_ENTRY listEntry; - - listEntry = RtlInterlockedPopEntrySList(&FreeList->ListHead); - - if (listEntry) - { - _InterlockedDecrement((PLONG)&FreeList->Count); - entry = CONTAINING_RECORD(listEntry, PH_FREE_LIST_ENTRY, ListEntry); - } - else - { - entry = PhAllocate(FIELD_OFFSET(PH_FREE_LIST_ENTRY, Body) + FreeList->Size); - } - - return &entry->Body; -} - -/** - * Frees a block of memory to a free list. - * - * \param FreeList A pointer to a free list object. - * \param Memory A pointer to a block of memory. - */ -VOID PhFreeToFreeList( - _Inout_ PPH_FREE_LIST FreeList, - _In_ PVOID Memory - ) -{ - PPH_FREE_LIST_ENTRY entry; - - entry = CONTAINING_RECORD(Memory, PH_FREE_LIST_ENTRY, Body); - - // We don't enforce Count <= MaximumCount (that would require locking), - // but we do check it. - if (FreeList->Count < FreeList->MaximumCount) - { - RtlInterlockedPushEntrySList(&FreeList->ListHead, &entry->ListEntry); - _InterlockedIncrement((PLONG)&FreeList->Count); - } - else - { - PhFree(entry); - } -} - -/** - * Initializes a callback object. - * - * \param Callback A pointer to a callback object. - */ -VOID PhInitializeCallback( - _Out_ PPH_CALLBACK Callback - ) -{ - InitializeListHead(&Callback->ListHead); - PhInitializeQueuedLock(&Callback->ListLock); - PhInitializeCondition(&Callback->BusyCondition); -} - -/** - * Frees resources used by a callback object. - * - * \param Callback A pointer to a callback object. - */ -VOID PhDeleteCallback( - _Inout_ PPH_CALLBACK Callback - ) -{ - // Nothing for now -} - -/** - * Registers a callback function to be notified. - * - * \param Callback A pointer to a callback object. - * \param Function The callback function. - * \param Context A user-defined value to pass to the callback function. - * \param Registration A variable which receives registration information for the callback. Do not - * modify the contents of this structure and do not free the storage for this structure until you - * have unregistered the callback. - */ -VOID PhRegisterCallback( - _Inout_ PPH_CALLBACK Callback, - _In_ PPH_CALLBACK_FUNCTION Function, - _In_opt_ PVOID Context, - _Out_ PPH_CALLBACK_REGISTRATION Registration - ) -{ - PhRegisterCallbackEx( - Callback, - Function, - Context, - 0, - Registration - ); -} - -/** - * Registers a callback function to be notified. - * - * \param Callback A pointer to a callback object. - * \param Function The callback function. - * \param Context A user-defined value to pass to the callback function. - * \param Flags A combination of flags controlling the callback. Set this parameter to 0. - * \param Registration A variable which receives registration information for the callback. Do not - * modify the contents of this structure and do not free the storage for this structure until you - * have unregistered the callback. - */ -VOID PhRegisterCallbackEx( - _Inout_ PPH_CALLBACK Callback, - _In_ PPH_CALLBACK_FUNCTION Function, - _In_opt_ PVOID Context, - _In_ USHORT Flags, - _Out_ PPH_CALLBACK_REGISTRATION Registration - ) -{ - Registration->Function = Function; - Registration->Context = Context; - Registration->Busy = 0; - Registration->Unregistering = FALSE; - Registration->Flags = Flags; - - PhAcquireQueuedLockExclusive(&Callback->ListLock); - InsertTailList(&Callback->ListHead, &Registration->ListEntry); - PhReleaseQueuedLockExclusive(&Callback->ListLock); -} - -/** - * Unregisters a callback function. - * - * \param Callback A pointer to a callback object. - * \param Registration The structure returned by PhRegisterCallback(). - * - * \remarks It is guaranteed that the callback function will not be in execution once this function - * returns. Attempting to unregister a callback function from within the same function will result - * in a deadlock. - */ -VOID PhUnregisterCallback( - _Inout_ PPH_CALLBACK Callback, - _Inout_ PPH_CALLBACK_REGISTRATION Registration - ) -{ - Registration->Unregistering = TRUE; - - PhAcquireQueuedLockExclusive(&Callback->ListLock); - - // Wait for the callback to be unbusy. - while (Registration->Busy) - PhWaitForCondition(&Callback->BusyCondition, &Callback->ListLock, NULL); - - RemoveEntryList(&Registration->ListEntry); - - PhReleaseQueuedLockExclusive(&Callback->ListLock); -} - -/** - * Notifies all registered callback functions. - * - * \param Callback A pointer to a callback object. - * \param Parameter A value to pass to all callback functions. - */ -VOID PhInvokeCallback( - _In_ PPH_CALLBACK Callback, - _In_opt_ PVOID Parameter - ) -{ - PLIST_ENTRY listEntry; - - PhAcquireQueuedLockShared(&Callback->ListLock); - - listEntry = Callback->ListHead.Flink; - - while (listEntry != &Callback->ListHead) - { - PPH_CALLBACK_REGISTRATION registration; - LONG busy; - - registration = CONTAINING_RECORD(listEntry, PH_CALLBACK_REGISTRATION, ListEntry); - - // Don't bother executing the callback function if it is being unregistered. - if (registration->Unregistering) - continue; - - _InterlockedIncrement(®istration->Busy); - - // Execute the callback function. - - PhReleaseQueuedLockShared(&Callback->ListLock); - registration->Function( - Parameter, - registration->Context - ); - PhAcquireQueuedLockShared(&Callback->ListLock); - - busy = _InterlockedDecrement(®istration->Busy); - - if (registration->Unregistering && busy == 0) - { - // Someone started unregistering while the callback function was executing, and we must - // wake them. - PhPulseAllCondition(&Callback->BusyCondition); - } - - listEntry = listEntry->Flink; - } - - PhReleaseQueuedLockShared(&Callback->ListLock); -} - -/** - * Retrieves a prime number bigger than or equal to the specified number. - */ -ULONG PhGetPrimeNumber( - _In_ ULONG Minimum - ) -{ - ULONG i, j; - - for (i = 0; i < sizeof(PhpPrimeNumbers) / sizeof(ULONG); i++) - { - if (PhpPrimeNumbers[i] >= Minimum) - return PhpPrimeNumbers[i]; - } - - for (i = Minimum | 1; i < MAXLONG; i += 2) - { - ULONG sqrtI = (ULONG)sqrt(i); - - for (j = 3; j <= sqrtI; j += 2) - { - if (i % j == 0) - { - // Not a prime. - goto NextPrime; - } - } - - // Success. - return i; -NextPrime: - NOTHING; - } - - return Minimum; -} - -/** - * Rounds up a number to the next power of two. - */ -ULONG PhRoundUpToPowerOfTwo( - _In_ ULONG Number - ) -{ - Number--; - Number |= Number >> 1; - Number |= Number >> 2; - Number |= Number >> 4; - Number |= Number >> 8; - Number |= Number >> 16; - Number++; - - return Number; -} - -/** - * Performs exponentiation. - */ -ULONG PhExponentiate( - _In_ ULONG Base, - _In_ ULONG Exponent - ) -{ - ULONG result = 1; - - while (Exponent) - { - if (Exponent & 1) - result *= Base; - - Exponent >>= 1; - Base *= Base; - } - - return result; -} - -/** - * Performs 64-bit exponentiation. - */ -ULONG64 PhExponentiate64( - _In_ ULONG64 Base, - _In_ ULONG Exponent - ) -{ - ULONG64 result = 1; - - while (Exponent) - { - if (Exponent & 1) - result *= Base; - - Exponent >>= 1; - Base *= Base; - } - - return result; -} - -/** - * Converts a sequence of hexadecimal digits into a byte array. - * - * \param String A string containing hexadecimal digits to convert. The string must have an even - * number of digits, because each pair of hexadecimal digits represents one byte. Example: - * "129a2eff5c0b". - * \param Buffer The output buffer. - * - * \return TRUE if the string was successfully converted, otherwise FALSE. - */ -BOOLEAN PhHexStringToBuffer( - _In_ PPH_STRINGREF String, - _Out_writes_bytes_(String->Length / sizeof(WCHAR) / 2) PUCHAR Buffer - ) -{ - SIZE_T i; - SIZE_T length; - - // The string must have an even length. - if ((String->Length / sizeof(WCHAR)) & 1) - return FALSE; - - length = String->Length / sizeof(WCHAR) / 2; - - for (i = 0; i < length; i++) - { - Buffer[i] = - (UCHAR)(PhCharToInteger[(UCHAR)String->Buffer[i * 2]] << 4) + - (UCHAR)PhCharToInteger[(UCHAR)String->Buffer[i * 2 + 1]]; - } - - return TRUE; -} - -/** - * Converts a byte array into a sequence of hexadecimal digits. - * - * \param Buffer The input buffer. - * \param Length The number of bytes to convert. - * - * \return A string containing a sequence of hexadecimal digits. - */ -PPH_STRING PhBufferToHexString( - _In_reads_bytes_(Length) PUCHAR Buffer, - _In_ ULONG Length - ) -{ - return PhBufferToHexStringEx(Buffer, Length, FALSE); -} - -/** - * Converts a byte array into a sequence of hexadecimal digits. - * - * \param Buffer The input buffer. - * \param Length The number of bytes to convert. - * \param UpperCase TRUE to use uppercase characters, otherwise FALSE. - * - * \return A string containing a sequence of hexadecimal digits. - */ -PPH_STRING PhBufferToHexStringEx( - _In_reads_bytes_(Length) PUCHAR Buffer, - _In_ ULONG Length, - _In_ BOOLEAN UpperCase - ) -{ - PCHAR table; - PPH_STRING string; - ULONG i; - - if (UpperCase) - table = PhIntegerToCharUpper; - else - table = PhIntegerToChar; - - string = PhCreateStringEx(NULL, Length * 2 * sizeof(WCHAR)); - - for (i = 0; i < Length; i++) - { - string->Buffer[i * 2] = table[Buffer[i] >> 4]; - string->Buffer[i * 2 + 1] = table[Buffer[i] & 0xf]; - } - - return string; -} - -/** - * Converts a string to an integer. - * - * \param String The string to process. - * \param Base The base which the string uses to represent the integer. The maximum value is 69. - * \param Integer The resulting integer. - */ -BOOLEAN PhpStringToInteger64( - _In_ PPH_STRINGREF String, - _In_ ULONG Base, - _Out_ PULONG64 Integer - ) -{ - BOOLEAN valid = TRUE; - ULONG64 result; - SIZE_T length; - SIZE_T i; - - length = String->Length / sizeof(WCHAR); - result = 0; - - for (i = 0; i < length; i++) - { - ULONG value; - - value = PhCharToInteger[(UCHAR)String->Buffer[i]]; - - if (value < Base) - result = result * Base + value; - else - valid = FALSE; - } - - *Integer = result; - - return valid; -} - -/** - * Converts a string to an integer. - * - * \param String The string to process. - * \param Base The base which the string uses to represent the integer. The maximum value is 69. If - * the parameter is 0, the base is inferred from the string. - * \param Integer The resulting integer. - * - * \remarks If \a Base is 0, the following prefixes may be used to indicate bases: - * - * \li \c 0x Base 16. - * \li \c 0o Base 8. - * \li \c 0b Base 2. - * \li \c 0t Base 3. - * \li \c 0q Base 4. - * \li \c 0w Base 12. - * \li \c 0r Base 32. - * - * If there is no recognized prefix, base 10 is used. - */ -BOOLEAN PhStringToInteger64( - _In_ PPH_STRINGREF String, - _In_opt_ ULONG Base, - _Out_opt_ PLONG64 Integer - ) -{ - BOOLEAN valid; - ULONG64 result; - PH_STRINGREF string; - BOOLEAN negative; - ULONG base; - - if (Base > 69) - return FALSE; - - string = *String; - negative = FALSE; - - if (string.Length != 0 && (string.Buffer[0] == '-' || string.Buffer[0] == '+')) - { - if (string.Buffer[0] == '-') - negative = TRUE; - - PhSkipStringRef(&string, sizeof(WCHAR)); - } - - // If the caller specified a base, don't perform any additional processing. - - if (Base) - { - base = Base; - } - else - { - base = 10; - - if (string.Length >= 2 * sizeof(WCHAR) && string.Buffer[0] == '0') - { - switch (string.Buffer[1]) - { - case 'x': - case 'X': - base = 16; - break; - case 'o': - case 'O': - base = 8; - break; - case 'b': - case 'B': - base = 2; - break; - case 't': // ternary - case 'T': - base = 3; - break; - case 'q': // quaternary - case 'Q': - base = 4; - break; - case 'w': // base 12 - case 'W': - base = 12; - break; - case 'r': // base 32 - case 'R': - base = 32; - break; - } - - if (base != 10) - PhSkipStringRef(&string, 2 * sizeof(WCHAR)); - } - } - - valid = PhpStringToInteger64(&string, base, &result); - - if (Integer) - *Integer = negative ? -(LONG64)result : result; - - return valid; -} - -BOOLEAN PhpStringToDouble( - _In_ PPH_STRINGREF String, - _In_ ULONG Base, - _Out_ DOUBLE *Double - ) -{ - BOOLEAN valid = TRUE; - BOOLEAN dotSeen = FALSE; - DOUBLE result; - DOUBLE fraction; - SIZE_T length; - SIZE_T i; - - length = String->Length / sizeof(WCHAR); - result = 0; - fraction = 1; - - for (i = 0; i < length; i++) - { - if (String->Buffer[i] == '.') - { - if (!dotSeen) - dotSeen = TRUE; - else - valid = FALSE; - } - else - { - ULONG value; - - value = PhCharToInteger[(UCHAR)String->Buffer[i]]; - - if (value < Base) - { - if (!dotSeen) - { - result = result * Base + value; - } - else - { - fraction /= Base; - result = result + value * fraction; - } - } - else - { - valid = FALSE; - } - } - } - - *Double = result; - - return valid; -} - -/** - * Converts a string to a double-precision floating point value. - * - * \param String The string to process. - * \param Base Reserved. - * \param Double The resulting double value. - */ -BOOLEAN PhStringToDouble( - _In_ PPH_STRINGREF String, - _Reserved_ ULONG Base, - _Out_opt_ DOUBLE *Double - ) -{ - BOOLEAN valid; - DOUBLE result; - PH_STRINGREF string; - BOOLEAN negative; - - string = *String; - negative = FALSE; - - if (string.Length != 0 && (string.Buffer[0] == '-' || string.Buffer[0] == '+')) - { - if (string.Buffer[0] == '-') - negative = TRUE; - - PhSkipStringRef(&string, sizeof(WCHAR)); - } - - valid = PhpStringToDouble(&string, 10, &result); - - if (Double) - *Double = negative ? -result : result; - - return valid; -} - -/** - * Converts an integer to a string. - * - * \param Integer The integer to process. - * \param Base The base which the integer is represented with. The maximum value is 69. The base - * cannot be 1. If the parameter is 0, the base used is 10. - * \param Signed TRUE if \a Integer is a signed value, otherwise FALSE. - * - * \return The resulting string, or NULL if an error occurred. - */ -PPH_STRING PhIntegerToString64( - _In_ LONG64 Integer, - _In_opt_ ULONG Base, - _In_ BOOLEAN Signed - ) -{ - PH_FORMAT format; - - if (Base == 1 || Base > 69) - return NULL; - - if (Signed) - PhInitFormatI64D(&format, Integer); - else - PhInitFormatI64U(&format, Integer); - - if (Base != 0) - { - format.Type |= FormatUseRadix; - format.Radix = (UCHAR)Base; - } - - return PhFormat(&format, 1, 0); -} - -VOID PhPrintTimeSpan( - _Out_writes_(PH_TIMESPAN_STR_LEN_1) PWSTR Destination, - _In_ ULONG64 Ticks, - _In_opt_ ULONG Mode - ) -{ - switch (Mode) - { - case PH_TIMESPAN_HMSM: - _snwprintf( - Destination, - PH_TIMESPAN_STR_LEN, - L"%02I64u:%02I64u:%02I64u.%03I64u", - PH_TICKS_PARTIAL_HOURS(Ticks), - PH_TICKS_PARTIAL_MIN(Ticks), - PH_TICKS_PARTIAL_SEC(Ticks), - PH_TICKS_PARTIAL_MS(Ticks) - ); - break; - case PH_TIMESPAN_DHMS: - _snwprintf( - Destination, - PH_TIMESPAN_STR_LEN, - L"%I64u:%02I64u:%02I64u:%02I64u", - PH_TICKS_PARTIAL_DAYS(Ticks), - PH_TICKS_PARTIAL_HOURS(Ticks), - PH_TICKS_PARTIAL_MIN(Ticks), - PH_TICKS_PARTIAL_SEC(Ticks) - ); - break; - default: - _snwprintf( - Destination, - PH_TIMESPAN_STR_LEN, - L"%02I64u:%02I64u:%02I64u", - PH_TICKS_PARTIAL_HOURS(Ticks), - PH_TICKS_PARTIAL_MIN(Ticks), - PH_TICKS_PARTIAL_SEC(Ticks) - ); - break; - } -} - -/** - * Fills a memory block with a ULONG pattern. - * - * \param Memory The memory block. The block must be 4 byte aligned. - * \param Value The ULONG pattern. - * \param Count The number of elements. - */ -VOID PhFillMemoryUlong( - _Inout_updates_(Count) _Needs_align_(4) PULONG Memory, - _In_ ULONG Value, - _In_ SIZE_T Count - ) -{ - __m128i pattern; - SIZE_T count; - - if (PhpVectorLevel < PH_VECTOR_LEVEL_SSE2) - { - if (Count != 0) - { - do - { - *Memory++ = Value; - } while (--Count != 0); - } - - return; - } - - if ((ULONG_PTR)Memory & 0xf) - { - switch ((ULONG_PTR)Memory & 0xf) - { - case 0x4: - if (Count >= 1) - { - *Memory++ = Value; - Count--; - } - __fallthrough; - case 0x8: - if (Count >= 1) - { - *Memory++ = Value; - Count--; - } - __fallthrough; - case 0xc: - if (Count >= 1) - { - *Memory++ = Value; - Count--; - } - break; - } - } - - pattern = _mm_set1_epi32(Value); - count = Count / 4; - - if (count != 0) - { - do - { - _mm_store_si128((__m128i *)Memory, pattern); - Memory += 4; - } while (--count != 0); - } - - switch (Count & 0x3) - { - case 0x3: - *Memory++ = Value; - __fallthrough; - case 0x2: - *Memory++ = Value; - __fallthrough; - case 0x1: - *Memory++ = Value; - break; - } -} - -/** - * Divides an array of numbers by a number. - * - * \param A The destination array, divided by \a B. - * \param B The number. - * \param Count The number of elements. - */ -VOID PhDivideSinglesBySingle( - _Inout_updates_(Count) PFLOAT A, - _In_ FLOAT B, - _In_ SIZE_T Count - ) -{ - PFLOAT endA; - __m128 b; - - if (PhpVectorLevel < PH_VECTOR_LEVEL_SSE2) - { - while (Count--) - *A++ /= B; - - return; - } - - if ((ULONG_PTR)A & 0xf) - { - switch ((ULONG_PTR)A & 0xf) - { - case 0x4: - if (Count >= 1) - { - *A++ /= B; - Count--; - } - __fallthrough; - case 0x8: - if (Count >= 1) - { - *A++ /= B; - Count--; - } - __fallthrough; - case 0xc: - if (Count >= 1) - { - *A++ /= B; - Count--; - } - else - { - return; // essential; A may not be aligned properly - } - break; - } - } - - endA = (PFLOAT)((ULONG_PTR)(A + Count) & ~0xf); - b = _mm_load1_ps(&B); - - while (A != endA) - { - __m128 a; - - a = _mm_load_ps(A); - a = _mm_div_ps(a, b); - _mm_store_ps(A, a); - - A += 4; - } - - switch (Count & 0x3) - { - case 0x3: - *A++ /= B; - __fallthrough; - case 0x2: - *A++ /= B; - __fallthrough; - case 0x1: - *A++ /= B; - break; - } -} +/* + * Process Hacker - + * base support functions + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This file contains basic low-level code as well as general algorithms and data structures. + * + * Memory allocation. PhAllocate is a wrapper around RtlAllocateHeap, and always allocates from the + * phlib heap. PhAllocatePage is a wrapper around NtAllocateVirtualMemory and allocates pages. + * + * Null-terminated strings. The Ph*StringZ functions manipulate null-terminated strings. The copying + * functions provide a simple way to copy strings which may not be null-terminated, but have a + * specified limit. + * + * String. The design of the string object was chosen for maximum compatibility. As such each string + * buffer must be null-terminated, and each object contains an embedded PH_STRINGREF structure. Note + * that efficient sub-string creation (no copying, only references the parent string object) could + * not be implemented due to the mandatory null-termination. String objects must be regarded as + * immutable (for thread-safety reasons) unless the object has just been created and no references + * have been shared. + * + * String builder. This is a set of functions which allow for efficient modification of strings. For + * performance reasons, these functions modify string objects directly, even though they are + * normally immutable. + * + * List. A simple PVOID list that resizes itself when needed. + * + * Pointer list. Similar to the normal list object, but uses a free list in order to support + * constant time insertion and deletion. In order for the free list to work, normal entries have + * their lowest bit clear while free entries have their lowest bit set, with the index of the next + * free entry in the upper bits. + * + * Hashtable. A hashtable with power-of-two bucket sizes and with all entries stored in a single + * array. This improves locality but may be inefficient when resizing the hashtable. It is a good + * idea to store pointers to objects as entries, as opposed to the objects themselves. + * + * Simple hashtable. A wrapper around the normal hashtable, with PVOID keys and PVOID values. + * + * Free list. A thread-safe memory allocation method where freed blocks are stored in a S-list, and + * allocations are made from this list whenever possible. + * + * Callback. A thread-safe notification mechanism where clients can register callback functions + * which are then invoked by other code. + */ + +#include + +#include +#include + +#include + +#define PH_VECTOR_LEVEL_NONE 0 +#define PH_VECTOR_LEVEL_SSE2 1 +#define PH_VECTOR_LEVEL_AVX 2 + +typedef struct _PHP_BASE_THREAD_CONTEXT +{ + PUSER_THREAD_START_ROUTINE StartAddress; + PVOID Parameter; +} PHP_BASE_THREAD_CONTEXT, *PPHP_BASE_THREAD_CONTEXT; + +VOID NTAPI PhpListDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID NTAPI PhpPointerListDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID NTAPI PhpHashtableDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +// Types + +PPH_OBJECT_TYPE PhStringType = NULL; +PPH_OBJECT_TYPE PhBytesType = NULL; +PPH_OBJECT_TYPE PhListType = NULL; +PPH_OBJECT_TYPE PhPointerListType = NULL; +PPH_OBJECT_TYPE PhHashtableType = NULL; + +// Misc. + +static BOOLEAN PhpVectorLevel = PH_VECTOR_LEVEL_NONE; +static PPH_STRING PhSharedEmptyString = NULL; + +// Threads + +static PH_FREE_LIST PhpBaseThreadContextFreeList; +#ifdef DEBUG +ULONG PhDbgThreadDbgTlsIndex; +LIST_ENTRY PhDbgThreadListHead = { &PhDbgThreadListHead, &PhDbgThreadListHead }; +PH_QUEUED_LOCK PhDbgThreadListLock = PH_QUEUED_LOCK_INIT; +#endif + +// Data + +static ULONG PhpPrimeNumbers[] = +{ + 0x3, 0x7, 0xb, 0x11, 0x17, 0x1d, 0x25, 0x2f, 0x3b, 0x47, 0x59, 0x6b, 0x83, + 0xa3, 0xc5, 0xef, 0x125, 0x161, 0x1af, 0x209, 0x277, 0x2f9, 0x397, 0x44f, + 0x52f, 0x63d, 0x78b, 0x91d, 0xaf1, 0xd2b, 0xfd1, 0x12fd, 0x16cf, 0x1b65, + 0x20e3, 0x2777, 0x2f6f, 0x38ff, 0x446f, 0x521f, 0x628d, 0x7655, 0x8e01, + 0xaa6b, 0xcc89, 0xf583, 0x126a7, 0x1619b, 0x1a857, 0x1fd3b, 0x26315, 0x2dd67, + 0x3701b, 0x42023, 0x4f361, 0x5f0ed, 0x72125, 0x88e31, 0xa443b, 0xc51eb, + 0xec8c1, 0x11bdbf, 0x154a3f, 0x198c4f, 0x1ea867, 0x24ca19, 0x2c25c1, 0x34fa1b, + 0x3f928f, 0x4c4987, 0x5b8b6f, 0x6dda89 +}; + +/** + * Initializes the base support module. + */ +BOOLEAN PhBaseInitialization( + VOID + ) +{ + PH_OBJECT_TYPE_PARAMETERS parameters; + + // The following relies on the (technically undefined) value of XState being zero before Windows 7 SP1. + // NOTE: This is unused for now. + /*if (USER_SHARED_DATA->XState.EnabledFeatures & XSTATE_MASK_AVX) + PhpVectorLevel = PH_VECTOR_LEVEL_AVX; + else*/ if (USER_SHARED_DATA->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE]) + PhpVectorLevel = PH_VECTOR_LEVEL_SSE2; + + PhStringType = PhCreateObjectType(L"String", 0, NULL); + PhBytesType = PhCreateObjectType(L"Bytes", 0, NULL); + + parameters.FreeListSize = sizeof(PH_LIST); + parameters.FreeListCount = 128; + + PhListType = PhCreateObjectTypeEx(L"List", PH_OBJECT_TYPE_USE_FREE_LIST, PhpListDeleteProcedure, ¶meters); + PhPointerListType = PhCreateObjectType(L"PointerList", 0, PhpPointerListDeleteProcedure); + + parameters.FreeListSize = sizeof(PH_HASHTABLE); + parameters.FreeListCount = 64; + + PhHashtableType = PhCreateObjectTypeEx(L"Hashtable", PH_OBJECT_TYPE_USE_FREE_LIST, PhpHashtableDeleteProcedure, ¶meters); + + PhInitializeFreeList(&PhpBaseThreadContextFreeList, sizeof(PHP_BASE_THREAD_CONTEXT), 16); + +#ifdef DEBUG + PhDbgThreadDbgTlsIndex = TlsAlloc(); +#endif + + return TRUE; +} + +NTSTATUS PhpBaseThreadStart( + _In_ PVOID Parameter + ) +{ + NTSTATUS status; + HRESULT result; + PHP_BASE_THREAD_CONTEXT context; +#ifdef DEBUG + PHP_BASE_THREAD_DBG dbg; +#endif + + context = *(PPHP_BASE_THREAD_CONTEXT)Parameter; + PhFreeToFreeList(&PhpBaseThreadContextFreeList, Parameter); + +#ifdef DEBUG + dbg.ClientId = NtCurrentTeb()->ClientId; + + dbg.StartAddress = context.StartAddress; + dbg.Parameter = context.Parameter; + dbg.CurrentAutoPool = NULL; + + PhAcquireQueuedLockExclusive(&PhDbgThreadListLock); + InsertTailList(&PhDbgThreadListHead, &dbg.ListEntry); + PhReleaseQueuedLockExclusive(&PhDbgThreadListLock); + + TlsSetValue(PhDbgThreadDbgTlsIndex, &dbg); +#endif + + // Initialization code + + result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + + // Call the user-supplied function. + + status = context.StartAddress(context.Parameter); + + // De-initialization code + + if (result == S_OK || result == S_FALSE) + CoUninitialize(); + +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&PhDbgThreadListLock); + RemoveEntryList(&dbg.ListEntry); + PhReleaseQueuedLockExclusive(&PhDbgThreadListLock); +#endif + + return status; +} + +/** + * Creates a thread. + * + * \param StackSize The initial stack size of the thread. + * \param StartAddress The function to execute in the thread. + * \param Parameter A user-defined value to pass to the function. + */ +HANDLE PhCreateThread( + _In_opt_ SIZE_T StackSize, + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter + ) +{ + NTSTATUS status; + HANDLE threadHandle; + PPHP_BASE_THREAD_CONTEXT context; + + context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList); + context->StartAddress = StartAddress; + context->Parameter = Parameter; + + status = RtlCreateUserThread( + NtCurrentProcess(), + NULL, + FALSE, + 0, + 0, + StackSize, + PhpBaseThreadStart, + context, + &threadHandle, + NULL + ); + + // NOTE: PhCreateThread previously used CreateThread with callers using GetLastError() + // for checking errors. We need to preserve this behavior for compatibility -dmex + // TODO: Migrate code over to PhCreateThreadEx and remove this function. + //RtlSetLastWin32ErrorAndNtStatusFromNtStatus(status); + SetLastError(RtlNtStatusToDosError(status)); + + if (NT_SUCCESS(status)) + { + PHLIB_INC_STATISTIC(BaseThreadsCreated); + return threadHandle; + } + else + { + PHLIB_INC_STATISTIC(BaseThreadsCreateFailed); + PhFreeToFreeList(&PhpBaseThreadContextFreeList, context); + return NULL; + } +} + +NTSTATUS PhCreateThreadEx( + _Out_ PHANDLE ThreadHandle, + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter + ) +{ + NTSTATUS status; + HANDLE threadHandle; + PPHP_BASE_THREAD_CONTEXT context; + + context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList); + context->StartAddress = StartAddress; + context->Parameter = Parameter; + + status = RtlCreateUserThread( + NtCurrentProcess(), + NULL, + FALSE, + 0, + 0, + 0, + PhpBaseThreadStart, + context, + &threadHandle, + NULL + ); + + if (NT_SUCCESS(status)) + { + PHLIB_INC_STATISTIC(BaseThreadsCreated); + *ThreadHandle = threadHandle; + } + else + { + PHLIB_INC_STATISTIC(BaseThreadsCreateFailed); + PhFreeToFreeList(&PhpBaseThreadContextFreeList, context); + } + + return status; +} + +NTSTATUS PhCreateThread2( + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter + ) +{ + NTSTATUS status; + HANDLE threadHandle; + PPHP_BASE_THREAD_CONTEXT context; + + context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList); + context->StartAddress = StartAddress; + context->Parameter = Parameter; + + status = RtlCreateUserThread( + NtCurrentProcess(), + NULL, + FALSE, + 0, + 0, + 0, + PhpBaseThreadStart, + context, + &threadHandle, + NULL + ); + + if (NT_SUCCESS(status)) + { + PHLIB_INC_STATISTIC(BaseThreadsCreated); + NtClose(threadHandle); + } + else + { + PHLIB_INC_STATISTIC(BaseThreadsCreateFailed); + PhFreeToFreeList(&PhpBaseThreadContextFreeList, context); + } + + return status; +} + +/** + * Gets the current system time (UTC). + * + * \remarks Use this function instead of NtQuerySystemTime() because no system calls are involved. + */ +VOID PhQuerySystemTime( + _Out_ PLARGE_INTEGER SystemTime + ) +{ + do + { + SystemTime->HighPart = USER_SHARED_DATA->SystemTime.High1Time; + SystemTime->LowPart = USER_SHARED_DATA->SystemTime.LowPart; + } while (SystemTime->HighPart != USER_SHARED_DATA->SystemTime.High2Time); +} + +/** + * Gets the offset of the current time zone from UTC. + */ +VOID PhQueryTimeZoneBias( + _Out_ PLARGE_INTEGER TimeZoneBias + ) +{ + do + { + TimeZoneBias->HighPart = USER_SHARED_DATA->TimeZoneBias.High1Time; + TimeZoneBias->LowPart = USER_SHARED_DATA->TimeZoneBias.LowPart; + } while (TimeZoneBias->HighPart != USER_SHARED_DATA->TimeZoneBias.High2Time); +} + +/** + * Converts system time to local time. + * + * \param SystemTime A UTC time value. + * \param LocalTime A variable which receives the local time value. This may be the same variable as + * \a SystemTime. + * + * \remarks Use this function instead of RtlSystemTimeToLocalTime() because no system calls are + * involved. + */ +VOID PhSystemTimeToLocalTime( + _In_ PLARGE_INTEGER SystemTime, + _Out_ PLARGE_INTEGER LocalTime + ) +{ + LARGE_INTEGER timeZoneBias; + + PhQueryTimeZoneBias(&timeZoneBias); + LocalTime->QuadPart = SystemTime->QuadPart - timeZoneBias.QuadPart; +} + +/** + * Converts local time to system time. + * + * \param LocalTime A local time value. + * \param SystemTime A variable which receives the UTC time value. This may be the same variable as + * \a LocalTime. + * + * \remarks Use this function instead of RtlLocalTimeToSystemTime() because no system calls are + * involved. + */ +VOID PhLocalTimeToSystemTime( + _In_ PLARGE_INTEGER LocalTime, + _Out_ PLARGE_INTEGER SystemTime + ) +{ + LARGE_INTEGER timeZoneBias; + + PhQueryTimeZoneBias(&timeZoneBias); + SystemTime->QuadPart = LocalTime->QuadPart + timeZoneBias.QuadPart; +} + +/** + * Allocates a block of memory. + * + * \param Size The number of bytes to allocate. + * + * \return A pointer to the allocated block of memory. + * + * \remarks If the function fails to allocate the block of memory, it raises an exception. The block + * is guaranteed to be aligned at MEMORY_ALLOCATION_ALIGNMENT bytes. + */ +_May_raise_ +_Check_return_ +_Ret_notnull_ +_Post_writable_byte_size_(Size) +PVOID PhAllocate( + _In_ SIZE_T Size + ) +{ + return RtlAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Size); +} + +/** + * Allocates a block of memory. + * + * \param Size The number of bytes to allocate. + * + * \return A pointer to the allocated block of memory, or NULL if the block could not be allocated. + */ +PVOID PhAllocateSafe( + _In_ SIZE_T Size + ) +{ + return RtlAllocateHeap(PhHeapHandle, 0, Size); +} + +/** + * Allocates a block of memory. + * + * \param Size The number of bytes to allocate. + * \param Flags Flags controlling the allocation. + * + * \return A pointer to the allocated block of memory, or NULL if the block could not be allocated. + */ +PVOID PhAllocateExSafe( + _In_ SIZE_T Size, + _In_ ULONG Flags + ) +{ + return RtlAllocateHeap(PhHeapHandle, Flags, Size); +} + +/** + * Frees a block of memory allocated with PhAllocate(). + * + * \param Memory A pointer to a block of memory. + */ +VOID PhFree( + _Frees_ptr_opt_ PVOID Memory + ) +{ + RtlFreeHeap(PhHeapHandle, 0, Memory); +} + +/** + * Re-allocates a block of memory originally allocated with PhAllocate(). + * + * \param Memory A pointer to a block of memory. + * \param Size The new size of the memory block, in bytes. + * + * \return A pointer to the new block of memory. The existing contents of the memory block are + * copied to the new block. + * + * \remarks If the function fails to allocate the block of memory, it raises an exception. + */ +_May_raise_ +_Post_writable_byte_size_(Size) +PVOID PhReAllocate( + _Frees_ptr_opt_ PVOID Memory, + _In_ SIZE_T Size + ) +{ + return RtlReAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Memory, Size); +} + +/** + * Re-allocates a block of memory originally allocated with PhAllocate(). + * + * \param Memory A pointer to a block of memory. + * \param Size The new size of the memory block, in bytes. + * + * \return A pointer to the new block of memory, or NULL if the block could not be allocated. The + * existing contents of the memory block are copied to the new block. + */ +PVOID PhReAllocateSafe( + _In_ PVOID Memory, + _In_ SIZE_T Size + ) +{ + return RtlReAllocateHeap(PhHeapHandle, 0, Memory, Size); +} + +/** + * Allocates pages of memory. + * + * \param Size The number of bytes to allocate. The number of pages allocated will be large enough + * to contain \a Size bytes. + * \param NewSize The number of bytes actually allocated. This is \a Size rounded up to the next + * multiple of PAGE_SIZE. + * + * \return A pointer to the allocated block of memory, or NULL if the block could not be allocated. + */ +_Check_return_ +_Ret_maybenull_ +PVOID PhAllocatePage( + _In_ SIZE_T Size, + _Out_opt_ PSIZE_T NewSize + ) +{ + PVOID baseAddress; + + baseAddress = NULL; + + if (NT_SUCCESS(NtAllocateVirtualMemory( + NtCurrentProcess(), + &baseAddress, + 0, + &Size, + MEM_COMMIT, + PAGE_READWRITE + ))) + { + if (NewSize) + *NewSize = Size; + + return baseAddress; + } + else + { + return NULL; + } +} + +/** + * Frees pages of memory allocated with PhAllocatePage(). + * + * \param Memory A pointer to a block of memory. + */ +VOID PhFreePage( + _Frees_ptr_opt_ PVOID Memory + ) +{ + SIZE_T size; + + size = 0; + + NtFreeVirtualMemory( + NtCurrentProcess(), + &Memory, + &size, + MEM_RELEASE + ); +} + +/** + * Determines the length of the specified string, in characters. + * + * \param String The string. + */ +SIZE_T PhCountStringZ( + _In_ PWSTR String + ) +{ + if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) + { + PWSTR p; + ULONG unaligned; + __m128i b; + __m128i z; + ULONG mask; + ULONG index; + + p = (PWSTR)((ULONG_PTR)String & ~0xe); // String should be 2 byte aligned + unaligned = PtrToUlong(String) & 0xf; + z = _mm_setzero_si128(); + + if (unaligned != 0) + { + b = _mm_load_si128((__m128i *)p); + b = _mm_cmpeq_epi16(b, z); + mask = _mm_movemask_epi8(b) >> unaligned; + + if (_BitScanForward(&index, mask)) + return index / sizeof(WCHAR); + + p += 16 / sizeof(WCHAR); + } + + while (TRUE) + { + b = _mm_load_si128((__m128i *)p); + b = _mm_cmpeq_epi16(b, z); + mask = _mm_movemask_epi8(b); + + if (_BitScanForward(&index, mask)) + return (SIZE_T)(p - String) + index / sizeof(WCHAR); + + p += 16 / sizeof(WCHAR); + } + } + else + { + return wcslen(String); + } +} + +/** + * Allocates space for and copies a byte string. + * + * \param String The string to duplicate. + * + * \return The new string, which can be freed using PhFree(). + */ +PSTR PhDuplicateBytesZ( + _In_ PSTR String + ) +{ + PSTR newString; + SIZE_T length; + + length = strlen(String) + sizeof(ANSI_NULL); // include the null terminator + + newString = PhAllocate(length); + memcpy(newString, String, length); + + return newString; +} + +/** + * Allocates space for and copies a byte string. + * + * \param String The string to duplicate. + * + * \return The new string, which can be freed using PhFree(), or NULL if storage could not be + * allocated. + */ +PSTR PhDuplicateBytesZSafe( + _In_ PSTR String + ) +{ + PSTR newString; + SIZE_T length; + + length = strlen(String) + sizeof(ANSI_NULL); // include the null terminator + + newString = PhAllocateSafe(length); + + if (!newString) + return NULL; + + memcpy(newString, String, length); + + return newString; +} + +/** + * Allocates space for and copies a 16-bit string. + * + * \param String The string to duplicate. + * + * \return The new string, which can be freed using PhFree(). + */ +PWSTR PhDuplicateStringZ( + _In_ PWSTR String + ) +{ + PWSTR newString; + SIZE_T length; + + length = PhCountStringZ(String) * sizeof(WCHAR) + sizeof(UNICODE_NULL); // include the null terminator + + newString = PhAllocate(length); + memcpy(newString, String, length); + + return newString; +} + +/** + * Copies a string with optional null termination and a maximum number of characters. + * + * \param InputBuffer A pointer to the input string. + * \param InputCount The maximum number of characters to copy, not including the null terminator. + * Specify -1 for no limit. + * \param OutputBuffer A pointer to the output buffer. + * \param OutputCount The number of characters in the output buffer, including the null terminator. + * \param ReturnCount A variable which receives the number of characters required to contain the + * input string, including the null terminator. If the function returns TRUE, this variable contains + * the number of characters written to the output buffer. + * + * \return TRUE if the input string was copied to the output string, otherwise FALSE. + * + * \remarks The function stops copying when it encounters the first null character in the input + * string. It will also stop copying when it reaches the character count specified in \a InputCount, + * if \a InputCount is not -1. + */ +BOOLEAN PhCopyBytesZ( + _In_ PSTR InputBuffer, + _In_ SIZE_T InputCount, + _Out_writes_opt_z_(OutputCount) PSTR OutputBuffer, + _In_ SIZE_T OutputCount, + _Out_opt_ PSIZE_T ReturnCount + ) +{ + SIZE_T i; + BOOLEAN copied; + + // Determine the length of the input string. + + if (InputCount != -1) + { + i = 0; + + while (i < InputCount && InputBuffer[i]) + i++; + } + else + { + i = strlen(InputBuffer); + } + + // Copy the string if there is enough room. + + if (OutputBuffer && OutputCount >= i + sizeof(ANSI_NULL)) // need one character for null terminator + { + memcpy(OutputBuffer, InputBuffer, i); + OutputBuffer[i] = ANSI_NULL; + copied = TRUE; + } + else + { + copied = FALSE; + } + + if (ReturnCount) + *ReturnCount = i + sizeof(ANSI_NULL); + + return copied; +} + +/** + * Copies a string with optional null termination and a maximum number of characters. + * + * \param InputBuffer A pointer to the input string. + * \param InputCount The maximum number of characters to copy, not including the null terminator. + * Specify -1 for no limit. + * \param OutputBuffer A pointer to the output buffer. + * \param OutputCount The number of characters in the output buffer, including the null terminator. + * \param ReturnCount A variable which receives the number of characters required to contain the + * input string, including the null terminator. If the function returns TRUE, this variable contains + * the number of characters written to the output buffer. + * + * \return TRUE if the input string was copied to the output string, otherwise FALSE. + * + * \remarks The function stops copying when it encounters the first null character in the input + * string. It will also stop copying when it reaches the character count specified in \a InputCount, + * if \a InputCount is not -1. + */ +BOOLEAN PhCopyStringZ( + _In_ PWSTR InputBuffer, + _In_ SIZE_T InputCount, + _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer, + _In_ SIZE_T OutputCount, + _Out_opt_ PSIZE_T ReturnCount + ) +{ + SIZE_T i; + BOOLEAN copied; + + // Determine the length of the input string. + + if (InputCount != -1) + { + i = 0; + + while (i < InputCount && InputBuffer[i]) + i++; + } + else + { + i = PhCountStringZ(InputBuffer); + } + + // Copy the string if there is enough room. + + if (OutputBuffer && OutputCount >= i + sizeof(UNICODE_NULL)) // need one character for null terminator + { + memcpy(OutputBuffer, InputBuffer, i * sizeof(WCHAR)); + OutputBuffer[i] = UNICODE_NULL; + copied = TRUE; + } + else + { + copied = FALSE; + } + + if (ReturnCount) + *ReturnCount = i + sizeof(UNICODE_NULL); + + return copied; +} + +/** + * Copies a string with optional null termination and a maximum number of characters. + * + * \param InputBuffer A pointer to the input string. + * \param InputCount The maximum number of characters to copy, not including the null terminator. + * Specify -1 for no limit. + * \param OutputBuffer A pointer to the output buffer. + * \param OutputCount The number of characters in the output buffer, including the null terminator. + * \param ReturnCount A variable which receives the number of characters required to contain the + * input string, including the null terminator. If the function returns TRUE, this variable contains + * the number of characters written to the output buffer. + * + * \return TRUE if the input string was copied to the output string, otherwise FALSE. + * + * \remarks The function stops copying when it encounters the first null character in the input + * string. It will also stop copying when it reaches the character count specified in \a InputCount, + * if \a InputCount is not -1. + */ +BOOLEAN PhCopyStringZFromBytes( + _In_ PSTR InputBuffer, + _In_ SIZE_T InputCount, + _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer, + _In_ SIZE_T OutputCount, + _Out_opt_ PSIZE_T ReturnCount + ) +{ + SIZE_T i; + BOOLEAN copied; + + // Determine the length of the input string. + + if (InputCount != -1) + { + i = 0; + + while (i < InputCount && InputBuffer[i]) + i++; + } + else + { + i = strlen(InputBuffer); + } + + // Copy the string if there is enough room. + + if (OutputBuffer && OutputCount >= i + sizeof(ANSI_NULL)) // need one character for null terminator + { + PhZeroExtendToUtf16Buffer(InputBuffer, i, OutputBuffer); + OutputBuffer[i] = UNICODE_NULL; + copied = TRUE; + } + else + { + copied = FALSE; + } + + if (ReturnCount) + *ReturnCount = i + sizeof(ANSI_NULL); + + return copied; +} + +/** + * Copies a string with optional null termination and a maximum number of characters. + * + * \param InputBuffer A pointer to the input string. + * \param InputCount The maximum number of characters to copy, not including the null terminator. + * Specify -1 for no limit. + * \param OutputBuffer A pointer to the output buffer. + * \param OutputCount The number of characters in the output buffer, including the null terminator. + * \param ReturnCount A variable which receives the number of characters required to contain the + * input string, including the null terminator. If the function returns TRUE, this variable contains + * the number of characters written to the output buffer. + * + * \return TRUE if the input string was copied to the output string, otherwise FALSE. + * + * \remarks The function stops copying when it encounters the first null character in the input + * string. It will also stop copying when it reaches the character count specified in \a InputCount, + * if \a InputCount is not -1. + */ +BOOLEAN PhCopyStringZFromMultiByte( + _In_ PSTR InputBuffer, + _In_ SIZE_T InputCount, + _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer, + _In_ SIZE_T OutputCount, + _Out_opt_ PSIZE_T ReturnCount + ) +{ + NTSTATUS status; + SIZE_T i; + ULONG unicodeBytes; + BOOLEAN copied; + + // Determine the length of the input string. + + if (InputCount != -1) + { + i = 0; + + while (i < InputCount && InputBuffer[i]) + i++; + } + else + { + i = strlen(InputBuffer); + } + + // Determine the length of the output string. + + status = RtlMultiByteToUnicodeSize( + &unicodeBytes, + InputBuffer, + (ULONG)i + ); + + if (!NT_SUCCESS(status)) + { + if (ReturnCount) + *ReturnCount = -1; + + return FALSE; + } + + // Convert the string to Unicode if there is enough room. + + if (OutputBuffer && OutputCount >= unicodeBytes / sizeof(WCHAR) + sizeof(UNICODE_NULL)) + { + status = RtlMultiByteToUnicodeN( + OutputBuffer, + unicodeBytes, + NULL, + InputBuffer, + (ULONG)i + ); + + if (NT_SUCCESS(status)) + { + // RtlMultiByteToUnicodeN doesn't null terminate the string. + *(PWCHAR)PTR_ADD_OFFSET(OutputBuffer, unicodeBytes) = UNICODE_NULL; + copied = TRUE; + } + else + { + copied = FALSE; + } + } + else + { + copied = FALSE; + } + + if (ReturnCount) + *ReturnCount = unicodeBytes / sizeof(WCHAR) + sizeof(UNICODE_NULL); + + return copied; +} + +FORCEINLINE LONG PhpCompareRightNatural( + _In_ PWSTR A, + _In_ PWSTR B + ) +{ + LONG bias = 0; + + for (; ; A++, B++) + { + if (!PhIsDigitCharacter(*A) && !PhIsDigitCharacter(*B)) + { + return bias; + } + else if (!PhIsDigitCharacter(*A)) + { + return -1; + } + else if (!PhIsDigitCharacter(*B)) + { + return 1; + } + else if (*A < *B) + { + if (bias == 0) + bias = -1; + } + else if (*A > *B) + { + if (bias == 0) + bias = 1; + } + else if (!*A && !*B) + { + return bias; + } + } + + return 0; +} + +FORCEINLINE LONG PhpCompareLeftNatural( + _In_ PWSTR A, + _In_ PWSTR B + ) +{ + for (; ; A++, B++) + { + if (!PhIsDigitCharacter(*A) && !PhIsDigitCharacter(*B)) + { + return 0; + } + else if (!PhIsDigitCharacter(*A)) + { + return -1; + } + else if (!PhIsDigitCharacter(*B)) + { + return 1; + } + else if (*A < *B) + { + return -1; + } + else if (*A > *B) + { + return 1; + } + } + + return 0; +} + +FORCEINLINE LONG PhpCompareStringZNatural( + _In_ PWSTR A, + _In_ PWSTR B, + _In_ BOOLEAN IgnoreCase + ) +{ + /* strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This code has been modified for Process Hacker. + */ + + ULONG ai, bi; + WCHAR ca, cb; + LONG result; + BOOLEAN fractional; + + ai = 0; + bi = 0; + + while (TRUE) + { + ca = A[ai]; + cb = B[bi]; + + /* Skip over leading spaces or zeros. */ + + while (ca == ' ') + ca = A[++ai]; + + while (cb == ' ') + cb = B[++bi]; + + /* Process run of digits. */ + if (PhIsDigitCharacter(ca) && PhIsDigitCharacter(cb)) + { + fractional = (ca == '0' || cb == '0'); + + if (fractional) + { + if ((result = PhpCompareLeftNatural(A + ai, B + bi)) != 0) + return result; + } + else + { + if ((result = PhpCompareRightNatural(A + ai, B + bi)) != 0) + return result; + } + } + + if (!ca && !cb) + { + /* Strings are considered the same. */ + return 0; + } + + if (IgnoreCase) + { + ca = towupper(ca); + cb = towupper(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return 1; + + ai++; + bi++; + } +} + +/** + * Compares two strings in natural sort order. + * + * \param A The first string. + * \param B The second string. + * \param IgnoreCase Whether to ignore character cases. + */ +LONG PhCompareStringZNatural( + _In_ PWSTR A, + _In_ PWSTR B, + _In_ BOOLEAN IgnoreCase + ) +{ + if (!IgnoreCase) + return PhpCompareStringZNatural(A, B, FALSE); + else + return PhpCompareStringZNatural(A, B, TRUE); +} + +/** + * Compares two strings. + * + * \param String1 The first string. + * \param String2 The second string. + * \param IgnoreCase TRUE to perform a case-insensitive comparison, otherwise FALSE. + */ +LONG PhCompareStringRef( + _In_ PPH_STRINGREF String1, + _In_ PPH_STRINGREF String2, + _In_ BOOLEAN IgnoreCase + ) +{ + SIZE_T l1; + SIZE_T l2; + PWCHAR s1; + PWCHAR s2; + WCHAR c1; + WCHAR c2; + PWCHAR end; + + // Note: this function assumes that the difference between the lengths of the two strings can + // fit inside a LONG. + + l1 = String1->Length; + l2 = String2->Length; + assert(!(l1 & 1)); + assert(!(l2 & 1)); + s1 = String1->Buffer; + s2 = String2->Buffer; + + end = (PWCHAR)PTR_ADD_OFFSET(s1, l1 <= l2 ? l1 : l2); + + if (!IgnoreCase) + { + while (s1 != end) + { + c1 = *s1; + c2 = *s2; + + if (c1 != c2) + return (LONG)c1 - (LONG)c2; + + s1++; + s2++; + } + } + else + { + while (s1 != end) + { + c1 = *s1; + c2 = *s2; + + if (c1 != c2) + { + c1 = RtlUpcaseUnicodeChar(c1); + c2 = RtlUpcaseUnicodeChar(c2); + + if (c1 != c2) + return (LONG)c1 - (LONG)c2; + } + + s1++; + s2++; + } + } + + return (LONG)(l1 - l2); +} + +/** + * Determines if two strings are equal. + * + * \param String1 The first string. + * \param String2 The second string. + * \param IgnoreCase TRUE to perform a case-insensitive comparison, otherwise FALSE. + */ +BOOLEAN PhEqualStringRef( + _In_ PPH_STRINGREF String1, + _In_ PPH_STRINGREF String2, + _In_ BOOLEAN IgnoreCase + ) +{ + SIZE_T l1; + SIZE_T l2; + PWSTR s1; + PWSTR s2; + WCHAR c1; + WCHAR c2; + SIZE_T length; + + l1 = String1->Length; + l2 = String2->Length; + assert(!(l1 & 1)); + assert(!(l2 & 1)); + + if (l1 != l2) + return FALSE; + + s1 = String1->Buffer; + s2 = String2->Buffer; + + if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) + { + length = l1 / 16; + + if (length != 0) + { + __m128i b1; + __m128i b2; + + do + { + b1 = _mm_loadu_si128((__m128i *)s1); + b2 = _mm_loadu_si128((__m128i *)s2); + b1 = _mm_cmpeq_epi32(b1, b2); + + if (_mm_movemask_epi8(b1) != 0xffff) + { + if (!IgnoreCase) + { + return FALSE; + } + else + { + // Compare character-by-character to ignore case. + l1 = length * 16 + (l1 & 15); + l1 /= sizeof(WCHAR); + goto CompareCharacters; + } + } + + s1 += 16 / sizeof(WCHAR); + s2 += 16 / sizeof(WCHAR); + } while (--length != 0); + } + + // Compare character-by-character because we have no more 16-byte blocks to compare. + l1 = (l1 & 15) / sizeof(WCHAR); + } + else + { + length = l1 / sizeof(ULONG_PTR); + + if (length != 0) + { + do + { + if (*(PULONG_PTR)s1 != *(PULONG_PTR)s2) + { + if (!IgnoreCase) + { + return FALSE; + } + else + { + // Compare character-by-character to ignore case. + l1 = length * sizeof(ULONG_PTR) + (l1 & (sizeof(ULONG_PTR) - 1)); + l1 /= sizeof(WCHAR); + goto CompareCharacters; + } + } + + s1 += sizeof(ULONG_PTR) / sizeof(WCHAR); + s2 += sizeof(ULONG_PTR) / sizeof(WCHAR); + } while (--length != 0); + } + + // Compare character-by-character because we have no more ULONG_PTR blocks to compare. + l1 = (l1 & (sizeof(ULONG_PTR) - 1)) / sizeof(WCHAR); + } + +CompareCharacters: + if (l1 != 0) + { + if (!IgnoreCase) + { + do + { + c1 = *s1; + c2 = *s2; + + if (c1 != c2) + return FALSE; + + s1++; + s2++; + } while (--l1 != 0); + } + else + { + do + { + c1 = *s1; + c2 = *s2; + + if (c1 != c2) + { + c1 = RtlUpcaseUnicodeChar(c1); + c2 = RtlUpcaseUnicodeChar(c2); + + if (c1 != c2) + return FALSE; + } + + s1++; + s2++; + } while (--l1 != 0); + } + } + + return TRUE; +} + +/** + * Locates a character in a string. + * + * \param String The string to search. + * \param Character The character to search for. + * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. + * + * \return The index, in characters, of the first occurrence of \a Character in \a String1. If + * \a Character was not found, -1 is returned. + */ +ULONG_PTR PhFindCharInStringRef( + _In_ PPH_STRINGREF String, + _In_ WCHAR Character, + _In_ BOOLEAN IgnoreCase + ) +{ + PWSTR buffer; + SIZE_T length; + + buffer = String->Buffer; + length = String->Length / sizeof(WCHAR); + + if (!IgnoreCase) + { + if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) + { + SIZE_T length16; + + length16 = String->Length / 16; + length &= 7; + + if (length16 != 0) + { + __m128i pattern; + __m128i block; + ULONG mask; + ULONG index; + + pattern = _mm_set1_epi16(Character); + + do + { + block = _mm_loadu_si128((__m128i *)buffer); + block = _mm_cmpeq_epi16(block, pattern); + mask = _mm_movemask_epi8(block); + + if (_BitScanForward(&index, mask)) + return (String->Length - length16 * 16) / sizeof(WCHAR) - length + index / 2; + + buffer += 16 / sizeof(WCHAR); + } while (--length16 != 0); + } + } + + if (length != 0) + { + do + { + if (*buffer == Character) + return String->Length / sizeof(WCHAR) - length; + + buffer++; + } while (--length != 0); + } + } + else + { + if (length != 0) + { + WCHAR c; + + c = RtlUpcaseUnicodeChar(Character); + + do + { + if (RtlUpcaseUnicodeChar(*buffer) == c) + return String->Length / sizeof(WCHAR) - length; + + buffer++; + } while (--length != 0); + } + } + + return -1; +} + +/** + * Locates a character in a string, searching backwards. + * + * \param String The string to search. + * \param Character The character to search for. + * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. + * + * \return The index, in characters, of the last occurrence of \a Character in \a String1. If + * \a Character was not found, -1 is returned. + */ +ULONG_PTR PhFindLastCharInStringRef( + _In_ PPH_STRINGREF String, + _In_ WCHAR Character, + _In_ BOOLEAN IgnoreCase + ) +{ + PWCHAR buffer; + SIZE_T length; + + buffer = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length); + length = String->Length / sizeof(WCHAR); + + if (!IgnoreCase) + { + if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2) + { + SIZE_T length16; + + length16 = String->Length / 16; + length &= 7; + + if (length16 != 0) + { + __m128i pattern; + __m128i block; + ULONG mask; + ULONG index; + + pattern = _mm_set1_epi16(Character); + buffer -= 16 / sizeof(WCHAR); + + do + { + block = _mm_loadu_si128((__m128i *)buffer); + block = _mm_cmpeq_epi16(block, pattern); + mask = _mm_movemask_epi8(block); + + if (_BitScanReverse(&index, mask)) + return (length16 - 1) * 16 / sizeof(WCHAR) + length + index / 2; + + buffer -= 16 / sizeof(WCHAR); + } while (--length16 != 0); + + buffer += 16 / sizeof(WCHAR); + } + } + + if (length != 0) + { + buffer--; + + do + { + if (*buffer == Character) + return length - 1; + + buffer--; + } while (--length != 0); + } + } + else + { + if (length != 0) + { + WCHAR c; + + c = RtlUpcaseUnicodeChar(Character); + buffer--; + + do + { + if (RtlUpcaseUnicodeChar(*buffer) == c) + return length - 1; + + buffer--; + } while (--length != 0); + } + } + + return -1; +} + +/** + * Locates a string in a string. + * + * \param String The string to search. + * \param SubString The string to search for. + * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. + * + * \return The index, in characters, of the first occurrence of \a SubString in \a String. If + * \a SubString was not found, -1 is returned. + */ +ULONG_PTR PhFindStringInStringRef( + _In_ PPH_STRINGREF String, + _In_ PPH_STRINGREF SubString, + _In_ BOOLEAN IgnoreCase + ) +{ + SIZE_T length1; + SIZE_T length2; + PH_STRINGREF sr1; + PH_STRINGREF sr2; + WCHAR c; + SIZE_T i; + + length1 = String->Length / sizeof(WCHAR); + length2 = SubString->Length / sizeof(WCHAR); + + // Can't be a substring if it's bigger than the first string. + if (length2 > length1) + return -1; + // We always get a match if the substring is zero-length. + if (length2 == 0) + return 0; + + sr1.Buffer = String->Buffer; + sr1.Length = SubString->Length - sizeof(WCHAR); + sr2.Buffer = SubString->Buffer; + sr2.Length = SubString->Length - sizeof(WCHAR); + + if (!IgnoreCase) + { + c = *sr2.Buffer++; + + for (i = length1 - length2 + 1; i != 0; i--) + { + if (*sr1.Buffer++ == c && PhEqualStringRef(&sr1, &sr2, FALSE)) + { + goto FoundUString; + } + } + } + else + { + c = RtlUpcaseUnicodeChar(*sr2.Buffer++); + + for (i = length1 - length2 + 1; i != 0; i--) + { + if (RtlUpcaseUnicodeChar(*sr1.Buffer++) == c && PhEqualStringRef(&sr1, &sr2, TRUE)) + { + goto FoundUString; + } + } + } + + return -1; +FoundUString: + return (ULONG_PTR)(sr1.Buffer - String->Buffer - 1); +} + +/** + * Splits a string. + * + * \param Input The input string. + * \param Separator The character to split at. + * \param FirstPart A variable which receives the part of \a Input before the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * \a Input. + * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * an empty string. + * + * \return TRUE if \a Separator was found in \a Input, otherwise FALSE. + */ +BOOLEAN PhSplitStringRefAtChar( + _In_ PPH_STRINGREF Input, + _In_ WCHAR Separator, + _Out_ PPH_STRINGREF FirstPart, + _Out_ PPH_STRINGREF SecondPart + ) +{ + PH_STRINGREF input; + ULONG_PTR index; + + input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input + index = PhFindCharInStringRef(Input, Separator, FALSE); + + if (index == -1) + { + // The separator was not found. + + FirstPart->Buffer = Input->Buffer; + FirstPart->Length = Input->Length; + SecondPart->Buffer = NULL; + SecondPart->Length = 0; + + return FALSE; + } + + FirstPart->Buffer = input.Buffer; + FirstPart->Length = index * sizeof(WCHAR); + SecondPart->Buffer = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, index * sizeof(WCHAR) + sizeof(WCHAR)); + SecondPart->Length = input.Length - index * sizeof(WCHAR) - sizeof(WCHAR); + + return TRUE; +} + +/** + * Splits a string at the last occurrence of a character. + * + * \param Input The input string. + * \param Separator The character to split at. + * \param FirstPart A variable which receives the part of \a Input before the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * \a Input. + * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * an empty string. + * + * \return TRUE if \a Separator was found in \a Input, otherwise FALSE. + */ +BOOLEAN PhSplitStringRefAtLastChar( + _In_ PPH_STRINGREF Input, + _In_ WCHAR Separator, + _Out_ PPH_STRINGREF FirstPart, + _Out_ PPH_STRINGREF SecondPart + ) +{ + PH_STRINGREF input; + ULONG_PTR index; + + input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input + index = PhFindLastCharInStringRef(Input, Separator, FALSE); + + if (index == -1) + { + // The separator was not found. + + FirstPart->Buffer = Input->Buffer; + FirstPart->Length = Input->Length; + SecondPart->Buffer = NULL; + SecondPart->Length = 0; + + return FALSE; + } + + FirstPart->Buffer = input.Buffer; + FirstPart->Length = index * sizeof(WCHAR); + SecondPart->Buffer = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, index * sizeof(WCHAR) + sizeof(WCHAR)); + SecondPart->Length = input.Length - index * sizeof(WCHAR) - sizeof(WCHAR); + + return TRUE; +} + +/** + * Splits a string. + * + * \param Input The input string. + * \param Separator The string to split at. + * \param IgnoreCase TRUE to perform a case-insensitive search, otherwise FALSE. + * \param FirstPart A variable which receives the part of \a Input before the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * \a Input. + * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * an empty string. + * + * \return TRUE if \a Separator was found in \a Input, otherwise FALSE. + */ +BOOLEAN PhSplitStringRefAtString( + _In_ PPH_STRINGREF Input, + _In_ PPH_STRINGREF Separator, + _In_ BOOLEAN IgnoreCase, + _Out_ PPH_STRINGREF FirstPart, + _Out_ PPH_STRINGREF SecondPart + ) +{ + PH_STRINGREF input; + ULONG_PTR index; + + input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input + index = PhFindStringInStringRef(Input, Separator, IgnoreCase); + + if (index == -1) + { + // The separator was not found. + + FirstPart->Buffer = Input->Buffer; + FirstPart->Length = Input->Length; + SecondPart->Buffer = NULL; + SecondPart->Length = 0; + + return FALSE; + } + + FirstPart->Buffer = input.Buffer; + FirstPart->Length = index * sizeof(WCHAR); + SecondPart->Buffer = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, index * sizeof(WCHAR) + Separator->Length); + SecondPart->Length = input.Length - index * sizeof(WCHAR) - Separator->Length; + + return TRUE; +} + +/** + * Splits a string. + * + * \param Input The input string. + * \param Separator The character set, string or range to split at. + * \param Flags A combination of flags. + * \li \c PH_SPLIT_AT_CHAR_SET \a Separator specifies a character set. \a Input will be split at a + * character which is contained in \a Separator. + * \li \c PH_SPLIT_AT_STRING \a Separator specifies a string. \a Input will be split an occurrence + * of \a Separator. + * \li \c PH_SPLIT_AT_RANGE \a Separator specifies a range. The \a Buffer field contains a character + * index cast to \c PWSTR and the \a Length field contains the length of the range, in bytes. + * \li \c PH_SPLIT_CASE_INSENSITIVE Specifies a case-insensitive search. + * \li \c PH_SPLIT_COMPLEMENT_CHAR_SET If used with \c PH_SPLIT_AT_CHAR_SET, the separator is a + * character which is not contained in \a Separator. + * \li \c PH_SPLIT_START_AT_END If used with \c PH_SPLIT_AT_CHAR_SET, the search is performed + * starting from the end of the string. + * \li \c PH_SPLIT_CHAR_SET_IS_UPPERCASE If used with \c PH_SPLIT_CASE_INSENSITIVE, specifies that + * the character set in \a Separator contains only uppercase characters. + * \param FirstPart A variable which receives the part of \a Input before the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * \a Input. + * \param SecondPart A variable which recieves the part of \a Input after the separator. This may be + * the same variable as \a Input. If the separator is not found in \a Input, this variable is set to + * an empty string. + * \param SeparatorPart A variable which receives the part of \a Input that is the separator. If the + * separator is not found in \a Input, this variable is set to an empty string. + * + * \return TRUE if a separator was found in \a Input, otherwise FALSE. + */ +BOOLEAN PhSplitStringRefEx( + _In_ PPH_STRINGREF Input, + _In_ PPH_STRINGREF Separator, + _In_ ULONG Flags, + _Out_ PPH_STRINGREF FirstPart, + _Out_ PPH_STRINGREF SecondPart, + _Out_opt_ PPH_STRINGREF SeparatorPart + ) +{ + PH_STRINGREF input; + SIZE_T separatorIndex; + SIZE_T separatorLength; + PWCHAR charSet; + SIZE_T charSetCount; + BOOLEAN charSetTable[256]; + BOOLEAN charSetTableComplete; + SIZE_T i; + SIZE_T j; + USHORT c; + PWCHAR s; + LONG_PTR direction; + + input = *Input; // Get a copy of the input because FirstPart/SecondPart/SeparatorPart may alias Input + + if (Flags & PH_SPLIT_AT_RANGE) + { + separatorIndex = (SIZE_T)Separator->Buffer; + separatorLength = Separator->Length; + + if (separatorIndex == -1) + goto SeparatorNotFound; + + goto SeparatorFound; + } + else if (Flags & PH_SPLIT_AT_STRING) + { + if (Flags & PH_SPLIT_START_AT_END) + { + // not implemented + goto SeparatorNotFound; + } + + separatorIndex = PhFindStringInStringRef(Input, Separator, !!(Flags & PH_SPLIT_CASE_INSENSITIVE)); + + if (separatorIndex == -1) + goto SeparatorNotFound; + + separatorLength = Separator->Length; + goto SeparatorFound; + } + + // Special case for character sets with only one character. + if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET) && Separator->Length == sizeof(WCHAR)) + { + if (!(Flags & PH_SPLIT_START_AT_END)) + separatorIndex = PhFindCharInStringRef(Input, Separator->Buffer[0], !!(Flags & PH_SPLIT_CASE_INSENSITIVE)); + else + separatorIndex = PhFindLastCharInStringRef(Input, Separator->Buffer[0], !!(Flags & PH_SPLIT_CASE_INSENSITIVE)); + + if (separatorIndex == -1) + goto SeparatorNotFound; + + separatorLength = sizeof(WCHAR); + goto SeparatorFound; + } + + if (input.Length == 0) + goto SeparatorNotFound; + + // Build the character set lookup table. + + charSet = Separator->Buffer; + charSetCount = Separator->Length / sizeof(WCHAR); + memset(charSetTable, 0, sizeof(charSetTable)); + charSetTableComplete = TRUE; + + for (i = 0; i < charSetCount; i++) + { + c = charSet[i]; + + if (Flags & PH_SPLIT_CASE_INSENSITIVE) + c = RtlUpcaseUnicodeChar(c); + + charSetTable[c & 0xff] = TRUE; + + if (c >= 256) + charSetTableComplete = FALSE; + } + + // Perform the search. + + i = input.Length / sizeof(WCHAR); + separatorLength = sizeof(WCHAR); + + if (!(Flags & PH_SPLIT_START_AT_END)) + { + s = input.Buffer; + direction = 1; + } + else + { + s = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, input.Length - sizeof(WCHAR)); + direction = -1; + } + + do + { + c = *s; + + if (Flags & PH_SPLIT_CASE_INSENSITIVE) + c = RtlUpcaseUnicodeChar(c); + + if (c < 256 && charSetTableComplete) + { + if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET)) + { + if (charSetTable[c]) + goto CharFound; + } + else + { + if (!charSetTable[c]) + goto CharFound; + } + } + else + { + if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET)) + { + if (charSetTable[c & 0xff]) + { + if (!(Flags & PH_SPLIT_CASE_INSENSITIVE) || (Flags & PH_SPLIT_CHAR_SET_IS_UPPERCASE)) + { + for (j = 0; j < charSetCount; j++) + { + if (charSet[j] == c) + goto CharFound; + } + } + else + { + for (j = 0; j < charSetCount; j++) + { + if (RtlUpcaseUnicodeChar(charSet[j]) == c) + goto CharFound; + } + } + } + } + else + { + if (charSetTable[c & 0xff]) + { + if (!(Flags & PH_SPLIT_CASE_INSENSITIVE) || (Flags & PH_SPLIT_CHAR_SET_IS_UPPERCASE)) + { + for (j = 0; j < charSetCount; j++) + { + if (charSet[j] == c) + break; + } + } + else + { + for (j = 0; j < charSetCount; j++) + { + if (RtlUpcaseUnicodeChar(charSet[j]) == c) + break; + } + } + + if (j == charSetCount) + goto CharFound; + } + else + { + goto CharFound; + } + } + } + + s += direction; + } while (--i != 0); + + goto SeparatorNotFound; + +CharFound: + separatorIndex = s - input.Buffer; + +SeparatorFound: + FirstPart->Buffer = input.Buffer; + FirstPart->Length = separatorIndex * sizeof(WCHAR); + SecondPart->Buffer = (PWCHAR)PTR_ADD_OFFSET(input.Buffer, separatorIndex * sizeof(WCHAR) + separatorLength); + SecondPart->Length = input.Length - separatorIndex * sizeof(WCHAR) - separatorLength; + + if (SeparatorPart) + { + SeparatorPart->Buffer = input.Buffer + separatorIndex; + SeparatorPart->Length = separatorLength; + } + + return TRUE; + +SeparatorNotFound: + FirstPart->Buffer = input.Buffer; + FirstPart->Length = input.Length; + SecondPart->Buffer = NULL; + SecondPart->Length = 0; + + if (SeparatorPart) + { + SeparatorPart->Buffer = NULL; + SeparatorPart->Length = 0; + } + + return FALSE; +} + +VOID PhTrimStringRef( + _Inout_ PPH_STRINGREF String, + _In_ PPH_STRINGREF CharSet, + _In_ ULONG Flags + ) +{ + PWCHAR charSet; + SIZE_T charSetCount; + BOOLEAN charSetTable[256]; + BOOLEAN charSetTableComplete; + SIZE_T i; + SIZE_T j; + USHORT c; + SIZE_T trimCount; + SIZE_T count; + PWCHAR s; + + if (String->Length == 0 || CharSet->Length == 0) + return; + + if (CharSet->Length == sizeof(WCHAR)) + { + c = CharSet->Buffer[0]; + + if (!(Flags & PH_TRIM_END_ONLY)) + { + trimCount = 0; + count = String->Length / sizeof(WCHAR); + s = String->Buffer; + + while (count-- != 0) + { + if (*s++ != c) + break; + + trimCount++; + } + + PhSkipStringRef(String, trimCount * sizeof(WCHAR)); + } + + if (!(Flags & PH_TRIM_START_ONLY)) + { + trimCount = 0; + count = String->Length / sizeof(WCHAR); + s = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length - sizeof(WCHAR)); + + while (count-- != 0) + { + if (*s-- != c) + break; + + trimCount++; + } + + String->Length -= trimCount * sizeof(WCHAR); + } + + return; + } + + // Build the character set lookup table. + + charSet = CharSet->Buffer; + charSetCount = CharSet->Length / sizeof(WCHAR); + memset(charSetTable, 0, sizeof(charSetTable)); + charSetTableComplete = TRUE; + + for (i = 0; i < charSetCount; i++) + { + c = charSet[i]; + charSetTable[c & 0xff] = TRUE; + + if (c >= 256) + charSetTableComplete = FALSE; + } + + // Trim the string. + + if (!(Flags & PH_TRIM_END_ONLY)) + { + trimCount = 0; + count = String->Length / sizeof(WCHAR); + s = String->Buffer; + + while (count-- != 0) + { + c = *s++; + + if (!charSetTable[c & 0xff]) + break; + + if (!charSetTableComplete) + { + for (j = 0; j < charSetCount; j++) + { + if (charSet[j] == c) + goto CharFound; + } + + break; + } + +CharFound: + trimCount++; + } + + PhSkipStringRef(String, trimCount * sizeof(WCHAR)); + } + + if (!(Flags & PH_TRIM_START_ONLY)) + { + trimCount = 0; + count = String->Length / sizeof(WCHAR); + s = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length - sizeof(WCHAR)); + + while (count-- != 0) + { + c = *s--; + + if (!charSetTable[c & 0xff]) + break; + + if (!charSetTableComplete) + { + for (j = 0; j < charSetCount; j++) + { + if (charSet[j] == c) + goto CharFound2; + } + + break; + } + +CharFound2: + trimCount++; + } + + String->Length -= trimCount * sizeof(WCHAR); + } +} + +/** + * Creates a string object from an existing null-terminated string. + * + * \param Buffer A null-terminated Unicode string. + */ +PPH_STRING PhCreateString( + _In_ PWSTR Buffer + ) +{ + return PhCreateStringEx(Buffer, wcslen(Buffer) * sizeof(WCHAR)); +} + +/** + * Creates a string object using a specified length. + * + * \param Buffer A null-terminated Unicode string. + * \param Length The length, in bytes, of the string. + */ +PPH_STRING PhCreateStringEx( + _In_opt_ PWCHAR Buffer, + _In_ SIZE_T Length + ) +{ + PPH_STRING string; + + string = PhCreateObject( + UFIELD_OFFSET(PH_STRING, Data) + Length + sizeof(UNICODE_NULL), // Null terminator for compatibility + PhStringType + ); + + assert(!(Length & 1)); + string->Length = Length; + string->Buffer = string->Data; + *(PWCHAR)PTR_ADD_OFFSET(string->Buffer, Length) = UNICODE_NULL; + + if (Buffer) + { + memcpy(string->Buffer, Buffer, Length); + } + + return string; +} + +/** + * Obtains a reference to a zero-length string. + */ +PPH_STRING PhReferenceEmptyString( + VOID + ) +{ + PPH_STRING string; + PPH_STRING newString; + + string = InterlockedCompareExchangePointer( + &PhSharedEmptyString, + NULL, + NULL + ); + + if (!string) + { + newString = PhCreateStringEx(NULL, 0); + + string = InterlockedCompareExchangePointer( + &PhSharedEmptyString, + newString, + NULL + ); + + if (!string) + { + string = newString; // success + } + else + { + PhDereferenceObject(newString); + } + } + + return PhReferenceObject(string); +} + +/** + * Concatenates multiple strings. + * + * \param Count The number of strings to concatenate. + */ +PPH_STRING PhConcatStrings( + _In_ ULONG Count, + ... + ) +{ + va_list argptr; + + va_start(argptr, Count); + + return PhConcatStrings_V(Count, argptr); +} + +/** + * Concatenates multiple strings. + * + * \param Count The number of strings to concatenate. + * \param ArgPtr A pointer to an array of strings. + */ +PPH_STRING PhConcatStrings_V( + _In_ ULONG Count, + _In_ va_list ArgPtr + ) +{ + va_list argptr; + ULONG i; + SIZE_T totalLength = 0; + SIZE_T stringLength; + SIZE_T cachedLengths[PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE]; + PWSTR arg; + PPH_STRING string; + + // Compute the total length, in bytes, of the strings. + + argptr = ArgPtr; + + for (i = 0; i < Count; i++) + { + arg = va_arg(argptr, PWSTR); + stringLength = PhCountStringZ(arg) * sizeof(WCHAR); + totalLength += stringLength; + + if (i < PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE) + cachedLengths[i] = stringLength; + } + + // Create the string. + + string = PhCreateStringEx(NULL, totalLength); + totalLength = 0; + + // Append the strings one by one. + + argptr = ArgPtr; + + for (i = 0; i < Count; i++) + { + arg = va_arg(argptr, PWSTR); + + if (i < PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE) + stringLength = cachedLengths[i]; + else + stringLength = PhCountStringZ(arg) * sizeof(WCHAR); + + memcpy( + PTR_ADD_OFFSET(string->Buffer, totalLength), + arg, + stringLength + ); + totalLength += stringLength; + } + + return string; +} + +/** + * Concatenates two strings. + * + * \param String1 The first string. + * \param String2 The second string. + */ +PPH_STRING PhConcatStrings2( + _In_ PWSTR String1, + _In_ PWSTR String2 + ) +{ + PPH_STRING string; + SIZE_T length1; + SIZE_T length2; + + length1 = PhCountStringZ(String1) * sizeof(WCHAR); + length2 = PhCountStringZ(String2) * sizeof(WCHAR); + string = PhCreateStringEx(NULL, length1 + length2); + memcpy( + string->Buffer, + String1, + length1 + ); + memcpy( + PTR_ADD_OFFSET(string->Buffer, length1), + String2, + length2 + ); + + return string; +} + +/** + * Concatenates two strings. + * + * \param String1 The first string. + * \param String2 The second string. + */ +PPH_STRING PhConcatStringRef2( + _In_ PPH_STRINGREF String1, + _In_ PPH_STRINGREF String2 + ) +{ + PPH_STRING string; + + assert(!(String1->Length & 1)); + assert(!(String2->Length & 1)); + + string = PhCreateStringEx(NULL, String1->Length + String2->Length); + memcpy( + string->Buffer, + String1->Buffer, + String1->Length + ); + memcpy( + PTR_ADD_OFFSET(string->Buffer, String1->Length), + String2->Buffer, + String2->Length + ); + + return string; +} + +/** + * Concatenates three strings. + * + * \param String1 The first string. + * \param String2 The second string. + * \param String3 The third string. + */ +PPH_STRING PhConcatStringRef3( + _In_ PPH_STRINGREF String1, + _In_ PPH_STRINGREF String2, + _In_ PPH_STRINGREF String3 + ) +{ + PPH_STRING string; + PCHAR buffer; + + assert(!(String1->Length & 1)); + assert(!(String2->Length & 1)); + assert(!(String3->Length & 1)); + + string = PhCreateStringEx(NULL, String1->Length + String2->Length + String3->Length); + + buffer = (PCHAR)string->Buffer; + memcpy(buffer, String1->Buffer, String1->Length); + + buffer += String1->Length; + memcpy(buffer, String2->Buffer, String2->Length); + + buffer += String2->Length; + memcpy(buffer, String3->Buffer, String3->Length); + + return string; +} + +/** + * Creates a string using format specifiers. + * + * \param Format The format-control string. + */ +PPH_STRING PhFormatString( + _In_ _Printf_format_string_ PWSTR Format, + ... + ) +{ + va_list argptr; + + va_start(argptr, Format); + + return PhFormatString_V(Format, argptr); +} + +/** + * Creates a string using format specifiers. + * + * \param Format The format-control string. + * \param ArgPtr A pointer to the list of arguments. + */ +PPH_STRING PhFormatString_V( + _In_ _Printf_format_string_ PWSTR Format, + _In_ va_list ArgPtr + ) +{ + PPH_STRING string; + int length; + + length = _vscwprintf(Format, ArgPtr); + + if (length == -1) + return NULL; + + string = PhCreateStringEx(NULL, length * sizeof(WCHAR)); + _vsnwprintf(string->Buffer, length, Format, ArgPtr); + + return string; +} + +/** + * Creates a bytes object from an existing null-terminated string of bytes. + * + * \param Buffer A null-terminated byte string. + */ +PPH_BYTES PhCreateBytes( + _In_ PSTR Buffer + ) +{ + return PhCreateBytesEx(Buffer, strlen(Buffer) * sizeof(CHAR)); +} + +/** + * Creates a bytes object. + * + * \param Buffer An array of bytes. + * \param Length The length of \a Buffer, in bytes. + */ +PPH_BYTES PhCreateBytesEx( + _In_opt_ PCHAR Buffer, + _In_ SIZE_T Length + ) +{ + PPH_BYTES bytes; + + bytes = PhCreateObject( + UFIELD_OFFSET(PH_BYTES, Data) + Length + sizeof(ANSI_NULL), // Null terminator for compatibility + PhBytesType + ); + + bytes->Length = Length; + bytes->Buffer = bytes->Data; + bytes->Buffer[Length] = ANSI_NULL; + + if (Buffer) + { + memcpy(bytes->Buffer, Buffer, Length); + } + + return bytes; +} + +BOOLEAN PhWriteUnicodeDecoder( + _Inout_ PPH_UNICODE_DECODER Decoder, + _In_ ULONG CodeUnit + ) +{ + switch (Decoder->Encoding) + { + case PH_UNICODE_UTF8: + if (Decoder->InputCount >= 4) + return FALSE; + Decoder->u.Utf8.Input[Decoder->InputCount] = (UCHAR)CodeUnit; + Decoder->InputCount++; + return TRUE; + case PH_UNICODE_UTF16: + if (Decoder->InputCount >= 2) + return FALSE; + Decoder->u.Utf16.Input[Decoder->InputCount] = (USHORT)CodeUnit; + Decoder->InputCount++; + return TRUE; + case PH_UNICODE_UTF32: + if (Decoder->InputCount >= 1) + return FALSE; + Decoder->u.Utf32.Input = CodeUnit; + Decoder->InputCount = 1; + return TRUE; + default: + PhRaiseStatus(STATUS_UNSUCCESSFUL); + } +} + +BOOLEAN PhpReadUnicodeDecoder( + _Inout_ PPH_UNICODE_DECODER Decoder, + _Out_ PULONG CodeUnit + ) +{ + switch (Decoder->Encoding) + { + case PH_UNICODE_UTF8: + if (Decoder->InputCount == 0) + return FALSE; + *CodeUnit = Decoder->u.Utf8.Input[0]; + Decoder->u.Utf8.Input[0] = Decoder->u.Utf8.Input[1]; + Decoder->u.Utf8.Input[1] = Decoder->u.Utf8.Input[2]; + Decoder->u.Utf8.Input[2] = Decoder->u.Utf8.Input[3]; + Decoder->InputCount--; + return TRUE; + case PH_UNICODE_UTF16: + if (Decoder->InputCount == 0) + return FALSE; + *CodeUnit = Decoder->u.Utf16.Input[0]; + Decoder->u.Utf16.Input[0] = Decoder->u.Utf16.Input[1]; + Decoder->InputCount--; + return TRUE; + case PH_UNICODE_UTF32: + if (Decoder->InputCount == 0) + return FALSE; + *CodeUnit = Decoder->u.Utf32.Input; + Decoder->InputCount--; + return TRUE; + default: + PhRaiseStatus(STATUS_UNSUCCESSFUL); + } +} + +VOID PhpUnreadUnicodeDecoder( + _Inout_ PPH_UNICODE_DECODER Decoder, + _In_ ULONG CodeUnit + ) +{ + switch (Decoder->Encoding) + { + case PH_UNICODE_UTF8: + if (Decoder->InputCount >= 4) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + Decoder->u.Utf8.Input[3] = Decoder->u.Utf8.Input[2]; + Decoder->u.Utf8.Input[2] = Decoder->u.Utf8.Input[1]; + Decoder->u.Utf8.Input[1] = Decoder->u.Utf8.Input[0]; + Decoder->u.Utf8.Input[0] = (UCHAR)CodeUnit; + Decoder->InputCount++; + break; + case PH_UNICODE_UTF16: + if (Decoder->InputCount >= 2) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + Decoder->u.Utf16.Input[1] = Decoder->u.Utf16.Input[0]; + Decoder->u.Utf16.Input[0] = (USHORT)CodeUnit; + Decoder->InputCount++; + break; + case PH_UNICODE_UTF32: + if (Decoder->InputCount >= 1) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + Decoder->u.Utf32.Input = CodeUnit; + Decoder->InputCount = 1; + break; + default: + PhRaiseStatus(STATUS_UNSUCCESSFUL); + } +} + +BOOLEAN PhpDecodeUtf8Error( + _Inout_ PPH_UNICODE_DECODER Decoder, + _Out_ PULONG CodePoint, + _In_ ULONG Which + ) +{ + if (Which >= 4) + PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit4); + if (Which >= 3) + PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit3); + if (Which >= 2) + PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit2); + + *CodePoint = (ULONG)Decoder->u.Utf8.CodeUnit1 + 0xdc00; + Decoder->State = 0; + + return TRUE; +} + +BOOLEAN PhDecodeUnicodeDecoder( + _Inout_ PPH_UNICODE_DECODER Decoder, + _Out_ PULONG CodePoint + ) +{ + ULONG codeUnit; + + while (TRUE) + { + switch (Decoder->Encoding) + { + case PH_UNICODE_UTF8: + if (!PhpReadUnicodeDecoder(Decoder, &codeUnit)) + return FALSE; + + switch (Decoder->State) + { + case 0: + Decoder->u.Utf8.CodeUnit1 = (UCHAR)codeUnit; + + if (codeUnit < 0x80) + { + *CodePoint = codeUnit; + return TRUE; + } + else if (codeUnit < 0xc2) + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 1); + } + else if (codeUnit < 0xe0) + { + Decoder->State = 1; // 2 byte sequence + continue; + } + else if (codeUnit < 0xf0) + { + Decoder->State = 2; // 3 byte sequence + continue; + } + else if (codeUnit < 0xf5) + { + Decoder->State = 3; // 4 byte sequence + continue; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 1); + } + + break; + case 1: // 2 byte sequence + Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit; + + if ((codeUnit & 0xc0) == 0x80) + { + *CodePoint = ((ULONG)Decoder->u.Utf8.CodeUnit1 << 6) + codeUnit - 0x3080; + Decoder->State = 0; + return TRUE; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 2); + } + + break; + case 2: // 3 byte sequence (1) + Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit; + + if (((codeUnit & 0xc0) == 0x80) && + (Decoder->u.Utf8.CodeUnit1 != 0xe0 || codeUnit >= 0xa0)) + { + Decoder->State = 4; // 3 byte sequence (2) + continue; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 2); + } + + break; + case 3: // 4 byte sequence (1) + Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit; + + if (((codeUnit & 0xc0) == 0x80) && + (Decoder->u.Utf8.CodeUnit1 != 0xf0 || codeUnit >= 0x90) && + (Decoder->u.Utf8.CodeUnit1 != 0xf4 || codeUnit < 0x90)) + { + Decoder->State = 5; // 4 byte sequence (2) + continue; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 2); + } + + break; + case 4: // 3 byte sequence (2) + Decoder->u.Utf8.CodeUnit3 = (UCHAR)codeUnit; + + if ((codeUnit & 0xc0) == 0x80) + { + *CodePoint = + ((ULONG)Decoder->u.Utf8.CodeUnit1 << 12) + + ((ULONG)Decoder->u.Utf8.CodeUnit2 << 6) + + codeUnit - 0xe2080; + Decoder->State = 0; + return TRUE; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 3); + } + + break; + case 5: // 4 byte sequence (2) + Decoder->u.Utf8.CodeUnit3 = (UCHAR)codeUnit; + + if ((codeUnit & 0xc0) == 0x80) + { + Decoder->State = 6; // 4 byte sequence (3) + continue; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 3); + } + + break; + case 6: // 4 byte sequence (3) + Decoder->u.Utf8.CodeUnit4 = (UCHAR)codeUnit; + + if ((codeUnit & 0xc0) == 0x80) + { + *CodePoint = + ((ULONG)Decoder->u.Utf8.CodeUnit1 << 18) + + ((ULONG)Decoder->u.Utf8.CodeUnit2 << 12) + + ((ULONG)Decoder->u.Utf8.CodeUnit3 << 6) + + codeUnit - 0x3c82080; + Decoder->State = 0; + return TRUE; + } + else + { + return PhpDecodeUtf8Error(Decoder, CodePoint, 4); + } + + break; + } + + return FALSE; + case PH_UNICODE_UTF16: + if (!PhpReadUnicodeDecoder(Decoder, &codeUnit)) + return FALSE; + + switch (Decoder->State) + { + case 0: + if (PH_UNICODE_UTF16_IS_HIGH_SURROGATE(codeUnit)) + { + Decoder->u.Utf16.CodeUnit = (USHORT)codeUnit; + Decoder->State = 1; + continue; + } + else + { + *CodePoint = codeUnit; + return TRUE; + } + break; + case 1: + if (PH_UNICODE_UTF16_IS_LOW_SURROGATE(codeUnit)) + { + *CodePoint = PH_UNICODE_UTF16_TO_CODE_POINT(Decoder->u.Utf16.CodeUnit, codeUnit); + Decoder->State = 0; + return TRUE; + } + else + { + *CodePoint = Decoder->u.Utf16.CodeUnit; + PhpUnreadUnicodeDecoder(Decoder, codeUnit); + Decoder->State = 0; + return TRUE; + } + break; + } + + return FALSE; + case PH_UNICODE_UTF32: + if (PhpReadUnicodeDecoder(Decoder, CodePoint)) + return TRUE; + return FALSE; + default: + return FALSE; + } + } +} + +BOOLEAN PhEncodeUnicode( + _In_ UCHAR Encoding, + _In_ ULONG CodePoint, + _Out_opt_ PVOID CodeUnits, + _Out_ PULONG NumberOfCodeUnits + ) +{ + switch (Encoding) + { + case PH_UNICODE_UTF8: + { + PUCHAR codeUnits = CodeUnits; + + if (CodePoint < 0x80) + { + *NumberOfCodeUnits = 1; + + if (codeUnits) + codeUnits[0] = (UCHAR)CodePoint; + } + else if (CodePoint <= 0x7ff) + { + *NumberOfCodeUnits = 2; + + if (codeUnits) + { + codeUnits[0] = (UCHAR)(CodePoint >> 6) + 0xc0; + codeUnits[1] = (UCHAR)(CodePoint & 0x3f) + 0x80; + } + } + else if (CodePoint <= 0xffff) + { + *NumberOfCodeUnits = 3; + + if (codeUnits) + { + codeUnits[0] = (UCHAR)(CodePoint >> 12) + 0xe0; + codeUnits[1] = (UCHAR)((CodePoint >> 6) & 0x3f) + 0x80; + codeUnits[2] = (UCHAR)(CodePoint & 0x3f) + 0x80; + } + } + else if (CodePoint <= PH_UNICODE_MAX_CODE_POINT) + { + *NumberOfCodeUnits = 4; + + if (codeUnits) + { + codeUnits[0] = (UCHAR)(CodePoint >> 18) + 0xf0; + codeUnits[1] = (UCHAR)((CodePoint >> 12) & 0x3f) + 0x80; + codeUnits[2] = (UCHAR)((CodePoint >> 6) & 0x3f) + 0x80; + codeUnits[3] = (UCHAR)(CodePoint & 0x3f) + 0x80; + } + } + else + { + return FALSE; + } + } + return TRUE; + case PH_UNICODE_UTF16: + { + PUSHORT codeUnits = CodeUnits; + + if (CodePoint < 0x10000) + { + *NumberOfCodeUnits = 1; + + if (codeUnits) + codeUnits[0] = (USHORT)CodePoint; + } + else if (CodePoint <= PH_UNICODE_MAX_CODE_POINT) + { + *NumberOfCodeUnits = 2; + + if (codeUnits) + { + codeUnits[0] = PH_UNICODE_UTF16_TO_HIGH_SURROGATE(CodePoint); + codeUnits[1] = PH_UNICODE_UTF16_TO_LOW_SURROGATE(CodePoint); + } + } + else + { + return FALSE; + } + } + return TRUE; + case PH_UNICODE_UTF32: + *NumberOfCodeUnits = 1; + if (CodeUnits) + *(PULONG)CodeUnits = CodePoint; + return TRUE; + default: + return FALSE; + } +} + +/** + * Converts an ASCII string to a UTF-16 string by zero-extending each byte. + * + * \param Input The original ASCII string. + * \param InputLength The length of \a Input. + * \param Output A buffer which will contain the converted string. + */ +VOID PhZeroExtendToUtf16Buffer( + _In_reads_bytes_(InputLength) PCH Input, + _In_ SIZE_T InputLength, + _Out_writes_bytes_(InputLength * sizeof(WCHAR)) PWCH Output + ) +{ + SIZE_T inputLength; + + inputLength = InputLength & -4; + + if (inputLength) + { + do + { + Output[0] = C_1uTo2(Input[0]); + Output[1] = C_1uTo2(Input[1]); + Output[2] = C_1uTo2(Input[2]); + Output[3] = C_1uTo2(Input[3]); + Input += 4; + Output += 4; + inputLength -= 4; + } while (inputLength != 0); + } + + switch (InputLength & 3) + { + case 3: + *Output++ = C_1uTo2(*Input++); + case 2: + *Output++ = C_1uTo2(*Input++); + case 1: + *Output++ = C_1uTo2(*Input++); + } +} + +PPH_STRING PhZeroExtendToUtf16Ex( + _In_reads_bytes_(InputLength) PCH Input, + _In_ SIZE_T InputLength + ) +{ + PPH_STRING string; + + string = PhCreateStringEx(NULL, InputLength * sizeof(WCHAR)); + PhZeroExtendToUtf16Buffer(Input, InputLength, string->Buffer); + + return string; +} + +PPH_BYTES PhConvertUtf16ToAsciiEx( + _In_ PWCH Buffer, + _In_ SIZE_T Length, + _In_opt_ CHAR Replacement + ) +{ + PPH_BYTES bytes; + PH_UNICODE_DECODER decoder; + PWCH in; + SIZE_T inRemaining; + PCH out; + SIZE_T outLength; + ULONG codePoint; + + bytes = PhCreateBytesEx(NULL, Length / sizeof(WCHAR)); + PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF16); + in = Buffer; + inRemaining = Length / sizeof(WCHAR); + out = bytes->Buffer; + outLength = 0; + + while (inRemaining != 0) + { + PhWriteUnicodeDecoder(&decoder, (USHORT)*in); + in++; + inRemaining--; + + while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) + { + if (codePoint < 0x80) + { + *out++ = (CHAR)codePoint; + outLength++; + } + else if (Replacement) + { + *out++ = Replacement; + outLength++; + } + } + } + + bytes->Length = outLength; + bytes->Buffer[outLength] = ANSI_NULL; + + return bytes; +} + +/** + * Creates a string object from an existing null-terminated multi-byte string. + * + * \param Buffer A null-terminated multi-byte string. + */ +PPH_STRING PhConvertMultiByteToUtf16( + _In_ PSTR Buffer + ) +{ + return PhConvertMultiByteToUtf16Ex( + Buffer, + strlen(Buffer) + ); +} + +/** + * Creates a string object from an existing null-terminated multi-byte string. + * + * \param Buffer A null-terminated multi-byte string. + * \param Length The number of bytes to use. + */ +PPH_STRING PhConvertMultiByteToUtf16Ex( + _In_ PCHAR Buffer, + _In_ SIZE_T Length + ) +{ + NTSTATUS status; + PPH_STRING string; + ULONG unicodeBytes; + + status = RtlMultiByteToUnicodeSize( + &unicodeBytes, + Buffer, + (ULONG)Length + ); + + if (!NT_SUCCESS(status)) + return NULL; + + string = PhCreateStringEx(NULL, unicodeBytes); + status = RtlMultiByteToUnicodeN( + string->Buffer, + (ULONG)string->Length, + NULL, + Buffer, + (ULONG)Length + ); + + if (!NT_SUCCESS(status)) + { + PhDereferenceObject(string); + return NULL; + } + + return string; +} + +/** + * Creates a multi-byte string from an existing null-terminated UTF-16 string. + * + * \param Buffer A null-terminated UTF-16 string. + */ +PPH_BYTES PhConvertUtf16ToMultiByte( + _In_ PWSTR Buffer + ) +{ + return PhConvertUtf16ToMultiByteEx( + Buffer, + PhCountStringZ(Buffer) * sizeof(WCHAR) + ); +} + +/** + * Creates a multi-byte string from an existing null-terminated UTF-16 string. + * + * \param Buffer A null-terminated UTF-16 string. + * \param Length The number of bytes to use. + */ +PPH_BYTES PhConvertUtf16ToMultiByteEx( + _In_ PWCHAR Buffer, + _In_ SIZE_T Length + ) +{ + NTSTATUS status; + PPH_BYTES bytes; + ULONG multiByteLength; + + status = RtlUnicodeToMultiByteSize( + &multiByteLength, + Buffer, + (ULONG)Length + ); + + if (!NT_SUCCESS(status)) + return NULL; + + bytes = PhCreateBytesEx(NULL, multiByteLength); + status = RtlUnicodeToMultiByteN( + bytes->Buffer, + (ULONG)bytes->Length, + NULL, + Buffer, + (ULONG)Length + ); + + if (!NT_SUCCESS(status)) + { + PhDereferenceObject(bytes); + return NULL; + } + + return bytes; +} + +BOOLEAN PhConvertUtf8ToUtf16Size( + _Out_ PSIZE_T BytesInUtf16String, + _In_reads_bytes_(BytesInUtf8String) PCH Utf8String, + _In_ SIZE_T BytesInUtf8String + ) +{ + BOOLEAN result; + PH_UNICODE_DECODER decoder; + PCH in; + SIZE_T inRemaining; + SIZE_T bytesInUtf16String; + ULONG codePoint; + ULONG numberOfCodeUnits; + + result = TRUE; + PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF8); + in = Utf8String; + inRemaining = BytesInUtf8String; + bytesInUtf16String = 0; + + while (inRemaining != 0) + { + PhWriteUnicodeDecoder(&decoder, (UCHAR)*in); + in++; + inRemaining--; + + while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) + { + if (PhEncodeUnicode(PH_UNICODE_UTF16, codePoint, NULL, &numberOfCodeUnits)) + bytesInUtf16String += numberOfCodeUnits * sizeof(WCHAR); + else + result = FALSE; + } + } + + *BytesInUtf16String = bytesInUtf16String; + + return result; +} + +BOOLEAN PhConvertUtf8ToUtf16Buffer( + _Out_writes_bytes_to_(MaxBytesInUtf16String, *BytesInUtf16String) PWCH Utf16String, + _In_ SIZE_T MaxBytesInUtf16String, + _Out_opt_ PSIZE_T BytesInUtf16String, + _In_reads_bytes_(BytesInUtf8String) PCH Utf8String, + _In_ SIZE_T BytesInUtf8String + ) +{ + BOOLEAN result; + PH_UNICODE_DECODER decoder; + PCH in; + SIZE_T inRemaining; + PWCH out; + SIZE_T outRemaining; + SIZE_T bytesInUtf16String; + ULONG codePoint; + USHORT codeUnits[2]; + ULONG numberOfCodeUnits; + + result = TRUE; + PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF8); + in = Utf8String; + inRemaining = BytesInUtf8String; + out = Utf16String; + outRemaining = MaxBytesInUtf16String / sizeof(WCHAR); + bytesInUtf16String = 0; + + while (inRemaining != 0) + { + PhWriteUnicodeDecoder(&decoder, (UCHAR)*in); + in++; + inRemaining--; + + while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) + { + if (PhEncodeUnicode(PH_UNICODE_UTF16, codePoint, codeUnits, &numberOfCodeUnits)) + { + bytesInUtf16String += numberOfCodeUnits * sizeof(WCHAR); + + if (outRemaining >= numberOfCodeUnits) + { + *out++ = codeUnits[0]; + + if (numberOfCodeUnits >= 2) + *out++ = codeUnits[1]; + + outRemaining -= numberOfCodeUnits; + } + else + { + result = FALSE; + } + } + else + { + result = FALSE; + } + } + } + + if (BytesInUtf16String) + *BytesInUtf16String = bytesInUtf16String; + + return result; +} + +PPH_STRING PhConvertUtf8ToUtf16( + _In_ PSTR Buffer + ) +{ + return PhConvertUtf8ToUtf16Ex( + Buffer, + strlen(Buffer) + ); +} + +PPH_STRING PhConvertUtf8ToUtf16Ex( + _In_ PCHAR Buffer, + _In_ SIZE_T Length + ) +{ + PPH_STRING string; + SIZE_T utf16Bytes; + + if (!PhConvertUtf8ToUtf16Size( + &utf16Bytes, + Buffer, + Length + )) + { + return NULL; + } + + string = PhCreateStringEx(NULL, utf16Bytes); + + if (!PhConvertUtf8ToUtf16Buffer( + string->Buffer, + string->Length, + NULL, + Buffer, + Length + )) + { + PhDereferenceObject(string); + return NULL; + } + + return string; +} + +BOOLEAN PhConvertUtf16ToUtf8Size( + _Out_ PSIZE_T BytesInUtf8String, + _In_reads_bytes_(BytesInUtf16String) PWCH Utf16String, + _In_ SIZE_T BytesInUtf16String + ) +{ + BOOLEAN result; + PH_UNICODE_DECODER decoder; + PWCH in; + SIZE_T inRemaining; + SIZE_T bytesInUtf8String; + ULONG codePoint; + ULONG numberOfCodeUnits; + + result = TRUE; + PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF16); + in = Utf16String; + inRemaining = BytesInUtf16String / sizeof(WCHAR); + bytesInUtf8String = 0; + + while (inRemaining != 0) + { + PhWriteUnicodeDecoder(&decoder, (USHORT)*in); + in++; + inRemaining--; + + while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) + { + if (PhEncodeUnicode(PH_UNICODE_UTF8, codePoint, NULL, &numberOfCodeUnits)) + bytesInUtf8String += numberOfCodeUnits; + else + result = FALSE; + } + } + + *BytesInUtf8String = bytesInUtf8String; + + return result; +} + +BOOLEAN PhConvertUtf16ToUtf8Buffer( + _Out_writes_bytes_to_(MaxBytesInUtf8String, *BytesInUtf8String) PCH Utf8String, + _In_ SIZE_T MaxBytesInUtf8String, + _Out_opt_ PSIZE_T BytesInUtf8String, + _In_reads_bytes_(BytesInUtf16String) PWCH Utf16String, + _In_ SIZE_T BytesInUtf16String + ) +{ + BOOLEAN result; + PH_UNICODE_DECODER decoder; + PWCH in; + SIZE_T inRemaining; + PCH out; + SIZE_T outRemaining; + SIZE_T bytesInUtf8String; + ULONG codePoint; + UCHAR codeUnits[4]; + ULONG numberOfCodeUnits; + + result = TRUE; + PhInitializeUnicodeDecoder(&decoder, PH_UNICODE_UTF16); + in = Utf16String; + inRemaining = BytesInUtf16String / sizeof(WCHAR); + out = Utf8String; + outRemaining = MaxBytesInUtf8String; + bytesInUtf8String = 0; + + while (inRemaining != 0) + { + PhWriteUnicodeDecoder(&decoder, (USHORT)*in); + in++; + inRemaining--; + + while (PhDecodeUnicodeDecoder(&decoder, &codePoint)) + { + if (PhEncodeUnicode(PH_UNICODE_UTF8, codePoint, codeUnits, &numberOfCodeUnits)) + { + bytesInUtf8String += numberOfCodeUnits; + + if (outRemaining >= numberOfCodeUnits) + { + *out++ = codeUnits[0]; + + if (numberOfCodeUnits >= 2) + *out++ = codeUnits[1]; + if (numberOfCodeUnits >= 3) + *out++ = codeUnits[2]; + if (numberOfCodeUnits >= 4) + *out++ = codeUnits[3]; + + outRemaining -= numberOfCodeUnits; + } + else + { + result = FALSE; + } + } + else + { + result = FALSE; + } + } + } + + if (BytesInUtf8String) + *BytesInUtf8String = bytesInUtf8String; + + return result; +} + +PPH_BYTES PhConvertUtf16ToUtf8( + _In_ PWSTR Buffer + ) +{ + return PhConvertUtf16ToUtf8Ex( + Buffer, + PhCountStringZ(Buffer) * sizeof(WCHAR) + ); +} + +PPH_BYTES PhConvertUtf16ToUtf8Ex( + _In_ PWCHAR Buffer, + _In_ SIZE_T Length + ) +{ + PPH_BYTES bytes; + SIZE_T utf8Bytes; + + if (!PhConvertUtf16ToUtf8Size( + &utf8Bytes, + Buffer, + Length + )) + { + return NULL; + } + + bytes = PhCreateBytesEx(NULL, utf8Bytes); + + if (!PhConvertUtf16ToUtf8Buffer( + bytes->Buffer, + bytes->Length, + NULL, + Buffer, + Length + )) + { + PhDereferenceObject(bytes); + return NULL; + } + + return bytes; +} + +/** + * Initializes a string builder object. + * + * \param StringBuilder A string builder object. + * \param InitialCapacity The number of bytes to allocate initially. + */ +VOID PhInitializeStringBuilder( + _Out_ PPH_STRING_BUILDER StringBuilder, + _In_ SIZE_T InitialCapacity + ) +{ + // Make sure the initial capacity is even, as required for all string objects. + if (InitialCapacity & 1) + InitialCapacity++; + + StringBuilder->AllocatedLength = InitialCapacity; + + // Allocate a PH_STRING for the string builder. + // We will dereference it and allocate a new one when we need to resize the string. + + StringBuilder->String = PhCreateStringEx(NULL, StringBuilder->AllocatedLength); + + // We will keep modifying the Length field of the string so that: + // 1. We know how much of the string is used, and + // 2. The user can simply get a reference to the string and use it as-is. + + StringBuilder->String->Length = 0; + + // Write the null terminator. + StringBuilder->String->Buffer[0] = UNICODE_NULL; + + PHLIB_INC_STATISTIC(BaseStringBuildersCreated); +} + +/** + * Frees resources used by a string builder object. + * + * \param StringBuilder A string builder object. + */ +VOID PhDeleteStringBuilder( + _Inout_ PPH_STRING_BUILDER StringBuilder + ) +{ + PhDereferenceObject(StringBuilder->String); +} + +/** + * Obtains a reference to the string constructed by a string builder object and frees resources used + * by the object. + * + * \param StringBuilder A string builder object. + * + * \return A pointer to a string. You must free the string using PhDereferenceObject() when you no + * longer need it. + */ +PPH_STRING PhFinalStringBuilderString( + _Inout_ PPH_STRING_BUILDER StringBuilder + ) +{ + return StringBuilder->String; +} + +VOID PhpResizeStringBuilder( + _In_ PPH_STRING_BUILDER StringBuilder, + _In_ SIZE_T NewCapacity + ) +{ + PPH_STRING newString; + + // Double the string size. If that still isn't enough room, just use the new length. + + StringBuilder->AllocatedLength *= 2; + + if (StringBuilder->AllocatedLength < NewCapacity) + StringBuilder->AllocatedLength = NewCapacity; + + // Allocate a new string. + newString = PhCreateStringEx(NULL, StringBuilder->AllocatedLength); + + // Copy the old string to the new string. + memcpy( + newString->Buffer, + StringBuilder->String->Buffer, + StringBuilder->String->Length + sizeof(UNICODE_NULL) // Include null terminator + ); + + // Copy the old string length. + newString->Length = StringBuilder->String->Length; + + // Dereference the old string and replace it with the new string. + PhMoveReference(&StringBuilder->String, newString); + + PHLIB_INC_STATISTIC(BaseStringBuildersResized); +} + +FORCEINLINE VOID PhpWriteNullTerminatorStringBuilder( + _In_ PPH_STRING_BUILDER StringBuilder + ) +{ + assert(!(StringBuilder->String->Length & 1)); + *(PWCHAR)PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length) = UNICODE_NULL; +} + +/** + * Appends a string to the end of a string builder string. + * + * \param StringBuilder A string builder object. + * \param String The string to append. + */ +VOID PhAppendStringBuilder( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ PPH_STRINGREF String + ) +{ + PhAppendStringBuilderEx( + StringBuilder, + String->Buffer, + String->Length + ); +} + +/** + * Appends a string to the end of a string builder string. + * + * \param StringBuilder A string builder object. + * \param String The string to append. + */ +VOID PhAppendStringBuilder2( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ PWSTR String + ) +{ + PhAppendStringBuilderEx( + StringBuilder, + String, + PhCountStringZ(String) * sizeof(WCHAR) + ); +} + +/** + * Appends a string to the end of a string builder string. + * + * \param StringBuilder A string builder object. + * \param String The string to append. Specify NULL to simply reserve \a Length bytes. + * \param Length The number of bytes to append. + */ +VOID PhAppendStringBuilderEx( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_opt_ PWCHAR String, + _In_ SIZE_T Length + ) +{ + if (Length == 0) + return; + + // See if we need to re-allocate the string. + if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Length) + { + PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Length); + } + + // Copy the string, add the length, then write the null terminator. + + if (String) + { + memcpy( + PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length), + String, + Length + ); + } + + StringBuilder->String->Length += Length; + PhpWriteNullTerminatorStringBuilder(StringBuilder); +} + +/** + * Appends a character to the end of a string builder string. + * + * \param StringBuilder A string builder object. + * \param Character The character to append. + */ +VOID PhAppendCharStringBuilder( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ WCHAR Character + ) +{ + if (StringBuilder->AllocatedLength < StringBuilder->String->Length + sizeof(WCHAR)) + { + PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + sizeof(WCHAR)); + } + + *(PWCHAR)PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length) = Character; + StringBuilder->String->Length += sizeof(WCHAR); + PhpWriteNullTerminatorStringBuilder(StringBuilder); +} + +/** + * Appends a number of characters to the end of a string builder string. + * + * \param StringBuilder A string builder object. + * \param Character The character to append. + * \param Count The number of times to append the character. + */ +VOID PhAppendCharStringBuilder2( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ WCHAR Character, + _In_ SIZE_T Count + ) +{ + if (Count == 0) + return; + + // See if we need to re-allocate the string. + if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Count * sizeof(WCHAR)) + { + PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Count * sizeof(WCHAR)); + } + + wmemset( + PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length), + Character, + Count + ); + + StringBuilder->String->Length += Count * sizeof(WCHAR); + PhpWriteNullTerminatorStringBuilder(StringBuilder); +} + +/** + * Appends a formatted string to the end of a string builder string. + * + * \param StringBuilder A string builder object. + * \param Format The format-control string. + */ +VOID PhAppendFormatStringBuilder( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ _Printf_format_string_ PWSTR Format, + ... + ) +{ + va_list argptr; + + va_start(argptr, Format); + PhAppendFormatStringBuilder_V(StringBuilder, Format, argptr); + va_end(argptr); +} + +VOID PhAppendFormatStringBuilder_V( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ _Printf_format_string_ PWSTR Format, + _In_ va_list ArgPtr + ) +{ + int length; + SIZE_T lengthInBytes; + + length = _vscwprintf(Format, ArgPtr); + + if (length == -1 || length == 0) + return; + + lengthInBytes = length * sizeof(WCHAR); + + if (StringBuilder->AllocatedLength < StringBuilder->String->Length + lengthInBytes) + PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + lengthInBytes); + + _vsnwprintf( + PTR_ADD_OFFSET(StringBuilder->String->Buffer, StringBuilder->String->Length), + length, + Format, + ArgPtr + ); + + StringBuilder->String->Length += lengthInBytes; + PhpWriteNullTerminatorStringBuilder(StringBuilder); +} + +/** + * Inserts a string into a string builder string. + * + * \param StringBuilder A string builder object. + * \param Index The index, in characters, at which to insert the string. + * \param String The string to insert. + */ +VOID PhInsertStringBuilder( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ SIZE_T Index, + _In_ PPH_STRINGREF String + ) +{ + PhInsertStringBuilderEx( + StringBuilder, + Index, + String->Buffer, + String->Length + ); +} + +/** + * Inserts a string into a string builder string. + * + * \param StringBuilder A string builder object. + * \param Index The index, in characters, at which to insert the string. + * \param String The string to insert. + */ +VOID PhInsertStringBuilder2( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ SIZE_T Index, + _In_ PWSTR String + ) +{ + PhInsertStringBuilderEx( + StringBuilder, + Index, + String, + PhCountStringZ(String) * sizeof(WCHAR) + ); +} + +/** + * Inserts a string into a string builder string. + * + * \param StringBuilder A string builder object. + * \param Index The index, in characters, at which to insert the string. + * \param String The string to insert. Specify NULL to simply reserve \a Length bytes at \a Index. + * \param Length The number of bytes to insert. + */ +VOID PhInsertStringBuilderEx( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ SIZE_T Index, + _In_opt_ PWCHAR String, + _In_ SIZE_T Length + ) +{ + if (Length == 0) + return; + + // See if we need to re-allocate the string. + if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Length) + { + PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Length); + } + + if (Index * sizeof(WCHAR) < StringBuilder->String->Length) + { + // Create some space for the string. + memmove( + &StringBuilder->String->Buffer[Index + Length / sizeof(WCHAR)], + &StringBuilder->String->Buffer[Index], + StringBuilder->String->Length - Index * sizeof(WCHAR) + ); + } + + if (String) + { + // Copy the new string. + memcpy( + &StringBuilder->String->Buffer[Index], + String, + Length + ); + } + + StringBuilder->String->Length += Length; + PhpWriteNullTerminatorStringBuilder(StringBuilder); +} + +/** + * Removes characters from a string builder string. + * + * \param StringBuilder A string builder object. + * \param StartIndex The index, in characters, at which to begin removing characters. + * \param Count The number of characters to remove. + */ +VOID PhRemoveStringBuilder( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ SIZE_T StartIndex, + _In_ SIZE_T Count + ) +{ + // Overwrite the removed part with the part + // behind it. + + memmove( + &StringBuilder->String->Buffer[StartIndex], + &StringBuilder->String->Buffer[StartIndex + Count], + StringBuilder->String->Length - (Count + StartIndex) * sizeof(WCHAR) + ); + StringBuilder->String->Length -= Count * sizeof(WCHAR); + PhpWriteNullTerminatorStringBuilder(StringBuilder); +} + +/** + * Initializes a byte string builder object. + * + * \param BytesBuilder A byte string builder object. + * \param InitialCapacity The number of bytes to allocate initially. + */ +VOID PhInitializeBytesBuilder( + _Out_ PPH_BYTES_BUILDER BytesBuilder, + _In_ SIZE_T InitialCapacity + ) +{ + BytesBuilder->AllocatedLength = InitialCapacity; + BytesBuilder->Bytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength); + BytesBuilder->Bytes->Length = 0; + BytesBuilder->Bytes->Buffer[0] = ANSI_NULL; +} + +/** + * Frees resources used by a byte string builder object. + * + * \param BytesBuilder A byte string builder object. + */ +VOID PhDeleteBytesBuilder( + _Inout_ PPH_BYTES_BUILDER BytesBuilder + ) +{ + PhDereferenceObject(BytesBuilder->Bytes); +} + +/** + * Obtains a reference to the byte string constructed by a byte string builder object and frees + * resources used by the object. + * + * \param BytesBuilder A byte string builder object. + * + * \return A pointer to a byte string. You must free the byte string using PhDereferenceObject() + * when you no longer need it. + */ +PPH_BYTES PhFinalBytesBuilderBytes( + _Inout_ PPH_BYTES_BUILDER BytesBuilder + ) +{ + return BytesBuilder->Bytes; +} + +VOID PhpResizeBytesBuilder( + _In_ PPH_BYTES_BUILDER BytesBuilder, + _In_ SIZE_T NewCapacity + ) +{ + PPH_BYTES newBytes; + + // Double the byte string size. If that still isn't enough room, just use the new length. + + BytesBuilder->AllocatedLength *= 2; + + if (BytesBuilder->AllocatedLength < NewCapacity) + BytesBuilder->AllocatedLength = NewCapacity; + + // Allocate a new byte string. + newBytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength); + + // Copy the old byte string to the new byte string. + memcpy( + newBytes->Buffer, + BytesBuilder->Bytes->Buffer, + BytesBuilder->Bytes->Length + sizeof(ANSI_NULL) // Include null terminator + ); + + // Copy the old byte string length. + newBytes->Length = BytesBuilder->Bytes->Length; + + // Dereference the old byte string and replace it with the new byte string. + PhMoveReference(&BytesBuilder->Bytes, newBytes); +} + +FORCEINLINE VOID PhpWriteNullTerminatorBytesBuilder( + _In_ PPH_BYTES_BUILDER BytesBuilder + ) +{ + BytesBuilder->Bytes->Buffer[BytesBuilder->Bytes->Length] = ANSI_NULL; +} + +/** + * Appends a byte string to the end of a byte string builder string. + * + * \param BytesBuilder A byte string builder object. + * \param Bytes The byte string to append. + */ +VOID PhAppendBytesBuilder( + _Inout_ PPH_BYTES_BUILDER BytesBuilder, + _In_ PPH_BYTESREF Bytes + ) +{ + PhAppendBytesBuilderEx( + BytesBuilder, + Bytes->Buffer, + Bytes->Length, + 0, + NULL + ); +} + +/** + * Appends a byte string to the end of a byte string builder string. + * + * \param BytesBuilder A byte string builder object. + * \param Bytes The byte string to append. + */ +VOID PhAppendBytesBuilder2( + _Inout_ PPH_BYTES_BUILDER BytesBuilder, + _In_ PCHAR Bytes + ) +{ + PhAppendBytesBuilderEx( + BytesBuilder, + Bytes, + strlen(Bytes), + 0, + NULL + ); +} + +/** + * Appends a byte string to the end of a byte string builder string. + * + * \param BytesBuilder A byte string builder object. + * \param Buffer The byte string to append. Specify NULL to simply reserve \a Length bytes. + * \param Length The number of bytes to append. + * \param Alignment The required alignment. This should not be greater than 8. + * \param Offset A variable which receives the byte offset of the appended or reserved bytes in the + * byte string builder string. + * + * \return A pointer to the appended or reserved bytes. + */ +PVOID PhAppendBytesBuilderEx( + _Inout_ PPH_BYTES_BUILDER BytesBuilder, + _In_opt_ PVOID Buffer, + _In_ SIZE_T Length, + _In_opt_ SIZE_T Alignment, + _Out_opt_ PSIZE_T Offset + ) +{ + SIZE_T currentLength; + + currentLength = BytesBuilder->Bytes->Length; + + if (Length == 0) + goto Done; + + if (Alignment) + currentLength = ALIGN_UP_BY(currentLength, Alignment); + + // See if we need to re-allocate the byte string. + if (BytesBuilder->AllocatedLength < currentLength + Length) + PhpResizeBytesBuilder(BytesBuilder, currentLength + Length); + + // Copy the byte string, add the length, then write the null terminator. + + if (Buffer) + memcpy(BytesBuilder->Bytes->Buffer + currentLength, Buffer, Length); + + BytesBuilder->Bytes->Length = currentLength + Length; + PhpWriteNullTerminatorBytesBuilder(BytesBuilder); + +Done: + if (Offset) + *Offset = currentLength; + + return BytesBuilder->Bytes->Buffer + currentLength; +} + +/** + * Creates an array object. + * + * \param Array An array object. + * \param ItemSize The size of each item, in bytes. + * \param InitialCapacity The number of elements to allocate storage for, initially. + */ +VOID PhInitializeArray( + _Out_ PPH_ARRAY Array, + _In_ SIZE_T ItemSize, + _In_ SIZE_T InitialCapacity + ) +{ + // Initial capacity of 0 is not allowed. + if (InitialCapacity == 0) + InitialCapacity = 1; + + Array->Count = 0; + Array->AllocatedCount = InitialCapacity; + Array->ItemSize = ItemSize; + Array->Items = PhAllocate(Array->AllocatedCount * ItemSize); +} + +/** + * Frees resources used by an array object. + * + * \param Array An array object. + */ +VOID PhDeleteArray( + _Inout_ PPH_ARRAY Array + ) +{ + PhFree(Array->Items); +} + +/** + * Obtains a copy of the array constructed by an array object and frees resources used by the + * object. + * + * \param Array An array object. + * + * \return The array buffer. + */ +PVOID PhFinalArrayItems( + _Inout_ PPH_ARRAY Array + ) +{ + return Array->Items; +} + +/** + * Resizes an array. + * + * \param Array An array object. + * \param NewCapacity The new required number of elements for which storage has been reserved. This + * must not be smaller than the current number of items in the array. + */ +VOID PhResizeArray( + _Inout_ PPH_ARRAY Array, + _In_ SIZE_T NewCapacity + ) +{ + if (Array->Count > NewCapacity) + PhRaiseStatus(STATUS_INVALID_PARAMETER_2); + + Array->AllocatedCount = NewCapacity; + Array->Items = PhReAllocate(Array->Items, Array->AllocatedCount * Array->ItemSize); +} + +/** + * Adds an item to an array. + * + * \param Array An array object. + * \param Item The item to add. + */ +VOID PhAddItemArray( + _Inout_ PPH_ARRAY Array, + _In_ PVOID Item + ) +{ + // See if we need to resize the list. + if (Array->Count == Array->AllocatedCount) + { + Array->AllocatedCount *= 2; + Array->Items = PhReAllocate(Array->Items, Array->AllocatedCount * Array->ItemSize); + } + + memcpy(PhItemArray(Array, Array->Count), Item, Array->ItemSize); + Array->Count++; +} + +/** + * Adds items to an array. + * + * \param Array An array object. + * \param Items An array containing the items to add. + * \param Count The number of items to add. + */ +VOID PhAddItemsArray( + _Inout_ PPH_ARRAY Array, + _In_ PVOID Items, + _In_ SIZE_T Count + ) +{ + // See if we need to resize the list. + if (Array->AllocatedCount < Array->Count + Count) + { + Array->AllocatedCount *= 2; + + if (Array->AllocatedCount < Array->Count + Count) + Array->AllocatedCount = Array->Count + Count; + + Array->Items = PhReAllocate(Array->Items, Array->AllocatedCount * Array->ItemSize); + } + + memcpy( + PhItemArray(Array, Array->Count), + Items, + Count * Array->ItemSize + ); + Array->Count += Count; +} + +/** + * Clears an array. + * + * \param Array An array object. + */ +VOID PhClearArray( + _Inout_ PPH_ARRAY Array + ) +{ + Array->Count = 0; +} + +/** + * Removes an item from an array. + * + * \param Array An array object. + * \param Index The index of the item. + */ +VOID PhRemoveItemArray( + _Inout_ PPH_ARRAY Array, + _In_ SIZE_T Index + ) +{ + PhRemoveItemsArray(Array, Index, 1); +} + +/** + * Removes items from an array. + * + * \param Array An array object. + * \param StartIndex The index at which to begin removing items. + * \param Count The number of items to remove. + */ +VOID PhRemoveItemsArray( + _Inout_ PPH_ARRAY Array, + _In_ SIZE_T StartIndex, + _In_ SIZE_T Count + ) +{ + // Shift the items after the items forward. + memmove( + PhItemArray(Array, StartIndex), + PhItemArray(Array, StartIndex + Count), + (Array->Count - StartIndex - Count) * Array->ItemSize + ); + Array->Count -= Count; +} + +/** + * Creates a list object. + * + * \param InitialCapacity The number of elements to allocate storage for, initially. + */ +PPH_LIST PhCreateList( + _In_ ULONG InitialCapacity + ) +{ + PPH_LIST list; + + list = PhCreateObject(sizeof(PH_LIST), PhListType); + + // Initial capacity of 0 is not allowed. + if (InitialCapacity == 0) + InitialCapacity = 1; + + list->Count = 0; + list->AllocatedCount = InitialCapacity; + list->Items = PhAllocate(list->AllocatedCount * sizeof(PVOID)); + + return list; +} + +VOID PhpListDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_LIST list = (PPH_LIST)Object; + + PhFree(list->Items); +} + +/** + * Resizes a list. + * + * \param List A list object. + * \param NewCapacity The new required number of elements for which storage has been reserved. This + * must not be smaller than the current number of items in the list. + */ +VOID PhResizeList( + _Inout_ PPH_LIST List, + _In_ ULONG NewCapacity + ) +{ + if (List->Count > NewCapacity) + PhRaiseStatus(STATUS_INVALID_PARAMETER_2); + + List->AllocatedCount = NewCapacity; + List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); +} + +/** + * Adds an item to a list. + * + * \param List A list object. + * \param Item The item to add. + */ +VOID PhAddItemList( + _Inout_ PPH_LIST List, + _In_ PVOID Item + ) +{ + // See if we need to resize the list. + if (List->Count == List->AllocatedCount) + { + List->AllocatedCount *= 2; + List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); + } + + List->Items[List->Count++] = Item; +} + +/** + * Adds items to a list. + * + * \param List A list object. + * \param Items An array containing the items to add. + * \param Count The number of items to add. + */ +VOID PhAddItemsList( + _Inout_ PPH_LIST List, + _In_ PVOID *Items, + _In_ ULONG Count + ) +{ + // See if we need to resize the list. + if (List->AllocatedCount < List->Count + Count) + { + List->AllocatedCount *= 2; + + if (List->AllocatedCount < List->Count + Count) + List->AllocatedCount = List->Count + Count; + + List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); + } + + memcpy( + &List->Items[List->Count], + Items, + Count * sizeof(PVOID) + ); + List->Count += Count; +} + +/** + * Clears a list. + * + * \param List A list object. + */ +VOID PhClearList( + _Inout_ PPH_LIST List + ) +{ + List->Count = 0; +} + +_Success_(return != -1) +/** + * Locates an item in a list. + * + * \param List A list object. + * \param Item The item to search for. + * + * \return The index of the item. If the + * item was not found, -1 is returned. + */ +ULONG PhFindItemList( + _In_ PPH_LIST List, + _In_ PVOID Item + ) +{ + ULONG i; + + for (i = 0; i < List->Count; i++) + { + if (List->Items[i] == Item) + return i; + } + + return ULONG_MAX; +} + +/** + * Inserts an item into a list. + * + * \param List A list object. + * \param Index The index at which to insert the item. + * \param Item The item to add. + */ +VOID PhInsertItemList( + _Inout_ PPH_LIST List, + _In_ ULONG Index, + _In_ PVOID Item + ) +{ + PhInsertItemsList(List, Index, &Item, 1); +} + +/** + * Inserts items into a list. + * + * \param List A list object. + * \param Index The index at which to insert the items. + * \param Items An array containing the items to add. + * \param Count The number of items to add. + */ +VOID PhInsertItemsList( + _Inout_ PPH_LIST List, + _In_ ULONG Index, + _In_ PVOID *Items, + _In_ ULONG Count + ) +{ + // See if we need to resize the list. + if (List->AllocatedCount < List->Count + Count) + { + List->AllocatedCount *= 2; + + if (List->AllocatedCount < List->Count + Count) + List->AllocatedCount = List->Count + Count; + + List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID)); + } + + if (Index < List->Count) + { + // Shift the existing items backward. + memmove( + &List->Items[Index + Count], + &List->Items[Index], + (List->Count - Index) * sizeof(PVOID) + ); + } + + // Copy the new items into the list. + memcpy( + &List->Items[Index], + Items, + Count * sizeof(PVOID) + ); + + List->Count += Count; +} + +/** + * Removes an item from a list. + * + * \param List A list object. + * \param Index The index of the item. + */ +VOID PhRemoveItemList( + _Inout_ PPH_LIST List, + _In_ ULONG Index + ) +{ + PhRemoveItemsList(List, Index, 1); +} + +/** + * Removes items from a list. + * + * \param List A list object. + * \param StartIndex The index at which to begin removing items. + * \param Count The number of items to remove. + */ +VOID PhRemoveItemsList( + _Inout_ PPH_LIST List, + _In_ ULONG StartIndex, + _In_ ULONG Count + ) +{ + // Shift the items after the items forward. + memmove( + &List->Items[StartIndex], + &List->Items[StartIndex + Count], + (List->Count - StartIndex - Count) * sizeof(PVOID) + ); + List->Count -= Count; +} + +/** + * Creates a pointer list object. + * + * \param InitialCapacity The number of elements to allocate storage for initially. + */ +PPH_POINTER_LIST PhCreatePointerList( + _In_ ULONG InitialCapacity + ) +{ + PPH_POINTER_LIST pointerList; + + pointerList = PhCreateObject(sizeof(PH_POINTER_LIST), PhPointerListType); + + // Initial capacity of 0 is not allowed. + if (InitialCapacity == 0) + InitialCapacity = 1; + + pointerList->Count = 0; + pointerList->AllocatedCount = InitialCapacity; + pointerList->FreeEntry = -1; + pointerList->NextEntry = 0; + pointerList->Items = PhAllocate(pointerList->AllocatedCount * sizeof(PVOID)); + + return pointerList; +} + +VOID NTAPI PhpPointerListDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_POINTER_LIST pointerList = (PPH_POINTER_LIST)Object; + + PhFree(pointerList->Items); +} + +/** + * Decodes an index stored in a free entry. + */ +FORCEINLINE ULONG PhpDecodePointerListIndex( + _In_ PVOID Index + ) +{ + // At least with Microsoft's compiler, shift right on a signed value preserves the sign. This is + // important because we want decode(encode(-1)) = ((-1 << 1) | 1) >> 1 = -1. + return (ULONG)((LONG_PTR)Index >> 1); +} + +/** + * Encodes an index for storage in a free entry. + */ +FORCEINLINE PVOID PhpEncodePointerListIndex( + _In_ ULONG Index + ) +{ + return (PVOID)(((ULONG_PTR)Index << 1) | 0x1); +} + +FORCEINLINE HANDLE PhpPointerListIndexToHandle( + _In_ ULONG Index + ) +{ + // Add one to allow NULL handles to indicate failure/an invalid index. + return UlongToHandle(Index + 1); +} + +FORCEINLINE ULONG PhpPointerListHandleToIndex( + _In_ HANDLE Handle + ) +{ + return HandleToUlong(Handle) - 1; +} + +/** + * Adds a pointer to a pointer list. + * + * \param PointerList A pointer list object. + * \param Pointer The pointer to add. The pointer must be at least 2 byte aligned. + * + * \return A handle to the pointer, valid until the pointer is removed from the pointer list. + */ +HANDLE PhAddItemPointerList( + _Inout_ PPH_POINTER_LIST PointerList, + _In_ PVOID Pointer + ) +{ + ULONG index; + + assert(PH_IS_LIST_POINTER_VALID(Pointer)); + + // Use a free entry if possible. + if (PointerList->FreeEntry != -1) + { + PVOID oldPointer; + + index = PointerList->FreeEntry; + oldPointer = PointerList->Items[index]; + PointerList->Items[index] = Pointer; + PointerList->FreeEntry = PhpDecodePointerListIndex(oldPointer); + } + else + { + // Use the next entry. + if (PointerList->NextEntry == PointerList->AllocatedCount) + { + PointerList->AllocatedCount *= 2; + PointerList->Items = PhReAllocate(PointerList->Items, PointerList->AllocatedCount * sizeof(PVOID)); + } + + index = PointerList->NextEntry++; + PointerList->Items[index] = Pointer; + } + + PointerList->Count++; + + return PhpPointerListIndexToHandle(index); +} + +BOOLEAN PhEnumPointerListEx( + _In_ PPH_POINTER_LIST PointerList, + _Inout_ PULONG EnumerationKey, + _Out_ PVOID *Pointer, + _Out_ PHANDLE PointerHandle + ) +{ + ULONG index; + + while ((index = *EnumerationKey) < PointerList->NextEntry) + { + PVOID pointer = PointerList->Items[index]; + + (*EnumerationKey)++; + + if (PH_IS_LIST_POINTER_VALID(pointer)) + { + *Pointer = pointer; + *PointerHandle = PhpPointerListIndexToHandle(index); + + return TRUE; + } + } + + return FALSE; +} + +/** + * Locates a pointer in a pointer list. + * + * \param PointerList A pointer list object. + * \param Pointer The pointer to find. The pointer must be at least 2 byte aligned. + * + * \return A handle to the pointer, valid until the pointer is removed from the pointer list. If the + * pointer is not contained in the pointer list, NULL is returned. + */ +HANDLE PhFindItemPointerList( + _In_ PPH_POINTER_LIST PointerList, + _In_ PVOID Pointer + ) +{ + ULONG i; + + assert(PH_IS_LIST_POINTER_VALID(Pointer)); + + for (i = 0; i < PointerList->NextEntry; i++) + { + if (PointerList->Items[i] == Pointer) + return PhpPointerListIndexToHandle(i); + } + + return NULL; +} + +/** + * Removes a pointer from a pointer list. + * + * \param PointerList A pointer list object. + * \param PointerHandle A handle to the pointer to remove. + * + * \remarks No checking is performed on the pointer handle. Make sure the handle is valid before + * calling the function. + */ +VOID PhRemoveItemPointerList( + _Inout_ PPH_POINTER_LIST PointerList, + _In_ HANDLE PointerHandle + ) +{ + ULONG index; + + assert(PointerHandle); + + index = PhpPointerListHandleToIndex(PointerHandle); + + PointerList->Items[index] = PhpEncodePointerListIndex(PointerList->FreeEntry); + PointerList->FreeEntry = index; + + PointerList->Count--; +} + +FORCEINLINE ULONG PhpValidateHash( + _In_ ULONG Hash + ) +{ + // No point in using a full hash when we're going to AND with size minus one anyway. +#if defined(PH_HASHTABLE_FULL_HASH) && !defined(PH_HASHTABLE_POWER_OF_TWO_SIZE) + if (Hash != -1) + return Hash; + else + return 0; +#else + return Hash & MAXLONG; +#endif +} + +FORCEINLINE ULONG PhpIndexFromHash( + _In_ PPH_HASHTABLE Hashtable, + _In_ ULONG Hash + ) +{ +#ifdef PH_HASHTABLE_POWER_OF_TWO_SIZE + return Hash & (Hashtable->AllocatedBuckets - 1); +#else + return Hash % Hashtable->AllocatedBuckets; +#endif +} + +FORCEINLINE ULONG PhpGetNumberOfBuckets( + _In_ ULONG Capacity + ) +{ +#ifdef PH_HASHTABLE_POWER_OF_TWO_SIZE + return PhRoundUpToPowerOfTwo(Capacity); +#else + return PhGetPrimeNumber(Capacity); +#endif +} + +/** + * Creates a hashtable object. + * + * \param EntrySize The size of each hashtable entry, in bytes. + * \param EqualFunction A comparison function that is executed to compare two hashtable entries. + * \param HashFunction A hash function that is executed to generate a hash code for a hashtable + * entry. + * \param InitialCapacity The number of entries to allocate storage for initially. + */ +PPH_HASHTABLE PhCreateHashtable( + _In_ ULONG EntrySize, + _In_ PPH_HASHTABLE_EQUAL_FUNCTION EqualFunction, + _In_ PPH_HASHTABLE_HASH_FUNCTION HashFunction, + _In_ ULONG InitialCapacity + ) +{ + PPH_HASHTABLE hashtable; + + hashtable = PhCreateObject(sizeof(PH_HASHTABLE), PhHashtableType); + + // Initial capacity of 0 is not allowed. + if (InitialCapacity == 0) + InitialCapacity = 1; + + hashtable->EntrySize = EntrySize; + hashtable->EqualFunction = EqualFunction; + hashtable->HashFunction = HashFunction; + + // Allocate the buckets. + hashtable->AllocatedBuckets = PhpGetNumberOfBuckets(InitialCapacity); + hashtable->Buckets = PhAllocate(sizeof(ULONG) * hashtable->AllocatedBuckets); + // Set all bucket values to -1. + memset(hashtable->Buckets, 0xff, sizeof(ULONG) * hashtable->AllocatedBuckets); + + // Allocate the entries. + hashtable->AllocatedEntries = hashtable->AllocatedBuckets; + hashtable->Entries = PhAllocate(PH_HASHTABLE_ENTRY_SIZE(EntrySize) * hashtable->AllocatedEntries); + + hashtable->Count = 0; + hashtable->FreeEntry = -1; + hashtable->NextEntry = 0; + + return hashtable; +} + +VOID PhpHashtableDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_HASHTABLE hashtable = (PPH_HASHTABLE)Object; + + PhFree(hashtable->Buckets); + PhFree(hashtable->Entries); +} + +VOID PhpResizeHashtable( + _Inout_ PPH_HASHTABLE Hashtable, + _In_ ULONG NewCapacity + ) +{ + PPH_HASHTABLE_ENTRY entry; + ULONG i; + + // Re-allocate the buckets. Note that we don't need to keep the contents. + Hashtable->AllocatedBuckets = PhpGetNumberOfBuckets(NewCapacity); + PhFree(Hashtable->Buckets); + Hashtable->Buckets = PhAllocate(sizeof(ULONG) * Hashtable->AllocatedBuckets); + // Set all bucket values to -1. + memset(Hashtable->Buckets, 0xff, sizeof(ULONG) * Hashtable->AllocatedBuckets); + + // Re-allocate the entries. + Hashtable->AllocatedEntries = Hashtable->AllocatedBuckets; + Hashtable->Entries = PhReAllocate( + Hashtable->Entries, + PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize) * Hashtable->AllocatedEntries + ); + + // Re-distribute the entries among the buckets. + + // PH_HASHTABLE_GET_ENTRY is quite slow (it involves a multiply), so we use a pointer here. + entry = Hashtable->Entries; + + for (i = 0; i < Hashtable->NextEntry; i++) + { + if (entry->HashCode != ULONG_MAX) + { + ULONG index = PhpIndexFromHash(Hashtable, entry->HashCode); + + entry->Next = Hashtable->Buckets[index]; + Hashtable->Buckets[index] = i; + } + + entry = PTR_ADD_OFFSET(entry, PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize)); + } +} + +FORCEINLINE PVOID PhpAddEntryHashtable( + _Inout_ PPH_HASHTABLE Hashtable, + _In_ PVOID Entry, + _In_ BOOLEAN CheckForDuplicate, + _Out_opt_ PBOOLEAN Added + ) +{ + ULONG hashCode; // hash code of the new entry + ULONG index; // bucket index of the new entry + ULONG freeEntry; // index of new entry in entry array + PPH_HASHTABLE_ENTRY entry; // pointer to new entry in entry array + + hashCode = PhpValidateHash(Hashtable->HashFunction(Entry)); + index = PhpIndexFromHash(Hashtable, hashCode); + + if (CheckForDuplicate) + { + ULONG i; + + for (i = Hashtable->Buckets[index]; i != ULONG_MAX; i = entry->Next) + { + entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i); + + if (entry->HashCode == hashCode && Hashtable->EqualFunction(&entry->Body, Entry)) + { + if (Added) + *Added = FALSE; + + return &entry->Body; + } + } + } + + // Use a free entry if possible. + if (Hashtable->FreeEntry != ULONG_MAX) + { + freeEntry = Hashtable->FreeEntry; + entry = PH_HASHTABLE_GET_ENTRY(Hashtable, freeEntry); + Hashtable->FreeEntry = entry->Next; + } + else + { + // Use the next entry in the entry array. + + if (Hashtable->NextEntry == Hashtable->AllocatedEntries) + { + // Resize the hashtable. + PhpResizeHashtable(Hashtable, Hashtable->AllocatedBuckets * 2); + index = PhpIndexFromHash(Hashtable, hashCode); + } + + freeEntry = Hashtable->NextEntry++; + entry = PH_HASHTABLE_GET_ENTRY(Hashtable, freeEntry); + } + + // Initialize the entry. + entry->HashCode = hashCode; + entry->Next = Hashtable->Buckets[index]; + Hashtable->Buckets[index] = freeEntry; + // Copy the user-supplied data to the entry. + memcpy(&entry->Body, Entry, Hashtable->EntrySize); + + Hashtable->Count++; + + if (Added) + *Added = TRUE; + + return &entry->Body; +} + +/** + * Adds an entry to a hashtable. + * + * \param Hashtable A hashtable object. + * \param Entry The entry to add. + * + * \return A pointer to the entry as stored in the hashtable. This pointer is valid until the + * hashtable is modified. If the hashtable already contained an equal entry, NULL is returned. + * + * \remarks Entries are only guaranteed to be 8 byte aligned, even on 64-bit systems. + */ +PVOID PhAddEntryHashtable( + _Inout_ PPH_HASHTABLE Hashtable, + _In_ PVOID Entry + ) +{ + PVOID entry; + BOOLEAN added; + + entry = PhpAddEntryHashtable(Hashtable, Entry, TRUE, &added); + + if (added) + return entry; + else + return NULL; +} + +/** + * Adds an entry to a hashtable or returns an existing one. + * + * \param Hashtable A hashtable object. + * \param Entry The entry to add. + * \param Added A variable which receives TRUE if a new entry was created, and FALSE if an existing + * entry was returned. + * + * \return A pointer to the entry as stored in the hashtable. This pointer is valid until the + * hashtable is modified. If the hashtable already contained an equal entry, the existing entry is + * returned. Check the value of \a Added to determine whether the returned entry is new or existing. + * + * \remarks Entries are only guaranteed to be 8 byte aligned, even on 64-bit systems. + */ +PVOID PhAddEntryHashtableEx( + _Inout_ PPH_HASHTABLE Hashtable, + _In_ PVOID Entry, + _Out_opt_ PBOOLEAN Added + ) +{ + return PhpAddEntryHashtable(Hashtable, Entry, TRUE, Added); +} + +/** + * Clears a hashtable. + * + * \param Hashtable A hashtable object. + */ +VOID PhClearHashtable( + _Inout_ PPH_HASHTABLE Hashtable + ) +{ + if (Hashtable->Count > 0) + { + memset(Hashtable->Buckets, 0xff, sizeof(ULONG) * Hashtable->AllocatedBuckets); + Hashtable->Count = 0; + Hashtable->FreeEntry = -1; + Hashtable->NextEntry = 0; + } +} + +/** + * Enumerates the entries in a hashtable. + * + * \param Hashtable A hashtable object. + * \param Entry A variable which receives a pointer to the hashtable entry. The pointer is valid + * until the hashtable is modified. + * \param EnumerationKey A variable which is initialized to 0 before first calling this function. + * + * \return TRUE if an entry pointer was stored in \a Entry, FALSE if there are no more entries. + * + * \remarks Do not modify the hashtable while the hashtable is being enumerated (between calls to + * this function). Otherwise, the function may behave unexpectedly. You may reset the + * \a EnumerationKey variable to 0 if you wish to restart the enumeration. + */ +BOOLEAN PhEnumHashtable( + _In_ PPH_HASHTABLE Hashtable, + _Out_ PVOID *Entry, + _Inout_ PULONG EnumerationKey + ) +{ + while (*EnumerationKey < Hashtable->NextEntry) + { + PPH_HASHTABLE_ENTRY entry = PH_HASHTABLE_GET_ENTRY(Hashtable, *EnumerationKey); + + (*EnumerationKey)++; + + if (entry->HashCode != ULONG_MAX) + { + *Entry = &entry->Body; + return TRUE; + } + } + + return FALSE; +} + +/** + * Locates an entry in a hashtable. + * + * \param Hashtable A hashtable object. + * \param Entry An entry representing the entry to find. + * + * \return A pointer to the entry as stored in the hashtable. This pointer is valid until the + * hashtable is modified. If the entry could not be found, NULL is returned. + * + * \remarks The entry specified in \a Entry can be a partial entry that is filled in enough so that + * the comparison and hash functions can work with them. + */ +PVOID PhFindEntryHashtable( + _In_ PPH_HASHTABLE Hashtable, + _In_ PVOID Entry + ) +{ + ULONG hashCode; + ULONG index; + ULONG i; + PPH_HASHTABLE_ENTRY entry; + + hashCode = PhpValidateHash(Hashtable->HashFunction(Entry)); + index = PhpIndexFromHash(Hashtable, hashCode); + + for (i = Hashtable->Buckets[index]; i != ULONG_MAX; i = entry->Next) + { + entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i); + + if (entry->HashCode == hashCode && Hashtable->EqualFunction(&entry->Body, Entry)) + { + return &entry->Body; + } + } + + return NULL; +} + +/** + * Removes an entry from a hashtable. + * + * \param Hashtable A hashtable object. + * \param Entry The entry to remove. + * + * \return TRUE if the entry was removed, FALSE if the entry could not be found. + * + * \remarks The entry specified in \a Entry can be an actual entry pointer returned by + * PhFindEntryHashtable, or a partial entry. + */ +BOOLEAN PhRemoveEntryHashtable( + _Inout_ PPH_HASHTABLE Hashtable, + _In_ PVOID Entry + ) +{ + ULONG hashCode; + ULONG index; + ULONG i; + ULONG previousIndex; + PPH_HASHTABLE_ENTRY entry; + + hashCode = PhpValidateHash(Hashtable->HashFunction(Entry)); + index = PhpIndexFromHash(Hashtable, hashCode); + previousIndex = ULONG_MAX; + + for (i = Hashtable->Buckets[index]; i != ULONG_MAX; i = entry->Next) + { + entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i); + + if (entry->HashCode == hashCode && Hashtable->EqualFunction(&entry->Body, Entry)) + { + // Unlink the entry from the bucket. + if (previousIndex == ULONG_MAX) + { + Hashtable->Buckets[index] = entry->Next; + } + else + { + PH_HASHTABLE_GET_ENTRY(Hashtable, previousIndex)->Next = entry->Next; + } + + entry->HashCode = ULONG_MAX; // indicates the entry is not being used + entry->Next = Hashtable->FreeEntry; + Hashtable->FreeEntry = i; + + Hashtable->Count--; + + return TRUE; + } + + previousIndex = i; + } + + return FALSE; +} + +/** + * Generates a hash code for a sequence of bytes. + * + * \param Bytes A pointer to a byte array. + * \param Length The number of bytes to hash. + */ +ULONG PhHashBytes( + _In_reads_(Length) PUCHAR Bytes, + _In_ SIZE_T Length + ) +{ + ULONG hash = 0; + + if (Length == 0) + return hash; + + // FNV-1a algorithm: http://www.isthe.com/chongo/src/fnv/hash_32a.c + + do + { + hash ^= *Bytes++; + hash *= 0x01000193; + } while (--Length != 0); + + return hash; +} + +/** + * Generates a hash code for a string. + * + * \param String The string to hash. + * \param IgnoreCase TRUE for a case-insensitive hash function, otherwise FALSE. + */ +ULONG PhHashStringRef( + _In_ PPH_STRINGREF String, + _In_ BOOLEAN IgnoreCase + ) +{ + ULONG hash = 0; + SIZE_T count; + PWCHAR p; + + if (String->Length == 0) + return 0; + + count = String->Length / sizeof(WCHAR); + p = String->Buffer; + + if (!IgnoreCase) + { + return PhHashBytes((PUCHAR)String->Buffer, String->Length); + } + else + { + do + { + hash ^= (USHORT)RtlUpcaseUnicodeChar(*p++); + hash *= 0x01000193; + } while (--count != 0); + } + + return hash; +} + +BOOLEAN NTAPI PhpSimpleHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_KEY_VALUE_PAIR entry1 = Entry1; + PPH_KEY_VALUE_PAIR entry2 = Entry2; + + return entry1->Key == entry2->Key; +} + +ULONG NTAPI PhpSimpleHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_KEY_VALUE_PAIR entry = Entry; + + return PhHashIntPtr((ULONG_PTR)entry->Key); +} + +PPH_HASHTABLE PhCreateSimpleHashtable( + _In_ ULONG InitialCapacity + ) +{ + return PhCreateHashtable( + sizeof(PH_KEY_VALUE_PAIR), + PhpSimpleHashtableEqualFunction, + PhpSimpleHashtableHashFunction, + InitialCapacity + ); +} + +PVOID PhAddItemSimpleHashtable( + _Inout_ PPH_HASHTABLE SimpleHashtable, + _In_opt_ PVOID Key, + _In_opt_ PVOID Value + ) +{ + PH_KEY_VALUE_PAIR entry; + + entry.Key = Key; + entry.Value = Value; + + if (PhAddEntryHashtable(SimpleHashtable, &entry)) + return Value; + else + return NULL; +} + +PVOID *PhFindItemSimpleHashtable( + _In_ PPH_HASHTABLE SimpleHashtable, + _In_opt_ PVOID Key + ) +{ + PH_KEY_VALUE_PAIR lookupEntry; + PPH_KEY_VALUE_PAIR entry; + + lookupEntry.Key = Key; + entry = PhFindEntryHashtable(SimpleHashtable, &lookupEntry); + + if (entry) + return &entry->Value; + else + return NULL; +} + +BOOLEAN PhRemoveItemSimpleHashtable( + _Inout_ PPH_HASHTABLE SimpleHashtable, + _In_opt_ PVOID Key + ) +{ + PH_KEY_VALUE_PAIR lookupEntry; + + lookupEntry.Key = Key; + + return PhRemoveEntryHashtable(SimpleHashtable, &lookupEntry); +} + +/** + * Initializes a free list object. + * + * \param FreeList A pointer to the free list object. + * \param Size The number of bytes in each allocation. + * \param MaximumCount The number of unused allocations to store. + */ +VOID PhInitializeFreeList( + _Out_ PPH_FREE_LIST FreeList, + _In_ SIZE_T Size, + _In_ ULONG MaximumCount + ) +{ + RtlInitializeSListHead(&FreeList->ListHead); + FreeList->Count = 0; + FreeList->MaximumCount = MaximumCount; + FreeList->Size = Size; +} + +/** + * Frees resources used by a free list object. + * + * \param FreeList A pointer to the free list object. + */ +VOID PhDeleteFreeList( + _Inout_ PPH_FREE_LIST FreeList + ) +{ + PPH_FREE_LIST_ENTRY entry; + PSLIST_ENTRY listEntry; + + listEntry = RtlInterlockedFlushSList(&FreeList->ListHead); + + while (listEntry) + { + entry = CONTAINING_RECORD(listEntry, PH_FREE_LIST_ENTRY, ListEntry); + listEntry = listEntry->Next; + PhFree(entry); + } +} + +/** + * Allocates a block of memory from a free list. + * + * \param FreeList A pointer to a free list object. + * + * \return A pointer to the allocated block of memory. The memory must be freed using + * PhFreeToFreeList(). The block is guaranteed to be aligned at MEMORY_ALLOCATION_ALIGNMENT bytes. + */ +PVOID PhAllocateFromFreeList( + _Inout_ PPH_FREE_LIST FreeList + ) +{ + PPH_FREE_LIST_ENTRY entry; + PSLIST_ENTRY listEntry; + + listEntry = RtlInterlockedPopEntrySList(&FreeList->ListHead); + + if (listEntry) + { + _InterlockedDecrement((PLONG)&FreeList->Count); + entry = CONTAINING_RECORD(listEntry, PH_FREE_LIST_ENTRY, ListEntry); + } + else + { + entry = PhAllocate(UFIELD_OFFSET(PH_FREE_LIST_ENTRY, Body) + FreeList->Size); + } + + return &entry->Body; +} + +/** + * Frees a block of memory to a free list. + * + * \param FreeList A pointer to a free list object. + * \param Memory A pointer to a block of memory. + */ +VOID PhFreeToFreeList( + _Inout_ PPH_FREE_LIST FreeList, + _In_ PVOID Memory + ) +{ + PPH_FREE_LIST_ENTRY entry; + + entry = CONTAINING_RECORD(Memory, PH_FREE_LIST_ENTRY, Body); + + // We don't enforce Count <= MaximumCount (that would require locking), + // but we do check it. + if (FreeList->Count < FreeList->MaximumCount) + { + RtlInterlockedPushEntrySList(&FreeList->ListHead, &entry->ListEntry); + _InterlockedIncrement((PLONG)&FreeList->Count); + } + else + { + PhFree(entry); + } +} + +/** + * Initializes a callback object. + * + * \param Callback A pointer to a callback object. + */ +VOID PhInitializeCallback( + _Out_ PPH_CALLBACK Callback + ) +{ + InitializeListHead(&Callback->ListHead); + PhInitializeQueuedLock(&Callback->ListLock); + PhInitializeCondition(&Callback->BusyCondition); +} + +/** + * Frees resources used by a callback object. + * + * \param Callback A pointer to a callback object. + */ +VOID PhDeleteCallback( + _Inout_ PPH_CALLBACK Callback + ) +{ + // Nothing for now +} + +/** + * Registers a callback function to be notified. + * + * \param Callback A pointer to a callback object. + * \param Function The callback function. + * \param Context A user-defined value to pass to the callback function. + * \param Registration A variable which receives registration information for the callback. Do not + * modify the contents of this structure and do not free the storage for this structure until you + * have unregistered the callback. + */ +VOID PhRegisterCallback( + _Inout_ PPH_CALLBACK Callback, + _In_ PPH_CALLBACK_FUNCTION Function, + _In_opt_ PVOID Context, + _Out_ PPH_CALLBACK_REGISTRATION Registration + ) +{ + PhRegisterCallbackEx( + Callback, + Function, + Context, + 0, + Registration + ); +} + +/** + * Registers a callback function to be notified. + * + * \param Callback A pointer to a callback object. + * \param Function The callback function. + * \param Context A user-defined value to pass to the callback function. + * \param Flags A combination of flags controlling the callback. Set this parameter to 0. + * \param Registration A variable which receives registration information for the callback. Do not + * modify the contents of this structure and do not free the storage for this structure until you + * have unregistered the callback. + */ +VOID PhRegisterCallbackEx( + _Inout_ PPH_CALLBACK Callback, + _In_ PPH_CALLBACK_FUNCTION Function, + _In_opt_ PVOID Context, + _In_ USHORT Flags, + _Out_ PPH_CALLBACK_REGISTRATION Registration + ) +{ + Registration->Function = Function; + Registration->Context = Context; + Registration->Busy = 0; + Registration->Unregistering = FALSE; + Registration->Flags = Flags; + + PhAcquireQueuedLockExclusive(&Callback->ListLock); + InsertTailList(&Callback->ListHead, &Registration->ListEntry); + PhReleaseQueuedLockExclusive(&Callback->ListLock); +} + +/** + * Unregisters a callback function. + * + * \param Callback A pointer to a callback object. + * \param Registration The structure returned by PhRegisterCallback(). + * + * \remarks It is guaranteed that the callback function will not be in execution once this function + * returns. Attempting to unregister a callback function from within the same function will result + * in a deadlock. + */ +VOID PhUnregisterCallback( + _Inout_ PPH_CALLBACK Callback, + _Inout_ PPH_CALLBACK_REGISTRATION Registration + ) +{ + Registration->Unregistering = TRUE; + + PhAcquireQueuedLockExclusive(&Callback->ListLock); + + // Wait for the callback to be unbusy. + while (Registration->Busy) + PhWaitForCondition(&Callback->BusyCondition, &Callback->ListLock, NULL); + + RemoveEntryList(&Registration->ListEntry); + + PhReleaseQueuedLockExclusive(&Callback->ListLock); +} + +/** + * Notifies all registered callback functions. + * + * \param Callback A pointer to a callback object. + * \param Parameter A value to pass to all callback functions. + */ +VOID PhInvokeCallback( + _In_ PPH_CALLBACK Callback, + _In_opt_ PVOID Parameter + ) +{ + PLIST_ENTRY listEntry; + + PhAcquireQueuedLockShared(&Callback->ListLock); + + for (listEntry = Callback->ListHead.Flink; listEntry != &Callback->ListHead; listEntry = listEntry->Flink) + { + PPH_CALLBACK_REGISTRATION registration; + LONG busy; + + registration = CONTAINING_RECORD(listEntry, PH_CALLBACK_REGISTRATION, ListEntry); + + // Don't bother executing the callback function if it is being unregistered. + if (registration->Unregistering) + continue; + + _InterlockedIncrement(®istration->Busy); + + // Execute the callback function. + + PhReleaseQueuedLockShared(&Callback->ListLock); + registration->Function( + Parameter, + registration->Context + ); + PhAcquireQueuedLockShared(&Callback->ListLock); + + busy = _InterlockedDecrement(®istration->Busy); + + if (registration->Unregistering && busy == 0) + { + // Someone started unregistering while the callback function was executing, and we must + // wake them. + PhPulseAllCondition(&Callback->BusyCondition); + } + } + + PhReleaseQueuedLockShared(&Callback->ListLock); +} + +/** + * Retrieves a prime number bigger than or equal to the specified number. + */ +ULONG PhGetPrimeNumber( + _In_ ULONG Minimum + ) +{ + ULONG i, j; + + for (i = 0; i < sizeof(PhpPrimeNumbers) / sizeof(ULONG); i++) + { + if (PhpPrimeNumbers[i] >= Minimum) + return PhpPrimeNumbers[i]; + } + + for (i = Minimum | 1; i < MAXLONG; i += 2) + { + ULONG sqrtI = (ULONG)sqrt(i); + + for (j = 3; j <= sqrtI; j += 2) + { + if (i % j == 0) + { + // Not a prime. + goto NextPrime; + } + } + + // Success. + return i; +NextPrime: + NOTHING; + } + + return Minimum; +} + +/** + * Rounds up a number to the next power of two. + */ +ULONG PhRoundUpToPowerOfTwo( + _In_ ULONG Number + ) +{ + Number--; + Number |= Number >> 1; + Number |= Number >> 2; + Number |= Number >> 4; + Number |= Number >> 8; + Number |= Number >> 16; + Number++; + + return Number; +} + +/** + * Performs exponentiation. + */ +ULONG PhExponentiate( + _In_ ULONG Base, + _In_ ULONG Exponent + ) +{ + ULONG result = 1; + + while (Exponent) + { + if (Exponent & 1) + result *= Base; + + Exponent >>= 1; + Base *= Base; + } + + return result; +} + +/** + * Performs 64-bit exponentiation. + */ +ULONG64 PhExponentiate64( + _In_ ULONG64 Base, + _In_ ULONG Exponent + ) +{ + ULONG64 result = 1; + + while (Exponent) + { + if (Exponent & 1) + result *= Base; + + Exponent >>= 1; + Base *= Base; + } + + return result; +} + +/** + * Converts a sequence of hexadecimal digits into a byte array. + * + * \param String A string containing hexadecimal digits to convert. The string must have an even + * number of digits, because each pair of hexadecimal digits represents one byte. Example: + * "129a2eff5c0b". + * \param Buffer The output buffer. + * + * \return TRUE if the string was successfully converted, otherwise FALSE. + */ +BOOLEAN PhHexStringToBuffer( + _In_ PPH_STRINGREF String, + _Out_writes_bytes_(String->Length / sizeof(WCHAR) / 2) PUCHAR Buffer + ) +{ + SIZE_T i; + SIZE_T length; + + // The string must have an even length. + if ((String->Length / sizeof(WCHAR)) & 1) + return FALSE; + + length = String->Length / sizeof(WCHAR) / 2; + + for (i = 0; i < length; i++) + { + Buffer[i] = + (UCHAR)(PhCharToInteger[(UCHAR)String->Buffer[i * sizeof(WCHAR)]] << 4) + + (UCHAR)PhCharToInteger[(UCHAR)String->Buffer[i * sizeof(WCHAR) + 1]]; + } + + return TRUE; +} + +PPH_STRING PhHexStringToBufferEx( + _In_ PPH_STRINGREF String + ) +{ + PPH_STRING string; + SIZE_T length; + SIZE_T i; + + if ((String->Length / sizeof(WCHAR)) & 1) + return FALSE; + + length = String->Length / sizeof(WCHAR) / 2; + string = PhCreateStringEx(NULL, length); + + for (i = 0; i < length; i++) + { + ((PUCHAR)string->Buffer)[i] = + (UCHAR)(PhCharToInteger[(UCHAR)String->Buffer[i * sizeof(WCHAR)]] << 4) + + (UCHAR)PhCharToInteger[(UCHAR)String->Buffer[i * sizeof(WCHAR) + 1]]; + } + + return string; +} + +/** + * Converts a byte array into a sequence of hexadecimal digits. + * + * \param Buffer The input buffer. + * \param Length The number of bytes to convert. + * + * \return A string containing a sequence of hexadecimal digits. + */ +PPH_STRING PhBufferToHexString( + _In_reads_bytes_(Length) PUCHAR Buffer, + _In_ ULONG Length + ) +{ + return PhBufferToHexStringEx(Buffer, Length, FALSE); +} + +/** + * Converts a byte array into a sequence of hexadecimal digits. + * + * \param Buffer The input buffer. + * \param Length The number of bytes to convert. + * \param UpperCase TRUE to use uppercase characters, otherwise FALSE. + * + * \return A string containing a sequence of hexadecimal digits. + */ +PPH_STRING PhBufferToHexStringEx( + _In_reads_bytes_(Length) PUCHAR Buffer, + _In_ ULONG Length, + _In_ BOOLEAN UpperCase + ) +{ + PPH_STRING string; + PCHAR table; + ULONG i; + + if (UpperCase) + table = PhIntegerToCharUpper; + else + table = PhIntegerToChar; + + string = PhCreateStringEx(NULL, Length * 2 * sizeof(WCHAR)); + + for (i = 0; i < Length; i++) + { + string->Buffer[i * sizeof(WCHAR)] = table[Buffer[i] >> 4]; + string->Buffer[i * sizeof(WCHAR) + 1] = table[Buffer[i] & 0xf]; + } + + return string; +} + +/** + * Converts a string to an integer. + * + * \param String The string to process. + * \param Base The base which the string uses to represent the integer. The maximum value is 69. + * \param Integer The resulting integer. + */ +BOOLEAN PhpStringToInteger64( + _In_ PPH_STRINGREF String, + _In_ ULONG Base, + _Out_ PULONG64 Integer + ) +{ + BOOLEAN valid = TRUE; + ULONG64 result; + SIZE_T length; + SIZE_T i; + + length = String->Length / sizeof(WCHAR); + result = 0; + + for (i = 0; i < length; i++) + { + ULONG value; + + value = PhCharToInteger[(UCHAR)String->Buffer[i]]; + + if (value < Base) + result = result * Base + value; + else + valid = FALSE; + } + + *Integer = result; + + return valid; +} + +/** + * Converts a string to an integer. + * + * \param String The string to process. + * \param Base The base which the string uses to represent the integer. The maximum value is 69. If + * the parameter is 0, the base is inferred from the string. + * \param Integer The resulting integer. + * + * \remarks If \a Base is 0, the following prefixes may be used to indicate bases: + * + * \li \c 0x Base 16. + * \li \c 0o Base 8. + * \li \c 0b Base 2. + * \li \c 0t Base 3. + * \li \c 0q Base 4. + * \li \c 0w Base 12. + * \li \c 0r Base 32. + * + * If there is no recognized prefix, base 10 is used. + */ +BOOLEAN PhStringToInteger64( + _In_ PPH_STRINGREF String, + _In_opt_ ULONG Base, + _Out_opt_ PLONG64 Integer + ) +{ + BOOLEAN valid; + ULONG64 result; + PH_STRINGREF string; + BOOLEAN negative; + ULONG base; + + if (Base > 69) + return FALSE; + + string = *String; + negative = FALSE; + + if (string.Length != 0 && (string.Buffer[0] == '-' || string.Buffer[0] == '+')) + { + if (string.Buffer[0] == '-') + negative = TRUE; + + PhSkipStringRef(&string, sizeof(WCHAR)); + } + + // If the caller specified a base, don't perform any additional processing. + + if (Base) + { + base = Base; + } + else + { + base = 10; + + if (string.Length >= 2 * sizeof(WCHAR) && string.Buffer[0] == '0') + { + switch (string.Buffer[1]) + { + case 'x': + case 'X': + base = 16; + break; + case 'o': + case 'O': + base = 8; + break; + case 'b': + case 'B': + base = 2; + break; + case 't': // ternary + case 'T': + base = 3; + break; + case 'q': // quaternary + case 'Q': + base = 4; + break; + case 'w': // base 12 + case 'W': + base = 12; + break; + case 'r': // base 32 + case 'R': + base = 32; + break; + } + + if (base != 10) + PhSkipStringRef(&string, 2 * sizeof(WCHAR)); + } + } + + valid = PhpStringToInteger64(&string, base, &result); + + if (Integer) + *Integer = negative ? -(LONG64)result : result; + + return valid; +} + +BOOLEAN PhpStringToDouble( + _In_ PPH_STRINGREF String, + _In_ ULONG Base, + _Out_ DOUBLE *Double + ) +{ + BOOLEAN valid = TRUE; + BOOLEAN dotSeen = FALSE; + DOUBLE result; + DOUBLE fraction; + SIZE_T length; + SIZE_T i; + + length = String->Length / sizeof(WCHAR); + result = 0; + fraction = 1; + + for (i = 0; i < length; i++) + { + if (String->Buffer[i] == '.') + { + if (!dotSeen) + dotSeen = TRUE; + else + valid = FALSE; + } + else + { + ULONG value; + + value = PhCharToInteger[(UCHAR)String->Buffer[i]]; + + if (value < Base) + { + if (!dotSeen) + { + result = result * Base + value; + } + else + { + fraction /= Base; + result = result + value * fraction; + } + } + else + { + valid = FALSE; + } + } + } + + *Double = result; + + return valid; +} + +/** + * Converts a string to a double-precision floating point value. + * + * \param String The string to process. + * \param Base Reserved. + * \param Double The resulting double value. + */ +BOOLEAN PhStringToDouble( + _In_ PPH_STRINGREF String, + _Reserved_ ULONG Base, + _Out_opt_ DOUBLE *Double + ) +{ + BOOLEAN valid; + DOUBLE result; + PH_STRINGREF string; + BOOLEAN negative; + + string = *String; + negative = FALSE; + + if (string.Length != 0 && (string.Buffer[0] == '-' || string.Buffer[0] == '+')) + { + if (string.Buffer[0] == '-') + negative = TRUE; + + PhSkipStringRef(&string, sizeof(WCHAR)); + } + + valid = PhpStringToDouble(&string, 10, &result); + + if (Double) + *Double = negative ? -result : result; + + return valid; +} + +/** + * Converts an integer to a string. + * + * \param Integer The integer to process. + * \param Base The base which the integer is represented with. The maximum value is 69. The base + * cannot be 1. If the parameter is 0, the base used is 10. + * \param Signed TRUE if \a Integer is a signed value, otherwise FALSE. + * + * \return The resulting string, or NULL if an error occurred. + */ +PPH_STRING PhIntegerToString64( + _In_ LONG64 Integer, + _In_opt_ ULONG Base, + _In_ BOOLEAN Signed + ) +{ + PH_FORMAT format; + + if (Base == 1 || Base > 69) + return NULL; + + if (Signed) + PhInitFormatI64D(&format, Integer); + else + PhInitFormatI64U(&format, Integer); + + if (Base != 0) + { + format.Type |= FormatUseRadix; + format.Radix = (UCHAR)Base; + } + + return PhFormat(&format, 1, 0); +} + +VOID PhPrintTimeSpan( + _Out_writes_(PH_TIMESPAN_STR_LEN_1) PWSTR Destination, + _In_ ULONG64 Ticks, + _In_opt_ ULONG Mode + ) +{ + switch (Mode) + { + case PH_TIMESPAN_HMSM: + _snwprintf_s( + Destination, + PH_TIMESPAN_STR_LEN, + _TRUNCATE, + L"%02I64u:%02I64u:%02I64u.%03I64u", + PH_TICKS_PARTIAL_HOURS(Ticks), + PH_TICKS_PARTIAL_MIN(Ticks), + PH_TICKS_PARTIAL_SEC(Ticks), + PH_TICKS_PARTIAL_MS(Ticks) + ); + break; + case PH_TIMESPAN_DHMS: + _snwprintf_s( + Destination, + PH_TIMESPAN_STR_LEN, + _TRUNCATE, + L"%I64u:%02I64u:%02I64u:%02I64u", + PH_TICKS_PARTIAL_DAYS(Ticks), + PH_TICKS_PARTIAL_HOURS(Ticks), + PH_TICKS_PARTIAL_MIN(Ticks), + PH_TICKS_PARTIAL_SEC(Ticks) + ); + break; + default: + _snwprintf_s( + Destination, + PH_TIMESPAN_STR_LEN, + _TRUNCATE, + L"%02I64u:%02I64u:%02I64u", + PH_TICKS_PARTIAL_HOURS(Ticks), + PH_TICKS_PARTIAL_MIN(Ticks), + PH_TICKS_PARTIAL_SEC(Ticks) + ); + break; + } +} + +/** + * Fills a memory block with a ULONG pattern. + * + * \param Memory The memory block. The block must be 4 byte aligned. + * \param Value The ULONG pattern. + * \param Count The number of elements. + */ +VOID PhFillMemoryUlong( + _Inout_updates_(Count) _Needs_align_(4) PULONG Memory, + _In_ ULONG Value, + _In_ SIZE_T Count + ) +{ + __m128i pattern; + SIZE_T count; + + if (PhpVectorLevel < PH_VECTOR_LEVEL_SSE2) + { + if (Count != 0) + { + do + { + *Memory++ = Value; + } while (--Count != 0); + } + + return; + } + + if ((ULONG_PTR)Memory & 0xf) + { + switch ((ULONG_PTR)Memory & 0xf) + { + case 0x4: + if (Count >= 1) + { + *Memory++ = Value; + Count--; + } + __fallthrough; + case 0x8: + if (Count >= 1) + { + *Memory++ = Value; + Count--; + } + __fallthrough; + case 0xc: + if (Count >= 1) + { + *Memory++ = Value; + Count--; + } + break; + } + } + + pattern = _mm_set1_epi32(Value); + count = Count / 4; + + if (count != 0) + { + do + { + _mm_store_si128((__m128i *)Memory, pattern); + Memory += 4; + } while (--count != 0); + } + + switch (Count & 0x3) + { + case 0x3: + *Memory++ = Value; + __fallthrough; + case 0x2: + *Memory++ = Value; + __fallthrough; + case 0x1: + *Memory++ = Value; + break; + } +} + +/** + * Divides an array of numbers by a number. + * + * \param A The destination array, divided by \a B. + * \param B The number. + * \param Count The number of elements. + */ +VOID PhDivideSinglesBySingle( + _Inout_updates_(Count) PFLOAT A, + _In_ FLOAT B, + _In_ SIZE_T Count + ) +{ + PFLOAT endA; + __m128 b; + + if (PhpVectorLevel < PH_VECTOR_LEVEL_SSE2) + { + while (Count--) + *A++ /= B; + + return; + } + + if ((ULONG_PTR)A & 0xf) + { + switch ((ULONG_PTR)A & 0xf) + { + case 0x4: + if (Count >= 1) + { + *A++ /= B; + Count--; + } + __fallthrough; + case 0x8: + if (Count >= 1) + { + *A++ /= B; + Count--; + } + __fallthrough; + case 0xc: + if (Count >= 1) + { + *A++ /= B; + Count--; + } + else + { + return; // essential; A may not be aligned properly + } + break; + } + } + + endA = (PFLOAT)((ULONG_PTR)(A + Count) & ~0xf); + b = _mm_load1_ps(&B); + + while (A != endA) + { + __m128 a; + + a = _mm_load_ps(A); + a = _mm_div_ps(a, b); + _mm_store_ps(A, a); + + A += 4; + } + + switch (Count & 0x3) + { + case 0x3: + *A++ /= B; + __fallthrough; + case 0x2: + *A++ /= B; + __fallthrough; + case 0x1: + *A++ /= B; + break; + } +} diff --git a/phlib/circbuf.c b/phlib/circbuf.c index 3696c11efc4e..1f9efb8bfd37 100644 --- a/phlib/circbuf.c +++ b/phlib/circbuf.c @@ -1,22 +1,22 @@ -#include -#include - -#undef T -#define T ULONG -#include "circbuf_i.h" - -#undef T -#define T ULONG64 -#include "circbuf_i.h" - -#undef T -#define T PVOID -#include "circbuf_i.h" - -#undef T -#define T SIZE_T -#include "circbuf_i.h" - -#undef T -#define T FLOAT -#include "circbuf_i.h" +#include +#include + +#undef T +#define T ULONG +#include "circbuf_i.h" + +#undef T +#define T ULONG64 +#include "circbuf_i.h" + +#undef T +#define T PVOID +#include "circbuf_i.h" + +#undef T +#define T SIZE_T +#include "circbuf_i.h" + +#undef T +#define T FLOAT +#include "circbuf_i.h" diff --git a/phlib/circbuf_i.h b/phlib/circbuf_i.h index c5e49b09398c..fd84c142880c 100644 --- a/phlib/circbuf_i.h +++ b/phlib/circbuf_i.h @@ -1,121 +1,121 @@ -#ifdef T - -#include - -VOID T___(PhInitializeCircularBuffer, T)( - _Out_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ ULONG Size - ) -{ -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - Buffer->Size = PhRoundUpToPowerOfTwo(Size); - Buffer->SizeMinusOne = Buffer->Size - 1; -#else - Buffer->Size = Size; -#endif - - Buffer->Count = 0; - Buffer->Index = 0; - Buffer->Data = PhAllocate(sizeof(T) * Buffer->Size); -} - -VOID T___(PhDeleteCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer - ) -{ - PhFree(Buffer->Data); -} - -VOID T___(PhResizeCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ ULONG NewSize - ) -{ - T *newData; - ULONG tailSize; - ULONG headSize; - -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - NewSize = PhRoundUpToPowerOfTwo(NewSize); -#endif - - // If we're not actually resizing it, return. - if (NewSize == Buffer->Size) - return; - - newData = PhAllocate(sizeof(T) * NewSize); - tailSize = (ULONG)(Buffer->Size - Buffer->Index); - headSize = Buffer->Count - tailSize; - - if (NewSize > Buffer->Size) - { - // Copy the tail, then the head. - memcpy(newData, &Buffer->Data[Buffer->Index], sizeof(T) * tailSize); - memcpy(&newData[tailSize], Buffer->Data, sizeof(T) * headSize); - Buffer->Index = 0; - } - else - { - if (tailSize >= NewSize) - { - // Copy only a part of the tail. - memcpy(newData, &Buffer->Data[Buffer->Index], sizeof(T) * NewSize); - Buffer->Index = 0; - } - else - { - // Copy the tail, then only part of the head. - memcpy(newData, &Buffer->Data[Buffer->Index], sizeof(T) * tailSize); - memcpy(&newData[tailSize], Buffer->Data, sizeof(T) * (NewSize - tailSize)); - Buffer->Index = 0; - } - - // Since we're making the circular buffer smaller, limit the count. - if (Buffer->Count > NewSize) - Buffer->Count = NewSize; - } - - Buffer->Data = newData; - Buffer->Size = NewSize; -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - Buffer->SizeMinusOne = NewSize - 1; -#endif -} - -VOID T___(PhClearCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer - ) -{ - Buffer->Count = 0; - Buffer->Index = 0; -} - -VOID T___(PhCopyCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _Out_writes_(Count) T *Destination, - _In_ ULONG Count - ) -{ - ULONG tailSize; - ULONG headSize; - - tailSize = (ULONG)(Buffer->Size - Buffer->Index); - headSize = Buffer->Count - tailSize; - - if (Count > Buffer->Count) - Count = Buffer->Count; - - if (tailSize >= Count) - { - // Copy only a part of the tail. - memcpy(Destination, &Buffer->Data[Buffer->Index], sizeof(T) * Count); - } - else - { - // Copy the tail, then only part of the head. - memcpy(Destination, &Buffer->Data[Buffer->Index], sizeof(T) * tailSize); - memcpy(&Destination[tailSize], Buffer->Data, sizeof(T) * (Count - tailSize)); - } -} - -#endif +#ifdef T + +#include + +VOID T___(PhInitializeCircularBuffer, T)( + _Out_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ ULONG Size + ) +{ +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + Buffer->Size = PhRoundUpToPowerOfTwo(Size); + Buffer->SizeMinusOne = Buffer->Size - 1; +#else + Buffer->Size = Size; +#endif + + Buffer->Count = 0; + Buffer->Index = 0; + Buffer->Data = PhAllocate(sizeof(T) * Buffer->Size); +} + +VOID T___(PhDeleteCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer + ) +{ + PhFree(Buffer->Data); +} + +VOID T___(PhResizeCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ ULONG NewSize + ) +{ + T *newData; + ULONG tailSize; + ULONG headSize; + +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + NewSize = PhRoundUpToPowerOfTwo(NewSize); +#endif + + // If we're not actually resizing it, return. + if (NewSize == Buffer->Size) + return; + + newData = PhAllocate(sizeof(T) * NewSize); + tailSize = (ULONG)(Buffer->Size - Buffer->Index); + headSize = Buffer->Count - tailSize; + + if (NewSize > Buffer->Size) + { + // Copy the tail, then the head. + memcpy(newData, &Buffer->Data[Buffer->Index], sizeof(T) * tailSize); + memcpy(&newData[tailSize], Buffer->Data, sizeof(T) * headSize); + Buffer->Index = 0; + } + else + { + if (tailSize >= NewSize) + { + // Copy only a part of the tail. + memcpy(newData, &Buffer->Data[Buffer->Index], sizeof(T) * NewSize); + Buffer->Index = 0; + } + else + { + // Copy the tail, then only part of the head. + memcpy(newData, &Buffer->Data[Buffer->Index], sizeof(T) * tailSize); + memcpy(&newData[tailSize], Buffer->Data, sizeof(T) * (NewSize - tailSize)); + Buffer->Index = 0; + } + + // Since we're making the circular buffer smaller, limit the count. + if (Buffer->Count > NewSize) + Buffer->Count = NewSize; + } + + Buffer->Data = newData; + Buffer->Size = NewSize; +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + Buffer->SizeMinusOne = NewSize - 1; +#endif +} + +VOID T___(PhClearCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer + ) +{ + Buffer->Count = 0; + Buffer->Index = 0; +} + +VOID T___(PhCopyCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _Out_writes_(Count) T *Destination, + _In_ ULONG Count + ) +{ + ULONG tailSize; + ULONG headSize; + + tailSize = (ULONG)(Buffer->Size - Buffer->Index); + headSize = Buffer->Count - tailSize; + + if (Count > Buffer->Count) + Count = Buffer->Count; + + if (tailSize >= Count) + { + // Copy only a part of the tail. + memcpy(Destination, &Buffer->Data[Buffer->Index], sizeof(T) * Count); + } + else + { + // Copy the tail, then only part of the head. + memcpy(Destination, &Buffer->Data[Buffer->Index], sizeof(T) * tailSize); + memcpy(&Destination[tailSize], Buffer->Data, sizeof(T) * (Count - tailSize)); + } +} + +#endif diff --git a/phlib/colorbox.c b/phlib/colorbox.c index 4011dd41b1fe..913e8b9a629a 100644 --- a/phlib/colorbox.c +++ b/phlib/colorbox.c @@ -1,230 +1,230 @@ -/* - * Process Hacker - - * color picker - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include - -typedef struct _PHP_COLORBOX_CONTEXT -{ - COLORREF SelectedColor; - BOOLEAN Hot; - BOOLEAN HasFocus; -} PHP_COLORBOX_CONTEXT, *PPHP_COLORBOX_CONTEXT; - -LRESULT CALLBACK PhpColorBoxWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -BOOLEAN PhColorBoxInitialization( - VOID - ) -{ - WNDCLASSEX c = { sizeof(c) }; - - c.style = CS_GLOBALCLASS; - c.lpfnWndProc = PhpColorBoxWndProc; - c.cbClsExtra = 0; - c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; - c.hIcon = NULL; - c.hCursor = LoadCursor(NULL, IDC_ARROW); - c.hbrBackground = NULL; - c.lpszMenuName = NULL; - c.lpszClassName = PH_COLORBOX_CLASSNAME; - c.hIconSm = NULL; - - if (!RegisterClassEx(&c)) - return FALSE; - - return TRUE; -} - -VOID PhpCreateColorBoxContext( - _Out_ PPHP_COLORBOX_CONTEXT *Context - ) -{ - PPHP_COLORBOX_CONTEXT context; - - context = PhAllocate(sizeof(PHP_COLORBOX_CONTEXT)); - memset(context, 0, sizeof(PHP_COLORBOX_CONTEXT)); - *Context = context; -} - -VOID PhpFreeColorBoxContext( - _In_ _Post_invalid_ PPHP_COLORBOX_CONTEXT Context - ) -{ - PhFree(Context); -} - -VOID PhpChooseColor( - _In_ HWND hwnd, - _In_ PPHP_COLORBOX_CONTEXT Context - ) -{ - CHOOSECOLOR chooseColor = { sizeof(chooseColor) }; - COLORREF customColors[16] = { 0 }; - - chooseColor.hwndOwner = hwnd; - chooseColor.rgbResult = Context->SelectedColor; - chooseColor.lpCustColors = customColors; - chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; - - if (ChooseColor(&chooseColor)) - { - Context->SelectedColor = chooseColor.rgbResult; - InvalidateRect(hwnd, NULL, TRUE); - } -} - -LRESULT CALLBACK PhpColorBoxWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPHP_COLORBOX_CONTEXT context; - - context = (PPHP_COLORBOX_CONTEXT)GetWindowLongPtr(hwnd, 0); - - if (uMsg == WM_CREATE) - { - PhpCreateColorBoxContext(&context); - SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); - } - - if (!context) - return DefWindowProc(hwnd, uMsg, wParam, lParam); - - switch (uMsg) - { - case WM_CREATE: - { - // Nothing - } - break; - case WM_DESTROY: - { - SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); - PhpFreeColorBoxContext(context); - } - break; - case WM_PAINT: - { - PAINTSTRUCT paintStruct; - RECT clientRect; - HDC hdc; - - if (hdc = BeginPaint(hwnd, &paintStruct)) - { - GetClientRect(hwnd, &clientRect); - - // Border color - SetDCPenColor(hdc, RGB(0x44, 0x44, 0x44)); - - // Fill color - if (!context->Hot && !context->HasFocus) - SetDCBrushColor(hdc, context->SelectedColor); - else - SetDCBrushColor(hdc, PhMakeColorBrighter(context->SelectedColor, 64)); - - // Draw the rectangle. - SelectObject(hdc, GetStockObject(DC_PEN)); - SelectObject(hdc, GetStockObject(DC_BRUSH)); - Rectangle(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom); - - EndPaint(hwnd, &paintStruct); - } - } - return 0; - case WM_ERASEBKGND: - return 1; - case WM_MOUSEMOVE: - { - if (!context->Hot) - { - TRACKMOUSEEVENT trackMouseEvent = { sizeof(trackMouseEvent) }; - - context->Hot = TRUE; - InvalidateRect(hwnd, NULL, TRUE); - - trackMouseEvent.dwFlags = TME_LEAVE; - trackMouseEvent.hwndTrack = hwnd; - TrackMouseEvent(&trackMouseEvent); - } - } - break; - case WM_MOUSELEAVE: - { - context->Hot = FALSE; - InvalidateRect(hwnd, NULL, TRUE); - } - break; - case WM_LBUTTONDOWN: - { - PhpChooseColor(hwnd, context); - } - break; - case WM_SETFOCUS: - { - context->HasFocus = TRUE; - InvalidateRect(hwnd, NULL, TRUE); - } - return 0; - case WM_KILLFOCUS: - { - context->HasFocus = FALSE; - InvalidateRect(hwnd, NULL, TRUE); - } - return 0; - case WM_GETDLGCODE: - if (wParam == VK_RETURN) - return DLGC_WANTMESSAGE; - return 0; - case WM_KEYDOWN: - { - switch (wParam) - { - case VK_SPACE: - case VK_RETURN: - PhpChooseColor(hwnd, context); - break; - } - } - break; - case CBCM_SETCOLOR: - context->SelectedColor = (COLORREF)wParam; - return TRUE; - case CBCM_GETCOLOR: - return (LRESULT)context->SelectedColor; - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} +/* + * Process Hacker - + * color picker + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#include + +typedef struct _PHP_COLORBOX_CONTEXT +{ + COLORREF SelectedColor; + BOOLEAN Hot; + BOOLEAN HasFocus; +} PHP_COLORBOX_CONTEXT, *PPHP_COLORBOX_CONTEXT; + +LRESULT CALLBACK PhpColorBoxWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOLEAN PhColorBoxInitialization( + VOID + ) +{ + WNDCLASSEX c = { sizeof(c) }; + + c.style = CS_GLOBALCLASS; + c.lpfnWndProc = PhpColorBoxWndProc; + c.cbClsExtra = 0; + c.cbWndExtra = sizeof(PVOID); + c.hInstance = PhInstanceHandle; + c.hIcon = NULL; + c.hCursor = LoadCursor(NULL, IDC_ARROW); + c.hbrBackground = NULL; + c.lpszMenuName = NULL; + c.lpszClassName = PH_COLORBOX_CLASSNAME; + c.hIconSm = NULL; + + if (!RegisterClassEx(&c)) + return FALSE; + + return TRUE; +} + +VOID PhpCreateColorBoxContext( + _Out_ PPHP_COLORBOX_CONTEXT *Context + ) +{ + PPHP_COLORBOX_CONTEXT context; + + context = PhAllocate(sizeof(PHP_COLORBOX_CONTEXT)); + memset(context, 0, sizeof(PHP_COLORBOX_CONTEXT)); + *Context = context; +} + +VOID PhpFreeColorBoxContext( + _In_ _Post_invalid_ PPHP_COLORBOX_CONTEXT Context + ) +{ + PhFree(Context); +} + +VOID PhpChooseColor( + _In_ HWND hwnd, + _In_ PPHP_COLORBOX_CONTEXT Context + ) +{ + CHOOSECOLOR chooseColor = { sizeof(chooseColor) }; + COLORREF customColors[16] = { 0 }; + + chooseColor.hwndOwner = hwnd; + chooseColor.rgbResult = Context->SelectedColor; + chooseColor.lpCustColors = customColors; + chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; + + if (ChooseColor(&chooseColor)) + { + Context->SelectedColor = chooseColor.rgbResult; + InvalidateRect(hwnd, NULL, TRUE); + } +} + +LRESULT CALLBACK PhpColorBoxWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_COLORBOX_CONTEXT context; + + context = (PPHP_COLORBOX_CONTEXT)GetWindowLongPtr(hwnd, 0); + + if (uMsg == WM_CREATE) + { + PhpCreateColorBoxContext(&context); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); + } + + if (!context) + return DefWindowProc(hwnd, uMsg, wParam, lParam); + + switch (uMsg) + { + case WM_CREATE: + { + // Nothing + } + break; + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); + PhpFreeColorBoxContext(context); + } + break; + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + RECT clientRect; + HDC hdc; + + if (hdc = BeginPaint(hwnd, &paintStruct)) + { + GetClientRect(hwnd, &clientRect); + + // Border color + SetDCPenColor(hdc, RGB(0x44, 0x44, 0x44)); + + // Fill color + if (!context->Hot && !context->HasFocus) + SetDCBrushColor(hdc, context->SelectedColor); + else + SetDCBrushColor(hdc, PhMakeColorBrighter(context->SelectedColor, 64)); + + // Draw the rectangle. + SelectPen(hdc, GetStockPen(DC_PEN)); + SelectBrush(hdc, GetStockBrush(DC_BRUSH)); + Rectangle(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom); + + EndPaint(hwnd, &paintStruct); + } + } + return 0; + case WM_ERASEBKGND: + return 1; + case WM_MOUSEMOVE: + { + if (!context->Hot) + { + TRACKMOUSEEVENT trackMouseEvent = { sizeof(trackMouseEvent) }; + + context->Hot = TRUE; + InvalidateRect(hwnd, NULL, TRUE); + + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + TrackMouseEvent(&trackMouseEvent); + } + } + break; + case WM_MOUSELEAVE: + { + context->Hot = FALSE; + InvalidateRect(hwnd, NULL, TRUE); + } + break; + case WM_LBUTTONDOWN: + { + PhpChooseColor(hwnd, context); + } + break; + case WM_SETFOCUS: + { + context->HasFocus = TRUE; + InvalidateRect(hwnd, NULL, TRUE); + } + return 0; + case WM_KILLFOCUS: + { + context->HasFocus = FALSE; + InvalidateRect(hwnd, NULL, TRUE); + } + return 0; + case WM_GETDLGCODE: + if (wParam == VK_RETURN) + return DLGC_WANTMESSAGE; + return 0; + case WM_KEYDOWN: + { + switch (wParam) + { + case VK_SPACE: + case VK_RETURN: + PhpChooseColor(hwnd, context); + break; + } + } + break; + case CBCM_SETCOLOR: + context->SelectedColor = (COLORREF)wParam; + return TRUE; + case CBCM_GETCOLOR: + return (LRESULT)context->SelectedColor; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} diff --git a/phlib/cpysave.c b/phlib/cpysave.c index 3076f9f491cc..5c39206e0a1d 100644 --- a/phlib/cpysave.c +++ b/phlib/cpysave.c @@ -1,574 +1,578 @@ -/* - * Process Hacker - - * copy/save code for listviews and treelists - * - * Copyright (C) 2010-2012 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#define TAB_SIZE 8 - -VOID PhpEscapeStringForCsv( - _Inout_ PPH_STRING_BUILDER StringBuilder, - _In_ PPH_STRING String - ) -{ - SIZE_T i; - SIZE_T length; - PWCHAR runStart; - SIZE_T runLength; - - length = String->Length / sizeof(WCHAR); - runStart = NULL; - - for (i = 0; i < length; i++) - { - switch (String->Buffer[i]) - { - case '\"': - if (runStart) - { - PhAppendStringBuilderEx(StringBuilder, runStart, runLength * sizeof(WCHAR)); - runStart = NULL; - } - - PhAppendStringBuilder2(StringBuilder, L"\"\""); - - break; - default: - if (runStart) - { - runLength++; - } - else - { - runStart = &String->Buffer[i]; - runLength = 1; - } - - break; - } - } - - if (runStart) - PhAppendStringBuilderEx(StringBuilder, runStart, runLength * sizeof(WCHAR)); -} - -/** - * Allocates a text table. - * - * \param Table A variable which receives a pointer to the text table. - * \param Rows The number of rows in the table. - * \param Columns The number of columns in the table. - */ -VOID PhaCreateTextTable( - _Out_ PPH_STRING ***Table, - _In_ ULONG Rows, - _In_ ULONG Columns - ) -{ - PPH_STRING **table; - ULONG i; - - table = PH_AUTO(PhCreateAlloc(sizeof(PPH_STRING *) * Rows)); - - for (i = 0; i < Rows; i++) - { - table[i] = PH_AUTO(PhCreateAlloc(sizeof(PPH_STRING) * Columns)); - memset(table[i], 0, sizeof(PPH_STRING) * Columns); - } - - *Table = table; -} - -/** - * Formats a text table to a list of lines. - * - * \param Table A pointer to the text table. - * \param Rows The number of rows in the table. - * \param Columns The number of columns in the table. - * \param Mode The export formatting mode. - * - * \return A list of strings for each line in the output. The list object and - * string objects are not auto-dereferenced. - */ -PPH_LIST PhaFormatTextTable( - _In_ PPH_STRING **Table, - _In_ ULONG Rows, - _In_ ULONG Columns, - _In_ ULONG Mode - ) -{ - PPH_LIST lines; - // The tab count array contains the number of tabs need to fill the biggest - // row cell in each column. - PULONG tabCount; - ULONG i; - ULONG j; - - if (Mode == PH_EXPORT_MODE_TABS || Mode == PH_EXPORT_MODE_SPACES) - { - // Create the tab count array. - - tabCount = PH_AUTO(PhCreateAlloc(sizeof(ULONG) * Columns)); - memset(tabCount, 0, sizeof(ULONG) * Columns); // zero all values - - for (i = 0; i < Rows; i++) - { - for (j = 0; j < Columns; j++) - { - ULONG newCount; - - if (Table[i][j]) - newCount = (ULONG)(Table[i][j]->Length / sizeof(WCHAR) / TAB_SIZE); - else - newCount = 0; - - // Replace the existing count if this tab count is bigger. - if (tabCount[j] < newCount) - tabCount[j] = newCount; - } - } - } - - // Create the final list of lines by going through each cell and appending - // the proper tab count (if we are using tabs). This will make sure each column - // is properly aligned. - - lines = PhCreateList(Rows); - - for (i = 0; i < Rows; i++) - { - PH_STRING_BUILDER stringBuilder; - - PhInitializeStringBuilder(&stringBuilder, 100); - - switch (Mode) - { - case PH_EXPORT_MODE_TABS: - { - for (j = 0; j < Columns; j++) - { - ULONG k; - - if (Table[i][j]) - { - // Calculate the number of tabs needed. - k = (ULONG)(tabCount[j] + 1 - Table[i][j]->Length / sizeof(WCHAR) / TAB_SIZE); - - PhAppendStringBuilder(&stringBuilder, &Table[i][j]->sr); - } - else - { - k = tabCount[j] + 1; - } - - PhAppendCharStringBuilder2(&stringBuilder, '\t', k); - } - } - break; - case PH_EXPORT_MODE_SPACES: - { - for (j = 0; j < Columns; j++) - { - ULONG k; - - if (Table[i][j]) - { - // Calculate the number of spaces needed. - k = (ULONG)((tabCount[j] + 1) * TAB_SIZE - Table[i][j]->Length / sizeof(WCHAR)); - - PhAppendStringBuilder(&stringBuilder, &Table[i][j]->sr); - } - else - { - k = (tabCount[j] + 1) * TAB_SIZE; - } - - PhAppendCharStringBuilder2(&stringBuilder, ' ', k); - } - } - break; - case PH_EXPORT_MODE_CSV: - { - for (j = 0; j < Columns; j++) - { - PhAppendCharStringBuilder(&stringBuilder, '\"'); - - if (Table[i][j]) - { - PhpEscapeStringForCsv(&stringBuilder, Table[i][j]); - } - - PhAppendCharStringBuilder(&stringBuilder, '\"'); - - if (j != Columns - 1) - PhAppendCharStringBuilder(&stringBuilder, ','); - } - } - break; - } - - PhAddItemList(lines, PhFinalStringBuilderString(&stringBuilder)); - } - - return lines; -} - -VOID PhMapDisplayIndexTreeNew( - _In_ HWND TreeNewHandle, - _Out_opt_ PULONG *DisplayToId, - _Out_opt_ PWSTR **DisplayToText, - _Out_ PULONG NumberOfColumns - ) -{ - PPH_TREENEW_COLUMN fixedColumn; - ULONG numberOfColumns; - PULONG displayToId; - PWSTR *displayToText; - ULONG i; - PH_TREENEW_COLUMN column; - - fixedColumn = TreeNew_GetFixedColumn(TreeNewHandle); - numberOfColumns = TreeNew_GetVisibleColumnCount(TreeNewHandle); - - displayToId = PhAllocate(numberOfColumns * sizeof(ULONG)); - - if (fixedColumn) - { - TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns - 1, displayToId + 1); - displayToId[0] = fixedColumn->Id; - } - else - { - TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns, displayToId); - } - - if (DisplayToText) - { - displayToText = PhAllocate(numberOfColumns * sizeof(PWSTR)); - - for (i = 0; i < numberOfColumns; i++) - { - if (TreeNew_GetColumn(TreeNewHandle, displayToId[i], &column)) - { - displayToText[i] = column.Text; - } - } - - *DisplayToText = displayToText; - } - - if (DisplayToId) - *DisplayToId = displayToId; - else - PhFree(displayToId); - - *NumberOfColumns = numberOfColumns; -} - -PPH_STRING PhGetTreeNewText( - _In_ HWND TreeNewHandle, - _Reserved_ ULONG Reserved - ) -{ - PH_STRING_BUILDER stringBuilder; - PULONG displayToId; - ULONG rows; - ULONG columns; - ULONG i; - ULONG j; - - PhMapDisplayIndexTreeNew(TreeNewHandle, &displayToId, NULL, &columns); - rows = TreeNew_GetFlatNodeCount(TreeNewHandle); - - PhInitializeStringBuilder(&stringBuilder, 0x100); - - for (i = 0; i < rows; i++) - { - PH_TREENEW_GET_CELL_TEXT getCellText; - - getCellText.Node = TreeNew_GetFlatNode(TreeNewHandle, i); - assert(getCellText.Node); - - if (!getCellText.Node->Selected) - continue; - - for (j = 0; j < columns; j++) - { - getCellText.Id = displayToId[j]; - PhInitializeEmptyStringRef(&getCellText.Text); - TreeNew_GetCellText(TreeNewHandle, &getCellText); - - PhAppendStringBuilder(&stringBuilder, &getCellText.Text); - PhAppendStringBuilder2(&stringBuilder, L", "); - } - - // Remove the trailing comma and space. - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 2); - - PhAppendStringBuilder2(&stringBuilder, L"\r\n"); - } - - PhFree(displayToId); - - return PhFinalStringBuilderString(&stringBuilder); -} - -PPH_LIST PhGetGenericTreeNewLines( - _In_ HWND TreeNewHandle, - _In_ ULONG Mode - ) -{ - PH_AUTO_POOL autoPool; - PPH_LIST lines; - ULONG rows; - ULONG columns; - ULONG numberOfNodes; - PULONG displayToId; - PWSTR *displayToText; - PPH_STRING **table; - ULONG i; - ULONG j; - - PhInitializeAutoPool(&autoPool); - - numberOfNodes = TreeNew_GetFlatNodeCount(TreeNewHandle); - - rows = numberOfNodes + 1; - PhMapDisplayIndexTreeNew(TreeNewHandle, &displayToId, &displayToText, &columns); - - PhaCreateTextTable(&table, rows, columns); - - for (i = 0; i < columns; i++) - table[0][i] = PhaCreateString(displayToText[i]); - - for (i = 0; i < numberOfNodes; i++) - { - PPH_TREENEW_NODE node; - - node = TreeNew_GetFlatNode(TreeNewHandle, i); - - if (node) - { - for (j = 0; j < columns; j++) - { - PH_TREENEW_GET_CELL_TEXT getCellText; - - getCellText.Node = node; - getCellText.Id = displayToId[j]; - PhInitializeEmptyStringRef(&getCellText.Text); - TreeNew_GetCellText(TreeNewHandle, &getCellText); - - table[i + 1][j] = PhaCreateStringEx(getCellText.Text.Buffer, getCellText.Text.Length); - } - } - else - { - for (j = 0; j < columns; j++) - { - table[i + 1][j] = PH_AUTO(PhReferenceEmptyString()); - } - } - } - - PhFree(displayToText); - PhFree(displayToId); - - lines = PhaFormatTextTable(table, rows, columns, Mode); - - PhDeleteAutoPool(&autoPool); - - return lines; -} - -VOID PhaMapDisplayIndexListView( - _In_ HWND ListViewHandle, - _Out_writes_(Count) PULONG DisplayToId, - _Out_writes_opt_(Count) PPH_STRING *DisplayToText, - _In_ ULONG Count, - _Out_ PULONG NumberOfColumns - ) -{ - LVCOLUMN lvColumn; - ULONG i; - ULONG count; - WCHAR buffer[128]; - - count = 0; - lvColumn.mask = LVCF_ORDER | LVCF_TEXT; - lvColumn.pszText = buffer; - lvColumn.cchTextMax = sizeof(buffer) / sizeof(WCHAR); - - for (i = 0; i < Count; i++) - { - if (ListView_GetColumn(ListViewHandle, i, &lvColumn)) - { - ULONG displayIndex; - - displayIndex = (ULONG)lvColumn.iOrder; - assert(displayIndex < Count); - DisplayToId[displayIndex] = i; - - if (DisplayToText) - DisplayToText[displayIndex] = PhaCreateString(buffer); - - count++; - } - else - { - break; - } - } - - *NumberOfColumns = count; -} - -PPH_STRING PhaGetListViewItemText( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ INT SubItemIndex - ) -{ - PPH_STRING buffer; - SIZE_T allocatedCount; - SIZE_T count; - LVITEM lvItem; - - // Unfortunately LVM_GETITEMTEXT doesn't want to return the actual length of the text. - // Keep doubling the buffer size until we get a return count that is strictly less than - // the amount we allocated. - - buffer = NULL; - allocatedCount = 256; - count = allocatedCount; - - while (count >= allocatedCount) - { - if (buffer) - PhDereferenceObject(buffer); - - allocatedCount *= 2; - buffer = PhCreateStringEx(NULL, allocatedCount * sizeof(WCHAR)); - buffer->Buffer[0] = 0; - - lvItem.iSubItem = SubItemIndex; - lvItem.cchTextMax = (INT)allocatedCount + 1; - lvItem.pszText = buffer->Buffer; - count = SendMessage(ListViewHandle, LVM_GETITEMTEXT, Index, (LPARAM)&lvItem); - } - - PhTrimToNullTerminatorString(buffer); - PH_AUTO(buffer); - - return buffer; -} - -PPH_STRING PhGetListViewText( - _In_ HWND ListViewHandle - ) -{ - PH_AUTO_POOL autoPool; - PH_STRING_BUILDER stringBuilder; - ULONG displayToId[100]; - ULONG rows; - ULONG columns; - ULONG i; - ULONG j; - - PhInitializeAutoPool(&autoPool); - - PhaMapDisplayIndexListView(ListViewHandle, displayToId, NULL, 100, &columns); - rows = ListView_GetItemCount(ListViewHandle); - - PhInitializeStringBuilder(&stringBuilder, 0x100); - - for (i = 0; i < rows; i++) - { - if (!(ListView_GetItemState(ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED)) - continue; - - for (j = 0; j < columns; j++) - { - PhAppendStringBuilder(&stringBuilder, &PhaGetListViewItemText(ListViewHandle, i, j)->sr); - PhAppendStringBuilder2(&stringBuilder, L", "); - } - - // Remove the trailing comma and space. - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 2); - - PhAppendStringBuilder2(&stringBuilder, L"\r\n"); - } - - PhDeleteAutoPool(&autoPool); - - return PhFinalStringBuilderString(&stringBuilder); -} - -PPH_LIST PhGetListViewLines( - _In_ HWND ListViewHandle, - _In_ ULONG Mode - ) -{ - PH_AUTO_POOL autoPool; - PPH_LIST lines; - ULONG rows; - ULONG columns; - ULONG displayToId[100]; - PPH_STRING displayToText[100]; - PPH_STRING **table; - ULONG i; - ULONG j; - - PhInitializeAutoPool(&autoPool); - - rows = ListView_GetItemCount(ListViewHandle) + 1; // +1 for column headers - - // Create the display index/text to ID map. - PhaMapDisplayIndexListView(ListViewHandle, displayToId, displayToText, 100, &columns); - - PhaCreateTextTable(&table, rows, columns); - - // Populate the first row with the column headers. - for (i = 0; i < columns; i++) - table[0][i] = displayToText[i]; - - // Populate the other rows with text. - for (i = 1; i < rows; i++) - { - for (j = 0; j < columns; j++) - { - // Important: use this to bypass extlv's hooking. - // extlv only hooks LVM_GETITEM, not LVM_GETITEMTEXT. - table[i][j] = PhaGetListViewItemText(ListViewHandle, i - 1, j); - } - } - - lines = PhaFormatTextTable(table, rows, columns, Mode); - - PhDeleteAutoPool(&autoPool); - - return lines; -} +/* + * Process Hacker - + * copy/save code for listviews and treelists + * + * Copyright (C) 2010-2012 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#define TAB_SIZE 8 + +VOID PhpEscapeStringForCsv( + _Inout_ PPH_STRING_BUILDER StringBuilder, + _In_ PPH_STRING String + ) +{ + SIZE_T i; + SIZE_T length; + PWCHAR runStart; + SIZE_T runLength; + + length = String->Length / sizeof(WCHAR); + runStart = NULL; + + for (i = 0; i < length; i++) + { + switch (String->Buffer[i]) + { + case '\"': + if (runStart) + { + PhAppendStringBuilderEx(StringBuilder, runStart, runLength * sizeof(WCHAR)); + runStart = NULL; + } + + PhAppendStringBuilder2(StringBuilder, L"\"\""); + + break; + default: + if (runStart) + { + runLength++; + } + else + { + runStart = &String->Buffer[i]; + runLength = 1; + } + + break; + } + } + + if (runStart) + PhAppendStringBuilderEx(StringBuilder, runStart, runLength * sizeof(WCHAR)); +} + +/** + * Allocates a text table. + * + * \param Table A variable which receives a pointer to the text table. + * \param Rows The number of rows in the table. + * \param Columns The number of columns in the table. + */ +VOID PhaCreateTextTable( + _Out_ PPH_STRING ***Table, + _In_ ULONG Rows, + _In_ ULONG Columns + ) +{ + PPH_STRING **table; + ULONG i; + + table = PH_AUTO(PhCreateAlloc(sizeof(PPH_STRING *) * Rows)); + + for (i = 0; i < Rows; i++) + { + table[i] = PH_AUTO(PhCreateAlloc(sizeof(PPH_STRING) * Columns)); + memset(table[i], 0, sizeof(PPH_STRING) * Columns); + } + + *Table = table; +} + +/** + * Formats a text table to a list of lines. + * + * \param Table A pointer to the text table. + * \param Rows The number of rows in the table. + * \param Columns The number of columns in the table. + * \param Mode The export formatting mode. + * + * \return A list of strings for each line in the output. The list object and + * string objects are not auto-dereferenced. + */ +PPH_LIST PhaFormatTextTable( + _In_ PPH_STRING **Table, + _In_ ULONG Rows, + _In_ ULONG Columns, + _In_ ULONG Mode + ) +{ + PPH_LIST lines; + // The tab count array contains the number of tabs need to fill the biggest + // row cell in each column. + PULONG tabCount; + ULONG i; + ULONG j; + + if (Mode == PH_EXPORT_MODE_TABS || Mode == PH_EXPORT_MODE_SPACES) + { + // Create the tab count array. + + tabCount = PH_AUTO(PhCreateAlloc(sizeof(ULONG) * Columns)); + memset(tabCount, 0, sizeof(ULONG) * Columns); // zero all values + + for (i = 0; i < Rows; i++) + { + for (j = 0; j < Columns; j++) + { + ULONG newCount; + + if (Table[i][j]) + newCount = (ULONG)(Table[i][j]->Length / sizeof(WCHAR) / TAB_SIZE); + else + newCount = 0; + + // Replace the existing count if this tab count is bigger. + if (tabCount[j] < newCount) + tabCount[j] = newCount; + } + } + } + + // Create the final list of lines by going through each cell and appending + // the proper tab count (if we are using tabs). This will make sure each column + // is properly aligned. + + lines = PhCreateList(Rows); + + for (i = 0; i < Rows; i++) + { + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + + switch (Mode) + { + case PH_EXPORT_MODE_TABS: + { + for (j = 0; j < Columns; j++) + { + ULONG k; + + if (Table[i][j]) + { + // Calculate the number of tabs needed. + k = (ULONG)(tabCount[j] + 1 - Table[i][j]->Length / sizeof(WCHAR) / TAB_SIZE); + + PhAppendStringBuilder(&stringBuilder, &Table[i][j]->sr); + } + else + { + k = tabCount[j] + 1; + } + + PhAppendCharStringBuilder2(&stringBuilder, '\t', k); + } + } + break; + case PH_EXPORT_MODE_SPACES: + { + for (j = 0; j < Columns; j++) + { + ULONG k; + + if (Table[i][j]) + { + // Calculate the number of spaces needed. + k = (ULONG)((tabCount[j] + 1) * TAB_SIZE - Table[i][j]->Length / sizeof(WCHAR)); + + PhAppendStringBuilder(&stringBuilder, &Table[i][j]->sr); + } + else + { + k = (tabCount[j] + 1) * TAB_SIZE; + } + + PhAppendCharStringBuilder2(&stringBuilder, ' ', k); + } + } + break; + case PH_EXPORT_MODE_CSV: + { + for (j = 0; j < Columns; j++) + { + PhAppendCharStringBuilder(&stringBuilder, '\"'); + + if (Table[i][j]) + { + PhpEscapeStringForCsv(&stringBuilder, Table[i][j]); + } + + PhAppendCharStringBuilder(&stringBuilder, '\"'); + + if (j != Columns - 1) + PhAppendCharStringBuilder(&stringBuilder, ','); + } + } + break; + } + + PhAddItemList(lines, PhFinalStringBuilderString(&stringBuilder)); + } + + return lines; +} + +VOID PhMapDisplayIndexTreeNew( + _In_ HWND TreeNewHandle, + _Out_opt_ PULONG *DisplayToId, + _Out_opt_ PWSTR **DisplayToText, + _Out_ PULONG NumberOfColumns + ) +{ + PPH_TREENEW_COLUMN fixedColumn; + ULONG numberOfColumns; + PULONG displayToId; + PWSTR *displayToText; + ULONG i; + PH_TREENEW_COLUMN column; + + fixedColumn = TreeNew_GetFixedColumn(TreeNewHandle); + numberOfColumns = TreeNew_GetVisibleColumnCount(TreeNewHandle); + + displayToId = PhAllocate(numberOfColumns * sizeof(ULONG)); + + if (fixedColumn) + { + TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns - 1, displayToId + 1); + displayToId[0] = fixedColumn->Id; + } + else + { + TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns, displayToId); + } + + if (DisplayToText) + { + displayToText = PhAllocate(numberOfColumns * sizeof(PWSTR)); + + for (i = 0; i < numberOfColumns; i++) + { + if (TreeNew_GetColumn(TreeNewHandle, displayToId[i], &column)) + { + displayToText[i] = column.Text; + } + } + + *DisplayToText = displayToText; + } + + if (DisplayToId) + *DisplayToId = displayToId; + else + PhFree(displayToId); + + *NumberOfColumns = numberOfColumns; +} + +PPH_STRING PhGetTreeNewText( + _In_ HWND TreeNewHandle, + _Reserved_ ULONG Reserved + ) +{ + PH_STRING_BUILDER stringBuilder; + PULONG displayToId; + ULONG rows; + ULONG columns; + ULONG i; + ULONG j; + + PhMapDisplayIndexTreeNew(TreeNewHandle, &displayToId, NULL, &columns); + rows = TreeNew_GetFlatNodeCount(TreeNewHandle); + + PhInitializeStringBuilder(&stringBuilder, 0x100); + + for (i = 0; i < rows; i++) + { + PH_TREENEW_GET_CELL_TEXT getCellText; + + getCellText.Node = TreeNew_GetFlatNode(TreeNewHandle, i); + assert(getCellText.Node); + + if (!getCellText.Node->Selected) + continue; + + for (j = 0; j < columns; j++) + { + getCellText.Id = displayToId[j]; + PhInitializeEmptyStringRef(&getCellText.Text); + TreeNew_GetCellText(TreeNewHandle, &getCellText); + + // Ignore empty columns. -dmex + if (getCellText.Text.Length != 0) + { + PhAppendStringBuilder(&stringBuilder, &getCellText.Text); + PhAppendStringBuilder2(&stringBuilder, L", "); + } + } + + // Remove the trailing comma and space. + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + } + + PhFree(displayToId); + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_LIST PhGetGenericTreeNewLines( + _In_ HWND TreeNewHandle, + _In_ ULONG Mode + ) +{ + PH_AUTO_POOL autoPool; + PPH_LIST lines; + ULONG rows; + ULONG columns; + ULONG numberOfNodes; + PULONG displayToId; + PWSTR *displayToText; + PPH_STRING **table; + ULONG i; + ULONG j; + + PhInitializeAutoPool(&autoPool); + + numberOfNodes = TreeNew_GetFlatNodeCount(TreeNewHandle); + + rows = numberOfNodes + 1; + PhMapDisplayIndexTreeNew(TreeNewHandle, &displayToId, &displayToText, &columns); + + PhaCreateTextTable(&table, rows, columns); + + for (i = 0; i < columns; i++) + table[0][i] = PhaCreateString(displayToText[i]); + + for (i = 0; i < numberOfNodes; i++) + { + PPH_TREENEW_NODE node; + + node = TreeNew_GetFlatNode(TreeNewHandle, i); + + if (node) + { + for (j = 0; j < columns; j++) + { + PH_TREENEW_GET_CELL_TEXT getCellText; + + getCellText.Node = node; + getCellText.Id = displayToId[j]; + PhInitializeEmptyStringRef(&getCellText.Text); + TreeNew_GetCellText(TreeNewHandle, &getCellText); + + table[i + 1][j] = PhaCreateStringEx(getCellText.Text.Buffer, getCellText.Text.Length); + } + } + else + { + for (j = 0; j < columns; j++) + { + table[i + 1][j] = PH_AUTO(PhReferenceEmptyString()); + } + } + } + + PhFree(displayToText); + PhFree(displayToId); + + lines = PhaFormatTextTable(table, rows, columns, Mode); + + PhDeleteAutoPool(&autoPool); + + return lines; +} + +VOID PhaMapDisplayIndexListView( + _In_ HWND ListViewHandle, + _Out_writes_(Count) PULONG DisplayToId, + _Out_writes_opt_(Count) PPH_STRING *DisplayToText, + _In_ ULONG Count, + _Out_ PULONG NumberOfColumns + ) +{ + LVCOLUMN lvColumn; + ULONG i; + ULONG count; + WCHAR buffer[128]; + + count = 0; + lvColumn.mask = LVCF_ORDER | LVCF_TEXT; + lvColumn.pszText = buffer; + lvColumn.cchTextMax = sizeof(buffer) / sizeof(WCHAR); + + for (i = 0; i < Count; i++) + { + if (ListView_GetColumn(ListViewHandle, i, &lvColumn)) + { + ULONG displayIndex; + + displayIndex = (ULONG)lvColumn.iOrder; + assert(displayIndex < Count); + DisplayToId[displayIndex] = i; + + if (DisplayToText) + DisplayToText[displayIndex] = PhaCreateString(buffer); + + count++; + } + else + { + break; + } + } + + *NumberOfColumns = count; +} + +PPH_STRING PhaGetListViewItemText( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ INT SubItemIndex + ) +{ + PPH_STRING buffer; + SIZE_T allocatedCount; + SIZE_T count; + LVITEM lvItem; + + // Unfortunately LVM_GETITEMTEXT doesn't want to return the actual length of the text. + // Keep doubling the buffer size until we get a return count that is strictly less than + // the amount we allocated. + + buffer = NULL; + allocatedCount = 256; + count = allocatedCount; + + while (count >= allocatedCount) + { + if (buffer) + PhDereferenceObject(buffer); + + allocatedCount *= 2; + buffer = PhCreateStringEx(NULL, allocatedCount * sizeof(WCHAR)); + buffer->Buffer[0] = UNICODE_NULL; + + lvItem.iSubItem = SubItemIndex; + lvItem.cchTextMax = (INT)allocatedCount + 1; + lvItem.pszText = buffer->Buffer; + count = SendMessage(ListViewHandle, LVM_GETITEMTEXT, Index, (LPARAM)&lvItem); + } + + PhTrimToNullTerminatorString(buffer); + PH_AUTO(buffer); + + return buffer; +} + +PPH_STRING PhGetListViewText( + _In_ HWND ListViewHandle + ) +{ + PH_AUTO_POOL autoPool; + PH_STRING_BUILDER stringBuilder; + ULONG displayToId[100]; + ULONG rows; + ULONG columns; + ULONG i; + ULONG j; + + PhInitializeAutoPool(&autoPool); + + PhaMapDisplayIndexListView(ListViewHandle, displayToId, NULL, 100, &columns); + rows = ListView_GetItemCount(ListViewHandle); + + PhInitializeStringBuilder(&stringBuilder, 0x100); + + for (i = 0; i < rows; i++) + { + if (!(ListView_GetItemState(ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED)) + continue; + + for (j = 0; j < columns; j++) + { + PhAppendStringBuilder(&stringBuilder, &PhaGetListViewItemText(ListViewHandle, i, j)->sr); + PhAppendStringBuilder2(&stringBuilder, L", "); + } + + // Remove the trailing comma and space. + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + } + + PhDeleteAutoPool(&autoPool); + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_LIST PhGetListViewLines( + _In_ HWND ListViewHandle, + _In_ ULONG Mode + ) +{ + PH_AUTO_POOL autoPool; + PPH_LIST lines; + ULONG rows; + ULONG columns; + ULONG displayToId[100]; + PPH_STRING displayToText[100]; + PPH_STRING **table; + ULONG i; + ULONG j; + + PhInitializeAutoPool(&autoPool); + + rows = ListView_GetItemCount(ListViewHandle) + 1; // +1 for column headers + + // Create the display index/text to ID map. + PhaMapDisplayIndexListView(ListViewHandle, displayToId, displayToText, 100, &columns); + + PhaCreateTextTable(&table, rows, columns); + + // Populate the first row with the column headers. + for (i = 0; i < columns; i++) + table[0][i] = displayToText[i]; + + // Populate the other rows with text. + for (i = 1; i < rows; i++) + { + for (j = 0; j < columns; j++) + { + // Important: use this to bypass extlv's hooking. + // extlv only hooks LVM_GETITEM, not LVM_GETITEMTEXT. + table[i][j] = PhaGetListViewItemText(ListViewHandle, i - 1, j); + } + } + + lines = PhaFormatTextTable(table, rows, columns, Mode); + + PhDeleteAutoPool(&autoPool); + + return lines; +} diff --git a/phlib/data.c b/phlib/data.c index 87939f96aa5f..0389dd563ce3 100644 --- a/phlib/data.c +++ b/phlib/data.c @@ -1,219 +1,219 @@ -#include -#include - -// SIDs - -SID PhSeNobodySid = { SID_REVISION, 1, SECURITY_NULL_SID_AUTHORITY, { SECURITY_NULL_RID } }; - -SID PhSeEveryoneSid = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, { SECURITY_WORLD_RID } }; - -SID PhSeLocalSid = { SID_REVISION, 1, SECURITY_LOCAL_SID_AUTHORITY, { SECURITY_LOCAL_RID } }; - -SID PhSeCreatorOwnerSid = { SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, { SECURITY_CREATOR_OWNER_RID } }; -SID PhSeCreatorGroupSid = { SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, { SECURITY_CREATOR_GROUP_RID } }; - -SID PhSeDialupSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_DIALUP_RID } }; -SID PhSeNetworkSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_NETWORK_RID } }; -SID PhSeBatchSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_BATCH_RID } }; -SID PhSeInteractiveSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_INTERACTIVE_RID } }; -SID PhSeServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_SERVICE_RID } }; -SID PhSeAnonymousLogonSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_ANONYMOUS_LOGON_RID } }; -SID PhSeProxySid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_PROXY_RID } }; -SID PhSeAuthenticatedUserSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_AUTHENTICATED_USER_RID } }; -SID PhSeRestrictedCodeSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_RESTRICTED_CODE_RID } }; -SID PhSeTerminalServerUserSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_TERMINAL_SERVER_RID } }; -SID PhSeRemoteInteractiveLogonSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_REMOTE_LOGON_RID } }; -SID PhSeLocalSystemSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_LOCAL_SYSTEM_RID } }; -SID PhSeLocalServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_LOCAL_SERVICE_RID } }; -SID PhSeNetworkServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_NETWORK_SERVICE_RID } }; - -// Unicode - -PH_STRINGREF PhUnicodeByteOrderMark = PH_STRINGREF_INIT(L"\ufeff"); - -// Characters - -DECLSPEC_SELECTANY -BOOLEAN PhCharIsPrintable[256] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 - 15 */ // TAB, LF and CR are printable - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ' ' - '/' */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* '0' - '9' */ - 1, 1, 1, 1, 1, 1, 1, /* ':' - '@' */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 'A' - 'Z' */ - 1, 1, 1, 1, 1, 1, /* '[' - '`' */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 'a' - 'z' */ - 1, 1, 1, 1, 0, /* '{' - 127 */ // DEL is not printable - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 240 - 255 */ -}; - -DECLSPEC_SELECTANY -ULONG PhCharToInteger[256] = -{ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 15 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 16 - 31 */ - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* ' ' - '/' */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */ - 52, 53, 54, 55, 56, 57, 58, /* ':' - '@' */ - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, /* 'A' - 'Z' */ - 59, 60, 61, 62, 63, 64, /* '[' - '`' */ - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, /* 'a' - 'z' */ - 65, 66, 67, 68, -1, /* '{' - 127 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 128 - 143 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 144 - 159 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 160 - 175 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 176 - 191 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 192 - 207 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 208 - 223 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 224 - 239 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 240 - 255 */ -}; - -DECLSPEC_SELECTANY -CHAR PhIntegerToChar[69] = - "0123456789" /* 0 - 9 */ - "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ - " !\"#$%&'()*+,-./" /* 36 - 51 */ - ":;<=>?@" /* 52 - 58 */ - "[\\]^_`" /* 59 - 64 */ - "{|}~" /* 65 - 68 */ - ; - -DECLSPEC_SELECTANY -CHAR PhIntegerToCharUpper[69] = - "0123456789" /* 0 - 9 */ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 10 - 35 */ - " !\"#$%&'()*+,-./" /* 36 - 51 */ - ":;<=>?@" /* 52 - 58 */ - "[\\]^_`" /* 59 - 64 */ - "{|}~" /* 65 - 68 */ - ; - -// CRC32 (IEEE 802.3) - -DECLSPEC_SELECTANY -ULONG PhCrc32Table[256] = -{ - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -// Enums - -DECLSPEC_SELECTANY -WCHAR *PhIoPriorityHintNames[MaxIoPriorityTypes] = -{ - L"Very low", - L"Low", - L"Normal", - L"High", - L"Critical" -}; - -DECLSPEC_SELECTANY -WCHAR *PhPagePriorityNames[MEMORY_PRIORITY_NORMAL + 1] = -{ - L"Lowest", - L"Very low", - L"Low", - L"Medium", - L"Below normal", - L"Normal" -}; - -DECLSPEC_SELECTANY -WCHAR *PhKThreadStateNames[MaximumThreadState] = -{ - L"Initialized", - L"Ready", - L"Running", - L"Standby", - L"Terminated", - L"Waiting", - L"Transition", - L"DeferredReady", - L"GateWait", - L"WaitingForProcessInSwap" -}; - -DECLSPEC_SELECTANY -WCHAR *PhKWaitReasonNames[MaximumWaitReason] = -{ - L"Executive", - L"FreePage", - L"PageIn", - L"PoolAllocation", - L"DelayExecution", - L"Suspended", - L"UserRequest", - L"WrExecutive", - L"WrFreePage", - L"WrPageIn", - L"WrPoolAllocation", - L"WrDelayExecution", - L"WrSuspended", - L"WrUserRequest", - L"WrEventPair", - L"WrQueue", - L"WrLpcReceive", - L"WrLpcReply", - L"WrVirtualMemory", - L"WrPageOut", - L"WrRendezvous", - L"WrKeyedEvent", - L"WrTerminated", - L"WrProcessInSwap", - L"WrCpuRateControl", - L"WrCalloutStack", - L"WrKernel", - L"WrResource", - L"WrPushLock", - L"WrMutex", - L"WrQuantumEnd", - L"WrDispatchInt", - L"WrPreempted", - L"WrYieldExecution", - L"WrFastMutex", - L"WrGuardedMutex", - L"WrRundown", - L"WrAlertByThreadId", - L"WrDeferredPreempt" -}; +#include +#include + +// SIDs + +SID PhSeNobodySid = { SID_REVISION, 1, SECURITY_NULL_SID_AUTHORITY, { SECURITY_NULL_RID } }; + +SID PhSeEveryoneSid = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, { SECURITY_WORLD_RID } }; + +SID PhSeLocalSid = { SID_REVISION, 1, SECURITY_LOCAL_SID_AUTHORITY, { SECURITY_LOCAL_RID } }; + +SID PhSeCreatorOwnerSid = { SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, { SECURITY_CREATOR_OWNER_RID } }; +SID PhSeCreatorGroupSid = { SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, { SECURITY_CREATOR_GROUP_RID } }; + +SID PhSeDialupSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_DIALUP_RID } }; +SID PhSeNetworkSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_NETWORK_RID } }; +SID PhSeBatchSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_BATCH_RID } }; +SID PhSeInteractiveSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_INTERACTIVE_RID } }; +SID PhSeServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_SERVICE_RID } }; +SID PhSeAnonymousLogonSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_ANONYMOUS_LOGON_RID } }; +SID PhSeProxySid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_PROXY_RID } }; +SID PhSeAuthenticatedUserSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_AUTHENTICATED_USER_RID } }; +SID PhSeRestrictedCodeSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_RESTRICTED_CODE_RID } }; +SID PhSeTerminalServerUserSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_TERMINAL_SERVER_RID } }; +SID PhSeRemoteInteractiveLogonSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_REMOTE_LOGON_RID } }; +SID PhSeLocalSystemSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_LOCAL_SYSTEM_RID } }; +SID PhSeLocalServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_LOCAL_SERVICE_RID } }; +SID PhSeNetworkServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_NETWORK_SERVICE_RID } }; + +// Unicode + +PH_STRINGREF PhUnicodeByteOrderMark = PH_STRINGREF_INIT(L"\ufeff"); + +// Characters + +DECLSPEC_SELECTANY +BOOLEAN PhCharIsPrintable[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 - 15 */ // TAB, LF and CR are printable + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ' ' - '/' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* '0' - '9' */ + 1, 1, 1, 1, 1, 1, 1, /* ':' - '@' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 'A' - 'Z' */ + 1, 1, 1, 1, 1, 1, /* '[' - '`' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 'a' - 'z' */ + 1, 1, 1, 1, 0, /* '{' - 127 */ // DEL is not printable + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 - 143 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 - 159 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 175 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 - 191 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 - 207 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 - 223 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 - 239 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 240 - 255 */ +}; + +DECLSPEC_SELECTANY +ULONG PhCharToInteger[256] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 15 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 16 - 31 */ + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* ' ' - '/' */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */ + 52, 53, 54, 55, 56, 57, 58, /* ':' - '@' */ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, /* 'A' - 'Z' */ + 59, 60, 61, 62, 63, 64, /* '[' - '`' */ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, /* 'a' - 'z' */ + 65, 66, 67, 68, -1, /* '{' - 127 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 128 - 143 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 144 - 159 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 160 - 175 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 176 - 191 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 192 - 207 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 208 - 223 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 224 - 239 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 240 - 255 */ +}; + +DECLSPEC_SELECTANY +CHAR PhIntegerToChar[69] = + "0123456789" /* 0 - 9 */ + "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ + " !\"#$%&'()*+,-./" /* 36 - 51 */ + ":;<=>?@" /* 52 - 58 */ + "[\\]^_`" /* 59 - 64 */ + "{|}~" /* 65 - 68 */ + ; + +DECLSPEC_SELECTANY +CHAR PhIntegerToCharUpper[69] = + "0123456789" /* 0 - 9 */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 10 - 35 */ + " !\"#$%&'()*+,-./" /* 36 - 51 */ + ":;<=>?@" /* 52 - 58 */ + "[\\]^_`" /* 59 - 64 */ + "{|}~" /* 65 - 68 */ + ; + +// CRC32 (IEEE 802.3) + +DECLSPEC_SELECTANY +ULONG PhCrc32Table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +// Enums + +DECLSPEC_SELECTANY +WCHAR *PhIoPriorityHintNames[MaxIoPriorityTypes] = +{ + L"Very low", + L"Low", + L"Normal", + L"High", + L"Critical" +}; + +DECLSPEC_SELECTANY +WCHAR *PhPagePriorityNames[MEMORY_PRIORITY_NORMAL + 1] = +{ + L"Lowest", + L"Very low", + L"Low", + L"Medium", + L"Below normal", + L"Normal" +}; + +DECLSPEC_SELECTANY +WCHAR *PhKThreadStateNames[MaximumThreadState] = +{ + L"Initialized", + L"Ready", + L"Running", + L"Standby", + L"Terminated", + L"Waiting", + L"Transition", + L"DeferredReady", + L"GateWait", + L"WaitingForProcessInSwap" +}; + +DECLSPEC_SELECTANY +WCHAR *PhKWaitReasonNames[MaximumWaitReason] = +{ + L"Executive", + L"FreePage", + L"PageIn", + L"PoolAllocation", + L"DelayExecution", + L"Suspended", + L"UserRequest", + L"WrExecutive", + L"WrFreePage", + L"WrPageIn", + L"WrPoolAllocation", + L"WrDelayExecution", + L"WrSuspended", + L"WrUserRequest", + L"WrEventPair", + L"WrQueue", + L"WrLpcReceive", + L"WrLpcReply", + L"WrVirtualMemory", + L"WrPageOut", + L"WrRendezvous", + L"WrKeyedEvent", + L"WrTerminated", + L"WrProcessInSwap", + L"WrCpuRateControl", + L"WrCalloutStack", + L"WrKernel", + L"WrResource", + L"WrPushLock", + L"WrMutex", + L"WrQuantumEnd", + L"WrDispatchInt", + L"WrPreempted", + L"WrYieldExecution", + L"WrFastMutex", + L"WrGuardedMutex", + L"WrRundown", + L"WrAlertByThreadId", + L"WrDeferredPreempt" +}; diff --git a/phlib/dspick.c b/phlib/dspick.c index f62cb3bcd44d..b5b410b751f7 100644 --- a/phlib/dspick.c +++ b/phlib/dspick.c @@ -1,251 +1,249 @@ -/* - * Process Hacker - - * DS object picker wrapper - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#define CINTERFACE -#define COBJMACROS -#include - -#include -#include - -#define IDataObject_AddRef(This) ((This)->lpVtbl->AddRef(This)) -#define IDataObject_Release(This) ((This)->lpVtbl->Release(This)) -#define IDataObject_GetData(This, pformatetcIn, pmedium) ((This)->lpVtbl->GetData(This, pformatetcIn, pmedium)) - -#define IDsObjectPicker_QueryInterface(This, riid, ppvObject) ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) -#define IDsObjectPicker_AddRef(This) ((This)->lpVtbl->AddRef(This)) -#define IDsObjectPicker_Release(This) ((This)->lpVtbl->Release(This)) -#define IDsObjectPicker_Initialize(This, pInitInfo) ((This)->lpVtbl->Initialize(This, pInitInfo)) -#define IDsObjectPicker_InvokeDialog(This, hwndParent, ppdoSelections) ((This)->lpVtbl->InvokeDialog(This, hwndParent, ppdoSelections)) - -IDsObjectPicker *PhpCreateDsObjectPicker( - VOID - ) -{ - static CLSID CLSID_DsObjectPicker_I = { 0x17d6ccd8, 0x3b7b, 0x11d2, { 0xb9, 0xe0, 0x00, 0xc0, 0x4f, 0xd8, 0xdb, 0xf7 } }; - static IID IID_IDsObjectPicker_I = { 0x0c87e64e, 0x3b7a, 0x11d2, { 0xb9, 0xe0, 0x00, 0xc0, 0x4f, 0xd8, 0xdb, 0xf7 } }; - - IDsObjectPicker *picker; - - if (SUCCEEDED(CoCreateInstance( - &CLSID_DsObjectPicker_I, - NULL, - CLSCTX_INPROC_SERVER, - &IID_IDsObjectPicker_I, - &picker - ))) - { - return picker; - } - else - { - return NULL; - } -} - -VOID PhFreeDsObjectPickerDialog( - _In_ PVOID PickerDialog - ) -{ - IDsObjectPicker_Release((IDsObjectPicker *)PickerDialog); -} - -PVOID PhCreateDsObjectPickerDialog( - _In_ ULONG Flags - ) -{ - IDsObjectPicker *picker; - DSOP_INIT_INFO initInfo; - DSOP_SCOPE_INIT_INFO scopeInit[1]; - - picker = PhpCreateDsObjectPicker(); - - if (!picker) - return NULL; - - memset(scopeInit, 0, sizeof(scopeInit)); - - scopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); - scopeInit[0].flType = DSOP_SCOPE_TYPE_TARGET_COMPUTER; - scopeInit[0].flScope = - DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT | - DSOP_SCOPE_FLAG_WANT_SID_PATH | - DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | - DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; - scopeInit[0].FilterFlags.Uplevel.flBothModes = - DSOP_FILTER_INCLUDE_ADVANCED_VIEW | - DSOP_FILTER_USERS | - DSOP_FILTER_BUILTIN_GROUPS | - DSOP_FILTER_WELL_KNOWN_PRINCIPALS; - scopeInit[0].FilterFlags.flDownlevel = - DSOP_DOWNLEVEL_FILTER_USERS | - DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS | - DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS | - DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS; - - memset(&initInfo, 0, sizeof(DSOP_INIT_INFO)); - initInfo.cbSize = sizeof(DSOP_INIT_INFO); - initInfo.pwzTargetComputer = NULL; - initInfo.cDsScopeInfos = 1; - initInfo.aDsScopeInfos = scopeInit; - initInfo.flOptions = DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK; - - if (Flags & PH_DSPICK_MULTISELECT) - initInfo.flOptions |= DSOP_FLAG_MULTISELECT; - - if (!SUCCEEDED(IDsObjectPicker_Initialize(picker, &initInfo))) - { - IDsObjectPicker_Release(picker); - return NULL; - } - - return picker; -} - -PDS_SELECTION_LIST PhpGetDsSelectionList( - _In_ IDataObject *Selections - ) -{ - FORMATETC format; - STGMEDIUM medium; - - format.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(L"CFSTR_DSOP_DS_SELECTION_LIST"); - format.ptd = NULL; - format.dwAspect = -1; - format.lindex = -1; - format.tymed = TYMED_HGLOBAL; - - if (SUCCEEDED(IDataObject_GetData(Selections, &format, &medium))) - { - if (medium.tymed != TYMED_HGLOBAL) - return NULL; - - return (PDS_SELECTION_LIST)GlobalLock(medium.hGlobal); - } - else - { - return NULL; - } -} - -BOOLEAN PhShowDsObjectPickerDialog( - _In_ HWND hWnd, - _In_ PVOID PickerDialog, - _Out_ PPH_DSPICK_OBJECTS *Objects - ) -{ - IDsObjectPicker *picker; - IDataObject *dataObject; - PDS_SELECTION_LIST selections; - PPH_DSPICK_OBJECTS objects; - ULONG i; - - picker = (IDsObjectPicker *)PickerDialog; - - if (!SUCCEEDED(IDsObjectPicker_InvokeDialog(picker, hWnd, &dataObject))) - return FALSE; - - if (!dataObject) - return FALSE; - - selections = PhpGetDsSelectionList(dataObject); - IDataObject_Release(dataObject); - - if (!selections) - return FALSE; - - objects = PhAllocate( - FIELD_OFFSET(PH_DSPICK_OBJECTS, Objects) + - selections->cItems * sizeof(PH_DSPICK_OBJECT) - ); - - objects->NumberOfObjects = selections->cItems; - - for (i = 0; i < selections->cItems; i++) - { - PDS_SELECTION selection; - PSID sid; - PH_STRINGREF path; - PH_STRINGREF prefix; - - selection = &selections->aDsSelection[i]; - - objects->Objects[i].Name = PhCreateString(selection->pwzName); - objects->Objects[i].Sid = NULL; - - if (selection->pwzADsPath && selection->pwzADsPath[0] != 0) - { - PhInitializeStringRef(&path, selection->pwzADsPath); - PhInitializeStringRef(&prefix, L"LDAP://" at end - - sid = PhAllocate(path.Length / sizeof(WCHAR) / 2); - - if (PhHexStringToBuffer(&path, (PUCHAR)sid)) - { - if (RtlValidSid(sid)) - objects->Objects[i].Sid = sid; - else - PhFree(sid); - } - else - { - PhFree(sid); - } - } - } - else - { - // Try to get the SID. - PhLookupName(&objects->Objects[i].Name->sr, &objects->Objects[i].Sid, NULL, NULL); - } - } - - *Objects = objects; - - return TRUE; -} - -VOID PhFreeDsObjectPickerObjects( - _In_ PPH_DSPICK_OBJECTS Objects - ) -{ - ULONG i; - - for (i = 0; i < Objects->NumberOfObjects; i++) - { - PhDereferenceObject(Objects->Objects[i].Name); - - if (Objects->Objects[i].Sid) - PhFree(Objects->Objects[i].Sid); - } - - PhFree(Objects); -} +/* + * Process Hacker - + * DS object picker wrapper + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#include +#include + +//#define IDataObject_AddRef(This) ((This)->lpVtbl->AddRef(This)) +//#define IDataObject_Release(This) ((This)->lpVtbl->Release(This)) +//#define IDataObject_GetData(This, pformatetcIn, pmedium) ((This)->lpVtbl->GetData(This, pformatetcIn, pmedium)) + +#define IDsObjectPicker_QueryInterface(This, riid, ppvObject) ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IDsObjectPicker_AddRef(This) ((This)->lpVtbl->AddRef(This)) +#define IDsObjectPicker_Release(This) ((This)->lpVtbl->Release(This)) +#define IDsObjectPicker_Initialize(This, pInitInfo) ((This)->lpVtbl->Initialize(This, pInitInfo)) +#define IDsObjectPicker_InvokeDialog(This, hwndParent, ppdoSelections) ((This)->lpVtbl->InvokeDialog(This, hwndParent, ppdoSelections)) + +IDsObjectPicker *PhpCreateDsObjectPicker( + VOID + ) +{ + static CLSID CLSID_DsObjectPicker_I = { 0x17d6ccd8, 0x3b7b, 0x11d2, { 0xb9, 0xe0, 0x00, 0xc0, 0x4f, 0xd8, 0xdb, 0xf7 } }; + static IID IID_IDsObjectPicker_I = { 0x0c87e64e, 0x3b7a, 0x11d2, { 0xb9, 0xe0, 0x00, 0xc0, 0x4f, 0xd8, 0xdb, 0xf7 } }; + + IDsObjectPicker *picker; + + if (SUCCEEDED(CoCreateInstance( + &CLSID_DsObjectPicker_I, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IDsObjectPicker_I, + &picker + ))) + { + return picker; + } + else + { + return NULL; + } +} + +VOID PhFreeDsObjectPickerDialog( + _In_ PVOID PickerDialog + ) +{ + IDsObjectPicker_Release((IDsObjectPicker *)PickerDialog); +} + +PVOID PhCreateDsObjectPickerDialog( + _In_ ULONG Flags + ) +{ + IDsObjectPicker *picker; + DSOP_INIT_INFO initInfo; + DSOP_SCOPE_INIT_INFO scopeInit[1]; + + picker = PhpCreateDsObjectPicker(); + + if (!picker) + return NULL; + + memset(scopeInit, 0, sizeof(scopeInit)); + + scopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); + scopeInit[0].flType = DSOP_SCOPE_TYPE_TARGET_COMPUTER; + scopeInit[0].flScope = + DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT | + DSOP_SCOPE_FLAG_WANT_SID_PATH | + DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | + DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; + scopeInit[0].FilterFlags.Uplevel.flBothModes = + DSOP_FILTER_INCLUDE_ADVANCED_VIEW | + DSOP_FILTER_USERS | + DSOP_FILTER_BUILTIN_GROUPS | + DSOP_FILTER_WELL_KNOWN_PRINCIPALS; + scopeInit[0].FilterFlags.flDownlevel = + DSOP_DOWNLEVEL_FILTER_USERS | + DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS | + DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS | + DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS; + + memset(&initInfo, 0, sizeof(DSOP_INIT_INFO)); + initInfo.cbSize = sizeof(DSOP_INIT_INFO); + initInfo.pwzTargetComputer = NULL; + initInfo.cDsScopeInfos = 1; + initInfo.aDsScopeInfos = scopeInit; + initInfo.flOptions = DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK; + + if (Flags & PH_DSPICK_MULTISELECT) + initInfo.flOptions |= DSOP_FLAG_MULTISELECT; + + if (!SUCCEEDED(IDsObjectPicker_Initialize(picker, &initInfo))) + { + IDsObjectPicker_Release(picker); + return NULL; + } + + return picker; +} + +PDS_SELECTION_LIST PhpGetDsSelectionList( + _In_ IDataObject *Selections + ) +{ + FORMATETC format; + STGMEDIUM medium; + + format.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(L"CFSTR_DSOP_DS_SELECTION_LIST"); + format.ptd = NULL; + format.dwAspect = -1; + format.lindex = -1; + format.tymed = TYMED_HGLOBAL; + + if (SUCCEEDED(IDataObject_GetData(Selections, &format, &medium))) + { + if (medium.tymed != TYMED_HGLOBAL) + return NULL; + + return (PDS_SELECTION_LIST)GlobalLock(medium.hGlobal); + } + else + { + return NULL; + } +} + +BOOLEAN PhShowDsObjectPickerDialog( + _In_ HWND hWnd, + _In_ PVOID PickerDialog, + _Out_ PPH_DSPICK_OBJECTS *Objects + ) +{ + IDsObjectPicker *picker; + IDataObject *dataObject; + PDS_SELECTION_LIST selections; + PPH_DSPICK_OBJECTS objects; + ULONG i; + + picker = (IDsObjectPicker *)PickerDialog; + + if (!SUCCEEDED(IDsObjectPicker_InvokeDialog(picker, hWnd, &dataObject))) + return FALSE; + + if (!dataObject) + return FALSE; + + selections = PhpGetDsSelectionList(dataObject); + IDataObject_Release(dataObject); + + if (!selections) + return FALSE; + + objects = PhAllocate( + FIELD_OFFSET(PH_DSPICK_OBJECTS, Objects) + + selections->cItems * sizeof(PH_DSPICK_OBJECT) + ); + + objects->NumberOfObjects = selections->cItems; + + for (i = 0; i < selections->cItems; i++) + { + PDS_SELECTION selection; + PSID sid; + PH_STRINGREF path; + PH_STRINGREF prefix; + + selection = &selections->aDsSelection[i]; + + objects->Objects[i].Name = PhCreateString(selection->pwzName); + objects->Objects[i].Sid = NULL; + + if (selection->pwzADsPath && selection->pwzADsPath[0] != 0) + { + PhInitializeStringRef(&path, selection->pwzADsPath); + PhInitializeStringRef(&prefix, L"LDAP://" at end + + sid = PhAllocate(path.Length / sizeof(WCHAR) / 2); + + if (PhHexStringToBuffer(&path, (PUCHAR)sid)) + { + if (RtlValidSid(sid)) + objects->Objects[i].Sid = sid; + else + PhFree(sid); + } + else + { + PhFree(sid); + } + } + } + else + { + // Try to get the SID. + PhLookupName(&objects->Objects[i].Name->sr, &objects->Objects[i].Sid, NULL, NULL); + } + } + + *Objects = objects; + + return TRUE; +} + +VOID PhFreeDsObjectPickerObjects( + _In_ PPH_DSPICK_OBJECTS Objects + ) +{ + ULONG i; + + for (i = 0; i < Objects->NumberOfObjects; i++) + { + PhDereferenceObject(Objects->Objects[i].Name); + + if (Objects->Objects[i].Sid) + PhFree(Objects->Objects[i].Sid); + } + + PhFree(Objects); +} diff --git a/phlib/emenu.c b/phlib/emenu.c index 128b1757692d..5d79ccc57245 100644 --- a/phlib/emenu.c +++ b/phlib/emenu.c @@ -1,857 +1,865 @@ -/* - * Process Hacker - - * extended menus - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -static const PH_FLAG_MAPPING EMenuTypeMappings[] = -{ - { PH_EMENU_MENUBARBREAK, MFT_MENUBARBREAK }, - { PH_EMENU_MENUBREAK, MFT_MENUBREAK }, - { PH_EMENU_RADIOCHECK, MFT_RADIOCHECK } -}; - -static const PH_FLAG_MAPPING EMenuStateMappings[] = -{ - { PH_EMENU_CHECKED, MFS_CHECKED }, - { PH_EMENU_DEFAULT, MFS_DEFAULT }, - { PH_EMENU_DISABLED, MFS_DISABLED }, - { PH_EMENU_HIGHLIGHT, MFS_HILITE } -}; - -PPH_EMENU_ITEM PhAllocateEMenuItem( - VOID - ) -{ - PPH_EMENU_ITEM item; - - item = PhAllocate(sizeof(PH_EMENU_ITEM)); - memset(item, 0, sizeof(PH_EMENU_ITEM)); - - return item; -} - -/** - * Creates a menu item. - * - * \param Flags A combination of the following: - * \li \c PH_EMENU_DISABLED The menu item is greyed and cannot be selected. - * \li \c PH_EMENU_CHECKED A check mark is displayed. - * \li \c PH_EMENU_HIGHLIGHT The menu item is highlighted. - * \li \c PH_EMENU_MENUBARBREAK Places the menu item in a new column, separated by a vertical line. - * \li \c PH_EMENU_MENUBREAK Places the menu item in a new column, with no vertical line. - * \li \c PH_EMENU_DEFAULT The menu item is displayed as the default item. This causes the text to - * be bolded. - * \li \c PH_EMENU_RADIOCHECK Uses a radio-button mark instead of a check mark. - * \param Id A unique identifier for the menu item. - * \param Text The text displayed for the menu item. - * \param Bitmap A bitmap image for the menu item. - * \param Context A user-defined value. - */ -PPH_EMENU_ITEM PhCreateEMenuItem( - _In_ ULONG Flags, - _In_ ULONG Id, - _In_ PWSTR Text, - _In_opt_ HBITMAP Bitmap, - _In_opt_ PVOID Context - ) -{ - PPH_EMENU_ITEM item; - - item = PhAllocateEMenuItem(); - - item->Flags = Flags; - item->Id = Id; - item->Text = Text; - item->Bitmap = Bitmap; - - item->Context = Context; - - return item; -} - -/** - * Frees resources used by a menu item and its children. - * - * \param Item The menu item. - * - * \remarks The menu item is NOT automatically removed from its parent. It is safe to call this - * function while enumerating menu items. - */ -VOID PhpDestroyEMenuItem( - _In_ PPH_EMENU_ITEM Item - ) -{ - if (Item->DeleteFunction) - Item->DeleteFunction(Item); - - if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text) - PhFree(Item->Text); - if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap) - DeleteObject(Item->Bitmap); - - if (Item->Items) - { - ULONG i; - - for (i = 0; i < Item->Items->Count; i++) - { - PhpDestroyEMenuItem(Item->Items->Items[i]); - } - - PhDereferenceObject(Item->Items); - } - - PhFree(Item); -} - -/** - * Frees resources used by a menu item and its children. - * - * \param Item The menu item. - * - * \remarks The menu item is automatically removed from its parent. - */ -VOID PhDestroyEMenuItem( - _In_ PPH_EMENU_ITEM Item - ) -{ - // Remove the item from its parent, if it has one. - if (Item->Parent) - PhRemoveEMenuItem(NULL, Item, -1); - - PhpDestroyEMenuItem(Item); -} - -/** - * Finds a child menu item. - * - * \param Item The parent menu item. - * \param Flags A combination of the following: - * \li \c PH_EMENU_FIND_DESCEND Searches recursively within child menu items. - * \li \c PH_EMENU_FIND_STARTSWITH Performs a partial text search instead of an exact search. - * \li \c PH_EMENU_FIND_LITERAL Performs a literal search instead of ignoring prefix characters - * (ampersands). - * \param Text The text of the menu item to find. If NULL, the text is ignored. - * \param Id The identifier of the menu item to find. If 0, the identifier is ignored. - * - * \return The found menu item, or NULL if the menu item could not be found. - */ -PPH_EMENU_ITEM PhFindEMenuItem( - _In_ PPH_EMENU_ITEM Item, - _In_ ULONG Flags, - _In_opt_ PWSTR Text, - _In_opt_ ULONG Id - ) -{ - return PhFindEMenuItemEx(Item, Flags, Text, Id, NULL, NULL); -} - -/** - * Finds a child menu item. - * - * \param Item The parent menu item. - * \param Flags A combination of the following: - * \li \c PH_EMENU_FIND_DESCEND Searches recursively within child menu items. - * \li \c PH_EMENU_FIND_STARTSWITH Performs a partial text search instead of an exact search. - * \li \c PH_EMENU_FIND_LITERAL Performs a literal search instead of ignoring prefix characters - * (ampersands). - * \param Text The text of the menu item to find. If NULL, the text is ignored. - * \param Id The identifier of the menu item to find. If 0, the identifier is ignored. - * \param FoundParent A variable which receives the parent of the found menu item. - * \param FoundIndex A variable which receives the index of the found menu item. - * - * \return The found menu item, or NULL if the menu item could not be found. - */ -PPH_EMENU_ITEM PhFindEMenuItemEx( - _In_ PPH_EMENU_ITEM Item, - _In_ ULONG Flags, - _In_opt_ PWSTR Text, - _In_opt_ ULONG Id, - _Out_opt_ PPH_EMENU_ITEM *FoundParent, - _Out_opt_ PULONG FoundIndex - ) -{ - PH_STRINGREF searchText; - ULONG i; - PPH_EMENU_ITEM item; - - if (!Item->Items) - return NULL; - - if (Text && (Flags & PH_EMENU_FIND_LITERAL)) - PhInitializeStringRef(&searchText, Text); - - for (i = 0; i < Item->Items->Count; i++) - { - item = Item->Items->Items[i]; - - if (Text) - { - if (Flags & PH_EMENU_FIND_LITERAL) - { - PH_STRINGREF text; - - PhInitializeStringRef(&text, item->Text); - - if (Flags & PH_EMENU_FIND_STARTSWITH) - { - if (PhStartsWithStringRef(&text, &searchText, TRUE)) - goto FoundItemHere; - } - else - { - if (PhEqualStringRef(&text, &searchText, TRUE)) - goto FoundItemHere; - } - } - else - { - if (PhCompareUnicodeStringZIgnoreMenuPrefix(Text, item->Text, - TRUE, !!(Flags & PH_EMENU_FIND_STARTSWITH)) == 0) - goto FoundItemHere; - } - } - - if (Id && item->Id == Id) - goto FoundItemHere; - - if (Flags & PH_EMENU_FIND_DESCEND) - { - PPH_EMENU_ITEM foundItem; - PPH_EMENU_ITEM foundParent; - ULONG foundIndex; - - foundItem = PhFindEMenuItemEx(item, Flags, Text, Id, &foundParent, &foundIndex); - - if (foundItem) - { - if (FoundParent) - *FoundParent = foundParent; - if (FoundIndex) - *FoundIndex = foundIndex; - - return foundItem; - } - } - } - - return NULL; - -FoundItemHere: - if (FoundParent) - *FoundParent = Item; - if (FoundIndex) - *FoundIndex = i; - - return item; -} - -/** - * Determines the index of a menu item. - * - * \param Parent The parent menu item. - * \param Item The child menu item. - * - * \return The index of the menu item, or -1 if the menu item was not found in the parent menu item. - */ -ULONG PhIndexOfEMenuItem( - _In_ PPH_EMENU_ITEM Parent, - _In_ PPH_EMENU_ITEM Item - ) -{ - if (!Parent->Items) - return -1; - - return PhFindItemList(Parent->Items, Item); -} - -/** - * Inserts a menu item into a parent menu item. - * - * \param Parent The parent menu item. - * \param Item The menu item to insert. - * \param Index The index at which to insert the menu item. If the index is too large, the menu item - * is inserted at the last position. - */ -VOID PhInsertEMenuItem( - _Inout_ PPH_EMENU_ITEM Parent, - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG Index - ) -{ - // Remove the item from its old parent if it has one. - if (Item->Parent) - PhRemoveEMenuItem(Item->Parent, Item, 0); - - if (!Parent->Items) - Parent->Items = PhCreateList(16); - - if (Index > Parent->Items->Count) - Index = Parent->Items->Count; - - if (Index == -1) - PhAddItemList(Parent->Items, Item); - else - PhInsertItemList(Parent->Items, Index, Item); - - Item->Parent = Parent; -} - -/** - * Removes a menu item from its parent. - * - * \param Parent The parent menu item. If \a Item is NULL, this parameter must be specified. - * \param Item The child menu item. This may be NULL if \a Index is specified. - * \param Index The index of the menu item to remove. If \a Item is specified, this parameter is - * ignored. - */ -BOOLEAN PhRemoveEMenuItem( - _Inout_opt_ PPH_EMENU_ITEM Parent, - _In_opt_ PPH_EMENU_ITEM Item, - _In_opt_ ULONG Index - ) -{ - if (Item) - { - if (!Parent) - Parent = Item->Parent; - if (!Parent->Items) - return FALSE; - - Index = PhFindItemList(Parent->Items, Item); - - if (Index == -1) - return FALSE; - } - else - { - if (!Parent) - return FALSE; - if (!Parent->Items) - return FALSE; - } - - Item = Parent->Items->Items[Index]; - PhRemoveItemList(Parent->Items, Index); - Item->Parent = NULL; - - return TRUE; -} - -/** - * Removes all children from a menu item. - * - * \param Parent The parent menu item. - */ -VOID PhRemoveAllEMenuItems( - _Inout_ PPH_EMENU_ITEM Parent - ) -{ - ULONG i; - - if (!Parent->Items) - return; - - for (i = 0; i < Parent->Items->Count; i++) - { - PhpDestroyEMenuItem(Parent->Items->Items[i]); - } - - PhClearList(Parent->Items); -} - -/** - * Creates a root menu. - */ -PPH_EMENU PhCreateEMenu( - VOID - ) -{ - PPH_EMENU menu; - - menu = PhAllocate(sizeof(PH_EMENU)); - memset(menu, 0, sizeof(PH_EMENU)); - menu->Items = PhCreateList(16); - - return menu; -} - -/** - * Frees resources used by a root menu and its children. - * - * \param Menu A root menu. - */ -VOID PhDestroyEMenu( - _In_ PPH_EMENU Menu - ) -{ - ULONG i; - - for (i = 0; i < Menu->Items->Count; i++) - { - PhpDestroyEMenuItem(Menu->Items->Items[i]); - } - - PhDereferenceObject(Menu->Items); - PhFree(Menu); -} - -/** - * Initializes a data structure containing additional information resulting from a call to - * PhEMenuToHMenu(). - */ -VOID PhInitializeEMenuData( - _Out_ PPH_EMENU_DATA Data - ) -{ - Data->IdToItem = PhCreateList(16); -} - -/** - * Frees resources used by a data structure initialized by PhInitializeEMenuData(). - */ -VOID PhDeleteEMenuData( - _Inout_ PPH_EMENU_DATA Data - ) -{ - PhDereferenceObject(Data->IdToItem); -} - -/** - * Converts an EMENU to a Windows menu object. - * - * \param Menu The menu item to convert. - * \param Flags A combination of the following: - * \li \c PH_EMENU_CONVERT_ID Automatically assigns a unique identifier to each converted menu item. - * The resulting mappings are placed in \a Data. - * \param Data Additional data resulting from the conversion. The data structure must be initialized - * by PhInitializeEMenuData() prior to calling this function. - * - * \return A menu handle. The menu object must be destroyed using DestroyMenu() when it is no longer - * needed. - */ -HMENU PhEMenuToHMenu( - _In_ PPH_EMENU_ITEM Menu, - _In_ ULONG Flags, - _Inout_opt_ PPH_EMENU_DATA Data - ) -{ - HMENU menuHandle; - - menuHandle = CreatePopupMenu(); - - if (!menuHandle) - return NULL; - - PhEMenuToHMenu2(menuHandle, Menu, Flags, Data); - - if (!(Menu->Flags & PH_EMENU_SEPARATECHECKSPACE)) - { - MENUINFO menuInfo; - - memset(&menuInfo, 0, sizeof(MENUINFO)); - menuInfo.cbSize = sizeof(MENUINFO); - menuInfo.fMask = MIM_STYLE; - menuInfo.dwStyle = MNS_CHECKORBMP; - SetMenuInfo(menuHandle, &menuInfo); - } - - return menuHandle; -} - -/** - * Converts an EMENU to a Windows menu object. - * - * \param MenuHandle A handle to a Windows menu object. - * \param Menu The menu item to convert. The items are appended to \a MenuHandle. - * \param Flags A combination of the following: - * \li \c PH_EMENU_CONVERT_ID Automatically assigns a unique identifier to each converted menu item. - * The resulting mappings are placed in \a Data. - * \param Data Additional data resulting from the conversion. The data structure must be initialized - * by PhInitializeEMenuData() prior to calling this function. - */ -VOID PhEMenuToHMenu2( - _In_ HMENU MenuHandle, - _In_ PPH_EMENU_ITEM Menu, - _In_ ULONG Flags, - _Inout_opt_ PPH_EMENU_DATA Data - ) -{ - ULONG i; - PPH_EMENU_ITEM item; - MENUITEMINFO menuItemInfo; - - for (i = 0; i < Menu->Items->Count; i++) - { - item = Menu->Items->Items[i]; - - memset(&menuItemInfo, 0, sizeof(MENUITEMINFO)); - menuItemInfo.cbSize = sizeof(MENUITEMINFO); - - // Type - - menuItemInfo.fMask = MIIM_FTYPE | MIIM_STATE; - - if (item->Flags & PH_EMENU_SEPARATOR) - { - menuItemInfo.fType = MFT_SEPARATOR; - } - else - { - menuItemInfo.fType = MFT_STRING; - menuItemInfo.fMask |= MIIM_STRING; - menuItemInfo.dwTypeData = item->Text; - } - - PhMapFlags1( - &menuItemInfo.fType, - item->Flags, - EMenuTypeMappings, - sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING) - ); - - // Bitmap - - if (item->Bitmap) - { - menuItemInfo.fMask |= MIIM_BITMAP; - menuItemInfo.hbmpItem = item->Bitmap; - } - - // Id - - if (Flags & PH_EMENU_CONVERT_ID) - { - ULONG id; - - if (Data) - { - PhAddItemList(Data->IdToItem, item); - id = Data->IdToItem->Count; - - menuItemInfo.fMask |= MIIM_ID; - menuItemInfo.wID = id; - } - } - else - { - if (item->Id) - { - menuItemInfo.fMask |= MIIM_ID; - menuItemInfo.wID = item->Id; - } - } - - // State - - PhMapFlags1( - &menuItemInfo.fState, - item->Flags, - EMenuStateMappings, - sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING) - ); - - // Context - - menuItemInfo.fMask |= MIIM_DATA; - menuItemInfo.dwItemData = (ULONG_PTR)item; - - // Submenu - - if (item->Items && item->Items->Count != 0) - { - menuItemInfo.fMask |= MIIM_SUBMENU; - menuItemInfo.hSubMenu = PhEMenuToHMenu(item, Flags, Data); - } - - InsertMenuItem(MenuHandle, MAXINT, TRUE, &menuItemInfo); - } -} - -/** - * Converts a Windows menu object to an EMENU. - * - * \param MenuItem The menu item in which the converted menu items will be placed. - * \param MenuHandle A menu handle. - */ -VOID PhHMenuToEMenuItem( - _Inout_ PPH_EMENU_ITEM MenuItem, - _In_ HMENU MenuHandle - ) -{ - ULONG i; - ULONG count; - - count = GetMenuItemCount(MenuHandle); - - if (count != -1) - { - for (i = 0; i < count; i++) - { - MENUITEMINFO menuItemInfo; - WCHAR buffer[256]; - PPH_EMENU_ITEM menuItem; - - menuItemInfo.cbSize = sizeof(menuItemInfo); - menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU; - menuItemInfo.cch = sizeof(buffer) / sizeof(WCHAR); - menuItemInfo.dwTypeData = buffer; - - if (!GetMenuItemInfo(MenuHandle, i, TRUE, &menuItemInfo)) - continue; - - menuItem = PhCreateEMenuItem( - PH_EMENU_TEXT_OWNED, - menuItemInfo.wID, - PhDuplicateStringZ(buffer), - NULL, - NULL - ); - - if (menuItemInfo.fType & MFT_SEPARATOR) - menuItem->Flags |= PH_EMENU_SEPARATOR; - - PhMapFlags2( - &menuItem->Flags, - menuItemInfo.fType, - EMenuTypeMappings, - sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING) - ); - PhMapFlags2( - &menuItem->Flags, - menuItemInfo.fState, - EMenuStateMappings, - sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING) - ); - - if (menuItemInfo.hSubMenu) - PhHMenuToEMenuItem(menuItem, menuItemInfo.hSubMenu); - - PhInsertEMenuItem(MenuItem, menuItem, -1); - } - } -} - -/** - * Loads a menu resource and converts it to an EMENU. - * - * \param MenuItem The menu item in which the converted menu items will be placed. - * \param InstanceHandle The module containing the menu resource. - * \param Resource The resource identifier. - * \param SubMenuIndex The index of the sub menu to use, or -1 to use the root menu. - */ -VOID PhLoadResourceEMenuItem( - _Inout_ PPH_EMENU_ITEM MenuItem, - _In_ HINSTANCE InstanceHandle, - _In_ PWSTR Resource, - _In_ ULONG SubMenuIndex - ) -{ - HMENU menu; - HMENU realMenu; - - menu = LoadMenu(InstanceHandle, Resource); - - if (SubMenuIndex != -1) - realMenu = GetSubMenu(menu, SubMenuIndex); - else - realMenu = menu; - - PhHMenuToEMenuItem(MenuItem, realMenu); - - DestroyMenu(menu); -} - -/** - * Displays a menu. - * - * \param Menu A menu. - * \param WindowHandle The window that owns the popup menu. - * \param Flags A combination of the following: - * \li \c PH_EMENU_SHOW_SEND_COMMAND A WM_COMMAND message is sent to the window when the user clicks - * on a menu item. - * \li \c PH_EMENU_SHOW_LEFTRIGHT The user can select menu items with both the left and right mouse - * buttons. - * \param Align The alignment of the menu. - * \param X The horizontal location of the menu. - * \param Y The vertical location of the menu. - * - * \return The selected menu item, or NULL if the menu was cancelled. - */ -PPH_EMENU_ITEM PhShowEMenu( - _In_ PPH_EMENU Menu, - _In_ HWND WindowHandle, - _In_ ULONG Flags, - _In_ ULONG Align, - _In_ ULONG X, - _In_ ULONG Y - ) -{ - PPH_EMENU_ITEM selectedItem; - ULONG result; - ULONG flags; - PH_EMENU_DATA data; - HMENU popupMenu; - - selectedItem = NULL; - flags = TPM_RETURNCMD | TPM_NONOTIFY; - - // Flags - - if (Flags & PH_EMENU_SHOW_LEFTRIGHT) - flags |= TPM_RIGHTBUTTON; - else - flags |= TPM_LEFTBUTTON; - - // Align - - if (Align & PH_ALIGN_LEFT) - flags |= TPM_LEFTALIGN; - else if (Align & PH_ALIGN_RIGHT) - flags |= TPM_RIGHTALIGN; - else - flags |= TPM_CENTERALIGN; - - if (Align & PH_ALIGN_TOP) - flags |= TPM_TOPALIGN; - else if (Align & PH_ALIGN_BOTTOM) - flags |= TPM_BOTTOMALIGN; - else - flags |= TPM_VCENTERALIGN; - - PhInitializeEMenuData(&data); - - if (popupMenu = PhEMenuToHMenu(Menu, PH_EMENU_CONVERT_ID, &data)) - { - result = TrackPopupMenu( - popupMenu, - flags, - X, - Y, - 0, - WindowHandle, - NULL - ); - - if (result != 0) - { - selectedItem = data.IdToItem->Items[result - 1]; - } - - DestroyMenu(popupMenu); - } - - PhDeleteEMenuData(&data); - - if ((Flags & PH_EMENU_SHOW_SEND_COMMAND) && selectedItem && selectedItem->Id != 0) - SendMessage(WindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, 0), 0); - - return selectedItem; -} - -/** - * Sets the flags of a menu item. - * - * \param Item The parent menu item. - * \param Id The identifier of the child menu item. - * \param Mask The flags to modify. - * \param Value The new value of the flags. - */ -BOOLEAN PhSetFlagsEMenuItem( - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG Id, - _In_ ULONG Mask, - _In_ ULONG Value - ) -{ - PPH_EMENU_ITEM item; - - item = PhFindEMenuItem(Item, PH_EMENU_FIND_DESCEND, NULL, Id); - - if (item) - { - item->Flags &= ~Mask; - item->Flags |= Value; - - return TRUE; - } - else - { - return FALSE; - } -} - -/** - * Sets flags for all children of a menu item. - * - * \param Item The parent menu item. - * \param Mask The flags to modify. - * \param Value The new value of the flags. - */ -VOID PhSetFlagsAllEMenuItems( - _In_ PPH_EMENU_ITEM Item, - _In_ ULONG Mask, - _In_ ULONG Value - ) -{ - ULONG i; - - for (i = 0; i < Item->Items->Count; i++) - { - PPH_EMENU_ITEM item = Item->Items->Items[i]; - - item->Flags &= ~Mask; - item->Flags |= Value; - } -} - -VOID PhModifyEMenuItem( - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG ModifyFlags, - _In_ ULONG OwnedFlags, - _In_opt_ PWSTR Text, - _In_opt_ HBITMAP Bitmap - ) -{ - if (ModifyFlags & PH_EMENU_MODIFY_TEXT) - { - if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text) - PhFree(Item->Text); - - Item->Text = Text; - Item->Flags &= ~PH_EMENU_TEXT_OWNED; - Item->Flags |= OwnedFlags & PH_EMENU_TEXT_OWNED; - } - - if (ModifyFlags & PH_EMENU_MODIFY_BITMAP) - { - if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap) - DeleteObject(Item->Bitmap); - - Item->Bitmap = Bitmap; - Item->Flags &= ~PH_EMENU_BITMAP_OWNED; - Item->Flags |= OwnedFlags & PH_EMENU_BITMAP_OWNED; - } -} +/* + * Process Hacker - + * extended menus + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +static const PH_FLAG_MAPPING EMenuTypeMappings[] = +{ + { PH_EMENU_MENUBARBREAK, MFT_MENUBARBREAK }, + { PH_EMENU_MENUBREAK, MFT_MENUBREAK }, + { PH_EMENU_RADIOCHECK, MFT_RADIOCHECK } +}; + +static const PH_FLAG_MAPPING EMenuStateMappings[] = +{ + { PH_EMENU_CHECKED, MFS_CHECKED }, + { PH_EMENU_DEFAULT, MFS_DEFAULT }, + { PH_EMENU_DISABLED, MFS_DISABLED }, + { PH_EMENU_HIGHLIGHT, MFS_HILITE } +}; + +/** + * Creates a menu item. + * + * \param Flags A combination of the following: + * \li \c PH_EMENU_DISABLED The menu item is greyed and cannot be selected. + * \li \c PH_EMENU_CHECKED A check mark is displayed. + * \li \c PH_EMENU_HIGHLIGHT The menu item is highlighted. + * \li \c PH_EMENU_MENUBARBREAK Places the menu item in a new column, separated by a vertical line. + * \li \c PH_EMENU_MENUBREAK Places the menu item in a new column, with no vertical line. + * \li \c PH_EMENU_DEFAULT The menu item is displayed as the default item. This causes the text to + * be bolded. + * \li \c PH_EMENU_RADIOCHECK Uses a radio-button mark instead of a check mark. + * \param Id A unique identifier for the menu item. + * \param Text The text displayed for the menu item. + * \param Bitmap A bitmap image for the menu item. + * \param Context A user-defined value. + */ +PPH_EMENU_ITEM PhCreateEMenuItem( + _In_ ULONG Flags, + _In_ ULONG Id, + _In_opt_ PWSTR Text, + _In_opt_ HBITMAP Bitmap, + _In_opt_ PVOID Context + ) +{ + PPH_EMENU_ITEM item; + + item = PhAllocate(sizeof(PH_EMENU_ITEM)); + memset(item, 0, sizeof(PH_EMENU_ITEM)); + + item->Flags = Flags; + item->Id = Id; + item->Text = Text; + item->Bitmap = Bitmap; + item->Context = Context; + + return item; +} + +/** + * Frees resources used by a menu item and its children. + * + * \param Item The menu item. + * + * \remarks The menu item is NOT automatically removed from its parent. It is safe to call this + * function while enumerating menu items. + */ +VOID PhpDestroyEMenuItem( + _In_ PPH_EMENU_ITEM Item + ) +{ + if (Item->DeleteFunction) + Item->DeleteFunction(Item); + + if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text) + PhFree(Item->Text); + if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap) + DeleteBitmap(Item->Bitmap); + + if (Item->Items) + { + for (ULONG i = 0; i < Item->Items->Count; i++) + PhpDestroyEMenuItem(Item->Items->Items[i]); + + PhDereferenceObject(Item->Items); + } + + PhFree(Item); +} + +/** + * Frees resources used by a menu item and its children. + * + * \param Item The menu item. + * + * \remarks The menu item is automatically removed from its parent. + */ +VOID PhDestroyEMenuItem( + _In_ PPH_EMENU_ITEM Item + ) +{ + // Remove the item from its parent, if it has one. + if (Item->Parent) + PhRemoveEMenuItem(NULL, Item, ULONG_MAX); + + PhpDestroyEMenuItem(Item); +} + +/** + * Finds a child menu item. + * + * \param Item The parent menu item. + * \param Flags A combination of the following: + * \li \c PH_EMENU_FIND_DESCEND Searches recursively within child menu items. + * \li \c PH_EMENU_FIND_STARTSWITH Performs a partial text search instead of an exact search. + * \li \c PH_EMENU_FIND_LITERAL Performs a literal search instead of ignoring prefix characters + * (ampersands). + * \param Text The text of the menu item to find. If NULL, the text is ignored. + * \param Id The identifier of the menu item to find. If 0, the identifier is ignored. + * + * \return The found menu item, or NULL if the menu item could not be found. + */ +PPH_EMENU_ITEM PhFindEMenuItem( + _In_ PPH_EMENU_ITEM Item, + _In_ ULONG Flags, + _In_opt_ PWSTR Text, + _In_opt_ ULONG Id + ) +{ + return PhFindEMenuItemEx(Item, Flags, Text, Id, NULL, NULL); +} + +/** + * Finds a child menu item. + * + * \param Item The parent menu item. + * \param Flags A combination of the following: + * \li \c PH_EMENU_FIND_DESCEND Searches recursively within child menu items. + * \li \c PH_EMENU_FIND_STARTSWITH Performs a partial text search instead of an exact search. + * \li \c PH_EMENU_FIND_LITERAL Performs a literal search instead of ignoring prefix characters + * (ampersands). + * \param Text The text of the menu item to find. If NULL, the text is ignored. + * \param Id The identifier of the menu item to find. If 0, the identifier is ignored. + * \param FoundParent A variable which receives the parent of the found menu item. + * \param FoundIndex A variable which receives the index of the found menu item. + * + * \return The found menu item, or NULL if the menu item could not be found. + */ +PPH_EMENU_ITEM PhFindEMenuItemEx( + _In_ PPH_EMENU_ITEM Item, + _In_ ULONG Flags, + _In_opt_ PWSTR Text, + _In_opt_ ULONG Id, + _Out_opt_ PPH_EMENU_ITEM *FoundParent, + _Out_opt_ PULONG FoundIndex + ) +{ + PH_STRINGREF searchText; + ULONG i; + PPH_EMENU_ITEM item; + + if (!Item->Items) + return NULL; + + if (Text && (Flags & PH_EMENU_FIND_LITERAL)) + PhInitializeStringRef(&searchText, Text); + + for (i = 0; i < Item->Items->Count; i++) + { + item = Item->Items->Items[i]; + + if (Text) + { + if (Flags & PH_EMENU_FIND_LITERAL) + { + PH_STRINGREF text; + + PhInitializeStringRef(&text, item->Text); + + if (Flags & PH_EMENU_FIND_STARTSWITH) + { + if (PhStartsWithStringRef(&text, &searchText, TRUE)) + goto FoundItemHere; + } + else + { + if (PhEqualStringRef(&text, &searchText, TRUE)) + goto FoundItemHere; + } + } + else + { + if (PhCompareUnicodeStringZIgnoreMenuPrefix(Text, item->Text, + TRUE, !!(Flags & PH_EMENU_FIND_STARTSWITH)) == 0) + goto FoundItemHere; + } + } + + if (Id && item->Id == Id) + goto FoundItemHere; + + if (Flags & PH_EMENU_FIND_DESCEND) + { + PPH_EMENU_ITEM foundItem; + PPH_EMENU_ITEM foundParent; + ULONG foundIndex; + + foundItem = PhFindEMenuItemEx(item, Flags, Text, Id, &foundParent, &foundIndex); + + if (foundItem) + { + if (FoundParent) + *FoundParent = foundParent; + if (FoundIndex) + *FoundIndex = foundIndex; + + return foundItem; + } + } + } + + return NULL; + +FoundItemHere: + if (FoundParent) + *FoundParent = Item; + if (FoundIndex) + *FoundIndex = i; + + return item; +} + +/** + * Determines the index of a menu item. + * + * \param Parent The parent menu item. + * \param Item The child menu item. + * + * \return The index of the menu item, or -1 if the menu item was not found in the parent menu item. + */ +ULONG PhIndexOfEMenuItem( + _In_ PPH_EMENU_ITEM Parent, + _In_ PPH_EMENU_ITEM Item + ) +{ + if (!Parent->Items) + return ULONG_MAX; + + return PhFindItemList(Parent->Items, Item); +} + +/** + * Inserts a menu item into a parent menu item. + * + * \param Parent The parent menu item. + * \param Item The menu item to insert. + * \param Index The index at which to insert the menu item. If the index is too large, the menu item + * is inserted at the last position. + */ +VOID PhInsertEMenuItem( + _Inout_ PPH_EMENU_ITEM Parent, + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG Index + ) +{ + // Remove the item from its old parent if it has one. + if (Item->Parent) + PhRemoveEMenuItem(Item->Parent, Item, 0); + + if (!Parent->Items) + Parent->Items = PhCreateList(16); + + if (Index > Parent->Items->Count) + Index = Parent->Items->Count; + + if (Index == ULONG_MAX) + PhAddItemList(Parent->Items, Item); + else + PhInsertItemList(Parent->Items, Index, Item); + + Item->Parent = Parent; +} + +/** + * Removes a menu item from its parent. + * + * \param Parent The parent menu item. If \a Item is NULL, this parameter must be specified. + * \param Item The child menu item. This may be NULL if \a Index is specified. + * \param Index The index of the menu item to remove. If \a Item is specified, this parameter is + * ignored. + */ +BOOLEAN PhRemoveEMenuItem( + _Inout_opt_ PPH_EMENU_ITEM Parent, + _In_opt_ PPH_EMENU_ITEM Item, + _In_opt_ ULONG Index + ) +{ + if (Item) + { + if (!Parent) + Parent = Item->Parent; + if (!Parent->Items) + return FALSE; + + Index = PhFindItemList(Parent->Items, Item); + + if (Index == ULONG_MAX) + return FALSE; + } + else + { + if (!Parent) + return FALSE; + if (!Parent->Items) + return FALSE; + } + + Item = Parent->Items->Items[Index]; + PhRemoveItemList(Parent->Items, Index); + Item->Parent = NULL; + + return TRUE; +} + +/** + * Removes all children from a menu item. + * + * \param Parent The parent menu item. + */ +VOID PhRemoveAllEMenuItems( + _Inout_ PPH_EMENU_ITEM Parent + ) +{ + ULONG i; + + if (!Parent->Items) + return; + + for (i = 0; i < Parent->Items->Count; i++) + { + PhpDestroyEMenuItem(Parent->Items->Items[i]); + } + + PhClearList(Parent->Items); +} + +/** + * Creates a root menu. + */ +PPH_EMENU PhCreateEMenu( + VOID + ) +{ + PPH_EMENU menu; + + menu = PhAllocate(sizeof(PH_EMENU)); + memset(menu, 0, sizeof(PH_EMENU)); + menu->Items = PhCreateList(16); + + return menu; +} + +/** + * Frees resources used by a root menu and its children. + * + * \param Menu A root menu. + */ +VOID PhDestroyEMenu( + _In_ PPH_EMENU Menu + ) +{ + ULONG i; + + for (i = 0; i < Menu->Items->Count; i++) + { + PhpDestroyEMenuItem(Menu->Items->Items[i]); + } + + PhDereferenceObject(Menu->Items); + PhFree(Menu); +} + +/** + * Initializes a data structure containing additional information resulting from a call to + * PhEMenuToHMenu(). + */ +VOID PhInitializeEMenuData( + _Out_ PPH_EMENU_DATA Data + ) +{ + Data->IdToItem = PhCreateList(16); +} + +/** + * Frees resources used by a data structure initialized by PhInitializeEMenuData(). + */ +VOID PhDeleteEMenuData( + _Inout_ PPH_EMENU_DATA Data + ) +{ + PhDereferenceObject(Data->IdToItem); +} + +/** + * Converts an EMENU to a Windows menu object. + * + * \param Menu The menu item to convert. + * \param Flags A combination of the following: + * \li \c PH_EMENU_CONVERT_ID Automatically assigns a unique identifier to each converted menu item. + * The resulting mappings are placed in \a Data. + * \param Data Additional data resulting from the conversion. The data structure must be initialized + * by PhInitializeEMenuData() prior to calling this function. + * + * \return A menu handle. The menu object must be destroyed using DestroyMenu() when it is no longer + * needed. + */ +HMENU PhEMenuToHMenu( + _In_ PPH_EMENU_ITEM Menu, + _In_ ULONG Flags, + _Inout_opt_ PPH_EMENU_DATA Data + ) +{ + HMENU menuHandle; + + menuHandle = CreatePopupMenu(); + + if (!menuHandle) + return NULL; + + PhEMenuToHMenu2(menuHandle, Menu, Flags, Data); + + if (!(Menu->Flags & PH_EMENU_SEPARATECHECKSPACE)) + { + MENUINFO menuInfo; + + memset(&menuInfo, 0, sizeof(MENUINFO)); + menuInfo.cbSize = sizeof(MENUINFO); + menuInfo.fMask = MIM_STYLE; + menuInfo.dwStyle = MNS_CHECKORBMP; + + if (PhGetIntegerSetting(L"EnableThemeSupport")) + { + menuInfo.fMask |= MIM_BACKGROUND; + menuInfo.hbrBack = PhMenuBackgroundBrush; + } + + SetMenuInfo(menuHandle, &menuInfo); + } + + return menuHandle; +} + +/** + * Converts an EMENU to a Windows menu object. + * + * \param MenuHandle A handle to a Windows menu object. + * \param Menu The menu item to convert. The items are appended to \a MenuHandle. + * \param Flags A combination of the following: + * \li \c PH_EMENU_CONVERT_ID Automatically assigns a unique identifier to each converted menu item. + * The resulting mappings are placed in \a Data. + * \param Data Additional data resulting from the conversion. The data structure must be initialized + * by PhInitializeEMenuData() prior to calling this function. + */ +VOID PhEMenuToHMenu2( + _In_ HMENU MenuHandle, + _In_ PPH_EMENU_ITEM Menu, + _In_ ULONG Flags, + _Inout_opt_ PPH_EMENU_DATA Data + ) +{ + ULONG i; + PPH_EMENU_ITEM item; + MENUITEMINFO menuItemInfo; + + for (i = 0; i < Menu->Items->Count; i++) + { + item = Menu->Items->Items[i]; + + memset(&menuItemInfo, 0, sizeof(MENUITEMINFO)); + menuItemInfo.cbSize = sizeof(MENUITEMINFO); + + // Type + + menuItemInfo.fMask = MIIM_FTYPE | MIIM_STATE; + + if (item->Flags & PH_EMENU_SEPARATOR) + { + menuItemInfo.fType = MFT_SEPARATOR; + } + else + { + menuItemInfo.fType = MFT_STRING; + menuItemInfo.fMask |= MIIM_STRING; + menuItemInfo.dwTypeData = item->Text; + } + + PhMapFlags1( + &menuItemInfo.fType, + item->Flags, + EMenuTypeMappings, + sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING) + ); + + // Bitmap + + if (item->Bitmap) + { + if (!PhGetIntegerSetting(L"EnableThemeSupport")) + menuItemInfo.fMask |= MIIM_BITMAP; // HACK + + menuItemInfo.hbmpItem = item->Bitmap; + } + + // Id + + if (Flags & PH_EMENU_CONVERT_ID) + { + ULONG id; + + if (Data) + { + PhAddItemList(Data->IdToItem, item); + id = Data->IdToItem->Count; + + menuItemInfo.fMask |= MIIM_ID; + menuItemInfo.wID = id; + } + } + else + { + if (item->Id) + { + menuItemInfo.fMask |= MIIM_ID; + menuItemInfo.wID = item->Id; + } + } + + // State + + PhMapFlags1( + &menuItemInfo.fState, + item->Flags, + EMenuStateMappings, + sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING) + ); + + // Context + + menuItemInfo.fMask |= MIIM_DATA; + menuItemInfo.dwItemData = (ULONG_PTR)item; + + // Submenu + + if (item->Items && item->Items->Count != 0) + { + menuItemInfo.fMask |= MIIM_SUBMENU; + menuItemInfo.hSubMenu = PhEMenuToHMenu(item, Flags, Data); + } + + // Theme + + if (PhGetIntegerSetting(L"EnableThemeSupport")) // HACK + { + switch (PhGetIntegerSetting(L"GraphColorMode")) + { + case 0: // New colors + menuItemInfo.fType |= MFT_OWNERDRAW; + break; + case 1: // Old colors + menuItemInfo.fType |= MFT_OWNERDRAW; + break; + } + } + + InsertMenuItem(MenuHandle, MAXINT, TRUE, &menuItemInfo); + } +} + +/** + * Converts a Windows menu object to an EMENU. + * + * \param MenuItem The menu item in which the converted menu items will be placed. + * \param MenuHandle A menu handle. + */ +VOID PhHMenuToEMenuItem( + _Inout_ PPH_EMENU_ITEM MenuItem, + _In_ HMENU MenuHandle + ) +{ + ULONG i; + ULONG count; + + count = GetMenuItemCount(MenuHandle); + + if (count == -1) + return; + + for (i = 0; i < count; i++) + { + MENUITEMINFO menuItemInfo; + PPH_EMENU_ITEM menuItem; + WCHAR buffer[MAX_PATH]; + + menuItemInfo.cbSize = sizeof(menuItemInfo); + menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU; + menuItemInfo.cch = RTL_NUMBER_OF(buffer); + menuItemInfo.dwTypeData = buffer; + + if (!GetMenuItemInfo(MenuHandle, i, TRUE, &menuItemInfo)) + continue; + + menuItem = PhCreateEMenuItem( + PH_EMENU_TEXT_OWNED, + menuItemInfo.wID, + PhDuplicateStringZ(buffer), + NULL, + NULL + ); + + if (menuItemInfo.fType & MFT_SEPARATOR) + menuItem->Flags |= PH_EMENU_SEPARATOR; + + PhMapFlags2( + &menuItem->Flags, + menuItemInfo.fType, + EMenuTypeMappings, + RTL_NUMBER_OF(EMenuTypeMappings) + ); + PhMapFlags2( + &menuItem->Flags, + menuItemInfo.fState, + EMenuStateMappings, + RTL_NUMBER_OF(EMenuStateMappings) + ); + + if (menuItemInfo.hSubMenu) + PhHMenuToEMenuItem(menuItem, menuItemInfo.hSubMenu); + + PhInsertEMenuItem(MenuItem, menuItem, ULONG_MAX); + } +} + +/** + * Loads a menu resource and converts it to an EMENU. + * + * \param MenuItem The menu item in which the converted menu items will be placed. + * \param InstanceHandle The module containing the menu resource. + * \param Resource The resource identifier. + * \param SubMenuIndex The index of the sub menu to use, or -1 to use the root menu. + */ +VOID PhLoadResourceEMenuItem( + _Inout_ PPH_EMENU_ITEM MenuItem, + _In_ HINSTANCE InstanceHandle, + _In_ PWSTR Resource, + _In_ ULONG SubMenuIndex + ) +{ + HMENU menu; + HMENU realMenu; + + menu = PhLoadMenu(InstanceHandle, Resource); + + if (SubMenuIndex != ULONG_MAX) + realMenu = GetSubMenu(menu, SubMenuIndex); + else + realMenu = menu; + + PhHMenuToEMenuItem(MenuItem, realMenu); + + DestroyMenu(menu); +} + +/** + * Displays a menu. + * + * \param Menu A menu. + * \param WindowHandle The window that owns the popup menu. + * \param Flags A combination of the following: + * \li \c PH_EMENU_SHOW_SEND_COMMAND A WM_COMMAND message is sent to the window when the user clicks + * on a menu item. + * \li \c PH_EMENU_SHOW_LEFTRIGHT The user can select menu items with both the left and right mouse + * buttons. + * \param Align The alignment of the menu. + * \param X The horizontal location of the menu. + * \param Y The vertical location of the menu. + * + * \return The selected menu item, or NULL if the menu was cancelled. + */ +PPH_EMENU_ITEM PhShowEMenu( + _In_ PPH_EMENU Menu, + _In_ HWND WindowHandle, + _In_ ULONG Flags, + _In_ ULONG Align, + _In_ ULONG X, + _In_ ULONG Y + ) +{ + PPH_EMENU_ITEM selectedItem; + ULONG result; + ULONG flags; + PH_EMENU_DATA data; + HMENU popupMenu; + + selectedItem = NULL; + flags = TPM_RETURNCMD | TPM_NONOTIFY; + + // Flags + + if (Flags & PH_EMENU_SHOW_LEFTRIGHT) + flags |= TPM_RIGHTBUTTON; + else + flags |= TPM_LEFTBUTTON; + + // Align + + if (Align & PH_ALIGN_LEFT) + flags |= TPM_LEFTALIGN; + else if (Align & PH_ALIGN_RIGHT) + flags |= TPM_RIGHTALIGN; + else + flags |= TPM_CENTERALIGN; + + if (Align & PH_ALIGN_TOP) + flags |= TPM_TOPALIGN; + else if (Align & PH_ALIGN_BOTTOM) + flags |= TPM_BOTTOMALIGN; + else + flags |= TPM_VCENTERALIGN; + + PhInitializeEMenuData(&data); + + if (popupMenu = PhEMenuToHMenu(Menu, PH_EMENU_CONVERT_ID, &data)) + { + result = TrackPopupMenu( + popupMenu, + flags, + X, + Y, + 0, + WindowHandle, + NULL + ); + + if (result != 0) + { + selectedItem = data.IdToItem->Items[result - 1]; + } + + DestroyMenu(popupMenu); + } + + PhDeleteEMenuData(&data); + + if ((Flags & PH_EMENU_SHOW_SEND_COMMAND) && selectedItem && selectedItem->Id != 0) + SendMessage(WindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, 0), 0); + + return selectedItem; +} + +/** + * Sets the flags of a menu item. + * + * \param Item The parent menu item. + * \param Id The identifier of the child menu item. + * \param Mask The flags to modify. + * \param Value The new value of the flags. + */ +BOOLEAN PhSetFlagsEMenuItem( + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG Id, + _In_ ULONG Mask, + _In_ ULONG Value + ) +{ + PPH_EMENU_ITEM item; + + item = PhFindEMenuItem(Item, PH_EMENU_FIND_DESCEND, NULL, Id); + + if (item) + { + item->Flags &= ~Mask; + item->Flags |= Value; + + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * Sets flags for all children of a menu item. + * + * \param Item The parent menu item. + * \param Mask The flags to modify. + * \param Value The new value of the flags. + */ +VOID PhSetFlagsAllEMenuItems( + _In_ PPH_EMENU_ITEM Item, + _In_ ULONG Mask, + _In_ ULONG Value + ) +{ + ULONG i; + + for (i = 0; i < Item->Items->Count; i++) + { + PPH_EMENU_ITEM item = Item->Items->Items[i]; + + item->Flags &= ~Mask; + item->Flags |= Value; + } +} + +VOID PhModifyEMenuItem( + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG ModifyFlags, + _In_ ULONG OwnedFlags, + _In_opt_ PWSTR Text, + _In_opt_ HBITMAP Bitmap + ) +{ + if (ModifyFlags & PH_EMENU_MODIFY_TEXT) + { + if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text) + PhFree(Item->Text); + + Item->Text = Text; + Item->Flags &= ~PH_EMENU_TEXT_OWNED; + Item->Flags |= OwnedFlags & PH_EMENU_TEXT_OWNED; + } + + if (ModifyFlags & PH_EMENU_MODIFY_BITMAP) + { + if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap) + DeleteBitmap(Item->Bitmap); + + Item->Bitmap = Bitmap; + Item->Flags &= ~PH_EMENU_BITMAP_OWNED; + Item->Flags |= OwnedFlags & PH_EMENU_BITMAP_OWNED; + } +} diff --git a/phlib/error.c b/phlib/error.c index 8c3f2373a917..08328b1e2b58 100644 --- a/phlib/error.c +++ b/phlib/error.c @@ -1,92 +1,92 @@ -/* - * Process Hacker - - * error codes - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -/** - * Converts a NTSTATUS value to a Win32 error code. - * - * \remarks This function handles FACILITY_NTWIN32 status values properly, unlike - * RtlNtStatusToDosError. - */ -ULONG PhNtStatusToDosError( - _In_ NTSTATUS Status - ) -{ - if (NT_NTWIN32(Status)) // RtlNtStatusToDosError doesn't seem to handle these cases correctly - return WIN32_FROM_NTSTATUS(Status); - else - return RtlNtStatusToDosError(Status); -} - -/** - * Converts a Win32 error code to a NTSTATUS value. - * - * \remarks Only a small number of cases are currently supported. Other status values are wrapped - * using FACILITY_NTWIN32. - */ -NTSTATUS PhDosErrorToNtStatus( - _In_ ULONG DosError - ) -{ - switch (DosError) - { - case ERROR_INVALID_FUNCTION: return STATUS_ILLEGAL_FUNCTION; - case ERROR_FILE_NOT_FOUND: return STATUS_NO_SUCH_FILE; - case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED; - case ERROR_INVALID_HANDLE: return STATUS_INVALID_HANDLE; - case ERROR_HANDLE_EOF: return STATUS_END_OF_FILE; - case ERROR_NOT_SUPPORTED: return STATUS_NOT_SUPPORTED; - case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER; - case ERROR_NOT_LOCKED: return STATUS_NOT_LOCKED; - case ERROR_MORE_DATA: return STATUS_MORE_ENTRIES; - case ERROR_NOACCESS: return STATUS_ACCESS_VIOLATION; - case ERROR_STACK_OVERFLOW: return STATUS_STACK_OVERFLOW; - case ERROR_INTERNAL_ERROR: return STATUS_INTERNAL_ERROR; - default: return NTSTATUS_FROM_WIN32(DosError); - } -} - -/** - * Determines whether a NTSTATUS value indicates that a file cannot be not found. - */ -BOOLEAN PhNtStatusFileNotFound( - _In_ NTSTATUS Status - ) -{ - switch (Status) - { - case STATUS_NO_SUCH_FILE: - return TRUE; - case STATUS_OBJECT_NAME_INVALID: - return TRUE; - case STATUS_OBJECT_NAME_NOT_FOUND: - return TRUE; - case STATUS_OBJECT_NO_LONGER_EXISTS: - return TRUE; - case STATUS_OBJECT_PATH_INVALID: - return TRUE; - case STATUS_OBJECT_PATH_NOT_FOUND: - return TRUE; - default: return FALSE; - } -} +/* + * Process Hacker - + * error codes + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +/** + * Converts a NTSTATUS value to a Win32 error code. + * + * \remarks This function handles FACILITY_NTWIN32 status values properly, unlike + * RtlNtStatusToDosError. + */ +ULONG PhNtStatusToDosError( + _In_ NTSTATUS Status + ) +{ + if (NT_NTWIN32(Status)) // RtlNtStatusToDosError doesn't seem to handle these cases correctly + return WIN32_FROM_NTSTATUS(Status); + else + return RtlNtStatusToDosErrorNoTeb(Status); +} + +/** + * Converts a Win32 error code to a NTSTATUS value. + * + * \remarks Only a small number of cases are currently supported. Other status values are wrapped + * using FACILITY_NTWIN32. + */ +NTSTATUS PhDosErrorToNtStatus( + _In_ ULONG DosError + ) +{ + switch (DosError) + { + case ERROR_INVALID_FUNCTION: return STATUS_ILLEGAL_FUNCTION; + case ERROR_FILE_NOT_FOUND: return STATUS_NO_SUCH_FILE; + case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED; + case ERROR_INVALID_HANDLE: return STATUS_INVALID_HANDLE; + case ERROR_HANDLE_EOF: return STATUS_END_OF_FILE; + case ERROR_NOT_SUPPORTED: return STATUS_NOT_SUPPORTED; + case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER; + case ERROR_NOT_LOCKED: return STATUS_NOT_LOCKED; + case ERROR_MORE_DATA: return STATUS_MORE_ENTRIES; + case ERROR_NOACCESS: return STATUS_ACCESS_VIOLATION; + case ERROR_STACK_OVERFLOW: return STATUS_STACK_OVERFLOW; + case ERROR_INTERNAL_ERROR: return STATUS_INTERNAL_ERROR; + default: return NTSTATUS_FROM_WIN32(DosError); + } +} + +/** + * Determines whether a NTSTATUS value indicates that a file cannot be not found. + */ +BOOLEAN PhNtStatusFileNotFound( + _In_ NTSTATUS Status + ) +{ + switch (Status) + { + case STATUS_NO_SUCH_FILE: + return TRUE; + case STATUS_OBJECT_NAME_INVALID: + return TRUE; + case STATUS_OBJECT_NAME_NOT_FOUND: + return TRUE; + case STATUS_OBJECT_NO_LONGER_EXISTS: + return TRUE; + case STATUS_OBJECT_PATH_INVALID: + return TRUE; + case STATUS_OBJECT_PATH_NOT_FOUND: + return TRUE; + default: return FALSE; + } +} diff --git a/phlib/extlv.c b/phlib/extlv.c index cb4c1654d951..f6aa450bb20a 100644 --- a/phlib/extlv.c +++ b/phlib/extlv.c @@ -1,734 +1,755 @@ -/* - * Process Hacker - - * extended list view - * - * Copyright (C) 2010-2012 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The extended list view adds some functionality to the default list view control, such as sorting, - * item colors and fonts, better redraw disabling, and the ability to change the cursor. This is - * currently implemented by hooking the window procedure. - */ - -#include - -#include - -#include - -#define PH_MAX_COMPARE_FUNCTIONS 16 - -typedef struct _PH_EXTLV_CONTEXT -{ - HWND Handle; - PVOID Context; - - // Sorting - - BOOLEAN TriState; - ULONG SortColumn; - PH_SORT_ORDER SortOrder; - BOOLEAN SortFast; - - PPH_COMPARE_FUNCTION TriStateCompareFunction; - PPH_COMPARE_FUNCTION CompareFunctions[PH_MAX_COMPARE_FUNCTIONS]; - ULONG FallbackColumns[PH_MAX_COMPARE_FUNCTIONS]; - ULONG NumberOfFallbackColumns; - - // Color and Font - PPH_EXTLV_GET_ITEM_COLOR ItemColorFunction; - PPH_EXTLV_GET_ITEM_FONT ItemFontFunction; - - // Misc. - - LONG EnableRedraw; - HCURSOR Cursor; -} PH_EXTLV_CONTEXT, *PPH_EXTLV_CONTEXT; - -LRESULT CALLBACK PhpExtendedListViewWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -INT PhpExtendedListViewCompareFunc( - _In_ LPARAM lParam1, - _In_ LPARAM lParam2, - _In_ LPARAM lParamSort - ); - -INT PhpExtendedListViewCompareFastFunc( - _In_ LPARAM lParam1, - _In_ LPARAM lParam2, - _In_ LPARAM lParamSort - ); - -INT PhpCompareListViewItems( - _In_ PPH_EXTLV_CONTEXT Context, - _In_ INT X, - _In_ INT Y, - _In_ PVOID XParam, - _In_ PVOID YParam, - _In_ ULONG Column, - _In_ BOOLEAN EnableDefault - ); - -INT PhpDefaultCompareListViewItems( - _In_ PPH_EXTLV_CONTEXT Context, - _In_ INT X, - _In_ INT Y, - _In_ ULONG Column - ); - -/** - * Enables extended list view support for a list view control. - * - * \param hWnd A handle to the list view control. - */ -VOID PhSetExtendedListView( - _In_ HWND hWnd - ) -{ - PPH_EXTLV_CONTEXT context; - - context = PhAllocate(sizeof(PH_EXTLV_CONTEXT)); - - context->Handle = hWnd; - context->Context = NULL; - context->TriState = FALSE; - context->SortColumn = 0; - context->SortOrder = AscendingSortOrder; - context->SortFast = FALSE; - context->TriStateCompareFunction = NULL; - memset(context->CompareFunctions, 0, sizeof(context->CompareFunctions)); - context->NumberOfFallbackColumns = 0; - - context->ItemColorFunction = NULL; - context->ItemFontFunction = NULL; - - context->EnableRedraw = 1; - context->Cursor = NULL; - - SetWindowSubclass(hWnd, PhpExtendedListViewWndProc, 0, (ULONG_PTR)context); - - ExtendedListView_Init(hWnd); -} - -LRESULT CALLBACK PhpExtendedListViewWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_EXTLV_CONTEXT context = (PPH_EXTLV_CONTEXT)dwRefData; - - switch (uMsg) - { - case WM_DESTROY: - { - RemoveWindowSubclass(hwnd, PhpExtendedListViewWndProc, uIdSubclass); - - PhFree(context); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case HDN_ITEMCLICK: - { - HWND headerHandle; - - headerHandle = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0); - - if (header->hwndFrom == headerHandle) - { - LPNMHEADER header2 = (LPNMHEADER)header; - - if (header2->iItem == context->SortColumn) - { - if (context->TriState) - { - if (context->SortOrder == AscendingSortOrder) - context->SortOrder = DescendingSortOrder; - else if (context->SortOrder == DescendingSortOrder) - context->SortOrder = NoSortOrder; - else - context->SortOrder = AscendingSortOrder; - } - else - { - if (context->SortOrder == AscendingSortOrder) - context->SortOrder = DescendingSortOrder; - else - context->SortOrder = AscendingSortOrder; - } - } - else - { - context->SortColumn = header2->iItem; - context->SortOrder = AscendingSortOrder; - } - - PhSetHeaderSortIcon(headerHandle, context->SortColumn, context->SortOrder); - ExtendedListView_SortItems(hwnd); - } - } - break; - } - } - break; - case WM_REFLECT + WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case NM_CUSTOMDRAW: - { - if (header->hwndFrom == hwnd) - { - LPNMLVCUSTOMDRAW customDraw = (LPNMLVCUSTOMDRAW)header; - - switch (customDraw->nmcd.dwDrawStage) - { - case CDDS_PREPAINT: - return CDRF_NOTIFYITEMDRAW; - case CDDS_ITEMPREPAINT: - { - BOOLEAN colorChanged = FALSE; - HFONT newFont = NULL; - - if (context->ItemColorFunction) - { - customDraw->clrTextBk = context->ItemColorFunction( - (INT)customDraw->nmcd.dwItemSpec, - (PVOID)customDraw->nmcd.lItemlParam, - context->Context - ); - colorChanged = TRUE; - } - - if (context->ItemFontFunction) - { - newFont = context->ItemFontFunction( - (INT)customDraw->nmcd.dwItemSpec, - (PVOID)customDraw->nmcd.lItemlParam, - context->Context - ); - } - - if (newFont) - SelectObject(customDraw->nmcd.hdc, newFont); - - if (colorChanged) - { - if (PhGetColorBrightness(customDraw->clrTextBk) > 100) // slightly less than half - customDraw->clrText = RGB(0x00, 0x00, 0x00); - else - customDraw->clrText = RGB(0xff, 0xff, 0xff); - } - - if (!newFont) - return CDRF_DODEFAULT; - else - return CDRF_NEWFONT; - } - break; - } - } - } - break; - } - } - break; - case WM_SETCURSOR: - { - if (context->Cursor) - { - SetCursor(context->Cursor); - return TRUE; - } - } - break; - case WM_UPDATEUISTATE: - { - // Disable focus rectangles by setting or masking out the flag where appropriate. - switch (LOWORD(wParam)) - { - case UIS_SET: - wParam |= UISF_HIDEFOCUS << 16; - break; - case UIS_CLEAR: - case UIS_INITIALIZE: - wParam &= ~(UISF_HIDEFOCUS << 16); - break; - } - } - break; - case ELVM_ADDFALLBACKCOLUMN: - { - if (context->NumberOfFallbackColumns < PH_MAX_COMPARE_FUNCTIONS) - context->FallbackColumns[context->NumberOfFallbackColumns++] = (ULONG)wParam; - else - return FALSE; - } - return TRUE; - case ELVM_ADDFALLBACKCOLUMNS: - { - ULONG numberOfColumns = (ULONG)wParam; - PULONG columns = (PULONG)lParam; - - if (context->NumberOfFallbackColumns + numberOfColumns <= PH_MAX_COMPARE_FUNCTIONS) - { - memcpy( - &context->FallbackColumns[context->NumberOfFallbackColumns], - columns, - numberOfColumns * sizeof(ULONG) - ); - context->NumberOfFallbackColumns += numberOfColumns; - } - else - { - return FALSE; - } - } - return TRUE; - case ELVM_INIT: - { - PhSetHeaderSortIcon(ListView_GetHeader(hwnd), context->SortColumn, context->SortOrder); - - // HACK to fix tooltips showing behind Always On Top windows. - SetWindowPos(ListView_GetToolTips(hwnd), HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - - // Make sure focus rectangles are disabled. - SendMessage(hwnd, WM_CHANGEUISTATE, MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0); - } - return TRUE; - case ELVM_SETCOLUMNWIDTH: - { - ULONG column = (ULONG)wParam; - LONG width = (LONG)lParam; - - if (width == ELVSCW_AUTOSIZE_REMAININGSPACE) - { - RECT clientRect; - LONG availableWidth; - ULONG i; - LVCOLUMN lvColumn; - - GetClientRect(hwnd, &clientRect); - availableWidth = clientRect.right; - i = 0; - lvColumn.mask = LVCF_WIDTH; - - while (TRUE) - { - if (i != column) - { - if (SendMessage(hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) - { - availableWidth -= lvColumn.cx; - } - else - { - break; - } - } - - i++; - } - - if (availableWidth >= 40) - return SendMessage(hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); - } - - return SendMessage(hwnd, LVM_SETCOLUMNWIDTH, column, width); - } - break; - case ELVM_SETCOMPAREFUNCTION: - { - ULONG column = (ULONG)wParam; - PPH_COMPARE_FUNCTION compareFunction = (PPH_COMPARE_FUNCTION)lParam; - - if (column >= PH_MAX_COMPARE_FUNCTIONS) - return FALSE; - - context->CompareFunctions[column] = compareFunction; - } - return TRUE; - case ELVM_SETCONTEXT: - { - context->Context = (PVOID)lParam; - } - return TRUE; - case ELVM_SETCURSOR: - { - context->Cursor = (HCURSOR)lParam; - } - return TRUE; - case ELVM_SETITEMCOLORFUNCTION: - { - context->ItemColorFunction = (PPH_EXTLV_GET_ITEM_COLOR)lParam; - } - return TRUE; - case ELVM_SETITEMFONTFUNCTION: - { - context->ItemFontFunction = (PPH_EXTLV_GET_ITEM_FONT)lParam; - } - return TRUE; - case ELVM_SETREDRAW: - { - if (wParam) - context->EnableRedraw++; - else - context->EnableRedraw--; - - if (context->EnableRedraw == 1) - { - SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); - InvalidateRect(hwnd, NULL, FALSE); - } - else if (context->EnableRedraw == 0) - { - SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); - } - } - return TRUE; - case ELVM_SETSORT: - { - context->SortColumn = (ULONG)wParam; - context->SortOrder = (PH_SORT_ORDER)lParam; - - PhSetHeaderSortIcon(ListView_GetHeader(hwnd), context->SortColumn, context->SortOrder); - } - return TRUE; - case ELVM_SETSORTFAST: - { - context->SortFast = !!wParam; - } - return TRUE; - case ELVM_SETTRISTATE: - { - context->TriState = !!wParam; - } - return TRUE; - case ELVM_SETTRISTATECOMPAREFUNCTION: - { - context->TriStateCompareFunction = (PPH_COMPARE_FUNCTION)lParam; - } - return TRUE; - case ELVM_SORTITEMS: - { - if (context->SortFast) - { - // This sort method is faster than the normal sort because our comparison function - // doesn't have to call the list view window procedure to get the item lParam - // values. The disadvantage of this method is that default sorting is not available - // - if a column doesn't have a comparison function, it doesn't get sorted at all. - - ListView_SortItems( - hwnd, - PhpExtendedListViewCompareFastFunc, - (LPARAM)context - ); - } - else - { - ListView_SortItemsEx( - hwnd, - PhpExtendedListViewCompareFunc, - (LPARAM)context - ); - } - } - return TRUE; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -/** - * Visually indicates the sort order of a header control item. - * - * \param hwnd A handle to the header control. - * \param Index The index of the item. - * \param Order The sort order of the item. - */ -VOID PhSetHeaderSortIcon( - _In_ HWND hwnd, - _In_ INT Index, - _In_ PH_SORT_ORDER Order - ) -{ - ULONG count; - ULONG i; - - count = Header_GetItemCount(hwnd); - - if (count == -1) - return; - - for (i = 0; i < count; i++) - { - HDITEM item; - - item.mask = HDI_FORMAT; - Header_GetItem(hwnd, i, &item); - - if (Order != NoSortOrder && i == Index) - { - if (Order == AscendingSortOrder) - { - item.fmt &= ~HDF_SORTDOWN; - item.fmt |= HDF_SORTUP; - } - else if (Order == DescendingSortOrder) - { - item.fmt &= ~HDF_SORTUP; - item.fmt |= HDF_SORTDOWN; - } - } - else - { - item.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); - } - - Header_SetItem(hwnd, i, &item); - } -} - -static INT PhpExtendedListViewCompareFunc( - _In_ LPARAM lParam1, - _In_ LPARAM lParam2, - _In_ LPARAM lParamSort - ) -{ - PPH_EXTLV_CONTEXT context = (PPH_EXTLV_CONTEXT)lParamSort; - INT result; - INT x = (INT)lParam1; - INT y = (INT)lParam2; - ULONG i; - PULONG fallbackColumns; - LVITEM xItem; - LVITEM yItem; - - // Get the param values. - - xItem.mask = LVIF_PARAM | LVIF_STATE; - xItem.iItem = x; - xItem.iSubItem = 0; - - yItem.mask = LVIF_PARAM | LVIF_STATE; - yItem.iItem = y; - yItem.iSubItem = 0; - - if (!SendMessage(context->Handle, LVM_GETITEM, 0, (LPARAM)&xItem)) - return 0; - if (!SendMessage(context->Handle, LVM_GETITEM, 0, (LPARAM)&yItem)) - return 0; - - // First, do tri-state sorting. - - if ( - context->TriState && - context->SortOrder == NoSortOrder && - context->TriStateCompareFunction - ) - { - result = context->TriStateCompareFunction( - (PVOID)xItem.lParam, - (PVOID)yItem.lParam, - context->Context - ); - - if (result != 0) - return result; - } - - // Compare using the user-selected column and move on to the fallback columns if necessary. - - result = PhpCompareListViewItems(context, x, y, (PVOID)xItem.lParam, (PVOID)yItem.lParam, context->SortColumn, TRUE); - - if (result != 0) - return result; - - fallbackColumns = context->FallbackColumns; - - for (i = context->NumberOfFallbackColumns; i != 0; i--) - { - ULONG fallbackColumn = *fallbackColumns++; - - if (fallbackColumn == context->SortColumn) - continue; - - result = PhpCompareListViewItems(context, x, y, (PVOID)xItem.lParam, (PVOID)yItem.lParam, fallbackColumn, TRUE); - - if (result != 0) - return result; - } - - return 0; -} - -static INT PhpExtendedListViewCompareFastFunc( - _In_ LPARAM lParam1, - _In_ LPARAM lParam2, - _In_ LPARAM lParamSort - ) -{ - PPH_EXTLV_CONTEXT context = (PPH_EXTLV_CONTEXT)lParamSort; - INT result; - ULONG i; - PULONG fallbackColumns; - - if (!lParam1 || !lParam2) - return 0; - - // First, do tri-state sorting. - - if ( - context->TriState && - context->SortOrder == NoSortOrder && - context->TriStateCompareFunction - ) - { - result = context->TriStateCompareFunction( - (PVOID)lParam1, - (PVOID)lParam2, - context->Context - ); - - if (result != 0) - return result; - } - - // Compare using the user-selected column and move on to the fallback columns if necessary. - - result = PhpCompareListViewItems(context, 0, 0, (PVOID)lParam1, (PVOID)lParam2, context->SortColumn, FALSE); - - if (result != 0) - return result; - - fallbackColumns = context->FallbackColumns; - - for (i = context->NumberOfFallbackColumns; i != 0; i--) - { - ULONG fallbackColumn = *fallbackColumns++; - - if (fallbackColumn == context->SortColumn) - continue; - - result = PhpCompareListViewItems(context, 0, 0, (PVOID)lParam1, (PVOID)lParam2, fallbackColumn, FALSE); - - if (result != 0) - return result; - } - - return 0; -} - -static FORCEINLINE INT PhpCompareListViewItems( - _In_ PPH_EXTLV_CONTEXT Context, - _In_ INT X, - _In_ INT Y, - _In_ PVOID XParam, - _In_ PVOID YParam, - _In_ ULONG Column, - _In_ BOOLEAN EnableDefault - ) -{ - INT result = 0; - - if ( - Column < PH_MAX_COMPARE_FUNCTIONS && - Context->CompareFunctions[Column] - ) - { - result = PhModifySort( - Context->CompareFunctions[Column](XParam, YParam, Context->Context), - Context->SortOrder - ); - - if (result != 0) - return result; - } - - if (EnableDefault) - { - return PhModifySort( - PhpDefaultCompareListViewItems(Context, X, Y, Column), - Context->SortOrder - ); - } - else - { - return 0; - } -} - -static INT PhpDefaultCompareListViewItems( - _In_ PPH_EXTLV_CONTEXT Context, - _In_ INT X, - _In_ INT Y, - _In_ ULONG Column - ) -{ - WCHAR xText[261]; - WCHAR yText[261]; - LVITEM item; - - // Get the X item text. - - item.mask = LVIF_TEXT; - item.iItem = X; - item.iSubItem = Column; - item.pszText = xText; - item.cchTextMax = 260; - - xText[0] = 0; - SendMessage(Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); - - // Get the Y item text. - - item.iItem = Y; - item.pszText = yText; - item.cchTextMax = 260; - - yText[0] = 0; - SendMessage(Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); - - // Compare them. - -#if 1 - return PhCompareStringZNatural(xText, yText, TRUE); -#else - return _wcsicmp(xText, yText); -#endif -} +/* + * Process Hacker - + * extended list view + * + * Copyright (C) 2010-2012 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The extended list view adds some functionality to the default list view control, such as sorting, + * item colors and fonts, better redraw disabling, and the ability to change the cursor. This is + * currently implemented by hooking the window procedure. + */ + +#include +#include + +#define PH_MAX_COMPARE_FUNCTIONS 16 + +typedef struct _PH_EXTLV_CONTEXT +{ + HWND Handle; + WNDPROC OldWndProc; + PVOID Context; + + // Sorting + + BOOLEAN TriState; + ULONG SortColumn; + PH_SORT_ORDER SortOrder; + BOOLEAN SortFast; + + PPH_COMPARE_FUNCTION TriStateCompareFunction; + PPH_COMPARE_FUNCTION CompareFunctions[PH_MAX_COMPARE_FUNCTIONS]; + ULONG FallbackColumns[PH_MAX_COMPARE_FUNCTIONS]; + ULONG NumberOfFallbackColumns; + + // Color and Font + PPH_EXTLV_GET_ITEM_COLOR ItemColorFunction; + PPH_EXTLV_GET_ITEM_FONT ItemFontFunction; + + // Misc. + + LONG EnableRedraw; + HCURSOR Cursor; +} PH_EXTLV_CONTEXT, *PPH_EXTLV_CONTEXT; + +LRESULT CALLBACK PhpExtendedListViewWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT PhpExtendedListViewCompareFunc( + _In_ LPARAM lParam1, + _In_ LPARAM lParam2, + _In_ LPARAM lParamSort + ); + +INT PhpExtendedListViewCompareFastFunc( + _In_ LPARAM lParam1, + _In_ LPARAM lParam2, + _In_ LPARAM lParamSort + ); + +INT PhpCompareListViewItems( + _In_ PPH_EXTLV_CONTEXT Context, + _In_ INT X, + _In_ INT Y, + _In_ PVOID XParam, + _In_ PVOID YParam, + _In_ ULONG Column, + _In_ BOOLEAN EnableDefault + ); + +INT PhpDefaultCompareListViewItems( + _In_ PPH_EXTLV_CONTEXT Context, + _In_ INT X, + _In_ INT Y, + _In_ ULONG Column + ); + +/** + * Enables extended list view support for a list view control. + * + * \param hWnd A handle to the list view control. + */ +VOID PhSetExtendedListView( + _In_ HWND hWnd + ) +{ + PhSetExtendedListViewEx(hWnd, 0, AscendingSortOrder); +} + +VOID PhSetExtendedListViewEx( + _In_ HWND WindowHandle, + _In_ ULONG SortColumn, + _In_ ULONG SortOrder + ) +{ + PPH_EXTLV_CONTEXT context; + + context = PhAllocateZero(sizeof(PH_EXTLV_CONTEXT)); + context->Handle = WindowHandle; + context->Context = NULL; + context->TriState = FALSE; + context->SortColumn = SortColumn; + context->SortOrder = SortOrder; + context->SortFast = FALSE; + context->TriStateCompareFunction = NULL; + memset(context->CompareFunctions, 0, sizeof(context->CompareFunctions)); + context->NumberOfFallbackColumns = 0; + context->ItemColorFunction = NULL; + context->ItemFontFunction = NULL; + context->EnableRedraw = 1; + context->Cursor = NULL; + + context->OldWndProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); + PhSetWindowContext(WindowHandle, MAXCHAR, context); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhpExtendedListViewWndProc); + + ExtendedListView_Init(WindowHandle); +} + +LRESULT CALLBACK PhpExtendedListViewWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_EXTLV_CONTEXT context; + WNDPROC oldWndProc; + + context = PhGetWindowContext(hwnd, MAXCHAR); + + if (!context) + return 0; + + oldWndProc = context->OldWndProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, MAXCHAR); + PhFree(context); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case HDN_ITEMCLICK: + { + HWND headerHandle; + + headerHandle = (HWND)CallWindowProc(oldWndProc, hwnd, LVM_GETHEADER, 0, 0); + + if (header->hwndFrom == headerHandle) + { + LPNMHEADER header2 = (LPNMHEADER)header; + + if (header2->iItem == context->SortColumn) + { + if (context->TriState) + { + if (context->SortOrder == AscendingSortOrder) + context->SortOrder = DescendingSortOrder; + else if (context->SortOrder == DescendingSortOrder) + context->SortOrder = NoSortOrder; + else + context->SortOrder = AscendingSortOrder; + } + else + { + if (context->SortOrder == AscendingSortOrder) + context->SortOrder = DescendingSortOrder; + else + context->SortOrder = AscendingSortOrder; + } + } + else + { + context->SortColumn = header2->iItem; + context->SortOrder = AscendingSortOrder; + } + + PhSetHeaderSortIcon(headerHandle, context->SortColumn, context->SortOrder); + ExtendedListView_SortItems(hwnd); + } + } + break; + } + } + break; + case WM_REFLECT + WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_CUSTOMDRAW: + { + if (header->hwndFrom == hwnd) + { + LPNMLVCUSTOMDRAW customDraw = (LPNMLVCUSTOMDRAW)header; + + switch (customDraw->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + { + BOOLEAN colorChanged = FALSE; + HFONT newFont = NULL; + + if (context->ItemColorFunction) + { + customDraw->clrTextBk = context->ItemColorFunction( + (INT)customDraw->nmcd.dwItemSpec, + (PVOID)customDraw->nmcd.lItemlParam, + context->Context + ); + colorChanged = TRUE; + } + + if (context->ItemFontFunction) + { + newFont = context->ItemFontFunction( + (INT)customDraw->nmcd.dwItemSpec, + (PVOID)customDraw->nmcd.lItemlParam, + context->Context + ); + } + + if (newFont) + SelectFont(customDraw->nmcd.hdc, newFont); + + if (colorChanged) + { + if (PhGetColorBrightness(customDraw->clrTextBk) > 100) // slightly less than half + customDraw->clrText = RGB(0x00, 0x00, 0x00); + else + customDraw->clrText = RGB(0xff, 0xff, 0xff); + } + + if (!newFont) + return CDRF_DODEFAULT; + else + return CDRF_NEWFONT; + } + break; + } + } + } + break; + } + } + break; + case WM_SETCURSOR: + { + if (context->Cursor) + { + SetCursor(context->Cursor); + return TRUE; + } + } + break; + case WM_UPDATEUISTATE: + { + // Disable focus rectangles by setting or masking out the flag where appropriate. + switch (LOWORD(wParam)) + { + case UIS_SET: + wParam |= UISF_HIDEFOCUS << 16; + break; + case UIS_CLEAR: + case UIS_INITIALIZE: + wParam &= ~(UISF_HIDEFOCUS << 16); + break; + } + } + break; + case ELVM_ADDFALLBACKCOLUMN: + { + if (context->NumberOfFallbackColumns < PH_MAX_COMPARE_FUNCTIONS) + context->FallbackColumns[context->NumberOfFallbackColumns++] = (ULONG)wParam; + else + return FALSE; + } + return TRUE; + case ELVM_ADDFALLBACKCOLUMNS: + { + ULONG numberOfColumns = (ULONG)wParam; + PULONG columns = (PULONG)lParam; + + if (context->NumberOfFallbackColumns + numberOfColumns <= PH_MAX_COMPARE_FUNCTIONS) + { + memcpy( + &context->FallbackColumns[context->NumberOfFallbackColumns], + columns, + numberOfColumns * sizeof(ULONG) + ); + context->NumberOfFallbackColumns += numberOfColumns; + } + else + { + return FALSE; + } + } + return TRUE; + case ELVM_INIT: + { + PhSetHeaderSortIcon(ListView_GetHeader(hwnd), context->SortColumn, context->SortOrder); + + // HACK to fix tooltips showing behind Always On Top windows. + SetWindowPos(ListView_GetToolTips(hwnd), HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + // Make sure focus rectangles are disabled. + SendMessage(hwnd, WM_CHANGEUISTATE, MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0); + } + return TRUE; + case ELVM_SETCOLUMNWIDTH: + { + ULONG column = (ULONG)wParam; + LONG width = (LONG)lParam; + + if (width == ELVSCW_AUTOSIZE_REMAININGSPACE) + { + RECT clientRect; + LONG availableWidth; + ULONG i; + LVCOLUMN lvColumn; + + GetClientRect(hwnd, &clientRect); + availableWidth = clientRect.right; + i = 0; + lvColumn.mask = LVCF_WIDTH; + + while (TRUE) + { + if (i != column) + { + if (CallWindowProc(oldWndProc, hwnd, LVM_GETCOLUMN, i, (LPARAM)&lvColumn)) + { + availableWidth -= lvColumn.cx; + } + else + { + break; + } + } + + i++; + } + + if (availableWidth >= 40) + return CallWindowProc(oldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, availableWidth); + } + + return CallWindowProc(oldWndProc, hwnd, LVM_SETCOLUMNWIDTH, column, width); + } + break; + case ELVM_SETCOMPAREFUNCTION: + { + ULONG column = (ULONG)wParam; + PPH_COMPARE_FUNCTION compareFunction = (PPH_COMPARE_FUNCTION)lParam; + + if (column >= PH_MAX_COMPARE_FUNCTIONS) + return FALSE; + + context->CompareFunctions[column] = compareFunction; + } + return TRUE; + case ELVM_SETCONTEXT: + { + context->Context = (PVOID)lParam; + } + return TRUE; + case ELVM_SETCURSOR: + { + context->Cursor = (HCURSOR)lParam; + } + return TRUE; + case ELVM_SETITEMCOLORFUNCTION: + { + context->ItemColorFunction = (PPH_EXTLV_GET_ITEM_COLOR)lParam; + } + return TRUE; + case ELVM_SETITEMFONTFUNCTION: + { + context->ItemFontFunction = (PPH_EXTLV_GET_ITEM_FONT)lParam; + } + return TRUE; + case ELVM_SETREDRAW: + { + if (wParam) + context->EnableRedraw++; + else + context->EnableRedraw--; + + if (context->EnableRedraw == 1) + { + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + InvalidateRect(hwnd, NULL, FALSE); + } + else if (context->EnableRedraw == 0) + { + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + } + } + return TRUE; + case ELVM_GETSORT: + { + PULONG sortColumn = (PULONG)wParam; + PPH_SORT_ORDER sortOrder = (PPH_SORT_ORDER)lParam; + + if (sortColumn) + *sortColumn = context->SortColumn; + if (sortOrder) + *sortOrder = context->SortOrder; + } + return TRUE; + case ELVM_SETSORT: + { + context->SortColumn = (ULONG)wParam; + context->SortOrder = (PH_SORT_ORDER)lParam; + + PhSetHeaderSortIcon(ListView_GetHeader(hwnd), context->SortColumn, context->SortOrder); + } + return TRUE; + case ELVM_SETSORTFAST: + { + context->SortFast = !!wParam; + } + return TRUE; + case ELVM_SETTRISTATE: + { + context->TriState = !!wParam; + } + return TRUE; + case ELVM_SETTRISTATECOMPAREFUNCTION: + { + context->TriStateCompareFunction = (PPH_COMPARE_FUNCTION)lParam; + } + return TRUE; + case ELVM_SORTITEMS: + { + if (context->SortFast) + { + // This sort method is faster than the normal sort because our comparison function + // doesn't have to call the list view window procedure to get the item lParam + // values. The disadvantage of this method is that default sorting is not available + // - if a column doesn't have a comparison function, it doesn't get sorted at all. + + ListView_SortItems( + hwnd, + PhpExtendedListViewCompareFastFunc, + (LPARAM)context + ); + } + else + { + ListView_SortItemsEx( + hwnd, + PhpExtendedListViewCompareFunc, + (LPARAM)context + ); + } + } + return TRUE; + } + + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); +} + +/** + * Visually indicates the sort order of a header control item. + * + * \param hwnd A handle to the header control. + * \param Index The index of the item. + * \param Order The sort order of the item. + */ +VOID PhSetHeaderSortIcon( + _In_ HWND hwnd, + _In_ INT Index, + _In_ PH_SORT_ORDER Order + ) +{ + ULONG count; + ULONG i; + + count = Header_GetItemCount(hwnd); + + if (count == -1) + return; + + for (i = 0; i < count; i++) + { + HDITEM item; + + item.mask = HDI_FORMAT; + Header_GetItem(hwnd, i, &item); + + if (Order != NoSortOrder && i == Index) + { + if (Order == AscendingSortOrder) + { + item.fmt &= ~HDF_SORTDOWN; + item.fmt |= HDF_SORTUP; + } + else if (Order == DescendingSortOrder) + { + item.fmt &= ~HDF_SORTUP; + item.fmt |= HDF_SORTDOWN; + } + } + else + { + item.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); + } + + Header_SetItem(hwnd, i, &item); + } +} + +static INT PhpExtendedListViewCompareFunc( + _In_ LPARAM lParam1, + _In_ LPARAM lParam2, + _In_ LPARAM lParamSort + ) +{ + PPH_EXTLV_CONTEXT context = (PPH_EXTLV_CONTEXT)lParamSort; + INT result; + INT x = (INT)lParam1; + INT y = (INT)lParam2; + ULONG i; + PULONG fallbackColumns; + LVITEM xItem; + LVITEM yItem; + + // Get the param values. + + xItem.mask = LVIF_PARAM | LVIF_STATE; + xItem.iItem = x; + xItem.iSubItem = 0; + + yItem.mask = LVIF_PARAM | LVIF_STATE; + yItem.iItem = y; + yItem.iSubItem = 0; + + if (!CallWindowProc(context->OldWndProc, context->Handle, LVM_GETITEM, 0, (LPARAM)&xItem)) + return 0; + if (!CallWindowProc(context->OldWndProc, context->Handle, LVM_GETITEM, 0, (LPARAM)&yItem)) + return 0; + + // First, do tri-state sorting. + + if ( + context->TriState && + context->SortOrder == NoSortOrder && + context->TriStateCompareFunction + ) + { + result = context->TriStateCompareFunction( + (PVOID)xItem.lParam, + (PVOID)yItem.lParam, + context->Context + ); + + if (result != 0) + return result; + } + + // Compare using the user-selected column and move on to the fallback columns if necessary. + + result = PhpCompareListViewItems(context, x, y, (PVOID)xItem.lParam, (PVOID)yItem.lParam, context->SortColumn, TRUE); + + if (result != 0) + return result; + + fallbackColumns = context->FallbackColumns; + + for (i = context->NumberOfFallbackColumns; i != 0; i--) + { + ULONG fallbackColumn = *fallbackColumns++; + + if (fallbackColumn == context->SortColumn) + continue; + + result = PhpCompareListViewItems(context, x, y, (PVOID)xItem.lParam, (PVOID)yItem.lParam, fallbackColumn, TRUE); + + if (result != 0) + return result; + } + + return 0; +} + +static INT PhpExtendedListViewCompareFastFunc( + _In_ LPARAM lParam1, + _In_ LPARAM lParam2, + _In_ LPARAM lParamSort + ) +{ + PPH_EXTLV_CONTEXT context = (PPH_EXTLV_CONTEXT)lParamSort; + INT result; + ULONG i; + PULONG fallbackColumns; + + if (!lParam1 || !lParam2) + return 0; + + // First, do tri-state sorting. + + if ( + context->TriState && + context->SortOrder == NoSortOrder && + context->TriStateCompareFunction + ) + { + result = context->TriStateCompareFunction( + (PVOID)lParam1, + (PVOID)lParam2, + context->Context + ); + + if (result != 0) + return result; + } + + // Compare using the user-selected column and move on to the fallback columns if necessary. + + result = PhpCompareListViewItems(context, 0, 0, (PVOID)lParam1, (PVOID)lParam2, context->SortColumn, FALSE); + + if (result != 0) + return result; + + fallbackColumns = context->FallbackColumns; + + for (i = context->NumberOfFallbackColumns; i != 0; i--) + { + ULONG fallbackColumn = *fallbackColumns++; + + if (fallbackColumn == context->SortColumn) + continue; + + result = PhpCompareListViewItems(context, 0, 0, (PVOID)lParam1, (PVOID)lParam2, fallbackColumn, FALSE); + + if (result != 0) + return result; + } + + return 0; +} + +static FORCEINLINE INT PhpCompareListViewItems( + _In_ PPH_EXTLV_CONTEXT Context, + _In_ INT X, + _In_ INT Y, + _In_ PVOID XParam, + _In_ PVOID YParam, + _In_ ULONG Column, + _In_ BOOLEAN EnableDefault + ) +{ + INT result = 0; + + if ( + Column < PH_MAX_COMPARE_FUNCTIONS && + Context->CompareFunctions[Column] + ) + { + result = PhModifySort( + Context->CompareFunctions[Column](XParam, YParam, Context->Context), + Context->SortOrder + ); + + if (result != 0) + return result; + } + + if (EnableDefault) + { + return PhModifySort( + PhpDefaultCompareListViewItems(Context, X, Y, Column), + Context->SortOrder + ); + } + else + { + return 0; + } +} + +static INT PhpDefaultCompareListViewItems( + _In_ PPH_EXTLV_CONTEXT Context, + _In_ INT X, + _In_ INT Y, + _In_ ULONG Column + ) +{ + WCHAR xText[MAX_PATH + 1]; + WCHAR yText[MAX_PATH + 1]; + LVITEM item; + + // Get the X item text. + + item.mask = LVIF_TEXT; + item.iItem = X; + item.iSubItem = Column; + item.pszText = xText; + item.cchTextMax = MAX_PATH; + + xText[0] = UNICODE_NULL; + CallWindowProc(Context->OldWndProc, Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); + + // Get the Y item text. + + item.iItem = Y; + item.pszText = yText; + item.cchTextMax = MAX_PATH; + + yText[0] = UNICODE_NULL; + CallWindowProc(Context->OldWndProc, Context->Handle, LVM_GETITEM, 0, (LPARAM)&item); + + // Compare them. + +#if 1 + return PhCompareStringZNatural(xText, yText, TRUE); +#else + return _wcsicmp(xText, yText); +#endif +} diff --git a/phlib/fastlock.c b/phlib/fastlock.c index f673ac2288d7..87b5db33a4ba 100644 --- a/phlib/fastlock.c +++ b/phlib/fastlock.c @@ -1,384 +1,384 @@ -/* - * Process Hacker - - * fast resource lock - * - * Copyright (C) 2009-2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -// FastLock is a port of FastResourceLock from PH 1.x. -// -// The code contains no comments because it is a direct port. Please see FastResourceLock.cs in PH -// 1.x for details. - -// The fast lock is around 7% faster than the critical section when there is no contention, when -// used solely for mutual exclusion. It is also much smaller than the critical section. - -#define PH_LOCK_OWNED 0x1 -#define PH_LOCK_EXCLUSIVE_WAKING 0x2 - -#define PH_LOCK_SHARED_OWNERS_SHIFT 2 -#define PH_LOCK_SHARED_OWNERS_MASK 0x3ff -#define PH_LOCK_SHARED_OWNERS_INC 0x4 - -#define PH_LOCK_SHARED_WAITERS_SHIFT 12 -#define PH_LOCK_SHARED_WAITERS_MASK 0x3ff -#define PH_LOCK_SHARED_WAITERS_INC 0x1000 - -#define PH_LOCK_EXCLUSIVE_WAITERS_SHIFT 22 -#define PH_LOCK_EXCLUSIVE_WAITERS_MASK 0x3ff -#define PH_LOCK_EXCLUSIVE_WAITERS_INC 0x400000 - -#define PH_LOCK_EXCLUSIVE_MASK \ - (PH_LOCK_EXCLUSIVE_WAKING | \ - (PH_LOCK_EXCLUSIVE_WAITERS_MASK << PH_LOCK_EXCLUSIVE_WAITERS_SHIFT)) - -VOID PhInitializeFastLock( - _Out_ PPH_FAST_LOCK FastLock - ) -{ - FastLock->Value = 0; - FastLock->ExclusiveWakeEvent = NULL; - FastLock->SharedWakeEvent = NULL; -} - -VOID PhDeleteFastLock( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - if (FastLock->ExclusiveWakeEvent) - { - NtClose(FastLock->ExclusiveWakeEvent); - FastLock->ExclusiveWakeEvent = NULL; - } - - if (FastLock->SharedWakeEvent) - { - NtClose(FastLock->SharedWakeEvent); - FastLock->SharedWakeEvent = NULL; - } -} - -FORCEINLINE VOID PhpEnsureEventCreated( - _Inout_ PHANDLE Handle - ) -{ - HANDLE handle; - - if (*Handle != NULL) - return; - - NtCreateSemaphore(&handle, SEMAPHORE_ALL_ACCESS, NULL, 0, MAXLONG); - - if (_InterlockedCompareExchangePointer( - Handle, - handle, - NULL - ) != NULL) - { - NtClose(handle); - } -} - -FORCEINLINE ULONG PhpGetSpinCount( - VOID - ) -{ - if ((ULONG)PhSystemBasicInformation.NumberOfProcessors > 1) - return 4000; - else - return 0; -} - -_May_raise_ -_Acquires_exclusive_lock_(*FastLock) -VOID FASTCALL PhfAcquireFastLockExclusive( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - ULONG value; - ULONG i = 0; - ULONG spinCount; - - spinCount = PhpGetSpinCount(); - - while (TRUE) - { - value = FastLock->Value; - - if (!(value & (PH_LOCK_OWNED | PH_LOCK_EXCLUSIVE_WAKING))) - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_OWNED, - value - ) == value) - break; - } - else if (i >= spinCount) - { - PhpEnsureEventCreated(&FastLock->ExclusiveWakeEvent); - - if (_InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_EXCLUSIVE_WAITERS_INC, - value - ) == value) - { - if (NtWaitForSingleObject( - FastLock->ExclusiveWakeEvent, - FALSE, - NULL - ) != STATUS_WAIT_0) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - - do - { - value = FastLock->Value; - } while (_InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_OWNED - PH_LOCK_EXCLUSIVE_WAKING, - value - ) != value); - - break; - } - } - - i++; - YieldProcessor(); - } -} - -_May_raise_ -_Acquires_shared_lock_(*FastLock) -VOID FASTCALL PhfAcquireFastLockShared( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - ULONG value; - ULONG i = 0; - ULONG spinCount; - - spinCount = PhpGetSpinCount(); - - while (TRUE) - { - value = FastLock->Value; - - if (!(value & ( - PH_LOCK_OWNED | - (PH_LOCK_SHARED_OWNERS_MASK << PH_LOCK_SHARED_OWNERS_SHIFT) | - PH_LOCK_EXCLUSIVE_MASK - ))) - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_OWNED + PH_LOCK_SHARED_OWNERS_INC, - value - ) == value) - break; - } - else if ( - (value & PH_LOCK_OWNED) && - ((value >> PH_LOCK_SHARED_OWNERS_SHIFT) & PH_LOCK_SHARED_OWNERS_MASK) > 0 && - !(value & PH_LOCK_EXCLUSIVE_MASK) - ) - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_SHARED_OWNERS_INC, - value - ) == value) - break; - } - else if (i >= spinCount) - { - PhpEnsureEventCreated(&FastLock->SharedWakeEvent); - - if (_InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_SHARED_WAITERS_INC, - value - ) == value) - { - if (NtWaitForSingleObject( - FastLock->SharedWakeEvent, - FALSE, - NULL - ) != STATUS_WAIT_0) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - - continue; - } - } - - i++; - YieldProcessor(); - } -} - -_Releases_exclusive_lock_(*FastLock) -VOID FASTCALL PhfReleaseFastLockExclusive( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - ULONG value; - - while (TRUE) - { - value = FastLock->Value; - - if ((value >> PH_LOCK_EXCLUSIVE_WAITERS_SHIFT) & PH_LOCK_EXCLUSIVE_WAITERS_MASK) - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value - PH_LOCK_OWNED + PH_LOCK_EXCLUSIVE_WAKING - PH_LOCK_EXCLUSIVE_WAITERS_INC, - value - ) == value) - { - NtReleaseSemaphore(FastLock->ExclusiveWakeEvent, 1, NULL); - - break; - } - } - else - { - ULONG sharedWaiters; - - sharedWaiters = (value >> PH_LOCK_SHARED_WAITERS_SHIFT) & PH_LOCK_SHARED_WAITERS_MASK; - - if (_InterlockedCompareExchange( - &FastLock->Value, - value & ~(PH_LOCK_OWNED | (PH_LOCK_SHARED_WAITERS_MASK << PH_LOCK_SHARED_WAITERS_SHIFT)), - value - ) == value) - { - if (sharedWaiters) - NtReleaseSemaphore(FastLock->SharedWakeEvent, sharedWaiters, 0); - - break; - } - } - - YieldProcessor(); - } -} - -_Releases_shared_lock_(*FastLock) -VOID FASTCALL PhfReleaseFastLockShared( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - ULONG value; - - while (TRUE) - { - value = FastLock->Value; - - if (((value >> PH_LOCK_SHARED_OWNERS_SHIFT) & PH_LOCK_SHARED_OWNERS_MASK) > 1) - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value - PH_LOCK_SHARED_OWNERS_INC, - value - ) == value) - break; - } - else if ((value >> PH_LOCK_EXCLUSIVE_WAITERS_SHIFT) & PH_LOCK_EXCLUSIVE_WAITERS_MASK) - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value - PH_LOCK_OWNED + PH_LOCK_EXCLUSIVE_WAKING - - PH_LOCK_SHARED_OWNERS_INC - PH_LOCK_EXCLUSIVE_WAITERS_INC, - value - ) == value) - { - NtReleaseSemaphore(FastLock->ExclusiveWakeEvent, 1, NULL); - - break; - } - } - else - { - if (_InterlockedCompareExchange( - &FastLock->Value, - value - PH_LOCK_OWNED - PH_LOCK_SHARED_OWNERS_INC, - value - ) == value) - break; - } - - YieldProcessor(); - } -} - -_When_(return != 0, _Acquires_exclusive_lock_(*FastLock)) -BOOLEAN FASTCALL PhfTryAcquireFastLockExclusive( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - ULONG value; - - value = FastLock->Value; - - if (value & (PH_LOCK_OWNED | PH_LOCK_EXCLUSIVE_WAKING)) - return FALSE; - - return _InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_OWNED, - value - ) == value; -} - -_When_(return != 0, _Acquires_shared_lock_(*FastLock)) -BOOLEAN FASTCALL PhfTryAcquireFastLockShared( - _Inout_ PPH_FAST_LOCK FastLock - ) -{ - ULONG value; - - value = FastLock->Value; - - if (value & PH_LOCK_EXCLUSIVE_MASK) - return FALSE; - - if (!(value & PH_LOCK_OWNED)) - { - return _InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_OWNED + PH_LOCK_SHARED_OWNERS_INC, - value - ) == value; - } - else if ((value >> PH_LOCK_SHARED_OWNERS_SHIFT) & PH_LOCK_SHARED_OWNERS_MASK) - { - return _InterlockedCompareExchange( - &FastLock->Value, - value + PH_LOCK_SHARED_OWNERS_INC, - value - ) == value; - } - else - { - return FALSE; - } -} +/* + * Process Hacker - + * fast resource lock + * + * Copyright (C) 2009-2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +// FastLock is a port of FastResourceLock from PH 1.x. +// +// The code contains no comments because it is a direct port. Please see FastResourceLock.cs in PH +// 1.x for details. + +// The fast lock is around 7% faster than the critical section when there is no contention, when +// used solely for mutual exclusion. It is also much smaller than the critical section. + +#define PH_LOCK_OWNED 0x1 +#define PH_LOCK_EXCLUSIVE_WAKING 0x2 + +#define PH_LOCK_SHARED_OWNERS_SHIFT 2 +#define PH_LOCK_SHARED_OWNERS_MASK 0x3ff +#define PH_LOCK_SHARED_OWNERS_INC 0x4 + +#define PH_LOCK_SHARED_WAITERS_SHIFT 12 +#define PH_LOCK_SHARED_WAITERS_MASK 0x3ff +#define PH_LOCK_SHARED_WAITERS_INC 0x1000 + +#define PH_LOCK_EXCLUSIVE_WAITERS_SHIFT 22 +#define PH_LOCK_EXCLUSIVE_WAITERS_MASK 0x3ff +#define PH_LOCK_EXCLUSIVE_WAITERS_INC 0x400000 + +#define PH_LOCK_EXCLUSIVE_MASK \ + (PH_LOCK_EXCLUSIVE_WAKING | \ + (PH_LOCK_EXCLUSIVE_WAITERS_MASK << PH_LOCK_EXCLUSIVE_WAITERS_SHIFT)) + +VOID PhInitializeFastLock( + _Out_ PPH_FAST_LOCK FastLock + ) +{ + FastLock->Value = 0; + FastLock->ExclusiveWakeEvent = NULL; + FastLock->SharedWakeEvent = NULL; +} + +VOID PhDeleteFastLock( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + if (FastLock->ExclusiveWakeEvent) + { + NtClose(FastLock->ExclusiveWakeEvent); + FastLock->ExclusiveWakeEvent = NULL; + } + + if (FastLock->SharedWakeEvent) + { + NtClose(FastLock->SharedWakeEvent); + FastLock->SharedWakeEvent = NULL; + } +} + +FORCEINLINE VOID PhpEnsureEventCreated( + _Inout_ PHANDLE Handle + ) +{ + HANDLE handle; + + if (*Handle != NULL) + return; + + NtCreateSemaphore(&handle, SEMAPHORE_ALL_ACCESS, NULL, 0, MAXLONG); + + if (_InterlockedCompareExchangePointer( + Handle, + handle, + NULL + ) != NULL) + { + NtClose(handle); + } +} + +FORCEINLINE ULONG PhpGetSpinCount( + VOID + ) +{ + if ((ULONG)PhSystemBasicInformation.NumberOfProcessors > 1) + return 4000; + else + return 0; +} + +_May_raise_ +_Acquires_exclusive_lock_(*FastLock) +VOID FASTCALL PhfAcquireFastLockExclusive( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + ULONG value; + ULONG i = 0; + ULONG spinCount; + + spinCount = PhpGetSpinCount(); + + while (TRUE) + { + value = FastLock->Value; + + if (!(value & (PH_LOCK_OWNED | PH_LOCK_EXCLUSIVE_WAKING))) + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_OWNED, + value + ) == value) + break; + } + else if (i >= spinCount) + { + PhpEnsureEventCreated(&FastLock->ExclusiveWakeEvent); + + if (_InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_EXCLUSIVE_WAITERS_INC, + value + ) == value) + { + if (NtWaitForSingleObject( + FastLock->ExclusiveWakeEvent, + FALSE, + NULL + ) != STATUS_WAIT_0) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + + do + { + value = FastLock->Value; + } while (_InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_OWNED - PH_LOCK_EXCLUSIVE_WAKING, + value + ) != value); + + break; + } + } + + i++; + YieldProcessor(); + } +} + +_May_raise_ +_Acquires_shared_lock_(*FastLock) +VOID FASTCALL PhfAcquireFastLockShared( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + ULONG value; + ULONG i = 0; + ULONG spinCount; + + spinCount = PhpGetSpinCount(); + + while (TRUE) + { + value = FastLock->Value; + + if (!(value & ( + PH_LOCK_OWNED | + (PH_LOCK_SHARED_OWNERS_MASK << PH_LOCK_SHARED_OWNERS_SHIFT) | + PH_LOCK_EXCLUSIVE_MASK + ))) + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_OWNED + PH_LOCK_SHARED_OWNERS_INC, + value + ) == value) + break; + } + else if ( + (value & PH_LOCK_OWNED) && + ((value >> PH_LOCK_SHARED_OWNERS_SHIFT) & PH_LOCK_SHARED_OWNERS_MASK) > 0 && + !(value & PH_LOCK_EXCLUSIVE_MASK) + ) + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_SHARED_OWNERS_INC, + value + ) == value) + break; + } + else if (i >= spinCount) + { + PhpEnsureEventCreated(&FastLock->SharedWakeEvent); + + if (_InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_SHARED_WAITERS_INC, + value + ) == value) + { + if (NtWaitForSingleObject( + FastLock->SharedWakeEvent, + FALSE, + NULL + ) != STATUS_WAIT_0) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + + continue; + } + } + + i++; + YieldProcessor(); + } +} + +_Releases_exclusive_lock_(*FastLock) +VOID FASTCALL PhfReleaseFastLockExclusive( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + ULONG value; + + while (TRUE) + { + value = FastLock->Value; + + if ((value >> PH_LOCK_EXCLUSIVE_WAITERS_SHIFT) & PH_LOCK_EXCLUSIVE_WAITERS_MASK) + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value - PH_LOCK_OWNED + PH_LOCK_EXCLUSIVE_WAKING - PH_LOCK_EXCLUSIVE_WAITERS_INC, + value + ) == value) + { + NtReleaseSemaphore(FastLock->ExclusiveWakeEvent, 1, NULL); + + break; + } + } + else + { + ULONG sharedWaiters; + + sharedWaiters = (value >> PH_LOCK_SHARED_WAITERS_SHIFT) & PH_LOCK_SHARED_WAITERS_MASK; + + if (_InterlockedCompareExchange( + &FastLock->Value, + value & ~(PH_LOCK_OWNED | (PH_LOCK_SHARED_WAITERS_MASK << PH_LOCK_SHARED_WAITERS_SHIFT)), + value + ) == value) + { + if (sharedWaiters) + NtReleaseSemaphore(FastLock->SharedWakeEvent, sharedWaiters, 0); + + break; + } + } + + YieldProcessor(); + } +} + +_Releases_shared_lock_(*FastLock) +VOID FASTCALL PhfReleaseFastLockShared( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + ULONG value; + + while (TRUE) + { + value = FastLock->Value; + + if (((value >> PH_LOCK_SHARED_OWNERS_SHIFT) & PH_LOCK_SHARED_OWNERS_MASK) > 1) + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value - PH_LOCK_SHARED_OWNERS_INC, + value + ) == value) + break; + } + else if ((value >> PH_LOCK_EXCLUSIVE_WAITERS_SHIFT) & PH_LOCK_EXCLUSIVE_WAITERS_MASK) + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value - PH_LOCK_OWNED + PH_LOCK_EXCLUSIVE_WAKING - + PH_LOCK_SHARED_OWNERS_INC - PH_LOCK_EXCLUSIVE_WAITERS_INC, + value + ) == value) + { + NtReleaseSemaphore(FastLock->ExclusiveWakeEvent, 1, NULL); + + break; + } + } + else + { + if (_InterlockedCompareExchange( + &FastLock->Value, + value - PH_LOCK_OWNED - PH_LOCK_SHARED_OWNERS_INC, + value + ) == value) + break; + } + + YieldProcessor(); + } +} + +_When_(return != 0, _Acquires_exclusive_lock_(*FastLock)) +BOOLEAN FASTCALL PhfTryAcquireFastLockExclusive( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + ULONG value; + + value = FastLock->Value; + + if (value & (PH_LOCK_OWNED | PH_LOCK_EXCLUSIVE_WAKING)) + return FALSE; + + return _InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_OWNED, + value + ) == value; +} + +_When_(return != 0, _Acquires_shared_lock_(*FastLock)) +BOOLEAN FASTCALL PhfTryAcquireFastLockShared( + _Inout_ PPH_FAST_LOCK FastLock + ) +{ + ULONG value; + + value = FastLock->Value; + + if (value & PH_LOCK_EXCLUSIVE_MASK) + return FALSE; + + if (!(value & PH_LOCK_OWNED)) + { + return _InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_OWNED + PH_LOCK_SHARED_OWNERS_INC, + value + ) == value; + } + else if ((value >> PH_LOCK_SHARED_OWNERS_SHIFT) & PH_LOCK_SHARED_OWNERS_MASK) + { + return _InterlockedCompareExchange( + &FastLock->Value, + value + PH_LOCK_SHARED_OWNERS_INC, + value + ) == value; + } + else + { + return FALSE; + } +} diff --git a/phlib/filepool.c b/phlib/filepool.c index 3d9e95ca6fe1..2933a7b5ad4d 100644 --- a/phlib/filepool.c +++ b/phlib/filepool.c @@ -1,1511 +1,1511 @@ -/* - * Process Hacker - - * file-based allocator - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * File pool allows blocks of storage to be allocated from a file. Each file looks like this: - * - * Segment 0 __________________________________________________________ - * | | | | | - * | Block Header | File Header | Block Header | Segment Header | - * |______________|________________|______________|________________| - * | | | | | - * | Block Header | User Data | Block Header | User Data | - * |______________|________________|______________|________________| - * | | | | | - * | ... | ... | ... | ... | - * |______________|________________|______________|________________| - * Segment 1 __________________________________________________________ - * | | | | | - * | Block Header | Segment Header | Block Header | User Data | - * |______________|________________|______________|________________| - * | | | | | - * | ... | ... | ... | ... | - * |______________|________________|______________|________________| - * Segment 2 __________________________________________________________ - * | | | | | - * | ... | ... | ... | ... | - * |______________|________________|______________|________________| - * - */ - -/* - * A file consists of a variable number of segments, with the segment size specified as a power of - * two. Each segment contains a fixed number of blocks, leading to a variable block size. Every - * allocation made by the user is an allocation of a certain number of blocks, with enough space for - * the block header. This is placed at the beginning of each allocation and contains the number of - * blocks in the allocation (a better name for it would be the allocation header). - * - * Block management in each segment is handled by a bitmap which is stored in the segment header at - * the beginning of each segment. The first segment (segment 0) is special with the file header - * being placed immediately after an initial block header. This is because the segment size is - * stored in the file header, and without it we cannot calculate the block size, which is used to - * locate everything else in the file. - * - * To speed up allocations a number of free lists are maintained which categorize each segment based - * on how many free blocks they have. This means we can avoid trying to allocate from every existing - * segment before finding out we have to allocate a new segment, or trying to allocate from segments - * without the required number of free blocks. The downside of this technique is that it doesn't - * account for fragmentation within the allocation bitmap. - * - * Each segment is mapped in separately, and each view is cached. Even after a view becomes inactive - * (has a reference count of 0) it remains mapped in until the maximum number of inactive views is - * reached. - */ - -#include -#include - -#include - -/** - * Creates or opens a file pool. - * - * \param Pool A variable which receives the file pool instance. - * \param FileHandle A handle to the file. - * \param ReadOnly TRUE to disallow writes to the file. - * \param Parameters Parameters for on-disk and runtime structures. - */ -NTSTATUS PhCreateFilePool( - _Out_ PPH_FILE_POOL *Pool, - _In_ HANDLE FileHandle, - _In_ BOOLEAN ReadOnly, - _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters - ) -{ - NTSTATUS status; - PPH_FILE_POOL pool; - LARGE_INTEGER fileSize; - PH_FILE_POOL_PARAMETERS localParameters; - BOOLEAN creating; - HANDLE sectionHandle; - PPH_FP_BLOCK_HEADER initialBlock; - PPH_FP_FILE_HEADER header; - ULONG i; - - if (Parameters) - { - PhpValidateFilePoolParameters(Parameters); - } - else - { - PhpSetDefaultFilePoolParameters(&localParameters); - Parameters = &localParameters; - } - - pool = PhAllocate(sizeof(PH_FILE_POOL)); - memset(pool, 0, sizeof(PH_FILE_POOL)); - - pool->FileHandle = FileHandle; - pool->ReadOnly = ReadOnly; - - if (!NT_SUCCESS(status = PhGetFileSize(FileHandle, &fileSize))) - goto CleanupExit; - - creating = FALSE; - - // If the file is smaller than the page size, assume we're creating a new file. - if (fileSize.QuadPart < PAGE_SIZE) - { - if (ReadOnly) - { - status = STATUS_BAD_FILE_TYPE; - goto CleanupExit; - } - - fileSize.QuadPart = PAGE_SIZE; - - if (!NT_SUCCESS(status = PhSetFileSize(FileHandle, &fileSize))) - goto CleanupExit; - - creating = TRUE; - } - - // Create a section. - status = NtCreateSection( - §ionHandle, - SECTION_ALL_ACCESS, - NULL, - &fileSize, - !ReadOnly ? PAGE_READWRITE : PAGE_READONLY, - SEC_COMMIT, - FileHandle - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - pool->SectionHandle = sectionHandle; - - // Map in the first segment, set up initial parameters, then remap the first segment. - - if (!NT_SUCCESS(status = PhFppMapRange(pool, 0, PAGE_SIZE, &initialBlock))) - goto CleanupExit; - - header = (PPH_FP_FILE_HEADER)&initialBlock->Body; - - if (creating) - { - header->Magic = PH_FP_MAGIC; - header->SegmentShift = Parameters->SegmentShift; - header->SegmentCount = 1; - - for (i = 0; i < PH_FP_FREE_LIST_COUNT; i++) - header->FreeLists[i] = -1; - } - else - { - if (header->Magic != PH_FP_MAGIC) - { - PhFppUnmapRange(pool, initialBlock); - status = STATUS_BAD_FILE_TYPE; - goto CleanupExit; - } - } - - pool->SegmentShift = header->SegmentShift; - pool->SegmentSize = 1 << pool->SegmentShift; - - pool->BlockShift = pool->SegmentShift - PH_FP_BLOCK_COUNT_SHIFT; - pool->BlockSize = 1 << pool->BlockShift; - pool->FileHeaderBlockSpan = (sizeof(PH_FP_FILE_HEADER) + pool->BlockSize - 1) >> pool->BlockShift; - pool->SegmentHeaderBlockSpan = (sizeof(PH_FP_SEGMENT_HEADER) + pool->BlockSize - 1) >> pool->BlockShift; - - // Unmap the first segment and remap with the new segment size. - - PhFppUnmapRange(pool, initialBlock); - - if (creating) - { - // Extend the section so it fits the entire first segment. - if (!NT_SUCCESS(status = PhFppExtendRange(pool, pool->SegmentSize))) - goto CleanupExit; - } - - // Runtime structure initialization - - PhInitializeFreeList(&pool->ViewFreeList, sizeof(PH_FILE_POOL_VIEW), 32); - pool->ByIndexSize = 32; - pool->ByIndexBuckets = PhAllocate(sizeof(PPH_FILE_POOL_VIEW) * pool->ByIndexSize); - memset(pool->ByIndexBuckets, 0, sizeof(PPH_FILE_POOL_VIEW) * pool->ByIndexSize); - PhInitializeAvlTree(&pool->ByBaseSet, PhpFilePoolViewByBaseCompareFunction); - - pool->MaximumInactiveViews = Parameters->MaximumInactiveViews; - InitializeListHead(&pool->InactiveViewsListHead); - - // File structure initialization - - pool->FirstBlockOfFirstSegment = PhFppReferenceSegment(pool, 0); - pool->Header = (PPH_FP_FILE_HEADER)&pool->FirstBlockOfFirstSegment->Body; - - if (creating) - { - PPH_FP_BLOCK_HEADER segmentHeaderBlock; - - // Set up the first segment properly. - - pool->FirstBlockOfFirstSegment->Span = pool->FileHeaderBlockSpan; - segmentHeaderBlock = (PPH_FP_BLOCK_HEADER)((PCHAR)pool->FirstBlockOfFirstSegment + (pool->FileHeaderBlockSpan << pool->BlockShift)); - PhFppInitializeSegment(pool, segmentHeaderBlock, pool->FileHeaderBlockSpan); - - pool->Header->FreeLists[1] = 0; - } - -CleanupExit: - if (NT_SUCCESS(status)) - { - *Pool = pool; - } - else - { - // Don't close the file handle the user passed in. - pool->FileHandle = NULL; - PhDestroyFilePool(pool); - } - - return status; -} - -/** - * Creates or opens a file pool. - * - * \param Pool A variable which receives the file pool instance. - * \param FileName The file name of the file pool. - * \param ReadOnly TRUE to disallow writes to the file. - * \param ShareAccess The file access granted to other threads. - * \param CreateDisposition The action to perform if the file does or does not exist. See - * PhCreateFileWin32() for more information. - * \param Parameters Parameters for on-disk and runtime structures. - */ -NTSTATUS PhCreateFilePool2( - _Out_ PPH_FILE_POOL *Pool, - _In_ PWSTR FileName, - _In_ BOOLEAN ReadOnly, - _In_ ULONG ShareAccess, - _In_ ULONG CreateDisposition, - _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters - ) -{ - NTSTATUS status; - PPH_FILE_POOL pool; - HANDLE fileHandle; - ULONG createStatus; - - if (!NT_SUCCESS(status = PhCreateFileWin32Ex( - &fileHandle, - FileName, - !ReadOnly ? (FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE) : FILE_GENERIC_READ, - FILE_ATTRIBUTE_NORMAL, - ShareAccess, - CreateDisposition, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, - &createStatus - ))) - { - return status; - } - - status = PhCreateFilePool(&pool, fileHandle, ReadOnly, Parameters); - - if (NT_SUCCESS(status)) - { - *Pool = pool; - } - else - { - if (!ReadOnly && createStatus == FILE_CREATED) - { - FILE_DISPOSITION_INFORMATION dispositionInfo; - IO_STATUS_BLOCK isb; - - dispositionInfo.DeleteFile = TRUE; - NtSetInformationFile(fileHandle, &isb, &dispositionInfo, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation); - } - - NtClose(fileHandle); - } - - return status; -} - -/** - * Frees resources used by a file pool instance. - * - * \param Pool The file pool. - */ -VOID PhDestroyFilePool( - _In_ _Post_invalid_ PPH_FILE_POOL Pool - ) -{ - ULONG i; - PLIST_ENTRY head; - PLIST_ENTRY entry; - PPH_FILE_POOL_VIEW view; - - // Unmap all views. - for (i = 0; i < Pool->ByIndexSize; i++) - { - if (head = Pool->ByIndexBuckets[i]) - { - entry = head; - - do - { - view = CONTAINING_RECORD(entry, PH_FILE_POOL_VIEW, ByIndexListEntry); - entry = entry->Flink; - PhFppUnmapRange(Pool, view->Base); - PhFreeToFreeList(&Pool->ViewFreeList, view); - } while (entry != head); - } - } - - if (Pool->ByIndexBuckets) - PhFree(Pool->ByIndexBuckets); - - PhDeleteFreeList(&Pool->ViewFreeList); - - if (Pool->SectionHandle) - NtClose(Pool->SectionHandle); - if (Pool->FileHandle) - NtClose(Pool->FileHandle); - - PhFree(Pool); -} - -/** - * Validates and corrects file pool parameters. - * - * \param Parameters The parameters structure which is validated and modified if necessary. - */ -NTSTATUS PhpValidateFilePoolParameters( - _Inout_ PPH_FILE_POOL_PARAMETERS Parameters - ) -{ - NTSTATUS status = STATUS_SUCCESS; - - // 16 <= SegmentShift <= 28 - - if (Parameters->SegmentShift < 16) - { - Parameters->SegmentShift = 16; - status = STATUS_SOME_NOT_MAPPED; - } - - if (Parameters->SegmentShift > 28) - { - Parameters->SegmentShift = 28; - status = STATUS_SOME_NOT_MAPPED; - } - - return status; -} - -/** - * Creates default file pool parameters. - * - * \param Parameters The parameters structure which receives the default parameter values. - */ -VOID PhpSetDefaultFilePoolParameters( - _Out_ PPH_FILE_POOL_PARAMETERS Parameters - ) -{ - Parameters->SegmentShift = 18; // 256kB - Parameters->MaximumInactiveViews = 128; -} - -/** - * Allocates a block from a file pool. - * - * \param Pool The file pool. - * \param Size The number of bytes to allocate. - * \param Rva A variable which receives the relative virtual address of the allocated block. - * - * \return A pointer to the allocated block. You must call PhDereferenceFilePool() or - * PhDereferenceFilePoolByRva() when you no longer need a reference to the block. - * - * \remarks The returned pointer is not valid beyond the lifetime of the file pool instance. Use the - * relative virtual address if you need a permanent reference to the allocated block. - */ -PVOID PhAllocateFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Size, - _Out_opt_ PULONG Rva - ) -{ - PPH_FP_BLOCK_HEADER blockHeader; - ULONG numberOfBlocks; - PPH_FP_BLOCK_HEADER firstBlock; - PPH_FP_SEGMENT_HEADER segmentHeader; - ULONG freeListIndex; - ULONG freeListLimit; - ULONG segmentIndex; - ULONG nextSegmentIndex; - ULONG newFreeListIndex; - - // Calculate the number of blocks needed for the allocation. - numberOfBlocks = (FIELD_OFFSET(PH_FP_BLOCK_HEADER, Body) + Size + Pool->BlockSize - 1) >> Pool->BlockShift; - - if (numberOfBlocks > PH_FP_BLOCK_COUNT - Pool->SegmentHeaderBlockSpan) - { - // TODO: Perform a large allocation. - return NULL; - } - - // Scan each applicable free list and try to allocate from those segments. - - freeListLimit = PhFppComputeFreeListIndex(Pool, numberOfBlocks); - - for (freeListIndex = 0; freeListIndex <= freeListLimit; freeListIndex++) - { - segmentIndex = Pool->Header->FreeLists[freeListIndex]; - - while (segmentIndex != -1) - { - firstBlock = PhFppReferenceSegment(Pool, segmentIndex); - - if (!firstBlock) - return NULL; - - segmentHeader = PhFppGetHeaderSegment(Pool, firstBlock); - nextSegmentIndex = segmentHeader->FreeFlink; - - blockHeader = PhFppAllocateBlocks(Pool, firstBlock, segmentHeader, numberOfBlocks); - - if (blockHeader) - goto BlockAllocated; - - PhFppDereferenceSegment(Pool, segmentIndex); - segmentIndex = nextSegmentIndex; - } - } - - // No segments have the required number of contiguous free blocks. Allocate a new one. - - firstBlock = PhFppAllocateSegment(Pool, &segmentIndex); - - if (!firstBlock) - return NULL; - - freeListIndex = 0; - segmentHeader = PhFppGetHeaderSegment(Pool, firstBlock); - blockHeader = PhFppAllocateBlocks(Pool, firstBlock, segmentHeader, numberOfBlocks); - - if (!blockHeader) - { - PhFppDereferenceSegment(Pool, segmentIndex); - return NULL; - } - -BlockAllocated: - - // Compute the new free list index of the segment and move it to another free list if necessary. - - newFreeListIndex = PhFppComputeFreeListIndex(Pool, segmentHeader->FreeBlocks); - - if (newFreeListIndex != freeListIndex) - { - PhFppRemoveFreeList(Pool, freeListIndex, segmentIndex, segmentHeader); - PhFppInsertFreeList(Pool, newFreeListIndex, segmentIndex, segmentHeader); - } - - if (Rva) - { - *Rva = PhFppEncodeRva(Pool, segmentIndex, firstBlock, &blockHeader->Body); - } - - return &blockHeader->Body; -} - -/** - * Frees a block. - * - * \param Pool The file pool. - * \param SegmentIndex The index of the segment containing the block. - * \param FirstBlock The first block of the segment containing the block. - * \param Block A pointer to the block. - */ -VOID PhpFreeFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _In_ PVOID Block - ) -{ - PPH_FP_SEGMENT_HEADER segmentHeader; - ULONG oldFreeListIndex; - ULONG newFreeListIndex; - - segmentHeader = PhFppGetHeaderSegment(Pool, FirstBlock); - oldFreeListIndex = PhFppComputeFreeListIndex(Pool, segmentHeader->FreeBlocks); - PhFppFreeBlocks(Pool, FirstBlock, segmentHeader, PhFppGetHeaderBlock(Pool, Block)); - newFreeListIndex = PhFppComputeFreeListIndex(Pool, segmentHeader->FreeBlocks); - - // Move the segment into another free list if needed. - if (newFreeListIndex != oldFreeListIndex) - { - PhFppRemoveFreeList(Pool, oldFreeListIndex, SegmentIndex, segmentHeader); - PhFppInsertFreeList(Pool, newFreeListIndex, SegmentIndex, segmentHeader); - } -} - -/** - * Frees a block allocated by PhAllocateFilePool(). - * - * \param Pool The file pool. - * \param Block A pointer to the block. The pointer is no longer valid after you call this function. - * Do not use PhDereferenceFilePool() or PhDereferenceFilePoolByRva(). - */ -VOID PhFreeFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Block - ) -{ - PPH_FILE_POOL_VIEW view; - PPH_FP_BLOCK_HEADER firstBlock; - - view = PhFppFindViewByBase(Pool, Block); - - if (!view) - PhRaiseStatus(STATUS_INVALID_PARAMETER_2); - - firstBlock = view->Base; - PhpFreeFilePool(Pool, view->SegmentIndex, firstBlock, Block); - PhFppDereferenceView(Pool, view); -} - -/** - * Frees a block allocated by PhAllocateFilePool(). - * - * \param Pool The file pool. - * \param Rva The relative virtual address of the block. - */ -BOOLEAN PhFreeFilePoolByRva( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Rva - ) -{ - ULONG segmentIndex; - ULONG offset; - PPH_FP_BLOCK_HEADER firstBlock; - - offset = PhFppDecodeRva(Pool, Rva, &segmentIndex); - - if (offset == -1) - return FALSE; - - firstBlock = PhFppReferenceSegment(Pool, segmentIndex); - - if (!firstBlock) - return FALSE; - - PhpFreeFilePool(Pool, segmentIndex, firstBlock, (PCHAR)firstBlock + offset); - PhFppDereferenceSegment(Pool, segmentIndex); - - return TRUE; -} - -/** - * Increments the reference count for the specified address. - * - * \param Pool The file pool. - * \param Address An address. - */ -VOID PhReferenceFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Address - ) -{ - PhFppReferenceSegmentByBase(Pool, Address); -} - -/** - * Decrements the reference count for the specified address. - * - * \param Pool The file pool. - * \param Address An address. - */ -VOID PhDereferenceFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Address - ) -{ - PhFppDereferenceSegmentByBase(Pool, Address); -} - -/** - * Obtains a pointer for a relative virtual address, incrementing the reference count of the - * address. - * - * \param Pool The file pool. - * \param Rva A relative virtual address. - */ -PVOID PhReferenceFilePoolByRva( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Rva - ) -{ - ULONG segmentIndex; - ULONG offset; - PPH_FP_BLOCK_HEADER firstBlock; - - if (Rva == 0) - return NULL; - - offset = PhFppDecodeRva(Pool, Rva, &segmentIndex); - - if (offset == -1) - return NULL; - - firstBlock = PhFppReferenceSegment(Pool, segmentIndex); - - if (!firstBlock) - return NULL; - - return (PCHAR)firstBlock + offset; -} - -/** - * Decrements the reference count for the specified relative virtual address. - * - * \param Pool The file pool. - * \param Rva A relative virtual address. - */ -BOOLEAN PhDereferenceFilePoolByRva( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Rva - ) -{ - ULONG segmentIndex; - ULONG offset; - - offset = PhFppDecodeRva(Pool, Rva, &segmentIndex); - - if (offset == -1) - return FALSE; - - PhFppDereferenceSegment(Pool, segmentIndex); - - return TRUE; -} - -/** - * Obtains a relative virtual address for a pointer. - * - * \param Pool The file pool. - * \param Address A pointer. - * - * \return The relative virtual address. - * - * \remarks No reference counts are changed. - */ -ULONG PhEncodeRvaFilePool( - _In_ PPH_FILE_POOL Pool, - _In_ PVOID Address - ) -{ - PPH_FILE_POOL_VIEW view; - - if (!Address) - return 0; - - view = PhFppFindViewByBase(Pool, Address); - - if (!view) - PhRaiseStatus(STATUS_INVALID_PARAMETER_2); - - return PhFppEncodeRva(Pool, view->SegmentIndex, view->Base, Address); -} - -/** - * Retrieves user data. - * - * \param Pool The file pool. - * \param Context A variable which receives the user data. - */ -VOID PhGetUserContextFilePool( - _In_ PPH_FILE_POOL Pool, - _Out_ PULONGLONG Context - ) -{ - *Context = Pool->Header->UserContext; -} - -/** - * Stores user data. - * - * \param Pool The file pool. - * \param Context A variable which contains the user data. - */ -VOID PhSetUserContextFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PULONGLONG Context - ) -{ - Pool->Header->UserContext = *Context; -} - -/** - * Extends a file pool. - * - * \param Pool The file pool. - * \param NewSize The new size of the file, in bytes. - */ -NTSTATUS PhFppExtendRange( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG NewSize - ) -{ - LARGE_INTEGER newSectionSize; - - newSectionSize.QuadPart = NewSize; - - return NtExtendSection(Pool->SectionHandle, &newSectionSize); -} - -/** - * Maps in a view of a file pool. - * - * \param Pool The file pool. - * \param Offset The offset of the view, in bytes. - * \param Size The size of the view, in bytes. - * \param Base A variable which receives the base address of the view. - */ -NTSTATUS PhFppMapRange( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Offset, - _In_ ULONG Size, - _Out_ PVOID *Base - ) -{ - NTSTATUS status; - PVOID baseAddress; - LARGE_INTEGER sectionOffset; - SIZE_T viewSize; - - baseAddress = NULL; - sectionOffset.QuadPart = Offset; - viewSize = Size; - - status = NtMapViewOfSection( - Pool->SectionHandle, - NtCurrentProcess(), - &baseAddress, - 0, - viewSize, - §ionOffset, - &viewSize, - ViewShare, - 0, - !Pool->ReadOnly ? PAGE_READWRITE : PAGE_READONLY - ); - - if (NT_SUCCESS(status)) - *Base = baseAddress; - - return status; -} - -/** - * Unmaps a view of a file pool. - * - * \param Pool The file pool. - * \param Base The base address of the view. - */ -NTSTATUS PhFppUnmapRange( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ) -{ - return NtUnmapViewOfSection(NtCurrentProcess(), Base); -} - -/** - * Initializes a segment. - * - * \param Pool The file pool. - * \param BlockOfSegmentHeader The block header of the span containing the segment header. - * \param AdditionalBlocksUsed The number of blocks already allocated from the segment, excluding - * the blocks comprising the segment header. - */ -VOID PhFppInitializeSegment( - _Inout_ PPH_FILE_POOL Pool, - _Out_ PPH_FP_BLOCK_HEADER BlockOfSegmentHeader, - _In_ ULONG AdditionalBlocksUsed - ) -{ - PPH_FP_SEGMENT_HEADER segmentHeader; - RTL_BITMAP bitmap; - - BlockOfSegmentHeader->Span = Pool->SegmentHeaderBlockSpan; - segmentHeader = (PPH_FP_SEGMENT_HEADER)&BlockOfSegmentHeader->Body; - - RtlInitializeBitMap(&bitmap, segmentHeader->Bitmap, PH_FP_BLOCK_COUNT); - RtlSetBits(&bitmap, 0, Pool->SegmentHeaderBlockSpan + AdditionalBlocksUsed); - segmentHeader->FreeBlocks = PH_FP_BLOCK_COUNT - (Pool->SegmentHeaderBlockSpan + AdditionalBlocksUsed); - segmentHeader->FreeFlink = -1; - segmentHeader->FreeBlink = -1; -} - -/** - * Allocates a segment. - * - * \param Pool The file pool. - * \param NewSegmentIndex A variable which receives the index of the new segment. - * - * \return A pointer to the first block of the segment. - */ -PPH_FP_BLOCK_HEADER PhFppAllocateSegment( - _Inout_ PPH_FILE_POOL Pool, - _Out_ PULONG NewSegmentIndex - ) -{ - ULONG newSize; - ULONG segmentIndex; - PPH_FP_BLOCK_HEADER firstBlock; - PPH_FP_SEGMENT_HEADER segmentHeader; - - newSize = (Pool->Header->SegmentCount + 1) << Pool->SegmentShift; - - if (!NT_SUCCESS(PhFppExtendRange(Pool, newSize))) - return NULL; - - segmentIndex = Pool->Header->SegmentCount++; - firstBlock = PhFppReferenceSegment(Pool, segmentIndex); - PhFppInitializeSegment(Pool, firstBlock, 0); - segmentHeader = (PPH_FP_SEGMENT_HEADER)&firstBlock->Body; - - PhFppInsertFreeList(Pool, 0, segmentIndex, segmentHeader); - - *NewSegmentIndex = segmentIndex; - - return firstBlock; -} - -/** - * Retrieves the header of a segment. - * - * \param Pool The file pool. - * \param FirstBlock The first block of the segment. - */ -PPH_FP_SEGMENT_HEADER PhFppGetHeaderSegment( - _Inout_ PPH_FILE_POOL Pool, - _In_ PPH_FP_BLOCK_HEADER FirstBlock - ) -{ - if (FirstBlock != Pool->FirstBlockOfFirstSegment) - { - return (PPH_FP_SEGMENT_HEADER)&FirstBlock->Body; - } - else - { - // In the first segment, the segment header is after the file header. - return (PPH_FP_SEGMENT_HEADER)&((PPH_FP_BLOCK_HEADER)((PCHAR)FirstBlock + (Pool->FileHeaderBlockSpan << Pool->BlockShift)))->Body; - } -} - -VOID PhFppAddViewByIndex( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - ULONG index; - PLIST_ENTRY head; - - index = View->SegmentIndex & (Pool->ByIndexSize - 1); - head = Pool->ByIndexBuckets[index]; - - if (head) - { - InsertHeadList(head, &View->ByIndexListEntry); - } - else - { - InitializeListHead(&View->ByIndexListEntry); - Pool->ByIndexBuckets[index] = &View->ByIndexListEntry; - } -} - -VOID PhFppRemoveViewByIndex( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - ULONG index; - PLIST_ENTRY head; - - index = View->SegmentIndex & (Pool->ByIndexSize - 1); - head = Pool->ByIndexBuckets[index]; - assert(head); - - // Unlink the entry from the chain. - RemoveEntryList(&View->ByIndexListEntry); - - if (&View->ByIndexListEntry == head) - { - // This entry is currently the chain head. - - // If this was the last entry in the chain, then indicate that the chain is empty. - // Otherwise, choose a new head. - - if (IsListEmpty(head)) - Pool->ByIndexBuckets[index] = NULL; - else - Pool->ByIndexBuckets[index] = head->Flink; - } -} - -/** - * Finds a view for the specified segment. - * - * \param Pool The file pool. - * \param SegmentIndex The index of the segment. - * - * \return The view for the segment, or NULL if no view is present for the segment. - */ -PPH_FILE_POOL_VIEW PhFppFindViewByIndex( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ) -{ - ULONG index; - PLIST_ENTRY head; - PLIST_ENTRY entry; - PPH_FILE_POOL_VIEW view; - - index = SegmentIndex & (Pool->ByIndexSize - 1); - head = Pool->ByIndexBuckets[index]; - - if (!head) - return NULL; - - entry = head; - - do - { - view = CONTAINING_RECORD(entry, PH_FILE_POOL_VIEW, ByIndexListEntry); - - if (view->SegmentIndex == SegmentIndex) - return view; - - entry = entry->Flink; - } while (entry != head); - - return NULL; -} - -LONG NTAPI PhpFilePoolViewByBaseCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ) -{ - PPH_FILE_POOL_VIEW view1 = CONTAINING_RECORD(Links1, PH_FILE_POOL_VIEW, ByBaseLinks); - PPH_FILE_POOL_VIEW view2 = CONTAINING_RECORD(Links2, PH_FILE_POOL_VIEW, ByBaseLinks); - - return uintptrcmp((ULONG_PTR)view1->Base, (ULONG_PTR)view2->Base); -} - -VOID PhFppAddViewByBase( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - PhAddElementAvlTree(&Pool->ByBaseSet, &View->ByBaseLinks); -} - -VOID PhFppRemoveViewByBase( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - PhRemoveElementAvlTree(&Pool->ByBaseSet, &View->ByBaseLinks); -} - -/** - * Finds a view containing the specified address. - * - * \param Pool The file pool. - * \param Base The address. - * - * \return The view containing the address, or NULL if no view is present for the address. - */ -PPH_FILE_POOL_VIEW PhFppFindViewByBase( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ) -{ - PPH_FILE_POOL_VIEW view; - PPH_AVL_LINKS links; - PH_FILE_POOL_VIEW lookupView; - - // This is an approximate search to find the target view in which the specified address lies. - - lookupView.Base = Base; - links = PhUpperDualBoundElementAvlTree(&Pool->ByBaseSet, &lookupView.ByBaseLinks); - - if (!links) - return NULL; - - view = CONTAINING_RECORD(links, PH_FILE_POOL_VIEW, ByBaseLinks); - - if ((ULONG_PTR)Base < (ULONG_PTR)view->Base + Pool->SegmentSize) - return view; - - return NULL; -} - -PPH_FILE_POOL_VIEW PhFppCreateView( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ) -{ - PPH_FILE_POOL_VIEW view; - PVOID base; - - // Map in the segment. - if (!NT_SUCCESS(PhFppMapRange(Pool, SegmentIndex << Pool->SegmentShift, Pool->SegmentSize, &base))) - return NULL; - - // Create and add the view entry. - - view = PhAllocateFromFreeList(&Pool->ViewFreeList); - memset(view, 0, sizeof(PH_FILE_POOL_VIEW)); - - view->RefCount = 1; - view->SegmentIndex = SegmentIndex; - view->Base = base; - - PhFppAddViewByIndex(Pool, view); - PhFppAddViewByBase(Pool, view); - - return view; -} - -VOID PhFppDestroyView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - PhFppUnmapRange(Pool, View->Base); - PhFppRemoveViewByIndex(Pool, View); - PhFppRemoveViewByBase(Pool, View); - - PhFreeToFreeList(&Pool->ViewFreeList, View); -} - -VOID PhFppActivateView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - RemoveEntryList(&View->InactiveViewsListEntry); - Pool->NumberOfInactiveViews--; -} - -VOID PhFppDeactivateView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - InsertHeadList(&Pool->InactiveViewsListHead, &View->InactiveViewsListEntry); - Pool->NumberOfInactiveViews++; - - // If we have too many inactive views, destroy the least recent ones. - while (Pool->NumberOfInactiveViews > Pool->MaximumInactiveViews) - { - PLIST_ENTRY lruEntry; - PPH_FILE_POOL_VIEW lruView; - - lruEntry = RemoveTailList(&Pool->InactiveViewsListHead); - Pool->NumberOfInactiveViews--; - - assert(lruEntry != &Pool->InactiveViewsListHead); - lruView = CONTAINING_RECORD(lruEntry, PH_FILE_POOL_VIEW, InactiveViewsListEntry); - PhFppDestroyView(Pool, lruView); - } -} - -VOID PhFppReferenceView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - if (View->RefCount == 0) - { - // The view is inactive, so make it active. - PhFppActivateView(Pool, View); - } - - View->RefCount++; -} - -VOID PhFppDereferenceView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ) -{ - if (--View->RefCount == 0) - { - if (View->SegmentIndex == 0) - PhRaiseStatus(STATUS_INTERNAL_ERROR); - - PhFppDeactivateView(Pool, View); - } -} - -PPH_FP_BLOCK_HEADER PhFppReferenceSegment( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ) -{ - PPH_FILE_POOL_VIEW view; - - // Validate parameters. - if (SegmentIndex != 0 && SegmentIndex >= Pool->Header->SegmentCount) - return NULL; - - // Try to get a cached view. - - view = PhFppFindViewByIndex(Pool, SegmentIndex); - - if (view) - { - PhFppReferenceView(Pool, view); - - return (PPH_FP_BLOCK_HEADER)view->Base; - } - - // No cached view, so create one. - - view = PhFppCreateView(Pool, SegmentIndex); - - if (!view) - return NULL; - - return (PPH_FP_BLOCK_HEADER)view->Base; -} - -VOID PhFppDereferenceSegment( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ) -{ - PPH_FILE_POOL_VIEW view; - - view = PhFppFindViewByIndex(Pool, SegmentIndex); - - if (!view) - PhRaiseStatus(STATUS_INTERNAL_ERROR); - - PhFppDereferenceView(Pool, view); -} - -VOID PhFppReferenceSegmentByBase( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ) -{ - PPH_FILE_POOL_VIEW view; - - view = PhFppFindViewByBase(Pool, Base); - - if (!view) - PhRaiseStatus(STATUS_INTERNAL_ERROR); - - PhFppReferenceView(Pool, view); -} - -VOID PhFppDereferenceSegmentByBase( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ) -{ - PPH_FILE_POOL_VIEW view; - - view = PhFppFindViewByBase(Pool, Base); - - if (!view) - PhRaiseStatus(STATUS_INTERNAL_ERROR); - - PhFppDereferenceView(Pool, view); -} - -/** - * Allocates blocks from a segment. - * - * \param Pool The file pool. - * \param FirstBlock The first block of the segment. - * \param SegmentHeader The header of the segment. - * \param NumberOfBlocks The number of blocks to allocate. - * - * \return The header of the allocated span, or NULL if there is an insufficient number of - * contiguous free blocks for the allocation. - */ -PPH_FP_BLOCK_HEADER PhFppAllocateBlocks( - _Inout_ PPH_FILE_POOL Pool, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, - _In_ ULONG NumberOfBlocks - ) -{ - RTL_BITMAP bitmap; - ULONG hintIndex; - ULONG foundIndex; - PPH_FP_BLOCK_HEADER blockHeader; - - if (FirstBlock != Pool->FirstBlockOfFirstSegment) - hintIndex = Pool->SegmentHeaderBlockSpan; - else - hintIndex = Pool->SegmentHeaderBlockSpan + Pool->FileHeaderBlockSpan; - - RtlInitializeBitMap(&bitmap, SegmentHeader->Bitmap, PH_FP_BLOCK_COUNT); - - // Find a range of free blocks and mark them as in-use. - foundIndex = RtlFindClearBitsAndSet(&bitmap, NumberOfBlocks, hintIndex); - - if (foundIndex == -1) - { - // No more space. - return NULL; - } - - SegmentHeader->FreeBlocks -= NumberOfBlocks; - - blockHeader = (PPH_FP_BLOCK_HEADER)((PCHAR)FirstBlock + (foundIndex << Pool->BlockShift)); - blockHeader->Flags = 0; - blockHeader->Span = NumberOfBlocks; - - return blockHeader; -} - -/** - * Frees blocks in a segment. - * - * \param Pool The file pool. - * \param FirstBlock The first block of the segment. - * \param SegmentHeader The header of the segment. - * \param BlockHeader The header of the allocated span. - */ -VOID PhFppFreeBlocks( - _Inout_ PPH_FILE_POOL Pool, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, - _In_ PPH_FP_BLOCK_HEADER BlockHeader - ) -{ - RTL_BITMAP bitmap; - ULONG startIndex; - ULONG blockSpan; - - RtlInitializeBitMap(&bitmap, SegmentHeader->Bitmap, PH_FP_BLOCK_COUNT); - - // Mark the blocks as free. - startIndex = (ULONG)((PCHAR)BlockHeader - (PCHAR)FirstBlock) >> Pool->BlockShift; - blockSpan = BlockHeader->Span; - RtlClearBits(&bitmap, startIndex, blockSpan); - SegmentHeader->FreeBlocks += blockSpan; -} - -/** - * Computes the free list index (category) for a specified number of blocks. - * - * \param Pool The file pool. - * \param NumberOfBlocks The number of free or required blocks. - */ -ULONG PhFppComputeFreeListIndex( - _In_ PPH_FILE_POOL Pool, - _In_ ULONG NumberOfBlocks - ) -{ - // Use a binary tree to speed up comparison. - - if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 64) - { - if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 2) - { - if (NumberOfBlocks >= PH_FP_BLOCK_COUNT - Pool->SegmentHeaderBlockSpan) - return 0; - else - return 1; - } - else - { - if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 16) - return 2; - else - return 3; - } - } - else - { - if (NumberOfBlocks >= 4) - { - if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 256) - return 4; - else - return 5; - } - else - { - if (NumberOfBlocks >= 1) - return 6; - else - return 7; - } - } -} - -/** - * Inserts a segment into a free list. - * - * \param Pool The file pool. - * \param FreeListIndex The index of a free list. - * \param SegmentIndex The index of the segment. - * \param SegmentHeader The header of the segment. - */ -BOOLEAN PhFppInsertFreeList( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG FreeListIndex, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_SEGMENT_HEADER SegmentHeader - ) -{ - ULONG oldSegmentIndex; - PPH_FP_BLOCK_HEADER oldSegmentFirstBlock; - PPH_FP_SEGMENT_HEADER oldSegmentHeader; - - oldSegmentIndex = Pool->Header->FreeLists[FreeListIndex]; - - // Try to reference the segment before we commit any changes. - - if (oldSegmentIndex != -1) - { - oldSegmentFirstBlock = PhFppReferenceSegment(Pool, oldSegmentIndex); - - if (!oldSegmentFirstBlock) - return FALSE; - } - - // Insert the segment into the list. - - SegmentHeader->FreeBlink = -1; - SegmentHeader->FreeFlink = oldSegmentIndex; - Pool->Header->FreeLists[FreeListIndex] = SegmentIndex; - - if (oldSegmentIndex != -1) - { - oldSegmentHeader = PhFppGetHeaderSegment(Pool, oldSegmentFirstBlock); - oldSegmentHeader->FreeBlink = SegmentIndex; - PhFppDereferenceSegment(Pool, oldSegmentIndex); - } - - return TRUE; -} - -/** - * Removes a segment from a free list. - * - * \param Pool The file pool. - * \param FreeListIndex The index of a free list. - * \param SegmentIndex The index of the segment. - * \param SegmentHeader The header of the segment. - */ -BOOLEAN PhFppRemoveFreeList( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG FreeListIndex, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_SEGMENT_HEADER SegmentHeader - ) -{ - ULONG flinkSegmentIndex; - PPH_FP_BLOCK_HEADER flinkSegmentFirstBlock; - PPH_FP_SEGMENT_HEADER flinkSegmentHeader; - ULONG blinkSegmentIndex; - PPH_FP_BLOCK_HEADER blinkSegmentFirstBlock; - PPH_FP_SEGMENT_HEADER blinkSegmentHeader; - - flinkSegmentIndex = SegmentHeader->FreeFlink; - blinkSegmentIndex = SegmentHeader->FreeBlink; - - // Try to reference the segments before we commit any changes. - - if (flinkSegmentIndex != -1) - { - flinkSegmentFirstBlock = PhFppReferenceSegment(Pool, flinkSegmentIndex); - - if (!flinkSegmentFirstBlock) - return FALSE; - } - - if (blinkSegmentIndex != -1) - { - blinkSegmentFirstBlock = PhFppReferenceSegment(Pool, blinkSegmentIndex); - - if (!blinkSegmentFirstBlock) - { - if (flinkSegmentIndex != -1) - PhFppDereferenceSegment(Pool, flinkSegmentIndex); - - return FALSE; - } - } - - // Unlink the segment from the list. - - if (flinkSegmentIndex != -1) - { - flinkSegmentHeader = PhFppGetHeaderSegment(Pool, flinkSegmentFirstBlock); - flinkSegmentHeader->FreeBlink = blinkSegmentIndex; - PhFppDereferenceSegment(Pool, flinkSegmentIndex); - } - - if (blinkSegmentIndex != -1) - { - blinkSegmentHeader = PhFppGetHeaderSegment(Pool, blinkSegmentFirstBlock); - blinkSegmentHeader->FreeFlink = flinkSegmentIndex; - PhFppDereferenceSegment(Pool, blinkSegmentIndex); - } - else - { - // The segment was the list head; select a new one. - Pool->Header->FreeLists[FreeListIndex] = flinkSegmentIndex; - } - - return TRUE; -} - -/** - * Retrieves the header of a block. - * - * \param Pool The file pool. - * \param Block A pointer to the body of the block. - */ -PPH_FP_BLOCK_HEADER PhFppGetHeaderBlock( - _In_ PPH_FILE_POOL Pool, - _In_ PVOID Block - ) -{ - return CONTAINING_RECORD(Block, PH_FP_BLOCK_HEADER, Body); -} - -/** - * Creates a relative virtual address. - * - * \param Pool The file pool. - * \param SegmentIndex The index of the segment containing \a Address. - * \param FirstBlock The first block of the segment containing \a Address. - * \param Address An address. - */ -ULONG PhFppEncodeRva( - _In_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _In_ PVOID Address - ) -{ - return (SegmentIndex << Pool->SegmentShift) + (ULONG)((PCHAR)Address - (PCHAR)FirstBlock); -} - -/** - * Decodes a relative virtual address. - * - * \param Pool The file pool. - * \param Rva The relative virtual address. - * \param SegmentIndex A variable which receives the segment index. - * - * \return An offset into the segment specified by \a SegmentIndex, or -1 if \a Rva is invalid. - */ -ULONG PhFppDecodeRva( - _In_ PPH_FILE_POOL Pool, - _In_ ULONG Rva, - _Out_ PULONG SegmentIndex - ) -{ - ULONG segmentIndex; - - segmentIndex = Rva >> Pool->SegmentShift; - - if (segmentIndex >= Pool->Header->SegmentCount) - return -1; - - *SegmentIndex = segmentIndex; - - return Rva & (Pool->SegmentSize - 1); -} +/* + * Process Hacker - + * file-based allocator + * + * Copyright (C) 2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * File pool allows blocks of storage to be allocated from a file. Each file looks like this: + * + * Segment 0 __________________________________________________________ + * | | | | | + * | Block Header | File Header | Block Header | Segment Header | + * |______________|________________|______________|________________| + * | | | | | + * | Block Header | User Data | Block Header | User Data | + * |______________|________________|______________|________________| + * | | | | | + * | ... | ... | ... | ... | + * |______________|________________|______________|________________| + * Segment 1 __________________________________________________________ + * | | | | | + * | Block Header | Segment Header | Block Header | User Data | + * |______________|________________|______________|________________| + * | | | | | + * | ... | ... | ... | ... | + * |______________|________________|______________|________________| + * Segment 2 __________________________________________________________ + * | | | | | + * | ... | ... | ... | ... | + * |______________|________________|______________|________________| + * + */ + +/* + * A file consists of a variable number of segments, with the segment size specified as a power of + * two. Each segment contains a fixed number of blocks, leading to a variable block size. Every + * allocation made by the user is an allocation of a certain number of blocks, with enough space for + * the block header. This is placed at the beginning of each allocation and contains the number of + * blocks in the allocation (a better name for it would be the allocation header). + * + * Block management in each segment is handled by a bitmap which is stored in the segment header at + * the beginning of each segment. The first segment (segment 0) is special with the file header + * being placed immediately after an initial block header. This is because the segment size is + * stored in the file header, and without it we cannot calculate the block size, which is used to + * locate everything else in the file. + * + * To speed up allocations a number of free lists are maintained which categorize each segment based + * on how many free blocks they have. This means we can avoid trying to allocate from every existing + * segment before finding out we have to allocate a new segment, or trying to allocate from segments + * without the required number of free blocks. The downside of this technique is that it doesn't + * account for fragmentation within the allocation bitmap. + * + * Each segment is mapped in separately, and each view is cached. Even after a view becomes inactive + * (has a reference count of 0) it remains mapped in until the maximum number of inactive views is + * reached. + */ + +#include +#include + +#include + +/** + * Creates or opens a file pool. + * + * \param Pool A variable which receives the file pool instance. + * \param FileHandle A handle to the file. + * \param ReadOnly TRUE to disallow writes to the file. + * \param Parameters Parameters for on-disk and runtime structures. + */ +NTSTATUS PhCreateFilePool( + _Out_ PPH_FILE_POOL *Pool, + _In_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters + ) +{ + NTSTATUS status; + PPH_FILE_POOL pool; + LARGE_INTEGER fileSize; + PH_FILE_POOL_PARAMETERS localParameters; + BOOLEAN creating; + HANDLE sectionHandle; + PPH_FP_BLOCK_HEADER initialBlock; + PPH_FP_FILE_HEADER header; + ULONG i; + + if (Parameters) + { + PhpValidateFilePoolParameters(Parameters); + } + else + { + PhpSetDefaultFilePoolParameters(&localParameters); + Parameters = &localParameters; + } + + pool = PhAllocate(sizeof(PH_FILE_POOL)); + memset(pool, 0, sizeof(PH_FILE_POOL)); + + pool->FileHandle = FileHandle; + pool->ReadOnly = ReadOnly; + + if (!NT_SUCCESS(status = PhGetFileSize(FileHandle, &fileSize))) + goto CleanupExit; + + creating = FALSE; + + // If the file is smaller than the page size, assume we're creating a new file. + if (fileSize.QuadPart < PAGE_SIZE) + { + if (ReadOnly) + { + status = STATUS_BAD_FILE_TYPE; + goto CleanupExit; + } + + fileSize.QuadPart = PAGE_SIZE; + + if (!NT_SUCCESS(status = PhSetFileSize(FileHandle, &fileSize))) + goto CleanupExit; + + creating = TRUE; + } + + // Create a section. + status = NtCreateSection( + §ionHandle, + SECTION_ALL_ACCESS, + NULL, + &fileSize, + !ReadOnly ? PAGE_READWRITE : PAGE_READONLY, + SEC_COMMIT, + FileHandle + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + pool->SectionHandle = sectionHandle; + + // Map in the first segment, set up initial parameters, then remap the first segment. + + if (!NT_SUCCESS(status = PhFppMapRange(pool, 0, PAGE_SIZE, &initialBlock))) + goto CleanupExit; + + header = (PPH_FP_FILE_HEADER)&initialBlock->Body; + + if (creating) + { + header->Magic = PH_FP_MAGIC; + header->SegmentShift = Parameters->SegmentShift; + header->SegmentCount = 1; + + for (i = 0; i < PH_FP_FREE_LIST_COUNT; i++) + header->FreeLists[i] = ULONG_MAX; + } + else + { + if (header->Magic != PH_FP_MAGIC) + { + PhFppUnmapRange(pool, initialBlock); + status = STATUS_BAD_FILE_TYPE; + goto CleanupExit; + } + } + + pool->SegmentShift = header->SegmentShift; + pool->SegmentSize = 1 << pool->SegmentShift; + + pool->BlockShift = pool->SegmentShift - PH_FP_BLOCK_COUNT_SHIFT; + pool->BlockSize = 1 << pool->BlockShift; + pool->FileHeaderBlockSpan = (sizeof(PH_FP_FILE_HEADER) + pool->BlockSize - 1) >> pool->BlockShift; + pool->SegmentHeaderBlockSpan = (sizeof(PH_FP_SEGMENT_HEADER) + pool->BlockSize - 1) >> pool->BlockShift; + + // Unmap the first segment and remap with the new segment size. + + PhFppUnmapRange(pool, initialBlock); + + if (creating) + { + // Extend the section so it fits the entire first segment. + if (!NT_SUCCESS(status = PhFppExtendRange(pool, pool->SegmentSize))) + goto CleanupExit; + } + + // Runtime structure initialization + + PhInitializeFreeList(&pool->ViewFreeList, sizeof(PH_FILE_POOL_VIEW), 32); + pool->ByIndexSize = 32; + pool->ByIndexBuckets = PhAllocate(sizeof(PPH_FILE_POOL_VIEW) * pool->ByIndexSize); + memset(pool->ByIndexBuckets, 0, sizeof(PPH_FILE_POOL_VIEW) * pool->ByIndexSize); + PhInitializeAvlTree(&pool->ByBaseSet, PhpFilePoolViewByBaseCompareFunction); + + pool->MaximumInactiveViews = Parameters->MaximumInactiveViews; + InitializeListHead(&pool->InactiveViewsListHead); + + // File structure initialization + + pool->FirstBlockOfFirstSegment = PhFppReferenceSegment(pool, 0); + pool->Header = (PPH_FP_FILE_HEADER)&pool->FirstBlockOfFirstSegment->Body; + + if (creating) + { + PPH_FP_BLOCK_HEADER segmentHeaderBlock; + + // Set up the first segment properly. + + pool->FirstBlockOfFirstSegment->Span = pool->FileHeaderBlockSpan; + segmentHeaderBlock = (PPH_FP_BLOCK_HEADER)PTR_ADD_OFFSET(pool->FirstBlockOfFirstSegment, (pool->FileHeaderBlockSpan << pool->BlockShift)); + PhFppInitializeSegment(pool, segmentHeaderBlock, pool->FileHeaderBlockSpan); + + pool->Header->FreeLists[1] = 0; + } + +CleanupExit: + if (NT_SUCCESS(status)) + { + *Pool = pool; + } + else + { + // Don't close the file handle the user passed in. + pool->FileHandle = NULL; + PhDestroyFilePool(pool); + } + + return status; +} + +/** + * Creates or opens a file pool. + * + * \param Pool A variable which receives the file pool instance. + * \param FileName The file name of the file pool. + * \param ReadOnly TRUE to disallow writes to the file. + * \param ShareAccess The file access granted to other threads. + * \param CreateDisposition The action to perform if the file does or does not exist. See + * PhCreateFileWin32() for more information. + * \param Parameters Parameters for on-disk and runtime structures. + */ +NTSTATUS PhCreateFilePool2( + _Out_ PPH_FILE_POOL *Pool, + _In_ PWSTR FileName, + _In_ BOOLEAN ReadOnly, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters + ) +{ + NTSTATUS status; + PPH_FILE_POOL pool; + HANDLE fileHandle; + ULONG createStatus; + + if (!NT_SUCCESS(status = PhCreateFileWin32Ex( + &fileHandle, + FileName, + !ReadOnly ? (FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE) : FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + ShareAccess, + CreateDisposition, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + &createStatus + ))) + { + return status; + } + + status = PhCreateFilePool(&pool, fileHandle, ReadOnly, Parameters); + + if (NT_SUCCESS(status)) + { + *Pool = pool; + } + else + { + if (!ReadOnly && createStatus == FILE_CREATED) + { + FILE_DISPOSITION_INFORMATION dispositionInfo; + IO_STATUS_BLOCK isb; + + dispositionInfo.DeleteFile = TRUE; + NtSetInformationFile(fileHandle, &isb, &dispositionInfo, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation); + } + + NtClose(fileHandle); + } + + return status; +} + +/** + * Frees resources used by a file pool instance. + * + * \param Pool The file pool. + */ +VOID PhDestroyFilePool( + _In_ _Post_invalid_ PPH_FILE_POOL Pool + ) +{ + ULONG i; + PLIST_ENTRY head; + PLIST_ENTRY entry; + PPH_FILE_POOL_VIEW view; + + // Unmap all views. + for (i = 0; i < Pool->ByIndexSize; i++) + { + if (head = Pool->ByIndexBuckets[i]) + { + entry = head; + + do + { + view = CONTAINING_RECORD(entry, PH_FILE_POOL_VIEW, ByIndexListEntry); + entry = entry->Flink; + PhFppUnmapRange(Pool, view->Base); + PhFreeToFreeList(&Pool->ViewFreeList, view); + } while (entry != head); + } + } + + if (Pool->ByIndexBuckets) + PhFree(Pool->ByIndexBuckets); + + PhDeleteFreeList(&Pool->ViewFreeList); + + if (Pool->SectionHandle) + NtClose(Pool->SectionHandle); + if (Pool->FileHandle) + NtClose(Pool->FileHandle); + + PhFree(Pool); +} + +/** + * Validates and corrects file pool parameters. + * + * \param Parameters The parameters structure which is validated and modified if necessary. + */ +NTSTATUS PhpValidateFilePoolParameters( + _Inout_ PPH_FILE_POOL_PARAMETERS Parameters + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + // 16 <= SegmentShift <= 28 + + if (Parameters->SegmentShift < 16) + { + Parameters->SegmentShift = 16; + status = STATUS_SOME_NOT_MAPPED; + } + + if (Parameters->SegmentShift > 28) + { + Parameters->SegmentShift = 28; + status = STATUS_SOME_NOT_MAPPED; + } + + return status; +} + +/** + * Creates default file pool parameters. + * + * \param Parameters The parameters structure which receives the default parameter values. + */ +VOID PhpSetDefaultFilePoolParameters( + _Out_ PPH_FILE_POOL_PARAMETERS Parameters + ) +{ + Parameters->SegmentShift = 18; // 256kB + Parameters->MaximumInactiveViews = 128; +} + +/** + * Allocates a block from a file pool. + * + * \param Pool The file pool. + * \param Size The number of bytes to allocate. + * \param Rva A variable which receives the relative virtual address of the allocated block. + * + * \return A pointer to the allocated block. You must call PhDereferenceFilePool() or + * PhDereferenceFilePoolByRva() when you no longer need a reference to the block. + * + * \remarks The returned pointer is not valid beyond the lifetime of the file pool instance. Use the + * relative virtual address if you need a permanent reference to the allocated block. + */ +PVOID PhAllocateFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Size, + _Out_opt_ PULONG Rva + ) +{ + PPH_FP_BLOCK_HEADER blockHeader; + ULONG numberOfBlocks; + PPH_FP_BLOCK_HEADER firstBlock; + PPH_FP_SEGMENT_HEADER segmentHeader; + ULONG freeListIndex; + ULONG freeListLimit; + ULONG segmentIndex; + ULONG nextSegmentIndex; + ULONG newFreeListIndex; + + // Calculate the number of blocks needed for the allocation. + numberOfBlocks = (FIELD_OFFSET(PH_FP_BLOCK_HEADER, Body) + Size + Pool->BlockSize - 1) >> Pool->BlockShift; + + if (numberOfBlocks > PH_FP_BLOCK_COUNT - Pool->SegmentHeaderBlockSpan) + { + // TODO: Perform a large allocation. + return NULL; + } + + // Scan each applicable free list and try to allocate from those segments. + + freeListLimit = PhFppComputeFreeListIndex(Pool, numberOfBlocks); + + for (freeListIndex = 0; freeListIndex <= freeListLimit; freeListIndex++) + { + segmentIndex = Pool->Header->FreeLists[freeListIndex]; + + while (segmentIndex != ULONG_MAX) + { + firstBlock = PhFppReferenceSegment(Pool, segmentIndex); + + if (!firstBlock) + return NULL; + + segmentHeader = PhFppGetHeaderSegment(Pool, firstBlock); + nextSegmentIndex = segmentHeader->FreeFlink; + + blockHeader = PhFppAllocateBlocks(Pool, firstBlock, segmentHeader, numberOfBlocks); + + if (blockHeader) + goto BlockAllocated; + + PhFppDereferenceSegment(Pool, segmentIndex); + segmentIndex = nextSegmentIndex; + } + } + + // No segments have the required number of contiguous free blocks. Allocate a new one. + + firstBlock = PhFppAllocateSegment(Pool, &segmentIndex); + + if (!firstBlock) + return NULL; + + freeListIndex = 0; + segmentHeader = PhFppGetHeaderSegment(Pool, firstBlock); + blockHeader = PhFppAllocateBlocks(Pool, firstBlock, segmentHeader, numberOfBlocks); + + if (!blockHeader) + { + PhFppDereferenceSegment(Pool, segmentIndex); + return NULL; + } + +BlockAllocated: + + // Compute the new free list index of the segment and move it to another free list if necessary. + + newFreeListIndex = PhFppComputeFreeListIndex(Pool, segmentHeader->FreeBlocks); + + if (newFreeListIndex != freeListIndex) + { + PhFppRemoveFreeList(Pool, freeListIndex, segmentIndex, segmentHeader); + PhFppInsertFreeList(Pool, newFreeListIndex, segmentIndex, segmentHeader); + } + + if (Rva) + { + *Rva = PhFppEncodeRva(Pool, segmentIndex, firstBlock, &blockHeader->Body); + } + + return &blockHeader->Body; +} + +/** + * Frees a block. + * + * \param Pool The file pool. + * \param SegmentIndex The index of the segment containing the block. + * \param FirstBlock The first block of the segment containing the block. + * \param Block A pointer to the block. + */ +VOID PhpFreeFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _In_ PVOID Block + ) +{ + PPH_FP_SEGMENT_HEADER segmentHeader; + ULONG oldFreeListIndex; + ULONG newFreeListIndex; + + segmentHeader = PhFppGetHeaderSegment(Pool, FirstBlock); + oldFreeListIndex = PhFppComputeFreeListIndex(Pool, segmentHeader->FreeBlocks); + PhFppFreeBlocks(Pool, FirstBlock, segmentHeader, PhFppGetHeaderBlock(Pool, Block)); + newFreeListIndex = PhFppComputeFreeListIndex(Pool, segmentHeader->FreeBlocks); + + // Move the segment into another free list if needed. + if (newFreeListIndex != oldFreeListIndex) + { + PhFppRemoveFreeList(Pool, oldFreeListIndex, SegmentIndex, segmentHeader); + PhFppInsertFreeList(Pool, newFreeListIndex, SegmentIndex, segmentHeader); + } +} + +/** + * Frees a block allocated by PhAllocateFilePool(). + * + * \param Pool The file pool. + * \param Block A pointer to the block. The pointer is no longer valid after you call this function. + * Do not use PhDereferenceFilePool() or PhDereferenceFilePoolByRva(). + */ +VOID PhFreeFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Block + ) +{ + PPH_FILE_POOL_VIEW view; + PPH_FP_BLOCK_HEADER firstBlock; + + view = PhFppFindViewByBase(Pool, Block); + + if (!view) + PhRaiseStatus(STATUS_INVALID_PARAMETER_2); + + firstBlock = view->Base; + PhpFreeFilePool(Pool, view->SegmentIndex, firstBlock, Block); + PhFppDereferenceView(Pool, view); +} + +/** + * Frees a block allocated by PhAllocateFilePool(). + * + * \param Pool The file pool. + * \param Rva The relative virtual address of the block. + */ +BOOLEAN PhFreeFilePoolByRva( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Rva + ) +{ + ULONG segmentIndex; + ULONG offset; + PPH_FP_BLOCK_HEADER firstBlock; + + offset = PhFppDecodeRva(Pool, Rva, &segmentIndex); + + if (offset == ULONG_MAX) + return FALSE; + + firstBlock = PhFppReferenceSegment(Pool, segmentIndex); + + if (!firstBlock) + return FALSE; + + PhpFreeFilePool(Pool, segmentIndex, firstBlock, PTR_ADD_OFFSET(firstBlock, offset)); + PhFppDereferenceSegment(Pool, segmentIndex); + + return TRUE; +} + +/** + * Increments the reference count for the specified address. + * + * \param Pool The file pool. + * \param Address An address. + */ +VOID PhReferenceFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Address + ) +{ + PhFppReferenceSegmentByBase(Pool, Address); +} + +/** + * Decrements the reference count for the specified address. + * + * \param Pool The file pool. + * \param Address An address. + */ +VOID PhDereferenceFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Address + ) +{ + PhFppDereferenceSegmentByBase(Pool, Address); +} + +/** + * Obtains a pointer for a relative virtual address, incrementing the reference count of the + * address. + * + * \param Pool The file pool. + * \param Rva A relative virtual address. + */ +PVOID PhReferenceFilePoolByRva( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Rva + ) +{ + ULONG segmentIndex; + ULONG offset; + PPH_FP_BLOCK_HEADER firstBlock; + + if (Rva == 0) + return NULL; + + offset = PhFppDecodeRva(Pool, Rva, &segmentIndex); + + if (offset == ULONG_MAX) + return NULL; + + firstBlock = PhFppReferenceSegment(Pool, segmentIndex); + + if (!firstBlock) + return NULL; + + return PTR_ADD_OFFSET(firstBlock, offset); +} + +/** + * Decrements the reference count for the specified relative virtual address. + * + * \param Pool The file pool. + * \param Rva A relative virtual address. + */ +BOOLEAN PhDereferenceFilePoolByRva( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Rva + ) +{ + ULONG segmentIndex; + ULONG offset; + + offset = PhFppDecodeRva(Pool, Rva, &segmentIndex); + + if (offset == ULONG_MAX) + return FALSE; + + PhFppDereferenceSegment(Pool, segmentIndex); + + return TRUE; +} + +/** + * Obtains a relative virtual address for a pointer. + * + * \param Pool The file pool. + * \param Address A pointer. + * + * \return The relative virtual address. + * + * \remarks No reference counts are changed. + */ +ULONG PhEncodeRvaFilePool( + _In_ PPH_FILE_POOL Pool, + _In_ PVOID Address + ) +{ + PPH_FILE_POOL_VIEW view; + + if (!Address) + return 0; + + view = PhFppFindViewByBase(Pool, Address); + + if (!view) + PhRaiseStatus(STATUS_INVALID_PARAMETER_2); + + return PhFppEncodeRva(Pool, view->SegmentIndex, view->Base, Address); +} + +/** + * Retrieves user data. + * + * \param Pool The file pool. + * \param Context A variable which receives the user data. + */ +VOID PhGetUserContextFilePool( + _In_ PPH_FILE_POOL Pool, + _Out_ PULONGLONG Context + ) +{ + *Context = Pool->Header->UserContext; +} + +/** + * Stores user data. + * + * \param Pool The file pool. + * \param Context A variable which contains the user data. + */ +VOID PhSetUserContextFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PULONGLONG Context + ) +{ + Pool->Header->UserContext = *Context; +} + +/** + * Extends a file pool. + * + * \param Pool The file pool. + * \param NewSize The new size of the file, in bytes. + */ +NTSTATUS PhFppExtendRange( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG NewSize + ) +{ + LARGE_INTEGER newSectionSize; + + newSectionSize.QuadPart = NewSize; + + return NtExtendSection(Pool->SectionHandle, &newSectionSize); +} + +/** + * Maps in a view of a file pool. + * + * \param Pool The file pool. + * \param Offset The offset of the view, in bytes. + * \param Size The size of the view, in bytes. + * \param Base A variable which receives the base address of the view. + */ +NTSTATUS PhFppMapRange( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Offset, + _In_ ULONG Size, + _Out_ PVOID *Base + ) +{ + NTSTATUS status; + PVOID baseAddress; + LARGE_INTEGER sectionOffset; + SIZE_T viewSize; + + baseAddress = NULL; + sectionOffset.QuadPart = Offset; + viewSize = Size; + + status = NtMapViewOfSection( + Pool->SectionHandle, + NtCurrentProcess(), + &baseAddress, + 0, + viewSize, + §ionOffset, + &viewSize, + ViewShare, + 0, + !Pool->ReadOnly ? PAGE_READWRITE : PAGE_READONLY + ); + + if (NT_SUCCESS(status)) + *Base = baseAddress; + + return status; +} + +/** + * Unmaps a view of a file pool. + * + * \param Pool The file pool. + * \param Base The base address of the view. + */ +NTSTATUS PhFppUnmapRange( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ) +{ + return NtUnmapViewOfSection(NtCurrentProcess(), Base); +} + +/** + * Initializes a segment. + * + * \param Pool The file pool. + * \param BlockOfSegmentHeader The block header of the span containing the segment header. + * \param AdditionalBlocksUsed The number of blocks already allocated from the segment, excluding + * the blocks comprising the segment header. + */ +VOID PhFppInitializeSegment( + _Inout_ PPH_FILE_POOL Pool, + _Out_ PPH_FP_BLOCK_HEADER BlockOfSegmentHeader, + _In_ ULONG AdditionalBlocksUsed + ) +{ + PPH_FP_SEGMENT_HEADER segmentHeader; + RTL_BITMAP bitmap; + + BlockOfSegmentHeader->Span = Pool->SegmentHeaderBlockSpan; + segmentHeader = (PPH_FP_SEGMENT_HEADER)&BlockOfSegmentHeader->Body; + + RtlInitializeBitMap(&bitmap, segmentHeader->Bitmap, PH_FP_BLOCK_COUNT); + RtlSetBits(&bitmap, 0, Pool->SegmentHeaderBlockSpan + AdditionalBlocksUsed); + segmentHeader->FreeBlocks = PH_FP_BLOCK_COUNT - (Pool->SegmentHeaderBlockSpan + AdditionalBlocksUsed); + segmentHeader->FreeFlink = ULONG_MAX; + segmentHeader->FreeBlink = ULONG_MAX; +} + +/** + * Allocates a segment. + * + * \param Pool The file pool. + * \param NewSegmentIndex A variable which receives the index of the new segment. + * + * \return A pointer to the first block of the segment. + */ +PPH_FP_BLOCK_HEADER PhFppAllocateSegment( + _Inout_ PPH_FILE_POOL Pool, + _Out_ PULONG NewSegmentIndex + ) +{ + ULONG newSize; + ULONG segmentIndex; + PPH_FP_BLOCK_HEADER firstBlock; + PPH_FP_SEGMENT_HEADER segmentHeader; + + newSize = (Pool->Header->SegmentCount + 1) << Pool->SegmentShift; + + if (!NT_SUCCESS(PhFppExtendRange(Pool, newSize))) + return NULL; + + segmentIndex = Pool->Header->SegmentCount++; + firstBlock = PhFppReferenceSegment(Pool, segmentIndex); + PhFppInitializeSegment(Pool, firstBlock, 0); + segmentHeader = (PPH_FP_SEGMENT_HEADER)&firstBlock->Body; + + PhFppInsertFreeList(Pool, 0, segmentIndex, segmentHeader); + + *NewSegmentIndex = segmentIndex; + + return firstBlock; +} + +/** + * Retrieves the header of a segment. + * + * \param Pool The file pool. + * \param FirstBlock The first block of the segment. + */ +PPH_FP_SEGMENT_HEADER PhFppGetHeaderSegment( + _Inout_ PPH_FILE_POOL Pool, + _In_ PPH_FP_BLOCK_HEADER FirstBlock + ) +{ + if (FirstBlock != Pool->FirstBlockOfFirstSegment) + { + return (PPH_FP_SEGMENT_HEADER)&FirstBlock->Body; + } + else + { + // In the first segment, the segment header is after the file header. + return (PPH_FP_SEGMENT_HEADER)&((PPH_FP_BLOCK_HEADER)PTR_ADD_OFFSET(FirstBlock, (Pool->FileHeaderBlockSpan << Pool->BlockShift)))->Body; + } +} + +VOID PhFppAddViewByIndex( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + ULONG index; + PLIST_ENTRY head; + + index = View->SegmentIndex & (Pool->ByIndexSize - 1); + head = Pool->ByIndexBuckets[index]; + + if (head) + { + InsertHeadList(head, &View->ByIndexListEntry); + } + else + { + InitializeListHead(&View->ByIndexListEntry); + Pool->ByIndexBuckets[index] = &View->ByIndexListEntry; + } +} + +VOID PhFppRemoveViewByIndex( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + ULONG index; + PLIST_ENTRY head; + + index = View->SegmentIndex & (Pool->ByIndexSize - 1); + head = Pool->ByIndexBuckets[index]; + assert(head); + + // Unlink the entry from the chain. + RemoveEntryList(&View->ByIndexListEntry); + + if (&View->ByIndexListEntry == head) + { + // This entry is currently the chain head. + + // If this was the last entry in the chain, then indicate that the chain is empty. + // Otherwise, choose a new head. + + if (IsListEmpty(head)) + Pool->ByIndexBuckets[index] = NULL; + else + Pool->ByIndexBuckets[index] = head->Flink; + } +} + +/** + * Finds a view for the specified segment. + * + * \param Pool The file pool. + * \param SegmentIndex The index of the segment. + * + * \return The view for the segment, or NULL if no view is present for the segment. + */ +PPH_FILE_POOL_VIEW PhFppFindViewByIndex( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ) +{ + ULONG index; + PLIST_ENTRY head; + PLIST_ENTRY entry; + PPH_FILE_POOL_VIEW view; + + index = SegmentIndex & (Pool->ByIndexSize - 1); + head = Pool->ByIndexBuckets[index]; + + if (!head) + return NULL; + + entry = head; + + do + { + view = CONTAINING_RECORD(entry, PH_FILE_POOL_VIEW, ByIndexListEntry); + + if (view->SegmentIndex == SegmentIndex) + return view; + + entry = entry->Flink; + } while (entry != head); + + return NULL; +} + +LONG NTAPI PhpFilePoolViewByBaseCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ) +{ + PPH_FILE_POOL_VIEW view1 = CONTAINING_RECORD(Links1, PH_FILE_POOL_VIEW, ByBaseLinks); + PPH_FILE_POOL_VIEW view2 = CONTAINING_RECORD(Links2, PH_FILE_POOL_VIEW, ByBaseLinks); + + return uintptrcmp((ULONG_PTR)view1->Base, (ULONG_PTR)view2->Base); +} + +VOID PhFppAddViewByBase( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + PhAddElementAvlTree(&Pool->ByBaseSet, &View->ByBaseLinks); +} + +VOID PhFppRemoveViewByBase( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + PhRemoveElementAvlTree(&Pool->ByBaseSet, &View->ByBaseLinks); +} + +/** + * Finds a view containing the specified address. + * + * \param Pool The file pool. + * \param Base The address. + * + * \return The view containing the address, or NULL if no view is present for the address. + */ +PPH_FILE_POOL_VIEW PhFppFindViewByBase( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ) +{ + PPH_FILE_POOL_VIEW view; + PPH_AVL_LINKS links; + PH_FILE_POOL_VIEW lookupView; + + // This is an approximate search to find the target view in which the specified address lies. + + lookupView.Base = Base; + links = PhUpperDualBoundElementAvlTree(&Pool->ByBaseSet, &lookupView.ByBaseLinks); + + if (!links) + return NULL; + + view = CONTAINING_RECORD(links, PH_FILE_POOL_VIEW, ByBaseLinks); + + if ((ULONG_PTR)Base < (ULONG_PTR)view->Base + Pool->SegmentSize) + return view; + + return NULL; +} + +PPH_FILE_POOL_VIEW PhFppCreateView( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ) +{ + PPH_FILE_POOL_VIEW view; + PVOID base; + + // Map in the segment. + if (!NT_SUCCESS(PhFppMapRange(Pool, SegmentIndex << Pool->SegmentShift, Pool->SegmentSize, &base))) + return NULL; + + // Create and add the view entry. + + view = PhAllocateFromFreeList(&Pool->ViewFreeList); + memset(view, 0, sizeof(PH_FILE_POOL_VIEW)); + + view->RefCount = 1; + view->SegmentIndex = SegmentIndex; + view->Base = base; + + PhFppAddViewByIndex(Pool, view); + PhFppAddViewByBase(Pool, view); + + return view; +} + +VOID PhFppDestroyView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + PhFppUnmapRange(Pool, View->Base); + PhFppRemoveViewByIndex(Pool, View); + PhFppRemoveViewByBase(Pool, View); + + PhFreeToFreeList(&Pool->ViewFreeList, View); +} + +VOID PhFppActivateView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + RemoveEntryList(&View->InactiveViewsListEntry); + Pool->NumberOfInactiveViews--; +} + +VOID PhFppDeactivateView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + InsertHeadList(&Pool->InactiveViewsListHead, &View->InactiveViewsListEntry); + Pool->NumberOfInactiveViews++; + + // If we have too many inactive views, destroy the least recent ones. + while (Pool->NumberOfInactiveViews > Pool->MaximumInactiveViews) + { + PLIST_ENTRY lruEntry; + PPH_FILE_POOL_VIEW lruView; + + lruEntry = RemoveTailList(&Pool->InactiveViewsListHead); + Pool->NumberOfInactiveViews--; + + assert(lruEntry != &Pool->InactiveViewsListHead); + lruView = CONTAINING_RECORD(lruEntry, PH_FILE_POOL_VIEW, InactiveViewsListEntry); + PhFppDestroyView(Pool, lruView); + } +} + +VOID PhFppReferenceView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + if (View->RefCount == 0) + { + // The view is inactive, so make it active. + PhFppActivateView(Pool, View); + } + + View->RefCount++; +} + +VOID PhFppDereferenceView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ) +{ + if (--View->RefCount == 0) + { + if (View->SegmentIndex == 0) + PhRaiseStatus(STATUS_INTERNAL_ERROR); + + PhFppDeactivateView(Pool, View); + } +} + +PPH_FP_BLOCK_HEADER PhFppReferenceSegment( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ) +{ + PPH_FILE_POOL_VIEW view; + + // Validate parameters. + if (SegmentIndex != 0 && SegmentIndex >= Pool->Header->SegmentCount) + return NULL; + + // Try to get a cached view. + + view = PhFppFindViewByIndex(Pool, SegmentIndex); + + if (view) + { + PhFppReferenceView(Pool, view); + + return (PPH_FP_BLOCK_HEADER)view->Base; + } + + // No cached view, so create one. + + view = PhFppCreateView(Pool, SegmentIndex); + + if (!view) + return NULL; + + return (PPH_FP_BLOCK_HEADER)view->Base; +} + +VOID PhFppDereferenceSegment( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ) +{ + PPH_FILE_POOL_VIEW view; + + view = PhFppFindViewByIndex(Pool, SegmentIndex); + + if (!view) + PhRaiseStatus(STATUS_INTERNAL_ERROR); + + PhFppDereferenceView(Pool, view); +} + +VOID PhFppReferenceSegmentByBase( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ) +{ + PPH_FILE_POOL_VIEW view; + + view = PhFppFindViewByBase(Pool, Base); + + if (!view) + PhRaiseStatus(STATUS_INTERNAL_ERROR); + + PhFppReferenceView(Pool, view); +} + +VOID PhFppDereferenceSegmentByBase( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ) +{ + PPH_FILE_POOL_VIEW view; + + view = PhFppFindViewByBase(Pool, Base); + + if (!view) + PhRaiseStatus(STATUS_INTERNAL_ERROR); + + PhFppDereferenceView(Pool, view); +} + +/** + * Allocates blocks from a segment. + * + * \param Pool The file pool. + * \param FirstBlock The first block of the segment. + * \param SegmentHeader The header of the segment. + * \param NumberOfBlocks The number of blocks to allocate. + * + * \return The header of the allocated span, or NULL if there is an insufficient number of + * contiguous free blocks for the allocation. + */ +PPH_FP_BLOCK_HEADER PhFppAllocateBlocks( + _Inout_ PPH_FILE_POOL Pool, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, + _In_ ULONG NumberOfBlocks + ) +{ + RTL_BITMAP bitmap; + ULONG hintIndex; + ULONG foundIndex; + PPH_FP_BLOCK_HEADER blockHeader; + + if (FirstBlock != Pool->FirstBlockOfFirstSegment) + hintIndex = Pool->SegmentHeaderBlockSpan; + else + hintIndex = Pool->SegmentHeaderBlockSpan + Pool->FileHeaderBlockSpan; + + RtlInitializeBitMap(&bitmap, SegmentHeader->Bitmap, PH_FP_BLOCK_COUNT); + + // Find a range of free blocks and mark them as in-use. + foundIndex = RtlFindClearBitsAndSet(&bitmap, NumberOfBlocks, hintIndex); + + if (foundIndex == ULONG_MAX) + { + // No more space. + return NULL; + } + + SegmentHeader->FreeBlocks -= NumberOfBlocks; + + blockHeader = (PPH_FP_BLOCK_HEADER)PTR_ADD_OFFSET(FirstBlock, (foundIndex << Pool->BlockShift)); + blockHeader->Flags = 0; + blockHeader->Span = NumberOfBlocks; + + return blockHeader; +} + +/** + * Frees blocks in a segment. + * + * \param Pool The file pool. + * \param FirstBlock The first block of the segment. + * \param SegmentHeader The header of the segment. + * \param BlockHeader The header of the allocated span. + */ +VOID PhFppFreeBlocks( + _Inout_ PPH_FILE_POOL Pool, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, + _In_ PPH_FP_BLOCK_HEADER BlockHeader + ) +{ + RTL_BITMAP bitmap; + ULONG startIndex; + ULONG blockSpan; + + RtlInitializeBitMap(&bitmap, SegmentHeader->Bitmap, PH_FP_BLOCK_COUNT); + + // Mark the blocks as free. + startIndex = PtrToUlong(PTR_SUB_OFFSET(BlockHeader, FirstBlock)) >> Pool->BlockShift; + blockSpan = BlockHeader->Span; + RtlClearBits(&bitmap, startIndex, blockSpan); + SegmentHeader->FreeBlocks += blockSpan; +} + +/** + * Computes the free list index (category) for a specified number of blocks. + * + * \param Pool The file pool. + * \param NumberOfBlocks The number of free or required blocks. + */ +ULONG PhFppComputeFreeListIndex( + _In_ PPH_FILE_POOL Pool, + _In_ ULONG NumberOfBlocks + ) +{ + // Use a binary tree to speed up comparison. + + if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 64) + { + if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 2) + { + if (NumberOfBlocks >= PH_FP_BLOCK_COUNT - Pool->SegmentHeaderBlockSpan) + return 0; + else + return 1; + } + else + { + if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 16) + return 2; + else + return 3; + } + } + else + { + if (NumberOfBlocks >= 4) + { + if (NumberOfBlocks >= PH_FP_BLOCK_COUNT / 256) + return 4; + else + return 5; + } + else + { + if (NumberOfBlocks >= 1) + return 6; + else + return 7; + } + } +} + +/** + * Inserts a segment into a free list. + * + * \param Pool The file pool. + * \param FreeListIndex The index of a free list. + * \param SegmentIndex The index of the segment. + * \param SegmentHeader The header of the segment. + */ +BOOLEAN PhFppInsertFreeList( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG FreeListIndex, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_SEGMENT_HEADER SegmentHeader + ) +{ + ULONG oldSegmentIndex; + PPH_FP_BLOCK_HEADER oldSegmentFirstBlock; + PPH_FP_SEGMENT_HEADER oldSegmentHeader; + + oldSegmentIndex = Pool->Header->FreeLists[FreeListIndex]; + + // Try to reference the segment before we commit any changes. + + if (oldSegmentIndex != ULONG_MAX) + { + oldSegmentFirstBlock = PhFppReferenceSegment(Pool, oldSegmentIndex); + + if (!oldSegmentFirstBlock) + return FALSE; + } + + // Insert the segment into the list. + + SegmentHeader->FreeBlink = ULONG_MAX; + SegmentHeader->FreeFlink = oldSegmentIndex; + Pool->Header->FreeLists[FreeListIndex] = SegmentIndex; + + if (oldSegmentIndex != ULONG_MAX) + { + oldSegmentHeader = PhFppGetHeaderSegment(Pool, oldSegmentFirstBlock); + oldSegmentHeader->FreeBlink = SegmentIndex; + PhFppDereferenceSegment(Pool, oldSegmentIndex); + } + + return TRUE; +} + +/** + * Removes a segment from a free list. + * + * \param Pool The file pool. + * \param FreeListIndex The index of a free list. + * \param SegmentIndex The index of the segment. + * \param SegmentHeader The header of the segment. + */ +BOOLEAN PhFppRemoveFreeList( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG FreeListIndex, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_SEGMENT_HEADER SegmentHeader + ) +{ + ULONG flinkSegmentIndex; + PPH_FP_BLOCK_HEADER flinkSegmentFirstBlock; + PPH_FP_SEGMENT_HEADER flinkSegmentHeader; + ULONG blinkSegmentIndex; + PPH_FP_BLOCK_HEADER blinkSegmentFirstBlock; + PPH_FP_SEGMENT_HEADER blinkSegmentHeader; + + flinkSegmentIndex = SegmentHeader->FreeFlink; + blinkSegmentIndex = SegmentHeader->FreeBlink; + + // Try to reference the segments before we commit any changes. + + if (flinkSegmentIndex != ULONG_MAX) + { + flinkSegmentFirstBlock = PhFppReferenceSegment(Pool, flinkSegmentIndex); + + if (!flinkSegmentFirstBlock) + return FALSE; + } + + if (blinkSegmentIndex != ULONG_MAX) + { + blinkSegmentFirstBlock = PhFppReferenceSegment(Pool, blinkSegmentIndex); + + if (!blinkSegmentFirstBlock) + { + if (flinkSegmentIndex != ULONG_MAX) + PhFppDereferenceSegment(Pool, flinkSegmentIndex); + + return FALSE; + } + } + + // Unlink the segment from the list. + + if (flinkSegmentIndex != ULONG_MAX) + { + flinkSegmentHeader = PhFppGetHeaderSegment(Pool, flinkSegmentFirstBlock); + flinkSegmentHeader->FreeBlink = blinkSegmentIndex; + PhFppDereferenceSegment(Pool, flinkSegmentIndex); + } + + if (blinkSegmentIndex != ULONG_MAX) + { + blinkSegmentHeader = PhFppGetHeaderSegment(Pool, blinkSegmentFirstBlock); + blinkSegmentHeader->FreeFlink = flinkSegmentIndex; + PhFppDereferenceSegment(Pool, blinkSegmentIndex); + } + else + { + // The segment was the list head; select a new one. + Pool->Header->FreeLists[FreeListIndex] = flinkSegmentIndex; + } + + return TRUE; +} + +/** + * Retrieves the header of a block. + * + * \param Pool The file pool. + * \param Block A pointer to the body of the block. + */ +PPH_FP_BLOCK_HEADER PhFppGetHeaderBlock( + _In_ PPH_FILE_POOL Pool, + _In_ PVOID Block + ) +{ + return CONTAINING_RECORD(Block, PH_FP_BLOCK_HEADER, Body); +} + +/** + * Creates a relative virtual address. + * + * \param Pool The file pool. + * \param SegmentIndex The index of the segment containing \a Address. + * \param FirstBlock The first block of the segment containing \a Address. + * \param Address An address. + */ +ULONG PhFppEncodeRva( + _In_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _In_ PVOID Address + ) +{ + return (SegmentIndex << Pool->SegmentShift) + PtrToUlong(PTR_SUB_OFFSET(Address, FirstBlock)); +} + +/** + * Decodes a relative virtual address. + * + * \param Pool The file pool. + * \param Rva The relative virtual address. + * \param SegmentIndex A variable which receives the segment index. + * + * \return An offset into the segment specified by \a SegmentIndex, or -1 if \a Rva is invalid. + */ +ULONG PhFppDecodeRva( + _In_ PPH_FILE_POOL Pool, + _In_ ULONG Rva, + _Out_ PULONG SegmentIndex + ) +{ + ULONG segmentIndex; + + segmentIndex = Rva >> Pool->SegmentShift; + + if (segmentIndex >= Pool->Header->SegmentCount) + return ULONG_MAX; + + *SegmentIndex = segmentIndex; + + return Rva & (Pool->SegmentSize - 1); +} diff --git a/phlib/filestream.c b/phlib/filestream.c index 3e7552d40ce5..6d12bc6f3741 100644 --- a/phlib/filestream.c +++ b/phlib/filestream.c @@ -24,21 +24,7 @@ #include #include -PPH_OBJECT_TYPE PhFileStreamType; - -BOOLEAN PhFileStreamInitialization( - VOID - ) -{ - PH_OBJECT_TYPE_PARAMETERS parameters; - - parameters.FreeListSize = sizeof(PH_FILE_STREAM); - parameters.FreeListCount = 16; - - PhFileStreamType = PhCreateObjectTypeEx(L"FileStream", PH_OBJECT_TYPE_USE_FREE_LIST, PhpFileStreamDeleteProcedure, ¶meters); - - return TRUE; -} +PPH_OBJECT_TYPE PhFileStreamType = NULL; NTSTATUS PhCreateFileStream( _Out_ PPH_FILE_STREAM *FileStream, @@ -110,8 +96,26 @@ NTSTATUS PhCreateFileStream2( _In_ ULONG BufferLength ) { + static PH_INITONCE fileStreamInitOnce = PH_INITONCE_INIT; PPH_FILE_STREAM fileStream; + if (PhBeginInitOnce(&fileStreamInitOnce)) + { + PH_OBJECT_TYPE_PARAMETERS parameters; + + parameters.FreeListSize = sizeof(PH_FILE_STREAM); + parameters.FreeListCount = 16; + + PhFileStreamType = PhCreateObjectTypeEx( + L"FileStream", + PH_OBJECT_TYPE_USE_FREE_LIST, + PhpFileStreamDeleteProcedure, + ¶meters + ); + + PhEndInitOnce(&fileStreamInitOnce); + } + fileStream = PhCreateObject(sizeof(PH_FILE_STREAM), PhFileStreamType); fileStream->FileHandle = FileHandle; fileStream->Flags = Flags; @@ -332,7 +336,7 @@ NTSTATUS PhReadFileStream( // Try to satisfy the request from the buffer. memcpy( Buffer, - (PCHAR)FileStream->Buffer + FileStream->ReadPosition, + PTR_ADD_OFFSET(FileStream->Buffer, FileStream->ReadPosition), readLength ); FileStream->ReadPosition += readLength; @@ -348,7 +352,7 @@ NTSTATUS PhReadFileStream( if (NT_SUCCESS(status = PhpReadFileStream( FileStream, - (PCHAR)Buffer + readLength, + PTR_ADD_OFFSET(Buffer, readLength), Length - readLength, &readLength2 ))) @@ -454,7 +458,7 @@ NTSTATUS PhWriteFileStream( writtenLength = Length; memcpy( - (PCHAR)FileStream->Buffer + FileStream->WritePosition, + PTR_ADD_OFFSET(FileStream->Buffer, FileStream->WritePosition), Buffer, writtenLength ); @@ -466,7 +470,7 @@ NTSTATUS PhWriteFileStream( return status; } - Buffer = (PCHAR)Buffer + writtenLength; + Buffer = PTR_ADD_OFFSET(Buffer, writtenLength); Length -= writtenLength; } diff --git a/phlib/format.c b/phlib/format.c index ec036cd0dd6f..bd273a8a1981 100644 --- a/phlib/format.c +++ b/phlib/format.c @@ -1,277 +1,277 @@ -/* - * Process Hacker - - * string formatting - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This module provides a high-performance string formatting mechanism. Instead of using format - * strings, the user supplies an array of structures. This system is 2-5 times faster than - * printf-based functions. - * - * This file contains the public interfaces, while including the real formatting code from - * elsewhere. There are currently two functions: PhFormat, which returns a string object containing - * the formatted string, and PhFormatToBuffer, which writes the formatted string to a buffer. The - * latter is a bit faster due to the lack of resizing logic. - */ - -#include - -#include - -extern ULONG PhMaxSizeUnit; - -#define SMALL_BUFFER_LENGTH (PH_OBJECT_SMALL_OBJECT_SIZE - FIELD_OFFSET(PH_STRING, Data) - sizeof(WCHAR)) -#define BUFFER_SIZE 512 - -#define PHP_FORMAT_NEGATIVE 0x1 -#define PHP_FORMAT_POSITIVE 0x2 -#define PHP_FORMAT_PAD 0x4 - -// Internal CRT routine needed for floating-point conversion - -errno_t __cdecl _cfltcvt_l(double *arg, char *buffer, size_t sizeInBytes, - int format, int precision, int caps, _locale_t plocinfo); - -// Keep in sync with PhSizeUnitNames -static PH_STRINGREF PhpSizeUnitNamesCounted[7] = -{ - PH_STRINGREF_INIT(L"B"), - PH_STRINGREF_INIT(L"kB"), - PH_STRINGREF_INIT(L"MB"), - PH_STRINGREF_INIT(L"GB"), - PH_STRINGREF_INIT(L"TB"), - PH_STRINGREF_INIT(L"PB"), - PH_STRINGREF_INIT(L"EB") -}; - -static PH_INITONCE PhpFormatInitOnce = PH_INITONCE_INIT; -static WCHAR PhpFormatDecimalSeparator = '.'; -static WCHAR PhpFormatThousandSeparator = ','; -static _locale_t PhpFormatUserLocale = NULL; - -#if (_MSC_VER >= 1900) - -// See Source\10.0.10150.0\ucrt\convert\cvt.cpp in SDK v10. -errno_t __cdecl __acrt_fp_format( - double const* const value, - char* const result_buffer, - size_t const result_buffer_count, - char* const scratch_buffer, - size_t const scratch_buffer_count, - int const format, - int const precision, - UINT64 const options, - _locale_t const locale - ); - -static errno_t __cdecl _cfltcvt_l(double *arg, char *buffer, size_t sizeInBytes, - int format, int precision, int caps, _locale_t plocinfo) -{ - char scratch_buffer[_CVTBUFSIZE + 1]; - - if (caps & 1) - format -= 32; // Make uppercase - - return __acrt_fp_format(arg, buffer, sizeInBytes, scratch_buffer, sizeof(scratch_buffer), - format, precision, 0, plocinfo); -} - -#endif - -// From Source\10.0.10150.0\ucrt\inc\corecrt_internal_stdio_output.h in SDK v10. -VOID PhpCropZeros( - _Inout_ PCHAR Buffer, - _In_ _locale_t Locale - ) -{ - CHAR decimalSeparator = (CHAR)PhpFormatDecimalSeparator; - - while (*Buffer && *Buffer != decimalSeparator) - ++Buffer; - - if (*Buffer++) - { - while (*Buffer && *Buffer != 'e' && *Buffer != 'E') - ++Buffer; - - PCHAR stop = Buffer--; - - while (*Buffer == '0') - --Buffer; - - if (*Buffer == decimalSeparator) - --Buffer; - - while ((*++Buffer = *stop++) != '\0') - NOTHING; - } -} - -PPH_STRING PhpResizeFormatBuffer( - _In_ PPH_STRING String, - _Inout_ PSIZE_T AllocatedLength, - _In_ SIZE_T UsedLength, - _In_ SIZE_T NeededLength - ) -{ - PPH_STRING newString; - SIZE_T allocatedLength; - - allocatedLength = *AllocatedLength; - allocatedLength *= 2; - - if (allocatedLength < UsedLength + NeededLength) - allocatedLength = UsedLength + NeededLength; - - newString = PhCreateStringEx(NULL, allocatedLength); - memcpy(newString->Buffer, String->Buffer, UsedLength); - PhDereferenceObject(String); - - *AllocatedLength = allocatedLength; - - return newString; -} - -/** - * Creates a formatted string. - * - * \param Format An array of format structures. - * \param Count The number of structures supplied in \a Format. - * \param InitialCapacity The number of bytes to reserve initially for the string. If 0 is - * specified, a default value is used. - */ -PPH_STRING PhFormat( - _In_reads_(Count) PPH_FORMAT Format, - _In_ ULONG Count, - _In_opt_ SIZE_T InitialCapacity - ) -{ - PPH_STRING string; - SIZE_T allocatedLength; - PWSTR buffer; - SIZE_T usedLength; - - // Set up the buffer. - - // If the specified initial capacity is too small (or zero), use the largest buffer size which - // will still be eligible for allocation from the small object free list. - if (InitialCapacity < SMALL_BUFFER_LENGTH) - InitialCapacity = SMALL_BUFFER_LENGTH; - - string = PhCreateStringEx(NULL, InitialCapacity); - allocatedLength = InitialCapacity; - buffer = string->Buffer; - usedLength = 0; - -#undef ENSURE_BUFFER -#undef OK_BUFFER -#undef ADVANCE_BUFFER - -#define ENSURE_BUFFER(NeededLength) \ - do { \ - if (allocatedLength < usedLength + (NeededLength)) \ - { \ - string = PhpResizeFormatBuffer(string, &allocatedLength, usedLength, (NeededLength)); \ - buffer = string->Buffer + usedLength / sizeof(WCHAR); \ - } \ - } while (0) - -#define OK_BUFFER (TRUE) - -#define ADVANCE_BUFFER(Length) \ - do { buffer += (Length) / sizeof(WCHAR); usedLength += (Length); } while (0) - -#include "format_i.h" - - string->Length = usedLength; - // Null-terminate the string. - string->Buffer[usedLength / sizeof(WCHAR)] = 0; - - return string; -} - -/** - * Writes a formatted string to a buffer. - * - * \param Format An array of format structures. - * \param Count The number of structures supplied in \a Format. - * \param Buffer A buffer. If NULL, no data is written. - * \param BufferLength The number of bytes available in \a Buffer, including space for the null - * terminator. - * \param ReturnLength The number of bytes required to hold the string, including the null - * terminator. - * - * \return TRUE if the buffer was large enough and the string was written (i.e. \a BufferLength >= - * \a ReturnLength), otherwise FALSE. In either case, the required number of bytes is stored in - * \a ReturnLength. - * - * \remarks If the function fails but \a BufferLength != 0, a single null byte is written to the - * start of \a Buffer. - */ -BOOLEAN PhFormatToBuffer( - _In_reads_(Count) PPH_FORMAT Format, - _In_ ULONG Count, - _Out_writes_bytes_opt_(BufferLength) PWSTR Buffer, - _In_opt_ SIZE_T BufferLength, - _Out_opt_ PSIZE_T ReturnLength - ) -{ - PWSTR buffer; - SIZE_T usedLength; - BOOLEAN overrun; - - buffer = Buffer; - usedLength = 0; - overrun = FALSE; - - // Make sure we don't try to write anything if we don't have a buffer. - if (!Buffer) - overrun = TRUE; - -#undef ENSURE_BUFFER -#undef OK_BUFFER -#undef ADVANCE_BUFFER - -#define ENSURE_BUFFER(NeededLength) \ - do { \ - if (!overrun && (BufferLength < usedLength + (NeededLength))) \ - overrun = TRUE; \ - } while (0) - -#define OK_BUFFER (!overrun) - -#define ADVANCE_BUFFER(Length) \ - do { buffer += (Length) / sizeof(WCHAR); usedLength += (Length); } while (0) - -#include "format_i.h" - - // Write the null-terminator. - ENSURE_BUFFER(sizeof(WCHAR)); - if (OK_BUFFER) - *buffer = 0; - else if (Buffer && BufferLength != 0) // try to null-terminate even if this function fails - *Buffer = 0; - ADVANCE_BUFFER(sizeof(WCHAR)); - - if (ReturnLength) - *ReturnLength = usedLength; - - return OK_BUFFER; -} +/* + * Process Hacker - + * string formatting + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This module provides a high-performance string formatting mechanism. Instead of using format + * strings, the user supplies an array of structures. This system is 2-5 times faster than + * printf-based functions. + * + * This file contains the public interfaces, while including the real formatting code from + * elsewhere. There are currently two functions: PhFormat, which returns a string object containing + * the formatted string, and PhFormatToBuffer, which writes the formatted string to a buffer. The + * latter is a bit faster due to the lack of resizing logic. + */ + +#include + +#include + +extern ULONG PhMaxSizeUnit; + +#define SMALL_BUFFER_LENGTH (PH_OBJECT_SMALL_OBJECT_SIZE - FIELD_OFFSET(PH_STRING, Data) - sizeof(WCHAR)) +#define BUFFER_SIZE 512 + +#define PHP_FORMAT_NEGATIVE 0x1 +#define PHP_FORMAT_POSITIVE 0x2 +#define PHP_FORMAT_PAD 0x4 + +// Internal CRT routine needed for floating-point conversion + +errno_t __cdecl _cfltcvt_l(double *arg, char *buffer, size_t sizeInBytes, + int format, int precision, int caps, _locale_t plocinfo); + +// Keep in sync with PhSizeUnitNames +static PH_STRINGREF PhpSizeUnitNamesCounted[7] = +{ + PH_STRINGREF_INIT(L"B"), + PH_STRINGREF_INIT(L"kB"), + PH_STRINGREF_INIT(L"MB"), + PH_STRINGREF_INIT(L"GB"), + PH_STRINGREF_INIT(L"TB"), + PH_STRINGREF_INIT(L"PB"), + PH_STRINGREF_INIT(L"EB") +}; + +static PH_INITONCE PhpFormatInitOnce = PH_INITONCE_INIT; +static WCHAR PhpFormatDecimalSeparator = '.'; +static WCHAR PhpFormatThousandSeparator = ','; +static _locale_t PhpFormatUserLocale = NULL; + +#if (_MSC_VER >= 1900) + +// See Source\10.0.10150.0\ucrt\convert\cvt.cpp in SDK v10. +errno_t __cdecl __acrt_fp_format( + double const* const value, + char* const result_buffer, + size_t const result_buffer_count, + char* const scratch_buffer, + size_t const scratch_buffer_count, + int const format, + int const precision, + UINT64 const options, + _locale_t const locale + ); + +static errno_t __cdecl _cfltcvt_l(double *arg, char *buffer, size_t sizeInBytes, + int format, int precision, int caps, _locale_t plocinfo) +{ + char scratch_buffer[_CVTBUFSIZE + 1]; + + if (caps & 1) + format -= 32; // Make uppercase + + return __acrt_fp_format(arg, buffer, sizeInBytes, scratch_buffer, sizeof(scratch_buffer), + format, precision, 0, plocinfo); +} + +#endif + +// From Source\10.0.10150.0\ucrt\inc\corecrt_internal_stdio_output.h in SDK v10. +VOID PhpCropZeros( + _Inout_ PCHAR Buffer, + _In_ _locale_t Locale + ) +{ + CHAR decimalSeparator = (CHAR)PhpFormatDecimalSeparator; + + while (*Buffer && *Buffer != decimalSeparator) + ++Buffer; + + if (*Buffer++) + { + while (*Buffer && *Buffer != 'e' && *Buffer != 'E') + ++Buffer; + + PCHAR stop = Buffer--; + + while (*Buffer == '0') + --Buffer; + + if (*Buffer == decimalSeparator) + --Buffer; + + while ((*++Buffer = *stop++) != '\0') + NOTHING; + } +} + +PPH_STRING PhpResizeFormatBuffer( + _In_ PPH_STRING String, + _Inout_ PSIZE_T AllocatedLength, + _In_ SIZE_T UsedLength, + _In_ SIZE_T NeededLength + ) +{ + PPH_STRING newString; + SIZE_T allocatedLength; + + allocatedLength = *AllocatedLength; + allocatedLength *= 2; + + if (allocatedLength < UsedLength + NeededLength) + allocatedLength = UsedLength + NeededLength; + + newString = PhCreateStringEx(NULL, allocatedLength); + memcpy(newString->Buffer, String->Buffer, UsedLength); + PhDereferenceObject(String); + + *AllocatedLength = allocatedLength; + + return newString; +} + +/** + * Creates a formatted string. + * + * \param Format An array of format structures. + * \param Count The number of structures supplied in \a Format. + * \param InitialCapacity The number of bytes to reserve initially for the string. If 0 is + * specified, a default value is used. + */ +PPH_STRING PhFormat( + _In_reads_(Count) PPH_FORMAT Format, + _In_ ULONG Count, + _In_opt_ SIZE_T InitialCapacity + ) +{ + PPH_STRING string; + SIZE_T allocatedLength; + PWSTR buffer; + SIZE_T usedLength; + + // Set up the buffer. + + // If the specified initial capacity is too small (or zero), use the largest buffer size which + // will still be eligible for allocation from the small object free list. + if (InitialCapacity < SMALL_BUFFER_LENGTH) + InitialCapacity = SMALL_BUFFER_LENGTH; + + string = PhCreateStringEx(NULL, InitialCapacity); + allocatedLength = InitialCapacity; + buffer = string->Buffer; + usedLength = 0; + +#undef ENSURE_BUFFER +#undef OK_BUFFER +#undef ADVANCE_BUFFER + +#define ENSURE_BUFFER(NeededLength) \ + do { \ + if (allocatedLength < usedLength + (NeededLength)) \ + { \ + string = PhpResizeFormatBuffer(string, &allocatedLength, usedLength, (NeededLength)); \ + buffer = string->Buffer + usedLength / sizeof(WCHAR); \ + } \ + } while (0) + +#define OK_BUFFER (TRUE) + +#define ADVANCE_BUFFER(Length) \ + do { buffer += (Length) / sizeof(WCHAR); usedLength += (Length); } while (0) + +#include "format_i.h" + + string->Length = usedLength; + // Null-terminate the string. + string->Buffer[usedLength / sizeof(WCHAR)] = UNICODE_NULL; + + return string; +} + +/** + * Writes a formatted string to a buffer. + * + * \param Format An array of format structures. + * \param Count The number of structures supplied in \a Format. + * \param Buffer A buffer. If NULL, no data is written. + * \param BufferLength The number of bytes available in \a Buffer, including space for the null + * terminator. + * \param ReturnLength The number of bytes required to hold the string, including the null + * terminator. + * + * \return TRUE if the buffer was large enough and the string was written (i.e. \a BufferLength >= + * \a ReturnLength), otherwise FALSE. In either case, the required number of bytes is stored in + * \a ReturnLength. + * + * \remarks If the function fails but \a BufferLength != 0, a single null byte is written to the + * start of \a Buffer. + */ +BOOLEAN PhFormatToBuffer( + _In_reads_(Count) PPH_FORMAT Format, + _In_ ULONG Count, + _Out_writes_bytes_opt_(BufferLength) PWSTR Buffer, + _In_opt_ SIZE_T BufferLength, + _Out_opt_ PSIZE_T ReturnLength + ) +{ + PWSTR buffer; + SIZE_T usedLength; + BOOLEAN overrun; + + buffer = Buffer; + usedLength = 0; + overrun = FALSE; + + // Make sure we don't try to write anything if we don't have a buffer. + if (!Buffer) + overrun = TRUE; + +#undef ENSURE_BUFFER +#undef OK_BUFFER +#undef ADVANCE_BUFFER + +#define ENSURE_BUFFER(NeededLength) \ + do { \ + if (!overrun && (BufferLength < usedLength + (NeededLength))) \ + overrun = TRUE; \ + } while (0) + +#define OK_BUFFER (!overrun) + +#define ADVANCE_BUFFER(Length) \ + do { buffer += (Length) / sizeof(WCHAR); usedLength += (Length); } while (0) + +#include "format_i.h" + + // Write the null-terminator. + ENSURE_BUFFER(sizeof(WCHAR)); + if (OK_BUFFER) + *buffer = 0; + else if (Buffer && BufferLength != 0) // try to null-terminate even if this function fails + *Buffer = 0; + ADVANCE_BUFFER(sizeof(WCHAR)); + + if (ReturnLength) + *ReturnLength = usedLength; + + return OK_BUFFER; +} diff --git a/phlib/format_i.h b/phlib/format_i.h index e3b219abe5f9..31a5771303ed 100644 --- a/phlib/format_i.h +++ b/phlib/format_i.h @@ -1,568 +1,568 @@ -/* - * This file contains the actual formatting code used by various public interface functions. - * - * There are three macros defined by the parent function which control how this code writes the - * formatted string: - * * ENSURE_BUFFER - This macro is passed the number of bytes required whenever characters need to - * be written to the buffer. The macro can resize the buffer if needed. - * * OK_BUFFER - This macro returns TRUE if it is OK to write to the buffer, otherwise FALSE when - * the buffer is too large, is not specified, or some other error has occurred. - * * ADVANCE_BUFFER - This macro is passed the number of bytes written to the buffer and should - * increment the "buffer" pointer and "usedLength" counter. - * In addition to these macros, the "buffer" and "usedLength" variables are assumed to be present. - * - * The below code defines many macros; this is so that composite formatting types can be constructed - * (e.g. the "size" type). - */ - -{ - if (PhBeginInitOnce(&PhpFormatInitOnce)) - { - WCHAR localeBuffer[4]; - - if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, localeBuffer, 4) && - (localeBuffer[0] != 0 && localeBuffer[1] == 0)) - { - PhpFormatDecimalSeparator = localeBuffer[0]; - } - - if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, localeBuffer, 4) && - (localeBuffer[0] != 0 && localeBuffer[1] == 0)) - { - PhpFormatThousandSeparator = localeBuffer[0]; - } - - if (PhpFormatDecimalSeparator != '.') - PhpFormatUserLocale = _create_locale(LC_ALL, ""); - - PhEndInitOnce(&PhpFormatInitOnce); - } - - while (Count--) - { - PPH_FORMAT format; - SIZE_T partLength; - WCHAR tempBuffer[BUFFER_SIZE]; - ULONG flags; - ULONG int32; - ULONG64 int64; - - format = Format++; - - // Save the currently used length so we can compute the part length later. - partLength = usedLength; - - flags = 0; - - switch (format->Type & FormatTypeMask) - { - - // Characters and Strings - - case CharFormatType: - ENSURE_BUFFER(sizeof(WCHAR)); - if (OK_BUFFER) - *buffer = format->u.Char; - ADVANCE_BUFFER(sizeof(WCHAR)); - break; - case StringFormatType: - ENSURE_BUFFER(format->u.String.Length); - if (OK_BUFFER) - memcpy(buffer, format->u.String.Buffer, format->u.String.Length); - ADVANCE_BUFFER(format->u.String.Length); - break; - case StringZFormatType: - { - SIZE_T count; - - count = PhCountStringZ(format->u.StringZ); - ENSURE_BUFFER(count * sizeof(WCHAR)); - if (OK_BUFFER) - memcpy(buffer, format->u.StringZ, count * sizeof(WCHAR)); - ADVANCE_BUFFER(count * sizeof(WCHAR)); - } - break; - case MultiByteStringFormatType: - case MultiByteStringZFormatType: - { - ULONG bytesInUnicodeString; - PSTR multiByteBuffer; - SIZE_T multiByteLength; - - if (format->Type == MultiByteStringFormatType) - { - multiByteBuffer = format->u.MultiByteString.Buffer; - multiByteLength = format->u.MultiByteString.Length; - } - else - { - multiByteBuffer = format->u.MultiByteStringZ; - multiByteLength = strlen(multiByteBuffer); - } - - if (NT_SUCCESS(RtlMultiByteToUnicodeSize( - &bytesInUnicodeString, - multiByteBuffer, - (ULONG)multiByteLength - ))) - { - ENSURE_BUFFER(bytesInUnicodeString); - - if (!OK_BUFFER || NT_SUCCESS(RtlMultiByteToUnicodeN( - buffer, - bytesInUnicodeString, - NULL, - multiByteBuffer, - (ULONG)multiByteLength - ))) - { - ADVANCE_BUFFER(bytesInUnicodeString); - } - } - } - break; - - // Integers - -#define PROCESS_DIGIT(Input) \ - do { \ - r = (ULONG)(Input % radix); \ - Input /= radix; \ - *temp-- = integerToChar[r]; \ - tempCount++; \ - } while (0) - -#define COMMON_INTEGER_FORMAT(Input, Format) \ - do { \ - ULONG radix; \ - PCHAR integerToChar; \ - PWSTR temp; \ - ULONG tempCount; \ - ULONG r; \ - ULONG preCount; \ - ULONG padCount; \ - \ - radix = 10; \ - if (((Format)->Type & FormatUseRadix) && (Format)->Radix >= 2 && (Format)->Radix <= 69) \ - radix = (Format)->Radix; \ - integerToChar = PhIntegerToChar; \ - if ((Format)->Type & FormatUpperCase) \ - integerToChar = PhIntegerToCharUpper; \ - temp = tempBuffer + BUFFER_SIZE - 1; \ - tempCount = 0; \ - \ - if (Input != 0) \ - { \ - if ((Format)->Type & FormatGroupDigits) \ - { \ - ULONG needsSep = 0; \ - \ - do \ - { \ - PROCESS_DIGIT(Input); \ - \ - if (++needsSep == 3 && Input != 0) /* get rid of trailing separator */ \ - { \ - *temp-- = PhpFormatThousandSeparator; \ - tempCount++; \ - needsSep = 0; \ - } \ - } while (Input != 0); \ - } \ - else \ - { \ - do \ - { \ - PROCESS_DIGIT(Input); \ - } while (Input != 0); \ - } \ - } \ - else \ - { \ - *temp-- = '0'; \ - tempCount++; \ - } \ - \ - preCount = 0; \ - \ - if (flags & PHP_FORMAT_NEGATIVE) \ - preCount++; \ - else if ((Format)->Type & FormatPrefixSign) \ - preCount++; \ - \ - if (((Format)->Type & FormatPadZeros) && !((Format)->Type & FormatGroupDigits)) \ - { \ - if (preCount + tempCount < (Format)->Width) \ - { \ - flags |= PHP_FORMAT_PAD; \ - padCount = (Format)->Width - (preCount + tempCount); \ - preCount += padCount; \ - } \ - } \ - \ - temp++; \ - ENSURE_BUFFER((preCount + tempCount) * sizeof(WCHAR)); \ - if (OK_BUFFER) \ - { \ - if (flags & PHP_FORMAT_NEGATIVE) \ - *buffer++ = '-'; \ - else if ((Format)->Type & FormatPrefixSign) \ - *buffer++ = '+'; \ - \ - if (flags & PHP_FORMAT_PAD) \ - { \ - wmemset(buffer, '0', padCount); \ - buffer += padCount; \ - } \ - \ - memcpy(buffer, temp, tempCount * sizeof(WCHAR)); \ - buffer += tempCount; \ - } \ - usedLength += (preCount + tempCount) * sizeof(WCHAR); \ - } while (0) - -#ifndef _WIN64 - case IntPtrFormatType: - int32 = format->u.IntPtr; - goto CommonMaybeNegativeInt32Format; -#endif - case Int32FormatType: - int32 = format->u.Int32; - -#ifndef _WIN64 -CommonMaybeNegativeInt32Format: -#endif - if ((LONG)int32 < 0) - { - int32 = -(LONG)int32; - flags |= PHP_FORMAT_NEGATIVE; - } - - goto CommonInt32Format; -#ifndef _WIN64 - case UIntPtrFormatType: - int32 = format->u.UIntPtr; - goto CommonInt32Format; -#endif - case UInt32FormatType: - int32 = format->u.UInt32; -CommonInt32Format: - COMMON_INTEGER_FORMAT(int32, format); - break; -#ifdef _WIN64 - case IntPtrFormatType: - int64 = format->u.IntPtr; - goto CommonMaybeNegativeInt64Format; -#endif - case Int64FormatType: - int64 = format->u.Int64; - -#ifdef _WIN64 -CommonMaybeNegativeInt64Format: -#endif - if ((LONG64)int64 < 0) - { - int64 = -(LONG64)int64; - flags |= PHP_FORMAT_NEGATIVE; - } - - goto CommonInt64Format; -#ifdef _WIN64 - case UIntPtrFormatType: - int64 = format->u.UIntPtr; - goto CommonInt64Format; -#endif - case UInt64FormatType: - int64 = format->u.UInt64; -CommonInt64Format: - COMMON_INTEGER_FORMAT(int64, format); - break; - - // Floating point numbers - -#define COMMON_DOUBLE_FORMAT(Format) \ - do { \ - ULONG precision; \ - DOUBLE value; \ - CHAR c; \ - PSTR temp; \ - ULONG length; \ - \ - if ((Format)->Type & FormatUsePrecision) \ - { \ - precision = (Format)->Precision; \ - \ - if (precision > BUFFER_SIZE - 1 - _CVTBUFSIZE) \ - precision = BUFFER_SIZE - 1 - _CVTBUFSIZE; \ - } \ - else \ - { \ - precision = 6; \ - } \ - \ - c = 'f'; \ - \ - if ((Format)->Type & FormatStandardForm) \ - c = 'e'; \ - else if ((Format)->Type & FormatHexadecimalForm) \ - c = 'a'; \ - \ - /* Use MS CRT routines to do the work. */ \ - \ - value = (Format)->u.Double; \ - temp = (PSTR)tempBuffer + 1; /* leave one character so we can insert a prefix if needed */ \ - _cfltcvt_l( \ - &value, \ - temp, \ - sizeof(tempBuffer) - 1, \ - c, \ - precision, \ - !!((Format)->Type & FormatUpperCase), \ - PhpFormatUserLocale \ - ); \ - \ - /* if (((Format)->Type & FormatForceDecimalPoint) && precision == 0) */ \ - /* _forcdecpt_l(tempBufferAnsi, PhpFormatUserLocale); */ \ - if ((Format)->Type & FormatCropZeros) \ - PhpCropZeros(temp, PhpFormatUserLocale); \ - \ - length = (ULONG)strlen(temp); \ - \ - if (temp[0] == '-') \ - { \ - flags |= PHP_FORMAT_NEGATIVE; \ - temp++; \ - length--; \ - } \ - else if ((Format)->Type & FormatPrefixSign) \ - { \ - flags |= PHP_FORMAT_POSITIVE; \ - } \ - \ - if (((Format)->Type & FormatGroupDigits) && !((Format)->Type & (FormatStandardForm | FormatHexadecimalForm))) \ - { \ - PSTR whole; \ - PSTR decimalPoint; \ - ULONG wholeCount; \ - ULONG sepsCount; \ - ULONG ensureLength; \ - ULONG copyCount; \ - ULONG needsSep; \ - \ - /* Find the first non-digit character and assume that is the */ \ - /* decimal point (or the end of the string). */ \ - \ - whole = temp; \ - decimalPoint = temp; \ - \ - while ((UCHAR)(*decimalPoint - '0') < 10) \ - decimalPoint++; \ - \ - /* Copy the characters to the output buffer, and at the same time */ \ - /* insert the separators. */ \ - \ - wholeCount = (ULONG)(decimalPoint - temp); \ - \ - if (wholeCount != 0) \ - sepsCount = (wholeCount + 2) / 3 - 1; \ - else \ - sepsCount = 0; \ - \ - ensureLength = (length + sepsCount) * sizeof(WCHAR); \ - if (flags & (PHP_FORMAT_NEGATIVE | PHP_FORMAT_POSITIVE)) \ - ensureLength += sizeof(WCHAR); \ - ENSURE_BUFFER(ensureLength); \ - \ - copyCount = wholeCount; \ - needsSep = (wholeCount + 2) % 3; \ - \ - if (OK_BUFFER) \ - { \ - if (flags & PHP_FORMAT_NEGATIVE) \ - *buffer++ = '-'; \ - else if (flags & PHP_FORMAT_POSITIVE) \ - *buffer++ = '+'; \ - \ - while (copyCount--) \ - { \ - *buffer++ = *whole++; \ - \ - if (needsSep-- == 0 && copyCount != 0) /* get rid of trailing separator */ \ - { \ - *buffer++ = PhpFormatThousandSeparator; \ - needsSep = 2; \ - } \ - } \ - } \ - \ - if (flags & (PHP_FORMAT_NEGATIVE | PHP_FORMAT_POSITIVE)) \ - usedLength += sizeof(WCHAR); \ - usedLength += (wholeCount + sepsCount) * sizeof(WCHAR); \ - \ - /* Copy the rest. */ \ - \ - copyCount = length - wholeCount; \ - \ - if (OK_BUFFER) \ - { \ - PhZeroExtendToUtf16Buffer(decimalPoint, copyCount, buffer); \ - ADVANCE_BUFFER(copyCount * sizeof(WCHAR)); \ - } \ - } \ - else \ - { \ - SIZE_T preLength; \ - SIZE_T padLength; \ - \ - /* Take care of the sign and zero padding. */ \ - preLength = 0; \ - \ - if (flags & (PHP_FORMAT_NEGATIVE | PHP_FORMAT_POSITIVE)) \ - preLength++; \ - \ - if ((Format)->Type & FormatPadZeros) \ - { \ - if (preLength + length < (Format)->Width) \ - { \ - flags |= PHP_FORMAT_PAD; \ - padLength = (Format)->Width - (preLength + length); \ - preLength += padLength; \ - } \ - } \ - /* We don't need to group digits, so directly copy the characters */ \ - /* to the output buffer. */ \ - \ - ENSURE_BUFFER((preLength + length) * sizeof(WCHAR)); \ - \ - if (OK_BUFFER) \ - { \ - if (flags & PHP_FORMAT_NEGATIVE) \ - *buffer++ = '-'; \ - else if (flags & PHP_FORMAT_POSITIVE) \ - *buffer++ = '+'; \ - \ - if (flags & PHP_FORMAT_PAD) \ - { \ - wmemset(buffer, '0', padLength); \ - buffer += padLength; \ - } \ - } \ - \ - usedLength += preLength * sizeof(WCHAR); \ - \ - if (OK_BUFFER) \ - { \ - PhZeroExtendToUtf16Buffer((PSTR)temp, length, buffer); \ - ADVANCE_BUFFER(length * sizeof(WCHAR)); \ - } \ - } \ - } while (0) - - case DoubleFormatType: - flags = 0; - COMMON_DOUBLE_FORMAT(format); - break; - - // Additional types - - case SizeFormatType: - { - ULONG i = 0; - ULONG maxSizeUnit; - DOUBLE s; - PH_FORMAT doubleFormat; - - s = (DOUBLE)format->u.Size; - - if (format->u.Size == 0) - { - ENSURE_BUFFER(sizeof(WCHAR)); - if (OK_BUFFER) - *buffer = '0'; - ADVANCE_BUFFER(sizeof(WCHAR)); - goto ContinueLoop; - } - - if (format->Type & FormatUseRadix) - maxSizeUnit = format->Radix; - else - maxSizeUnit = PhMaxSizeUnit; - - while ( - s >= 1000 && - i < sizeof(PhpSizeUnitNamesCounted) / sizeof(PH_STRINGREF) && - i < maxSizeUnit - ) - { - s /= 1024; - i++; - } - - // Format the number, then append the unit name. - - doubleFormat.Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros | FormatGroupDigits; - doubleFormat.Precision = (format->Type & FormatUsePrecision) ? format->Precision : 2; - doubleFormat.Width = 0; // stupid compiler - doubleFormat.u.Double = s; - flags = 0; - COMMON_DOUBLE_FORMAT(&doubleFormat); - - ENSURE_BUFFER(sizeof(WCHAR) + PhpSizeUnitNamesCounted[i].Length); - if (OK_BUFFER) - { - *buffer = ' '; - memcpy(buffer + 1, PhpSizeUnitNamesCounted[i].Buffer, PhpSizeUnitNamesCounted[i].Length); - } - ADVANCE_BUFFER(sizeof(WCHAR) + PhpSizeUnitNamesCounted[i].Length); - } - break; - } - -ContinueLoop: - partLength = usedLength - partLength; - - if (format->Type & (FormatLeftAlign | FormatRightAlign)) - { - SIZE_T newLength; - SIZE_T addLength; - - newLength = format->Width * sizeof(WCHAR); - - // We only pad and never truncate. - if (partLength < newLength) - { - addLength = newLength - partLength; - ENSURE_BUFFER(addLength); - - if (OK_BUFFER) - { - WCHAR pad; - - if (format->Type & FormatUsePad) - pad = format->Pad; - else - pad = ' '; - - if (format->Type & FormatLeftAlign) - { - // Left alignment is easy; we just fill the remaining space with the pad - // character. - wmemset(buffer, pad, addLength / sizeof(WCHAR)); - } - else - { - PWSTR start; - - // Right alignment is much slower and involves moving the text forward, then - // filling in the space before it. - start = buffer - partLength / sizeof(WCHAR); - memmove(start + addLength / sizeof(WCHAR), start, partLength); - wmemset(start, pad, addLength / sizeof(WCHAR)); - } - } - - ADVANCE_BUFFER(addLength); - } - } - } -} +/* + * This file contains the actual formatting code used by various public interface functions. + * + * There are three macros defined by the parent function which control how this code writes the + * formatted string: + * * ENSURE_BUFFER - This macro is passed the number of bytes required whenever characters need to + * be written to the buffer. The macro can resize the buffer if needed. + * * OK_BUFFER - This macro returns TRUE if it is OK to write to the buffer, otherwise FALSE when + * the buffer is too large, is not specified, or some other error has occurred. + * * ADVANCE_BUFFER - This macro is passed the number of bytes written to the buffer and should + * increment the "buffer" pointer and "usedLength" counter. + * In addition to these macros, the "buffer" and "usedLength" variables are assumed to be present. + * + * The below code defines many macros; this is so that composite formatting types can be constructed + * (e.g. the "size" type). + */ + +{ + if (PhBeginInitOnce(&PhpFormatInitOnce)) + { + WCHAR localeBuffer[4]; + + if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, localeBuffer, 4) && + (localeBuffer[0] != 0 && localeBuffer[1] == 0)) + { + PhpFormatDecimalSeparator = localeBuffer[0]; + } + + if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, localeBuffer, 4) && + (localeBuffer[0] != 0 && localeBuffer[1] == 0)) + { + PhpFormatThousandSeparator = localeBuffer[0]; + } + + if (PhpFormatDecimalSeparator != '.') + PhpFormatUserLocale = _create_locale(LC_ALL, ""); + + PhEndInitOnce(&PhpFormatInitOnce); + } + + while (Count--) + { + PPH_FORMAT format; + SIZE_T partLength; + WCHAR tempBuffer[BUFFER_SIZE]; + ULONG flags; + ULONG int32; + ULONG64 int64; + + format = Format++; + + // Save the currently used length so we can compute the part length later. + partLength = usedLength; + + flags = 0; + + switch (format->Type & FormatTypeMask) + { + + // Characters and Strings + + case CharFormatType: + ENSURE_BUFFER(sizeof(WCHAR)); + if (OK_BUFFER) + *buffer = format->u.Char; + ADVANCE_BUFFER(sizeof(WCHAR)); + break; + case StringFormatType: + ENSURE_BUFFER(format->u.String.Length); + if (OK_BUFFER) + memcpy(buffer, format->u.String.Buffer, format->u.String.Length); + ADVANCE_BUFFER(format->u.String.Length); + break; + case StringZFormatType: + { + SIZE_T count; + + count = PhCountStringZ(format->u.StringZ); + ENSURE_BUFFER(count * sizeof(WCHAR)); + if (OK_BUFFER) + memcpy(buffer, format->u.StringZ, count * sizeof(WCHAR)); + ADVANCE_BUFFER(count * sizeof(WCHAR)); + } + break; + case MultiByteStringFormatType: + case MultiByteStringZFormatType: + { + ULONG bytesInUnicodeString; + PSTR multiByteBuffer; + SIZE_T multiByteLength; + + if (format->Type == MultiByteStringFormatType) + { + multiByteBuffer = format->u.MultiByteString.Buffer; + multiByteLength = format->u.MultiByteString.Length; + } + else + { + multiByteBuffer = format->u.MultiByteStringZ; + multiByteLength = strlen(multiByteBuffer); + } + + if (NT_SUCCESS(RtlMultiByteToUnicodeSize( + &bytesInUnicodeString, + multiByteBuffer, + (ULONG)multiByteLength + ))) + { + ENSURE_BUFFER(bytesInUnicodeString); + + if (!OK_BUFFER || NT_SUCCESS(RtlMultiByteToUnicodeN( + buffer, + bytesInUnicodeString, + NULL, + multiByteBuffer, + (ULONG)multiByteLength + ))) + { + ADVANCE_BUFFER(bytesInUnicodeString); + } + } + } + break; + + // Integers + +#define PROCESS_DIGIT(Input) \ + do { \ + r = (ULONG)(Input % radix); \ + Input /= radix; \ + *temp-- = integerToChar[r]; \ + tempCount++; \ + } while (0) + +#define COMMON_INTEGER_FORMAT(Input, Format) \ + do { \ + ULONG radix; \ + PCHAR integerToChar; \ + PWSTR temp; \ + ULONG tempCount; \ + ULONG r; \ + ULONG preCount; \ + ULONG padCount; \ + \ + radix = 10; \ + if (((Format)->Type & FormatUseRadix) && (Format)->Radix >= 2 && (Format)->Radix <= 69) \ + radix = (Format)->Radix; \ + integerToChar = PhIntegerToChar; \ + if ((Format)->Type & FormatUpperCase) \ + integerToChar = PhIntegerToCharUpper; \ + temp = tempBuffer + BUFFER_SIZE - 1; \ + tempCount = 0; \ + \ + if (Input != 0) \ + { \ + if ((Format)->Type & FormatGroupDigits) \ + { \ + ULONG needsSep = 0; \ + \ + do \ + { \ + PROCESS_DIGIT(Input); \ + \ + if (++needsSep == 3 && Input != 0) /* get rid of trailing separator */ \ + { \ + *temp-- = PhpFormatThousandSeparator; \ + tempCount++; \ + needsSep = 0; \ + } \ + } while (Input != 0); \ + } \ + else \ + { \ + do \ + { \ + PROCESS_DIGIT(Input); \ + } while (Input != 0); \ + } \ + } \ + else \ + { \ + *temp-- = '0'; \ + tempCount++; \ + } \ + \ + preCount = 0; \ + \ + if (flags & PHP_FORMAT_NEGATIVE) \ + preCount++; \ + else if ((Format)->Type & FormatPrefixSign) \ + preCount++; \ + \ + if (((Format)->Type & FormatPadZeros) && !((Format)->Type & FormatGroupDigits)) \ + { \ + if (preCount + tempCount < (Format)->Width) \ + { \ + flags |= PHP_FORMAT_PAD; \ + padCount = (Format)->Width - (preCount + tempCount); \ + preCount += padCount; \ + } \ + } \ + \ + temp++; \ + ENSURE_BUFFER((preCount + tempCount) * sizeof(WCHAR)); \ + if (OK_BUFFER) \ + { \ + if (flags & PHP_FORMAT_NEGATIVE) \ + *buffer++ = '-'; \ + else if ((Format)->Type & FormatPrefixSign) \ + *buffer++ = '+'; \ + \ + if (flags & PHP_FORMAT_PAD) \ + { \ + wmemset(buffer, '0', padCount); \ + buffer += padCount; \ + } \ + \ + memcpy(buffer, temp, tempCount * sizeof(WCHAR)); \ + buffer += tempCount; \ + } \ + usedLength += (preCount + tempCount) * sizeof(WCHAR); \ + } while (0) + +#ifndef _WIN64 + case IntPtrFormatType: + int32 = format->u.IntPtr; + goto CommonMaybeNegativeInt32Format; +#endif + case Int32FormatType: + int32 = format->u.Int32; + +#ifndef _WIN64 +CommonMaybeNegativeInt32Format: +#endif + if ((LONG)int32 < 0) + { + int32 = -(LONG)int32; + flags |= PHP_FORMAT_NEGATIVE; + } + + goto CommonInt32Format; +#ifndef _WIN64 + case UIntPtrFormatType: + int32 = format->u.UIntPtr; + goto CommonInt32Format; +#endif + case UInt32FormatType: + int32 = format->u.UInt32; +CommonInt32Format: + COMMON_INTEGER_FORMAT(int32, format); + break; +#ifdef _WIN64 + case IntPtrFormatType: + int64 = format->u.IntPtr; + goto CommonMaybeNegativeInt64Format; +#endif + case Int64FormatType: + int64 = format->u.Int64; + +#ifdef _WIN64 +CommonMaybeNegativeInt64Format: +#endif + if ((LONG64)int64 < 0) + { + int64 = -(LONG64)int64; + flags |= PHP_FORMAT_NEGATIVE; + } + + goto CommonInt64Format; +#ifdef _WIN64 + case UIntPtrFormatType: + int64 = format->u.UIntPtr; + goto CommonInt64Format; +#endif + case UInt64FormatType: + int64 = format->u.UInt64; +CommonInt64Format: + COMMON_INTEGER_FORMAT(int64, format); + break; + + // Floating point numbers + +#define COMMON_DOUBLE_FORMAT(Format) \ + do { \ + ULONG precision; \ + DOUBLE value; \ + CHAR c; \ + PSTR temp; \ + ULONG length; \ + \ + if ((Format)->Type & FormatUsePrecision) \ + { \ + precision = (Format)->Precision; \ + \ + if (precision > BUFFER_SIZE - 1 - _CVTBUFSIZE) \ + precision = BUFFER_SIZE - 1 - _CVTBUFSIZE; \ + } \ + else \ + { \ + precision = 6; \ + } \ + \ + c = 'f'; \ + \ + if ((Format)->Type & FormatStandardForm) \ + c = 'e'; \ + else if ((Format)->Type & FormatHexadecimalForm) \ + c = 'a'; \ + \ + /* Use MS CRT routines to do the work. */ \ + \ + value = (Format)->u.Double; \ + temp = (PSTR)tempBuffer + 1; /* leave one character so we can insert a prefix if needed */ \ + _cfltcvt_l( \ + &value, \ + temp, \ + sizeof(tempBuffer) - 1, \ + c, \ + precision, \ + !!((Format)->Type & FormatUpperCase), \ + PhpFormatUserLocale \ + ); \ + \ + /* if (((Format)->Type & FormatForceDecimalPoint) && precision == 0) */ \ + /* _forcdecpt_l(tempBufferAnsi, PhpFormatUserLocale); */ \ + if ((Format)->Type & FormatCropZeros) \ + PhpCropZeros(temp, PhpFormatUserLocale); \ + \ + length = (ULONG)strlen(temp); \ + \ + if (temp[0] == '-') \ + { \ + flags |= PHP_FORMAT_NEGATIVE; \ + temp++; \ + length--; \ + } \ + else if ((Format)->Type & FormatPrefixSign) \ + { \ + flags |= PHP_FORMAT_POSITIVE; \ + } \ + \ + if (((Format)->Type & FormatGroupDigits) && !((Format)->Type & (FormatStandardForm | FormatHexadecimalForm))) \ + { \ + PSTR whole; \ + PSTR decimalPoint; \ + ULONG wholeCount; \ + ULONG sepsCount; \ + ULONG ensureLength; \ + ULONG copyCount; \ + ULONG needsSep; \ + \ + /* Find the first non-digit character and assume that is the */ \ + /* decimal point (or the end of the string). */ \ + \ + whole = temp; \ + decimalPoint = temp; \ + \ + while ((UCHAR)(*decimalPoint - '0') < 10) \ + decimalPoint++; \ + \ + /* Copy the characters to the output buffer, and at the same time */ \ + /* insert the separators. */ \ + \ + wholeCount = (ULONG)(decimalPoint - temp); \ + \ + if (wholeCount != 0) \ + sepsCount = (wholeCount + 2) / 3 - 1; \ + else \ + sepsCount = 0; \ + \ + ensureLength = (length + sepsCount) * sizeof(WCHAR); \ + if (flags & (PHP_FORMAT_NEGATIVE | PHP_FORMAT_POSITIVE)) \ + ensureLength += sizeof(WCHAR); \ + ENSURE_BUFFER(ensureLength); \ + \ + copyCount = wholeCount; \ + needsSep = (wholeCount + 2) % 3; \ + \ + if (OK_BUFFER) \ + { \ + if (flags & PHP_FORMAT_NEGATIVE) \ + *buffer++ = '-'; \ + else if (flags & PHP_FORMAT_POSITIVE) \ + *buffer++ = '+'; \ + \ + while (copyCount--) \ + { \ + *buffer++ = *whole++; \ + \ + if (needsSep-- == 0 && copyCount != 0) /* get rid of trailing separator */ \ + { \ + *buffer++ = PhpFormatThousandSeparator; \ + needsSep = 2; \ + } \ + } \ + } \ + \ + if (flags & (PHP_FORMAT_NEGATIVE | PHP_FORMAT_POSITIVE)) \ + usedLength += sizeof(WCHAR); \ + usedLength += (wholeCount + sepsCount) * sizeof(WCHAR); \ + \ + /* Copy the rest. */ \ + \ + copyCount = length - wholeCount; \ + \ + if (OK_BUFFER) \ + { \ + PhZeroExtendToUtf16Buffer(decimalPoint, copyCount, buffer); \ + ADVANCE_BUFFER(copyCount * sizeof(WCHAR)); \ + } \ + } \ + else \ + { \ + SIZE_T preLength; \ + SIZE_T padLength; \ + \ + /* Take care of the sign and zero padding. */ \ + preLength = 0; \ + \ + if (flags & (PHP_FORMAT_NEGATIVE | PHP_FORMAT_POSITIVE)) \ + preLength++; \ + \ + if ((Format)->Type & FormatPadZeros) \ + { \ + if (preLength + length < (Format)->Width) \ + { \ + flags |= PHP_FORMAT_PAD; \ + padLength = (Format)->Width - (preLength + length); \ + preLength += padLength; \ + } \ + } \ + /* We don't need to group digits, so directly copy the characters */ \ + /* to the output buffer. */ \ + \ + ENSURE_BUFFER((preLength + length) * sizeof(WCHAR)); \ + \ + if (OK_BUFFER) \ + { \ + if (flags & PHP_FORMAT_NEGATIVE) \ + *buffer++ = '-'; \ + else if (flags & PHP_FORMAT_POSITIVE) \ + *buffer++ = '+'; \ + \ + if (flags & PHP_FORMAT_PAD) \ + { \ + wmemset(buffer, '0', padLength); \ + buffer += padLength; \ + } \ + } \ + \ + usedLength += preLength * sizeof(WCHAR); \ + \ + if (OK_BUFFER) \ + { \ + PhZeroExtendToUtf16Buffer((PSTR)temp, length, buffer); \ + ADVANCE_BUFFER(length * sizeof(WCHAR)); \ + } \ + } \ + } while (0) + + case DoubleFormatType: + flags = 0; + COMMON_DOUBLE_FORMAT(format); + break; + + // Additional types + + case SizeFormatType: + { + ULONG i = 0; + ULONG maxSizeUnit; + DOUBLE s; + PH_FORMAT doubleFormat; + + s = (DOUBLE)format->u.Size; + + if (format->u.Size == 0) + { + ENSURE_BUFFER(sizeof(WCHAR)); + if (OK_BUFFER) + *buffer = '0'; + ADVANCE_BUFFER(sizeof(WCHAR)); + goto ContinueLoop; + } + + if (format->Type & FormatUseRadix) + maxSizeUnit = format->Radix; + else + maxSizeUnit = PhMaxSizeUnit; + + while ( + s >= 1000 && + i < sizeof(PhpSizeUnitNamesCounted) / sizeof(PH_STRINGREF) && + i < maxSizeUnit + ) + { + s /= 1024; + i++; + } + + // Format the number, then append the unit name. + + doubleFormat.Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros | FormatGroupDigits; + doubleFormat.Precision = (format->Type & FormatUsePrecision) ? format->Precision : 2; + doubleFormat.Width = 0; // stupid compiler + doubleFormat.u.Double = s; + flags = 0; + COMMON_DOUBLE_FORMAT(&doubleFormat); + + ENSURE_BUFFER(sizeof(WCHAR) + PhpSizeUnitNamesCounted[i].Length); + if (OK_BUFFER) + { + *buffer = ' '; + memcpy(buffer + 1, PhpSizeUnitNamesCounted[i].Buffer, PhpSizeUnitNamesCounted[i].Length); + } + ADVANCE_BUFFER(sizeof(WCHAR) + PhpSizeUnitNamesCounted[i].Length); + } + break; + } + +ContinueLoop: + partLength = usedLength - partLength; + + if (format->Type & (FormatLeftAlign | FormatRightAlign)) + { + SIZE_T newLength; + SIZE_T addLength; + + newLength = format->Width * sizeof(WCHAR); + + // We only pad and never truncate. + if (partLength < newLength) + { + addLength = newLength - partLength; + ENSURE_BUFFER(addLength); + + if (OK_BUFFER) + { + WCHAR pad; + + if (format->Type & FormatUsePad) + pad = format->Pad; + else + pad = ' '; + + if (format->Type & FormatLeftAlign) + { + // Left alignment is easy; we just fill the remaining space with the pad + // character. + wmemset(buffer, pad, addLength / sizeof(WCHAR)); + } + else + { + PWSTR start; + + // Right alignment is much slower and involves moving the text forward, then + // filling in the space before it. + start = buffer - partLength / sizeof(WCHAR); + memmove(start + addLength / sizeof(WCHAR), start, partLength); + wmemset(start, pad, addLength / sizeof(WCHAR)); + } + } + + ADVANCE_BUFFER(addLength); + } + } + } +} diff --git a/phlib/global.c b/phlib/global.c index 3575b0f358c3..6370e3a1f745 100644 --- a/phlib/global.c +++ b/phlib/global.c @@ -1,239 +1,222 @@ -/* - * Process Hacker - - * global variables and initialization functions - * - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include -#include - -BOOLEAN PhInitializeSystem( - _In_ ULONG Flags - ); - -VOID PhInitializeSystemInformation( - VOID - ); - -VOID PhInitializeWindowsVersion( - VOID - ); - -PHLIBAPI PVOID PhLibImageBase; - -PHLIBAPI PWSTR PhApplicationName = L"Application"; -PHLIBAPI ULONG PhGlobalDpi = 96; -PHLIBAPI PVOID PhHeapHandle; -PHLIBAPI RTL_OSVERSIONINFOEXW PhOsVersion; -PHLIBAPI SYSTEM_BASIC_INFORMATION PhSystemBasicInformation; -PHLIBAPI ULONG WindowsVersion; - -PHLIBAPI ACCESS_MASK ProcessQueryAccess; -PHLIBAPI ACCESS_MASK ProcessAllAccess; -PHLIBAPI ACCESS_MASK ThreadQueryAccess; -PHLIBAPI ACCESS_MASK ThreadSetAccess; -PHLIBAPI ACCESS_MASK ThreadAllAccess; - -// Internal data -#ifdef DEBUG -PHLIB_STATISTICS_BLOCK PhLibStatisticsBlock; -#endif - -NTSTATUS PhInitializePhLib( - VOID - ) -{ - return PhInitializePhLibEx( - 0xffffffff, // all possible features - 0, - 0 - ); -} - -NTSTATUS PhInitializePhLibEx( - _In_ ULONG Flags, - _In_opt_ SIZE_T HeapReserveSize, - _In_opt_ SIZE_T HeapCommitSize - ) -{ - PhHeapHandle = RtlCreateHeap( - HEAP_GROWABLE | HEAP_CLASS_1, - NULL, - HeapReserveSize ? HeapReserveSize : 2 * 1024 * 1024, // 2 MB - HeapCommitSize ? HeapCommitSize : 1024 * 1024, // 1 MB - NULL, - NULL - ); - - if (!PhHeapHandle) - return STATUS_INSUFFICIENT_RESOURCES; - - PhLibImageBase = NtCurrentPeb()->ImageBaseAddress; - - PhInitializeWindowsVersion(); - PhInitializeSystemInformation(); - - if (!PhQueuedLockInitialization()) - return STATUS_UNSUCCESSFUL; - - if (!NT_SUCCESS(PhRefInitialization())) - return STATUS_UNSUCCESSFUL; - if (!PhBaseInitialization()) - return STATUS_UNSUCCESSFUL; - - if (!PhInitializeSystem(Flags)) - return STATUS_UNSUCCESSFUL; - - return STATUS_SUCCESS; -} - -#ifndef _WIN64 -BOOLEAN PhIsExecutingInWow64( - VOID - ) -{ - static BOOLEAN valid = FALSE; - static BOOLEAN isWow64; - - if (!valid) - { - PhGetProcessIsWow64(NtCurrentProcess(), &isWow64); - MemoryBarrier(); - valid = TRUE; - } - - return isWow64; -} -#endif - -static BOOLEAN PhInitializeSystem( - _In_ ULONG Flags - ) -{ - if (Flags & PHLIB_INIT_MODULE_FILE_STREAM) - { - if (!PhFileStreamInitialization()) - return FALSE; - } - - if (Flags & PHLIB_INIT_MODULE_SYMBOL_PROVIDER) - { - if (!PhSymbolProviderInitialization()) - return FALSE; - } - - return TRUE; -} - -static VOID PhInitializeSystemInformation( - VOID - ) -{ - NtQuerySystemInformation( - SystemBasicInformation, - &PhSystemBasicInformation, - sizeof(SYSTEM_BASIC_INFORMATION), - NULL - ); -} - -static VOID PhInitializeWindowsVersion( - VOID - ) -{ - RTL_OSVERSIONINFOEXW versionInfo; - ULONG majorVersion; - ULONG minorVersion; - - versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - - if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo))) - { - WindowsVersion = WINDOWS_NEW; - return; - } - - memcpy(&PhOsVersion, &versionInfo, sizeof(RTL_OSVERSIONINFOEXW)); - majorVersion = versionInfo.dwMajorVersion; - minorVersion = versionInfo.dwMinorVersion; - - if (majorVersion == 5 && minorVersion < 1 || majorVersion < 5) - { - WindowsVersion = WINDOWS_ANCIENT; - } - /* Windows XP */ - else if (majorVersion == 5 && minorVersion == 1) - { - WindowsVersion = WINDOWS_XP; - } - /* Windows Server 2003 */ - else if (majorVersion == 5 && minorVersion == 2) - { - WindowsVersion = WINDOWS_SERVER_2003; - } - /* Windows Vista, Windows Server 2008 */ - else if (majorVersion == 6 && minorVersion == 0) - { - WindowsVersion = WINDOWS_VISTA; - } - /* Windows 7, Windows Server 2008 R2 */ - else if (majorVersion == 6 && minorVersion == 1) - { - WindowsVersion = WINDOWS_7; - } - /* Windows 8 */ - else if (majorVersion == 6 && minorVersion == 2) - { - WindowsVersion = WINDOWS_8; - } - /* Windows 8.1 */ - else if (majorVersion == 6 && minorVersion == 3) - { - WindowsVersion = WINDOWS_8_1; - } - /* Windows 10 */ - else if (majorVersion == 10 && minorVersion == 0) - { - WindowsVersion = WINDOWS_10; - } - else if (majorVersion == 10 && minorVersion > 0 || majorVersion > 10) - { - WindowsVersion = WINDOWS_NEW; - } - - if (WINDOWS_HAS_LIMITED_ACCESS) - { - ProcessQueryAccess = PROCESS_QUERY_LIMITED_INFORMATION; - ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1fff; - ThreadQueryAccess = THREAD_QUERY_LIMITED_INFORMATION; - ThreadSetAccess = THREAD_SET_LIMITED_INFORMATION; - ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; - } - else - { - ProcessQueryAccess = PROCESS_QUERY_INFORMATION; - ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; - ThreadQueryAccess = THREAD_QUERY_INFORMATION; - ThreadSetAccess = THREAD_SET_INFORMATION; - ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff; - } -} +/* + * Process Hacker - + * global variables and initialization functions + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +VOID PhInitializeSystemInformation( + VOID + ); + +VOID PhInitializeWindowsVersion( + VOID + ); + +PHLIBAPI PVOID PhInstanceHandle = NULL; +PHLIBAPI PWSTR PhApplicationName = NULL; +PHLIBAPI ULONG PhGlobalDpi = 96; +PHLIBAPI PVOID PhHeapHandle = NULL; +PHLIBAPI RTL_OSVERSIONINFOEXW PhOsVersion = { 0 }; +PHLIBAPI SYSTEM_BASIC_INFORMATION PhSystemBasicInformation = { 0 }; +PHLIBAPI ULONG WindowsVersion = WINDOWS_NEW; + +PHLIBAPI ACCESS_MASK ProcessQueryAccess = PROCESS_QUERY_LIMITED_INFORMATION; +PHLIBAPI ACCESS_MASK ProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1fff; +PHLIBAPI ACCESS_MASK ThreadQueryAccess = THREAD_QUERY_LIMITED_INFORMATION; +PHLIBAPI ACCESS_MASK ThreadSetAccess = THREAD_SET_LIMITED_INFORMATION; +PHLIBAPI ACCESS_MASK ThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff; + +// Internal data +#ifdef DEBUG +PHLIB_STATISTICS_BLOCK PhLibStatisticsBlock; +#endif + +NTSTATUS PhInitializePhLib( + VOID + ) +{ + return PhInitializePhLibEx( + L"Application", + ULONG_MAX, // all possible features + NtCurrentPeb()->ImageBaseAddress, + 0, + 0 + ); +} + +NTSTATUS PhInitializePhLibEx( + _In_ PWSTR ApplicationName, + _In_ ULONG Flags, + _In_ PVOID ImageBaseAddress, + _In_opt_ SIZE_T HeapReserveSize, + _In_opt_ SIZE_T HeapCommitSize + ) +{ + PhApplicationName = ApplicationName; + PhInstanceHandle = ImageBaseAddress; + + PhHeapHandle = RtlCreateHeap( + HEAP_GROWABLE | HEAP_CLASS_1, + NULL, + HeapReserveSize ? HeapReserveSize : 2 * 1024 * 1024, // 2 MB + HeapCommitSize ? HeapCommitSize : 1024 * 1024, // 1 MB + NULL, + NULL + ); + + if (!PhHeapHandle) + return STATUS_INSUFFICIENT_RESOURCES; + + RtlSetHeapInformation( + PhHeapHandle, + HeapCompatibilityInformation, + &(ULONG){ HEAP_COMPATIBILITY_LFH }, + sizeof(ULONG) + ); + + PhInitializeWindowsVersion(); + PhInitializeSystemInformation(); + + if (!PhQueuedLockInitialization()) + return STATUS_UNSUCCESSFUL; + + if (!NT_SUCCESS(PhRefInitialization())) + return STATUS_UNSUCCESSFUL; + + if (!PhBaseInitialization()) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} + +BOOLEAN PhIsExecutingInWow64( + VOID + ) +{ +#ifndef _WIN64 + static BOOLEAN valid = FALSE; + static BOOLEAN isWow64; + + if (!valid) + { + PhGetProcessIsWow64(NtCurrentProcess(), &isWow64); + MemoryBarrier(); + valid = TRUE; + } + + return isWow64; +#else + return FALSE; +#endif +} + +VOID PhInitializeSystemInformation( + VOID + ) +{ + NtQuerySystemInformation( + SystemBasicInformation, + &PhSystemBasicInformation, + sizeof(SYSTEM_BASIC_INFORMATION), + NULL + ); +} + +VOID PhInitializeWindowsVersion( + VOID + ) +{ + RTL_OSVERSIONINFOEXW versionInfo; + ULONG majorVersion; + ULONG minorVersion; + ULONG buildVersion; + + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + + if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo))) + { + WindowsVersion = WINDOWS_NEW; + return; + } + + memcpy(&PhOsVersion, &versionInfo, sizeof(RTL_OSVERSIONINFOEXW)); + majorVersion = versionInfo.dwMajorVersion; + minorVersion = versionInfo.dwMinorVersion; + buildVersion = versionInfo.dwBuildNumber; + + // Windows 7, Windows Server 2008 R2 + if (majorVersion == 6 && minorVersion == 1) + { + WindowsVersion = WINDOWS_7; + } + // Windows 8, Windows Server 2012 + else if (majorVersion == 6 && minorVersion == 2) + { + WindowsVersion = WINDOWS_8; + } + // Windows 8.1, Windows Server 2012 R2 + else if (majorVersion == 6 && minorVersion == 3) + { + WindowsVersion = WINDOWS_8_1; + } + // Windows 10, Windows Server 2016 + else if (majorVersion == 10 && minorVersion == 0) + { + switch (buildVersion) + { + case 10240: + WindowsVersion = WINDOWS_10; + break; + case 10586: + WindowsVersion = WINDOWS_10_TH2; + break; + case 14393: + WindowsVersion = WINDOWS_10_RS1; + break; + case 15063: + WindowsVersion = WINDOWS_10_RS2; + break; + case 16299: + WindowsVersion = WINDOWS_10_RS3; + break; + case 17134: + WindowsVersion = WINDOWS_10_RS4; + break; + case 17763: + WindowsVersion = WINDOWS_10_RS5; + break; + case 18362: + WindowsVersion = WINDOWS_10_RS6; + break; + default: + WindowsVersion = WINDOWS_10; + break; + } + } + else + { + WindowsVersion = WINDOWS_NEW; + } +} diff --git a/phlib/graph.c b/phlib/graph.c index 27dfb6359b3a..6c990a4abd56 100644 --- a/phlib/graph.c +++ b/phlib/graph.c @@ -1,1335 +1,1424 @@ -/* - * Process Hacker - - * graph control - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include -#include - -#define COLORREF_TO_BITS(Color) (_byteswap_ulong(Color) >> 8) - -typedef struct _PHP_GRAPH_CONTEXT -{ - HWND Handle; - ULONG Style; - ULONG_PTR Id; - PH_GRAPH_DRAW_INFO DrawInfo; - PH_GRAPH_OPTIONS Options; - BOOLEAN NeedsUpdate; - BOOLEAN NeedsDraw; - - HDC BufferedContext; - HBITMAP BufferedOldBitmap; - HBITMAP BufferedBitmap; - PVOID BufferedBits; - RECT BufferedContextRect; - - HDC FadeOutContext; - HBITMAP FadeOutOldBitmap; - HBITMAP FadeOutBitmap; - PVOID FadeOutBits; - RECT FadeOutContextRect; - - HWND TooltipHandle; - WNDPROC TooltipOldWndProc; - POINT LastCursorLocation; -} PHP_GRAPH_CONTEXT, *PPHP_GRAPH_CONTEXT; - -LRESULT CALLBACK PhpGraphWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -RECT PhNormalGraphTextMargin = { 5, 5, 5, 5 }; -RECT PhNormalGraphTextPadding = { 3, 3, 3, 3 }; - -BOOLEAN PhGraphControlInitialization( - VOID - ) -{ - WNDCLASSEX c = { sizeof(c) }; - - c.style = CS_GLOBALCLASS | CS_DBLCLKS; - c.lpfnWndProc = PhpGraphWndProc; - c.cbClsExtra = 0; - c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; - c.hIcon = NULL; - c.hCursor = LoadCursor(NULL, IDC_ARROW); - c.hbrBackground = NULL; - c.lpszMenuName = NULL; - c.lpszClassName = PH_GRAPH_CLASSNAME; - c.hIconSm = NULL; - - if (!RegisterClassEx(&c)) - return FALSE; - - return TRUE; -} - -FORCEINLINE VOID PhpGetGraphPoint( - _In_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ ULONG Index, - _Out_ PULONG H1, - _Out_ PULONG H2 - ) -{ - if (Index < DrawInfo->LineDataCount) - { - FLOAT f1; - FLOAT f2; - - f1 = DrawInfo->LineData1[Index]; - - if (f1 < 0) - f1 = 0; - if (f1 > 1) - f1 = 1; - - *H1 = (ULONG)(f1 * (DrawInfo->Height - 1)); - - if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2) - { - f2 = f1 + DrawInfo->LineData2[Index]; - - if (f2 < 0) - f2 = 0; - if (f2 > 1) - f2 = 1; - - *H2 = (ULONG)(f2 * (DrawInfo->Height - 1)); - } - else - { - *H2 = *H1; - } - } - else - { - *H1 = 0; - *H2 = 0; - } -} - -/** - * Draws a graph directly to memory. - * - * \param hdc The DC to draw to. This is only used when drawing text. - * \param Bits The bits in a bitmap. - * \param DrawInfo A structure which contains graphing information. - * - * \remarks The following information is fixed: - * \li The graph is fixed to the origin (0, 0). - * \li The total size of the bitmap is assumed to be \a Width and \a Height in \a DrawInfo. - * \li \a Step is fixed at 2. - * \li If \ref PH_GRAPH_USE_LINE_2 is specified in \a Flags, \ref PH_GRAPH_OVERLAY_LINE_2 is never - * used. - */ -VOID PhDrawGraphDirect( - _In_ HDC hdc, - _In_ PVOID Bits, - _In_ PPH_GRAPH_DRAW_INFO DrawInfo - ) -{ - PULONG bits = Bits; - LONG width = DrawInfo->Width; - LONG height = DrawInfo->Height; - LONG numberOfPixels = width * height; - ULONG flags = DrawInfo->Flags; - LONG i; - LONG x; - - BOOLEAN intermediate = FALSE; // whether we are currently between two data positions - ULONG dataIndex = 0; // the data index of the current position - ULONG h1_i; // the line 1 height value to the left of the current position - ULONG h1_o; // the line 1 height value at the current position - ULONG h2_i; // the line 1 + line 2 height value to the left of the current position - ULONG h2_o; // the line 1 + line 2 height value at the current position - ULONG h1; // current pixel - ULONG h1_left; // current pixel - ULONG h2; // current pixel - ULONG h2_left; // current pixel - - LONG mid; - LONG h1_low1; - LONG h1_high1; - LONG h1_low2; - LONG h1_high2; - LONG h2_low1; - LONG h2_high1; - LONG h2_low2; - LONG h2_high2; - LONG old_low2; - LONG old_high2; - - ULONG lineColor1; - ULONG lineBackColor1; - ULONG lineColor2; - ULONG lineBackColor2; - FLOAT gridHeight; - LONG gridYThreshold; - ULONG gridYCounter; - ULONG gridColor; - FLOAT gridBase; - FLOAT gridLevel; - - ULONG yLabelMax; - ULONG yLabelDataIndex; - - lineColor1 = COLORREF_TO_BITS(DrawInfo->LineColor1); - lineBackColor1 = COLORREF_TO_BITS(DrawInfo->LineBackColor1); - lineColor2 = COLORREF_TO_BITS(DrawInfo->LineColor2); - lineBackColor2 = COLORREF_TO_BITS(DrawInfo->LineBackColor2); - - if (DrawInfo->BackColor == 0) - { - memset(bits, 0, numberOfPixels * 4); - } - else - { - PhFillMemoryUlong(bits, COLORREF_TO_BITS(DrawInfo->BackColor), numberOfPixels); - } - - x = width - 1; - h1_low2 = MAXLONG; - h1_high2 = 0; - h2_low2 = MAXLONG; - h2_high2 = 0; - - PhpGetGraphPoint(DrawInfo, 0, &h1_i, &h2_i); - - if (flags & (PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y)) - { - gridHeight = max(DrawInfo->GridHeight, 0); - gridLevel = gridHeight; - gridYThreshold = DrawInfo->GridYThreshold; - gridYCounter = DrawInfo->GridWidth - (DrawInfo->GridXOffset * DrawInfo->Step) % DrawInfo->GridWidth - 1; - gridColor = COLORREF_TO_BITS(DrawInfo->GridColor); - } - - if ((flags & (PH_GRAPH_USE_GRID_Y | PH_GRAPH_LOGARITHMIC_GRID_Y)) == (PH_GRAPH_USE_GRID_Y | PH_GRAPH_LOGARITHMIC_GRID_Y)) - { - // Pre-process to find the largest integer n such that GridHeight*GridBase^n < 1. - - gridBase = DrawInfo->GridBase; - - if (gridBase > 1) - { - DOUBLE logBase; - DOUBLE exponent; - DOUBLE high; - - logBase = log(gridBase); - exponent = ceil(-log(gridHeight) / logBase) - 1; // Works for both GridHeight > 1 and GridHeight < 1 - high = exp(exponent * logBase); - gridLevel = (FLOAT)(gridHeight * high); - - if (gridLevel < 0 || !isfinite(gridLevel)) - gridLevel = 0; - if (gridLevel > 1) - gridLevel = 1; - } - else - { - // This is an error. - gridLevel = 0; - } - } - - if (flags & PH_GRAPH_LABEL_MAX_Y) - { - yLabelMax = h2_i; - yLabelDataIndex = 0; - } - - while (x >= 0) - { - // Calculate the height of the graph at this point. - - if (!intermediate) - { - h1_o = h1_i; - h2_o = h2_i; - - // Pull in new data. - dataIndex++; - PhpGetGraphPoint(DrawInfo, dataIndex, &h1_i, &h2_i); - - h1 = h1_o; - h1_left = (h1_i + h1_o) / 2; - h2 = h2_o; - h2_left = (h2_i + h2_o) / 2; - - if ((flags & PH_GRAPH_LABEL_MAX_Y) && dataIndex < DrawInfo->LabelMaxYIndexLimit) - { - if (yLabelMax <= h2_i) - { - yLabelMax = h2_i; - yLabelDataIndex = dataIndex; - } - } - } - else - { - h1 = h1_left; - h1_left = h1_i; - h2 = h2_left; - h2_left = h2_i; - } - - // The graph is drawn right-to-left. There is one iteration of the loop per horizontal pixel. - // There is a fixed step value of 2, so every other iteration is a mid-point (intermediate) - // iteration with a height value of (left + right) / 2. In order to rasterize the outline, - // effectively in each iteration half of the line is drawn at the current column and the other - // half is drawn in the column to the left. - - // Rasterize the data outline. - // h?_low2 to h?_high2 is the vertical line to the left of the current pixel. - // h?_low1 to h?_high1 is the vertical line at the current pixel. - // We merge (union) the old h?_low2 to h?_high2 line with the current line in each iteration. - // - // For example: - // - // X represents a data point. M represents the mid-point between two data points ("intermediate"). - // X, M and x are all part of the outline. # represents the background filled in during - // the current loop iteration. - // - // slope > 0: slope < 0: - // - // X < high1 X < high2 (of next loop iteration) - // x x - // x < low1 x < low2 (of next loop iteration) - // x# < high2 x < high1 (of next loop iteration) - // M# < low2 M < low1 (of next loop iteration) - // x# < high1 (of next loop iteration) x < high2 - // x# < low1 (of next loop iteration) x < low2 - // x # < high2 (of next loop iteration) x < high1 - // x # x - // X # < low2 (of next loop iteration) X < low1 - // # # - // ^ ^ - // ^| current pixel ^| current pixel - // | | - // | left of current pixel | left of current pixel - // - // In both examples above, the line low2-high2 will be merged with the line low1-high1 of - // the next iteration. - - mid = ((h1_left + h1) / 2) * width; - old_low2 = h1_low2; - old_high2 = h1_high2; - - if (h1_left < h1) // slope > 0 - { - h1_low2 = h1_left * width; - h1_high2 = mid; - h1_low1 = mid + width; - h1_high1 = h1 * width; - } - else // slope < 0 - { - h1_high2 = h1_left * width; - h1_low2 = mid + width; - h1_high1 = mid; - h1_low1 = h1 * width; - } - - // Merge the lines. - if (h1_low1 > old_low2) - h1_low1 = old_low2; - if (h1_high1 < old_high2) - h1_high1 = old_high2; - - // Fix up values for the current horizontal offset. - h1_low1 += x; - h1_high1 += x; - - if (flags & PH_GRAPH_USE_LINE_2) - { - mid = ((h2_left + h2) / 2) * width; - old_low2 = h2_low2; - old_high2 = h2_high2; - - if (h2_left < h2) // slope > 0 - { - h2_low2 = h2_left * width; - h2_high2 = mid; - h2_low1 = mid + width; - h2_high1 = h2 * width; - } - else // slope < 0 - { - h2_high2 = h2_left * width; - h2_low2 = mid + width; - h2_high1 = mid; - h2_low1 = h2 * width; - } - - // Merge the lines. - if (h2_low1 > old_low2) - h2_low1 = old_low2; - if (h2_high1 < old_high2) - h2_high1 = old_high2; - - // Fix up values for the current horizontal offset. - h2_low1 += x; - h2_high1 += x; - } - - // Fill in the background. - - if (flags & PH_GRAPH_USE_LINE_2) - { - for (i = h1_high1 + width; i < h2_low1; i += width) - { - bits[i] = lineBackColor2; - } - } - - for (i = x; i < h1_low1; i += width) - { - bits[i] = lineBackColor1; - } - - // Draw the grid. - - if (flags & PH_GRAPH_USE_GRID_X) - { - // Draw the vertical grid line. - if (gridYCounter == 0) - { - for (i = x; i < numberOfPixels; i += width) - { - bits[i] = gridColor; - } - } - - gridYCounter++; - - if (gridYCounter == DrawInfo->GridWidth) - gridYCounter = 0; - } - - if (flags & PH_GRAPH_USE_GRID_Y) - { - FLOAT level; - LONG h; - LONG h_last; - - // Draw the horizontal grid line. - if (flags & PH_GRAPH_LOGARITHMIC_GRID_Y) - { - level = gridLevel; - h = (LONG)(level * (height - 1)); - h_last = height + gridYThreshold - 1; - - while (TRUE) - { - if (h <= h_last - gridYThreshold) - { - bits[x + h * width] = gridColor; - h_last = h; - } - else - { - break; - } - - level /= gridBase; - h = (LONG)(level * (height - 1)); - } - } - else - { - level = gridHeight; - h = (LONG)(level * (height - 1)); - h_last = 0; - - while (h < height - 1) - { - if (h >= h_last + gridYThreshold) - { - bits[x + h * width] = gridColor; - h_last = h; - } - - level += gridHeight; - h = (LONG)(level * (height - 1)); - } - } - } - - // Draw the outline (line 1 is allowed to paint over line 2). - - if (flags & PH_GRAPH_USE_LINE_2) - { - for (i = h2_low1; i <= h2_high1; i += width) // exclude pixel in the middle - { - bits[i] = lineColor2; - } - } - - for (i = h1_low1; i <= h1_high1; i += width) - { - bits[i] = lineColor1; - } - - intermediate = !intermediate; - x--; - } - - if ((flags & PH_GRAPH_LABEL_MAX_Y) && yLabelDataIndex < DrawInfo->LineDataCount) - { - FLOAT value; - PPH_STRING label; - - value = DrawInfo->LineData1[yLabelDataIndex]; - - if (flags & PH_GRAPH_USE_LINE_2) - value += DrawInfo->LineData2[yLabelDataIndex]; - - if (label = DrawInfo->LabelYFunction(DrawInfo, yLabelDataIndex, value, DrawInfo->LabelYFunctionParameter)) - { - HFONT oldFont = NULL; - SIZE textSize; - RECT rect; - - if (DrawInfo->LabelYFont) - oldFont = SelectObject(hdc, DrawInfo->LabelYFont); - - SetTextColor(hdc, DrawInfo->LabelYColor); - SetBkMode(hdc, TRANSPARENT); - - GetTextExtentPoint32(hdc, label->Buffer, (ULONG)label->Length / 2, &textSize); - - rect.bottom = height - yLabelMax - PhNormalGraphTextPadding.top; - rect.top = rect.bottom - textSize.cy; - - if (rect.top < PhNormalGraphTextPadding.top) - { - rect.top = PhNormalGraphTextPadding.top; - rect.bottom = rect.top + textSize.cy; - } - - rect.left = 0; - rect.right = width - min((LONG)yLabelDataIndex * 2, width) - PhNormalGraphTextPadding.right; - DrawText(hdc, label->Buffer, (ULONG)label->Length / 2, &rect, DT_NOCLIP | DT_RIGHT); - - if (oldFont) - SelectObject(hdc, oldFont); - - PhDereferenceObject(label); - } - } - - if (DrawInfo->Text.Buffer) - { - HFONT oldFont = NULL; - - if (DrawInfo->TextFont) - oldFont = SelectObject(hdc, DrawInfo->TextFont); - - // Fill in the text box. - SetDCBrushColor(hdc, DrawInfo->TextBoxColor); - FillRect(hdc, &DrawInfo->TextBoxRect, GetStockObject(DC_BRUSH)); - - // Draw the text. - SetTextColor(hdc, DrawInfo->TextColor); - SetBkMode(hdc, TRANSPARENT); - DrawText(hdc, DrawInfo->Text.Buffer, (ULONG)DrawInfo->Text.Length / 2, &DrawInfo->TextRect, DT_NOCLIP); - - if (oldFont) - SelectObject(hdc, oldFont); - } -} - -/** - * Sets the text in a graphing information structure. - * - * \param hdc The DC to perform calculations from. - * \param DrawInfo A structure which contains graphing information. The structure is modified to - * contain the new text information. - * \param Text The text. - * \param Margin The margins of the text box from the edges of the graph. - * \param Padding The padding within the text box. - * \param Align The alignment of the text box. - */ -VOID PhSetGraphText( - _In_ HDC hdc, - _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ PPH_STRINGREF Text, - _In_ PRECT Margin, - _In_ PRECT Padding, - _In_ ULONG Align - ) -{ - HFONT oldFont = NULL; - SIZE textSize; - PH_RECTANGLE boxRectangle; - PH_RECTANGLE textRectangle; - - if (DrawInfo->TextFont) - oldFont = SelectObject(hdc, DrawInfo->TextFont); - - DrawInfo->Text = *Text; - GetTextExtentPoint32(hdc, Text->Buffer, (ULONG)Text->Length / 2, &textSize); - - if (oldFont) - SelectObject(hdc, oldFont); - - // Calculate the box rectangle. - - boxRectangle.Width = textSize.cx + Padding->left + Padding->right; - boxRectangle.Height = textSize.cy + Padding->top + Padding->bottom; - - if (Align & PH_ALIGN_LEFT) - boxRectangle.Left = Margin->left; - else if (Align & PH_ALIGN_RIGHT) - boxRectangle.Left = DrawInfo->Width - boxRectangle.Width - Margin->right; - else - boxRectangle.Left = (DrawInfo->Width - boxRectangle.Width) / 2; - - if (Align & PH_ALIGN_TOP) - boxRectangle.Top = Margin->top; - else if (Align & PH_ALIGN_BOTTOM) - boxRectangle.Top = DrawInfo->Height - boxRectangle.Height - Margin->bottom; - else - boxRectangle.Top = (DrawInfo->Height - boxRectangle.Height) / 2; - - // Calculate the text rectangle. - - textRectangle.Left = boxRectangle.Left + Padding->left; - textRectangle.Top = boxRectangle.Top + Padding->top; - textRectangle.Width = textSize.cx; - textRectangle.Height = textSize.cy; - - // Save the rectangles. - DrawInfo->TextRect = PhRectangleToRect(textRectangle); - DrawInfo->TextBoxRect = PhRectangleToRect(boxRectangle); -} - -VOID PhpCreateGraphContext( - _Out_ PPHP_GRAPH_CONTEXT *Context - ) -{ - PPHP_GRAPH_CONTEXT context; - - context = PhAllocate(sizeof(PHP_GRAPH_CONTEXT)); - memset(context, 0, sizeof(PHP_GRAPH_CONTEXT)); - - context->DrawInfo.Width = 3; - context->DrawInfo.Height = 3; - context->DrawInfo.Flags = PH_GRAPH_USE_GRID_X; - context->DrawInfo.Step = 2; - context->DrawInfo.BackColor = RGB(0xef, 0xef, 0xef); - context->DrawInfo.LineDataCount = 0; - context->DrawInfo.LineData1 = NULL; - context->DrawInfo.LineData2 = NULL; - context->DrawInfo.LineColor1 = RGB(0x00, 0xff, 0x00); - context->DrawInfo.LineColor2 = RGB(0xff, 0x00, 0x00); - context->DrawInfo.LineBackColor1 = RGB(0x00, 0x77, 0x00); - context->DrawInfo.LineBackColor2 = RGB(0x77, 0x00, 0x00); - context->DrawInfo.GridColor = RGB(0xc7, 0xc7, 0xc7); - context->DrawInfo.GridWidth = 20; - context->DrawInfo.GridHeight = 0.25f; - context->DrawInfo.GridXOffset = 0; - context->DrawInfo.GridYThreshold = 10; - context->DrawInfo.GridBase = 2.0f; - context->DrawInfo.LabelYColor = RGB(0x77, 0x77, 0x77); - context->DrawInfo.LabelMaxYIndexLimit = -1; - context->DrawInfo.TextColor = RGB(0x00, 0xff, 0x00); - context->DrawInfo.TextBoxColor = RGB(0x00, 0x22, 0x00); - - context->Options.FadeOutBackColor = RGB(0xef, 0xef, 0xef); - context->Options.FadeOutWidth = 100; - - *Context = context; -} - -VOID PhpFreeGraphContext( - _Inout_ _Post_invalid_ PPHP_GRAPH_CONTEXT Context - ) -{ - PhFree(Context); -} - -static PWSTR PhpMakeGraphTooltipContextAtom( - VOID - ) -{ - PH_DEFINE_MAKE_ATOM(L"PhLib_GraphTooltipContext"); -} - -static VOID PhpDeleteBufferedContext( - _In_ PPHP_GRAPH_CONTEXT Context - ) -{ - if (Context->BufferedContext) - { - // The original bitmap must be selected back into the context, otherwise the bitmap can't be - // deleted. - SelectObject(Context->BufferedContext, Context->BufferedOldBitmap); - DeleteObject(Context->BufferedBitmap); - DeleteDC(Context->BufferedContext); - - Context->BufferedContext = NULL; - Context->BufferedBitmap = NULL; - Context->BufferedBits = NULL; - } -} - -static VOID PhpCreateBufferedContext( - _In_ PPHP_GRAPH_CONTEXT Context - ) -{ - HDC hdc; - BITMAPINFOHEADER header; - - PhpDeleteBufferedContext(Context); - - GetClientRect(Context->Handle, &Context->BufferedContextRect); - - hdc = GetDC(Context->Handle); - Context->BufferedContext = CreateCompatibleDC(hdc); - - memset(&header, 0, sizeof(BITMAPINFOHEADER)); - header.biSize = sizeof(BITMAPINFOHEADER); - header.biWidth = Context->BufferedContextRect.right; - header.biHeight = Context->BufferedContextRect.bottom; - header.biPlanes = 1; - header.biBitCount = 32; - - Context->BufferedBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->BufferedBits, NULL, 0); - - ReleaseDC(Context->Handle, hdc); - Context->BufferedOldBitmap = SelectObject(Context->BufferedContext, Context->BufferedBitmap); -} - -static VOID PhpDeleteFadeOutContext( - _In_ PPHP_GRAPH_CONTEXT Context - ) -{ - if (Context->FadeOutContext) - { - SelectObject(Context->FadeOutContext, Context->FadeOutOldBitmap); - DeleteObject(Context->FadeOutBitmap); - DeleteDC(Context->FadeOutContext); - - Context->FadeOutContext = NULL; - Context->FadeOutBitmap = NULL; - Context->FadeOutBits = NULL; - } -} - -static VOID PhpCreateFadeOutContext( - _In_ PPHP_GRAPH_CONTEXT Context - ) -{ - HDC hdc; - BITMAPINFOHEADER header; - ULONG i; - ULONG j; - ULONG height; - COLORREF backColor; - ULONG fadeOutWidth; - FLOAT fadeOutWidthSquared; - ULONG currentAlpha; - ULONG currentColor; - - PhpDeleteFadeOutContext(Context); - - GetClientRect(Context->Handle, &Context->FadeOutContextRect); - Context->FadeOutContextRect.right = Context->Options.FadeOutWidth; - - hdc = GetDC(Context->Handle); - Context->FadeOutContext = CreateCompatibleDC(hdc); - - memset(&header, 0, sizeof(BITMAPINFOHEADER)); - header.biSize = sizeof(BITMAPINFOHEADER); - header.biWidth = Context->FadeOutContextRect.right; - header.biHeight = Context->FadeOutContextRect.bottom; - header.biPlanes = 1; - header.biBitCount = 32; - - Context->FadeOutBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->FadeOutBits, NULL, 0); - - ReleaseDC(Context->Handle, hdc); - Context->FadeOutOldBitmap = SelectObject(Context->FadeOutContext, Context->FadeOutBitmap); - - if (!Context->FadeOutBits) - return; - - height = Context->FadeOutContextRect.bottom; - backColor = Context->Options.FadeOutBackColor; - fadeOutWidth = Context->Options.FadeOutWidth; - fadeOutWidthSquared = (FLOAT)fadeOutWidth * fadeOutWidth; - - for (i = 0; i < fadeOutWidth; i++) - { - currentAlpha = 255 - (ULONG)((FLOAT)(i * i) / fadeOutWidthSquared * 255); - currentColor = - ((backColor & 0xff) * currentAlpha / 255) + - ((((backColor >> 8) & 0xff) * currentAlpha / 255) << 8) + - ((((backColor >> 16) & 0xff) * currentAlpha / 255) << 16) + - (currentAlpha << 24); - - for (j = i; j < height * fadeOutWidth; j += fadeOutWidth) - { - ((PULONG)Context->FadeOutBits)[j] = currentColor; - } - } -} - -VOID PhpUpdateDrawInfo( - _In_ HWND hwnd, - _In_ PPHP_GRAPH_CONTEXT Context - ) -{ - PH_GRAPH_GETDRAWINFO getDrawInfo; - - Context->DrawInfo.Width = Context->BufferedContextRect.right; - Context->DrawInfo.Height = Context->BufferedContextRect.bottom; - - getDrawInfo.Header.hwndFrom = hwnd; - getDrawInfo.Header.idFrom = Context->Id; - getDrawInfo.Header.code = GCN_GETDRAWINFO; - getDrawInfo.DrawInfo = &Context->DrawInfo; - - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getDrawInfo); -} - -VOID PhpDrawGraphControl( - _In_ HWND hwnd, - _In_ PPHP_GRAPH_CONTEXT Context - ) -{ - if (Context->BufferedBits) - PhDrawGraphDirect(Context->BufferedContext, Context->BufferedBits, &Context->DrawInfo); - - if (Context->Style & GC_STYLE_FADEOUT) - { - BLENDFUNCTION blendFunction; - - if (!Context->FadeOutContext) - PhpCreateFadeOutContext(Context); - - blendFunction.BlendOp = AC_SRC_OVER; - blendFunction.BlendFlags = 0; - blendFunction.SourceConstantAlpha = 255; - blendFunction.AlphaFormat = AC_SRC_ALPHA; - GdiAlphaBlend( - Context->BufferedContext, - 0, - 0, - Context->Options.FadeOutWidth, - Context->FadeOutContextRect.bottom, - Context->FadeOutContext, - 0, - 0, - Context->Options.FadeOutWidth, - Context->FadeOutContextRect.bottom, - blendFunction - ); - } - - if (Context->Style & GC_STYLE_DRAW_PANEL) - { - PH_GRAPH_DRAWPANEL drawPanel; - - drawPanel.Header.hwndFrom = hwnd; - drawPanel.Header.idFrom = Context->Id; - drawPanel.Header.code = GCN_DRAWPANEL; - drawPanel.hdc = Context->BufferedContext; - drawPanel.Rect = Context->BufferedContextRect; - - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&drawPanel); - } -} - -LRESULT CALLBACK PhpGraphWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPHP_GRAPH_CONTEXT context; - - context = (PPHP_GRAPH_CONTEXT)GetWindowLongPtr(hwnd, 0); - - if (uMsg == WM_CREATE) - { - PhpCreateGraphContext(&context); - SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); - } - - if (!context) - return DefWindowProc(hwnd, uMsg, wParam, lParam); - - switch (uMsg) - { - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - { - if (context->TooltipHandle) - { - MSG message; - - message.hwnd = hwnd; - message.message = uMsg; - message.wParam = wParam; - message.lParam = lParam; - SendMessage(context->TooltipHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); - } - } - break; - } - - switch (uMsg) - { - case WM_CREATE: - { - CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam; - - context->Handle = hwnd; - context->Style = createStruct->style; - context->Id = (ULONG_PTR)createStruct->hMenu; - } - break; - case WM_DESTROY: - { - SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); - - if (context->TooltipHandle) - DestroyWindow(context->TooltipHandle); - - PhpDeleteFadeOutContext(context); - PhpDeleteBufferedContext(context); - PhpFreeGraphContext(context); - } - break; - case WM_STYLECHANGED: - { - STYLESTRUCT *styleStruct = (STYLESTRUCT *)lParam; - - if (wParam == GWL_STYLE) - { - context->Style = styleStruct->styleNew; - context->NeedsDraw = TRUE; - } - } - break; - case WM_SIZE: - { - // Force a re-create of the buffered context. - PhpCreateBufferedContext(context); - PhpDeleteFadeOutContext(context); - - PhpUpdateDrawInfo(hwnd, context); - context->NeedsDraw = TRUE; - InvalidateRect(hwnd, NULL, FALSE); - - if (context->TooltipHandle) - { - TOOLINFO toolInfo; - - memset(&toolInfo, 0, sizeof(TOOLINFO)); - toolInfo.cbSize = sizeof(TOOLINFO); - toolInfo.hwnd = hwnd; - toolInfo.uId = 1; - GetClientRect(hwnd, &toolInfo.rect); - SendMessage(context->TooltipHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); - } - } - break; - case WM_PAINT: - { - PAINTSTRUCT paintStruct; - HDC hdc; - - if (hdc = BeginPaint(hwnd, &paintStruct)) - { - if (!context->BufferedContext) - PhpCreateBufferedContext(context); - - if (context->NeedsUpdate) - { - PhpUpdateDrawInfo(hwnd, context); - context->NeedsUpdate = FALSE; - } - - if (context->NeedsDraw) - { - PhpDrawGraphControl(hwnd, context); - context->NeedsDraw = FALSE; - } - - BitBlt( - hdc, - paintStruct.rcPaint.left, - paintStruct.rcPaint.top, - paintStruct.rcPaint.right - paintStruct.rcPaint.left, - paintStruct.rcPaint.bottom - paintStruct.rcPaint.top, - context->BufferedContext, - paintStruct.rcPaint.left, - paintStruct.rcPaint.top, - SRCCOPY - ); - - EndPaint(hwnd, &paintStruct); - } - } - return 0; - case WM_ERASEBKGND: - return 1; - case WM_NCPAINT: - { - HRGN updateRegion; - - updateRegion = (HRGN)wParam; - - if (updateRegion == (HRGN)1) // HRGN_FULL - updateRegion = NULL; - - // Themed border - if (context->Style & WS_BORDER) - { - HDC hdc; - ULONG flags; - RECT rect; - - // Note the use of undocumented flags below. GetDCEx doesn't work without these. - - flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000; - - if (updateRegion) - flags |= DCX_INTERSECTRGN | 0x40000; - - if (hdc = GetDCEx(hwnd, updateRegion, flags)) - { - GetClientRect(hwnd, &rect); - rect.right += 2; - rect.bottom += 2; - SetDCBrushColor(hdc, RGB(0x8f, 0x8f, 0x8f)); - FrameRect(hdc, &rect, GetStockObject(DC_BRUSH)); - - ReleaseDC(hwnd, hdc); - return 0; - } - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - if (header->hwndFrom == context->TooltipHandle) - { - switch (header->code) - { - case TTN_GETDISPINFO: - { - LPNMTTDISPINFO dispInfo = (LPNMTTDISPINFO)header; - POINT point; - RECT clientRect; - PH_GRAPH_GETTOOLTIPTEXT getTooltipText; - - GetCursorPos(&point); - ScreenToClient(hwnd, &point); - GetClientRect(hwnd, &clientRect); - - getTooltipText.Header.hwndFrom = hwnd; - getTooltipText.Header.idFrom = context->Id; - getTooltipText.Header.code = GCN_GETTOOLTIPTEXT; - getTooltipText.Index = (clientRect.right - point.x - 1) / context->DrawInfo.Step; - getTooltipText.TotalCount = context->DrawInfo.LineDataCount; - getTooltipText.Text.Buffer = NULL; - getTooltipText.Text.Length = 0; - - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getTooltipText); - - if (getTooltipText.Text.Buffer) - { - dispInfo->lpszText = getTooltipText.Text.Buffer; - } - } - break; - } - } - } - break; - case WM_SETCURSOR: - { - if (context->Options.DefaultCursor) - { - SetCursor(context->Options.DefaultCursor); - return TRUE; - } - } - break; - case WM_MOUSEMOVE: - { - if (context->TooltipHandle) - { - POINT point; - - GetCursorPos(&point); - ScreenToClient(hwnd, &point); - - if (context->LastCursorLocation.x != point.x || context->LastCursorLocation.y != point.y) - { - SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0); - context->LastCursorLocation = point; - } - } - } - break; - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_RBUTTONDBLCLK: - { - PH_GRAPH_MOUSEEVENT mouseEvent; - RECT clientRect; - - GetClientRect(hwnd, &clientRect); - - mouseEvent.Header.hwndFrom = hwnd; - mouseEvent.Header.idFrom = context->Id; - mouseEvent.Header.code = GCN_MOUSEEVENT; - mouseEvent.Message = uMsg; - mouseEvent.Keys = (ULONG)wParam; - mouseEvent.Point.x = LOWORD(lParam); - mouseEvent.Point.y = HIWORD(lParam); - - mouseEvent.Index = (clientRect.right - mouseEvent.Point.x - 1) / context->DrawInfo.Step; - mouseEvent.TotalCount = context->DrawInfo.LineDataCount; - - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&mouseEvent); - } - break; - case GCM_GETDRAWINFO: - { - PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam; - - memcpy(drawInfo, &context->DrawInfo, sizeof(PH_GRAPH_DRAW_INFO)); - } - return TRUE; - case GCM_SETDRAWINFO: - { - PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam; - ULONG width; - ULONG height; - - width = context->DrawInfo.Width; - height = context->DrawInfo.Height; - memcpy(&context->DrawInfo, drawInfo, sizeof(PH_GRAPH_DRAW_INFO)); - context->DrawInfo.Width = width; - context->DrawInfo.Height = height; - } - return TRUE; - case GCM_DRAW: - { - PhpUpdateDrawInfo(hwnd, context); - context->NeedsDraw = TRUE; - } - return TRUE; - case GCM_MOVEGRID: - { - LONG increment = (LONG)wParam; - - context->DrawInfo.GridXOffset += increment; - } - return TRUE; - case GCM_GETBUFFEREDCONTEXT: - return (LRESULT)context->BufferedContext; - case GCM_SETTOOLTIP: - { - if (wParam) - { - TOOLINFO toolInfo = { sizeof(toolInfo) }; - - context->TooltipHandle = CreateWindow( - TOOLTIPS_CLASS, - NULL, - WS_POPUP | WS_EX_TRANSPARENT | TTS_NOPREFIX, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - NULL, - NULL, - PhLibImageBase, - NULL - ); - - SetWindowPos(context->TooltipHandle, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - - toolInfo.uFlags = 0; - toolInfo.hwnd = hwnd; - toolInfo.uId = 1; - toolInfo.lpszText = LPSTR_TEXTCALLBACK; - GetClientRect(hwnd, &toolInfo.rect); - SendMessage(context->TooltipHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); - - SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_INITIAL, 0); - SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); - // Allow newlines (-1 doesn't work) - SendMessage(context->TooltipHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); - } - else - { - DestroyWindow(context->TooltipHandle); - context->TooltipHandle = NULL; - } - } - return TRUE; - case GCM_UPDATETOOLTIP: - { - if (!context->TooltipHandle) - return FALSE; - - SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0); - } - return TRUE; - case GCM_GETOPTIONS: - memcpy((PPH_GRAPH_OPTIONS)lParam, &context->Options, sizeof(PH_GRAPH_OPTIONS)); - return TRUE; - case GCM_SETOPTIONS: - memcpy(&context->Options, (PPH_GRAPH_OPTIONS)lParam, sizeof(PH_GRAPH_OPTIONS)); - PhpDeleteFadeOutContext(context); - return TRUE; - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -/** - * Initializes a graph buffer management structure. - * - * \param Buffers The buffer management structure. - */ -VOID PhInitializeGraphBuffers( - _Out_ PPH_GRAPH_BUFFERS Buffers - ) -{ - Buffers->AllocatedCount = 0; - Buffers->Data1 = NULL; - Buffers->Data2 = NULL; - Buffers->Valid = FALSE; -} - -/** - * Frees resources used by a graph buffer management structure. - * - * \param Buffers The buffer management structure. - */ -VOID PhDeleteGraphBuffers( - _Inout_ PPH_GRAPH_BUFFERS Buffers - ) -{ - if (Buffers->Data1) PhFree(Buffers->Data1); - if (Buffers->Data2) PhFree(Buffers->Data2); -} - -/** - * Sets up a graphing information structure with information from a graph buffer management - * structure. - * - * \param Buffers The buffer management structure. - * \param DrawInfo The graphing information structure. - * \param DataCount The number of data points currently required. The buffers are resized if needed. - */ -VOID PhGetDrawInfoGraphBuffers( - _Inout_ PPH_GRAPH_BUFFERS Buffers, - _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ ULONG DataCount - ) -{ - DrawInfo->LineDataCount = min(DataCount, PH_GRAPH_DATA_COUNT(DrawInfo->Width, DrawInfo->Step)); - - // Do we need to allocate or re-allocate the data buffers? - if (Buffers->AllocatedCount < DrawInfo->LineDataCount) - { - if (Buffers->Data1) - PhFree(Buffers->Data1); - if ((DrawInfo->Flags & PH_GRAPH_USE_LINE_2) && Buffers->Data2) - PhFree(Buffers->Data2); - - Buffers->AllocatedCount *= 2; - - if (Buffers->AllocatedCount < DrawInfo->LineDataCount) - Buffers->AllocatedCount = DrawInfo->LineDataCount; - - Buffers->Data1 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT)); - - if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2) - { - Buffers->Data2 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT)); - } - - Buffers->Valid = FALSE; - } - - DrawInfo->LineData1 = Buffers->Data1; - DrawInfo->LineData2 = Buffers->Data2; -} - -VOID PhInitializeGraphState( - _Out_ PPH_GRAPH_STATE State - ) -{ - PhInitializeGraphBuffers(&State->Buffers); - State->Text = NULL; - State->TooltipText = NULL; - State->TooltipIndex = -1; -} - -VOID PhDeleteGraphState( - _Inout_ PPH_GRAPH_STATE State - ) -{ - PhDeleteGraphBuffers(&State->Buffers); - if (State->Text) PhDereferenceObject(State->Text); - if (State->TooltipText) PhDereferenceObject(State->TooltipText); -} - -VOID PhGraphStateGetDrawInfo( - _Inout_ PPH_GRAPH_STATE State, - _In_ PPH_GRAPH_GETDRAWINFO GetDrawInfo, - _In_ ULONG DataCount - ) -{ - PhGetDrawInfoGraphBuffers(&State->Buffers, GetDrawInfo->DrawInfo, DataCount); -} +/* + * Process Hacker - + * graph control + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#define COLORREF_TO_BITS(Color) (_byteswap_ulong(Color) >> 8) + +typedef struct _PHP_GRAPH_CONTEXT +{ + HWND Handle; + ULONG Style; + ULONG_PTR Id; + PH_GRAPH_DRAW_INFO DrawInfo; + PH_GRAPH_OPTIONS Options; + BOOLEAN NeedsUpdate; + BOOLEAN NeedsDraw; + + HDC BufferedContext; + HBITMAP BufferedOldBitmap; + HBITMAP BufferedBitmap; + PVOID BufferedBits; + RECT BufferedContextRect; + + HDC FadeOutContext; + HBITMAP FadeOutOldBitmap; + HBITMAP FadeOutBitmap; + PVOID FadeOutBits; + RECT FadeOutContextRect; + + HWND TooltipHandle; + WNDPROC TooltipOldWndProc; + POINT LastCursorLocation; +} PHP_GRAPH_CONTEXT, *PPHP_GRAPH_CONTEXT; + +LRESULT CALLBACK PhpGraphWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +RECT PhNormalGraphTextMargin = { 5, 5, 5, 5 }; +RECT PhNormalGraphTextPadding = { 3, 3, 3, 3 }; + +BOOLEAN PhGraphControlInitialization( + VOID + ) +{ + WNDCLASSEX c = { sizeof(c) }; + + c.style = CS_GLOBALCLASS | CS_DBLCLKS; + c.lpfnWndProc = PhpGraphWndProc; + c.cbClsExtra = 0; + c.cbWndExtra = sizeof(PVOID); + c.hInstance = PhInstanceHandle; + c.hIcon = NULL; + c.hCursor = LoadCursor(NULL, IDC_ARROW); + c.hbrBackground = NULL; + c.lpszMenuName = NULL; + c.lpszClassName = PH_GRAPH_CLASSNAME; + c.hIconSm = NULL; + + if (!RegisterClassEx(&c)) + return FALSE; + + return TRUE; +} + +FORCEINLINE VOID PhpGetGraphPoint( + _In_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ ULONG Index, + _Out_ PULONG H1, + _Out_ PULONG H2 + ) +{ + if (Index < DrawInfo->LineDataCount) + { + FLOAT f1; + FLOAT f2; + + f1 = DrawInfo->LineData1[Index]; + + if (f1 < 0) + f1 = 0; + if (f1 > 1) + f1 = 1; + + *H1 = (ULONG)(f1 * (DrawInfo->Height - 1)); + + if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2) + { + f2 = f1 + DrawInfo->LineData2[Index]; + + if (f2 < 0) + f2 = 0; + if (f2 > 1) + f2 = 1; + + *H2 = (ULONG)(f2 * (DrawInfo->Height - 1)); + } + else + { + *H2 = *H1; + } + } + else + { + *H1 = 0; + *H2 = 0; + } +} + +/** + * Draws a graph directly to memory. + * + * \param hdc The DC to draw to. This is only used when drawing text. + * \param Bits The bits in a bitmap. + * \param DrawInfo A structure which contains graphing information. + * + * \remarks The following information is fixed: + * \li The graph is fixed to the origin (0, 0). + * \li The total size of the bitmap is assumed to be \a Width and \a Height in \a DrawInfo. + * \li \a Step is fixed at 2. + * \li If \ref PH_GRAPH_USE_LINE_2 is specified in \a Flags, \ref PH_GRAPH_OVERLAY_LINE_2 is never + * used. + */ +VOID PhDrawGraphDirect( + _In_ HDC hdc, + _In_ PVOID Bits, + _In_ PPH_GRAPH_DRAW_INFO DrawInfo + ) +{ + PULONG bits = Bits; + LONG width = DrawInfo->Width; + LONG height = DrawInfo->Height; + LONG numberOfPixels = width * height; + ULONG flags = DrawInfo->Flags; + LONG i; + LONG x; + + BOOLEAN intermediate = FALSE; // whether we are currently between two data positions + ULONG dataIndex = 0; // the data index of the current position + ULONG h1_i; // the line 1 height value to the left of the current position + ULONG h1_o; // the line 1 height value at the current position + ULONG h2_i; // the line 1 + line 2 height value to the left of the current position + ULONG h2_o; // the line 1 + line 2 height value at the current position + ULONG h1; // current pixel + ULONG h1_left; // current pixel + ULONG h2; // current pixel + ULONG h2_left; // current pixel + + LONG mid; + LONG h1_low1; + LONG h1_high1; + LONG h1_low2; + LONG h1_high2; + LONG h2_low1; + LONG h2_high1; + LONG h2_low2; + LONG h2_high2; + LONG old_low2; + LONG old_high2; + + ULONG lineColor1; + ULONG lineBackColor1; + ULONG lineColor2; + ULONG lineBackColor2; + FLOAT gridHeight; + LONG gridYThreshold; + ULONG gridYCounter; + ULONG gridColor; + FLOAT gridBase; + FLOAT gridLevel; + + ULONG yLabelMax; + ULONG yLabelDataIndex; + + lineColor1 = COLORREF_TO_BITS(DrawInfo->LineColor1); + lineBackColor1 = COLORREF_TO_BITS(DrawInfo->LineBackColor1); + lineColor2 = COLORREF_TO_BITS(DrawInfo->LineColor2); + lineBackColor2 = COLORREF_TO_BITS(DrawInfo->LineBackColor2); + + if (DrawInfo->BackColor == 0) + { + memset(bits, 0, numberOfPixels * 4); + } + else + { + PhFillMemoryUlong(bits, COLORREF_TO_BITS(DrawInfo->BackColor), numberOfPixels); + } + + x = width - 1; + h1_low2 = MAXLONG; + h1_high2 = 0; + h2_low2 = MAXLONG; + h2_high2 = 0; + + PhpGetGraphPoint(DrawInfo, 0, &h1_i, &h2_i); + + if (flags & (PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y)) + { + gridHeight = max(DrawInfo->GridHeight, 0); + gridLevel = gridHeight; + gridYThreshold = DrawInfo->GridYThreshold; + gridYCounter = DrawInfo->GridWidth - (DrawInfo->GridXOffset * DrawInfo->Step) % DrawInfo->GridWidth - 1; + gridColor = COLORREF_TO_BITS(DrawInfo->GridColor); + } + + if ((flags & (PH_GRAPH_USE_GRID_Y | PH_GRAPH_LOGARITHMIC_GRID_Y)) == (PH_GRAPH_USE_GRID_Y | PH_GRAPH_LOGARITHMIC_GRID_Y)) + { + // Pre-process to find the largest integer n such that GridHeight*GridBase^n < 1. + + gridBase = DrawInfo->GridBase; + + if (gridBase > 1) + { + DOUBLE logBase; + DOUBLE exponent; + DOUBLE high; + + logBase = log(gridBase); + exponent = ceil(-log(gridHeight) / logBase) - 1; // Works for both GridHeight > 1 and GridHeight < 1 + high = exp(exponent * logBase); + gridLevel = (FLOAT)(gridHeight * high); + + if (gridLevel < 0 || !isfinite(gridLevel)) + gridLevel = 0; + if (gridLevel > 1) + gridLevel = 1; + } + else + { + // This is an error. + gridLevel = 0; + } + } + + if (flags & PH_GRAPH_LABEL_MAX_Y) + { + yLabelMax = h2_i; + yLabelDataIndex = 0; + } + + while (x >= 0) + { + // Calculate the height of the graph at this point. + + if (!intermediate) + { + h1_o = h1_i; + h2_o = h2_i; + + // Pull in new data. + dataIndex++; + PhpGetGraphPoint(DrawInfo, dataIndex, &h1_i, &h2_i); + + h1 = h1_o; + h1_left = (h1_i + h1_o) / 2; + h2 = h2_o; + h2_left = (h2_i + h2_o) / 2; + + if ((flags & PH_GRAPH_LABEL_MAX_Y) && dataIndex < DrawInfo->LabelMaxYIndexLimit) + { + if (yLabelMax <= h2_i) + { + yLabelMax = h2_i; + yLabelDataIndex = dataIndex; + } + } + } + else + { + h1 = h1_left; + h1_left = h1_i; + h2 = h2_left; + h2_left = h2_i; + } + + // The graph is drawn right-to-left. There is one iteration of the loop per horizontal pixel. + // There is a fixed step value of 2, so every other iteration is a mid-point (intermediate) + // iteration with a height value of (left + right) / 2. In order to rasterize the outline, + // effectively in each iteration half of the line is drawn at the current column and the other + // half is drawn in the column to the left. + + // Rasterize the data outline. + // h?_low2 to h?_high2 is the vertical line to the left of the current pixel. + // h?_low1 to h?_high1 is the vertical line at the current pixel. + // We merge (union) the old h?_low2 to h?_high2 line with the current line in each iteration. + // + // For example: + // + // X represents a data point. M represents the mid-point between two data points ("intermediate"). + // X, M and x are all part of the outline. # represents the background filled in during + // the current loop iteration. + // + // slope > 0: slope < 0: + // + // X < high1 X < high2 (of next loop iteration) + // x x + // x < low1 x < low2 (of next loop iteration) + // x# < high2 x < high1 (of next loop iteration) + // M# < low2 M < low1 (of next loop iteration) + // x# < high1 (of next loop iteration) x < high2 + // x# < low1 (of next loop iteration) x < low2 + // x # < high2 (of next loop iteration) x < high1 + // x # x + // X # < low2 (of next loop iteration) X < low1 + // # # + // ^ ^ + // ^| current pixel ^| current pixel + // | | + // | left of current pixel | left of current pixel + // + // In both examples above, the line low2-high2 will be merged with the line low1-high1 of + // the next iteration. + + mid = ((h1_left + h1) / 2) * width; + old_low2 = h1_low2; + old_high2 = h1_high2; + + if (h1_left < h1) // slope > 0 + { + h1_low2 = h1_left * width; + h1_high2 = mid; + h1_low1 = mid + width; + h1_high1 = h1 * width; + } + else // slope < 0 + { + h1_high2 = h1_left * width; + h1_low2 = mid + width; + h1_high1 = mid; + h1_low1 = h1 * width; + } + + // Merge the lines. + if (h1_low1 > old_low2) + h1_low1 = old_low2; + if (h1_high1 < old_high2) + h1_high1 = old_high2; + + // Fix up values for the current horizontal offset. + h1_low1 += x; + h1_high1 += x; + + if (flags & PH_GRAPH_USE_LINE_2) + { + mid = ((h2_left + h2) / 2) * width; + old_low2 = h2_low2; + old_high2 = h2_high2; + + if (h2_left < h2) // slope > 0 + { + h2_low2 = h2_left * width; + h2_high2 = mid; + h2_low1 = mid + width; + h2_high1 = h2 * width; + } + else // slope < 0 + { + h2_high2 = h2_left * width; + h2_low2 = mid + width; + h2_high1 = mid; + h2_low1 = h2 * width; + } + + // Merge the lines. + if (h2_low1 > old_low2) + h2_low1 = old_low2; + if (h2_high1 < old_high2) + h2_high1 = old_high2; + + // Fix up values for the current horizontal offset. + h2_low1 += x; + h2_high1 += x; + } + + // Fill in the background. + + if (flags & PH_GRAPH_USE_LINE_2) + { + for (i = h1_high1 + width; i < h2_low1; i += width) + { + bits[i] = lineBackColor2; + } + } + + for (i = x; i < h1_low1; i += width) + { + bits[i] = lineBackColor1; + } + + // Draw the grid. + + if (flags & PH_GRAPH_USE_GRID_X) + { + // Draw the vertical grid line. + if (gridYCounter == 0) + { + for (i = x; i < numberOfPixels; i += width) + { + bits[i] = gridColor; + } + } + + gridYCounter++; + + if (gridYCounter == DrawInfo->GridWidth) + gridYCounter = 0; + } + + if (flags & PH_GRAPH_USE_GRID_Y) + { + FLOAT level; + LONG h; + LONG h_last; + + // Draw the horizontal grid line. + if (flags & PH_GRAPH_LOGARITHMIC_GRID_Y) + { + level = gridLevel; + h = (LONG)(level * (height - 1)); + h_last = height + gridYThreshold - 1; + + while (TRUE) + { + if (h <= h_last - gridYThreshold) + { + bits[x + h * width] = gridColor; + h_last = h; + } + else + { + break; + } + + level /= gridBase; + h = (LONG)(level * (height - 1)); + } + } + else + { + level = gridHeight; + h = (LONG)(level * (height - 1)); + h_last = 0; + + while (h < height - 1) + { + if (h >= h_last + gridYThreshold) + { + bits[x + h * width] = gridColor; + h_last = h; + } + + level += gridHeight; + h = (LONG)(level * (height - 1)); + } + } + } + + // Draw the outline (line 1 is allowed to paint over line 2). + + if (flags & PH_GRAPH_USE_LINE_2) + { + for (i = h2_low1; i <= h2_high1; i += width) // exclude pixel in the middle + { + bits[i] = lineColor2; + } + } + + for (i = h1_low1; i <= h1_high1; i += width) + { + bits[i] = lineColor1; + } + + intermediate = !intermediate; + x--; + } + + if ((flags & PH_GRAPH_LABEL_MAX_Y) && yLabelDataIndex < DrawInfo->LineDataCount) + { + FLOAT value; + PPH_STRING label; + + value = DrawInfo->LineData1[yLabelDataIndex]; + + if (flags & PH_GRAPH_USE_LINE_2) + value += DrawInfo->LineData2[yLabelDataIndex]; + + if (label = DrawInfo->LabelYFunction(DrawInfo, yLabelDataIndex, value, DrawInfo->LabelYFunctionParameter)) + { + HFONT oldFont = NULL; + SIZE textSize; + RECT rect; + + if (DrawInfo->LabelYFont) + oldFont = SelectFont(hdc, DrawInfo->LabelYFont); + + SetTextColor(hdc, DrawInfo->LabelYColor); + SetBkMode(hdc, TRANSPARENT); + + GetTextExtentPoint32(hdc, label->Buffer, (ULONG)label->Length / sizeof(WCHAR), &textSize); + + rect.bottom = height - yLabelMax - PhNormalGraphTextPadding.top; + rect.top = rect.bottom - textSize.cy; + + if (rect.top < PhNormalGraphTextPadding.top) + { + rect.top = PhNormalGraphTextPadding.top; + rect.bottom = rect.top + textSize.cy; + } + + rect.left = 0; + rect.right = width - min((LONG)yLabelDataIndex * 2, width) - PhNormalGraphTextPadding.right; + DrawText(hdc, label->Buffer, (ULONG)label->Length / sizeof(WCHAR), &rect, DT_NOCLIP | DT_RIGHT); + + if (oldFont) + SelectFont(hdc, oldFont); + + PhDereferenceObject(label); + } + } + + if (DrawInfo->Text.Buffer) + { + HFONT oldFont = NULL; + + if (DrawInfo->TextFont) + oldFont = SelectFont(hdc, DrawInfo->TextFont); + + // Fill in the text box. + SetDCBrushColor(hdc, DrawInfo->TextBoxColor); + FillRect(hdc, &DrawInfo->TextBoxRect, GetStockBrush(DC_BRUSH)); + + // Draw the text. + SetTextColor(hdc, DrawInfo->TextColor); + SetBkMode(hdc, TRANSPARENT); + DrawText(hdc, DrawInfo->Text.Buffer, (ULONG)DrawInfo->Text.Length / sizeof(WCHAR), &DrawInfo->TextRect, DT_NOCLIP); + + if (oldFont) + SelectFont(hdc, oldFont); + } +} + +/** + * Sets the text in a graphing information structure. + * + * \param hdc The DC to perform calculations from. + * \param DrawInfo A structure which contains graphing information. The structure is modified to + * contain the new text information. + * \param Text The text. + * \param Margin The margins of the text box from the edges of the graph. + * \param Padding The padding within the text box. + * \param Align The alignment of the text box. + */ +VOID PhSetGraphText( + _In_ HDC hdc, + _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ PPH_STRINGREF Text, + _In_ PRECT Margin, + _In_ PRECT Padding, + _In_ ULONG Align + ) +{ + HFONT oldFont = NULL; + SIZE textSize; + PH_RECTANGLE boxRectangle; + PH_RECTANGLE textRectangle; + + if (DrawInfo->TextFont) + oldFont = SelectFont(hdc, DrawInfo->TextFont); + + DrawInfo->Text = *Text; + GetTextExtentPoint32(hdc, Text->Buffer, (ULONG)Text->Length / sizeof(WCHAR), &textSize); + + if (oldFont) + SelectFont(hdc, oldFont); + + // Calculate the box rectangle. + + boxRectangle.Width = textSize.cx + Padding->left + Padding->right; + boxRectangle.Height = textSize.cy + Padding->top + Padding->bottom; + + if (Align & PH_ALIGN_LEFT) + boxRectangle.Left = Margin->left; + else if (Align & PH_ALIGN_RIGHT) + boxRectangle.Left = DrawInfo->Width - boxRectangle.Width - Margin->right; + else + boxRectangle.Left = (DrawInfo->Width - boxRectangle.Width) / sizeof(WCHAR); + + if (Align & PH_ALIGN_TOP) + boxRectangle.Top = Margin->top; + else if (Align & PH_ALIGN_BOTTOM) + boxRectangle.Top = DrawInfo->Height - boxRectangle.Height - Margin->bottom; + else + boxRectangle.Top = (DrawInfo->Height - boxRectangle.Height) / sizeof(WCHAR); + + // Calculate the text rectangle. + + textRectangle.Left = boxRectangle.Left + Padding->left; + textRectangle.Top = boxRectangle.Top + Padding->top; + textRectangle.Width = textSize.cx; + textRectangle.Height = textSize.cy; + + // Save the rectangles. + DrawInfo->TextRect = PhRectangleToRect(textRectangle); + DrawInfo->TextBoxRect = PhRectangleToRect(boxRectangle); +} + +static HFONT PhpTrayIconFont( // dmex + VOID + ) +{ + static HFONT iconTextFont = NULL; + + if (!iconTextFont) + { + iconTextFont = CreateFont( + PhMultiplyDivideSigned(-11, PhGlobalDpi, 96), + 0, + 0, + 0, + FW_NORMAL, + FALSE, + FALSE, + FALSE, + ANSI_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + ANTIALIASED_QUALITY, + DEFAULT_PITCH, + L"Tahoma" + ); + } + + return iconTextFont; +} + +VOID PhDrawTrayIconText( // dmex + _In_ HDC hdc, + _In_ PVOID Bits, + _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ PPH_STRINGREF Text + ) +{ + PULONG bits = Bits; + LONG width = DrawInfo->Width; + LONG height = DrawInfo->Height; + LONG numberOfPixels = width * height; + ULONG flags = DrawInfo->Flags; + HFONT oldFont = NULL; + SIZE textSize; + PH_RECTANGLE boxRectangle; + PH_RECTANGLE textRectangle; + + if (DrawInfo->BackColor == 0) + { + memset(bits, 0, numberOfPixels * 4); + } + else + { + PhFillMemoryUlong(bits, COLORREF_TO_BITS(DrawInfo->BackColor), numberOfPixels); + } + + if (!DrawInfo->TextFont) // HACK: default font for plugins. + DrawInfo->TextFont = PhpTrayIconFont(); + + if (DrawInfo->TextFont) + oldFont = SelectFont(hdc, DrawInfo->TextFont); + + DrawInfo->Text = *Text; + GetTextExtentPoint32(hdc, Text->Buffer, (ULONG)Text->Length / sizeof(WCHAR), &textSize); + + // Calculate the box rectangle. + + boxRectangle.Width = textSize.cx; + boxRectangle.Height = textSize.cy; + boxRectangle.Left = (DrawInfo->Width - boxRectangle.Width) / sizeof(WCHAR); + boxRectangle.Top = (DrawInfo->Height - boxRectangle.Height) / sizeof(WCHAR); + + // Calculate the text rectangle. + + textRectangle.Left = boxRectangle.Left; + textRectangle.Top = boxRectangle.Top; + textRectangle.Width = textSize.cx; + textRectangle.Height = textSize.cy; + + // Save the rectangles. + DrawInfo->TextRect = PhRectangleToRect(textRectangle); + DrawInfo->TextBoxRect = PhRectangleToRect(boxRectangle); + + // Fill in the text box. + //SetDCBrushColor(hdc, DrawInfo->TextBoxColor); + //FillRect(hdc, &DrawInfo->TextBoxRect, GetStockBrush(DC_BRUSH)); + + // Draw the text. + SetTextColor(hdc, DrawInfo->TextColor); + SetBkMode(hdc, TRANSPARENT); + + DrawText(hdc, DrawInfo->Text.Buffer, (ULONG)DrawInfo->Text.Length / sizeof(WCHAR), &DrawInfo->TextRect, DT_NOCLIP | DT_SINGLELINE); + + if (oldFont) + SelectFont(hdc, oldFont); +} + +VOID PhpCreateGraphContext( + _Out_ PPHP_GRAPH_CONTEXT *Context + ) +{ + PPHP_GRAPH_CONTEXT context; + + context = PhAllocate(sizeof(PHP_GRAPH_CONTEXT)); + memset(context, 0, sizeof(PHP_GRAPH_CONTEXT)); + + context->DrawInfo.Width = 3; + context->DrawInfo.Height = 3; + context->DrawInfo.Flags = PH_GRAPH_USE_GRID_X; + context->DrawInfo.Step = 2; + context->DrawInfo.BackColor = RGB(0xef, 0xef, 0xef); + context->DrawInfo.LineDataCount = 0; + context->DrawInfo.LineData1 = NULL; + context->DrawInfo.LineData2 = NULL; + context->DrawInfo.LineColor1 = RGB(0x00, 0xff, 0x00); + context->DrawInfo.LineColor2 = RGB(0xff, 0x00, 0x00); + context->DrawInfo.LineBackColor1 = RGB(0x00, 0x77, 0x00); + context->DrawInfo.LineBackColor2 = RGB(0x77, 0x00, 0x00); + context->DrawInfo.GridColor = RGB(0xc7, 0xc7, 0xc7); + context->DrawInfo.GridWidth = 20; + context->DrawInfo.GridHeight = 0.25f; + context->DrawInfo.GridXOffset = 0; + context->DrawInfo.GridYThreshold = 10; + context->DrawInfo.GridBase = 2.0f; + context->DrawInfo.LabelYColor = RGB(0x77, 0x77, 0x77); + context->DrawInfo.LabelMaxYIndexLimit = -1; + context->DrawInfo.TextColor = RGB(0x00, 0xff, 0x00); + context->DrawInfo.TextBoxColor = RGB(0x00, 0x22, 0x00); + + context->Options.FadeOutBackColor = RGB(0xef, 0xef, 0xef); + context->Options.FadeOutWidth = 100; + + *Context = context; +} + +VOID PhpFreeGraphContext( + _Inout_ _Post_invalid_ PPHP_GRAPH_CONTEXT Context + ) +{ + PhFree(Context); +} + +static VOID PhpDeleteBufferedContext( + _In_ PPHP_GRAPH_CONTEXT Context + ) +{ + if (Context->BufferedContext) + { + // The original bitmap must be selected back into the context, otherwise the bitmap can't be + // deleted. + SelectBitmap(Context->BufferedContext, Context->BufferedOldBitmap); + DeleteBitmap(Context->BufferedBitmap); + DeleteDC(Context->BufferedContext); + + Context->BufferedContext = NULL; + Context->BufferedBitmap = NULL; + Context->BufferedBits = NULL; + } +} + +static VOID PhpCreateBufferedContext( + _In_ PPHP_GRAPH_CONTEXT Context + ) +{ + HDC hdc; + BITMAPINFOHEADER header; + + PhpDeleteBufferedContext(Context); + + GetClientRect(Context->Handle, &Context->BufferedContextRect); + + hdc = GetDC(Context->Handle); + Context->BufferedContext = CreateCompatibleDC(hdc); + + memset(&header, 0, sizeof(BITMAPINFOHEADER)); + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = Context->BufferedContextRect.right; + header.biHeight = Context->BufferedContextRect.bottom; + header.biPlanes = 1; + header.biBitCount = 32; + + Context->BufferedBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->BufferedBits, NULL, 0); + + ReleaseDC(Context->Handle, hdc); + Context->BufferedOldBitmap = SelectBitmap(Context->BufferedContext, Context->BufferedBitmap); +} + +static VOID PhpDeleteFadeOutContext( + _In_ PPHP_GRAPH_CONTEXT Context + ) +{ + if (Context->FadeOutContext) + { + SelectBitmap(Context->FadeOutContext, Context->FadeOutOldBitmap); + DeleteBitmap(Context->FadeOutBitmap); + DeleteDC(Context->FadeOutContext); + + Context->FadeOutContext = NULL; + Context->FadeOutBitmap = NULL; + Context->FadeOutBits = NULL; + } +} + +static VOID PhpCreateFadeOutContext( + _In_ PPHP_GRAPH_CONTEXT Context + ) +{ + HDC hdc; + BITMAPINFOHEADER header; + ULONG i; + ULONG j; + ULONG height; + COLORREF backColor; + ULONG fadeOutWidth; + FLOAT fadeOutWidthSquared; + ULONG currentAlpha; + ULONG currentColor; + + PhpDeleteFadeOutContext(Context); + + GetClientRect(Context->Handle, &Context->FadeOutContextRect); + Context->FadeOutContextRect.right = Context->Options.FadeOutWidth; + + hdc = GetDC(Context->Handle); + Context->FadeOutContext = CreateCompatibleDC(hdc); + + memset(&header, 0, sizeof(BITMAPINFOHEADER)); + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = Context->FadeOutContextRect.right; + header.biHeight = Context->FadeOutContextRect.bottom; + header.biPlanes = 1; + header.biBitCount = 32; + + Context->FadeOutBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &Context->FadeOutBits, NULL, 0); + + ReleaseDC(Context->Handle, hdc); + Context->FadeOutOldBitmap = SelectBitmap(Context->FadeOutContext, Context->FadeOutBitmap); + + if (!Context->FadeOutBits) + return; + + height = Context->FadeOutContextRect.bottom; + backColor = Context->Options.FadeOutBackColor; + fadeOutWidth = Context->Options.FadeOutWidth; + fadeOutWidthSquared = (FLOAT)fadeOutWidth * fadeOutWidth; + + for (i = 0; i < fadeOutWidth; i++) + { + currentAlpha = 255 - (ULONG)((FLOAT)(i * i) / fadeOutWidthSquared * 255); + currentColor = + ((backColor & 0xff) * currentAlpha / 255) + + ((((backColor >> 8) & 0xff) * currentAlpha / 255) << 8) + + ((((backColor >> 16) & 0xff) * currentAlpha / 255) << 16) + + (currentAlpha << 24); + + for (j = i; j < height * fadeOutWidth; j += fadeOutWidth) + { + ((PULONG)Context->FadeOutBits)[j] = currentColor; + } + } +} + +VOID PhpUpdateDrawInfo( + _In_ HWND hwnd, + _In_ PPHP_GRAPH_CONTEXT Context + ) +{ + PH_GRAPH_GETDRAWINFO getDrawInfo; + + Context->DrawInfo.Width = Context->BufferedContextRect.right; + Context->DrawInfo.Height = Context->BufferedContextRect.bottom; + + getDrawInfo.Header.hwndFrom = hwnd; + getDrawInfo.Header.idFrom = Context->Id; + getDrawInfo.Header.code = GCN_GETDRAWINFO; + getDrawInfo.DrawInfo = &Context->DrawInfo; + + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getDrawInfo); +} + +VOID PhpDrawGraphControl( + _In_ HWND hwnd, + _In_ PPHP_GRAPH_CONTEXT Context + ) +{ + if (Context->BufferedBits) + PhDrawGraphDirect(Context->BufferedContext, Context->BufferedBits, &Context->DrawInfo); + + if (Context->Style & GC_STYLE_FADEOUT) + { + BLENDFUNCTION blendFunction; + + if (!Context->FadeOutContext) + PhpCreateFadeOutContext(Context); + + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.BlendFlags = 0; + blendFunction.SourceConstantAlpha = 255; + blendFunction.AlphaFormat = AC_SRC_ALPHA; + GdiAlphaBlend( + Context->BufferedContext, + 0, + 0, + Context->Options.FadeOutWidth, + Context->FadeOutContextRect.bottom, + Context->FadeOutContext, + 0, + 0, + Context->Options.FadeOutWidth, + Context->FadeOutContextRect.bottom, + blendFunction + ); + } + + if (Context->Style & GC_STYLE_DRAW_PANEL) + { + PH_GRAPH_DRAWPANEL drawPanel; + + drawPanel.Header.hwndFrom = hwnd; + drawPanel.Header.idFrom = Context->Id; + drawPanel.Header.code = GCN_DRAWPANEL; + drawPanel.hdc = Context->BufferedContext; + drawPanel.Rect = Context->BufferedContextRect; + + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&drawPanel); + } +} + +LRESULT CALLBACK PhpGraphWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_GRAPH_CONTEXT context; + + context = (PPHP_GRAPH_CONTEXT)GetWindowLongPtr(hwnd, 0); + + if (uMsg == WM_CREATE) + { + PhpCreateGraphContext(&context); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); + } + + if (!context) + return DefWindowProc(hwnd, uMsg, wParam, lParam); + + switch (uMsg) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + { + if (context->TooltipHandle) + { + MSG message; + + message.hwnd = hwnd; + message.message = uMsg; + message.wParam = wParam; + message.lParam = lParam; + SendMessage(context->TooltipHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); + } + } + break; + } + + switch (uMsg) + { + case WM_CREATE: + { + CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam; + + context->Handle = hwnd; + context->Style = createStruct->style; + context->Id = (ULONG_PTR)createStruct->hMenu; + } + break; + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); + + if (context->TooltipHandle) + DestroyWindow(context->TooltipHandle); + + PhpDeleteFadeOutContext(context); + PhpDeleteBufferedContext(context); + PhpFreeGraphContext(context); + } + break; + case WM_STYLECHANGED: + { + STYLESTRUCT *styleStruct = (STYLESTRUCT *)lParam; + + if (wParam == GWL_STYLE) + { + context->Style = styleStruct->styleNew; + context->NeedsDraw = TRUE; + } + } + break; + case WM_SIZE: + { + // Force a re-create of the buffered context. + PhpCreateBufferedContext(context); + PhpDeleteFadeOutContext(context); + + PhpUpdateDrawInfo(hwnd, context); + context->NeedsDraw = TRUE; + InvalidateRect(hwnd, NULL, FALSE); + + if (context->TooltipHandle) + { + TOOLINFO toolInfo; + + memset(&toolInfo, 0, sizeof(TOOLINFO)); + toolInfo.cbSize = sizeof(TOOLINFO); + toolInfo.hwnd = hwnd; + toolInfo.uId = 1; + GetClientRect(hwnd, &toolInfo.rect); + SendMessage(context->TooltipHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); + } + } + break; + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + HDC hdc; + + if (hdc = BeginPaint(hwnd, &paintStruct)) + { + if (!context->BufferedContext) + PhpCreateBufferedContext(context); + + if (context->NeedsUpdate) + { + PhpUpdateDrawInfo(hwnd, context); + context->NeedsUpdate = FALSE; + } + + if (context->NeedsDraw) + { + PhpDrawGraphControl(hwnd, context); + context->NeedsDraw = FALSE; + } + + BitBlt( + hdc, + paintStruct.rcPaint.left, + paintStruct.rcPaint.top, + paintStruct.rcPaint.right - paintStruct.rcPaint.left, + paintStruct.rcPaint.bottom - paintStruct.rcPaint.top, + context->BufferedContext, + paintStruct.rcPaint.left, + paintStruct.rcPaint.top, + SRCCOPY + ); + + EndPaint(hwnd, &paintStruct); + } + } + return 0; + case WM_ERASEBKGND: + return 1; + case WM_NCPAINT: + { + HRGN updateRegion; + + updateRegion = (HRGN)wParam; + + if (updateRegion == (HRGN)1) // HRGN_FULL + updateRegion = NULL; + + // Themed border + if (context->Style & WS_BORDER) + { + HDC hdc; + ULONG flags; + RECT rect; + + // Note the use of undocumented flags below. GetDCEx doesn't work without these. + + flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000; + + if (updateRegion) + flags |= DCX_INTERSECTRGN | 0x40000; + + if (hdc = GetDCEx(hwnd, updateRegion, flags)) + { + GetClientRect(hwnd, &rect); + rect.right += 2; + rect.bottom += 2; + SetDCBrushColor(hdc, RGB(0x8f, 0x8f, 0x8f)); + FrameRect(hdc, &rect, GetStockBrush(DC_BRUSH)); + + ReleaseDC(hwnd, hdc); + return 0; + } + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + if (header->hwndFrom == context->TooltipHandle) + { + switch (header->code) + { + case TTN_GETDISPINFO: + { + LPNMTTDISPINFO dispInfo = (LPNMTTDISPINFO)header; + POINT point; + RECT clientRect; + PH_GRAPH_GETTOOLTIPTEXT getTooltipText; + + GetCursorPos(&point); + ScreenToClient(hwnd, &point); + GetClientRect(hwnd, &clientRect); + + getTooltipText.Header.hwndFrom = hwnd; + getTooltipText.Header.idFrom = context->Id; + getTooltipText.Header.code = GCN_GETTOOLTIPTEXT; + getTooltipText.Index = (clientRect.right - point.x - 1) / context->DrawInfo.Step; + getTooltipText.TotalCount = context->DrawInfo.LineDataCount; + getTooltipText.Text.Buffer = NULL; + getTooltipText.Text.Length = 0; + + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&getTooltipText); + + if (getTooltipText.Text.Buffer) + { + dispInfo->lpszText = getTooltipText.Text.Buffer; + } + } + break; + } + } + } + break; + case WM_SETCURSOR: + { + if (context->Options.DefaultCursor) + { + SetCursor(context->Options.DefaultCursor); + return TRUE; + } + } + break; + case WM_MOUSEMOVE: + { + if (context->TooltipHandle) + { + POINT point; + + GetCursorPos(&point); + ScreenToClient(hwnd, &point); + + if (context->LastCursorLocation.x != point.x || context->LastCursorLocation.y != point.y) + { + SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0); + context->LastCursorLocation = point; + } + } + } + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + { + PH_GRAPH_MOUSEEVENT mouseEvent; + RECT clientRect; + + GetClientRect(hwnd, &clientRect); + + mouseEvent.Header.hwndFrom = hwnd; + mouseEvent.Header.idFrom = context->Id; + mouseEvent.Header.code = GCN_MOUSEEVENT; + mouseEvent.Message = uMsg; + mouseEvent.Keys = (ULONG)wParam; + mouseEvent.Point.x = GET_X_LPARAM(lParam); + mouseEvent.Point.y = GET_Y_LPARAM(lParam); + + mouseEvent.Index = (clientRect.right - mouseEvent.Point.x - 1) / context->DrawInfo.Step; + mouseEvent.TotalCount = context->DrawInfo.LineDataCount; + + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&mouseEvent); + } + break; + case GCM_GETDRAWINFO: + { + PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam; + + memcpy(drawInfo, &context->DrawInfo, sizeof(PH_GRAPH_DRAW_INFO)); + } + return TRUE; + case GCM_SETDRAWINFO: + { + PPH_GRAPH_DRAW_INFO drawInfo = (PPH_GRAPH_DRAW_INFO)lParam; + ULONG width; + ULONG height; + + width = context->DrawInfo.Width; + height = context->DrawInfo.Height; + memcpy(&context->DrawInfo, drawInfo, sizeof(PH_GRAPH_DRAW_INFO)); + context->DrawInfo.Width = width; + context->DrawInfo.Height = height; + } + return TRUE; + case GCM_DRAW: + { + PhpUpdateDrawInfo(hwnd, context); + context->NeedsDraw = TRUE; + } + return TRUE; + case GCM_MOVEGRID: + { + LONG increment = (LONG)wParam; + + context->DrawInfo.GridXOffset += increment; + } + return TRUE; + case GCM_GETBUFFEREDCONTEXT: + return (LRESULT)context->BufferedContext; + case GCM_SETTOOLTIP: + { + if (wParam) + { + TOOLINFO toolInfo = { sizeof(toolInfo) }; + + context->TooltipHandle = CreateWindow( + TOOLTIPS_CLASS, + NULL, + WS_POPUP | WS_EX_TRANSPARENT | TTS_NOPREFIX | TTS_ALWAYSTIP, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + PhInstanceHandle, + NULL + ); + + SetWindowPos(context->TooltipHandle, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + toolInfo.uFlags = 0; + toolInfo.hwnd = hwnd; + toolInfo.uId = 1; + toolInfo.lpszText = LPSTR_TEXTCALLBACK; + GetClientRect(hwnd, &toolInfo.rect); + SendMessage(context->TooltipHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + + SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_INITIAL, 0); + SendMessage(context->TooltipHandle, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT); + // Allow newlines (-1 doesn't work) + SendMessage(context->TooltipHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); + } + else + { + DestroyWindow(context->TooltipHandle); + context->TooltipHandle = NULL; + } + } + return TRUE; + case GCM_UPDATETOOLTIP: + { + if (!context->TooltipHandle) + return FALSE; + + SendMessage(context->TooltipHandle, TTM_UPDATE, 0, 0); + } + return TRUE; + case GCM_GETOPTIONS: + memcpy((PPH_GRAPH_OPTIONS)lParam, &context->Options, sizeof(PH_GRAPH_OPTIONS)); + return TRUE; + case GCM_SETOPTIONS: + memcpy(&context->Options, (PPH_GRAPH_OPTIONS)lParam, sizeof(PH_GRAPH_OPTIONS)); + PhpDeleteFadeOutContext(context); + return TRUE; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +/** + * Initializes a graph buffer management structure. + * + * \param Buffers The buffer management structure. + */ +VOID PhInitializeGraphBuffers( + _Out_ PPH_GRAPH_BUFFERS Buffers + ) +{ + Buffers->AllocatedCount = 0; + Buffers->Data1 = NULL; + Buffers->Data2 = NULL; + Buffers->Valid = FALSE; +} + +/** + * Frees resources used by a graph buffer management structure. + * + * \param Buffers The buffer management structure. + */ +VOID PhDeleteGraphBuffers( + _Inout_ PPH_GRAPH_BUFFERS Buffers + ) +{ + if (Buffers->Data1) PhFree(Buffers->Data1); + if (Buffers->Data2) PhFree(Buffers->Data2); +} + +/** + * Sets up a graphing information structure with information from a graph buffer management + * structure. + * + * \param Buffers The buffer management structure. + * \param DrawInfo The graphing information structure. + * \param DataCount The number of data points currently required. The buffers are resized if needed. + */ +VOID PhGetDrawInfoGraphBuffers( + _Inout_ PPH_GRAPH_BUFFERS Buffers, + _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ ULONG DataCount + ) +{ + DrawInfo->LineDataCount = min(DataCount, PH_GRAPH_DATA_COUNT(DrawInfo->Width, DrawInfo->Step)); + + // Do we need to allocate or re-allocate the data buffers? + if (Buffers->AllocatedCount < DrawInfo->LineDataCount) + { + if (Buffers->Data1) + PhFree(Buffers->Data1); + if ((DrawInfo->Flags & PH_GRAPH_USE_LINE_2) && Buffers->Data2) + PhFree(Buffers->Data2); + + Buffers->AllocatedCount *= 2; + + if (Buffers->AllocatedCount < DrawInfo->LineDataCount) + Buffers->AllocatedCount = DrawInfo->LineDataCount; + + Buffers->Data1 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT)); + + if (DrawInfo->Flags & PH_GRAPH_USE_LINE_2) + { + Buffers->Data2 = PhAllocate(Buffers->AllocatedCount * sizeof(FLOAT)); + } + + Buffers->Valid = FALSE; + } + + DrawInfo->LineData1 = Buffers->Data1; + DrawInfo->LineData2 = Buffers->Data2; +} + +VOID PhInitializeGraphState( + _Out_ PPH_GRAPH_STATE State + ) +{ + PhInitializeGraphBuffers(&State->Buffers); + State->Text = NULL; + State->TooltipText = NULL; + State->TooltipIndex = -1; +} + +VOID PhDeleteGraphState( + _Inout_ PPH_GRAPH_STATE State + ) +{ + PhDeleteGraphBuffers(&State->Buffers); + if (State->Text) PhDereferenceObject(State->Text); + if (State->TooltipText) PhDereferenceObject(State->TooltipText); +} + +VOID PhGraphStateGetDrawInfo( + _Inout_ PPH_GRAPH_STATE State, + _In_ PPH_GRAPH_GETDRAWINFO GetDrawInfo, + _In_ ULONG DataCount + ) +{ + PhGetDrawInfoGraphBuffers(&State->Buffers, GetDrawInfo->DrawInfo, DataCount); +} diff --git a/phlib/guisup.c b/phlib/guisup.c index 60c354eec614..740cc4d6c6da 100644 --- a/phlib/guisup.c +++ b/phlib/guisup.c @@ -1,1374 +1,1715 @@ -/* - * Process Hacker - - * GUI support functions - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include -#include - -#include - -#define SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) - -_ChangeWindowMessageFilter ChangeWindowMessageFilter_I; -_IsImmersiveProcess IsImmersiveProcess_I; -_RunFileDlg RunFileDlg; -_SHAutoComplete SHAutoComplete_I; - -static PH_INITONCE SharedIconCacheInitOnce = PH_INITONCE_INIT; -static PPH_HASHTABLE SharedIconCacheHashtable; -static PH_QUEUED_LOCK SharedIconCacheLock = PH_QUEUED_LOCK_INIT; - -VOID PhGuiSupportInitialization( - VOID - ) -{ - HMODULE shell32Handle; - HMODULE shlwapiHandle; - - shell32Handle = LoadLibrary(L"shell32.dll"); - shlwapiHandle = LoadLibrary(L"shlwapi.dll"); - - if (WINDOWS_HAS_UAC) - ChangeWindowMessageFilter_I = PhGetModuleProcAddress(L"user32.dll", "ChangeWindowMessageFilter"); - if (WINDOWS_HAS_IMMERSIVE) - IsImmersiveProcess_I = PhGetModuleProcAddress(L"user32.dll", "IsImmersiveProcess"); - RunFileDlg = PhGetProcedureAddress(shell32Handle, NULL, 61); - SHAutoComplete_I = PhGetProcedureAddress(shlwapiHandle, "SHAutoComplete", 0); -} - -VOID PhSetControlTheme( - _In_ HWND Handle, - _In_ PWSTR Theme - ) -{ - if (WindowsVersion >= WINDOWS_VISTA) - { - SetWindowTheme(Handle, Theme, NULL); - } -} - -INT PhAddListViewColumn( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ INT DisplayIndex, - _In_ INT SubItemIndex, - _In_ INT Format, - _In_ INT Width, - _In_ PWSTR Text - ) -{ - LVCOLUMN column; - - column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER; - column.fmt = Format; - column.cx = Width < 0 ? -Width : SCALE_DPI(Width); - column.pszText = Text; - column.iSubItem = SubItemIndex; - column.iOrder = DisplayIndex; - - return ListView_InsertColumn(ListViewHandle, Index, &column); -} - -INT PhAddListViewItem( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ PWSTR Text, - _In_opt_ PVOID Param - ) -{ - LVITEM item; - - item.mask = LVIF_TEXT | LVIF_PARAM; - item.iItem = Index; - item.iSubItem = 0; - item.pszText = Text; - item.lParam = (LPARAM)Param; - - return ListView_InsertItem(ListViewHandle, &item); -} - -INT PhFindListViewItemByFlags( - _In_ HWND ListViewHandle, - _In_ INT StartIndex, - _In_ ULONG Flags - ) -{ - return ListView_GetNextItem(ListViewHandle, StartIndex, Flags); -} - -INT PhFindListViewItemByParam( - _In_ HWND ListViewHandle, - _In_ INT StartIndex, - _In_opt_ PVOID Param - ) -{ - LVFINDINFO findInfo; - - findInfo.flags = LVFI_PARAM; - findInfo.lParam = (LPARAM)Param; - - return ListView_FindItem(ListViewHandle, StartIndex, &findInfo); -} - -LOGICAL PhGetListViewItemImageIndex( - _In_ HWND ListViewHandle, - _In_ INT Index, - _Out_ PINT ImageIndex - ) -{ - LOGICAL result; - LVITEM item; - - item.mask = LVIF_IMAGE; - item.iItem = Index; - item.iSubItem = 0; - - result = ListView_GetItem(ListViewHandle, &item); - - if (!result) - return result; - - *ImageIndex = item.iImage; - - return result; -} - -LOGICAL PhGetListViewItemParam( - _In_ HWND ListViewHandle, - _In_ INT Index, - _Out_ PVOID *Param - ) -{ - LOGICAL result; - LVITEM item; - - item.mask = LVIF_PARAM; - item.iItem = Index; - item.iSubItem = 0; - - result = ListView_GetItem(ListViewHandle, &item); - - if (!result) - return result; - - *Param = (PVOID)item.lParam; - - return result; -} - -VOID PhRemoveListViewItem( - _In_ HWND ListViewHandle, - _In_ INT Index - ) -{ - ListView_DeleteItem(ListViewHandle, Index); -} - -VOID PhSetListViewItemImageIndex( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ INT ImageIndex - ) -{ - LVITEM item; - - item.mask = LVIF_IMAGE; - item.iItem = Index; - item.iSubItem = 0; - item.iImage = ImageIndex; - - ListView_SetItem(ListViewHandle, &item); -} - -VOID PhSetListViewSubItem( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ INT SubItemIndex, - _In_ PWSTR Text - ) -{ - LVITEM item; - - item.mask = LVIF_TEXT; - item.iItem = Index; - item.iSubItem = SubItemIndex; - item.pszText = Text; - - ListView_SetItem(ListViewHandle, &item); -} - -BOOLEAN PhLoadListViewColumnSettings( - _In_ HWND ListViewHandle, - _In_ PPH_STRING Settings - ) -{ -#define ORDER_LIMIT 50 - PH_STRINGREF remainingPart; - ULONG columnIndex; - ULONG orderArray[ORDER_LIMIT]; // HACK, but reasonable limit - ULONG maxOrder; - ULONG scale; - - if (Settings->Length == 0) - return FALSE; - - remainingPart = Settings->sr; - columnIndex = 0; - memset(orderArray, 0, sizeof(orderArray)); - maxOrder = 0; - - if (remainingPart.Length != 0 && remainingPart.Buffer[0] == '@') - { - PH_STRINGREF scalePart; - ULONG64 integer; - - PhSkipStringRef(&remainingPart, sizeof(WCHAR)); - PhSplitStringRefAtChar(&remainingPart, '|', &scalePart, &remainingPart); - - if (scalePart.Length == 0 || !PhStringToInteger64(&scalePart, 10, &integer)) - return FALSE; - - scale = (ULONG)integer; - } - else - { - scale = PhGlobalDpi; - } - - while (remainingPart.Length != 0) - { - PH_STRINGREF columnPart; - PH_STRINGREF orderPart; - PH_STRINGREF widthPart; - ULONG64 integer; - ULONG order; - ULONG width; - LVCOLUMN lvColumn; - - PhSplitStringRefAtChar(&remainingPart, '|', &columnPart, &remainingPart); - - if (columnPart.Length == 0) - return FALSE; - - PhSplitStringRefAtChar(&columnPart, ',', &orderPart, &widthPart); - - if (orderPart.Length == 0 || widthPart.Length == 0) - return FALSE; - - // Order - - if (!PhStringToInteger64(&orderPart, 10, &integer)) - return FALSE; - - order = (ULONG)integer; - - if (order < ORDER_LIMIT) - { - orderArray[order] = columnIndex; - - if (maxOrder < order + 1) - maxOrder = order + 1; - } - - // Width - - if (!PhStringToInteger64(&widthPart, 10, &integer)) - return FALSE; - - width = (ULONG)integer; - - if (scale != PhGlobalDpi && scale != 0) - width = PhMultiplyDivide(width, PhGlobalDpi, scale); - - lvColumn.mask = LVCF_WIDTH; - lvColumn.cx = width; - ListView_SetColumn(ListViewHandle, columnIndex, &lvColumn); - - columnIndex++; - } - - ListView_SetColumnOrderArray(ListViewHandle, maxOrder, orderArray); - - return TRUE; -} - -PPH_STRING PhSaveListViewColumnSettings( - _In_ HWND ListViewHandle - ) -{ - PH_STRING_BUILDER stringBuilder; - ULONG i = 0; - LVCOLUMN lvColumn; - - PhInitializeStringBuilder(&stringBuilder, 20); - - PhAppendFormatStringBuilder(&stringBuilder, L"@%u|", PhGlobalDpi); - - lvColumn.mask = LVCF_WIDTH | LVCF_ORDER; - - while (ListView_GetColumn(ListViewHandle, i, &lvColumn)) - { - PhAppendFormatStringBuilder( - &stringBuilder, - L"%u,%u|", - lvColumn.iOrder, - lvColumn.cx - ); - i++; - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - return PhFinalStringBuilderString(&stringBuilder); -} - -INT PhAddTabControlTab( - _In_ HWND TabControlHandle, - _In_ INT Index, - _In_ PWSTR Text - ) -{ - TCITEM item; - - item.mask = TCIF_TEXT; - item.pszText = Text; - - return TabCtrl_InsertItem(TabControlHandle, Index, &item); -} - -PPH_STRING PhGetWindowText( - _In_ HWND hwnd - ) -{ - PPH_STRING text; - - PhGetWindowTextEx(hwnd, 0, &text); - return text; -} - -ULONG PhGetWindowTextEx( - _In_ HWND hwnd, - _In_ ULONG Flags, - _Out_opt_ PPH_STRING *Text - ) -{ - PPH_STRING string; - ULONG length; - - if (Flags & PH_GET_WINDOW_TEXT_INTERNAL) - { - if (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY) - { - WCHAR buffer[32]; - length = InternalGetWindowText(hwnd, buffer, sizeof(buffer) / sizeof(WCHAR)); - } - else - { - // TODO: Resize the buffer until we get the entire thing. - string = PhCreateStringEx(NULL, 256 * sizeof(WCHAR)); - length = InternalGetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1); - string->Length = length * sizeof(WCHAR); - - if (Text) - *Text = string; - else - PhDereferenceObject(string); - } - - return length; - } - else - { - length = GetWindowTextLength(hwnd); - - if (length == 0 || (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY)) - { - if (Text) - *Text = PhReferenceEmptyString(); - - return length; - } - - string = PhCreateStringEx(NULL, length * sizeof(WCHAR)); - - if (GetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1)) - { - if (Text) - *Text = string; - else - PhDereferenceObject(string); - - return length; - } - else - { - if (Text) - *Text = PhReferenceEmptyString(); - - PhDereferenceObject(string); - - return 0; - } - } -} - -VOID PhAddComboBoxStrings( - _In_ HWND hWnd, - _In_ PWSTR *Strings, - _In_ ULONG NumberOfStrings - ) -{ - ULONG i; - - for (i = 0; i < NumberOfStrings; i++) - ComboBox_AddString(hWnd, Strings[i]); -} - -PPH_STRING PhGetComboBoxString( - _In_ HWND hwnd, - _In_ INT Index - ) -{ - PPH_STRING string; - ULONG length; - - if (Index == -1) - { - Index = ComboBox_GetCurSel(hwnd); - - if (Index == -1) - return NULL; - } - - length = ComboBox_GetLBTextLen(hwnd, Index); - - if (length == CB_ERR) - return NULL; - if (length == 0) - return PhReferenceEmptyString(); - - string = PhCreateStringEx(NULL, length * 2); - - if (ComboBox_GetLBText(hwnd, Index, string->Buffer) != CB_ERR) - { - return string; - } - else - { - PhDereferenceObject(string); - return NULL; - } -} - -INT PhSelectComboBoxString( - _In_ HWND hwnd, - _In_ PWSTR String, - _In_ BOOLEAN Partial - ) -{ - if (Partial) - { - return ComboBox_SelectString(hwnd, -1, String); - } - else - { - INT index; - - index = ComboBox_FindStringExact(hwnd, -1, String); - - if (index == CB_ERR) - return CB_ERR; - - ComboBox_SetCurSel(hwnd, index); - - return index; - } -} - -PPH_STRING PhGetListBoxString( - _In_ HWND hwnd, - _In_ INT Index - ) -{ - PPH_STRING string; - ULONG length; - - if (Index == -1) - { - Index = ListBox_GetCurSel(hwnd); - - if (Index == -1) - return NULL; - } - - length = ListBox_GetTextLen(hwnd, Index); - - if (length == LB_ERR) - return NULL; - if (length == 0) - return PhReferenceEmptyString(); - - string = PhCreateStringEx(NULL, length * 2); - - if (ListBox_GetText(hwnd, Index, string->Buffer) != LB_ERR) - { - return string; - } - else - { - PhDereferenceObject(string); - return NULL; - } -} - -VOID PhSetStateAllListViewItems( - _In_ HWND hWnd, - _In_ ULONG State, - _In_ ULONG Mask - ) -{ - ULONG i; - ULONG count; - - count = ListView_GetItemCount(hWnd); - - if (count == -1) - return; - - for (i = 0; i < count; i++) - { - ListView_SetItemState(hWnd, i, State, Mask); - } -} - -PVOID PhGetSelectedListViewItemParam( - _In_ HWND hWnd - ) -{ - INT index; - PVOID param; - - index = PhFindListViewItemByFlags( - hWnd, - -1, - LVNI_SELECTED - ); - - if (index != -1) - { - if (PhGetListViewItemParam( - hWnd, - index, - ¶m - )) - { - return param; - } - } - - return NULL; -} - -VOID PhGetSelectedListViewItemParams( - _In_ HWND hWnd, - _Out_ PVOID **Items, - _Out_ PULONG NumberOfItems - ) -{ - PH_ARRAY array; - ULONG index; - PVOID param; - - PhInitializeArray(&array, sizeof(PVOID), 2); - index = -1; - - while ((index = PhFindListViewItemByFlags( - hWnd, - index, - LVNI_SELECTED - )) != -1) - { - if (PhGetListViewItemParam(hWnd, index, ¶m)) - PhAddItemArray(&array, ¶m); - } - - *NumberOfItems = (ULONG)array.Count; - *Items = PhFinalArrayItems(&array); -} - -VOID PhSetImageListBitmap( - _In_ HIMAGELIST ImageList, - _In_ INT Index, - _In_ HINSTANCE InstanceHandle, - _In_ LPCWSTR BitmapName - ) -{ - HBITMAP bitmap; - - bitmap = LoadImage(InstanceHandle, BitmapName, IMAGE_BITMAP, 0, 0, 0); - - if (bitmap) - { - ImageList_Replace(ImageList, Index, bitmap, NULL); - DeleteObject(bitmap); - } -} - -static BOOLEAN SharedIconCacheHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPHP_ICON_ENTRY entry1 = Entry1; - PPHP_ICON_ENTRY entry2 = Entry2; - - if (entry1->InstanceHandle != entry2->InstanceHandle || - entry1->Width != entry2->Width || - entry1->Height != entry2->Height) - { - return FALSE; - } - - if (IS_INTRESOURCE(entry1->Name)) - { - if (IS_INTRESOURCE(entry2->Name)) - return entry1->Name == entry2->Name; - else - return FALSE; - } - else - { - if (!IS_INTRESOURCE(entry2->Name)) - return PhEqualStringZ(entry1->Name, entry2->Name, FALSE); - else - return FALSE; - } -} - -static ULONG SharedIconCacheHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PPHP_ICON_ENTRY entry = Entry; - ULONG nameHash; - - if (IS_INTRESOURCE(entry->Name)) - nameHash = PtrToUlong(entry->Name); - else - nameHash = PhHashBytes((PUCHAR)entry->Name, PhCountStringZ(entry->Name)); - - return nameHash ^ (PtrToUlong(entry->InstanceHandle) >> 5) ^ (entry->Width << 3) ^ entry->Height; -} - -HICON PhLoadIcon( - _In_opt_ HINSTANCE InstanceHandle, - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_opt_ ULONG Width, - _In_opt_ ULONG Height - ) -{ - static _LoadIconMetric loadIconMetric; - static _LoadIconWithScaleDown loadIconWithScaleDown; - - PHP_ICON_ENTRY entry; - PPHP_ICON_ENTRY actualEntry; - HICON icon = NULL; - - if (PhBeginInitOnce(&SharedIconCacheInitOnce)) - { - loadIconMetric = (_LoadIconMetric)PhGetModuleProcAddress(L"comctl32.dll", "LoadIconMetric"); - loadIconWithScaleDown = (_LoadIconWithScaleDown)PhGetModuleProcAddress(L"comctl32.dll", "LoadIconWithScaleDown"); - SharedIconCacheHashtable = PhCreateHashtable(sizeof(PHP_ICON_ENTRY), - SharedIconCacheHashtableEqualFunction, SharedIconCacheHashtableHashFunction, 10); - PhEndInitOnce(&SharedIconCacheInitOnce); - } - - if (Flags & PH_LOAD_ICON_SHARED) - { - PhAcquireQueuedLockExclusive(&SharedIconCacheLock); - - entry.InstanceHandle = InstanceHandle; - entry.Name = Name; - entry.Width = PhpGetIconEntrySize(Width, Flags); - entry.Height = PhpGetIconEntrySize(Height, Flags); - actualEntry = PhFindEntryHashtable(SharedIconCacheHashtable, &entry); - - if (actualEntry) - { - icon = actualEntry->Icon; - PhReleaseQueuedLockExclusive(&SharedIconCacheLock); - return icon; - } - } - - if (Flags & (PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_SIZE_LARGE)) - { - if (loadIconMetric) - loadIconMetric(InstanceHandle, Name, (Flags & PH_LOAD_ICON_SIZE_SMALL) ? LIM_SMALL : LIM_LARGE, &icon); - } - else - { - if (loadIconWithScaleDown) - loadIconWithScaleDown(InstanceHandle, Name, Width, Height, &icon); - } - - if (!icon && !(Flags & PH_LOAD_ICON_STRICT)) - { - if (Flags & PH_LOAD_ICON_SIZE_SMALL) - { - static ULONG smallWidth = 0; - static ULONG smallHeight = 0; - - if (!smallWidth) - smallWidth = GetSystemMetrics(SM_CXSMICON); - if (!smallHeight) - smallHeight = GetSystemMetrics(SM_CYSMICON); - - Width = smallWidth; - Height = smallHeight; - } - else if (Flags & PH_LOAD_ICON_SIZE_LARGE) - { - static ULONG largeWidth = 0; - static ULONG largeHeight = 0; - - if (!largeWidth) - largeWidth = GetSystemMetrics(SM_CXICON); - if (!largeHeight) - largeHeight = GetSystemMetrics(SM_CYICON); - - Width = largeWidth; - Height = largeHeight; - } - - icon = LoadImage(InstanceHandle, Name, IMAGE_ICON, Width, Height, 0); - } - - if (Flags & PH_LOAD_ICON_SHARED) - { - if (icon) - { - if (!IS_INTRESOURCE(Name)) - entry.Name = PhDuplicateStringZ(Name); - entry.Icon = icon; - PhAddEntryHashtable(SharedIconCacheHashtable, &entry); - } - - PhReleaseQueuedLockExclusive(&SharedIconCacheLock); - } - - return icon; -} - -/** - * Gets the default icon used for executable files. - * - * \param SmallIcon A variable which receives the small default executable icon. Do not destroy the - * icon using DestroyIcon(); it is shared between callers. - * \param LargeIcon A variable which receives the large default executable icon. Do not destroy the - * icon using DestroyIcon(); it is shared between callers. - */ -VOID PhGetStockApplicationIcon( - _Out_opt_ HICON *SmallIcon, - _Out_opt_ HICON *LargeIcon - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static HICON smallIcon = NULL; - static HICON largeIcon = NULL; - - // This no longer uses SHGetFileInfo because it is *very* slow and causes many other DLLs to be - // loaded, increasing memory usage. The worst thing about it, however, is that it is horribly - // incompatible with multi-threading. The first time it is called, it tries to perform some - // one-time initialization. It guards this with a lock, but when multiple threads try to call - // the function at the same time, instead of waiting for initialization to finish it simply - // fails the other threads. - - if (PhBeginInitOnce(&initOnce)) - { - PPH_STRING systemDirectory; - PPH_STRING dllFileName; - - // imageres,11 (Windows 10 and above), user32,0 (Vista and above) or shell32,2 (XP) contains - // the default application icon. - - if (systemDirectory = PhGetSystemDirectory()) - { - PH_STRINGREF dllBaseName; - ULONG index; - - // TODO: Find a better solution. - if (WindowsVersion >= WINDOWS_10) - { - PhInitializeStringRef(&dllBaseName, L"\\imageres.dll"); - index = 11; - } - else if (WindowsVersion >= WINDOWS_VISTA) - { - PhInitializeStringRef(&dllBaseName, L"\\user32.dll"); - index = 0; - } - else - { - PhInitializeStringRef(&dllBaseName, L"\\shell32.dll"); - index = 2; - } - - dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName); - PhDereferenceObject(systemDirectory); - - ExtractIconEx(dllFileName->Buffer, index, &largeIcon, &smallIcon, 1); - PhDereferenceObject(dllFileName); - } - - // Fallback icons - if (!smallIcon) - smallIcon = PhLoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION), PH_LOAD_ICON_SIZE_SMALL, 0, 0); - if (!largeIcon) - largeIcon = PhLoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION), PH_LOAD_ICON_SIZE_LARGE, 0, 0); - - PhEndInitOnce(&initOnce); - } - - if (SmallIcon) - *SmallIcon = smallIcon; - if (LargeIcon) - *LargeIcon = largeIcon; -} - -HICON PhGetFileShellIcon( - _In_opt_ PWSTR FileName, - _In_opt_ PWSTR DefaultExtension, - _In_ BOOLEAN LargeIcon - ) -{ - SHFILEINFO fileInfo; - ULONG iconFlag; - HICON icon; - - if (DefaultExtension && PhEqualStringZ(DefaultExtension, L".exe", TRUE)) - { - // Special case for executable files (see above for reasoning). - - icon = NULL; - - if (FileName) - { - ExtractIconEx( - FileName, - 0, - LargeIcon ? &icon : NULL, - !LargeIcon ? &icon : NULL, - 1 - ); - } - - if (!icon) - { - PhGetStockApplicationIcon( - !LargeIcon ? &icon : NULL, - LargeIcon ? &icon : NULL - ); - - if (icon) - icon = DuplicateIcon(NULL, icon); - } - - return icon; - } - - iconFlag = LargeIcon ? SHGFI_LARGEICON : SHGFI_SMALLICON; - icon = NULL; - - if (FileName && SHGetFileInfo( - FileName, - 0, - &fileInfo, - sizeof(SHFILEINFO), - SHGFI_ICON | iconFlag - )) - { - icon = fileInfo.hIcon; - } - - if (!icon && DefaultExtension) - { - if (SHGetFileInfo( - DefaultExtension, - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(SHFILEINFO), - SHGFI_ICON | iconFlag | SHGFI_USEFILEATTRIBUTES - )) - icon = fileInfo.hIcon; - } - - return icon; -} - -VOID PhpSetClipboardData( - _In_ HWND hWnd, - _In_ ULONG Format, - _In_ HANDLE Data - ) -{ - if (OpenClipboard(hWnd)) - { - if (!EmptyClipboard()) - goto Fail; - - if (!SetClipboardData(Format, Data)) - goto Fail; - - CloseClipboard(); - - return; - } - -Fail: - GlobalFree(Data); -} - -VOID PhSetClipboardString( - _In_ HWND hWnd, - _In_ PPH_STRINGREF String - ) -{ - HANDLE data; - PVOID memory; - - data = GlobalAlloc(GMEM_MOVEABLE, String->Length + sizeof(WCHAR)); - memory = GlobalLock(data); - - memcpy(memory, String->Buffer, String->Length); - *(PWCHAR)((PCHAR)memory + String->Length) = 0; - - GlobalUnlock(memory); - - PhpSetClipboardData(hWnd, CF_UNICODETEXT, data); -} - -HWND PhCreateDialogFromTemplate( - _In_ HWND Parent, - _In_ ULONG Style, - _In_ PVOID Instance, - _In_ PWSTR Template, - _In_ DLGPROC DialogProc, - _In_ PVOID Parameter - ) -{ - HRSRC resourceInfo; - ULONG resourceSize; - HGLOBAL resourceHandle; - PDLGTEMPLATEEX dialog; - PDLGTEMPLATEEX dialogCopy; - HWND dialogHandle; - - resourceInfo = FindResource(Instance, Template, MAKEINTRESOURCE(RT_DIALOG)); - - if (!resourceInfo) - return NULL; - - resourceSize = SizeofResource(Instance, resourceInfo); - - if (resourceSize == 0) - return NULL; - - resourceHandle = LoadResource(Instance, resourceInfo); - - if (!resourceHandle) - return NULL; - - dialog = LockResource(resourceHandle); - - if (!dialog) - return NULL; - - dialogCopy = PhAllocateCopy(dialog, resourceSize); - - if (dialogCopy->signature == 0xffff) - { - dialogCopy->style = Style; - } - else - { - ((DLGTEMPLATE *)dialogCopy)->style = Style; - } - - dialogHandle = CreateDialogIndirectParam(Instance, (DLGTEMPLATE *)dialogCopy, Parent, DialogProc, (LPARAM)Parameter); - - PhFree(dialogCopy); - - return dialogHandle; -} - -BOOLEAN PhModalPropertySheet( - _Inout_ PROPSHEETHEADER *Header - ) -{ - // PropertySheet incorrectly discards WM_QUIT messages in certain cases, so we will use our own - // message loop. An example of this is when GetMessage (called by PropertySheet's message loop) - // dispatches a message directly from kernel-mode that causes the property sheet to close. In - // that case PropertySheet will retrieve the WM_QUIT message but will ignore it because of its - // buggy logic. - - // This is also a good opportunity to introduce an auto-pool. - - PH_AUTO_POOL autoPool; - HWND oldFocus; - HWND topLevelOwner; - HWND hwnd; - BOOL result; - MSG message; - - PhInitializeAutoPool(&autoPool); - - oldFocus = GetFocus(); - topLevelOwner = Header->hwndParent; - - while (topLevelOwner && (GetWindowLong(topLevelOwner, GWL_STYLE) & WS_CHILD)) - topLevelOwner = GetParent(topLevelOwner); - - if (topLevelOwner && (topLevelOwner == GetDesktopWindow() || EnableWindow(topLevelOwner, FALSE))) - topLevelOwner = NULL; - - Header->dwFlags |= PSH_MODELESS; - hwnd = (HWND)PropertySheet(Header); - - if (!hwnd) - { - if (topLevelOwner) - EnableWindow(topLevelOwner, TRUE); - - return FALSE; - } - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (!PropSheet_IsDialogMessage(hwnd, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&autoPool); - - // Destroy the window when necessary. - if (!PropSheet_GetCurrentPageHwnd(hwnd)) - break; - } - - if (result == 0) - PostQuitMessage((INT)message.wParam); - if (Header->hwndParent && GetActiveWindow() == hwnd) - SetActiveWindow(Header->hwndParent); - if (topLevelOwner) - EnableWindow(topLevelOwner, TRUE); - if (oldFocus && IsWindow(oldFocus)) - SetFocus(oldFocus); - - DestroyWindow(hwnd); - PhDeleteAutoPool(&autoPool); - - return TRUE; -} - -VOID PhInitializeLayoutManager( - _Out_ PPH_LAYOUT_MANAGER Manager, - _In_ HWND RootWindowHandle - ) -{ - Manager->List = PhCreateList(4); - Manager->LayoutNumber = 0; - - Manager->RootItem.Handle = RootWindowHandle; - GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect); - Manager->RootItem.OrigRect = Manager->RootItem.Rect; - Manager->RootItem.ParentItem = NULL; - Manager->RootItem.LayoutParentItem = NULL; - Manager->RootItem.LayoutNumber = 0; - Manager->RootItem.NumberOfChildren = 0; - Manager->RootItem.DeferHandle = NULL; -} - -VOID PhDeleteLayoutManager( - _Inout_ PPH_LAYOUT_MANAGER Manager - ) -{ - ULONG i; - - for (i = 0; i < Manager->List->Count; i++) - PhFree(Manager->List->Items[i]); - - PhDereferenceObject(Manager->List); -} - -// HACK: The math below is all horribly broken, especially the HACK for multiline tab controls. - -PPH_LAYOUT_ITEM PhAddLayoutItem( - _Inout_ PPH_LAYOUT_MANAGER Manager, - _In_ HWND Handle, - _In_opt_ PPH_LAYOUT_ITEM ParentItem, - _In_ ULONG Anchor - ) -{ - PPH_LAYOUT_ITEM layoutItem; - RECT dummy = { 0 }; - - layoutItem = PhAddLayoutItemEx( - Manager, - Handle, - ParentItem, - Anchor, - dummy - ); - - layoutItem->Margin = layoutItem->Rect; - PhConvertRect(&layoutItem->Margin, &layoutItem->ParentItem->Rect); - - if (layoutItem->ParentItem != layoutItem->LayoutParentItem) - { - // Fix the margin because the item has a dummy parent. They share the same layout parent - // item. - layoutItem->Margin.top -= layoutItem->ParentItem->Rect.top; - layoutItem->Margin.left -= layoutItem->ParentItem->Rect.left; - layoutItem->Margin.right = layoutItem->ParentItem->Margin.right; - layoutItem->Margin.bottom = layoutItem->ParentItem->Margin.bottom; - } - - return layoutItem; -} - -PPH_LAYOUT_ITEM PhAddLayoutItemEx( - _Inout_ PPH_LAYOUT_MANAGER Manager, - _In_ HWND Handle, - _In_opt_ PPH_LAYOUT_ITEM ParentItem, - _In_ ULONG Anchor, - _In_ RECT Margin - ) -{ - PPH_LAYOUT_ITEM item; - - if (!ParentItem) - ParentItem = &Manager->RootItem; - - item = PhAllocate(sizeof(PH_LAYOUT_ITEM)); - item->Handle = Handle; - item->ParentItem = ParentItem; - item->LayoutNumber = Manager->LayoutNumber; - item->NumberOfChildren = 0; - item->DeferHandle = NULL; - item->Anchor = Anchor; - - item->LayoutParentItem = item->ParentItem; - - while ((item->LayoutParentItem->Anchor & PH_LAYOUT_DUMMY_MASK) && - item->LayoutParentItem->LayoutParentItem) - { - item->LayoutParentItem = item->LayoutParentItem->LayoutParentItem; - } - - item->LayoutParentItem->NumberOfChildren++; - - GetWindowRect(Handle, &item->Rect); - MapWindowPoints(NULL, item->LayoutParentItem->Handle, (POINT *)&item->Rect, 2); - - if (item->Anchor & PH_LAYOUT_TAB_CONTROL) - { - // We want to convert the tab control rectangle to the tab page display rectangle. - TabCtrl_AdjustRect(Handle, FALSE, &item->Rect); - } - - item->Margin = Margin; - - item->OrigRect = item->Rect; - - PhAddItemList(Manager->List, item); - - return item; -} - -VOID PhpLayoutItemLayout( - _Inout_ PPH_LAYOUT_MANAGER Manager, - _Inout_ PPH_LAYOUT_ITEM Item - ) -{ - RECT rect; - BOOLEAN hasDummyParent; - - if (Item->NumberOfChildren > 0 && !Item->DeferHandle) - Item->DeferHandle = BeginDeferWindowPos(Item->NumberOfChildren); - - if (Item->LayoutNumber == Manager->LayoutNumber) - return; - - // If this is the root item we must stop here. - if (!Item->ParentItem) - return; - - PhpLayoutItemLayout(Manager, Item->ParentItem); - - if (Item->ParentItem != Item->LayoutParentItem) - { - PhpLayoutItemLayout(Manager, Item->LayoutParentItem); - hasDummyParent = TRUE; - } - else - { - hasDummyParent = FALSE; - } - - GetWindowRect(Item->Handle, &Item->Rect); - MapWindowPoints(NULL, Item->LayoutParentItem->Handle, (POINT *)&Item->Rect, 2); - - if (Item->Anchor & PH_LAYOUT_TAB_CONTROL) - { - // We want to convert the tab control rectangle to the tab page display rectangle. - TabCtrl_AdjustRect(Item->Handle, FALSE, &Item->Rect); - } - - if (!(Item->Anchor & PH_LAYOUT_DUMMY_MASK)) - { - // Convert right/bottom into margins to make the calculations - // easier. - rect = Item->Rect; - PhConvertRect(&rect, &Item->LayoutParentItem->Rect); - - if (!(Item->Anchor & (PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT))) - { - // TODO - PhRaiseStatus(STATUS_NOT_IMPLEMENTED); - } - else if (Item->Anchor & PH_ANCHOR_RIGHT) - { - if (Item->Anchor & PH_ANCHOR_LEFT) - { - rect.left = (hasDummyParent ? Item->ParentItem->Rect.left : 0) + Item->Margin.left; - rect.right = Item->Margin.right; - } - else - { - ULONG diff = Item->Margin.right - rect.right; - - rect.left -= diff; - rect.right += diff; - } - } - - if (!(Item->Anchor & (PH_ANCHOR_TOP | PH_ANCHOR_BOTTOM))) - { - // TODO - PhRaiseStatus(STATUS_NOT_IMPLEMENTED); - } - else if (Item->Anchor & PH_ANCHOR_BOTTOM) - { - if (Item->Anchor & PH_ANCHOR_TOP) - { - // tab control hack - rect.top = (hasDummyParent ? Item->ParentItem->Rect.top : 0) + Item->Margin.top; - rect.bottom = Item->Margin.bottom; - } - else - { - ULONG diff = Item->Margin.bottom - rect.bottom; - - rect.top -= diff; - rect.bottom += diff; - } - } - - // Convert the right/bottom back into co-ordinates. - PhConvertRect(&rect, &Item->LayoutParentItem->Rect); - Item->Rect = rect; - - if (!(Item->Anchor & PH_LAYOUT_IMMEDIATE_RESIZE)) - { - Item->LayoutParentItem->DeferHandle = DeferWindowPos( - Item->LayoutParentItem->DeferHandle, Item->Handle, - NULL, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER - ); - } - else - { - // This is needed for tab controls, so that TabCtrl_AdjustRect will give us an - // up-to-date result. - SetWindowPos( - Item->Handle, - NULL, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER - ); - } - } - - Item->LayoutNumber = Manager->LayoutNumber; -} - -VOID PhLayoutManagerLayout( - _Inout_ PPH_LAYOUT_MANAGER Manager - ) -{ - ULONG i; - - Manager->LayoutNumber++; - - GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect); - - for (i = 0; i < Manager->List->Count; i++) - { - PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i]; - - PhpLayoutItemLayout(Manager, item); - } - - for (i = 0; i < Manager->List->Count; i++) - { - PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i]; - - if (item->DeferHandle) - { - EndDeferWindowPos(item->DeferHandle); - item->DeferHandle = NULL; - } - - if (item->Anchor & PH_LAYOUT_FORCE_INVALIDATE) - { - InvalidateRect(item->Handle, NULL, FALSE); - } - } - - if (Manager->RootItem.DeferHandle) - { - EndDeferWindowPos(Manager->RootItem.DeferHandle); - Manager->RootItem.DeferHandle = NULL; - } -} +/* + * Process Hacker - + * GUI support functions + * + * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include + +#include + +#define SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) + +BOOLEAN NTAPI PhpWindowContextHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); +ULONG NTAPI PhpWindowContextHashtableHashFunction( + _In_ PVOID Entry + ); +BOOLEAN NTAPI PhpWindowCallbackHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); +ULONG NTAPI PhpWindowCallbackHashtableHashFunction( + _In_ PVOID Entry + ); + +typedef struct _PH_WINDOW_PROPERTY_CONTEXT +{ + ULONG PropertyHash; + HWND WindowHandle; + PVOID Context; +} PH_WINDOW_PROPERTY_CONTEXT, *PPH_WINDOW_PROPERTY_CONTEXT; + +HFONT PhApplicationFont = NULL; +HFONT PhTreeWindowFont = NULL; +PH_INTEGER_PAIR PhSmallIconSize = { 16, 16 }; +PH_INTEGER_PAIR PhLargeIconSize = { 32, 32 }; + +static PH_INITONCE SharedIconCacheInitOnce = PH_INITONCE_INIT; +static PPH_HASHTABLE SharedIconCacheHashtable; +static PH_QUEUED_LOCK SharedIconCacheLock = PH_QUEUED_LOCK_INIT; + +static PPH_HASHTABLE WindowCallbackHashTable = NULL; +static PH_QUEUED_LOCK WindowCallbackListLock = PH_QUEUED_LOCK_INIT; +static PPH_HASHTABLE WindowContextHashTable = NULL; +static PH_QUEUED_LOCK WindowContextListLock = PH_QUEUED_LOCK_INIT; + +VOID PhGuiSupportInitialization( + VOID + ) +{ + HDC hdc; + + WindowCallbackHashTable = PhCreateHashtable( + sizeof(PH_PLUGIN_WINDOW_CALLBACK_REGISTRATION), + PhpWindowCallbackHashtableEqualFunction, + PhpWindowCallbackHashtableHashFunction, + 10 + ); + WindowContextHashTable = PhCreateHashtable( + sizeof(PH_WINDOW_PROPERTY_CONTEXT), + PhpWindowContextHashtableEqualFunction, + PhpWindowContextHashtableHashFunction, + 10 + ); + + PhSmallIconSize.X = GetSystemMetrics(SM_CXSMICON); + PhSmallIconSize.Y = GetSystemMetrics(SM_CYSMICON); + PhLargeIconSize.X = GetSystemMetrics(SM_CXICON); + PhLargeIconSize.Y = GetSystemMetrics(SM_CYICON); + + if (hdc = GetDC(NULL)) + { + PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + } +} + +VOID PhSetControlTheme( + _In_ HWND Handle, + _In_ PWSTR Theme + ) +{ + SetWindowTheme(Handle, Theme, NULL); +} + +INT PhAddListViewColumn( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ INT DisplayIndex, + _In_ INT SubItemIndex, + _In_ INT Format, + _In_ INT Width, + _In_ PWSTR Text + ) +{ + LVCOLUMN column; + + column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER; + column.fmt = Format; + column.cx = Width < 0 ? -Width : SCALE_DPI(Width); + column.pszText = Text; + column.iSubItem = SubItemIndex; + column.iOrder = DisplayIndex; + + return ListView_InsertColumn(ListViewHandle, Index, &column); +} + +INT PhAddListViewItem( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ PWSTR Text, + _In_opt_ PVOID Param + ) +{ + LVITEM item; + + item.mask = LVIF_TEXT | LVIF_PARAM; + item.iItem = Index; + item.iSubItem = 0; + item.pszText = Text; + item.lParam = (LPARAM)Param; + + return ListView_InsertItem(ListViewHandle, &item); +} + +INT PhFindListViewItemByFlags( + _In_ HWND ListViewHandle, + _In_ INT StartIndex, + _In_ ULONG Flags + ) +{ + return ListView_GetNextItem(ListViewHandle, StartIndex, Flags); +} + +INT PhFindListViewItemByParam( + _In_ HWND ListViewHandle, + _In_ INT StartIndex, + _In_opt_ PVOID Param + ) +{ + LVFINDINFO findInfo; + + findInfo.flags = LVFI_PARAM; + findInfo.lParam = (LPARAM)Param; + + return ListView_FindItem(ListViewHandle, StartIndex, &findInfo); +} + +BOOLEAN PhGetListViewItemImageIndex( + _In_ HWND ListViewHandle, + _In_ INT Index, + _Out_ PINT ImageIndex + ) +{ + LVITEM item; + + item.mask = LVIF_IMAGE; + item.iItem = Index; + item.iSubItem = 0; + + if (!ListView_GetItem(ListViewHandle, &item)) + return FALSE; + + *ImageIndex = item.iImage; + + return TRUE; +} + +BOOLEAN PhGetListViewItemParam( + _In_ HWND ListViewHandle, + _In_ INT Index, + _Out_ PVOID *Param + ) +{ + LVITEM item; + + item.mask = LVIF_PARAM; + item.iItem = Index; + item.iSubItem = 0; + + if (!ListView_GetItem(ListViewHandle, &item)) + return FALSE; + + *Param = (PVOID)item.lParam; + + return TRUE; +} + +VOID PhRemoveListViewItem( + _In_ HWND ListViewHandle, + _In_ INT Index + ) +{ + ListView_DeleteItem(ListViewHandle, Index); +} + +VOID PhSetListViewItemImageIndex( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ INT ImageIndex + ) +{ + LVITEM item; + + item.mask = LVIF_IMAGE; + item.iItem = Index; + item.iSubItem = 0; + item.iImage = ImageIndex; + + ListView_SetItem(ListViewHandle, &item); +} + +VOID PhSetListViewSubItem( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ INT SubItemIndex, + _In_ PWSTR Text + ) +{ + LVITEM item; + + item.mask = LVIF_TEXT; + item.iItem = Index; + item.iSubItem = SubItemIndex; + item.pszText = Text; + + ListView_SetItem(ListViewHandle, &item); +} + +INT PhAddListViewGroup( + _In_ HWND ListViewHandle, + _In_ INT GroupId, + _In_ PWSTR Text + ) +{ + LVGROUP group; + + memset(&group, 0, sizeof(LVGROUP)); + group.cbSize = sizeof(LVGROUP); + group.mask = LVGF_HEADER | LVGF_ALIGN | LVGF_STATE | LVGF_GROUPID; + group.uAlign = LVGA_HEADER_LEFT; + group.state = LVGS_COLLAPSIBLE; + group.iGroupId = GroupId; + group.pszHeader = Text; + + return (INT)ListView_InsertGroup(ListViewHandle, MAXINT, &group); +} + +INT PhAddListViewGroupItem( + _In_ HWND ListViewHandle, + _In_ INT GroupId, + _In_ INT Index, + _In_ PWSTR Text, + _In_opt_ PVOID Param + ) +{ + LVITEM item; + + item.mask = LVIF_TEXT | LVIF_GROUPID; + item.iItem = Index; + item.iSubItem = 0; + item.pszText = Text; + item.iGroupId = GroupId; + + if (Param) + { + item.mask |= LVIF_PARAM; + item.lParam = (LPARAM)Param; + } + + return ListView_InsertItem(ListViewHandle, &item); +} + +INT PhAddTabControlTab( + _In_ HWND TabControlHandle, + _In_ INT Index, + _In_ PWSTR Text + ) +{ + TCITEM item; + + item.mask = TCIF_TEXT; + item.pszText = Text; + + return TabCtrl_InsertItem(TabControlHandle, Index, &item); +} + +PPH_STRING PhGetWindowText( + _In_ HWND hwnd + ) +{ + PPH_STRING text; + + PhGetWindowTextEx(hwnd, 0, &text); + return text; +} + +ULONG PhGetWindowTextEx( + _In_ HWND hwnd, + _In_ ULONG Flags, + _Out_opt_ PPH_STRING *Text + ) +{ + PPH_STRING string; + ULONG length; + + if (Flags & PH_GET_WINDOW_TEXT_INTERNAL) + { + if (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY) + { + WCHAR buffer[32]; + length = InternalGetWindowText(hwnd, buffer, sizeof(buffer) / sizeof(WCHAR)); + } + else + { + // TODO: Resize the buffer until we get the entire thing. + string = PhCreateStringEx(NULL, 256 * sizeof(WCHAR)); + length = InternalGetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1); + string->Length = length * sizeof(WCHAR); + + if (Text) + *Text = string; + else + PhDereferenceObject(string); + } + + return length; + } + else + { + length = GetWindowTextLength(hwnd); + + if (length == 0 || (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY)) + { + if (Text) + *Text = PhReferenceEmptyString(); + + return length; + } + + string = PhCreateStringEx(NULL, length * sizeof(WCHAR)); + + if (GetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1)) + { + if (Text) + *Text = string; + else + PhDereferenceObject(string); + + return length; + } + else + { + if (Text) + *Text = PhReferenceEmptyString(); + + PhDereferenceObject(string); + + return 0; + } + } +} + +ULONG PhGetWindowTextToBuffer( + _In_ HWND hwnd, + _In_ ULONG Flags, + _Out_writes_bytes_opt_(BufferLength) PWSTR Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength + ) +{ + ULONG status = ERROR_SUCCESS; + ULONG length; + + if (Flags & PH_GET_WINDOW_TEXT_INTERNAL) + length = InternalGetWindowText(hwnd, Buffer, BufferLength); + else + length = GetWindowText(hwnd, Buffer, BufferLength); + + if (length == 0) + status = GetLastError(); + + if (ReturnLength) + *ReturnLength = length; + + return status; +} + +VOID PhAddComboBoxStrings( + _In_ HWND hWnd, + _In_ PWSTR *Strings, + _In_ ULONG NumberOfStrings + ) +{ + ULONG i; + + for (i = 0; i < NumberOfStrings; i++) + ComboBox_AddString(hWnd, Strings[i]); +} + +PPH_STRING PhGetComboBoxString( + _In_ HWND hwnd, + _In_ INT Index + ) +{ + PPH_STRING string; + ULONG length; + + if (Index == -1) + { + Index = ComboBox_GetCurSel(hwnd); + + if (Index == CB_ERR) + return NULL; + } + + length = ComboBox_GetLBTextLen(hwnd, Index); + + if (length == CB_ERR) + return NULL; + if (length == 0) + return PhReferenceEmptyString(); + + string = PhCreateStringEx(NULL, length * sizeof(WCHAR)); + + if (ComboBox_GetLBText(hwnd, Index, string->Buffer) != CB_ERR) + { + return string; + } + else + { + PhDereferenceObject(string); + return NULL; + } +} + +INT PhSelectComboBoxString( + _In_ HWND hwnd, + _In_ PWSTR String, + _In_ BOOLEAN Partial + ) +{ + if (Partial) + { + return ComboBox_SelectString(hwnd, -1, String); + } + else + { + INT index; + + index = ComboBox_FindStringExact(hwnd, -1, String); + + if (index == CB_ERR) + return CB_ERR; + + ComboBox_SetCurSel(hwnd, index); + + return index; + } +} + +PPH_STRING PhGetListBoxString( + _In_ HWND hwnd, + _In_ INT Index + ) +{ + PPH_STRING string; + ULONG length; + + if (Index == -1) + { + Index = ListBox_GetCurSel(hwnd); + + if (Index == LB_ERR) + return NULL; + } + + length = ListBox_GetTextLen(hwnd, Index); + + if (length == LB_ERR) + return NULL; + if (length == 0) + return PhReferenceEmptyString(); + + string = PhCreateStringEx(NULL, length * sizeof(WCHAR)); + + if (ListBox_GetText(hwnd, Index, string->Buffer) != LB_ERR) + { + return string; + } + else + { + PhDereferenceObject(string); + return NULL; + } +} + +VOID PhSetStateAllListViewItems( + _In_ HWND hWnd, + _In_ ULONG State, + _In_ ULONG Mask + ) +{ + INT i; + INT count; + + count = ListView_GetItemCount(hWnd); + + if (count <= 0) + return; + + for (i = 0; i < count; i++) + { + ListView_SetItemState(hWnd, i, State, Mask); + } +} + +PVOID PhGetSelectedListViewItemParam( + _In_ HWND hWnd + ) +{ + INT index; + PVOID param; + + index = PhFindListViewItemByFlags( + hWnd, + -1, + LVNI_SELECTED + ); + + if (index != -1) + { + if (PhGetListViewItemParam( + hWnd, + index, + ¶m + )) + { + return param; + } + } + + return NULL; +} + +VOID PhGetSelectedListViewItemParams( + _In_ HWND hWnd, + _Out_ PVOID **Items, + _Out_ PULONG NumberOfItems + ) +{ + PH_ARRAY array; + ULONG index; + PVOID param; + + PhInitializeArray(&array, sizeof(PVOID), 2); + index = -1; + + while ((index = PhFindListViewItemByFlags( + hWnd, + index, + LVNI_SELECTED + )) != -1) + { + if (PhGetListViewItemParam(hWnd, index, ¶m)) + PhAddItemArray(&array, ¶m); + } + + *NumberOfItems = (ULONG)array.Count; + *Items = PhFinalArrayItems(&array); +} + +VOID PhSetImageListBitmap( + _In_ HIMAGELIST ImageList, + _In_ INT Index, + _In_ HINSTANCE InstanceHandle, + _In_ LPCWSTR BitmapName + ) +{ + HBITMAP bitmap; + + bitmap = LoadImage(InstanceHandle, BitmapName, IMAGE_BITMAP, 0, 0, 0); + + if (bitmap) + { + ImageList_Replace(ImageList, Index, bitmap, NULL); + DeleteBitmap(bitmap); + } +} + +static BOOLEAN SharedIconCacheHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPHP_ICON_ENTRY entry1 = Entry1; + PPHP_ICON_ENTRY entry2 = Entry2; + + if (entry1->InstanceHandle != entry2->InstanceHandle || + entry1->Width != entry2->Width || + entry1->Height != entry2->Height) + { + return FALSE; + } + + if (IS_INTRESOURCE(entry1->Name)) + { + if (IS_INTRESOURCE(entry2->Name)) + return PtrToUlong(entry1->Name) == PtrToUlong(entry2->Name); + else + return FALSE; + } + else + { + if (!IS_INTRESOURCE(entry2->Name)) + return PhEqualStringZ(entry1->Name, entry2->Name, FALSE); + else + return FALSE; + } +} + +static ULONG SharedIconCacheHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPHP_ICON_ENTRY entry = Entry; + ULONG nameHash; + + if (IS_INTRESOURCE(entry->Name)) + nameHash = PtrToUlong(entry->Name); + else + nameHash = PhHashBytes((PUCHAR)entry->Name, PhCountStringZ(entry->Name)); + + return nameHash ^ (PtrToUlong(entry->InstanceHandle) >> 5) ^ (entry->Width << 3) ^ entry->Height; +} + +HICON PhLoadIcon( + _In_opt_ HINSTANCE InstanceHandle, + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_opt_ ULONG Width, + _In_opt_ ULONG Height + ) +{ + PHP_ICON_ENTRY entry; + PPHP_ICON_ENTRY actualEntry; + HICON icon = NULL; + + if (PhBeginInitOnce(&SharedIconCacheInitOnce)) + { + SharedIconCacheHashtable = PhCreateHashtable(sizeof(PHP_ICON_ENTRY), + SharedIconCacheHashtableEqualFunction, SharedIconCacheHashtableHashFunction, 10); + PhEndInitOnce(&SharedIconCacheInitOnce); + } + + if (Flags & PH_LOAD_ICON_SHARED) + { + PhAcquireQueuedLockExclusive(&SharedIconCacheLock); + + entry.InstanceHandle = InstanceHandle; + entry.Name = Name; + entry.Width = PhpGetIconEntrySize(Width, Flags); + entry.Height = PhpGetIconEntrySize(Height, Flags); + actualEntry = PhFindEntryHashtable(SharedIconCacheHashtable, &entry); + + if (actualEntry) + { + icon = actualEntry->Icon; + PhReleaseQueuedLockExclusive(&SharedIconCacheLock); + return icon; + } + } + + if (Flags & (PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_SIZE_LARGE)) + { + LoadIconMetric(InstanceHandle, Name, (Flags & PH_LOAD_ICON_SIZE_SMALL) ? LIM_SMALL : LIM_LARGE, &icon); + } + else + { + LoadIconWithScaleDown(InstanceHandle, Name, Width, Height, &icon); + } + + if (!icon && !(Flags & PH_LOAD_ICON_STRICT)) + { + if (Flags & PH_LOAD_ICON_SIZE_SMALL) + { + static ULONG smallWidth = 0; + static ULONG smallHeight = 0; + + if (!smallWidth) + smallWidth = GetSystemMetrics(SM_CXSMICON); + if (!smallHeight) + smallHeight = GetSystemMetrics(SM_CYSMICON); + + Width = smallWidth; + Height = smallHeight; + } + else if (Flags & PH_LOAD_ICON_SIZE_LARGE) + { + static ULONG largeWidth = 0; + static ULONG largeHeight = 0; + + if (!largeWidth) + largeWidth = GetSystemMetrics(SM_CXICON); + if (!largeHeight) + largeHeight = GetSystemMetrics(SM_CYICON); + + Width = largeWidth; + Height = largeHeight; + } + + icon = LoadImage(InstanceHandle, Name, IMAGE_ICON, Width, Height, 0); + } + + if (Flags & PH_LOAD_ICON_SHARED) + { + if (icon) + { + if (!IS_INTRESOURCE(Name)) + entry.Name = PhDuplicateStringZ(Name); + entry.Icon = icon; + PhAddEntryHashtable(SharedIconCacheHashtable, &entry); + } + + PhReleaseQueuedLockExclusive(&SharedIconCacheLock); + } + + return icon; +} + +/** + * Gets the default icon used for executable files. + * + * \param SmallIcon A variable which receives the small default executable icon. Do not destroy the + * icon using DestroyIcon(); it is shared between callers. + * \param LargeIcon A variable which receives the large default executable icon. Do not destroy the + * icon using DestroyIcon(); it is shared between callers. + */ +VOID PhGetStockApplicationIcon( + _Out_opt_ HICON *SmallIcon, + _Out_opt_ HICON *LargeIcon + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static HICON smallIcon = NULL; + static HICON largeIcon = NULL; + + // This no longer uses SHGetFileInfo because it is *very* slow and causes many other DLLs to be + // loaded, increasing memory usage. The worst thing about it, however, is that it is horribly + // incompatible with multi-threading. The first time it is called, it tries to perform some + // one-time initialization. It guards this with a lock, but when multiple threads try to call + // the function at the same time, instead of waiting for initialization to finish it simply + // fails the other threads. + + if (PhBeginInitOnce(&initOnce)) + { + if (WindowsVersion < WINDOWS_10) + { + PPH_STRING systemDirectory; + PPH_STRING dllFileName; + + // imageres,11 (Windows 10 and above), user32,0 (Vista and above) or shell32,2 (XP) contains + // the default application icon. + + if (systemDirectory = PhGetSystemDirectory()) + { + dllFileName = PhConcatStringRefZ(&systemDirectory->sr, L"\\user32.dll"); + + PhExtractIcon( + dllFileName->Buffer, + &largeIcon, + &smallIcon + ); + + PhDereferenceObject(dllFileName); + PhDereferenceObject(systemDirectory); + } + } + + // Fallback icons + if (!smallIcon) + smallIcon = PhLoadIcon(NULL, IDI_APPLICATION, PH_LOAD_ICON_SIZE_SMALL, 0, 0); + if (!largeIcon) + largeIcon = PhLoadIcon(NULL, IDI_APPLICATION, PH_LOAD_ICON_SIZE_LARGE, 0, 0); + + PhEndInitOnce(&initOnce); + } + + if (SmallIcon) + *SmallIcon = smallIcon; + if (LargeIcon) + *LargeIcon = largeIcon; +} + +HICON PhGetFileShellIcon( + _In_opt_ PWSTR FileName, + _In_opt_ PWSTR DefaultExtension, + _In_ BOOLEAN LargeIcon + ) +{ + SHFILEINFO fileInfo; + ULONG iconFlag; + HICON icon; + + if (DefaultExtension && PhEqualStringZ(DefaultExtension, L".exe", TRUE)) + { + // Special case for executable files (see above for reasoning). + + icon = NULL; + + if (FileName) + { + PhExtractIcon( + FileName, + LargeIcon ? &icon : NULL, + !LargeIcon ? &icon : NULL + ); + } + + if (!icon) + { + PhGetStockApplicationIcon( + !LargeIcon ? &icon : NULL, + LargeIcon ? &icon : NULL + ); + + if (icon) + icon = CopyIcon(icon); + } + + return icon; + } + + iconFlag = LargeIcon ? SHGFI_LARGEICON : SHGFI_SMALLICON; + icon = NULL; + + if (FileName && SHGetFileInfo( + FileName, + 0, + &fileInfo, + sizeof(SHFILEINFO), + SHGFI_ICON | iconFlag + )) + { + icon = fileInfo.hIcon; + } + + if (!icon && DefaultExtension) + { + if (SHGetFileInfo( + DefaultExtension, + FILE_ATTRIBUTE_NORMAL, + &fileInfo, + sizeof(SHFILEINFO), + SHGFI_ICON | iconFlag | SHGFI_USEFILEATTRIBUTES + )) + icon = fileInfo.hIcon; + } + + return icon; +} + +VOID PhpSetClipboardData( + _In_ HWND hWnd, + _In_ ULONG Format, + _In_ HANDLE Data + ) +{ + if (OpenClipboard(hWnd)) + { + if (!EmptyClipboard()) + goto Fail; + + if (!SetClipboardData(Format, Data)) + goto Fail; + + CloseClipboard(); + + return; + } + +Fail: + GlobalFree(Data); +} + +VOID PhSetClipboardString( + _In_ HWND hWnd, + _In_ PPH_STRINGREF String + ) +{ + HANDLE data; + PVOID memory; + + data = GlobalAlloc(GMEM_MOVEABLE, String->Length + sizeof(UNICODE_NULL)); + memory = GlobalLock(data); + + memcpy(memory, String->Buffer, String->Length); + *(PWCHAR)PTR_ADD_OFFSET(memory, String->Length) = UNICODE_NULL; + + GlobalUnlock(memory); + + PhpSetClipboardData(hWnd, CF_UNICODETEXT, data); +} + +HWND PhCreateDialogFromTemplate( + _In_ HWND Parent, + _In_ ULONG Style, + _In_ PVOID Instance, + _In_ PWSTR Template, + _In_ DLGPROC DialogProc, + _In_ PVOID Parameter + ) +{ + PDLGTEMPLATEEX dialogTemplate; + HWND dialogHandle; + + if (!PhLoadResource(Instance, Template, RT_DIALOG, NULL, &dialogTemplate)) + return NULL; + + if (dialogTemplate->signature == USHRT_MAX) + { + dialogTemplate->style = Style; + } + else + { + ((DLGTEMPLATE *)dialogTemplate)->style = Style; + } + + dialogHandle = CreateDialogIndirectParam( + Instance, + (DLGTEMPLATE *)dialogTemplate, + Parent, + DialogProc, + (LPARAM)Parameter + ); + + PhFree(dialogTemplate); + + return dialogHandle; +} + +BOOLEAN PhModalPropertySheet( + _Inout_ PROPSHEETHEADER *Header + ) +{ + // PropertySheet incorrectly discards WM_QUIT messages in certain cases, so we will use our own + // message loop. An example of this is when GetMessage (called by PropertySheet's message loop) + // dispatches a message directly from kernel-mode that causes the property sheet to close. In + // that case PropertySheet will retrieve the WM_QUIT message but will ignore it because of its + // buggy logic. + + // This is also a good opportunity to introduce an auto-pool. + + PH_AUTO_POOL autoPool; + HWND oldFocus; + HWND topLevelOwner; + HWND hwnd; + BOOL result; + MSG message; + + PhInitializeAutoPool(&autoPool); + + oldFocus = GetFocus(); + topLevelOwner = Header->hwndParent; + + while (topLevelOwner && (GetWindowLongPtr(topLevelOwner, GWL_STYLE) & WS_CHILD)) + topLevelOwner = GetParent(topLevelOwner); + + if (topLevelOwner && (topLevelOwner == GetDesktopWindow() || EnableWindow(topLevelOwner, FALSE))) + topLevelOwner = NULL; + + Header->dwFlags |= PSH_MODELESS; + hwnd = (HWND)PropertySheet(Header); + + if (!hwnd) + { + if (topLevelOwner) + EnableWindow(topLevelOwner, TRUE); + + return FALSE; + } + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!PropSheet_IsDialogMessage(hwnd, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + + // Destroy the window when necessary. + if (!PropSheet_GetCurrentPageHwnd(hwnd)) + break; + } + + if (result == 0) + PostQuitMessage((INT)message.wParam); + if (Header->hwndParent && GetActiveWindow() == hwnd) + SetActiveWindow(Header->hwndParent); + if (topLevelOwner) + EnableWindow(topLevelOwner, TRUE); + if (oldFocus && IsWindow(oldFocus)) + SetFocus(oldFocus); + + DestroyWindow(hwnd); + PhDeleteAutoPool(&autoPool); + + return TRUE; +} + +VOID PhInitializeLayoutManager( + _Out_ PPH_LAYOUT_MANAGER Manager, + _In_ HWND RootWindowHandle + ) +{ + Manager->List = PhCreateList(4); + Manager->LayoutNumber = 0; + + Manager->RootItem.Handle = RootWindowHandle; + GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect); + Manager->RootItem.OrigRect = Manager->RootItem.Rect; + Manager->RootItem.ParentItem = NULL; + Manager->RootItem.LayoutParentItem = NULL; + Manager->RootItem.LayoutNumber = 0; + Manager->RootItem.NumberOfChildren = 0; + Manager->RootItem.DeferHandle = NULL; +} + +VOID PhDeleteLayoutManager( + _Inout_ PPH_LAYOUT_MANAGER Manager + ) +{ + ULONG i; + + for (i = 0; i < Manager->List->Count; i++) + PhFree(Manager->List->Items[i]); + + PhDereferenceObject(Manager->List); +} + +// HACK: The math below is all horribly broken, especially the HACK for multiline tab controls. + +PPH_LAYOUT_ITEM PhAddLayoutItem( + _Inout_ PPH_LAYOUT_MANAGER Manager, + _In_ HWND Handle, + _In_opt_ PPH_LAYOUT_ITEM ParentItem, + _In_ ULONG Anchor + ) +{ + PPH_LAYOUT_ITEM layoutItem; + RECT dummy = { 0 }; + + layoutItem = PhAddLayoutItemEx( + Manager, + Handle, + ParentItem, + Anchor, + dummy + ); + + layoutItem->Margin = layoutItem->Rect; + PhConvertRect(&layoutItem->Margin, &layoutItem->ParentItem->Rect); + + if (layoutItem->ParentItem != layoutItem->LayoutParentItem) + { + // Fix the margin because the item has a dummy parent. They share the same layout parent + // item. + layoutItem->Margin.top -= layoutItem->ParentItem->Rect.top; + layoutItem->Margin.left -= layoutItem->ParentItem->Rect.left; + layoutItem->Margin.right = layoutItem->ParentItem->Margin.right; + layoutItem->Margin.bottom = layoutItem->ParentItem->Margin.bottom; + } + + return layoutItem; +} + +PPH_LAYOUT_ITEM PhAddLayoutItemEx( + _Inout_ PPH_LAYOUT_MANAGER Manager, + _In_ HWND Handle, + _In_opt_ PPH_LAYOUT_ITEM ParentItem, + _In_ ULONG Anchor, + _In_ RECT Margin + ) +{ + PPH_LAYOUT_ITEM item; + + if (!ParentItem) + ParentItem = &Manager->RootItem; + + item = PhAllocate(sizeof(PH_LAYOUT_ITEM)); + item->Handle = Handle; + item->ParentItem = ParentItem; + item->LayoutNumber = Manager->LayoutNumber; + item->NumberOfChildren = 0; + item->DeferHandle = NULL; + item->Anchor = Anchor; + + item->LayoutParentItem = item->ParentItem; + + while ((item->LayoutParentItem->Anchor & PH_LAYOUT_DUMMY_MASK) && + item->LayoutParentItem->LayoutParentItem) + { + item->LayoutParentItem = item->LayoutParentItem->LayoutParentItem; + } + + item->LayoutParentItem->NumberOfChildren++; + + GetWindowRect(Handle, &item->Rect); + MapWindowPoints(NULL, item->LayoutParentItem->Handle, (POINT *)&item->Rect, 2); + + if (item->Anchor & PH_LAYOUT_TAB_CONTROL) + { + // We want to convert the tab control rectangle to the tab page display rectangle. + TabCtrl_AdjustRect(Handle, FALSE, &item->Rect); + } + + item->Margin = Margin; + + item->OrigRect = item->Rect; + + PhAddItemList(Manager->List, item); + + return item; +} + +VOID PhpLayoutItemLayout( + _Inout_ PPH_LAYOUT_MANAGER Manager, + _Inout_ PPH_LAYOUT_ITEM Item + ) +{ + RECT rect; + BOOLEAN hasDummyParent; + + if (Item->NumberOfChildren > 0 && !Item->DeferHandle) + Item->DeferHandle = BeginDeferWindowPos(Item->NumberOfChildren); + + if (Item->LayoutNumber == Manager->LayoutNumber) + return; + + // If this is the root item we must stop here. + if (!Item->ParentItem) + return; + + PhpLayoutItemLayout(Manager, Item->ParentItem); + + if (Item->ParentItem != Item->LayoutParentItem) + { + PhpLayoutItemLayout(Manager, Item->LayoutParentItem); + hasDummyParent = TRUE; + } + else + { + hasDummyParent = FALSE; + } + + GetWindowRect(Item->Handle, &Item->Rect); + MapWindowPoints(NULL, Item->LayoutParentItem->Handle, (POINT *)&Item->Rect, 2); + + if (Item->Anchor & PH_LAYOUT_TAB_CONTROL) + { + // We want to convert the tab control rectangle to the tab page display rectangle. + TabCtrl_AdjustRect(Item->Handle, FALSE, &Item->Rect); + } + + if (!(Item->Anchor & PH_LAYOUT_DUMMY_MASK)) + { + // Convert right/bottom into margins to make the calculations + // easier. + rect = Item->Rect; + PhConvertRect(&rect, &Item->LayoutParentItem->Rect); + + if (!(Item->Anchor & (PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT))) + { + // TODO + PhRaiseStatus(STATUS_NOT_IMPLEMENTED); + } + else if (Item->Anchor & PH_ANCHOR_RIGHT) + { + if (Item->Anchor & PH_ANCHOR_LEFT) + { + rect.left = (hasDummyParent ? Item->ParentItem->Rect.left : 0) + Item->Margin.left; + rect.right = Item->Margin.right; + } + else + { + ULONG diff = Item->Margin.right - rect.right; + + rect.left -= diff; + rect.right += diff; + } + } + + if (!(Item->Anchor & (PH_ANCHOR_TOP | PH_ANCHOR_BOTTOM))) + { + // TODO + PhRaiseStatus(STATUS_NOT_IMPLEMENTED); + } + else if (Item->Anchor & PH_ANCHOR_BOTTOM) + { + if (Item->Anchor & PH_ANCHOR_TOP) + { + // tab control hack + rect.top = (hasDummyParent ? Item->ParentItem->Rect.top : 0) + Item->Margin.top; + rect.bottom = Item->Margin.bottom; + } + else + { + ULONG diff = Item->Margin.bottom - rect.bottom; + + rect.top -= diff; + rect.bottom += diff; + } + } + + // Convert the right/bottom back into co-ordinates. + PhConvertRect(&rect, &Item->LayoutParentItem->Rect); + Item->Rect = rect; + + if (!(Item->Anchor & PH_LAYOUT_IMMEDIATE_RESIZE)) + { + Item->LayoutParentItem->DeferHandle = DeferWindowPos( + Item->LayoutParentItem->DeferHandle, Item->Handle, + NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER + ); + } + else + { + // This is needed for tab controls, so that TabCtrl_AdjustRect will give us an + // up-to-date result. + SetWindowPos( + Item->Handle, + NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER + ); + } + } + + Item->LayoutNumber = Manager->LayoutNumber; +} + +VOID PhLayoutManagerLayout( + _Inout_ PPH_LAYOUT_MANAGER Manager + ) +{ + ULONG i; + + Manager->LayoutNumber++; + + GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect); + + for (i = 0; i < Manager->List->Count; i++) + { + PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i]; + + PhpLayoutItemLayout(Manager, item); + } + + for (i = 0; i < Manager->List->Count; i++) + { + PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i]; + + if (item->DeferHandle) + { + EndDeferWindowPos(item->DeferHandle); + item->DeferHandle = NULL; + } + + if (item->Anchor & PH_LAYOUT_FORCE_INVALIDATE) + { + InvalidateRect(item->Handle, NULL, FALSE); + } + } + + if (Manager->RootItem.DeferHandle) + { + EndDeferWindowPos(Manager->RootItem.DeferHandle); + Manager->RootItem.DeferHandle = NULL; + } +} + +static BOOLEAN NTAPI PhpWindowContextHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_WINDOW_PROPERTY_CONTEXT entry1 = Entry1; + PPH_WINDOW_PROPERTY_CONTEXT entry2 = Entry2; + + return + entry1->WindowHandle == entry2->WindowHandle && + entry1->PropertyHash == entry2->PropertyHash; +} + +static ULONG NTAPI PhpWindowContextHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_WINDOW_PROPERTY_CONTEXT entry = Entry; + + return PhHashIntPtr((ULONG_PTR)entry->WindowHandle) ^ PhHashInt32(entry->PropertyHash); +} + +PVOID PhGetWindowContext( + _In_ HWND WindowHandle, + _In_ ULONG PropertyHash + ) +{ + PH_WINDOW_PROPERTY_CONTEXT lookupEntry; + PPH_WINDOW_PROPERTY_CONTEXT entry; + + lookupEntry.WindowHandle = WindowHandle; + lookupEntry.PropertyHash = PropertyHash; + + PhAcquireQueuedLockShared(&WindowContextListLock); + entry = PhFindEntryHashtable(WindowContextHashTable, &lookupEntry); + PhReleaseQueuedLockShared(&WindowContextListLock); + + if (entry) + return entry->Context; + else + return NULL; +} + +VOID PhSetWindowContext( + _In_ HWND WindowHandle, + _In_ ULONG PropertyHash, + _In_ PVOID Context + ) +{ + PH_WINDOW_PROPERTY_CONTEXT entry; + + entry.WindowHandle = WindowHandle; + entry.PropertyHash = PropertyHash; + entry.Context = Context; + + PhAcquireQueuedLockExclusive(&WindowContextListLock); + PhAddEntryHashtable(WindowContextHashTable, &entry); + PhReleaseQueuedLockExclusive(&WindowContextListLock); +} + +VOID PhRemoveWindowContext( + _In_ HWND WindowHandle, + _In_ ULONG PropertyHash + ) +{ + PH_WINDOW_PROPERTY_CONTEXT lookupEntry; + + lookupEntry.WindowHandle = WindowHandle; + lookupEntry.PropertyHash = PropertyHash; + + PhAcquireQueuedLockExclusive(&WindowContextListLock); + PhRemoveEntryHashtable(WindowContextHashTable, &lookupEntry); + PhReleaseQueuedLockExclusive(&WindowContextListLock); +} + +VOID PhEnumWindows( + _In_ PH_ENUM_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + EnumWindows((WNDENUMPROC)Callback, (LPARAM)Context); +} + +VOID PhEnumChildWindows( + _In_opt_ HWND WindowHandle, + _In_ ULONG Limit, + _In_ PH_CHILD_ENUM_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + HWND childWindow = NULL; + ULONG i = 0; + + while (i < Limit && (childWindow = FindWindowEx(WindowHandle, childWindow, NULL, NULL))) + { + if (!Callback(childWindow, Context)) + return; + + i++; + } +} + +typedef struct _GET_PROCESS_MAIN_WINDOW_CONTEXT +{ + HWND Window; + HWND ImmersiveWindow; + HANDLE ProcessId; + BOOLEAN IsImmersive; + BOOLEAN SkipInvisible; +} GET_PROCESS_MAIN_WINDOW_CONTEXT, *PGET_PROCESS_MAIN_WINDOW_CONTEXT; + +BOOL CALLBACK PhpGetProcessMainWindowEnumWindowsProc( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ) +{ + PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)Context; + ULONG processId; + WINDOWINFO windowInfo; + + if (context->SkipInvisible && !IsWindowVisible(WindowHandle)) + return TRUE; + + GetWindowThreadProcessId(WindowHandle, &processId); + + //if (UlongToHandle(processId) == context->ProcessId && (context->SkipInvisible ? + // !((parentWindow = GetParent(WindowHandle)) && IsWindowVisible(parentWindow)) && // skip windows with a visible parent + // PhGetWindowTextEx(WindowHandle, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0 : TRUE)) // skip windows with no title + + if (UlongToHandle(processId) == context->ProcessId) + { + if (!context->ImmersiveWindow && context->IsImmersive && + GetProp(WindowHandle, L"Windows.ImmersiveShell.IdentifyAsMainCoreWindow")) + { + context->ImmersiveWindow = WindowHandle; + } + + windowInfo.cbSize = sizeof(WINDOWINFO); + + if (!context->Window && GetWindowInfo(WindowHandle, &windowInfo) && (windowInfo.dwStyle & WS_DLGFRAME)) + { + context->Window = WindowHandle; + + // If we're not looking at an immersive process, there's no need to search any more windows. + if (!context->IsImmersive) + return FALSE; + } + } + + return TRUE; +} + +HWND PhGetProcessMainWindow( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle + ) +{ + return PhGetProcessMainWindowEx(ProcessId, ProcessHandle, TRUE); +} + +HWND PhGetProcessMainWindowEx( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle, + _In_ BOOLEAN SkipInvisible + ) +{ + GET_PROCESS_MAIN_WINDOW_CONTEXT context; + HANDLE processHandle = NULL; + + memset(&context, 0, sizeof(GET_PROCESS_MAIN_WINDOW_CONTEXT)); + context.ProcessId = ProcessId; + context.SkipInvisible = SkipInvisible; + + if (ProcessHandle) + processHandle = ProcessHandle; + else + PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, ProcessId); + + if (processHandle && WINDOWS_HAS_IMMERSIVE && IsImmersiveProcess) + context.IsImmersive = IsImmersiveProcess(processHandle); + + PhEnumWindows(PhpGetProcessMainWindowEnumWindowsProc, &context); + //PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, &context); + + if (!ProcessHandle && processHandle) + NtClose(processHandle); + + return context.ImmersiveWindow ? context.ImmersiveWindow : context.Window; +} + +ULONG PhGetDialogItemValue( + _In_ HWND WindowHandle, + _In_ INT ControlID + ) +{ + ULONG64 controlValue = 0; + HWND controlHandle; + PPH_STRING controlText; + + if (controlHandle = GetDlgItem(WindowHandle, ControlID)) + { + if (controlText = PhGetWindowText(controlHandle)) + { + PhStringToInteger64(&controlText->sr, 10, &controlValue); + PhDereferenceObject(controlText); + } + } + + return (ULONG)controlValue; +} + +VOID PhSetDialogItemValue( + _In_ HWND WindowHandle, + _In_ INT ControlID, + _In_ ULONG Value, + _In_ BOOLEAN Signed + ) +{ + HWND controlHandle; + WCHAR valueString[PH_INT32_STR_LEN_1]; + + if (Signed) + PhPrintInt32(valueString, (LONG)Value); + else + PhPrintUInt32(valueString, Value); + + if (controlHandle = GetDlgItem(WindowHandle, ControlID)) + { + PhSetWindowText(controlHandle, valueString); + } +} + +VOID PhSetDialogItemText( + _In_ HWND WindowHandle, + _In_ INT ControlID, + _In_ PCWSTR WindowText + ) +{ + HWND controlHandle; + + if (controlHandle = GetDlgItem(WindowHandle, ControlID)) + { + PhSetWindowText(controlHandle, WindowText); + } +} + +VOID PhSetWindowText( + _In_ HWND WindowHandle, + _In_ PCWSTR WindowText + ) +{ + SendMessage(WindowHandle, WM_SETTEXT, 0, (LPARAM)WindowText); // TODO: DefWindowProc (dmex) +} + +VOID PhSetWindowAlwaysOnTop( + _In_ HWND WindowHandle, + _In_ BOOLEAN AlwaysOnTop + ) +{ + SetFocus(WindowHandle); // HACK - SetWindowPos doesn't work properly without this (wj32) + SetWindowPos( + WindowHandle, + AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE + ); +} + +static BOOLEAN NTAPI PhpWindowCallbackHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + return + (*(PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION *)Entry1)->WindowHandle == + (*(PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION *)Entry2)->WindowHandle; +} + +static ULONG NTAPI PhpWindowCallbackHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION *)Entry)->WindowHandle); +} + +VOID PhRegisterWindowCallback( + _In_ HWND WindowHandle, + _In_ PH_PLUGIN_WINDOW_EVENT_TYPE Type, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION entry; + + entry = PhAllocate(sizeof(PH_PLUGIN_WINDOW_CALLBACK_REGISTRATION)); + entry->WindowHandle = WindowHandle; + entry->Type = Type; + + switch (Type) // HACK + { + case PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST: + if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) + PhSetWindowAlwaysOnTop(WindowHandle, TRUE); + break; + } + + PhAcquireQueuedLockExclusive(&WindowCallbackListLock); + PhAddEntryHashtable(WindowCallbackHashTable, &entry); + PhReleaseQueuedLockExclusive(&WindowCallbackListLock); +} + +VOID PhUnregisterWindowCallback( + _In_ HWND WindowHandle + ) +{ + PH_PLUGIN_WINDOW_CALLBACK_REGISTRATION lookupEntry; + PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION lookupEntryPtr = &lookupEntry; + PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION *entryPtr; + PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION entry; + + lookupEntry.WindowHandle = WindowHandle; + + PhAcquireQueuedLockExclusive(&WindowCallbackListLock); + + entryPtr = (PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION*)PhFindEntryHashtable( + WindowCallbackHashTable, + &lookupEntryPtr + ); + + assert(entryPtr); + + if (entryPtr && PhRemoveEntryHashtable(WindowCallbackHashTable, entryPtr)) + { + entry = *entryPtr; + PhFree(entry); + } + + PhReleaseQueuedLockExclusive(&WindowCallbackListLock); +} + +VOID PhWindowNotifyTopMostEvent( + _In_ BOOLEAN TopMost + ) +{ + PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION *entry; + ULONG i = 0; + + PhAcquireQueuedLockExclusive(&WindowCallbackListLock); + + while (PhEnumHashtable(WindowCallbackHashTable, (PVOID*)&entry, &i)) + { + if ((*entry)->Type & PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST) + { + PhSetWindowAlwaysOnTop((*entry)->WindowHandle, TopMost); + } + } + + PhReleaseQueuedLockExclusive(&WindowCallbackListLock); +} + +HICON PhGetInternalWindowIcon( + _In_ HWND WindowHandle, + _In_ UINT Type + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static HICON (WINAPI *InternalGetWindowIcon_I)( + _In_ HWND WindowHandle, + _In_ ULONG Type + ) = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + PVOID shell32Handle; + + if (shell32Handle = LoadLibrary(L"shell32.dll")) + { + InternalGetWindowIcon_I = PhGetDllBaseProcedureAddress(shell32Handle, "InternalGetWindowIcon", 0); + } + + PhEndInitOnce(&initOnce); + } + + if (!InternalGetWindowIcon_I) + return NULL; + + return InternalGetWindowIcon_I(WindowHandle, Type); +} + +HANDLE PhGetGlobalTimerQueue( + VOID + ) +{ + static HANDLE PhTimerQueueHandle = NULL; + static PH_INITONCE PhTimerQueueHandleInitOnce = PH_INITONCE_INIT; + + if (PhBeginInitOnce(&PhTimerQueueHandleInitOnce)) + { + RtlCreateTimerQueue(&PhTimerQueueHandle); + + PhEndInitOnce(&PhTimerQueueHandleInitOnce); + } + + return PhTimerQueueHandle; +} diff --git a/phlib/handle.c b/phlib/handle.c index d3708b25b6f7..6f7d5fe505a3 100644 --- a/phlib/handle.c +++ b/phlib/handle.c @@ -1,1097 +1,1097 @@ -/* - * Process Hacker - - * handle table - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include - -static PH_INITONCE PhHandleTableInitOnce = PH_INITONCE_INIT; -static PH_FREE_LIST PhHandleTableLevel0FreeList; -static PH_FREE_LIST PhHandleTableLevel1FreeList; - -PPH_HANDLE_TABLE PhCreateHandleTable( - VOID - ) -{ - PPH_HANDLE_TABLE handleTable; - ULONG i; - - if (PhBeginInitOnce(&PhHandleTableInitOnce)) - { - PhInitializeFreeList( - &PhHandleTableLevel0FreeList, - sizeof(PH_HANDLE_TABLE_ENTRY) * PH_HANDLE_TABLE_LEVEL_ENTRIES, - 64 - ); - PhInitializeFreeList( - &PhHandleTableLevel1FreeList, - sizeof(PPH_HANDLE_TABLE_ENTRY) * PH_HANDLE_TABLE_LEVEL_ENTRIES, - 64 - ); - PhEndInitOnce(&PhHandleTableInitOnce); - } - -#ifdef PH_HANDLE_TABLE_SAFE - handleTable = PhAllocateSafe(sizeof(PH_HANDLE_TABLE)); - - if (!handleTable) - return NULL; -#else - handleTable = PhAllocate(sizeof(PH_HANDLE_TABLE)); -#endif - - PhInitializeQueuedLock(&handleTable->Lock); - PhInitializeWakeEvent(&handleTable->HandleWakeEvent); - - handleTable->NextValue = 0; - - handleTable->Count = 0; - handleTable->TableValue = (ULONG_PTR)PhpCreateHandleTableLevel0(handleTable, TRUE); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!handleTable->TableValue) - { - PhFree(handleTable); - return NULL; - } -#endif - - // We have now created the level 0 table. The free list can now be set up to point to handle 0, - // which points to the rest of the free list (1 -> 2 -> 3 -> ...). The next batch of handles - // that need to be created start at PH_HANDLE_TABLE_LEVEL_ENTRIES. - - handleTable->FreeValue = 0; - handleTable->NextValue = PH_HANDLE_TABLE_LEVEL_ENTRIES; - - handleTable->FreeValueAlt = PH_HANDLE_VALUE_INVALID; // no entries in alt. free list - - handleTable->Flags = 0; - - for (i = 0; i < PH_HANDLE_TABLE_LOCKS; i++) - PhInitializeQueuedLock(&handleTable->Locks[i]); - - return handleTable; -} - -VOID PhDestroyHandleTable( - _In_ _Post_invalid_ PPH_HANDLE_TABLE HandleTable - ) -{ - ULONG_PTR tableValue; - ULONG tableLevel; - PPH_HANDLE_TABLE_ENTRY table0; - PPH_HANDLE_TABLE_ENTRY *table1; - PPH_HANDLE_TABLE_ENTRY **table2; - ULONG i; - ULONG j; - - tableValue = HandleTable->TableValue; - tableLevel = tableValue & PH_HANDLE_TABLE_LEVEL_MASK; - tableValue -= tableLevel; - - switch (tableLevel) - { - case 0: - { - table0 = (PPH_HANDLE_TABLE_ENTRY)tableValue; - - PhpFreeHandleTableLevel0(table0); - } - break; - case 1: - { - table1 = (PPH_HANDLE_TABLE_ENTRY *)tableValue; - - for (i = 0; i < PH_HANDLE_TABLE_LEVEL_ENTRIES; i++) - { - if (!table1[i]) - break; - - PhpFreeHandleTableLevel0(table1[i]); - } - - PhpFreeHandleTableLevel1(table1); - } - break; - case 2: - { - table2 = (PPH_HANDLE_TABLE_ENTRY **)tableValue; - - for (i = 0; i < PH_HANDLE_TABLE_LEVEL_ENTRIES; i++) - { - if (!table2[i]) - break; - - for (j = 0; j < PH_HANDLE_TABLE_LEVEL_ENTRIES; j++) - { - if (!table2[i][j]) - break; - - PhpFreeHandleTableLevel0(table2[i][j]); - } - - PhpFreeHandleTableLevel1(table2[i]); - } - - PhpFreeHandleTableLevel2(table2); - } - break; - default: - ASSUME_NO_DEFAULT; - } - - PhFree(HandleTable); -} - -VOID PhpBlockOnLockedHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - PH_QUEUED_WAIT_BLOCK waitBlock; - ULONG_PTR value; - - PhQueueWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock); - - value = HandleTableEntry->Value; - - if ( - (value & PH_HANDLE_TABLE_ENTRY_TYPE) != PH_HANDLE_TABLE_ENTRY_IN_USE || - (value & PH_HANDLE_TABLE_ENTRY_LOCKED) - ) - { - // Entry is not in use or has been unlocked; cancel the wait. - PhSetWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock); - } - else - { - PhWaitForWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock, TRUE, NULL); - } -} - -BOOLEAN PhLockHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - ULONG_PTR value; - - while (TRUE) - { - value = HandleTableEntry->Value; - - if ((value & PH_HANDLE_TABLE_ENTRY_TYPE) != PH_HANDLE_TABLE_ENTRY_IN_USE) - return FALSE; - - if (value & PH_HANDLE_TABLE_ENTRY_LOCKED) - { - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&HandleTableEntry->Value, - (PVOID)(value - PH_HANDLE_TABLE_ENTRY_LOCKED), - (PVOID)value - ) == value) - { - return TRUE; - } - } - - PhpBlockOnLockedHandleTableEntry(HandleTable, HandleTableEntry); - } -} - -VOID PhUnlockHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - _interlockedbittestandset( - (PLONG)&HandleTableEntry->Value, - PH_HANDLE_TABLE_ENTRY_LOCKED_SHIFT - ); - PhSetWakeEvent(&HandleTable->HandleWakeEvent, NULL); -} - -HANDLE PhCreateHandle( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - PPH_HANDLE_TABLE_ENTRY entry; - ULONG handleValue; - - entry = PhpAllocateHandleTableEntry(HandleTable, &handleValue); - - if (!entry) - return NULL; - - // Copy the given handle table entry to the allocated entry. - - // All free entries have the Free type and have the (not) locked bit clear. There is no problem - // with setting the Type now; the entry is still locked, so they will block. - entry->TypeAndValue.Type = PH_HANDLE_TABLE_ENTRY_IN_USE; - entry->TypeAndValue.Value = HandleTableEntry->TypeAndValue.Value; - entry->Value2 = HandleTableEntry->Value2; - - // Now we unlock this entry, waking anyone who was caught back there before we had finished - // setting up the entry. - PhUnlockHandleTableEntry(HandleTable, entry); - - return PhpEncodeHandle(handleValue); -} - -BOOLEAN PhDestroyHandle( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ HANDLE Handle, - _In_opt_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - ULONG handleValue; - - handleValue = PhpDecodeHandle(Handle); - - if (!HandleTableEntry) - { - HandleTableEntry = PhpLookupHandleTableEntry(HandleTable, handleValue); - - if (!HandleTableEntry) - return FALSE; - - if (!PhLockHandleTableEntry(HandleTable, HandleTableEntry)) - return FALSE; - } - - _InterlockedExchangePointer( - (PVOID *)&HandleTableEntry->Value, - (PVOID)PH_HANDLE_TABLE_ENTRY_FREE - ); - - // The handle table entry is now free; wake any waiters because they can't lock the entry now. - // Any future lock attempts will fail because the entry is marked as being free. - PhSetWakeEvent(&HandleTable->HandleWakeEvent, NULL); - - PhpFreeHandleTableEntry(HandleTable, handleValue, HandleTableEntry); - - return TRUE; -} - -PPH_HANDLE_TABLE_ENTRY PhLookupHandleTableEntry( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ HANDLE Handle - ) -{ - PPH_HANDLE_TABLE_ENTRY entry; - - entry = PhpLookupHandleTableEntry(HandleTable, PhpDecodeHandle(Handle)); - - if (!entry) - return NULL; - - if (!PhLockHandleTableEntry(HandleTable, entry)) - return NULL; - - return entry; -} - -VOID PhEnumHandleTable( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ PPH_ENUM_HANDLE_TABLE_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - ULONG handleValue; - PPH_HANDLE_TABLE_ENTRY entry; - BOOLEAN cont; - - handleValue = 0; - - while (entry = PhpLookupHandleTableEntry(HandleTable, handleValue)) - { - if (PhLockHandleTableEntry(HandleTable, entry)) - { - cont = Callback( - HandleTable, - PhpEncodeHandle(handleValue), - entry, - Context - ); - PhUnlockHandleTableEntry(HandleTable, entry); - - if (!cont) - break; - } - - handleValue++; - } -} - -VOID PhSweepHandleTable( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ PPH_ENUM_HANDLE_TABLE_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - ULONG handleValue; - PPH_HANDLE_TABLE_ENTRY entry; - BOOLEAN cont; - - handleValue = 0; - - while (entry = PhpLookupHandleTableEntry(HandleTable, handleValue)) - { - if (entry->TypeAndValue.Type == PH_HANDLE_TABLE_ENTRY_IN_USE) - { - cont = Callback( - HandleTable, - PhpEncodeHandle(handleValue), - entry, - Context - ); - - if (!cont) - break; - } - - handleValue++; - } -} - -NTSTATUS PhQueryInformationHandleTable( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, - _Out_writes_bytes_opt_(BufferLength) PVOID Buffer, - _In_ ULONG BufferLength, - _Out_opt_ PULONG ReturnLength - ) -{ - NTSTATUS status = STATUS_SUCCESS; - ULONG returnLength; - - switch (InformationClass) - { - case HandleTableBasicInformation: - { - PPH_HANDLE_TABLE_BASIC_INFORMATION basicInfo = Buffer; - - if (BufferLength == sizeof(PH_HANDLE_TABLE_BASIC_INFORMATION)) - { - basicInfo->Count = HandleTable->Count; - basicInfo->Flags = HandleTable->Flags; - basicInfo->TableLevel = HandleTable->TableValue & PH_HANDLE_TABLE_LEVEL_MASK; - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - - returnLength = sizeof(PH_HANDLE_TABLE_BASIC_INFORMATION); - } - break; - case HandleTableFlagsInformation: - { - PPH_HANDLE_TABLE_FLAGS_INFORMATION flagsInfo = Buffer; - - if (BufferLength == sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION)) - { - flagsInfo->Flags = HandleTable->Flags; - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - - returnLength = sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION); - } - break; - default: - status = STATUS_INVALID_INFO_CLASS; - returnLength = 0; - break; - } - - if (ReturnLength) - *ReturnLength = returnLength; - - return status; -} - -NTSTATUS PhSetInformationHandleTable( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, - _In_reads_bytes_(BufferLength) PVOID Buffer, - _In_ ULONG BufferLength - ) -{ - NTSTATUS status = STATUS_SUCCESS; - - switch (InformationClass) - { - case HandleTableFlagsInformation: - { - PPH_HANDLE_TABLE_FLAGS_INFORMATION flagsInfo = Buffer; - ULONG flags; - - if (BufferLength == sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION)) - { - flags = flagsInfo->Flags; - - if ((flags & PH_HANDLE_TABLE_VALID_FLAGS) == flags) - HandleTable->Flags = flags; - else - status = STATUS_INVALID_PARAMETER; - } - else - { - status = STATUS_INFO_LENGTH_MISMATCH; - } - } - break; - default: - status = STATUS_INVALID_INFO_CLASS; - } - - return status; -} - -PPH_HANDLE_TABLE_ENTRY PhpAllocateHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _Out_ PULONG HandleValue - ) -{ - PPH_HANDLE_TABLE_ENTRY entry; - ULONG freeValue; - ULONG lockIndex; - ULONG nextFreeValue; - ULONG oldFreeValue; - BOOLEAN result; - - while (TRUE) - { - freeValue = HandleTable->FreeValue; - - while (freeValue == PH_HANDLE_VALUE_INVALID) - { - PhAcquireQueuedLockExclusive(&HandleTable->Lock); - - // Check again to see if we have free handles. - - freeValue = HandleTable->FreeValue; - - if (freeValue != PH_HANDLE_VALUE_INVALID) - { - PhReleaseQueuedLockExclusive(&HandleTable->Lock); - break; - } - - // Move handles from the alt. free list to the main free list, and check again. - - freeValue = PhpMoveFreeHandleTableEntries(HandleTable); - - if (freeValue != PH_HANDLE_VALUE_INVALID) - { - PhReleaseQueuedLockExclusive(&HandleTable->Lock); - break; - } - - result = PhpAllocateMoreHandleTableEntries(HandleTable, TRUE); - - PhReleaseQueuedLockExclusive(&HandleTable->Lock); - - freeValue = HandleTable->FreeValue; - - // Note that PhpAllocateMoreHandleTableEntries only returns FALSE if it failed to - // allocate memory. Success does not guarantee a free handle to be allocated, as they - // may have been all used up (however unlikely) when we reach this point. Success simply - // means to retry the allocation using the fast path. - - if (!result && freeValue == PH_HANDLE_VALUE_INVALID) - return NULL; - } - - entry = PhpLookupHandleTableEntry(HandleTable, freeValue); - lockIndex = PH_HANDLE_TABLE_LOCK_INDEX(freeValue); - - // To avoid the ABA problem, we would ideally have one queued lock per handle table entry. - // That would make the overhead too large, so instead there is a fixed number of locks, - // indexed by the handle value (mod no. locks). - - // Possibilities at this point: - // 1. freeValue != A (our copy), but the other thread has freed A, so FreeValue = A. No ABA - // problem since freeValue != A. - // 2. freeValue != A, and FreeValue != A. No ABA problem. - // 3. freeValue = A, and the other thread has freed A, so FreeValue = A. No ABA problem - // since we haven't read NextFreeValue yet. - // 4. freeValue = A, and FreeValue != A. No problem if this stays the same later, as the CAS - // will take care of it. - - PhpLockHandleTableShared(HandleTable, lockIndex); - - if (HandleTable->FreeValue != freeValue) - { - PhpUnlockHandleTableShared(HandleTable, lockIndex); - continue; - } - - MemoryBarrier(); - - nextFreeValue = entry->NextFreeValue; - - // Possibilities/non-possibilities at this point: - // 1. freeValue != A (our copy), but the other thread has freed A, so FreeValue = A. This is - // actually impossible since we have acquired the lock on A and the free code checks that - // and uses the alt. free list instead. - // 2. freeValue != A, and FreeValue != A. No ABA problem. - // 3. freeValue = A, and the other thread has freed A, so FreeValue = A. Impossible like - // above. This is *the* ABA problem which we have now prevented. - // 4. freeValue = A, and FreeValue != A. CAS will take care of it. - - oldFreeValue = _InterlockedCompareExchange( - &HandleTable->FreeValue, - nextFreeValue, - freeValue - ); - - PhpUnlockHandleTableShared(HandleTable, lockIndex); - - if (oldFreeValue == freeValue) - break; - } - - _InterlockedIncrement((PLONG)&HandleTable->Count); - - *HandleValue = freeValue; - - return entry; -} - -VOID PhpFreeHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ ULONG HandleValue, - _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ) -{ - PULONG freeList; - ULONG flags; - ULONG oldValue; - - _InterlockedDecrement((PLONG)&HandleTable->Count); - - flags = HandleTable->Flags; - - // Choose the free list to use depending on whether someone is popping from the main free list - // (see PhpAllocateHandleTableEntry for details). We always use the alt. free list if strict - // FIFO is enabled. - if (!(flags & PH_HANDLE_TABLE_STRICT_FIFO) && - PhTryAcquireReleaseQueuedLockExclusive( - &HandleTable->Locks[PH_HANDLE_TABLE_LOCK_INDEX(HandleValue)])) - { - freeList = &HandleTable->FreeValue; - } - else - { - freeList = &HandleTable->FreeValueAlt; - } - - while (TRUE) - { - oldValue = *freeList; - HandleTableEntry->NextFreeValue = oldValue; - - if (_InterlockedCompareExchange( - freeList, - HandleValue, - oldValue - ) == oldValue) - { - break; - } - } -} - -BOOLEAN PhpAllocateMoreHandleTableEntries( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ BOOLEAN Initialize - ) -{ - ULONG_PTR tableValue; - ULONG tableLevel; - PPH_HANDLE_TABLE_ENTRY table0; - PPH_HANDLE_TABLE_ENTRY *table1; - PPH_HANDLE_TABLE_ENTRY **table2; - ULONG i; - ULONG j; - ULONG oldNextValue; - ULONG freeValue; - - // Get a pointer to the table, and its level. - - tableValue = HandleTable->TableValue; - tableLevel = tableValue & PH_HANDLE_TABLE_LEVEL_MASK; - tableValue -= tableLevel; - - switch (tableLevel) - { - case 0: - { - // Create a level 1 table. - - table1 = PhpCreateHandleTableLevel1(HandleTable); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table1) - return FALSE; -#endif - - // Create a new level 0 table and move the existing level into the new level 1 table. - - table0 = PhpCreateHandleTableLevel0(HandleTable, Initialize); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table0) - { - PhpFreeHandleTableLevel1(table1); - return FALSE; - } -#endif - - table1[0] = (PPH_HANDLE_TABLE_ENTRY)tableValue; - table1[1] = table0; - - tableValue = (ULONG_PTR)table1 | 1; - //_InterlockedExchangePointer((PVOID *)&HandleTable->TableValue, (PVOID)tableValue); - HandleTable->TableValue = tableValue; - } - break; - case 1: - { - table1 = (PPH_HANDLE_TABLE_ENTRY *)tableValue; - - // Determine whether we need to create a new level 0 table or create a level 2 table. - - i = HandleTable->NextValue / PH_HANDLE_TABLE_LEVEL_ENTRIES; - - if (i < PH_HANDLE_TABLE_LEVEL_ENTRIES) - { - table0 = PhpCreateHandleTableLevel0(HandleTable, TRUE); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table0) - return FALSE; -#endif - - //_InterlockedExchangePointer((PVOID *)&table1[i], table0); - table1[i] = table0; - } - else - { - // Create a level 2 table. - - table2 = PhpCreateHandleTableLevel2(HandleTable); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table2) - return FALSE; -#endif - - // Create a new level 1 table and move the existing level into the new level 2 - // table. - - table1 = PhpCreateHandleTableLevel1(HandleTable); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table1) - { - PhpFreeHandleTableLevel2(table2); - return FALSE; - } -#endif - - table0 = PhpCreateHandleTableLevel0(HandleTable, Initialize); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table0) - { - PhpFreeHandleTableLevel1(table1); - PhpFreeHandleTableLevel2(table2); - return FALSE; - } -#endif - - table1[0] = table0; - - table2[0] = (PPH_HANDLE_TABLE_ENTRY *)tableValue; - table2[1] = table1; - - tableValue = (ULONG_PTR)table2 | 2; - //_InterlockedExchangePointer((PVOID *)&HandleTable->TableValue, (PVOID)tableValue); - HandleTable->TableValue = tableValue; - } - } - break; - case 2: - { - table2 = (PPH_HANDLE_TABLE_ENTRY **)tableValue; - - i = HandleTable->NextValue / - (PH_HANDLE_TABLE_LEVEL_ENTRIES * PH_HANDLE_TABLE_LEVEL_ENTRIES); - // i contains an index into the level 2 table, of the containing level 1 table. - - // Check if we have exceeded the maximum number of handles. - - if (i >= PH_HANDLE_TABLE_LEVEL_ENTRIES) - return FALSE; - - // Check if we should create a new level 0 table or a new level 2 table. - if (table2[i]) - { - table0 = PhpCreateHandleTableLevel0(HandleTable, Initialize); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table0) - return FALSE; -#endif - - // Same as j = HandleTable->NextValue % (no. entries * no. entries), but we already - // calculated i so just use it. - j = HandleTable->NextValue - i * - (PH_HANDLE_TABLE_LEVEL_ENTRIES * PH_HANDLE_TABLE_LEVEL_ENTRIES); - j /= PH_HANDLE_TABLE_LEVEL_ENTRIES; - // j now contains an index into the level 1 table, of the containing level 0 table - // (the one which was created). - - //_InterlockedExchangePointer((PVOID *)&table2[i][j], table0); - table2[i][j] = table0; - } - else - { - table1 = PhpCreateHandleTableLevel1(HandleTable); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table1) - return FALSE; -#endif - - table0 = PhpCreateHandleTableLevel0(HandleTable, TRUE); - -#ifdef PH_HANDLE_TABLE_SAFE - if (!table0) - { - PhpFreeHandleTableLevel1(table1); - return FALSE; - } -#endif - - table1[0] = table0; - - //_InterlockedExchangePointer((PVOID *)&table2[i], table1); - table2[i] = table1; - } - } - break; - default: - ASSUME_NO_DEFAULT; - } - - // In each of the cases above, we allocated one additional level 0 table. - oldNextValue = _InterlockedExchangeAdd( - (PLONG)&HandleTable->NextValue, - PH_HANDLE_TABLE_LEVEL_ENTRIES - ); - - if (Initialize) - { - // No ABA problem since these are new handles being pushed. - - while (TRUE) - { - freeValue = HandleTable->FreeValue; - table0[PH_HANDLE_TABLE_LEVEL_ENTRIES - 1].NextFreeValue = freeValue; - - if (_InterlockedCompareExchange( - &HandleTable->FreeValue, - oldNextValue, - freeValue - ) == freeValue) - { - break; - } - } - } - - return TRUE; -} - -PPH_HANDLE_TABLE_ENTRY PhpLookupHandleTableEntry( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ ULONG HandleValue - ) -{ - ULONG_PTR tableValue; - ULONG tableLevel; - PPH_HANDLE_TABLE_ENTRY table0; - PPH_HANDLE_TABLE_ENTRY *table1; - PPH_HANDLE_TABLE_ENTRY **table2; - PPH_HANDLE_TABLE_ENTRY entry; - - if (HandleValue >= HandleTable->NextValue) - return NULL; - - // Get a pointer to the table, and its level. - - tableValue = HandleTable->TableValue; - tableLevel = tableValue & PH_HANDLE_TABLE_LEVEL_MASK; - tableValue -= tableLevel; - - // No additional checking needed; aleady checked against NextValue. - - switch (tableLevel) - { - case 0: - { - table0 = (PPH_HANDLE_TABLE_ENTRY)tableValue; - entry = &table0[HandleValue]; - } - break; - case 1: - { - table1 = (PPH_HANDLE_TABLE_ENTRY *)tableValue; - table0 = table1[PH_HANDLE_VALUE_LEVEL1_U(HandleValue)]; - entry = &table0[PH_HANDLE_VALUE_LEVEL0(HandleValue)]; - } - break; - case 2: - { - table2 = (PPH_HANDLE_TABLE_ENTRY **)tableValue; - table1 = table2[PH_HANDLE_VALUE_LEVEL2_U(HandleValue)]; - table0 = table1[PH_HANDLE_VALUE_LEVEL1(HandleValue)]; - entry = &table0[PH_HANDLE_VALUE_LEVEL0(HandleValue)]; - } - break; - default: - ASSUME_NO_DEFAULT; - } - - return entry; -} - -ULONG PhpMoveFreeHandleTableEntries( - _Inout_ PPH_HANDLE_TABLE HandleTable - ) -{ - ULONG freeValueAlt; - ULONG flags; - ULONG i; - ULONG index; - ULONG nextIndex; - ULONG lastIndex; - PPH_HANDLE_TABLE_ENTRY entry; - PPH_HANDLE_TABLE_ENTRY firstEntry; - ULONG count; - ULONG freeValue; - - // Remove all entries from the alt. free list. - freeValueAlt = _InterlockedExchange(&HandleTable->FreeValueAlt, PH_HANDLE_VALUE_INVALID); - - if (freeValueAlt == PH_HANDLE_VALUE_INVALID) - { - // No handles on the alt. free list. - return PH_HANDLE_VALUE_INVALID; - } - - // Avoid the ABA problem by testing all locks (see PhpAllocateHandleTableEntry for details). - // Unlike in PhpFreeHandleTableEntry we have no "alternative" list, so we must allow blocking. - for (i = 0; i < PH_HANDLE_TABLE_LOCKS; i++) - PhAcquireReleaseQueuedLockExclusive(&HandleTable->Locks[i]); - - flags = HandleTable->Flags; - - if (!(flags & PH_HANDLE_TABLE_STRICT_FIFO)) - { - // Shortcut: if there are no entries in the main free list and we don't need to reverse the - // chain, just return. - if (_InterlockedCompareExchange( - &HandleTable->FreeValue, - freeValueAlt, - PH_HANDLE_VALUE_INVALID - ) == PH_HANDLE_VALUE_INVALID) - return freeValueAlt; - } - - // Reverse the chain (even if strict FIFO is off; we have to traverse the list to find the last - // entry, so we might as well reverse it along the way). - - index = freeValueAlt; - lastIndex = PH_HANDLE_VALUE_INVALID; - count = 0; - - while (TRUE) - { - entry = PhpLookupHandleTableEntry(HandleTable, index); - count++; - - if (lastIndex == PH_HANDLE_VALUE_INVALID) - firstEntry = entry; - - nextIndex = entry->NextFreeValue; - entry->NextFreeValue = lastIndex; - lastIndex = index; - - if (nextIndex == PH_HANDLE_VALUE_INVALID) - break; - - index = nextIndex; - } - - // Note that firstEntry actually contains the last free entry, since we reversed the list. - // Similarly index/lastIndex both contain the index of the first free entry. - - // Push the entries onto the free list. - while (TRUE) - { - freeValue = HandleTable->FreeValue; - firstEntry->NextFreeValue = freeValue; - - if (_InterlockedCompareExchange( - &HandleTable->FreeValue, - index, - freeValue - ) == freeValue) - break; - } - - // Force expansion if we don't have enough free handles. - if ( - (flags & PH_HANDLE_TABLE_STRICT_FIFO) && - count < PH_HANDLE_TABLE_FREE_COUNT - ) - { - index = PH_HANDLE_VALUE_INVALID; - } - - return index; -} - -PPH_HANDLE_TABLE_ENTRY PhpCreateHandleTableLevel0( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ BOOLEAN Initialize - ) -{ - PPH_HANDLE_TABLE_ENTRY table; - PPH_HANDLE_TABLE_ENTRY entry; - ULONG baseValue; - ULONG i; - -#ifdef PH_HANDLE_TABLE_SAFE - __try - { - table = PhAllocateFromFreeList(&PhHandleTableLevel0FreeList); - } - __except (SIMPLE_EXCEPTION_FILTER(GetExceptionCode() == STATUS_NO_MEMORY)) - { - return NULL; - } -#else - table = PhAllocateFromFreeList(&PhHandleTableLevel0FreeList); -#endif - - if (Initialize) - { - entry = &table[0]; - baseValue = HandleTable->NextValue; - - for (i = baseValue + 1; i < baseValue + PH_HANDLE_TABLE_LEVEL_ENTRIES; i++) - { - entry->Value = PH_HANDLE_TABLE_ENTRY_FREE; - entry->NextFreeValue = i; - entry++; - } - - entry->Value = PH_HANDLE_TABLE_ENTRY_FREE; - entry->NextFreeValue = PH_HANDLE_VALUE_INVALID; - } - - return table; -} - -VOID PhpFreeHandleTableLevel0( - _In_ PPH_HANDLE_TABLE_ENTRY Table - ) -{ - PhFreeToFreeList(&PhHandleTableLevel0FreeList, Table); -} - -PPH_HANDLE_TABLE_ENTRY *PhpCreateHandleTableLevel1( - _In_ PPH_HANDLE_TABLE HandleTable - ) -{ - PPH_HANDLE_TABLE_ENTRY *table; - -#ifdef PH_HANDLE_TABLE_SAFE - __try - { - table = PhAllocateFromFreeList(&PhHandleTableLevel1FreeList); - } - __except (SIMPLE_EXCEPTION_FILTER(GetExceptionCode() == STATUS_NO_MEMORY)) - { - return NULL; - } -#else - table = PhAllocateFromFreeList(&PhHandleTableLevel1FreeList); -#endif - - memset(table, 0, sizeof(PPH_HANDLE_TABLE_ENTRY) * PH_HANDLE_TABLE_LEVEL_ENTRIES); - - return table; -} - -VOID PhpFreeHandleTableLevel1( - _In_ PPH_HANDLE_TABLE_ENTRY *Table - ) -{ - PhFreeToFreeList(&PhHandleTableLevel1FreeList, Table); -} - -PPH_HANDLE_TABLE_ENTRY **PhpCreateHandleTableLevel2( - _In_ PPH_HANDLE_TABLE HandleTable - ) -{ - PPH_HANDLE_TABLE_ENTRY **table; - -#ifdef PH_HANDLE_TABLE_SAFE - table = PhAllocateSafe(sizeof(PPH_HANDLE_TABLE_ENTRY *) * PH_HANDLE_TABLE_LEVEL_ENTRIES); - - if (!table) - return NULL; -#else - table = PhAllocate(sizeof(PPH_HANDLE_TABLE_ENTRY *) * PH_HANDLE_TABLE_LEVEL_ENTRIES); -#endif - - memset(table, 0, sizeof(PPH_HANDLE_TABLE_ENTRY *) * PH_HANDLE_TABLE_LEVEL_ENTRIES); - - return table; -} - -VOID PhpFreeHandleTableLevel2( - _In_ PPH_HANDLE_TABLE_ENTRY **Table - ) -{ - PhFree(Table); -} +/* + * Process Hacker - + * handle table + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +static PH_INITONCE PhHandleTableInitOnce = PH_INITONCE_INIT; +static PH_FREE_LIST PhHandleTableLevel0FreeList; +static PH_FREE_LIST PhHandleTableLevel1FreeList; + +PPH_HANDLE_TABLE PhCreateHandleTable( + VOID + ) +{ + PPH_HANDLE_TABLE handleTable; + ULONG i; + + if (PhBeginInitOnce(&PhHandleTableInitOnce)) + { + PhInitializeFreeList( + &PhHandleTableLevel0FreeList, + sizeof(PH_HANDLE_TABLE_ENTRY) * PH_HANDLE_TABLE_LEVEL_ENTRIES, + 64 + ); + PhInitializeFreeList( + &PhHandleTableLevel1FreeList, + sizeof(PPH_HANDLE_TABLE_ENTRY) * PH_HANDLE_TABLE_LEVEL_ENTRIES, + 64 + ); + PhEndInitOnce(&PhHandleTableInitOnce); + } + +#ifdef PH_HANDLE_TABLE_SAFE + handleTable = PhAllocateSafe(sizeof(PH_HANDLE_TABLE)); + + if (!handleTable) + return NULL; +#else + handleTable = PhAllocate(sizeof(PH_HANDLE_TABLE)); +#endif + + PhInitializeQueuedLock(&handleTable->Lock); + PhInitializeWakeEvent(&handleTable->HandleWakeEvent); + + handleTable->NextValue = 0; + + handleTable->Count = 0; + handleTable->TableValue = (ULONG_PTR)PhpCreateHandleTableLevel0(handleTable, TRUE); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!handleTable->TableValue) + { + PhFree(handleTable); + return NULL; + } +#endif + + // We have now created the level 0 table. The free list can now be set up to point to handle 0, + // which points to the rest of the free list (1 -> 2 -> 3 -> ...). The next batch of handles + // that need to be created start at PH_HANDLE_TABLE_LEVEL_ENTRIES. + + handleTable->FreeValue = 0; + handleTable->NextValue = PH_HANDLE_TABLE_LEVEL_ENTRIES; + + handleTable->FreeValueAlt = PH_HANDLE_VALUE_INVALID; // no entries in alt. free list + + handleTable->Flags = 0; + + for (i = 0; i < PH_HANDLE_TABLE_LOCKS; i++) + PhInitializeQueuedLock(&handleTable->Locks[i]); + + return handleTable; +} + +VOID PhDestroyHandleTable( + _In_ _Post_invalid_ PPH_HANDLE_TABLE HandleTable + ) +{ + ULONG_PTR tableValue; + ULONG tableLevel; + PPH_HANDLE_TABLE_ENTRY table0; + PPH_HANDLE_TABLE_ENTRY *table1; + PPH_HANDLE_TABLE_ENTRY **table2; + ULONG i; + ULONG j; + + tableValue = HandleTable->TableValue; + tableLevel = tableValue & PH_HANDLE_TABLE_LEVEL_MASK; + tableValue -= tableLevel; + + switch (tableLevel) + { + case 0: + { + table0 = (PPH_HANDLE_TABLE_ENTRY)tableValue; + + PhpFreeHandleTableLevel0(table0); + } + break; + case 1: + { + table1 = (PPH_HANDLE_TABLE_ENTRY *)tableValue; + + for (i = 0; i < PH_HANDLE_TABLE_LEVEL_ENTRIES; i++) + { + if (!table1[i]) + break; + + PhpFreeHandleTableLevel0(table1[i]); + } + + PhpFreeHandleTableLevel1(table1); + } + break; + case 2: + { + table2 = (PPH_HANDLE_TABLE_ENTRY **)tableValue; + + for (i = 0; i < PH_HANDLE_TABLE_LEVEL_ENTRIES; i++) + { + if (!table2[i]) + break; + + for (j = 0; j < PH_HANDLE_TABLE_LEVEL_ENTRIES; j++) + { + if (!table2[i][j]) + break; + + PhpFreeHandleTableLevel0(table2[i][j]); + } + + PhpFreeHandleTableLevel1(table2[i]); + } + + PhpFreeHandleTableLevel2(table2); + } + break; + default: + ASSUME_NO_DEFAULT; + } + + PhFree(HandleTable); +} + +VOID PhpBlockOnLockedHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + PH_QUEUED_WAIT_BLOCK waitBlock; + ULONG_PTR value; + + PhQueueWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock); + + value = HandleTableEntry->Value; + + if ( + (value & PH_HANDLE_TABLE_ENTRY_TYPE) != PH_HANDLE_TABLE_ENTRY_IN_USE || + (value & PH_HANDLE_TABLE_ENTRY_LOCKED) + ) + { + // Entry is not in use or has been unlocked; cancel the wait. + PhSetWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock); + } + else + { + PhWaitForWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock, TRUE, NULL); + } +} + +BOOLEAN PhLockHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + ULONG_PTR value; + + while (TRUE) + { + value = HandleTableEntry->Value; + + if ((value & PH_HANDLE_TABLE_ENTRY_TYPE) != PH_HANDLE_TABLE_ENTRY_IN_USE) + return FALSE; + + if (value & PH_HANDLE_TABLE_ENTRY_LOCKED) + { + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&HandleTableEntry->Value, + (PVOID)(value - PH_HANDLE_TABLE_ENTRY_LOCKED), + (PVOID)value + ) == value) + { + return TRUE; + } + } + + PhpBlockOnLockedHandleTableEntry(HandleTable, HandleTableEntry); + } +} + +VOID PhUnlockHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + _interlockedbittestandset( + (PLONG)&HandleTableEntry->Value, + PH_HANDLE_TABLE_ENTRY_LOCKED_SHIFT + ); + PhSetWakeEvent(&HandleTable->HandleWakeEvent, NULL); +} + +HANDLE PhCreateHandle( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + PPH_HANDLE_TABLE_ENTRY entry; + ULONG handleValue; + + entry = PhpAllocateHandleTableEntry(HandleTable, &handleValue); + + if (!entry) + return NULL; + + // Copy the given handle table entry to the allocated entry. + + // All free entries have the Free type and have the (not) locked bit clear. There is no problem + // with setting the Type now; the entry is still locked, so they will block. + entry->TypeAndValue.Type = PH_HANDLE_TABLE_ENTRY_IN_USE; + entry->TypeAndValue.Value = HandleTableEntry->TypeAndValue.Value; + entry->Value2 = HandleTableEntry->Value2; + + // Now we unlock this entry, waking anyone who was caught back there before we had finished + // setting up the entry. + PhUnlockHandleTableEntry(HandleTable, entry); + + return PhpEncodeHandle(handleValue); +} + +BOOLEAN PhDestroyHandle( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ HANDLE Handle, + _In_opt_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + ULONG handleValue; + + handleValue = PhpDecodeHandle(Handle); + + if (!HandleTableEntry) + { + HandleTableEntry = PhpLookupHandleTableEntry(HandleTable, handleValue); + + if (!HandleTableEntry) + return FALSE; + + if (!PhLockHandleTableEntry(HandleTable, HandleTableEntry)) + return FALSE; + } + + _InterlockedExchangePointer( + (PVOID *)&HandleTableEntry->Value, + (PVOID)PH_HANDLE_TABLE_ENTRY_FREE + ); + + // The handle table entry is now free; wake any waiters because they can't lock the entry now. + // Any future lock attempts will fail because the entry is marked as being free. + PhSetWakeEvent(&HandleTable->HandleWakeEvent, NULL); + + PhpFreeHandleTableEntry(HandleTable, handleValue, HandleTableEntry); + + return TRUE; +} + +PPH_HANDLE_TABLE_ENTRY PhLookupHandleTableEntry( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ HANDLE Handle + ) +{ + PPH_HANDLE_TABLE_ENTRY entry; + + entry = PhpLookupHandleTableEntry(HandleTable, PhpDecodeHandle(Handle)); + + if (!entry) + return NULL; + + if (!PhLockHandleTableEntry(HandleTable, entry)) + return NULL; + + return entry; +} + +VOID PhEnumHandleTable( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ PPH_ENUM_HANDLE_TABLE_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + ULONG handleValue; + PPH_HANDLE_TABLE_ENTRY entry; + BOOLEAN cont; + + handleValue = 0; + + while (entry = PhpLookupHandleTableEntry(HandleTable, handleValue)) + { + if (PhLockHandleTableEntry(HandleTable, entry)) + { + cont = Callback( + HandleTable, + PhpEncodeHandle(handleValue), + entry, + Context + ); + PhUnlockHandleTableEntry(HandleTable, entry); + + if (!cont) + break; + } + + handleValue++; + } +} + +VOID PhSweepHandleTable( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ PPH_ENUM_HANDLE_TABLE_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + ULONG handleValue; + PPH_HANDLE_TABLE_ENTRY entry; + BOOLEAN cont; + + handleValue = 0; + + while (entry = PhpLookupHandleTableEntry(HandleTable, handleValue)) + { + if (entry->TypeAndValue.Type == PH_HANDLE_TABLE_ENTRY_IN_USE) + { + cont = Callback( + HandleTable, + PhpEncodeHandle(handleValue), + entry, + Context + ); + + if (!cont) + break; + } + + handleValue++; + } +} + +NTSTATUS PhQueryInformationHandleTable( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, + _Out_writes_bytes_opt_(BufferLength) PVOID Buffer, + _In_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength; + + switch (InformationClass) + { + case HandleTableBasicInformation: + { + PPH_HANDLE_TABLE_BASIC_INFORMATION basicInfo = Buffer; + + if (BufferLength == sizeof(PH_HANDLE_TABLE_BASIC_INFORMATION)) + { + basicInfo->Count = HandleTable->Count; + basicInfo->Flags = HandleTable->Flags; + basicInfo->TableLevel = HandleTable->TableValue & PH_HANDLE_TABLE_LEVEL_MASK; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + returnLength = sizeof(PH_HANDLE_TABLE_BASIC_INFORMATION); + } + break; + case HandleTableFlagsInformation: + { + PPH_HANDLE_TABLE_FLAGS_INFORMATION flagsInfo = Buffer; + + if (BufferLength == sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION)) + { + flagsInfo->Flags = HandleTable->Flags; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + returnLength = sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION); + } + break; + default: + status = STATUS_INVALID_INFO_CLASS; + returnLength = 0; + break; + } + + if (ReturnLength) + *ReturnLength = returnLength; + + return status; +} + +NTSTATUS PhSetInformationHandleTable( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, + _In_reads_bytes_(BufferLength) PVOID Buffer, + _In_ ULONG BufferLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + switch (InformationClass) + { + case HandleTableFlagsInformation: + { + PPH_HANDLE_TABLE_FLAGS_INFORMATION flagsInfo = Buffer; + ULONG flags; + + if (BufferLength == sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION)) + { + flags = flagsInfo->Flags; + + if ((flags & PH_HANDLE_TABLE_VALID_FLAGS) == flags) + HandleTable->Flags = flags; + else + status = STATUS_INVALID_PARAMETER; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + break; + default: + status = STATUS_INVALID_INFO_CLASS; + } + + return status; +} + +PPH_HANDLE_TABLE_ENTRY PhpAllocateHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _Out_ PULONG HandleValue + ) +{ + PPH_HANDLE_TABLE_ENTRY entry; + ULONG freeValue; + ULONG lockIndex; + ULONG nextFreeValue; + ULONG oldFreeValue; + BOOLEAN result; + + while (TRUE) + { + freeValue = HandleTable->FreeValue; + + while (freeValue == PH_HANDLE_VALUE_INVALID) + { + PhAcquireQueuedLockExclusive(&HandleTable->Lock); + + // Check again to see if we have free handles. + + freeValue = HandleTable->FreeValue; + + if (freeValue != PH_HANDLE_VALUE_INVALID) + { + PhReleaseQueuedLockExclusive(&HandleTable->Lock); + break; + } + + // Move handles from the alt. free list to the main free list, and check again. + + freeValue = PhpMoveFreeHandleTableEntries(HandleTable); + + if (freeValue != PH_HANDLE_VALUE_INVALID) + { + PhReleaseQueuedLockExclusive(&HandleTable->Lock); + break; + } + + result = PhpAllocateMoreHandleTableEntries(HandleTable, TRUE); + + PhReleaseQueuedLockExclusive(&HandleTable->Lock); + + freeValue = HandleTable->FreeValue; + + // Note that PhpAllocateMoreHandleTableEntries only returns FALSE if it failed to + // allocate memory. Success does not guarantee a free handle to be allocated, as they + // may have been all used up (however unlikely) when we reach this point. Success simply + // means to retry the allocation using the fast path. + + if (!result && freeValue == PH_HANDLE_VALUE_INVALID) + return NULL; + } + + entry = PhpLookupHandleTableEntry(HandleTable, freeValue); + lockIndex = PH_HANDLE_TABLE_LOCK_INDEX(freeValue); + + // To avoid the ABA problem, we would ideally have one queued lock per handle table entry. + // That would make the overhead too large, so instead there is a fixed number of locks, + // indexed by the handle value (mod no. locks). + + // Possibilities at this point: + // 1. freeValue != A (our copy), but the other thread has freed A, so FreeValue = A. No ABA + // problem since freeValue != A. + // 2. freeValue != A, and FreeValue != A. No ABA problem. + // 3. freeValue = A, and the other thread has freed A, so FreeValue = A. No ABA problem + // since we haven't read NextFreeValue yet. + // 4. freeValue = A, and FreeValue != A. No problem if this stays the same later, as the CAS + // will take care of it. + + PhpLockHandleTableShared(HandleTable, lockIndex); + + if (HandleTable->FreeValue != freeValue) + { + PhpUnlockHandleTableShared(HandleTable, lockIndex); + continue; + } + + MemoryBarrier(); + + nextFreeValue = entry->NextFreeValue; + + // Possibilities/non-possibilities at this point: + // 1. freeValue != A (our copy), but the other thread has freed A, so FreeValue = A. This is + // actually impossible since we have acquired the lock on A and the free code checks that + // and uses the alt. free list instead. + // 2. freeValue != A, and FreeValue != A. No ABA problem. + // 3. freeValue = A, and the other thread has freed A, so FreeValue = A. Impossible like + // above. This is *the* ABA problem which we have now prevented. + // 4. freeValue = A, and FreeValue != A. CAS will take care of it. + + oldFreeValue = _InterlockedCompareExchange( + &HandleTable->FreeValue, + nextFreeValue, + freeValue + ); + + PhpUnlockHandleTableShared(HandleTable, lockIndex); + + if (oldFreeValue == freeValue) + break; + } + + _InterlockedIncrement((PLONG)&HandleTable->Count); + + *HandleValue = freeValue; + + return entry; +} + +VOID PhpFreeHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ ULONG HandleValue, + _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + PULONG freeList; + ULONG flags; + ULONG oldValue; + + _InterlockedDecrement((PLONG)&HandleTable->Count); + + flags = HandleTable->Flags; + + // Choose the free list to use depending on whether someone is popping from the main free list + // (see PhpAllocateHandleTableEntry for details). We always use the alt. free list if strict + // FIFO is enabled. + if (!(flags & PH_HANDLE_TABLE_STRICT_FIFO) && + PhTryAcquireReleaseQueuedLockExclusive( + &HandleTable->Locks[PH_HANDLE_TABLE_LOCK_INDEX(HandleValue)])) + { + freeList = &HandleTable->FreeValue; + } + else + { + freeList = &HandleTable->FreeValueAlt; + } + + while (TRUE) + { + oldValue = *freeList; + HandleTableEntry->NextFreeValue = oldValue; + + if (_InterlockedCompareExchange( + freeList, + HandleValue, + oldValue + ) == oldValue) + { + break; + } + } +} + +BOOLEAN PhpAllocateMoreHandleTableEntries( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ BOOLEAN Initialize + ) +{ + ULONG_PTR tableValue; + ULONG tableLevel; + PPH_HANDLE_TABLE_ENTRY table0; + PPH_HANDLE_TABLE_ENTRY *table1; + PPH_HANDLE_TABLE_ENTRY **table2; + ULONG i; + ULONG j; + ULONG oldNextValue; + ULONG freeValue; + + // Get a pointer to the table, and its level. + + tableValue = HandleTable->TableValue; + tableLevel = tableValue & PH_HANDLE_TABLE_LEVEL_MASK; + tableValue -= tableLevel; + + switch (tableLevel) + { + case 0: + { + // Create a level 1 table. + + table1 = PhpCreateHandleTableLevel1(HandleTable); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table1) + return FALSE; +#endif + + // Create a new level 0 table and move the existing level into the new level 1 table. + + table0 = PhpCreateHandleTableLevel0(HandleTable, Initialize); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table0) + { + PhpFreeHandleTableLevel1(table1); + return FALSE; + } +#endif + + table1[0] = (PPH_HANDLE_TABLE_ENTRY)tableValue; + table1[1] = table0; + + tableValue = (ULONG_PTR)table1 | 1; + //_InterlockedExchangePointer((PVOID *)&HandleTable->TableValue, (PVOID)tableValue); + HandleTable->TableValue = tableValue; + } + break; + case 1: + { + table1 = (PPH_HANDLE_TABLE_ENTRY *)tableValue; + + // Determine whether we need to create a new level 0 table or create a level 2 table. + + i = HandleTable->NextValue / PH_HANDLE_TABLE_LEVEL_ENTRIES; + + if (i < PH_HANDLE_TABLE_LEVEL_ENTRIES) + { + table0 = PhpCreateHandleTableLevel0(HandleTable, TRUE); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table0) + return FALSE; +#endif + + //_InterlockedExchangePointer((PVOID *)&table1[i], table0); + table1[i] = table0; + } + else + { + // Create a level 2 table. + + table2 = PhpCreateHandleTableLevel2(HandleTable); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table2) + return FALSE; +#endif + + // Create a new level 1 table and move the existing level into the new level 2 + // table. + + table1 = PhpCreateHandleTableLevel1(HandleTable); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table1) + { + PhpFreeHandleTableLevel2(table2); + return FALSE; + } +#endif + + table0 = PhpCreateHandleTableLevel0(HandleTable, Initialize); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table0) + { + PhpFreeHandleTableLevel1(table1); + PhpFreeHandleTableLevel2(table2); + return FALSE; + } +#endif + + table1[0] = table0; + + table2[0] = (PPH_HANDLE_TABLE_ENTRY *)tableValue; + table2[1] = table1; + + tableValue = (ULONG_PTR)table2 | 2; + //_InterlockedExchangePointer((PVOID *)&HandleTable->TableValue, (PVOID)tableValue); + HandleTable->TableValue = tableValue; + } + } + break; + case 2: + { + table2 = (PPH_HANDLE_TABLE_ENTRY **)tableValue; + + i = HandleTable->NextValue / + (PH_HANDLE_TABLE_LEVEL_ENTRIES * PH_HANDLE_TABLE_LEVEL_ENTRIES); + // i contains an index into the level 2 table, of the containing level 1 table. + + // Check if we have exceeded the maximum number of handles. + + if (i >= PH_HANDLE_TABLE_LEVEL_ENTRIES) + return FALSE; + + // Check if we should create a new level 0 table or a new level 2 table. + if (table2[i]) + { + table0 = PhpCreateHandleTableLevel0(HandleTable, Initialize); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table0) + return FALSE; +#endif + + // Same as j = HandleTable->NextValue % (no. entries * no. entries), but we already + // calculated i so just use it. + j = HandleTable->NextValue - i * + (PH_HANDLE_TABLE_LEVEL_ENTRIES * PH_HANDLE_TABLE_LEVEL_ENTRIES); + j /= PH_HANDLE_TABLE_LEVEL_ENTRIES; + // j now contains an index into the level 1 table, of the containing level 0 table + // (the one which was created). + + //_InterlockedExchangePointer((PVOID *)&table2[i][j], table0); + table2[i][j] = table0; + } + else + { + table1 = PhpCreateHandleTableLevel1(HandleTable); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table1) + return FALSE; +#endif + + table0 = PhpCreateHandleTableLevel0(HandleTable, TRUE); + +#ifdef PH_HANDLE_TABLE_SAFE + if (!table0) + { + PhpFreeHandleTableLevel1(table1); + return FALSE; + } +#endif + + table1[0] = table0; + + //_InterlockedExchangePointer((PVOID *)&table2[i], table1); + table2[i] = table1; + } + } + break; + default: + ASSUME_NO_DEFAULT; + } + + // In each of the cases above, we allocated one additional level 0 table. + oldNextValue = _InterlockedExchangeAdd( + (PLONG)&HandleTable->NextValue, + PH_HANDLE_TABLE_LEVEL_ENTRIES + ); + + if (Initialize) + { + // No ABA problem since these are new handles being pushed. + + while (TRUE) + { + freeValue = HandleTable->FreeValue; + table0[PH_HANDLE_TABLE_LEVEL_ENTRIES - 1].NextFreeValue = freeValue; + + if (_InterlockedCompareExchange( + &HandleTable->FreeValue, + oldNextValue, + freeValue + ) == freeValue) + { + break; + } + } + } + + return TRUE; +} + +PPH_HANDLE_TABLE_ENTRY PhpLookupHandleTableEntry( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ ULONG HandleValue + ) +{ + ULONG_PTR tableValue; + ULONG tableLevel; + PPH_HANDLE_TABLE_ENTRY table0; + PPH_HANDLE_TABLE_ENTRY *table1; + PPH_HANDLE_TABLE_ENTRY **table2; + PPH_HANDLE_TABLE_ENTRY entry; + + if (HandleValue >= HandleTable->NextValue) + return NULL; + + // Get a pointer to the table, and its level. + + tableValue = HandleTable->TableValue; + tableLevel = tableValue & PH_HANDLE_TABLE_LEVEL_MASK; + tableValue -= tableLevel; + + // No additional checking needed; aleady checked against NextValue. + + switch (tableLevel) + { + case 0: + { + table0 = (PPH_HANDLE_TABLE_ENTRY)tableValue; + entry = &table0[HandleValue]; + } + break; + case 1: + { + table1 = (PPH_HANDLE_TABLE_ENTRY *)tableValue; + table0 = table1[PH_HANDLE_VALUE_LEVEL1_U(HandleValue)]; + entry = &table0[PH_HANDLE_VALUE_LEVEL0(HandleValue)]; + } + break; + case 2: + { + table2 = (PPH_HANDLE_TABLE_ENTRY **)tableValue; + table1 = table2[PH_HANDLE_VALUE_LEVEL2_U(HandleValue)]; + table0 = table1[PH_HANDLE_VALUE_LEVEL1(HandleValue)]; + entry = &table0[PH_HANDLE_VALUE_LEVEL0(HandleValue)]; + } + break; + default: + ASSUME_NO_DEFAULT; + } + + return entry; +} + +ULONG PhpMoveFreeHandleTableEntries( + _Inout_ PPH_HANDLE_TABLE HandleTable + ) +{ + ULONG freeValueAlt; + ULONG flags; + ULONG i; + ULONG index; + ULONG nextIndex; + ULONG lastIndex; + PPH_HANDLE_TABLE_ENTRY entry; + PPH_HANDLE_TABLE_ENTRY firstEntry; + ULONG count; + ULONG freeValue; + + // Remove all entries from the alt. free list. + freeValueAlt = _InterlockedExchange(&HandleTable->FreeValueAlt, PH_HANDLE_VALUE_INVALID); + + if (freeValueAlt == PH_HANDLE_VALUE_INVALID) + { + // No handles on the alt. free list. + return PH_HANDLE_VALUE_INVALID; + } + + // Avoid the ABA problem by testing all locks (see PhpAllocateHandleTableEntry for details). + // Unlike in PhpFreeHandleTableEntry we have no "alternative" list, so we must allow blocking. + for (i = 0; i < PH_HANDLE_TABLE_LOCKS; i++) + PhAcquireReleaseQueuedLockExclusive(&HandleTable->Locks[i]); + + flags = HandleTable->Flags; + + if (!(flags & PH_HANDLE_TABLE_STRICT_FIFO)) + { + // Shortcut: if there are no entries in the main free list and we don't need to reverse the + // chain, just return. + if (_InterlockedCompareExchange( + &HandleTable->FreeValue, + freeValueAlt, + PH_HANDLE_VALUE_INVALID + ) == PH_HANDLE_VALUE_INVALID) + return freeValueAlt; + } + + // Reverse the chain (even if strict FIFO is off; we have to traverse the list to find the last + // entry, so we might as well reverse it along the way). + + index = freeValueAlt; + lastIndex = PH_HANDLE_VALUE_INVALID; + count = 0; + + while (TRUE) + { + entry = PhpLookupHandleTableEntry(HandleTable, index); + count++; + + if (lastIndex == PH_HANDLE_VALUE_INVALID) + firstEntry = entry; + + nextIndex = entry->NextFreeValue; + entry->NextFreeValue = lastIndex; + lastIndex = index; + + if (nextIndex == PH_HANDLE_VALUE_INVALID) + break; + + index = nextIndex; + } + + // Note that firstEntry actually contains the last free entry, since we reversed the list. + // Similarly index/lastIndex both contain the index of the first free entry. + + // Push the entries onto the free list. + while (TRUE) + { + freeValue = HandleTable->FreeValue; + firstEntry->NextFreeValue = freeValue; + + if (_InterlockedCompareExchange( + &HandleTable->FreeValue, + index, + freeValue + ) == freeValue) + break; + } + + // Force expansion if we don't have enough free handles. + if ( + (flags & PH_HANDLE_TABLE_STRICT_FIFO) && + count < PH_HANDLE_TABLE_FREE_COUNT + ) + { + index = PH_HANDLE_VALUE_INVALID; + } + + return index; +} + +PPH_HANDLE_TABLE_ENTRY PhpCreateHandleTableLevel0( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ BOOLEAN Initialize + ) +{ + PPH_HANDLE_TABLE_ENTRY table; + PPH_HANDLE_TABLE_ENTRY entry; + ULONG baseValue; + ULONG i; + +#ifdef PH_HANDLE_TABLE_SAFE + __try + { + table = PhAllocateFromFreeList(&PhHandleTableLevel0FreeList); + } + __except (SIMPLE_EXCEPTION_FILTER(GetExceptionCode() == STATUS_NO_MEMORY)) + { + return NULL; + } +#else + table = PhAllocateFromFreeList(&PhHandleTableLevel0FreeList); +#endif + + if (Initialize) + { + entry = &table[0]; + baseValue = HandleTable->NextValue; + + for (i = baseValue + 1; i < baseValue + PH_HANDLE_TABLE_LEVEL_ENTRIES; i++) + { + entry->Value = PH_HANDLE_TABLE_ENTRY_FREE; + entry->NextFreeValue = i; + entry++; + } + + entry->Value = PH_HANDLE_TABLE_ENTRY_FREE; + entry->NextFreeValue = PH_HANDLE_VALUE_INVALID; + } + + return table; +} + +VOID PhpFreeHandleTableLevel0( + _In_ PPH_HANDLE_TABLE_ENTRY Table + ) +{ + PhFreeToFreeList(&PhHandleTableLevel0FreeList, Table); +} + +PPH_HANDLE_TABLE_ENTRY *PhpCreateHandleTableLevel1( + _In_ PPH_HANDLE_TABLE HandleTable + ) +{ + PPH_HANDLE_TABLE_ENTRY *table; + +#ifdef PH_HANDLE_TABLE_SAFE + __try + { + table = PhAllocateFromFreeList(&PhHandleTableLevel1FreeList); + } + __except (SIMPLE_EXCEPTION_FILTER(GetExceptionCode() == STATUS_NO_MEMORY)) + { + return NULL; + } +#else + table = PhAllocateFromFreeList(&PhHandleTableLevel1FreeList); +#endif + + memset(table, 0, sizeof(PPH_HANDLE_TABLE_ENTRY) * PH_HANDLE_TABLE_LEVEL_ENTRIES); + + return table; +} + +VOID PhpFreeHandleTableLevel1( + _In_ PPH_HANDLE_TABLE_ENTRY *Table + ) +{ + PhFreeToFreeList(&PhHandleTableLevel1FreeList, Table); +} + +PPH_HANDLE_TABLE_ENTRY **PhpCreateHandleTableLevel2( + _In_ PPH_HANDLE_TABLE HandleTable + ) +{ + PPH_HANDLE_TABLE_ENTRY **table; + +#ifdef PH_HANDLE_TABLE_SAFE + table = PhAllocateSafe(sizeof(PPH_HANDLE_TABLE_ENTRY *) * PH_HANDLE_TABLE_LEVEL_ENTRIES); + + if (!table) + return NULL; +#else + table = PhAllocate(sizeof(PPH_HANDLE_TABLE_ENTRY *) * PH_HANDLE_TABLE_LEVEL_ENTRIES); +#endif + + memset(table, 0, sizeof(PPH_HANDLE_TABLE_ENTRY *) * PH_HANDLE_TABLE_LEVEL_ENTRIES); + + return table; +} + +VOID PhpFreeHandleTableLevel2( + _In_ PPH_HANDLE_TABLE_ENTRY **Table + ) +{ + PhFree(Table); +} diff --git a/phlib/hexedit.c b/phlib/hexedit.c index 2dea6c9f4a7b..18e1f5f4f4bb 100644 --- a/phlib/hexedit.c +++ b/phlib/hexedit.c @@ -1,1868 +1,1894 @@ -/* - * Process Hacker - - * hex editor control - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include - -// Code originally from http://www.codeguru.com/Cpp/controls/editctrl/article.php/c539 - -BOOLEAN PhHexEditInitialization( - VOID - ) -{ - WNDCLASSEX c = { sizeof(c) }; - - c.style = CS_GLOBALCLASS; - c.lpfnWndProc = PhpHexEditWndProc; - c.cbClsExtra = 0; - c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; - c.hIcon = NULL; - c.hCursor = LoadCursor(NULL, IDC_ARROW); - c.hbrBackground = NULL; - c.lpszMenuName = NULL; - c.lpszClassName = PH_HEXEDIT_CLASSNAME; - c.hIconSm = NULL; - - if (!RegisterClassEx(&c)) - return FALSE; - - return TRUE; -} - -VOID PhpCreateHexEditContext( - _Out_ PPHP_HEXEDIT_CONTEXT *Context - ) -{ - PPHP_HEXEDIT_CONTEXT context; - - context = PhAllocate(sizeof(PHP_HEXEDIT_CONTEXT)); - memset(context, 0, sizeof(PHP_HEXEDIT_CONTEXT)); // important, set NullWidth to 0 - - context->Data = NULL; - context->Length = 0; - context->TopIndex = 0; - context->BytesPerRow = 16; - context->LinesPerPage = 1; - - context->ShowHex = TRUE; - context->ShowAscii = TRUE; - context->ShowAddress = TRUE; - context->AddressIsWide = TRUE; - context->AllowLengthChange = FALSE; - - context->AddressOffset = 0; - context->HexOffset = 0; - context->AsciiOffset = 0; - - context->Update = TRUE; - context->NoAddressChange = FALSE; - context->CurrentMode = EDIT_NONE; - - context->EditPosition.x = 0; - context->EditPosition.y = 0; - context->CurrentAddress = 0; - context->HalfPage = TRUE; - - context->SelStart = -1; - context->SelEnd = -1; - - *Context = context; -} - -VOID PhpFreeHexEditContext( - _In_ _Post_invalid_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - if (!Context->UserBuffer && Context->Data) PhFree(Context->Data); - if (Context->CharBuffer) PhFree(Context->CharBuffer); - if (Context->Font) DeleteObject(Context->Font); - PhFree(Context); -} - -LRESULT CALLBACK PhpHexEditWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPHP_HEXEDIT_CONTEXT context; - - context = (PPHP_HEXEDIT_CONTEXT)GetWindowLongPtr(hwnd, 0); - - if (uMsg == WM_CREATE) - { - PhpCreateHexEditContext(&context); - SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); - } - - if (!context) - return DefWindowProc(hwnd, uMsg, wParam, lParam); - - switch (uMsg) - { - case WM_CREATE: - { - context->Font = CreateFont(-(LONG)PhMultiplyDivide(12, PhGlobalDpi, 96), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Courier New"); - } - break; - case WM_DESTROY: - { - SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); - PhpFreeHexEditContext(context); - } - break; - case WM_PAINT: - { - PAINTSTRUCT paintStruct; - HDC hdc; - - if (hdc = BeginPaint(hwnd, &paintStruct)) - { - PhpHexEditOnPaint(hwnd, context, &paintStruct, hdc); - EndPaint(hwnd, &paintStruct); - } - } - break; - case WM_SIZE: - { - PhpHexEditUpdateMetrics(hwnd, context, FALSE, NULL); - } - break; - case WM_SETFOCUS: - { - if (context->Data && !PhpHexEditHasSelected(context)) - { - if (context->EditPosition.x == 0 && context->ShowAddress) - PhpHexEditCreateAddressCaret(hwnd, context); - else - PhpHexEditCreateEditCaret(hwnd, context); - - SetCaretPos(context->EditPosition.x, context->EditPosition.y); - ShowCaret(hwnd); - } - } - break; - case WM_KILLFOCUS: - { - DestroyCaret(); - } - break; - case WM_VSCROLL: - { - SHORT scrollRequest = LOWORD(wParam); - LONG currentPosition; - LONG originalTopIndex; - SCROLLINFO scrollInfo = { sizeof(scrollInfo) }; - - originalTopIndex = context->TopIndex; - - scrollInfo.fMask = SIF_TRACKPOS; - GetScrollInfo(hwnd, SB_VERT, &scrollInfo); - currentPosition = scrollInfo.nTrackPos; - - if (context->Data) - { - LONG mult; - - mult = context->LinesPerPage * context->BytesPerRow; - - switch (scrollRequest) - { - case SB_LINEDOWN: - if (context->TopIndex < context->Length - mult) - { - context->TopIndex += context->BytesPerRow; - REDRAW_WINDOW(hwnd); - } - break; - case SB_LINEUP: - if (context->TopIndex >= context->BytesPerRow) - { - context->TopIndex -= context->BytesPerRow; - REDRAW_WINDOW(hwnd); - } - break; - case SB_PAGEDOWN: - if (context->TopIndex < context->Length - mult) - { - context->TopIndex += mult; - - if (context->TopIndex > context->Length - mult) - context->TopIndex = context->Length - mult; - - REDRAW_WINDOW(hwnd); - } - break; - case SB_PAGEUP: - if (context->TopIndex > 0) - { - context->TopIndex -= mult; - - if (context->TopIndex < 0) - context->TopIndex = 0; - - REDRAW_WINDOW(hwnd); - } - break; - case SB_THUMBTRACK: - context->TopIndex = currentPosition * context->BytesPerRow; - REDRAW_WINDOW(hwnd); - break; - } - - SetScrollPos(hwnd, SB_VERT, context->TopIndex / context->BytesPerRow, TRUE); - - if (!context->NoAddressChange && FALSE) // this behaviour sucks, so just leave it out - context->CurrentAddress += context->TopIndex - originalTopIndex; - - PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); - } - } - break; - case WM_MOUSEWHEEL: - { - SHORT wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); - - if (context->Data) - { - ULONG wheelScrollLines; - - if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScrollLines, 0)) - wheelScrollLines = 3; - - context->TopIndex += context->BytesPerRow * (LONG)wheelScrollLines * -wheelDelta / WHEEL_DELTA; - - if (context->TopIndex < 0) - context->TopIndex = 0; - - if (context->Length >= context->LinesPerPage * context->BytesPerRow) - { - if (context->TopIndex > context->Length - context->LinesPerPage * context->BytesPerRow) - context->TopIndex = context->Length - context->LinesPerPage * context->BytesPerRow; - } - - REDRAW_WINDOW(hwnd); - - SetScrollPos(hwnd, SB_VERT, context->TopIndex / context->BytesPerRow, TRUE); - - PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); - } - } - break; - case WM_GETDLGCODE: - if (wParam != VK_ESCAPE) - return DLGC_WANTALLKEYS; - break; - case WM_ERASEBKGND: - return 1; - case WM_LBUTTONDOWN: - { - ULONG flags = (ULONG)wParam; - POINT cursorPos; - - cursorPos.x = (LONG)(SHORT)LOWORD(lParam); - cursorPos.y = (LONG)(SHORT)HIWORD(lParam); - - SetFocus(hwnd); - - if (context->Data) - { - POINT point; - - if (wParam & MK_SHIFT) - context->SelStart = context->CurrentAddress; - - PhpHexEditCalculatePosition(hwnd, context, cursorPos.x, cursorPos.y, &point); - - if (point.x > -1) - { - context->EditPosition = point; - - point.x *= context->NullWidth; - point.y *= context->LineHeight; - - if (point.x == 0 && context->ShowAddress) - PhpHexEditCreateAddressCaret(hwnd, context); - else - PhpHexEditCreateEditCaret(hwnd, context); - - SetCaretPos(point.x, point.y); - - if (flags & MK_SHIFT) - { - context->SelEnd = context->CurrentAddress; - - if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) - context->SelEnd++; - - REDRAW_WINDOW(hwnd); - } - } - - if (!(flags & MK_SHIFT)) - { - if (DragDetect(hwnd, cursorPos)) - { - context->SelStart = context->CurrentAddress; - context->SelEnd = context->SelStart; - SetCapture(hwnd); - context->HasCapture = TRUE; - } - else - { - BOOLEAN selected; - - selected = context->SelStart != -1; - context->SelStart = -1; - context->SelEnd = -1; - - if (selected) - REDRAW_WINDOW(hwnd); - } - } - - if (!PhpHexEditHasSelected(context)) - ShowCaret(hwnd); - } - } - break; - case WM_LBUTTONUP: - { - if (context->HasCapture && PhpHexEditHasSelected(context)) - ReleaseCapture(); - - context->HasCapture = FALSE; - } - break; - case WM_MOUSEMOVE: - { - ULONG flags = (ULONG)wParam; - POINT cursorPos; - - cursorPos.x = (LONG)(SHORT)LOWORD(lParam); - cursorPos.y = (LONG)(SHORT)HIWORD(lParam); - - if ( - context->Data && - context->HasCapture && - context->SelStart != -1 - ) - { - RECT rect; - POINT point; - ULONG oldSelEnd; - - // User is dragging. - - GetClientRect(hwnd, &rect); - - if (!PtInRect(&rect, cursorPos)) - { - if (cursorPos.y < 0) - { - SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); - cursorPos.y = 0; - } - else if (cursorPos.y > rect.bottom) - { - SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); - cursorPos.y = rect.bottom - 1; - } - } - - oldSelEnd = context->SelEnd; - PhpHexEditCalculatePosition(hwnd, context, cursorPos.x, cursorPos.y, &point); - - if (point.x > -1) - { - context->SelEnd = context->CurrentAddress; - - if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) - context->SelEnd++; - } - - if (PhpHexEditHasSelected(context)) - DestroyCaret(); - - if (context->SelEnd != oldSelEnd) - REDRAW_WINDOW(hwnd); - } - } - break; - case WM_CHAR: - { - ULONG c = (ULONG)wParam; - - if (!context->Data) - goto DefaultHandler; - if (c == '\t') - goto DefaultHandler; - - if (GetKeyState(VK_CONTROL) < 0) - { - switch (c) - { - case 0x3: - if (PhpHexEditHasSelected(context)) - PhpHexEditCopyEdit(hwnd, context); - goto DefaultHandler; - case 0x16: - PhpHexEditPasteEdit(hwnd, context); - goto DefaultHandler; - case 0x18: - if (PhpHexEditHasSelected(context)) - PhpHexEditCutEdit(hwnd, context); - goto DefaultHandler; - case 0x1a: - PhpHexEditUndoEdit(hwnd, context); - goto DefaultHandler; - } - } - - // Disallow editing beyond the end of the data. - if (context->CurrentAddress >= context->Length) - goto DefaultHandler; - - if (c == 0x8) - { - if (context->CurrentAddress != 0) - { - context->CurrentAddress--; - PhpHexEditSelDelete(hwnd, context, context->CurrentAddress, context->CurrentAddress + 1); - PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); - REDRAW_WINDOW(hwnd); - } - - goto DefaultHandler; - } - - PhpHexEditSetSel(hwnd, context, -1, -1); - - switch (context->CurrentMode) - { - case EDIT_NONE: - goto DefaultHandler; - case EDIT_HIGH: - case EDIT_LOW: - if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) - { - ULONG b = c - '0'; - - if (b > 9) - b = 10 + c - 'a'; - - if (context->CurrentMode == EDIT_HIGH) - { - context->Data[context->CurrentAddress] = - (UCHAR)((context->Data[context->CurrentAddress] & 0x0f) | (b << 4)); - } - else - { - context->Data[context->CurrentAddress] = - (UCHAR)((context->Data[context->CurrentAddress] & 0xf0) | b); - } - - PhpHexEditMove(hwnd, context, 1, 0); - } - break; - case EDIT_ASCII: - context->Data[context->CurrentAddress] = (UCHAR)c; - PhpHexEditMove(hwnd, context, 1, 0); - break; - } - - REDRAW_WINDOW(hwnd); - } - break; - case WM_KEYDOWN: - { - ULONG vk = (ULONG)wParam; - BOOLEAN shift = GetKeyState(VK_SHIFT) < 0; - BOOLEAN oldNoAddressChange = context->NoAddressChange; - BOOLEAN noScrollIntoView = FALSE; - - context->NoAddressChange = TRUE; - - switch (vk) - { - case VK_DOWN: - if (context->CurrentMode != EDIT_NONE) - { - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - PhpHexEditMove(hwnd, context, 0, 1); - context->SelEnd = context->CurrentAddress; - - if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) - context->SelEnd++; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - PhpHexEditMove(hwnd, context, 0, 1); - noScrollIntoView = TRUE; - } - else - { - PhpHexEditMove(hwnd, context, 0, 1); - } - break; - case VK_UP: - if (context->CurrentMode != EDIT_NONE) - { - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - PhpHexEditMove(hwnd, context, 0, -1); - context->SelEnd = context->CurrentAddress; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - PhpHexEditMove(hwnd, context, 0, -1); - noScrollIntoView = TRUE; - } - else - { - PhpHexEditMove(hwnd, context, 0, -1); - } - break; - case VK_LEFT: - if (context->CurrentMode != EDIT_NONE) - { - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - PhpHexEditMove(hwnd, context, -1, 0); - context->SelEnd = context->CurrentAddress; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - PhpHexEditMove(hwnd, context, -1, 0); - noScrollIntoView = TRUE; - } - break; - case VK_RIGHT: - if (context->CurrentMode != EDIT_NONE) - { - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - PhpHexEditMove(hwnd, context, 1, 0); - context->SelEnd = context->CurrentAddress; - - if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) - context->SelEnd++; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - PhpHexEditMove(hwnd, context, 1, 0); - noScrollIntoView = TRUE; - } - break; - case VK_PRIOR: - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); - PhpHexEditMove(hwnd, context, 0, 0); - context->SelEnd = context->CurrentAddress; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); - PhpHexEditMove(hwnd, context, 0, 0); - noScrollIntoView = TRUE; - break; - case VK_NEXT: - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); - PhpHexEditMove(hwnd, context, 0, 0); - context->SelEnd = context->CurrentAddress; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); - PhpHexEditMove(hwnd, context, 0, 0); - noScrollIntoView = TRUE; - break; - case VK_HOME: - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - if (GetKeyState(VK_CONTROL) < 0) - { - SendMessage(hwnd, WM_VSCROLL, SB_THUMBTRACK, 0); - } - else - { - // Round down. - context->CurrentAddress /= context->BytesPerRow; - context->CurrentAddress *= context->BytesPerRow; - } - - PhpHexEditMove(hwnd, context, 0, 0); - context->SelEnd = context->CurrentAddress; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - if (GetKeyState(VK_CONTROL) < 0) - { - SendMessage(hwnd, WM_VSCROLL, SB_THUMBTRACK, 0); - context->CurrentAddress = 0; - } - else - { - // Round down. - context->CurrentAddress /= context->BytesPerRow; - context->CurrentAddress *= context->BytesPerRow; - } - - PhpHexEditMove(hwnd, context, 0, 0); - noScrollIntoView = TRUE; - - break; - case VK_END: - if (shift) - { - if (!PhpHexEditHasSelected(context)) - context->SelStart = context->CurrentAddress; - - if (GetKeyState(VK_CONTROL) < 0) - { - context->CurrentAddress = context->Length - 1; - SendMessage(hwnd, WM_VSCROLL, - MAKEWPARAM(SB_THUMBTRACK, ((context->Length + (context->BytesPerRow / 2)) / context->BytesPerRow) - context->LinesPerPage), - 0); - } - else - { - context->CurrentAddress /= context->BytesPerRow; - context->CurrentAddress *= context->BytesPerRow; - context->CurrentAddress += context->BytesPerRow - 1; - - if (context->CurrentAddress > context->Length) - context->CurrentAddress = context->Length - 1; - } - - PhpHexEditMove(hwnd, context, 0, 0); - context->SelEnd = context->CurrentAddress; - - REDRAW_WINDOW(hwnd); - break; - } - else - { - PhpHexEditSetSel(hwnd, context, -1, -1); - } - - if (GetKeyState(VK_CONTROL) < 0) - { - context->CurrentAddress = context->Length - 1; - - if (context->HalfPage) - { - SendMessage(hwnd, WM_VSCROLL, 0, 0); - } - else - { - SendMessage(hwnd, WM_VSCROLL, - MAKEWPARAM(SB_THUMBTRACK, ((context->Length + (context->BytesPerRow / 2)) / context->BytesPerRow) - context->LinesPerPage), - 0); - } - } - else - { - context->CurrentAddress /= context->BytesPerRow; - context->CurrentAddress *= context->BytesPerRow; - context->CurrentAddress += context->BytesPerRow - 1; - - if (context->CurrentAddress > context->Length) - context->CurrentAddress = context->Length - 1; - } - - PhpHexEditMove(hwnd, context, 0, 0); - noScrollIntoView = TRUE; - - break; - case VK_INSERT: - PhpHexEditSelInsert(hwnd, context, context->CurrentAddress, - max(1, context->SelEnd - context->SelStart)); - REDRAW_WINDOW(hwnd); - break; - case VK_DELETE: - if (PhpHexEditHasSelected(context)) - { - PhpHexEditClearEdit(hwnd, context); - } - else - { - PhpHexEditSelDelete(hwnd, context, context->CurrentAddress, context->CurrentAddress + 1); - REDRAW_WINDOW(hwnd); - } - break; - case '\t': - switch (context->CurrentMode) - { - case EDIT_NONE: - context->CurrentMode = EDIT_HIGH; - break; - case EDIT_HIGH: - case EDIT_LOW: - context->CurrentMode = EDIT_ASCII; - break; - case EDIT_ASCII: - context->CurrentMode = EDIT_HIGH; - break; - } - - PhpHexEditMove(hwnd, context, 0, 0); - - break; - } - - // Scroll into view if not in view. - if ( - !noScrollIntoView && - (context->CurrentAddress < context->TopIndex || - context->CurrentAddress >= context->TopIndex + context->LinesPerPage * context->BytesPerRow) - ) - { - PhpHexEditScrollTo(hwnd, context, context->CurrentAddress); - } - - context->NoAddressChange = oldNoAddressChange; - } - break; - case HEM_SETBUFFER: - { - PhpHexEditSetBuffer(hwnd, context, (PUCHAR)lParam, (ULONG)wParam); - } - return TRUE; - case HEM_SETDATA: - { - PhpHexEditSetData(hwnd, context, (PUCHAR)lParam, (ULONG)wParam); - } - return TRUE; - case HEM_GETBUFFER: - { - PULONG length = (PULONG)wParam; - - if (length) - *length = context->Length; - - return (LPARAM)context->Data; - } - case HEM_SETSEL: - { - LONG selStart = (LONG)wParam; - LONG selEnd = (LONG)lParam; - - if (selStart <= 0) - return FALSE; - if (selEnd > context->Length) - return FALSE; - - PhpHexEditScrollTo(hwnd, context, selStart); - PhpHexEditSetSel(hwnd, context, selStart, selEnd); - PhpHexEditRepositionCaret(hwnd, context, selStart); - REDRAW_WINDOW(hwnd); - } - return TRUE; - case HEM_SETEDITMODE: - { - context->CurrentMode = (LONG)wParam; - REDRAW_WINDOW(hwnd); - } - return TRUE; - case HEM_SETBYTESPERROW: - { - LONG bytesPerRow = (LONG)wParam; - - if (bytesPerRow >= 4) - { - context->BytesPerRow = bytesPerRow; - PhpHexEditUpdateMetrics(hwnd, context, TRUE, NULL); - PhpHexEditUpdateScrollbars(hwnd, context); - PhpHexEditScrollTo(hwnd, context, context->CurrentAddress); - PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); - REDRAW_WINDOW(hwnd); - } - } - return TRUE; - } - -DefaultHandler: - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -FORCEINLINE VOID PhpPrintHex( - _In_ HDC hdc, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _Inout_ PWCHAR Buffer, - _In_ UCHAR Byte, - _Inout_ PLONG X, - _Inout_ PLONG Y, - _Inout_ PULONG N - ) -{ - PWCHAR p = Buffer; - - TO_HEX(p, Byte); - *p++ = ' '; - TextOut(hdc, *X, *Y, Buffer, 3); - *X += Context->NullWidth * 3; - (*N)++; - - if (*N == Context->BytesPerRow) - { - *N = 0; - *X = Context->HexOffset; - *Y += Context->LineHeight; - } -} - -FORCEINLINE VOID PhpPrintAscii( - _In_ HDC hdc, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ UCHAR Byte, - _Inout_ PLONG X, - _Inout_ PLONG Y, - _Inout_ PULONG N - ) -{ - WCHAR c; - - c = IS_PRINTABLE(Byte) ? Byte : '.'; - TextOut(hdc, *X, *Y, &c, 1); - *X += Context->NullWidth; - (*N)++; - - if (*N == Context->BytesPerRow) - { - *N = 0; - *X = Context->AsciiOffset; - *Y += Context->LineHeight; - } -} - -FORCEINLINE COLORREF GetLighterHighlightColor( - VOID - ) -{ - COLORREF color; - UCHAR r; - UCHAR g; - UCHAR b; - - color = GetSysColor(COLOR_HIGHLIGHT); - r = (UCHAR)color; - g = (UCHAR)(color >> 8); - b = (UCHAR)(color >> 16); - - if (r <= 255 - 64) - r += 64; - else - r = 255; - - if (g <= 255 - 64) - g += 64; - else - g = 255; - - if (b <= 255 - 64) - b += 64; - else - b = 255; - - return RGB(r, g, b); -} - -VOID PhpHexEditUpdateMetrics( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ BOOLEAN UpdateLineHeight, - _In_opt_ HDC hdc - ) -{ - BOOLEAN freeHdc = FALSE; - RECT clientRect; - SIZE size; - - if (!hdc && UpdateLineHeight) - { - hdc = CreateCompatibleDC(hdc); - SelectObject(hdc, Context->Font); - freeHdc = TRUE; - } - - GetClientRect(hwnd, &clientRect); - - if (UpdateLineHeight) - { - GetCharWidth(hdc, '0', '0', &Context->NullWidth); - GetTextExtentPoint32(hdc, L"0", 1, &size); - Context->LineHeight = size.cy; - } - - Context->HexOffset = Context->ShowAddress ? (Context->AddressIsWide ? Context->NullWidth * 9 : Context->NullWidth * 5) : 0; - Context->AsciiOffset = Context->HexOffset + (Context->ShowHex ? (Context->BytesPerRow * 3 * Context->NullWidth) : 0); - - if (Context->LineHeight != 0) - { - Context->LinesPerPage = clientRect.bottom / Context->LineHeight; - Context->HalfPage = FALSE; - - if (Context->LinesPerPage * Context->BytesPerRow > Context->Length) - { - Context->LinesPerPage = (Context->Length + Context->BytesPerRow / 2) / Context->BytesPerRow; - - if (Context->Length % Context->BytesPerRow != 0) - { - Context->HalfPage = TRUE; - Context->LinesPerPage++; - } - } - } - - if (freeHdc && hdc) - DeleteDC(hdc); -} - -VOID PhpHexEditOnPaint( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ PAINTSTRUCT *PaintStruct, - _In_ HDC hdc - ) -{ - RECT clientRect; - HDC bufferDc; - HBITMAP bufferBitmap; - HBITMAP oldBufferBitmap; - LONG height; - LONG x; - LONG y; - LONG i; - ULONG requiredBufferLength; - PWCHAR buffer; - - GetClientRect(hwnd, &clientRect); - - bufferDc = CreateCompatibleDC(hdc); - bufferBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom); - oldBufferBitmap = SelectObject(bufferDc, bufferBitmap); - - SetDCBrushColor(bufferDc, GetSysColor(COLOR_WINDOW)); - FillRect(bufferDc, &clientRect, GetStockObject(DC_BRUSH)); - SelectObject(bufferDc, Context->Font); - SetBoundsRect(bufferDc, &clientRect, DCB_DISABLE); - - requiredBufferLength = (max(8, Context->BytesPerRow * 3) + 1) * sizeof(WCHAR); - - if (Context->CharBufferLength < requiredBufferLength) - { - if (Context->CharBuffer) - PhFree(Context->CharBuffer); - - Context->CharBuffer = PhAllocate(requiredBufferLength); - Context->CharBufferLength = requiredBufferLength; - buffer = Context->CharBuffer; - } - - buffer = Context->CharBuffer; - - if (Context->Data) - { - // Get character dimensions. - if (Context->Update) - { - PhpHexEditUpdateMetrics(hwnd, Context, TRUE, bufferDc); - Context->Update = FALSE; - PhpHexEditUpdateScrollbars(hwnd, Context); - } - - height = (clientRect.bottom + Context->LineHeight - 1) / Context->LineHeight * Context->LineHeight; // round up to height - - if (Context->ShowAddress) - { - PH_FORMAT format; - ULONG w; - RECT rect; - - PhInitFormatX(&format, 0); - format.Type |= FormatPadZeros; - format.Width = Context->AddressIsWide ? 8 : 4; - - w = Context->AddressIsWide ? 8 : 4; - - rect = clientRect; - rect.left = Context->AddressOffset; - rect.top = 0; - - for (i = Context->TopIndex; i < Context->Length && rect.top < height; i += Context->BytesPerRow) - { - format.u.Int32 = i; - PhFormatToBuffer(&format, 1, buffer, requiredBufferLength, NULL); - DrawText(bufferDc, buffer, w, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP); - rect.top += Context->LineHeight; - } - } - - if (Context->ShowHex) - { - RECT rect; - LONG n = 0; - - x = Context->HexOffset; - y = 0; - rect = clientRect; - rect.left = x; - rect.top = 0; - - if (Context->SelStart != -1) - { - COLORREF highlightColor; - LONG selStart; - LONG selEnd; - - if (Context->CurrentMode == EDIT_HIGH || Context->CurrentMode == EDIT_LOW) - highlightColor = GetSysColor(COLOR_HIGHLIGHT); - else - highlightColor = GetLighterHighlightColor(); - - selStart = Context->SelStart; - selEnd = Context->SelEnd; - - if (selStart > selEnd) - { - ULONG t; - - t = selEnd; - selEnd = selStart; - selStart = t; - } - - if (selStart >= Context->Length) - selStart = Context->Length - 1; - if (selEnd > Context->Length) - selEnd = Context->Length; - - // Bytes before the selection - - for (i = Context->TopIndex; i < selStart && y < height; i++) - { - PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n); - } - - // Bytes in the selection - - SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkColor(bufferDc, highlightColor); - - for (; i < selEnd && i < Context->Length && y < height; i++) - { - PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n); - } - - // Bytes after the selection - - SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); - SetBkColor(bufferDc, GetSysColor(COLOR_WINDOW)); - - for (; i < Context->Length && y < height; i++) - { - PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n); - } - } - else - { - i = Context->TopIndex; - - while (i < Context->Length && rect.top < height) - { - PWCHAR p = buffer; - - for (n = 0; n < Context->BytesPerRow && i < Context->Length; n++) - { - TO_HEX(p, Context->Data[i]); - *p++ = ' '; - i++; - } - - while (n < Context->BytesPerRow) - { - p[0] = ' '; - p[1] = ' '; - p[2] = ' '; - p += 3; - n++; - } - - DrawText(bufferDc, buffer, Context->BytesPerRow * 3, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP); - rect.top += Context->LineHeight; - } - } - } - - if (Context->ShowAscii) - { - RECT rect; - LONG n = 0; - - x = Context->AsciiOffset; - y = 0; - rect = clientRect; - rect.left = x; - rect.top = 0; - - if (Context->SelStart != -1) - { - COLORREF highlightColor; - LONG selStart; - LONG selEnd; - - if (Context->CurrentMode == EDIT_ASCII) - highlightColor = GetSysColor(COLOR_HIGHLIGHT); - else - highlightColor = GetLighterHighlightColor(); - - selStart = Context->SelStart; - selEnd = Context->SelEnd; - - if (selStart > selEnd) - { - LONG t; - - t = selEnd; - selEnd = selStart; - selStart = t; - } - - if (selStart >= Context->Length) - selStart = Context->Length - 1; - if (selEnd > Context->Length) - selEnd = Context->Length; - - // Bytes before the selection - - for (i = Context->TopIndex; i < selStart && y < height; i++) - { - PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n); - } - - // Bytes in the selection - - SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkColor(bufferDc, highlightColor); - - for (; i < selEnd && i < Context->Length && y < height; i++) - { - PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n); - } - - // Bytes after the selection - - SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); - SetBkColor(bufferDc, GetSysColor(COLOR_WINDOW)); - - for (; i < Context->Length && y < height; i++) - { - PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n); - } - } - else - { - i = Context->TopIndex; - - while (i < Context->Length && rect.top < height) - { - PWCHAR p = buffer; - - for (n = 0; n < Context->BytesPerRow && i < Context->Length; n++) - { - *p++ = IS_PRINTABLE(Context->Data[i]) ? Context->Data[i] : '.'; // 1 - i++; - } - - DrawText(bufferDc, buffer, n, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP); - rect.top += Context->LineHeight; - } - } - } - } - - BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, bufferDc, 0, 0, SRCCOPY); - SelectObject(bufferDc, oldBufferBitmap); - DeleteObject(bufferBitmap); - DeleteDC(bufferDc); -} - -VOID PhpHexEditUpdateScrollbars( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - SCROLLINFO si = { sizeof(si) }; - - si.fMask = SIF_ALL; - si.nMin = 0; - si.nMax = (Context->Length / Context->BytesPerRow) - 1; - si.nPage = Context->LinesPerPage; - si.nPos = Context->TopIndex / Context->BytesPerRow; - SetScrollInfo(hwnd, SB_VERT, &si, TRUE); - - if (si.nMax > (LONG)si.nPage) - EnableScrollBar(hwnd, SB_VERT, ESB_ENABLE_BOTH); - - // No horizontal scrollbar please. - /*si.nMin = 0; - si.nMax = ((Context->ShowAddress ? (Context->AddressIsWide ? 8 : 4) : 0) + - (Context->ShowHex ? Context->BytesPerRow * 3 : 0) + - (Context->ShowAscii ? Context->BytesPerRow : 0)) * Context->NullWidth; - si.nPage = 1; - si.nPos = 0; - SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);*/ -} - -VOID PhpHexEditCreateAddressCaret( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - DestroyCaret(); - CreateCaret(hwnd, NULL, Context->NullWidth * (Context->AddressIsWide ? 8 : 4), Context->LineHeight); -} - -VOID PhpHexEditCreateEditCaret( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - DestroyCaret(); - CreateCaret(hwnd, NULL, Context->NullWidth, Context->LineHeight); -} - -VOID PhpHexEditRepositionCaret( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG Position - ) -{ - ULONG x; - ULONG y; - RECT rect; - - x = (Position - Context->TopIndex) % Context->BytesPerRow; - y = (Position - Context->TopIndex) / Context->BytesPerRow; - - switch (Context->CurrentMode) - { - case EDIT_NONE: - PhpHexEditCreateAddressCaret(hwnd, Context); - x = 0; - break; - case EDIT_HIGH: - PhpHexEditCreateEditCaret(hwnd, Context); - x *= Context->NullWidth * 3; - x += Context->HexOffset; - break; - case EDIT_LOW: - PhpHexEditCreateEditCaret(hwnd, Context); - x *= Context->NullWidth * 3; - x += Context->NullWidth; - x += Context->HexOffset; - break; - case EDIT_ASCII: - PhpHexEditCreateEditCaret(hwnd, Context); - x *= Context->NullWidth; - x += Context->AsciiOffset; - break; - } - - Context->EditPosition.x = x; - Context->EditPosition.y = y * Context->LineHeight; - - GetClientRect(hwnd, &rect); - - if (PtInRect(&rect, Context->EditPosition)) - { - SetCaretPos(Context->EditPosition.x, Context->EditPosition.y); - ShowCaret(hwnd); - } -} - -VOID PhpHexEditCalculatePosition( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG X, - _In_ LONG Y, - _Out_ POINT *Point - ) -{ - LONG xp; - - Y /= Context->LineHeight; - - if (Y < 0 || Y >= Context->LinesPerPage) - { - Point->x = -1; - Point->y = -1; - return; - } - - if (Y * Context->BytesPerRow >= Context->Length) - { - Point->x = -1; - Point->y = -1; - return; - } - - X += Context->NullWidth; - X /= Context->NullWidth; - - if (Context->ShowAddress && X <= (Context->AddressIsWide ? 8 : 4)) - { - Context->CurrentAddress = Context->TopIndex + Context->BytesPerRow * Y; - Context->CurrentMode = EDIT_NONE; - - Point->x = 0; - Point->y = Y; - return; - } - - xp = Context->HexOffset / Context->NullWidth + Context->BytesPerRow * 3; - - if (Context->ShowHex && X < xp) - { - if (X % 3) - X--; - - Context->CurrentAddress = Context->TopIndex + - Context->BytesPerRow * Y + - (X - (Context->HexOffset / Context->NullWidth)) / 3; - Context->CurrentMode = ((X % 3) & 1) ? EDIT_LOW : EDIT_HIGH; - - Point->x = X; - Point->y = Y; - return; - } - - X--; // fix selection problem - - xp = Context->AsciiOffset / Context->NullWidth + Context->BytesPerRow; - - if (Context->ShowAscii && X * Context->NullWidth >= Context->AsciiOffset && X <= xp) - { - Context->CurrentAddress = Context->TopIndex + - Context->BytesPerRow * Y + - (X - (Context->AsciiOffset / Context->NullWidth)); - Context->CurrentMode = EDIT_ASCII; - - Point->x = X; - Point->y = Y; - return; - } - - Point->x = -1; - Point->y = -1; -} - -VOID PhpHexEditMove( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG X, - _In_ LONG Y - ) -{ - switch (Context->CurrentMode) - { - case EDIT_NONE: - Context->CurrentAddress += Y * Context->BytesPerRow; - break; - case EDIT_HIGH: - if (X != 0) - Context->CurrentMode = EDIT_LOW; - if (X == -1) - Context->CurrentAddress--; - Context->CurrentAddress += Y * Context->BytesPerRow; - break; - case EDIT_LOW: - if (X != 0) - Context->CurrentMode = EDIT_HIGH; - if (X == 1) - Context->CurrentAddress++; - Context->CurrentAddress += Y * Context->BytesPerRow; - break; - case EDIT_ASCII: - Context->CurrentAddress += X; - Context->CurrentAddress += Y * Context->BytesPerRow; - break; - } - - if (Context->CurrentAddress < 0) - Context->CurrentAddress = 0; - - if (Context->CurrentAddress >= Context->Length) - { - Context->CurrentAddress -= X; - Context->CurrentAddress -= Y * Context->BytesPerRow; - } - - Context->NoAddressChange = TRUE; - - if (Context->CurrentAddress < Context->TopIndex) - SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); - if (Context->CurrentAddress >= Context->TopIndex + Context->LinesPerPage * Context->BytesPerRow) - SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); - - Context->NoAddressChange = FALSE; - PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress); -} - -VOID PhpHexEditSetSel( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG S, - _In_ LONG E - ) -{ - DestroyCaret(); - Context->SelStart = S; - Context->SelEnd = E; - REDRAW_WINDOW(hwnd); - - if (S != -1 && E != -1) - { - Context->CurrentAddress = S; - } - else - { - if (Context->EditPosition.x == 0 && Context->ShowAddress) - PhpHexEditCreateAddressCaret(hwnd, Context); - else - PhpHexEditCreateEditCaret(hwnd, Context); - - SetCaretPos(Context->EditPosition.x, Context->EditPosition.y); - ShowCaret(hwnd); - } -} - -VOID PhpHexEditScrollTo( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG Position - ) -{ - if (Position < Context->TopIndex || Position > Context->TopIndex + Context->LinesPerPage * Context->BytesPerRow) - { - Context->TopIndex = Position / Context->BytesPerRow * Context->BytesPerRow; // round down - Context->TopIndex -= Context->LinesPerPage / 3 * Context->BytesPerRow; - - if (Context->TopIndex < 0) - Context->TopIndex = 0; - - if (Context->Length >= Context->LinesPerPage * Context->BytesPerRow) - { - if (Context->TopIndex > Context->Length - Context->LinesPerPage * Context->BytesPerRow) - Context->TopIndex = Context->Length - Context->LinesPerPage * Context->BytesPerRow; - } - - PhpHexEditUpdateScrollbars(hwnd, Context); - REDRAW_WINDOW(hwnd); - } -} - -VOID PhpHexEditClearEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - if (Context->AllowLengthChange) - { - Context->CurrentAddress = Context->SelStart; - PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd); - PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress); - REDRAW_WINDOW(hwnd); - } -} - -VOID PhpHexEditCopyEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - if (OpenClipboard(hwnd)) - { - EmptyClipboard(); - PhpHexEditNormalizeSel(hwnd, Context); - - if (Context->CurrentMode != EDIT_ASCII) - { - ULONG length = Context->SelEnd - Context->SelStart; - HGLOBAL binaryMemory; - HGLOBAL hexMemory; - - binaryMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length); - - if (binaryMemory) - { - PUCHAR p = GlobalLock(binaryMemory); - memcpy(p, &Context->Data[Context->SelStart], length); - GlobalUnlock(binaryMemory); - - hexMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (length * 3 + 1) * sizeof(WCHAR)); - - if (hexMemory) - { - PWCHAR pw; - ULONG i; - - pw = GlobalLock(hexMemory); - - for (i = 0; i < length; i++) - { - TO_HEX(pw, Context->Data[Context->SelStart + i]); - *pw++ = ' '; - } - *pw = 0; - - GlobalUnlock(hexMemory); - - SetClipboardData(CF_UNICODETEXT, hexMemory); - } - - SetClipboardData(RegisterClipboardFormat(L"BinaryData"), binaryMemory); - } - } - else - { - ULONG length = Context->SelEnd - Context->SelStart; - HGLOBAL binaryMemory; - HGLOBAL asciiMemory; - - binaryMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length); - asciiMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length + 1); - - if (binaryMemory) - { - PUCHAR p = GlobalLock(binaryMemory); - memcpy(p, &Context->Data[Context->SelStart], length); - GlobalUnlock(binaryMemory); - - if (asciiMemory) - { - ULONG i; - - p = GlobalLock(asciiMemory); - memcpy(p, &Context->Data[Context->SelStart], length); - - for (i = 0; i < length; i++) - { - if (!IS_PRINTABLE(*p)) - *p = '.'; - p++; - } - *p = 0; - - GlobalUnlock(asciiMemory); - - SetClipboardData(CF_TEXT, asciiMemory); - } - - SetClipboardData(RegisterClipboardFormat(L"BinaryData"), binaryMemory); - } - } - - CloseClipboard(); - } -} - -VOID PhpHexEditCutEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - if (Context->AllowLengthChange) - { - PhpHexEditCopyEdit(hwnd, Context); - PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd); - REDRAW_WINDOW(hwnd); - } -} - -VOID PhpHexEditPasteEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - if (OpenClipboard(hwnd)) - { - HANDLE memory; - - memory = GetClipboardData(RegisterClipboardFormat(L"BinaryData")); - - if (!memory) - memory = GetClipboardData(CF_TEXT); - - if (memory) - { - PUCHAR p = GlobalLock(memory); - ULONG length = (ULONG)GlobalSize(memory); - ULONG paste; - ULONG oldCurrentAddress = Context->CurrentAddress; - - PhpHexEditNormalizeSel(hwnd, Context); - - if (Context->AllowLengthChange) - { - if (Context->SelStart == -1) - { - if (Context->CurrentMode == EDIT_LOW) - Context->CurrentAddress++; - - paste = Context->CurrentAddress; - PhpHexEditSelInsert(hwnd, Context, Context->CurrentAddress, length); - } - else - { - paste = Context->SelStart; - PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd); - PhpHexEditSelInsert(hwnd, Context, paste, length); - PhpHexEditSetSel(hwnd, Context, -1, -1); - } - } - else - { - if (Context->SelStart == -1) - { - if (Context->CurrentMode == EDIT_LOW) - Context->CurrentAddress++; - - paste = Context->CurrentAddress; - } - else - { - paste = Context->SelStart; - } - - if (length > Context->Length - paste) - length = Context->Length - paste; - } - - memcpy(&Context->Data[paste], p, length); - GlobalUnlock(memory); - - Context->CurrentAddress = oldCurrentAddress; - REDRAW_WINDOW(hwnd); - } - - CloseClipboard(); - } -} - -VOID PhpHexEditSelectAll( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - Context->SelStart = 0; - Context->SelEnd = Context->Length; - DestroyCaret(); - REDRAW_WINDOW(hwnd); -} - -VOID PhpHexEditUndoEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - // TODO -} - -VOID PhpHexEditNormalizeSel( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - if (Context->SelStart > Context->SelEnd) - { - LONG t; - - t = Context->SelEnd; - Context->SelEnd = Context->SelStart; - Context->SelStart = t; - } -} - -VOID PhpHexEditSelDelete( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG S, - _In_ LONG E - ) -{ - if (Context->AllowLengthChange && Context->Length > 0) - { - PUCHAR p = PhAllocate(Context->Length - (E - S) + 1); - - memcpy(p, Context->Data, S); - - if (S < Context->Length - (E - S)) - memcpy(&p[S], &Context->Data[E], Context->Length - E); - - PhFree(Context->Data); - Context->Data = p; - PhpHexEditSetSel(hwnd, Context, -1, -1); - Context->Length -= E - S; - - if (Context->CurrentAddress > Context->Length) - { - Context->CurrentAddress = Context->Length; - PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress); - } - - Context->Update = TRUE; - } -} - -VOID PhpHexEditSelInsert( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG S, - _In_ LONG L - ) -{ - if (Context->AllowLengthChange) - { - PUCHAR p = PhAllocate(Context->Length + L); - - memset(p, 0, Context->Length + L); - memcpy(p, Context->Data, S); - memcpy(&p[S + L], &Context->Data[S], Context->Length - S); - - PhFree(Context->Data); - Context->Data = p; - PhpHexEditSetSel(hwnd, Context, -1, -1); - Context->Length += L; - - Context->Update = TRUE; - } -} - -VOID PhpHexEditSetBuffer( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ PUCHAR Data, - _In_ ULONG Length - ) -{ - Context->Data = Data; - PhpHexEditSetSel(hwnd, Context, -1, -1); - Context->Length = Length; - Context->CurrentAddress = 0; - Context->EditPosition.x = Context->EditPosition.y = 0; - Context->CurrentMode = EDIT_HIGH; - Context->TopIndex = 0; - Context->Update = TRUE; - - Context->UserBuffer = TRUE; - Context->AllowLengthChange = FALSE; -} - -VOID PhpHexEditSetData( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ PUCHAR Data, - _In_ ULONG Length - ) -{ - if (Context->Data) PhFree(Context->Data); - Context->Data = PhAllocate(Length); - memcpy(Context->Data, Data, Length); - PhpHexEditSetBuffer(hwnd, Context, Context->Data, Length); - Context->UserBuffer = FALSE; - Context->AllowLengthChange = TRUE; -} +/* + * Process Hacker - + * hex editor control + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +// Code originally from http://www.codeguru.com/Cpp/controls/editctrl/article.php/c539 + +BOOLEAN PhHexEditInitialization( + VOID + ) +{ + WNDCLASSEX c; + + memset(&c, 0, sizeof(WNDCLASSEX)); + c.cbSize = sizeof(WNDCLASSEX); + c.lpszClassName = PH_HEXEDIT_CLASSNAME; + c.style = CS_GLOBALCLASS; + c.cbWndExtra = sizeof(PVOID); + c.hInstance = PhInstanceHandle; + c.lpfnWndProc = PhpHexEditWndProc; + c.hCursor = LoadCursor(NULL, IDC_ARROW); + + if (!RegisterClassEx(&c)) + return FALSE; + + return TRUE; +} + +VOID PhpCreateHexEditContext( + _Out_ PPHP_HEXEDIT_CONTEXT *Context + ) +{ + PPHP_HEXEDIT_CONTEXT context; + + context = PhAllocate(sizeof(PHP_HEXEDIT_CONTEXT)); + memset(context, 0, sizeof(PHP_HEXEDIT_CONTEXT)); // important, set NullWidth to 0 + + context->Data = NULL; + context->Length = 0; + context->TopIndex = 0; + context->BytesPerRow = 16; + context->LinesPerPage = 1; + + context->ShowHex = TRUE; + context->ShowAscii = TRUE; + context->ShowAddress = TRUE; + context->AddressIsWide = TRUE; + context->AllowLengthChange = FALSE; + + context->AddressOffset = 0; + context->HexOffset = 0; + context->AsciiOffset = 0; + + context->Update = TRUE; + context->NoAddressChange = FALSE; + context->CurrentMode = EDIT_NONE; + + context->EditPosition.x = 0; + context->EditPosition.y = 0; + context->CurrentAddress = 0; + context->HalfPage = TRUE; + + context->SelStart = -1; + context->SelEnd = -1; + + *Context = context; +} + +VOID PhpFreeHexEditContext( + _In_ _Post_invalid_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + if (!Context->UserBuffer && Context->Data) PhFree(Context->Data); + if (Context->CharBuffer) PhFree(Context->CharBuffer); + if (Context->Font) DeleteFont(Context->Font); + PhFree(Context); +} + +LRESULT CALLBACK PhpHexEditWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_HEXEDIT_CONTEXT context; + + context = (PPHP_HEXEDIT_CONTEXT)GetWindowLongPtr(hwnd, 0); + + if (uMsg == WM_CREATE) + { + PhpCreateHexEditContext(&context); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); + } + + if (!context) + return DefWindowProc(hwnd, uMsg, wParam, lParam); + + switch (uMsg) + { + case WM_CREATE: + { + context->Font = CreateFont(-(LONG)PhMultiplyDivide(12, PhGlobalDpi, 96), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Courier New"); + } + break; + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); + PhpFreeHexEditContext(context); + } + break; + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + HDC hdc; + + if (hdc = BeginPaint(hwnd, &paintStruct)) + { + PhpHexEditOnPaint(hwnd, context, &paintStruct, hdc); + EndPaint(hwnd, &paintStruct); + } + } + break; + case WM_SIZE: + { + PhpHexEditUpdateMetrics(hwnd, context, FALSE, NULL); + } + break; + case WM_SETFOCUS: + { + if (context->Data && !PhpHexEditHasSelected(context)) + { + if (context->EditPosition.x == 0 && context->ShowAddress) + PhpHexEditCreateAddressCaret(hwnd, context); + else + PhpHexEditCreateEditCaret(hwnd, context); + + SetCaretPos(context->EditPosition.x, context->EditPosition.y); + ShowCaret(hwnd); + } + } + break; + case WM_KILLFOCUS: + { + DestroyCaret(); + } + break; + case WM_VSCROLL: + { + SHORT scrollRequest = LOWORD(wParam); + LONG currentPosition; + LONG originalTopIndex; + SCROLLINFO scrollInfo = { sizeof(scrollInfo) }; + + originalTopIndex = context->TopIndex; + + scrollInfo.fMask = SIF_TRACKPOS; + GetScrollInfo(hwnd, SB_VERT, &scrollInfo); + currentPosition = scrollInfo.nTrackPos; + + if (context->Data) + { + LONG mult; + + mult = context->LinesPerPage * context->BytesPerRow; + + switch (scrollRequest) + { + case SB_LINEDOWN: + if (context->TopIndex < context->Length - mult) + { + context->TopIndex += context->BytesPerRow; + REDRAW_WINDOW(hwnd); + } + break; + case SB_LINEUP: + if (context->TopIndex >= context->BytesPerRow) + { + context->TopIndex -= context->BytesPerRow; + REDRAW_WINDOW(hwnd); + } + break; + case SB_PAGEDOWN: + if (context->TopIndex < context->Length - mult) + { + LONG pageEnd = 0; + + while (pageEnd < context->Length - mult) + pageEnd += context->BytesPerRow; + + context->TopIndex += mult; + + if (context->TopIndex > pageEnd) + context->TopIndex = pageEnd; + + REDRAW_WINDOW(hwnd); + } + break; + case SB_PAGEUP: + if (context->TopIndex > 0) + { + context->TopIndex -= mult; + + if (context->TopIndex < 0) + context->TopIndex = 0; + + REDRAW_WINDOW(hwnd); + } + break; + case SB_THUMBTRACK: + context->TopIndex = currentPosition * context->BytesPerRow; + REDRAW_WINDOW(hwnd); + break; + case SB_TOP: + context->TopIndex = 0; + REDRAW_WINDOW(hwnd); + break; + case SB_BOTTOM: + while (context->TopIndex < context->Length - mult) + context->TopIndex += context->BytesPerRow; + REDRAW_WINDOW(hwnd); + break; + } + + SetScrollPos(hwnd, SB_VERT, context->TopIndex / context->BytesPerRow, TRUE); + + if (!context->NoAddressChange && FALSE) // this behaviour sucks, so just leave it out + context->CurrentAddress += context->TopIndex - originalTopIndex; + + PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); + } + } + break; + case WM_MOUSEWHEEL: + { + SHORT wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); + + if (context->Data) + { + ULONG wheelScrollLines; + + if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScrollLines, 0)) + wheelScrollLines = 3; + + context->TopIndex += context->BytesPerRow * (LONG)wheelScrollLines * -wheelDelta / WHEEL_DELTA; + + if (context->TopIndex < 0) + context->TopIndex = 0; + + if (context->Length >= context->LinesPerPage * context->BytesPerRow) + { + if (context->TopIndex > context->Length - context->LinesPerPage * context->BytesPerRow) + context->TopIndex = context->Length - context->LinesPerPage * context->BytesPerRow; + } + + REDRAW_WINDOW(hwnd); + + SetScrollPos(hwnd, SB_VERT, context->TopIndex / context->BytesPerRow, TRUE); + + PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); + } + } + break; + case WM_GETDLGCODE: + if (wParam != VK_ESCAPE) + return DLGC_WANTALLKEYS; + break; + case WM_ERASEBKGND: + return 1; + case WM_LBUTTONDOWN: + { + ULONG flags = (ULONG)wParam; + POINT cursorPos; + + cursorPos.x = GET_X_LPARAM(lParam); + cursorPos.y = GET_Y_LPARAM(lParam); + + SetFocus(hwnd); + + if (context->Data) + { + POINT point; + + if (wParam & MK_SHIFT) + context->SelStart = context->CurrentAddress; + + PhpHexEditCalculatePosition(hwnd, context, cursorPos.x, cursorPos.y, &point); + + if (point.x > -1) + { + context->EditPosition = point; + + point.x *= context->NullWidth; + point.y *= context->LineHeight; + + if (point.x == 0 && context->ShowAddress) + PhpHexEditCreateAddressCaret(hwnd, context); + else + PhpHexEditCreateEditCaret(hwnd, context); + + SetCaretPos(point.x, point.y); + + if (flags & MK_SHIFT) + { + context->SelEnd = context->CurrentAddress; + + if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) + context->SelEnd++; + + REDRAW_WINDOW(hwnd); + } + } + + if (!(flags & MK_SHIFT)) + { + if (DragDetect(hwnd, cursorPos)) + { + context->SelStart = context->CurrentAddress; + context->SelEnd = context->SelStart; + SetCapture(hwnd); + context->HasCapture = TRUE; + } + else + { + BOOLEAN selected; + + selected = context->SelStart != -1; + context->SelStart = -1; + context->SelEnd = -1; + + if (selected) + REDRAW_WINDOW(hwnd); + } + } + + if (!PhpHexEditHasSelected(context)) + ShowCaret(hwnd); + } + } + break; + case WM_LBUTTONUP: + { + if (context->HasCapture && PhpHexEditHasSelected(context)) + ReleaseCapture(); + + context->HasCapture = FALSE; + } + break; + case WM_MOUSEMOVE: + { + ULONG flags = (ULONG)wParam; + POINT cursorPos; + + cursorPos.x = GET_X_LPARAM(lParam); + cursorPos.y = GET_Y_LPARAM(lParam); + + if ( + context->Data && + context->HasCapture && + context->SelStart != -1 + ) + { + RECT rect; + POINT point; + ULONG oldSelEnd; + + // User is dragging. + + GetClientRect(hwnd, &rect); + + if (!PtInRect(&rect, cursorPos)) + { + if (cursorPos.y < 0) + { + SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); + cursorPos.y = 0; + } + else if (cursorPos.y > rect.bottom) + { + SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); + cursorPos.y = rect.bottom - 1; + } + } + + oldSelEnd = context->SelEnd; + PhpHexEditCalculatePosition(hwnd, context, cursorPos.x, cursorPos.y, &point); + + if (point.x > -1) + { + context->SelEnd = context->CurrentAddress; + + if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) + context->SelEnd++; + } + + if (PhpHexEditHasSelected(context)) + DestroyCaret(); + + if (context->SelEnd != oldSelEnd) + REDRAW_WINDOW(hwnd); + } + } + break; + case WM_CHAR: + { + ULONG c = (ULONG)wParam; + + if (!context->Data) + goto DefaultHandler; + if (c == '\t') + goto DefaultHandler; + + if (GetKeyState(VK_CONTROL) < 0) + { + switch (c) + { + case 0x3: + if (PhpHexEditHasSelected(context)) + PhpHexEditCopyEdit(hwnd, context); + goto DefaultHandler; + case 0x16: + PhpHexEditPasteEdit(hwnd, context); + goto DefaultHandler; + case 0x18: + if (PhpHexEditHasSelected(context)) + PhpHexEditCutEdit(hwnd, context); + goto DefaultHandler; + case 0x1a: + PhpHexEditUndoEdit(hwnd, context); + goto DefaultHandler; + } + } + + // Disallow editing beyond the end of the data. + if (context->CurrentAddress >= context->Length) + goto DefaultHandler; + + if (c == 0x8) + { + if (context->CurrentAddress != 0) + { + context->CurrentAddress--; + PhpHexEditSelDelete(hwnd, context, context->CurrentAddress, context->CurrentAddress + 1); + PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); + REDRAW_WINDOW(hwnd); + } + + goto DefaultHandler; + } + + PhpHexEditSetSel(hwnd, context, -1, -1); + + switch (context->CurrentMode) + { + case EDIT_NONE: + goto DefaultHandler; + case EDIT_HIGH: + case EDIT_LOW: + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) + { + ULONG b = c - '0'; + + if (b > 9) + b = 10 + c - 'a'; + + if (context->CurrentMode == EDIT_HIGH) + { + context->Data[context->CurrentAddress] = + (UCHAR)((context->Data[context->CurrentAddress] & 0x0f) | (b << 4)); + } + else + { + context->Data[context->CurrentAddress] = + (UCHAR)((context->Data[context->CurrentAddress] & 0xf0) | b); + } + + PhpHexEditMove(hwnd, context, 1, 0); + } + break; + case EDIT_ASCII: + context->Data[context->CurrentAddress] = (UCHAR)c; + PhpHexEditMove(hwnd, context, 1, 0); + break; + } + + REDRAW_WINDOW(hwnd); + } + break; + case WM_KEYDOWN: + { + ULONG vk = (ULONG)wParam; + BOOLEAN shift = GetKeyState(VK_SHIFT) < 0; + BOOLEAN oldNoAddressChange = context->NoAddressChange; + BOOLEAN noScrollIntoView = FALSE; + + context->NoAddressChange = TRUE; + + switch (vk) + { + case VK_DOWN: + if (context->CurrentMode != EDIT_NONE) + { + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + PhpHexEditMove(hwnd, context, 0, 1); + context->SelEnd = context->CurrentAddress; + + if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) + context->SelEnd++; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + PhpHexEditMove(hwnd, context, 0, 1); + noScrollIntoView = TRUE; + } + else + { + PhpHexEditMove(hwnd, context, 0, 1); + } + break; + case VK_UP: + if (context->CurrentMode != EDIT_NONE) + { + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + PhpHexEditMove(hwnd, context, 0, -1); + context->SelEnd = context->CurrentAddress; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + PhpHexEditMove(hwnd, context, 0, -1); + noScrollIntoView = TRUE; + } + else + { + PhpHexEditMove(hwnd, context, 0, -1); + } + break; + case VK_LEFT: + if (context->CurrentMode != EDIT_NONE) + { + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + PhpHexEditMove(hwnd, context, -1, 0); + context->SelEnd = context->CurrentAddress; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + PhpHexEditMove(hwnd, context, -1, 0); + noScrollIntoView = TRUE; + } + break; + case VK_RIGHT: + if (context->CurrentMode != EDIT_NONE) + { + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + PhpHexEditMove(hwnd, context, 1, 0); + context->SelEnd = context->CurrentAddress; + + if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW) + context->SelEnd++; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + PhpHexEditMove(hwnd, context, 1, 0); + noScrollIntoView = TRUE; + } + break; + case VK_PRIOR: + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); + PhpHexEditMove(hwnd, context, 0, 0); + context->SelEnd = context->CurrentAddress; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); + PhpHexEditMove(hwnd, context, 0, 0); + noScrollIntoView = TRUE; + break; + case VK_NEXT: + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); + PhpHexEditMove(hwnd, context, 0, 0); + context->SelEnd = context->CurrentAddress; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); + PhpHexEditMove(hwnd, context, 0, 0); + noScrollIntoView = TRUE; + break; + case VK_HOME: + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + if (GetKeyState(VK_CONTROL) < 0) + { + SendMessage(hwnd, WM_VSCROLL, SB_THUMBTRACK, 0); + } + else + { + // Round down. + context->CurrentAddress /= context->BytesPerRow; + context->CurrentAddress *= context->BytesPerRow; + } + + PhpHexEditMove(hwnd, context, 0, 0); + context->SelEnd = context->CurrentAddress; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + if (GetKeyState(VK_CONTROL) < 0) + { + SendMessage(hwnd, WM_VSCROLL, SB_THUMBTRACK, 0); + context->CurrentAddress = 0; + } + else + { + // Round down. + context->CurrentAddress /= context->BytesPerRow; + context->CurrentAddress *= context->BytesPerRow; + } + + PhpHexEditMove(hwnd, context, 0, 0); + noScrollIntoView = TRUE; + + break; + case VK_END: + if (shift) + { + if (!PhpHexEditHasSelected(context)) + context->SelStart = context->CurrentAddress; + + if (GetKeyState(VK_CONTROL) < 0) + { + context->CurrentAddress = context->Length - 1; + SendMessage(hwnd, WM_VSCROLL, + MAKEWPARAM(SB_THUMBTRACK, ((context->Length + (context->BytesPerRow / 2)) / context->BytesPerRow) - context->LinesPerPage), + 0); + } + else + { + context->CurrentAddress /= context->BytesPerRow; + context->CurrentAddress *= context->BytesPerRow; + context->CurrentAddress += context->BytesPerRow - 1; + + if (context->CurrentAddress > context->Length) + context->CurrentAddress = context->Length - 1; + } + + PhpHexEditMove(hwnd, context, 0, 0); + context->SelEnd = context->CurrentAddress; + + REDRAW_WINDOW(hwnd); + break; + } + else + { + PhpHexEditSetSel(hwnd, context, -1, -1); + } + + if (GetKeyState(VK_CONTROL) < 0) + { + context->CurrentAddress = context->Length - 1; + + if (context->HalfPage) + { + SendMessage(hwnd, WM_VSCROLL, 0, 0); + } + else + { + SendMessage(hwnd, WM_VSCROLL, + MAKEWPARAM(SB_THUMBTRACK, ((context->Length + (context->BytesPerRow / 2)) / context->BytesPerRow) - context->LinesPerPage), + 0); + } + } + else + { + context->CurrentAddress /= context->BytesPerRow; + context->CurrentAddress *= context->BytesPerRow; + context->CurrentAddress += context->BytesPerRow - 1; + + if (context->CurrentAddress > context->Length) + context->CurrentAddress = context->Length - 1; + } + + PhpHexEditMove(hwnd, context, 0, 0); + noScrollIntoView = TRUE; + + break; + case VK_INSERT: + PhpHexEditSelInsert(hwnd, context, context->CurrentAddress, + max(1, context->SelEnd - context->SelStart)); + REDRAW_WINDOW(hwnd); + break; + case VK_DELETE: + if (PhpHexEditHasSelected(context)) + { + PhpHexEditClearEdit(hwnd, context); + } + else + { + PhpHexEditSelDelete(hwnd, context, context->CurrentAddress, context->CurrentAddress + 1); + REDRAW_WINDOW(hwnd); + } + break; + case '\t': + switch (context->CurrentMode) + { + case EDIT_NONE: + context->CurrentMode = EDIT_HIGH; + break; + case EDIT_HIGH: + case EDIT_LOW: + context->CurrentMode = EDIT_ASCII; + break; + case EDIT_ASCII: + context->CurrentMode = EDIT_HIGH; + break; + } + + PhpHexEditMove(hwnd, context, 0, 0); + + break; + } + + // Scroll into view if not in view. + if ( + !noScrollIntoView && + (context->CurrentAddress < context->TopIndex || + context->CurrentAddress >= context->TopIndex + context->LinesPerPage * context->BytesPerRow) + ) + { + PhpHexEditScrollTo(hwnd, context, context->CurrentAddress); + } + + context->NoAddressChange = oldNoAddressChange; + } + break; + case HEM_SETBUFFER: + { + PhpHexEditSetBuffer(hwnd, context, (PUCHAR)lParam, (ULONG)wParam); + } + return TRUE; + case HEM_SETDATA: + { + PhpHexEditSetData(hwnd, context, (PUCHAR)lParam, (ULONG)wParam); + } + return TRUE; + case HEM_GETBUFFER: + { + PULONG length = (PULONG)wParam; + + if (length) + *length = context->Length; + + return (LPARAM)context->Data; + } + case HEM_SETSEL: + { + LONG selStart = (LONG)wParam; + LONG selEnd = (LONG)lParam; + + if (selStart <= 0) + return FALSE; + if (selEnd > context->Length) + return FALSE; + + PhpHexEditScrollTo(hwnd, context, selStart); + PhpHexEditSetSel(hwnd, context, selStart, selEnd); + PhpHexEditRepositionCaret(hwnd, context, selStart); + REDRAW_WINDOW(hwnd); + } + return TRUE; + case HEM_SETEDITMODE: + { + context->CurrentMode = (LONG)wParam; + REDRAW_WINDOW(hwnd); + } + return TRUE; + case HEM_SETBYTESPERROW: + { + LONG bytesPerRow = (LONG)wParam; + + if (bytesPerRow >= 4) + { + context->BytesPerRow = bytesPerRow; + PhpHexEditUpdateMetrics(hwnd, context, TRUE, NULL); + PhpHexEditUpdateScrollbars(hwnd, context); + PhpHexEditScrollTo(hwnd, context, context->CurrentAddress); + PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress); + REDRAW_WINDOW(hwnd); + } + } + return TRUE; + case HEM_SETEXTENDEDUNICODE: + { + context->ExtendedUnicode = !!(LONG)wParam; + } + return TRUE; + } + +DefaultHandler: + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +FORCEINLINE INT PhpIsPrintable( + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ UCHAR Byte + ) +{ + if (Context->ExtendedUnicode) + return iswctype(Byte, _PUNCT | _ALPHA | _DIGIT); // iswprint + else + return ((ULONG)((Byte)-' ') <= (ULONG)('~' - ' ')); +} + +FORCEINLINE VOID PhpPrintHex( + _In_ HDC hdc, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _Inout_ PWCHAR Buffer, + _In_ UCHAR Byte, + _Inout_ PLONG X, + _Inout_ PLONG Y, + _Inout_ PULONG N + ) +{ + PWCHAR p = Buffer; + + TO_HEX(p, Byte); + *p++ = ' '; + TextOut(hdc, *X, *Y, Buffer, 3); + *X += Context->NullWidth * 3; + (*N)++; + + if (*N == Context->BytesPerRow) + { + *N = 0; + *X = Context->HexOffset; + *Y += Context->LineHeight; + } +} + +FORCEINLINE VOID PhpPrintAscii( + _In_ HDC hdc, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ UCHAR Byte, + _Inout_ PLONG X, + _Inout_ PLONG Y, + _Inout_ PULONG N + ) +{ + WCHAR c; + + c = PhpIsPrintable(Context, Byte) ? Byte : '.'; + TextOut(hdc, *X, *Y, &c, 1); + *X += Context->NullWidth; + (*N)++; + + if (*N == Context->BytesPerRow) + { + *N = 0; + *X = Context->AsciiOffset; + *Y += Context->LineHeight; + } +} + +FORCEINLINE COLORREF GetLighterHighlightColor( + VOID + ) +{ + COLORREF color; + UCHAR r; + UCHAR g; + UCHAR b; + + color = GetSysColor(COLOR_HIGHLIGHT); + r = (UCHAR)color; + g = (UCHAR)(color >> 8); + b = (UCHAR)(color >> 16); + + if (r <= 255 - 64) + r += 64; + else + r = 255; + + if (g <= 255 - 64) + g += 64; + else + g = 255; + + if (b <= 255 - 64) + b += 64; + else + b = 255; + + return RGB(r, g, b); +} + +VOID PhpHexEditUpdateMetrics( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ BOOLEAN UpdateLineHeight, + _In_opt_ HDC hdc + ) +{ + BOOLEAN freeHdc = FALSE; + RECT clientRect; + SIZE size; + + if (!hdc && UpdateLineHeight) + { + hdc = CreateCompatibleDC(hdc); + SelectFont(hdc, Context->Font); + freeHdc = TRUE; + } + + GetClientRect(hwnd, &clientRect); + + if (UpdateLineHeight) + { + GetCharWidth(hdc, '0', '0', &Context->NullWidth); + GetTextExtentPoint32(hdc, L"0", 1, &size); + Context->LineHeight = size.cy; + } + + Context->HexOffset = Context->ShowAddress ? (Context->AddressIsWide ? Context->NullWidth * 9 : Context->NullWidth * 5) : 0; + Context->AsciiOffset = Context->HexOffset + (Context->ShowHex ? (Context->BytesPerRow * 3 * Context->NullWidth) : 0); + + if (Context->LineHeight != 0) + { + Context->LinesPerPage = clientRect.bottom / Context->LineHeight; + Context->HalfPage = FALSE; + + if (Context->LinesPerPage * Context->BytesPerRow > Context->Length) + { + Context->LinesPerPage = (Context->Length + Context->BytesPerRow / 2) / Context->BytesPerRow; + + if (Context->Length % Context->BytesPerRow != 0) + { + Context->HalfPage = TRUE; + Context->LinesPerPage++; + } + } + } + + if (freeHdc && hdc) + DeleteDC(hdc); +} + +VOID PhpHexEditOnPaint( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ PAINTSTRUCT *PaintStruct, + _In_ HDC hdc + ) +{ + RECT clientRect; + HDC bufferDc; + HBITMAP bufferBitmap; + HBITMAP oldBufferBitmap; + LONG height; + LONG x; + LONG y; + LONG i; + ULONG requiredBufferLength; + PWCHAR buffer; + + GetClientRect(hwnd, &clientRect); + + bufferDc = CreateCompatibleDC(hdc); + bufferBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom); + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); + + SetDCBrushColor(bufferDc, GetSysColor(COLOR_WINDOW)); + FillRect(bufferDc, &clientRect, GetStockBrush(DC_BRUSH)); + SelectFont(bufferDc, Context->Font); + SetBoundsRect(bufferDc, &clientRect, DCB_DISABLE); + + requiredBufferLength = (max(8, Context->BytesPerRow * 3) + 1) * sizeof(WCHAR); + + if (Context->CharBufferLength < requiredBufferLength) + { + if (Context->CharBuffer) + PhFree(Context->CharBuffer); + + Context->CharBuffer = PhAllocate(requiredBufferLength); + Context->CharBufferLength = requiredBufferLength; + buffer = Context->CharBuffer; + } + + buffer = Context->CharBuffer; + + if (Context->Data) + { + // Get character dimensions. + if (Context->Update) + { + PhpHexEditUpdateMetrics(hwnd, Context, TRUE, bufferDc); + Context->Update = FALSE; + PhpHexEditUpdateScrollbars(hwnd, Context); + } + + height = (clientRect.bottom + Context->LineHeight - 1) / Context->LineHeight * Context->LineHeight; // round up to height + + if (Context->ShowAddress) + { + PH_FORMAT format; + ULONG w; + RECT rect; + + PhInitFormatX(&format, 0); + format.Type |= FormatPadZeros; + format.Width = Context->AddressIsWide ? 8 : 4; + + w = Context->AddressIsWide ? 8 : 4; + + rect = clientRect; + rect.left = Context->AddressOffset; + rect.top = 0; + + for (i = Context->TopIndex; i < Context->Length && rect.top < height; i += Context->BytesPerRow) + { + format.u.Int32 = i; + PhFormatToBuffer(&format, 1, buffer, requiredBufferLength, NULL); + DrawText(bufferDc, buffer, w, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP); + rect.top += Context->LineHeight; + } + } + + if (Context->ShowHex) + { + RECT rect; + LONG n = 0; + + x = Context->HexOffset; + y = 0; + rect = clientRect; + rect.left = x; + rect.top = 0; + + if (Context->SelStart != -1) + { + COLORREF highlightColor; + LONG selStart; + LONG selEnd; + + if (Context->CurrentMode == EDIT_HIGH || Context->CurrentMode == EDIT_LOW) + highlightColor = GetSysColor(COLOR_HIGHLIGHT); + else + highlightColor = GetLighterHighlightColor(); + + selStart = Context->SelStart; + selEnd = Context->SelEnd; + + if (selStart > selEnd) + { + ULONG t; + + t = selEnd; + selEnd = selStart; + selStart = t; + } + + if (selStart >= Context->Length) + selStart = Context->Length - 1; + if (selEnd > Context->Length) + selEnd = Context->Length; + + // Bytes before the selection + + for (i = Context->TopIndex; i < selStart && y < height; i++) + { + PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n); + } + + // Bytes in the selection + + SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkColor(bufferDc, highlightColor); + + for (; i < selEnd && i < Context->Length && y < height; i++) + { + PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n); + } + + // Bytes after the selection + + SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); + SetBkColor(bufferDc, GetSysColor(COLOR_WINDOW)); + + for (; i < Context->Length && y < height; i++) + { + PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n); + } + } + else + { + i = Context->TopIndex; + + while (i < Context->Length && rect.top < height) + { + PWCHAR p = buffer; + + for (n = 0; n < Context->BytesPerRow && i < Context->Length; n++) + { + TO_HEX(p, Context->Data[i]); + *p++ = ' '; + i++; + } + + while (n < Context->BytesPerRow) + { + p[0] = ' '; + p[1] = ' '; + p[2] = ' '; + p += 3; + n++; + } + + DrawText(bufferDc, buffer, Context->BytesPerRow * 3, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP); + rect.top += Context->LineHeight; + } + } + } + + if (Context->ShowAscii) + { + RECT rect; + LONG n = 0; + + x = Context->AsciiOffset; + y = 0; + rect = clientRect; + rect.left = x; + rect.top = 0; + + if (Context->SelStart != -1) + { + COLORREF highlightColor; + LONG selStart; + LONG selEnd; + + if (Context->CurrentMode == EDIT_ASCII) + highlightColor = GetSysColor(COLOR_HIGHLIGHT); + else + highlightColor = GetLighterHighlightColor(); + + selStart = Context->SelStart; + selEnd = Context->SelEnd; + + if (selStart > selEnd) + { + LONG t; + + t = selEnd; + selEnd = selStart; + selStart = t; + } + + if (selStart >= Context->Length) + selStart = Context->Length - 1; + if (selEnd > Context->Length) + selEnd = Context->Length; + + // Bytes before the selection + + for (i = Context->TopIndex; i < selStart && y < height; i++) + { + PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n); + } + + // Bytes in the selection + + SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkColor(bufferDc, highlightColor); + + for (; i < selEnd && i < Context->Length && y < height; i++) + { + PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n); + } + + // Bytes after the selection + + SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); + SetBkColor(bufferDc, GetSysColor(COLOR_WINDOW)); + + for (; i < Context->Length && y < height; i++) + { + PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n); + } + } + else + { + i = Context->TopIndex; + + while (i < Context->Length && rect.top < height) + { + PWCHAR p = buffer; + + for (n = 0; n < Context->BytesPerRow && i < Context->Length; n++) + { + *p++ = PhpIsPrintable(Context, Context->Data[i]) ? Context->Data[i] : '.'; // 1 + i++; + } + + DrawText(bufferDc, buffer, n, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP); + rect.top += Context->LineHeight; + } + } + } + } + + BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, bufferDc, 0, 0, SRCCOPY); + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); + DeleteDC(bufferDc); +} + +VOID PhpHexEditUpdateScrollbars( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + SCROLLINFO si = { sizeof(si) }; + + si.fMask = SIF_ALL; + si.nMin = 0; + si.nMax = Context->Length / Context->BytesPerRow; + si.nPage = Context->LinesPerPage; + si.nPos = Context->TopIndex / Context->BytesPerRow; + SetScrollInfo(hwnd, SB_VERT, &si, TRUE); + + if (si.nMax > (LONG)si.nPage - 1) + EnableScrollBar(hwnd, SB_VERT, ESB_ENABLE_BOTH); + + // No horizontal scrollbar please. + /*si.nMin = 0; + si.nMax = ((Context->ShowAddress ? (Context->AddressIsWide ? 8 : 4) : 0) + + (Context->ShowHex ? Context->BytesPerRow * 3 : 0) + + (Context->ShowAscii ? Context->BytesPerRow : 0)) * Context->NullWidth; + si.nPage = 1; + si.nPos = 0; + SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);*/ +} + +VOID PhpHexEditCreateAddressCaret( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + DestroyCaret(); + CreateCaret(hwnd, NULL, Context->NullWidth * (Context->AddressIsWide ? 8 : 4), Context->LineHeight); +} + +VOID PhpHexEditCreateEditCaret( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + DestroyCaret(); + CreateCaret(hwnd, NULL, Context->NullWidth, Context->LineHeight); +} + +VOID PhpHexEditRepositionCaret( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG Position + ) +{ + ULONG x; + ULONG y; + RECT rect; + + x = (Position - Context->TopIndex) % Context->BytesPerRow; + y = (Position - Context->TopIndex) / Context->BytesPerRow; + + switch (Context->CurrentMode) + { + case EDIT_NONE: + PhpHexEditCreateAddressCaret(hwnd, Context); + x = 0; + break; + case EDIT_HIGH: + PhpHexEditCreateEditCaret(hwnd, Context); + x *= Context->NullWidth * 3; + x += Context->HexOffset; + break; + case EDIT_LOW: + PhpHexEditCreateEditCaret(hwnd, Context); + x *= Context->NullWidth * 3; + x += Context->NullWidth; + x += Context->HexOffset; + break; + case EDIT_ASCII: + PhpHexEditCreateEditCaret(hwnd, Context); + x *= Context->NullWidth; + x += Context->AsciiOffset; + break; + } + + Context->EditPosition.x = x; + Context->EditPosition.y = y * Context->LineHeight; + + GetClientRect(hwnd, &rect); + + if (PtInRect(&rect, Context->EditPosition)) + { + SetCaretPos(Context->EditPosition.x, Context->EditPosition.y); + ShowCaret(hwnd); + } +} + +VOID PhpHexEditCalculatePosition( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG X, + _In_ LONG Y, + _Out_ POINT *Point + ) +{ + LONG xp; + + Y /= Context->LineHeight; + + if (Y < 0 || Y >= Context->LinesPerPage) + { + Point->x = -1; + Point->y = -1; + return; + } + + if (Y * Context->BytesPerRow >= Context->Length) + { + Point->x = -1; + Point->y = -1; + return; + } + + X += Context->NullWidth; + X /= Context->NullWidth; + + if (Context->ShowAddress && X <= (Context->AddressIsWide ? 8 : 4)) + { + Context->CurrentAddress = Context->TopIndex + Context->BytesPerRow * Y; + Context->CurrentMode = EDIT_NONE; + + Point->x = 0; + Point->y = Y; + return; + } + + xp = Context->HexOffset / Context->NullWidth + Context->BytesPerRow * 3; + + if (Context->ShowHex && X < xp) + { + if (X % 3) + X--; + + Context->CurrentAddress = Context->TopIndex + + Context->BytesPerRow * Y + + (X - (Context->HexOffset / Context->NullWidth)) / 3; + Context->CurrentMode = ((X % 3) & 1) ? EDIT_LOW : EDIT_HIGH; + + Point->x = X; + Point->y = Y; + return; + } + + X--; // fix selection problem + + xp = Context->AsciiOffset / Context->NullWidth + Context->BytesPerRow; + + if (Context->ShowAscii && X * Context->NullWidth >= Context->AsciiOffset && X <= xp) + { + Context->CurrentAddress = Context->TopIndex + + Context->BytesPerRow * Y + + (X - (Context->AsciiOffset / Context->NullWidth)); + Context->CurrentMode = EDIT_ASCII; + + Point->x = X; + Point->y = Y; + return; + } + + Point->x = -1; + Point->y = -1; +} + +VOID PhpHexEditMove( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG X, + _In_ LONG Y + ) +{ + switch (Context->CurrentMode) + { + case EDIT_NONE: + Context->CurrentAddress += Y * Context->BytesPerRow; + break; + case EDIT_HIGH: + if (X != 0) + Context->CurrentMode = EDIT_LOW; + if (X == -1) + Context->CurrentAddress--; + Context->CurrentAddress += Y * Context->BytesPerRow; + break; + case EDIT_LOW: + if (X != 0) + Context->CurrentMode = EDIT_HIGH; + if (X == 1) + Context->CurrentAddress++; + Context->CurrentAddress += Y * Context->BytesPerRow; + break; + case EDIT_ASCII: + Context->CurrentAddress += X; + Context->CurrentAddress += Y * Context->BytesPerRow; + break; + } + + if (Context->CurrentAddress < 0) + Context->CurrentAddress = 0; + + if (Context->CurrentAddress >= Context->Length) + { + Context->CurrentAddress -= X; + Context->CurrentAddress -= Y * Context->BytesPerRow; + } + + Context->NoAddressChange = TRUE; + + if (Context->CurrentAddress < Context->TopIndex) + SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); + if (Context->CurrentAddress >= Context->TopIndex + Context->LinesPerPage * Context->BytesPerRow) + SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); + + Context->NoAddressChange = FALSE; + PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress); +} + +VOID PhpHexEditSetSel( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG S, + _In_ LONG E + ) +{ + DestroyCaret(); + Context->SelStart = S; + Context->SelEnd = E; + REDRAW_WINDOW(hwnd); + + if (S != -1 && E != -1) + { + Context->CurrentAddress = S; + } + else + { + if (Context->EditPosition.x == 0 && Context->ShowAddress) + PhpHexEditCreateAddressCaret(hwnd, Context); + else + PhpHexEditCreateEditCaret(hwnd, Context); + + SetCaretPos(Context->EditPosition.x, Context->EditPosition.y); + ShowCaret(hwnd); + } +} + +VOID PhpHexEditScrollTo( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG Position + ) +{ + if (Position < Context->TopIndex || Position > Context->TopIndex + Context->LinesPerPage * Context->BytesPerRow) + { + Context->TopIndex = Position / Context->BytesPerRow * Context->BytesPerRow; // round down + Context->TopIndex -= Context->LinesPerPage / 3 * Context->BytesPerRow; + + if (Context->TopIndex < 0) + Context->TopIndex = 0; + + if (Context->Length >= Context->LinesPerPage * Context->BytesPerRow) + { + if (Context->TopIndex > Context->Length - Context->LinesPerPage * Context->BytesPerRow) + Context->TopIndex = Context->Length - Context->LinesPerPage * Context->BytesPerRow; + } + + PhpHexEditUpdateScrollbars(hwnd, Context); + REDRAW_WINDOW(hwnd); + } +} + +VOID PhpHexEditClearEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + if (Context->AllowLengthChange) + { + Context->CurrentAddress = Context->SelStart; + PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd); + PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress); + REDRAW_WINDOW(hwnd); + } +} + +VOID PhpHexEditCopyEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + if (OpenClipboard(hwnd)) + { + EmptyClipboard(); + PhpHexEditNormalizeSel(hwnd, Context); + + if (Context->CurrentMode != EDIT_ASCII) + { + ULONG length = Context->SelEnd - Context->SelStart; + HGLOBAL binaryMemory; + HGLOBAL hexMemory; + + binaryMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length); + + if (binaryMemory) + { + PUCHAR p = GlobalLock(binaryMemory); + memcpy(p, &Context->Data[Context->SelStart], length); + GlobalUnlock(binaryMemory); + + hexMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (length * 3 + 1) * sizeof(WCHAR)); + + if (hexMemory) + { + PWCHAR pw; + ULONG i; + + pw = GlobalLock(hexMemory); + + for (i = 0; i < length; i++) + { + TO_HEX(pw, Context->Data[Context->SelStart + i]); + *pw++ = ' '; + } + *pw = 0; + + GlobalUnlock(hexMemory); + + SetClipboardData(CF_UNICODETEXT, hexMemory); + } + + SetClipboardData(RegisterClipboardFormat(L"BinaryData"), binaryMemory); + } + } + else + { + ULONG length = Context->SelEnd - Context->SelStart; + HGLOBAL binaryMemory; + HGLOBAL asciiMemory; + + binaryMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length); + asciiMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length + 1); + + if (binaryMemory) + { + PUCHAR p = GlobalLock(binaryMemory); + memcpy(p, &Context->Data[Context->SelStart], length); + GlobalUnlock(binaryMemory); + + if (asciiMemory) + { + ULONG i; + + p = GlobalLock(asciiMemory); + memcpy(p, &Context->Data[Context->SelStart], length); + + for (i = 0; i < length; i++) + { + if (!PhpIsPrintable(Context, *p)) + *p = '.'; + p++; + } + *p = 0; + + GlobalUnlock(asciiMemory); + + SetClipboardData(CF_TEXT, asciiMemory); + } + + SetClipboardData(RegisterClipboardFormat(L"BinaryData"), binaryMemory); + } + } + + CloseClipboard(); + } +} + +VOID PhpHexEditCutEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + if (Context->AllowLengthChange) + { + PhpHexEditCopyEdit(hwnd, Context); + PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd); + REDRAW_WINDOW(hwnd); + } +} + +VOID PhpHexEditPasteEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + if (OpenClipboard(hwnd)) + { + HANDLE memory; + + memory = GetClipboardData(RegisterClipboardFormat(L"BinaryData")); + + if (!memory) + memory = GetClipboardData(CF_TEXT); + + if (memory) + { + PUCHAR p = GlobalLock(memory); + ULONG length = (ULONG)GlobalSize(memory); + ULONG paste; + ULONG oldCurrentAddress = Context->CurrentAddress; + + PhpHexEditNormalizeSel(hwnd, Context); + + if (Context->AllowLengthChange) + { + if (Context->SelStart == -1) + { + if (Context->CurrentMode == EDIT_LOW) + Context->CurrentAddress++; + + paste = Context->CurrentAddress; + PhpHexEditSelInsert(hwnd, Context, Context->CurrentAddress, length); + } + else + { + paste = Context->SelStart; + PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd); + PhpHexEditSelInsert(hwnd, Context, paste, length); + PhpHexEditSetSel(hwnd, Context, -1, -1); + } + } + else + { + if (Context->SelStart == -1) + { + if (Context->CurrentMode == EDIT_LOW) + Context->CurrentAddress++; + + paste = Context->CurrentAddress; + } + else + { + paste = Context->SelStart; + } + + if (length > Context->Length - paste) + length = Context->Length - paste; + } + + memcpy(&Context->Data[paste], p, length); + GlobalUnlock(memory); + + Context->CurrentAddress = oldCurrentAddress; + REDRAW_WINDOW(hwnd); + } + + CloseClipboard(); + } +} + +VOID PhpHexEditSelectAll( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + Context->SelStart = 0; + Context->SelEnd = Context->Length; + DestroyCaret(); + REDRAW_WINDOW(hwnd); +} + +VOID PhpHexEditUndoEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + // TODO +} + +VOID PhpHexEditNormalizeSel( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + if (Context->SelStart > Context->SelEnd) + { + LONG t; + + t = Context->SelEnd; + Context->SelEnd = Context->SelStart; + Context->SelStart = t; + } +} + +VOID PhpHexEditSelDelete( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG S, + _In_ LONG E + ) +{ + if (Context->AllowLengthChange && Context->Length > 0) + { + PUCHAR p = PhAllocate(Context->Length - (E - S) + 1); + + memcpy(p, Context->Data, S); + + if (S < Context->Length - (E - S)) + memcpy(&p[S], &Context->Data[E], Context->Length - E); + + PhFree(Context->Data); + Context->Data = p; + PhpHexEditSetSel(hwnd, Context, -1, -1); + Context->Length -= E - S; + + if (Context->CurrentAddress > Context->Length) + { + Context->CurrentAddress = Context->Length; + PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress); + } + + Context->Update = TRUE; + } +} + +VOID PhpHexEditSelInsert( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG S, + _In_ LONG L + ) +{ + if (Context->AllowLengthChange) + { + PUCHAR p = PhAllocate(Context->Length + L); + + memset(p, 0, Context->Length + L); + memcpy(p, Context->Data, S); + memcpy(&p[S + L], &Context->Data[S], Context->Length - S); + + PhFree(Context->Data); + Context->Data = p; + PhpHexEditSetSel(hwnd, Context, -1, -1); + Context->Length += L; + + Context->Update = TRUE; + } +} + +VOID PhpHexEditSetBuffer( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ PUCHAR Data, + _In_ ULONG Length + ) +{ + Context->Data = Data; + PhpHexEditSetSel(hwnd, Context, -1, -1); + Context->Length = Length; + Context->CurrentAddress = 0; + Context->EditPosition.x = Context->EditPosition.y = 0; + Context->CurrentMode = EDIT_HIGH; + Context->TopIndex = 0; + Context->Update = TRUE; + + Context->UserBuffer = TRUE; + Context->AllowLengthChange = FALSE; +} + +VOID PhpHexEditSetData( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ PUCHAR Data, + _In_ ULONG Length + ) +{ + if (Context->Data) PhFree(Context->Data); + Context->Data = PhAllocate(Length); + memcpy(Context->Data, Data, Length); + PhpHexEditSetBuffer(hwnd, Context, Context->Data, Length); + Context->UserBuffer = FALSE; + Context->AllowLengthChange = TRUE; +} diff --git a/phlib/hndlinfo.c b/phlib/hndlinfo.c index 5c0109acc8d0..8a5267ecf901 100644 --- a/phlib/hndlinfo.c +++ b/phlib/hndlinfo.c @@ -1,1776 +1,1909 @@ -/* - * Process Hacker - - * handle information - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#define PH_QUERY_HACK_MAX_THREADS 20 - -typedef struct _PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT -{ - SLIST_ENTRY ListEntry; - - PUSER_THREAD_START_ROUTINE Routine; - PVOID Context; - - HANDLE StartEventHandle; - HANDLE CompletedEventHandle; - HANDLE ThreadHandle; -} PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT, *PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT; - -typedef enum _PHP_QUERY_OBJECT_WORK -{ - NtQueryObjectWork, - NtQuerySecurityObjectWork, - NtSetSecurityObjectWork -} PHP_QUERY_OBJECT_WORK; - -typedef struct _PHP_QUERY_OBJECT_COMMON_CONTEXT -{ - PHP_QUERY_OBJECT_WORK Work; - NTSTATUS Status; - - union - { - struct - { - HANDLE Handle; - OBJECT_INFORMATION_CLASS ObjectInformationClass; - PVOID ObjectInformation; - ULONG ObjectInformationLength; - PULONG ReturnLength; - } NtQueryObject; - struct - { - HANDLE Handle; - SECURITY_INFORMATION SecurityInformation; - PSECURITY_DESCRIPTOR SecurityDescriptor; - ULONG Length; - PULONG LengthNeeded; - } NtQuerySecurityObject; - struct - { - HANDLE Handle; - SECURITY_INFORMATION SecurityInformation; - PSECURITY_DESCRIPTOR SecurityDescriptor; - } NtSetSecurityObject; - } u; -} PHP_QUERY_OBJECT_COMMON_CONTEXT, *PPHP_QUERY_OBJECT_COMMON_CONTEXT; - -PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT PhpAcquireCallWithTimeoutThread( - _In_opt_ PLARGE_INTEGER Timeout - ); - -VOID PhpReleaseCallWithTimeoutThread( - _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext - ); - -NTSTATUS PhpCallWithTimeout( - _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext, - _In_ PUSER_THREAD_START_ROUTINE Routine, - _In_opt_ PVOID Context, - _In_ PLARGE_INTEGER Timeout - ); - -NTSTATUS PhpCallWithTimeoutThreadStart( - _In_ PVOID Parameter - ); - -static PPH_STRING PhObjectTypeNames[MAX_OBJECT_TYPE_NUMBER] = { 0 }; -static PPH_GET_CLIENT_ID_NAME PhHandleGetClientIdName = PhStdGetClientIdName; - -static SLIST_HEADER PhpCallWithTimeoutThreadListHead; -static PH_WAKE_EVENT PhpCallWithTimeoutThreadReleaseEvent = PH_WAKE_EVENT_INIT; - -PPH_GET_CLIENT_ID_NAME PhSetHandleClientIdFunction( - _In_ PPH_GET_CLIENT_ID_NAME GetClientIdName - ) -{ - return _InterlockedExchangePointer( - (PVOID *)&PhHandleGetClientIdName, - GetClientIdName - ); -} - -NTSTATUS PhpGetObjectBasicInformation( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _Out_ POBJECT_BASIC_INFORMATION BasicInformation - ) -{ - NTSTATUS status; - - if (KphIsConnected()) - { - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectBasicInformation, - BasicInformation, - sizeof(OBJECT_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - { - // The object was referenced in KProcessHacker, so we need to subtract 1 from the - // pointer count. - BasicInformation->PointerCount -= 1; - } - } - else - { - status = NtQueryObject( - Handle, - ObjectBasicInformation, - BasicInformation, - sizeof(OBJECT_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - { - // The object was referenced in NtQueryObject and a handle was opened to the object. We - // need to subtract 1 from the pointer count, then subtract 1 from both counts. - BasicInformation->HandleCount -= 1; - BasicInformation->PointerCount -= 2; - } - } - - return status; -} - -NTSTATUS PhpGetObjectTypeName( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ ULONG ObjectTypeNumber, - _Out_ PPH_STRING *TypeName - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PPH_STRING typeName = NULL; - - // If the cache contains the object type name, use it. Otherwise, query the type name. - - if (ObjectTypeNumber != -1 && ObjectTypeNumber < MAX_OBJECT_TYPE_NUMBER) - typeName = PhObjectTypeNames[ObjectTypeNumber]; - - if (typeName) - { - PhReferenceObject(typeName); - } - else - { - POBJECT_TYPE_INFORMATION buffer; - ULONG returnLength = 0; - PPH_STRING oldTypeName; - - // Get the needed buffer size. - if (KphIsConnected()) - { - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectTypeInformation, - NULL, - 0, - &returnLength - ); - } - else - { - status = NtQueryObject( - Handle, - ObjectTypeInformation, - NULL, - 0, - &returnLength - ); - } - - if (returnLength == 0) - return status; - - buffer = PhAllocate(returnLength); - - if (KphIsConnected()) - { - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectTypeInformation, - buffer, - returnLength, - &returnLength - ); - } - else - { - status = NtQueryObject( - Handle, - ObjectTypeInformation, - buffer, - returnLength, - &returnLength - ); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - // Create a copy of the type name. - typeName = PhCreateStringFromUnicodeString(&buffer->TypeName); - - if (ObjectTypeNumber != -1 && ObjectTypeNumber < MAX_OBJECT_TYPE_NUMBER) - { - // Try to store the type name in the cache. - oldTypeName = _InterlockedCompareExchangePointer( - &PhObjectTypeNames[ObjectTypeNumber], - typeName, - NULL - ); - - // Add a reference if we stored the type name - // successfully. - if (!oldTypeName) - PhReferenceObject(typeName); - } - - PhFree(buffer); - } - - // At this point typeName should contain a type name with one additional reference. - - *TypeName = typeName; - - return status; -} - -NTSTATUS PhpGetObjectName( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ BOOLEAN WithTimeout, - _Out_ PPH_STRING *ObjectName - ) -{ - NTSTATUS status; - POBJECT_NAME_INFORMATION buffer; - ULONG bufferSize; - ULONG attempts = 8; - - bufferSize = 0x200; - buffer = PhAllocate(bufferSize); - - // A loop is needed because the I/O subsystem likes to give us the wrong return lengths... - do - { - if (KphIsConnected()) - { - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectNameInformation, - buffer, - bufferSize, - &bufferSize - ); - } - else - { - if (WithTimeout) - { - status = PhCallNtQueryObjectWithTimeout( - Handle, - ObjectNameInformation, - buffer, - bufferSize, - &bufferSize - ); - } - else - { - status = NtQueryObject( - Handle, - ObjectNameInformation, - buffer, - bufferSize, - &bufferSize - ); - } - } - - if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH || - status == STATUS_BUFFER_TOO_SMALL) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } while (--attempts); - - if (NT_SUCCESS(status)) - { - *ObjectName = PhCreateStringFromUnicodeString(&buffer->Name); - } - - PhFree(buffer); - - return status; -} - -PPH_STRING PhFormatNativeKeyName( - _In_ PPH_STRING Name - ) -{ - static PH_STRINGREF hklmPrefix = PH_STRINGREF_INIT(L"\\Registry\\Machine"); - static PH_STRINGREF hkcrPrefix = PH_STRINGREF_INIT(L"\\Registry\\Machine\\Software\\Classes"); - static PH_STRINGREF hkuPrefix = PH_STRINGREF_INIT(L"\\Registry\\User"); - static PPH_STRING hkcuPrefix; - static PPH_STRING hkcucrPrefix; - - static PH_STRINGREF hklmString = PH_STRINGREF_INIT(L"HKLM"); - static PH_STRINGREF hkcrString = PH_STRINGREF_INIT(L"HKCR"); - static PH_STRINGREF hkuString = PH_STRINGREF_INIT(L"HKU"); - static PH_STRINGREF hkcuString = PH_STRINGREF_INIT(L"HKCU"); - static PH_STRINGREF hkcucrString = PH_STRINGREF_INIT(L"HKCU\\Software\\Classes"); - - static PH_INITONCE initOnce = PH_INITONCE_INIT; - - PPH_STRING newName; - PH_STRINGREF name; - - if (PhBeginInitOnce(&initOnce)) - { - HANDLE currentTokenHandle; - PTOKEN_USER tokenUser; - PPH_STRING stringSid = NULL; - - currentTokenHandle = PhGetOwnTokenAttributes().TokenHandle; - - if (currentTokenHandle && NT_SUCCESS(PhGetTokenUser( - currentTokenHandle, - &tokenUser - ))) - { - stringSid = PhSidToStringSid(tokenUser->User.Sid); - PhFree(tokenUser); - } - - if (stringSid) - { - static PH_STRINGREF registryUserPrefix = PH_STRINGREF_INIT(L"\\Registry\\User\\"); - static PH_STRINGREF classesString = PH_STRINGREF_INIT(L"_Classes"); - - hkcuPrefix = PhConcatStringRef2(®istryUserPrefix, &stringSid->sr); - hkcucrPrefix = PhConcatStringRef2(&hkcuPrefix->sr, &classesString); - - PhDereferenceObject(stringSid); - } - else - { - hkcuPrefix = PhCreateString(L"..."); // some random string that won't ever get matched - hkcucrPrefix = PhCreateString(L"..."); - } - - PhEndInitOnce(&initOnce); - } - - name = Name->sr; - - if (PhStartsWithStringRef(&name, &hkcrPrefix, TRUE)) - { - PhSkipStringRef(&name, hkcrPrefix.Length); - newName = PhConcatStringRef2(&hkcrString, &name); - } - else if (PhStartsWithStringRef(&name, &hklmPrefix, TRUE)) - { - PhSkipStringRef(&name, hklmPrefix.Length); - newName = PhConcatStringRef2(&hklmString, &name); - } - else if (PhStartsWithStringRef(&name, &hkcucrPrefix->sr, TRUE)) - { - PhSkipStringRef(&name, hkcucrPrefix->Length); - newName = PhConcatStringRef2(&hkcucrString, &name); - } - else if (PhStartsWithStringRef(&name, &hkcuPrefix->sr, TRUE)) - { - PhSkipStringRef(&name, hkcuPrefix->Length); - newName = PhConcatStringRef2(&hkcuString, &name); - } - else if (PhStartsWithStringRef(&name, &hkuPrefix, TRUE)) - { - PhSkipStringRef(&name, hkuPrefix.Length); - newName = PhConcatStringRef2(&hkuString, &name); - } - else - { - PhSetReference(&newName, Name); - } - - return newName; -} - -NTSTATUS PhGetSectionFileName( - _In_ HANDLE SectionHandle, - _Out_ PPH_STRING *FileName - ) -{ - NTSTATUS status; - SIZE_T viewSize; - PVOID viewBase; - - viewSize = 1; - viewBase = NULL; - - status = NtMapViewOfSection( - SectionHandle, - NtCurrentProcess(), - &viewBase, - 0, - 0, - NULL, - &viewSize, - ViewShare, - 0, - PAGE_READONLY - ); - - if (!NT_SUCCESS(status)) - return status; - - status = PhGetProcessMappedFileName(NtCurrentProcess(), viewBase, FileName); - NtUnmapViewOfSection(NtCurrentProcess(), viewBase); - - return status; -} - -_Callback_ PPH_STRING PhStdGetClientIdName( - _In_ PCLIENT_ID ClientId - ) -{ - static PH_QUEUED_LOCK cachedProcessesLock = PH_QUEUED_LOCK_INIT; - static PVOID processes = NULL; - static ULONG lastProcessesTickCount = 0; - - PPH_STRING name; - ULONG tickCount; - PSYSTEM_PROCESS_INFORMATION processInfo; - - // Get a new process list only if 2 seconds have passed since the last update. - - tickCount = GetTickCount(); - - if (tickCount - lastProcessesTickCount >= 2000) - { - PhAcquireQueuedLockExclusive(&cachedProcessesLock); - - // Re-check the tick count. - if (tickCount - lastProcessesTickCount >= 2000) - { - if (processes) - { - PhFree(processes); - processes = NULL; - } - - if (!NT_SUCCESS(PhEnumProcesses(&processes))) - { - PhReleaseQueuedLockExclusive(&cachedProcessesLock); - return PhCreateString(L"(Error querying processes)"); - } - - lastProcessesTickCount = tickCount; - } - - PhReleaseQueuedLockExclusive(&cachedProcessesLock); - } - - // Get a lock on the process list and get a name for the client ID. - - PhAcquireQueuedLockShared(&cachedProcessesLock); - - if (!processes) - { - PhReleaseQueuedLockShared(&cachedProcessesLock); - return NULL; - } - - processInfo = PhFindProcessInformation(processes, ClientId->UniqueProcess); - - if (ClientId->UniqueThread) - { - if (processInfo) - { - name = PhFormatString( - L"%.*s (%u): %u", - processInfo->ImageName.Length / 2, - processInfo->ImageName.Buffer, - HandleToUlong(ClientId->UniqueProcess), - HandleToUlong(ClientId->UniqueThread) - ); - } - else - { - name = PhFormatString( - L"Non-existent process (%u): %u", - HandleToUlong(ClientId->UniqueProcess), - HandleToUlong(ClientId->UniqueThread) - ); - } - } - else - { - if (processInfo) - { - name = PhFormatString( - L"%.*s (%u)", - processInfo->ImageName.Length / 2, - processInfo->ImageName.Buffer, - HandleToUlong(ClientId->UniqueProcess) - ); - } - else - { - name = PhFormatString(L"Non-existent process (%u)", HandleToUlong(ClientId->UniqueProcess)); - } - } - - PhReleaseQueuedLockShared(&cachedProcessesLock); - - return name; -} - -NTSTATUS PhpGetBestObjectName( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ PPH_STRING ObjectName, - _In_ PPH_STRING TypeName, - _Out_ PPH_STRING *BestObjectName - ) -{ - NTSTATUS status; - PPH_STRING bestObjectName = NULL; - PPH_GET_CLIENT_ID_NAME handleGetClientIdName = PhHandleGetClientIdName; - - if (PhEqualString2(TypeName, L"EtwRegistration", TRUE)) - { - if (KphIsConnected()) - { - ETWREG_BASIC_INFORMATION basicInfo; - - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectEtwRegBasicInformation, - &basicInfo, - sizeof(ETWREG_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - { - static PH_STRINGREF publishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\"); - - PPH_STRING guidString; - PPH_STRING keyName; - HANDLE keyHandle; - PPH_STRING publisherName = NULL; - - guidString = PhFormatGuid(&basicInfo.Guid); - - // We should perform a lookup on the GUID to get the publisher name. - - keyName = PhConcatStringRef2(&publishersKeyName, &guidString->sr); - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName->sr, - 0 - ))) - { - publisherName = PhQueryRegistryString(keyHandle, NULL); - - if (publisherName && publisherName->Length == 0) - { - PhDereferenceObject(publisherName); - publisherName = NULL; - } - - NtClose(keyHandle); - } - - PhDereferenceObject(keyName); - - if (publisherName) - { - bestObjectName = publisherName; - PhDereferenceObject(guidString); - } - else - { - bestObjectName = guidString; - } - } - } - } - else if (PhEqualString2(TypeName, L"File", TRUE)) - { - // Convert the file name to a DOS file name. - bestObjectName = PhResolveDevicePrefix(ObjectName); - - if (!bestObjectName) - { - // The file doesn't have a DOS name. - PhSetReference(&bestObjectName, ObjectName); - } - - if (PhIsNullOrEmptyString(bestObjectName) && KphIsConnected()) - { - KPH_FILE_OBJECT_DRIVER fileObjectDriver; - PPH_STRING driverName; - - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectFileObjectDriver, - &fileObjectDriver, - sizeof(KPH_FILE_OBJECT_DRIVER), - NULL - ); - - if (NT_SUCCESS(status) && fileObjectDriver.DriverHandle) - { - if (NT_SUCCESS(PhGetDriverName(fileObjectDriver.DriverHandle, &driverName))) - { - static PH_STRINGREF prefix = PH_STRINGREF_INIT(L"Unnamed file: "); - - PhMoveReference(&bestObjectName, PhConcatStringRef2(&prefix, &driverName->sr)); - PhDereferenceObject(driverName); - } - - NtClose(fileObjectDriver.DriverHandle); - } - } - } - else if (PhEqualString2(TypeName, L"Job", TRUE)) - { - HANDLE dupHandle; - PJOBOBJECT_BASIC_PROCESS_ID_LIST processIdList; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - JOB_OBJECT_QUERY, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - if (handleGetClientIdName && NT_SUCCESS(PhGetJobProcessIdList(dupHandle, &processIdList))) - { - PH_STRING_BUILDER sb; - ULONG i; - CLIENT_ID clientId; - PPH_STRING name; - - PhInitializeStringBuilder(&sb, 40); - clientId.UniqueThread = NULL; - - for (i = 0; i < processIdList->NumberOfProcessIdsInList; i++) - { - clientId.UniqueProcess = (HANDLE)processIdList->ProcessIdList[i]; - name = handleGetClientIdName(&clientId); - - if (name) - { - PhAppendStringBuilder(&sb, &name->sr); - PhAppendStringBuilder2(&sb, L"; "); - PhDereferenceObject(name); - } - } - - PhFree(processIdList); - - if (sb.String->Length != 0) - PhRemoveEndStringBuilder(&sb, 2); - - if (sb.String->Length == 0) - PhAppendStringBuilder2(&sb, L"(No processes)"); - - bestObjectName = PhFinalStringBuilderString(&sb); - } - - NtClose(dupHandle); - } - else if (PhEqualString2(TypeName, L"Key", TRUE)) - { - bestObjectName = PhFormatNativeKeyName(ObjectName); - } - else if (PhEqualString2(TypeName, L"Process", TRUE)) - { - CLIENT_ID clientId; - - clientId.UniqueThread = NULL; - - if (KphIsConnected()) - { - PROCESS_BASIC_INFORMATION basicInfo; - - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectProcessBasicInformation, - &basicInfo, - sizeof(PROCESS_BASIC_INFORMATION), - NULL - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - clientId.UniqueProcess = basicInfo.UniqueProcessId; - } - else - { - HANDLE dupHandle; - PROCESS_BASIC_INFORMATION basicInfo; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - ProcessQueryAccess, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetProcessBasicInformation(dupHandle, &basicInfo); - NtClose(dupHandle); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - clientId.UniqueProcess = basicInfo.UniqueProcessId; - } - - if (handleGetClientIdName) - bestObjectName = handleGetClientIdName(&clientId); - } - else if (PhEqualString2(TypeName, L"Section", TRUE)) - { - HANDLE dupHandle; - PPH_STRING fileName; - - if (!PhIsNullOrEmptyString(ObjectName)) - goto CleanupExit; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - SECTION_QUERY | SECTION_MAP_READ, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetSectionFileName(dupHandle, &fileName); - - if (NT_SUCCESS(status)) - { - bestObjectName = PhResolveDevicePrefix(fileName); - PhDereferenceObject(fileName); - } - else - { - SECTION_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhGetSectionBasicInformation(dupHandle, &basicInfo))) - { - PH_FORMAT format[4]; - PWSTR sectionType = L"Unknown"; - - if (basicInfo.AllocationAttributes & SEC_COMMIT) - sectionType = L"Commit"; - else if (basicInfo.AllocationAttributes & SEC_FILE) - sectionType = L"File"; - else if (basicInfo.AllocationAttributes & SEC_IMAGE) - sectionType = L"Image"; - else if (basicInfo.AllocationAttributes & SEC_RESERVE) - sectionType = L"Reserve"; - - PhInitFormatS(&format[0], sectionType); - PhInitFormatS(&format[1], L" ("); - PhInitFormatSize(&format[2], basicInfo.MaximumSize.QuadPart); - PhInitFormatC(&format[3], ')'); - bestObjectName = PhFormat(format, 4, 20); - } - } - - NtClose(dupHandle); - } - else if (PhEqualString2(TypeName, L"Thread", TRUE)) - { - CLIENT_ID clientId; - - if (KphIsConnected()) - { - THREAD_BASIC_INFORMATION basicInfo; - - status = KphQueryInformationObject( - ProcessHandle, - Handle, - KphObjectThreadBasicInformation, - &basicInfo, - sizeof(THREAD_BASIC_INFORMATION), - NULL - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - clientId = basicInfo.ClientId; - } - else - { - HANDLE dupHandle; - THREAD_BASIC_INFORMATION basicInfo; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - ThreadQueryAccess, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetThreadBasicInformation(dupHandle, &basicInfo); - NtClose(dupHandle); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - clientId = basicInfo.ClientId; - } - - if (handleGetClientIdName) - bestObjectName = handleGetClientIdName(&clientId); - } - else if (PhEqualString2(TypeName, L"TmEn", TRUE)) - { - HANDLE dupHandle; - ENLISTMENT_BASIC_INFORMATION basicInfo; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - ENLISTMENT_QUERY_INFORMATION, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetEnlistmentBasicInformation(dupHandle, &basicInfo); - NtClose(dupHandle); - - if (NT_SUCCESS(status)) - { - bestObjectName = PhFormatGuid(&basicInfo.EnlistmentId); - } - } - else if (PhEqualString2(TypeName, L"TmRm", TRUE)) - { - HANDLE dupHandle; - GUID guid; - PPH_STRING description; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - RESOURCEMANAGER_QUERY_INFORMATION, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetResourceManagerBasicInformation( - dupHandle, - &guid, - &description - ); - NtClose(dupHandle); - - if (NT_SUCCESS(status)) - { - if (!PhIsNullOrEmptyString(description)) - { - bestObjectName = description; - } - else - { - bestObjectName = PhFormatGuid(&guid); - - if (description) - PhDereferenceObject(description); - } - } - } - else if (PhEqualString2(TypeName, L"TmTm", TRUE)) - { - HANDLE dupHandle; - PPH_STRING logFileName = NULL; - TRANSACTIONMANAGER_BASIC_INFORMATION basicInfo; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - TRANSACTIONMANAGER_QUERY_INFORMATION, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetTransactionManagerLogFileName( - dupHandle, - &logFileName - ); - - if (NT_SUCCESS(status) && !PhIsNullOrEmptyString(logFileName)) - { - bestObjectName = PhGetFileName(logFileName); - PhDereferenceObject(logFileName); - } - else - { - if (logFileName) - PhDereferenceObject(logFileName); - - status = PhGetTransactionManagerBasicInformation( - dupHandle, - &basicInfo - ); - - if (NT_SUCCESS(status)) - { - bestObjectName = PhFormatGuid(&basicInfo.TmIdentity); - } - } - - NtClose(dupHandle); - } - else if (PhEqualString2(TypeName, L"TmTx", TRUE)) - { - HANDLE dupHandle; - PPH_STRING description = NULL; - TRANSACTION_BASIC_INFORMATION basicInfo; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - TRANSACTION_QUERY_INFORMATION, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetTransactionPropertiesInformation( - dupHandle, - NULL, - NULL, - &description - ); - - if (NT_SUCCESS(status) && !PhIsNullOrEmptyString(description)) - { - bestObjectName = description; - } - else - { - if (description) - PhDereferenceObject(description); - - status = PhGetTransactionBasicInformation( - dupHandle, - &basicInfo - ); - - if (NT_SUCCESS(status)) - { - bestObjectName = PhFormatGuid(&basicInfo.TransactionId); - } - } - - NtClose(dupHandle); - } - else if (PhEqualString2(TypeName, L"Token", TRUE)) - { - HANDLE dupHandle; - PTOKEN_USER tokenUser = NULL; - TOKEN_STATISTICS statistics = { 0 }; - - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - TOKEN_QUERY, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = PhGetTokenUser(dupHandle, &tokenUser); - PhGetTokenStatistics(dupHandle, &statistics); - - if (NT_SUCCESS(status)) - { - PPH_STRING fullName; - - fullName = PhGetSidFullName(tokenUser->User.Sid, TRUE, NULL); - - if (fullName) - { - PH_FORMAT format[4]; - - PhInitFormatSR(&format[0], fullName->sr); - PhInitFormatS(&format[1], L": 0x"); - PhInitFormatX(&format[2], statistics.AuthenticationId.LowPart); - PhInitFormatS(&format[3], statistics.TokenType == TokenPrimary ? L" (Primary)" : L" (Impersonation)"); - - bestObjectName = PhFormat(format, 4, fullName->Length + 8 + 16 + 16); - PhDereferenceObject(fullName); - } - - PhFree(tokenUser); - } - - NtClose(dupHandle); - } - -CleanupExit: - - if (!bestObjectName) - PhSetReference(&bestObjectName, ObjectName); - - *BestObjectName = bestObjectName; - - return STATUS_SUCCESS; -} - -/** - * Gets information for a handle. - * - * \param ProcessHandle A handle to the process in which the handle resides. - * \param Handle The handle value. - * \param ObjectTypeNumber The object type number of the handle. You can specify -1 for this - * parameter if the object type number is not known. - * \param BasicInformation A variable which receives basic information about the object. - * \param TypeName A variable which receives the object type name. - * \param ObjectName A variable which receives the object name. - * \param BestObjectName A variable which receives the formatted object name. - * - * \retval STATUS_INVALID_HANDLE The handle specified in \c ProcessHandle or \c Handle is invalid. - * \retval STATUS_INVALID_PARAMETER_3 The value specified in \c ObjectTypeNumber is invalid. - */ -NTSTATUS PhGetHandleInformation( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ ULONG ObjectTypeNumber, - _Out_opt_ POBJECT_BASIC_INFORMATION BasicInformation, - _Out_opt_ PPH_STRING *TypeName, - _Out_opt_ PPH_STRING *ObjectName, - _Out_opt_ PPH_STRING *BestObjectName - ) -{ - NTSTATUS status; - NTSTATUS subStatus; - - status = PhGetHandleInformationEx( - ProcessHandle, - Handle, - ObjectTypeNumber, - 0, - &subStatus, - BasicInformation, - TypeName, - ObjectName, - BestObjectName, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Fail if any component failed, for compatibility reasons. - if (!NT_SUCCESS(subStatus)) - { - if (TypeName) - PhClearReference(TypeName); - if (ObjectName) - PhClearReference(ObjectName); - if (BestObjectName) - PhClearReference(BestObjectName); - - return subStatus; - } - - return status; -} - -/** - * Gets information for a handle. - * - * \param ProcessHandle A handle to the process in which the handle resides. - * \param Handle The handle value. - * \param ObjectTypeNumber The object type number of the handle. You can specify -1 for this - * parameter if the object type number is not known. - * \param Flags Reserved. - * \param SubStatus A variable which receives the NTSTATUS value of the last component that fails. - * If all operations succeed, the value will be STATUS_SUCCESS. If the function returns an error - * status, this variable is not set. - * \param BasicInformation A variable which receives basic information about the object. - * \param TypeName A variable which receives the object type name. - * \param ObjectName A variable which receives the object name. - * \param BestObjectName A variable which receives the formatted object name. - * \param ExtraInformation Reserved. - * - * \retval STATUS_INVALID_HANDLE The handle specified in \c ProcessHandle or \c Handle is invalid. - * \retval STATUS_INVALID_PARAMETER_3 The value specified in \c ObjectTypeNumber is invalid. - * - * \remarks If \a BasicInformation or \a TypeName are specified, the function will fail if either - * cannot be queried. \a ObjectName, \a BestObjectName and \a ExtraInformation will be NULL if they - * cannot be queried. - */ -NTSTATUS PhGetHandleInformationEx( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ ULONG ObjectTypeNumber, - _Reserved_ ULONG Flags, - _Out_opt_ PNTSTATUS SubStatus, - _Out_opt_ POBJECT_BASIC_INFORMATION BasicInformation, - _Out_opt_ PPH_STRING *TypeName, - _Out_opt_ PPH_STRING *ObjectName, - _Out_opt_ PPH_STRING *BestObjectName, - _Reserved_ PVOID *ExtraInformation - ) -{ - NTSTATUS status = STATUS_SUCCESS; - NTSTATUS subStatus = STATUS_SUCCESS; - HANDLE dupHandle = NULL; - PPH_STRING typeName = NULL; - PPH_STRING objectName = NULL; - PPH_STRING bestObjectName = NULL; - - if (Handle == NULL || Handle == NtCurrentProcess() || Handle == NtCurrentThread()) - return STATUS_INVALID_HANDLE; - if (ObjectTypeNumber != -1 && ObjectTypeNumber >= MAX_OBJECT_TYPE_NUMBER) - return STATUS_INVALID_PARAMETER_3; - - // Duplicate the handle if we're not using KPH. - if (!KphIsConnected()) - { - // However, we obviously don't need to duplicate it - // if the handle is in the current process. - if (ProcessHandle != NtCurrentProcess()) - { - status = NtDuplicateObject( - ProcessHandle, - Handle, - NtCurrentProcess(), - &dupHandle, - 0, - 0, - 0 - ); - - if (!NT_SUCCESS(status)) - return status; - } - else - { - dupHandle = Handle; - } - } - - // Get basic information. - if (BasicInformation) - { - status = PhpGetObjectBasicInformation( - ProcessHandle, - KphIsConnected() ? Handle : dupHandle, - BasicInformation - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - } - - // Exit early if we don't need to get any other information. - if (!TypeName && !ObjectName && !BestObjectName) - goto CleanupExit; - - // Get the type name. - status = PhpGetObjectTypeName( - ProcessHandle, - KphIsConnected() ? Handle : dupHandle, - ObjectTypeNumber, - &typeName - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - // Exit early if we don't need to get the object name. - if (!ObjectName && !BestObjectName) - goto CleanupExit; - - // Get the object name. - // If we're dealing with a file handle we must take special precautions so we don't hang. - if (PhEqualString2(typeName, L"File", TRUE) && !KphIsConnected()) - { -#define QUERY_NORMALLY 0 -#define QUERY_WITH_TIMEOUT 1 -#define QUERY_FAIL 2 - - ULONG hackLevel = QUERY_WITH_TIMEOUT; - - // We can't use the timeout method on XP because hanging threads can't even be terminated! - if (WindowsVersion <= WINDOWS_XP) - hackLevel = QUERY_FAIL; - - if (hackLevel == QUERY_NORMALLY || hackLevel == QUERY_WITH_TIMEOUT) - { - status = PhpGetObjectName( - ProcessHandle, - KphIsConnected() ? Handle : dupHandle, - hackLevel == QUERY_WITH_TIMEOUT, - &objectName - ); - } - else - { - // Pretend the file object has no name. - objectName = PhReferenceEmptyString(); - status = STATUS_SUCCESS; - } - } - else - { - // Query the object normally. - status = PhpGetObjectName( - ProcessHandle, - KphIsConnected() ? Handle : dupHandle, - FALSE, - &objectName - ); - } - - if (!NT_SUCCESS(status)) - { - if (PhEqualString2(typeName, L"File", TRUE) && KphIsConnected()) - { - // PhpGetBestObjectName can provide us with a name. - objectName = PhReferenceEmptyString(); - status = STATUS_SUCCESS; - } - else - { - subStatus = status; - status = STATUS_SUCCESS; - goto CleanupExit; - } - } - - // Exit early if we don't need to get the best object name. - if (!BestObjectName) - goto CleanupExit; - - status = PhpGetBestObjectName( - ProcessHandle, - Handle, - objectName, - typeName, - &bestObjectName - ); - - if (!NT_SUCCESS(status)) - { - subStatus = status; - status = STATUS_SUCCESS; - goto CleanupExit; - } - -CleanupExit: - - if (NT_SUCCESS(status)) - { - if (SubStatus) - *SubStatus = subStatus; - if (TypeName) - PhSetReference(TypeName, typeName); - if (ObjectName) - PhSetReference(ObjectName, objectName); - if (BestObjectName) - PhSetReference(BestObjectName, bestObjectName); - } - - if (dupHandle && ProcessHandle != NtCurrentProcess()) - NtClose(dupHandle); - - PhClearReference(&typeName); - PhClearReference(&objectName); - PhClearReference(&bestObjectName); - - return status; -} - -NTSTATUS PhEnumObjectTypes( - _Out_ POBJECT_TYPES_INFORMATION *ObjectTypes - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize; - - bufferSize = 0x1000; - buffer = PhAllocate(bufferSize); - - while ((status = NtQueryObject( - NULL, - ObjectTypesInformation, - buffer, - bufferSize, - NULL - )) == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - - // Fail if we're resizing the buffer to something very large. - if (bufferSize > PH_LARGE_BUFFER_SIZE) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *ObjectTypes = (POBJECT_TYPES_INFORMATION)buffer; - - return status; -} - -ULONG PhGetObjectTypeNumber( - _In_ PUNICODE_STRING TypeName - ) -{ - POBJECT_TYPES_INFORMATION objectTypes; - POBJECT_TYPE_INFORMATION objectType; - ULONG objectIndex = -1; - ULONG i; - - if (NT_SUCCESS(PhEnumObjectTypes(&objectTypes))) - { - objectType = PH_FIRST_OBJECT_TYPE(objectTypes); - - for (i = 0; i < objectTypes->NumberOfTypes; i++) - { - if (RtlEqualUnicodeString(&objectType->TypeName, TypeName, TRUE)) - { - if (WindowsVersion >= WINDOWS_8_1) - { - objectIndex = objectType->TypeIndex; - break; - } - else if (WindowsVersion >= WINDOWS_7) - { - objectIndex = i + 2; - break; - } - else - { - objectIndex = i + 1; - break; - } - } - - objectType = PH_NEXT_OBJECT_TYPE(objectType); - } - - PhFree(objectTypes); - } - - return objectIndex; -} - -PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT PhpAcquireCallWithTimeoutThread( - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - - PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT threadContext; - PSLIST_ENTRY listEntry; - PH_QUEUED_WAIT_BLOCK waitBlock; - - if (PhBeginInitOnce(&initOnce)) - { - ULONG i; - - for (i = 0; i < PH_QUERY_HACK_MAX_THREADS; i++) - { - threadContext = PhAllocate(sizeof(PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT)); - memset(threadContext, 0, sizeof(PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT)); - RtlInterlockedPushEntrySList(&PhpCallWithTimeoutThreadListHead, &threadContext->ListEntry); - } - - PhEndInitOnce(&initOnce); - } - - while (TRUE) - { - if (listEntry = RtlInterlockedPopEntrySList(&PhpCallWithTimeoutThreadListHead)) - break; - - if (!Timeout || Timeout->QuadPart != 0) - { - PhQueueWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, &waitBlock); - - if (listEntry = RtlInterlockedPopEntrySList(&PhpCallWithTimeoutThreadListHead)) - { - // A new entry has just become available; cancel the wait. - PhSetWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, &waitBlock); - break; - } - else - { - PhWaitForWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, &waitBlock, FALSE, Timeout); - // TODO: Recompute the timeout value. - } - } - else - { - return NULL; - } - } - - return CONTAINING_RECORD(listEntry, PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT, ListEntry); -} - -VOID PhpReleaseCallWithTimeoutThread( - _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext - ) -{ - RtlInterlockedPushEntrySList(&PhpCallWithTimeoutThreadListHead, &ThreadContext->ListEntry); - PhSetWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, NULL); -} - -NTSTATUS PhpCallWithTimeout( - _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext, - _In_ PUSER_THREAD_START_ROUTINE Routine, - _In_opt_ PVOID Context, - _In_ PLARGE_INTEGER Timeout - ) -{ - NTSTATUS status; - - // Create objects if necessary. - - if (!ThreadContext->StartEventHandle) - { - if (!NT_SUCCESS(status = NtCreateEvent(&ThreadContext->StartEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) - return status; - } - - if (!ThreadContext->CompletedEventHandle) - { - if (!NT_SUCCESS(status = NtCreateEvent(&ThreadContext->CompletedEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) - return status; - } - - // Create a query thread if we don't have one. - if (!ThreadContext->ThreadHandle) - { - CLIENT_ID clientId; - - NtClearEvent(ThreadContext->StartEventHandle); - NtClearEvent(ThreadContext->CompletedEventHandle); - - if (!NT_SUCCESS(status = RtlCreateUserThread( - NtCurrentProcess(), - NULL, - FALSE, - 0, - 0, - 32 * 1024, - PhpCallWithTimeoutThreadStart, - ThreadContext, - &ThreadContext->ThreadHandle, - &clientId - ))) - { - return status; - } - - // Wait for the thread to initialize. - NtWaitForSingleObject(ThreadContext->CompletedEventHandle, FALSE, NULL); - } - - ThreadContext->Routine = Routine; - ThreadContext->Context = Context; - - NtSetEvent(ThreadContext->StartEventHandle, NULL); - status = NtWaitForSingleObject(ThreadContext->CompletedEventHandle, FALSE, Timeout); - - ThreadContext->Routine = NULL; - MemoryBarrier(); - ThreadContext->Context = NULL; - - if (status != STATUS_WAIT_0) - { - // The operation timed out, or there was an error. Kill the thread. On Vista and above, the - // thread stack is freed automatically. - NtTerminateThread(ThreadContext->ThreadHandle, STATUS_UNSUCCESSFUL); - status = NtWaitForSingleObject(ThreadContext->ThreadHandle, FALSE, NULL); - NtClose(ThreadContext->ThreadHandle); - ThreadContext->ThreadHandle = NULL; - - status = STATUS_UNSUCCESSFUL; - } - - return status; -} - -NTSTATUS PhpCallWithTimeoutThreadStart( - _In_ PVOID Parameter - ) -{ - PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT threadContext = Parameter; - - NtSetEvent(threadContext->CompletedEventHandle, NULL); - - while (TRUE) - { - if (NtWaitForSingleObject(threadContext->StartEventHandle, FALSE, NULL) != STATUS_WAIT_0) - continue; - - if (threadContext->Routine) - threadContext->Routine(threadContext->Context); - - NtSetEvent(threadContext->CompletedEventHandle, NULL); - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhCallWithTimeout( - _In_ PUSER_THREAD_START_ROUTINE Routine, - _In_opt_ PVOID Context, - _In_opt_ PLARGE_INTEGER AcquireTimeout, - _In_ PLARGE_INTEGER CallTimeout - ) -{ - NTSTATUS status; - PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT threadContext; - - if (threadContext = PhpAcquireCallWithTimeoutThread(AcquireTimeout)) - { - status = PhpCallWithTimeout(threadContext, Routine, Context, CallTimeout); - PhpReleaseCallWithTimeoutThread(threadContext); - } - else - { - status = STATUS_UNSUCCESSFUL; - } - - return status; -} - -NTSTATUS PhpCommonQueryObjectRoutine( - _In_ PVOID Parameter - ) -{ - PPHP_QUERY_OBJECT_COMMON_CONTEXT context = Parameter; - - switch (context->Work) - { - case NtQueryObjectWork: - context->Status = NtQueryObject( - context->u.NtQueryObject.Handle, - context->u.NtQueryObject.ObjectInformationClass, - context->u.NtQueryObject.ObjectInformation, - context->u.NtQueryObject.ObjectInformationLength, - context->u.NtQueryObject.ReturnLength - ); - break; - case NtQuerySecurityObjectWork: - context->Status = NtQuerySecurityObject( - context->u.NtQuerySecurityObject.Handle, - context->u.NtQuerySecurityObject.SecurityInformation, - context->u.NtQuerySecurityObject.SecurityDescriptor, - context->u.NtQuerySecurityObject.Length, - context->u.NtQuerySecurityObject.LengthNeeded - ); - break; - case NtSetSecurityObjectWork: - context->Status = NtSetSecurityObject( - context->u.NtSetSecurityObject.Handle, - context->u.NtSetSecurityObject.SecurityInformation, - context->u.NtSetSecurityObject.SecurityDescriptor - ); - break; - default: - context->Status = STATUS_INVALID_PARAMETER; - break; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhpCommonQueryObjectWithTimeout( - _In_ PPHP_QUERY_OBJECT_COMMON_CONTEXT Context - ) -{ - NTSTATUS status; - LARGE_INTEGER timeout; - - timeout.QuadPart = -1 * PH_TIMEOUT_SEC; - status = PhCallWithTimeout(PhpCommonQueryObjectRoutine, Context, NULL, &timeout); - - if (NT_SUCCESS(status)) - status = Context->Status; - - PhFree(Context); - - return status; -} - -NTSTATUS PhCallNtQueryObjectWithTimeout( - _In_ HANDLE Handle, - _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, - _Out_writes_bytes_opt_(ObjectInformationLength) PVOID ObjectInformation, - _In_ ULONG ObjectInformationLength, - _Out_opt_ PULONG ReturnLength - ) -{ - PPHP_QUERY_OBJECT_COMMON_CONTEXT context; - - context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); - context->Work = NtQueryObjectWork; - context->Status = STATUS_UNSUCCESSFUL; - context->u.NtQueryObject.Handle = Handle; - context->u.NtQueryObject.ObjectInformationClass = ObjectInformationClass; - context->u.NtQueryObject.ObjectInformation = ObjectInformation; - context->u.NtQueryObject.ObjectInformationLength = ObjectInformationLength; - context->u.NtQueryObject.ReturnLength = ReturnLength; - - return PhpCommonQueryObjectWithTimeout(context); -} - -NTSTATUS PhCallNtQuerySecurityObjectWithTimeout( - _In_ HANDLE Handle, - _In_ SECURITY_INFORMATION SecurityInformation, - _Out_writes_bytes_opt_(Length) PSECURITY_DESCRIPTOR SecurityDescriptor, - _In_ ULONG Length, - _Out_ PULONG LengthNeeded - ) -{ - PPHP_QUERY_OBJECT_COMMON_CONTEXT context; - - context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); - context->Work = NtQuerySecurityObjectWork; - context->Status = STATUS_UNSUCCESSFUL; - context->u.NtQuerySecurityObject.Handle = Handle; - context->u.NtQuerySecurityObject.SecurityInformation = SecurityInformation; - context->u.NtQuerySecurityObject.SecurityDescriptor = SecurityDescriptor; - context->u.NtQuerySecurityObject.Length = Length; - context->u.NtQuerySecurityObject.LengthNeeded = LengthNeeded; - - return PhpCommonQueryObjectWithTimeout(context); -} - -NTSTATUS PhCallNtSetSecurityObjectWithTimeout( - _In_ HANDLE Handle, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ) -{ - PPHP_QUERY_OBJECT_COMMON_CONTEXT context; - - context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); - context->Work = NtSetSecurityObjectWork; - context->Status = STATUS_UNSUCCESSFUL; - context->u.NtSetSecurityObject.Handle = Handle; - context->u.NtSetSecurityObject.SecurityInformation = SecurityInformation; - context->u.NtSetSecurityObject.SecurityDescriptor = SecurityDescriptor; - - return PhpCommonQueryObjectWithTimeout(context); -} +/* + * Process Hacker - + * handle information + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#define PH_QUERY_HACK_MAX_THREADS 20 + +typedef struct _PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT +{ + SLIST_ENTRY ListEntry; + + PUSER_THREAD_START_ROUTINE Routine; + PVOID Context; + + HANDLE StartEventHandle; + HANDLE CompletedEventHandle; + HANDLE ThreadHandle; +} PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT, *PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT; + +typedef enum _PHP_QUERY_OBJECT_WORK +{ + NtQueryObjectWork, + NtQuerySecurityObjectWork, + NtSetSecurityObjectWork, + NtQueryFileInformationWork +} PHP_QUERY_OBJECT_WORK; + +typedef struct _PHP_QUERY_OBJECT_COMMON_CONTEXT +{ + PHP_QUERY_OBJECT_WORK Work; + NTSTATUS Status; + + union + { + struct + { + HANDLE Handle; + OBJECT_INFORMATION_CLASS ObjectInformationClass; + PVOID ObjectInformation; + ULONG ObjectInformationLength; + PULONG ReturnLength; + } NtQueryObject; + struct + { + HANDLE Handle; + SECURITY_INFORMATION SecurityInformation; + PSECURITY_DESCRIPTOR SecurityDescriptor; + ULONG Length; + PULONG LengthNeeded; + } NtQuerySecurityObject; + struct + { + HANDLE Handle; + SECURITY_INFORMATION SecurityInformation; + PSECURITY_DESCRIPTOR SecurityDescriptor; + } NtSetSecurityObject; + struct + { + HANDLE Handle; + FILE_INFORMATION_CLASS FileInformationClass; + PVOID FileInformation; + ULONG FileInformationLength; + } NtQueryFileInformation; + } u; +} PHP_QUERY_OBJECT_COMMON_CONTEXT, *PPHP_QUERY_OBJECT_COMMON_CONTEXT; + +PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT PhpAcquireCallWithTimeoutThread( + _In_opt_ PLARGE_INTEGER Timeout + ); + +VOID PhpReleaseCallWithTimeoutThread( + _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext + ); + +NTSTATUS PhpCallWithTimeout( + _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext, + _In_ PUSER_THREAD_START_ROUTINE Routine, + _In_opt_ PVOID Context, + _In_ PLARGE_INTEGER Timeout + ); + +NTSTATUS PhpCallWithTimeoutThreadStart( + _In_ PVOID Parameter + ); + +static PPH_STRING PhObjectTypeNames[MAX_OBJECT_TYPE_NUMBER] = { 0 }; +static PPH_GET_CLIENT_ID_NAME PhHandleGetClientIdName = PhStdGetClientIdName; + +static SLIST_HEADER PhpCallWithTimeoutThreadListHead; +static PH_WAKE_EVENT PhpCallWithTimeoutThreadReleaseEvent = PH_WAKE_EVENT_INIT; + +PPH_GET_CLIENT_ID_NAME PhSetHandleClientIdFunction( + _In_ PPH_GET_CLIENT_ID_NAME GetClientIdName + ) +{ + return _InterlockedExchangePointer( + (PVOID *)&PhHandleGetClientIdName, + GetClientIdName + ); +} + +NTSTATUS PhpGetObjectBasicInformation( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _Out_ POBJECT_BASIC_INFORMATION BasicInformation + ) +{ + NTSTATUS status; + + // TODO: KphIsVerified() fixes a bug on Windows 10 but this should be removed + // since KphQueryInformationObject doesn't require verification. (dmex) + + if (KphIsConnected() && KphIsVerified()) + { + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectBasicInformation, + BasicInformation, + sizeof(OBJECT_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + // The object was referenced in KProcessHacker, so we need to subtract 1 from the + // pointer count. + BasicInformation->PointerCount -= 1; + } + } + else + { + ULONG returnLength; + + status = NtQueryObject( + Handle, + ObjectBasicInformation, + BasicInformation, + sizeof(OBJECT_BASIC_INFORMATION), + &returnLength + ); + + if (NT_SUCCESS(status)) + { + // The object was referenced in NtQueryObject and a handle was opened to the object. We + // need to subtract 1 from the pointer count, then subtract 1 from both counts. + BasicInformation->HandleCount -= 1; + BasicInformation->PointerCount -= 2; + } + } + + return status; +} + +NTSTATUS PhpGetObjectTypeName( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ ULONG ObjectTypeNumber, + _Out_ PPH_STRING *TypeName + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PPH_STRING typeName = NULL; + + // dmex: Enumerate the available object types and pre-cache the object type name. + if (WindowsVersion >= WINDOWS_8_1) + { + static PH_INITONCE initOnce = PH_INITONCE_INIT; + + if (PhBeginInitOnce(&initOnce)) + { + POBJECT_TYPES_INFORMATION objectTypes; + POBJECT_TYPE_INFORMATION objectType; + + if (NT_SUCCESS(PhEnumObjectTypes(&objectTypes))) + { + objectType = PH_FIRST_OBJECT_TYPE(objectTypes); + + for (ULONG i = 0; i < objectTypes->NumberOfTypes; i++) + { + PhMoveReference( + &PhObjectTypeNames[objectType->TypeIndex], + PhCreateStringFromUnicodeString(&objectType->TypeName) + ); + + objectType = PH_NEXT_OBJECT_TYPE(objectType); + } + + PhFree(objectTypes); + } + + PhEndInitOnce(&initOnce); + } + } + + // If the cache contains the object type name, use it. Otherwise, query the type name. + + if (ObjectTypeNumber != ULONG_MAX && ObjectTypeNumber < MAX_OBJECT_TYPE_NUMBER) + typeName = PhObjectTypeNames[ObjectTypeNumber]; + + if (typeName) + { + PhReferenceObject(typeName); + } + else + { + POBJECT_TYPE_INFORMATION buffer; + ULONG returnLength = 0; + PPH_STRING oldTypeName; + + // Get the needed buffer size. + if (KphIsConnected()) + { + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectTypeInformation, + NULL, + 0, + &returnLength + ); + } + else + { + status = NtQueryObject( + Handle, + ObjectTypeInformation, + NULL, + 0, + &returnLength + ); + } + + if (returnLength == 0) + return status; + + buffer = PhAllocate(returnLength); + + if (KphIsConnected()) + { + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectTypeInformation, + buffer, + returnLength, + &returnLength + ); + } + else + { + status = NtQueryObject( + Handle, + ObjectTypeInformation, + buffer, + returnLength, + &returnLength + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + // Create a copy of the type name. + typeName = PhCreateStringFromUnicodeString(&buffer->TypeName); + + if (ObjectTypeNumber != ULONG_MAX && ObjectTypeNumber < MAX_OBJECT_TYPE_NUMBER) + { + // Try to store the type name in the cache. + oldTypeName = _InterlockedCompareExchangePointer( + &PhObjectTypeNames[ObjectTypeNumber], + typeName, + NULL + ); + + // Add a reference if we stored the type name successfully. + if (!oldTypeName) + PhReferenceObject(typeName); + } + + PhFree(buffer); + } + + // At this point typeName should contain a type name with one additional reference. + + *TypeName = typeName; + + return status; +} + +NTSTATUS PhpGetObjectName( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ BOOLEAN WithTimeout, + _Out_ PPH_STRING *ObjectName + ) +{ + NTSTATUS status; + POBJECT_NAME_INFORMATION buffer; + ULONG bufferSize; + ULONG attempts = 8; + + bufferSize = 0x200; + buffer = PhAllocate(bufferSize); + + // A loop is needed because the I/O subsystem likes to give us the wrong return lengths... + do + { + if (KphIsConnected()) + { + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectNameInformation, + buffer, + bufferSize, + &bufferSize + ); + } + else + { + if (WithTimeout) + { + status = PhCallNtQueryObjectWithTimeout( + Handle, + ObjectNameInformation, + buffer, + bufferSize, + &bufferSize + ); + } + else + { + status = NtQueryObject( + Handle, + ObjectNameInformation, + buffer, + bufferSize, + &bufferSize + ); + } + } + + if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH || + status == STATUS_BUFFER_TOO_SMALL) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } while (--attempts); + + if (NT_SUCCESS(status)) + { + *ObjectName = PhCreateStringFromUnicodeString(&buffer->Name); + } + + PhFree(buffer); + + return status; +} + +NTSTATUS PhpGetEtwObjectName( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _Out_ PPH_STRING *ObjectName + ) +{ + NTSTATUS status; + ETWREG_BASIC_INFORMATION basicInfo; + + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectEtwRegBasicInformation, + &basicInfo, + sizeof(ETWREG_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + *ObjectName = PhFormatGuid(&basicInfo.Guid); + } + + return status; +} + +PPH_STRING PhFormatNativeKeyName( + _In_ PPH_STRING Name + ) +{ + static PH_STRINGREF hklmPrefix = PH_STRINGREF_INIT(L"\\Registry\\Machine"); + static PH_STRINGREF hkcrPrefix = PH_STRINGREF_INIT(L"\\Registry\\Machine\\Software\\Classes"); + static PH_STRINGREF hkuPrefix = PH_STRINGREF_INIT(L"\\Registry\\User"); + static PPH_STRING hkcuPrefix; + static PPH_STRING hkcucrPrefix; + + static PH_STRINGREF hklmString = PH_STRINGREF_INIT(L"HKLM"); + static PH_STRINGREF hkcrString = PH_STRINGREF_INIT(L"HKCR"); + static PH_STRINGREF hkuString = PH_STRINGREF_INIT(L"HKU"); + static PH_STRINGREF hkcuString = PH_STRINGREF_INIT(L"HKCU"); + static PH_STRINGREF hkcucrString = PH_STRINGREF_INIT(L"HKCU\\Software\\Classes"); + + static PH_INITONCE initOnce = PH_INITONCE_INIT; + + PPH_STRING newName; + PH_STRINGREF name; + + if (PhBeginInitOnce(&initOnce)) + { + PTOKEN_USER tokenUser; + PPH_STRING stringSid = NULL; + + if (NT_SUCCESS(PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &tokenUser))) + { + stringSid = PhSidToStringSid(tokenUser->User.Sid); + PhFree(tokenUser); + } + + if (stringSid) + { + static PH_STRINGREF registryUserPrefix = PH_STRINGREF_INIT(L"\\Registry\\User\\"); + static PH_STRINGREF classesString = PH_STRINGREF_INIT(L"_Classes"); + + hkcuPrefix = PhConcatStringRef2(®istryUserPrefix, &stringSid->sr); + hkcucrPrefix = PhConcatStringRef2(&hkcuPrefix->sr, &classesString); + + PhDereferenceObject(stringSid); + } + else + { + hkcuPrefix = PhCreateString(L"..."); // some random string that won't ever get matched + hkcucrPrefix = PhCreateString(L"..."); + } + + PhEndInitOnce(&initOnce); + } + + name = Name->sr; + + if (PhStartsWithStringRef(&name, &hkcrPrefix, TRUE)) + { + PhSkipStringRef(&name, hkcrPrefix.Length); + newName = PhConcatStringRef2(&hkcrString, &name); + } + else if (PhStartsWithStringRef(&name, &hklmPrefix, TRUE)) + { + PhSkipStringRef(&name, hklmPrefix.Length); + newName = PhConcatStringRef2(&hklmString, &name); + } + else if (PhStartsWithStringRef(&name, &hkcucrPrefix->sr, TRUE)) + { + PhSkipStringRef(&name, hkcucrPrefix->Length); + newName = PhConcatStringRef2(&hkcucrString, &name); + } + else if (PhStartsWithStringRef(&name, &hkcuPrefix->sr, TRUE)) + { + PhSkipStringRef(&name, hkcuPrefix->Length); + newName = PhConcatStringRef2(&hkcuString, &name); + } + else if (PhStartsWithStringRef(&name, &hkuPrefix, TRUE)) + { + PhSkipStringRef(&name, hkuPrefix.Length); + newName = PhConcatStringRef2(&hkuString, &name); + } + else + { + PhSetReference(&newName, Name); + } + + return newName; +} + +NTSTATUS PhGetSectionFileName( + _In_ HANDLE SectionHandle, + _Out_ PPH_STRING *FileName + ) +{ + NTSTATUS status; + SIZE_T viewSize; + PVOID viewBase; + + viewSize = 1; + viewBase = NULL; + + status = NtMapViewOfSection( + SectionHandle, + NtCurrentProcess(), + &viewBase, + 0, + 0, + NULL, + &viewSize, + ViewShare, + 0, + PAGE_READONLY + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PhGetProcessMappedFileName(NtCurrentProcess(), viewBase, FileName); + NtUnmapViewOfSection(NtCurrentProcess(), viewBase); + + return status; +} + +_Callback_ PPH_STRING PhStdGetClientIdName( + _In_ PCLIENT_ID ClientId + ) +{ + static PH_QUEUED_LOCK cachedProcessesLock = PH_QUEUED_LOCK_INIT; + static PVOID processes = NULL; + static ULONG64 lastProcessesTickCount = 0; + + PPH_STRING name; + ULONG64 tickCount; + PSYSTEM_PROCESS_INFORMATION processInfo; + + // Get a new process list only if 2 seconds have passed since the last update. + + tickCount = NtGetTickCount64(); + + if (tickCount - lastProcessesTickCount >= 2000) + { + PhAcquireQueuedLockExclusive(&cachedProcessesLock); + + // Re-check the tick count. + if (tickCount - lastProcessesTickCount >= 2000) + { + if (processes) + { + PhFree(processes); + processes = NULL; + } + + if (!NT_SUCCESS(PhEnumProcesses(&processes))) + { + PhReleaseQueuedLockExclusive(&cachedProcessesLock); + return PhCreateString(L"(Error querying processes)"); + } + + lastProcessesTickCount = tickCount; + } + + PhReleaseQueuedLockExclusive(&cachedProcessesLock); + } + + // Get a lock on the process list and get a name for the client ID. + + PhAcquireQueuedLockShared(&cachedProcessesLock); + + if (!processes) + { + PhReleaseQueuedLockShared(&cachedProcessesLock); + return NULL; + } + + processInfo = PhFindProcessInformation(processes, ClientId->UniqueProcess); + + if (ClientId->UniqueThread) + { + if (processInfo) + { + name = PhFormatString( + L"%.*s (%lu): %lu", + processInfo->ImageName.Length / sizeof(WCHAR), + processInfo->ImageName.Buffer, + HandleToUlong(ClientId->UniqueProcess), + HandleToUlong(ClientId->UniqueThread) + ); + } + else + { + name = PhFormatString( + L"Non-existent process (%lu): %lu", + HandleToUlong(ClientId->UniqueProcess), + HandleToUlong(ClientId->UniqueThread) + ); + } + } + else + { + if (processInfo) + { + name = PhFormatString( + L"%.*s (%lu)", + processInfo->ImageName.Length / sizeof(WCHAR), + processInfo->ImageName.Buffer, + HandleToUlong(ClientId->UniqueProcess) + ); + } + else + { + name = PhFormatString(L"Non-existent process (%lu)", HandleToUlong(ClientId->UniqueProcess)); + } + } + + PhReleaseQueuedLockShared(&cachedProcessesLock); + + return name; +} + +NTSTATUS PhpGetBestObjectName( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ PPH_STRING ObjectName, + _In_ PPH_STRING TypeName, + _Out_ PPH_STRING *BestObjectName + ) +{ + NTSTATUS status; + PPH_STRING bestObjectName = NULL; + PPH_GET_CLIENT_ID_NAME handleGetClientIdName = PhHandleGetClientIdName; + + if (PhEqualString2(TypeName, L"EtwRegistration", TRUE)) + { + if (KphIsConnected()) + { + ETWREG_BASIC_INFORMATION basicInfo; + + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectEtwRegBasicInformation, + &basicInfo, + sizeof(ETWREG_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + static PH_STRINGREF publishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\"); + + PPH_STRING guidString; + PPH_STRING keyName; + HANDLE keyHandle; + PPH_STRING publisherName = NULL; + + guidString = PhFormatGuid(&basicInfo.Guid); + + // We should perform a lookup on the GUID to get the publisher name. + + keyName = PhConcatStringRef2(&publishersKeyName, &guidString->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) + { + publisherName = PhQueryRegistryString(keyHandle, NULL); + + if (publisherName && publisherName->Length == 0) + { + PhDereferenceObject(publisherName); + publisherName = NULL; + } + + NtClose(keyHandle); + } + + PhDereferenceObject(keyName); + + if (publisherName) + { + bestObjectName = publisherName; + PhDereferenceObject(guidString); + } + else + { + bestObjectName = guidString; + } + } + } + } + else if (PhEqualString2(TypeName, L"File", TRUE)) + { + // Convert the file name to a DOS file name. + bestObjectName = PhResolveDevicePrefix(ObjectName); + + if (!bestObjectName) + { + // The file doesn't have a DOS name. + PhSetReference(&bestObjectName, ObjectName); + } + + if (PhIsNullOrEmptyString(bestObjectName) && KphIsConnected()) + { + KPH_FILE_OBJECT_DRIVER fileObjectDriver; + PPH_STRING driverName; + + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectFileObjectDriver, + &fileObjectDriver, + sizeof(KPH_FILE_OBJECT_DRIVER), + NULL + ); + + if (NT_SUCCESS(status) && fileObjectDriver.DriverHandle) + { + if (NT_SUCCESS(PhGetDriverName(fileObjectDriver.DriverHandle, &driverName))) + { + static PH_STRINGREF prefix = PH_STRINGREF_INIT(L"Unnamed file: "); + + PhMoveReference(&bestObjectName, PhConcatStringRef2(&prefix, &driverName->sr)); + PhDereferenceObject(driverName); + } + + NtClose(fileObjectDriver.DriverHandle); + } + } + } + // Note: Uncomment below code if we want to return job names identical to Process Explorer. + // Todo: Should we remove since it's not the actual job name? -dmex + //else if (PhEqualString2(TypeName, L"Job", TRUE)) + //{ + // HANDLE dupHandle; + // PJOBOBJECT_BASIC_PROCESS_ID_LIST processIdList; + // + // // dmex: Don't do anything when we already have a valid job object name. + // if (!PhIsNullOrEmptyString(ObjectName)) + // goto CleanupExit; + // + // status = NtDuplicateObject( + // ProcessHandle, + // Handle, + // NtCurrentProcess(), + // &dupHandle, + // JOB_OBJECT_QUERY, + // 0, + // 0 + // ); + // + // if (!NT_SUCCESS(status)) + // goto CleanupExit; + // + // if (handleGetClientIdName && NT_SUCCESS(PhGetJobProcessIdList(dupHandle, &processIdList))) + // { + // PH_STRING_BUILDER sb; + // ULONG i; + // CLIENT_ID clientId; + // PPH_STRING name; + // + // PhInitializeStringBuilder(&sb, 40); + // clientId.UniqueThread = NULL; + // + // for (i = 0; i < processIdList->NumberOfProcessIdsInList; i++) + // { + // clientId.UniqueProcess = (HANDLE)processIdList->ProcessIdList[i]; + // name = handleGetClientIdName(&clientId); + // + // if (name) + // { + // PhAppendStringBuilder(&sb, &name->sr); + // PhAppendStringBuilder2(&sb, L"; "); + // PhDereferenceObject(name); + // } + // } + // + // PhFree(processIdList); + // + // if (sb.String->Length != 0) + // PhRemoveEndStringBuilder(&sb, 2); + // + // if (sb.String->Length == 0) + // PhAppendStringBuilder2(&sb, L"(No processes)"); + // + // bestObjectName = PhFinalStringBuilderString(&sb); + // } + // + // NtClose(dupHandle); + //} + else if (PhEqualString2(TypeName, L"Key", TRUE)) + { + bestObjectName = PhFormatNativeKeyName(ObjectName); + } + else if (PhEqualString2(TypeName, L"Process", TRUE)) + { + CLIENT_ID clientId; + + clientId.UniqueThread = NULL; + + if (KphIsConnected()) + { + PROCESS_BASIC_INFORMATION basicInfo; + + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectProcessBasicInformation, + &basicInfo, + sizeof(PROCESS_BASIC_INFORMATION), + NULL + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + clientId.UniqueProcess = basicInfo.UniqueProcessId; + } + else + { + HANDLE dupHandle; + PROCESS_BASIC_INFORMATION basicInfo; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetProcessBasicInformation(dupHandle, &basicInfo); + NtClose(dupHandle); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + clientId.UniqueProcess = basicInfo.UniqueProcessId; + } + + if (handleGetClientIdName) + bestObjectName = handleGetClientIdName(&clientId); + } + else if (PhEqualString2(TypeName, L"Section", TRUE)) + { + HANDLE dupHandle; + PPH_STRING fileName; + + if (!PhIsNullOrEmptyString(ObjectName)) + goto CleanupExit; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + SECTION_QUERY | SECTION_MAP_READ, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetSectionFileName(dupHandle, &fileName); + + if (NT_SUCCESS(status)) + { + bestObjectName = PhResolveDevicePrefix(fileName); + PhDereferenceObject(fileName); + } + else + { + SECTION_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(PhGetSectionBasicInformation(dupHandle, &basicInfo))) + { + PH_FORMAT format[4]; + PWSTR sectionType = L"Unknown"; + + if (basicInfo.AllocationAttributes & SEC_COMMIT) + sectionType = L"Commit"; + else if (basicInfo.AllocationAttributes & SEC_FILE) + sectionType = L"File"; + else if (basicInfo.AllocationAttributes & SEC_IMAGE) + sectionType = L"Image"; + else if (basicInfo.AllocationAttributes & SEC_RESERVE) + sectionType = L"Reserve"; + + PhInitFormatS(&format[0], sectionType); + PhInitFormatS(&format[1], L" ("); + PhInitFormatSize(&format[2], basicInfo.MaximumSize.QuadPart); + PhInitFormatC(&format[3], ')'); + bestObjectName = PhFormat(format, 4, 20); + } + } + + NtClose(dupHandle); + } + else if (PhEqualString2(TypeName, L"Thread", TRUE)) + { + CLIENT_ID clientId; + + if (KphIsConnected()) + { + THREAD_BASIC_INFORMATION basicInfo; + + status = KphQueryInformationObject( + ProcessHandle, + Handle, + KphObjectThreadBasicInformation, + &basicInfo, + sizeof(THREAD_BASIC_INFORMATION), + NULL + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + clientId = basicInfo.ClientId; + } + else + { + HANDLE dupHandle; + THREAD_BASIC_INFORMATION basicInfo; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + THREAD_QUERY_LIMITED_INFORMATION, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetThreadBasicInformation(dupHandle, &basicInfo); + NtClose(dupHandle); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + clientId = basicInfo.ClientId; + } + + if (handleGetClientIdName) + bestObjectName = handleGetClientIdName(&clientId); + } + else if (PhEqualString2(TypeName, L"TmEn", TRUE)) + { + HANDLE dupHandle; + ENLISTMENT_BASIC_INFORMATION basicInfo; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + ENLISTMENT_QUERY_INFORMATION, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetEnlistmentBasicInformation(dupHandle, &basicInfo); + NtClose(dupHandle); + + if (NT_SUCCESS(status)) + { + bestObjectName = PhFormatGuid(&basicInfo.EnlistmentId); + } + } + else if (PhEqualString2(TypeName, L"TmRm", TRUE)) + { + HANDLE dupHandle; + GUID guid; + PPH_STRING description; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + RESOURCEMANAGER_QUERY_INFORMATION, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetResourceManagerBasicInformation( + dupHandle, + &guid, + &description + ); + NtClose(dupHandle); + + if (NT_SUCCESS(status)) + { + if (!PhIsNullOrEmptyString(description)) + { + bestObjectName = description; + } + else + { + bestObjectName = PhFormatGuid(&guid); + + if (description) + PhDereferenceObject(description); + } + } + } + else if (PhEqualString2(TypeName, L"TmTm", TRUE)) + { + HANDLE dupHandle; + PPH_STRING logFileName = NULL; + TRANSACTIONMANAGER_BASIC_INFORMATION basicInfo; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + TRANSACTIONMANAGER_QUERY_INFORMATION, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetTransactionManagerLogFileName( + dupHandle, + &logFileName + ); + + if (NT_SUCCESS(status) && !PhIsNullOrEmptyString(logFileName)) + { + bestObjectName = PhGetFileName(logFileName); + PhDereferenceObject(logFileName); + } + else + { + if (logFileName) + PhDereferenceObject(logFileName); + + status = PhGetTransactionManagerBasicInformation( + dupHandle, + &basicInfo + ); + + if (NT_SUCCESS(status)) + { + bestObjectName = PhFormatGuid(&basicInfo.TmIdentity); + } + } + + NtClose(dupHandle); + } + else if (PhEqualString2(TypeName, L"TmTx", TRUE)) + { + HANDLE dupHandle; + PPH_STRING description = NULL; + TRANSACTION_BASIC_INFORMATION basicInfo; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + TRANSACTION_QUERY_INFORMATION, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetTransactionPropertiesInformation( + dupHandle, + NULL, + NULL, + &description + ); + + if (NT_SUCCESS(status) && !PhIsNullOrEmptyString(description)) + { + bestObjectName = description; + } + else + { + if (description) + PhDereferenceObject(description); + + status = PhGetTransactionBasicInformation( + dupHandle, + &basicInfo + ); + + if (NT_SUCCESS(status)) + { + bestObjectName = PhFormatGuid(&basicInfo.TransactionId); + } + } + + NtClose(dupHandle); + } + else if (PhEqualString2(TypeName, L"Token", TRUE)) + { + HANDLE dupHandle; + PTOKEN_USER tokenUser = NULL; + TOKEN_STATISTICS statistics = { 0 }; + + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + TOKEN_QUERY, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetTokenUser(dupHandle, &tokenUser); + PhGetTokenStatistics(dupHandle, &statistics); + + if (NT_SUCCESS(status)) + { + PPH_STRING fullName; + + fullName = PhGetSidFullName(tokenUser->User.Sid, TRUE, NULL); + + if (fullName) + { + PH_FORMAT format[4]; + + PhInitFormatSR(&format[0], fullName->sr); + PhInitFormatS(&format[1], L": 0x"); + PhInitFormatX(&format[2], statistics.AuthenticationId.LowPart); + PhInitFormatS(&format[3], statistics.TokenType == TokenPrimary ? L" (Primary)" : L" (Impersonation)"); + + bestObjectName = PhFormat(format, 4, fullName->Length + 8 + 16 + 16); + PhDereferenceObject(fullName); + } + + PhFree(tokenUser); + } + + NtClose(dupHandle); + } + +CleanupExit: + + if (!bestObjectName) + PhSetReference(&bestObjectName, ObjectName); + + *BestObjectName = bestObjectName; + + return STATUS_SUCCESS; +} + +/** + * Gets information for a handle. + * + * \param ProcessHandle A handle to the process in which the handle resides. + * \param Handle The handle value. + * \param ObjectTypeNumber The object type number of the handle. You can specify -1 for this + * parameter if the object type number is not known. + * \param BasicInformation A variable which receives basic information about the object. + * \param TypeName A variable which receives the object type name. + * \param ObjectName A variable which receives the object name. + * \param BestObjectName A variable which receives the formatted object name. + * + * \retval STATUS_INVALID_HANDLE The handle specified in \c ProcessHandle or \c Handle is invalid. + * \retval STATUS_INVALID_PARAMETER_3 The value specified in \c ObjectTypeNumber is invalid. + */ +NTSTATUS PhGetHandleInformation( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ ULONG ObjectTypeNumber, + _Out_opt_ POBJECT_BASIC_INFORMATION BasicInformation, + _Out_opt_ PPH_STRING *TypeName, + _Out_opt_ PPH_STRING *ObjectName, + _Out_opt_ PPH_STRING *BestObjectName + ) +{ + NTSTATUS status; + NTSTATUS subStatus; + + status = PhGetHandleInformationEx( + ProcessHandle, + Handle, + ObjectTypeNumber, + 0, + &subStatus, + BasicInformation, + TypeName, + ObjectName, + BestObjectName, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Fail if any component failed, for compatibility reasons. + if (!NT_SUCCESS(subStatus)) + { + if (TypeName) + PhClearReference(TypeName); + if (ObjectName) + PhClearReference(ObjectName); + if (BestObjectName) + PhClearReference(BestObjectName); + + return subStatus; + } + + return status; +} + +/** + * Gets information for a handle. + * + * \param ProcessHandle A handle to the process in which the handle resides. + * \param Handle The handle value. + * \param ObjectTypeNumber The object type number of the handle. You can specify -1 for this + * parameter if the object type number is not known. + * \param Flags Reserved. + * \param SubStatus A variable which receives the NTSTATUS value of the last component that fails. + * If all operations succeed, the value will be STATUS_SUCCESS. If the function returns an error + * status, this variable is not set. + * \param BasicInformation A variable which receives basic information about the object. + * \param TypeName A variable which receives the object type name. + * \param ObjectName A variable which receives the object name. + * \param BestObjectName A variable which receives the formatted object name. + * \param ExtraInformation Reserved. + * + * \retval STATUS_INVALID_HANDLE The handle specified in \c ProcessHandle or \c Handle is invalid. + * \retval STATUS_INVALID_PARAMETER_3 The value specified in \c ObjectTypeNumber is invalid. + * + * \remarks If \a BasicInformation or \a TypeName are specified, the function will fail if either + * cannot be queried. \a ObjectName, \a BestObjectName and \a ExtraInformation will be NULL if they + * cannot be queried. + */ +NTSTATUS PhGetHandleInformationEx( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ ULONG ObjectTypeNumber, + _Reserved_ ULONG Flags, + _Out_opt_ PNTSTATUS SubStatus, + _Out_opt_ POBJECT_BASIC_INFORMATION BasicInformation, + _Out_opt_ PPH_STRING *TypeName, + _Out_opt_ PPH_STRING *ObjectName, + _Out_opt_ PPH_STRING *BestObjectName, + _Reserved_ PVOID *ExtraInformation + ) +{ + NTSTATUS status = STATUS_SUCCESS; + NTSTATUS subStatus = STATUS_SUCCESS; + HANDLE dupHandle = NULL; + PPH_STRING typeName = NULL; + PPH_STRING objectName = NULL; + PPH_STRING bestObjectName = NULL; + + if (Handle == NULL || Handle == NtCurrentProcess() || Handle == NtCurrentThread()) + return STATUS_INVALID_HANDLE; + if (ObjectTypeNumber != ULONG_MAX && ObjectTypeNumber >= MAX_OBJECT_TYPE_NUMBER) + return STATUS_INVALID_PARAMETER_3; + + // Duplicate the handle if we're not using KPH. + if (!KphIsConnected()) + { + // However, we obviously don't need to duplicate it + // if the handle is in the current process. + if (ProcessHandle != NtCurrentProcess()) + { + status = NtDuplicateObject( + ProcessHandle, + Handle, + NtCurrentProcess(), + &dupHandle, + 0, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + return status; + } + else + { + dupHandle = Handle; + } + } + + // Get basic information. + if (BasicInformation) + { + status = PhpGetObjectBasicInformation( + ProcessHandle, + KphIsConnected() ? Handle : dupHandle, + BasicInformation + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + } + + // Exit early if we don't need to get any other information. + if (!TypeName && !ObjectName && !BestObjectName) + goto CleanupExit; + + // Get the type name. + status = PhpGetObjectTypeName( + ProcessHandle, + KphIsConnected() ? Handle : dupHandle, + ObjectTypeNumber, + &typeName + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + // Exit early if we don't need to get the object name. + if (!ObjectName && !BestObjectName) + goto CleanupExit; + + // Get the object name. + // If we're dealing with a file handle we must take special precautions so we don't hang. + if (PhEqualString2(typeName, L"File", TRUE) && !KphIsConnected()) + { + status = PhpGetObjectName( + ProcessHandle, + KphIsConnected() ? Handle : dupHandle, + TRUE, + &objectName + ); + } + else if (PhEqualString2(typeName, L"EtwRegistration", TRUE) && KphIsConnected()) + { + status = PhpGetEtwObjectName( + ProcessHandle, + Handle, + &objectName + ); + } + else + { + // Query the object normally. + status = PhpGetObjectName( + ProcessHandle, + KphIsConnected() ? Handle : dupHandle, + FALSE, + &objectName + ); + } + + if (!NT_SUCCESS(status)) + { + if (PhEqualString2(typeName, L"File", TRUE) && KphIsConnected()) + { + // PhpGetBestObjectName can provide us with a name. + objectName = PhReferenceEmptyString(); + status = STATUS_SUCCESS; + } + else + { + subStatus = status; + status = STATUS_SUCCESS; + goto CleanupExit; + } + } + + // Exit early if we don't need to get the best object name. + if (!BestObjectName) + goto CleanupExit; + + status = PhpGetBestObjectName( + ProcessHandle, + Handle, + objectName, + typeName, + &bestObjectName + ); + + if (!NT_SUCCESS(status)) + { + subStatus = status; + status = STATUS_SUCCESS; + goto CleanupExit; + } + +CleanupExit: + + if (NT_SUCCESS(status)) + { + if (SubStatus) + *SubStatus = subStatus; + if (TypeName) + PhSetReference(TypeName, typeName); + if (ObjectName) + PhSetReference(ObjectName, objectName); + if (BestObjectName) + PhSetReference(BestObjectName, bestObjectName); + } + + if (dupHandle && ProcessHandle != NtCurrentProcess()) + NtClose(dupHandle); + + PhClearReference(&typeName); + PhClearReference(&objectName); + PhClearReference(&bestObjectName); + + return status; +} + +NTSTATUS PhEnumObjectTypes( + _Out_ POBJECT_TYPES_INFORMATION *ObjectTypes + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + ULONG returnLength; + + bufferSize = 0x1000; + buffer = PhAllocate(bufferSize); + + while ((status = NtQueryObject( + NULL, + ObjectTypesInformation, + buffer, + bufferSize, + &returnLength + )) == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *ObjectTypes = (POBJECT_TYPES_INFORMATION)buffer; + + return status; +} + +ULONG PhGetObjectTypeNumber( + _In_ PUNICODE_STRING TypeName + ) +{ + POBJECT_TYPES_INFORMATION objectTypes; + POBJECT_TYPE_INFORMATION objectType; + ULONG objectIndex = ULONG_MAX; + ULONG i; + + if (NT_SUCCESS(PhEnumObjectTypes(&objectTypes))) + { + objectType = PH_FIRST_OBJECT_TYPE(objectTypes); + + for (i = 0; i < objectTypes->NumberOfTypes; i++) + { + if (RtlEqualUnicodeString(&objectType->TypeName, TypeName, TRUE)) + { + if (WindowsVersion >= WINDOWS_8_1) + { + objectIndex = objectType->TypeIndex; + break; + } + else + { + objectIndex = i + 2; + break; + } + } + + objectType = PH_NEXT_OBJECT_TYPE(objectType); + } + + PhFree(objectTypes); + } + + return objectIndex; +} + +PPH_STRING PhGetObjectTypeName( + _In_ ULONG TypeIndex + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static POBJECT_TYPES_INFORMATION objectTypes = NULL; + POBJECT_TYPE_INFORMATION objectType; + PPH_STRING objectTypeName = NULL; + ULONG i; + + if (PhBeginInitOnce(&initOnce)) + { + PhEnumObjectTypes(&objectTypes); + PhEndInitOnce(&initOnce); + } + + if (objectTypes) + { + objectType = PH_FIRST_OBJECT_TYPE(objectTypes); + + for (i = 0; i < objectTypes->NumberOfTypes; i++) + { + if (WindowsVersion >= WINDOWS_8_1) + { + if (TypeIndex == objectType->TypeIndex) + { + objectTypeName = PhCreateStringFromUnicodeString(&objectType->TypeName); + break; + } + } + else + { + if (TypeIndex == (i + 2)) + { + objectTypeName = PhCreateStringFromUnicodeString(&objectType->TypeName); + break; + } + } + + objectType = PH_NEXT_OBJECT_TYPE(objectType); + } + } + + return objectTypeName; +} + +PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT PhpAcquireCallWithTimeoutThread( + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT threadContext; + PSLIST_ENTRY listEntry; + PH_QUEUED_WAIT_BLOCK waitBlock; + + if (PhBeginInitOnce(&initOnce)) + { + ULONG i; + + for (i = 0; i < PH_QUERY_HACK_MAX_THREADS; i++) + { + threadContext = PhAllocate(sizeof(PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT)); + memset(threadContext, 0, sizeof(PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT)); + RtlInterlockedPushEntrySList(&PhpCallWithTimeoutThreadListHead, &threadContext->ListEntry); + } + + PhEndInitOnce(&initOnce); + } + + while (TRUE) + { + if (listEntry = RtlInterlockedPopEntrySList(&PhpCallWithTimeoutThreadListHead)) + break; + + if (!Timeout || Timeout->QuadPart != 0) + { + PhQueueWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, &waitBlock); + + if (listEntry = RtlInterlockedPopEntrySList(&PhpCallWithTimeoutThreadListHead)) + { + // A new entry has just become available; cancel the wait. + PhSetWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, &waitBlock); + break; + } + else + { + PhWaitForWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, &waitBlock, FALSE, Timeout); + // TODO: Recompute the timeout value. + } + } + else + { + return NULL; + } + } + + return CONTAINING_RECORD(listEntry, PHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT, ListEntry); +} + +VOID PhpReleaseCallWithTimeoutThread( + _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext + ) +{ + RtlInterlockedPushEntrySList(&PhpCallWithTimeoutThreadListHead, &ThreadContext->ListEntry); + PhSetWakeEvent(&PhpCallWithTimeoutThreadReleaseEvent, NULL); +} + +NTSTATUS PhpCallWithTimeout( + _Inout_ PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT ThreadContext, + _In_ PUSER_THREAD_START_ROUTINE Routine, + _In_opt_ PVOID Context, + _In_ PLARGE_INTEGER Timeout + ) +{ + NTSTATUS status; + + // Create objects if necessary. + + if (!ThreadContext->StartEventHandle) + { + if (!NT_SUCCESS(status = NtCreateEvent(&ThreadContext->StartEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) + return status; + } + + if (!ThreadContext->CompletedEventHandle) + { + if (!NT_SUCCESS(status = NtCreateEvent(&ThreadContext->CompletedEventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) + return status; + } + + // Create a query thread if we don't have one. + if (!ThreadContext->ThreadHandle) + { + CLIENT_ID clientId; + + NtClearEvent(ThreadContext->StartEventHandle); + NtClearEvent(ThreadContext->CompletedEventHandle); + + if (!NT_SUCCESS(status = RtlCreateUserThread( + NtCurrentProcess(), + NULL, + FALSE, + 0, + 0, + 32 * 1024, + PhpCallWithTimeoutThreadStart, + ThreadContext, + &ThreadContext->ThreadHandle, + &clientId + ))) + { + return status; + } + + // Wait for the thread to initialize. + NtWaitForSingleObject(ThreadContext->CompletedEventHandle, FALSE, NULL); + } + + ThreadContext->Routine = Routine; + ThreadContext->Context = Context; + + NtSetEvent(ThreadContext->StartEventHandle, NULL); + status = NtWaitForSingleObject(ThreadContext->CompletedEventHandle, FALSE, Timeout); + + ThreadContext->Routine = NULL; + MemoryBarrier(); + ThreadContext->Context = NULL; + + if (status != STATUS_WAIT_0) + { + // The operation timed out, or there was an error. Kill the thread. On Vista and above, the + // thread stack is freed automatically. + NtTerminateThread(ThreadContext->ThreadHandle, STATUS_UNSUCCESSFUL); + status = NtWaitForSingleObject(ThreadContext->ThreadHandle, FALSE, NULL); + NtClose(ThreadContext->ThreadHandle); + ThreadContext->ThreadHandle = NULL; + + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +NTSTATUS PhpCallWithTimeoutThreadStart( + _In_ PVOID Parameter + ) +{ + PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT threadContext = Parameter; + + NtSetEvent(threadContext->CompletedEventHandle, NULL); + + while (TRUE) + { + if (NtWaitForSingleObject(threadContext->StartEventHandle, FALSE, NULL) != STATUS_WAIT_0) + continue; + + if (threadContext->Routine) + threadContext->Routine(threadContext->Context); + + NtSetEvent(threadContext->CompletedEventHandle, NULL); + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhCallWithTimeout( + _In_ PUSER_THREAD_START_ROUTINE Routine, + _In_opt_ PVOID Context, + _In_opt_ PLARGE_INTEGER AcquireTimeout, + _In_ PLARGE_INTEGER CallTimeout + ) +{ + NTSTATUS status; + PPHP_CALL_WITH_TIMEOUT_THREAD_CONTEXT threadContext; + + if (threadContext = PhpAcquireCallWithTimeoutThread(AcquireTimeout)) + { + status = PhpCallWithTimeout(threadContext, Routine, Context, CallTimeout); + PhpReleaseCallWithTimeoutThread(threadContext); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +NTSTATUS PhpCommonQueryObjectRoutine( + _In_ PVOID Parameter + ) +{ + PPHP_QUERY_OBJECT_COMMON_CONTEXT context = Parameter; + + switch (context->Work) + { + case NtQueryObjectWork: + context->Status = NtQueryObject( + context->u.NtQueryObject.Handle, + context->u.NtQueryObject.ObjectInformationClass, + context->u.NtQueryObject.ObjectInformation, + context->u.NtQueryObject.ObjectInformationLength, + context->u.NtQueryObject.ReturnLength + ); + break; + case NtQuerySecurityObjectWork: + context->Status = NtQuerySecurityObject( + context->u.NtQuerySecurityObject.Handle, + context->u.NtQuerySecurityObject.SecurityInformation, + context->u.NtQuerySecurityObject.SecurityDescriptor, + context->u.NtQuerySecurityObject.Length, + context->u.NtQuerySecurityObject.LengthNeeded + ); + break; + case NtSetSecurityObjectWork: + context->Status = NtSetSecurityObject( + context->u.NtSetSecurityObject.Handle, + context->u.NtSetSecurityObject.SecurityInformation, + context->u.NtSetSecurityObject.SecurityDescriptor + ); + break; + case NtQueryFileInformationWork: + { + IO_STATUS_BLOCK isb; + + context->Status = NtQueryInformationFile( + context->u.NtQueryFileInformation.Handle, + &isb, + context->u.NtQueryFileInformation.FileInformation, + context->u.NtQueryFileInformation.FileInformationLength, + context->u.NtQueryFileInformation.FileInformationClass + ); + } + break; + default: + context->Status = STATUS_INVALID_PARAMETER; + break; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhpCommonQueryObjectWithTimeout( + _In_ PPHP_QUERY_OBJECT_COMMON_CONTEXT Context + ) +{ + NTSTATUS status; + LARGE_INTEGER timeout; + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(1, PH_TIMEOUT_SEC); + status = PhCallWithTimeout(PhpCommonQueryObjectRoutine, Context, NULL, &timeout); + + if (NT_SUCCESS(status)) + status = Context->Status; + + PhFree(Context); + + return status; +} + +NTSTATUS PhCallNtQueryObjectWithTimeout( + _In_ HANDLE Handle, + _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_writes_bytes_opt_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + PPHP_QUERY_OBJECT_COMMON_CONTEXT context; + + context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); + context->Work = NtQueryObjectWork; + context->Status = STATUS_UNSUCCESSFUL; + context->u.NtQueryObject.Handle = Handle; + context->u.NtQueryObject.ObjectInformationClass = ObjectInformationClass; + context->u.NtQueryObject.ObjectInformation = ObjectInformation; + context->u.NtQueryObject.ObjectInformationLength = ObjectInformationLength; + context->u.NtQueryObject.ReturnLength = ReturnLength; + + return PhpCommonQueryObjectWithTimeout(context); +} + +NTSTATUS PhCallNtQuerySecurityObjectWithTimeout( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_writes_bytes_opt_(Length) PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ ULONG Length, + _Out_ PULONG LengthNeeded + ) +{ + PPHP_QUERY_OBJECT_COMMON_CONTEXT context; + + context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); + context->Work = NtQuerySecurityObjectWork; + context->Status = STATUS_UNSUCCESSFUL; + context->u.NtQuerySecurityObject.Handle = Handle; + context->u.NtQuerySecurityObject.SecurityInformation = SecurityInformation; + context->u.NtQuerySecurityObject.SecurityDescriptor = SecurityDescriptor; + context->u.NtQuerySecurityObject.Length = Length; + context->u.NtQuerySecurityObject.LengthNeeded = LengthNeeded; + + return PhpCommonQueryObjectWithTimeout(context); +} + +NTSTATUS PhCallNtSetSecurityObjectWithTimeout( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + PPHP_QUERY_OBJECT_COMMON_CONTEXT context; + + context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); + context->Work = NtSetSecurityObjectWork; + context->Status = STATUS_UNSUCCESSFUL; + context->u.NtSetSecurityObject.Handle = Handle; + context->u.NtSetSecurityObject.SecurityInformation = SecurityInformation; + context->u.NtSetSecurityObject.SecurityDescriptor = SecurityDescriptor; + + return PhpCommonQueryObjectWithTimeout(context); +} + +NTSTATUS PhCallNtQueryFileInformationWithTimeout( + _In_ HANDLE Handle, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _Out_writes_bytes_opt_(FileInformationLength) PVOID FileInformation, + _In_ ULONG FileInformationLength + ) +{ + PPHP_QUERY_OBJECT_COMMON_CONTEXT context; + + context = PhAllocate(sizeof(PHP_QUERY_OBJECT_COMMON_CONTEXT)); + context->Work = NtQueryFileInformationWork; + context->Status = STATUS_UNSUCCESSFUL; + context->u.NtQueryFileInformation.Handle = Handle; + context->u.NtQueryFileInformation.FileInformationClass = FileInformationClass; + context->u.NtQueryFileInformation.FileInformation = FileInformation; + context->u.NtQueryFileInformation.FileInformationLength = FileInformationLength; + + return PhpCommonQueryObjectWithTimeout(context); +} diff --git a/phlib/icotobmp.c b/phlib/icotobmp.c index c969a01b5e19..048a6f17aec4 100644 --- a/phlib/icotobmp.c +++ b/phlib/icotobmp.c @@ -1,192 +1,192 @@ -#include -#include -#include - -// code from http://msdn.microsoft.com/en-us/library/bb757020.aspx - -static HBITMAP PhpCreateBitmap32( - _In_ HDC hdc, - _In_ ULONG Width, - _In_ ULONG Height, - _Outptr_opt_ PVOID *Bits - ) -{ - BITMAPINFO bitmapInfo; - - memset(&bitmapInfo, 0, sizeof(BITMAPINFO)); - bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmapInfo.bmiHeader.biPlanes = 1; - bitmapInfo.bmiHeader.biCompression = BI_RGB; - - bitmapInfo.bmiHeader.biWidth = Width; - bitmapInfo.bmiHeader.biHeight = Height; - bitmapInfo.bmiHeader.biBitCount = 32; - - return CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, Bits, NULL, 0); -} - -static BOOLEAN PhpHasAlpha( - _In_ PULONG Argb, - _In_ ULONG Width, - _In_ ULONG Height, - _In_ ULONG RowWidth - ) -{ - ULONG delta; - ULONG x; - ULONG y; - - delta = RowWidth - Width; - - for (y = Width; y; y--) - { - for (x = Height; x; x--) - { - if (*Argb++ & 0xff000000) - return TRUE; - } - - Argb += delta; - } - - return FALSE; -} - -static VOID PhpConvertToPArgb32( - _In_ HDC hdc, - _Inout_ PULONG Argb, - _In_ HBITMAP Bitmap, - _In_ ULONG Width, - _In_ ULONG Height, - _In_ ULONG RowWidth - ) -{ - BITMAPINFO bitmapInfo; - PVOID bits; - - memset(&bitmapInfo, 0, sizeof(BITMAPINFO)); - bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmapInfo.bmiHeader.biPlanes = 1; - bitmapInfo.bmiHeader.biCompression = BI_RGB; - - bitmapInfo.bmiHeader.biWidth = Width; - bitmapInfo.bmiHeader.biHeight = Height; - bitmapInfo.bmiHeader.biBitCount = 32; - - bits = PhAllocate(Width * sizeof(ULONG) * Height); - - if (GetDIBits(hdc, Bitmap, 0, Height, bits, &bitmapInfo, DIB_RGB_COLORS) == Height) - { - PULONG argbMask; - ULONG delta; - ULONG x; - ULONG y; - - argbMask = (PULONG)bits; - delta = RowWidth - Width; - - for (y = Height; y; y--) - { - for (x = Width; x; x--) - { - if (*argbMask++) - { - *Argb++ = 0; // transparent - } - else - { - *Argb++ |= 0xff000000; // opaque - } - } - - Argb += delta; - } - } - - PhFree(bits); -} - -static VOID PhpConvertToPArgb32IfNeeded( - _In_ HPAINTBUFFER PaintBuffer, - _In_ HDC hdc, - _In_ HICON Icon, - _In_ ULONG Width, - _In_ ULONG Height - ) -{ - RGBQUAD *quad; - ULONG rowWidth; - - if (SUCCEEDED(GetBufferedPaintBits(PaintBuffer, &quad, &rowWidth))) - { - PULONG argb = (PULONG)quad; - - if (!PhpHasAlpha(argb, Width, Height, rowWidth)) - { - ICONINFO iconInfo; - - if (GetIconInfo(Icon, &iconInfo)) - { - if (iconInfo.hbmMask) - { - PhpConvertToPArgb32(hdc, argb, iconInfo.hbmMask, Width, Height, rowWidth); - } - - DeleteObject(iconInfo.hbmColor); - DeleteObject(iconInfo.hbmMask); - } - } - } -} - -HBITMAP PhIconToBitmap( - _In_ HICON Icon, - _In_ ULONG Width, - _In_ ULONG Height - ) -{ - HBITMAP bitmap; - RECT iconRectangle; - HDC screenHdc; - HDC hdc; - HBITMAP oldBitmap; - BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - BP_PAINTPARAMS paintParams = { sizeof(paintParams) }; - HDC bufferHdc; - HPAINTBUFFER paintBuffer; - - iconRectangle.left = 0; - iconRectangle.top = 0; - iconRectangle.right = Width; - iconRectangle.bottom = Height; - - screenHdc = GetDC(NULL); - hdc = CreateCompatibleDC(screenHdc); - bitmap = PhpCreateBitmap32(screenHdc, Width, Height, NULL); - ReleaseDC(NULL, screenHdc); - oldBitmap = SelectObject(hdc, bitmap); - - paintParams.dwFlags = BPPF_ERASE; - paintParams.pBlendFunction = &blendFunction; - - if (paintBuffer = BeginBufferedPaint(hdc, &iconRectangle, BPBF_DIB, &paintParams, &bufferHdc)) - { - DrawIconEx(bufferHdc, 0, 0, Icon, Width, Height, 0, NULL, DI_NORMAL); - // If the icon did not have an alpha channel, we need to convert the buffer to PARGB. - PhpConvertToPArgb32IfNeeded(paintBuffer, hdc, Icon, Width, Height); - // This will write the buffer contents to the destination bitmap. - EndBufferedPaint(paintBuffer, TRUE); - } - else - { - // Default to unbuffered painting. - FillRect(hdc, &iconRectangle, (HBRUSH)(COLOR_WINDOW + 1)); - DrawIconEx(hdc, 0, 0, Icon, Width, Height, 0, NULL, DI_NORMAL); - SelectObject(hdc, oldBitmap); - } - - SelectObject(hdc, oldBitmap); - DeleteDC(hdc); - - return bitmap; -} +#include +#include +#include + +// code from http://msdn.microsoft.com/en-us/library/bb757020.aspx + +static HBITMAP PhpCreateBitmap32( + _In_ HDC hdc, + _In_ ULONG Width, + _In_ ULONG Height, + _Outptr_opt_ PVOID *Bits + ) +{ + BITMAPINFO bitmapInfo; + + memset(&bitmapInfo, 0, sizeof(BITMAPINFO)); + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + bitmapInfo.bmiHeader.biWidth = Width; + bitmapInfo.bmiHeader.biHeight = Height; + bitmapInfo.bmiHeader.biBitCount = 32; + + return CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, Bits, NULL, 0); +} + +static BOOLEAN PhpHasAlpha( + _In_ PULONG Argb, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG RowWidth + ) +{ + ULONG delta; + ULONG x; + ULONG y; + + delta = RowWidth - Width; + + for (y = Width; y; y--) + { + for (x = Height; x; x--) + { + if (*Argb++ & 0xff000000) + return TRUE; + } + + Argb += delta; + } + + return FALSE; +} + +static VOID PhpConvertToPArgb32( + _In_ HDC hdc, + _Inout_ PULONG Argb, + _In_ HBITMAP Bitmap, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG RowWidth + ) +{ + BITMAPINFO bitmapInfo; + PVOID bits; + + memset(&bitmapInfo, 0, sizeof(BITMAPINFO)); + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + bitmapInfo.bmiHeader.biWidth = Width; + bitmapInfo.bmiHeader.biHeight = Height; + bitmapInfo.bmiHeader.biBitCount = 32; + + bits = PhAllocate(Width * sizeof(ULONG) * Height); + + if (GetDIBits(hdc, Bitmap, 0, Height, bits, &bitmapInfo, DIB_RGB_COLORS) == Height) + { + PULONG argbMask; + ULONG delta; + ULONG x; + ULONG y; + + argbMask = (PULONG)bits; + delta = RowWidth - Width; + + for (y = Height; y; y--) + { + for (x = Width; x; x--) + { + if (*argbMask++) + { + *Argb++ = 0; // transparent + } + else + { + *Argb++ |= 0xff000000; // opaque + } + } + + Argb += delta; + } + } + + PhFree(bits); +} + +static VOID PhpConvertToPArgb32IfNeeded( + _In_ HPAINTBUFFER PaintBuffer, + _In_ HDC hdc, + _In_ HICON Icon, + _In_ ULONG Width, + _In_ ULONG Height + ) +{ + RGBQUAD *quad; + ULONG rowWidth; + + if (SUCCEEDED(GetBufferedPaintBits(PaintBuffer, &quad, &rowWidth))) + { + PULONG argb = (PULONG)quad; + + if (!PhpHasAlpha(argb, Width, Height, rowWidth)) + { + ICONINFO iconInfo; + + if (GetIconInfo(Icon, &iconInfo)) + { + if (iconInfo.hbmMask) + { + PhpConvertToPArgb32(hdc, argb, iconInfo.hbmMask, Width, Height, rowWidth); + } + + DeleteBitmap(iconInfo.hbmColor); + DeleteBitmap(iconInfo.hbmMask); + } + } + } +} + +HBITMAP PhIconToBitmap( + _In_ HICON Icon, + _In_ ULONG Width, + _In_ ULONG Height + ) +{ + HBITMAP bitmap; + RECT iconRectangle; + HDC screenHdc; + HDC hdc; + HBITMAP oldBitmap; + BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + BP_PAINTPARAMS paintParams = { sizeof(paintParams) }; + HDC bufferHdc; + HPAINTBUFFER paintBuffer; + + iconRectangle.left = 0; + iconRectangle.top = 0; + iconRectangle.right = Width; + iconRectangle.bottom = Height; + + screenHdc = GetDC(NULL); + hdc = CreateCompatibleDC(screenHdc); + bitmap = PhpCreateBitmap32(screenHdc, Width, Height, NULL); + ReleaseDC(NULL, screenHdc); + oldBitmap = SelectBitmap(hdc, bitmap); + + paintParams.dwFlags = BPPF_ERASE; + paintParams.pBlendFunction = &blendFunction; + + if (paintBuffer = BeginBufferedPaint(hdc, &iconRectangle, BPBF_DIB, &paintParams, &bufferHdc)) + { + DrawIconEx(bufferHdc, 0, 0, Icon, Width, Height, 0, NULL, DI_NORMAL); + // If the icon did not have an alpha channel, we need to convert the buffer to PARGB. + PhpConvertToPArgb32IfNeeded(paintBuffer, hdc, Icon, Width, Height); + // This will write the buffer contents to the destination bitmap. + EndBufferedPaint(paintBuffer, TRUE); + } + else + { + // Default to unbuffered painting. + FillRect(hdc, &iconRectangle, (HBRUSH)(COLOR_WINDOW + 1)); + DrawIconEx(hdc, 0, 0, Icon, Width, Height, 0, NULL, DI_NORMAL); + //SelectBitmap(hdc, oldBitmap); // ?? (dmex) + } + + SelectBitmap(hdc, oldBitmap); + DeleteDC(hdc); + + return bitmap; +} diff --git a/phlib/include/apiimport.h b/phlib/include/apiimport.h index 8a046e762054..78b0e7f694fa 100644 --- a/phlib/include/apiimport.h +++ b/phlib/include/apiimport.h @@ -1,86 +1,114 @@ -#ifndef _PH_APIIMPORT_H -#define _PH_APIIMPORT_H - -// comctl32 - -typedef HRESULT (WINAPI *_TaskDialogIndirect)( - _In_ const struct _TASKDIALOGCONFIG *pTaskConfig, - _In_ int *pnButton, - _In_ int *pnRadioButton, - _In_ BOOL *pfVerificationFlagChecked - ); - -// ntdll - -typedef NTSTATUS (NTAPI *_NtQueryInformationEnlistment)( - _In_ HANDLE EnlistmentHandle, - _In_ ENLISTMENT_INFORMATION_CLASS EnlistmentInformationClass, - _Out_writes_bytes_(EnlistmentInformationLength) PVOID EnlistmentInformation, - _In_ ULONG EnlistmentInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -typedef NTSTATUS (NTAPI *_NtQueryInformationResourceManager)( - _In_ HANDLE ResourceManagerHandle, - _In_ RESOURCEMANAGER_INFORMATION_CLASS ResourceManagerInformationClass, - _Out_writes_bytes_(ResourceManagerInformationLength) PVOID ResourceManagerInformation, - _In_ ULONG ResourceManagerInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -typedef NTSTATUS (NTAPI *_NtQueryInformationTransaction)( - _In_ HANDLE TransactionHandle, - _In_ TRANSACTION_INFORMATION_CLASS TransactionInformationClass, - _Out_writes_bytes_(TransactionInformationLength) PVOID TransactionInformation, - _In_ ULONG TransactionInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -typedef NTSTATUS (NTAPI *_NtQueryInformationTransactionManager)( - _In_ HANDLE TransactionManagerHandle, - _In_ TRANSACTIONMANAGER_INFORMATION_CLASS TransactionManagerInformationClass, - _Out_writes_bytes_(TransactionManagerInformationLength) PVOID TransactionManagerInformation, - _In_ ULONG TransactionManagerInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -// shell32 - -#if defined(_M_IX86) -#define __unaligned -#endif - -typedef HRESULT (WINAPI *_SHCreateShellItem)( - _In_opt_ const struct _ITEMIDLIST __unaligned *pidlParent, - _In_opt_ struct IShellFolder *psfParent, - _In_ const struct _ITEMIDLIST __unaligned *pidl, - _Out_ struct IShellItem **ppsi - ); - -typedef HRESULT (WINAPI *_SHOpenFolderAndSelectItems)( - _In_ const struct _ITEMIDLIST __unaligned *pidlFolder, - _In_ UINT cidl, - _In_reads_opt_(cidl) const struct _ITEMIDLIST __unaligned **apidl, - _In_ DWORD dwFlags - ); - -typedef HRESULT (WINAPI *_SHParseDisplayName)( - _In_ LPCWSTR pszName, - _In_opt_ struct IBindCtx *pbc, - _Out_ const struct _ITEMIDLIST __unaligned **ppidl, - _In_ ULONG sfgaoIn, - _Out_ ULONG *psfgaoOut - ); - -#define PH_DECLARE_IMPORT(Name) _##Name Name##_Import(VOID) - -PH_DECLARE_IMPORT(TaskDialogIndirect); -PH_DECLARE_IMPORT(NtQueryInformationEnlistment); -PH_DECLARE_IMPORT(NtQueryInformationResourceManager); -PH_DECLARE_IMPORT(NtQueryInformationTransaction); -PH_DECLARE_IMPORT(NtQueryInformationTransactionManager); -PH_DECLARE_IMPORT(SHCreateShellItem); -PH_DECLARE_IMPORT(SHOpenFolderAndSelectItems); -PH_DECLARE_IMPORT(SHParseDisplayName); - -#endif +#ifndef _PH_APIIMPORT_H +#define _PH_APIIMPORT_H + +// ntdll + +typedef NTSTATUS (NTAPI *_NtQueryInformationEnlistment)( + _In_ HANDLE EnlistmentHandle, + _In_ ENLISTMENT_INFORMATION_CLASS EnlistmentInformationClass, + _Out_writes_bytes_(EnlistmentInformationLength) PVOID EnlistmentInformation, + _In_ ULONG EnlistmentInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +typedef NTSTATUS (NTAPI *_NtQueryInformationResourceManager)( + _In_ HANDLE ResourceManagerHandle, + _In_ RESOURCEMANAGER_INFORMATION_CLASS ResourceManagerInformationClass, + _Out_writes_bytes_(ResourceManagerInformationLength) PVOID ResourceManagerInformation, + _In_ ULONG ResourceManagerInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +typedef NTSTATUS (NTAPI *_NtQueryInformationTransaction)( + _In_ HANDLE TransactionHandle, + _In_ TRANSACTION_INFORMATION_CLASS TransactionInformationClass, + _Out_writes_bytes_(TransactionInformationLength) PVOID TransactionInformation, + _In_ ULONG TransactionInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +typedef NTSTATUS (NTAPI *_NtQueryInformationTransactionManager)( + _In_ HANDLE TransactionManagerHandle, + _In_ TRANSACTIONMANAGER_INFORMATION_CLASS TransactionManagerInformationClass, + _Out_writes_bytes_(TransactionManagerInformationLength) PVOID TransactionManagerInformation, + _In_ ULONG TransactionManagerInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +typedef NTSTATUS (NTAPI *_NtQueryDefaultLocale)( + _In_ BOOLEAN UserProfile, + _Out_ PLCID DefaultLocaleId + ); + +typedef NTSTATUS (NTAPI *_NtQueryDefaultUILanguage)( + _Out_ LANGID* DefaultUILanguageId + ); + +typedef NTSTATUS (NTAPI* _RtlGetTokenNamedObjectPath)( + _In_ HANDLE Token, + _In_opt_ PSID Sid, + _Out_ PUNICODE_STRING ObjectPath + ); + +typedef NTSTATUS (NTAPI* _RtlGetAppContainerNamedObjectPath)( + _In_opt_ HANDLE Token, + _In_opt_ PSID AppContainerSid, + _In_ BOOLEAN RelativePath, + _Out_ PUNICODE_STRING ObjectPath + ); + +typedef NTSTATUS (NTAPI* _RtlGetAppContainerSidType)( + _In_ PSID AppContainerSid, + _Out_ PAPPCONTAINER_SID_TYPE AppContainerSidType + ); + +typedef NTSTATUS (NTAPI* _RtlGetAppContainerParent)( + _In_ PSID AppContainerSid, + _Out_ PSID* AppContainerSidParent + ); + +typedef NTSTATUS (NTAPI* _RtlDeriveCapabilitySidsFromName)( + _Inout_ PUNICODE_STRING UnicodeString, + _Out_ PSID CapabilityGroupSid, + _Out_ PSID CapabilitySid + ); + +typedef HRESULT (WINAPI* _GetAppContainerRegistryLocation)( + _In_ REGSAM desiredAccess, + _Outptr_ PHKEY phAppContainerKey + ); + +typedef HRESULT (WINAPI* _GetAppContainerFolderPath)( + _In_ PCWSTR pszAppContainerSid, + _Outptr_ PWSTR* ppszPath + ); + +typedef BOOL (WINAPI* _ConvertSecurityDescriptorToStringSecurityDescriptorW)( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ DWORD RequestedStringSDRevision, + _In_ SECURITY_INFORMATION SecurityInformation, + _Outptr_ LPWSTR* StringSecurityDescriptor, + _Out_opt_ PULONG StringSecurityDescriptorLen + ); + +#define PH_DECLARE_IMPORT(Name) _##Name Name##_Import(VOID) + +PH_DECLARE_IMPORT(NtQueryInformationEnlistment); +PH_DECLARE_IMPORT(NtQueryInformationResourceManager); +PH_DECLARE_IMPORT(NtQueryInformationTransaction); +PH_DECLARE_IMPORT(NtQueryInformationTransactionManager); +PH_DECLARE_IMPORT(NtQueryDefaultLocale); +PH_DECLARE_IMPORT(NtQueryDefaultUILanguage); + +PH_DECLARE_IMPORT(RtlGetTokenNamedObjectPath); +PH_DECLARE_IMPORT(RtlGetAppContainerNamedObjectPath); +PH_DECLARE_IMPORT(RtlGetAppContainerSidType); +PH_DECLARE_IMPORT(RtlGetAppContainerParent); +PH_DECLARE_IMPORT(RtlDeriveCapabilitySidsFromName); + +PH_DECLARE_IMPORT(GetAppContainerRegistryLocation); +PH_DECLARE_IMPORT(GetAppContainerFolderPath); + +PH_DECLARE_IMPORT(ConvertSecurityDescriptorToStringSecurityDescriptorW); + +#endif diff --git a/phlib/include/appresolver.h b/phlib/include/appresolver.h new file mode 100644 index 000000000000..073342d28a2c --- /dev/null +++ b/phlib/include/appresolver.h @@ -0,0 +1,89 @@ +/* + * Process Hacker - + * Appmodel support functions + * + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PH_APPRESOLVER_H +#define _PH_APPRESOLVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +BOOLEAN PhAppResolverGetAppIdForProcess( + _In_ HANDLE ProcessId, + _Out_ PPH_STRING *ApplicationUserModelId + ); + +BOOLEAN PhAppResolverGetAppIdForWindow( + _In_ HWND WindowHandle, + _Out_ PPH_STRING *ApplicationUserModelId + ); + +HRESULT PhAppResolverActivateAppId( + _In_ PPH_STRING AppUserModelId, + _In_opt_ PWSTR CommandLine, + _Out_opt_ HANDLE *ProcessId + ); + +typedef struct _PH_PACKAGE_TASK_ENTRY +{ + PPH_STRING TaskName; + GUID TaskGuid; +} PH_PACKAGE_TASK_ENTRY, *PPH_PACKAGE_TASK_ENTRY; + +PPH_LIST PhAppResolverEnumeratePackageBackgroundTasks( + _In_ PPH_STRING PackageFullName + ); + +PPH_STRING PhGetAppContainerName( + _In_ PSID AppContainerSid + ); + +PPH_STRING PhGetAppContainerSidFromName( + _In_ PWSTR AppContainerName + ); + +PPH_STRING PhGetAppContainerPackageName( + _In_ PSID Sid + ); + +PPH_STRING PhGetProcessPackageFullName( + _In_ HANDLE ProcessHandle + ); + +BOOLEAN PhIsPackageCapabilitySid( + _In_ PSID AppContainerSid, + _In_ PSID Sid + ); + +PPH_STRING PhGetPackagePath( + _In_ PPH_STRING PackageFullName + ); + +PPH_LIST PhGetPackageAssetsFromResourceFile( + _In_ PWSTR FilePath + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/appresolverp.h b/phlib/include/appresolverp.h new file mode 100644 index 000000000000..4279f69a230b --- /dev/null +++ b/phlib/include/appresolverp.h @@ -0,0 +1,585 @@ +/* + * Process Hacker - + * Appmodel support functions + * + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PH_APPRESOLVER_P_H +#define _PH_APPRESOLVER_P_H + +// "660B90C8-73A9-4B58-8CAE-355B7F55341B" +static CLSID CLSID_StartMenuCacheAndAppResolver_I = { 0x660B90C8, 0x73A9, 0x4B58,{ 0x8C, 0xAE, 0x35, 0x5B, 0x7F, 0x55, 0x34, 0x1B } }; +// "46A6EEFF-908E-4DC6-92A6-64BE9177B41C" +static IID IID_IApplicationResolver61_I = { 0x46A6EEFF, 0x908E, 0x4DC6,{ 0x92, 0xA6, 0x64, 0xBE, 0x91, 0x77, 0xB4, 0x1c } }; +// "DE25675A-72DE-44b4-9373-05170450C140" +static IID IID_IApplicationResolver62_I = { 0xDE25675A, 0x72DE, 0x44b4,{ 0x93, 0x73, 0x05, 0x17, 0x04, 0x50, 0xC1, 0x40 } }; +// "33F71155-C2E9-4FFE-9786-A32D98577CFF" +static IID IID_IStartMenuAppItems61_I = { 0x33F71155, 0xC2E9, 0x4FFE,{ 0x97, 0x86, 0xA3, 0x2D, 0x98, 0x57, 0x7C, 0xFF } }; +// "02C5CCF3-805F-4654-A7B7-340A74335365" +static IID IID_IStartMenuAppItems62_I = { 0x02C5CCF3, 0x805F, 0x4654,{ 0xA7, 0xB7, 0x34, 0x0A, 0x74, 0x33, 0x53, 0x65 } }; + +// "DBCE7E40-7345-439D-B12C-114A11819A09" +static CLSID CLSID_MrtResourceManager_I = { 0xDBCE7E40, 0x7345, 0x439D,{ 0xB1, 0x2C, 0x11, 0x4A, 0x11, 0x81, 0x9A, 0x09 } }; +// "130A2F65-2BE7-4309-9A58-A9052FF2B61C" +static IID IID_IMrtResourceManager_I = { 0x130A2F65, 0x2BE7, 0x4309,{ 0x9A, 0x58, 0xA9, 0x05, 0x2F, 0xF2, 0xB6, 0x1C } }; +// "E3C22B30-8502-4B2F-9133-559674587E51" +static IID IID_IResourceContext_I = { 0xE3C22B30, 0x8502, 0x4B2F,{ 0x91, 0x33, 0x55, 0x96, 0x74, 0x58, 0x7E, 0x51 } }; +// "6E21E72B-B9B0-42AE-A686-983CF784EDCD" +static IID IID_IResourceMap_I = { 0x6E21E72B, 0xB9B0, 0x42AE,{ 0xA6, 0x86, 0x98, 0x3C, 0xF7, 0x84, 0xED, 0xCD } }; + +static HRESULT (WINAPI* AppContainerDeriveSidFromMoniker_I)( // DeriveAppContainerSidFromAppContainerName + _In_ PCWSTR AppContainerName, + _Out_ PSID *AppContainerSid + ) = NULL; + +// Note: LookupAppContainerDisplayName (userenv.dll, ordinal 211) has the same prototype but returns 'PackageName/ContainerName'. +static HRESULT (WINAPI* AppContainerLookupMoniker_I)( + _In_ PSID AppContainerSid, + _Out_ PWSTR *PackageFamilyName + ) = NULL; + +static HRESULT (WINAPI* AppContainerRegisterSid_I)( + _In_ PSID Sid, + _In_ PCWSTR AppContainerName, + _In_ PCWSTR DisplayName + ) = NULL; + +static HRESULT (WINAPI* AppContainerUnregisterSid_I)( + _In_ PSID Sid + ) = NULL; + +static BOOL (WINAPI* AppContainerFreeMemory_I)( + _Frees_ptr_opt_ PVOID Memory + ) = NULL; + +static HRESULT (WINAPI* AppPolicyGetWindowingModel_I)( + _In_ HANDLE ProcessTokenHandle, + _Out_ AppPolicyWindowingModel *ProcessWindowingModelPolicy + ) = NULL; + +// rev +static NTSTATUS (NTAPI* PsmGetKeyFromProcess_I)( + _In_ HANDLE ProcessHandle, + _Out_ PVOID KeyBuffer, + _Inout_ PULONG KeyLength + ) = NULL; + +// rev +static NTSTATUS (NTAPI* PsmGetKeyFromToken_I)( + _In_ HANDLE TokenHandle, + _Out_ PVOID KeyBuffer, + _Inout_ PULONG KeyLength + ) = NULL; + +// rev +static NTSTATUS (NTAPI* PsmGetApplicationNameFromKey_I)( + _In_ PVOID KeyBuffer, + _Out_ PVOID NameBuffer, + _Inout_ PULONG NameLength + ) = NULL; + +// rev +static NTSTATUS (NTAPI* PsmGetPackageFullNameFromKey_I)( + _In_ PVOID KeyBuffer, + _Out_ PVOID NameBuffer, + _Inout_ PULONG NameLength + ) = NULL; + +typedef enum _START_MENU_APP_ITEMS_FLAGS +{ + SMAIF_DEFAULT = 0, + SMAIF_EXTENDED = 1, + SMAIF_USAGEINFO = 2 +} START_MENU_APP_ITEMS_FLAGS; + +#undef INTERFACE +#define INTERFACE IApplicationResolver61 +DECLARE_INTERFACE_IID(IApplicationResolver61, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IApplicationResolver61 + STDMETHOD(GetAppIDForShortcut)(THIS, + _In_ IShellItem *psi, + _Outptr_ PWSTR *ppszAppID + ) PURE; + STDMETHOD(GetAppIDForWindow)(THIS, + _In_ HWND hwnd, + _Outptr_ PWSTR *ppszAppID, + _Out_opt_ BOOL *pfPinningPrevented, + _Out_opt_ BOOL *pfExplicitAppID, + _Out_opt_ BOOL *pfEmbeddedShortcutValid + ) PURE; + STDMETHOD(GetAppIDForProcess)(THIS, + _In_ ULONG dwProcessID, + _Outptr_ PWSTR *ppszAppID, + _Out_opt_ BOOL *pfPinningPrevented, + _Out_opt_ BOOL *pfExplicitAppID, + _Out_opt_ BOOL *pfEmbeddedShortcutValid + ) PURE; + STDMETHOD(GetShortcutForProcess)(THIS, + _In_ ULONG dwProcessID, + _Outptr_ IShellItem **ppsi + ) PURE; + STDMETHOD(GetBestShortcutForAppID)(THIS, + _In_ PCWSTR pszAppID, + _Outptr_ IShellItem **ppsi + ) PURE; + STDMETHOD(GetBestShortcutAndAppIDForAppPath)(THIS, + _In_ PCWSTR pszAppPath, + _Outptr_opt_ IShellItem **ppsi, + _Outptr_opt_ PWSTR *ppszAppID + ) PURE; + STDMETHOD(CanPinApp)(THIS, + _In_ IShellItem *psi + ) PURE; + STDMETHOD(GetRelaunchProperties)(THIS, + _In_ HWND hwnd, + _Outptr_opt_result_maybenull_ PWSTR *ppszAppID, + _Outptr_opt_result_maybenull_ PWSTR *ppszCmdLine, + _Outptr_opt_result_maybenull_ PWSTR *ppszIconResource, + _Outptr_opt_result_maybenull_ PWSTR *ppszDisplayNameResource, + _Out_opt_ BOOL *pfPinnable + ) PURE; + STDMETHOD(GenerateShortcutFromWindowProperties)(THIS, + _In_ HWND hwnd, + _Outptr_ IShellItem **ppsi + ) PURE; + STDMETHOD(GenerateShortcutFromItemProperties)(THIS, + _In_ IShellItem2 *psi2, + _Out_opt_ IShellItem **ppsi + ) PURE; + + END_INTERFACE +}; + +#define IApplicationResolver_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IApplicationResolver_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) +#define IApplicationResolver_Release(This) \ + ((This)->lpVtbl->Release(This)) +#define IApplicationResolver_GetAppIDForShortcut(This, psi, ppszAppID) \ + ((This)->lpVtbl->GetAppIDForShortcut(This, psi, ppszAppID)) +#define IApplicationResolver_GetAppIDForWindow(This, hwnd, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid) \ + ((This)->lpVtbl->GetAppIDForWindow(This, hwnd, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid)) +#define IApplicationResolver_GetAppIDForProcess(This, dwProcessID, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid) \ + ((This)->lpVtbl->GetAppIDForProcess(This, dwProcessID, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid)) +#define IApplicationResolver_GetShortcutForProcess(This, dwProcessID, ppsi) \ + ((This)->lpVtbl->GetShortcutForProcess(This, dwProcessID, ppsi)) +#define IApplicationResolver_GetBestShortcutForAppID(This, pszAppID, ppsi) \ + ((This)->lpVtbl->GetBestShortcutForAppID(This, pszAppID, ppsi)) +#define IApplicationResolver_GetBestShortcutAndAppIDForAppPath(This, pszAppPath, ppsi, ppszAppID) \ + ((This)->lpVtbl->GetBestShortcutAndAppIDForAppPath(This, pszAppPath, ppsi, ppszAppID)) +#define IApplicationResolver_CanPinApp(This, psi) \ + ((This)->lpVtbl->CanPinApp(This, psi)) +#define IApplicationResolver_GetRelaunchProperties(This, hwnd, ppszAppID, ppszCmdLine, ppszIconResource, ppszDisplayNameResource, pfPinnable) \ + ((This)->lpVtbl->GetRelaunchProperties(This, hwnd, ppszAppID, ppszCmdLine, ppszIconResource, ppszDisplayNameResource, pfPinnable)) +#define IApplicationResolver_GenerateShortcutFromWindowProperties(This, ppsi) \ + ((This)->lpVtbl->GenerateShortcutFromWindowProperties(This, ppsi)) +#define IApplicationResolver_GenerateShortcutFromItemProperties(This, psi2, ppsi) \ + ((This)->lpVtbl->GenerateShortcutFromItemProperties(This, psi2, ppsi)) + +#undef INTERFACE +#define INTERFACE IApplicationResolver62 +DECLARE_INTERFACE_IID(IApplicationResolver62, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IApplicationResolver62 + STDMETHOD(GetAppIDForShortcut)(THIS, + _In_ IShellItem *psi, + _Outptr_ PWSTR *ppszAppID + ) PURE; + STDMETHOD(GetAppIDForShortcutObject)(THIS, + _In_ IShellLinkW *psl, + _In_ IShellItem *psi, + _Outptr_ PWSTR *ppszAppID + ) PURE; + STDMETHOD(GetAppIDForWindow)(THIS, + _In_ HWND hwnd, + _Outptr_ PWSTR *ppszAppID, + _Out_opt_ BOOL *pfPinningPrevented, + _Out_opt_ BOOL *pfExplicitAppID, + _Out_opt_ BOOL *pfEmbeddedShortcutValid + ) PURE; + STDMETHOD(GetAppIDForProcess)(THIS, + _In_ ULONG dwProcessID, + _Outptr_ PWSTR *ppszAppID, + _Out_opt_ BOOL *pfPinningPrevented, + _Out_opt_ BOOL *pfExplicitAppID, + _Out_opt_ BOOL *pfEmbeddedShortcutValid + ) PURE; + STDMETHOD(GetShortcutForProcess)(THIS, + _In_ ULONG dwProcessID, + _Outptr_ IShellItem **ppsi + ) PURE; + STDMETHOD(GetBestShortcutForAppID)(THIS, + _In_ PCWSTR pszAppID, + _Outptr_ IShellItem **ppsi + ) PURE; + STDMETHOD(GetBestShortcutAndAppIDForAppPath)(THIS, + _In_ PCWSTR pszAppPath, + _Outptr_opt_ IShellItem **ppsi, + _Outptr_opt_ PWSTR *ppszAppID + ) PURE; + STDMETHOD(CanPinApp)(THIS, + _In_ IShellItem *psi + ) PURE; + STDMETHOD(CanPinAppShortcut)(THIS, + _In_ IShellLinkW *psl, + _In_ IShellItem *psi + ) PURE; + STDMETHOD(GetRelaunchProperties)(THIS, + _In_ HWND hwnd, + _Outptr_opt_result_maybenull_ PWSTR *ppszAppID, + _Outptr_opt_result_maybenull_ PWSTR *ppszCmdLine, + _Outptr_opt_result_maybenull_ PWSTR *ppszIconResource, + _Outptr_opt_result_maybenull_ PWSTR *ppszDisplayNameResource, + _Out_opt_ BOOL *pfPinnable + ) PURE; + STDMETHOD(GenerateShortcutFromWindowProperties)(THIS, + _In_ HWND hwnd, + _Outptr_ IShellItem **ppsi + ) PURE; + STDMETHOD(GenerateShortcutFromItemProperties)(THIS, + _In_ IShellItem2 *psi2, + _Out_opt_ IShellItem **ppsi + ) PURE; + + END_INTERFACE +}; + +#define IApplicationResolver2_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IApplicationResolver2_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) +#define IApplicationResolver2_Release(This) \ + ((This)->lpVtbl->Release(This)) +#define IApplicationResolver2_GetAppIDForShortcut(This, psl, psi, ppszAppID) \ + ((This)->lpVtbl->GetAppIDForShortcut(This, psl, psi, ppszAppID)) +#define IApplicationResolver2_GetAppIDForShortcutObject(This, psl, ppszAppID) \ + ((This)->lpVtbl->GetAppIDForShortcutObject(This, psl, ppszAppID)) +#define IApplicationResolver2_GetAppIDForWindow(This, hwnd, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid) \ + ((This)->lpVtbl->GetAppIDForWindow(This, hwnd, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid)) +#define IApplicationResolver2_GetAppIDForProcess(This, dwProcessID, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid) \ + ((This)->lpVtbl->GetAppIDForProcess(This, dwProcessID, ppszAppID, pfPinningPrevented, pfExplicitAppID, pfEmbeddedShortcutValid)) +#define IApplicationResolver2_GetShortcutForProcess(This, dwProcessID, ppsi) \ + ((This)->lpVtbl->GetShortcutForProcess(This, dwProcessID, ppsi)) +#define IApplicationResolver2_GetBestShortcutForAppID(This, pszAppID, ppsi) \ + ((This)->lpVtbl->GetBestShortcutForAppID(This, pszAppID, ppsi)) +#define IApplicationResolver2_GetBestShortcutAndAppIDForAppPath(This, pszAppPath, ppsi, ppszAppID) \ + ((This)->lpVtbl->GetBestShortcutAndAppIDForAppPath(This, pszAppPath, ppsi, ppszAppID)) +#define IApplicationResolver2_CanPinApp(This, psi) \ + ((This)->lpVtbl->CanPinApp(This, psi)) +#define IApplicationResolver2_CanPinAppShortcut(This, psl, psi) \ + ((This)->lpVtbl->CanPinAppShortcut(This, psl, psi)) +#define IApplicationResolver2_GetRelaunchProperties(This, hwnd, ppszAppID, ppszCmdLine, ppszIconResource, ppszDisplayNameResource, pfPinnable) \ + ((This)->lpVtbl->GetRelaunchProperties(This, hwnd, ppszAppID, ppszCmdLine, ppszIconResource, ppszDisplayNameResource, pfPinnable)) +#define IApplicationResolver2_GenerateShortcutFromWindowProperties(This, ppsi) \ + ((This)->lpVtbl->GenerateShortcutFromWindowProperties(This, ppsi)) +#define IApplicationResolver2_GenerateShortcutFromItemProperties(This, psi2, ppsi) \ + ((This)->lpVtbl->GenerateShortcutFromItemProperties(This, psi2, ppsi)) + +#undef INTERFACE +#define INTERFACE IStartMenuAppItems61 +DECLARE_INTERFACE_IID(IStartMenuAppItems61, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IStartMenuAppItems61 + STDMETHOD(EnumItems)(THIS, + _In_ ULONG Flags, + _In_ REFIID riid, + _Outptr_ IEnumObjects **ppvObject + ) PURE; + STDMETHOD(GetItem)(THIS, + _In_ ULONG Flags, + _In_ PWSTR AppUserModelId, + _In_ REFIID riid, + _Outptr_ PVOID *ppvObject // ppvObject == IPropertyStore, IStartMenuAppItems61 + ) PURE; + + END_INTERFACE +}; + +#define IStartMenuAppItems_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IStartMenuAppItems_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) +#define IStartMenuAppItems_Release(This) \ + ((This)->lpVtbl->Release(This)) +#define IStartMenuAppItems_EnumItems(This, Flags, riid, ppvObject) \ + ((This)->lpVtbl->EnumItems(This, Flags, riid, ppvObject)) +#define IStartMenuAppItems_GetItem(This, Flags, AppUserModelId, riid, ppvObject) \ + ((This)->lpVtbl->GetItem(This, Flags, AppUserModelId, riid, ppvObject)) + +#undef INTERFACE +#define INTERFACE IStartMenuAppItems62 +DECLARE_INTERFACE_IID(IStartMenuAppItems62, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IStartMenuAppItems62 + STDMETHOD(EnumItems)(THIS, + _In_ ULONG Flags, + _In_ REFIID riid, + _Outptr_ IEnumObjects **ppvObject + ) PURE; + STDMETHOD(GetItem)(THIS, + _In_ ULONG Flags, + _In_ PWSTR AppUserModelId, + _In_ REFIID riid, + _Outptr_ PVOID *ppvObject // ppvObject == IPropertyStore, IStartMenuAppItems61 + ) PURE; + // STDMETHOD(Unknown)(THIS) + // STDMETHOD(Unknown)(THIS) + + END_INTERFACE +}; + +#define IStartMenuAppItems2_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IStartMenuAppItems2_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) +#define IStartMenuAppItems2_Release(This) \ + ((This)->lpVtbl->Release(This)) +#define IStartMenuAppItems2_EnumItems(This, Flags, riid, ppvObject) \ + ((This)->lpVtbl->EnumItems(This, Flags, riid, ppvObject)) +#define IStartMenuAppItems2_GetItem(This, Flags, AppUserModelId, riid, ppvObject) \ + ((This)->lpVtbl->GetItem(This, Flags, AppUserModelId, riid, ppvObject)) + +#undef INTERFACE +#define INTERFACE IMrtResourceManager +DECLARE_INTERFACE_IID(IMrtResourceManager, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IMrtResourceManager + STDMETHOD(Initialize)(THIS) PURE; + STDMETHOD(InitializeForCurrentApplication)(THIS) PURE; + STDMETHOD(InitializeForPackage)(THIS, PCWSTR PackageName) PURE; + STDMETHOD(InitializeForFile)(THIS, PCWSTR) PURE; + STDMETHOD(GetMainResourceMap)(THIS, REFIID riid, PVOID *ppvObject) PURE; // IResourceMap + STDMETHOD(GetResourceMap)(THIS, PCWSTR, REFIID riid, PVOID *ppvObject) PURE; // IResourceMap + STDMETHOD(GetDefaultContext)(THIS, REFIID riid, PVOID *ppvObject) PURE; // IResourceContext + STDMETHOD(GetReference)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD(IsResourceReference)(THIS, PCWSTR, BOOL *IsReference) PURE; + + END_INTERFACE +}; + +#define IMrtResourceManager_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IMrtResourceManager_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) +#define IMrtResourceManager_Release(This) \ + ((This)->lpVtbl->Release(This)) +#define IMrtResourceManager_Initialize(This) \ + ((This)->lpVtbl->Initialize(This)) +#define IMrtResourceManager_InitializeForCurrentApplication(This) \ + ((This)->lpVtbl->InitializeForCurrentApplication(This)) +#define IMrtResourceManager_InitializeForPackage(This, PackageName) \ + ((This)->lpVtbl->InitializeForPackage(This, PackageName)) +#define IMrtResourceManager_InitializeForFile(This, FilePath) \ + ((This)->lpVtbl->InitializeForFile(This, FilePath)) +#define IMrtResourceManager_GetMainResourceMap(This, riid, ppvObject) \ + ((This)->lpVtbl->GetMainResourceMap(This, riid, ppvObject)) +#define IMrtResourceManager_GetResourceMap(This, Name, riid, ppvObject) \ + ((This)->lpVtbl->GetResourceMap(This, Name, riid, ppvObject)) +#define IMrtResourceManager_GetDefaultContext(This, riid, ppvObject) \ + ((This)->lpVtbl->GetDefaultContext(This, riid, ppvObject)) +#define IMrtResourceManager_GetReference(This, riid, ppvObject) \ + ((This)->lpVtbl->GetReference(This, riid, ppvObject)) +#define IMrtResourceManager_IsResourceReference(This, Name, IsReference) \ + ((This)->lpVtbl->IsResourceReference(This, Name, IsReference)) + +#undef INTERFACE +#define INTERFACE IResourceContext +DECLARE_INTERFACE_IID(IResourceContext, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IResourceContext + STDMETHOD(GetLanguage)(THIS, PWSTR*) PURE; + STDMETHOD(GetHomeRegion)(THIS, PCWSTR*) PURE; + STDMETHOD(GetLayoutDirection)(THIS, ULONG*) PURE; // RESOURCE_LAYOUT_DIRECTION + STDMETHOD(GetTargetSize)(THIS, USHORT*) PURE; + STDMETHOD(GetScale)(THIS, ULONG*) PURE; // RESOURCE_SCALE + STDMETHOD(GetContrast)(THIS, ULONG*) PURE; // RESOURCE_CONTRAST + STDMETHOD(GetAlternateForm)(THIS, PCWSTR*) PURE; + STDMETHOD(GetQualifierValue)(THIS, LPCWSTR, LPWSTR*) PURE; + STDMETHOD(SetLanguage)(THIS, LPCWSTR) PURE; + STDMETHOD(SetHomeRegion)(THIS, LPCWSTR) PURE; + STDMETHOD(SetLayoutDirection)(THIS, ULONG) PURE; // RESOURCE_LAYOUT_DIRECTION + STDMETHOD(SetTargetSize)(THIS, USHORT) PURE; + STDMETHOD(SetScale)(THIS, ULONG) PURE; // RESOURCE_SCALE + STDMETHOD(SetContrast)(THIS, ULONG) PURE; // RESOURCE_CONTRAST + STDMETHOD(SetAlternateForm)(THIS, LPCWSTR) PURE; + STDMETHOD(SetQualifierValue)(THIS, LPCWSTR, LPCWSTR) PURE; + STDMETHOD(TrySetQualifierValue)(THIS, LPCWSTR, LPCWSTR, HRESULT*) PURE; + STDMETHOD(Reset)(THIS) PURE; + STDMETHOD(ResetQualifierValue)(THIS, LPCWSTR) PURE; + STDMETHOD(Clone)(THIS, IResourceContext**) PURE; + STDMETHOD(OverrideToMatch)(THIS, struct IResourceCandidate*) PURE; + + END_INTERFACE +}; + +#undef INTERFACE +#define INTERFACE IResourceMap +DECLARE_INTERFACE_IID(IResourceMap, IUnknown) +{ + BEGIN_INTERFACE + + // IUnknown + STDMETHOD(QueryInterface)(THIS, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IResourceMap + STDMETHOD(GetUri)(THIS, PWSTR *UriString) PURE; + STDMETHOD(GetSubtree)(THIS, PCWSTR *Name, IResourceMap** ResourceMap) PURE; + STDMETHOD(GetString)(THIS, PCWSTR Key, PWSTR *Value) PURE; + STDMETHOD(GetStringForContext)(THIS, IResourceContext* Context, PCWSTR Key, PWSTR *Value) PURE; + STDMETHOD(GetFilePath)(THIS, PCWSTR Key, PWSTR *Value) PURE; + STDMETHOD(GetFilePathForContext)(THIS, IResourceContext*, PCWSTR, PWSTR*) PURE; + STDMETHOD(GetNamedResourceCount)(THIS, PULONG) PURE; + STDMETHOD(GetNamedResourceUri)(THIS, ULONG, PWSTR*) PURE; + STDMETHOD(GetNamedResource)(THIS, PCWSTR, REFIID riid, PVOID *ppvObject) PURE; + STDMETHOD(GetFullyQualifiedReference)(THIS, LPCWSTR, LPCWSTR, LPWSTR*) PURE; + STDMETHOD(GetFilePathByUri)(THIS, IUri*, LPWSTR*) PURE; + STDMETHOD(GetFilePathForContextByUri)(THIS, IResourceContext*, IUri*, LPWSTR*) PURE; + + END_INTERFACE +}; + +#define IResourceMap_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) +#define IResourceMap_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) +#define IResourceMap_Release(This) \ + ((This)->lpVtbl->Release(This)) +#define IResourceMap_GetUri(This, UriString) \ + ((This)->lpVtbl->GetUri(This, UriString)) +#define IResourceMap_GetSubtree(This, Name, ResourceMap) \ + ((This)->lpVtbl->GetSubtree(This, Name, ResourceMap)) +#define IResourceMap_GetString(This, Key, Value) \ + ((This)->lpVtbl->GetString(This, Key, Value)) +#define IResourceMap_GetStringForContext(This, Context, Key, Value) \ + ((This)->lpVtbl->GetStringForContext(This, Context, Key, Value)) +#define IResourceMap_GetFilePath(This, Key, Value) \ + ((This)->lpVtbl->GetFilePath(This, Key, Value)) +#define IResourceMap_GetFilePathForContext(This) \ + ((This)->lpVtbl->GetFilePathForContext(This)) +#define IResourceMap_GetNamedResourceCount(This, Count) \ + ((This)->lpVtbl->GetNamedResourceCount(This, Count)) +#define IResourceMap_GetNamedResourceUri(This, Index, Name) \ + ((This)->lpVtbl->GetNamedResourceUri(This, Index, Name)) +#define IResourceMap_GetNamedResource(This, Name, rrid, ppvObject) \ + ((This)->lpVtbl->GetNamedResource(This, Name, rrid, ppvObject)) +#define IResourceMap_GetFullyQualifiedReference(This) \ + ((This)->lpVtbl->GetFullyQualifiedReference(This)) +#define IResourceMap_GetFilePathByUri(This) \ + ((This)->lpVtbl->GetFilePathByUri(This)) +#define IResourceMap_GetFilePathForContextByUri(This) \ + ((This)->lpVtbl->GetFilePathForContextByUri(This)) + +// Note: Documented PKEY_AppUserModel_XYZ keys can be found in propkey.h +DEFINE_PROPERTYKEY(PKEY_AppUserModel_HostEnvironment, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 14); +DEFINE_PROPERTYKEY(PKEY_AppUserModel_PackageInstallPath, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 15); +DEFINE_PROPERTYKEY(PKEY_AppUserModel_PackageFamilyName, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 17); +DEFINE_PROPERTYKEY(PKEY_AppUserModel_ParentID, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 19); +DEFINE_PROPERTYKEY(PKEY_AppUserModel_PackageFullName, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 21); +// PKEY_AppUserModel_ExcludeFromShowInNewInstall {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 8 +//2 PKEY_AppUserModel_ID {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 5 +//3 PKEY_AppUserModel_IsDestListSeparator {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 6 +//4 PKEY_AppUserModel_IsDualMode {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 11 +//5 PKEY_AppUserModel_PreventPinning {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 9 +//6 PKEY_AppUserModel_RelaunchCommand {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 2 +//7 PKEY_AppUserModel_RelaunchDisplayNameResource {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 4 +//8 PKEY_AppUserModel_RelaunchIconResource {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 3 +//9 PKEY_AppUserModel_StartPinOption {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 12 +//10 PKEY_AppUserModel_ToastActivatorCLSID {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 26 +//11 PKEY_AppUserModel_PackageInstallPath {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 15 +//12 PKEY_AppUserModel_RecordState {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 16 +//13 PKEY_AppUserModel_PackageFullName {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 21 +//14 PKEY_AppUserModel_DestListProvidedTitle {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 27 +//15 PKEY_AppUserModel_DestListProvidedDescription {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 28 +//16 PKEY_AppUserModel_ParentID {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 19 +//17 PKEY_AppUserModel_HostEnvironment {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 14 +//18 PKEY_AppUserModel_Relevance {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 13 +//19 PKEY_AppUserModel_PackageRelativeApplicationID {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 22 +//20 PKEY_AppUserModel_ExcludedFromLauncher {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 23 +//21 PKEY_AppUserModel_DestListLogoUri {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 29 +//22 PKEY_AppUserModel_ActivationContext {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 20 +//23 PKEY_AppUserModel_PackageFamilyName {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 17 +//24 PKEY_AppUserModel_BestShortcut {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 10 +//25 PKEY_AppUserModel_IsDestListLink {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 7 +//26 PKEY_AppUserModel_InstalledBy {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 18 +//27 PKEY_AppUserModel_RunFlags {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 25 +//28 PKEY_AppUserModel_DestListProvidedGroupName {9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3} 30 +DEFINE_PROPERTYKEY(PKEY_Tile_SmallLogoPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 2); +DEFINE_PROPERTYKEY(PKEY_Tile_Background, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 4); +DEFINE_PROPERTYKEY(PKEY_Tile_Foreground, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 5); +DEFINE_PROPERTYKEY(PKEY_Tile_LongDisplayName, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 11); +DEFINE_PROPERTYKEY(PKEY_Tile_Flags, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 14); +DEFINE_PROPERTYKEY(PKEY_Tile_SuiteDisplayName, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 16); +DEFINE_PROPERTYKEY(PKEY_Tile_SuiteSortName, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 17); +DEFINE_PROPERTYKEY(PKEY_Tile_DisplayNameLanguage, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 18); +DEFINE_PROPERTYKEY(PKEY_Tile_BadgeLogoPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 15); +DEFINE_PROPERTYKEY(PKEY_Tile_Square150x150LogoPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 12); +DEFINE_PROPERTYKEY(PKEY_Tile_Wide310x150LogoPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 13); +DEFINE_PROPERTYKEY(PKEY_Tile_Square310x310LogoPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 19); +DEFINE_PROPERTYKEY(PKEY_Tile_Square70x70LogoPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 20); +DEFINE_PROPERTYKEY(PKEY_Tile_FencePost, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 21); +DEFINE_PROPERTYKEY(PKEY_Tile_InstallProgress, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 22); +DEFINE_PROPERTYKEY(PKEY_Tile_EncodedTargetPath, 0x86D40B4D, 0x9069, 0x443C, 0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC, 23); + +#endif diff --git a/phlib/include/circbuf.h b/phlib/include/circbuf.h index 9c3564cfc183..b3e0d761651b 100644 --- a/phlib/include/circbuf.h +++ b/phlib/include/circbuf.h @@ -1,26 +1,26 @@ -#ifndef _PH_CIRCBUF_H -#define _PH_CIRCBUF_H - -#define PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - -#undef T -#define T ULONG -#include "circbuf_h.h" - -#undef T -#define T ULONG64 -#include "circbuf_h.h" - -#undef T -#define T PVOID -#include "circbuf_h.h" - -#undef T -#define T SIZE_T -#include "circbuf_h.h" - -#undef T -#define T FLOAT -#include "circbuf_h.h" - -#endif +#ifndef _PH_CIRCBUF_H +#define _PH_CIRCBUF_H + +#define PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + +#undef T +#define T ULONG +#include "circbuf_h.h" + +#undef T +#define T ULONG64 +#include "circbuf_h.h" + +#undef T +#define T PVOID +#include "circbuf_h.h" + +#undef T +#define T SIZE_T +#include "circbuf_h.h" + +#undef T +#define T FLOAT +#include "circbuf_h.h" + +#endif diff --git a/phlib/include/circbuf_h.h b/phlib/include/circbuf_h.h index 7b775b956000..84b9c105257f 100644 --- a/phlib/include/circbuf_h.h +++ b/phlib/include/circbuf_h.h @@ -1,140 +1,140 @@ -#ifdef T - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct T___(_PH_CIRCULAR_BUFFER, T) -{ - ULONG Size; -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - ULONG SizeMinusOne; -#endif - ULONG Count; - LONG Index; - T *Data; -} T___(PH_CIRCULAR_BUFFER, T), *T___(PPH_CIRCULAR_BUFFER, T); - -PHLIBAPI -VOID -NTAPI -T___(PhInitializeCircularBuffer, T)( - _Out_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ ULONG Size - ); - -PHLIBAPI -VOID -NTAPI -T___(PhDeleteCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer - ); - -PHLIBAPI -VOID -NTAPI -T___(PhResizeCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ ULONG NewSize - ); - -PHLIBAPI -VOID -NTAPI -T___(PhClearCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer - ); - -PHLIBAPI -VOID -NTAPI -T___(PhCopyCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _Out_writes_(Count) T *Destination, - _In_ ULONG Count - ); - -FORCEINLINE T T___(PhGetItemCircularBuffer, T)( - _In_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ LONG Index - ) -{ -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - return Buffer->Data[(Buffer->Index + Index) & Buffer->SizeMinusOne]; -#else - ULONG size; - - size = Buffer->Size; - // Modulo is dividend-based. - return Buffer->Data[(((Buffer->Index + Index) % size) + size) % size]; -#endif -} - -FORCEINLINE VOID T___(PhSetItemCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ LONG Index, - _In_ T Value - ) -{ -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - Buffer->Data[(Buffer->Index + Index) & Buffer->SizeMinusOne] = Value; -#else - ULONG size; - - size = Buffer->Size; - Buffer->Data[(((Buffer->Index + Index) % size) + size) % size] = Value; -#endif -} - -FORCEINLINE VOID T___(PhAddItemCircularBuffer, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ T Value - ) -{ -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - Buffer->Data[Buffer->Index = ((Buffer->Index - 1) & Buffer->SizeMinusOne)] = Value; -#else - ULONG size; - - size = Buffer->Size; - Buffer->Data[Buffer->Index = (((Buffer->Index - 1) % size) + size) % size] = Value; -#endif - - if (Buffer->Count < Buffer->Size) - Buffer->Count++; -} - -FORCEINLINE T T___(PhAddItemCircularBuffer2, T)( - _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, - _In_ T Value - ) -{ - LONG index; - T oldValue; - -#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE - index = ((Buffer->Index - 1) & Buffer->SizeMinusOne); -#else - ULONG size; - - size = Buffer->Size; - index = (((Buffer->Index - 1) % size) + size) % size; -#endif - - Buffer->Index = index; - oldValue = Buffer->Data[index]; - Buffer->Data[index] = Value; - - if (Buffer->Count < Buffer->Size) - Buffer->Count++; - - return oldValue; -} - -#ifdef __cplusplus -} -#endif - -#endif +#ifdef T + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct T___(_PH_CIRCULAR_BUFFER, T) +{ + ULONG Size; +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + ULONG SizeMinusOne; +#endif + ULONG Count; + LONG Index; + T *Data; +} T___(PH_CIRCULAR_BUFFER, T), *T___(PPH_CIRCULAR_BUFFER, T); + +PHLIBAPI +VOID +NTAPI +T___(PhInitializeCircularBuffer, T)( + _Out_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ ULONG Size + ); + +PHLIBAPI +VOID +NTAPI +T___(PhDeleteCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer + ); + +PHLIBAPI +VOID +NTAPI +T___(PhResizeCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ ULONG NewSize + ); + +PHLIBAPI +VOID +NTAPI +T___(PhClearCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer + ); + +PHLIBAPI +VOID +NTAPI +T___(PhCopyCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _Out_writes_(Count) T *Destination, + _In_ ULONG Count + ); + +FORCEINLINE T T___(PhGetItemCircularBuffer, T)( + _In_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ LONG Index + ) +{ +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + return Buffer->Data[(Buffer->Index + Index) & Buffer->SizeMinusOne]; +#else + ULONG size; + + size = Buffer->Size; + // Modulo is dividend-based. + return Buffer->Data[(((Buffer->Index + Index) % size) + size) % size]; +#endif +} + +FORCEINLINE VOID T___(PhSetItemCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ LONG Index, + _In_ T Value + ) +{ +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + Buffer->Data[(Buffer->Index + Index) & Buffer->SizeMinusOne] = Value; +#else + ULONG size; + + size = Buffer->Size; + Buffer->Data[(((Buffer->Index + Index) % size) + size) % size] = Value; +#endif +} + +FORCEINLINE VOID T___(PhAddItemCircularBuffer, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ T Value + ) +{ +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + Buffer->Data[Buffer->Index = ((Buffer->Index - 1) & Buffer->SizeMinusOne)] = Value; +#else + ULONG size; + + size = Buffer->Size; + Buffer->Data[Buffer->Index = (((Buffer->Index - 1) % size) + size) % size] = Value; +#endif + + if (Buffer->Count < Buffer->Size) + Buffer->Count++; +} + +FORCEINLINE T T___(PhAddItemCircularBuffer2, T)( + _Inout_ T___(PPH_CIRCULAR_BUFFER, T) Buffer, + _In_ T Value + ) +{ + LONG index; + T oldValue; + +#ifdef PH_CIRCULAR_BUFFER_POWER_OF_TWO_SIZE + index = ((Buffer->Index - 1) & Buffer->SizeMinusOne); +#else + ULONG size; + + size = Buffer->Size; + index = (((Buffer->Index - 1) % size) + size) % size; +#endif + + Buffer->Index = index; + oldValue = Buffer->Data[index]; + Buffer->Data[index] = Value; + + if (Buffer->Count < Buffer->Size) + Buffer->Count++; + + return oldValue; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/colorbox.h b/phlib/include/colorbox.h index 2a489b32199a..b6ec680d7dd3 100644 --- a/phlib/include/colorbox.h +++ b/phlib/include/colorbox.h @@ -1,30 +1,30 @@ -#ifndef _PH_COLORBOX_H -#define _PH_COLORBOX_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_COLORBOX_CLASSNAME L"PhColorBox" - -PHLIBAPI -BOOLEAN -NTAPI -PhColorBoxInitialization( - VOID - ); - -#define CBCM_SETCOLOR (WM_APP + 1501) -#define CBCM_GETCOLOR (WM_APP + 1502) - -#define ColorBox_SetColor(hWnd, Color) \ - SendMessage((hWnd), CBCM_SETCOLOR, (WPARAM)(Color), 0) - -#define ColorBox_GetColor(hWnd) \ - ((COLORREF)SendMessage((hWnd), CBCM_GETCOLOR, 0, 0)) - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_COLORBOX_H +#define _PH_COLORBOX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_COLORBOX_CLASSNAME L"PhColorBox" + +PHLIBAPI +BOOLEAN +NTAPI +PhColorBoxInitialization( + VOID + ); + +#define CBCM_SETCOLOR (WM_APP + 1501) +#define CBCM_GETCOLOR (WM_APP + 1502) + +#define ColorBox_SetColor(hWnd, Color) \ + SendMessage((hWnd), CBCM_SETCOLOR, (WPARAM)(Color), 0) + +#define ColorBox_GetColor(hWnd) \ + ((COLORREF)SendMessage((hWnd), CBCM_GETCOLOR, 0, 0)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/cpysave.h b/phlib/include/cpysave.h index 5ed893b581d1..5b91b605536e 100644 --- a/phlib/include/cpysave.h +++ b/phlib/include/cpysave.h @@ -1,78 +1,78 @@ -#ifndef _PH_CPYSAVE_H -#define _PH_CPYSAVE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_EXPORT_MODE_TABS 0 -#define PH_EXPORT_MODE_SPACES 1 -#define PH_EXPORT_MODE_CSV 2 - -PHLIBAPI -VOID PhaCreateTextTable( - _Out_ PPH_STRING ***Table, - _In_ ULONG Rows, - _In_ ULONG Columns - ); - -PHLIBAPI -PPH_LIST PhaFormatTextTable( - _In_ PPH_STRING **Table, - _In_ ULONG Rows, - _In_ ULONG Columns, - _In_ ULONG Mode - ); - -PHLIBAPI -VOID PhMapDisplayIndexTreeNew( - _In_ HWND TreeNewHandle, - _Out_opt_ PULONG *DisplayToId, - _Out_opt_ PWSTR **DisplayToText, - _Out_ PULONG NumberOfColumns - ); - -PHLIBAPI -PPH_STRING PhGetTreeNewText( - _In_ HWND TreeNewHandle, - _Reserved_ ULONG Reserved - ); - -PHLIBAPI -PPH_LIST PhGetGenericTreeNewLines( - _In_ HWND TreeNewHandle, - _In_ ULONG Mode - ); - -PHLIBAPI -VOID PhaMapDisplayIndexListView( - _In_ HWND ListViewHandle, - _Out_writes_(Count) PULONG DisplayToId, - _Out_writes_opt_(Count) PPH_STRING *DisplayToText, - _In_ ULONG Count, - _Out_ PULONG NumberOfColumns - ); - -PHLIBAPI -PPH_STRING PhaGetListViewItemText( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ INT SubItemIndex - ); - -PHLIBAPI -PPH_STRING PhGetListViewText( - _In_ HWND ListViewHandle - ); - -PHLIBAPI -PPH_LIST PhGetListViewLines( - _In_ HWND ListViewHandle, - _In_ ULONG Mode - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_CPYSAVE_H +#define _PH_CPYSAVE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_EXPORT_MODE_TABS 0 +#define PH_EXPORT_MODE_SPACES 1 +#define PH_EXPORT_MODE_CSV 2 + +PHLIBAPI +VOID PhaCreateTextTable( + _Out_ PPH_STRING ***Table, + _In_ ULONG Rows, + _In_ ULONG Columns + ); + +PHLIBAPI +PPH_LIST PhaFormatTextTable( + _In_ PPH_STRING **Table, + _In_ ULONG Rows, + _In_ ULONG Columns, + _In_ ULONG Mode + ); + +PHLIBAPI +VOID PhMapDisplayIndexTreeNew( + _In_ HWND TreeNewHandle, + _Out_opt_ PULONG *DisplayToId, + _Out_opt_ PWSTR **DisplayToText, + _Out_ PULONG NumberOfColumns + ); + +PHLIBAPI +PPH_STRING PhGetTreeNewText( + _In_ HWND TreeNewHandle, + _Reserved_ ULONG Reserved + ); + +PHLIBAPI +PPH_LIST PhGetGenericTreeNewLines( + _In_ HWND TreeNewHandle, + _In_ ULONG Mode + ); + +PHLIBAPI +VOID PhaMapDisplayIndexListView( + _In_ HWND ListViewHandle, + _Out_writes_(Count) PULONG DisplayToId, + _Out_writes_opt_(Count) PPH_STRING *DisplayToText, + _In_ ULONG Count, + _Out_ PULONG NumberOfColumns + ); + +PHLIBAPI +PPH_STRING PhaGetListViewItemText( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ INT SubItemIndex + ); + +PHLIBAPI +PPH_STRING PhGetListViewText( + _In_ HWND ListViewHandle + ); + +PHLIBAPI +PPH_LIST PhGetListViewLines( + _In_ HWND ListViewHandle, + _In_ ULONG Mode + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/dltmgr.h b/phlib/include/dltmgr.h index 593f4763bddc..f292bd770312 100644 --- a/phlib/include/dltmgr.h +++ b/phlib/include/dltmgr.h @@ -1,35 +1,35 @@ -#ifndef _PH_DLTMGR_H -#define _PH_DLTMGR_H - -typedef struct _PH_SINGLE_DELTA -{ - FLOAT Value; - FLOAT Delta; -} PH_SINGLE_DELTA, *PPH_SINGLE_DELTA; - -typedef struct _PH_UINT32_DELTA -{ - ULONG Value; - ULONG Delta; -} PH_UINT32_DELTA, *PPH_UINT32_DELTA; - -typedef struct _PH_UINT64_DELTA -{ - ULONG64 Value; - ULONG64 Delta; -} PH_UINT64_DELTA, *PPH_UINT64_DELTA; - -typedef struct _PH_UINTPTR_DELTA -{ - ULONG_PTR Value; - ULONG_PTR Delta; -} PH_UINTPTR_DELTA, *PPH_UINTPTR_DELTA; - -#define PhInitializeDelta(DltMgr) \ - ((DltMgr)->Value = 0, (DltMgr)->Delta = 0) - -#define PhUpdateDelta(DltMgr, NewValue) \ - ((DltMgr)->Delta = (NewValue) - (DltMgr)->Value, \ - (DltMgr)->Value = (NewValue), (DltMgr)->Delta) - -#endif +#ifndef _PH_DLTMGR_H +#define _PH_DLTMGR_H + +typedef struct _PH_SINGLE_DELTA +{ + FLOAT Value; + FLOAT Delta; +} PH_SINGLE_DELTA, *PPH_SINGLE_DELTA; + +typedef struct _PH_UINT32_DELTA +{ + ULONG Value; + ULONG Delta; +} PH_UINT32_DELTA, *PPH_UINT32_DELTA; + +typedef struct _PH_UINT64_DELTA +{ + ULONG64 Value; + ULONG64 Delta; +} PH_UINT64_DELTA, *PPH_UINT64_DELTA; + +typedef struct _PH_UINTPTR_DELTA +{ + ULONG_PTR Value; + ULONG_PTR Delta; +} PH_UINTPTR_DELTA, *PPH_UINTPTR_DELTA; + +#define PhInitializeDelta(DltMgr) \ + ((DltMgr)->Value = 0, (DltMgr)->Delta = 0) + +#define PhUpdateDelta(DltMgr, NewValue) \ + ((DltMgr)->Delta = (NewValue) - (DltMgr)->Value, \ + (DltMgr)->Value = (NewValue), (DltMgr)->Delta) + +#endif diff --git a/phlib/include/dspick.h b/phlib/include/dspick.h index c99a80bed4c2..e0a945caf3a5 100644 --- a/phlib/include/dspick.h +++ b/phlib/include/dspick.h @@ -1,48 +1,48 @@ -#ifndef _PH_DSPICK_H -#define _PH_DSPICK_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_DSPICK_MULTISELECT 0x1 - -typedef struct _PH_DSPICK_OBJECT -{ - PPH_STRING Name; - PSID Sid; -} PH_DSPICK_OBJECT, *PPH_DSPICK_OBJECT; - -typedef struct _PH_DSPICK_OBJECTS -{ - ULONG NumberOfObjects; - PH_DSPICK_OBJECT Objects[1]; -} PH_DSPICK_OBJECTS, *PPH_DSPICK_OBJECTS; - -PHLIBAPI -VOID PhFreeDsObjectPickerDialog( - _In_ PVOID PickerDialog - ); - -PHLIBAPI -PVOID PhCreateDsObjectPickerDialog( - _In_ ULONG Flags - ); - -PHLIBAPI -BOOLEAN PhShowDsObjectPickerDialog( - _In_ HWND hWnd, - _In_ PVOID PickerDialog, - _Out_ PPH_DSPICK_OBJECTS *Objects - ); - -PHLIBAPI -VOID PhFreeDsObjectPickerObjects( - _In_ PPH_DSPICK_OBJECTS Objects - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_DSPICK_H +#define _PH_DSPICK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_DSPICK_MULTISELECT 0x1 + +typedef struct _PH_DSPICK_OBJECT +{ + PPH_STRING Name; + PSID Sid; +} PH_DSPICK_OBJECT, *PPH_DSPICK_OBJECT; + +typedef struct _PH_DSPICK_OBJECTS +{ + ULONG NumberOfObjects; + PH_DSPICK_OBJECT Objects[1]; +} PH_DSPICK_OBJECTS, *PPH_DSPICK_OBJECTS; + +PHLIBAPI +VOID PhFreeDsObjectPickerDialog( + _In_ PVOID PickerDialog + ); + +PHLIBAPI +PVOID PhCreateDsObjectPickerDialog( + _In_ ULONG Flags + ); + +PHLIBAPI +BOOLEAN PhShowDsObjectPickerDialog( + _In_ HWND hWnd, + _In_ PVOID PickerDialog, + _Out_ PPH_DSPICK_OBJECTS *Objects + ); + +PHLIBAPI +VOID PhFreeDsObjectPickerObjects( + _In_ PPH_DSPICK_OBJECTS Objects + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/emenu.h b/phlib/include/emenu.h index 42955abce781..bde90b75c9c6 100644 --- a/phlib/include/emenu.h +++ b/phlib/include/emenu.h @@ -1,219 +1,236 @@ -#ifndef _PH_EMENU_H -#define _PH_EMENU_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_EMENU_DISABLED 0x1 -#define PH_EMENU_CHECKED 0x2 -#define PH_EMENU_HIGHLIGHT 0x4 -#define PH_EMENU_MENUBARBREAK 0x8 -#define PH_EMENU_MENUBREAK 0x10 -#define PH_EMENU_DEFAULT 0x20 -#define PH_EMENU_MOUSESELECT 0x40 -#define PH_EMENU_RADIOCHECK 0x80 - -#define PH_EMENU_SEPARATECHECKSPACE 0x100000 -#define PH_EMENU_SEPARATOR 0x200000 - -#define PH_EMENU_TEXT_OWNED 0x80000000 -#define PH_EMENU_BITMAP_OWNED 0x40000000 - -struct _PH_EMENU_ITEM; - -typedef VOID (NTAPI *PPH_EMENU_ITEM_DELETE_FUNCTION)( - _In_ struct _PH_EMENU_ITEM *Item - ); - -typedef struct _PH_EMENU_ITEM -{ - ULONG Flags; - ULONG Id; - PWSTR Text; - HBITMAP Bitmap; - - PVOID Parameter; - PVOID Context; - PPH_EMENU_ITEM_DELETE_FUNCTION DeleteFunction; - PVOID Reserved; - - struct _PH_EMENU_ITEM *Parent; - PPH_LIST Items; -} PH_EMENU_ITEM, *PPH_EMENU_ITEM; - -typedef struct _PH_EMENU_ITEM PH_EMENU, *PPH_EMENU; - -PHLIBAPI -PPH_EMENU_ITEM PhCreateEMenuItem( - _In_ ULONG Flags, - _In_ ULONG Id, - _In_ PWSTR Text, - _In_opt_ HBITMAP Bitmap, - _In_opt_ PVOID Context - ); - -PHLIBAPI -VOID PhDestroyEMenuItem( - _In_ PPH_EMENU_ITEM Item - ); - -#define PH_EMENU_FIND_DESCEND 0x1 -#define PH_EMENU_FIND_STARTSWITH 0x2 -#define PH_EMENU_FIND_LITERAL 0x4 - -PHLIBAPI -PPH_EMENU_ITEM PhFindEMenuItem( - _In_ PPH_EMENU_ITEM Item, - _In_ ULONG Flags, - _In_opt_ PWSTR Text, - _In_opt_ ULONG Id - ); - -PHLIBAPI -PPH_EMENU_ITEM PhFindEMenuItemEx( - _In_ PPH_EMENU_ITEM Item, - _In_ ULONG Flags, - _In_opt_ PWSTR Text, - _In_opt_ ULONG Id, - _Out_opt_ PPH_EMENU_ITEM *FoundParent, - _Out_opt_ PULONG FoundIndex - ); - -PHLIBAPI -ULONG PhIndexOfEMenuItem( - _In_ PPH_EMENU_ITEM Parent, - _In_ PPH_EMENU_ITEM Item - ); - -PHLIBAPI -VOID PhInsertEMenuItem( - _Inout_ PPH_EMENU_ITEM Parent, - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG Index - ); - -PHLIBAPI -BOOLEAN PhRemoveEMenuItem( - _Inout_opt_ PPH_EMENU_ITEM Parent, - _In_opt_ PPH_EMENU_ITEM Item, - _In_opt_ ULONG Index - ); - -PHLIBAPI -VOID PhRemoveAllEMenuItems( - _Inout_ PPH_EMENU_ITEM Parent - ); - -PHLIBAPI -PPH_EMENU PhCreateEMenu( - VOID - ); - -PHLIBAPI -VOID PhDestroyEMenu( - _In_ PPH_EMENU Menu - ); - -#define PH_EMENU_CONVERT_ID 0x1 - -typedef struct _PH_EMENU_DATA -{ - PPH_LIST IdToItem; -} PH_EMENU_DATA, *PPH_EMENU_DATA; - -PHLIBAPI -VOID PhInitializeEMenuData( - _Out_ PPH_EMENU_DATA Data - ); - -PHLIBAPI -VOID PhDeleteEMenuData( - _Inout_ PPH_EMENU_DATA Data - ); - -PHLIBAPI -HMENU PhEMenuToHMenu( - _In_ PPH_EMENU_ITEM Menu, - _In_ ULONG Flags, - _Inout_opt_ PPH_EMENU_DATA Data - ); - -PHLIBAPI -VOID PhEMenuToHMenu2( - _In_ HMENU MenuHandle, - _In_ PPH_EMENU_ITEM Menu, - _In_ ULONG Flags, - _Inout_opt_ PPH_EMENU_DATA Data - ); - -PHLIBAPI -VOID PhHMenuToEMenuItem( - _Inout_ PPH_EMENU_ITEM MenuItem, - _In_ HMENU MenuHandle - ); - -PHLIBAPI -VOID PhLoadResourceEMenuItem( - _Inout_ PPH_EMENU_ITEM MenuItem, - _In_ HINSTANCE InstanceHandle, - _In_ PWSTR Resource, - _In_ ULONG SubMenuIndex - ); - -#define PH_EMENU_SHOW_SEND_COMMAND 0x1 -#define PH_EMENU_SHOW_LEFTRIGHT 0x2 - -PHLIBAPI -PPH_EMENU_ITEM PhShowEMenu( - _In_ PPH_EMENU Menu, - _In_ HWND WindowHandle, - _In_ ULONG Flags, - _In_ ULONG Align, - _In_ ULONG X, - _In_ ULONG Y - ); - -// Convenience functions - -PHLIBAPI -BOOLEAN PhSetFlagsEMenuItem( - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG Id, - _In_ ULONG Mask, - _In_ ULONG Value - ); - -FORCEINLINE BOOLEAN PhEnableEMenuItem( - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG Id, - _In_ BOOLEAN Enable - ) -{ - return PhSetFlagsEMenuItem(Item, Id, PH_EMENU_DISABLED, Enable ? 0 : PH_EMENU_DISABLED); -} - -PHLIBAPI -VOID PhSetFlagsAllEMenuItems( - _In_ PPH_EMENU_ITEM Item, - _In_ ULONG Mask, - _In_ ULONG Value - ); - -#define PH_EMENU_MODIFY_TEXT 0x1 -#define PH_EMENU_MODIFY_BITMAP 0x2 - -PHLIBAPI -VOID PhModifyEMenuItem( - _Inout_ PPH_EMENU_ITEM Item, - _In_ ULONG ModifyFlags, - _In_ ULONG OwnedFlags, - _In_opt_ PWSTR Text, - _In_opt_ HBITMAP Bitmap - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_EMENU_H +#define _PH_EMENU_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_EMENU_DISABLED 0x1 +#define PH_EMENU_CHECKED 0x2 +#define PH_EMENU_HIGHLIGHT 0x4 +#define PH_EMENU_MENUBARBREAK 0x8 +#define PH_EMENU_MENUBREAK 0x10 +#define PH_EMENU_DEFAULT 0x20 +#define PH_EMENU_MOUSESELECT 0x40 +#define PH_EMENU_RADIOCHECK 0x80 + +#define PH_EMENU_SEPARATECHECKSPACE 0x100000 +#define PH_EMENU_SEPARATOR 0x200000 + +#define PH_EMENU_TEXT_OWNED 0x80000000 +#define PH_EMENU_BITMAP_OWNED 0x40000000 + +struct _PH_EMENU_ITEM; + +typedef VOID (NTAPI *PPH_EMENU_ITEM_DELETE_FUNCTION)( + _In_ struct _PH_EMENU_ITEM *Item + ); + +typedef struct _PH_EMENU_ITEM +{ + ULONG Flags; + ULONG Id; + PWSTR Text; + HBITMAP Bitmap; + + PVOID Parameter; + PVOID Context; + PPH_EMENU_ITEM_DELETE_FUNCTION DeleteFunction; + PVOID Reserved; + + struct _PH_EMENU_ITEM *Parent; + PPH_LIST Items; +} PH_EMENU_ITEM, *PPH_EMENU_ITEM; + +typedef struct _PH_EMENU_ITEM PH_EMENU, *PPH_EMENU; + +PHLIBAPI +PPH_EMENU_ITEM PhCreateEMenuItem( + _In_ ULONG Flags, + _In_ ULONG Id, + _In_opt_ PWSTR Text, + _In_opt_ HBITMAP Bitmap, + _In_opt_ PVOID Context + ); + +PHLIBAPI +VOID PhDestroyEMenuItem( + _In_ PPH_EMENU_ITEM Item + ); + +#define PH_EMENU_FIND_DESCEND 0x1 +#define PH_EMENU_FIND_STARTSWITH 0x2 +#define PH_EMENU_FIND_LITERAL 0x4 + +PHLIBAPI +PPH_EMENU_ITEM PhFindEMenuItem( + _In_ PPH_EMENU_ITEM Item, + _In_ ULONG Flags, + _In_opt_ PWSTR Text, + _In_opt_ ULONG Id + ); + +PHLIBAPI +PPH_EMENU_ITEM PhFindEMenuItemEx( + _In_ PPH_EMENU_ITEM Item, + _In_ ULONG Flags, + _In_opt_ PWSTR Text, + _In_opt_ ULONG Id, + _Out_opt_ PPH_EMENU_ITEM *FoundParent, + _Out_opt_ PULONG FoundIndex + ); + +PHLIBAPI +ULONG PhIndexOfEMenuItem( + _In_ PPH_EMENU_ITEM Parent, + _In_ PPH_EMENU_ITEM Item + ); + +PHLIBAPI +VOID PhInsertEMenuItem( + _Inout_ PPH_EMENU_ITEM Parent, + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG Index + ); + +PHLIBAPI +BOOLEAN PhRemoveEMenuItem( + _Inout_opt_ PPH_EMENU_ITEM Parent, + _In_opt_ PPH_EMENU_ITEM Item, + _In_opt_ ULONG Index + ); + +PHLIBAPI +VOID PhRemoveAllEMenuItems( + _Inout_ PPH_EMENU_ITEM Parent + ); + +PHLIBAPI +PPH_EMENU PhCreateEMenu( + VOID + ); + +PHLIBAPI +VOID PhDestroyEMenu( + _In_ PPH_EMENU Menu + ); + +#define PH_EMENU_CONVERT_ID 0x1 + +typedef struct _PH_EMENU_DATA +{ + PPH_LIST IdToItem; +} PH_EMENU_DATA, *PPH_EMENU_DATA; + +PHLIBAPI +VOID PhInitializeEMenuData( + _Out_ PPH_EMENU_DATA Data + ); + +PHLIBAPI +VOID PhDeleteEMenuData( + _Inout_ PPH_EMENU_DATA Data + ); + +PHLIBAPI +HMENU PhEMenuToHMenu( + _In_ PPH_EMENU_ITEM Menu, + _In_ ULONG Flags, + _Inout_opt_ PPH_EMENU_DATA Data + ); + +PHLIBAPI +VOID PhEMenuToHMenu2( + _In_ HMENU MenuHandle, + _In_ PPH_EMENU_ITEM Menu, + _In_ ULONG Flags, + _Inout_opt_ PPH_EMENU_DATA Data + ); + +PHLIBAPI +VOID PhHMenuToEMenuItem( + _Inout_ PPH_EMENU_ITEM MenuItem, + _In_ HMENU MenuHandle + ); + +PHLIBAPI +VOID PhLoadResourceEMenuItem( + _Inout_ PPH_EMENU_ITEM MenuItem, + _In_ HINSTANCE InstanceHandle, + _In_ PWSTR Resource, + _In_ ULONG SubMenuIndex + ); + +#define PH_EMENU_SHOW_SEND_COMMAND 0x1 +#define PH_EMENU_SHOW_LEFTRIGHT 0x2 + +PHLIBAPI +PPH_EMENU_ITEM PhShowEMenu( + _In_ PPH_EMENU Menu, + _In_ HWND WindowHandle, + _In_ ULONG Flags, + _In_ ULONG Align, + _In_ ULONG X, + _In_ ULONG Y + ); + +PHLIBAPI +BOOLEAN PhSetFlagsEMenuItem( + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG Id, + _In_ ULONG Mask, + _In_ ULONG Value + ); + +PHLIBAPI +VOID PhSetFlagsAllEMenuItems( + _In_ PPH_EMENU_ITEM Item, + _In_ ULONG Mask, + _In_ ULONG Value + ); + +#define PH_EMENU_MODIFY_TEXT 0x1 +#define PH_EMENU_MODIFY_BITMAP 0x2 + +PHLIBAPI +VOID PhModifyEMenuItem( + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG ModifyFlags, + _In_ ULONG OwnedFlags, + _In_opt_ PWSTR Text, + _In_opt_ HBITMAP Bitmap + ); + +// Convenience functions + +FORCEINLINE +PPH_EMENU_ITEM PhCreateEMenuSeparator( + VOID + ) +{ + return PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL); +} + +FORCEINLINE +BOOLEAN PhEnableEMenuItem( + _Inout_ PPH_EMENU_ITEM Item, + _In_ ULONG Id, + _In_ BOOLEAN Enable + ) +{ + return PhSetFlagsEMenuItem(Item, Id, PH_EMENU_DISABLED, Enable ? 0 : PH_EMENU_DISABLED); +} + +FORCEINLINE +VOID PhSetDisabledEMenuItem( + _In_ PPH_EMENU_ITEM Item + ) +{ + Item->Flags |= PH_EMENU_DISABLED; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/exlf.h b/phlib/include/exlf.h new file mode 100644 index 000000000000..e80542b70c99 --- /dev/null +++ b/phlib/include/exlf.h @@ -0,0 +1,434 @@ +#ifndef _PH_EXLF_H +#define _PH_EXLF_H + +/* + * This file contains the required types for ELF bianires. + * + * References: + * http://man7.org/linux/man-pages/man5/elf.5.html + * http://www.skyfree.org/linux/references/ELF_Format.pdf + * https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h + * https://chromium.googlesource.com/chromiumos/chromite/+/HEAD/lib/parseelf.py + */ + +#define EI_NIDENT 16 + +// e_ident[] indexes +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_PAD 8 + +// EI_MAG +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +// EI_CLASS +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +// EI_DATA +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +// EI_VERSION and e_version +#define EV_NONE 0 +#define EV_CURRENT 1 + +#define ELFOSABI_NONE 0 +#define ELFOSABI_LINUX 3 + +// e_type +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +// e_machine +#define EM_386 3 +#define EM_X86_64 62 + +/* segment types */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 /* Thread local storage segment */ +#define PT_LOOS 0x60000000 /* OS-specific */ +#define PT_HIOS 0x6fffffff /* OS-specific */ +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_EH_FRAME 0x6474e550 +#define PT_GNU_STACK (PT_LOOS + 0x474e551) + +/* permissions on sections in the program header, p_flags. */ +#define PF_NONE 0x0 +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +// sh_type +#define SHT_NULL 0 // Section header table entry (unused). +#define SHT_PROGBITS 1 // Program data. +#define SHT_SYMTAB 2 // Link editing symbol table. +#define SHT_STRTAB 3 // A string table. +#define SHT_RELA 4 // Relocation entries with addends. +#define SHT_HASH 5 // A symbol hash table. +#define SHT_DYNAMIC 6 // Information for dynamic linking. +#define SHT_NOTE 7 // Information that marks file. +#define SHT_NOBITS 8 // Section occupies no space in file. +#define SHT_REL 9 // Relocation entries, no addends. +#define SHT_SHLIB 10 // Reserved, unspecified semantics. +#define SHT_DYNSYM 11 // Dynamic linking symbol table. +//#define SHT_NUM 12 +#define SHT_INIT_ARRAY 14 // Array of constructors. +#define SHT_FINI_ARRAY 15 // Array of destructors. +#define SHT_PREINIT_ARRAY 16 // Array of pre-constructors. +#define SHT_GROUP 17 // Section group. +#define SHT_SYMTAB_SHNDX 18 // Extended section indeces. +#define SHT_NUM 19 // Number of defined types. (dmex: Some tools define this as 19 and others as 12???) +#define SHT_LOOS 0x60000000 // First of OS specific semantics. +#define SHT_HIOS 0x6fffffff // Last of OS specific semantics. +#define SHT_GNU_INCREMENTAL_INPUTS 0x6fff4700 // incremental build data. +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 // Object attributes. +#define SHT_GNU_HASH 0x6ffffff6 // GNU style symbol hash table. +#define SHT_GNU_LIBLIST 0x6ffffff7 // List of prelink dependencies. +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff +#define SHT_SUNW_verdef 0x6ffffffd // Versions defined by file. +#define SHT_SUNW_verneed 0x6ffffffe // Versions needed by file. +#define SHT_SUNW_versym 0x6fffffff // Symbol versions. + +// sh_flags +#define SHF_WRITE 0x1 /* Section contains writable data. */ +#define SHF_ALLOC 0x2 /* Section occupies memory. */ +#define SHF_EXECINSTR 0x4 /* Section contains instructions. */ +#define SHF_MERGE 0x10 /* Section may be merged. */ +#define SHF_STRINGS 0x20 /* Section contains strings. */ +#define SHF_INFO_LINK 0x40 /* sh_info holds section index. */ +#define SHF_LINK_ORDER 0x80 /* Special ordering requirements. */ +#define SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required. */ +#define SHF_GROUP 0x200 /* Member of section group. */ +#define SHF_TLS 0x400 /* Section contains TLS data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific semantics. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific semantics. */ + +// special section indexes. +#define SHN_UNDEF 0 // An undefined symbol. +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_LIVEPATCH 0xff20 +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +// dynamic section +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_INIT_ARRAY 25 +#define DT_FINI_ARRAY 26 +#define DT_INIT_ARRAYSZ 27 +#define DT_FINI_ARRAYSZ 28 +#define DT_RUNPATH 29 +#define DT_FLAGS 30 +#define DT_PREINIT_ARRAY 32 // DT_ENCODING +#define DT_PREINIT_ARRAYSZ 33 +#define OLD_DT_LOOS 0x60000000 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 +#define DT_VALRNGLO 0x6ffffd00 +#define DT_VALRNGHI 0x6ffffdff +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_GNU_HASH 0x6ffffef5 +#define DT_VERSYM 0x6ffffff0 +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff +#define OLD_DT_HIOS 0x6fffffff +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +// symbol table section +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_COMMON 5 +#define STT_TLS 6 +#define STT_GNU_IFUNC 10 +#define STT_LOOS 10 +#define STT_HIOS 12 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + +#define ELF_ST_BIND(x) ((x) >> 4) +#define ELF_ST_TYPE(x) ((x) & 0xF) +#define ELF_ST_VISIBILITY(x) ((x) & 0x03) + +#define ELF32_ST_BIND(x) ELF_ST_BIND(x) +#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF32_ST_VIS(a) ELF_ST_VISIBILITY(a) +#define ELF64_ST_BIND(x) ELF_ST_BIND(x) +#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF64_ST_VIS(a) ELF_ST_VISIBILITY(a) + +// Non-standard ELF definitions (dmex) + +#define IMAGE_ELF_SIGNATURE 0x457f // "\x7fELF" +#define ELFMAG ((BYTE[4]){ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3}) + +typedef struct _ELF_IMAGE_HEADER +{ + union + { + unsigned char e_ident[EI_NIDENT]; + struct + { + unsigned char MagicNumber[4]; + unsigned char Class; + unsigned char Data; + unsigned char Version; + unsigned char Abi; + unsigned char AbiVersion; + unsigned char Unused[7]; + }; + }; + unsigned short e_type; + unsigned short e_machine; + unsigned int e_version; + //union { + // PELF_IMAGE_HEADER32 Headers32; + // PELF_IMAGE_HEADER64 Headers64; + //}; +} ELF_IMAGE_HEADER, *PELF_IMAGE_HEADER; + +typedef struct _ELF_IMAGE_HEADER32 +{ + // ELF_IMAGE_HEADER Header; + unsigned int e_entry; // Entry point virtual address. + unsigned int e_phoff; // Program header table file offset. + unsigned int e_shoff; // Section header table file offset. + unsigned int e_flags; // Processor-specific flags. + unsigned short e_ehsize; // ELF header size in bytes. + unsigned short e_phentsize; // Program header table entry size. + unsigned short e_phnum; // Program header table entry count. + unsigned short e_shentsize; // Section header table entry size. + unsigned short e_shnum; // Section header table entry count. + unsigned short e_shstrndx; // Section header string table index. +} ELF_IMAGE_HEADER32, *PELF_IMAGE_HEADER32; + +typedef struct _ELF_IMAGE_HEADER64 +{ + // ELF_IMAGE_HEADER Header; + unsigned long long e_entry; // Entry point virtual address. + unsigned long long e_phoff; // Program header table file offset. + unsigned long long e_shoff; // Section header table file offset. + unsigned int e_flags; // Processor-specific flags. + unsigned short e_ehsize; // ELF header size in bytes. + unsigned short e_phentsize; // Program header table entry size. + unsigned short e_phnum; // Program header table entry count. + unsigned short e_shentsize; // Section header table entry size. + unsigned short e_shnum; // Section header table entry count. + unsigned short e_shstrndx; // Section header string table index. +} ELF_IMAGE_HEADER64, *PELF_IMAGE_HEADER64; + +typedef struct _ELF32_IMAGE_SEGMENT_HEADER +{ + unsigned int p_type; // Segment type. + unsigned int p_offset; // Segment file offset. + unsigned int p_vaddr; // Segment virtual address. + unsigned int p_paddr; // Segment physical address. + unsigned int p_filesz; // Segment size in file. + unsigned int p_memsz; // Segment size in memory. + unsigned int p_flags; // Segment flags. + unsigned int p_align; // Segment alignment. +} ELF32_IMAGE_SEGMENT_HEADER; + +typedef struct _ELF64_IMAGE_SEGMENT_HEADER +{ + unsigned int p_type; // Segment type. + unsigned int p_flags; // Segment flags. + unsigned long long p_offset; // Segment file offset. + unsigned long long p_vaddr; // Segment virtual address. + unsigned long long p_paddr; // Segment physical address. + unsigned long long p_filesz; // Segment size in file. + unsigned long long p_memsz; // Segment size in memory. + unsigned long long p_align; // Segment alignment. +} ELF64_IMAGE_SEGMENT_HEADER, *PELF64_IMAGE_SEGMENT_HEADER; + +#define IMAGE_FIRST_ELF64_SEGMENT(MappedWslImage) \ + ((PELF64_IMAGE_SEGMENT_HEADER)PTR_ADD_OFFSET(MappedWslImage->Header, MappedWslImage->Headers64->e_phoff)) + +#define IMAGE_ELF64_SEGMENT_BY_INDEX(SegmentHeader, Index) \ + ((PELF64_IMAGE_SEGMENT_HEADER)PTR_ADD_OFFSET(SegmentHeader, sizeof(ELF64_IMAGE_SEGMENT_HEADER) * Index)) + //((PELF64_IMAGE_SEGMENT_HEADER)&SegmentHeaderTable[Index]) + +typedef struct _ELF32_IMAGE_SECTION_HEADER +{ + unsigned int sh_name; + unsigned int sh_type; + unsigned int sh_flags; + unsigned int sh_addr; + unsigned int sh_offset; + unsigned int sh_size; + unsigned int sh_link; + unsigned int sh_info; + unsigned int sh_addralign; + unsigned int sh_entsize; +} ELF32_IMAGE_SECTION_HEADER; + +typedef struct _ELF64_IMAGE_SECTION_HEADER +{ + unsigned int sh_name; /* Section name, index in string tbl */ + unsigned int sh_type; /* Type of section */ + unsigned long long sh_flags; /* Miscellaneous section attributes */ + unsigned long long sh_addr; /* Section virtual addr at execution */ + unsigned long long sh_offset; /* Section file offset */ + unsigned long long sh_size; /* Size of section in bytes */ + unsigned int sh_link; /* Index of another section */ + unsigned int sh_info; /* Additional section information */ + unsigned long long sh_addralign; /* Section alignment */ + unsigned long long sh_entsize; /* Entry size if section holds table */ +} ELF64_IMAGE_SECTION_HEADER, *PELF64_IMAGE_SECTION_HEADER; + +#define IMAGE_FIRST_ELF64_SECTION(MappedWslImage) \ + ((PELF64_IMAGE_SECTION_HEADER)PTR_ADD_OFFSET(MappedWslImage->Header, MappedWslImage->Headers64->e_shoff)) + +#define IMAGE_ELF64_SECTION_BY_INDEX(SectionHeader, Index) \ + ((PELF64_IMAGE_SECTION_HEADER)PTR_ADD_OFFSET(SectionHeader, sizeof(ELF64_IMAGE_SECTION_HEADER) * Index)) + // ((PELF64_IMAGE_SECTION_HEADER)&SectionHeaderTable[Index]) + +// ELF dynamic entries + +typedef struct _ELF32_IMAGE_DYNAMIC_ENTRY // Elf32_Dyn +{ + int d_tag; + unsigned int d_val; +} ELF32_IMAGE_DYNAMIC_ENTRY, *PELF32_IMAGE_DYNAMIC_ENTRY; + +typedef struct _ELF64_IMAGE_DYNAMIC_ENTRY // Elf64_Dyn +{ + long long d_tag; + unsigned long long d_val; +} ELF64_IMAGE_DYNAMIC_ENTRY, *PELF64_IMAGE_DYNAMIC_ENTRY; + +// ELF symbol entries + +typedef struct _ELF_IMAGE_SYMBOL_ENTRY // Elf_Sym +{ + unsigned int st_name; + unsigned char st_info; + unsigned char st_other; + unsigned short st_shndx; + unsigned long long st_value; + unsigned long long st_size; +} ELF_IMAGE_SYMBOL_ENTRY, *PELF_IMAGE_SYMBOL_ENTRY; + +// ELF version entires + +typedef struct _ELF_VERSION_TABLE // Elf_Versym +{ + unsigned short vs_vers; +} ELF_VERSION_TABLE, *PELF_VERSION_TABLE; + +typedef struct +{ + unsigned short vd_version; + unsigned short vd_flags; // flags (VER_FLG_*). + unsigned short vd_ndx; // version index. + unsigned short vd_cnt; // number of verdaux entries. + unsigned int vd_hash; // hash of name. + unsigned int vd_aux; // offset to verdaux entries. + unsigned int vd_next; // offset to next verdef. +} Elf_Verdef; + +typedef struct +{ + unsigned int vda_name; /* string table offset of name */ + unsigned int vda_next; /* offset to verdaux */ +} Elf_Verdaux; + +// vn_version +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +typedef struct _ELF_VERSION_NEED // Elf_Verneed +{ + unsigned short vn_version; + unsigned short vn_cnt; + unsigned int vn_file; + unsigned int vn_aux; + unsigned int vn_next; +} ELF_VERSION_NEED, *PELF_VERSION_NEED; + +typedef struct _ELF_VERSION_AUX // Elf_Vernaux +{ + unsigned int vna_hash; // dependency name hash. + unsigned short vna_flags; // flags (VER_FLG_*). + unsigned short vna_other; + unsigned int vna_name; + unsigned int vna_next; +} ELF_VERSION_AUX, *PELF_VERSION_AUX; + +#endif diff --git a/phlib/include/fastlock.h b/phlib/include/fastlock.h index f817a2be1c39..d059e6160390 100644 --- a/phlib/include/fastlock.h +++ b/phlib/include/fastlock.h @@ -1,93 +1,93 @@ -#ifndef _PH_FASTLOCK_H -#define _PH_FASTLOCK_H - -// FastLock is a port of FastResourceLock from PH 1.x. - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _PH_FAST_LOCK -{ - ULONG Value; - HANDLE ExclusiveWakeEvent; - HANDLE SharedWakeEvent; -} PH_FAST_LOCK, *PPH_FAST_LOCK; - -#define PH_FAST_LOCK_INIT { 0, NULL, NULL } - -PHLIBAPI -VOID -NTAPI -PhInitializeFastLock( - _Out_ PPH_FAST_LOCK FastLock - ); - -PHLIBAPI -VOID -NTAPI -PhDeleteFastLock( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#define PhAcquireFastLockExclusive PhfAcquireFastLockExclusive -_May_raise_ -_Acquires_exclusive_lock_(*FastLock) -PHLIBAPI -VOID -FASTCALL -PhfAcquireFastLockExclusive( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#define PhAcquireFastLockShared PhfAcquireFastLockShared -_May_raise_ -_Acquires_shared_lock_(*FastLock) -PHLIBAPI -VOID -FASTCALL -PhfAcquireFastLockShared( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#define PhReleaseFastLockExclusive PhfReleaseFastLockExclusive -_Releases_exclusive_lock_(*FastLock) -PHLIBAPI -VOID -FASTCALL -PhfReleaseFastLockExclusive( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#define PhReleaseFastLockShared PhfReleaseFastLockShared -_Releases_shared_lock_(*FastLock) -PHLIBAPI -VOID -FASTCALL -PhfReleaseFastLockShared( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#define PhTryAcquireFastLockExclusive PhfTryAcquireFastLockExclusive -_When_(return != 0, _Acquires_exclusive_lock_(*FastLock)) -PHLIBAPI -BOOLEAN -FASTCALL -PhfTryAcquireFastLockExclusive( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#define PhTryAcquireFastLockShared PhfTryAcquireFastLockShared -_When_(return != 0, _Acquires_shared_lock_(*FastLock)) -PHLIBAPI -BOOLEAN -FASTCALL -PhfTryAcquireFastLockShared( - _Inout_ PPH_FAST_LOCK FastLock - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_FASTLOCK_H +#define _PH_FASTLOCK_H + +// FastLock is a port of FastResourceLock from PH 1.x. + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _PH_FAST_LOCK +{ + ULONG Value; + HANDLE ExclusiveWakeEvent; + HANDLE SharedWakeEvent; +} PH_FAST_LOCK, *PPH_FAST_LOCK; + +#define PH_FAST_LOCK_INIT { 0, NULL, NULL } + +PHLIBAPI +VOID +NTAPI +PhInitializeFastLock( + _Out_ PPH_FAST_LOCK FastLock + ); + +PHLIBAPI +VOID +NTAPI +PhDeleteFastLock( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#define PhAcquireFastLockExclusive PhfAcquireFastLockExclusive +_May_raise_ +_Acquires_exclusive_lock_(*FastLock) +PHLIBAPI +VOID +FASTCALL +PhfAcquireFastLockExclusive( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#define PhAcquireFastLockShared PhfAcquireFastLockShared +_May_raise_ +_Acquires_shared_lock_(*FastLock) +PHLIBAPI +VOID +FASTCALL +PhfAcquireFastLockShared( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#define PhReleaseFastLockExclusive PhfReleaseFastLockExclusive +_Releases_exclusive_lock_(*FastLock) +PHLIBAPI +VOID +FASTCALL +PhfReleaseFastLockExclusive( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#define PhReleaseFastLockShared PhfReleaseFastLockShared +_Releases_shared_lock_(*FastLock) +PHLIBAPI +VOID +FASTCALL +PhfReleaseFastLockShared( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#define PhTryAcquireFastLockExclusive PhfTryAcquireFastLockExclusive +_When_(return != 0, _Acquires_exclusive_lock_(*FastLock)) +PHLIBAPI +BOOLEAN +FASTCALL +PhfTryAcquireFastLockExclusive( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#define PhTryAcquireFastLockShared PhfTryAcquireFastLockShared +_When_(return != 0, _Acquires_shared_lock_(*FastLock)) +PHLIBAPI +BOOLEAN +FASTCALL +PhfTryAcquireFastLockShared( + _Inout_ PPH_FAST_LOCK FastLock + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/filepool.h b/phlib/include/filepool.h index 2d2263b5d674..26fa765bc503 100644 --- a/phlib/include/filepool.h +++ b/phlib/include/filepool.h @@ -1,196 +1,196 @@ -#ifndef _PH_FILEPOOL_H -#define _PH_FILEPOOL_H - -#ifdef __cplusplus -extern "C" { -#endif - -// On-disk structures - -// Each file has at least one segment. Each segment has a number of blocks, which are allocated from -// a bitmap. The segment header is always in the first block of each segment, except for the first -// segment. In the first segment, the file header is in the first few blocks, followed by the -// segment header. -// -// The segments are placed in a particular free list depending on how many blocks they have free; -// this allows allocators to simply skip the segments which don't have enough segments free, and -// allocate new segments if necessary. The free list does not however guarantee that a particular -// segment has a particular number of contiguous blocks free; low performance can still occur when -// there is fragmentation. - -/** The number of 32-bit integers used for each allocation bitmap. */ -#define PH_FP_BITMAP_SIZE 64 -/** The power-of-two index of the bitmap size. */ -#define PH_FP_BITMAP_SIZE_SHIFT 6 -/** The number of blocks that are available in each segment. */ -#define PH_FP_BLOCK_COUNT (PH_FP_BITMAP_SIZE * 32) -/** The power-of-two index of the block count. */ -#define PH_FP_BLOCK_COUNT_SHIFT (PH_FP_BITMAP_SIZE_SHIFT + 5) -/** The number of free lists for segments. */ -#define PH_FP_FREE_LIST_COUNT 8 - -// Block flags -/** The block is the beginning of a large allocation (one that spans several segments). */ -#define PH_FP_BLOCK_LARGE_ALLOCATION 0x1 - -typedef struct _PH_FP_BLOCK_HEADER -{ - ULONG Flags; // PH_FP_BLOCK_* - /** The number of blocks in the entire logical block, or the number - * of segments in a large allocation. */ - ULONG Span; - ULONGLONG Body; -} PH_FP_BLOCK_HEADER, *PPH_FP_BLOCK_HEADER; - -typedef struct _PH_FP_SEGMENT_HEADER -{ - ULONG Bitmap[PH_FP_BITMAP_SIZE]; - ULONG FreeBlocks; - ULONG FreeFlink; - ULONG FreeBlink; - ULONG Reserved[13]; -} PH_FP_SEGMENT_HEADER, *PPH_FP_SEGMENT_HEADER; - -#define PH_FP_MAGIC ('loPF') - -typedef struct _PH_FP_FILE_HEADER -{ - ULONG Magic; - ULONG SegmentShift; - ULONG SegmentCount; - ULONGLONG UserContext; - ULONG FreeLists[PH_FP_FREE_LIST_COUNT]; -} PH_FP_FILE_HEADER, *PPH_FP_FILE_HEADER; - -// Runtime - -typedef struct _PH_FILE_POOL_PARAMETERS -{ - // File options - - /** - * The base-2 logarithm of the size of each segment. This value must be between 16 and 28, - * inclusive. - */ - ULONG SegmentShift; - - // Runtime options - - /** The maximum number of inactive segments to keep mapped. */ - ULONG MaximumInactiveViews; -} PH_FILE_POOL_PARAMETERS, *PPH_FILE_POOL_PARAMETERS; - -typedef struct _PH_FILE_POOL -{ - HANDLE FileHandle; - HANDLE SectionHandle; - BOOLEAN ReadOnly; - - PH_FREE_LIST ViewFreeList; - PLIST_ENTRY *ByIndexBuckets; - ULONG ByIndexSize; - PH_AVL_TREE ByBaseSet; - - ULONG MaximumInactiveViews; - ULONG NumberOfInactiveViews; - LIST_ENTRY InactiveViewsListHead; - - PPH_FP_BLOCK_HEADER FirstBlockOfFirstSegment; - PPH_FP_FILE_HEADER Header; - ULONG SegmentShift; // The power-of-two size of each segment - ULONG SegmentSize; // The size of each segment - ULONG BlockShift; // The power-of-two size of each block in each segment - ULONG BlockSize; // The size of each block in each segment - ULONG FileHeaderBlockSpan; // The number of blocks needed to store a file header - ULONG SegmentHeaderBlockSpan; // The number of blocks needed to store a segment header -} PH_FILE_POOL, *PPH_FILE_POOL; - -PHLIBAPI -NTSTATUS PhCreateFilePool( - _Out_ PPH_FILE_POOL *Pool, - _In_ HANDLE FileHandle, - _In_ BOOLEAN ReadOnly, - _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters - ); - -PHLIBAPI -NTSTATUS PhCreateFilePool2( - _Out_ PPH_FILE_POOL *Pool, - _In_ PWSTR FileName, - _In_ BOOLEAN ReadOnly, - _In_ ULONG ShareAccess, - _In_ ULONG CreateDisposition, - _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters - ); - -PHLIBAPI -VOID PhDestroyFilePool( - _In_ _Post_invalid_ PPH_FILE_POOL Pool - ); - -PHLIBAPI -PVOID PhAllocateFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Size, - _Out_opt_ PULONG Rva - ); - -PHLIBAPI -VOID PhFreeFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Block - ); - -PHLIBAPI -BOOLEAN PhFreeFilePoolByRva( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Rva - ); - -PHLIBAPI -VOID PhReferenceFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Address - ); - -PHLIBAPI -VOID PhDereferenceFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Address - ); - -PHLIBAPI -PVOID PhReferenceFilePoolByRva( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Rva - ); - -PHLIBAPI -BOOLEAN PhDereferenceFilePoolByRva( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Rva - ); - -PHLIBAPI -ULONG PhEncodeRvaFilePool( - _In_ PPH_FILE_POOL Pool, - _In_ PVOID Address - ); - -PHLIBAPI -VOID PhGetUserContextFilePool( - _In_ PPH_FILE_POOL Pool, - _Out_ PULONGLONG Context - ); - -PHLIBAPI -VOID PhSetUserContextFilePool( - _Inout_ PPH_FILE_POOL Pool, - _In_ PULONGLONG Context - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_FILEPOOL_H +#define _PH_FILEPOOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +// On-disk structures + +// Each file has at least one segment. Each segment has a number of blocks, which are allocated from +// a bitmap. The segment header is always in the first block of each segment, except for the first +// segment. In the first segment, the file header is in the first few blocks, followed by the +// segment header. +// +// The segments are placed in a particular free list depending on how many blocks they have free; +// this allows allocators to simply skip the segments which don't have enough segments free, and +// allocate new segments if necessary. The free list does not however guarantee that a particular +// segment has a particular number of contiguous blocks free; low performance can still occur when +// there is fragmentation. + +/** The number of 32-bit integers used for each allocation bitmap. */ +#define PH_FP_BITMAP_SIZE 64 +/** The power-of-two index of the bitmap size. */ +#define PH_FP_BITMAP_SIZE_SHIFT 6 +/** The number of blocks that are available in each segment. */ +#define PH_FP_BLOCK_COUNT (PH_FP_BITMAP_SIZE * 32) +/** The power-of-two index of the block count. */ +#define PH_FP_BLOCK_COUNT_SHIFT (PH_FP_BITMAP_SIZE_SHIFT + 5) +/** The number of free lists for segments. */ +#define PH_FP_FREE_LIST_COUNT 8 + +// Block flags +/** The block is the beginning of a large allocation (one that spans several segments). */ +#define PH_FP_BLOCK_LARGE_ALLOCATION 0x1 + +typedef struct _PH_FP_BLOCK_HEADER +{ + ULONG Flags; // PH_FP_BLOCK_* + /** The number of blocks in the entire logical block, or the number + * of segments in a large allocation. */ + ULONG Span; + ULONGLONG Body; +} PH_FP_BLOCK_HEADER, *PPH_FP_BLOCK_HEADER; + +typedef struct _PH_FP_SEGMENT_HEADER +{ + ULONG Bitmap[PH_FP_BITMAP_SIZE]; + ULONG FreeBlocks; + ULONG FreeFlink; + ULONG FreeBlink; + ULONG Reserved[13]; +} PH_FP_SEGMENT_HEADER, *PPH_FP_SEGMENT_HEADER; + +#define PH_FP_MAGIC ('loPF') + +typedef struct _PH_FP_FILE_HEADER +{ + ULONG Magic; + ULONG SegmentShift; + ULONG SegmentCount; + ULONGLONG UserContext; + ULONG FreeLists[PH_FP_FREE_LIST_COUNT]; +} PH_FP_FILE_HEADER, *PPH_FP_FILE_HEADER; + +// Runtime + +typedef struct _PH_FILE_POOL_PARAMETERS +{ + // File options + + /** + * The base-2 logarithm of the size of each segment. This value must be between 16 and 28, + * inclusive. + */ + ULONG SegmentShift; + + // Runtime options + + /** The maximum number of inactive segments to keep mapped. */ + ULONG MaximumInactiveViews; +} PH_FILE_POOL_PARAMETERS, *PPH_FILE_POOL_PARAMETERS; + +typedef struct _PH_FILE_POOL +{ + HANDLE FileHandle; + HANDLE SectionHandle; + BOOLEAN ReadOnly; + + PH_FREE_LIST ViewFreeList; + PLIST_ENTRY *ByIndexBuckets; + ULONG ByIndexSize; + PH_AVL_TREE ByBaseSet; + + ULONG MaximumInactiveViews; + ULONG NumberOfInactiveViews; + LIST_ENTRY InactiveViewsListHead; + + PPH_FP_BLOCK_HEADER FirstBlockOfFirstSegment; + PPH_FP_FILE_HEADER Header; + ULONG SegmentShift; // The power-of-two size of each segment + ULONG SegmentSize; // The size of each segment + ULONG BlockShift; // The power-of-two size of each block in each segment + ULONG BlockSize; // The size of each block in each segment + ULONG FileHeaderBlockSpan; // The number of blocks needed to store a file header + ULONG SegmentHeaderBlockSpan; // The number of blocks needed to store a segment header +} PH_FILE_POOL, *PPH_FILE_POOL; + +PHLIBAPI +NTSTATUS PhCreateFilePool( + _Out_ PPH_FILE_POOL *Pool, + _In_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters + ); + +PHLIBAPI +NTSTATUS PhCreateFilePool2( + _Out_ PPH_FILE_POOL *Pool, + _In_ PWSTR FileName, + _In_ BOOLEAN ReadOnly, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters + ); + +PHLIBAPI +VOID PhDestroyFilePool( + _In_ _Post_invalid_ PPH_FILE_POOL Pool + ); + +PHLIBAPI +PVOID PhAllocateFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Size, + _Out_opt_ PULONG Rva + ); + +PHLIBAPI +VOID PhFreeFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Block + ); + +PHLIBAPI +BOOLEAN PhFreeFilePoolByRva( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Rva + ); + +PHLIBAPI +VOID PhReferenceFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Address + ); + +PHLIBAPI +VOID PhDereferenceFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Address + ); + +PHLIBAPI +PVOID PhReferenceFilePoolByRva( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Rva + ); + +PHLIBAPI +BOOLEAN PhDereferenceFilePoolByRva( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Rva + ); + +PHLIBAPI +ULONG PhEncodeRvaFilePool( + _In_ PPH_FILE_POOL Pool, + _In_ PVOID Address + ); + +PHLIBAPI +VOID PhGetUserContextFilePool( + _In_ PPH_FILE_POOL Pool, + _Out_ PULONGLONG Context + ); + +PHLIBAPI +VOID PhSetUserContextFilePool( + _Inout_ PPH_FILE_POOL Pool, + _In_ PULONGLONG Context + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/filepoolp.h b/phlib/include/filepoolp.h index 0a823ea60e41..f11cc0f902f0 100644 --- a/phlib/include/filepoolp.h +++ b/phlib/include/filepoolp.h @@ -1,204 +1,204 @@ -#ifndef _PH_FILEPOOLP_H -#define _PH_FILEPOOLP_H - -typedef struct _PH_FILE_POOL_VIEW -{ - LIST_ENTRY ByIndexListEntry; - PH_AVL_LINKS ByBaseLinks; - LIST_ENTRY InactiveViewsListEntry; - - ULONG RefCount; - ULONG SegmentIndex; - PVOID Base; -} PH_FILE_POOL_VIEW, *PPH_FILE_POOL_VIEW; - -NTSTATUS PhpValidateFilePoolParameters( - _Inout_ PPH_FILE_POOL_PARAMETERS Parameters - ); - -VOID PhpSetDefaultFilePoolParameters( - _Out_ PPH_FILE_POOL_PARAMETERS Parameters - ); - -// Range mapping - -NTSTATUS PhFppExtendRange( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG NewSize - ); - -NTSTATUS PhFppMapRange( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG Offset, - _In_ ULONG Size, - _Out_ PVOID *Base - ); - -NTSTATUS PhFppUnmapRange( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ); - -// Segments - -VOID PhFppInitializeSegment( - _Inout_ PPH_FILE_POOL Pool, - _Out_ PPH_FP_BLOCK_HEADER BlockOfSegmentHeader, - _In_ ULONG AdditionalBlocksUsed - ); - -PPH_FP_BLOCK_HEADER PhFppAllocateSegment( - _Inout_ PPH_FILE_POOL Pool, - _Out_ PULONG NewSegmentIndex - ); - -PPH_FP_SEGMENT_HEADER PhFppGetHeaderSegment( - _Inout_ PPH_FILE_POOL Pool, - _In_ PPH_FP_BLOCK_HEADER FirstBlock - ); - -// Views - -VOID PhFppAddViewByIndex( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -VOID PhFppRemoveViewByIndex( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -PPH_FILE_POOL_VIEW PhFppFindViewByIndex( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ); - -LONG NTAPI PhpFilePoolViewByBaseCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ); - -VOID PhFppAddViewByBase( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -VOID PhFppRemoveViewByBase( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -PPH_FILE_POOL_VIEW PhFppFindViewByBase( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ); - -PPH_FILE_POOL_VIEW PhFppCreateView( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ); - -VOID PhFppDestroyView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -VOID PhFppActivateView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -VOID PhFppDeactivateView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -VOID PhFppReferenceView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -VOID PhFppDereferenceView( - _Inout_ PPH_FILE_POOL Pool, - _Inout_ PPH_FILE_POOL_VIEW View - ); - -PPH_FP_BLOCK_HEADER PhFppReferenceSegment( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ); - -VOID PhFppDereferenceSegment( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex - ); - -VOID PhFppReferenceSegmentByBase( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ); - -VOID PhFppDereferenceSegmentByBase( - _Inout_ PPH_FILE_POOL Pool, - _In_ PVOID Base - ); - -// Bitmap allocation - -PPH_FP_BLOCK_HEADER PhFppAllocateBlocks( - _Inout_ PPH_FILE_POOL Pool, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, - _In_ ULONG NumberOfBlocks - ); - -VOID PhFppFreeBlocks( - _Inout_ PPH_FILE_POOL Pool, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, - _In_ PPH_FP_BLOCK_HEADER BlockHeader - ); - -// Free list - -ULONG PhFppComputeFreeListIndex( - _In_ PPH_FILE_POOL Pool, - _In_ ULONG NumberOfBlocks - ); - -BOOLEAN PhFppInsertFreeList( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG FreeListIndex, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_SEGMENT_HEADER SegmentHeader - ); - -BOOLEAN PhFppRemoveFreeList( - _Inout_ PPH_FILE_POOL Pool, - _In_ ULONG FreeListIndex, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_SEGMENT_HEADER SegmentHeader - ); - -// Misc. - -PPH_FP_BLOCK_HEADER PhFppGetHeaderBlock( - _In_ PPH_FILE_POOL Pool, - _In_ PVOID Block - ); - -ULONG PhFppEncodeRva( - _In_ PPH_FILE_POOL Pool, - _In_ ULONG SegmentIndex, - _In_ PPH_FP_BLOCK_HEADER FirstBlock, - _In_ PVOID Address - ); - -ULONG PhFppDecodeRva( - _In_ PPH_FILE_POOL Pool, - _In_ ULONG Rva, - _Out_ PULONG SegmentIndex - ); - -#endif +#ifndef _PH_FILEPOOLP_H +#define _PH_FILEPOOLP_H + +typedef struct _PH_FILE_POOL_VIEW +{ + LIST_ENTRY ByIndexListEntry; + PH_AVL_LINKS ByBaseLinks; + LIST_ENTRY InactiveViewsListEntry; + + ULONG RefCount; + ULONG SegmentIndex; + PVOID Base; +} PH_FILE_POOL_VIEW, *PPH_FILE_POOL_VIEW; + +NTSTATUS PhpValidateFilePoolParameters( + _Inout_ PPH_FILE_POOL_PARAMETERS Parameters + ); + +VOID PhpSetDefaultFilePoolParameters( + _Out_ PPH_FILE_POOL_PARAMETERS Parameters + ); + +// Range mapping + +NTSTATUS PhFppExtendRange( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG NewSize + ); + +NTSTATUS PhFppMapRange( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG Offset, + _In_ ULONG Size, + _Out_ PVOID *Base + ); + +NTSTATUS PhFppUnmapRange( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ); + +// Segments + +VOID PhFppInitializeSegment( + _Inout_ PPH_FILE_POOL Pool, + _Out_ PPH_FP_BLOCK_HEADER BlockOfSegmentHeader, + _In_ ULONG AdditionalBlocksUsed + ); + +PPH_FP_BLOCK_HEADER PhFppAllocateSegment( + _Inout_ PPH_FILE_POOL Pool, + _Out_ PULONG NewSegmentIndex + ); + +PPH_FP_SEGMENT_HEADER PhFppGetHeaderSegment( + _Inout_ PPH_FILE_POOL Pool, + _In_ PPH_FP_BLOCK_HEADER FirstBlock + ); + +// Views + +VOID PhFppAddViewByIndex( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +VOID PhFppRemoveViewByIndex( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +PPH_FILE_POOL_VIEW PhFppFindViewByIndex( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ); + +LONG NTAPI PhpFilePoolViewByBaseCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ); + +VOID PhFppAddViewByBase( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +VOID PhFppRemoveViewByBase( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +PPH_FILE_POOL_VIEW PhFppFindViewByBase( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ); + +PPH_FILE_POOL_VIEW PhFppCreateView( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ); + +VOID PhFppDestroyView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +VOID PhFppActivateView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +VOID PhFppDeactivateView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +VOID PhFppReferenceView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +VOID PhFppDereferenceView( + _Inout_ PPH_FILE_POOL Pool, + _Inout_ PPH_FILE_POOL_VIEW View + ); + +PPH_FP_BLOCK_HEADER PhFppReferenceSegment( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ); + +VOID PhFppDereferenceSegment( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex + ); + +VOID PhFppReferenceSegmentByBase( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ); + +VOID PhFppDereferenceSegmentByBase( + _Inout_ PPH_FILE_POOL Pool, + _In_ PVOID Base + ); + +// Bitmap allocation + +PPH_FP_BLOCK_HEADER PhFppAllocateBlocks( + _Inout_ PPH_FILE_POOL Pool, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, + _In_ ULONG NumberOfBlocks + ); + +VOID PhFppFreeBlocks( + _Inout_ PPH_FILE_POOL Pool, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _Inout_ PPH_FP_SEGMENT_HEADER SegmentHeader, + _In_ PPH_FP_BLOCK_HEADER BlockHeader + ); + +// Free list + +ULONG PhFppComputeFreeListIndex( + _In_ PPH_FILE_POOL Pool, + _In_ ULONG NumberOfBlocks + ); + +BOOLEAN PhFppInsertFreeList( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG FreeListIndex, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_SEGMENT_HEADER SegmentHeader + ); + +BOOLEAN PhFppRemoveFreeList( + _Inout_ PPH_FILE_POOL Pool, + _In_ ULONG FreeListIndex, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_SEGMENT_HEADER SegmentHeader + ); + +// Misc. + +PPH_FP_BLOCK_HEADER PhFppGetHeaderBlock( + _In_ PPH_FILE_POOL Pool, + _In_ PVOID Block + ); + +ULONG PhFppEncodeRva( + _In_ PPH_FILE_POOL Pool, + _In_ ULONG SegmentIndex, + _In_ PPH_FP_BLOCK_HEADER FirstBlock, + _In_ PVOID Address + ); + +ULONG PhFppDecodeRva( + _In_ PPH_FILE_POOL Pool, + _In_ ULONG Rva, + _Out_ PULONG SegmentIndex + ); + +#endif diff --git a/phlib/include/filestream.h b/phlib/include/filestream.h index ec4a2077bc47..769f2c0f280f 100644 --- a/phlib/include/filestream.h +++ b/phlib/include/filestream.h @@ -55,12 +55,6 @@ typedef struct _PH_FILE_STREAM extern PPH_OBJECT_TYPE PhFileStreamType; -BOOLEAN -NTAPI -PhFileStreamInitialization( - VOID - ); - PHLIBAPI NTSTATUS NTAPI diff --git a/phlib/include/graph.h b/phlib/include/graph.h index 87dc2209483e..2b10225c5954 100644 --- a/phlib/include/graph.h +++ b/phlib/include/graph.h @@ -1,255 +1,264 @@ -#ifndef _PH_GRAPH_H -#define _PH_GRAPH_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Graph drawing - -extern RECT PhNormalGraphTextMargin; -extern RECT PhNormalGraphTextPadding; - -#define PH_GRAPH_USE_GRID_X 0x1 -#define PH_GRAPH_USE_GRID_Y 0x2 -#define PH_GRAPH_LOGARITHMIC_GRID_Y 0x4 -#define PH_GRAPH_USE_LINE_2 0x10 -#define PH_GRAPH_OVERLAY_LINE_2 0x20 -#define PH_GRAPH_LABEL_MAX_Y 0x1000 - -typedef PPH_STRING (NTAPI *PPH_GRAPH_LABEL_Y_FUNCTION)( - _In_ struct _PH_GRAPH_DRAW_INFO *DrawInfo, - _In_ ULONG DataIndex, - _In_ FLOAT Value, - _In_ FLOAT Parameter - ); - -typedef struct _PH_GRAPH_DRAW_INFO -{ - // Basic - ULONG Width; - ULONG Height; - ULONG Flags; - ULONG Step; - COLORREF BackColor; - - // Data/lines - ULONG LineDataCount; - PFLOAT LineData1; - PFLOAT LineData2; - COLORREF LineColor1; - COLORREF LineColor2; - COLORREF LineBackColor1; - COLORREF LineBackColor2; - - // Grid - COLORREF GridColor; - ULONG GridWidth; - FLOAT GridHeight; - ULONG GridXOffset; - ULONG GridYThreshold; - FLOAT GridBase; // Base for logarithmic grid - - // y-axis label - PPH_GRAPH_LABEL_Y_FUNCTION LabelYFunction; - FLOAT LabelYFunctionParameter; - HFONT LabelYFont; - COLORREF LabelYColor; - ULONG LabelMaxYIndexLimit; - - // Text - PH_STRINGREF Text; - RECT TextRect; - RECT TextBoxRect; - HFONT TextFont; - COLORREF TextColor; - COLORREF TextBoxColor; -} PH_GRAPH_DRAW_INFO, *PPH_GRAPH_DRAW_INFO; - -// Graph control - -#define PH_GRAPH_CLASSNAME L"PhGraph" - -PHLIBAPI -BOOLEAN PhGraphControlInitialization( - VOID - ); - -PHLIBAPI -VOID PhDrawGraphDirect( - _In_ HDC hdc, - _In_ PVOID Bits, - _In_ PPH_GRAPH_DRAW_INFO DrawInfo - ); - -PHLIBAPI -VOID PhSetGraphText( - _In_ HDC hdc, - _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ PPH_STRINGREF Text, - _In_ PRECT Margin, - _In_ PRECT Padding, - _In_ ULONG Align - ); - -// Configuration - -typedef struct _PH_GRAPH_OPTIONS -{ - COLORREF FadeOutBackColor; - ULONG FadeOutWidth; - HCURSOR DefaultCursor; -} PH_GRAPH_OPTIONS, *PPH_GRAPH_OPTIONS; - -// Styles - -#define GC_STYLE_FADEOUT 0x1 -#define GC_STYLE_DRAW_PANEL 0x2 - -// Messages - -#define GCM_GETDRAWINFO (WM_USER + 1301) -#define GCM_SETDRAWINFO (WM_USER + 1302) -#define GCM_DRAW (WM_USER + 1303) -#define GCM_MOVEGRID (WM_USER + 1304) -#define GCM_GETBUFFEREDCONTEXT (WM_USER + 1305) -#define GCM_SETTOOLTIP (WM_USER + 1306) -#define GCM_UPDATETOOLTIP (WM_USER + 1307) -#define GCM_GETOPTIONS (WM_USER + 1308) -#define GCM_SETOPTIONS (WM_USER + 1309) - -#define Graph_GetDrawInfo(hWnd, DrawInfo) \ - SendMessage((hWnd), GCM_GETDRAWINFO, 0, (LPARAM)(DrawInfo)) -#define Graph_SetDrawInfo(hWnd, DrawInfo) \ - SendMessage((hWnd), GCM_SETDRAWINFO, 0, (LPARAM)(DrawInfo)) -#define Graph_Draw(hWnd) \ - SendMessage((hWnd), GCM_DRAW, 0, 0) -#define Graph_MoveGrid(hWnd, Increment) \ - SendMessage((hWnd), GCM_MOVEGRID, (WPARAM)(Increment), 0) -#define Graph_GetBufferedContext(hWnd) \ - ((HDC)SendMessage((hWnd), GCM_GETBUFFEREDCONTEXT, 0, 0)) -#define Graph_SetTooltip(hWnd, Enable) \ - ((HDC)SendMessage((hWnd), GCM_SETTOOLTIP, (WPARAM)(Enable), 0)) -#define Graph_UpdateTooltip(hWnd) \ - ((HDC)SendMessage((hWnd), GCM_UPDATETOOLTIP, 0, 0)) -#define Graph_GetOptions(hWnd, Options) \ - SendMessage((hWnd), GCM_GETOPTIONS, 0, (LPARAM)(Options)) -#define Graph_SetOptions(hWnd, Options) \ - SendMessage((hWnd), GCM_SETOPTIONS, 0, (LPARAM)(Options)) - -// Notifications - -#define GCN_GETDRAWINFO (WM_USER + 1351) -#define GCN_GETTOOLTIPTEXT (WM_USER + 1352) -#define GCN_MOUSEEVENT (WM_USER + 1353) -#define GCN_DRAWPANEL (WM_USER + 1354) - -typedef struct _PH_GRAPH_GETDRAWINFO -{ - NMHDR Header; - PPH_GRAPH_DRAW_INFO DrawInfo; -} PH_GRAPH_GETDRAWINFO, *PPH_GRAPH_GETDRAWINFO; - -typedef struct _PH_GRAPH_GETTOOLTIPTEXT -{ - NMHDR Header; - ULONG Index; - ULONG TotalCount; - - PH_STRINGREF Text; // must be null-terminated -} PH_GRAPH_GETTOOLTIPTEXT, *PPH_GRAPH_GETTOOLTIPTEXT; - -typedef struct _PH_GRAPH_MOUSEEVENT -{ - NMHDR Header; - ULONG Index; - ULONG TotalCount; - - ULONG Message; - ULONG Keys; - POINT Point; -} PH_GRAPH_MOUSEEVENT, *PPH_GRAPH_MOUSEEVENT; - -typedef struct _PH_GRAPH_DRAWPANEL -{ - NMHDR Header; - HDC hdc; - RECT Rect; -} PH_GRAPH_DRAWPANEL, *PPH_GRAPH_DRAWPANEL; - -// Graph buffer management - -#define PH_GRAPH_DATA_COUNT(Width, Step) (((Width) + (Step) - 1) / (Step) + 1) // round up in division - -typedef struct _PH_GRAPH_BUFFERS -{ - PFLOAT Data1; // invalidate by setting Valid to FALSE - PFLOAT Data2; // invalidate by setting Valid to FALSE - ULONG AllocatedCount; - BOOLEAN Valid; // indicates the data is valid -} PH_GRAPH_BUFFERS, *PPH_GRAPH_BUFFERS; - -PHLIBAPI -VOID PhInitializeGraphBuffers( - _Out_ PPH_GRAPH_BUFFERS Buffers - ); - -PHLIBAPI -VOID PhDeleteGraphBuffers( - _Inout_ PPH_GRAPH_BUFFERS Buffers - ); - -PHLIBAPI -VOID PhGetDrawInfoGraphBuffers( - _Inout_ PPH_GRAPH_BUFFERS Buffers, - _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, - _In_ ULONG DataCount - ); - -// Graph control state - -// The basic buffer management structure was moved out of this section because -// the text management is not needed for most cases. - -typedef struct _PH_GRAPH_STATE -{ - // Union for compatibility - union - { - struct - { - PFLOAT Data1; // invalidate by setting Valid to FALSE - PFLOAT Data2; // invalidate by setting Valid to FALSE - ULONG AllocatedCount; - BOOLEAN Valid; // indicates the data is valid - }; - PH_GRAPH_BUFFERS Buffers; - }; - - PPH_STRING Text; - PPH_STRING TooltipText; // invalidate by setting TooltipIndex to -1 - ULONG TooltipIndex; // indicates the tooltip text is valid for this index -} PH_GRAPH_STATE, *PPH_GRAPH_STATE; - -PHLIBAPI -VOID PhInitializeGraphState( - _Out_ PPH_GRAPH_STATE State - ); - -PHLIBAPI -VOID PhDeleteGraphState( - _Inout_ PPH_GRAPH_STATE State - ); - -PHLIBAPI -VOID PhGraphStateGetDrawInfo( - _Inout_ PPH_GRAPH_STATE State, - _In_ PPH_GRAPH_GETDRAWINFO GetDrawInfo, - _In_ ULONG DataCount - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_GRAPH_H +#define _PH_GRAPH_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Graph drawing + +extern RECT PhNormalGraphTextMargin; +extern RECT PhNormalGraphTextPadding; + +#define PH_GRAPH_USE_GRID_X 0x1 +#define PH_GRAPH_USE_GRID_Y 0x2 +#define PH_GRAPH_LOGARITHMIC_GRID_Y 0x4 +#define PH_GRAPH_USE_LINE_2 0x10 +#define PH_GRAPH_OVERLAY_LINE_2 0x20 +#define PH_GRAPH_LABEL_MAX_Y 0x1000 + +typedef PPH_STRING (NTAPI *PPH_GRAPH_LABEL_Y_FUNCTION)( + _In_ struct _PH_GRAPH_DRAW_INFO *DrawInfo, + _In_ ULONG DataIndex, + _In_ FLOAT Value, + _In_ FLOAT Parameter + ); + +typedef struct _PH_GRAPH_DRAW_INFO +{ + // Basic + ULONG Width; + ULONG Height; + ULONG Flags; + ULONG Step; + COLORREF BackColor; + + // Data/lines + ULONG LineDataCount; + PFLOAT LineData1; + PFLOAT LineData2; + COLORREF LineColor1; + COLORREF LineColor2; + COLORREF LineBackColor1; + COLORREF LineBackColor2; + + // Grid + COLORREF GridColor; + ULONG GridWidth; + FLOAT GridHeight; + ULONG GridXOffset; + ULONG GridYThreshold; + FLOAT GridBase; // Base for logarithmic grid + + // y-axis label + PPH_GRAPH_LABEL_Y_FUNCTION LabelYFunction; + FLOAT LabelYFunctionParameter; + HFONT LabelYFont; + COLORREF LabelYColor; + ULONG LabelMaxYIndexLimit; + + // Text + PH_STRINGREF Text; + RECT TextRect; + RECT TextBoxRect; + HFONT TextFont; + COLORREF TextColor; + COLORREF TextBoxColor; +} PH_GRAPH_DRAW_INFO, *PPH_GRAPH_DRAW_INFO; + +// Graph control + +#define PH_GRAPH_CLASSNAME L"PhGraph" + +PHLIBAPI +BOOLEAN PhGraphControlInitialization( + VOID + ); + +PHLIBAPI +VOID PhDrawGraphDirect( + _In_ HDC hdc, + _In_ PVOID Bits, + _In_ PPH_GRAPH_DRAW_INFO DrawInfo + ); + +PHLIBAPI +VOID PhSetGraphText( + _In_ HDC hdc, + _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ PPH_STRINGREF Text, + _In_ PRECT Margin, + _In_ PRECT Padding, + _In_ ULONG Align + ); + +PHLIBAPI +VOID PhDrawTrayIconText( + _In_ HDC hdc, + _In_ PVOID Bits, + _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ PPH_STRINGREF Text + ); + + +// Configuration + +typedef struct _PH_GRAPH_OPTIONS +{ + COLORREF FadeOutBackColor; + ULONG FadeOutWidth; + HCURSOR DefaultCursor; +} PH_GRAPH_OPTIONS, *PPH_GRAPH_OPTIONS; + +// Styles + +#define GC_STYLE_FADEOUT 0x1 +#define GC_STYLE_DRAW_PANEL 0x2 + +// Messages + +#define GCM_GETDRAWINFO (WM_USER + 1301) +#define GCM_SETDRAWINFO (WM_USER + 1302) +#define GCM_DRAW (WM_USER + 1303) +#define GCM_MOVEGRID (WM_USER + 1304) +#define GCM_GETBUFFEREDCONTEXT (WM_USER + 1305) +#define GCM_SETTOOLTIP (WM_USER + 1306) +#define GCM_UPDATETOOLTIP (WM_USER + 1307) +#define GCM_GETOPTIONS (WM_USER + 1308) +#define GCM_SETOPTIONS (WM_USER + 1309) + +#define Graph_GetDrawInfo(hWnd, DrawInfo) \ + SendMessage((hWnd), GCM_GETDRAWINFO, 0, (LPARAM)(DrawInfo)) +#define Graph_SetDrawInfo(hWnd, DrawInfo) \ + SendMessage((hWnd), GCM_SETDRAWINFO, 0, (LPARAM)(DrawInfo)) +#define Graph_Draw(hWnd) \ + SendMessage((hWnd), GCM_DRAW, 0, 0) +#define Graph_MoveGrid(hWnd, Increment) \ + SendMessage((hWnd), GCM_MOVEGRID, (WPARAM)(Increment), 0) +#define Graph_GetBufferedContext(hWnd) \ + ((HDC)SendMessage((hWnd), GCM_GETBUFFEREDCONTEXT, 0, 0)) +#define Graph_SetTooltip(hWnd, Enable) \ + ((HDC)SendMessage((hWnd), GCM_SETTOOLTIP, (WPARAM)(Enable), 0)) +#define Graph_UpdateTooltip(hWnd) \ + ((HDC)SendMessage((hWnd), GCM_UPDATETOOLTIP, 0, 0)) +#define Graph_GetOptions(hWnd, Options) \ + SendMessage((hWnd), GCM_GETOPTIONS, 0, (LPARAM)(Options)) +#define Graph_SetOptions(hWnd, Options) \ + SendMessage((hWnd), GCM_SETOPTIONS, 0, (LPARAM)(Options)) + +// Notifications + +#define GCN_GETDRAWINFO (WM_USER + 1351) +#define GCN_GETTOOLTIPTEXT (WM_USER + 1352) +#define GCN_MOUSEEVENT (WM_USER + 1353) +#define GCN_DRAWPANEL (WM_USER + 1354) + +typedef struct _PH_GRAPH_GETDRAWINFO +{ + NMHDR Header; + PPH_GRAPH_DRAW_INFO DrawInfo; +} PH_GRAPH_GETDRAWINFO, *PPH_GRAPH_GETDRAWINFO; + +typedef struct _PH_GRAPH_GETTOOLTIPTEXT +{ + NMHDR Header; + ULONG Index; + ULONG TotalCount; + + PH_STRINGREF Text; // must be null-terminated +} PH_GRAPH_GETTOOLTIPTEXT, *PPH_GRAPH_GETTOOLTIPTEXT; + +typedef struct _PH_GRAPH_MOUSEEVENT +{ + NMHDR Header; + ULONG Index; + ULONG TotalCount; + + ULONG Message; + ULONG Keys; + POINT Point; +} PH_GRAPH_MOUSEEVENT, *PPH_GRAPH_MOUSEEVENT; + +typedef struct _PH_GRAPH_DRAWPANEL +{ + NMHDR Header; + HDC hdc; + RECT Rect; +} PH_GRAPH_DRAWPANEL, *PPH_GRAPH_DRAWPANEL; + +// Graph buffer management + +#define PH_GRAPH_DATA_COUNT(Width, Step) (((Width) + (Step) - 1) / (Step) + 1) // round up in division + +typedef struct _PH_GRAPH_BUFFERS +{ + PFLOAT Data1; // invalidate by setting Valid to FALSE + PFLOAT Data2; // invalidate by setting Valid to FALSE + ULONG AllocatedCount; + BOOLEAN Valid; // indicates the data is valid +} PH_GRAPH_BUFFERS, *PPH_GRAPH_BUFFERS; + +PHLIBAPI +VOID PhInitializeGraphBuffers( + _Out_ PPH_GRAPH_BUFFERS Buffers + ); + +PHLIBAPI +VOID PhDeleteGraphBuffers( + _Inout_ PPH_GRAPH_BUFFERS Buffers + ); + +PHLIBAPI +VOID PhGetDrawInfoGraphBuffers( + _Inout_ PPH_GRAPH_BUFFERS Buffers, + _Inout_ PPH_GRAPH_DRAW_INFO DrawInfo, + _In_ ULONG DataCount + ); + +// Graph control state + +// The basic buffer management structure was moved out of this section because +// the text management is not needed for most cases. + +typedef struct _PH_GRAPH_STATE +{ + // Union for compatibility + union + { + struct + { + PFLOAT Data1; // invalidate by setting Valid to FALSE + PFLOAT Data2; // invalidate by setting Valid to FALSE + ULONG AllocatedCount; + BOOLEAN Valid; // indicates the data is valid + }; + PH_GRAPH_BUFFERS Buffers; + }; + + PPH_STRING Text; + PPH_STRING TooltipText; // invalidate by setting TooltipIndex to -1 + ULONG TooltipIndex; // indicates the tooltip text is valid for this index +} PH_GRAPH_STATE, *PPH_GRAPH_STATE; + +PHLIBAPI +VOID PhInitializeGraphState( + _Out_ PPH_GRAPH_STATE State + ); + +PHLIBAPI +VOID PhDeleteGraphState( + _Inout_ PPH_GRAPH_STATE State + ); + +PHLIBAPI +VOID PhGraphStateGetDrawInfo( + _Inout_ PPH_GRAPH_STATE State, + _In_ PPH_GRAPH_GETDRAWINFO GetDrawInfo, + _In_ ULONG DataCount + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/guisup.h b/phlib/include/guisup.h index b1fe736ed688..fab1134fe434 100644 --- a/phlib/include/guisup.h +++ b/phlib/include/guisup.h @@ -11,29 +11,22 @@ extern "C" { // guisup -typedef BOOL (WINAPI *_ChangeWindowMessageFilter)( - _In_ UINT message, - _In_ DWORD dwFlag - ); - -typedef BOOL (WINAPI *_IsImmersiveProcess)( - _In_ HANDLE hProcess - ); - #define RFF_NOBROWSE 0x0001 #define RFF_NODEFAULT 0x0002 #define RFF_CALCDIRECTORY 0x0004 #define RFF_NOLABEL 0x0008 #define RFF_NOSEPARATEMEM 0x0020 +#define RFF_OPTRUNAS 0x0040 #define RFN_VALIDATE (-510) +#define RFN_LIMITEDRUNAS (-511) typedef struct _NMRUNFILEDLGW { NMHDR hdr; - LPCWSTR lpszFile; - LPCWSTR lpszDirectory; - UINT nShow; + PWSTR lpszFile; + PWSTR lpszDirectory; + UINT ShowCmd; } NMRUNFILEDLGW, *LPNMRUNFILEDLGW, *PNMRUNFILEDLGW; typedef NMRUNFILEDLGW NMRUNFILEDLG; @@ -46,24 +39,8 @@ typedef LPNMRUNFILEDLGW LPNMRUNFILEDLG; typedef HANDLE HTHEME; -typedef BOOL (WINAPI *_RunFileDlg)( - _In_ HWND hwndOwner, - _In_opt_ HICON hIcon, - _In_opt_ LPCWSTR lpszDirectory, - _In_opt_ LPCWSTR lpszTitle, - _In_opt_ LPCWSTR lpszDescription, - _In_ ULONG uFlags - ); - -typedef HRESULT (WINAPI *_SHAutoComplete)( - _In_ HWND hwndEdit, - _In_ DWORD dwFlags - ); - -extern _ChangeWindowMessageFilter ChangeWindowMessageFilter_I; -extern _IsImmersiveProcess IsImmersiveProcess_I; -extern _RunFileDlg RunFileDlg; -extern _SHAutoComplete SHAutoComplete_I; +extern PH_INTEGER_PAIR PhSmallIconSize; +extern PH_INTEGER_PAIR PhLargeIconSize; PHLIBAPI VOID PhGuiSupportInitialization( @@ -76,6 +53,13 @@ VOID PhSetControlTheme( _In_ PWSTR Theme ); +FORCEINLINE LONG_PTR PhGetWindowStyle( + _In_ HWND WindowHandle + ) +{ + return GetWindowLongPtr(WindowHandle, GWL_STYLE); +} + FORCEINLINE VOID PhSetWindowStyle( _In_ HWND Handle, _In_ LONG_PTR Mask, @@ -143,24 +127,6 @@ FORCEINLINE LRESULT PhReflectMessage( return 0; } -#define PH_DEFINE_MAKE_ATOM(AtomName) \ -do { \ - static UNICODE_STRING atomName = RTL_CONSTANT_STRING(AtomName); \ - static PH_INITONCE initOnce = PH_INITONCE_INIT; \ - static RTL_ATOM atom = 0; \ -\ - if (PhBeginInitOnce(&initOnce)) \ - { \ - NtAddAtom(atomName.Buffer, atomName.Length, &atom); \ - PhEndInitOnce(&initOnce); \ - } \ -\ - if (atom) \ - return (PWSTR)(ULONG_PTR)atom; \ - else \ - return atomName.Buffer; \ -} while (0) - FORCEINLINE VOID PhSetListViewStyle( _In_ HWND Handle, _In_ BOOLEAN AllowDragDrop, @@ -217,14 +183,14 @@ INT PhFindListViewItemByParam( ); PHLIBAPI -LOGICAL PhGetListViewItemImageIndex( +BOOLEAN PhGetListViewItemImageIndex( _In_ HWND ListViewHandle, _In_ INT Index, _Out_ PINT ImageIndex ); PHLIBAPI -LOGICAL PhGetListViewItemParam( +BOOLEAN PhGetListViewItemParam( _In_ HWND ListViewHandle, _In_ INT Index, _Out_ PVOID *Param @@ -252,14 +218,22 @@ VOID PhSetListViewSubItem( ); PHLIBAPI -BOOLEAN PhLoadListViewColumnSettings( +INT +NTAPI +PhAddListViewGroup( _In_ HWND ListViewHandle, - _In_ PPH_STRING Settings + _In_ INT GroupId, + _In_ PWSTR Text ); PHLIBAPI -PPH_STRING PhSaveListViewColumnSettings( - _In_ HWND ListViewHandle +INT +NTAPI PhAddListViewGroupItem( + _In_ HWND ListViewHandle, + _In_ INT GroupId, + _In_ INT Index, + _In_ PWSTR Text, + _In_opt_ PVOID Param ); PHLIBAPI @@ -287,6 +261,17 @@ ULONG PhGetWindowTextEx( _Out_opt_ PPH_STRING *Text ); +PHLIBAPI +ULONG +NTAPI +PhGetWindowTextToBuffer( + _In_ HWND hwnd, + _In_ ULONG Flags, + _Out_writes_bytes_opt_(BufferLength) PWSTR Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength + ); + PHLIBAPI VOID PhAddComboBoxStrings( _In_ HWND hWnd, @@ -373,19 +358,29 @@ VOID PhSetClipboardString( _In_ PPH_STRINGREF String ); +#include typedef struct _DLGTEMPLATEEX { - WORD dlgVer; - WORD signature; - DWORD helpID; - DWORD exStyle; - DWORD style; - WORD cDlgItems; - short x; - short y; - short cx; - short cy; + USHORT dlgVer; + USHORT signature; + ULONG helpID; + ULONG exStyle; + ULONG style; + USHORT cDlgItems; + SHORT x; + SHORT y; + SHORT cx; + SHORT cy; + //sz_Or_Ord menu; + //sz_Or_Ord windowClass; + //WCHAR title[titleLen]; + //USHORT pointsize; + //USHORT weight; + //BYTE italic; + //BYTE charset; + //WCHAR typeface[stringLen]; } DLGTEMPLATEEX, *PDLGTEMPLATEEX; +#include PHLIBAPI HWND PhCreateDialogFromTemplate( @@ -472,6 +467,123 @@ VOID PhLayoutManagerLayout( _Inout_ PPH_LAYOUT_MANAGER Manager ); +#define PH_WINDOW_CONTEXT_DEFAULT 0xFFFF + +PHLIBAPI +PVOID +PhGetWindowContext( + _In_ HWND WindowHandle, + _In_ ULONG PropertyHash + ); + +PHLIBAPI +VOID +PhSetWindowContext( + _In_ HWND WindowHandle, + _In_ ULONG PropertyHash, + _In_ PVOID Context + ); + +PHLIBAPI +VOID +PhRemoveWindowContext( + _In_ HWND WindowHandle, + _In_ ULONG PropertyHash + ); + +typedef BOOL (CALLBACK* PH_ENUM_CALLBACK)( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ); + +VOID PhEnumWindows( + _In_ PH_ENUM_CALLBACK Callback, + _In_opt_ PVOID Context + ); + +typedef BOOLEAN (CALLBACK *PH_CHILD_ENUM_CALLBACK)( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ); + +VOID PhEnumChildWindows( + _In_opt_ HWND WindowHandle, + _In_ ULONG Limit, + _In_ PH_CHILD_ENUM_CALLBACK Callback, + _In_opt_ PVOID Context + ); + +HWND PhGetProcessMainWindow( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle + ); + +HWND PhGetProcessMainWindowEx( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle, + _In_ BOOLEAN SkipInvisible + ); + +PHLIBAPI +ULONG +NTAPI +PhGetDialogItemValue( + _In_ HWND WindowHandle, + _In_ INT ControlID + ); + +PHLIBAPI +VOID +NTAPI +PhSetDialogItemValue( + _In_ HWND WindowHandle, + _In_ INT ControlID, + _In_ ULONG Value, + _In_ BOOLEAN Signed + ); + +PHLIBAPI +VOID +NTAPI +PhSetDialogItemText( + _In_ HWND WindowHandle, + _In_ INT ControlID, + _In_ PCWSTR WindowText + ); + +PHLIBAPI +VOID +NTAPI +PhSetWindowText( + _In_ HWND WindowHandle, + _In_ PCWSTR WindowText + ); + +PHLIBAPI +VOID +NTAPI +PhSetWindowAlwaysOnTop( + _In_ HWND WindowHandle, + _In_ BOOLEAN AlwaysOnTop + ); + +FORCEINLINE ULONG PhGetWindowTextLength( + _In_ HWND WindowHandle + ) +{ + return (ULONG)SendMessage(WindowHandle, WM_GETTEXTLENGTH, 0, 0); // DefWindowProc +} + +FORCEINLINE VOID PhSetDialogFocus( + _In_ HWND WindowHandle, + _In_ HWND FocusHandle + ) +{ + // Do not use the SendMessage function to send a WM_NEXTDLGCTL message if your application will + // concurrently process other messages that set the focus. Use the PostMessage function instead. + SendMessage(WindowHandle, WM_NEXTDLGCTL, (WPARAM)FocusHandle, MAKELPARAM(TRUE, 0)); +} + FORCEINLINE VOID PhResizingMinimumSize( _Inout_ PRECT Rect, _In_ WPARAM Edge, @@ -566,6 +678,15 @@ PhSetExtendedListView( _In_ HWND hWnd ); +PHLIBAPI +VOID +NTAPI +PhSetExtendedListViewEx( + _In_ HWND WindowHandle, + _In_ ULONG SortColumn, + _In_ ULONG SortOrder + ); + PHLIBAPI VOID NTAPI @@ -590,7 +711,7 @@ PhSetHeaderSortIcon( #define ELVM_SETITEMFONTFUNCTION (WM_APP + 1117) #define ELVM_RESERVED1 (WM_APP + 1112) #define ELVM_SETREDRAW (WM_APP + 1116) -#define ELVM_RESERVED2 (WM_APP + 1113) +#define ELVM_GETSORT (WM_APP + 1113) #define ELVM_SETSORT (WM_APP + 1108) #define ELVM_SETSORTFAST (WM_APP + 1119) #define ELVM_RESERVED0 (WM_APP + 1110) @@ -619,6 +740,8 @@ PhSetHeaderSortIcon( SendMessage((hWnd), ELVM_SETITEMFONTFUNCTION, 0, (LPARAM)(ItemFontFunction)) #define ExtendedListView_SetRedraw(hWnd, Redraw) \ SendMessage((hWnd), ELVM_SETREDRAW, (WPARAM)(Redraw), 0) +#define ExtendedListView_GetSort(hWnd, Column, Order) \ + SendMessage((hWnd), ELVM_GETSORT, (WPARAM)(Column), (LPARAM)(Order)) #define ExtendedListView_SetSort(hWnd, Column, Order) \ SendMessage((hWnd), ELVM_SETSORT, (WPARAM)(Column), (LPARAM)(Order)) #define ExtendedListView_SetSortFast(hWnd, Fast) \ @@ -716,8 +839,143 @@ PhMakeColorBrighter( return RGB(r, g, b); } +// Window support + +typedef enum _PH_PLUGIN_WINDOW_EVENT_TYPE +{ + PH_PLUGIN_WINDOW_EVENT_TYPE_NONE, + PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, + PH_PLUGIN_WINDOW_EVENT_TYPE_MAX +} PH_PLUGIN_WINDOW_EVENT_TYPE; + +typedef struct _PH_PLUGIN_WINDOW_CALLBACK_REGISTRATION +{ + HWND WindowHandle; + PH_PLUGIN_WINDOW_EVENT_TYPE Type; +} PH_PLUGIN_WINDOW_CALLBACK_REGISTRATION, *PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION; + +typedef struct _PH_PLUGIN_WINDOW_NOTIFY_EVENT +{ + PH_PLUGIN_WINDOW_EVENT_TYPE Type; + union + { + BOOLEAN TopMost; + //HFONT FontHandle; + }; +} PH_PLUGIN_WINDOW_NOTIFY_EVENT, *PPH_PLUGIN_WINDOW_NOTIFY_EVENT; + +typedef struct _PH_PLUGIN_MAINWINDOW_NOTIFY_EVENT +{ + PPH_PLUGIN_WINDOW_NOTIFY_EVENT Event; + PPH_PLUGIN_WINDOW_CALLBACK_REGISTRATION Callback; +} PH_PLUGIN_MAINWINDOW_NOTIFY_EVENT, *PPH_PLUGIN_MAINWINDOW_NOTIFY_EVENT; + +PHLIBAPI +VOID +NTAPI +PhRegisterWindowCallback( + _In_ HWND WindowHandle, + _In_ PH_PLUGIN_WINDOW_EVENT_TYPE Type, + _In_opt_ PVOID Context + ); + +PHLIBAPI +VOID +NTAPI +PhUnregisterWindowCallback( + _In_ HWND WindowHandle + ); + +PHLIBAPI +VOID +NTAPI +PhWindowNotifyTopMostEvent( + _In_ BOOLEAN TopMost + ); + +PHLIBAPI +HANDLE +NTAPI +PhGetGlobalTimerQueue( + VOID + ); + +// theme support (theme.c) + +PHLIBAPI extern HFONT PhApplicationFont; // phapppub +PHLIBAPI extern HFONT PhTreeWindowFont; // phapppub +PHLIBAPI extern HBRUSH PhMenuBackgroundBrush; + +PHLIBAPI +VOID +NTAPI +PhInitializeWindowTheme( + _In_ HWND WindowHandle, + _In_ BOOLEAN EnableThemeSupport + ); + +PHLIBAPI +VOID +NTAPI +PhInitializeWindowThemeEx( + _In_ HWND WindowHandle + ); + +PHLIBAPI +VOID +NTAPI +PhReInitializeWindowTheme( + _In_ HWND WindowHandle + ); + +PHLIBAPI +VOID +NTAPI +PhInitializeThemeWindowFrame( + _In_ HWND WindowHandle + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhThemeWindowDrawItem( + _In_ PDRAWITEMSTRUCT DrawInfo + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhThemeWindowMeasureItem( + _In_ PMEASUREITEMSTRUCT DrawInfo + ); + +PHLIBAPI +VOID +NTAPI +PhInitializeWindowThemeStatusBar( + _In_ HWND StatusBarHandle + ); + +FORCEINLINE +HFONT +PhDuplicateFontWithNewHeight( + _In_ HFONT Font, + _In_ LONG NewHeight + ) +{ + LOGFONT logFont; + + if (GetObject(Font, sizeof(LOGFONT), &logFont)) + { + logFont.lfHeight = PhMultiplyDivide(NewHeight, PhGlobalDpi, 96); + return CreateFontIndirect(&logFont); + } + + return NULL; +} + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/phlib/include/guisupp.h b/phlib/include/guisupp.h index e5e8a5e0daa2..9fd0465ff20e 100644 --- a/phlib/include/guisupp.h +++ b/phlib/include/guisupp.h @@ -1,43 +1,28 @@ -#ifndef _PH_GUISUPP_H -#define _PH_GUISUPP_H - -typedef HRESULT (WINAPI *_LoadIconMetric)( - _In_ HINSTANCE hinst, - _In_ PCWSTR pszName, - _In_ int lims, - _Out_ HICON *phico - ); - -typedef HRESULT (WINAPI *_LoadIconWithScaleDown)( - _In_ HINSTANCE hinst, - _In_ PCWSTR pszName, - _In_ int cx, - _In_ int cy, - _Out_ HICON *phico - ); - -typedef struct _PHP_ICON_ENTRY -{ - HINSTANCE InstanceHandle; - PWSTR Name; - ULONG Width; - ULONG Height; - HICON Icon; -} PHP_ICON_ENTRY, *PPHP_ICON_ENTRY; - -#define PHP_ICON_ENTRY_SIZE_SMALL (-1) -#define PHP_ICON_ENTRY_SIZE_LARGE (-2) - -FORCEINLINE ULONG PhpGetIconEntrySize( - _In_ ULONG InputSize, - _In_ ULONG Flags - ) -{ - if (Flags & PH_LOAD_ICON_SIZE_SMALL) - return PHP_ICON_ENTRY_SIZE_SMALL; - if (Flags & PH_LOAD_ICON_SIZE_LARGE) - return PHP_ICON_ENTRY_SIZE_LARGE; - return InputSize; -} - -#endif +#ifndef _PH_GUISUPP_H +#define _PH_GUISUPP_H + +typedef struct _PHP_ICON_ENTRY +{ + HINSTANCE InstanceHandle; + PWSTR Name; + ULONG Width; + ULONG Height; + HICON Icon; +} PHP_ICON_ENTRY, *PPHP_ICON_ENTRY; + +#define PHP_ICON_ENTRY_SIZE_SMALL (-1) +#define PHP_ICON_ENTRY_SIZE_LARGE (-2) + +FORCEINLINE ULONG PhpGetIconEntrySize( + _In_ ULONG InputSize, + _In_ ULONG Flags + ) +{ + if (Flags & PH_LOAD_ICON_SIZE_SMALL) + return PHP_ICON_ENTRY_SIZE_SMALL; + if (Flags & PH_LOAD_ICON_SIZE_LARGE) + return PHP_ICON_ENTRY_SIZE_LARGE; + return InputSize; +} + +#endif diff --git a/phlib/include/handlep.h b/phlib/include/handlep.h index 06cf2cf899f4..ed1b69dae068 100644 --- a/phlib/include/handlep.h +++ b/phlib/include/handlep.h @@ -1,147 +1,147 @@ -#ifndef _PH_HANDLEP_H -#define _PH_HANDLEP_H - -#define PH_HANDLE_TABLE_ENTRY_TYPE 0x1 -#define PH_HANDLE_TABLE_ENTRY_IN_USE 0x0 -#define PH_HANDLE_TABLE_ENTRY_FREE 0x1 - -// Locked actually means Not Locked. This means that an in use, locked handle table entry can be -// used as-is. -#define PH_HANDLE_TABLE_ENTRY_LOCKED 0x2 -#define PH_HANDLE_TABLE_ENTRY_LOCKED_SHIFT 1 - -// There is initially one handle table level, with 256 entries. When the handle table is expanded, -// the table is replaced with a level 1 table, which contains 256 pointers to level 0 tables (the -// first entry already points to the initial level 0 table). Similarly, when the handle table is -// expanded a second time, the table is replaced with a level 2 table, which contains 256 pointers -// to level 1 tables. -// -// This provides a maximum of 16,777,216 handles. - -#define PH_HANDLE_TABLE_LEVEL_ENTRIES 256 -#define PH_HANDLE_TABLE_LEVEL_MASK 0x3 - -#define PH_HANDLE_TABLE_LOCKS 8 -#define PH_HANDLE_TABLE_LOCK_INDEX(HandleValue) ((HandleValue) % PH_HANDLE_TABLE_LOCKS) - -typedef struct _PH_HANDLE_TABLE -{ - PH_QUEUED_LOCK Lock; - PH_WAKE_EVENT HandleWakeEvent; - - ULONG Count; - ULONG_PTR TableValue; - ULONG FreeValue; - ULONG NextValue; - ULONG FreeValueAlt; - - ULONG Flags; - - PH_QUEUED_LOCK Locks[PH_HANDLE_TABLE_LOCKS]; -} PH_HANDLE_TABLE, *PPH_HANDLE_TABLE; - -FORCEINLINE VOID PhpLockHandleTableShared( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ ULONG Index - ) -{ - PhAcquireQueuedLockShared(&HandleTable->Locks[Index]); -} - -FORCEINLINE VOID PhpUnlockHandleTableShared( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ ULONG Index - ) -{ - PhReleaseQueuedLockShared(&HandleTable->Locks[Index]); -} - -// Handle values work by specifying indicies into each -// level. -// -// Bits 0-7: level 0 -// Bits 8-15: level 1 -// Bits 16-23: level 2 -// Bits 24-31: reserved - -#define PH_HANDLE_VALUE_INVALID ((ULONG)-1) -#define PH_HANDLE_VALUE_SHIFT 2 -#define PH_HANDLE_VALUE_BIAS 4 - -#define PH_HANDLE_VALUE_LEVEL0(HandleValue) ((HandleValue) & 0xff) -#define PH_HANDLE_VALUE_LEVEL1_U(HandleValue) ((HandleValue) >> 8) -#define PH_HANDLE_VALUE_LEVEL1(HandleValue) (PH_HANDLE_VALUE_LEVEL1_U(HandleValue) & 0xff) -#define PH_HANDLE_VALUE_LEVEL2_U(HandleValue) ((HandleValue) >> 16) -#define PH_HANDLE_VALUE_LEVEL2(HandleValue) (PH_HANDLE_VALUE_LEVEL2_U(HandleValue) & 0xff) -#define PH_HANDLE_VALUE_IS_INVALID(HandleValue) (((HandleValue) >> 24) != 0) - -FORCEINLINE HANDLE PhpEncodeHandle( - _In_ ULONG HandleValue - ) -{ - return UlongToHandle(((HandleValue << PH_HANDLE_VALUE_SHIFT) + PH_HANDLE_VALUE_BIAS)); -} - -FORCEINLINE ULONG PhpDecodeHandle( - _In_ HANDLE Handle - ) -{ - return (HandleToUlong(Handle) - PH_HANDLE_VALUE_BIAS) >> PH_HANDLE_VALUE_SHIFT; -} - -VOID PhpBlockOnLockedHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ); - -PPH_HANDLE_TABLE_ENTRY PhpAllocateHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _Out_ PULONG HandleValue - ); - -VOID PhpFreeHandleTableEntry( - _Inout_ PPH_HANDLE_TABLE HandleTable, - _In_ ULONG HandleValue, - _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry - ); - -BOOLEAN PhpAllocateMoreHandleTableEntries( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ BOOLEAN Initialize - ); - -PPH_HANDLE_TABLE_ENTRY PhpLookupHandleTableEntry( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ ULONG HandleValue - ); - -ULONG PhpMoveFreeHandleTableEntries( - _Inout_ PPH_HANDLE_TABLE HandleTable - ); - -PPH_HANDLE_TABLE_ENTRY PhpCreateHandleTableLevel0( - _In_ PPH_HANDLE_TABLE HandleTable, - _In_ BOOLEAN Initialize - ); - -VOID PhpFreeHandleTableLevel0( - _In_ PPH_HANDLE_TABLE_ENTRY Table - ); - -PPH_HANDLE_TABLE_ENTRY *PhpCreateHandleTableLevel1( - _In_ PPH_HANDLE_TABLE HandleTable - ); - -VOID PhpFreeHandleTableLevel1( - _In_ PPH_HANDLE_TABLE_ENTRY *Table - ); - -PPH_HANDLE_TABLE_ENTRY **PhpCreateHandleTableLevel2( - _In_ PPH_HANDLE_TABLE HandleTable - ); - -VOID PhpFreeHandleTableLevel2( - _In_ PPH_HANDLE_TABLE_ENTRY **Table - ); - -#endif +#ifndef _PH_HANDLEP_H +#define _PH_HANDLEP_H + +#define PH_HANDLE_TABLE_ENTRY_TYPE 0x1 +#define PH_HANDLE_TABLE_ENTRY_IN_USE 0x0 +#define PH_HANDLE_TABLE_ENTRY_FREE 0x1 + +// Locked actually means Not Locked. This means that an in use, locked handle table entry can be +// used as-is. +#define PH_HANDLE_TABLE_ENTRY_LOCKED 0x2 +#define PH_HANDLE_TABLE_ENTRY_LOCKED_SHIFT 1 + +// There is initially one handle table level, with 256 entries. When the handle table is expanded, +// the table is replaced with a level 1 table, which contains 256 pointers to level 0 tables (the +// first entry already points to the initial level 0 table). Similarly, when the handle table is +// expanded a second time, the table is replaced with a level 2 table, which contains 256 pointers +// to level 1 tables. +// +// This provides a maximum of 16,777,216 handles. + +#define PH_HANDLE_TABLE_LEVEL_ENTRIES 256 +#define PH_HANDLE_TABLE_LEVEL_MASK 0x3 + +#define PH_HANDLE_TABLE_LOCKS 8 +#define PH_HANDLE_TABLE_LOCK_INDEX(HandleValue) ((HandleValue) % PH_HANDLE_TABLE_LOCKS) + +typedef struct _PH_HANDLE_TABLE +{ + PH_QUEUED_LOCK Lock; + PH_WAKE_EVENT HandleWakeEvent; + + ULONG Count; + ULONG_PTR TableValue; + ULONG FreeValue; + ULONG NextValue; + ULONG FreeValueAlt; + + ULONG Flags; + + PH_QUEUED_LOCK Locks[PH_HANDLE_TABLE_LOCKS]; +} PH_HANDLE_TABLE, *PPH_HANDLE_TABLE; + +FORCEINLINE VOID PhpLockHandleTableShared( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ ULONG Index + ) +{ + PhAcquireQueuedLockShared(&HandleTable->Locks[Index]); +} + +FORCEINLINE VOID PhpUnlockHandleTableShared( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ ULONG Index + ) +{ + PhReleaseQueuedLockShared(&HandleTable->Locks[Index]); +} + +// Handle values work by specifying indicies into each +// level. +// +// Bits 0-7: level 0 +// Bits 8-15: level 1 +// Bits 16-23: level 2 +// Bits 24-31: reserved + +#define PH_HANDLE_VALUE_INVALID ((ULONG)-1) +#define PH_HANDLE_VALUE_SHIFT 2 +#define PH_HANDLE_VALUE_BIAS 4 + +#define PH_HANDLE_VALUE_LEVEL0(HandleValue) ((HandleValue) & 0xff) +#define PH_HANDLE_VALUE_LEVEL1_U(HandleValue) ((HandleValue) >> 8) +#define PH_HANDLE_VALUE_LEVEL1(HandleValue) (PH_HANDLE_VALUE_LEVEL1_U(HandleValue) & 0xff) +#define PH_HANDLE_VALUE_LEVEL2_U(HandleValue) ((HandleValue) >> 16) +#define PH_HANDLE_VALUE_LEVEL2(HandleValue) (PH_HANDLE_VALUE_LEVEL2_U(HandleValue) & 0xff) +#define PH_HANDLE_VALUE_IS_INVALID(HandleValue) (((HandleValue) >> 24) != 0) + +FORCEINLINE HANDLE PhpEncodeHandle( + _In_ ULONG HandleValue + ) +{ + return UlongToHandle(((HandleValue << PH_HANDLE_VALUE_SHIFT) + PH_HANDLE_VALUE_BIAS)); +} + +FORCEINLINE ULONG PhpDecodeHandle( + _In_ HANDLE Handle + ) +{ + return (HandleToUlong(Handle) - PH_HANDLE_VALUE_BIAS) >> PH_HANDLE_VALUE_SHIFT; +} + +VOID PhpBlockOnLockedHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ); + +PPH_HANDLE_TABLE_ENTRY PhpAllocateHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _Out_ PULONG HandleValue + ); + +VOID PhpFreeHandleTableEntry( + _Inout_ PPH_HANDLE_TABLE HandleTable, + _In_ ULONG HandleValue, + _Inout_ PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ); + +BOOLEAN PhpAllocateMoreHandleTableEntries( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ BOOLEAN Initialize + ); + +PPH_HANDLE_TABLE_ENTRY PhpLookupHandleTableEntry( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ ULONG HandleValue + ); + +ULONG PhpMoveFreeHandleTableEntries( + _Inout_ PPH_HANDLE_TABLE HandleTable + ); + +PPH_HANDLE_TABLE_ENTRY PhpCreateHandleTableLevel0( + _In_ PPH_HANDLE_TABLE HandleTable, + _In_ BOOLEAN Initialize + ); + +VOID PhpFreeHandleTableLevel0( + _In_ PPH_HANDLE_TABLE_ENTRY Table + ); + +PPH_HANDLE_TABLE_ENTRY *PhpCreateHandleTableLevel1( + _In_ PPH_HANDLE_TABLE HandleTable + ); + +VOID PhpFreeHandleTableLevel1( + _In_ PPH_HANDLE_TABLE_ENTRY *Table + ); + +PPH_HANDLE_TABLE_ENTRY **PhpCreateHandleTableLevel2( + _In_ PPH_HANDLE_TABLE HandleTable + ); + +VOID PhpFreeHandleTableLevel2( + _In_ PPH_HANDLE_TABLE_ENTRY **Table + ); + +#endif diff --git a/phlib/include/hexedit.h b/phlib/include/hexedit.h index 93287013fb32..ce63c80e07b4 100644 --- a/phlib/include/hexedit.h +++ b/phlib/include/hexedit.h @@ -1,49 +1,53 @@ -#ifndef _PH_HEXEDIT_H -#define _PH_HEXEDIT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_HEXEDIT_CLASSNAME L"PhHexEdit" - -#define EDIT_NONE 0 -#define EDIT_ASCII 1 -#define EDIT_HIGH 2 -#define EDIT_LOW 3 - -PHLIBAPI -BOOLEAN PhHexEditInitialization( - VOID - ); - -#define HEM_SETBUFFER (WM_USER + 1) -#define HEM_SETDATA (WM_USER + 2) -#define HEM_GETBUFFER (WM_USER + 3) -#define HEM_SETSEL (WM_USER + 4) -#define HEM_SETEDITMODE (WM_USER + 5) -#define HEM_SETBYTESPERROW (WM_USER + 6) - -#define HexEdit_SetBuffer(hWnd, Buffer, Length) \ - SendMessage((hWnd), HEM_SETBUFFER, (WPARAM)(Length), (LPARAM)(Buffer)) - -#define HexEdit_SetData(hWnd, Buffer, Length) \ - SendMessage((hWnd), HEM_SETDATA, (WPARAM)(Length), (LPARAM)(Buffer)) - -#define HexEdit_GetBuffer(hWnd, Length) \ - ((PUCHAR)SendMessage((hWnd), HEM_GETBUFFER, (WPARAM)(Length), 0)) - -#define HexEdit_SetSel(hWnd, Start, End) \ - SendMessage((hWnd), HEM_SETSEL, (WPARAM)(Start), (LPARAM)(End)) - -#define HexEdit_SetEditMode(hWnd, Mode) \ - SendMessage((hWnd), HEM_SETEDITMODE, (WPARAM)(Mode), 0) - -#define HexEdit_SetBytesPerRow(hWnd, BytesPerRow) \ - SendMessage((hWnd), HEM_SETBYTESPERROW, (WPARAM)(BytesPerRow), 0) - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_HEXEDIT_H +#define _PH_HEXEDIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_HEXEDIT_CLASSNAME L"PhHexEdit" + +#define EDIT_NONE 0 +#define EDIT_ASCII 1 +#define EDIT_HIGH 2 +#define EDIT_LOW 3 + +PHLIBAPI +BOOLEAN PhHexEditInitialization( + VOID + ); + +#define HEM_SETBUFFER (WM_USER + 1) +#define HEM_SETDATA (WM_USER + 2) +#define HEM_GETBUFFER (WM_USER + 3) +#define HEM_SETSEL (WM_USER + 4) +#define HEM_SETEDITMODE (WM_USER + 5) +#define HEM_SETBYTESPERROW (WM_USER + 6) +#define HEM_SETEXTENDEDUNICODE (WM_USER + 7) + +#define HexEdit_SetBuffer(hWnd, Buffer, Length) \ + SendMessage((hWnd), HEM_SETBUFFER, (WPARAM)(Length), (LPARAM)(Buffer)) + +#define HexEdit_SetData(hWnd, Buffer, Length) \ + SendMessage((hWnd), HEM_SETDATA, (WPARAM)(Length), (LPARAM)(Buffer)) + +#define HexEdit_GetBuffer(hWnd, Length) \ + ((PUCHAR)SendMessage((hWnd), HEM_GETBUFFER, (WPARAM)(Length), 0)) + +#define HexEdit_SetSel(hWnd, Start, End) \ + SendMessage((hWnd), HEM_SETSEL, (WPARAM)(Start), (LPARAM)(End)) + +#define HexEdit_SetEditMode(hWnd, Mode) \ + SendMessage((hWnd), HEM_SETEDITMODE, (WPARAM)(Mode), 0) + +#define HexEdit_SetBytesPerRow(hWnd, BytesPerRow) \ + SendMessage((hWnd), HEM_SETBYTESPERROW, (WPARAM)(BytesPerRow), 0) + +#define HexEdit_SetExtendedUnicode(hWnd, ExtendedUnicode) \ + SendMessage((hWnd), HEM_SETEXTENDEDUNICODE, (WPARAM)(ExtendedUnicode), 0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/hexeditp.h b/phlib/include/hexeditp.h index f7c59bba41ab..b32d1c1d0155 100644 --- a/phlib/include/hexeditp.h +++ b/phlib/include/hexeditp.h @@ -1,201 +1,207 @@ -#ifndef _PH_HEXEDITP_H -#define _PH_HEXEDITP_H - -typedef struct _PHP_HEXEDIT_CONTEXT -{ - PUCHAR Data; - LONG Length; - BOOLEAN UserBuffer; - LONG TopIndex; // index of first visible byte on screen - - LONG CurrentAddress; - LONG CurrentMode; - LONG SelStart; - LONG SelEnd; - - LONG BytesPerRow; - LONG LinesPerPage; - BOOLEAN ShowAddress; - BOOLEAN ShowAscii; - BOOLEAN ShowHex; - BOOLEAN AddressIsWide; - BOOLEAN AllowLengthChange; - - BOOLEAN NoAddressChange; - BOOLEAN HalfPage; - - HFONT Font; - LONG LineHeight; - LONG NullWidth; - PWCHAR CharBuffer; - ULONG CharBufferLength; - BOOLEAN Update; - - LONG HexOffset; - LONG AsciiOffset; - LONG AddressOffset; - - BOOLEAN HasCapture; - POINT EditPosition; -} PHP_HEXEDIT_CONTEXT, *PPHP_HEXEDIT_CONTEXT; - -#define IS_PRINTABLE(Byte) ((ULONG)((Byte) - ' ') <= (ULONG)('~' - ' ')) - -#define TO_HEX(Buffer, Byte) \ -{ \ - *(Buffer)++ = PhIntegerToChar[(Byte) >> 4]; \ - *(Buffer)++ = PhIntegerToChar[(Byte) & 0xf]; \ -} - -#define REDRAW_WINDOW(hwnd) \ - RedrawWindow((hwnd), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE) - -VOID PhpCreateHexEditContext( - _Out_ PPHP_HEXEDIT_CONTEXT *Context - ); - -VOID PhpFreeHexEditContext( - _In_ _Post_invalid_ PPHP_HEXEDIT_CONTEXT Context - ); - -LRESULT CALLBACK PhpHexEditWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID PhpHexEditUpdateMetrics( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ BOOLEAN UpdateLineHeight, - _In_opt_ HDC hdc - ); - -VOID PhpHexEditOnPaint( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ PAINTSTRUCT *PaintStruct, - _In_ HDC hdc - ); - -VOID PhpHexEditUpdateScrollbars( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -FORCEINLINE BOOLEAN PhpHexEditHasSelected( - _In_ PPHP_HEXEDIT_CONTEXT Context - ) -{ - return Context->SelStart != -1; -} - -VOID PhpHexEditCreateAddressCaret( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditCreateEditCaret( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditRepositionCaret( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG Position - ); - -VOID PhpHexEditCalculatePosition( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG X, - _In_ LONG Y, - _Out_ POINT *Point - ); - -VOID PhpHexEditMove( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG X, - _In_ LONG Y - ); - -VOID PhpHexEditSetSel( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG S, - _In_ LONG E - ); - -VOID PhpHexEditScrollTo( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG Position - ); - -VOID PhpHexEditClearEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditCopyEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditCutEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditPasteEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditSelectAll( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditUndoEdit( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditNormalizeSel( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context - ); - -VOID PhpHexEditSelDelete( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG S, - _In_ LONG E - ); - -VOID PhpHexEditSelInsert( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ LONG S, - _In_ LONG L - ); - -VOID PhpHexEditSetBuffer( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ PUCHAR Data, - _In_ ULONG Length - ); - -VOID PhpHexEditSetData( - _In_ HWND hwnd, - _In_ PPHP_HEXEDIT_CONTEXT Context, - _In_ PUCHAR Data, - _In_ ULONG Length - ); - -#endif +#ifndef _PH_HEXEDITP_H +#define _PH_HEXEDITP_H + +typedef struct _PHP_HEXEDIT_CONTEXT +{ + PUCHAR Data; + LONG Length; + BOOLEAN UserBuffer; + LONG TopIndex; // index of first visible byte on screen + + LONG CurrentAddress; + LONG CurrentMode; + LONG SelStart; + LONG SelEnd; + + LONG BytesPerRow; + LONG LinesPerPage; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN ShowAddress : 1; + BOOLEAN ShowAscii : 1; + BOOLEAN ShowHex : 1; + BOOLEAN AddressIsWide : 1; + BOOLEAN AllowLengthChange : 1; + BOOLEAN NoAddressChange : 1; + BOOLEAN HalfPage : 1; + BOOLEAN ExtendedUnicode : 1; + }; + }; + + HFONT Font; + LONG LineHeight; + LONG NullWidth; + PWCHAR CharBuffer; + ULONG CharBufferLength; + BOOLEAN Update; + + LONG HexOffset; + LONG AsciiOffset; + LONG AddressOffset; + + BOOLEAN HasCapture; + POINT EditPosition; +} PHP_HEXEDIT_CONTEXT, *PPHP_HEXEDIT_CONTEXT; + +#define TO_HEX(Buffer, Byte) \ +{ \ + *(Buffer)++ = PhIntegerToChar[(Byte) >> 4]; \ + *(Buffer)++ = PhIntegerToChar[(Byte) & 0xf]; \ +} + +#define REDRAW_WINDOW(hwnd) \ + RedrawWindow((hwnd), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE) + +VOID PhpCreateHexEditContext( + _Out_ PPHP_HEXEDIT_CONTEXT *Context + ); + +VOID PhpFreeHexEditContext( + _In_ _Post_invalid_ PPHP_HEXEDIT_CONTEXT Context + ); + +LRESULT CALLBACK PhpHexEditWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID PhpHexEditUpdateMetrics( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ BOOLEAN UpdateLineHeight, + _In_opt_ HDC hdc + ); + +VOID PhpHexEditOnPaint( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ PAINTSTRUCT *PaintStruct, + _In_ HDC hdc + ); + +VOID PhpHexEditUpdateScrollbars( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +FORCEINLINE BOOLEAN PhpHexEditHasSelected( + _In_ PPHP_HEXEDIT_CONTEXT Context + ) +{ + return Context->SelStart != -1; +} + +VOID PhpHexEditCreateAddressCaret( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditCreateEditCaret( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditRepositionCaret( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG Position + ); + +VOID PhpHexEditCalculatePosition( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG X, + _In_ LONG Y, + _Out_ POINT *Point + ); + +VOID PhpHexEditMove( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG X, + _In_ LONG Y + ); + +VOID PhpHexEditSetSel( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG S, + _In_ LONG E + ); + +VOID PhpHexEditScrollTo( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG Position + ); + +VOID PhpHexEditClearEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditCopyEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditCutEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditPasteEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditSelectAll( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditUndoEdit( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditNormalizeSel( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context + ); + +VOID PhpHexEditSelDelete( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG S, + _In_ LONG E + ); + +VOID PhpHexEditSelInsert( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ LONG S, + _In_ LONG L + ); + +VOID PhpHexEditSetBuffer( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ PUCHAR Data, + _In_ ULONG Length + ); + +VOID PhpHexEditSetData( + _In_ HWND hwnd, + _In_ PPHP_HEXEDIT_CONTEXT Context, + _In_ PUCHAR Data, + _In_ ULONG Length + ); + +#endif diff --git a/phlib/include/hndlinfo.h b/phlib/include/hndlinfo.h index c92bd4b8de57..292dd01f3497 100644 --- a/phlib/include/hndlinfo.h +++ b/phlib/include/hndlinfo.h @@ -70,10 +70,10 @@ PhGetHandleInformationEx( ); #define PH_FIRST_OBJECT_TYPE(ObjectTypes) \ - (POBJECT_TYPE_INFORMATION)((PCHAR)(ObjectTypes) + ALIGN_UP(sizeof(OBJECT_TYPES_INFORMATION), ULONG_PTR)) + PTR_ADD_OFFSET(ObjectTypes, ALIGN_UP(sizeof(OBJECT_TYPES_INFORMATION), ULONG_PTR)) #define PH_NEXT_OBJECT_TYPE(ObjectType) \ - (POBJECT_TYPE_INFORMATION)((PCHAR)(ObjectType) + sizeof(OBJECT_TYPE_INFORMATION) + \ + PTR_ADD_OFFSET(ObjectType, sizeof(OBJECT_TYPE_INFORMATION) + \ ALIGN_UP(ObjectType->TypeName.MaximumLength, ULONG_PTR)) PHLIBAPI @@ -90,6 +90,13 @@ PhGetObjectTypeNumber( _In_ PUNICODE_STRING TypeName ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetObjectTypeName( + _In_ ULONG TypeIndex + ); + PHLIBAPI NTSTATUS NTAPI @@ -131,6 +138,16 @@ PhCallNtSetSecurityObjectWithTimeout( _In_ PSECURITY_DESCRIPTOR SecurityDescriptor ); +PHLIBAPI +NTSTATUS +NTAPI +PhCallNtQueryFileInformationWithTimeout( + _In_ HANDLE Handle, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _Out_writes_bytes_opt_(FileInformationLength) PVOID FileInformation, + _In_ ULONG FileInformationLength + ); + #ifdef __cplusplus } #endif diff --git a/phlib/include/json.h b/phlib/include/json.h new file mode 100644 index 000000000000..74ed35752f23 --- /dev/null +++ b/phlib/include/json.h @@ -0,0 +1,176 @@ +/* + * Process Hacker - + * json wrapper + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PH_PHJSON_H +#define _PH_PHJSON_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _JSON_ARRAY_LIST_OBJECT +{ + PSTR Key; + PVOID Entry; +} JSON_ARRAY_LIST_OBJECT, *PJSON_ARRAY_LIST_OBJECT; + +PHLIBAPI +PVOID +NTAPI +PhCreateJsonParser( + _In_ PSTR JsonString + ); + +PHLIBAPI +VOID +NTAPI +PhFreeJsonParser( + _In_ PVOID Object + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetJsonValueAsString( + _In_ PVOID Object, + _In_ PSTR Key + ); + +PHLIBAPI +INT64 +NTAPI +PhGetJsonValueAsLong64( + _In_ PVOID Object, + _In_ PSTR Key + ); + +PHLIBAPI +PVOID +NTAPI +PhCreateJsonObject( + VOID + ); + +PHLIBAPI +PVOID +NTAPI +PhGetJsonObject( + _In_ PVOID Object, + _In_ PSTR Key + ); + +PHLIBAPI +INT +NTAPI +PhGetJsonObjectLength( + _In_ PVOID Object + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhGetJsonObjectBool( + _In_ PVOID Object, + _In_ PSTR Key + ); + +PHLIBAPI +VOID +NTAPI +PhAddJsonObject( + _In_ PVOID Object, + _In_ PSTR Key, + _In_ PSTR Value + ); + +PHLIBAPI +PVOID +NTAPI +PhCreateJsonArray( + VOID + ); + +PHLIBAPI +VOID +NTAPI +PhAddJsonArrayObject( + _In_ PVOID Object, + _In_ PVOID jsonEntry + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetJsonArrayString( + _In_ PVOID Object + ); + +PHLIBAPI +INT64 +NTAPI +PhGetJsonArrayLong64( + _In_ PVOID Object, + _In_ INT Index + ); + +PHLIBAPI +INT +NTAPI +PhGetJsonArrayLength( + _In_ PVOID Object + ); + +PHLIBAPI +PVOID +NTAPI +PhGetJsonArrayIndexObject( + _In_ PVOID Object, + _In_ INT Index + ); + +PHLIBAPI +PPH_LIST +NTAPI +PhGetJsonObjectAsArrayList( + _In_ PVOID Object + ); + +PHLIBAPI +PVOID +NTAPI +PhLoadJsonObjectFromFile( + _In_ PWSTR FileName + ); + +PHLIBAPI +VOID +NTAPI +PhSaveJsonObjectToFile( + _In_ PWSTR FileName, + _In_ PVOID Object + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/kphapi.h b/phlib/include/kphapi.h index e8a41992b376..a802089b7587 100644 --- a/phlib/include/kphapi.h +++ b/phlib/include/kphapi.h @@ -1,241 +1,241 @@ -#ifndef _KPHAPI_H -#define _KPHAPI_H - -// This file contains KProcessHacker definitions shared across kernel-mode and user-mode. - -// Process information - -typedef enum _KPH_PROCESS_INFORMATION_CLASS -{ - KphProcessReserved1 = 1, - KphProcessReserved2 = 2, - KphProcessReserved3 = 3, - MaxKphProcessInfoClass -} KPH_PROCESS_INFORMATION_CLASS; - -// Thread information - -typedef enum _KPH_THREAD_INFORMATION_CLASS -{ - KphThreadReserved1 = 1, - KphThreadReserved2 = 2, - KphThreadReserved3 = 3, - MaxKphThreadInfoClass -} KPH_THREAD_INFORMATION_CLASS; - -// Process handle information - -typedef struct _KPH_PROCESS_HANDLE -{ - HANDLE Handle; - PVOID Object; - ACCESS_MASK GrantedAccess; - USHORT ObjectTypeIndex; - USHORT Reserved1; - ULONG HandleAttributes; - ULONG Reserved2; -} KPH_PROCESS_HANDLE, *PKPH_PROCESS_HANDLE; - -typedef struct _KPH_PROCESS_HANDLE_INFORMATION -{ - ULONG HandleCount; - KPH_PROCESS_HANDLE Handles[1]; -} KPH_PROCESS_HANDLE_INFORMATION, *PKPH_PROCESS_HANDLE_INFORMATION; - -// Object information - -typedef enum _KPH_OBJECT_INFORMATION_CLASS -{ - KphObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION - KphObjectNameInformation, // q: OBJECT_NAME_INFORMATION - KphObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION - KphObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION - KphObjectProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION - KphObjectThreadBasicInformation, // q: THREAD_BASIC_INFORMATION - KphObjectEtwRegBasicInformation, // q: ETWREG_BASIC_INFORMATION - KphObjectFileObjectInformation, // q: KPH_FILE_OBJECT_INFORMATION - KphObjectFileObjectDriver, // q: KPH_FILE_OBJECT_DRIVER - MaxKphObjectInfoClass -} KPH_OBJECT_INFORMATION_CLASS; - -typedef struct _KPH_FILE_OBJECT_INFORMATION -{ - BOOLEAN LockOperation; - BOOLEAN DeletePending; - BOOLEAN ReadAccess; - BOOLEAN WriteAccess; - BOOLEAN DeleteAccess; - BOOLEAN SharedRead; - BOOLEAN SharedWrite; - BOOLEAN SharedDelete; - LARGE_INTEGER CurrentByteOffset; - ULONG Flags; -} KPH_FILE_OBJECT_INFORMATION, *PKPH_FILE_OBJECT_INFORMATION; - -typedef struct _KPH_FILE_OBJECT_DRIVER -{ - HANDLE DriverHandle; -} KPH_FILE_OBJECT_DRIVER, *PKPH_FILE_OBJECT_DRIVER; - -// Driver information - -typedef enum _DRIVER_INFORMATION_CLASS -{ - DriverBasicInformation, - DriverNameInformation, - DriverServiceKeyNameInformation, - MaxDriverInfoClass -} DRIVER_INFORMATION_CLASS; - -typedef struct _DRIVER_BASIC_INFORMATION -{ - ULONG Flags; - PVOID DriverStart; - ULONG DriverSize; -} DRIVER_BASIC_INFORMATION, *PDRIVER_BASIC_INFORMATION; - -typedef struct _DRIVER_NAME_INFORMATION -{ - UNICODE_STRING DriverName; -} DRIVER_NAME_INFORMATION, *PDRIVER_NAME_INFORMATION; - -typedef struct _DRIVER_SERVICE_KEY_NAME_INFORMATION -{ - UNICODE_STRING ServiceKeyName; -} DRIVER_SERVICE_KEY_NAME_INFORMATION, *PDRIVER_SERVICE_KEY_NAME_INFORMATION; - -// ETW registration object information - -typedef struct _ETWREG_BASIC_INFORMATION -{ - GUID Guid; - ULONG_PTR SessionId; -} ETWREG_BASIC_INFORMATION, *PETWREG_BASIC_INFORMATION; - -// Device - -#define KPH_DEVICE_SHORT_NAME L"KProcessHacker3" -#define KPH_DEVICE_TYPE 0x9999 -#define KPH_DEVICE_NAME (L"\\Device\\" KPH_DEVICE_SHORT_NAME) - -// Parameters - -typedef enum _KPH_SECURITY_LEVEL -{ - KphSecurityNone = 0, // all clients are allowed - KphSecurityPrivilegeCheck = 1, // require SeDebugPrivilege - KphSecuritySignatureCheck = 2, // require trusted signature - KphSecuritySignatureAndPrivilegeCheck = 3, // require trusted signature and SeDebugPrivilege - KphMaxSecurityLevel -} KPH_SECURITY_LEVEL, *PKPH_SECURITY_LEVEL; - -typedef struct _KPH_DYN_STRUCT_DATA -{ - SHORT EgeGuid; - SHORT EpObjectTable; - SHORT Reserved0; - SHORT Reserved1; - SHORT Reserved2; - SHORT EreGuidEntry; - SHORT HtHandleContentionEvent; - SHORT OtName; - SHORT OtIndex; - SHORT ObDecodeShift; - SHORT ObAttributesShift; -} KPH_DYN_STRUCT_DATA, *PKPH_DYN_STRUCT_DATA; - -typedef struct _KPH_DYN_PACKAGE -{ - USHORT MajorVersion; - USHORT MinorVersion; - USHORT ServicePackMajor; // -1 to ignore - USHORT BuildNumber; // -1 to ignore - ULONG ResultingNtVersion; // PHNT_* - KPH_DYN_STRUCT_DATA StructData; -} KPH_DYN_PACKAGE, *PKPH_DYN_PACKAGE; - -#define KPH_DYN_CONFIGURATION_VERSION 3 -#define KPH_DYN_MAXIMUM_PACKAGES 64 - -typedef struct _KPH_DYN_CONFIGURATION -{ - ULONG Version; - ULONG NumberOfPackages; - KPH_DYN_PACKAGE Packages[1]; -} KPH_DYN_CONFIGURATION, *PKPH_DYN_CONFIGURATION; - -// Verification - -#ifdef __BCRYPT_H__ -#define KPH_SIGN_ALGORITHM BCRYPT_ECDSA_P256_ALGORITHM -#define KPH_SIGN_ALGORITHM_BITS 256 -#define KPH_HASH_ALGORITHM BCRYPT_SHA256_ALGORITHM -#define KPH_BLOB_PUBLIC BCRYPT_ECCPUBLIC_BLOB -#endif - -#define KPH_SIGNATURE_MAX_SIZE (128 * 1024) // 128 kB - -typedef ULONG KPH_KEY, *PKPH_KEY; - -typedef enum _KPH_KEY_LEVEL -{ - KphKeyLevel1 = 1, - KphKeyLevel2 = 2 -} KPH_KEY_LEVEL; - -#define KPH_KEY_BACKOFF_TIME ((LONGLONG)(100 * 1000 * 10)) // 100ms - -#define KPH_PROCESS_READ_ACCESS (STANDARD_RIGHTS_READ | SYNCHRONIZE | PROCESS_QUERY_INFORMATION | \ - PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ) -#define KPH_THREAD_READ_ACCESS (STANDARD_RIGHTS_READ | SYNCHRONIZE | THREAD_QUERY_INFORMATION | \ - THREAD_QUERY_LIMITED_INFORMATION | THREAD_GET_CONTEXT) -#define KPH_TOKEN_READ_ACCESS TOKEN_READ - -// Features - -// No features defined. - -// Control codes - -#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_NEITHER, FILE_ANY_ACCESS) - -// General -#define KPH_GETFEATURES KPH_CTL_CODE(0) -#define KPH_VERIFYCLIENT KPH_CTL_CODE(1) -#define KPH_RETRIEVEKEY KPH_CTL_CODE(2) // User-mode only - -// Processes -#define KPH_OPENPROCESS KPH_CTL_CODE(50) // L1/L2 protected API -#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(51) // L1/L2 protected API -#define KPH_OPENPROCESSJOB KPH_CTL_CODE(52) -#define KPH_RESERVED53 KPH_CTL_CODE(53) -#define KPH_RESERVED54 KPH_CTL_CODE(54) -#define KPH_TERMINATEPROCESS KPH_CTL_CODE(55) // L2 protected API -#define KPH_RESERVED56 KPH_CTL_CODE(56) -#define KPH_RESERVED57 KPH_CTL_CODE(57) -#define KPH_READVIRTUALMEMORYUNSAFE KPH_CTL_CODE(58) // L2 protected API -#define KPH_QUERYINFORMATIONPROCESS KPH_CTL_CODE(59) -#define KPH_SETINFORMATIONPROCESS KPH_CTL_CODE(60) - -// Threads -#define KPH_OPENTHREAD KPH_CTL_CODE(100) // L1/L2 protected API -#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(101) -#define KPH_RESERVED102 KPH_CTL_CODE(102) -#define KPH_RESERVED103 KPH_CTL_CODE(103) -#define KPH_RESERVED104 KPH_CTL_CODE(104) -#define KPH_RESERVED105 KPH_CTL_CODE(105) -#define KPH_CAPTURESTACKBACKTRACETHREAD KPH_CTL_CODE(106) -#define KPH_QUERYINFORMATIONTHREAD KPH_CTL_CODE(107) -#define KPH_SETINFORMATIONTHREAD KPH_CTL_CODE(108) - -// Handles -#define KPH_ENUMERATEPROCESSHANDLES KPH_CTL_CODE(150) -#define KPH_QUERYINFORMATIONOBJECT KPH_CTL_CODE(151) -#define KPH_SETINFORMATIONOBJECT KPH_CTL_CODE(152) -#define KPH_RESERVED153 KPH_CTL_CODE(153) - -// Misc. -#define KPH_OPENDRIVER KPH_CTL_CODE(200) -#define KPH_QUERYINFORMATIONDRIVER KPH_CTL_CODE(201) - +#ifndef _KPHAPI_H +#define _KPHAPI_H + +// This file contains KProcessHacker definitions shared across kernel-mode and user-mode. + +// Process information + +typedef enum _KPH_PROCESS_INFORMATION_CLASS +{ + KphProcessReserved1 = 1, + KphProcessReserved2 = 2, + KphProcessReserved3 = 3, + MaxKphProcessInfoClass +} KPH_PROCESS_INFORMATION_CLASS; + +// Thread information + +typedef enum _KPH_THREAD_INFORMATION_CLASS +{ + KphThreadReserved1 = 1, + KphThreadReserved2 = 2, + KphThreadReserved3 = 3, + MaxKphThreadInfoClass +} KPH_THREAD_INFORMATION_CLASS; + +// Process handle information + +typedef struct _KPH_PROCESS_HANDLE +{ + HANDLE Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; + USHORT ObjectTypeIndex; + USHORT Reserved1; + ULONG HandleAttributes; + ULONG Reserved2; +} KPH_PROCESS_HANDLE, *PKPH_PROCESS_HANDLE; + +typedef struct _KPH_PROCESS_HANDLE_INFORMATION +{ + ULONG HandleCount; + KPH_PROCESS_HANDLE Handles[1]; +} KPH_PROCESS_HANDLE_INFORMATION, *PKPH_PROCESS_HANDLE_INFORMATION; + +// Object information + +typedef enum _KPH_OBJECT_INFORMATION_CLASS +{ + KphObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION + KphObjectNameInformation, // q: OBJECT_NAME_INFORMATION + KphObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION + KphObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION + KphObjectProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION + KphObjectThreadBasicInformation, // q: THREAD_BASIC_INFORMATION + KphObjectEtwRegBasicInformation, // q: ETWREG_BASIC_INFORMATION + KphObjectFileObjectInformation, // q: KPH_FILE_OBJECT_INFORMATION + KphObjectFileObjectDriver, // q: KPH_FILE_OBJECT_DRIVER + MaxKphObjectInfoClass +} KPH_OBJECT_INFORMATION_CLASS; + +typedef struct _KPH_FILE_OBJECT_INFORMATION +{ + BOOLEAN LockOperation; + BOOLEAN DeletePending; + BOOLEAN ReadAccess; + BOOLEAN WriteAccess; + BOOLEAN DeleteAccess; + BOOLEAN SharedRead; + BOOLEAN SharedWrite; + BOOLEAN SharedDelete; + LARGE_INTEGER CurrentByteOffset; + ULONG Flags; +} KPH_FILE_OBJECT_INFORMATION, *PKPH_FILE_OBJECT_INFORMATION; + +typedef struct _KPH_FILE_OBJECT_DRIVER +{ + HANDLE DriverHandle; +} KPH_FILE_OBJECT_DRIVER, *PKPH_FILE_OBJECT_DRIVER; + +// Driver information + +typedef enum _DRIVER_INFORMATION_CLASS +{ + DriverBasicInformation, + DriverNameInformation, + DriverServiceKeyNameInformation, + MaxDriverInfoClass +} DRIVER_INFORMATION_CLASS; + +typedef struct _DRIVER_BASIC_INFORMATION +{ + ULONG Flags; + PVOID DriverStart; + ULONG DriverSize; +} DRIVER_BASIC_INFORMATION, *PDRIVER_BASIC_INFORMATION; + +typedef struct _DRIVER_NAME_INFORMATION +{ + UNICODE_STRING DriverName; +} DRIVER_NAME_INFORMATION, *PDRIVER_NAME_INFORMATION; + +typedef struct _DRIVER_SERVICE_KEY_NAME_INFORMATION +{ + UNICODE_STRING ServiceKeyName; +} DRIVER_SERVICE_KEY_NAME_INFORMATION, *PDRIVER_SERVICE_KEY_NAME_INFORMATION; + +// ETW registration object information + +typedef struct _ETWREG_BASIC_INFORMATION +{ + GUID Guid; + ULONG_PTR SessionId; +} ETWREG_BASIC_INFORMATION, *PETWREG_BASIC_INFORMATION; + +// Device + +#define KPH_DEVICE_SHORT_NAME L"KProcessHacker3" +#define KPH_DEVICE_TYPE 0x9999 +#define KPH_DEVICE_NAME (L"\\Device\\" KPH_DEVICE_SHORT_NAME) + +// Parameters + +typedef enum _KPH_SECURITY_LEVEL +{ + KphSecurityNone = 0, // all clients are allowed + KphSecurityPrivilegeCheck = 1, // require SeDebugPrivilege + KphSecuritySignatureCheck = 2, // require trusted signature + KphSecuritySignatureAndPrivilegeCheck = 3, // require trusted signature and SeDebugPrivilege + KphMaxSecurityLevel +} KPH_SECURITY_LEVEL, *PKPH_SECURITY_LEVEL; + +typedef struct _KPH_DYN_STRUCT_DATA +{ + SHORT EgeGuid; // dt nt!_ETW_GUID_ENTRY Guid + SHORT EpObjectTable; // dt nt!_EPROCESS ObjectTable + SHORT Reserved0; + SHORT Reserved1; + SHORT Reserved2; + SHORT EreGuidEntry; // dt nt!_ETW_REG_ENTRY GuidEntry + SHORT HtHandleContentionEvent; // dt nt!_HANDLE_TABLE HandleContentionEvent + SHORT OtName; // dt nt!_OBJECT_ATTRIBUTES ObjectName + SHORT OtIndex; // dt nt!_OBJECT_TYPE Index + SHORT ObDecodeShift; + SHORT ObAttributesShift; // dt nt!_HANDLE_TABLE_ENTRY +} KPH_DYN_STRUCT_DATA, *PKPH_DYN_STRUCT_DATA; + +typedef struct _KPH_DYN_PACKAGE +{ + USHORT MajorVersion; + USHORT MinorVersion; + USHORT ServicePackMajor; // -1 to ignore + USHORT BuildNumber; // -1 to ignore + ULONG ResultingNtVersion; // PHNT_* + KPH_DYN_STRUCT_DATA StructData; +} KPH_DYN_PACKAGE, *PKPH_DYN_PACKAGE; + +#define KPH_DYN_CONFIGURATION_VERSION 3 +#define KPH_DYN_MAXIMUM_PACKAGES 64 + +typedef struct _KPH_DYN_CONFIGURATION +{ + ULONG Version; + ULONG NumberOfPackages; + KPH_DYN_PACKAGE Packages[1]; +} KPH_DYN_CONFIGURATION, *PKPH_DYN_CONFIGURATION; + +// Verification + +#ifdef __BCRYPT_H__ +#define KPH_SIGN_ALGORITHM BCRYPT_ECDSA_P256_ALGORITHM +#define KPH_SIGN_ALGORITHM_BITS 256 +#define KPH_HASH_ALGORITHM BCRYPT_SHA256_ALGORITHM +#define KPH_BLOB_PUBLIC BCRYPT_ECCPUBLIC_BLOB +#endif + +#define KPH_SIGNATURE_MAX_SIZE (128 * 1024) // 128 kB + +typedef ULONG KPH_KEY, *PKPH_KEY; + +typedef enum _KPH_KEY_LEVEL +{ + KphKeyLevel1 = 1, + KphKeyLevel2 = 2 +} KPH_KEY_LEVEL; + +#define KPH_KEY_BACKOFF_TIME ((LONGLONG)(100 * 1000 * 10)) // 100ms + +#define KPH_PROCESS_READ_ACCESS (STANDARD_RIGHTS_READ | SYNCHRONIZE | PROCESS_QUERY_INFORMATION | \ + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ) +#define KPH_THREAD_READ_ACCESS (STANDARD_RIGHTS_READ | SYNCHRONIZE | THREAD_QUERY_INFORMATION | \ + THREAD_QUERY_LIMITED_INFORMATION | THREAD_GET_CONTEXT) +#define KPH_TOKEN_READ_ACCESS TOKEN_READ + +// Features + +// No features defined. + +// Control codes + +#define KPH_CTL_CODE(x) CTL_CODE(KPH_DEVICE_TYPE, 0x800 + x, METHOD_NEITHER, FILE_ANY_ACCESS) + +// General +#define KPH_GETFEATURES KPH_CTL_CODE(0) +#define KPH_VERIFYCLIENT KPH_CTL_CODE(1) +#define KPH_RETRIEVEKEY KPH_CTL_CODE(2) // User-mode only + +// Processes +#define KPH_OPENPROCESS KPH_CTL_CODE(50) // L1/L2 protected API +#define KPH_OPENPROCESSTOKEN KPH_CTL_CODE(51) // L1/L2 protected API +#define KPH_OPENPROCESSJOB KPH_CTL_CODE(52) +#define KPH_RESERVED53 KPH_CTL_CODE(53) +#define KPH_RESERVED54 KPH_CTL_CODE(54) +#define KPH_TERMINATEPROCESS KPH_CTL_CODE(55) // L2 protected API +#define KPH_RESERVED56 KPH_CTL_CODE(56) +#define KPH_RESERVED57 KPH_CTL_CODE(57) +#define KPH_READVIRTUALMEMORYUNSAFE KPH_CTL_CODE(58) // L2 protected API +#define KPH_QUERYINFORMATIONPROCESS KPH_CTL_CODE(59) +#define KPH_SETINFORMATIONPROCESS KPH_CTL_CODE(60) + +// Threads +#define KPH_OPENTHREAD KPH_CTL_CODE(100) // L1/L2 protected API +#define KPH_OPENTHREADPROCESS KPH_CTL_CODE(101) +#define KPH_RESERVED102 KPH_CTL_CODE(102) +#define KPH_RESERVED103 KPH_CTL_CODE(103) +#define KPH_RESERVED104 KPH_CTL_CODE(104) +#define KPH_RESERVED105 KPH_CTL_CODE(105) +#define KPH_CAPTURESTACKBACKTRACETHREAD KPH_CTL_CODE(106) +#define KPH_QUERYINFORMATIONTHREAD KPH_CTL_CODE(107) +#define KPH_SETINFORMATIONTHREAD KPH_CTL_CODE(108) + +// Handles +#define KPH_ENUMERATEPROCESSHANDLES KPH_CTL_CODE(150) +#define KPH_QUERYINFORMATIONOBJECT KPH_CTL_CODE(151) +#define KPH_SETINFORMATIONOBJECT KPH_CTL_CODE(152) +#define KPH_RESERVED153 KPH_CTL_CODE(153) + +// Misc. +#define KPH_OPENDRIVER KPH_CTL_CODE(200) +#define KPH_QUERYINFORMATIONDRIVER KPH_CTL_CODE(201) + #endif \ No newline at end of file diff --git a/phlib/include/kphuser.h b/phlib/include/kphuser.h index 55c90b84db5b..e58b99f9de10 100644 --- a/phlib/include/kphuser.h +++ b/phlib/include/kphuser.h @@ -1,300 +1,307 @@ -#ifndef _PH_KPHUSER_H -#define _PH_KPHUSER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _KPH_PARAMETERS -{ - KPH_SECURITY_LEVEL SecurityLevel; - BOOLEAN CreateDynamicConfiguration; -} KPH_PARAMETERS, *PKPH_PARAMETERS; - -PHLIBAPI -NTSTATUS -NTAPI -KphConnect( - _In_opt_ PWSTR DeviceName - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphConnect2( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphConnect2Ex( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName, - _In_opt_ PKPH_PARAMETERS Parameters - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphDisconnect( - VOID - ); - -PHLIBAPI -BOOLEAN -NTAPI -KphIsConnected( - VOID - ); - -PHLIBAPI -BOOLEAN -NTAPI -KphIsVerified( - VOID - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphSetParameters( - _In_opt_ PWSTR DeviceName, - _In_ PKPH_PARAMETERS Parameters - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphInstall( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphInstallEx( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName, - _In_opt_ PKPH_PARAMETERS Parameters - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphUninstall( - _In_opt_ PWSTR DeviceName - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphGetFeatures( - _Out_ PULONG Features - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphVerifyClient( - _In_reads_bytes_(SignatureSize) PUCHAR Signature, - _In_ ULONG SignatureSize - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphOpenProcess( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PCLIENT_ID ClientId - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphOpenProcessToken( - _In_ HANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE TokenHandle - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphOpenProcessJob( - _In_ HANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE JobHandle - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphTerminateProcess( - _In_ HANDLE ProcessHandle, - _In_ NTSTATUS ExitStatus - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphReadVirtualMemoryUnsafe( - _In_opt_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, - _Out_writes_bytes_(BufferSize) PVOID Buffer, - _In_ SIZE_T BufferSize, - _Out_opt_ PSIZE_T NumberOfBytesRead - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphQueryInformationProcess( - _In_ HANDLE ProcessHandle, - _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, - _In_ ULONG ProcessInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphSetInformationProcess( - _In_ HANDLE ProcessHandle, - _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, - _In_ ULONG ProcessInformationLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphOpenThread( - _Out_ PHANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PCLIENT_ID ClientId - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphOpenThreadProcess( - _In_ HANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE ProcessHandle - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphCaptureStackBackTraceThread( - _In_ HANDLE ThreadHandle, - _In_ ULONG FramesToSkip, - _In_ ULONG FramesToCapture, - _Out_writes_(FramesToCapture) PVOID *BackTrace, - _Out_opt_ PULONG CapturedFrames, - _Out_opt_ PULONG BackTraceHash - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphQueryInformationThread( - _In_ HANDLE ThreadHandle, - _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, - _In_ ULONG ThreadInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphSetInformationThread( - _In_ HANDLE ThreadHandle, - _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, - _In_ ULONG ThreadInformationLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphEnumerateProcessHandles( - _In_ HANDLE ProcessHandle, - _Out_writes_bytes_(BufferLength) PVOID Buffer, - _In_opt_ ULONG BufferLength, - _Out_opt_ PULONG ReturnLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphEnumerateProcessHandles2( - _In_ HANDLE ProcessHandle, - _Out_ PKPH_PROCESS_HANDLE_INFORMATION *Handles - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphQueryInformationObject( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - _Out_writes_bytes_(ObjectInformationLength) PVOID ObjectInformation, - _In_ ULONG ObjectInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphSetInformationObject( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - _In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation, - _In_ ULONG ObjectInformationLength - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphOpenDriver( - _Out_ PHANDLE DriverHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ POBJECT_ATTRIBUTES ObjectAttributes - ); - -PHLIBAPI -NTSTATUS -NTAPI -KphQueryInformationDriver( - _In_ HANDLE DriverHandle, - _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, - _Out_writes_bytes_(DriverInformationLength) PVOID DriverInformation, - _In_ ULONG DriverInformationLength, - _Out_opt_ PULONG ReturnLength - ); - -// kphdata - -PHLIBAPI -NTSTATUS -NTAPI -KphInitializeDynamicPackage( - _Out_ PKPH_DYN_PACKAGE Package - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_KPHUSER_H +#define _PH_KPHUSER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _KPH_PARAMETERS +{ + KPH_SECURITY_LEVEL SecurityLevel; + BOOLEAN CreateDynamicConfiguration; +} KPH_PARAMETERS, *PKPH_PARAMETERS; + +PHLIBAPI +NTSTATUS +NTAPI +KphConnect( + _In_opt_ PWSTR DeviceName + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphConnect2( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphConnect2Ex( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName, + _In_opt_ PKPH_PARAMETERS Parameters + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphDisconnect( + VOID + ); + +PHLIBAPI +BOOLEAN +NTAPI +KphIsConnected( + VOID + ); + +PHLIBAPI +BOOLEAN +NTAPI +KphIsVerified( + VOID + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphSetParameters( + _In_opt_ PWSTR DeviceName, + _In_ PKPH_PARAMETERS Parameters + ); + +PHLIBAPI +VOID +NTAPI +KphSetServiceSecurity( + _In_ SC_HANDLE ServiceHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphInstall( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphInstallEx( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName, + _In_opt_ PKPH_PARAMETERS Parameters + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphUninstall( + _In_opt_ PWSTR DeviceName + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphGetFeatures( + _Out_ PULONG Features + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphVerifyClient( + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphOpenProcessJob( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE JobHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphTerminateProcess( + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphReadVirtualMemoryUnsafe( + _In_opt_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_writes_bytes_(BufferSize) PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphSetInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphOpenThreadProcess( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE ProcessHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphCaptureStackBackTraceThread( + _In_ HANDLE ThreadHandle, + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG CapturedFrames, + _Out_opt_ PULONG BackTraceHash + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphSetInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphEnumerateProcessHandles( + _In_ HANDLE ProcessHandle, + _Out_writes_bytes_(BufferLength) PVOID Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphEnumerateProcessHandles2( + _In_ HANDLE ProcessHandle, + _Out_ PKPH_PROCESS_HANDLE_INFORMATION *Handles + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphQueryInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_writes_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphSetInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphOpenDriver( + _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +PHLIBAPI +NTSTATUS +NTAPI +KphQueryInformationDriver( + _In_ HANDLE DriverHandle, + _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, + _Out_writes_bytes_(DriverInformationLength) PVOID DriverInformation, + _In_ ULONG DriverInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +// kphdata + +PHLIBAPI +NTSTATUS +NTAPI +KphInitializeDynamicPackage( + _Out_ PKPH_DYN_PACKAGE Package + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/lsasup.h b/phlib/include/lsasup.h index 3784f413bd6d..d5a07d7d5c0c 100644 --- a/phlib/include/lsasup.h +++ b/phlib/include/lsasup.h @@ -55,6 +55,15 @@ PhLookupSid( _Out_opt_ PSID_NAME_USE NameUse ); +PHLIBAPI +VOID +NTAPI +PhLookupSids( + _In_ ULONG Count, + _In_ PSID *Sids, + _Out_ PPH_STRING **FullNames + ); + PHLIBAPI NTSTATUS NTAPI @@ -81,6 +90,36 @@ PhSidToStringSid( _In_ PSID Sid ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetTokenUserString( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN IncludeDomain + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetAccountPrivileges( + _In_ PSID AccountSid, + _Out_ PTOKEN_PRIVILEGES* Privileges + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetCapabilitySidName( + _In_ PSID CapabilitySid + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetCapabilityGuidName( + _In_ PPH_STRING GuidString + ); + #ifdef __cplusplus } #endif diff --git a/phlib/include/mapimg.h b/phlib/include/mapimg.h index dca55b9b2274..5f85b2528229 100644 --- a/phlib/include/mapimg.h +++ b/phlib/include/mapimg.h @@ -5,15 +5,38 @@ extern "C" { #endif +#include + typedef struct _PH_MAPPED_IMAGE { + USHORT Signature; PVOID ViewBase; SIZE_T Size; - PIMAGE_NT_HEADERS NtHeaders; - ULONG NumberOfSections; - PIMAGE_SECTION_HEADER Sections; - USHORT Magic; + union + { + struct + { + union + { + PIMAGE_NT_HEADERS32 NtHeaders32; + PIMAGE_NT_HEADERS NtHeaders; + }; + + ULONG NumberOfSections; + PIMAGE_SECTION_HEADER Sections; + USHORT Magic; + }; + struct + { + struct _ELF_IMAGE_HEADER *Header; + union + { + struct _ELF_IMAGE_HEADER32 *Headers32; + struct _ELF_IMAGE_HEADER64 *Headers64; + }; + }; + }; } PH_MAPPED_IMAGE, *PPH_MAPPED_IMAGE; PHLIBAPI @@ -35,6 +58,16 @@ PhLoadMappedImage( _Out_ PPH_MAPPED_IMAGE MappedImage ); +PHLIBAPI +NTSTATUS +NTAPI +PhLoadMappedImageEx( + _In_opt_ PWSTR FileName, + _In_opt_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _Out_ PPH_MAPPED_IMAGE MappedImage + ); + PHLIBAPI NTSTATUS NTAPI @@ -70,12 +103,21 @@ PhMappedImageRvaToVa( _Out_opt_ PIMAGE_SECTION_HEADER *Section ); +PHLIBAPI +PVOID +NTAPI +PhMappedImageVaToVa( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ ULONG Va, + _Out_opt_ PIMAGE_SECTION_HEADER* Section + ); + PHLIBAPI BOOLEAN NTAPI PhGetMappedImageSectionName( _In_ PIMAGE_SECTION_HEADER Section, - _Out_writes_opt_z_(Count) PSTR Buffer, + _Out_writes_opt_z_(Count) PWSTR Buffer, _In_ ULONG Count, _Out_opt_ PULONG ReturnCount ); @@ -123,6 +165,23 @@ PhLoadRemoteMappedImage( _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ); +typedef NTSTATUS (NTAPI *PPH_READ_VIRTUAL_MEMORY_CALLBACK)( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _Out_writes_bytes_(BufferSize) PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead + ); + +NTSTATUS +NTAPI +PhLoadRemoteMappedImageEx( + _In_ HANDLE ProcessHandle, + _In_ PVOID ViewBase, + _In_ PPH_READ_VIRTUAL_MEMORY_CALLBACK ReadVirtualMemoryCallback, + _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage + ); + NTSTATUS NTAPI PhUnloadRemoteMappedImage( @@ -144,6 +203,7 @@ typedef struct _PH_MAPPED_IMAGE_EXPORTS typedef struct _PH_MAPPED_IMAGE_EXPORT_ENTRY { USHORT Ordinal; + ULONG Hint; PSTR Name; } PH_MAPPED_IMAGE_EXPORT_ENTRY, *PPH_MAPPED_IMAGE_EXPORT_ENTRY; @@ -153,7 +213,6 @@ typedef struct _PH_MAPPED_IMAGE_EXPORT_FUNCTION PSTR ForwardedName; } PH_MAPPED_IMAGE_EXPORT_FUNCTION, *PPH_MAPPED_IMAGE_EXPORT_FUNCTION; - PHLIBAPI NTSTATUS NTAPI @@ -193,6 +252,7 @@ PhGetMappedImageExportFunctionRemote( ); #define PH_MAPPED_IMAGE_DELAY_IMPORTS 0x1 +#define PH_MAPPED_IMAGE_DELAY_IMPORTS_V1 0x2 typedef struct _PH_MAPPED_IMAGE_IMPORTS { @@ -203,7 +263,7 @@ typedef struct _PH_MAPPED_IMAGE_IMPORTS union { PIMAGE_IMPORT_DESCRIPTOR DescriptorTable; - PVOID DelayDescriptorTable; + PIMAGE_DELAYLOAD_DESCRIPTOR DelayDescriptorTable; }; } PH_MAPPED_IMAGE_IMPORTS, *PPH_MAPPED_IMAGE_IMPORTS; @@ -217,9 +277,9 @@ typedef struct _PH_MAPPED_IMAGE_IMPORT_DLL union { PIMAGE_IMPORT_DESCRIPTOR Descriptor; - PVOID DelayDescriptor; + PIMAGE_DELAYLOAD_DESCRIPTOR DelayDescriptor; }; - PVOID *LookupTable; + PVOID LookupTable; } PH_MAPPED_IMAGE_IMPORT_DLL, *PPH_MAPPED_IMAGE_IMPORT_DLL; typedef struct _PH_MAPPED_IMAGE_IMPORT_ENTRY @@ -281,6 +341,133 @@ PhCheckSumMappedImage( _In_ PPH_MAPPED_IMAGE MappedImage ); +typedef struct _IMAGE_CFG_ENTRY +{ + ULONG Rva; + struct + { + BOOLEAN SuppressedCall : 1; + BOOLEAN Reserved : 7; + }; +} IMAGE_CFG_ENTRY, *PIMAGE_CFG_ENTRY; + +typedef struct _PH_MAPPED_IMAGE_CFG +{ + PPH_MAPPED_IMAGE MappedImage; + ULONG EntrySize; + + union + { + ULONG GuardFlags; + struct + { + ULONG CfgInstrumented : 1; + ULONG WriteIntegrityChecks : 1; + ULONG CfgFunctionTablePresent : 1; + ULONG SecurityCookieUnused : 1; + ULONG ProtectDelayLoadedIat : 1; + ULONG DelayLoadInDidatSection : 1; + ULONG HasExportSuppressionInfos : 1; + ULONG EnableExportSuppression : 1; + ULONG CfgLongJumpTablePresent : 1; + ULONG Spare : 23; + }; + }; + + PULONGLONG GuardFunctionTable; + ULONGLONG NumberOfGuardFunctionEntries; + + PULONGLONG GuardAdressIatTable; // not currently used + ULONGLONG NumberOfGuardAdressIatEntries; + + PULONGLONG GuardLongJumpTable; // not currently used + ULONGLONG NumberOfGuardLongJumpEntries; +} PH_MAPPED_IMAGE_CFG, *PPH_MAPPED_IMAGE_CFG; + +typedef enum _CFG_ENTRY_TYPE +{ + ControlFlowGuardFunction, + ControlFlowGuardtakenIatEntry, + ControlFlowGuardLongJump +} CFG_ENTRY_TYPE; + +PHLIBAPI +NTSTATUS +NTAPI +PhGetMappedImageCfg( + _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, + _In_ PPH_MAPPED_IMAGE MappedImage + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetMappedImageCfgEntry( + _In_ PPH_MAPPED_IMAGE_CFG CfgConfig, + _In_ ULONGLONG Index, + _In_ CFG_ENTRY_TYPE Type, + _Out_ PIMAGE_CFG_ENTRY Entry + ); + +typedef struct _PH_IMAGE_RESOURCE_ENTRY +{ + ULONG_PTR Type; + ULONG_PTR Name; + ULONG_PTR Language; + ULONG Size; + PVOID Data; +} PH_IMAGE_RESOURCE_ENTRY, *PPH_IMAGE_RESOURCE_ENTRY; + +typedef struct _PH_MAPPED_IMAGE_RESOURCES +{ + PPH_MAPPED_IMAGE MappedImage; + PIMAGE_DATA_DIRECTORY DataDirectory; + PIMAGE_RESOURCE_DIRECTORY ResourceDirectory; + + ULONG NumberOfEntries; + PPH_IMAGE_RESOURCE_ENTRY ResourceEntries; +} PH_MAPPED_IMAGE_RESOURCES, *PPH_MAPPED_IMAGE_RESOURCES; + +PHLIBAPI +NTSTATUS +NTAPI +PhGetMappedImageResources( + _Out_ PPH_MAPPED_IMAGE_RESOURCES Resources, + _In_ PPH_MAPPED_IMAGE MappedImage + ); + +typedef struct _PH_IMAGE_TLS_CALLBACK_ENTRY +{ + ULONGLONG Index; + ULONGLONG Address; +} PH_IMAGE_TLS_CALLBACK_ENTRY, *PPH_IMAGE_TLS_CALLBACK_ENTRY; + +typedef struct _PH_MAPPED_IMAGE_TLS_CALLBACKS +{ + PPH_MAPPED_IMAGE MappedImage; + PIMAGE_DATA_DIRECTORY DataDirectory; + + union + { + PIMAGE_TLS_DIRECTORY32 TlsDirectory32; + PIMAGE_TLS_DIRECTORY64 TlsDirectory64; + }; + + PVOID CallbackIndexes; + PVOID CallbackAddress; + + ULONG NumberOfEntries; + PPH_IMAGE_TLS_CALLBACK_ENTRY Entries; +} PH_MAPPED_IMAGE_TLS_CALLBACKS, *PPH_MAPPED_IMAGE_TLS_CALLBACKS; + +PHLIBAPI +NTSTATUS +NTAPI +PhGetMappedImageTlsCallbacks( + _Out_ PPH_MAPPED_IMAGE_TLS_CALLBACKS TlsCallbacks, + _In_ PPH_MAPPED_IMAGE MappedImage + ); + // maplib struct _PH_MAPPED_ARCHIVE; @@ -382,72 +569,79 @@ PhGetMappedArchiveImportEntry( _Out_ PPH_MAPPED_ARCHIVE_IMPORT_ENTRY Entry ); -typedef struct _IMAGE_CFG_ENTRY -{ - ULONG Rva; - struct - { - BOOLEAN SuppressedCall : 1; - BOOLEAN Reserved : 7; - }; -} IMAGE_CFG_ENTRY, *PIMAGE_CFG_ENTRY; +// ELF binary support -typedef struct _PH_MAPPED_IMAGE_CFG +NTSTATUS PhInitializeMappedWslImage( + _Out_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ PVOID ViewBase, + _In_ SIZE_T Size + ); + +ULONG64 PhGetMappedWslImageBaseAddress( + _In_ PPH_MAPPED_IMAGE MappedWslImage + ); + +typedef struct _PH_ELF_IMAGE_SECTION { - PPH_MAPPED_IMAGE MappedImage; - ULONG EntrySize; + UINT32 Type; + ULONGLONG Flags; + ULONGLONG Address; + ULONGLONG Offset; + ULONGLONG Size; + WCHAR Name[MAX_PATH]; +} PH_ELF_IMAGE_SECTION, *PPH_ELF_IMAGE_SECTION; + +BOOLEAN PhGetMappedWslImageSections( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ USHORT *NumberOfSections, + _Out_ PPH_ELF_IMAGE_SECTION *ImageSections + ); +typedef struct _PH_ELF_IMAGE_SYMBOL_ENTRY +{ union { - ULONG GuardFlags; + BOOLEAN Flags; struct { - ULONG CfgInstrumented : 1; - ULONG WriteIntegrityChecks : 1; - ULONG CfgFunctionTablePresent : 1; - ULONG SecurityCookieUnused : 1; - ULONG ProtectDelayLoadedIat : 1; - ULONG DelayLoadInDidatSection : 1; - ULONG HasExportSuppressionInfos : 1; - ULONG EnableExportSuppression : 1; - ULONG CfgLongJumpTablePresent : 1; - ULONG Spare : 23; + BOOLEAN ImportSymbol : 1; + BOOLEAN ExportSymbol : 1; + BOOLEAN UnknownSymbol : 1; + BOOLEAN Spare : 5; }; }; + UCHAR TypeInfo; + UCHAR OtherInfo; + ULONG SectionIndex; + ULONGLONG Address; + ULONGLONG Size; + WCHAR Name[MAX_PATH * 2]; + WCHAR Module[MAX_PATH * 2]; +} PH_ELF_IMAGE_SYMBOL_ENTRY, *PPH_ELF_IMAGE_SYMBOL_ENTRY; + +BOOLEAN PhGetMappedWslImageSymbols( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ PPH_LIST *ImageSymbols + ); - PULONGLONG GuardFunctionTable; - ULONGLONG NumberOfGuardFunctionEntries; - - PULONGLONG GuardAdressIatTable; // not currently used - ULONGLONG NumberOfGuardAdressIatEntries; - - PULONGLONG GuardLongJumpTable; // not currently used - ULONGLONG NumberOfGuardLongJumpEntries; -} PH_MAPPED_IMAGE_CFG, *PPH_MAPPED_IMAGE_CFG; +VOID PhFreeMappedWslImageSymbols( + _In_ PPH_LIST ImageSymbols + ); -typedef enum _CFG_ENTRY_TYPE +typedef struct _PH_ELF_IMAGE_DYNAMIC_ENTRY { - ControlFlowGuardFunction, - ControlFlowGuardtakenIatEntry, - ControlFlowGuardLongJump -} CFG_ENTRY_TYPE; - -PHLIBAPI -NTSTATUS -NTAPI -PhGetMappedImageCfg( - _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, - _In_ PPH_MAPPED_IMAGE MappedImage + LONGLONG Tag; + PWSTR Type; + PPH_STRING Value; +} PH_ELF_IMAGE_DYNAMIC_ENTRY, *PPH_ELF_IMAGE_DYNAMIC_ENTRY; + +BOOLEAN PhGetMappedWslImageDynamic( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ PPH_LIST *DynamicSymbols ); -PHLIBAPI -NTSTATUS -NTAPI -PhGetMappedImageCfgEntry( - _In_ PPH_MAPPED_IMAGE_CFG CfgConfig, - _In_ ULONGLONG Index, - _In_ CFG_ENTRY_TYPE Type, - _Out_ PIMAGE_CFG_ENTRY Entry +VOID PhFreeMappedWslImageDynamic( + _In_ PPH_LIST ImageDynamic ); #ifdef __cplusplus diff --git a/phlib/include/ph.h b/phlib/include/ph.h index eba1c5032fd1..f64117102381 100644 --- a/phlib/include/ph.h +++ b/phlib/include/ph.h @@ -1,11 +1,9 @@ -#ifndef _PH_PH_H -#define _PH_PH_H - -#pragma once - -#include -#include -#include -#include - -#endif +#ifndef _PH_PH_H +#define _PH_PH_H + +#include +#include +#include +#include + +#endif diff --git a/phlib/include/phbase.h b/phlib/include/phbase.h index 9dd78bc0b8f7..910eaff0b60d 100644 --- a/phlib/include/phbase.h +++ b/phlib/include/phbase.h @@ -1,37 +1,36 @@ -#ifndef _PH_PHBASE_H -#define _PH_PHBASE_H - -#pragma once - -#ifndef PHLIB_NO_DEFAULT_LIB -#pragma comment(lib, "ntdll.lib") -#pragma comment(lib, "comctl32.lib") -#pragma comment(lib, "version.lib") -#endif - -#ifndef UNICODE -#define UNICODE -#endif - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#if !defined(_PHLIB_) -#define PHLIBAPI __declspec(dllimport) -#else -#define PHLIBAPI -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#endif \ No newline at end of file +#ifndef _PH_PHBASE_H +#define _PH_PHBASE_H + +#ifndef PHLIB_NO_DEFAULT_LIB +#pragma comment(lib, "ntdll.lib") +#pragma comment(lib, "comctl32.lib") +#pragma comment(lib, "version.lib") +#endif + +#ifndef UNICODE +#define UNICODE +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#if !defined(_PHLIB_) +#define PHLIBAPI __declspec(dllimport) +#else +#define PHLIBAPI +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#endif diff --git a/phlib/include/phbasesup.h b/phlib/include/phbasesup.h index f59e12f07c38..c710b5cff8e5 100644 --- a/phlib/include/phbasesup.h +++ b/phlib/include/phbasesup.h @@ -40,62 +40,22 @@ PhCreateThread( _In_opt_ PVOID Parameter ); -// DLLs - -FORCEINLINE -PVOID -PhGetDllHandle( - _In_ PWSTR DllName - ) -{ - UNICODE_STRING dllName; - PVOID dllHandle; - - RtlInitUnicodeString(&dllName, DllName); - - if (NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &dllName, &dllHandle))) - return dllHandle; - else - return NULL; -} - -FORCEINLINE -PVOID -PhGetProcedureAddress( - _In_ PVOID DllHandle, - _In_opt_ PSTR ProcedureName, - _In_opt_ ULONG ProcedureNumber - ) -{ - NTSTATUS status; - ANSI_STRING procedureName; - PVOID procedureAddress; - - if (ProcedureName) - { - RtlInitAnsiString(&procedureName, ProcedureName); - status = LdrGetProcedureAddress( - DllHandle, - &procedureName, - 0, - &procedureAddress - ); - } - else - { - status = LdrGetProcedureAddress( - DllHandle, - NULL, - ProcedureNumber, - &procedureAddress - ); - } - - if (!NT_SUCCESS(status)) - return NULL; +PHLIBAPI +NTSTATUS +NTAPI +PhCreateThreadEx( + _Out_ PHANDLE ThreadHandle, + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter + ); - return procedureAddress; -} +PHLIBAPI +NTSTATUS +NTAPI +PhCreateThread2( + _In_ PUSER_THREAD_START_ROUTINE StartAddress, + _In_opt_ PVOID Parameter + ); // Misc. system @@ -129,6 +89,27 @@ PhLocalTimeToSystemTime( _Out_ PLARGE_INTEGER SystemTime ); +FORCEINLINE +NTSTATUS +NTAPI +PhDelayExecution( + _In_ LONGLONG Interval + ) +{ + if (Interval == INFINITE) + { + return NtDelayExecution(FALSE, NULL); + } + else + { + LARGE_INTEGER interval; + + interval.QuadPart = -(LONGLONG)UInt32x32To64(Interval, PH_TIMEOUT_MS); + + return NtDelayExecution(FALSE, &interval); + } +} + // Heap _May_raise_ @@ -170,7 +151,7 @@ PHLIBAPI PVOID NTAPI PhReAllocate( - _Frees_ptr_ PVOID Memory, + _Frees_ptr_opt_ PVOID Memory, _In_ SIZE_T Size ); @@ -214,6 +195,20 @@ PhAllocateCopy( return copy; } +FORCEINLINE +PVOID +PhAllocateZero( + _In_ SIZE_T Size + ) +{ + PVOID buffer; + + buffer = PhAllocate(Size); + memset(buffer, 0, Size); + + return buffer; +} + // Event #define PH_EVENT_SET 0x1 @@ -747,8 +742,8 @@ typedef struct _PH_RELATIVE_BYTESREF ULONG Offset; } PH_RELATIVE_BYTESREF, *PPH_RELATIVE_BYTESREF, PH_RELATIVE_STRINGREF, *PPH_RELATIVE_STRINGREF; -#define PH_STRINGREF_INIT(String) { sizeof(String) - sizeof(WCHAR), (String) } -#define PH_BYTESREF_INIT(String) { sizeof(String) - sizeof(CHAR), (String) } +#define PH_STRINGREF_INIT(String) { sizeof(String) - sizeof(UNICODE_NULL), (String) } +#define PH_BYTESREF_INIT(String) { sizeof(String) - sizeof(ANSI_NULL), (String) } FORCEINLINE VOID @@ -801,7 +796,7 @@ PhStringRefToUnicodeString( ) { UnicodeString->Length = (USHORT)String->Length; - UnicodeString->MaximumLength = (USHORT)String->Length; + UnicodeString->MaximumLength = (USHORT)String->Length + sizeof(UNICODE_NULL); UnicodeString->Buffer = String->Buffer; return String->Length <= UNICODE_STRING_MAX_BYTES; @@ -1003,7 +998,7 @@ PhEndsWithStringRef( if (Suffix->Length > String->Length) return FALSE; - sr.Buffer = (PWCHAR)((PCHAR)String->Buffer + String->Length - Suffix->Length); + sr.Buffer = (PWCHAR)PTR_ADD_OFFSET(String->Buffer, String->Length - Suffix->Length); sr.Length = Suffix->Length; return PhEqualStringRef(&sr, Suffix, IgnoreCase); @@ -1031,7 +1026,7 @@ PhSkipStringRef( _In_ LONG_PTR Length ) { - String->Buffer = (PWCH)((PCHAR)String->Buffer + Length); + String->Buffer = (PWCH)PTR_ADD_OFFSET(String->Buffer, Length); String->Length -= Length; } @@ -1106,12 +1101,22 @@ PhCreateStringEx( _In_ SIZE_T Length ); +PHLIBAPI +PPH_STRING +NTAPI +PhReferenceEmptyString( + VOID + ); + FORCEINLINE PPH_STRING PhCreateString2( _In_ PPH_STRINGREF String ) { + if (String->Length == 0) + return PhReferenceEmptyString(); + return PhCreateStringEx(String->Buffer, String->Length); } @@ -1121,16 +1126,12 @@ PhCreateStringFromUnicodeString( _In_ PUNICODE_STRING UnicodeString ) { + if (UnicodeString->Length == 0) + return PhReferenceEmptyString(); + return PhCreateStringEx(UnicodeString->Buffer, UnicodeString->Length); } -PHLIBAPI -PPH_STRING -NTAPI -PhReferenceEmptyString( - VOID - ); - PHLIBAPI PPH_STRING NTAPI @@ -1173,6 +1174,20 @@ PhConcatStringRef3( _In_ PPH_STRINGREF String3 ); +FORCEINLINE +PPH_STRING +PhConcatStringRefZ( + _In_ PPH_STRINGREF String1, + _In_ PWSTR String2 + ) +{ + PH_STRINGREF string; + + PhInitializeStringRef(&string, String2); + + return PhConcatStringRef2(String1, &string); +} + PHLIBAPI PPH_STRING NTAPI @@ -1362,11 +1377,35 @@ PhCompareStringWithNull( } else if (!String1) { - return !String2 ? 0 : -1; + return !String2 ? 0 : 1; } else { - return 1; + return -1; + } +} + +// dmex: Compares two strings, always sorting NULL strings after all other strings. +FORCEINLINE +LONG +PhCompareStringWithNullSortOrder( + _In_opt_ PPH_STRING String1, + _In_opt_ PPH_STRING String2, + _In_ PH_SORT_ORDER Order, + _In_ BOOLEAN IgnoreCase + ) +{ + if (String1 && String2) + { + return PhCompareString(String1, String2, IgnoreCase); + } + else if (!String1) + { + return !String2 ? 0 : (Order == AscendingSortOrder ? 1 : -1); + } + else + { + return (Order == AscendingSortOrder ? -1 : 1); } } @@ -2211,7 +2250,7 @@ PhItemArray( _In_ SIZE_T Index ) { - return (PCHAR)Array->Items + Index * Array->ItemSize; + return PTR_ADD_OFFSET(Array->Items, Index * Array->ItemSize); } PHLIBAPI @@ -2816,7 +2855,7 @@ typedef struct _PH_HASHTABLE ULONG NextEntry; } PH_HASHTABLE, *PPH_HASHTABLE; -#define PH_HASHTABLE_ENTRY_SIZE(InnerSize) (FIELD_OFFSET(PH_HASHTABLE_ENTRY, Body) + (InnerSize)) +#define PH_HASHTABLE_ENTRY_SIZE(InnerSize) (UFIELD_OFFSET(PH_HASHTABLE_ENTRY, Body) + (InnerSize)) #define PH_HASHTABLE_GET_ENTRY(Hashtable, Index) \ ((PPH_HASHTABLE_ENTRY)PTR_ADD_OFFSET((Hashtable)->Entries, \ PH_HASHTABLE_ENTRY_SIZE((Hashtable)->EntrySize) * (Index))) @@ -2900,7 +2939,7 @@ PhBeginEnumHashtable( ) { Context->Current = (ULONG_PTR)Hashtable->Entries; - Context->Step = PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize); + Context->Step = PH_HASHTABLE_ENTRY_SIZE((ULONG_PTR)Hashtable->EntrySize); Context->End = Context->Current + (ULONG_PTR)Hashtable->NextEntry * Context->Step; } @@ -2917,7 +2956,7 @@ PhNextEnumHashtable( entry = (PPH_HASHTABLE_ENTRY)Context->Current; Context->Current += Context->Step; - if (entry->HashCode != -1) + if (entry->HashCode != ULONG_MAX) return &entry->Body; } @@ -3234,6 +3273,13 @@ PhHexStringToBuffer( _Out_writes_bytes_(String->Length / sizeof(WCHAR) / 2) PUCHAR Buffer ); +PHLIBAPI +PPH_STRING +NTAPI +PhHexStringToBufferEx( + _In_ PPH_STRINGREF String + ); + PHLIBAPI PPH_STRING NTAPI @@ -3534,6 +3580,7 @@ typedef struct _PH_FORMAT #define PhInitFormatX(f, v) do { (f)->Type = UInt32FormatType | FormatUseRadix; (f)->u.UInt32 = (v); (f)->Radix = 16; } while (0) #define PhInitFormatI64D(f, v) do { (f)->Type = Int64FormatType; (f)->u.Int64 = (v); } while (0) #define PhInitFormatI64U(f, v) do { (f)->Type = UInt64FormatType; (f)->u.UInt64 = (v); } while (0) +#define PhInitFormatI64UGroupDigits(f, v) do { (f)->Type = UInt64FormatType | FormatGroupDigits; (f)->u.UInt64 = (v); } while (0) #define PhInitFormatI64X(f, v) do { (f)->Type = UInt64FormatType | FormatUseRadix; (f)->u.UInt64 = (v); (f)->Radix = 16; } while (0) #define PhInitFormatIU(f, v) do { (f)->Type = UIntPtrFormatType; (f)->u.UIntPtr = (v); } while (0) #define PhInitFormatIX(f, v) do { (f)->Type = UIntPtrFormatType | FormatUseRadix; (f)->u.UIntPtr = (v); (f)->Radix = 16; } while (0) diff --git a/phlib/include/phconfig.h b/phlib/include/phconfig.h index 75892b856fe8..a3c573d96076 100644 --- a/phlib/include/phconfig.h +++ b/phlib/include/phconfig.h @@ -7,8 +7,7 @@ extern "C" { #define _User_set_ -PHLIBAPI extern _User_set_ PVOID PhLibImageBase; - +PHLIBAPI extern _User_set_ PVOID PhInstanceHandle; PHLIBAPI extern _User_set_ PWSTR PhApplicationName; PHLIBAPI extern _User_set_ ULONG PhGlobalDpi; PHLIBAPI extern PVOID PhHeapHandle; @@ -24,22 +23,21 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define WINDOWS_ANCIENT 0 #define WINDOWS_XP 51 -#define WINDOWS_SERVER_2003 52 #define WINDOWS_VISTA 60 #define WINDOWS_7 61 #define WINDOWS_8 62 #define WINDOWS_8_1 63 -#define WINDOWS_10 100 -#define WINDOWS_NEW MAXLONG +#define WINDOWS_10 100 // TH1 +#define WINDOWS_10_TH2 101 +#define WINDOWS_10_RS1 102 +#define WINDOWS_10_RS2 103 +#define WINDOWS_10_RS3 104 +#define WINDOWS_10_RS4 105 +#define WINDOWS_10_RS5 106 +#define WINDOWS_10_RS6 107 +#define WINDOWS_NEW ULONG_MAX -#define WINDOWS_HAS_CONSOLE_HOST (WindowsVersion >= WINDOWS_7) -#define WINDOWS_HAS_CYCLE_TIME (WindowsVersion >= WINDOWS_VISTA) -#define WINDOWS_HAS_IFILEDIALOG (WindowsVersion >= WINDOWS_VISTA) -#define WINDOWS_HAS_IMAGE_FILE_NAME_BY_PROCESS_ID (WindowsVersion >= WINDOWS_VISTA) #define WINDOWS_HAS_IMMERSIVE (WindowsVersion >= WINDOWS_8) -#define WINDOWS_HAS_LIMITED_ACCESS (WindowsVersion >= WINDOWS_VISTA) -#define WINDOWS_HAS_SERVICE_TAGS (WindowsVersion >= WINDOWS_VISTA) -#define WINDOWS_HAS_UAC (WindowsVersion >= WINDOWS_VISTA) // Debugging @@ -59,14 +57,11 @@ PHLIBAPI extern ACCESS_MASK ThreadAllAccess; #define PHLIB_INIT_MODULE_RESERVED1 0x1 #define PHLIB_INIT_MODULE_RESERVED2 0x2 -/** Needed to use work queues. */ #define PHLIB_INIT_MODULE_RESERVED3 0x4 #define PHLIB_INIT_MODULE_RESERVED4 0x8 -/** Needed to use file streams. */ -#define PHLIB_INIT_MODULE_FILE_STREAM 0x10 -/** Needed to use symbol providers. */ -#define PHLIB_INIT_MODULE_SYMBOL_PROVIDER 0x20 -#define PHLIB_INIT_MODULE_RESERVED5 0x40 +#define PHLIB_INIT_MODULE_RESERVED5 0x10 +#define PHLIB_INIT_MODULE_RESERVED6 0x20 +#define PHLIB_INIT_MODULE_RESERVED7 0x40 PHLIBAPI NTSTATUS @@ -79,28 +74,19 @@ PHLIBAPI NTSTATUS NTAPI PhInitializePhLibEx( + _In_ PWSTR Name, _In_ ULONG Flags, + _In_ PVOID ImageBaseAddress, _In_opt_ SIZE_T HeapReserveSize, _In_opt_ SIZE_T HeapCommitSize ); -#ifdef _WIN64 -FORCEINLINE -BOOLEAN -PhIsExecutingInWow64( - VOID - ) -{ - return FALSE; -} -#else PHLIBAPI BOOLEAN NTAPI PhIsExecutingInWow64( VOID ); -#endif #ifdef __cplusplus } diff --git a/phlib/include/phintrnl.h b/phlib/include/phintrnl.h index 55f6629232b4..2fe39cfed1e2 100644 --- a/phlib/include/phintrnl.h +++ b/phlib/include/phintrnl.h @@ -1,49 +1,49 @@ -#ifndef _PH_PHINTRNL_H -#define _PH_PHINTRNL_H - -typedef struct _PHLIB_STATISTICS_BLOCK -{ - // basesup - ULONG BaseThreadsCreated; - ULONG BaseThreadsCreateFailed; - ULONG BaseStringBuildersCreated; - ULONG BaseStringBuildersResized; - - // ref - ULONG RefObjectsCreated; - ULONG RefObjectsDestroyed; - ULONG RefObjectsAllocated; - ULONG RefObjectsFreed; - ULONG RefObjectsAllocatedFromSmallFreeList; - ULONG RefObjectsFreedToSmallFreeList; - ULONG RefObjectsAllocatedFromTypeFreeList; - ULONG RefObjectsFreedToTypeFreeList; - ULONG RefObjectsDeleteDeferred; - ULONG RefAutoPoolsCreated; - ULONG RefAutoPoolsDestroyed; - ULONG RefAutoPoolsDynamicAllocated; - ULONG RefAutoPoolsDynamicResized; - - // queuedlock - ULONG QlBlockSpins; - ULONG QlBlockWaits; - ULONG QlAcquireExclusiveBlocks; - ULONG QlAcquireSharedBlocks; - - // workqueue - ULONG WqWorkQueueThreadsCreated; - ULONG WqWorkQueueThreadsCreateFailed; - ULONG WqWorkItemsQueued; -} PHLIB_STATISTICS_BLOCK; - -#ifdef DEBUG -extern PHLIB_STATISTICS_BLOCK PhLibStatisticsBlock; -#endif - -#ifdef DEBUG -#define PHLIB_INC_STATISTIC(Name) (_InterlockedIncrement(&PhLibStatisticsBlock.Name)) -#else -#define PHLIB_INC_STATISTIC(Name) -#endif - -#endif +#ifndef _PH_PHINTRNL_H +#define _PH_PHINTRNL_H + +typedef struct _PHLIB_STATISTICS_BLOCK +{ + // basesup + ULONG BaseThreadsCreated; + ULONG BaseThreadsCreateFailed; + ULONG BaseStringBuildersCreated; + ULONG BaseStringBuildersResized; + + // ref + ULONG RefObjectsCreated; + ULONG RefObjectsDestroyed; + ULONG RefObjectsAllocated; + ULONG RefObjectsFreed; + ULONG RefObjectsAllocatedFromSmallFreeList; + ULONG RefObjectsFreedToSmallFreeList; + ULONG RefObjectsAllocatedFromTypeFreeList; + ULONG RefObjectsFreedToTypeFreeList; + ULONG RefObjectsDeleteDeferred; + ULONG RefAutoPoolsCreated; + ULONG RefAutoPoolsDestroyed; + ULONG RefAutoPoolsDynamicAllocated; + ULONG RefAutoPoolsDynamicResized; + + // queuedlock + ULONG QlBlockSpins; + ULONG QlBlockWaits; + ULONG QlAcquireExclusiveBlocks; + ULONG QlAcquireSharedBlocks; + + // workqueue + ULONG WqWorkQueueThreadsCreated; + ULONG WqWorkQueueThreadsCreateFailed; + ULONG WqWorkItemsQueued; +} PHLIB_STATISTICS_BLOCK; + +#ifdef DEBUG +extern PHLIB_STATISTICS_BLOCK PhLibStatisticsBlock; +#endif + +#ifdef DEBUG +#define PHLIB_INC_STATISTIC(Name) (_InterlockedIncrement(&PhLibStatisticsBlock.Name)) +#else +#define PHLIB_INC_STATISTIC(Name) +#endif + +#endif diff --git a/phlib/include/phnative.h b/phlib/include/phnative.h index 6d1ff9b694fa..2f591f8ff0e8 100644 --- a/phlib/include/phnative.h +++ b/phlib/include/phnative.h @@ -20,6 +20,10 @@ typedef NTSTATUS (NTAPI *PPH_OPEN_OBJECT)( _In_opt_ PVOID Context ); +typedef NTSTATUS (NTAPI *PPH_CLOSE_OBJECT)( + _In_opt_ PVOID Context + ); + typedef NTSTATUS (NTAPI *PPH_GET_OBJECT_SECURITY)( _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, _In_ SECURITY_INFORMATION SecurityInformation, @@ -42,8 +46,18 @@ typedef struct _PH_TOKEN_ATTRIBUTES ULONG ReservedBits : 29; }; ULONG Reserved; + PSID TokenSid; } PH_TOKEN_ATTRIBUTES, *PPH_TOKEN_ATTRIBUTES; +typedef enum _MANDATORY_LEVEL_RID { + MandatoryUntrustedRID = SECURITY_MANDATORY_UNTRUSTED_RID, + MandatoryLowRID = SECURITY_MANDATORY_LOW_RID, + MandatoryMediumRID = SECURITY_MANDATORY_MEDIUM_RID, + MandatoryHighRID = SECURITY_MANDATORY_HIGH_RID, + MandatorySystemRID = SECURITY_MANDATORY_SYSTEM_RID, + MandatorySecureProcessRID = SECURITY_MANDATORY_PROTECTED_PROCESS_RID +} MANDATORY_LEVEL_RID, *PMANDATORY_LEVEL_RID; + PHLIBAPI PH_TOKEN_ATTRIBUTES NTAPI @@ -105,6 +119,15 @@ PhOpenProcessToken( _Out_ PHANDLE TokenHandle ); +PHLIBAPI +NTSTATUS +NTAPI +PhOpenProcessTokenPublic( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ); + PHLIBAPI NTSTATUS NTAPI @@ -123,6 +146,22 @@ PhSetObjectSecurity( _In_ PSECURITY_DESCRIPTOR SecurityDescriptor ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetSecurityDescriptorAsString( + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhGetObjectSecurityDescriptorAsString( + _In_ HANDLE Handle, + _Out_ PPH_STRING* SecurityDescriptorString + ); + PHLIBAPI NTSTATUS NTAPI @@ -155,6 +194,14 @@ PhGetProcessImageFileNameWin32( _Out_ PPH_STRING *FileName ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetProcessIsBeingDebugged( + _In_ HANDLE ProcessHandle, + _Out_ PBOOLEAN IsBeingDebugged + ); + /** Specifies a PEB string. */ typedef enum _PH_PEB_OFFSET { @@ -188,6 +235,14 @@ PhGetProcessCommandLine( _Out_ PPH_STRING *CommandLine ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetProcessDesktopInfo( + _In_ HANDLE ProcessHandle, + _Out_ PPH_STRING *DesktopInfo + ); + PHLIBAPI NTSTATUS NTAPI @@ -237,6 +292,15 @@ PhEnumProcessEnvironmentVariables( _Out_ PPH_ENVIRONMENT_VARIABLE Variable ); +PHLIBAPI +NTSTATUS +NTAPI +PhQueryEnvironmentVariable( + _In_opt_ PVOID Environment, + _In_ PPH_STRINGREF Name, + _Out_opt_ PPH_STRING* Value + ); + PHLIBAPI NTSTATUS NTAPI @@ -273,7 +337,7 @@ PhGetProcessWsCounters( PHLIBAPI NTSTATUS NTAPI -PhInjectDllProcess( +PhLoadDllProcess( _In_ HANDLE ProcessHandle, _In_ PWSTR FileName, _In_opt_ PLARGE_INTEGER Timeout @@ -288,6 +352,16 @@ PhUnloadDllProcess( _In_opt_ PLARGE_INTEGER Timeout ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetProcessUnloadedDlls( + _In_ HANDLE ProcessId, + _Out_ PVOID *EventTrace, + _Out_ ULONG *EventTraceSize, + _Out_ ULONG *EventTraceCount + ); + PHLIBAPI NTSTATUS NTAPI @@ -347,6 +421,14 @@ PhGetTokenGroups( _Out_ PTOKEN_GROUPS *Groups ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetTokenRestrictedSids( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_GROUPS* RestrictedSids + ); + PHLIBAPI NTSTATUS NTAPI @@ -355,6 +437,33 @@ PhGetTokenPrivileges( _Out_ PTOKEN_PRIVILEGES *Privileges ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetTokenTrustLevel( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_PROCESS_TRUST_LEVEL *TrustLevel + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetTokenNamedObjectPath( + _In_ HANDLE TokenHandle, + _In_opt_ PSID Sid, + _Out_ PPH_STRING* ObjectPath + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetAppContainerNamedObjectPath( + _In_ HANDLE TokenHandle, + _In_opt_ PSID AppContainerSid, + _In_ BOOLEAN RelativePath, + _Out_ PPH_STRING* ObjectPath + ); + PHLIBAPI NTSTATUS NTAPI @@ -382,6 +491,16 @@ PhSetTokenPrivilege2( _In_ ULONG Attributes ); +PHLIBAPI +NTSTATUS +NTAPI +PhSetTokenGroups( + _In_ HANDLE TokenHandle, + _In_opt_ PWSTR GroupName, + _In_opt_ PSID GroupSid, + _In_ ULONG Attributes + ); + PHLIBAPI NTSTATUS NTAPI @@ -390,6 +509,15 @@ PhSetTokenIsVirtualizationEnabled( _In_ BOOLEAN IsVirtualizationEnabled ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetTokenIntegrityLevelRID( + _In_ HANDLE TokenHandle, + _Out_opt_ PMANDATORY_LEVEL_RID IntegrityLevelRID, + _Out_opt_ PWSTR *IntegrityString + ); + PHLIBAPI NTSTATUS NTAPI @@ -399,6 +527,17 @@ PhGetTokenIntegrityLevel( _Out_opt_ PWSTR *IntegrityString ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetTokenProcessTrustLevelRID( + _In_ HANDLE TokenHandle, + _Out_opt_ PULONG ProtectionType, + _Out_opt_ PULONG ProtectionLevel, + _Out_opt_ PPH_STRING* TrustLevelString, + _Out_opt_ PPH_STRING* TrustLevelSidString + ); + PHLIBAPI NTSTATUS NTAPI @@ -415,6 +554,45 @@ PhSetFileSize( _In_ PLARGE_INTEGER Size ); +PHLIBAPI +NTSTATUS +NTAPI +PhDeleteFile( + _In_ HANDLE FileHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetFileHandleName( + _In_ HANDLE FileHandle, + _Out_ PPH_STRING *FileName + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetFileAllInformation( + _In_ HANDLE FileHandle, + _Out_ PFILE_ALL_INFORMATION *FileId + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetFileId( + _In_ HANDLE FileHandle, + _Out_ PFILE_ID_INFORMATION *FileId + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetProcessIdsUsingFile( + _In_ HANDLE FileHandle, + _Out_ PFILE_PROCESS_IDS_USING_FILE_INFORMATION *ProcessIdsUsingFile + ); + PHLIBAPI NTSTATUS NTAPI @@ -575,6 +753,30 @@ PhSetProcessModuleLoadCount32( _In_ ULONG LoadCount ); +PHLIBAPI +PVOID +NTAPI +PhGetDllHandle( + _In_ PWSTR DllName + ); + +PHLIBAPI +PVOID +NTAPI +PhGetModuleProcAddress( + _In_ PWSTR ModuleName, + _In_ PSTR ProcName + ); + +PHLIBAPI +PVOID +NTAPI +PhGetProcedureAddress( + _In_ PVOID DllHandle, + _In_opt_ PSTR ProcedureName, + _In_opt_ ULONG ProcedureNumber + ); + PHLIBAPI NTSTATUS NTAPI @@ -625,11 +827,23 @@ PhGetKernelFileName( */ #define PH_NEXT_PROCESS(Process) ( \ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ - (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ + (PSYSTEM_PROCESS_INFORMATION)PTR_ADD_OFFSET((Process), \ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \ NULL \ ) +#define PH_PROCESS_EXTENSION(Process) \ + ((PSYSTEM_PROCESS_INFORMATION_EXTENSION)PTR_ADD_OFFSET((Process), \ + UFIELD_OFFSET(SYSTEM_PROCESS_INFORMATION, Threads) + \ + sizeof(SYSTEM_THREAD_INFORMATION) * \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NumberOfThreads)) + +#define PH_EXTENDED_PROCESS_EXTENSION(Process) \ + ((PSYSTEM_PROCESS_INFORMATION_EXTENSION)PTR_ADD_OFFSET((Process), \ + UFIELD_OFFSET(SYSTEM_PROCESS_INFORMATION, Threads) + \ + sizeof(SYSTEM_EXTENDED_THREAD_INFORMATION) * \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NumberOfThreads)) + PHLIBAPI NTSTATUS NTAPI @@ -683,6 +897,14 @@ PhEnumHandlesEx( _Out_ PSYSTEM_HANDLE_INFORMATION_EX *Handles ); +PHLIBAPI +NTSTATUS +NTAPI +PhEnumHandlesEx2( + _In_ HANDLE ProcessHandle, + _Out_ PPROCESS_HANDLE_SNAPSHOT_INFORMATION *Handles + ); + #define PH_FIRST_PAGEFILE(Pagefiles) ( \ /* The size of a pagefile can never be 0. A TotalSize of 0 * is used to indicate that there are no pagefiles. @@ -692,7 +914,7 @@ PhEnumHandlesEx( ) #define PH_NEXT_PAGEFILE(Pagefile) ( \ ((PSYSTEM_PAGEFILE_INFORMATION)(Pagefile))->NextEntryOffset ? \ - (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR)(Pagefile) + \ + (PSYSTEM_PAGEFILE_INFORMATION)PTR_ADD_OFFSET((Pagefile), \ ((PSYSTEM_PAGEFILE_INFORMATION)(Pagefile))->NextEntryOffset) : \ NULL \ ) @@ -769,7 +991,7 @@ PhEnumDirectoryObjects( ); typedef BOOLEAN (NTAPI *PPH_ENUM_DIRECTORY_FILE)( - _In_ PFILE_DIRECTORY_INFORMATION Information, + _In_ PVOID Information, _In_opt_ PVOID Context ); @@ -783,11 +1005,45 @@ PhEnumDirectoryFile( _In_opt_ PVOID Context ); +PHLIBAPI +NTSTATUS +NTAPI +PhEnumDirectoryFileEx( + _In_ HANDLE FileHandle, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING SearchPattern, + _In_ PPH_ENUM_DIRECTORY_FILE Callback, + _In_opt_ PVOID Context + ); + +#define PH_FIRST_FILE_EA(Information) \ + ((PFILE_FULL_EA_INFORMATION)(Information)) +#define PH_NEXT_FILE_EA(Information) \ + (((PFILE_FULL_EA_INFORMATION)(Information))->NextEntryOffset ? \ + (PTR_ADD_OFFSET((Information), ((PFILE_FULL_EA_INFORMATION)(Information))->NextEntryOffset)) : \ + NULL \ + ) + +typedef BOOLEAN (NTAPI *PPH_ENUM_FILE_EA)( + _In_ PFILE_FULL_EA_INFORMATION Information, + _In_opt_ PVOID Context + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhEnumFileExtendedAttributes( + _In_ HANDLE FileHandle, + _In_ PPH_ENUM_FILE_EA Callback, + _In_opt_ PVOID Context + ); + #define PH_FIRST_STREAM(Streams) ((PFILE_STREAM_INFORMATION)(Streams)) #define PH_NEXT_STREAM(Stream) ( \ ((PFILE_STREAM_INFORMATION)(Stream))->NextEntryOffset ? \ - (PFILE_STREAM_INFORMATION)((PCHAR)(Stream) + \ - ((PFILE_STREAM_INFORMATION)(Stream))->NextEntryOffset) : \ + (PFILE_STREAM_INFORMATION)(PTR_ADD_OFFSET((Stream), \ + ((PFILE_STREAM_INFORMATION)(Stream))->NextEntryOffset)) : \ NULL \ ) @@ -799,6 +1055,50 @@ PhEnumFileStreams( _Out_ PVOID *Streams ); +typedef BOOLEAN (NTAPI *PPH_ENUM_FILE_STREAMS)( + _In_ PFILE_STREAM_INFORMATION Information, + _In_opt_ PVOID Context + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhEnumFileStreamsEx( + _In_ HANDLE FileHandle, + _In_ PPH_ENUM_FILE_STREAMS Callback, + _In_opt_ PVOID Context + ); + +#define PH_FIRST_LINK(Links) ((PFILE_LINK_ENTRY_INFORMATION)(Links)) +#define PH_NEXT_LINK(Links) ( \ + ((PFILE_LINK_ENTRY_INFORMATION)(Links))->NextEntryOffset ? \ + (PFILE_LINK_ENTRY_INFORMATION)(PTR_ADD_OFFSET((Links), \ + ((PFILE_LINK_ENTRY_INFORMATION)(Links))->NextEntryOffset)) : \ + NULL \ + ) + +typedef BOOLEAN (NTAPI *PPH_ENUM_FILE_HARDLINKS)( + _In_ PFILE_LINK_ENTRY_INFORMATION Information, + _In_opt_ PVOID Context + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhEnumFileHardLinks( + _In_ HANDLE FileHandle, + _Out_ PVOID *HardLinks + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhEnumFileHardLinksEx( + _In_ HANDLE FileHandle, + _In_ PPH_ENUM_FILE_HARDLINKS Callback, + _In_opt_ PVOID Context + ); + PHLIBAPI VOID NTAPI @@ -832,16 +1132,19 @@ PhGetFileName( #define PH_MODULE_TYPE_WOW64_MODULE 3 #define PH_MODULE_TYPE_KERNEL_MODULE 4 #define PH_MODULE_TYPE_MAPPED_IMAGE 5 +#define PH_MODULE_TYPE_ELF_MAPPED_IMAGE 6 typedef struct _PH_MODULE_INFO { ULONG Type; PVOID BaseAddress; + PVOID ParentBaseAddress; ULONG Size; PVOID EntryPoint; ULONG Flags; PPH_STRING Name; PPH_STRING FileName; + PPH_STRING OriginalFileName; USHORT LoadOrderIndex; // -1 if N/A USHORT LoadCount; // -1 if N/A @@ -912,6 +1215,16 @@ PhOpenKey( _In_ ULONG Attributes ); +PHLIBAPI +NTSTATUS +NTAPI +PhLoadAppKey( + _Out_ PHANDLE KeyHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ ULONG Flags + ); + PHLIBAPI NTSTATUS NTAPI @@ -931,6 +1244,21 @@ PhQueryValueKey( _Out_ PVOID *Buffer ); +typedef BOOLEAN (NTAPI *PPH_ENUM_KEY_CALLBACK)( + _In_ HANDLE RootDirectory, + _In_ PKEY_BASIC_INFORMATION Information, + _In_opt_ PVOID Context + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ PPH_ENUM_KEY_CALLBACK Callback, + _In_opt_ PVOID Context + ); + PHLIBAPI NTSTATUS NTAPI @@ -958,6 +1286,29 @@ PhCreateFileWin32Ex( _Out_opt_ PULONG CreateStatus ); +PHLIBAPI +NTSTATUS +NTAPI +PhOpenFileWin32( + _Out_ PHANDLE FileHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG ShareAccess, + _In_ ULONG CreateOptions + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhOpenFileWin32Ex( + _Out_ PHANDLE FileHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions, + _Out_opt_ PULONG OpenStatus + ); + PHLIBAPI NTSTATUS NTAPI @@ -966,6 +1317,21 @@ PhQueryFullAttributesFileWin32( _Out_ PFILE_NETWORK_OPEN_INFORMATION FileInformation ); +PHLIBAPI +NTSTATUS +NTAPI +PhQueryAttributesFileWin32( + _In_ PWSTR FileName, + _Out_ PFILE_BASIC_INFORMATION FileInformation + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhDoesFileExistsWin32( + _In_ PWSTR FileName + ); + PHLIBAPI NTSTATUS NTAPI @@ -973,29 +1339,73 @@ PhDeleteFileWin32( _In_ PWSTR FileName ); +PHLIBAPI +NTSTATUS +NTAPI +PhCreateDirectory( + _In_ PPH_STRING DirectoryPath + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhDeleteDirectory( + _In_ PPH_STRING DirectoryPath + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhCreatePipe( + _Out_ PHANDLE PipeReadHandle, + _Out_ PHANDLE PipeWriteHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhCreatePipeEx( + _Out_ PHANDLE PipeReadHandle, + _Out_ PHANDLE PipeWriteHandle, + _In_ BOOLEAN InheritHandles, + _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhCreateNamedPipe( + _Out_ PHANDLE PipeHandle, + _In_ PWSTR PipeName + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhConnectPipe( + _Out_ PHANDLE PipeHandle, + _In_ PWSTR PipeName + ); + PHLIBAPI NTSTATUS NTAPI PhListenNamedPipe( - _In_ HANDLE FileHandle, - _In_opt_ HANDLE Event, - _In_opt_ PIO_APC_ROUTINE ApcRoutine, - _In_opt_ PVOID ApcContext, - _Out_ PIO_STATUS_BLOCK IoStatusBlock + _In_ HANDLE PipeHandle ); PHLIBAPI NTSTATUS NTAPI PhDisconnectNamedPipe( - _In_ HANDLE FileHandle + _In_ HANDLE PipeHandle ); PHLIBAPI NTSTATUS NTAPI PhPeekNamedPipe( - _In_ HANDLE FileHandle, + _In_ HANDLE PipeHandle, _Out_writes_bytes_opt_(Length) PVOID Buffer, _In_ ULONG Length, _Out_opt_ PULONG NumberOfBytesRead, @@ -1007,11 +1417,7 @@ PHLIBAPI NTSTATUS NTAPI PhTransceiveNamedPipe( - _In_ HANDLE FileHandle, - _In_opt_ HANDLE Event, - _In_opt_ PIO_APC_ROUTINE ApcRoutine, - _In_opt_ PVOID ApcContext, - _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ HANDLE PipeHandle, _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, _In_ ULONG InputBufferLength, _Out_writes_bytes_(OutputBufferLength) PVOID OutputBuffer, @@ -1022,17 +1428,38 @@ PHLIBAPI NTSTATUS NTAPI PhWaitForNamedPipe( - _In_opt_ PUNICODE_STRING FileSystemName, - _In_ PUNICODE_STRING Name, - _In_opt_ PLARGE_INTEGER Timeout, - _In_ BOOLEAN UseDefaultTimeout + _In_ PWSTR PipeName, + _In_opt_ ULONG Timeout ); PHLIBAPI NTSTATUS NTAPI PhImpersonateClientOfNamedPipe( - _In_ HANDLE FileHandle + _In_ HANDLE PipeHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetThreadName( + _In_ HANDLE ThreadHandle, + _Out_ PPH_STRING *ThreadName + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhImpersonateToken( + _In_ HANDLE ThreadHandle, + _In_ HANDLE TokenHandle + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhRevertImpersonationToken( + _In_ HANDLE ThreadHandle ); #ifdef __cplusplus diff --git a/phlib/include/phnativeinl.h b/phlib/include/phnativeinl.h index d2954d866481..a76e4f9f0c3c 100644 --- a/phlib/include/phnativeinl.h +++ b/phlib/include/phnativeinl.h @@ -178,40 +178,6 @@ PhGetProcessPeb32( return status; } -/** - * Gets whether a process is being debugged. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION - * access. - * \param IsBeingDebugged A variable which receives a boolean indicating whether the process is - * being debugged. - */ -FORCEINLINE -NTSTATUS -PhGetProcessIsBeingDebugged( - _In_ HANDLE ProcessHandle, - _Out_ PBOOLEAN IsBeingDebugged - ) -{ - NTSTATUS status; - PVOID debugPort; - - status = NtQueryInformationProcess( - ProcessHandle, - ProcessDebugPort, - &debugPort, - sizeof(PVOID), - NULL - ); - - if (NT_SUCCESS(status)) - { - *IsBeingDebugged = !!debugPort; - } - - return status; -} - /** * Gets a handle to a process' debug object. * @@ -238,6 +204,37 @@ PhGetProcessDebugObject( ); } +FORCEINLINE +NTSTATUS +PhGetProcessErrorMode( + _In_ HANDLE ProcessHandle, + _Out_ PULONG ErrorMode + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessDefaultHardErrorMode, + ErrorMode, + sizeof(ULONG), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhSetProcessErrorMode( + _In_ HANDLE ProcessHandle, + _In_ ULONG ErrorMode + ) +{ + return NtSetInformationProcess( + ProcessHandle, + ProcessDefaultHardErrorMode, + &ErrorMode, + sizeof(ULONG) + ); +} + /** * Gets a process' no-execute status. * @@ -261,6 +258,37 @@ PhGetProcessExecuteFlags( ); } +FORCEINLINE +NTSTATUS +PhGetProcessPriority( + _In_ HANDLE ProcessHandle, + _Out_ PPROCESS_PRIORITY_CLASS PriorityClass + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessPriorityClass, + PriorityClass, + sizeof(PROCESS_PRIORITY_CLASS), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhSetProcessPriority( + _In_ HANDLE ProcessHandle, + _In_ PROCESS_PRIORITY_CLASS PriorityClass + ) +{ + return NtSetInformationProcess( + ProcessHandle, + ProcessPriorityClass, + &PriorityClass, + sizeof(PROCESS_PRIORITY_CLASS) + ); +} + /** * Gets a process' I/O priority. * @@ -284,6 +312,27 @@ PhGetProcessIoPriority( ); } +/** + * Sets a process' I/O priority. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_SET_INFORMATION access. + * \param IoPriority The new I/O priority. + */ +FORCEINLINE +NTSTATUS +PhSetProcessIoPriority( + _In_ HANDLE ProcessHandle, + _In_ IO_PRIORITY_HINT IoPriority + ) +{ + return NtSetInformationProcess( + ProcessHandle, + ProcessIoPriority, + &IoPriority, + sizeof(IO_PRIORITY_HINT) + ); +} + /** * Gets a process' page priority. * @@ -317,6 +366,25 @@ PhGetProcessPagePriority( return status; } +FORCEINLINE +NTSTATUS +PhSetProcessPagePriority( + _In_ HANDLE ProcessHandle, + _In_ ULONG PagePriority + ) +{ + PAGE_PRIORITY_INFORMATION pagePriorityInfo; + + pagePriorityInfo.PagePriority = PagePriority; + + return NtSetInformationProcess( + ProcessHandle, + ProcessPagePriority, + &pagePriorityInfo, + sizeof(PAGE_PRIORITY_INFORMATION) + ); +} + /** * Gets a process' cycle count. * @@ -350,6 +418,32 @@ PhGetProcessCycleTime( return status; } +FORCEINLINE +NTSTATUS +PhGetProcessUptime( + _In_ HANDLE ProcessHandle, + _Out_ PPROCESS_UPTIME_INFORMATION Uptime + ) +{ + NTSTATUS status; + PROCESS_UPTIME_INFORMATION uptimeInfo; + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessUptimeInformation, + &uptimeInfo, + sizeof(PROCESS_UPTIME_INFORMATION), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + *Uptime = uptimeInfo; + + return status; +} + FORCEINLINE NTSTATUS PhGetProcessConsoleHostProcessId( @@ -376,6 +470,53 @@ PhGetProcessConsoleHostProcessId( return status; } +FORCEINLINE +NTSTATUS +PhGetProcessProtection( + _In_ HANDLE ProcessHandle, + _Out_ PPS_PROTECTION Protection + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessProtectionInformation, + Protection, + sizeof(PS_PROTECTION), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhGetProcessQuotaLimits( + _In_ HANDLE ProcessHandle, + _Out_ PQUOTA_LIMITS QuotaLimits + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessQuotaLimits, + QuotaLimits, + sizeof(QUOTA_LIMITS), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhSetProcessQuotaLimits( + _In_ HANDLE ProcessHandle, + _In_ QUOTA_LIMITS QuotaLimits + ) +{ + return NtSetInformationProcess( + ProcessHandle, + ProcessQuotaLimits, + &QuotaLimits, + sizeof(QUOTA_LIMITS) + ); +} + /** * Sets a process' affinity mask. * @@ -397,180 +538,484 @@ PhSetProcessAffinityMask( ); } +FORCEINLINE +NTSTATUS +PhGetProcessIsCFGuardEnabled( + _In_ HANDLE ProcessHandle, + _Out_ PBOOLEAN IsControlFlowGuardEnabled + ) +{ + NTSTATUS status; + PROCESS_MITIGATION_POLICY_INFORMATION policyInfo; + + policyInfo.Policy = ProcessControlFlowGuardPolicy; + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessMitigationPolicy, + &policyInfo, + sizeof(PROCESS_MITIGATION_POLICY_INFORMATION), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + *IsControlFlowGuardEnabled = !!policyInfo.ControlFlowGuardPolicy.EnableControlFlowGuard; + + return status; +} + +FORCEINLINE +NTSTATUS +PhGetProcessHandleCount( + _In_ HANDLE ProcessHandle, + _Out_ PPROCESS_HANDLE_INFORMATION HandleInfo + ) +{ + return NtQueryInformationProcess( + ProcessHandle, + ProcessHandleCount, + HandleInfo, + sizeof(PROCESS_HANDLE_INFORMATION), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhGetProcessBreakOnTermination( + _In_ HANDLE ProcessHandle, + _Out_ PBOOLEAN BreakOnTermination + ) +{ + NTSTATUS status; + ULONG breakOnTermination; + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessBreakOnTermination, + &breakOnTermination, + sizeof(ULONG), + NULL + ); + + if (NT_SUCCESS(status)) + { + *BreakOnTermination = !!breakOnTermination; + } + + return status; +} + +FORCEINLINE +NTSTATUS +PhSetProcessBreakOnTermination( + _In_ HANDLE ProcessHandle, + _In_ BOOLEAN BreakOnTermination + ) +{ + ULONG breakOnTermination; + + breakOnTermination = BreakOnTermination ? 1 : 0; + + return NtSetInformationProcess( + ProcessHandle, + ProcessBreakOnTermination, + &breakOnTermination, + sizeof(ULONG) + ); +} + /** - * Sets a process' I/O priority. + * Gets basic information for a thread. * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_SET_INFORMATION access. + * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION + * access. + * \param BasicInformation A variable which receives the information. + */ +FORCEINLINE +NTSTATUS +PhGetThreadBasicInformation( + _In_ HANDLE ThreadHandle, + _Out_ PTHREAD_BASIC_INFORMATION BasicInformation + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadBasicInformation, + BasicInformation, + sizeof(THREAD_BASIC_INFORMATION), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhGetThreadBasePriority( + _In_ HANDLE ThreadHandle, + _Out_ PLONG Increment + ) +{ + NTSTATUS status; + THREAD_BASIC_INFORMATION basicInfo; + + status = PhGetThreadBasicInformation(ThreadHandle, &basicInfo); + + if (NT_SUCCESS(status)) + { + *Increment = basicInfo.BasePriority; + } + + return status; + + //return NtQueryInformationThread( + // ThreadHandle, + // ThreadBasePriority, + // Increment, + // sizeof(LONG), + // NULL + // ); +} + +FORCEINLINE +NTSTATUS +PhGetThreadStartAddress( + _In_ HANDLE ThreadHandle, + _Out_ PVOID *StartAddress + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadQuerySetWin32StartAddress, + StartAddress, + sizeof(PVOID), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhSetThreadBasePriority( + _In_ HANDLE ThreadHandle, + _In_ LONG Increment + ) +{ + return NtSetInformationThread( + ThreadHandle, + ThreadBasePriority, + &Increment, + sizeof(LONG) + ); +} + +/** + * Gets a thread's I/O priority. + * + * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION + * access. + * \param IoPriority A variable which receives the I/O priority of the thread. + */ +FORCEINLINE +NTSTATUS +PhGetThreadIoPriority( + _In_ HANDLE ThreadHandle, + _Out_ IO_PRIORITY_HINT *IoPriority + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadIoPriority, + IoPriority, + sizeof(IO_PRIORITY_HINT), + NULL + ); +} + +/** + * Sets a thread's I/O priority. + * + * \param ThreadHandle A handle to a thread. The handle must have THREAD_SET_LIMITED_INFORMATION + * access. * \param IoPriority The new I/O priority. */ FORCEINLINE NTSTATUS -PhSetProcessIoPriority( - _In_ HANDLE ProcessHandle, +PhSetThreadIoPriority( + _In_ HANDLE ThreadHandle, _In_ IO_PRIORITY_HINT IoPriority ) { - return NtSetInformationProcess( - ProcessHandle, - ProcessIoPriority, + return NtSetInformationThread( + ThreadHandle, + ThreadIoPriority, &IoPriority, sizeof(IO_PRIORITY_HINT) ); } -/** - * Gets basic information for a thread. - * - * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION - * access. - * \param BasicInformation A variable which receives the information. - */ +/** + * Gets a thread's page priority. + * + * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION + * access. + * \param PagePriority A variable which receives the page priority of the thread. + */ +FORCEINLINE +NTSTATUS +PhGetThreadPagePriority( + _In_ HANDLE ThreadHandle, + _Out_ PULONG PagePriority + ) +{ + NTSTATUS status; + PAGE_PRIORITY_INFORMATION pagePriorityInfo; + + status = NtQueryInformationThread( + ThreadHandle, + ThreadPagePriority, + &pagePriorityInfo, + sizeof(PAGE_PRIORITY_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + *PagePriority = pagePriorityInfo.PagePriority; + } + + return status; +} + +FORCEINLINE +NTSTATUS +PhSetThreadPagePriority( + _In_ HANDLE ThreadHandle, + _In_ ULONG PagePriority + ) +{ + PAGE_PRIORITY_INFORMATION pagePriorityInfo; + + pagePriorityInfo.PagePriority = PagePriority; + + return NtSetInformationThread( + ThreadHandle, + ThreadPagePriority, + &pagePriorityInfo, + sizeof(PAGE_PRIORITY_INFORMATION) + ); +} + +/** + * Gets a thread's cycle count. + * + * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION + * access. + * \param CycleTime A variable which receives the 64-bit cycle time. + */ +FORCEINLINE +NTSTATUS +PhGetThreadCycleTime( + _In_ HANDLE ThreadHandle, + _Out_ PULONG64 CycleTime + ) +{ + NTSTATUS status; + THREAD_CYCLE_TIME_INFORMATION cycleTimeInfo; + + status = NtQueryInformationThread( + ThreadHandle, + ThreadCycleTime, + &cycleTimeInfo, + sizeof(THREAD_CYCLE_TIME_INFORMATION), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + *CycleTime = cycleTimeInfo.AccumulatedCycles; + + return status; +} + +FORCEINLINE +NTSTATUS +PhGetThreadIdealProcessor( + _In_ HANDLE ThreadHandle, + _Out_ PPROCESSOR_NUMBER ProcessorNumber + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadIdealProcessorEx, + ProcessorNumber, + sizeof(PROCESSOR_NUMBER), + NULL + ); +} + +FORCEINLINE +NTSTATUS +PhGetThreadSuspendCount( + _In_ HANDLE ThreadHandle, + _Out_ PULONG SuspendCount + ) +{ + return NtQueryInformationThread( + ThreadHandle, + ThreadSuspendCount, + SuspendCount, + sizeof(ULONG), + NULL + ); +} + FORCEINLINE NTSTATUS -PhGetThreadBasicInformation( +PhGetThreadLastSystemCall( _In_ HANDLE ThreadHandle, - _Out_ PTHREAD_BASIC_INFORMATION BasicInformation + _Out_ PTHREAD_LAST_SYSCALL_INFORMATION LastSystemCall ) { return NtQueryInformationThread( ThreadHandle, - ThreadBasicInformation, - BasicInformation, - sizeof(THREAD_BASIC_INFORMATION), + ThreadLastSystemCall, + LastSystemCall, + RTL_SIZEOF_THROUGH_FIELD(THREAD_LAST_SYSCALL_INFORMATION, Pad), // HACK: Win7 requires exact size. (dmex) NULL ); } -/** - * Gets a thread's I/O priority. - * - * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION - * access. - * \param IoPriority A variable which receives the I/O priority of the thread. - */ FORCEINLINE NTSTATUS -PhGetThreadIoPriority( +PhGetThreadWow64Context( _In_ HANDLE ThreadHandle, - _Out_ IO_PRIORITY_HINT *IoPriority + _Out_ PWOW64_CONTEXT Context ) { return NtQueryInformationThread( ThreadHandle, - ThreadIoPriority, - IoPriority, - sizeof(IO_PRIORITY_HINT), + ThreadWow64Context, + Context, + sizeof(WOW64_CONTEXT), NULL ); } -/** - * Gets a thread's page priority. - * - * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION - * access. - * \param PagePriority A variable which receives the page priority of the thread. - */ FORCEINLINE NTSTATUS -PhGetThreadPagePriority( +PhGetThreadBreakOnTermination( _In_ HANDLE ThreadHandle, - _Out_ PULONG PagePriority + _Out_ PBOOLEAN BreakOnTermination ) { NTSTATUS status; - PAGE_PRIORITY_INFORMATION pagePriorityInfo; + ULONG breakOnTermination; status = NtQueryInformationThread( ThreadHandle, - ThreadPagePriority, - &pagePriorityInfo, - sizeof(PAGE_PRIORITY_INFORMATION), + ThreadBreakOnTermination, + &breakOnTermination, + sizeof(ULONG), NULL ); if (NT_SUCCESS(status)) { - *PagePriority = pagePriorityInfo.PagePriority; + *BreakOnTermination = !!breakOnTermination; } return status; } -/** - * Gets a thread's cycle count. - * - * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION - * access. - * \param CycleTime A variable which receives the 64-bit cycle time. - */ FORCEINLINE NTSTATUS -PhGetThreadCycleTime( +PhSetThreadBreakOnTermination( _In_ HANDLE ThreadHandle, - _Out_ PULONG64 CycleTime + _In_ BOOLEAN BreakOnTermination + ) +{ + ULONG breakOnTermination; + + breakOnTermination = BreakOnTermination ? 1 : 0; + + return NtSetInformationThread( + ThreadHandle, + ThreadBreakOnTermination, + &breakOnTermination, + sizeof(ULONG) + ); +} + +FORCEINLINE +NTSTATUS +PhGetThreadIsIoPending( + _In_ HANDLE ThreadHandle, + _Out_ PBOOLEAN IsIoPending ) { NTSTATUS status; - THREAD_CYCLE_TIME_INFORMATION cycleTimeInfo; + ULONG isIoPending; status = NtQueryInformationThread( ThreadHandle, - ThreadCycleTime, - &cycleTimeInfo, - sizeof(THREAD_CYCLE_TIME_INFORMATION), + ThreadIsIoPending, + &isIoPending, + sizeof(ULONG), NULL ); - if (!NT_SUCCESS(status)) - return status; - - *CycleTime = cycleTimeInfo.AccumulatedCycles; + if (NT_SUCCESS(status)) + { + *IsIoPending = !!isIoPending; + } return status; } /** - * Sets a thread's affinity mask. + * Gets time information for a thread. * - * \param ThreadHandle A handle to a thread. The handle must have THREAD_SET_LIMITED_INFORMATION - * access. - * \param AffinityMask The new affinity mask. + * \param ProcessHandle A handle to a thread. The handle must have + * THREAD_QUERY_LIMITED_INFORMATION access. + * \param Times A variable which receives the information. */ FORCEINLINE NTSTATUS -PhSetThreadAffinityMask( +PhGetThreadTimes( _In_ HANDLE ThreadHandle, - _In_ ULONG_PTR AffinityMask + _Out_ PKERNEL_USER_TIMES Times ) { - return NtSetInformationThread( + return NtQueryInformationThread( ThreadHandle, - ThreadAffinityMask, - &AffinityMask, - sizeof(ULONG_PTR) + ThreadTimes, + Times, + sizeof(KERNEL_USER_TIMES), + NULL ); } /** - * Sets a thread's I/O priority. + * Sets a thread's affinity mask. * * \param ThreadHandle A handle to a thread. The handle must have THREAD_SET_LIMITED_INFORMATION * access. - * \param IoPriority The new I/O priority. + * \param AffinityMask The new affinity mask. */ FORCEINLINE NTSTATUS -PhSetThreadIoPriority( +PhSetThreadAffinityMask( _In_ HANDLE ThreadHandle, - _In_ IO_PRIORITY_HINT IoPriority + _In_ ULONG_PTR AffinityMask ) { return NtSetInformationThread( ThreadHandle, - ThreadIoPriority, - &IoPriority, - sizeof(IO_PRIORITY_HINT) + ThreadAffinityMask, + &AffinityMask, + sizeof(ULONG_PTR) ); } @@ -801,6 +1246,33 @@ PhGetTokenLinkedToken( return status; } +FORCEINLINE +NTSTATUS +PhGetTokenIsRestricted( + _In_ HANDLE TokenHandle, + _Out_ PBOOLEAN IsRestricted + ) +{ + NTSTATUS status; + ULONG returnLength; + ULONG restricted; + + status = NtQueryInformationToken( + TokenHandle, + TokenIsRestricted, + &restricted, + sizeof(ULONG), + &returnLength + ); + + if (!NT_SUCCESS(status)) + return status; + + *IsRestricted = !!restricted; + + return status; +} + /** * Gets whether virtualization is allowed for a token. * @@ -869,6 +1341,192 @@ PhGetTokenIsVirtualizationEnabled( return status; } +/** +* Gets UIAccess flag for a token. +* +* \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. +* \param IsUIAccessEnabled A variable which receives a boolean indicating whether +* UIAccess is enabled for the token. +*/ +FORCEINLINE +NTSTATUS +PhGetTokenIsUIAccessEnabled( + _In_ HANDLE TokenHandle, + _Out_ PBOOLEAN IsUIAccessEnabled + ) +{ + NTSTATUS status; + ULONG returnLength; + ULONG uiAccess; + + status = NtQueryInformationToken( + TokenHandle, + TokenUIAccess, + &uiAccess, + sizeof(ULONG), + &returnLength + ); + + if (!NT_SUCCESS(status)) + return status; + + *IsUIAccessEnabled = !!uiAccess; + + return status; +} + +/** +* Sets UIAccess flag for a token. +* +* \param TokenHandle A handle to a token. The handle must have TOKEN_ADJUST_DEFAULT access. +* \param IsUIAccessEnabled The new flag state. +* \remarks Enabling UIAccess requires SeTcbPrivilege. +*/ +FORCEINLINE +NTSTATUS +PhSetTokenUIAccessEnabled( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN IsUIAccessEnabled + ) +{ + ULONG uiAccess; + + uiAccess = IsUIAccessEnabled ? 1 : 0; + + return NtSetInformationToken( + TokenHandle, + TokenUIAccess, + &uiAccess, + sizeof(ULONG) + ); +} + +/** +* Gets SandBoxInert flag for a token. +* +* \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. +* \param IsSandBoxInert A variable which receives a boolean indicating whether +* AppLocker rules or Software Restriction Policies are enabled for the token. +*/ +FORCEINLINE +NTSTATUS +PhGetTokenIsSandBoxInert( + _In_ HANDLE TokenHandle, + _Out_ PBOOLEAN IsSandBoxInert + ) +{ + NTSTATUS status; + ULONG returnLength; + ULONG sandBoxInert; + + status = NtQueryInformationToken( + TokenHandle, + TokenSandBoxInert, + &sandBoxInert, + sizeof(ULONG), + &returnLength + ); + + if (!NT_SUCCESS(status)) + return status; + + *IsSandBoxInert = !!sandBoxInert; + + return status; +} + +/** +* Gets Mandatory Policy for a token. +* +* \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. +* \param MandatoryPolicy A variable which receives a set of mandatory integrity +* policies enforced for the token. +*/ +FORCEINLINE +NTSTATUS +PhGetTokenMandatoryPolicy( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_MANDATORY_POLICY MandatoryPolicy + ) +{ + NTSTATUS status; + ULONG returnLength; + + status = NtQueryInformationToken( + TokenHandle, + TokenMandatoryPolicy, + MandatoryPolicy, + sizeof(TOKEN_MANDATORY_POLICY), + &returnLength + ); + + return status; +} + +FORCEINLINE +NTSTATUS +PhGetTokenOrigin( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_ORIGIN Origin + ) +{ + ULONG returnLength; + + return NtQueryInformationToken( + TokenHandle, + TokenOrigin, + Origin, + sizeof(TOKEN_ORIGIN), + &returnLength + ); +} + +FORCEINLINE +NTSTATUS +PhGetTokenIsAppContainer( + _In_ HANDLE TokenHandle, + _Out_ PBOOLEAN IsAppContainer + ) +{ + NTSTATUS status; + ULONG returnLength; + ULONG isAppContainer; + + status = NtQueryInformationToken( + TokenHandle, + TokenIsAppContainer, + &isAppContainer, + sizeof(ULONG), + &returnLength + ); + + if (!NT_SUCCESS(status)) + return status; + + *IsAppContainer = !!isAppContainer; + + return status; +} + +FORCEINLINE +NTSTATUS +PhGetTokenAppContainerNumber( + _In_ HANDLE TokenHandle, + _Out_ PULONG AppContainerNumber + ) +{ + ULONG returnLength; + + return NtQueryInformationToken( + TokenHandle, + TokenAppContainerNumber, + AppContainerNumber, + sizeof(ULONG), + &returnLength + ); +} + + FORCEINLINE NTSTATUS PhGetEventBasicInformation( @@ -965,4 +1623,24 @@ PhGetTimerBasicInformation( ); } +FORCEINLINE +NTSTATUS +PhSetDebugKillProcessOnExit( + _In_ HANDLE DebugObjectHandle, + _In_ BOOLEAN KillProcessOnExit + ) +{ + ULONG killProcessOnExit; + + killProcessOnExit = KillProcessOnExit ? 1 : 0; + + return NtSetInformationDebugObject( + DebugObjectHandle, + DebugObjectKillProcessOnExitInformation, + &killProcessOnExit, + sizeof(ULONG), + NULL + ); +} + #endif diff --git a/phlib/include/phnet.h b/phlib/include/phnet.h index 6c4dcbf8f95d..faa2e19f0dfa 100644 --- a/phlib/include/phnet.h +++ b/phlib/include/phnet.h @@ -1,139 +1,146 @@ -#ifndef _PH_PHNET_H -#define _PH_PHNET_H - -#include -#include - -#define PH_IPV4_NETWORK_TYPE 0x1 -#define PH_IPV6_NETWORK_TYPE 0x2 -#define PH_NETWORK_TYPE_MASK 0x3 - -#define PH_TCP_PROTOCOL_TYPE 0x10 -#define PH_UDP_PROTOCOL_TYPE 0x20 -#define PH_PROTOCOL_TYPE_MASK 0x30 - -#define PH_NO_NETWORK_PROTOCOL 0x0 -#define PH_TCP4_NETWORK_PROTOCOL (PH_IPV4_NETWORK_TYPE | PH_TCP_PROTOCOL_TYPE) -#define PH_TCP6_NETWORK_PROTOCOL (PH_IPV6_NETWORK_TYPE | PH_TCP_PROTOCOL_TYPE) -#define PH_UDP4_NETWORK_PROTOCOL (PH_IPV4_NETWORK_TYPE | PH_UDP_PROTOCOL_TYPE) -#define PH_UDP6_NETWORK_PROTOCOL (PH_IPV6_NETWORK_TYPE | PH_UDP_PROTOCOL_TYPE) - -typedef struct _PH_IP_ADDRESS -{ - ULONG Type; - union - { - ULONG Ipv4; - struct in_addr InAddr; - UCHAR Ipv6[16]; - struct in6_addr In6Addr; - }; -} PH_IP_ADDRESS, *PPH_IP_ADDRESS; - -FORCEINLINE BOOLEAN PhEqualIpAddress( - _In_ PPH_IP_ADDRESS Address1, - _In_ PPH_IP_ADDRESS Address2 - ) -{ - if ((Address1->Type | Address2->Type) == 0) // don't check addresses if both are invalid - return TRUE; - if (Address1->Type != Address2->Type) - return FALSE; - - if (Address1->Type == PH_IPV4_NETWORK_TYPE) - { - return Address1->Ipv4 == Address2->Ipv4; - } - else - { -#ifdef _WIN64 - return - *(PULONG64)(Address1->Ipv6) == *(PULONG64)(Address2->Ipv6) && - *(PULONG64)(Address1->Ipv6 + 8) == *(PULONG64)(Address2->Ipv6 + 8); -#else - return - *(PULONG)(Address1->Ipv6) == *(PULONG)(Address2->Ipv6) && - *(PULONG)(Address1->Ipv6 + 4) == *(PULONG)(Address2->Ipv6 + 4) && - *(PULONG)(Address1->Ipv6 + 8) == *(PULONG)(Address2->Ipv6 + 8) && - *(PULONG)(Address1->Ipv6 + 12) == *(PULONG)(Address2->Ipv6 + 12); -#endif - } -} - -FORCEINLINE ULONG PhHashIpAddress( - _In_ PPH_IP_ADDRESS Address - ) -{ - ULONG hash = 0; - - if (Address->Type == 0) - return 0; - - hash = Address->Type | (Address->Type << 16); - - if (Address->Type == PH_IPV4_NETWORK_TYPE) - { - hash ^= Address->Ipv4; - } - else - { - hash += *(PULONG)(Address->Ipv6); - hash ^= *(PULONG)(Address->Ipv6 + 4); - hash += *(PULONG)(Address->Ipv6 + 8); - hash ^= *(PULONG)(Address->Ipv6 + 12); - } - - return hash; -} - -FORCEINLINE BOOLEAN PhIsNullIpAddress( - _In_ PPH_IP_ADDRESS Address - ) -{ - if (Address->Type == 0) - { - return TRUE; - } - else if (Address->Type == PH_IPV4_NETWORK_TYPE) - { - return Address->Ipv4 == 0; - } - else if (Address->Type == PH_IPV6_NETWORK_TYPE) - { -#ifdef _WIN64 - return (*(PULONG64)(Address->Ipv6) | *(PULONG64)(Address->Ipv6 + 8)) == 0; -#else - return (*(PULONG)(Address->Ipv6) | *(PULONG)(Address->Ipv6 + 4) | - *(PULONG)(Address->Ipv6 + 8) | *(PULONG)(Address->Ipv6 + 12)) == 0; -#endif - } - else - { - return TRUE; - } -} - -typedef struct _PH_IP_ENDPOINT -{ - PH_IP_ADDRESS Address; - ULONG Port; -} PH_IP_ENDPOINT, *PPH_IP_ENDPOINT; - -FORCEINLINE BOOLEAN PhEqualIpEndpoint( - _In_ PPH_IP_ENDPOINT Endpoint1, - _In_ PPH_IP_ENDPOINT Endpoint2 - ) -{ - return - PhEqualIpAddress(&Endpoint1->Address, &Endpoint2->Address) && - Endpoint1->Port == Endpoint2->Port; -} - -FORCEINLINE ULONG PhHashIpEndpoint( - _In_ PPH_IP_ENDPOINT Endpoint - ) -{ - return PhHashIpAddress(&Endpoint->Address) ^ Endpoint->Port; -} - -#endif +#ifndef _PH_PHNET_H +#define _PH_PHNET_H + +#include +#include +#include +#include +#include +#include + +#define PH_IPV4_NETWORK_TYPE 0x1 +#define PH_IPV6_NETWORK_TYPE 0x2 +#define PH_NETWORK_TYPE_MASK 0x3 + +#define PH_TCP_PROTOCOL_TYPE 0x10 +#define PH_UDP_PROTOCOL_TYPE 0x20 +#define PH_PROTOCOL_TYPE_MASK 0x30 + +#define PH_NO_NETWORK_PROTOCOL 0x0 +#define PH_TCP4_NETWORK_PROTOCOL (PH_IPV4_NETWORK_TYPE | PH_TCP_PROTOCOL_TYPE) +#define PH_TCP6_NETWORK_PROTOCOL (PH_IPV6_NETWORK_TYPE | PH_TCP_PROTOCOL_TYPE) +#define PH_UDP4_NETWORK_PROTOCOL (PH_IPV4_NETWORK_TYPE | PH_UDP_PROTOCOL_TYPE) +#define PH_UDP6_NETWORK_PROTOCOL (PH_IPV6_NETWORK_TYPE | PH_UDP_PROTOCOL_TYPE) + +typedef struct _PH_IP_ADDRESS +{ + ULONG Type; + union + { + ULONG Ipv4; + IN_ADDR InAddr; + UCHAR Ipv6[16]; + IN6_ADDR In6Addr; + }; +} PH_IP_ADDRESS, *PPH_IP_ADDRESS; + +FORCEINLINE BOOLEAN PhEqualIpAddress( + _In_ PPH_IP_ADDRESS Address1, + _In_ PPH_IP_ADDRESS Address2 + ) +{ + if ((Address1->Type | Address2->Type) == 0) // don't check addresses if both are invalid + return TRUE; + if (Address1->Type != Address2->Type) + return FALSE; + + // TODO: Remove the below commented code if the ADDR_EQUAL macros work -dmex + if (Address1->Type == PH_IPV4_NETWORK_TYPE) + { + return IN4_ADDR_EQUAL(&Address1->InAddr, &Address2->InAddr); + // return Address1->Ipv4 == Address2->Ipv4; + } + else + { + return IN6_ADDR_EQUAL(&Address1->In6Addr, &Address2->In6Addr); +//#ifdef _WIN64 +// return +// *(PULONG64)(Address1->Ipv6) == *(PULONG64)(Address2->Ipv6) && +// *(PULONG64)(Address1->Ipv6 + 8) == *(PULONG64)(Address2->Ipv6 + 8); +//#else +// return +// *(PULONG)(Address1->Ipv6) == *(PULONG)(Address2->Ipv6) && +// *(PULONG)(Address1->Ipv6 + 4) == *(PULONG)(Address2->Ipv6 + 4) && +// *(PULONG)(Address1->Ipv6 + 8) == *(PULONG)(Address2->Ipv6 + 8) && +// *(PULONG)(Address1->Ipv6 + 12) == *(PULONG)(Address2->Ipv6 + 12); +//#endif + } +} + +FORCEINLINE ULONG PhHashIpAddress( + _In_ PPH_IP_ADDRESS Address + ) +{ + ULONG hash = 0; + + if (Address->Type == 0) + return 0; + + hash = Address->Type | (Address->Type << 16); + + if (Address->Type == PH_IPV4_NETWORK_TYPE) + { + hash ^= Address->Ipv4; + } + else + { + hash += *(PULONG)(Address->Ipv6); + hash ^= *(PULONG)(Address->Ipv6 + 4); + hash += *(PULONG)(Address->Ipv6 + 8); + hash ^= *(PULONG)(Address->Ipv6 + 12); + } + + return hash; +} + +FORCEINLINE BOOLEAN PhIsNullIpAddress( + _In_ PPH_IP_ADDRESS Address + ) +{ + if (Address->Type == 0) + { + return TRUE; + } + else if (Address->Type == PH_IPV4_NETWORK_TYPE) + { + return Address->Ipv4 == 0; + } + else if (Address->Type == PH_IPV6_NETWORK_TYPE) + { +#ifdef _WIN64 + return (*(PULONG64)(Address->Ipv6) | *(PULONG64)(Address->Ipv6 + 8)) == 0; +#else + return (*(PULONG)(Address->Ipv6) | *(PULONG)(Address->Ipv6 + 4) | + *(PULONG)(Address->Ipv6 + 8) | *(PULONG)(Address->Ipv6 + 12)) == 0; +#endif + } + else + { + return TRUE; + } +} + +typedef struct _PH_IP_ENDPOINT +{ + PH_IP_ADDRESS Address; + ULONG Port; +} PH_IP_ENDPOINT, *PPH_IP_ENDPOINT; + +FORCEINLINE BOOLEAN PhEqualIpEndpoint( + _In_ PPH_IP_ENDPOINT Endpoint1, + _In_ PPH_IP_ENDPOINT Endpoint2 + ) +{ + return + PhEqualIpAddress(&Endpoint1->Address, &Endpoint2->Address) && + Endpoint1->Port == Endpoint2->Port; +} + +FORCEINLINE ULONG PhHashIpEndpoint( + _In_ PPH_IP_ENDPOINT Endpoint + ) +{ + return PhHashIpAddress(&Endpoint->Address) ^ Endpoint->Port; +} + +#endif diff --git a/phlib/include/phsup.h b/phlib/include/phsup.h index 3985914d1f49..3ef4ed36fdc2 100644 --- a/phlib/include/phsup.h +++ b/phlib/include/phsup.h @@ -1,546 +1,552 @@ -#ifndef _PH_PHSUP_H -#define _PH_PHSUP_H - -// This header file provides some useful definitions specific to phlib. - -#include -#include -#include -#include - -// Memory - -#define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) -#define PTR_SUB_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) - (ULONG_PTR)(Offset))) -#define ALIGN_UP_BY(Address, Align) (((ULONG_PTR)(Address) + (Align) - 1) & ~((Align) - 1)) -#define ALIGN_UP_POINTER_BY(Pointer, Align) ((PVOID)ALIGN_UP_BY(Pointer, Align)) -#define ALIGN_UP(Address, Type) ALIGN_UP_BY(Address, sizeof(Type)) -#define ALIGN_UP_POINTER(Pointer, Type) ((PVOID)ALIGN_UP(Pointer, Type)) - -#define PAGE_SIZE 0x1000 - -#define PH_LARGE_BUFFER_SIZE (256 * 1024 * 1024) - -// Exceptions - -#define PhRaiseStatus(Status) RtlRaiseStatus(Status) - -#define SIMPLE_EXCEPTION_FILTER(Condition) \ - ((Condition) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) - -// Compiler - -#ifdef DEBUG -#define ASSUME_ASSERT(Expression) assert(Expression) -#define ASSUME_NO_DEFAULT assert(FALSE) -#else -#define ASSUME_ASSERT(Expression) __assume(Expression) -#define ASSUME_NO_DEFAULT __assume(FALSE) -#endif - -// Time - -#define PH_TICKS_PER_NS ((LONG64)1 * 10) -#define PH_TICKS_PER_MS (PH_TICKS_PER_NS * 1000) -#define PH_TICKS_PER_SEC (PH_TICKS_PER_MS * 1000) -#define PH_TICKS_PER_MIN (PH_TICKS_PER_SEC * 60) -#define PH_TICKS_PER_HOUR (PH_TICKS_PER_MIN * 60) -#define PH_TICKS_PER_DAY (PH_TICKS_PER_HOUR * 24) - -#define PH_TICKS_PARTIAL_MS(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_MS) % 1000) -#define PH_TICKS_PARTIAL_SEC(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_SEC) % 60) -#define PH_TICKS_PARTIAL_MIN(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_MIN) % 60) -#define PH_TICKS_PARTIAL_HOURS(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_HOUR) % 24) -#define PH_TICKS_PARTIAL_DAYS(Ticks) ((ULONG64)(Ticks) / PH_TICKS_PER_DAY) - -#define PH_TIMEOUT_MS PH_TICKS_PER_MS -#define PH_TIMEOUT_SEC PH_TICKS_PER_SEC - -// Annotations - -/** - * Indicates that a function assumes the specified number of references are available for the - * object. - * - * \remarks Usually functions reference objects if they store them for later usage; this annotation - * specifies that the caller must supply these extra references itself. In effect these references - * are "transferred" to the function and must not be used. E.g. if you create an object and - * immediately call a function with _Assume_refs_(1), you may no longer use the object since that - * one reference you held is no longer yours. - */ -#define _Assume_refs_(count) - -#define _Callback_ - -/** - * Indicates that a function may raise a software exception. - * - * \remarks Do not use this annotation for temporary usages of exceptions, e.g. unimplemented - * functions. - */ -#define _May_raise_ - -/** - * Indicates that a function requires the specified value to be aligned at the specified number of - * bytes. - */ -#define _Needs_align_(align) - -// Casts - -// Zero extension and sign extension macros -#define C_1uTo2(x) ((unsigned short)(unsigned char)(x)) -#define C_1sTo2(x) ((unsigned short)(signed char)(x)) -#define C_1uTo4(x) ((unsigned int)(unsigned char)(x)) -#define C_1sTo4(x) ((unsigned int)(signed char)(x)) -#define C_2uTo4(x) ((unsigned int)(unsigned short)(x)) -#define C_2sTo4(x) ((unsigned int)(signed short)(x)) -#define C_4uTo8(x) ((unsigned __int64)(unsigned int)(x)) -#define C_4sTo8(x) ((unsigned __int64)(signed int)(x)) - -// Sorting - -typedef enum _PH_SORT_ORDER -{ - NoSortOrder = 0, - AscendingSortOrder, - DescendingSortOrder -} PH_SORT_ORDER, *PPH_SORT_ORDER; - -FORCEINLINE LONG PhModifySort( - _In_ LONG Result, - _In_ PH_SORT_ORDER Order - ) -{ - if (Order == AscendingSortOrder) - return Result; - else if (Order == DescendingSortOrder) - return -Result; - else - return Result; -} - -#define PH_BUILTIN_COMPARE(value1, value2) \ - if (value1 > value2) \ - return 1; \ - else if (value1 < value2) \ - return -1; \ - \ - return 0 - -FORCEINLINE int charcmp( - _In_ signed char value1, - _In_ signed char value2 - ) -{ - return C_1sTo4(value1 - value2); -} - -FORCEINLINE int ucharcmp( - _In_ unsigned char value1, - _In_ unsigned char value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int shortcmp( - _In_ signed short value1, - _In_ signed short value2 - ) -{ - return C_2sTo4(value1 - value2); -} - -FORCEINLINE int ushortcmp( - _In_ unsigned short value1, - _In_ unsigned short value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int intcmp( - _In_ int value1, - _In_ int value2 - ) -{ - return value1 - value2; -} - -FORCEINLINE int uintcmp( - _In_ unsigned int value1, - _In_ unsigned int value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int int64cmp( - _In_ __int64 value1, - _In_ __int64 value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int uint64cmp( - _In_ unsigned __int64 value1, - _In_ unsigned __int64 value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int intptrcmp( - _In_ LONG_PTR value1, - _In_ LONG_PTR value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int uintptrcmp( - _In_ ULONG_PTR value1, - _In_ ULONG_PTR value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int singlecmp( - _In_ float value1, - _In_ float value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int doublecmp( - _In_ double value1, - _In_ double value2 - ) -{ - PH_BUILTIN_COMPARE(value1, value2); -} - -FORCEINLINE int wcsicmp2( - _In_opt_ PWSTR Value1, - _In_opt_ PWSTR Value2 - ) -{ - if (Value1 && Value2) - return _wcsicmp(Value1, Value2); - else if (!Value1) - return !Value2 ? 0 : -1; - else - return 1; -} - -typedef int (__cdecl *PC_COMPARE_FUNCTION)(void *, const void *, const void *); - -// Synchronization - -#ifndef _WIN64 - -#ifndef _InterlockedCompareExchangePointer -void *_InterlockedCompareExchangePointer( - void *volatile *Destination, - void *Exchange, - void *Comparand - ); -#endif - -#if (_MSC_VER < 1900) -#ifndef _InterlockedExchangePointer -FORCEINLINE void *_InterlockedExchangePointer( - void *volatile *Destination, - void *Exchange - ) -{ - return (PVOID)_InterlockedExchange( - (PLONG_PTR)Destination, - (LONG_PTR)Exchange - ); -} -#endif -#endif - -#endif - -FORCEINLINE LONG_PTR _InterlockedExchangeAddPointer( - _Inout_ _Interlocked_operand_ LONG_PTR volatile *Addend, - _In_ LONG_PTR Value - ) -{ -#ifdef _WIN64 - return (LONG_PTR)_InterlockedExchangeAdd64((PLONG64)Addend, (LONG64)Value); -#else - return (LONG_PTR)_InterlockedExchangeAdd((PLONG)Addend, (LONG)Value); -#endif -} - -FORCEINLINE LONG_PTR _InterlockedIncrementPointer( - _Inout_ _Interlocked_operand_ LONG_PTR volatile *Addend - ) -{ -#ifdef _WIN64 - return (LONG_PTR)_InterlockedIncrement64((PLONG64)Addend); -#else - return (LONG_PTR)_InterlockedIncrement((PLONG)Addend); -#endif -} - -FORCEINLINE LONG_PTR _InterlockedDecrementPointer( - _Inout_ _Interlocked_operand_ LONG_PTR volatile *Addend - ) -{ -#ifdef _WIN64 - return (LONG_PTR)_InterlockedDecrement64((PLONG64)Addend); -#else - return (LONG_PTR)_InterlockedDecrement((PLONG)Addend); -#endif -} - -FORCEINLINE BOOLEAN _InterlockedBitTestAndResetPointer( - _Inout_ _Interlocked_operand_ LONG_PTR volatile *Base, - _In_ LONG_PTR Bit - ) -{ -#ifdef _WIN64 - return _interlockedbittestandreset64((PLONG64)Base, (LONG64)Bit); -#else - return _interlockedbittestandreset((PLONG)Base, (LONG)Bit); -#endif -} - -FORCEINLINE BOOLEAN _InterlockedBitTestAndSetPointer( - _Inout_ _Interlocked_operand_ LONG_PTR volatile *Base, - _In_ LONG_PTR Bit - ) -{ -#ifdef _WIN64 - return _interlockedbittestandset64((PLONG64)Base, (LONG64)Bit); -#else - return _interlockedbittestandset((PLONG)Base, (LONG)Bit); -#endif -} - -FORCEINLINE BOOLEAN _InterlockedIncrementNoZero( - _Inout_ _Interlocked_operand_ LONG volatile *Addend - ) -{ - LONG value; - LONG newValue; - - value = *Addend; - - while (TRUE) - { - if (value == 0) - return FALSE; - - if ((newValue = _InterlockedCompareExchange( - Addend, - value + 1, - value - )) == value) - { - return TRUE; - } - - value = newValue; - } -} - -FORCEINLINE BOOLEAN _InterlockedIncrementPositive( - _Inout_ _Interlocked_operand_ LONG volatile *Addend - ) -{ - LONG value; - LONG newValue; - - value = *Addend; - - while (TRUE) - { - if (value <= 0) - return FALSE; - - if ((newValue = _InterlockedCompareExchange( - Addend, - value + 1, - value - )) == value) - { - return TRUE; - } - - value = newValue; - } -} - -// Strings - -#define PH_INT32_STR_LEN 12 -#define PH_INT32_STR_LEN_1 (PH_INT32_STR_LEN + 1) - -#define PH_INT64_STR_LEN 50 -#define PH_INT64_STR_LEN_1 (PH_INT64_STR_LEN + 1) - -#define PH_PTR_STR_LEN 24 -#define PH_PTR_STR_LEN_1 (PH_PTR_STR_LEN + 1) - -FORCEINLINE VOID PhPrintInt32( - _Out_writes_(PH_INT32_STR_LEN_1) PWSTR Destination, - _In_ LONG Int32 - ) -{ - _ltow(Int32, Destination, 10); -} - -FORCEINLINE VOID PhPrintUInt32( - _Out_writes_(PH_INT32_STR_LEN_1) PWSTR Destination, - _In_ ULONG UInt32 - ) -{ - _ultow(UInt32, Destination, 10); -} - -FORCEINLINE VOID PhPrintInt64( - _Out_writes_(PH_INT64_STR_LEN_1) PWSTR Destination, - _In_ LONG64 Int64 - ) -{ - _i64tow(Int64, Destination, 10); -} - -FORCEINLINE VOID PhPrintUInt64( - _Out_writes_(PH_INT64_STR_LEN_1) PWSTR Destination, - _In_ ULONG64 UInt64 - ) -{ - _ui64tow(UInt64, Destination, 10); -} - -FORCEINLINE VOID PhPrintPointer( - _Out_writes_(PH_PTR_STR_LEN_1) PWSTR Destination, - _In_ PVOID Pointer - ) -{ - Destination[0] = '0'; - Destination[1] = 'x'; -#ifdef _WIN64 - _ui64tow((ULONG64)Pointer, &Destination[2], 16); -#else - _ultow((ULONG)Pointer, &Destination[2], 16); -#endif -} - -// Misc. - -FORCEINLINE ULONG PhCountBits( - _In_ ULONG Value - ) -{ - ULONG count = 0; - - while (Value) - { - count++; - Value &= Value - 1; - } - - return count; -} - -FORCEINLINE ULONG PhRoundNumber( - _In_ ULONG Value, - _In_ ULONG Granularity - ) -{ - return (Value + Granularity / 2) / Granularity * Granularity; -} - -FORCEINLINE ULONG PhMultiplyDivide( - _In_ ULONG Number, - _In_ ULONG Numerator, - _In_ ULONG Denominator - ) -{ - return (ULONG)(((ULONG64)Number * (ULONG64)Numerator + Denominator / 2) / (ULONG64)Denominator); -} - -FORCEINLINE LONG PhMultiplyDivideSigned( - _In_ LONG Number, - _In_ ULONG Numerator, - _In_ ULONG Denominator - ) -{ - if (Number >= 0) - return PhMultiplyDivide(Number, Numerator, Denominator); - else - return -(LONG)PhMultiplyDivide(-Number, Numerator, Denominator); -} - -FORCEINLINE VOID PhProbeAddress( - _In_ PVOID UserAddress, - _In_ SIZE_T UserLength, - _In_ PVOID BufferAddress, - _In_ SIZE_T BufferLength, - _In_ ULONG Alignment - ) -{ - if (UserLength != 0) - { - if (((ULONG_PTR)UserAddress & (Alignment - 1)) != 0) - PhRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); - - if ( - ((ULONG_PTR)UserAddress + UserLength < (ULONG_PTR)UserAddress) || - ((ULONG_PTR)UserAddress < (ULONG_PTR)BufferAddress) || - ((ULONG_PTR)UserAddress + UserLength > (ULONG_PTR)BufferAddress + BufferLength) - ) - PhRaiseStatus(STATUS_ACCESS_VIOLATION); - } -} - -FORCEINLINE PLARGE_INTEGER PhTimeoutFromMilliseconds( - _Out_ PLARGE_INTEGER Timeout, - _In_ ULONG Milliseconds - ) -{ - if (Milliseconds == INFINITE) - return NULL; - - Timeout->QuadPart = -(LONGLONG)UInt32x32To64(Milliseconds, PH_TIMEOUT_MS); - - return Timeout; -} - -FORCEINLINE NTSTATUS PhGetLastWin32ErrorAsNtStatus() -{ - ULONG win32Result; - - // This is needed because NTSTATUS_FROM_WIN32 uses the argument multiple times. - win32Result = GetLastError(); - - return NTSTATUS_FROM_WIN32(win32Result); -} - -FORCEINLINE PVOID PhGetModuleProcAddress( - _In_ PWSTR ModuleName, - _In_ PSTR ProcName - ) -{ - HMODULE module; - - module = GetModuleHandle(ModuleName); - - if (module) - return GetProcAddress(module, ProcName); - else - return NULL; -} - -#endif +#ifndef _PH_PHSUP_H +#define _PH_PHSUP_H + +// This header file provides some useful definitions specific to phlib. + +#include +#include +#include +#include + +// Memory + +#define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) +#define PTR_SUB_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) - (ULONG_PTR)(Offset))) +#define ALIGN_UP_BY(Address, Align) (((ULONG_PTR)(Address) + (Align) - 1) & ~((Align) - 1)) +#define ALIGN_UP_POINTER_BY(Pointer, Align) ((PVOID)ALIGN_UP_BY(Pointer, Align)) +#define ALIGN_UP(Address, Type) ALIGN_UP_BY(Address, sizeof(Type)) +#define ALIGN_UP_POINTER(Pointer, Type) ((PVOID)ALIGN_UP(Pointer, Type)) +#define ALIGN_DOWN_BY(Address, Align) ((ULONG_PTR)(Address) & ~((ULONG_PTR)(Align) - 1)) +#define ALIGN_DOWN_POINTER_BY(Pointer, Align) ((PVOID)ALIGN_DOWN_BY(Pointer, Align)) +#define ALIGN_DOWN(Address, Type) ALIGN_DOWN_BY(Address, sizeof(Type)) +#define ALIGN_DOWN_POINTER(Pointer, Type) ((PVOID)ALIGN_DOWN(Pointer, Type)) + +#define PAGE_SIZE 0x1000 + +#define PH_LARGE_BUFFER_SIZE (256 * 1024 * 1024) + +// Exceptions + +#define PhRaiseStatus(Status) RtlRaiseStatus(Status) + +#define SIMPLE_EXCEPTION_FILTER(Condition) \ + ((Condition) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + +// Compiler + +#ifdef DEBUG +#define ASSUME_ASSERT(Expression) assert(Expression) +#define ASSUME_NO_DEFAULT assert(FALSE) +#else +#define ASSUME_ASSERT(Expression) __assume(Expression) +#define ASSUME_NO_DEFAULT __assume(FALSE) +#endif + +// Math + +#define UInt32Add32To64(a, b) ((unsigned __int64)((unsigned __int64)(a) + ((unsigned __int64)(b)))) // Avoids warning C26451 (dmex) + +// Time + +#define PH_TICKS_PER_NS ((LONG64)1 * 10) +#define PH_TICKS_PER_MS (PH_TICKS_PER_NS * 1000) +#define PH_TICKS_PER_SEC (PH_TICKS_PER_MS * 1000) +#define PH_TICKS_PER_MIN (PH_TICKS_PER_SEC * 60) +#define PH_TICKS_PER_HOUR (PH_TICKS_PER_MIN * 60) +#define PH_TICKS_PER_DAY (PH_TICKS_PER_HOUR * 24) + +#define PH_TICKS_PARTIAL_MS(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_MS) % 1000) +#define PH_TICKS_PARTIAL_SEC(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_SEC) % 60) +#define PH_TICKS_PARTIAL_MIN(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_MIN) % 60) +#define PH_TICKS_PARTIAL_HOURS(Ticks) (((ULONG64)(Ticks) / PH_TICKS_PER_HOUR) % 24) +#define PH_TICKS_PARTIAL_DAYS(Ticks) ((ULONG64)(Ticks) / PH_TICKS_PER_DAY) + +#define PH_TIMEOUT_MS PH_TICKS_PER_MS +#define PH_TIMEOUT_SEC PH_TICKS_PER_SEC + +// Annotations + +/** + * Indicates that a function assumes the specified number of references are available for the + * object. + * + * \remarks Usually functions reference objects if they store them for later usage; this annotation + * specifies that the caller must supply these extra references itself. In effect these references + * are "transferred" to the function and must not be used. E.g. if you create an object and + * immediately call a function with _Assume_refs_(1), you may no longer use the object since that + * one reference you held is no longer yours. + */ +#define _Assume_refs_(count) + +#define _Callback_ + +/** + * Indicates that a function may raise a software exception. + * + * \remarks Do not use this annotation for temporary usages of exceptions, e.g. unimplemented + * functions. + */ +#define _May_raise_ + +/** + * Indicates that a function requires the specified value to be aligned at the specified number of + * bytes. + */ +#define _Needs_align_(align) + +// Casts + +// Zero extension and sign extension macros +#define C_1uTo2(x) ((unsigned short)(unsigned char)(x)) +#define C_1sTo2(x) ((unsigned short)(signed char)(x)) +#define C_1uTo4(x) ((unsigned int)(unsigned char)(x)) +#define C_1sTo4(x) ((unsigned int)(signed char)(x)) +#define C_2uTo4(x) ((unsigned int)(unsigned short)(x)) +#define C_2sTo4(x) ((unsigned int)(signed short)(x)) +#define C_4uTo8(x) ((unsigned __int64)(unsigned int)(x)) +#define C_4sTo8(x) ((unsigned __int64)(signed int)(x)) + +// Sorting + +typedef enum _PH_SORT_ORDER +{ + NoSortOrder = 0, + AscendingSortOrder, + DescendingSortOrder +} PH_SORT_ORDER, *PPH_SORT_ORDER; + +FORCEINLINE LONG PhModifySort( + _In_ LONG Result, + _In_ PH_SORT_ORDER Order + ) +{ + if (Order == AscendingSortOrder) + return Result; + else if (Order == DescendingSortOrder) + return -Result; + else + return Result; +} + +#define PH_BUILTIN_COMPARE(value1, value2) \ + if (value1 > value2) \ + return 1; \ + else if (value1 < value2) \ + return -1; \ + \ + return 0 + +FORCEINLINE int charcmp( + _In_ signed char value1, + _In_ signed char value2 + ) +{ + return C_1sTo4(value1 - value2); +} + +FORCEINLINE int ucharcmp( + _In_ unsigned char value1, + _In_ unsigned char value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int shortcmp( + _In_ signed short value1, + _In_ signed short value2 + ) +{ + return C_2sTo4(value1 - value2); +} + +FORCEINLINE int ushortcmp( + _In_ unsigned short value1, + _In_ unsigned short value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int intcmp( + _In_ int value1, + _In_ int value2 + ) +{ + return value1 - value2; +} + +FORCEINLINE int uintcmp( + _In_ unsigned int value1, + _In_ unsigned int value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int int64cmp( + _In_ __int64 value1, + _In_ __int64 value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int uint64cmp( + _In_ unsigned __int64 value1, + _In_ unsigned __int64 value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int intptrcmp( + _In_ LONG_PTR value1, + _In_ LONG_PTR value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int uintptrcmp( + _In_ ULONG_PTR value1, + _In_ ULONG_PTR value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int singlecmp( + _In_ float value1, + _In_ float value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int doublecmp( + _In_ double value1, + _In_ double value2 + ) +{ + PH_BUILTIN_COMPARE(value1, value2); +} + +FORCEINLINE int wcsicmp2( + _In_opt_ PWSTR Value1, + _In_opt_ PWSTR Value2 + ) +{ + if (Value1 && Value2) + return _wcsicmp(Value1, Value2); + else if (!Value1) + return !Value2 ? 0 : -1; + else + return 1; +} + +typedef int (__cdecl *PC_COMPARE_FUNCTION)(void *, const void *, const void *); + +// Synchronization + +#ifndef _WIN64 + +#ifndef _InterlockedCompareExchangePointer +void *_InterlockedCompareExchangePointer( + void *volatile *Destination, + void *Exchange, + void *Comparand + ); +#endif + +#if (_MSC_VER < 1900) +#ifndef _InterlockedExchangePointer +FORCEINLINE void *_InterlockedExchangePointer( + void *volatile *Destination, + void *Exchange + ) +{ + return (PVOID)_InterlockedExchange( + (PLONG_PTR)Destination, + (LONG_PTR)Exchange + ); +} +#endif +#endif + +#endif + +FORCEINLINE LONG_PTR _InterlockedExchangeAddPointer( + _Inout_ _Interlocked_operand_ LONG_PTR volatile *Addend, + _In_ LONG_PTR Value + ) +{ +#ifdef _WIN64 + return (LONG_PTR)_InterlockedExchangeAdd64((PLONG64)Addend, (LONG64)Value); +#else + return (LONG_PTR)_InterlockedExchangeAdd((PLONG)Addend, (LONG)Value); +#endif +} + +FORCEINLINE LONG_PTR _InterlockedIncrementPointer( + _Inout_ _Interlocked_operand_ LONG_PTR volatile *Addend + ) +{ +#ifdef _WIN64 + return (LONG_PTR)_InterlockedIncrement64((PLONG64)Addend); +#else + return (LONG_PTR)_InterlockedIncrement((PLONG)Addend); +#endif +} + +FORCEINLINE LONG_PTR _InterlockedDecrementPointer( + _Inout_ _Interlocked_operand_ LONG_PTR volatile *Addend + ) +{ +#ifdef _WIN64 + return (LONG_PTR)_InterlockedDecrement64((PLONG64)Addend); +#else + return (LONG_PTR)_InterlockedDecrement((PLONG)Addend); +#endif +} + +FORCEINLINE BOOLEAN _InterlockedBitTestAndResetPointer( + _Inout_ _Interlocked_operand_ LONG_PTR volatile *Base, + _In_ LONG_PTR Bit + ) +{ +#ifdef _WIN64 + return _interlockedbittestandreset64((PLONG64)Base, (LONG64)Bit); +#else + return _interlockedbittestandreset((PLONG)Base, (LONG)Bit); +#endif +} + +FORCEINLINE BOOLEAN _InterlockedBitTestAndSetPointer( + _Inout_ _Interlocked_operand_ LONG_PTR volatile *Base, + _In_ LONG_PTR Bit + ) +{ +#ifdef _WIN64 + return _interlockedbittestandset64((PLONG64)Base, (LONG64)Bit); +#else + return _interlockedbittestandset((PLONG)Base, (LONG)Bit); +#endif +} + +FORCEINLINE BOOLEAN _InterlockedIncrementNoZero( + _Inout_ _Interlocked_operand_ LONG volatile *Addend + ) +{ + LONG value; + LONG newValue; + + value = *Addend; + + while (TRUE) + { + if (value == 0) + return FALSE; + + if ((newValue = _InterlockedCompareExchange( + Addend, + value + 1, + value + )) == value) + { + return TRUE; + } + + value = newValue; + } +} + +FORCEINLINE BOOLEAN _InterlockedIncrementPositive( + _Inout_ _Interlocked_operand_ LONG volatile *Addend + ) +{ + LONG value; + LONG newValue; + + value = *Addend; + + while (TRUE) + { + if (value <= 0) + return FALSE; + + if ((newValue = _InterlockedCompareExchange( + Addend, + value + 1, + value + )) == value) + { + return TRUE; + } + + value = newValue; + } +} + +// Strings + +#define PH_INT32_STR_LEN 12 +#define PH_INT32_STR_LEN_1 (PH_INT32_STR_LEN + 1) + +#define PH_INT64_STR_LEN 50 +#define PH_INT64_STR_LEN_1 (PH_INT64_STR_LEN + 1) + +#define PH_PTR_STR_LEN 24 +#define PH_PTR_STR_LEN_1 (PH_PTR_STR_LEN + 1) + +FORCEINLINE VOID PhPrintInt32( + _Out_writes_(PH_INT32_STR_LEN_1) PWSTR Destination, + _In_ LONG Int32 + ) +{ + _ltow(Int32, Destination, 10); +} + +FORCEINLINE VOID PhPrintUInt32( + _Out_writes_(PH_INT32_STR_LEN_1) PWSTR Destination, + _In_ ULONG UInt32 + ) +{ + _ultow(UInt32, Destination, 10); +} + +FORCEINLINE VOID PhPrintUInt32Hex( + _Out_writes_(PH_PTR_STR_LEN_1) PWSTR Destination, + _In_ ULONG UInt32 + ) +{ + _ultow(UInt32, Destination, 16); +} + +FORCEINLINE VOID PhPrintInt64( + _Out_writes_(PH_INT64_STR_LEN_1) PWSTR Destination, + _In_ LONG64 Int64 + ) +{ + _i64tow(Int64, Destination, 10); +} + +FORCEINLINE VOID PhPrintUInt64( + _Out_writes_(PH_INT64_STR_LEN_1) PWSTR Destination, + _In_ ULONG64 UInt64 + ) +{ + _ui64tow(UInt64, Destination, 10); +} + +FORCEINLINE VOID PhPrintPointer( + _Out_writes_(PH_PTR_STR_LEN_1) PWSTR Destination, + _In_ PVOID Pointer + ) +{ + Destination[0] = '0'; + Destination[1] = 'x'; +#ifdef _WIN64 + _ui64tow((ULONG64)Pointer, &Destination[2], 16); +#else + _ultow((ULONG)Pointer, &Destination[2], 16); +#endif +} + +// Misc. + +FORCEINLINE ULONG PhCountBits( + _In_ ULONG Value + ) +{ + ULONG count = 0; + + while (Value) + { + count++; + Value &= Value - 1; + } + + return count; +} + +FORCEINLINE ULONG64 PhRoundNumber( + _In_ ULONG64 Value, + _In_ ULONG64 Granularity + ) +{ + return (Value + Granularity / 2) / Granularity * Granularity; +} + +FORCEINLINE ULONG PhMultiplyDivide( + _In_ ULONG Number, + _In_ ULONG Numerator, + _In_ ULONG Denominator + ) +{ + return (ULONG)(((ULONG64)Number * (ULONG64)Numerator + Denominator / 2) / (ULONG64)Denominator); +} + +FORCEINLINE LONG PhMultiplyDivideSigned( + _In_ LONG Number, + _In_ ULONG Numerator, + _In_ ULONG Denominator + ) +{ + if (Number >= 0) + return PhMultiplyDivide(Number, Numerator, Denominator); + else + return -(LONG)PhMultiplyDivide(-Number, Numerator, Denominator); +} + +FORCEINLINE VOID PhProbeAddress( + _In_ PVOID UserAddress, + _In_ SIZE_T UserLength, + _In_ PVOID BufferAddress, + _In_ SIZE_T BufferLength, + _In_ ULONG Alignment + ) +{ + if (UserLength != 0) + { + if (((ULONG_PTR)UserAddress & (Alignment - 1)) != 0) + PhRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); + + if ( + ((ULONG_PTR)UserAddress + UserLength < (ULONG_PTR)UserAddress) || + ((ULONG_PTR)UserAddress < (ULONG_PTR)BufferAddress) || + ((ULONG_PTR)UserAddress + UserLength > (ULONG_PTR)BufferAddress + BufferLength) + ) + PhRaiseStatus(STATUS_ACCESS_VIOLATION); + } +} + +FORCEINLINE PLARGE_INTEGER PhTimeoutFromMilliseconds( + _Out_ PLARGE_INTEGER Timeout, + _In_ ULONG Milliseconds + ) +{ + if (Milliseconds == INFINITE) + return NULL; + + Timeout->QuadPart = -(LONGLONG)UInt32x32To64(Milliseconds, PH_TIMEOUT_MS); + + return Timeout; +} + +#define PhTimeoutFromMillisecondsEx(Milliseconds) \ + &(LARGE_INTEGER) { -(LONGLONG)UInt32x32To64((Milliseconds), PH_TIMEOUT_MS) } + +FORCEINLINE NTSTATUS PhGetLastWin32ErrorAsNtStatus( + VOID + ) +{ + ULONG win32Result; + + // This is needed because NTSTATUS_FROM_WIN32 uses the argument multiple times. + win32Result = GetLastError(); + + return NTSTATUS_FROM_WIN32(win32Result); +} + +#endif diff --git a/phlib/include/phutil.h b/phlib/include/phutil.h index 0ee65e7c0fd3..7dbfecbf9bd8 100644 --- a/phlib/include/phutil.h +++ b/phlib/include/phutil.h @@ -172,22 +172,18 @@ PhLargeIntegerToLocalSystemTime( FileTimeToSystemTime(&newFileTime, SystemTime); } -PHLIBAPI -VOID -NTAPI -PhReferenceObjects( - _In_reads_(NumberOfObjects) PVOID *Objects, - _In_ ULONG NumberOfObjects +// NLS + +LCID PhGetSystemDefaultLCID( + VOID ); -PHLIBAPI -VOID -NTAPI -PhDereferenceObjects( - _In_reads_(NumberOfObjects) PVOID *Objects, - _In_ ULONG NumberOfObjects +LCID PhGetUserDefaultLCID( + VOID ); +// Error messages + PHLIBAPI PPH_STRING NTAPI @@ -216,35 +212,32 @@ PHLIBAPI INT NTAPI PhShowMessage( - _In_ HWND hWnd, + _In_opt_ HWND hWnd, _In_ ULONG Type, _In_ PWSTR Format, ... ); -PHLIBAPI -INT -NTAPI -PhShowMessage_V( - _In_ HWND hWnd, - _In_ ULONG Type, - _In_ PWSTR Format, - _In_ va_list ArgPtr - ); - #define PhShowError(hWnd, Format, ...) PhShowMessage(hWnd, MB_OK | MB_ICONERROR, Format, __VA_ARGS__) #define PhShowWarning(hWnd, Format, ...) PhShowMessage(hWnd, MB_OK | MB_ICONWARNING, Format, __VA_ARGS__) #define PhShowInformation(hWnd, Format, ...) PhShowMessage(hWnd, MB_OK | MB_ICONINFORMATION, Format, __VA_ARGS__) PHLIBAPI -INT +INT NTAPI -PhShowError2( - _In_ HWND hWnd, - _In_opt_ PWSTR Title, - _In_opt_ PWSTR Message +PhShowMessage2( + _In_opt_ HWND hWnd, + _In_ ULONG Buttons, + _In_opt_ PWSTR Icon, + _In_opt_ PWSTR Title, + _In_ PWSTR Format, + ... ); +#define PhShowError2(hWnd, Title, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, Title, Format, __VA_ARGS__) +#define PhShowWarning2(hWnd, Title, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_WARNING_ICON, Title, Format, __VA_ARGS__) +#define PhShowInformation2(hWnd, Title, Format, ...) PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_INFORMATION_ICON, Title, Format, __VA_ARGS__) + PHLIBAPI PPH_STRING NTAPI @@ -257,7 +250,7 @@ PHLIBAPI VOID NTAPI PhShowStatus( - _In_ HWND hWnd, + _In_opt_ HWND hWnd, _In_opt_ PWSTR Message, _In_ NTSTATUS Status, _In_opt_ ULONG Win32Result @@ -557,6 +550,22 @@ PhFormatImageVersionInfo( _In_opt_ ULONG LineLimit ); +PHLIBAPI +BOOLEAN +NTAPI +PhInitializeImageVersionInfoCached( + _Out_ PPH_IMAGE_VERSION_INFO ImageVersionInfo, + _In_ PPH_STRING FileName, + _In_ BOOLEAN IsSubsystemProcess + ); + +PHLIBAPI +VOID +NTAPI +PhFlushImageVersionInfoCache( + VOID + ); + PHLIBAPI PPH_STRING NTAPI @@ -565,6 +574,15 @@ PhGetFullPath( _Out_opt_ PULONG IndexOfFileName ); +PHLIBAPI +NTSTATUS +NTAPI +PhGetFullPathEx( + _In_ PWSTR FileName, + _Out_opt_ PULONG IndexOfFileName, + _Out_ PPH_STRING *FullPath + ); + PHLIBAPI PPH_STRING NTAPI @@ -579,6 +597,13 @@ PhGetBaseName( _In_ PPH_STRING FileName ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetBaseDirectory( + _In_ PPH_STRING FileName + ); + PHLIBAPI PPH_STRING NTAPI @@ -594,20 +619,29 @@ PhGetSystemRoot( ); PHLIBAPI -PLDR_DATA_TABLE_ENTRY +PPH_STRING NTAPI -PhFindLoaderEntry( - _In_opt_ PVOID DllBase, - _In_opt_ PPH_STRINGREF FullDllName, - _In_opt_ PPH_STRINGREF BaseDllName +PhGetDllFileName( + _In_ PVOID DllBase, + _Out_opt_ PULONG IndexOfFileName ); PHLIBAPI -PPH_STRING +PVOID NTAPI -PhGetDllFileName( - _In_ PVOID DllHandle, - _Out_opt_ PULONG IndexOfFileName +PhGetDllBaseProcedureAddress( + _In_ PVOID DllBase, + _In_opt_ PSTR ProcedureName, + _In_opt_ USHORT ProcedureNumber + ); + +PHLIBAPI +PVOID +NTAPI +PhGetDllProcedureAddress( + _In_ PWSTR DllEntryName, + _In_opt_ PSTR ProcedureName, + _In_opt_ USHORT ProcedureNumber ); PHLIBAPI @@ -656,6 +690,9 @@ typedef struct _PH_CREATE_PROCESS_INFO #define PH_CREATE_PROCESS_SUSPENDED 0x4 #define PH_CREATE_PROCESS_BREAKAWAY_FROM_JOB 0x8 #define PH_CREATE_PROCESS_NEW_CONSOLE 0x10 +#define PH_CREATE_PROCESS_DEBUG 0x20 +#define PH_CREATE_PROCESS_DEBUG_ONLY_THIS_PROCESS 0x40 +#define PH_CREATE_PROCESS_EXTENDED_STARTUPINFO 0x80 PHLIBAPI NTSTATUS @@ -761,6 +798,7 @@ PhShellExecute( #define PH_SHELL_EXECUTE_ADMIN 0x1 #define PH_SHELL_EXECUTE_PUMP_MESSAGES 0x2 +#define PH_SHELL_EXECUTE_NOZONECHECKS 0x4 PHLIBAPI BOOLEAN @@ -815,6 +853,22 @@ PhQueryRegistryString( _In_opt_ PWSTR ValueName ); +PHLIBAPI +ULONG +NTAPI +PhQueryRegistryUlong( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ); + +PHLIBAPI +ULONG64 +NTAPI +PhQueryRegistryUlong64( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ); + typedef struct _PH_FLAG_MAPPING { ULONG Flag1; @@ -866,7 +920,7 @@ PHLIBAPI BOOLEAN NTAPI PhShowFileDialog( - _In_ HWND hWnd, + _In_opt_ HWND hWnd, _In_ PVOID FileDialog ); @@ -879,6 +933,7 @@ PhShowFileDialog( #define PH_FILEDIALOG_DEFAULTEXPANDED 0x40 #define PH_FILEDIALOG_STRICTFILETYPES 0x80 #define PH_FILEDIALOG_PICKFOLDERS 0x100 +#define PH_FILEDIALOG_NOPATHVALIDATE 0x200 PHLIBAPI ULONG @@ -1052,6 +1107,221 @@ PhParseCommandLineFuzzy( _Out_opt_ PPH_STRING *FullFileName ); +PHLIBAPI +PPH_STRING +NTAPI +PhSearchFilePath( + _In_ PWSTR FileName, + _In_opt_ PWSTR Extension + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhCreateCacheFile( + _In_ PPH_STRING FileName + ); + +PHLIBAPI +VOID +NTAPI +PhClearCacheDirectory( + VOID + ); + +PHLIBAPI +VOID +NTAPI +PhDeleteCacheFile( + _In_ PPH_STRING FileName + ); + +PHLIBAPI +HANDLE +NTAPI +PhGetNamespaceHandle( + VOID + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhLoadResource( + _In_ PVOID DllBase, + _In_ PCWSTR Name, + _In_ PCWSTR Type, + _Out_opt_ ULONG *ResourceLength, + _Out_ PVOID *ResourceBuffer + ); + +PHLIBAPI +HMENU +NTAPI +PhLoadMenu( + _In_ PVOID DllBase, + _In_ PWSTR MenuName + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhLoadIndirectString( + _In_ PWSTR SourceString + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhExtractIcon( + _In_ PWSTR FileName, + _Out_opt_ HICON *IconLarge, + _Out_opt_ HICON *IconSmall + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhExtractIconEx( + _In_ PWSTR FileName, + _In_ INT IconIndex, + _Out_opt_ HICON *IconLarge, + _Out_opt_ HICON *IconSmall + ); + +PHLIBAPI +PLDR_DATA_TABLE_ENTRY +NTAPI +PhFindLoaderEntry( + _In_opt_ PVOID DllBase, + _In_opt_ PPH_STRINGREF FullDllName, + _In_opt_ PPH_STRINGREF BaseDllName + ); + +PHLIBAPI +PVOID +NTAPI +PhGetDllBaseProcedureAddress( + _In_ PVOID DllBase, + _In_opt_ PSTR ProcedureName, + _In_opt_ USHORT ProcedureNumber + ); + +PHLIBAPI +PVOID +NTAPI +PhGetDllProcedureAddress( + _In_ PWSTR DllName, + _In_opt_ PSTR ProcedureName, + _In_opt_ USHORT ProcedureNumber + ); + +PHLIBAPI +PVOID +NTAPI +PhGetLoaderEntryDllBase( + _In_ PWSTR DllName + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetLoaderEntryImageNtHeaders( + _In_ PVOID BaseAddress, + _Out_ PIMAGE_NT_HEADERS *ImageNtHeaders + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetLoaderEntryImageDirectory( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ ULONG ImageDirectoryIndex, + _Out_ PIMAGE_DATA_DIRECTORY *ImageDataDirectoryEntry, + _Out_ PVOID *ImageDirectoryEntry, + _Out_opt_ SIZE_T *ImageDirectoryLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetLoaderEntryImageVaToSection( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ PVOID ImageDirectoryAddress, + _Out_ PVOID *ImageSectionAddress, + _Out_ SIZE_T *ImageSectionLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhLoaderEntryImageRvaToSection( + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ ULONG Rva, + _Out_ PIMAGE_SECTION_HEADER *ImageSection, + _Out_ SIZE_T *ImageSectionLength + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhLoaderEntryImageRvaToVa( + _In_ PVOID BaseAddress, + _In_ ULONG Rva, + _Out_ PVOID *Va + ); + +PHLIBAPI +PVOID +NTAPI +PhGetLoaderEntryImageExportFunction( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ PIMAGE_DATA_DIRECTORY DataDirectory, + _In_ PIMAGE_EXPORT_DIRECTORY ExportDirectory, + _In_opt_ PSTR ExportName, + _In_opt_ USHORT ExportOrdinal + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhLoadPluginImage( + _In_ PPH_STRING FileName, + _Out_opt_ PVOID *BaseAddress + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetExportNameFromOrdinal( + _In_ PVOID DllBase, + _In_opt_ USHORT ProcedureNumber + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhLoadAllImportsForDll( + _In_ PWSTR TargetDllName, + _In_ PSTR ImportDllName + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetFileText( + _In_ HANDLE FileHandle + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhFileReadAllText( + _In_ PWSTR FileName + ); + #ifdef __cplusplus } #endif diff --git a/phlib/include/queuedlock.h b/phlib/include/queuedlock.h index 2066b1bb2ee2..dd96ea0ecb36 100644 --- a/phlib/include/queuedlock.h +++ b/phlib/include/queuedlock.h @@ -1,349 +1,349 @@ -#ifndef _PH_QUEUEDLOCK_H -#define _PH_QUEUEDLOCK_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_QUEUED_LOCK_OWNED ((ULONG_PTR)0x1) -#define PH_QUEUED_LOCK_OWNED_SHIFT 0 -#define PH_QUEUED_LOCK_WAITERS ((ULONG_PTR)0x2) - -// Valid only if Waiters = 0 -#define PH_QUEUED_LOCK_SHARED_INC ((ULONG_PTR)0x4) -#define PH_QUEUED_LOCK_SHARED_SHIFT 2 - -// Valid only if Waiters = 1 -#define PH_QUEUED_LOCK_TRAVERSING ((ULONG_PTR)0x4) -#define PH_QUEUED_LOCK_MULTIPLE_SHARED ((ULONG_PTR)0x8) - -#define PH_QUEUED_LOCK_FLAGS ((ULONG_PTR)0xf) - -#define PhGetQueuedLockSharedOwners(Value) \ - ((ULONG_PTR)(Value) >> PH_QUEUED_LOCK_SHARED_SHIFT) -#define PhGetQueuedLockWaitBlock(Value) \ - ((PPH_QUEUED_WAIT_BLOCK)((ULONG_PTR)(Value) & ~PH_QUEUED_LOCK_FLAGS)) - -typedef struct _PH_QUEUED_LOCK -{ - ULONG_PTR Value; -} PH_QUEUED_LOCK, *PPH_QUEUED_LOCK; - -#define PH_QUEUED_LOCK_INIT { 0 } - -#define PH_QUEUED_WAITER_EXCLUSIVE 0x1 -#define PH_QUEUED_WAITER_SPINNING 0x2 -#define PH_QUEUED_WAITER_SPINNING_SHIFT 1 - -typedef struct DECLSPEC_ALIGN(16) _PH_QUEUED_WAIT_BLOCK -{ - /** A pointer to the next wait block, i.e. the wait block pushed onto the list before this one. */ - struct _PH_QUEUED_WAIT_BLOCK *Next; - /** - * A pointer to the previous wait block, i.e. the wait block pushed onto the list after this - * one. - */ - struct _PH_QUEUED_WAIT_BLOCK *Previous; - /** A pointer to the last wait block, i.e. the first waiter pushed onto the list. */ - struct _PH_QUEUED_WAIT_BLOCK *Last; - - ULONG SharedOwners; - ULONG Flags; -} PH_QUEUED_WAIT_BLOCK, *PPH_QUEUED_WAIT_BLOCK; - -BOOLEAN PhQueuedLockInitialization( - VOID - ); - -// Queued lock - -FORCEINLINE -VOID -PhInitializeQueuedLock( - _Out_ PPH_QUEUED_LOCK QueuedLock - ) -{ - QueuedLock->Value = 0; -} - -PHLIBAPI -VOID -FASTCALL -PhfAcquireQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ); - -_Acquires_exclusive_lock_(*QueuedLock) -FORCEINLINE -VOID -PhAcquireQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - if (_InterlockedBitTestAndSetPointer((PLONG_PTR)&QueuedLock->Value, PH_QUEUED_LOCK_OWNED_SHIFT)) - { - // Owned bit was already set. Slow path. - PhfAcquireQueuedLockExclusive(QueuedLock); - } -} - -PHLIBAPI -VOID -FASTCALL -PhfAcquireQueuedLockShared( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ); - -_Acquires_shared_lock_(*QueuedLock) -FORCEINLINE -VOID -PhAcquireQueuedLockShared( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)(PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_SHARED_INC), - (PVOID)0 - ) != 0) - { - PhfAcquireQueuedLockShared(QueuedLock); - } -} - -_When_(return != 0, _Acquires_exclusive_lock_(*QueuedLock)) -FORCEINLINE -BOOLEAN -PhTryAcquireQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - if (!_InterlockedBitTestAndSetPointer((PLONG_PTR)&QueuedLock->Value, PH_QUEUED_LOCK_OWNED_SHIFT)) - { - return TRUE; - } - else - { - return FALSE; - } -} - -PHLIBAPI -VOID -FASTCALL -PhfReleaseQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ); - -PHLIBAPI -VOID -FASTCALL -PhfWakeForReleaseQueuedLock( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value - ); - -_Releases_exclusive_lock_(*QueuedLock) -FORCEINLINE -VOID -PhReleaseQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - ULONG_PTR value; - - value = (ULONG_PTR)_InterlockedExchangeAddPointer((PLONG_PTR)&QueuedLock->Value, -(LONG_PTR)PH_QUEUED_LOCK_OWNED); - - if ((value & (PH_QUEUED_LOCK_WAITERS | PH_QUEUED_LOCK_TRAVERSING)) == PH_QUEUED_LOCK_WAITERS) - { - PhfWakeForReleaseQueuedLock(QueuedLock, value - PH_QUEUED_LOCK_OWNED); - } -} - -PHLIBAPI -VOID -FASTCALL -PhfReleaseQueuedLockShared( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ); - -_Releases_shared_lock_(*QueuedLock) -FORCEINLINE -VOID -PhReleaseQueuedLockShared( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - ULONG_PTR value; - - value = PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_SHARED_INC; - - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)0, - (PVOID)value - ) != value) - { - PhfReleaseQueuedLockShared(QueuedLock); - } -} - -FORCEINLINE -VOID -PhAcquireReleaseQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - BOOLEAN owned; - - MemoryBarrier(); - owned = !!(QueuedLock->Value & PH_QUEUED_LOCK_OWNED); - MemoryBarrier(); - - if (owned) - { - PhAcquireQueuedLockExclusive(QueuedLock); - PhReleaseQueuedLockExclusive(QueuedLock); - } -} - -FORCEINLINE -BOOLEAN -PhTryAcquireReleaseQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - BOOLEAN owned; - - // Need two memory barriers because we don't want the compiler re-ordering the following check - // in either direction. - MemoryBarrier(); - owned = !(QueuedLock->Value & PH_QUEUED_LOCK_OWNED); - MemoryBarrier(); - - return owned; -} - -// Condition variable - -typedef struct _PH_QUEUED_LOCK PH_CONDITION, *PPH_CONDITION; - -#define PH_CONDITION_INIT PH_QUEUED_LOCK_INIT - -FORCEINLINE -VOID -PhInitializeCondition( - _Out_ PPH_CONDITION Condition - ) -{ - PhInitializeQueuedLock(Condition); -} - -#define PhPulseCondition PhfPulseCondition -PHLIBAPI -VOID -FASTCALL -PhfPulseCondition( - _Inout_ PPH_CONDITION Condition - ); - -#define PhPulseAllCondition PhfPulseAllCondition -PHLIBAPI -VOID -FASTCALL -PhfPulseAllCondition( - _Inout_ PPH_CONDITION Condition - ); - -#define PhWaitForCondition PhfWaitForCondition -PHLIBAPI -VOID -FASTCALL -PhfWaitForCondition( - _Inout_ PPH_CONDITION Condition, - _Inout_ PPH_QUEUED_LOCK Lock, - _In_opt_ PLARGE_INTEGER Timeout - ); - -#define PH_CONDITION_WAIT_QUEUED_LOCK 0x1 -#define PH_CONDITION_WAIT_CRITICAL_SECTION 0x2 -#define PH_CONDITION_WAIT_FAST_LOCK 0x4 -#define PH_CONDITION_WAIT_LOCK_TYPE_MASK 0xfff - -#define PH_CONDITION_WAIT_SHARED 0x1000 -#define PH_CONDITION_WAIT_SPIN 0x2000 - -#define PhWaitForConditionEx PhfWaitForConditionEx -PHLIBAPI -VOID -FASTCALL -PhfWaitForConditionEx( - _Inout_ PPH_CONDITION Condition, - _Inout_ PVOID Lock, - _In_ ULONG Flags, - _In_opt_ PLARGE_INTEGER Timeout - ); - -// Wake event - -typedef struct _PH_QUEUED_LOCK PH_WAKE_EVENT, *PPH_WAKE_EVENT; - -#define PH_WAKE_EVENT_INIT PH_QUEUED_LOCK_INIT - -FORCEINLINE -VOID -PhInitializeWakeEvent( - _Out_ PPH_WAKE_EVENT WakeEvent - ) -{ - PhInitializeQueuedLock(WakeEvent); -} - -#define PhQueueWakeEvent PhfQueueWakeEvent -PHLIBAPI -VOID -FASTCALL -PhfQueueWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Out_ PPH_QUEUED_WAIT_BLOCK WaitBlock - ); - -PHLIBAPI -VOID -FASTCALL -PhfSetWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Inout_opt_ PPH_QUEUED_WAIT_BLOCK WaitBlock - ); - -FORCEINLINE -VOID -PhSetWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Inout_opt_ PPH_QUEUED_WAIT_BLOCK WaitBlock - ) -{ - // The wake event is similar to a synchronization event in that it does not have thread-safe - // pulsing; we can simply skip the function call if there's nothing to wake. However, if we're - // cancelling a wait (WaitBlock != NULL) we need to make the call. - - if (WakeEvent->Value || WaitBlock) - PhfSetWakeEvent(WakeEvent, WaitBlock); -} - -#define PhWaitForWakeEvent PhfWaitForWakeEvent -PHLIBAPI -NTSTATUS -FASTCALL -PhfWaitForWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock, - _In_ BOOLEAN Spin, - _In_opt_ PLARGE_INTEGER Timeout - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_QUEUEDLOCK_H +#define _PH_QUEUEDLOCK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_QUEUED_LOCK_OWNED ((ULONG_PTR)0x1) +#define PH_QUEUED_LOCK_OWNED_SHIFT 0 +#define PH_QUEUED_LOCK_WAITERS ((ULONG_PTR)0x2) + +// Valid only if Waiters = 0 +#define PH_QUEUED_LOCK_SHARED_INC ((ULONG_PTR)0x4) +#define PH_QUEUED_LOCK_SHARED_SHIFT 2 + +// Valid only if Waiters = 1 +#define PH_QUEUED_LOCK_TRAVERSING ((ULONG_PTR)0x4) +#define PH_QUEUED_LOCK_MULTIPLE_SHARED ((ULONG_PTR)0x8) + +#define PH_QUEUED_LOCK_FLAGS ((ULONG_PTR)0xf) + +#define PhGetQueuedLockSharedOwners(Value) \ + ((ULONG_PTR)(Value) >> PH_QUEUED_LOCK_SHARED_SHIFT) +#define PhGetQueuedLockWaitBlock(Value) \ + ((PPH_QUEUED_WAIT_BLOCK)((ULONG_PTR)(Value) & ~PH_QUEUED_LOCK_FLAGS)) + +typedef struct _PH_QUEUED_LOCK +{ + ULONG_PTR Value; +} PH_QUEUED_LOCK, *PPH_QUEUED_LOCK; + +#define PH_QUEUED_LOCK_INIT { 0 } + +#define PH_QUEUED_WAITER_EXCLUSIVE 0x1 +#define PH_QUEUED_WAITER_SPINNING 0x2 +#define PH_QUEUED_WAITER_SPINNING_SHIFT 1 + +typedef struct DECLSPEC_ALIGN(16) _PH_QUEUED_WAIT_BLOCK +{ + /** A pointer to the next wait block, i.e. the wait block pushed onto the list before this one. */ + struct _PH_QUEUED_WAIT_BLOCK *Next; + /** + * A pointer to the previous wait block, i.e. the wait block pushed onto the list after this + * one. + */ + struct _PH_QUEUED_WAIT_BLOCK *Previous; + /** A pointer to the last wait block, i.e. the first waiter pushed onto the list. */ + struct _PH_QUEUED_WAIT_BLOCK *Last; + + ULONG SharedOwners; + ULONG Flags; +} PH_QUEUED_WAIT_BLOCK, *PPH_QUEUED_WAIT_BLOCK; + +BOOLEAN PhQueuedLockInitialization( + VOID + ); + +// Queued lock + +FORCEINLINE +VOID +PhInitializeQueuedLock( + _Out_ PPH_QUEUED_LOCK QueuedLock + ) +{ + QueuedLock->Value = 0; +} + +PHLIBAPI +VOID +FASTCALL +PhfAcquireQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ); + +_Acquires_exclusive_lock_(*QueuedLock) +FORCEINLINE +VOID +PhAcquireQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + if (_InterlockedBitTestAndSetPointer((PLONG_PTR)&QueuedLock->Value, PH_QUEUED_LOCK_OWNED_SHIFT)) + { + // Owned bit was already set. Slow path. + PhfAcquireQueuedLockExclusive(QueuedLock); + } +} + +PHLIBAPI +VOID +FASTCALL +PhfAcquireQueuedLockShared( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ); + +_Acquires_shared_lock_(*QueuedLock) +FORCEINLINE +VOID +PhAcquireQueuedLockShared( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)(PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_SHARED_INC), + (PVOID)0 + ) != 0) + { + PhfAcquireQueuedLockShared(QueuedLock); + } +} + +_When_(return != 0, _Acquires_exclusive_lock_(*QueuedLock)) +FORCEINLINE +BOOLEAN +PhTryAcquireQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + if (!_InterlockedBitTestAndSetPointer((PLONG_PTR)&QueuedLock->Value, PH_QUEUED_LOCK_OWNED_SHIFT)) + { + return TRUE; + } + else + { + return FALSE; + } +} + +PHLIBAPI +VOID +FASTCALL +PhfReleaseQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ); + +PHLIBAPI +VOID +FASTCALL +PhfWakeForReleaseQueuedLock( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value + ); + +_Releases_exclusive_lock_(*QueuedLock) +FORCEINLINE +VOID +PhReleaseQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + ULONG_PTR value; + + value = (ULONG_PTR)_InterlockedExchangeAddPointer((PLONG_PTR)&QueuedLock->Value, -(LONG_PTR)PH_QUEUED_LOCK_OWNED); + + if ((value & (PH_QUEUED_LOCK_WAITERS | PH_QUEUED_LOCK_TRAVERSING)) == PH_QUEUED_LOCK_WAITERS) + { + PhfWakeForReleaseQueuedLock(QueuedLock, value - PH_QUEUED_LOCK_OWNED); + } +} + +PHLIBAPI +VOID +FASTCALL +PhfReleaseQueuedLockShared( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ); + +_Releases_shared_lock_(*QueuedLock) +FORCEINLINE +VOID +PhReleaseQueuedLockShared( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + ULONG_PTR value; + + value = PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_SHARED_INC; + + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)0, + (PVOID)value + ) != value) + { + PhfReleaseQueuedLockShared(QueuedLock); + } +} + +FORCEINLINE +VOID +PhAcquireReleaseQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + BOOLEAN owned; + + MemoryBarrier(); + owned = !!(QueuedLock->Value & PH_QUEUED_LOCK_OWNED); + MemoryBarrier(); + + if (owned) + { + PhAcquireQueuedLockExclusive(QueuedLock); + PhReleaseQueuedLockExclusive(QueuedLock); + } +} + +FORCEINLINE +BOOLEAN +PhTryAcquireReleaseQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + BOOLEAN owned; + + // Need two memory barriers because we don't want the compiler re-ordering the following check + // in either direction. + MemoryBarrier(); + owned = !(QueuedLock->Value & PH_QUEUED_LOCK_OWNED); + MemoryBarrier(); + + return owned; +} + +// Condition variable + +typedef struct _PH_QUEUED_LOCK PH_CONDITION, *PPH_CONDITION; + +#define PH_CONDITION_INIT PH_QUEUED_LOCK_INIT + +FORCEINLINE +VOID +PhInitializeCondition( + _Out_ PPH_CONDITION Condition + ) +{ + PhInitializeQueuedLock(Condition); +} + +#define PhPulseCondition PhfPulseCondition +PHLIBAPI +VOID +FASTCALL +PhfPulseCondition( + _Inout_ PPH_CONDITION Condition + ); + +#define PhPulseAllCondition PhfPulseAllCondition +PHLIBAPI +VOID +FASTCALL +PhfPulseAllCondition( + _Inout_ PPH_CONDITION Condition + ); + +#define PhWaitForCondition PhfWaitForCondition +PHLIBAPI +VOID +FASTCALL +PhfWaitForCondition( + _Inout_ PPH_CONDITION Condition, + _Inout_ PPH_QUEUED_LOCK Lock, + _In_opt_ PLARGE_INTEGER Timeout + ); + +#define PH_CONDITION_WAIT_QUEUED_LOCK 0x1 +#define PH_CONDITION_WAIT_CRITICAL_SECTION 0x2 +#define PH_CONDITION_WAIT_FAST_LOCK 0x4 +#define PH_CONDITION_WAIT_LOCK_TYPE_MASK 0xfff + +#define PH_CONDITION_WAIT_SHARED 0x1000 +#define PH_CONDITION_WAIT_SPIN 0x2000 + +#define PhWaitForConditionEx PhfWaitForConditionEx +PHLIBAPI +VOID +FASTCALL +PhfWaitForConditionEx( + _Inout_ PPH_CONDITION Condition, + _Inout_ PVOID Lock, + _In_ ULONG Flags, + _In_opt_ PLARGE_INTEGER Timeout + ); + +// Wake event + +typedef struct _PH_QUEUED_LOCK PH_WAKE_EVENT, *PPH_WAKE_EVENT; + +#define PH_WAKE_EVENT_INIT PH_QUEUED_LOCK_INIT + +FORCEINLINE +VOID +PhInitializeWakeEvent( + _Out_ PPH_WAKE_EVENT WakeEvent + ) +{ + PhInitializeQueuedLock(WakeEvent); +} + +#define PhQueueWakeEvent PhfQueueWakeEvent +PHLIBAPI +VOID +FASTCALL +PhfQueueWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Out_ PPH_QUEUED_WAIT_BLOCK WaitBlock + ); + +PHLIBAPI +VOID +FASTCALL +PhfSetWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Inout_opt_ PPH_QUEUED_WAIT_BLOCK WaitBlock + ); + +FORCEINLINE +VOID +PhSetWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Inout_opt_ PPH_QUEUED_WAIT_BLOCK WaitBlock + ) +{ + // The wake event is similar to a synchronization event in that it does not have thread-safe + // pulsing; we can simply skip the function call if there's nothing to wake. However, if we're + // cancelling a wait (WaitBlock != NULL) we need to make the call. + + if (WakeEvent->Value || WaitBlock) + PhfSetWakeEvent(WakeEvent, WaitBlock); +} + +#define PhWaitForWakeEvent PhfWaitForWakeEvent +PHLIBAPI +NTSTATUS +FASTCALL +PhfWaitForWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock, + _In_ BOOLEAN Spin, + _In_opt_ PLARGE_INTEGER Timeout + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/ref.h b/phlib/include/ref.h index 266b82cce516..24162dd978e3 100644 --- a/phlib/include/ref.h +++ b/phlib/include/ref.h @@ -1,309 +1,342 @@ -/* - * Process Hacker - - * internal object manager - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef _PH_REF_H -#define _PH_REF_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Configuration - -#define PH_OBJECT_SMALL_OBJECT_SIZE 48 -#define PH_OBJECT_SMALL_OBJECT_COUNT 512 - -// Object type flags -#define PH_OBJECT_TYPE_USE_FREE_LIST 0x00000001 -#define PH_OBJECT_TYPE_VALID_FLAGS 0x00000001 - -// Object type callbacks - -/** - * The delete procedure for an object type, called when an object of the type is being freed. - * - * \param Object A pointer to the object being freed. - * \param Flags Reserved. - */ -typedef VOID (NTAPI *PPH_TYPE_DELETE_PROCEDURE)( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -struct _PH_OBJECT_TYPE; -typedef struct _PH_OBJECT_TYPE *PPH_OBJECT_TYPE; - -struct _PH_QUEUED_LOCK; -typedef struct _PH_QUEUED_LOCK PH_QUEUED_LOCK, *PPH_QUEUED_LOCK; - -#ifdef DEBUG -typedef VOID (NTAPI *PPH_CREATE_OBJECT_HOOK)( - _In_ PVOID Object, - _In_ SIZE_T Size, - _In_ ULONG Flags, - _In_ PPH_OBJECT_TYPE ObjectType - ); -#endif - -typedef struct _PH_OBJECT_TYPE_PARAMETERS -{ - SIZE_T FreeListSize; - ULONG FreeListCount; -} PH_OBJECT_TYPE_PARAMETERS, *PPH_OBJECT_TYPE_PARAMETERS; - -typedef struct _PH_OBJECT_TYPE_INFORMATION -{ - PWSTR Name; - ULONG NumberOfObjects; - USHORT Flags; - UCHAR TypeIndex; - UCHAR Reserved; -} PH_OBJECT_TYPE_INFORMATION, *PPH_OBJECT_TYPE_INFORMATION; - -extern PPH_OBJECT_TYPE PhObjectTypeObject; -extern PPH_OBJECT_TYPE PhAllocType; - -#ifdef DEBUG -extern LIST_ENTRY PhDbgObjectListHead; -extern PH_QUEUED_LOCK PhDbgObjectListLock; -extern PPH_CREATE_OBJECT_HOOK PhDbgCreateObjectHook; -#endif - -NTSTATUS PhRefInitialization( - VOID - ); - -_May_raise_ -PHLIBAPI -PVOID -NTAPI -PhCreateObject( - _In_ SIZE_T ObjectSize, - _In_ PPH_OBJECT_TYPE ObjectType - ); - -PHLIBAPI -PVOID -NTAPI -PhReferenceObject( - _In_ PVOID Object - ); - -_May_raise_ -PHLIBAPI -PVOID -NTAPI -PhReferenceObjectEx( - _In_ PVOID Object, - _In_ LONG RefCount - ); - -PHLIBAPI -PVOID -NTAPI -PhReferenceObjectSafe( - _In_ PVOID Object - ); - -PHLIBAPI -VOID -NTAPI -PhDereferenceObject( - _In_ PVOID Object - ); - -PHLIBAPI -VOID -NTAPI -PhDereferenceObjectDeferDelete( - _In_ PVOID Object - ); - -_May_raise_ -PHLIBAPI -VOID -NTAPI -PhDereferenceObjectEx( - _In_ PVOID Object, - _In_ LONG RefCount, - _In_ BOOLEAN DeferDelete - ); - -PHLIBAPI -PPH_OBJECT_TYPE -NTAPI -PhGetObjectType( - _In_ PVOID Object - ); - -PHLIBAPI -PPH_OBJECT_TYPE -NTAPI -PhCreateObjectType( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure - ); - -PHLIBAPI -PPH_OBJECT_TYPE -NTAPI -PhCreateObjectTypeEx( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure, - _In_opt_ PPH_OBJECT_TYPE_PARAMETERS Parameters - ); - -PHLIBAPI -VOID -NTAPI -PhGetObjectTypeInformation( - _In_ PPH_OBJECT_TYPE ObjectType, - _Out_ PPH_OBJECT_TYPE_INFORMATION Information - ); - -PHLIBAPI -PVOID -NTAPI -PhCreateAlloc( - _In_ SIZE_T Size - ); - -// Object reference functions - -FORCEINLINE -VOID -PhSwapReference( - _Inout_ PVOID *ObjectReference, - _In_opt_ PVOID NewObject - ) -{ - PVOID oldObject; - - oldObject = *ObjectReference; - *ObjectReference = NewObject; - - if (NewObject) PhReferenceObject(NewObject); - if (oldObject) PhDereferenceObject(oldObject); -} - -FORCEINLINE -VOID -PhMoveReference( - _Inout_ PVOID *ObjectReference, - _In_opt_ _Assume_refs_(1) PVOID NewObject - ) -{ - PVOID oldObject; - - oldObject = *ObjectReference; - *ObjectReference = NewObject; - - if (oldObject) PhDereferenceObject(oldObject); -} - -FORCEINLINE -VOID -PhSetReference( - _Out_ PVOID *ObjectReference, - _In_opt_ PVOID NewObject - ) -{ - *ObjectReference = NewObject; - - if (NewObject) PhReferenceObject(NewObject); -} - -FORCEINLINE -VOID -PhClearReference( - _Inout_ PVOID *ObjectReference - ) -{ - PhMoveReference(ObjectReference, NULL); -} - -// Auto-dereference pool - -/** The size of the static array in an auto-release pool. */ -#define PH_AUTO_POOL_STATIC_SIZE 64 -/** The maximum size of the dynamic array for it to be kept after the auto-release pool is drained. */ -#define PH_AUTO_POOL_DYNAMIC_BIG_SIZE 256 - -/** - * An auto-dereference pool can be used for semi-automatic reference counting. Batches of objects - * are dereferenced at a certain time. - * - * This object is not thread-safe and cannot be used across thread boundaries. Always store them as - * local variables. - */ -typedef struct _PH_AUTO_POOL -{ - ULONG StaticCount; - PVOID StaticObjects[PH_AUTO_POOL_STATIC_SIZE]; - - ULONG DynamicCount; - ULONG DynamicAllocated; - PVOID *DynamicObjects; - - struct _PH_AUTO_POOL *NextPool; -} PH_AUTO_POOL, *PPH_AUTO_POOL; - -PHLIBAPI -VOID -NTAPI -PhInitializeAutoPool( - _Out_ PPH_AUTO_POOL AutoPool - ); - -_May_raise_ -PHLIBAPI -VOID -NTAPI -PhDeleteAutoPool( - _Inout_ PPH_AUTO_POOL AutoPool - ); - -PHLIBAPI -VOID -NTAPI -PhDrainAutoPool( - _In_ PPH_AUTO_POOL AutoPool - ); - -_May_raise_ -PHLIBAPI -PVOID -NTAPI -PhAutoDereferenceObject( - _In_opt_ PVOID Object - ); - -#define PH_AUTO PhAutoDereferenceObject -#define PH_AUTO_T(Type, Object) ((Type *)PH_AUTO(Object)) - -#ifdef __cplusplus -} -#endif - -#endif +/* + * Process Hacker - + * internal object manager + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PH_REF_H +#define _PH_REF_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Configuration + +#define PH_OBJECT_SMALL_OBJECT_SIZE 48 +#define PH_OBJECT_SMALL_OBJECT_COUNT 512 + +// Object type flags +#define PH_OBJECT_TYPE_USE_FREE_LIST 0x00000001 +#define PH_OBJECT_TYPE_VALID_FLAGS 0x00000001 + +// Object type callbacks + +/** + * The delete procedure for an object type, called when an object of the type is being freed. + * + * \param Object A pointer to the object being freed. + * \param Flags Reserved. + */ +typedef VOID (NTAPI *PPH_TYPE_DELETE_PROCEDURE)( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +struct _PH_OBJECT_TYPE; +typedef struct _PH_OBJECT_TYPE *PPH_OBJECT_TYPE; + +struct _PH_QUEUED_LOCK; +typedef struct _PH_QUEUED_LOCK PH_QUEUED_LOCK, *PPH_QUEUED_LOCK; + +#ifdef DEBUG +typedef VOID (NTAPI *PPH_CREATE_OBJECT_HOOK)( + _In_ PVOID Object, + _In_ SIZE_T Size, + _In_ ULONG Flags, + _In_ PPH_OBJECT_TYPE ObjectType + ); +#endif + +typedef struct _PH_OBJECT_TYPE_PARAMETERS +{ + SIZE_T FreeListSize; + ULONG FreeListCount; +} PH_OBJECT_TYPE_PARAMETERS, *PPH_OBJECT_TYPE_PARAMETERS; + +typedef struct _PH_OBJECT_TYPE_INFORMATION +{ + PWSTR Name; + ULONG NumberOfObjects; + USHORT Flags; + UCHAR TypeIndex; + UCHAR Reserved; +} PH_OBJECT_TYPE_INFORMATION, *PPH_OBJECT_TYPE_INFORMATION; + +extern PPH_OBJECT_TYPE PhObjectTypeObject; +extern PPH_OBJECT_TYPE PhAllocType; + +#ifdef DEBUG +extern LIST_ENTRY PhDbgObjectListHead; +extern PH_QUEUED_LOCK PhDbgObjectListLock; +extern PPH_CREATE_OBJECT_HOOK PhDbgCreateObjectHook; +#endif + +NTSTATUS PhRefInitialization( + VOID + ); + +_May_raise_ +PHLIBAPI +PVOID +NTAPI +PhCreateObject( + _In_ SIZE_T ObjectSize, + _In_ PPH_OBJECT_TYPE ObjectType + ); + +PHLIBAPI +PVOID +NTAPI +PhReferenceObject( + _In_ PVOID Object + ); + +_May_raise_ +PHLIBAPI +PVOID +NTAPI +PhReferenceObjectEx( + _In_ PVOID Object, + _In_ LONG RefCount + ); + +PHLIBAPI +PVOID +NTAPI +PhReferenceObjectSafe( + _In_ PVOID Object + ); + +PHLIBAPI +VOID +NTAPI +PhDereferenceObject( + _In_ PVOID Object + ); + +PHLIBAPI +VOID +NTAPI +PhDereferenceObjectDeferDelete( + _In_ PVOID Object + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhDereferenceObjectEx( + _In_ PVOID Object, + _In_ LONG RefCount, + _In_ BOOLEAN DeferDelete + ); + +PHLIBAPI +VOID +NTAPI +PhReferenceObjects( + _In_reads_(NumberOfObjects) PVOID *Objects, + _In_ ULONG NumberOfObjects + ); + +PHLIBAPI +VOID +NTAPI +PhDereferenceObjects( + _In_reads_(NumberOfObjects) PVOID *Objects, + _In_ ULONG NumberOfObjects + ); + +PHLIBAPI +PPH_OBJECT_TYPE +NTAPI +PhGetObjectType( + _In_ PVOID Object + ); + +PHLIBAPI +PPH_OBJECT_TYPE +NTAPI +PhCreateObjectType( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure + ); + +PHLIBAPI +PPH_OBJECT_TYPE +NTAPI +PhCreateObjectTypeEx( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure, + _In_opt_ PPH_OBJECT_TYPE_PARAMETERS Parameters + ); + +PHLIBAPI +VOID +NTAPI +PhGetObjectTypeInformation( + _In_ PPH_OBJECT_TYPE ObjectType, + _Out_ PPH_OBJECT_TYPE_INFORMATION Information + ); + +PHLIBAPI +PVOID +NTAPI +PhCreateAlloc( + _In_ SIZE_T Size + ); + +// Object reference functions + +FORCEINLINE +VOID +PhSwapReference( + _Inout_ PVOID *ObjectReference, + _In_opt_ PVOID NewObject + ) +{ + PVOID oldObject; + + oldObject = *ObjectReference; + *ObjectReference = NewObject; + + if (NewObject) PhReferenceObject(NewObject); + if (oldObject) PhDereferenceObject(oldObject); +} + +FORCEINLINE +VOID +PhMoveReference( + _Inout_ PVOID *ObjectReference, + _In_opt_ _Assume_refs_(1) PVOID NewObject + ) +{ + PVOID oldObject; + + oldObject = *ObjectReference; + *ObjectReference = NewObject; + + if (oldObject) PhDereferenceObject(oldObject); +} + +FORCEINLINE +VOID +PhSetReference( + _Out_ PVOID *ObjectReference, + _In_opt_ PVOID NewObject + ) +{ + *ObjectReference = NewObject; + + if (NewObject) PhReferenceObject(NewObject); +} + +FORCEINLINE +VOID +PhClearReference( + _Inout_ PVOID *ObjectReference + ) +{ + PhMoveReference(ObjectReference, NULL); +} + +// Convenience functions + +FORCEINLINE +PVOID +PhCreateObjectZero( + _In_ SIZE_T ObjectSize, + _In_ PPH_OBJECT_TYPE ObjectType + ) +{ + PVOID object; + + object = PhCreateObject(ObjectSize, ObjectType); + memset(object, 0, ObjectSize); + + return object; +} + +// Auto-dereference pool + +/** The size of the static array in an auto-release pool. */ +#define PH_AUTO_POOL_STATIC_SIZE 64 +/** The maximum size of the dynamic array for it to be kept after the auto-release pool is drained. */ +#define PH_AUTO_POOL_DYNAMIC_BIG_SIZE 256 + +/** + * An auto-dereference pool can be used for semi-automatic reference counting. Batches of objects + * are dereferenced at a certain time. + * + * This object is not thread-safe and cannot be used across thread boundaries. Always store them as + * local variables. + */ +typedef struct _PH_AUTO_POOL +{ + ULONG StaticCount; + PVOID StaticObjects[PH_AUTO_POOL_STATIC_SIZE]; + + ULONG DynamicCount; + ULONG DynamicAllocated; + PVOID *DynamicObjects; + + struct _PH_AUTO_POOL *NextPool; +} PH_AUTO_POOL, *PPH_AUTO_POOL; + +PHLIBAPI +VOID +NTAPI +PhInitializeAutoPool( + _Out_ PPH_AUTO_POOL AutoPool + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhDeleteAutoPool( + _Inout_ PPH_AUTO_POOL AutoPool + ); + +PHLIBAPI +VOID +NTAPI +PhDrainAutoPool( + _In_ PPH_AUTO_POOL AutoPool + ); + +_May_raise_ +PHLIBAPI +PVOID +NTAPI +PhAutoDereferenceObject( + _In_opt_ PVOID Object + ); + +#define PH_AUTO PhAutoDereferenceObject +#define PH_AUTO_T(Type, Object) ((Type *)PH_AUTO(Object)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/refp.h b/phlib/include/refp.h index f1059d9e4d36..71becfa13964 100644 --- a/phlib/include/refp.h +++ b/phlib/include/refp.h @@ -1,175 +1,175 @@ -/* - * Process Hacker - - * internal object manager - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef _PH_REFP_H -#define _PH_REFP_H - -#define PH_OBJECT_TYPE_TABLE_SIZE 256 - -/** The object was allocated from the small free list. */ -#define PH_OBJECT_FROM_SMALL_FREE_LIST 0x1 -/** The object was allocated from the type free list. */ -#define PH_OBJECT_FROM_TYPE_FREE_LIST 0x2 - -/** - * The object header contains object manager information including the reference count of an object - * and its type. - */ -typedef struct _PH_OBJECT_HEADER -{ - union - { - struct - { - USHORT TypeIndex; - UCHAR Flags; - UCHAR Reserved1; -#ifdef _WIN64 - ULONG Reserved2; -#endif - union - { - LONG RefCount; - struct - { - LONG SavedTypeIndex : 16; - LONG SavedFlags : 8; - LONG Reserved : 7; - LONG DeferDelete : 1; // MUST be the high bit, so that RefCount < 0 when deferring delete - }; - }; -#ifdef _WIN64 - ULONG Reserved3; -#endif - }; - SLIST_ENTRY DeferDeleteListEntry; - }; - -#ifdef DEBUG - PVOID StackBackTrace[16]; - LIST_ENTRY ObjectListEntry; -#endif - - /** - * The body of the object. For use by the \ref PhObjectToObjectHeader and - * \ref PhObjectHeaderToObject macros. - */ - QUAD_PTR Body; -} PH_OBJECT_HEADER, *PPH_OBJECT_HEADER; - -#ifndef DEBUG -#ifdef _WIN64 -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, TypeIndex) == 0x0); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Flags) == 0x2); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved1) == 0x3); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved2) == 0x4); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, RefCount) == 0x8); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved3) == 0xc); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, DeferDeleteListEntry) == 0x0); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Body) == 0x10); -#else -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, TypeIndex) == 0x0); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Flags) == 0x2); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved1) == 0x3); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, RefCount) == 0x4); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, DeferDeleteListEntry) == 0x0); -C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Body) == 0x8); -#endif -#endif - -/** - * Gets a pointer to the object header for an object. - * - * \param Object A pointer to an object. - * - * \return A pointer to the object header of the object. - */ -#define PhObjectToObjectHeader(Object) ((PPH_OBJECT_HEADER)CONTAINING_RECORD((PCHAR)(Object), PH_OBJECT_HEADER, Body)) - -/** - * Gets a pointer to an object from an object header. - * - * \param ObjectHeader A pointer to an object header. - * - * \return A pointer to an object. - */ -#define PhObjectHeaderToObject(ObjectHeader) ((PVOID)&((PPH_OBJECT_HEADER)(ObjectHeader))->Body) - -/** - * Calculates the total size to allocate for an object. - * - * \param Size The size of the object to allocate. - * - * \return The new size, including space for the object header. - */ -#define PhAddObjectHeaderSize(Size) ((Size) + FIELD_OFFSET(PH_OBJECT_HEADER, Body)) - -/** An object type specifies a kind of object and its delete procedure. */ -typedef struct _PH_OBJECT_TYPE -{ - /** The flags that were used to create the object type. */ - USHORT Flags; - UCHAR TypeIndex; - UCHAR Reserved; - /** The total number of objects of this type that are alive. */ - ULONG NumberOfObjects; - /** An optional procedure called when objects of this type are freed. */ - PPH_TYPE_DELETE_PROCEDURE DeleteProcedure; - /** The name of the type. */ - PWSTR Name; - /** A free list to use when allocating for this type. */ - PH_FREE_LIST FreeList; -} PH_OBJECT_TYPE, *PPH_OBJECT_TYPE; - -/** - * Increments a reference count, but will never increment from a nonpositive value to 1. - * - * \param RefCount A pointer to a reference count. - */ -FORCEINLINE -BOOLEAN -PhpInterlockedIncrementSafe( - _Inout_ PLONG RefCount - ) -{ - /* Here we will attempt to increment the reference count, making sure that it is positive. */ - return _InterlockedIncrementPositive(RefCount); -} - -PPH_OBJECT_HEADER PhpAllocateObject( - _In_ PPH_OBJECT_TYPE ObjectType, - _In_ SIZE_T ObjectSize - ); - -VOID PhpFreeObject( - _In_ PPH_OBJECT_HEADER ObjectHeader - ); - -VOID PhpDeferDeleteObject( - _In_ PPH_OBJECT_HEADER ObjectHeader - ); - -NTSTATUS PhpDeferDeleteObjectRoutine( - _In_ PVOID Parameter - ); - -#endif +/* + * Process Hacker - + * internal object manager + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PH_REFP_H +#define _PH_REFP_H + +#define PH_OBJECT_TYPE_TABLE_SIZE 256 + +/** The object was allocated from the small free list. */ +#define PH_OBJECT_FROM_SMALL_FREE_LIST 0x1 +/** The object was allocated from the type free list. */ +#define PH_OBJECT_FROM_TYPE_FREE_LIST 0x2 + +/** + * The object header contains object manager information including the reference count of an object + * and its type. + */ +typedef struct _PH_OBJECT_HEADER +{ + union + { + struct + { + USHORT TypeIndex; + UCHAR Flags; + UCHAR Reserved1; +#ifdef _WIN64 + ULONG Reserved2; +#endif + union + { + LONG RefCount; + struct + { + LONG SavedTypeIndex : 16; + LONG SavedFlags : 8; + LONG Reserved : 7; + LONG DeferDelete : 1; // MUST be the high bit, so that RefCount < 0 when deferring delete + }; + }; +#ifdef _WIN64 + ULONG Reserved3; +#endif + }; + SLIST_ENTRY DeferDeleteListEntry; + }; + +#ifdef DEBUG + PVOID StackBackTrace[16]; + LIST_ENTRY ObjectListEntry; +#endif + + /** + * The body of the object. For use by the \ref PhObjectToObjectHeader and + * \ref PhObjectHeaderToObject macros. + */ + QUAD_PTR Body; +} PH_OBJECT_HEADER, *PPH_OBJECT_HEADER; + +#ifndef DEBUG +#ifdef _WIN64 +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, TypeIndex) == 0x0); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Flags) == 0x2); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved1) == 0x3); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved2) == 0x4); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, RefCount) == 0x8); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved3) == 0xc); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, DeferDeleteListEntry) == 0x0); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Body) == 0x10); +#else +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, TypeIndex) == 0x0); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Flags) == 0x2); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Reserved1) == 0x3); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, RefCount) == 0x4); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, DeferDeleteListEntry) == 0x0); +C_ASSERT(FIELD_OFFSET(PH_OBJECT_HEADER, Body) == 0x8); +#endif +#endif + +/** + * Gets a pointer to the object header for an object. + * + * \param Object A pointer to an object. + * + * \return A pointer to the object header of the object. + */ +#define PhObjectToObjectHeader(Object) ((PPH_OBJECT_HEADER)CONTAINING_RECORD((PCHAR)(Object), PH_OBJECT_HEADER, Body)) + +/** + * Gets a pointer to an object from an object header. + * + * \param ObjectHeader A pointer to an object header. + * + * \return A pointer to an object. + */ +#define PhObjectHeaderToObject(ObjectHeader) ((PVOID)&((PPH_OBJECT_HEADER)(ObjectHeader))->Body) + +/** + * Calculates the total size to allocate for an object. + * + * \param Size The size of the object to allocate. + * + * \return The new size, including space for the object header. + */ +#define PhAddObjectHeaderSize(Size) ((Size) + UFIELD_OFFSET(PH_OBJECT_HEADER, Body)) + +/** An object type specifies a kind of object and its delete procedure. */ +typedef struct _PH_OBJECT_TYPE +{ + /** The flags that were used to create the object type. */ + USHORT Flags; + UCHAR TypeIndex; + UCHAR Reserved; + /** The total number of objects of this type that are alive. */ + ULONG NumberOfObjects; + /** An optional procedure called when objects of this type are freed. */ + PPH_TYPE_DELETE_PROCEDURE DeleteProcedure; + /** The name of the type. */ + PWSTR Name; + /** A free list to use when allocating for this type. */ + PH_FREE_LIST FreeList; +} PH_OBJECT_TYPE, *PPH_OBJECT_TYPE; + +/** + * Increments a reference count, but will never increment from a nonpositive value to 1. + * + * \param RefCount A pointer to a reference count. + */ +FORCEINLINE +BOOLEAN +PhpInterlockedIncrementSafe( + _Inout_ PLONG RefCount + ) +{ + /* Here we will attempt to increment the reference count, making sure that it is positive. */ + return _InterlockedIncrementPositive(RefCount); +} + +PPH_OBJECT_HEADER PhpAllocateObject( + _In_ PPH_OBJECT_TYPE ObjectType, + _In_ SIZE_T ObjectSize + ); + +VOID PhpFreeObject( + _In_ PPH_OBJECT_HEADER ObjectHeader + ); + +VOID PhpDeferDeleteObject( + _In_ PPH_OBJECT_HEADER ObjectHeader + ); + +NTSTATUS PhpDeferDeleteObjectRoutine( + _In_ PVOID Parameter + ); + +#endif diff --git a/phlib/include/secedit.h b/phlib/include/secedit.h index 18bf0322632d..8f34cf621878 100644 --- a/phlib/include/secedit.h +++ b/phlib/include/secedit.h @@ -1,165 +1,186 @@ -#ifndef _PH_SECEDIT_H -#define _PH_SECEDIT_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _PSP *HPROPSHEETPAGE; - -// secedit - -typedef struct _PH_ACCESS_ENTRY -{ - PWSTR Name; - ACCESS_MASK Access; - BOOLEAN General; - BOOLEAN Specific; - PWSTR ShortName; -} PH_ACCESS_ENTRY, *PPH_ACCESS_ENTRY; - -PHLIBAPI -HPROPSHEETPAGE -NTAPI -PhCreateSecurityPage( - _In_ PWSTR ObjectName, - _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, - _In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity, - _In_opt_ PVOID Context, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries - ); - -PHLIBAPI -VOID -NTAPI -PhEditSecurity( - _In_ HWND hWnd, - _In_ PWSTR ObjectName, - _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, - _In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity, - _In_opt_ PVOID Context, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries - ); - -typedef struct _PH_STD_OBJECT_SECURITY -{ - PPH_OPEN_OBJECT OpenObject; - PWSTR ObjectType; - PVOID Context; -} PH_STD_OBJECT_SECURITY, *PPH_STD_OBJECT_SECURITY; - -FORCEINLINE ACCESS_MASK PhGetAccessForGetSecurity( - _In_ SECURITY_INFORMATION SecurityInformation - ) -{ - ACCESS_MASK access = 0; - - if ( - (SecurityInformation & OWNER_SECURITY_INFORMATION) || - (SecurityInformation & GROUP_SECURITY_INFORMATION) || - (SecurityInformation & DACL_SECURITY_INFORMATION) - ) - { - access |= READ_CONTROL; - } - - if (SecurityInformation & SACL_SECURITY_INFORMATION) - { - access |= ACCESS_SYSTEM_SECURITY; - } - - return access; -} - -FORCEINLINE ACCESS_MASK PhGetAccessForSetSecurity( - _In_ SECURITY_INFORMATION SecurityInformation - ) -{ - ACCESS_MASK access = 0; - - if ( - (SecurityInformation & OWNER_SECURITY_INFORMATION) || - (SecurityInformation & GROUP_SECURITY_INFORMATION) - ) - { - access |= WRITE_OWNER; - } - - if (SecurityInformation & DACL_SECURITY_INFORMATION) - { - access |= WRITE_DAC; - } - - if (SecurityInformation & SACL_SECURITY_INFORMATION) - { - access |= ACCESS_SYSTEM_SECURITY; - } - - return access; -} - -PHLIBAPI -_Callback_ NTSTATUS -NTAPI -PhStdGetObjectSecurity( - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_opt_ PVOID Context - ); - -PHLIBAPI -_Callback_ NTSTATUS -NTAPI -PhStdSetObjectSecurity( - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_opt_ PVOID Context - ); - -PHLIBAPI -NTSTATUS -NTAPI -PhGetSeObjectSecurity( - _In_ HANDLE Handle, - _In_ ULONG ObjectType, - _In_ SECURITY_INFORMATION SecurityInformation, - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor - ); - -PHLIBAPI -NTSTATUS -NTAPI -PhSetSeObjectSecurity( - _In_ HANDLE Handle, - _In_ ULONG ObjectType, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ); - -// secdata - -PHLIBAPI -BOOLEAN -NTAPI -PhGetAccessEntries( - _In_ PWSTR Type, - _Out_ PPH_ACCESS_ENTRY *AccessEntries, - _Out_ PULONG NumberOfAccessEntries - ); - -PHLIBAPI -PPH_STRING -NTAPI -PhGetAccessString( - _In_ ACCESS_MASK Access, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries - ); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file +#ifndef _PH_SECEDIT_H +#define _PH_SECEDIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _PSP *HPROPSHEETPAGE; + +// secedit + +typedef struct _PH_ACCESS_ENTRY +{ + PWSTR Name; + ACCESS_MASK Access; + BOOLEAN General; + BOOLEAN Specific; + PWSTR ShortName; +} PH_ACCESS_ENTRY, *PPH_ACCESS_ENTRY; + +PHLIBAPI +HPROPSHEETPAGE +NTAPI +PhCreateSecurityPage( + _In_ PWSTR ObjectName, + _In_ PWSTR ObjectType, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PPH_CLOSE_OBJECT CloseObject, + _In_opt_ PVOID Context + ); + +PHLIBAPI +VOID +NTAPI +PhEditSecurity( + _In_opt_ HWND WindowHandle, + _In_ PWSTR ObjectName, + _In_ PWSTR ObjectType, + _In_ PPH_OPEN_OBJECT OpenCallback, + _In_opt_ PPH_CLOSE_OBJECT CloseCallback, + _In_opt_ PVOID Context + ); + +typedef struct _PH_STD_OBJECT_SECURITY +{ + PWSTR ObjectType; + PVOID Context; +} PH_STD_OBJECT_SECURITY, *PPH_STD_OBJECT_SECURITY; + +FORCEINLINE ACCESS_MASK PhGetAccessForGetSecurity( + _In_ SECURITY_INFORMATION SecurityInformation + ) +{ + ACCESS_MASK access = 0; + + if ( + (SecurityInformation & OWNER_SECURITY_INFORMATION) || + (SecurityInformation & GROUP_SECURITY_INFORMATION) || + (SecurityInformation & DACL_SECURITY_INFORMATION) || + (SecurityInformation & LABEL_SECURITY_INFORMATION) || + (SecurityInformation & ATTRIBUTE_SECURITY_INFORMATION) || + (SecurityInformation & SCOPE_SECURITY_INFORMATION) + ) + { + access |= READ_CONTROL; + } + + if (SecurityInformation & SACL_SECURITY_INFORMATION) + { + access |= ACCESS_SYSTEM_SECURITY; + } + + if (SecurityInformation & BACKUP_SECURITY_INFORMATION) + { + access |= READ_CONTROL | ACCESS_SYSTEM_SECURITY; + } + + return access; +} + +FORCEINLINE ACCESS_MASK PhGetAccessForSetSecurity( + _In_ SECURITY_INFORMATION SecurityInformation + ) +{ + ACCESS_MASK access = 0; + + if ( + (SecurityInformation & OWNER_SECURITY_INFORMATION) || + (SecurityInformation & GROUP_SECURITY_INFORMATION) || + (SecurityInformation & LABEL_SECURITY_INFORMATION) + ) + { + access |= WRITE_OWNER; + } + + if ( + (SecurityInformation & DACL_SECURITY_INFORMATION) || + (SecurityInformation & ATTRIBUTE_SECURITY_INFORMATION) || + (SecurityInformation & PROTECTED_DACL_SECURITY_INFORMATION) || + (SecurityInformation & UNPROTECTED_DACL_SECURITY_INFORMATION) + ) + { + access |= WRITE_DAC; + } + + if ( + (SecurityInformation & SACL_SECURITY_INFORMATION) || + (SecurityInformation & SCOPE_SECURITY_INFORMATION) || + (SecurityInformation & PROTECTED_SACL_SECURITY_INFORMATION) || + (SecurityInformation & UNPROTECTED_SACL_SECURITY_INFORMATION) + ) + { + access |= ACCESS_SYSTEM_SECURITY; + } + + if (SecurityInformation & BACKUP_SECURITY_INFORMATION) + { + access |= WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY; + } + + return access; +} + +PHLIBAPI +_Callback_ NTSTATUS +NTAPI +PhStdGetObjectSecurity( + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_opt_ PVOID Context + ); + +PHLIBAPI +_Callback_ NTSTATUS +NTAPI +PhStdSetObjectSecurity( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_opt_ PVOID Context + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhGetSeObjectSecurity( + _In_ HANDLE Handle, + _In_ ULONG ObjectType, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhSetSeObjectSecurity( + _In_ HANDLE Handle, + _In_ ULONG ObjectType, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +// secdata + +PHLIBAPI +BOOLEAN +NTAPI +PhGetAccessEntries( + _In_ PWSTR Type, + _Out_ PPH_ACCESS_ENTRY *AccessEntries, + _Out_ PULONG NumberOfAccessEntries + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetAccessString( + _In_ ACCESS_MASK Access, + _In_ PPH_ACCESS_ENTRY AccessEntries, + _In_ ULONG NumberOfAccessEntries + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/seceditp.h b/phlib/include/seceditp.h index e2773866fc1c..a08e30720873 100644 --- a/phlib/include/seceditp.h +++ b/phlib/include/seceditp.h @@ -1,93 +1,298 @@ -#ifndef _PH_SECEDITP_H -#define _PH_SECEDITP_H - -#include -#include - -typedef struct -{ - ISecurityInformationVtbl *VTable; - - ULONG RefCount; - - PPH_STRING ObjectName; - PPH_GET_OBJECT_SECURITY GetObjectSecurity; - PPH_SET_OBJECT_SECURITY SetObjectSecurity; - PVOID Context; - PSI_ACCESS AccessEntries; - ULONG NumberOfAccessEntries; - BOOLEAN IsPage; -} PhSecurityInformation; - -ISecurityInformation *PhSecurityInformation_Create( - _In_ PWSTR ObjectName, - _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, - _In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity, - _In_opt_ PVOID Context, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries, - _In_ BOOLEAN IsPage - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( - _In_ ISecurityInformation *This, - _In_ REFIID Riid, - _Out_ PVOID *Object - ); - -ULONG STDMETHODCALLTYPE PhSecurityInformation_AddRef( - _In_ ISecurityInformation *This - ); - -ULONG STDMETHODCALLTYPE PhSecurityInformation_Release( - _In_ ISecurityInformation *This - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetObjectInformation( - _In_ ISecurityInformation *This, - _Out_ PSI_OBJECT_INFO ObjectInfo - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetSecurity( - _In_ ISecurityInformation *This, - _In_ SECURITY_INFORMATION RequestedInformation, - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, - _In_ BOOL Default - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_SetSecurity( - _In_ ISecurityInformation *This, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetAccessRights( - _In_ ISecurityInformation *This, - _In_ const GUID *ObjectType, - _In_ ULONG Flags, - _Out_ PSI_ACCESS *Access, - _Out_ PULONG Accesses, - _Out_ PULONG DefaultAccess - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_MapGeneric( - _In_ ISecurityInformation *This, - _In_ const GUID *ObjectType, - _In_ PUCHAR AceFlags, - _Inout_ PACCESS_MASK Mask - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetInheritTypes( - _In_ ISecurityInformation *This, - _Out_ PSI_INHERIT_TYPE *InheritTypes, - _Out_ PULONG InheritTypesCount - ); - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback( - _In_ ISecurityInformation *This, - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ SI_PAGE_TYPE uPage - ); - -#endif +#ifndef _PH_SECEDITP_H +#define _PH_SECEDITP_H + +#include +#include + +typedef struct +{ + ISecurityInformationVtbl *VTable; + + ULONG RefCount; + + HWND WindowHandle; + BOOLEAN IsPage; + PPH_ACCESS_ENTRY AccessEntriesArray; + PSI_ACCESS AccessEntries; + ULONG NumberOfAccessEntries; + + PPH_STRING ObjectName; + PPH_STRING ObjectType; + PPH_OPEN_OBJECT OpenObject; + PPH_CLOSE_OBJECT CloseObject; + PVOID Context; +} PhSecurityInformation; + +typedef struct +{ + ISecurityInformation2Vtbl *VTable; + + PhSecurityInformation *Context; + ULONG RefCount; +} PhSecurityInformation2; + +typedef struct +{ + ISecurityInformation3Vtbl *VTable; + + PhSecurityInformation *Context; + ULONG RefCount; +} PhSecurityInformation3; + +typedef struct +{ + IDataObjectVtbl *VTable; + + PhSecurityInformation *Context; + ULONG RefCount; + ULONG SidCount; + PSID *Sids; + PPH_LIST NameCache; +} PhSecurityIDataObject; + + +#undef INTERFACE +#define INTERFACE ISecurityObjectTypeInfoEx +DECLARE_INTERFACE_IID_(ISecurityObjectTypeInfoEx, IUnknown, "FC3066EB-79EF-444b-9111-D18A75EBF2FA") +{ + // *** IUnknown methods *** + STDMETHOD(QueryInterface) (THIS_ _In_ REFIID riid, _Outptr_ void** ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + + // *** ISecurityInformation methods *** + STDMETHOD(GetInheritSource)(THIS_ SECURITY_INFORMATION si, + PACL pACL, + PINHERITED_FROM * ppInheritArray) PURE; +}; +typedef ISecurityObjectTypeInfoEx* LPSecurityObjectTypeInfoEx; + +typedef struct +{ + ISecurityObjectTypeInfoExVtbl* VTable; + + PhSecurityInformation* Context; + ULONG RefCount; +} PhSecurityObjectTypeInfo; + +// ISecurityInformation + +ISecurityInformation *PhSecurityInformation_Create( + _In_opt_ HWND WindowHandle, + _In_ PWSTR ObjectName, + _In_ PWSTR ObjectType, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PPH_CLOSE_OBJECT CloseObject, + _In_opt_ PVOID Context, + _In_ BOOLEAN IsPage + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( + _In_ ISecurityInformation *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ); + +ULONG STDMETHODCALLTYPE PhSecurityInformation_AddRef( + _In_ ISecurityInformation *This + ); + +ULONG STDMETHODCALLTYPE PhSecurityInformation_Release( + _In_ ISecurityInformation *This + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetObjectInformation( + _In_ ISecurityInformation *This, + _Out_ PSI_OBJECT_INFO ObjectInfo + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetSecurity( + _In_ ISecurityInformation *This, + _In_ SECURITY_INFORMATION RequestedInformation, + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, + _In_ BOOL Default + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_SetSecurity( + _In_ ISecurityInformation *This, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetAccessRights( + _In_ ISecurityInformation *This, + _In_ const GUID *ObjectType, + _In_ ULONG Flags, + _Out_ PSI_ACCESS *Access, + _Out_ PULONG Accesses, + _Out_ PULONG DefaultAccess + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_MapGeneric( + _In_ ISecurityInformation *This, + _In_ const GUID *ObjectType, + _In_ PUCHAR AceFlags, + _Inout_ PACCESS_MASK Mask + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetInheritTypes( + _In_ ISecurityInformation *This, + _Out_ PSI_INHERIT_TYPE *InheritTypes, + _Out_ PULONG InheritTypesCount + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback( + _In_ ISecurityInformation *This, + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ SI_PAGE_TYPE uPage + ); + +// ISecurityInformation2 + +HRESULT STDMETHODCALLTYPE PhSecurityInformation2_QueryInterface( + _In_ ISecurityInformation2 *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ); + +ULONG STDMETHODCALLTYPE PhSecurityInformation2_AddRef( + _In_ ISecurityInformation2 *This + ); + +ULONG STDMETHODCALLTYPE PhSecurityInformation2_Release( + _In_ ISecurityInformation2 *This + ); + +BOOL STDMETHODCALLTYPE PhSecurityInformation2_IsDaclCanonical( + _In_ ISecurityInformation2 *This, + _In_ PACL pDacl + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation2_LookupSids( + _In_ ISecurityInformation2 *This, + _In_ ULONG cSids, + _In_ PSID *rgpSids, + _Out_ LPDATAOBJECT *ppdo + ); + +// ISecurityInformation3 + +HRESULT STDMETHODCALLTYPE PhSecurityInformation3_QueryInterface( + _In_ ISecurityInformation3 *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ); + +ULONG STDMETHODCALLTYPE PhSecurityInformation3_AddRef( + _In_ ISecurityInformation3 *This + ); + +ULONG STDMETHODCALLTYPE PhSecurityInformation3_Release( + _In_ ISecurityInformation3 *This + ); + +BOOL STDMETHODCALLTYPE PhSecurityInformation3_GetFullResourceName( + _In_ ISecurityInformation3 *This, + _Outptr_ PWSTR *ppszResourceName + ); + +HRESULT STDMETHODCALLTYPE PhSecurityInformation3_OpenElevatedEditor( + _In_ ISecurityInformation3 *This, + _In_ HWND hWnd, + _In_ SI_PAGE_TYPE uPage + ); + +// IDataObject + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_QueryInterface( + _In_ IDataObject *This, + _In_ REFIID Riid, + _COM_Outptr_ PVOID *Object + ); + +ULONG STDMETHODCALLTYPE PhSecurityDataObject_AddRef( + _In_ IDataObject *This + ); + +ULONG STDMETHODCALLTYPE PhSecurityDataObject_Release( + _In_ IDataObject *This + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetData( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetcIn, + _Out_ STGMEDIUM *pmedium); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetDataHere( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetc, + _Inout_ STGMEDIUM *pmedium + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_QueryGetData( + _In_ IDataObject *This, + _In_opt_ FORMATETC *pformatetc + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetCanonicalFormatEtc( + _In_ IDataObject *This, + _In_opt_ FORMATETC *pformatectIn, + _Out_ FORMATETC *pformatetcOut + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_SetData( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetc, + _In_ STGMEDIUM *pmedium, + _In_ BOOL fRelease + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_EnumFormatEtc( + _In_ IDataObject *This, + _In_ ULONG dwDirection, + _Out_opt_ IEnumFORMATETC **ppenumFormatEtc + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_DAdvise( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetc, + _In_ ULONG advf, + _In_opt_ IAdviseSink *pAdvSink, + _Out_ ULONG *pdwConnection + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_DUnadvise( + _In_ IDataObject *This, + _In_ ULONG dwConnection + ); + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_EnumDAdvise( + _In_ IDataObject *This, + _Out_opt_ IEnumSTATDATA **ppenumAdvise + ); + +// ISecurityObjectTypeInfo + +HRESULT STDMETHODCALLTYPE PhSecurityObjectTypeInfo_QueryInterface( + _In_ ISecurityObjectTypeInfoEx* This, + _In_ REFIID Riid, + _Out_ PVOID* Object + ); + +ULONG STDMETHODCALLTYPE PhSecurityObjectTypeInfo_AddRef( + _In_ ISecurityObjectTypeInfoEx* This + ); + +ULONG STDMETHODCALLTYPE PhSecurityObjectTypeInfo_Release( + _In_ ISecurityObjectTypeInfoEx* This + ); + +HRESULT STDMETHODCALLTYPE PhSecurityObjectTypeInfo_GetInheritSource( + _In_ ISecurityObjectTypeInfoEx* This, + _In_ SECURITY_INFORMATION SecurityInfo, + _In_ PACL Acl, + _Out_ PINHERITED_FROM *InheritArray + ); + +#endif diff --git a/phlib/include/settings.h b/phlib/include/settings.h new file mode 100644 index 000000000000..a98f2cc845d9 --- /dev/null +++ b/phlib/include/settings.h @@ -0,0 +1,302 @@ +#ifndef PHLIB_SETTINGS_H +#define PHLIB_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// begin_phapppub + +// These macros make sure the C strings can be seamlessly converted into +// PH_STRINGREFs at compile time, for a small speed boost. + +#define ADD_SETTING_WRAPPER(Type, Name, DefaultValue) \ +{ \ + static PH_STRINGREF name = PH_STRINGREF_INIT(Name); \ + static PH_STRINGREF defaultValue = PH_STRINGREF_INIT(DefaultValue); \ + PhAddSetting(Type, &name, &defaultValue); \ +} + +#define PhpAddStringSetting(A, B) ADD_SETTING_WRAPPER(StringSettingType, A, B) +#define PhpAddIntegerSetting(A, B) ADD_SETTING_WRAPPER(IntegerSettingType, A, B) +#define PhpAddIntegerPairSetting(A, B) ADD_SETTING_WRAPPER(IntegerPairSettingType, A, B) +#define PhpAddScalableIntegerPairSetting(A, B) ADD_SETTING_WRAPPER(ScalableIntegerPairSettingType, A, B) + +typedef enum _PH_SETTING_TYPE +{ + StringSettingType, + IntegerSettingType, + IntegerPairSettingType, + ScalableIntegerPairSettingType +} PH_SETTING_TYPE, PPH_SETTING_TYPE; +// end_phapppub + +typedef struct _PH_SETTING +{ + PH_SETTING_TYPE Type; + PH_STRINGREF Name; + PH_STRINGREF DefaultValue; + + union + { + PVOID Pointer; + ULONG Integer; + PH_INTEGER_PAIR IntegerPair; + } u; +} PH_SETTING, *PPH_SETTING; + +PHLIBAPI +VOID +PhSettingsInitialization( + VOID + ); + +// Note: Program specific function. +VOID PhAddDefaultSettings( + VOID + ); + +// Note: Program specific function. +VOID PhUpdateCachedSettings( + VOID + ); + +// private + +PPH_STRING PhSettingToString( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_SETTING Setting + ); + +BOOLEAN PhSettingFromString( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_STRINGREF StringRef, + _In_opt_ PPH_STRING String, + _Inout_ PPH_SETTING Setting + ); + +typedef BOOLEAN (NTAPI *PPH_SETTINGS_ENUM_CALLBACK)( + _In_ PPH_SETTING Setting, + _In_ PVOID Context + ); + +VOID PhEnumSettings( + _In_ PPH_SETTINGS_ENUM_CALLBACK Callback, + _In_ PVOID Context + ); + +// begin_phapppub +_May_raise_ +PHLIBAPI +ULONG +NTAPI +PhGetIntegerSetting( + _In_ PWSTR Name + ); + +_May_raise_ +PHLIBAPI +PH_INTEGER_PAIR +NTAPI +PhGetIntegerPairSetting( + _In_ PWSTR Name + ); + +_May_raise_ +PHLIBAPI +PH_SCALABLE_INTEGER_PAIR +NTAPI +PhGetScalableIntegerPairSetting( + _In_ PWSTR Name, + _In_ BOOLEAN ScaleToCurrent + ); + +_May_raise_ +PHLIBAPI +PPH_STRING +NTAPI +PhGetStringSetting( + _In_ PWSTR Name + ); + +FORCEINLINE +PPH_STRING +PhGetExpandStringSetting( + _In_ PWSTR Name + ) +{ + PPH_STRING setting; + + setting = PhGetStringSetting(Name); + PhMoveReference(&setting, PhExpandEnvironmentStrings(&setting->sr)); + + return setting; +} + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhSetIntegerSetting( + _In_ PWSTR Name, + _In_ ULONG Value + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhSetIntegerPairSetting( + _In_ PWSTR Name, + _In_ PH_INTEGER_PAIR Value + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhSetScalableIntegerPairSetting( + _In_ PWSTR Name, + _In_ PH_SCALABLE_INTEGER_PAIR Value + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhSetScalableIntegerPairSetting2( + _In_ PWSTR Name, + _In_ PH_INTEGER_PAIR Value + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhSetStringSetting( + _In_ PWSTR Name, + _In_ PWSTR Value + ); + +_May_raise_ +PHLIBAPI +VOID +NTAPI +PhSetStringSetting2( + _In_ PWSTR Name, + _In_ PPH_STRINGREF Value + ); +// end_phapppub + +VOID PhClearIgnoredSettings( + VOID + ); + +VOID PhConvertIgnoredSettings( + VOID + ); + +NTSTATUS PhLoadSettings( + _In_ PWSTR FileName + ); + +NTSTATUS PhSaveSettings( + _In_ PWSTR FileName + ); + +VOID PhResetSettings( + VOID + ); + +#define PhaGetStringSetting(Name) PH_AUTO_T(PH_STRING, PhGetStringSetting(Name)) // phapppub + +// begin_phapppub +// High-level settings creation + +VOID PhAddSetting( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF DefaultValue + ); + +typedef struct _PH_SETTING_CREATE +{ + PH_SETTING_TYPE Type; + PWSTR Name; + PWSTR DefaultValue; +} PH_SETTING_CREATE, *PPH_SETTING_CREATE; + +PHLIBAPI +VOID +NTAPI +PhAddSettings( + _In_ PPH_SETTING_CREATE Settings, + _In_ ULONG NumberOfSettings + ); + +VOID +NTAPI +PhLoadWindowPlacementFromSetting( + _In_opt_ PWSTR PositionSettingName, + _In_opt_ PWSTR SizeSettingName, + _In_ HWND WindowHandle + ); + +VOID +NTAPI +PhSaveWindowPlacementToSetting( + _In_opt_ PWSTR PositionSettingName, + _In_opt_ PWSTR SizeSettingName, + _In_ HWND WindowHandle + ); + +VOID +NTAPI +PhLoadListViewColumnsFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); + +VOID +NTAPI +PhSaveListViewColumnsToSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); + +VOID +NTAPI +PhLoadListViewSortColumnsFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); + +VOID +NTAPI +PhSaveListViewSortColumnsToSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); + +VOID +NTAPI +PhLoadListViewGroupStatesFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); + +VOID +NTAPI +PhSaveListViewGroupStatesToSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ); +// end_phapppub + +#define PH_SET_INTEGER_CACHED_SETTING(Name, Value) (PhSetIntegerSetting(L#Name, PhCs##Name = (Value))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/svcsup.h b/phlib/include/svcsup.h index ab3b9437c6c4..47fb1fbeb51f 100644 --- a/phlib/include/svcsup.h +++ b/phlib/include/svcsup.h @@ -122,6 +122,14 @@ PhGetServiceNameFromTag( _In_ PVOID ServiceTag ); +PHLIBAPI +PPH_STRING +NTAPI +PhGetServiceNameForModuleReference( + _In_ HANDLE ProcessId, + _In_ PWSTR ModuleName + ); + PHLIBAPI NTSTATUS NTAPI @@ -135,6 +143,7 @@ PHLIBAPI NTSTATUS NTAPI PhGetServiceDllParameter( + _In_ ULONG ServiceType, _In_ PPH_STRINGREF ServiceName, _Out_ PPH_STRING *ServiceDll ); diff --git a/phlib/include/symprv.h b/phlib/include/symprv.h index 742f1148d39f..9970de6a583d 100644 --- a/phlib/include/symprv.h +++ b/phlib/include/symprv.h @@ -1,303 +1,326 @@ -#ifndef _PH_SYMPRV_H -#define _PH_SYMPRV_H - -#ifdef __cplusplus -extern "C" { -#endif - -extern PPH_OBJECT_TYPE PhSymbolProviderType; -extern PH_CALLBACK PhSymInitCallback; - -#define PH_MAX_SYMBOL_NAME_LEN 128 - -typedef struct _PH_SYMBOL_PROVIDER -{ - LIST_ENTRY ModulesListHead; - PH_QUEUED_LOCK ModulesListLock; - HANDLE ProcessHandle; - BOOLEAN IsRealHandle; - BOOLEAN IsRegistered; - - PH_INITONCE InitOnce; - PH_AVL_TREE ModulesSet; - PH_CALLBACK EventCallback; -} PH_SYMBOL_PROVIDER, *PPH_SYMBOL_PROVIDER; - -typedef enum _PH_SYMBOL_RESOLVE_LEVEL -{ - PhsrlFunction, - PhsrlModule, - PhsrlAddress, - PhsrlInvalid -} PH_SYMBOL_RESOLVE_LEVEL, *PPH_SYMBOL_RESOLVE_LEVEL; - -typedef struct _PH_SYMBOL_INFORMATION -{ - ULONG64 Address; - ULONG64 ModuleBase; - ULONG Index; - ULONG Size; -} PH_SYMBOL_INFORMATION, *PPH_SYMBOL_INFORMATION; - -typedef struct _PH_SYMBOL_LINE_INFORMATION -{ - ULONG LineNumber; - ULONG64 Address; -} PH_SYMBOL_LINE_INFORMATION, *PPH_SYMBOL_LINE_INFORMATION; - -typedef enum _PH_SYMBOL_EVENT_TYPE -{ - SymbolDeferredSymbolLoadStart = 1, - SymbolDeferredSymbolLoadComplete = 2, - SymbolDeferredSymbolLoadFailure = 3, - SymbolSymbolsUnloaded = 4, - SymbolDeferredSymbolLoadCancel = 7 -} PH_SYMBOL_EVENT_TYPE; - -typedef struct _PH_SYMBOL_EVENT_DATA -{ - PPH_SYMBOL_PROVIDER SymbolProvider; - PH_SYMBOL_EVENT_TYPE Type; - - ULONG64 BaseAddress; - ULONG CheckSum; - ULONG TimeStamp; - PPH_STRING FileName; -} PH_SYMBOL_EVENT_DATA, *PPH_SYMBOL_EVENT_DATA; - -PHLIBAPI -BOOLEAN -NTAPI -PhSymbolProviderInitialization( - VOID - ); - -PHLIBAPI -VOID -NTAPI -PhSymbolProviderCompleteInitialization( - _In_opt_ PVOID DbgHelpBase - ); - -PHLIBAPI -PPH_SYMBOL_PROVIDER -NTAPI -PhCreateSymbolProvider( - _In_opt_ HANDLE ProcessId - ); - -PHLIBAPI -BOOLEAN -NTAPI -PhGetLineFromAddress( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address, - _Out_ PPH_STRING *FileName, - _Out_opt_ PULONG Displacement, - _Out_opt_ PPH_SYMBOL_LINE_INFORMATION Information - ); - -PHLIBAPI -ULONG64 -NTAPI -PhGetModuleFromAddress( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address, - _Out_opt_ PPH_STRING *FileName - ); - -PHLIBAPI -PPH_STRING -NTAPI -PhGetSymbolFromAddress( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address, - _Out_opt_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel, - _Out_opt_ PPH_STRING *FileName, - _Out_opt_ PPH_STRING *SymbolName, - _Out_opt_ PULONG64 Displacement - ); - -PHLIBAPI -BOOLEAN -NTAPI -PhGetSymbolFromName( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ PWSTR Name, - _Out_ PPH_SYMBOL_INFORMATION Information - ); - -PHLIBAPI -BOOLEAN -NTAPI -PhLoadModuleSymbolProvider( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ PWSTR FileName, - _In_ ULONG64 BaseAddress, - _In_ ULONG Size - ); - -PHLIBAPI -VOID -NTAPI -PhSetOptionsSymbolProvider( - _In_ ULONG Mask, - _In_ ULONG Value - ); - -PHLIBAPI -VOID -NTAPI -PhSetSearchPathSymbolProvider( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ PWSTR Path - ); - -#ifdef _WIN64 -PHLIBAPI -NTSTATUS -NTAPI -PhAccessOutOfProcessFunctionEntry( - _In_ HANDLE ProcessHandle, - _In_ ULONG64 ControlPc, - _Out_ PRUNTIME_FUNCTION Function - ); -#endif - -PHLIBAPI -ULONG64 -__stdcall -PhGetModuleBase64( - _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr - ); - -PHLIBAPI -PVOID -__stdcall -PhFunctionTableAccess64( - _In_ HANDLE hProcess, - _In_ DWORD64 AddrBase - ); - -#ifndef _DBGHELP_ - -// Some of the types used below are defined in dbghelp.h. - -typedef struct _tagSTACKFRAME64 *LPSTACKFRAME64; -typedef struct _tagADDRESS64 *LPADDRESS64; - -typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( - _In_ HANDLE hProcess, - _In_ DWORD64 qwBaseAddress, - _Out_writes_bytes_(nSize) PVOID lpBuffer, - _In_ DWORD nSize, - _Out_ LPDWORD lpNumberOfBytesRead - ); - -typedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( - _In_ HANDLE ahProcess, - _In_ DWORD64 AddrBase - ); - -typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)( - _In_ HANDLE hProcess, - _In_ DWORD64 Address - ); - -typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( - _In_ HANDLE hProcess, - _In_ HANDLE hThread, - _In_ LPADDRESS64 lpaddr - ); - -typedef enum _MINIDUMP_TYPE MINIDUMP_TYPE; -typedef struct _MINIDUMP_EXCEPTION_INFORMATION *PMINIDUMP_EXCEPTION_INFORMATION; -typedef struct _MINIDUMP_USER_STREAM_INFORMATION *PMINIDUMP_USER_STREAM_INFORMATION; -typedef struct _MINIDUMP_CALLBACK_INFORMATION *PMINIDUMP_CALLBACK_INFORMATION; - -#endif - -PHLIBAPI -BOOLEAN -NTAPI -PhStackWalk( - _In_ ULONG MachineType, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ThreadHandle, - _Inout_ LPSTACKFRAME64 StackFrame, - _Inout_ PVOID ContextRecord, - _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, - _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, - _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, - _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress - ); - -PHLIBAPI -BOOLEAN -NTAPI -PhWriteMiniDumpProcess( - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId, - _In_ HANDLE FileHandle, - _In_ MINIDUMP_TYPE DumpType, - _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam - ); - -// High-level stack walking - -#define PH_THREAD_STACK_FRAME_I386 0x1 -#define PH_THREAD_STACK_FRAME_AMD64 0x2 -#define PH_THREAD_STACK_FRAME_KERNEL 0x4 -#define PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT 0x100 - -/** Contains information about a thread stack frame. */ -typedef struct _PH_THREAD_STACK_FRAME -{ - PVOID PcAddress; - PVOID ReturnAddress; - PVOID FrameAddress; - PVOID StackAddress; - PVOID BStoreAddress; - PVOID Params[4]; - ULONG Flags; -} PH_THREAD_STACK_FRAME, *PPH_THREAD_STACK_FRAME; - -#define PH_WALK_I386_STACK 0x1 -#define PH_WALK_AMD64_STACK 0x2 -#define PH_WALK_KERNEL_STACK 0x10 - -/** - * A callback function passed to PhWalkThreadStack() and called for each stack frame. - * - * \param StackFrame A structure providing information about the stack frame. - * \param Context A user-defined value passed to PhWalkThreadStack(). - * - * \return TRUE to continue the stack walk, FALSE to stop. - */ -typedef BOOLEAN (NTAPI *PPH_WALK_THREAD_STACK_CALLBACK)( - _In_ PPH_THREAD_STACK_FRAME StackFrame, - _In_opt_ PVOID Context - ); - -PHLIBAPI -NTSTATUS -NTAPI -PhWalkThreadStack( - _In_ HANDLE ThreadHandle, - _In_opt_ HANDLE ProcessHandle, - _In_opt_ PCLIENT_ID ClientId, - _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG Flags, - _In_ PPH_WALK_THREAD_STACK_CALLBACK Callback, - _In_opt_ PVOID Context - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_SYMPRV_H +#define _PH_SYMPRV_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern PPH_OBJECT_TYPE PhSymbolProviderType; +extern PH_CALLBACK PhSymbolEventCallback; + +#define PH_MAX_SYMBOL_NAME_LEN 128 + +typedef struct _PH_SYMBOL_PROVIDER +{ + LIST_ENTRY ModulesListHead; + PH_QUEUED_LOCK ModulesListLock; + HANDLE ProcessHandle; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN IsRealHandle : 1; + BOOLEAN IsRegistered : 1; + BOOLEAN Terminating : 1; + BOOLEAN Spare : 5; + }; + }; + + PH_INITONCE InitOnce; + PH_AVL_TREE ModulesSet; +} PH_SYMBOL_PROVIDER, *PPH_SYMBOL_PROVIDER; + +typedef enum _PH_SYMBOL_RESOLVE_LEVEL +{ + PhsrlFunction, + PhsrlModule, + PhsrlAddress, + PhsrlInvalid +} PH_SYMBOL_RESOLVE_LEVEL, *PPH_SYMBOL_RESOLVE_LEVEL; + +typedef struct _PH_SYMBOL_INFORMATION +{ + ULONG64 Address; + ULONG64 ModuleBase; + ULONG Index; + ULONG Size; +} PH_SYMBOL_INFORMATION, *PPH_SYMBOL_INFORMATION; + +typedef struct _PH_SYMBOL_LINE_INFORMATION +{ + ULONG LineNumber; + ULONG64 Address; +} PH_SYMBOL_LINE_INFORMATION, *PPH_SYMBOL_LINE_INFORMATION; + +typedef struct _PH_SYMBOL_EVENT_DATA +{ + ULONG ActionCode; + HANDLE ProcessHandle; + PPH_SYMBOL_PROVIDER SymbolProvider; + PVOID EventData; +} PH_SYMBOL_EVENT_DATA, *PPH_SYMBOL_EVENT_DATA; + +PHLIBAPI +PPH_SYMBOL_PROVIDER +NTAPI +PhCreateSymbolProvider( + _In_opt_ HANDLE ProcessId + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhGetLineFromAddress( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address, + _Out_ PPH_STRING *FileName, + _Out_opt_ PULONG Displacement, + _Out_opt_ PPH_SYMBOL_LINE_INFORMATION Information + ); + +PHLIBAPI +ULONG64 +NTAPI +PhGetModuleFromAddress( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address, + _Out_opt_ PPH_STRING *FileName + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetSymbolFromAddress( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address, + _Out_opt_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel, + _Out_opt_ PPH_STRING *FileName, + _Out_opt_ PPH_STRING *SymbolName, + _Out_opt_ PULONG64 Displacement + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhGetSymbolFromName( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR Name, + _Out_ PPH_SYMBOL_INFORMATION Information + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhLoadModuleSymbolProvider( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR FileName, + _In_ ULONG64 BaseAddress, + _In_ ULONG Size + ); + +PHLIBAPI +VOID +NTAPI +PhSetOptionsSymbolProvider( + _In_ ULONG Mask, + _In_ ULONG Value + ); + +PHLIBAPI +VOID +NTAPI +PhSetSearchPathSymbolProvider( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR Path + ); + +#ifdef _WIN64 +PHLIBAPI +NTSTATUS +NTAPI +PhAccessOutOfProcessFunctionEntry( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 ControlPc, + _Out_ PRUNTIME_FUNCTION Function + ); +#endif + +PHLIBAPI +ULONG64 +__stdcall +PhGetModuleBase64( + _In_ HANDLE hProcess, + _In_ ULONG64 dwAddr + ); + +PHLIBAPI +PVOID +__stdcall +PhFunctionTableAccess64( + _In_ HANDLE hProcess, + _In_ ULONG64 AddrBase + ); + +#ifndef _DBGHELP_ + +// Some of the types used below are defined in dbghelp.h. + +typedef struct _tagSTACKFRAME64 *LPSTACKFRAME64; +typedef struct _tagADDRESS64 *LPADDRESS64; + +typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( + _In_ HANDLE hProcess, + _In_ ULONG64 qwBaseAddress, + _Out_writes_bytes_(nSize) PVOID lpBuffer, + _In_ ULONG nSize, + _Out_ PULONG lpNumberOfBytesRead + ); + +typedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( + _In_ HANDLE ahProcess, + _In_ ULONG64 AddrBase + ); + +typedef ULONG64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)( + _In_ HANDLE hProcess, + _In_ ULONG64 Address + ); + +typedef ULONG64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( + _In_ HANDLE hProcess, + _In_ HANDLE hThread, + _In_ LPADDRESS64 lpaddr + ); + +typedef enum _MINIDUMP_TYPE MINIDUMP_TYPE; +typedef struct _MINIDUMP_EXCEPTION_INFORMATION *PMINIDUMP_EXCEPTION_INFORMATION; +typedef struct _MINIDUMP_USER_STREAM_INFORMATION *PMINIDUMP_USER_STREAM_INFORMATION; +typedef struct _MINIDUMP_CALLBACK_INFORMATION *PMINIDUMP_CALLBACK_INFORMATION; + +#endif + +PHLIBAPI +BOOLEAN +NTAPI +PhStackWalk( + _In_ ULONG MachineType, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ThreadHandle, + _Inout_ LPSTACKFRAME64 StackFrame, + _Inout_ PVOID ContextRecord, + _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhWriteMiniDumpProcess( + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE FileHandle, + _In_ MINIDUMP_TYPE DumpType, + _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + +// High-level stack walking + +#define PH_THREAD_STACK_FRAME_I386 0x1 +#define PH_THREAD_STACK_FRAME_AMD64 0x2 +#define PH_THREAD_STACK_FRAME_KERNEL 0x4 +#define PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT 0x100 + +/** Contains information about a thread stack frame. */ +typedef struct _PH_THREAD_STACK_FRAME +{ + PVOID PcAddress; + PVOID ReturnAddress; + PVOID FrameAddress; + PVOID StackAddress; + PVOID BStoreAddress; + PVOID Params[4]; + ULONG Flags; +} PH_THREAD_STACK_FRAME, *PPH_THREAD_STACK_FRAME; + +#define PH_WALK_I386_STACK 0x1 +#define PH_WALK_AMD64_STACK 0x2 +#define PH_WALK_KERNEL_STACK 0x10 + +/** + * A callback function passed to PhWalkThreadStack() and called for each stack frame. + * + * \param StackFrame A structure providing information about the stack frame. + * \param Context A user-defined value passed to PhWalkThreadStack(). + * + * \return TRUE to continue the stack walk, FALSE to stop. + */ +typedef BOOLEAN (NTAPI *PPH_WALK_THREAD_STACK_CALLBACK)( + _In_ PPH_THREAD_STACK_FRAME StackFrame, + _In_opt_ PVOID Context + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhWalkThreadStack( + _In_ HANDLE ThreadHandle, + _In_opt_ HANDLE ProcessHandle, + _In_opt_ PCLIENT_ID ClientId, + _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG Flags, + _In_ PPH_WALK_THREAD_STACK_CALLBACK Callback, + _In_opt_ PVOID Context + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhUndecorateSymbolName( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR DecoratedName + ); + +typedef struct _PH_SYMBOL_INFO { + PH_STRINGREF Name; + ULONG TypeIndex; // Type Index of symbol + ULONG Index; + ULONG Size; + ULONG64 ModBase; // Base Address of module comtaining this symbol + ULONG Flags; + ULONG64 Value; // Value of symbol, ValuePresent should be 1 + ULONG64 Address; // Address of symbol including base address of module + ULONG Register; // register holding value or pointer to value + ULONG Scope; // scope of the symbol + ULONG Tag; // pdb classification +} PH_SYMBOL_INFO, *PPH_SYMBOL_INFO; + +typedef BOOLEAN (NTAPI* PPH_ENUMERATE_SYMBOLS_CALLBACK)( + _In_ PPH_SYMBOL_INFO pSymInfo, + _In_ ULONG SymbolSize, + _In_opt_ PVOID UserContext + ); + +PHLIBAPI +BOOLEAN +NTAPI +PhEnumerateSymbols( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ HANDLE ProcessHandle, + _In_ ULONG64 BaseOfDll, + _In_opt_ PCWSTR Mask, + _In_ PPH_ENUMERATE_SYMBOLS_CALLBACK EnumSymbolsCallback, + _In_opt_ const PVOID UserContext + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/symprvp.h b/phlib/include/symprvp.h index 8f6bd6bb580e..48af6207819d 100644 --- a/phlib/include/symprvp.h +++ b/phlib/include/symprvp.h @@ -1,170 +1,149 @@ -#ifndef _PH_SYMPRVP_H -#define _PH_SYMPRVP_H - -typedef BOOL (WINAPI *_SymInitialize)( - _In_ HANDLE hProcess, - _In_opt_ PCSTR UserSearchPath, - _In_ BOOL fInvadeProcess - ); - -typedef BOOL (WINAPI *_SymCleanup)( - _In_ HANDLE hProcess - ); - -typedef BOOL (WINAPI *_SymEnumSymbols)( - _In_ HANDLE hProcess, - _In_ ULONG64 BaseOfDll, - _In_opt_ PCSTR Mask, - _In_ PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, - _In_opt_ const PVOID UserContext - ); - -typedef BOOL (WINAPI *_SymEnumSymbolsW)( - _In_ HANDLE hProcess, - _In_ ULONG64 BaseOfDll, - _In_opt_ PCWSTR Mask, - _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, - _In_opt_ const PVOID UserContext - ); - -typedef BOOL (WINAPI *_SymFromAddr)( - _In_ HANDLE hProcess, - _In_ DWORD64 Address, - _Out_opt_ PDWORD64 Displacement, - _Inout_ PSYMBOL_INFO Symbol - ); - -typedef BOOL (WINAPI *_SymFromAddrW)( - _In_ HANDLE hProcess, - _In_ DWORD64 Address, - _Out_opt_ PDWORD64 Displacement, - _Inout_ PSYMBOL_INFOW Symbol - ); - -typedef BOOL (WINAPI *_SymFromName)( - _In_ HANDLE hProcess, - _In_ PCSTR Name, - _Inout_ PSYMBOL_INFO Symbol - ); - -typedef BOOL (WINAPI *_SymFromNameW)( - _In_ HANDLE hProcess, - _In_ PCWSTR Name, - _Inout_ PSYMBOL_INFOW Symbol - ); - -typedef BOOL (WINAPI *_SymGetLineFromAddr64)( - _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr, - _Out_ PDWORD pdwDisplacement, - _Out_ PIMAGEHLP_LINE64 Line - ); - -typedef BOOL (WINAPI *_SymGetLineFromAddrW64)( - _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr, - _Out_ PDWORD pdwDisplacement, - _Out_ PIMAGEHLP_LINEW64 Line - ); - -typedef DWORD64 (WINAPI *_SymLoadModule64)( - _In_ HANDLE hProcess, - _In_opt_ HANDLE hFile, - _In_opt_ PCSTR ImageName, - _In_opt_ PCSTR ModuleName, - _In_ DWORD64 BaseOfDll, - _In_ DWORD SizeOfDll - ); - -typedef DWORD64 (WINAPI *_SymLoadModuleExW)( - _In_ HANDLE hProcess, - _In_ HANDLE hFile, - _In_ PCWSTR ImageName, - _In_ PCWSTR ModuleName, - _In_ DWORD64 BaseOfDll, - _In_ DWORD DllSize, - _In_ PMODLOAD_DATA Data, - _In_ DWORD Flags - ); - -typedef DWORD (WINAPI *_SymGetOptions)(); - -typedef DWORD (WINAPI *_SymSetOptions)( - _In_ DWORD SymOptions - ); - -typedef BOOL (WINAPI *_SymGetSearchPath)( - _In_ HANDLE hProcess, - _Out_ PSTR SearchPath, - _In_ DWORD SearchPathLength - ); - -typedef BOOL (WINAPI *_SymGetSearchPathW)( - _In_ HANDLE hProcess, - _Out_ PWSTR SearchPath, - _In_ DWORD SearchPathLength - ); - -typedef BOOL (WINAPI *_SymSetSearchPath)( - _In_ HANDLE hProcess, - _In_opt_ PCSTR SearchPath - ); - -typedef BOOL (WINAPI *_SymSetSearchPathW)( - _In_ HANDLE hProcess, - _In_opt_ PCWSTR SearchPath - ); - -typedef BOOL (WINAPI *_SymUnloadModule64)( - _In_ HANDLE hProcess, - _In_ DWORD64 BaseOfDll - ); - -typedef PVOID (WINAPI *_SymFunctionTableAccess64)( - _In_ HANDLE hProcess, - _In_ DWORD64 AddrBase - ); - -typedef DWORD64 (WINAPI *_SymGetModuleBase64)( - _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr - ); - -typedef BOOL (WINAPI *_SymRegisterCallbackW64)( - _In_ HANDLE hProcess, - _In_ PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction, - _In_ ULONG64 UserContext - ); - -typedef BOOL (WINAPI *_StackWalk64)( - _In_ DWORD MachineType, - _In_ HANDLE hProcess, - _In_ HANDLE hThread, - _Inout_ LPSTACKFRAME64 StackFrame, - _Inout_ PVOID ContextRecord, - _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, - _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, - _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, - _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress - ); - -typedef BOOL (WINAPI *_MiniDumpWriteDump)( - _In_ HANDLE hProcess, - _In_ DWORD ProcessId, - _In_ HANDLE hFile, - _In_ MINIDUMP_TYPE DumpType, - _In_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - _In_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - _In_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam - ); - -typedef UINT_PTR (CALLBACK *_SymbolServerGetOptions)( - VOID - ); - -typedef BOOL (CALLBACK *_SymbolServerSetOptions)( - _In_ UINT_PTR options, - _In_ ULONG64 data - ); - -#endif \ No newline at end of file +#ifndef _PH_SYMPRVP_H +#define _PH_SYMPRVP_H + +typedef BOOL (WINAPI *_SymInitializeW)( + _In_ HANDLE ProcessHandle, + _In_opt_ PCWSTR UserSearchPath, + _In_ BOOL fInvadeProcess + ); + +typedef BOOL (WINAPI *_SymCleanup)( + _In_ HANDLE ProcessHandle + ); + +typedef BOOL (WINAPI *_SymEnumSymbolsW)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 BaseOfDll, + _In_opt_ PCWSTR Mask, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, + _In_opt_ const PVOID UserContext + ); + +typedef BOOL (WINAPI *_SymFromAddrW)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement, + _Inout_ PSYMBOL_INFOW Symbol + ); + +typedef BOOL (WINAPI *_SymFromNameW)( + _In_ HANDLE ProcessHandle, + _In_ PCWSTR Name, + _Inout_ PSYMBOL_INFOW Symbol + ); + +typedef BOOL (WINAPI *_SymEnumTypesW)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 BaseOfDll, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, + _In_opt_ PVOID UserContext + ); + +typedef BOOL (WINAPI *_SymGetModuleInfoW64)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 qwAddr, + _Out_ PIMAGEHLP_MODULEW64 ModuleInfo + ); + +typedef BOOL(WINAPI *_SymGetTypeFromNameW)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 BaseOfDll, + _In_ PCWSTR Name, + _Inout_ PSYMBOL_INFOW Symbol + ); + +typedef BOOL (WINAPI *_SymGetLineFromAddrW64)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 dwAddr, + _Out_ PULONG pdwDisplacement, + _Out_ PIMAGEHLP_LINEW64 Line + ); + +typedef ULONG64 (WINAPI *_SymLoadModuleExW)( + _In_ HANDLE ProcessHandle, + _In_opt_ HANDLE FileHandle, + _In_opt_ PCWSTR ImageName, + _In_opt_ PCWSTR ModuleName, + _In_ ULONG64 BaseOfDll, + _In_ ULONG DllSize, + _In_opt_ PMODLOAD_DATA Data, + _In_opt_ ULONG Flags + ); + +typedef ULONG (WINAPI *_SymGetOptions)(); + +typedef ULONG (WINAPI *_SymSetOptions)( + _In_ ULONG SymOptions + ); + +typedef BOOL (WINAPI *_SymGetSearchPathW)( + _In_ HANDLE ProcessHandle, + _Out_ PWSTR SearchPath, + _In_ ULONG SearchPathLength + ); + +typedef BOOL (WINAPI *_SymSetSearchPathW)( + _In_ HANDLE ProcessHandle, + _In_opt_ PCWSTR SearchPath + ); + +typedef BOOL (WINAPI *_SymUnloadModule64)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 BaseOfDll + ); + +typedef PVOID (WINAPI *_SymFunctionTableAccess64)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 AddrBase + ); + +typedef ULONG64 (WINAPI *_SymGetModuleBase64)( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 dwAddr + ); + +typedef BOOL (WINAPI *_SymRegisterCallbackW64)( + _In_ HANDLE ProcessHandle, + _In_ PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction, + _In_ ULONG64 UserContext + ); + +typedef BOOL (WINAPI *_StackWalk64)( + _In_ ULONG MachineType, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ThreadHandle, + _Inout_ LPSTACKFRAME64 StackFrame, + _Inout_ PVOID ContextRecord, + _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress + ); + +typedef BOOL (WINAPI *_MiniDumpWriteDump)( + _In_ HANDLE ProcessHandle, + _In_ ULONG ProcessId, + _In_ HANDLE FileHandle, + _In_ MINIDUMP_TYPE DumpType, + _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + +typedef UINT_PTR (CALLBACK *_SymbolServerGetOptions)( + VOID + ); + +typedef BOOL (CALLBACK *_SymbolServerSetOptions)( + _In_ UINT_PTR options, + _In_ ULONG64 data + ); + +typedef ULONG (WINAPI *_UnDecorateSymbolNameW)( + _In_ PCWSTR DecoratedName, + _Out_ PWSTR UnDecoratedName, + _In_ ULONG UndecoratedLength, + _In_ ULONG Flags + ); + +#endif diff --git a/phlib/include/templ.h b/phlib/include/templ.h index 7a61f9a359f6..7cff6a1469a6 100644 --- a/phlib/include/templ.h +++ b/phlib/include/templ.h @@ -1,7 +1,7 @@ -#ifndef _PH_TEMPL_H -#define _PH_TEMPL_H - -#define TEMPLATE_(f,T) f##_##T -#define T___(f,T) TEMPLATE_(f,T) - -#endif +#ifndef _PH_TEMPL_H +#define _PH_TEMPL_H + +#define TEMPLATE_(f,T) f##_##T +#define T___(f,T) TEMPLATE_(f,T) + +#endif diff --git a/phlib/include/treenew.h b/phlib/include/treenew.h index bc4c649cfca3..ae1fb9e58a61 100644 --- a/phlib/include/treenew.h +++ b/phlib/include/treenew.h @@ -1,672 +1,686 @@ -#ifndef _PH_TREENEW_H -#define _PH_TREENEW_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_TREENEW_CLASSNAME L"PhTreeNew" - -#define PH_TREENEW_SEARCH_TIMEOUT 1000 -#define PH_TREENEW_SEARCH_MAXIMUM_LENGTH 1023 - -typedef struct _PH_TREENEW_COLUMN -{ - union - { - ULONG Flags; - struct - { - ULONG Visible : 1; - ULONG CustomDraw : 1; - ULONG Fixed : 1; // Whether this is the fixed column - ULONG SortDescending : 1; // Sort descending on initial click rather than ascending - ULONG DpiScaleOnAdd : 1; // Whether to DPI scale the width (only when adding) - ULONG SpareFlags : 27; - }; - }; - ULONG Id; - PVOID Context; - PWSTR Text; - LONG Width; - ULONG Alignment; - ULONG DisplayIndex; // -1 for fixed column or invalid - - ULONG TextFlags; - - struct - { - LONG ViewIndex; // Actual index in header control - LONG ViewX; // 0 for the fixed column, and an offset from the divider for normal columns - } s; -} PH_TREENEW_COLUMN, *PPH_TREENEW_COLUMN; - -typedef struct _PH_TREENEW_NODE -{ - union - { - ULONG Flags; - struct - { - ULONG Visible : 1; - ULONG Selected : 1; - ULONG Expanded : 1; - ULONG UseAutoForeColor : 1; - ULONG UseTempBackColor : 1; - ULONG Unselectable : 1; - ULONG SpareFlags : 26; - }; - }; - - COLORREF BackColor; - COLORREF ForeColor; - COLORREF TempBackColor; - HFONT Font; - HICON Icon; - - PPH_STRINGREF TextCache; - ULONG TextCacheSize; - - ULONG Index; // Index within the flat list - ULONG Level; // 0 for root, 1, 2, ... - - struct - { - union - { - ULONG Flags2; - struct - { - ULONG IsLeaf : 1; - ULONG CachedColorValid : 1; - ULONG CachedFontValid : 1; - ULONG CachedIconValid : 1; - ULONG PlusMinusHot : 1; - ULONG SpareFlags2 : 27; - }; - }; - - // Temp. drawing data - COLORREF DrawBackColor; - COLORREF DrawForeColor; - } s; -} PH_TREENEW_NODE, *PPH_TREENEW_NODE; - -// Styles -#define TN_STYLE_ICONS 0x1 -#define TN_STYLE_DOUBLE_BUFFERED 0x2 -#define TN_STYLE_NO_DIVIDER 0x4 -#define TN_STYLE_ANIMATE_DIVIDER 0x8 -#define TN_STYLE_NO_COLUMN_SORT 0x10 -#define TN_STYLE_NO_COLUMN_REORDER 0x20 -#define TN_STYLE_THIN_ROWS 0x40 -#define TN_STYLE_NO_COLUMN_HEADER 0x80 - -// Extended flags -#define TN_FLAG_ITEM_DRAG_SELECT 0x1 -#define TN_FLAG_NO_UNFOLDING_TOOLTIPS 0x2 - -// Callback flags -#define TN_CACHE 0x1 -#define TN_AUTO_FORECOLOR 0x1000 - -// Column change flags -#define TN_COLUMN_CONTEXT 0x1 -#define TN_COLUMN_TEXT 0x2 -#define TN_COLUMN_WIDTH 0x4 -#define TN_COLUMN_ALIGNMENT 0x8 -#define TN_COLUMN_DISPLAYINDEX 0x10 -#define TN_COLUMN_TEXTFLAGS 0x20 -#define TN_COLUMN_FLAG_VISIBLE 0x100000 -#define TN_COLUMN_FLAG_CUSTOMDRAW 0x200000 -#define TN_COLUMN_FLAG_FIXED 0x400000 -#define TN_COLUMN_FLAG_SORTDESCENDING 0x800000 -#define TN_COLUMN_FLAG_NODPISCALEONADD 0x1000000 -#define TN_COLUMN_FLAGS 0xfff00000 - -// Cache flags -#define TN_CACHE_COLOR 0x1 -#define TN_CACHE_FONT 0x2 -#define TN_CACHE_ICON 0x4 - -// Cell part input flags -#define TN_MEASURE_TEXT 0x1 - -// Cell part flags -#define TN_PART_CELL 0x1 -#define TN_PART_PLUSMINUS 0x2 -#define TN_PART_ICON 0x4 -#define TN_PART_CONTENT 0x8 -#define TN_PART_TEXT 0x10 - -// Hit test input flags -#define TN_TEST_COLUMN 0x1 -#define TN_TEST_SUBITEM 0x2 // requires TN_TEST_COLUMN - -// Hit test flags -#define TN_HIT_LEFT 0x1 -#define TN_HIT_RIGHT 0x2 -#define TN_HIT_ABOVE 0x4 -#define TN_HIT_BELOW 0x8 -#define TN_HIT_ITEM 0x10 -#define TN_HIT_ITEM_PLUSMINUS 0x20 // requires TN_TEST_SUBITEM -#define TN_HIT_ITEM_ICON 0x40 // requires TN_TEST_SUBITEM -#define TN_HIT_ITEM_CONTENT 0x80 // requires TN_TEST_SUBITEM -#define TN_HIT_DIVIDER 0x100 - -// Selection flags -#define TN_SELECT_DESELECT 0x1 -#define TN_SELECT_TOGGLE 0x2 -#define TN_SELECT_RESET 0x4 - -// Auto-size flags -#define TN_AUTOSIZE_REMAINING_SPACE 0x1 - -typedef struct _PH_TREENEW_CELL_PARTS -{ - ULONG Flags; - RECT RowRect; - RECT CellRect; // TN_PART_CELL - RECT PlusMinusRect; // TN_PART_PLUSMINUS - RECT IconRect; // TN_PART_ICON - RECT ContentRect; // TN_PART_CONTENT - RECT TextRect; // TN_PART_TEXT - PH_STRINGREF Text; // TN_PART_TEXT - HFONT Font; // TN_PART_TEXT -} PH_TREENEW_CELL_PARTS, *PPH_TREENEW_CELL_PARTS; - -typedef struct _PH_TREENEW_HIT_TEST -{ - POINT Point; - ULONG InFlags; - - ULONG Flags; - PPH_TREENEW_NODE Node; - PPH_TREENEW_COLUMN Column; // requires TN_TEST_COLUMN -} PH_TREENEW_HIT_TEST, *PPH_TREENEW_HIT_TEST; - -typedef enum _PH_TREENEW_MESSAGE -{ - TreeNewGetChildren, // PPH_TREENEW_GET_CHILDREN Parameter1 - TreeNewIsLeaf, // PPH_TREENEW_IS_LEAF Parameter1 - TreeNewGetCellText, // PPH_TREENEW_GET_CELL_TEXT Parameter1 - TreeNewGetNodeColor, // PPH_TREENEW_GET_NODE_COLOR Parameter1 - TreeNewGetNodeFont, // PPH_TREENEW_GET_NODE_FONT Parameter1 - TreeNewGetNodeIcon, // PPH_TREENEW_GET_NODE_ICON Parameter1 - TreeNewGetCellTooltip, // PPH_TREENEW_GET_CELL_TOOLTIP Parameter1 - TreeNewCustomDraw, // PPH_TREENEW_CUSTOM_DRAW Parameter1 - - // Notifications - TreeNewNodeExpanding, // PPH_TREENEW_NODE Parameter1, PPH_TREENEW_NODE_EVENT Parameter2 - TreeNewNodeSelecting, // PPH_TREENEW_NODE Parameter1 - - TreeNewSortChanged, - TreeNewSelectionChanged, - - TreeNewKeyDown, // PPH_TREENEW_KEY_EVENT Parameter1 - TreeNewLeftClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 - TreeNewRightClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 - TreeNewLeftDoubleClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 - TreeNewRightDoubleClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 - TreeNewContextMenu, // PPH_TREENEW_CONTEXT_MENU Parameter1 - - TreeNewHeaderRightClick, // PPH_TREENEW_HEADER_MOUSE_EVENT Parameter1 - TreeNewIncrementalSearch, // PPH_TREENEW_SEARCH_EVENT Parameter1 - - TreeNewColumnResized, // PPH_TREENEW_COLUMN Parameter1 - TreeNewColumnReordered, - - TreeNewDestroying, - TreeNewGetDialogCode, // ULONG Parameter1, PULONG Parameter2 - - MaxTreeNewMessage -} PH_TREENEW_MESSAGE; - -typedef BOOLEAN (NTAPI *PPH_TREENEW_CALLBACK)( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -typedef struct _PH_TREENEW_GET_CHILDREN -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - - ULONG NumberOfChildren; - PPH_TREENEW_NODE *Children; // can be NULL if no children -} PH_TREENEW_GET_CHILDREN, *PPH_TREENEW_GET_CHILDREN; - -typedef struct _PH_TREENEW_IS_LEAF -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - - BOOLEAN IsLeaf; -} PH_TREENEW_IS_LEAF, *PPH_TREENEW_IS_LEAF; - -typedef struct _PH_TREENEW_GET_CELL_TEXT -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - ULONG Id; - - PH_STRINGREF Text; -} PH_TREENEW_GET_CELL_TEXT, *PPH_TREENEW_GET_CELL_TEXT; - -typedef struct _PH_TREENEW_GET_NODE_COLOR -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - - COLORREF BackColor; - COLORREF ForeColor; -} PH_TREENEW_GET_NODE_COLOR, *PPH_TREENEW_GET_NODE_COLOR; - -typedef struct _PH_TREENEW_GET_NODE_FONT -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - - HFONT Font; -} PH_TREENEW_GET_NODE_FONT, *PPH_TREENEW_GET_NODE_FONT; - -typedef struct _PH_TREENEW_GET_NODE_ICON -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - - HICON Icon; -} PH_TREENEW_GET_NODE_ICON, *PPH_TREENEW_GET_NODE_ICON; - -typedef struct _PH_TREENEW_GET_CELL_TOOLTIP -{ - ULONG Flags; - PPH_TREENEW_NODE Node; - PPH_TREENEW_COLUMN Column; - - BOOLEAN Unfolding; - PH_STRINGREF Text; - HFONT Font; - ULONG MaximumWidth; -} PH_TREENEW_GET_CELL_TOOLTIP, *PPH_TREENEW_GET_CELL_TOOLTIP; - -typedef struct _PH_TREENEW_CUSTOM_DRAW -{ - PPH_TREENEW_NODE Node; - PPH_TREENEW_COLUMN Column; - - HDC Dc; - RECT CellRect; - RECT TextRect; -} PH_TREENEW_CUSTOM_DRAW, *PPH_TREENEW_CUSTOM_DRAW; - -typedef struct _PH_TREENEW_MOUSE_EVENT -{ - POINT Location; - PPH_TREENEW_NODE Node; - PPH_TREENEW_COLUMN Column; - ULONG KeyFlags; -} PH_TREENEW_MOUSE_EVENT, *PPH_TREENEW_MOUSE_EVENT; - -typedef struct _PH_TREENEW_KEY_EVENT -{ - BOOLEAN Handled; - ULONG VirtualKey; - ULONG Data; -} PH_TREENEW_KEY_EVENT, *PPH_TREENEW_KEY_EVENT; - -typedef struct _PH_TREENEW_NODE_EVENT -{ - BOOLEAN Handled; - ULONG Flags; - PVOID Reserved1; - PVOID Reserved2; -} PH_TREENEW_NODE_EVENT, *PPH_TREENEW_NODE_EVENT; - -typedef struct _PH_TREENEW_CONTEXT_MENU -{ - POINT Location; - POINT ClientLocation; - PPH_TREENEW_NODE Node; - PPH_TREENEW_COLUMN Column; - BOOLEAN KeyboardInvoked; -} PH_TREENEW_CONTEXT_MENU, *PPH_TREENEW_CONTEXT_MENU; - -typedef struct _PH_TREENEW_HEADER_MOUSE_EVENT -{ - POINT ScreenLocation; - POINT Location; - POINT HeaderLocation; - PPH_TREENEW_COLUMN Column; -} PH_TREENEW_HEADER_MOUSE_EVENT, *PPH_TREENEW_HEADER_MOUSE_EVENT; - -typedef struct _PH_TREENEW_SEARCH_EVENT -{ - LONG FoundIndex; - LONG StartIndex; - PH_STRINGREF String; -} PH_TREENEW_SEARCH_EVENT, *PPH_TREENEW_SEARCH_EVENT; - -#define TNM_FIRST (WM_USER + 1) -#define TNM_SETCALLBACK (WM_USER + 1) -#define TNM_NODESADDED (WM_USER + 2) // unimplemented -#define TNM_NODESREMOVED (WM_USER + 3) // unimplemented -#define TNM_NODESSTRUCTURED (WM_USER + 4) -#define TNM_ADDCOLUMN (WM_USER + 5) -#define TNM_REMOVECOLUMN (WM_USER + 6) -#define TNM_GETCOLUMN (WM_USER + 7) -#define TNM_SETCOLUMN (WM_USER + 8) -#define TNM_GETCOLUMNORDERARRAY (WM_USER + 9) -#define TNM_SETCOLUMNORDERARRAY (WM_USER + 10) -#define TNM_SETCURSOR (WM_USER + 11) -#define TNM_GETSORT (WM_USER + 12) -#define TNM_SETSORT (WM_USER + 13) -#define TNM_SETTRISTATE (WM_USER + 14) -#define TNM_ENSUREVISIBLE (WM_USER + 15) -#define TNM_SCROLL (WM_USER + 16) -#define TNM_GETFLATNODECOUNT (WM_USER + 17) -#define TNM_GETFLATNODE (WM_USER + 18) -#define TNM_GETCELLTEXT (WM_USER + 19) -#define TNM_SETNODEEXPANDED (WM_USER + 20) -#define TNM_GETMAXID (WM_USER + 21) -#define TNM_SETMAXID (WM_USER + 22) -#define TNM_INVALIDATENODE (WM_USER + 23) -#define TNM_INVALIDATENODES (WM_USER + 24) -#define TNM_GETFIXEDHEADER (WM_USER + 25) -#define TNM_GETHEADER (WM_USER + 26) -#define TNM_GETTOOLTIPS (WM_USER + 27) -#define TNM_SELECTRANGE (WM_USER + 28) -#define TNM_DESELECTRANGE (WM_USER + 29) -#define TNM_GETCOLUMNCOUNT (WM_USER + 30) -#define TNM_SETREDRAW (WM_USER + 31) -#define TNM_GETVIEWPARTS (WM_USER + 32) -#define TNM_GETFIXEDCOLUMN (WM_USER + 33) -#define TNM_GETFIRSTCOLUMN (WM_USER + 34) -#define TNM_SETFOCUSNODE (WM_USER + 35) -#define TNM_SETMARKNODE (WM_USER + 36) -#define TNM_SETHOTNODE (WM_USER + 37) -#define TNM_SETEXTENDEDFLAGS (WM_USER + 38) -#define TNM_GETCALLBACK (WM_USER + 39) -#define TNM_HITTEST (WM_USER + 40) -#define TNM_GETVISIBLECOLUMNCOUNT (WM_USER + 41) -#define TNM_AUTOSIZECOLUMN (WM_USER + 42) -#define TNM_SETEMPTYTEXT (WM_USER + 43) -#define TNM_SETROWHEIGHT (WM_USER + 44) -#define TNM_ISFLATNODEVALID (WM_USER + 45) -#define TNM_LAST (WM_USER + 45) - -#define TreeNew_SetCallback(hWnd, Callback, Context) \ - SendMessage((hWnd), TNM_SETCALLBACK, (WPARAM)(Context), (LPARAM)(Callback)) - -#define TreeNew_NodesStructured(hWnd) \ - SendMessage((hWnd), TNM_NODESSTRUCTURED, 0, 0) - -#define TreeNew_AddColumn(hWnd, Column) \ - SendMessage((hWnd), TNM_ADDCOLUMN, 0, (LPARAM)(Column)) - -#define TreeNew_RemoveColumn(hWnd, Id) \ - SendMessage((hWnd), TNM_REMOVECOLUMN, (WPARAM)(Id), 0) - -#define TreeNew_GetColumn(hWnd, Id, Column) \ - SendMessage((hWnd), TNM_GETCOLUMN, (WPARAM)(Id), (LPARAM)(Column)) - -#define TreeNew_SetColumn(hWnd, Mask, Column) \ - SendMessage((hWnd), TNM_SETCOLUMN, (WPARAM)(Mask), (LPARAM)(Column)) - -#define TreeNew_GetColumnOrderArray(hWnd, Count, Array) \ - SendMessage((hWnd), TNM_GETCOLUMNORDERARRAY, (WPARAM)(Count), (LPARAM)(Array)) - -#define TreeNew_SetColumnOrderArray(hWnd, Count, Array) \ - SendMessage((hWnd), TNM_SETCOLUMNORDERARRAY, (WPARAM)(Count), (LPARAM)(Array)) - -#define TreeNew_SetCursor(hWnd, Cursor) \ - SendMessage((hWnd), TNM_SETCURSOR, 0, (LPARAM)(Cursor)) - -#define TreeNew_GetSort(hWnd, Column, Order) \ - SendMessage((hWnd), TNM_GETSORT, (WPARAM)(Column), (LPARAM)(Order)) - -#define TreeNew_SetSort(hWnd, Column, Order) \ - SendMessage((hWnd), TNM_SETSORT, (WPARAM)(Column), (LPARAM)(Order)) - -#define TreeNew_SetTriState(hWnd, TriState) \ - SendMessage((hWnd), TNM_SETTRISTATE, (WPARAM)(TriState), 0) - -#define TreeNew_EnsureVisible(hWnd, Node) \ - SendMessage((hWnd), TNM_ENSUREVISIBLE, 0, (LPARAM)(Node)) - -#define TreeNew_Scroll(hWnd, DeltaRows, DeltaX) \ - SendMessage((hWnd), TNM_SCROLL, (WPARAM)(DeltaRows), (LPARAM)(DeltaX)) - -#define TreeNew_GetFlatNodeCount(hWnd) \ - ((ULONG)SendMessage((hWnd), TNM_GETFLATNODECOUNT, 0, 0)) - -#define TreeNew_GetFlatNode(hWnd, Index) \ - ((PPH_TREENEW_NODE)SendMessage((hWnd), TNM_GETFLATNODE, (WPARAM)(Index), 0)) - -#define TreeNew_GetCellText(hWnd, GetCellText) \ - SendMessage((hWnd), TNM_GETCELLTEXT, 0, (LPARAM)(GetCellText)) - -#define TreeNew_SetNodeExpanded(hWnd, Node, Expanded) \ - SendMessage((hWnd), TNM_SETNODEEXPANDED, (WPARAM)(Expanded), (LPARAM)(Node)) - -#define TreeNew_GetMaxId(hWnd) \ - ((ULONG)SendMessage((hWnd), TNM_GETMAXID, 0, 0)) - -#define TreeNew_SetMaxId(hWnd, MaxId) \ - SendMessage((hWnd), TNM_SETMAXID, (WPARAM)(MaxId), 0) - -#define TreeNew_InvalidateNode(hWnd, Node) \ - SendMessage((hWnd), TNM_INVALIDATENODE, 0, (LPARAM)(Node)) - -#define TreeNew_InvalidateNodes(hWnd, Start, End) \ - SendMessage((hWnd), TNM_INVALIDATENODES, (WPARAM)(Start), (LPARAM)(End)) - -#define TreeNew_GetFixedHeader(hWnd) \ - ((HWND)SendMessage((hWnd), TNM_GETFIXEDHEADER, 0, 0)) - -#define TreeNew_GetHeader(hWnd) \ - ((HWND)SendMessage((hWnd), TNM_GETHEADER, 0, 0)) - -#define TreeNew_GetTooltips(hWnd) \ - ((HWND)SendMessage((hWnd), TNM_GETTOOLTIPS, 0, 0)) - -#define TreeNew_SelectRange(hWnd, Start, End) \ - SendMessage((hWnd), TNM_SELECTRANGE, (WPARAM)(Start), (LPARAM)(End)) - -#define TreeNew_DeselectRange(hWnd, Start, End) \ - SendMessage((hWnd), TNM_DESELECTRANGE, (WPARAM)(Start), (LPARAM)(End)) - -#define TreeNew_GetColumnCount(hWnd) \ - ((ULONG)SendMessage((hWnd), TNM_GETCOLUMNCOUNT, 0, 0)) - -#define TreeNew_SetRedraw(hWnd, Redraw) \ - ((LONG)SendMessage((hWnd), TNM_SETREDRAW, (WPARAM)(Redraw), 0)) - -#define TreeNew_GetViewParts(hWnd, Parts) \ - SendMessage((hWnd), TNM_GETVIEWPARTS, 0, (LPARAM)(Parts)) - -#define TreeNew_GetFixedColumn(hWnd) \ - ((PPH_TREENEW_COLUMN)SendMessage((hWnd), TNM_GETFIXEDCOLUMN, 0, 0)) - -#define TreeNew_GetFirstColumn(hWnd) \ - ((PPH_TREENEW_COLUMN)SendMessage((hWnd), TNM_GETFIRSTCOLUMN, 0, 0)) - -#define TreeNew_SetFocusNode(hWnd, Node) \ - SendMessage((hWnd), TNM_SETFOCUSNODE, 0, (LPARAM)(Node)) - -#define TreeNew_SetMarkNode(hWnd, Node) \ - SendMessage((hWnd), TNM_SETMARKNODE, 0, (LPARAM)(Node)) - -#define TreeNew_SetHotNode(hWnd, Node) \ - SendMessage((hWnd), TNM_SETHOTNODE, 0, (LPARAM)(Node)) - -#define TreeNew_SetExtendedFlags(hWnd, Mask, Value) \ - SendMessage((hWnd), TNM_SETEXTENDEDFLAGS, (WPARAM)(Mask), (LPARAM)(Value)) - -#define TreeNew_GetCallback(hWnd, Callback, Context) \ - SendMessage((hWnd), TNM_GETCALLBACK, (WPARAM)(Context), (LPARAM)(Callback)) - -#define TreeNew_HitTest(hWnd, HitTest) \ - SendMessage((hWnd), TNM_HITTEST, 0, (LPARAM)(HitTest)) - -#define TreeNew_GetVisibleColumnCount(hWnd) \ - ((ULONG)SendMessage((hWnd), TNM_GETVISIBLECOLUMNCOUNT, 0, 0)) - -#define TreeNew_AutoSizeColumn(hWnd, Id, Flags) \ - SendMessage((hWnd), TNM_AUTOSIZECOLUMN, (WPARAM)(Id), (LPARAM)(Flags)) - -#define TreeNew_SetEmptyText(hWnd, Text, Flags) \ - SendMessage((hWnd), TNM_SETEMPTYTEXT, (WPARAM)(Flags), (LPARAM)(Text)) - -#define TreeNew_SetRowHeight(hWnd, RowHeight) \ - SendMessage((hWnd), TNM_SETROWHEIGHT, (WPARAM)(RowHeight), 0) - -#define TreeNew_IsFlatNodeValid(hWnd) \ - ((BOOLEAN)SendMessage((hWnd), TNM_ISFLATNODEVALID, 0, 0)) - -typedef struct _PH_TREENEW_VIEW_PARTS -{ - RECT ClientRect; - LONG HeaderHeight; - LONG RowHeight; - ULONG VScrollWidth; - ULONG HScrollHeight; - LONG VScrollPosition; - LONG HScrollPosition; - LONG FixedWidth; - LONG NormalLeft; - LONG NormalWidth; -} PH_TREENEW_VIEW_PARTS, *PPH_TREENEW_VIEW_PARTS; - -PHLIBAPI -BOOLEAN PhTreeNewInitialization( - VOID - ); - -FORCEINLINE VOID PhInitializeTreeNewNode( - _In_ PPH_TREENEW_NODE Node - ) -{ - memset(Node, 0, sizeof(PH_TREENEW_NODE)); - - Node->Visible = TRUE; - Node->Expanded = TRUE; -} - -FORCEINLINE VOID PhInvalidateTreeNewNode( - _Inout_ PPH_TREENEW_NODE Node, - _In_ ULONG Flags - ) -{ - if (Flags & TN_CACHE_COLOR) - Node->s.CachedColorValid = FALSE; - if (Flags & TN_CACHE_FONT) - Node->s.CachedFontValid = FALSE; - if (Flags & TN_CACHE_ICON) - Node->s.CachedIconValid = FALSE; -} - -FORCEINLINE BOOLEAN PhAddTreeNewColumn( - _In_ HWND hwnd, - _In_ ULONG Id, - _In_ BOOLEAN Visible, - _In_ PWSTR Text, - _In_ ULONG Width, - _In_ ULONG Alignment, - _In_ ULONG DisplayIndex, - _In_ ULONG TextFlags - ) -{ - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Id = Id; - column.Visible = Visible; - column.Text = Text; - column.Width = Width; - column.Alignment = Alignment; - column.DisplayIndex = DisplayIndex; - column.TextFlags = TextFlags; - column.DpiScaleOnAdd = TRUE; - - if (DisplayIndex == -2) - column.Fixed = TRUE; - - return !!TreeNew_AddColumn(hwnd, &column); -} - -FORCEINLINE BOOLEAN PhAddTreeNewColumnEx( - _In_ HWND hwnd, - _In_ ULONG Id, - _In_ BOOLEAN Visible, - _In_ PWSTR Text, - _In_ ULONG Width, - _In_ ULONG Alignment, - _In_ ULONG DisplayIndex, - _In_ ULONG TextFlags, - _In_ BOOLEAN SortDescending - ) -{ - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Id = Id; - column.Visible = Visible; - column.Text = Text; - column.Width = Width; - column.Alignment = Alignment; - column.DisplayIndex = DisplayIndex; - column.TextFlags = TextFlags; - column.DpiScaleOnAdd = TRUE; - - if (DisplayIndex == -2) - column.Fixed = TRUE; - if (SortDescending) - column.SortDescending = TRUE; - - return !!TreeNew_AddColumn(hwnd, &column); -} - -FORCEINLINE BOOLEAN PhAddTreeNewColumnEx2( - _In_ HWND hwnd, - _In_ ULONG Id, - _In_ BOOLEAN Visible, - _In_ PWSTR Text, - _In_ ULONG Width, - _In_ ULONG Alignment, - _In_ ULONG DisplayIndex, - _In_ ULONG TextFlags, - _In_ ULONG ExtraFlags - ) -{ - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Id = Id; - column.Visible = Visible; - column.Text = Text; - column.Width = Width; - column.Alignment = Alignment; - column.DisplayIndex = DisplayIndex; - column.TextFlags = TextFlags; - - if (DisplayIndex == -2) - column.Fixed = TRUE; - if (ExtraFlags & TN_COLUMN_FLAG_CUSTOMDRAW) - column.CustomDraw = TRUE; - if (ExtraFlags & TN_COLUMN_FLAG_SORTDESCENDING) - column.SortDescending = TRUE; - if (!(ExtraFlags & TN_COLUMN_FLAG_NODPISCALEONADD)) - column.DpiScaleOnAdd = TRUE; - - return !!TreeNew_AddColumn(hwnd, &column); -} - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_TREENEW_H +#define _PH_TREENEW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_TREENEW_CLASSNAME L"PhTreeNew" + +#define PH_TREENEW_SEARCH_TIMEOUT 1000 +#define PH_TREENEW_SEARCH_MAXIMUM_LENGTH 1023 + +typedef struct _PH_TREENEW_CREATEPARAMS +{ + COLORREF TextColor; + COLORREF FocusColor; + COLORREF SelectionColor; + // Add new fields here. +} PH_TREENEW_CREATEPARAMS, *PPH_TREENEW_CREATEPARAMS; + +typedef struct _PH_TREENEW_COLUMN +{ + union + { + ULONG Flags; + struct + { + ULONG Visible : 1; + ULONG CustomDraw : 1; + ULONG Fixed : 1; // Whether this is the fixed column + ULONG SortDescending : 1; // Sort descending on initial click rather than ascending + ULONG DpiScaleOnAdd : 1; // Whether to DPI scale the width (only when adding) + ULONG SpareFlags : 27; + }; + }; + ULONG Id; + PVOID Context; + PWSTR Text; + LONG Width; + ULONG Alignment; + ULONG DisplayIndex; // -1 for fixed column or invalid + + ULONG TextFlags; + + struct + { + LONG ViewIndex; // Actual index in header control + LONG ViewX; // 0 for the fixed column, and an offset from the divider for normal columns + } s; +} PH_TREENEW_COLUMN, *PPH_TREENEW_COLUMN; + +typedef struct _PH_TREENEW_NODE +{ + union + { + ULONG Flags; + struct + { + ULONG Visible : 1; + ULONG Selected : 1; + ULONG Expanded : 1; + ULONG UseAutoForeColor : 1; + ULONG UseTempBackColor : 1; + ULONG Unselectable : 1; + ULONG SpareFlags : 26; + }; + }; + + COLORREF BackColor; + COLORREF ForeColor; + COLORREF TempBackColor; + HFONT Font; + HICON Icon; + + PPH_STRINGREF TextCache; + ULONG TextCacheSize; + + ULONG Index; // Index within the flat list + ULONG Level; // 0 for root, 1, 2, ... + + struct + { + union + { + ULONG Flags2; + struct + { + ULONG IsLeaf : 1; + ULONG CachedColorValid : 1; + ULONG CachedFontValid : 1; + ULONG CachedIconValid : 1; + ULONG PlusMinusHot : 1; + ULONG SpareFlags2 : 27; + }; + }; + + // Temp. drawing data + COLORREF DrawBackColor; + COLORREF DrawForeColor; + } s; +} PH_TREENEW_NODE, *PPH_TREENEW_NODE; + +// Styles +#define TN_STYLE_ICONS 0x1 +#define TN_STYLE_DOUBLE_BUFFERED 0x2 +#define TN_STYLE_NO_DIVIDER 0x4 +#define TN_STYLE_ANIMATE_DIVIDER 0x8 +#define TN_STYLE_NO_COLUMN_SORT 0x10 +#define TN_STYLE_NO_COLUMN_REORDER 0x20 +#define TN_STYLE_THIN_ROWS 0x40 +#define TN_STYLE_NO_COLUMN_HEADER 0x80 +#define TN_STYLE_CUSTOM_COLORS 0x100 +#define TN_STYLE_ALWAYS_SHOW_SELECTION 0x200 + +// Extended flags +#define TN_FLAG_ITEM_DRAG_SELECT 0x1 +#define TN_FLAG_NO_UNFOLDING_TOOLTIPS 0x2 + +// Callback flags +#define TN_CACHE 0x1 +#define TN_AUTO_FORECOLOR 0x1000 + +// Column change flags +#define TN_COLUMN_CONTEXT 0x1 +#define TN_COLUMN_TEXT 0x2 +#define TN_COLUMN_WIDTH 0x4 +#define TN_COLUMN_ALIGNMENT 0x8 +#define TN_COLUMN_DISPLAYINDEX 0x10 +#define TN_COLUMN_TEXTFLAGS 0x20 +#define TN_COLUMN_FLAG_VISIBLE 0x100000 +#define TN_COLUMN_FLAG_CUSTOMDRAW 0x200000 +#define TN_COLUMN_FLAG_FIXED 0x400000 +#define TN_COLUMN_FLAG_SORTDESCENDING 0x800000 +#define TN_COLUMN_FLAG_NODPISCALEONADD 0x1000000 +#define TN_COLUMN_FLAGS 0xfff00000 + +// Cache flags +#define TN_CACHE_COLOR 0x1 +#define TN_CACHE_FONT 0x2 +#define TN_CACHE_ICON 0x4 + +// Cell part input flags +#define TN_MEASURE_TEXT 0x1 + +// Cell part flags +#define TN_PART_CELL 0x1 +#define TN_PART_PLUSMINUS 0x2 +#define TN_PART_ICON 0x4 +#define TN_PART_CONTENT 0x8 +#define TN_PART_TEXT 0x10 + +// Hit test input flags +#define TN_TEST_COLUMN 0x1 +#define TN_TEST_SUBITEM 0x2 // requires TN_TEST_COLUMN + +// Hit test flags +#define TN_HIT_LEFT 0x1 +#define TN_HIT_RIGHT 0x2 +#define TN_HIT_ABOVE 0x4 +#define TN_HIT_BELOW 0x8 +#define TN_HIT_ITEM 0x10 +#define TN_HIT_ITEM_PLUSMINUS 0x20 // requires TN_TEST_SUBITEM +#define TN_HIT_ITEM_ICON 0x40 // requires TN_TEST_SUBITEM +#define TN_HIT_ITEM_CONTENT 0x80 // requires TN_TEST_SUBITEM +#define TN_HIT_DIVIDER 0x100 + +// Selection flags +#define TN_SELECT_DESELECT 0x1 +#define TN_SELECT_TOGGLE 0x2 +#define TN_SELECT_RESET 0x4 + +// Auto-size flags +#define TN_AUTOSIZE_REMAINING_SPACE 0x1 + +typedef struct _PH_TREENEW_CELL_PARTS +{ + ULONG Flags; + RECT RowRect; + RECT CellRect; // TN_PART_CELL + RECT PlusMinusRect; // TN_PART_PLUSMINUS + RECT IconRect; // TN_PART_ICON + RECT ContentRect; // TN_PART_CONTENT + RECT TextRect; // TN_PART_TEXT + PH_STRINGREF Text; // TN_PART_TEXT + HFONT Font; // TN_PART_TEXT +} PH_TREENEW_CELL_PARTS, *PPH_TREENEW_CELL_PARTS; + +typedef struct _PH_TREENEW_HIT_TEST +{ + POINT Point; + ULONG InFlags; + + ULONG Flags; + PPH_TREENEW_NODE Node; + PPH_TREENEW_COLUMN Column; // requires TN_TEST_COLUMN +} PH_TREENEW_HIT_TEST, *PPH_TREENEW_HIT_TEST; + +typedef enum _PH_TREENEW_MESSAGE +{ + TreeNewGetChildren, // PPH_TREENEW_GET_CHILDREN Parameter1 + TreeNewIsLeaf, // PPH_TREENEW_IS_LEAF Parameter1 + TreeNewGetCellText, // PPH_TREENEW_GET_CELL_TEXT Parameter1 + TreeNewGetNodeColor, // PPH_TREENEW_GET_NODE_COLOR Parameter1 + TreeNewGetNodeFont, // PPH_TREENEW_GET_NODE_FONT Parameter1 + TreeNewGetNodeIcon, // PPH_TREENEW_GET_NODE_ICON Parameter1 + TreeNewGetCellTooltip, // PPH_TREENEW_GET_CELL_TOOLTIP Parameter1 + TreeNewCustomDraw, // PPH_TREENEW_CUSTOM_DRAW Parameter1 + + // Notifications + TreeNewNodeExpanding, // PPH_TREENEW_NODE Parameter1, PPH_TREENEW_NODE_EVENT Parameter2 + TreeNewNodeSelecting, // PPH_TREENEW_NODE Parameter1 + + TreeNewSortChanged, + TreeNewSelectionChanged, + + TreeNewKeyDown, // PPH_TREENEW_KEY_EVENT Parameter1 + TreeNewLeftClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 + TreeNewRightClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 + TreeNewLeftDoubleClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 + TreeNewRightDoubleClick, // PPH_TREENEW_MOUSE_EVENT Parameter1 + TreeNewContextMenu, // PPH_TREENEW_CONTEXT_MENU Parameter1 + + TreeNewHeaderRightClick, // PPH_TREENEW_HEADER_MOUSE_EVENT Parameter1 + TreeNewIncrementalSearch, // PPH_TREENEW_SEARCH_EVENT Parameter1 + + TreeNewColumnResized, // PPH_TREENEW_COLUMN Parameter1 + TreeNewColumnReordered, + + TreeNewDestroying, + TreeNewGetDialogCode, // ULONG Parameter1, PULONG Parameter2 + + MaxTreeNewMessage +} PH_TREENEW_MESSAGE; + +typedef BOOLEAN (NTAPI *PPH_TREENEW_CALLBACK)( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +typedef struct _PH_TREENEW_GET_CHILDREN +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + + ULONG NumberOfChildren; + PPH_TREENEW_NODE *Children; // can be NULL if no children +} PH_TREENEW_GET_CHILDREN, *PPH_TREENEW_GET_CHILDREN; + +typedef struct _PH_TREENEW_IS_LEAF +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + + BOOLEAN IsLeaf; +} PH_TREENEW_IS_LEAF, *PPH_TREENEW_IS_LEAF; + +typedef struct _PH_TREENEW_GET_CELL_TEXT +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + ULONG Id; + + PH_STRINGREF Text; +} PH_TREENEW_GET_CELL_TEXT, *PPH_TREENEW_GET_CELL_TEXT; + +typedef struct _PH_TREENEW_GET_NODE_COLOR +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + + COLORREF BackColor; + COLORREF ForeColor; +} PH_TREENEW_GET_NODE_COLOR, *PPH_TREENEW_GET_NODE_COLOR; + +typedef struct _PH_TREENEW_GET_NODE_FONT +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + + HFONT Font; +} PH_TREENEW_GET_NODE_FONT, *PPH_TREENEW_GET_NODE_FONT; + +typedef struct _PH_TREENEW_GET_NODE_ICON +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + + HICON Icon; +} PH_TREENEW_GET_NODE_ICON, *PPH_TREENEW_GET_NODE_ICON; + +typedef struct _PH_TREENEW_GET_CELL_TOOLTIP +{ + ULONG Flags; + PPH_TREENEW_NODE Node; + PPH_TREENEW_COLUMN Column; + + BOOLEAN Unfolding; + PH_STRINGREF Text; + HFONT Font; + ULONG MaximumWidth; +} PH_TREENEW_GET_CELL_TOOLTIP, *PPH_TREENEW_GET_CELL_TOOLTIP; + +typedef struct _PH_TREENEW_CUSTOM_DRAW +{ + PPH_TREENEW_NODE Node; + PPH_TREENEW_COLUMN Column; + + HDC Dc; + RECT CellRect; + RECT TextRect; +} PH_TREENEW_CUSTOM_DRAW, *PPH_TREENEW_CUSTOM_DRAW; + +typedef struct _PH_TREENEW_MOUSE_EVENT +{ + POINT Location; + PPH_TREENEW_NODE Node; + PPH_TREENEW_COLUMN Column; + ULONG KeyFlags; +} PH_TREENEW_MOUSE_EVENT, *PPH_TREENEW_MOUSE_EVENT; + +typedef struct _PH_TREENEW_KEY_EVENT +{ + BOOLEAN Handled; + ULONG VirtualKey; + ULONG Data; +} PH_TREENEW_KEY_EVENT, *PPH_TREENEW_KEY_EVENT; + +typedef struct _PH_TREENEW_NODE_EVENT +{ + BOOLEAN Handled; + ULONG Flags; + PVOID Reserved1; + PVOID Reserved2; +} PH_TREENEW_NODE_EVENT, *PPH_TREENEW_NODE_EVENT; + +typedef struct _PH_TREENEW_CONTEXT_MENU +{ + POINT Location; + POINT ClientLocation; + PPH_TREENEW_NODE Node; + PPH_TREENEW_COLUMN Column; + BOOLEAN KeyboardInvoked; +} PH_TREENEW_CONTEXT_MENU, *PPH_TREENEW_CONTEXT_MENU; + +typedef struct _PH_TREENEW_HEADER_MOUSE_EVENT +{ + POINT ScreenLocation; + POINT Location; + POINT HeaderLocation; + PPH_TREENEW_COLUMN Column; +} PH_TREENEW_HEADER_MOUSE_EVENT, *PPH_TREENEW_HEADER_MOUSE_EVENT; + +typedef struct _PH_TREENEW_SEARCH_EVENT +{ + LONG FoundIndex; + LONG StartIndex; + PH_STRINGREF String; +} PH_TREENEW_SEARCH_EVENT, *PPH_TREENEW_SEARCH_EVENT; + +#define TNM_FIRST (WM_USER + 1) +#define TNM_SETCALLBACK (WM_USER + 1) +#define TNM_NODESADDED (WM_USER + 2) // unimplemented +#define TNM_NODESREMOVED (WM_USER + 3) // unimplemented +#define TNM_NODESSTRUCTURED (WM_USER + 4) +#define TNM_ADDCOLUMN (WM_USER + 5) +#define TNM_REMOVECOLUMN (WM_USER + 6) +#define TNM_GETCOLUMN (WM_USER + 7) +#define TNM_SETCOLUMN (WM_USER + 8) +#define TNM_GETCOLUMNORDERARRAY (WM_USER + 9) +#define TNM_SETCOLUMNORDERARRAY (WM_USER + 10) +#define TNM_SETCURSOR (WM_USER + 11) +#define TNM_GETSORT (WM_USER + 12) +#define TNM_SETSORT (WM_USER + 13) +#define TNM_SETTRISTATE (WM_USER + 14) +#define TNM_ENSUREVISIBLE (WM_USER + 15) +#define TNM_SCROLL (WM_USER + 16) +#define TNM_GETFLATNODECOUNT (WM_USER + 17) +#define TNM_GETFLATNODE (WM_USER + 18) +#define TNM_GETCELLTEXT (WM_USER + 19) +#define TNM_SETNODEEXPANDED (WM_USER + 20) +#define TNM_GETMAXID (WM_USER + 21) +#define TNM_SETMAXID (WM_USER + 22) +#define TNM_INVALIDATENODE (WM_USER + 23) +#define TNM_INVALIDATENODES (WM_USER + 24) +#define TNM_GETFIXEDHEADER (WM_USER + 25) +#define TNM_GETHEADER (WM_USER + 26) +#define TNM_GETTOOLTIPS (WM_USER + 27) +#define TNM_SELECTRANGE (WM_USER + 28) +#define TNM_DESELECTRANGE (WM_USER + 29) +#define TNM_GETCOLUMNCOUNT (WM_USER + 30) +#define TNM_SETREDRAW (WM_USER + 31) +#define TNM_GETVIEWPARTS (WM_USER + 32) +#define TNM_GETFIXEDCOLUMN (WM_USER + 33) +#define TNM_GETFIRSTCOLUMN (WM_USER + 34) +#define TNM_SETFOCUSNODE (WM_USER + 35) +#define TNM_SETMARKNODE (WM_USER + 36) +#define TNM_SETHOTNODE (WM_USER + 37) +#define TNM_SETEXTENDEDFLAGS (WM_USER + 38) +#define TNM_GETCALLBACK (WM_USER + 39) +#define TNM_HITTEST (WM_USER + 40) +#define TNM_GETVISIBLECOLUMNCOUNT (WM_USER + 41) +#define TNM_AUTOSIZECOLUMN (WM_USER + 42) +#define TNM_SETEMPTYTEXT (WM_USER + 43) +#define TNM_SETROWHEIGHT (WM_USER + 44) +#define TNM_ISFLATNODEVALID (WM_USER + 45) +#define TNM_THEMESUPPORT (WM_USER + 46) +#define TNM_LAST (WM_USER + 47) + +#define TreeNew_SetCallback(hWnd, Callback, Context) \ + SendMessage((hWnd), TNM_SETCALLBACK, (WPARAM)(Context), (LPARAM)(Callback)) + +#define TreeNew_NodesStructured(hWnd) \ + SendMessage((hWnd), TNM_NODESSTRUCTURED, 0, 0) + +#define TreeNew_AddColumn(hWnd, Column) \ + SendMessage((hWnd), TNM_ADDCOLUMN, 0, (LPARAM)(Column)) + +#define TreeNew_RemoveColumn(hWnd, Id) \ + SendMessage((hWnd), TNM_REMOVECOLUMN, (WPARAM)(Id), 0) + +#define TreeNew_GetColumn(hWnd, Id, Column) \ + SendMessage((hWnd), TNM_GETCOLUMN, (WPARAM)(Id), (LPARAM)(Column)) + +#define TreeNew_SetColumn(hWnd, Mask, Column) \ + SendMessage((hWnd), TNM_SETCOLUMN, (WPARAM)(Mask), (LPARAM)(Column)) + +#define TreeNew_GetColumnOrderArray(hWnd, Count, Array) \ + SendMessage((hWnd), TNM_GETCOLUMNORDERARRAY, (WPARAM)(Count), (LPARAM)(Array)) + +#define TreeNew_SetColumnOrderArray(hWnd, Count, Array) \ + SendMessage((hWnd), TNM_SETCOLUMNORDERARRAY, (WPARAM)(Count), (LPARAM)(Array)) + +#define TreeNew_SetCursor(hWnd, Cursor) \ + SendMessage((hWnd), TNM_SETCURSOR, 0, (LPARAM)(Cursor)) + +#define TreeNew_GetSort(hWnd, Column, Order) \ + SendMessage((hWnd), TNM_GETSORT, (WPARAM)(Column), (LPARAM)(Order)) + +#define TreeNew_SetSort(hWnd, Column, Order) \ + SendMessage((hWnd), TNM_SETSORT, (WPARAM)(Column), (LPARAM)(Order)) + +#define TreeNew_SetTriState(hWnd, TriState) \ + SendMessage((hWnd), TNM_SETTRISTATE, (WPARAM)(TriState), 0) + +#define TreeNew_EnsureVisible(hWnd, Node) \ + SendMessage((hWnd), TNM_ENSUREVISIBLE, 0, (LPARAM)(Node)) + +#define TreeNew_Scroll(hWnd, DeltaRows, DeltaX) \ + SendMessage((hWnd), TNM_SCROLL, (WPARAM)(DeltaRows), (LPARAM)(DeltaX)) + +#define TreeNew_GetFlatNodeCount(hWnd) \ + ((ULONG)SendMessage((hWnd), TNM_GETFLATNODECOUNT, 0, 0)) + +#define TreeNew_GetFlatNode(hWnd, Index) \ + ((PPH_TREENEW_NODE)SendMessage((hWnd), TNM_GETFLATNODE, (WPARAM)(Index), 0)) + +#define TreeNew_GetCellText(hWnd, GetCellText) \ + SendMessage((hWnd), TNM_GETCELLTEXT, 0, (LPARAM)(GetCellText)) + +#define TreeNew_SetNodeExpanded(hWnd, Node, Expanded) \ + SendMessage((hWnd), TNM_SETNODEEXPANDED, (WPARAM)(Expanded), (LPARAM)(Node)) + +#define TreeNew_GetMaxId(hWnd) \ + ((ULONG)SendMessage((hWnd), TNM_GETMAXID, 0, 0)) + +#define TreeNew_SetMaxId(hWnd, MaxId) \ + SendMessage((hWnd), TNM_SETMAXID, (WPARAM)(MaxId), 0) + +#define TreeNew_InvalidateNode(hWnd, Node) \ + SendMessage((hWnd), TNM_INVALIDATENODE, 0, (LPARAM)(Node)) + +#define TreeNew_InvalidateNodes(hWnd, Start, End) \ + SendMessage((hWnd), TNM_INVALIDATENODES, (WPARAM)(Start), (LPARAM)(End)) + +#define TreeNew_GetFixedHeader(hWnd) \ + ((HWND)SendMessage((hWnd), TNM_GETFIXEDHEADER, 0, 0)) + +#define TreeNew_GetHeader(hWnd) \ + ((HWND)SendMessage((hWnd), TNM_GETHEADER, 0, 0)) + +#define TreeNew_GetTooltips(hWnd) \ + ((HWND)SendMessage((hWnd), TNM_GETTOOLTIPS, 0, 0)) + +#define TreeNew_SelectRange(hWnd, Start, End) \ + SendMessage((hWnd), TNM_SELECTRANGE, (WPARAM)(Start), (LPARAM)(End)) + +#define TreeNew_DeselectRange(hWnd, Start, End) \ + SendMessage((hWnd), TNM_DESELECTRANGE, (WPARAM)(Start), (LPARAM)(End)) + +#define TreeNew_GetColumnCount(hWnd) \ + ((ULONG)SendMessage((hWnd), TNM_GETCOLUMNCOUNT, 0, 0)) + +#define TreeNew_SetRedraw(hWnd, Redraw) \ + ((LONG)SendMessage((hWnd), TNM_SETREDRAW, (WPARAM)(Redraw), 0)) + +#define TreeNew_GetViewParts(hWnd, Parts) \ + SendMessage((hWnd), TNM_GETVIEWPARTS, 0, (LPARAM)(Parts)) + +#define TreeNew_GetFixedColumn(hWnd) \ + ((PPH_TREENEW_COLUMN)SendMessage((hWnd), TNM_GETFIXEDCOLUMN, 0, 0)) + +#define TreeNew_GetFirstColumn(hWnd) \ + ((PPH_TREENEW_COLUMN)SendMessage((hWnd), TNM_GETFIRSTCOLUMN, 0, 0)) + +#define TreeNew_SetFocusNode(hWnd, Node) \ + SendMessage((hWnd), TNM_SETFOCUSNODE, 0, (LPARAM)(Node)) + +#define TreeNew_SetMarkNode(hWnd, Node) \ + SendMessage((hWnd), TNM_SETMARKNODE, 0, (LPARAM)(Node)) + +#define TreeNew_SetHotNode(hWnd, Node) \ + SendMessage((hWnd), TNM_SETHOTNODE, 0, (LPARAM)(Node)) + +#define TreeNew_SetExtendedFlags(hWnd, Mask, Value) \ + SendMessage((hWnd), TNM_SETEXTENDEDFLAGS, (WPARAM)(Mask), (LPARAM)(Value)) + +#define TreeNew_GetCallback(hWnd, Callback, Context) \ + SendMessage((hWnd), TNM_GETCALLBACK, (WPARAM)(Context), (LPARAM)(Callback)) + +#define TreeNew_HitTest(hWnd, HitTest) \ + SendMessage((hWnd), TNM_HITTEST, 0, (LPARAM)(HitTest)) + +#define TreeNew_GetVisibleColumnCount(hWnd) \ + ((ULONG)SendMessage((hWnd), TNM_GETVISIBLECOLUMNCOUNT, 0, 0)) + +#define TreeNew_AutoSizeColumn(hWnd, Id, Flags) \ + SendMessage((hWnd), TNM_AUTOSIZECOLUMN, (WPARAM)(Id), (LPARAM)(Flags)) + +#define TreeNew_SetEmptyText(hWnd, Text, Flags) \ + SendMessage((hWnd), TNM_SETEMPTYTEXT, (WPARAM)(Flags), (LPARAM)(Text)) + +#define TreeNew_SetRowHeight(hWnd, RowHeight) \ + SendMessage((hWnd), TNM_SETROWHEIGHT, (WPARAM)(RowHeight), 0) + +#define TreeNew_IsFlatNodeValid(hWnd) \ + ((BOOLEAN)SendMessage((hWnd), TNM_ISFLATNODEVALID, 0, 0)) + +#define TreeNew_ThemeSupport(hWnd, Enable) \ + SendMessage((hWnd), TNM_THEMESUPPORT, (WPARAM)(Enable), 0); + +typedef struct _PH_TREENEW_VIEW_PARTS +{ + RECT ClientRect; + LONG HeaderHeight; + LONG RowHeight; + ULONG VScrollWidth; + ULONG HScrollHeight; + LONG VScrollPosition; + LONG HScrollPosition; + LONG FixedWidth; + LONG NormalLeft; + LONG NormalWidth; +} PH_TREENEW_VIEW_PARTS, *PPH_TREENEW_VIEW_PARTS; + +PHLIBAPI +BOOLEAN PhTreeNewInitialization( + VOID + ); + +FORCEINLINE VOID PhInitializeTreeNewNode( + _In_ PPH_TREENEW_NODE Node + ) +{ + memset(Node, 0, sizeof(PH_TREENEW_NODE)); + + Node->Visible = TRUE; + Node->Expanded = TRUE; +} + +FORCEINLINE VOID PhInvalidateTreeNewNode( + _Inout_ PPH_TREENEW_NODE Node, + _In_ ULONG Flags + ) +{ + if (Flags & TN_CACHE_COLOR) + Node->s.CachedColorValid = FALSE; + if (Flags & TN_CACHE_FONT) + Node->s.CachedFontValid = FALSE; + if (Flags & TN_CACHE_ICON) + Node->s.CachedIconValid = FALSE; +} + +FORCEINLINE BOOLEAN PhAddTreeNewColumn( + _In_ HWND hwnd, + _In_ ULONG Id, + _In_ BOOLEAN Visible, + _In_ PWSTR Text, + _In_ ULONG Width, + _In_ ULONG Alignment, + _In_ ULONG DisplayIndex, + _In_ ULONG TextFlags + ) +{ + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Id = Id; + column.Visible = Visible; + column.Text = Text; + column.Width = Width; + column.Alignment = Alignment; + column.DisplayIndex = DisplayIndex; + column.TextFlags = TextFlags; + column.DpiScaleOnAdd = TRUE; + + if (DisplayIndex == -2) + column.Fixed = TRUE; + + return !!TreeNew_AddColumn(hwnd, &column); +} + +FORCEINLINE BOOLEAN PhAddTreeNewColumnEx( + _In_ HWND hwnd, + _In_ ULONG Id, + _In_ BOOLEAN Visible, + _In_ PWSTR Text, + _In_ ULONG Width, + _In_ ULONG Alignment, + _In_ ULONG DisplayIndex, + _In_ ULONG TextFlags, + _In_ BOOLEAN SortDescending + ) +{ + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Id = Id; + column.Visible = Visible; + column.Text = Text; + column.Width = Width; + column.Alignment = Alignment; + column.DisplayIndex = DisplayIndex; + column.TextFlags = TextFlags; + column.DpiScaleOnAdd = TRUE; + + if (DisplayIndex == -2) + column.Fixed = TRUE; + if (SortDescending) + column.SortDescending = TRUE; + + return !!TreeNew_AddColumn(hwnd, &column); +} + +FORCEINLINE BOOLEAN PhAddTreeNewColumnEx2( + _In_ HWND hwnd, + _In_ ULONG Id, + _In_ BOOLEAN Visible, + _In_ PWSTR Text, + _In_ ULONG Width, + _In_ ULONG Alignment, + _In_ ULONG DisplayIndex, + _In_ ULONG TextFlags, + _In_ ULONG ExtraFlags + ) +{ + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Id = Id; + column.Visible = Visible; + column.Text = Text; + column.Width = Width; + column.Alignment = Alignment; + column.DisplayIndex = DisplayIndex; + column.TextFlags = TextFlags; + + if (DisplayIndex == -2) + column.Fixed = TRUE; + if (ExtraFlags & TN_COLUMN_FLAG_CUSTOMDRAW) + column.CustomDraw = TRUE; + if (ExtraFlags & TN_COLUMN_FLAG_SORTDESCENDING) + column.SortDescending = TRUE; + if (!(ExtraFlags & TN_COLUMN_FLAG_NODPISCALEONADD)) + column.DpiScaleOnAdd = TRUE; + + return !!TreeNew_AddColumn(hwnd, &column); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/treenewp.h b/phlib/include/treenewp.h index b8f1d8472ae3..03d798c876d5 100644 --- a/phlib/include/treenewp.h +++ b/phlib/include/treenewp.h @@ -1,799 +1,810 @@ -#ifndef _PH_TREENEWP_H -#define _PH_TREENEWP_H - -// Important notes about pointers: -// -// All memory allocation for nodes and strings is handled by the user. This usually means there is a -// very limited time during which they can be safely accessed. -// -// Node pointers are valid through the duration of message processing, and also up to the next -// restructure operation, either user- or control- initiated. This means that state such as the -// focused node, hot node and mark node must be carefully preserved through restructuring. If -// restructuring is suspended by a set-redraw call, all nodes must be considered invalid and no user -// input can be handled. -// -// Strings are valid only through the duration of message processing. - -typedef struct _PH_TREENEW_CONTEXT -{ - HWND Handle; - PVOID InstanceHandle; - HWND FixedHeaderHandle; - HWND HeaderHandle; - HWND VScrollHandle; - HWND HScrollHandle; - HWND FillerBoxHandle; - HWND TooltipsHandle; - - union - { - struct - { - ULONG FontOwned : 1; - ULONG Tracking : 1; // tracking for fixed divider - ULONG VScrollVisible : 1; - ULONG HScrollVisible : 1; - ULONG FixedColumnVisible : 1; - ULONG FixedDividerVisible : 1; - ULONG AnimateDivider : 1; - ULONG AnimateDividerFadingIn : 1; - ULONG AnimateDividerFadingOut : 1; - ULONG CanAnyExpand : 1; - ULONG TriState : 1; - ULONG HasFocus : 1; - ULONG ThemeInitialized : 1; // delay load theme data - ULONG ThemeActive : 1; - ULONG ThemeHasItemBackground : 1; - ULONG ThemeHasGlyph : 1; - ULONG ThemeHasHotGlyph : 1; - ULONG FocusNodeFound : 1; // used to preserve the focused node across restructuring - ULONG SearchFailed : 1; // used to prevent multiple beeps - ULONG SearchSingleCharMode : 1; // LV style single-character search - ULONG TooltipUnfolding : 1; // whether the current tooltip is unfolding - ULONG DoubleBuffered : 1; - ULONG SuspendUpdateStructure : 1; - ULONG SuspendUpdateLayout : 1; - ULONG SuspendUpdateMoveMouse : 1; - ULONG DragSelectionActive : 1; - ULONG SelectionRectangleAlpha : 1; // use alpha blending for the selection rectangle - ULONG CustomRowHeight : 1; - ULONG Spare : 4; - }; - ULONG Flags; - }; - ULONG Style; - ULONG ExtendedStyle; - ULONG ExtendedFlags; - - HFONT Font; - HCURSOR Cursor; - HCURSOR DividerCursor; - - RECT ClientRect; - LONG HeaderHeight; - LONG RowHeight; - ULONG VScrollWidth; - ULONG HScrollHeight; - LONG VScrollPosition; - LONG HScrollPosition; - LONG FixedWidth; // width of the fixed part of the tree list - LONG FixedWidthMinimum; - LONG NormalLeft; // FixedWidth + 1 if there is a fixed column, otherwise 0 - - PPH_TREENEW_NODE FocusNode; - ULONG HotNodeIndex; - ULONG MarkNodeIndex; // selection mark - - ULONG MouseDownLast; - POINT MouseDownLocation; - - PPH_TREENEW_CALLBACK Callback; - PVOID CallbackContext; - - PPH_TREENEW_COLUMN *Columns; // columns, indexed by ID - ULONG NextId; - ULONG AllocatedColumns; - ULONG NumberOfColumns; // just a statistic; do not use for actual logic - - PPH_TREENEW_COLUMN *ColumnsByDisplay; // columns, indexed by display order (excluding the fixed column) - ULONG AllocatedColumnsByDisplay; - ULONG NumberOfColumnsByDisplay; // the number of visible columns (excluding the fixed column) - LONG TotalViewX; // total width of normal columns - PPH_TREENEW_COLUMN FixedColumn; - PPH_TREENEW_COLUMN FirstColumn; // first column, by display order (including the fixed column) - PPH_TREENEW_COLUMN LastColumn; // last column, by display order (including the fixed column) - - PPH_TREENEW_COLUMN ResizingColumn; - LONG OldColumnWidth; - LONG TrackStartX; - LONG TrackOldFixedWidth; - ULONG DividerHot; // 0 for un-hot, 100 for completely hot - - PPH_LIST FlatList; - - ULONG SortColumn; // ID of the column to sort by - PH_SORT_ORDER SortOrder; - - FLOAT VScrollRemainder; - FLOAT HScrollRemainder; - - LONG SearchMessageTime; - PWSTR SearchString; - ULONG SearchStringCount; - ULONG AllocatedSearchString; - - ULONG TooltipIndex; - ULONG TooltipId; - PPH_STRING TooltipText; - RECT TooltipRect; // text rectangle of an unfolding tooltip - HFONT TooltipFont; - HFONT NewTooltipFont; - ULONG TooltipColumnId; - - TEXTMETRIC TextMetrics; - HTHEME ThemeData; - LONG SystemBorderX; - LONG SystemBorderY; - LONG SystemEdgeX; - LONG SystemEdgeY; - - HDC BufferedContext; - HBITMAP BufferedOldBitmap; - HBITMAP BufferedBitmap; - RECT BufferedContextRect; - - LONG SystemDragX; - LONG SystemDragY; - RECT DragRect; - - LONG EnableRedraw; - HRGN SuspendUpdateRegion; - - PH_STRINGREF EmptyText; -} PH_TREENEW_CONTEXT, *PPH_TREENEW_CONTEXT; - -LRESULT CALLBACK PhTnpWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -BOOLEAN NTAPI PhTnpNullCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -VOID PhTnpCreateTreeNewContext( - _Out_ PPH_TREENEW_CONTEXT *Context - ); - -VOID PhTnpDestroyTreeNewContext( - _In_ PPH_TREENEW_CONTEXT Context - ); - -// Event handlers - -BOOLEAN PhTnpOnCreate( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ CREATESTRUCT *CreateStruct - ); - -VOID PhTnpOnSize( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpOnSetFont( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ HFONT Font, - _In_ LOGICAL Redraw - ); - -VOID PhTnpOnStyleChanged( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Type, - _In_ STYLESTRUCT *StyleStruct - ); - -VOID PhTnpOnSettingChange( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpOnThemeChanged( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ); - -ULONG PhTnpOnGetDlgCode( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey, - _In_opt_ PMSG Message - ); - -VOID PhTnpOnPaint( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpOnPrintClient( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ ULONG Flags - ); - -BOOLEAN PhTnpOnNcPaint( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ HRGN UpdateRegion - ); - -BOOLEAN PhTnpOnSetCursor( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HWND CursorWindowHandle - ); - -VOID PhTnpOnTimer( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id - ); - -VOID PhTnpOnMouseMove( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ); - -VOID PhTnpOnMouseLeave( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpOnXxxButtonXxx( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Message, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ); - -VOID PhTnpOnCaptureChanged( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpOnKeyDown( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey, - _In_ ULONG Data - ); - -VOID PhTnpOnChar( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Character, - _In_ ULONG Data - ); - -VOID PhTnpOnMouseWheel( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ); - -VOID PhTnpOnMouseHWheel( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ); - -VOID PhTnpOnContextMenu( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorScreenX, - _In_ LONG CursorScreenY - ); - -VOID PhTnpOnVScroll( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Request, - _In_ USHORT Position - ); - -VOID PhTnpOnHScroll( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Request, - _In_ USHORT Position - ); - -BOOLEAN PhTnpOnNotify( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ); - -ULONG_PTR PhTnpOnUserMessage( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ); - -// Misc. - -VOID PhTnpSetFont( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ HFONT Font, - _In_ BOOLEAN Redraw - ); - -VOID PhTnpUpdateSystemMetrics( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpUpdateTextMetrics( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpUpdateThemeData( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpInitializeThemeData( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpCancelTrack( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpLayout( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpLayoutHeader( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpSetFixedWidth( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG FixedWidth - ); - -VOID PhTnpSetRedraw( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Redraw - ); - -VOID PhTnpSendMouseEvent( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PH_TREENEW_MESSAGE Message, - _In_ LONG CursorX, - _In_ LONG CursorY, - _In_ PPH_TREENEW_NODE Node, - _In_ PPH_TREENEW_COLUMN Column, - _In_ ULONG VirtualKeys - ); - -// Columns - -PPH_TREENEW_COLUMN PhTnpLookupColumnById( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id - ); - -BOOLEAN PhTnpAddColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN Column - ); - -BOOLEAN PhTnpRemoveColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id - ); - -BOOLEAN PhTnpCopyColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id, - _Out_ PPH_TREENEW_COLUMN Column - ); - -BOOLEAN PhTnpChangeColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Mask, - _In_ ULONG Id, - _In_ PPH_TREENEW_COLUMN Column - ); - -VOID PhTnpExpandAllocatedColumns( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpUpdateColumnMaps( - _In_ PPH_TREENEW_CONTEXT Context - ); - -// Columns (header control) - -LONG PhTnpInsertColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN Column - ); - -VOID PhTnpChangeColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Mask, - _In_ PPH_TREENEW_COLUMN Column - ); - -VOID PhTnpDeleteColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _Inout_ PPH_TREENEW_COLUMN Column - ); - -VOID PhTnpUpdateColumnHeaders( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpProcessResizeColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN Column, - _In_ LONG Delta - ); - -VOID PhTnpProcessSortColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN NewColumn - ); - -BOOLEAN PhTnpSetColumnHeaderSortIcon( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ PPH_TREENEW_COLUMN SortColumnPointer - ); - -VOID PhTnpAutoSizeColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HWND HeaderHandle, - _In_ PPH_TREENEW_COLUMN Column, - _In_ ULONG Flags - ); - -// Nodes - -BOOLEAN PhTnpGetNodeChildren( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ PPH_TREENEW_NODE Node, - _Out_ PPH_TREENEW_NODE **Children, - _Out_ PULONG NumberOfChildren - ); - -BOOLEAN PhTnpIsNodeLeaf( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node - ); - -BOOLEAN PhTnpGetCellText( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ ULONG Id, - _Out_ PPH_STRINGREF Text - ); - -VOID PhTnpRestructureNodes( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpInsertNodeChildren( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ ULONG Level - ); - -VOID PhTnpSetExpandedNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ BOOLEAN Expanded - ); - -BOOLEAN PhTnpGetCellParts( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Index, - _In_opt_ PPH_TREENEW_COLUMN Column, - _In_ ULONG Flags, - _Out_ PPH_TREENEW_CELL_PARTS Parts - ); - -BOOLEAN PhTnpGetRowRects( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Start, - _In_ ULONG End, - _In_ BOOLEAN Clip, - _Out_ PRECT Rect - ); - -VOID PhTnpHitTest( - _In_ PPH_TREENEW_CONTEXT Context, - _Inout_ PPH_TREENEW_HIT_TEST HitTest - ); - -VOID PhTnpSelectRange( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Start, - _In_ ULONG End, - _In_ ULONG Flags, - _Out_opt_ PULONG ChangedStart, - _Out_opt_ PULONG ChangedEnd - ); - -VOID PhTnpSetHotNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ PPH_TREENEW_NODE NewHotNode, - _In_ BOOLEAN NewPlusMinusHot - ); - -VOID PhTnpProcessSelectNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ LOGICAL ControlKey, - _In_ LOGICAL ShiftKey, - _In_ LOGICAL RightButton - ); - -BOOLEAN PhTnpEnsureVisibleNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Index - ); - -// Mouse - -VOID PhTnpProcessMoveMouse( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorX, - _In_ LONG CursorY - ); - -VOID PhTnpProcessMouseVWheel( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance - ); - -VOID PhTnpProcessMouseHWheel( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance - ); - -// Keyboard - -BOOLEAN PhTnpProcessFocusKey( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey - ); - -BOOLEAN PhTnpProcessNodeKey( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey - ); - -VOID PhTnpProcessSearchKey( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Character - ); - -BOOLEAN PhTnpDefaultIncrementalSearch( - _In_ PPH_TREENEW_CONTEXT Context, - _Inout_ PPH_TREENEW_SEARCH_EVENT SearchEvent, - _In_ BOOLEAN Partial, - _In_ BOOLEAN Wrap - ); - -// Scrolling - -VOID PhTnpUpdateScrollBars( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpScroll( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG DeltaRows, - _In_ LONG DeltaX - ); - -VOID PhTnpProcessScroll( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG DeltaRows, - _In_ LONG DeltaX - ); - -BOOLEAN PhTnpCanScroll( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Horizontal, - _In_ BOOLEAN Positive - ); - -// Drawing - -VOID PhTnpPaint( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ PRECT PaintRect - ); - -VOID PhTnpPrepareRowForDraw( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _Inout_ PPH_TREENEW_NODE Node - ); - -VOID PhTnpDrawCell( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ PRECT CellRect, - _In_ PPH_TREENEW_NODE Node, - _In_ PPH_TREENEW_COLUMN Column, - _In_ LONG RowIndex, - _In_ LONG ColumnIndex - ); - -VOID PhTnpDrawDivider( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc - ); - -VOID PhTnpDrawPlusMinusGlyph( - _In_ HDC hdc, - _In_ PRECT Rect, - _In_ BOOLEAN Plus - ); - -VOID PhTnpDrawSelectionRectangle( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ PRECT Rect - ); - -VOID PhTnpDrawThemedBorder( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc - ); - -// Tooltips - -VOID PhTnpInitializeTooltips( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpGetTooltipText( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPOINT Point, - _Out_ PWSTR *Text - ); - -BOOLEAN PhTnpPrepareTooltipShow( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpPrepareTooltipPop( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpPopTooltip( - _In_ PPH_TREENEW_CONTEXT Context - ); - -PPH_TREENEW_COLUMN PhTnpHitTestHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Fixed, - _In_ PPOINT Point, - _Out_opt_ PRECT ItemRect - ); - -VOID PhTnpGetHeaderTooltipText( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Fixed, - _In_ PPOINT Point, - _Out_ PWSTR *Text - ); - - -LRESULT CALLBACK PhTnpHeaderHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ); - -// Drag selection - -BOOLEAN PhTnpDetectDrag( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorX, - _In_ LONG CursorY, - _In_ BOOLEAN DispatchMessages, - _Out_opt_ PULONG CancelledByMessage - ); - -VOID PhTnpDragSelect( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorX, - _In_ LONG CursorY - ); - -VOID PhTnpProcessDragSelect( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKeys, - _In_ PRECT OldRect, - _In_ PRECT NewRect, - _In_ PRECT TotalRect - ); - -// Double buffering - -VOID PhTnpCreateBufferedContext( - _In_ PPH_TREENEW_CONTEXT Context - ); - -VOID PhTnpDestroyBufferedContext( - _In_ PPH_TREENEW_CONTEXT Context - ); - -// Support functions - -VOID PhTnpGetMessagePos( - _In_ HWND hwnd, - _Out_ PPOINT ClientPoint - ); - -// Macros - -#define HRGN_FULL ((HRGN)1) // passed by WM_NCPAINT even though it's completely undocumented - -#define TNP_CELL_LEFT_MARGIN 6 -#define TNP_CELL_RIGHT_MARGIN 6 -#define TNP_ICON_RIGHT_PADDING 4 - -#define TNP_TIMER_NULL 1 -#define TNP_TIMER_ANIMATE_DIVIDER 2 - -#define TNP_TOOLTIPS_ITEM 0 -#define TNP_TOOLTIPS_FIXED_HEADER 1 -#define TNP_TOOLTIPS_HEADER 2 -#define TNP_TOOLTIPS_DEFAULT_MAXIMUM_WIDTH 550 - -#define TNP_ANIMATE_DIVIDER_INTERVAL 10 -#define TNP_ANIMATE_DIVIDER_INCREMENT 17 -#define TNP_ANIMATE_DIVIDER_DECREMENT 2 - -#define TNP_HIT_TEST_FIXED_DIVIDER(X, Context) \ - ((Context)->FixedDividerVisible && (X) >= (Context)->FixedWidth - 8 && (X) < (Context)->FixedWidth + 8) -#define TNP_HIT_TEST_PLUS_MINUS_GLYPH(X, NodeLevel) \ - (((X) >= TNP_CELL_LEFT_MARGIN + ((LONG)(NodeLevel) * SmallIconWidth)) && ((X) < TNP_CELL_LEFT_MARGIN + ((LONG)(NodeLevel) * SmallIconWidth) + SmallIconWidth)) - -#endif +#ifndef _PH_TREENEWP_H +#define _PH_TREENEWP_H + +// Important notes about pointers: +// +// All memory allocation for nodes and strings is handled by the user. This usually means there is a +// very limited time during which they can be safely accessed. +// +// Node pointers are valid through the duration of message processing, and also up to the next +// restructure operation, either user- or control- initiated. This means that state such as the +// focused node, hot node and mark node must be carefully preserved through restructuring. If +// restructuring is suspended by a set-redraw call, all nodes must be considered invalid and no user +// input can be handled. +// +// Strings are valid only through the duration of message processing. + +typedef struct _PH_TREENEW_CONTEXT +{ + HWND Handle; + PVOID InstanceHandle; + HWND FixedHeaderHandle; + HWND HeaderHandle; + HWND VScrollHandle; + HWND HScrollHandle; + HWND FillerBoxHandle; + HWND TooltipsHandle; + + union + { + struct + { + ULONG FontOwned : 1; + ULONG Tracking : 1; // tracking for fixed divider + ULONG VScrollVisible : 1; + ULONG HScrollVisible : 1; + ULONG FixedColumnVisible : 1; + ULONG FixedDividerVisible : 1; + ULONG AnimateDivider : 1; + ULONG AnimateDividerFadingIn : 1; + ULONG AnimateDividerFadingOut : 1; + ULONG CanAnyExpand : 1; + ULONG TriState : 1; + ULONG HasFocus : 1; + ULONG ThemeInitialized : 1; // delay load theme data + ULONG ThemeActive : 1; + ULONG ThemeHasItemBackground : 1; + ULONG ThemeHasGlyph : 1; + ULONG ThemeHasHotGlyph : 1; + ULONG FocusNodeFound : 1; // used to preserve the focused node across restructuring + ULONG SearchFailed : 1; // used to prevent multiple beeps + ULONG SearchSingleCharMode : 1; // LV style single-character search + ULONG TooltipUnfolding : 1; // whether the current tooltip is unfolding + ULONG DoubleBuffered : 1; + ULONG SuspendUpdateStructure : 1; + ULONG SuspendUpdateLayout : 1; + ULONG SuspendUpdateMoveMouse : 1; + ULONG DragSelectionActive : 1; + ULONG SelectionRectangleAlpha : 1; // use alpha blending for the selection rectangle + ULONG CustomRowHeight : 1; + ULONG CustomColors : 1; + ULONG ContextMenuActive : 1; + ULONG ThemeSupport : 1; + ULONG Spare : 1; + }; + ULONG Flags; + }; + ULONG Style; + ULONG ExtendedStyle; + ULONG ExtendedFlags; + + HFONT Font; + HCURSOR Cursor; + HCURSOR DividerCursor; + + RECT ClientRect; + LONG HeaderHeight; + LONG RowHeight; + ULONG VScrollWidth; + ULONG HScrollHeight; + LONG VScrollPosition; + LONG HScrollPosition; + LONG FixedWidth; // width of the fixed part of the tree list + LONG FixedWidthMinimum; + LONG NormalLeft; // FixedWidth + 1 if there is a fixed column, otherwise 0 + + PPH_TREENEW_NODE FocusNode; + ULONG HotNodeIndex; + ULONG MarkNodeIndex; // selection mark + + ULONG MouseDownLast; + POINT MouseDownLocation; + + PPH_TREENEW_CALLBACK Callback; + PVOID CallbackContext; + + PPH_TREENEW_COLUMN *Columns; // columns, indexed by ID + ULONG NextId; + ULONG AllocatedColumns; + ULONG NumberOfColumns; // just a statistic; do not use for actual logic + + PPH_TREENEW_COLUMN *ColumnsByDisplay; // columns, indexed by display order (excluding the fixed column) + ULONG AllocatedColumnsByDisplay; + ULONG NumberOfColumnsByDisplay; // the number of visible columns (excluding the fixed column) + LONG TotalViewX; // total width of normal columns + PPH_TREENEW_COLUMN FixedColumn; + PPH_TREENEW_COLUMN FirstColumn; // first column, by display order (including the fixed column) + PPH_TREENEW_COLUMN LastColumn; // last column, by display order (including the fixed column) + + PPH_TREENEW_COLUMN ResizingColumn; + LONG OldColumnWidth; + LONG TrackStartX; + LONG TrackOldFixedWidth; + ULONG DividerHot; // 0 for un-hot, 100 for completely hot + + PPH_LIST FlatList; + + ULONG SortColumn; // ID of the column to sort by + PH_SORT_ORDER SortOrder; + + FLOAT VScrollRemainder; + FLOAT HScrollRemainder; + + LONG SearchMessageTime; + PWSTR SearchString; + ULONG SearchStringCount; + ULONG AllocatedSearchString; + + ULONG TooltipIndex; + ULONG TooltipId; + PPH_STRING TooltipText; + RECT TooltipRect; // text rectangle of an unfolding tooltip + HFONT TooltipFont; + HFONT NewTooltipFont; + ULONG TooltipColumnId; + + TEXTMETRIC TextMetrics; + HTHEME ThemeData; + COLORREF DefaultBackColor; + COLORREF DefaultForeColor; + + // User configurable colors. + COLORREF CustomTextColor; + COLORREF CustomFocusColor; + COLORREF CustomSelectedColor; + + LONG SystemBorderX; + LONG SystemBorderY; + LONG SystemEdgeX; + LONG SystemEdgeY; + + HDC BufferedContext; + HBITMAP BufferedOldBitmap; + HBITMAP BufferedBitmap; + RECT BufferedContextRect; + + LONG SystemDragX; + LONG SystemDragY; + RECT DragRect; + + LONG EnableRedraw; + HRGN SuspendUpdateRegion; + + PH_STRINGREF EmptyText; + + WNDPROC HeaderWindowProc; + WNDPROC FixedHeaderWindowProc; +} PH_TREENEW_CONTEXT, *PPH_TREENEW_CONTEXT; + +LRESULT CALLBACK PhTnpWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOLEAN NTAPI PhTnpNullCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +VOID PhTnpCreateTreeNewContext( + _Out_ PPH_TREENEW_CONTEXT *Context + ); + +VOID PhTnpDestroyTreeNewContext( + _In_ PPH_TREENEW_CONTEXT Context + ); + +// Event handlers + +BOOLEAN PhTnpOnCreate( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ CREATESTRUCT *CreateStruct + ); + +VOID PhTnpOnSize( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpOnSetFont( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ HFONT Font, + _In_ LOGICAL Redraw + ); + +VOID PhTnpOnStyleChanged( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Type, + _In_ STYLESTRUCT *StyleStruct + ); + +VOID PhTnpOnSettingChange( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpOnThemeChanged( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ); + +ULONG PhTnpOnGetDlgCode( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey, + _In_opt_ PMSG Message + ); + +VOID PhTnpOnPaint( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpOnPrintClient( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ ULONG Flags + ); + +BOOLEAN PhTnpOnNcPaint( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ HRGN UpdateRegion + ); + +BOOLEAN PhTnpOnSetCursor( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HWND CursorWindowHandle + ); + +VOID PhTnpOnTimer( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id + ); + +VOID PhTnpOnMouseMove( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ); + +VOID PhTnpOnMouseLeave( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpOnXxxButtonXxx( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Message, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ); + +VOID PhTnpOnCaptureChanged( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpOnKeyDown( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey, + _In_ ULONG Data + ); + +VOID PhTnpOnChar( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Character, + _In_ ULONG Data + ); + +VOID PhTnpOnMouseWheel( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ); + +VOID PhTnpOnMouseHWheel( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ); + +VOID PhTnpOnContextMenu( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorScreenX, + _In_ LONG CursorScreenY + ); + +VOID PhTnpOnVScroll( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Request, + _In_ USHORT Position + ); + +VOID PhTnpOnHScroll( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Request, + _In_ USHORT Position + ); + +BOOLEAN PhTnpOnNotify( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ); + +ULONG_PTR PhTnpOnUserMessage( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ); + +// Misc. + +VOID PhTnpSetFont( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ HFONT Font, + _In_ BOOLEAN Redraw + ); + +VOID PhTnpUpdateSystemMetrics( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpUpdateTextMetrics( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpUpdateThemeData( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpInitializeThemeData( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpCancelTrack( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpLayout( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpLayoutHeader( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpSetFixedWidth( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG FixedWidth + ); + +VOID PhTnpSetRedraw( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Redraw + ); + +VOID PhTnpSendMouseEvent( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PH_TREENEW_MESSAGE Message, + _In_ LONG CursorX, + _In_ LONG CursorY, + _In_ PPH_TREENEW_NODE Node, + _In_ PPH_TREENEW_COLUMN Column, + _In_ ULONG VirtualKeys + ); + +// Columns + +PPH_TREENEW_COLUMN PhTnpLookupColumnById( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id + ); + +BOOLEAN PhTnpAddColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN Column + ); + +BOOLEAN PhTnpRemoveColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id + ); + +BOOLEAN PhTnpCopyColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id, + _Out_ PPH_TREENEW_COLUMN Column + ); + +BOOLEAN PhTnpChangeColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Mask, + _In_ ULONG Id, + _In_ PPH_TREENEW_COLUMN Column + ); + +VOID PhTnpExpandAllocatedColumns( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpUpdateColumnMaps( + _In_ PPH_TREENEW_CONTEXT Context + ); + +// Columns (header control) + +LONG PhTnpInsertColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN Column + ); + +VOID PhTnpChangeColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Mask, + _In_ PPH_TREENEW_COLUMN Column + ); + +VOID PhTnpDeleteColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _Inout_ PPH_TREENEW_COLUMN Column + ); + +VOID PhTnpUpdateColumnHeaders( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpProcessResizeColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN Column, + _In_ LONG Delta + ); + +VOID PhTnpProcessSortColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN NewColumn + ); + +BOOLEAN PhTnpSetColumnHeaderSortIcon( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ PPH_TREENEW_COLUMN SortColumnPointer + ); + +VOID PhTnpAutoSizeColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HWND HeaderHandle, + _In_ PPH_TREENEW_COLUMN Column, + _In_ ULONG Flags + ); + +// Nodes + +BOOLEAN PhTnpGetNodeChildren( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ PPH_TREENEW_NODE Node, + _Out_ PPH_TREENEW_NODE **Children, + _Out_ PULONG NumberOfChildren + ); + +BOOLEAN PhTnpIsNodeLeaf( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node + ); + +BOOLEAN PhTnpGetCellText( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ ULONG Id, + _Out_ PPH_STRINGREF Text + ); + +VOID PhTnpRestructureNodes( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpInsertNodeChildren( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ ULONG Level + ); + +VOID PhTnpSetExpandedNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ BOOLEAN Expanded + ); + +BOOLEAN PhTnpGetCellParts( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Index, + _In_opt_ PPH_TREENEW_COLUMN Column, + _In_ ULONG Flags, + _Out_ PPH_TREENEW_CELL_PARTS Parts + ); + +BOOLEAN PhTnpGetRowRects( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Start, + _In_ ULONG End, + _In_ BOOLEAN Clip, + _Out_ PRECT Rect + ); + +VOID PhTnpHitTest( + _In_ PPH_TREENEW_CONTEXT Context, + _Inout_ PPH_TREENEW_HIT_TEST HitTest + ); + +VOID PhTnpSelectRange( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Start, + _In_ ULONG End, + _In_ ULONG Flags, + _Out_opt_ PULONG ChangedStart, + _Out_opt_ PULONG ChangedEnd + ); + +VOID PhTnpSetHotNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ PPH_TREENEW_NODE NewHotNode, + _In_ BOOLEAN NewPlusMinusHot + ); + +VOID PhTnpProcessSelectNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ LOGICAL ControlKey, + _In_ LOGICAL ShiftKey, + _In_ LOGICAL RightButton + ); + +BOOLEAN PhTnpEnsureVisibleNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Index + ); + +// Mouse + +VOID PhTnpProcessMoveMouse( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorX, + _In_ LONG CursorY + ); + +VOID PhTnpProcessMouseVWheel( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance + ); + +VOID PhTnpProcessMouseHWheel( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance + ); + +// Keyboard + +BOOLEAN PhTnpProcessFocusKey( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey + ); + +BOOLEAN PhTnpProcessNodeKey( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey + ); + +VOID PhTnpProcessSearchKey( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Character + ); + +BOOLEAN PhTnpDefaultIncrementalSearch( + _In_ PPH_TREENEW_CONTEXT Context, + _Inout_ PPH_TREENEW_SEARCH_EVENT SearchEvent, + _In_ BOOLEAN Partial, + _In_ BOOLEAN Wrap + ); + +// Scrolling + +VOID PhTnpUpdateScrollBars( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpScroll( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG DeltaRows, + _In_ LONG DeltaX + ); + +VOID PhTnpProcessScroll( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG DeltaRows, + _In_ LONG DeltaX + ); + +BOOLEAN PhTnpCanScroll( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Horizontal, + _In_ BOOLEAN Positive + ); + +// Drawing + +VOID PhTnpPaint( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ PRECT PaintRect + ); + +VOID PhTnpPrepareRowForDraw( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _Inout_ PPH_TREENEW_NODE Node + ); + +VOID PhTnpDrawCell( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ PRECT CellRect, + _In_ PPH_TREENEW_NODE Node, + _In_ PPH_TREENEW_COLUMN Column, + _In_ LONG RowIndex, + _In_ LONG ColumnIndex + ); + +VOID PhTnpDrawDivider( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc + ); + +VOID PhTnpDrawPlusMinusGlyph( + _In_ HDC hdc, + _In_ PRECT Rect, + _In_ BOOLEAN Plus + ); + +VOID PhTnpDrawSelectionRectangle( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ PRECT Rect + ); + +VOID PhTnpDrawThemedBorder( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc + ); + +// Tooltips + +VOID PhTnpInitializeTooltips( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpGetTooltipText( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPOINT Point, + _Out_ PWSTR *Text + ); + +BOOLEAN PhTnpPrepareTooltipShow( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpPrepareTooltipPop( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpPopTooltip( + _In_ PPH_TREENEW_CONTEXT Context + ); + +PPH_TREENEW_COLUMN PhTnpHitTestHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Fixed, + _In_ PPOINT Point, + _Out_opt_ PRECT ItemRect + ); + +VOID PhTnpGetHeaderTooltipText( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Fixed, + _In_ PPOINT Point, + _Out_ PWSTR *Text + ); + +LRESULT CALLBACK PhTnpHeaderHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// Drag selection + +BOOLEAN PhTnpDetectDrag( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorX, + _In_ LONG CursorY, + _In_ BOOLEAN DispatchMessages, + _Out_opt_ PULONG CancelledByMessage + ); + +VOID PhTnpDragSelect( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorX, + _In_ LONG CursorY + ); + +VOID PhTnpProcessDragSelect( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKeys, + _In_ PRECT OldRect, + _In_ PRECT NewRect, + _In_ PRECT TotalRect + ); + +// Double buffering + +VOID PhTnpCreateBufferedContext( + _In_ PPH_TREENEW_CONTEXT Context + ); + +VOID PhTnpDestroyBufferedContext( + _In_ PPH_TREENEW_CONTEXT Context + ); + +// Support functions + +VOID PhTnpGetMessagePos( + _In_ HWND hwnd, + _Out_ PPOINT ClientPoint + ); + +// Macros + +#define HRGN_FULL ((HRGN)1) // passed by WM_NCPAINT even though it's completely undocumented + +#define TNP_CELL_LEFT_MARGIN 6 +#define TNP_CELL_RIGHT_MARGIN 6 +#define TNP_ICON_RIGHT_PADDING 4 + +#define TNP_TIMER_NULL 1 +#define TNP_TIMER_ANIMATE_DIVIDER 2 + +#define TNP_TOOLTIPS_ITEM 0 +#define TNP_TOOLTIPS_FIXED_HEADER 1 +#define TNP_TOOLTIPS_HEADER 2 +#define TNP_TOOLTIPS_DEFAULT_MAXIMUM_WIDTH 550 + +#define TNP_ANIMATE_DIVIDER_INTERVAL 10 +#define TNP_ANIMATE_DIVIDER_INCREMENT 17 +#define TNP_ANIMATE_DIVIDER_DECREMENT 2 + +#define TNP_HIT_TEST_FIXED_DIVIDER(X, Context) \ + ((Context)->FixedDividerVisible && (X) >= (Context)->FixedWidth - 8 && (X) < (Context)->FixedWidth + 8) +#define TNP_HIT_TEST_PLUS_MINUS_GLYPH(X, NodeLevel) \ + (((X) >= TNP_CELL_LEFT_MARGIN + ((LONG)(NodeLevel) * SmallIconWidth)) && ((X) < TNP_CELL_LEFT_MARGIN + ((LONG)(NodeLevel) * SmallIconWidth) + SmallIconWidth)) + +#endif diff --git a/phlib/include/verify.h b/phlib/include/verify.h index 741feb9f7b89..f18a20450b53 100644 --- a/phlib/include/verify.h +++ b/phlib/include/verify.h @@ -1,77 +1,77 @@ -#ifndef _PH_VERIFY_H -#define _PH_VERIFY_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define PH_VERIFY_DEFAULT_SIZE_LIMIT (32 * 1024 * 1024) - -typedef enum _VERIFY_RESULT -{ - VrUnknown = 0, - VrNoSignature, - VrTrusted, - VrExpired, - VrRevoked, - VrDistrust, - VrSecuritySettings, - VrBadSignature -} VERIFY_RESULT, *PVERIFY_RESULT; - -#define PH_VERIFY_PREVENT_NETWORK_ACCESS 0x1 -#define PH_VERIFY_VIEW_PROPERTIES 0x2 - -typedef struct _PH_VERIFY_FILE_INFO -{ - PWSTR FileName; - ULONG Flags; // PH_VERIFY_* - - ULONG FileSizeLimitForHash; // 0 for PH_VERIFY_DEFAULT_SIZE_LIMIT, -1 for unlimited - ULONG NumberOfCatalogFileNames; - PWSTR *CatalogFileNames; - - HWND hWnd; // for PH_VERIFY_VIEW_PROPERTIES -} PH_VERIFY_FILE_INFO, *PPH_VERIFY_FILE_INFO; - -PHLIBAPI -VERIFY_RESULT -NTAPI -PhVerifyFile( - _In_ PWSTR FileName, - _Out_opt_ PPH_STRING *SignerName - ); - -PHLIBAPI -NTSTATUS -NTAPI -PhVerifyFileEx( - _In_ PPH_VERIFY_FILE_INFO Information, - _Out_ VERIFY_RESULT *VerifyResult, - _Out_opt_ PCERT_CONTEXT **Signatures, - _Out_opt_ PULONG NumberOfSignatures - ); - -PHLIBAPI -VOID -NTAPI -PhFreeVerifySignatures( - _In_ PCERT_CONTEXT *Signatures, - _In_ ULONG NumberOfSignatures - ); - -PHLIBAPI -PPH_STRING -NTAPI -PhGetSignerNameFromCertificate( - _In_ PCERT_CONTEXT Certificate - ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef _PH_VERIFY_H +#define _PH_VERIFY_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PH_VERIFY_DEFAULT_SIZE_LIMIT (32 * 1024 * 1024) + +typedef enum _VERIFY_RESULT +{ + VrUnknown = 0, + VrNoSignature, + VrTrusted, + VrExpired, + VrRevoked, + VrDistrust, + VrSecuritySettings, + VrBadSignature +} VERIFY_RESULT, *PVERIFY_RESULT; + +#define PH_VERIFY_PREVENT_NETWORK_ACCESS 0x1 +#define PH_VERIFY_VIEW_PROPERTIES 0x2 + +typedef struct _PH_VERIFY_FILE_INFO +{ + PWSTR FileName; + ULONG Flags; // PH_VERIFY_* + + ULONG FileSizeLimitForHash; // 0 for PH_VERIFY_DEFAULT_SIZE_LIMIT, -1 for unlimited + ULONG NumberOfCatalogFileNames; + PWSTR *CatalogFileNames; + + HWND hWnd; // for PH_VERIFY_VIEW_PROPERTIES +} PH_VERIFY_FILE_INFO, *PPH_VERIFY_FILE_INFO; + +PHLIBAPI +VERIFY_RESULT +NTAPI +PhVerifyFile( + _In_ PWSTR FileName, + _Out_opt_ PPH_STRING *SignerName + ); + +PHLIBAPI +NTSTATUS +NTAPI +PhVerifyFileEx( + _In_ PPH_VERIFY_FILE_INFO Information, + _Out_ VERIFY_RESULT *VerifyResult, + _Out_opt_ PCERT_CONTEXT **Signatures, + _Out_opt_ PULONG NumberOfSignatures + ); + +PHLIBAPI +VOID +NTAPI +PhFreeVerifySignatures( + _In_ PCERT_CONTEXT *Signatures, + _In_ ULONG NumberOfSignatures + ); + +PHLIBAPI +PPH_STRING +NTAPI +PhGetSignerNameFromCertificate( + _In_ PCERT_CONTEXT Certificate + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/include/verifyp.h b/phlib/include/verifyp.h index 7a709f74203e..65831d223f07 100644 --- a/phlib/include/verifyp.h +++ b/phlib/include/verifyp.h @@ -1,118 +1,132 @@ -#ifndef _PH_VERIFYP_H -#define _PH_VERIFYP_H - -#include - -typedef struct _CATALOG_INFO -{ - DWORD cbStruct; - WCHAR wszCatalogFile[MAX_PATH]; -} CATALOG_INFO, *PCATALOG_INFO; - -typedef struct tagCRYPTUI_VIEWSIGNERINFO_STRUCT { - DWORD dwSize; - HWND hwndParent; - DWORD dwFlags; - LPCTSTR szTitle; - CMSG_SIGNER_INFO *pSignerInfo; - HCRYPTMSG hMsg; - LPCSTR pszOID; - DWORD_PTR dwReserved; - DWORD cStores; - HCERTSTORE *rghStores; - DWORD cPropSheetPages; - LPCPROPSHEETPAGE rgPropSheetPages; -} CRYPTUI_VIEWSIGNERINFO_STRUCT, *PCRYPTUI_VIEWSIGNERINFO_STRUCT; - -typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle)( - HANDLE hFile, - DWORD *pcbHash, - BYTE *pbHash, - DWORD dwFlags - ); - -typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle2)( - HCATADMIN hCatAdmin, - HANDLE hFile, - DWORD *pcbHash, - BYTE *pbHash, - DWORD dwFlags - ); - -typedef BOOL (WINAPI *_CryptCATAdminAcquireContext)( - HANDLE *phCatAdmin, - GUID *pgSubsystem, - DWORD dwFlags - ); - -typedef BOOL (WINAPI *_CryptCATAdminAcquireContext2)( - HCATADMIN *phCatAdmin, - const GUID *pgSubsystem, - PCWSTR pwszHashAlgorithm, - PCCERT_STRONG_SIGN_PARA pStrongHashPolicy, - DWORD dwFlags - ); - -typedef HANDLE (WINAPI *_CryptCATAdminEnumCatalogFromHash)( - HANDLE hCatAdmin, - BYTE *pbHash, - DWORD cbHash, - DWORD dwFlags, - HANDLE *phPrevCatInfo - ); - -typedef BOOL (WINAPI *_CryptCATCatalogInfoFromContext)( - HANDLE hCatInfo, - CATALOG_INFO *psCatInfo, - DWORD dwFlags - ); - -typedef BOOL (WINAPI *_CryptCATAdminReleaseCatalogContext)( - HANDLE hCatAdmin, - HANDLE hCatInfo, - DWORD dwFlags - ); - -typedef BOOL (WINAPI *_CryptCATAdminReleaseContext)( - HANDLE hCatAdmin, - DWORD dwFlags - ); - -typedef PCRYPT_PROVIDER_DATA (WINAPI *_WTHelperProvDataFromStateData)( - HANDLE hStateData - ); - -typedef PCRYPT_PROVIDER_SGNR (WINAPI *_WTHelperGetProvSignerFromChain)( - CRYPT_PROVIDER_DATA *pProvData, - DWORD idxSigner, - BOOL fCounterSigner, - DWORD idxCounterSigner - ); - -typedef LONG (WINAPI *_WinVerifyTrust)( - HWND hWnd, - GUID *pgActionID, - LPVOID pWVTData - ); - -typedef DWORD (WINAPI *_CertNameToStr)( - DWORD dwCertEncodingType, - PCERT_NAME_BLOB pName, - DWORD dwStrType, - LPTSTR psz, - DWORD csz - ); - -typedef PCCERT_CONTEXT (WINAPI *_CertDuplicateCertificateContext)( - _In_ PCCERT_CONTEXT pCertContext - ); - -typedef BOOL (WINAPI *_CertFreeCertificateContext)( - _In_ PCCERT_CONTEXT pCertContext - ); - -typedef BOOL (WINAPI *_CryptUIDlgViewSignerInfo)( - _In_ CRYPTUI_VIEWSIGNERINFO_STRUCT *pcvsi - ); - -#endif +#ifndef _PH_VERIFYP_H +#define _PH_VERIFYP_H + +#include + +typedef struct _PH_VERIFY_CACHE_ENTRY +{ + PH_AVL_LINKS Links; + + PPH_STRING FileName; + VERIFY_RESULT VerifyResult; + PPH_STRING VerifySignerName; +} PH_VERIFY_CACHE_ENTRY, *PPH_VERIFY_CACHE_ENTRY; + +INT NTAPI PhpVerifyCacheCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ); + +typedef struct _CATALOG_INFO +{ + ULONG cbStruct; + WCHAR wszCatalogFile[MAX_PATH]; +} CATALOG_INFO, *PCATALOG_INFO; + +typedef struct tagCRYPTUI_VIEWSIGNERINFO_STRUCT { + ULONG dwSize; + HWND hwndParent; + ULONG dwFlags; + LPCTSTR szTitle; + CMSG_SIGNER_INFO *pSignerInfo; + HCRYPTMSG hMsg; + LPCSTR pszOID; + ULONG_PTR dwReserved; + ULONG cStores; + HCERTSTORE *rghStores; + ULONG cPropSheetPages; + LPCPROPSHEETPAGE rgPropSheetPages; +} CRYPTUI_VIEWSIGNERINFO_STRUCT, *PCRYPTUI_VIEWSIGNERINFO_STRUCT; + +typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle)( + HANDLE hFile, + ULONG *pcbHash, + BYTE *pbHash, + ULONG dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminCalcHashFromFileHandle2)( + HCATADMIN hCatAdmin, + HANDLE hFile, + ULONG *pcbHash, + BYTE *pbHash, + ULONG dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminAcquireContext)( + HANDLE *phCatAdmin, + GUID *pgSubsystem, + ULONG dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminAcquireContext2)( + HCATADMIN *phCatAdmin, + const GUID *pgSubsystem, + PCWSTR pwszHashAlgorithm, + PCCERT_STRONG_SIGN_PARA pStrongHashPolicy, + ULONG dwFlags + ); + +typedef HANDLE (WINAPI *_CryptCATAdminEnumCatalogFromHash)( + HANDLE hCatAdmin, + BYTE *pbHash, + ULONG cbHash, + ULONG dwFlags, + HANDLE *phPrevCatInfo + ); + +typedef BOOL (WINAPI *_CryptCATCatalogInfoFromContext)( + HANDLE hCatInfo, + CATALOG_INFO *psCatInfo, + ULONG dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminReleaseCatalogContext)( + HANDLE hCatAdmin, + HANDLE hCatInfo, + ULONG dwFlags + ); + +typedef BOOL (WINAPI *_CryptCATAdminReleaseContext)( + HANDLE hCatAdmin, + ULONG dwFlags + ); + +typedef PCRYPT_PROVIDER_DATA (WINAPI *_WTHelperProvDataFromStateData)( + HANDLE hStateData + ); + +typedef PCRYPT_PROVIDER_SGNR (WINAPI *_WTHelperGetProvSignerFromChain)( + CRYPT_PROVIDER_DATA *pProvData, + ULONG idxSigner, + BOOL fCounterSigner, + ULONG idxCounterSigner + ); + +typedef LONG (WINAPI *_WinVerifyTrust)( + HWND hWnd, + GUID *pgActionID, + LPVOID pWVTData + ); + +typedef ULONG (WINAPI *_CertNameToStr)( + ULONG dwCertEncodingType, + PCERT_NAME_BLOB pName, + ULONG dwStrType, + LPTSTR psz, + ULONG csz + ); + +typedef PCCERT_CONTEXT (WINAPI *_CertDuplicateCertificateContext)( + _In_ PCCERT_CONTEXT pCertContext + ); + +typedef BOOL (WINAPI *_CertFreeCertificateContext)( + _In_ PCCERT_CONTEXT pCertContext + ); + +typedef BOOL (WINAPI *_CryptUIDlgViewSignerInfo)( + _In_ CRYPTUI_VIEWSIGNERINFO_STRUCT *pcvsi + ); + +#endif diff --git a/plugins/ToolStatus/searchbox.c b/phlib/include/wslsup.h similarity index 52% rename from plugins/ToolStatus/searchbox.c rename to phlib/include/wslsup.h index c23f5f5ba8bc..830d443fb511 100644 --- a/plugins/ToolStatus/searchbox.c +++ b/phlib/include/wslsup.h @@ -1,48 +1,46 @@ -/* - * Process Hacker - - * Search control stub - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - * - */ - -#include "toolstatus.h" -#include "commonutil.h" - -BOOLEAN CreateSearchboxControl( - VOID - ) -{ - if (SearchboxHandle = CreateWindowEx( - WS_EX_CLIENTEDGE, - WC_EDIT, - NULL, - WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | ES_LEFT | ES_AUTOHSCROLL | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - RebarHandle, - NULL, - NULL, - NULL - )) - { - CreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); - return TRUE; - } - - return FALSE; -} \ No newline at end of file +/* + * Process Hacker - + * LXSS support helpers + * + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _PH_WSLSUP_H +#define _PH_WSLSUP_H + +#ifdef __cplusplus +extern "C" { +#endif + +BOOLEAN PhInitializeLxssImageVersionInfo( + _Out_ PPH_IMAGE_VERSION_INFO ImageVersionInfo, + _In_ PPH_STRING FileName + ); + +ULONG PhCreateProcessLxss( + _In_ PWSTR LxssDistribution, + _In_ PWSTR LxssCommandLine, + _In_opt_ PWSTR LxssCurrentDirectory, + _Out_ PPH_STRING *Result + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json.c b/phlib/json.c similarity index 61% rename from plugins/CommonUtil/json.c rename to phlib/json.c index 8b1d3e8f062b..f59a479b0614 100644 --- a/plugins/CommonUtil/json.c +++ b/phlib/json.c @@ -1,8 +1,8 @@ /* - * Process Hacker Plugins - - * CommonUtil Plugin + * Process Hacker - + * json wrapper * - * Copyright (C) 2016 dmex + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -18,13 +18,16 @@ * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . - * */ -#include "main.h" -#include "json-c\json.h" +#include +#include +#include + +#include "jsonc\json.h" +#include -json_object_ptr json_get_object( +static json_object_ptr json_get_object( _In_ json_object_ptr rootObj, _In_ const PSTR key ) @@ -39,29 +42,34 @@ json_object_ptr json_get_object( return NULL; } -PVOID UtilCreateJsonParser( +PVOID PhCreateJsonParser( _In_ PSTR JsonString ) { return json_tokener_parse(JsonString); } -VOID UtilCleanupJsonParser( +VOID PhFreeJsonParser( _In_ PVOID Object ) { json_object_put(Object); } -PSTR UtilGetJsonValueAsString( +PPH_STRING PhGetJsonValueAsString( _In_ PVOID Object, _In_ PSTR Key ) { - return json_object_get_string(json_get_object(Object, Key)); + PCSTR value; + + if (value = json_object_get_string(json_get_object(Object, Key))) + return PhConvertUtf8ToUtf16((PSTR)value); + else + return NULL; } -INT64 UtilGetJsonValueAsUlong( +INT64 PhGetJsonValueAsLong64( _In_ PVOID Object, _In_ PSTR Key ) @@ -69,14 +77,14 @@ INT64 UtilGetJsonValueAsUlong( return json_object_get_int64(json_get_object(Object, Key)); } -PVOID UtilCreateJsonObject( +PVOID PhCreateJsonObject( VOID ) { return json_object_new_object(); } -PVOID UtilGetJsonObject( +PVOID PhGetJsonObject( _In_ PVOID Object, _In_ PSTR Key ) @@ -84,22 +92,22 @@ PVOID UtilGetJsonObject( return json_get_object(Object, Key); } -INT UtilGetJsonObjectLength( +INT PhGetJsonObjectLength( _In_ PVOID Object ) { return json_object_object_length(Object); } -BOOL UtilGetJsonObjectBool( +BOOLEAN PhGetJsonObjectBool( _In_ PVOID Object, _In_ PSTR Key ) { - return json_object_get_boolean(json_get_object(Object, Key)); + return json_object_get_boolean(json_get_object(Object, Key)) == TRUE; } -VOID UtilJsonAddObject( +VOID PhAddJsonObject( _In_ PVOID Object, _In_ PSTR Key, _In_ PSTR Value @@ -108,14 +116,14 @@ VOID UtilJsonAddObject( json_object_object_add(Object, Key, json_object_new_string(Value)); } -PVOID UtilCreateJsonArray( +PVOID PhCreateJsonArray( VOID ) { return json_object_new_array(); } -VOID UtilAddJsonArray( +VOID PhAddJsonArrayObject( _In_ PVOID Object, _In_ PVOID jsonEntry ) @@ -123,14 +131,19 @@ VOID UtilAddJsonArray( json_object_array_add(Object, jsonEntry); } -PSTR UtilGetJsonArrayString( +PPH_STRING PhGetJsonArrayString( _In_ PVOID Object ) { - return _strdup( json_object_to_json_string(Object) ); // leak + PCSTR value; + + if (value = json_object_to_json_string(Object)) + return PhConvertUtf8ToUtf16((PSTR)value); + else + return NULL; } -INT64 UtilGetJsonArrayUlong( +INT64 PhGetJsonArrayLong64( _In_ PVOID Object, _In_ INT Index ) @@ -138,14 +151,14 @@ INT64 UtilGetJsonArrayUlong( return json_object_get_int64(json_object_array_get_idx(Object, Index)); } -INT UtilGetArrayLength( +INT PhGetJsonArrayLength( _In_ PVOID Object ) { return json_object_array_length(Object); } -PVOID UtilGetObjectArrayIndex( +PVOID PhGetJsonArrayIndexObject( _In_ PVOID Object, _In_ INT Index ) @@ -153,25 +166,42 @@ PVOID UtilGetObjectArrayIndex( return json_object_array_get_idx(Object, Index); } -PPH_LIST UtilGetObjectArrayList( +PPH_LIST PhGetJsonObjectAsArrayList( _In_ PVOID Object ) { - json_object_iter object_iter; PPH_LIST listArray; + json_object_iter json_array_ptr; listArray = PhCreateList(1); - json_object_object_foreachC(Object, object_iter) + json_object_object_foreachC(Object, json_array_ptr) { - PJSON_ARRAY_LIST_OBJECT object = PhAllocate(sizeof(JSON_ARRAY_LIST_OBJECT)); + PJSON_ARRAY_LIST_OBJECT object; + + object = PhAllocate(sizeof(JSON_ARRAY_LIST_OBJECT)); memset(object, 0, sizeof(JSON_ARRAY_LIST_OBJECT)); - object->Key = object_iter.key; - object->Entry = object_iter.val; + object->Key = json_array_ptr.key; + object->Entry = json_array_ptr.val; PhAddItemList(listArray, object); } return listArray; -} \ No newline at end of file +} + +PVOID PhLoadJsonObjectFromFile( + _In_ PWSTR FileName + ) +{ + return json_object_from_file(FileName); +} + +VOID PhSaveJsonObjectToFile( + _In_ PWSTR FileName, + _In_ PVOID Object + ) +{ + json_object_to_file(FileName, Object); +} diff --git a/plugins/CommonUtil/json-c/AUTHORS b/phlib/jsonc/AUTHORS similarity index 100% rename from plugins/CommonUtil/json-c/AUTHORS rename to phlib/jsonc/AUTHORS diff --git a/plugins/CommonUtil/json-c/COPYING b/phlib/jsonc/COPYING similarity index 100% rename from plugins/CommonUtil/json-c/COPYING rename to phlib/jsonc/COPYING diff --git a/plugins/CommonUtil/json-c/ChangeLog b/phlib/jsonc/ChangeLog similarity index 100% rename from plugins/CommonUtil/json-c/ChangeLog rename to phlib/jsonc/ChangeLog diff --git a/plugins/CommonUtil/json-c/arraylist.c b/phlib/jsonc/arraylist.c similarity index 52% rename from plugins/CommonUtil/json-c/arraylist.c rename to phlib/jsonc/arraylist.c index 97f2c921711b..3354671b0f84 100644 --- a/plugins/CommonUtil/json-c/arraylist.c +++ b/phlib/jsonc/arraylist.c @@ -11,6 +11,8 @@ #include "config.h" +#include + #ifdef STDC_HEADERS # include # include @@ -20,7 +22,18 @@ # include #endif /* HAVE_STRINGS_H */ -#include "bits.h" +#ifndef SIZE_T_MAX +#if SIZEOF_SIZE_T == SIZEOF_INT +#define SIZE_T_MAX UINT_MAX +#elif SIZEOF_SIZE_T == SIZEOF_LONG +#define SIZE_T_MAX ULONG_MAX +#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG +#define SIZE_T_MAX ULLONG_MAX +#else +#error Unable to determine size of size_t +#endif +#endif + #include "arraylist.h" struct array_list* @@ -43,7 +56,7 @@ array_list_new(array_list_free_fn *free_fn) extern void array_list_free(struct array_list *arr) { - int i; + size_t i; for(i = 0; i < arr->length; i++) if(arr->array[i]) arr->free_fn(arr->array[i]); free(arr->array); @@ -51,20 +64,29 @@ array_list_free(struct array_list *arr) } void* -array_list_get_idx(struct array_list *arr, int i) +array_list_get_idx(struct array_list *arr, size_t i) { if(i >= arr->length) return NULL; return arr->array[i]; } -static int array_list_expand_internal(struct array_list *arr, int max) +static int array_list_expand_internal(struct array_list *arr, size_t max) { void *t; - int new_size; + size_t new_size; if(max < arr->size) return 0; - new_size = json_max(arr->size << 1, max); - if(!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; + /* Avoid undefined behaviour on size_t overflow */ + if( arr->size >= SIZE_T_MAX / 2 ) + new_size = max; + else + { + new_size = arr->size << 1; + if (new_size < max) + new_size = max; + } + if (new_size > (~((size_t)0)) / sizeof(void*)) return -1; + if (!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; arr->array = (void**)t; (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); arr->size = new_size; @@ -72,10 +94,12 @@ static int array_list_expand_internal(struct array_list *arr, int max) } int -array_list_put_idx(struct array_list *arr, int idx, void *data) +array_list_put_idx(struct array_list *arr, size_t idx, void *data) { + if (idx > SIZE_T_MAX - 1 ) return -1; if(array_list_expand_internal(arr, idx+1)) return -1; - if(arr->array[idx]) arr->free_fn(arr->array[idx]); + if(idx < arr->length && arr->array[idx]) + arr->free_fn(arr->array[idx]); arr->array[idx] = data; if(arr->length <= idx) arr->length = idx + 1; return 0; @@ -90,12 +114,33 @@ array_list_add(struct array_list *arr, void *data) void array_list_sort(struct array_list *arr, int(__cdecl* sort_fn)(const void *, const void *)) { - qsort(arr->array, arr->length, sizeof(arr->array[0]), - (int (__cdecl*)(const void *, const void *))sort_fn); + qsort(arr->array, arr->length, sizeof(arr->array[0]), sort_fn); } -int +void* array_list_bsearch(const void **key, struct array_list *arr, + int (__cdecl* sort_fn)(const void *, const void *)) +{ + return bsearch(key, arr->array, arr->length, sizeof(arr->array[0]), + sort_fn); +} + +size_t array_list_length(struct array_list *arr) { return arr->length; } + +int +array_list_del_idx( struct array_list *arr, size_t idx, size_t count ) +{ + size_t i, stop; + + stop = idx + count; + if ( idx >= arr->length || stop > arr->length ) return -1; + for ( i = idx; i < stop; ++i ) { + if ( arr->array[i] ) arr->free_fn( arr->array[i] ); + } + memmove( arr->array + idx, arr->array + stop, (arr->length - stop) * sizeof(void*) ); + arr->length -= count; + return 0; +} diff --git a/plugins/CommonUtil/json-c/arraylist.h b/phlib/jsonc/arraylist.h similarity index 60% rename from plugins/CommonUtil/json-c/arraylist.h rename to phlib/jsonc/arraylist.h index 089be0bd7215..546b375a3ee0 100644 --- a/plugins/CommonUtil/json-c/arraylist.h +++ b/phlib/jsonc/arraylist.h @@ -9,6 +9,12 @@ * */ +/** + * @file + * @brief Internal methods for working with json_type_array objects. + * Although this is exposed by the json_object_get_array() method, + * it is not recommended for direct use. + */ #ifndef _arraylist_h_ #define _arraylist_h_ @@ -23,10 +29,11 @@ typedef void (array_list_free_fn) (void *data); struct array_list { void **array; - int length; - int size; + size_t length; + size_t size; array_list_free_fn *free_fn; }; +typedef struct array_list array_list; extern struct array_list* array_list_new(array_list_free_fn *free_fn); @@ -35,20 +42,27 @@ extern void array_list_free(struct array_list *al); extern void* -array_list_get_idx(struct array_list *al, int i); +array_list_get_idx(struct array_list *al, size_t i); extern int -array_list_put_idx(struct array_list *al, int i, void *data); +array_list_put_idx(struct array_list *al, size_t i, void *data); extern int array_list_add(struct array_list *al, void *data); -extern int +extern size_t array_list_length(struct array_list *al); extern void array_list_sort(struct array_list *arr, int(__cdecl* compar)(const void *, const void *)); +extern void* array_list_bsearch(const void **key, + struct array_list *arr, + int (__cdecl* sort_fn)(const void *, const void *)); + +extern int +array_list_del_idx(struct array_list *arr, size_t idx, size_t count); + #ifdef __cplusplus } #endif diff --git a/plugins/CommonUtil/json-c/bits.h b/phlib/jsonc/bits.h similarity index 64% rename from plugins/CommonUtil/json-c/bits.h rename to phlib/jsonc/bits.h index c8cbbc820d5b..14c1c132d374 100644 --- a/plugins/CommonUtil/json-c/bits.h +++ b/phlib/jsonc/bits.h @@ -1,4 +1,8 @@ -/* +/** + * @file + * @brief Do not use, only contains deprecated defines. + * @deprecated Use json_util.h instead. + * * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. @@ -12,17 +16,21 @@ #ifndef _bits_h_ #define _bits_h_ -#ifndef json_min -#define json_min(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef json_max -#define json_max(a,b) ((a) > (b) ? (a) : (b)) -#endif - +/** + * @deprecated + */ #define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +/** + * @deprecated + */ #define error_ptr(error) ((void*)error) -#define error_description(error) (json_tokener_errors[error]) +/** + * @deprecated + */ +#define error_description(error) (json_tokener_get_error(error)) +/** + * @deprecated + */ #define is_error(ptr) (ptr == NULL) #endif diff --git a/phlib/jsonc/config.h b/phlib/jsonc/config.h new file mode 100644 index 000000000000..b84c3ba26910 --- /dev/null +++ b/phlib/jsonc/config.h @@ -0,0 +1,200 @@ +/* Enable RDRANR Hardware RNG Hash Seed */ +#undef ENABLE_RDRAND + +/* Define if .gnu.warning accepts long strings. */ +#undef HAS_GNU_WARNING_LONG + +/* Define to 1 if you have the declaration of `INFINITY', and to 0 if you +don't. */ +#if (defined(_MSC_VER) && _MSC_VER >= 1800) || defined(__MINGW32__) +#define HAVE_DECL_INFINITY 1 +#endif + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. +*/ +#if (defined(_MSC_VER) && _MSC_VER >= 1800) || defined(__MINGW32__) +#define HAVE_DECL_ISINF 1 +#endif + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. +*/ +#if (defined(_MSC_VER) && _MSC_VER >= 1800) || defined(__MINGW32__) +#define HAVE_DECL_ISNAN 1 +#endif + +/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ +#if (defined(_MSC_VER) && _MSC_VER >= 1800) || defined(__MINGW32__) +#define HAVE_DECL_NAN 1 +#endif + +/* Define to 1 if you have the declaration of `_finite', and to 0 if you +don't. */ +#define HAVE_DECL__FINITE 1 + +/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. +*/ +#define HAVE_DECL__ISNAN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#define HAVE_DOPRNT 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and +to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, +and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strdup' function. */ +//#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#if defined(__MINGW32__) +#define HAVE_STRNCASECMP 1 +#else +#undef HAVE_STRNCASECMP +#endif + +//#cmakedefine HAVE_STRTOLL +//#cmakedefine strtoll @cmake_strtoll@ +#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_SYS_PARAM_H 1 +#else +#undef HAVE_SYS_PARAM_H +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#if defined(__MINGW32__) +#define HAVE_UNISTD_H 1 +#else +#undef HAVE_UNISTD_H +#endif + +/* Define to 1 if you have the `vasprintf' function. */ +#if defined(__MINGW32__) +#define HAVE_VASPRINTF 1 +#else +#undef HAVE_VASPRINTF +#endif + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Define to the sub-directory in which libtool stores uninstalled libraries. +*/ +#undef LT_OBJDIR + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Name of package */ +#define PACKAGE "json-c" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "json-c@googlegroups.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "JSON C Library" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "JSON C Library 0.13.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "json-c" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://github.com/json-c/json-c" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.13.1" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.13.1" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/plugins/CommonUtil/json-c/config.h.in b/phlib/jsonc/config.h.in similarity index 100% rename from plugins/CommonUtil/json-c/config.h.in rename to phlib/jsonc/config.h.in diff --git a/plugins/CommonUtil/json-c/debug.c b/phlib/jsonc/debug.c similarity index 99% rename from plugins/CommonUtil/json-c/debug.c rename to phlib/jsonc/debug.c index 9dff7818bf86..8504d0f59512 100644 --- a/plugins/CommonUtil/json-c/debug.c +++ b/phlib/jsonc/debug.c @@ -76,7 +76,7 @@ void mc_info(const char *msg, ...) #if HAVE_VSYSLOG if(_syslog) { vsyslog(LOG_INFO, msg, ap); - } else + } else #endif vfprintf(stderr, msg, ap); va_end(ap); diff --git a/plugins/CommonUtil/json-c/debug.h b/phlib/jsonc/debug.h similarity index 94% rename from plugins/CommonUtil/json-c/debug.h rename to phlib/jsonc/debug.h index 80ca3e43042e..07fcc380b3c6 100644 --- a/plugins/CommonUtil/json-c/debug.h +++ b/phlib/jsonc/debug.h @@ -10,6 +10,10 @@ * */ +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ #ifndef _DEBUG_H_ #define _DEBUG_H_ diff --git a/plugins/CommonUtil/json-c/json.h b/phlib/jsonc/json.h similarity index 84% rename from plugins/CommonUtil/json-c/json.h rename to phlib/jsonc/json.h index 4339b20e9060..fc2daddee290 100644 --- a/plugins/CommonUtil/json-c/json.h +++ b/phlib/jsonc/json.h @@ -10,6 +10,10 @@ * */ +/** + * @file + * @brief A convenience header that may be included instead of other individual ones. + */ #ifndef _json_h_ #define _json_h_ @@ -17,12 +21,12 @@ extern "C" { #endif -#include "bits.h" #include "debug.h" #include "linkhash.h" #include "arraylist.h" #include "json_util.h" #include "json_object.h" +#include "json_pointer.h" #include "json_tokener.h" #include "json_object_iterator.h" #include "json_c_version.h" diff --git a/plugins/CommonUtil/json-c/json_c_version.c b/phlib/jsonc/json_c_version.c similarity index 100% rename from plugins/CommonUtil/json-c/json_c_version.c rename to phlib/jsonc/json_c_version.c diff --git a/phlib/jsonc/json_c_version.h b/phlib/jsonc/json_c_version.h new file mode 100644 index 000000000000..98838be9469a --- /dev/null +++ b/phlib/jsonc/json_c_version.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012,2017 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +/** + * @file + * @brief Methods for retrieving the json-c version. + */ +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 13 +#define JSON_C_MICRO_VERSION 01 +#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ + (JSON_C_MINOR_VERSION << 8) | \ + JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.13.1" + +/** + * @see JSON_C_VERSION + * @return the version of the json-c library as a string + */ +const char *json_c_version(void); /* Returns JSON_C_VERSION */ + +/** + * The json-c version encoded into an int, with the low order 8 bits + * being the micro version, the next higher 8 bits being the minor version + * and the next higher 8 bits being the major version. + * For example, 7.12.99 would be 0x00070B63. + * + * @see JSON_C_VERSION_NUM + * @return the version of the json-c library as an int + */ +int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#endif diff --git a/plugins/CommonUtil/json-c/json_config.h b/phlib/jsonc/json_config.h similarity index 100% rename from plugins/CommonUtil/json-c/json_config.h rename to phlib/jsonc/json_config.h diff --git a/phlib/jsonc/json_inttypes.h b/phlib/jsonc/json_inttypes.h new file mode 100644 index 000000000000..3854913fc58a --- /dev/null +++ b/phlib/jsonc/json_inttypes.h @@ -0,0 +1,23 @@ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#ifdef JSON_C_HAVE_INTTYPES_H +/* inttypes.h includes stdint.h */ +#include + +#else +#include + +#define PRId64 "I64d" +#define SCNd64 "I64d" + +#endif + +#endif diff --git a/phlib/jsonc/json_object.c b/phlib/jsonc/json_object.c new file mode 100644 index 000000000000..5d8155c1f28c --- /dev/null +++ b/phlib/jsonc/json_object.c @@ -0,0 +1,1497 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#pragma warning(push) +#pragma warning(disable: 4996) + +#include "config.h" + +#include "strerror_override.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" +#include "math_compat.h" +#include "strdup_compat.h" +#include "snprintf_compat.h" + +#if SIZEOF_LONG_LONG != SIZEOF_INT64_T +#error "The long long type isn't 64-bits" +#endif + +// Don't define this. It's not thread-safe. +/* #define REFCOUNT_DEBUG 1 */ + +const char *json_number_chars = "0123456789.+-eE"; +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object* jso); +static struct json_object* json_object_new(enum json_type o_type); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string_default; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init(void) __attribute__ ((constructor)); +static void json_object_init(void) { + MC_DEBUG("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, NULL); +} + +static void json_object_fini(void) __attribute__ ((destructor)); +static void json_object_fini(void) +{ + struct lh_entry *ent; + if (MC_GET_DEBUG()) + { + if (json_object_table->count) + { + MC_DEBUG("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) + { + struct json_object* obj = + (struct json_object*) lh_entry_v(ent); + MC_DEBUG("\t%s:%p\n", + json_type_to_name(obj->o_type), obj); + } + } + } + MC_DEBUG("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* helper for accessing the optimized string data component in json_object + */ +static const char * +get_string_component(const struct json_object *jso) +{ + return (jso->o.c_string.len < LEN_DIRECT_STRING_DATA) ? + jso->o.c_string.str.data : jso->o.c_string.str.ptr; +} + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, const char *str, int len, int flags) +{ + int pos = 0, start_offset = 0; + unsigned char c; + while (len--) + { + c = str[pos]; + switch(c) + { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if((flags & JSON_C_TO_STRING_NOSLASHESCAPE) && c == '/') + { + pos++; + break; + } + + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '\f') printbuf_memappend(pb, "\\f", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + + start_offset = ++pos; + break; + default: + if(c < ' ') + { + char sbuf[7]; + if(pos - start_offset > 0) + printbuf_memappend(pb, + str + start_offset, + pos - start_offset); + snprintf(sbuf, sizeof(sbuf), + "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + printbuf_memappend_fast(pb, sbuf, (int) sizeof(sbuf) - 1); + start_offset = ++pos; + } else + pos++; + } + } + if (pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *jso) +{ + if (!jso) return jso; + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + __sync_add_and_fetch(&jso->_ref_count, 1); +#else + ++jso->_ref_count; +#endif + + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if(!jso) return 0; + + /* Avoid invalid free and crash explicitly instead of (silently) + * segfaulting. + */ + assert(jso->_ref_count > 0); + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + /* Note: this only allow the refcount to remain correct + * when multiple threads are adjusting it. It is still an error + * for a thread to decrement the refcount if it doesn't "own" it, + * as that can result in the thread that loses the race to 0 + * operating on an already-freed object. + */ + if (__sync_sub_and_fetch(&jso->_ref_count, 1) > 0) return 0; +#else + if (--jso->_ref_count > 0) return 0; +#endif + + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + jso->_delete(jso); + return 1; +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* jso) +{ +#ifdef REFCOUNT_DEBUG + MC_DEBUG("json_object_delete_%s: %p\n", + json_type_to_name(jso->o_type), jso); + lh_table_delete(json_object_table, jso); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(jso->_pb); + free(jso); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *jso; + + jso = (struct json_object*)calloc(sizeof(struct json_object), 1); + if (!jso) + return NULL; + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, jso, jso); + MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); +#endif /* REFCOUNT_DEBUG */ + return jso; +} + + +/* type checking functions */ + +int json_object_is_type(const struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(const struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +void* json_object_get_userdata(json_object *jso) { + return jso ? jso->_userdata : NULL; +} + +void json_object_set_userdata(json_object *jso, void *userdata, + json_object_delete_fn *user_delete) +{ + // Can't return failure, so abort if we can't perform the operation. + assert(jso != NULL); + + // First, clean up any previously existing user info + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn *to_string_func, + void *userdata, + json_object_delete_fn *user_delete) +{ + json_object_set_userdata(jso, userdata, user_delete); + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch(jso->o_type) + { + case json_type_null: + jso->_to_json_string = NULL; + break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string_default; + break; + case json_type_int: + jso->_to_json_string = &json_object_int_to_json_string; + break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; +} + + +/* extended conversion to string */ + +const char* json_object_to_json_string_length(struct json_object *jso, int flags, size_t *length) +{ + const char *r = NULL; + size_t s = 0; + + if (!jso) + { + s = 4; + r = "null"; + } + else if ((jso->_pb) || (jso->_pb = printbuf_new())) + { + printbuf_reset(jso->_pb); + + if(jso->_to_json_string(jso, jso->_pb, 0, flags) >= 0) + { + s = (size_t)jso->_pb->bpos; + r = jso->_pb->buf; + } + } + + if (length) + *length = s; + return r; +} + +const char* json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + return json_object_to_json_string_length(jso, flags, NULL); +} + +/* backwards-compatible conversion to string */ + +const char* json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (flags & JSON_C_TO_STRING_PRETTY_TAB) + { + printbuf_memset(pb, -1, '\t', level); + } + else + { + printbuf_memset(pb, -1, ' ', level * 2); + } + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + printbuf_strappend(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, " "); + indent(pb, level+1, flags); + printbuf_strappend(pb, "\""); + json_escape_str(pb, iter.key, (int)strlen(iter.key), flags); + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, "\": "); + else + printbuf_strappend(pb, "\":"); + if(iter.val == NULL) + printbuf_strappend(pb, "null"); + else + if (iter.val->_to_json_string(iter.val, pb, level+1,flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb,level,flags); + } + if (flags & JSON_C_TO_STRING_SPACED) + return (int)printbuf_strappend(pb, /*{*/ " }"); + else + return (int)printbuf_strappend(pb, /*{*/ "}"); +} + + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + if (!ent->k_is_constant) + free(lh_entry_k(ent)); + json_object_put((struct json_object*)lh_entry_v(ent)); +} + +static void json_object_object_delete(struct json_object* jso) +{ + lh_table_free(jso->o.c_object); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_object(void) +{ + struct json_object *jso = json_object_new(json_type_object); + if (!jso) + return NULL; + jso->_delete = &json_object_object_delete; + jso->_to_json_string = &json_object_object_to_json_string; + jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, + &json_object_lh_entry_free); + if (!jso->o.c_object) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + return jso; +} + +struct lh_table* json_object_get_object(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch(jso->o_type) + { + case json_type_object: + return jso->o.c_object; + default: + return NULL; + } +} + +int json_object_object_add_ex(struct json_object* jso, + const char *const key, + struct json_object *const val, + const unsigned opts) +{ + struct json_object *existing_value = NULL; + struct lh_entry *existing_entry; + unsigned long hash; + + assert(json_object_get_type(jso) == json_type_object); + + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + hash = lh_get_hash(jso->o.c_object, (const void *)key); + existing_entry = (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) ? NULL : + lh_table_lookup_entry_w_hash(jso->o.c_object, + (const void *)key, hash); + + // The caller must avoid creating loops in the object tree, but do a + // quick check anyway to make sure we're not creating a trivial loop. + if (jso == val) + return -1; + + if (!existing_entry) + { + const void *const k = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT) ? + (const void *)key : strdup(key); + if (k == NULL) + return -1; + return lh_table_insert_w_hash(jso->o.c_object, k, val, hash, opts); + } + existing_value = (json_object *) lh_entry_v(existing_entry); + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; + return 0; +} + +int json_object_object_add(struct json_object* jso, const char *key, + struct json_object *val) +{ + return json_object_object_add_ex(jso, key, val, 0); +} + + +int json_object_object_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_object); + return lh_table_length(jso->o.c_object); +} + +size_t json_c_object_sizeof(void) +{ + return sizeof(struct json_object); +} + +struct json_object* json_object_object_get(const struct json_object* jso, + const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(const struct json_object* jso, const char *key, + struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return FALSE; + + switch(jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (const void *) key, + (void**) value); + default: + if (value != NULL) + *value = NULL; + return FALSE; + } +} + +void json_object_object_del(struct json_object* jso, const char *key) +{ + assert(json_object_get_type(jso) == json_type_object); + lh_table_delete(jso->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + if (jso->o.c_boolean) + return printbuf_strappend(pb, "true"); + return printbuf_strappend(pb, "false"); +} + +struct json_object* json_object_new_boolean(json_bool b) +{ + struct json_object *jso = json_object_new(json_type_boolean); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_boolean_to_json_string; + jso->o.c_boolean = b; + return jso; +} + +json_bool json_object_get_boolean(const struct json_object *jso) +{ + if (!jso) + return FALSE; + switch(jso->o_type) + { + case json_type_boolean: + return jso->o.c_boolean; + case json_type_int: + return (jso->o.c_int64 != 0); + case json_type_double: + return (jso->o.c_double != 0); + case json_type_string: + return (jso->o.c_string.len != 0); + default: + return FALSE; + } +} + +int json_object_set_boolean(struct json_object *jso,json_bool new_value){ + if (!jso || jso->o_type!=json_type_boolean) + return 0; + jso->o.c_boolean=new_value; + return 1; +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + /* room for 19 digits, the sign char, and a null term */ + char sbuf[21]; + snprintf(sbuf, sizeof(sbuf), "%" PRId64, jso->o.c_int64); + return printbuf_memappend (pb, sbuf, strlen(sbuf)); +} + +struct json_object* json_object_new_int(int32_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int32_t json_object_get_int(const struct json_object *jso) +{ + int64_t cint64; + enum json_type o_type; + + if(!jso) return 0; + + o_type = jso->o_type; + cint64 = jso->o.c_int64; + + if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(get_string_component(jso), &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch(o_type) { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + if (cint64 >= INT32_MAX) + return INT32_MAX; + return (int32_t) cint64; + case json_type_double: + if (jso->o.c_double <= INT32_MIN) + return INT32_MIN; + if (jso->o.c_double >= INT32_MAX) + return INT32_MAX; + return (int32_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + default: + return 0; + } +} + +int json_object_set_int(struct json_object *jso,int new_value){ + if (!jso || jso->o_type!=json_type_int) + return 0; + jso->o.c_int64=new_value; + return 1; +} + +struct json_object* json_object_new_int64(int64_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int64_t json_object_get_int64(const struct json_object *jso) +{ + int64_t cint; + + if (!jso) + return 0; + switch(jso->o_type) + { + case json_type_int: + return jso->o.c_int64; + case json_type_double: + if (jso->o.c_double >= INT64_MAX) + return INT64_MAX; + if (jso->o.c_double <= INT64_MIN) + return INT64_MIN; + return (int64_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if (json_parse_int64(get_string_component(jso), &cint) == 0) + return cint; + /* FALLTHRU */ + default: + return 0; + } +} + +int json_object_set_int64(struct json_object *jso,int64_t new_value){ + if (!jso || jso->o_type!=json_type_int) + return 0; + jso->o.c_int64=new_value; + return 1; +} + +int json_object_int_inc(struct json_object *jso, int64_t val) { + if (!jso || jso->o_type != json_type_int) + return 0; + if (val > 0 && jso->o.c_int64 > INT64_MAX - val) { + jso->o.c_int64 = INT64_MAX; + } else if (val < 0 && jso->o.c_int64 < INT64_MIN - val) { + jso->o.c_int64 = INT64_MIN; + } else { + jso->o.c_int64 += val; + } + return 1; +} + +/* json_object_double */ + +#if defined(HAVE___THREAD) +// i.e. __thread or __declspec(thread) +static SPEC___THREAD char *tls_serialization_float_format = NULL; +#endif +static char *global_serialization_float_format = NULL; + +int json_c_set_serialization_double_format(const char *double_format, int global_or_thread) +{ + if (global_or_thread == JSON_C_OPTION_GLOBAL) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } +#endif + if (global_serialization_float_format) + free(global_serialization_float_format); + global_serialization_float_format = double_format ? strdup(double_format) : NULL; + } + else if (global_or_thread == JSON_C_OPTION_THREAD) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } + tls_serialization_float_format = double_format ? strdup(double_format) : NULL; +#else + _json_c_set_last_err("json_c_set_option: not compiled with __thread support\n"); + return -1; +#endif + } + else + { + _json_c_set_last_err("json_c_set_option: invalid global_or_thread value: %d\n", global_or_thread); + return -1; + } + return 0; +} + + +static int json_object_double_to_json_string_format(struct json_object* jso, + struct printbuf *pb, + int level, + int flags, + const char *format) +{ + char buf[128], *p, *q; + size_t size; + /* Although JSON RFC does not support + NaN or Infinity as numeric values + ECMA 262 section 9.8.1 defines + how to handle these cases as strings */ + if (isnan(jso->o.c_double)) + { + size = snprintf(buf, sizeof(buf), "NaN"); + } + else if (isinf(jso->o.c_double)) + { + if(jso->o.c_double > 0) + size = snprintf(buf, sizeof(buf), "Infinity"); + else + size = snprintf(buf, sizeof(buf), "-Infinity"); + } + else + { + const char *std_format = "%.17g"; + int format_drops_decimals = 0; + + if (!format) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + format = tls_serialization_float_format; + else +#endif + if (global_serialization_float_format) + format = global_serialization_float_format; + else + format = std_format; + } + size = snprintf(buf, sizeof(buf), format, jso->o.c_double); + + if (size < 0) + return -1; + + p = strchr(buf, ','); + if (p) + *p = '.'; + else + p = strchr(buf, '.'); + + if (format == std_format || strstr(format, ".0f") == NULL) + format_drops_decimals = 1; + + if (size < (int)sizeof(buf) - 2 && + isdigit((int)buf[0]) && /* Looks like *some* kind of number */ + !p && /* Has no decimal point */ + strchr(buf, 'e') == NULL && /* Not scientific notation */ + format_drops_decimals) + { + // Ensure it looks like a float, even if snprintf didn't, + // unless a custom format is set to omit the decimal. + strcat(buf, ".0"); + size += 2; + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) + { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + } + // although unlikely, snprintf can fail + if (size < 0) + return -1; + + if (size >= (int)sizeof(buf)) + // The standard formats are guaranteed not to overrun the buffer, + // but if a custom one happens to do so, just silently truncate. + size = sizeof(buf) - 1; + printbuf_memappend(pb, buf, size); + return (int)size; +} + +static int json_object_double_to_json_string_default(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + NULL); +} + +int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + (const char *)jso->_userdata); +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *jso = json_object_new(json_type_double); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_double_to_json_string_default; + jso->o.c_double = d; + return jso; +} + +struct json_object* json_object_new_double_s(double d, const char *ds) +{ + char *new_ds; + struct json_object *jso = json_object_new_double(d); + if (!jso) + return NULL; + + new_ds = strdup(ds); + if (!new_ds) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + json_object_set_serializer(jso, json_object_userdata_to_json_string, + new_ds, json_object_free_userdata); + return jso; +} + +int json_object_userdata_to_json_string(struct json_object *jso, + struct printbuf *pb, int level, int flags) +{ + size_t userdata_len = strlen((const char *)jso->_userdata); + printbuf_memappend(pb, (const char *)jso->_userdata, userdata_len); + return (int)userdata_len; +} + +void json_object_free_userdata(struct json_object *jso, void *userdata) +{ + free(userdata); +} + +double json_object_get_double(const struct json_object *jso) +{ + double cdouble; + char *errPtr = NULL; + + if(!jso) return 0.0; + switch(jso->o_type) { + case json_type_double: + return jso->o.c_double; + case json_type_int: + return (double)jso->o.c_int64; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + errno = 0; + cdouble = strtod(get_string_component(jso), &errPtr); + + /* if conversion stopped at the first character, return 0.0 */ + if (errPtr == get_string_component(jso)) + return 0.0; + + /* + * Check that the conversion terminated on something sensible + * + * For example, { "pay" : 123AB } would parse as 123. + */ + if (*errPtr != '\0') + return 0.0; + + /* + * If strtod encounters a string which would exceed the + * capacity of a double, it returns +/- HUGE_VAL and sets + * errno to ERANGE. But +/- HUGE_VAL is also a valid result + * from a conversion, so we need to check errno. + * + * Underflow also sets errno to ERANGE, but it returns 0 in + * that case, which is what we will return anyway. + * + * See CERT guideline ERR30-C + */ + if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && + (ERANGE == errno)) + cdouble = 0.0; + return cdouble; + default: + return 0.0; + } +} + +int json_object_set_double(struct json_object *jso,double new_value){ + if (!jso || jso->o_type!=json_type_double) + return 0; + jso->o.c_double=new_value; + return 1; +} + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + printbuf_strappend(pb, "\""); + json_escape_str(pb, get_string_component(jso), jso->o.c_string.len, flags); + printbuf_strappend(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* jso) +{ + if(jso->o.c_string.len >= LEN_DIRECT_STRING_DATA) + free(jso->o.c_string.str.ptr); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_string(const char *s) +{ + struct json_object *jso = json_object_new(json_type_string); + if (!jso) + return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.len = (int)strlen(s); + if(jso->o.c_string.len < LEN_DIRECT_STRING_DATA) { + memcpy(jso->o.c_string.str.data, s, jso->o.c_string.len); + } else { + jso->o.c_string.str.ptr = strdup(s); + if (!jso->o.c_string.str.ptr) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + } + return jso; +} + +struct json_object* json_object_new_string_len(const char *s, int len) +{ + char *dstbuf; + struct json_object *jso = json_object_new(json_type_string); + if (!jso) + return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + if(len < LEN_DIRECT_STRING_DATA) { + dstbuf = jso->o.c_string.str.data; + } else { + jso->o.c_string.str.ptr = (char*)malloc(len + 1); + if (!jso->o.c_string.str.ptr) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + dstbuf = jso->o.c_string.str.ptr; + } + memcpy(dstbuf, (const void *)s, len); + dstbuf[len] = '\0'; + jso->o.c_string.len = len; + return jso; +} + +const char* json_object_get_string(struct json_object *jso) +{ + if (!jso) + return NULL; + switch(jso->o_type) + { + case json_type_string: + return get_string_component(jso); + default: + return json_object_to_json_string(jso); + } +} + +int json_object_get_string_len(const struct json_object *jso) +{ + if (!jso) + return 0; + switch(jso->o_type) + { + case json_type_string: + return jso->o.c_string.len; + default: + return 0; + } +} + +int json_object_set_string(json_object* jso, const char* s) { + return json_object_set_string_len(jso, s, (int)(strlen(s))); +} + +int json_object_set_string_len(json_object* jso, const char* s, int len){ + char *dstbuf; + if (jso==NULL || jso->o_type!=json_type_string) return 0; + if (leno.c_string.str.data; + if (jso->o.c_string.len>=LEN_DIRECT_STRING_DATA) free(jso->o.c_string.str.ptr); + } else { + dstbuf=(char *)malloc(len+1); + if (dstbuf==NULL) return 0; + if (jso->o.c_string.len>=LEN_DIRECT_STRING_DATA) free(jso->o.c_string.str.ptr); + jso->o.c_string.str.ptr=dstbuf; + } + jso->o.c_string.len=len; + memcpy(dstbuf, (const void *)s, len); + dstbuf[len] = '\0'; + return 1; +} + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + size_t ii; + + printbuf_strappend(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + for(ii=0; ii < (size_t)json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if(val == NULL) + printbuf_strappend(pb, "null"); + else + if (val->_to_json_string(val, pb, level+1, flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb,level,flags); + } + + if (flags & JSON_C_TO_STRING_SPACED) + return printbuf_strappend(pb, " ]"); + return printbuf_strappend(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* jso) +{ + array_list_free(jso->o.c_array); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_array(void) +{ + struct json_object *jso = json_object_new(json_type_array); + if (!jso) + return NULL; + jso->_delete = &json_object_array_delete; + jso->_to_json_string = &json_object_array_to_json_string; + jso->o.c_array = array_list_new(&json_object_array_entry_free); + if(jso->o.c_array == NULL) + { + free(jso); + return NULL; + } + return jso; +} + +struct array_list* json_object_get_array(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch(jso->o_type) + { + case json_type_array: + return jso->o.c_array; + default: + return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, + int(__cdecl* sort_fn)(const void *, const void *)) +{ + assert(json_object_get_type(jso) == json_type_array); + array_list_sort(jso->o.c_array, sort_fn); +} + +struct json_object* json_object_array_bsearch( + const struct json_object *key, + const struct json_object *jso, + int (__cdecl* sort_fn)(const void *, const void *)) +{ + struct json_object **result; + + assert(json_object_get_type(jso) == json_type_array); + result = (struct json_object **)array_list_bsearch( + (const void **)(void *)&key, jso->o.c_array, sort_fn); + + if (!result) + return NULL; + return *result; +} + +int json_object_array_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_array); + return (int)array_list_length(jso->o.c_array); +} + +int json_object_array_add(struct json_object *jso,struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_add(jso->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, size_t idx, + struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_put_idx(jso->o.c_array, idx, val); +} + +int json_object_array_del_idx(struct json_object *jso, size_t idx, size_t count) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_del_idx(jso->o.c_array, idx, count); +} + +struct json_object* json_object_array_get_idx(const struct json_object *jso, + size_t idx) +{ + assert(json_object_get_type(jso) == json_type_array); + return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); +} + +static int json_array_equal(struct json_object* jso1, + struct json_object* jso2) +{ + size_t len, i; + + len = json_object_array_length(jso1); + if (len != json_object_array_length(jso2)) + return 0; + + for (i = 0; i < len; i++) { + if (!json_object_equal(json_object_array_get_idx(jso1, i), + json_object_array_get_idx(jso2, i))) + return 0; + } + return 1; +} + +static int json_object_all_values_equal(struct json_object* jso1, + struct json_object* jso2) +{ + struct json_object_iter iter; + struct json_object *sub; + + assert(json_object_get_type(jso1) == json_type_object); + assert(json_object_get_type(jso2) == json_type_object); + /* Iterate over jso1 keys and see if they exist and are equal in jso2 */ + json_object_object_foreachC(jso1, iter) { + if (!lh_table_lookup_ex(jso2->o.c_object, (void*)iter.key, + (void**)(void *)&sub)) + return 0; + if (!json_object_equal(iter.val, sub)) + return 0; + } + + /* Iterate over jso2 keys to see if any exist that are not in jso1 */ + json_object_object_foreachC(jso2, iter) { + if (!lh_table_lookup_ex(jso1->o.c_object, (void*)iter.key, + (void**)(void *)&sub)) + return 0; + } + + return 1; +} + +int json_object_equal(struct json_object* jso1, struct json_object* jso2) +{ + if (jso1 == jso2) + return 1; + + if (!jso1 || !jso2) + return 0; + + if (jso1->o_type != jso2->o_type) + return 0; + + switch(jso1->o_type) { + case json_type_boolean: + return (jso1->o.c_boolean == jso2->o.c_boolean); + + case json_type_double: + return (jso1->o.c_double == jso2->o.c_double); + + case json_type_int: + return (jso1->o.c_int64 == jso2->o.c_int64); + + case json_type_string: + return (jso1->o.c_string.len == jso2->o.c_string.len && + memcmp(get_string_component(jso1), + get_string_component(jso2), + jso1->o.c_string.len) == 0); + + case json_type_object: + return json_object_all_values_equal(jso1, jso2); + + case json_type_array: + return json_array_equal(jso1, jso2); + + case json_type_null: + return 1; + }; + + return 0; +} + +static int json_object_copy_serializer_data(struct json_object *src, struct json_object *dst) +{ + if (!src->_userdata && !src->_user_delete) + return 0; + + if (dst->_to_json_string == json_object_userdata_to_json_string) + { + dst->_userdata = strdup(src->_userdata); + } + // else if ... other supported serializers ... + else + { + _json_c_set_last_err("json_object_deep_copy: unable to copy unknown serializer data: %p\n", dst->_to_json_string); + return -1; + } + dst->_user_delete = src->_user_delete; + return 0; +} + + +/** + * The default shallow copy implementation. Simply creates a new object of the same + * type but does *not* copy over _userdata nor retain any custom serializer. + * If custom serializers are in use, json_object_deep_copy() must be passed a shallow copy + * implementation that is aware of how to copy them. + * + * This always returns -1 or 1. It will never return 2 since it does not copy the serializer. + */ +int json_c_shallow_copy_default(json_object *src, json_object *parent, const char *key, size_t index, json_object **dst) +{ + switch (src->o_type) { + case json_type_boolean: + *dst = json_object_new_boolean(src->o.c_boolean); + break; + + case json_type_double: + *dst = json_object_new_double(src->o.c_double); + break; + + case json_type_int: + *dst = json_object_new_int64(src->o.c_int64); + break; + + case json_type_string: + *dst = json_object_new_string(get_string_component(src)); + break; + + case json_type_object: + *dst = json_object_new_object(); + break; + + case json_type_array: + *dst = json_object_new_array(); + break; + + default: + errno = EINVAL; + return -1; + } + + if (!*dst) { + errno = ENOMEM; + return -1; + } + (*dst)->_to_json_string = src->_to_json_string; + // _userdata and _user_delete are copied later + return 1; +} + +/* + * The actual guts of json_object_deep_copy(), with a few additional args + * needed so we can keep track of where we are within the object tree. + * + * Note: caller is responsible for freeing *dst if this fails and returns -1. + */ +static int json_object_deep_copy_recursive(struct json_object *src, struct json_object *parent, const char *key_in_parent, size_t index_in_parent, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy) +{ + struct json_object_iter iter; + size_t src_array_len, ii; + + int shallow_copy_rc = 0; + shallow_copy_rc = shallow_copy(src, parent, key_in_parent, index_in_parent, dst); + /* -1=error, 1=object created ok, 2=userdata set */ + if (shallow_copy_rc < 1) + { + errno = EINVAL; + return -1; + } + assert(*dst != NULL); + + switch (src->o_type) { + case json_type_object: + json_object_object_foreachC(src, iter) { + struct json_object *jso = NULL; + /* This handles the `json_type_null` case */ + if (!iter.val) + jso = NULL; + else if (json_object_deep_copy_recursive(iter.val, src, iter.key, -1, &jso, shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_object_add(*dst, iter.key, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + case json_type_array: + src_array_len = json_object_array_length(src); + for (ii = 0; ii < src_array_len; ii++) { + struct json_object *jso = NULL; + struct json_object *jso1 = json_object_array_get_idx(src, ii); + /* This handles the `json_type_null` case */ + if (!jso1) + jso = NULL; + else if (json_object_deep_copy_recursive(jso1, src, NULL, ii, &jso, shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_array_add(*dst, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + default: + break; + /* else, nothing to do, shallow_copy already did. */ + } + + if (shallow_copy_rc != 2) + return json_object_copy_serializer_data(src, *dst); + + return 0; +} + +int json_object_deep_copy(struct json_object *src, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy) +{ + int rc; + + /* Check if arguments are sane ; *dst must not point to a non-NULL object */ + if (!src || !dst || *dst) { + errno = EINVAL; + return -1; + } + + if (shallow_copy == NULL) + shallow_copy = json_c_shallow_copy_default; + + rc = json_object_deep_copy_recursive(src, NULL, NULL, -1, dst, shallow_copy); + if (rc < 0) { + json_object_put(*dst); + *dst = NULL; + } + + return rc; +} + +#pragma warning(pop) diff --git a/phlib/jsonc/json_object.h b/phlib/jsonc/json_object.h new file mode 100644 index 000000000000..fda7c6509862 --- /dev/null +++ b/phlib/jsonc/json_object.h @@ -0,0 +1,1035 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Core json-c API. Start here, or with json_tokener.h + */ +#ifndef _json_object_h_ +#define _json_object_h_ + +#ifdef __GNUC__ +#define THIS_FUNCTION_IS_DEPRECATED(func) func __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define THIS_FUNCTION_IS_DEPRECATED(func) __declspec(deprecated) func +#elif defined(__clang__) +#define THIS_FUNCTION_IS_DEPRECATED(func) func __deprecated +#else +#define THIS_FUNCTION_IS_DEPRECATED(func) func +#endif + +#ifdef __GNUC__ +#define JSON_C_CONST_FUNCTION(func) func __attribute__((const)) +#else +#define JSON_C_CONST_FUNCTION(func) func +#endif + +//#if defined(_MSC_VER) -- (disable dllexport - dmex) +//#define JSON_EXPORT __declspec(dllexport) +//#else +#define JSON_EXPORT extern +//#endif + +#include +#include "json_inttypes.h" +#include "printbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1<<0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1<<1) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * Instead of a "Two Space Tab" this gives a single tab character. + */ +#define JSON_C_TO_STRING_PRETTY_TAB (1<<3) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1<<2) + +/** + * Don't escape forward slashes. + */ +#define JSON_C_TO_STRING_NOSLASHESCAPE (1<<4) + +/** + * A flag for the json_object_object_add_ex function which + * causes the value to be added without a check if it already exists. + * Note: it is the responsibilty of the caller to ensure that no + * key is added multiple times. If this is done, results are + * unpredictable. While this option is somewhat dangerous, it + * permits potentially large performance savings in code that + * knows for sure the key values are unique (e.g. because the + * code adds a well-known set of constant key values). + */ +#define JSON_C_OBJECT_ADD_KEY_IS_NEW (1<<1) +/** + * A flag for the json_object_object_add_ex function which + * flags the key as being constant memory. This means that + * the key will NOT be copied via strdup(), resulting in a + * potentially huge performance win (malloc, strdup and + * free are usually performance hogs). It is acceptable to + * use this flag for keys in non-constant memory blocks if + * the caller ensure that the memory holding the key lives + * longer than the corresponding json object. However, this + * is somewhat dangerous and should only be done if really + * justified. + * The general use-case for this flag is cases where the + * key is given as a real constant value in the function + * call, e.g. as in + * json_object_object_add_ex(obj, "ip", json, + * JSON_C_OBJECT_KEY_IS_CONSTANT); + */ +#define JSON_C_OBJECT_KEY_IS_CONSTANT (1<<2) + +#undef FALSE +#define FALSE ((json_bool)0) + +#undef TRUE +#define TRUE ((json_bool)1) + +/** + * Set the global value of an option, which will apply to all + * current and future threads that have not set a thread-local value. + * + * @see json_c_set_serialization_double_format + */ +#define JSON_C_OPTION_GLOBAL (0) +/** + * Set a thread-local value of an option, overriding the global value. + * This will fail if json-c is not compiled with threading enabled, and + * with the __thread specifier (or equivalent) available. + * + * @see json_c_set_serialization_double_format + */ +#define JSON_C_OPTION_THREAD (1) + +/** + * A structure to use with json_object_object_foreachC() loops. + * Contains key, val and entry members. + */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; +typedef struct json_object_iter json_object_iter; + +typedef int json_bool; + +/** + * @brief The core type for all type of JSON objects handled by json-c + */ +typedef struct json_object json_object; +typedef struct json_object *json_object_ptr; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int (json_object_to_json_string_fn)(struct json_object *jso, + struct printbuf *pb, + int level, + int flags); + +/* supported object types */ + +typedef enum json_type { + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string +} json_type; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby grabbing shared + * ownership of obj. + * + * @param obj the json_object instance + */ +JSON_EXPORT struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + */ +JSON_EXPORT int json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string + */ +JSON_EXPORT int json_object_is_type(const struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string + */ +JSON_EXPORT enum json_type json_object_get_type(const struct json_object *obj); + + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * The pointer you get is an internal of your json object. You don't + * have to free it, later use of json_object_put() should be sufficient. + * If you can not ensure there's no concurrent access to *obj use + * strdup(). + * @param obj the json_object instance + * @returns a string in JSON format + */ +JSON_EXPORT const char* json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @see json_object_to_json_string() for details on how to free string. + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +JSON_EXPORT const char* json_object_to_json_string_ext(struct json_object *obj, int +flags); + +/** Stringify object to json format + * @see json_object_to_json_string() for details on how to free string. + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @param length a pointer where, if not NULL, the length (without null) is stored + * @returns a string in JSON format and the length if not NULL + */ +JSON_EXPORT const char* json_object_to_json_string_length(struct json_object *obj, int +flags, size_t *length); + +/** + * Returns the userdata set by json_object_set_userdata() or + * json_object_set_serializer() + * + * @param jso the object to return the userdata for + */ +JSON_EXPORT void* json_object_get_userdata(json_object *jso); + +/** + * Set an opaque userdata value for an object + * + * The userdata can be retrieved using json_object_get_userdata(). + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * Note: Objects created by parsing strings may have custom serializers set + * which expect the userdata to contain specific data (due to use of + * json_object_new_double_s()). In this case, json_object_set_serialiser() with + * NULL as to_string_func should be used instead to set the userdata and reset + * the serializer to its default value. + * + * @param jso the object to set the userdata for + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +JSON_EXPORT void json_object_set_userdata(json_object *jso, void *userdata, + json_object_delete_fn *user_delete); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * If to_string_func is NULL the default behaviour is reset (but the userdata + * and user_delete fields are still set). + * + * The userdata parameter is optional and may be passed as NULL. It can be used + * to provide additional data for to_string_func to use. This parameter may + * be NULL even if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * Note that the userdata is the same as set by json_object_set_userdata(), so + * care must be taken not to overwrite the value when both a custom serializer + * and json_object_set_userdata() are used. + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +JSON_EXPORT void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn *to_string_func, + void *userdata, + json_object_delete_fn *user_delete); + +#ifdef __clang__ +/* + * Clang doesn't pay attention to the parameters defined in the + * function typedefs used here, so turn off spurious doc warnings. + * { + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" +#endif + +/** + * Simply call free on the userdata pointer. + * Can be used with json_object_set_serializer(). + * + * @param jso unused + * @param userdata the pointer that is passed to free(). + */ +json_object_delete_fn json_object_free_userdata; + +/** + * Copy the jso->_userdata string over to pb as-is. + * Can be used with json_object_set_serializer(). + * + * @param jso The object whose _userdata is used. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +json_object_to_json_string_fn json_object_userdata_to_json_string; + +#ifdef __clang__ +/* } */ +#pragma clang diagnostic pop +#endif + + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +JSON_EXPORT struct json_object* json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +JSON_EXPORT struct lh_table* json_object_get_object(const struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +JSON_EXPORT int json_object_object_length(const struct json_object* obj); + +/** Get the sizeof (struct json_object). + * @returns a size_t with the sizeof (struct json_object) + */ +JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object, independent of the lifetime of obj, you must wrap the + * passed object with json_object_get. + * + * Upon calling this, the ownership of val transfers to obj. Thus you must + * make sure that you do in fact have ownership over this object. For instance, + * json_object_new_object will give you ownership until you transfer it, + * whereas json_object_object_get does not. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +JSON_EXPORT int json_object_object_add(struct json_object* obj, const char *key, + struct json_object *val); + +/** Add an object field to a json_object of type json_type_object + * + * The semantics are identical to json_object_object_add, except that an + * additional flag fields gives you more control over some detail aspects + * of processing. See the description of JSON_C_OBJECT_ADD_* flags for more + * details. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + * @param opts process-modifying options. To specify multiple options, use + * arithmetic or (OPT1|OPT2) + */ +JSON_EXPORT int json_object_object_add_ex(struct json_object* obj, + const char *const key, + struct json_object *const val, + const unsigned opts); + +/** Get the json_object associate with a given object field. + * Deprecated/discouraged: used json_object_object_get_ex instead. + * + * This returns NULL if the field is found but its value is null, or if + * the field is not found, or if obj is not a json_type_object. If you + * need to distinguis between these cases, use json_object_object_get_ex(). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + */ +JSON_EXPORT struct json_object* json_object_object_get(const struct json_object* obj, + const char *key); + +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object* obj, + const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +JSON_EXPORT void json_object_object_del(struct json_object* obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L + +# define json_object_object_foreach(obj,key,val) \ + char *key = NULL; \ + struct json_object *val __attribute__((__unused__)) = NULL; \ + for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ + ({ if(entry ## key) { \ + key = (char*)lh_entry_k(entry ## key); \ + val = (struct json_object*)lh_entry_v(entry ## key); \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + char *key = NULL;\ + struct json_object *val = NULL; \ + struct lh_entry *entry ## key; \ + struct lh_entry *entry_next ## key = NULL; \ + for(entry ## key = json_object_get_object(obj)->head; \ + (entry ## key ? ( \ + key = (char*)lh_entry_k(entry ## key), \ + val = (struct json_object*)lh_entry_v(entry ## key), \ + entry_next ## key = entry ## key->next, \ + entry ## key) : 0); \ + entry ## key = entry_next ## key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator, use type json_object_iter + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; \ + (iter.entry ? (iter.key = (char*)lh_entry_k(iter.entry), iter.val = (struct json_object*)lh_entry_v(iter.entry), iter.entry) : 0); \ + iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +JSON_EXPORT struct json_object* json_object_new_array(void); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +JSON_EXPORT struct array_list* json_object_get_array(const struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +JSON_EXPORT int json_object_array_length(const struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to sort_fn +* +* @param jso the json_object instance +* @param sort_fn a sorting function +*/ +JSON_EXPORT void json_object_array_sort(struct json_object *jso, int(__cdecl* sort_fn)(const void *, const void *)); + +/** Binary search a sorted array for a specified key object. + * + * It depends on your compare function what's sufficient as a key. + * Usually you create some dummy object with the parameter compared in + * it, to identify the right item you're actually looking for. + * + * @see json_object_array_sort() for hints on the compare function. + * + * @param key a dummy json_object with the right key + * @param jso the array object we're searching + * @param sort_fn the sort/compare function + * + * @return the wanted json_object instance + */ +JSON_EXPORT struct json_object* json_object_array_bsearch( + const struct json_object *key, + const struct json_object *jso, + int (__cdecl* sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +JSON_EXPORT int json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +JSON_EXPORT int json_object_array_put_idx(struct json_object *obj, size_t idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +JSON_EXPORT struct json_object* json_object_array_get_idx(const struct json_object *obj, + size_t idx); + +/** Delete an elements from a specified index in an array (a json_object of type json_type_array) + * + * The reference count will be decremented for each of the deleted objects. If there + * are no more owners of an element that is being deleted, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param idx the index to start deleting elements at + * @param count the number of elements to delete + * @returns 0 if the elements were successfully deleted + */ +JSON_EXPORT int json_object_array_del_idx(struct json_object *obj, size_t idx, size_t count); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool TRUE or FALSE (1 or 0) + * @returns a json_object of type json_type_boolean + */ +JSON_EXPORT struct json_object* json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a json_bool + */ +JSON_EXPORT json_bool json_object_get_boolean(const struct json_object *obj); + + +/** Set the json_bool value of a json_object + * + * The type of obj is checked to be a json_type_boolean and 0 is returned + * if it is not without any further actions. If type of obj is json_type_boolean + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_boolean(struct json_object *obj,json_bool new_value); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +JSON_EXPORT struct json_object* json_object_new_int(int32_t i); + + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +JSON_EXPORT struct json_object* json_object_new_int64(int64_t i); + + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +JSON_EXPORT int32_t json_object_get_int(const struct json_object *obj); + +/** Set the int value of a json_object + * + * The type of obj is checked to be a json_type_int and 0 is returned + * if it is not without any further actions. If type of obj is json_type_int + * the obect value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_int(struct json_object *obj,int new_value); + +/** Increment a json_type_int object by the given amount, which may be negative. + * + * If the type of obj is not json_type_int then 0 is returned with no further + * action taken. + * If the addition would result in a overflow, the object value + * is set to INT64_MAX. + * If the addition would result in a underflow, the object value + * is set to INT64_MIN. + * Neither overflow nor underflow affect the return value. + * + * @param obj the json_object instance + * @param val the value to add + * @returns 1 if the increment succeded, 0 otherwise + */ +JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); + + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj); + + +/** Set the int64_t value of a json_object + * + * The type of obj is checked to be a json_type_int and 0 is returned + * if it is not without any further actions. If type of obj is json_type_int + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_int64(struct json_object *obj,int64_t new_value); + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * + * @see json_object_double_to_json_string() for how to set a custom format string. + * + * @param d the double + * @returns a json_object of type json_type_double + */ +JSON_EXPORT struct json_object* json_object_new_double(double d); + +/** + * Create a new json_object of type json_type_double, using + * the exact serialized representation of the value. + * + * This allows for numbers that would otherwise get displayed + * inefficiently (e.g. 12.3 => "12.300000000000001") to be + * serialized with the more convenient form. + * + * Notes: + * + * This is used by json_tokener_parse_ex() to allow for + * an exact re-serialization of a parsed object. + * + * The userdata field is used to store the string representation, so it + * can't be used for other data if this function is used. + * + * An equivalent sequence of calls is: + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(jso, json_object_userdata_to_json_string, + * strdup(ds), json_object_free_userdata); + * @endcode + * + * @param d the numeric value of the double. + * @param ds the string representation of the double. This will be copied. + */ +JSON_EXPORT struct json_object* json_object_new_double_s(double d, const char *ds); + +/** + * Set a global or thread-local json-c option, depending on whether + * JSON_C_OPTION_GLOBAL or JSON_C_OPTION_THREAD is passed. + * Thread-local options default to undefined, and inherit from the global + * value, even if the global value is changed after the thread is created. + * Attempting to set thread-local options when threading is not compiled in + * will result in an error. Be sure to check the return value. + * + * double_format is a "%g" printf format, such as "%.20g" + * + * @return -1 on errors, 0 on success. + */ +int json_c_set_serialization_double_format(const char *double_format, int global_or_thread); + + + +/** Serialize a json_object of type json_type_double to a string. + * + * This function isn't meant to be called directly. Instead, you can set a + * custom format string for the serialization of this double using the + * following call (where "%.17g" actually is the default): + * + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(jso, json_object_double_to_json_string, + * "%.17g", NULL); + * @endcode + * + * @see printf(3) man page for format strings + * + * @param jso The json_type_double object that is serialized. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +JSON_EXPORT int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +JSON_EXPORT double json_object_get_double(const struct json_object *obj); + + +/** Set the double value of a json_object + * + * The type of obj is checked to be a json_type_double and 0 is returned + * if it is not without any further actions. If type of obj is json_type_double + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_double(struct json_object *obj,double new_value); + + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +JSON_EXPORT struct json_object* json_object_new_string(const char *s); + +JSON_EXPORT struct json_object* json_object_new_string_len(const char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is of type json_type_null (i.e. obj == NULL), + * NULL is returned. + * + * If the passed object of type json_type_string, the string contents + * are returned. + * + * Otherwise the JSON representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string or NULL + */ +JSON_EXPORT const char* json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +JSON_EXPORT int json_object_get_string_len(const struct json_object *obj); + + +/** Set the string value of a json_object with zero terminated strings + * equivalent to json_object_set_string_len (obj, new_value, strlen(new_value)) + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_string(json_object* obj, const char* new_value); + +/** Set the string value of a json_object str + * + * The type of obj is checked to be a json_type_string and 0 is returned + * if it is not without any further actions. If type of obj is json_type_string + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set; Since string legth is given in len this need not be zero terminated + * @param len the length of new_value + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_string_len(json_object* obj, const char* new_value, int len); + +/** Check if two json_object's are equal + * + * If the passed objects are equal 1 will be returned. + * Equality is defined as follows: + * - json_objects of different types are never equal + * - json_objects of the same primitive type are equal if the + * c-representation of their value is equal + * - json-arrays are considered equal if all values at the same + * indices are equal (same order) + * - Complex json_objects are considered equal if all + * contained objects referenced by their key are equal, + * regardless their order. + * + * @param obj1 the first json_object instance + * @param obj2 the second json_object instance + * @returns whether both objects are equal or not + */ +JSON_EXPORT int json_object_equal(struct json_object *obj1, + struct json_object *obj2); + +/** + * Perform a shallow copy of src into *dst as part of an overall json_object_deep_copy(). + * + * If src is part of a containing object or array, parent will be non-NULL, + * and key or index will be provided. + * When shallow_copy is called *dst will be NULL, and must be non-NULL when it returns. + * src will never be NULL. + * + * If shallow_copy sets the serializer on an object, return 2 to indicate to + * json_object_deep_copy that it should not attempt to use the standard userdata + * copy function. + * + * @return On success 1 or 2, -1 on errors + */ +typedef int (json_c_shallow_copy_fn)(json_object *src, json_object *parent, const char *key, size_t index, json_object **dst); + +/** + * The default shallow copy implementation for use with json_object_deep_copy(). + * This simply calls the appropriate json_object_new_() function and + * copies over the serializer function (_to_json_string internal field of + * the json_object structure) but not any _userdata or _user_delete values. + * + * If you're writing a custom shallow_copy function, perhaps because you're using + * your own custom serializer, you can call this first to create the new object + * before customizing it with json_object_set_serializer(). + * + * @return 1 on success, -1 on errors, but never 2. + */ +json_c_shallow_copy_fn json_c_shallow_copy_default; + +/** + * Copy the contents of the JSON object. + * The destination object must be initialized to NULL, + * to make sure this function won't overwrite an existing JSON object. + * + * This does roughly the same thing as + * `json_tokener_parse(json_object_get_string(src))`. + * + * @param src source JSON object whose contents will be copied + * @param dst pointer to the destination object where the contents of `src`; + * make sure this pointer is initialized to NULL + * @param shallow_copy an optional function to copy individual objects, needed + * when custom serializers are in use. See also + * json_object set_serializer. + * + * @returns 0 if the copy went well, -1 if an error occured during copy + * or if the destination pointer is non-NULL + */ + +JSON_EXPORT int json_object_deep_copy(struct json_object *src, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json-c/json_object_iterator.c b/phlib/jsonc/json_object_iterator.c similarity index 91% rename from plugins/CommonUtil/json-c/json_object_iterator.c rename to phlib/jsonc/json_object_iterator.c index 7066649c3bc5..f8d69ab3689a 100644 --- a/plugins/CommonUtil/json-c/json_object_iterator.c +++ b/phlib/jsonc/json_object_iterator.c @@ -7,11 +7,6 @@ * This library is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See COPYING for details. * -* @brief json-c forces clients to use its private data -* structures for JSON Object iteration. This API -* implementation corrects that by abstracting the -* private json-c details. -* ******************************************************************************* */ @@ -105,7 +100,7 @@ json_object_iter_next(struct json_object_iterator* iter) JASSERT(NULL != iter); JASSERT(kObjectEndIterValue != iter->opaque_); - iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; + iter->opaque_ = ((const struct lh_entry *)iter->opaque_)->next; } @@ -118,7 +113,7 @@ json_object_iter_peek_name(const struct json_object_iterator* iter) JASSERT(NULL != iter); JASSERT(kObjectEndIterValue != iter->opaque_); - return (const char*)(((struct lh_entry *)iter->opaque_)->k); + return (const char*)(((const struct lh_entry *)iter->opaque_)->k); } @@ -131,7 +126,7 @@ json_object_iter_peek_value(const struct json_object_iterator* iter) JASSERT(NULL != iter); JASSERT(kObjectEndIterValue != iter->opaque_); - return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v); + return (struct json_object*)lh_entry_v((const struct lh_entry *)iter->opaque_); } diff --git a/plugins/CommonUtil/json-c/json_object_iterator.h b/phlib/jsonc/json_object_iterator.h similarity index 96% rename from plugins/CommonUtil/json-c/json_object_iterator.h rename to phlib/jsonc/json_object_iterator.h index 44c9fb25b6ca..f226cbd524a3 100644 --- a/plugins/CommonUtil/json-c/json_object_iterator.h +++ b/phlib/jsonc/json_object_iterator.h @@ -7,10 +7,11 @@ * This library is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See COPYING for details. * -* @brief json-c forces clients to use its private data -* structures for JSON Object iteration. This API -* corrects that by abstracting the private json-c -* details. +* @brief An API for iterating over json_type_object objects, +* styled to be familiar to C++ programmers. +* Unlike json_object_object_foreach() and +* json_object_object_foreachC(), this avoids the need to expose +* json-c internals like lh_entry. * * API attributes:
* * Thread-safe: NO
diff --git a/plugins/CommonUtil/json-c/json_object_private.h b/phlib/jsonc/json_object_private.h similarity index 62% rename from plugins/CommonUtil/json-c/json_object_private.h rename to phlib/jsonc/json_object_private.h index deff7e8df48f..dfe9ce6a174b 100644 --- a/plugins/CommonUtil/json-c/json_object_private.h +++ b/phlib/jsonc/json_object_private.h @@ -9,6 +9,10 @@ * */ +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ #ifndef _json_object_private_h_ #define _json_object_private_h_ @@ -16,6 +20,8 @@ extern "C" { #endif +#define LEN_DIRECT_STRING_DATA 32 /**< how many bytes are directly stored in json_object for strings? */ + typedef void (json_object_private_delete_fn)(struct json_object *o); struct json_object @@ -32,14 +38,25 @@ struct json_object struct lh_table *c_object; struct array_list *c_array; struct { - char *str; - size_t len; + union { + /* optimize: if we have small strings, we can store them + * directly. This saves considerable CPU cycles AND memory. + */ + char *ptr; + char data[LEN_DIRECT_STRING_DATA]; + } str; + int len; } c_string; } o; json_object_delete_fn *_user_delete; void *_userdata; }; +void _json_c_set_last_err(const char *err_fmt, ...); + +extern const char *json_number_chars; +extern const char *json_hex_chars; + #ifdef __cplusplus } #endif diff --git a/phlib/jsonc/json_pointer.c b/phlib/jsonc/json_pointer.c new file mode 100644 index 000000000000..1ce3feed5a83 --- /dev/null +++ b/phlib/jsonc/json_pointer.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2016 Alexandru Ardelean. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include "strerror_override.h" + +#include +#include +#include +#include +#include + +#include "json_pointer.h" +#include "strdup_compat.h" +#include "vasprintf_compat.h" + +/** + * JavaScript Object Notation (JSON) Pointer + * RFC 6901 - https://tools.ietf.org/html/rfc6901 + */ + +static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char) +{ + int slen = (int)strlen(s); + int skip = (int)strlen(occur) - 1; /* length of the occurence, minus the char we're replacing */ + char *p = s; + while ((p = strstr(p, occur))) { + *p = repl_char; + p++; + slen -= skip; + memmove(p, (p + skip), slen - (p - s) + 1); /* includes null char too */ + } +} + +static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx) +{ + int i, len = (int)strlen(path); + /* this code-path optimizes a bit, for when we reference the 0-9 index range in a JSON array + and because leading zeros not allowed */ + if (len == 1) { + if (isdigit((int)path[0])) { + *idx = (path[0] - '0'); + goto check_oob; + } + errno = EINVAL; + return 0; + } + /* leading zeros not allowed per RFC */ + if (path[0] == '0') { + errno = EINVAL; + return 0; + } + /* RFC states base-10 decimals */ + for (i = 0; i < len; i++) { + if (!isdigit((int)path[i])) { + errno = EINVAL; + return 0; + } + } + + *idx = strtol(path, NULL, 10); + if (*idx < 0) { + errno = EINVAL; + return 0; + } +check_oob: + len = (int)json_object_array_length(jo); + if (*idx >= len) { + errno = ENOENT; + return 0; + } + + return 1; +} + +static int json_pointer_get_single_path(struct json_object *obj, char *path, struct json_object **value) +{ + if (json_object_is_type(obj, json_type_array)) { + int32_t idx; + if (!is_valid_index(obj, path, &idx)) + return -1; + obj = json_object_array_get_idx(obj, idx); + if (obj) { + if (value) + *value = obj; + return 0; + } + /* Entry not found */ + errno = ENOENT; + return -1; + } + + /* RFC states that we first must eval all ~1 then all ~0 */ + string_replace_all_occurrences_with_char(path, "~1", '/'); + string_replace_all_occurrences_with_char(path, "~0", '~'); + + if (!json_object_object_get_ex(obj, path, value)) { + errno = ENOENT; + return -1; + } + + return 0; +} + +static int json_pointer_set_single_path( + struct json_object *parent, + const char *path, + struct json_object *value) +{ + if (json_object_is_type(parent, json_type_array)) { + int32_t idx; + /* RFC (Chapter 4) states that '-' may be used to add new elements to an array */ + if (path[0] == '-' && path[1] == '\0') + return json_object_array_add(parent, value); + if (!is_valid_index(parent, path, &idx)) + return -1; + return json_object_array_put_idx(parent, idx, value); + } + + /* path replacements should have been done in json_pointer_get_single_path(), + and we should still be good here */ + if (json_object_is_type(parent, json_type_object)) + return json_object_object_add(parent, path, value); + + /* Getting here means that we tried to "dereference" a primitive JSON type (like string, int, bool). + i.e. add a sub-object to it */ + errno = ENOENT; + return -1; +} + +static int json_pointer_get_recursive( + struct json_object *obj, + char *path, + struct json_object **value) +{ + char *endp; + int rc; + + /* All paths (on each recursion level must have a leading '/' */ + if (path[0] != '/') { + errno = EINVAL; + return -1; + } + path++; + + endp = strchr(path, '/'); + if (endp) + *endp = '\0'; + + /* If we err-ed here, return here */ + if ((rc = json_pointer_get_single_path(obj, path, &obj))) + return rc; + + if (endp) { + *endp = '/'; /* Put the slash back, so that the sanity check passes on next recursion level */ + return json_pointer_get_recursive(obj, endp, value); + } + + /* We should be at the end of the recursion here */ + if (value) + *value = obj; + + return 0; +} + +int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res) +{ + char *path_copy = NULL; + int rc; + + if (!obj || !path) { + errno = EINVAL; + return -1; + } + + if (path[0] == '\0') { + if (res) + *res = obj; + return 0; + } + + /* pass a working copy to the recursive call */ + if (!(path_copy = _strdup(path))) { + errno = ENOMEM; + return -1; + } + rc = json_pointer_get_recursive(obj, path_copy, res); + free(path_copy); + + return rc; +} + +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...) +{ + char *path_copy = NULL; + int rc = 0; + va_list args; + + if (!obj || !path_fmt) { + errno = EINVAL; + return -1; + } + + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') { + if (res) + *res = obj; + goto out; + } + + rc = json_pointer_get_recursive(obj, path_copy, res); +out: + free(path_copy); + + return rc; +} + +int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value) +{ + const char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + int rc; + + if (!obj || !path) { + errno = EINVAL; + return -1; + } + + if (path[0] == '\0') { + json_object_put(*obj); + *obj = value; + return 0; + } + + if (path[0] != '/') { + errno = EINVAL; + return -1; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path, '/')) == path) { + path++; + return json_pointer_set_single_path(*obj, path, value); + } + + /* pass a working copy to the recursive call */ + if (!(path_copy = strdup(path))) { + errno = ENOMEM; + return -1; + } + path_copy[endp - path] = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + free(path_copy); + + if (rc) + return rc; + + endp++; + return json_pointer_set_single_path(set, endp, value); +} + +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...) +{ + char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + va_list args; + int rc = 0; + + if (!obj || !path_fmt) { + errno = EINVAL; + return -1; + } + + /* pass a working copy to the recursive call */ + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') { + json_object_put(*obj); + *obj = value; + goto out; + } + + if (path_copy[0] != '/') { + errno = EINVAL; + rc = -1; + goto out; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path_copy, '/')) == path_copy) { + set = *obj; + goto set_single_path; + } + + *endp = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + + if (rc) + goto out; + +set_single_path: + endp++; + rc = json_pointer_set_single_path(set, endp, value); +out: + free(path_copy); + return rc; +} + diff --git a/phlib/jsonc/json_pointer.h b/phlib/jsonc/json_pointer.h new file mode 100644 index 000000000000..b8746c0c9707 --- /dev/null +++ b/phlib/jsonc/json_pointer.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Alexadru Ardelean. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief JSON Pointer (RFC 6901) implementation for retrieving + * objects from a json-c object tree. + */ +#ifndef _json_pointer_h_ +#define _json_pointer_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Retrieves a JSON sub-object from inside another JSON object + * using the JSON pointer notation as defined in RFC 6901 + * https://tools.ietf.org/html/rfc6901 + * + * The returned JSON sub-object is equivalent to parsing manually the + * 'obj' JSON tree ; i.e. it's not a new object that is created, but rather + * a pointer inside the JSON tree. + * + * Internally, this is equivalent to doing a series of 'json_object_object_get()' + * and 'json_object_array_get_idx()' along the given 'path'. + * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'res' param will be treated as an argument for 'path' + * Example: json_pointer_get(obj, "/foo/%d/%s", &res, 0, bar) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * + * @param obj the json_object instance/tree from where to retrieve sub-objects + * @param path a (RFC6901) string notation for the sub-object to retrieve + * @param res a pointer where to store a reference to the json_object + * associated with the given path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res); + +/** + * This is a variant of 'json_pointer_get()' that supports printf() style arguments. + * + * Example: json_pointer_getf(obj, res, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param res a pointer where to store a reference to the json_object + * associated with the given path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...); + +/** + * Sets JSON object 'value' in the 'obj' tree at the location specified + * by the 'path'. 'path' is JSON pointer notation as defined in RFC 6901 + * https://tools.ietf.org/html/rfc6901 + * + * Note that 'obj' is a double pointer, mostly for the "" (empty string) + * case, where the entire JSON object would be replaced by 'value'. + * In the case of the "" path, the object at '*obj' will have it's refcount + * decremented with 'json_object_put()' and the 'value' object will be assigned to it. + * + * For other cases (JSON sub-objects) ownership of 'value' will be transferred into + * '*obj' via 'json_object_object_add()' & 'json_object_array_put_idx()', so the + * only time the refcount should be decremented for 'value' is when the return value of + * 'json_pointer_set()' is negative (meaning the 'value' object did not get set into '*obj'). + * + * That also implies that 'json_pointer_set()' does not do any refcount incrementing. + * (Just that single decrement that was mentioned above). + * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'value' param will be treated as an argument for 'path' + * Example: json_pointer_set(obj, "/foo/%d/%s", value, 0, bak) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param path a (RFC6901) string notation for the sub-object to set in the tree + * @param value object to set at path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value); + +/** + * This is a variant of 'json_pointer_set()' that supports printf() style arguments. + * + * Example: json_pointer_setf(obj, value, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param value object to set at path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json-c/json_tokener.c b/phlib/jsonc/json_tokener.c similarity index 78% rename from plugins/CommonUtil/json-c/json_tokener.c rename to phlib/jsonc/json_tokener.c index 2959cbd6aea2..8ad430d25c31 100644 --- a/plugins/CommonUtil/json-c/json_tokener.c +++ b/phlib/jsonc/json_tokener.c @@ -16,6 +16,7 @@ #include "config.h" #include +#include "math_compat.h" #include #include #include @@ -23,25 +24,24 @@ #include #include -#include "bits.h" #include "debug.h" #include "printbuf.h" #include "arraylist.h" #include "json_inttypes.h" #include "json_object.h" +#include "json_object_private.h" #include "json_tokener.h" #include "json_util.h" +#include "strdup_compat.h" #ifdef HAVE_LOCALE_H #include #endif /* HAVE_LOCALE_H */ +#ifdef HAVE_XLOCALE_H +#include +#endif -#if !HAVE_STRDUP && defined(_MSC_VER) - /* MSC has the version as _strdup */ -# define strdup _strdup -#elif !HAVE_STRDUP -# error You do not have strdup on your system. -#endif /* HAVE_STRDUP */ +#define jt_hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) #if !HAVE_STRNCASECMP && defined(_MSC_VER) /* MSC has the version as _strnicmp */ @@ -58,7 +58,8 @@ static const char json_null_str[] = "null"; static const int json_null_str_len = sizeof(json_null_str) - 1; static const char json_inf_str[] = "Infinity"; -static const int json_inf_str_len = sizeof(json_inf_str) - 1; +static const char json_inf_str_lower[] = "infinity"; +static const unsigned int json_inf_str_len = sizeof(json_inf_str) - 1; static const char json_nan_str[] = "NaN"; static const int json_nan_str_len = sizeof(json_nan_str) - 1; static const char json_true_str[] = "true"; @@ -86,13 +87,15 @@ static const char* json_tokener_errors[] = { const char *json_tokener_error_desc(enum json_tokener_error jerr) { - int jerr_int = (int)jerr; - if (jerr_int < 0 || jerr_int >= (int)(sizeof(json_tokener_errors) / sizeof(json_tokener_errors[0]))) - return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()"; + int jerr_int = (int) jerr; + if (jerr_int < 0 || + jerr_int >= (int)(sizeof(json_tokener_errors) / sizeof(json_tokener_errors[0]))) + return "Unknown error, " + "invalid json_tokener_error value passed to json_tokener_error_desc()"; return json_tokener_errors[jerr]; } -enum json_tokener_error json_tokener_get_error(json_tokener *tok) +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok) { return tok->err; } @@ -109,7 +112,8 @@ struct json_tokener* json_tokener_new_ex(int depth) tok = (struct json_tokener*)calloc(1, sizeof(struct json_tokener)); if (!tok) return NULL; - tok->stack = (struct json_tokener_srec *)calloc(depth, sizeof(struct json_tokener_srec)); + tok->stack = (struct json_tokener_srec *) calloc(depth, + sizeof(struct json_tokener_srec)); if (!tok->stack) { free(tok); return NULL; @@ -129,7 +133,7 @@ void json_tokener_free(struct json_tokener *tok) { json_tokener_reset(tok); if (tok->pb) printbuf_free(tok->pb); - if (tok->stack) free(tok->stack); + free(tok->stack); free(tok); } @@ -163,7 +167,8 @@ struct json_object* json_tokener_parse(const char *str) return obj; } -struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +struct json_object* json_tokener_parse_verbose(const char *str, + enum json_tokener_error *error) { struct json_tokener* tok; struct json_object* obj; @@ -206,14 +211,17 @@ struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokene * Returns 1 on success, sets tok->err and returns 0 if no more chars. * Implicit inputs: str, len vars */ -#define PEEK_CHAR(dest, tok) \ - (((tok)->char_offset == len) ? \ - (((tok)->depth == 0 && state == json_tokener_state_eatws && saved_state == json_tokener_state_finish) ? \ - (((tok)->err = json_tokener_success), 0) \ - : \ - (((tok)->err = json_tokener_continue), 0) \ - ) : \ - (((dest) = *str), 1) \ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) ? \ + (((tok)->depth == 0 && \ + state == json_tokener_state_eatws && \ + saved_state == json_tokener_state_finish \ + ) ? \ + (((tok)->err = json_tokener_success), 0) \ + : \ + (((tok)->err = json_tokener_continue), 0) \ + ) : \ + (((dest) = *str), 1) \ ) /* ADVANCE_CHAR() macro: @@ -233,12 +241,11 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, { struct json_object *obj = NULL; char c = '\1'; -#ifdef HAVE_SETLOCALE - char *oldlocale=NULL, *tmplocale; - - tmplocale = setlocale(LC_NUMERIC, NULL); - if (tmplocale) oldlocale = strdup(tmplocale); - setlocale(LC_NUMERIC, "C"); +#ifdef HAVE_USELOCALE + locale_t oldlocale = uselocale(NULL); + locale_t newloc; +#elif defined(HAVE_SETLOCALE) + char *oldlocale = NULL; #endif tok->char_offset = 0; @@ -254,6 +261,33 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, return NULL; } +#ifdef HAVE_USELOCALE + { + locale_t duploc = duplocale(oldlocale); + newloc = newlocale(LC_NUMERIC, "C", duploc); + // XXX at least Debian 8.4 has a bug in newlocale where it doesn't + // change the decimal separator unless you set LC_TIME! + if (newloc) + { + duploc = newloc; // original duploc has been freed by newlocale() + newloc = newlocale(LC_TIME, "C", duploc); + } + if (newloc == NULL) + { + freelocale(duploc); + return NULL; + } + uselocale(newloc); + } +#elif defined(HAVE_SETLOCALE) + { + char *tmplocale; + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); + } +#endif + while (PEEK_CHAR(c, tok)) { redo_char: @@ -281,11 +315,15 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, state = json_tokener_state_eatws; saved_state = json_tokener_state_object_field_start; current = json_object_new_object(); + if(current == NULL) + goto out; break; case '[': state = json_tokener_state_eatws; saved_state = json_tokener_state_array; current = json_object_new_array(); + if(current == NULL) + goto out; break; case 'I': case 'i': @@ -305,6 +343,7 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, tok->err = json_tokener_error_parse_unexpected; goto out; } + /* FALLTHRU */ case '"': state = json_tokener_state_string; printbuf_reset(tok->pb); @@ -318,9 +357,6 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, printbuf_reset(tok->pb); tok->st_pos = 0; goto redo_char; -#if defined(__GNUC__) - case '0' ... '9': -#else case '0': case '1': case '2': @@ -331,7 +367,6 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, case '7': case '8': case '9': -#endif case '-': state = json_tokener_state_number; printbuf_reset(tok->pb); @@ -350,38 +385,55 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, tok->depth--; goto redo_char; - case json_tokener_state_inf: /* aka starts with 'i' */ + case json_tokener_state_inf: /* aka starts with 'i' (or 'I', or "-i", or "-I") */ { - int size; - int size_inf; + /* If we were guaranteed to have len set, then we could (usually) handle + * the entire "Infinity" check in a single strncmp (strncasecmp), but + * since len might be -1 (i.e. "read until \0"), we need to check it + * a character at a time. + * Trying to handle it both ways would make this code considerably more + * complicated with likely little performance benefit. + */ int is_negative = 0; + const char *_json_inf_str = json_inf_str; + if (!(tok->flags & JSON_TOKENER_STRICT)) + _json_inf_str = json_inf_str_lower; - printbuf_memappend_fast(tok->pb, &c, 1); - size = json_min(tok->st_pos+1, json_null_str_len); - size_inf = json_min(tok->st_pos+1, json_inf_str_len); - char *infbuf = tok->pb->buf; - if (*infbuf == '-') + /* Note: tok->st_pos must be 0 when state is set to json_tokener_state_inf */ + while (tok->st_pos < (int)json_inf_str_len) { - infbuf++; - is_negative = 1; - } - if ((!(tok->flags & JSON_TOKENER_STRICT) && - strncasecmp(json_inf_str, infbuf, size_inf) == 0) || - (strncmp(json_inf_str, infbuf, size_inf) == 0) - ) - { - if (tok->st_pos == json_inf_str_len) + char inf_char = *str; + if (!(tok->flags & JSON_TOKENER_STRICT)) + inf_char = tolower((int)*str); + if (inf_char != _json_inf_str[tok->st_pos]) { - current = json_object_new_double(is_negative ? -INFINITY : INFINITY); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + tok->st_pos++; + (void)ADVANCE_CHAR(str, tok); + if (!PEEK_CHAR(c, tok)) + { + /* out of input chars, for now at least */ + goto out; } - } else { - tok->err = json_tokener_error_parse_unexpected; - goto out; } - tok->st_pos++; + /* We checked the full length of "Infinity", so create the object. + * When handling -Infinity, the number parsing code will have dropped + * the "-" into tok->pb for us, so check it now. + */ + if (printbuf_length(tok->pb) > 0 && *(tok->pb->buf) == '-') + { + is_negative = 1; + } + current = json_object_new_double(is_negative + ? -INFINITY : INFINITY); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } break; case json_tokener_state_null: /* aka starts with 'n' */ @@ -410,6 +462,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, if (tok->st_pos == json_nan_str_len) { current = json_object_new_double(NAN); + if (current == NULL) + goto out; saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; goto redo_char; @@ -482,7 +536,9 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, while(1) { if(c == tok->quote_char) { printbuf_memappend_fast(tok->pb, case_start, str-case_start); - current = json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + current = json_object_new_string_len(tok->pb->buf, (int)tok->pb->bpos); + if(current == NULL) + goto out; saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; break; @@ -537,8 +593,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, /* Handle a 4-byte sequence, or two sequences if a surrogate pair */ while(1) { - if(strchr(json_hex_chars, c)) { - tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if (c && strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)jt_hexdigit(c) << ((3-tok->st_pos++)*4)); if(tok->st_pos == 4) { unsigned char unescaped_utf[4]; @@ -568,8 +624,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, */ got_hi_surrogate = tok->ucs_char; /* Not at end, and the next two chars should be "\u" */ - if ((tok->char_offset+1 != len) && - (tok->char_offset+2 != len) && + if ((len == -1 || len > (tok->char_offset + 2)) && + // str[0] != '0' && // implied by json_hex_chars, above. (str[1] == '\\') && (str[2] == 'u')) { @@ -578,13 +634,15 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, * characters. */ if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); - } + printbuf_memappend_fast(tok->pb, + (char*) utf8_replacement_char, 3); + } /* Advance to the first char of the next sequence and * continue processing with the next sequence. */ if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + printbuf_memappend_fast(tok->pb, + (char*) utf8_replacement_char, 3); goto out; } tok->ucs_char = 0; @@ -595,7 +653,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, * it. Put a replacement char in for the hi surrogate * and pretend we finished. */ - printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + printbuf_memappend_fast(tok->pb, + (char*) utf8_replacement_char, 3); } } else if (IS_LOW_SURROGATE(tok->ucs_char)) { /* Got a low surrogate not preceded by a high */ @@ -643,6 +702,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, ) { if(tok->st_pos == json_true_str_len) { current = json_object_new_boolean(1); + if(current == NULL) + goto out; saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; goto redo_char; @@ -652,6 +713,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, || (strncmp(json_false_str, tok->pb->buf, size2) == 0)) { if(tok->st_pos == json_false_str_len) { current = json_object_new_boolean(0); + if(current == NULL) + goto out; saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; goto redo_char; @@ -669,10 +732,45 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, /* Advance until we change state */ const char *case_start = str; int case_len=0; + int is_exponent=0; + int negativesign_next_possible_location=1; while(c && strchr(json_number_chars, c)) { ++case_len; - if(c == '.' || c == 'e' || c == 'E') + + /* non-digit characters checks */ + /* note: since the main loop condition to get here was + an input starting with 0-9 or '-', we are + protected from input starting with '.' or + e/E. */ + if (c == '.') { + if (tok->is_double != 0) { + /* '.' can only be found once, and out of the exponent part. + Thus, if the input is already flagged as double, it + is invalid. */ + tok->err = json_tokener_error_parse_number; + goto out; + } + tok->is_double = 1; + } + if (c == 'e' || c == 'E') { + if (is_exponent != 0) { + /* only one exponent possible */ + tok->err = json_tokener_error_parse_number; + goto out; + } + is_exponent = 1; tok->is_double = 1; + /* the exponent part can begin with a negative sign */ + negativesign_next_possible_location = case_len + 1; + } + if (c == '-' && case_len != negativesign_next_possible_location) { + /* If the negative sign is not where expected (ie + start of input or start of exponent part), the + input is invalid. */ + tok->err = json_tokener_error_parse_number; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { printbuf_memappend_fast(tok->pb, case_start, case_len); goto out; @@ -682,10 +780,11 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, printbuf_memappend_fast(tok->pb, case_start, case_len); // Check for -Infinity - if (tok->pb->buf[0] == '-' && case_len == 1 && + if (tok->pb->buf[0] == '-' && case_len <= 1 && (c == 'i' || c == 'I')) { state = json_tokener_state_inf; + tok->st_pos = 0; goto redo_char; } } @@ -693,16 +792,21 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, int64_t num64; double numd; if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { - if (num64 && tok->pb->buf[0]=='0' && (tok->flags & JSON_TOKENER_STRICT)) { + if (num64 && tok->pb->buf[0]=='0' && + (tok->flags & JSON_TOKENER_STRICT)) { /* in strict mode, number must not start with 0 */ tok->err = json_tokener_error_parse_number; goto out; } current = json_object_new_int64(num64); + if(current == NULL) + goto out; } else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) { current = json_object_new_double_s(numd, tok->pb->buf); + if(current == NULL) + goto out; } else { tok->err = json_tokener_error_parse_number; goto out; @@ -716,12 +820,12 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, case json_tokener_state_array_after_sep: case json_tokener_state_array: if(c == ']') { - if (state == json_tokener_state_array_after_sep && - (tok->flags & JSON_TOKENER_STRICT)) - { - tok->err = json_tokener_error_parse_unexpected; - goto out; - } + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; } else { @@ -737,7 +841,8 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, break; case json_tokener_state_array_add: - json_object_array_add(current, obj); + if( json_object_array_add(current, obj) != 0 ) + goto out; saved_state = json_tokener_state_array_sep; state = json_tokener_state_eatws; goto redo_char; @@ -830,6 +935,7 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, goto redo_char; case json_tokener_state_object_sep: + /* { */ if(c == '}') { saved_state = json_tokener_state_finish; state = json_tokener_state_eatws; @@ -845,7 +951,7 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, } if (!ADVANCE_CHAR(str, tok)) goto out; - } /* while(POP_CHAR) */ + } /* while(PEEK_CHAR) */ out: if (c && @@ -861,9 +967,12 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, tok->err = json_tokener_error_parse_eof; } -#ifdef HAVE_SETLOCALE +#ifdef HAVE_USELOCALE + uselocale(oldlocale); + freelocale(newloc); +#elif defined(HAVE_SETLOCALE) setlocale(LC_NUMERIC, oldlocale); - if (oldlocale) free(oldlocale); + free(oldlocale); #endif if (tok->err == json_tokener_success) diff --git a/plugins/CommonUtil/json-c/json_tokener.h b/phlib/jsonc/json_tokener.h similarity index 84% rename from plugins/CommonUtil/json-c/json_tokener.h rename to phlib/jsonc/json_tokener.h index a72d2bdefe00..ed272b673dfd 100644 --- a/plugins/CommonUtil/json-c/json_tokener.h +++ b/phlib/jsonc/json_tokener.h @@ -9,6 +9,10 @@ * */ +/** + * @file + * @brief Methods to parse an input string into a tree of json_object objects. + */ #ifndef _json_tokener_h_ #define _json_tokener_h_ @@ -86,6 +90,10 @@ struct json_tokener struct json_tokener_srec *stack; int flags; }; +/** + * @deprecated Unused in json-c code + */ +typedef struct json_tokener json_tokener; /** * Be strict when parsing JSON input. Use caution with @@ -116,34 +124,34 @@ const char *json_tokener_error_desc(enum json_tokener_error jerr); * * See also json_tokener_error_desc(). */ -enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); +JSON_EXPORT enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); -extern struct json_tokener* json_tokener_new(void); -extern struct json_tokener* json_tokener_new_ex(int depth); -extern void json_tokener_free(struct json_tokener *tok); -extern void json_tokener_reset(struct json_tokener *tok); -extern struct json_object* json_tokener_parse(const char *str); -extern struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); +JSON_EXPORT struct json_tokener* json_tokener_new(void); +JSON_EXPORT struct json_tokener* json_tokener_new_ex(int depth); +JSON_EXPORT void json_tokener_free(struct json_tokener *tok); +JSON_EXPORT void json_tokener_reset(struct json_tokener *tok); +JSON_EXPORT struct json_object* json_tokener_parse(const char *str); +JSON_EXPORT struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); /** * Set flags that control how parsing will be done. */ -extern void json_tokener_set_flags(struct json_tokener *tok, int flags); +JSON_EXPORT void json_tokener_set_flags(struct json_tokener *tok, int flags); -/** +/** * Parse a string and return a non-NULL json_object if a valid JSON value * is found. The string does not need to be a JSON object or array; * it can also be a string, number or boolean value. * * A partial JSON string can be parsed. If the parsing is incomplete, - * NULL will be returned and json_tokener_get_error() will be return + * NULL will be returned and json_tokener_get_error() will return * json_tokener_continue. * json_tokener_parse_ex() can then be called with additional bytes in str - * to continue the parsing. + * to continue the parsing. * - * If json_tokener_parse_ex() returns NULL and the error anything other than + * If json_tokener_parse_ex() returns NULL and the error is anything other than * json_tokener_continue, a fatal error has occurred and parsing must be - * halted. Then tok object must not be re-used until json_tokener_reset() is + * halted. Then, the tok object must not be reused until json_tokener_reset() is * called. * * When a valid JSON value is parsed, a non-NULL json_object will be @@ -152,7 +160,7 @@ extern void json_tokener_set_flags(struct json_tokener *tok, int flags); * json_object_get_type() before using the object. * * @b XXX this shouldn't use internal fields: - * Trailing characters after the parsed value do not automatically cause an + * Trailing characters after the parsed value do not automatically cause an * error. It is up to the caller to decide whether to treat this as an * error or to handle the additional characters, perhaps by parsing another * json value starting from that point. @@ -198,7 +206,7 @@ if (tok->char_offset < stringlen) // XXX shouldn't access internal fields * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. * @param len the length of str */ -extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok, +JSON_EXPORT struct json_object* json_tokener_parse_ex(struct json_tokener *tok, const char *str, int len); #ifdef __cplusplus diff --git a/phlib/jsonc/json_util.c b/phlib/jsonc/json_util.c new file mode 100644 index 000000000000..79e2bd4b77ae --- /dev/null +++ b/phlib/jsonc/json_util.c @@ -0,0 +1,319 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include + +#include "config.h" +#undef realloc + +#include "strerror_override.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#ifdef _WIN32 +# if MSC_VER < 1800 +/* strtoll is available only since Visual Studio 2013 */ +# define strtoll _strtoi64 +# endif +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(_WIN32) */ + +#if !defined(HAVE_OPEN) && defined(_WIN32) +# define open _open +#endif + +#include "snprintf_compat.h" + +#include "debug.h" +#include "printbuf.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename); + +static char _last_err[256] = ""; + +const char *json_util_get_last_err() +{ + if (_last_err[0] == '\0') + return NULL; + return _last_err; +} + +void _json_c_set_last_err(const char *err_fmt, ...) +{ + va_list ap; + va_start(ap, err_fmt); + // Ignore (attempted) overruns from snprintf + (void)vsnprintf(_last_err, sizeof(_last_err), err_fmt, ap); + va_end(ap); +} + +struct json_object* json_object_from_fd(int fd) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int ret; + + if(!(pb = printbuf_new())) { + _json_c_set_last_err("json_object_from_file: printbuf_new failed\n"); + return NULL; + } + while((ret = _read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + if(ret < 0) { + _json_c_set_last_err("json_object_from_fd: error reading fd %d: %s\n", fd, strerror(errno)); + printbuf_free(pb); + return NULL; + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +struct json_object* json_object_from_file(wchar_t * filename) +{ + NTSTATUS status; + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + struct json_object *obj = NULL; + + status = PhCreateFileWin32( + &fileHandle, + filename, + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (NT_SUCCESS(status)) + { + PSTR data; + ULONG allocatedLength; + ULONG dataLength; + ULONG returnLength; + BYTE buffer[PAGE_SIZE]; + + allocatedLength = sizeof(buffer); + data = (PSTR)PhAllocate(allocatedLength); + dataLength = 0; + + memset(data, 0, allocatedLength); + + while (NT_SUCCESS(NtReadFile(fileHandle, NULL, NULL, NULL, &isb, buffer, PAGE_SIZE, NULL, NULL))) + { + returnLength = (ULONG)isb.Information; + + if (returnLength == 0) + break; + + if (allocatedLength < dataLength + returnLength) + { + allocatedLength *= 2; + data = (PSTR)PhReAllocate(data, allocatedLength); + } + + memcpy(data + dataLength, buffer, returnLength); + + dataLength += returnLength; + } + + if (allocatedLength < dataLength + 1) + { + allocatedLength++; + data = (PSTR)PhReAllocate(data, allocatedLength); + } + + data[dataLength] = ANSI_NULL; + + obj = json_tokener_parse(data); + + PhFree(data); + } + + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(wchar_t * filename, struct json_object *obj, int flags) +{ + NTSTATUS status; + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + PSTR json_str; + + if (!(json_str = (PSTR)json_object_to_json_string_ext(obj, flags))) + return -1; + + status = PhCreateFileWin32( + &fileHandle, + filename, + FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return -1; + + status = NtWriteFile( + fileHandle, + NULL, + NULL, + NULL, + &isb, + json_str, + (ULONG)strlen(json_str), + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + { + NtClose(fileHandle); + return -1; + } + + NtClose(fileHandle); + return 0; +} + +int json_object_to_fd(int fd, struct json_object *obj, int flags) +{ + if (!obj) { + _json_c_set_last_err("json_object_to_fd: object is null\n"); + return -1; + } + + return _json_object_to_fd(fd, obj, flags, NULL); +} +static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename) +{ + int ret; + const char *json_str; + unsigned int wpos, wsize; + + filename = filename ? filename : "(fd)"; + + if (!(json_str = json_object_to_json_string_ext(obj,flags))) { + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = _write(fd, json_str + wpos, wsize-wpos)) < 0) { + _json_c_set_last_err("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(wchar_t *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +int json_parse_double(const char *buf, double *retval) +{ + char *end; + *retval = strtod(buf, &end); + return end == buf ? 1 : 0; +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + char *end = NULL; + int64_t val; + + errno = 0; + val = strtoll(buf, &end, 10); + if (end != buf) + *retval = val; + return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; +} + +#ifndef HAVE_REALLOC +void* rpl_realloc(void* p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +static const char* json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + _json_c_set_last_err("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} + diff --git a/phlib/jsonc/json_util.h b/phlib/jsonc/json_util.h new file mode 100644 index 000000000000..83a75b99c6e5 --- /dev/null +++ b/phlib/jsonc/json_util.h @@ -0,0 +1,106 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Miscllaneous utility functions and macros. + */ +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifndef json_min +#define json_min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a,b) ((a) > (b) ? (a) : (b)) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +/** + * Read the full contents of the given file, then convert it to a + * json_object using json_tokener_parse(). + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern struct json_object* json_object_from_file(wchar_t * filename); + +/** + * Create a JSON object from already opened file descriptor. + * + * This function can be helpful, when you opened the file already, + * e.g. when you have a temp file. + * Note, that the fd must be readable at the actual position, i.e. + * use lseek(fd, 0, SEEK_SET) before. + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern struct json_object* json_object_from_fd(int fd); + +/** + * Equivalent to: + * json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern int json_object_to_file(wchar_t *filename, struct json_object *obj); + +/** + * Open and truncate the given file, creating it if necessary, then + * convert the json_object to a string and write it to the file. + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern int json_object_to_file_ext(wchar_t *filename, struct json_object *obj, int flags); + +/** + * Convert the json_object to a string and write it to the file descriptor. + * Handles partial writes and will keep writing until done, or an error + * occurs. + * + * @param fd an open, writable file descriptor to write to + * @param obj the object to serializer and write + * @param flags flags to pass to json_object_to_json_string_ext() + * @return -1 if something fails. See json_util_get_last_err() for details. + */ +extern int json_object_to_fd(int fd, struct json_object *obj, int flags); + +/** + * Return the last error from various json-c functions, including: + * json_object_to_file{,_ext}, json_object_to_fd() or + * json_object_from_{file,fd}, or NULL if there is none. + */ +const char *json_util_get_last_err(void); + + +extern int json_parse_int64(const char *buf, int64_t *retval); +extern int json_parse_double(const char *buf, double *retval); + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +extern const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/phlib/jsonc/json_visit.c b/phlib/jsonc/json_visit.c new file mode 100644 index 000000000000..df92fee23007 --- /dev/null +++ b/phlib/jsonc/json_visit.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016 Eric Haszlakiewicz + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#include + +#include "config.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_visit.h" +#include "linkhash.h" + +static int _json_c_visit(json_object *jso, json_object *parent_jso, + const char *jso_key, size_t *jso_index, + json_c_visit_userfunc *userfunc, void *userarg); + +int json_c_visit(json_object *jso, int future_flags, + json_c_visit_userfunc *userfunc, void *userarg) +{ + int ret = _json_c_visit(jso, NULL, NULL, NULL, userfunc, userarg); + switch(ret) + { + case JSON_C_VISIT_RETURN_CONTINUE: + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + case JSON_C_VISIT_RETURN_STOP: + return 0; + default: + return JSON_C_VISIT_RETURN_ERROR; + } +} +static int _json_c_visit(json_object *jso, json_object *parent_jso, + const char *jso_key, size_t *jso_index, + json_c_visit_userfunc *userfunc, void *userarg) +{ + int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); + switch(userret) + { + case JSON_C_VISIT_RETURN_CONTINUE: + break; + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + case JSON_C_VISIT_RETURN_STOP: + case JSON_C_VISIT_RETURN_ERROR: + return userret; + default: + fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + + switch(json_object_get_type(jso)) + { + case json_type_null: + case json_type_boolean: + case json_type_double: + case json_type_int: + case json_type_string: + // we already called userfunc above, move on to the next object + return JSON_C_VISIT_RETURN_CONTINUE; + + case json_type_object: + { + json_object_object_foreach(jso, key, child) + { + userret = _json_c_visit(child, jso, key, NULL, userfunc, userarg); + if (userret == JSON_C_VISIT_RETURN_POP) + break; + if (userret == JSON_C_VISIT_RETURN_STOP || + userret == JSON_C_VISIT_RETURN_ERROR) + return userret; + if (userret != JSON_C_VISIT_RETURN_CONTINUE && + userret != JSON_C_VISIT_RETURN_SKIP) + { + fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + } + break; + } + case json_type_array: + { + size_t array_len = json_object_array_length(jso); + size_t ii; + for (ii = 0; ii < array_len; ii++) + { + json_object *child = json_object_array_get_idx(jso, ii); + userret = _json_c_visit(child, jso, NULL, &ii, userfunc, userarg); + if (userret == JSON_C_VISIT_RETURN_POP) + break; + if (userret == JSON_C_VISIT_RETURN_STOP || + userret == JSON_C_VISIT_RETURN_ERROR) + return userret; + if (userret != JSON_C_VISIT_RETURN_CONTINUE && + userret != JSON_C_VISIT_RETURN_SKIP) + { + fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + } + break; + } + default: + fprintf(stderr, "INTERNAL ERROR: _json_c_visit found object of unknown type: %d\n", json_object_get_type(jso)); + return JSON_C_VISIT_RETURN_ERROR; + } + + // Call userfunc for the second type on container types, after all + // members of the container have been visited. + // Non-container types will have already returned before this point. + + userret = userfunc(jso, JSON_C_VISIT_SECOND, parent_jso, jso_key, jso_index, userarg); + switch(userret) + { + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + // These are not really sensible during JSON_C_VISIT_SECOND, + // but map them to JSON_C_VISIT_CONTINUE anyway. + // FALLTHROUGH + case JSON_C_VISIT_RETURN_CONTINUE: + return JSON_C_VISIT_RETURN_CONTINUE; + case JSON_C_VISIT_RETURN_STOP: + case JSON_C_VISIT_RETURN_ERROR: + return userret; + default: + fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + // NOTREACHED +} + diff --git a/phlib/jsonc/json_visit.h b/phlib/jsonc/json_visit.h new file mode 100644 index 000000000000..b147d99ebc84 --- /dev/null +++ b/phlib/jsonc/json_visit.h @@ -0,0 +1,95 @@ + +#ifndef _json_c_json_visit_h_ +#define _json_c_json_visit_h_ + +/** + * @file + * @brief Methods for walking a tree of objects. + */ +#include "json_object.h" + +typedef int (json_c_visit_userfunc)(json_object *jso, int flags, + json_object *parent_jso, const char *jso_key, + size_t *jso_index, void *userarg); + +/** + * Visit each object in the JSON hierarchy starting at jso. + * For each object, userfunc is called, passing the object and userarg. + * If the object has a parent (i.e. anything other than jso itself) + * its parent will be passed as parent_jso, and either jso_key or jso_index + * will be set, depending on whether the parent is an object or an array. + * + * Nodes will be visited depth first, but containers (arrays and objects) + * will be visited twice, the second time with JSON_C_VISIT_SECOND set in + * flags. + * + * userfunc must return one of the defined return values, to indicate + * whether and how to continue visiting nodes, or one of various ways to stop. + * + * Returns 0 if nodes were visited successfully, even if some were + * intentionally skipped due to what userfunc returned. + * Returns <0 if an error occurred during iteration, including if + * userfunc returned JSON_C_VISIT_RETURN_ERROR. + */ +int json_c_visit(json_object *jso, int future_flags, + json_c_visit_userfunc *userfunc, void *userarg); + +/** + * Passed to json_c_visit_userfunc as one of the flags values to indicate + * that this is the second time a container (array or object) is being + * called, after all of it's members have been iterated over. + */ +#define JSON_C_VISIT_SECOND 0x02 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should proceed normally. + */ +#define JSON_C_VISIT_RETURN_CONTINUE 0 + + +/** + * This json_c_visit_userfunc return value indicates that iteration + * over the members of the current object should be skipped. + * If the current object isn't a container (array or object), this + * is no different than JSON_C_VISIT_RETURN_CONTINUE. + */ +#define JSON_C_VISIT_RETURN_SKIP 7547 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * of the fields/elements of the containing object should stop + * and continue "popped up" a level of the object hierarchy. + * For example, returning this when handling arg will result in + * arg3 and any other fields being skipped. The next call to userfunc + * will be the JSON_C_VISIT_SECOND call on "foo", followed by a userfunc + * call on "bar". + *
+ * {
+ *   "foo": {
+ *     "arg1": 1,
+ *     "arg2": 2,
+ *     "arg3": 3,
+ *     ...
+ *   },
+ *   "bar": {
+ *     ...
+ *   }
+ * }
+ * 
+ */ +#define JSON_C_VISIT_RETURN_POP 767 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should stop immediately, and cause json_c_visit to return success. + */ +#define JSON_C_VISIT_RETURN_STOP 7867 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should stop immediately, and cause json_c_visit to return an error. + */ +#define JSON_C_VISIT_RETURN_ERROR -1 + +#endif /* _json_c_json_visit_h_ */ diff --git a/plugins/CommonUtil/json-c/libjson.c b/phlib/jsonc/libjson.c similarity index 98% rename from plugins/CommonUtil/json-c/libjson.c rename to phlib/jsonc/libjson.c index 5284fd0e70b7..b19df42f7298 100644 --- a/plugins/CommonUtil/json-c/libjson.c +++ b/phlib/jsonc/libjson.c @@ -16,7 +16,7 @@ #define __warn_references(sym,msg) /* nothing */ #endif -#endif +#endif #include "json_object.h" diff --git a/plugins/CommonUtil/json-c/linkhash.c b/phlib/jsonc/linkhash.c similarity index 78% rename from plugins/CommonUtil/json-c/linkhash.c rename to phlib/jsonc/linkhash.c index 50de485638e7..a8c78ca44baa 100644 --- a/plugins/CommonUtil/json-c/linkhash.c +++ b/phlib/jsonc/linkhash.c @@ -10,6 +10,8 @@ * */ +#include "config.h" + #include #include #include @@ -17,15 +19,39 @@ #include #include -#include - #ifdef HAVE_ENDIAN_H # include /* attempt to define endianness */ #endif +#if defined(_MSC_VER) || defined(__MINGW32__) +# define WIN32_LEAN_AND_MEAN +# include /* Get InterlockedCompareExchange */ +#endif + #include "random_seed.h" #include "linkhash.h" +/* hash functions */ +static unsigned long lh_char_hash(const void *k); +static unsigned long lh_perllike_str_hash(const void *k); +static lh_hash_fn *char_hash_fn = lh_char_hash; + +int +json_global_set_string_hash(const int h) +{ + switch(h) { + case JSON_C_STR_HASH_DFLT: + char_hash_fn = lh_char_hash; + break; + case JSON_C_STR_HASH_PERLLIKE: + char_hash_fn = lh_perllike_str_hash; + break; + default: + return -1; + } + return 0; +} + void lh_abort(const char *msg, ...) { va_list ap; @@ -35,7 +61,7 @@ void lh_abort(const char *msg, ...) exit(1); } -unsigned long lh_ptr_hash(const void *k) +static unsigned long lh_ptr_hash(const void *k) { /* CAW: refactored to be 64bit nice */ return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); @@ -46,7 +72,7 @@ int lh_ptr_equal(const void *k1, const void *k2) return (k1 == k2); } -/* +/* * hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain. * http://burtleburtle.net/bob/c/lookup3.c * minor modifications to make functions static so no symbols are exported @@ -58,8 +84,8 @@ int lh_ptr_equal(const void *k1, const void *k2) lookup3.c, by Bob Jenkins, May 2006, Public Domain. These are functions for producing 32-bit hashes for hash table lookup. -hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() -are externally useful functions. Routines to test the hash are included +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included if SELF_TEST is defined. You can use this free for any purpose. It's in the public domain. It has no warranty. @@ -67,7 +93,7 @@ You probably want to use hashlittle(). hashlittle() and hashbig() hash byte arrays. hashlittle() is is faster than hashbig() on little-endian machines. Intel and AMD are little-endian machines. On second thought, you probably want hashlittle2(), which is identical to -hashlittle() except it returns two 32-bit hashes for the price of one. +hashlittle() except it returns two 32-bit hashes for the price of one. You could implement hashbig2() if you wanted but I haven't bothered here. If you want to find a hash of, say, exactly 7 integers, do @@ -80,9 +106,9 @@ If you want to find a hash of, say, exactly 7 integers, do then use c as the hash value. If you have a variable length array of 4-byte integers to hash, use hashword(). If you have a byte array (like a character string), use hashlittle(). If you have several byte arrays, or -a mix of things, see the comments above hashlittle(). +a mix of things, see the comments above hashlittle(). -Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then mix those integers. This is fast (you can do a lot more thorough mixing with 12*3 instructions on 3 integers than you can with 3 instructions on 1 byte), but shoehorning those bytes into integers efficiently is messy. @@ -131,7 +157,7 @@ This was tested for: the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. -* the base values were pseudorandom, all zero but one bit set, or +* the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that @@ -141,7 +167,7 @@ satisfy this are 14 9 3 7 17 3 Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined as + with a one-bit base and a two-bit delta. I -used http://burtleburtle.net/bob/hash/avalanche.html to choose +used http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, constants, and arrangements of the variables. This does not achieve avalanche. There are input bits of (a,b,c) @@ -180,7 +206,7 @@ produce values of c that look totally different. This was tested for the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. -* the base values were pseudorandom, all zero but one bit set, or +* the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. These constants passed: @@ -255,7 +281,7 @@ static uint32_t hashlittle( const void *key, size_t length, uint32_t initval) } /*----------------------------- handle the last (probably partial) block */ - /* + /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the @@ -263,8 +289,19 @@ static uint32_t hashlittle( const void *key, size_t length, uint32_t initval) * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). + * AddressSanitizer is similarly picky about overrunning + * the buffer. (http://clang.llvm.org/docs/AddressSanitizer.html */ -#ifndef VALGRIND +#ifdef VALGRIND +# define PRECISE_MEMORY_ACCESS 1 +#elif defined(__SANITIZE_ADDRESS__) /* GCC's ASAN */ +# define PRECISE_MEMORY_ACCESS 1 +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) /* Clang's ASAN */ +# define PRECISE_MEMORY_ACCESS 1 +# endif +#endif +#ifndef PRECISE_MEMORY_ACCESS switch(length) { @@ -378,17 +415,17 @@ static uint32_t hashlittle( const void *key, size_t length, uint32_t initval) /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { - case 12: c+=((uint32_t)k[11])<<24; - case 11: c+=((uint32_t)k[10])<<16; - case 10: c+=((uint32_t)k[9])<<8; - case 9 : c+=k[8]; - case 8 : b+=((uint32_t)k[7])<<24; - case 7 : b+=((uint32_t)k[6])<<16; - case 6 : b+=((uint32_t)k[5])<<8; - case 5 : b+=k[4]; - case 4 : a+=((uint32_t)k[3])<<24; - case 3 : a+=((uint32_t)k[2])<<16; - case 2 : a+=((uint32_t)k[1])<<8; + case 12: c+=((uint32_t)k[11])<<24; /* FALLTHRU */ + case 11: c+=((uint32_t)k[10])<<16; /* FALLTHRU */ + case 10: c+=((uint32_t)k[9])<<8; /* FALLTHRU */ + case 9 : c+=k[8]; /* FALLTHRU */ + case 8 : b+=((uint32_t)k[7])<<24; /* FALLTHRU */ + case 7 : b+=((uint32_t)k[6])<<16; /* FALLTHRU */ + case 6 : b+=((uint32_t)k[5])<<8; /* FALLTHRU */ + case 5 : b+=k[4]; /* FALLTHRU */ + case 4 : a+=((uint32_t)k[3])<<24; /* FALLTHRU */ + case 3 : a+=((uint32_t)k[2])<<16; /* FALLTHRU */ + case 2 : a+=((uint32_t)k[1])<<8; /* FALLTHRU */ case 1 : a+=k[0]; break; case 0 : return c; @@ -399,25 +436,53 @@ static uint32_t hashlittle( const void *key, size_t length, uint32_t initval) return c; } -unsigned long lh_char_hash(const void *k) +/* a simple hash function similiar to what perl does for strings. + * for good results, the string should not be excessivly large. + */ +static unsigned long lh_perllike_str_hash(const void *k) { - static volatile int random_seed = -1; + const char *rkey = (const char *)k; + unsigned hashval = 1; + + while (*rkey) + hashval = hashval * 33 + *rkey++; + + return hashval; +} + +static unsigned long lh_char_hash(const void *k) +{ +#if defined _MSC_VER || defined __MINGW32__ +#define RANDOM_SEED_TYPE LONG +#else +#define RANDOM_SEED_TYPE int +#endif + static volatile RANDOM_SEED_TYPE random_seed = -1; if (random_seed == -1) { - int seed; + RANDOM_SEED_TYPE seed; /* we can't use -1 as it is the unitialized sentinel */ while ((seed = json_c_get_random_seed()) == -1); -#if defined __GNUC__ - __sync_val_compare_and_swap(&random_seed, -1, seed); -#elif defined _MSC_VER +#if SIZEOF_INT == 8 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if SIZEOF_INT == 4 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if SIZEOF_INT == 2 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if defined USE_SYNC_COMPARE_AND_SWAP + (void)__sync_val_compare_and_swap(&random_seed, -1, seed); +#elif defined _MSC_VER || defined __MINGW32__ InterlockedCompareExchange(&random_seed, seed, -1); #else -#warning "racy random seed initializtion if used by multiple threads" +//#warning "racy random seed initializtion if used by multiple threads" random_seed = seed; /* potentially racy */ #endif } - return hashlittle((const char*)k, strlen((const char*)k), random_seed); + return hashlittle((const char*)k, strlen((const char*)k), random_seed); } int lh_char_equal(const void *k1, const void *k2) @@ -425,7 +490,7 @@ int lh_char_equal(const void *k1, const void *k2) return (strcmp((const char*)k1, (const char*)k2) == 0); } -struct lh_table* lh_table_new(int size, const char *name, +struct lh_table* lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, lh_equal_fn *equal_fn) @@ -434,12 +499,17 @@ struct lh_table* lh_table_new(int size, const char *name, struct lh_table *t; t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); - if(!t) lh_abort("lh_table_new: calloc failed\n"); + if (!t) + return NULL; + t->count = 0; t->size = size; - t->name = name; t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry)); - if(!t->table) lh_abort("lh_table_new: calloc failed\n"); + if (!t->table) + { + free(t); + return NULL; + } t->free_fn = free_fn; t->hash_fn = hash_fn; t->equal_fn = equal_fn; @@ -447,68 +517,78 @@ struct lh_table* lh_table_new(int size, const char *name, return t; } -struct lh_table* lh_kchar_table_new(int size, const char *name, +struct lh_table* lh_kchar_table_new(int size, lh_entry_free_fn *free_fn) { - return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); + return lh_table_new(size, free_fn, char_hash_fn, lh_char_equal); } -struct lh_table* lh_kptr_table_new(int size, const char *name, +struct lh_table* lh_kptr_table_new(int size, lh_entry_free_fn *free_fn) { - return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); + return lh_table_new(size, free_fn, lh_ptr_hash, lh_ptr_equal); } -void lh_table_resize(struct lh_table *t, int new_size) +int lh_table_resize(struct lh_table *t, int new_size) { struct lh_table *new_t; struct lh_entry *ent; - new_t = lh_table_new(new_size, t->name, NULL, t->hash_fn, t->equal_fn); - ent = t->head; - while(ent) { - lh_table_insert(new_t, ent->k, ent->v); - ent = ent->next; + new_t = lh_table_new(new_size, NULL, t->hash_fn, t->equal_fn); + if (new_t == NULL) + return -1; + + for (ent = t->head; ent != NULL; ent = ent->next) + { + unsigned long h = lh_get_hash(new_t, ent->k); + unsigned int opts = 0; + if (ent->k_is_constant) + opts = JSON_C_OBJECT_KEY_IS_CONSTANT; + if (lh_table_insert_w_hash(new_t, ent->k, ent->v, h, opts) != 0) + { + lh_table_free(new_t); + return -1; + } } free(t->table); t->table = new_t->table; t->size = new_size; t->head = new_t->head; t->tail = new_t->tail; - t->resizes++; free(new_t); + + return 0; } void lh_table_free(struct lh_table *t) { struct lh_entry *c; - for(c = t->head; c != NULL; c = c->next) { - if(t->free_fn) { + if(t->free_fn) { + for(c = t->head; c != NULL; c = c->next) t->free_fn(c); - } } free(t->table); free(t); } -int lh_table_insert(struct lh_table *t, void *k, const void *v) +int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, const unsigned long h, const unsigned opts) { - unsigned long h, n; + unsigned long n; - t->inserts++; - if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); + if (t->count >= t->size * LH_LOAD_FACTOR) + if (lh_table_resize(t, t->size * 2) != 0) + return -1; - h = t->hash_fn(k); n = h % t->size; while( 1 ) { if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; - t->collisions++; if ((int)++n == t->size) n = 0; } t->table[n].k = k; + t->table[n].k_is_constant = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT); t->table[n].v = v; t->count++; @@ -524,15 +604,17 @@ int lh_table_insert(struct lh_table *t, void *k, const void *v) return 0; } +int lh_table_insert(struct lh_table *t, const void *k, const void *v) +{ + return lh_table_insert_w_hash(t, k, v, lh_get_hash(t, k), 0); +} -struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +struct lh_entry* lh_table_lookup_entry_w_hash(struct lh_table *t, const void *k, const unsigned long h) { - unsigned long h = t->hash_fn(k); unsigned long n = h % t->size; int count = 0; - t->lookups++; while( count < t->size ) { if(t->table[n].k == LH_EMPTY) return NULL; if(t->table[n].k != LH_FREED && @@ -543,6 +625,10 @@ struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) return NULL; } +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + return lh_table_lookup_entry_w_hash(t, k, lh_get_hash(t, k)); +} const void* lh_table_lookup(struct lh_table *t, const void *k) { @@ -555,11 +641,11 @@ json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) { struct lh_entry *e = lh_table_lookup_entry(t, k); if (e != NULL) { - if (v != NULL) *v = (void *)e->v; + if (v != NULL) *v = lh_entry_v(e); return TRUE; /* key found */ } if (v != NULL) *v = NULL; - return FALSE; /* key not found */ + return FALSE; /* key not found */ } int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) diff --git a/phlib/jsonc/linkhash.h b/phlib/jsonc/linkhash.h new file mode 100644 index 000000000000..9c2f5c1b2581 --- /dev/null +++ b/phlib/jsonc/linkhash.h @@ -0,0 +1,406 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Internal methods for working with json_type_object objects. Although + * this is exposed by the json_object_get_object() function and within the + * json_object_iter type, it is not recommended for direct use. + */ +#ifndef _linkhash_h_ +#define _linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +/** + * default string hash function + */ +#define JSON_C_STR_HASH_DFLT 0 + +/** + * perl-like string hash function + */ +#define JSON_C_STR_HASH_PERLLIKE 1 + +/** + * This function sets the hash function to be used for strings. + * Must be one of the JSON_C_STR_HASH_* values. + * @returns 0 - ok, -1 if parameter was invalid + */ +int json_global_set_string_hash(const int h); + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (const void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (const void *k1, const void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. Use lh_entry_k() instead of accessing this directly. + */ + const void *k; + /** + * A flag for users of linkhash to know whether or not they + * need to free k. + */ + int k_is_constant; + /** + * The value. Use lh_entry_v() instead of accessing this directly. + */ + const void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; +typedef struct lh_table lh_table; + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + * + * @param table a struct lh_table * to iterate over + * @param entry a struct lh_entry * variable to hold each element + * @param tmp a struct lh_entry * variable to hold a temporary pointer to the next element + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table* lh_table_new(int size, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash table with char keys. + * + * @param size initial table size. + * @param free_fn callback function used to free memory for entries. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table* lh_kchar_table_new(int size, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash table with ptr keys. + * + * @param size initial table size. + * @param free_fn callback function used to free memory for entries. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table* lh_kptr_table_new(int size, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * + * If a lh_entry_free_fn callback free function was provided then it is + * called for all entries in the table. + * + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +extern int lh_table_insert(struct lh_table *t, const void *k, const void *v); + + +/** + * Insert a record into the table using a precalculated key hash. + * + * The hash h, which should be calculated with lh_get_hash() on k, is provided by + * the caller, to allow for optimization when multiple operations with the same + * key are known to be needed. + * + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + * @param h hash value of the key to insert + * @param opts if set to JSON_C_OBJECT_KEY_IS_CONSTANT, sets lh_entry.k_is_constant + * so t's free function knows to avoid freeing the key. + */ +extern int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, const unsigned long h, const unsigned opts); + + +/** + * Lookup a record in the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record in the table using a precalculated key hash. + * + * The hash h, which should be calculated with lh_get_hash() on k, is provided by + * the caller, to allow for optimization when multiple operations with the same + * key are known to be needed. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param h hash value of the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry_w_hash(struct lh_table *t, const void *k, const unsigned long h); + +/** + * Lookup a record into the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex() instead. + */ +THIS_FUNCTION_IS_DEPRECATED(extern const void* lh_table_lookup(struct lh_table *t, const void *k)); + +/** + * Lookup a record in the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +extern int lh_table_length(struct lh_table *t); + +/** + * Prints a message to stdout, + * then exits the program with an exit code of 1. + * + * @param msg Message format string, like for printf. + * @param ... Format args. + * + * @deprecated Since it is not a good idea to exit the entire program + * because of an internal library failure, json-c will no longer + * use this function internally. + * However, because its interface is public, it will remain part of + * the API on the off chance of legacy software using it externally. + */ +THIS_FUNCTION_IS_DEPRECATED(void lh_abort(const char *msg, ...)); + +/** + * Resizes the specified table. + * + * @param t Pointer to table to resize. + * @param new_size New table size. Must be positive. + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +int lh_table_resize(struct lh_table *t, int new_size); + + +/** + * @deprecated Don't use this outside of linkhash.h: + */ +#if !defined(_MSC_VER) || (_MSC_VER > 1800) +/* VS2010 can't handle inline funcs, so skip it there */ +#define _LH_INLINE inline +#else +#define _LH_INLINE +#endif + +/** + * Calculate the hash of a key for a given table. + * + * This is an exension to support functions that need to calculate + * the hash several times and allows them to do it just once and then pass + * in the hash to all utility functions. Depending on use case, this can be a + * considerable performance improvement. + * @param t the table (used to obtain hash function) + * @param k a pointer to the key to lookup + * @return the key's hash + */ +static _LH_INLINE unsigned long lh_get_hash(const struct lh_table *t, const void *k) +{ + return t->hash_fn(k); +} + +#undef _LH_INLINE + +/** + * @deprecated Don't use this outside of linkhash.h: + */ +#ifdef __UNCONST +#define _LH_UNCONST(a) __UNCONST(a) +#else +#define _LH_UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) +#endif + +/** + * Return a non-const version of lh_entry.k. + * + * lh_entry.k is const to indicate and help ensure that linkhash itself doesn't modify + * it, but callers are allowed to do what they want with it. + * See also lh_entry.k_is_constant + */ +#define lh_entry_k(entry) _LH_UNCONST((entry)->k) + +/** + * Return a non-const version of lh_entry.v. + * + * v is const to indicate and help ensure that linkhash itself doesn't modify + * it, but callers are allowed to do what they want with it. + */ +#define lh_entry_v(entry) _LH_UNCONST((entry)->v) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json-c/math_compat.h b/phlib/jsonc/math_compat.h similarity index 54% rename from plugins/CommonUtil/json-c/math_compat.h rename to phlib/jsonc/math_compat.h index f40b8faf8fd4..d530537b94f9 100644 --- a/plugins/CommonUtil/json-c/math_compat.h +++ b/phlib/jsonc/math_compat.h @@ -1,7 +1,12 @@ #ifndef __math_compat_h #define __math_compat_h -/* Define isnan and isinf on Windows/MSVC */ +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* Define isnan, isinf, infinity and nan on Windows/MSVC */ #ifndef HAVE_DECL_ISNAN # ifdef HAVE_DECL__ISNAN @@ -17,12 +22,15 @@ # endif #endif -#ifndef HAVE_DECL_NAN -#error This platform does not have nan() +#ifndef HAVE_DECL_INFINITY +#include +#define INFINITY (DBL_MAX + DBL_MAX) +#define HAVE_DECL_INFINITY #endif -#ifndef HAVE_DECL_INFINITY -#error This platform does not have INFINITY +#ifndef HAVE_DECL_NAN +#define NAN (INFINITY - INFINITY) +#define HAVE_DECL_NAN #endif #endif diff --git a/plugins/CommonUtil/json-c/printbuf.c b/phlib/jsonc/printbuf.c similarity index 71% rename from plugins/CommonUtil/json-c/printbuf.c rename to phlib/jsonc/printbuf.c index 94d41b0d6a5e..b57275e9b5f9 100644 --- a/plugins/CommonUtil/json-c/printbuf.c +++ b/phlib/jsonc/printbuf.c @@ -25,9 +25,10 @@ # error Not enough var arg support! #endif /* HAVE_STDARG_H */ -#include "bits.h" #include "debug.h" #include "printbuf.h" +#include "snprintf_compat.h" +#include "vasprintf_compat.h" static int printbuf_extend(struct printbuf *p, size_t min_size); @@ -43,6 +44,7 @@ struct printbuf* printbuf_new(void) free(p); return NULL; } + p->buf[0]= '\0'; return p; } @@ -63,7 +65,9 @@ static int printbuf_extend(struct printbuf *p, size_t min_size) if (p->size >= min_size) return 0; - new_size = json_max(p->size * 2, min_size + 8); + new_size = p->size * 2; + if (new_size < min_size + 8) + new_size = min_size + 8; #ifdef PRINTBUF_DEBUG MC_DEBUG("printbuf_memappend: realloc " "bpos=%d min_size=%d old_size=%d new_size=%d\n", @@ -76,7 +80,7 @@ static int printbuf_extend(struct printbuf *p, size_t min_size) return 0; } -size_t printbuf_memappend(struct printbuf *p, const char *buf, size_t size) +int printbuf_memappend(struct printbuf *p, const char *buf, size_t size) { if (p->size <= p->bpos + size + 1) { if (printbuf_extend(p, p->bpos + size + 1) < 0) @@ -85,7 +89,22 @@ size_t printbuf_memappend(struct printbuf *p, const char *buf, size_t size) memcpy(p->buf + p->bpos, buf, size); p->bpos += size; p->buf[p->bpos]= '\0'; - return size; + return (int)size; +} + +// modified by dmex +void printbuf_memappend_fast(struct printbuf *p, const char *bufptr, size_t bufsize) +{ + if ((p->size - p->bpos) > bufsize) + { + memcpy(p->buf + p->bpos, (bufptr), bufsize); + p->bpos += (int)bufsize; + p->buf[p->bpos] = '\0'; + } + else + { + printbuf_memappend(p, (bufptr), bufsize); + } } int printbuf_memset(struct printbuf *pb, size_t offset, int charvalue, size_t len) @@ -108,49 +127,6 @@ int printbuf_memset(struct printbuf *pb, size_t offset, int charvalue, size_t le return 0; } -#if !defined(HAVE_VSNPRINTF) && defined(_MSC_VER) -# define vsnprintf _vsnprintf -#elif !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ -# error Need vsnprintf! -#endif /* !HAVE_VSNPRINTF && defined(_WIN32) */ - -#if !defined(HAVE_VASPRINTF) -/* CAW: compliant version of vasprintf */ -static int vasprintf(char **buf, const char *fmt, va_list ap) -{ -#ifndef _WIN32 - static char _T_emptybuffer = '\0'; -#endif /* !defined(_WIN32) */ - int chars; - char *b; - - if(!buf) { return -1; } - -#ifdef _WIN32 - chars = _vscprintf(fmt, ap)+1; -#else /* !defined(_WIN32) */ - /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite - our buffer like on some 64bit sun systems.... but hey, its time to move on */ - chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; - if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ -#endif /* defined(_WIN32) */ - - b = (char*)malloc(sizeof(char) * chars); - if(!b) { return -1; } - - if ((chars = vsprintf_s(b, sizeof(char), fmt, ap)) < 0) - { - free(b); - } - else - { - *buf = b; - } - - return chars; -} -#endif /* !HAVE_VASPRINTF */ - int sprintbuf(struct printbuf *p, const char *msg, ...) { va_list ap; @@ -160,7 +136,7 @@ int sprintbuf(struct printbuf *p, const char *msg, ...) /* user stack buffer first */ va_start(ap, msg); - size = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, msg, ap); + size = vsnprintf(buf, 128, msg, ap); va_end(ap); /* if string is greater than stack buffer, then use dynamic string with vasprintf. Note: some implementation of vsnprintf return -1 diff --git a/phlib/jsonc/printbuf.h b/phlib/jsonc/printbuf.h new file mode 100644 index 000000000000..e653cba7b3d3 --- /dev/null +++ b/phlib/jsonc/printbuf.h @@ -0,0 +1,117 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +/** + * @file + * @brief Internal string buffer handing. Unless you're writing a + * json_object_to_json_string_fn implementation for use with + * json_object_set_serializer() direct use of this is not + * recommended. + */ +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf { + char *buf; + size_t bpos; + size_t size; +}; +typedef struct printbuf printbuf; + +extern struct printbuf* +printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast() is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * + * Your code should not use printbuf_memappend() directly unless it + * checks the return code. Use printbuf_memappend_fast() instead. + */ +extern int +printbuf_memappend(struct printbuf *p, const char *buf, size_t size); + +// printbuf_memappend_fast(p, bufptr, bufsize) +// Modified by dmex. +void printbuf_memappend_fast(struct printbuf *p, const char *bufptr, size_t bufsize); + +#define printbuf_length(p) ((p)->bpos) + +/** + * Results in a compile error if the argument is not a string literal. + */ +#define _printbuf_check_literal(mystr) ("" mystr) + +/** + * This is an optimization wrapper around printbuf_memappend() that is useful + * for appending string literals. Since the size of string constants is known + * at compile time, using this macro can avoid a costly strlen() call. This is + * especially helpful when a constant string must be appended many times. If + * you got here because of a compilation error caused by passing something + * other than a string literal, use printbuf_memappend_fast() in conjunction + * with strlen(). + * + * See also: + * printbuf_memappend_fast() + * printbuf_memappend() + * sprintbuf() + */ +#define printbuf_strappend(pb, str) \ + printbuf_memappend ((pb), _printbuf_check_literal(str), sizeof(str) - 1) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +extern int +printbuf_memset(struct printbuf *pb, size_t offset, int charvalue, size_t len); + +/** + * Formatted print to printbuf. + * + * This function is the most expensive of the available functions for appending + * string data to a printbuf and should be used only where convenience is more + * important than speed. Avoid using this function in high performance code or + * tight loops; in these scenarios, consider using snprintf() with a static + * buffer in conjunction with one of the printbuf_*append() functions. + * + * See also: + * printbuf_memappend_fast() + * printbuf_memappend() + * printbuf_strappend() + */ +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json-c/random_seed.c b/phlib/jsonc/random_seed.c similarity index 91% rename from plugins/CommonUtil/json-c/random_seed.c rename to phlib/jsonc/random_seed.c index ece374e5b74e..f0b92b9fd3a7 100644 --- a/plugins/CommonUtil/json-c/random_seed.c +++ b/phlib/jsonc/random_seed.c @@ -9,8 +9,10 @@ * */ +#include "strerror_override.h" #include #include "config.h" +#include "random_seed.h" #define DEBUG_SEED(s) @@ -127,7 +129,6 @@ static int get_rdrand_seed() #include #include #include -#include #include #include @@ -150,23 +151,20 @@ static int has_dev_urandom() static int get_dev_random_seed() { DEBUG_SEED("get_dev_random_seed"); - + int fd = open(dev_random_file, O_RDONLY); if (fd < 0) { fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); exit(1); } - + int r; ssize_t nread = read(fd, &r, sizeof(r)); if (nread != sizeof(r)) { - fprintf(stderr, "error read %s: %s", dev_random_file, strerror(errno)); - exit(1); - } - else if (nread != sizeof(r)) { - fprintf(stderr, "error short read %s", dev_random_file); + fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno)); exit(1); } + close(fd); return r; } @@ -181,28 +179,30 @@ static int get_dev_random_seed() #define HAVE_CRYPTGENRANDOM 1 #include +#include +#ifndef __GNUC__ +#pragma comment(lib, "advapi32.lib") +#endif static int get_cryptgenrandom_seed() { - DEBUG_SEED("get_cryptgenrandom_seed"); - HCRYPTPROV hProvider = 0; int r; - - if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - { + + DEBUG_SEED("get_cryptgenrandom_seed"); + + if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { fprintf(stderr, "error CryptAcquireContextW"); exit(1); } - - if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) - { + + if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) { fprintf(stderr, "error CryptGenRandom"); exit(1); } - + CryptReleaseContext(hProvider, 0); - + return r; } @@ -216,7 +216,7 @@ static int get_cryptgenrandom_seed() static int get_time_seed() { DEBUG_SEED("get_time_seed"); - + return (int)time(NULL) * 433494437; } diff --git a/plugins/CommonUtil/json-c/random_seed.h b/phlib/jsonc/random_seed.h similarity index 81% rename from plugins/CommonUtil/json-c/random_seed.h rename to phlib/jsonc/random_seed.h index 7362d67d9cd5..2f43dad1f35f 100644 --- a/plugins/CommonUtil/json-c/random_seed.h +++ b/phlib/jsonc/random_seed.h @@ -9,6 +9,10 @@ * */ +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ #ifndef seed_h #define seed_h diff --git a/phlib/jsonc/snprintf_compat.h b/phlib/jsonc/snprintf_compat.h new file mode 100644 index 000000000000..69b11d94e94e --- /dev/null +++ b/phlib/jsonc/snprintf_compat.h @@ -0,0 +1,41 @@ +#ifndef __snprintf_compat_h +#define __snprintf_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* + * Microsoft's _vsnprintf and _snprint don't always terminate + * the string, so use wrappers that ensure that. + */ + +#include + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) +static int json_c_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + int ret; + ret = _vsnprintf(str, size, format, ap); + str[size - 1] = '\0'; + return ret; +} +#define vsnprintf json_c_vsnprintf + +static int json_c_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + ret = json_c_vsnprintf(str, size, format, ap); + va_end(ap); + return ret; +} +#define snprintf json_c_snprintf + +#elif !defined(HAVE_SNPRINTF) /* !HAVE_SNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_SNPRINTF && defined(_WIN32) */ + +#endif /* __snprintf_compat_h */ diff --git a/phlib/jsonc/strdup_compat.h b/phlib/jsonc/strdup_compat.h new file mode 100644 index 000000000000..9c523596e36a --- /dev/null +++ b/phlib/jsonc/strdup_compat.h @@ -0,0 +1,16 @@ +#ifndef __strdup_compat_h +#define __strdup_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#endif diff --git a/phlib/jsonc/strerror_override.c b/phlib/jsonc/strerror_override.c new file mode 100644 index 000000000000..b87cb494c93e --- /dev/null +++ b/phlib/jsonc/strerror_override.c @@ -0,0 +1,105 @@ +#define STRERROR_OVERRIDE_IMPL 1 +#include "strerror_override.h" + +#pragma warning(push) +#pragma warning(disable: 4996) + +/* + * Override strerror() to get consistent output across platforms. + */ + +static struct { + int errno_value; + const char *errno_str; +} errno_list[] = { +#define STRINGIFY(x) #x +#define ENTRY(x) {x, &STRINGIFY(undef_ ## x)[6]} + ENTRY(EPERM), + ENTRY(ENOENT), + ENTRY(ESRCH), + ENTRY(EINTR), + ENTRY(EIO), + ENTRY(ENXIO), + ENTRY(E2BIG), + ENTRY(ENOEXEC), + ENTRY(EBADF), + ENTRY(ECHILD), + ENTRY(EDEADLK), + ENTRY(ENOMEM), + ENTRY(EACCES), + ENTRY(EFAULT), +#ifdef ENOTBLK + ENTRY(ENOTBLK), +#endif + ENTRY(EBUSY), + ENTRY(EEXIST), + ENTRY(EXDEV), + ENTRY(ENODEV), + ENTRY(ENOTDIR), + ENTRY(EISDIR), + ENTRY(EINVAL), + ENTRY(ENFILE), + ENTRY(EMFILE), + ENTRY(ENOTTY), +#ifdef ETXTBSY + ENTRY(ETXTBSY), +#endif + ENTRY(EFBIG), + ENTRY(ENOSPC), + ENTRY(ESPIPE), + ENTRY(EROFS), + ENTRY(EMLINK), + ENTRY(EPIPE), + ENTRY(EDOM), + ENTRY(ERANGE), + ENTRY(EAGAIN), + { 0, (char *)0 } +}; + +// Enabled during tests +int _json_c_strerror_enable = 0; + +#define PREFIX "ERRNO=" +static char errno_buf[128] = PREFIX; +char *_json_c_strerror(int errno_in) +{ + int start_idx; + char digbuf[20]; + int ii, jj; + + if (!_json_c_strerror_enable) + return strerror(errno_in); + + // Avoid standard functions, so we don't need to include any + // headers, or guess at signatures. + + for (ii = 0; errno_list[ii].errno_str != (char *)0; ii++) + { + const char *errno_str = errno_list[ii].errno_str; + if (errno_list[ii].errno_value != errno_in) + continue; + + for (start_idx = sizeof(PREFIX) - 1, jj = 0; errno_str[jj] != '\0'; jj++, start_idx++) + { + errno_buf[start_idx] = errno_str[jj]; + } + errno_buf[start_idx] = '\0'; + return errno_buf; + } + + // It's not one of the known errno values, return the numeric value. + for (ii = 0; errno_in > 10; errno_in /= 10, ii++) + { + digbuf[ii] = "0123456789"[(errno_in % 10)]; + } + digbuf[ii] = "0123456789"[(errno_in % 10)]; + + // Reverse the digits + for (start_idx = sizeof(PREFIX) - 1 ; ii >= 0; ii--, start_idx++) + { + errno_buf[start_idx] = digbuf[ii]; + } + return errno_buf; +} + +#pragma warning(pop) diff --git a/phlib/jsonc/strerror_override.h b/phlib/jsonc/strerror_override.h new file mode 100644 index 000000000000..567438180439 --- /dev/null +++ b/phlib/jsonc/strerror_override.h @@ -0,0 +1,30 @@ +#ifndef _json_strerror_override_h_ +#define _json_strerror_override_h_ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "config.h" +#include + +#include "json_object.h" /* for JSON_EXPORT */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +JSON_EXPORT char *_json_c_strerror(int errno_in); + +#ifndef STRERROR_OVERRIDE_IMPL +#define strerror _json_c_strerror +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _json_strerror_override_h_ */ diff --git a/phlib/jsonc/strerror_override_private.h b/phlib/jsonc/strerror_override_private.h new file mode 100644 index 000000000000..5180b4f30764 --- /dev/null +++ b/phlib/jsonc/strerror_override_private.h @@ -0,0 +1,12 @@ +#ifndef __json_strerror_override_private_h__ +#define __json_strerror_override_private_h__ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* Used by tests to get consistent output */ +extern int _json_c_strerror_enable; + +#endif diff --git a/phlib/jsonc/vasprintf_compat.h b/phlib/jsonc/vasprintf_compat.h new file mode 100644 index 000000000000..b1d12e9b143d --- /dev/null +++ b/phlib/jsonc/vasprintf_compat.h @@ -0,0 +1,51 @@ +#ifndef __vasprintf_compat_h +#define __vasprintf_compat_h + +#pragma warning(push) +#pragma warning(disable: 4996) + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "snprintf_compat.h" + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef _WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(_WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef _WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(_WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(_WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +#pragma warning(pop) + +#endif /* __vasprintf_compat_h */ diff --git a/phlib/kph.c b/phlib/kph.c index 3e427585210f..95e510f7f94e 100644 --- a/phlib/kph.c +++ b/phlib/kph.c @@ -1,1102 +1,1160 @@ -/* - * Process Hacker - - * KProcessHacker API - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include - -HANDLE PhKphHandle = NULL; -BOOLEAN PhKphVerified; -KPH_KEY PhKphL1Key; - -NTSTATUS KphConnect( - _In_opt_ PWSTR DeviceName - ) -{ - NTSTATUS status; - HANDLE kphHandle; - UNICODE_STRING objectName; - OBJECT_ATTRIBUTES objectAttributes; - IO_STATUS_BLOCK isb; - OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; - - if (PhKphHandle) - return STATUS_ADDRESS_ALREADY_EXISTS; - - if (DeviceName) - RtlInitUnicodeString(&objectName, DeviceName); - else - RtlInitUnicodeString(&objectName, KPH_DEVICE_NAME); - - InitializeObjectAttributes( - &objectAttributes, - &objectName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - status = NtOpenFile( - &kphHandle, - FILE_GENERIC_READ | FILE_GENERIC_WRITE, - &objectAttributes, - &isb, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_NON_DIRECTORY_FILE - ); - - if (NT_SUCCESS(status)) - { - // Protect the handle from being closed. - - handleFlagInfo.Inherit = FALSE; - handleFlagInfo.ProtectFromClose = TRUE; - - NtSetInformationObject( - kphHandle, - ObjectHandleFlagInformation, - &handleFlagInfo, - sizeof(OBJECT_HANDLE_FLAG_INFORMATION) - ); - - PhKphHandle = kphHandle; - PhKphVerified = FALSE; - PhKphL1Key = 0; - } - - return status; -} - -NTSTATUS KphConnect2( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName - ) -{ - return KphConnect2Ex(DeviceName, FileName, NULL); -} - -NTSTATUS KphConnect2Ex( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName, - _In_opt_ PKPH_PARAMETERS Parameters - ) -{ - NTSTATUS status; - WCHAR fullDeviceName[256]; - PH_FORMAT format[2]; - SC_HANDLE scmHandle; - SC_HANDLE serviceHandle; - BOOLEAN started = FALSE; - BOOLEAN created = FALSE; - - if (!DeviceName) - DeviceName = KPH_DEVICE_SHORT_NAME; - - PhInitFormatS(&format[0], L"\\Device\\"); - PhInitFormatS(&format[1], DeviceName); - - if (!PhFormatToBuffer(format, 2, fullDeviceName, sizeof(fullDeviceName), NULL)) - return STATUS_NAME_TOO_LONG; - - // Try to open the device. - status = KphConnect(fullDeviceName); - - if (NT_SUCCESS(status) || status == STATUS_ADDRESS_ALREADY_EXISTS) - return status; - - if ( - status != STATUS_NO_SUCH_DEVICE && - status != STATUS_NO_SUCH_FILE && - status != STATUS_OBJECT_NAME_NOT_FOUND && - status != STATUS_OBJECT_PATH_NOT_FOUND - ) - return status; - - // Load the driver, and try again. - - // Try to start the service, if it exists. - - scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); - - if (scmHandle) - { - serviceHandle = OpenService(scmHandle, DeviceName, SERVICE_START); - - if (serviceHandle) - { - if (StartService(serviceHandle, 0, NULL)) - started = TRUE; - - CloseServiceHandle(serviceHandle); - } - - CloseServiceHandle(scmHandle); - } - - if (!started && RtlDoesFileExists_U(FileName)) - { - // Try to create the service. - - scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); - - if (scmHandle) - { - serviceHandle = CreateService( - scmHandle, - DeviceName, - DeviceName, - SERVICE_ALL_ACCESS, - SERVICE_KERNEL_DRIVER, - SERVICE_DEMAND_START, - SERVICE_ERROR_IGNORE, - FileName, - NULL, - NULL, - NULL, - NULL, - L"" - ); - - if (serviceHandle) - { - created = TRUE; - - // Set parameters if the caller supplied them. Note that we fail the entire function - // if this fails, because failing to set parameters like SecurityLevel may result in - // security vulnerabilities. - if (Parameters) - { - status = KphSetParameters(DeviceName, Parameters); - - if (!NT_SUCCESS(status)) - { - // Delete the service and fail. - goto CreateAndConnectEnd; - } - } - - if (StartService(serviceHandle, 0, NULL)) - started = TRUE; - } - - CloseServiceHandle(scmHandle); - } - } - - if (started) - { - // Try to open the device again. - status = KphConnect(fullDeviceName); - } - -CreateAndConnectEnd: - if (created) - { - // "Delete" the service. Since we (may) have a handle to the device, the SCM will delete the - // service automatically when it is stopped (upon reboot). If we don't have a handle to the - // device, the service will get deleted immediately, which is a good thing anyway. - DeleteService(serviceHandle); - CloseServiceHandle(serviceHandle); - } - - return status; -} - -NTSTATUS KphDisconnect( - VOID - ) -{ - NTSTATUS status; - OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; - - if (!PhKphHandle) - return STATUS_ALREADY_DISCONNECTED; - - // Unprotect the handle. - - handleFlagInfo.Inherit = FALSE; - handleFlagInfo.ProtectFromClose = FALSE; - - NtSetInformationObject( - PhKphHandle, - ObjectHandleFlagInformation, - &handleFlagInfo, - sizeof(OBJECT_HANDLE_FLAG_INFORMATION) - ); - - status = NtClose(PhKphHandle); - PhKphHandle = NULL; - PhKphVerified = FALSE; - PhKphL1Key = 0; - - return status; -} - -BOOLEAN KphIsConnected( - VOID - ) -{ - return PhKphHandle != NULL; -} - -BOOLEAN KphIsVerified( - VOID - ) -{ - return PhKphVerified; -} - -NTSTATUS KphSetParameters( - _In_opt_ PWSTR DeviceName, - _In_ PKPH_PARAMETERS Parameters - ) -{ - NTSTATUS status; - HANDLE parametersKeyHandle = NULL; - PPH_STRING parametersKeyName; - ULONG disposition; - UNICODE_STRING valueName; - - if (!DeviceName) - DeviceName = KPH_DEVICE_SHORT_NAME; - - parametersKeyName = PhConcatStrings( - 3, - L"System\\CurrentControlSet\\Services\\", - DeviceName, - L"\\Parameters" - ); - status = PhCreateKey( - ¶metersKeyHandle, - KEY_WRITE | DELETE, - PH_KEY_LOCAL_MACHINE, - ¶metersKeyName->sr, - 0, - 0, - &disposition - ); - PhDereferenceObject(parametersKeyName); - - if (!NT_SUCCESS(status)) - return status; - - RtlInitUnicodeString(&valueName, L"SecurityLevel"); - status = NtSetValueKey(parametersKeyHandle, &valueName, 0, REG_DWORD, &Parameters->SecurityLevel, sizeof(ULONG)); - - if (!NT_SUCCESS(status)) - goto SetValuesEnd; - - if (Parameters->CreateDynamicConfiguration) - { - KPH_DYN_CONFIGURATION configuration; - - RtlInitUnicodeString(&valueName, L"DynamicConfiguration"); - - configuration.Version = KPH_DYN_CONFIGURATION_VERSION; - configuration.NumberOfPackages = 1; - - if (NT_SUCCESS(KphInitializeDynamicPackage(&configuration.Packages[0]))) - { - status = NtSetValueKey(parametersKeyHandle, &valueName, 0, REG_BINARY, &configuration, sizeof(KPH_DYN_CONFIGURATION)); - - if (!NT_SUCCESS(status)) - goto SetValuesEnd; - } - } - - // Put more parameters here... - -SetValuesEnd: - if (!NT_SUCCESS(status)) - { - // Delete the key if we created it. - if (disposition == REG_CREATED_NEW_KEY) - NtDeleteKey(parametersKeyHandle); - } - - NtClose(parametersKeyHandle); - - return status; -} - -NTSTATUS KphInstall( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName - ) -{ - return KphInstallEx(DeviceName, FileName, NULL); -} - -NTSTATUS KphInstallEx( - _In_opt_ PWSTR DeviceName, - _In_ PWSTR FileName, - _In_opt_ PKPH_PARAMETERS Parameters - ) -{ - NTSTATUS status = STATUS_SUCCESS; - SC_HANDLE scmHandle; - SC_HANDLE serviceHandle; - - if (!DeviceName) - DeviceName = KPH_DEVICE_SHORT_NAME; - - scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); - - if (!scmHandle) - return PhGetLastWin32ErrorAsNtStatus(); - - serviceHandle = CreateService( - scmHandle, - DeviceName, - DeviceName, - SERVICE_ALL_ACCESS, - SERVICE_KERNEL_DRIVER, - SERVICE_SYSTEM_START, - SERVICE_ERROR_IGNORE, - FileName, - NULL, - NULL, - NULL, - NULL, - L"" - ); - - if (serviceHandle) - { - // See KphConnect2Ex for more details. - if (Parameters) - { - status = KphSetParameters(DeviceName, Parameters); - - if (!NT_SUCCESS(status)) - { - DeleteService(serviceHandle); - goto CreateEnd; - } - } - - if (!StartService(serviceHandle, 0, NULL)) - status = PhGetLastWin32ErrorAsNtStatus(); - -CreateEnd: - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - - CloseServiceHandle(scmHandle); - - return status; -} - -NTSTATUS KphUninstall( - _In_opt_ PWSTR DeviceName - ) -{ - NTSTATUS status = STATUS_SUCCESS; - SC_HANDLE scmHandle; - SC_HANDLE serviceHandle; - - if (!DeviceName) - DeviceName = KPH_DEVICE_SHORT_NAME; - - scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); - - if (!scmHandle) - return PhGetLastWin32ErrorAsNtStatus(); - - serviceHandle = OpenService(scmHandle, DeviceName, SERVICE_STOP | DELETE); - - if (serviceHandle) - { - SERVICE_STATUS serviceStatus; - - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); - - if (!DeleteService(serviceHandle)) - status = PhGetLastWin32ErrorAsNtStatus(); - - CloseServiceHandle(serviceHandle); - } - else - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - - CloseServiceHandle(scmHandle); - - return status; -} - -NTSTATUS KphGetFeatures( - _Out_ PULONG Features - ) -{ - struct - { - PULONG Features; - } input = { Features }; - - return KphpDeviceIoControl( - KPH_GETFEATURES, - &input, - sizeof(input) - ); -} - -NTSTATUS KphVerifyClient( - _In_reads_bytes_(SignatureSize) PUCHAR Signature, - _In_ ULONG SignatureSize - ) -{ - NTSTATUS status; - struct - { - PVOID CodeAddress; - PUCHAR Signature; - ULONG SignatureSize; - } input = { KphpWithKeyApcRoutine, Signature, SignatureSize }; - - status = KphpDeviceIoControl( - KPH_VERIFYCLIENT, - &input, - sizeof(input) - ); - - if (NT_SUCCESS(status)) - PhKphVerified = TRUE; - - return status; -} - -NTSTATUS KphOpenProcess( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PCLIENT_ID ClientId - ) -{ - KPH_OPEN_PROCESS_INPUT input = { ProcessHandle, DesiredAccess, ClientId, 0 }; - - if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) == DesiredAccess) - { - KphpGetL1Key(&input.Key); - return KphpDeviceIoControl( - KPH_OPENPROCESS, - &input, - sizeof(input) - ); - } - else - { - return KphpWithKey(KphKeyLevel2, KphpOpenProcessContinuation, &input); - } -} - -NTSTATUS KphOpenProcessToken( - _In_ HANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE TokenHandle - ) -{ - KPH_OPEN_PROCESS_TOKEN_INPUT input = { ProcessHandle, DesiredAccess, TokenHandle, 0 }; - - if ((DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) - { - KphpGetL1Key(&input.Key); - return KphpDeviceIoControl( - KPH_OPENPROCESSTOKEN, - &input, - sizeof(input) - ); - } - else - { - return KphpWithKey(KphKeyLevel2, KphpOpenProcessTokenContinuation, &input); - } -} - -NTSTATUS KphOpenProcessJob( - _In_ HANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE JobHandle - ) -{ - struct - { - HANDLE ProcessHandle; - ACCESS_MASK DesiredAccess; - PHANDLE JobHandle; - } input = { ProcessHandle, DesiredAccess, JobHandle }; - - return KphpDeviceIoControl( - KPH_OPENPROCESSJOB, - &input, - sizeof(input) - ); -} - -NTSTATUS KphTerminateProcess( - _In_ HANDLE ProcessHandle, - _In_ NTSTATUS ExitStatus - ) -{ - NTSTATUS status; - KPH_TERMINATE_PROCESS_INPUT input = { ProcessHandle, ExitStatus, 0 }; - - status = KphpWithKey(KphKeyLevel2, KphpTerminateProcessContinuation, &input); - - // Check if we're trying to terminate the current process, because kernel-mode can't do it. - if (status == STATUS_CANT_TERMINATE_SELF) - { - RtlExitUserProcess(ExitStatus); - } - - return status; -} - -NTSTATUS KphReadVirtualMemoryUnsafe( - _In_opt_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, - _Out_writes_bytes_(BufferSize) PVOID Buffer, - _In_ SIZE_T BufferSize, - _Out_opt_ PSIZE_T NumberOfBytesRead - ) -{ - KPH_READ_VIRTUAL_MEMORY_UNSAFE_INPUT input = { ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead, 0 }; - - return KphpWithKey(KphKeyLevel2, KphpReadVirtualMemoryUnsafeContinuation, &input); -} - -NTSTATUS KphQueryInformationProcess( - _In_ HANDLE ProcessHandle, - _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, - _In_ ULONG ProcessInformationLength, - _Out_opt_ PULONG ReturnLength - ) -{ - struct - { - HANDLE ProcessHandle; - KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; - PVOID ProcessInformation; - ULONG ProcessInformationLength; - PULONG ReturnLength; - } input = { ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength }; - - return KphpDeviceIoControl( - KPH_QUERYINFORMATIONPROCESS, - &input, - sizeof(input) - ); -} - -NTSTATUS KphSetInformationProcess( - _In_ HANDLE ProcessHandle, - _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, - _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, - _In_ ULONG ProcessInformationLength - ) -{ - struct - { - HANDLE ProcessHandle; - KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; - PVOID ProcessInformation; - ULONG ProcessInformationLength; - } input = { ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength }; - - return KphpDeviceIoControl( - KPH_SETINFORMATIONPROCESS, - &input, - sizeof(input) - ); -} - -NTSTATUS KphOpenThread( - _Out_ PHANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PCLIENT_ID ClientId - ) -{ - KPH_OPEN_THREAD_INPUT input = { ThreadHandle, DesiredAccess, ClientId, 0 }; - - if ((DesiredAccess & KPH_THREAD_READ_ACCESS) == DesiredAccess) - { - KphpGetL1Key(&input.Key); - return KphpDeviceIoControl( - KPH_OPENTHREAD, - &input, - sizeof(input) - ); - } - else - { - return KphpWithKey(KphKeyLevel2, KphpOpenThreadContinuation, &input); - } -} - -NTSTATUS KphOpenThreadProcess( - _In_ HANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE ProcessHandle - ) -{ - struct - { - HANDLE ThreadHandle; - ACCESS_MASK DesiredAccess; - PHANDLE ProcessHandle; - } input = { ThreadHandle, DesiredAccess, ProcessHandle }; - - return KphpDeviceIoControl( - KPH_OPENTHREADPROCESS, - &input, - sizeof(input) - ); -} - -NTSTATUS KphCaptureStackBackTraceThread( - _In_ HANDLE ThreadHandle, - _In_ ULONG FramesToSkip, - _In_ ULONG FramesToCapture, - _Out_writes_(FramesToCapture) PVOID *BackTrace, - _Out_opt_ PULONG CapturedFrames, - _Out_opt_ PULONG BackTraceHash - ) -{ - struct - { - HANDLE ThreadHandle; - ULONG FramesToSkip; - ULONG FramesToCapture; - PVOID *BackTrace; - PULONG CapturedFrames; - PULONG BackTraceHash; - } input = { ThreadHandle, FramesToSkip, FramesToCapture, BackTrace, CapturedFrames, BackTraceHash }; - - return KphpDeviceIoControl( - KPH_CAPTURESTACKBACKTRACETHREAD, - &input, - sizeof(input) - ); -} - -NTSTATUS KphQueryInformationThread( - _In_ HANDLE ThreadHandle, - _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, - _In_ ULONG ThreadInformationLength, - _Out_opt_ PULONG ReturnLength - ) -{ - struct - { - HANDLE ThreadHandle; - KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; - PVOID ThreadInformation; - ULONG ThreadInformationLength; - PULONG ReturnLength; - } input = { ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength, ReturnLength }; - - return KphpDeviceIoControl( - KPH_QUERYINFORMATIONTHREAD, - &input, - sizeof(input) - ); -} - -NTSTATUS KphSetInformationThread( - _In_ HANDLE ThreadHandle, - _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, - _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, - _In_ ULONG ThreadInformationLength - ) -{ - struct - { - HANDLE ThreadHandle; - KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; - PVOID ThreadInformation; - ULONG ThreadInformationLength; - } input = { ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength }; - - return KphpDeviceIoControl( - KPH_SETINFORMATIONTHREAD, - &input, - sizeof(input) - ); -} - -NTSTATUS KphEnumerateProcessHandles( - _In_ HANDLE ProcessHandle, - _Out_writes_bytes_(BufferLength) PVOID Buffer, - _In_opt_ ULONG BufferLength, - _Out_opt_ PULONG ReturnLength - ) -{ - struct - { - HANDLE ProcessHandle; - PVOID Buffer; - ULONG BufferLength; - PULONG ReturnLength; - } input = { ProcessHandle, Buffer, BufferLength, ReturnLength }; - - return KphpDeviceIoControl( - KPH_ENUMERATEPROCESSHANDLES, - &input, - sizeof(input) - ); -} - -NTSTATUS KphEnumerateProcessHandles2( - _In_ HANDLE ProcessHandle, - _Out_ PKPH_PROCESS_HANDLE_INFORMATION *Handles - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 2048; - - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - status = KphEnumerateProcessHandles( - ProcessHandle, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_BUFFER_TOO_SMALL) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *Handles = buffer; - - return status; -} - -NTSTATUS KphQueryInformationObject( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - _Out_writes_bytes_(ObjectInformationLength) PVOID ObjectInformation, - _In_ ULONG ObjectInformationLength, - _Out_opt_ PULONG ReturnLength - ) -{ - struct - { - HANDLE ProcessHandle; - HANDLE Handle; - KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; - PVOID ObjectInformation; - ULONG ObjectInformationLength; - PULONG ReturnLength; - } input = { ProcessHandle, Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength }; - - return KphpDeviceIoControl( - KPH_QUERYINFORMATIONOBJECT, - &input, - sizeof(input) - ); -} - -NTSTATUS KphSetInformationObject( - _In_ HANDLE ProcessHandle, - _In_ HANDLE Handle, - _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, - _In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation, - _In_ ULONG ObjectInformationLength - ) -{ - struct - { - HANDLE ProcessHandle; - HANDLE Handle; - KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; - PVOID ObjectInformation; - ULONG ObjectInformationLength; - } input = { ProcessHandle, Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength }; - - return KphpDeviceIoControl( - KPH_SETINFORMATIONOBJECT, - &input, - sizeof(input) - ); -} - -NTSTATUS KphOpenDriver( - _Out_ PHANDLE DriverHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ POBJECT_ATTRIBUTES ObjectAttributes - ) -{ - struct - { - PHANDLE DriverHandle; - ACCESS_MASK DesiredAccess; - POBJECT_ATTRIBUTES ObjectAttributes; - } input = { DriverHandle, DesiredAccess, ObjectAttributes }; - - return KphpDeviceIoControl( - KPH_OPENDRIVER, - &input, - sizeof(input) - ); -} - -NTSTATUS KphQueryInformationDriver( - _In_ HANDLE DriverHandle, - _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, - _Out_writes_bytes_(DriverInformationLength) PVOID DriverInformation, - _In_ ULONG DriverInformationLength, - _Out_opt_ PULONG ReturnLength - ) -{ - struct - { - HANDLE DriverHandle; - DRIVER_INFORMATION_CLASS DriverInformationClass; - PVOID DriverInformation; - ULONG DriverInformationLength; - PULONG ReturnLength; - } input = { DriverHandle, DriverInformationClass, DriverInformation, DriverInformationLength, ReturnLength }; - - return KphpDeviceIoControl( - KPH_QUERYINFORMATIONDRIVER, - &input, - sizeof(input) - ); -} - -NTSTATUS KphpDeviceIoControl( - _In_ ULONG KphControlCode, - _In_ PVOID InBuffer, - _In_ ULONG InBufferLength - ) -{ - IO_STATUS_BLOCK iosb; - - return NtDeviceIoControlFile( - PhKphHandle, - NULL, - NULL, - NULL, - &iosb, - KphControlCode, - InBuffer, - InBufferLength, - NULL, - 0 - ); -} - -VOID KphpWithKeyApcRoutine( - _In_ PVOID ApcContext, - _In_ PIO_STATUS_BLOCK IoStatusBlock, - _In_ ULONG Reserved - ) -{ - PKPHP_RETRIEVE_KEY_CONTEXT context = CONTAINING_RECORD(IoStatusBlock, KPHP_RETRIEVE_KEY_CONTEXT, Iosb); - KPH_KEY key = PtrToUlong(ApcContext); - - if (context->Continuation != KphpGetL1KeyContinuation && - context->Continuation != KphpOpenProcessContinuation && - context->Continuation != KphpOpenProcessTokenContinuation && - context->Continuation != KphpTerminateProcessContinuation && - context->Continuation != KphpReadVirtualMemoryUnsafeContinuation && - context->Continuation != KphpOpenThreadContinuation) - { - PhRaiseStatus(STATUS_ACCESS_DENIED); - context->Status = STATUS_ACCESS_DENIED; - return; - } - - context->Status = context->Continuation(key, context->Context); -} - -NTSTATUS KphpWithKey( - _In_ KPH_KEY_LEVEL KeyLevel, - _In_ PKPHP_WITH_KEY_CONTINUATION Continuation, - _In_ PVOID Context - ) -{ - NTSTATUS status; - struct - { - KPH_KEY_LEVEL KeyLevel; - } input = { KeyLevel }; - KPHP_RETRIEVE_KEY_CONTEXT context; - - context.Continuation = Continuation; - context.Context = Context; - context.Status = STATUS_UNSUCCESSFUL; - - status = NtDeviceIoControlFile( - PhKphHandle, - NULL, - KphpWithKeyApcRoutine, - NULL, - &context.Iosb, - KPH_RETRIEVEKEY, - &input, - sizeof(input), - NULL, - 0 - ); - - NtTestAlert(); - - if (!NT_SUCCESS(status)) - return status; - - return context.Status; -} - -NTSTATUS KphpGetL1KeyContinuation( - _In_ KPH_KEY Key, - _In_ PVOID Context - ) -{ - PKPHP_GET_L1_KEY_CONTEXT context = Context; - - *context->Key = Key; - PhKphL1Key = Key; - - return STATUS_SUCCESS; -} - -NTSTATUS KphpGetL1Key( - _Out_ PKPH_KEY Key - ) -{ - KPHP_GET_L1_KEY_CONTEXT context; - - if (PhKphL1Key) - { - *Key = PhKphL1Key; - return STATUS_SUCCESS; - } - - context.Key = Key; - - return KphpWithKey(KphKeyLevel1, KphpGetL1KeyContinuation, &context); -} - -NTSTATUS KphpOpenProcessContinuation( - _In_ KPH_KEY Key, - _In_ PVOID Context - ) -{ - PKPH_OPEN_PROCESS_INPUT input = Context; - - input->Key = Key; - - return KphpDeviceIoControl( - KPH_OPENPROCESS, - input, - sizeof(*input) - ); -} - -NTSTATUS KphpOpenProcessTokenContinuation( - _In_ KPH_KEY Key, - _In_ PVOID Context - ) -{ - PKPH_OPEN_PROCESS_TOKEN_INPUT input = Context; - - input->Key = Key; - - return KphpDeviceIoControl( - KPH_OPENPROCESSTOKEN, - input, - sizeof(*input) - ); -} - -NTSTATUS KphpTerminateProcessContinuation( - _In_ KPH_KEY Key, - _In_ PVOID Context - ) -{ - PKPH_TERMINATE_PROCESS_INPUT input = Context; - - input->Key = Key; - - return KphpDeviceIoControl( - KPH_TERMINATEPROCESS, - input, - sizeof(*input) - ); -} - -NTSTATUS KphpReadVirtualMemoryUnsafeContinuation( - _In_ KPH_KEY Key, - _In_ PVOID Context - ) -{ - PKPH_READ_VIRTUAL_MEMORY_UNSAFE_INPUT input = Context; - - input->Key = Key; - - return KphpDeviceIoControl( - KPH_READVIRTUALMEMORYUNSAFE, - input, - sizeof(*input) - ); -} - -NTSTATUS KphpOpenThreadContinuation( - _In_ KPH_KEY Key, - _In_ PVOID Context - ) -{ - PKPH_OPEN_PROCESS_INPUT input = Context; - - input->Key = Key; - - return KphpDeviceIoControl( - KPH_OPENTHREAD, - input, - sizeof(*input) - ); -} +/* + * Process Hacker - + * KProcessHacker API + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +HANDLE PhKphHandle = NULL; +BOOLEAN PhKphVerified = FALSE; +KPH_KEY PhKphL1Key = 0; + +NTSTATUS KphConnect( + _In_opt_ PWSTR DeviceName + ) +{ + NTSTATUS status; + HANDLE kphHandle; + UNICODE_STRING objectName; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK isb; + OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; + + if (PhKphHandle) + return STATUS_ADDRESS_ALREADY_EXISTS; + + if (DeviceName) + RtlInitUnicodeString(&objectName, DeviceName); + else + RtlInitUnicodeString(&objectName, KPH_DEVICE_NAME); + + InitializeObjectAttributes( + &objectAttributes, + &objectName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtOpenFile( + &kphHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &objectAttributes, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE + ); + + if (NT_SUCCESS(status)) + { + // Protect the handle from being closed. + + handleFlagInfo.Inherit = FALSE; + handleFlagInfo.ProtectFromClose = TRUE; + + NtSetInformationObject( + kphHandle, + ObjectHandleFlagInformation, + &handleFlagInfo, + sizeof(OBJECT_HANDLE_FLAG_INFORMATION) + ); + + PhKphHandle = kphHandle; + PhKphVerified = FALSE; + PhKphL1Key = 0; + } + + return status; +} + +NTSTATUS KphConnect2( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName + ) +{ + return KphConnect2Ex(DeviceName, FileName, NULL); +} + +NTSTATUS KphConnect2Ex( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName, + _In_opt_ PKPH_PARAMETERS Parameters + ) +{ + NTSTATUS status; + WCHAR fullDeviceName[256]; + PH_FORMAT format[2]; + SC_HANDLE scmHandle; + SC_HANDLE serviceHandle; + BOOLEAN started = FALSE; + BOOLEAN created = FALSE; + + if (!DeviceName) + DeviceName = KPH_DEVICE_SHORT_NAME; + + PhInitFormatS(&format[0], L"\\Device\\"); + PhInitFormatS(&format[1], DeviceName); + + if (!PhFormatToBuffer(format, 2, fullDeviceName, sizeof(fullDeviceName), NULL)) + return STATUS_NAME_TOO_LONG; + + // Try to open the device. + status = KphConnect(fullDeviceName); + + if (NT_SUCCESS(status) || status == STATUS_ADDRESS_ALREADY_EXISTS) + return status; + + if ( + status != STATUS_NO_SUCH_DEVICE && + status != STATUS_NO_SUCH_FILE && + status != STATUS_OBJECT_NAME_NOT_FOUND && + status != STATUS_OBJECT_PATH_NOT_FOUND + ) + return status; + + // Load the driver, and try again. + + // Try to start the service, if it exists. + + scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + + if (scmHandle) + { + serviceHandle = OpenService(scmHandle, DeviceName, SERVICE_START); + + if (serviceHandle) + { + if (StartService(serviceHandle, 0, NULL)) + started = TRUE; + + CloseServiceHandle(serviceHandle); + } + + CloseServiceHandle(scmHandle); + } + + if (!started && PhDoesFileExistsWin32(FileName)) + { + // Try to create the service. + + scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + + if (scmHandle) + { + serviceHandle = CreateService( + scmHandle, + DeviceName, + DeviceName, + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_DEMAND_START, + SERVICE_ERROR_IGNORE, + FileName, + NULL, + NULL, + NULL, + NULL, + L"" + ); + + if (serviceHandle) + { + created = TRUE; + + KphSetServiceSecurity(serviceHandle); + + // Set parameters if the caller supplied them. Note that we fail the entire function + // if this fails, because failing to set parameters like SecurityLevel may result in + // security vulnerabilities. + if (Parameters) + { + status = KphSetParameters(DeviceName, Parameters); + + if (!NT_SUCCESS(status)) + { + // Delete the service and fail. + goto CreateAndConnectEnd; + } + } + + if (StartService(serviceHandle, 0, NULL)) + started = TRUE; + else + status = PhGetLastWin32ErrorAsNtStatus(); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + CloseServiceHandle(scmHandle); + } + } + + if (started) + { + // Try to open the device again. + status = KphConnect(fullDeviceName); + } + +CreateAndConnectEnd: + if (created && serviceHandle) + { + // "Delete" the service. Since we (may) have a handle to the device, the SCM will delete the + // service automatically when it is stopped (upon reboot). If we don't have a handle to the + // device, the service will get deleted immediately, which is a good thing anyway. + DeleteService(serviceHandle); + CloseServiceHandle(serviceHandle); + } + + return status; +} + +NTSTATUS KphDisconnect( + VOID + ) +{ + NTSTATUS status; + OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo; + + if (!PhKphHandle) + return STATUS_ALREADY_DISCONNECTED; + + // Unprotect the handle. + + handleFlagInfo.Inherit = FALSE; + handleFlagInfo.ProtectFromClose = FALSE; + + NtSetInformationObject( + PhKphHandle, + ObjectHandleFlagInformation, + &handleFlagInfo, + sizeof(OBJECT_HANDLE_FLAG_INFORMATION) + ); + + status = NtClose(PhKphHandle); + PhKphHandle = NULL; + PhKphVerified = FALSE; + PhKphL1Key = 0; + + return status; +} + +BOOLEAN KphIsConnected( + VOID + ) +{ + return PhKphHandle != NULL; +} + +BOOLEAN KphIsVerified( + VOID + ) +{ + return PhKphVerified; +} + +NTSTATUS KphSetParameters( + _In_opt_ PWSTR DeviceName, + _In_ PKPH_PARAMETERS Parameters + ) +{ + NTSTATUS status; + HANDLE parametersKeyHandle = NULL; + PPH_STRING parametersKeyName; + ULONG disposition; + UNICODE_STRING valueName; + + if (!DeviceName) + DeviceName = KPH_DEVICE_SHORT_NAME; + + parametersKeyName = PhConcatStrings( + 3, + L"System\\CurrentControlSet\\Services\\", + DeviceName, + L"\\Parameters" + ); + status = PhCreateKey( + ¶metersKeyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + ¶metersKeyName->sr, + 0, + 0, + &disposition + ); + PhDereferenceObject(parametersKeyName); + + if (!NT_SUCCESS(status)) + return status; + + RtlInitUnicodeString(&valueName, L"SecurityLevel"); + status = NtSetValueKey(parametersKeyHandle, &valueName, 0, REG_DWORD, &Parameters->SecurityLevel, sizeof(ULONG)); + + if (!NT_SUCCESS(status)) + goto SetValuesEnd; + + if (Parameters->CreateDynamicConfiguration) + { + KPH_DYN_CONFIGURATION configuration; + + RtlInitUnicodeString(&valueName, L"DynamicConfiguration"); + + configuration.Version = KPH_DYN_CONFIGURATION_VERSION; + configuration.NumberOfPackages = 1; + + if (NT_SUCCESS(KphInitializeDynamicPackage(&configuration.Packages[0]))) + { + status = NtSetValueKey(parametersKeyHandle, &valueName, 0, REG_BINARY, &configuration, sizeof(KPH_DYN_CONFIGURATION)); + + if (!NT_SUCCESS(status)) + goto SetValuesEnd; + } + } + + // Put more parameters here... + +SetValuesEnd: + if (!NT_SUCCESS(status)) + { + // Delete the key if we created it. + if (disposition == REG_CREATED_NEW_KEY) + NtDeleteKey(parametersKeyHandle); + } + + NtClose(parametersKeyHandle); + + return status; +} + +VOID KphSetServiceSecurity( + _In_ SC_HANDLE ServiceHandle + ) +{ + static SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; + PSECURITY_DESCRIPTOR securityDescriptor; + ULONG sdAllocationLength; + UCHAR administratorsSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2]; + PSID administratorsSid; + PACL dacl; + + administratorsSid = (PSID)administratorsSidBuffer; + RtlInitializeSid(administratorsSid, &ntAuthority, 2); + *RtlSubAuthoritySid(administratorsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID; + *RtlSubAuthoritySid(administratorsSid, 1) = DOMAIN_ALIAS_RID_ADMINS; + + sdAllocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + + (ULONG)sizeof(ACL) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(&PhSeServiceSid) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(administratorsSid) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(&PhSeInteractiveSid); + + securityDescriptor = PhAllocate(sdAllocationLength); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); + + RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, SERVICE_ALL_ACCESS, &PhSeServiceSid); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, SERVICE_ALL_ACCESS, administratorsSid); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, + SERVICE_QUERY_CONFIG | + SERVICE_QUERY_STATUS | + SERVICE_START | + SERVICE_STOP | + SERVICE_INTERROGATE | + DELETE, + &PhSeInteractiveSid + ); + RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE); + + SetServiceObjectSecurity(ServiceHandle, DACL_SECURITY_INFORMATION, securityDescriptor); + + PhFree(securityDescriptor); +} + +NTSTATUS KphInstall( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName + ) +{ + return KphInstallEx(DeviceName, FileName, NULL); +} + +NTSTATUS KphInstallEx( + _In_opt_ PWSTR DeviceName, + _In_ PWSTR FileName, + _In_opt_ PKPH_PARAMETERS Parameters + ) +{ + NTSTATUS status = STATUS_SUCCESS; + SC_HANDLE scmHandle; + SC_HANDLE serviceHandle; + + if (!DeviceName) + DeviceName = KPH_DEVICE_SHORT_NAME; + + scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + + if (!scmHandle) + return PhGetLastWin32ErrorAsNtStatus(); + + serviceHandle = CreateService( + scmHandle, + DeviceName, + DeviceName, + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_SYSTEM_START, + SERVICE_ERROR_IGNORE, + FileName, + NULL, + NULL, + NULL, + NULL, + L"" + ); + + if (serviceHandle) + { + KphSetServiceSecurity(serviceHandle); + + // See KphConnect2Ex for more details. + if (Parameters) + { + status = KphSetParameters(DeviceName, Parameters); + + if (!NT_SUCCESS(status)) + { + DeleteService(serviceHandle); + goto CreateEnd; + } + } + + if (!StartService(serviceHandle, 0, NULL)) + status = PhGetLastWin32ErrorAsNtStatus(); + +CreateEnd: + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + CloseServiceHandle(scmHandle); + + return status; +} + +NTSTATUS KphUninstall( + _In_opt_ PWSTR DeviceName + ) +{ + NTSTATUS status = STATUS_SUCCESS; + SC_HANDLE scmHandle; + SC_HANDLE serviceHandle; + + if (!DeviceName) + DeviceName = KPH_DEVICE_SHORT_NAME; + + scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + + if (!scmHandle) + return PhGetLastWin32ErrorAsNtStatus(); + + serviceHandle = OpenService(scmHandle, DeviceName, SERVICE_STOP | DELETE); + + if (serviceHandle) + { + SERVICE_STATUS serviceStatus; + + ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + + if (!DeleteService(serviceHandle)) + status = PhGetLastWin32ErrorAsNtStatus(); + + CloseServiceHandle(serviceHandle); + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + + CloseServiceHandle(scmHandle); + + return status; +} + +NTSTATUS KphGetFeatures( + _Out_ PULONG Features + ) +{ + struct + { + PULONG Features; + } input = { Features }; + + return KphpDeviceIoControl( + KPH_GETFEATURES, + &input, + sizeof(input) + ); +} + +NTSTATUS KphVerifyClient( + _In_reads_bytes_(SignatureSize) PUCHAR Signature, + _In_ ULONG SignatureSize + ) +{ + NTSTATUS status; + struct + { + PVOID CodeAddress; + PUCHAR Signature; + ULONG SignatureSize; + } input = { KphpWithKeyApcRoutine, Signature, SignatureSize }; + + status = KphpDeviceIoControl( + KPH_VERIFYCLIENT, + &input, + sizeof(input) + ); + + if (NT_SUCCESS(status)) + PhKphVerified = TRUE; + + return status; +} + +NTSTATUS KphOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId + ) +{ + KPH_OPEN_PROCESS_INPUT input = { ProcessHandle, DesiredAccess, ClientId, 0 }; + + if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) == DesiredAccess) + { + KphpGetL1Key(&input.Key); + return KphpDeviceIoControl( + KPH_OPENPROCESS, + &input, + sizeof(input) + ); + } + else + { + return KphpWithKey(KphKeyLevel2, KphpOpenProcessContinuation, &input); + } +} + +NTSTATUS KphOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ) +{ + KPH_OPEN_PROCESS_TOKEN_INPUT input = { ProcessHandle, DesiredAccess, TokenHandle, 0 }; + + if ((DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) + { + KphpGetL1Key(&input.Key); + return KphpDeviceIoControl( + KPH_OPENPROCESSTOKEN, + &input, + sizeof(input) + ); + } + else + { + return KphpWithKey(KphKeyLevel2, KphpOpenProcessTokenContinuation, &input); + } +} + +NTSTATUS KphOpenProcessJob( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE JobHandle + ) +{ + struct + { + HANDLE ProcessHandle; + ACCESS_MASK DesiredAccess; + PHANDLE JobHandle; + } input = { ProcessHandle, DesiredAccess, JobHandle }; + + return KphpDeviceIoControl( + KPH_OPENPROCESSJOB, + &input, + sizeof(input) + ); +} + +NTSTATUS KphTerminateProcess( + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ) +{ + NTSTATUS status; + KPH_TERMINATE_PROCESS_INPUT input = { ProcessHandle, ExitStatus, 0 }; + + status = KphpWithKey(KphKeyLevel2, KphpTerminateProcessContinuation, &input); + + // Check if we're trying to terminate the current process, because kernel-mode can't do it. + if (status == STATUS_CANT_TERMINATE_SELF) + { + RtlExitUserProcess(ExitStatus); + } + + return status; +} + +NTSTATUS KphReadVirtualMemoryUnsafe( + _In_opt_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_writes_bytes_(BufferSize) PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead + ) +{ + KPH_READ_VIRTUAL_MEMORY_UNSAFE_INPUT input = { ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead, 0 }; + + return KphpWithKey(KphKeyLevel2, KphpReadVirtualMemoryUnsafeContinuation, &input); +} + +NTSTATUS KphQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; + PVOID ProcessInformation; + ULONG ProcessInformationLength; + PULONG ReturnLength; + } input = { ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength }; + + return KphpDeviceIoControl( + KPH_QUERYINFORMATIONPROCESS, + &input, + sizeof(input) + ); +} + +NTSTATUS KphSetInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass, + _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength + ) +{ + struct + { + HANDLE ProcessHandle; + KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass; + PVOID ProcessInformation; + ULONG ProcessInformationLength; + } input = { ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength }; + + return KphpDeviceIoControl( + KPH_SETINFORMATIONPROCESS, + &input, + sizeof(input) + ); +} + +NTSTATUS KphOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCLIENT_ID ClientId + ) +{ + KPH_OPEN_THREAD_INPUT input = { ThreadHandle, DesiredAccess, ClientId, 0 }; + + if ((DesiredAccess & KPH_THREAD_READ_ACCESS) == DesiredAccess) + { + KphpGetL1Key(&input.Key); + return KphpDeviceIoControl( + KPH_OPENTHREAD, + &input, + sizeof(input) + ); + } + else + { + return KphpWithKey(KphKeyLevel2, KphpOpenThreadContinuation, &input); + } +} + +NTSTATUS KphOpenThreadProcess( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE ProcessHandle + ) +{ + struct + { + HANDLE ThreadHandle; + ACCESS_MASK DesiredAccess; + PHANDLE ProcessHandle; + } input = { ThreadHandle, DesiredAccess, ProcessHandle }; + + return KphpDeviceIoControl( + KPH_OPENTHREADPROCESS, + &input, + sizeof(input) + ); +} + +NTSTATUS KphCaptureStackBackTraceThread( + _In_ HANDLE ThreadHandle, + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID *BackTrace, + _Out_opt_ PULONG CapturedFrames, + _Out_opt_ PULONG BackTraceHash + ) +{ + struct + { + HANDLE ThreadHandle; + ULONG FramesToSkip; + ULONG FramesToCapture; + PVOID *BackTrace; + PULONG CapturedFrames; + PULONG BackTraceHash; + } input = { ThreadHandle, FramesToSkip, FramesToCapture, BackTrace, CapturedFrames, BackTraceHash }; + + return KphpDeviceIoControl( + KPH_CAPTURESTACKBACKTRACETHREAD, + &input, + sizeof(input) + ); +} + +NTSTATUS KphQueryInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + struct + { + HANDLE ThreadHandle; + KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; + PVOID ThreadInformation; + ULONG ThreadInformationLength; + PULONG ReturnLength; + } input = { ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength, ReturnLength }; + + return KphpDeviceIoControl( + KPH_QUERYINFORMATIONTHREAD, + &input, + sizeof(input) + ); +} + +NTSTATUS KphSetInformationThread( + _In_ HANDLE ThreadHandle, + _In_ KPH_THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, + _In_ ULONG ThreadInformationLength + ) +{ + struct + { + HANDLE ThreadHandle; + KPH_THREAD_INFORMATION_CLASS ThreadInformationClass; + PVOID ThreadInformation; + ULONG ThreadInformationLength; + } input = { ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength }; + + return KphpDeviceIoControl( + KPH_SETINFORMATIONTHREAD, + &input, + sizeof(input) + ); +} + +NTSTATUS KphEnumerateProcessHandles( + _In_ HANDLE ProcessHandle, + _Out_writes_bytes_(BufferLength) PVOID Buffer, + _In_opt_ ULONG BufferLength, + _Out_opt_ PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + PVOID Buffer; + ULONG BufferLength; + PULONG ReturnLength; + } input = { ProcessHandle, Buffer, BufferLength, ReturnLength }; + + return KphpDeviceIoControl( + KPH_ENUMERATEPROCESSHANDLES, + &input, + sizeof(input) + ); +} + +NTSTATUS KphEnumerateProcessHandles2( + _In_ HANDLE ProcessHandle, + _Out_ PKPH_PROCESS_HANDLE_INFORMATION *Handles + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 2048; + + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + status = KphEnumerateProcessHandles( + ProcessHandle, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_BUFFER_TOO_SMALL) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *Handles = buffer; + + return status; +} + +NTSTATUS KphQueryInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_writes_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; + PVOID ObjectInformation; + ULONG ObjectInformationLength; + PULONG ReturnLength; + } input = { ProcessHandle, Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength }; + + return KphpDeviceIoControl( + KPH_QUERYINFORMATIONOBJECT, + &input, + sizeof(input) + ); +} + +NTSTATUS KphSetInformationObject( + _In_ HANDLE ProcessHandle, + _In_ HANDLE Handle, + _In_ KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass, + _In_reads_bytes_(ObjectInformationLength) PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength + ) +{ + struct + { + HANDLE ProcessHandle; + HANDLE Handle; + KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass; + PVOID ObjectInformation; + ULONG ObjectInformationLength; + } input = { ProcessHandle, Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength }; + + return KphpDeviceIoControl( + KPH_SETINFORMATIONOBJECT, + &input, + sizeof(input) + ); +} + +NTSTATUS KphOpenDriver( + _Out_ PHANDLE DriverHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ) +{ + struct + { + PHANDLE DriverHandle; + ACCESS_MASK DesiredAccess; + POBJECT_ATTRIBUTES ObjectAttributes; + } input = { DriverHandle, DesiredAccess, ObjectAttributes }; + + return KphpDeviceIoControl( + KPH_OPENDRIVER, + &input, + sizeof(input) + ); +} + +NTSTATUS KphQueryInformationDriver( + _In_ HANDLE DriverHandle, + _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, + _Out_writes_bytes_(DriverInformationLength) PVOID DriverInformation, + _In_ ULONG DriverInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + struct + { + HANDLE DriverHandle; + DRIVER_INFORMATION_CLASS DriverInformationClass; + PVOID DriverInformation; + ULONG DriverInformationLength; + PULONG ReturnLength; + } input = { DriverHandle, DriverInformationClass, DriverInformation, DriverInformationLength, ReturnLength }; + + return KphpDeviceIoControl( + KPH_QUERYINFORMATIONDRIVER, + &input, + sizeof(input) + ); +} + +NTSTATUS KphpDeviceIoControl( + _In_ ULONG KphControlCode, + _In_ PVOID InBuffer, + _In_ ULONG InBufferLength + ) +{ + IO_STATUS_BLOCK iosb; + + return NtDeviceIoControlFile( + PhKphHandle, + NULL, + NULL, + NULL, + &iosb, + KphControlCode, + InBuffer, + InBufferLength, + NULL, + 0 + ); +} + +VOID KphpWithKeyApcRoutine( + _In_ PVOID ApcContext, + _In_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG Reserved + ) +{ + PKPHP_RETRIEVE_KEY_CONTEXT context = CONTAINING_RECORD(IoStatusBlock, KPHP_RETRIEVE_KEY_CONTEXT, Iosb); + KPH_KEY key = PtrToUlong(ApcContext); + + if (context->Continuation != KphpGetL1KeyContinuation && + context->Continuation != KphpOpenProcessContinuation && + context->Continuation != KphpOpenProcessTokenContinuation && + context->Continuation != KphpTerminateProcessContinuation && + context->Continuation != KphpReadVirtualMemoryUnsafeContinuation && + context->Continuation != KphpOpenThreadContinuation) + { + PhRaiseStatus(STATUS_ACCESS_DENIED); + context->Status = STATUS_ACCESS_DENIED; + return; + } + + context->Status = context->Continuation(key, context->Context); +} + +NTSTATUS KphpWithKey( + _In_ KPH_KEY_LEVEL KeyLevel, + _In_ PKPHP_WITH_KEY_CONTINUATION Continuation, + _In_ PVOID Context + ) +{ + NTSTATUS status; + struct + { + KPH_KEY_LEVEL KeyLevel; + } input = { KeyLevel }; + KPHP_RETRIEVE_KEY_CONTEXT context; + + context.Continuation = Continuation; + context.Context = Context; + context.Status = STATUS_UNSUCCESSFUL; + + status = NtDeviceIoControlFile( + PhKphHandle, + NULL, + KphpWithKeyApcRoutine, + NULL, + &context.Iosb, + KPH_RETRIEVEKEY, + &input, + sizeof(input), + NULL, + 0 + ); + + NtTestAlert(); + + if (!NT_SUCCESS(status)) + return status; + + return context.Status; +} + +NTSTATUS KphpGetL1KeyContinuation( + _In_ KPH_KEY Key, + _In_ PVOID Context + ) +{ + PKPHP_GET_L1_KEY_CONTEXT context = Context; + + *context->Key = Key; + PhKphL1Key = Key; + + return STATUS_SUCCESS; +} + +NTSTATUS KphpGetL1Key( + _Out_ PKPH_KEY Key + ) +{ + KPHP_GET_L1_KEY_CONTEXT context; + + if (PhKphL1Key) + { + *Key = PhKphL1Key; + return STATUS_SUCCESS; + } + + context.Key = Key; + + return KphpWithKey(KphKeyLevel1, KphpGetL1KeyContinuation, &context); +} + +NTSTATUS KphpOpenProcessContinuation( + _In_ KPH_KEY Key, + _In_ PVOID Context + ) +{ + PKPH_OPEN_PROCESS_INPUT input = Context; + + input->Key = Key; + + return KphpDeviceIoControl( + KPH_OPENPROCESS, + input, + sizeof(*input) + ); +} + +NTSTATUS KphpOpenProcessTokenContinuation( + _In_ KPH_KEY Key, + _In_ PVOID Context + ) +{ + PKPH_OPEN_PROCESS_TOKEN_INPUT input = Context; + + input->Key = Key; + + return KphpDeviceIoControl( + KPH_OPENPROCESSTOKEN, + input, + sizeof(*input) + ); +} + +NTSTATUS KphpTerminateProcessContinuation( + _In_ KPH_KEY Key, + _In_ PVOID Context + ) +{ + PKPH_TERMINATE_PROCESS_INPUT input = Context; + + input->Key = Key; + + return KphpDeviceIoControl( + KPH_TERMINATEPROCESS, + input, + sizeof(*input) + ); +} + +NTSTATUS KphpReadVirtualMemoryUnsafeContinuation( + _In_ KPH_KEY Key, + _In_ PVOID Context + ) +{ + PKPH_READ_VIRTUAL_MEMORY_UNSAFE_INPUT input = Context; + + input->Key = Key; + + return KphpDeviceIoControl( + KPH_READVIRTUALMEMORYUNSAFE, + input, + sizeof(*input) + ); +} + +NTSTATUS KphpOpenThreadContinuation( + _In_ KPH_KEY Key, + _In_ PVOID Context + ) +{ + PKPH_OPEN_PROCESS_INPUT input = Context; + + input->Key = Key; + + return KphpDeviceIoControl( + KPH_OPENTHREAD, + input, + sizeof(*input) + ); +} diff --git a/phlib/kphdata.c b/phlib/kphdata.c index 179d0df5c90f..6b141ef8f62b 100644 --- a/phlib/kphdata.c +++ b/phlib/kphdata.c @@ -1,329 +1,326 @@ -/* - * Process Hacker - - * KProcessHacker dynamic data definitions - * - * Copyright (C) 2011-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#ifdef _WIN64 - -ULONG KphpGetKernelRevisionNumber( - VOID - ) -{ - ULONG result; - PPH_STRING kernelFileName; - PVOID versionInfo; - VS_FIXEDFILEINFO *rootBlock; - ULONG rootBlockLength; - - result = 0; - kernelFileName = PhGetKernelFileName(); - PhMoveReference(&kernelFileName, PhGetFileName(kernelFileName)); - versionInfo = PhGetFileVersionInfo(kernelFileName->Buffer); - PhDereferenceObject(kernelFileName); - - if (versionInfo && VerQueryValue(versionInfo, L"\\", &rootBlock, &rootBlockLength) && rootBlockLength != 0) - result = rootBlock->dwFileVersionLS & 0xffff; - - PhFree(versionInfo); - - return result; -} - -NTSTATUS KphInitializeDynamicPackage( - _Out_ PKPH_DYN_PACKAGE Package - ) -{ - ULONG majorVersion, minorVersion, servicePack, buildNumber; - - majorVersion = PhOsVersion.dwMajorVersion; - minorVersion = PhOsVersion.dwMinorVersion; - servicePack = PhOsVersion.wServicePackMajor; - buildNumber = PhOsVersion.dwBuildNumber; - - memset(&Package->StructData, -1, sizeof(KPH_DYN_STRUCT_DATA)); - - Package->MajorVersion = (USHORT)majorVersion; - Package->MinorVersion = (USHORT)minorVersion; - Package->ServicePackMajor = (USHORT)servicePack; - Package->BuildNumber = -1; - - // Windows 7, Windows Server 2008 R2 - if (majorVersion == 6 && minorVersion == 1) - { - ULONG revisionNumber = KphpGetKernelRevisionNumber(); - - Package->ResultingNtVersion = PHNT_WIN7; - - if (servicePack == 0) - { - } - else if (servicePack == 1) - { - } - else - { - return STATUS_NOT_SUPPORTED; - } - - Package->StructData.EgeGuid = 0x14; - Package->StructData.EpObjectTable = 0x200; - Package->StructData.EreGuidEntry = revisionNumber >= 19160 ? 0x20 : 0x10; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; // now only a UCHAR, not a ULONG - } - // Windows 8, Windows Server 2012 - else if (majorVersion == 6 && minorVersion == 2 && buildNumber == 9200) - { - Package->BuildNumber = 9200; - Package->ResultingNtVersion = PHNT_WIN8; - - Package->StructData.EgeGuid = 0x14; - Package->StructData.EpObjectTable = 0x408; - Package->StructData.EreGuidEntry = 0x10; - Package->StructData.HtHandleContentionEvent = 0x30; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; - Package->StructData.ObDecodeShift = 19; - Package->StructData.ObAttributesShift = 20; - } - // Windows 8.1, Windows Server 2012 R2 - else if (majorVersion == 6 && minorVersion == 3 && buildNumber == 9600) - { - ULONG revisionNumber = KphpGetKernelRevisionNumber(); - - Package->BuildNumber = 9600; - Package->ResultingNtVersion = PHNT_WINBLUE; - - Package->StructData.EgeGuid = 0x18; - Package->StructData.EpObjectTable = 0x408; - Package->StructData.EreGuidEntry = revisionNumber >= 17736 ? 0x20 : 0x10; - Package->StructData.HtHandleContentionEvent = 0x30; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; - Package->StructData.ObDecodeShift = 16; - Package->StructData.ObAttributesShift = 17; - } - // Windows 10 - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 10240) - { - Package->BuildNumber = 10240; - Package->ResultingNtVersion = PHNT_THRESHOLD; - - Package->StructData.EgeGuid = 0x18; - Package->StructData.EpObjectTable = 0x418; - Package->StructData.EreGuidEntry = 0x20; - Package->StructData.HtHandleContentionEvent = 0x30; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; - Package->StructData.ObDecodeShift = 16; - Package->StructData.ObAttributesShift = 17; - } - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 10586) - { - Package->BuildNumber = 10586; - Package->ResultingNtVersion = PHNT_THRESHOLD2; - - Package->StructData.EgeGuid = 0x18; - Package->StructData.EpObjectTable = 0x418; - Package->StructData.EreGuidEntry = 0x20; - Package->StructData.HtHandleContentionEvent = 0x30; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; - Package->StructData.ObDecodeShift = 16; - Package->StructData.ObAttributesShift = 17; - } - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 14393) - { - Package->BuildNumber = 14393; - Package->ResultingNtVersion = PHNT_REDSTONE; - - Package->StructData.EgeGuid = 0x18; - Package->StructData.EpObjectTable = 0x418; - Package->StructData.EreGuidEntry = 0x20; - Package->StructData.HtHandleContentionEvent = 0x30; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; - Package->StructData.ObDecodeShift = 16; - Package->StructData.ObAttributesShift = 17; - } - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 15063) - { - Package->BuildNumber = 15063; - Package->ResultingNtVersion = PHNT_REDSTONE2; - - Package->StructData.EgeGuid = 0x18; - Package->StructData.EpObjectTable = 0x418; - Package->StructData.EreGuidEntry = 0x20; - Package->StructData.HtHandleContentionEvent = 0x30; - Package->StructData.OtName = 0x10; - Package->StructData.OtIndex = 0x28; - Package->StructData.ObDecodeShift = 16; - Package->StructData.ObAttributesShift = 17; - } - else - { - return STATUS_NOT_SUPPORTED; - } - - return STATUS_SUCCESS; -} - -#else - -NTSTATUS KphInitializeDynamicPackage( - _Out_ PKPH_DYN_PACKAGE Package - ) -{ - ULONG majorVersion, minorVersion, servicePack, buildNumber; - - majorVersion = PhOsVersion.dwMajorVersion; - minorVersion = PhOsVersion.dwMinorVersion; - servicePack = PhOsVersion.wServicePackMajor; - buildNumber = PhOsVersion.dwBuildNumber; - - memset(&Package->StructData, -1, sizeof(KPH_DYN_STRUCT_DATA)); - - Package->MajorVersion = (USHORT)majorVersion; - Package->MinorVersion = (USHORT)minorVersion; - Package->ServicePackMajor = (USHORT)servicePack; - Package->BuildNumber = -1; - - // Windows 7, Windows Server 2008 R2 - if (majorVersion == 6 && minorVersion == 1) - { - Package->ResultingNtVersion = PHNT_WIN7; - - if (servicePack == 0) - { - NOTHING; - } - else if (servicePack == 1) - { - NOTHING; - } - else - { - return STATUS_NOT_SUPPORTED; - } - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0xf4; - Package->StructData.EreGuidEntry = 0x8; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; // now only a UCHAR, not a ULONG - } - // Windows 8, Windows Server 2012 - else if (majorVersion == 6 && minorVersion == 2) - { - Package->ResultingNtVersion = PHNT_WIN8; - - if (servicePack == 0) - { - NOTHING; - } - else - { - return STATUS_NOT_SUPPORTED; - } - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0x150; - Package->StructData.EreGuidEntry = 0x8; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; - } - // Windows 8.1, Windows Server 2012 R2 - else if (majorVersion == 6 && minorVersion == 3) - { - Package->ResultingNtVersion = PHNT_WINBLUE; - - if (servicePack == 0) - { - NOTHING; - } - else - { - return STATUS_NOT_SUPPORTED; - } - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0x150; - Package->StructData.EreGuidEntry = 0x8; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; - } - // Windows 10 - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 10240) - { - Package->BuildNumber = 10240; - Package->ResultingNtVersion = PHNT_THRESHOLD; - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0x154; - Package->StructData.EreGuidEntry = 0x10; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; - } - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 10586) - { - Package->BuildNumber = 10586; - Package->ResultingNtVersion = PHNT_THRESHOLD2; - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0x154; - Package->StructData.EreGuidEntry = 0x10; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; - } - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 14393) - { - Package->BuildNumber = 14393; - Package->ResultingNtVersion = PHNT_REDSTONE; - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0x154; - Package->StructData.EreGuidEntry = 0x10; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; - } - else if (majorVersion == 10 && minorVersion == 0 && buildNumber == 15063) - { - Package->BuildNumber = 15063; - Package->ResultingNtVersion = PHNT_REDSTONE2; - - Package->StructData.EgeGuid = 0xc; - Package->StructData.EpObjectTable = 0x154; - Package->StructData.EreGuidEntry = 0x10; - Package->StructData.OtName = 0x8; - Package->StructData.OtIndex = 0x14; - } - else - { - return STATUS_NOT_SUPPORTED; - } - - return STATUS_SUCCESS; -} - -#endif +/* + * Process Hacker - + * KProcessHacker dynamic data definitions + * + * Copyright (C) 2011-2016 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#ifdef _WIN64 + +ULONG KphpGetKernelRevisionNumber( + VOID + ) +{ + ULONG result; + PPH_STRING kernelFileName; + PVOID versionInfo; + VS_FIXEDFILEINFO *rootBlock; + ULONG rootBlockLength; + + result = 0; + kernelFileName = PhGetKernelFileName(); + PhMoveReference(&kernelFileName, PhGetFileName(kernelFileName)); + versionInfo = PhGetFileVersionInfo(kernelFileName->Buffer); + PhDereferenceObject(kernelFileName); + + if (versionInfo && VerQueryValue(versionInfo, L"\\", &rootBlock, &rootBlockLength) && rootBlockLength != 0) + result = LOWORD(rootBlock->dwFileVersionLS); + + PhFree(versionInfo); + + return result; +} + +NTSTATUS KphInitializeDynamicPackage( + _Out_ PKPH_DYN_PACKAGE Package + ) +{ + ULONG majorVersion, minorVersion, servicePack, buildNumber; + + majorVersion = PhOsVersion.dwMajorVersion; + minorVersion = PhOsVersion.dwMinorVersion; + servicePack = PhOsVersion.wServicePackMajor; + buildNumber = PhOsVersion.dwBuildNumber; + + memset(&Package->StructData, -1, sizeof(KPH_DYN_STRUCT_DATA)); + + Package->MajorVersion = (USHORT)majorVersion; + Package->MinorVersion = (USHORT)minorVersion; + Package->ServicePackMajor = (USHORT)servicePack; + Package->BuildNumber = USHRT_MAX; + + // Windows 7, Windows Server 2008 R2 + if (majorVersion == 6 && minorVersion == 1) + { + ULONG revisionNumber = KphpGetKernelRevisionNumber(); + + Package->ResultingNtVersion = PHNT_WIN7; + + if (servicePack == 0) + { + NOTHING; + } + else if (servicePack == 1) + { + NOTHING; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + Package->StructData.EgeGuid = 0x14; + Package->StructData.EpObjectTable = 0x200; + Package->StructData.EreGuidEntry = revisionNumber >= 19160 ? 0x20 : 0x10; + Package->StructData.OtName = 0x10; + Package->StructData.OtIndex = 0x28; // now only a UCHAR, not a ULONG + } + // Windows 8, Windows Server 2012 + else if (majorVersion == 6 && minorVersion == 2 && buildNumber == 9200) + { + Package->BuildNumber = 9200; + Package->ResultingNtVersion = PHNT_WIN8; + + Package->StructData.EgeGuid = 0x14; + Package->StructData.EpObjectTable = 0x408; + Package->StructData.EreGuidEntry = 0x10; + Package->StructData.HtHandleContentionEvent = 0x30; + Package->StructData.OtName = 0x10; + Package->StructData.OtIndex = 0x28; + Package->StructData.ObDecodeShift = 19; + Package->StructData.ObAttributesShift = 20; + } + // Windows 8.1, Windows Server 2012 R2 + else if (majorVersion == 6 && minorVersion == 3 && buildNumber == 9600) + { + ULONG revisionNumber = KphpGetKernelRevisionNumber(); + + Package->BuildNumber = 9600; + Package->ResultingNtVersion = PHNT_WINBLUE; + + Package->StructData.EgeGuid = 0x18; + Package->StructData.EpObjectTable = 0x408; + Package->StructData.EreGuidEntry = revisionNumber >= 17736 ? 0x20 : 0x10; + Package->StructData.HtHandleContentionEvent = 0x30; + Package->StructData.OtName = 0x10; + Package->StructData.OtIndex = 0x28; + Package->StructData.ObDecodeShift = 16; + Package->StructData.ObAttributesShift = 17; + } + // Windows 10, Windows Server 2016 + else if (majorVersion == 10 && minorVersion == 0) + { + switch (buildNumber) + { + case 10240: + Package->BuildNumber = 10240; + Package->ResultingNtVersion = PHNT_THRESHOLD; + break; + case 10586: + Package->BuildNumber = 10586; + Package->ResultingNtVersion = PHNT_THRESHOLD2; + break; + case 14393: + Package->BuildNumber = 14393; + Package->ResultingNtVersion = PHNT_REDSTONE; + break; + case 15063: + Package->BuildNumber = 15063; + Package->ResultingNtVersion = PHNT_REDSTONE2; + break; + case 16299: + Package->BuildNumber = 16299; + Package->ResultingNtVersion = PHNT_REDSTONE3; + break; + case 17134: + Package->BuildNumber = 17134; + Package->ResultingNtVersion = PHNT_REDSTONE4; + break; + case 17763: + Package->BuildNumber = 17763; + Package->ResultingNtVersion = PHNT_REDSTONE5; + break; + case 18362: + Package->BuildNumber = 18362; + Package->ResultingNtVersion = PHNT_REDSTONE6; + break; + default: + return STATUS_NOT_SUPPORTED; + } + + Package->StructData.EgeGuid = 0x18; + Package->StructData.EpObjectTable = 0x418; + Package->StructData.EreGuidEntry = 0x20; + Package->StructData.HtHandleContentionEvent = 0x30; + Package->StructData.OtName = 0x10; + Package->StructData.OtIndex = 0x28; + Package->StructData.ObDecodeShift = 16; + Package->StructData.ObAttributesShift = 17; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + return STATUS_SUCCESS; +} + +#else + +NTSTATUS KphInitializeDynamicPackage( + _Out_ PKPH_DYN_PACKAGE Package + ) +{ + ULONG majorVersion, minorVersion, servicePack, buildNumber; + + majorVersion = PhOsVersion.dwMajorVersion; + minorVersion = PhOsVersion.dwMinorVersion; + servicePack = PhOsVersion.wServicePackMajor; + buildNumber = PhOsVersion.dwBuildNumber; + + memset(&Package->StructData, -1, sizeof(KPH_DYN_STRUCT_DATA)); + + Package->MajorVersion = (USHORT)majorVersion; + Package->MinorVersion = (USHORT)minorVersion; + Package->ServicePackMajor = (USHORT)servicePack; + Package->BuildNumber = -1; + + // Windows 7, Windows Server 2008 R2 + if (majorVersion == 6 && minorVersion == 1) + { + Package->ResultingNtVersion = PHNT_WIN7; + + if (servicePack == 0) + { + NOTHING; + } + else if (servicePack == 1) + { + NOTHING; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + Package->StructData.EgeGuid = 0xc; + Package->StructData.EpObjectTable = 0xf4; + Package->StructData.EreGuidEntry = 0x8; + Package->StructData.OtName = 0x8; + Package->StructData.OtIndex = 0x14; // now only a UCHAR, not a ULONG + } + // Windows 8, Windows Server 2012 + else if (majorVersion == 6 && minorVersion == 2) + { + Package->ResultingNtVersion = PHNT_WIN8; + + if (servicePack == 0) + { + NOTHING; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + Package->StructData.EgeGuid = 0xc; + Package->StructData.EpObjectTable = 0x150; + Package->StructData.EreGuidEntry = 0x8; + Package->StructData.OtName = 0x8; + Package->StructData.OtIndex = 0x14; + } + // Windows 8.1, Windows Server 2012 R2 + else if (majorVersion == 6 && minorVersion == 3) + { + Package->ResultingNtVersion = PHNT_WINBLUE; + + if (servicePack == 0) + { + NOTHING; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + Package->StructData.EgeGuid = 0xc; + Package->StructData.EpObjectTable = 0x150; + Package->StructData.EreGuidEntry = 0x8; + Package->StructData.OtName = 0x8; + Package->StructData.OtIndex = 0x14; + } + // Windows 10 + else if (majorVersion == 10 && minorVersion == 0) + { + switch (buildNumber) + { + case 10240: + Package->BuildNumber = 10240; + Package->ResultingNtVersion = PHNT_THRESHOLD; + break; + case 10586: + Package->BuildNumber = 10586; + Package->ResultingNtVersion = PHNT_THRESHOLD2; + break; + case 14393: + Package->BuildNumber = 14393; + Package->ResultingNtVersion = PHNT_REDSTONE; + break; + case 15063: + Package->BuildNumber = 15063; + Package->ResultingNtVersion = PHNT_REDSTONE2; + break; + case 16299: + Package->BuildNumber = 16299; + Package->ResultingNtVersion = PHNT_REDSTONE3; + break; + case 17134: + Package->BuildNumber = 17134; + Package->ResultingNtVersion = PHNT_REDSTONE4; + break; + case 17763: + Package->BuildNumber = 17763; + Package->ResultingNtVersion = PHNT_REDSTONE5; + break; + case 18362: + Package->BuildNumber = 18362; + Package->ResultingNtVersion = PHNT_REDSTONE6; + break; + default: + return STATUS_NOT_SUPPORTED; + } + + Package->StructData.EgeGuid = 0xc; + Package->StructData.EpObjectTable = 0x154; + Package->StructData.EreGuidEntry = 0x10; + Package->StructData.OtName = 0x8; + Package->StructData.OtIndex = 0x14; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + return STATUS_SUCCESS; +} + +#endif diff --git a/phlib/lsasup.c b/phlib/lsasup.c index c81051484571..ea662665b325 100644 --- a/phlib/lsasup.c +++ b/phlib/lsasup.c @@ -3,6 +3,7 @@ * LSA support functions * * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2019 dmex * * This file is part of Process Hacker. * @@ -28,21 +29,22 @@ */ #include +#include #include -static LSA_HANDLE PhLookupPolicyHandle = NULL; - NTSTATUS PhOpenLsaPolicy( _Out_ PLSA_HANDLE PolicyHandle, _In_ ACCESS_MASK DesiredAccess, _In_opt_ PUNICODE_STRING SystemName ) { - OBJECT_ATTRIBUTES oa = { 0 }; + OBJECT_ATTRIBUTES objectAttributes; + + InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); return LsaOpenPolicy( SystemName, - &oa, + &objectAttributes, DesiredAccess, PolicyHandle ); @@ -57,10 +59,13 @@ LSA_HANDLE PhGetLookupPolicyHandle( VOID ) { + static LSA_HANDLE cachedLookupPolicyHandle = NULL; LSA_HANDLE lookupPolicyHandle; LSA_HANDLE newLookupPolicyHandle; - lookupPolicyHandle = PhLookupPolicyHandle; + // Use the cached value if possible. + + lookupPolicyHandle = InterlockedCompareExchangePointer(&cachedLookupPolicyHandle, NULL, NULL); // If there is no cached handle, open one. @@ -75,8 +80,8 @@ LSA_HANDLE PhGetLookupPolicyHandle( // We succeeded in opening a policy handle, and since we did not have a cached handle // before, we will now store it. - lookupPolicyHandle = _InterlockedCompareExchangePointer( - &PhLookupPolicyHandle, + lookupPolicyHandle = InterlockedCompareExchangePointer( + &cachedLookupPolicyHandle, newLookupPolicyHandle, NULL ); @@ -265,6 +270,127 @@ NTSTATUS PhLookupSid( return status; } +/** + * Converts an array of SIDs to a human-readable form. + * + * \param Count The size of the array. + * \param Sids An array of SIDs to query. + * \param FullNames A variable which receives a pointer to an array of strings in the following format: + * domain\\user. If not applicable to a particular SID, the function returns its SDDL representation. + * You must free each item using PhDereferenceObject(), and then free the array by calling PhFree(). + */ +VOID PhLookupSids( + _In_ ULONG Count, + _In_ PSID *Sids, + _Out_ PPH_STRING **FullNames + ) +{ + NTSTATUS status; + PLSA_REFERENCED_DOMAIN_LIST referencedDomains = NULL; + PLSA_TRANSLATED_NAME names = NULL; + PPH_STRING *translatedNames; + + translatedNames = PhAllocateZero(sizeof(PPH_STRING) * Count); + + status = LsaLookupSids( + PhGetLookupPolicyHandle(), + Count, + Sids, + &referencedDomains, + &names + ); + + if (status == STATUS_NONE_MAPPED) + { + // Even without mapping names it converts most of them to SDDL representation + status = STATUS_SOME_NOT_MAPPED; + } + + if (NT_SUCCESS(status)) + { + PPH_STRING userName; + PPH_STRING domainName; + + for (ULONG i = 0; i < Count; i++) + { + userName = NULL; + domainName = NULL; + + // Reference user if present + if (names[i].Name.Length > 0) + { + userName = PhCreateStringFromUnicodeString(&names[i].Name); + } + + // Reference domain if present + if (names[i].DomainIndex >= 0) + { + PLSA_TRUST_INFORMATION trustInfo; + + trustInfo = &referencedDomains->Domains[names[i].DomainIndex]; + + if (trustInfo->Name.Length > 0) + { + domainName = PhCreateStringFromUnicodeString(&trustInfo->Name); + } + } + + // Construct the name + if (names[i].Use != SidTypeInvalid && names[i].Use != SidTypeUnknown) + { + if (domainName && userName) + { + translatedNames[i] = PhConcatStrings( + 3, + domainName->Buffer, + L"\\", + userName->Buffer + ); + } + else if (domainName) + { + translatedNames[i] = PhReferenceObject(domainName); + } + else if (userName) + { + translatedNames[i] = PhReferenceObject(userName); + } + } + else + { + if (PhStartsWithString2(userName, L"S-1-", TRUE)) + { + translatedNames[i] = PhReferenceObject(userName); + } + } + + if (userName) + { + PhDereferenceObject(userName); + } + + if (domainName) + { + PhDereferenceObject(domainName); + } + } + + LsaFreeMemory(referencedDomains); + LsaFreeMemory(names); + } + + for (ULONG i = 0; i < Count; i++) + { + // Make sure everything is converted at least to SDDL + if (!translatedNames[i]) + { + translatedNames[i] = PhSidToStringSid(Sids[i]); + } + } + + *FullNames = translatedNames; +} + /** * Gets information about a name. * @@ -308,14 +434,7 @@ NTSTATUS PhLookupName( { if (Sid) { - PSID sid; - ULONG sidLength; - - sidLength = RtlLengthSid(sids[0].Sid); - sid = PhAllocate(sidLength); - memcpy(sid, sids[0].Sid, sidLength); - - *Sid = sid; + *Sid = PhAllocateCopy(sids[0].Sid, RtlLengthSid(sids[0].Sid)); } if (DomainName) @@ -410,9 +529,9 @@ PPH_STRING PhGetSidFullName( if (domainNameBuffer && domainNameLength != 0) { - fullName = PhCreateStringEx(NULL, domainNameLength + sizeof(WCHAR) + names[0].Name.Length); + fullName = PhCreateStringEx(NULL, domainNameLength + sizeof(UNICODE_NULL) + names[0].Name.Length); memcpy(&fullName->Buffer[0], domainNameBuffer, domainNameLength); - fullName->Buffer[domainNameLength / sizeof(WCHAR)] = '\\'; + fullName->Buffer[domainNameLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; memcpy(&fullName->Buffer[domainNameLength / sizeof(WCHAR) + 1], names[0].Name.Buffer, names[0].Name.Length); } else @@ -459,7 +578,7 @@ PPH_STRING PhSidToStringSid( PPH_STRING string; UNICODE_STRING us; - string = PhCreateStringEx(NULL, MAX_UNICODE_STACK_BUFFER_LENGTH * sizeof(WCHAR)); + string = PhCreateStringEx(NULL, SECURITY_MAX_SID_STRING_CHARACTERS * sizeof(WCHAR)); PhStringRefToUnicodeString(&string->sr, &us); if (NT_SUCCESS(RtlConvertSidToUnicodeString( @@ -469,7 +588,7 @@ PPH_STRING PhSidToStringSid( ))) { string->Length = us.Length; - string->Buffer[us.Length / sizeof(WCHAR)] = 0; + string->Buffer[us.Length / sizeof(WCHAR)] = UNICODE_NULL; return string; } @@ -478,3 +597,345 @@ PPH_STRING PhSidToStringSid( return NULL; } } + +PPH_STRING PhGetTokenUserString( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN IncludeDomain + ) +{ + PPH_STRING tokenUserString = NULL; + PTOKEN_USER tokenUser; + + if (NT_SUCCESS(PhGetTokenUser(TokenHandle, &tokenUser))) + { + tokenUserString = PhGetSidFullName(tokenUser->User.Sid, IncludeDomain, NULL); + PhFree(tokenUser); + } + + return tokenUserString; +} + +NTSTATUS PhGetAccountPrivileges( + _In_ PSID AccountSid, + _Out_ PTOKEN_PRIVILEGES *Privileges + ) +{ + NTSTATUS status; + LSA_HANDLE accountHandle; + PPRIVILEGE_SET accountPrivileges; + PTOKEN_PRIVILEGES privileges; + + status = LsaOpenAccount(PhGetLookupPolicyHandle(), AccountSid, ACCOUNT_VIEW, &accountHandle); + + if (!NT_SUCCESS(status)) + return status; + + status = LsaEnumeratePrivilegesOfAccount(accountHandle, &accountPrivileges); + LsaClose(accountHandle); + + if (!NT_SUCCESS(status)) + return status; + + privileges = PhAllocate(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES) * accountPrivileges->PrivilegeCount); + privileges->PrivilegeCount = accountPrivileges->PrivilegeCount; + memcpy(privileges->Privileges, accountPrivileges->Privilege, sizeof(LUID_AND_ATTRIBUTES) * accountPrivileges->PrivilegeCount); + + LsaFreeMemory(accountPrivileges); + + *Privileges = privileges; + + return status; +} + +typedef struct _PH_CAPABILITY_ENTRY +{ + PPH_STRING Name; + PSID CapabilityGroupSid; + PSID CapabilitySid; +} PH_CAPABILITY_ENTRY, *PPH_CAPABILITY_ENTRY; + +VOID PhInitializeCapabilitySidCache( + _Inout_ PPH_ARRAY CapabilitySidArrayList + ) +{ + PPH_STRING applicationDirectory; + PPH_STRING capabilityListString = NULL; + PH_STRINGREF namePart; + PH_STRINGREF remainingPart; + + if (!RtlDeriveCapabilitySidsFromName_Import()) + return; + + if (applicationDirectory = PhGetApplicationDirectory()) + { + PPH_STRING capabilityListFileName; + + capabilityListFileName = PhConcatStringRefZ(&applicationDirectory->sr, L"capslist.txt"); + PhDereferenceObject(applicationDirectory); + + capabilityListString = PhFileReadAllText(capabilityListFileName->Buffer); + PhDereferenceObject(capabilityListFileName); + } + + if (!capabilityListString) + return; + + PhInitializeArray(CapabilitySidArrayList, sizeof(PH_CAPABILITY_ENTRY), 800); + remainingPart = PhGetStringRef(capabilityListString); + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, '\n', &namePart, &remainingPart); + + if (namePart.Length != 0) + { + BYTE capabilityGroupSidBuffer[SECURITY_MAX_SID_SIZE]; + BYTE capabilitySidBuffer[SECURITY_MAX_SID_SIZE]; + PSID capabilityGroupSid = (PSID)capabilityGroupSidBuffer; + PSID capabilitySid = (PSID)capabilitySidBuffer; + UNICODE_STRING capabilityNameUs; + + if (PhEndsWithStringRef2(&namePart, L"\r", FALSE)) + namePart.Length -= sizeof(WCHAR); + + if (!PhStringRefToUnicodeString(&namePart, &capabilityNameUs)) + continue; + + if (NT_SUCCESS(RtlDeriveCapabilitySidsFromName_Import()( + &capabilityNameUs, + capabilityGroupSid, + capabilitySid + ))) + { + PH_CAPABILITY_ENTRY entry; + + entry.Name = PhCreateStringFromUnicodeString(&capabilityNameUs); + entry.CapabilityGroupSid = PhAllocateCopy(capabilityGroupSid, RtlLengthSid(capabilityGroupSid)); + entry.CapabilitySid = PhAllocateCopy(capabilitySid, RtlLengthSid(capabilitySid)); + + PhAddItemArray(CapabilitySidArrayList, &entry); + } + } + } + + PhDereferenceObject(capabilityListString); +} + +PPH_STRING PhGetCapabilitySidName( + _In_ PSID CapabilitySid + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PH_ARRAY capabilitySidArrayList; + PPH_CAPABILITY_ENTRY entry; + SIZE_T i; + + if (WindowsVersion < WINDOWS_8) + return NULL; + + if (PhBeginInitOnce(&initOnce)) + { + PhInitializeCapabilitySidCache(&capabilitySidArrayList); + PhEndInitOnce(&initOnce); + } + + for (i = 0; i < capabilitySidArrayList.Count; i++) + { + entry = PhItemArray(&capabilitySidArrayList, i); + + if (RtlEqualSid(entry->CapabilitySid, CapabilitySid)) + { + return PhReferenceObject(entry->Name); + } + + if (RtlEqualSid(entry->CapabilityGroupSid, CapabilitySid)) + { + return PhReferenceObject(entry->Name); + } + } + + return NULL; +} + +typedef struct _PH_CAPABILITY_GUID_ENTRY +{ + PPH_STRING Name; + PPH_STRING CapabilityGuid; +} PH_CAPABILITY_GUID_ENTRY, *PPH_CAPABILITY_GUID_ENTRY; + +typedef struct _PH_CAPABILITY_KEY_CALLBACK +{ + PPH_STRING KeyName; + PVOID Context; +} PH_CAPABILITY_KEY_CALLBACK, *PPH_CAPABILITY_KEY_CALLBACK; + +BOOLEAN NTAPI PhpAccessManagerEnumerateKeyCallback( + _In_ HANDLE RootDirectory, + _In_ PKEY_BASIC_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + HANDLE keyHandle; + PPH_STRING guidString; + PH_STRINGREF keyName; + + keyName.Buffer = Information->Name; + keyName.Length = Information->NameLength; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + RootDirectory, + &keyName, + 0 + ))) + { + if (guidString = PhQueryRegistryString(keyHandle, L"LegacyInterfaceClassGuid")) + { + PH_CAPABILITY_GUID_ENTRY entry; + + PhSetReference(&entry.Name, PhCreateString2(&keyName)); + PhSetReference(&entry.CapabilityGuid, guidString); + PhAddItemArray(Context, &entry); + + PhDereferenceObject(guidString); + } + + NtClose(keyHandle); + } + + return TRUE; +} + +BOOLEAN NTAPI PhpDeviceAccessSubKeyEnumerateKeyCallback( + _In_ HANDLE RootDirectory, + _In_ PKEY_BASIC_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + PPH_CAPABILITY_KEY_CALLBACK context = Context; + HANDLE keyHandle; + PH_STRINGREF keyName; + + keyName.Buffer = Information->Name; + keyName.Length = Information->NameLength; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + RootDirectory, + &keyName, + 0 + ))) + { + PH_CAPABILITY_GUID_ENTRY entry; + + PhSetReference(&entry.Name, context->KeyName); + PhSetReference(&entry.CapabilityGuid, PhCreateString2(&keyName)); + PhAddItemArray(context->Context, &entry); + + NtClose(keyHandle); + } + + return TRUE; +} + +BOOLEAN NTAPI PhpDeviceAccessEnumerateKeyCallback( + _In_ HANDLE RootDirectory, + _In_ PKEY_BASIC_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + HANDLE keyHandle; + PH_STRINGREF keyName; + + keyName.Buffer = Information->Name; + keyName.Length = Information->NameLength; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + RootDirectory, + &keyName, + 0 + ))) + { + PH_CAPABILITY_KEY_CALLBACK entry; + + entry.KeyName = PhCreateString2(&keyName); + entry.Context = Context; + + PhEnumerateKey(keyHandle, PhpDeviceAccessSubKeyEnumerateKeyCallback, &entry); + + PhDereferenceObject(entry.KeyName); + NtClose(keyHandle); + } + + return TRUE; +} + +VOID PhInitializeCapabilityGuidCache( + _Inout_ PPH_ARRAY CapabilityGuidArrayList + ) +{ + static PH_STRINGREF accessManagerKeyPath = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\Capabilities"); + static PH_STRINGREF deviceAccessKeyPath = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\DeviceAccess\\CapabilityMappings"); + HANDLE keyHandle; + + PhInitializeArray(CapabilityGuidArrayList, sizeof(PH_CAPABILITY_GUID_ENTRY), 100); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &accessManagerKeyPath, + 0 + ))) + { + PhEnumerateKey(keyHandle, PhpAccessManagerEnumerateKeyCallback, CapabilityGuidArrayList); + NtClose(keyHandle); + } + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &deviceAccessKeyPath, + 0 + ))) + { + PhEnumerateKey(keyHandle, PhpDeviceAccessEnumerateKeyCallback, CapabilityGuidArrayList); + NtClose(keyHandle); + } +} + +PPH_STRING PhGetCapabilityGuidName( + _In_ PPH_STRING GuidString + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PH_ARRAY capabilityGuidArrayList; + PPH_CAPABILITY_GUID_ENTRY entry; + SIZE_T i; + + if (WindowsVersion < WINDOWS_8) + return NULL; + + if (PhBeginInitOnce(&initOnce)) + { + PhInitializeCapabilityGuidCache(&capabilityGuidArrayList); + PhEndInitOnce(&initOnce); + } + + for (i = 0; i < capabilityGuidArrayList.Count; i++) + { + entry = PhItemArray(&capabilityGuidArrayList, i); + + if (PhEqualString(entry->CapabilityGuid, GuidString, TRUE)) + { + return PhReferenceObject(entry->Name); + } + } + + return NULL; +} diff --git a/phlib/mapexlf.c b/phlib/mapexlf.c new file mode 100644 index 000000000000..aed2fb818468 --- /dev/null +++ b/phlib/mapexlf.c @@ -0,0 +1,617 @@ +/* + * Process Hacker - + * ELF library support + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +NTSTATUS PhInitializeMappedWslImage( + _Out_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ PVOID ViewBase, + _In_ SIZE_T Size + ) +{ + MappedWslImage->ViewBase = ViewBase; + MappedWslImage->Size = Size; + MappedWslImage->Header = (PELF_IMAGE_HEADER)ViewBase; + + __try + { + PhProbeAddress( + MappedWslImage->Header, + sizeof(ELF_IMAGE_HEADER), + MappedWslImage->ViewBase, + MappedWslImage->Size, + 1 + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + // Check the magic number. + + if (!RtlEqualMemory(MappedWslImage->Header->e_ident, ELFMAG, sizeof(ELFMAG))) + return STATUS_FAIL_CHECK; + + // Check the class type. + + if (MappedWslImage->Header->e_ident[EI_CLASS] == ELFCLASS64) + { + MappedWslImage->Headers64 = PTR_ADD_OFFSET(MappedWslImage->Header, sizeof(ELF_IMAGE_HEADER)); + + __try + { + PhProbeAddress( + MappedWslImage->Headers64, + sizeof(ELF64_IMAGE_SECTION_HEADER), + MappedWslImage->ViewBase, + MappedWslImage->Size, + 1 + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + // TODO: Add 32bit ELF support. + return STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT; + } + + //if (MappedWslImage->Headers64->e_phentsize != sizeof(ELF64_IMAGE_SEGMENT_HEADER)) + // return STATUS_FAIL_CHECK; + if (MappedWslImage->Headers64->e_shentsize != sizeof(ELF64_IMAGE_SECTION_HEADER)) + return STATUS_FAIL_CHECK; + + return STATUS_SUCCESS; +} + +PELF64_IMAGE_SEGMENT_HEADER PhGetMappedWslImageSegment( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ USHORT Index + ) +{ + return IMAGE_ELF64_SEGMENT_BY_INDEX(IMAGE_FIRST_ELF64_SEGMENT(MappedWslImage), Index); +} + +PELF64_IMAGE_SEGMENT_HEADER PhGetWslImageSegmentByType( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_opt_ USHORT Type + ) +{ + PELF64_IMAGE_SEGMENT_HEADER segmentHeader; + USHORT i; + + segmentHeader = IMAGE_FIRST_ELF64_SEGMENT(MappedWslImage); + + for (i = 0; i < MappedWslImage->Headers64->e_phnum; i++) + { + if (segmentHeader[i].p_type == Type) + return segmentHeader; + } + + return NULL; +} + +PVOID PhGetMappedWslImageSectionData( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_opt_ PSTR Name, + _In_opt_ USHORT Index + ) +{ + if (Name) + { + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER stringSection; + PELF64_IMAGE_SECTION_HEADER section; + PVOID stringTable; + USHORT i; + + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + stringSection = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, MappedWslImage->Headers64->e_shstrndx); + stringTable = PTR_ADD_OFFSET(MappedWslImage->Header, stringSection->sh_offset); + + for (i = 0; i < MappedWslImage->Headers64->e_shnum; i++) + { + section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, i); + + if (PhEqualBytesZ(Name, PTR_ADD_OFFSET(stringTable, section->sh_name), FALSE)) + { + return PTR_ADD_OFFSET(MappedWslImage->Header, section->sh_offset); + } + } + + return NULL; + } + else + { + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER section; + + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, Index); + + return PTR_ADD_OFFSET(MappedWslImage->Header, section->sh_offset); + } +} + +PVOID PhGetMappedWslImageSectionDataByType( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ UINT32 Type + ) +{ + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER section; + USHORT i; + + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + + for (i = 0; i < MappedWslImage->Headers64->e_shnum; i++) + { + section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, i); + + if (section->sh_type == Type) + { + return PTR_ADD_OFFSET(MappedWslImage->Header, section->sh_offset); + } + } + + return NULL; +} + +PELF64_IMAGE_SECTION_HEADER PhGetMappedWslImageSectionByIndex( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ USHORT Index + ) +{ + return IMAGE_ELF64_SECTION_BY_INDEX(IMAGE_FIRST_ELF64_SECTION(MappedWslImage), Index); +} + +PELF64_IMAGE_SECTION_HEADER PhGetMappedWslImageSectionByType( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ UINT32 Type + ) +{ + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER section; + USHORT i; + + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + + for (i = 0; i < MappedWslImage->Headers64->e_shnum; i++) + { + section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, i); + + if (section->sh_type == Type) + return section; + } + + return NULL; +} + +ULONG64 PhGetMappedWslImageBaseAddress( + _In_ PPH_MAPPED_IMAGE MappedWslImage + ) +{ + ULONG64 baseAddress = MAXULONG64; + PELF64_IMAGE_SEGMENT_HEADER segment; + USHORT i; + + segment = IMAGE_FIRST_ELF64_SEGMENT(MappedWslImage); + + for (i = 0; i < MappedWslImage->Headers64->e_phnum; i++) + { + if (segment[i].p_type == PT_LOAD) + { + if (segment[i].p_paddr < baseAddress) + baseAddress = segment[i].p_paddr; + } + } + + if (baseAddress == MAXULONG64) + baseAddress = 0; + + return baseAddress; +} + +BOOLEAN PhGetMappedWslImageSections( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ USHORT *NumberOfSections, + _Out_ PPH_ELF_IMAGE_SECTION *ImageSections + ) +{ + USHORT count; + USHORT i; + PPH_ELF_IMAGE_SECTION sections; + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER stringSection; + PVOID stringTableAddress; + + count = MappedWslImage->Headers64->e_shnum; + sections = PhAllocate(sizeof(PH_ELF_IMAGE_SECTION) * count); + memset(sections, 0, sizeof(PH_ELF_IMAGE_SECTION) * count); + + // Get the first section. + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + + // Get the string section. + stringSection = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, MappedWslImage->Headers64->e_shstrndx); + + // Get the string table (The string table is an array of null terminated strings). + stringTableAddress = PTR_ADD_OFFSET(MappedWslImage->Header, stringSection->sh_offset); + + // Enumerate the sections. + for (i = 0; i < count; i++) + { + sections[i].Type = sectionHeader[i].sh_type; + sections[i].Flags = sectionHeader[i].sh_flags; + sections[i].Address = sectionHeader[i].sh_addr; + sections[i].Offset = sectionHeader[i].sh_offset; + sections[i].Size = sectionHeader[i].sh_size; + + if (sectionHeader[i].sh_name) + { + // Get the section name from the ELF string table and convert to unicode. + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTableAddress, sectionHeader[i].sh_name), + -1, + sections[i].Name, + sizeof(sections[i].Name), + NULL + ); + } + } + + *NumberOfSections = count; + *ImageSections = sections; + + return TRUE; +} + +typedef struct _PH_ELF_VERSION_RECORD +{ + USHORT Version; + PSTR Name; + PSTR FileName; +} PH_ELF_VERSION_RECORD, *PPH_ELF_VERSION_RECORD; + +static PPH_LIST PhpParseMappedWslImageVersionRecords( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _In_ PVOID SymbolStringTable + ) +{ + PPH_LIST recordsList; + PELF_VERSION_NEED version; + + if (!(version = PhGetMappedWslImageSectionDataByType(MappedWslImage, SHT_SUNW_verneed))) + return NULL; + + recordsList = PhCreateList(10); + + while (TRUE) + { + PELF_VERSION_AUX versionAux = PTR_ADD_OFFSET(version, version->vn_aux); + + while (TRUE) + { + PPH_ELF_VERSION_RECORD versionInfo; + + versionInfo = PhAllocateZero(sizeof(PH_ELF_VERSION_RECORD)); + versionInfo->Version = versionAux->vna_other; + versionInfo->Name = PTR_ADD_OFFSET(SymbolStringTable, versionAux->vna_name); + versionInfo->FileName = PTR_ADD_OFFSET(SymbolStringTable, version->vn_file); + + PhAddItemList(recordsList, versionInfo); + + if (versionAux->vna_next == 0) + break; + + versionAux = PTR_ADD_OFFSET(versionAux, versionAux->vna_next); + } + + if (version->vn_next == 0) + break; + + version = PTR_ADD_OFFSET(version, version->vn_next); + } + + return recordsList; +} + +static VOID PhpFreeMappedWslImageVersionRecords( + _In_ PPH_LIST RecordsList + ) +{ + for (ULONG i = 0; i < RecordsList->Count; i++) + PhFree(RecordsList->Items[i]); + + PhDereferenceObject(RecordsList); +} + +static PSTR PhpFindWslImageVersionRecordName( + _In_ PPH_LIST VersionRecordList, + _In_ USHORT VersionIndex + ) +{ + // Note: 'ldconfig -v' and 'ldconfig -p' can be used to locate the path from the FileName. + for (ULONG i = 0; i < VersionRecordList->Count; i++) + { + PPH_ELF_VERSION_RECORD versionInfo = VersionRecordList->Items[i]; + + if (versionInfo->Version == VersionIndex) + return versionInfo->FileName; + } + + return NULL; +} + +BOOLEAN PhGetMappedWslImageSymbols( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ PPH_LIST *ImageSymbols + ) +{ + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER section; + PPH_LIST sectionSymbols; + USHORT i; + + sectionSymbols = PhCreateList(0x800); + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + + for (i = 0; i < MappedWslImage->Headers64->e_shnum; i++) + { + ULONGLONG count; + ULONGLONG ii; + PELF_IMAGE_SYMBOL_ENTRY entry; + PVOID stringTable; + PELF_VERSION_TABLE versionTable; + PPH_LIST versionRecords; + + section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, i); + + // NOTE: The below code needs some improvements for SHT_SYMTAB -dmex + if (section->sh_type != SHT_SYMTAB && section->sh_type != SHT_DYNSYM) + continue; + + if (section->sh_entsize != sizeof(ELF_IMAGE_SYMBOL_ENTRY)) + return FALSE; + + count = section->sh_size / sizeof(ELF_IMAGE_SYMBOL_ENTRY); + entry = PTR_ADD_OFFSET(MappedWslImage->Header, section->sh_offset); + stringTable = PhGetMappedWslImageSectionData(MappedWslImage, NULL, section->sh_link); + + // NOTE: SHT_DYNSYM entries include the version in the symbol name (e.g. printf@GLIBC_2.2.5) + // instead of using a version record entry from the SHT_SUNW_versym section -dmex + versionTable = PhGetMappedWslImageSectionDataByType(MappedWslImage, SHT_SUNW_versym); + versionRecords = PhpParseMappedWslImageVersionRecords(MappedWslImage, stringTable); + + for (ii = 1; ii < count; ii++) + { + if (entry[ii].st_shndx == SHN_UNDEF) + { + PPH_ELF_IMAGE_SYMBOL_ENTRY import; + + import = PhAllocateZero(sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + import->ImportSymbol = TRUE; + import->Address = entry[ii].st_value; + import->Size = entry[ii].st_size; + import->TypeInfo = entry[ii].st_info; + import->OtherInfo = entry[ii].st_other; + import->SectionIndex = entry[ii].st_shndx; + + // function name + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTable, entry[ii].st_name), + -1, + import->Name, + sizeof(import->Name), + NULL + ); + + // import library name + if (versionTable && versionRecords) + { + PSTR moduleName; + + if (moduleName = PhpFindWslImageVersionRecordName(versionRecords, versionTable[ii].vs_vers)) + { + PhCopyStringZFromBytes( + moduleName, + -1, + import->Module, + sizeof(import->Module), + NULL + ); + } + } + + PhAddItemList(sectionSymbols, import); + } + else if (entry[ii].st_shndx != SHN_UNDEF && entry[ii].st_value != 0) + { + PPH_ELF_IMAGE_SYMBOL_ENTRY export; + + if (ELF_ST_TYPE(entry[ii].st_info) == STT_SECTION) // Ignore section symbol types. + continue; + + export = PhAllocateZero(sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + export->ExportSymbol = TRUE; + export->Address = entry[ii].st_value; + export->Size = entry[ii].st_size; + export->TypeInfo = entry[ii].st_info; + export->OtherInfo = entry[ii].st_other; + export->SectionIndex = entry[ii].st_shndx; + + // function name + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTable, entry[ii].st_name), + -1, + export->Name, + sizeof(export->Name), + NULL + ); + + PhAddItemList(sectionSymbols, export); + } + else + { + PPH_ELF_IMAGE_SYMBOL_ENTRY export; + + export = PhAllocateZero(sizeof(PH_ELF_IMAGE_SYMBOL_ENTRY)); + export->UnknownSymbol = TRUE; + export->Address = entry[ii].st_value; + export->Size = entry[ii].st_size; + export->TypeInfo = entry[ii].st_info; + export->OtherInfo = entry[ii].st_other; + export->SectionIndex = entry[ii].st_shndx; + + // function name + PhCopyStringZFromBytes( + PTR_ADD_OFFSET(stringTable, entry[ii].st_name), + -1, + export->Name, + sizeof(export->Name), + NULL + ); + + PhAddItemList(sectionSymbols, export); + } + } + + if (versionRecords) + PhpFreeMappedWslImageVersionRecords(versionRecords); + } + + *ImageSymbols = sectionSymbols; + + return TRUE; +} + +VOID PhFreeMappedWslImageSymbols( + _In_ PPH_LIST ImageSymbols + ) +{ + for (ULONG i = 0; i < ImageSymbols->Count; i++) + PhFree(ImageSymbols->Items[i]); + + PhDereferenceObject(ImageSymbols); +} + +BOOLEAN PhGetMappedWslImageDynamic( + _In_ PPH_MAPPED_IMAGE MappedWslImage, + _Out_ PPH_LIST *ImageDynamic + ) +{ + PELF64_IMAGE_SECTION_HEADER sectionHeader; + PELF64_IMAGE_SECTION_HEADER section; + PPH_LIST dynamicSymbols; + USHORT i; + + dynamicSymbols = PhCreateList(0x40); + sectionHeader = IMAGE_FIRST_ELF64_SECTION(MappedWslImage); + + for (i = 0; i < MappedWslImage->Headers64->e_shnum; i++) + { + ULONGLONG count; + ULONGLONG ii; + PELF64_IMAGE_DYNAMIC_ENTRY entry; + PVOID stringTable; + + section = IMAGE_ELF64_SECTION_BY_INDEX(sectionHeader, i); + + if (section->sh_type != SHT_DYNAMIC) + continue; + + if (section->sh_entsize != sizeof(ELF64_IMAGE_DYNAMIC_ENTRY)) + return FALSE; + + count = section->sh_size / sizeof(ELF64_IMAGE_DYNAMIC_ENTRY); + entry = PTR_ADD_OFFSET(MappedWslImage->Header, section->sh_offset); + stringTable = PhGetMappedWslImageSectionData(MappedWslImage, NULL, section->sh_link); + + for (ii = 0; ii < count; ii++) + { + PPH_ELF_IMAGE_DYNAMIC_ENTRY dynamic; + + dynamic = PhAllocateZero(sizeof(PH_ELF_IMAGE_DYNAMIC_ENTRY)); + dynamic->Tag = entry[ii].d_tag; + + switch (dynamic->Tag) + { + case DT_NEEDED: + case DT_SONAME: + case DT_RPATH: + case DT_RUNPATH: + dynamic->Value = PhConvertUtf8ToUtf16(PTR_ADD_OFFSET(stringTable, entry[ii].d_val)); + break; + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_RELAENT: + case DT_STRSZ: + case DT_SYMENT: + case DT_RELSZ: + case DT_RELENT: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_PREINIT_ARRAYSZ: + dynamic->Value = PhFormatSize(entry[ii].d_val, ULONG_MAX); + break; + case DT_RELACOUNT: + case DT_RELCOUNT: + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + dynamic->Value = PhFormatUInt64(entry[ii].d_val, TRUE); + break; + default: + dynamic->Value = PhFormatString(L"0x%llx", entry[ii].d_val); + break; + } + + PhAddItemList(dynamicSymbols, dynamic); + + if (entry[ii].d_tag == DT_NULL) + break; + } + } + + *ImageDynamic = dynamicSymbols; + + return TRUE; +} + +VOID PhFreeMappedWslImageDynamic( + _In_ PPH_LIST ImageDynamic + ) +{ + for (ULONG i = 0; i < ImageDynamic->Count; i++) + { + PPH_ELF_IMAGE_DYNAMIC_ENTRY dynamic = ImageDynamic->Items[i]; + + PhDereferenceObject(dynamic->Value); + PhFree(dynamic); + } + + PhDereferenceObject(ImageDynamic); +} diff --git a/phlib/mapimg.c b/phlib/mapimg.c index 05d179f6bcf4..d9d77bc132af 100644 --- a/phlib/mapimg.c +++ b/phlib/mapimg.c @@ -1,1404 +1,1975 @@ -/* - * Process Hacker - - * mapped image - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This file contains functions to load and retrieve information for image files (exe, dll). The - * file format for image files is explained in the PE/COFF specification located at: - * - * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx - */ - -#include -#include - -#include - -VOID PhpMappedImageProbe( - _In_ PPH_MAPPED_IMAGE MappedImage, - _In_ PVOID Address, - _In_ SIZE_T Length - ); - -ULONG PhpLookupMappedImageExportName( - _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, - _In_ PSTR Name - ); - -NTSTATUS PhInitializeMappedImage( - _Out_ PPH_MAPPED_IMAGE MappedImage, - _In_ PVOID ViewBase, - _In_ SIZE_T Size - ) -{ - PIMAGE_DOS_HEADER dosHeader; - ULONG ntHeadersOffset; - - MappedImage->ViewBase = ViewBase; - MappedImage->Size = Size; - - dosHeader = (PIMAGE_DOS_HEADER)ViewBase; - - __try - { - PhpMappedImageProbe(MappedImage, dosHeader, sizeof(IMAGE_DOS_HEADER)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - // Check the initial MZ. - - if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) - return STATUS_INVALID_IMAGE_NOT_MZ; - - // Get a pointer to the NT headers and probe it. - - ntHeadersOffset = (ULONG)dosHeader->e_lfanew; - - if (ntHeadersOffset == 0) - return STATUS_INVALID_IMAGE_FORMAT; - if (ntHeadersOffset >= 0x10000000 || ntHeadersOffset >= Size) - return STATUS_INVALID_IMAGE_FORMAT; - - MappedImage->NtHeaders = (PIMAGE_NT_HEADERS)PTR_ADD_OFFSET(ViewBase, ntHeadersOffset); - - __try - { - PhpMappedImageProbe( - MappedImage, - MappedImage->NtHeaders, - FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) - ); - PhpMappedImageProbe( - MappedImage, - MappedImage->NtHeaders, - FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + - MappedImage->NtHeaders->FileHeader.SizeOfOptionalHeader + - MappedImage->NtHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - // Check the signature and verify the magic. - - if (MappedImage->NtHeaders->Signature != IMAGE_NT_SIGNATURE) - return STATUS_INVALID_IMAGE_FORMAT; - - MappedImage->Magic = MappedImage->NtHeaders->OptionalHeader.Magic; - - if ( - MappedImage->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && - MappedImage->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC - ) - return STATUS_INVALID_IMAGE_FORMAT; - - // Get a pointer to the first section. - - MappedImage->NumberOfSections = MappedImage->NtHeaders->FileHeader.NumberOfSections; - - MappedImage->Sections = (PIMAGE_SECTION_HEADER)( - ((PCHAR)&MappedImage->NtHeaders->OptionalHeader) + - MappedImage->NtHeaders->FileHeader.SizeOfOptionalHeader - ); - - return STATUS_SUCCESS; -} - -NTSTATUS PhLoadMappedImage( - _In_opt_ PWSTR FileName, - _In_opt_ HANDLE FileHandle, - _In_ BOOLEAN ReadOnly, - _Out_ PPH_MAPPED_IMAGE MappedImage - ) -{ - NTSTATUS status; - - status = PhMapViewOfEntireFile( - FileName, - FileHandle, - ReadOnly, - &MappedImage->ViewBase, - &MappedImage->Size - ); - - if (NT_SUCCESS(status)) - { - status = PhInitializeMappedImage( - MappedImage, - MappedImage->ViewBase, - MappedImage->Size - ); - - if (!NT_SUCCESS(status)) - { - NtUnmapViewOfSection(NtCurrentProcess(), MappedImage->ViewBase); - } - } - - return status; -} - -NTSTATUS PhUnloadMappedImage( - _Inout_ PPH_MAPPED_IMAGE MappedImage - ) -{ - return NtUnmapViewOfSection( - NtCurrentProcess(), - MappedImage->ViewBase - ); -} - -NTSTATUS PhMapViewOfEntireFile( - _In_opt_ PWSTR FileName, - _In_opt_ HANDLE FileHandle, - _In_ BOOLEAN ReadOnly, - _Out_ PVOID *ViewBase, - _Out_ PSIZE_T Size - ) -{ - NTSTATUS status; - BOOLEAN openedFile = FALSE; - LARGE_INTEGER size; - HANDLE sectionHandle = NULL; - SIZE_T viewSize; - PVOID viewBase; - - if (!FileName && !FileHandle) - return STATUS_INVALID_PARAMETER_MIX; - - // Open the file if we weren't supplied a file handle. - if (!FileHandle) - { - status = PhCreateFileWin32( - &FileHandle, - FileName, - ((FILE_READ_ATTRIBUTES | FILE_READ_DATA) | - (!ReadOnly ? (FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA) : 0)) | SYNCHRONIZE, - 0, - FILE_SHARE_READ, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - return status; - - openedFile = TRUE; - } - - // Get the file size and create the section. - - status = PhGetFileSize(FileHandle, &size); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - status = NtCreateSection( - §ionHandle, - SECTION_ALL_ACCESS, - NULL, - &size, - ReadOnly ? PAGE_READONLY : PAGE_READWRITE, - SEC_COMMIT, - FileHandle - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - // Map the section. - - viewSize = (SIZE_T)size.QuadPart; - viewBase = NULL; - - status = NtMapViewOfSection( - sectionHandle, - NtCurrentProcess(), - &viewBase, - 0, - 0, - NULL, - &viewSize, - ViewShare, - 0, - ReadOnly ? PAGE_READONLY : PAGE_READWRITE - ); - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - *ViewBase = viewBase; - *Size = (SIZE_T)size.QuadPart; - -CleanupExit: - if (sectionHandle) - NtClose(sectionHandle); - if (openedFile) - NtClose(FileHandle); - - return status; -} - -VOID PhpMappedImageProbe( - _In_ PPH_MAPPED_IMAGE MappedImage, - _In_ PVOID Address, - _In_ SIZE_T Length - ) -{ - PhProbeAddress(Address, Length, MappedImage->ViewBase, MappedImage->Size, 1); -} - -PIMAGE_SECTION_HEADER PhMappedImageRvaToSection( - _In_ PPH_MAPPED_IMAGE MappedImage, - _In_ ULONG Rva - ) -{ - ULONG i; - - for (i = 0; i < MappedImage->NumberOfSections; i++) - { - if ( - (Rva >= MappedImage->Sections[i].VirtualAddress) && - (Rva < MappedImage->Sections[i].VirtualAddress + MappedImage->Sections[i].SizeOfRawData) - ) - { - return &MappedImage->Sections[i]; - } - } - - return NULL; -} - -PVOID PhMappedImageRvaToVa( - _In_ PPH_MAPPED_IMAGE MappedImage, - _In_ ULONG Rva, - _Out_opt_ PIMAGE_SECTION_HEADER *Section - ) -{ - PIMAGE_SECTION_HEADER section; - - section = PhMappedImageRvaToSection(MappedImage, Rva); - - if (!section) - return NULL; - - if (Section) - *Section = section; - - return (PVOID)( - (ULONG_PTR)MappedImage->ViewBase + - (Rva - section->VirtualAddress) + - section->PointerToRawData - ); -} - -BOOLEAN PhGetMappedImageSectionName( - _In_ PIMAGE_SECTION_HEADER Section, - _Out_writes_opt_z_(Count) PSTR Buffer, - _In_ ULONG Count, - _Out_opt_ PULONG ReturnCount - ) -{ - BOOLEAN result; - SIZE_T returnCount; - - result = PhCopyBytesZ( - Section->Name, - IMAGE_SIZEOF_SHORT_NAME, - Buffer, - Count, - &returnCount - ); - - if (ReturnCount) - *ReturnCount = (ULONG)returnCount; - - return result; -} - -NTSTATUS PhGetMappedImageDataEntry( - _In_ PPH_MAPPED_IMAGE MappedImage, - _In_ ULONG Index, - _Out_ PIMAGE_DATA_DIRECTORY *Entry - ) -{ - if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - PIMAGE_OPTIONAL_HEADER32 optionalHeader; - - optionalHeader = (PIMAGE_OPTIONAL_HEADER32)&MappedImage->NtHeaders->OptionalHeader; - - if (Index >= optionalHeader->NumberOfRvaAndSizes) - return STATUS_INVALID_PARAMETER_2; - - *Entry = &optionalHeader->DataDirectory[Index]; - } - else if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) - { - PIMAGE_OPTIONAL_HEADER64 optionalHeader; - - optionalHeader = (PIMAGE_OPTIONAL_HEADER64)&MappedImage->NtHeaders->OptionalHeader; - - if (Index >= optionalHeader->NumberOfRvaAndSizes) - return STATUS_INVALID_PARAMETER_2; - - *Entry = &optionalHeader->DataDirectory[Index]; - } - else - { - return STATUS_INVALID_PARAMETER; - } - - return STATUS_SUCCESS; -} - -FORCEINLINE NTSTATUS PhpGetMappedImageLoadConfig( - _In_ PPH_MAPPED_IMAGE MappedImage, - _In_ USHORT Magic, - _In_ ULONG ProbeLength, - _Out_ PVOID *LoadConfig - ) -{ - NTSTATUS status; - PIMAGE_DATA_DIRECTORY entry; - PVOID loadConfig; - - if (MappedImage->Magic != Magic) - return STATUS_INVALID_PARAMETER; - - status = PhGetMappedImageDataEntry(MappedImage, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &entry); - - if (!NT_SUCCESS(status)) - return status; - - loadConfig = PhMappedImageRvaToVa(MappedImage, entry->VirtualAddress, NULL); - - if (!loadConfig) - return STATUS_INVALID_PARAMETER; - - __try - { - PhpMappedImageProbe(MappedImage, loadConfig, ProbeLength); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - *LoadConfig = loadConfig; - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageLoadConfig32( - _In_ PPH_MAPPED_IMAGE MappedImage, - _Out_ PIMAGE_LOAD_CONFIG_DIRECTORY32 *LoadConfig - ) -{ - return PhpGetMappedImageLoadConfig( - MappedImage, - IMAGE_NT_OPTIONAL_HDR32_MAGIC, - sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32), - LoadConfig - ); -} - -NTSTATUS PhGetMappedImageLoadConfig64( - _In_ PPH_MAPPED_IMAGE MappedImage, - _Out_ PIMAGE_LOAD_CONFIG_DIRECTORY64 *LoadConfig - ) -{ - return PhpGetMappedImageLoadConfig( - MappedImage, - IMAGE_NT_OPTIONAL_HDR64_MAGIC, - sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64), - LoadConfig - ); -} - -NTSTATUS PhLoadRemoteMappedImage( - _In_ HANDLE ProcessHandle, - _In_ PVOID ViewBase, - _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage - ) -{ - NTSTATUS status; - IMAGE_DOS_HEADER dosHeader; - ULONG ntHeadersOffset; - IMAGE_NT_HEADERS32 ntHeaders; - ULONG ntHeadersSize; - - RemoteMappedImage->ViewBase = ViewBase; - - status = NtReadVirtualMemory( - ProcessHandle, - ViewBase, - &dosHeader, - sizeof(IMAGE_DOS_HEADER), - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Check the initial MZ. - - if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) - return STATUS_INVALID_IMAGE_NOT_MZ; - - // Get a pointer to the NT headers and read it in for some basic information. - - ntHeadersOffset = (ULONG)dosHeader.e_lfanew; - - if (ntHeadersOffset == 0 || ntHeadersOffset >= 0x10000000) - return STATUS_INVALID_IMAGE_FORMAT; - - status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), - &ntHeaders, - sizeof(IMAGE_NT_HEADERS32), - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Check the signature and verify the magic. - - if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) - return STATUS_INVALID_IMAGE_FORMAT; - - RemoteMappedImage->Magic = ntHeaders.OptionalHeader.Magic; - - if ( - RemoteMappedImage->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && - RemoteMappedImage->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC - ) - return STATUS_INVALID_IMAGE_FORMAT; - - // Get the real size and read in the whole thing. - - RemoteMappedImage->NumberOfSections = ntHeaders.FileHeader.NumberOfSections; - ntHeadersSize = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + - ntHeaders.FileHeader.SizeOfOptionalHeader + - RemoteMappedImage->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); - - if (ntHeadersSize > 1024 * 1024) // 1 MB - return STATUS_INVALID_IMAGE_FORMAT; - - RemoteMappedImage->NtHeaders = PhAllocate(ntHeadersSize); - - status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), - RemoteMappedImage->NtHeaders, - ntHeadersSize, - NULL - ); - - if (!NT_SUCCESS(status)) - { - PhFree(RemoteMappedImage->NtHeaders); - return status; - } - - RemoteMappedImage->Sections = (PIMAGE_SECTION_HEADER)( - (PCHAR)RemoteMappedImage->NtHeaders + - FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + ntHeaders.FileHeader.SizeOfOptionalHeader - ); - - return STATUS_SUCCESS; -} - -NTSTATUS PhUnloadRemoteMappedImage( - _Inout_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage - ) -{ - PhFree(RemoteMappedImage->NtHeaders); - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageExports( - _Out_ PPH_MAPPED_IMAGE_EXPORTS Exports, - _In_ PPH_MAPPED_IMAGE MappedImage - ) -{ - NTSTATUS status; - PIMAGE_EXPORT_DIRECTORY exportDirectory; - - Exports->MappedImage = MappedImage; - - // Get a pointer to the export directory. - - status = PhGetMappedImageDataEntry( - MappedImage, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &Exports->DataDirectory - ); - - if (!NT_SUCCESS(status)) - return status; - - exportDirectory = PhMappedImageRvaToVa( - MappedImage, - Exports->DataDirectory->VirtualAddress, - NULL - ); - - if (!exportDirectory) - return STATUS_INVALID_PARAMETER; - - __try - { - PhpMappedImageProbe(MappedImage, exportDirectory, sizeof(IMAGE_EXPORT_DIRECTORY)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - Exports->ExportDirectory = exportDirectory; - Exports->NumberOfEntries = exportDirectory->NumberOfFunctions; - - // Get pointers to the various tables and probe them. - - Exports->AddressTable = (PULONG)PhMappedImageRvaToVa( - MappedImage, - exportDirectory->AddressOfFunctions, - NULL - ); - Exports->NamePointerTable = (PULONG)PhMappedImageRvaToVa( - MappedImage, - exportDirectory->AddressOfNames, - NULL - ); - Exports->OrdinalTable = (PUSHORT)PhMappedImageRvaToVa( - MappedImage, - exportDirectory->AddressOfNameOrdinals, - NULL - ); - - if ( - !Exports->AddressTable || - !Exports->NamePointerTable || - !Exports->OrdinalTable - ) - return STATUS_INVALID_PARAMETER; - - __try - { - PhpMappedImageProbe( - MappedImage, - Exports->AddressTable, - exportDirectory->NumberOfFunctions * sizeof(ULONG) - ); - PhpMappedImageProbe( - MappedImage, - Exports->NamePointerTable, - exportDirectory->NumberOfNames * sizeof(ULONG) - ); - PhpMappedImageProbe( - MappedImage, - Exports->OrdinalTable, // ordinal list for named exports - exportDirectory->NumberOfNames * sizeof(USHORT) - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - // The ordinal and name tables are parallel. - // Getting an index into the name table (e.g. by doing a binary - // search) and indexing into the ordinal table will produce the - // ordinal for that name, *unbiased* (unlike in the specification). - // The unbiased ordinal is an index into the address table. - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageExportEntry( - _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, - _In_ ULONG Index, - _Out_ PPH_MAPPED_IMAGE_EXPORT_ENTRY Entry - ) -{ - ULONG nameIndex = 0; - BOOLEAN exportByName = FALSE; - PSTR name; - - if (Index >= Exports->ExportDirectory->NumberOfFunctions) - return STATUS_PROCEDURE_NOT_FOUND; - - Entry->Ordinal = (USHORT)Index + (USHORT)Exports->ExportDirectory->Base; - - // look into named exports ordinal list. - for (nameIndex = 0; nameIndex < Exports->ExportDirectory->NumberOfNames; nameIndex++) - { - if (Index == Exports->OrdinalTable[nameIndex]) - { - exportByName = TRUE; - break; - } - } - - - if (exportByName) - { - name = PhMappedImageRvaToVa( - Exports->MappedImage, - Exports->NamePointerTable[nameIndex], - NULL - ); - - if (!name) - return STATUS_INVALID_PARAMETER; - - // TODO: Probe the name. - - Entry->Name = name; - } - else - { - Entry->Name = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageExportFunction( - _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, - _In_opt_ PSTR Name, - _In_opt_ USHORT Ordinal, - _Out_ PPH_MAPPED_IMAGE_EXPORT_FUNCTION Function - ) -{ - ULONG rva; - - if (Name) - { - ULONG index; - - index = PhpLookupMappedImageExportName(Exports, Name); - - if (index == -1) - return STATUS_PROCEDURE_NOT_FOUND; - - Ordinal = Exports->OrdinalTable[index] + (USHORT)Exports->ExportDirectory->Base; - } - - Ordinal -= (USHORT)Exports->ExportDirectory->Base; - - if (Ordinal >= Exports->ExportDirectory->NumberOfFunctions) - return STATUS_PROCEDURE_NOT_FOUND; - - rva = Exports->AddressTable[Ordinal]; - - if ( - (rva >= Exports->DataDirectory->VirtualAddress) && - (rva < Exports->DataDirectory->VirtualAddress + Exports->DataDirectory->Size) - ) - { - // This is a forwarder RVA. - - Function->ForwardedName = PhMappedImageRvaToVa( - Exports->MappedImage, - rva, - NULL - ); - - if (!Function->ForwardedName) - return STATUS_INVALID_PARAMETER; - - // TODO: Probe the name. - - Function->Function = NULL; - } - else - { - Function->Function = PhMappedImageRvaToVa( - Exports->MappedImage, - rva, - NULL - ); - Function->ForwardedName = NULL; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageExportFunctionRemote( - _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, - _In_opt_ PSTR Name, - _In_opt_ USHORT Ordinal, - _In_ PVOID RemoteBase, - _Out_ PVOID *Function - ) -{ - ULONG rva; - - if (Name) - { - ULONG index; - - index = PhpLookupMappedImageExportName(Exports, Name); - - if (index == -1) - return STATUS_PROCEDURE_NOT_FOUND; - - Ordinal = Exports->OrdinalTable[index] + (USHORT)Exports->ExportDirectory->Base; - } - - Ordinal -= (USHORT)Exports->ExportDirectory->Base; - - if (Ordinal >= Exports->ExportDirectory->NumberOfFunctions) - return STATUS_PROCEDURE_NOT_FOUND; - - rva = Exports->AddressTable[Ordinal]; - - if ( - (rva >= Exports->DataDirectory->VirtualAddress) && - (rva < Exports->DataDirectory->VirtualAddress + Exports->DataDirectory->Size) - ) - { - // This is a forwarder RVA. Not supported for remote lookup. - return STATUS_NOT_SUPPORTED; - } - else - { - *Function = PTR_ADD_OFFSET(RemoteBase, rva); - } - - return STATUS_SUCCESS; -} - -ULONG PhpLookupMappedImageExportName( - _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, - _In_ PSTR Name - ) -{ - LONG low; - LONG high; - LONG i; - - if (Exports->ExportDirectory->NumberOfNames == 0) - return -1; - - low = 0; - high = Exports->ExportDirectory->NumberOfNames - 1; - - do - { - PSTR name; - INT comparison; - - i = (low + high) / 2; - - name = PhMappedImageRvaToVa( - Exports->MappedImage, - Exports->NamePointerTable[i], - NULL - ); - - if (!name) - return -1; - - // TODO: Probe the name. - - comparison = strcmp(Name, name); - - if (comparison == 0) - return i; - else if (comparison < 0) - high = i - 1; - else - low = i + 1; - } while (low <= high); - - return -1; -} - -NTSTATUS PhGetMappedImageImports( - _Out_ PPH_MAPPED_IMAGE_IMPORTS Imports, - _In_ PPH_MAPPED_IMAGE MappedImage - ) -{ - NTSTATUS status; - PIMAGE_DATA_DIRECTORY dataDirectory; - PIMAGE_IMPORT_DESCRIPTOR descriptor; - ULONG i; - - Imports->MappedImage = MappedImage; - Imports->Flags = 0; - - status = PhGetMappedImageDataEntry( - MappedImage, - IMAGE_DIRECTORY_ENTRY_IMPORT, - &dataDirectory - ); - - if (!NT_SUCCESS(status)) - return status; - - descriptor = PhMappedImageRvaToVa( - MappedImage, - dataDirectory->VirtualAddress, - NULL - ); - - if (!descriptor) - return STATUS_INVALID_PARAMETER; - - Imports->DescriptorTable = descriptor; - - // Do a scan to determine how many import descriptors there are. - - i = 0; - - __try - { - while (TRUE) - { - PhpMappedImageProbe(MappedImage, descriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR)); - - if (descriptor->OriginalFirstThunk == 0 && descriptor->FirstThunk == 0) - break; - - descriptor++; - i++; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - Imports->NumberOfDlls = i; - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageImportDll( - _In_ PPH_MAPPED_IMAGE_IMPORTS Imports, - _In_ ULONG Index, - _Out_ PPH_MAPPED_IMAGE_IMPORT_DLL ImportDll - ) -{ - ULONG i; - - if (Index >= Imports->NumberOfDlls) - return STATUS_INVALID_PARAMETER_2; - - ImportDll->MappedImage = Imports->MappedImage; - ImportDll->Flags = Imports->Flags; - - if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS)) - { - ImportDll->Descriptor = &Imports->DescriptorTable[Index]; - - ImportDll->Name = PhMappedImageRvaToVa( - ImportDll->MappedImage, - ImportDll->Descriptor->Name, - NULL - ); - - if (!ImportDll->Name) - return STATUS_INVALID_PARAMETER; - - // TODO: Probe the name. - - if (ImportDll->Descriptor->OriginalFirstThunk) - { - ImportDll->LookupTable = PhMappedImageRvaToVa( - ImportDll->MappedImage, - ImportDll->Descriptor->OriginalFirstThunk, - NULL - ); - } - else - { - ImportDll->LookupTable = PhMappedImageRvaToVa( - ImportDll->MappedImage, - ImportDll->Descriptor->FirstThunk, - NULL - ); - } - } - else - { - ImportDll->DelayDescriptor = &((PImgDelayDescr)Imports->DelayDescriptorTable)[Index]; - - ImportDll->Name = PhMappedImageRvaToVa( - ImportDll->MappedImage, - ((PImgDelayDescr)ImportDll->DelayDescriptor)->rvaDLLName, - NULL - ); - - if (!ImportDll->Name) - return STATUS_INVALID_PARAMETER; - - // TODO: Probe the name. - - ImportDll->LookupTable = PhMappedImageRvaToVa( - ImportDll->MappedImage, - ((PImgDelayDescr)ImportDll->DelayDescriptor)->rvaINT, - NULL - ); - } - - if (!ImportDll->LookupTable) - return STATUS_INVALID_PARAMETER; - - // Do a scan to determine how many entries there are. - - i = 0; - - if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - PULONG entry; - - entry = (PULONG)ImportDll->LookupTable; - - __try - { - while (TRUE) - { - PhpMappedImageProbe( - ImportDll->MappedImage, - entry, - sizeof(ULONG) - ); - - if (*entry == 0) - break; - - entry++; - i++; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) - { - PULONG64 entry; - - entry = (PULONG64)ImportDll->LookupTable; - - __try - { - while (TRUE) - { - PhpMappedImageProbe( - ImportDll->MappedImage, - entry, - sizeof(ULONG64) - ); - - if (*entry == 0) - break; - - entry++; - i++; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - else - { - return STATUS_INVALID_PARAMETER; - } - - ImportDll->NumberOfEntries = i; - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageImportEntry( - _In_ PPH_MAPPED_IMAGE_IMPORT_DLL ImportDll, - _In_ ULONG Index, - _Out_ PPH_MAPPED_IMAGE_IMPORT_ENTRY Entry - ) -{ - PIMAGE_IMPORT_BY_NAME importByName; - - if (Index >= ImportDll->NumberOfEntries) - return STATUS_INVALID_PARAMETER_2; - - if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - ULONG entry; - - entry = ((PULONG)ImportDll->LookupTable)[Index]; - - // Is this entry using an ordinal? - if (entry & IMAGE_ORDINAL_FLAG32) - { - Entry->Name = NULL; - Entry->Ordinal = (USHORT)IMAGE_ORDINAL32(entry); - - return STATUS_SUCCESS; - } - else - { - importByName = PhMappedImageRvaToVa( - ImportDll->MappedImage, - entry, - NULL - ); - } - } - else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) - { - ULONG64 entry; - - entry = ((PULONG64)ImportDll->LookupTable)[Index]; - - // Is this entry using an ordinal? - if (entry & IMAGE_ORDINAL_FLAG64) - { - Entry->Name = NULL; - Entry->Ordinal = (USHORT)IMAGE_ORDINAL64(entry); - - return STATUS_SUCCESS; - } - else - { - importByName = PhMappedImageRvaToVa( - ImportDll->MappedImage, - (ULONG)entry, - NULL - ); - } - } - else - { - return STATUS_INVALID_PARAMETER; - } - - if (!importByName) - return STATUS_INVALID_PARAMETER; - - __try - { - PhpMappedImageProbe( - ImportDll->MappedImage, - importByName, - sizeof(IMAGE_IMPORT_BY_NAME) - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - Entry->Name = (PSTR)importByName->Name; - Entry->NameHint = importByName->Hint; - - // TODO: Probe the name. - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageDelayImports( - _Out_ PPH_MAPPED_IMAGE_IMPORTS Imports, - _In_ PPH_MAPPED_IMAGE MappedImage - ) -{ - NTSTATUS status; - PIMAGE_DATA_DIRECTORY dataDirectory; - PImgDelayDescr descriptor; - ULONG i; - - Imports->MappedImage = MappedImage; - Imports->Flags = PH_MAPPED_IMAGE_DELAY_IMPORTS; - - status = PhGetMappedImageDataEntry( - MappedImage, - IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, - &dataDirectory - ); - - if (!NT_SUCCESS(status)) - return status; - - descriptor = PhMappedImageRvaToVa( - MappedImage, - dataDirectory->VirtualAddress, - NULL - ); - - if (!descriptor) - return STATUS_INVALID_PARAMETER; - - Imports->DelayDescriptorTable = descriptor; - - // Do a scan to determine how many import descriptors there are. - - i = 0; - - __try - { - while (TRUE) - { - PhpMappedImageProbe(MappedImage, descriptor, sizeof(ImgDelayDescr)); - - if (descriptor->rvaIAT == 0 && descriptor->rvaINT == 0) - break; - - descriptor++; - i++; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - Imports->NumberOfDlls = i; - - return STATUS_SUCCESS; -} - -USHORT PhCheckSum( - _In_ ULONG Sum, - _In_reads_(Count) PUSHORT Buffer, - _In_ ULONG Count - ) -{ - while (Count--) - { - Sum += *Buffer++; - Sum = (Sum >> 16) + (Sum & 0xffff); - } - - Sum = (Sum >> 16) + Sum; - - return (USHORT)Sum; -} - -ULONG PhCheckSumMappedImage( - _In_ PPH_MAPPED_IMAGE MappedImage - ) -{ - ULONG checkSum; - USHORT partialSum; - PUSHORT adjust; - - partialSum = PhCheckSum(0, (PUSHORT)MappedImage->ViewBase, (ULONG)(MappedImage->Size + 1) / 2); - - // This is actually the same for 32-bit and 64-bit executables. - adjust = (PUSHORT)&MappedImage->NtHeaders->OptionalHeader.CheckSum; - - // Subtract the existing check sum (with carry). - partialSum -= partialSum < adjust[0]; - partialSum -= adjust[0]; - partialSum -= partialSum < adjust[1]; - partialSum -= adjust[1]; - - checkSum = partialSum + (ULONG)MappedImage->Size; - - return checkSum; -} - -NTSTATUS PhGetMappedImageCfg( - _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, - _In_ PPH_MAPPED_IMAGE MappedImage - ) -{ - NTSTATUS status; - PIMAGE_LOAD_CONFIG_DIRECTORY64 config64; - - if (!NT_SUCCESS(status = PhGetMappedImageLoadConfig64(MappedImage, &config64))) - return status; - - // Not every load configuration defines CFG characteristics - if (config64->Size < (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardFlags)) - return STATUS_INVALID_VIEW_SIZE; - - CfgConfig->MappedImage = MappedImage; - CfgConfig->EntrySize = sizeof(FIELD_OFFSET(IMAGE_CFG_ENTRY, Rva)) + - (ULONG)((config64->GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT); - CfgConfig->CfgInstrumented = !!(config64->GuardFlags & IMAGE_GUARD_CF_INSTRUMENTED); - CfgConfig->WriteIntegrityChecks = !!(config64->GuardFlags & IMAGE_GUARD_CFW_INSTRUMENTED); - CfgConfig->CfgFunctionTablePresent = !!(config64->GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT); - CfgConfig->SecurityCookieUnused = !!(config64->GuardFlags & IMAGE_GUARD_SECURITY_COOKIE_UNUSED); - CfgConfig->ProtectDelayLoadedIat = !!(config64->GuardFlags & IMAGE_GUARD_PROTECT_DELAYLOAD_IAT); - CfgConfig->DelayLoadInDidatSection = !!(config64->GuardFlags & IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION); - CfgConfig->EnableExportSuppression = !!(config64->GuardFlags & IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION); - CfgConfig->HasExportSuppressionInfos = !!(config64->GuardFlags & IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT); - CfgConfig->CfgLongJumpTablePresent = !!(config64->GuardFlags & IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT); - - CfgConfig->NumberOfGuardFunctionEntries = config64->GuardCFFunctionCount; - CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( - MappedImage, - (ULONG)(config64->GuardCFFunctionTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), - NULL - ); - - if (CfgConfig->GuardFunctionTable && CfgConfig->NumberOfGuardFunctionEntries) - { - __try - { - PhpMappedImageProbe( - MappedImage, - CfgConfig->GuardFunctionTable, - (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardFunctionEntries) - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - - CfgConfig->NumberOfGuardAdressIatEntries = 0; - CfgConfig->GuardAdressIatTable = 0; - - if ( - config64->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardAddressTakenIatEntryTable) + - sizeof(config64->GuardAddressTakenIatEntryTable) + - sizeof(config64->GuardAddressTakenIatEntryCount) - ) - { - CfgConfig->NumberOfGuardAdressIatEntries = config64->GuardAddressTakenIatEntryCount; - CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( - MappedImage, - (ULONG)(config64->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), - NULL - ); - - if (CfgConfig->GuardAdressIatTable && CfgConfig->NumberOfGuardAdressIatEntries) - { - __try - { - PhpMappedImageProbe( - MappedImage, - CfgConfig->GuardAdressIatTable, - (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardAdressIatEntries) - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - } - - CfgConfig->NumberOfGuardLongJumpEntries = 0; - CfgConfig->GuardLongJumpTable = 0; - - if ( - config64->Size >= (ULONG)FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardLongJumpTargetTable) + - sizeof(config64->GuardLongJumpTargetTable) + - sizeof(config64->GuardLongJumpTargetCount) - ) - { - CfgConfig->NumberOfGuardLongJumpEntries = config64->GuardLongJumpTargetCount; - CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( - MappedImage, - (ULONG)(config64->GuardLongJumpTargetTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), - NULL - ); - - if (CfgConfig->GuardLongJumpTable && CfgConfig->NumberOfGuardLongJumpEntries) - { - __try - { - PhpMappedImageProbe( - MappedImage, - CfgConfig->GuardLongJumpTable, - (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardLongJumpEntries) - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - } - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhGetMappedImageCfgEntry( - _In_ PPH_MAPPED_IMAGE_CFG CfgConfig, - _In_ ULONGLONG Index, - _In_ CFG_ENTRY_TYPE Type, - _Out_ PIMAGE_CFG_ENTRY Entry - ) -{ - PULONGLONG guardTable; - ULONGLONG numberofGuardEntries; - PIMAGE_CFG_ENTRY cfgMappedEntry; - - switch (Type) - { - case ControlFlowGuardFunction: - { - guardTable = CfgConfig->GuardFunctionTable; - numberofGuardEntries = CfgConfig->NumberOfGuardFunctionEntries; - } - break; - case ControlFlowGuardtakenIatEntry: - { - guardTable = CfgConfig->GuardAdressIatTable; - numberofGuardEntries = CfgConfig->NumberOfGuardAdressIatEntries; - } - break; - case ControlFlowGuardLongJump: - { - guardTable = CfgConfig->GuardLongJumpTable; - numberofGuardEntries = CfgConfig->NumberOfGuardLongJumpEntries; - } - break; - default: - return STATUS_INVALID_PARAMETER_3; - } - - if (!guardTable || Index >= numberofGuardEntries) - return STATUS_PROCEDURE_NOT_FOUND; - - cfgMappedEntry = (PIMAGE_CFG_ENTRY)PTR_ADD_OFFSET(guardTable, Index * CfgConfig->EntrySize); - - Entry->Rva = cfgMappedEntry->Rva; - - // Optional header after the rva entry - if (CfgConfig->EntrySize > sizeof(FIELD_OFFSET(IMAGE_CFG_ENTRY, Rva))) - { - Entry->SuppressedCall = cfgMappedEntry->SuppressedCall; - Entry->Reserved = cfgMappedEntry->Reserved; - } - - return STATUS_SUCCESS; -} \ No newline at end of file +/* + * Process Hacker - + * mapped image + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This file contains functions to load and retrieve information for image files (exe, dll). The + * file format for image files is explained in the PE/COFF specification located at: + * + * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + */ + +#include +#include + +VOID PhpMappedImageProbe( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ PVOID Address, + _In_ SIZE_T Length + ); + +ULONG PhpLookupMappedImageExportName( + _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, + _In_ PSTR Name + ); + +NTSTATUS PhInitializeMappedImage( + _Out_ PPH_MAPPED_IMAGE MappedImage, + _In_ PVOID ViewBase, + _In_ SIZE_T Size + ) +{ + PIMAGE_DOS_HEADER dosHeader; + ULONG ntHeadersOffset; + + MappedImage->ViewBase = ViewBase; + MappedImage->Size = Size; + + dosHeader = (PIMAGE_DOS_HEADER)ViewBase; + + __try + { + PhpMappedImageProbe(MappedImage, dosHeader, sizeof(IMAGE_DOS_HEADER)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + // Check the initial MZ. + + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return STATUS_INVALID_IMAGE_NOT_MZ; + + // Get a pointer to the NT headers and probe it. + + ntHeadersOffset = (ULONG)dosHeader->e_lfanew; + + if (ntHeadersOffset == 0) + return STATUS_INVALID_IMAGE_FORMAT; + if (ntHeadersOffset >= 0x10000000 || ntHeadersOffset >= Size) + return STATUS_INVALID_IMAGE_FORMAT; + + MappedImage->NtHeaders = (PIMAGE_NT_HEADERS)PTR_ADD_OFFSET(ViewBase, ntHeadersOffset); + + __try + { + PhpMappedImageProbe( + MappedImage, + MappedImage->NtHeaders, + UFIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + ); + PhpMappedImageProbe( + MappedImage, + MappedImage->NtHeaders, + UFIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + MappedImage->NtHeaders->FileHeader.SizeOfOptionalHeader + + MappedImage->NtHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + // Check the signature and verify the magic. + + if (MappedImage->NtHeaders->Signature != IMAGE_NT_SIGNATURE) + return STATUS_INVALID_IMAGE_FORMAT; + + MappedImage->Magic = MappedImage->NtHeaders->OptionalHeader.Magic; + + if ( + MappedImage->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && + MappedImage->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC + ) + return STATUS_INVALID_IMAGE_FORMAT; + + // Get a pointer to the first section. + + MappedImage->NumberOfSections = MappedImage->NtHeaders->FileHeader.NumberOfSections; + MappedImage->Sections = IMAGE_FIRST_SECTION(MappedImage->NtHeaders); + + return STATUS_SUCCESS; +} + +NTSTATUS PhLoadMappedImage( + _In_opt_ PWSTR FileName, + _In_opt_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _Out_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PVOID viewBase; + SIZE_T size; + + status = PhMapViewOfEntireFile( + FileName, + FileHandle, + ReadOnly, + &viewBase, + &size + ); + + if (NT_SUCCESS(status)) + { + status = PhInitializeMappedImage( + MappedImage, + viewBase, + size + ); + + if (!NT_SUCCESS(status)) + { + PhUnloadMappedImage(MappedImage); + } + } + + return status; +} + +NTSTATUS PhLoadMappedImageEx( + _In_opt_ PWSTR FileName, + _In_opt_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _Out_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PVOID viewBase; + SIZE_T size; + + status = PhMapViewOfEntireFile( + FileName, + FileHandle, + ReadOnly, + &viewBase, + &size + ); + + if (NT_SUCCESS(status)) + { + MappedImage->Signature = *(PUSHORT)viewBase; + MappedImage->ViewBase = viewBase; + MappedImage->Size = size; + + switch (MappedImage->Signature) + { + case IMAGE_DOS_SIGNATURE: + { + status = PhInitializeMappedImage( + MappedImage, + viewBase, + size + ); + } + break; + case IMAGE_ELF_SIGNATURE: + { + status = PhInitializeMappedWslImage( + MappedImage, + viewBase, + size + ); + } + break; + default: + status = STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT; + break; + } + + if (!NT_SUCCESS(status)) + { + PhUnloadMappedImage(MappedImage); + } + } + + return status; +} + +NTSTATUS PhUnloadMappedImage( + _Inout_ PPH_MAPPED_IMAGE MappedImage + ) +{ + return NtUnmapViewOfSection( + NtCurrentProcess(), + MappedImage->ViewBase + ); +} + +NTSTATUS PhMapViewOfEntireFile( + _In_opt_ PWSTR FileName, + _In_opt_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _Out_ PVOID *ViewBase, + _Out_ PSIZE_T Size + ) +{ + NTSTATUS status; + BOOLEAN openedFile = FALSE; + LARGE_INTEGER size; + HANDLE sectionHandle = NULL; + SIZE_T viewSize; + PVOID viewBase; + + if (!FileName && !FileHandle) + return STATUS_INVALID_PARAMETER_MIX; + + // Open the file if we weren't supplied a file handle. + if (!FileHandle) + { + status = PhCreateFileWin32( + &FileHandle, + FileName, + ((FILE_READ_ATTRIBUTES | FILE_READ_DATA) | + (!ReadOnly ? (FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA) : 0)) | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return status; + + openedFile = TRUE; + } + + // Get the file size and create the section. + + status = PhGetFileSize(FileHandle, &size); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtCreateSection( + §ionHandle, + SECTION_ALL_ACCESS, + NULL, + &size, + ReadOnly ? PAGE_READONLY : PAGE_READWRITE, + SEC_COMMIT, + FileHandle + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + // Map the section. + + viewSize = (SIZE_T)size.QuadPart; + viewBase = NULL; + + status = NtMapViewOfSection( + sectionHandle, + NtCurrentProcess(), + &viewBase, + 0, + 0, + NULL, + &viewSize, + ViewShare, + 0, + ReadOnly ? PAGE_READONLY : PAGE_READWRITE + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + *ViewBase = viewBase; + *Size = (SIZE_T)size.QuadPart; + +CleanupExit: + if (sectionHandle) + NtClose(sectionHandle); + if (openedFile) + NtClose(FileHandle); + + return status; +} + +VOID PhpMappedImageProbe( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ PVOID Address, + _In_ SIZE_T Length + ) +{ + PhProbeAddress(Address, Length, MappedImage->ViewBase, MappedImage->Size, 1); +} + +PIMAGE_SECTION_HEADER PhMappedImageRvaToSection( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ ULONG Rva + ) +{ + ULONG i; + + for (i = 0; i < MappedImage->NumberOfSections; i++) + { + if ( + (Rva >= MappedImage->Sections[i].VirtualAddress) && + (Rva < MappedImage->Sections[i].VirtualAddress + MappedImage->Sections[i].SizeOfRawData) + ) + { + return &MappedImage->Sections[i]; + } + } + + return NULL; +} + +PVOID PhMappedImageRvaToVa( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ ULONG Rva, + _Out_opt_ PIMAGE_SECTION_HEADER *Section + ) +{ + PIMAGE_SECTION_HEADER section; + + section = PhMappedImageRvaToSection(MappedImage, Rva); + + if (!section) + return NULL; + + if (Section) + *Section = section; + + return PTR_ADD_OFFSET( + MappedImage->ViewBase, + (Rva - section->VirtualAddress) + + section->PointerToRawData + ); +} + +PVOID PhMappedImageVaToVa( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ ULONG Va, + _Out_opt_ PIMAGE_SECTION_HEADER *Section + ) +{ + ULONG rva; + PIMAGE_SECTION_HEADER section; + + if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + rva = PtrToUlong(PTR_SUB_OFFSET(Va, MappedImage->NtHeaders32->OptionalHeader.ImageBase)); + } + else if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + rva = PtrToUlong(PTR_SUB_OFFSET(Va, MappedImage->NtHeaders->OptionalHeader.ImageBase)); + } + else + { + return NULL; + } + + section = PhMappedImageRvaToSection(MappedImage, rva); + + if (!section) + return NULL; + + if (Section) + *Section = section; + + return PTR_ADD_OFFSET(MappedImage->ViewBase, PTR_ADD_OFFSET( + PTR_SUB_OFFSET(rva, section->VirtualAddress), + section->PointerToRawData + )); +} + +BOOLEAN PhGetMappedImageSectionName( + _In_ PIMAGE_SECTION_HEADER Section, + _Out_writes_opt_z_(Count) PWSTR Buffer, + _In_ ULONG Count, + _Out_opt_ PULONG ReturnCount + ) +{ + BOOLEAN result; + SIZE_T returnCount; + + result = PhCopyStringZFromBytes( + Section->Name, + IMAGE_SIZEOF_SHORT_NAME, + Buffer, + Count, + &returnCount + ); + + if (ReturnCount) + *ReturnCount = (ULONG)returnCount; + + return result; +} + +NTSTATUS PhGetMappedImageDataEntry( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ ULONG Index, + _Out_ PIMAGE_DATA_DIRECTORY *Entry + ) +{ + if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PIMAGE_OPTIONAL_HEADER32 optionalHeader; + + optionalHeader = (PIMAGE_OPTIONAL_HEADER32)&MappedImage->NtHeaders->OptionalHeader; + + if (Index >= optionalHeader->NumberOfRvaAndSizes) + return STATUS_INVALID_PARAMETER_2; + + *Entry = &optionalHeader->DataDirectory[Index]; + } + else if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + PIMAGE_OPTIONAL_HEADER64 optionalHeader; + + optionalHeader = (PIMAGE_OPTIONAL_HEADER64)&MappedImage->NtHeaders->OptionalHeader; + + if (Index >= optionalHeader->NumberOfRvaAndSizes) + return STATUS_INVALID_PARAMETER_2; + + *Entry = &optionalHeader->DataDirectory[Index]; + } + else + { + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +FORCEINLINE NTSTATUS PhpGetMappedImageLoadConfig( + _In_ PPH_MAPPED_IMAGE MappedImage, + _In_ USHORT Magic, + _In_ ULONG ProbeLength, + _Out_ PVOID *LoadConfig + ) +{ + NTSTATUS status; + PIMAGE_DATA_DIRECTORY entry; + PVOID loadConfig; + + if (MappedImage->Magic != Magic) + return STATUS_INVALID_PARAMETER; + + status = PhGetMappedImageDataEntry(MappedImage, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &entry); + + if (!NT_SUCCESS(status)) + return status; + + loadConfig = PhMappedImageRvaToVa(MappedImage, entry->VirtualAddress, NULL); + + if (!loadConfig) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe(MappedImage, loadConfig, ProbeLength); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + *LoadConfig = loadConfig; + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageLoadConfig32( + _In_ PPH_MAPPED_IMAGE MappedImage, + _Out_ PIMAGE_LOAD_CONFIG_DIRECTORY32 *LoadConfig + ) +{ + return PhpGetMappedImageLoadConfig( + MappedImage, + IMAGE_NT_OPTIONAL_HDR32_MAGIC, + sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32), + LoadConfig + ); +} + +NTSTATUS PhGetMappedImageLoadConfig64( + _In_ PPH_MAPPED_IMAGE MappedImage, + _Out_ PIMAGE_LOAD_CONFIG_DIRECTORY64 *LoadConfig + ) +{ + return PhpGetMappedImageLoadConfig( + MappedImage, + IMAGE_NT_OPTIONAL_HDR64_MAGIC, + sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64), + LoadConfig + ); +} + +NTSTATUS PhLoadRemoteMappedImage( + _In_ HANDLE ProcessHandle, + _In_ PVOID ViewBase, + _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage + ) +{ + return PhLoadRemoteMappedImageEx(ProcessHandle, ViewBase, NtReadVirtualMemory, RemoteMappedImage); +} + +NTSTATUS PhLoadRemoteMappedImageEx( + _In_ HANDLE ProcessHandle, + _In_ PVOID ViewBase, + _In_ PPH_READ_VIRTUAL_MEMORY_CALLBACK ReadVirtualMemoryCallback, + _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage + ) +{ + NTSTATUS status; + IMAGE_DOS_HEADER dosHeader; + ULONG ntHeadersOffset; + IMAGE_NT_HEADERS32 ntHeaders; + ULONG ntHeadersSize; + + RemoteMappedImage->ViewBase = ViewBase; + + status = ReadVirtualMemoryCallback( + ProcessHandle, + ViewBase, + &dosHeader, + sizeof(IMAGE_DOS_HEADER), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Check the initial MZ. + + if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) + return STATUS_INVALID_IMAGE_NOT_MZ; + + // Get a pointer to the NT headers and read it in for some basic information. + + ntHeadersOffset = (ULONG)dosHeader.e_lfanew; + + if (ntHeadersOffset == 0 || ntHeadersOffset >= 0x10000000) + return STATUS_INVALID_IMAGE_FORMAT; + + status = ReadVirtualMemoryCallback( + ProcessHandle, + PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), + &ntHeaders, + sizeof(IMAGE_NT_HEADERS32), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Check the signature and verify the magic. + + if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) + return STATUS_INVALID_IMAGE_FORMAT; + + RemoteMappedImage->Magic = ntHeaders.OptionalHeader.Magic; + + if ( + RemoteMappedImage->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && + RemoteMappedImage->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC + ) + return STATUS_INVALID_IMAGE_FORMAT; + + // Get the real size and read in the whole thing. + + RemoteMappedImage->NumberOfSections = ntHeaders.FileHeader.NumberOfSections; + ntHeadersSize = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + ntHeaders.FileHeader.SizeOfOptionalHeader + + RemoteMappedImage->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + + if (ntHeadersSize > 1024 * 1024) // 1 MB + return STATUS_INVALID_IMAGE_FORMAT; + + RemoteMappedImage->NtHeaders = PhAllocate(ntHeadersSize); + + status = ReadVirtualMemoryCallback( + ProcessHandle, + PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), + RemoteMappedImage->NtHeaders, + ntHeadersSize, + NULL + ); + + if (!NT_SUCCESS(status)) + { + PhFree(RemoteMappedImage->NtHeaders); + return status; + } + + RemoteMappedImage->Sections = IMAGE_FIRST_SECTION(RemoteMappedImage->NtHeaders); + + return STATUS_SUCCESS; +} + +NTSTATUS PhUnloadRemoteMappedImage( + _Inout_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage + ) +{ + PhFree(RemoteMappedImage->NtHeaders); + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageExports( + _Out_ PPH_MAPPED_IMAGE_EXPORTS Exports, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_EXPORT_DIRECTORY exportDirectory; + + Exports->MappedImage = MappedImage; + + // Get a pointer to the export directory. + + status = PhGetMappedImageDataEntry( + MappedImage, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &Exports->DataDirectory + ); + + if (!NT_SUCCESS(status)) + return status; + + exportDirectory = PhMappedImageRvaToVa( + MappedImage, + Exports->DataDirectory->VirtualAddress, + NULL + ); + + if (!exportDirectory) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe(MappedImage, exportDirectory, sizeof(IMAGE_EXPORT_DIRECTORY)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + Exports->ExportDirectory = exportDirectory; + Exports->NumberOfEntries = exportDirectory->NumberOfFunctions; + + // Get pointers to the various tables and probe them. + + Exports->AddressTable = (PULONG)PhMappedImageRvaToVa( + MappedImage, + exportDirectory->AddressOfFunctions, + NULL + ); + Exports->NamePointerTable = (PULONG)PhMappedImageRvaToVa( + MappedImage, + exportDirectory->AddressOfNames, + NULL + ); + Exports->OrdinalTable = (PUSHORT)PhMappedImageRvaToVa( + MappedImage, + exportDirectory->AddressOfNameOrdinals, + NULL + ); + + if ( + !Exports->AddressTable || + !Exports->NamePointerTable || + !Exports->OrdinalTable + ) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe( + MappedImage, + Exports->AddressTable, + exportDirectory->NumberOfFunctions * sizeof(ULONG) + ); + PhpMappedImageProbe( + MappedImage, + Exports->NamePointerTable, + exportDirectory->NumberOfNames * sizeof(ULONG) + ); + PhpMappedImageProbe( + MappedImage, + Exports->OrdinalTable, // ordinal list for named exports + exportDirectory->NumberOfNames * sizeof(USHORT) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + // The ordinal and name tables are parallel. + // Getting an index into the name table (e.g. by doing a binary + // search) and indexing into the ordinal table will produce the + // ordinal for that name, *unbiased* (unlike in the specification). + // The unbiased ordinal is an index into the address table. + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageExportEntry( + _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, + _In_ ULONG Index, + _Out_ PPH_MAPPED_IMAGE_EXPORT_ENTRY Entry + ) +{ + ULONG nameIndex = 0; + BOOLEAN exportByName = FALSE; + PSTR name; + + if (Index >= Exports->ExportDirectory->NumberOfFunctions) + return STATUS_PROCEDURE_NOT_FOUND; + + Entry->Ordinal = (USHORT)Index + (USHORT)Exports->ExportDirectory->Base; + + // look into named exports ordinal list. + for (nameIndex = 0; nameIndex < Exports->ExportDirectory->NumberOfNames; nameIndex++) + { + if (Index == Exports->OrdinalTable[nameIndex]) + { + exportByName = TRUE; + break; + } + } + + if (exportByName) + { + name = PhMappedImageRvaToVa( + Exports->MappedImage, + Exports->NamePointerTable[nameIndex], + NULL + ); + + if (!name) + return STATUS_INVALID_PARAMETER; + + // TODO: Probe the name. + + Entry->Name = name; + Entry->Hint = nameIndex; + } + else + { + Entry->Name = NULL; + Entry->Hint = 0; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageExportFunction( + _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, + _In_opt_ PSTR Name, + _In_opt_ USHORT Ordinal, + _Out_ PPH_MAPPED_IMAGE_EXPORT_FUNCTION Function + ) +{ + ULONG rva; + + if (Name) + { + ULONG index; + + index = PhpLookupMappedImageExportName(Exports, Name); + + if (index == ULONG_MAX) + return STATUS_PROCEDURE_NOT_FOUND; + + Ordinal = Exports->OrdinalTable[index] + (USHORT)Exports->ExportDirectory->Base; + } + + Ordinal -= (USHORT)Exports->ExportDirectory->Base; + + if (Ordinal >= Exports->ExportDirectory->NumberOfFunctions) + return STATUS_PROCEDURE_NOT_FOUND; + + rva = Exports->AddressTable[Ordinal]; + + if ( + (rva >= Exports->DataDirectory->VirtualAddress) && + (rva < Exports->DataDirectory->VirtualAddress + Exports->DataDirectory->Size) + ) + { + // This is a forwarder RVA. + + Function->ForwardedName = PhMappedImageRvaToVa( + Exports->MappedImage, + rva, + NULL + ); + + if (!Function->ForwardedName) + return STATUS_INVALID_PARAMETER; + + // TODO: Probe the name. + + Function->Function = NULL; + } + else + { + Function->Function = UlongToPtr(rva); + Function->ForwardedName = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageExportFunctionRemote( + _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, + _In_opt_ PSTR Name, + _In_opt_ USHORT Ordinal, + _In_ PVOID RemoteBase, + _Out_ PVOID *Function + ) +{ + ULONG rva; + + if (Name) + { + ULONG index; + + index = PhpLookupMappedImageExportName(Exports, Name); + + if (index == ULONG_MAX) + return STATUS_PROCEDURE_NOT_FOUND; + + Ordinal = Exports->OrdinalTable[index] + (USHORT)Exports->ExportDirectory->Base; + } + + Ordinal -= (USHORT)Exports->ExportDirectory->Base; + + if (Ordinal >= Exports->ExportDirectory->NumberOfFunctions) + return STATUS_PROCEDURE_NOT_FOUND; + + rva = Exports->AddressTable[Ordinal]; + + if ( + (rva >= Exports->DataDirectory->VirtualAddress) && + (rva < Exports->DataDirectory->VirtualAddress + Exports->DataDirectory->Size) + ) + { + // This is a forwarder RVA. Not supported for remote lookup. + return STATUS_NOT_SUPPORTED; + } + else + { + *Function = PTR_ADD_OFFSET(RemoteBase, rva); + } + + return STATUS_SUCCESS; +} + +ULONG PhpLookupMappedImageExportName( + _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, + _In_ PSTR Name + ) +{ + LONG low; + LONG high; + LONG i; + + if (Exports->ExportDirectory->NumberOfNames == 0) + return ULONG_MAX; + + low = 0; + high = Exports->ExportDirectory->NumberOfNames - 1; + + do + { + PSTR name; + INT comparison; + + i = (low + high) / 2; + + name = PhMappedImageRvaToVa( + Exports->MappedImage, + Exports->NamePointerTable[i], + NULL + ); + + if (!name) + return ULONG_MAX; + + // TODO: Probe the name. + + comparison = strcmp(Name, name); + + if (comparison == 0) + return i; + else if (comparison < 0) + high = i - 1; + else + low = i + 1; + } while (low <= high); + + return ULONG_MAX; +} + +NTSTATUS PhGetMappedImageImports( + _Out_ PPH_MAPPED_IMAGE_IMPORTS Imports, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_DATA_DIRECTORY dataDirectory; + PIMAGE_IMPORT_DESCRIPTOR descriptor; + ULONG i; + + Imports->MappedImage = MappedImage; + Imports->Flags = 0; + + status = PhGetMappedImageDataEntry( + MappedImage, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &dataDirectory + ); + + if (!NT_SUCCESS(status)) + return status; + + descriptor = PhMappedImageRvaToVa( + MappedImage, + dataDirectory->VirtualAddress, + NULL + ); + + if (!descriptor) + return STATUS_INVALID_PARAMETER; + + Imports->DescriptorTable = descriptor; + + // Do a scan to determine how many import descriptors there are. + + i = 0; + + __try + { + while (TRUE) + { + PhpMappedImageProbe(MappedImage, descriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR)); + + if (descriptor->OriginalFirstThunk == 0 && descriptor->FirstThunk == 0) + break; + + descriptor++; + i++; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + Imports->NumberOfDlls = i; + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageImportDll( + _In_ PPH_MAPPED_IMAGE_IMPORTS Imports, + _In_ ULONG Index, + _Out_ PPH_MAPPED_IMAGE_IMPORT_DLL ImportDll + ) +{ + ULONG i; + + if (Index >= Imports->NumberOfDlls) + return STATUS_INVALID_PARAMETER_2; + + ImportDll->MappedImage = Imports->MappedImage; + ImportDll->Flags = Imports->Flags; + + if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS)) + { + ImportDll->Descriptor = &Imports->DescriptorTable[Index]; + + ImportDll->Name = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ImportDll->Descriptor->Name, + NULL + ); + + if (!ImportDll->Name) + return STATUS_INVALID_PARAMETER; + + // TODO: Probe the name. + + if (ImportDll->Descriptor->OriginalFirstThunk) + { + ImportDll->LookupTable = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ImportDll->Descriptor->OriginalFirstThunk, + NULL + ); + } + else + { + ImportDll->LookupTable = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ImportDll->Descriptor->FirstThunk, + NULL + ); + } + } + else + { + ImportDll->DelayDescriptor = &Imports->DelayDescriptorTable[Index]; + + // Backwards compatible support for legacy V1 delay imports. (dmex) + if (ImportDll->DelayDescriptor->Attributes.RvaBased == 0) + { + ImportDll->Flags |= PH_MAPPED_IMAGE_DELAY_IMPORTS_V1; + } + + if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS_V1)) + { + ImportDll->Name = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ImportDll->DelayDescriptor->DllNameRVA, + NULL + ); + } + else + { + ImportDll->Name = PhMappedImageVaToVa( + ImportDll->MappedImage, + ImportDll->DelayDescriptor->DllNameRVA, + NULL + ); + } + + if (!ImportDll->Name) + return STATUS_INVALID_PARAMETER; + + // TODO: Probe the name. + + if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS_V1)) + { + ImportDll->LookupTable = PhMappedImageRvaToVa( + ImportDll->MappedImage, + ImportDll->DelayDescriptor->ImportNameTableRVA, + NULL + ); + } + else + { + ImportDll->LookupTable = PhMappedImageVaToVa( + ImportDll->MappedImage, + ImportDll->DelayDescriptor->ImportNameTableRVA, + NULL + ); + } + } + + if (!ImportDll->LookupTable) + return STATUS_INVALID_PARAMETER; + + // Do a scan to determine how many entries there are. + + i = 0; + + if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PIMAGE_THUNK_DATA32 entry; + + entry = (PIMAGE_THUNK_DATA32)ImportDll->LookupTable; + + __try + { + while (TRUE) + { + PhpMappedImageProbe(ImportDll->MappedImage, entry, sizeof(IMAGE_THUNK_DATA32)); + + if (entry->u1.AddressOfData == 0) + break; + + entry++; + i++; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + PIMAGE_THUNK_DATA64 entry; + + entry = (PIMAGE_THUNK_DATA64)ImportDll->LookupTable; + + __try + { + while (TRUE) + { + PhpMappedImageProbe(ImportDll->MappedImage, entry, sizeof(IMAGE_THUNK_DATA64)); + + if (entry->u1.AddressOfData == 0) + break; + + entry++; + i++; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + else + { + return STATUS_INVALID_PARAMETER; + } + + ImportDll->NumberOfEntries = i; + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageImportEntry( + _In_ PPH_MAPPED_IMAGE_IMPORT_DLL ImportDll, + _In_ ULONG Index, + _Out_ PPH_MAPPED_IMAGE_IMPORT_ENTRY Entry + ) +{ + PIMAGE_IMPORT_BY_NAME importByName; + + if (Index >= ImportDll->NumberOfEntries) + return STATUS_INVALID_PARAMETER_2; + + if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + IMAGE_THUNK_DATA32 entry; + + entry = ((PIMAGE_THUNK_DATA32)ImportDll->LookupTable)[Index]; + + // Is this entry using an ordinal? + if (IMAGE_SNAP_BY_ORDINAL32(entry.u1.Ordinal)) + { + Entry->Name = NULL; + Entry->Ordinal = IMAGE_ORDINAL32(entry.u1.Ordinal); + + return STATUS_SUCCESS; + } + else + { + if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS_V1)) + { + importByName = PhMappedImageRvaToVa( + ImportDll->MappedImage, + entry.u1.AddressOfData, + NULL + ); + } + else + { + importByName = PhMappedImageVaToVa( + ImportDll->MappedImage, + entry.u1.AddressOfData, + NULL + ); + } + } + } + else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + IMAGE_THUNK_DATA64 entry; + + entry = ((PIMAGE_THUNK_DATA64)ImportDll->LookupTable)[Index]; + + // Is this entry using an ordinal? + if (IMAGE_SNAP_BY_ORDINAL64(entry.u1.Ordinal)) + { + Entry->Name = NULL; + Entry->Ordinal = IMAGE_ORDINAL64(entry.u1.Ordinal); + + return STATUS_SUCCESS; + } + else + { + if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS_V1)) + { + importByName = PhMappedImageRvaToVa( + ImportDll->MappedImage, + (ULONG)entry.u1.AddressOfData, + NULL + ); + } + else + { + importByName = PhMappedImageVaToVa( + ImportDll->MappedImage, + (ULONG)entry.u1.AddressOfData, + NULL + ); + } + } + } + else + { + return STATUS_INVALID_PARAMETER; + } + + if (!importByName) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe(ImportDll->MappedImage, importByName, sizeof(IMAGE_IMPORT_BY_NAME)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + Entry->Name = (PSTR)importByName->Name; + Entry->NameHint = importByName->Hint; + + // TODO: Probe the name. + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageDelayImports( + _Out_ PPH_MAPPED_IMAGE_IMPORTS Imports, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_DATA_DIRECTORY dataDirectory; + PIMAGE_DELAYLOAD_DESCRIPTOR descriptor; + ULONG i; + + Imports->MappedImage = MappedImage; + Imports->Flags = PH_MAPPED_IMAGE_DELAY_IMPORTS; + + status = PhGetMappedImageDataEntry( + MappedImage, + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, + &dataDirectory + ); + + if (!NT_SUCCESS(status)) + return status; + + descriptor = PhMappedImageRvaToVa( + MappedImage, + dataDirectory->VirtualAddress, + NULL + ); + + if (!descriptor) + return STATUS_INVALID_PARAMETER; + + Imports->DelayDescriptorTable = descriptor; + + // Do a scan to determine how many import descriptors there are. + + i = 0; + + __try + { + while (TRUE) + { + PhpMappedImageProbe(MappedImage, descriptor, sizeof(PIMAGE_DELAYLOAD_DESCRIPTOR)); + + if (descriptor->ImportAddressTableRVA == 0 && descriptor->ImportNameTableRVA == 0) + break; + + descriptor++; + i++; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + Imports->NumberOfDlls = i; + + return STATUS_SUCCESS; +} + +USHORT PhCheckSum( + _In_ ULONG Sum, + _In_reads_(Count) PUSHORT Buffer, + _In_ ULONG Count + ) +{ + while (Count--) + { + Sum += *Buffer++; + Sum = (Sum >> 16) + (Sum & 0xffff); + } + + Sum = (Sum >> 16) + Sum; + + return (USHORT)Sum; +} + +ULONG PhCheckSumMappedImage( + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + ULONG checkSum; + USHORT partialSum; + PUSHORT adjust; + + partialSum = PhCheckSum(0, (PUSHORT)MappedImage->ViewBase, (ULONG)(MappedImage->Size + 1) / 2); + + // This is actually the same for 32-bit and 64-bit executables. + adjust = (PUSHORT)&MappedImage->NtHeaders->OptionalHeader.CheckSum; + + // Subtract the existing check sum (with carry). + partialSum -= partialSum < adjust[0]; + partialSum -= adjust[0]; + partialSum -= partialSum < adjust[1]; + partialSum -= adjust[1]; + + checkSum = partialSum + (ULONG)MappedImage->Size; + + return checkSum; +} + +NTSTATUS PhGetMappedImageCfg64( + _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_LOAD_CONFIG_DIRECTORY64 config64; + + if (!NT_SUCCESS(status = PhGetMappedImageLoadConfig64(MappedImage, &config64))) + return status; + + // Not every load configuration defines CFG characteristics + if (!RTL_CONTAINS_FIELD(config64, config64->Size, GuardFlags)) + return STATUS_INVALID_VIEW_SIZE; + + CfgConfig->MappedImage = MappedImage; + CfgConfig->EntrySize = sizeof(FIELD_OFFSET(IMAGE_CFG_ENTRY, Rva)) + + (ULONG)((config64->GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT); + CfgConfig->CfgInstrumented = !!(config64->GuardFlags & IMAGE_GUARD_CF_INSTRUMENTED); + CfgConfig->WriteIntegrityChecks = !!(config64->GuardFlags & IMAGE_GUARD_CFW_INSTRUMENTED); + CfgConfig->CfgFunctionTablePresent = !!(config64->GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT); + CfgConfig->SecurityCookieUnused = !!(config64->GuardFlags & IMAGE_GUARD_SECURITY_COOKIE_UNUSED); + CfgConfig->ProtectDelayLoadedIat = !!(config64->GuardFlags & IMAGE_GUARD_PROTECT_DELAYLOAD_IAT); + CfgConfig->DelayLoadInDidatSection = !!(config64->GuardFlags & IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION); + CfgConfig->EnableExportSuppression = !!(config64->GuardFlags & IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION); + CfgConfig->HasExportSuppressionInfos = !!(config64->GuardFlags & IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT); + CfgConfig->CfgLongJumpTablePresent = !!(config64->GuardFlags & IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT); + + CfgConfig->NumberOfGuardFunctionEntries = config64->GuardCFFunctionCount; + CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( + MappedImage, + (ULONG)(config64->GuardCFFunctionTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + NULL + ); + + if (CfgConfig->GuardFunctionTable && CfgConfig->NumberOfGuardFunctionEntries) + { + __try + { + PhpMappedImageProbe( + MappedImage, + CfgConfig->GuardFunctionTable, + (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardFunctionEntries) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + CfgConfig->NumberOfGuardAdressIatEntries = 0; + CfgConfig->GuardAdressIatTable = 0; + + if (RTL_CONTAINS_FIELD(config64, config64->Size, GuardAddressTakenIatEntryTable)) + { + CfgConfig->NumberOfGuardAdressIatEntries = config64->GuardAddressTakenIatEntryCount; + CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( + MappedImage, + (ULONG)(config64->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + NULL + ); + + if (CfgConfig->GuardAdressIatTable && CfgConfig->NumberOfGuardAdressIatEntries) + { + __try + { + PhpMappedImageProbe( + MappedImage, + CfgConfig->GuardAdressIatTable, + (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardAdressIatEntries) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + } + + CfgConfig->NumberOfGuardLongJumpEntries = 0; + CfgConfig->GuardLongJumpTable = 0; + + if (RTL_CONTAINS_FIELD(config64, config64->Size, GuardLongJumpTargetTable)) + { + CfgConfig->NumberOfGuardLongJumpEntries = config64->GuardLongJumpTargetCount; + CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( + MappedImage, + (ULONG)(config64->GuardLongJumpTargetTable - MappedImage->NtHeaders->OptionalHeader.ImageBase), + NULL + ); + + if (CfgConfig->GuardLongJumpTable && CfgConfig->NumberOfGuardLongJumpEntries) + { + __try + { + PhpMappedImageProbe( + MappedImage, + CfgConfig->GuardLongJumpTable, + (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardLongJumpEntries) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageCfg32( + _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_LOAD_CONFIG_DIRECTORY32 config32; + + if (!NT_SUCCESS(status = PhGetMappedImageLoadConfig32(MappedImage, &config32))) + return status; + + // Not every load configuration defines CFG characteristics + if (!RTL_CONTAINS_FIELD(config32, config32->Size, GuardFlags)) + return STATUS_INVALID_VIEW_SIZE; + + CfgConfig->MappedImage = MappedImage; + CfgConfig->EntrySize = sizeof(FIELD_OFFSET(IMAGE_CFG_ENTRY, Rva)) + + (ULONG)((config32->GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT); + CfgConfig->CfgInstrumented = !!(config32->GuardFlags & IMAGE_GUARD_CF_INSTRUMENTED); + CfgConfig->WriteIntegrityChecks = !!(config32->GuardFlags & IMAGE_GUARD_CFW_INSTRUMENTED); + CfgConfig->CfgFunctionTablePresent = !!(config32->GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT); + CfgConfig->SecurityCookieUnused = !!(config32->GuardFlags & IMAGE_GUARD_SECURITY_COOKIE_UNUSED); + CfgConfig->ProtectDelayLoadedIat = !!(config32->GuardFlags & IMAGE_GUARD_PROTECT_DELAYLOAD_IAT); + CfgConfig->DelayLoadInDidatSection = !!(config32->GuardFlags & IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION); + CfgConfig->EnableExportSuppression = !!(config32->GuardFlags & IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION); + CfgConfig->HasExportSuppressionInfos = !!(config32->GuardFlags & IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT); + CfgConfig->CfgLongJumpTablePresent = !!(config32->GuardFlags & IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT); + + CfgConfig->NumberOfGuardFunctionEntries = config32->GuardCFFunctionCount; + CfgConfig->GuardFunctionTable = PhMappedImageRvaToVa( + MappedImage, + config32->GuardCFFunctionTable - MappedImage->NtHeaders32->OptionalHeader.ImageBase, + NULL + ); + + if (CfgConfig->GuardFunctionTable && CfgConfig->NumberOfGuardFunctionEntries) + { + __try + { + PhpMappedImageProbe( + MappedImage, + CfgConfig->GuardFunctionTable, + (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardFunctionEntries) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + + CfgConfig->NumberOfGuardAdressIatEntries = 0; + CfgConfig->GuardAdressIatTable = 0; + + if (RTL_CONTAINS_FIELD(config32, config32->Size, GuardAddressTakenIatEntryTable)) + { + CfgConfig->NumberOfGuardAdressIatEntries = config32->GuardAddressTakenIatEntryCount; + CfgConfig->GuardAdressIatTable = PhMappedImageRvaToVa( + MappedImage, + config32->GuardAddressTakenIatEntryTable - MappedImage->NtHeaders32->OptionalHeader.ImageBase, + NULL + ); + + if (CfgConfig->GuardAdressIatTable && CfgConfig->NumberOfGuardAdressIatEntries) + { + __try + { + PhpMappedImageProbe( + MappedImage, + CfgConfig->GuardAdressIatTable, + (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardAdressIatEntries) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + } + + CfgConfig->NumberOfGuardLongJumpEntries = 0; + CfgConfig->GuardLongJumpTable = 0; + + if (RTL_CONTAINS_FIELD(config32, config32->Size, GuardLongJumpTargetTable)) + { + CfgConfig->NumberOfGuardLongJumpEntries = config32->GuardLongJumpTargetCount; + CfgConfig->GuardLongJumpTable = PhMappedImageRvaToVa( + MappedImage, + config32->GuardLongJumpTargetTable - MappedImage->NtHeaders32->OptionalHeader.ImageBase, + NULL + ); + + if (CfgConfig->GuardLongJumpTable && CfgConfig->NumberOfGuardLongJumpEntries) + { + __try + { + PhpMappedImageProbe( + MappedImage, + CfgConfig->GuardLongJumpTable, + (SIZE_T)(CfgConfig->EntrySize * CfgConfig->NumberOfGuardLongJumpEntries) + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageCfg( + _Out_ PPH_MAPPED_IMAGE_CFG CfgConfig, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + return PhGetMappedImageCfg32(CfgConfig, MappedImage); + } + else + { + return PhGetMappedImageCfg64(CfgConfig, MappedImage); + } +} + +NTSTATUS PhGetMappedImageCfgEntry( + _In_ PPH_MAPPED_IMAGE_CFG CfgConfig, + _In_ ULONGLONG Index, + _In_ CFG_ENTRY_TYPE Type, + _Out_ PIMAGE_CFG_ENTRY Entry + ) +{ + PULONGLONG guardTable; + ULONGLONG numberofGuardEntries; + PIMAGE_CFG_ENTRY cfgMappedEntry; + + switch (Type) + { + case ControlFlowGuardFunction: + { + guardTable = CfgConfig->GuardFunctionTable; + numberofGuardEntries = CfgConfig->NumberOfGuardFunctionEntries; + } + break; + case ControlFlowGuardtakenIatEntry: + { + guardTable = CfgConfig->GuardAdressIatTable; + numberofGuardEntries = CfgConfig->NumberOfGuardAdressIatEntries; + } + break; + case ControlFlowGuardLongJump: + { + guardTable = CfgConfig->GuardLongJumpTable; + numberofGuardEntries = CfgConfig->NumberOfGuardLongJumpEntries; + } + break; + default: + return STATUS_INVALID_PARAMETER_3; + } + + if (!guardTable || Index >= numberofGuardEntries) + return STATUS_PROCEDURE_NOT_FOUND; + + cfgMappedEntry = (PIMAGE_CFG_ENTRY)PTR_ADD_OFFSET(guardTable, Index * CfgConfig->EntrySize); + + Entry->Rva = cfgMappedEntry->Rva; + + // Optional header after the rva entry + if (CfgConfig->EntrySize > sizeof(FIELD_OFFSET(IMAGE_CFG_ENTRY, Rva))) + { + Entry->SuppressedCall = cfgMappedEntry->SuppressedCall; + Entry->Reserved = cfgMappedEntry->Reserved; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetMappedImageResources( + _Out_ PPH_MAPPED_IMAGE_RESOURCES Resources, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_RESOURCE_DIRECTORY resourceDirectory; + PIMAGE_RESOURCE_DIRECTORY nameDirectory; + PIMAGE_RESOURCE_DIRECTORY languageDirectory; + PIMAGE_RESOURCE_DIRECTORY_ENTRY resourceType; + PIMAGE_RESOURCE_DIRECTORY_ENTRY resourceName; + PIMAGE_RESOURCE_DIRECTORY_ENTRY resourceLanguage; + ULONG resourceCount = 0; + ULONG resourceIndex = 0; + ULONG resourceTypeCount; + ULONG resourceNameCount; + ULONG resourceLanguageCount; + + // Get a pointer to the resource directory. + + status = PhGetMappedImageDataEntry( + MappedImage, + IMAGE_DIRECTORY_ENTRY_RESOURCE, + &Resources->DataDirectory + ); + + if (!NT_SUCCESS(status)) + return status; + + resourceDirectory = PhMappedImageRvaToVa( + MappedImage, + Resources->DataDirectory->VirtualAddress, + NULL + ); + + if (!resourceDirectory) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe(MappedImage, resourceDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + Resources->ResourceDirectory = resourceDirectory; + + // NOTE: We can't use LdrEnumResources here because we're using an image mapped with SEC_COMMIT. + + // Do a scan to determine how many resources there are. + + resourceType = PTR_ADD_OFFSET(resourceDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + resourceTypeCount = resourceDirectory->NumberOfNamedEntries + resourceDirectory->NumberOfIdEntries; + + for (ULONG i = 0; i < resourceTypeCount; ++i, ++resourceType) + { + if (!resourceType->DataIsDirectory) + continue; // return STATUS_RESOURCE_TYPE_NOT_FOUND; + + nameDirectory = PTR_ADD_OFFSET(resourceDirectory, resourceType->OffsetToDirectory); + resourceName = PTR_ADD_OFFSET(nameDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + resourceNameCount = nameDirectory->NumberOfNamedEntries + nameDirectory->NumberOfIdEntries; + + for (ULONG j = 0; j < resourceNameCount; ++j, ++resourceName) + { + if (!resourceName->DataIsDirectory) + continue; // return STATUS_RESOURCE_NAME_NOT_FOUND; + + languageDirectory = PTR_ADD_OFFSET(resourceDirectory, resourceName->OffsetToDirectory); + resourceLanguage = PTR_ADD_OFFSET(languageDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + resourceLanguageCount = languageDirectory->NumberOfNamedEntries + languageDirectory->NumberOfIdEntries; + + for (ULONG k = 0; k < resourceLanguageCount; ++k, ++resourceLanguage) + { + if (resourceLanguage->DataIsDirectory) + continue; // return STATUS_RESOURCE_DATA_NOT_FOUND; + + resourceCount++; + } + } + } + + if (resourceCount == 0) + return STATUS_INVALID_IMAGE_FORMAT; + + // Allocate the number of resources. + + Resources->NumberOfEntries = resourceCount; + Resources->ResourceEntries = PhAllocate(sizeof(PH_IMAGE_RESOURCE_ENTRY) * resourceCount); + memset(Resources->ResourceEntries, 0, sizeof(PH_IMAGE_RESOURCE_ENTRY) * resourceCount); + + // Enumerate the resources adding them into our buffer. + + resourceType = PTR_ADD_OFFSET(resourceDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + resourceTypeCount = resourceDirectory->NumberOfNamedEntries + resourceDirectory->NumberOfIdEntries; + + for (ULONG i = 0; i < resourceTypeCount; ++i, ++resourceType) + { + if (!resourceType->DataIsDirectory) + goto CleanupExit; + + nameDirectory = PTR_ADD_OFFSET(resourceDirectory, resourceType->OffsetToDirectory); + resourceName = PTR_ADD_OFFSET(nameDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + resourceNameCount = nameDirectory->NumberOfNamedEntries + nameDirectory->NumberOfIdEntries; + + for (ULONG j = 0; j < resourceNameCount; ++j, ++resourceName) + { + if (!resourceName->DataIsDirectory) + goto CleanupExit; + + languageDirectory = PTR_ADD_OFFSET(resourceDirectory, resourceName->OffsetToDirectory); + resourceLanguage = PTR_ADD_OFFSET(languageDirectory, sizeof(IMAGE_RESOURCE_DIRECTORY)); + resourceLanguageCount = languageDirectory->NumberOfNamedEntries + languageDirectory->NumberOfIdEntries; + + for (ULONG k = 0; k < resourceLanguageCount; ++k, ++resourceLanguage) + { + PIMAGE_RESOURCE_DATA_ENTRY resourceData; + + if (resourceLanguage->DataIsDirectory) + goto CleanupExit; + + resourceData = PTR_ADD_OFFSET(resourceDirectory, resourceLanguage->OffsetToData); + + Resources->ResourceEntries[resourceIndex].Type = NAME_FROM_RESOURCE_ENTRY(resourceDirectory, resourceType); + Resources->ResourceEntries[resourceIndex].Name = NAME_FROM_RESOURCE_ENTRY(resourceDirectory, resourceName); + Resources->ResourceEntries[resourceIndex].Language = NAME_FROM_RESOURCE_ENTRY(resourceDirectory, resourceLanguage); + Resources->ResourceEntries[resourceIndex].Data = PhMappedImageRvaToVa(MappedImage, resourceData->OffsetToData, NULL); + Resources->ResourceEntries[resourceIndex++].Size = resourceData->Size; + } + } + } + +CleanupExit: + return status; +} + +NTSTATUS PhGetMappedImageTlsCallbackDirectory32( + _Out_ PPH_MAPPED_IMAGE_TLS_CALLBACKS TlsCallbacks, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_TLS_DIRECTORY32 tlsDirectory; + ULONG_PTR tlsCallbacksOffset; + + // Get a pointer to the resource directory. + + status = PhGetMappedImageDataEntry( + MappedImage, + IMAGE_DIRECTORY_ENTRY_TLS, + &TlsCallbacks->DataDirectory + ); + + if (!NT_SUCCESS(status)) + return status; + + tlsDirectory = PhMappedImageRvaToVa( + MappedImage, + TlsCallbacks->DataDirectory->VirtualAddress, + NULL + ); + + if (!tlsDirectory) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe(MappedImage, tlsDirectory, sizeof(PIMAGE_TLS_DIRECTORY32)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + TlsCallbacks->TlsDirectory32 = tlsDirectory; + + if (tlsDirectory->StartAddressOfRawData > MappedImage->NtHeaders32->OptionalHeader.ImageBase) + tlsCallbacksOffset = MappedImage->NtHeaders32->OptionalHeader.ImageBase; + else + tlsCallbacksOffset = 0; + + TlsCallbacks->CallbackIndexes = PhMappedImageRvaToVa(MappedImage, PtrToUlong(PTR_SUB_OFFSET(tlsDirectory->AddressOfIndex, tlsCallbacksOffset)), NULL); + TlsCallbacks->CallbackAddress = PhMappedImageRvaToVa(MappedImage, PtrToUlong(PTR_SUB_OFFSET(tlsDirectory->AddressOfCallBacks, tlsCallbacksOffset)), NULL); + + if (TlsCallbacks->CallbackAddress) + return STATUS_SUCCESS; + + return STATUS_INVALID_PARAMETER; +} + +NTSTATUS PhGetMappedImageTlsCallbackDirectory64( + _Out_ PPH_MAPPED_IMAGE_TLS_CALLBACKS TlsCallbacks, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + PIMAGE_TLS_DIRECTORY64 tlsDirectory; + ULONG_PTR tlsCallbacksOffset; + + // Get a pointer to the resource directory. + + status = PhGetMappedImageDataEntry( + MappedImage, + IMAGE_DIRECTORY_ENTRY_TLS, + &TlsCallbacks->DataDirectory + ); + + if (!NT_SUCCESS(status)) + return status; + + tlsDirectory = PhMappedImageRvaToVa( + MappedImage, + TlsCallbacks->DataDirectory->VirtualAddress, + NULL + ); + + if (!tlsDirectory) + return STATUS_INVALID_PARAMETER; + + __try + { + PhpMappedImageProbe(MappedImage, tlsDirectory, sizeof(PIMAGE_TLS_DIRECTORY64)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + TlsCallbacks->TlsDirectory64 = tlsDirectory; + + if (tlsDirectory->StartAddressOfRawData > MappedImage->NtHeaders->OptionalHeader.ImageBase) + tlsCallbacksOffset = MappedImage->NtHeaders->OptionalHeader.ImageBase; + else + tlsCallbacksOffset = 0; + + TlsCallbacks->CallbackIndexes = PhMappedImageRvaToVa(MappedImage, PtrToUlong(PTR_SUB_OFFSET(tlsDirectory->AddressOfIndex, tlsCallbacksOffset)), NULL); + TlsCallbacks->CallbackAddress = PhMappedImageRvaToVa(MappedImage, PtrToUlong(PTR_SUB_OFFSET(tlsDirectory->AddressOfCallBacks, tlsCallbacksOffset)), NULL); + + if (TlsCallbacks->CallbackAddress) + return STATUS_SUCCESS; + + return STATUS_INVALID_PARAMETER; +} + +NTSTATUS PhGetMappedImageTlsCallbacks( + _Out_ PPH_MAPPED_IMAGE_TLS_CALLBACKS TlsCallbacks, + _In_ PPH_MAPPED_IMAGE MappedImage + ) +{ + NTSTATUS status; + ULONG count = 0; + ULONG i; + + // Get a pointer to the TLS directory. + + if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + status = PhGetMappedImageTlsCallbackDirectory32(TlsCallbacks, MappedImage); + else + status = PhGetMappedImageTlsCallbackDirectory64(TlsCallbacks, MappedImage); + + if (!NT_SUCCESS(status)) + return status; + + // Do a scan to determine how many callbacks there are. + + if (TlsCallbacks->CallbackAddress) + { + if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PULONG array = (PULONG)(PULONG_PTR)TlsCallbacks->CallbackAddress; + + for (i = 0; array[i]; i++) + count++; + } + else + { + PULONGLONG array = (PULONGLONG)(PULONG_PTR)TlsCallbacks->CallbackAddress; + + for (i = 0; array[i]; i++) + count++; + } + } + + if (count == 0) + return STATUS_INVALID_IMAGE_FORMAT; + + // Allocate the number of callbacks. + + TlsCallbacks->NumberOfEntries = count; + TlsCallbacks->Entries = PhAllocate(sizeof(PH_IMAGE_TLS_CALLBACK_ENTRY) * count); + memset(TlsCallbacks->Entries, 0, sizeof(PH_IMAGE_TLS_CALLBACK_ENTRY) * count); + + // Add the callbacks into our buffer. + + if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PULONG array = (PULONG)(PULONG_PTR)TlsCallbacks->CallbackAddress; + + for (i = 0; i < count; i++) + { + TlsCallbacks->Entries[i].Address = array[i]; + } + } + else + { + PULONGLONG array = (PULONGLONG)(PULONG_PTR)TlsCallbacks->CallbackAddress; + + for (i = 0; i < count; i++) + { + TlsCallbacks->Entries[i].Address = array[i]; + } + } + + return status; +} diff --git a/phlib/maplib.c b/phlib/maplib.c index 3d01feddb655..2e9c171de5a3 100644 --- a/phlib/maplib.c +++ b/phlib/maplib.c @@ -1,402 +1,404 @@ -/* - * Process Hacker - - * mapped library - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This file contains functions to load and retrieve information for library/archive files (lib). - * The file format for archive files is explained in the PE/COFF specification located at: - * - * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx - */ - -#include -#include - -VOID PhpMappedArchiveProbe( - _In_ PPH_MAPPED_ARCHIVE MappedArchive, - _In_ PVOID Address, - _In_ SIZE_T Length - ); - -NTSTATUS PhpGetMappedArchiveMemberFromHeader( - _In_ PPH_MAPPED_ARCHIVE MappedArchive, - _In_ PIMAGE_ARCHIVE_MEMBER_HEADER Header, - _Out_ PPH_MAPPED_ARCHIVE_MEMBER Member - ); - -NTSTATUS PhInitializeMappedArchive( - _Out_ PPH_MAPPED_ARCHIVE MappedArchive, - _In_ PVOID ViewBase, - _In_ SIZE_T Size - ) -{ - NTSTATUS status; - PCHAR start; - - start = (PCHAR)ViewBase; - - memset(MappedArchive, 0, sizeof(PH_MAPPED_ARCHIVE)); - MappedArchive->ViewBase = ViewBase; - MappedArchive->Size = Size; - - __try - { - // Verify the file signature. - - PhpMappedArchiveProbe(MappedArchive, start, IMAGE_ARCHIVE_START_SIZE); - - if (memcmp(start, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE) != 0) - PhRaiseStatus(STATUS_INVALID_IMAGE_FORMAT); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - // Get the members. - // Note: the names are checked. - - // First linker member - - status = PhpGetMappedArchiveMemberFromHeader( - MappedArchive, - (PIMAGE_ARCHIVE_MEMBER_HEADER)(start + IMAGE_ARCHIVE_START_SIZE), - &MappedArchive->FirstLinkerMember - ); - - if (!NT_SUCCESS(status)) - return status; - - if (MappedArchive->FirstLinkerMember.Type != LinkerArchiveMemberType) - return STATUS_INVALID_PARAMETER; - - MappedArchive->FirstStandardMember = &MappedArchive->FirstLinkerMember; - - // Second linker member - - status = PhGetNextMappedArchiveMember( - &MappedArchive->FirstLinkerMember, - &MappedArchive->SecondLinkerMember - ); - - if (!NT_SUCCESS(status)) - return status; - - if (MappedArchive->SecondLinkerMember.Type != LinkerArchiveMemberType) - return STATUS_INVALID_PARAMETER; - - // Longnames member - // This member doesn't seem to be mandatory, contrary to the specification. - // So we'll check if it's actually a longnames member, and if not, ignore it. - - status = PhGetNextMappedArchiveMember( - &MappedArchive->SecondLinkerMember, - &MappedArchive->LongnamesMember - ); - - if ( - NT_SUCCESS(status) && - MappedArchive->LongnamesMember.Type == LongnamesArchiveMemberType - ) - { - MappedArchive->HasLongnamesMember = TRUE; - MappedArchive->LastStandardMember = &MappedArchive->LongnamesMember; - } - else - { - MappedArchive->LastStandardMember = &MappedArchive->SecondLinkerMember; - } - - return STATUS_SUCCESS; -} - -NTSTATUS PhLoadMappedArchive( - _In_opt_ PWSTR FileName, - _In_opt_ HANDLE FileHandle, - _In_ BOOLEAN ReadOnly, - _Out_ PPH_MAPPED_ARCHIVE MappedArchive - ) -{ - NTSTATUS status; - - status = PhMapViewOfEntireFile( - FileName, - FileHandle, - ReadOnly, - &MappedArchive->ViewBase, - &MappedArchive->Size - ); - - if (NT_SUCCESS(status)) - { - status = PhInitializeMappedArchive( - MappedArchive, - MappedArchive->ViewBase, - MappedArchive->Size - ); - - if (!NT_SUCCESS(status)) - { - NtUnmapViewOfSection(NtCurrentProcess(), MappedArchive->ViewBase); - } - } - - return status; -} - -NTSTATUS PhUnloadMappedArchive( - _Inout_ PPH_MAPPED_ARCHIVE MappedArchive - ) -{ - return NtUnmapViewOfSection( - NtCurrentProcess(), - MappedArchive->ViewBase - ); -} - -VOID PhpMappedArchiveProbe( - _In_ PPH_MAPPED_ARCHIVE MappedArchive, - _In_ PVOID Address, - _In_ SIZE_T Length - ) -{ - PhProbeAddress(Address, Length, MappedArchive->ViewBase, MappedArchive->Size, 1); -} - -/** - * Gets the next archive member. - * - * \param Member An archive member structure. - * \param NextMember A variable which receives a structure describing the next archive member. This - * pointer may be the same as the pointer specified in \a Member. - */ -NTSTATUS PhGetNextMappedArchiveMember( - _In_ PPH_MAPPED_ARCHIVE_MEMBER Member, - _Out_ PPH_MAPPED_ARCHIVE_MEMBER NextMember - ) -{ - PIMAGE_ARCHIVE_MEMBER_HEADER nextHeader; - - nextHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)PTR_ADD_OFFSET( - Member->Data, - Member->Size - ); - - // 2 byte alignment. - if ((ULONG_PTR)nextHeader & 0x1) - nextHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)PTR_ADD_OFFSET(nextHeader, 1); - - return PhpGetMappedArchiveMemberFromHeader( - Member->MappedArchive, - nextHeader, - NextMember - ); -} - -NTSTATUS PhpGetMappedArchiveMemberFromHeader( - _In_ PPH_MAPPED_ARCHIVE MappedArchive, - _In_ PIMAGE_ARCHIVE_MEMBER_HEADER Header, - _Out_ PPH_MAPPED_ARCHIVE_MEMBER Member - ) -{ - WCHAR integerString[11]; - ULONG64 size; - PH_STRINGREF string; - PWSTR digit; - PSTR slash; - - if ((ULONG_PTR)Header >= (ULONG_PTR)MappedArchive->ViewBase + MappedArchive->Size) - return STATUS_NO_MORE_ENTRIES; - - __try - { - PhpMappedArchiveProbe(MappedArchive, Header, sizeof(IMAGE_ARCHIVE_MEMBER_HEADER)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - Member->MappedArchive = MappedArchive; - Member->Header = Header; - Member->Data = PTR_ADD_OFFSET(Header, sizeof(IMAGE_ARCHIVE_MEMBER_HEADER)); - Member->Type = NormalArchiveMemberType; - - // Read the size string, terminate it after the last digit and parse it. - - if (!PhCopyStringZFromBytes(Header->Size, 10, integerString, 11, NULL)) - return STATUS_INVALID_PARAMETER; - - string.Buffer = integerString; - string.Length = 0; - digit = string.Buffer; - - while (iswdigit(*digit++)) - string.Length += sizeof(WCHAR); - - if (!PhStringToInteger64(&string, 10, &size)) - return STATUS_INVALID_PARAMETER; - - Member->Size = (ULONG)size; - - __try - { - PhpMappedArchiveProbe(MappedArchive, Member->Data, Member->Size); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return GetExceptionCode(); - } - - // Parse the name. - - if (!PhCopyBytesZ(Header->Name, 16, Member->NameBuffer, 20, NULL)) - return STATUS_INVALID_PARAMETER; - - Member->Name = Member->NameBuffer; - - slash = strchr(Member->NameBuffer, '/'); - - if (!slash) - return STATUS_INVALID_PARAMETER; - - // Special names: - // * If the slash is the first character, then this is a linker member. - // * If there is a slash after the slash which is a first character, then this is the longnames - // member. - // * If there are digits after the slash, then the real name is stored in the longnames member. - - if (slash == Member->NameBuffer) - { - if (Member->NameBuffer[1] == '/') - { - // Longnames member. Set the name to "/". - Member->NameBuffer[0] = '/'; - Member->NameBuffer[1] = 0; - - Member->Type = LongnamesArchiveMemberType; - } - else - { - // Linker member. Set the name to "". - Member->NameBuffer[0] = 0; - - Member->Type = LinkerArchiveMemberType; - } - } - else - { - if (isdigit(slash[1])) - { - PSTR digita; - ULONG64 offset64; - ULONG offset; - - // The name is stored in the longnames member. - // Note: we make sure we have the longnames member first. - - if (!MappedArchive->LongnamesMember.Header) - return STATUS_INVALID_PARAMETER; - - // Find the last digit and null terminate the string there. - - digita = slash + 2; - - while (isdigit(*digita)) - digita++; - - *digita = 0; - - // Parse the offset and make sure it lies within the longnames member. - - if (!PhCopyStringZFromBytes(slash + 1, -1, integerString, 11, NULL)) - return STATUS_INVALID_PARAMETER; - PhInitializeStringRefLongHint(&string, integerString); - if (!PhStringToInteger64(&string, 10, &offset64)) - return STATUS_INVALID_PARAMETER; - - offset = (ULONG)offset64; - - if (offset >= MappedArchive->LongnamesMember.Size) - return STATUS_INVALID_PARAMETER; - - // TODO: Probe the name. - Member->Name = (PSTR)PTR_ADD_OFFSET(MappedArchive->LongnamesMember.Data, offset); - } - else - { - // Null terminate the string. - slash[0] = 0; - } - } - - return STATUS_SUCCESS; -} - -BOOLEAN PhIsMappedArchiveMemberShortFormat( - _In_ PPH_MAPPED_ARCHIVE_MEMBER Member - ) -{ - PIMAGE_FILE_HEADER header; - - header = (PIMAGE_FILE_HEADER)Member->Data; - - return header->Machine != IMAGE_FILE_MACHINE_UNKNOWN; -} - -NTSTATUS PhGetMappedArchiveImportEntry( - _In_ PPH_MAPPED_ARCHIVE_MEMBER Member, - _Out_ PPH_MAPPED_ARCHIVE_IMPORT_ENTRY Entry - ) -{ - IMPORT_OBJECT_HEADER *importHeader; - - importHeader = (IMPORT_OBJECT_HEADER *)Member->Data; - - if (Member->Type != NormalArchiveMemberType) - return STATUS_INVALID_PARAMETER; - if ( - importHeader->Sig1 != IMAGE_FILE_MACHINE_UNKNOWN || - importHeader->Sig2 != IMPORT_OBJECT_HDR_SIG2 - ) - return STATUS_INVALID_PARAMETER; - - Entry->Type = (BYTE)importHeader->Type; - Entry->NameType = (BYTE)importHeader->NameType; - Entry->Machine = importHeader->Machine; - - // TODO: Probe the name. - Entry->Name = (PSTR)PTR_ADD_OFFSET(importHeader, sizeof(IMPORT_OBJECT_HEADER)); - Entry->DllName = (PSTR)PTR_ADD_OFFSET(Entry->Name, strlen(Entry->Name) + 1); - - // Ordinal/NameHint are union'ed, so these statements are exactly the same. - // It's there in case this changes in the future. - if (Entry->NameType == IMPORT_OBJECT_ORDINAL) - { - Entry->Ordinal = importHeader->Ordinal; - } - else - { - Entry->NameHint = importHeader->Hint; - } - - return STATUS_SUCCESS; -} +/* + * Process Hacker - + * mapped library + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This file contains functions to load and retrieve information for library/archive files (lib). + * The file format for archive files is explained in the PE/COFF specification located at: + * + * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + */ + +#include +#include + +VOID PhpMappedArchiveProbe( + _In_ PPH_MAPPED_ARCHIVE MappedArchive, + _In_ PVOID Address, + _In_ SIZE_T Length + ); + +NTSTATUS PhpGetMappedArchiveMemberFromHeader( + _In_ PPH_MAPPED_ARCHIVE MappedArchive, + _In_ PIMAGE_ARCHIVE_MEMBER_HEADER Header, + _Out_ PPH_MAPPED_ARCHIVE_MEMBER Member + ); + +NTSTATUS PhInitializeMappedArchive( + _Out_ PPH_MAPPED_ARCHIVE MappedArchive, + _In_ PVOID ViewBase, + _In_ SIZE_T Size + ) +{ + NTSTATUS status; + PVOID start; + + start = ViewBase; + + memset(MappedArchive, 0, sizeof(PH_MAPPED_ARCHIVE)); + MappedArchive->ViewBase = ViewBase; + MappedArchive->Size = Size; + + __try + { + // Verify the file signature. + + PhpMappedArchiveProbe(MappedArchive, start, IMAGE_ARCHIVE_START_SIZE); + + if (memcmp(start, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE) != 0) + PhRaiseStatus(STATUS_INVALID_IMAGE_FORMAT); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + // Get the members. + // Note: the names are checked. + + // First linker member + + status = PhpGetMappedArchiveMemberFromHeader( + MappedArchive, + PTR_ADD_OFFSET(start, IMAGE_ARCHIVE_START_SIZE), + &MappedArchive->FirstLinkerMember + ); + + if (!NT_SUCCESS(status)) + return status; + + if (MappedArchive->FirstLinkerMember.Type != LinkerArchiveMemberType) + return STATUS_INVALID_PARAMETER; + + MappedArchive->FirstStandardMember = &MappedArchive->FirstLinkerMember; + + // Second linker member + + status = PhGetNextMappedArchiveMember( + &MappedArchive->FirstLinkerMember, + &MappedArchive->SecondLinkerMember + ); + + if (!NT_SUCCESS(status)) + return status; + + if ( + MappedArchive->SecondLinkerMember.Type != LinkerArchiveMemberType && + MappedArchive->SecondLinkerMember.Type != NormalArchiveMemberType // NormalArchiveMemberType might not be correct here but set by LLVM compiled libs (dmex) + ) + return STATUS_INVALID_PARAMETER; + + // Longnames member + // This member doesn't seem to be mandatory, contrary to the specification. + // So we'll check if it's actually a longnames member, and if not, ignore it. + + status = PhGetNextMappedArchiveMember( + &MappedArchive->SecondLinkerMember, + &MappedArchive->LongnamesMember + ); + + if ( + NT_SUCCESS(status) && + MappedArchive->LongnamesMember.Type == LongnamesArchiveMemberType + ) + { + MappedArchive->HasLongnamesMember = TRUE; + MappedArchive->LastStandardMember = &MappedArchive->LongnamesMember; + } + else + { + MappedArchive->LastStandardMember = &MappedArchive->SecondLinkerMember; + } + + return STATUS_SUCCESS; +} + +NTSTATUS PhLoadMappedArchive( + _In_opt_ PWSTR FileName, + _In_opt_ HANDLE FileHandle, + _In_ BOOLEAN ReadOnly, + _Out_ PPH_MAPPED_ARCHIVE MappedArchive + ) +{ + NTSTATUS status; + + status = PhMapViewOfEntireFile( + FileName, + FileHandle, + ReadOnly, + &MappedArchive->ViewBase, + &MappedArchive->Size + ); + + if (NT_SUCCESS(status)) + { + status = PhInitializeMappedArchive( + MappedArchive, + MappedArchive->ViewBase, + MappedArchive->Size + ); + + if (!NT_SUCCESS(status)) + { + PhUnloadMappedArchive(MappedArchive); + } + } + + return status; +} + +NTSTATUS PhUnloadMappedArchive( + _Inout_ PPH_MAPPED_ARCHIVE MappedArchive + ) +{ + return NtUnmapViewOfSection( + NtCurrentProcess(), + MappedArchive->ViewBase + ); +} + +VOID PhpMappedArchiveProbe( + _In_ PPH_MAPPED_ARCHIVE MappedArchive, + _In_ PVOID Address, + _In_ SIZE_T Length + ) +{ + PhProbeAddress(Address, Length, MappedArchive->ViewBase, MappedArchive->Size, 1); +} + +/** + * Gets the next archive member. + * + * \param Member An archive member structure. + * \param NextMember A variable which receives a structure describing the next archive member. This + * pointer may be the same as the pointer specified in \a Member. + */ +NTSTATUS PhGetNextMappedArchiveMember( + _In_ PPH_MAPPED_ARCHIVE_MEMBER Member, + _Out_ PPH_MAPPED_ARCHIVE_MEMBER NextMember + ) +{ + PIMAGE_ARCHIVE_MEMBER_HEADER nextHeader; + + nextHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)PTR_ADD_OFFSET( + Member->Data, + Member->Size + ); + + // 2 byte alignment. + if ((ULONG_PTR)nextHeader & 0x1) + nextHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)PTR_ADD_OFFSET(nextHeader, 1); + + return PhpGetMappedArchiveMemberFromHeader( + Member->MappedArchive, + nextHeader, + NextMember + ); +} + +NTSTATUS PhpGetMappedArchiveMemberFromHeader( + _In_ PPH_MAPPED_ARCHIVE MappedArchive, + _In_ PIMAGE_ARCHIVE_MEMBER_HEADER Header, + _Out_ PPH_MAPPED_ARCHIVE_MEMBER Member + ) +{ + WCHAR integerString[11]; + ULONG64 size; + PH_STRINGREF string; + PWSTR digit; + PSTR slash; + + if ((ULONG_PTR)Header >= (ULONG_PTR)MappedArchive->ViewBase + MappedArchive->Size) + return STATUS_NO_MORE_ENTRIES; + + __try + { + PhpMappedArchiveProbe(MappedArchive, Header, sizeof(IMAGE_ARCHIVE_MEMBER_HEADER)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + Member->MappedArchive = MappedArchive; + Member->Header = Header; + Member->Data = PTR_ADD_OFFSET(Header, sizeof(IMAGE_ARCHIVE_MEMBER_HEADER)); + Member->Type = NormalArchiveMemberType; + + // Read the size string, terminate it after the last digit and parse it. + + if (!PhCopyStringZFromBytes(Header->Size, 10, integerString, 11, NULL)) + return STATUS_INVALID_PARAMETER; + + string.Buffer = integerString; + string.Length = 0; + digit = string.Buffer; + + while (iswdigit(*digit++)) + string.Length += sizeof(WCHAR); + + if (!PhStringToInteger64(&string, 10, &size)) + return STATUS_INVALID_PARAMETER; + + Member->Size = (ULONG)size; + + __try + { + PhpMappedArchiveProbe(MappedArchive, Member->Data, Member->Size); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return GetExceptionCode(); + } + + // Parse the name. + + if (!PhCopyBytesZ(Header->Name, 16, Member->NameBuffer, 20, NULL)) + return STATUS_INVALID_PARAMETER; + + Member->Name = Member->NameBuffer; + + slash = strchr(Member->NameBuffer, '/'); + + if (!slash) + return STATUS_INVALID_PARAMETER; + + // Special names: + // * If the slash is the first character, then this is a linker member. + // * If there is a slash after the slash which is a first character, then this is the longnames + // member. + // * If there are digits after the slash, then the real name is stored in the longnames member. + + if (slash == Member->NameBuffer) + { + if (Member->NameBuffer[1] == '/') + { + // Longnames member. Set the name to "/". + Member->NameBuffer[0] = '/'; + Member->NameBuffer[1] = ANSI_NULL; + + Member->Type = LongnamesArchiveMemberType; + } + else + { + // Linker member. Set the name to "". + Member->NameBuffer[0] = ANSI_NULL; + + Member->Type = LinkerArchiveMemberType; + } + } + else + { + if (isdigit(slash[1])) + { + PSTR digita; + ULONG64 offset64; + ULONG offset; + + // The name is stored in the longnames member. + // Note: we make sure we have the longnames member first. + + if (!MappedArchive->LongnamesMember.Header) + return STATUS_INVALID_PARAMETER; + + // Find the last digit and null terminate the string there. + + digita = slash + 2; + + while (isdigit(*digita)) + digita++; + + *digita = 0; + + // Parse the offset and make sure it lies within the longnames member. + + if (!PhCopyStringZFromBytes(slash + 1, -1, integerString, 11, NULL)) + return STATUS_INVALID_PARAMETER; + PhInitializeStringRefLongHint(&string, integerString); + if (!PhStringToInteger64(&string, 10, &offset64)) + return STATUS_INVALID_PARAMETER; + + offset = (ULONG)offset64; + + if (offset >= MappedArchive->LongnamesMember.Size) + return STATUS_INVALID_PARAMETER; + + // TODO: Probe the name. + Member->Name = (PSTR)PTR_ADD_OFFSET(MappedArchive->LongnamesMember.Data, offset); + } + else + { + // Null terminate the string. + slash[0] = 0; + } + } + + return STATUS_SUCCESS; +} + +BOOLEAN PhIsMappedArchiveMemberShortFormat( + _In_ PPH_MAPPED_ARCHIVE_MEMBER Member + ) +{ + PIMAGE_FILE_HEADER header; + + header = (PIMAGE_FILE_HEADER)Member->Data; + + return header->Machine != IMAGE_FILE_MACHINE_UNKNOWN; +} + +NTSTATUS PhGetMappedArchiveImportEntry( + _In_ PPH_MAPPED_ARCHIVE_MEMBER Member, + _Out_ PPH_MAPPED_ARCHIVE_IMPORT_ENTRY Entry + ) +{ + IMPORT_OBJECT_HEADER *importHeader; + + importHeader = (IMPORT_OBJECT_HEADER *)Member->Data; + + if ( + importHeader->Sig1 != IMAGE_FILE_MACHINE_UNKNOWN || + importHeader->Sig2 != IMPORT_OBJECT_HDR_SIG2 + ) + return STATUS_INVALID_PARAMETER; + + Entry->Type = (BYTE)importHeader->Type; + Entry->NameType = (BYTE)importHeader->NameType; + Entry->Machine = importHeader->Machine; + + // TODO: Probe the name. + Entry->Name = (PSTR)PTR_ADD_OFFSET(importHeader, sizeof(IMPORT_OBJECT_HEADER)); + Entry->DllName = (PSTR)PTR_ADD_OFFSET(Entry->Name, strlen(Entry->Name) + 1); + + // Ordinal/NameHint are union'ed, so these statements are exactly the same. + // It's there in case this changes in the future. + if (Entry->NameType == IMPORT_OBJECT_ORDINAL) + { + Entry->Ordinal = importHeader->Ordinal; + } + else + { + Entry->NameHint = importHeader->Hint; + } + + return STATUS_SUCCESS; +} diff --git a/phlib/md5.c b/phlib/md5.c index 8752c08c1b13..b984655e6220 100644 --- a/phlib/md5.c +++ b/phlib/md5.c @@ -1,225 +1,225 @@ -/* - * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* This code was modified for Process Hacker. */ - -#include -#include "md5.h" - -void MD5Transform(ULONG buf[4], ULONG in[16]); - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -VOID MD5Init( - _Out_ MD5_CTX *Context - ) -{ - Context->buf[0] = 0x67452301; - Context->buf[1] = 0xefcdab89; - Context->buf[2] = 0x98badcfe; - Context->buf[3] = 0x10325476; - - Context->i[0] = 0; - Context->i[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -VOID MD5Update( - _Inout_ MD5_CTX *Context, - _In_reads_bytes_(Length) UCHAR *Input, - _In_ ULONG Length - ) -{ - ULONG t; - - /* Update bitcount */ - - t = Context->i[0]; - if ((Context->i[0] = t + ((ULONG) Length << 3)) < t) - Context->i[1]++; /* Carry from low to high */ - Context->i[1] += Length >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if (t) { - unsigned char *p = (unsigned char *) Context->in + t; - - t = 64 - t; - if (Length < t) { - memcpy(p, Input, Length); - return; - } - memcpy(p, Input, t); - MD5Transform(Context->buf, (ULONG *) Context->in); - Input += t; - Length -= t; - } - /* Process data in 64-byte chunks */ - - while (Length >= 64) { - memcpy(Context->in, Input, 64); - MD5Transform(Context->buf, (ULONG *) Context->in); - Input += 64; - Length -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(Context->in, Input, Length); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -VOID MD5Final( - _Inout_ MD5_CTX *Context - ) -{ - unsigned int count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (Context->i[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = Context->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - MD5Transform(Context->buf, (ULONG *) Context->in); - - /* Now fill the next block with 56 bytes */ - memset(Context->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count - 8); - } - - /* Append length in bits and transform */ - ((ULONG *) Context->in)[14] = Context->i[0]; - ((ULONG *) Context->in)[15] = Context->i[1]; - - MD5Transform(Context->buf, (ULONG *) Context->in); - memcpy(Context->digest, Context->buf, 16); -} - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = _rotl(w, s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -void MD5Transform(ULONG buf[4], ULONG in[16]) -{ - register ULONG a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* This code was modified for Process Hacker. */ + +#include +#include "md5.h" + +void MD5Transform(ULONG buf[4], ULONG in[16]); + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +VOID MD5Init( + _Out_ MD5_CTX *Context + ) +{ + Context->buf[0] = 0x67452301; + Context->buf[1] = 0xefcdab89; + Context->buf[2] = 0x98badcfe; + Context->buf[3] = 0x10325476; + + Context->i[0] = 0; + Context->i[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +VOID MD5Update( + _Inout_ MD5_CTX *Context, + _In_reads_bytes_(Length) UCHAR *Input, + _In_ ULONG Length + ) +{ + ULONG t; + + /* Update bitcount */ + + t = Context->i[0]; + if ((Context->i[0] = t + ((ULONG) Length << 3)) < t) + Context->i[1]++; /* Carry from low to high */ + Context->i[1] += Length >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) Context->in + t; + + t = 64 - t; + if (Length < t) { + memcpy(p, Input, Length); + return; + } + memcpy(p, Input, t); + MD5Transform(Context->buf, (ULONG *) Context->in); + Input += t; + Length -= t; + } + /* Process data in 64-byte chunks */ + + while (Length >= 64) { + memcpy(Context->in, Input, 64); + MD5Transform(Context->buf, (ULONG *) Context->in); + Input += 64; + Length -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(Context->in, Input, Length); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +VOID MD5Final( + _Inout_ MD5_CTX *Context + ) +{ + unsigned int count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (Context->i[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = Context->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + MD5Transform(Context->buf, (ULONG *) Context->in); + + /* Now fill the next block with 56 bytes */ + memset(Context->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + + /* Append length in bits and transform */ + ((ULONG *) Context->in)[14] = Context->i[0]; + ((ULONG *) Context->in)[15] = Context->i[1]; + + MD5Transform(Context->buf, (ULONG *) Context->in); + memcpy(Context->digest, Context->buf, 16); +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = _rotl(w, s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(ULONG buf[4], ULONG in[16]) +{ + register ULONG a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/phlib/mxml/LICENSE b/phlib/mxml/LICENSE new file mode 100644 index 000000000000..261eeb9e9f8b --- /dev/null +++ b/phlib/mxml/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/phlib/mxml/config.h b/phlib/mxml/config.h new file mode 100644 index 000000000000..da2fd910e593 --- /dev/null +++ b/phlib/mxml/config.h @@ -0,0 +1,137 @@ +/* + * Visual Studio configuration file for Mini-XML, a small XML file parsing + * library. + * + * Copyright 2003-2019 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * https://michaelrsweet.github.io/mxml + */ + +/* + * Beginning with VC2005, Microsoft breaks ISO C and POSIX conformance + * by deprecating a number of functions in the name of security, even + * when many of the affected functions are otherwise completely secure. + * The _CRT_SECURE_NO_DEPRECATE definition ensures that we won't get + * warnings from their use... + * + * Then Microsoft decided that they should ignore this in VC2008 and use + * yet another define (_CRT_SECURE_NO_WARNINGS) instead... + */ + +#define _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_WARNINGS + + +/* + * Include necessary headers... + */ + +#include +#include +#include +#include +#include +#include + + +/* + * Microsoft also renames the POSIX functions to _name, and introduces + * a broken compatibility layer using the original names. As a result, + * random crashes can occur when, for example, strdup() allocates memory + * from a different heap than used by malloc() and free(). + * + * To avoid moronic problems like this, we #define the POSIX function + * names to the corresponding non-standard Microsoft names. + */ + +#define close _close +#define open _open +#define read _read +#define snprintf _snprintf +#define strdup _strdup +#define vsnprintf _vsnprintf +#define write _write + + +/* + * Version number... + */ + +#define MXML_VERSION "Mini-XML v3.0" + + +/* + * Inline function support... + */ + +#define inline _inline + + +/* + * Long long support... + */ + +#define HAVE_LONG_LONG 1 + + +/* + * Do we have the *printf() functions? + */ + +#define HAVE_SNPRINTF 1 +/* #undef HAVE_VASPRINTF */ +#define HAVE_VSNPRINTF 1 + + +/* + * Do we have the strXXX() functions? + */ + +#define HAVE_STRDUP 1 +/* #undef HAVE_STRLCPY */ + + +/* + * Do we have threading support? + */ + +/* #undef HAVE_PTHREAD_H */ + + +/* + * Define prototypes for string functions as needed... + */ + +# ifndef HAVE_STRDUP +extern char *_mxml_strdup(const char *); +# define strdup _mxml_strdup +# endif /* !HAVE_STRDUP */ + +# ifndef HAVE_STRLCAT +extern size_t _mxml_strlcat(char *, const char *, size_t); +# define strlcat _mxml_strlcat +# endif /* !HAVE_STRLCAT */ + +# ifndef HAVE_STRLCPY +extern size_t _mxml_strlcpy(char *, const char *, size_t); +# define strlcpy _mxml_strlcpy +# endif /* !HAVE_STRLCPY */ + +extern char *_mxml_strdupf(const char *, ...); +extern char *_mxml_vstrdupf(const char *, va_list); + +# ifndef HAVE_SNPRINTF +extern int _mxml_snprintf(char *, size_t, const char *, ...); +# define snprintf _mxml_snprintf +# endif /* !HAVE_SNPRINTF */ + +# ifndef HAVE_VSNPRINTF +extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); +# define vsnprintf _mxml_vsnprintf +# endif /* !HAVE_VSNPRINTF */ diff --git a/ProcessHacker/mxml/mxml-attr.c b/phlib/mxml/mxml-attr.c similarity index 70% rename from ProcessHacker/mxml/mxml-attr.c rename to phlib/mxml/mxml-attr.c index 53b7cd0e86af..6a1336f76780 100644 --- a/ProcessHacker/mxml/mxml-attr.c +++ b/phlib/mxml/mxml-attr.c @@ -1,34 +1,27 @@ /* - * "$Id: mxml-attr.c 451 2014-01-04 21:50:06Z msweet $" + * Attribute support code for Mini-XML, a small XML file parsing library. * - * Attribute support code for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2014 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* * Include necessary headers... */ -#include #include "config.h" -#include "mxml.h" +#include "mxml-private.h" /* * Local functions... */ -static int mxml_set_attr(mxml_node_t *node, const char *name, - char *value); +static int mxml_set_attr(mxml_node_t *node, const char *name, char *value); /* @@ -42,13 +35,13 @@ mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ const char *name)/* I - Attribute name */ { int i; /* Looping var */ - mxml_attr_t *attr; /* Cirrent attribute */ + _mxml_attr_t *attr; /* Cirrent attribute */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -65,9 +58,9 @@ mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ i > 0; i --, attr ++) { -#if DEBUG > 1 +#ifdef MXMLDEBUG printf(" %s=\"%s\"\n", attr->name, attr->value); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ if (!strcmp(attr->name, name)) { @@ -75,17 +68,17 @@ mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ * Delete this attribute... */ - PhFree(attr->name); - PhFree(attr->value); + PhFree(attr->name); + PhFree(attr->value); i --; if (i > 0) - memmove(attr, attr + 1, i * sizeof(mxml_attr_t)); + memmove(attr, attr + 1, i * sizeof(_mxml_attr_t)); node->value.element.num_attrs --; if (node->value.element.num_attrs == 0) - PhFree(node->value.element.attrs); + PhFree(node->value.element.attrs); return; } } @@ -95,22 +88,22 @@ mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ /* * 'mxmlElementGetAttr()' - Get an attribute. * - * This function returns NULL if the node is not an element or the + * This function returns @code NULL@ if the node is not an element or the * named attribute does not exist. */ -const char * /* O - Attribute value or NULL */ +const char * /* O - Attribute value or @code NULL@ */ mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ const char *name) /* I - Name of attribute */ { int i; /* Looping var */ - mxml_attr_t *attr; /* Cirrent attribute */ + _mxml_attr_t *attr; /* Cirrent attribute */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -127,15 +120,15 @@ mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ i > 0; i --, attr ++) { -#if DEBUG > 1 +#ifdef MXMLDEBUG printf(" %s=\"%s\"\n", attr->name, attr->value); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ if (!strcmp(attr->name, name)) { -#if DEBUG > 1 +#ifdef MXMLDEBUG printf(" Returning \"%s\"!\n", attr->value); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ return (attr->value); } } @@ -144,14 +137,56 @@ mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ * Didn't find attribute, so return NULL... */ -#if DEBUG > 1 +#ifdef MXMLDEBUG puts(" Returning NULL!\n"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ return (NULL); } +/* + * 'mxmlElementGetAttrByIndex()' - Get an element attribute by index. + * + * The index ("idx") is 0-based. @code NULL@ is returned if the specified index + * is out of range. + * + * @since Mini-XML 2.11@ + */ + +const char * /* O - Attribute value */ +mxmlElementGetAttrByIndex( + mxml_node_t *node, /* I - Node */ + int idx, /* I - Attribute index, starting at 0 */ + const char **name) /* O - Attribute name */ +{ + if (!node || node->type != MXML_ELEMENT || idx < 0 || idx >= node->value.element.num_attrs) + return (NULL); + + if (name) + *name = node->value.element.attrs[idx].name; + + return (node->value.element.attrs[idx].value); +} + + +/* + * 'mxmlElementGetAttrCount()' - Get the number of element attributes. + * + * @since Mini-XML 2.11@ + */ + +int /* O - Number of attributes */ +mxmlElementGetAttrCount( + mxml_node_t *node) /* I - Node */ +{ + if (node && node->type == MXML_ELEMENT) + return (node->value.element.num_attrs); + else + return (0); +} + + /* * 'mxmlElementSetAttr()' - Set an attribute. * @@ -169,10 +204,10 @@ mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */ char *valuec; /* Copy of value */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n", node, name ? name : "(null)", value ? value : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -187,7 +222,7 @@ mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */ valuec = NULL; if (mxml_set_attr(node, name, valuec)) - PhFree(valuec); + PhFree(valuec); } @@ -206,17 +241,17 @@ void mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */ const char *name, /* I - Name of attribute */ const char *format,/* I - Printf-style attribute value */ - ...) /* I - Additional arguments as needed */ + ...) /* I - Additional arguments as needed */ { va_list ap; /* Argument pointer */ char *value; /* Value */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n", node, name ? name : "(null)", format ? format : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -237,7 +272,7 @@ mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */ mxml_error("Unable to allocate memory for attribute '%s' in element %s!", name, node->value.element.name); else if (mxml_set_attr(node, name, value)) - PhFree(value); + PhFree(value); } @@ -251,7 +286,7 @@ mxml_set_attr(mxml_node_t *node, /* I - Element node */ char *value) /* I - Attribute value */ { int i; /* Looping var */ - mxml_attr_t *attr; /* New attribute */ + _mxml_attr_t *attr; /* New attribute */ /* @@ -268,7 +303,7 @@ mxml_set_attr(mxml_node_t *node, /* I - Element node */ */ if (attr->value) - PhFree(attr->value); + PhFree(attr->value); attr->value = value; @@ -280,10 +315,10 @@ mxml_set_attr(mxml_node_t *node, /* I - Element node */ */ if (node->value.element.num_attrs == 0) - attr = PhAllocateSafe(sizeof(mxml_attr_t)); + attr = PhAllocateSafe(sizeof(_mxml_attr_t)); else attr = PhReAllocateSafe(node->value.element.attrs, - (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t)); + (node->value.element.num_attrs + 1) * sizeof(_mxml_attr_t)); if (!attr) { @@ -308,8 +343,3 @@ mxml_set_attr(mxml_node_t *node, /* I - Element node */ return (0); } - - -/* - * End of "$Id: mxml-attr.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-entity.c b/phlib/mxml/mxml-entity.c similarity index 89% rename from ProcessHacker/mxml/mxml-entity.c rename to phlib/mxml/mxml-entity.c index b32e8a91ffef..3b17d1e06914 100644 --- a/ProcessHacker/mxml/mxml-entity.c +++ b/phlib/mxml/mxml-entity.c @@ -1,18 +1,12 @@ /* - * "$Id: mxml-entity.c 451 2014-01-04 21:50:06Z msweet $" + * Character entity support code for Mini-XML, a small XML file parsing library. * - * Character entity support code for Mini-XML, a small XML-like - * file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2014 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -31,7 +25,7 @@ mxmlEntityAddCallback( mxml_entity_cb_t cb) /* I - Callback function to add */ { _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0]))) @@ -53,10 +47,10 @@ mxmlEntityAddCallback( /* * 'mxmlEntityGetName()' - Get the name that corresponds to the character value. * - * If val does not need to be represented by a named entity, NULL is returned. + * If val does not need to be represented by a named entity, @code NULL@ is returned. */ -const char * /* O - Entity name or NULL */ +const char * /* O - Entity name or @code NULL@ */ mxmlEntityGetName(int val) /* I - Character value */ { switch (val) @@ -92,7 +86,7 @@ mxmlEntityGetValue(const char *name) /* I - Entity name */ int i; /* Looping var */ int ch; /* Character value */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ for (i = 0; i < global->num_entity_cbs; i ++) @@ -113,7 +107,7 @@ mxmlEntityRemoveCallback( { int i; /* Looping var */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ for (i = 0; i < global->num_entity_cbs; i ++) @@ -127,7 +121,7 @@ mxmlEntityRemoveCallback( if (i < global->num_entity_cbs) memmove(global->entity_cbs + i, global->entity_cbs + i + 1, - (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); + (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); return; } @@ -142,9 +136,9 @@ int /* O - Unicode value or -1 */ _mxml_entity_cb(const char *name) /* I - Entity name */ { int diff, /* Difference between names */ - current, /* Current entity in search */ - first, /* First entity in search */ - last; /* Last entity in search */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ static const struct { const char *name; /* Entity name */ @@ -442,8 +436,3 @@ _mxml_entity_cb(const char *name) /* I - Entity name */ else return (-1); } - - -/* - * End of "$Id: mxml-entity.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-file.c b/phlib/mxml/mxml-file.c similarity index 51% rename from ProcessHacker/mxml/mxml-file.c rename to phlib/mxml/mxml-file.c index d6306cb999d6..1ef0279efdf4 100644 --- a/ProcessHacker/mxml/mxml-file.c +++ b/phlib/mxml/mxml-file.c @@ -1,27 +1,21 @@ /* - * "$Id: mxml-file.c 467 2016-06-13 00:51:16Z msweet $" + * File loading code for Mini-XML, a small XML file parsing library. * - * File loading code for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2016 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* * Include necessary headers... */ -#include -#ifndef WIN32 +#ifndef _WIN32 # include -#endif /* !WIN32 */ +#endif /* !_WIN32 */ #include "mxml-private.h" @@ -52,8 +46,8 @@ typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/ { HANDLE fd; /* File descriptor */ unsigned char *current, /* Current position in buffer */ - *end, /* End of buffer */ - buffer[8192]; /* Character buffer */ + *end, /* End of buffer */ + buffer[8192]; /* Character buffer */ } _mxml_fdbuf_t; @@ -61,42 +55,26 @@ typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/ * Local functions... */ -static int mxml_add_char(int ch, char **ptr, char **buffer, - int *bufsize); +static int mxml_add_char(int ch, char **ptr, char **buffer, int *bufsize); static int mxml_fd_getc(void *p, int *encoding); static int mxml_fd_putc(int ch, void *p); static int mxml_fd_read(_mxml_fdbuf_t *buf); static int mxml_fd_write(_mxml_fdbuf_t *buf); static int mxml_file_getc(void *p, int *encoding); static int mxml_file_putc(int ch, void *p); -static int mxml_get_entity(mxml_node_t *parent, void *p, - int *encoding, - _mxml_getc_cb_t getc_cb); +static int mxml_get_entity(mxml_node_t *parent, void *p, int *encoding, _mxml_getc_cb_t getc_cb, int *line); static inline int mxml_isspace(int ch) - { - return (ch == ' ' || ch == '\t' || ch == '\r' || - ch == '\n'); - } -static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, - mxml_load_cb_t cb, - _mxml_getc_cb_t getc_cb, - mxml_sax_cb_t sax_cb, void *sax_data); -static int mxml_parse_element(mxml_node_t *node, void *p, - int *encoding, - _mxml_getc_cb_t getc_cb); + { + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); + } +static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, mxml_load_cb_t cb, _mxml_getc_cb_t getc_cb, mxml_sax_cb_t sax_cb, void *sax_data); +static int mxml_parse_element(mxml_node_t *node, void *p, int *encoding, _mxml_getc_cb_t getc_cb, int *line); static int mxml_string_getc(void *p, int *encoding); static int mxml_string_putc(int ch, void *p); -static int mxml_write_name(const char *s, void *p, - _mxml_putc_cb_t putc_cb); -static int mxml_write_node(mxml_node_t *node, void *p, - mxml_save_cb_t cb, int col, - _mxml_putc_cb_t putc_cb, - _mxml_global_t *global); -static int mxml_write_string(const char *s, void *p, - _mxml_putc_cb_t putc_cb); -static int mxml_write_ws(mxml_node_t *node, void *p, - mxml_save_cb_t cb, int ws, - int col, _mxml_putc_cb_t putc_cb); +static int mxml_write_name(const char *s, void *p, _mxml_putc_cb_t putc_cb); +static int mxml_write_node(mxml_node_t *node, void *p, mxml_save_cb_t cb, int col, _mxml_putc_cb_t putc_cb, _mxml_global_t *global); +static int mxml_write_string(const char *s, void *p, _mxml_putc_cb_t putc_cb); +static int mxml_write_ws(mxml_node_t *node, void *p, mxml_save_cb_t cb, int ws, int col, _mxml_putc_cb_t putc_cb); /* @@ -106,18 +84,21 @@ static int mxml_write_ws(mxml_node_t *node, void *p, * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. - * If MXML_NO_CALLBACK is specified then all child nodes will be either - * MXML_ELEMENT or MXML_TEXT nodes. + * The constants @code MXML_INTEGER_CALLBACK@, @code MXML_OPAQUE_CALLBACK@, + * @code MXML_REAL_CALLBACK@, and @code MXML_TEXT_CALLBACK@ are defined for + * loading child (data) nodes of the specified type. * - * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, - * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading - * child nodes of the specified type. + * Note: The most common programming error when using the Mini-XML library is + * to load an XML file using the @code MXML_TEXT_CALLBACK@, which returns inline + * text as a series of whitespace-delimited words, instead of using the + * @code MXML_OPAQUE_CALLBACK@ which returns the inline text as a single string + * (including whitespace). */ -mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_node_t * /* O - First node or @code NULL@ if the file could not be read. */ mxmlLoadFd(mxml_node_t *top, /* I - Top node */ HANDLE fd, /* I - File descriptor to read from */ - mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ + mxml_load_cb_t cb) /* I - Callback function or constant */ { _mxml_fdbuf_t buf; /* File descriptor buffer */ @@ -145,18 +126,21 @@ mxmlLoadFd(mxml_node_t *top, /* I - Top node */ * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. - * If MXML_NO_CALLBACK is specified then all child nodes will be either - * MXML_ELEMENT or MXML_TEXT nodes. + * The constants @code MXML_INTEGER_CALLBACK@, @code MXML_OPAQUE_CALLBACK@, + * @code MXML_REAL_CALLBACK@, and @code MXML_TEXT_CALLBACK@ are defined for + * loading child (data) nodes of the specified type. * - * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, - * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading - * child nodes of the specified type. + * Note: The most common programming error when using the Mini-XML library is + * to load an XML file using the @code MXML_TEXT_CALLBACK@, which returns inline + * text as a series of whitespace-delimited words, instead of using the + * @code MXML_OPAQUE_CALLBACK@ which returns the inline text as a single string + * (including whitespace). */ -mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_node_t * /* O - First node or @code NULL@ if the file could not be read. */ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ FILE *fp, /* I - File to read from */ - mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ + mxml_load_cb_t cb) /* I - Callback function or constant */ { /* * Read the XML data... @@ -173,18 +157,21 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ * If no top node is provided, the XML string MUST be well-formed with a * single parent node like for the entire string. The callback * function returns the value type that should be used for child nodes. - * If MXML_NO_CALLBACK is specified then all child nodes will be either - * MXML_ELEMENT or MXML_TEXT nodes. + * The constants @code MXML_INTEGER_CALLBACK@, @code MXML_OPAQUE_CALLBACK@, + * @code MXML_REAL_CALLBACK@, and @code MXML_TEXT_CALLBACK@ are defined for + * loading child (data) nodes of the specified type. * - * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, - * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading - * child nodes of the specified type. + * Note: The most common programming error when using the Mini-XML library is + * to load an XML file using the @code MXML_TEXT_CALLBACK@, which returns inline + * text as a series of whitespace-delimited words, instead of using the + * @code MXML_OPAQUE_CALLBACK@ which returns the inline text as a single string + * (including whitespace). */ -mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxml_node_t * /* O - First node or @code NULL@ if the string has errors. */ mxmlLoadString(mxml_node_t *top, /* I - Top node */ const char *s, /* I - String to load */ - mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ + mxml_load_cb_t cb) /* I - Callback function or constant */ { /* * Read the XML data... @@ -200,21 +187,21 @@ mxmlLoadString(mxml_node_t *top, /* I - Top node */ * * This function returns a pointer to a string containing the textual * representation of the XML node tree. The string should be freed - * using the free() function when you are done with it. NULL is returned + * using the free() function when you are done with it. @code NULL@ is returned * if the node would produce an empty string or if the string cannot be * allocated. * * The callback argument specifies a function that returns a whitespace - * string or NULL before and after each element. If MXML_NO_CALLBACK - * is specified, whitespace will only be added before MXML_TEXT nodes + * string or NULL before and after each element. If @code MXML_NO_CALLBACK@ + * is specified, whitespace will only be added before @code MXML_TEXT@ nodes * with leading whitespace and before attribute names inside opening * element tags. */ -char * /* O - Allocated string or NULL */ +char * /* O - Allocated string or @code NULL@ */ mxmlSaveAllocString( mxml_node_t *node, /* I - Node to write */ - mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ + mxml_save_cb_t cb) /* I - Whitespace callback or @code MXML_NO_CALLBACK@ */ { int bytes; /* Required bytes */ char buffer[8192]; /* Temporary buffer */ @@ -262,8 +249,8 @@ mxmlSaveAllocString( * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. * * The callback argument specifies a function that returns a whitespace - * string or NULL before and after each element. If MXML_NO_CALLBACK - * is specified, whitespace will only be added before MXML_TEXT nodes + * string or NULL before and after each element. If @code MXML_NO_CALLBACK@ + * is specified, whitespace will only be added before @code MXML_TEXT@ nodes * with leading whitespace and before attribute names inside opening * element tags. */ @@ -271,12 +258,12 @@ mxmlSaveAllocString( int /* O - 0 on success, -1 on error. */ mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ HANDLE fd, /* I - File descriptor to write to */ - mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ + mxml_save_cb_t cb) /* I - Whitespace callback or @code MXML_NO_CALLBACK@ */ { int col; /* Final column */ _mxml_fdbuf_t buf; /* File descriptor buffer */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ /* @@ -310,8 +297,8 @@ mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ * 'mxmlSaveFile()' - Save an XML tree to a file. * * The callback argument specifies a function that returns a whitespace - * string or NULL before and after each element. If MXML_NO_CALLBACK - * is specified, whitespace will only be added before MXML_TEXT nodes + * string or NULL before and after each element. If @code MXML_NO_CALLBACK@ + * is specified, whitespace will only be added before @code MXML_TEXT@ nodes * with leading whitespace and before attribute names inside opening * element tags. */ @@ -319,11 +306,11 @@ mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ int /* O - 0 on success, -1 on error. */ mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ FILE *fp, /* I - File to write to */ - mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ + mxml_save_cb_t cb) /* I - Whitespace callback or @code MXML_NO_CALLBACK@ */ { int col; /* Final column */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ /* @@ -353,8 +340,8 @@ mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ * into the specified buffer. * * The callback argument specifies a function that returns a whitespace - * string or NULL before and after each element. If MXML_NO_CALLBACK - * is specified, whitespace will only be added before MXML_TEXT nodes + * string or NULL before and after each element. If @code MXML_NO_CALLBACK@ + * is specified, whitespace will only be added before @code MXML_TEXT@ nodes * with leading whitespace and before attribute names inside opening * element tags. */ @@ -363,12 +350,12 @@ int /* O - Size of string */ mxmlSaveString(mxml_node_t *node, /* I - Node to write */ char *buffer, /* I - String buffer */ int bufsize, /* I - Size of string buffer */ - mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ + mxml_save_cb_t cb) /* I - Whitespace callback or @code MXML_NO_CALLBACK@ */ { int col; /* Final column */ char *ptr[2]; /* Pointers for putc_cb */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ /* @@ -397,7 +384,7 @@ mxmlSaveString(mxml_node_t *node, /* I - Node to write */ * Return the number of characters... */ - return (int)(ptr[0] - buffer); + return ((int)(ptr[0] - buffer)); } @@ -409,25 +396,22 @@ mxmlSaveString(mxml_node_t *node, /* I - Node to write */ * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. - * If MXML_NO_CALLBACK is specified then all child nodes will be either - * MXML_ELEMENT or MXML_TEXT nodes. - * - * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, - * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading - * child nodes of the specified type. + * The constants @code MXML_INTEGER_CALLBACK@, @code MXML_OPAQUE_CALLBACK@, + * @code MXML_REAL_CALLBACK@, and @code MXML_TEXT_CALLBACK@ are defined for + * loading child nodes of the specified type. * - * The SAX callback must call mxmlRetain() for any nodes that need to + * The SAX callback must call @link mxmlRetain@ for any nodes that need to * be kept for later use. Otherwise, nodes are deleted when the parent * node is closed or after each data, comment, CDATA, or directive node. * * @since Mini-XML 2.3@ */ -mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_node_t * /* O - First node or @code NULL@ if the file could not be read. */ mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */ HANDLE fd, /* I - File descriptor to read from */ - mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ - mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + mxml_load_cb_t cb, /* I - Callback function or constant */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or @code MXML_NO_CALLBACK@ */ void *sax_data) /* I - SAX user data */ { _mxml_fdbuf_t buf; /* File descriptor buffer */ @@ -457,26 +441,23 @@ mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */ * If no top node is provided, the XML file MUST be well-formed with a * single parent node like for the entire file. The callback * function returns the value type that should be used for child nodes. - * If MXML_NO_CALLBACK is specified then all child nodes will be either - * MXML_ELEMENT or MXML_TEXT nodes. + * The constants @code MXML_INTEGER_CALLBACK@, @code MXML_OPAQUE_CALLBACK@, + * @code MXML_REAL_CALLBACK@, and @code MXML_TEXT_CALLBACK@ are defined for + * loading child nodes of the specified type. * - * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, - * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading - * child nodes of the specified type. - * - * The SAX callback must call mxmlRetain() for any nodes that need to + * The SAX callback must call @link mxmlRetain@ for any nodes that need to * be kept for later use. Otherwise, nodes are deleted when the parent * node is closed or after each data, comment, CDATA, or directive node. * * @since Mini-XML 2.3@ */ -mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_node_t * /* O - First node or @code NULL@ if the file could not be read. */ mxmlSAXLoadFile( mxml_node_t *top, /* I - Top node */ FILE *fp, /* I - File to read from */ - mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ - mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + mxml_load_cb_t cb, /* I - Callback function or constant */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or @code MXML_NO_CALLBACK@ */ void *sax_data) /* I - SAX user data */ { /* @@ -495,26 +476,23 @@ mxmlSAXLoadFile( * If no top node is provided, the XML string MUST be well-formed with a * single parent node like for the entire string. The callback * function returns the value type that should be used for child nodes. - * If MXML_NO_CALLBACK is specified then all child nodes will be either - * MXML_ELEMENT or MXML_TEXT nodes. - * - * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, - * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading - * child nodes of the specified type. + * The constants @code MXML_INTEGER_CALLBACK@, @code MXML_OPAQUE_CALLBACK@, + * @code MXML_REAL_CALLBACK@, and @code MXML_TEXT_CALLBACK@ are defined for + * loading child nodes of the specified type. * - * The SAX callback must call mxmlRetain() for any nodes that need to + * The SAX callback must call @link mxmlRetain@ for any nodes that need to * be kept for later use. Otherwise, nodes are deleted when the parent * node is closed or after each data, comment, CDATA, or directive node. * * @since Mini-XML 2.3@ */ -mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxml_node_t * /* O - First node or @code NULL@ if the string has errors. */ mxmlSAXLoadString( mxml_node_t *top, /* I - Top node */ const char *s, /* I - String to load */ - mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ - mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + mxml_load_cb_t cb, /* I - Callback function or constant */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or @code MXML_NO_CALLBACK@ */ void *sax_data) /* I - SAX user data */ { /* @@ -532,7 +510,7 @@ mxmlSAXLoadString( * return 0 on success and non-zero on error. * * The save function accepts a node pointer and must return a malloc'd - * string on success and NULL on error. + * string on success and @code NULL@ on error. * */ @@ -542,7 +520,7 @@ mxmlSetCustomHandlers( mxml_custom_save_cb_t save) /* I - Save function */ { _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ global->custom_load_cb = load; @@ -558,7 +536,7 @@ void mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */ { _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ global->error_cb = cb; @@ -577,7 +555,7 @@ void mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */ { _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ global->wrap = column; @@ -591,8 +569,8 @@ mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping static int /* O - 0 on success, -1 on error */ mxml_add_char(int ch, /* I - Character to add */ char **bufptr, /* IO - Current position in buffer */ - char **buffer, /* IO - Current buffer */ - int *bufsize) /* IO - Current buffer size */ + char **buffer, /* IO - Current buffer */ + int *bufsize) /* IO - Current buffer size */ { char *newbuffer; /* New buffer value */ @@ -674,7 +652,7 @@ mxml_fd_getc(void *p, /* I - File descriptor buffer */ { _mxml_fdbuf_t *buf; /* File descriptor buffer */ int ch, /* Current character */ - temp; /* Temporary character */ + temp; /* Temporary character */ /* @@ -693,270 +671,270 @@ mxml_fd_getc(void *p, /* I - File descriptor buffer */ { case ENCODE_UTF8 : /* - * Got a UTF-8 character; convert UTF-8 to Unicode and return... - */ + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ - if (!(ch & 0x80)) - { + if (!(ch & 0x80)) + { #if DEBUG > 1 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } - return (ch); + return (ch); } - else if (ch == 0xfe) - { - /* - * UTF-16 big-endian BOM? - */ + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - ch = *(buf->current)++; + ch = *(buf->current)++; - if (ch != 0xff) - return (EOF); + if (ch != 0xff) + return (EOF); - *encoding = ENCODE_UTF16BE; + *encoding = ENCODE_UTF16BE; - return (mxml_fd_getc(p, encoding)); - } - else if (ch == 0xff) - { - /* - * UTF-16 little-endian BOM? - */ + return (mxml_fd_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - ch = *(buf->current)++; + ch = *(buf->current)++; - if (ch != 0xfe) - return (EOF); + if (ch != 0xfe) + return (EOF); - *encoding = ENCODE_UTF16LE; + *encoding = ENCODE_UTF16LE; - return (mxml_fd_getc(p, encoding)); - } - else if ((ch & 0xe0) == 0xc0) - { - /* - * Two-byte value... - */ + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - if ((temp & 0xc0) != 0x80) - return (EOF); + if ((temp & 0xc0) != 0x80) + return (EOF); - ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); - if (ch < 0x80) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } - } - else if ((ch & 0xf0) == 0xe0) - { - /* - * Three-byte value... - */ + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - if ((temp & 0xc0) != 0x80) - return (EOF); + if ((temp & 0xc0) != 0x80) + return (EOF); - ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - if ((temp & 0xc0) != 0x80) - return (EOF); + if ((temp & 0xc0) != 0x80) + return (EOF); - ch = (ch << 6) | (temp & 0x3f); + ch = (ch << 6) | (temp & 0x3f); - if (ch < 0x800) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } /* - * Ignore (strip) Byte Order Mark (BOM)... - */ + * Ignore (strip) Byte Order Mark (BOM)... + */ - if (ch == 0xfeff) - return (mxml_fd_getc(p, encoding)); - } - else if ((ch & 0xf8) == 0xf0) - { - /* - * Four-byte value... - */ + if (ch == 0xfeff) + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - if ((temp & 0xc0) != 0x80) - return (EOF); + if ((temp & 0xc0) != 0x80) + return (EOF); - ch = ((ch & 0x07) << 6) | (temp & 0x3f); + ch = ((ch & 0x07) << 6) | (temp & 0x3f); - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - if ((temp & 0xc0) != 0x80) - return (EOF); + if ((temp & 0xc0) != 0x80) + return (EOF); - ch = (ch << 6) | (temp & 0x3f); + ch = (ch << 6) | (temp & 0x3f); - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - if ((temp & 0xc0) != 0x80) - return (EOF); + if ((temp & 0xc0) != 0x80) + return (EOF); - ch = (ch << 6) | (temp & 0x3f); + ch = (ch << 6) | (temp & 0x3f); - if (ch < 0x10000) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } - } - else - return (EOF); - break; + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; case ENCODE_UTF16BE : /* * Read UTF-16 big-endian char... - */ + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - ch = (ch << 8) | temp; + ch = (ch << 8) | temp; - if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } else if (ch >= 0xd800 && ch <= 0xdbff) - { - /* - * Multi-word UTF-16 char... - */ + { + /* + * Multi-word UTF-16 char... + */ int lch; - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - lch = *(buf->current)++; + lch = *(buf->current)++; - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - lch = (lch << 8) | temp; + lch = (lch << 8) | temp; if (lch < 0xdc00 || lch >= 0xdfff) - return (EOF); + return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } - break; + } + break; case ENCODE_UTF16LE : /* * Read UTF-16 little-endian char... - */ + */ - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - ch |= (temp << 8); + ch |= (temp << 8); if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } else if (ch >= 0xd800 && ch <= 0xdbff) - { - /* - * Multi-word UTF-16 char... - */ + { + /* + * Multi-word UTF-16 char... + */ int lch; - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - lch = *(buf->current)++; + lch = *(buf->current)++; - if (buf->current >= buf->end) - if (mxml_fd_read(buf) < 0) - return (EOF); + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); - temp = *(buf->current)++; + temp = *(buf->current)++; - lch |= (temp << 8); + lch |= (temp << 8); if (lch < 0xdc00 || lch >= 0xdfff) - return (EOF); + return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } - break; + } + break; } #if DEBUG > 1 @@ -1001,7 +979,6 @@ mxml_fd_putc(int ch, /* I - Character */ /* * 'mxml_fd_read()' - Read a buffer of data from a file descriptor. */ - /* wj32: modified to use file handles */ static int /* O - 0 on success, -1 on error */ mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ @@ -1027,7 +1004,6 @@ mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ /* * 'mxml_fd_write()' - Write a buffer of data to a file descriptor. */ - /* wj32: modified to use file handles */ static int /* O - 0 on success, -1 on error */ mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ @@ -1035,7 +1011,7 @@ mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ IO_STATUS_BLOCK isb; if (!buf) - return 1; + return -1; if (buf->current == buf->buffer) return 0; @@ -1058,7 +1034,7 @@ mxml_file_getc(void *p, /* I - Pointer to file */ int *encoding) /* IO - Encoding */ { int ch, /* Character from file */ - temp; /* Temporary character */ + temp; /* Temporary character */ FILE *fp; /* Pointer to file */ @@ -1076,186 +1052,186 @@ mxml_file_getc(void *p, /* I - Pointer to file */ { case ENCODE_UTF8 : /* - * Got a UTF-8 character; convert UTF-8 to Unicode and return... - */ - - if (!(ch & 0x80)) - { - if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } #if DEBUG > 1 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - return (ch); + return (ch); } - else if (ch == 0xfe) - { - /* - * UTF-16 big-endian BOM? - */ + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ ch = getc(fp); - if (ch != 0xff) - return (EOF); + if (ch != 0xff) + return (EOF); - *encoding = ENCODE_UTF16BE; + *encoding = ENCODE_UTF16BE; - return (mxml_file_getc(p, encoding)); - } - else if (ch == 0xff) - { - /* - * UTF-16 little-endian BOM? - */ + return (mxml_file_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ ch = getc(fp); - if (ch != 0xfe) - return (EOF); + if (ch != 0xfe) + return (EOF); - *encoding = ENCODE_UTF16LE; + *encoding = ENCODE_UTF16LE; - return (mxml_file_getc(p, encoding)); - } - else if ((ch & 0xe0) == 0xc0) - { - /* - * Two-byte value... - */ + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ - if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) - return (EOF); + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); - ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); - if (ch < 0x80) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } - } - else if ((ch & 0xf0) == 0xe0) - { - /* - * Three-byte value... - */ + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ - if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) - return (EOF); + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); - ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); - if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) - return (EOF); + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); - ch = (ch << 6) | (temp & 0x3f); + ch = (ch << 6) | (temp & 0x3f); - if (ch < 0x800) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } /* - * Ignore (strip) Byte Order Mark (BOM)... - */ + * Ignore (strip) Byte Order Mark (BOM)... + */ - if (ch == 0xfeff) - return (mxml_file_getc(p, encoding)); - } - else if ((ch & 0xf8) == 0xf0) - { - /* - * Four-byte value... - */ + if (ch == 0xfeff) + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ - if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) - return (EOF); + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); - ch = ((ch & 0x07) << 6) | (temp & 0x3f); + ch = ((ch & 0x07) << 6) | (temp & 0x3f); - if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) - return (EOF); + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); - ch = (ch << 6) | (temp & 0x3f); + ch = (ch << 6) | (temp & 0x3f); - if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) - return (EOF); + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); - ch = (ch << 6) | (temp & 0x3f); + ch = (ch << 6) | (temp & 0x3f); - if (ch < 0x10000) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } - } - else - return (EOF); - break; + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; case ENCODE_UTF16BE : /* * Read UTF-16 big-endian char... - */ + */ - ch = (ch << 8) | getc(fp); + ch = (ch << 8) | getc(fp); - if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } else if (ch >= 0xd800 && ch <= 0xdbff) - { - /* - * Multi-word UTF-16 char... - */ + { + /* + * Multi-word UTF-16 char... + */ int lch = getc(fp); lch = (lch << 8) | getc(fp); if (lch < 0xdc00 || lch >= 0xdfff) - return (EOF); + return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } - break; + } + break; case ENCODE_UTF16LE : /* * Read UTF-16 little-endian char... - */ + */ - ch |= (getc(fp) << 8); + ch |= (getc(fp) << 8); if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } else if (ch >= 0xd800 && ch <= 0xdbff) - { - /* - * Multi-word UTF-16 char... - */ + { + /* + * Multi-word UTF-16 char... + */ int lch = getc(fp); lch |= (getc(fp) << 8); if (lch < 0xdc00 || lch >= 0xdfff) - return (EOF); + return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } - break; + } + break; } #if DEBUG > 1 @@ -1284,54 +1260,57 @@ mxml_file_putc(int ch, /* I - Character to write */ static int /* O - Character value or EOF on error */ mxml_get_entity(mxml_node_t *parent, /* I - Parent node */ - void *p, /* I - Pointer to source */ - int *encoding, /* IO - Character encoding */ - int (*getc_cb)(void *, int *)) - /* I - Get character function */ + void *p, /* I - Pointer to source */ + int *encoding, /* IO - Character encoding */ + int (*getc_cb)(void *, int *), + /* I - Get character function */ + int *line) /* IO - Current line number */ { int ch; /* Current character */ char entity[64], /* Entity string */ - *entptr; /* Pointer into entity */ + *entptr; /* Pointer into entity */ entptr = entity; while ((ch = (*getc_cb)(p, encoding)) != EOF) + { if (ch > 126 || (!isalnum(ch) && ch != '#')) break; else if (entptr < (entity + sizeof(entity) - 1)) *entptr++ = ch; else { - mxml_error("Entity name too long under parent <%s>!", - parent ? parent->value.element.name : "null"); + mxml_error("Entity name too long under parent <%s> on line %d.", parent ? parent->value.element.name : "null", *line); break; } + } *entptr = '\0'; if (ch != ';') { - mxml_error("Character entity \"%s\" not terminated under parent <%s>!", - entity, parent ? parent->value.element.name : "null"); + mxml_error("Character entity '%s' not terminated under parent <%s> on line %d.", entity, parent ? parent->value.element.name : "null", *line); + + if (ch == '\n') + (*line)++; + return (EOF); } if (entity[0] == '#') { if (entity[1] == 'x') - ch = strtol(entity + 2, NULL, 16); + ch = (int)strtol(entity + 2, NULL, 16); else - ch = strtol(entity + 1, NULL, 10); + ch = (int)strtol(entity + 1, NULL, 10); } else if ((ch = mxmlEntityGetValue(entity)) < 0) - mxml_error("Entity name \"%s;\" not supported under parent <%s>!", - entity, parent ? parent->value.element.name : "null"); + mxml_error("Entity name '%s;' not supported under parent <%s> on line %d.", entity, parent ? parent->value.element.name : "null", *line); if (mxml_bad_char(ch)) { - mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!", - ch, parent ? parent->value.element.name : "null"); + mxml_error("Bad control character 0x%02x under parent <%s> on line %d not allowed by XML standard.", ch, parent ? parent->value.element.name : "null", *line); return (EOF); } @@ -1353,26 +1332,27 @@ mxml_load_data( void *sax_data) /* I - SAX user data */ { mxml_node_t *node, /* Current node */ - *first, /* First node added */ - *parent; /* Current parent node */ - int ch, /* Character from file */ - whitespace; /* Non-zero if whitespace seen */ + *first, /* First node added */ + *parent; /* Current parent node */ + int line = 1, /* Current line number */ + ch, /* Character from file */ + whitespace; /* Non-zero if whitespace seen */ char *buffer, /* String buffer */ - *bufptr; /* Pointer into buffer */ + *bufptr; /* Pointer into buffer */ int bufsize; /* Size of buffer */ mxml_type_t type; /* Current node type */ int encoding; /* Character encoding */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ static const char * const types[] = /* Type strings... */ - { - "MXML_ELEMENT", /* XML element with attributes */ - "MXML_INTEGER", /* Integer value */ - "MXML_OPAQUE", /* Opaque string */ - "MXML_REAL", /* Real value */ - "MXML_TEXT", /* Text fragment */ - "MXML_CUSTOM" /* Custom data */ - }; + { + "MXML_ELEMENT", /* XML element with attributes */ + "MXML_INTEGER", /* Integer value */ + "MXML_OPAQUE", /* Opaque string */ + "MXML_REAL", /* Real value */ + "MXML_TEXT", /* Text fragment */ + "MXML_CUSTOM" /* Custom data */ + }; /* @@ -1413,56 +1393,53 @@ mxml_load_data( switch (type) { - case MXML_INTEGER : - node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0)); - break; + case MXML_INTEGER : + node = mxmlNewInteger(parent, (int)strtol(buffer, &bufptr, 0)); + break; - case MXML_OPAQUE : + case MXML_OPAQUE : node = mxmlNewOpaque(parent, buffer); - break; + break; - case MXML_REAL : + case MXML_REAL : node = mxmlNewReal(parent, strtod(buffer, &bufptr)); - break; + break; - case MXML_TEXT : + case MXML_TEXT : node = mxmlNewText(parent, whitespace, buffer); - break; + break; - case MXML_CUSTOM : - if (global->custom_load_cb) - { - /* - * Use the callback to fill in the custom data... - */ + case MXML_CUSTOM : + if (global->custom_load_cb) + { + /* + * Use the callback to fill in the custom data... + */ node = mxmlNewCustom(parent, NULL, NULL); - if ((*global->custom_load_cb)(node, buffer)) - { - mxml_error("Bad custom value '%s' in parent <%s>!", - buffer, parent ? parent->value.element.name : "null"); - mxmlDelete(node); - node = NULL; - } - break; - } + if ((*global->custom_load_cb)(node, buffer)) + { + mxml_error("Bad custom value '%s' in parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "null", line); + mxmlDelete(node); + node = NULL; + } + break; + } default : /* Ignore... */ - node = NULL; - break; + node = NULL; + break; } if (*bufptr) { /* * Bad integer/real number value... - */ + */ - mxml_error("Bad %s value '%s' in parent <%s>!", - type == MXML_INTEGER ? "integer" : "real", buffer, - parent ? parent->value.element.name : "null"); - break; + mxml_error("Bad %s value '%s' in parent <%s> on line %d.", type == MXML_INTEGER ? "integer" : "real", buffer, parent ? parent->value.element.name : "null", line); + break; } bufptr = buffer; @@ -1471,12 +1448,11 @@ mxml_load_data( if (!node && type != MXML_IGNORE) { /* - * Print error and return... - */ + * Print error and return... + */ - mxml_error("Unable to add value node of type %s to parent <%s>!", - types[type], parent ? parent->value.element.name : "null"); - goto error; + mxml_error("Unable to add value node of type %s to parent <%s> on line %d.", types[type], parent ? parent->value.element.name : "null", line); + goto error; } if (sax_cb) @@ -1493,6 +1469,9 @@ mxml_load_data( else if (mxml_isspace(ch) && type == MXML_TEXT) whitespace = 1; + if (ch == '\n') + line ++; + /* * Add lone whitespace node if we have an element and existing * whitespace... @@ -1502,18 +1481,18 @@ mxml_load_data( { if (parent) { - node = mxmlNewText(parent, whitespace, ""); + node = mxmlNewText(parent, whitespace, ""); - if (sax_cb) - { - (*sax_cb)(node, MXML_SAX_DATA, sax_data); + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); - if (!mxmlRelease(node)) - node = NULL; - } + if (!mxmlRelease(node)) + node = NULL; + } - if (!first && node) - first = node; + if (!first && node) + first = node; } whitespace = 0; @@ -1528,29 +1507,34 @@ mxml_load_data( bufptr = buffer; while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) - break; - else if (ch == '<') - { - mxml_error("Bare < in element!"); - goto error; - } - else if (ch == '&') - { - if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) - goto error; - - if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; - } - else if (ch < '0' && ch != '!' && ch != '-' && ch != '.' && ch != '/') - goto error; - else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; - else if (((bufptr - buffer) == 1 && buffer[0] == '?') || - ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || - ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) - break; + break; + else if (ch == '<') + { + mxml_error("Bare < in element!"); + goto error; + } + else if (ch == '&') + { + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb, &line)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (ch < '0' && ch != '!' && ch != '-' && ch != '.' && ch != '/') + goto error; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + else if (((bufptr - buffer) == 1 && buffer[0] == '?') || + ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || + ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) + break; + + if (ch == '\n') + line ++; + } *bufptr = '\0'; @@ -1558,59 +1542,60 @@ mxml_load_data( { /* * Gather rest of comment... - */ + */ - while ((ch = (*getc_cb)(p, &encoding)) != EOF) - { - if (ch == '>' && bufptr > (buffer + 4) && - bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') - break; - else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; - } + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > (buffer + 4) && + bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + + if (ch == '\n') + line ++; + } /* * Error out if we didn't get the whole comment... - */ + */ if (ch != '>') - { - /* - * Print error and return... - */ + { + /* + * Print error and return... + */ - mxml_error("Early EOF in comment node!"); - goto error; - } + mxml_error("Early EOF in comment node on line %d.", line); + goto error; + } /* * Otherwise add this as an element under the current parent... - */ + */ - *bufptr = '\0'; + *bufptr = '\0'; if (!parent && first) - { - /* - * There can only be one root element! - */ + { + /* + * There can only be one root element! + */ - mxml_error("<%s> cannot be a second root node after <%s>", - buffer, first->value.element.name); + mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; - } + } - if ((node = mxmlNewElement(parent, buffer)) == NULL) - { - /* - * Just print error for now... - */ + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ - mxml_error("Unable to add comment node to parent <%s>!", - parent ? parent->value.element.name : "null"); - break; - } + mxml_error("Unable to add comment node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + break; + } if (sax_cb) { @@ -1620,65 +1605,73 @@ mxml_load_data( node = NULL; } - if (node && !first) - first = node; + if (node && !first) + first = node; } else if (!strcmp(buffer, "![CDATA[")) { /* * Gather CDATA section... - */ - - while ((ch = (*getc_cb)(p, &encoding)) != EOF) - { - if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) - break; - else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; - } + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) + { + /* + * Drop terminator from CDATA string... + */ + + bufptr[-2] = '\0'; + break; + } + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + + if (ch == '\n') + line ++; + } /* * Error out if we didn't get the whole comment... - */ + */ if (ch != '>') - { - /* - * Print error and return... - */ + { + /* + * Print error and return... + */ - mxml_error("Early EOF in CDATA node!"); - goto error; - } + mxml_error("Early EOF in CDATA node on line %d.", line); + goto error; + } /* * Otherwise add this as an element under the current parent... - */ + */ - *bufptr = '\0'; + *bufptr = '\0'; if (!parent && first) - { - /* - * There can only be one root element! - */ + { + /* + * There can only be one root element! + */ - mxml_error("<%s> cannot be a second root node after <%s>", - buffer, first->value.element.name); + mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; - } + } - if ((node = mxmlNewElement(parent, buffer)) == NULL) - { - /* - * Print error and return... - */ + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ - mxml_error("Unable to add CDATA node to parent <%s>!", - parent ? parent->value.element.name : "null"); - goto error; - } + mxml_error("Unable to add CDATA node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + goto error; + } if (sax_cb) { @@ -1688,64 +1681,65 @@ mxml_load_data( node = NULL; } - if (node && !first) - first = node; + if (node && !first) + first = node; } else if (buffer[0] == '?') { /* * Gather rest of processing instruction... - */ + */ - while ((ch = (*getc_cb)(p, &encoding)) != EOF) - { - if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') - break; - else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; - } + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + + if (ch == '\n') + line ++; + } /* * Error out if we didn't get the whole processing instruction... - */ + */ if (ch != '>') - { - /* - * Print error and return... - */ + { + /* + * Print error and return... + */ - mxml_error("Early EOF in processing instruction node!"); - goto error; - } + mxml_error("Early EOF in processing instruction node on line %d.", line); + goto error; + } /* * Otherwise add this as an element under the current parent... - */ + */ - *bufptr = '\0'; + *bufptr = '\0'; if (!parent && first) - { - /* - * There can only be one root element! - */ + { + /* + * There can only be one root element! + */ - mxml_error("<%s> cannot be a second root node after <%s>", - buffer, first->value.element.name); + mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; - } + } - if ((node = mxmlNewElement(parent, buffer)) == NULL) - { - /* - * Print error and return... - */ + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ - mxml_error("Unable to add processing instruction node to parent <%s>!", - parent ? parent->value.element.name : "null"); - goto error; - } + mxml_error("Unable to add processing instruction node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + goto error; + } if (sax_cb) { @@ -1756,84 +1750,87 @@ mxml_load_data( } if (node) - { - if (!first) + { + if (!first) first = node; - if (!parent) - { - parent = node; + if (!parent) + { + parent = node; - if (cb) - type = (*cb)(parent); - else - type = MXML_TEXT; - } - } + if (cb) + type = (*cb)(parent); + else + type = MXML_TEXT; + } + } } else if (buffer[0] == '!') { /* * Gather rest of declaration... - */ - - do - { - if (ch == '>') - break; - else - { + */ + + do + { + if (ch == '>') + break; + else + { if (ch == '&') - if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) - goto error; + { + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb, &line)) == EOF) + goto error; + } - if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; - } - } + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + if (ch == '\n') + line ++; + } while ((ch = (*getc_cb)(p, &encoding)) != EOF); /* * Error out if we didn't get the whole declaration... - */ + */ if (ch != '>') - { - /* - * Print error and return... - */ + { + /* + * Print error and return... + */ - mxml_error("Early EOF in declaration node!"); - goto error; - } + mxml_error("Early EOF in declaration node on line %d.", line); + goto error; + } /* * Otherwise add this as an element under the current parent... - */ + */ - *bufptr = '\0'; + *bufptr = '\0'; if (!parent && first) - { - /* - * There can only be one root element! - */ + { + /* + * There can only be one root element! + */ - mxml_error("<%s> cannot be a second root node after <%s>", - buffer, first->value.element.name); + mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; - } + } - if ((node = mxmlNewElement(parent, buffer)) == NULL) - { - /* - * Print error and return... - */ + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ - mxml_error("Unable to add declaration node to parent <%s>!", - parent ? parent->value.element.name : "null"); - goto error; - } + mxml_error("Unable to add declaration node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + goto error; + } if (sax_cb) { @@ -1844,44 +1841,43 @@ mxml_load_data( } if (node) - { - if (!first) + { + if (!first) first = node; - if (!parent) - { - parent = node; + if (!parent) + { + parent = node; - if (cb) - type = (*cb)(parent); - else - type = MXML_TEXT; - } - } + if (cb) + type = (*cb)(parent); + else + type = MXML_TEXT; + } + } } else if (buffer[0] == '/') { /* * Handle close tag... - */ + */ if (!parent || strcmp(buffer + 1, parent->value.element.name)) - { - /* - * Close tag doesn't match tree; print an error for now... - */ + { + /* + * Close tag doesn't match tree; print an error for now... + */ - mxml_error("Mismatched close tag <%s> under parent <%s>!", - buffer, parent ? parent->value.element.name : "(null)"); + mxml_error("Mismatched close tag <%s> under parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "(null)", line); goto error; - } + } /* * Keep reading until we see >... - */ + */ while (ch != '>' && ch != EOF) - ch = (*getc_cb)(p, &encoding); + ch = (*getc_cb)(p, &encoding); node = parent; parent = parent->parent; @@ -1891,84 +1887,81 @@ mxml_load_data( (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); if (!mxmlRelease(node) && first == node) - first = NULL; + first = NULL; } /* - * Ascend into the parent and set the value type as needed... - */ + * Ascend into the parent and set the value type as needed... + */ - if (cb && parent) - type = (*cb)(parent); + if (cb && parent) + type = (*cb)(parent); } else { /* * Handle open tag... - */ + */ if (!parent && first) - { - /* - * There can only be one root element! - */ + { + /* + * There can only be one root element! + */ - mxml_error("<%s> cannot be a second root node after <%s>", - buffer, first->value.element.name); + mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; - } + } if ((node = mxmlNewElement(parent, buffer)) == NULL) - { - /* - * Just print error for now... - */ + { + /* + * Just print error for now... + */ - mxml_error("Unable to add element node to parent <%s>!", - parent ? parent->value.element.name : "null"); - goto error; - } + mxml_error("Unable to add element node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + goto error; + } if (mxml_isspace(ch)) { - if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF) - goto error; + if ((ch = mxml_parse_element(node, p, &encoding, getc_cb, &line)) == EOF) + goto error; } else if (ch == '/') - { - if ((ch = (*getc_cb)(p, &encoding)) != '>') - { - mxml_error("Expected > but got '%c' instead for element <%s/>!", - ch, buffer); + { + if ((ch = (*getc_cb)(p, &encoding)) != '>') + { + mxml_error("Expected > but got '%c' instead for element <%s/> on line %d.", ch, buffer, line); mxmlDelete(node); goto error; - } + } - ch = '/'; - } + ch = '/'; + } if (sax_cb) (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data); if (!first) - first = node; + first = node; - if (ch == EOF) - break; + if (ch == EOF) + break; if (ch != '/') - { - /* - * Descend into this node, setting the value type as needed... - */ - - parent = node; - - if (cb && parent) - type = (*cb)(parent); - else - type = MXML_TEXT; - } + { + /* + * Descend into this node, setting the value type as needed... + */ + + parent = node; + + if (cb && parent) + type = (*cb)(parent); + else + type = MXML_TEXT; + } else if (sax_cb) { (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); @@ -1986,11 +1979,11 @@ mxml_load_data( * Add character entity to current buffer... */ - if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) - goto error; + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb, &line)) == EOF) + goto error; if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; + goto error; } else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch)) { @@ -1999,7 +1992,7 @@ mxml_load_data( */ if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) - goto error; + goto error; } } @@ -2022,9 +2015,7 @@ mxml_load_data( if (node != parent) { - mxml_error("Missing close tag under parent <%s>!", - node->value.element.name, - node->parent ? node->parent->value.element.name : "(null)"); + mxml_error("Missing close tag under parent <%s> on line %d.", node->value.element.name, node->parent ? node->parent->value.element.name : "(null)", line); mxmlDelete(first); @@ -2041,7 +2032,7 @@ mxml_load_data( * Common error return... */ -error: + error: mxmlDelete(first); @@ -2060,15 +2051,16 @@ mxml_parse_element( mxml_node_t *node, /* I - Element node */ void *p, /* I - Data to read from */ int *encoding, /* IO - Encoding */ - _mxml_getc_cb_t getc_cb) /* I - Data callback */ + _mxml_getc_cb_t getc_cb, /* I - Data callback */ + int *line) /* IO - Current line number */ { int ch, /* Current character in file */ - quote; /* Quoting character */ + quote; /* Quoting character */ char *name, /* Attribute name */ - *value, /* Attribute value */ - *ptr; /* Pointer into name/value */ + *value, /* Attribute value */ + *ptr; /* Pointer into name/value */ int namesize, /* Size of name string */ - valsize; /* Size of value string */ + valsize; /* Size of value string */ /* @@ -2107,7 +2099,12 @@ mxml_parse_element( */ if (mxml_isspace(ch)) + { + if (ch == '\n') + (*line)++; + continue; + } /* * Stop at /, ?, or >... @@ -2123,8 +2120,7 @@ mxml_parse_element( if (quote != '>') { - mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", - ch, node->value.element.name, quote); + mxml_error("Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, node->value.element.name, quote, *line); goto error; } @@ -2132,7 +2128,7 @@ mxml_parse_element( } else if (ch == '<') { - mxml_error("Bare < in element %s!", node->value.element.name); + mxml_error("Bare < in element %s on line %d.", node->value.element.name, *line); goto error; } else if (ch == '>') @@ -2156,13 +2152,17 @@ mxml_parse_element( while ((ch = (*getc_cb)(p, encoding)) != EOF) { if (ch == '&') - if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) - goto error; + { + if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF) + goto error; + } + else if (ch == '\n') + (*line)++; - if (mxml_add_char(ch, &ptr, &name, &namesize)) - goto error; + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; - if (ch == quote) + if (ch == quote) break; } } @@ -2173,40 +2173,59 @@ mxml_parse_element( */ while ((ch = (*getc_cb)(p, encoding)) != EOF) - if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || - ch == '?') + { + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || + ch == '?') + { + if (ch == '\n') + (*line)++; break; - else - { + } + else + { if (ch == '&') - if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) - goto error; + { + if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF) + goto error; + } - if (mxml_add_char(ch, &ptr, &name, &namesize)) - goto error; - } + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + } + } } *ptr = '\0'; if (mxmlElementGetAttr(node, name)) + { + mxml_error("Duplicate attribute '%s' in element %s on line %d.", name, node->value.element.name, name, *line); goto error; + } while (ch != EOF && mxml_isspace(ch)) + { ch = (*getc_cb)(p, encoding); + if (ch == '\n') + (*line)++; + } + if (ch == '=') { /* * Read the attribute value... */ - while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch)); + while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch)) + { + if (ch == '\n') + (*line)++; + } if (ch == EOF) { - mxml_error("Missing value for attribute '%s' in element %s!", - name, node->value.element.name); + mxml_error("Missing value for attribute '%s' in element %s on line %d.", name, node->value.element.name, *line); goto error; } @@ -2214,23 +2233,31 @@ mxml_parse_element( { /* * Read quoted value... - */ + */ quote = ch; - ptr = value; + ptr = value; while ((ch = (*getc_cb)(p, encoding)) != EOF) - if (ch == quote) - break; - else - { - if (ch == '&') - if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) - goto error; - - if (mxml_add_char(ch, &ptr, &value, &valsize)) - goto error; - } + { + if (ch == quote) + { + break; + } + else + { + if (ch == '&') + { + if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF) + goto error; + } + else if (ch == '\n') + (*line)++; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + } *ptr = '\0'; } @@ -2238,23 +2265,32 @@ mxml_parse_element( { /* * Read unquoted value... - */ + */ - value[0] = ch; - ptr = value + 1; + value[0] = ch; + ptr = value + 1; - while ((ch = (*getc_cb)(p, encoding)) != EOF) - if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') - break; - else - { - if (ch == '&') - if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) - goto error; + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') + { + if (ch == '\n') + (*line)++; - if (mxml_add_char(ch, &ptr, &value, &valsize)) - goto error; - } + break; + } + else + { + if (ch == '&') + { + if ((ch = mxml_get_entity(node, p, encoding, getc_cb, line)) == EOF) + goto error; + } + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + } *ptr = '\0'; } @@ -2267,8 +2303,7 @@ mxml_parse_element( } else { - mxml_error("Missing value for attribute '%s' in element %s!", - name, node->value.element.name); + mxml_error("Missing value for attribute '%s' in element %s on line %d.", name, node->value.element.name, *line); goto error; } @@ -2286,8 +2321,7 @@ mxml_parse_element( if (quote != '>') { - mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", - ch, node->value.element.name, quote); + mxml_error("Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, node->value.element.name, quote, *line); ch = EOF; } @@ -2310,7 +2344,7 @@ mxml_parse_element( * Common error return point... */ -error: + error: PhFree(name); PhFree(value); @@ -2344,226 +2378,226 @@ mxml_string_getc(void *p, /* I - Pointer to file */ switch (*encoding) { case ENCODE_UTF8 : - if (!(ch & 0x80)) - { + if (!(ch & 0x80)) + { #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } - return (ch); + return (ch); } - else if (ch == 0xfe) - { - /* - * UTF-16 big-endian BOM? - */ + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ if (((*s)[0] & 255) != 0xff) - return (EOF); + return (EOF); - *encoding = ENCODE_UTF16BE; - (*s)++; + *encoding = ENCODE_UTF16BE; + (*s)++; - return (mxml_string_getc(p, encoding)); - } - else if (ch == 0xff) - { - /* - * UTF-16 little-endian BOM? - */ + return (mxml_string_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ if (((*s)[0] & 255) != 0xfe) - return (EOF); + return (EOF); - *encoding = ENCODE_UTF16LE; - (*s)++; + *encoding = ENCODE_UTF16LE; + (*s)++; - return (mxml_string_getc(p, encoding)); - } - else if ((ch & 0xe0) == 0xc0) - { - /* - * Two-byte value... - */ + return (mxml_string_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ - if (((*s)[0] & 0xc0) != 0x80) + if (((*s)[0] & 0xc0) != 0x80) return (EOF); - ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); + ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); - (*s)++; + (*s)++; - if (ch < 0x80) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - return (ch); - } - else if ((ch & 0xf0) == 0xe0) - { - /* - * Three-byte value... - */ + return (ch); + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ - if (((*s)[0] & 0xc0) != 0x80 || - ((*s)[1] & 0xc0) != 0x80) + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80) return (EOF); - ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); + ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); - (*s) += 2; + (*s) += 2; - if (ch < 0x800) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } - /* - * Ignore (strip) Byte Order Mark (BOM)... - */ + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ - if (ch == 0xfeff) - return (mxml_string_getc(p, encoding)); + if (ch == 0xfeff) + return (mxml_string_getc(p, encoding)); #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - return (ch); - } - else if ((ch & 0xf8) == 0xf0) - { - /* - * Four-byte value... - */ - - if (((*s)[0] & 0xc0) != 0x80 || - ((*s)[1] & 0xc0) != 0x80 || - ((*s)[2] & 0xc0) != 0x80) + return (ch); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80 || + ((*s)[2] & 0xc0) != 0x80) return (EOF); - ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | - ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); + ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | + ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); - (*s) += 3; + (*s) += 3; - if (ch < 0x10000) - { - mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); - return (EOF); - } + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - return (ch); - } - else - return (EOF); + return (ch); + } + else + return (EOF); case ENCODE_UTF16BE : - /* + /* * Read UTF-16 big-endian char... - */ + */ - ch = (ch << 8) | ((*s)[0] & 255); - (*s) ++; + ch = (ch << 8) | ((*s)[0] & 255); + (*s) ++; if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } else if (ch >= 0xd800 && ch <= 0xdbff) - { - /* - * Multi-word UTF-16 char... - */ + { + /* + * Multi-word UTF-16 char... + */ int lch; /* Lower word */ if (!(*s)[0]) - return (EOF); + return (EOF); lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255); - (*s) += 2; + (*s) += 2; if (lch < 0xdc00 || lch >= 0xdfff) - return (EOF); + return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } + } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - return (ch); + return (ch); case ENCODE_UTF16LE : - /* + /* * Read UTF-16 little-endian char... - */ + */ - ch = ch | (((*s)[0] & 255) << 8); + ch = ch | (((*s)[0] & 255) << 8); - if (!ch) - { - (*s) --; - return (EOF); - } + if (!ch) + { + (*s) --; + return (EOF); + } - (*s) ++; + (*s) ++; if (mxml_bad_char(ch)) - { - mxml_error("Bad control character 0x%02x not allowed by XML standard!", - ch); - return (EOF); - } + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } else if (ch >= 0xd800 && ch <= 0xdbff) - { - /* - * Multi-word UTF-16 char... - */ + { + /* + * Multi-word UTF-16 char... + */ int lch; /* Lower word */ if (!(*s)[1]) - return (EOF); + return (EOF); lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255); - (*s) += 2; + (*s) += 2; if (lch < 0xdc00 || lch >= 0xdfff) - return (EOF); + return (EOF); ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; - } + } #if DEBUG > 1 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); #endif /* DEBUG > 1 */ - return (ch); + return (ch); } } @@ -2600,8 +2634,8 @@ mxml_string_putc(int ch, /* I - Character to write */ static int /* O - 0 on success, -1 on failure */ mxml_write_name(const char *s, /* I - Name to write */ void *p, /* I - Write pointer */ - int (*putc_cb)(int, void *)) - /* I - Write callback */ + int (*putc_cb)(int, void *)) + /* I - Write callback */ { char quote; /* Quote character */ const char *name; /* Entity name */ @@ -2622,22 +2656,22 @@ mxml_write_name(const char *s, /* I - Name to write */ { if ((name = mxmlEntityGetName(*s)) != NULL) { - if ((*putc_cb)('&', p) < 0) + if ((*putc_cb)('&', p) < 0) return (-1); while (*name) - { - if ((*putc_cb)(*name, p) < 0) + { + if ((*putc_cb)(*name, p) < 0) return (-1); name ++; - } + } - if ((*putc_cb)(';', p) < 0) + if ((*putc_cb)(';', p) < 0) return (-1); } else if ((*putc_cb)(*s, p) < 0) - return (-1); + return (-1); s ++; } @@ -2658,7 +2692,7 @@ mxml_write_name(const char *s, /* I - Name to write */ while (*s) { if ((*putc_cb)(*s, p) < 0) - return (-1); + return (-1); s ++; } @@ -2675,16 +2709,16 @@ mxml_write_name(const char *s, /* I - Name to write */ static int /* O - Column or -1 on error */ mxml_write_node(mxml_node_t *node, /* I - Node to write */ void *p, /* I - File to write to */ - mxml_save_cb_t cb, /* I - Whitespace callback */ - int col, /* I - Current column */ - _mxml_putc_cb_t putc_cb,/* I - Output callback */ - _mxml_global_t *global)/* I - Global data */ + mxml_save_cb_t cb, /* I - Whitespace callback */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb,/* I - Output callback */ + _mxml_global_t *global)/* I - Global data */ { mxml_node_t *current, /* Current node */ - *next; /* Next node */ + *next; /* Next node */ int i, /* Looping var */ - width; /* Width of attr + value */ - mxml_attr_t *attr; /* Current attribute */ + width; /* Width of attr + value */ + _mxml_attr_t *attr; /* Current attribute */ char s[255]; /* Temporary string */ @@ -2701,213 +2735,230 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ switch (current->type) { case MXML_ELEMENT : - col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); - - if ((*putc_cb)('<', p) < 0) - return (-1); - if (current->value.element.name[0] == '?' || - !strncmp(current->value.element.name, "!--", 3) || - !strncmp(current->value.element.name, "![CDATA[", 8)) - { - /* - * Comments, CDATA, and processing instructions do not - * use character entities. - */ - - const char *ptr; /* Pointer into name */ - - for (ptr = current->value.element.name; *ptr; ptr ++) - if ((*putc_cb)(*ptr, p) < 0) - return (-1); - } - else if (mxml_write_name(current->value.element.name, p, putc_cb) < 0) - return (-1); - - col += (int)strlen(current->value.element.name) + 1; - - for (i = current->value.element.num_attrs, attr = current->value.element.attrs; - i > 0; - i --, attr ++) - { - width = (int)strlen(attr->name); - - if (attr->value) - width += (int)strlen(attr->value) + 3; - - if (global->wrap > 0 && (col + width) > global->wrap) - { - if ((*putc_cb)('\n', p) < 0) - return (-1); - - col = 0; - } - else - { - if ((*putc_cb)(' ', p) < 0) - return (-1); - - col ++; - } - - if (mxml_write_name(attr->name, p, putc_cb) < 0) - return (-1); - - if (attr->value) - { - if ((*putc_cb)('=', p) < 0) - return (-1); - if ((*putc_cb)('\"', p) < 0) - return (-1); - if (mxml_write_string(attr->value, p, putc_cb) < 0) - return (-1); - if ((*putc_cb)('\"', p) < 0) - return (-1); - } - - col += width; - } - - if (current->child) - { - /* - * Write children... - */ - - if ((*putc_cb)('>', p) < 0) - return (-1); - else - col ++; - - col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); - } - else if (current->value.element.name[0] == '!' || - current->value.element.name[0] == '?') - { - /* - * The ? and ! elements are special-cases... - */ - - if ((*putc_cb)('>', p) < 0) - return (-1); - else - col ++; - - col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); - } - else - { - if ((*putc_cb)(' ', p) < 0) - return (-1); - if ((*putc_cb)('/', p) < 0) - return (-1); - if ((*putc_cb)('>', p) < 0) - return (-1); - - col += 3; - - col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); - } - break; + col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if (current->value.element.name[0] == '?' || + !strncmp(current->value.element.name, "!--", 3)) + { + /* + * Comments and processing instructions do not use character + * entities. + */ + + const char *ptr; /* Pointer into name */ + + for (ptr = current->value.element.name; *ptr; ptr ++) + if ((*putc_cb)(*ptr, p) < 0) + return (-1); + } + else if (!strncmp(current->value.element.name, "![CDATA[", 8)) + { + /* + * CDATA elements do not use character entities, but also need the + * "]]" terminator added at the end. + */ + + const char *ptr; /* Pointer into name */ + + for (ptr = current->value.element.name; *ptr; ptr ++) + if ((*putc_cb)(*ptr, p) < 0) + return (-1); + + if ((*putc_cb)(']', p) < 0) + return (-1); + if ((*putc_cb)(']', p) < 0) + return (-1); + } + else if (mxml_write_name(current->value.element.name, p, putc_cb) < 0) + return (-1); + + col += (int)strlen(current->value.element.name) + 1; + + for (i = current->value.element.num_attrs, attr = current->value.element.attrs; + i > 0; + i --, attr ++) + { + width = (int)strlen(attr->name); + + if (attr->value) + width += (int)strlen(attr->value) + 3; + + if (global->wrap > 0 && (col + width) > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + + col ++; + } + + if (mxml_write_name(attr->name, p, putc_cb) < 0) + return (-1); + + if (attr->value) + { + if ((*putc_cb)('=', p) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + if (mxml_write_string(attr->value, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + } + + col += width; + } + + if (current->child) + { + /* + * Write children... + */ + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + else if (current->value.element.name[0] == '!' || + current->value.element.name[0] == '?') + { + /* + * The ? and ! elements are special-cases... + */ + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += 3; + + col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + break; case MXML_INTEGER : - if (current->prev) - { - if (global->wrap > 0 && col > global->wrap) - { - if ((*putc_cb)('\n', p) < 0) - return (-1); - - col = 0; - } - else if ((*putc_cb)(' ', p) < 0) - return (-1); - else - col ++; - } - - sprintf(s, "%d", current->value.integer); - if (mxml_write_string(s, p, putc_cb) < 0) - return (-1); - - col += (int)strlen(s); - break; + if (current->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + snprintf(s, sizeof(s), "%d", current->value.integer); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += (int)strlen(s); + break; case MXML_OPAQUE : - if (mxml_write_string(current->value.opaque, p, putc_cb) < 0) - return (-1); + if (mxml_write_string(current->value.opaque, p, putc_cb) < 0) + return (-1); - col += (int)strlen(current->value.opaque); - break; + col += (int)strlen(current->value.opaque); + break; case MXML_REAL : - if (current->prev) - { - if (global->wrap > 0 && col > global->wrap) - { - if ((*putc_cb)('\n', p) < 0) - return (-1); - - col = 0; - } - else if ((*putc_cb)(' ', p) < 0) - return (-1); - else - col ++; - } - - sprintf(s, "%f", current->value.real); - if (mxml_write_string(s, p, putc_cb) < 0) - return (-1); - - col += (int)strlen(s); - break; + if (current->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + snprintf(s, sizeof(s), "%f", current->value.real); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += (int)strlen(s); + break; case MXML_TEXT : - if (current->value.text.whitespace && col > 0) - { - if (global->wrap > 0 && col > global->wrap) - { - if ((*putc_cb)('\n', p) < 0) - return (-1); - - col = 0; - } - else if ((*putc_cb)(' ', p) < 0) - return (-1); - else - col ++; - } - - if (mxml_write_string(current->value.text.string, p, putc_cb) < 0) - return (-1); - - col += (int)strlen(current->value.text.string); - break; + if (current->value.text.whitespace && col > 0) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + if (mxml_write_string(current->value.text.string, p, putc_cb) < 0) + return (-1); + + col += (int)strlen(current->value.text.string); + break; case MXML_CUSTOM : - if (global->custom_save_cb) - { - char *data; /* Custom data string */ - const char *newline; /* Last newline in string */ + if (global->custom_save_cb) + { + char *data; /* Custom data string */ + const char *newline; /* Last newline in string */ - if ((data = (*global->custom_save_cb)(node)) == NULL) - return (-1); + if ((data = (*global->custom_save_cb)(current)) == NULL) + return (-1); - if (mxml_write_string(data, p, putc_cb) < 0) - return (-1); + if (mxml_write_string(data, p, putc_cb) < 0) + return (-1); - if ((newline = strrchr(data, '\n')) == NULL) - col += (int)strlen(data); - else - col = (int)strlen(newline); + if ((newline = strrchr(data, '\n')) == NULL) + col += (int)strlen(data); + else + col = (int)strlen(newline); PhFree(data); - break; - } + break; + } default : /* Should never happen */ - return (-1); + return (-1); } /* @@ -2916,35 +2967,53 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ if ((next = current->child) == NULL) { - while ((next = current->next) == NULL) + if (current == node) { - if (current == node) - break; + /* + * Don't traverse to sibling node if we are at the "root" node... + */ + next = NULL; + } + else + { /* - * The ? and ! elements are special-cases and have no end tags... - */ + * Try the next sibling, and continue traversing upwards as needed... + */ - current = current->parent; + while ((next = current->next) == NULL) + { + if (current == node || !current->parent) + break; - if (current->value.element.name[0] != '!' && - current->value.element.name[0] != '?') - { - col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); + /* + * The ? and ! elements are special-cases and have no end tags... + */ - if ((*putc_cb)('<', p) < 0) - return (-1); - if ((*putc_cb)('/', p) < 0) - return (-1); - if (mxml_write_string(current->value.element.name, p, putc_cb) < 0) - return (-1); - if ((*putc_cb)('>', p) < 0) - return (-1); + current = current->parent; - col += (int)strlen(current->value.element.name) + 3; + if (current->value.element.name[0] != '!' && + current->value.element.name[0] != '?') + { + col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); - col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); - } + if ((*putc_cb)('<', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if (mxml_write_string(current->value.element.name, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += (int)strlen(current->value.element.name) + 3; + + col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); + } + + if (current == node) + break; + } } } } @@ -2975,7 +3044,7 @@ mxml_write_string( while (*name) { - if ((*putc_cb)(*name, p) < 0) + if ((*putc_cb)(*name, p) < 0) return (-1); name ++; } @@ -3001,8 +3070,8 @@ static int /* O - New column */ mxml_write_ws(mxml_node_t *node, /* I - Current node */ void *p, /* I - Write pointer */ mxml_save_cb_t cb, /* I - Callback function */ - int ws, /* I - Where value */ - int col, /* I - Current column */ + int ws, /* I - Where value */ + int col, /* I - Current column */ _mxml_putc_cb_t putc_cb) /* I - Write callback */ { const char *s; /* Whitespace string */ @@ -3013,16 +3082,16 @@ mxml_write_ws(mxml_node_t *node, /* I - Current node */ while (*s) { if ((*putc_cb)(*s, p) < 0) - return (-1); + return (-1); else if (*s == '\n') - col = 0; + col = 0; else if (*s == '\t') { - col += MXML_TAB; - col = col - (col % MXML_TAB); + col += MXML_TAB; + col = col - (col % MXML_TAB); } else - col ++; + col ++; s ++; } @@ -3030,8 +3099,3 @@ mxml_write_ws(mxml_node_t *node, /* I - Current node */ return (col); } - - -/* - * End of "$Id: mxml-file.c 467 2016-06-13 00:51:16Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-get.c b/phlib/mxml/mxml-get.c similarity index 85% rename from ProcessHacker/mxml/mxml-get.c rename to phlib/mxml/mxml-get.c index 40ed3d0839b4..e2ff2bd545d2 100644 --- a/ProcessHacker/mxml/mxml-get.c +++ b/phlib/mxml/mxml-get.c @@ -1,17 +1,12 @@ /* - * "$Id: mxml-get.c 451 2014-01-04 21:50:06Z msweet $" + * Node get functions for Mini-XML, a small XML file parsing library. * - * Node get functions for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2014 by Michael R Sweet. + * Copyright © 2014-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -19,7 +14,7 @@ */ #include "config.h" -#include "mxml.h" +#include "mxml-private.h" /* @@ -30,7 +25,7 @@ * @since Mini-XML 2.7@ */ -const char * /* O - CDATA value or NULL */ +const char * /* O - CDATA value or @code NULL@ */ mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */ { /* @@ -58,7 +53,7 @@ mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -const void * /* O - Custom value or NULL */ +const void * /* O - Custom value or @code NULL@ */ mxmlGetCustom(mxml_node_t *node) /* I - Node to get */ { /* @@ -91,7 +86,7 @@ mxmlGetCustom(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -const char * /* O - Element name or NULL */ +const char * /* O - Element name or @code NULL@ */ mxmlGetElement(mxml_node_t *node) /* I - Node to get */ { /* @@ -118,7 +113,7 @@ mxmlGetElement(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -mxml_node_t * /* O - First child or NULL */ +mxml_node_t * /* O - First child or @code NULL@ */ mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */ { /* @@ -179,7 +174,7 @@ mxmlGetInteger(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -mxml_node_t * /* O - Last child or NULL */ +mxml_node_t * /* O - Last child or @code NULL@ */ mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */ { /* @@ -232,7 +227,7 @@ mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -const char * /* O - Opaque string or NULL */ +const char * /* O - Opaque string or @code NULL@ */ mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */ { /* @@ -265,7 +260,7 @@ mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -mxml_node_t * /* O - Parent node or NULL */ +mxml_node_t * /* O - Parent node or @code NULL@ */ mxmlGetParent(mxml_node_t *node) /* I - Node to get */ { /* @@ -291,7 +286,7 @@ mxmlGetParent(mxml_node_t *node) /* I - Node to get */ * @since Mini-XML 2.7@ */ -mxml_node_t * /* O - Previous node or NULL */ +mxml_node_t * /* O - Previous node or @code NULL@ */ mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */ { /* @@ -346,12 +341,18 @@ mxmlGetReal(mxml_node_t *node) /* I - Node to get */ * 'mxmlGetText()' - Get the text value for a node or its first child. * * @code NULL@ is returned if the node (or its first child) is not a text node. - * The "whitespace" argument can be NULL. + * The "whitespace" argument can be @code NULL@. + * + * Note: Text nodes consist of whitespace-delimited words. You will only get + * single words of text when reading an XML file with @code MXML_TEXT@ nodes. + * If you want the entire string between elements in the XML file, you MUST read + * the XML file with @code MXML_OPAQUE@ nodes and get the resulting strings + * using the @link mxmlGetOpaque@ function instead. * * @since Mini-XML 2.7@ */ -const char * /* O - Text string or NULL */ +const char * /* O - Text string or @code NULL@ */ mxmlGetText(mxml_node_t *node, /* I - Node to get */ int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */ { @@ -445,8 +446,3 @@ mxmlGetUserData(mxml_node_t *node) /* I - Node to get */ return (node->user_data); } - - -/* - * End of "$Id: mxml-get.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-index.c b/phlib/mxml/mxml-index.c similarity index 75% rename from ProcessHacker/mxml/mxml-index.c rename to phlib/mxml/mxml-index.c index 2db94eabd7de..c5766a4488b5 100644 --- a/ProcessHacker/mxml/mxml-index.c +++ b/phlib/mxml/mxml-index.c @@ -1,26 +1,20 @@ /* - * "$Id: mxml-index.c 451 2014-01-04 21:50:06Z msweet $" + * Index support code for Mini-XML, a small XML file parsing library. * - * Index support code for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2014 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* * Include necessary headers... */ -#include #include "config.h" -#include "mxml.h" +#include "mxml-private.h" /* @@ -28,9 +22,9 @@ */ static int index_compare(mxml_index_t *ind, mxml_node_t *first, - mxml_node_t *second); + mxml_node_t *second); static int index_find(mxml_index_t *ind, const char *element, - const char *value, mxml_node_t *node); + const char *value, mxml_node_t *node); static void index_sort(mxml_index_t *ind, int left, int right); @@ -65,10 +59,12 @@ mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */ /* * 'mxmlIndexEnum()' - Return the next node in the index. * - * Nodes are returned in the sorted order of the index. + * You should call @link mxmlIndexReset@ prior to using this function to get + * the first node in the index. Nodes are returned in the sorted order of the + * index. */ -mxml_node_t * /* O - Next node or NULL if there is none */ +mxml_node_t * /* O - Next node or @code NULL@ if there is none */ mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */ { /* @@ -92,24 +88,24 @@ mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */ /* * 'mxmlIndexFind()' - Find the next matching node. * - * You should call mxmlIndexReset() prior to using this function for + * You should call @link mxmlIndexReset@ prior to using this function for * the first time with a particular set of "element" and "value" - * strings. Passing NULL for both "element" and "value" is equivalent - * to calling mxmlIndexEnum(). + * strings. Passing @code NULL@ for both "element" and "value" is equivalent + * to calling @link mxmlIndexEnum@. */ -mxml_node_t * /* O - Node or NULL if none found */ +mxml_node_t * /* O - Node or @code NULL@ if none found */ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ const char *element, /* I - Element name to find, if any */ - const char *value) /* I - Attribute value, if any */ + const char *value) /* I - Attribute value, if any */ { int diff, /* Difference between names */ - current, /* Current entity in search */ - first, /* First entity in search */ - last; /* Last entity in search */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ -#if DEBUG > 1 +#ifdef DEBUG printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n", ind, element ? element : "(null)", value ? value : "(null)"); #endif /* DEBUG */ @@ -120,9 +116,10 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ if (!ind || (!ind->attr && value)) { -#if DEBUG > 1 +#ifdef DEBUG puts(" returning NULL..."); - printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)"); + if (ind) + printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)"); #endif /* DEBUG */ return (NULL); @@ -142,7 +139,7 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ if (!ind->num_nodes) { -#if DEBUG > 1 +#ifdef DEBUG puts(" returning NULL..."); puts(" no nodes!"); #endif /* DEBUG */ @@ -163,7 +160,7 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ first = 0; last = ind->num_nodes - 1; -#if DEBUG > 1 +#ifdef DEBUG printf(" find first time, num_nodes=%d...\n", ind->num_nodes); #endif /* DEBUG */ @@ -171,7 +168,7 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ { current = (first + last) / 2; -#if DEBUG > 1 +#ifdef DEBUG printf(" first=%d, last=%d, current=%d\n", first, last, current); #endif /* DEBUG */ @@ -179,34 +176,34 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ { /* * Found a match, move back to find the first... - */ + */ -#if DEBUG > 1 +#ifdef DEBUG puts(" match!"); #endif /* DEBUG */ while (current > 0 && - !index_find(ind, element, value, ind->nodes[current - 1])) - current --; + !index_find(ind, element, value, ind->nodes[current - 1])) + current --; -#if DEBUG > 1 +#ifdef DEBUG printf(" returning first match=%d\n", current); #endif /* DEBUG */ /* * Return the first match and save the index to the next... - */ + */ ind->cur_node = current + 1; - return (ind->nodes[current]); + return (ind->nodes[current]); } else if (diff < 0) - last = current; + last = current; else - first = current; + first = current; -#if DEBUG > 1 +#ifdef DEBUG printf(" diff=%d\n", diff); #endif /* DEBUG */ } @@ -219,16 +216,16 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ if (!index_find(ind, element, value, ind->nodes[current])) { /* - * Found exactly one (or possibly two) match... - */ + * Found exactly one (or possibly two) match... + */ -#if DEBUG > 1 - printf(" returning only match %d...\n", current); +#ifdef DEBUG + printf(" returning only match %d...\n", current); #endif /* DEBUG */ - ind->cur_node = current + 1; + ind->cur_node = current + 1; - return (ind->nodes[current]); + return (ind->nodes[current]); } /* @@ -237,7 +234,7 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ ind->cur_node = ind->num_nodes; -#if DEBUG > 1 +#ifdef DEBUG puts(" returning NULL..."); #endif /* DEBUG */ @@ -250,7 +247,7 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ * Return the next matching node... */ -#if DEBUG > 1 +#ifdef DEBUG printf(" returning next match %d...\n", ind->cur_node); #endif /* DEBUG */ @@ -263,7 +260,7 @@ mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ ind->cur_node = ind->num_nodes; -#if DEBUG > 1 +#ifdef DEBUG puts(" returning NULL..."); #endif /* DEBUG */ @@ -299,7 +296,7 @@ mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */ * 'mxmlIndexNew()' - Create a new index. * * The index will contain all nodes that contain the named element and/or - * attribute. If both "element" and "attr" are NULL, then the index will + * attribute. If both "element" and "attr" are @code NULL@, then the index will * contain a sorted list of the elements in the node tree. Nodes are * sorted by element name and optionally by attribute value if the "attr" * argument is not NULL. @@ -307,19 +304,19 @@ mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */ mxml_index_t * /* O - New index */ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ - const char *element, /* I - Element to index or NULL for all */ - const char *attr) /* I - Attribute to index or NULL for none */ + const char *element, /* I - Element to index or @code NULL@ for all */ + const char *attr) /* I - Attribute to index or @code NULL@ for none */ { mxml_index_t *ind; /* New index */ mxml_node_t *current, /* Current node in index */ - **temp; /* Temporary node pointer array */ + **temp; /* Temporary node pointer array */ /* * Range check input... */ -#if DEBUG > 1 +#ifdef DEBUG printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n", node, element ? element : "(null)", attr ? attr : "(null)"); #endif /* DEBUG */ @@ -359,14 +356,14 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ { /* * Unable to allocate memory for the index, so abort... - */ + */ mxml_error("Unable to allocate %d bytes for index: %s", - (ind->alloc_nodes + 64) * sizeof(mxml_node_t *), - strerror(errno)); + (ind->alloc_nodes + 64) * sizeof(mxml_node_t *), + strerror(errno)); mxmlIndexDelete(ind); - return (NULL); + return (NULL); } ind->nodes = temp; @@ -382,7 +379,7 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ * Sort nodes based upon the search criteria... */ -#if DEBUG > 1 +#ifdef DEBUG { int i; /* Looping var */ @@ -395,9 +392,9 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ puts("-------- -------- -------------- ------------------------------"); for (i = 0; i < ind->num_nodes; i ++) - printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], - ind->nodes[i]->value.element.name, - mxmlElementGetAttr(ind->nodes[i], attr)); + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); } else { @@ -405,8 +402,8 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ puts("-------- -------- --------------"); for (i = 0; i < ind->num_nodes; i ++) - printf("%8d %-8p %s\n", i, ind->nodes[i], - ind->nodes[i]->value.element.name); + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); } putchar('\n'); @@ -416,7 +413,7 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ if (ind->num_nodes > 1) index_sort(ind, 0, ind->num_nodes - 1); -#if DEBUG > 1 +#ifdef DEBUG { int i; /* Looping var */ @@ -429,9 +426,9 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ puts("-------- -------- -------------- ------------------------------"); for (i = 0; i < ind->num_nodes; i ++) - printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], - ind->nodes[i]->value.element.name, - mxmlElementGetAttr(ind->nodes[i], attr)); + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); } else { @@ -439,8 +436,8 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ puts("-------- -------- --------------"); for (i = 0; i < ind->num_nodes; i ++) - printf("%8d %-8p %s\n", i, ind->nodes[i], - ind->nodes[i]->value.element.name); + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); } putchar('\n'); @@ -459,14 +456,14 @@ mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and * return the first node in the index. * - * This function should be called prior to using mxmlIndexEnum() or - * mxmlIndexFind() for the first time. + * This function should be called prior to using @link mxmlIndexEnum@ or + * @link mxmlIndexFind@ for the first time. */ -mxml_node_t * /* O - First node or NULL if there is none */ +mxml_node_t * /* O - First node or @code NULL@ if there is none */ mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */ { -#if DEBUG > 1 +#ifdef DEBUG printf("mxmlIndexReset(ind=%p)\n", ind); #endif /* DEBUG */ @@ -539,8 +536,8 @@ index_compare(mxml_index_t *ind, /* I - Index */ static int /* O - Result of comparison */ index_find(mxml_index_t *ind, /* I - Index */ - const char *element, /* I - Element name or NULL */ - const char *value, /* I - Attribute value or NULL */ + const char *element, /* I - Element name or @code NULL@ */ + const char *value, /* I - Attribute value or @code NULL@ */ mxml_node_t *node) /* I - Node */ { int diff; /* Difference */ @@ -583,12 +580,12 @@ index_find(mxml_index_t *ind, /* I - Index */ static void index_sort(mxml_index_t *ind, /* I - Index to sort */ int left, /* I - Left node in partition */ - int right) /* I - Right node in partition */ + int right) /* I - Right node in partition */ { mxml_node_t *pivot, /* Pivot node */ - *temp; /* Swap node */ + *temp; /* Swap node */ int templ, /* Temporary left node */ - tempr; /* Temporary right node */ + tempr; /* Temporary right node */ /* @@ -611,7 +608,7 @@ index_sort(mxml_index_t *ind, /* I - Index to sort */ while ((templ < right) && index_compare(ind, ind->nodes[templ], pivot) <= 0) - templ ++; + templ ++; /* * Move right while right node > pivot node... @@ -619,7 +616,7 @@ index_sort(mxml_index_t *ind, /* I - Index to sort */ while ((tempr > left) && index_compare(ind, ind->nodes[tempr], pivot) > 0) - tempr --; + tempr --; /* * Swap nodes if needed... @@ -627,9 +624,9 @@ index_sort(mxml_index_t *ind, /* I - Index to sort */ if (templ < tempr) { - temp = ind->nodes[templ]; - ind->nodes[templ] = ind->nodes[tempr]; - ind->nodes[tempr] = temp; + temp = ind->nodes[templ]; + ind->nodes[templ] = ind->nodes[tempr]; + ind->nodes[tempr] = temp; } } @@ -653,8 +650,3 @@ index_sort(mxml_index_t *ind, /* I - Index to sort */ } while (right > (left = tempr + 1)); } - - -/* - * End of "$Id: mxml-index.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-node.c b/phlib/mxml/mxml-node.c similarity index 64% rename from ProcessHacker/mxml/mxml-node.c rename to phlib/mxml/mxml-node.c index 62d35aea3a47..f67b06b96e15 100644 --- a/ProcessHacker/mxml/mxml-node.c +++ b/phlib/mxml/mxml-node.c @@ -1,26 +1,20 @@ /* - * "$Id: mxml-node.c 462 2016-06-11 20:51:49Z msweet $" + * Node support code for Mini-XML, a small XML file parsing library. * - * Node support code for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2016 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* * Include necessary headers... */ -#include #include "config.h" -#include "mxml.h" +#include "mxml-private.h" /* @@ -34,24 +28,24 @@ static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type); /* * 'mxmlAdd()' - Add a node to a tree. * - * Adds the specified node to the parent. If the child argument is not - * NULL, puts the new node before or after the specified child depending - * on the value of the where argument. If the child argument is NULL, - * puts the new node at the beginning of the child list (MXML_ADD_BEFORE) - * or at the end of the child list (MXML_ADD_AFTER). The constant - * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. + * Adds the specified node to the parent. If the child argument is not + * @code NULL@, puts the new node before or after the specified child depending + * on the value of the where argument. If the child argument is @code NULL@, + * puts the new node at the beginning of the child list (@code MXML_ADD_BEFORE@) + * or at the end of the child list (@code MXML_ADD_AFTER@). The constant + * @code MXML_ADD_TO_PARENT@ can be used to specify a @code NULL@ child pointer. */ void mxmlAdd(mxml_node_t *parent, /* I - Parent node */ - int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */ - mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */ - mxml_node_t *node) /* I - Node to add */ + int where, /* I - Where to add, @code MXML_ADD_BEFORE@ or @code MXML_ADD_AFTER@ */ + mxml_node_t *child, /* I - Child node for where or @code MXML_ADD_TO_PARENT@ */ + mxml_node_t *node) /* I - Node to add */ { -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent, where, child, node); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -60,7 +54,7 @@ mxmlAdd(mxml_node_t *parent, /* I - Parent node */ if (!parent || !node) return; -#if DEBUG > 1 +#if MXMLDEBUG > 1 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); if (parent) { @@ -69,7 +63,7 @@ mxmlAdd(mxml_node_t *parent, /* I - Parent node */ fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev); fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next); } -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ /* * Remove the node from any existing parent... @@ -88,75 +82,75 @@ mxmlAdd(mxml_node_t *parent, /* I - Parent node */ { case MXML_ADD_BEFORE : if (!child || child == parent->child || child->parent != parent) - { - /* - * Insert as first node under parent... - */ - - node->next = parent->child; - - if (parent->child) - parent->child->prev = node; - else - parent->last_child = node; - - parent->child = node; - } - else - { - /* - * Insert node before this child... - */ - - node->next = child; - node->prev = child->prev; - - if (child->prev) - child->prev->next = node; - else - parent->child = node; - - child->prev = node; - } + { + /* + * Insert as first node under parent... + */ + + node->next = parent->child; + + if (parent->child) + parent->child->prev = node; + else + parent->last_child = node; + + parent->child = node; + } + else + { + /* + * Insert node before this child... + */ + + node->next = child; + node->prev = child->prev; + + if (child->prev) + child->prev->next = node; + else + parent->child = node; + + child->prev = node; + } break; case MXML_ADD_AFTER : if (!child || child == parent->last_child || child->parent != parent) - { - /* - * Insert as last node under parent... - */ + { + /* + * Insert as last node under parent... + */ - node->parent = parent; - node->prev = parent->last_child; + node->parent = parent; + node->prev = parent->last_child; - if (parent->last_child) - parent->last_child->next = node; - else - parent->child = node; + if (parent->last_child) + parent->last_child->next = node; + else + parent->child = node; - parent->last_child = node; + parent->last_child = node; } - else - { - /* - * Insert node after this child... - */ - - node->prev = child; - node->next = child->next; - - if (child->next) - child->next->prev = node; - else - parent->last_child = node; - - child->next = node; - } + else + { + /* + * Insert node after this child... + */ + + node->prev = child; + node->next = child->next; + + if (child->next) + child->next->prev = node; + else + parent->last_child = node; + + child->next = node; + } break; } -#if DEBUG > 1 +#if MXMLDEBUG > 1 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); if (parent) { @@ -165,7 +159,7 @@ mxmlAdd(mxml_node_t *parent, /* I - Parent node */ fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev); fprintf(stderr, " AFTER: parent->next=%p\n", parent->next); } -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ } @@ -173,19 +167,19 @@ mxmlAdd(mxml_node_t *parent, /* I - Parent node */ * 'mxmlDelete()' - Delete a node and all of its children. * * If the specified node has a parent, this function first removes the - * node from its parent using the mxmlRemove() function. + * node from its parent using the @link mxmlRemove@ function. */ void mxmlDelete(mxml_node_t *node) /* I - Node to delete */ { mxml_node_t *current, /* Current node */ - *next; /* Next node */ + *next; /* Next node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlDelete(node=%p)\n", node); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -222,29 +216,23 @@ mxmlDelete(mxml_node_t *node) /* I - Node to delete */ if ((next = current->next) == NULL) { - mxml_node_t *temp = current->parent; - /* Pointer to parent node */ - - if (temp == node) - { - /* - * Got back to the top node... - */ + /* + * Next node is the parent, which we'll free as needed... + */ + if ((next = current->parent) == node) next = NULL; - } - else if ((next = temp->next) == NULL) - { - if ((next = temp->parent) == node) - next = NULL; - } } + /* + * Free child... + */ + mxml_free(current); } /* - * Then free the memory used by this node... + * Then free the memory used by the parent node... */ mxml_free(node); @@ -283,24 +271,25 @@ mxmlGetRefCount(mxml_node_t *node) /* I - Node */ * 'mxmlNewCDATA()' - Create a new CDATA node. * * The new CDATA node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new - * CDATA node has no parent. The data string must be nul-terminated and - * is copied into the new node. CDATA nodes use the MXML_ELEMENT type. + * list. The constant @code MXML_NO_PARENT@ can be used to specify that the new + * CDATA node has no parent. The data string must be nul-terminated and + * is copied into the new node. CDATA nodes currently use the + * @code MXML_ELEMENT@ type. * * @since Mini-XML 2.3@ */ mxml_node_t * /* O - New node */ -mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ - const char *data) /* I - Data string */ +mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ + const char *data) /* I - Data string */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n", parent, data ? data : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -314,7 +303,7 @@ mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ */ if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) - node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + node->value.element.name = _mxml_strdupf("![CDATA[%s", data); return (node); } @@ -324,8 +313,8 @@ mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ * 'mxmlNewCustom()' - Create a new custom data node. * * The new custom node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new - * element node has no parent. NULL can be passed when the data in the + * list. The constant @code MXML_NO_PARENT@ can be used to specify that the new + * element node has no parent. @code NULL@ can be passed when the data in the * node is not dynamically allocated or is separately managed. * * @since Mini-XML 2.1@ @@ -333,17 +322,17 @@ mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ mxml_node_t * /* O - New node */ mxmlNewCustom( - mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ void *data, /* I - Pointer to data */ mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, data, destroy); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Create the node and set the value... @@ -363,21 +352,21 @@ mxmlNewCustom( * 'mxmlNewElement()' - Create a new element node. * * The new element node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new + * list. The constant @code MXML_NO_PARENT@ can be used to specify that the new * element node has no parent. */ mxml_node_t * /* O - New node */ -mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ +mxmlNewElement(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ const char *name) /* I - Name of element */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent, name ? name : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -401,20 +390,20 @@ mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ * 'mxmlNewInteger()' - Create a new integer node. * * The new integer node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new + * list. The constant @code MXML_NO_PARENT@ can be used to specify that the new * integer node has no parent. */ mxml_node_t * /* O - New node */ -mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ +mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ int integer) /* I - Integer value */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Create the node and set the element name... @@ -430,23 +419,23 @@ mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ /* * 'mxmlNewOpaque()' - Create a new opaque string. * - * The new opaque node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new - * opaque node has no parent. The opaque string must be nul-terminated and - * is copied into the new node. + * The new opaque string node is added to the end of the specified parent's + * child list. The constant @code MXML_NO_PARENT@ can be used to specify that + * the new opaque string node has no parent. The opaque string must be nul- + * terminated and is copied into the new node. */ mxml_node_t * /* O - New node */ -mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ +mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ const char *opaque) /* I - Opaque string */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, opaque ? opaque : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -466,24 +455,70 @@ mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ } +/* + * 'mxmlNewOpaquef()' - Create a new formatted opaque string node. + * + * The new opaque string node is added to the end of the specified parent's + * child list. The constant @code MXML_NO_PARENT@ can be used to specify that + * the new opaque string node has no parent. The format string must be + * nul-terminated and is formatted into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewOpaquef(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional args as needed */ +{ + mxml_node_t *node; /* New node */ + va_list ap; /* Pointer to arguments */ + + +#ifdef MXMLDEBUG + fprintf(stderr, "mxmlNewOpaquef(parent=%p, format=\"%s\", ...)\n", parent, format ? format : "(null)"); +#endif /* MXMLDEBUG */ + + /* + * Range check input... + */ + + if (!format) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL) + { + va_start(ap, format); + + node->value.opaque = _mxml_vstrdupf(format, ap); + + va_end(ap); + } + + return (node); +} + + /* * 'mxmlNewReal()' - Create a new real number node. * * The new real number node is added to the end of the specified parent's - * child list. The constant MXML_NO_PARENT can be used to specify that + * child list. The constant @code MXML_NO_PARENT@ can be used to specify that * the new real number node has no parent. */ mxml_node_t * /* O - New node */ -mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ +mxmlNewReal(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ double real) /* I - Real number value */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Create the node and set the element name... @@ -500,24 +535,24 @@ mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ * 'mxmlNewText()' - Create a new text fragment node. * * The new text node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new - * text node has no parent. The whitespace parameter is used to specify - * whether leading whitespace is present before the node. The text + * list. The constant @code MXML_NO_PARENT@ can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The text * string must be nul-terminated and is copied into the new node. */ mxml_node_t * /* O - New node */ -mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ +mxmlNewText(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ - const char *string) /* I - String */ + const char *string) /* I - String */ { mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", parent, whitespace, string ? string : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -544,26 +579,26 @@ mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ * 'mxmlNewTextf()' - Create a new formatted text fragment node. * * The new text node is added to the end of the specified parent's child - * list. The constant MXML_NO_PARENT can be used to specify that the new - * text node has no parent. The whitespace parameter is used to specify - * whether leading whitespace is present before the node. The format + * list. The constant @code MXML_NO_PARENT@ can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The format * string must be nul-terminated and is formatted into the new node. */ mxml_node_t * /* O - New node */ -mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ +mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or @code MXML_NO_PARENT@ */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ - const char *format, /* I - Printf-style frmat string */ - ...) /* I - Additional args as needed */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional args as needed */ { mxml_node_t *node; /* New node */ va_list ap; /* Pointer to arguments */ -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", parent, whitespace, format ? format : "(null)"); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -593,16 +628,16 @@ mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ /* * 'mxmlRemove()' - Remove a node from its parent. * - * Does not free memory used by the node - use mxmlDelete() for that. - * This function does nothing if the node has no parent. + * This function does not free memory used by the node - use @link mxmlDelete@ + * for that. This function does nothing if the node has no parent. */ void mxmlRemove(mxml_node_t *node) /* I - Node to remove */ { -#if DEBUG > 1 +#ifdef MXMLDEBUG fprintf(stderr, "mxmlRemove(node=%p)\n", node); -#endif /* DEBUG */ +#endif /* MXMLDEBUG */ /* * Range check input... @@ -615,7 +650,7 @@ mxmlRemove(mxml_node_t *node) /* I - Node to remove */ * Remove from parent... */ -#if DEBUG > 1 +#if MXMLDEBUG > 1 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); if (node->parent) { @@ -626,7 +661,7 @@ mxmlRemove(mxml_node_t *node) /* I - Node to remove */ fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child); fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev); fprintf(stderr, " BEFORE: node->next=%p\n", node->next); -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ if (node->prev) node->prev->next = node->next; @@ -642,7 +677,7 @@ mxmlRemove(mxml_node_t *node) /* I - Node to remove */ node->prev = NULL; node->next = NULL; -#if DEBUG > 1 +#if MXMLDEBUG > 1 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); if (node->parent) { @@ -653,7 +688,7 @@ mxmlRemove(mxml_node_t *node) /* I - Node to remove */ fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child); fprintf(stderr, " AFTER: node->prev=%p\n", node->prev); fprintf(stderr, " AFTER: node->next=%p\n", node->next); -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ } @@ -661,7 +696,7 @@ mxmlRemove(mxml_node_t *node) /* I - Node to remove */ * 'mxmlNewXML()' - Create a new XML document tree. * * The "version" argument specifies the version number to put in the - * ?xml element node. If NULL, version 1.0 is assumed. + * ?xml element node. If @code NULL@, version "1.0" is assumed. * * @since Mini-XML 2.3@ */ @@ -683,7 +718,7 @@ mxmlNewXML(const char *version) /* I - Version number to use */ * 'mxmlRelease()' - Release a node. * * When the reference count reaches zero, the node (and any children) - * is deleted via mxmlDelete(). + * is deleted via @link mxmlDelete@. * * @since Mini-XML 2.3@ */ @@ -738,40 +773,40 @@ mxml_free(mxml_node_t *node) /* I - Node */ { case MXML_ELEMENT : if (node->value.element.name) - PhFree(node->value.element.name); - - if (node->value.element.num_attrs) - { - for (i = 0; i < node->value.element.num_attrs; i ++) - { - if (node->value.element.attrs[i].name) - PhFree(node->value.element.attrs[i].name); - if (node->value.element.attrs[i].value) - PhFree(node->value.element.attrs[i].value); - } - - PhFree(node->value.element.attrs); - } + PhFree(node->value.element.name); + + if (node->value.element.num_attrs) + { + for (i = 0; i < node->value.element.num_attrs; i ++) + { + if (node->value.element.attrs[i].name) + PhFree(node->value.element.attrs[i].name); + if (node->value.element.attrs[i].value) + PhFree(node->value.element.attrs[i].value); + } + + PhFree(node->value.element.attrs); + } break; case MXML_INTEGER : /* Nothing to do */ break; case MXML_OPAQUE : if (node->value.opaque) - PhFree(node->value.opaque); + PhFree(node->value.opaque); break; case MXML_REAL : /* Nothing to do */ break; case MXML_TEXT : if (node->value.text.string) - PhFree(node->value.text.string); + PhFree(node->value.text.string); break; case MXML_CUSTOM : if (node->value.custom.data && - node->value.custom.destroy) - (*(node->value.custom.destroy))(node->value.custom.data); - break; + node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + break; default : break; } @@ -795,9 +830,9 @@ mxml_new(mxml_node_t *parent, /* I - Parent node */ mxml_node_t *node; /* New node */ -#if DEBUG > 1 +#if MXMLDEBUG > 1 fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type); -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ /* * Allocate memory for the node... @@ -805,16 +840,16 @@ mxml_new(mxml_node_t *parent, /* I - Parent node */ if ((node = PhAllocateExSafe(sizeof(mxml_node_t), HEAP_ZERO_MEMORY)) == NULL) { -#if DEBUG > 1 +#if MXMLDEBUG > 1 fputs(" returning NULL\n", stderr); -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ return (NULL); } -#if DEBUG > 1 +#if MXMLDEBUG > 1 fprintf(stderr, " returning %p\n", node); -#endif /* DEBUG > 1 */ +#endif /* MXMLDEBUG > 1 */ /* * Set the node type... @@ -836,8 +871,3 @@ mxml_new(mxml_node_t *parent, /* I - Parent node */ return (node); } - - -/* - * End of "$Id: mxml-node.c 462 2016-06-11 20:51:49Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-private.c b/phlib/mxml/mxml-private.c similarity index 88% rename from ProcessHacker/mxml/mxml-private.c rename to phlib/mxml/mxml-private.c index 6f6e480c2d61..726acc9a8705 100644 --- a/ProcessHacker/mxml/mxml-private.c +++ b/phlib/mxml/mxml-private.c @@ -1,17 +1,12 @@ /* - * "$Id: mxml-private.c 451 2014-01-04 21:50:06Z msweet $" + * Private functions for Mini-XML, a small XML file parsing library. * - * Private functions for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2014 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -59,7 +54,7 @@ mxml_error(const char *format, /* I - Printf-style format string */ va_list ap; /* Pointer to arguments */ char s[1024]; /* Message string */ _mxml_global_t *global = _mxml_global(); - /* Global data */ + /* Global data */ /* @@ -147,7 +142,7 @@ mxml_real_cb(mxml_node_t *node) /* I - Current node */ static pthread_key_t _mxml_key = -1; /* Thread local storage key */ static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT; - /* One-time initialization object */ + /* One-time initialization object */ static void _mxml_init(void); static void _mxml_destructor(void *g); @@ -221,7 +216,7 @@ _mxml_init(void) } -#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/ +#elif defined(_WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/ # include static DWORD _mxml_tls_index; /* Index for global storage */ @@ -316,8 +311,3 @@ _mxml_global(void) return (&global); } #endif /* HAVE_PTHREAD_H */ - - -/* - * End of "$Id: mxml-private.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/phlib/mxml/mxml-private.h b/phlib/mxml/mxml-private.h new file mode 100644 index 000000000000..a97d76947748 --- /dev/null +++ b/phlib/mxml/mxml-private.h @@ -0,0 +1,98 @@ +/* + * Private definitions for Mini-XML, a small XML file parsing library. + * + * https://www.msweet.org/mxml + * + * Copyright © 2003-2019 by Michael R Sweet. + * + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Private structures... + */ + +typedef struct _mxml_attr_s /**** An XML element attribute value. ****/ +{ + char *name; /* Attribute name */ + char *value; /* Attribute value */ +} _mxml_attr_t; + +typedef struct _mxml_element_s /**** An XML element value. ****/ +{ + char *name; /* Name of element */ + int num_attrs; /* Number of attributes */ + _mxml_attr_t *attrs; /* Attributes */ +} _mxml_element_t; + +typedef struct _mxml_text_s /**** An XML text value. ****/ +{ + int whitespace; /* Leading whitespace? */ + char *string; /* Fragment string */ +} _mxml_text_t; + +typedef struct _mxml_custom_s /**** An XML custom value. ****/ +{ + void *data; /* Pointer to (allocated) custom data */ + mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */ +} _mxml_custom_t; + +typedef union _mxml_value_u /**** An XML node value. ****/ +{ + _mxml_element_t element; /* Element */ + int integer; /* Integer number */ + char *opaque; /* Opaque string */ + double real; /* Real number */ + _mxml_text_t text; /* Text fragment */ + _mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */ +} _mxml_value_t; + +struct _mxml_node_s /**** An XML node. ****/ +{ + mxml_type_t type; /* Node type */ + struct _mxml_node_s *next; /* Next node under same parent */ + struct _mxml_node_s *prev; /* Previous node under same parent */ + struct _mxml_node_s *parent; /* Parent node */ + struct _mxml_node_s *child; /* First child node */ + struct _mxml_node_s *last_child; /* Last child node */ + _mxml_value_t value; /* Node value */ + int ref_count; /* Use count */ + void *user_data; /* User data */ +}; + +struct _mxml_index_s /**** An XML node index. ****/ +{ + char *attr; /* Attribute used for indexing or NULL */ + int num_nodes; /* Number of nodes in index */ + int alloc_nodes; /* Allocated nodes in index */ + int cur_node; /* Current node */ + mxml_node_t **nodes; /* Node array */ +}; + +typedef struct _mxml_global_s /**** Global, per-thread data ****/ + +{ + void (*error_cb)(const char *); + int num_entity_cbs; + int (*entity_cbs[100])(const char *name); + int wrap; + mxml_custom_load_cb_t custom_load_cb; + mxml_custom_save_cb_t custom_save_cb; +} _mxml_global_t; + + +/* + * Functions... + */ + +extern _mxml_global_t *_mxml_global(void); +extern int _mxml_entity_cb(const char *name); diff --git a/ProcessHacker/mxml/mxml-search.c b/phlib/mxml/mxml-search.c similarity index 71% rename from ProcessHacker/mxml/mxml-search.c rename to phlib/mxml/mxml-search.c index ec7ef3f357c4..8e2ef0afc29b 100644 --- a/ProcessHacker/mxml/mxml-search.c +++ b/phlib/mxml/mxml-search.c @@ -1,18 +1,12 @@ /* - * "$Id: mxml-search.c 451 2014-01-04 21:50:06Z msweet $" + * Search/navigation functions for Mini-XML, a small XML file parsing library. * - * Search/navigation functions for Mini-XML, a small XML-like file - * parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2014 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -20,29 +14,29 @@ */ #include "config.h" -#include "mxml.h" +#include "mxml-private.h" /* * 'mxmlFindElement()' - Find the named element. * * The search is constrained by the name, attribute name, and value; any - * NULL names or values are treated as wildcards, so different kinds of + * @code NULL@ names or values are treated as wildcards, so different kinds of * searches can be implemented by looking for all elements of a given name * or all elements with a specific attribute. The descend argument determines * whether the search descends into child nodes; normally you will use - * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find - * additional direct descendents of the node. The top node argument + * @code MXML_DESCEND_FIRST@ for the initial search and @code MXML_NO_DESCEND@ + * to find additional direct descendents of the node. The top node argument * constrains the search to a particular node's children. */ -mxml_node_t * /* O - Element node or NULL */ +mxml_node_t * /* O - Element node or @code NULL@ */ mxmlFindElement(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ - const char *name, /* I - Element name or NULL for any */ - const char *attr, /* I - Attribute name, or NULL for none */ - const char *value, /* I - Attribute value, or NULL for any */ - int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ + const char *element, /* I - Element name or @code NULL@ for any */ + const char *attr, /* I - Attribute name, or @code NULL@ for none */ + const char *value, /* I - Attribute value, or @code NULL@ for any */ + int descend) /* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */ { const char *temp; /* Current attribute value */ @@ -72,7 +66,7 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ if (node->type == MXML_ELEMENT && node->value.element.name && - (!name || !strcmp(node->value.element.name, name))) + (!element || !strcmp(node->value.element.name, element))) { /* * See if we need to check for an attribute... @@ -89,10 +83,10 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ { /* * OK, we have the attribute, does it match? - */ + */ - if (!value || !strcmp(value, temp)) - return (node); /* Yes, return it... */ + if (!value || !strcmp(value, temp)) + return (node); /* Yes, return it... */ } } @@ -123,9 +117,9 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ * @since Mini-XML 2.7@ */ -mxml_node_t * /* O - Found node or NULL */ +mxml_node_t * /* O - Found node or @code NULL@ */ mxmlFindPath(mxml_node_t *top, /* I - Top node */ - const char *path) /* I - Path to element */ + const char *path) /* I - Path to element */ { mxml_node_t *node; /* Current node */ char element[256]; /* Current element name */ @@ -201,14 +195,14 @@ mxmlFindPath(mxml_node_t *top, /* I - Top node */ * 'mxmlWalkNext()' - Walk to the next logical node in the tree. * * The descend argument controls whether the first child is considered - * to be the next node. The top node argument constrains the walk to + * to be the next node. The top node argument constrains the walk to * the node's children. */ -mxml_node_t * /* O - Next node or NULL */ +mxml_node_t * /* O - Next node or @code NULL@ */ mxmlWalkNext(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ - int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ + int descend) /* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */ { if (!node) return (NULL); @@ -239,14 +233,14 @@ mxmlWalkNext(mxml_node_t *node, /* I - Current node */ * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree. * * The descend argument controls whether the previous node's last child - * is considered to be the previous node. The top node argument constrains + * is considered to be the previous node. The top node argument constrains * the walk to the node's children. */ -mxml_node_t * /* O - Previous node or NULL */ +mxml_node_t * /* O - Previous node or @code NULL@ */ mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ - int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ + int descend) /* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */ { if (!node || node == top) return (NULL); @@ -273,8 +267,3 @@ mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ else return (NULL); } - - -/* - * End of "$Id: mxml-search.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/ProcessHacker/mxml/mxml-set.c b/phlib/mxml/mxml-set.c similarity index 74% rename from ProcessHacker/mxml/mxml-set.c rename to phlib/mxml/mxml-set.c index be7593f4e661..cf8d0c105629 100644 --- a/ProcessHacker/mxml/mxml-set.c +++ b/phlib/mxml/mxml-set.c @@ -1,26 +1,20 @@ /* - * "$Id: mxml-set.c 451 2014-01-04 21:50:06Z msweet $" + * Node set functions for Mini-XML, a small XML file parsing library. * - * Node set functions for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2014 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* * Include necessary headers... */ -#include #include "config.h" -#include "mxml.h" +#include "mxml-private.h" /* @@ -35,6 +29,9 @@ int /* O - 0 on success, -1 on failure */ mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */ const char *data) /* I - New data string */ { + char *s; /* String pointer */ + + /* * Range check input... */ @@ -49,14 +46,19 @@ mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */ strncmp(node->value.element.name, "![CDATA[", 8)) return (-1); + if (data == (node->value.element.name + 8)) + return (0); + /* - * Free any old element value and set the new value... + * Allocate the new value, free any old element value, and set the new value... */ + s = _mxml_strdupf("![CDATA[%s", data); + if (node->value.element.name) - PhFree(node->value.element.name); + PhFree(node->value.element.name); - node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + node->value.element.name = s; return (0); } @@ -87,6 +89,12 @@ mxmlSetCustom( if (!node || node->type != MXML_CUSTOM) return (-1); + if (data == node->value.custom.data) + { + node->value.custom.destroy = destroy; + return (0); + } + /* * Free any old element value and set the new value... */ @@ -118,12 +126,15 @@ mxmlSetElement(mxml_node_t *node, /* I - Node to set */ if (!node || node->type != MXML_ELEMENT || !name) return (-1); + if (name == node->value.element.name) + return (0); + /* * Free any old element value and set the new value... */ if (node->value.element.name) - PhFree(node->value.element.name); + PhFree(node->value.element.name); node->value.element.name = PhDuplicateBytesZSafe((char *)name); @@ -183,12 +194,15 @@ mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */ if (!node || node->type != MXML_OPAQUE || !opaque) return (-1); + if (node->value.opaque == opaque) + return (0); + /* * Free any old opaque value and set the new value... */ if (node->value.opaque) - PhFree(node->value.opaque); + PhFree(node->value.opaque); node->value.opaque = PhDuplicateBytesZSafe((char *)opaque); @@ -196,6 +210,51 @@ mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */ } +/* + * 'mxmlSetOpaquef()' - Set the value of an opaque string node to a formatted string. + * + * The node is not changed if it (or its first child) is not an opaque node. + * + * @since Mini-XML 2.11@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetOpaquef(mxml_node_t *node, /* I - Node to set */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + char *s; /* Temporary string */ + + + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_OPAQUE) + node = node->child; + + if (!node || node->type != MXML_OPAQUE || !format) + return (-1); + + /* + * Format the new string, free any old string value, and set the new value... + */ + + va_start(ap, format); + s = _mxml_vstrdupf(format, ap); + va_end(ap); + + if (node->value.opaque) + PhFree(node->value.opaque); + + node->value.opaque = s; + + return (0); +} + + /* * 'mxmlSetReal()' - Set the value of a real number node. * @@ -236,7 +295,7 @@ mxmlSetReal(mxml_node_t *node, /* I - Node to set */ int /* O - 0 on success, -1 on failure */ mxmlSetText(mxml_node_t *node, /* I - Node to set */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ - const char *string) /* I - String */ + const char *string) /* I - String */ { /* * Range check input... @@ -249,12 +308,18 @@ mxmlSetText(mxml_node_t *node, /* I - Node to set */ if (!node || node->type != MXML_TEXT || !string) return (-1); + if (string == node->value.text.string) + { + node->value.text.whitespace = whitespace; + return (0); + } + /* * Free any old string value and set the new value... */ if (node->value.text.string) - PhFree(node->value.text.string); + PhFree(node->value.text.string); node->value.text.whitespace = whitespace; node->value.text.string = PhDuplicateBytesZSafe((char *)string); @@ -273,9 +338,10 @@ int /* O - 0 on success, -1 on failure */ mxmlSetTextf(mxml_node_t *node, /* I - Node to set */ int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ const char *format, /* I - Printf-style format string */ - ...) /* I - Additional arguments as needed */ + ...) /* I - Additional arguments as needed */ { va_list ap; /* Pointer to arguments */ + char *s; /* Temporary string */ /* @@ -293,15 +359,15 @@ mxmlSetTextf(mxml_node_t *node, /* I - Node to set */ * Free any old string value and set the new value... */ + va_start(ap, format); + s = _mxml_vstrdupf(format, ap); + va_end(ap); + if (node->value.text.string) PhFree(node->value.text.string); - va_start(ap, format); - node->value.text.whitespace = whitespace; - node->value.text.string = _mxml_strdupf(format, ap); - - va_end(ap); + node->value.text.string = s; return (0); } @@ -331,8 +397,3 @@ mxmlSetUserData(mxml_node_t *node, /* I - Node to set */ node->user_data = data; return (0); } - - -/* - * End of "$Id: mxml-set.c 451 2014-01-04 21:50:06Z msweet $". - */ diff --git a/phlib/mxml/mxml-string.c b/phlib/mxml/mxml-string.c new file mode 100644 index 000000000000..942087382dcf --- /dev/null +++ b/phlib/mxml/mxml-string.c @@ -0,0 +1,559 @@ +/* + * String functions for Mini-XML, a small XML file parsing library. + * + * https://www.msweet.org/mxml + * + * Copyright © 2003-2019 by Michael R Sweet. + * + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml-private.h" + +/* + * The va_copy macro is part of C99, but many compilers don't implement it. + * Provide a "direct assignment" implmentation when va_copy isn't defined... + */ + +#ifndef va_copy +# ifdef __va_copy +# define va_copy(dst,src) __va_copy(dst,src) +# else +# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list)) +# endif /* __va_copy */ +#endif /* va_copy */ + + +#ifndef HAVE_SNPRINTF +/* + * '_mxml_snprintf()' - Format a string. + */ + +int /* O - Number of bytes formatted */ +_mxml_snprintf(char *buffer, /* I - Output buffer */ + size_t bufsize, /* I - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument list */ + int bytes; /* Number of bytes formatted */ + + + va_start(ap, format); + bytes = vsnprintf(buffer, bufsize, format, ap); + va_end(ap); + + return (bytes); +} +#endif /* !HAVE_SNPRINTF */ + + +/* + * '_mxml_strdup()' - Duplicate a string. + */ + +#ifndef HAVE_STRDUP +char * /* O - New string pointer */ +_mxml_strdup(const char *s) /* I - String to duplicate */ +{ + char *t; /* New string pointer */ + + + if (s == NULL) + return (NULL); + + if ((t = malloc(strlen(s) + 1)) == NULL) + return (NULL); + + return (strcpy(t, s)); +} +#endif /* !HAVE_STRDUP */ + + +/* + * '_mxml_strdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_strdupf(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to additional arguments */ + char *s; /* Pointer to formatted string */ + + + /* + * Get a pointer to the additional arguments, format the string, + * and return it... + */ + + va_start(ap, format); +#ifdef HAVE_VASPRINTF + if (vasprintf(&s, format, ap) < 0) + s = NULL; +#else + s = _mxml_vstrdupf(format, ap); +#endif /* HAVE_VASPRINTF */ + va_end(ap); + + return (s); +} + + +#ifndef HAVE_STRLCAT +/* + * '_mxml_strlcat()' - Safely concatenate a string. + */ + +size_t /* O - Number of bytes copied */ +_mxml_strlcat(char *dst, /* I - Destination buffer */ + const char *src, /* I - Source string */ + size_t dstsize) /* I - Size of destinatipon buffer */ +{ + size_t srclen; /* Length of source string */ + size_t dstlen; /* Length of destination string */ + + + /* + * Figure out how much room is left... + */ + + dstlen = strlen(dst); + + if (dstsize <= (dstlen + 1)) + return (dstlen); /* No room, return immediately... */ + + dstsize -= dstlen + 1; + + /* + * Figure out how much room is needed... + */ + + srclen = strlen(src); + + /* + * Copy the appropriate amount... + */ + + if (srclen > dstsize) + srclen = dstsize; + + memmove(dst + dstlen, src, srclen); + dst[dstlen + srclen] = '\0'; + + return (dstlen + srclen); +} +#endif /* !HAVE_STRLCAT */ + + +#ifndef HAVE_STRLCPY +/* + * '_mxml_strlcpy()' - Safely copy a string. + */ + +size_t /* O - Number of bytes copied */ +_mxml_strlcpy(char *dst, /* I - Destination buffer */ + const char *src, /* I - Source string */ + size_t dstsize) /* I - Size of destinatipon buffer */ +{ + size_t srclen; /* Length of source string */ + + + /* + * Figure out how much room is needed... + */ + + dstsize --; + + srclen = strlen(src); + + /* + * Copy the appropriate amount... + */ + + if (srclen > dstsize) + srclen = dstsize; + + memmove(dst, src, srclen); + dst[srclen] = '\0'; + + return (srclen); +} +#endif /* !HAVE_STRLCPY */ + + +#ifndef HAVE_VSNPRINTF +/* + * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. + */ + +int /* O - Number of bytes formatted */ +_mxml_vsnprintf(char *buffer, /* O - Output buffer */ + size_t bufsize, /* O - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + char *bufptr, /* Pointer to position in buffer */ + *bufend, /* Pointer to end of buffer */ + sign, /* Sign of format width */ + size, /* Size character (h, l, L) */ + type; /* Format type character */ + int width, /* Width of field */ + prec; /* Number of characters of precision */ + char tformat[100], /* Temporary format string for sprintf() */ + *tptr, /* Pointer into temporary format */ + temp[1024]; /* Buffer for formatted numbers */ + char *s; /* Pointer to string */ + int slen; /* Length of string */ + int bytes; /* Total number of bytes needed */ + + + /* + * Loop through the format string, formatting as needed... + */ + + bufptr = buffer; + bufend = buffer + bufsize - 1; + bytes = 0; + + while (*format) + { + if (*format == '%') + { + tptr = tformat; + *tptr++ = *format++; + + if (*format == '%') + { + if (bufptr && bufptr < bufend) + *bufptr++ = *format; + bytes ++; + format ++; + continue; + } + else if (strchr(" -+#\'", *format)) + { + *tptr++ = *format; + sign = *format++; + } + else + sign = 0; + + if (*format == '*') + { + /* + * Get width from argument... + */ + + format ++; + width = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); + tptr += strlen(tptr); + } + else + { + width = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + width = width * 10 + *format++ - '0'; + } + } + + if (*format == '.') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + format ++; + + if (*format == '*') + { + /* + * Get precision from argument... + */ + + format ++; + prec = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); + tptr += strlen(tptr); + } + else + { + prec = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + prec = prec * 10 + *format++ - '0'; + } + } + } + else + prec = -1; + + if (*format == 'l' && format[1] == 'l') + { + size = 'L'; + + if (tptr < (tformat + sizeof(tformat) - 2)) + { + *tptr++ = 'l'; + *tptr++ = 'l'; + } + + format += 2; + } + else if (*format == 'h' || *format == 'l' || *format == 'L') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + size = *format++; + } + + if (!*format) + break; + + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + type = *format++; + *tptr = '\0'; + + switch (type) + { + case 'E' : /* Floating point formats */ + case 'G' : + case 'e' : + case 'f' : + case 'g' : + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, double)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'B' : /* Integer formats */ + case 'X' : + case 'b' : + case 'd' : + case 'i' : + case 'o' : + case 'u' : + case 'x' : + if ((width + 2) > sizeof(temp)) + break; + +#ifdef HAVE_LONG_LONG + if (size == 'L') + sprintf(temp, tformat, va_arg(ap, long long)); + else +#endif /* HAVE_LONG_LONG */ + sprintf(temp, tformat, va_arg(ap, int)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'p' : /* Pointer value */ + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, void *)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'c' : /* Character or character array */ + bytes += width; + + if (bufptr) + { + if (width <= 1) + *bufptr++ = va_arg(ap, int); + else + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + memcpy(bufptr, va_arg(ap, char *), (size_t)width); + bufptr += width; + } + } + break; + + case 's' : /* String */ + if ((s = va_arg(ap, char *)) == NULL) + s = "(null)"; + + slen = strlen(s); + if (slen > width && prec != width) + width = slen; + + bytes += width; + + if (bufptr) + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + if (slen > width) + slen = width; + + if (sign == '-') + { + strncpy(bufptr, s, (size_t)slen); + memset(bufptr + slen, ' ', (size_t)(width - slen)); + } + else + { + memset(bufptr, ' ', (size_t)(width - slen)); + strncpy(bufptr + width - slen, s, (size_t)slen); + } + + bufptr += width; + } + break; + + case 'n' : /* Output number of chars so far */ + *(va_arg(ap, int *)) = bytes; + break; + } + } + else + { + bytes ++; + + if (bufptr && bufptr < bufend) + *bufptr++ = *format; + + format ++; + } + } + + /* + * Nul-terminate the string and return the number of characters needed. + */ + + *bufptr = '\0'; + + return (bytes); +} +#endif /* !HAVE_VSNPRINTF */ + + +/* + * '_mxml_vstrdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_vstrdupf(const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ +#ifdef HAVE_VASPRINTF + char *s; /* String */ + + if (vasprintf(&s, format, ap) < 0) + s = NULL; + + return (s); + +#else + int bytes; /* Number of bytes required */ + char *buffer; /* String buffer */ + + + /* + * First format with a tiny buffer; this will tell us how many bytes are + * needed... + */ + +# ifdef _WIN32 + bytes = _vscprintf(format, ap); + +# else + va_list apcopy; /* Copy of argument list */ + char* temp[256]; /* Small buffer for first vsnprintf */ + + va_copy(apcopy, ap); + if ((bytes = vsnprintf(temp, sizeof(temp), format, apcopy)) < sizeof(temp)) + { + /* + * Hey, the formatted string fits in the tiny buffer, so just dup that... + */ + + return (PhDuplicateBytesZSafe(temp)); + } +# endif /* _WIN32 */ + + /* + * Allocate memory for the whole thing and reformat to the new buffer... + */ + + if ((buffer = PhAllocateExSafe(bytes + 1, HEAP_ZERO_MEMORY)) != NULL) + vsnprintf(buffer, bytes + 1, format, ap); + + /* + * Return the new string... + */ + + return (buffer); +#endif /* HAVE_VASPRINTF */ +} diff --git a/ProcessHacker/mxml/mxml.h b/phlib/mxml/mxml.h similarity index 74% rename from ProcessHacker/mxml/mxml.h rename to phlib/mxml/mxml.h index 6e8015874882..79122d3adc2b 100644 --- a/ProcessHacker/mxml/mxml.h +++ b/phlib/mxml/mxml.h @@ -1,17 +1,12 @@ /* - * "$Id: mxml.h 464 2016-06-12 21:16:14Z msweet $" + * Header file for Mini-XML, a small XML file parsing library. * - * Header file for Mini-XML, a small XML-like file parsing library. + * https://www.msweet.org/mxml * - * Copyright 2003-2016 by Michael R Sweet. + * Copyright © 2003-2019 by Michael R Sweet. * - * These coded instructions, statements, and computer programs are the - * property of Michael R Sweet and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "COPYING" - * which should have been included with this file. If this file is - * missing or damaged, see the license at: - * - * http://www.msweet.org/projects.php/Mini-XML + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -30,20 +25,23 @@ # include # include # include -# include -#ifdef _PHAPP_ -#define PHMXMLAPI __declspec(dllexport) -#else -#define PHMXMLAPI + // dmex: custom headers. +#include + +#ifdef _PHAPP_ +#define PHMXMLAPI __declspec(dllexport) +#else +#define PHMXMLAPI #endif + /* * Constants... */ -# define MXML_MAJOR_VERSION 2 /* Major version number */ -# define MXML_MINOR_VERSION 10 /* Minor version number */ +# define MXML_MAJOR_VERSION 3 /* Major version number */ +# define MXML_MINOR_VERSION 0 /* Minor version number */ # define MXML_TAB 8 /* Tabs every N columns */ @@ -105,66 +103,9 @@ typedef void (*mxml_custom_destroy_cb_t)(void *); typedef void (*mxml_error_cb_t)(const char *); /**** Error callback function ****/ -typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/ -{ - char *name; /* Attribute name */ - char *value; /* Attribute value */ -} mxml_attr_t; - -typedef struct mxml_element_s /**** An XML element value. @private@ ****/ -{ - char *name; /* Name of element */ - int num_attrs; /* Number of attributes */ - mxml_attr_t *attrs; /* Attributes */ -} mxml_element_t; - -typedef struct mxml_text_s /**** An XML text value. @private@ ****/ -{ - int whitespace; /* Leading whitespace? */ - char *string; /* Fragment string */ -} mxml_text_t; - -typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/ -{ - void *data; /* Pointer to (allocated) custom data */ - mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */ -} mxml_custom_t; +typedef struct _mxml_node_s mxml_node_t; /**** An XML node. ****/ -typedef union mxml_value_u /**** An XML node value. @private@ ****/ -{ - mxml_element_t element; /* Element */ - int integer; /* Integer number */ - char *opaque; /* Opaque string */ - double real; /* Real number */ - mxml_text_t text; /* Text fragment */ - mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */ -} mxml_value_t; - -struct mxml_node_s /**** An XML node. @private@ ****/ -{ - mxml_type_t type; /* Node type */ - struct mxml_node_s *next; /* Next node under same parent */ - struct mxml_node_s *prev; /* Previous node under same parent */ - struct mxml_node_s *parent; /* Parent node */ - struct mxml_node_s *child; /* First child node */ - struct mxml_node_s *last_child; /* Last child node */ - mxml_value_t value; /* Node value */ - int ref_count; /* Use count */ - void *user_data; /* User data */ -}; - -typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/ - -struct mxml_index_s /**** An XML node index. @private@ ****/ -{ - char *attr; /* Attribute used for indexing or NULL */ - int num_nodes; /* Number of nodes in index */ - int alloc_nodes; /* Allocated nodes in index */ - int cur_node; /* Current node */ - mxml_node_t **nodes; /* Node array */ -}; - -typedef struct mxml_index_s mxml_index_t; +typedef struct _mxml_index_s mxml_index_t; /**** An XML node index. ****/ typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *); @@ -204,6 +145,8 @@ PHMXMLAPI extern void mxmlDelete(mxml_node_t *node); PHMXMLAPI extern void mxmlElementDeleteAttr(mxml_node_t *node, const char *name); PHMXMLAPI extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name); +PHMXMLAPI extern const char *mxmlElementGetAttrByIndex(mxml_node_t *node, int idx, const char **name); +PHMXMLAPI extern int mxmlElementGetAttrCount(mxml_node_t *node); PHMXMLAPI extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, const char *value); extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, @@ -217,23 +160,23 @@ extern const char *mxmlEntityGetName(int val); extern int mxmlEntityGetValue(const char *name); extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); PHMXMLAPI extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, - const char *name, const char *attr, + const char *element, const char *attr, const char *value, int descend); extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path); extern const char *mxmlGetCDATA(mxml_node_t *node); extern const void *mxmlGetCustom(mxml_node_t *node); extern const char *mxmlGetElement(mxml_node_t *node); -extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node); +PHMXMLAPI extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node); extern int mxmlGetInteger(mxml_node_t *node); extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node); -extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node); -extern const char *mxmlGetOpaque(mxml_node_t *node); +PHMXMLAPI extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node); +PHMXMLAPI extern const char *mxmlGetOpaque(mxml_node_t *node); extern mxml_node_t *mxmlGetParent(mxml_node_t *node); extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node); extern double mxmlGetReal(mxml_node_t *node); extern int mxmlGetRefCount(mxml_node_t *node); extern const char *mxmlGetText(mxml_node_t *node, int *whitespace); -extern mxml_type_t mxmlGetType(mxml_node_t *node); +PHMXMLAPI extern mxml_type_t mxmlGetType(mxml_node_t *node); extern void *mxmlGetUserData(mxml_node_t *node); extern void mxmlIndexDelete(mxml_index_t *ind); extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind); @@ -256,11 +199,14 @@ PHMXMLAPI extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, PHMXMLAPI extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name); PHMXMLAPI extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer); PHMXMLAPI extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque); +extern mxml_node_t *mxmlNewOpaquef(mxml_node_t *parent, const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 2, 3))) +# endif /* __GNUC__ */ +; PHMXMLAPI extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real); -PHMXMLAPI extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, - const char *string); -extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, - const char *format, ...) +PHMXMLAPI extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, const char *string); +extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, const char *format, ...) # ifdef __GNUC__ __attribute__ ((__format__ (__printf__, 3, 4))) # endif /* __GNUC__ */ @@ -275,7 +221,7 @@ PHMXMLAPI extern int mxmlSaveFd(mxml_node_t *node, HANDLE fd, mxml_save_cb_t cb); extern int mxmlSaveFile(mxml_node_t *node, FILE *fp, mxml_save_cb_t cb); -PHMXMLAPI extern int mxmlSaveString(mxml_node_t *node, char *buffer, +extern int mxmlSaveString(mxml_node_t *node, char *buffer, int bufsize, mxml_save_cb_t cb); extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, HANDLE fd, mxml_type_t (*cb)(mxml_node_t *), @@ -295,6 +241,11 @@ PHMXMLAPI extern int mxmlSetElement(mxml_node_t *node, const char *name); PHMXMLAPI extern void mxmlSetErrorCallback(mxml_error_cb_t cb); extern int mxmlSetInteger(mxml_node_t *node, int integer); PHMXMLAPI extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque); +extern int mxmlSetOpaquef(mxml_node_t *node, const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 2, 3))) +# endif /* __GNUC__ */ +; extern int mxmlSetReal(mxml_node_t *node, double real); PHMXMLAPI extern int mxmlSetText(mxml_node_t *node, int whitespace, const char *string); @@ -331,8 +282,3 @@ PHMXMLAPI extern mxml_type_t mxml_real_cb(mxml_node_t *node); } # endif /* __cplusplus */ #endif /* !_mxml_h_ */ - - -/* - * End of "$Id: mxml.h 464 2016-06-12 21:16:14Z msweet $". - */ diff --git a/phlib/native.c b/phlib/native.c index 5173837f70b6..28af3916cdc2 100644 --- a/phlib/native.c +++ b/phlib/native.c @@ -1,6462 +1,8422 @@ -/* - * Process Hacker - - * native wrapper and support functions - * - * Copyright (C) 2009-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include -#include -#include -#include - -#define PH_DEVICE_PREFIX_LENGTH 64 -#define PH_DEVICE_MUP_PREFIX_MAX_COUNT 16 - -typedef BOOLEAN (NTAPI *PPHP_ENUM_PROCESS_MODULES_CALLBACK)( - _In_ HANDLE ProcessHandle, - _In_ PLDR_DATA_TABLE_ENTRY Entry, - _In_ PVOID AddressOfEntry, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ); - -typedef BOOLEAN (NTAPI *PPHP_ENUM_PROCESS_MODULES32_CALLBACK)( - _In_ HANDLE ProcessHandle, - _In_ PLDR_DATA_TABLE_ENTRY32 Entry, - _In_ ULONG AddressOfEntry, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ); - -static PH_INITONCE PhDevicePrefixesInitOnce = PH_INITONCE_INIT; - -static UNICODE_STRING PhDevicePrefixes[26]; -static PH_QUEUED_LOCK PhDevicePrefixesLock = PH_QUEUED_LOCK_INIT; - -static PPH_STRING PhDeviceMupPrefixes[PH_DEVICE_MUP_PREFIX_MAX_COUNT] = { 0 }; -static ULONG PhDeviceMupPrefixesCount = 0; -static PH_QUEUED_LOCK PhDeviceMupPrefixesLock = PH_QUEUED_LOCK_INIT; - -static PH_INITONCE PhPredefineKeyInitOnce = PH_INITONCE_INIT; -static UNICODE_STRING PhPredefineKeyNames[PH_KEY_MAXIMUM_PREDEFINE] = -{ - RTL_CONSTANT_STRING(L"\\Registry\\Machine"), - RTL_CONSTANT_STRING(L"\\Registry\\User"), - RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Classes"), - { 0, 0, NULL } -}; -static HANDLE PhPredefineKeyHandles[PH_KEY_MAXIMUM_PREDEFINE] = { 0 }; - -/** - * Queries information about the token of the current process. - */ -PH_TOKEN_ATTRIBUTES PhGetOwnTokenAttributes( - VOID - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static PH_TOKEN_ATTRIBUTES attributes; - - if (PhBeginInitOnce(&initOnce)) - { - if (NT_SUCCESS(NtOpenProcessToken( - NtCurrentProcess(), - TOKEN_QUERY, - &attributes.TokenHandle - ))) - { - BOOLEAN elevated = TRUE; - TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeFull; - - if (WINDOWS_HAS_UAC) - { - PhGetTokenIsElevated(attributes.TokenHandle, &elevated); - PhGetTokenElevationType(attributes.TokenHandle, &elevationType); - } - - attributes.Elevated = elevated; - attributes.ElevationType = elevationType; - } - - PhEndInitOnce(&initOnce); - } - - return attributes; -} - -/** - * Opens a process. - * - * \param ProcessHandle A variable which receives a handle to the process. - * \param DesiredAccess The desired access to the process. - * \param ProcessId The ID of the process. - */ -NTSTATUS PhOpenProcess( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ HANDLE ProcessId - ) -{ - NTSTATUS status; - OBJECT_ATTRIBUTES objectAttributes; - CLIENT_ID clientId; - - clientId.UniqueProcess = ProcessId; - clientId.UniqueThread = NULL; - - if (KphIsVerified() && (DesiredAccess & KPH_PROCESS_READ_ACCESS) == DesiredAccess) - { - status = KphOpenProcess( - ProcessHandle, - DesiredAccess, - &clientId - ); - } - else - { - InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - status = NtOpenProcess( - ProcessHandle, - DesiredAccess, - &objectAttributes, - &clientId - ); - - if (status == STATUS_ACCESS_DENIED && KphIsVerified()) - { - status = KphOpenProcess( - ProcessHandle, - DesiredAccess, - &clientId - ); - } - } - - return status; -} - -/** Limited API for untrusted/external code. */ -NTSTATUS PhOpenProcessPublic( - _Out_ PHANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ HANDLE ProcessId - ) -{ - OBJECT_ATTRIBUTES objectAttributes; - CLIENT_ID clientId; - - InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - clientId.UniqueProcess = ProcessId; - clientId.UniqueThread = NULL; - - return NtOpenProcess( - ProcessHandle, - DesiredAccess, - &objectAttributes, - &clientId - ); -} - -/** - * Opens a thread. - * - * \param ThreadHandle A variable which receives a handle to the thread. - * \param DesiredAccess The desired access to the thread. - * \param ThreadId The ID of the thread. - */ -NTSTATUS PhOpenThread( - _Out_ PHANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ HANDLE ThreadId - ) -{ - NTSTATUS status; - OBJECT_ATTRIBUTES objectAttributes; - CLIENT_ID clientId; - - clientId.UniqueProcess = NULL; - clientId.UniqueThread = ThreadId; - - if (KphIsVerified() && (DesiredAccess & KPH_THREAD_READ_ACCESS) == DesiredAccess) - { - status = KphOpenThread( - ThreadHandle, - DesiredAccess, - &clientId - ); - } - else - { - InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - status = NtOpenThread( - ThreadHandle, - DesiredAccess, - &objectAttributes, - &clientId - ); - - if (status == STATUS_ACCESS_DENIED && KphIsVerified()) - { - status = KphOpenThread( - ThreadHandle, - DesiredAccess, - &clientId - ); - } - } - - return status; -} - -/** Limited API for untrusted/external code. */ -NTSTATUS PhOpenThreadPublic( - _Out_ PHANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_ HANDLE ThreadId - ) -{ - OBJECT_ATTRIBUTES objectAttributes; - CLIENT_ID clientId; - - clientId.UniqueProcess = NULL; - clientId.UniqueThread = ThreadId; - - InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); - - return NtOpenThread( - ThreadHandle, - DesiredAccess, - &objectAttributes, - &clientId - ); -} - -NTSTATUS PhOpenThreadProcess( - _In_ HANDLE ThreadHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE ProcessHandle - ) -{ - if (KphIsConnected()) - { - return KphOpenThreadProcess( - ThreadHandle, - DesiredAccess, - ProcessHandle - ); - } - else - { - NTSTATUS status; - THREAD_BASIC_INFORMATION basicInfo; - - if (!NT_SUCCESS(status = PhGetThreadBasicInformation( - ThreadHandle, - &basicInfo - ))) - return status; - - return PhOpenProcess( - ProcessHandle, - DesiredAccess, - basicInfo.ClientId.UniqueProcess - ); - } -} - -/** - * Opens a process token. - * - * \param ProcessHandle A handle to a process. - * \param DesiredAccess The desired access to the token. - * \param TokenHandle A variable which receives a handle to the token. - */ -NTSTATUS PhOpenProcessToken( - _In_ HANDLE ProcessHandle, - _In_ ACCESS_MASK DesiredAccess, - _Out_ PHANDLE TokenHandle - ) -{ - NTSTATUS status; - - if (KphIsVerified() && (DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) - { - status = KphOpenProcessToken( - ProcessHandle, - DesiredAccess, - TokenHandle - ); - } - else - { - status = NtOpenProcessToken( - ProcessHandle, - DesiredAccess, - TokenHandle - ); - - if (status == STATUS_ACCESS_DENIED && KphIsVerified()) - { - status = KphOpenProcessToken( - ProcessHandle, - DesiredAccess, - TokenHandle - ); - } - } - - return status; -} - -NTSTATUS PhGetObjectSecurity( - _In_ HANDLE Handle, - _In_ SECURITY_INFORMATION SecurityInformation, - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor - ) -{ - NTSTATUS status; - ULONG bufferSize; - PVOID buffer; - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - // This is required (especially for File objects) because some drivers don't seem to handle - // QuerySecurity properly. - memset(buffer, 0, bufferSize); - - status = NtQuerySecurityObject( - Handle, - SecurityInformation, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_BUFFER_TOO_SMALL) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - memset(buffer, 0, bufferSize); - - status = NtQuerySecurityObject( - Handle, - SecurityInformation, - buffer, - bufferSize, - &bufferSize - ); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *SecurityDescriptor = (PSECURITY_DESCRIPTOR)buffer; - - return status; -} - -NTSTATUS PhSetObjectSecurity( - _In_ HANDLE Handle, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ) -{ - return NtSetSecurityObject( - Handle, - SecurityInformation, - SecurityDescriptor - ); -} - -/** - * Terminates a process. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_TERMINATE access. - * \param ExitStatus A status value that indicates why the process is being terminated. - */ -NTSTATUS PhTerminateProcess( - _In_ HANDLE ProcessHandle, - _In_ NTSTATUS ExitStatus - ) -{ - NTSTATUS status; - - if (KphIsVerified()) - { - status = KphTerminateProcess( - ProcessHandle, - ExitStatus - ); - - if (status != STATUS_NOT_SUPPORTED) - return status; - } - - return NtTerminateProcess( - ProcessHandle, - ExitStatus - ); -} - -/** Limited API for untrusted/external code. */ -NTSTATUS PhTerminateProcessPublic( - _In_ HANDLE ProcessHandle, - _In_ NTSTATUS ExitStatus - ) -{ - return NtTerminateProcess( - ProcessHandle, - ExitStatus - ); -} - -/** - * Queries variable-sized information for a process. The function allocates a buffer to contain the - * information. - * - * \param ProcessHandle A handle to a process. The access required depends on the information class - * specified. - * \param ProcessInformationClass The information class to retrieve. - * \param Buffer A variable which receives a pointer to a buffer containing the information. You - * must free the buffer using PhFree() when you no longer need it. - */ -NTSTATUS PhpQueryProcessVariableSize( - _In_ HANDLE ProcessHandle, - _In_ PROCESSINFOCLASS ProcessInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG returnLength = 0; - - status = NtQueryInformationProcess( - ProcessHandle, - ProcessInformationClass, - NULL, - 0, - &returnLength - ); - - if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL && status != STATUS_INFO_LENGTH_MISMATCH) - return status; - - buffer = PhAllocate(returnLength); - status = NtQueryInformationProcess( - ProcessHandle, - ProcessInformationClass, - buffer, - returnLength, - &returnLength - ); - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -/** - * Gets the file name of the process' image. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION access. - * \param FileName A variable which receives a pointer to a string containing the file name. You - * must free the string using PhDereferenceObject() when you no longer need it. - */ -NTSTATUS PhGetProcessImageFileName( - _In_ HANDLE ProcessHandle, - _Out_ PPH_STRING *FileName - ) -{ - NTSTATUS status; - PUNICODE_STRING fileName; - - status = PhpQueryProcessVariableSize( - ProcessHandle, - ProcessImageFileName, - &fileName - ); - - if (!NT_SUCCESS(status)) - return status; - - *FileName = PhCreateStringFromUnicodeString(fileName); - PhFree(fileName); - - return status; -} - -/** - * Gets the Win32 file name of the process' image. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION access. - * \param FileName A variable which receives a pointer to a string containing the file name. You - * must free the string using PhDereferenceObject() when you no longer need it. - * - * \remarks This function is only available on Windows Vista and above. - */ -NTSTATUS PhGetProcessImageFileNameWin32( - _In_ HANDLE ProcessHandle, - _Out_ PPH_STRING *FileName - ) -{ - NTSTATUS status; - PUNICODE_STRING fileName; - - status = PhpQueryProcessVariableSize( - ProcessHandle, - ProcessImageFileNameWin32, - &fileName - ); - - if (!NT_SUCCESS(status)) - return status; - - *FileName = PhCreateStringFromUnicodeString(fileName); - PhFree(fileName); - - return status; -} - -/** - * Gets a string stored in a process' parameters structure. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. - * \param Offset The string to retrieve. - * \param String A variable which receives a pointer to the requested string. You must free the - * string using PhDereferenceObject() when you no longer need it. - * - * \retval STATUS_INVALID_PARAMETER_2 An invalid value was specified in the Offset parameter. - */ -NTSTATUS PhGetProcessPebString( - _In_ HANDLE ProcessHandle, - _In_ PH_PEB_OFFSET Offset, - _Out_ PPH_STRING *String - ) -{ - NTSTATUS status; - PPH_STRING string; - ULONG offset; - -#define PEB_OFFSET_CASE(Enum, Field) \ - case Enum: offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Field); break; \ - case Enum | PhpoWow64: offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS32, Field); break - - switch (Offset) - { - PEB_OFFSET_CASE(PhpoCurrentDirectory, CurrentDirectory); - PEB_OFFSET_CASE(PhpoDllPath, DllPath); - PEB_OFFSET_CASE(PhpoImagePathName, ImagePathName); - PEB_OFFSET_CASE(PhpoCommandLine, CommandLine); - PEB_OFFSET_CASE(PhpoWindowTitle, WindowTitle); - PEB_OFFSET_CASE(PhpoDesktopInfo, DesktopInfo); - PEB_OFFSET_CASE(PhpoShellInfo, ShellInfo); - PEB_OFFSET_CASE(PhpoRuntimeData, RuntimeData); - default: - return STATUS_INVALID_PARAMETER_2; - } - - if (!(Offset & PhpoWow64)) - { - PROCESS_BASIC_INFORMATION basicInfo; - PVOID processParameters; - UNICODE_STRING unicodeString; - - // Get the PEB address. - if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo))) - return status; - - // Read the address of the process parameters. - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)), - &processParameters, - sizeof(PVOID), - NULL - ))) - return status; - - // Read the string structure. - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(processParameters, offset), - &unicodeString, - sizeof(UNICODE_STRING), - NULL - ))) - return status; - - string = PhCreateStringEx(NULL, unicodeString.Length); - - // Read the string contents. - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - unicodeString.Buffer, - string->Buffer, - string->Length, - NULL - ))) - { - PhDereferenceObject(string); - return status; - } - } - else - { - PVOID peb32; - ULONG processParameters32; - UNICODE_STRING32 unicodeString32; - - if (!NT_SUCCESS(status = PhGetProcessPeb32(ProcessHandle, &peb32))) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessParameters)), - &processParameters32, - sizeof(ULONG), - NULL - ))) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(processParameters32, offset), - &unicodeString32, - sizeof(UNICODE_STRING32), - NULL - ))) - return status; - - string = PhCreateStringEx(NULL, unicodeString32.Length); - - // Read the string contents. - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - UlongToPtr(unicodeString32.Buffer), - string->Buffer, - string->Length, - NULL - ))) - { - PhDereferenceObject(string); - return status; - } - } - - *String = string; - - return status; -} - -/** - * Gets a process' command line. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION. Before Windows 8.1, the handle must also have PROCESS_VM_READ - * access. - * \param String A variable which receives a pointer to a string containing the command line. You - * must free the string using PhDereferenceObject() when you no longer need it. - */ -NTSTATUS PhGetProcessCommandLine( - _In_ HANDLE ProcessHandle, - _Out_ PPH_STRING *CommandLine - ) -{ - NTSTATUS status; - - if (WindowsVersion >= WINDOWS_8_1) - { - PUNICODE_STRING commandLine; - - status = PhpQueryProcessVariableSize( - ProcessHandle, - ProcessCommandLineInformation, - &commandLine - ); - - if (NT_SUCCESS(status)) - { - *CommandLine = PhCreateStringFromUnicodeString(commandLine); - PhFree(commandLine); - - return status; - } - } - - return PhGetProcessPebString(ProcessHandle, PhpoCommandLine, CommandLine); -} - -/** - * Gets the window flags and window title of a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION. Before Windows 7 SP1, the handle must also have - * PROCESS_VM_READ access. - * \param WindowFlags A variable which receives the window flags. - * \param WindowTitle A variable which receives a pointer to the window title. You must free the - * string using PhDereferenceObject() when you no longer need it. - */ -NTSTATUS PhGetProcessWindowTitle( - _In_ HANDLE ProcessHandle, - _Out_ PULONG WindowFlags, - _Out_ PPH_STRING *WindowTitle - ) -{ - NTSTATUS status; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; -#endif - ULONG windowFlags; - - if (WindowsVersion >= WINDOWS_7) - { - PPROCESS_WINDOW_INFORMATION windowInfo; - - status = PhpQueryProcessVariableSize( - ProcessHandle, - ProcessWindowInformation, - &windowInfo - ); - - if (NT_SUCCESS(status)) - { - *WindowFlags = windowInfo->WindowFlags; - *WindowTitle = PhCreateStringEx(windowInfo->WindowTitle, windowInfo->WindowTitleLength); - PhFree(windowInfo); - - return status; - } - } - -#ifdef _WIN64 - PhGetProcessIsWow64(ProcessHandle, &isWow64); - - if (!isWow64) -#endif - { - PROCESS_BASIC_INFORMATION basicInfo; - PVOID processParameters; - - // Get the PEB address. - if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo))) - return status; - - // Read the address of the process parameters. - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)), - &processParameters, - sizeof(PVOID), - NULL - ))) - return status; - - // Read the window flags. - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(processParameters, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, WindowFlags)), - &windowFlags, - sizeof(ULONG), - NULL - ))) - return status; - } -#ifdef _WIN64 - else - { - PVOID peb32; - ULONG processParameters32; - - if (!NT_SUCCESS(status = PhGetProcessPeb32(ProcessHandle, &peb32))) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessParameters)), - &processParameters32, - sizeof(ULONG), - NULL - ))) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(processParameters32, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS32, WindowFlags)), - &windowFlags, - sizeof(ULONG), - NULL - ))) - return status; - } -#endif - -#ifdef _WIN64 - status = PhGetProcessPebString(ProcessHandle, PhpoWindowTitle | (isWow64 ? PhpoWow64 : 0), WindowTitle); -#else - status = PhGetProcessPebString(ProcessHandle, PhpoWindowTitle, WindowTitle); -#endif - - if (NT_SUCCESS(status)) - *WindowFlags = windowFlags; - - return status; -} - -NTSTATUS PhGetProcessDepStatus( - _In_ HANDLE ProcessHandle, - _Out_ PULONG DepStatus - ) -{ - NTSTATUS status; - ULONG executeFlags; - ULONG depStatus; - - if (!NT_SUCCESS(status = PhGetProcessExecuteFlags( - ProcessHandle, - &executeFlags - ))) - return status; - - // Check if execution of data pages is enabled. - if (executeFlags & MEM_EXECUTE_OPTION_ENABLE) - depStatus = 0; - else - depStatus = PH_PROCESS_DEP_ENABLED; - - if (executeFlags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) - depStatus |= PH_PROCESS_DEP_ATL_THUNK_EMULATION_DISABLED; - if (executeFlags & MEM_EXECUTE_OPTION_PERMANENT) - depStatus |= PH_PROCESS_DEP_PERMANENT; - - *DepStatus = depStatus; - - return status; -} - -/** - * Gets a process' environment block. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION and - * PROCESS_VM_READ access. - * \param Flags A combination of flags. - * \li \c PH_GET_PROCESS_ENVIRONMENT_WOW64 Retrieve the environment block from the WOW64 PEB. - * \param Environment A variable which will receive a pointer to the environment block copied from - * the process. You must free the block using PhFreePage() when you no longer need it. - * \param EnvironmentLength A variable which will receive the length of the environment block, in - * bytes. - */ -NTSTATUS PhGetProcessEnvironment( - _In_ HANDLE ProcessHandle, - _In_ ULONG Flags, - _Out_ PVOID *Environment, - _Out_ PULONG EnvironmentLength - ) -{ - NTSTATUS status; - PVOID environmentRemote; - MEMORY_BASIC_INFORMATION mbi; - PVOID environment; - ULONG environmentLength; - - if (!(Flags & PH_GET_PROCESS_ENVIRONMENT_WOW64)) - { - PROCESS_BASIC_INFORMATION basicInfo; - PVOID processParameters; - - status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo); - - if (!NT_SUCCESS(status)) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)), - &processParameters, - sizeof(PVOID), - NULL - ))) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(processParameters, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment)), - &environmentRemote, - sizeof(PVOID), - NULL - ))) - return status; - } - else - { - PVOID peb32; - ULONG processParameters32; - ULONG environmentRemote32; - - status = PhGetProcessPeb32(ProcessHandle, &peb32); - - if (!NT_SUCCESS(status)) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessParameters)), - &processParameters32, - sizeof(ULONG), - NULL - ))) - return status; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(processParameters32, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS32, Environment)), - &environmentRemote32, - sizeof(ULONG), - NULL - ))) - return status; - - environmentRemote = UlongToPtr(environmentRemote32); - } - - if (!NT_SUCCESS(status = NtQueryVirtualMemory( - ProcessHandle, - environmentRemote, - MemoryBasicInformation, - &mbi, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - return status; - - environmentLength = (ULONG)(mbi.RegionSize - - ((ULONG_PTR)environmentRemote - (ULONG_PTR)mbi.BaseAddress)); - - // Read in the entire region of memory. - - environment = PhAllocatePage(environmentLength, NULL); - - if (!environment) - return STATUS_NO_MEMORY; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - environmentRemote, - environment, - environmentLength, - NULL - ))) - { - PhFreePage(environment); - return status; - } - - *Environment = environment; - - if (EnvironmentLength) - *EnvironmentLength = environmentLength; - - return status; -} - -BOOLEAN PhEnumProcessEnvironmentVariables( - _In_ PVOID Environment, - _In_ ULONG EnvironmentLength, - _Inout_ PULONG EnumerationKey, - _Out_ PPH_ENVIRONMENT_VARIABLE Variable - ) -{ - ULONG length; - ULONG startIndex; - PWCHAR name; - ULONG nameLength; - PWCHAR value; - ULONG valueLength; - PWCHAR currentChar; - ULONG currentIndex; - - length = EnvironmentLength / sizeof(WCHAR); - - currentIndex = *EnumerationKey; - currentChar = (PWCHAR)Environment + currentIndex; - startIndex = currentIndex; - name = currentChar; - - // Find the end of the name. - while (TRUE) - { - if (currentIndex >= length) - return FALSE; - if (*currentChar == '=') - break; - if (*currentChar == 0) - return FALSE; // no more variables - - currentIndex++; - currentChar++; - } - - nameLength = currentIndex - startIndex; - - currentIndex++; - currentChar++; - startIndex = currentIndex; - value = currentChar; - - // Find the end of the value. - while (TRUE) - { - if (currentIndex >= length) - return FALSE; - if (*currentChar == 0) - break; - - currentIndex++; - currentChar++; - } - - valueLength = currentIndex - startIndex; - - currentIndex++; - *EnumerationKey = currentIndex; - - Variable->Name.Buffer = name; - Variable->Name.Length = nameLength * sizeof(WCHAR); - Variable->Value.Buffer = value; - Variable->Value.Length = valueLength * sizeof(WCHAR); - - return TRUE; -} - -/** - * Gets the file name of a mapped section. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION - * access. - * \param BaseAddress The base address of the section view. - * \param FileName A variable which receives a pointer to a string containing the file name of the - * section. You must free the string using PhDereferenceObject() when you no longer need it. - */ -NTSTATUS PhGetProcessMappedFileName( - _In_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, - _Out_ PPH_STRING *FileName - ) -{ - NTSTATUS status; - PVOID buffer; - SIZE_T bufferSize; - SIZE_T returnLength; - PUNICODE_STRING unicodeString; - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - - status = NtQueryVirtualMemory( - ProcessHandle, - BaseAddress, - MemoryMappedFilenameInformation, - buffer, - bufferSize, - &returnLength - ); - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - bufferSize = returnLength; - buffer = PhAllocate(bufferSize); - - status = NtQueryVirtualMemory( - ProcessHandle, - BaseAddress, - MemoryMappedFilenameInformation, - buffer, - bufferSize, - &returnLength - ); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - unicodeString = (PUNICODE_STRING)buffer; - *FileName = PhCreateStringEx( - unicodeString->Buffer, - unicodeString->Length - ); - PhFree(buffer); - - return status; -} - -/** - * Gets working set information for a process. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION - * access. - * \param WorkingSetInformation A variable which receives a pointer to the information. You must - * free the buffer using PhFree() when you no longer need it. - */ -NTSTATUS PhGetProcessWorkingSetInformation( - _In_ HANDLE ProcessHandle, - _Out_ PMEMORY_WORKING_SET_INFORMATION *WorkingSetInformation - ) -{ - NTSTATUS status; - PVOID buffer; - SIZE_T bufferSize; - - bufferSize = 0x8000; - buffer = PhAllocate(bufferSize); - - while ((status = NtQueryVirtualMemory( - ProcessHandle, - NULL, - MemoryWorkingSetInformation, - buffer, - bufferSize, - NULL - )) == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - - // Fail if we're resizing the buffer to something very large. - if (bufferSize > PH_LARGE_BUFFER_SIZE) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *WorkingSetInformation = (PMEMORY_WORKING_SET_INFORMATION)buffer; - - return status; -} - -/** - * Gets working set counters for a process. - * - * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION - * access. - * \param WsCounters A variable which receives the counters. - */ -NTSTATUS PhGetProcessWsCounters( - _In_ HANDLE ProcessHandle, - _Out_ PPH_PROCESS_WS_COUNTERS WsCounters - ) -{ - NTSTATUS status; - PMEMORY_WORKING_SET_INFORMATION wsInfo; - PH_PROCESS_WS_COUNTERS wsCounters; - ULONG_PTR i; - - if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation( - ProcessHandle, - &wsInfo - ))) - return status; - - memset(&wsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS)); - - for (i = 0; i < wsInfo->NumberOfEntries; i++) - { - wsCounters.NumberOfPages++; - - if (wsInfo->WorkingSetInfo[i].ShareCount > 1) - wsCounters.NumberOfSharedPages++; - if (wsInfo->WorkingSetInfo[i].ShareCount == 0) - wsCounters.NumberOfPrivatePages++; - if (wsInfo->WorkingSetInfo[i].Shared) - wsCounters.NumberOfShareablePages++; - } - - PhFree(wsInfo); - - *WsCounters = wsCounters; - - return status; -} - -/** - * Causes a process to load a DLL. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ - * and PROCESS_VM_WRITE access. - * \param FileName The file name of the DLL to inject. - * \param Timeout The timeout, in milliseconds, for the process to load the DLL. - * - * \remarks If the process does not load the DLL before the timeout expires it may crash. Choose the - * timeout value carefully. - */ -NTSTATUS PhInjectDllProcess( - _In_ HANDLE ProcessHandle, - _In_ PWSTR FileName, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ -#ifdef _WIN64 - static PVOID loadLibraryW32 = NULL; -#endif - - NTSTATUS status; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; - BOOLEAN isModule32 = FALSE; - PH_MAPPED_IMAGE mappedImage; -#endif - PVOID threadStart; - PH_STRINGREF fileName; - PVOID baseAddress = NULL; - SIZE_T allocSize; - HANDLE threadHandle; - -#ifdef _WIN64 - PhGetProcessIsWow64(ProcessHandle, &isWow64); - - if (isWow64) - { - if (!NT_SUCCESS(status = PhLoadMappedImage(FileName, NULL, TRUE, &mappedImage))) - return status; - - isModule32 = mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC; - PhUnloadMappedImage(&mappedImage); - } - - if (!isModule32) - { -#endif - threadStart = PhGetModuleProcAddress(L"kernel32.dll", "LoadLibraryW"); -#ifdef _WIN64 - } - else - { - threadStart = loadLibraryW32; - - if (!threadStart) - { - PPH_STRING kernel32FileName; - - kernel32FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\kernel32.dll"); - status = PhGetProcedureAddressRemote( - ProcessHandle, - kernel32FileName->Buffer, - "LoadLibraryW", - 0, - &loadLibraryW32, - NULL - ); - PhDereferenceObject(kernel32FileName); - - if (!NT_SUCCESS(status)) - return status; - - threadStart = loadLibraryW32; - } - } -#endif - - PhInitializeStringRefLongHint(&fileName, FileName); - allocSize = fileName.Length + sizeof(WCHAR); - - if (!NT_SUCCESS(status = NtAllocateVirtualMemory( - ProcessHandle, - &baseAddress, - 0, - &allocSize, - MEM_COMMIT, - PAGE_READWRITE - ))) - return status; - - if (!NT_SUCCESS(status = NtWriteVirtualMemory( - ProcessHandle, - baseAddress, - fileName.Buffer, - fileName.Length + sizeof(WCHAR), - NULL - ))) - goto FreeExit; - - // Vista seems to support native threads better than XP. - if (WindowsVersion >= WINDOWS_VISTA) - { - if (!NT_SUCCESS(status = RtlCreateUserThread( - ProcessHandle, - NULL, - FALSE, - 0, - 0, - 0, - (PUSER_THREAD_START_ROUTINE)threadStart, - baseAddress, - &threadHandle, - NULL - ))) - goto FreeExit; - } - else - { - if (!(threadHandle = CreateRemoteThread( - ProcessHandle, - NULL, - 0, - (PTHREAD_START_ROUTINE)threadStart, - baseAddress, - 0, - NULL - ))) - { - status = PhGetLastWin32ErrorAsNtStatus(); - goto FreeExit; - } - } - - // Wait for the thread to finish. - status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); - NtClose(threadHandle); - -FreeExit: - // Size needs to be zero if we're freeing. - allocSize = 0; - NtFreeVirtualMemory( - ProcessHandle, - &baseAddress, - &allocSize, - MEM_RELEASE - ); - - return status; -} - -/** - * Causes a process to unload a DLL. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ - * and PROCESS_VM_WRITE access. - * \param BaseAddress The base address of the DLL to unload. - * \param Timeout The timeout, in milliseconds, for the process to unload the DLL. - */ -NTSTATUS PhUnloadDllProcess( - _In_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ -#ifdef _WIN64 - static PVOID ldrUnloadDll32 = NULL; -#endif - - NTSTATUS status; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; - BOOLEAN isModule32 = FALSE; -#endif - HANDLE threadHandle; - THREAD_BASIC_INFORMATION basicInfo; - PVOID threadStart; - -#ifdef _WIN64 - PhGetProcessIsWow64(ProcessHandle, &isWow64); -#endif - - // No point trying to set the load count on Windows 8 and higher, because NT now uses a DAG of - // loader nodes. - if (WindowsVersion < WINDOWS_8) - { - status = PhSetProcessModuleLoadCount( - ProcessHandle, - BaseAddress, - 1 - ); - -#ifdef _WIN64 - if (isWow64 && status == STATUS_DLL_NOT_FOUND) - { - // The DLL might be 32-bit. - status = PhSetProcessModuleLoadCount32( - ProcessHandle, - BaseAddress, - 1 - ); - - if (NT_SUCCESS(status)) - isModule32 = TRUE; - } -#endif - - if (!NT_SUCCESS(status)) - return status; - } - -#ifdef _WIN64 - if (!isModule32) - { -#endif - threadStart = PhGetModuleProcAddress(L"ntdll.dll", "LdrUnloadDll"); -#ifdef _WIN64 - } - else - { - threadStart = ldrUnloadDll32; - - if (!threadStart) - { - PPH_STRING ntdll32FileName; - - ntdll32FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\ntdll.dll"); - status = PhGetProcedureAddressRemote( - ProcessHandle, - ntdll32FileName->Buffer, - "LdrUnloadDll", - 0, - &ldrUnloadDll32, - NULL - ); - PhDereferenceObject(ntdll32FileName); - - if (!NT_SUCCESS(status)) - return status; - - threadStart = ldrUnloadDll32; - } - } -#endif - - if (WindowsVersion >= WINDOWS_VISTA) - { - status = RtlCreateUserThread( - ProcessHandle, - NULL, - FALSE, - 0, - 0, - 0, - (PUSER_THREAD_START_ROUTINE)threadStart, - BaseAddress, - &threadHandle, - NULL - ); - } - else - { - if (!(threadHandle = CreateRemoteThread( - ProcessHandle, - NULL, - 0, - (PTHREAD_START_ROUTINE)threadStart, - BaseAddress, - 0, - NULL - ))) - { - status = PhGetLastWin32ErrorAsNtStatus(); - } - } - - if (!NT_SUCCESS(status)) - return status; - - status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); - - if (status == STATUS_WAIT_0) - { - status = PhGetThreadBasicInformation(threadHandle, &basicInfo); - - if (NT_SUCCESS(status)) - status = basicInfo.ExitStatus; - } - - NtClose(threadHandle); - - return status; -} - -// Contributed by dmex -/** - * Sets an environment variable in a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ - * and PROCESS_VM_WRITE access. - * \param Name The name of the environment variable to set. - * \param Value The new value of the environment variable. If this parameter is NULL, the - * environment variable is deleted. - * \param Timeout The timeout, in milliseconds, for the process to set the environment variable. - */ -NTSTATUS PhSetEnvironmentVariableRemote( - _In_ HANDLE ProcessHandle, - _In_ PPH_STRINGREF Name, - _In_opt_ PPH_STRINGREF Value, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - NTSTATUS status; -#ifdef _WIN64 - BOOLEAN isWow64; -#endif - PPH_STRING ntdllFileName = NULL; - PPH_STRING kernel32FileName = NULL; - PVOID nameBaseAddress = NULL; - PVOID valueBaseAddress = NULL; - SIZE_T nameAllocationSize = 0; - SIZE_T valueAllocationSize = 0; - PVOID rtlExitUserThread = NULL; - PVOID setEnvironmentVariableW = NULL; - HANDLE threadHandle = NULL; - - nameAllocationSize = Name->Length + sizeof(WCHAR); - - if (Value) - valueAllocationSize = Value->Length + sizeof(WCHAR); - -#ifdef _WIN64 - if (!NT_SUCCESS(status = PhGetProcessIsWow64(ProcessHandle, &isWow64))) - goto CleanupExit; - - if (isWow64) - { - ntdllFileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\ntdll.dll"); - kernel32FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\SysWow64\\kernel32.dll"); - } - else - { -#endif - ntdllFileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\System32\\ntdll.dll"); - kernel32FileName = PhConcatStrings2(USER_SHARED_DATA->NtSystemRoot, L"\\System32\\kernel32.dll"); -#ifdef _WIN64 - } -#endif - - if (!NT_SUCCESS(status = PhGetProcedureAddressRemote( - ProcessHandle, - ntdllFileName->Buffer, - "RtlExitUserThread", - 0, - &rtlExitUserThread, - NULL - ))) - { - goto CleanupExit; - } - if (!NT_SUCCESS(status = PhGetProcedureAddressRemote( - ProcessHandle, - kernel32FileName->Buffer, - "SetEnvironmentVariableW", - 0, - &setEnvironmentVariableW, - NULL - ))) - { - goto CleanupExit; - } - if (!NT_SUCCESS(status = NtAllocateVirtualMemory( - ProcessHandle, - &nameBaseAddress, - 0, - &nameAllocationSize, - MEM_COMMIT, - PAGE_READWRITE - ))) - { - goto CleanupExit; - } - if (!NT_SUCCESS(status = NtWriteVirtualMemory( - ProcessHandle, - nameBaseAddress, - Name->Buffer, - Name->Length, - NULL - ))) - { - goto CleanupExit; - } - - if (Value) - { - if (!NT_SUCCESS(status = NtAllocateVirtualMemory( - ProcessHandle, - &valueBaseAddress, - 0, - &valueAllocationSize, - MEM_COMMIT, - PAGE_READWRITE - ))) - { - goto CleanupExit; - } - if (!NT_SUCCESS(status = NtWriteVirtualMemory( - ProcessHandle, - valueBaseAddress, - Value->Buffer, - Value->Length, - NULL - ))) - { - goto CleanupExit; - } - } - - if (WindowsVersion >= WINDOWS_VISTA) - { - if (!NT_SUCCESS(status = RtlCreateUserThread( - ProcessHandle, - NULL, - TRUE, - 0, - 0, - 0, - (PUSER_THREAD_START_ROUTINE)rtlExitUserThread, - NULL, - &threadHandle, - NULL - ))) - { - goto CleanupExit; - } - } - else - { - if (!(threadHandle = CreateRemoteThread( - ProcessHandle, - NULL, - 0, - (PTHREAD_START_ROUTINE)rtlExitUserThread, - NULL, - CREATE_SUSPENDED, - NULL - ))) - { - status = PhGetLastWin32ErrorAsNtStatus(); - goto CleanupExit; - } - } - -#ifdef _WIN64 - if (isWow64) - { - // NtQueueApcThread doesn't work for WOW64 processes - we need to use RtlQueueApcWow64Thread - // instead. - if (!NT_SUCCESS(status = RtlQueueApcWow64Thread( - threadHandle, - setEnvironmentVariableW, - nameBaseAddress, - valueBaseAddress, - NULL - ))) - { - goto CleanupExit; - } - } - else - { -#endif - if (!NT_SUCCESS(status = NtQueueApcThread( - threadHandle, - setEnvironmentVariableW, - nameBaseAddress, - valueBaseAddress, - NULL - ))) - { - goto CleanupExit; - } -#ifdef _WIN64 - } -#endif - - // This causes our APC to be executed. - NtResumeThread(threadHandle, NULL); - status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); - -CleanupExit: - if (threadHandle) - NtClose(threadHandle); - if (nameBaseAddress) - { - nameAllocationSize = 0; - NtFreeVirtualMemory( - ProcessHandle, - &nameBaseAddress, - &nameAllocationSize, - MEM_RELEASE - ); - } - if (valueBaseAddress) - { - valueAllocationSize = 0; - NtFreeVirtualMemory( - ProcessHandle, - &valueBaseAddress, - &valueAllocationSize, - MEM_RELEASE - ); - } - PhClearReference(&ntdllFileName); - PhClearReference(&kernel32FileName); - - return status; -} - -NTSTATUS PhGetJobProcessIdList( - _In_ HANDLE JobHandle, - _Out_ PJOBOBJECT_BASIC_PROCESS_ID_LIST *ProcessIdList - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize; - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - - status = NtQueryInformationJobObject( - JobHandle, - JobObjectBasicProcessIdList, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - - status = NtQueryInformationJobObject( - JobHandle, - JobObjectBasicProcessIdList, - buffer, - bufferSize, - NULL - ); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *ProcessIdList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)buffer; - - return status; -} - -/** - * Queries variable-sized information for a token. The function allocates a buffer to contain the - * information. - * - * \param TokenHandle A handle to a token. The access required depends on the information class - * specified. - * \param TokenInformationClass The information class to retrieve. - * \param Buffer A variable which receives a pointer to a buffer containing the information. You - * must free the buffer using PhFree() when you no longer need it. - */ -NTSTATUS PhpQueryTokenVariableSize( - _In_ HANDLE TokenHandle, - _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG returnLength = 0; - - NtQueryInformationToken( - TokenHandle, - TokenInformationClass, - NULL, - 0, - &returnLength - ); - buffer = PhAllocate(returnLength); - status = NtQueryInformationToken( - TokenHandle, - TokenInformationClass, - buffer, - returnLength, - &returnLength - ); - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -/** - * Queries variable-sized information for a token. The function allocates a buffer to contain the - * information. - * - * \param TokenHandle A handle to a token. The access required depends on the information class - * specified. - * \param TokenInformationClass The information class to retrieve. - * \param Buffer A variable which receives a pointer to a buffer containing the information. You - * must free the buffer using PhFree() when you no longer need it. - */ -NTSTATUS PhQueryTokenVariableSize( - _In_ HANDLE TokenHandle, - _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, - _Out_ PVOID *Buffer - ) -{ - return PhpQueryTokenVariableSize( - TokenHandle, - TokenInformationClass, - Buffer - ); -} - -/** - * Gets a token's user. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. - * \param User A variable which receives a pointer to a structure containing the token's user. You - * must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhGetTokenUser( - _In_ HANDLE TokenHandle, - _Out_ PTOKEN_USER *User - ) -{ - return PhpQueryTokenVariableSize( - TokenHandle, - TokenUser, - User - ); -} - -/** - * Gets a token's owner. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. - * \param Owner A variable which receives a pointer to a structure containing the token's owner. You - * must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhGetTokenOwner( - _In_ HANDLE TokenHandle, - _Out_ PTOKEN_OWNER *Owner - ) -{ - return PhpQueryTokenVariableSize( - TokenHandle, - TokenOwner, - Owner - ); -} - -/** - * Gets a token's primary group. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. - * \param PrimaryGroup A variable which receives a pointer to a structure containing the token's - * primary group. You must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhGetTokenPrimaryGroup( - _In_ HANDLE TokenHandle, - _Out_ PTOKEN_PRIMARY_GROUP *PrimaryGroup - ) -{ - return PhpQueryTokenVariableSize( - TokenHandle, - TokenPrimaryGroup, - PrimaryGroup - ); -} - -/** - * Gets a token's groups. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. - * \param Groups A variable which receives a pointer to a structure containing the token's groups. - * You must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhGetTokenGroups( - _In_ HANDLE TokenHandle, - _Out_ PTOKEN_GROUPS *Groups - ) -{ - return PhpQueryTokenVariableSize( - TokenHandle, - TokenGroups, - Groups - ); -} - -/** - * Gets a token's privileges. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. - * \param Privileges A variable which receives a pointer to a structure containing the token's - * privileges. You must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhGetTokenPrivileges( - _In_ HANDLE TokenHandle, - _Out_ PTOKEN_PRIVILEGES *Privileges - ) -{ - return PhpQueryTokenVariableSize( - TokenHandle, - TokenPrivileges, - Privileges - ); -} - -NTSTATUS PhSetTokenSessionId( - _In_ HANDLE TokenHandle, - _In_ ULONG SessionId - ) -{ - return NtSetInformationToken( - TokenHandle, - TokenSessionId, - &SessionId, - sizeof(ULONG) - ); -} - -/** - * Modifies a token privilege. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_ADJUST_PRIVILEGES access. - * \param PrivilegeName The name of the privilege to modify. If this parameter is NULL, you must - * specify a LUID in the \a PrivilegeLuid parameter. - * \param PrivilegeLuid The LUID of the privilege to modify. If this parameter is NULL, you must - * specify a name in the \a PrivilegeName parameter. - * \param Attributes The new attributes of the privilege. - */ -BOOLEAN PhSetTokenPrivilege( - _In_ HANDLE TokenHandle, - _In_opt_ PWSTR PrivilegeName, - _In_opt_ PLUID PrivilegeLuid, - _In_ ULONG Attributes - ) -{ - NTSTATUS status; - TOKEN_PRIVILEGES privileges; - - privileges.PrivilegeCount = 1; - privileges.Privileges[0].Attributes = Attributes; - - if (PrivilegeLuid) - { - privileges.Privileges[0].Luid = *PrivilegeLuid; - } - else if (PrivilegeName) - { - PH_STRINGREF privilegeName; - - PhInitializeStringRef(&privilegeName, PrivilegeName); - - if (!PhLookupPrivilegeValue( - &privilegeName, - &privileges.Privileges[0].Luid - )) - return FALSE; - } - else - { - return FALSE; - } - - if (!NT_SUCCESS(status = NtAdjustPrivilegesToken( - TokenHandle, - FALSE, - &privileges, - 0, - NULL, - NULL - ))) - return FALSE; - - if (status == STATUS_NOT_ALL_ASSIGNED) - return FALSE; - - return TRUE; -} - -BOOLEAN PhSetTokenPrivilege2( - _In_ HANDLE TokenHandle, - _In_ LONG Privilege, - _In_ ULONG Attributes - ) -{ - LUID privilegeLuid; - - privilegeLuid = RtlConvertLongToLuid(Privilege); - - return PhSetTokenPrivilege(TokenHandle, NULL, &privilegeLuid, Attributes); -} - -/** - * Sets whether virtualization is enabled for a token. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_WRITE access. - * \param IsVirtualizationEnabled A boolean indicating whether virtualization is to be enabled for - * the token. - */ -NTSTATUS PhSetTokenIsVirtualizationEnabled( - _In_ HANDLE TokenHandle, - _In_ BOOLEAN IsVirtualizationEnabled - ) -{ - ULONG virtualizationEnabled; - - virtualizationEnabled = IsVirtualizationEnabled; - - return NtSetInformationToken( - TokenHandle, - TokenVirtualizationEnabled, - &virtualizationEnabled, - sizeof(ULONG) - ); -} - -/** - * Gets a token's integrity level. - * - * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. - * \param IntegrityLevel A variable which receives the integrity level of the token. - * \param IntegrityString A variable which receives a pointer to a string containing a string - * representation of the integrity level. - */ -NTSTATUS PhGetTokenIntegrityLevel( - _In_ HANDLE TokenHandle, - _Out_opt_ PMANDATORY_LEVEL IntegrityLevel, - _Out_opt_ PWSTR *IntegrityString - ) -{ - NTSTATUS status; - PTOKEN_MANDATORY_LABEL mandatoryLabel; - ULONG subAuthority; - MANDATORY_LEVEL integrityLevel; - PWSTR integrityString; - - status = PhpQueryTokenVariableSize(TokenHandle, TokenIntegrityLevel, &mandatoryLabel); - - if (!NT_SUCCESS(status)) - return status; - - subAuthority = *RtlSubAuthoritySid(mandatoryLabel->Label.Sid, 0); - PhFree(mandatoryLabel); - - switch (subAuthority) - { - case SECURITY_MANDATORY_UNTRUSTED_RID: - integrityLevel = MandatoryLevelUntrusted; - integrityString = L"Untrusted"; - break; - case SECURITY_MANDATORY_LOW_RID: - integrityLevel = MandatoryLevelLow; - integrityString = L"Low"; - break; - case SECURITY_MANDATORY_MEDIUM_RID: - integrityLevel = MandatoryLevelMedium; - integrityString = L"Medium"; - break; - case SECURITY_MANDATORY_HIGH_RID: - integrityLevel = MandatoryLevelHigh; - integrityString = L"High"; - break; - case SECURITY_MANDATORY_SYSTEM_RID: - integrityLevel = MandatoryLevelSystem; - integrityString = L"System"; - break; - case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: - integrityLevel = MandatoryLevelSecureProcess; - integrityString = L"Protected"; - break; - default: - return STATUS_UNSUCCESSFUL; - } - - if (IntegrityLevel) - *IntegrityLevel = integrityLevel; - if (IntegrityString) - *IntegrityString = integrityString; - - return status; -} - -NTSTATUS PhpQueryFileVariableSize( - _In_ HANDLE FileHandle, - _In_ FILE_INFORMATION_CLASS FileInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK isb; - PVOID buffer; - ULONG bufferSize = 0x200; - - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - status = NtQueryInformationFile( - FileHandle, - &isb, - buffer, - bufferSize, - FileInformationClass - ); - - if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -NTSTATUS PhGetFileSize( - _In_ HANDLE FileHandle, - _Out_ PLARGE_INTEGER Size - ) -{ - NTSTATUS status; - FILE_STANDARD_INFORMATION standardInfo; - IO_STATUS_BLOCK isb; - - status = NtQueryInformationFile( - FileHandle, - &isb, - &standardInfo, - sizeof(FILE_STANDARD_INFORMATION), - FileStandardInformation - ); - - if (!NT_SUCCESS(status)) - return status; - - *Size = standardInfo.EndOfFile; - - return status; -} - -NTSTATUS PhSetFileSize( - _In_ HANDLE FileHandle, - _In_ PLARGE_INTEGER Size - ) -{ - FILE_END_OF_FILE_INFORMATION endOfFileInfo; - IO_STATUS_BLOCK isb; - - endOfFileInfo.EndOfFile = *Size; - - return NtSetInformationFile( - FileHandle, - &isb, - &endOfFileInfo, - sizeof(FILE_END_OF_FILE_INFORMATION), - FileEndOfFileInformation - ); -} - -NTSTATUS PhpQueryTransactionManagerVariableSize( - _In_ HANDLE TransactionManagerHandle, - _In_ TRANSACTIONMANAGER_INFORMATION_CLASS TransactionManagerInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 0x100; - - if (!NtQueryInformationTransactionManager_Import()) - return STATUS_NOT_SUPPORTED; - - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - status = NtQueryInformationTransactionManager_Import()( - TransactionManagerHandle, - TransactionManagerInformationClass, - buffer, - bufferSize, - NULL - ); - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - bufferSize *= 2; - - if (bufferSize > 1 * 1024 * 1024) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -NTSTATUS PhGetTransactionManagerBasicInformation( - _In_ HANDLE TransactionManagerHandle, - _Out_ PTRANSACTIONMANAGER_BASIC_INFORMATION BasicInformation - ) -{ - if (NtQueryInformationTransactionManager_Import()) - { - return NtQueryInformationTransactionManager_Import()( - TransactionManagerHandle, - TransactionManagerBasicInformation, - BasicInformation, - sizeof(TRANSACTIONMANAGER_BASIC_INFORMATION), - NULL - ); - } - else - { - return STATUS_NOT_SUPPORTED; - } -} - -NTSTATUS PhGetTransactionManagerLogFileName( - _In_ HANDLE TransactionManagerHandle, - _Out_ PPH_STRING *LogFileName - ) -{ - NTSTATUS status; - PTRANSACTIONMANAGER_LOGPATH_INFORMATION logPathInfo; - - status = PhpQueryTransactionManagerVariableSize( - TransactionManagerHandle, - TransactionManagerLogPathInformation, - &logPathInfo - ); - - if (!NT_SUCCESS(status)) - return status; - - *LogFileName = PhCreateStringEx( - logPathInfo->LogPath, - logPathInfo->LogPathLength - ); - PhFree(logPathInfo); - - return status; -} - -NTSTATUS PhpQueryTransactionVariableSize( - _In_ HANDLE TransactionHandle, - _In_ TRANSACTION_INFORMATION_CLASS TransactionInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 0x100; - - if (!NtQueryInformationTransaction_Import()) - return STATUS_NOT_SUPPORTED; - - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - status = NtQueryInformationTransaction_Import()( - TransactionHandle, - TransactionInformationClass, - buffer, - bufferSize, - NULL - ); - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - bufferSize *= 2; - - if (bufferSize > 1 * 1024 * 1024) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -NTSTATUS PhGetTransactionBasicInformation( - _In_ HANDLE TransactionHandle, - _Out_ PTRANSACTION_BASIC_INFORMATION BasicInformation - ) -{ - if (NtQueryInformationTransaction_Import()) - { - return NtQueryInformationTransaction_Import()( - TransactionHandle, - TransactionBasicInformation, - BasicInformation, - sizeof(TRANSACTION_BASIC_INFORMATION), - NULL - ); - } - else - { - return STATUS_NOT_SUPPORTED; - } -} - -NTSTATUS PhGetTransactionPropertiesInformation( - _In_ HANDLE TransactionHandle, - _Out_opt_ PLARGE_INTEGER Timeout, - _Out_opt_ TRANSACTION_OUTCOME *Outcome, - _Out_opt_ PPH_STRING *Description - ) -{ - NTSTATUS status; - PTRANSACTION_PROPERTIES_INFORMATION propertiesInfo; - - status = PhpQueryTransactionVariableSize( - TransactionHandle, - TransactionPropertiesInformation, - &propertiesInfo - ); - - if (!NT_SUCCESS(status)) - return status; - - if (Timeout) - { - *Timeout = propertiesInfo->Timeout; - } - - if (Outcome) - { - *Outcome = propertiesInfo->Outcome; - } - - if (Description) - { - *Description = PhCreateStringEx( - propertiesInfo->Description, - propertiesInfo->DescriptionLength - ); - } - - PhFree(propertiesInfo); - - return status; -} - -NTSTATUS PhpQueryResourceManagerVariableSize( - _In_ HANDLE ResourceManagerHandle, - _In_ RESOURCEMANAGER_INFORMATION_CLASS ResourceManagerInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 0x100; - - if (!NtQueryInformationResourceManager_Import()) - return STATUS_NOT_SUPPORTED; - - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - status = NtQueryInformationResourceManager_Import()( - ResourceManagerHandle, - ResourceManagerInformationClass, - buffer, - bufferSize, - NULL - ); - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - bufferSize *= 2; - - if (bufferSize > 1 * 1024 * 1024) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -NTSTATUS PhGetResourceManagerBasicInformation( - _In_ HANDLE ResourceManagerHandle, - _Out_opt_ PGUID Guid, - _Out_opt_ PPH_STRING *Description - ) -{ - NTSTATUS status; - PRESOURCEMANAGER_BASIC_INFORMATION basicInfo; - - status = PhpQueryResourceManagerVariableSize( - ResourceManagerHandle, - ResourceManagerBasicInformation, - &basicInfo - ); - - if (!NT_SUCCESS(status)) - return status; - - if (Guid) - { - *Guid = basicInfo->ResourceManagerId; - } - - if (Description) - { - *Description = PhCreateStringEx( - basicInfo->Description, - basicInfo->DescriptionLength - ); - } - - PhFree(basicInfo); - - return status; -} - -NTSTATUS PhGetEnlistmentBasicInformation( - _In_ HANDLE EnlistmentHandle, - _Out_ PENLISTMENT_BASIC_INFORMATION BasicInformation - ) -{ - if (NtQueryInformationEnlistment_Import()) - { - return NtQueryInformationEnlistment_Import()( - EnlistmentHandle, - EnlistmentBasicInformation, - BasicInformation, - sizeof(ENLISTMENT_BASIC_INFORMATION), - NULL - ); - } - else - { - return STATUS_NOT_SUPPORTED; - } -} - -typedef struct _OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT -{ - NTSTATUS Status; - PVOID BaseAddress; - HANDLE DriverHandle; -} OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT, *POPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT; - -BOOLEAN NTAPI PhpOpenDriverByBaseAddressCallback( - _In_ PPH_STRINGREF Name, - _In_ PPH_STRINGREF TypeName, - _In_opt_ PVOID Context - ) -{ - static PH_STRINGREF driverDirectoryName = PH_STRINGREF_INIT(L"\\Driver\\"); - - NTSTATUS status; - POPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT context = Context; - PPH_STRING driverName; - UNICODE_STRING driverNameUs; - OBJECT_ATTRIBUTES objectAttributes; - HANDLE driverHandle; - DRIVER_BASIC_INFORMATION basicInfo; - - driverName = PhConcatStringRef2(&driverDirectoryName, Name); - - if (!PhStringRefToUnicodeString(&driverName->sr, &driverNameUs)) - { - PhDereferenceObject(driverName); - return TRUE; - } - - InitializeObjectAttributes( - &objectAttributes, - &driverNameUs, - 0, - NULL, - NULL - ); - - status = KphOpenDriver(&driverHandle, SYNCHRONIZE, &objectAttributes); - PhDereferenceObject(driverName); - - if (!NT_SUCCESS(status)) - return TRUE; - - status = KphQueryInformationDriver( - driverHandle, - DriverBasicInformation, - &basicInfo, - sizeof(DRIVER_BASIC_INFORMATION), - NULL - ); - - if (NT_SUCCESS(status)) - { - if (basicInfo.DriverStart == context->BaseAddress) - { - context->Status = STATUS_SUCCESS; - context->DriverHandle = driverHandle; - - return FALSE; - } - } - - NtClose(driverHandle); - - return TRUE; -} - -/** - * Opens a driver object using a base address. - * - * \param DriverHandle A variable which receives a handle to the driver object. - * \param BaseAddress The base address of the driver to open. - * - * \retval STATUS_OBJECT_NAME_NOT_FOUND The driver could not be found. - * - * \remarks This function requires a valid KProcessHacker handle. - */ -NTSTATUS PhOpenDriverByBaseAddress( - _Out_ PHANDLE DriverHandle, - _In_ PVOID BaseAddress - ) -{ - NTSTATUS status; - UNICODE_STRING driverDirectoryName; - OBJECT_ATTRIBUTES objectAttributes; - HANDLE driverDirectoryHandle; - OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT context; - - RtlInitUnicodeString( - &driverDirectoryName, - L"\\Driver" - ); - InitializeObjectAttributes( - &objectAttributes, - &driverDirectoryName, - 0, - NULL, - NULL - ); - - if (!NT_SUCCESS(status = NtOpenDirectoryObject( - &driverDirectoryHandle, - DIRECTORY_QUERY, - &objectAttributes - ))) - return status; - - context.Status = STATUS_OBJECT_NAME_NOT_FOUND; - context.BaseAddress = BaseAddress; - - status = PhEnumDirectoryObjects( - driverDirectoryHandle, - PhpOpenDriverByBaseAddressCallback, - &context - ); - NtClose(driverDirectoryHandle); - - if (!NT_SUCCESS(status) && !NT_SUCCESS(context.Status)) - return status; - - if (NT_SUCCESS(context.Status)) - { - *DriverHandle = context.DriverHandle; - } - - return context.Status; -} - -/** - * Queries variable-sized information for a driver. The function allocates a buffer to contain the - * information. - * - * \param DriverHandle A handle to a driver. The access required depends on the information class - * specified. - * \param DriverInformationClass The information class to retrieve. - * \param Buffer A variable which receives a pointer to a buffer containing the information. You - * must free the buffer using PhFree() when you no longer need it. - * - * \remarks This function requires a valid KProcessHacker handle. - */ -NTSTATUS PhpQueryDriverVariableSize( - _In_ HANDLE DriverHandle, - _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG returnLength = 0; - - KphQueryInformationDriver( - DriverHandle, - DriverInformationClass, - NULL, - 0, - &returnLength - ); - buffer = PhAllocate(returnLength); - status = KphQueryInformationDriver( - DriverHandle, - DriverInformationClass, - buffer, - returnLength, - &returnLength - ); - - if (NT_SUCCESS(status)) - { - *Buffer = buffer; - } - else - { - PhFree(buffer); - } - - return status; -} - -/** - * Gets the object name of a driver. - * - * \param DriverHandle A handle to a driver. - * \param Name A variable which receives a pointer to a string containing the object name. You must - * free the string using PhDereferenceObject() when you no longer need it. - * - * \remarks This function requires a valid KProcessHacker handle. - */ -NTSTATUS PhGetDriverName( - _In_ HANDLE DriverHandle, - _Out_ PPH_STRING *Name - ) -{ - NTSTATUS status; - PUNICODE_STRING unicodeString; - - if (!NT_SUCCESS(status = PhpQueryDriverVariableSize( - DriverHandle, - DriverNameInformation, - &unicodeString - ))) - return status; - - *Name = PhCreateStringEx( - unicodeString->Buffer, - unicodeString->Length - ); - PhFree(unicodeString); - - return status; -} - -/** - * Gets the service key name of a driver. - * - * \param DriverHandle A handle to a driver. - * \param ServiceKeyName A variable which receives a pointer to a string containing the service key - * name. You must free the string using PhDereferenceObject() when you no longer need it. - * - * \remarks This function requires a valid KProcessHacker handle. - */ -NTSTATUS PhGetDriverServiceKeyName( - _In_ HANDLE DriverHandle, - _Out_ PPH_STRING *ServiceKeyName - ) -{ - NTSTATUS status; - PUNICODE_STRING unicodeString; - - if (!NT_SUCCESS(status = PhpQueryDriverVariableSize( - DriverHandle, - DriverServiceKeyNameInformation, - &unicodeString - ))) - return status; - - *ServiceKeyName = PhCreateStringEx( - unicodeString->Buffer, - unicodeString->Length - ); - PhFree(unicodeString); - - return status; -} - -NTSTATUS PhpUnloadDriver( - _In_ PPH_STRING ServiceKeyName - ) -{ - static PH_STRINGREF fullServicesKeyName = PH_STRINGREF_INIT(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); - - NTSTATUS status; - PPH_STRING fullServiceKeyName; - UNICODE_STRING fullServiceKeyNameUs; - HANDLE serviceKeyHandle; - ULONG disposition; - - fullServiceKeyName = PhConcatStringRef2(&fullServicesKeyName, &ServiceKeyName->sr); - - if (!PhStringRefToUnicodeString(&fullServiceKeyName->sr, &fullServiceKeyNameUs)) - { - PhDereferenceObject(fullServiceKeyName); - return STATUS_NAME_TOO_LONG; - } - - if (NT_SUCCESS(status = PhCreateKey( - &serviceKeyHandle, - KEY_WRITE | DELETE, - NULL, - &fullServiceKeyName->sr, - 0, - 0, - &disposition - ))) - { - if (disposition == REG_CREATED_NEW_KEY) - { - static UNICODE_STRING imagePath = RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\drivers\\ntfs.sys"); - - UNICODE_STRING valueName; - ULONG dword; - - // Set up the required values. - dword = 1; - RtlInitUnicodeString(&valueName, L"ErrorControl"); - NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_DWORD, &dword, sizeof(ULONG)); - RtlInitUnicodeString(&valueName, L"Start"); - NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_DWORD, &dword, sizeof(ULONG)); - RtlInitUnicodeString(&valueName, L"Type"); - NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_DWORD, &dword, sizeof(ULONG)); - - // Use a bogus name. - RtlInitUnicodeString(&valueName, L"ImagePath"); - NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_SZ, imagePath.Buffer, imagePath.Length + 2); - } - - status = NtUnloadDriver(&fullServiceKeyNameUs); - - if (disposition == REG_CREATED_NEW_KEY) - { - // We added values, not subkeys, so this function will work correctly. - NtDeleteKey(serviceKeyHandle); - } - - NtClose(serviceKeyHandle); - } - - PhDereferenceObject(fullServiceKeyName); - - return status; -} - -/** - * Unloads a driver. - * - * \param BaseAddress The base address of the driver. This parameter can be NULL if a value is - * specified in \c Name. - * \param Name The base name of the driver. This parameter can be NULL if a value is specified in - * \c BaseAddress and KProcessHacker is loaded. - * - * \retval STATUS_INVALID_PARAMETER_MIX Both \c BaseAddress and \c Name were null, or \c Name was - * not specified and KProcessHacker is not loaded. - * \retval STATUS_OBJECT_NAME_NOT_FOUND The driver could not be found. - */ -NTSTATUS PhUnloadDriver( - _In_opt_ PVOID BaseAddress, - _In_opt_ PWSTR Name - ) -{ - NTSTATUS status; - HANDLE driverHandle; - PPH_STRING serviceKeyName = NULL; - - if (!BaseAddress && !Name) - return STATUS_INVALID_PARAMETER_MIX; - if (!Name && !KphIsConnected()) - return STATUS_INVALID_PARAMETER_MIX; - - // Try to get the service key name by scanning the Driver directory. - - if (KphIsConnected() && BaseAddress) - { - if (NT_SUCCESS(PhOpenDriverByBaseAddress( - &driverHandle, - BaseAddress - ))) - { - PhGetDriverServiceKeyName(driverHandle, &serviceKeyName); - NtClose(driverHandle); - } - } - - // Use the base name if we didn't get the service key name. - - if (!serviceKeyName && Name) - { - PPH_STRING name; - - name = PhCreateString(Name); - - // Remove the extension if it is present. - if (PhEndsWithString2(name, L".sys", TRUE)) - { - serviceKeyName = PhSubstring(name, 0, name->Length / 2 - 4); - PhDereferenceObject(name); - } - else - { - serviceKeyName = name; - } - } - - if (!serviceKeyName) - return STATUS_OBJECT_NAME_NOT_FOUND; - - status = PhpUnloadDriver(serviceKeyName); - PhDereferenceObject(serviceKeyName); - - return status; -} - -NTSTATUS PhpEnumProcessModules( - _In_ HANDLE ProcessHandle, - _In_ PPHP_ENUM_PROCESS_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ) -{ - NTSTATUS status; - PROCESS_BASIC_INFORMATION basicInfo; - PPEB_LDR_DATA ldr; - PEB_LDR_DATA pebLdrData; - PLIST_ENTRY startLink; - PLIST_ENTRY currentLink; - ULONG dataTableEntrySize; - LDR_DATA_TABLE_ENTRY currentEntry; - ULONG i; - - // Get the PEB address. - status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo); - - if (!NT_SUCCESS(status)) - return status; - - // Read the address of the loader data. - status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, Ldr)), - &ldr, - sizeof(PVOID), - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Read the loader data. - status = NtReadVirtualMemory( - ProcessHandle, - ldr, - &pebLdrData, - sizeof(PEB_LDR_DATA), - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - if (!pebLdrData.Initialized) - return STATUS_UNSUCCESSFUL; - - if (WindowsVersion >= WINDOWS_8) - dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN8; - else if (WindowsVersion >= WINDOWS_7) - dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN7; - else - dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WINXP; - - // Traverse the linked list (in load order). - - i = 0; - startLink = PTR_ADD_OFFSET(ldr, FIELD_OFFSET(PEB_LDR_DATA, InLoadOrderModuleList)); - currentLink = pebLdrData.InLoadOrderModuleList.Flink; - - while ( - currentLink != startLink && - i <= PH_ENUM_PROCESS_MODULES_LIMIT - ) - { - PVOID addressOfEntry; - - addressOfEntry = CONTAINING_RECORD(currentLink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); - status = NtReadVirtualMemory( - ProcessHandle, - addressOfEntry, - ¤tEntry, - dataTableEntrySize, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Make sure the entry is valid. - if (currentEntry.DllBase) - { - // Execute the callback. - if (!Callback( - ProcessHandle, - ¤tEntry, - addressOfEntry, - Context1, - Context2 - )) - break; - } - - currentLink = currentEntry.InLoadOrderLinks.Flink; - i++; - } - - return status; -} - -BOOLEAN NTAPI PhpEnumProcessModulesCallback( - _In_ HANDLE ProcessHandle, - _In_ PLDR_DATA_TABLE_ENTRY Entry, - _In_ PVOID AddressOfEntry, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ) -{ - NTSTATUS status; - PPH_ENUM_PROCESS_MODULES_PARAMETERS parameters; - BOOLEAN cont; - PPH_STRING mappedFileName; - PWSTR fullDllNameOriginal; - PWSTR fullDllNameBuffer; - PWSTR baseDllNameOriginal; - PWSTR baseDllNameBuffer; - - parameters = Context1; - mappedFileName = NULL; - - if (parameters->Flags & PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME) - { - PhGetProcessMappedFileName(ProcessHandle, Entry->DllBase, &mappedFileName); - } - - if (mappedFileName) - { - ULONG_PTR indexOfLastBackslash; - - PhStringRefToUnicodeString(&mappedFileName->sr, &Entry->FullDllName); - indexOfLastBackslash = PhFindLastCharInString(mappedFileName, 0, '\\'); - - if (indexOfLastBackslash != -1) - { - Entry->BaseDllName.Buffer = Entry->FullDllName.Buffer + indexOfLastBackslash + 1; - Entry->BaseDllName.Length = Entry->FullDllName.Length - (USHORT)indexOfLastBackslash * 2 - 2; - Entry->BaseDllName.MaximumLength = Entry->BaseDllName.Length; - } - else - { - Entry->BaseDllName = Entry->FullDllName; - } - } - else - { - // Read the full DLL name string and add a null terminator. - - fullDllNameOriginal = Entry->FullDllName.Buffer; - fullDllNameBuffer = PhAllocate(Entry->FullDllName.Length + 2); - Entry->FullDllName.Buffer = fullDllNameBuffer; - - if (NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - fullDllNameOriginal, - fullDllNameBuffer, - Entry->FullDllName.Length, - NULL - ))) - { - fullDllNameBuffer[Entry->FullDllName.Length / 2] = 0; - } - else - { - fullDllNameBuffer[0] = 0; - Entry->FullDllName.Length = 0; - } - - baseDllNameOriginal = Entry->BaseDllName.Buffer; - - // Try to use the buffer we just read in. - if ( - NT_SUCCESS(status) && - (ULONG_PTR)baseDllNameOriginal >= (ULONG_PTR)fullDllNameOriginal && - (ULONG_PTR)baseDllNameOriginal + Entry->BaseDllName.Length >= (ULONG_PTR)baseDllNameOriginal && - (ULONG_PTR)baseDllNameOriginal + Entry->BaseDllName.Length <= (ULONG_PTR)fullDllNameOriginal + Entry->FullDllName.Length - ) - { - baseDllNameBuffer = NULL; - - Entry->BaseDllName.Buffer = (PWCHAR)((ULONG_PTR)Entry->FullDllName.Buffer + - ((ULONG_PTR)baseDllNameOriginal - (ULONG_PTR)fullDllNameOriginal)); - } - else - { - // Read the base DLL name string and add a null terminator. - - baseDllNameBuffer = PhAllocate(Entry->BaseDllName.Length + 2); - Entry->BaseDllName.Buffer = baseDllNameBuffer; - - if (NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - baseDllNameOriginal, - baseDllNameBuffer, - Entry->BaseDllName.Length, - NULL - ))) - { - baseDllNameBuffer[Entry->BaseDllName.Length / 2] = 0; - } - else - { - baseDllNameBuffer[0] = 0; - Entry->BaseDllName.Length = 0; - } - } - } - - // Execute the callback. - cont = parameters->Callback(Entry, parameters->Context); - - if (mappedFileName) - { - PhDereferenceObject(mappedFileName); - } - else - { - PhFree(fullDllNameBuffer); - - if (baseDllNameBuffer) - PhFree(baseDllNameBuffer); - } - - return cont; -} - -/** - * Enumerates the modules loaded by a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. - * \param Callback A callback function which is executed for each process module. - * \param Context A user-defined value to pass to the callback function. - */ -NTSTATUS PhEnumProcessModules( - _In_ HANDLE ProcessHandle, - _In_ PPH_ENUM_PROCESS_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - PH_ENUM_PROCESS_MODULES_PARAMETERS parameters; - - parameters.Callback = Callback; - parameters.Context = Context; - parameters.Flags = 0; - - return PhEnumProcessModulesEx(ProcessHandle, ¶meters); -} - -/** - * Enumerates the modules loaded by a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. If - * \c PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME is specified in \a Parameters, the handle should - * have PROCESS_QUERY_INFORMATION access. - * \param Parameters The enumeration parameters. - */ -NTSTATUS PhEnumProcessModulesEx( - _In_ HANDLE ProcessHandle, - _In_ PPH_ENUM_PROCESS_MODULES_PARAMETERS Parameters - ) -{ - return PhpEnumProcessModules( - ProcessHandle, - PhpEnumProcessModulesCallback, - Parameters, - NULL - ); -} - -typedef struct _SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT -{ - NTSTATUS Status; - PVOID BaseAddress; - ULONG LoadCount; -} SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT, *PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT; - -BOOLEAN NTAPI PhpSetProcessModuleLoadCountCallback( - _In_ HANDLE ProcessHandle, - _In_ PLDR_DATA_TABLE_ENTRY Entry, - _In_ PVOID AddressOfEntry, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ) -{ - PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context = Context1; - - if (Entry->DllBase == context->BaseAddress) - { - context->Status = NtWriteVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(AddressOfEntry, FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, ObsoleteLoadCount)), - &context->LoadCount, - sizeof(USHORT), - NULL - ); - - return FALSE; - } - - return TRUE; -} - -/** - * Sets the load count of a process module. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ and PROCESS_VM_WRITE access. - * \param BaseAddress The base address of a module. - * \param LoadCount The new load count of the module. - * - * \retval STATUS_DLL_NOT_FOUND The module was not found. - */ -NTSTATUS PhSetProcessModuleLoadCount( - _In_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, - _In_ ULONG LoadCount - ) -{ - NTSTATUS status; - SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context; - - context.Status = STATUS_DLL_NOT_FOUND; - context.BaseAddress = BaseAddress; - context.LoadCount = LoadCount; - - status = PhpEnumProcessModules( - ProcessHandle, - PhpSetProcessModuleLoadCountCallback, - &context, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - return context.Status; -} - -NTSTATUS PhpEnumProcessModules32( - _In_ HANDLE ProcessHandle, - _In_ PPHP_ENUM_PROCESS_MODULES32_CALLBACK Callback, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ) -{ - NTSTATUS status; - PPEB32 peb; - ULONG ldr; // PEB_LDR_DATA32 *32 - PEB_LDR_DATA32 pebLdrData; - ULONG startLink; // LIST_ENTRY32 *32 - ULONG currentLink; // LIST_ENTRY32 *32 - ULONG dataTableEntrySize; - LDR_DATA_TABLE_ENTRY32 currentEntry; - ULONG i; - - // Get the 32-bit PEB address. - status = PhGetProcessPeb32(ProcessHandle, &peb); - - if (!NT_SUCCESS(status)) - return status; - - if (!peb) - return STATUS_NOT_SUPPORTED; // not a WOW64 process - - // Read the address of the loader data. - status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(peb, FIELD_OFFSET(PEB32, Ldr)), - &ldr, - sizeof(ULONG), - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Read the loader data. - status = NtReadVirtualMemory( - ProcessHandle, - UlongToPtr(ldr), - &pebLdrData, - sizeof(PEB_LDR_DATA32), - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - if (!pebLdrData.Initialized) - return STATUS_UNSUCCESSFUL; - - if (WindowsVersion >= WINDOWS_8) - dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN8_32; - else if (WindowsVersion >= WINDOWS_7) - dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN7_32; - else - dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WINXP_32; - - // Traverse the linked list (in load order). - - i = 0; - startLink = (ULONG)(ldr + FIELD_OFFSET(PEB_LDR_DATA32, InLoadOrderModuleList)); - currentLink = pebLdrData.InLoadOrderModuleList.Flink; - - while ( - currentLink != startLink && - i <= PH_ENUM_PROCESS_MODULES_LIMIT - ) - { - ULONG addressOfEntry; - - addressOfEntry = PtrToUlong(CONTAINING_RECORD(UlongToPtr(currentLink), LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks)); - status = NtReadVirtualMemory( - ProcessHandle, - UlongToPtr(addressOfEntry), - ¤tEntry, - dataTableEntrySize, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - // Make sure the entry is valid. - if (currentEntry.DllBase) - { - // Execute the callback. - if (!Callback( - ProcessHandle, - ¤tEntry, - addressOfEntry, - Context1, - Context2 - )) - break; - } - - currentLink = currentEntry.InLoadOrderLinks.Flink; - i++; - } - - return status; -} - -BOOLEAN NTAPI PhpEnumProcessModules32Callback( - _In_ HANDLE ProcessHandle, - _In_ PLDR_DATA_TABLE_ENTRY32 Entry, - _In_ ULONG AddressOfEntry, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ) -{ - static PH_STRINGREF system32String = PH_STRINGREF_INIT(L"\\system32\\"); - - PPH_ENUM_PROCESS_MODULES_PARAMETERS parameters; - BOOLEAN cont; - LDR_DATA_TABLE_ENTRY nativeEntry; - PPH_STRING mappedFileName; - PWSTR baseDllNameBuffer; - PWSTR fullDllNameBuffer; - PH_STRINGREF fullDllName; - PH_STRINGREF systemRootString; - - parameters = Context1; - - // Convert the 32-bit entry to a native-sized entry. - - memset(&nativeEntry, 0, sizeof(LDR_DATA_TABLE_ENTRY)); - nativeEntry.DllBase = UlongToPtr(Entry->DllBase); - nativeEntry.EntryPoint = UlongToPtr(Entry->EntryPoint); - nativeEntry.SizeOfImage = Entry->SizeOfImage; - UStr32ToUStr(&nativeEntry.FullDllName, &Entry->FullDllName); - UStr32ToUStr(&nativeEntry.BaseDllName, &Entry->BaseDllName); - nativeEntry.Flags = Entry->Flags; - nativeEntry.ObsoleteLoadCount = Entry->ObsoleteLoadCount; - nativeEntry.TlsIndex = Entry->TlsIndex; - nativeEntry.TimeDateStamp = Entry->TimeDateStamp; - nativeEntry.OriginalBase = Entry->OriginalBase; - nativeEntry.LoadTime = Entry->LoadTime; - nativeEntry.BaseNameHashValue = Entry->BaseNameHashValue; - nativeEntry.LoadReason = Entry->LoadReason; - - mappedFileName = NULL; - - if (parameters->Flags & PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME) - { - PhGetProcessMappedFileName(ProcessHandle, nativeEntry.DllBase, &mappedFileName); - } - - if (mappedFileName) - { - ULONG_PTR indexOfLastBackslash; - - PhStringRefToUnicodeString(&mappedFileName->sr, &nativeEntry.FullDllName); - indexOfLastBackslash = PhFindLastCharInString(mappedFileName, 0, '\\'); - - if (indexOfLastBackslash != -1) - { - nativeEntry.BaseDllName.Buffer = nativeEntry.FullDllName.Buffer + indexOfLastBackslash + 1; - nativeEntry.BaseDllName.Length = nativeEntry.FullDllName.Length - (USHORT)indexOfLastBackslash * 2 - 2; - nativeEntry.BaseDllName.MaximumLength = nativeEntry.BaseDllName.Length; - } - else - { - nativeEntry.BaseDllName = nativeEntry.FullDllName; - } - } - else - { - // Read the base DLL name string and add a null terminator. - - baseDllNameBuffer = PhAllocate(nativeEntry.BaseDllName.Length + 2); - - if (NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - nativeEntry.BaseDllName.Buffer, - baseDllNameBuffer, - nativeEntry.BaseDllName.Length, - NULL - ))) - { - baseDllNameBuffer[nativeEntry.BaseDllName.Length / 2] = 0; - } - else - { - baseDllNameBuffer[0] = 0; - nativeEntry.BaseDllName.Length = 0; - } - - nativeEntry.BaseDllName.Buffer = baseDllNameBuffer; - - // Read the full DLL name string and add a null terminator. - - fullDllNameBuffer = PhAllocate(nativeEntry.FullDllName.Length + 2); - - if (NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - nativeEntry.FullDllName.Buffer, - fullDllNameBuffer, - nativeEntry.FullDllName.Length, - NULL - ))) - { - fullDllNameBuffer[nativeEntry.FullDllName.Length / 2] = 0; - - if (!(parameters->Flags & PH_ENUM_PROCESS_MODULES_DONT_RESOLVE_WOW64_FS)) - { - // WOW64 file system redirection - convert "system32" to "SysWOW64". - if (!(nativeEntry.FullDllName.Length & 1)) // validate the string length - { - fullDllName.Buffer = fullDllNameBuffer; - fullDllName.Length = nativeEntry.FullDllName.Length; - - PhGetSystemRoot(&systemRootString); - - if (PhStartsWithStringRef(&fullDllName, &systemRootString, TRUE)) - { - PhSkipStringRef(&fullDllName, systemRootString.Length); - - if (PhStartsWithStringRef(&fullDllName, &system32String, TRUE)) - { - fullDllName.Buffer[1] = 'S'; - fullDllName.Buffer[4] = 'W'; - fullDllName.Buffer[5] = 'O'; - fullDllName.Buffer[6] = 'W'; - fullDllName.Buffer[7] = '6'; - fullDllName.Buffer[8] = '4'; - } - } - } - } - } - else - { - fullDllNameBuffer[0] = 0; - nativeEntry.FullDllName.Length = 0; - } - - nativeEntry.FullDllName.Buffer = fullDllNameBuffer; - } - - // Execute the callback. - cont = parameters->Callback(&nativeEntry, parameters->Context); - - if (mappedFileName) - { - PhDereferenceObject(mappedFileName); - } - else - { - PhFree(baseDllNameBuffer); - PhFree(fullDllNameBuffer); - } - - return cont; -} - -/** - * Enumerates the 32-bit modules loaded by a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. - * \param Callback A callback function which is executed for each process module. - * \param Context A user-defined value to pass to the callback function. - * - * \retval STATUS_NOT_SUPPORTED The process is not running under WOW64. - * - * \remarks Do not use this function under a 32-bit environment. - */ -NTSTATUS PhEnumProcessModules32( - _In_ HANDLE ProcessHandle, - _In_ PPH_ENUM_PROCESS_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - PH_ENUM_PROCESS_MODULES_PARAMETERS parameters; - - parameters.Callback = Callback; - parameters.Context = Context; - parameters.Flags = 0; - - return PhEnumProcessModules32Ex(ProcessHandle, ¶meters); -} - -/** - * Enumerates the 32-bit modules loaded by a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. If - * \c PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME is specified in \a Parameters, the handle should - * have PROCESS_QUERY_INFORMATION access. - * \param Parameters The enumeration parameters. - * - * \retval STATUS_NOT_SUPPORTED The process is not running under WOW64. - * - * \remarks Do not use this function under a 32-bit environment. - */ -NTSTATUS PhEnumProcessModules32Ex( - _In_ HANDLE ProcessHandle, - _In_ PPH_ENUM_PROCESS_MODULES_PARAMETERS Parameters - ) -{ - return PhpEnumProcessModules32( - ProcessHandle, - PhpEnumProcessModules32Callback, - Parameters, - NULL - ); -} - -BOOLEAN NTAPI PhpSetProcessModuleLoadCount32Callback( - _In_ HANDLE ProcessHandle, - _In_ PLDR_DATA_TABLE_ENTRY32 Entry, - _In_ ULONG AddressOfEntry, - _In_opt_ PVOID Context1, - _In_opt_ PVOID Context2 - ) -{ - PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context = Context1; - - if (UlongToPtr(Entry->DllBase) == context->BaseAddress) - { - context->Status = NtWriteVirtualMemory( - ProcessHandle, - UlongToPtr(AddressOfEntry + FIELD_OFFSET(LDR_DATA_TABLE_ENTRY32, ObsoleteLoadCount)), - &context->LoadCount, - sizeof(USHORT), - NULL - ); - - return FALSE; - } - - return TRUE; -} - -/** - * Sets the load count of a 32-bit process module. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ and PROCESS_VM_WRITE access. - * \param BaseAddress The base address of a module. - * \param LoadCount The new load count of the module. - * - * \retval STATUS_DLL_NOT_FOUND The module was not found. - * \retval STATUS_NOT_SUPPORTED The process is not running under WOW64. - * - * \remarks Do not use this function under a 32-bit environment. - */ -NTSTATUS PhSetProcessModuleLoadCount32( - _In_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, - _In_ ULONG LoadCount - ) -{ - NTSTATUS status; - SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context; - - context.Status = STATUS_DLL_NOT_FOUND; - context.BaseAddress = BaseAddress; - context.LoadCount = LoadCount; - - status = PhpEnumProcessModules32( - ProcessHandle, - PhpSetProcessModuleLoadCount32Callback, - &context, - NULL - ); - - if (!NT_SUCCESS(status)) - return status; - - return context.Status; -} - -typedef struct _GET_PROCEDURE_ADDRESS_REMOTE_CONTEXT -{ - PH_STRINGREF FileName; - PVOID DllBase; -} GET_PROCEDURE_ADDRESS_REMOTE_CONTEXT, *PGET_PROCEDURE_ADDRESS_REMOTE_CONTEXT; - -static BOOLEAN PhpGetProcedureAddressRemoteCallback( - _In_ PLDR_DATA_TABLE_ENTRY Module, - _In_opt_ PVOID Context - ) -{ - PGET_PROCEDURE_ADDRESS_REMOTE_CONTEXT context = Context; - PH_STRINGREF fullDllName; - - PhUnicodeStringToStringRef(&Module->FullDllName, &fullDllName); - - if (PhEqualStringRef(&fullDllName, &context->FileName, TRUE)) - { - context->DllBase = Module->DllBase; - return FALSE; - } - - return TRUE; -} - -/** - * Gets the address of a procedure in a process. - * - * \param ProcessHandle A handle to a process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. - * \param FileName The file name of the DLL containing the procedure. - * \param ProcedureName The name of the procedure. - * \param ProcedureNumber The ordinal of the procedure. - * \param ProcedureAddress A variable which receives the address of the procedure in the address - * space of the process. - * \param DllBase A variable which receives the base address of the DLL containing the procedure. - */ -NTSTATUS PhGetProcedureAddressRemote( - _In_ HANDLE ProcessHandle, - _In_ PWSTR FileName, - _In_opt_ PSTR ProcedureName, - _In_opt_ ULONG ProcedureNumber, - _Out_ PVOID *ProcedureAddress, - _Out_opt_ PVOID *DllBase - ) -{ - NTSTATUS status; - PH_MAPPED_IMAGE mappedImage; - PH_MAPPED_IMAGE_EXPORTS exports; - GET_PROCEDURE_ADDRESS_REMOTE_CONTEXT context; - - if (!NT_SUCCESS(status = PhLoadMappedImage(FileName, NULL, TRUE, &mappedImage))) - return status; - - PhInitializeStringRef(&context.FileName, FileName); - context.DllBase = NULL; - - if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { -#ifdef _WIN64 - status = PhEnumProcessModules32(ProcessHandle, PhpGetProcedureAddressRemoteCallback, &context); -#else - status = PhEnumProcessModules(ProcessHandle, PhpGetProcedureAddressRemoteCallback, &context); -#endif - } - else - { -#ifdef _WIN64 - status = PhEnumProcessModules(ProcessHandle, PhpGetProcedureAddressRemoteCallback, &context); -#else - status = STATUS_NOT_SUPPORTED; -#endif - } - - if (!NT_SUCCESS(status)) - goto CleanupExit; - - if (!NT_SUCCESS(status = PhGetMappedImageExports(&exports, &mappedImage))) - goto CleanupExit; - - status = PhGetMappedImageExportFunctionRemote( - &exports, - ProcedureName, - (USHORT)ProcedureNumber, - context.DllBase, - ProcedureAddress - ); - - if (NT_SUCCESS(status)) - { - if (DllBase) - *DllBase = context.DllBase; - } - -CleanupExit: - PhUnloadMappedImage(&mappedImage); - - return status; -} - -/** - * Enumerates the modules loaded by the kernel. - * - * \param Modules A variable which receives a pointer to a structure containing information about - * the kernel modules. You must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhEnumKernelModules( - _Out_ PRTL_PROCESS_MODULES *Modules - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 2048; - - buffer = PhAllocate(bufferSize); - - status = NtQuerySystemInformation( - SystemModuleInformation, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - - status = NtQuerySystemInformation( - SystemModuleInformation, - buffer, - bufferSize, - &bufferSize - ); - } - - if (!NT_SUCCESS(status)) - return status; - - *Modules = buffer; - - return status; -} - -/** - * Enumerates the modules loaded by the kernel. - * - * \param Modules A variable which receives a pointer to a structure containing information about - * the kernel modules. You must free the structure using PhFree() when you no longer need it. - */ -NTSTATUS PhEnumKernelModulesEx( - _Out_ PRTL_PROCESS_MODULE_INFORMATION_EX *Modules - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 2048; - - buffer = PhAllocate(bufferSize); - - status = NtQuerySystemInformation( - SystemModuleInformationEx, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - - status = NtQuerySystemInformation( - SystemModuleInformationEx, - buffer, - bufferSize, - &bufferSize - ); - } - - if (!NT_SUCCESS(status)) - return status; - - *Modules = buffer; - - return status; -} - -/** - * Gets the file name of the kernel image. - * - * \return A pointer to a string containing the kernel image file name. You must free the string - * using PhDereferenceObject() when you no longer need it. - */ -PPH_STRING PhGetKernelFileName( - VOID - ) -{ - PRTL_PROCESS_MODULES modules; - PPH_STRING fileName = NULL; - - if (!NT_SUCCESS(PhEnumKernelModules(&modules))) - return NULL; - - if (modules->NumberOfModules >= 1) - { - fileName = PhConvertMultiByteToUtf16(modules->Modules[0].FullPathName); - } - - PhFree(modules); - - return fileName; -} - -/** - * Enumerates the running processes. - * - * \param Processes A variable which receives a pointer to a buffer containing process information. - * You must free the buffer using PhFree() when you no longer need it. - * - * \remarks You can use the \ref PH_FIRST_PROCESS and \ref PH_NEXT_PROCESS macros to process the - * information contained in the buffer. - */ -NTSTATUS PhEnumProcesses( - _Out_ PVOID *Processes - ) -{ - return PhEnumProcessesEx(Processes, SystemProcessInformation); -} - -/** - * Enumerates the running processes. - * - * \param Processes A variable which receives a pointer to a buffer containing process information. - * You must free the buffer using PhFree() when you no longer need it. - * - * \remarks You can use the \ref PH_FIRST_PROCESS and \ref PH_NEXT_PROCESS macros to process the - * information contained in the buffer. - */ -NTSTATUS PhEnumProcessesEx( - _Out_ PVOID *Processes, - _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass - ) -{ - static ULONG initialBufferSize[3] = { 0x4000, 0x4000, 0x4000 }; - NTSTATUS status; - ULONG classIndex; - PVOID buffer; - ULONG bufferSize; - - switch (SystemInformationClass) - { - case SystemProcessInformation: - classIndex = 0; - break; - case SystemExtendedProcessInformation: - classIndex = 1; - break; - case SystemFullProcessInformation: - classIndex = 2; - break; - default: - return STATUS_INVALID_INFO_CLASS; - } - - bufferSize = initialBufferSize[classIndex]; - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - status = NtQuerySystemInformation( - SystemInformationClass, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - if (bufferSize <= 0x100000) initialBufferSize[classIndex] = bufferSize; - *Processes = buffer; - - return status; -} - -/** - * Enumerates the running processes for a session. - * - * \param Processes A variable which receives a pointer to a buffer containing process information. - * You must free the buffer using PhFree() when you no longer need it. - * \param SessionId A session ID. - * - * \remarks You can use the \ref PH_FIRST_PROCESS and \ref PH_NEXT_PROCESS macros to process the - * information contained in the buffer. - */ -NTSTATUS PhEnumProcessesForSession( - _Out_ PVOID *Processes, - _In_ ULONG SessionId - ) -{ - static ULONG initialBufferSize = 0x4000; - NTSTATUS status; - SYSTEM_SESSION_PROCESS_INFORMATION sessionProcessInfo; - PVOID buffer; - ULONG bufferSize; - - bufferSize = initialBufferSize; - buffer = PhAllocate(bufferSize); - - sessionProcessInfo.SessionId = SessionId; - - while (TRUE) - { - sessionProcessInfo.SizeOfBuf = bufferSize; - sessionProcessInfo.Buffer = buffer; - - status = NtQuerySystemInformation( - SystemSessionProcessInformation, - &sessionProcessInfo, - sizeof(SYSTEM_SESSION_PROCESS_INFORMATION), - &bufferSize // size of the inner buffer gets returned - ); - - if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - if (bufferSize <= 0x100000) initialBufferSize = bufferSize; - *Processes = buffer; - - return status; -} - -/** - * Finds the process information structure for a specific process. - * - * \param Processes A pointer to a buffer returned by PhEnumProcesses(). - * \param ProcessId The ID of the process. - * - * \return A pointer to the process information structure for the specified process, or NULL if the - * structure could not be found. - */ -PSYSTEM_PROCESS_INFORMATION PhFindProcessInformation( - _In_ PVOID Processes, - _In_ HANDLE ProcessId - ) -{ - PSYSTEM_PROCESS_INFORMATION process; - - process = PH_FIRST_PROCESS(Processes); - - do - { - if (process->UniqueProcessId == ProcessId) - return process; - } while (process = PH_NEXT_PROCESS(process)); - - return NULL; -} - -/** - * Finds the process information structure for a specific process. - * - * \param Processes A pointer to a buffer returned by PhEnumProcesses(). - * \param ImageName The image name to search for. - * - * \return A pointer to the process information structure for the specified process, or NULL if the - * structure could not be found. - */ -PSYSTEM_PROCESS_INFORMATION PhFindProcessInformationByImageName( - _In_ PVOID Processes, - _In_ PPH_STRINGREF ImageName - ) -{ - PSYSTEM_PROCESS_INFORMATION process; - PH_STRINGREF processImageName; - - process = PH_FIRST_PROCESS(Processes); - - do - { - PhUnicodeStringToStringRef(&process->ImageName, &processImageName); - - if (PhEqualStringRef(&processImageName, ImageName, TRUE)) - return process; - } while (process = PH_NEXT_PROCESS(process)); - - return NULL; -} - -/** - * Enumerates all open handles. - * - * \param Handles A variable which receives a pointer to a structure containing information about - * all opened handles. You must free the structure using PhFree() when you no longer need it. - * - * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. - */ -NTSTATUS PhEnumHandles( - _Out_ PSYSTEM_HANDLE_INFORMATION *Handles - ) -{ - static ULONG initialBufferSize = 0x4000; - NTSTATUS status; - PVOID buffer; - ULONG bufferSize; - - bufferSize = initialBufferSize; - buffer = PhAllocate(bufferSize); - - while ((status = NtQuerySystemInformation( - SystemHandleInformation, - buffer, - bufferSize, - NULL - )) == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - - // Fail if we're resizing the buffer to something very large. - if (bufferSize > PH_LARGE_BUFFER_SIZE) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - if (bufferSize <= 0x100000) initialBufferSize = bufferSize; - *Handles = (PSYSTEM_HANDLE_INFORMATION)buffer; - - return status; -} - -/** - * Enumerates all open handles. - * - * \param Handles A variable which receives a pointer to a structure containing information about - * all opened handles. You must free the structure using PhFree() when you no longer need it. - * - * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. - * - * \remarks This function is only available starting with Windows XP. - */ -NTSTATUS PhEnumHandlesEx( - _Out_ PSYSTEM_HANDLE_INFORMATION_EX *Handles - ) -{ - static ULONG initialBufferSize = 0x10000; - NTSTATUS status; - PVOID buffer; - ULONG bufferSize; - - bufferSize = initialBufferSize; - buffer = PhAllocate(bufferSize); - - while ((status = NtQuerySystemInformation( - SystemExtendedHandleInformation, - buffer, - bufferSize, - NULL - )) == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - - // Fail if we're resizing the buffer to something very large. - if (bufferSize > PH_LARGE_BUFFER_SIZE) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - if (bufferSize <= 0x200000) initialBufferSize = bufferSize; - *Handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer; - - return status; -} - -/** - * Enumerates all pagefiles. - * - * \param Pagefiles A variable which receives a pointer to a buffer containing information about all - * active pagefiles. You must free the structure using PhFree() when you no longer need it. - * - * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. - */ -NTSTATUS PhEnumPagefiles( - _Out_ PVOID *Pagefiles - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 0x200; - - buffer = PhAllocate(bufferSize); - - while ((status = NtQuerySystemInformation( - SystemPageFileInformation, - buffer, - bufferSize, - NULL - )) == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - - // Fail if we're resizing the buffer to something very large. - if (bufferSize > PH_LARGE_BUFFER_SIZE) - return STATUS_INSUFFICIENT_RESOURCES; - - buffer = PhAllocate(bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *Pagefiles = buffer; - - return status; -} - -/** - * Gets the file name of a process' image. - * - * \param ProcessId The ID of the process. - * \param FileName A variable which receives a pointer to a string containing the file name. You - * must free the string using PhDereferenceObject() when you no longer need it. - * - * \remarks This function only works on Windows Vista and above. There does not appear to be any - * access checking performed by the kernel for this. - */ -NTSTATUS PhGetProcessImageFileNameByProcessId( - _In_ HANDLE ProcessId, - _Out_ PPH_STRING *FileName - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize = 0x100; - SYSTEM_PROCESS_ID_INFORMATION processIdInfo; - - buffer = PhAllocate(bufferSize); - - processIdInfo.ProcessId = ProcessId; - processIdInfo.ImageName.Length = 0; - processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize; - processIdInfo.ImageName.Buffer = buffer; - - status = NtQuerySystemInformation( - SystemProcessIdInformation, - &processIdInfo, - sizeof(SYSTEM_PROCESS_ID_INFORMATION), - NULL - ); - - if (status == STATUS_INFO_LENGTH_MISMATCH) - { - // Required length is stored in MaximumLength. - - PhFree(buffer); - buffer = PhAllocate(processIdInfo.ImageName.MaximumLength); - processIdInfo.ImageName.Buffer = buffer; - - status = NtQuerySystemInformation( - SystemProcessIdInformation, - &processIdInfo, - sizeof(SYSTEM_PROCESS_ID_INFORMATION), - NULL - ); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *FileName = PhCreateStringFromUnicodeString(&processIdInfo.ImageName); - PhFree(buffer); - - return status; -} - -/** - * Determines if a process is managed. - * - * \param ProcessId The ID of the process. - * \param IsDotNet A variable which receives a boolean indicating whether the process is managed. - */ -NTSTATUS PhGetProcessIsDotNet( - _In_ HANDLE ProcessId, - _Out_ PBOOLEAN IsDotNet - ) -{ - return PhGetProcessIsDotNetEx(ProcessId, NULL, 0, IsDotNet, NULL); -} - -BOOLEAN NTAPI PhpIsDotNetEnumProcessModulesCallback( - _In_ PLDR_DATA_TABLE_ENTRY Module, - _In_opt_ PVOID Context - ) -{ - static UNICODE_STRING clrString = RTL_CONSTANT_STRING(L"clr.dll"); - static UNICODE_STRING mscorwksString = RTL_CONSTANT_STRING(L"mscorwks.dll"); - static UNICODE_STRING mscorsvrString = RTL_CONSTANT_STRING(L"mscorsvr.dll"); - static UNICODE_STRING mscorlibString = RTL_CONSTANT_STRING(L"mscorlib.dll"); - static UNICODE_STRING mscorlibNiString = RTL_CONSTANT_STRING(L"mscorlib.ni.dll"); - static UNICODE_STRING clrjitString = RTL_CONSTANT_STRING(L"clrjit.dll"); - static UNICODE_STRING frameworkString = RTL_CONSTANT_STRING(L"\\Microsoft.NET\\Framework\\"); - static UNICODE_STRING framework64String = RTL_CONSTANT_STRING(L"\\Microsoft.NET\\Framework64\\"); - - if ( - RtlEqualUnicodeString(&Module->BaseDllName, &clrString, TRUE) || - RtlEqualUnicodeString(&Module->BaseDllName, &mscorwksString, TRUE) || - RtlEqualUnicodeString(&Module->BaseDllName, &mscorsvrString, TRUE) - ) - { - UNICODE_STRING fileName; - PH_STRINGREF systemRootSr; - UNICODE_STRING systemRoot; - PUNICODE_STRING frameworkPart; - -#ifdef _WIN64 - if (*(PULONG)Context & PH_CLR_PROCESS_IS_WOW64) - { -#endif - frameworkPart = &frameworkString; -#ifdef _WIN64 - } - else - { - frameworkPart = &framework64String; - } -#endif - - fileName = Module->FullDllName; - PhGetSystemRoot(&systemRootSr); - PhStringRefToUnicodeString(&systemRootSr, &systemRoot); - - if (RtlPrefixUnicodeString(&systemRoot, &fileName, TRUE)) - { - fileName.Buffer = (PWCHAR)((PCHAR)fileName.Buffer + systemRoot.Length); - fileName.Length -= systemRoot.Length; - - if (RtlPrefixUnicodeString(frameworkPart, &fileName, TRUE)) - { - fileName.Buffer = (PWCHAR)((PCHAR)fileName.Buffer + frameworkPart->Length); - fileName.Length -= frameworkPart->Length; - - if (fileName.Length >= 4 * sizeof(WCHAR)) // vx.x - { - if (fileName.Buffer[1] == '1') - { - if (fileName.Buffer[3] == '0') - *(PULONG)Context |= PH_CLR_VERSION_1_0; - else if (fileName.Buffer[3] == '1') - *(PULONG)Context |= PH_CLR_VERSION_1_1; - } - else if (fileName.Buffer[1] == '2') - { - *(PULONG)Context |= PH_CLR_VERSION_2_0; - } - else if (fileName.Buffer[1] >= '4' && fileName.Buffer[1] <= '9') - { - *(PULONG)Context |= PH_CLR_VERSION_4_ABOVE; - } - } - } - } - } - else if ( - RtlEqualUnicodeString(&Module->BaseDllName, &mscorlibString, TRUE) || - RtlEqualUnicodeString(&Module->BaseDllName, &mscorlibNiString, TRUE) - ) - { - *(PULONG)Context |= PH_CLR_MSCORLIB_PRESENT; - } - else if (RtlEqualUnicodeString(&Module->BaseDllName, &clrjitString, TRUE)) - { - *(PULONG)Context |= PH_CLR_JIT_PRESENT; - } - - return TRUE; -} - -/** - * Determines if a process is managed. - * - * \param ProcessId The ID of the process. - * \param ProcessHandle An optional handle to the process. The handle must have - * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. - * \param InFlags A combination of flags. - * \li \c PH_CLR_USE_SECTION_CHECK Checks for the existence of related section objects to determine - * whether the process is managed. - * \li \c PH_CLR_NO_WOW64_CHECK Instead of a separate query, uses the presence of the - * \c PH_CLR_KNOWN_IS_WOW64 flag to determine whether the process is running under WOW64. - * \li \c PH_CLR_KNOWN_IS_WOW64 When \c PH_CLR_NO_WOW64_CHECK is specified, indicates that the - * process is managed. - * \param IsDotNet A variable which receives a boolean indicating whether the process is managed. - * \param Flags A variable which receives additional flags. - */ -NTSTATUS PhGetProcessIsDotNetEx( - _In_ HANDLE ProcessId, - _In_opt_ HANDLE ProcessHandle, - _In_ ULONG InFlags, - _Out_opt_ PBOOLEAN IsDotNet, - _Out_opt_ PULONG Flags - ) -{ - NTSTATUS status = STATUS_SUCCESS; - HANDLE processHandle; - ULONG flags; -#ifdef _WIN64 - BOOLEAN isWow64; -#endif - - if (InFlags & PH_CLR_USE_SECTION_CHECK) - { - HANDLE sectionHandle; - OBJECT_ATTRIBUTES objectAttributes; - PPH_STRING sectionName; - UNICODE_STRING sectionNameUs; - PH_FORMAT format[2]; - - // Most .NET processes have a handle open to a section named - // \BaseNamedObjects\Cor_Private_IPCBlock(_v4)_. This is the same object used by - // the ICorPublish::GetProcess function. Instead of calling that function, we simply check - // for the existence of that section object. This means: - // * Better performance. - // * No need for admin rights to get .NET status of processes owned by other users. - - PhInitFormatIU(&format[1], (ULONG_PTR)ProcessId); - - // Version 4 section object - - PhInitFormatS(&format[0], L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_"); - sectionName = PhFormat(format, 2, 96); - PhStringRefToUnicodeString(§ionName->sr, §ionNameUs); - - InitializeObjectAttributes( - &objectAttributes, - §ionNameUs, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - status = NtOpenSection( - §ionHandle, - SECTION_QUERY, - &objectAttributes - ); - PhDereferenceObject(sectionName); - - if (NT_SUCCESS(status) || status == STATUS_ACCESS_DENIED) - { - if (NT_SUCCESS(status)) - NtClose(sectionHandle); - - if (IsDotNet) - *IsDotNet = TRUE; - - if (Flags) - *Flags = PH_CLR_VERSION_4_ABOVE; - - return STATUS_SUCCESS; - } - - // Version 2 section object - - PhInitFormatS(&format[0], L"\\BaseNamedObjects\\Cor_Private_IPCBlock_"); - sectionName = PhFormat(format, 2, 90); - PhStringRefToUnicodeString(§ionName->sr, §ionNameUs); - - InitializeObjectAttributes( - &objectAttributes, - §ionNameUs, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - status = NtOpenSection( - §ionHandle, - SECTION_QUERY, - &objectAttributes - ); - PhDereferenceObject(sectionName); - - if (NT_SUCCESS(status) || status == STATUS_ACCESS_DENIED) - { - if (NT_SUCCESS(status)) - NtClose(sectionHandle); - - if (IsDotNet) - *IsDotNet = TRUE; - - if (Flags) - *Flags = PH_CLR_VERSION_2_0; - - return STATUS_SUCCESS; - } - } - - flags = 0; - processHandle = NULL; - - if (!ProcessHandle) - { - if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId))) - return status; - - ProcessHandle = processHandle; - } - -#ifdef _WIN64 - if (InFlags & PH_CLR_NO_WOW64_CHECK) - { - isWow64 = !!(InFlags & PH_CLR_KNOWN_IS_WOW64); - } - else - { - isWow64 = FALSE; - PhGetProcessIsWow64(ProcessHandle, &isWow64); - } - - if (isWow64) - { - flags |= PH_CLR_PROCESS_IS_WOW64; - PhEnumProcessModules32(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); - } - else - { -#endif - PhEnumProcessModules(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); -#ifdef _WIN64 - } -#endif - - if (processHandle) - NtClose(processHandle); - - if (IsDotNet) - *IsDotNet = (flags & PH_CLR_VERSION_MASK) && (flags & (PH_CLR_MSCORLIB_PRESENT | PH_CLR_JIT_PRESENT)); - - if (Flags) - *Flags = flags; - - return status; -} - -/** - * Enumerates the objects in a directory object. - * - * \param DirectoryHandle A handle to a directory. The handle must have DIRECTORY_QUERY access. - * \param Callback A callback function which is executed for each object. - * \param Context A user-defined value to pass to the callback function. - */ -NTSTATUS PhEnumDirectoryObjects( - _In_ HANDLE DirectoryHandle, - _In_ PPH_ENUM_DIRECTORY_OBJECTS Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - ULONG context = 0; - BOOLEAN firstTime = TRUE; - ULONG bufferSize; - POBJECT_DIRECTORY_INFORMATION buffer; - ULONG i; - BOOLEAN cont; - - bufferSize = 0x200; - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - // Get a batch of entries. - - while ((status = NtQueryDirectoryObject( - DirectoryHandle, - buffer, - bufferSize, - FALSE, - firstTime, - &context, - NULL - )) == STATUS_MORE_ENTRIES) - { - // Check if we have at least one entry. If not, we'll double the buffer size and try - // again. - if (buffer[0].Name.Buffer) - break; - - // Make sure we don't use too much memory. - if (bufferSize > PH_LARGE_BUFFER_SIZE) - { - PhFree(buffer); - return STATUS_INSUFFICIENT_RESOURCES; - } - - PhFree(buffer); - bufferSize *= 2; - buffer = PhAllocate(bufferSize); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - // Read the batch and execute the callback function for each object. - - i = 0; - cont = TRUE; - - while (TRUE) - { - POBJECT_DIRECTORY_INFORMATION info; - PH_STRINGREF name; - PH_STRINGREF typeName; - - info = &buffer[i]; - - if (!info->Name.Buffer) - break; - - PhUnicodeStringToStringRef(&info->Name, &name); - PhUnicodeStringToStringRef(&info->TypeName, &typeName); - - cont = Callback(&name, &typeName, Context); - - if (!cont) - break; - - i++; - } - - if (!cont) - break; - - if (status != STATUS_MORE_ENTRIES) - break; - - firstTime = FALSE; - } - - PhFree(buffer); - - return STATUS_SUCCESS; -} - -NTSTATUS PhEnumDirectoryFile( - _In_ HANDLE FileHandle, - _In_opt_ PUNICODE_STRING SearchPattern, - _In_ PPH_ENUM_DIRECTORY_FILE Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK isb; - BOOLEAN firstTime = TRUE; - PVOID buffer; - ULONG bufferSize = 0x400; - ULONG i; - BOOLEAN cont; - - buffer = PhAllocate(bufferSize); - - while (TRUE) - { - // Query the directory, doubling the buffer each time NtQueryDirectoryFile fails. - while (TRUE) - { - status = NtQueryDirectoryFile( - FileHandle, - NULL, - NULL, - NULL, - &isb, - buffer, - bufferSize, - FileDirectoryInformation, - FALSE, - SearchPattern, - firstTime - ); - - // Our ISB is on the stack, so we have to wait for the operation to complete before - // continuing. - if (status == STATUS_PENDING) - { - status = NtWaitForSingleObject(FileHandle, FALSE, NULL); - - if (NT_SUCCESS(status)) - status = isb.Status; - } - - if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(buffer); - bufferSize *= 2; - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - // If we don't have any entries to read, exit. - if (status == STATUS_NO_MORE_FILES) - { - status = STATUS_SUCCESS; - break; - } - - if (!NT_SUCCESS(status)) - break; - - // Read the batch and execute the callback function for each file. - - i = 0; - cont = TRUE; - - while (TRUE) - { - PFILE_DIRECTORY_INFORMATION information; - - information = (PFILE_DIRECTORY_INFORMATION)(PTR_ADD_OFFSET(buffer, i)); - - if (!Callback( - information, - Context - )) - { - cont = FALSE; - break; - } - - if (information->NextEntryOffset != 0) - i += information->NextEntryOffset; - else - break; - } - - if (!cont) - break; - - firstTime = FALSE; - } - - PhFree(buffer); - - return status; -} - -NTSTATUS PhEnumFileStreams( - _In_ HANDLE FileHandle, - _Out_ PVOID *Streams - ) -{ - return PhpQueryFileVariableSize( - FileHandle, - FileStreamInformation, - Streams - ); -} - -/** - * Initializes the device prefixes module. - */ -VOID PhpInitializeDevicePrefixes( - VOID - ) -{ - ULONG i; - PUCHAR buffer; - - // Allocate one buffer for all 26 prefixes to reduce overhead. - buffer = PhAllocate(PH_DEVICE_PREFIX_LENGTH * sizeof(WCHAR) * 26); - - for (i = 0; i < 26; i++) - { - PhDevicePrefixes[i].Length = 0; - PhDevicePrefixes[i].MaximumLength = PH_DEVICE_PREFIX_LENGTH * sizeof(WCHAR); - PhDevicePrefixes[i].Buffer = (PWCHAR)buffer; - buffer += PH_DEVICE_PREFIX_LENGTH * sizeof(WCHAR); - } -} - -VOID PhUpdateMupDevicePrefixes( - VOID - ) -{ - static PH_STRINGREF orderKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Control\\NetworkProvider\\Order"); - static PH_STRINGREF servicesStringPart = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); - static PH_STRINGREF networkProviderStringPart = PH_STRINGREF_INIT(L"\\NetworkProvider"); - - HANDLE orderKeyHandle; - PPH_STRING providerOrder = NULL; - ULONG i; - PH_STRINGREF remainingPart; - PH_STRINGREF part; - - // The provider names are stored in the ProviderOrder value in this key: - // HKLM\System\CurrentControlSet\Control\NetworkProvider\Order - // Each name can then be looked up, its device name in the DeviceName value in: - // HKLM\System\CurrentControlSet\Services\\NetworkProvider - - // Note that we assume the providers only claim their device name. Some providers such as DFS - // claim an extra part, and are not resolved correctly here. - - if (NT_SUCCESS(PhOpenKey( - &orderKeyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &orderKeyName, - 0 - ))) - { - providerOrder = PhQueryRegistryString(orderKeyHandle, L"ProviderOrder"); - NtClose(orderKeyHandle); - } - - if (!providerOrder) - return; - - PhAcquireQueuedLockExclusive(&PhDeviceMupPrefixesLock); - - for (i = 0; i < PhDeviceMupPrefixesCount; i++) - { - PhDereferenceObject(PhDeviceMupPrefixes[i]); - PhDeviceMupPrefixes[i] = NULL; - } - - PhDeviceMupPrefixesCount = 0; - - PhDeviceMupPrefixes[PhDeviceMupPrefixesCount++] = PhCreateString(L"\\Device\\Mup"); - - // DFS claims an extra part of file names, which we don't handle. - /*if (WindowsVersion >= WINDOWS_VISTA) - PhDeviceMupPrefixes[PhDeviceMupPrefixesCount++] = PhCreateString(L"\\Device\\DfsClient"); - else - PhDeviceMupPrefixes[PhDeviceMupPrefixesCount++] = PhCreateString(L"\\Device\\WinDfs");*/ - - remainingPart = providerOrder->sr; - - while (remainingPart.Length != 0) - { - PPH_STRING serviceKeyName; - HANDLE networkProviderKeyHandle; - PPH_STRING deviceName; - - if (PhDeviceMupPrefixesCount == PH_DEVICE_MUP_PREFIX_MAX_COUNT) - break; - - PhSplitStringRefAtChar(&remainingPart, ',', &part, &remainingPart); - - if (part.Length != 0) - { - serviceKeyName = PhConcatStringRef3(&servicesStringPart, &part, &networkProviderStringPart); - - if (NT_SUCCESS(PhOpenKey( - &networkProviderKeyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &serviceKeyName->sr, - 0 - ))) - { - if (deviceName = PhQueryRegistryString(networkProviderKeyHandle, L"DeviceName")) - { - PhDeviceMupPrefixes[PhDeviceMupPrefixesCount] = deviceName; - PhDeviceMupPrefixesCount++; - } - - NtClose(networkProviderKeyHandle); - } - - PhDereferenceObject(serviceKeyName); - } - } - - PhReleaseQueuedLockExclusive(&PhDeviceMupPrefixesLock); - - PhDereferenceObject(providerOrder); -} - -/** - * Updates the DOS device names array. - */ -VOID PhUpdateDosDevicePrefixes( - VOID - ) -{ - WCHAR deviceNameBuffer[7] = L"\\??\\ :"; - ULONG i; - - for (i = 0; i < 26; i++) - { - HANDLE linkHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING deviceName; - - deviceNameBuffer[4] = (WCHAR)('A' + i); - deviceName.Buffer = deviceNameBuffer; - deviceName.Length = 6 * sizeof(WCHAR); - - InitializeObjectAttributes( - &oa, - &deviceName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - if (NT_SUCCESS(NtOpenSymbolicLinkObject( - &linkHandle, - SYMBOLIC_LINK_QUERY, - &oa - ))) - { - PhAcquireQueuedLockExclusive(&PhDevicePrefixesLock); - - if (!NT_SUCCESS(NtQuerySymbolicLinkObject( - linkHandle, - &PhDevicePrefixes[i], - NULL - ))) - { - PhDevicePrefixes[i].Length = 0; - } - - PhReleaseQueuedLockExclusive(&PhDevicePrefixesLock); - - NtClose(linkHandle); - } - else - { - PhDevicePrefixes[i].Length = 0; - } - } -} - -/** - * Resolves a NT path into a Win32 path. - * - * \param Name A string containing the path to resolve. - * - * \return A pointer to a string containing the Win32 path. You must free the string using - * PhDereferenceObject() when you no longer need it. - */ -PPH_STRING PhResolveDevicePrefix( - _In_ PPH_STRING Name - ) -{ - ULONG i; - PPH_STRING newName = NULL; - - if (PhBeginInitOnce(&PhDevicePrefixesInitOnce)) - { - PhpInitializeDevicePrefixes(); - PhUpdateDosDevicePrefixes(); - PhUpdateMupDevicePrefixes(); - - PhEndInitOnce(&PhDevicePrefixesInitOnce); - } - - // Go through the DOS devices and try to find a matching prefix. - for (i = 0; i < 26; i++) - { - BOOLEAN isPrefix = FALSE; - PH_STRINGREF prefix; - - PhAcquireQueuedLockShared(&PhDevicePrefixesLock); - - PhUnicodeStringToStringRef(&PhDevicePrefixes[i], &prefix); - - if (prefix.Length != 0) - { - if (PhStartsWithStringRef(&Name->sr, &prefix, TRUE)) - { - // To ensure we match the longest prefix, make sure the next character is a - // backslash or the path is equal to the prefix. - if (Name->Length == prefix.Length || Name->Buffer[prefix.Length / sizeof(WCHAR)] == '\\') - { - isPrefix = TRUE; - } - } - } - - PhReleaseQueuedLockShared(&PhDevicePrefixesLock); - - if (isPrefix) - { - // :path - newName = PhCreateStringEx(NULL, 2 * sizeof(WCHAR) + Name->Length - prefix.Length); - newName->Buffer[0] = (WCHAR)('A' + i); - newName->Buffer[1] = ':'; - memcpy( - &newName->Buffer[2], - &Name->Buffer[prefix.Length / sizeof(WCHAR)], - Name->Length - prefix.Length - ); - - break; - } - } - - if (i == 26) - { - // Resolve network providers. - - PhAcquireQueuedLockShared(&PhDeviceMupPrefixesLock); - - for (i = 0; i < PhDeviceMupPrefixesCount; i++) - { - BOOLEAN isPrefix = FALSE; - SIZE_T prefixLength; - - prefixLength = PhDeviceMupPrefixes[i]->Length; - - if (prefixLength != 0) - { - if (PhStartsWithString(Name, PhDeviceMupPrefixes[i], TRUE)) - { - // To ensure we match the longest prefix, make sure the next character is a - // backslash. Don't resolve if the name *is* the prefix. Otherwise, we will end - // up with a useless string like "\". - if (Name->Length != prefixLength && Name->Buffer[prefixLength / sizeof(WCHAR)] == '\\') - { - isPrefix = TRUE; - } - } - } - - if (isPrefix) - { - // \path - newName = PhCreateStringEx(NULL, 1 * sizeof(WCHAR) + Name->Length - prefixLength); - newName->Buffer[0] = '\\'; - memcpy( - &newName->Buffer[1], - &Name->Buffer[prefixLength / sizeof(WCHAR)], - Name->Length - prefixLength - ); - - break; - } - } - - PhReleaseQueuedLockShared(&PhDeviceMupPrefixesLock); - } - - return newName; -} - -/** - * Converts a file name into Win32 format. - * - * \param FileName A string containing a file name. - * - * \return A pointer to a string containing the Win32 file name. You must free the string using - * PhDereferenceObject() when you no longer need it. - * - * \remarks This function may convert NT object name paths to invalid ones. If the path to be - * converted is not necessarily a file name, use PhResolveDevicePrefix(). - */ -PPH_STRING PhGetFileName( - _In_ PPH_STRING FileName - ) -{ - PPH_STRING newFileName; - - newFileName = FileName; - - // "\??\" refers to \GLOBAL??\. Just remove it. - if (PhStartsWithString2(FileName, L"\\??\\", FALSE)) - { - newFileName = PhCreateStringEx(NULL, FileName->Length - 4 * 2); - memcpy(newFileName->Buffer, &FileName->Buffer[4], FileName->Length - 4 * 2); - } - // "\SystemRoot" means "C:\Windows". - else if (PhStartsWithString2(FileName, L"\\SystemRoot", TRUE)) - { - PH_STRINGREF systemRoot; - - PhGetSystemRoot(&systemRoot); - newFileName = PhCreateStringEx(NULL, systemRoot.Length + FileName->Length - 11 * 2); - memcpy(newFileName->Buffer, systemRoot.Buffer, systemRoot.Length); - memcpy((PCHAR)newFileName->Buffer + systemRoot.Length, &FileName->Buffer[11], FileName->Length - 11 * 2); - } - // "system32\" means "C:\Windows\system32\". - else if (PhStartsWithString2(FileName, L"system32\\", TRUE)) - { - PH_STRINGREF systemRoot; - - PhGetSystemRoot(&systemRoot); - newFileName = PhCreateStringEx(NULL, systemRoot.Length + 2 + FileName->Length); - memcpy(newFileName->Buffer, systemRoot.Buffer, systemRoot.Length); - newFileName->Buffer[systemRoot.Length / 2] = '\\'; - memcpy((PCHAR)newFileName->Buffer + systemRoot.Length + 2, FileName->Buffer, FileName->Length); - } - else if (FileName->Length != 0 && FileName->Buffer[0] == '\\') - { - PPH_STRING resolvedName; - - resolvedName = PhResolveDevicePrefix(FileName); - - if (resolvedName) - { - newFileName = resolvedName; - } - else - { - // We didn't find a match. - // If the file name starts with "\Windows", prepend the system drive. - if (PhStartsWithString2(newFileName, L"\\Windows", TRUE)) - { - newFileName = PhCreateStringEx(NULL, FileName->Length + 2 * 2); - newFileName->Buffer[0] = USER_SHARED_DATA->NtSystemRoot[0]; - newFileName->Buffer[1] = ':'; - memcpy(&newFileName->Buffer[2], FileName->Buffer, FileName->Length); - } - else - { - PhReferenceObject(newFileName); - } - } - } - else - { - // Just return the supplied file name. Note that we need to add a reference. - PhReferenceObject(newFileName); - } - - return newFileName; -} - -typedef struct _ENUM_GENERIC_PROCESS_MODULES_CONTEXT -{ - PPH_ENUM_GENERIC_MODULES_CALLBACK Callback; - PVOID Context; - ULONG Type; - PPH_HASHTABLE BaseAddressHashtable; - - ULONG LoadOrderIndex; -} ENUM_GENERIC_PROCESS_MODULES_CONTEXT, *PENUM_GENERIC_PROCESS_MODULES_CONTEXT; - -static BOOLEAN EnumGenericProcessModulesCallback( - _In_ PLDR_DATA_TABLE_ENTRY Module, - _In_opt_ PVOID Context - ) -{ - PENUM_GENERIC_PROCESS_MODULES_CONTEXT context; - PH_MODULE_INFO moduleInfo; - PPH_STRING fileName; - BOOLEAN cont; - - context = (PENUM_GENERIC_PROCESS_MODULES_CONTEXT)Context; - - // Check if we have a duplicate base address. - if (PhFindEntryHashtable(context->BaseAddressHashtable, &Module->DllBase)) - { - return TRUE; - } - else - { - PhAddEntryHashtable(context->BaseAddressHashtable, &Module->DllBase); - } - - fileName = PhCreateStringFromUnicodeString(&Module->FullDllName); - - moduleInfo.Type = context->Type; - moduleInfo.BaseAddress = Module->DllBase; - moduleInfo.Size = Module->SizeOfImage; - moduleInfo.EntryPoint = Module->EntryPoint; - moduleInfo.Flags = Module->Flags; - moduleInfo.Name = PhCreateStringFromUnicodeString(&Module->BaseDllName); - moduleInfo.FileName = PhGetFileName(fileName); - moduleInfo.LoadOrderIndex = (USHORT)(context->LoadOrderIndex++); - moduleInfo.LoadCount = Module->ObsoleteLoadCount; - - if (WindowsVersion >= WINDOWS_8) - { - moduleInfo.LoadReason = (USHORT)Module->LoadReason; - moduleInfo.LoadTime = Module->LoadTime; - } - else - { - moduleInfo.LoadReason = -1; - moduleInfo.LoadTime.QuadPart = 0; - } - - PhDereferenceObject(fileName); - - cont = context->Callback(&moduleInfo, context->Context); - - PhDereferenceObject(moduleInfo.Name); - PhDereferenceObject(moduleInfo.FileName); - - return cont; -} - -VOID PhpRtlModulesToGenericModules( - _In_ PRTL_PROCESS_MODULES Modules, - _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context, - _In_ PPH_HASHTABLE BaseAddressHashtable - ) -{ - PRTL_PROCESS_MODULE_INFORMATION module; - ULONG i; - PH_MODULE_INFO moduleInfo; - BOOLEAN cont; - - for (i = 0; i < Modules->NumberOfModules; i++) - { - PPH_STRING fileName; - - module = &Modules->Modules[i]; - - // Check if we have a duplicate base address. - if (PhFindEntryHashtable(BaseAddressHashtable, &module->ImageBase)) - { - continue; - } - else - { - PhAddEntryHashtable(BaseAddressHashtable, &module->ImageBase); - } - - fileName = PhConvertMultiByteToUtf16(module->FullPathName); - - if ((ULONG_PTR)module->ImageBase <= PhSystemBasicInformation.MaximumUserModeAddress) - moduleInfo.Type = PH_MODULE_TYPE_MODULE; - else - moduleInfo.Type = PH_MODULE_TYPE_KERNEL_MODULE; - - moduleInfo.BaseAddress = module->ImageBase; - moduleInfo.Size = module->ImageSize; - moduleInfo.EntryPoint = NULL; - moduleInfo.Flags = module->Flags; - moduleInfo.Name = PhConvertMultiByteToUtf16(&module->FullPathName[module->OffsetToFileName]); - moduleInfo.FileName = PhGetFileName(fileName); // convert to DOS file name - moduleInfo.LoadOrderIndex = module->LoadOrderIndex; - moduleInfo.LoadCount = module->LoadCount; - moduleInfo.LoadReason = -1; - moduleInfo.LoadTime.QuadPart = 0; - - PhDereferenceObject(fileName); - - if (module->OffsetToFileName == 0) - { - static PH_STRINGREF driversString = PH_STRINGREF_INIT(L"\\System32\\Drivers\\"); - PH_STRINGREF systemRoot; - PPH_STRING newFileName; - - // We only have the file name, without a path. The driver must be in the default drivers - // directory. - PhGetSystemRoot(&systemRoot); - newFileName = PhConcatStringRef3(&systemRoot, &driversString, &moduleInfo.Name->sr); - PhDereferenceObject(moduleInfo.FileName); - moduleInfo.FileName = newFileName; - } - - cont = Callback(&moduleInfo, Context); - - PhDereferenceObject(moduleInfo.Name); - PhDereferenceObject(moduleInfo.FileName); - - if (!cont) - break; - } -} - -VOID PhpRtlModulesExToGenericModules( - _In_ PRTL_PROCESS_MODULE_INFORMATION_EX Modules, - _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context, - _In_ PPH_HASHTABLE BaseAddressHashtable - ) -{ - PRTL_PROCESS_MODULE_INFORMATION_EX module; - PH_MODULE_INFO moduleInfo; - BOOLEAN cont; - - module = Modules; - - while (module->NextOffset != 0) - { - PPH_STRING fileName; - - // Check if we have a duplicate base address. - if (PhFindEntryHashtable(BaseAddressHashtable, &module->BaseInfo.ImageBase)) - { - continue; - } - else - { - PhAddEntryHashtable(BaseAddressHashtable, &module->BaseInfo.ImageBase); - } - - fileName = PhConvertMultiByteToUtf16(module->BaseInfo.FullPathName); - - if ((ULONG_PTR)module->BaseInfo.ImageBase <= PhSystemBasicInformation.MaximumUserModeAddress) - moduleInfo.Type = PH_MODULE_TYPE_MODULE; - else - moduleInfo.Type = PH_MODULE_TYPE_KERNEL_MODULE; - - moduleInfo.BaseAddress = module->BaseInfo.ImageBase; - moduleInfo.Size = module->BaseInfo.ImageSize; - moduleInfo.EntryPoint = NULL; - moduleInfo.Flags = module->BaseInfo.Flags; - moduleInfo.Name = PhConvertMultiByteToUtf16(&module->BaseInfo.FullPathName[module->BaseInfo.OffsetToFileName]); - moduleInfo.FileName = PhGetFileName(fileName); // convert to DOS file name - moduleInfo.LoadOrderIndex = module->BaseInfo.LoadOrderIndex; - moduleInfo.LoadCount = module->BaseInfo.LoadCount; - moduleInfo.LoadReason = -1; - moduleInfo.LoadTime.QuadPart = 0; - - PhDereferenceObject(fileName); - - cont = Callback(&moduleInfo, Context); - - PhDereferenceObject(moduleInfo.Name); - PhDereferenceObject(moduleInfo.FileName); - - if (!cont) - break; - - module = PTR_ADD_OFFSET(module, module->NextOffset); - } -} - -BOOLEAN PhpCallbackMappedFileOrImage( - _In_ PVOID AllocationBase, - _In_ SIZE_T AllocationSize, - _In_ ULONG Type, - _In_ PPH_STRING FileName, - _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context, - _In_ PPH_HASHTABLE BaseAddressHashtable - ) -{ - PH_MODULE_INFO moduleInfo; - BOOLEAN cont; - - moduleInfo.Type = Type; - moduleInfo.BaseAddress = AllocationBase; - moduleInfo.Size = (ULONG)AllocationSize; - moduleInfo.EntryPoint = NULL; - moduleInfo.Flags = 0; - moduleInfo.FileName = PhGetFileName(FileName); - moduleInfo.Name = PhGetBaseName(moduleInfo.FileName); - moduleInfo.LoadOrderIndex = -1; - moduleInfo.LoadCount = -1; - moduleInfo.LoadReason = -1; - moduleInfo.LoadTime.QuadPart = 0; - - cont = Callback(&moduleInfo, Context); - - PhDereferenceObject(moduleInfo.FileName); - PhDereferenceObject(moduleInfo.Name); - - return cont; -} - -VOID PhpEnumGenericMappedFilesAndImages( - _In_ HANDLE ProcessHandle, - _In_ ULONG Flags, - _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context, - _In_ PPH_HASHTABLE BaseAddressHashtable - ) -{ - BOOLEAN querySucceeded; - PVOID baseAddress; - MEMORY_BASIC_INFORMATION basicInfo; - - baseAddress = (PVOID)0; - - if (!NT_SUCCESS(NtQueryVirtualMemory( - ProcessHandle, - baseAddress, - MemoryBasicInformation, - &basicInfo, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - { - return; - } - - querySucceeded = TRUE; - - while (querySucceeded) - { - PVOID allocationBase; - SIZE_T allocationSize; - ULONG type; - PPH_STRING fileName; - BOOLEAN cont; - - if (basicInfo.Type == MEM_MAPPED || basicInfo.Type == MEM_IMAGE) - { - if (basicInfo.Type == MEM_MAPPED) - type = PH_MODULE_TYPE_MAPPED_FILE; - else - type = PH_MODULE_TYPE_MAPPED_IMAGE; - - // Find the total allocation size. - - allocationBase = basicInfo.AllocationBase; - allocationSize = 0; - - do - { - baseAddress = (PVOID)((ULONG_PTR)baseAddress + basicInfo.RegionSize); - allocationSize += basicInfo.RegionSize; - - if (!NT_SUCCESS(NtQueryVirtualMemory( - ProcessHandle, - baseAddress, - MemoryBasicInformation, - &basicInfo, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - { - querySucceeded = FALSE; - break; - } - } while (basicInfo.AllocationBase == allocationBase); - - if ((type == PH_MODULE_TYPE_MAPPED_FILE && !(Flags & PH_ENUM_GENERIC_MAPPED_FILES)) || - (type == PH_MODULE_TYPE_MAPPED_IMAGE && !(Flags & PH_ENUM_GENERIC_MAPPED_IMAGES))) - { - // The user doesn't want this type of entry. - continue; - } - - // Check if we have a duplicate base address. - if (PhFindEntryHashtable(BaseAddressHashtable, &allocationBase)) - { - continue; - } - - if (!NT_SUCCESS(PhGetProcessMappedFileName( - ProcessHandle, - allocationBase, - &fileName - ))) - { - continue; - } - - PhAddEntryHashtable(BaseAddressHashtable, &allocationBase); - - cont = PhpCallbackMappedFileOrImage( - allocationBase, - allocationSize, - type, - fileName, - Callback, - Context, - BaseAddressHashtable - ); - - PhDereferenceObject(fileName); - - if (!cont) - break; - } - else - { - baseAddress = (PVOID)((ULONG_PTR)baseAddress + basicInfo.RegionSize); - - if (!NT_SUCCESS(NtQueryVirtualMemory( - ProcessHandle, - baseAddress, - MemoryBasicInformation, - &basicInfo, - sizeof(MEMORY_BASIC_INFORMATION), - NULL - ))) - { - querySucceeded = FALSE; - } - } - } -} - -BOOLEAN NTAPI PhpBaseAddressHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - return *(PVOID *)Entry1 == *(PVOID *)Entry2; -} - -ULONG NTAPI PhpBaseAddressHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashIntPtr((ULONG_PTR)*(PVOID *)Entry); -} - -/** - * Enumerates the modules loaded by a process. - * - * \param ProcessId The ID of a process. If \ref SYSTEM_PROCESS_ID is specified the function - * enumerates the kernel modules. - * \param ProcessHandle A handle to the process. - * \param Flags Flags controlling the information to retrieve. - * \li \c PH_ENUM_GENERIC_MAPPED_FILES Enumerate mapped files. - * \li \c PH_ENUM_GENERIC_MAPPED_IMAGES Enumerate mapped images (those which are not mapped by the - * loader). - * \param Callback A callback function which is executed for each module. - * \param Context A user-defined value to pass to the callback function. - */ -NTSTATUS PhEnumGenericModules( - _In_ HANDLE ProcessId, - _In_opt_ HANDLE ProcessHandle, - _In_ ULONG Flags, - _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PPH_HASHTABLE baseAddressHashtable; - - baseAddressHashtable = PhCreateHashtable( - sizeof(PVOID), - PhpBaseAddressHashtableEqualFunction, - PhpBaseAddressHashtableHashFunction, - 32 - ); - - if (ProcessId == SYSTEM_PROCESS_ID) - { - // Kernel modules - - PVOID modules; - - if (NT_SUCCESS(status = PhEnumKernelModules((PRTL_PROCESS_MODULES *)&modules))) - { - PhpRtlModulesToGenericModules( - modules, - Callback, - Context, - baseAddressHashtable - ); - PhFree(modules); - } - } - else - { - // Process modules - - BOOLEAN opened = FALSE; -#ifdef _WIN64 - BOOLEAN isWow64 = FALSE; -#endif - ENUM_GENERIC_PROCESS_MODULES_CONTEXT context; - PH_ENUM_PROCESS_MODULES_PARAMETERS parameters; - - if (!ProcessHandle) - { - if (!NT_SUCCESS(status = PhOpenProcess( - &ProcessHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, // needed for enumerating mapped files - ProcessId - ))) - { - if (!NT_SUCCESS(status = PhOpenProcess( - &ProcessHandle, - ProcessQueryAccess | PROCESS_VM_READ, - ProcessId - ))) - { - goto CleanupExit; - } - } - - opened = TRUE; - } - - context.Callback = Callback; - context.Context = Context; - context.Type = PH_MODULE_TYPE_MODULE; - context.BaseAddressHashtable = baseAddressHashtable; - context.LoadOrderIndex = 0; - - parameters.Callback = EnumGenericProcessModulesCallback; - parameters.Context = &context; - parameters.Flags = PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME; - - status = PhEnumProcessModulesEx( - ProcessHandle, - ¶meters - ); - -#ifdef _WIN64 - PhGetProcessIsWow64(ProcessHandle, &isWow64); - - // 32-bit process modules - if (isWow64) - { - context.Callback = Callback; - context.Context = Context; - context.Type = PH_MODULE_TYPE_WOW64_MODULE; - context.BaseAddressHashtable = baseAddressHashtable; - context.LoadOrderIndex = 0; - - status = PhEnumProcessModules32Ex( - ProcessHandle, - ¶meters - ); - } -#endif - - // Mapped files and mapped images - // This is done last because it provides the least amount of information. - - if (Flags & (PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES)) - { - PhpEnumGenericMappedFilesAndImages( - ProcessHandle, - Flags, - Callback, - Context, - baseAddressHashtable - ); - } - - if (opened) - NtClose(ProcessHandle); - } - -CleanupExit: - PhDereferenceObject(baseAddressHashtable); - - return status; -} - -/** - * Initializes usage of predefined keys. - */ -VOID PhpInitializePredefineKeys( - VOID - ) -{ - static UNICODE_STRING currentUserPrefix = RTL_CONSTANT_STRING(L"\\Registry\\User\\"); - - NTSTATUS status; - HANDLE tokenHandle; - PTOKEN_USER tokenUser; - UNICODE_STRING stringSid; - WCHAR stringSidBuffer[MAX_UNICODE_STACK_BUFFER_LENGTH]; - PUNICODE_STRING currentUserKeyName; - - // Get the string SID of the current user. - if (NT_SUCCESS(status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &tokenHandle))) - { - if (NT_SUCCESS(status = PhGetTokenUser(tokenHandle, &tokenUser))) - { - stringSid.Buffer = stringSidBuffer; - stringSid.MaximumLength = sizeof(stringSidBuffer); - - status = RtlConvertSidToUnicodeString( - &stringSid, - tokenUser->User.Sid, - FALSE - ); - - PhFree(tokenUser); - } - - NtClose(tokenHandle); - } - - // Construct the current user key name. - if (NT_SUCCESS(status)) - { - currentUserKeyName = &PhPredefineKeyNames[PH_KEY_CURRENT_USER_NUMBER]; - currentUserKeyName->Length = currentUserPrefix.Length + stringSid.Length; - currentUserKeyName->Buffer = PhAllocate(currentUserKeyName->Length + sizeof(WCHAR)); - memcpy(currentUserKeyName->Buffer, currentUserPrefix.Buffer, currentUserPrefix.Length); - memcpy(¤tUserKeyName->Buffer[currentUserPrefix.Length / sizeof(WCHAR)], stringSid.Buffer, stringSid.Length); - } -} - -/** - * Initializes the attributes of a key object for creating/opening. - * - * \param RootDirectory A handle to a root key, or one of the predefined keys. See PhCreateKey() for - * details. - * \param ObjectName The path to the key. - * \param Attributes Additional object flags. - * \param ObjectAttributes The OBJECT_ATTRIBUTES structure to initialize. - * \param NeedsClose A variable which receives a handle that must be closed when the create/open - * operation is finished. The variable may be set to NULL if no handle needs to be closed. - */ -NTSTATUS PhpInitializeKeyObjectAttributes( - _In_opt_ HANDLE RootDirectory, - _In_ PUNICODE_STRING ObjectName, - _In_ ULONG Attributes, - _Out_ POBJECT_ATTRIBUTES ObjectAttributes, - _Out_ PHANDLE NeedsClose - ) -{ - NTSTATUS status; - ULONG predefineIndex; - HANDLE predefineHandle; - OBJECT_ATTRIBUTES predefineObjectAttributes; - - InitializeObjectAttributes( - ObjectAttributes, - ObjectName, - Attributes | OBJ_CASE_INSENSITIVE, - RootDirectory, - NULL - ); - - *NeedsClose = NULL; - - if (RootDirectory && PH_KEY_IS_PREDEFINED(RootDirectory)) - { - predefineIndex = PH_KEY_PREDEFINE_TO_NUMBER(RootDirectory); - - if (predefineIndex < PH_KEY_MAXIMUM_PREDEFINE) - { - if (PhBeginInitOnce(&PhPredefineKeyInitOnce)) - { - PhpInitializePredefineKeys(); - PhEndInitOnce(&PhPredefineKeyInitOnce); - } - - predefineHandle = PhPredefineKeyHandles[predefineIndex]; - - if (!predefineHandle) - { - // The predefined key has not been opened yet. Do so now. - - if (!PhPredefineKeyNames[predefineIndex].Buffer) // we may have failed in getting the current user key name - return STATUS_UNSUCCESSFUL; - - InitializeObjectAttributes( - &predefineObjectAttributes, - &PhPredefineKeyNames[predefineIndex], - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - status = NtOpenKey( - &predefineHandle, - KEY_READ, - &predefineObjectAttributes - ); - - if (!NT_SUCCESS(status)) - return status; - - if (_InterlockedCompareExchangePointer( - &PhPredefineKeyHandles[predefineIndex], - predefineHandle, - NULL - ) != NULL) - { - // Someone else already opened the key and cached it. Indicate that the caller - // needs to close the handle later, since it isn't shared. - *NeedsClose = predefineHandle; - } - } - - ObjectAttributes->RootDirectory = predefineHandle; - } - } - - return STATUS_SUCCESS; -} - -/** - * Creates or opens a registry key. - * - * \param KeyHandle A variable which receives a handle to the key. - * \param DesiredAccess The desired access to the key. - * \param RootDirectory A handle to a root key, or one of the following predefined keys: - * \li \c PH_KEY_LOCAL_MACHINE Represents \\Registry\\Machine. - * \li \c PH_KEY_USERS Represents \\Registry\\User. - * \li \c PH_KEY_CLASSES_ROOT Represents \\Registry\\Machine\\Software\\Classes. - * \li \c PH_KEY_CURRENT_USER Represents \\Registry\\User\\[SID of current user]. - * \param ObjectName The path to the key. - * \param Attributes Additional object flags. - * \param CreateOptions The options to apply when creating or opening the key. - * \param Disposition A variable which receives a value indicating whether a new key was created or - * an existing key was opened: - * \li \c REG_CREATED_NEW_KEY A new key was created. - * \li \c REG_OPENED_EXISTING_KEY An existing key was opened. - */ -NTSTATUS PhCreateKey( - _Out_ PHANDLE KeyHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ HANDLE RootDirectory, - _In_ PPH_STRINGREF ObjectName, - _In_ ULONG Attributes, - _In_ ULONG CreateOptions, - _Out_opt_ PULONG Disposition - ) -{ - NTSTATUS status; - UNICODE_STRING objectName; - OBJECT_ATTRIBUTES objectAttributes; - HANDLE needsClose; - - if (!PhStringRefToUnicodeString(ObjectName, &objectName)) - return STATUS_NAME_TOO_LONG; - - if (!NT_SUCCESS(status = PhpInitializeKeyObjectAttributes( - RootDirectory, - &objectName, - Attributes, - &objectAttributes, - &needsClose - ))) - { - return status; - } - - status = NtCreateKey( - KeyHandle, - DesiredAccess, - &objectAttributes, - 0, - NULL, - CreateOptions, - Disposition - ); - - if (needsClose) - NtClose(needsClose); - - return status; -} - -/** - * Opens a registry key. - * - * \param KeyHandle A variable which receives a handle to the key. - * \param DesiredAccess The desired access to the key. - * \param RootDirectory A handle to a root key, or one of the predefined keys. See PhCreateKey() for - * details. - * \param ObjectName The path to the key. - * \param Attributes Additional object flags. - */ -NTSTATUS PhOpenKey( - _Out_ PHANDLE KeyHandle, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ HANDLE RootDirectory, - _In_ PPH_STRINGREF ObjectName, - _In_ ULONG Attributes - ) -{ - NTSTATUS status; - UNICODE_STRING objectName; - OBJECT_ATTRIBUTES objectAttributes; - HANDLE needsClose; - - if (!PhStringRefToUnicodeString(ObjectName, &objectName)) - return STATUS_NAME_TOO_LONG; - - if (!NT_SUCCESS(status = PhpInitializeKeyObjectAttributes( - RootDirectory, - &objectName, - Attributes, - &objectAttributes, - &needsClose - ))) - { - return status; - } - - status = NtOpenKey( - KeyHandle, - DesiredAccess, - &objectAttributes - ); - - if (needsClose) - NtClose(needsClose); - - return status; -} - -/** - * Gets information about a registry key. - * - * \param KeyHandle A handle to the key. - * \param KeyInformationClass The information class to query. - * \param Buffer A variable which receives a pointer to a buffer containing information about the - * registry key. You must free the buffer with PhFree() when you no longer need it. - */ -NTSTATUS PhQueryKey( - _In_ HANDLE KeyHandle, - _In_ KEY_INFORMATION_CLASS KeyInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - PVOID buffer; - ULONG bufferSize; - ULONG attempts = 16; - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - - do - { - status = NtQueryKey( - KeyHandle, - KeyInformationClass, - buffer, - bufferSize, - &bufferSize - ); - - if (NT_SUCCESS(status)) - break; - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - PhFree(buffer); - return status; - } - } while (--attempts); - - *Buffer = buffer; - - return status; -} - -/** - * Gets a registry value of any type. - * - * \param KeyHandle A handle to the key. - * \param ValueName The name of the value. - * \param KeyValueInformationClass The information class to query. - * \param Buffer A variable which receives a pointer to a buffer containing information about the - * registry value. You must free the buffer with PhFree() when you no longer need it. - */ -NTSTATUS PhQueryValueKey( - _In_ HANDLE KeyHandle, - _In_opt_ PPH_STRINGREF ValueName, - _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, - _Out_ PVOID *Buffer - ) -{ - NTSTATUS status; - UNICODE_STRING valueName; - PVOID buffer; - ULONG bufferSize; - ULONG attempts = 16; - - if (ValueName) - { - if (!PhStringRefToUnicodeString(ValueName, &valueName)) - return STATUS_NAME_TOO_LONG; - } - else - { - RtlInitUnicodeString(&valueName, NULL); - } - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - - do - { - status = NtQueryValueKey( - KeyHandle, - &valueName, - KeyValueInformationClass, - buffer, - bufferSize, - &bufferSize - ); - - if (NT_SUCCESS(status)) - break; - - if (status == STATUS_BUFFER_OVERFLOW) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - PhFree(buffer); - return status; - } - } while (--attempts); - - *Buffer = buffer; - - return status; -} - -/** - * Creates or opens a file. - * - * \param FileHandle A variable that receives the file handle. - * \param FileName The Win32 file name. - * \param DesiredAccess The desired access to the file. - * \param FileAttributes File attributes applied if the file is created or overwritten. - * \param ShareAccess The file access granted to other threads. - * \li \c FILE_SHARE_READ Allows other threads to read from the file. - * \li \c FILE_SHARE_WRITE Allows other threads to write to the file. - * \li \c FILE_SHARE_DELETE Allows other threads to delete the file. - * \param CreateDisposition The action to perform if the file does or does not exist. - * \li \c FILE_SUPERSEDE If the file exists, replace it. Otherwise, create the file. - * \li \c FILE_CREATE If the file exists, fail. Otherwise, create the file. - * \li \c FILE_OPEN If the file exists, open it. Otherwise, fail. - * \li \c FILE_OPEN_IF If the file exists, open it. Otherwise, create the file. - * \li \c FILE_OVERWRITE If the file exists, open and overwrite it. Otherwise, fail. - * \li \c FILE_OVERWRITE_IF If the file exists, open and overwrite it. Otherwise, create the file. - * \param CreateOptions The options to apply when the file is opened or created. - */ -NTSTATUS PhCreateFileWin32( - _Out_ PHANDLE FileHandle, - _In_ PWSTR FileName, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ ULONG FileAttributes, - _In_ ULONG ShareAccess, - _In_ ULONG CreateDisposition, - _In_ ULONG CreateOptions - ) -{ - return PhCreateFileWin32Ex( - FileHandle, - FileName, - DesiredAccess, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - NULL - ); -} - -/** - * Creates or opens a file. - * - * \param FileHandle A variable that receives the file handle. - * \param FileName The Win32 file name. - * \param DesiredAccess The desired access to the file. - * \param FileAttributes File attributes applied if the file is created or overwritten. - * \param ShareAccess The file access granted to other threads. - * \li \c FILE_SHARE_READ Allows other threads to read from the file. - * \li \c FILE_SHARE_WRITE Allows other threads to write to the file. - * \li \c FILE_SHARE_DELETE Allows other threads to delete the file. - * \param CreateDisposition The action to perform if the file does or does not exist. - * \li \c FILE_SUPERSEDE If the file exists, replace it. Otherwise, create the file. - * \li \c FILE_CREATE If the file exists, fail. Otherwise, create the file. - * \li \c FILE_OPEN If the file exists, open it. Otherwise, fail. - * \li \c FILE_OPEN_IF If the file exists, open it. Otherwise, create the file. - * \li \c FILE_OVERWRITE If the file exists, open and overwrite it. Otherwise, fail. - * \li \c FILE_OVERWRITE_IF If the file exists, open and overwrite it. Otherwise, create the file. - * \param CreateOptions The options to apply when the file is opened or created. - * \param CreateStatus A variable that receives creation information. - * \li \c FILE_SUPERSEDED The file was replaced because \c FILE_SUPERSEDE was specified in - * \a CreateDisposition. - * \li \c FILE_OPENED The file was opened because \c FILE_OPEN or \c FILE_OPEN_IF was specified in - * \a CreateDisposition. - * \li \c FILE_CREATED The file was created because \c FILE_CREATE or \c FILE_OPEN_IF was specified - * in \a CreateDisposition. - * \li \c FILE_OVERWRITTEN The file was overwritten because \c FILE_OVERWRITE or - * \c FILE_OVERWRITE_IF was specified in \a CreateDisposition. - * \li \c FILE_EXISTS The file was not opened because it already existed and \c FILE_CREATE was - * specified in \a CreateDisposition. - * \li \c FILE_DOES_NOT_EXIST The file was not opened because it did not exist and \c FILE_OPEN or - * \c FILE_OVERWRITE was specified in \a CreateDisposition. - */ -NTSTATUS PhCreateFileWin32Ex( - _Out_ PHANDLE FileHandle, - _In_ PWSTR FileName, - _In_ ACCESS_MASK DesiredAccess, - _In_opt_ ULONG FileAttributes, - _In_ ULONG ShareAccess, - _In_ ULONG CreateDisposition, - _In_ ULONG CreateOptions, - _Out_opt_ PULONG CreateStatus - ) -{ - NTSTATUS status; - HANDLE fileHandle; - UNICODE_STRING fileName; - OBJECT_ATTRIBUTES oa; - IO_STATUS_BLOCK isb; - - if (!FileAttributes) - FileAttributes = FILE_ATTRIBUTE_NORMAL; - - if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( - FileName, - &fileName, - NULL, - NULL - ))) - return status; - - InitializeObjectAttributes( - &oa, - &fileName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - status = NtCreateFile( - &fileHandle, - DesiredAccess, - &oa, - &isb, - NULL, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - NULL, - 0 - ); - - RtlFreeUnicodeString(&fileName); - - if (NT_SUCCESS(status)) - { - *FileHandle = fileHandle; - } - - if (CreateStatus) - *CreateStatus = (ULONG)isb.Information; - - return status; -} - -/** - * Queries file attributes. - * - * \param FileName The Win32 file name. - * \param FileInformation A variable that receives the file information. - */ -NTSTATUS PhQueryFullAttributesFileWin32( - _In_ PWSTR FileName, - _Out_ PFILE_NETWORK_OPEN_INFORMATION FileInformation - ) -{ - NTSTATUS status; - UNICODE_STRING fileName; - OBJECT_ATTRIBUTES oa; - - if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( - FileName, - &fileName, - NULL, - NULL - ))) - return status; - - InitializeObjectAttributes( - &oa, - &fileName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - status = NtQueryFullAttributesFile(&oa, FileInformation); - RtlFreeUnicodeString(&fileName); - - return status; -} - -/** - * Deletes a file. - * - * \param FileName The Win32 file name. - */ -NTSTATUS PhDeleteFileWin32( - _In_ PWSTR FileName - ) -{ - NTSTATUS status; - HANDLE fileHandle; - - status = PhCreateFileWin32( - &fileHandle, - FileName, - DELETE, - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_DELETE_ON_CLOSE - ); - - if (!NT_SUCCESS(status)) - return status; - - NtClose(fileHandle); - - return status; -} - -NTSTATUS PhListenNamedPipe( - _In_ HANDLE FileHandle, - _In_opt_ HANDLE Event, - _In_opt_ PIO_APC_ROUTINE ApcRoutine, - _In_opt_ PVOID ApcContext, - _Out_ PIO_STATUS_BLOCK IoStatusBlock - ) -{ - return NtFsControlFile( - FileHandle, - Event, - ApcRoutine, - ApcContext, - IoStatusBlock, - FSCTL_PIPE_LISTEN, - NULL, - 0, - NULL, - 0 - ); -} - -NTSTATUS PhDisconnectNamedPipe( - _In_ HANDLE FileHandle - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK isb; - - status = NtFsControlFile( - FileHandle, - NULL, - NULL, - NULL, - &isb, - FSCTL_PIPE_DISCONNECT, - NULL, - 0, - NULL, - 0 - ); - - if (status == STATUS_PENDING) - { - status = NtWaitForSingleObject(FileHandle, FALSE, NULL); - - if (NT_SUCCESS(status)) - status = isb.Status; - } - - return status; -} - -NTSTATUS PhPeekNamedPipe( - _In_ HANDLE FileHandle, - _Out_writes_bytes_opt_(Length) PVOID Buffer, - _In_ ULONG Length, - _Out_opt_ PULONG NumberOfBytesRead, - _Out_opt_ PULONG NumberOfBytesAvailable, - _Out_opt_ PULONG NumberOfBytesLeftInMessage - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK isb; - PFILE_PIPE_PEEK_BUFFER peekBuffer; - ULONG peekBufferLength; - - peekBufferLength = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data) + Length; - peekBuffer = PhAllocate(peekBufferLength); - - status = NtFsControlFile( - FileHandle, - NULL, - NULL, - NULL, - &isb, - FSCTL_PIPE_PEEK, - NULL, - 0, - peekBuffer, - peekBufferLength - ); - - if (status == STATUS_PENDING) - { - status = NtWaitForSingleObject(FileHandle, FALSE, NULL); - - if (NT_SUCCESS(status)) - status = isb.Status; - } - - // STATUS_BUFFER_OVERFLOW means that there is data remaining; this is normal. - if (status == STATUS_BUFFER_OVERFLOW) - status = STATUS_SUCCESS; - - if (NT_SUCCESS(status)) - { - ULONG numberOfBytesRead; - - if (Buffer || NumberOfBytesRead || NumberOfBytesLeftInMessage) - numberOfBytesRead = (ULONG)(isb.Information - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data)); - - if (Buffer) - memcpy(Buffer, peekBuffer->Data, numberOfBytesRead); - - if (NumberOfBytesRead) - *NumberOfBytesRead = numberOfBytesRead; - - if (NumberOfBytesAvailable) - *NumberOfBytesAvailable = peekBuffer->ReadDataAvailable; - - if (NumberOfBytesLeftInMessage) - *NumberOfBytesLeftInMessage = peekBuffer->MessageLength - numberOfBytesRead; - } - - PhFree(peekBuffer); - - return status; -} - -NTSTATUS PhTransceiveNamedPipe( - _In_ HANDLE FileHandle, - _In_opt_ HANDLE Event, - _In_opt_ PIO_APC_ROUTINE ApcRoutine, - _In_opt_ PVOID ApcContext, - _Out_ PIO_STATUS_BLOCK IoStatusBlock, - _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, - _In_ ULONG InputBufferLength, - _Out_writes_bytes_(OutputBufferLength) PVOID OutputBuffer, - _In_ ULONG OutputBufferLength - ) -{ - return NtFsControlFile( - FileHandle, - Event, - ApcRoutine, - ApcContext, - IoStatusBlock, - FSCTL_PIPE_TRANSCEIVE, - InputBuffer, - InputBufferLength, - OutputBuffer, - OutputBufferLength - ); -} - -NTSTATUS PhWaitForNamedPipe( - _In_opt_ PUNICODE_STRING FileSystemName, - _In_ PUNICODE_STRING Name, - _In_opt_ PLARGE_INTEGER Timeout, - _In_ BOOLEAN UseDefaultTimeout - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK isb; - UNICODE_STRING localNpfsName; - HANDLE fileSystemHandle; - OBJECT_ATTRIBUTES oa; - PFILE_PIPE_WAIT_FOR_BUFFER waitForBuffer; - ULONG waitForBufferLength; - - if (!FileSystemName) - { - RtlInitUnicodeString(&localNpfsName, L"\\Device\\NamedPipe"); - FileSystemName = &localNpfsName; - } - - InitializeObjectAttributes( - &oa, - FileSystemName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - status = NtOpenFile( - &fileSystemHandle, - FILE_READ_ATTRIBUTES | SYNCHRONIZE, - &oa, - &isb, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - return status; - - waitForBufferLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name) + Name->Length; - waitForBuffer = PhAllocate(waitForBufferLength); - - if (UseDefaultTimeout) - { - waitForBuffer->TimeoutSpecified = FALSE; - } - else - { - if (Timeout) - { - waitForBuffer->Timeout = *Timeout; - } - else - { - waitForBuffer->Timeout.LowPart = 0; - waitForBuffer->Timeout.HighPart = MINLONG; // a very long time - } - - waitForBuffer->TimeoutSpecified = TRUE; - } - - waitForBuffer->NameLength = (ULONG)Name->Length; - memcpy(waitForBuffer->Name, Name->Buffer, Name->Length); - - status = NtFsControlFile( - fileSystemHandle, - NULL, - NULL, - NULL, - &isb, - FSCTL_PIPE_WAIT, - waitForBuffer, - waitForBufferLength, - NULL, - 0 - ); - - PhFree(waitForBuffer); - NtClose(fileSystemHandle); - - return status; -} - -NTSTATUS PhImpersonateClientOfNamedPipe( - _In_ HANDLE FileHandle - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK isb; - - status = NtFsControlFile( - FileHandle, - NULL, - NULL, - NULL, - &isb, - FSCTL_PIPE_IMPERSONATE, - NULL, - 0, - NULL, - 0 - ); - - return status; -} +/* + * Process Hacker - + * native wrapper and support functions + * + * Copyright (C) 2009-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include +#include +#include +#include + +#include + +#define PH_DEVICE_PREFIX_LENGTH 64 +#define PH_DEVICE_MUP_PREFIX_MAX_COUNT 16 + +typedef BOOLEAN (NTAPI *PPHP_ENUM_PROCESS_MODULES_CALLBACK)( + _In_ HANDLE ProcessHandle, + _In_ PLDR_DATA_TABLE_ENTRY Entry, + _In_ PVOID AddressOfEntry, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ); + +typedef BOOLEAN (NTAPI *PPHP_ENUM_PROCESS_MODULES32_CALLBACK)( + _In_ HANDLE ProcessHandle, + _In_ PLDR_DATA_TABLE_ENTRY32 Entry, + _In_ ULONG AddressOfEntry, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ); + +static PH_INITONCE PhDevicePrefixesInitOnce = PH_INITONCE_INIT; + +static UNICODE_STRING PhDevicePrefixes[26]; +static PH_QUEUED_LOCK PhDevicePrefixesLock = PH_QUEUED_LOCK_INIT; + +static PPH_STRING PhDeviceMupPrefixes[PH_DEVICE_MUP_PREFIX_MAX_COUNT] = { 0 }; +static ULONG PhDeviceMupPrefixesCount = 0; +static PH_QUEUED_LOCK PhDeviceMupPrefixesLock = PH_QUEUED_LOCK_INIT; + +static PH_INITONCE PhPredefineKeyInitOnce = PH_INITONCE_INIT; +static UNICODE_STRING PhPredefineKeyNames[PH_KEY_MAXIMUM_PREDEFINE] = +{ + RTL_CONSTANT_STRING(L"\\Registry\\Machine"), + RTL_CONSTANT_STRING(L"\\Registry\\User"), + RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Classes"), + { 0, 0, NULL } +}; +static HANDLE PhPredefineKeyHandles[PH_KEY_MAXIMUM_PREDEFINE] = { 0 }; + +/** + * Queries information about the token of the current process. + */ +PH_TOKEN_ATTRIBUTES PhGetOwnTokenAttributes( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PH_TOKEN_ATTRIBUTES attributes = { 0 }; + + if (PhBeginInitOnce(&initOnce)) + { + BOOLEAN elevated = TRUE; + TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeFull; + + if (WindowsVersion >= WINDOWS_8) + { + attributes.TokenHandle = NtCurrentProcessToken(); + } + else + { + HANDLE tokenHandle; + + if (NT_SUCCESS(PhOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &tokenHandle))) + attributes.TokenHandle = tokenHandle; + } + + if (attributes.TokenHandle) + { + PTOKEN_USER tokenUser; + + PhGetTokenIsElevated(attributes.TokenHandle, &elevated); + PhGetTokenElevationType(attributes.TokenHandle, &elevationType); + + if (NT_SUCCESS(PhGetTokenUser(attributes.TokenHandle, &tokenUser))) + { + attributes.TokenSid = PhAllocateCopy(tokenUser->User.Sid, RtlLengthSid(tokenUser->User.Sid)); + PhFree(tokenUser); + } + } + + attributes.Elevated = elevated; + attributes.ElevationType = elevationType; + + PhEndInitOnce(&initOnce); + } + + return attributes; +} + +/** + * Opens a process. + * + * \param ProcessHandle A variable which receives a handle to the process. + * \param DesiredAccess The desired access to the process. + * \param ProcessId The ID of the process. + */ +NTSTATUS PhOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ProcessId + ) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES objectAttributes; + CLIENT_ID clientId; + + clientId.UniqueProcess = ProcessId; + clientId.UniqueThread = NULL; + +#ifdef _DEBUG + if (ProcessId == NtCurrentProcessId()) + { + *ProcessHandle = NtCurrentProcess(); + return STATUS_SUCCESS; + } +#endif + + if (KphIsVerified() && (DesiredAccess & KPH_PROCESS_READ_ACCESS) == DesiredAccess) + { + status = KphOpenProcess( + ProcessHandle, + DesiredAccess, + &clientId + ); + } + else + { + InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); + status = NtOpenProcess( + ProcessHandle, + DesiredAccess, + &objectAttributes, + &clientId + ); + + if (status == STATUS_ACCESS_DENIED && KphIsVerified()) + { + status = KphOpenProcess( + ProcessHandle, + DesiredAccess, + &clientId + ); + } + } + + return status; +} + +/** Limited API for untrusted/external code. */ +NTSTATUS PhOpenProcessPublic( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ProcessId + ) +{ + OBJECT_ATTRIBUTES objectAttributes; + CLIENT_ID clientId; + + InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); + clientId.UniqueProcess = ProcessId; + clientId.UniqueThread = NULL; + + return NtOpenProcess( + ProcessHandle, + DesiredAccess, + &objectAttributes, + &clientId + ); +} + +/** + * Opens a thread. + * + * \param ThreadHandle A variable which receives a handle to the thread. + * \param DesiredAccess The desired access to the thread. + * \param ThreadId The ID of the thread. + */ +NTSTATUS PhOpenThread( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ThreadId + ) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES objectAttributes; + CLIENT_ID clientId; + + clientId.UniqueProcess = NULL; + clientId.UniqueThread = ThreadId; + +#ifdef _DEBUG + if (ThreadId == NtCurrentThreadId()) + { + *ThreadHandle = NtCurrentThread(); + return STATUS_SUCCESS; + } +#endif + + if (KphIsVerified() && (DesiredAccess & KPH_THREAD_READ_ACCESS) == DesiredAccess) + { + status = KphOpenThread( + ThreadHandle, + DesiredAccess, + &clientId + ); + } + else + { + InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); + status = NtOpenThread( + ThreadHandle, + DesiredAccess, + &objectAttributes, + &clientId + ); + + if (status == STATUS_ACCESS_DENIED && KphIsVerified()) + { + status = KphOpenThread( + ThreadHandle, + DesiredAccess, + &clientId + ); + } + } + + return status; +} + +/** Limited API for untrusted/external code. */ +NTSTATUS PhOpenThreadPublic( + _Out_ PHANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ThreadId + ) +{ + OBJECT_ATTRIBUTES objectAttributes; + CLIENT_ID clientId; + + clientId.UniqueProcess = NULL; + clientId.UniqueThread = ThreadId; + + InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); + + return NtOpenThread( + ThreadHandle, + DesiredAccess, + &objectAttributes, + &clientId + ); +} + +NTSTATUS PhOpenThreadProcess( + _In_ HANDLE ThreadHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE ProcessHandle + ) +{ + if (KphIsConnected()) + { + return KphOpenThreadProcess( + ThreadHandle, + DesiredAccess, + ProcessHandle + ); + } + else + { + NTSTATUS status; + THREAD_BASIC_INFORMATION basicInfo; + + if (!NT_SUCCESS(status = PhGetThreadBasicInformation( + ThreadHandle, + &basicInfo + ))) + return status; + + return PhOpenProcess( + ProcessHandle, + DesiredAccess, + basicInfo.ClientId.UniqueProcess + ); + } +} + +/** + * Opens a process token. + * + * \param ProcessHandle A handle to a process. + * \param DesiredAccess The desired access to the token. + * \param TokenHandle A variable which receives a handle to the token. + */ +NTSTATUS PhOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ) +{ + NTSTATUS status; + + if (KphIsVerified() && (DesiredAccess & KPH_TOKEN_READ_ACCESS) == DesiredAccess) + { + status = KphOpenProcessToken( + ProcessHandle, + DesiredAccess, + TokenHandle + ); + } + else + { + status = NtOpenProcessToken( + ProcessHandle, + DesiredAccess, + TokenHandle + ); + + if (status == STATUS_ACCESS_DENIED && KphIsVerified()) + { + status = KphOpenProcessToken( + ProcessHandle, + DesiredAccess, + TokenHandle + ); + } + } + + return status; +} + +/** Limited API for untrusted/external code. */ +NTSTATUS PhOpenProcessTokenPublic( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ) +{ + return NtOpenProcessToken( + ProcessHandle, + DesiredAccess, + TokenHandle + ); +} + +NTSTATUS PhGetObjectSecurity( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor + ) +{ + NTSTATUS status; + ULONG bufferSize; + PVOID buffer; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + // This is required (especially for File objects) because some drivers don't seem to handle + // QuerySecurity properly. + memset(buffer, 0, bufferSize); + + status = NtQuerySecurityObject( + Handle, + SecurityInformation, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_BUFFER_TOO_SMALL) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + memset(buffer, 0, bufferSize); + + status = NtQuerySecurityObject( + Handle, + SecurityInformation, + buffer, + bufferSize, + &bufferSize + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *SecurityDescriptor = (PSECURITY_DESCRIPTOR)buffer; + + return status; +} + +NTSTATUS PhSetObjectSecurity( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + return NtSetSecurityObject( + Handle, + SecurityInformation, + SecurityDescriptor + ); +} + +PPH_STRING PhGetSecurityDescriptorAsString( + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + PPH_STRING securityDescriptorString = NULL; + ULONG stringSecurityDescriptorLength; + PWSTR stringSecurityDescriptor; + + if (!ConvertSecurityDescriptorToStringSecurityDescriptorW_Import()) + return NULL; + + if (ConvertSecurityDescriptorToStringSecurityDescriptorW_Import()( + SecurityDescriptor, + SDDL_REVISION, + SecurityInformation, + &stringSecurityDescriptor, + &stringSecurityDescriptorLength + )) + { + securityDescriptorString = PhCreateStringEx( + stringSecurityDescriptor, + stringSecurityDescriptorLength * sizeof(WCHAR) + ); + LocalFree(stringSecurityDescriptor); + } + + return securityDescriptorString; +} + +BOOLEAN PhGetObjectSecurityDescriptorAsString( + _In_ HANDLE Handle, + _Out_ PPH_STRING* SecurityDescriptorString + ) +{ + PSECURITY_DESCRIPTOR securityDescriptor; + PPH_STRING securityDescriptorString; + + if (NT_SUCCESS(PhGetObjectSecurity( + Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | + ATTRIBUTE_SECURITY_INFORMATION | SCOPE_SECURITY_INFORMATION, + &securityDescriptor + ))) + { + if (securityDescriptorString = PhGetSecurityDescriptorAsString( + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | + ATTRIBUTE_SECURITY_INFORMATION | SCOPE_SECURITY_INFORMATION, + securityDescriptor + )) + { + *SecurityDescriptorString = securityDescriptorString; + + PhFree(securityDescriptor); + return TRUE; + } + + PhFree(securityDescriptor); + } + + return FALSE; +} + + +/** + * Terminates a process. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_TERMINATE access. + * \param ExitStatus A status value that indicates why the process is being terminated. + */ +NTSTATUS PhTerminateProcess( + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ) +{ + NTSTATUS status; + + if (KphIsVerified()) + { + status = KphTerminateProcess( + ProcessHandle, + ExitStatus + ); + + if (status != STATUS_NOT_SUPPORTED) + return status; + } + + return NtTerminateProcess( + ProcessHandle, + ExitStatus + ); +} + +/** Limited API for untrusted/external code. */ +NTSTATUS PhTerminateProcessPublic( + _In_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ) +{ + return NtTerminateProcess( + ProcessHandle, + ExitStatus + ); +} + +/** + * Queries variable-sized information for a process. The function allocates a buffer to contain the + * information. + * + * \param ProcessHandle A handle to a process. The access required depends on the information class + * specified. + * \param ProcessInformationClass The information class to retrieve. + * \param Buffer A variable which receives a pointer to a buffer containing the information. You + * must free the buffer using PhFree() when you no longer need it. + */ +NTSTATUS PhpQueryProcessVariableSize( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG returnLength = 0; + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessInformationClass, + NULL, + 0, + &returnLength + ); + + if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL && status != STATUS_INFO_LENGTH_MISMATCH) + return status; + + buffer = PhAllocate(returnLength); + status = NtQueryInformationProcess( + ProcessHandle, + ProcessInformationClass, + buffer, + returnLength, + &returnLength + ); + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +/** + * Gets the file name of the process' image. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION access. + * \param FileName A variable which receives a pointer to a string containing the file name. You + * must free the string using PhDereferenceObject() when you no longer need it. + */ +NTSTATUS PhGetProcessImageFileName( + _In_ HANDLE ProcessHandle, + _Out_ PPH_STRING *FileName + ) +{ + NTSTATUS status; + PUNICODE_STRING fileName; + + status = PhpQueryProcessVariableSize( + ProcessHandle, + ProcessImageFileName, + &fileName + ); + + if (!NT_SUCCESS(status)) + return status; + + *FileName = PhCreateStringFromUnicodeString(fileName); + PhFree(fileName); + + return status; +} + +/** + * Gets the Win32 file name of the process' image. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION access. + * \param FileName A variable which receives a pointer to a string containing the file name. You + * must free the string using PhDereferenceObject() when you no longer need it. + * + * \remarks This function is only available on Windows Vista and above. + */ +NTSTATUS PhGetProcessImageFileNameWin32( + _In_ HANDLE ProcessHandle, + _Out_ PPH_STRING *FileName + ) +{ + NTSTATUS status; + PUNICODE_STRING fileName; + + status = PhpQueryProcessVariableSize( + ProcessHandle, + ProcessImageFileNameWin32, + &fileName + ); + + if (!NT_SUCCESS(status)) + return status; + + *FileName = PhCreateStringFromUnicodeString(fileName); + PhFree(fileName); + + return status; +} + +/** + * Gets whether a process is being debugged. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION + * access. + * \param IsBeingDebugged A variable which receives a boolean indicating whether the process is + * being debugged. + */ +NTSTATUS PhGetProcessIsBeingDebugged( + _In_ HANDLE ProcessHandle, + _Out_ PBOOLEAN IsBeingDebugged + ) +{ + NTSTATUS status; + + // NOTE: The ProcessDebugObjectHandle is always valid when the process is being debugged while the ProcessDebugPort + // is only set when the process was created with DEBUG_PROCESS flag by CsrCreateProcess. (dmex) + if (KphIsVerified()) + { + HANDLE debugHandle; + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessDebugObjectHandle, + &debugHandle, + sizeof(HANDLE), + NULL + ); + + if (NT_SUCCESS(status)) + { + *IsBeingDebugged = TRUE; + NtClose(debugHandle); + } + else if (status == STATUS_ACCESS_DENIED) + { + *IsBeingDebugged = TRUE; + status = STATUS_SUCCESS; + } + } + else + { + PVOID debugPort; + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessDebugPort, + &debugPort, + sizeof(PVOID), + NULL + ); + + if (NT_SUCCESS(status)) + { + *IsBeingDebugged = !!debugPort; + } + } + + return status; +} + +/** + * Gets a string stored in a process' parameters structure. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. + * \param Offset The string to retrieve. + * \param String A variable which receives a pointer to the requested string. You must free the + * string using PhDereferenceObject() when you no longer need it. + * + * \retval STATUS_INVALID_PARAMETER_2 An invalid value was specified in the Offset parameter. + */ +NTSTATUS PhGetProcessPebString( + _In_ HANDLE ProcessHandle, + _In_ PH_PEB_OFFSET Offset, + _Out_ PPH_STRING *String + ) +{ + NTSTATUS status; + PPH_STRING string; + ULONG offset; + +#define PEB_OFFSET_CASE(Enum, Field) \ + case Enum: offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Field); break; \ + case Enum | PhpoWow64: offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS32, Field); break + + switch (Offset) + { + PEB_OFFSET_CASE(PhpoCurrentDirectory, CurrentDirectory); + PEB_OFFSET_CASE(PhpoDllPath, DllPath); + PEB_OFFSET_CASE(PhpoImagePathName, ImagePathName); + PEB_OFFSET_CASE(PhpoCommandLine, CommandLine); + PEB_OFFSET_CASE(PhpoWindowTitle, WindowTitle); + PEB_OFFSET_CASE(PhpoDesktopInfo, DesktopInfo); + PEB_OFFSET_CASE(PhpoShellInfo, ShellInfo); + PEB_OFFSET_CASE(PhpoRuntimeData, RuntimeData); + default: + return STATUS_INVALID_PARAMETER_2; + } + + if (!(Offset & PhpoWow64)) + { + PROCESS_BASIC_INFORMATION basicInfo; + PVOID processParameters; + UNICODE_STRING unicodeString; + + // Get the PEB address. + if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo))) + return status; + + // Read the address of the process parameters. + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)), + &processParameters, + sizeof(PVOID), + NULL + ))) + return status; + + // Read the string structure. + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(processParameters, offset), + &unicodeString, + sizeof(UNICODE_STRING), + NULL + ))) + return status; + + if (unicodeString.Length == 0) + { + *String = PhReferenceEmptyString(); + return status; + } + + string = PhCreateStringEx(NULL, unicodeString.Length); + + // Read the string contents. + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + unicodeString.Buffer, + string->Buffer, + string->Length, + NULL + ))) + { + PhDereferenceObject(string); + return status; + } + } + else + { + PVOID peb32; + ULONG processParameters32; + UNICODE_STRING32 unicodeString32; + + if (!NT_SUCCESS(status = PhGetProcessPeb32(ProcessHandle, &peb32))) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessParameters)), + &processParameters32, + sizeof(ULONG), + NULL + ))) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(processParameters32, offset), + &unicodeString32, + sizeof(UNICODE_STRING32), + NULL + ))) + return status; + + if (unicodeString32.Length == 0) + { + *String = PhReferenceEmptyString(); + return status; + } + + string = PhCreateStringEx(NULL, unicodeString32.Length); + + // Read the string contents. + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + UlongToPtr(unicodeString32.Buffer), + string->Buffer, + string->Length, + NULL + ))) + { + PhDereferenceObject(string); + return status; + } + } + + *String = string; + + return status; +} + +/** + * Gets a process' command line. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION. Before Windows 8.1, the handle must also have PROCESS_VM_READ + * access. + * \param String A variable which receives a pointer to a string containing the command line. You + * must free the string using PhDereferenceObject() when you no longer need it. + */ +NTSTATUS PhGetProcessCommandLine( + _In_ HANDLE ProcessHandle, + _Out_ PPH_STRING *CommandLine + ) +{ + if (WindowsVersion >= WINDOWS_8_1) + { + NTSTATUS status; + PUNICODE_STRING commandLine; + + status = PhpQueryProcessVariableSize( + ProcessHandle, + ProcessCommandLineInformation, + &commandLine + ); + + if (NT_SUCCESS(status)) + { + *CommandLine = PhCreateStringFromUnicodeString(commandLine); + PhFree(commandLine); + + return status; + } + } + + return PhGetProcessPebString(ProcessHandle, PhpoCommandLine, CommandLine); +} + +NTSTATUS PhGetProcessDesktopInfo( + _In_ HANDLE ProcessHandle, + _Out_ PPH_STRING *DesktopInfo + ) +{ + return PhGetProcessPebString(ProcessHandle, PhpoDesktopInfo, DesktopInfo); +} + +/** + * Gets the window flags and window title of a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION. Before Windows 7 SP1, the handle must also have + * PROCESS_VM_READ access. + * \param WindowFlags A variable which receives the window flags. + * \param WindowTitle A variable which receives a pointer to the window title. You must free the + * string using PhDereferenceObject() when you no longer need it. + */ +NTSTATUS PhGetProcessWindowTitle( + _In_ HANDLE ProcessHandle, + _Out_ PULONG WindowFlags, + _Out_ PPH_STRING *WindowTitle + ) +{ + NTSTATUS status; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; +#endif + ULONG windowFlags; + PPROCESS_WINDOW_INFORMATION windowInfo; + + status = PhpQueryProcessVariableSize( + ProcessHandle, + ProcessWindowInformation, + &windowInfo + ); + + if (NT_SUCCESS(status)) + { + *WindowFlags = windowInfo->WindowFlags; + *WindowTitle = PhCreateStringEx(windowInfo->WindowTitle, windowInfo->WindowTitleLength); + PhFree(windowInfo); + + return status; + } + +#ifdef _WIN64 + PhGetProcessIsWow64(ProcessHandle, &isWow64); + + if (!isWow64) +#endif + { + PROCESS_BASIC_INFORMATION basicInfo; + PVOID processParameters; + + // Get the PEB address. + if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo))) + return status; + + // Read the address of the process parameters. + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)), + &processParameters, + sizeof(PVOID), + NULL + ))) + return status; + + // Read the window flags. + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(processParameters, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, WindowFlags)), + &windowFlags, + sizeof(ULONG), + NULL + ))) + return status; + } +#ifdef _WIN64 + else + { + PVOID peb32; + ULONG processParameters32; + + if (!NT_SUCCESS(status = PhGetProcessPeb32(ProcessHandle, &peb32))) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessParameters)), + &processParameters32, + sizeof(ULONG), + NULL + ))) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(processParameters32, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS32, WindowFlags)), + &windowFlags, + sizeof(ULONG), + NULL + ))) + return status; + } +#endif + +#ifdef _WIN64 + status = PhGetProcessPebString(ProcessHandle, PhpoWindowTitle | (isWow64 ? PhpoWow64 : 0), WindowTitle); +#else + status = PhGetProcessPebString(ProcessHandle, PhpoWindowTitle, WindowTitle); +#endif + + if (NT_SUCCESS(status)) + *WindowFlags = windowFlags; + + return status; +} + +NTSTATUS PhGetProcessDepStatus( + _In_ HANDLE ProcessHandle, + _Out_ PULONG DepStatus + ) +{ + NTSTATUS status; + ULONG executeFlags; + ULONG depStatus; + + if (!NT_SUCCESS(status = PhGetProcessExecuteFlags( + ProcessHandle, + &executeFlags + ))) + return status; + + // Check if execution of data pages is enabled. + if (executeFlags & MEM_EXECUTE_OPTION_ENABLE) + depStatus = 0; + else + depStatus = PH_PROCESS_DEP_ENABLED; + + if (executeFlags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) + depStatus |= PH_PROCESS_DEP_ATL_THUNK_EMULATION_DISABLED; + if (executeFlags & MEM_EXECUTE_OPTION_PERMANENT) + depStatus |= PH_PROCESS_DEP_PERMANENT; + + *DepStatus = depStatus; + + return status; +} + +/** + * Gets a process' environment block. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION and + * PROCESS_VM_READ access. + * \param Flags A combination of flags. + * \li \c PH_GET_PROCESS_ENVIRONMENT_WOW64 Retrieve the environment block from the WOW64 PEB. + * \param Environment A variable which will receive a pointer to the environment block copied from + * the process. You must free the block using PhFreePage() when you no longer need it. + * \param EnvironmentLength A variable which will receive the length of the environment block, in + * bytes. + */ +NTSTATUS PhGetProcessEnvironment( + _In_ HANDLE ProcessHandle, + _In_ ULONG Flags, + _Out_ PVOID *Environment, + _Out_ PULONG EnvironmentLength + ) +{ + NTSTATUS status; + PVOID environmentRemote; + MEMORY_BASIC_INFORMATION mbi; + PVOID environment; + ULONG environmentLength; + + if (!(Flags & PH_GET_PROCESS_ENVIRONMENT_WOW64)) + { + PROCESS_BASIC_INFORMATION basicInfo; + PVOID processParameters; + + status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo); + + if (!NT_SUCCESS(status)) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)), + &processParameters, + sizeof(PVOID), + NULL + ))) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(processParameters, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment)), + &environmentRemote, + sizeof(PVOID), + NULL + ))) + return status; + } + else + { + PVOID peb32; + ULONG processParameters32; + ULONG environmentRemote32; + + status = PhGetProcessPeb32(ProcessHandle, &peb32); + + if (!NT_SUCCESS(status)) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, ProcessParameters)), + &processParameters32, + sizeof(ULONG), + NULL + ))) + return status; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(processParameters32, FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS32, Environment)), + &environmentRemote32, + sizeof(ULONG), + NULL + ))) + return status; + + environmentRemote = UlongToPtr(environmentRemote32); + } + + if (!NT_SUCCESS(status = NtQueryVirtualMemory( + ProcessHandle, + environmentRemote, + MemoryBasicInformation, + &mbi, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + return status; + + environmentLength = (ULONG)(mbi.RegionSize - + ((ULONG_PTR)environmentRemote - (ULONG_PTR)mbi.BaseAddress)); + + // Read in the entire region of memory. + + environment = PhAllocatePage(environmentLength, NULL); + + if (!environment) + return STATUS_NO_MEMORY; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + environmentRemote, + environment, + environmentLength, + NULL + ))) + { + PhFreePage(environment); + return status; + } + + *Environment = environment; + + if (EnvironmentLength) + *EnvironmentLength = environmentLength; + + return status; +} + +BOOLEAN PhEnumProcessEnvironmentVariables( + _In_ PVOID Environment, + _In_ ULONG EnvironmentLength, + _Inout_ PULONG EnumerationKey, + _Out_ PPH_ENVIRONMENT_VARIABLE Variable + ) +{ + ULONG length; + ULONG startIndex; + PWCHAR name; + ULONG nameLength; + PWCHAR value; + ULONG valueLength; + PWCHAR currentChar; + ULONG currentIndex; + + length = EnvironmentLength / sizeof(WCHAR); + + currentIndex = *EnumerationKey; + currentChar = (PWCHAR)Environment + currentIndex; + startIndex = currentIndex; + name = currentChar; + + // Find the end of the name. + while (TRUE) + { + if (currentIndex >= length) + return FALSE; + if ((*currentChar == '=') && (startIndex != currentIndex)) + break; // equality sign is considered as a delimiter unless it is the first character (diversenok) + if (*currentChar == 0) + return FALSE; // no more variables + + currentIndex++; + currentChar++; + } + + nameLength = currentIndex - startIndex; + + currentIndex++; + currentChar++; + startIndex = currentIndex; + value = currentChar; + + // Find the end of the value. + while (TRUE) + { + if (currentIndex >= length) + return FALSE; + if (*currentChar == 0) + break; + + currentIndex++; + currentChar++; + } + + valueLength = currentIndex - startIndex; + + currentIndex++; + *EnumerationKey = currentIndex; + + Variable->Name.Buffer = name; + Variable->Name.Length = nameLength * sizeof(WCHAR); + Variable->Value.Buffer = value; + Variable->Value.Length = valueLength * sizeof(WCHAR); + + return TRUE; +} + +NTSTATUS PhQueryEnvironmentVariable( + _In_opt_ PVOID Environment, + _In_ PPH_STRINGREF Name, + _Out_opt_ PPH_STRING* Value + ) +{ + NTSTATUS status; + UNICODE_STRING variableNameUs; + UNICODE_STRING variableValueUs; + + PhStringRefToUnicodeString(Name, &variableNameUs); + + if (Value) + { + variableValueUs.Length = 0x100 * sizeof(WCHAR); + variableValueUs.MaximumLength = variableValueUs.Length + sizeof(UNICODE_NULL); + variableValueUs.Buffer = PhAllocateZero(variableValueUs.MaximumLength); + } + else + { + RtlInitEmptyUnicodeString(&variableValueUs, NULL, 0); + } + + status = RtlQueryEnvironmentVariable_U( + Environment, + &variableNameUs, + &variableValueUs + ); + + if (Value && status == STATUS_BUFFER_TOO_SMALL) + { + if (variableValueUs.Length + sizeof(UNICODE_NULL) > UNICODE_STRING_MAX_BYTES) + variableValueUs.MaximumLength = variableValueUs.Length; + else + variableValueUs.MaximumLength = variableValueUs.Length + sizeof(UNICODE_NULL); + + PhFree(variableValueUs.Buffer); + variableValueUs.Buffer = PhAllocateZero(variableValueUs.MaximumLength); + + status = RtlQueryEnvironmentVariable_U( + Environment, + &variableNameUs, + &variableValueUs + ); + } + + if (Value && NT_SUCCESS(status)) + { + *Value = PhCreateStringFromUnicodeString(&variableValueUs); + } + + if (Value && variableValueUs.Buffer) + { + PhFree(variableValueUs.Buffer); + } + + return status; +} + +/** + * Gets the file name of a mapped section. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION + * access. + * \param BaseAddress The base address of the section view. + * \param FileName A variable which receives a pointer to a string containing the file name of the + * section. You must free the string using PhDereferenceObject() when you no longer need it. + */ +NTSTATUS PhGetProcessMappedFileName( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _Out_ PPH_STRING *FileName + ) +{ + NTSTATUS status; + SIZE_T bufferSize; + SIZE_T returnLength; + PUNICODE_STRING buffer; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + status = NtQueryVirtualMemory( + ProcessHandle, + BaseAddress, + MemoryMappedFilenameInformation, + buffer, + bufferSize, + &returnLength + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + bufferSize = returnLength; + buffer = PhAllocate(bufferSize); + + status = NtQueryVirtualMemory( + ProcessHandle, + BaseAddress, + MemoryMappedFilenameInformation, + buffer, + bufferSize, + &returnLength + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *FileName = PhCreateStringFromUnicodeString(buffer); + PhFree(buffer); + + return status; +} + +/** + * Gets working set information for a process. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION + * access. + * \param WorkingSetInformation A variable which receives a pointer to the information. You must + * free the buffer using PhFree() when you no longer need it. + */ +NTSTATUS PhGetProcessWorkingSetInformation( + _In_ HANDLE ProcessHandle, + _Out_ PMEMORY_WORKING_SET_INFORMATION *WorkingSetInformation + ) +{ + NTSTATUS status; + PVOID buffer; + SIZE_T bufferSize; + + bufferSize = 0x8000; + buffer = PhAllocate(bufferSize); + + while ((status = NtQueryVirtualMemory( + ProcessHandle, + NULL, + MemoryWorkingSetInformation, + buffer, + bufferSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *WorkingSetInformation = (PMEMORY_WORKING_SET_INFORMATION)buffer; + + return status; +} + +/** + * Gets working set counters for a process. + * + * \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION + * access. + * \param WsCounters A variable which receives the counters. + */ +NTSTATUS PhGetProcessWsCounters( + _In_ HANDLE ProcessHandle, + _Out_ PPH_PROCESS_WS_COUNTERS WsCounters + ) +{ + NTSTATUS status; + PMEMORY_WORKING_SET_INFORMATION wsInfo; + PH_PROCESS_WS_COUNTERS wsCounters; + ULONG_PTR i; + + if (!NT_SUCCESS(status = PhGetProcessWorkingSetInformation( + ProcessHandle, + &wsInfo + ))) + return status; + + memset(&wsCounters, 0, sizeof(PH_PROCESS_WS_COUNTERS)); + + for (i = 0; i < wsInfo->NumberOfEntries; i++) + { + wsCounters.NumberOfPages++; + + if (wsInfo->WorkingSetInfo[i].ShareCount > 1) + wsCounters.NumberOfSharedPages++; + if (wsInfo->WorkingSetInfo[i].ShareCount == 0) + wsCounters.NumberOfPrivatePages++; + if (wsInfo->WorkingSetInfo[i].Shared) + wsCounters.NumberOfShareablePages++; + } + + PhFree(wsInfo); + + *WsCounters = wsCounters; + + return status; +} + +NTSTATUS PhGetProcessUnloadedDlls( + _In_ HANDLE ProcessId, + _Out_ PVOID *EventTrace, + _Out_ ULONG *EventTraceSize, + _Out_ ULONG *EventTraceCount + ) +{ + NTSTATUS status; + PULONG elementSize; + PULONG elementCount; + PVOID eventTrace; + HANDLE processHandle = NULL; + ULONG eventTraceSize; + ULONG capturedElementSize; + ULONG capturedElementCount; + PVOID capturedEventTracePointer; + PVOID capturedEventTrace = NULL; + + RtlGetUnloadEventTraceEx(&elementSize, &elementCount, &eventTrace); + + if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_VM_READ, ProcessId))) + goto CleanupExit; + + // We have the pointers for the unload event trace information. + // Since ntdll is loaded at the same base address across all processes, + // we can read the information in. + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + processHandle, + elementSize, + &capturedElementSize, + sizeof(ULONG), + NULL + ))) + goto CleanupExit; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + processHandle, + elementCount, + &capturedElementCount, + sizeof(ULONG), + NULL + ))) + goto CleanupExit; + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + processHandle, + eventTrace, + &capturedEventTracePointer, + sizeof(PVOID), + NULL + ))) + goto CleanupExit; + + if (!capturedEventTracePointer) + { + status = STATUS_NOT_FOUND; // no events + goto CleanupExit; + } + + if (capturedElementCount > 0x4000) + capturedElementCount = 0x4000; + + eventTraceSize = capturedElementSize * capturedElementCount; + capturedEventTrace = PhAllocateSafe(eventTraceSize); + + if (!capturedEventTrace) + { + status = STATUS_NO_MEMORY; + goto CleanupExit; + } + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + processHandle, + capturedEventTracePointer, + capturedEventTrace, + eventTraceSize, + NULL + ))) + goto CleanupExit; + +CleanupExit: + + if (processHandle) + NtClose(processHandle); + + if (NT_SUCCESS(status)) + { + *EventTrace = capturedEventTrace; + *EventTraceSize = capturedElementSize; + *EventTraceCount = capturedElementCount; + } + + return status; +} + +/** + * Causes a process to load a DLL. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ + * and PROCESS_VM_WRITE access. + * \param FileName The file name of the DLL to inject. + * \param Timeout The timeout, in milliseconds, for the process to load the DLL. + * + * \remarks If the process does not load the DLL before the timeout expires it may crash. Choose the + * timeout value carefully. + */ +NTSTATUS PhLoadDllProcess( + _In_ HANDLE ProcessHandle, + _In_ PWSTR FileName, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ +#ifdef _WIN64 + static PVOID loadLibraryW32 = NULL; +#endif + NTSTATUS status; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; + BOOLEAN isModule32 = FALSE; + PH_MAPPED_IMAGE mappedImage; +#endif + PVOID threadStart; + PH_STRINGREF fileName; + PVOID baseAddress = NULL; + SIZE_T allocSize; + HANDLE threadHandle; + +#ifdef _WIN64 + PhGetProcessIsWow64(ProcessHandle, &isWow64); + + if (isWow64) + { + if (!NT_SUCCESS(status = PhLoadMappedImage(FileName, NULL, TRUE, &mappedImage))) + return status; + + isModule32 = mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC; + PhUnloadMappedImage(&mappedImage); + } + + if (!isModule32) + { +#endif + threadStart = PhGetDllProcedureAddress(L"kernel32.dll", "LoadLibraryW", 0); +#ifdef _WIN64 + } + else + { + threadStart = loadLibraryW32; + + if (!threadStart) + { + PH_STRINGREF systemRoot; + PPH_STRING kernel32FileName; + + PhGetSystemRoot(&systemRoot); + kernel32FileName = PhConcatStringRefZ(&systemRoot, L"\\SysWow64\\kernel32.dll"); + + status = PhGetProcedureAddressRemote( + ProcessHandle, + kernel32FileName->Buffer, + "LoadLibraryW", + 0, + &loadLibraryW32, + NULL + ); + PhDereferenceObject(kernel32FileName); + + if (!NT_SUCCESS(status)) + return status; + + threadStart = loadLibraryW32; + } + } +#endif + + PhInitializeStringRefLongHint(&fileName, FileName); + allocSize = fileName.Length + sizeof(UNICODE_NULL); + + if (!NT_SUCCESS(status = NtAllocateVirtualMemory( + ProcessHandle, + &baseAddress, + 0, + &allocSize, + MEM_COMMIT, + PAGE_READWRITE + ))) + return status; + + if (!NT_SUCCESS(status = NtWriteVirtualMemory( + ProcessHandle, + baseAddress, + fileName.Buffer, + fileName.Length + sizeof(UNICODE_NULL), + NULL + ))) + goto FreeExit; + + if (!NT_SUCCESS(status = RtlCreateUserThread( + ProcessHandle, + NULL, + FALSE, + 0, + 0, + 0, + threadStart, + baseAddress, + &threadHandle, + NULL + ))) + goto FreeExit; + + // Wait for the thread to finish. + status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); + NtClose(threadHandle); + +FreeExit: + // Size needs to be zero if we're freeing. + allocSize = 0; + NtFreeVirtualMemory( + ProcessHandle, + &baseAddress, + &allocSize, + MEM_RELEASE + ); + + return status; +} + +/** + * Causes a process to unload a DLL. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ + * and PROCESS_VM_WRITE access. + * \param BaseAddress The base address of the DLL to unload. + * \param Timeout The timeout, in milliseconds, for the process to unload the DLL. + */ +NTSTATUS PhUnloadDllProcess( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ +#ifdef _WIN64 + static PVOID ldrUnloadDll32 = NULL; +#endif + + NTSTATUS status; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; + BOOLEAN isModule32 = FALSE; +#endif + HANDLE threadHandle; + THREAD_BASIC_INFORMATION basicInfo; + PVOID threadStart; + +#ifdef _WIN64 + PhGetProcessIsWow64(ProcessHandle, &isWow64); +#endif + + // No point trying to set the load count on Windows 8 and higher, because NT now uses a DAG of + // loader nodes. + if (WindowsVersion < WINDOWS_8) + { + status = PhSetProcessModuleLoadCount( + ProcessHandle, + BaseAddress, + 1 + ); + +#ifdef _WIN64 + if (isWow64 && status == STATUS_DLL_NOT_FOUND) + { + // The DLL might be 32-bit. + status = PhSetProcessModuleLoadCount32( + ProcessHandle, + BaseAddress, + 1 + ); + + if (NT_SUCCESS(status)) + isModule32 = TRUE; + } +#endif + + if (!NT_SUCCESS(status)) + return status; + } + +#ifdef _WIN64 + if (!isModule32) + { +#endif + threadStart = PhGetDllProcedureAddress(L"ntdll.dll", "LdrUnloadDll", 0); +#ifdef _WIN64 + } + else + { + threadStart = ldrUnloadDll32; + + if (!threadStart) + { + PH_STRINGREF systemRoot; + PPH_STRING ntdll32FileName; + + PhGetSystemRoot(&systemRoot); + ntdll32FileName = PhConcatStringRefZ(&systemRoot, L"\\SysWow64\\ntdll.dll"); + + status = PhGetProcedureAddressRemote( + ProcessHandle, + ntdll32FileName->Buffer, + "LdrUnloadDll", + 0, + &ldrUnloadDll32, + NULL + ); + PhDereferenceObject(ntdll32FileName); + + if (!NT_SUCCESS(status)) + return status; + + threadStart = ldrUnloadDll32; + } + } +#endif + + status = RtlCreateUserThread( + ProcessHandle, + NULL, + FALSE, + 0, + 0, + 0, + threadStart, + BaseAddress, + &threadHandle, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); + + if (status == STATUS_WAIT_0) + { + status = PhGetThreadBasicInformation(threadHandle, &basicInfo); + + if (NT_SUCCESS(status)) + status = basicInfo.ExitStatus; + } + + NtClose(threadHandle); + + return status; +} + +/** + * Sets an environment variable in a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ + * and PROCESS_VM_WRITE access. + * \param Name The name of the environment variable to set. + * \param Value The new value of the environment variable. If this parameter is NULL, the + * environment variable is deleted. + * \param Timeout The timeout, in milliseconds, for the process to set the environment variable. + */ +NTSTATUS PhSetEnvironmentVariableRemote( + _In_ HANDLE ProcessHandle, + _In_ PPH_STRINGREF Name, + _In_opt_ PPH_STRINGREF Value, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + NTSTATUS status; +#ifdef _WIN64 + BOOLEAN isWow64; +#endif + PPH_STRING ntdllFileName = NULL; + PPH_STRING kernel32FileName = NULL; + PVOID nameBaseAddress = NULL; + PVOID valueBaseAddress = NULL; + SIZE_T nameAllocationSize = 0; + SIZE_T valueAllocationSize = 0; + PVOID rtlExitUserThread = NULL; + PVOID setEnvironmentVariableW = NULL; + HANDLE threadHandle = NULL; + + nameAllocationSize = Name->Length + sizeof(UNICODE_NULL); + + if (Value) + valueAllocationSize = Value->Length + sizeof(UNICODE_NULL); + +#ifdef _WIN64 + if (!NT_SUCCESS(status = PhGetProcessIsWow64(ProcessHandle, &isWow64))) + goto CleanupExit; + + if (isWow64) + { + PH_STRINGREF systemRootSr; + + PhGetSystemRoot(&systemRootSr); + ntdllFileName = PhConcatStringRefZ(&systemRootSr, L"\\SysWow64\\ntdll.dll"); + kernel32FileName = PhConcatStringRefZ(&systemRootSr, L"\\SysWow64\\kernel32.dll"); + } + else + { +#endif + PH_STRINGREF systemRootSr; + + PhGetSystemRoot(&systemRootSr); + ntdllFileName = PhConcatStringRefZ(&systemRootSr, L"\\System32\\ntdll.dll"); + kernel32FileName = PhConcatStringRefZ(&systemRootSr, L"\\System32\\kernel32.dll"); +#ifdef _WIN64 + } +#endif + + if (!NT_SUCCESS(status = PhGetProcedureAddressRemote( + ProcessHandle, + ntdllFileName->Buffer, + "RtlExitUserThread", + 0, + &rtlExitUserThread, + NULL + ))) + { + goto CleanupExit; + } + + if (!NT_SUCCESS(status = PhGetProcedureAddressRemote( + ProcessHandle, + kernel32FileName->Buffer, + "SetEnvironmentVariableW", + 0, + &setEnvironmentVariableW, + NULL + ))) + { + goto CleanupExit; + } + + if (!NT_SUCCESS(status = NtAllocateVirtualMemory( + ProcessHandle, + &nameBaseAddress, + 0, + &nameAllocationSize, + MEM_COMMIT, + PAGE_READWRITE + ))) + { + goto CleanupExit; + } + + if (!NT_SUCCESS(status = NtWriteVirtualMemory( + ProcessHandle, + nameBaseAddress, + Name->Buffer, + Name->Length, + NULL + ))) + { + goto CleanupExit; + } + + if (Value) + { + if (!NT_SUCCESS(status = NtAllocateVirtualMemory( + ProcessHandle, + &valueBaseAddress, + 0, + &valueAllocationSize, + MEM_COMMIT, + PAGE_READWRITE + ))) + { + goto CleanupExit; + } + + if (!NT_SUCCESS(status = NtWriteVirtualMemory( + ProcessHandle, + valueBaseAddress, + Value->Buffer, + Value->Length, + NULL + ))) + { + goto CleanupExit; + } + } + + if (!NT_SUCCESS(status = RtlCreateUserThread( + ProcessHandle, + NULL, + TRUE, + 0, + 0, + 0, + rtlExitUserThread, + NULL, + &threadHandle, + NULL + ))) + { + goto CleanupExit; + } + +#ifdef _WIN64 + if (isWow64) + { + if (!NT_SUCCESS(status = RtlQueueApcWow64Thread( + threadHandle, + setEnvironmentVariableW, + nameBaseAddress, + valueBaseAddress, + NULL + ))) + { + goto CleanupExit; + } + } + else + { +#endif + if (!NT_SUCCESS(status = NtQueueApcThreadEx( + threadHandle, + NULL, + setEnvironmentVariableW, + nameBaseAddress, + valueBaseAddress, + NULL + ))) + { + goto CleanupExit; + } +#ifdef _WIN64 + } +#endif + + status = NtAlertResumeThread(threadHandle, NULL); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtWaitForSingleObject(threadHandle, FALSE, Timeout); + +CleanupExit: + + if (threadHandle) + { + NtClose(threadHandle); + } + + if (nameBaseAddress) + { + nameAllocationSize = 0; + NtFreeVirtualMemory( + ProcessHandle, + &nameBaseAddress, + &nameAllocationSize, + MEM_RELEASE + ); + } + + if (valueBaseAddress) + { + valueAllocationSize = 0; + NtFreeVirtualMemory( + ProcessHandle, + &valueBaseAddress, + &valueAllocationSize, + MEM_RELEASE + ); + } + + PhClearReference(&ntdllFileName); + PhClearReference(&kernel32FileName); + + return status; +} + +NTSTATUS PhGetJobProcessIdList( + _In_ HANDLE JobHandle, + _Out_ PJOBOBJECT_BASIC_PROCESS_ID_LIST *ProcessIdList + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + status = NtQueryInformationJobObject( + JobHandle, + JobObjectBasicProcessIdList, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + status = NtQueryInformationJobObject( + JobHandle, + JobObjectBasicProcessIdList, + buffer, + bufferSize, + NULL + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *ProcessIdList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)buffer; + + return status; +} + +/** + * Queries variable-sized information for a token. The function allocates a buffer to contain the + * information. + * + * \param TokenHandle A handle to a token. The access required depends on the information class + * specified. + * \param TokenInformationClass The information class to retrieve. + * \param Buffer A variable which receives a pointer to a buffer containing the information. You + * must free the buffer using PhFree() when you no longer need it. + */ +NTSTATUS PhpQueryTokenVariableSize( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG returnLength = 0; + + NtQueryInformationToken( + TokenHandle, + TokenInformationClass, + NULL, + 0, + &returnLength + ); + buffer = PhAllocate(returnLength); + status = NtQueryInformationToken( + TokenHandle, + TokenInformationClass, + buffer, + returnLength, + &returnLength + ); + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +/** + * Queries variable-sized information for a token. The function allocates a buffer to contain the + * information. + * + * \param TokenHandle A handle to a token. The access required depends on the information class + * specified. + * \param TokenInformationClass The information class to retrieve. + * \param Buffer A variable which receives a pointer to a buffer containing the information. You + * must free the buffer using PhFree() when you no longer need it. + */ +NTSTATUS PhQueryTokenVariableSize( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID *Buffer + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenInformationClass, + Buffer + ); +} + +/** + * Gets a token's user. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param User A variable which receives a pointer to a structure containing the token's user. You + * must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhGetTokenUser( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_USER *User + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenUser, + User + ); +} + +/** + * Gets a token's owner. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param Owner A variable which receives a pointer to a structure containing the token's owner. You + * must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhGetTokenOwner( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_OWNER *Owner + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenOwner, + Owner + ); +} + +/** + * Gets a token's primary group. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param PrimaryGroup A variable which receives a pointer to a structure containing the token's + * primary group. You must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhGetTokenPrimaryGroup( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_PRIMARY_GROUP *PrimaryGroup + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenPrimaryGroup, + PrimaryGroup + ); +} + +/** + * Gets a token's groups. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param Groups A variable which receives a pointer to a structure containing the token's groups. + * You must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhGetTokenGroups( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_GROUPS *Groups + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenGroups, + Groups + ); +} + +/** + * Get a token's restricted SIDs. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param RestrictedSids A variable which receives a pointer to a structure containing the token's restricted SIDs. + * You must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhGetTokenRestrictedSids( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_GROUPS* RestrictedSids +) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenRestrictedSids, + RestrictedSids + ); +} + +/** + * Gets a token's privileges. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param Privileges A variable which receives a pointer to a structure containing the token's + * privileges. You must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhGetTokenPrivileges( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_PRIVILEGES *Privileges + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenPrivileges, + Privileges + ); +} + +NTSTATUS PhGetTokenTrustLevel( + _In_ HANDLE TokenHandle, + _Out_ PTOKEN_PROCESS_TRUST_LEVEL *TrustLevel + ) +{ + return PhpQueryTokenVariableSize( + TokenHandle, + TokenProcessTrustLevel, + TrustLevel + ); +} + +NTSTATUS PhSetTokenSessionId( + _In_ HANDLE TokenHandle, + _In_ ULONG SessionId + ) +{ + return NtSetInformationToken( + TokenHandle, + TokenSessionId, + &SessionId, + sizeof(ULONG) + ); +} + +NTSTATUS PhGetTokenNamedObjectPath( + _In_ HANDLE TokenHandle, + _In_opt_ PSID Sid, + _Out_ PPH_STRING* ObjectPath + ) +{ + NTSTATUS status; + UNICODE_STRING objectPathUs; + + if (!RtlGetTokenNamedObjectPath_Import()) + return STATUS_NOT_SUPPORTED; + + status = RtlGetTokenNamedObjectPath_Import()( + TokenHandle, + Sid, + &objectPathUs + ); + + if (NT_SUCCESS(status)) + { + *ObjectPath = PhCreateStringFromUnicodeString(&objectPathUs); + RtlFreeUnicodeString(&objectPathUs); + } + + return status; +} + +NTSTATUS PhGetAppContainerNamedObjectPath( + _In_ HANDLE TokenHandle, + _In_opt_ PSID AppContainerSid, + _In_ BOOLEAN RelativePath, + _Out_ PPH_STRING* ObjectPath + ) +{ + NTSTATUS status; + UNICODE_STRING objectPathUs; + + if (!RtlGetAppContainerNamedObjectPath_Import()) + return STATUS_UNSUCCESSFUL; + + status = RtlGetAppContainerNamedObjectPath_Import()( + TokenHandle, + AppContainerSid, + RelativePath, + &objectPathUs + ); + + if (NT_SUCCESS(status)) + { + *ObjectPath = PhCreateStringFromUnicodeString(&objectPathUs); + RtlFreeUnicodeString(&objectPathUs); + } + + return status; +} + +/** + * Modifies a token privilege. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_ADJUST_PRIVILEGES access. + * \param PrivilegeName The name of the privilege to modify. If this parameter is NULL, you must + * specify a LUID in the \a PrivilegeLuid parameter. + * \param PrivilegeLuid The LUID of the privilege to modify. If this parameter is NULL, you must + * specify a name in the \a PrivilegeName parameter. + * \param Attributes The new attributes of the privilege. + */ +BOOLEAN PhSetTokenPrivilege( + _In_ HANDLE TokenHandle, + _In_opt_ PWSTR PrivilegeName, + _In_opt_ PLUID PrivilegeLuid, + _In_ ULONG Attributes + ) +{ + NTSTATUS status; + TOKEN_PRIVILEGES privileges; + + privileges.PrivilegeCount = 1; + privileges.Privileges[0].Attributes = Attributes; + + if (PrivilegeLuid) + { + privileges.Privileges[0].Luid = *PrivilegeLuid; + } + else if (PrivilegeName) + { + PH_STRINGREF privilegeName; + + PhInitializeStringRef(&privilegeName, PrivilegeName); + + if (!PhLookupPrivilegeValue( + &privilegeName, + &privileges.Privileges[0].Luid + )) + return FALSE; + } + else + { + return FALSE; + } + + if (!NT_SUCCESS(status = NtAdjustPrivilegesToken( + TokenHandle, + FALSE, + &privileges, + 0, + NULL, + NULL + ))) + return FALSE; + + if (status == STATUS_NOT_ALL_ASSIGNED) + return FALSE; + + return TRUE; +} + +BOOLEAN PhSetTokenPrivilege2( + _In_ HANDLE TokenHandle, + _In_ LONG Privilege, + _In_ ULONG Attributes + ) +{ + LUID privilegeLuid; + + privilegeLuid = RtlConvertLongToLuid(Privilege); + + return PhSetTokenPrivilege(TokenHandle, NULL, &privilegeLuid, Attributes); +} + +/** +* Modifies a token group. +* +* \param TokenHandle A handle to a token. The handle must have TOKEN_ADJUST_GROUPS access. +* \param GroupName The name of the group to modify. If this parameter is NULL, you must +* specify a PSID in the \a GroupSid parameter. +* \param GroupSid The PSID of the group to modify. If this parameter is NULL, you must +* specify a group name in the \a GroupName parameter. +* \param Attributes The new attributes of the group. +*/ +NTSTATUS PhSetTokenGroups( + _In_ HANDLE TokenHandle, + _In_opt_ PWSTR GroupName, + _In_opt_ PSID GroupSid, + _In_ ULONG Attributes + ) +{ + NTSTATUS status; + TOKEN_GROUPS groups; + + groups.GroupCount = 1; + groups.Groups[0].Attributes = Attributes; + + if (GroupSid) + { + groups.Groups[0].Sid = GroupSid; + } + else if (GroupName) + { + PH_STRINGREF groupName; + + PhInitializeStringRef(&groupName, GroupName); + + if (!NT_SUCCESS(status = PhLookupName(&groupName, &groups.Groups[0].Sid, NULL, NULL))) + return status; + } + else + { + return STATUS_INVALID_PARAMETER; + } + + status = NtAdjustGroupsToken( + TokenHandle, + FALSE, + &groups, + 0, + NULL, + NULL + ); + + if (GroupName && groups.Groups[0].Sid) + PhFree(groups.Groups[0].Sid); + + return status; +} + +/** + * Sets whether virtualization is enabled for a token. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_WRITE access. + * \param IsVirtualizationEnabled A boolean indicating whether virtualization is to be enabled for + * the token. + */ +NTSTATUS PhSetTokenIsVirtualizationEnabled( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN IsVirtualizationEnabled + ) +{ + ULONG virtualizationEnabled; + + virtualizationEnabled = IsVirtualizationEnabled; + + return NtSetInformationToken( + TokenHandle, + TokenVirtualizationEnabled, + &virtualizationEnabled, + sizeof(ULONG) + ); +} + +/** +* Gets a token's integrity level RID. Can handle custom integrity levels. +* +* \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. +* \param IntegrityLevelRID A variable which receives the integrity level of the token. +* \param IntegrityString A variable which receives a pointer to a string containing a string +* representation of the integrity level. +*/ +NTSTATUS PhGetTokenIntegrityLevelRID( + _In_ HANDLE TokenHandle, + _Out_opt_ PMANDATORY_LEVEL_RID IntegrityLevelRID, + _Out_opt_ PWSTR *IntegrityString + ) +{ + NTSTATUS status; + PTOKEN_MANDATORY_LABEL mandatoryLabel; + ULONG subAuthoritiesCount; + ULONG subAuthority; + PWSTR integrityString; + + status = PhpQueryTokenVariableSize(TokenHandle, TokenIntegrityLevel, &mandatoryLabel); + + if (!NT_SUCCESS(status)) + return status; + + subAuthoritiesCount = *RtlSubAuthorityCountSid(mandatoryLabel->Label.Sid); + + if (subAuthoritiesCount > 0) + { + subAuthority = *RtlSubAuthoritySid(mandatoryLabel->Label.Sid, subAuthoritiesCount - 1); + } + else + { + subAuthority = SECURITY_MANDATORY_UNTRUSTED_RID; + } + + PhFree(mandatoryLabel); + + if (IntegrityString) + { + switch (subAuthority) + { + case SECURITY_MANDATORY_UNTRUSTED_RID: + integrityString = L"Untrusted"; + break; + case SECURITY_MANDATORY_LOW_RID: + integrityString = L"Low"; + break; + case SECURITY_MANDATORY_MEDIUM_RID: + integrityString = L"Medium"; + break; + case SECURITY_MANDATORY_MEDIUM_PLUS_RID: + integrityString = L"Medium +"; + break; + case SECURITY_MANDATORY_HIGH_RID: + integrityString = L"High"; + break; + case SECURITY_MANDATORY_SYSTEM_RID: + integrityString = L"System"; + break; + case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: + integrityString = L"Protected"; + break; + default: + integrityString = L"Other"; + break; + } + + *IntegrityString = integrityString; + } + + if (IntegrityLevelRID) + *IntegrityLevelRID = subAuthority; + + return status; +} + +/** + * Gets a token's integrity level. + * + * \param TokenHandle A handle to a token. The handle must have TOKEN_QUERY access. + * \param IntegrityLevel A variable which receives the integrity level of the token. + * If the integrity level is not a well-known one the function fails. + * \param IntegrityString A variable which receives a pointer to a string containing a string + * representation of the integrity level. + */ +NTSTATUS PhGetTokenIntegrityLevel( + _In_ HANDLE TokenHandle, + _Out_opt_ PMANDATORY_LEVEL IntegrityLevel, + _Out_opt_ PWSTR *IntegrityString + ) +{ + NTSTATUS status; + MANDATORY_LEVEL_RID integrityLevelRID; + MANDATORY_LEVEL integrityLevel; + + status = PhGetTokenIntegrityLevelRID(TokenHandle, &integrityLevelRID, IntegrityString); + + if (!NT_SUCCESS(status)) + return status; + + if (IntegrityLevel) + { + switch (integrityLevelRID) + { + case SECURITY_MANDATORY_UNTRUSTED_RID: + integrityLevel = MandatoryLevelUntrusted; + break; + case SECURITY_MANDATORY_LOW_RID: + integrityLevel = MandatoryLevelLow; + break; + case SECURITY_MANDATORY_MEDIUM_RID: + integrityLevel = MandatoryLevelMedium; + break; + case SECURITY_MANDATORY_HIGH_RID: + integrityLevel = MandatoryLevelHigh; + break; + case SECURITY_MANDATORY_SYSTEM_RID: + integrityLevel = MandatoryLevelSystem; + break; + case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: + integrityLevel = MandatoryLevelSecureProcess; + break; + default: + return STATUS_UNSUCCESSFUL; + } + + *IntegrityLevel = integrityLevel; + } + + return status; +} + +NTSTATUS PhGetTokenProcessTrustLevelRID( + _In_ HANDLE TokenHandle, + _Out_opt_ PULONG ProtectionType, + _Out_opt_ PULONG ProtectionLevel, + _Out_opt_ PPH_STRING* TrustLevelString, + _Out_opt_ PPH_STRING* TrustLevelSidString + ) +{ + NTSTATUS status; + PTOKEN_SID_INFORMATION trustLevel; + ULONG subAuthoritiesCount; + ULONG protectionType; + ULONG protectionLevel; + + status = PhpQueryTokenVariableSize(TokenHandle, TokenProcessTrustLevel, &trustLevel); + + if (!NT_SUCCESS(status)) + return status; + + if (!RtlValidSid(trustLevel->Sid)) + return STATUS_UNSUCCESSFUL; + + subAuthoritiesCount = *RtlSubAuthorityCountSid(trustLevel->Sid); + //RtlIdentifierAuthoritySid(TokenPageContext->Capabilities->Groups[i].Sid) == (BYTE[])SECURITY_PROCESS_TRUST_AUTHORITY + + if (subAuthoritiesCount == SECURITY_PROCESS_TRUST_AUTHORITY_RID_COUNT) + { + protectionType = *RtlSubAuthoritySid(trustLevel->Sid, 0); + protectionLevel = *RtlSubAuthoritySid(trustLevel->Sid, 1); + } + else + { + protectionType = SECURITY_PROCESS_PROTECTION_TYPE_NONE_RID; + protectionLevel = SECURITY_PROCESS_PROTECTION_LEVEL_NONE_RID; + } + + if (ProtectionType) + *ProtectionType = protectionType; + if (ProtectionLevel) + *ProtectionLevel = protectionLevel; + + if (TrustLevelString) + { + PWSTR protectionTypeString = NULL; + PWSTR protectionLevelString = NULL; + + switch (protectionType) + { + case SECURITY_PROCESS_PROTECTION_TYPE_FULL_RID: + protectionTypeString = L"Full"; + break; + case SECURITY_PROCESS_PROTECTION_TYPE_LITE_RID: + protectionTypeString = L"Lite"; + break; + } + + switch (protectionLevel) + { + case SECURITY_PROCESS_PROTECTION_LEVEL_WINTCB_RID: + protectionLevelString = L" (WinTcb)"; + break; + case SECURITY_PROCESS_PROTECTION_LEVEL_WINDOWS_RID: + protectionLevelString = L" (Windows)"; + break; + case SECURITY_PROCESS_PROTECTION_LEVEL_APP_RID: + protectionLevelString = L" (StoreApp)"; + break; + case SECURITY_PROCESS_PROTECTION_LEVEL_ANTIMALWARE_RID: + protectionLevelString = L" (Antimalware)"; + break; + case SECURITY_PROCESS_PROTECTION_LEVEL_AUTHENTICODE_RID: + protectionLevelString = L" (Authenticode)"; + break; + } + + if (protectionTypeString && protectionLevelString) + *TrustLevelString = PhConcatStrings2(protectionTypeString, protectionLevelString); + else + *TrustLevelString = PhCreateString(L"Unknown"); + } + + if (TrustLevelSidString) + { + *TrustLevelSidString = PhSidToStringSid(trustLevel->Sid); + } + + PhFree(trustLevel); + + return status; +} + +NTSTATUS PhpQueryFileVariableSize( + _In_ HANDLE FileHandle, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + PVOID buffer; + ULONG bufferSize = 0x200; + + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + status = NtQueryInformationFile( + FileHandle, + &isb, + buffer, + bufferSize, + FileInformationClass + ); + + if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +NTSTATUS PhGetFileSize( + _In_ HANDLE FileHandle, + _Out_ PLARGE_INTEGER Size + ) +{ + NTSTATUS status; + FILE_STANDARD_INFORMATION standardInfo; + IO_STATUS_BLOCK isb; + + status = NtQueryInformationFile( + FileHandle, + &isb, + &standardInfo, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation + ); + + if (!NT_SUCCESS(status)) + return status; + + *Size = standardInfo.EndOfFile; + + return status; +} + +NTSTATUS PhSetFileSize( + _In_ HANDLE FileHandle, + _In_ PLARGE_INTEGER Size + ) +{ + FILE_END_OF_FILE_INFORMATION endOfFileInfo; + IO_STATUS_BLOCK isb; + + endOfFileInfo.EndOfFile = *Size; + + return NtSetInformationFile( + FileHandle, + &isb, + &endOfFileInfo, + sizeof(FILE_END_OF_FILE_INFORMATION), + FileEndOfFileInformation + ); +} + +NTSTATUS PhDeleteFile( + _In_ HANDLE FileHandle + ) +{ + if (WindowsVersion >= WINDOWS_10_RS5) + { + FILE_DISPOSITION_INFO_EX dispositionInfoEx; + IO_STATUS_BLOCK isb; + + dispositionInfoEx.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE; + + return NtSetInformationFile( + FileHandle, + &isb, + &dispositionInfoEx, + sizeof(FILE_DISPOSITION_INFO_EX), + FileDispositionInformationEx + ); + } + else + { + FILE_DISPOSITION_INFORMATION dispositionInfo; + IO_STATUS_BLOCK isb; + + dispositionInfo.DeleteFile = TRUE; + + return NtSetInformationFile( + FileHandle, + &isb, + &dispositionInfo, + sizeof(FILE_DISPOSITION_INFORMATION), + FileDispositionInformation + ); + } +} + +NTSTATUS PhGetFileHandleName( + _In_ HANDLE FileHandle, + _Out_ PPH_STRING *FileName + ) +{ + NTSTATUS status; + ULONG bufferSize; + PFILE_NAME_INFORMATION buffer; + IO_STATUS_BLOCK isb; + + bufferSize = sizeof(FILE_NAME_INFORMATION) + MAX_PATH; + buffer = PhAllocateZero(bufferSize); + + status = NtQueryInformationFile( + FileHandle, + &isb, + buffer, + bufferSize, + FileNameInformation + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + bufferSize = sizeof(FILE_NAME_INFORMATION) + buffer->FileNameLength + sizeof(UNICODE_NULL); + PhFree(buffer); + buffer = PhAllocateZero(bufferSize); + + status = NtQueryInformationFile( + FileHandle, + &isb, + buffer, + bufferSize, + FileNameInformation + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *FileName = PhCreateStringEx(buffer->FileName, buffer->FileNameLength); + PhFree(buffer); + + return status; +} + +NTSTATUS PhGetFileAllInformation( + _In_ HANDLE FileHandle, + _Out_ PFILE_ALL_INFORMATION *FileInformation + ) +{ + return PhpQueryFileVariableSize( + FileHandle, + FileAllInformation, + FileInformation + ); +} + +NTSTATUS PhGetFileId( + _In_ HANDLE FileHandle, + _Out_ PFILE_ID_INFORMATION *FileId + ) +{ + return PhpQueryFileVariableSize( + FileHandle, + FileIdInformation, + FileId + ); +} + +NTSTATUS PhGetProcessIdsUsingFile( + _In_ HANDLE FileHandle, + _Out_ PFILE_PROCESS_IDS_USING_FILE_INFORMATION *ProcessIdsUsingFile + ) +{ + return PhpQueryFileVariableSize( + FileHandle, + FileProcessIdsUsingFileInformation, + ProcessIdsUsingFile + ); +} + +NTSTATUS PhpQueryTransactionManagerVariableSize( + _In_ HANDLE TransactionManagerHandle, + _In_ TRANSACTIONMANAGER_INFORMATION_CLASS TransactionManagerInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x100; + + if (!NtQueryInformationTransactionManager_Import()) + return STATUS_NOT_SUPPORTED; + + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + status = NtQueryInformationTransactionManager_Import()( + TransactionManagerHandle, + TransactionManagerInformationClass, + buffer, + bufferSize, + NULL + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + bufferSize *= 2; + + if (bufferSize > 1 * 1024 * 1024) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +NTSTATUS PhGetTransactionManagerBasicInformation( + _In_ HANDLE TransactionManagerHandle, + _Out_ PTRANSACTIONMANAGER_BASIC_INFORMATION BasicInformation + ) +{ + if (NtQueryInformationTransactionManager_Import()) + { + return NtQueryInformationTransactionManager_Import()( + TransactionManagerHandle, + TransactionManagerBasicInformation, + BasicInformation, + sizeof(TRANSACTIONMANAGER_BASIC_INFORMATION), + NULL + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} + +NTSTATUS PhGetTransactionManagerLogFileName( + _In_ HANDLE TransactionManagerHandle, + _Out_ PPH_STRING *LogFileName + ) +{ + NTSTATUS status; + PTRANSACTIONMANAGER_LOGPATH_INFORMATION logPathInfo; + + status = PhpQueryTransactionManagerVariableSize( + TransactionManagerHandle, + TransactionManagerLogPathInformation, + &logPathInfo + ); + + if (!NT_SUCCESS(status)) + return status; + + if (logPathInfo->LogPathLength == 0) + { + *LogFileName = PhReferenceEmptyString(); + } + else + { + *LogFileName = PhCreateStringEx( + logPathInfo->LogPath, + logPathInfo->LogPathLength + ); + } + + PhFree(logPathInfo); + + return status; +} + +NTSTATUS PhpQueryTransactionVariableSize( + _In_ HANDLE TransactionHandle, + _In_ TRANSACTION_INFORMATION_CLASS TransactionInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x100; + + if (!NtQueryInformationTransaction_Import()) + return STATUS_NOT_SUPPORTED; + + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + status = NtQueryInformationTransaction_Import()( + TransactionHandle, + TransactionInformationClass, + buffer, + bufferSize, + NULL + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + bufferSize *= 2; + + if (bufferSize > 1 * 1024 * 1024) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +NTSTATUS PhGetTransactionBasicInformation( + _In_ HANDLE TransactionHandle, + _Out_ PTRANSACTION_BASIC_INFORMATION BasicInformation + ) +{ + if (NtQueryInformationTransaction_Import()) + { + return NtQueryInformationTransaction_Import()( + TransactionHandle, + TransactionBasicInformation, + BasicInformation, + sizeof(TRANSACTION_BASIC_INFORMATION), + NULL + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} + +NTSTATUS PhGetTransactionPropertiesInformation( + _In_ HANDLE TransactionHandle, + _Out_opt_ PLARGE_INTEGER Timeout, + _Out_opt_ TRANSACTION_OUTCOME *Outcome, + _Out_opt_ PPH_STRING *Description + ) +{ + NTSTATUS status; + PTRANSACTION_PROPERTIES_INFORMATION propertiesInfo; + + status = PhpQueryTransactionVariableSize( + TransactionHandle, + TransactionPropertiesInformation, + &propertiesInfo + ); + + if (!NT_SUCCESS(status)) + return status; + + if (Timeout) + { + *Timeout = propertiesInfo->Timeout; + } + + if (Outcome) + { + *Outcome = propertiesInfo->Outcome; + } + + if (Description) + { + *Description = PhCreateStringEx( + propertiesInfo->Description, + propertiesInfo->DescriptionLength + ); + } + + PhFree(propertiesInfo); + + return status; +} + +NTSTATUS PhpQueryResourceManagerVariableSize( + _In_ HANDLE ResourceManagerHandle, + _In_ RESOURCEMANAGER_INFORMATION_CLASS ResourceManagerInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x100; + + if (!NtQueryInformationResourceManager_Import()) + return STATUS_NOT_SUPPORTED; + + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + status = NtQueryInformationResourceManager_Import()( + ResourceManagerHandle, + ResourceManagerInformationClass, + buffer, + bufferSize, + NULL + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + bufferSize *= 2; + + if (bufferSize > 1 * 1024 * 1024) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +NTSTATUS PhGetResourceManagerBasicInformation( + _In_ HANDLE ResourceManagerHandle, + _Out_opt_ PGUID Guid, + _Out_opt_ PPH_STRING *Description + ) +{ + NTSTATUS status; + PRESOURCEMANAGER_BASIC_INFORMATION basicInfo; + + status = PhpQueryResourceManagerVariableSize( + ResourceManagerHandle, + ResourceManagerBasicInformation, + &basicInfo + ); + + if (!NT_SUCCESS(status)) + return status; + + if (Guid) + { + *Guid = basicInfo->ResourceManagerId; + } + + if (Description) + { + if (basicInfo->DescriptionLength == 0) + { + *Description = PhReferenceEmptyString(); + } + else + { + *Description = PhCreateStringEx( + basicInfo->Description, + basicInfo->DescriptionLength + ); + } + } + + PhFree(basicInfo); + + return status; +} + +NTSTATUS PhGetEnlistmentBasicInformation( + _In_ HANDLE EnlistmentHandle, + _Out_ PENLISTMENT_BASIC_INFORMATION BasicInformation + ) +{ + if (NtQueryInformationEnlistment_Import()) + { + return NtQueryInformationEnlistment_Import()( + EnlistmentHandle, + EnlistmentBasicInformation, + BasicInformation, + sizeof(ENLISTMENT_BASIC_INFORMATION), + NULL + ); + } + else + { + return STATUS_NOT_SUPPORTED; + } +} + +typedef struct _OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT +{ + NTSTATUS Status; + PVOID BaseAddress; + HANDLE DriverHandle; +} OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT, *POPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT; + +BOOLEAN NTAPI PhpOpenDriverByBaseAddressCallback( + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF TypeName, + _In_opt_ PVOID Context + ) +{ + static PH_STRINGREF driverDirectoryName = PH_STRINGREF_INIT(L"\\Driver\\"); + + NTSTATUS status; + POPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT context = Context; + PPH_STRING driverName; + UNICODE_STRING driverNameUs; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE driverHandle; + DRIVER_BASIC_INFORMATION basicInfo; + + driverName = PhConcatStringRef2(&driverDirectoryName, Name); + + if (!PhStringRefToUnicodeString(&driverName->sr, &driverNameUs)) + { + PhDereferenceObject(driverName); + return TRUE; + } + + InitializeObjectAttributes( + &objectAttributes, + &driverNameUs, + 0, + NULL, + NULL + ); + + status = KphOpenDriver(&driverHandle, SYNCHRONIZE, &objectAttributes); + PhDereferenceObject(driverName); + + if (!NT_SUCCESS(status)) + return TRUE; + + status = KphQueryInformationDriver( + driverHandle, + DriverBasicInformation, + &basicInfo, + sizeof(DRIVER_BASIC_INFORMATION), + NULL + ); + + if (NT_SUCCESS(status)) + { + if (basicInfo.DriverStart == context->BaseAddress) + { + context->Status = STATUS_SUCCESS; + context->DriverHandle = driverHandle; + + return FALSE; + } + } + + NtClose(driverHandle); + + return TRUE; +} + +/** + * Opens a driver object using a base address. + * + * \param DriverHandle A variable which receives a handle to the driver object. + * \param BaseAddress The base address of the driver to open. + * + * \retval STATUS_OBJECT_NAME_NOT_FOUND The driver could not be found. + * + * \remarks This function requires a valid KProcessHacker handle. + */ +NTSTATUS PhOpenDriverByBaseAddress( + _Out_ PHANDLE DriverHandle, + _In_ PVOID BaseAddress + ) +{ + NTSTATUS status; + UNICODE_STRING driverDirectoryName; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE driverDirectoryHandle; + OPEN_DRIVER_BY_BASE_ADDRESS_CONTEXT context; + + RtlInitUnicodeString( + &driverDirectoryName, + L"\\Driver" + ); + InitializeObjectAttributes( + &objectAttributes, + &driverDirectoryName, + 0, + NULL, + NULL + ); + + if (!NT_SUCCESS(status = NtOpenDirectoryObject( + &driverDirectoryHandle, + DIRECTORY_QUERY, + &objectAttributes + ))) + return status; + + context.Status = STATUS_OBJECT_NAME_NOT_FOUND; + context.BaseAddress = BaseAddress; + + status = PhEnumDirectoryObjects( + driverDirectoryHandle, + PhpOpenDriverByBaseAddressCallback, + &context + ); + NtClose(driverDirectoryHandle); + + if (!NT_SUCCESS(status) && !NT_SUCCESS(context.Status)) + return status; + + if (NT_SUCCESS(context.Status)) + { + *DriverHandle = context.DriverHandle; + } + + return context.Status; +} + +/** + * Queries variable-sized information for a driver. The function allocates a buffer to contain the + * information. + * + * \param DriverHandle A handle to a driver. The access required depends on the information class + * specified. + * \param DriverInformationClass The information class to retrieve. + * \param Buffer A variable which receives a pointer to a buffer containing the information. You + * must free the buffer using PhFree() when you no longer need it. + * + * \remarks This function requires a valid KProcessHacker handle. + */ +NTSTATUS PhpQueryDriverVariableSize( + _In_ HANDLE DriverHandle, + _In_ DRIVER_INFORMATION_CLASS DriverInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG returnLength = 0; + + KphQueryInformationDriver( + DriverHandle, + DriverInformationClass, + NULL, + 0, + &returnLength + ); + buffer = PhAllocate(returnLength); + status = KphQueryInformationDriver( + DriverHandle, + DriverInformationClass, + buffer, + returnLength, + &returnLength + ); + + if (NT_SUCCESS(status)) + { + *Buffer = buffer; + } + else + { + PhFree(buffer); + } + + return status; +} + +/** + * Gets the object name of a driver. + * + * \param DriverHandle A handle to a driver. + * \param Name A variable which receives a pointer to a string containing the object name. You must + * free the string using PhDereferenceObject() when you no longer need it. + * + * \remarks This function requires a valid KProcessHacker handle. + */ +NTSTATUS PhGetDriverName( + _In_ HANDLE DriverHandle, + _Out_ PPH_STRING *Name + ) +{ + NTSTATUS status; + PUNICODE_STRING unicodeString; + + if (!NT_SUCCESS(status = PhpQueryDriverVariableSize( + DriverHandle, + DriverNameInformation, + &unicodeString + ))) + return status; + + *Name = PhCreateStringFromUnicodeString(unicodeString); + PhFree(unicodeString); + + return status; +} + +/** + * Gets the service key name of a driver. + * + * \param DriverHandle A handle to a driver. + * \param ServiceKeyName A variable which receives a pointer to a string containing the service key + * name. You must free the string using PhDereferenceObject() when you no longer need it. + * + * \remarks This function requires a valid KProcessHacker handle. + */ +NTSTATUS PhGetDriverServiceKeyName( + _In_ HANDLE DriverHandle, + _Out_ PPH_STRING *ServiceKeyName + ) +{ + NTSTATUS status; + PUNICODE_STRING unicodeString; + + if (!NT_SUCCESS(status = PhpQueryDriverVariableSize( + DriverHandle, + DriverServiceKeyNameInformation, + &unicodeString + ))) + return status; + + *ServiceKeyName = PhCreateStringFromUnicodeString(unicodeString); + PhFree(unicodeString); + + return status; +} + +NTSTATUS PhpUnloadDriver( + _In_ PPH_STRING ServiceKeyName + ) +{ + static PH_STRINGREF fullServicesKeyName = PH_STRINGREF_INIT(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); + + NTSTATUS status; + PPH_STRING fullServiceKeyName; + UNICODE_STRING fullServiceKeyNameUs; + HANDLE serviceKeyHandle; + ULONG disposition; + + fullServiceKeyName = PhConcatStringRef2(&fullServicesKeyName, &ServiceKeyName->sr); + + if (!PhStringRefToUnicodeString(&fullServiceKeyName->sr, &fullServiceKeyNameUs)) + { + PhDereferenceObject(fullServiceKeyName); + return STATUS_NAME_TOO_LONG; + } + + if (NT_SUCCESS(status = PhCreateKey( + &serviceKeyHandle, + KEY_WRITE | DELETE, + NULL, + &fullServiceKeyName->sr, + 0, + 0, + &disposition + ))) + { + if (disposition == REG_CREATED_NEW_KEY) + { + static UNICODE_STRING imagePath = RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\drivers\\ntfs.sys"); + + UNICODE_STRING valueName; + ULONG dword; + + // Set up the required values. + dword = 1; + RtlInitUnicodeString(&valueName, L"ErrorControl"); + NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_DWORD, &dword, sizeof(ULONG)); + RtlInitUnicodeString(&valueName, L"Start"); + NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_DWORD, &dword, sizeof(ULONG)); + RtlInitUnicodeString(&valueName, L"Type"); + NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_DWORD, &dword, sizeof(ULONG)); + + // Use a bogus name. + RtlInitUnicodeString(&valueName, L"ImagePath"); + NtSetValueKey(serviceKeyHandle, &valueName, 0, REG_SZ, imagePath.Buffer, imagePath.Length + sizeof(WCHAR)); + } + + status = NtUnloadDriver(&fullServiceKeyNameUs); + + if (disposition == REG_CREATED_NEW_KEY) + { + // We added values, not subkeys, so this function will work correctly. + NtDeleteKey(serviceKeyHandle); + } + + NtClose(serviceKeyHandle); + } + + PhDereferenceObject(fullServiceKeyName); + + return status; +} + +/** + * Unloads a driver. + * + * \param BaseAddress The base address of the driver. This parameter can be NULL if a value is + * specified in \c Name. + * \param Name The base name of the driver. This parameter can be NULL if a value is specified in + * \c BaseAddress and KProcessHacker is loaded. + * + * \retval STATUS_INVALID_PARAMETER_MIX Both \c BaseAddress and \c Name were null, or \c Name was + * not specified and KProcessHacker is not loaded. + * \retval STATUS_OBJECT_NAME_NOT_FOUND The driver could not be found. + */ +NTSTATUS PhUnloadDriver( + _In_opt_ PVOID BaseAddress, + _In_opt_ PWSTR Name + ) +{ + NTSTATUS status; + HANDLE driverHandle; + PPH_STRING serviceKeyName = NULL; + + if (!BaseAddress && !Name) + return STATUS_INVALID_PARAMETER_MIX; + if (!Name && !KphIsConnected()) + return STATUS_INVALID_PARAMETER_MIX; + + // Try to get the service key name by scanning the Driver directory. + + if (KphIsConnected() && BaseAddress) + { + if (NT_SUCCESS(PhOpenDriverByBaseAddress( + &driverHandle, + BaseAddress + ))) + { + PhGetDriverServiceKeyName(driverHandle, &serviceKeyName); + NtClose(driverHandle); + } + } + + // Use the base name if we didn't get the service key name. + + if (!serviceKeyName && Name) + { + PPH_STRING name; + + name = PhCreateString(Name); + + // Remove the extension if it is present. + if (PhEndsWithString2(name, L".sys", TRUE)) + { + serviceKeyName = PhSubstring(name, 0, name->Length / sizeof(WCHAR) - 4); + PhDereferenceObject(name); + } + else + { + serviceKeyName = name; + } + } + + if (!serviceKeyName) + return STATUS_OBJECT_NAME_NOT_FOUND; + + status = PhpUnloadDriver(serviceKeyName); + PhDereferenceObject(serviceKeyName); + + return status; +} + +NTSTATUS PhpEnumProcessModules( + _In_ HANDLE ProcessHandle, + _In_ PPHP_ENUM_PROCESS_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ) +{ + NTSTATUS status; + PROCESS_BASIC_INFORMATION basicInfo; + PPEB_LDR_DATA ldr; + PEB_LDR_DATA pebLdrData; + PLIST_ENTRY startLink; + PLIST_ENTRY currentLink; + ULONG dataTableEntrySize; + LDR_DATA_TABLE_ENTRY currentEntry; + ULONG i; + + // Get the PEB address. + status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo); + + if (!NT_SUCCESS(status)) + return status; + + // Read the address of the loader data. + status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, Ldr)), + &ldr, + sizeof(PVOID), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Read the loader data. + status = NtReadVirtualMemory( + ProcessHandle, + ldr, + &pebLdrData, + sizeof(PEB_LDR_DATA), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (!pebLdrData.Initialized) + return STATUS_UNSUCCESSFUL; + + if (WindowsVersion >= WINDOWS_8) + dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN8; + else + dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN7; + + // Traverse the linked list (in load order). + + i = 0; + startLink = PTR_ADD_OFFSET(ldr, FIELD_OFFSET(PEB_LDR_DATA, InLoadOrderModuleList)); + currentLink = pebLdrData.InLoadOrderModuleList.Flink; + + while ( + currentLink != startLink && + i <= PH_ENUM_PROCESS_MODULES_LIMIT + ) + { + PVOID addressOfEntry; + + addressOfEntry = CONTAINING_RECORD(currentLink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + status = NtReadVirtualMemory( + ProcessHandle, + addressOfEntry, + ¤tEntry, + dataTableEntrySize, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Make sure the entry is valid. + if (currentEntry.DllBase) + { + // Execute the callback. + if (!Callback( + ProcessHandle, + ¤tEntry, + addressOfEntry, + Context1, + Context2 + )) + break; + } + + currentLink = currentEntry.InLoadOrderLinks.Flink; + i++; + } + + return status; +} + +BOOLEAN NTAPI PhpEnumProcessModulesCallback( + _In_ HANDLE ProcessHandle, + _In_ PLDR_DATA_TABLE_ENTRY Entry, + _In_ PVOID AddressOfEntry, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ) +{ + NTSTATUS status; + PPH_ENUM_PROCESS_MODULES_PARAMETERS parameters; + BOOLEAN cont; + PPH_STRING mappedFileName; + PWSTR fullDllNameOriginal; + PWSTR fullDllNameBuffer; + PWSTR baseDllNameOriginal; + PWSTR baseDllNameBuffer; + + parameters = Context1; + mappedFileName = NULL; + + if (parameters->Flags & PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME) + { + PhGetProcessMappedFileName(ProcessHandle, Entry->DllBase, &mappedFileName); + } + + if (mappedFileName) + { + ULONG_PTR indexOfLastBackslash; + + PhStringRefToUnicodeString(&mappedFileName->sr, &Entry->FullDllName); + indexOfLastBackslash = PhFindLastCharInString(mappedFileName, 0, OBJ_NAME_PATH_SEPARATOR); + + if (indexOfLastBackslash != -1) + { + Entry->BaseDllName.Buffer = PTR_ADD_OFFSET(Entry->FullDllName.Buffer, PTR_ADD_OFFSET(indexOfLastBackslash * sizeof(WCHAR), sizeof(UNICODE_NULL))); + Entry->BaseDllName.Length = Entry->FullDllName.Length - (USHORT)indexOfLastBackslash * sizeof(WCHAR) - sizeof(UNICODE_NULL); + Entry->BaseDllName.MaximumLength = Entry->BaseDllName.Length; + } + else + { + Entry->BaseDllName = Entry->FullDllName; + } + } + else + { + // Read the full DLL name string and add a null terminator. + + fullDllNameOriginal = Entry->FullDllName.Buffer; + fullDllNameBuffer = PhAllocate(Entry->FullDllName.Length + sizeof(UNICODE_NULL)); + Entry->FullDllName.Buffer = fullDllNameBuffer; + + if (NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + fullDllNameOriginal, + fullDllNameBuffer, + Entry->FullDllName.Length, + NULL + ))) + { + fullDllNameBuffer[Entry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + fullDllNameBuffer[0] = UNICODE_NULL; + Entry->FullDllName.Length = 0; + } + + baseDllNameOriginal = Entry->BaseDllName.Buffer; + + // Try to use the buffer we just read in. + if ( + NT_SUCCESS(status) && + (ULONG_PTR)baseDllNameOriginal >= (ULONG_PTR)fullDllNameOriginal && + (ULONG_PTR)PTR_ADD_OFFSET(baseDllNameOriginal, Entry->BaseDllName.Length) >= (ULONG_PTR)baseDllNameOriginal && + (ULONG_PTR)PTR_ADD_OFFSET(baseDllNameOriginal, Entry->BaseDllName.Length) <= (ULONG_PTR)PTR_ADD_OFFSET(fullDllNameOriginal, Entry->FullDllName.Length) + ) + { + baseDllNameBuffer = NULL; + + Entry->BaseDllName.Buffer = PTR_ADD_OFFSET(Entry->FullDllName.Buffer, PTR_SUB_OFFSET(baseDllNameOriginal, fullDllNameOriginal)); + } + else + { + // Read the base DLL name string and add a null terminator. + + baseDllNameBuffer = PhAllocate(Entry->BaseDllName.Length + sizeof(UNICODE_NULL)); + Entry->BaseDllName.Buffer = baseDllNameBuffer; + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + baseDllNameOriginal, + baseDllNameBuffer, + Entry->BaseDllName.Length, + NULL + ))) + { + baseDllNameBuffer[Entry->BaseDllName.Length / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + baseDllNameBuffer[0] = UNICODE_NULL; + Entry->BaseDllName.Length = 0; + } + } + } + + if (WindowsVersion >= WINDOWS_8) + { + LDR_DDAG_NODE ldrDagNode; + + memset(&ldrDagNode, 0, sizeof(LDR_DDAG_NODE)); + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + Entry->DdagNode, + &ldrDagNode, + sizeof(LDR_DDAG_NODE), + NULL + ))) + { + // Fixup the module load count. (dmex) + Entry->ObsoleteLoadCount = (USHORT)ldrDagNode.LoadCount; + } + } + + // Execute the callback. + cont = parameters->Callback(Entry, parameters->Context); + + if (mappedFileName) + { + PhDereferenceObject(mappedFileName); + } + else + { + PhFree(fullDllNameBuffer); + + if (baseDllNameBuffer) + PhFree(baseDllNameBuffer); + } + + return cont; +} + +/** + * Enumerates the modules loaded by a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. + * \param Callback A callback function which is executed for each process module. + * \param Context A user-defined value to pass to the callback function. + */ +NTSTATUS PhEnumProcessModules( + _In_ HANDLE ProcessHandle, + _In_ PPH_ENUM_PROCESS_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + PH_ENUM_PROCESS_MODULES_PARAMETERS parameters; + + parameters.Callback = Callback; + parameters.Context = Context; + parameters.Flags = 0; + + return PhEnumProcessModulesEx(ProcessHandle, ¶meters); +} + +/** + * Enumerates the modules loaded by a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. If + * \c PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME is specified in \a Parameters, the handle should + * have PROCESS_QUERY_INFORMATION access. + * \param Parameters The enumeration parameters. + */ +NTSTATUS PhEnumProcessModulesEx( + _In_ HANDLE ProcessHandle, + _In_ PPH_ENUM_PROCESS_MODULES_PARAMETERS Parameters + ) +{ + return PhpEnumProcessModules( + ProcessHandle, + PhpEnumProcessModulesCallback, + Parameters, + NULL + ); +} + +typedef struct _SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT +{ + NTSTATUS Status; + PVOID BaseAddress; + ULONG LoadCount; +} SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT, *PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT; + +BOOLEAN NTAPI PhpSetProcessModuleLoadCountCallback( + _In_ HANDLE ProcessHandle, + _In_ PLDR_DATA_TABLE_ENTRY Entry, + _In_ PVOID AddressOfEntry, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ) +{ + PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context = Context1; + + if (Entry->DllBase == context->BaseAddress) + { + context->Status = NtWriteVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(AddressOfEntry, FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, ObsoleteLoadCount)), + &context->LoadCount, + sizeof(USHORT), + NULL + ); + + return FALSE; + } + + return TRUE; +} + +/** + * Sets the load count of a process module. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ and PROCESS_VM_WRITE access. + * \param BaseAddress The base address of a module. + * \param LoadCount The new load count of the module. + * + * \retval STATUS_DLL_NOT_FOUND The module was not found. + */ +NTSTATUS PhSetProcessModuleLoadCount( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_ ULONG LoadCount + ) +{ + NTSTATUS status; + SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context; + + context.Status = STATUS_DLL_NOT_FOUND; + context.BaseAddress = BaseAddress; + context.LoadCount = LoadCount; + + status = PhpEnumProcessModules( + ProcessHandle, + PhpSetProcessModuleLoadCountCallback, + &context, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + return context.Status; +} + +NTSTATUS PhpEnumProcessModules32( + _In_ HANDLE ProcessHandle, + _In_ PPHP_ENUM_PROCESS_MODULES32_CALLBACK Callback, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ) +{ + NTSTATUS status; + PPEB32 peb; + ULONG ldr; // PEB_LDR_DATA32 *32 + PEB_LDR_DATA32 pebLdrData; + ULONG startLink; // LIST_ENTRY32 *32 + ULONG currentLink; // LIST_ENTRY32 *32 + ULONG dataTableEntrySize; + LDR_DATA_TABLE_ENTRY32 currentEntry; + ULONG i; + + // Get the 32-bit PEB address. + status = PhGetProcessPeb32(ProcessHandle, &peb); + + if (!NT_SUCCESS(status)) + return status; + + if (!peb) + return STATUS_NOT_SUPPORTED; // not a WOW64 process + + // Read the address of the loader data. + status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(peb, FIELD_OFFSET(PEB32, Ldr)), + &ldr, + sizeof(ULONG), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Read the loader data. + status = NtReadVirtualMemory( + ProcessHandle, + UlongToPtr(ldr), + &pebLdrData, + sizeof(PEB_LDR_DATA32), + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + if (!pebLdrData.Initialized) + return STATUS_UNSUCCESSFUL; + + if (WindowsVersion >= WINDOWS_8) + dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN8_32; + else + dataTableEntrySize = LDR_DATA_TABLE_ENTRY_SIZE_WIN7_32; + + // Traverse the linked list (in load order). + + i = 0; + startLink = (ULONG)(ldr + FIELD_OFFSET(PEB_LDR_DATA32, InLoadOrderModuleList)); + currentLink = pebLdrData.InLoadOrderModuleList.Flink; + + while ( + currentLink != startLink && + i <= PH_ENUM_PROCESS_MODULES_LIMIT + ) + { + ULONG addressOfEntry; + + addressOfEntry = PtrToUlong(CONTAINING_RECORD(UlongToPtr(currentLink), LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks)); + status = NtReadVirtualMemory( + ProcessHandle, + UlongToPtr(addressOfEntry), + ¤tEntry, + dataTableEntrySize, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + // Make sure the entry is valid. + if (currentEntry.DllBase) + { + // Execute the callback. + if (!Callback( + ProcessHandle, + ¤tEntry, + addressOfEntry, + Context1, + Context2 + )) + break; + } + + currentLink = currentEntry.InLoadOrderLinks.Flink; + i++; + } + + return status; +} + +BOOLEAN NTAPI PhpEnumProcessModules32Callback( + _In_ HANDLE ProcessHandle, + _In_ PLDR_DATA_TABLE_ENTRY32 Entry, + _In_ ULONG AddressOfEntry, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ) +{ + static PH_STRINGREF system32String = PH_STRINGREF_INIT(L"\\system32\\"); + + PPH_ENUM_PROCESS_MODULES_PARAMETERS parameters; + BOOLEAN cont; + LDR_DATA_TABLE_ENTRY nativeEntry; + PPH_STRING mappedFileName; + PWSTR baseDllNameBuffer; + PWSTR fullDllNameBuffer; + PH_STRINGREF fullDllName; + PH_STRINGREF systemRootString; + + parameters = Context1; + + // Convert the 32-bit entry to a native-sized entry. + + memset(&nativeEntry, 0, sizeof(LDR_DATA_TABLE_ENTRY)); + nativeEntry.DllBase = UlongToPtr(Entry->DllBase); + nativeEntry.EntryPoint = UlongToPtr(Entry->EntryPoint); + nativeEntry.SizeOfImage = Entry->SizeOfImage; + UStr32ToUStr(&nativeEntry.FullDllName, &Entry->FullDllName); + UStr32ToUStr(&nativeEntry.BaseDllName, &Entry->BaseDllName); + nativeEntry.Flags = Entry->Flags; + nativeEntry.ObsoleteLoadCount = Entry->ObsoleteLoadCount; + nativeEntry.TlsIndex = Entry->TlsIndex; + nativeEntry.TimeDateStamp = Entry->TimeDateStamp; + nativeEntry.OriginalBase = Entry->OriginalBase; + nativeEntry.LoadTime = Entry->LoadTime; + nativeEntry.BaseNameHashValue = Entry->BaseNameHashValue; + nativeEntry.LoadReason = Entry->LoadReason; + nativeEntry.ParentDllBase = UlongToPtr(Entry->ParentDllBase); + + mappedFileName = NULL; + + if (parameters->Flags & PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME) + { + PhGetProcessMappedFileName(ProcessHandle, nativeEntry.DllBase, &mappedFileName); + } + + if (mappedFileName) + { + ULONG_PTR indexOfLastBackslash; + + PhStringRefToUnicodeString(&mappedFileName->sr, &nativeEntry.FullDllName); + indexOfLastBackslash = PhFindLastCharInString(mappedFileName, 0, OBJ_NAME_PATH_SEPARATOR); + + if (indexOfLastBackslash != -1) + { + nativeEntry.BaseDllName.Buffer = PTR_ADD_OFFSET(nativeEntry.FullDllName.Buffer, PTR_ADD_OFFSET(indexOfLastBackslash * sizeof(WCHAR), sizeof(WCHAR))); + nativeEntry.BaseDllName.Length = nativeEntry.FullDllName.Length - (USHORT)indexOfLastBackslash * sizeof(WCHAR) - sizeof(WCHAR); + nativeEntry.BaseDllName.MaximumLength = nativeEntry.BaseDllName.Length; + } + else + { + nativeEntry.BaseDllName = nativeEntry.FullDllName; + } + } + else + { + // Read the base DLL name string and add a null terminator. + + baseDllNameBuffer = PhAllocate(nativeEntry.BaseDllName.Length + sizeof(UNICODE_NULL)); + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + nativeEntry.BaseDllName.Buffer, + baseDllNameBuffer, + nativeEntry.BaseDllName.Length, + NULL + ))) + { + baseDllNameBuffer[nativeEntry.BaseDllName.Length / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + baseDllNameBuffer[0] = UNICODE_NULL; + nativeEntry.BaseDllName.Length = 0; + } + + nativeEntry.BaseDllName.Buffer = baseDllNameBuffer; + + // Read the full DLL name string and add a null terminator. + + fullDllNameBuffer = PhAllocate(nativeEntry.FullDllName.Length + sizeof(UNICODE_NULL)); + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + nativeEntry.FullDllName.Buffer, + fullDllNameBuffer, + nativeEntry.FullDllName.Length, + NULL + ))) + { + fullDllNameBuffer[nativeEntry.FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL; + + if (!(parameters->Flags & PH_ENUM_PROCESS_MODULES_DONT_RESOLVE_WOW64_FS)) + { + // WOW64 file system redirection - convert "system32" to "SysWOW64". + if (!(nativeEntry.FullDllName.Length & 1)) // validate the string length + { + fullDllName.Buffer = fullDllNameBuffer; + fullDllName.Length = nativeEntry.FullDllName.Length; + + PhGetSystemRoot(&systemRootString); + + if (PhStartsWithStringRef(&fullDllName, &systemRootString, TRUE)) + { + PhSkipStringRef(&fullDllName, systemRootString.Length); + + if (PhStartsWithStringRef(&fullDllName, &system32String, TRUE)) + { + fullDllName.Buffer[1] = 'S'; + fullDllName.Buffer[4] = 'W'; + fullDllName.Buffer[5] = 'O'; + fullDllName.Buffer[6] = 'W'; + fullDllName.Buffer[7] = '6'; + fullDllName.Buffer[8] = '4'; + } + } + } + } + } + else + { + fullDllNameBuffer[0] = UNICODE_NULL; + nativeEntry.FullDllName.Length = 0; + } + + nativeEntry.FullDllName.Buffer = fullDllNameBuffer; + } + + if (WindowsVersion >= WINDOWS_8) + { + LDR_DDAG_NODE32 ldrDagNode32 = { 0 }; + + if (NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + UlongToPtr(Entry->DdagNode), + &ldrDagNode32, + sizeof(LDR_DDAG_NODE32), + NULL + ))) + { + // Fixup the module load count. (dmex) + nativeEntry.ObsoleteLoadCount = (USHORT)ldrDagNode32.LoadCount; + } + } + + // Execute the callback. + cont = parameters->Callback(&nativeEntry, parameters->Context); + + if (mappedFileName) + { + PhDereferenceObject(mappedFileName); + } + else + { + PhFree(baseDllNameBuffer); + PhFree(fullDllNameBuffer); + } + + return cont; +} + +/** + * Enumerates the 32-bit modules loaded by a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. + * \param Callback A callback function which is executed for each process module. + * \param Context A user-defined value to pass to the callback function. + * + * \retval STATUS_NOT_SUPPORTED The process is not running under WOW64. + * + * \remarks Do not use this function under a 32-bit environment. + */ +NTSTATUS PhEnumProcessModules32( + _In_ HANDLE ProcessHandle, + _In_ PPH_ENUM_PROCESS_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + PH_ENUM_PROCESS_MODULES_PARAMETERS parameters; + + parameters.Callback = Callback; + parameters.Context = Context; + parameters.Flags = 0; + + return PhEnumProcessModules32Ex(ProcessHandle, ¶meters); +} + +/** + * Enumerates the 32-bit modules loaded by a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. If + * \c PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME is specified in \a Parameters, the handle should + * have PROCESS_QUERY_INFORMATION access. + * \param Parameters The enumeration parameters. + * + * \retval STATUS_NOT_SUPPORTED The process is not running under WOW64. + * + * \remarks Do not use this function under a 32-bit environment. + */ +NTSTATUS PhEnumProcessModules32Ex( + _In_ HANDLE ProcessHandle, + _In_ PPH_ENUM_PROCESS_MODULES_PARAMETERS Parameters + ) +{ + return PhpEnumProcessModules32( + ProcessHandle, + PhpEnumProcessModules32Callback, + Parameters, + NULL + ); +} + +BOOLEAN NTAPI PhpSetProcessModuleLoadCount32Callback( + _In_ HANDLE ProcessHandle, + _In_ PLDR_DATA_TABLE_ENTRY32 Entry, + _In_ ULONG AddressOfEntry, + _In_opt_ PVOID Context1, + _In_opt_ PVOID Context2 + ) +{ + PSET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context = Context1; + + if (UlongToPtr(Entry->DllBase) == context->BaseAddress) + { + context->Status = NtWriteVirtualMemory( + ProcessHandle, + UlongToPtr(AddressOfEntry + FIELD_OFFSET(LDR_DATA_TABLE_ENTRY32, ObsoleteLoadCount)), + &context->LoadCount, + sizeof(USHORT), + NULL + ); + + return FALSE; + } + + return TRUE; +} + +/** + * Sets the load count of a 32-bit process module. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ and PROCESS_VM_WRITE access. + * \param BaseAddress The base address of a module. + * \param LoadCount The new load count of the module. + * + * \retval STATUS_DLL_NOT_FOUND The module was not found. + * \retval STATUS_NOT_SUPPORTED The process is not running under WOW64. + * + * \remarks Do not use this function under a 32-bit environment. + */ +NTSTATUS PhSetProcessModuleLoadCount32( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_ ULONG LoadCount + ) +{ + NTSTATUS status; + SET_PROCESS_MODULE_LOAD_COUNT_CONTEXT context; + + context.Status = STATUS_DLL_NOT_FOUND; + context.BaseAddress = BaseAddress; + context.LoadCount = LoadCount; + + status = PhpEnumProcessModules32( + ProcessHandle, + PhpSetProcessModuleLoadCount32Callback, + &context, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + return context.Status; +} + +PVOID PhGetDllHandle( + _In_ PWSTR DllName + ) +{ + return PhGetLoaderEntryDllBase(DllName); + + //UNICODE_STRING dllName; + //PVOID dllHandle; + // + //RtlInitUnicodeString(&dllName, DllName); + // + //if (NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &dllName, &dllHandle))) + // return dllHandle; + //else + // return NULL; +} + +PVOID PhGetModuleProcAddress( + _In_ PWSTR ModuleName, + _In_ PSTR ProcedureName + ) +{ + return PhGetDllProcedureAddress(ModuleName, ProcedureName, 0); + + //PVOID module; + // + //module = PhGetDllHandle(ModuleName); + // + //if (module) + // return PhGetProcedureAddress(module, ProcName, 0); + //else + // return NULL; +} + +PVOID PhGetProcedureAddress( + _In_ PVOID DllHandle, + _In_opt_ PSTR ProcedureName, + _In_opt_ ULONG ProcedureNumber + ) +{ + return PhGetDllBaseProcedureAddress(DllHandle, ProcedureName, (USHORT)ProcedureNumber); + + //NTSTATUS status; + //ANSI_STRING procedureName; + //PVOID procedureAddress; + // + //if (ProcedureName) + //{ + // RtlInitAnsiString(&procedureName, ProcedureName); + // status = LdrGetProcedureAddress( + // DllHandle, + // &procedureName, + // 0, + // &procedureAddress + // ); + //} + //else + //{ + // status = LdrGetProcedureAddress( + // DllHandle, + // NULL, + // ProcedureNumber, + // &procedureAddress + // ); + //} + // + //if (!NT_SUCCESS(status)) + // return NULL; + // + //return procedureAddress; +} + +typedef struct _GET_PROCEDURE_ADDRESS_REMOTE_CONTEXT +{ + PH_STRINGREF FileName; + PVOID DllBase; +} GET_PROCEDURE_ADDRESS_REMOTE_CONTEXT, *PGET_PROCEDURE_ADDRESS_REMOTE_CONTEXT; + +static BOOLEAN PhpGetProcedureAddressRemoteCallback( + _In_ PLDR_DATA_TABLE_ENTRY Module, + _In_opt_ PVOID Context + ) +{ + PGET_PROCEDURE_ADDRESS_REMOTE_CONTEXT context = Context; + PH_STRINGREF fullDllName; + + PhUnicodeStringToStringRef(&Module->FullDllName, &fullDllName); + + if (PhEqualStringRef(&fullDllName, &context->FileName, TRUE)) + { + context->DllBase = Module->DllBase; + return FALSE; + } + + return TRUE; +} + +/** + * Gets the address of a procedure in a process. + * + * \param ProcessHandle A handle to a process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. + * \param FileName The file name of the DLL containing the procedure. + * \param ProcedureName The name of the procedure. + * \param ProcedureNumber The ordinal of the procedure. + * \param ProcedureAddress A variable which receives the address of the procedure in the address + * space of the process. + * \param DllBase A variable which receives the base address of the DLL containing the procedure. + */ +NTSTATUS PhGetProcedureAddressRemote( + _In_ HANDLE ProcessHandle, + _In_ PWSTR FileName, + _In_opt_ PSTR ProcedureName, + _In_opt_ ULONG ProcedureNumber, + _Out_ PVOID *ProcedureAddress, + _Out_opt_ PVOID *DllBase + ) +{ + NTSTATUS status; + PH_MAPPED_IMAGE mappedImage; + PH_MAPPED_IMAGE_EXPORTS exports; + GET_PROCEDURE_ADDRESS_REMOTE_CONTEXT context; + + if (!NT_SUCCESS(status = PhLoadMappedImage(FileName, NULL, TRUE, &mappedImage))) + return status; + + PhInitializeStringRef(&context.FileName, FileName); + context.DllBase = NULL; + + if (mappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { +#ifdef _WIN64 + status = PhEnumProcessModules32(ProcessHandle, PhpGetProcedureAddressRemoteCallback, &context); +#else + status = PhEnumProcessModules(ProcessHandle, PhpGetProcedureAddressRemoteCallback, &context); +#endif + } + else + { +#ifdef _WIN64 + status = PhEnumProcessModules(ProcessHandle, PhpGetProcedureAddressRemoteCallback, &context); +#else + status = STATUS_NOT_SUPPORTED; +#endif + } + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + if (!NT_SUCCESS(status = PhGetMappedImageExports(&exports, &mappedImage))) + goto CleanupExit; + + status = PhGetMappedImageExportFunctionRemote( + &exports, + ProcedureName, + (USHORT)ProcedureNumber, + context.DllBase, + ProcedureAddress + ); + + if (NT_SUCCESS(status)) + { + if (DllBase) + *DllBase = context.DllBase; + } + +CleanupExit: + PhUnloadMappedImage(&mappedImage); + + return status; +} + +/** + * Enumerates the modules loaded by the kernel. + * + * \param Modules A variable which receives a pointer to a structure containing information about + * the kernel modules. You must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhEnumKernelModules( + _Out_ PRTL_PROCESS_MODULES *Modules + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 2048; + + buffer = PhAllocate(bufferSize); + + status = NtQuerySystemInformation( + SystemModuleInformation, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + status = NtQuerySystemInformation( + SystemModuleInformation, + buffer, + bufferSize, + &bufferSize + ); + } + + if (!NT_SUCCESS(status)) + return status; + + *Modules = buffer; + + return status; +} + +/** + * Enumerates the modules loaded by the kernel. + * + * \param Modules A variable which receives a pointer to a structure containing information about + * the kernel modules. You must free the structure using PhFree() when you no longer need it. + */ +NTSTATUS PhEnumKernelModulesEx( + _Out_ PRTL_PROCESS_MODULE_INFORMATION_EX *Modules + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 2048; + + buffer = PhAllocate(bufferSize); + + status = NtQuerySystemInformation( + SystemModuleInformationEx, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + status = NtQuerySystemInformation( + SystemModuleInformationEx, + buffer, + bufferSize, + &bufferSize + ); + } + + if (!NT_SUCCESS(status)) + return status; + + *Modules = buffer; + + return status; +} + +/** + * Gets the file name of the kernel image. + * + * \return A pointer to a string containing the kernel image file name. You must free the string + * using PhDereferenceObject() when you no longer need it. + */ +PPH_STRING PhGetKernelFileName( + VOID + ) +{ + PRTL_PROCESS_MODULES modules; + PPH_STRING fileName = NULL; + + if (!NT_SUCCESS(PhEnumKernelModules(&modules))) + return NULL; + + if (modules->NumberOfModules >= 1) + { + fileName = PhConvertMultiByteToUtf16(modules->Modules[0].FullPathName); + } + + PhFree(modules); + + return fileName; +} + +/** + * Enumerates the running processes. + * + * \param Processes A variable which receives a pointer to a buffer containing process information. + * You must free the buffer using PhFree() when you no longer need it. + * + * \remarks You can use the \ref PH_FIRST_PROCESS and \ref PH_NEXT_PROCESS macros to process the + * information contained in the buffer. + */ +NTSTATUS PhEnumProcesses( + _Out_ PVOID *Processes + ) +{ + return PhEnumProcessesEx(Processes, SystemProcessInformation); +} + +/** + * Enumerates the running processes. + * + * \param Processes A variable which receives a pointer to a buffer containing process information. + * You must free the buffer using PhFree() when you no longer need it. + * + * \remarks You can use the \ref PH_FIRST_PROCESS and \ref PH_NEXT_PROCESS macros to process the + * information contained in the buffer. + */ +NTSTATUS PhEnumProcessesEx( + _Out_ PVOID *Processes, + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass + ) +{ + static ULONG initialBufferSize[3] = { 0x4000, 0x4000, 0x4000 }; + NTSTATUS status; + ULONG classIndex; + PVOID buffer; + ULONG bufferSize; + + switch (SystemInformationClass) + { + case SystemProcessInformation: + classIndex = 0; + break; + case SystemExtendedProcessInformation: + classIndex = 1; + break; + case SystemFullProcessInformation: + classIndex = 2; + break; + default: + return STATUS_INVALID_INFO_CLASS; + } + + bufferSize = initialBufferSize[classIndex]; + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + status = NtQuerySystemInformation( + SystemInformationClass, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + if (bufferSize <= 0x100000) initialBufferSize[classIndex] = bufferSize; + *Processes = buffer; + + return status; +} + +/** + * Enumerates the running processes for a session. + * + * \param Processes A variable which receives a pointer to a buffer containing process information. + * You must free the buffer using PhFree() when you no longer need it. + * \param SessionId A session ID. + * + * \remarks You can use the \ref PH_FIRST_PROCESS and \ref PH_NEXT_PROCESS macros to process the + * information contained in the buffer. + */ +NTSTATUS PhEnumProcessesForSession( + _Out_ PVOID *Processes, + _In_ ULONG SessionId + ) +{ + static ULONG initialBufferSize = 0x4000; + NTSTATUS status; + SYSTEM_SESSION_PROCESS_INFORMATION sessionProcessInfo; + PVOID buffer; + ULONG bufferSize; + + bufferSize = initialBufferSize; + buffer = PhAllocate(bufferSize); + + sessionProcessInfo.SessionId = SessionId; + + while (TRUE) + { + sessionProcessInfo.SizeOfBuf = bufferSize; + sessionProcessInfo.Buffer = buffer; + + status = NtQuerySystemInformation( + SystemSessionProcessInformation, + &sessionProcessInfo, + sizeof(SYSTEM_SESSION_PROCESS_INFORMATION), + &bufferSize // size of the inner buffer gets returned + ); + + if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + if (bufferSize <= 0x100000) initialBufferSize = bufferSize; + *Processes = buffer; + + return status; +} + +/** + * Finds the process information structure for a specific process. + * + * \param Processes A pointer to a buffer returned by PhEnumProcesses(). + * \param ProcessId The ID of the process. + * + * \return A pointer to the process information structure for the specified process, or NULL if the + * structure could not be found. + */ +PSYSTEM_PROCESS_INFORMATION PhFindProcessInformation( + _In_ PVOID Processes, + _In_ HANDLE ProcessId + ) +{ + PSYSTEM_PROCESS_INFORMATION process; + + process = PH_FIRST_PROCESS(Processes); + + do + { + if (process->UniqueProcessId == ProcessId) + return process; + } while (process = PH_NEXT_PROCESS(process)); + + return NULL; +} + +/** + * Finds the process information structure for a specific process. + * + * \param Processes A pointer to a buffer returned by PhEnumProcesses(). + * \param ImageName The image name to search for. + * + * \return A pointer to the process information structure for the specified process, or NULL if the + * structure could not be found. + */ +PSYSTEM_PROCESS_INFORMATION PhFindProcessInformationByImageName( + _In_ PVOID Processes, + _In_ PPH_STRINGREF ImageName + ) +{ + PSYSTEM_PROCESS_INFORMATION process; + PH_STRINGREF processImageName; + + process = PH_FIRST_PROCESS(Processes); + + do + { + PhUnicodeStringToStringRef(&process->ImageName, &processImageName); + + if (PhEqualStringRef(&processImageName, ImageName, TRUE)) + return process; + } while (process = PH_NEXT_PROCESS(process)); + + return NULL; +} + +/** + * Enumerates all open handles. + * + * \param Handles A variable which receives a pointer to a structure containing information about + * all opened handles. You must free the structure using PhFree() when you no longer need it. + * + * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. + */ +NTSTATUS PhEnumHandles( + _Out_ PSYSTEM_HANDLE_INFORMATION *Handles + ) +{ + static ULONG initialBufferSize = 0x4000; + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + + bufferSize = initialBufferSize; + buffer = PhAllocate(bufferSize); + + while ((status = NtQuerySystemInformation( + SystemHandleInformation, + buffer, + bufferSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + if (bufferSize <= 0x100000) initialBufferSize = bufferSize; + *Handles = (PSYSTEM_HANDLE_INFORMATION)buffer; + + return status; +} + +/** + * Enumerates all open handles. + * + * \param Handles A variable which receives a pointer to a structure containing information about + * all opened handles. You must free the structure using PhFree() when you no longer need it. + * + * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. + * + * \remarks This function is only available starting with Windows XP. + */ +NTSTATUS PhEnumHandlesEx( + _Out_ PSYSTEM_HANDLE_INFORMATION_EX *Handles + ) +{ + static ULONG initialBufferSize = 0x10000; + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + + bufferSize = initialBufferSize; + buffer = PhAllocate(bufferSize); + + while ((status = NtQuerySystemInformation( + SystemExtendedHandleInformation, + buffer, + bufferSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + if (bufferSize <= 0x200000) initialBufferSize = bufferSize; + *Handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer; + + return status; +} + +/** + * Enumerates all open handles. + * + * \param ProcessHandle A handle to the process. The handle must have PROCESS_QUERY_INFORMATION access. + * \param Handles A variable which receives a pointer to a structure containing information about + * handles opened by the process. You must free the structure using PhFree() when you no longer need it. + * + * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. + * + * \remarks This function is only available starting with Windows 8. + */ +NTSTATUS PhEnumHandlesEx2( + _In_ HANDLE ProcessHandle, + _Out_ PPROCESS_HANDLE_SNAPSHOT_INFORMATION *Handles + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + ULONG returnLength = 0; + ULONG attempts = 0; + + bufferSize = 0x8000; + buffer = PhAllocate(bufferSize); + memset(buffer, 0, bufferSize); + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessHandleInformation, + buffer, + bufferSize, + &returnLength + ); + + while (status == STATUS_INFO_LENGTH_MISMATCH && attempts < 8) + { + PhFree(buffer); + bufferSize = returnLength; + buffer = PhAllocate(bufferSize); + memset(buffer, 0, bufferSize); + + status = NtQueryInformationProcess( + ProcessHandle, + ProcessHandleInformation, + buffer, + bufferSize, + &returnLength + ); + attempts++; + } + + if (NT_SUCCESS(status)) + *Handles = buffer; + else + PhFree(buffer); + + return status; +} + +/** + * Enumerates all pagefiles. + * + * \param Pagefiles A variable which receives a pointer to a buffer containing information about all + * active pagefiles. You must free the structure using PhFree() when you no longer need it. + * + * \retval STATUS_INSUFFICIENT_RESOURCES The handle information returned by the kernel is too large. + */ +NTSTATUS PhEnumPagefiles( + _Out_ PVOID *Pagefiles + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x200; + + buffer = PhAllocate(bufferSize); + + while ((status = NtQuerySystemInformation( + SystemPageFileInformation, + buffer, + bufferSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *Pagefiles = buffer; + + return status; +} + +NTSTATUS PhEnumPagefilesEx( + _Out_ PVOID *Pagefiles + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x200; + + buffer = PhAllocate(bufferSize); + + while ((status = NtQuerySystemInformation( + SystemPageFileInformationEx, + buffer, + bufferSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + return STATUS_INSUFFICIENT_RESOURCES; + + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *Pagefiles = buffer; + + return status; +} + +/** + * Gets the file name of a process' image. + * + * \param ProcessId The ID of the process. + * \param FileName A variable which receives a pointer to a string containing the file name. You + * must free the string using PhDereferenceObject() when you no longer need it. + * + * \remarks This function only works on Windows Vista and above. There does not appear to be any + * access checking performed by the kernel for this. + */ +NTSTATUS PhGetProcessImageFileNameByProcessId( + _In_ HANDLE ProcessId, + _Out_ PPH_STRING *FileName + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x100; + SYSTEM_PROCESS_ID_INFORMATION processIdInfo; + + buffer = PhAllocate(bufferSize); + + processIdInfo.ProcessId = ProcessId; + processIdInfo.ImageName.Length = 0; + processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize; + processIdInfo.ImageName.Buffer = buffer; + + status = NtQuerySystemInformation( + SystemProcessIdInformation, + &processIdInfo, + sizeof(SYSTEM_PROCESS_ID_INFORMATION), + NULL + ); + + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + // Required length is stored in MaximumLength. + + PhFree(buffer); + buffer = PhAllocate(processIdInfo.ImageName.MaximumLength); + processIdInfo.ImageName.Buffer = buffer; + + status = NtQuerySystemInformation( + SystemProcessIdInformation, + &processIdInfo, + sizeof(SYSTEM_PROCESS_ID_INFORMATION), + NULL + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *FileName = PhCreateStringFromUnicodeString(&processIdInfo.ImageName); + PhFree(buffer); + + return status; +} + +/** + * Determines if a process is managed. + * + * \param ProcessId The ID of the process. + * \param IsDotNet A variable which receives a boolean indicating whether the process is managed. + */ +NTSTATUS PhGetProcessIsDotNet( + _In_ HANDLE ProcessId, + _Out_ PBOOLEAN IsDotNet + ) +{ + return PhGetProcessIsDotNetEx(ProcessId, NULL, 0, IsDotNet, NULL); +} + +BOOLEAN NTAPI PhpIsDotNetEnumProcessModulesCallback( + _In_ PLDR_DATA_TABLE_ENTRY Module, + _In_opt_ PVOID Context + ) +{ + static UNICODE_STRING clrString = RTL_CONSTANT_STRING(L"clr.dll"); + static UNICODE_STRING mscorwksString = RTL_CONSTANT_STRING(L"mscorwks.dll"); + static UNICODE_STRING mscorsvrString = RTL_CONSTANT_STRING(L"mscorsvr.dll"); + static UNICODE_STRING mscorlibString = RTL_CONSTANT_STRING(L"mscorlib.dll"); + static UNICODE_STRING mscorlibNiString = RTL_CONSTANT_STRING(L"mscorlib.ni.dll"); + static UNICODE_STRING clrjitString = RTL_CONSTANT_STRING(L"clrjit.dll"); + static UNICODE_STRING frameworkString = RTL_CONSTANT_STRING(L"\\Microsoft.NET\\Framework\\"); + static UNICODE_STRING framework64String = RTL_CONSTANT_STRING(L"\\Microsoft.NET\\Framework64\\"); + + if ( + RtlEqualUnicodeString(&Module->BaseDllName, &clrString, TRUE) || + RtlEqualUnicodeString(&Module->BaseDllName, &mscorwksString, TRUE) || + RtlEqualUnicodeString(&Module->BaseDllName, &mscorsvrString, TRUE) + ) + { + UNICODE_STRING fileName; + PH_STRINGREF systemRootSr; + UNICODE_STRING systemRoot; + PUNICODE_STRING frameworkPart; + +#ifdef _WIN64 + if (*(PULONG)Context & PH_CLR_PROCESS_IS_WOW64) + { +#endif + frameworkPart = &frameworkString; +#ifdef _WIN64 + } + else + { + frameworkPart = &framework64String; + } +#endif + + fileName = Module->FullDllName; + PhGetSystemRoot(&systemRootSr); + PhStringRefToUnicodeString(&systemRootSr, &systemRoot); + + if (RtlPrefixUnicodeString(&systemRoot, &fileName, TRUE)) + { + fileName.Buffer = (PWCHAR)PTR_ADD_OFFSET(fileName.Buffer, systemRoot.Length); + fileName.Length -= systemRoot.Length; + + if (RtlPrefixUnicodeString(frameworkPart, &fileName, TRUE)) + { + fileName.Buffer = (PWCHAR)PTR_ADD_OFFSET(fileName.Buffer, frameworkPart->Length); + fileName.Length -= frameworkPart->Length; + + if (fileName.Length >= 4 * sizeof(WCHAR)) // vx.x + { + if (fileName.Buffer[1] == '1') + { + if (fileName.Buffer[3] == '0') + *(PULONG)Context |= PH_CLR_VERSION_1_0; + else if (fileName.Buffer[3] == '1') + *(PULONG)Context |= PH_CLR_VERSION_1_1; + } + else if (fileName.Buffer[1] == '2') + { + *(PULONG)Context |= PH_CLR_VERSION_2_0; + } + else if (fileName.Buffer[1] >= '4' && fileName.Buffer[1] <= '9') + { + *(PULONG)Context |= PH_CLR_VERSION_4_ABOVE; + } + } + } + } + } + else if ( + RtlEqualUnicodeString(&Module->BaseDllName, &mscorlibString, TRUE) || + RtlEqualUnicodeString(&Module->BaseDllName, &mscorlibNiString, TRUE) + ) + { + *(PULONG)Context |= PH_CLR_MSCORLIB_PRESENT; + } + else if (RtlEqualUnicodeString(&Module->BaseDllName, &clrjitString, TRUE)) + { + *(PULONG)Context |= PH_CLR_JIT_PRESENT; + } + + return TRUE; +} + +/** + * Determines if a process is managed. + * + * \param ProcessId The ID of the process. + * \param ProcessHandle An optional handle to the process. The handle must have + * PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access. + * \param InFlags A combination of flags. + * \li \c PH_CLR_USE_SECTION_CHECK Checks for the existence of related section objects to determine + * whether the process is managed. + * \li \c PH_CLR_NO_WOW64_CHECK Instead of a separate query, uses the presence of the + * \c PH_CLR_KNOWN_IS_WOW64 flag to determine whether the process is running under WOW64. + * \li \c PH_CLR_KNOWN_IS_WOW64 When \c PH_CLR_NO_WOW64_CHECK is specified, indicates that the + * process is managed. + * \param IsDotNet A variable which receives a boolean indicating whether the process is managed. + * \param Flags A variable which receives additional flags. + */ +NTSTATUS PhGetProcessIsDotNetEx( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle, + _In_ ULONG InFlags, + _Out_opt_ PBOOLEAN IsDotNet, + _Out_opt_ PULONG Flags + ) +{ + if (InFlags & PH_CLR_USE_SECTION_CHECK) + { + NTSTATUS status; + HANDLE sectionHandle; + OBJECT_ATTRIBUTES objectAttributes; + PPH_STRING sectionName; + UNICODE_STRING sectionNameUs; + PH_FORMAT format[2]; + + // Most .NET processes have a handle open to a section named + // \BaseNamedObjects\Cor_Private_IPCBlock(_v4)_. This is the same object used by + // the ICorPublish::GetProcess function. Instead of calling that function, we simply check + // for the existence of that section object. This means: + // * Better performance. + // * No need for admin rights to get .NET status of processes owned by other users. + + PhInitFormatU(&format[1], HandleToUlong(ProcessId)); + + // Version 4 section object + + PhInitFormatS(&format[0], L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_"); + sectionName = PhFormat(format, 2, 96); + PhStringRefToUnicodeString(§ionName->sr, §ionNameUs); + + InitializeObjectAttributes( + &objectAttributes, + §ionNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + status = NtOpenSection( + §ionHandle, + SECTION_QUERY, + &objectAttributes + ); + PhDereferenceObject(sectionName); + + if (NT_SUCCESS(status) || status == STATUS_ACCESS_DENIED) + { + if (NT_SUCCESS(status)) + NtClose(sectionHandle); + + if (IsDotNet) + *IsDotNet = TRUE; + + if (Flags) + *Flags = PH_CLR_VERSION_4_ABOVE; + + return STATUS_SUCCESS; + } + + // Version 2 section object + + PhInitFormatS(&format[0], L"\\BaseNamedObjects\\Cor_Private_IPCBlock_"); + sectionName = PhFormat(format, 2, 90); + PhStringRefToUnicodeString(§ionName->sr, §ionNameUs); + + InitializeObjectAttributes( + &objectAttributes, + §ionNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + status = NtOpenSection( + §ionHandle, + SECTION_QUERY, + &objectAttributes + ); + PhDereferenceObject(sectionName); + + if (NT_SUCCESS(status) || status == STATUS_ACCESS_DENIED) + { + if (NT_SUCCESS(status)) + NtClose(sectionHandle); + + if (IsDotNet) + *IsDotNet = TRUE; + + if (Flags) + *Flags = PH_CLR_VERSION_2_0; + + return STATUS_SUCCESS; + } + + return status; + } + else + { + NTSTATUS status; + HANDLE processHandle = NULL; + ULONG flags = 0; +#ifdef _WIN64 + BOOLEAN isWow64; +#endif + + if (!ProcessHandle) + { + if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, ProcessId))) + return status; + + ProcessHandle = processHandle; + } + +#ifdef _WIN64 + if (InFlags & PH_CLR_NO_WOW64_CHECK) + { + isWow64 = !!(InFlags & PH_CLR_KNOWN_IS_WOW64); + } + else + { + isWow64 = FALSE; + PhGetProcessIsWow64(ProcessHandle, &isWow64); + } + + if (isWow64) + { + flags |= PH_CLR_PROCESS_IS_WOW64; + status = PhEnumProcessModules32(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); + } + else + { +#endif + status = PhEnumProcessModules(ProcessHandle, PhpIsDotNetEnumProcessModulesCallback, &flags); +#ifdef _WIN64 + } +#endif + + if (processHandle) + NtClose(processHandle); + + if (IsDotNet) + *IsDotNet = (flags & PH_CLR_VERSION_MASK) && (flags & (PH_CLR_MSCORLIB_PRESENT | PH_CLR_JIT_PRESENT)); + + if (Flags) + *Flags = flags; + + return status; + } +} + +/** + * Enumerates the objects in a directory object. + * + * \param DirectoryHandle A handle to a directory. The handle must have DIRECTORY_QUERY access. + * \param Callback A callback function which is executed for each object. + * \param Context A user-defined value to pass to the callback function. + */ +NTSTATUS PhEnumDirectoryObjects( + _In_ HANDLE DirectoryHandle, + _In_ PPH_ENUM_DIRECTORY_OBJECTS Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + ULONG context = 0; + BOOLEAN firstTime = TRUE; + ULONG bufferSize; + POBJECT_DIRECTORY_INFORMATION buffer; + ULONG i; + BOOLEAN cont; + + bufferSize = 0x200; + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + // Get a batch of entries. + + while ((status = NtQueryDirectoryObject( + DirectoryHandle, + buffer, + bufferSize, + FALSE, + firstTime, + &context, + NULL + )) == STATUS_MORE_ENTRIES) + { + // Check if we have at least one entry. If not, we'll double the buffer size and try + // again. + if (buffer[0].Name.Buffer) + break; + + // Make sure we don't use too much memory. + if (bufferSize > PH_LARGE_BUFFER_SIZE) + { + PhFree(buffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + PhFree(buffer); + bufferSize *= 2; + buffer = PhAllocate(bufferSize); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + // Read the batch and execute the callback function for each object. + + i = 0; + cont = TRUE; + + while (TRUE) + { + POBJECT_DIRECTORY_INFORMATION info; + PH_STRINGREF name; + PH_STRINGREF typeName; + + info = &buffer[i]; + + if (!info->Name.Buffer) + break; + + PhUnicodeStringToStringRef(&info->Name, &name); + PhUnicodeStringToStringRef(&info->TypeName, &typeName); + + cont = Callback(&name, &typeName, Context); + + if (!cont) + break; + + i++; + } + + if (!cont) + break; + + if (status != STATUS_MORE_ENTRIES) + break; + + firstTime = FALSE; + } + + PhFree(buffer); + + return STATUS_SUCCESS; +} + +NTSTATUS PhEnumDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ PUNICODE_STRING SearchPattern, + _In_ PPH_ENUM_DIRECTORY_FILE Callback, + _In_opt_ PVOID Context + ) +{ + return PhEnumDirectoryFileEx( + FileHandle, + FileDirectoryInformation, + FALSE, + SearchPattern, + Callback, + Context + ); +} + +NTSTATUS PhEnumDirectoryFileEx( + _In_ HANDLE FileHandle, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING SearchPattern, + _In_ PPH_ENUM_DIRECTORY_FILE Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + BOOLEAN firstTime = TRUE; + PVOID buffer; + ULONG bufferSize = 0x400; + ULONG i; + BOOLEAN cont; + + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + // Query the directory, doubling the buffer each time NtQueryDirectoryFile fails. (wj32) + while (TRUE) + { + status = NtQueryDirectoryFile( + FileHandle, + NULL, + NULL, + NULL, + &isb, + buffer, + bufferSize, + FileInformationClass, + ReturnSingleEntry, + SearchPattern, + firstTime + ); + + // Our ISB is on the stack, so we have to wait for the operation to complete before continuing. (wj32) + if (status == STATUS_PENDING) + { + status = NtWaitForSingleObject(FileHandle, FALSE, NULL); + + if (NT_SUCCESS(status)) + status = isb.Status; + } + + if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + // If we don't have any entries to read, exit. (wj32) + if (status == STATUS_NO_MORE_FILES) + { + status = STATUS_SUCCESS; + break; + } + + if (!NT_SUCCESS(status)) + break; + + // Read the batch and execute the callback function for each file. (wj32) + + i = 0; + cont = TRUE; + + while (TRUE) + { + PFILE_NAMES_INFORMATION information; + + // HACK: Use the wrong structure for the NextEntryOffset. (dmex) + information = PTR_ADD_OFFSET(buffer, i); + + if (!Callback(information, Context)) + { + cont = FALSE; + break; + } + + if (information->NextEntryOffset != 0) + i += information->NextEntryOffset; + else + break; + } + + if (!cont) + break; + + firstTime = FALSE; + } + + PhFree(buffer); + + return status; +} + +NTSTATUS PhEnumFileExtendedAttributes( + _In_ HANDLE FileHandle, + _In_ PPH_ENUM_FILE_EA Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + BOOLEAN firstTime = TRUE; + BOOLEAN success = FALSE; + IO_STATUS_BLOCK isb; + ULONG bufferSize; + PVOID buffer; + PFILE_FULL_EA_INFORMATION i; + + bufferSize = 0x400; + buffer = PhAllocate(bufferSize); + + while (TRUE) + { + while (TRUE) + { + status = NtQueryEaFile( + FileHandle, + &isb, + buffer, + bufferSize, + FALSE, + NULL, + 0, + NULL, + firstTime + ); + + if (status == STATUS_PENDING) + { + status = NtWaitForSingleObject(FileHandle, FALSE, NULL); + + if (NT_SUCCESS(status)) + status = isb.Status; + } + + if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(buffer); + bufferSize *= 2; + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + if (status == STATUS_NO_MORE_FILES) + { + status = STATUS_SUCCESS; + break; + } + + if (!NT_SUCCESS(status)) + break; + + success = TRUE; + + for (i = PH_FIRST_FILE_EA(buffer); i; i = PH_NEXT_FILE_EA(i)) + { + if (!Callback(i, Context)) + { + success = FALSE; + break; + } + } + + if (!success) + break; + + firstTime = FALSE; + } + + PhFree(buffer); + + return status; +} + +NTSTATUS PhEnumFileStreams( + _In_ HANDLE FileHandle, + _Out_ PVOID *Streams + ) +{ + return PhpQueryFileVariableSize( + FileHandle, + FileStreamInformation, + Streams + ); +} + +NTSTATUS PhEnumFileStreamsEx( + _In_ HANDLE FileHandle, + _In_ PPH_ENUM_FILE_STREAMS Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PVOID buffer; + PFILE_STREAM_INFORMATION i; + + status = PhpQueryFileVariableSize( + FileHandle, + FileStreamInformation, + &buffer + ); + + if (NT_SUCCESS(status)) + { + for (i = PH_FIRST_STREAM(buffer); i; i = PH_NEXT_STREAM(i)) + { + if (!Callback(i, Context)) + break; + } + + PhFree(buffer); + } + + return status; +} + +NTSTATUS PhEnumFileHardLinks( + _In_ HANDLE FileHandle, + _Out_ PVOID *HardLinks + ) +{ + return PhpQueryFileVariableSize( + FileHandle, + FileHardLinkInformation, + HardLinks + ); +} + +NTSTATUS PhEnumFileHardLinksEx( + _In_ HANDLE FileHandle, + _In_ PPH_ENUM_FILE_HARDLINKS Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PFILE_LINKS_INFORMATION buffer; + PFILE_LINK_ENTRY_INFORMATION i; + + status = PhpQueryFileVariableSize( + FileHandle, + FileHardLinkInformation, + &buffer + ); + + if (NT_SUCCESS(status)) + { + for (i = PH_FIRST_LINK(&buffer->Entry); i; i = PH_NEXT_LINK(i)) + { + if (!Callback(i, Context)) + break; + } + + PhFree(buffer); + } + + return status; +} + +/** + * Initializes the device prefixes module. + */ +VOID PhpInitializeDevicePrefixes( + VOID + ) +{ + ULONG i; + PUCHAR buffer; + + // Allocate one buffer for all 26 prefixes to reduce overhead. + buffer = PhAllocate(PH_DEVICE_PREFIX_LENGTH * sizeof(WCHAR) * 26); + + for (i = 0; i < 26; i++) + { + PhDevicePrefixes[i].Length = 0; + PhDevicePrefixes[i].MaximumLength = PH_DEVICE_PREFIX_LENGTH * sizeof(WCHAR); + PhDevicePrefixes[i].Buffer = (PWCHAR)buffer; + buffer += PH_DEVICE_PREFIX_LENGTH * sizeof(WCHAR); + } +} + +VOID PhUpdateMupDevicePrefixes( + VOID + ) +{ + static PH_STRINGREF orderKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Control\\NetworkProvider\\Order"); + static PH_STRINGREF servicesStringPart = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + static PH_STRINGREF networkProviderStringPart = PH_STRINGREF_INIT(L"\\NetworkProvider"); + + HANDLE orderKeyHandle; + PPH_STRING providerOrder = NULL; + ULONG i; + PH_STRINGREF remainingPart; + PH_STRINGREF part; + + // The provider names are stored in the ProviderOrder value in this key: + // HKLM\System\CurrentControlSet\Control\NetworkProvider\Order + // Each name can then be looked up, its device name in the DeviceName value in: + // HKLM\System\CurrentControlSet\Services\\NetworkProvider + + // Note that we assume the providers only claim their device name. Some providers such as DFS + // claim an extra part, and are not resolved correctly here. + + if (NT_SUCCESS(PhOpenKey( + &orderKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &orderKeyName, + 0 + ))) + { + providerOrder = PhQueryRegistryString(orderKeyHandle, L"ProviderOrder"); + NtClose(orderKeyHandle); + } + + if (!providerOrder) + return; + + PhAcquireQueuedLockExclusive(&PhDeviceMupPrefixesLock); + + for (i = 0; i < PhDeviceMupPrefixesCount; i++) + { + PhDereferenceObject(PhDeviceMupPrefixes[i]); + PhDeviceMupPrefixes[i] = NULL; + } + + PhDeviceMupPrefixesCount = 0; + + PhDeviceMupPrefixes[PhDeviceMupPrefixesCount++] = PhCreateString(L"\\Device\\Mup"); + + // DFS claims an extra part of file names, which we don't handle. + // PhDeviceMupPrefixes[PhDeviceMupPrefixesCount++] = PhCreateString(L"\\Device\\DfsClient"); + + remainingPart = providerOrder->sr; + + while (remainingPart.Length != 0) + { + PPH_STRING serviceKeyName; + HANDLE networkProviderKeyHandle; + PPH_STRING deviceName; + + if (PhDeviceMupPrefixesCount == PH_DEVICE_MUP_PREFIX_MAX_COUNT) + break; + + PhSplitStringRefAtChar(&remainingPart, ',', &part, &remainingPart); + + if (part.Length != 0) + { + serviceKeyName = PhConcatStringRef3(&servicesStringPart, &part, &networkProviderStringPart); + + if (NT_SUCCESS(PhOpenKey( + &networkProviderKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &serviceKeyName->sr, + 0 + ))) + { + if (deviceName = PhQueryRegistryString(networkProviderKeyHandle, L"DeviceName")) + { + PhDeviceMupPrefixes[PhDeviceMupPrefixesCount] = deviceName; + PhDeviceMupPrefixesCount++; + } + + NtClose(networkProviderKeyHandle); + } + + PhDereferenceObject(serviceKeyName); + } + } + + PhReleaseQueuedLockExclusive(&PhDeviceMupPrefixesLock); + + PhDereferenceObject(providerOrder); +} + +/** + * Updates the DOS device names array. + */ +VOID PhUpdateDosDevicePrefixes( + VOID + ) +{ + WCHAR deviceNameBuffer[7] = L"\\??\\ :"; +#ifndef _WIN64 + PROCESS_DEVICEMAP_INFORMATION deviceMapInfo; +#else + PROCESS_DEVICEMAP_INFORMATION_EX deviceMapInfo; +#endif + memset(&deviceMapInfo, 0, sizeof(deviceMapInfo)); + + NtQueryInformationProcess( + NtCurrentProcess(), + ProcessDeviceMap, + &deviceMapInfo, + sizeof(deviceMapInfo), + NULL + ); + + for (ULONG i = 0; i < 0x1A; i++) + { + HANDLE linkHandle; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING deviceName; + + if (deviceMapInfo.Query.DriveMap) + { + if (!(deviceMapInfo.Query.DriveMap & (0x1 << i))) + continue; + } + + deviceNameBuffer[4] = (WCHAR)('A' + i); + deviceName.Buffer = deviceNameBuffer; + deviceName.Length = 6 * sizeof(WCHAR); + + InitializeObjectAttributes( + &oa, + &deviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (NT_SUCCESS(NtOpenSymbolicLinkObject( + &linkHandle, + SYMBOLIC_LINK_QUERY, + &oa + ))) + { + PhAcquireQueuedLockExclusive(&PhDevicePrefixesLock); + + if (!NT_SUCCESS(NtQuerySymbolicLinkObject( + linkHandle, + &PhDevicePrefixes[i], + NULL + ))) + { + PhDevicePrefixes[i].Length = 0; + } + + PhReleaseQueuedLockExclusive(&PhDevicePrefixesLock); + + NtClose(linkHandle); + } + else + { + PhDevicePrefixes[i].Length = 0; + } + } +} + +/** + * Resolves a NT path into a Win32 path. + * + * \param Name A string containing the path to resolve. + * + * \return A pointer to a string containing the Win32 path. You must free the string using + * PhDereferenceObject() when you no longer need it. + */ +PPH_STRING PhResolveDevicePrefix( + _In_ PPH_STRING Name + ) +{ + ULONG i; + PPH_STRING newName = NULL; + + if (PhBeginInitOnce(&PhDevicePrefixesInitOnce)) + { + PhpInitializeDevicePrefixes(); + PhUpdateDosDevicePrefixes(); + PhUpdateMupDevicePrefixes(); + + PhEndInitOnce(&PhDevicePrefixesInitOnce); + } + + // Go through the DOS devices and try to find a matching prefix. + for (i = 0; i < 26; i++) + { + BOOLEAN isPrefix = FALSE; + PH_STRINGREF prefix; + + PhAcquireQueuedLockShared(&PhDevicePrefixesLock); + + PhUnicodeStringToStringRef(&PhDevicePrefixes[i], &prefix); + + if (prefix.Length != 0) + { + if (PhStartsWithStringRef(&Name->sr, &prefix, TRUE)) + { + // To ensure we match the longest prefix, make sure the next character is a + // backslash or the path is equal to the prefix. + if (Name->Length == prefix.Length || Name->Buffer[prefix.Length / sizeof(WCHAR)] == OBJ_NAME_PATH_SEPARATOR) + { + isPrefix = TRUE; + } + } + } + + PhReleaseQueuedLockShared(&PhDevicePrefixesLock); + + if (isPrefix) + { + // :path + newName = PhCreateStringEx(NULL, 2 * sizeof(WCHAR) + Name->Length - prefix.Length); + newName->Buffer[0] = (WCHAR)('A' + i); + newName->Buffer[1] = ':'; + memcpy( + &newName->Buffer[2], + &Name->Buffer[prefix.Length / sizeof(WCHAR)], + Name->Length - prefix.Length + ); + + break; + } + } + + if (i == 26) + { + // Resolve network providers. + + PhAcquireQueuedLockShared(&PhDeviceMupPrefixesLock); + + for (i = 0; i < PhDeviceMupPrefixesCount; i++) + { + BOOLEAN isPrefix = FALSE; + SIZE_T prefixLength; + + prefixLength = PhDeviceMupPrefixes[i]->Length; + + if (prefixLength != 0) + { + if (PhStartsWithString(Name, PhDeviceMupPrefixes[i], TRUE)) + { + // To ensure we match the longest prefix, make sure the next character is a + // backslash. Don't resolve if the name *is* the prefix. Otherwise, we will end + // up with a useless string like "\". + if (Name->Length != prefixLength && Name->Buffer[prefixLength / sizeof(WCHAR)] == OBJ_NAME_PATH_SEPARATOR) + { + isPrefix = TRUE; + } + } + } + + if (isPrefix) + { + // \path + newName = PhCreateStringEx(NULL, 1 * sizeof(WCHAR) + Name->Length - prefixLength); + newName->Buffer[0] = OBJ_NAME_PATH_SEPARATOR; + memcpy( + &newName->Buffer[1], + &Name->Buffer[prefixLength / sizeof(WCHAR)], + Name->Length - prefixLength + ); + + break; + } + } + + PhReleaseQueuedLockShared(&PhDeviceMupPrefixesLock); + } + + if (newName) + PhTrimToNullTerminatorString(newName); + + return newName; +} + +/** + * Converts a file name into Win32 format. + * + * \param FileName A string containing a file name. + * + * \return A pointer to a string containing the Win32 file name. You must free the string using + * PhDereferenceObject() when you no longer need it. + * + * \remarks This function may convert NT object name paths to invalid ones. If the path to be + * converted is not necessarily a file name, use PhResolveDevicePrefix(). + */ +PPH_STRING PhGetFileName( + _In_ PPH_STRING FileName + ) +{ + PPH_STRING newFileName; + + newFileName = FileName; + + // "\??\" refers to \GLOBAL??\. Just remove it. + if (PhStartsWithString2(FileName, L"\\??\\", FALSE)) + { + newFileName = PhCreateStringEx(NULL, FileName->Length - 4 * sizeof(WCHAR)); + memcpy(newFileName->Buffer, &FileName->Buffer[4], FileName->Length - 4 * sizeof(WCHAR)); + } + // "\SystemRoot" means "C:\Windows". + else if (PhStartsWithString2(FileName, L"\\SystemRoot", TRUE)) + { + PH_STRINGREF systemRoot; + + PhGetSystemRoot(&systemRoot); + newFileName = PhCreateStringEx(NULL, systemRoot.Length + FileName->Length - 11 * sizeof(WCHAR)); + memcpy(newFileName->Buffer, systemRoot.Buffer, systemRoot.Length); + memcpy(PTR_ADD_OFFSET(newFileName->Buffer, systemRoot.Length), &FileName->Buffer[11], FileName->Length - 11 * sizeof(WCHAR)); + } + // "system32\" means "C:\Windows\system32\". + else if (PhStartsWithString2(FileName, L"system32\\", TRUE)) + { + PH_STRINGREF systemRoot; + + PhGetSystemRoot(&systemRoot); + newFileName = PhCreateStringEx(NULL, systemRoot.Length + sizeof(UNICODE_NULL) + FileName->Length); + memcpy(newFileName->Buffer, systemRoot.Buffer, systemRoot.Length); + newFileName->Buffer[systemRoot.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; + memcpy(PTR_ADD_OFFSET(newFileName->Buffer, systemRoot.Length + sizeof(UNICODE_NULL)), FileName->Buffer, FileName->Length); + } + else if (FileName->Length != 0 && FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) + { + PPH_STRING resolvedName; + + resolvedName = PhResolveDevicePrefix(FileName); + + if (resolvedName) + { + newFileName = resolvedName; + } + else + { + // We didn't find a match. + // If the file name starts with "\Windows", prepend the system drive. + if (PhStartsWithString2(newFileName, L"\\Windows", TRUE)) + { + newFileName = PhCreateStringEx(NULL, FileName->Length + 2 * sizeof(WCHAR)); + newFileName->Buffer[0] = USER_SHARED_DATA->NtSystemRoot[0]; + newFileName->Buffer[1] = ':'; + memcpy(&newFileName->Buffer[2], FileName->Buffer, FileName->Length); + } + else + { + PhReferenceObject(newFileName); + } + } + } + else + { + // Just return the supplied file name. Note that we need to add a reference. + PhReferenceObject(newFileName); + } + + return newFileName; +} + +typedef struct _ENUM_GENERIC_PROCESS_MODULES_CONTEXT +{ + PPH_ENUM_GENERIC_MODULES_CALLBACK Callback; + PVOID Context; + ULONG Type; + PPH_HASHTABLE BaseAddressHashtable; + + ULONG LoadOrderIndex; +} ENUM_GENERIC_PROCESS_MODULES_CONTEXT, *PENUM_GENERIC_PROCESS_MODULES_CONTEXT; + +static BOOLEAN EnumGenericProcessModulesCallback( + _In_ PLDR_DATA_TABLE_ENTRY Module, + _In_opt_ PVOID Context + ) +{ + PENUM_GENERIC_PROCESS_MODULES_CONTEXT context; + PH_MODULE_INFO moduleInfo; + BOOLEAN cont; + + context = (PENUM_GENERIC_PROCESS_MODULES_CONTEXT)Context; + + // Check if we have a duplicate base address. + if (PhFindEntryHashtable(context->BaseAddressHashtable, &Module->DllBase)) + { + return TRUE; + } + else + { + PhAddEntryHashtable(context->BaseAddressHashtable, &Module->DllBase); + } + + moduleInfo.Type = context->Type; + moduleInfo.BaseAddress = Module->DllBase; + moduleInfo.Size = Module->SizeOfImage; + moduleInfo.EntryPoint = Module->EntryPoint; + moduleInfo.Flags = Module->Flags; + moduleInfo.LoadOrderIndex = (USHORT)(context->LoadOrderIndex++); + moduleInfo.LoadCount = Module->ObsoleteLoadCount; + + moduleInfo.Name = PhCreateStringFromUnicodeString(&Module->BaseDllName); + moduleInfo.OriginalFileName = PhCreateStringFromUnicodeString(&Module->FullDllName); + moduleInfo.FileName = PhGetFileName(moduleInfo.OriginalFileName); + + if (WindowsVersion >= WINDOWS_8) + { + moduleInfo.ParentBaseAddress = Module->ParentDllBase; + moduleInfo.LoadReason = (USHORT)Module->LoadReason; + moduleInfo.LoadTime = Module->LoadTime; + } + else + { + moduleInfo.ParentBaseAddress = NULL; + moduleInfo.LoadReason = USHRT_MAX; + moduleInfo.LoadTime.QuadPart = 0; + } + + cont = context->Callback(&moduleInfo, context->Context); + + PhDereferenceObject(moduleInfo.Name); + PhDereferenceObject(moduleInfo.FileName); + PhDereferenceObject(moduleInfo.OriginalFileName); + + return cont; +} + +VOID PhpRtlModulesToGenericModules( + _In_ PRTL_PROCESS_MODULES Modules, + _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context, + _In_ PPH_HASHTABLE BaseAddressHashtable + ) +{ + PRTL_PROCESS_MODULE_INFORMATION module; + ULONG i; + PH_MODULE_INFO moduleInfo; + BOOLEAN cont; + + for (i = 0; i < Modules->NumberOfModules; i++) + { + PPH_STRING fileName; + + module = &Modules->Modules[i]; + + // Check if we have a duplicate base address. + if (PhFindEntryHashtable(BaseAddressHashtable, &module->ImageBase)) + { + continue; + } + else + { + PhAddEntryHashtable(BaseAddressHashtable, &module->ImageBase); + } + + fileName = PhConvertMultiByteToUtf16(module->FullPathName); + + if ((ULONG_PTR)module->ImageBase <= PhSystemBasicInformation.MaximumUserModeAddress) + moduleInfo.Type = PH_MODULE_TYPE_MODULE; + else + moduleInfo.Type = PH_MODULE_TYPE_KERNEL_MODULE; + + moduleInfo.BaseAddress = module->ImageBase; + moduleInfo.Size = module->ImageSize; + moduleInfo.EntryPoint = NULL; + moduleInfo.Flags = module->Flags; + moduleInfo.Name = PhConvertMultiByteToUtf16(&module->FullPathName[module->OffsetToFileName]); + moduleInfo.FileName = PhGetFileName(fileName); // convert to DOS file name + moduleInfo.OriginalFileName = fileName; + moduleInfo.LoadOrderIndex = module->LoadOrderIndex; + moduleInfo.LoadCount = module->LoadCount; + moduleInfo.LoadReason = USHRT_MAX; + moduleInfo.LoadTime.QuadPart = 0; + moduleInfo.ParentBaseAddress = NULL; + + if (module->OffsetToFileName == 0) + { + static PH_STRINGREF driversString = PH_STRINGREF_INIT(L"\\System32\\Drivers\\"); + PH_STRINGREF systemRoot; + PPH_STRING newFileName; + + // We only have the file name, without a path. The driver must be in the default drivers + // directory. + PhGetSystemRoot(&systemRoot); + newFileName = PhConcatStringRef3(&systemRoot, &driversString, &moduleInfo.Name->sr); + PhMoveReference(&moduleInfo.FileName, newFileName); + } + + cont = Callback(&moduleInfo, Context); + + PhDereferenceObject(moduleInfo.Name); + PhDereferenceObject(moduleInfo.FileName); + PhDereferenceObject(moduleInfo.OriginalFileName); + + if (!cont) + break; + } +} + +VOID PhpRtlModulesExToGenericModules( + _In_ PRTL_PROCESS_MODULE_INFORMATION_EX Modules, + _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context, + _In_ PPH_HASHTABLE BaseAddressHashtable + ) +{ + PRTL_PROCESS_MODULE_INFORMATION_EX module; + PH_MODULE_INFO moduleInfo; + BOOLEAN cont; + + module = Modules; + + while (module->NextOffset != 0) + { + PPH_STRING fileName; + + // Check if we have a duplicate base address. + if (PhFindEntryHashtable(BaseAddressHashtable, &module->BaseInfo.ImageBase)) + { + continue; + } + else + { + PhAddEntryHashtable(BaseAddressHashtable, &module->BaseInfo.ImageBase); + } + + fileName = PhConvertMultiByteToUtf16(module->BaseInfo.FullPathName); + + if ((ULONG_PTR)module->BaseInfo.ImageBase <= PhSystemBasicInformation.MaximumUserModeAddress) + moduleInfo.Type = PH_MODULE_TYPE_MODULE; + else + moduleInfo.Type = PH_MODULE_TYPE_KERNEL_MODULE; + + moduleInfo.BaseAddress = module->BaseInfo.ImageBase; + moduleInfo.Size = module->BaseInfo.ImageSize; + moduleInfo.EntryPoint = NULL; + moduleInfo.Flags = module->BaseInfo.Flags; + moduleInfo.Name = PhConvertMultiByteToUtf16(&module->BaseInfo.FullPathName[module->BaseInfo.OffsetToFileName]); + moduleInfo.FileName = PhGetFileName(fileName); // convert to DOS file name + moduleInfo.LoadOrderIndex = module->BaseInfo.LoadOrderIndex; + moduleInfo.LoadCount = module->BaseInfo.LoadCount; + moduleInfo.LoadReason = USHRT_MAX; + moduleInfo.LoadTime.QuadPart = 0; + moduleInfo.ParentBaseAddress = NULL; + + PhDereferenceObject(fileName); + + cont = Callback(&moduleInfo, Context); + + PhDereferenceObject(moduleInfo.Name); + PhDereferenceObject(moduleInfo.FileName); + + if (!cont) + break; + + module = PTR_ADD_OFFSET(module, module->NextOffset); + } +} + +BOOLEAN PhpCallbackMappedFileOrImage( + _In_ PVOID AllocationBase, + _In_ SIZE_T AllocationSize, + _In_ ULONG Type, + _In_ PPH_STRING FileName, + _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context, + _In_ PPH_HASHTABLE BaseAddressHashtable + ) +{ + PH_MODULE_INFO moduleInfo; + BOOLEAN cont; + + moduleInfo.Type = Type; + moduleInfo.BaseAddress = AllocationBase; + moduleInfo.Size = (ULONG)AllocationSize; + moduleInfo.EntryPoint = NULL; + moduleInfo.Flags = 0; + moduleInfo.FileName = PhGetFileName(FileName); + moduleInfo.OriginalFileName = FileName; + moduleInfo.Name = PhGetBaseName(moduleInfo.FileName); + moduleInfo.LoadOrderIndex = USHRT_MAX; + moduleInfo.LoadCount = USHRT_MAX; + moduleInfo.LoadReason = USHRT_MAX; + moduleInfo.LoadTime.QuadPart = 0; + moduleInfo.ParentBaseAddress = NULL; + + cont = Callback(&moduleInfo, Context); + + PhDereferenceObject(moduleInfo.FileName); + PhDereferenceObject(moduleInfo.OriginalFileName); + PhDereferenceObject(moduleInfo.Name); + + return cont; +} + +VOID PhpEnumGenericMappedFilesAndImages( + _In_ HANDLE ProcessHandle, + _In_ ULONG Flags, + _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context, + _In_ PPH_HASHTABLE BaseAddressHashtable + ) +{ + BOOLEAN querySucceeded; + PVOID baseAddress; + MEMORY_BASIC_INFORMATION basicInfo; + + baseAddress = (PVOID)0; + + if (!NT_SUCCESS(NtQueryVirtualMemory( + ProcessHandle, + baseAddress, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + return; + } + + querySucceeded = TRUE; + + while (querySucceeded) + { + PVOID allocationBase; + SIZE_T allocationSize; + ULONG type; + PPH_STRING fileName; + BOOLEAN cont; + + if (basicInfo.Type == MEM_MAPPED || basicInfo.Type == MEM_IMAGE) + { + if (basicInfo.Type == MEM_MAPPED) + type = PH_MODULE_TYPE_MAPPED_FILE; + else + type = PH_MODULE_TYPE_MAPPED_IMAGE; + + // Find the total allocation size. + + allocationBase = basicInfo.AllocationBase; + allocationSize = 0; + + do + { + baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); + allocationSize += basicInfo.RegionSize; + + if (!NT_SUCCESS(NtQueryVirtualMemory( + ProcessHandle, + baseAddress, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + querySucceeded = FALSE; + break; + } + } while (basicInfo.AllocationBase == allocationBase); + + if ((type == PH_MODULE_TYPE_MAPPED_FILE && !(Flags & PH_ENUM_GENERIC_MAPPED_FILES)) || + (type == PH_MODULE_TYPE_MAPPED_IMAGE && !(Flags & PH_ENUM_GENERIC_MAPPED_IMAGES))) + { + // The user doesn't want this type of entry. + continue; + } + + // Check if we have a duplicate base address. + if (PhFindEntryHashtable(BaseAddressHashtable, &allocationBase)) + { + continue; + } + + if (!NT_SUCCESS(PhGetProcessMappedFileName( + ProcessHandle, + allocationBase, + &fileName + ))) + { + continue; + } + + PhAddEntryHashtable(BaseAddressHashtable, &allocationBase); + + cont = PhpCallbackMappedFileOrImage( + allocationBase, + allocationSize, + type, + fileName, + Callback, + Context, + BaseAddressHashtable + ); + + if (!cont) + break; + } + else + { + baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize); + + if (!NT_SUCCESS(NtQueryVirtualMemory( + ProcessHandle, + baseAddress, + MemoryBasicInformation, + &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL + ))) + { + querySucceeded = FALSE; + } + } + } +} + +BOOLEAN NTAPI PhpBaseAddressHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + return *(PVOID *)Entry1 == *(PVOID *)Entry2; +} + +ULONG NTAPI PhpBaseAddressHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)*(PVOID *)Entry); +} + +/** + * Enumerates the modules loaded by a process. + * + * \param ProcessId The ID of a process. If \ref SYSTEM_PROCESS_ID is specified the function + * enumerates the kernel modules. + * \param ProcessHandle A handle to the process. + * \param Flags Flags controlling the information to retrieve. + * \li \c PH_ENUM_GENERIC_MAPPED_FILES Enumerate mapped files. + * \li \c PH_ENUM_GENERIC_MAPPED_IMAGES Enumerate mapped images (those which are not mapped by the + * loader). + * \param Callback A callback function which is executed for each module. + * \param Context A user-defined value to pass to the callback function. + */ +NTSTATUS PhEnumGenericModules( + _In_ HANDLE ProcessId, + _In_opt_ HANDLE ProcessHandle, + _In_ ULONG Flags, + _In_ PPH_ENUM_GENERIC_MODULES_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PPH_HASHTABLE baseAddressHashtable; + + baseAddressHashtable = PhCreateHashtable( + sizeof(PVOID), + PhpBaseAddressHashtableEqualFunction, + PhpBaseAddressHashtableHashFunction, + 100 + ); + + if (ProcessId == SYSTEM_PROCESS_ID) + { + // Kernel modules + + PVOID modules; + + if (NT_SUCCESS(status = PhEnumKernelModules((PRTL_PROCESS_MODULES *)&modules))) + { + PhpRtlModulesToGenericModules( + modules, + Callback, + Context, + baseAddressHashtable + ); + PhFree(modules); + } + } + else + { + // Process modules + + BOOLEAN opened = FALSE; +#ifdef _WIN64 + BOOLEAN isWow64 = FALSE; +#endif + ENUM_GENERIC_PROCESS_MODULES_CONTEXT context; + PH_ENUM_PROCESS_MODULES_PARAMETERS parameters; + + if (!ProcessHandle) + { + if (!NT_SUCCESS(status = PhOpenProcess( + &ProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, // needed for enumerating mapped files + ProcessId + ))) + { + if (!NT_SUCCESS(status = PhOpenProcess( + &ProcessHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, + ProcessId + ))) + { + goto CleanupExit; + } + } + + opened = TRUE; + } + + context.Callback = Callback; + context.Context = Context; + context.Type = PH_MODULE_TYPE_MODULE; + context.BaseAddressHashtable = baseAddressHashtable; + context.LoadOrderIndex = 0; + + parameters.Callback = EnumGenericProcessModulesCallback; + parameters.Context = &context; + parameters.Flags = PH_ENUM_PROCESS_MODULES_TRY_MAPPED_FILE_NAME; + + status = PhEnumProcessModulesEx( + ProcessHandle, + ¶meters + ); + +#ifdef _WIN64 + PhGetProcessIsWow64(ProcessHandle, &isWow64); + + // 32-bit process modules + if (isWow64) + { + context.Callback = Callback; + context.Context = Context; + context.Type = PH_MODULE_TYPE_WOW64_MODULE; + context.BaseAddressHashtable = baseAddressHashtable; + context.LoadOrderIndex = 0; + + status = PhEnumProcessModules32Ex( + ProcessHandle, + ¶meters + ); + } +#endif + + // Mapped files and mapped images + // This is done last because it provides the least amount of information. + + if (Flags & (PH_ENUM_GENERIC_MAPPED_FILES | PH_ENUM_GENERIC_MAPPED_IMAGES)) + { + PhpEnumGenericMappedFilesAndImages( + ProcessHandle, + Flags, + Callback, + Context, + baseAddressHashtable + ); + } + + if (opened) + NtClose(ProcessHandle); + } + +CleanupExit: + PhDereferenceObject(baseAddressHashtable); + + return status; +} + +/** + * Initializes usage of predefined keys. + */ +VOID PhpInitializePredefineKeys( + VOID + ) +{ + static UNICODE_STRING currentUserPrefix = RTL_CONSTANT_STRING(L"\\Registry\\User\\"); + NTSTATUS status; + PTOKEN_USER tokenUser; + UNICODE_STRING stringSid; + WCHAR stringSidBuffer[SECURITY_MAX_SID_STRING_CHARACTERS]; + PUNICODE_STRING currentUserKeyName; + + // Get the string SID of the current user. + + if (NT_SUCCESS(status = PhGetTokenUser(PhGetOwnTokenAttributes().TokenHandle, &tokenUser))) + { + stringSid.Buffer = stringSidBuffer; + stringSid.MaximumLength = sizeof(stringSidBuffer); + + status = RtlConvertSidToUnicodeString( + &stringSid, + tokenUser->User.Sid, + FALSE + ); + + PhFree(tokenUser); + } + + // Construct the current user key name. + + if (NT_SUCCESS(status)) + { + currentUserKeyName = &PhPredefineKeyNames[PH_KEY_CURRENT_USER_NUMBER]; + currentUserKeyName->Length = currentUserPrefix.Length + stringSid.Length; + currentUserKeyName->Buffer = PhAllocate(currentUserKeyName->Length + sizeof(WCHAR)); + memcpy(currentUserKeyName->Buffer, currentUserPrefix.Buffer, currentUserPrefix.Length); + memcpy(¤tUserKeyName->Buffer[currentUserPrefix.Length / sizeof(WCHAR)], stringSid.Buffer, stringSid.Length); + } +} + +/** + * Initializes the attributes of a key object for creating/opening. + * + * \param RootDirectory A handle to a root key, or one of the predefined keys. See PhCreateKey() for + * details. + * \param ObjectName The path to the key. + * \param Attributes Additional object flags. + * \param ObjectAttributes The OBJECT_ATTRIBUTES structure to initialize. + * \param NeedsClose A variable which receives a handle that must be closed when the create/open + * operation is finished. The variable may be set to NULL if no handle needs to be closed. + */ +NTSTATUS PhpInitializeKeyObjectAttributes( + _In_opt_ HANDLE RootDirectory, + _In_ PUNICODE_STRING ObjectName, + _In_ ULONG Attributes, + _Out_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PHANDLE NeedsClose + ) +{ + NTSTATUS status; + ULONG predefineIndex; + HANDLE predefineHandle; + OBJECT_ATTRIBUTES predefineObjectAttributes; + + InitializeObjectAttributes( + ObjectAttributes, + ObjectName, + Attributes | OBJ_CASE_INSENSITIVE, + RootDirectory, + NULL + ); + + *NeedsClose = NULL; + + if (RootDirectory && PH_KEY_IS_PREDEFINED(RootDirectory)) + { + predefineIndex = PH_KEY_PREDEFINE_TO_NUMBER(RootDirectory); + + if (predefineIndex < PH_KEY_MAXIMUM_PREDEFINE) + { + if (PhBeginInitOnce(&PhPredefineKeyInitOnce)) + { + PhpInitializePredefineKeys(); + PhEndInitOnce(&PhPredefineKeyInitOnce); + } + + predefineHandle = PhPredefineKeyHandles[predefineIndex]; + + if (!predefineHandle) + { + // The predefined key has not been opened yet. Do so now. + + if (!PhPredefineKeyNames[predefineIndex].Buffer) // we may have failed in getting the current user key name + return STATUS_UNSUCCESSFUL; + + InitializeObjectAttributes( + &predefineObjectAttributes, + &PhPredefineKeyNames[predefineIndex], + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtOpenKey( + &predefineHandle, + KEY_READ, + &predefineObjectAttributes + ); + + if (!NT_SUCCESS(status)) + return status; + + if (_InterlockedCompareExchangePointer( + &PhPredefineKeyHandles[predefineIndex], + predefineHandle, + NULL + ) != NULL) + { + // Someone else already opened the key and cached it. Indicate that the caller + // needs to close the handle later, since it isn't shared. + *NeedsClose = predefineHandle; + } + } + + ObjectAttributes->RootDirectory = predefineHandle; + } + } + + return STATUS_SUCCESS; +} + +/** + * Creates or opens a registry key. + * + * \param KeyHandle A variable which receives a handle to the key. + * \param DesiredAccess The desired access to the key. + * \param RootDirectory A handle to a root key, or one of the following predefined keys: + * \li \c PH_KEY_LOCAL_MACHINE Represents \\Registry\\Machine. + * \li \c PH_KEY_USERS Represents \\Registry\\User. + * \li \c PH_KEY_CLASSES_ROOT Represents \\Registry\\Machine\\Software\\Classes. + * \li \c PH_KEY_CURRENT_USER Represents \\Registry\\User\\[SID of current user]. + * \param ObjectName The path to the key. + * \param Attributes Additional object flags. + * \param CreateOptions The options to apply when creating or opening the key. + * \param Disposition A variable which receives a value indicating whether a new key was created or + * an existing key was opened: + * \li \c REG_CREATED_NEW_KEY A new key was created. + * \li \c REG_OPENED_EXISTING_KEY An existing key was opened. + */ +NTSTATUS PhCreateKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ HANDLE RootDirectory, + _In_ PPH_STRINGREF ObjectName, + _In_ ULONG Attributes, + _In_ ULONG CreateOptions, + _Out_opt_ PULONG Disposition + ) +{ + NTSTATUS status; + UNICODE_STRING objectName; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE needsClose; + + if (!PhStringRefToUnicodeString(ObjectName, &objectName)) + return STATUS_NAME_TOO_LONG; + + if (!NT_SUCCESS(status = PhpInitializeKeyObjectAttributes( + RootDirectory, + &objectName, + Attributes, + &objectAttributes, + &needsClose + ))) + { + return status; + } + + status = NtCreateKey( + KeyHandle, + DesiredAccess, + &objectAttributes, + 0, + NULL, + CreateOptions, + Disposition + ); + + if (needsClose) + NtClose(needsClose); + + return status; +} + +/** + * Opens a registry key. + * + * \param KeyHandle A variable which receives a handle to the key. + * \param DesiredAccess The desired access to the key. + * \param RootDirectory A handle to a root key, or one of the predefined keys. See PhCreateKey() for + * details. + * \param ObjectName The path to the key. + * \param Attributes Additional object flags. + */ +NTSTATUS PhOpenKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ HANDLE RootDirectory, + _In_ PPH_STRINGREF ObjectName, + _In_ ULONG Attributes + ) +{ + NTSTATUS status; + UNICODE_STRING objectName; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE needsClose; + + if (!PhStringRefToUnicodeString(ObjectName, &objectName)) + return STATUS_NAME_TOO_LONG; + + if (!NT_SUCCESS(status = PhpInitializeKeyObjectAttributes( + RootDirectory, + &objectName, + Attributes, + &objectAttributes, + &needsClose + ))) + { + return status; + } + + status = NtOpenKey( + KeyHandle, + DesiredAccess, + &objectAttributes + ); + + if (needsClose) + NtClose(needsClose); + + return status; +} + +// rev from RegLoadAppKey +/** + * Loads the specified registry hive file into a private application hive. + * + * \param KeyHandle A variable which receives a handle to the key. + * \param FileName The Win32 file name. + * \param DesiredAccess The desired access to the key. + * \param Flags Optional flags for loading the hive. + */ +NTSTATUS PhLoadAppKey( + _Out_ PHANDLE KeyHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ ULONG Flags + ) +{ + NTSTATUS status; + GUID guid; + UNICODE_STRING fileName; + UNICODE_STRING objectName; + UNICODE_STRING guidStringUs; + OBJECT_ATTRIBUTES targetAttributes; + OBJECT_ATTRIBUTES sourceAttributes; + WCHAR objectNameBuffer[MAX_PATH]; + + RtlInitEmptyUnicodeString(&objectName, objectNameBuffer, sizeof(objectNameBuffer)); + + PhGenerateGuid(&guid); + + if (!NT_SUCCESS(status = RtlStringFromGUID(&guid, &guidStringUs))) + return status; + + if (!NT_SUCCESS(status = RtlAppendUnicodeToString(&objectName, L"\\REGISTRY\\A\\"))) + goto CleanupExit; + + if (!NT_SUCCESS(status = RtlAppendUnicodeStringToString(&objectName, &guidStringUs))) + goto CleanupExit; + + if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( + FileName, + &fileName, + NULL, + NULL + ))) + { + goto CleanupExit; + } + + InitializeObjectAttributes( + &targetAttributes, + &objectName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + InitializeObjectAttributes( + &sourceAttributes, + &fileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtLoadKeyEx( + &targetAttributes, + &sourceAttributes, + REG_APP_HIVE | Flags, + NULL, + NULL, + DesiredAccess, + KeyHandle, + NULL + ); + + RtlFreeUnicodeString(&fileName); + +CleanupExit: + RtlFreeUnicodeString(&guidStringUs); + + return status; +} + +/** + * Gets information about a registry key. + * + * \param KeyHandle A handle to the key. + * \param KeyInformationClass The information class to query. + * \param Buffer A variable which receives a pointer to a buffer containing information about the + * registry key. You must free the buffer with PhFree() when you no longer need it. + */ +NTSTATUS PhQueryKey( + _In_ HANDLE KeyHandle, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + ULONG attempts = 16; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + do + { + status = NtQueryKey( + KeyHandle, + KeyInformationClass, + buffer, + bufferSize, + &bufferSize + ); + + if (NT_SUCCESS(status)) + break; + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + PhFree(buffer); + return status; + } + } while (--attempts); + + *Buffer = buffer; + + return status; +} + +/** + * Gets a registry value of any type. + * + * \param KeyHandle A handle to the key. + * \param ValueName The name of the value. + * \param KeyValueInformationClass The information class to query. + * \param Buffer A variable which receives a pointer to a buffer containing information about the + * registry value. You must free the buffer with PhFree() when you no longer need it. + */ +NTSTATUS PhQueryValueKey( + _In_ HANDLE KeyHandle, + _In_opt_ PPH_STRINGREF ValueName, + _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + _Out_ PVOID *Buffer + ) +{ + NTSTATUS status; + UNICODE_STRING valueName; + PVOID buffer; + ULONG bufferSize; + ULONG attempts = 16; + + if (ValueName) + { + if (!PhStringRefToUnicodeString(ValueName, &valueName)) + return STATUS_NAME_TOO_LONG; + } + else + { + RtlInitEmptyUnicodeString(&valueName, NULL, 0); + } + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + do + { + status = NtQueryValueKey( + KeyHandle, + &valueName, + KeyValueInformationClass, + buffer, + bufferSize, + &bufferSize + ); + + if (NT_SUCCESS(status)) + break; + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + PhFree(buffer); + return status; + } + } while (--attempts); + + *Buffer = buffer; + + return status; +} + +NTSTATUS PhEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ PPH_ENUM_KEY_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + ULONG index = 0; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + do + { + status = NtEnumerateKey( + KeyHandle, + index, + KeyBasicInformation, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_NO_MORE_ENTRIES) + { + status = STATUS_SUCCESS; + break; + } + + if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + status = NtEnumerateKey( + KeyHandle, + index, + KeyBasicInformation, + buffer, + bufferSize, + &bufferSize + ); + } + + if (!NT_SUCCESS(status)) + break; + + if (!Callback(KeyHandle, buffer, Context)) + break; + + index++; + } while (TRUE); + + PhFree(buffer); + + return status; +} + +/** + * Creates or opens a file. + * + * \param FileHandle A variable that receives the file handle. + * \param FileName The Win32 file name. + * \param DesiredAccess The desired access to the file. + * \param FileAttributes File attributes applied if the file is created or overwritten. + * \param ShareAccess The file access granted to other threads. + * \li \c FILE_SHARE_READ Allows other threads to read from the file. + * \li \c FILE_SHARE_WRITE Allows other threads to write to the file. + * \li \c FILE_SHARE_DELETE Allows other threads to delete the file. + * \param CreateDisposition The action to perform if the file does or does not exist. + * \li \c FILE_SUPERSEDE If the file exists, replace it. Otherwise, create the file. + * \li \c FILE_CREATE If the file exists, fail. Otherwise, create the file. + * \li \c FILE_OPEN If the file exists, open it. Otherwise, fail. + * \li \c FILE_OPEN_IF If the file exists, open it. Otherwise, create the file. + * \li \c FILE_OVERWRITE If the file exists, open and overwrite it. Otherwise, fail. + * \li \c FILE_OVERWRITE_IF If the file exists, open and overwrite it. Otherwise, create the file. + * \param CreateOptions The options to apply when the file is opened or created. + */ +NTSTATUS PhCreateFileWin32( + _Out_ PHANDLE FileHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions + ) +{ + return PhCreateFileWin32Ex( + FileHandle, + FileName, + DesiredAccess, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + NULL + ); +} + +/** + * Creates or opens a file. + * + * \param FileHandle A variable that receives the file handle. + * \param FileName The Win32 file name. + * \param DesiredAccess The desired access to the file. + * \param FileAttributes File attributes applied if the file is created or overwritten. + * \param ShareAccess The file access granted to other threads. + * \li \c FILE_SHARE_READ Allows other threads to read from the file. + * \li \c FILE_SHARE_WRITE Allows other threads to write to the file. + * \li \c FILE_SHARE_DELETE Allows other threads to delete the file. + * \param CreateDisposition The action to perform if the file does or does not exist. + * \li \c FILE_SUPERSEDE If the file exists, replace it. Otherwise, create the file. + * \li \c FILE_CREATE If the file exists, fail. Otherwise, create the file. + * \li \c FILE_OPEN If the file exists, open it. Otherwise, fail. + * \li \c FILE_OPEN_IF If the file exists, open it. Otherwise, create the file. + * \li \c FILE_OVERWRITE If the file exists, open and overwrite it. Otherwise, fail. + * \li \c FILE_OVERWRITE_IF If the file exists, open and overwrite it. Otherwise, create the file. + * \param CreateOptions The options to apply when the file is opened or created. + * \param CreateStatus A variable that receives creation information. + * \li \c FILE_SUPERSEDED The file was replaced because \c FILE_SUPERSEDE was specified in + * \a CreateDisposition. + * \li \c FILE_OPENED The file was opened because \c FILE_OPEN or \c FILE_OPEN_IF was specified in + * \a CreateDisposition. + * \li \c FILE_CREATED The file was created because \c FILE_CREATE or \c FILE_OPEN_IF was specified + * in \a CreateDisposition. + * \li \c FILE_OVERWRITTEN The file was overwritten because \c FILE_OVERWRITE or + * \c FILE_OVERWRITE_IF was specified in \a CreateDisposition. + * \li \c FILE_EXISTS The file was not opened because it already existed and \c FILE_CREATE was + * specified in \a CreateDisposition. + * \li \c FILE_DOES_NOT_EXIST The file was not opened because it did not exist and \c FILE_OPEN or + * \c FILE_OVERWRITE was specified in \a CreateDisposition. + */ +NTSTATUS PhCreateFileWin32Ex( + _Out_ PHANDLE FileHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _Out_opt_ PULONG CreateStatus + ) +{ + NTSTATUS status; + HANDLE fileHandle; + UNICODE_STRING fileName; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + + if (!FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( + FileName, + &fileName, + NULL, + NULL + ))) + return status; + + InitializeObjectAttributes( + &oa, + &fileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtCreateFile( + &fileHandle, + DesiredAccess, + &oa, + &isb, + NULL, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + NULL, + 0 + ); + + RtlFreeUnicodeString(&fileName); + + if (NT_SUCCESS(status)) + { + *FileHandle = fileHandle; + } + + if (CreateStatus) + *CreateStatus = (ULONG)isb.Information; + + return status; +} + +NTSTATUS PhOpenFileWin32( + _Out_ PHANDLE FileHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions + ) +{ + return PhOpenFileWin32Ex( + FileHandle, + FileName, + DesiredAccess, + ShareAccess, + OpenOptions, + NULL + ); +} + +NTSTATUS PhOpenFileWin32Ex( + _Out_ PHANDLE FileHandle, + _In_ PWSTR FileName, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions, + _Out_opt_ PULONG OpenStatus + ) +{ + NTSTATUS status; + HANDLE fileHandle; + UNICODE_STRING fileNameUs; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK isb; + + if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( + FileName, + &fileNameUs, + NULL, + NULL + ))) + return status; + + InitializeObjectAttributes( + &objectAttributes, + &fileNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtOpenFile( + &fileHandle, + DesiredAccess, + &objectAttributes, + &isb, + ShareAccess, + OpenOptions + ); + + RtlFreeUnicodeString(&fileNameUs); + + if (NT_SUCCESS(status)) + { + *FileHandle = fileHandle; + } + + if (OpenStatus) + *OpenStatus = (ULONG)isb.Information; + + return status; +} + +/** + * Queries file attributes. + * + * \param FileName The Win32 file name. + * \param FileInformation A variable that receives the file information. + */ +NTSTATUS PhQueryFullAttributesFileWin32( + _In_ PWSTR FileName, + _Out_ PFILE_NETWORK_OPEN_INFORMATION FileInformation + ) +{ + NTSTATUS status; + UNICODE_STRING fileName; + OBJECT_ATTRIBUTES oa; + + if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( + FileName, + &fileName, + NULL, + NULL + ))) + return status; + + InitializeObjectAttributes( + &oa, + &fileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtQueryFullAttributesFile(&oa, FileInformation); + + RtlFreeUnicodeString(&fileName); + + return status; +} + +NTSTATUS PhQueryAttributesFileWin32( + _In_ PWSTR FileName, + _Out_ PFILE_BASIC_INFORMATION FileInformation + ) +{ + NTSTATUS status; + UNICODE_STRING fileName; + OBJECT_ATTRIBUTES oa; + + if (!NT_SUCCESS(status = RtlDosPathNameToNtPathName_U_WithStatus( + FileName, + &fileName, + NULL, + NULL + ))) + return status; + + InitializeObjectAttributes( + &oa, + &fileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtQueryAttributesFile(&oa, FileInformation); + + RtlFreeUnicodeString(&fileName); + + return status; +} + +BOOLEAN PhDoesFileExistsWin32( + _In_ PWSTR FileName + ) +{ + NTSTATUS status; + FILE_BASIC_INFORMATION basicInfo; + + status = PhQueryAttributesFileWin32(FileName, &basicInfo); + + if ( + NT_SUCCESS(status) || + status == STATUS_SHARING_VIOLATION || + status == STATUS_ACCESS_DENIED + ) + { + return TRUE; + } + + return FALSE; +} + +/** + * Deletes a file. + * + * \param FileName The Win32 file name. + */ +NTSTATUS PhDeleteFileWin32( + _In_ PWSTR FileName + ) +{ + NTSTATUS status; + HANDLE fileHandle; + + status = PhCreateFileWin32( + &fileHandle, + FileName, + DELETE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DELETE_ON_CLOSE + ); + + if (!NT_SUCCESS(status)) + return status; + + NtClose(fileHandle); + + return status; +} + +/** +* Creates a directory path recursively. +* +* \param DirectoryPath The Win32 directory path. +*/ +NTSTATUS PhCreateDirectory( + _In_ PPH_STRING DirectoryPath + ) +{ + static PH_STRINGREF directorySeparator = PH_STRINGREF_INIT(L"\\"); + PPH_STRING directoryPath = NULL; + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + if (PhIsNullOrEmptyString(DirectoryPath)) + return STATUS_INVALID_PARAMETER; + + if (PhDoesFileExistsWin32(PhGetString(DirectoryPath))) + return STATUS_SUCCESS; + + remainingPart = PhGetStringRef(DirectoryPath); + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, OBJ_NAME_PATH_SEPARATOR, &part, &remainingPart); + + if (part.Length != 0) + { + if (PhIsNullOrEmptyString(directoryPath)) + directoryPath = PhCreateString2(&part); + else + { + PPH_STRING tempPathString; + + tempPathString = PhConcatStringRef3( + &directoryPath->sr, + &directorySeparator, + &part + ); + + // Check if the directory already exists. + if (!PhDoesFileExistsWin32(PhGetString(tempPathString))) + { + HANDLE directoryHandle; + + // Create the directory. + if (NT_SUCCESS(PhCreateFileWin32( + &directoryHandle, + PhGetString(tempPathString), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT + ))) + { + NtClose(directoryHandle); + } + } + + PhMoveReference(&directoryPath, tempPathString); + } + } + } + + if (directoryPath) + PhDereferenceObject(directoryPath); + + if (PhDoesFileExistsWin32(PhGetString(DirectoryPath))) + return STATUS_SUCCESS; + else + return STATUS_NOT_FOUND; +} + +static BOOLEAN PhpDeleteDirectoryCallback( + _In_ PFILE_DIRECTORY_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + static PH_STRINGREF directorySeparator = PH_STRINGREF_INIT(L"\\"); + PPH_STRING parentDirectory = Context; + PPH_STRING fullName; + PH_STRINGREF baseName; + + baseName.Buffer = Information->FileName; + baseName.Length = Information->FileNameLength; + + if (PhEqualStringRef2(&baseName, L".", TRUE) || PhEqualStringRef2(&baseName, L"..", TRUE)) + return TRUE; + + fullName = PhConcatStringRef3( + &parentDirectory->sr, + &directorySeparator, + &baseName + ); + + if (Information->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + HANDLE directoryHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &directoryHandle, + PhGetString(fullName), + FILE_GENERIC_READ | DELETE, + FILE_ATTRIBUTE_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + PhEnumDirectoryFile(directoryHandle, NULL, PhpDeleteDirectoryCallback, fullName); + + // Delete the directory. + PhDeleteFile(directoryHandle); + + NtClose(directoryHandle); + } + } + else + { + if (Information->FileAttributes & FILE_ATTRIBUTE_READONLY) + { + HANDLE fileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(fullName), + FILE_GENERIC_READ | FILE_WRITE_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + IO_STATUS_BLOCK isb; + FILE_BASIC_INFORMATION fileInfo; + + memset(&fileInfo, 0, sizeof(FILE_BASIC_INFORMATION)); + + // Clear the read-only flag. + fileInfo.FileAttributes = Information->FileAttributes &= ~FILE_ATTRIBUTE_READONLY; + + NtSetInformationFile( + fileHandle, + &isb, + &fileInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation + ); + + NtClose(fileHandle); + } + } + + // Delete the file. + PhDeleteFileWin32(PhGetString(fullName)); + } + + PhDereferenceObject(fullName); + return TRUE; +} + +/** +* Deletes a directory path recursively. +* +* \param DirectoryPath The Win32 directory path. +*/ +NTSTATUS PhDeleteDirectory( + _In_ PPH_STRING DirectoryPath + ) +{ + NTSTATUS status; + HANDLE directoryHandle; + + status = PhCreateFileWin32( + &directoryHandle, + PhGetString(DirectoryPath), + FILE_GENERIC_READ | DELETE, + FILE_ATTRIBUTE_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (NT_SUCCESS(status)) + { + // Remove any files or folders inside the directory. + status = PhEnumDirectoryFile( + directoryHandle, + NULL, + PhpDeleteDirectoryCallback, + DirectoryPath + ); + + // Remove the directory. + PhDeleteFile(directoryHandle); + + NtClose(directoryHandle); + } + + if (!PhDoesFileExistsWin32(PhGetString(DirectoryPath))) + return STATUS_SUCCESS; + + return status; +} + +/** +* Creates an anonymous pipe. +* +* \param PipeReadHandle The pipe read handle. +* \param PipeWriteHandle The pipe write handle. +*/ +NTSTATUS PhCreatePipe( + _Out_ PHANDLE PipeReadHandle, + _Out_ PHANDLE PipeWriteHandle + ) +{ + return PhCreatePipeEx(PipeReadHandle, PipeWriteHandle, FALSE, NULL); +} + +NTSTATUS PhCreatePipeEx( + _Out_ PHANDLE PipeReadHandle, + _Out_ PHANDLE PipeWriteHandle, + _In_ BOOLEAN InheritHandles, + _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + NTSTATUS status; + PACL pipeAcl = NULL; + HANDLE pipeDirectoryHandle; + HANDLE pipeReadHandle; + HANDLE pipeWriteHandle; + LARGE_INTEGER pipeTimeout; + UNICODE_STRING pipeNameUs; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK isb; + + RtlInitUnicodeString(&pipeNameUs, DEVICE_NAMED_PIPE); + InitializeObjectAttributes( + &objectAttributes, + &pipeNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtOpenFile( + &pipeDirectoryHandle, + GENERIC_READ | SYNCHRONIZE, + &objectAttributes, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return status; + + RtlInitEmptyUnicodeString(&pipeNameUs, NULL, 0); + InitializeObjectAttributes( + &objectAttributes, + &pipeNameUs, + OBJ_CASE_INSENSITIVE | (InheritHandles ? OBJ_INHERIT : 0), + pipeDirectoryHandle, + NULL + ); + + if (SecurityDescriptor) + { + objectAttributes.SecurityDescriptor = SecurityDescriptor; + } + else + { + if (NT_SUCCESS(RtlDefaultNpAcl(&pipeAcl))) + { + SECURITY_DESCRIPTOR securityDescriptor; + + RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlSetDaclSecurityDescriptor(&securityDescriptor, TRUE, pipeAcl, FALSE); + + objectAttributes.SecurityDescriptor = &securityDescriptor; + } + } + + status = NtCreateNamedPipeFile( + &pipeReadHandle, + FILE_WRITE_ATTRIBUTES | GENERIC_READ | SYNCHRONIZE, + &objectAttributes, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, + FILE_PIPE_INBOUND | FILE_SYNCHRONOUS_IO_NONALERT, + FILE_PIPE_BYTE_STREAM_TYPE, + FILE_PIPE_BYTE_STREAM_MODE, + FILE_PIPE_QUEUE_OPERATION, + 1, + PAGE_SIZE, + PAGE_SIZE, + PhTimeoutFromMilliseconds(&pipeTimeout, 120000) + ); + + if (!NT_SUCCESS(status)) + { + if (pipeAcl) + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + + NtClose(pipeDirectoryHandle); + return status; + } + + RtlInitEmptyUnicodeString(&pipeNameUs, NULL, 0); + InitializeObjectAttributes( + &objectAttributes, + &pipeNameUs, + OBJ_CASE_INSENSITIVE | (InheritHandles ? OBJ_INHERIT : 0), + pipeReadHandle, + NULL + ); + + status = NtOpenFile( + &pipeWriteHandle, + FILE_READ_ATTRIBUTES | GENERIC_WRITE | SYNCHRONIZE, + &objectAttributes, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (NT_SUCCESS(status)) + { + *PipeReadHandle = pipeReadHandle; + *PipeWriteHandle = pipeWriteHandle; + } + + if (pipeAcl) + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + + NtClose(pipeDirectoryHandle); + return status; +} + +/** +* Creates an named pipe. +* +* \param PipeHandle The pipe read/write handle. +* \param PipeName The pipe name. +*/ +NTSTATUS PhCreateNamedPipe( + _Out_ PHANDLE PipeHandle, + _In_ PWSTR PipeName + ) +{ + NTSTATUS status; + PACL pipeAcl = NULL; + HANDLE pipeHandle; + PPH_STRING pipeName; + LARGE_INTEGER pipeTimeout; + UNICODE_STRING pipeNameUs; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + + pipeName = PhConcatStrings2(DEVICE_NAMED_PIPE, PipeName); + PhStringRefToUnicodeString(&pipeName->sr, &pipeNameUs); + PhTimeoutFromMilliseconds(&pipeTimeout, 500); + + InitializeObjectAttributes( + &oa, + &pipeNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (NT_SUCCESS(RtlDefaultNpAcl(&pipeAcl))) + { + SECURITY_DESCRIPTOR securityDescriptor; + + RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlSetDaclSecurityDescriptor(&securityDescriptor, TRUE, pipeAcl, FALSE); + + oa.SecurityDescriptor = &securityDescriptor; + } + + status = NtCreateNamedPipeFile( + &pipeHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &oa, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + FILE_PIPE_FULL_DUPLEX | FILE_SYNCHRONOUS_IO_NONALERT, + FILE_PIPE_MESSAGE_TYPE, + FILE_PIPE_MESSAGE_MODE, + FILE_PIPE_QUEUE_OPERATION, + ULONG_MAX, + PAGE_SIZE, + PAGE_SIZE, + &pipeTimeout + ); + + if (NT_SUCCESS(status)) + { + *PipeHandle = pipeHandle; + } + + if (pipeAcl) + RtlFreeHeap(RtlProcessHeap(), 0, pipeAcl); + + PhDereferenceObject(pipeName); + return status; +} + +NTSTATUS PhConnectPipe( + _Out_ PHANDLE PipeHandle, + _In_ PWSTR PipeName + ) +{ + NTSTATUS status; + HANDLE pipeHandle; + PPH_STRING pipeName; + UNICODE_STRING pipeNameUs; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + + pipeName = PhConcatStrings2(DEVICE_NAMED_PIPE, PipeName); + PhStringRefToUnicodeString(&pipeName->sr, &pipeNameUs); + + InitializeObjectAttributes( + &oa, + &pipeNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtCreateFile( + &pipeHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &oa, + &isb, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + + if (NT_SUCCESS(status)) + { + *PipeHandle = pipeHandle; + } + + PhDereferenceObject(pipeName); + return status; +} + +NTSTATUS PhListenNamedPipe( + _In_ HANDLE PipeHandle + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + + status = NtFsControlFile( + PipeHandle, + NULL, + NULL, + NULL, + &isb, + FSCTL_PIPE_LISTEN, + NULL, + 0, + NULL, + 0 + ); + + if (status == STATUS_PENDING) + { + status = NtWaitForSingleObject(PipeHandle, FALSE, NULL); + + if (NT_SUCCESS(status)) + status = isb.Status; + } + + return status; +} + +NTSTATUS PhDisconnectNamedPipe( + _In_ HANDLE PipeHandle + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + + status = NtFsControlFile( + PipeHandle, + NULL, + NULL, + NULL, + &isb, + FSCTL_PIPE_DISCONNECT, + NULL, + 0, + NULL, + 0 + ); + + if (status == STATUS_PENDING) + { + status = NtWaitForSingleObject(PipeHandle, FALSE, NULL); + + if (NT_SUCCESS(status)) + status = isb.Status; + } + + return status; +} + +NTSTATUS PhPeekNamedPipe( + _In_ HANDLE PipeHandle, + _Out_writes_bytes_opt_(Length) PVOID Buffer, + _In_ ULONG Length, + _Out_opt_ PULONG NumberOfBytesRead, + _Out_opt_ PULONG NumberOfBytesAvailable, + _Out_opt_ PULONG NumberOfBytesLeftInMessage + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + PFILE_PIPE_PEEK_BUFFER peekBuffer; + ULONG peekBufferLength; + + peekBufferLength = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data) + Length; + peekBuffer = PhAllocate(peekBufferLength); + + status = NtFsControlFile( + PipeHandle, + NULL, + NULL, + NULL, + &isb, + FSCTL_PIPE_PEEK, + NULL, + 0, + peekBuffer, + peekBufferLength + ); + + if (status == STATUS_PENDING) + { + status = NtWaitForSingleObject(PipeHandle, FALSE, NULL); + + if (NT_SUCCESS(status)) + status = isb.Status; + } + + // STATUS_BUFFER_OVERFLOW means that there is data remaining; this is normal. + if (status == STATUS_BUFFER_OVERFLOW) + status = STATUS_SUCCESS; + + if (NT_SUCCESS(status)) + { + ULONG numberOfBytesRead = 0; + + if (Buffer || NumberOfBytesRead || NumberOfBytesLeftInMessage) + numberOfBytesRead = (ULONG)(isb.Information - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data)); + + if (Buffer) + memcpy(Buffer, peekBuffer->Data, numberOfBytesRead); + + if (NumberOfBytesRead) + *NumberOfBytesRead = numberOfBytesRead; + + if (NumberOfBytesAvailable) + *NumberOfBytesAvailable = peekBuffer->ReadDataAvailable; + + if (NumberOfBytesLeftInMessage) + *NumberOfBytesLeftInMessage = peekBuffer->MessageLength - numberOfBytesRead; + } + + PhFree(peekBuffer); + + return status; +} + +NTSTATUS PhTransceiveNamedPipe( + _In_ HANDLE PipeHandle, + _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + + status = NtFsControlFile( + PipeHandle, + NULL, + NULL, + NULL, + &isb, + FSCTL_PIPE_TRANSCEIVE, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength + ); + + if (status == STATUS_PENDING) + { + status = NtWaitForSingleObject(PipeHandle, FALSE, NULL); + + if (NT_SUCCESS(status)) + status = isb.Status; + } + + return status; +} + +NTSTATUS PhWaitForNamedPipe( + _In_ PWSTR PipeName, + _In_opt_ ULONG Timeout + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK isb; + PH_STRINGREF localNpfsNameSr; + UNICODE_STRING localNpfsName; + HANDLE fileSystemHandle; + OBJECT_ATTRIBUTES oa; + PFILE_PIPE_WAIT_FOR_BUFFER waitForBuffer; + ULONG waitForBufferLength; + + RtlInitUnicodeString(&localNpfsName, DEVICE_NAMED_PIPE); + InitializeObjectAttributes( + &oa, + &localNpfsName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + status = NtOpenFile( + &fileSystemHandle, + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + &oa, + &isb, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return status; + + PhInitializeStringRefLongHint(&localNpfsNameSr, PipeName); + waitForBufferLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name) + (ULONG)localNpfsNameSr.Length; + waitForBuffer = PhAllocate(waitForBufferLength); + + if (Timeout) + { + PhTimeoutFromMilliseconds(&waitForBuffer->Timeout, Timeout); + waitForBuffer->TimeoutSpecified = TRUE; + } + else + { + waitForBuffer->Timeout.LowPart = 0; + waitForBuffer->Timeout.HighPart = MINLONG; // a very long time + waitForBuffer->TimeoutSpecified = TRUE; + } + + waitForBuffer->NameLength = (ULONG)localNpfsNameSr.Length; + memcpy(waitForBuffer->Name, localNpfsNameSr.Buffer, localNpfsNameSr.Length); + + status = NtFsControlFile( + fileSystemHandle, + NULL, + NULL, + NULL, + &isb, + FSCTL_PIPE_WAIT, + waitForBuffer, + waitForBufferLength, + NULL, + 0 + ); + + PhFree(waitForBuffer); + NtClose(fileSystemHandle); + + return status; +} + +NTSTATUS PhImpersonateClientOfNamedPipe( + _In_ HANDLE PipeHandle + ) +{ + IO_STATUS_BLOCK isb; + + return NtFsControlFile( + PipeHandle, + NULL, + NULL, + NULL, + &isb, + FSCTL_PIPE_IMPERSONATE, + NULL, + 0, + NULL, + 0 + ); +} + +NTSTATUS PhGetThreadName( + _In_ HANDLE ThreadHandle, + _Out_ PPH_STRING *ThreadName + ) +{ + NTSTATUS status; + PTHREAD_NAME_INFORMATION buffer; + ULONG bufferSize; + ULONG returnLength; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + + status = NtQueryInformationThread( + ThreadHandle, + ThreadNameInformation, + buffer, + bufferSize, + &returnLength + ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + PhFree(buffer); + bufferSize = returnLength; + buffer = PhAllocate(bufferSize); + + status = NtQueryInformationThread( + ThreadHandle, + ThreadNameInformation, + buffer, + bufferSize, + &returnLength + ); + } + + if (NT_SUCCESS(status)) + { + *ThreadName = PhCreateStringFromUnicodeString(&buffer->ThreadName); + } + + PhFree(buffer); + + return status; +} + +NTSTATUS PhImpersonateToken( + _In_ HANDLE ThreadHandle, + _In_ HANDLE TokenHandle + ) +{ + NTSTATUS status; + SECURITY_QUALITY_OF_SERVICE securityService; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE tokenHandle; + + InitializeObjectAttributes( + &objectAttributes, + NULL, + 0, + NULL, + NULL + ); + + securityService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + securityService.ImpersonationLevel = SecurityImpersonation; + securityService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + securityService.EffectiveOnly = FALSE; + objectAttributes.SecurityQualityOfService = &securityService; + + status = NtDuplicateToken( + TokenHandle, + TOKEN_IMPERSONATE | TOKEN_QUERY, + &objectAttributes, + FALSE, + TokenImpersonation, + &tokenHandle + ); + + if (!NT_SUCCESS(status)) + return status; + + status = NtSetInformationThread( + ThreadHandle, + ThreadImpersonationToken, + &tokenHandle, + sizeof(HANDLE) + ); + + NtClose(tokenHandle); + + return status; +} + +NTSTATUS PhRevertImpersonationToken( + _In_ HANDLE ThreadHandle + ) +{ + HANDLE tokenHandle = NULL; + + return NtSetInformationThread( + ThreadHandle, + ThreadImpersonationToken, + &tokenHandle, + sizeof(HANDLE) + ); +} diff --git a/phlib/phlib.vcxproj b/phlib/phlib.vcxproj index 38d58d7a1bfe..c927de8a9cfd 100644 --- a/phlib/phlib.vcxproj +++ b/phlib/phlib.vcxproj @@ -1,249 +1,326 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {477D0215-F252-41A1-874B-F27E3EA1ED17} - phlib - Win32Proj - 10.0.15063.0 - - - - StaticLibrary - Unicode - true - v141 - - - StaticLibrary - Unicode - v141 - - - StaticLibrary - Unicode - true - v141 - - - StaticLibrary - Unicode - v141 - - - - - - - - - - - - - - - - - - - $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - - - - Disabled - ..\phnt\include;include;%(AdditionalIncludeDirectories) - DEBUG;_PHLIB_;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - - - - - Disabled - ..\phnt\include;include;%(AdditionalIncludeDirectories) - DEBUG;_PHLIB_;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - - - - - MaxSpeed - true - ..\phnt\include;include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_PHLIB_;%(PreprocessorDefinitions) - true - MultiThreaded - false - true - Level3 - ProgramDatabase - StdCall - true - true - StreamingSIMDExtensions - - - - - MaxSpeed - true - ..\phnt\include;include;%(AdditionalIncludeDirectories) - WIN64;NDEBUG;_WINDOWS;_PHLIB_;%(PreprocessorDefinitions) - true - MultiThreaded - false - true - Level3 - ProgramDatabase - StdCall - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {477D0215-F252-41A1-874B-F27E3EA1ED17} + phlib + Win32Proj + 10.0 + + + + StaticLibrary + Unicode + true + v142 + Spectre + + + StaticLibrary + Unicode + v142 + + + StaticLibrary + Unicode + true + v142 + Spectre + + + StaticLibrary + Unicode + v142 + + + + + + + + + + + + + + + + + + + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + + + + Disabled + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) + DEBUG;_DEBUG;_PHLIB_;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + + + MachineX86 + + + + + Disabled + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) + DEBUG;_DEBUG;_PHLIB_;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + + + MachineX64 + + + + + MaxSpeed + true + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_PHLIB_;%(PreprocessorDefinitions) + true + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + true + StreamingSIMDExtensions + Guard + + + MachineX86 + + + + + MaxSpeed + true + $(SolutionDir)phnt\include;include;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_PHLIB_;%(PreprocessorDefinitions) + true + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + true + Guard + + + MachineX64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/phlib/phlib.vcxproj.filters b/phlib/phlib.vcxproj.filters index 176452ccb152..bc73d8f38594 100644 --- a/phlib/phlib.vcxproj.filters +++ b/phlib/phlib.vcxproj.filters @@ -1,320 +1,521 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {7d0c2da2-811d-4844-9704-0c87067c7be1} + + + {874b89c3-fb60-46f3-a62c-49ac88e3026d} + + + {fd6c74dc-d93e-4dd1-9a8b-5bdf525e0709} + + + {4f0e2a3e-3009-4e13-8d38-e33c46d00044} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Mini-XML + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Json-C + + + Source Files + + + Source Files + + + Json-C + + + Json-C + + + Mini-XML + + + Json-C + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Mini-XML\Headers + + + Mini-XML\Headers + + + Mini-XML\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Json-C\Headers + + + Header Files + + \ No newline at end of file diff --git a/phlib/provider.c b/phlib/provider.c index 2e5366a806aa..9550eba2cda8 100644 --- a/phlib/provider.c +++ b/phlib/provider.c @@ -1,470 +1,465 @@ -/* - * Process Hacker - - * provider system - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * Provider objects allow a function to be executed periodically. This is managed by a - * synchronization timer object which is signaled periodically. The use of a timer object as opposed - * to a simple sleep call means that the length of time a provider function takes to execute has no - * effect on the interval between runs. - * - * In contrast to callback objects, the context passed to provider functions must be - * reference-counted objects. This means that it is not guaranteed that the function will not be in - * execution after the unregister operation is complete. However, the since the context object is - * reference-counted, there are no safety issues. - * - * Providers can be boosted, which causes them to be run immediately ignoring the interval. This is - * separate to the periodic runs, and does not cause the next periodic run to be missed. Providers, - * even when boosted, always run on the same provider thread. The other option would be to have the - * boosting thread run the provider function directly, which would involve unnecessary blocking and - * synchronization. - */ - -#include -#include - -#ifdef DEBUG -PPH_LIST PhDbgProviderList; -PH_QUEUED_LOCK PhDbgProviderListLock = PH_QUEUED_LOCK_INIT; -#endif - -/** - * Initializes a provider thread. - * - * \param ProviderThread A pointer to a provider thread object. - * \param Interval The interval between each run, in milliseconds. - */ -VOID PhInitializeProviderThread( - _Out_ PPH_PROVIDER_THREAD ProviderThread, - _In_ ULONG Interval - ) -{ - ProviderThread->ThreadHandle = NULL; - ProviderThread->TimerHandle = NULL; - ProviderThread->Interval = Interval; - ProviderThread->State = ProviderThreadStopped; - - PhInitializeQueuedLock(&ProviderThread->Lock); - InitializeListHead(&ProviderThread->ListHead); - ProviderThread->BoostCount = 0; - -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&PhDbgProviderListLock); - if (!PhDbgProviderList) - PhDbgProviderList = PhCreateList(4); - PhAddItemList(PhDbgProviderList, ProviderThread); - PhReleaseQueuedLockExclusive(&PhDbgProviderListLock); -#endif -} - -/** - * Frees resources used by a provider thread. - * - * \param ProviderThread A pointer to a provider thread object. - */ -VOID PhDeleteProviderThread( - _Inout_ PPH_PROVIDER_THREAD ProviderThread - ) -{ -#ifdef DEBUG - ULONG index; -#endif - // Nothing - -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&PhDbgProviderListLock); - if ((index = PhFindItemList(PhDbgProviderList, ProviderThread)) != -1) - PhRemoveItemList(PhDbgProviderList, index); - PhReleaseQueuedLockExclusive(&PhDbgProviderListLock); -#endif -} - -NTSTATUS NTAPI PhpProviderThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - PPH_PROVIDER_THREAD providerThread = (PPH_PROVIDER_THREAD)Parameter; - NTSTATUS status = STATUS_SUCCESS; - PLIST_ENTRY listEntry; - PPH_PROVIDER_REGISTRATION registration; - PPH_PROVIDER_FUNCTION providerFunction; - PVOID object; - LIST_ENTRY tempListHead; - - PhInitializeAutoPool(&autoPool); - - while (providerThread->State != ProviderThreadStopping) - { - // Keep removing and executing providers from the list until there are no more. Each removed - // provider will be placed on the temporary list. After this is done, all providers on the - // temporary list will be re-added to the list again. - // - // The key to this working safely with the other functions (boost, register, unregister) is - // that at all times when the mutex is not acquired every single provider must be in a list - // (main list or the temp list). - - InitializeListHead(&tempListHead); - - PhAcquireQueuedLockExclusive(&providerThread->Lock); - - // Main loop. - - // We check the status variable for STATUS_ALERTED, which means that someone is requesting - // that a provider be boosted. Note that if they alert this thread while we are not waiting - // on the timer, when we do perform the wait it will return immediately with STATUS_ALERTED. - - while (TRUE) - { - if (status == STATUS_ALERTED) - { - // Check if we have any more providers to boost. Note that this always works because - // boosted providers are always in front of normal providers. Therefore we will - // never mistakenly boost normal providers. - - if (providerThread->BoostCount == 0) - break; - } - - listEntry = RemoveHeadList(&providerThread->ListHead); - - if (listEntry == &providerThread->ListHead) - break; - - registration = CONTAINING_RECORD(listEntry, PH_PROVIDER_REGISTRATION, ListEntry); - - // Add the provider to the temp list. - InsertTailList(&tempListHead, listEntry); - - if (status != STATUS_ALERTED) - { - if (!registration->Enabled || registration->Unregistering) - continue; - } - else - { - // If we're boosting providers, we don't care if they are enabled or not. However, - // we have to make sure any providers which are being unregistered get a chance to - // fix the boost count. - - if (registration->Unregistering) - { - PhReleaseQueuedLockExclusive(&providerThread->Lock); - PhAcquireQueuedLockExclusive(&providerThread->Lock); - - continue; - } - } - - if (status == STATUS_ALERTED) - { - assert(registration->Boosting); - registration->Boosting = FALSE; - providerThread->BoostCount--; - } - - providerFunction = registration->Function; - object = registration->Object; - - if (object) - PhReferenceObject(object); - - registration->RunId++; - - PhReleaseQueuedLockExclusive(&providerThread->Lock); - providerFunction(object); - PhDrainAutoPool(&autoPool); - PhAcquireQueuedLockExclusive(&providerThread->Lock); - - if (object) - PhDereferenceObject(object); - } - - // Re-add the items in the temp list to the main list. - - while ((listEntry = RemoveHeadList(&tempListHead)) != &tempListHead) - { - registration = CONTAINING_RECORD(listEntry, PH_PROVIDER_REGISTRATION, ListEntry); - - // We must insert boosted providers at the front of the list in order to maintain the - // condition that boosted providers are always in front of normal providers. This occurs - // when the timer is signaled just before a boosting provider alerts our thread. - if (!registration->Boosting) - InsertTailList(&providerThread->ListHead, listEntry); - else - InsertHeadList(&providerThread->ListHead, listEntry); - } - - PhReleaseQueuedLockExclusive(&providerThread->Lock); - - // Perform an alertable wait so we can be woken up by someone telling us to boost providers, - // or to terminate. - status = NtWaitForSingleObject( - providerThread->TimerHandle, - TRUE, - NULL - ); - } - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -/** - * Starts a provider thread. - * - * \param ProviderThread A pointer to a provider thread object. - */ -VOID PhStartProviderThread( - _Inout_ PPH_PROVIDER_THREAD ProviderThread - ) -{ - if (ProviderThread->State != ProviderThreadStopped) - return; - - // Create and set the timer. - NtCreateTimer(&ProviderThread->TimerHandle, TIMER_ALL_ACCESS, NULL, SynchronizationTimer); - PhSetIntervalProviderThread(ProviderThread, ProviderThread->Interval); - - // Create and start the thread. - ProviderThread->ThreadHandle = PhCreateThread( - 0, - PhpProviderThreadStart, - ProviderThread - ); - - ProviderThread->State = ProviderThreadRunning; -} - -/** - * Stops a provider thread. - * - * \param ProviderThread A pointer to a provider thread object. - */ -VOID PhStopProviderThread( - _Inout_ PPH_PROVIDER_THREAD ProviderThread - ) -{ - if (ProviderThread->State != ProviderThreadRunning) - return; - - // Signal to the thread that we are shutting down, and wait for it to exit. - ProviderThread->State = ProviderThreadStopping; - NtAlertThread(ProviderThread->ThreadHandle); // wake it up - NtWaitForSingleObject(ProviderThread->ThreadHandle, FALSE, NULL); - - // Free resources. - NtClose(ProviderThread->ThreadHandle); - NtClose(ProviderThread->TimerHandle); - ProviderThread->ThreadHandle = NULL; - ProviderThread->TimerHandle = NULL; - - ProviderThread->State = ProviderThreadStopped; -} - -/** - * Sets the run interval for a provider thread. - * - * \param ProviderThread A pointer to a provider thread object. - * \param Interval The interval between each run, in milliseconds. - */ -VOID PhSetIntervalProviderThread( - _Inout_ PPH_PROVIDER_THREAD ProviderThread, - _In_ ULONG Interval - ) -{ - ProviderThread->Interval = Interval; - - if (ProviderThread->TimerHandle) - { - LARGE_INTEGER interval; - - interval.QuadPart = -(LONGLONG)Interval * PH_TIMEOUT_MS; - NtSetTimer(ProviderThread->TimerHandle, &interval, NULL, NULL, FALSE, Interval, NULL); - } -} - -/** - * Registers a provider with a provider thread. - * - * \param ProviderThread A pointer to a provider thread object. - * \param Function The provider function. - * \param Object A pointer to an object to pass to the provider function. The object must be managed - * by the reference-counting system. - * \param Registration A variable which receives registration information for the provider. - * - * \remarks The provider is initially disabled. Call PhSetEnabledProvider() to enable it. - */ -VOID PhRegisterProvider( - _Inout_ PPH_PROVIDER_THREAD ProviderThread, - _In_ PPH_PROVIDER_FUNCTION Function, - _In_opt_ PVOID Object, - _Out_ PPH_PROVIDER_REGISTRATION Registration - ) -{ - Registration->ProviderThread = ProviderThread; - Registration->Function = Function; - Registration->Object = Object; - Registration->RunId = 0; - Registration->Enabled = FALSE; - Registration->Unregistering = FALSE; - Registration->Boosting = FALSE; - - if (Object) - PhReferenceObject(Object); - - PhAcquireQueuedLockExclusive(&ProviderThread->Lock); - InsertTailList(&ProviderThread->ListHead, &Registration->ListEntry); - PhReleaseQueuedLockExclusive(&ProviderThread->Lock); -} - -/** - * Unregisters a provider. - * - * \param Registration A pointer to the registration object for a provider. - * - * \remarks The provider function may still be in execution once this function returns. - */ -VOID PhUnregisterProvider( - _Inout_ PPH_PROVIDER_REGISTRATION Registration - ) -{ - PPH_PROVIDER_THREAD providerThread; - - providerThread = Registration->ProviderThread; - - Registration->Unregistering = TRUE; - - // There are two possibilities for removal: - // 1. The provider is removed while the thread is not in the main loop. This is the normal case. - // 2. The provider is removed while the thread is in the main loop. In that case the provider - // will be removed from the temp list and so it won't be re-added to the main list. - - PhAcquireQueuedLockExclusive(&providerThread->Lock); - - RemoveEntryList(&Registration->ListEntry); - - // Fix the boost count. - if (Registration->Boosting) - providerThread->BoostCount--; - - // The user-supplied object must be dereferenced - // while the mutex is held. - if (Registration->Object) - PhDereferenceObject(Registration->Object); - - PhReleaseQueuedLockExclusive(&providerThread->Lock); -} - -/** - * Causes a provider to be queued for immediate execution. - * - * \param Registration A pointer to the registration object for a provider. - * \param FutureRunId A variable which receives the run ID of the future run. - * - * \return TRUE if the operation was successful; FALSE if the provider is being unregistered, the - * provider is already being boosted, or the provider thread is not running. - * - * \remarks Boosted providers will be run immediately, ignoring the run interval. Boosting will not - * however affect the normal runs. - */ -BOOLEAN PhBoostProvider( - _Inout_ PPH_PROVIDER_REGISTRATION Registration, - _Out_opt_ PULONG FutureRunId - ) -{ - PPH_PROVIDER_THREAD providerThread; - ULONG futureRunId; - - if (Registration->Unregistering) - return FALSE; - - providerThread = Registration->ProviderThread; - - // Simply move to the provider to the front of the list. This works even if the provider is - // currently in the temp list. - - PhAcquireQueuedLockExclusive(&providerThread->Lock); - - // Abort if the provider is already being boosted or the provider thread is stopping/stopped. - if (Registration->Boosting || providerThread->State != ProviderThreadRunning) - { - PhReleaseQueuedLockExclusive(&providerThread->Lock); - return FALSE; - } - - RemoveEntryList(&Registration->ListEntry); - InsertHeadList(&providerThread->ListHead, &Registration->ListEntry); - - Registration->Boosting = TRUE; - providerThread->BoostCount++; - - futureRunId = Registration->RunId + 1; - - PhReleaseQueuedLockExclusive(&providerThread->Lock); - - // Wake up the thread. - NtAlertThread(providerThread->ThreadHandle); - - if (FutureRunId) - *FutureRunId = futureRunId; - - return TRUE; -} - -/** - * Gets the current run ID of a provider. - * - * \param Registration A pointer to the registration object for a provider. - */ -ULONG PhGetRunIdProvider( - _In_ PPH_PROVIDER_REGISTRATION Registration - ) -{ - return Registration->RunId; -} - -/** - * Gets whether a provider is enabled. - * - * \param Registration A pointer to the registration object for a provider. - */ -BOOLEAN PhGetEnabledProvider( - _In_ PPH_PROVIDER_REGISTRATION Registration - ) -{ - return Registration->Enabled; -} - -/** - * Sets whether a provider is enabled. - * - * \param Registration A pointer to the registration object for a provider. - * \param Enabled TRUE if the provider is enabled, otherwise FALSE. - */ -VOID PhSetEnabledProvider( - _Inout_ PPH_PROVIDER_REGISTRATION Registration, - _In_ BOOLEAN Enabled - ) -{ - Registration->Enabled = Enabled; -} +/* + * Process Hacker - + * provider system + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * Provider objects allow a function to be executed periodically. This is managed by a + * synchronization timer object which is signaled periodically. The use of a timer object as opposed + * to a simple sleep call means that the length of time a provider function takes to execute has no + * effect on the interval between runs. + * + * In contrast to callback objects, the context passed to provider functions must be + * reference-counted objects. This means that it is not guaranteed that the function will not be in + * execution after the unregister operation is complete. However, the since the context object is + * reference-counted, there are no safety issues. + * + * Providers can be boosted, which causes them to be run immediately ignoring the interval. This is + * separate to the periodic runs, and does not cause the next periodic run to be missed. Providers, + * even when boosted, always run on the same provider thread. The other option would be to have the + * boosting thread run the provider function directly, which would involve unnecessary blocking and + * synchronization. + */ + +#include +#include + +#ifdef DEBUG +PPH_LIST PhDbgProviderList; +PH_QUEUED_LOCK PhDbgProviderListLock = PH_QUEUED_LOCK_INIT; +#endif + +/** + * Initializes a provider thread. + * + * \param ProviderThread A pointer to a provider thread object. + * \param Interval The interval between each run, in milliseconds. + */ +VOID PhInitializeProviderThread( + _Out_ PPH_PROVIDER_THREAD ProviderThread, + _In_ ULONG Interval + ) +{ + ProviderThread->ThreadHandle = NULL; + ProviderThread->TimerHandle = NULL; + ProviderThread->Interval = Interval; + ProviderThread->State = ProviderThreadStopped; + + PhInitializeQueuedLock(&ProviderThread->Lock); + InitializeListHead(&ProviderThread->ListHead); + ProviderThread->BoostCount = 0; + +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&PhDbgProviderListLock); + if (!PhDbgProviderList) + PhDbgProviderList = PhCreateList(4); + PhAddItemList(PhDbgProviderList, ProviderThread); + PhReleaseQueuedLockExclusive(&PhDbgProviderListLock); +#endif +} + +/** + * Frees resources used by a provider thread. + * + * \param ProviderThread A pointer to a provider thread object. + */ +VOID PhDeleteProviderThread( + _Inout_ PPH_PROVIDER_THREAD ProviderThread + ) +{ +#ifdef DEBUG + ULONG index; +#endif + // Nothing + +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&PhDbgProviderListLock); + if ((index = PhFindItemList(PhDbgProviderList, ProviderThread)) != -1) + PhRemoveItemList(PhDbgProviderList, index); + PhReleaseQueuedLockExclusive(&PhDbgProviderListLock); +#endif +} + +NTSTATUS NTAPI PhpProviderThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + PPH_PROVIDER_THREAD providerThread = (PPH_PROVIDER_THREAD)Parameter; + NTSTATUS status = STATUS_SUCCESS; + PLIST_ENTRY listEntry; + PPH_PROVIDER_REGISTRATION registration; + PPH_PROVIDER_FUNCTION providerFunction; + PVOID object; + LIST_ENTRY tempListHead; + + PhInitializeAutoPool(&autoPool); + + while (providerThread->State != ProviderThreadStopping) + { + // Keep removing and executing providers from the list until there are no more. Each removed + // provider will be placed on the temporary list. After this is done, all providers on the + // temporary list will be re-added to the list again. + // + // The key to this working safely with the other functions (boost, register, unregister) is + // that at all times when the mutex is not acquired every single provider must be in a list + // (main list or the temp list). + + InitializeListHead(&tempListHead); + + PhAcquireQueuedLockExclusive(&providerThread->Lock); + + // Main loop. + + // We check the status variable for STATUS_ALERTED, which means that someone is requesting + // that a provider be boosted. Note that if they alert this thread while we are not waiting + // on the timer, when we do perform the wait it will return immediately with STATUS_ALERTED. + + while (TRUE) + { + if (status == STATUS_ALERTED) + { + // Check if we have any more providers to boost. Note that this always works because + // boosted providers are always in front of normal providers. Therefore we will + // never mistakenly boost normal providers. + + if (providerThread->BoostCount == 0) + break; + } + + listEntry = RemoveHeadList(&providerThread->ListHead); + + if (listEntry == &providerThread->ListHead) + break; + + registration = CONTAINING_RECORD(listEntry, PH_PROVIDER_REGISTRATION, ListEntry); + + // Add the provider to the temp list. + InsertTailList(&tempListHead, listEntry); + + if (status != STATUS_ALERTED) + { + if (!registration->Enabled || registration->Unregistering) + continue; + } + else + { + // If we're boosting providers, we don't care if they are enabled or not. However, + // we have to make sure any providers which are being unregistered get a chance to + // fix the boost count. + + if (registration->Unregistering) + { + PhReleaseQueuedLockExclusive(&providerThread->Lock); + PhAcquireQueuedLockExclusive(&providerThread->Lock); + + continue; + } + } + + if (status == STATUS_ALERTED) + { + assert(registration->Boosting); + registration->Boosting = FALSE; + providerThread->BoostCount--; + } + + providerFunction = registration->Function; + object = registration->Object; + + if (object) + PhReferenceObject(object); + + registration->RunId++; + + PhReleaseQueuedLockExclusive(&providerThread->Lock); + providerFunction(object); + PhDrainAutoPool(&autoPool); + PhAcquireQueuedLockExclusive(&providerThread->Lock); + + if (object) + PhDereferenceObject(object); + } + + // Re-add the items in the temp list to the main list. + + while ((listEntry = RemoveHeadList(&tempListHead)) != &tempListHead) + { + registration = CONTAINING_RECORD(listEntry, PH_PROVIDER_REGISTRATION, ListEntry); + + // We must insert boosted providers at the front of the list in order to maintain the + // condition that boosted providers are always in front of normal providers. This occurs + // when the timer is signaled just before a boosting provider alerts our thread. + if (!registration->Boosting) + InsertTailList(&providerThread->ListHead, listEntry); + else + InsertHeadList(&providerThread->ListHead, listEntry); + } + + PhReleaseQueuedLockExclusive(&providerThread->Lock); + + // Perform an alertable wait so we can be woken up by someone telling us to boost providers, + // or to terminate. + status = NtWaitForSingleObject( + providerThread->TimerHandle, + TRUE, + NULL + ); + } + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + +/** + * Starts a provider thread. + * + * \param ProviderThread A pointer to a provider thread object. + */ +VOID PhStartProviderThread( + _Inout_ PPH_PROVIDER_THREAD ProviderThread + ) +{ + if (ProviderThread->State != ProviderThreadStopped) + return; + + // Create and set the timer. + NtCreateTimer(&ProviderThread->TimerHandle, TIMER_ALL_ACCESS, NULL, SynchronizationTimer); + PhSetIntervalProviderThread(ProviderThread, ProviderThread->Interval); + + // Create and start the thread. + PhCreateThreadEx(&ProviderThread->ThreadHandle, PhpProviderThreadStart, ProviderThread); + ProviderThread->State = ProviderThreadRunning; +} + +/** + * Stops a provider thread. + * + * \param ProviderThread A pointer to a provider thread object. + */ +VOID PhStopProviderThread( + _Inout_ PPH_PROVIDER_THREAD ProviderThread + ) +{ + if (ProviderThread->State != ProviderThreadRunning) + return; + + // Signal to the thread that we are shutting down, and wait for it to exit. + ProviderThread->State = ProviderThreadStopping; + NtAlertThread(ProviderThread->ThreadHandle); // wake it up + NtWaitForSingleObject(ProviderThread->ThreadHandle, FALSE, NULL); + + // Free resources. + NtClose(ProviderThread->ThreadHandle); + NtClose(ProviderThread->TimerHandle); + ProviderThread->ThreadHandle = NULL; + ProviderThread->TimerHandle = NULL; + + ProviderThread->State = ProviderThreadStopped; +} + +/** + * Sets the run interval for a provider thread. + * + * \param ProviderThread A pointer to a provider thread object. + * \param Interval The interval between each run, in milliseconds. + */ +VOID PhSetIntervalProviderThread( + _Inout_ PPH_PROVIDER_THREAD ProviderThread, + _In_ ULONG Interval + ) +{ + ProviderThread->Interval = Interval; + + if (ProviderThread->TimerHandle) + { + LARGE_INTEGER interval; + + interval.QuadPart = -(LONGLONG)UInt32x32To64(Interval, PH_TIMEOUT_MS); + NtSetTimer(ProviderThread->TimerHandle, &interval, NULL, NULL, FALSE, Interval, NULL); + } +} + +/** + * Registers a provider with a provider thread. + * + * \param ProviderThread A pointer to a provider thread object. + * \param Function The provider function. + * \param Object A pointer to an object to pass to the provider function. The object must be managed + * by the reference-counting system. + * \param Registration A variable which receives registration information for the provider. + * + * \remarks The provider is initially disabled. Call PhSetEnabledProvider() to enable it. + */ +VOID PhRegisterProvider( + _Inout_ PPH_PROVIDER_THREAD ProviderThread, + _In_ PPH_PROVIDER_FUNCTION Function, + _In_opt_ PVOID Object, + _Out_ PPH_PROVIDER_REGISTRATION Registration + ) +{ + Registration->ProviderThread = ProviderThread; + Registration->Function = Function; + Registration->Object = Object; + Registration->RunId = 0; + Registration->Enabled = FALSE; + Registration->Unregistering = FALSE; + Registration->Boosting = FALSE; + + if (Object) + PhReferenceObject(Object); + + PhAcquireQueuedLockExclusive(&ProviderThread->Lock); + InsertTailList(&ProviderThread->ListHead, &Registration->ListEntry); + PhReleaseQueuedLockExclusive(&ProviderThread->Lock); +} + +/** + * Unregisters a provider. + * + * \param Registration A pointer to the registration object for a provider. + * + * \remarks The provider function may still be in execution once this function returns. + */ +VOID PhUnregisterProvider( + _Inout_ PPH_PROVIDER_REGISTRATION Registration + ) +{ + PPH_PROVIDER_THREAD providerThread; + + providerThread = Registration->ProviderThread; + + Registration->Unregistering = TRUE; + + // There are two possibilities for removal: + // 1. The provider is removed while the thread is not in the main loop. This is the normal case. + // 2. The provider is removed while the thread is in the main loop. In that case the provider + // will be removed from the temp list and so it won't be re-added to the main list. + + PhAcquireQueuedLockExclusive(&providerThread->Lock); + + RemoveEntryList(&Registration->ListEntry); + + // Fix the boost count. + if (Registration->Boosting) + providerThread->BoostCount--; + + // The user-supplied object must be dereferenced + // while the mutex is held. + if (Registration->Object) + PhDereferenceObject(Registration->Object); + + PhReleaseQueuedLockExclusive(&providerThread->Lock); +} + +/** + * Causes a provider to be queued for immediate execution. + * + * \param Registration A pointer to the registration object for a provider. + * \param FutureRunId A variable which receives the run ID of the future run. + * + * \return TRUE if the operation was successful; FALSE if the provider is being unregistered, the + * provider is already being boosted, or the provider thread is not running. + * + * \remarks Boosted providers will be run immediately, ignoring the run interval. Boosting will not + * however affect the normal runs. + */ +BOOLEAN PhBoostProvider( + _Inout_ PPH_PROVIDER_REGISTRATION Registration, + _Out_opt_ PULONG FutureRunId + ) +{ + PPH_PROVIDER_THREAD providerThread; + ULONG futureRunId; + + if (Registration->Unregistering) + return FALSE; + + providerThread = Registration->ProviderThread; + + // Simply move to the provider to the front of the list. This works even if the provider is + // currently in the temp list. + + PhAcquireQueuedLockExclusive(&providerThread->Lock); + + // Abort if the provider is already being boosted or the provider thread is stopping/stopped. + if (Registration->Boosting || providerThread->State != ProviderThreadRunning) + { + PhReleaseQueuedLockExclusive(&providerThread->Lock); + return FALSE; + } + + RemoveEntryList(&Registration->ListEntry); + InsertHeadList(&providerThread->ListHead, &Registration->ListEntry); + + Registration->Boosting = TRUE; + providerThread->BoostCount++; + + futureRunId = Registration->RunId + 1; + + PhReleaseQueuedLockExclusive(&providerThread->Lock); + + // Wake up the thread. + NtAlertThread(providerThread->ThreadHandle); + + if (FutureRunId) + *FutureRunId = futureRunId; + + return TRUE; +} + +/** + * Gets the current run ID of a provider. + * + * \param Registration A pointer to the registration object for a provider. + */ +ULONG PhGetRunIdProvider( + _In_ PPH_PROVIDER_REGISTRATION Registration + ) +{ + return Registration->RunId; +} + +/** + * Gets whether a provider is enabled. + * + * \param Registration A pointer to the registration object for a provider. + */ +BOOLEAN PhGetEnabledProvider( + _In_ PPH_PROVIDER_REGISTRATION Registration + ) +{ + return Registration->Enabled; +} + +/** + * Sets whether a provider is enabled. + * + * \param Registration A pointer to the registration object for a provider. + * \param Enabled TRUE if the provider is enabled, otherwise FALSE. + */ +VOID PhSetEnabledProvider( + _Inout_ PPH_PROVIDER_REGISTRATION Registration, + _In_ BOOLEAN Enabled + ) +{ + Registration->Enabled = Enabled; +} diff --git a/phlib/queuedlock.c b/phlib/queuedlock.c index 957a32a4bd3a..22d9602b1ce0 100644 --- a/phlib/queuedlock.c +++ b/phlib/queuedlock.c @@ -1,1202 +1,1202 @@ -/* - * Process Hacker - - * queued lock - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * Queued lock, a.k.a. push lock (kernel-mode) or slim reader-writer lock (user-mode). - * - * The queued lock is: - * * Around 10% faster than the fast lock. - * * Only the size of a pointer. - * * Low on resource usage (no additional kernel objects are created for blocking). - * - * The usual flags are used for contention-free acquire/release. When there is contention, - * stack-based wait blocks are chained. The first wait block contains the shared owners count which - * is decremented by shared releasers. - * - * Naturally these wait blocks would be chained in FILO order, but list optimization is done for two - * purposes: - * * Finding the last wait block (where the shared owners count is stored). This is implemented by - * the Last pointer. - * * Unblocking the wait blocks in FIFO order. This is implemented by the Previous pointer. - * - * The optimization is incremental - each optimization run will stop at the first optimized wait - * block. Any needed optimization is completed just before waking waiters. - * - * The waiters list/chain has the following restrictions: - * * At any time wait blocks may be pushed onto the list. - * * While waking waiters, the list may not be traversed nor optimized. - * * When there are multiple shared owners, shared releasers may traverse the list (to find the last - * wait block). This is not an issue because waiters wouldn't be woken until there are no more - * shared owners. - * * List optimization may be done at any time except for when someone else is waking waiters. This - * is controlled by the traversing bit. - * - * The traversing bit has the following rules: - * * The list may be optimized only after the traversing bit is set, checking that it wasn't set - * already. If it was set, it would indicate that someone else is optimizing the list or waking - * waiters. - * * Before waking waiters the traversing bit must be set. If it was set already, just clear the - * owned bit. - * * If during list optimization the owned bit is detected to be cleared, the function begins waking - * waiters. This is because the owned bit is cleared when a releaser fails to set the traversing - * bit. - * - * Blocking is implemented through a process-wide keyed event. A spin count is also used before - * blocking on the keyed event. - * - * Queued locks can act as condition variables, with wait, pulse and pulse all support. Waiters are - * released in FIFO order. - * - * Queued locks can act as wake events. These are designed for tiny one-bit locks which share a - * single event to block on. Spurious wake-ups are a part of normal operation. - */ - -#include - -#include -#include -#include - -VOID FASTCALL PhpfOptimizeQueuedLockList( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value - ); - -VOID FASTCALL PhpfWakeQueuedLock( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value - ); - -VOID FASTCALL PhpfWakeQueuedLockEx( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value, - _In_ BOOLEAN IgnoreOwned, - _In_ BOOLEAN WakeAll - ); - -static HANDLE PhQueuedLockKeyedEventHandle; -static ULONG PhQueuedLockSpinCount = 2000; - -BOOLEAN PhQueuedLockInitialization( - VOID - ) -{ - if (!NT_SUCCESS(NtCreateKeyedEvent( - &PhQueuedLockKeyedEventHandle, - KEYEDEVENT_ALL_ACCESS, - NULL, - 0 - ))) - return FALSE; - - if ((ULONG)PhSystemBasicInformation.NumberOfProcessors > 1) - PhQueuedLockSpinCount = 4000; - else - PhQueuedLockSpinCount = 0; - - return TRUE; -} - -/** - * Pushes a wait block onto a queued lock's waiters list. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * \param Exclusive Whether the wait block is in exclusive mode. - * \param WaitBlock A variable which receives the resulting wait block structure. - * \param Optimize A variable which receives a boolean indicating whether to optimize the waiters - * list. - * \param NewValue The old value of the queued lock. This value is useful only if the function - * returns FALSE. - * \param CurrentValue The new value of the queued lock. This value is useful only if the function - * returns TRUE. - * - * \return TRUE if the wait block was pushed onto the waiters list, otherwise FALSE. - * - * \remarks - * \li The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_OWNED. - * \li Do not move the wait block location after this function is called. - * \li The \a Optimize boolean is a hint to call PhpfOptimizeQueuedLockList() if the function - * succeeds. It is recommended, but not essential that this occurs. - * \li Call PhpBlockOnQueuedWaitBlock() to wait for the wait block to be unblocked. - */ -FORCEINLINE BOOLEAN PhpPushQueuedWaitBlock( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value, - _In_ BOOLEAN Exclusive, - _Out_ PPH_QUEUED_WAIT_BLOCK WaitBlock, - _Out_ PBOOLEAN Optimize, - _Out_ PULONG_PTR NewValue, - _Out_ PULONG_PTR CurrentValue - ) -{ - ULONG_PTR newValue; - BOOLEAN optimize; - - WaitBlock->Previous = NULL; // set later by optimization - optimize = FALSE; - - if (Exclusive) - WaitBlock->Flags = PH_QUEUED_WAITER_EXCLUSIVE | PH_QUEUED_WAITER_SPINNING; - else - WaitBlock->Flags = PH_QUEUED_WAITER_SPINNING; - - if (Value & PH_QUEUED_LOCK_WAITERS) - { - // We're not the first waiter. - - WaitBlock->Last = NULL; // set later by optimization - WaitBlock->Next = PhGetQueuedLockWaitBlock(Value); - WaitBlock->SharedOwners = 0; - - // Push our wait block onto the list. - // Set the traversing bit because we'll be optimizing the list. - newValue = ((ULONG_PTR)WaitBlock) | (Value & PH_QUEUED_LOCK_FLAGS) | - PH_QUEUED_LOCK_TRAVERSING; - - if (!(Value & PH_QUEUED_LOCK_TRAVERSING)) - optimize = TRUE; - } - else - { - // We're the first waiter. - - WaitBlock->Last = WaitBlock; // indicate that this is the last wait block - - if (Exclusive) - { - // We're the first waiter. Save the shared owners count. - WaitBlock->SharedOwners = (ULONG)PhGetQueuedLockSharedOwners(Value); - - if (WaitBlock->SharedOwners > 1) - { - newValue = ((ULONG_PTR)WaitBlock) | PH_QUEUED_LOCK_OWNED | - PH_QUEUED_LOCK_WAITERS | PH_QUEUED_LOCK_MULTIPLE_SHARED; - } - else - { - newValue = ((ULONG_PTR)WaitBlock) | PH_QUEUED_LOCK_OWNED | - PH_QUEUED_LOCK_WAITERS; - } - } - else - { - // We're waiting in shared mode, which means there can't be any shared owners (otherwise - // we would've acquired the lock already). - - WaitBlock->SharedOwners = 0; - - newValue = ((ULONG_PTR)WaitBlock) | PH_QUEUED_LOCK_OWNED | - PH_QUEUED_LOCK_WAITERS; - } - } - - *Optimize = optimize; - *CurrentValue = newValue; - - newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)Value - ); - - *NewValue = newValue; - - return newValue == Value; -} - -/** - * Finds the last wait block in the waiters list. - * - * \param Value The current value of the queued lock. - * - * \return A pointer to the last wait block. - * - * \remarks The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_WAITERS, - * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED or - * \ref PH_QUEUED_LOCK_TRAVERSING. - */ -FORCEINLINE PPH_QUEUED_WAIT_BLOCK PhpFindLastQueuedWaitBlock( - _In_ ULONG_PTR Value - ) -{ - PPH_QUEUED_WAIT_BLOCK waitBlock; - PPH_QUEUED_WAIT_BLOCK lastWaitBlock; - - waitBlock = PhGetQueuedLockWaitBlock(Value); - - // Traverse the list until we find the last wait block. - // The Last pointer should be set by list optimization, allowing us to skip all, if not most of - // the wait blocks. - - while (TRUE) - { - lastWaitBlock = waitBlock->Last; - - if (lastWaitBlock) - { - // Follow the Last pointer. This can mean two things: the pointer was set by list - // optimization, or this wait block is actually the last wait block (set when it was - // pushed onto the list). - waitBlock = lastWaitBlock; - break; - } - - waitBlock = waitBlock->Next; - } - - return waitBlock; -} - -/** - * Waits for a wait block to be unblocked. - * - * \param WaitBlock A wait block. - * \param Spin TRUE to spin, FALSE to block immediately. - * \param Timeout A timeout value. - */ -_May_raise_ FORCEINLINE NTSTATUS PhpBlockOnQueuedWaitBlock( - _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock, - _In_ BOOLEAN Spin, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - NTSTATUS status; - ULONG i; - - if (Spin) - { - PHLIB_INC_STATISTIC(QlBlockSpins); - - for (i = PhQueuedLockSpinCount; i != 0; i--) - { - if (!(*(volatile ULONG *)&WaitBlock->Flags & PH_QUEUED_WAITER_SPINNING)) - return STATUS_SUCCESS; - - YieldProcessor(); - } - } - - if (_interlockedbittestandreset((PLONG)&WaitBlock->Flags, PH_QUEUED_WAITER_SPINNING_SHIFT)) - { - PHLIB_INC_STATISTIC(QlBlockWaits); - - status = NtWaitForKeyedEvent( - PhQueuedLockKeyedEventHandle, - WaitBlock, - FALSE, - Timeout - ); - - // If an error occurred (timeout is not an error), raise an exception as it is nearly - // impossible to recover from this situation. - if (!NT_SUCCESS(status)) - PhRaiseStatus(status); - } - else - { - status = STATUS_SUCCESS; - } - - return status; -} - -/** - * Unblocks a wait block. - * - * \param WaitBlock A wait block. - * - * \remarks The wait block is in an undefined state after it is unblocked. Do not attempt to read - * any values from it. All relevant information should be saved before unblocking the wait block. - */ -_May_raise_ FORCEINLINE VOID PhpUnblockQueuedWaitBlock( - _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock - ) -{ - NTSTATUS status; - - if (!_interlockedbittestandreset((PLONG)&WaitBlock->Flags, PH_QUEUED_WAITER_SPINNING_SHIFT)) - { - if (!NT_SUCCESS(status = NtReleaseKeyedEvent( - PhQueuedLockKeyedEventHandle, - WaitBlock, - FALSE, - NULL - ))) - PhRaiseStatus(status); - } -} - -/** - * Optimizes a queued lock waiters list. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * \param IgnoreOwned TRUE to ignore lock state, FALSE to conduct normal checks. - * - * \remarks The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. - */ -FORCEINLINE VOID PhpOptimizeQueuedLockListEx( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value, - _In_ BOOLEAN IgnoreOwned - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - PPH_QUEUED_WAIT_BLOCK waitBlock; - PPH_QUEUED_WAIT_BLOCK firstWaitBlock; - PPH_QUEUED_WAIT_BLOCK lastWaitBlock; - PPH_QUEUED_WAIT_BLOCK previousWaitBlock; - - value = Value; - - while (TRUE) - { - assert(value & PH_QUEUED_LOCK_TRAVERSING); - - if (!IgnoreOwned && !(value & PH_QUEUED_LOCK_OWNED)) - { - // Someone has requested that we wake waiters. - PhpfWakeQueuedLock(QueuedLock, value); - break; - } - - // Perform the optimization. - - waitBlock = PhGetQueuedLockWaitBlock(value); - firstWaitBlock = waitBlock; - - while (TRUE) - { - lastWaitBlock = waitBlock->Last; - - if (lastWaitBlock) - { - // Save a pointer to the last wait block in the first wait block and stop - // optimizing. - // - // We don't need to continue setting Previous pointers because the last optimization - // run would have set them already. - - firstWaitBlock->Last = lastWaitBlock; - break; - } - - previousWaitBlock = waitBlock; - waitBlock = waitBlock->Next; - waitBlock->Previous = previousWaitBlock; - } - - // Try to clear the traversing bit. - - newValue = value - PH_QUEUED_LOCK_TRAVERSING; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - break; - - // Either someone pushed a wait block onto the list or someone released ownership. In either - // case we need to go back. - - value = newValue; - } -} - -/** - * Optimizes a queued lock waiters list. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * - * \remarks The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. - */ -VOID FASTCALL PhpfOptimizeQueuedLockList( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value - ) -{ - PhpOptimizeQueuedLockListEx(QueuedLock, Value, FALSE); -} - -/** - * Dequeues the appropriate number of wait blocks in a queued lock. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * \param IgnoreOwned TRUE to ignore lock state, FALSE to conduct normal checks. - * \param WakeAll TRUE to remove all wait blocks, FALSE to decide based on the wait block type. - */ -FORCEINLINE PPH_QUEUED_WAIT_BLOCK PhpPrepareToWakeQueuedLock( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value, - _In_ BOOLEAN IgnoreOwned, - _In_ BOOLEAN WakeAll - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - PPH_QUEUED_WAIT_BLOCK waitBlock; - PPH_QUEUED_WAIT_BLOCK firstWaitBlock; - PPH_QUEUED_WAIT_BLOCK lastWaitBlock; - PPH_QUEUED_WAIT_BLOCK previousWaitBlock; - - value = Value; - - while (TRUE) - { - // If there are multiple shared owners, no one is going to wake waiters since the lock would - // still be owned. Also if there are multiple shared owners they may be traversing the list. - // While that is safe when done concurrently with list optimization, we may be removing and - // waking waiters. - assert(!(value & PH_QUEUED_LOCK_MULTIPLE_SHARED)); - assert(IgnoreOwned || (value & PH_QUEUED_LOCK_TRAVERSING)); - - // There's no point in waking a waiter if the lock is owned. Clear the traversing bit. - while (!IgnoreOwned && (value & PH_QUEUED_LOCK_OWNED)) - { - newValue = value - PH_QUEUED_LOCK_TRAVERSING; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - return NULL; - - value = newValue; - } - - // Finish up any needed optimization (setting the Previous pointers) while finding the last - // wait block. - - waitBlock = PhGetQueuedLockWaitBlock(value); - firstWaitBlock = waitBlock; - - while (TRUE) - { - lastWaitBlock = waitBlock->Last; - - if (lastWaitBlock) - { - waitBlock = lastWaitBlock; - break; - } - - previousWaitBlock = waitBlock; - waitBlock = waitBlock->Next; - waitBlock->Previous = previousWaitBlock; - } - - // Unlink the relevant wait blocks and clear the traversing bit before we wake waiters. - - if ( - !WakeAll && - (waitBlock->Flags & PH_QUEUED_WAITER_EXCLUSIVE) && - (previousWaitBlock = waitBlock->Previous) - ) - { - // We have an exclusive waiter and there are multiple waiters. We'll only be waking this - // waiter. - - // Unlink the wait block from the list. Although other wait blocks may have their Last - // pointers set to this wait block, the algorithm to find the last wait block will stop - // here. Likewise the Next pointers are never followed beyond this point, so we don't - // need to clear those. - firstWaitBlock->Last = previousWaitBlock; - - // Make sure we only wake this waiter. - waitBlock->Previous = NULL; - - if (!IgnoreOwned) - { - // Clear the traversing bit. - _InterlockedExchangeAddPointer((PLONG_PTR)&QueuedLock->Value, -(LONG_PTR)PH_QUEUED_LOCK_TRAVERSING); - } - - break; - } - else - { - // We're waking an exclusive waiter and there is only one waiter, or we are waking a - // shared waiter and possibly others. - - newValue = 0; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - break; - - // Someone changed the lock (acquired it or pushed a wait block). - - value = newValue; - } - } - - return waitBlock; -} - -/** - * Wakes waiters in a queued lock. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * - * \remarks The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. The function assumes the following - * flags are not set: - * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED. - */ -VOID FASTCALL PhpfWakeQueuedLock( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value - ) -{ - PPH_QUEUED_WAIT_BLOCK waitBlock; - PPH_QUEUED_WAIT_BLOCK previousWaitBlock; - - waitBlock = PhpPrepareToWakeQueuedLock(QueuedLock, Value, FALSE, FALSE); - - // Wake waiters. - - while (waitBlock) - { - previousWaitBlock = waitBlock->Previous; - PhpUnblockQueuedWaitBlock(waitBlock); - waitBlock = previousWaitBlock; - } -} - -/** - * Wakes waiters in a queued lock. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * \param IgnoreOwned TRUE to ignore lock state, FALSE to conduct normal checks. - * \param WakeAll TRUE to wake all waiters, FALSE to decide based on the wait block type. - * - * \remarks The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. The function assumes the following - * flags are not set: - * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED. - */ -VOID FASTCALL PhpfWakeQueuedLockEx( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value, - _In_ BOOLEAN IgnoreOwned, - _In_ BOOLEAN WakeAll - ) -{ - PPH_QUEUED_WAIT_BLOCK waitBlock; - PPH_QUEUED_WAIT_BLOCK previousWaitBlock; - - waitBlock = PhpPrepareToWakeQueuedLock(QueuedLock, Value, IgnoreOwned, WakeAll); - - // Wake waiters. - - while (waitBlock) - { - previousWaitBlock = waitBlock->Previous; - PhpUnblockQueuedWaitBlock(waitBlock); - waitBlock = previousWaitBlock; - } -} - -/** - * Acquires a queued lock in exclusive mode. - * - * \param QueuedLock A queued lock. - */ -VOID FASTCALL PhfAcquireQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - ULONG_PTR currentValue; - BOOLEAN optimize; - PH_QUEUED_WAIT_BLOCK waitBlock; - - value = QueuedLock->Value; - - while (TRUE) - { - if (!(value & PH_QUEUED_LOCK_OWNED)) - { - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)(value + PH_QUEUED_LOCK_OWNED), - (PVOID)value - )) == value) - break; - } - else - { - if (PhpPushQueuedWaitBlock( - QueuedLock, - value, - TRUE, - &waitBlock, - &optimize, - &newValue, - ¤tValue - )) - { - if (optimize) - PhpfOptimizeQueuedLockList(QueuedLock, currentValue); - - PHLIB_INC_STATISTIC(QlAcquireExclusiveBlocks); - PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); - } - } - - value = newValue; - } -} - -/** - * Acquires a queued lock in shared mode. - * - * \param QueuedLock A queued lock. - */ -VOID FASTCALL PhfAcquireQueuedLockShared( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - ULONG_PTR currentValue; - BOOLEAN optimize; - PH_QUEUED_WAIT_BLOCK waitBlock; - - value = QueuedLock->Value; - - while (TRUE) - { - // We can't acquire if there are waiters for two reasons: - // - // We want to prioritize exclusive acquires over shared acquires. There's currently no fast, - // safe way of finding the last wait block and incrementing the shared owners count here. - if ( - !(value & PH_QUEUED_LOCK_WAITERS) && - (!(value & PH_QUEUED_LOCK_OWNED) || (PhGetQueuedLockSharedOwners(value) > 0)) - ) - { - newValue = (value + PH_QUEUED_LOCK_SHARED_INC) | PH_QUEUED_LOCK_OWNED; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - break; - } - else - { - if (PhpPushQueuedWaitBlock( - QueuedLock, - value, - FALSE, - &waitBlock, - &optimize, - &newValue, - ¤tValue - )) - { - if (optimize) - PhpfOptimizeQueuedLockList(QueuedLock, currentValue); - - PHLIB_INC_STATISTIC(QlAcquireSharedBlocks); - PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); - } - } - - value = newValue; - } -} - -/** - * Releases a queued lock in exclusive mode. - * - * \param QueuedLock A queued lock. - */ -VOID FASTCALL PhfReleaseQueuedLockExclusive( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - ULONG_PTR currentValue; - - value = QueuedLock->Value; - - while (TRUE) - { - assert(value & PH_QUEUED_LOCK_OWNED); - assert((value & PH_QUEUED_LOCK_WAITERS) || (PhGetQueuedLockSharedOwners(value) == 0)); - - if ((value & (PH_QUEUED_LOCK_WAITERS | PH_QUEUED_LOCK_TRAVERSING)) != PH_QUEUED_LOCK_WAITERS) - { - // There are no waiters or someone is traversing the list. - // - // If there are no waiters, we're simply releasing ownership. If someone is traversing - // the list, clearing the owned bit is a signal for them to wake waiters. - - newValue = value - PH_QUEUED_LOCK_OWNED; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - break; - } - else - { - // We need to wake waiters and no one is traversing the list. - // Try to set the traversing bit and wake waiters. - - newValue = value - PH_QUEUED_LOCK_OWNED + PH_QUEUED_LOCK_TRAVERSING; - currentValue = newValue; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - { - PhpfWakeQueuedLock(QueuedLock, currentValue); - break; - } - } - - value = newValue; - } -} - -/** - * Wakes waiters in a queued lock for releasing it in exclusive mode. - * - * \param QueuedLock A queued lock. - * \param Value The current value of the queued lock. - * - * \remarks The function assumes the following flags are set: - * \ref PH_QUEUED_LOCK_WAITERS. The function assumes the following flags are not set: - * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED, \ref PH_QUEUED_LOCK_TRAVERSING. - */ -VOID FASTCALL PhfWakeForReleaseQueuedLock( - _Inout_ PPH_QUEUED_LOCK QueuedLock, - _In_ ULONG_PTR Value - ) -{ - ULONG_PTR newValue; - - newValue = Value + PH_QUEUED_LOCK_TRAVERSING; - - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)Value - ) == Value) - { - PhpfWakeQueuedLock(QueuedLock, newValue); - } -} - -/** - * Releases a queued lock in shared mode. - * - * \param QueuedLock A queued lock. - */ -VOID FASTCALL PhfReleaseQueuedLockShared( - _Inout_ PPH_QUEUED_LOCK QueuedLock - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - ULONG_PTR currentValue; - PPH_QUEUED_WAIT_BLOCK waitBlock; - - value = QueuedLock->Value; - - while (!(value & PH_QUEUED_LOCK_WAITERS)) - { - assert(value & PH_QUEUED_LOCK_OWNED); - assert((value & PH_QUEUED_LOCK_WAITERS) || (PhGetQueuedLockSharedOwners(value) > 0)); - - if (PhGetQueuedLockSharedOwners(value) > 1) - newValue = value - PH_QUEUED_LOCK_SHARED_INC; - else - newValue = 0; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - return; - - value = newValue; - } - - if (value & PH_QUEUED_LOCK_MULTIPLE_SHARED) - { - // Unfortunately we have to find the last wait block and decrement the shared owners count. - waitBlock = PhpFindLastQueuedWaitBlock(value); - - if ((ULONG)_InterlockedDecrement((PLONG)&waitBlock->SharedOwners) > 0) - return; - } - - while (TRUE) - { - if (value & PH_QUEUED_LOCK_TRAVERSING) - { - newValue = value & ~(PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_MULTIPLE_SHARED); - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - break; - } - else - { - newValue = (value & ~(PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_MULTIPLE_SHARED)) | - PH_QUEUED_LOCK_TRAVERSING; - currentValue = newValue; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&QueuedLock->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - { - PhpfWakeQueuedLock(QueuedLock, currentValue); - break; - } - } - - value = newValue; - } -} - -/** - * Wakes one thread sleeping on a condition variable. - * - * \param Condition A condition variable. - * - * \remarks The associated lock must be acquired before calling the function. - */ -VOID FASTCALL PhfPulseCondition( - _Inout_ PPH_CONDITION Condition - ) -{ - if (Condition->Value & PH_QUEUED_LOCK_WAITERS) - PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, FALSE); -} - -/** - * Wakes all threads sleeping on a condition variable. - * - * \param Condition A condition variable. - * - * \remarks The associated lock must be acquired before calling the function. - */ -VOID FASTCALL PhfPulseAllCondition( - _Inout_ PPH_CONDITION Condition - ) -{ - if (Condition->Value & PH_QUEUED_LOCK_WAITERS) - PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, TRUE); -} - -/** - * Sleeps on a condition variable. - * - * \param Condition A condition variable. - * \param Lock A queued lock to release/acquire in exclusive mode. - * \param Timeout Not implemented. - * - * \remarks The associated lock must be acquired before calling the function. - */ -VOID FASTCALL PhfWaitForCondition( - _Inout_ PPH_CONDITION Condition, - _Inout_ PPH_QUEUED_LOCK Lock, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - ULONG_PTR value; - ULONG_PTR currentValue; - PH_QUEUED_WAIT_BLOCK waitBlock; - BOOLEAN optimize; - - value = Condition->Value; - - while (TRUE) - { - if (PhpPushQueuedWaitBlock( - Condition, - value, - TRUE, - &waitBlock, - &optimize, - &value, - ¤tValue - )) - { - if (optimize) - { - PhpOptimizeQueuedLockListEx(Condition, currentValue, TRUE); - } - - PhReleaseQueuedLockExclusive(Lock); - - PhpBlockOnQueuedWaitBlock(&waitBlock, FALSE, NULL); - - // Don't use the inline variant; it is extremely likely that the lock is still owned. - PhfAcquireQueuedLockExclusive(Lock); - - break; - } - } -} - -/** - * Sleeps on a condition variable. - * - * \param Condition A condition variable. - * \param Lock A pointer to a lock. - * \param Flags A combination of flags controlling the operation. - * \param Timeout Not implemented. - */ -VOID FASTCALL PhfWaitForConditionEx( - _Inout_ PPH_CONDITION Condition, - _Inout_ PVOID Lock, - _In_ ULONG Flags, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - ULONG_PTR value; - ULONG_PTR currentValue; - PH_QUEUED_WAIT_BLOCK waitBlock; - BOOLEAN optimize; - - value = Condition->Value; - - while (TRUE) - { - if (PhpPushQueuedWaitBlock( - Condition, - value, - TRUE, - &waitBlock, - &optimize, - &value, - ¤tValue - )) - { - if (optimize) - { - PhpOptimizeQueuedLockListEx(Condition, currentValue, TRUE); - } - - switch (Flags & PH_CONDITION_WAIT_LOCK_TYPE_MASK) - { - case PH_CONDITION_WAIT_QUEUED_LOCK: - if (!(Flags & PH_CONDITION_WAIT_SHARED)) - PhReleaseQueuedLockExclusive((PPH_QUEUED_LOCK)Lock); - else - PhReleaseQueuedLockShared((PPH_QUEUED_LOCK)Lock); - break; - case PH_CONDITION_WAIT_CRITICAL_SECTION: - RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)Lock); - break; - case PH_CONDITION_WAIT_FAST_LOCK: - if (!(Flags & PH_CONDITION_WAIT_SHARED)) - PhReleaseFastLockExclusive((PPH_FAST_LOCK)Lock); - else - PhReleaseFastLockShared((PPH_FAST_LOCK)Lock); - break; - } - - if (!(Flags & PH_CONDITION_WAIT_SPIN)) - { - PhpBlockOnQueuedWaitBlock(&waitBlock, FALSE, NULL); - } - else - { - PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); - } - - switch (Flags & PH_CONDITION_WAIT_LOCK_TYPE_MASK) - { - case PH_CONDITION_WAIT_QUEUED_LOCK: - if (!(Flags & PH_CONDITION_WAIT_SHARED)) - PhfAcquireQueuedLockExclusive((PPH_QUEUED_LOCK)Lock); - else - PhfAcquireQueuedLockShared((PPH_QUEUED_LOCK)Lock); - break; - case PH_CONDITION_WAIT_CRITICAL_SECTION: - RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)Lock); - break; - case PH_CONDITION_WAIT_FAST_LOCK: - if (!(Flags & PH_CONDITION_WAIT_SHARED)) - PhAcquireFastLockExclusive((PPH_FAST_LOCK)Lock); - else - PhAcquireFastLockShared((PPH_FAST_LOCK)Lock); - break; - } - - break; - } - } -} - -/** - * Queues a wait block to a wake event. - * - * \param WakeEvent A wake event. - * \param WaitBlock A wait block. - * - * \remarks If you later determine that the wait should not occur, you must call PhfSetWakeEvent() - * to dequeue the wait block. - */ -VOID FASTCALL PhfQueueWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Out_ PPH_QUEUED_WAIT_BLOCK WaitBlock - ) -{ - PPH_QUEUED_WAIT_BLOCK value; - PPH_QUEUED_WAIT_BLOCK newValue; - - WaitBlock->Flags = PH_QUEUED_WAITER_SPINNING; - - value = (PPH_QUEUED_WAIT_BLOCK)WakeEvent->Value; - - while (TRUE) - { - WaitBlock->Next = value; - - if ((newValue = _InterlockedCompareExchangePointer( - (PVOID *)&WakeEvent->Value, - WaitBlock, - value - )) == value) - break; - - value = newValue; - } -} - -/** - * Sets a wake event, unblocking all queued wait blocks. - * - * \param WakeEvent A wake event. - * \param WaitBlock A wait block for a cancelled wait, otherwise NULL. - */ -VOID FASTCALL PhfSetWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Inout_opt_ PPH_QUEUED_WAIT_BLOCK WaitBlock - ) -{ - PPH_QUEUED_WAIT_BLOCK waitBlock; - PPH_QUEUED_WAIT_BLOCK nextWaitBlock; - - // Pop all waiters and unblock them. - - waitBlock = _InterlockedExchangePointer((PVOID *)&WakeEvent->Value, NULL); - - while (waitBlock) - { - nextWaitBlock = waitBlock->Next; - PhpUnblockQueuedWaitBlock(waitBlock); - waitBlock = nextWaitBlock; - } - - if (WaitBlock) - { - // We're cancelling a wait; the thread called this function instead of PhfWaitForWakeEvent. - // This will remove all waiters from the list. However, we may not have popped and unblocked - // the cancelled wait block ourselves. Another thread may have popped all waiters but not - // unblocked them yet at this point: - // - // 1. This thread: calls PhfQueueWakeEvent. - // 2. This thread: code determines that the wait should be cancelled. - // 3. Other thread: calls PhfSetWakeEvent and pops our wait block off. It hasn't unblocked - // any wait blocks yet. - // 4. This thread: calls PhfSetWakeEvent. Since all wait blocks have been popped, we don't - // do anything. The caller function exits, making our wait block invalid. - // 5. Other thread: tries to unblock our wait block. Anything could happen, since our caller - // already returned. - // - // The solution is to (always) wait for an unblock. Note that the check below for the - // spinning flag is not required, but it is a small optimization. If the wait block has been - // unblocked (i.e. the spinning flag is cleared), then there's no danger. - - if (WaitBlock->Flags & PH_QUEUED_WAITER_SPINNING) - PhpBlockOnQueuedWaitBlock(WaitBlock, FALSE, NULL); - } -} - -/** - * Waits for a wake event to be set. - * - * \param WakeEvent A wake event. - * \param WaitBlock A wait block previously queued to the wake event using PhfQueueWakeEvent(). - * \param Spin TRUE to spin on the wake event before blocking, FALSE to block immediately. - * \param Timeout A timeout value. - * - * \remarks Wake events are subject to spurious wakeups. You should call this function in a loop - * which checks a predicate. - */ -NTSTATUS FASTCALL PhfWaitForWakeEvent( - _Inout_ PPH_WAKE_EVENT WakeEvent, - _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock, - _In_ BOOLEAN Spin, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - NTSTATUS status; - - status = PhpBlockOnQueuedWaitBlock(WaitBlock, Spin, Timeout); - - if (status != STATUS_SUCCESS) - { - // Probably a timeout. There's no way of unlinking the wait block safely, so just wake - // everyone. - PhSetWakeEvent(WakeEvent, WaitBlock); - } - - return status; -} +/* + * Process Hacker - + * queued lock + * + * Copyright (C) 2010-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * Queued lock, a.k.a. push lock (kernel-mode) or slim reader-writer lock (user-mode). + * + * The queued lock is: + * * Around 10% faster than the fast lock. + * * Only the size of a pointer. + * * Low on resource usage (no additional kernel objects are created for blocking). + * + * The usual flags are used for contention-free acquire/release. When there is contention, + * stack-based wait blocks are chained. The first wait block contains the shared owners count which + * is decremented by shared releasers. + * + * Naturally these wait blocks would be chained in FILO order, but list optimization is done for two + * purposes: + * * Finding the last wait block (where the shared owners count is stored). This is implemented by + * the Last pointer. + * * Unblocking the wait blocks in FIFO order. This is implemented by the Previous pointer. + * + * The optimization is incremental - each optimization run will stop at the first optimized wait + * block. Any needed optimization is completed just before waking waiters. + * + * The waiters list/chain has the following restrictions: + * * At any time wait blocks may be pushed onto the list. + * * While waking waiters, the list may not be traversed nor optimized. + * * When there are multiple shared owners, shared releasers may traverse the list (to find the last + * wait block). This is not an issue because waiters wouldn't be woken until there are no more + * shared owners. + * * List optimization may be done at any time except for when someone else is waking waiters. This + * is controlled by the traversing bit. + * + * The traversing bit has the following rules: + * * The list may be optimized only after the traversing bit is set, checking that it wasn't set + * already. If it was set, it would indicate that someone else is optimizing the list or waking + * waiters. + * * Before waking waiters the traversing bit must be set. If it was set already, just clear the + * owned bit. + * * If during list optimization the owned bit is detected to be cleared, the function begins waking + * waiters. This is because the owned bit is cleared when a releaser fails to set the traversing + * bit. + * + * Blocking is implemented through a process-wide keyed event. A spin count is also used before + * blocking on the keyed event. + * + * Queued locks can act as condition variables, with wait, pulse and pulse all support. Waiters are + * released in FIFO order. + * + * Queued locks can act as wake events. These are designed for tiny one-bit locks which share a + * single event to block on. Spurious wake-ups are a part of normal operation. + */ + +#include + +#include +#include +#include + +VOID FASTCALL PhpfOptimizeQueuedLockList( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value + ); + +VOID FASTCALL PhpfWakeQueuedLock( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value + ); + +VOID FASTCALL PhpfWakeQueuedLockEx( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value, + _In_ BOOLEAN IgnoreOwned, + _In_ BOOLEAN WakeAll + ); + +static HANDLE PhQueuedLockKeyedEventHandle; +static ULONG PhQueuedLockSpinCount = 2000; + +BOOLEAN PhQueuedLockInitialization( + VOID + ) +{ + if (!NT_SUCCESS(NtCreateKeyedEvent( + &PhQueuedLockKeyedEventHandle, + KEYEDEVENT_ALL_ACCESS, + NULL, + 0 + ))) + return FALSE; + + if ((ULONG)PhSystemBasicInformation.NumberOfProcessors > 1) + PhQueuedLockSpinCount = 4000; + else + PhQueuedLockSpinCount = 0; + + return TRUE; +} + +/** + * Pushes a wait block onto a queued lock's waiters list. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * \param Exclusive Whether the wait block is in exclusive mode. + * \param WaitBlock A variable which receives the resulting wait block structure. + * \param Optimize A variable which receives a boolean indicating whether to optimize the waiters + * list. + * \param NewValue The old value of the queued lock. This value is useful only if the function + * returns FALSE. + * \param CurrentValue The new value of the queued lock. This value is useful only if the function + * returns TRUE. + * + * \return TRUE if the wait block was pushed onto the waiters list, otherwise FALSE. + * + * \remarks + * \li The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_OWNED. + * \li Do not move the wait block location after this function is called. + * \li The \a Optimize boolean is a hint to call PhpfOptimizeQueuedLockList() if the function + * succeeds. It is recommended, but not essential that this occurs. + * \li Call PhpBlockOnQueuedWaitBlock() to wait for the wait block to be unblocked. + */ +FORCEINLINE BOOLEAN PhpPushQueuedWaitBlock( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value, + _In_ BOOLEAN Exclusive, + _Out_ PPH_QUEUED_WAIT_BLOCK WaitBlock, + _Out_ PBOOLEAN Optimize, + _Out_ PULONG_PTR NewValue, + _Out_ PULONG_PTR CurrentValue + ) +{ + ULONG_PTR newValue; + BOOLEAN optimize; + + WaitBlock->Previous = NULL; // set later by optimization + optimize = FALSE; + + if (Exclusive) + WaitBlock->Flags = PH_QUEUED_WAITER_EXCLUSIVE | PH_QUEUED_WAITER_SPINNING; + else + WaitBlock->Flags = PH_QUEUED_WAITER_SPINNING; + + if (Value & PH_QUEUED_LOCK_WAITERS) + { + // We're not the first waiter. + + WaitBlock->Last = NULL; // set later by optimization + WaitBlock->Next = PhGetQueuedLockWaitBlock(Value); + WaitBlock->SharedOwners = 0; + + // Push our wait block onto the list. + // Set the traversing bit because we'll be optimizing the list. + newValue = ((ULONG_PTR)WaitBlock) | (Value & PH_QUEUED_LOCK_FLAGS) | + PH_QUEUED_LOCK_TRAVERSING; + + if (!(Value & PH_QUEUED_LOCK_TRAVERSING)) + optimize = TRUE; + } + else + { + // We're the first waiter. + + WaitBlock->Last = WaitBlock; // indicate that this is the last wait block + + if (Exclusive) + { + // We're the first waiter. Save the shared owners count. + WaitBlock->SharedOwners = (ULONG)PhGetQueuedLockSharedOwners(Value); + + if (WaitBlock->SharedOwners > 1) + { + newValue = ((ULONG_PTR)WaitBlock) | PH_QUEUED_LOCK_OWNED | + PH_QUEUED_LOCK_WAITERS | PH_QUEUED_LOCK_MULTIPLE_SHARED; + } + else + { + newValue = ((ULONG_PTR)WaitBlock) | PH_QUEUED_LOCK_OWNED | + PH_QUEUED_LOCK_WAITERS; + } + } + else + { + // We're waiting in shared mode, which means there can't be any shared owners (otherwise + // we would've acquired the lock already). + + WaitBlock->SharedOwners = 0; + + newValue = ((ULONG_PTR)WaitBlock) | PH_QUEUED_LOCK_OWNED | + PH_QUEUED_LOCK_WAITERS; + } + } + + *Optimize = optimize; + *CurrentValue = newValue; + + newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)Value + ); + + *NewValue = newValue; + + return newValue == Value; +} + +/** + * Finds the last wait block in the waiters list. + * + * \param Value The current value of the queued lock. + * + * \return A pointer to the last wait block. + * + * \remarks The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_WAITERS, + * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED or + * \ref PH_QUEUED_LOCK_TRAVERSING. + */ +FORCEINLINE PPH_QUEUED_WAIT_BLOCK PhpFindLastQueuedWaitBlock( + _In_ ULONG_PTR Value + ) +{ + PPH_QUEUED_WAIT_BLOCK waitBlock; + PPH_QUEUED_WAIT_BLOCK lastWaitBlock; + + waitBlock = PhGetQueuedLockWaitBlock(Value); + + // Traverse the list until we find the last wait block. + // The Last pointer should be set by list optimization, allowing us to skip all, if not most of + // the wait blocks. + + while (TRUE) + { + lastWaitBlock = waitBlock->Last; + + if (lastWaitBlock) + { + // Follow the Last pointer. This can mean two things: the pointer was set by list + // optimization, or this wait block is actually the last wait block (set when it was + // pushed onto the list). + waitBlock = lastWaitBlock; + break; + } + + waitBlock = waitBlock->Next; + } + + return waitBlock; +} + +/** + * Waits for a wait block to be unblocked. + * + * \param WaitBlock A wait block. + * \param Spin TRUE to spin, FALSE to block immediately. + * \param Timeout A timeout value. + */ +_May_raise_ FORCEINLINE NTSTATUS PhpBlockOnQueuedWaitBlock( + _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock, + _In_ BOOLEAN Spin, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + NTSTATUS status; + ULONG i; + + if (Spin) + { + PHLIB_INC_STATISTIC(QlBlockSpins); + + for (i = PhQueuedLockSpinCount; i != 0; i--) + { + if (!(*(volatile ULONG *)&WaitBlock->Flags & PH_QUEUED_WAITER_SPINNING)) + return STATUS_SUCCESS; + + YieldProcessor(); + } + } + + if (_interlockedbittestandreset((PLONG)&WaitBlock->Flags, PH_QUEUED_WAITER_SPINNING_SHIFT)) + { + PHLIB_INC_STATISTIC(QlBlockWaits); + + status = NtWaitForKeyedEvent( + PhQueuedLockKeyedEventHandle, + WaitBlock, + FALSE, + Timeout + ); + + // If an error occurred (timeout is not an error), raise an exception as it is nearly + // impossible to recover from this situation. + if (!NT_SUCCESS(status)) + PhRaiseStatus(status); + } + else + { + status = STATUS_SUCCESS; + } + + return status; +} + +/** + * Unblocks a wait block. + * + * \param WaitBlock A wait block. + * + * \remarks The wait block is in an undefined state after it is unblocked. Do not attempt to read + * any values from it. All relevant information should be saved before unblocking the wait block. + */ +_May_raise_ FORCEINLINE VOID PhpUnblockQueuedWaitBlock( + _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock + ) +{ + NTSTATUS status; + + if (!_interlockedbittestandreset((PLONG)&WaitBlock->Flags, PH_QUEUED_WAITER_SPINNING_SHIFT)) + { + if (!NT_SUCCESS(status = NtReleaseKeyedEvent( + PhQueuedLockKeyedEventHandle, + WaitBlock, + FALSE, + NULL + ))) + PhRaiseStatus(status); + } +} + +/** + * Optimizes a queued lock waiters list. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * \param IgnoreOwned TRUE to ignore lock state, FALSE to conduct normal checks. + * + * \remarks The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. + */ +FORCEINLINE VOID PhpOptimizeQueuedLockListEx( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value, + _In_ BOOLEAN IgnoreOwned + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + PPH_QUEUED_WAIT_BLOCK waitBlock; + PPH_QUEUED_WAIT_BLOCK firstWaitBlock; + PPH_QUEUED_WAIT_BLOCK lastWaitBlock; + PPH_QUEUED_WAIT_BLOCK previousWaitBlock; + + value = Value; + + while (TRUE) + { + assert(value & PH_QUEUED_LOCK_TRAVERSING); + + if (!IgnoreOwned && !(value & PH_QUEUED_LOCK_OWNED)) + { + // Someone has requested that we wake waiters. + PhpfWakeQueuedLock(QueuedLock, value); + break; + } + + // Perform the optimization. + + waitBlock = PhGetQueuedLockWaitBlock(value); + firstWaitBlock = waitBlock; + + while (TRUE) + { + lastWaitBlock = waitBlock->Last; + + if (lastWaitBlock) + { + // Save a pointer to the last wait block in the first wait block and stop + // optimizing. + // + // We don't need to continue setting Previous pointers because the last optimization + // run would have set them already. + + firstWaitBlock->Last = lastWaitBlock; + break; + } + + previousWaitBlock = waitBlock; + waitBlock = waitBlock->Next; + waitBlock->Previous = previousWaitBlock; + } + + // Try to clear the traversing bit. + + newValue = value - PH_QUEUED_LOCK_TRAVERSING; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + break; + + // Either someone pushed a wait block onto the list or someone released ownership. In either + // case we need to go back. + + value = newValue; + } +} + +/** + * Optimizes a queued lock waiters list. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * + * \remarks The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. + */ +VOID FASTCALL PhpfOptimizeQueuedLockList( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value + ) +{ + PhpOptimizeQueuedLockListEx(QueuedLock, Value, FALSE); +} + +/** + * Dequeues the appropriate number of wait blocks in a queued lock. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * \param IgnoreOwned TRUE to ignore lock state, FALSE to conduct normal checks. + * \param WakeAll TRUE to remove all wait blocks, FALSE to decide based on the wait block type. + */ +FORCEINLINE PPH_QUEUED_WAIT_BLOCK PhpPrepareToWakeQueuedLock( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value, + _In_ BOOLEAN IgnoreOwned, + _In_ BOOLEAN WakeAll + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + PPH_QUEUED_WAIT_BLOCK waitBlock; + PPH_QUEUED_WAIT_BLOCK firstWaitBlock; + PPH_QUEUED_WAIT_BLOCK lastWaitBlock; + PPH_QUEUED_WAIT_BLOCK previousWaitBlock; + + value = Value; + + while (TRUE) + { + // If there are multiple shared owners, no one is going to wake waiters since the lock would + // still be owned. Also if there are multiple shared owners they may be traversing the list. + // While that is safe when done concurrently with list optimization, we may be removing and + // waking waiters. + assert(!(value & PH_QUEUED_LOCK_MULTIPLE_SHARED)); + assert(IgnoreOwned || (value & PH_QUEUED_LOCK_TRAVERSING)); + + // There's no point in waking a waiter if the lock is owned. Clear the traversing bit. + while (!IgnoreOwned && (value & PH_QUEUED_LOCK_OWNED)) + { + newValue = value - PH_QUEUED_LOCK_TRAVERSING; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + return NULL; + + value = newValue; + } + + // Finish up any needed optimization (setting the Previous pointers) while finding the last + // wait block. + + waitBlock = PhGetQueuedLockWaitBlock(value); + firstWaitBlock = waitBlock; + + while (TRUE) + { + lastWaitBlock = waitBlock->Last; + + if (lastWaitBlock) + { + waitBlock = lastWaitBlock; + break; + } + + previousWaitBlock = waitBlock; + waitBlock = waitBlock->Next; + waitBlock->Previous = previousWaitBlock; + } + + // Unlink the relevant wait blocks and clear the traversing bit before we wake waiters. + + if ( + !WakeAll && + (waitBlock->Flags & PH_QUEUED_WAITER_EXCLUSIVE) && + (previousWaitBlock = waitBlock->Previous) + ) + { + // We have an exclusive waiter and there are multiple waiters. We'll only be waking this + // waiter. + + // Unlink the wait block from the list. Although other wait blocks may have their Last + // pointers set to this wait block, the algorithm to find the last wait block will stop + // here. Likewise the Next pointers are never followed beyond this point, so we don't + // need to clear those. + firstWaitBlock->Last = previousWaitBlock; + + // Make sure we only wake this waiter. + waitBlock->Previous = NULL; + + if (!IgnoreOwned) + { + // Clear the traversing bit. + _InterlockedExchangeAddPointer((PLONG_PTR)&QueuedLock->Value, -(LONG_PTR)PH_QUEUED_LOCK_TRAVERSING); + } + + break; + } + else + { + // We're waking an exclusive waiter and there is only one waiter, or we are waking a + // shared waiter and possibly others. + + newValue = 0; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + break; + + // Someone changed the lock (acquired it or pushed a wait block). + + value = newValue; + } + } + + return waitBlock; +} + +/** + * Wakes waiters in a queued lock. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * + * \remarks The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. The function assumes the following + * flags are not set: + * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED. + */ +VOID FASTCALL PhpfWakeQueuedLock( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value + ) +{ + PPH_QUEUED_WAIT_BLOCK waitBlock; + PPH_QUEUED_WAIT_BLOCK previousWaitBlock; + + waitBlock = PhpPrepareToWakeQueuedLock(QueuedLock, Value, FALSE, FALSE); + + // Wake waiters. + + while (waitBlock) + { + previousWaitBlock = waitBlock->Previous; + PhpUnblockQueuedWaitBlock(waitBlock); + waitBlock = previousWaitBlock; + } +} + +/** + * Wakes waiters in a queued lock. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * \param IgnoreOwned TRUE to ignore lock state, FALSE to conduct normal checks. + * \param WakeAll TRUE to wake all waiters, FALSE to decide based on the wait block type. + * + * \remarks The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_WAITERS, \ref PH_QUEUED_LOCK_TRAVERSING. The function assumes the following + * flags are not set: + * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED. + */ +VOID FASTCALL PhpfWakeQueuedLockEx( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value, + _In_ BOOLEAN IgnoreOwned, + _In_ BOOLEAN WakeAll + ) +{ + PPH_QUEUED_WAIT_BLOCK waitBlock; + PPH_QUEUED_WAIT_BLOCK previousWaitBlock; + + waitBlock = PhpPrepareToWakeQueuedLock(QueuedLock, Value, IgnoreOwned, WakeAll); + + // Wake waiters. + + while (waitBlock) + { + previousWaitBlock = waitBlock->Previous; + PhpUnblockQueuedWaitBlock(waitBlock); + waitBlock = previousWaitBlock; + } +} + +/** + * Acquires a queued lock in exclusive mode. + * + * \param QueuedLock A queued lock. + */ +VOID FASTCALL PhfAcquireQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + ULONG_PTR currentValue; + BOOLEAN optimize; + PH_QUEUED_WAIT_BLOCK waitBlock; + + value = QueuedLock->Value; + + while (TRUE) + { + if (!(value & PH_QUEUED_LOCK_OWNED)) + { + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)(value + PH_QUEUED_LOCK_OWNED), + (PVOID)value + )) == value) + break; + } + else + { + if (PhpPushQueuedWaitBlock( + QueuedLock, + value, + TRUE, + &waitBlock, + &optimize, + &newValue, + ¤tValue + )) + { + if (optimize) + PhpfOptimizeQueuedLockList(QueuedLock, currentValue); + + PHLIB_INC_STATISTIC(QlAcquireExclusiveBlocks); + PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); + } + } + + value = newValue; + } +} + +/** + * Acquires a queued lock in shared mode. + * + * \param QueuedLock A queued lock. + */ +VOID FASTCALL PhfAcquireQueuedLockShared( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + ULONG_PTR currentValue; + BOOLEAN optimize; + PH_QUEUED_WAIT_BLOCK waitBlock; + + value = QueuedLock->Value; + + while (TRUE) + { + // We can't acquire if there are waiters for two reasons: + // + // We want to prioritize exclusive acquires over shared acquires. There's currently no fast, + // safe way of finding the last wait block and incrementing the shared owners count here. + if ( + !(value & PH_QUEUED_LOCK_WAITERS) && + (!(value & PH_QUEUED_LOCK_OWNED) || (PhGetQueuedLockSharedOwners(value) > 0)) + ) + { + newValue = (value + PH_QUEUED_LOCK_SHARED_INC) | PH_QUEUED_LOCK_OWNED; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + break; + } + else + { + if (PhpPushQueuedWaitBlock( + QueuedLock, + value, + FALSE, + &waitBlock, + &optimize, + &newValue, + ¤tValue + )) + { + if (optimize) + PhpfOptimizeQueuedLockList(QueuedLock, currentValue); + + PHLIB_INC_STATISTIC(QlAcquireSharedBlocks); + PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); + } + } + + value = newValue; + } +} + +/** + * Releases a queued lock in exclusive mode. + * + * \param QueuedLock A queued lock. + */ +VOID FASTCALL PhfReleaseQueuedLockExclusive( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + ULONG_PTR currentValue; + + value = QueuedLock->Value; + + while (TRUE) + { + assert(value & PH_QUEUED_LOCK_OWNED); + assert((value & PH_QUEUED_LOCK_WAITERS) || (PhGetQueuedLockSharedOwners(value) == 0)); + + if ((value & (PH_QUEUED_LOCK_WAITERS | PH_QUEUED_LOCK_TRAVERSING)) != PH_QUEUED_LOCK_WAITERS) + { + // There are no waiters or someone is traversing the list. + // + // If there are no waiters, we're simply releasing ownership. If someone is traversing + // the list, clearing the owned bit is a signal for them to wake waiters. + + newValue = value - PH_QUEUED_LOCK_OWNED; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + break; + } + else + { + // We need to wake waiters and no one is traversing the list. + // Try to set the traversing bit and wake waiters. + + newValue = value - PH_QUEUED_LOCK_OWNED + PH_QUEUED_LOCK_TRAVERSING; + currentValue = newValue; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + { + PhpfWakeQueuedLock(QueuedLock, currentValue); + break; + } + } + + value = newValue; + } +} + +/** + * Wakes waiters in a queued lock for releasing it in exclusive mode. + * + * \param QueuedLock A queued lock. + * \param Value The current value of the queued lock. + * + * \remarks The function assumes the following flags are set: + * \ref PH_QUEUED_LOCK_WAITERS. The function assumes the following flags are not set: + * \ref PH_QUEUED_LOCK_MULTIPLE_SHARED, \ref PH_QUEUED_LOCK_TRAVERSING. + */ +VOID FASTCALL PhfWakeForReleaseQueuedLock( + _Inout_ PPH_QUEUED_LOCK QueuedLock, + _In_ ULONG_PTR Value + ) +{ + ULONG_PTR newValue; + + newValue = Value + PH_QUEUED_LOCK_TRAVERSING; + + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)Value + ) == Value) + { + PhpfWakeQueuedLock(QueuedLock, newValue); + } +} + +/** + * Releases a queued lock in shared mode. + * + * \param QueuedLock A queued lock. + */ +VOID FASTCALL PhfReleaseQueuedLockShared( + _Inout_ PPH_QUEUED_LOCK QueuedLock + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + ULONG_PTR currentValue; + PPH_QUEUED_WAIT_BLOCK waitBlock; + + value = QueuedLock->Value; + + while (!(value & PH_QUEUED_LOCK_WAITERS)) + { + assert(value & PH_QUEUED_LOCK_OWNED); + assert((value & PH_QUEUED_LOCK_WAITERS) || (PhGetQueuedLockSharedOwners(value) > 0)); + + if (PhGetQueuedLockSharedOwners(value) > 1) + newValue = value - PH_QUEUED_LOCK_SHARED_INC; + else + newValue = 0; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + return; + + value = newValue; + } + + if (value & PH_QUEUED_LOCK_MULTIPLE_SHARED) + { + // Unfortunately we have to find the last wait block and decrement the shared owners count. + waitBlock = PhpFindLastQueuedWaitBlock(value); + + if ((ULONG)_InterlockedDecrement((PLONG)&waitBlock->SharedOwners) > 0) + return; + } + + while (TRUE) + { + if (value & PH_QUEUED_LOCK_TRAVERSING) + { + newValue = value & ~(PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_MULTIPLE_SHARED); + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + break; + } + else + { + newValue = (value & ~(PH_QUEUED_LOCK_OWNED | PH_QUEUED_LOCK_MULTIPLE_SHARED)) | + PH_QUEUED_LOCK_TRAVERSING; + currentValue = newValue; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&QueuedLock->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + { + PhpfWakeQueuedLock(QueuedLock, currentValue); + break; + } + } + + value = newValue; + } +} + +/** + * Wakes one thread sleeping on a condition variable. + * + * \param Condition A condition variable. + * + * \remarks The associated lock must be acquired before calling the function. + */ +VOID FASTCALL PhfPulseCondition( + _Inout_ PPH_CONDITION Condition + ) +{ + if (Condition->Value & PH_QUEUED_LOCK_WAITERS) + PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, FALSE); +} + +/** + * Wakes all threads sleeping on a condition variable. + * + * \param Condition A condition variable. + * + * \remarks The associated lock must be acquired before calling the function. + */ +VOID FASTCALL PhfPulseAllCondition( + _Inout_ PPH_CONDITION Condition + ) +{ + if (Condition->Value & PH_QUEUED_LOCK_WAITERS) + PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, TRUE); +} + +/** + * Sleeps on a condition variable. + * + * \param Condition A condition variable. + * \param Lock A queued lock to release/acquire in exclusive mode. + * \param Timeout Not implemented. + * + * \remarks The associated lock must be acquired before calling the function. + */ +VOID FASTCALL PhfWaitForCondition( + _Inout_ PPH_CONDITION Condition, + _Inout_ PPH_QUEUED_LOCK Lock, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + ULONG_PTR value; + ULONG_PTR currentValue; + PH_QUEUED_WAIT_BLOCK waitBlock; + BOOLEAN optimize; + + value = Condition->Value; + + while (TRUE) + { + if (PhpPushQueuedWaitBlock( + Condition, + value, + TRUE, + &waitBlock, + &optimize, + &value, + ¤tValue + )) + { + if (optimize) + { + PhpOptimizeQueuedLockListEx(Condition, currentValue, TRUE); + } + + PhReleaseQueuedLockExclusive(Lock); + + PhpBlockOnQueuedWaitBlock(&waitBlock, FALSE, NULL); + + // Don't use the inline variant; it is extremely likely that the lock is still owned. + PhfAcquireQueuedLockExclusive(Lock); + + break; + } + } +} + +/** + * Sleeps on a condition variable. + * + * \param Condition A condition variable. + * \param Lock A pointer to a lock. + * \param Flags A combination of flags controlling the operation. + * \param Timeout Not implemented. + */ +VOID FASTCALL PhfWaitForConditionEx( + _Inout_ PPH_CONDITION Condition, + _Inout_ PVOID Lock, + _In_ ULONG Flags, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + ULONG_PTR value; + ULONG_PTR currentValue; + PH_QUEUED_WAIT_BLOCK waitBlock; + BOOLEAN optimize; + + value = Condition->Value; + + while (TRUE) + { + if (PhpPushQueuedWaitBlock( + Condition, + value, + TRUE, + &waitBlock, + &optimize, + &value, + ¤tValue + )) + { + if (optimize) + { + PhpOptimizeQueuedLockListEx(Condition, currentValue, TRUE); + } + + switch (Flags & PH_CONDITION_WAIT_LOCK_TYPE_MASK) + { + case PH_CONDITION_WAIT_QUEUED_LOCK: + if (!(Flags & PH_CONDITION_WAIT_SHARED)) + PhReleaseQueuedLockExclusive((PPH_QUEUED_LOCK)Lock); + else + PhReleaseQueuedLockShared((PPH_QUEUED_LOCK)Lock); + break; + case PH_CONDITION_WAIT_CRITICAL_SECTION: + RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)Lock); + break; + case PH_CONDITION_WAIT_FAST_LOCK: + if (!(Flags & PH_CONDITION_WAIT_SHARED)) + PhReleaseFastLockExclusive((PPH_FAST_LOCK)Lock); + else + PhReleaseFastLockShared((PPH_FAST_LOCK)Lock); + break; + } + + if (!(Flags & PH_CONDITION_WAIT_SPIN)) + { + PhpBlockOnQueuedWaitBlock(&waitBlock, FALSE, NULL); + } + else + { + PhpBlockOnQueuedWaitBlock(&waitBlock, TRUE, NULL); + } + + switch (Flags & PH_CONDITION_WAIT_LOCK_TYPE_MASK) + { + case PH_CONDITION_WAIT_QUEUED_LOCK: + if (!(Flags & PH_CONDITION_WAIT_SHARED)) + PhfAcquireQueuedLockExclusive((PPH_QUEUED_LOCK)Lock); + else + PhfAcquireQueuedLockShared((PPH_QUEUED_LOCK)Lock); + break; + case PH_CONDITION_WAIT_CRITICAL_SECTION: + RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)Lock); + break; + case PH_CONDITION_WAIT_FAST_LOCK: + if (!(Flags & PH_CONDITION_WAIT_SHARED)) + PhAcquireFastLockExclusive((PPH_FAST_LOCK)Lock); + else + PhAcquireFastLockShared((PPH_FAST_LOCK)Lock); + break; + } + + break; + } + } +} + +/** + * Queues a wait block to a wake event. + * + * \param WakeEvent A wake event. + * \param WaitBlock A wait block. + * + * \remarks If you later determine that the wait should not occur, you must call PhfSetWakeEvent() + * to dequeue the wait block. + */ +VOID FASTCALL PhfQueueWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Out_ PPH_QUEUED_WAIT_BLOCK WaitBlock + ) +{ + PPH_QUEUED_WAIT_BLOCK value; + PPH_QUEUED_WAIT_BLOCK newValue; + + WaitBlock->Flags = PH_QUEUED_WAITER_SPINNING; + + value = (PPH_QUEUED_WAIT_BLOCK)WakeEvent->Value; + + while (TRUE) + { + WaitBlock->Next = value; + + if ((newValue = _InterlockedCompareExchangePointer( + (PVOID *)&WakeEvent->Value, + WaitBlock, + value + )) == value) + break; + + value = newValue; + } +} + +/** + * Sets a wake event, unblocking all queued wait blocks. + * + * \param WakeEvent A wake event. + * \param WaitBlock A wait block for a cancelled wait, otherwise NULL. + */ +VOID FASTCALL PhfSetWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Inout_opt_ PPH_QUEUED_WAIT_BLOCK WaitBlock + ) +{ + PPH_QUEUED_WAIT_BLOCK waitBlock; + PPH_QUEUED_WAIT_BLOCK nextWaitBlock; + + // Pop all waiters and unblock them. + + waitBlock = _InterlockedExchangePointer((PVOID *)&WakeEvent->Value, NULL); + + while (waitBlock) + { + nextWaitBlock = waitBlock->Next; + PhpUnblockQueuedWaitBlock(waitBlock); + waitBlock = nextWaitBlock; + } + + if (WaitBlock) + { + // We're cancelling a wait; the thread called this function instead of PhfWaitForWakeEvent. + // This will remove all waiters from the list. However, we may not have popped and unblocked + // the cancelled wait block ourselves. Another thread may have popped all waiters but not + // unblocked them yet at this point: + // + // 1. This thread: calls PhfQueueWakeEvent. + // 2. This thread: code determines that the wait should be cancelled. + // 3. Other thread: calls PhfSetWakeEvent and pops our wait block off. It hasn't unblocked + // any wait blocks yet. + // 4. This thread: calls PhfSetWakeEvent. Since all wait blocks have been popped, we don't + // do anything. The caller function exits, making our wait block invalid. + // 5. Other thread: tries to unblock our wait block. Anything could happen, since our caller + // already returned. + // + // The solution is to (always) wait for an unblock. Note that the check below for the + // spinning flag is not required, but it is a small optimization. If the wait block has been + // unblocked (i.e. the spinning flag is cleared), then there's no danger. + + if (WaitBlock->Flags & PH_QUEUED_WAITER_SPINNING) + PhpBlockOnQueuedWaitBlock(WaitBlock, FALSE, NULL); + } +} + +/** + * Waits for a wake event to be set. + * + * \param WakeEvent A wake event. + * \param WaitBlock A wait block previously queued to the wake event using PhfQueueWakeEvent(). + * \param Spin TRUE to spin on the wake event before blocking, FALSE to block immediately. + * \param Timeout A timeout value. + * + * \remarks Wake events are subject to spurious wakeups. You should call this function in a loop + * which checks a predicate. + */ +NTSTATUS FASTCALL PhfWaitForWakeEvent( + _Inout_ PPH_WAKE_EVENT WakeEvent, + _Inout_ PPH_QUEUED_WAIT_BLOCK WaitBlock, + _In_ BOOLEAN Spin, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + NTSTATUS status; + + status = PhpBlockOnQueuedWaitBlock(WaitBlock, Spin, Timeout); + + if (status != STATUS_SUCCESS) + { + // Probably a timeout. There's no way of unlinking the wait block safely, so just wake + // everyone. + PhSetWakeEvent(WakeEvent, WaitBlock); + } + + return status; +} diff --git a/phlib/ref.c b/phlib/ref.c index faea6188bf42..c14cc3614b24 100644 --- a/phlib/ref.c +++ b/phlib/ref.c @@ -1,754 +1,789 @@ -/* - * Process Hacker - - * object manager - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -PPH_OBJECT_TYPE PhObjectTypeObject = NULL; -SLIST_HEADER PhObjectDeferDeleteListHead; -PH_FREE_LIST PhObjectSmallFreeList; -PPH_OBJECT_TYPE PhAllocType = NULL; - -ULONG PhObjectTypeCount = 0; -PPH_OBJECT_TYPE PhObjectTypeTable[PH_OBJECT_TYPE_TABLE_SIZE]; - -static ULONG PhpAutoPoolTlsIndex; - -#ifdef DEBUG -LIST_ENTRY PhDbgObjectListHead; -PH_QUEUED_LOCK PhDbgObjectListLock = PH_QUEUED_LOCK_INIT; -PPH_CREATE_OBJECT_HOOK PhDbgCreateObjectHook = NULL; -#endif - -#define REF_STAT_UP(Name) PHLIB_INC_STATISTIC(Name) - -/** - * Initializes the object manager module. - */ -NTSTATUS PhRefInitialization( - VOID - ) -{ - PH_OBJECT_TYPE dummyObjectType; - -#ifdef DEBUG - InitializeListHead(&PhDbgObjectListHead); -#endif - - RtlInitializeSListHead(&PhObjectDeferDeleteListHead); - PhInitializeFreeList( - &PhObjectSmallFreeList, - PhAddObjectHeaderSize(PH_OBJECT_SMALL_OBJECT_SIZE), - PH_OBJECT_SMALL_OBJECT_COUNT - ); - - // Create the fundamental object type. - - memset(&dummyObjectType, 0, sizeof(PH_OBJECT_TYPE)); - PhObjectTypeObject = &dummyObjectType; // PhCreateObject expects an object type. - PhObjectTypeTable[0] = &dummyObjectType; // PhCreateObject also expects PhObjectTypeTable[0] to be filled in. - PhObjectTypeObject = PhCreateObjectType(L"Type", 0, NULL); - - // Now that the fundamental object type exists, fix it up. - PhObjectToObjectHeader(PhObjectTypeObject)->TypeIndex = PhObjectTypeObject->TypeIndex; - PhObjectTypeObject->NumberOfObjects = 1; - - // Create the allocated memory object type. - PhAllocType = PhCreateObjectType(L"Alloc", 0, NULL); - - // Reserve a slot for the auto pool. - PhpAutoPoolTlsIndex = TlsAlloc(); - - if (PhpAutoPoolTlsIndex == TLS_OUT_OF_INDEXES) - return STATUS_INSUFFICIENT_RESOURCES; - - return STATUS_SUCCESS; -} - -/** - * Allocates a object. - * - * \param ObjectSize The size of the object. - * \param ObjectType The type of the object. - * - * \return A pointer to the newly allocated object. - */ -_May_raise_ PVOID PhCreateObject( - _In_ SIZE_T ObjectSize, - _In_ PPH_OBJECT_TYPE ObjectType - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PPH_OBJECT_HEADER objectHeader; - - // Allocate storage for the object. Note that this includes the object header followed by the - // object body. - objectHeader = PhpAllocateObject(ObjectType, ObjectSize); - - // Object type statistics. - _InterlockedIncrement((PLONG)&ObjectType->NumberOfObjects); - - // Initialize the object header. - objectHeader->RefCount = 1; - objectHeader->TypeIndex = ObjectType->TypeIndex; - // objectHeader->Flags is set by PhpAllocateObject. - - REF_STAT_UP(RefObjectsCreated); - -#ifdef DEBUG - { - USHORT capturedFrames; - - capturedFrames = RtlCaptureStackBackTrace(1, 16, objectHeader->StackBackTrace, NULL); - memset( - &objectHeader->StackBackTrace[capturedFrames], - 0, - sizeof(objectHeader->StackBackTrace) - capturedFrames * sizeof(PVOID) - ); - } - - PhAcquireQueuedLockExclusive(&PhDbgObjectListLock); - InsertTailList(&PhDbgObjectListHead, &objectHeader->ObjectListEntry); - PhReleaseQueuedLockExclusive(&PhDbgObjectListLock); - - { - PPH_CREATE_OBJECT_HOOK dbgCreateObjectHook; - - dbgCreateObjectHook = PhDbgCreateObjectHook; - - if (dbgCreateObjectHook) - { - dbgCreateObjectHook( - PhObjectHeaderToObject(objectHeader), - ObjectSize, - 0, - ObjectType - ); - } - } -#endif - - return PhObjectHeaderToObject(objectHeader); -} - -/** - * References the specified object. - * - * \param Object A pointer to the object to reference. - * - * \return The object. - */ -PVOID PhReferenceObject( - _In_ PVOID Object - ) -{ - PPH_OBJECT_HEADER objectHeader; - - objectHeader = PhObjectToObjectHeader(Object); - // Increment the reference count. - _InterlockedIncrement(&objectHeader->RefCount); - - return Object; -} - -/** - * References the specified object. - * - * \param Object A pointer to the object to reference. - * \param RefCount The number of references to add. - * - * \return The object. - */ -_May_raise_ PVOID PhReferenceObjectEx( - _In_ PVOID Object, - _In_ LONG RefCount - ) -{ - PPH_OBJECT_HEADER objectHeader; - LONG oldRefCount; - - assert(!(RefCount < 0)); - - objectHeader = PhObjectToObjectHeader(Object); - // Increase the reference count. - oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, RefCount); - - return Object; -} - -/** - * Attempts to reference an object and fails if it is being destroyed. - * - * \param Object The object to reference if it is not being deleted. - * - * \return The object itself if the object was referenced, NULL if it was being deleted and was not - * referenced. - * - * \remarks This function is useful if a reference to an object is held, protected by a mutex, and - * the delete procedure of the object's type attempts to acquire the mutex. If this function is - * called while the mutex is owned, you can avoid referencing an object that is being destroyed. - */ -PVOID PhReferenceObjectSafe( - _In_ PVOID Object - ) -{ - PPH_OBJECT_HEADER objectHeader; - - objectHeader = PhObjectToObjectHeader(Object); - - // Increase the reference count only if it positive already (atomically). - if (PhpInterlockedIncrementSafe(&objectHeader->RefCount)) - return Object; - else - return NULL; -} - -/** - * Dereferences the specified object. - * The object will be freed if its reference count reaches 0. - * - * \param Object A pointer to the object to dereference. - */ -VOID PhDereferenceObject( - _In_ PVOID Object - ) -{ - PPH_OBJECT_HEADER objectHeader; - LONG newRefCount; - - objectHeader = PhObjectToObjectHeader(Object); - // Decrement the reference count. - newRefCount = _InterlockedDecrement(&objectHeader->RefCount); - ASSUME_ASSERT(newRefCount >= 0); - - // Free the object if it has 0 references. - if (newRefCount == 0) - { - PhpFreeObject(objectHeader); - } -} - -/** - * Dereferences the specified object. - * The object will be freed in a worker thread if its reference count reaches 0. - * - * \param Object A pointer to the object to dereference. - */ -VOID PhDereferenceObjectDeferDelete( - _In_ PVOID Object - ) -{ - PhDereferenceObjectEx(Object, 1, TRUE); -} - -/** - * Dereferences the specified object. - * The object will be freed if its reference count reaches 0. - * - * \param Object A pointer to the object to dereference. - * \param RefCount The number of references to remove. - * \param DeferDelete Whether to defer deletion of the object. - */ -_May_raise_ VOID PhDereferenceObjectEx( - _In_ PVOID Object, - _In_ LONG RefCount, - _In_ BOOLEAN DeferDelete - ) -{ - PPH_OBJECT_HEADER objectHeader; - LONG oldRefCount; - LONG newRefCount; - - assert(!(RefCount < 0)); - - objectHeader = PhObjectToObjectHeader(Object); - - // Decrease the reference count. - oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount); - newRefCount = oldRefCount - RefCount; - - // Free the object if it has 0 references. - if (newRefCount == 0) - { - if (DeferDelete) - { - PhpDeferDeleteObject(objectHeader); - } - else - { - // Free the object. - PhpFreeObject(objectHeader); - } - } - else if (newRefCount < 0) - { - PhRaiseStatus(STATUS_INVALID_PARAMETER); - } -} - -/** - * Gets an object's type. - * - * \param Object A pointer to an object. - * - * \return A pointer to a type object. - */ -PPH_OBJECT_TYPE PhGetObjectType( - _In_ PVOID Object - ) -{ - return PhObjectTypeTable[PhObjectToObjectHeader(Object)->TypeIndex]; -} - -/** - * Creates an object type. - * - * \param Name The name of the type. - * \param Flags A combination of flags affecting the behaviour of the object type. - * \param DeleteProcedure A callback function that is executed when an object of this type is about - * to be freed (i.e. when its reference count is 0). - * - * \return A pointer to the newly created object type. - * - * \remarks Do not reference or dereference the object type once it is created. - */ -PPH_OBJECT_TYPE PhCreateObjectType( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure - ) -{ - return PhCreateObjectTypeEx( - Name, - Flags, - DeleteProcedure, - NULL - ); -} - -/** - * Creates an object type. - * - * \param Name The name of the type. - * \param Flags A combination of flags affecting the behaviour of the object type. - * \param DeleteProcedure A callback function that is executed when an object of this type is about - * to be freed (i.e. when its reference count is 0). - * \param Parameters A structure containing additional parameters for the object type. - * - * \return A pointer to the newly created object type. - * - * \remarks Do not reference or dereference the object type once it is created. - */ -PPH_OBJECT_TYPE PhCreateObjectTypeEx( - _In_ PWSTR Name, - _In_ ULONG Flags, - _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure, - _In_opt_ PPH_OBJECT_TYPE_PARAMETERS Parameters - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PPH_OBJECT_TYPE objectType; - - // Check the flags. - if ((Flags & PH_OBJECT_TYPE_VALID_FLAGS) != Flags) /* Valid flag mask */ - PhRaiseStatus(STATUS_INVALID_PARAMETER_3); - if ((Flags & PH_OBJECT_TYPE_USE_FREE_LIST) && !Parameters) - PhRaiseStatus(STATUS_INVALID_PARAMETER_MIX); - - // Create the type object. - objectType = PhCreateObject(sizeof(PH_OBJECT_TYPE), PhObjectTypeObject); - - // Initialize the type object. - objectType->Flags = (USHORT)Flags; - objectType->TypeIndex = (USHORT)_InterlockedIncrement(&PhObjectTypeCount) - 1; - objectType->NumberOfObjects = 0; - objectType->DeleteProcedure = DeleteProcedure; - objectType->Name = Name; - - if (objectType->TypeIndex < PH_OBJECT_TYPE_TABLE_SIZE) - PhObjectTypeTable[objectType->TypeIndex] = objectType; - else - PhRaiseStatus(STATUS_UNSUCCESSFUL); - - if (Parameters) - { - if (Flags & PH_OBJECT_TYPE_USE_FREE_LIST) - { - PhInitializeFreeList( - &objectType->FreeList, - PhAddObjectHeaderSize(Parameters->FreeListSize), - Parameters->FreeListCount - ); - } - } - - return objectType; -} - -/** - * Gets information about an object type. - * - * \param ObjectType A pointer to an object type. - * \param Information A variable which receives information about the object type. - */ -VOID PhGetObjectTypeInformation( - _In_ PPH_OBJECT_TYPE ObjectType, - _Out_ PPH_OBJECT_TYPE_INFORMATION Information - ) -{ - Information->Name = ObjectType->Name; - Information->NumberOfObjects = ObjectType->NumberOfObjects; - Information->Flags = ObjectType->Flags; - Information->TypeIndex = ObjectType->TypeIndex; -} - -/** - * Allocates storage for an object. - * - * \param ObjectType The type of the object. - * \param ObjectSize The size of the object, excluding the header. - */ -PPH_OBJECT_HEADER PhpAllocateObject( - _In_ PPH_OBJECT_TYPE ObjectType, - _In_ SIZE_T ObjectSize - ) -{ - PPH_OBJECT_HEADER objectHeader; - - if (ObjectType->Flags & PH_OBJECT_TYPE_USE_FREE_LIST) - { - assert(ObjectType->FreeList.Size == PhAddObjectHeaderSize(ObjectSize)); - - objectHeader = PhAllocateFromFreeList(&ObjectType->FreeList); - objectHeader->Flags = PH_OBJECT_FROM_TYPE_FREE_LIST; - REF_STAT_UP(RefObjectsAllocatedFromTypeFreeList); - } - else if (ObjectSize <= PH_OBJECT_SMALL_OBJECT_SIZE) - { - objectHeader = PhAllocateFromFreeList(&PhObjectSmallFreeList); - objectHeader->Flags = PH_OBJECT_FROM_SMALL_FREE_LIST; - REF_STAT_UP(RefObjectsAllocatedFromSmallFreeList); - } - else - { - objectHeader = PhAllocate(PhAddObjectHeaderSize(ObjectSize)); - objectHeader->Flags = 0; - REF_STAT_UP(RefObjectsAllocated); - } - - return objectHeader; -} - -/** - * Calls the delete procedure for an object and frees its allocated storage. - * - * \param ObjectHeader A pointer to the object header of an allocated object. - */ -VOID PhpFreeObject( - _In_ PPH_OBJECT_HEADER ObjectHeader - ) -{ - PPH_OBJECT_TYPE objectType; - - objectType = PhObjectTypeTable[ObjectHeader->TypeIndex]; - - // Object type statistics. - _InterlockedDecrement(&objectType->NumberOfObjects); - -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&PhDbgObjectListLock); - RemoveEntryList(&ObjectHeader->ObjectListEntry); - PhReleaseQueuedLockExclusive(&PhDbgObjectListLock); -#endif - - REF_STAT_UP(RefObjectsDestroyed); - - // Call the delete procedure if we have one. - if (objectType->DeleteProcedure) - { - objectType->DeleteProcedure(PhObjectHeaderToObject(ObjectHeader), 0); - } - - if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST) - { - PhFreeToFreeList(&objectType->FreeList, ObjectHeader); - REF_STAT_UP(RefObjectsFreedToTypeFreeList); - } - else if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST) - { - PhFreeToFreeList(&PhObjectSmallFreeList, ObjectHeader); - REF_STAT_UP(RefObjectsFreedToSmallFreeList); - } - else - { - PhFree(ObjectHeader); - REF_STAT_UP(RefObjectsFreed); - } -} - -/** - * Queues an object for deletion. - * - * \param ObjectHeader A pointer to the object header of the object to delete. - */ -VOID PhpDeferDeleteObject( - _In_ PPH_OBJECT_HEADER ObjectHeader - ) -{ - PSLIST_ENTRY oldFirstEntry; - - // Save TypeIndex and Flags since they get overwritten when we push the object onto the defer - // delete list. - ObjectHeader->DeferDelete = 1; - MemoryBarrier(); - ObjectHeader->SavedTypeIndex = ObjectHeader->TypeIndex; - ObjectHeader->SavedFlags = ObjectHeader->Flags; - - oldFirstEntry = RtlFirstEntrySList(&PhObjectDeferDeleteListHead); - RtlInterlockedPushEntrySList(&PhObjectDeferDeleteListHead, &ObjectHeader->DeferDeleteListEntry); - REF_STAT_UP(RefObjectsDeleteDeferred); - - // Was the to-free list empty before? If so, we need to queue a work item. - if (!oldFirstEntry) - { - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhpDeferDeleteObjectRoutine, NULL); - } -} - -/** - * Removes and frees objects from the to-free list. - */ -NTSTATUS PhpDeferDeleteObjectRoutine( - _In_ PVOID Parameter - ) -{ - PSLIST_ENTRY listEntry; - PPH_OBJECT_HEADER objectHeader; - - // Clear the list and obtain the first object to free. - listEntry = RtlInterlockedFlushSList(&PhObjectDeferDeleteListHead); - - while (listEntry) - { - objectHeader = CONTAINING_RECORD(listEntry, PH_OBJECT_HEADER, DeferDeleteListEntry); - listEntry = listEntry->Next; - - // Restore TypeIndex and Flags. - objectHeader->TypeIndex = (USHORT)objectHeader->SavedTypeIndex; - objectHeader->Flags = (UCHAR)objectHeader->SavedFlags; - - PhpFreeObject(objectHeader); - } - - return STATUS_SUCCESS; -} - -/** - * Creates a reference-counted memory block. - * - * \param Size The number of bytes to allocate. - * - * \return A pointer to the memory block. - */ -PVOID PhCreateAlloc( - _In_ SIZE_T Size - ) -{ - return PhCreateObject(Size, PhAllocType); -} - -/** - * Gets the current auto-dereference pool for the current thread. - */ -FORCEINLINE PPH_AUTO_POOL PhpGetCurrentAutoPool( - VOID - ) -{ - return (PPH_AUTO_POOL)TlsGetValue(PhpAutoPoolTlsIndex); -} - -/** - * Sets the current auto-dereference pool for the current thread. - */ -_May_raise_ FORCEINLINE VOID PhpSetCurrentAutoPool( - _In_ PPH_AUTO_POOL AutoPool - ) -{ - if (!TlsSetValue(PhpAutoPoolTlsIndex, AutoPool)) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - -#ifdef DEBUG - { - PPHP_BASE_THREAD_DBG dbg; - - dbg = (PPHP_BASE_THREAD_DBG)TlsGetValue(PhDbgThreadDbgTlsIndex); - - if (dbg) - { - dbg->CurrentAutoPool = AutoPool; - } - } -#endif -} - -/** - * Initializes an auto-dereference pool and sets it as the current pool for the current thread. You - * must call PhDeleteAutoPool() before storage for the auto-dereference pool is freed. - * - * \remarks Always store auto-dereference pools in local variables, and do not share the pool with - * any other functions. - */ -VOID PhInitializeAutoPool( - _Out_ PPH_AUTO_POOL AutoPool - ) -{ - AutoPool->StaticCount = 0; - AutoPool->DynamicCount = 0; - AutoPool->DynamicAllocated = 0; - AutoPool->DynamicObjects = NULL; - - // Add the pool to the stack. - AutoPool->NextPool = PhpGetCurrentAutoPool(); - PhpSetCurrentAutoPool(AutoPool); - - REF_STAT_UP(RefAutoPoolsCreated); -} - -/** - * Deletes an auto-dereference pool. The function will dereference any objects currently in the - * pool. If a pool other than the current pool is passed to the function, an exception is raised. - * - * \param AutoPool The auto-dereference pool to delete. - */ -_May_raise_ VOID PhDeleteAutoPool( - _Inout_ PPH_AUTO_POOL AutoPool - ) -{ - PhDrainAutoPool(AutoPool); - - if (PhpGetCurrentAutoPool() != AutoPool) - PhRaiseStatus(STATUS_UNSUCCESSFUL); - - // Remove the pool from the stack. - PhpSetCurrentAutoPool(AutoPool->NextPool); - - // Free the dynamic array if it hasn't been freed yet. - if (AutoPool->DynamicObjects) - PhFree(AutoPool->DynamicObjects); - - REF_STAT_UP(RefAutoPoolsDestroyed); -} - -/** - * Dereferences and removes all objects in an auto-release pool. - * - * \param AutoPool The auto-release pool to drain. - */ -VOID PhDrainAutoPool( - _In_ PPH_AUTO_POOL AutoPool - ) -{ - ULONG i; - - for (i = 0; i < AutoPool->StaticCount; i++) - PhDereferenceObject(AutoPool->StaticObjects[i]); - - AutoPool->StaticCount = 0; - - if (AutoPool->DynamicObjects) - { - for (i = 0; i < AutoPool->DynamicCount; i++) - { - PhDereferenceObject(AutoPool->DynamicObjects[i]); - } - - AutoPool->DynamicCount = 0; - - if (AutoPool->DynamicAllocated > PH_AUTO_POOL_DYNAMIC_BIG_SIZE) - { - AutoPool->DynamicAllocated = 0; - PhFree(AutoPool->DynamicObjects); - AutoPool->DynamicObjects = NULL; - } - } -} - -/** - * Adds an object to the current auto-dereference pool for the current thread. If the current thread - * does not have an auto-dereference pool, the function raises an exception. - * - * \param Object A pointer to an object. The object will be dereferenced when the current - * auto-dereference pool is drained or freed. - */ -_May_raise_ PVOID PhAutoDereferenceObject( - _In_opt_ PVOID Object - ) -{ - PPH_AUTO_POOL autoPool = PhpGetCurrentAutoPool(); - -#ifdef DEBUG - // If we don't have an auto-dereference pool, we don't want to leak the object (unlike what - // Apple does with NSAutoreleasePool). - if (!autoPool) - PhRaiseStatus(STATUS_UNSUCCESSFUL); -#endif - - if (!Object) - return NULL; - - // See if we can use the static array. - if (autoPool->StaticCount < PH_AUTO_POOL_STATIC_SIZE) - { - autoPool->StaticObjects[autoPool->StaticCount++] = Object; - return Object; - } - - // Use the dynamic array. - - // Allocate the array if we haven't already. - if (!autoPool->DynamicObjects) - { - autoPool->DynamicAllocated = 64; - autoPool->DynamicObjects = PhAllocate( - sizeof(PVOID) * autoPool->DynamicAllocated - ); - REF_STAT_UP(RefAutoPoolsDynamicAllocated); - } - - // See if we need to resize the array. - if (autoPool->DynamicCount == autoPool->DynamicAllocated) - { - autoPool->DynamicAllocated *= 2; - autoPool->DynamicObjects = PhReAllocate( - autoPool->DynamicObjects, - sizeof(PVOID) * autoPool->DynamicAllocated - ); - REF_STAT_UP(RefAutoPoolsDynamicResized); - } - - autoPool->DynamicObjects[autoPool->DynamicCount++] = Object; - - return Object; -} +/* + * Process Hacker - + * object manager + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +PPH_OBJECT_TYPE PhObjectTypeObject = NULL; +SLIST_HEADER PhObjectDeferDeleteListHead; +PH_FREE_LIST PhObjectSmallFreeList; +PPH_OBJECT_TYPE PhAllocType = NULL; + +ULONG PhObjectTypeCount = 0; +PPH_OBJECT_TYPE PhObjectTypeTable[PH_OBJECT_TYPE_TABLE_SIZE]; + +static ULONG PhpAutoPoolTlsIndex; + +#ifdef DEBUG +LIST_ENTRY PhDbgObjectListHead; +PH_QUEUED_LOCK PhDbgObjectListLock = PH_QUEUED_LOCK_INIT; +PPH_CREATE_OBJECT_HOOK PhDbgCreateObjectHook = NULL; +#endif + +#define REF_STAT_UP(Name) PHLIB_INC_STATISTIC(Name) + +/** + * Initializes the object manager module. + */ +NTSTATUS PhRefInitialization( + VOID + ) +{ + PH_OBJECT_TYPE dummyObjectType; + +#ifdef DEBUG + InitializeListHead(&PhDbgObjectListHead); +#endif + + RtlInitializeSListHead(&PhObjectDeferDeleteListHead); + PhInitializeFreeList( + &PhObjectSmallFreeList, + PhAddObjectHeaderSize(PH_OBJECT_SMALL_OBJECT_SIZE), + PH_OBJECT_SMALL_OBJECT_COUNT + ); + + // Create the fundamental object type. + + memset(&dummyObjectType, 0, sizeof(PH_OBJECT_TYPE)); + PhObjectTypeObject = &dummyObjectType; // PhCreateObject expects an object type. + PhObjectTypeTable[0] = &dummyObjectType; // PhCreateObject also expects PhObjectTypeTable[0] to be filled in. + PhObjectTypeObject = PhCreateObjectType(L"Type", 0, NULL); + + // Now that the fundamental object type exists, fix it up. + PhObjectToObjectHeader(PhObjectTypeObject)->TypeIndex = PhObjectTypeObject->TypeIndex; + PhObjectTypeObject->NumberOfObjects = 1; + + // Create the allocated memory object type. + PhAllocType = PhCreateObjectType(L"Alloc", 0, NULL); + + // Reserve a slot for the auto pool. + PhpAutoPoolTlsIndex = TlsAlloc(); + + if (PhpAutoPoolTlsIndex == TLS_OUT_OF_INDEXES) + return STATUS_INSUFFICIENT_RESOURCES; + + return STATUS_SUCCESS; +} + +/** + * Allocates a object. + * + * \param ObjectSize The size of the object. + * \param ObjectType The type of the object. + * + * \return A pointer to the newly allocated object. + */ +_May_raise_ PVOID PhCreateObject( + _In_ SIZE_T ObjectSize, + _In_ PPH_OBJECT_TYPE ObjectType + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PPH_OBJECT_HEADER objectHeader; + + // Allocate storage for the object. Note that this includes the object header followed by the + // object body. + objectHeader = PhpAllocateObject(ObjectType, ObjectSize); + + // Object type statistics. + _InterlockedIncrement((PLONG)&ObjectType->NumberOfObjects); + + // Initialize the object header. + objectHeader->RefCount = 1; + objectHeader->TypeIndex = ObjectType->TypeIndex; + // objectHeader->Flags is set by PhpAllocateObject. + + REF_STAT_UP(RefObjectsCreated); + +#ifdef DEBUG + { + USHORT capturedFrames; + + capturedFrames = RtlCaptureStackBackTrace(1, 16, objectHeader->StackBackTrace, NULL); + memset( + &objectHeader->StackBackTrace[capturedFrames], + 0, + sizeof(objectHeader->StackBackTrace) - capturedFrames * sizeof(PVOID) + ); + } + + PhAcquireQueuedLockExclusive(&PhDbgObjectListLock); + InsertTailList(&PhDbgObjectListHead, &objectHeader->ObjectListEntry); + PhReleaseQueuedLockExclusive(&PhDbgObjectListLock); + + { + PPH_CREATE_OBJECT_HOOK dbgCreateObjectHook; + + dbgCreateObjectHook = PhDbgCreateObjectHook; + + if (dbgCreateObjectHook) + { + dbgCreateObjectHook( + PhObjectHeaderToObject(objectHeader), + ObjectSize, + 0, + ObjectType + ); + } + } +#endif + + return PhObjectHeaderToObject(objectHeader); +} + +/** + * References the specified object. + * + * \param Object A pointer to the object to reference. + * + * \return The object. + */ +PVOID PhReferenceObject( + _In_ PVOID Object + ) +{ + PPH_OBJECT_HEADER objectHeader; + + objectHeader = PhObjectToObjectHeader(Object); + // Increment the reference count. + _InterlockedIncrement(&objectHeader->RefCount); + + return Object; +} + +/** + * References the specified object. + * + * \param Object A pointer to the object to reference. + * \param RefCount The number of references to add. + * + * \return The object. + */ +_May_raise_ PVOID PhReferenceObjectEx( + _In_ PVOID Object, + _In_ LONG RefCount + ) +{ + PPH_OBJECT_HEADER objectHeader; + LONG oldRefCount; + + assert(!(RefCount < 0)); + + objectHeader = PhObjectToObjectHeader(Object); + // Increase the reference count. + oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, RefCount); + + return Object; +} + +/** + * Attempts to reference an object and fails if it is being destroyed. + * + * \param Object The object to reference if it is not being deleted. + * + * \return The object itself if the object was referenced, NULL if it was being deleted and was not + * referenced. + * + * \remarks This function is useful if a reference to an object is held, protected by a mutex, and + * the delete procedure of the object's type attempts to acquire the mutex. If this function is + * called while the mutex is owned, you can avoid referencing an object that is being destroyed. + */ +PVOID PhReferenceObjectSafe( + _In_ PVOID Object + ) +{ + PPH_OBJECT_HEADER objectHeader; + + objectHeader = PhObjectToObjectHeader(Object); + + // Increase the reference count only if it positive already (atomically). + if (PhpInterlockedIncrementSafe(&objectHeader->RefCount)) + return Object; + else + return NULL; +} + +/** + * Dereferences the specified object. + * The object will be freed if its reference count reaches 0. + * + * \param Object A pointer to the object to dereference. + */ +VOID PhDereferenceObject( + _In_ PVOID Object + ) +{ + PPH_OBJECT_HEADER objectHeader; + LONG newRefCount; + + objectHeader = PhObjectToObjectHeader(Object); + // Decrement the reference count. + newRefCount = _InterlockedDecrement(&objectHeader->RefCount); + ASSUME_ASSERT(newRefCount >= 0); + ASSUME_ASSERT(!(newRefCount < 0)); + + // Free the object if it has 0 references. + if (newRefCount == 0) + { + PhpFreeObject(objectHeader); + } +} + +/** + * Dereferences the specified object. + * The object will be freed in a worker thread if its reference count reaches 0. + * + * \param Object A pointer to the object to dereference. + */ +VOID PhDereferenceObjectDeferDelete( + _In_ PVOID Object + ) +{ + PhDereferenceObjectEx(Object, 1, TRUE); +} + +/** + * Dereferences the specified object. + * The object will be freed if its reference count reaches 0. + * + * \param Object A pointer to the object to dereference. + * \param RefCount The number of references to remove. + * \param DeferDelete Whether to defer deletion of the object. + */ +_May_raise_ VOID PhDereferenceObjectEx( + _In_ PVOID Object, + _In_ LONG RefCount, + _In_ BOOLEAN DeferDelete + ) +{ + PPH_OBJECT_HEADER objectHeader; + LONG oldRefCount; + LONG newRefCount; + + assert(!(RefCount < 0)); + + objectHeader = PhObjectToObjectHeader(Object); + + // Decrease the reference count. + oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount); + newRefCount = oldRefCount - RefCount; + + // Free the object if it has 0 references. + if (newRefCount == 0) + { + if (DeferDelete) + { + PhpDeferDeleteObject(objectHeader); + } + else + { + // Free the object. + PhpFreeObject(objectHeader); + } + } + else if (newRefCount < 0) + { + PhRaiseStatus(STATUS_INVALID_PARAMETER); + } +} + +/** + * References an array of objects. + * + * \param Objects An array of objects. + * \param NumberOfObjects The number of elements in \a Objects. + */ +VOID PhReferenceObjects( + _In_reads_(NumberOfObjects) PVOID *Objects, + _In_ ULONG NumberOfObjects + ) +{ + ULONG i; + + for (i = 0; i < NumberOfObjects; i++) + PhReferenceObject(Objects[i]); +} + +/** + * Dereferences an array of objects. + * + * \param Objects An array of objects. + * \param NumberOfObjects The number of elements in \a Objects. + */ +VOID PhDereferenceObjects( + _In_reads_(NumberOfObjects) PVOID *Objects, + _In_ ULONG NumberOfObjects + ) +{ + ULONG i; + + for (i = 0; i < NumberOfObjects; i++) + PhDereferenceObject(Objects[i]); +} + +/** + * Gets an object's type. + * + * \param Object A pointer to an object. + * + * \return A pointer to a type object. + */ +PPH_OBJECT_TYPE PhGetObjectType( + _In_ PVOID Object + ) +{ + return PhObjectTypeTable[PhObjectToObjectHeader(Object)->TypeIndex]; +} + +/** + * Creates an object type. + * + * \param Name The name of the type. + * \param Flags A combination of flags affecting the behaviour of the object type. + * \param DeleteProcedure A callback function that is executed when an object of this type is about + * to be freed (i.e. when its reference count is 0). + * + * \return A pointer to the newly created object type. + * + * \remarks Do not reference or dereference the object type once it is created. + */ +PPH_OBJECT_TYPE PhCreateObjectType( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure + ) +{ + return PhCreateObjectTypeEx( + Name, + Flags, + DeleteProcedure, + NULL + ); +} + +/** + * Creates an object type. + * + * \param Name The name of the type. + * \param Flags A combination of flags affecting the behaviour of the object type. + * \param DeleteProcedure A callback function that is executed when an object of this type is about + * to be freed (i.e. when its reference count is 0). + * \param Parameters A structure containing additional parameters for the object type. + * + * \return A pointer to the newly created object type. + * + * \remarks Do not reference or dereference the object type once it is created. + */ +PPH_OBJECT_TYPE PhCreateObjectTypeEx( + _In_ PWSTR Name, + _In_ ULONG Flags, + _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure, + _In_opt_ PPH_OBJECT_TYPE_PARAMETERS Parameters + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PPH_OBJECT_TYPE objectType; + + // Check the flags. + if ((Flags & PH_OBJECT_TYPE_VALID_FLAGS) != Flags) /* Valid flag mask */ + PhRaiseStatus(STATUS_INVALID_PARAMETER_3); + if ((Flags & PH_OBJECT_TYPE_USE_FREE_LIST) && !Parameters) + PhRaiseStatus(STATUS_INVALID_PARAMETER_MIX); + + // Create the type object. + objectType = PhCreateObject(sizeof(PH_OBJECT_TYPE), PhObjectTypeObject); + + // Initialize the type object. + objectType->Flags = (USHORT)Flags; + objectType->TypeIndex = (USHORT)_InterlockedIncrement(&PhObjectTypeCount) - 1; + objectType->NumberOfObjects = 0; + objectType->DeleteProcedure = DeleteProcedure; + objectType->Name = Name; + + if (objectType->TypeIndex < PH_OBJECT_TYPE_TABLE_SIZE) + PhObjectTypeTable[objectType->TypeIndex] = objectType; + else + PhRaiseStatus(STATUS_UNSUCCESSFUL); + + if (Parameters) + { + if (Flags & PH_OBJECT_TYPE_USE_FREE_LIST) + { + PhInitializeFreeList( + &objectType->FreeList, + PhAddObjectHeaderSize(Parameters->FreeListSize), + Parameters->FreeListCount + ); + } + } + + return objectType; +} + +/** + * Gets information about an object type. + * + * \param ObjectType A pointer to an object type. + * \param Information A variable which receives information about the object type. + */ +VOID PhGetObjectTypeInformation( + _In_ PPH_OBJECT_TYPE ObjectType, + _Out_ PPH_OBJECT_TYPE_INFORMATION Information + ) +{ + Information->Name = ObjectType->Name; + Information->NumberOfObjects = ObjectType->NumberOfObjects; + Information->Flags = ObjectType->Flags; + Information->TypeIndex = ObjectType->TypeIndex; +} + +/** + * Allocates storage for an object. + * + * \param ObjectType The type of the object. + * \param ObjectSize The size of the object, excluding the header. + */ +PPH_OBJECT_HEADER PhpAllocateObject( + _In_ PPH_OBJECT_TYPE ObjectType, + _In_ SIZE_T ObjectSize + ) +{ + PPH_OBJECT_HEADER objectHeader; + + if (ObjectType->Flags & PH_OBJECT_TYPE_USE_FREE_LIST) + { + assert(ObjectType->FreeList.Size == PhAddObjectHeaderSize(ObjectSize)); + + objectHeader = PhAllocateFromFreeList(&ObjectType->FreeList); + objectHeader->Flags = PH_OBJECT_FROM_TYPE_FREE_LIST; + REF_STAT_UP(RefObjectsAllocatedFromTypeFreeList); + } + else if (ObjectSize <= PH_OBJECT_SMALL_OBJECT_SIZE) + { + objectHeader = PhAllocateFromFreeList(&PhObjectSmallFreeList); + objectHeader->Flags = PH_OBJECT_FROM_SMALL_FREE_LIST; + REF_STAT_UP(RefObjectsAllocatedFromSmallFreeList); + } + else + { + objectHeader = PhAllocate(PhAddObjectHeaderSize(ObjectSize)); + objectHeader->Flags = 0; + REF_STAT_UP(RefObjectsAllocated); + } + + return objectHeader; +} + +/** + * Calls the delete procedure for an object and frees its allocated storage. + * + * \param ObjectHeader A pointer to the object header of an allocated object. + */ +VOID PhpFreeObject( + _In_ PPH_OBJECT_HEADER ObjectHeader + ) +{ + PPH_OBJECT_TYPE objectType; + + objectType = PhObjectTypeTable[ObjectHeader->TypeIndex]; + + // Object type statistics. + _InterlockedDecrement(&objectType->NumberOfObjects); + +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&PhDbgObjectListLock); + RemoveEntryList(&ObjectHeader->ObjectListEntry); + PhReleaseQueuedLockExclusive(&PhDbgObjectListLock); +#endif + + REF_STAT_UP(RefObjectsDestroyed); + + // Call the delete procedure if we have one. + if (objectType->DeleteProcedure) + { + objectType->DeleteProcedure(PhObjectHeaderToObject(ObjectHeader), 0); + } + + if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST) + { + PhFreeToFreeList(&objectType->FreeList, ObjectHeader); + REF_STAT_UP(RefObjectsFreedToTypeFreeList); + } + else if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST) + { + PhFreeToFreeList(&PhObjectSmallFreeList, ObjectHeader); + REF_STAT_UP(RefObjectsFreedToSmallFreeList); + } + else + { + PhFree(ObjectHeader); + REF_STAT_UP(RefObjectsFreed); + } +} + +/** + * Queues an object for deletion. + * + * \param ObjectHeader A pointer to the object header of the object to delete. + */ +VOID PhpDeferDeleteObject( + _In_ PPH_OBJECT_HEADER ObjectHeader + ) +{ + PSLIST_ENTRY oldFirstEntry; + + // Save TypeIndex and Flags since they get overwritten when we push the object onto the defer + // delete list. + ObjectHeader->DeferDelete = 1; + MemoryBarrier(); + ObjectHeader->SavedTypeIndex = ObjectHeader->TypeIndex; + ObjectHeader->SavedFlags = ObjectHeader->Flags; + + oldFirstEntry = RtlFirstEntrySList(&PhObjectDeferDeleteListHead); + RtlInterlockedPushEntrySList(&PhObjectDeferDeleteListHead, &ObjectHeader->DeferDeleteListEntry); + REF_STAT_UP(RefObjectsDeleteDeferred); + + // Was the to-free list empty before? If so, we need to queue a work item. + if (!oldFirstEntry) + { + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhpDeferDeleteObjectRoutine, NULL); + } +} + +/** + * Removes and frees objects from the to-free list. + */ +NTSTATUS PhpDeferDeleteObjectRoutine( + _In_ PVOID Parameter + ) +{ + PSLIST_ENTRY listEntry; + PPH_OBJECT_HEADER objectHeader; + + // Clear the list and obtain the first object to free. + listEntry = RtlInterlockedFlushSList(&PhObjectDeferDeleteListHead); + + while (listEntry) + { + objectHeader = CONTAINING_RECORD(listEntry, PH_OBJECT_HEADER, DeferDeleteListEntry); + listEntry = listEntry->Next; + + // Restore TypeIndex and Flags. + objectHeader->TypeIndex = (USHORT)objectHeader->SavedTypeIndex; + objectHeader->Flags = (UCHAR)objectHeader->SavedFlags; + + PhpFreeObject(objectHeader); + } + + return STATUS_SUCCESS; +} + +/** + * Creates a reference-counted memory block. + * + * \param Size The number of bytes to allocate. + * + * \return A pointer to the memory block. + */ +PVOID PhCreateAlloc( + _In_ SIZE_T Size + ) +{ + return PhCreateObject(Size, PhAllocType); +} + +/** + * Gets the current auto-dereference pool for the current thread. + */ +FORCEINLINE PPH_AUTO_POOL PhpGetCurrentAutoPool( + VOID + ) +{ + return (PPH_AUTO_POOL)TlsGetValue(PhpAutoPoolTlsIndex); +} + +/** + * Sets the current auto-dereference pool for the current thread. + */ +_May_raise_ FORCEINLINE VOID PhpSetCurrentAutoPool( + _In_ PPH_AUTO_POOL AutoPool + ) +{ + if (!TlsSetValue(PhpAutoPoolTlsIndex, AutoPool)) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + +#ifdef DEBUG + { + PPHP_BASE_THREAD_DBG dbg; + + dbg = (PPHP_BASE_THREAD_DBG)TlsGetValue(PhDbgThreadDbgTlsIndex); + + if (dbg) + { + dbg->CurrentAutoPool = AutoPool; + } + } +#endif +} + +/** + * Initializes an auto-dereference pool and sets it as the current pool for the current thread. You + * must call PhDeleteAutoPool() before storage for the auto-dereference pool is freed. + * + * \remarks Always store auto-dereference pools in local variables, and do not share the pool with + * any other functions. + */ +VOID PhInitializeAutoPool( + _Out_ PPH_AUTO_POOL AutoPool + ) +{ + AutoPool->StaticCount = 0; + AutoPool->DynamicCount = 0; + AutoPool->DynamicAllocated = 0; + AutoPool->DynamicObjects = NULL; + + // Add the pool to the stack. + AutoPool->NextPool = PhpGetCurrentAutoPool(); + PhpSetCurrentAutoPool(AutoPool); + + REF_STAT_UP(RefAutoPoolsCreated); +} + +/** + * Deletes an auto-dereference pool. The function will dereference any objects currently in the + * pool. If a pool other than the current pool is passed to the function, an exception is raised. + * + * \param AutoPool The auto-dereference pool to delete. + */ +_May_raise_ VOID PhDeleteAutoPool( + _Inout_ PPH_AUTO_POOL AutoPool + ) +{ + PhDrainAutoPool(AutoPool); + + if (PhpGetCurrentAutoPool() != AutoPool) + PhRaiseStatus(STATUS_UNSUCCESSFUL); + + // Remove the pool from the stack. + PhpSetCurrentAutoPool(AutoPool->NextPool); + + // Free the dynamic array if it hasn't been freed yet. + if (AutoPool->DynamicObjects) + PhFree(AutoPool->DynamicObjects); + + REF_STAT_UP(RefAutoPoolsDestroyed); +} + +/** + * Dereferences and removes all objects in an auto-release pool. + * + * \param AutoPool The auto-release pool to drain. + */ +VOID PhDrainAutoPool( + _In_ PPH_AUTO_POOL AutoPool + ) +{ + ULONG i; + + for (i = 0; i < AutoPool->StaticCount; i++) + PhDereferenceObject(AutoPool->StaticObjects[i]); + + AutoPool->StaticCount = 0; + + if (AutoPool->DynamicObjects) + { + for (i = 0; i < AutoPool->DynamicCount; i++) + { + PhDereferenceObject(AutoPool->DynamicObjects[i]); + } + + AutoPool->DynamicCount = 0; + + if (AutoPool->DynamicAllocated > PH_AUTO_POOL_DYNAMIC_BIG_SIZE) + { + AutoPool->DynamicAllocated = 0; + PhFree(AutoPool->DynamicObjects); + AutoPool->DynamicObjects = NULL; + } + } +} + +/** + * Adds an object to the current auto-dereference pool for the current thread. If the current thread + * does not have an auto-dereference pool, the function raises an exception. + * + * \param Object A pointer to an object. The object will be dereferenced when the current + * auto-dereference pool is drained or freed. + */ +_May_raise_ PVOID PhAutoDereferenceObject( + _In_opt_ PVOID Object + ) +{ + PPH_AUTO_POOL autoPool = PhpGetCurrentAutoPool(); + +#ifdef DEBUG + // If we don't have an auto-dereference pool, we don't want to leak the object (unlike what + // Apple does with NSAutoreleasePool). + if (!autoPool) + PhRaiseStatus(STATUS_UNSUCCESSFUL); +#endif + + if (!Object) + return NULL; + + // See if we can use the static array. + if (autoPool->StaticCount < PH_AUTO_POOL_STATIC_SIZE) + { + autoPool->StaticObjects[autoPool->StaticCount++] = Object; + return Object; + } + + // Use the dynamic array. + + // Allocate the array if we haven't already. + if (!autoPool->DynamicObjects) + { + autoPool->DynamicAllocated = 64; + autoPool->DynamicObjects = PhAllocate( + sizeof(PVOID) * autoPool->DynamicAllocated + ); + REF_STAT_UP(RefAutoPoolsDynamicAllocated); + } + + // See if we need to resize the array. + if (autoPool->DynamicCount == autoPool->DynamicAllocated) + { + autoPool->DynamicAllocated *= 2; + autoPool->DynamicObjects = PhReAllocate( + autoPool->DynamicObjects, + sizeof(PVOID) * autoPool->DynamicAllocated + ); + REF_STAT_UP(RefAutoPoolsDynamicResized); + } + + autoPool->DynamicObjects[autoPool->DynamicCount++] = Object; + + return Object; +} diff --git a/phlib/secdata.c b/phlib/secdata.c index 43b9e2c72b82..4aec01b09e65 100644 --- a/phlib/secdata.c +++ b/phlib/secdata.c @@ -1,788 +1,810 @@ -/* - * Process Hacker - - * object security data - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include - -#define ACCESS_ENTRIES(Type) static PH_ACCESS_ENTRY Ph##Type##AccessEntries[] = -#define ACCESS_ENTRY(Type, HasSynchronize) \ - { L#Type, Ph##Type##AccessEntries, sizeof(Ph##Type##AccessEntries), HasSynchronize } - -typedef struct _PH_SPECIFIC_TYPE -{ - PWSTR Type; - PPH_ACCESS_ENTRY AccessEntries; - ULONG SizeOfAccessEntries; - BOOLEAN HasSynchronize; -} PH_SPECIFIC_TYPE, *PPH_SPECIFIC_TYPE; - -ACCESS_ENTRIES(Standard) -{ - { L"Synchronize", SYNCHRONIZE, FALSE, TRUE }, - { L"Delete", DELETE, FALSE, TRUE }, - { L"Read permissions", READ_CONTROL, FALSE, TRUE, L"Read control" }, - { L"Change permissions", WRITE_DAC, FALSE, TRUE, L"Write DAC" }, - { L"Take ownership", WRITE_OWNER, FALSE, TRUE, L"Write owner" } -}; - -ACCESS_ENTRIES(AlpcPort) -{ - { L"Full control", PORT_ALL_ACCESS, TRUE, TRUE }, - { L"Connect", PORT_CONNECT, TRUE, TRUE } -}; - -ACCESS_ENTRIES(DebugObject) -{ - { L"Full control", DEBUG_ALL_ACCESS, TRUE, TRUE }, - { L"Read events", DEBUG_READ_EVENT, TRUE, TRUE }, - { L"Assign processes", DEBUG_PROCESS_ASSIGN, TRUE, TRUE }, - { L"Query information", DEBUG_QUERY_INFORMATION, TRUE, TRUE }, - { L"Set information", DEBUG_SET_INFORMATION, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Desktop) -{ - { L"Full control", DESKTOP_ALL_ACCESS, TRUE, TRUE }, - { L"Read", DESKTOP_GENERIC_READ, TRUE, FALSE }, - { L"Write", DESKTOP_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", DESKTOP_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Enumerate", DESKTOP_ENUMERATE, FALSE, TRUE }, - { L"Read objects", DESKTOP_READOBJECTS, FALSE, TRUE }, - { L"Playback journals", DESKTOP_JOURNALPLAYBACK, FALSE, TRUE }, - { L"Write objects", DESKTOP_WRITEOBJECTS, FALSE, TRUE }, - { L"Create windows", DESKTOP_CREATEWINDOW, FALSE, TRUE }, - { L"Create menus", DESKTOP_CREATEMENU, FALSE, TRUE }, - { L"Create window hooks", DESKTOP_HOOKCONTROL, FALSE, TRUE }, - { L"Record journals", DESKTOP_JOURNALRECORD, FALSE, TRUE }, - { L"Switch desktop", DESKTOP_SWITCHDESKTOP, FALSE, TRUE } -}; - -ACCESS_ENTRIES(Directory) -{ - { L"Full control", DIRECTORY_ALL_ACCESS, TRUE, TRUE }, - { L"Query", DIRECTORY_QUERY, TRUE, TRUE}, - { L"Traverse", DIRECTORY_TRAVERSE, TRUE, TRUE}, - { L"Create objects", DIRECTORY_CREATE_OBJECT, TRUE, TRUE}, - { L"Create subdirectories", DIRECTORY_CREATE_SUBDIRECTORY, TRUE, TRUE} -}; - -ACCESS_ENTRIES(Event) -{ - { L"Full control", EVENT_ALL_ACCESS, TRUE, TRUE }, - { L"Query", EVENT_QUERY_STATE, TRUE, TRUE }, - { L"Modify", EVENT_MODIFY_STATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(EventPair) -{ - { L"Full control", EVENT_PAIR_ALL_ACCESS, TRUE, TRUE } -}; - -ACCESS_ENTRIES(File) -{ - { L"Full control", FILE_ALL_ACCESS, TRUE, TRUE }, - { L"Read & execute", FILE_GENERIC_READ | FILE_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Read", FILE_GENERIC_READ, TRUE, FALSE }, - { L"Write", FILE_GENERIC_WRITE, TRUE, FALSE }, - { L"Traverse folder / execute file", FILE_EXECUTE, FALSE, TRUE, L"Execute" }, - { L"List folder / read data", FILE_READ_DATA, FALSE, TRUE, L"Read data" }, - { L"Read attributes", FILE_READ_ATTRIBUTES, FALSE, TRUE }, - { L"Read extended attributes", FILE_READ_EA, FALSE, TRUE, L"Read EA" }, - { L"Create files / write data", FILE_WRITE_DATA, FALSE, TRUE, L"Write data" }, - { L"Create folders / append data", FILE_APPEND_DATA, FALSE, TRUE, L"Append data" }, - { L"Write attributes", FILE_WRITE_ATTRIBUTES, FALSE, TRUE }, - { L"Write extended attributes", FILE_WRITE_EA, FALSE, TRUE, L"Write EA" }, - { L"Delete subfolders and files", FILE_DELETE_CHILD, FALSE, TRUE, L"Delete child" } -}; - -ACCESS_ENTRIES(FilterConnectionPort) -{ - { L"Full control", FLT_PORT_ALL_ACCESS, TRUE, TRUE }, - { L"Connect", FLT_PORT_CONNECT, TRUE, TRUE } -}; - -ACCESS_ENTRIES(IoCompletion) -{ - { L"Full control", IO_COMPLETION_ALL_ACCESS, TRUE, TRUE }, - { L"Query", IO_COMPLETION_QUERY_STATE, TRUE, TRUE }, - { L"Modify", IO_COMPLETION_MODIFY_STATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Job) -{ - { L"Full control", JOB_OBJECT_ALL_ACCESS, TRUE, TRUE }, - { L"Query", JOB_OBJECT_QUERY, TRUE, TRUE }, - { L"Assign processes", JOB_OBJECT_ASSIGN_PROCESS, TRUE, TRUE }, - { L"Set attributes", JOB_OBJECT_SET_ATTRIBUTES, TRUE, TRUE }, - { L"Set security attributes", JOB_OBJECT_SET_SECURITY_ATTRIBUTES, TRUE, TRUE }, - { L"Terminate", JOB_OBJECT_TERMINATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Key) -{ - { L"Full control", KEY_ALL_ACCESS, TRUE, TRUE }, - { L"Read", KEY_READ, TRUE, FALSE }, - { L"Write", KEY_WRITE, TRUE, FALSE }, - { L"Execute", KEY_EXECUTE, TRUE, FALSE }, - { L"Enumerate subkeys", KEY_ENUMERATE_SUB_KEYS, FALSE, TRUE }, - { L"Query values", KEY_QUERY_VALUE, FALSE, TRUE }, - { L"Notify", KEY_NOTIFY, FALSE, TRUE }, - { L"Set values", KEY_SET_VALUE, FALSE, TRUE }, - { L"Create subkeys", KEY_CREATE_SUB_KEY, FALSE, TRUE }, - { L"Create links", KEY_CREATE_LINK, FALSE, TRUE } -}; - -ACCESS_ENTRIES(KeyedEvent) -{ - { L"Full control", KEYEDEVENT_ALL_ACCESS, TRUE, TRUE }, - { L"Wait", KEYEDEVENT_WAIT, TRUE, TRUE }, - { L"Wake", KEYEDEVENT_WAKE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(LsaAccount) -{ - { L"Full control", ACCOUNT_ALL_ACCESS, TRUE, TRUE }, - { L"Read", ACCOUNT_READ, TRUE, FALSE }, - { L"Write", ACCOUNT_WRITE, TRUE, FALSE }, - { L"Execute", ACCOUNT_EXECUTE, TRUE, FALSE }, - { L"View", ACCOUNT_VIEW, FALSE, TRUE }, - { L"Adjust privileges", ACCOUNT_ADJUST_PRIVILEGES, FALSE, TRUE }, - { L"Adjust quotas", ACCOUNT_ADJUST_QUOTAS, FALSE, TRUE }, - { L"Adjust system access", ACCOUNT_ADJUST_SYSTEM_ACCESS, FALSE, TRUE } -}; - -ACCESS_ENTRIES(LsaPolicy) -{ - { L"Full control", POLICY_ALL_ACCESS | POLICY_NOTIFICATION, TRUE, TRUE }, - { L"Read", POLICY_READ, TRUE, FALSE }, - { L"Write", POLICY_WRITE, TRUE, FALSE }, - { L"Execute", POLICY_EXECUTE | POLICY_NOTIFICATION, TRUE, FALSE }, - { L"View local information", POLICY_VIEW_LOCAL_INFORMATION, FALSE, TRUE }, - { L"View audit information", POLICY_VIEW_AUDIT_INFORMATION, FALSE, TRUE }, - { L"Get private information", POLICY_GET_PRIVATE_INFORMATION, FALSE, TRUE }, - { L"Administer trust", POLICY_TRUST_ADMIN, FALSE, TRUE }, - { L"Create account", POLICY_CREATE_ACCOUNT, FALSE, TRUE }, - { L"Create secret", POLICY_CREATE_SECRET, FALSE, TRUE }, - { L"Create privilege", POLICY_CREATE_PRIVILEGE, FALSE, TRUE }, - { L"Set default quota limits", POLICY_SET_DEFAULT_QUOTA_LIMITS, FALSE, TRUE }, - { L"Set audit requirements", POLICY_SET_AUDIT_REQUIREMENTS, FALSE, TRUE }, - { L"Administer audit log", POLICY_AUDIT_LOG_ADMIN, FALSE, TRUE }, - { L"Administer server", POLICY_SERVER_ADMIN, FALSE, TRUE }, - { L"Lookup names", POLICY_LOOKUP_NAMES, FALSE, TRUE }, - { L"Get notifications", POLICY_NOTIFICATION, FALSE, TRUE } -}; - -ACCESS_ENTRIES(LsaSecret) -{ - { L"Full control", SECRET_ALL_ACCESS, TRUE, TRUE }, - { L"Read", SECRET_READ, TRUE, FALSE }, - { L"Write", SECRET_WRITE, TRUE, FALSE }, - { L"Execute", SECRET_EXECUTE, TRUE, FALSE }, - { L"Set value", SECRET_SET_VALUE, FALSE, TRUE }, - { L"Query value", SECRET_QUERY_VALUE, FALSE, TRUE } -}; - -ACCESS_ENTRIES(LsaTrusted) -{ - { L"Full control", TRUSTED_ALL_ACCESS, TRUE, TRUE }, - { L"Read", TRUSTED_READ, TRUE, FALSE }, - { L"Write", TRUSTED_WRITE, TRUE, FALSE }, - { L"Execute", TRUSTED_EXECUTE, TRUE, FALSE }, - { L"Query domain name", TRUSTED_QUERY_DOMAIN_NAME, FALSE, TRUE }, - { L"Query controllers", TRUSTED_QUERY_CONTROLLERS, FALSE, TRUE }, - { L"Set controllers", TRUSTED_SET_CONTROLLERS, FALSE, TRUE }, - { L"Query POSIX", TRUSTED_QUERY_POSIX, FALSE, TRUE }, - { L"Set POSIX", TRUSTED_SET_POSIX, FALSE, TRUE }, - { L"Query authentication", TRUSTED_QUERY_AUTH, FALSE, TRUE }, - { L"Set authentication", TRUSTED_SET_AUTH, FALSE, TRUE } -}; - -ACCESS_ENTRIES(Mutant) -{ - { L"Full control", MUTANT_ALL_ACCESS, TRUE, TRUE }, - { L"Query", MUTANT_QUERY_STATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Partition) -{ - { L"Full control", MEMORY_PARTITION_ALL_ACCESS, TRUE, TRUE }, - { L"Query", MEMORY_PARTITION_QUERY_ACCESS, TRUE, TRUE }, - { L"Modify", MEMORY_PARTITION_MODIFY_ACCESS, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Process) -{ - { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff, TRUE, TRUE }, - { L"Query information", PROCESS_QUERY_INFORMATION, TRUE, TRUE }, - { L"Set information", PROCESS_SET_INFORMATION, TRUE, TRUE }, - { L"Set quotas", PROCESS_SET_QUOTA, TRUE, TRUE }, - { L"Set session ID", PROCESS_SET_SESSIONID, TRUE, TRUE }, - { L"Create threads", PROCESS_CREATE_THREAD, TRUE, TRUE }, - { L"Create processes", PROCESS_CREATE_PROCESS, TRUE, TRUE }, - { L"Modify memory", PROCESS_VM_OPERATION, TRUE, TRUE, L"VM operation" }, - { L"Read memory", PROCESS_VM_READ, TRUE, TRUE, L"VM read" }, - { L"Write memory", PROCESS_VM_WRITE, TRUE, TRUE, L"VM write" }, - { L"Duplicate handles", PROCESS_DUP_HANDLE, TRUE, TRUE }, - { L"Suspend / resume / set port", PROCESS_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, - { L"Terminate", PROCESS_TERMINATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Process60) -{ - { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff, TRUE, TRUE }, // PROCESS_ALL_ACCESS - { L"Query limited information", PROCESS_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, - { L"Query information", PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, - { L"Set information", PROCESS_SET_INFORMATION, TRUE, TRUE }, - { L"Set quotas", PROCESS_SET_QUOTA, TRUE, TRUE }, - { L"Set session ID", PROCESS_SET_SESSIONID, TRUE, TRUE }, - { L"Create threads", PROCESS_CREATE_THREAD, TRUE, TRUE }, - { L"Create processes", PROCESS_CREATE_PROCESS, TRUE, TRUE }, - { L"Modify memory", PROCESS_VM_OPERATION, TRUE, TRUE, L"VM operation" }, - { L"Read memory", PROCESS_VM_READ, TRUE, TRUE, L"VM read" }, - { L"Write memory", PROCESS_VM_WRITE, TRUE, TRUE, L"VM write" }, - { L"Duplicate handles", PROCESS_DUP_HANDLE, TRUE, TRUE }, - { L"Suspend / resume / set port", PROCESS_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, - { L"Terminate", PROCESS_TERMINATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Profile) -{ - { L"Full control", PROFILE_ALL_ACCESS, TRUE, TRUE }, - { L"Control", PROFILE_CONTROL, TRUE, TRUE } -}; - -ACCESS_ENTRIES(SamAlias) -{ - { L"Full control", ALIAS_ALL_ACCESS, TRUE, TRUE }, - { L"Read", ALIAS_READ, TRUE, FALSE }, - { L"Write", ALIAS_WRITE, TRUE, FALSE }, - { L"Execute", ALIAS_EXECUTE, TRUE, FALSE }, - { L"Read information", ALIAS_READ_INFORMATION, FALSE, TRUE }, - { L"Write account", ALIAS_WRITE_ACCOUNT, FALSE, TRUE }, - { L"Add member", ALIAS_ADD_MEMBER, FALSE, TRUE }, - { L"Remove member", ALIAS_REMOVE_MEMBER, FALSE, TRUE }, - { L"List members", ALIAS_LIST_MEMBERS, FALSE, TRUE } -}; - -ACCESS_ENTRIES(SamDomain) -{ - { L"Full control", DOMAIN_ALL_ACCESS, TRUE, TRUE }, - { L"Read", DOMAIN_READ, TRUE, FALSE }, - { L"Write", DOMAIN_WRITE, TRUE, FALSE }, - { L"Execute", DOMAIN_EXECUTE, TRUE, FALSE }, - { L"Read password parameters", DOMAIN_READ_PASSWORD_PARAMETERS, FALSE, TRUE }, - { L"Write password parameters", DOMAIN_WRITE_PASSWORD_PARAMS, FALSE, TRUE }, - { L"Read other parameters", DOMAIN_READ_OTHER_PARAMETERS, FALSE, TRUE }, - { L"Write other parameters", DOMAIN_WRITE_OTHER_PARAMETERS, FALSE, TRUE }, - { L"Create user", DOMAIN_CREATE_USER, FALSE, TRUE }, - { L"Create group", DOMAIN_CREATE_GROUP, FALSE, TRUE }, - { L"Create alias", DOMAIN_CREATE_ALIAS, FALSE, TRUE }, - { L"Get alias membership", DOMAIN_GET_ALIAS_MEMBERSHIP, FALSE, TRUE }, - { L"List accounts", DOMAIN_LIST_ACCOUNTS, FALSE, TRUE }, - { L"Lookup", DOMAIN_LOOKUP, FALSE, TRUE }, - { L"Administer server", DOMAIN_ADMINISTER_SERVER, FALSE, TRUE } -}; - -ACCESS_ENTRIES(SamGroup) -{ - { L"Full control", GROUP_ALL_ACCESS, TRUE, TRUE }, - { L"Read", GROUP_READ, TRUE, FALSE }, - { L"Write", GROUP_WRITE, TRUE, FALSE }, - { L"Execute", GROUP_EXECUTE, TRUE, FALSE }, - { L"Read information", GROUP_READ_INFORMATION, FALSE, TRUE }, - { L"Write account", GROUP_WRITE_ACCOUNT, FALSE, TRUE }, - { L"Add member", GROUP_ADD_MEMBER, FALSE, TRUE }, - { L"Remove member", GROUP_REMOVE_MEMBER, FALSE, TRUE }, - { L"List members", GROUP_LIST_MEMBERS, FALSE, TRUE } -}; - -ACCESS_ENTRIES(SamServer) -{ - { L"Full control", SAM_SERVER_ALL_ACCESS, TRUE, TRUE }, - { L"Read", SAM_SERVER_READ, TRUE, FALSE }, - { L"Write", SAM_SERVER_WRITE, TRUE, FALSE }, - { L"Execute", SAM_SERVER_EXECUTE, TRUE, FALSE }, - { L"Connect", SAM_SERVER_CONNECT, FALSE, TRUE }, - { L"Shutdown", SAM_SERVER_SHUTDOWN, FALSE, TRUE }, - { L"Initialize", SAM_SERVER_INITIALIZE, FALSE, TRUE }, - { L"Create domain", SAM_SERVER_CREATE_DOMAIN, FALSE, TRUE }, - { L"Enumerate domains", SAM_SERVER_ENUMERATE_DOMAINS, FALSE, TRUE }, - { L"Lookup domain", SAM_SERVER_LOOKUP_DOMAIN, FALSE, TRUE } -}; - -ACCESS_ENTRIES(SamUser) -{ - { L"Full control", USER_ALL_ACCESS, TRUE, TRUE }, - { L"Read", USER_READ, TRUE, FALSE }, - { L"Write", USER_WRITE, TRUE, FALSE }, - { L"Execute", USER_EXECUTE, TRUE, FALSE }, - { L"Read general", USER_READ_GENERAL, FALSE, TRUE }, - { L"Read preferences", USER_READ_PREFERENCES, FALSE, TRUE }, - { L"Write preferences", USER_WRITE_PREFERENCES, FALSE, TRUE }, - { L"Read logon", USER_READ_LOGON, FALSE, TRUE }, - { L"Read account", USER_READ_ACCOUNT, FALSE, TRUE }, - { L"Write account", USER_WRITE_ACCOUNT, FALSE, TRUE }, - { L"Change password", USER_CHANGE_PASSWORD, FALSE, TRUE }, - { L"Force password change", USER_FORCE_PASSWORD_CHANGE, FALSE, TRUE }, - { L"List groups", USER_LIST_GROUPS, FALSE, TRUE }, - { L"Read group information", USER_READ_GROUP_INFORMATION, FALSE, TRUE }, - { L"Write group information", USER_WRITE_GROUP_INFORMATION, FALSE, TRUE } -}; - -ACCESS_ENTRIES(Section) -{ - { L"Full control", SECTION_ALL_ACCESS, TRUE, TRUE }, - { L"Query", SECTION_QUERY, TRUE, TRUE }, - { L"Map for read", SECTION_MAP_READ, TRUE, TRUE, L"Map read" }, - { L"Map for write", SECTION_MAP_WRITE, TRUE, TRUE, L"Map write" }, - { L"Map for execute", SECTION_MAP_EXECUTE, TRUE, TRUE, L"Map execute" }, - { L"Map for execute (explicit)", SECTION_MAP_EXECUTE_EXPLICIT, TRUE, TRUE, L"Map execute explicit" }, - { L"Extend size", SECTION_EXTEND_SIZE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Semaphore) -{ - { L"Full control", SEMAPHORE_ALL_ACCESS, TRUE, TRUE }, - { L"Query", SEMAPHORE_QUERY_STATE, TRUE, TRUE }, - { L"Modify", SEMAPHORE_MODIFY_STATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Service) -{ - { L"Full control", SERVICE_ALL_ACCESS, TRUE, TRUE }, - { L"Query status", SERVICE_QUERY_STATUS, TRUE, TRUE }, - { L"Query configuration", SERVICE_QUERY_CONFIG, TRUE, TRUE }, - { L"Modify configuration", SERVICE_CHANGE_CONFIG, TRUE, TRUE }, - { L"Enumerate dependents", SERVICE_ENUMERATE_DEPENDENTS, TRUE, TRUE }, - { L"Start", SERVICE_START, TRUE, TRUE }, - { L"Stop", SERVICE_STOP, TRUE, TRUE }, - { L"Pause / continue", SERVICE_PAUSE_CONTINUE, TRUE, TRUE, L"Pause/continue" }, - { L"Interrogate", SERVICE_INTERROGATE, TRUE, TRUE }, - { L"User-defined control", SERVICE_USER_DEFINED_CONTROL, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Session) -{ - { L"Full control", SESSION_ALL_ACCESS, TRUE, TRUE }, - { L"Query", SESSION_QUERY_ACCESS, TRUE, TRUE }, - { L"Modify", SESSION_MODIFY_ACCESS, TRUE, TRUE } -}; - -ACCESS_ENTRIES(SymbolicLink) -{ - { L"Full control", SYMBOLIC_LINK_ALL_ACCESS, TRUE, TRUE }, - { L"Query", SYMBOLIC_LINK_QUERY, TRUE, TRUE } -}; - -ACCESS_ENTRIES(Thread) -{ - { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff, TRUE, TRUE }, - { L"Query information", THREAD_QUERY_INFORMATION, TRUE, TRUE }, - { L"Set information", THREAD_SET_INFORMATION, TRUE, TRUE }, - { L"Get context", THREAD_GET_CONTEXT, TRUE, TRUE }, - { L"Set context", THREAD_SET_CONTEXT, TRUE, TRUE }, - { L"Set token", THREAD_SET_THREAD_TOKEN, TRUE, TRUE }, - { L"Alert", THREAD_ALERT, TRUE, TRUE }, - { L"Impersonate", THREAD_IMPERSONATE, TRUE, TRUE }, - { L"Direct impersonate", THREAD_DIRECT_IMPERSONATION, TRUE, TRUE }, - { L"Suspend / resume", THREAD_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, - { L"Terminate", THREAD_TERMINATE, TRUE, TRUE }, -}; - -ACCESS_ENTRIES(Thread60) -{ - { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff, TRUE, TRUE }, // THREAD_ALL_ACCESS - { L"Query limited information", THREAD_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, - { L"Query information", THREAD_QUERY_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, - { L"Set limited information", THREAD_SET_LIMITED_INFORMATION, TRUE, TRUE }, - { L"Set information", THREAD_SET_INFORMATION | THREAD_SET_LIMITED_INFORMATION, TRUE, TRUE }, - { L"Get context", THREAD_GET_CONTEXT, TRUE, TRUE }, - { L"Set context", THREAD_SET_CONTEXT, TRUE, TRUE }, - { L"Set token", THREAD_SET_THREAD_TOKEN, TRUE, TRUE }, - { L"Alert", THREAD_ALERT, TRUE, TRUE }, - { L"Impersonate", THREAD_IMPERSONATE, TRUE, TRUE }, - { L"Direct impersonate", THREAD_DIRECT_IMPERSONATION, TRUE, TRUE }, - { L"Suspend / resume", THREAD_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, - { L"Terminate", THREAD_TERMINATE, TRUE, TRUE }, -}; - -ACCESS_ENTRIES(Timer) -{ - { L"Full control", TIMER_ALL_ACCESS, TRUE, TRUE }, - { L"Query", TIMER_QUERY_STATE, TRUE, TRUE }, - { L"Modify", TIMER_MODIFY_STATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(TmEn) -{ - { L"Full control", ENLISTMENT_ALL_ACCESS, TRUE, TRUE }, - { L"Read", ENLISTMENT_GENERIC_READ, TRUE, FALSE }, - { L"Write", ENLISTMENT_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", ENLISTMENT_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Query information", ENLISTMENT_QUERY_INFORMATION, FALSE, TRUE }, - { L"Set information", ENLISTMENT_SET_INFORMATION, FALSE, TRUE }, - { L"Recover", ENLISTMENT_RECOVER, FALSE, TRUE }, - { L"Subordinate rights", ENLISTMENT_SUBORDINATE_RIGHTS, FALSE, TRUE }, - { L"Superior rights", ENLISTMENT_SUPERIOR_RIGHTS, FALSE, TRUE } -}; - -ACCESS_ENTRIES(TmRm) -{ - { L"Full control", RESOURCEMANAGER_ALL_ACCESS, TRUE, TRUE }, - { L"Read", RESOURCEMANAGER_GENERIC_READ, TRUE, FALSE }, - { L"Write", RESOURCEMANAGER_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", RESOURCEMANAGER_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Query information", RESOURCEMANAGER_QUERY_INFORMATION, FALSE, TRUE }, - { L"Set information", RESOURCEMANAGER_SET_INFORMATION, FALSE, TRUE }, - { L"Get notifications", RESOURCEMANAGER_GET_NOTIFICATION, FALSE, TRUE }, - { L"Enlist", RESOURCEMANAGER_ENLIST, FALSE, TRUE }, - { L"Recover", RESOURCEMANAGER_RECOVER, FALSE, TRUE }, - { L"Register protocols", RESOURCEMANAGER_REGISTER_PROTOCOL, FALSE, TRUE }, - { L"Complete propagation", RESOURCEMANAGER_COMPLETE_PROPAGATION, FALSE, TRUE } -}; - -ACCESS_ENTRIES(TmTm) -{ - { L"Full control", TRANSACTIONMANAGER_ALL_ACCESS, TRUE, TRUE }, - { L"Read", TRANSACTIONMANAGER_GENERIC_READ, TRUE, FALSE }, - { L"Write", TRANSACTIONMANAGER_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", TRANSACTIONMANAGER_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Query information", TRANSACTIONMANAGER_QUERY_INFORMATION, FALSE, TRUE }, - { L"Set information", TRANSACTIONMANAGER_SET_INFORMATION, FALSE, TRUE }, - { L"Recover", TRANSACTIONMANAGER_RECOVER, FALSE, TRUE }, - { L"Rename", TRANSACTIONMANAGER_RENAME, FALSE, TRUE }, - { L"Create resource manager", TRANSACTIONMANAGER_CREATE_RM, FALSE, TRUE }, - { L"Bind transactions", TRANSACTIONMANAGER_BIND_TRANSACTION, FALSE, TRUE } -}; - -ACCESS_ENTRIES(TmTx) -{ - { L"Full control", TRANSACTION_ALL_ACCESS, TRUE, TRUE }, - { L"Read", TRANSACTION_GENERIC_READ, TRUE, FALSE }, - { L"Write", TRANSACTION_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", TRANSACTION_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Query information", TRANSACTION_QUERY_INFORMATION, FALSE, TRUE }, - { L"Set information", TRANSACTION_SET_INFORMATION, FALSE, TRUE }, - { L"Enlist", TRANSACTION_ENLIST, FALSE, TRUE }, - { L"Commit", TRANSACTION_COMMIT, FALSE, TRUE }, - { L"Rollback", TRANSACTION_ROLLBACK, FALSE, TRUE }, - { L"Propagate", TRANSACTION_PROPAGATE, FALSE, TRUE } -}; - -ACCESS_ENTRIES(Token) -{ - { L"Full control", TOKEN_ALL_ACCESS, TRUE, TRUE }, - { L"Read", TOKEN_READ, TRUE, FALSE }, - { L"Write", TOKEN_WRITE, TRUE, FALSE }, - { L"Execute", TOKEN_EXECUTE, TRUE, FALSE }, - { L"Adjust privileges", TOKEN_ADJUST_PRIVILEGES, FALSE, TRUE }, - { L"Adjust groups", TOKEN_ADJUST_GROUPS, FALSE, TRUE }, - { L"Adjust defaults", TOKEN_ADJUST_DEFAULT, FALSE, TRUE }, - { L"Adjust session ID", TOKEN_ADJUST_SESSIONID, FALSE, TRUE }, - { L"Assign as primary token", TOKEN_ASSIGN_PRIMARY, FALSE, TRUE, L"Assign primary" }, - { L"Duplicate", TOKEN_DUPLICATE, FALSE, TRUE }, - { L"Impersonate", TOKEN_IMPERSONATE, FALSE, TRUE }, - { L"Query", TOKEN_QUERY, FALSE, TRUE }, - { L"Query source", TOKEN_QUERY_SOURCE, FALSE, TRUE } -}; - -ACCESS_ENTRIES(TpWorkerFactory) -{ - { L"Full control", WORKER_FACTORY_ALL_ACCESS, TRUE, TRUE }, - { L"Release worker", WORKER_FACTORY_RELEASE_WORKER, FALSE, TRUE }, - { L"Ready worker", WORKER_FACTORY_READY_WORKER, FALSE, TRUE }, - { L"Wait", WORKER_FACTORY_WAIT, FALSE, TRUE }, - { L"Set information", WORKER_FACTORY_SET_INFORMATION, FALSE, TRUE }, - { L"Query information", WORKER_FACTORY_QUERY_INFORMATION, FALSE, TRUE }, - { L"Shutdown", WORKER_FACTORY_SHUTDOWN, FALSE, TRUE } -}; - -ACCESS_ENTRIES(Type) -{ - { L"Full control", OBJECT_TYPE_ALL_ACCESS, TRUE, TRUE }, - { L"Create", OBJECT_TYPE_CREATE, TRUE, TRUE } -}; - -ACCESS_ENTRIES(WindowStation) -{ - { L"Full control", WINSTA_ALL_ACCESS | STANDARD_RIGHTS_REQUIRED, TRUE, TRUE }, - { L"Read", WINSTA_GENERIC_READ, TRUE, FALSE }, - { L"Write", WINSTA_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", WINSTA_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Enumerate", WINSTA_ENUMERATE, FALSE, TRUE }, - { L"Enumerate desktops", WINSTA_ENUMDESKTOPS, FALSE, TRUE }, - { L"Read attributes", WINSTA_READATTRIBUTES, FALSE, TRUE }, - { L"Read screen", WINSTA_READSCREEN, FALSE, TRUE }, - { L"Access clipboard", WINSTA_ACCESSCLIPBOARD, FALSE, TRUE }, - { L"Access global atoms", WINSTA_ACCESSGLOBALATOMS, FALSE, TRUE }, - { L"Create desktop", WINSTA_CREATEDESKTOP, FALSE, TRUE }, - { L"Write attributes", WINSTA_WRITEATTRIBUTES, FALSE, TRUE }, - { L"Exit windows", WINSTA_EXITWINDOWS, FALSE, TRUE } -}; - -ACCESS_ENTRIES(WmiGuid) -{ - { L"Full control", WMIGUID_ALL_ACCESS, TRUE, TRUE }, - { L"Read", WMIGUID_GENERIC_READ, TRUE, FALSE }, - { L"Write", WMIGUID_GENERIC_WRITE, TRUE, FALSE }, - { L"Execute", WMIGUID_GENERIC_EXECUTE, TRUE, FALSE }, - { L"Query information", WMIGUID_QUERY, FALSE, TRUE }, - { L"Set information", WMIGUID_SET, FALSE, TRUE }, - { L"Get notifications", WMIGUID_NOTIFICATION, FALSE, TRUE }, - { L"Read description", WMIGUID_READ_DESCRIPTION, FALSE, TRUE }, - { L"Execute", WMIGUID_EXECUTE, FALSE, TRUE }, - { L"Create real-time logs", TRACELOG_CREATE_REALTIME, FALSE, TRUE, L"Create real-time" }, - { L"Create on disk logs", TRACELOG_CREATE_ONDISK, FALSE, TRUE, L"Create on disk" }, - { L"Enable provider GUIDs", TRACELOG_GUID_ENABLE, FALSE, TRUE, L"Enable GUIDs" }, - { L"Access kernel logger", TRACELOG_ACCESS_KERNEL_LOGGER, FALSE, TRUE }, - { L"Log events", TRACELOG_LOG_EVENT, FALSE, TRUE }, - { L"Access real-time events", TRACELOG_ACCESS_REALTIME, FALSE, TRUE, L"Access real-time" }, - { L"Register provider GUIDs", TRACELOG_REGISTER_GUIDS, FALSE, TRUE, L"Register GUIDs" } -}; - -static PH_SPECIFIC_TYPE PhSpecificTypes[] = -{ - ACCESS_ENTRY(AlpcPort, TRUE), - ACCESS_ENTRY(DebugObject, TRUE), - ACCESS_ENTRY(Desktop, FALSE), - ACCESS_ENTRY(Directory, FALSE), - ACCESS_ENTRY(Event, TRUE), - ACCESS_ENTRY(EventPair, TRUE), - ACCESS_ENTRY(File, TRUE), - ACCESS_ENTRY(FilterConnectionPort, FALSE), - ACCESS_ENTRY(IoCompletion, TRUE), - ACCESS_ENTRY(Job, TRUE), - ACCESS_ENTRY(Key, FALSE), - ACCESS_ENTRY(KeyedEvent, FALSE), - ACCESS_ENTRY(LsaAccount, FALSE), - ACCESS_ENTRY(LsaPolicy, FALSE), - ACCESS_ENTRY(LsaSecret, FALSE), - ACCESS_ENTRY(LsaTrusted, FALSE), - ACCESS_ENTRY(Mutant, TRUE), - ACCESS_ENTRY(Partition, TRUE), - ACCESS_ENTRY(Process, TRUE), - ACCESS_ENTRY(Process60, TRUE), - ACCESS_ENTRY(Profile, FALSE), - ACCESS_ENTRY(SamAlias, FALSE), - ACCESS_ENTRY(SamDomain, FALSE), - ACCESS_ENTRY(SamGroup, FALSE), - ACCESS_ENTRY(SamServer, FALSE), - ACCESS_ENTRY(SamUser, FALSE), - ACCESS_ENTRY(Section, FALSE), - ACCESS_ENTRY(Semaphore, TRUE), - ACCESS_ENTRY(Service, FALSE), - ACCESS_ENTRY(Session, FALSE), - ACCESS_ENTRY(SymbolicLink, FALSE), - ACCESS_ENTRY(Thread, TRUE), - ACCESS_ENTRY(Thread60, TRUE), - ACCESS_ENTRY(Timer, TRUE), - ACCESS_ENTRY(TmEn, FALSE), - ACCESS_ENTRY(TmRm, FALSE), - ACCESS_ENTRY(TmTm, FALSE), - ACCESS_ENTRY(TmTx, FALSE), - ACCESS_ENTRY(Token, FALSE), - ACCESS_ENTRY(TpWorkerFactory, FALSE), - ACCESS_ENTRY(Type, FALSE), - ACCESS_ENTRY(WindowStation, FALSE), - ACCESS_ENTRY(WmiGuid, TRUE) -}; - -/** - * Gets access entries for an object type. - * - * \param Type The name of the object type. - * \param AccessEntries A variable which receives an array of access entry structures. You must free - * the buffer with PhFree() when you no longer need it. - * \param NumberOfAccessEntries A variable which receives the number of access entry structures - * returned in - * \a AccessEntries. - */ -BOOLEAN PhGetAccessEntries( - _In_ PWSTR Type, - _Out_ PPH_ACCESS_ENTRY *AccessEntries, - _Out_ PULONG NumberOfAccessEntries - ) -{ - ULONG i; - PPH_SPECIFIC_TYPE specificType = NULL; - PPH_ACCESS_ENTRY accessEntries; - - if (PhEqualStringZ(Type, L"ALPC Port", TRUE)) - { - Type = L"AlpcPort"; - } - else if (PhEqualStringZ(Type, L"Port", TRUE)) - { - Type = L"AlpcPort"; - } - else if (PhEqualStringZ(Type, L"WaitablePort", TRUE)) - { - Type = L"AlpcPort"; - } - else if (PhEqualStringZ(Type, L"Process", TRUE)) - { - if (WindowsVersion >= WINDOWS_VISTA) - Type = L"Process60"; - } - else if (PhEqualStringZ(Type, L"Thread", TRUE)) - { - if (WindowsVersion >= WINDOWS_VISTA) - Type = L"Thread60"; - } - - // Find the specific type. - for (i = 0; i < sizeof(PhSpecificTypes) / sizeof(PH_SPECIFIC_TYPE); i++) - { - if (PhEqualStringZ(PhSpecificTypes[i].Type, Type, TRUE)) - { - specificType = &PhSpecificTypes[i]; - break; - } - } - - if (specificType) - { - ULONG sizeOfEntries; - - // Copy the specific access entries and append the standard access entries. - - if (specificType->HasSynchronize) - sizeOfEntries = specificType->SizeOfAccessEntries + sizeof(PhStandardAccessEntries); - else - sizeOfEntries = specificType->SizeOfAccessEntries + sizeof(PhStandardAccessEntries) - sizeof(PH_ACCESS_ENTRY); - - accessEntries = PhAllocate(sizeOfEntries); - memcpy(accessEntries, specificType->AccessEntries, specificType->SizeOfAccessEntries); - - if (specificType->HasSynchronize) - { - memcpy( - PTR_ADD_OFFSET(accessEntries, specificType->SizeOfAccessEntries), - PhStandardAccessEntries, - sizeof(PhStandardAccessEntries) - ); - } - else - { - memcpy( - PTR_ADD_OFFSET(accessEntries, specificType->SizeOfAccessEntries), - &PhStandardAccessEntries[1], - sizeof(PhStandardAccessEntries) - sizeof(PH_ACCESS_ENTRY) - ); - } - - *AccessEntries = accessEntries; - *NumberOfAccessEntries = sizeOfEntries / sizeof(PH_ACCESS_ENTRY); - } - else - { - accessEntries = PhAllocate(sizeof(PhStandardAccessEntries)); - memcpy(accessEntries, PhStandardAccessEntries, sizeof(PhStandardAccessEntries)); - - *AccessEntries = accessEntries; - *NumberOfAccessEntries = sizeof(PhStandardAccessEntries) / sizeof(PH_ACCESS_ENTRY); - } - - return TRUE; -} - -static int __cdecl PhpAccessEntryCompare( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_ACCESS_ENTRY entry1 = (PPH_ACCESS_ENTRY)elem1; - PPH_ACCESS_ENTRY entry2 = (PPH_ACCESS_ENTRY)elem2; - - return intcmp(PhCountBits(entry2->Access), PhCountBits(entry1->Access)); -} - -/** - * Creates a string representation of an access mask. - * - * \param Access The access mask. - * \param AccessEntries An array of access entry structures. You can call PhGetAccessEntries() to - * retrieve the access entry structures for a standard object type. - * \param NumberOfAccessEntries The number of elements in \a AccessEntries. - * - * \return The string representation of \a Access. - */ -PPH_STRING PhGetAccessString( - _In_ ACCESS_MASK Access, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries - ) -{ - PH_STRING_BUILDER stringBuilder; - PPH_ACCESS_ENTRY accessEntries; - PBOOLEAN matched; - ULONG i; - ULONG j; - - PhInitializeStringBuilder(&stringBuilder, 32); - - // Sort the access entries according to how many access rights they include. - accessEntries = PhAllocateCopy(AccessEntries, NumberOfAccessEntries * sizeof(PH_ACCESS_ENTRY)); - qsort(accessEntries, NumberOfAccessEntries, sizeof(PH_ACCESS_ENTRY), PhpAccessEntryCompare); - - matched = PhAllocate(NumberOfAccessEntries * sizeof(BOOLEAN)); - memset(matched, 0, NumberOfAccessEntries * sizeof(BOOLEAN)); - - for (i = 0; i < NumberOfAccessEntries; i++) - { - // We make sure we haven't matched this access entry yet. This ensures that we won't get - // duplicates, e.g. FILE_GENERIC_READ includes FILE_READ_DATA, and we don't want to display - // both to the user. - if ( - !matched[i] && - ((Access & accessEntries[i].Access) == accessEntries[i].Access) - ) - { - if (accessEntries[i].ShortName) - PhAppendStringBuilder2(&stringBuilder, accessEntries[i].ShortName); - else - PhAppendStringBuilder2(&stringBuilder, accessEntries[i].Name); - - PhAppendStringBuilder2(&stringBuilder, L", "); - - // Disable equal or more specific entries. - for (j = i; j < NumberOfAccessEntries; j++) - { - if ((accessEntries[i].Access | accessEntries[j].Access) == accessEntries[i].Access) - matched[j] = TRUE; - } - } - } - - // Remove the trailing ", ". - if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) - PhRemoveEndStringBuilder(&stringBuilder, 2); - - PhFree(matched); - PhFree(accessEntries); - - return PhFinalStringBuilderString(&stringBuilder); -} +/* + * Process Hacker - + * object security data + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#include + +#define ACCESS_ENTRIES(Type) static PH_ACCESS_ENTRY Ph##Type##AccessEntries[] = +#define ACCESS_ENTRY(Type, HasSynchronize) \ + { L#Type, Ph##Type##AccessEntries, sizeof(Ph##Type##AccessEntries), HasSynchronize } + +typedef struct _PH_SPECIFIC_TYPE +{ + PWSTR Type; + PPH_ACCESS_ENTRY AccessEntries; + ULONG SizeOfAccessEntries; + BOOLEAN HasSynchronize; +} PH_SPECIFIC_TYPE, *PPH_SPECIFIC_TYPE; + +ACCESS_ENTRIES(Standard) +{ + { L"Synchronize", SYNCHRONIZE, FALSE, TRUE }, + { L"Delete", DELETE, FALSE, TRUE }, + { L"Read permissions", READ_CONTROL, FALSE, TRUE, L"Read control" }, + { L"Change permissions", WRITE_DAC, FALSE, TRUE, L"Write DAC" }, + { L"Take ownership", WRITE_OWNER, FALSE, TRUE, L"Write owner" } +}; + +ACCESS_ENTRIES(AlpcPort) +{ + { L"Full control", PORT_ALL_ACCESS, TRUE, TRUE }, + { L"Connect", PORT_CONNECT, TRUE, TRUE } +}; + +ACCESS_ENTRIES(DebugObject) +{ + { L"Full control", DEBUG_ALL_ACCESS, TRUE, TRUE }, + { L"Read events", DEBUG_READ_EVENT, TRUE, TRUE }, + { L"Assign processes", DEBUG_PROCESS_ASSIGN, TRUE, TRUE }, + { L"Query information", DEBUG_QUERY_INFORMATION, TRUE, TRUE }, + { L"Set information", DEBUG_SET_INFORMATION, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Desktop) +{ + { L"Full control", DESKTOP_ALL_ACCESS, TRUE, TRUE }, + { L"Read", DESKTOP_GENERIC_READ, TRUE, FALSE }, + { L"Write", DESKTOP_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", DESKTOP_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Enumerate", DESKTOP_ENUMERATE, FALSE, TRUE }, + { L"Read objects", DESKTOP_READOBJECTS, FALSE, TRUE }, + { L"Playback journals", DESKTOP_JOURNALPLAYBACK, FALSE, TRUE }, + { L"Write objects", DESKTOP_WRITEOBJECTS, FALSE, TRUE }, + { L"Create windows", DESKTOP_CREATEWINDOW, FALSE, TRUE }, + { L"Create menus", DESKTOP_CREATEMENU, FALSE, TRUE }, + { L"Create window hooks", DESKTOP_HOOKCONTROL, FALSE, TRUE }, + { L"Record journals", DESKTOP_JOURNALRECORD, FALSE, TRUE }, + { L"Switch desktop", DESKTOP_SWITCHDESKTOP, FALSE, TRUE } +}; + +ACCESS_ENTRIES(Directory) +{ + { L"Full control", DIRECTORY_ALL_ACCESS, TRUE, TRUE }, + { L"Query", DIRECTORY_QUERY, TRUE, TRUE}, + { L"Traverse", DIRECTORY_TRAVERSE, TRUE, TRUE}, + { L"Create objects", DIRECTORY_CREATE_OBJECT, TRUE, TRUE}, + { L"Create subdirectories", DIRECTORY_CREATE_SUBDIRECTORY, TRUE, TRUE} +}; + +ACCESS_ENTRIES(Event) +{ + { L"Full control", EVENT_ALL_ACCESS, TRUE, TRUE }, + { L"Query", EVENT_QUERY_STATE, TRUE, TRUE }, + { L"Modify", EVENT_MODIFY_STATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(EventPair) +{ + { L"Full control", EVENT_PAIR_ALL_ACCESS, TRUE, TRUE } +}; + +ACCESS_ENTRIES(File) +{ + { L"Full control", FILE_ALL_ACCESS, TRUE, TRUE }, + { L"Read & execute", FILE_GENERIC_READ | FILE_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Read", FILE_GENERIC_READ, TRUE, FALSE }, + { L"Write", FILE_GENERIC_WRITE, TRUE, FALSE }, + { L"Traverse folder / execute file", FILE_EXECUTE, FALSE, TRUE, L"Execute" }, + { L"List folder / read data", FILE_READ_DATA, FALSE, TRUE, L"Read data" }, + { L"Read attributes", FILE_READ_ATTRIBUTES, FALSE, TRUE }, + { L"Read extended attributes", FILE_READ_EA, FALSE, TRUE, L"Read EA" }, + { L"Create files / write data", FILE_WRITE_DATA, FALSE, TRUE, L"Write data" }, + { L"Create folders / append data", FILE_APPEND_DATA, FALSE, TRUE, L"Append data" }, + { L"Write attributes", FILE_WRITE_ATTRIBUTES, FALSE, TRUE }, + { L"Write extended attributes", FILE_WRITE_EA, FALSE, TRUE, L"Write EA" }, + { L"Delete subfolders and files", FILE_DELETE_CHILD, FALSE, TRUE, L"Delete child" } +}; + +ACCESS_ENTRIES(FilterConnectionPort) +{ + { L"Full control", FLT_PORT_ALL_ACCESS, TRUE, TRUE }, + { L"Connect", FLT_PORT_CONNECT, TRUE, TRUE } +}; + +ACCESS_ENTRIES(IoCompletion) +{ + { L"Full control", IO_COMPLETION_ALL_ACCESS, TRUE, TRUE }, + { L"Query", IO_COMPLETION_QUERY_STATE, TRUE, TRUE }, + { L"Modify", IO_COMPLETION_MODIFY_STATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Job) +{ + { L"Full control", JOB_OBJECT_ALL_ACCESS, TRUE, TRUE }, + { L"Query", JOB_OBJECT_QUERY, TRUE, TRUE }, + { L"Assign processes", JOB_OBJECT_ASSIGN_PROCESS, TRUE, TRUE }, + { L"Set attributes", JOB_OBJECT_SET_ATTRIBUTES, TRUE, TRUE }, + { L"Set security attributes", JOB_OBJECT_SET_SECURITY_ATTRIBUTES, TRUE, TRUE }, + { L"Terminate", JOB_OBJECT_TERMINATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Key) +{ + { L"Full control", KEY_ALL_ACCESS, TRUE, TRUE }, + { L"Read", KEY_READ, TRUE, FALSE }, + { L"Write", KEY_WRITE, TRUE, FALSE }, + { L"Execute", KEY_EXECUTE, TRUE, FALSE }, + { L"Enumerate subkeys", KEY_ENUMERATE_SUB_KEYS, FALSE, TRUE }, + { L"Query values", KEY_QUERY_VALUE, FALSE, TRUE }, + { L"Notify", KEY_NOTIFY, FALSE, TRUE }, + { L"Set values", KEY_SET_VALUE, FALSE, TRUE }, + { L"Create subkeys", KEY_CREATE_SUB_KEY, FALSE, TRUE }, + { L"Create links", KEY_CREATE_LINK, FALSE, TRUE } +}; + +ACCESS_ENTRIES(KeyedEvent) +{ + { L"Full control", KEYEDEVENT_ALL_ACCESS, TRUE, TRUE }, + { L"Wait", KEYEDEVENT_WAIT, TRUE, TRUE }, + { L"Wake", KEYEDEVENT_WAKE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(LsaAccount) +{ + { L"Full control", ACCOUNT_ALL_ACCESS, TRUE, TRUE }, + { L"Read", ACCOUNT_READ, TRUE, FALSE }, + { L"Write", ACCOUNT_WRITE, TRUE, FALSE }, + { L"Execute", ACCOUNT_EXECUTE, TRUE, FALSE }, + { L"View", ACCOUNT_VIEW, FALSE, TRUE }, + { L"Adjust privileges", ACCOUNT_ADJUST_PRIVILEGES, FALSE, TRUE }, + { L"Adjust quotas", ACCOUNT_ADJUST_QUOTAS, FALSE, TRUE }, + { L"Adjust system access", ACCOUNT_ADJUST_SYSTEM_ACCESS, FALSE, TRUE } +}; + +ACCESS_ENTRIES(LsaPolicy) +{ + { L"Full control", POLICY_ALL_ACCESS | POLICY_NOTIFICATION, TRUE, TRUE }, + { L"Read", POLICY_READ, TRUE, FALSE }, + { L"Write", POLICY_WRITE, TRUE, FALSE }, + { L"Execute", POLICY_EXECUTE | POLICY_NOTIFICATION, TRUE, FALSE }, + { L"View local information", POLICY_VIEW_LOCAL_INFORMATION, FALSE, TRUE }, + { L"View audit information", POLICY_VIEW_AUDIT_INFORMATION, FALSE, TRUE }, + { L"Get private information", POLICY_GET_PRIVATE_INFORMATION, FALSE, TRUE }, + { L"Administer trust", POLICY_TRUST_ADMIN, FALSE, TRUE }, + { L"Create account", POLICY_CREATE_ACCOUNT, FALSE, TRUE }, + { L"Create secret", POLICY_CREATE_SECRET, FALSE, TRUE }, + { L"Create privilege", POLICY_CREATE_PRIVILEGE, FALSE, TRUE }, + { L"Set default quota limits", POLICY_SET_DEFAULT_QUOTA_LIMITS, FALSE, TRUE }, + { L"Set audit requirements", POLICY_SET_AUDIT_REQUIREMENTS, FALSE, TRUE }, + { L"Administer audit log", POLICY_AUDIT_LOG_ADMIN, FALSE, TRUE }, + { L"Administer server", POLICY_SERVER_ADMIN, FALSE, TRUE }, + { L"Lookup names", POLICY_LOOKUP_NAMES, FALSE, TRUE }, + { L"Get notifications", POLICY_NOTIFICATION, FALSE, TRUE } +}; + +ACCESS_ENTRIES(LsaSecret) +{ + { L"Full control", SECRET_ALL_ACCESS, TRUE, TRUE }, + { L"Read", SECRET_READ, TRUE, FALSE }, + { L"Write", SECRET_WRITE, TRUE, FALSE }, + { L"Execute", SECRET_EXECUTE, TRUE, FALSE }, + { L"Set value", SECRET_SET_VALUE, FALSE, TRUE }, + { L"Query value", SECRET_QUERY_VALUE, FALSE, TRUE } +}; + +ACCESS_ENTRIES(LsaTrusted) +{ + { L"Full control", TRUSTED_ALL_ACCESS, TRUE, TRUE }, + { L"Read", TRUSTED_READ, TRUE, FALSE }, + { L"Write", TRUSTED_WRITE, TRUE, FALSE }, + { L"Execute", TRUSTED_EXECUTE, TRUE, FALSE }, + { L"Query domain name", TRUSTED_QUERY_DOMAIN_NAME, FALSE, TRUE }, + { L"Query controllers", TRUSTED_QUERY_CONTROLLERS, FALSE, TRUE }, + { L"Set controllers", TRUSTED_SET_CONTROLLERS, FALSE, TRUE }, + { L"Query POSIX", TRUSTED_QUERY_POSIX, FALSE, TRUE }, + { L"Set POSIX", TRUSTED_SET_POSIX, FALSE, TRUE }, + { L"Query authentication", TRUSTED_QUERY_AUTH, FALSE, TRUE }, + { L"Set authentication", TRUSTED_SET_AUTH, FALSE, TRUE } +}; + +ACCESS_ENTRIES(Mutant) +{ + { L"Full control", MUTANT_ALL_ACCESS, TRUE, TRUE }, + { L"Query", MUTANT_QUERY_STATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Partition) +{ + { L"Full control", MEMORY_PARTITION_ALL_ACCESS, TRUE, TRUE }, + { L"Query", MEMORY_PARTITION_QUERY_ACCESS, TRUE, TRUE }, + { L"Modify", MEMORY_PARTITION_MODIFY_ACCESS, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Process) +{ + { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff, TRUE, TRUE }, + { L"Query information", PROCESS_QUERY_INFORMATION, TRUE, TRUE }, + { L"Set information", PROCESS_SET_INFORMATION, TRUE, TRUE }, + { L"Set quotas", PROCESS_SET_QUOTA, TRUE, TRUE }, + { L"Set session ID", PROCESS_SET_SESSIONID, TRUE, TRUE }, + { L"Create threads", PROCESS_CREATE_THREAD, TRUE, TRUE }, + { L"Create processes", PROCESS_CREATE_PROCESS, TRUE, TRUE }, + { L"Modify memory", PROCESS_VM_OPERATION, TRUE, TRUE, L"VM operation" }, + { L"Read memory", PROCESS_VM_READ, TRUE, TRUE, L"VM read" }, + { L"Write memory", PROCESS_VM_WRITE, TRUE, TRUE, L"VM write" }, + { L"Duplicate handles", PROCESS_DUP_HANDLE, TRUE, TRUE }, + { L"Suspend / resume / set port", PROCESS_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, + { L"Terminate", PROCESS_TERMINATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Process60) +{ + { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff, TRUE, TRUE }, // PROCESS_ALL_ACCESS + { L"Query limited information", PROCESS_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Query information", PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Set information", PROCESS_SET_INFORMATION, TRUE, TRUE }, + { L"Set limited information", PROCESS_SET_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Set quotas", PROCESS_SET_QUOTA, TRUE, TRUE }, + { L"Set session ID", PROCESS_SET_SESSIONID, TRUE, TRUE }, + { L"Create threads", PROCESS_CREATE_THREAD, TRUE, TRUE }, + { L"Create processes", PROCESS_CREATE_PROCESS, TRUE, TRUE }, + { L"Modify memory", PROCESS_VM_OPERATION, TRUE, TRUE, L"VM operation" }, + { L"Read memory", PROCESS_VM_READ, TRUE, TRUE, L"VM read" }, + { L"Write memory", PROCESS_VM_WRITE, TRUE, TRUE, L"VM write" }, + { L"Duplicate handles", PROCESS_DUP_HANDLE, TRUE, TRUE }, + { L"Suspend / resume / set port", PROCESS_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, + { L"Terminate", PROCESS_TERMINATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Profile) +{ + { L"Full control", PROFILE_ALL_ACCESS, TRUE, TRUE }, + { L"Control", PROFILE_CONTROL, TRUE, TRUE } +}; + +ACCESS_ENTRIES(SamAlias) +{ + { L"Full control", ALIAS_ALL_ACCESS, TRUE, TRUE }, + { L"Read", ALIAS_READ, TRUE, FALSE }, + { L"Write", ALIAS_WRITE, TRUE, FALSE }, + { L"Execute", ALIAS_EXECUTE, TRUE, FALSE }, + { L"Read information", ALIAS_READ_INFORMATION, FALSE, TRUE }, + { L"Write account", ALIAS_WRITE_ACCOUNT, FALSE, TRUE }, + { L"Add member", ALIAS_ADD_MEMBER, FALSE, TRUE }, + { L"Remove member", ALIAS_REMOVE_MEMBER, FALSE, TRUE }, + { L"List members", ALIAS_LIST_MEMBERS, FALSE, TRUE } +}; + +ACCESS_ENTRIES(SamDomain) +{ + { L"Full control", DOMAIN_ALL_ACCESS, TRUE, TRUE }, + { L"Read", DOMAIN_READ, TRUE, FALSE }, + { L"Write", DOMAIN_WRITE, TRUE, FALSE }, + { L"Execute", DOMAIN_EXECUTE, TRUE, FALSE }, + { L"Read password parameters", DOMAIN_READ_PASSWORD_PARAMETERS, FALSE, TRUE }, + { L"Write password parameters", DOMAIN_WRITE_PASSWORD_PARAMS, FALSE, TRUE }, + { L"Read other parameters", DOMAIN_READ_OTHER_PARAMETERS, FALSE, TRUE }, + { L"Write other parameters", DOMAIN_WRITE_OTHER_PARAMETERS, FALSE, TRUE }, + { L"Create user", DOMAIN_CREATE_USER, FALSE, TRUE }, + { L"Create group", DOMAIN_CREATE_GROUP, FALSE, TRUE }, + { L"Create alias", DOMAIN_CREATE_ALIAS, FALSE, TRUE }, + { L"Get alias membership", DOMAIN_GET_ALIAS_MEMBERSHIP, FALSE, TRUE }, + { L"List accounts", DOMAIN_LIST_ACCOUNTS, FALSE, TRUE }, + { L"Lookup", DOMAIN_LOOKUP, FALSE, TRUE }, + { L"Administer server", DOMAIN_ADMINISTER_SERVER, FALSE, TRUE } +}; + +ACCESS_ENTRIES(SamGroup) +{ + { L"Full control", GROUP_ALL_ACCESS, TRUE, TRUE }, + { L"Read", GROUP_READ, TRUE, FALSE }, + { L"Write", GROUP_WRITE, TRUE, FALSE }, + { L"Execute", GROUP_EXECUTE, TRUE, FALSE }, + { L"Read information", GROUP_READ_INFORMATION, FALSE, TRUE }, + { L"Write account", GROUP_WRITE_ACCOUNT, FALSE, TRUE }, + { L"Add member", GROUP_ADD_MEMBER, FALSE, TRUE }, + { L"Remove member", GROUP_REMOVE_MEMBER, FALSE, TRUE }, + { L"List members", GROUP_LIST_MEMBERS, FALSE, TRUE } +}; + +ACCESS_ENTRIES(SamServer) +{ + { L"Full control", SAM_SERVER_ALL_ACCESS, TRUE, TRUE }, + { L"Read", SAM_SERVER_READ, TRUE, FALSE }, + { L"Write", SAM_SERVER_WRITE, TRUE, FALSE }, + { L"Execute", SAM_SERVER_EXECUTE, TRUE, FALSE }, + { L"Connect", SAM_SERVER_CONNECT, FALSE, TRUE }, + { L"Shutdown", SAM_SERVER_SHUTDOWN, FALSE, TRUE }, + { L"Initialize", SAM_SERVER_INITIALIZE, FALSE, TRUE }, + { L"Create domain", SAM_SERVER_CREATE_DOMAIN, FALSE, TRUE }, + { L"Enumerate domains", SAM_SERVER_ENUMERATE_DOMAINS, FALSE, TRUE }, + { L"Lookup domain", SAM_SERVER_LOOKUP_DOMAIN, FALSE, TRUE } +}; + +ACCESS_ENTRIES(SamUser) +{ + { L"Full control", USER_ALL_ACCESS, TRUE, TRUE }, + { L"Read", USER_READ, TRUE, FALSE }, + { L"Write", USER_WRITE, TRUE, FALSE }, + { L"Execute", USER_EXECUTE, TRUE, FALSE }, + { L"Read general", USER_READ_GENERAL, FALSE, TRUE }, + { L"Read preferences", USER_READ_PREFERENCES, FALSE, TRUE }, + { L"Write preferences", USER_WRITE_PREFERENCES, FALSE, TRUE }, + { L"Read logon", USER_READ_LOGON, FALSE, TRUE }, + { L"Read account", USER_READ_ACCOUNT, FALSE, TRUE }, + { L"Write account", USER_WRITE_ACCOUNT, FALSE, TRUE }, + { L"Change password", USER_CHANGE_PASSWORD, FALSE, TRUE }, + { L"Force password change", USER_FORCE_PASSWORD_CHANGE, FALSE, TRUE }, + { L"List groups", USER_LIST_GROUPS, FALSE, TRUE }, + { L"Read group information", USER_READ_GROUP_INFORMATION, FALSE, TRUE }, + { L"Write group information", USER_WRITE_GROUP_INFORMATION, FALSE, TRUE } +}; + +ACCESS_ENTRIES(Section) +{ + { L"Full control", SECTION_ALL_ACCESS, TRUE, TRUE }, + { L"Query", SECTION_QUERY, TRUE, TRUE }, + { L"Map for read", SECTION_MAP_READ, TRUE, TRUE, L"Map read" }, + { L"Map for write", SECTION_MAP_WRITE, TRUE, TRUE, L"Map write" }, + { L"Map for execute", SECTION_MAP_EXECUTE, TRUE, TRUE, L"Map execute" }, + { L"Map for execute (explicit)", SECTION_MAP_EXECUTE_EXPLICIT, TRUE, TRUE, L"Map execute explicit" }, + { L"Extend size", SECTION_EXTEND_SIZE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Semaphore) +{ + { L"Full control", SEMAPHORE_ALL_ACCESS, TRUE, TRUE }, + { L"Query", SEMAPHORE_QUERY_STATE, TRUE, TRUE }, + { L"Modify", SEMAPHORE_MODIFY_STATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Service) +{ + { L"Full control", SERVICE_ALL_ACCESS, TRUE, TRUE }, + { L"Query status", SERVICE_QUERY_STATUS, TRUE, TRUE }, + { L"Query configuration", SERVICE_QUERY_CONFIG, TRUE, TRUE }, + { L"Modify configuration", SERVICE_CHANGE_CONFIG, TRUE, TRUE }, + { L"Enumerate dependents", SERVICE_ENUMERATE_DEPENDENTS, TRUE, TRUE }, + { L"Start", SERVICE_START, TRUE, TRUE }, + { L"Stop", SERVICE_STOP, TRUE, TRUE }, + { L"Pause / continue", SERVICE_PAUSE_CONTINUE, TRUE, TRUE, L"Pause/continue" }, + { L"Interrogate", SERVICE_INTERROGATE, TRUE, TRUE }, + { L"User-defined control", SERVICE_USER_DEFINED_CONTROL, TRUE, TRUE } +}; + +ACCESS_ENTRIES(SCManager) +{ + { L"Full control", SC_MANAGER_ALL_ACCESS, TRUE, TRUE }, + { L"Create service", SC_MANAGER_CREATE_SERVICE, TRUE, TRUE }, + { L"Connect", SC_MANAGER_CONNECT, TRUE, TRUE }, + { L"Enumerate services", SC_MANAGER_ENUMERATE_SERVICE, TRUE, TRUE }, + { L"Lock", SC_MANAGER_LOCK, TRUE, TRUE }, + { L"Modify boot config", SC_MANAGER_MODIFY_BOOT_CONFIG, TRUE, TRUE }, + { L"Query lock status", SC_MANAGER_QUERY_LOCK_STATUS, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Session) +{ + { L"Full control", SESSION_ALL_ACCESS, TRUE, TRUE }, + { L"Query", SESSION_QUERY_ACCESS, TRUE, TRUE }, + { L"Modify", SESSION_MODIFY_ACCESS, TRUE, TRUE } +}; + +ACCESS_ENTRIES(SymbolicLink) +{ + { L"Full control", SYMBOLIC_LINK_ALL_ACCESS, TRUE, TRUE }, + { L"Query", SYMBOLIC_LINK_QUERY, TRUE, TRUE } +}; + +ACCESS_ENTRIES(Thread) +{ + { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff, TRUE, TRUE }, + { L"Query information", THREAD_QUERY_INFORMATION, TRUE, TRUE }, + { L"Set information", THREAD_SET_INFORMATION, TRUE, TRUE }, + { L"Get context", THREAD_GET_CONTEXT, TRUE, TRUE }, + { L"Set context", THREAD_SET_CONTEXT, TRUE, TRUE }, + { L"Set token", THREAD_SET_THREAD_TOKEN, TRUE, TRUE }, + { L"Alert", THREAD_ALERT, TRUE, TRUE }, + { L"Impersonate", THREAD_IMPERSONATE, TRUE, TRUE }, + { L"Direct impersonate", THREAD_DIRECT_IMPERSONATION, TRUE, TRUE }, + { L"Suspend / resume", THREAD_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, + { L"Terminate", THREAD_TERMINATE, TRUE, TRUE }, +}; + +ACCESS_ENTRIES(Thread60) +{ + { L"Full control", STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff, TRUE, TRUE }, // THREAD_ALL_ACCESS + { L"Query limited information", THREAD_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Query information", THREAD_QUERY_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Set limited information", THREAD_SET_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Set information", THREAD_SET_INFORMATION | THREAD_SET_LIMITED_INFORMATION, TRUE, TRUE }, + { L"Get context", THREAD_GET_CONTEXT, TRUE, TRUE }, + { L"Set context", THREAD_SET_CONTEXT, TRUE, TRUE }, + { L"Set token", THREAD_SET_THREAD_TOKEN, TRUE, TRUE }, + { L"Alert", THREAD_ALERT, TRUE, TRUE }, + { L"Impersonate", THREAD_IMPERSONATE, TRUE, TRUE }, + { L"Direct impersonate", THREAD_DIRECT_IMPERSONATION, TRUE, TRUE }, + { L"Suspend / resume", THREAD_SUSPEND_RESUME, TRUE, TRUE, L"Suspend/resume" }, + { L"Terminate", THREAD_TERMINATE, TRUE, TRUE }, +}; + +ACCESS_ENTRIES(Timer) +{ + { L"Full control", TIMER_ALL_ACCESS, TRUE, TRUE }, + { L"Query", TIMER_QUERY_STATE, TRUE, TRUE }, + { L"Modify", TIMER_MODIFY_STATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(TmEn) +{ + { L"Full control", ENLISTMENT_ALL_ACCESS, TRUE, TRUE }, + { L"Read", ENLISTMENT_GENERIC_READ, TRUE, FALSE }, + { L"Write", ENLISTMENT_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", ENLISTMENT_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Query information", ENLISTMENT_QUERY_INFORMATION, FALSE, TRUE }, + { L"Set information", ENLISTMENT_SET_INFORMATION, FALSE, TRUE }, + { L"Recover", ENLISTMENT_RECOVER, FALSE, TRUE }, + { L"Subordinate rights", ENLISTMENT_SUBORDINATE_RIGHTS, FALSE, TRUE }, + { L"Superior rights", ENLISTMENT_SUPERIOR_RIGHTS, FALSE, TRUE } +}; + +ACCESS_ENTRIES(TmRm) +{ + { L"Full control", RESOURCEMANAGER_ALL_ACCESS, TRUE, TRUE }, + { L"Read", RESOURCEMANAGER_GENERIC_READ, TRUE, FALSE }, + { L"Write", RESOURCEMANAGER_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", RESOURCEMANAGER_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Query information", RESOURCEMANAGER_QUERY_INFORMATION, FALSE, TRUE }, + { L"Set information", RESOURCEMANAGER_SET_INFORMATION, FALSE, TRUE }, + { L"Get notifications", RESOURCEMANAGER_GET_NOTIFICATION, FALSE, TRUE }, + { L"Enlist", RESOURCEMANAGER_ENLIST, FALSE, TRUE }, + { L"Recover", RESOURCEMANAGER_RECOVER, FALSE, TRUE }, + { L"Register protocols", RESOURCEMANAGER_REGISTER_PROTOCOL, FALSE, TRUE }, + { L"Complete propagation", RESOURCEMANAGER_COMPLETE_PROPAGATION, FALSE, TRUE } +}; + +ACCESS_ENTRIES(TmTm) +{ + { L"Full control", TRANSACTIONMANAGER_ALL_ACCESS, TRUE, TRUE }, + { L"Read", TRANSACTIONMANAGER_GENERIC_READ, TRUE, FALSE }, + { L"Write", TRANSACTIONMANAGER_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", TRANSACTIONMANAGER_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Query information", TRANSACTIONMANAGER_QUERY_INFORMATION, FALSE, TRUE }, + { L"Set information", TRANSACTIONMANAGER_SET_INFORMATION, FALSE, TRUE }, + { L"Recover", TRANSACTIONMANAGER_RECOVER, FALSE, TRUE }, + { L"Rename", TRANSACTIONMANAGER_RENAME, FALSE, TRUE }, + { L"Create resource manager", TRANSACTIONMANAGER_CREATE_RM, FALSE, TRUE }, + { L"Bind transactions", TRANSACTIONMANAGER_BIND_TRANSACTION, FALSE, TRUE } +}; + +ACCESS_ENTRIES(TmTx) +{ + { L"Full control", TRANSACTION_ALL_ACCESS, TRUE, TRUE }, + { L"Read", TRANSACTION_GENERIC_READ, TRUE, FALSE }, + { L"Write", TRANSACTION_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", TRANSACTION_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Query information", TRANSACTION_QUERY_INFORMATION, FALSE, TRUE }, + { L"Set information", TRANSACTION_SET_INFORMATION, FALSE, TRUE }, + { L"Enlist", TRANSACTION_ENLIST, FALSE, TRUE }, + { L"Commit", TRANSACTION_COMMIT, FALSE, TRUE }, + { L"Rollback", TRANSACTION_ROLLBACK, FALSE, TRUE }, + { L"Propagate", TRANSACTION_PROPAGATE, FALSE, TRUE } +}; + +ACCESS_ENTRIES(Token) +{ + { L"Full control", TOKEN_ALL_ACCESS, TRUE, TRUE }, + { L"Read", TOKEN_READ, FALSE, FALSE }, + { L"Write", TOKEN_WRITE, FALSE, FALSE }, + { L"Execute", TOKEN_EXECUTE, FALSE, FALSE }, + { L"Adjust privileges", TOKEN_ADJUST_PRIVILEGES, TRUE, TRUE }, + { L"Adjust groups", TOKEN_ADJUST_GROUPS, TRUE, TRUE }, + { L"Adjust defaults", TOKEN_ADJUST_DEFAULT, TRUE, TRUE }, + { L"Adjust session ID", TOKEN_ADJUST_SESSIONID, TRUE, TRUE }, + { L"Assign as primary token", TOKEN_ASSIGN_PRIMARY, TRUE, TRUE, L"Assign primary" }, + { L"Duplicate", TOKEN_DUPLICATE, TRUE, TRUE }, + { L"Impersonate", TOKEN_IMPERSONATE, TRUE, TRUE }, + { L"Query", TOKEN_QUERY, TRUE, TRUE }, + { L"Query source", TOKEN_QUERY_SOURCE, FALSE, TRUE } +}; + +ACCESS_ENTRIES(TokenDefault) +{ + { L"Full control", GENERIC_ALL, TRUE, TRUE }, + { L"Read", GENERIC_READ, TRUE, TRUE }, + { L"Write", GENERIC_WRITE, TRUE, TRUE }, + { L"Execute", GENERIC_EXECUTE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(TpWorkerFactory) +{ + { L"Full control", WORKER_FACTORY_ALL_ACCESS, TRUE, TRUE }, + { L"Release worker", WORKER_FACTORY_RELEASE_WORKER, FALSE, TRUE }, + { L"Ready worker", WORKER_FACTORY_READY_WORKER, FALSE, TRUE }, + { L"Wait", WORKER_FACTORY_WAIT, FALSE, TRUE }, + { L"Set information", WORKER_FACTORY_SET_INFORMATION, FALSE, TRUE }, + { L"Query information", WORKER_FACTORY_QUERY_INFORMATION, FALSE, TRUE }, + { L"Shutdown", WORKER_FACTORY_SHUTDOWN, FALSE, TRUE } +}; + +ACCESS_ENTRIES(Type) +{ + { L"Full control", OBJECT_TYPE_ALL_ACCESS, TRUE, TRUE }, + { L"Create", OBJECT_TYPE_CREATE, TRUE, TRUE } +}; + +ACCESS_ENTRIES(WindowStation) +{ + { L"Full control", WINSTA_ALL_ACCESS | STANDARD_RIGHTS_REQUIRED, TRUE, TRUE }, + { L"Read", WINSTA_GENERIC_READ, TRUE, FALSE }, + { L"Write", WINSTA_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", WINSTA_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Enumerate", WINSTA_ENUMERATE, FALSE, TRUE }, + { L"Enumerate desktops", WINSTA_ENUMDESKTOPS, FALSE, TRUE }, + { L"Read attributes", WINSTA_READATTRIBUTES, FALSE, TRUE }, + { L"Read screen", WINSTA_READSCREEN, FALSE, TRUE }, + { L"Access clipboard", WINSTA_ACCESSCLIPBOARD, FALSE, TRUE }, + { L"Access global atoms", WINSTA_ACCESSGLOBALATOMS, FALSE, TRUE }, + { L"Create desktop", WINSTA_CREATEDESKTOP, FALSE, TRUE }, + { L"Write attributes", WINSTA_WRITEATTRIBUTES, FALSE, TRUE }, + { L"Exit windows", WINSTA_EXITWINDOWS, FALSE, TRUE } +}; + +ACCESS_ENTRIES(WmiGuid) +{ + { L"Full control", WMIGUID_ALL_ACCESS, TRUE, TRUE }, + { L"Read", WMIGUID_GENERIC_READ, TRUE, FALSE }, + { L"Write", WMIGUID_GENERIC_WRITE, TRUE, FALSE }, + { L"Execute", WMIGUID_GENERIC_EXECUTE, TRUE, FALSE }, + { L"Query information", WMIGUID_QUERY, FALSE, TRUE }, + { L"Set information", WMIGUID_SET, FALSE, TRUE }, + { L"Get notifications", WMIGUID_NOTIFICATION, FALSE, TRUE }, + { L"Read description", WMIGUID_READ_DESCRIPTION, FALSE, TRUE }, + { L"Execute", WMIGUID_EXECUTE, FALSE, TRUE }, + { L"Create real-time logs", TRACELOG_CREATE_REALTIME, FALSE, TRUE, L"Create real-time" }, + { L"Create on disk logs", TRACELOG_CREATE_ONDISK, FALSE, TRUE, L"Create on disk" }, + { L"Enable provider GUIDs", TRACELOG_GUID_ENABLE, FALSE, TRUE, L"Enable GUIDs" }, + { L"Access kernel logger", TRACELOG_ACCESS_KERNEL_LOGGER, FALSE, TRUE }, + { L"Log events", TRACELOG_LOG_EVENT, FALSE, TRUE }, + { L"Access real-time events", TRACELOG_ACCESS_REALTIME, FALSE, TRUE, L"Access real-time" }, + { L"Register provider GUIDs", TRACELOG_REGISTER_GUIDS, FALSE, TRUE, L"Register GUIDs" } +}; + +static PH_SPECIFIC_TYPE PhSpecificTypes[] = +{ + ACCESS_ENTRY(AlpcPort, TRUE), + ACCESS_ENTRY(DebugObject, TRUE), + ACCESS_ENTRY(Desktop, FALSE), + ACCESS_ENTRY(Directory, FALSE), + ACCESS_ENTRY(Event, TRUE), + ACCESS_ENTRY(EventPair, TRUE), + ACCESS_ENTRY(File, TRUE), + ACCESS_ENTRY(FilterConnectionPort, FALSE), + ACCESS_ENTRY(IoCompletion, TRUE), + ACCESS_ENTRY(Job, TRUE), + ACCESS_ENTRY(Key, FALSE), + ACCESS_ENTRY(KeyedEvent, FALSE), + ACCESS_ENTRY(LsaAccount, FALSE), + ACCESS_ENTRY(LsaPolicy, FALSE), + ACCESS_ENTRY(LsaSecret, FALSE), + ACCESS_ENTRY(LsaTrusted, FALSE), + ACCESS_ENTRY(Mutant, TRUE), + ACCESS_ENTRY(Partition, TRUE), + ACCESS_ENTRY(Process, TRUE), + ACCESS_ENTRY(Process60, TRUE), + ACCESS_ENTRY(Profile, FALSE), + ACCESS_ENTRY(SamAlias, FALSE), + ACCESS_ENTRY(SamDomain, FALSE), + ACCESS_ENTRY(SamGroup, FALSE), + ACCESS_ENTRY(SamServer, FALSE), + ACCESS_ENTRY(SamUser, FALSE), + ACCESS_ENTRY(Section, FALSE), + ACCESS_ENTRY(Semaphore, TRUE), + ACCESS_ENTRY(Service, FALSE), + ACCESS_ENTRY(SCManager, FALSE), + ACCESS_ENTRY(Session, FALSE), + ACCESS_ENTRY(SymbolicLink, FALSE), + ACCESS_ENTRY(Thread, TRUE), + ACCESS_ENTRY(Thread60, TRUE), + ACCESS_ENTRY(Timer, TRUE), + ACCESS_ENTRY(TmEn, FALSE), + ACCESS_ENTRY(TmRm, FALSE), + ACCESS_ENTRY(TmTm, FALSE), + ACCESS_ENTRY(TmTx, FALSE), + ACCESS_ENTRY(Token, FALSE), + ACCESS_ENTRY(TokenDefault, FALSE), + ACCESS_ENTRY(TpWorkerFactory, FALSE), + ACCESS_ENTRY(Type, FALSE), + ACCESS_ENTRY(WindowStation, FALSE), + ACCESS_ENTRY(WmiGuid, TRUE) +}; + +/** + * Gets access entries for an object type. + * + * \param Type The name of the object type. + * \param AccessEntries A variable which receives an array of access entry structures. You must free + * the buffer with PhFree() when you no longer need it. + * \param NumberOfAccessEntries A variable which receives the number of access entry structures + * returned in + * \a AccessEntries. + */ +BOOLEAN PhGetAccessEntries( + _In_ PWSTR Type, + _Out_ PPH_ACCESS_ENTRY *AccessEntries, + _Out_ PULONG NumberOfAccessEntries + ) +{ + ULONG i; + PPH_SPECIFIC_TYPE specificType = NULL; + PPH_ACCESS_ENTRY accessEntries; + + if (PhEqualStringZ(Type, L"ALPC Port", TRUE)) + { + Type = L"AlpcPort"; + } + else if (PhEqualStringZ(Type, L"Port", TRUE)) + { + Type = L"AlpcPort"; + } + else if (PhEqualStringZ(Type, L"WaitablePort", TRUE)) + { + Type = L"AlpcPort"; + } + else if (PhEqualStringZ(Type, L"Process", TRUE)) + { + Type = L"Process60"; + } + else if (PhEqualStringZ(Type, L"Thread", TRUE)) + { + Type = L"Thread60"; + } + else if (PhEqualStringZ(Type, L"FileObject", TRUE)) + { + Type = L"File"; + } + + // Find the specific type. + for (i = 0; i < sizeof(PhSpecificTypes) / sizeof(PH_SPECIFIC_TYPE); i++) + { + if (PhEqualStringZ(PhSpecificTypes[i].Type, Type, TRUE)) + { + specificType = &PhSpecificTypes[i]; + break; + } + } + + if (specificType) + { + ULONG sizeOfEntries; + + // Copy the specific access entries and append the standard access entries. + + if (specificType->HasSynchronize) + sizeOfEntries = specificType->SizeOfAccessEntries + sizeof(PhStandardAccessEntries); + else + sizeOfEntries = specificType->SizeOfAccessEntries + sizeof(PhStandardAccessEntries) - sizeof(PH_ACCESS_ENTRY); + + accessEntries = PhAllocate(sizeOfEntries); + memcpy(accessEntries, specificType->AccessEntries, specificType->SizeOfAccessEntries); + + if (specificType->HasSynchronize) + { + memcpy( + PTR_ADD_OFFSET(accessEntries, specificType->SizeOfAccessEntries), + PhStandardAccessEntries, + sizeof(PhStandardAccessEntries) + ); + } + else + { + memcpy( + PTR_ADD_OFFSET(accessEntries, specificType->SizeOfAccessEntries), + &PhStandardAccessEntries[1], + sizeof(PhStandardAccessEntries) - sizeof(PH_ACCESS_ENTRY) + ); + } + + *AccessEntries = accessEntries; + *NumberOfAccessEntries = sizeOfEntries / sizeof(PH_ACCESS_ENTRY); + } + else + { + *AccessEntries = PhAllocateCopy(PhStandardAccessEntries, sizeof(PhStandardAccessEntries)); + *NumberOfAccessEntries = sizeof(PhStandardAccessEntries) / sizeof(PH_ACCESS_ENTRY); + } + + return TRUE; +} + +static int __cdecl PhpAccessEntryCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_ACCESS_ENTRY entry1 = (PPH_ACCESS_ENTRY)elem1; + PPH_ACCESS_ENTRY entry2 = (PPH_ACCESS_ENTRY)elem2; + + return intcmp(PhCountBits(entry2->Access), PhCountBits(entry1->Access)); +} + +/** + * Creates a string representation of an access mask. + * + * \param Access The access mask. + * \param AccessEntries An array of access entry structures. You can call PhGetAccessEntries() to + * retrieve the access entry structures for a standard object type. + * \param NumberOfAccessEntries The number of elements in \a AccessEntries. + * + * \return The string representation of \a Access. + */ +PPH_STRING PhGetAccessString( + _In_ ACCESS_MASK Access, + _In_ PPH_ACCESS_ENTRY AccessEntries, + _In_ ULONG NumberOfAccessEntries + ) +{ + PH_STRING_BUILDER stringBuilder; + PPH_ACCESS_ENTRY accessEntries; + PBOOLEAN matched; + ULONG i; + ULONG j; + + PhInitializeStringBuilder(&stringBuilder, 32); + + // Sort the access entries according to how many access rights they include. + accessEntries = PhAllocateCopy(AccessEntries, NumberOfAccessEntries * sizeof(PH_ACCESS_ENTRY)); + qsort(accessEntries, NumberOfAccessEntries, sizeof(PH_ACCESS_ENTRY), PhpAccessEntryCompare); + + matched = PhAllocate(NumberOfAccessEntries * sizeof(BOOLEAN)); + memset(matched, 0, NumberOfAccessEntries * sizeof(BOOLEAN)); + + for (i = 0; i < NumberOfAccessEntries; i++) + { + // We make sure we haven't matched this access entry yet. This ensures that we won't get + // duplicates, e.g. FILE_GENERIC_READ includes FILE_READ_DATA, and we don't want to display + // both to the user. + if ( + !matched[i] && + ((Access & accessEntries[i].Access) == accessEntries[i].Access) + ) + { + if (accessEntries[i].ShortName) + PhAppendStringBuilder2(&stringBuilder, accessEntries[i].ShortName); + else + PhAppendStringBuilder2(&stringBuilder, accessEntries[i].Name); + + PhAppendStringBuilder2(&stringBuilder, L", "); + + // Disable equal or more specific entries. + for (j = i; j < NumberOfAccessEntries; j++) + { + if ((accessEntries[i].Access | accessEntries[j].Access) == accessEntries[i].Access) + matched[j] = TRUE; + } + } + } + + // Remove the trailing ", ". + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhFree(matched); + PhFree(accessEntries); + + return PhFinalStringBuilderString(&stringBuilder); +} diff --git a/phlib/secedit.c b/phlib/secedit.c index d1641425a106..18c8e1ac9f54 100644 --- a/phlib/secedit.c +++ b/phlib/secedit.c @@ -1,577 +1,1402 @@ -/* - * Process Hacker - - * object security editor - * - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include -#include - -#include - -static ISecurityInformationVtbl PhSecurityInformation_VTable = -{ - PhSecurityInformation_QueryInterface, - PhSecurityInformation_AddRef, - PhSecurityInformation_Release, - PhSecurityInformation_GetObjectInformation, - PhSecurityInformation_GetSecurity, - PhSecurityInformation_SetSecurity, - PhSecurityInformation_GetAccessRights, - PhSecurityInformation_MapGeneric, - PhSecurityInformation_GetInheritTypes, - PhSecurityInformation_PropertySheetPageCallback -}; - -/** - * Creates a security editor page. - * - * \param ObjectName The name of the object. - * \param GetObjectSecurity A callback function executed to retrieve the security descriptor of the - * object. - * \param SetObjectSecurity A callback function executed to modify the security descriptor of the - * object. - * \param Context A user-defined value to pass to the callback functions. - * \param AccessEntries An array of access mask descriptors. - * \param NumberOfAccessEntries The number of elements in \a AccessEntries. - */ -HPROPSHEETPAGE PhCreateSecurityPage( - _In_ PWSTR ObjectName, - _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, - _In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity, - _In_opt_ PVOID Context, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries - ) -{ - ISecurityInformation *info; - HPROPSHEETPAGE page; - - info = PhSecurityInformation_Create( - ObjectName, - GetObjectSecurity, - SetObjectSecurity, - Context, - AccessEntries, - NumberOfAccessEntries, - TRUE - ); - - page = CreateSecurityPage(info); - - PhSecurityInformation_Release(info); - - return page; -} - -/** - * Displays a security editor dialog. - * - * \param hWnd The parent window of the dialog. - * \param ObjectName The name of the object. - * \param GetObjectSecurity A callback function executed to retrieve the security descriptor of the - * object. - * \param SetObjectSecurity A callback function executed to modify the security descriptor of the - * object. - * \param Context A user-defined value to pass to the callback functions. - * \param AccessEntries An array of access mask descriptors. - * \param NumberOfAccessEntries The number of elements in \a AccessEntries. - */ -VOID PhEditSecurity( - _In_ HWND hWnd, - _In_ PWSTR ObjectName, - _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, - _In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity, - _In_opt_ PVOID Context, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries - ) -{ - ISecurityInformation *info; - - info = PhSecurityInformation_Create( - ObjectName, - GetObjectSecurity, - SetObjectSecurity, - Context, - AccessEntries, - NumberOfAccessEntries, - FALSE - ); - - EditSecurity(hWnd, info); - - PhSecurityInformation_Release(info); -} - -ISecurityInformation *PhSecurityInformation_Create( - _In_ PWSTR ObjectName, - _In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity, - _In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity, - _In_opt_ PVOID Context, - _In_ PPH_ACCESS_ENTRY AccessEntries, - _In_ ULONG NumberOfAccessEntries, - _In_ BOOLEAN IsPage - ) -{ - PhSecurityInformation *info; - ULONG i; - - info = PhAllocate(sizeof(PhSecurityInformation)); - info->VTable = &PhSecurityInformation_VTable; - info->RefCount = 1; - - info->ObjectName = PhCreateString(ObjectName); - info->GetObjectSecurity = GetObjectSecurity; - info->SetObjectSecurity = SetObjectSecurity; - info->Context = Context; - info->AccessEntries = PhAllocate(sizeof(SI_ACCESS) * NumberOfAccessEntries); - info->NumberOfAccessEntries = NumberOfAccessEntries; - info->IsPage = IsPage; - - for (i = 0; i < NumberOfAccessEntries; i++) - { - memset(&info->AccessEntries[i], 0, sizeof(SI_ACCESS)); - info->AccessEntries[i].pszName = AccessEntries[i].Name; - info->AccessEntries[i].mask = AccessEntries[i].Access; - - if (AccessEntries[i].General) - info->AccessEntries[i].dwFlags |= SI_ACCESS_GENERAL; - if (AccessEntries[i].Specific) - info->AccessEntries[i].dwFlags |= SI_ACCESS_SPECIFIC; - } - - return (ISecurityInformation *)info; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( - _In_ ISecurityInformation *This, - _In_ REFIID Riid, - _Out_ PVOID *Object - ) -{ - if ( - IsEqualIID(Riid, &IID_IUnknown) || - IsEqualIID(Riid, &IID_ISecurityInformation) - ) - { - PhSecurityInformation_AddRef(This); - *Object = This; - return S_OK; - } - - *Object = NULL; - return E_NOINTERFACE; -} - -ULONG STDMETHODCALLTYPE PhSecurityInformation_AddRef( - _In_ ISecurityInformation *This - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - - this->RefCount++; - - return this->RefCount; -} - -ULONG STDMETHODCALLTYPE PhSecurityInformation_Release( - _In_ ISecurityInformation *This - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - - this->RefCount--; - - if (this->RefCount == 0) - { - if (this->ObjectName) PhDereferenceObject(this->ObjectName); - PhFree(this->AccessEntries); - - PhFree(this); - - return 0; - } - - return this->RefCount; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetObjectInformation( - _In_ ISecurityInformation *This, - _Out_ PSI_OBJECT_INFO ObjectInfo - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - - memset(ObjectInfo, 0, sizeof(SI_OBJECT_INFO)); - ObjectInfo->dwFlags = - SI_EDIT_AUDITS | - SI_EDIT_OWNER | - SI_EDIT_PERMS | - SI_ADVANCED | - SI_NO_ACL_PROTECT | - SI_NO_TREE_APPLY; - ObjectInfo->hInstance = NULL; - ObjectInfo->pszObjectName = this->ObjectName->Buffer; - - return S_OK; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetSecurity( - _In_ ISecurityInformation *This, - _In_ SECURITY_INFORMATION RequestedInformation, - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, - _In_ BOOL Default - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - NTSTATUS status; - PSECURITY_DESCRIPTOR securityDescriptor; - ULONG sdLength; - PSECURITY_DESCRIPTOR newSd; - - status = this->GetObjectSecurity( - &securityDescriptor, - RequestedInformation, - this->Context - ); - - if (!NT_SUCCESS(status)) - return HRESULT_FROM_WIN32(PhNtStatusToDosError(status)); - - sdLength = RtlLengthSecurityDescriptor(securityDescriptor); - newSd = LocalAlloc(0, sdLength); - memcpy(newSd, securityDescriptor, sdLength); - PhFree(securityDescriptor); - - *SecurityDescriptor = newSd; - - return S_OK; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_SetSecurity( - _In_ ISecurityInformation *This, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - NTSTATUS status; - - status = this->SetObjectSecurity( - SecurityDescriptor, - SecurityInformation, - this->Context - ); - - if (!NT_SUCCESS(status)) - return HRESULT_FROM_WIN32(PhNtStatusToDosError(status)); - - return S_OK; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetAccessRights( - _In_ ISecurityInformation *This, - _In_ const GUID *ObjectType, - _In_ ULONG Flags, - _Out_ PSI_ACCESS *Access, - _Out_ PULONG Accesses, - _Out_ PULONG DefaultAccess - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - - *Access = this->AccessEntries; - *Accesses = this->NumberOfAccessEntries; - *DefaultAccess = 0; - - return S_OK; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_MapGeneric( - _In_ ISecurityInformation *This, - _In_ const GUID *ObjectType, - _In_ PUCHAR AceFlags, - _Inout_ PACCESS_MASK Mask - ) -{ - return S_OK; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetInheritTypes( - _In_ ISecurityInformation *This, - _Out_ PSI_INHERIT_TYPE *InheritTypes, - _Out_ PULONG InheritTypesCount - ) -{ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback( - _In_ ISecurityInformation *This, - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ SI_PAGE_TYPE uPage - ) -{ - PhSecurityInformation *this = (PhSecurityInformation *)This; - - if (uMsg == PSPCB_SI_INITDIALOG && !this->IsPage) - { - // Center the security editor window. - PhCenterWindow(GetParent(hwnd), GetParent(GetParent(hwnd))); - } - - return E_NOTIMPL; -} - -NTSTATUS PhpGetObjectSecurityWithTimeout( - _In_ HANDLE Handle, - _In_ SECURITY_INFORMATION SecurityInformation, - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor - ) -{ - NTSTATUS status; - ULONG bufferSize; - PVOID buffer; - - bufferSize = 0x100; - buffer = PhAllocate(bufferSize); - // This is required (especially for File objects) because some drivers don't seem to handle - // QuerySecurity properly. - memset(buffer, 0, bufferSize); - - status = PhCallNtQuerySecurityObjectWithTimeout( - Handle, - SecurityInformation, - buffer, - bufferSize, - &bufferSize - ); - - if (status == STATUS_BUFFER_TOO_SMALL) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - memset(buffer, 0, bufferSize); - - status = PhCallNtQuerySecurityObjectWithTimeout( - Handle, - SecurityInformation, - buffer, - bufferSize, - &bufferSize - ); - } - - if (!NT_SUCCESS(status)) - { - PhFree(buffer); - return status; - } - - *SecurityDescriptor = (PSECURITY_DESCRIPTOR)buffer; - - return status; -} - -/** - * Retrieves the security descriptor of an object. - * - * \param SecurityDescriptor A variable which receives a pointer to the security descriptor of the - * object. The security descriptor must be freed using PhFree() when no longer needed. - * \param SecurityInformation The security information to retrieve. - * \param Context A pointer to a PH_STD_OBJECT_SECURITY structure describing the object. - * - * \remarks This function may be used for the \a GetObjectSecurity callback in - * PhCreateSecurityPage() or PhEditSecurity(). - */ -_Callback_ NTSTATUS PhStdGetObjectSecurity( - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PPH_STD_OBJECT_SECURITY stdObjectSecurity; - HANDLE handle; - - stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context; - - status = stdObjectSecurity->OpenObject( - &handle, - PhGetAccessForGetSecurity(SecurityInformation), - stdObjectSecurity->Context - ); - - if (!NT_SUCCESS(status)) - return status; - - if (PhEqualStringZ(stdObjectSecurity->ObjectType, L"Service", TRUE)) - { - status = PhGetSeObjectSecurity(handle, SE_SERVICE, SecurityInformation, SecurityDescriptor); - CloseServiceHandle(handle); - } - else if (PhEqualStringZ(stdObjectSecurity->ObjectType, L"File", TRUE)) - { - status = PhpGetObjectSecurityWithTimeout(handle, SecurityInformation, SecurityDescriptor); - NtClose(handle); - } - else - { - status = PhGetObjectSecurity(handle, SecurityInformation, SecurityDescriptor); - NtClose(handle); - } - - return status; -} - -/** - * Modifies the security descriptor of an object. - * - * \param SecurityDescriptor A security descriptor containing security information to set. - * \param SecurityInformation The security information to retrieve. - * \param Context A pointer to a PH_STD_OBJECT_SECURITY structure describing the object. - * - * \remarks This function may be used for the \a SetObjectSecurity callback in - * PhCreateSecurityPage() or PhEditSecurity(). - */ -_Callback_ NTSTATUS PhStdSetObjectSecurity( - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status; - PPH_STD_OBJECT_SECURITY stdObjectSecurity; - HANDLE handle; - - stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context; - - status = stdObjectSecurity->OpenObject( - &handle, - PhGetAccessForSetSecurity(SecurityInformation), - stdObjectSecurity->Context - ); - - if (!NT_SUCCESS(status)) - return status; - - if (PhEqualStringZ(stdObjectSecurity->ObjectType, L"Service", TRUE)) - { - status = PhSetSeObjectSecurity(handle, SE_SERVICE, SecurityInformation, SecurityDescriptor); - CloseServiceHandle(handle); - } - else - { - status = PhSetObjectSecurity(handle, SecurityInformation, SecurityDescriptor); - NtClose(handle); - } - - return status; -} - -NTSTATUS PhGetSeObjectSecurity( - _In_ HANDLE Handle, - _In_ ULONG ObjectType, - _In_ SECURITY_INFORMATION SecurityInformation, - _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor - ) -{ - ULONG win32Result; - PSECURITY_DESCRIPTOR securityDescriptor; - - win32Result = GetSecurityInfo( - Handle, - ObjectType, - SecurityInformation, - NULL, - NULL, - NULL, - NULL, - &securityDescriptor - ); - - if (win32Result != ERROR_SUCCESS) - return NTSTATUS_FROM_WIN32(win32Result); - - *SecurityDescriptor = PhAllocateCopy(securityDescriptor, RtlLengthSecurityDescriptor(securityDescriptor)); - LocalFree(securityDescriptor); - - return STATUS_SUCCESS; -} - -NTSTATUS PhSetSeObjectSecurity( - _In_ HANDLE Handle, - _In_ ULONG ObjectType, - _In_ SECURITY_INFORMATION SecurityInformation, - _In_ PSECURITY_DESCRIPTOR SecurityDescriptor - ) -{ - ULONG win32Result; - SECURITY_INFORMATION securityInformation = 0; - BOOLEAN present; - BOOLEAN defaulted; - PSID owner = NULL; - PSID group = NULL; - PACL dacl = NULL; - PACL sacl = NULL; - - if (SecurityInformation & OWNER_SECURITY_INFORMATION) - { - if (NT_SUCCESS(RtlGetOwnerSecurityDescriptor(SecurityDescriptor, &owner, &defaulted))) - securityInformation |= OWNER_SECURITY_INFORMATION; - } - - if (SecurityInformation & GROUP_SECURITY_INFORMATION) - { - if (NT_SUCCESS(RtlGetGroupSecurityDescriptor(SecurityDescriptor, &group, &defaulted))) - securityInformation |= GROUP_SECURITY_INFORMATION; - } - - if (SecurityInformation & DACL_SECURITY_INFORMATION) - { - if (NT_SUCCESS(RtlGetDaclSecurityDescriptor(SecurityDescriptor, &present, &dacl, &defaulted)) && present) - securityInformation |= DACL_SECURITY_INFORMATION; - } - - if (SecurityInformation & SACL_SECURITY_INFORMATION) - { - if (NT_SUCCESS(RtlGetSaclSecurityDescriptor(SecurityDescriptor, &present, &sacl, &defaulted)) && present) - securityInformation |= SACL_SECURITY_INFORMATION; - } - - win32Result = SetSecurityInfo( - Handle, - ObjectType, - SecurityInformation, - owner, - group, - dacl, - sacl - ); - - if (win32Result != ERROR_SUCCESS) - return NTSTATUS_FROM_WIN32(win32Result); - - return STATUS_SUCCESS; -} +/* + * Process Hacker - + * object security editor + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static ISecurityInformationVtbl PhSecurityInformation_VTable = +{ + PhSecurityInformation_QueryInterface, + PhSecurityInformation_AddRef, + PhSecurityInformation_Release, + PhSecurityInformation_GetObjectInformation, + PhSecurityInformation_GetSecurity, + PhSecurityInformation_SetSecurity, + PhSecurityInformation_GetAccessRights, + PhSecurityInformation_MapGeneric, + PhSecurityInformation_GetInheritTypes, + PhSecurityInformation_PropertySheetPageCallback +}; + +static ISecurityInformation2Vtbl PhSecurityInformation_VTable2 = +{ + PhSecurityInformation2_QueryInterface, + PhSecurityInformation2_AddRef, + PhSecurityInformation2_Release, + PhSecurityInformation2_IsDaclCanonical, + PhSecurityInformation2_LookupSids +}; + +static ISecurityInformation3Vtbl PhSecurityInformation_VTable3 = +{ + PhSecurityInformation3_QueryInterface, + PhSecurityInformation3_AddRef, + PhSecurityInformation3_Release, + PhSecurityInformation3_GetFullResourceName, + PhSecurityInformation3_OpenElevatedEditor +}; + +static IDataObjectVtbl PhDataObject_VTable = +{ + PhSecurityDataObject_QueryInterface, + PhSecurityDataObject_AddRef, + PhSecurityDataObject_Release, + PhSecurityDataObject_GetData, + PhSecurityDataObject_GetDataHere, + PhSecurityDataObject_QueryGetData, + PhSecurityDataObject_GetCanonicalFormatEtc, + PhSecurityDataObject_SetData, + PhSecurityDataObject_EnumFormatEtc, + PhSecurityDataObject_DAdvise, + PhSecurityDataObject_DUnadvise, + PhSecurityDataObject_EnumDAdvise +}; + +static ISecurityObjectTypeInfoExVtbl PhSecurityObjectTypeInfo_VTable3 = +{ + PhSecurityObjectTypeInfo_QueryInterface, + PhSecurityObjectTypeInfo_AddRef, + PhSecurityObjectTypeInfo_Release, + PhSecurityObjectTypeInfo_GetInheritSource +}; + +/** + * Creates a security editor page. + * + * \param ObjectName The name of the object. + * \param ObjectType The type name of the object. + * \param Context A user-defined value to pass to the callback functions. + */ +HPROPSHEETPAGE PhCreateSecurityPage( + _In_ PWSTR ObjectName, + _In_ PWSTR ObjectType, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PPH_CLOSE_OBJECT CloseObject, + _In_opt_ PVOID Context + ) +{ + ISecurityInformation *info; + HPROPSHEETPAGE page; + + info = PhSecurityInformation_Create( + NULL, + ObjectName, + ObjectType, + OpenObject, + CloseObject, + Context, + TRUE + ); + + page = CreateSecurityPage(info); + + PhSecurityInformation_Release(info); + + return page; +} + +static NTSTATUS PhpEditSecurityInformationThread( + _In_opt_ PVOID Context + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)Context; + + // The EditSecurityAdvanced function on Windows 7 doesn't handle the SI_PAGE_TYPE + // parameter correctly and also doesn't show the Audit and Owner tabs... (dmex) + if (WindowsVersion >= WINDOWS_8 && PhGetIntegerSetting(L"EnableSecurityAdvancedDialog")) + EditSecurityAdvanced(this->WindowHandle, Context, COMBINE_PAGE_ACTIVATION(SI_PAGE_PERM, SI_SHOW_PERM_ACTIVATED)); + else + EditSecurity(this->WindowHandle, Context); + + PhSecurityInformation_Release(Context); + + return STATUS_SUCCESS; +} + +/** + * Displays a security editor dialog. + * + * \param hWnd The parent window of the dialog. + * \param ObjectName The name of the object. + * \param Context A user-defined value to pass to the callback functions. + */ +VOID PhEditSecurity( + _In_opt_ HWND WindowHandle, + _In_ PWSTR ObjectName, + _In_ PWSTR ObjectType, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PPH_CLOSE_OBJECT CloseObject, + _In_opt_ PVOID Context + ) +{ + ISecurityInformation *info; + + info = PhSecurityInformation_Create( + WindowHandle, + ObjectName, + ObjectType, + OpenObject, + CloseObject, + Context, + FALSE + ); + + PhCreateThread2(PhpEditSecurityInformationThread, info); +} + +ISecurityInformation *PhSecurityInformation_Create( + _In_opt_ HWND WindowHandle, + _In_ PWSTR ObjectName, + _In_ PWSTR ObjectType, + _In_ PPH_OPEN_OBJECT OpenObject, + _In_opt_ PPH_CLOSE_OBJECT CloseObject, + _In_opt_ PVOID Context, + _In_ BOOLEAN IsPage + ) +{ + PhSecurityInformation *info; + ULONG i; + + info = PhAllocateZero(sizeof(PhSecurityInformation)); + info->VTable = &PhSecurityInformation_VTable; + info->RefCount = 1; + + info->WindowHandle = WindowHandle; + info->ObjectName = PhCreateString(ObjectName); + info->ObjectType = PhCreateString(ObjectType); + info->OpenObject = OpenObject; + info->CloseObject = CloseObject; + info->Context = Context; + info->IsPage = IsPage; + + if (PhGetAccessEntries(ObjectType, &info->AccessEntriesArray, &info->NumberOfAccessEntries)) + { + info->AccessEntries = PhAllocateZero(sizeof(SI_ACCESS) * info->NumberOfAccessEntries); + + for (i = 0; i < info->NumberOfAccessEntries; i++) + { + info->AccessEntries[i].pszName = info->AccessEntriesArray[i].Name; + info->AccessEntries[i].mask = info->AccessEntriesArray[i].Access; + + if (info->AccessEntriesArray[i].General) + info->AccessEntries[i].dwFlags |= SI_ACCESS_GENERAL; + if (info->AccessEntriesArray[i].Specific) + info->AccessEntries[i].dwFlags |= SI_ACCESS_SPECIFIC; + + if (PhEqualString2(info->ObjectType, L"FileObject", TRUE)) // TODO: Remove PhEqualString2 (dmex) + info->AccessEntries[i].dwFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + } + } + + return (ISecurityInformation *)info; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface( + _In_ ISecurityInformation *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + + if ( + IsEqualIID(Riid, &IID_IUnknown) || + IsEqualIID(Riid, &IID_ISecurityInformation) + ) + { + PhSecurityInformation_AddRef(This); + *Object = This; + return S_OK; + } + else if (IsEqualGUID(Riid, &IID_ISecurityInformation2)) + { + if (WindowsVersion >= WINDOWS_8) + { + PhSecurityInformation2 *info; + + info = PhAllocateZero(sizeof(PhSecurityInformation2)); + info->VTable = &PhSecurityInformation_VTable2; + info->Context = this; + info->RefCount = 1; + + *Object = info; + return S_OK; + } + } + else if (IsEqualGUID(Riid, &IID_ISecurityInformation3)) + { + if (WindowsVersion >= WINDOWS_8) + { + PhSecurityInformation3 *info; + + info = PhAllocateZero(sizeof(PhSecurityInformation3)); + info->VTable = &PhSecurityInformation_VTable3; + info->Context = this; + info->RefCount = 1; + + *Object = info; + return S_OK; + } + } + else if (IsEqualGUID(Riid, &IID_ISecurityObjectTypeInfo)) + { + if (WindowsVersion >= WINDOWS_8) + { + PhSecurityObjectTypeInfo* info; + + info = PhAllocateZero(sizeof(PhSecurityObjectTypeInfo)); + info->VTable = &PhSecurityObjectTypeInfo_VTable3; + info->Context = this; + info->RefCount = 1; + + *Object = info; + return S_OK; + } + } + + *Object = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE PhSecurityInformation_AddRef( + _In_ ISecurityInformation *This + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + + this->RefCount++; + + return this->RefCount; +} + +ULONG STDMETHODCALLTYPE PhSecurityInformation_Release( + _In_ ISecurityInformation *This + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + + this->RefCount--; + + if (this->RefCount == 0) + { + if (this->CloseObject) + this->CloseObject(this->Context); + + if (this->ObjectName) + PhDereferenceObject(this->ObjectName); + if (this->ObjectType) + PhDereferenceObject(this->ObjectType); + if (this->AccessEntries) + PhFree(this->AccessEntries); + if (this->AccessEntriesArray) + PhFree(this->AccessEntriesArray); + + PhFree(this); + + return 0; + } + + return this->RefCount; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetObjectInformation( + _In_ ISecurityInformation *This, + _Out_ PSI_OBJECT_INFO ObjectInfo + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + + memset(ObjectInfo, 0, sizeof(SI_OBJECT_INFO)); + ObjectInfo->dwFlags = SI_EDIT_ALL | SI_ADVANCED | (WindowsVersion >= WINDOWS_8 ? SI_VIEW_ONLY : 0); + ObjectInfo->pszObjectName = PhGetString(this->ObjectName); + + if (PhEqualString2(this->ObjectType, L"FileObject", TRUE)) + { + ObjectInfo->dwFlags |= SI_ENABLE_EDIT_ATTRIBUTE_CONDITION | SI_MAY_WRITE; // SI_RESET | SI_READONLY + //if (Folder) ObjectInfo->dwFlags |= SI_CONTAINER; + } + if (PhEqualString2(this->ObjectType, L"TokenDefault", TRUE)) + { + ObjectInfo->dwFlags &= ~(SI_EDIT_OWNER | SI_EDIT_AUDITS); + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetSecurity( + _In_ ISecurityInformation *This, + _In_ SECURITY_INFORMATION RequestedInformation, + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, + _In_ BOOL Default + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + NTSTATUS status; + PSECURITY_DESCRIPTOR securityDescriptor; + ULONG sdLength; + PSECURITY_DESCRIPTOR newSd; + + //if (Default) + //{ + // securityDescriptor = PhAllocateZero(SECURITY_DESCRIPTOR_MIN_LENGTH); + // + // status = RtlCreateSecurityDescriptor( + // securityDescriptor, + // SECURITY_DESCRIPTOR_REVISION + // ); + // + // if (!NT_SUCCESS(status)) + // return HRESULT_FROM_WIN32(PhNtStatusToDosError(status)); + // + // status = RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, NULL, FALSE); + // + // if (!NT_SUCCESS(status)) + // return HRESULT_FROM_WIN32(PhNtStatusToDosError(status)); + //} + //else + { + status = PhStdGetObjectSecurity( + &securityDescriptor, + RequestedInformation, + this + ); + + if (!NT_SUCCESS(status)) + return HRESULT_FROM_WIN32(PhNtStatusToDosError(status)); + } + + sdLength = RtlLengthSecurityDescriptor(securityDescriptor); + newSd = LocalAlloc(0, sdLength); + memcpy(newSd, securityDescriptor, sdLength); + PhFree(securityDescriptor); + + *SecurityDescriptor = newSd; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_SetSecurity( + _In_ ISecurityInformation *This, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + NTSTATUS status; + + status = PhStdSetObjectSecurity( + SecurityDescriptor, + SecurityInformation, + this + ); + + if (!NT_SUCCESS(status)) + return HRESULT_FROM_WIN32(PhNtStatusToDosError(status)); + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetAccessRights( + _In_ ISecurityInformation *This, + _In_ const GUID *ObjectType, + _In_ ULONG Flags, + _Out_ PSI_ACCESS *Access, + _Out_ PULONG Accesses, + _Out_ PULONG DefaultAccess + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + + *Access = this->AccessEntries; + *Accesses = this->NumberOfAccessEntries; + *DefaultAccess = 0; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_MapGeneric( + _In_ ISecurityInformation *This, + _In_ const GUID *ObjectType, + _In_ PUCHAR AceFlags, + _Inout_ PACCESS_MASK Mask + ) +{ + PhSecurityInformation* this = (PhSecurityInformation*)This; + + if (PhEqualString2(this->ObjectType, L"FileObject", TRUE)) + { + static GENERIC_MAPPING genericMappings = + { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE, + FILE_ALL_ACCESS + }; + + RtlMapGenericMask(Mask, &genericMappings); + } + + // TODO we're supposed to lookup the GenericMapping for the object type. (dmex) + + //POBJECT_TYPES_INFORMATION objectTypes; + //POBJECT_TYPE_INFORMATION objectType; + + //if (NT_SUCCESS(PhEnumObjectTypes(&objectTypes))) + //{ + // objectType = PH_FIRST_OBJECT_TYPE(objectTypes); + // + // for (ULONG i = 0; i < objectTypes->NumberOfTypes; i++) + // { + // RtlMapGenericMask(Mask, &objectType->GenericMapping); + // } + // + // PhFree(objectTypes); + //} + + // TODO + // NtQuerySystemInformation(SystemObjectInformation); + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetInheritTypes( + _In_ ISecurityInformation *This, + _Out_ PSI_INHERIT_TYPE *InheritTypes, + _Out_ PULONG InheritTypesCount + ) +{ + static SI_INHERIT_TYPE inheritTypes[] = + { + 0, 0, L"This folder only", + 0, CONTAINER_INHERIT_ACE, L"This folder, subfolders and files", + 0, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE, L"Subfolders and files only", + }; + + PhSecurityInformation* this = (PhSecurityInformation*)This; + + // if (Folder-Container) + *InheritTypes = inheritTypes; + *InheritTypesCount = RTL_NUMBER_OF(inheritTypes); + return S_OK; + // else + //return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback( + _In_ ISecurityInformation *This, + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ SI_PAGE_TYPE uPage + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)This; + + if (uMsg == PSPCB_SI_INITDIALOG) + { + // Center the security editor window. + if (!this->IsPage) + PhCenterWindow(GetParent(hwnd), GetParent(GetParent(hwnd))); + + PhInitializeWindowTheme(hwnd, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + + return E_NOTIMPL; +} + +// ISecurityInformation2 + +HRESULT STDMETHODCALLTYPE PhSecurityInformation2_QueryInterface( + _In_ ISecurityInformation2 *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ) +{ + if ( + IsEqualIID(Riid, &IID_IUnknown) || + IsEqualIID(Riid, &IID_ISecurityInformation2) + ) + { + PhSecurityInformation2_AddRef(This); + *Object = This; + return S_OK; + } + + *Object = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE PhSecurityInformation2_AddRef( + _In_ ISecurityInformation2 *This + ) +{ + PhSecurityInformation2 *this = (PhSecurityInformation2 *)This; + + this->RefCount++; + + return this->RefCount; +} + +ULONG STDMETHODCALLTYPE PhSecurityInformation2_Release( + _In_ ISecurityInformation2 *This + ) +{ + PhSecurityInformation2 *this = (PhSecurityInformation2 *)This; + + this->RefCount--; + + if (this->RefCount == 0) + { + PhFree(this); + return 0; + } + + return this->RefCount; +} + +BOOL STDMETHODCALLTYPE PhSecurityInformation2_IsDaclCanonical( + _In_ ISecurityInformation2 *This, + _In_ PACL pDacl + ) +{ + return TRUE; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation2_LookupSids( + _In_ ISecurityInformation2 *This, + _In_ ULONG cSids, + _In_ PSID *rgpSids, + _Out_ LPDATAOBJECT *ppdo + ) +{ + PhSecurityInformation2 *this = (PhSecurityInformation2 *)This; + PhSecurityIDataObject *dataObject; + + dataObject = PhAllocateZero(sizeof(PhSecurityInformation)); + dataObject->VTable = &PhDataObject_VTable; + dataObject->Context = this->Context; + dataObject->RefCount = 1; + + dataObject->SidCount = cSids; + dataObject->Sids = rgpSids; + dataObject->NameCache = PhCreateList(1); + + *ppdo = (LPDATAOBJECT)dataObject; + + return S_OK; +} + +// ISecurityInformation3 + +HRESULT STDMETHODCALLTYPE PhSecurityInformation3_QueryInterface( + _In_ ISecurityInformation3 *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ) +{ + if ( + IsEqualIID(Riid, &IID_IUnknown) || + IsEqualIID(Riid, &IID_ISecurityInformation3) + ) + { + PhSecurityInformation3_AddRef(This); + *Object = This; + return S_OK; + } + + *Object = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE PhSecurityInformation3_AddRef( + _In_ ISecurityInformation3 *This + ) +{ + PhSecurityInformation3 *this = (PhSecurityInformation3 *)This; + + this->RefCount++; + + return this->RefCount; +} + +ULONG STDMETHODCALLTYPE PhSecurityInformation3_Release( + _In_ ISecurityInformation3 *This + ) +{ + PhSecurityInformation3 *this = (PhSecurityInformation3 *)This; + + this->RefCount--; + + if (this->RefCount == 0) + { + PhFree(this); + return 0; + } + + return this->RefCount; +} + +BOOL STDMETHODCALLTYPE PhSecurityInformation3_GetFullResourceName( + _In_ ISecurityInformation3 *This, + _Outptr_ PWSTR *ppszResourceName + ) +{ + PhSecurityInformation3 *this = (PhSecurityInformation3 *)This; + + if (PhIsNullOrEmptyString(this->Context->ObjectName)) + *ppszResourceName = PhGetString(this->Context->ObjectType); + else + *ppszResourceName = PhGetString(this->Context->ObjectName); + + return TRUE; +} + +HRESULT STDMETHODCALLTYPE PhSecurityInformation3_OpenElevatedEditor( + _In_ ISecurityInformation3 *This, + _In_ HWND hWnd, + _In_ SI_PAGE_TYPE uPage + ) +{ + PhSecurityInformation3 *this = (PhSecurityInformation3 *)This; + + return E_NOTIMPL; +} + +// IDataObject + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_QueryInterface( + _In_ IDataObject *This, + _In_ REFIID Riid, + _COM_Outptr_ PVOID *Object + ) +{ + if ( + IsEqualIID(Riid, &IID_IUnknown) || + IsEqualIID(Riid, &IID_IDataObject) + ) + { + PhSecurityDataObject_AddRef(This); + *Object = This; + return S_OK; + } + + *Object = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE PhSecurityDataObject_AddRef( + _In_ IDataObject *This + ) +{ + PhSecurityIDataObject *this = (PhSecurityIDataObject *)This; + + this->RefCount++; + + return this->RefCount; +} + +ULONG STDMETHODCALLTYPE PhSecurityDataObject_Release( + _In_ IDataObject *This + ) +{ + PhSecurityIDataObject *this = (PhSecurityIDataObject *)This; + + this->RefCount--; + + if (this->RefCount == 0) + { + PhDereferenceObjects(this->NameCache->Items, this->NameCache->Count); + PhDereferenceObject(this->NameCache); + + PhFree(this); + return 0; + } + + return this->RefCount; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetData( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetcIn, + _Out_ STGMEDIUM *pmedium + ) +{ + PhSecurityIDataObject *this = (PhSecurityIDataObject *)This; + PSID_INFO_LIST sidInfoList; + ULONG i; + + sidInfoList = (PSID_INFO_LIST)GlobalAlloc(GMEM_ZEROINIT, sizeof(SID_INFO_LIST) + (sizeof(SID_INFO) * this->SidCount)); + sidInfoList->cItems = this->SidCount; + + for (i = 0; i < this->SidCount; i++) + { + SID_INFO sidInfo; + PPH_STRING sidString; + SID_NAME_USE sidNameUse; + + memset(&sidInfo, 0, sizeof(SID_INFO)); + sidInfo.pSid = this->Sids[i]; + + if (sidString = PhGetSidFullName(sidInfo.pSid, FALSE, &sidNameUse)) + { + switch (sidNameUse) + { + case SidTypeUser: + case SidTypeLogonSession: + sidInfo.pwzClass = L"User"; + break; + case SidTypeAlias: + case SidTypeGroup: + sidInfo.pwzClass = L"Group"; + break; + case SidTypeComputer: + sidInfo.pwzClass = L"Computer"; + break; + } + + sidInfo.pwzCommonName = PhGetString(sidString); + PhAddItemList(this->NameCache, sidString); + } + else if (sidString = PhGetAppContainerPackageName(sidInfo.pSid)) + { + PhMoveReference(&sidString, PhFormatString(L"%s (APP_PACKAGE)", PhGetString(sidString))); + sidInfo.pwzCommonName = PhGetString(sidString); + PhAddItemList(this->NameCache, sidString); + } + else if (sidString = PhGetAppContainerName(sidInfo.pSid)) + { + PhMoveReference(&sidString, PhFormatString(L"%s (APP_CONTAINER)", PhGetString(sidString))); + sidInfo.pwzCommonName = PhGetString(sidString); + PhAddItemList(this->NameCache, sidString); + } + else if (sidString = PhGetCapabilitySidName(sidInfo.pSid)) + { + PhMoveReference(&sidString, PhFormatString(L"%s (APP_CAPABILITY)", PhGetString(sidString))); + sidInfo.pwzCommonName = PhGetString(sidString); + PhAddItemList(this->NameCache, sidString); + } + + sidInfoList->aSidInfo[i] = sidInfo; + } + + pmedium->tymed = TYMED_HGLOBAL; + pmedium->hGlobal = (HGLOBAL)sidInfoList; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetDataHere( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetc, + _Inout_ STGMEDIUM *pmedium + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_QueryGetData( + _In_ IDataObject *This, + _In_opt_ FORMATETC *pformatetc + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_GetCanonicalFormatEtc( + _In_ IDataObject * This, + _In_opt_ FORMATETC *pformatectIn, + _Out_ FORMATETC *pformatetcOut + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_SetData( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetc, + _In_ STGMEDIUM *pmedium, + _In_ BOOL fRelease + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_EnumFormatEtc( + _In_ IDataObject *This, + _In_ ULONG dwDirection, + _Out_opt_ IEnumFORMATETC **ppenumFormatEtc + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_DAdvise( + _In_ IDataObject *This, + _In_ FORMATETC *pformatetc, + _In_ ULONG advf, + _In_opt_ IAdviseSink *pAdvSink, + _Out_ ULONG *pdwConnection + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_DUnadvise( + _In_ IDataObject *This, + _In_ ULONG dwConnection + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE PhSecurityDataObject_EnumDAdvise( + _In_ IDataObject *This, + _Out_opt_ IEnumSTATDATA **ppenumAdvise + ) +{ + return E_NOTIMPL; +} + +// ISecurityObjectTypeInfo + +HRESULT STDMETHODCALLTYPE PhSecurityObjectTypeInfo_QueryInterface( + _In_ ISecurityObjectTypeInfoEx* This, + _In_ REFIID Riid, + _Out_ PVOID* Object + ) +{ + if ( + IsEqualIID(Riid, &IID_IUnknown) || + IsEqualIID(Riid, &IID_ISecurityObjectTypeInfo) + ) + { + PhSecurityObjectTypeInfo_AddRef(This); + *Object = This; + return S_OK; + } + + *Object = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE PhSecurityObjectTypeInfo_AddRef( + _In_ ISecurityObjectTypeInfoEx* This + ) +{ + PhSecurityObjectTypeInfo* this = (PhSecurityObjectTypeInfo*)This; + + this->RefCount++; + + return this->RefCount; +} + +ULONG STDMETHODCALLTYPE PhSecurityObjectTypeInfo_Release( + _In_ ISecurityObjectTypeInfoEx* This + ) +{ + PhSecurityObjectTypeInfo* this = (PhSecurityObjectTypeInfo*)This; + + this->RefCount--; + + if (this->RefCount == 0) + { + PhFree(this); + return 0; + } + + return this->RefCount; +} + +HRESULT STDMETHODCALLTYPE PhSecurityObjectTypeInfo_GetInheritSource( + _In_ ISecurityObjectTypeInfoEx* This, + _In_ SECURITY_INFORMATION SecurityInfo, + _In_ PACL Acl, + _Out_ PINHERITED_FROM* InheritArray + ) +{ + static GENERIC_MAPPING genericMappings = + { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE, + FILE_ALL_ACCESS + }; + + PhSecurityObjectTypeInfo* this = (PhSecurityObjectTypeInfo*)This; + PINHERITED_FROM result; + ULONG status; + + result = (PINHERITED_FROM)LocalAlloc(LPTR, ((ULONGLONG)Acl->AceCount + 1) * sizeof(INHERITED_FROM)); + + if ((status = GetInheritanceSource( + PhGetString(this->Context->ObjectName), + SE_FILE_OBJECT, + SecurityInfo, + TRUE, // Container + NULL, + 0, + Acl, + NULL, + &genericMappings, + result + )) == ERROR_SUCCESS) + { + *InheritArray = result; + } + else + { + LocalFree(result); + } + + return HRESULT_FROM_WIN32(status); +} + +NTSTATUS PhpGetObjectSecurityWithTimeout( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor + ) +{ + NTSTATUS status; + ULONG bufferSize; + PVOID buffer; + + bufferSize = 0x100; + buffer = PhAllocate(bufferSize); + // This is required (especially for File objects) because some drivers don't seem to handle + // QuerySecurity properly. (wj32) + memset(buffer, 0, bufferSize); + + status = PhCallNtQuerySecurityObjectWithTimeout( + Handle, + SecurityInformation, + buffer, + bufferSize, + &bufferSize + ); + + if (status == STATUS_BUFFER_TOO_SMALL) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + memset(buffer, 0, bufferSize); + + status = PhCallNtQuerySecurityObjectWithTimeout( + Handle, + SecurityInformation, + buffer, + bufferSize, + &bufferSize + ); + } + + if (!NT_SUCCESS(status)) + { + PhFree(buffer); + return status; + } + + *SecurityDescriptor = (PSECURITY_DESCRIPTOR)buffer; + + return status; +} + +/** + * Retrieves the security descriptor of an object. + * + * \param SecurityDescriptor A variable which receives a pointer to the security descriptor of the + * object. The security descriptor must be freed using PhFree() when no longer needed. + * \param SecurityInformation The security information to retrieve. + * \param Context A pointer to a PH_STD_OBJECT_SECURITY structure describing the object. + * + * \remarks This function may be used for the \a GetObjectSecurity callback in + * PhCreateSecurityPage() or PhEditSecurity(). + */ +_Callback_ NTSTATUS PhStdGetObjectSecurity( + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_opt_ PVOID Context + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)Context; + NTSTATUS status; + HANDLE handle; + + status = this->OpenObject( + &handle, + PhGetAccessForGetSecurity(SecurityInformation), + this->Context + ); + + if (!NT_SUCCESS(status)) + return status; + + if (PhEqualString2(this->ObjectType, L"Service", TRUE) || PhEqualString2(this->ObjectType, L"SCManager", TRUE)) + { + status = PhGetSeObjectSecurity(handle, SE_SERVICE, SecurityInformation, SecurityDescriptor); + CloseServiceHandle(handle); + } + else if (PhEqualString2(this->ObjectType, L"File", TRUE)) + { + status = PhpGetObjectSecurityWithTimeout(handle, SecurityInformation, SecurityDescriptor); + NtClose(handle); + } + else if (PhEqualString2(this->ObjectType, L"FileObject", TRUE)) + { + status = PhGetSeObjectSecurity(handle, SE_FILE_OBJECT, SecurityInformation, SecurityDescriptor); + NtClose(handle); + } + else if ( + PhEqualString2(this->ObjectType, L"LsaAccount", TRUE) || + PhEqualString2(this->ObjectType, L"LsaPolicy", TRUE) || + PhEqualString2(this->ObjectType, L"LsaSecret", TRUE) || + PhEqualString2(this->ObjectType, L"LsaTrusted", TRUE) + ) + { + PSECURITY_DESCRIPTOR securityDescriptor; + + status = LsaQuerySecurityObject( + handle, + SecurityInformation, + &securityDescriptor + ); + + if (NT_SUCCESS(status)) + { + *SecurityDescriptor = PhAllocateCopy( + securityDescriptor, + RtlLengthSecurityDescriptor(securityDescriptor) + ); + LsaFreeMemory(securityDescriptor); + } + + LsaClose(handle); + } + else if ( + PhEqualString2(this->ObjectType, L"SamAlias", TRUE) || + PhEqualString2(this->ObjectType, L"SamDomain", TRUE) || + PhEqualString2(this->ObjectType, L"SamGroup", TRUE) || + PhEqualString2(this->ObjectType, L"SamServer", TRUE) || + PhEqualString2(this->ObjectType, L"SamUser", TRUE) + ) + { + //PSECURITY_DESCRIPTOR securityDescriptor; + // + //status = SamQuerySecurityObject( + // handle, + // SecurityInformation, + // &securityDescriptor + // ); + // + //if (NT_SUCCESS(status)) + //{ + // *SecurityDescriptor = PhAllocateCopy( + // securityDescriptor, + // RtlLengthSecurityDescriptor(securityDescriptor) + // ); + // SamFreeMemory(securityDescriptor); + //} + // + //SamCloseHandle(handle); + } + else if (PhEqualString2(this->ObjectType, L"TokenDefault", TRUE)) + { + PTOKEN_DEFAULT_DACL defaultDacl = NULL; + + status = PhQueryTokenVariableSize( + handle, + TokenDefaultDacl, + &defaultDacl + ); + + // Note: NtQueryInformationToken returns success for processes with a NULL DefaultDacl. (dmex) + if (NT_SUCCESS(status) && !defaultDacl->DefaultDacl) + status = STATUS_INVALID_SECURITY_DESCR; + + if (NT_SUCCESS(status)) + { + ULONG allocationLength; + PSECURITY_DESCRIPTOR securityDescriptor; + + allocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + defaultDacl->DefaultDacl->AclSize; + + securityDescriptor = PhAllocateZero(allocationLength); + RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, defaultDacl->DefaultDacl, FALSE); + + assert(allocationLength == RtlLengthSecurityDescriptor(securityDescriptor)); + + *SecurityDescriptor = PhAllocateCopy( + securityDescriptor, + RtlLengthSecurityDescriptor(securityDescriptor) + ); + PhFree(securityDescriptor); + } + + if (defaultDacl) + PhFree(defaultDacl); + + NtClose(handle); + } + else + { + status = PhGetObjectSecurity(handle, SecurityInformation, SecurityDescriptor); + NtClose(handle); + } + + return status; +} + +/** + * Modifies the security descriptor of an object. + * + * \param SecurityDescriptor A security descriptor containing security information to set. + * \param SecurityInformation The security information to retrieve. + * \param Context A pointer to a PH_STD_OBJECT_SECURITY structure describing the object. + * + * \remarks This function may be used for the \a SetObjectSecurity callback in + * PhCreateSecurityPage() or PhEditSecurity(). + */ +_Callback_ NTSTATUS PhStdSetObjectSecurity( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_opt_ PVOID Context + ) +{ + PhSecurityInformation *this = (PhSecurityInformation *)Context; + NTSTATUS status; + HANDLE handle; + + status = this->OpenObject( + &handle, + PhGetAccessForSetSecurity(SecurityInformation), + this->Context + ); + + if (!NT_SUCCESS(status)) + return status; + + if (PhEqualString2(this->ObjectType, L"Service", TRUE) || PhEqualString2(this->ObjectType, L"SCManager", TRUE)) + { + status = PhSetSeObjectSecurity(handle, SE_SERVICE, SecurityInformation, SecurityDescriptor); + CloseServiceHandle(handle); + } + else if (PhEqualString2(this->ObjectType, L"File", TRUE)) + { + status = PhSetObjectSecurity(handle, SecurityInformation, SecurityDescriptor); + NtClose(handle); + } + else if (PhEqualString2(this->ObjectType, L"FileObject", TRUE)) + { + status = PhSetSeObjectSecurity(handle, SE_FILE_OBJECT, SecurityInformation, SecurityDescriptor); + NtClose(handle); + } + else if ( + PhEqualString2(this->ObjectType, L"LsaAccount", TRUE) || + PhEqualString2(this->ObjectType, L"LsaPolicy", TRUE) || + PhEqualString2(this->ObjectType, L"LsaSecret", TRUE) || + PhEqualString2(this->ObjectType, L"LsaTrusted", TRUE) + ) + { + status = LsaSetSecurityObject( + handle, + SecurityInformation, + SecurityDescriptor + ); + + LsaClose(handle); + } + else if ( + PhEqualString2(this->ObjectType, L"SamAlias", TRUE) || + PhEqualString2(this->ObjectType, L"SamDomain", TRUE) || + PhEqualString2(this->ObjectType, L"SamGroup", TRUE) || + PhEqualString2(this->ObjectType, L"SamServer", TRUE) || + PhEqualString2(this->ObjectType, L"SamUser", TRUE) + ) + { + //status = SamSetSecurityObject( + // handle, + // SecurityInformation, + // SecurityDescriptor + // ); + // + //SamCloseHandle(handle); + } + else if (PhEqualString2(this->ObjectType, L"TokenDefault", TRUE)) + { + BOOLEAN present = FALSE; + BOOLEAN defaulted = FALSE; + PACL dacl = NULL; + + status = RtlGetDaclSecurityDescriptor( + SecurityDescriptor, + &present, + &dacl, + &defaulted + ); + + // Note: RtlGetDaclSecurityDescriptor returns success for security descriptors with a NULL dacl. (dmex) + if (NT_SUCCESS(status) && !dacl) + status = STATUS_INVALID_SECURITY_DESCR; + + if (NT_SUCCESS(status)) + { + TOKEN_DEFAULT_DACL defaultDacl; + + defaultDacl.DefaultDacl = dacl; + + status = NtSetInformationToken( + handle, + TokenDefaultDacl, + &defaultDacl, + sizeof(TOKEN_DEFAULT_DACL) + ); + } + + NtClose(handle); + } + else + { + status = PhSetObjectSecurity(handle, SecurityInformation, SecurityDescriptor); + NtClose(handle); + } + + return status; +} + +NTSTATUS PhGetSeObjectSecurity( + _In_ HANDLE Handle, + _In_ ULONG ObjectType, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor + ) +{ + ULONG win32Result; + PSECURITY_DESCRIPTOR securityDescriptor; + + win32Result = GetSecurityInfo( + Handle, + ObjectType, + SecurityInformation, + NULL, + NULL, + NULL, + NULL, + &securityDescriptor + ); + + if (win32Result != ERROR_SUCCESS) + return NTSTATUS_FROM_WIN32(win32Result); + + *SecurityDescriptor = PhAllocateCopy(securityDescriptor, RtlLengthSecurityDescriptor(securityDescriptor)); + LocalFree(securityDescriptor); + + return STATUS_SUCCESS; +} + +NTSTATUS PhSetSeObjectSecurity( + _In_ HANDLE Handle, + _In_ ULONG ObjectType, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + ULONG win32Result = NO_ERROR; + SECURITY_INFORMATION securityInformation = 0; + BOOLEAN present = FALSE; + BOOLEAN defaulted = FALSE; + PSID owner = NULL; + PSID group = NULL; + PACL dacl = NULL; + PACL sacl = NULL; + + if (SecurityInformation & OWNER_SECURITY_INFORMATION) + { + if (NT_SUCCESS(RtlGetOwnerSecurityDescriptor(SecurityDescriptor, &owner, &defaulted))) + securityInformation |= OWNER_SECURITY_INFORMATION; + } + + if (SecurityInformation & GROUP_SECURITY_INFORMATION) + { + if (NT_SUCCESS(RtlGetGroupSecurityDescriptor(SecurityDescriptor, &group, &defaulted))) + securityInformation |= GROUP_SECURITY_INFORMATION; + } + + if (SecurityInformation & DACL_SECURITY_INFORMATION) + { + if (NT_SUCCESS(RtlGetDaclSecurityDescriptor(SecurityDescriptor, &present, &dacl, &defaulted)) && present) + securityInformation |= DACL_SECURITY_INFORMATION; + } + + if (SecurityInformation & SACL_SECURITY_INFORMATION) + { + if (NT_SUCCESS(RtlGetSaclSecurityDescriptor(SecurityDescriptor, &present, &sacl, &defaulted)) && present) + securityInformation |= SACL_SECURITY_INFORMATION; + } + + if (ObjectType == SE_FILE_OBJECT) // probably works with other types but haven't checked (dmex) + { + SECURITY_DESCRIPTOR_CONTROL control; + ULONG revision; + + if (NT_SUCCESS(RtlGetControlSecurityDescriptor(SecurityDescriptor, &control, &revision))) + { + if (SecurityInformation & DACL_SECURITY_INFORMATION) + { + if (control & SE_DACL_PROTECTED) + securityInformation |= PROTECTED_DACL_SECURITY_INFORMATION; + else + securityInformation |= UNPROTECTED_DACL_SECURITY_INFORMATION; + } + + if (SecurityInformation & SACL_SECURITY_INFORMATION) + { + if (control & SE_SACL_PROTECTED) + securityInformation |= PROTECTED_SACL_SECURITY_INFORMATION; + else + securityInformation |= UNPROTECTED_SACL_SECURITY_INFORMATION; + } + } + } + + win32Result = SetSecurityInfo( + Handle, + ObjectType, + securityInformation, // SecurityInformation + owner, + group, + dacl, + sacl + ); + + if (win32Result != ERROR_SUCCESS) + return NTSTATUS_FROM_WIN32(win32Result); + + return STATUS_SUCCESS; +} diff --git a/phlib/settings.c b/phlib/settings.c new file mode 100644 index 000000000000..dd0899a45a78 --- /dev/null +++ b/phlib/settings.c @@ -0,0 +1,1456 @@ +/* + * Process Hacker - + * settings + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This file contains a program-specific settings system. All possible + * settings are defined at program startup and added to a hashtable. + * The values of these settings can then be read in from a XML file or + * saved to a XML file at any time. Settings which are not recognized + * are added to a list of "ignored settings"; this is necessary to + * support plugin settings, as we don't want their settings to get + * deleted whenever the plugins are disabled. + * + * The get/set functions are very strict. If the wrong function is used + * (the get-integer-setting function is used on a string setting) or + * the setting does not exist, an exception will be raised. + */ + +#include +#include +#include +#include +#include + +#include + +#include "mxml/mxml.h" + +BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI PhpSettingsHashtableHashFunction( + _In_ PVOID Entry + ); + +ULONG PhpGetCurrentScale( + VOID + ); + +VOID PhpFreeSettingValue( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_SETTING Setting + ); + +PVOID PhpLookupSetting( + _In_ PPH_STRINGREF Name + ); + +PPH_HASHTABLE PhSettingsHashtable; +PH_QUEUED_LOCK PhSettingsLock = PH_QUEUED_LOCK_INIT; +PPH_LIST PhIgnoredSettings; + +VOID PhSettingsInitialization( + VOID + ) +{ + PhSettingsHashtable = PhCreateHashtable( + sizeof(PH_SETTING), + PhpSettingsHashtableEqualFunction, + PhpSettingsHashtableHashFunction, + 256 + ); + PhIgnoredSettings = PhCreateList(4); + + PhAddDefaultSettings(); + PhUpdateCachedSettings(); +} + +BOOLEAN NTAPI PhpSettingsHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPH_SETTING setting1 = (PPH_SETTING)Entry1; + PPH_SETTING setting2 = (PPH_SETTING)Entry2; + + return PhEqualStringRef(&setting1->Name, &setting2->Name, FALSE); +} + +ULONG NTAPI PhpSettingsHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_SETTING setting = (PPH_SETTING)Entry; + + return PhHashBytes((PUCHAR)setting->Name.Buffer, setting->Name.Length); +} + +static ULONG PhpGetCurrentScale( + VOID + ) +{ + static PH_INITONCE initOnce; + static ULONG dpi = 96; + + if (PhBeginInitOnce(&initOnce)) + { + HDC hdc; + + if (hdc = GetDC(NULL)) + { + dpi = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + } + + PhEndInitOnce(&initOnce); + } + + return dpi; +} + +PPH_STRING PhSettingToString( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_SETTING Setting + ) +{ + switch (Type) + { + case StringSettingType: + { + if (!Setting->u.Pointer) + return PhReferenceEmptyString(); + + PhReferenceObject(Setting->u.Pointer); + + return (PPH_STRING)Setting->u.Pointer; + } + case IntegerSettingType: + { + return PhFormatString(L"%x", Setting->u.Integer); + } + case IntegerPairSettingType: + { + PPH_INTEGER_PAIR integerPair = &Setting->u.IntegerPair; + + return PhFormatString(L"%ld,%ld", integerPair->X, integerPair->Y); + } + case ScalableIntegerPairSettingType: + { + PPH_SCALABLE_INTEGER_PAIR scalableIntegerPair = Setting->u.Pointer; + + if (!scalableIntegerPair) + return PhReferenceEmptyString(); + + return PhFormatString(L"@%lu|%ld,%ld", scalableIntegerPair->Scale, scalableIntegerPair->X, scalableIntegerPair->Y); + } + } + + return PhReferenceEmptyString(); +} + +BOOLEAN PhSettingFromString( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_STRINGREF StringRef, + _In_opt_ PPH_STRING String, + _Inout_ PPH_SETTING Setting + ) +{ + switch (Type) + { + case StringSettingType: + { + if (String) + { + PhSetReference(&Setting->u.Pointer, String); + } + else + { + Setting->u.Pointer = PhCreateString2(StringRef); + } + + return TRUE; + } + case IntegerSettingType: + { + ULONG64 integer; + + if (PhStringToInteger64(StringRef, 16, &integer)) + { + Setting->u.Integer = (ULONG)integer; + return TRUE; + } + else + { + return FALSE; + } + } + case IntegerPairSettingType: + { + LONG64 x; + LONG64 y; + PH_STRINGREF xString; + PH_STRINGREF yString; + + if (!PhSplitStringRefAtChar(StringRef, ',', &xString, &yString)) + return FALSE; + + if (PhStringToInteger64(&xString, 10, &x) && PhStringToInteger64(&yString, 10, &y)) + { + Setting->u.IntegerPair.X = (LONG)x; + Setting->u.IntegerPair.Y = (LONG)y; + return TRUE; + } + else + { + return FALSE; + } + } + case ScalableIntegerPairSettingType: + { + ULONG64 scale; + LONG64 x; + LONG64 y; + PH_STRINGREF stringRef; + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + PPH_SCALABLE_INTEGER_PAIR scalableIntegerPair; + + stringRef = *StringRef; + + if (stringRef.Length != 0 && stringRef.Buffer[0] == '@') + { + PhSkipStringRef(&stringRef, sizeof(WCHAR)); + + if (!PhSplitStringRefAtChar(&stringRef, '|', &firstPart, &stringRef)) + return FALSE; + if (!PhStringToInteger64(&firstPart, 10, &scale)) + return FALSE; + } + else + { + scale = PhpGetCurrentScale(); + } + + if (!PhSplitStringRefAtChar(&stringRef, ',', &firstPart, &secondPart)) + return FALSE; + + if (PhStringToInteger64(&firstPart, 10, &x) && PhStringToInteger64(&secondPart, 10, &y)) + { + scalableIntegerPair = PhAllocate(sizeof(PH_SCALABLE_INTEGER_PAIR)); + scalableIntegerPair->X = (LONG)x; + scalableIntegerPair->Y = (LONG)y; + scalableIntegerPair->Scale = (ULONG)scale; + Setting->u.Pointer = scalableIntegerPair; + return TRUE; + } + else + { + return FALSE; + } + } + } + + return FALSE; +} + +static VOID PhpFreeSettingValue( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_SETTING Setting + ) +{ + switch (Type) + { + case StringSettingType: + PhClearReference(&Setting->u.Pointer); + break; + case ScalableIntegerPairSettingType: + PhFree(Setting->u.Pointer); + Setting->u.Pointer = NULL; + break; + } +} + +static PVOID PhpLookupSetting( + _In_ PPH_STRINGREF Name + ) +{ + PH_SETTING lookupSetting; + PPH_SETTING setting; + + lookupSetting.Name = *Name; + setting = (PPH_SETTING)PhFindEntryHashtable( + PhSettingsHashtable, + &lookupSetting + ); + + return setting; +} + +VOID PhEnumSettings( + _In_ PPH_SETTINGS_ENUM_CALLBACK Callback, + _In_ PVOID Context + ) +{ + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_SETTING setting; + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + PhBeginEnumHashtable(PhSettingsHashtable, &enumContext); + + while (setting = PhNextEnumHashtable(&enumContext)) + { + if (!Callback(setting, Context)) + break; + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); +} + +_May_raise_ ULONG PhGetIntegerSetting( + _In_ PWSTR Name + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + ULONG value; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockShared(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == IntegerSettingType) + { + value = setting->u.Integer; + } + else + { + setting = NULL; + } + + PhReleaseQueuedLockShared(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); + + return value; +} + +_May_raise_ PH_INTEGER_PAIR PhGetIntegerPairSetting( + _In_ PWSTR Name + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + PH_INTEGER_PAIR value; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockShared(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == IntegerPairSettingType) + { + value = setting->u.IntegerPair; + } + else + { + setting = NULL; + } + + PhReleaseQueuedLockShared(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); + + return value; +} + +_May_raise_ PH_SCALABLE_INTEGER_PAIR PhGetScalableIntegerPairSetting( + _In_ PWSTR Name, + _In_ BOOLEAN ScaleToCurrent + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + PH_SCALABLE_INTEGER_PAIR value; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockShared(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == ScalableIntegerPairSettingType) + { + value = *(PPH_SCALABLE_INTEGER_PAIR)setting->u.Pointer; + } + else + { + setting = NULL; + } + + PhReleaseQueuedLockShared(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); + + if (ScaleToCurrent) + { + ULONG currentScale; + + currentScale = PhpGetCurrentScale(); + + if (value.Scale != currentScale && value.Scale != 0) + { + value.X = PhMultiplyDivideSigned(value.X, currentScale, value.Scale); + value.Y = PhMultiplyDivideSigned(value.Y, currentScale, value.Scale); + value.Scale = currentScale; + } + } + + return value; +} + +_May_raise_ PPH_STRING PhGetStringSetting( + _In_ PWSTR Name + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + PPH_STRING value; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockShared(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == StringSettingType) + { + if (setting->u.Pointer) + { + PhSetReference(&value, setting->u.Pointer); + } + else + { + // Set to NULL, create an empty string + // outside of the lock. + value = NULL; + } + } + else + { + setting = NULL; + } + + PhReleaseQueuedLockShared(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); + + if (!value) + value = PhReferenceEmptyString(); + + return value; +} + +_May_raise_ BOOLEAN PhGetBinarySetting( + _In_ PWSTR Name, + _Out_ PVOID Buffer + ) +{ + PPH_STRING setting; + BOOLEAN result; + + setting = PhGetStringSetting(Name); + result = PhHexStringToBuffer(&setting->sr, (PUCHAR)Buffer); + PhDereferenceObject(setting); + + return result; +} + +_May_raise_ VOID PhSetIntegerSetting( + _In_ PWSTR Name, + _In_ ULONG Value + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == IntegerSettingType) + { + setting->u.Integer = Value; + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); +} + +_May_raise_ VOID PhSetIntegerPairSetting( + _In_ PWSTR Name, + _In_ PH_INTEGER_PAIR Value + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == IntegerPairSettingType) + { + setting->u.IntegerPair = Value; + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); +} + +_May_raise_ VOID PhSetScalableIntegerPairSetting( + _In_ PWSTR Name, + _In_ PH_SCALABLE_INTEGER_PAIR Value + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == ScalableIntegerPairSettingType) + { + PhpFreeSettingValue(ScalableIntegerPairSettingType, setting); + setting->u.Pointer = PhAllocateCopy(&Value, sizeof(PH_SCALABLE_INTEGER_PAIR)); + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); +} + +_May_raise_ VOID PhSetScalableIntegerPairSetting2( + _In_ PWSTR Name, + _In_ PH_INTEGER_PAIR Value + ) +{ + PH_SCALABLE_INTEGER_PAIR scalableIntegerPair; + + scalableIntegerPair.Pair = Value; + scalableIntegerPair.Scale = PhpGetCurrentScale(); + PhSetScalableIntegerPairSetting(Name, scalableIntegerPair); +} + +_May_raise_ VOID PhSetStringSetting( + _In_ PWSTR Name, + _In_ PWSTR Value + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == StringSettingType) + { + PhpFreeSettingValue(StringSettingType, setting); + setting->u.Pointer = PhCreateString(Value); + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); +} + +_May_raise_ VOID PhSetStringSetting2( + _In_ PWSTR Name, + _In_ PPH_STRINGREF Value + ) +{ + PPH_SETTING setting; + PH_STRINGREF name; + + PhInitializeStringRef(&name, Name); + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + setting = PhpLookupSetting(&name); + + if (setting && setting->Type == StringSettingType) + { + PhpFreeSettingValue(StringSettingType, setting); + setting->u.Pointer = PhCreateString2(Value); + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); + + if (!setting) + PhRaiseStatus(STATUS_NOT_FOUND); +} + +_May_raise_ VOID PhSetBinarySetting( + _In_ PWSTR Name, + _In_ PVOID Buffer, + _In_ ULONG Length + ) +{ + PPH_STRING binaryString; + + binaryString = PhBufferToHexString((PUCHAR)Buffer, Length); + PhSetStringSetting(Name, binaryString->Buffer); + PhDereferenceObject(binaryString); +} + +VOID PhpFreeIgnoredSetting( + _In_ PPH_SETTING Setting + ) +{ + PhFree(Setting->Name.Buffer); + PhDereferenceObject(Setting->u.Pointer); + + PhFree(Setting); +} + +VOID PhpClearIgnoredSettings( + VOID + ) +{ + ULONG i; + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + for (i = 0; i < PhIgnoredSettings->Count; i++) + { + PhpFreeIgnoredSetting(PhIgnoredSettings->Items[i]); + } + + PhClearList(PhIgnoredSettings); + + PhReleaseQueuedLockExclusive(&PhSettingsLock); +} + +VOID PhClearIgnoredSettings( + VOID + ) +{ + PhpClearIgnoredSettings(); +} + +VOID PhConvertIgnoredSettings( + VOID + ) +{ + ULONG i; + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + for (i = 0; i < PhIgnoredSettings->Count; i++) + { + PPH_SETTING ignoredSetting = PhIgnoredSettings->Items[i]; + PPH_SETTING setting; + + setting = PhpLookupSetting(&ignoredSetting->Name); + + if (setting) + { + PhpFreeSettingValue(setting->Type, setting); + + if (!PhSettingFromString( + setting->Type, + &((PPH_STRING)ignoredSetting->u.Pointer)->sr, + ignoredSetting->u.Pointer, + setting + )) + { + PhSettingFromString( + setting->Type, + &setting->DefaultValue, + NULL, + setting + ); + } + + PhpFreeIgnoredSetting(ignoredSetting); + + PhRemoveItemList(PhIgnoredSettings, i); + i--; + } + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); +} + +PPH_STRING PhpGetOpaqueXmlNodeText( + _In_ mxml_node_t *node + ) +{ + PCSTR string; + + if (string = mxmlGetOpaque(node)) + { + return PhConvertUtf8ToUtf16((PSTR)string); + } + else + { + return PhReferenceEmptyString(); + } +} + +NTSTATUS PhLoadSettings( + _In_ PWSTR FileName + ) +{ + NTSTATUS status; + HANDLE fileHandle; + LARGE_INTEGER fileSize; + mxml_node_t *topNode; + mxml_node_t *currentNode; + + PhpClearIgnoredSettings(); + + status = PhCreateFileWin32( + &fileHandle, + FileName, + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return status; + + if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) + { + // A blank file is OK. There are no settings to load. + NtClose(fileHandle); + return status; + } + + topNode = mxmlLoadFd(NULL, fileHandle, MXML_OPAQUE_CALLBACK); + NtClose(fileHandle); + + if (!topNode) + return STATUS_FILE_CORRUPT_ERROR; + + if (mxmlGetType(topNode) != MXML_ELEMENT) + { + mxmlDelete(topNode); + return STATUS_FILE_CORRUPT_ERROR; + } + + currentNode = mxmlGetFirstChild(topNode); + + while (currentNode) + { + PPH_STRING settingName = NULL; + PCSTR elementValue; + + if (elementValue = mxmlElementGetAttr(currentNode, "name")) + { + settingName = PhConvertUtf8ToUtf16((PSTR)elementValue); + } + + if (settingName) + { + PPH_STRING settingValue = 0; + + settingValue = PhpGetOpaqueXmlNodeText(currentNode); + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + { + PPH_SETTING setting; + + setting = PhpLookupSetting(&settingName->sr); + + if (setting) + { + PhpFreeSettingValue(setting->Type, setting); + + if (!PhSettingFromString( + setting->Type, + &settingValue->sr, + settingValue, + setting + )) + { + PhSettingFromString( + setting->Type, + &setting->DefaultValue, + NULL, + setting + ); + } + } + else + { + setting = PhAllocate(sizeof(PH_SETTING)); + setting->Name.Buffer = PhAllocateCopy(settingName->Buffer, settingName->Length + sizeof(WCHAR)); + setting->Name.Length = settingName->Length; + PhReferenceObject(settingValue); + setting->u.Pointer = settingValue; + + PhAddItemList(PhIgnoredSettings, setting); + } + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); + + PhDereferenceObject(settingValue); + PhDereferenceObject(settingName); + } + + currentNode = mxmlGetNextSibling(currentNode); + } + + mxmlDelete(topNode); + + PhUpdateCachedSettings(); + + return STATUS_SUCCESS; +} + +PSTR PhpSettingsSaveCallback( + _In_ mxml_node_t *node, + _In_ int position + ) +{ + PSTR elementName; + + if (!(elementName = (PSTR)mxmlGetElement(node))) + return NULL; + + if (PhEqualBytesZ(elementName, "setting", TRUE)) + { + if (position == MXML_WS_BEFORE_OPEN) + return " "; + else if (position == MXML_WS_AFTER_CLOSE) + return "\r\n"; + } + else if (PhEqualBytesZ(elementName, "settings", TRUE)) + { + if (position == MXML_WS_AFTER_OPEN) + return "\r\n"; + } + + return NULL; +} + +mxml_node_t *PhpCreateSettingElement( + _Inout_ mxml_node_t *ParentNode, + _In_ PPH_STRINGREF SettingName, + _In_ PPH_STRINGREF SettingValue + ) +{ + mxml_node_t *settingNode; + mxml_node_t *textNode; + PPH_BYTES settingNameUtf8; + PPH_BYTES settingValueUtf8; + + // Create the setting element. + + settingNode = mxmlNewElement(ParentNode, "setting"); + + settingNameUtf8 = PhConvertUtf16ToUtf8Ex(SettingName->Buffer, SettingName->Length); + mxmlElementSetAttr(settingNode, "name", settingNameUtf8->Buffer); + PhDereferenceObject(settingNameUtf8); + + // Set the value. + + settingValueUtf8 = PhConvertUtf16ToUtf8Ex(SettingValue->Buffer, SettingValue->Length); + textNode = mxmlNewOpaque(settingNode, settingValueUtf8->Buffer); + PhDereferenceObject(settingValueUtf8); + + return settingNode; +} + +NTSTATUS PhSaveSettings( + _In_ PWSTR FileName + ) +{ + NTSTATUS status; + HANDLE fileHandle; + mxml_node_t *topNode; + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_SETTING setting; + + topNode = mxmlNewElement(MXML_NO_PARENT, "settings"); + + PhAcquireQueuedLockShared(&PhSettingsLock); + + PhBeginEnumHashtable(PhSettingsHashtable, &enumContext); + + while (setting = PhNextEnumHashtable(&enumContext)) + { + PPH_STRING settingValue; + + settingValue = PhSettingToString(setting->Type, setting); + PhpCreateSettingElement(topNode, &setting->Name, &settingValue->sr); + PhDereferenceObject(settingValue); + } + + // Write the ignored settings. + { + ULONG i; + + for (i = 0; i < PhIgnoredSettings->Count; i++) + { + PPH_STRING settingValue; + + setting = PhIgnoredSettings->Items[i]; + settingValue = setting->u.Pointer; + PhpCreateSettingElement(topNode, &setting->Name, &settingValue->sr); + } + } + + PhReleaseQueuedLockShared(&PhSettingsLock); + + // Create the directory if it does not exist. + { + PPH_STRING fullPath; + ULONG indexOfFileName; + PPH_STRING directoryName; + + fullPath = PhGetFullPath(FileName, &indexOfFileName); + + if (fullPath) + { + if (indexOfFileName != -1) + { + directoryName = PhSubstring(fullPath, 0, indexOfFileName); + PhCreateDirectory(directoryName); + PhDereferenceObject(directoryName); + } + + PhDereferenceObject(fullPath); + } + } + + status = PhCreateFileWin32( + &fileHandle, + FileName, + FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + { + mxmlDelete(topNode); + return status; + } + + mxmlSaveFd(topNode, fileHandle, PhpSettingsSaveCallback); + mxmlDelete(topNode); + NtClose(fileHandle); + + return STATUS_SUCCESS; +} + +VOID PhResetSettings( + VOID + ) +{ + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_SETTING setting; + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + PhBeginEnumHashtable(PhSettingsHashtable, &enumContext); + + while (setting = PhNextEnumHashtable(&enumContext)) + { + PhpFreeSettingValue(setting->Type, setting); + PhSettingFromString(setting->Type, &setting->DefaultValue, NULL, setting); + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); +} + +VOID PhAddSetting( + _In_ PH_SETTING_TYPE Type, + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF DefaultValue + ) +{ + PH_SETTING setting; + + setting.Type = Type; + setting.Name = *Name; + setting.DefaultValue = *DefaultValue; + memset(&setting.u, 0, sizeof(setting.u)); + + PhSettingFromString(Type, &setting.DefaultValue, NULL, &setting); + + PhAddEntryHashtable(PhSettingsHashtable, &setting); +} + +VOID PhAddSettings( + _In_ PPH_SETTING_CREATE Settings, + _In_ ULONG NumberOfSettings + ) +{ + ULONG i; + + PhAcquireQueuedLockExclusive(&PhSettingsLock); + + for (i = 0; i < NumberOfSettings; i++) + { + PH_STRINGREF name; + PH_STRINGREF defaultValue; + + PhInitializeStringRefLongHint(&name, Settings[i].Name); + PhInitializeStringRefLongHint(&defaultValue, Settings[i].DefaultValue); + PhAddSetting(Settings[i].Type, &name, &defaultValue); + } + + PhReleaseQueuedLockExclusive(&PhSettingsLock); +} + +VOID PhLoadWindowPlacementFromSetting( + _In_opt_ PWSTR PositionSettingName, + _In_opt_ PWSTR SizeSettingName, + _In_ HWND WindowHandle + ) +{ + PH_RECTANGLE windowRectangle; + + if (PositionSettingName && SizeSettingName) + { + RECT rectForAdjust; + + windowRectangle.Position = PhGetIntegerPairSetting(PositionSettingName); + windowRectangle.Size = PhGetScalableIntegerPairSetting(SizeSettingName, TRUE).Pair; + PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); + + // Let the window adjust for the minimum size if needed. + rectForAdjust = PhRectangleToRect(windowRectangle); + SendMessage(WindowHandle, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&rectForAdjust); + windowRectangle = PhRectToRectangle(rectForAdjust); + + MoveWindow(WindowHandle, windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, FALSE); + } + else + { + PH_INTEGER_PAIR position; + PH_INTEGER_PAIR size; + ULONG flags; + + flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER; + + if (PositionSettingName) + { + position = PhGetIntegerPairSetting(PositionSettingName); + flags &= ~SWP_NOMOVE; + } + else + { + position.X = 0; + position.Y = 0; + } + + if (SizeSettingName) + { + size = PhGetScalableIntegerPairSetting(SizeSettingName, TRUE).Pair; + flags &= ~SWP_NOSIZE; + } + else + { + size.X = 16; + size.Y = 16; + } + + SetWindowPos(WindowHandle, NULL, position.X, position.Y, size.X, size.Y, flags); + } +} + +VOID PhSaveWindowPlacementToSetting( + _In_opt_ PWSTR PositionSettingName, + _In_opt_ PWSTR SizeSettingName, + _In_ HWND WindowHandle + ) +{ + WINDOWPLACEMENT placement = { sizeof(placement) }; + PH_RECTANGLE windowRectangle; + MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; + + GetWindowPlacement(WindowHandle, &placement); + windowRectangle = PhRectToRectangle(placement.rcNormalPosition); + + // The rectangle is in workspace coordinates. Convert the values back to screen coordinates. + if (GetMonitorInfo(MonitorFromRect(&placement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo)) + { + windowRectangle.Left += monitorInfo.rcWork.left - monitorInfo.rcMonitor.left; + windowRectangle.Top += monitorInfo.rcWork.top - monitorInfo.rcMonitor.top; + } + + if (PositionSettingName) + PhSetIntegerPairSetting(PositionSettingName, windowRectangle.Position); + if (SizeSettingName) + PhSetScalableIntegerPairSetting2(SizeSettingName, windowRectangle.Size); +} + +BOOLEAN PhLoadListViewColumnSettings( + _In_ HWND ListViewHandle, + _In_ PPH_STRING Settings + ) +{ +#define ORDER_LIMIT 50 + PH_STRINGREF remainingPart; + ULONG columnIndex; + ULONG orderArray[ORDER_LIMIT]; // HACK, but reasonable limit + ULONG maxOrder; + ULONG scale; + + if (PhIsNullOrEmptyString(Settings)) + return FALSE; + + remainingPart = Settings->sr; + columnIndex = 0; + memset(orderArray, 0, sizeof(orderArray)); + maxOrder = 0; + + if (remainingPart.Length != 0 && remainingPart.Buffer[0] == '@') + { + PH_STRINGREF scalePart; + ULONG64 integer; + + PhSkipStringRef(&remainingPart, sizeof(WCHAR)); + PhSplitStringRefAtChar(&remainingPart, '|', &scalePart, &remainingPart); + + if (scalePart.Length == 0 || !PhStringToInteger64(&scalePart, 10, &integer)) + return FALSE; + + scale = (ULONG)integer; + } + else + { + scale = PhGlobalDpi; + } + + while (remainingPart.Length != 0) + { + PH_STRINGREF columnPart; + PH_STRINGREF orderPart; + PH_STRINGREF widthPart; + ULONG64 integer; + ULONG order; + ULONG width; + LVCOLUMN lvColumn; + + PhSplitStringRefAtChar(&remainingPart, '|', &columnPart, &remainingPart); + + if (columnPart.Length == 0) + return FALSE; + + PhSplitStringRefAtChar(&columnPart, ',', &orderPart, &widthPart); + + if (orderPart.Length == 0 || widthPart.Length == 0) + return FALSE; + + // Order + + if (!PhStringToInteger64(&orderPart, 10, &integer)) + return FALSE; + + order = (ULONG)integer; + + if (order < ORDER_LIMIT) + { + orderArray[order] = columnIndex; + + if (maxOrder < order + 1) + maxOrder = order + 1; + } + + // Width + + if (!PhStringToInteger64(&widthPart, 10, &integer)) + return FALSE; + + width = (ULONG)integer; + + if (scale != PhGlobalDpi && scale != 0) + width = PhMultiplyDivide(width, PhGlobalDpi, scale); + + lvColumn.mask = LVCF_WIDTH; + lvColumn.cx = width; + ListView_SetColumn(ListViewHandle, columnIndex, &lvColumn); + + columnIndex++; + } + + ListView_SetColumnOrderArray(ListViewHandle, maxOrder, orderArray); + + return TRUE; +} + +PPH_STRING PhSaveListViewColumnSettings( + _In_ HWND ListViewHandle + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i = 0; + LVCOLUMN lvColumn; + + PhInitializeStringBuilder(&stringBuilder, 20); + + PhAppendFormatStringBuilder(&stringBuilder, L"@%lu|", PhGlobalDpi); + + lvColumn.mask = LVCF_WIDTH | LVCF_ORDER; + + while (ListView_GetColumn(ListViewHandle, i, &lvColumn)) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%u,%u|", + lvColumn.iOrder, + lvColumn.cx + ); + i++; + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + return PhFinalStringBuilderString(&stringBuilder); +} + +VOID PhLoadListViewColumnsFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ) +{ + PPH_STRING string; + + string = PhGetStringSetting(Name); + PhLoadListViewColumnSettings(ListViewHandle, string); + PhDereferenceObject(string); +} + +VOID PhSaveListViewColumnsToSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ) +{ + PPH_STRING string; + + string = PhSaveListViewColumnSettings(ListViewHandle); + PhSetStringSetting2(Name, &string->sr); + PhDereferenceObject(string); +} + +VOID PhLoadListViewSortColumnsFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ) +{ + PPH_STRING string; + ULONG sortColumn = 0; + PH_SORT_ORDER sortOrder = AscendingSortOrder; + PH_STRINGREF remainingPart; + + string = PhGetStringSetting(Name); + + if (PhIsNullOrEmptyString(string)) + return; + + remainingPart = string->sr; + + if (remainingPart.Length != 0) + { + PH_STRINGREF columnPart; + PH_STRINGREF orderPart; + ULONG64 integer; + + if (!PhSplitStringRefAtChar(&remainingPart, ',', &columnPart, &orderPart)) + return; + + if (!PhStringToInteger64(&columnPart, 10, &integer)) + return; + + sortColumn = (ULONG)integer; + + if (!PhStringToInteger64(&orderPart, 10, &integer)) + return; + + sortOrder = (ULONG)integer; + } + + ExtendedListView_SetSort(ListViewHandle, sortColumn, sortOrder); + + PhDereferenceObject(string); +} + +VOID PhSaveListViewSortColumnsToSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ) +{ + PPH_STRING string; + ULONG sortColumn = 0; + PH_SORT_ORDER sortOrder = AscendingSortOrder; + + if (ExtendedListView_GetSort(ListViewHandle, &sortColumn, &sortOrder)) + string = PhFormatString(L"%lu,%lu", sortColumn, sortOrder); + else + string = PhCreateString(L"0,0"); + + PhSetStringSetting2(Name, &string->sr); + PhDereferenceObject(string); +} + +VOID PhLoadListViewGroupStatesFromSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ) +{ + ULONG64 countInteger; + PPH_STRING settingsString; + PH_STRINGREF remaining; + PH_STRINGREF part; + + settingsString = PhaGetStringSetting(Name); + remaining = settingsString->sr; + + if (remaining.Length == 0) + return; + + if (!PhSplitStringRefAtChar(&remaining, '|', &part, &remaining)) + return; + + if (!PhStringToInteger64(&part, 10, &countInteger)) + return; + + for (INT index = 0; index < (INT)countInteger; index++) + { + ULONG64 groupId; + ULONG64 stateMask; + PH_STRINGREF groupIdPart; + PH_STRINGREF stateMaskPart; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '|', &groupIdPart, &remaining); + + if (groupIdPart.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '|', &stateMaskPart, &remaining); + + if (stateMaskPart.Length == 0) + break; + + if (!PhStringToInteger64(&groupIdPart, 10, &groupId)) + break; + if (!PhStringToInteger64(&stateMaskPart, 10, &stateMask)) + break; + + ListView_SetGroupState( + ListViewHandle, + (INT)groupId, + LVGS_NORMAL | LVGS_COLLAPSED, + (UINT)stateMask + ); + } +} + +VOID PhSaveListViewGroupStatesToSetting( + _In_ PWSTR Name, + _In_ HWND ListViewHandle + ) +{ + INT index; + INT count; + PPH_STRING settingsString; + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + + count = (INT)ListView_GetGroupCount(ListViewHandle); + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%d|", + count + ); + + for (index = 0; index < count; index++) + { + LVGROUP group; + + memset(&group, 0, sizeof(LVGROUP)); + group.cbSize = sizeof(LVGROUP); + group.mask = LVGF_GROUPID | LVGF_STATE; + group.stateMask = LVGS_NORMAL | LVGS_COLLAPSED; + + if (ListView_GetGroupInfoByIndex(ListViewHandle, index, &group) == -1) + continue; + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%d|%u|", + group.iGroupId, + group.state + ); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); + PhSetStringSetting2(Name, &settingsString->sr); +} diff --git a/phlib/sha.c b/phlib/sha.c index 00cc9bd5a292..6b69ddee5c18 100644 --- a/phlib/sha.c +++ b/phlib/sha.c @@ -1,172 +1,172 @@ -/* - * Copyright 2004 Filip Navara - * Based on public domain SHA code by Steve Reid - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/* This code was modified for Process Hacker. */ - -#include -#include "sha.h" - -/* SHA1 Helper Macros */ - -//#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) -#define rol(value, bits) (_rotl((value), (bits))) -#define DWORD2BE(x) (((x) >> 24) & 0xff) | (((x) >> 8) & 0xff00) | (((x) << 8) & 0xff0000) | (((x) << 24) & 0xff000000); -#define blk0(i) (Block[i] = (rol(Block[i],24)&0xFF00FF00)|(rol(Block[i],8)&0x00FF00FF)) -#define blk1(i) (Block[i&15] = rol(Block[(i+13)&15]^Block[(i+8)&15]^Block[(i+2)&15]^Block[i&15],1)) -#define f1(x,y,z) (z^(x&(y^z))) -#define f2(x,y,z) (x^y^z) -#define f3(x,y,z) ((x&y)|(z&(x|y))) -#define f4(x,y,z) (x^y^z) -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -/* Hash a single 512-bit block. This is the core of the algorithm. */ -static void SHATransform(ULONG State[5], UCHAR Buffer[64]) -{ - ULONG a, b, c, d, e; - ULONG *Block; - - Block = (ULONG*)Buffer; - - /* Copy Context->State[] to working variables */ - a = State[0]; - b = State[1]; - c = State[2]; - d = State[3]; - e = State[4]; - - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - - /* Add the working variables back into Context->State[] */ - State[0] += a; - State[1] += b; - State[2] += c; - State[3] += d; - State[4] += e; - - /* Wipe variables */ - a = b = c = d = e = 0; -} - -VOID A_SHAInit( - _Out_ A_SHA_CTX *Context - ) -{ - /* SHA1 initialization constants */ - Context->state[0] = 0x67452301; - Context->state[1] = 0xEFCDAB89; - Context->state[2] = 0x98BADCFE; - Context->state[3] = 0x10325476; - Context->state[4] = 0xC3D2E1F0; - Context->count[0] = 0; - Context->count[1] = 0; -} - -VOID A_SHAUpdate( - _Inout_ A_SHA_CTX *Context, - _In_reads_bytes_(Length) UCHAR *Input, - _In_ ULONG Length - ) -{ - ULONG InputContentSize; - - InputContentSize = Context->count[1] & 63; - Context->count[1] += Length; - if (Context->count[1] < Length) - Context->count[0]++; - Context->count[0] += (Length >> 29); - - if (InputContentSize + Length < 64) - { - RtlCopyMemory(&Context->buffer[InputContentSize], Input, - Length); - } - else - { - while (InputContentSize + Length >= 64) - { - RtlCopyMemory(Context->buffer + InputContentSize, Input, - 64 - InputContentSize); - Input += 64 - InputContentSize; - Length -= 64 - InputContentSize; - SHATransform(Context->state, Context->buffer); - InputContentSize = 0; - } - RtlCopyMemory(Context->buffer + InputContentSize, Input, Length); - } -} - -VOID A_SHAFinal( - _Inout_ A_SHA_CTX *Context, - _Out_writes_bytes_(20) UCHAR *Hash - ) -{ - INT Pad, Index; - UCHAR Buffer[72]; - ULONG *Count; - ULONG BufferContentSize, LengthHi, LengthLo; - ULONG *Result; - - BufferContentSize = Context->count[1] & 63; - if (BufferContentSize >= 56) - Pad = 56 + 64 - BufferContentSize; - else - Pad = 56 - BufferContentSize; - - LengthHi = (Context->count[0] << 3) | (Context->count[1] >> (32 - 3)); - LengthLo = (Context->count[1] << 3); - - RtlZeroMemory(Buffer + 1, Pad - 1); - Buffer[0] = 0x80; - Count = (ULONG*)(Buffer + Pad); - Count[0] = DWORD2BE(LengthHi); - Count[1] = DWORD2BE(LengthLo); - A_SHAUpdate(Context, Buffer, Pad + 8); - - Result = (ULONG *)Hash; - - for (Index = 0; Index < 5; Index++) - Result[Index] = DWORD2BE(Context->state[Index]); - - A_SHAInit(Context); -} +/* + * Copyright 2004 Filip Navara + * Based on public domain SHA code by Steve Reid + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* This code was modified for Process Hacker. */ + +#include +#include "sha.h" + +/* SHA1 Helper Macros */ + +//#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) +#define rol(value, bits) (_rotl((value), (bits))) +#define DWORD2BE(x) (((x) >> 24) & 0xff) | (((x) >> 8) & 0xff00) | (((x) << 8) & 0xff0000) | (((x) << 24) & 0xff000000); +#define blk0(i) (Block[i] = (rol(Block[i],24)&0xFF00FF00)|(rol(Block[i],8)&0x00FF00FF)) +#define blk1(i) (Block[i&15] = rol(Block[(i+13)&15]^Block[(i+8)&15]^Block[(i+2)&15]^Block[i&15],1)) +#define f1(x,y,z) (z^(x&(y^z))) +#define f2(x,y,z) (x^y^z) +#define f3(x,y,z) ((x&y)|(z&(x|y))) +#define f4(x,y,z) (x^y^z) +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +static void SHATransform(ULONG State[5], UCHAR Buffer[64]) +{ + ULONG a, b, c, d, e; + ULONG *Block; + + Block = (ULONG*)Buffer; + + /* Copy Context->State[] to working variables */ + a = State[0]; + b = State[1]; + c = State[2]; + d = State[3]; + e = State[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working variables back into Context->State[] */ + State[0] += a; + State[1] += b; + State[2] += c; + State[3] += d; + State[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + +VOID A_SHAInit( + _Out_ A_SHA_CTX *Context + ) +{ + /* SHA1 initialization constants */ + Context->state[0] = 0x67452301; + Context->state[1] = 0xEFCDAB89; + Context->state[2] = 0x98BADCFE; + Context->state[3] = 0x10325476; + Context->state[4] = 0xC3D2E1F0; + Context->count[0] = 0; + Context->count[1] = 0; +} + +VOID A_SHAUpdate( + _Inout_ A_SHA_CTX *Context, + _In_reads_bytes_(Length) UCHAR *Input, + _In_ ULONG Length + ) +{ + ULONG InputContentSize; + + InputContentSize = Context->count[1] & 63; + Context->count[1] += Length; + if (Context->count[1] < Length) + Context->count[0]++; + Context->count[0] += (Length >> 29); + + if (InputContentSize + Length < 64) + { + RtlCopyMemory(&Context->buffer[InputContentSize], Input, + Length); + } + else + { + while (InputContentSize + Length >= 64) + { + RtlCopyMemory(Context->buffer + InputContentSize, Input, + 64 - InputContentSize); + Input += 64 - InputContentSize; + Length -= 64 - InputContentSize; + SHATransform(Context->state, Context->buffer); + InputContentSize = 0; + } + RtlCopyMemory(Context->buffer + InputContentSize, Input, Length); + } +} + +VOID A_SHAFinal( + _Inout_ A_SHA_CTX *Context, + _Out_writes_bytes_(20) UCHAR *Hash + ) +{ + INT Pad, Index; + UCHAR Buffer[72]; + ULONG *Count; + ULONG BufferContentSize, LengthHi, LengthLo; + ULONG *Result; + + BufferContentSize = Context->count[1] & 63; + if (BufferContentSize >= 56) + Pad = 56 + 64 - BufferContentSize; + else + Pad = 56 - BufferContentSize; + + LengthHi = (Context->count[0] << 3) | (Context->count[1] >> (32 - 3)); + LengthLo = (Context->count[1] << 3); + + RtlZeroMemory(Buffer + 1, Pad - 1); + Buffer[0] = 0x80; + Count = (ULONG*)(Buffer + Pad); + Count[0] = DWORD2BE(LengthHi); + Count[1] = DWORD2BE(LengthLo); + A_SHAUpdate(Context, Buffer, Pad + 8); + + Result = (ULONG *)Hash; + + for (Index = 0; Index < 5; Index++) + Result[Index] = DWORD2BE(Context->state[Index]); + + A_SHAInit(Context); +} diff --git a/phlib/svcsup.c b/phlib/svcsup.c index 2df8df0baa6b..850357f97470 100644 --- a/phlib/svcsup.c +++ b/phlib/svcsup.c @@ -1,554 +1,612 @@ -/* - * Process Hacker - - * service support functions - * - * Copyright (C) 2010-2012 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include - -#include - -#include - -#define SIP(String, Integer) { (String), (PVOID)(Integer) } - -static PH_KEY_VALUE_PAIR PhpServiceStatePairs[] = -{ - SIP(L"Stopped", SERVICE_STOPPED), - SIP(L"Start pending", SERVICE_START_PENDING), - SIP(L"Stop pending", SERVICE_STOP_PENDING), - SIP(L"Running", SERVICE_RUNNING), - SIP(L"Continue pending", SERVICE_CONTINUE_PENDING), - SIP(L"Pause pending", SERVICE_PAUSE_PENDING), - SIP(L"Paused", SERVICE_PAUSED) -}; - -static PH_KEY_VALUE_PAIR PhpServiceTypePairs[] = -{ - SIP(L"Driver", SERVICE_KERNEL_DRIVER), - SIP(L"FS driver", SERVICE_FILE_SYSTEM_DRIVER), - SIP(L"Own process", SERVICE_WIN32_OWN_PROCESS), - SIP(L"Share process", SERVICE_WIN32_SHARE_PROCESS), - SIP(L"Own interactive process", SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS), - SIP(L"Share interactive process", SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS), - SIP(L"User own process", SERVICE_USER_OWN_PROCESS), - SIP(L"User own process (instance)", SERVICE_USER_OWN_PROCESS | SERVICE_USERSERVICE_INSTANCE), - SIP(L"User share process", SERVICE_USER_SHARE_PROCESS), - SIP(L"User share process (instance)", SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE), -}; - -static PH_KEY_VALUE_PAIR PhpServiceStartTypePairs[] = -{ - SIP(L"Disabled", SERVICE_DISABLED), - SIP(L"Boot start", SERVICE_BOOT_START), - SIP(L"System start", SERVICE_SYSTEM_START), - SIP(L"Auto start", SERVICE_AUTO_START), - SIP(L"Demand start", SERVICE_DEMAND_START) -}; - -static PH_KEY_VALUE_PAIR PhpServiceErrorControlPairs[] = -{ - SIP(L"Ignore", SERVICE_ERROR_IGNORE), - SIP(L"Normal", SERVICE_ERROR_NORMAL), - SIP(L"Severe", SERVICE_ERROR_SEVERE), - SIP(L"Critical", SERVICE_ERROR_CRITICAL) -}; - -WCHAR *PhServiceTypeStrings[10] = { L"Driver", L"FS driver", L"Own process", L"Share process", - L"Own interactive process", L"Share interactive process", L"User own process", L"User own process (instance)", - L"User share process", L"User share process (instance)" }; -WCHAR *PhServiceStartTypeStrings[5] = { L"Disabled", L"Boot start", L"System start", - L"Auto start", L"Demand start" }; -WCHAR *PhServiceErrorControlStrings[4] = { L"Ignore", L"Normal", L"Severe", L"Critical" }; - -PVOID PhEnumServices( - _In_ SC_HANDLE ScManagerHandle, - _In_opt_ ULONG Type, - _In_opt_ ULONG State, - _Out_ PULONG Count - ) -{ - static ULONG initialBufferSize = 0x8000; - LOGICAL result; - PVOID buffer; - ULONG bufferSize; - ULONG returnLength; - ULONG servicesReturned; - - if (!Type) - { - if (WindowsVersion >= WINDOWS_10) - { - if (PhOsVersion.dwBuildNumber >= 14393) - { - Type = SERVICE_TYPE_ALL; - } - else - { - Type = SERVICE_WIN32 | - SERVICE_ADAPTER | - SERVICE_DRIVER | - SERVICE_INTERACTIVE_PROCESS | - SERVICE_USER_SERVICE | - SERVICE_USERSERVICE_INSTANCE; - } - } - else - { - Type = SERVICE_DRIVER | SERVICE_WIN32; - } - } - - if (!State) - State = SERVICE_STATE_ALL; - - bufferSize = initialBufferSize; - buffer = PhAllocate(bufferSize); - - if (!(result = EnumServicesStatusEx( - ScManagerHandle, - SC_ENUM_PROCESS_INFO, - Type, - State, - buffer, - bufferSize, - &returnLength, - &servicesReturned, - NULL, - NULL - ))) - { - if (GetLastError() == ERROR_MORE_DATA) - { - PhFree(buffer); - bufferSize += returnLength; - buffer = PhAllocate(bufferSize); - - result = EnumServicesStatusEx( - ScManagerHandle, - SC_ENUM_PROCESS_INFO, - Type, - State, - buffer, - bufferSize, - &returnLength, - &servicesReturned, - NULL, - NULL - ); - } - - if (!result) - { - PhFree(buffer); - return NULL; - } - } - - if (bufferSize <= 0x20000) initialBufferSize = bufferSize; - *Count = servicesReturned; - - return buffer; -} - -SC_HANDLE PhOpenService( - _In_ PWSTR ServiceName, - _In_ ACCESS_MASK DesiredAccess - ) -{ - SC_HANDLE scManagerHandle; - SC_HANDLE serviceHandle; - - scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); - - if (!scManagerHandle) - return NULL; - - serviceHandle = OpenService(scManagerHandle, ServiceName, DesiredAccess); - CloseServiceHandle(scManagerHandle); - - return serviceHandle; -} - -PVOID PhGetServiceConfig( - _In_ SC_HANDLE ServiceHandle - ) -{ - PVOID buffer; - ULONG bufferSize = 0x200; - - buffer = PhAllocate(bufferSize); - - if (!QueryServiceConfig(ServiceHandle, buffer, bufferSize, &bufferSize)) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - - if (!QueryServiceConfig(ServiceHandle, buffer, bufferSize, &bufferSize)) - { - PhFree(buffer); - return NULL; - } - } - - return buffer; -} - -PVOID PhQueryServiceVariableSize( - _In_ SC_HANDLE ServiceHandle, - _In_ ULONG InfoLevel - ) -{ - PVOID buffer; - ULONG bufferSize = 0x100; - - buffer = PhAllocate(bufferSize); - - if (!QueryServiceConfig2( - ServiceHandle, - InfoLevel, - (BYTE *)buffer, - bufferSize, - &bufferSize - )) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - - if (!QueryServiceConfig2( - ServiceHandle, - InfoLevel, - (BYTE *)buffer, - bufferSize, - &bufferSize - )) - { - PhFree(buffer); - return NULL; - } - } - - return buffer; -} - -PPH_STRING PhGetServiceDescription( - _In_ SC_HANDLE ServiceHandle - ) -{ - PPH_STRING description = NULL; - LPSERVICE_DESCRIPTION serviceDescription; - - serviceDescription = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_DESCRIPTION); - - if (serviceDescription) - { - if (serviceDescription->lpDescription) - description = PhCreateString(serviceDescription->lpDescription); - - PhFree(serviceDescription); - - return description; - } - else - { - return NULL; - } -} - -BOOLEAN PhGetServiceDelayedAutoStart( - _In_ SC_HANDLE ServiceHandle, - _Out_ PBOOLEAN DelayedAutoStart - ) -{ - SERVICE_DELAYED_AUTO_START_INFO delayedAutoStartInfo; - ULONG returnLength; - - if (QueryServiceConfig2( - ServiceHandle, - SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - (BYTE *)&delayedAutoStartInfo, - sizeof(SERVICE_DELAYED_AUTO_START_INFO), - &returnLength - )) - { - *DelayedAutoStart = !!delayedAutoStartInfo.fDelayedAutostart; - return TRUE; - } - else - { - return FALSE; - } -} - -BOOLEAN PhSetServiceDelayedAutoStart( - _In_ SC_HANDLE ServiceHandle, - _In_ BOOLEAN DelayedAutoStart - ) -{ - SERVICE_DELAYED_AUTO_START_INFO delayedAutoStartInfo; - - delayedAutoStartInfo.fDelayedAutostart = DelayedAutoStart; - - return !!ChangeServiceConfig2( - ServiceHandle, - SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - &delayedAutoStartInfo - ); -} - -PWSTR PhGetServiceStateString( - _In_ ULONG ServiceState - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs( - PhpServiceStatePairs, - sizeof(PhpServiceStatePairs), - ServiceState, - &string - )) - return string; - else - return L"Unknown"; -} - -PWSTR PhGetServiceTypeString( - _In_ ULONG ServiceType - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs( - PhpServiceTypePairs, - sizeof(PhpServiceTypePairs), - ServiceType, - &string - )) - return string; - else - return L"Unknown"; -} - -ULONG PhGetServiceTypeInteger( - _In_ PWSTR ServiceType - ) -{ - ULONG integer; - - if (PhFindIntegerSiKeyValuePairs( - PhpServiceTypePairs, - sizeof(PhpServiceTypePairs), - ServiceType, - &integer - )) - return integer; - else - return -1; -} - -PWSTR PhGetServiceStartTypeString( - _In_ ULONG ServiceStartType - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs( - PhpServiceStartTypePairs, - sizeof(PhpServiceStartTypePairs), - ServiceStartType, - &string - )) - return string; - else - return L"Unknown"; -} - -ULONG PhGetServiceStartTypeInteger( - _In_ PWSTR ServiceStartType - ) -{ - ULONG integer; - - if (PhFindIntegerSiKeyValuePairs( - PhpServiceStartTypePairs, - sizeof(PhpServiceStartTypePairs), - ServiceStartType, - &integer - )) - return integer; - else - return -1; -} - -PWSTR PhGetServiceErrorControlString( - _In_ ULONG ServiceErrorControl - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs( - PhpServiceErrorControlPairs, - sizeof(PhpServiceErrorControlPairs), - ServiceErrorControl, - &string - )) - return string; - else - return L"Unknown"; -} - -ULONG PhGetServiceErrorControlInteger( - _In_ PWSTR ServiceErrorControl - ) -{ - ULONG integer; - - if (PhFindIntegerSiKeyValuePairs( - PhpServiceErrorControlPairs, - sizeof(PhpServiceErrorControlPairs), - ServiceErrorControl, - &integer - )) - return integer; - else - return -1; -} - -PPH_STRING PhGetServiceNameFromTag( - _In_ HANDLE ProcessId, - _In_ PVOID ServiceTag - ) -{ - static PQUERY_TAG_INFORMATION I_QueryTagInformation = NULL; - PPH_STRING serviceName = NULL; - TAG_INFO_NAME_FROM_TAG nameFromTag; - - if (!I_QueryTagInformation) - { - I_QueryTagInformation = PhGetModuleProcAddress(L"advapi32.dll", "I_QueryTagInformation"); - - if (!I_QueryTagInformation) - return NULL; - } - - memset(&nameFromTag, 0, sizeof(TAG_INFO_NAME_FROM_TAG)); - nameFromTag.InParams.dwPid = HandleToUlong(ProcessId); - nameFromTag.InParams.dwTag = PtrToUlong(ServiceTag); - - I_QueryTagInformation(NULL, eTagInfoLevelNameFromTag, &nameFromTag); - - if (nameFromTag.OutParams.pszName) - { - serviceName = PhCreateString(nameFromTag.OutParams.pszName); - LocalFree(nameFromTag.OutParams.pszName); - } - - return serviceName; -} - -NTSTATUS PhGetThreadServiceTag( - _In_ HANDLE ThreadHandle, - _In_opt_ HANDLE ProcessHandle, - _Out_ PVOID *ServiceTag - ) -{ - NTSTATUS status; - THREAD_BASIC_INFORMATION basicInfo; - BOOLEAN openedProcessHandle = FALSE; - - if (!NT_SUCCESS(status = PhGetThreadBasicInformation(ThreadHandle, &basicInfo))) - return status; - - if (!ProcessHandle) - { - if (!NT_SUCCESS(status = PhOpenThreadProcess( - ThreadHandle, - PROCESS_VM_READ, - &ProcessHandle - ))) - return status; - - openedProcessHandle = TRUE; - } - - status = NtReadVirtualMemory( - ProcessHandle, - PTR_ADD_OFFSET(basicInfo.TebBaseAddress, FIELD_OFFSET(TEB, SubProcessTag)), - ServiceTag, - sizeof(PVOID), - NULL - ); - - if (openedProcessHandle) - NtClose(ProcessHandle); - - return status; -} - -NTSTATUS PhGetServiceDllParameter( - _In_ PPH_STRINGREF ServiceName, - _Out_ PPH_STRING *ServiceDll - ) -{ - static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); - static PH_STRINGREF parameters = PH_STRINGREF_INIT(L"\\Parameters"); - - NTSTATUS status; - HANDLE keyHandle; - PPH_STRING keyName; - - keyName = PhConcatStringRef3(&servicesKeyName, ServiceName, ¶meters); - - if (NT_SUCCESS(status = PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName->sr, - 0 - ))) - { - PPH_STRING serviceDllString; - - if (serviceDllString = PhQueryRegistryString(keyHandle, L"ServiceDll")) - { - PPH_STRING expandedString; - - if (expandedString = PhExpandEnvironmentStrings(&serviceDllString->sr)) - { - *ServiceDll = expandedString; - PhDereferenceObject(serviceDllString); - } - else - { - *ServiceDll = serviceDllString; - } - } - else - { - status = STATUS_NOT_FOUND; - } - - NtClose(keyHandle); - } - - PhDereferenceObject(keyName); - - return status; -} +/* + * Process Hacker - + * service support functions + * + * Copyright (C) 2010-2012 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#include + +#include + +#define SIP(String, Integer) { (String), (PVOID)(Integer) } + +static PH_KEY_VALUE_PAIR PhpServiceStatePairs[] = +{ + SIP(L"Stopped", SERVICE_STOPPED), + SIP(L"Start pending", SERVICE_START_PENDING), + SIP(L"Stop pending", SERVICE_STOP_PENDING), + SIP(L"Running", SERVICE_RUNNING), + SIP(L"Continue pending", SERVICE_CONTINUE_PENDING), + SIP(L"Pause pending", SERVICE_PAUSE_PENDING), + SIP(L"Paused", SERVICE_PAUSED) +}; + +static PH_KEY_VALUE_PAIR PhpServiceTypePairs[] = +{ + SIP(L"Driver", SERVICE_KERNEL_DRIVER), + SIP(L"FS driver", SERVICE_FILE_SYSTEM_DRIVER), + SIP(L"Own process", SERVICE_WIN32_OWN_PROCESS), + SIP(L"Share process", SERVICE_WIN32_SHARE_PROCESS), + SIP(L"Own interactive process", SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS), + SIP(L"Share interactive process", SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS), + SIP(L"User own process", SERVICE_USER_OWN_PROCESS), + SIP(L"User own process (instance)", SERVICE_USER_OWN_PROCESS | SERVICE_USERSERVICE_INSTANCE), + SIP(L"User share process", SERVICE_USER_SHARE_PROCESS), + SIP(L"User share process (instance)", SERVICE_USER_SHARE_PROCESS | SERVICE_USERSERVICE_INSTANCE) +}; + +static PH_KEY_VALUE_PAIR PhpServiceStartTypePairs[] = +{ + SIP(L"Disabled", SERVICE_DISABLED), + SIP(L"Boot start", SERVICE_BOOT_START), + SIP(L"System start", SERVICE_SYSTEM_START), + SIP(L"Auto start", SERVICE_AUTO_START), + SIP(L"Demand start", SERVICE_DEMAND_START) +}; + +static PH_KEY_VALUE_PAIR PhpServiceErrorControlPairs[] = +{ + SIP(L"Ignore", SERVICE_ERROR_IGNORE), + SIP(L"Normal", SERVICE_ERROR_NORMAL), + SIP(L"Severe", SERVICE_ERROR_SEVERE), + SIP(L"Critical", SERVICE_ERROR_CRITICAL) +}; + +WCHAR *PhServiceTypeStrings[10] = { L"Driver", L"FS driver", L"Own process", L"Share process", + L"Own interactive process", L"Share interactive process", L"User own process", L"User own process (instance)", + L"User share process", L"User share process (instance)" }; +WCHAR *PhServiceStartTypeStrings[5] = { L"Disabled", L"Boot start", L"System start", + L"Auto start", L"Demand start" }; +WCHAR *PhServiceErrorControlStrings[4] = { L"Ignore", L"Normal", L"Severe", L"Critical" }; + +PVOID PhEnumServices( + _In_ SC_HANDLE ScManagerHandle, + _In_opt_ ULONG Type, + _In_opt_ ULONG State, + _Out_ PULONG Count + ) +{ + static ULONG initialBufferSize = 0x8000; + LOGICAL result; + PVOID buffer; + ULONG bufferSize; + ULONG returnLength; + ULONG servicesReturned; + + if (!Type) + { + if (WindowsVersion >= WINDOWS_10_RS1) + { + Type = SERVICE_TYPE_ALL; + } + else if (WindowsVersion >= WINDOWS_10) + { + Type = SERVICE_WIN32 | + SERVICE_ADAPTER | + SERVICE_DRIVER | + SERVICE_INTERACTIVE_PROCESS | + SERVICE_USER_SERVICE | + SERVICE_USERSERVICE_INSTANCE; + } + else + { + Type = SERVICE_DRIVER | SERVICE_WIN32; + } + } + + if (!State) + State = SERVICE_STATE_ALL; + + bufferSize = initialBufferSize; + buffer = PhAllocate(bufferSize); + + if (!(result = EnumServicesStatusEx( + ScManagerHandle, + SC_ENUM_PROCESS_INFO, + Type, + State, + buffer, + bufferSize, + &returnLength, + &servicesReturned, + NULL, + NULL + ))) + { + if (GetLastError() == ERROR_MORE_DATA) + { + PhFree(buffer); + bufferSize += returnLength; + buffer = PhAllocate(bufferSize); + + result = EnumServicesStatusEx( + ScManagerHandle, + SC_ENUM_PROCESS_INFO, + Type, + State, + buffer, + bufferSize, + &returnLength, + &servicesReturned, + NULL, + NULL + ); + } + + if (!result) + { + PhFree(buffer); + return NULL; + } + } + + if (bufferSize <= 0x20000) initialBufferSize = bufferSize; + *Count = servicesReturned; + + return buffer; +} + +SC_HANDLE PhOpenService( + _In_ PWSTR ServiceName, + _In_ ACCESS_MASK DesiredAccess + ) +{ + SC_HANDLE scManagerHandle; + SC_HANDLE serviceHandle; + + scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + + if (!scManagerHandle) + return NULL; + + serviceHandle = OpenService(scManagerHandle, ServiceName, DesiredAccess); + CloseServiceHandle(scManagerHandle); + + return serviceHandle; +} + +PVOID PhGetServiceConfig( + _In_ SC_HANDLE ServiceHandle + ) +{ + PVOID buffer; + ULONG bufferSize = 0x200; + + buffer = PhAllocate(bufferSize); + + if (!QueryServiceConfig(ServiceHandle, buffer, bufferSize, &bufferSize)) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + if (!QueryServiceConfig(ServiceHandle, buffer, bufferSize, &bufferSize)) + { + PhFree(buffer); + return NULL; + } + } + + return buffer; +} + +PVOID PhQueryServiceVariableSize( + _In_ SC_HANDLE ServiceHandle, + _In_ ULONG InfoLevel + ) +{ + PVOID buffer; + ULONG bufferSize = 0x100; + + buffer = PhAllocate(bufferSize); + + if (!QueryServiceConfig2( + ServiceHandle, + InfoLevel, + (BYTE *)buffer, + bufferSize, + &bufferSize + )) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + if (!QueryServiceConfig2( + ServiceHandle, + InfoLevel, + (BYTE *)buffer, + bufferSize, + &bufferSize + )) + { + PhFree(buffer); + return NULL; + } + } + + return buffer; +} + +PPH_STRING PhGetServiceDescription( + _In_ SC_HANDLE ServiceHandle + ) +{ + PPH_STRING description = NULL; + LPSERVICE_DESCRIPTION serviceDescription; + + serviceDescription = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_DESCRIPTION); + + if (serviceDescription) + { + if (serviceDescription->lpDescription) + description = PhCreateString(serviceDescription->lpDescription); + + PhFree(serviceDescription); + + return description; + } + else + { + return NULL; + } +} + +BOOLEAN PhGetServiceDelayedAutoStart( + _In_ SC_HANDLE ServiceHandle, + _Out_ PBOOLEAN DelayedAutoStart + ) +{ + SERVICE_DELAYED_AUTO_START_INFO delayedAutoStartInfo; + ULONG returnLength; + + if (QueryServiceConfig2( + ServiceHandle, + SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + (BYTE *)&delayedAutoStartInfo, + sizeof(SERVICE_DELAYED_AUTO_START_INFO), + &returnLength + )) + { + *DelayedAutoStart = !!delayedAutoStartInfo.fDelayedAutostart; + return TRUE; + } + else + { + return FALSE; + } +} + +BOOLEAN PhSetServiceDelayedAutoStart( + _In_ SC_HANDLE ServiceHandle, + _In_ BOOLEAN DelayedAutoStart + ) +{ + SERVICE_DELAYED_AUTO_START_INFO delayedAutoStartInfo; + + delayedAutoStartInfo.fDelayedAutostart = DelayedAutoStart; + + return !!ChangeServiceConfig2( + ServiceHandle, + SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + &delayedAutoStartInfo + ); +} + +PWSTR PhGetServiceStateString( + _In_ ULONG ServiceState + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs( + PhpServiceStatePairs, + sizeof(PhpServiceStatePairs), + ServiceState, + &string + )) + return string; + else + return L"Unknown"; +} + +PWSTR PhGetServiceTypeString( + _In_ ULONG ServiceType + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs( + PhpServiceTypePairs, + sizeof(PhpServiceTypePairs), + ServiceType, + &string + )) + return string; + else + return L"Unknown"; +} + +ULONG PhGetServiceTypeInteger( + _In_ PWSTR ServiceType + ) +{ + ULONG integer; + + if (PhFindIntegerSiKeyValuePairs( + PhpServiceTypePairs, + sizeof(PhpServiceTypePairs), + ServiceType, + &integer + )) + return integer; + else + return ULONG_MAX; +} + +PWSTR PhGetServiceStartTypeString( + _In_ ULONG ServiceStartType + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs( + PhpServiceStartTypePairs, + sizeof(PhpServiceStartTypePairs), + ServiceStartType, + &string + )) + return string; + else + return L"Unknown"; +} + +ULONG PhGetServiceStartTypeInteger( + _In_ PWSTR ServiceStartType + ) +{ + ULONG integer; + + if (PhFindIntegerSiKeyValuePairs( + PhpServiceStartTypePairs, + sizeof(PhpServiceStartTypePairs), + ServiceStartType, + &integer + )) + return integer; + else + return ULONG_MAX; +} + +PWSTR PhGetServiceErrorControlString( + _In_ ULONG ServiceErrorControl + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs( + PhpServiceErrorControlPairs, + sizeof(PhpServiceErrorControlPairs), + ServiceErrorControl, + &string + )) + return string; + else + return L"Unknown"; +} + +ULONG PhGetServiceErrorControlInteger( + _In_ PWSTR ServiceErrorControl + ) +{ + ULONG integer; + + if (PhFindIntegerSiKeyValuePairs( + PhpServiceErrorControlPairs, + sizeof(PhpServiceErrorControlPairs), + ServiceErrorControl, + &integer + )) + return integer; + else + return ULONG_MAX; +} + +PPH_STRING PhGetServiceNameFromTag( + _In_ HANDLE ProcessId, + _In_ PVOID ServiceTag + ) +{ + static PQUERY_TAG_INFORMATION I_QueryTagInformation = NULL; + PPH_STRING serviceName = NULL; + TAG_INFO_NAME_FROM_TAG nameFromTag; + + if (!I_QueryTagInformation) + { + I_QueryTagInformation = PhGetDllProcedureAddress(L"advapi32.dll", "I_QueryTagInformation", 0); + + if (!I_QueryTagInformation) + return NULL; + } + + memset(&nameFromTag, 0, sizeof(TAG_INFO_NAME_FROM_TAG)); + nameFromTag.InParams.dwPid = HandleToUlong(ProcessId); + nameFromTag.InParams.dwTag = PtrToUlong(ServiceTag); + + I_QueryTagInformation(NULL, eTagInfoLevelNameFromTag, &nameFromTag); + + if (nameFromTag.OutParams.pszName) + { + serviceName = PhCreateString(nameFromTag.OutParams.pszName); + LocalFree(nameFromTag.OutParams.pszName); + } + + return serviceName; +} + +PPH_STRING PhGetServiceNameForModuleReference( + _In_ HANDLE ProcessId, + _In_ PWSTR ModuleName + ) +{ + static PQUERY_TAG_INFORMATION I_QueryTagInformation = NULL; + PPH_STRING serviceNames = NULL; + TAG_INFO_NAMES_REFERENCING_MODULE moduleNameRef; + + if (!I_QueryTagInformation) + { + I_QueryTagInformation = PhGetDllProcedureAddress(L"advapi32.dll", "I_QueryTagInformation", 0); + + if (!I_QueryTagInformation) + return NULL; + } + + memset(&moduleNameRef, 0, sizeof(TAG_INFO_NAMES_REFERENCING_MODULE)); + moduleNameRef.InParams.dwPid = HandleToUlong(ProcessId); + moduleNameRef.InParams.pszModule = ModuleName; + + I_QueryTagInformation(NULL, eTagInfoLevelNamesReferencingModule, &moduleNameRef); + + if (moduleNameRef.OutParams.pmszNames) + { + PH_STRING_BUILDER sb; + PWSTR serviceName; + + PhInitializeStringBuilder(&sb, 0x40); + + for (serviceName = moduleNameRef.OutParams.pmszNames; *serviceName; serviceName += PhCountStringZ(serviceName) + 1) + PhAppendFormatStringBuilder(&sb, L"%s, ", serviceName); + + if (sb.String->Length != 0) + PhRemoveEndStringBuilder(&sb, 2); + + serviceNames = PhFinalStringBuilderString(&sb); + LocalFree(moduleNameRef.OutParams.pmszNames); + } + + return serviceNames; +} + +NTSTATUS PhGetThreadServiceTag( + _In_ HANDLE ThreadHandle, + _In_opt_ HANDLE ProcessHandle, + _Out_ PVOID *ServiceTag + ) +{ + NTSTATUS status; + THREAD_BASIC_INFORMATION basicInfo; + BOOLEAN openedProcessHandle = FALSE; + + if (!NT_SUCCESS(status = PhGetThreadBasicInformation(ThreadHandle, &basicInfo))) + return status; + + if (!ProcessHandle) + { + if (!NT_SUCCESS(status = PhOpenThreadProcess( + ThreadHandle, + PROCESS_VM_READ, + &ProcessHandle + ))) + return status; + + openedProcessHandle = TRUE; + } + + status = NtReadVirtualMemory( + ProcessHandle, + PTR_ADD_OFFSET(basicInfo.TebBaseAddress, FIELD_OFFSET(TEB, SubProcessTag)), + ServiceTag, + sizeof(PVOID), + NULL + ); + + if (openedProcessHandle) + NtClose(ProcessHandle); + + return status; +} + +NTSTATUS PhGetServiceDllParameter( + _In_ ULONG ServiceType, + _In_ PPH_STRINGREF ServiceName, + _Out_ PPH_STRING *ServiceDll + ) +{ + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + static PH_STRINGREF parameters = PH_STRINGREF_INIT(L"\\Parameters"); + NTSTATUS status; + HANDLE keyHandle; + PPH_STRING keyName; + + if (ServiceType & SERVICE_USERSERVICE_INSTANCE) + { + PH_STRINGREF hostServiceName; + PH_STRINGREF userSessionLuid; + + // The SCM creates multiple "user service instance" processes for each user session with the following template: + // [Host Service Instance Name]_[LUID for Session] + // The SCM internally uses the ServiceDll of the "host service instance" for all "user service instance" processes/services + // and we need to parse the user service template and query the "host service instance" configuration. (hsebs) + + if (PhSplitStringRefAtLastChar(ServiceName, L'_', &hostServiceName, &userSessionLuid)) + keyName = PhConcatStringRef3(&servicesKeyName, &hostServiceName, ¶meters); + else + keyName = PhConcatStringRef3(&servicesKeyName, ServiceName, ¶meters); + } + else + { + keyName = PhConcatStringRef3(&servicesKeyName, ServiceName, ¶meters); + } + + if (NT_SUCCESS(status = PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) + { + PPH_STRING serviceDllString; + + if (serviceDllString = PhQueryRegistryString(keyHandle, L"ServiceDll")) + { + PPH_STRING expandedString; + + if (expandedString = PhExpandEnvironmentStrings(&serviceDllString->sr)) + { + *ServiceDll = expandedString; + PhDereferenceObject(serviceDllString); + } + else + { + *ServiceDll = serviceDllString; + } + } + else + { + status = STATUS_NOT_FOUND; + } + + NtClose(keyHandle); + } + + PhDereferenceObject(keyName); + + return status; +} diff --git a/phlib/symprv.c b/phlib/symprv.c index 633be28a2d33..bf4181a53e24 100644 --- a/phlib/symprv.c +++ b/phlib/symprv.c @@ -1,1760 +1,1779 @@ -/* - * Process Hacker - - * symbol provider - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include -#include -#include - -#include - -typedef struct _PH_SYMBOL_MODULE -{ - LIST_ENTRY ListEntry; - PH_AVL_LINKS Links; - ULONG64 BaseAddress; - ULONG Size; - PPH_STRING FileName; - ULONG BaseNameIndex; -} PH_SYMBOL_MODULE, *PPH_SYMBOL_MODULE; - -VOID NTAPI PhpSymbolProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -VOID PhpRegisterSymbolProvider( - _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider - ); - -VOID PhpFreeSymbolModule( - _In_ PPH_SYMBOL_MODULE SymbolModule - ); - -LONG NTAPI PhpSymbolModuleCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ); - -PPH_OBJECT_TYPE PhSymbolProviderType; - -static PH_INITONCE PhSymInitOnce = PH_INITONCE_INIT; -DECLSPEC_SELECTANY PH_CALLBACK_DECLARE(PhSymInitCallback); - -static HANDLE PhNextFakeHandle = (HANDLE)0; -static PH_FAST_LOCK PhSymMutex = PH_FAST_LOCK_INIT; - -#define PH_LOCK_SYMBOLS() PhAcquireFastLockExclusive(&PhSymMutex) -#define PH_UNLOCK_SYMBOLS() PhReleaseFastLockExclusive(&PhSymMutex) - -_SymInitialize SymInitialize_I; -_SymCleanup SymCleanup_I; -_SymEnumSymbols SymEnumSymbols_I; -_SymEnumSymbolsW SymEnumSymbolsW_I; -_SymFromAddr SymFromAddr_I; -_SymFromAddrW SymFromAddrW_I; -_SymFromName SymFromName_I; -_SymFromNameW SymFromNameW_I; -_SymGetLineFromAddr64 SymGetLineFromAddr64_I; -_SymGetLineFromAddrW64 SymGetLineFromAddrW64_I; -_SymLoadModule64 SymLoadModule64_I; -_SymLoadModuleExW SymLoadModuleExW_I; -_SymGetOptions SymGetOptions_I; -_SymSetOptions SymSetOptions_I; -_SymGetSearchPath SymGetSearchPath_I; -_SymGetSearchPathW SymGetSearchPathW_I; -_SymSetSearchPath SymSetSearchPath_I; -_SymSetSearchPathW SymSetSearchPathW_I; -_SymUnloadModule64 SymUnloadModule64_I; -_SymFunctionTableAccess64 SymFunctionTableAccess64_I; -_SymGetModuleBase64 SymGetModuleBase64_I; -_SymRegisterCallbackW64 SymRegisterCallbackW64_I; -_StackWalk64 StackWalk64_I; -_MiniDumpWriteDump MiniDumpWriteDump_I; -_SymbolServerGetOptions SymbolServerGetOptions; -_SymbolServerSetOptions SymbolServerSetOptions; - -BOOLEAN PhSymbolProviderInitialization( - VOID - ) -{ - PhSymbolProviderType = PhCreateObjectType(L"SymbolProvider", 0, PhpSymbolProviderDeleteProcedure); - - return TRUE; -} - -VOID PhSymbolProviderCompleteInitialization( - _In_opt_ PVOID DbgHelpBase - ) -{ - HMODULE dbghelpHandle; - HMODULE symsrvHandle; - - // The user should have loaded dbghelp.dll and symsrv.dll already. If not, it's not our problem. - - // The Unicode versions aren't available in dbghelp.dll 5.1, so we fallback on the ANSI versions. - - if (DbgHelpBase) - dbghelpHandle = DbgHelpBase; - else - dbghelpHandle = GetModuleHandle(L"dbghelp.dll"); - - symsrvHandle = GetModuleHandle(L"symsrv.dll"); - - SymInitialize_I = PhGetProcedureAddress(dbghelpHandle, "SymInitialize", 0); - SymCleanup_I = PhGetProcedureAddress(dbghelpHandle, "SymCleanup", 0); - if (!(SymEnumSymbolsW_I = PhGetProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0))) - SymEnumSymbols_I = PhGetProcedureAddress(dbghelpHandle, "SymEnumSymbols", 0); - if (!(SymFromAddrW_I = PhGetProcedureAddress(dbghelpHandle, "SymFromAddrW", 0))) - SymFromAddr_I = PhGetProcedureAddress(dbghelpHandle, "SymFromAddr", 0); - if (!(SymFromNameW_I = PhGetProcedureAddress(dbghelpHandle, "SymFromNameW", 0))) - SymFromName_I = PhGetProcedureAddress(dbghelpHandle, "SymFromName", 0); - if (!(SymGetLineFromAddrW64_I = PhGetProcedureAddress(dbghelpHandle, "SymGetLineFromAddrW64", 0))) - SymGetLineFromAddr64_I = PhGetProcedureAddress(dbghelpHandle, "SymGetLineFromAddr64", 0); - if (!(SymLoadModuleExW_I = PhGetProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0))) - SymLoadModule64_I = PhGetProcedureAddress(dbghelpHandle, "SymLoadModule64", 0); - SymGetOptions_I = PhGetProcedureAddress(dbghelpHandle, "SymGetOptions", 0); - SymSetOptions_I = PhGetProcedureAddress(dbghelpHandle, "SymSetOptions", 0); - if (!(SymGetSearchPathW_I = PhGetProcedureAddress(dbghelpHandle, "SymGetSearchPathW", 0))) - SymGetSearchPath_I = PhGetProcedureAddress(dbghelpHandle, "SymGetSearchPath", 0); - if (!(SymSetSearchPathW_I = PhGetProcedureAddress(dbghelpHandle, "SymSetSearchPathW", 0))) - SymSetSearchPath_I = PhGetProcedureAddress(dbghelpHandle, "SymSetSearchPath", 0); - SymUnloadModule64_I = PhGetProcedureAddress(dbghelpHandle, "SymUnloadModule64", 0); - SymFunctionTableAccess64_I = PhGetProcedureAddress(dbghelpHandle, "SymFunctionTableAccess64", 0); - SymGetModuleBase64_I = PhGetProcedureAddress(dbghelpHandle, "SymGetModuleBase64", 0); - SymRegisterCallbackW64_I = PhGetProcedureAddress(dbghelpHandle, "SymRegisterCallbackW64", 0); - StackWalk64_I = PhGetProcedureAddress(dbghelpHandle, "StackWalk64", 0); - MiniDumpWriteDump_I = PhGetProcedureAddress(dbghelpHandle, "MiniDumpWriteDump", 0); - SymbolServerGetOptions = PhGetProcedureAddress(symsrvHandle, "SymbolServerGetOptions", 0); - SymbolServerSetOptions = PhGetProcedureAddress(symsrvHandle, "SymbolServerSetOptions", 0); - - if (SymGetOptions_I && SymSetOptions_I) - SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED); -} - -PPH_SYMBOL_PROVIDER PhCreateSymbolProvider( - _In_opt_ HANDLE ProcessId - ) -{ - PPH_SYMBOL_PROVIDER symbolProvider; - - symbolProvider = PhCreateObject(sizeof(PH_SYMBOL_PROVIDER), PhSymbolProviderType); - memset(symbolProvider, 0, sizeof(PH_SYMBOL_PROVIDER)); - InitializeListHead(&symbolProvider->ModulesListHead); - PhInitializeQueuedLock(&symbolProvider->ModulesListLock); - PhInitializeAvlTree(&symbolProvider->ModulesSet, PhpSymbolModuleCompareFunction); - PhInitializeCallback(&symbolProvider->EventCallback); - PhInitializeInitOnce(&symbolProvider->InitOnce); - - if (ProcessId) - { - static ACCESS_MASK accesses[] = - { - STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff, // pre-Vista full access - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - MAXIMUM_ALLOWED - }; - - ULONG i; - - // Try to open the process with many different accesses. - // This handle will be re-used when walking stacks, and doing various other things. - for (i = 0; i < sizeof(accesses) / sizeof(ACCESS_MASK); i++) - { - if (NT_SUCCESS(PhOpenProcess(&symbolProvider->ProcessHandle, accesses[i], ProcessId))) - { - symbolProvider->IsRealHandle = TRUE; - break; - } - } - } - - if (!symbolProvider->IsRealHandle) - { - HANDLE fakeHandle; - - // Just generate a fake handle. - fakeHandle = (HANDLE)_InterlockedExchangeAddPointer((PLONG_PTR)&PhNextFakeHandle, 4); - // Add one to make sure it isn't divisible by 4 (so it can't be mistaken for a real handle). - symbolProvider->ProcessHandle = (HANDLE)((ULONG_PTR)fakeHandle + 1); - } - - return symbolProvider; -} - -VOID NTAPI PhpSymbolProviderDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PPH_SYMBOL_PROVIDER symbolProvider = (PPH_SYMBOL_PROVIDER)Object; - PLIST_ENTRY listEntry; - - PhDeleteCallback(&symbolProvider->EventCallback); - - if (SymCleanup_I) - { - PH_LOCK_SYMBOLS(); - - if (symbolProvider->IsRegistered) - SymCleanup_I(symbolProvider->ProcessHandle); - - PH_UNLOCK_SYMBOLS(); - } - - listEntry = symbolProvider->ModulesListHead.Flink; - - while (listEntry != &symbolProvider->ModulesListHead) - { - PPH_SYMBOL_MODULE module; - - module = CONTAINING_RECORD(listEntry, PH_SYMBOL_MODULE, ListEntry); - listEntry = listEntry->Flink; - - PhpFreeSymbolModule(module); - } - - if (symbolProvider->IsRealHandle) NtClose(symbolProvider->ProcessHandle); -} - -NTSTATUS PhpSymbolCallbackWorker( - _In_ PVOID Parameter - ) -{ - PPH_SYMBOL_EVENT_DATA data = Parameter; - - dprintf("symbol event %d: %S\n", data->Type, data->FileName->Buffer); - PhInvokeCallback(&data->SymbolProvider->EventCallback, data); - PhClearReference(&data->FileName); - PhDereferenceObject(data); - - return STATUS_SUCCESS; -} - -BOOL CALLBACK PhpSymbolCallbackFunction( - _In_ HANDLE hProcess, - _In_ ULONG ActionCode, - _In_opt_ ULONG64 CallbackData, - _In_opt_ ULONG64 UserContext - ) -{ - PPH_SYMBOL_PROVIDER symbolProvider = (PPH_SYMBOL_PROVIDER)UserContext; - PPH_SYMBOL_EVENT_DATA data; - PIMAGEHLP_DEFERRED_SYMBOL_LOADW64 callbackData; - - if (!IsListEmpty(&symbolProvider->EventCallback.ListHead)) - { - switch (ActionCode) - { - case SymbolDeferredSymbolLoadStart: - case SymbolDeferredSymbolLoadComplete: - case SymbolDeferredSymbolLoadFailure: - case SymbolSymbolsUnloaded: - case SymbolDeferredSymbolLoadCancel: - data = PhCreateAlloc(sizeof(PH_SYMBOL_EVENT_DATA)); - memset(data, 0, sizeof(PH_SYMBOL_EVENT_DATA)); - data->SymbolProvider = symbolProvider; - data->Type = ActionCode; - - if (ActionCode != SymbolSymbolsUnloaded) - { - callbackData = (PIMAGEHLP_DEFERRED_SYMBOL_LOADW64)CallbackData; - data->BaseAddress = callbackData->BaseOfImage; - data->CheckSum = callbackData->CheckSum; - data->TimeStamp = callbackData->TimeDateStamp; - data->FileName = PhCreateString(callbackData->FileName); - } - - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhpSymbolCallbackWorker, data); - - break; - } - } - - return FALSE; -} - -VOID PhpRegisterSymbolProvider( - _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider - ) -{ - if (PhBeginInitOnce(&PhSymInitOnce)) - { - PhInvokeCallback(&PhSymInitCallback, NULL); - PhEndInitOnce(&PhSymInitOnce); - } - - if (!SymbolProvider) - return; - - if (PhBeginInitOnce(&SymbolProvider->InitOnce)) - { - if (SymInitialize_I) - { - PH_LOCK_SYMBOLS(); - - SymInitialize_I(SymbolProvider->ProcessHandle, NULL, FALSE); - - if (SymRegisterCallbackW64_I) - SymRegisterCallbackW64_I(SymbolProvider->ProcessHandle, PhpSymbolCallbackFunction, (ULONG64)SymbolProvider); - - PH_UNLOCK_SYMBOLS(); - - SymbolProvider->IsRegistered = TRUE; - } - - PhEndInitOnce(&SymbolProvider->InitOnce); - } -} - -VOID PhpFreeSymbolModule( - _In_ PPH_SYMBOL_MODULE SymbolModule - ) -{ - if (SymbolModule->FileName) PhDereferenceObject(SymbolModule->FileName); - - PhFree(SymbolModule); -} - -static LONG NTAPI PhpSymbolModuleCompareFunction( - _In_ PPH_AVL_LINKS Links1, - _In_ PPH_AVL_LINKS Links2 - ) -{ - PPH_SYMBOL_MODULE symbolModule1 = CONTAINING_RECORD(Links1, PH_SYMBOL_MODULE, Links); - PPH_SYMBOL_MODULE symbolModule2 = CONTAINING_RECORD(Links2, PH_SYMBOL_MODULE, Links); - - return uint64cmp(symbolModule1->BaseAddress, symbolModule2->BaseAddress); -} - -BOOLEAN PhGetLineFromAddress( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address, - _Out_ PPH_STRING *FileName, - _Out_opt_ PULONG Displacement, - _Out_opt_ PPH_SYMBOL_LINE_INFORMATION Information - ) -{ - IMAGEHLP_LINEW64 line; - BOOL result; - ULONG displacement; - PPH_STRING fileName; - - PhpRegisterSymbolProvider(SymbolProvider); - - if (!SymGetLineFromAddrW64_I && !SymGetLineFromAddr64_I) - return FALSE; - - line.SizeOfStruct = sizeof(IMAGEHLP_LINEW64); - - PH_LOCK_SYMBOLS(); - - if (SymGetLineFromAddrW64_I) - { - result = SymGetLineFromAddrW64_I( - SymbolProvider->ProcessHandle, - Address, - &displacement, - &line - ); - - if (result) - fileName = PhCreateString(line.FileName); - } - else - { - IMAGEHLP_LINE64 lineA; - - lineA.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - - result = SymGetLineFromAddr64_I( - SymbolProvider->ProcessHandle, - Address, - &displacement, - &lineA - ); - - if (result) - { - fileName = PhConvertMultiByteToUtf16(lineA.FileName); - line.LineNumber = lineA.LineNumber; - line.Address = lineA.Address; - } - } - - PH_UNLOCK_SYMBOLS(); - - if (!result) - return FALSE; - - *FileName = fileName; - - if (Displacement) - *Displacement = displacement; - - if (Information) - { - Information->LineNumber = line.LineNumber; - Information->Address = line.Address; - } - - return TRUE; -} - -ULONG64 PhGetModuleFromAddress( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address, - _Out_opt_ PPH_STRING *FileName - ) -{ - PH_SYMBOL_MODULE lookupModule; - PPH_AVL_LINKS links; - PPH_SYMBOL_MODULE module; - PPH_STRING foundFileName; - ULONG64 foundBaseAddress; - - foundFileName = NULL; - foundBaseAddress = 0; - - PhAcquireQueuedLockShared(&SymbolProvider->ModulesListLock); - - // Do an approximate search on the modules set to locate the module with the largest - // base address that is still smaller than the given address. - lookupModule.BaseAddress = Address; - links = PhUpperDualBoundElementAvlTree(&SymbolProvider->ModulesSet, &lookupModule.Links); - - if (links) - { - module = CONTAINING_RECORD(links, PH_SYMBOL_MODULE, Links); - - if (Address < module->BaseAddress + module->Size) - { - PhSetReference(&foundFileName, module->FileName); - foundBaseAddress = module->BaseAddress; - } - } - - PhReleaseQueuedLockShared(&SymbolProvider->ModulesListLock); - - if (foundFileName) - { - if (FileName) - { - *FileName = foundFileName; - } - else - { - PhDereferenceObject(foundFileName); - } - } - - return foundBaseAddress; -} - -VOID PhpSymbolInfoAnsiToUnicode( - _Out_ PSYMBOL_INFOW SymbolInfoW, - _In_ PSYMBOL_INFO SymbolInfoA - ) -{ - SymbolInfoW->TypeIndex = SymbolInfoA->TypeIndex; - SymbolInfoW->Index = SymbolInfoA->Index; - SymbolInfoW->Size = SymbolInfoA->Size; - SymbolInfoW->ModBase = SymbolInfoA->ModBase; - SymbolInfoW->Flags = SymbolInfoA->Flags; - SymbolInfoW->Value = SymbolInfoA->Value; - SymbolInfoW->Address = SymbolInfoA->Address; - SymbolInfoW->Register = SymbolInfoA->Register; - SymbolInfoW->Scope = SymbolInfoA->Scope; - SymbolInfoW->Tag = SymbolInfoA->Tag; - SymbolInfoW->NameLen = 0; - - if (SymbolInfoA->NameLen != 0 && SymbolInfoW->MaxNameLen != 0) - { - ULONG copyCount; - - copyCount = min(SymbolInfoA->NameLen, SymbolInfoW->MaxNameLen - 1); - - if (PhCopyStringZFromMultiByte( - SymbolInfoA->Name, - copyCount, - SymbolInfoW->Name, - SymbolInfoW->MaxNameLen, - NULL - )) - { - SymbolInfoW->NameLen = copyCount; - } - } -} - -PPH_STRING PhGetSymbolFromAddress( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address, - _Out_opt_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel, - _Out_opt_ PPH_STRING *FileName, - _Out_opt_ PPH_STRING *SymbolName, - _Out_opt_ PULONG64 Displacement - ) -{ - PSYMBOL_INFOW symbolInfo; - ULONG nameLength; - PPH_STRING symbol = NULL; - PH_SYMBOL_RESOLVE_LEVEL resolveLevel; - ULONG64 displacement; - PPH_STRING modFileName = NULL; - PPH_STRING modBaseName = NULL; - ULONG64 modBase; - PPH_STRING symbolName = NULL; - - if (Address == 0) - { - if (ResolveLevel) *ResolveLevel = PhsrlInvalid; - if (FileName) *FileName = NULL; - if (SymbolName) *SymbolName = NULL; - if (Displacement) *Displacement = 0; - - return NULL; - } - - PhpRegisterSymbolProvider(SymbolProvider); - - if (!SymFromAddrW_I && !SymFromAddr_I) - return NULL; - - symbolInfo = PhAllocate(FIELD_OFFSET(SYMBOL_INFOW, Name) + PH_MAX_SYMBOL_NAME_LEN * 2); - memset(symbolInfo, 0, sizeof(SYMBOL_INFOW)); - symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); - symbolInfo->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN; - - // Get the symbol name. - - PH_LOCK_SYMBOLS(); - - // Note that we don't care whether this call succeeds or not, based on the assumption that it - // will not write to the symbolInfo structure if it fails. We've already zeroed the structure, - // so we can deal with it. - - if (SymFromAddrW_I) - { - SymFromAddrW_I( - SymbolProvider->ProcessHandle, - Address, - &displacement, - symbolInfo - ); - nameLength = symbolInfo->NameLen; - - if (nameLength + 1 > PH_MAX_SYMBOL_NAME_LEN) - { - PhFree(symbolInfo); - symbolInfo = PhAllocate(FIELD_OFFSET(SYMBOL_INFOW, Name) + nameLength * 2 + 2); - memset(symbolInfo, 0, sizeof(SYMBOL_INFOW)); - symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); - symbolInfo->MaxNameLen = nameLength + 1; - - SymFromAddrW_I( - SymbolProvider->ProcessHandle, - Address, - &displacement, - symbolInfo - ); - } - } - else if (SymFromAddr_I) - { - PSYMBOL_INFO symbolInfoA; - - symbolInfoA = PhAllocate(FIELD_OFFSET(SYMBOL_INFO, Name) + PH_MAX_SYMBOL_NAME_LEN); - memset(symbolInfoA, 0, sizeof(SYMBOL_INFO)); - symbolInfoA->SizeOfStruct = sizeof(SYMBOL_INFO); - symbolInfoA->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN; - - SymFromAddr_I( - SymbolProvider->ProcessHandle, - Address, - &displacement, - symbolInfoA - ); - nameLength = symbolInfoA->NameLen; - - if (nameLength + 1 > PH_MAX_SYMBOL_NAME_LEN) - { - PhFree(symbolInfoA); - symbolInfoA = PhAllocate(FIELD_OFFSET(SYMBOL_INFO, Name) + nameLength + 1); - memset(symbolInfoA, 0, sizeof(SYMBOL_INFO)); - symbolInfoA->SizeOfStruct = sizeof(SYMBOL_INFO); - symbolInfoA->MaxNameLen = nameLength + 1; - - SymFromAddr_I( - SymbolProvider->ProcessHandle, - Address, - &displacement, - symbolInfoA - ); - - // Also reallocate the Unicode-based buffer. - PhFree(symbolInfo); - symbolInfo = PhAllocate(FIELD_OFFSET(SYMBOL_INFOW, Name) + nameLength * 2 + 2); - memset(symbolInfo, 0, sizeof(SYMBOL_INFOW)); - symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); - symbolInfo->MaxNameLen = nameLength + 1; - } - - PhpSymbolInfoAnsiToUnicode(symbolInfo, symbolInfoA); - PhFree(symbolInfoA); - } - - PH_UNLOCK_SYMBOLS(); - - // Find the module name. - - if (symbolInfo->ModBase == 0) - { - modBase = PhGetModuleFromAddress( - SymbolProvider, - Address, - &modFileName - ); - } - else - { - PH_SYMBOL_MODULE lookupSymbolModule; - PPH_AVL_LINKS existingLinks; - PPH_SYMBOL_MODULE symbolModule; - - lookupSymbolModule.BaseAddress = symbolInfo->ModBase; - - PhAcquireQueuedLockShared(&SymbolProvider->ModulesListLock); - - existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links); - - if (existingLinks) - { - symbolModule = CONTAINING_RECORD(existingLinks, PH_SYMBOL_MODULE, Links); - PhSetReference(&modFileName, symbolModule->FileName); - } - - PhReleaseQueuedLockShared(&SymbolProvider->ModulesListLock); - } - - // If we don't have a module name, return an address. - if (!modFileName) - { - resolveLevel = PhsrlAddress; - symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2); - PhPrintPointer(symbol->Buffer, (PVOID)Address); - PhTrimToNullTerminatorString(symbol); - - goto CleanupExit; - } - - modBaseName = PhGetBaseName(modFileName); - - // If we have a module name but not a symbol name, return the module plus an offset: - // module+offset. - - if (symbolInfo->NameLen == 0) - { - PH_FORMAT format[3]; - - resolveLevel = PhsrlModule; - - PhInitFormatSR(&format[0], modBaseName->sr); - PhInitFormatS(&format[1], L"+0x"); - PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase)); - symbol = PhFormat(format, 3, modBaseName->Length + 6 + 32); - - goto CleanupExit; - } - - // If we have everything, return the full symbol name: module!symbol+offset. - - symbolName = PhCreateStringEx(symbolInfo->Name, symbolInfo->NameLen * 2); - resolveLevel = PhsrlFunction; - - if (displacement == 0) - { - PH_FORMAT format[3]; - - PhInitFormatSR(&format[0], modBaseName->sr); - PhInitFormatC(&format[1], '!'); - PhInitFormatSR(&format[2], symbolName->sr); - - symbol = PhFormat(format, 3, modBaseName->Length + 2 + symbolName->Length); - } - else - { - PH_FORMAT format[5]; - - PhInitFormatSR(&format[0], modBaseName->sr); - PhInitFormatC(&format[1], '!'); - PhInitFormatSR(&format[2], symbolName->sr); - PhInitFormatS(&format[3], L"+0x"); - PhInitFormatIX(&format[4], (ULONG_PTR)displacement); - - symbol = PhFormat(format, 5, modBaseName->Length + 2 + symbolName->Length + 6 + 32); - } - -CleanupExit: - - if (ResolveLevel) - *ResolveLevel = resolveLevel; - if (FileName) - PhSetReference(FileName, modFileName); - if (SymbolName) - PhSetReference(SymbolName, symbolName); - if (Displacement) - *Displacement = displacement; - - PhClearReference(&modFileName); - PhClearReference(&modBaseName); - PhClearReference(&symbolName); - PhFree(symbolInfo); - - return symbol; -} - -BOOLEAN PhGetSymbolFromName( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ PWSTR Name, - _Out_ PPH_SYMBOL_INFORMATION Information - ) -{ - PSYMBOL_INFOW symbolInfo; - UCHAR symbolInfoBuffer[FIELD_OFFSET(SYMBOL_INFOW, Name) + PH_MAX_SYMBOL_NAME_LEN * 2]; - BOOL result; - - PhpRegisterSymbolProvider(SymbolProvider); - - if (!SymFromNameW_I && !SymFromName_I) - return FALSE; - - symbolInfo = (PSYMBOL_INFOW)symbolInfoBuffer; - memset(symbolInfo, 0, sizeof(SYMBOL_INFOW)); - symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); - symbolInfo->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN; - - // Get the symbol information. - - PH_LOCK_SYMBOLS(); - - if (SymFromNameW_I) - { - result = SymFromNameW_I( - SymbolProvider->ProcessHandle, - Name, - symbolInfo - ); - } - else if (SymFromName_I) - { - UCHAR buffer[FIELD_OFFSET(SYMBOL_INFO, Name) + PH_MAX_SYMBOL_NAME_LEN]; - PSYMBOL_INFO symbolInfoA; - PPH_BYTES name; - - symbolInfoA = (PSYMBOL_INFO)buffer; - memset(symbolInfoA, 0, sizeof(SYMBOL_INFO)); - symbolInfoA->SizeOfStruct = sizeof(SYMBOL_INFO); - symbolInfoA->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN; - - name = PhConvertUtf16ToMultiByte(Name); - - if (result = SymFromName_I( - SymbolProvider->ProcessHandle, - name->Buffer, - symbolInfoA - )) - { - PhpSymbolInfoAnsiToUnicode(symbolInfo, symbolInfoA); - } - - PhDereferenceObject(name); - } - else - { - result = FALSE; - } - - PH_UNLOCK_SYMBOLS(); - - if (!result) - return FALSE; - - Information->Address = symbolInfo->Address; - Information->ModuleBase = symbolInfo->ModBase; - Information->Index = symbolInfo->Index; - Information->Size = symbolInfo->Size; - - return TRUE; -} - -BOOLEAN PhLoadModuleSymbolProvider( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ PWSTR FileName, - _In_ ULONG64 BaseAddress, - _In_ ULONG Size - ) -{ - ULONG64 baseAddress; - PPH_SYMBOL_MODULE symbolModule = NULL; - PPH_AVL_LINKS existingLinks; - PH_SYMBOL_MODULE lookupSymbolModule; - - PhpRegisterSymbolProvider(SymbolProvider); - - if (!SymLoadModuleExW_I && !SymLoadModule64_I) - return FALSE; - - // Check for duplicates. It is better to do this before calling SymLoadModuleExW, because it - // seems to force symbol loading when it is called twice on the same module even if deferred - // loading is enabled. - PhAcquireQueuedLockExclusive(&SymbolProvider->ModulesListLock); - lookupSymbolModule.BaseAddress = BaseAddress; - existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links); - PhReleaseQueuedLockExclusive(&SymbolProvider->ModulesListLock); - - if (existingLinks) - return TRUE; - - PH_LOCK_SYMBOLS(); - - if (SymLoadModuleExW_I) - { - baseAddress = SymLoadModuleExW_I( - SymbolProvider->ProcessHandle, - NULL, - FileName, - NULL, - BaseAddress, - Size, - NULL, - 0 - ); - } - else - { - PPH_BYTES fileName; - - fileName = PhConvertUtf16ToMultiByte(FileName); - baseAddress = SymLoadModule64_I( - SymbolProvider->ProcessHandle, - NULL, - fileName->Buffer, - NULL, - BaseAddress, - Size - ); - PhDereferenceObject(fileName); - } - - PH_UNLOCK_SYMBOLS(); - - // Add the module to the list, even if we couldn't load symbols for the module. - - PhAcquireQueuedLockExclusive(&SymbolProvider->ModulesListLock); - - // Check for duplicates again. - lookupSymbolModule.BaseAddress = BaseAddress; - existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links); - - if (!existingLinks) - { - symbolModule = PhAllocate(sizeof(PH_SYMBOL_MODULE)); - symbolModule->BaseAddress = BaseAddress; - symbolModule->Size = Size; - symbolModule->FileName = PhGetFullPath(FileName, &symbolModule->BaseNameIndex); - - existingLinks = PhAddElementAvlTree(&SymbolProvider->ModulesSet, &symbolModule->Links); - assert(!existingLinks); - InsertTailList(&SymbolProvider->ModulesListHead, &symbolModule->ListEntry); - } - - PhReleaseQueuedLockExclusive(&SymbolProvider->ModulesListLock); - - if (!baseAddress) - { - if (GetLastError() != ERROR_SUCCESS) - return FALSE; - else - return TRUE; - } - - return TRUE; -} - -VOID PhSetOptionsSymbolProvider( - _In_ ULONG Mask, - _In_ ULONG Value - ) -{ - ULONG options; - - PhpRegisterSymbolProvider(NULL); - - if (!SymGetOptions_I || !SymSetOptions_I) - return; - - PH_LOCK_SYMBOLS(); - - options = SymGetOptions_I(); - options &= ~Mask; - options |= Value; - SymSetOptions_I(options); - - PH_UNLOCK_SYMBOLS(); -} - -VOID PhSetSearchPathSymbolProvider( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ PWSTR Path - ) -{ - PhpRegisterSymbolProvider(SymbolProvider); - - if (!SymSetSearchPathW_I && !SymSetSearchPath_I) - return; - - PH_LOCK_SYMBOLS(); - - if (SymSetSearchPathW_I) - { - SymSetSearchPathW_I(SymbolProvider->ProcessHandle, Path); - } - else if (SymSetSearchPath_I) - { - PPH_BYTES path; - - path = PhConvertUtf16ToMultiByte(Path); - SymSetSearchPath_I(SymbolProvider->ProcessHandle, path->Buffer); - PhDereferenceObject(path); - } - - PH_UNLOCK_SYMBOLS(); -} - -#ifdef _WIN64 - -NTSTATUS PhpLookupDynamicFunctionTable( - _In_ HANDLE ProcessHandle, - _In_ ULONG64 Address, - _Out_opt_ PDYNAMIC_FUNCTION_TABLE *FunctionTableAddress, - _Out_opt_ PDYNAMIC_FUNCTION_TABLE FunctionTable, - _Out_writes_bytes_opt_(OutOfProcessCallbackDllBufferSize) PWCHAR OutOfProcessCallbackDllBuffer, - _In_ ULONG OutOfProcessCallbackDllBufferSize, - _Out_opt_ PUNICODE_STRING OutOfProcessCallbackDllString - ) -{ - NTSTATUS status; - PLIST_ENTRY (NTAPI *rtlGetFunctionTableListHead)(VOID); - PLIST_ENTRY tableListHead; - LIST_ENTRY tableListHeadEntry; - PLIST_ENTRY tableListEntry; - PDYNAMIC_FUNCTION_TABLE functionTableAddress; - DYNAMIC_FUNCTION_TABLE functionTable; - ULONG count; - SIZE_T numberOfBytesRead; - ULONG i; - BOOLEAN foundNull; - - rtlGetFunctionTableListHead = PhGetModuleProcAddress(L"ntdll.dll", "RtlGetFunctionTableListHead"); - - if (!rtlGetFunctionTableListHead) - return STATUS_PROCEDURE_NOT_FOUND; - - tableListHead = rtlGetFunctionTableListHead(); - - // Find the function table entry for this address. - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - tableListHead, - &tableListHeadEntry, - sizeof(LIST_ENTRY), - NULL - ))) - return status; - - tableListEntry = tableListHeadEntry.Flink; - count = 0; // make sure we can't be forced into an infinite loop by crafted data - - while (tableListEntry != tableListHead && count < PH_ENUM_PROCESS_MODULES_LIMIT) - { - functionTableAddress = CONTAINING_RECORD(tableListEntry, DYNAMIC_FUNCTION_TABLE, ListEntry); - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - ProcessHandle, - functionTableAddress, - &functionTable, - sizeof(DYNAMIC_FUNCTION_TABLE), - NULL - ))) - return status; - - if (Address >= functionTable.MinimumAddress && Address < functionTable.MaximumAddress) - { - if (OutOfProcessCallbackDllBuffer) - { - if (functionTable.OutOfProcessCallbackDll) - { - // Read the out-of-process callback DLL path. We don't have a length, so we'll - // just have to read as much as possible. - - memset(OutOfProcessCallbackDllBuffer, 0xff, OutOfProcessCallbackDllBufferSize); - status = NtReadVirtualMemory( - ProcessHandle, - functionTable.OutOfProcessCallbackDll, - OutOfProcessCallbackDllBuffer, - OutOfProcessCallbackDllBufferSize, - &numberOfBytesRead - ); - - if (status != STATUS_PARTIAL_COPY && !NT_SUCCESS(status)) - return status; - - foundNull = FALSE; - - for (i = 0; i < OutOfProcessCallbackDllBufferSize / sizeof(WCHAR); i++) - { - if (OutOfProcessCallbackDllBuffer[i] == 0) - { - foundNull = TRUE; - - if (OutOfProcessCallbackDllString) - { - OutOfProcessCallbackDllString->Buffer = OutOfProcessCallbackDllBuffer; - OutOfProcessCallbackDllString->Length = (USHORT)(i * sizeof(WCHAR)); - OutOfProcessCallbackDllString->MaximumLength = OutOfProcessCallbackDllString->Length; - } - - break; - } - } - - // If there was no null terminator, then we didn't read the whole string in. - // Fail the operation. - if (!foundNull) - return STATUS_BUFFER_OVERFLOW; - } - else - { - OutOfProcessCallbackDllBuffer[0] = 0; - - if (OutOfProcessCallbackDllString) - { - OutOfProcessCallbackDllString->Buffer = NULL; - OutOfProcessCallbackDllString->Length = 0; - OutOfProcessCallbackDllString->MaximumLength = 0; - } - } - } - - if (FunctionTableAddress) - *FunctionTableAddress = functionTableAddress; - - if (FunctionTable) - *FunctionTable = functionTable; - - return STATUS_SUCCESS; - } - - tableListEntry = functionTable.ListEntry.Flink; - count++; - } - - return STATUS_NOT_FOUND; -} - -PRUNTIME_FUNCTION PhpLookupFunctionEntry( - _In_ PRUNTIME_FUNCTION Functions, - _In_ ULONG NumberOfFunctions, - _In_ BOOLEAN Sorted, - _In_ ULONG64 RelativeControlPc - ) -{ - LONG low; - LONG high; - ULONG i; - - if (Sorted) - { - if (NumberOfFunctions == 0) - return NULL; - - low = 0; - high = NumberOfFunctions - 1; - - do - { - i = (low + high) / 2; - - if (RelativeControlPc < Functions[i].BeginAddress) - high = i - 1; - else if (RelativeControlPc >= Functions[i].EndAddress) - low = i + 1; - else - return &Functions[i]; - } while (low <= high); - } - else - { - for (i = 0; i < NumberOfFunctions; i++) - { - if (RelativeControlPc >= Functions[i].BeginAddress && RelativeControlPc < Functions[i].EndAddress) - return &Functions[i]; - } - } - - return NULL; -} - -NTSTATUS PhpAccessCallbackFunctionTable( - _In_ HANDLE ProcessHandle, - _In_ PVOID FunctionTableAddress, - _In_ PUNICODE_STRING OutOfProcessCallbackDllString, - _Out_ PRUNTIME_FUNCTION *Functions, - _Out_ PULONG NumberOfFunctions - ) -{ - static PH_STRINGREF knownFunctionTableDllsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\KnownFunctionTableDlls"); - - NTSTATUS status; - HANDLE keyHandle; - ULONG returnLength; - PVOID dllHandle; - ANSI_STRING outOfProcessFunctionTableCallbackName; - POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK outOfProcessFunctionTableCallback; - - if (!OutOfProcessCallbackDllString->Buffer) - return STATUS_INVALID_PARAMETER; - - // Don't load DLLs from arbitrary locations. Check if this is a known function table DLL. - - if (!NT_SUCCESS(status = PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &knownFunctionTableDllsKeyName, - 0 - ))) - return status; - - status = NtQueryValueKey(keyHandle, OutOfProcessCallbackDllString, KeyValuePartialInformation, NULL, 0, &returnLength); - NtClose(keyHandle); - - if (status == STATUS_OBJECT_NAME_NOT_FOUND) - return STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT; - - status = LdrLoadDll(NULL, NULL, OutOfProcessCallbackDllString, &dllHandle); - - if (!NT_SUCCESS(status)) - return status; - - RtlInitAnsiString(&outOfProcessFunctionTableCallbackName, OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME); - status = LdrGetProcedureAddress(dllHandle, &outOfProcessFunctionTableCallbackName, 0, (PVOID *)&outOfProcessFunctionTableCallback); - - if (NT_SUCCESS(status)) - { - status = outOfProcessFunctionTableCallback( - ProcessHandle, - FunctionTableAddress, - NumberOfFunctions, - Functions - ); - } - - LdrUnloadDll(dllHandle); - - return status; -} - -NTSTATUS PhpAccessNormalFunctionTable( - _In_ HANDLE ProcessHandle, - _In_ PDYNAMIC_FUNCTION_TABLE FunctionTable, - _Out_ PRUNTIME_FUNCTION *Functions, - _Out_ PULONG NumberOfFunctions - ) -{ - NTSTATUS status; - SIZE_T bufferSize; - PRUNTIME_FUNCTION functions; - - // Put a reasonable limit on the number of entries we read. - if (FunctionTable->EntryCount > 0x100000) - return STATUS_BUFFER_OVERFLOW; - - bufferSize = FunctionTable->EntryCount * sizeof(RUNTIME_FUNCTION); - functions = PhAllocatePage(bufferSize, NULL); - - if (!functions) - return STATUS_NO_MEMORY; - - status = NtReadVirtualMemory(ProcessHandle, FunctionTable->FunctionTable, functions, bufferSize, NULL); - - if (NT_SUCCESS(status)) - { - *Functions = functions; - *NumberOfFunctions = FunctionTable->EntryCount; - } - else - { - PhFreePage(functions); - } - - return status; -} - -NTSTATUS PhAccessOutOfProcessFunctionEntry( - _In_ HANDLE ProcessHandle, - _In_ ULONG64 ControlPc, - _Out_ PRUNTIME_FUNCTION Function - ) -{ - NTSTATUS status; - PDYNAMIC_FUNCTION_TABLE functionTableAddress; - DYNAMIC_FUNCTION_TABLE functionTable; - WCHAR outOfProcessCallbackDll[512]; - UNICODE_STRING outOfProcessCallbackDllString; - PRUNTIME_FUNCTION functions; - ULONG numberOfFunctions; - PRUNTIME_FUNCTION function; - - if (!NT_SUCCESS(status = PhpLookupDynamicFunctionTable( - ProcessHandle, - ControlPc, - &functionTableAddress, - &functionTable, - outOfProcessCallbackDll, - sizeof(outOfProcessCallbackDll), - &outOfProcessCallbackDllString - ))) - { - return status; - } - - if (functionTable.Type == RF_CALLBACK) - { - if (!NT_SUCCESS(status = PhpAccessCallbackFunctionTable( - ProcessHandle, - functionTableAddress, - &outOfProcessCallbackDllString, - &functions, - &numberOfFunctions - ))) - { - return status; - } - - function = PhpLookupFunctionEntry(functions, numberOfFunctions, FALSE, ControlPc - functionTable.BaseAddress); - - if (function) - *Function = *function; - else - status = STATUS_NOT_FOUND; - - RtlFreeHeap(RtlProcessHeap(), 0, functions); - } - else - { - if (!NT_SUCCESS(status = PhpAccessNormalFunctionTable( - ProcessHandle, - &functionTable, - &functions, - &numberOfFunctions - ))) - { - return status; - } - - function = PhpLookupFunctionEntry(functions, numberOfFunctions, functionTable.Type == RF_SORTED, ControlPc - functionTable.BaseAddress); - - if (function) - *Function = *function; - else - status = STATUS_NOT_FOUND; - - PhFreePage(functions); - } - - return status; -} - -#endif - -ULONG64 __stdcall PhGetModuleBase64( - _In_ HANDLE hProcess, - _In_ DWORD64 dwAddr - ) -{ - ULONG64 base; -#ifdef _WIN64 - DYNAMIC_FUNCTION_TABLE functionTable; -#endif - -#ifdef _WIN64 - if (NT_SUCCESS(PhpLookupDynamicFunctionTable( - hProcess, - dwAddr, - NULL, - &functionTable, - NULL, - 0, - NULL - ))) - { - base = functionTable.BaseAddress; - } - else - { - base = 0; - } -#else - base = 0; -#endif - - if (base == 0 && SymGetModuleBase64_I) - base = SymGetModuleBase64_I(hProcess, dwAddr); - - return base; -} - -PVOID __stdcall PhFunctionTableAccess64( - _In_ HANDLE hProcess, - _In_ DWORD64 AddrBase - ) -{ -#ifdef _WIN64 - static RUNTIME_FUNCTION lastRuntimeFunction; -#endif - - PVOID entry; - -#ifdef _WIN64 - if (NT_SUCCESS(PhAccessOutOfProcessFunctionEntry(hProcess, AddrBase, &lastRuntimeFunction))) - entry = &lastRuntimeFunction; - else - entry = NULL; -#else - entry = NULL; -#endif - - if (!entry && SymFunctionTableAccess64_I) - entry = SymFunctionTableAccess64_I(hProcess, AddrBase); - - return entry; -} - -BOOLEAN PhStackWalk( - _In_ ULONG MachineType, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ThreadHandle, - _Inout_ LPSTACKFRAME64 StackFrame, - _Inout_ PVOID ContextRecord, - _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, - _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, - _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, - _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress - ) -{ - BOOLEAN result; - - PhpRegisterSymbolProvider(SymbolProvider); - - if (!StackWalk64_I) - return FALSE; - - if (!FunctionTableAccessRoutine) - { - if (MachineType == IMAGE_FILE_MACHINE_AMD64) - FunctionTableAccessRoutine = PhFunctionTableAccess64; - else - FunctionTableAccessRoutine = SymFunctionTableAccess64_I; - } - - if (!GetModuleBaseRoutine) - { - if (MachineType == IMAGE_FILE_MACHINE_AMD64) - GetModuleBaseRoutine = PhGetModuleBase64; - else - GetModuleBaseRoutine = SymGetModuleBase64_I; - } - - PH_LOCK_SYMBOLS(); - result = StackWalk64_I( - MachineType, - ProcessHandle, - ThreadHandle, - StackFrame, - ContextRecord, - ReadMemoryRoutine, - FunctionTableAccessRoutine, - GetModuleBaseRoutine, - TranslateAddress - ); - PH_UNLOCK_SYMBOLS(); - - return result; -} - -BOOLEAN PhWriteMiniDumpProcess( - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId, - _In_ HANDLE FileHandle, - _In_ MINIDUMP_TYPE DumpType, - _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam - ) -{ - PhpRegisterSymbolProvider(NULL); - - if (!MiniDumpWriteDump_I) - { - SetLastError(ERROR_PROC_NOT_FOUND); - return FALSE; - } - - return MiniDumpWriteDump_I( - ProcessHandle, - HandleToUlong(ProcessId), - FileHandle, - DumpType, - ExceptionParam, - UserStreamParam, - CallbackParam - ); -} - -/** - * Converts a STACKFRAME64 structure to a PH_THREAD_STACK_FRAME structure. - * - * \param StackFrame64 A pointer to the STACKFRAME64 structure to convert. - * \param Flags Flags to set in the resulting structure. - * \param ThreadStackFrame A pointer to the resulting PH_THREAD_STACK_FRAME structure. - */ -VOID PhpConvertStackFrame( - _In_ STACKFRAME64 *StackFrame64, - _In_ ULONG Flags, - _Out_ PPH_THREAD_STACK_FRAME ThreadStackFrame - ) -{ - ULONG i; - - ThreadStackFrame->PcAddress = (PVOID)StackFrame64->AddrPC.Offset; - ThreadStackFrame->ReturnAddress = (PVOID)StackFrame64->AddrReturn.Offset; - ThreadStackFrame->FrameAddress = (PVOID)StackFrame64->AddrFrame.Offset; - ThreadStackFrame->StackAddress = (PVOID)StackFrame64->AddrStack.Offset; - ThreadStackFrame->BStoreAddress = (PVOID)StackFrame64->AddrBStore.Offset; - - for (i = 0; i < 4; i++) - ThreadStackFrame->Params[i] = (PVOID)StackFrame64->Params[i]; - - ThreadStackFrame->Flags = Flags; - - if (StackFrame64->FuncTableEntry) - ThreadStackFrame->Flags |= PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT; -} - -/** - * Walks a thread's stack. - * - * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION, - * THREAD_GET_CONTEXT and THREAD_SUSPEND_RESUME access. The handle can have any access for kernel - * stack walking. - * \param ProcessHandle A handle to the thread's parent process. The handle must have - * PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access. If a symbol provider is being used, pass - * its process handle and specify the symbol provider in \a SymbolProvider. - * \param ClientId The client ID identifying the thread. - * \param SymbolProvider The associated symbol provider. - * \param Flags A combination of flags. - * \li \c PH_WALK_I386_STACK Walks the x86 stack. On AMD64 systems this flag walks the WOW64 stack. - * \li \c PH_WALK_AMD64_STACK Walks the AMD64 stack. On x86 systems this flag is ignored. - * \li \c PH_WALK_KERNEL_STACK Walks the kernel stack. This flag is ignored if there is no active - * KProcessHacker connection. - * \param Callback A callback function which is executed for each stack frame. - * \param Context A user-defined value to pass to the callback function. - */ -NTSTATUS PhWalkThreadStack( - _In_ HANDLE ThreadHandle, - _In_opt_ HANDLE ProcessHandle, - _In_opt_ PCLIENT_ID ClientId, - _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG Flags, - _In_ PPH_WALK_THREAD_STACK_CALLBACK Callback, - _In_opt_ PVOID Context - ) -{ - NTSTATUS status = STATUS_SUCCESS; - BOOLEAN suspended = FALSE; - BOOLEAN processOpened = FALSE; - BOOLEAN isCurrentThread = FALSE; - BOOLEAN isSystemThread = FALSE; - THREAD_BASIC_INFORMATION basicInfo; - - // Open a handle to the process if we weren't given one. - if (!ProcessHandle) - { - if (KphIsConnected() || !ClientId) - { - if (!NT_SUCCESS(status = PhOpenThreadProcess( - ThreadHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - &ProcessHandle - ))) - return status; - } - else - { - if (!NT_SUCCESS(status = PhOpenProcess( - &ProcessHandle, - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - ClientId->UniqueProcess - ))) - return status; - } - - processOpened = TRUE; - } - - // Determine if the caller specified the current thread. - if (ClientId) - { - if (ClientId->UniqueThread == NtCurrentTeb()->ClientId.UniqueThread) - isCurrentThread = TRUE; - if (ClientId->UniqueProcess == SYSTEM_PROCESS_ID) - isSystemThread = TRUE; - } - else - { - if (ThreadHandle == NtCurrentThread()) - { - isCurrentThread = TRUE; - } - else if (NT_SUCCESS(PhGetThreadBasicInformation(ThreadHandle, &basicInfo))) - { - if (basicInfo.ClientId.UniqueThread == NtCurrentTeb()->ClientId.UniqueThread) - isCurrentThread = TRUE; - if (basicInfo.ClientId.UniqueProcess == SYSTEM_PROCESS_ID) - isSystemThread = TRUE; - } - } - - // Make sure this isn't a kernel-mode thread. - if (!isSystemThread) - { - PVOID startAddress; - - if (NT_SUCCESS(NtQueryInformationThread(ThreadHandle, ThreadQuerySetWin32StartAddress, - &startAddress, sizeof(PVOID), NULL))) - { - if ((ULONG_PTR)startAddress > PhSystemBasicInformation.MaximumUserModeAddress) - isSystemThread = TRUE; - } - } - - // Suspend the thread to avoid inaccurate results. Don't suspend if we're walking the stack of - // the current thread or this is a kernel-mode thread. - if (!isCurrentThread && !isSystemThread) - { - if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL))) - suspended = TRUE; - } - - // Kernel stack walk. - if ((Flags & PH_WALK_KERNEL_STACK) && KphIsConnected()) - { - PVOID stack[256 - 2]; // See MAX_STACK_DEPTH - ULONG capturedFrames; - ULONG i; - - if (NT_SUCCESS(KphCaptureStackBackTraceThread( - ThreadHandle, - 1, - sizeof(stack) / sizeof(PVOID), - stack, - &capturedFrames, - NULL - ))) - { - PH_THREAD_STACK_FRAME threadStackFrame; - - memset(&threadStackFrame, 0, sizeof(PH_THREAD_STACK_FRAME)); - - for (i = 0; i < capturedFrames; i++) - { - threadStackFrame.PcAddress = stack[i]; - threadStackFrame.Flags = PH_THREAD_STACK_FRAME_KERNEL; - - if (!Callback(&threadStackFrame, Context)) - { - goto ResumeExit; - } - } - } - } - -#ifdef _WIN64 - if (Flags & PH_WALK_AMD64_STACK) - { - STACKFRAME64 stackFrame; - PH_THREAD_STACK_FRAME threadStackFrame; - CONTEXT context; - - context.ContextFlags = CONTEXT_ALL; - - if (!NT_SUCCESS(status = NtGetContextThread( - ThreadHandle, - &context - ))) - goto SkipAmd64Stack; - - memset(&stackFrame, 0, sizeof(STACKFRAME64)); - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrPC.Offset = context.Rip; - stackFrame.AddrStack.Mode = AddrModeFlat; - stackFrame.AddrStack.Offset = context.Rsp; - stackFrame.AddrFrame.Mode = AddrModeFlat; - stackFrame.AddrFrame.Offset = context.Rbp; - - while (TRUE) - { - if (!PhStackWalk( - IMAGE_FILE_MACHINE_AMD64, - ProcessHandle, - ThreadHandle, - &stackFrame, - &context, - SymbolProvider, - NULL, - NULL, - NULL, - NULL - )) - break; - - // If we have an invalid instruction pointer, break. - if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1) - break; - - // Convert the stack frame and execute the callback. - - PhpConvertStackFrame(&stackFrame, PH_THREAD_STACK_FRAME_AMD64, &threadStackFrame); - - if (!Callback(&threadStackFrame, Context)) - goto ResumeExit; - } - } - -SkipAmd64Stack: -#endif - - // x86/WOW64 stack walk. - if (Flags & PH_WALK_I386_STACK) - { - STACKFRAME64 stackFrame; - PH_THREAD_STACK_FRAME threadStackFrame; -#ifndef _WIN64 - CONTEXT context; - - context.ContextFlags = CONTEXT_ALL; - - if (!NT_SUCCESS(status = NtGetContextThread( - ThreadHandle, - &context - ))) - goto SkipI386Stack; -#else - WOW64_CONTEXT context; - - context.ContextFlags = WOW64_CONTEXT_ALL; - - if (!NT_SUCCESS(status = NtQueryInformationThread( - ThreadHandle, - ThreadWow64Context, - &context, - sizeof(WOW64_CONTEXT), - NULL - ))) - goto SkipI386Stack; -#endif - - memset(&stackFrame, 0, sizeof(STACKFRAME64)); - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrPC.Offset = context.Eip; - stackFrame.AddrStack.Mode = AddrModeFlat; - stackFrame.AddrStack.Offset = context.Esp; - stackFrame.AddrFrame.Mode = AddrModeFlat; - stackFrame.AddrFrame.Offset = context.Ebp; - - while (TRUE) - { - if (!PhStackWalk( - IMAGE_FILE_MACHINE_I386, - ProcessHandle, - ThreadHandle, - &stackFrame, - &context, - SymbolProvider, - NULL, - NULL, - NULL, - NULL - )) - break; - - // If we have an invalid instruction pointer, break. - if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1) - break; - - // Convert the stack frame and execute the callback. - - PhpConvertStackFrame(&stackFrame, PH_THREAD_STACK_FRAME_I386, &threadStackFrame); - - if (!Callback(&threadStackFrame, Context)) - goto ResumeExit; - - // (x86 only) Allow the user to change Eip, Esp and Ebp. - context.Eip = PtrToUlong(threadStackFrame.PcAddress); - stackFrame.AddrPC.Offset = PtrToUlong(threadStackFrame.PcAddress); - context.Ebp = PtrToUlong(threadStackFrame.FrameAddress); - stackFrame.AddrFrame.Offset = PtrToUlong(threadStackFrame.FrameAddress); - context.Esp = PtrToUlong(threadStackFrame.StackAddress); - stackFrame.AddrStack.Offset = PtrToUlong(threadStackFrame.StackAddress); - } - } - -SkipI386Stack: - -ResumeExit: - if (suspended) - NtResumeThread(ThreadHandle, NULL); - - if (processOpened) - NtClose(ProcessHandle); - - return status; -} +/* + * Process Hacker - + * symbol provider + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +typedef struct _PH_SYMBOL_MODULE +{ + LIST_ENTRY ListEntry; + PH_AVL_LINKS Links; + ULONG64 BaseAddress; + ULONG Size; + PPH_STRING FileName; + ULONG BaseNameIndex; +} PH_SYMBOL_MODULE, *PPH_SYMBOL_MODULE; + +VOID NTAPI PhpSymbolProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID PhpRegisterSymbolProvider( + _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider + ); + +VOID PhpFreeSymbolModule( + _In_ PPH_SYMBOL_MODULE SymbolModule + ); + +LONG NTAPI PhpSymbolModuleCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ); + +PPH_OBJECT_TYPE PhSymbolProviderType = NULL; +PH_CALLBACK_DECLARE(PhSymbolEventCallback); +static PH_INITONCE PhSymInitOnce = PH_INITONCE_INIT; +static HANDLE PhNextFakeHandle = (HANDLE)0; +static PH_FAST_LOCK PhSymMutex = PH_FAST_LOCK_INIT; +#define PH_LOCK_SYMBOLS() PhAcquireFastLockExclusive(&PhSymMutex) +#define PH_UNLOCK_SYMBOLS() PhReleaseFastLockExclusive(&PhSymMutex) + +_SymInitializeW SymInitializeW_I = NULL; +_SymCleanup SymCleanup_I = NULL; +_SymEnumSymbolsW SymEnumSymbolsW_I = NULL; +_SymFromAddrW SymFromAddrW_I = NULL; +_SymFromNameW SymFromNameW_I = NULL; +_SymGetLineFromAddrW64 SymGetLineFromAddrW64_I = NULL; +_SymLoadModuleExW SymLoadModuleExW_I = NULL; +_SymGetOptions SymGetOptions_I = NULL; +_SymSetOptions SymSetOptions_I = NULL; +_SymGetSearchPathW SymGetSearchPathW_I = NULL; +_SymSetSearchPathW SymSetSearchPathW_I = NULL; +_SymUnloadModule64 SymUnloadModule64_I = NULL; +_SymFunctionTableAccess64 SymFunctionTableAccess64_I = NULL; +_SymGetModuleBase64 SymGetModuleBase64_I = NULL; +_SymRegisterCallbackW64 SymRegisterCallbackW64_I = NULL; +_StackWalk64 StackWalk64_I = NULL; +_MiniDumpWriteDump MiniDumpWriteDump_I = NULL; +_SymbolServerGetOptions SymbolServerGetOptions = NULL; +_SymbolServerSetOptions SymbolServerSetOptions = NULL; +_UnDecorateSymbolNameW UnDecorateSymbolNameW_I = NULL; + +PPH_SYMBOL_PROVIDER PhCreateSymbolProvider( + _In_opt_ HANDLE ProcessId + ) +{ + static PH_INITONCE symbolProviderInitOnce = PH_INITONCE_INIT; + PPH_SYMBOL_PROVIDER symbolProvider; + + if (PhBeginInitOnce(&symbolProviderInitOnce)) + { + PhSymbolProviderType = PhCreateObjectType(L"SymbolProvider", 0, PhpSymbolProviderDeleteProcedure); + PhEndInitOnce(&symbolProviderInitOnce); + } + + symbolProvider = PhCreateObject(sizeof(PH_SYMBOL_PROVIDER), PhSymbolProviderType); + memset(symbolProvider, 0, sizeof(PH_SYMBOL_PROVIDER)); + InitializeListHead(&symbolProvider->ModulesListHead); + PhInitializeQueuedLock(&symbolProvider->ModulesListLock); + PhInitializeAvlTree(&symbolProvider->ModulesSet, PhpSymbolModuleCompareFunction); + PhInitializeInitOnce(&symbolProvider->InitOnce); + + if (ProcessId) + { + static ACCESS_MASK accesses[] = + { + //STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff, // pre-Vista full access + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + MAXIMUM_ALLOWED + }; + + // Try to open the process with many different accesses. + // This handle will be re-used when walking stacks, and doing various other things. + for (ULONG i = 0; i < sizeof(accesses) / sizeof(ACCESS_MASK); i++) + { + if (NT_SUCCESS(PhOpenProcess(&symbolProvider->ProcessHandle, accesses[i], ProcessId))) + { + symbolProvider->IsRealHandle = TRUE; + break; + } + } + } + + if (!symbolProvider->IsRealHandle) + { + HANDLE fakeHandle; + + // Just generate a fake handle. + fakeHandle = (HANDLE)_InterlockedExchangeAddPointer((PLONG_PTR)&PhNextFakeHandle, 4); + // Add one to make sure it isn't divisible by 4 (so it can't be mistaken for a real handle). + symbolProvider->ProcessHandle = (HANDLE)((ULONG_PTR)fakeHandle + 1); + } + + return symbolProvider; +} + +VOID NTAPI PhpSymbolProviderDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_SYMBOL_PROVIDER symbolProvider = (PPH_SYMBOL_PROVIDER)Object; + PLIST_ENTRY listEntry; + + if (SymCleanup_I) + { + PH_LOCK_SYMBOLS(); + + if (symbolProvider->IsRegistered) + SymCleanup_I(symbolProvider->ProcessHandle); + + PH_UNLOCK_SYMBOLS(); + } + + listEntry = symbolProvider->ModulesListHead.Flink; + + while (listEntry != &symbolProvider->ModulesListHead) + { + PPH_SYMBOL_MODULE module; + + module = CONTAINING_RECORD(listEntry, PH_SYMBOL_MODULE, ListEntry); + listEntry = listEntry->Flink; + + PhpFreeSymbolModule(module); + } + + if (symbolProvider->IsRealHandle) NtClose(symbolProvider->ProcessHandle); +} + +BOOL CALLBACK PhpSymbolCallbackFunction( + _In_ HANDLE ProcessHandle, + _In_ ULONG ActionCode, + _In_opt_ ULONG64 CallbackData, + _In_opt_ ULONG64 UserContext + ) +{ + PPH_SYMBOL_PROVIDER symbolProvider = (PPH_SYMBOL_PROVIDER)UserContext; + + if (!IsListEmpty(&PhSymbolEventCallback.ListHead)) + { + PH_SYMBOL_EVENT_DATA data; + + memset(&data, 0, sizeof(PH_SYMBOL_EVENT_DATA)); + data.ActionCode = ActionCode; + data.ProcessHandle = ProcessHandle; + data.SymbolProvider = symbolProvider; + data.EventData = (PVOID)CallbackData; + + PhInvokeCallback(&PhSymbolEventCallback, &data); + } + + if (ActionCode == CBA_DEFERRED_SYMBOL_LOAD_CANCEL) + { + if (symbolProvider->Terminating) // HACK + return TRUE; + } + + return FALSE; +} + +VOID PhpSymbolProviderCompleteInitialization( + VOID + ) +{ +#ifdef _WIN64 + static PH_STRINGREF windowsKitsRootKeyName = PH_STRINGREF_INIT(L"Software\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots"); +#else + static PH_STRINGREF windowsKitsRootKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows Kits\\Installed Roots"); +#endif + static PH_STRINGREF dbghelpFileName = PH_STRINGREF_INIT(L"dbghelp.dll"); + static PH_STRINGREF symsrvFileName = PH_STRINGREF_INIT(L"symsrv.dll"); + PVOID dbghelpHandle; + PVOID symsrvHandle; + HANDLE keyHandle; + + if (PhFindLoaderEntry(NULL, NULL, &dbghelpFileName) && + PhFindLoaderEntry(NULL, NULL, &symsrvFileName)) + { + return; + } + + dbghelpHandle = NULL; + symsrvHandle = NULL; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &windowsKitsRootKeyName, + 0 + ))) + { + PPH_STRING winsdkPath; + PPH_STRING dbghelpName; + PPH_STRING symsrvName; + + winsdkPath = PhQueryRegistryString(keyHandle, L"KitsRoot10"); // Windows 10 SDK + + if (PhIsNullOrEmptyString(winsdkPath)) + PhMoveReference(&winsdkPath, PhQueryRegistryString(keyHandle, L"KitsRoot81")); // Windows 8.1 SDK + + if (PhIsNullOrEmptyString(winsdkPath)) + PhMoveReference(&winsdkPath, PhQueryRegistryString(keyHandle, L"KitsRoot")); // Windows 8 SDK + + if (!PhIsNullOrEmptyString(winsdkPath)) + { +#ifdef _WIN64 + PhMoveReference(&winsdkPath, PhConcatStringRefZ(&winsdkPath->sr, L"\\Debuggers\\x64\\")); +#else + PhMoveReference(&winsdkPath, PhConcatStringRefZ(&winsdkPath->sr, L"\\Debuggers\\x86\\")); +#endif + } + + if (winsdkPath) + { + if (dbghelpName = PhConcatStringRef2(&winsdkPath->sr, &dbghelpFileName)) + { + dbghelpHandle = LoadLibrary(dbghelpName->Buffer); + PhDereferenceObject(dbghelpName); + } + + if (symsrvName = PhConcatStringRef2(&winsdkPath->sr, &symsrvFileName)) + { + symsrvHandle = LoadLibrary(symsrvName->Buffer); + PhDereferenceObject(symsrvName); + } + + PhDereferenceObject(winsdkPath); + } + + NtClose(keyHandle); + } + + if (!dbghelpHandle) + dbghelpHandle = LoadLibrary(L"dbghelp.dll"); + + if (!symsrvHandle) + symsrvHandle = LoadLibrary(L"symsrv.dll"); + + if (dbghelpHandle) + { + SymInitializeW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymInitializeW", 0); + SymCleanup_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymCleanup", 0); + SymEnumSymbolsW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0); + SymFromAddrW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymFromAddrW", 0); + SymFromNameW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymFromNameW", 0); + SymGetLineFromAddrW64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetLineFromAddrW64", 0); + SymLoadModuleExW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0); + SymGetOptions_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetOptions", 0); + SymSetOptions_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetOptions", 0); + SymGetSearchPathW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetSearchPathW", 0); + SymSetSearchPathW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetSearchPathW", 0); + SymUnloadModule64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymUnloadModule64", 0); + SymFunctionTableAccess64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymFunctionTableAccess64", 0); + SymGetModuleBase64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetModuleBase64", 0); + SymRegisterCallbackW64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymRegisterCallbackW64", 0); + StackWalk64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "StackWalk64", 0); + MiniDumpWriteDump_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "MiniDumpWriteDump", 0); + UnDecorateSymbolNameW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "UnDecorateSymbolNameW", 0); + } + + if (symsrvHandle) + { + SymbolServerGetOptions = PhGetDllBaseProcedureAddress(symsrvHandle, "SymbolServerGetOptions", 0); + SymbolServerSetOptions = PhGetDllBaseProcedureAddress(symsrvHandle, "SymbolServerSetOptions", 0); + } +} + +VOID PhpRegisterSymbolProvider( + _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider + ) +{ + if (PhBeginInitOnce(&PhSymInitOnce)) + { + PhpSymbolProviderCompleteInitialization(); + + if (SymGetOptions_I && SymSetOptions_I) + { + PH_LOCK_SYMBOLS(); + + SymSetOptions_I( + SymGetOptions_I() | + SYMOPT_AUTO_PUBLICS | SYMOPT_CASE_INSENSITIVE | SYMOPT_DEFERRED_LOADS | + SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_INCLUDE_32BIT_MODULES | + SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_UNDNAME // | SYMOPT_DEBUG + ); + + PH_UNLOCK_SYMBOLS(); + } + + PhEndInitOnce(&PhSymInitOnce); + } + + if (!SymbolProvider) + return; + + if (PhBeginInitOnce(&SymbolProvider->InitOnce)) + { + if (SymInitializeW_I) + { + PH_LOCK_SYMBOLS(); + + SymInitializeW_I(SymbolProvider->ProcessHandle, NULL, FALSE); + + if (SymRegisterCallbackW64_I) + SymRegisterCallbackW64_I(SymbolProvider->ProcessHandle, PhpSymbolCallbackFunction, (ULONG64)SymbolProvider); + + PH_UNLOCK_SYMBOLS(); + + SymbolProvider->IsRegistered = TRUE; + } + + PhEndInitOnce(&SymbolProvider->InitOnce); + } +} + +VOID PhpFreeSymbolModule( + _In_ PPH_SYMBOL_MODULE SymbolModule + ) +{ + if (SymbolModule->FileName) PhDereferenceObject(SymbolModule->FileName); + + PhFree(SymbolModule); +} + +static LONG NTAPI PhpSymbolModuleCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ) +{ + PPH_SYMBOL_MODULE symbolModule1 = CONTAINING_RECORD(Links1, PH_SYMBOL_MODULE, Links); + PPH_SYMBOL_MODULE symbolModule2 = CONTAINING_RECORD(Links2, PH_SYMBOL_MODULE, Links); + + return uint64cmp(symbolModule1->BaseAddress, symbolModule2->BaseAddress); +} + +BOOLEAN PhGetLineFromAddress( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address, + _Out_ PPH_STRING *FileName, + _Out_opt_ PULONG Displacement, + _Out_opt_ PPH_SYMBOL_LINE_INFORMATION Information + ) +{ + IMAGEHLP_LINEW64 line; + BOOL result; + ULONG displacement; + PPH_STRING fileName; + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!SymGetLineFromAddrW64_I) + return FALSE; + + line.SizeOfStruct = sizeof(IMAGEHLP_LINEW64); + + PH_LOCK_SYMBOLS(); + + result = SymGetLineFromAddrW64_I( + SymbolProvider->ProcessHandle, + Address, + &displacement, + &line + ); + + PH_UNLOCK_SYMBOLS(); + + if (result) + fileName = PhCreateString(line.FileName); + + if (!result) + return FALSE; + + *FileName = fileName; + + if (Displacement) + *Displacement = displacement; + + if (Information) + { + Information->LineNumber = line.LineNumber; + Information->Address = line.Address; + } + + return TRUE; +} + +ULONG64 PhGetModuleFromAddress( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address, + _Out_opt_ PPH_STRING *FileName + ) +{ + PH_SYMBOL_MODULE lookupModule; + PPH_AVL_LINKS links; + PPH_SYMBOL_MODULE module; + PPH_STRING foundFileName; + ULONG64 foundBaseAddress; + + foundFileName = NULL; + foundBaseAddress = 0; + + PhAcquireQueuedLockShared(&SymbolProvider->ModulesListLock); + + // Do an approximate search on the modules set to locate the module with the largest + // base address that is still smaller than the given address. + lookupModule.BaseAddress = Address; + links = PhUpperDualBoundElementAvlTree(&SymbolProvider->ModulesSet, &lookupModule.Links); + + if (links) + { + module = CONTAINING_RECORD(links, PH_SYMBOL_MODULE, Links); + + if (Address < module->BaseAddress + module->Size) + { + PhSetReference(&foundFileName, module->FileName); + foundBaseAddress = module->BaseAddress; + } + } + + PhReleaseQueuedLockShared(&SymbolProvider->ModulesListLock); + + if (foundFileName) + { + if (FileName) + { + *FileName = foundFileName; + } + else + { + PhDereferenceObject(foundFileName); + } + } + + return foundBaseAddress; +} + +VOID PhpSymbolInfoAnsiToUnicode( + _Out_ PSYMBOL_INFOW SymbolInfoW, + _In_ PSYMBOL_INFO SymbolInfoA + ) +{ + SymbolInfoW->TypeIndex = SymbolInfoA->TypeIndex; + SymbolInfoW->Index = SymbolInfoA->Index; + SymbolInfoW->Size = SymbolInfoA->Size; + SymbolInfoW->ModBase = SymbolInfoA->ModBase; + SymbolInfoW->Flags = SymbolInfoA->Flags; + SymbolInfoW->Value = SymbolInfoA->Value; + SymbolInfoW->Address = SymbolInfoA->Address; + SymbolInfoW->Register = SymbolInfoA->Register; + SymbolInfoW->Scope = SymbolInfoA->Scope; + SymbolInfoW->Tag = SymbolInfoA->Tag; + SymbolInfoW->NameLen = 0; + + if (SymbolInfoA->NameLen != 0 && SymbolInfoW->MaxNameLen != 0) + { + ULONG copyCount; + + copyCount = min(SymbolInfoA->NameLen, SymbolInfoW->MaxNameLen - 1); + + if (PhCopyStringZFromMultiByte( + SymbolInfoA->Name, + copyCount, + SymbolInfoW->Name, + SymbolInfoW->MaxNameLen, + NULL + )) + { + SymbolInfoW->NameLen = copyCount; + } + } +} + +PPH_STRING PhGetSymbolFromAddress( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address, + _Out_opt_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel, + _Out_opt_ PPH_STRING *FileName, + _Out_opt_ PPH_STRING *SymbolName, + _Out_opt_ PULONG64 Displacement + ) +{ + PSYMBOL_INFOW symbolInfo; + ULONG nameLength; + PPH_STRING symbol = NULL; + PH_SYMBOL_RESOLVE_LEVEL resolveLevel; + ULONG64 displacement; + PPH_STRING modFileName = NULL; + PPH_STRING modBaseName = NULL; + ULONG64 modBase = 0; + PPH_STRING symbolName = NULL; + + if (Address == 0) + { + if (ResolveLevel) *ResolveLevel = PhsrlInvalid; + if (FileName) *FileName = NULL; + if (SymbolName) *SymbolName = NULL; + if (Displacement) *Displacement = 0; + + return NULL; + } + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!SymFromAddrW_I) + return NULL; + + symbolInfo = PhAllocateZero(FIELD_OFFSET(SYMBOL_INFOW, Name) + PH_MAX_SYMBOL_NAME_LEN * sizeof(WCHAR)); + symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); + symbolInfo->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN; + + // Get the symbol name. + + PH_LOCK_SYMBOLS(); + + // Note that we don't care whether this call succeeds or not, based on the assumption that it + // will not write to the symbolInfo structure if it fails. We've already zeroed the structure, + // so we can deal with it. + + SymFromAddrW_I( + SymbolProvider->ProcessHandle, + Address, + &displacement, + symbolInfo + ); + nameLength = symbolInfo->NameLen; + + if (nameLength + 1 > PH_MAX_SYMBOL_NAME_LEN) + { + PhFree(symbolInfo); + symbolInfo = PhAllocateZero(FIELD_OFFSET(SYMBOL_INFOW, Name) + nameLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); + symbolInfo->MaxNameLen = nameLength + 1; + + SymFromAddrW_I( + SymbolProvider->ProcessHandle, + Address, + &displacement, + symbolInfo + ); + } + + PH_UNLOCK_SYMBOLS(); + + // Find the module name. + + if (symbolInfo->ModBase == 0) + { + modBase = PhGetModuleFromAddress( + SymbolProvider, + Address, + &modFileName + ); + } + else + { + PH_SYMBOL_MODULE lookupSymbolModule; + PPH_AVL_LINKS existingLinks; + PPH_SYMBOL_MODULE symbolModule; + + lookupSymbolModule.BaseAddress = symbolInfo->ModBase; + + PhAcquireQueuedLockShared(&SymbolProvider->ModulesListLock); + + existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links); + + if (existingLinks) + { + symbolModule = CONTAINING_RECORD(existingLinks, PH_SYMBOL_MODULE, Links); + PhSetReference(&modFileName, symbolModule->FileName); + } + + PhReleaseQueuedLockShared(&SymbolProvider->ModulesListLock); + } + + // If we don't have a module name, return an address. + if (!modFileName) + { + resolveLevel = PhsrlAddress; + symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * sizeof(WCHAR)); + PhPrintPointer(symbol->Buffer, (PVOID)Address); + PhTrimToNullTerminatorString(symbol); + + goto CleanupExit; + } + + modBaseName = PhGetBaseName(modFileName); + + // If we have a module name but not a symbol name, return the module plus an offset: + // module+offset. + + if (symbolInfo->NameLen == 0) + { + PH_FORMAT format[3]; + + resolveLevel = PhsrlModule; + + PhInitFormatSR(&format[0], modBaseName->sr); + PhInitFormatS(&format[1], L"+0x"); + PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase)); + symbol = PhFormat(format, 3, modBaseName->Length + 6 + 32); + + goto CleanupExit; + } + + // If we have everything, return the full symbol name: module!symbol+offset. + + symbolName = PhCreateStringEx(symbolInfo->Name, symbolInfo->NameLen * sizeof(WCHAR)); + PhTrimToNullTerminatorString(symbolName); // SymFromAddr doesn't give us the correct name length for vm protected binaries (dmex) + resolveLevel = PhsrlFunction; + + if (displacement == 0) + { + PH_FORMAT format[3]; + + PhInitFormatSR(&format[0], modBaseName->sr); + PhInitFormatC(&format[1], '!'); + PhInitFormatSR(&format[2], symbolName->sr); + + symbol = PhFormat(format, 3, modBaseName->Length + 2 + symbolName->Length); + } + else + { + PH_FORMAT format[5]; + + PhInitFormatSR(&format[0], modBaseName->sr); + PhInitFormatC(&format[1], '!'); + PhInitFormatSR(&format[2], symbolName->sr); + PhInitFormatS(&format[3], L"+0x"); + PhInitFormatIX(&format[4], (ULONG_PTR)displacement); + + symbol = PhFormat(format, 5, modBaseName->Length + 2 + symbolName->Length + 6 + 32); + } + +CleanupExit: + + if (ResolveLevel) + *ResolveLevel = resolveLevel; + if (FileName) + PhSetReference(FileName, modFileName); + if (SymbolName) + PhSetReference(SymbolName, symbolName); + if (Displacement) + *Displacement = displacement; + + PhClearReference(&modFileName); + PhClearReference(&modBaseName); + PhClearReference(&symbolName); + PhFree(symbolInfo); + + return symbol; +} + +BOOLEAN PhGetSymbolFromName( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR Name, + _Out_ PPH_SYMBOL_INFORMATION Information + ) +{ + PSYMBOL_INFOW symbolInfo; + UCHAR symbolInfoBuffer[FIELD_OFFSET(SYMBOL_INFOW, Name) + PH_MAX_SYMBOL_NAME_LEN * sizeof(WCHAR)]; + BOOL result; + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!SymFromNameW_I) + return FALSE; + + symbolInfo = (PSYMBOL_INFOW)symbolInfoBuffer; + memset(symbolInfo, 0, sizeof(SYMBOL_INFOW)); + symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW); + symbolInfo->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN; + + // Get the symbol information. + + PH_LOCK_SYMBOLS(); + + result = SymFromNameW_I( + SymbolProvider->ProcessHandle, + Name, + symbolInfo + ); + + PH_UNLOCK_SYMBOLS(); + + if (!result) + return FALSE; + + Information->Address = symbolInfo->Address; + Information->ModuleBase = symbolInfo->ModBase; + Information->Index = symbolInfo->Index; + Information->Size = symbolInfo->Size; + + return TRUE; +} + +BOOLEAN PhLoadModuleSymbolProvider( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR FileName, + _In_ ULONG64 BaseAddress, + _In_ ULONG Size + ) +{ + ULONG64 baseAddress; + PPH_SYMBOL_MODULE symbolModule = NULL; + PPH_AVL_LINKS existingLinks; + PH_SYMBOL_MODULE lookupSymbolModule; + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!SymLoadModuleExW_I) + return FALSE; + + // Check for duplicates. It is better to do this before calling SymLoadModuleExW, because it + // seems to force symbol loading when it is called twice on the same module even if deferred + // loading is enabled. + PhAcquireQueuedLockExclusive(&SymbolProvider->ModulesListLock); + lookupSymbolModule.BaseAddress = BaseAddress; + existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links); + PhReleaseQueuedLockExclusive(&SymbolProvider->ModulesListLock); + + if (existingLinks) + return TRUE; + + PH_LOCK_SYMBOLS(); + + baseAddress = SymLoadModuleExW_I( + SymbolProvider->ProcessHandle, + NULL, + FileName, + NULL, + BaseAddress, + Size, + NULL, + 0 + ); + + PH_UNLOCK_SYMBOLS(); + + // Add the module to the list, even if we couldn't load symbols for the module. + + PhAcquireQueuedLockExclusive(&SymbolProvider->ModulesListLock); + + // Check for duplicates again. + lookupSymbolModule.BaseAddress = BaseAddress; + existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links); + + if (!existingLinks) + { + symbolModule = PhAllocate(sizeof(PH_SYMBOL_MODULE)); + symbolModule->BaseAddress = BaseAddress; + symbolModule->Size = Size; + symbolModule->FileName = PhGetFullPath(FileName, &symbolModule->BaseNameIndex); + + existingLinks = PhAddElementAvlTree(&SymbolProvider->ModulesSet, &symbolModule->Links); + assert(!existingLinks); + InsertTailList(&SymbolProvider->ModulesListHead, &symbolModule->ListEntry); + } + + PhReleaseQueuedLockExclusive(&SymbolProvider->ModulesListLock); + + if (!baseAddress) + { + if (GetLastError() != ERROR_SUCCESS) + return FALSE; + else + return TRUE; + } + + return TRUE; +} + +VOID PhSetOptionsSymbolProvider( + _In_ ULONG Mask, + _In_ ULONG Value + ) +{ + ULONG options; + + PhpRegisterSymbolProvider(NULL); + + if (!SymGetOptions_I || !SymSetOptions_I) + return; + + PH_LOCK_SYMBOLS(); + + options = SymGetOptions_I(); + options &= ~Mask; + options |= Value; + SymSetOptions_I(options); + + PH_UNLOCK_SYMBOLS(); +} + +VOID PhSetSearchPathSymbolProvider( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR Path + ) +{ + PhpRegisterSymbolProvider(SymbolProvider); + + if (!SymSetSearchPathW_I) + return; + + PH_LOCK_SYMBOLS(); + + SymSetSearchPathW_I(SymbolProvider->ProcessHandle, Path); + + PH_UNLOCK_SYMBOLS(); +} + +#ifdef _WIN64 + +NTSTATUS PhpLookupDynamicFunctionTable( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 Address, + _Out_opt_ PDYNAMIC_FUNCTION_TABLE *FunctionTableAddress, + _Out_opt_ PDYNAMIC_FUNCTION_TABLE FunctionTable, + _Out_writes_bytes_opt_(OutOfProcessCallbackDllBufferSize) PWCHAR OutOfProcessCallbackDllBuffer, + _In_ ULONG OutOfProcessCallbackDllBufferSize, + _Out_opt_ PUNICODE_STRING OutOfProcessCallbackDllString + ) +{ + NTSTATUS status; + PLIST_ENTRY (NTAPI *rtlGetFunctionTableListHead)(VOID); + PLIST_ENTRY tableListHead; + LIST_ENTRY tableListHeadEntry; + PLIST_ENTRY tableListEntry; + PDYNAMIC_FUNCTION_TABLE functionTableAddress; + DYNAMIC_FUNCTION_TABLE functionTable; + ULONG count; + SIZE_T numberOfBytesRead; + ULONG i; + BOOLEAN foundNull; + + rtlGetFunctionTableListHead = PhGetDllProcedureAddress(L"ntdll.dll", "RtlGetFunctionTableListHead", 0); + + if (!rtlGetFunctionTableListHead) + return STATUS_PROCEDURE_NOT_FOUND; + + tableListHead = rtlGetFunctionTableListHead(); + + // Find the function table entry for this address. + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + tableListHead, + &tableListHeadEntry, + sizeof(LIST_ENTRY), + NULL + ))) + return status; + + tableListEntry = tableListHeadEntry.Flink; + count = 0; // make sure we can't be forced into an infinite loop by crafted data + + while (tableListEntry != tableListHead && count < PH_ENUM_PROCESS_MODULES_LIMIT) + { + functionTableAddress = CONTAINING_RECORD(tableListEntry, DYNAMIC_FUNCTION_TABLE, ListEntry); + + if (!NT_SUCCESS(status = NtReadVirtualMemory( + ProcessHandle, + functionTableAddress, + &functionTable, + sizeof(DYNAMIC_FUNCTION_TABLE), + NULL + ))) + return status; + + if (Address >= functionTable.MinimumAddress && Address < functionTable.MaximumAddress) + { + if (OutOfProcessCallbackDllBuffer) + { + if (functionTable.OutOfProcessCallbackDll) + { + // Read the out-of-process callback DLL path. We don't have a length, so we'll + // just have to read as much as possible. + + memset(OutOfProcessCallbackDllBuffer, 0xff, OutOfProcessCallbackDllBufferSize); + status = NtReadVirtualMemory( + ProcessHandle, + functionTable.OutOfProcessCallbackDll, + OutOfProcessCallbackDllBuffer, + OutOfProcessCallbackDllBufferSize, + &numberOfBytesRead + ); + + if (status != STATUS_PARTIAL_COPY && !NT_SUCCESS(status)) + return status; + + foundNull = FALSE; + + for (i = 0; i < OutOfProcessCallbackDllBufferSize / sizeof(WCHAR); i++) + { + if (OutOfProcessCallbackDllBuffer[i] == 0) + { + foundNull = TRUE; + + if (OutOfProcessCallbackDllString) + { + OutOfProcessCallbackDllString->Buffer = OutOfProcessCallbackDllBuffer; + OutOfProcessCallbackDllString->Length = (USHORT)(i * sizeof(WCHAR)); + OutOfProcessCallbackDllString->MaximumLength = OutOfProcessCallbackDllString->Length; + } + + break; + } + } + + // If there was no null terminator, then we didn't read the whole string in. + // Fail the operation. + if (!foundNull) + return STATUS_BUFFER_OVERFLOW; + } + else + { + OutOfProcessCallbackDllBuffer[0] = UNICODE_NULL; + + if (OutOfProcessCallbackDllString) + { + OutOfProcessCallbackDllString->Buffer = NULL; + OutOfProcessCallbackDllString->Length = 0; + OutOfProcessCallbackDllString->MaximumLength = 0; + } + } + } + + if (FunctionTableAddress) + *FunctionTableAddress = functionTableAddress; + + if (FunctionTable) + *FunctionTable = functionTable; + + return STATUS_SUCCESS; + } + + tableListEntry = functionTable.ListEntry.Flink; + count++; + } + + return STATUS_NOT_FOUND; +} + +PRUNTIME_FUNCTION PhpLookupFunctionEntry( + _In_ PRUNTIME_FUNCTION Functions, + _In_ ULONG NumberOfFunctions, + _In_ BOOLEAN Sorted, + _In_ ULONG64 RelativeControlPc + ) +{ + LONG low; + LONG high; + ULONG i; + + if (Sorted) + { + if (NumberOfFunctions == 0) + return NULL; + + low = 0; + high = NumberOfFunctions - 1; + + do + { + i = (low + high) / 2; + + if (RelativeControlPc < Functions[i].BeginAddress) + high = i - 1; + else if (RelativeControlPc >= Functions[i].EndAddress) + low = i + 1; + else + return &Functions[i]; + } while (low <= high); + } + else + { + for (i = 0; i < NumberOfFunctions; i++) + { + if (RelativeControlPc >= Functions[i].BeginAddress && RelativeControlPc < Functions[i].EndAddress) + return &Functions[i]; + } + } + + return NULL; +} + +NTSTATUS PhpAccessCallbackFunctionTable( + _In_ HANDLE ProcessHandle, + _In_ PVOID FunctionTableAddress, + _In_ PUNICODE_STRING OutOfProcessCallbackDllString, + _Out_ PRUNTIME_FUNCTION *Functions, + _Out_ PULONG NumberOfFunctions + ) +{ + static PH_STRINGREF knownFunctionTableDllsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\KnownFunctionTableDlls"); + + NTSTATUS status; + HANDLE keyHandle; + ULONG returnLength; + PVOID dllHandle; + ANSI_STRING outOfProcessFunctionTableCallbackName; + POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK outOfProcessFunctionTableCallback; + + if (!OutOfProcessCallbackDllString->Buffer) + return STATUS_INVALID_PARAMETER; + + // Don't load DLLs from arbitrary locations. Check if this is a known function table DLL. + + if (!NT_SUCCESS(status = PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &knownFunctionTableDllsKeyName, + 0 + ))) + return status; + + status = NtQueryValueKey(keyHandle, OutOfProcessCallbackDllString, KeyValuePartialInformation, NULL, 0, &returnLength); + NtClose(keyHandle); + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + return STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT; + + status = LdrLoadDll(NULL, NULL, OutOfProcessCallbackDllString, &dllHandle); + + if (!NT_SUCCESS(status)) + return status; + + RtlInitAnsiString(&outOfProcessFunctionTableCallbackName, OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME); + status = LdrGetProcedureAddress(dllHandle, &outOfProcessFunctionTableCallbackName, 0, (PVOID *)&outOfProcessFunctionTableCallback); + + if (NT_SUCCESS(status)) + { + status = outOfProcessFunctionTableCallback( + ProcessHandle, + FunctionTableAddress, + NumberOfFunctions, + Functions + ); + } + + LdrUnloadDll(dllHandle); + + return status; +} + +NTSTATUS PhpAccessNormalFunctionTable( + _In_ HANDLE ProcessHandle, + _In_ PDYNAMIC_FUNCTION_TABLE FunctionTable, + _Out_ PRUNTIME_FUNCTION *Functions, + _Out_ PULONG NumberOfFunctions + ) +{ + NTSTATUS status; + SIZE_T bufferSize; + PRUNTIME_FUNCTION functions; + + // Put a reasonable limit on the number of entries we read. + if (FunctionTable->EntryCount > 0x100000) + return STATUS_BUFFER_OVERFLOW; + + bufferSize = FunctionTable->EntryCount * sizeof(RUNTIME_FUNCTION); + functions = PhAllocatePage(bufferSize, NULL); + + if (!functions) + return STATUS_NO_MEMORY; + + status = NtReadVirtualMemory(ProcessHandle, FunctionTable->FunctionTable, functions, bufferSize, NULL); + + if (NT_SUCCESS(status)) + { + *Functions = functions; + *NumberOfFunctions = FunctionTable->EntryCount; + } + else + { + PhFreePage(functions); + } + + return status; +} + +NTSTATUS PhAccessOutOfProcessFunctionEntry( + _In_ HANDLE ProcessHandle, + _In_ ULONG64 ControlPc, + _Out_ PRUNTIME_FUNCTION Function + ) +{ + NTSTATUS status; + PDYNAMIC_FUNCTION_TABLE functionTableAddress; + DYNAMIC_FUNCTION_TABLE functionTable; + WCHAR outOfProcessCallbackDll[512]; + UNICODE_STRING outOfProcessCallbackDllString; + PRUNTIME_FUNCTION functions; + ULONG numberOfFunctions; + PRUNTIME_FUNCTION function; + + if (!NT_SUCCESS(status = PhpLookupDynamicFunctionTable( + ProcessHandle, + ControlPc, + &functionTableAddress, + &functionTable, + outOfProcessCallbackDll, + sizeof(outOfProcessCallbackDll), + &outOfProcessCallbackDllString + ))) + { + return status; + } + + if (functionTable.Type == RF_CALLBACK) + { + if (!NT_SUCCESS(status = PhpAccessCallbackFunctionTable( + ProcessHandle, + functionTableAddress, + &outOfProcessCallbackDllString, + &functions, + &numberOfFunctions + ))) + { + return status; + } + + function = PhpLookupFunctionEntry(functions, numberOfFunctions, FALSE, ControlPc - functionTable.BaseAddress); + + if (function) + *Function = *function; + else + status = STATUS_NOT_FOUND; + + RtlFreeHeap(RtlProcessHeap(), 0, functions); + } + else + { + if (!NT_SUCCESS(status = PhpAccessNormalFunctionTable( + ProcessHandle, + &functionTable, + &functions, + &numberOfFunctions + ))) + { + return status; + } + + function = PhpLookupFunctionEntry(functions, numberOfFunctions, functionTable.Type == RF_SORTED, ControlPc - functionTable.BaseAddress); + + if (function) + *Function = *function; + else + status = STATUS_NOT_FOUND; + + PhFreePage(functions); + } + + return status; +} + +#endif + +ULONG64 __stdcall PhGetModuleBase64( + _In_ HANDLE hProcess, + _In_ ULONG64 dwAddr + ) +{ + ULONG64 base; +#ifdef _WIN64 + DYNAMIC_FUNCTION_TABLE functionTable; +#endif + +#ifdef _WIN64 + if (NT_SUCCESS(PhpLookupDynamicFunctionTable( + hProcess, + dwAddr, + NULL, + &functionTable, + NULL, + 0, + NULL + ))) + { + base = functionTable.BaseAddress; + } + else + { + base = 0; + } +#else + base = 0; +#endif + + if (base == 0 && SymGetModuleBase64_I) + base = SymGetModuleBase64_I(hProcess, dwAddr); + + return base; +} + +PVOID __stdcall PhFunctionTableAccess64( + _In_ HANDLE hProcess, + _In_ ULONG64 AddrBase + ) +{ +#ifdef _WIN64 + static RUNTIME_FUNCTION lastRuntimeFunction; +#endif + + PVOID entry; + +#ifdef _WIN64 + if (NT_SUCCESS(PhAccessOutOfProcessFunctionEntry(hProcess, AddrBase, &lastRuntimeFunction))) + entry = &lastRuntimeFunction; + else + entry = NULL; +#else + entry = NULL; +#endif + + if (!entry && SymFunctionTableAccess64_I) + entry = SymFunctionTableAccess64_I(hProcess, AddrBase); + + return entry; +} + +BOOLEAN PhStackWalk( + _In_ ULONG MachineType, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ThreadHandle, + _Inout_ LPSTACKFRAME64 StackFrame, + _Inout_ PVOID ContextRecord, + _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress + ) +{ + BOOLEAN result; + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!StackWalk64_I) + return FALSE; + + if (!FunctionTableAccessRoutine) + { + if (MachineType == IMAGE_FILE_MACHINE_AMD64) + FunctionTableAccessRoutine = PhFunctionTableAccess64; + else + FunctionTableAccessRoutine = SymFunctionTableAccess64_I; + } + + if (!GetModuleBaseRoutine) + { + if (MachineType == IMAGE_FILE_MACHINE_AMD64) + GetModuleBaseRoutine = PhGetModuleBase64; + else + GetModuleBaseRoutine = SymGetModuleBase64_I; + } + + PH_LOCK_SYMBOLS(); + result = StackWalk64_I( + MachineType, + ProcessHandle, + ThreadHandle, + StackFrame, + ContextRecord, + ReadMemoryRoutine, + FunctionTableAccessRoutine, + GetModuleBaseRoutine, + TranslateAddress + ); + PH_UNLOCK_SYMBOLS(); + + return result; +} + +BOOLEAN PhWriteMiniDumpProcess( + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _In_ HANDLE FileHandle, + _In_ MINIDUMP_TYPE DumpType, + _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ) +{ + PhpRegisterSymbolProvider(NULL); + + if (!MiniDumpWriteDump_I) + { + SetLastError(ERROR_PROC_NOT_FOUND); + return FALSE; + } + + return MiniDumpWriteDump_I( + ProcessHandle, + HandleToUlong(ProcessId), + FileHandle, + DumpType, + ExceptionParam, + UserStreamParam, + CallbackParam + ); +} + +/** + * Converts a STACKFRAME64 structure to a PH_THREAD_STACK_FRAME structure. + * + * \param StackFrame64 A pointer to the STACKFRAME64 structure to convert. + * \param Flags Flags to set in the resulting structure. + * \param ThreadStackFrame A pointer to the resulting PH_THREAD_STACK_FRAME structure. + */ +VOID PhpConvertStackFrame( + _In_ STACKFRAME64 *StackFrame64, + _In_ ULONG Flags, + _Out_ PPH_THREAD_STACK_FRAME ThreadStackFrame + ) +{ + ULONG i; + + ThreadStackFrame->PcAddress = (PVOID)StackFrame64->AddrPC.Offset; + ThreadStackFrame->ReturnAddress = (PVOID)StackFrame64->AddrReturn.Offset; + ThreadStackFrame->FrameAddress = (PVOID)StackFrame64->AddrFrame.Offset; + ThreadStackFrame->StackAddress = (PVOID)StackFrame64->AddrStack.Offset; + ThreadStackFrame->BStoreAddress = (PVOID)StackFrame64->AddrBStore.Offset; + + for (i = 0; i < 4; i++) + ThreadStackFrame->Params[i] = (PVOID)StackFrame64->Params[i]; + + ThreadStackFrame->Flags = Flags; + + if (StackFrame64->FuncTableEntry) + ThreadStackFrame->Flags |= PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT; +} + +/** + * Walks a thread's stack. + * + * \param ThreadHandle A handle to a thread. The handle must have THREAD_QUERY_LIMITED_INFORMATION, + * THREAD_GET_CONTEXT and THREAD_SUSPEND_RESUME access. The handle can have any access for kernel + * stack walking. + * \param ProcessHandle A handle to the thread's parent process. The handle must have + * PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access. If a symbol provider is being used, pass + * its process handle and specify the symbol provider in \a SymbolProvider. + * \param ClientId The client ID identifying the thread. + * \param SymbolProvider The associated symbol provider. + * \param Flags A combination of flags. + * \li \c PH_WALK_I386_STACK Walks the x86 stack. On AMD64 systems this flag walks the WOW64 stack. + * \li \c PH_WALK_AMD64_STACK Walks the AMD64 stack. On x86 systems this flag is ignored. + * \li \c PH_WALK_KERNEL_STACK Walks the kernel stack. This flag is ignored if there is no active + * KProcessHacker connection. + * \param Callback A callback function which is executed for each stack frame. + * \param Context A user-defined value to pass to the callback function. + */ +NTSTATUS PhWalkThreadStack( + _In_ HANDLE ThreadHandle, + _In_opt_ HANDLE ProcessHandle, + _In_opt_ PCLIENT_ID ClientId, + _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG Flags, + _In_ PPH_WALK_THREAD_STACK_CALLBACK Callback, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN suspended = FALSE; + BOOLEAN processOpened = FALSE; + BOOLEAN isCurrentThread = FALSE; + BOOLEAN isSystemThread = FALSE; + THREAD_BASIC_INFORMATION basicInfo; + + // Open a handle to the process if we weren't given one. + if (!ProcessHandle) + { + if (KphIsConnected() || !ClientId) + { + if (!NT_SUCCESS(status = PhOpenThreadProcess( + ThreadHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + &ProcessHandle + ))) + return status; + } + else + { + if (!NT_SUCCESS(status = PhOpenProcess( + &ProcessHandle, + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + ClientId->UniqueProcess + ))) + return status; + } + + processOpened = TRUE; + } + + // Determine if the caller specified the current thread. + if (ClientId) + { + if (ClientId->UniqueThread == NtCurrentTeb()->ClientId.UniqueThread) + isCurrentThread = TRUE; + if (ClientId->UniqueProcess == SYSTEM_PROCESS_ID) + isSystemThread = TRUE; + } + else + { + if (ThreadHandle == NtCurrentThread()) + { + isCurrentThread = TRUE; + } + else if (NT_SUCCESS(PhGetThreadBasicInformation(ThreadHandle, &basicInfo))) + { + if (basicInfo.ClientId.UniqueThread == NtCurrentTeb()->ClientId.UniqueThread) + isCurrentThread = TRUE; + if (basicInfo.ClientId.UniqueProcess == SYSTEM_PROCESS_ID) + isSystemThread = TRUE; + } + } + + // Make sure this isn't a kernel-mode thread. + if (!isSystemThread) + { + PVOID startAddress; + + if (NT_SUCCESS(PhGetThreadStartAddress(ThreadHandle, &startAddress))) + { + if ((ULONG_PTR)startAddress > PhSystemBasicInformation.MaximumUserModeAddress) + isSystemThread = TRUE; + } + } + + // Suspend the thread to avoid inaccurate results. Don't suspend if we're walking the stack of + // the current thread or a kernel-mode thread. + if (!isCurrentThread && !isSystemThread) + { + if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL))) + suspended = TRUE; + } + + // Kernel stack walk. + if ((Flags & PH_WALK_KERNEL_STACK) && KphIsConnected()) + { + PVOID stack[256 - 2]; // See MAX_STACK_DEPTH + ULONG capturedFrames; + ULONG i; + + if (NT_SUCCESS(KphCaptureStackBackTraceThread( + ThreadHandle, + 1, + sizeof(stack) / sizeof(PVOID), + stack, + &capturedFrames, + NULL + ))) + { + PH_THREAD_STACK_FRAME threadStackFrame; + + memset(&threadStackFrame, 0, sizeof(PH_THREAD_STACK_FRAME)); + + for (i = 0; i < capturedFrames; i++) + { + threadStackFrame.PcAddress = stack[i]; + threadStackFrame.Flags = PH_THREAD_STACK_FRAME_KERNEL; + + if (!Callback(&threadStackFrame, Context)) + { + goto ResumeExit; + } + } + } + } + +#ifdef _WIN64 + if (Flags & PH_WALK_AMD64_STACK) + { + STACKFRAME64 stackFrame; + PH_THREAD_STACK_FRAME threadStackFrame; + CONTEXT context; + + context.ContextFlags = CONTEXT_ALL; + + if (!NT_SUCCESS(status = NtGetContextThread(ThreadHandle, &context))) + goto SkipAmd64Stack; + + memset(&stackFrame, 0, sizeof(STACKFRAME64)); + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrPC.Offset = context.Rip; + stackFrame.AddrStack.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context.Rsp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context.Rbp; + + while (TRUE) + { + if (!PhStackWalk( + IMAGE_FILE_MACHINE_AMD64, + ProcessHandle, + ThreadHandle, + &stackFrame, + &context, + SymbolProvider, + NULL, + NULL, + NULL, + NULL + )) + break; + + // If we have an invalid instruction pointer, break. + if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1) + break; + + // Convert the stack frame and execute the callback. + + PhpConvertStackFrame(&stackFrame, PH_THREAD_STACK_FRAME_AMD64, &threadStackFrame); + + if (!Callback(&threadStackFrame, Context)) + goto ResumeExit; + } + } + +SkipAmd64Stack: +#endif + + // x86/WOW64 stack walk. + if (Flags & PH_WALK_I386_STACK) + { + STACKFRAME64 stackFrame; + PH_THREAD_STACK_FRAME threadStackFrame; +#ifndef _WIN64 + CONTEXT context; + + context.ContextFlags = CONTEXT_ALL; + + if (!NT_SUCCESS(status = NtGetContextThread(ThreadHandle, &context))) + goto SkipI386Stack; +#else + WOW64_CONTEXT context; + + context.ContextFlags = WOW64_CONTEXT_ALL; + + if (!NT_SUCCESS(status = PhGetThreadWow64Context(ThreadHandle, &context))) + goto SkipI386Stack; +#endif + + memset(&stackFrame, 0, sizeof(STACKFRAME64)); + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrPC.Offset = context.Eip; + stackFrame.AddrStack.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context.Esp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context.Ebp; + + while (TRUE) + { + if (!PhStackWalk( + IMAGE_FILE_MACHINE_I386, + ProcessHandle, + ThreadHandle, + &stackFrame, + &context, + SymbolProvider, + NULL, + NULL, + NULL, + NULL + )) + break; + + // If we have an invalid instruction pointer, break. + if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1) + break; + + // Convert the stack frame and execute the callback. + + PhpConvertStackFrame(&stackFrame, PH_THREAD_STACK_FRAME_I386, &threadStackFrame); + + if (!Callback(&threadStackFrame, Context)) + goto ResumeExit; + + // (x86 only) Allow the user to change Eip, Esp and Ebp. + context.Eip = PtrToUlong(threadStackFrame.PcAddress); + stackFrame.AddrPC.Offset = PtrToUlong(threadStackFrame.PcAddress); + context.Ebp = PtrToUlong(threadStackFrame.FrameAddress); + stackFrame.AddrFrame.Offset = PtrToUlong(threadStackFrame.FrameAddress); + context.Esp = PtrToUlong(threadStackFrame.StackAddress); + stackFrame.AddrStack.Offset = PtrToUlong(threadStackFrame.StackAddress); + } + } + +SkipI386Stack: + +ResumeExit: + if (suspended) + NtResumeThread(ThreadHandle, NULL); + + if (processOpened) + NtClose(ProcessHandle); + + return status; +} + +PPH_STRING PhUndecorateSymbolName( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ PWSTR DecoratedName + ) +{ + PPH_STRING undecoratedSymbolName = NULL; + PWSTR undecoratedBuffer; + ULONG result; + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!UnDecorateSymbolNameW_I) + return NULL; + + // lucasg: there is no way to know the resulting length of an undecorated name. + // If there is not enough space, the function does not fail and instead returns a truncated name. + + undecoratedBuffer = PhAllocate(PAGE_SIZE * sizeof(WCHAR)); + //memset(undecoratedBuffer, 0, PAGE_SIZE * sizeof(WCHAR)); + + PH_LOCK_SYMBOLS(); + + result = UnDecorateSymbolNameW_I( + DecoratedName, + undecoratedBuffer, + PAGE_SIZE, + UNDNAME_COMPLETE + ); + + PH_UNLOCK_SYMBOLS(); + + if (result != 0) + undecoratedSymbolName = PhCreateStringEx(undecoratedBuffer, result * sizeof(WCHAR)); + PhFree(undecoratedBuffer); + + return undecoratedSymbolName; +} + +typedef struct _PH_ENUMERATE_SYMBOLS_CONTEXT +{ + PVOID UserContext; + PPH_ENUMERATE_SYMBOLS_CALLBACK UserCallback; +} PH_ENUMERATE_SYMBOLS_CONTEXT, *PPH_ENUMERATE_SYMBOLS_CONTEXT; + +BOOL +CALLBACK +PhEnumerateSymbolsCallback( + _In_ PSYMBOL_INFOW pSymInfo, + _In_ ULONG SymbolSize, + _In_ PVOID Context + ) +{ + PPH_ENUMERATE_SYMBOLS_CONTEXT phContext = (PPH_ENUMERATE_SYMBOLS_CONTEXT)Context; + BOOLEAN result; + PH_SYMBOL_INFO symbolInfo = { 0 }; + + if (pSymInfo->MaxNameLen) + { + SIZE_T SuggestedLength; + + symbolInfo.Name.Buffer = pSymInfo->Name; + symbolInfo.Name.Length = min(pSymInfo->NameLen, pSymInfo->MaxNameLen - 1) * sizeof(WCHAR); + + // NameLen is unreliable, might be greater that expected + + SuggestedLength = PhCountStringZ(symbolInfo.Name.Buffer) * sizeof(WCHAR); + symbolInfo.Name.Length = min(symbolInfo.Name.Length, SuggestedLength); + } + else + { + PhInitializeEmptyStringRef(&symbolInfo.Name); + } + + symbolInfo.TypeIndex = pSymInfo->TypeIndex; + symbolInfo.Index = pSymInfo->Index; + symbolInfo.Size = pSymInfo->Size; + symbolInfo.ModBase = pSymInfo->ModBase; + symbolInfo.Flags = pSymInfo->Flags; + symbolInfo.Value = pSymInfo->Value; + symbolInfo.Address = pSymInfo->Address; + symbolInfo.Register = pSymInfo->Register; + symbolInfo.Scope = pSymInfo->Scope; + symbolInfo.Tag = pSymInfo->Tag; + + result = phContext->UserCallback(&symbolInfo, SymbolSize, phContext->UserContext); + + return (BOOL)result; +} + +BOOLEAN PhEnumerateSymbols( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ HANDLE ProcessHandle, + _In_ ULONG64 BaseOfDll, + _In_opt_ PCWSTR Mask, + _In_ PPH_ENUMERATE_SYMBOLS_CALLBACK EnumSymbolsCallback, + _In_opt_ const PVOID UserContext + ) +{ + BOOLEAN result; + + PhpRegisterSymbolProvider(SymbolProvider); + + if (!SymEnumSymbolsW_I) + { + SetLastError(ERROR_PROC_NOT_FOUND); + return FALSE; + } + + PH_ENUMERATE_SYMBOLS_CONTEXT Context = { 0 }; + Context.UserContext = UserContext; + Context.UserCallback = EnumSymbolsCallback; + + PH_LOCK_SYMBOLS(); + + result = SymEnumSymbolsW_I( + ProcessHandle, + BaseOfDll, + Mask, + PhEnumerateSymbolsCallback, + &Context + ); + + PH_UNLOCK_SYMBOLS(); + + return result; +} + diff --git a/phlib/sync.c b/phlib/sync.c index 0c946e66727a..f624bd060841 100644 --- a/phlib/sync.c +++ b/phlib/sync.c @@ -1,496 +1,496 @@ -/* - * Process Hacker - - * misc. synchronization utilities - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * This file contains code for several synchronization objects. - * - * Event. This is a lightweight notification event object that does not create a kernel event object - * until needed. Additionally the kernel event object is automatically freed when no longer needed. - * Note that PhfResetEvent is NOT thread-safe. - * - * Barrier. This is a non-traditional implementation of a barrier, built on the wake event object. I - * have identified three types of participants in this process: - * 1. The slaves, who wait for the master to release them. This is the main mechanism through which - * the threads are synchronized. - * 2. The master, who is the last thread to wait on the barrier. This thread triggers the waking - * process, and waits until all slaves have woken. - * 3. The observers, who are simply threads which were slaves before, were woken, and have tried to - * wait on the barrier again before all other slaves have woken. - * - * Rundown protection. This object allows a thread to wait until all other threads have finished - * using a particular resource before freeing the resource. - * - * Init-once. This is a lightweight one-time initialization mechanism which uses the event object - * for any required blocking. The overhead is very small - only a single inlined comparison. - */ - -#include - -/** - * Initializes an event object. - * - * \param Event A pointer to an event object. - */ -VOID FASTCALL PhfInitializeEvent( - _Out_ PPH_EVENT Event - ) -{ - Event->Value = PH_EVENT_REFCOUNT_INC; - Event->EventHandle = NULL; -} - -/** - * Dereferences the event object used by an event. - * - * \param Event A pointer to an event object. - * \param EventHandle The current value of the event object. - */ -FORCEINLINE VOID PhpDereferenceEvent( - _Inout_ PPH_EVENT Event, - _In_opt_ HANDLE EventHandle - ) -{ - ULONG_PTR value; - - value = _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, -PH_EVENT_REFCOUNT_INC); - - // See if the reference count has become 0. - if (((value >> PH_EVENT_REFCOUNT_SHIFT) & PH_EVENT_REFCOUNT_MASK) - 1 == 0) - { - if (EventHandle) - { - NtClose(EventHandle); - Event->EventHandle = NULL; - } - } -} - -/** - * References the event object used by an event. - * - * \param Event A pointer to an event object. - */ -FORCEINLINE VOID PhpReferenceEvent( - _Inout_ PPH_EVENT Event - ) -{ - _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, PH_EVENT_REFCOUNT_INC); -} - -/** - * Sets an event object. Any threads waiting on the event will be released. - * - * \param Event A pointer to an event object. - */ -VOID FASTCALL PhfSetEvent( - _Inout_ PPH_EVENT Event - ) -{ - HANDLE eventHandle; - - // Only proceed if the event isn't set already. - if (!_InterlockedBitTestAndSetPointer((PLONG_PTR)&Event->Value, PH_EVENT_SET_SHIFT)) - { - eventHandle = Event->EventHandle; - - if (eventHandle) - { - NtSetEvent(eventHandle, NULL); - } - - PhpDereferenceEvent(Event, eventHandle); - } -} - -/** - * Waits for an event object to be set. - * - * \param Event A pointer to an event object. - * \param Timeout The timeout value. - * - * \return TRUE if the event object was set before the timeout period expired, otherwise FALSE. - * - * \remarks To test the event, use PhTestEvent() instead of using a timeout of zero. - */ -BOOLEAN FASTCALL PhfWaitForEvent( - _Inout_ PPH_EVENT Event, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - BOOLEAN result; - ULONG_PTR value; - HANDLE eventHandle; - - value = Event->Value; - - // Shortcut: if the event is set, return immediately. - if (value & PH_EVENT_SET) - return TRUE; - - // Shortcut: if the timeout is 0, return immediately if the event isn't set. - if (Timeout && Timeout->QuadPart == 0) - return FALSE; - - // Prevent the event from being invalidated. - PhpReferenceEvent(Event); - - eventHandle = Event->EventHandle; - - if (!eventHandle) - { - NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); - assert(eventHandle); - - // Try to set the event handle to our event. - if (_InterlockedCompareExchangePointer( - &Event->EventHandle, - eventHandle, - NULL - ) != NULL) - { - // Someone else set the event before we did. - NtClose(eventHandle); - eventHandle = Event->EventHandle; - } - } - - // Essential: check the event one last time to see if it is set. - if (!(Event->Value & PH_EVENT_SET)) - { - result = NtWaitForSingleObject(eventHandle, FALSE, Timeout) == STATUS_WAIT_0; - } - else - { - result = TRUE; - } - - PhpDereferenceEvent(Event, eventHandle); - - return result; -} - -/** - * Resets an event's state. - * - * \param Event A pointer to an event object. - * - * \remarks This function is not thread-safe. Make sure no other threads are using the event when - * you call this function. - */ -VOID FASTCALL PhfResetEvent( - _Inout_ PPH_EVENT Event - ) -{ - assert(!Event->EventHandle); - - if (PhTestEvent(Event)) - Event->Value = PH_EVENT_REFCOUNT_INC; -} - -VOID FASTCALL PhfInitializeBarrier( - _Out_ PPH_BARRIER Barrier, - _In_ ULONG_PTR Target - ) -{ - Barrier->Value = Target << PH_BARRIER_TARGET_SHIFT; - PhInitializeWakeEvent(&Barrier->WakeEvent); -} - -FORCEINLINE VOID PhpBlockOnBarrier( - _Inout_ PPH_BARRIER Barrier, - _In_ ULONG Role, - _In_ BOOLEAN Spin - ) -{ - PH_QUEUED_WAIT_BLOCK waitBlock; - ULONG_PTR cancel; - - PhQueueWakeEvent(&Barrier->WakeEvent, &waitBlock); - - cancel = 0; - - switch (Role) - { - case PH_BARRIER_MASTER: - cancel = ((Barrier->Value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK) == 1; - break; - case PH_BARRIER_SLAVE: - cancel = Barrier->Value & PH_BARRIER_WAKING; - break; - case PH_BARRIER_OBSERVER: - cancel = !(Barrier->Value & PH_BARRIER_WAKING); - break; - default: - ASSUME_NO_DEFAULT; - } - - if (cancel) - { - PhSetWakeEvent(&Barrier->WakeEvent, &waitBlock); - return; - } - - PhWaitForWakeEvent(&Barrier->WakeEvent, &waitBlock, Spin, NULL); -} - -/** - * Waits until all threads are blocking on the barrier, and resets the state of the barrier. - * - * \param Barrier A barrier. - * \param Spin TRUE to spin on the barrier before blocking, FALSE to block immediately. - * - * \return TRUE for an unspecified thread after each phase, and FALSE for all other threads. - * - * \remarks By checking the return value of the function, in each phase an action can be performed - * exactly once. This could, for example, involve merging the results of calculations. - */ -BOOLEAN FASTCALL PhfWaitForBarrier( - _Inout_ PPH_BARRIER Barrier, - _In_ BOOLEAN Spin - ) -{ - ULONG_PTR value; - ULONG_PTR newValue; - ULONG_PTR count; - ULONG_PTR target; - - value = Barrier->Value; - - while (TRUE) - { - if (!(value & PH_BARRIER_WAKING)) - { - count = (value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK; - target = (value >> PH_BARRIER_TARGET_SHIFT) & PH_BARRIER_TARGET_MASK; - assert(count != target); - - count++; - - if (count != target) - newValue = value + PH_BARRIER_COUNT_INC; - else - newValue = value + PH_BARRIER_COUNT_INC + PH_BARRIER_WAKING; - - if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&Barrier->Value, - (PVOID)newValue, - (PVOID)value - )) == value) - { - if (count != target) - { - // Wait for the master signal (the last thread to reach the barrier). - // Once we get it, decrement the count to allow the master to continue. - - do - { - PhpBlockOnBarrier(Barrier, PH_BARRIER_SLAVE, Spin); - } while (!(Barrier->Value & PH_BARRIER_WAKING)); - - value = _InterlockedExchangeAddPointer((PLONG_PTR)&Barrier->Value, -PH_BARRIER_COUNT_INC); - - if (((value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK) - 1 == 1) - { - PhSetWakeEvent(&Barrier->WakeEvent, NULL); // for the master - } - - return FALSE; - } - else - { - // We're the last one to reach the barrier, so we become the master. - // Wake the slaves and wait for them to decrease the count to 1. This is so that - // we know the slaves have woken and we don't clear the waking bit before they - // wake. - - PhSetWakeEvent(&Barrier->WakeEvent, NULL); // for slaves - - do - { - PhpBlockOnBarrier(Barrier, PH_BARRIER_MASTER, Spin); - } while (((Barrier->Value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK) != 1); - - _InterlockedExchangeAddPointer((PLONG_PTR)&Barrier->Value, -(PH_BARRIER_WAKING + PH_BARRIER_COUNT_INC)); - PhSetWakeEvent(&Barrier->WakeEvent, NULL); // for observers - - return TRUE; - } - } - } - else - { - // We're too early; other threads are still waking. Wait for them to finish. - - PhpBlockOnBarrier(Barrier, PH_BARRIER_OBSERVER, Spin); - newValue = Barrier->Value; - } - - value = newValue; - } -} - -VOID FASTCALL PhfInitializeRundownProtection( - _Out_ PPH_RUNDOWN_PROTECT Protection - ) -{ - Protection->Value = 0; -} - -BOOLEAN FASTCALL PhfAcquireRundownProtection( - _Inout_ PPH_RUNDOWN_PROTECT Protection - ) -{ - ULONG_PTR value; - - // Increment the reference count only if rundown has not started. - - while (TRUE) - { - value = Protection->Value; - - if (value & PH_RUNDOWN_ACTIVE) - return FALSE; - - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&Protection->Value, - (PVOID)(value + PH_RUNDOWN_REF_INC), - (PVOID)value - ) == value) - return TRUE; - } -} - -VOID FASTCALL PhfReleaseRundownProtection( - _Inout_ PPH_RUNDOWN_PROTECT Protection - ) -{ - ULONG_PTR value; - - while (TRUE) - { - value = Protection->Value; - - if (value & PH_RUNDOWN_ACTIVE) - { - PPH_RUNDOWN_WAIT_BLOCK waitBlock; - - // Since rundown is active, the reference count has been moved to the waiter's wait - // block. If we are the last user, we must wake up the waiter. - - waitBlock = (PPH_RUNDOWN_WAIT_BLOCK)(value & ~PH_RUNDOWN_ACTIVE); - - if (_InterlockedDecrementPointer(&waitBlock->Count) == 0) - { - PhSetEvent(&waitBlock->WakeEvent); - } - - break; - } - else - { - // Decrement the reference count normally. - - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&Protection->Value, - (PVOID)(value - PH_RUNDOWN_REF_INC), - (PVOID)value - ) == value) - break; - } - } -} - -VOID FASTCALL PhfWaitForRundownProtection( - _Inout_ PPH_RUNDOWN_PROTECT Protection - ) -{ - ULONG_PTR value; - ULONG_PTR count; - PH_RUNDOWN_WAIT_BLOCK waitBlock; - BOOLEAN waitBlockInitialized; - - // Fast path. If the reference count is 0 or rundown has already been completed, return. - value = (ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&Protection->Value, - (PVOID)PH_RUNDOWN_ACTIVE, - (PVOID)0 - ); - - if (value == 0 || value == PH_RUNDOWN_ACTIVE) - return; - - waitBlockInitialized = FALSE; - - while (TRUE) - { - value = Protection->Value; - count = value >> PH_RUNDOWN_REF_SHIFT; - - // Initialize the wait block if necessary. - if (count != 0 && !waitBlockInitialized) - { - PhInitializeEvent(&waitBlock.WakeEvent); - waitBlockInitialized = TRUE; - } - - // Save the existing reference count. - waitBlock.Count = count; - - if ((ULONG_PTR)_InterlockedCompareExchangePointer( - (PVOID *)&Protection->Value, - (PVOID)((ULONG_PTR)&waitBlock | PH_RUNDOWN_ACTIVE), - (PVOID)value - ) == value) - { - if (count != 0) - PhWaitForEvent(&waitBlock.WakeEvent, NULL); - - break; - } - } -} - -VOID FASTCALL PhfInitializeInitOnce( - _Out_ PPH_INITONCE InitOnce - ) -{ - PhInitializeEvent(&InitOnce->Event); -} - -BOOLEAN FASTCALL PhfBeginInitOnce( - _Inout_ PPH_INITONCE InitOnce - ) -{ - if (!_InterlockedBitTestAndSetPointer(&InitOnce->Event.Value, PH_INITONCE_INITIALIZING_SHIFT)) - return TRUE; - - PhWaitForEvent(&InitOnce->Event, NULL); - - return FALSE; -} - -VOID FASTCALL PhfEndInitOnce( - _Inout_ PPH_INITONCE InitOnce - ) -{ - PhSetEvent(&InitOnce->Event); -} +/* + * Process Hacker - + * misc. synchronization utilities + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * This file contains code for several synchronization objects. + * + * Event. This is a lightweight notification event object that does not create a kernel event object + * until needed. Additionally the kernel event object is automatically freed when no longer needed. + * Note that PhfResetEvent is NOT thread-safe. + * + * Barrier. This is a non-traditional implementation of a barrier, built on the wake event object. I + * have identified three types of participants in this process: + * 1. The slaves, who wait for the master to release them. This is the main mechanism through which + * the threads are synchronized. + * 2. The master, who is the last thread to wait on the barrier. This thread triggers the waking + * process, and waits until all slaves have woken. + * 3. The observers, who are simply threads which were slaves before, were woken, and have tried to + * wait on the barrier again before all other slaves have woken. + * + * Rundown protection. This object allows a thread to wait until all other threads have finished + * using a particular resource before freeing the resource. + * + * Init-once. This is a lightweight one-time initialization mechanism which uses the event object + * for any required blocking. The overhead is very small - only a single inlined comparison. + */ + +#include + +/** + * Initializes an event object. + * + * \param Event A pointer to an event object. + */ +VOID FASTCALL PhfInitializeEvent( + _Out_ PPH_EVENT Event + ) +{ + Event->Value = PH_EVENT_REFCOUNT_INC; + Event->EventHandle = NULL; +} + +/** + * Dereferences the event object used by an event. + * + * \param Event A pointer to an event object. + * \param EventHandle The current value of the event object. + */ +FORCEINLINE VOID PhpDereferenceEvent( + _Inout_ PPH_EVENT Event, + _In_opt_ HANDLE EventHandle + ) +{ + ULONG_PTR value; + + value = _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, -PH_EVENT_REFCOUNT_INC); + + // See if the reference count has become 0. + if (((value >> PH_EVENT_REFCOUNT_SHIFT) & PH_EVENT_REFCOUNT_MASK) - 1 == 0) + { + if (EventHandle) + { + NtClose(EventHandle); + Event->EventHandle = NULL; + } + } +} + +/** + * References the event object used by an event. + * + * \param Event A pointer to an event object. + */ +FORCEINLINE VOID PhpReferenceEvent( + _Inout_ PPH_EVENT Event + ) +{ + _InterlockedExchangeAddPointer((PLONG_PTR)&Event->Value, PH_EVENT_REFCOUNT_INC); +} + +/** + * Sets an event object. Any threads waiting on the event will be released. + * + * \param Event A pointer to an event object. + */ +VOID FASTCALL PhfSetEvent( + _Inout_ PPH_EVENT Event + ) +{ + HANDLE eventHandle; + + // Only proceed if the event isn't set already. + if (!_InterlockedBitTestAndSetPointer((PLONG_PTR)&Event->Value, PH_EVENT_SET_SHIFT)) + { + eventHandle = Event->EventHandle; + + if (eventHandle) + { + NtSetEvent(eventHandle, NULL); + } + + PhpDereferenceEvent(Event, eventHandle); + } +} + +/** + * Waits for an event object to be set. + * + * \param Event A pointer to an event object. + * \param Timeout The timeout value. + * + * \return TRUE if the event object was set before the timeout period expired, otherwise FALSE. + * + * \remarks To test the event, use PhTestEvent() instead of using a timeout of zero. + */ +BOOLEAN FASTCALL PhfWaitForEvent( + _Inout_ PPH_EVENT Event, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + BOOLEAN result; + ULONG_PTR value; + HANDLE eventHandle; + + value = Event->Value; + + // Shortcut: if the event is set, return immediately. + if (value & PH_EVENT_SET) + return TRUE; + + // Shortcut: if the timeout is 0, return immediately if the event isn't set. + if (Timeout && Timeout->QuadPart == 0) + return FALSE; + + // Prevent the event from being invalidated. + PhpReferenceEvent(Event); + + eventHandle = Event->EventHandle; + + if (!eventHandle) + { + NtCreateEvent(&eventHandle, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); + assert(eventHandle); + + // Try to set the event handle to our event. + if (_InterlockedCompareExchangePointer( + &Event->EventHandle, + eventHandle, + NULL + ) != NULL) + { + // Someone else set the event before we did. + NtClose(eventHandle); + eventHandle = Event->EventHandle; + } + } + + // Essential: check the event one last time to see if it is set. + if (!(Event->Value & PH_EVENT_SET)) + { + result = NtWaitForSingleObject(eventHandle, FALSE, Timeout) == STATUS_WAIT_0; + } + else + { + result = TRUE; + } + + PhpDereferenceEvent(Event, eventHandle); + + return result; +} + +/** + * Resets an event's state. + * + * \param Event A pointer to an event object. + * + * \remarks This function is not thread-safe. Make sure no other threads are using the event when + * you call this function. + */ +VOID FASTCALL PhfResetEvent( + _Inout_ PPH_EVENT Event + ) +{ + assert(!Event->EventHandle); + + if (PhTestEvent(Event)) + Event->Value = PH_EVENT_REFCOUNT_INC; +} + +VOID FASTCALL PhfInitializeBarrier( + _Out_ PPH_BARRIER Barrier, + _In_ ULONG_PTR Target + ) +{ + Barrier->Value = Target << PH_BARRIER_TARGET_SHIFT; + PhInitializeWakeEvent(&Barrier->WakeEvent); +} + +FORCEINLINE VOID PhpBlockOnBarrier( + _Inout_ PPH_BARRIER Barrier, + _In_ ULONG Role, + _In_ BOOLEAN Spin + ) +{ + PH_QUEUED_WAIT_BLOCK waitBlock; + ULONG_PTR cancel; + + PhQueueWakeEvent(&Barrier->WakeEvent, &waitBlock); + + cancel = 0; + + switch (Role) + { + case PH_BARRIER_MASTER: + cancel = ((Barrier->Value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK) == 1; + break; + case PH_BARRIER_SLAVE: + cancel = Barrier->Value & PH_BARRIER_WAKING; + break; + case PH_BARRIER_OBSERVER: + cancel = !(Barrier->Value & PH_BARRIER_WAKING); + break; + default: + ASSUME_NO_DEFAULT; + } + + if (cancel) + { + PhSetWakeEvent(&Barrier->WakeEvent, &waitBlock); + return; + } + + PhWaitForWakeEvent(&Barrier->WakeEvent, &waitBlock, Spin, NULL); +} + +/** + * Waits until all threads are blocking on the barrier, and resets the state of the barrier. + * + * \param Barrier A barrier. + * \param Spin TRUE to spin on the barrier before blocking, FALSE to block immediately. + * + * \return TRUE for an unspecified thread after each phase, and FALSE for all other threads. + * + * \remarks By checking the return value of the function, in each phase an action can be performed + * exactly once. This could, for example, involve merging the results of calculations. + */ +BOOLEAN FASTCALL PhfWaitForBarrier( + _Inout_ PPH_BARRIER Barrier, + _In_ BOOLEAN Spin + ) +{ + ULONG_PTR value; + ULONG_PTR newValue; + ULONG_PTR count; + ULONG_PTR target; + + value = Barrier->Value; + + while (TRUE) + { + if (!(value & PH_BARRIER_WAKING)) + { + count = (value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK; + target = (value >> PH_BARRIER_TARGET_SHIFT) & PH_BARRIER_TARGET_MASK; + assert(count != target); + + count++; + + if (count != target) + newValue = value + PH_BARRIER_COUNT_INC; + else + newValue = value + PH_BARRIER_COUNT_INC + PH_BARRIER_WAKING; + + if ((newValue = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&Barrier->Value, + (PVOID)newValue, + (PVOID)value + )) == value) + { + if (count != target) + { + // Wait for the master signal (the last thread to reach the barrier). + // Once we get it, decrement the count to allow the master to continue. + + do + { + PhpBlockOnBarrier(Barrier, PH_BARRIER_SLAVE, Spin); + } while (!(Barrier->Value & PH_BARRIER_WAKING)); + + value = _InterlockedExchangeAddPointer((PLONG_PTR)&Barrier->Value, -PH_BARRIER_COUNT_INC); + + if (((value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK) - 1 == 1) + { + PhSetWakeEvent(&Barrier->WakeEvent, NULL); // for the master + } + + return FALSE; + } + else + { + // We're the last one to reach the barrier, so we become the master. + // Wake the slaves and wait for them to decrease the count to 1. This is so that + // we know the slaves have woken and we don't clear the waking bit before they + // wake. + + PhSetWakeEvent(&Barrier->WakeEvent, NULL); // for slaves + + do + { + PhpBlockOnBarrier(Barrier, PH_BARRIER_MASTER, Spin); + } while (((Barrier->Value >> PH_BARRIER_COUNT_SHIFT) & PH_BARRIER_COUNT_MASK) != 1); + + _InterlockedExchangeAddPointer((PLONG_PTR)&Barrier->Value, -(PH_BARRIER_WAKING + PH_BARRIER_COUNT_INC)); + PhSetWakeEvent(&Barrier->WakeEvent, NULL); // for observers + + return TRUE; + } + } + } + else + { + // We're too early; other threads are still waking. Wait for them to finish. + + PhpBlockOnBarrier(Barrier, PH_BARRIER_OBSERVER, Spin); + newValue = Barrier->Value; + } + + value = newValue; + } +} + +VOID FASTCALL PhfInitializeRundownProtection( + _Out_ PPH_RUNDOWN_PROTECT Protection + ) +{ + Protection->Value = 0; +} + +BOOLEAN FASTCALL PhfAcquireRundownProtection( + _Inout_ PPH_RUNDOWN_PROTECT Protection + ) +{ + ULONG_PTR value; + + // Increment the reference count only if rundown has not started. + + while (TRUE) + { + value = Protection->Value; + + if (value & PH_RUNDOWN_ACTIVE) + return FALSE; + + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&Protection->Value, + (PVOID)(value + PH_RUNDOWN_REF_INC), + (PVOID)value + ) == value) + return TRUE; + } +} + +VOID FASTCALL PhfReleaseRundownProtection( + _Inout_ PPH_RUNDOWN_PROTECT Protection + ) +{ + ULONG_PTR value; + + while (TRUE) + { + value = Protection->Value; + + if (value & PH_RUNDOWN_ACTIVE) + { + PPH_RUNDOWN_WAIT_BLOCK waitBlock; + + // Since rundown is active, the reference count has been moved to the waiter's wait + // block. If we are the last user, we must wake up the waiter. + + waitBlock = (PPH_RUNDOWN_WAIT_BLOCK)(value & ~PH_RUNDOWN_ACTIVE); + + if (_InterlockedDecrementPointer(&waitBlock->Count) == 0) + { + PhSetEvent(&waitBlock->WakeEvent); + } + + break; + } + else + { + // Decrement the reference count normally. + + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&Protection->Value, + (PVOID)(value - PH_RUNDOWN_REF_INC), + (PVOID)value + ) == value) + break; + } + } +} + +VOID FASTCALL PhfWaitForRundownProtection( + _Inout_ PPH_RUNDOWN_PROTECT Protection + ) +{ + ULONG_PTR value; + ULONG_PTR count; + PH_RUNDOWN_WAIT_BLOCK waitBlock; + BOOLEAN waitBlockInitialized; + + // Fast path. If the reference count is 0 or rundown has already been completed, return. + value = (ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&Protection->Value, + (PVOID)PH_RUNDOWN_ACTIVE, + (PVOID)0 + ); + + if (value == 0 || value == PH_RUNDOWN_ACTIVE) + return; + + waitBlockInitialized = FALSE; + + while (TRUE) + { + value = Protection->Value; + count = value >> PH_RUNDOWN_REF_SHIFT; + + // Initialize the wait block if necessary. + if (count != 0 && !waitBlockInitialized) + { + PhInitializeEvent(&waitBlock.WakeEvent); + waitBlockInitialized = TRUE; + } + + // Save the existing reference count. + waitBlock.Count = count; + + if ((ULONG_PTR)_InterlockedCompareExchangePointer( + (PVOID *)&Protection->Value, + (PVOID)((ULONG_PTR)&waitBlock | PH_RUNDOWN_ACTIVE), + (PVOID)value + ) == value) + { + if (count != 0) + PhWaitForEvent(&waitBlock.WakeEvent, NULL); + + break; + } + } +} + +VOID FASTCALL PhfInitializeInitOnce( + _Out_ PPH_INITONCE InitOnce + ) +{ + PhInitializeEvent(&InitOnce->Event); +} + +BOOLEAN FASTCALL PhfBeginInitOnce( + _Inout_ PPH_INITONCE InitOnce + ) +{ + if (!_InterlockedBitTestAndSetPointer(&InitOnce->Event.Value, PH_INITONCE_INITIALIZING_SHIFT)) + return TRUE; + + PhWaitForEvent(&InitOnce->Event, NULL); + + return FALSE; +} + +VOID FASTCALL PhfEndInitOnce( + _Inout_ PPH_INITONCE InitOnce + ) +{ + PhSetEvent(&InitOnce->Event); +} diff --git a/phlib/theme.c b/phlib/theme.c new file mode 100644 index 000000000000..cbc34f7e5572 --- /dev/null +++ b/phlib/theme.c @@ -0,0 +1,2502 @@ +/* +* Process Hacker - +* Window theme support functions +* +* Copyright (C) 2018 dmex +* +* This file is part of Process Hacker. +* +* Process Hacker is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Process Hacker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Process Hacker. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct _PHP_THEME_WINDOW_TAB_CONTEXT +{ + WNDPROC DefaultWindowProc; + BOOLEAN MouseActive; +} PHP_THEME_WINDOW_TAB_CONTEXT, *PPHP_THEME_WINDOW_TAB_CONTEXT; + +typedef struct _PHP_THEME_WINDOW_HEADER_CONTEXT +{ + WNDPROC DefaultWindowProc; + BOOLEAN MouseActive; +} PHP_THEME_WINDOW_HEADER_CONTEXT, *PPHP_THEME_WINDOW_HEADER_CONTEXT; + +typedef struct _PHP_THEME_WINDOW_STATUSBAR_CONTEXT +{ + WNDPROC DefaultWindowProc; + BOOLEAN MouseActive; + HTHEME StatusThemeData; +} PHP_THEME_WINDOW_STATUSBAR_CONTEXT, *PPHP_THEME_WINDOW_STATUSBAR_CONTEXT; + +BOOLEAN CALLBACK PhpThemeWindowEnumChildWindows( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ); +BOOLEAN CALLBACK PhpReInitializeThemeWindowEnumChildWindows( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ); + +LRESULT CALLBACK PhpThemeWindowSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); +LRESULT CALLBACK PhpThemeWindowGroupBoxSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); +LRESULT CALLBACK PhpThemeWindowEditSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); +LRESULT CALLBACK PhpThemeWindowTabControlWndSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); +LRESULT CALLBACK PhpThemeWindowHeaderSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); +LRESULT CALLBACK PhpThemeWindowStatusbarWndSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// rev // Win10-RS5 (uxtheme.dll ordinal 132) +BOOL (WINAPI *ShouldAppsUseDarkMode_I)( + VOID + ) = NULL; +// rev // Win10-RS5 (uxtheme.dll ordinal 133) +BOOL (WINAPI *AllowDarkModeForWindow_I)( + _In_ HWND WindowHandle, + _In_ BOOL Enabled + ) = NULL; +// rev // Win10-RS5 (uxtheme.dll ordinal 137) +BOOL (WINAPI *IsDarkModeAllowedForWindow_I)( + _In_ HWND WindowHandle + ) = NULL; + +ULONG PhpThemeColorMode = 0; +BOOLEAN PhpThemeEnable = FALSE; +BOOLEAN PhpThemeBorderEnable = TRUE; +HBRUSH PhMenuBackgroundBrush = NULL; +COLORREF PhpThemeWindowForegroundColor = RGB(28, 28, 28); +COLORREF PhpThemeWindowBackgroundColor = RGB(64, 64, 64); +COLORREF PhpThemeWindowTextColor = RGB(0xff, 0xff, 0xff); +HFONT PhpTabControlFontHandle = NULL; +HFONT PhpToolBarFontHandle = NULL; +HFONT PhpHeaderFontHandle = NULL; +HFONT PhpListViewFontHandle = NULL; +HFONT PhpMenuFontHandle = NULL; +HFONT PhpGroupboxFontHandle = NULL; +HFONT PhpStatusBarFontHandle = NULL; + +VOID PhInitializeWindowTheme( + _In_ HWND WindowHandle, + _In_ BOOLEAN EnableThemeSupport + ) +{ + PhpThemeColorMode = PhGetIntegerSetting(L"GraphColorMode"); + PhpThemeEnable = !!PhGetIntegerSetting(L"EnableThemeSupport"); + PhpThemeBorderEnable = !!PhGetIntegerSetting(L"TreeListBorderEnable"); + + switch (PhpThemeColorMode) + { + case 0: // New colors + { + if (PhMenuBackgroundBrush) + DeleteBrush(PhMenuBackgroundBrush); + + PhMenuBackgroundBrush = CreateSolidBrush(PhpThemeWindowTextColor); + } + break; + case 1: // Old colors + { + if (PhMenuBackgroundBrush) + DeleteBrush(PhMenuBackgroundBrush); + + PhMenuBackgroundBrush = CreateSolidBrush(PhpThemeWindowForegroundColor); + } + break; + } + + if (EnableThemeSupport) + { + WNDPROC defaultWindowProc; + + defaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); + + if (defaultWindowProc != PhpThemeWindowSubclassProc) + { + PhSetWindowContext(WindowHandle, SHRT_MAX, defaultWindowProc); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhpThemeWindowSubclassProc); + } + + // Enable dark window frame support (undocumented RS5 feature). + switch (PhpThemeColorMode) + { + case 0: // New colors + if (WindowsVersion >= WINDOWS_10_RS5) + RemoveProp(WindowHandle, L"UseImmersiveDarkModeColors"); + break; + case 1: // Old colors + if (WindowsVersion >= WINDOWS_10_RS5) + SetProp(WindowHandle, L"UseImmersiveDarkModeColors", (HANDLE)TRUE); + break; + } + + PhEnumChildWindows( + WindowHandle, + 0x1000, + PhpThemeWindowEnumChildWindows, + NULL + ); + + InvalidateRect(WindowHandle, NULL, FALSE); // HACK + } + else + { + EnableThemeDialogTexture(WindowHandle, ETDT_ENABLETAB); + } +} + +VOID PhInitializeWindowThemeEx( + _In_ HWND WindowHandle + ) +{ + static PH_STRINGREF keyPath = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"); + HANDLE keyHandle; + BOOLEAN enableThemeSupport = FALSE; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &keyPath, + 0 + ))) + { + enableThemeSupport = !PhQueryRegistryUlong(keyHandle, L"AppsUseLightTheme"); + NtClose(keyHandle); + } + + PhInitializeWindowTheme(WindowHandle, enableThemeSupport); +} + +VOID PhReInitializeWindowTheme( + _In_ HWND WindowHandle + ) +{ + HWND currentWindow = NULL; + + if (!PhpThemeEnable) + return; + + // HACK + { + if (PhMenuBackgroundBrush) + { + DeleteBrush(PhMenuBackgroundBrush); + } + + PhpThemeColorMode = PhGetIntegerSetting(L"GraphColorMode"); + PhpThemeBorderEnable = !!PhGetIntegerSetting(L"TreeListBorderEnable"); + + switch (PhpThemeColorMode) + { + case 0: // New colors + PhMenuBackgroundBrush = CreateSolidBrush(PhpThemeWindowTextColor); + if (WindowsVersion >= WINDOWS_10_RS5) + RemoveProp(WindowHandle, L"UseImmersiveDarkModeColors"); + break; + case 1: // Old colors + PhMenuBackgroundBrush = CreateSolidBrush(PhpThemeWindowForegroundColor); + if (WindowsVersion >= WINDOWS_10_RS5) + SetProp(WindowHandle, L"UseImmersiveDarkModeColors", (HANDLE)TRUE); + break; + } + } + + PhEnumChildWindows( + WindowHandle, + 0x1000, + PhpReInitializeThemeWindowEnumChildWindows, + NULL + ); + + do + { + if (currentWindow = FindWindowEx(NULL, currentWindow, NULL, NULL)) + { + ULONG processID = 0; + + GetWindowThreadProcessId(currentWindow, &processID); + + if (UlongToHandle(processID) == NtCurrentProcessId()) + { + WCHAR windowClassName[MAX_PATH]; + + if (!GetClassName(currentWindow, windowClassName, RTL_NUMBER_OF(windowClassName))) + windowClassName[0] = UNICODE_NULL; + + //dprintf("PhReInitializeWindowTheme: %S\r\n", windowClassName); + + if (currentWindow != WindowHandle) + { + if (PhEqualStringZ(windowClassName, L"#32770", FALSE)) + { + PhEnumChildWindows( + currentWindow, + 0x1000, + PhpReInitializeThemeWindowEnumChildWindows, + NULL + ); + //PhReInitializeWindowTheme(currentWindow); + } + + InvalidateRect(currentWindow, NULL, TRUE); + } + } + } + } while (currentWindow); + + InvalidateRect(WindowHandle, NULL, FALSE); +} + +VOID PhInitializeThemeWindowFrame( + _In_ HWND WindowHandle + ) +{ + if (!PhpThemeEnable) + return; + + switch (PhpThemeColorMode) + { + case 0: // New colors + if (WindowsVersion >= WINDOWS_10_RS5) + RemoveProp(WindowHandle, L"UseImmersiveDarkModeColors"); + break; + case 1: // Old colors + if (WindowsVersion >= WINDOWS_10_RS5) + SetProp(WindowHandle, L"UseImmersiveDarkModeColors", (HANDLE)TRUE); + break; + } +} + +VOID PhInitializeThemeWindowHeader( + _In_ HWND HeaderWindow + ) +{ + PPHP_THEME_WINDOW_HEADER_CONTEXT context; + + context = PhAllocateZero(sizeof(PHP_THEME_WINDOW_HEADER_CONTEXT)); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(HeaderWindow, GWLP_WNDPROC); + + PhSetWindowContext(HeaderWindow, SHRT_MAX, context); + SetWindowLongPtr(HeaderWindow, GWLP_WNDPROC, (LONG_PTR)PhpThemeWindowHeaderSubclassProc); + + InvalidateRect(HeaderWindow, NULL, FALSE); +} + +VOID PhInitializeThemeWindowTabControl( + _In_ HWND TabControlWindow + ) +{ + PPHP_THEME_WINDOW_TAB_CONTEXT context; + + if (!PhpTabControlFontHandle) + PhpTabControlFontHandle = PhDuplicateFontWithNewHeight(PhApplicationFont, 15); + + context = PhAllocateZero(sizeof(PHP_THEME_WINDOW_TAB_CONTEXT)); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(TabControlWindow, GWLP_WNDPROC); + + PhSetWindowContext(TabControlWindow, SHRT_MAX, context); + SetWindowLongPtr(TabControlWindow, GWLP_WNDPROC, (LONG_PTR)PhpThemeWindowTabControlWndSubclassProc); + + PhSetWindowStyle(TabControlWindow, TCS_OWNERDRAWFIXED, TCS_OWNERDRAWFIXED); + + SetWindowFont(TabControlWindow, PhpTabControlFontHandle, FALSE); + + InvalidateRect(TabControlWindow, NULL, FALSE); +} + +VOID PhInitializeWindowThemeStatusBar( + _In_ HWND StatusBarHandle + ) +{ + PPHP_THEME_WINDOW_STATUSBAR_CONTEXT context; + + context = PhAllocateZero(sizeof(PHP_THEME_WINDOW_STATUSBAR_CONTEXT)); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(StatusBarHandle, GWLP_WNDPROC); + context->StatusThemeData = OpenThemeData(StatusBarHandle, VSCLASS_STATUS); + + PhSetWindowContext(StatusBarHandle, SHRT_MAX, context); + SetWindowLongPtr(StatusBarHandle, GWLP_WNDPROC, (LONG_PTR)PhpThemeWindowStatusbarWndSubclassProc); + + InvalidateRect(StatusBarHandle, NULL, FALSE); +} + +VOID PhInitializeThemeWindowGroupBox( + _In_ HWND GroupBoxHandle + ) +{ + WNDPROC groupboxWindowProc; + + groupboxWindowProc = (WNDPROC)GetWindowLongPtr(GroupBoxHandle, GWLP_WNDPROC); + PhSetWindowContext(GroupBoxHandle, SHRT_MAX, groupboxWindowProc); + SetWindowLongPtr(GroupBoxHandle, GWLP_WNDPROC, (LONG_PTR)PhpThemeWindowGroupBoxSubclassProc); + + InvalidateRect(GroupBoxHandle, NULL, FALSE); +} + +VOID PhInitializeThemeWindowEditControl( + _In_ HWND EditControlHandle + ) +{ + WNDPROC editControlWindowProc; + + // HACK: The searchbox control does its own themed drawing and it uses the + // same window context value so we know when to ignore theming. + if (PhGetWindowContext(EditControlHandle, SHRT_MAX)) + return; + + editControlWindowProc = (WNDPROC)GetWindowLongPtr(EditControlHandle, GWLP_WNDPROC); + PhSetWindowContext(EditControlHandle, SHRT_MAX, editControlWindowProc); + SetWindowLongPtr(EditControlHandle, GWLP_WNDPROC, (LONG_PTR)PhpThemeWindowEditSubclassProc); + + InvalidateRect(EditControlHandle, NULL, FALSE); + //SetWindowPos(EditControlHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_DRAWFRAME); +} + +BOOLEAN CALLBACK PhpThemeWindowEnumChildWindows( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ) +{ + WCHAR windowClassName[MAX_PATH]; + + PhEnumChildWindows( + WindowHandle, + 0x1000, + PhpThemeWindowEnumChildWindows, + NULL + ); + + if (PhGetWindowContext(WindowHandle, SHRT_MAX)) // HACK + return TRUE; + + if (!GetClassName(WindowHandle, windowClassName, RTL_NUMBER_OF(windowClassName))) + windowClassName[0] = UNICODE_NULL; + + //dprintf("PhpThemeWindowEnumChildWindows: %S\r\n", windowClassName); + + if (PhEqualStringZ(windowClassName, L"#32770", TRUE)) + { + PhInitializeWindowTheme(WindowHandle, TRUE); + } + else if (PhEqualStringZ(windowClassName, L"Button", FALSE)) + { + if ((PhGetWindowStyle(WindowHandle) & BS_GROUPBOX) == BS_GROUPBOX) + { + PhInitializeThemeWindowGroupBox(WindowHandle); + } + } + else if (PhEqualStringZ(windowClassName, L"SysTabControl32", FALSE)) + { + PhInitializeThemeWindowTabControl(WindowHandle); + } + else if (PhEqualStringZ(windowClassName, L"msctls_statusbar32", FALSE)) + { + PhInitializeWindowThemeStatusBar(WindowHandle); + } + else if (PhEqualStringZ(windowClassName, L"Edit", TRUE)) + { + PhInitializeThemeWindowEditControl(WindowHandle); + } + else if (PhEqualStringZ(windowClassName, L"ScrollBar", FALSE)) + { + if (WindowsVersion >= WINDOWS_10_RS5) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + PhSetControlTheme(WindowHandle, L""); + break; + case 1: // Old colors + PhSetControlTheme(WindowHandle, L"DarkMode_Explorer"); + break; + } + } + } + else if (PhEqualStringZ(windowClassName, L"SysHeader32", TRUE)) + { + PhInitializeThemeWindowHeader(WindowHandle); + } + else if (PhEqualStringZ(windowClassName, L"SysListView32", FALSE)) + { + if (WindowsVersion >= WINDOWS_10_RS5) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + PhSetControlTheme(WindowHandle, L"explorer"); + break; + case 1: // Old colors + PhSetControlTheme(WindowHandle, L"DarkMode_Explorer"); + break; + } + } + + if (PhpThemeBorderEnable) + { + PhSetWindowStyle(WindowHandle, WS_BORDER, WS_BORDER); + PhSetWindowExStyle(WindowHandle, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE); + } + else + { + PhSetWindowStyle(WindowHandle, WS_BORDER, 0); + PhSetWindowExStyle(WindowHandle, WS_EX_CLIENTEDGE, 0); + } + + SetWindowPos(WindowHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + + switch (PhpThemeColorMode) + { + case 0: // New colors + ListView_SetBkColor(WindowHandle, RGB(0xff, 0xff, 0xff)); + ListView_SetTextBkColor(WindowHandle, RGB(0xff, 0xff, 0xff)); + ListView_SetTextColor(WindowHandle, RGB(0x0, 0x0, 0x0)); + break; + case 1: // Old colors + ListView_SetBkColor(WindowHandle, RGB(30, 30, 30)); + ListView_SetTextBkColor(WindowHandle, RGB(30, 30, 30)); + ListView_SetTextColor(WindowHandle, PhpThemeWindowTextColor); + break; + } + + //InvalidateRect(WindowHandle, NULL, FALSE); + } + else if (PhEqualStringZ(windowClassName, L"SysTreeView32", FALSE)) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + break; + case 1: // Old colors + TreeView_SetBkColor(WindowHandle, RGB(30, 30, 30)); + //TreeView_SetTextBkColor(WindowHandle, RGB(30, 30, 30)); + TreeView_SetTextColor(WindowHandle, PhpThemeWindowTextColor); + break; + } + + //InvalidateRect(WindowHandle, NULL, FALSE); + } + else if (PhEqualStringZ(windowClassName, L"RICHEDIT50W", FALSE)) + { + if (PhpThemeBorderEnable) + PhSetWindowStyle(WindowHandle, WS_BORDER, WS_BORDER); + else + PhSetWindowStyle(WindowHandle, WS_BORDER, 0); + + SetWindowPos(WindowHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + +#define EM_SETBKGNDCOLOR (WM_USER + 67) + switch (PhpThemeColorMode) + { + case 0: // New colors + SendMessage(WindowHandle, EM_SETBKGNDCOLOR, 0, RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SendMessage(WindowHandle, EM_SETBKGNDCOLOR, 0, RGB(30, 30, 30)); + break; + } + + //InvalidateRect(WindowHandle, NULL, FALSE); + } + else if (PhEqualStringZ(windowClassName, L"PhTreeNew", FALSE)) + { + if (PhpThemeBorderEnable) + PhSetWindowExStyle(WindowHandle, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE); + else + PhSetWindowExStyle(WindowHandle, WS_EX_CLIENTEDGE, 0); + + SetWindowPos(WindowHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + + switch (PhpThemeColorMode) + { + case 0: // New colors + TreeNew_ThemeSupport(WindowHandle, FALSE); + break; + case 1: // Old colors + TreeNew_ThemeSupport(WindowHandle, TRUE); + break; + } + + //InvalidateRect(WindowHandle, NULL, TRUE); + } + + return TRUE; +} + +BOOLEAN CALLBACK PhpReInitializeThemeWindowEnumChildWindows( + _In_ HWND WindowHandle, + _In_opt_ PVOID Context + ) +{ + WCHAR windowClassName[MAX_PATH]; + + PhEnumChildWindows( + WindowHandle, + 0x1000, + PhpReInitializeThemeWindowEnumChildWindows, + NULL + ); + + if (!GetClassName(WindowHandle, windowClassName, RTL_NUMBER_OF(windowClassName))) + windowClassName[0] = UNICODE_NULL; + + //dprintf("PhpReInitializeThemeWindowEnumChildWindows: %S\r\n", windowClassName); + + if (PhEqualStringZ(windowClassName, L"SysListView32", FALSE)) + { + if (WindowsVersion >= WINDOWS_10_RS5) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + PhSetControlTheme(WindowHandle, L"explorer"); + break; + case 1: // Old colors + PhSetControlTheme(WindowHandle, L"DarkMode_Explorer"); + break; + } + } + + switch (PhpThemeColorMode) + { + case 0: // New colors + ListView_SetBkColor(WindowHandle, RGB(0xff, 0xff, 0xff)); + ListView_SetTextBkColor(WindowHandle, RGB(0xff, 0xff, 0xff)); + ListView_SetTextColor(WindowHandle, RGB(0x0, 0x0, 0x0)); + break; + case 1: // Old colors + ListView_SetBkColor(WindowHandle, RGB(30, 30, 30)); + ListView_SetTextBkColor(WindowHandle, RGB(30, 30, 30)); + ListView_SetTextColor(WindowHandle, PhpThemeWindowTextColor); + break; + } + } + else if (PhEqualStringZ(windowClassName, L"ScrollBar", FALSE)) + { + if (WindowsVersion >= WINDOWS_10_RS5) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + PhSetControlTheme(WindowHandle, L""); + break; + case 1: // Old colors + PhSetControlTheme(WindowHandle, L"DarkMode_Explorer"); + break; + } + } + } + else if (PhEqualStringZ(windowClassName, L"PhTreeNew", FALSE)) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + TreeNew_ThemeSupport(WindowHandle, FALSE); + break; + case 1: // Old colors + TreeNew_ThemeSupport(WindowHandle, TRUE); + break; + } + } + else if (PhEqualStringZ(windowClassName, L"Edit", FALSE)) + { + SendMessage(WindowHandle, WM_THEMECHANGED, 0, 0); // searchbox.c + } + + InvalidateRect(WindowHandle, NULL, TRUE); + + return TRUE; +} + +BOOLEAN PhThemeWindowDrawItem( + _In_ PDRAWITEMSTRUCT DrawInfo + ) +{ + BOOLEAN isGrayed = (DrawInfo->itemState & CDIS_GRAYED) == CDIS_GRAYED; + BOOLEAN isChecked = (DrawInfo->itemState & CDIS_CHECKED) == CDIS_CHECKED; + BOOLEAN isDisabled = (DrawInfo->itemState & CDIS_DISABLED) == CDIS_DISABLED; + BOOLEAN isSelected = (DrawInfo->itemState & CDIS_SELECTED) == CDIS_SELECTED; + //BOOLEAN isHighlighted = (DrawInfo->itemState & CDIS_HOT) == CDIS_HOT; + BOOLEAN isFocused = (DrawInfo->itemState & CDIS_FOCUS) == CDIS_FOCUS; + //BOOLEAN isGrayed = (DrawInfo->itemState & ODS_GRAYED) == ODS_GRAYED; + //BOOLEAN isChecked = (DrawInfo->itemState & ODS_CHECKED) == ODS_CHECKED; + //BOOLEAN isDisabled = (DrawInfo->itemState & ODS_DISABLED) == ODS_DISABLED; + //BOOLEAN isSelected = (DrawInfo->itemState & ODS_SELECTED) == ODS_SELECTED; + BOOLEAN isHighlighted = (DrawInfo->itemState & ODS_HOTLIGHT) == ODS_HOTLIGHT; + + SetBkMode(DrawInfo->hDC, TRANSPARENT); + + switch (DrawInfo->CtlType) + { + case ODT_STATIC: + break; + case ODT_HEADER: + { + HDITEM headerItem; + WCHAR headerText[MAX_PATH]; + + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(30, 30, 30)); + + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + DrawEdge(DrawInfo->hDC, &DrawInfo->rcItem, BDR_RAISEDOUTER, BF_RIGHT); + + memset(headerText, 0, sizeof(headerText)); + memset(&headerItem, 0, sizeof(HDITEM)); + headerItem.mask = HDI_TEXT; + headerItem.cchTextMax = RTL_NUMBER_OF(headerText); + headerItem.pszText = headerText; + + if (Header_GetItem(DrawInfo->hwndItem, DrawInfo->itemID, &headerItem)) + { + DrawText( + DrawInfo->hDC, + headerText, + (UINT)PhCountStringZ(headerText), + &DrawInfo->rcItem, + DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE //DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX | DT_NOCLIP + ); + } + } + return TRUE; + case ODT_MENU: + { + PPH_EMENU_ITEM menuItemInfo = (PPH_EMENU_ITEM)DrawInfo->itemData; + + //FillRect( + // DrawInfo->hDC, + // &DrawInfo->rcItem, + // CreateSolidBrush(RGB(0, 0, 0)) + // ); + //SetTextColor(DrawInfo->hDC, RGB(0xff, 0xff, 0xff)); + + if (isDisabled) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_GRAYTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_GRAYTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(28, 28, 28)); + break; + } + + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + } + else if (isSelected) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->hDC, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->hDC, PhpThemeWindowBackgroundColor); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hDC, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->hDC, RGB(128, 128, 128)); + break; + } + + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + } + else + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_WINDOWTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(28, 28, 28)); + break; + } + + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + } + + if (isChecked) + { + static PH_STRINGREF menuCheckText = PH_STRINGREF_INIT(L"\u2713"); + COLORREF oldTextColor; + + //HFONT marlettFontHandle = CreateFont( + // 0, 0, 0, 0, + // FW_DONTCARE, + // FALSE, + // FALSE, + // FALSE, + // DEFAULT_CHARSET, + // OUT_OUTLINE_PRECIS, + // CLIP_DEFAULT_PRECIS, + // CLEARTYPE_QUALITY, + // VARIABLE_PITCH, + // L"Arial Unicode MS" + // ); + + switch (PhpThemeColorMode) + { + case 0: // New colors + oldTextColor = SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_WINDOWTEXT)); + break; + case 1: // Old colors + oldTextColor = SetTextColor(DrawInfo->hDC, PhpThemeWindowTextColor); + //FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + //SetTextColor(DrawInfo->hDC, PhpThemeWindowTextColor); + break; + } + + DrawInfo->rcItem.left += 8; + DrawInfo->rcItem.top += 3; + DrawText( + DrawInfo->hDC, + menuCheckText.Buffer, + (UINT)menuCheckText.Length / sizeof(WCHAR), + &DrawInfo->rcItem, + DT_VCENTER | DT_NOCLIP + ); + DrawInfo->rcItem.left -= 8; + DrawInfo->rcItem.top -= 3; + + SetTextColor(DrawInfo->hDC, oldTextColor); + } + + if (menuItemInfo->Flags & PH_EMENU_SEPARATOR) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetDCBrushColor(DrawInfo->hDC, RGB(0xff, 0xff, 0xff)); + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetDCBrushColor(DrawInfo->hDC, RGB(28, 28, 28)); + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + break; + } + + //DrawInfo->rcItem.top += 1; + //DrawInfo->rcItem.bottom -= 2; + //DrawFocusRect(drawInfo->hDC, &drawInfo->rcItem); + + SetRect( + &DrawInfo->rcItem, + DrawInfo->rcItem.left + 25, + DrawInfo->rcItem.top + 2, + DrawInfo->rcItem.right, + DrawInfo->rcItem.bottom - 2 + ); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetDCBrushColor(DrawInfo->hDC, RGB(0xff, 0xff, 0xff)); + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetDCBrushColor(DrawInfo->hDC, RGB(78, 78, 78)); + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + break; + } + + //DrawEdge(drawInfo->hDC, &drawInfo->rcItem, BDR_RAISEDINNER, BF_TOP); + } + else + { + PH_STRINGREF part; + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + + PhInitializeStringRef(&part, menuItemInfo->Text); + PhSplitStringRefAtLastChar(&part, '\b', &firstPart, &secondPart); + + //SetDCBrushColor(DrawInfo->hDC, RGB(28, 28, 28)); + //FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + + if (menuItemInfo->Bitmap) + { + HDC hdcMem; + BLENDFUNCTION blendFunction; + + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.BlendFlags = 0; + blendFunction.SourceConstantAlpha = 255; + blendFunction.AlphaFormat = AC_SRC_ALPHA; + + hdcMem = CreateCompatibleDC(DrawInfo->hDC); + SelectBitmap(hdcMem, menuItemInfo->Bitmap); + + GdiAlphaBlend( + DrawInfo->hDC, + DrawInfo->rcItem.left + 4, + DrawInfo->rcItem.top + 4, + 16, + 16, + hdcMem, + 0, + 0, + 16, + 16, + blendFunction + ); + + DeleteDC(hdcMem); + } + + DrawInfo->rcItem.left += 25; + DrawInfo->rcItem.right -= 25; + + DrawText( + DrawInfo->hDC, + firstPart.Buffer, + (UINT)firstPart.Length / sizeof(WCHAR), + &DrawInfo->rcItem, + DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX | DT_NOCLIP + ); + + DrawText( + DrawInfo->hDC, + secondPart.Buffer, + (UINT)secondPart.Length / sizeof(WCHAR), + &DrawInfo->rcItem, + DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX | DT_NOCLIP + ); + } + + return TRUE; + } + case ODT_COMBOBOX: + { + WCHAR comboText[MAX_PATH]; + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_WINDOWTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(DrawInfo->hDC, RGB(28, 28, 28)); + break; + } + + FillRect(DrawInfo->hDC, &DrawInfo->rcItem, GetStockBrush(DC_BRUSH)); + + if (ComboBox_GetLBText(DrawInfo->hwndItem, DrawInfo->itemID, (LPARAM)comboText) != CB_ERR) + { + DrawText( + DrawInfo->hDC, + comboText, + (UINT)PhCountStringZ(comboText), + &DrawInfo->rcItem, + DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE + ); + } + return TRUE; + } + break; + } + + return FALSE; +} + +BOOLEAN PhThemeWindowMeasureItem( + _In_ PMEASUREITEMSTRUCT DrawInfo + ) +{ + if (DrawInfo->CtlType == ODT_MENU) + { + PPH_EMENU_ITEM menuItemInfo = (PPH_EMENU_ITEM)DrawInfo->itemData; + + DrawInfo->itemWidth = 150; + DrawInfo->itemHeight = 26; + + if ((menuItemInfo->Flags & PH_EMENU_SEPARATOR) == PH_EMENU_SEPARATOR) + { + DrawInfo->itemHeight = GetSystemMetrics(SM_CYMENU) >> 2; + } + else if (menuItemInfo->Text) + { + HDC hdc; + + if (!PhpMenuFontHandle) + { + NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) }; + + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + { + PhpMenuFontHandle = CreateFontIndirect(&metrics.lfMessageFont); + } + } + + if (hdc = GetDC(NULL)) + { + PWSTR text; + SIZE_T textCount; + SIZE textSize; + HFONT oldFont; + + text = menuItemInfo->Text; + textCount = PhCountStringZ(text); + oldFont = SelectFont(hdc, PhpMenuFontHandle); + + if (GetTextExtentPoint32(hdc, text, (ULONG)textCount, &textSize)) + { + DrawInfo->itemWidth = textSize.cx + (GetSystemMetrics(SM_CYBORDER) * 2) + 90; // HACK + DrawInfo->itemHeight = GetSystemMetrics(SM_CYMENU) + (GetSystemMetrics(SM_CYBORDER) * 2) + 1; + } + + SelectFont(hdc, oldFont); + ReleaseDC(NULL, hdc); + } + } + + return TRUE; + } + + return FALSE; +} + +LRESULT CALLBACK PhpThemeWindowDrawButton( + _In_ LPNMCUSTOMDRAW DrawInfo + ) +{ + BOOLEAN isGrayed = (DrawInfo->uItemState & CDIS_GRAYED) == CDIS_GRAYED; + BOOLEAN isChecked = (DrawInfo->uItemState & CDIS_CHECKED) == CDIS_CHECKED; + BOOLEAN isDisabled = (DrawInfo->uItemState & CDIS_DISABLED) == CDIS_DISABLED; + BOOLEAN isSelected = (DrawInfo->uItemState & CDIS_SELECTED) == CDIS_SELECTED; + BOOLEAN isHighlighted = (DrawInfo->uItemState & CDIS_HOT) == CDIS_HOT; + BOOLEAN isFocused = (DrawInfo->uItemState & CDIS_FOCUS) == CDIS_FOCUS; + + switch (DrawInfo->dwDrawStage) + { + case CDDS_PREPAINT: + { + PPH_STRING buttonText; + ULONG_PTR buttonStyle; + HFONT oldFont; + + buttonText = PhGetWindowText(DrawInfo->hdr.hwndFrom); + buttonStyle = PhGetWindowStyle(DrawInfo->hdr.hwndFrom); + + if (isSelected) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + //SetTextColor(DrawInfo->hdc, RGB(0, 0, 0xff)); + SetDCBrushColor(DrawInfo->hdc, GetSysColor(COLOR_HIGHLIGHT)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(DrawInfo->hdc, RGB(78, 78, 78)); + break; + } + FillRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + } + else if (isHighlighted) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + //SetTextColor(DrawInfo->hdc, RGB(0, 0, 0xff)); + SetDCBrushColor(DrawInfo->hdc, GetSysColor(COLOR_HIGHLIGHT)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetDCBrushColor(DrawInfo->hdc, RGB(65, 65, 65)); + break; + } + FillRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + } + else + { + switch (PhpThemeColorMode) + { + case 0: // New colors + //SetTextColor(DrawInfo->hdc, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->hdc, PhpThemeWindowTextColor); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hdc, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->hdc, RGB(42, 42, 42)); // WindowForegroundColor + break; + } + FillRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + } + + if ((buttonStyle & BS_ICON) == BS_ICON) + { + RECT bufferRect = + { + 0, 0, + DrawInfo->rc.right - DrawInfo->rc.left, + DrawInfo->rc.bottom - DrawInfo->rc.top + }; + HICON buttonIcon; + + SetDCBrushColor(DrawInfo->hdc, RGB(65, 65, 65)); + FrameRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + + if (!(buttonIcon = Static_GetIcon(DrawInfo->hdr.hwndFrom, 0))) + buttonIcon = (HICON)SendMessage(DrawInfo->hdr.hwndFrom, BM_GETIMAGE, IMAGE_ICON, 0); + + if (buttonIcon) + { + ICONINFO iconInfo; + BITMAP bmp; + LONG width; + LONG height; + + memset(&iconInfo, 0, sizeof(ICONINFO)); + memset(&bmp, 0, sizeof(BITMAP)); + + GetIconInfo(buttonIcon, &iconInfo); + + if (iconInfo.hbmColor) + { + if (GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmp)) + { + width = bmp.bmWidth; + height = bmp.bmHeight; + } + + DeleteBitmap(iconInfo.hbmColor); + } + else if (iconInfo.hbmMask) + { + if (GetObject(iconInfo.hbmMask, sizeof(BITMAP), &bmp)) + { + width = bmp.bmWidth; + height = bmp.bmHeight / 2; + } + + DeleteBitmap(iconInfo.hbmMask); + } + + bufferRect.left += 1; // HACK + bufferRect.top += 1; // HACK + + DrawIconEx( + DrawInfo->hdc, + bufferRect.left + ((bufferRect.right - bufferRect.left) - width) / 2, + bufferRect.top + ((bufferRect.bottom - bufferRect.top) - height) / 2, + buttonIcon, + width, + height, + 0, + NULL, + DI_NORMAL + ); + } + } + else if ((buttonStyle & BS_CHECKBOX) == BS_CHECKBOX) + { + + if (Button_GetCheck(DrawInfo->hdr.hwndFrom) == BST_CHECKED) + { + HFONT newFont = PhDuplicateFontWithNewHeight(PhApplicationFont, 16); + + oldFont = SelectFont(DrawInfo->hdc, newFont); + DrawText( + DrawInfo->hdc, + L"\u2611", + -1, + &DrawInfo->rc, + DT_LEFT | DT_SINGLELINE | DT_VCENTER + ); + SelectFont(DrawInfo->hdc, oldFont); + DeleteFont(newFont); + } + else + { + HFONT newFont = PhDuplicateFontWithNewHeight(PhApplicationFont, 22); + + oldFont = SelectFont(DrawInfo->hdc, newFont); + DrawText( + DrawInfo->hdc, + L"\u2610", + -1, + &DrawInfo->rc, + DT_LEFT | DT_SINGLELINE | DT_VCENTER + ); + SelectFont(DrawInfo->hdc, oldFont); + DeleteFont(newFont); + } + + DrawInfo->rc.left += 10; + DrawText( + DrawInfo->hdc, + buttonText->Buffer, + (UINT)buttonText->Length / sizeof(WCHAR), + &DrawInfo->rc, + DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_HIDEPREFIX + ); + DrawInfo->rc.left -= 10; + } + else + { + RECT bufferRect = + { + 0, 0, + DrawInfo->rc.right - DrawInfo->rc.left, + DrawInfo->rc.bottom - DrawInfo->rc.top + }; + HICON buttonIcon; + + SetDCBrushColor(DrawInfo->hdc, RGB(65, 65, 65)); + FrameRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + + if (!(buttonIcon = Static_GetIcon(DrawInfo->hdr.hwndFrom, 0))) + buttonIcon = (HICON)SendMessage(DrawInfo->hdr.hwndFrom, BM_GETIMAGE, IMAGE_ICON, 0); + + if (buttonIcon) + { + DrawIconEx( + DrawInfo->hdc, + bufferRect.left + ((bufferRect.right - bufferRect.left) - 16) / 2, + bufferRect.top + ((bufferRect.bottom - bufferRect.top) - 16) / 2, + buttonIcon, + 16, + 16, + 0, + NULL, + DI_NORMAL + ); + } + + DrawText( + DrawInfo->hdc, + buttonText->Buffer, + (UINT)buttonText->Length / sizeof(WCHAR), + &DrawInfo->rc, + DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_HIDEPREFIX + ); + } + + PhDereferenceObject(buttonText); + } + + return CDRF_SKIPDEFAULT; + } + + return CDRF_DODEFAULT; +} + +LRESULT CALLBACK PhThemeWindowDrawRebar( + _In_ LPNMCUSTOMDRAW DrawInfo + ) +{ + switch (DrawInfo->dwDrawStage) + { + case CDDS_PREPAINT: + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(DrawInfo->hdc, GetSysColor(COLOR_3DFACE)); // RGB(0xff, 0xff, 0xff)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(DrawInfo->hdc, RGB(65, 65, 65)); + break; + } + + FillRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + } + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(DrawInfo->hdc, GetSysColor(COLOR_3DFACE)); // RGB(0x0, 0x0, 0x0)); + case 1: // Old colors + SetTextColor(DrawInfo->hdc, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->hdc, RGB(65, 65, 65)); + break; + } + + FillRect(DrawInfo->hdc, &DrawInfo->rc, GetStockBrush(DC_BRUSH)); + } + return CDRF_SKIPDEFAULT; + } + + return CDRF_DODEFAULT; +} + +LRESULT CALLBACK PhThemeWindowDrawToolbar( + _In_ LPNMTBCUSTOMDRAW DrawInfo + ) +{ + switch (DrawInfo->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT; + case CDDS_ITEMPREPAINT: + { + TBBUTTONINFO buttonInfo = + { + sizeof(TBBUTTONINFO), + TBIF_STYLE | TBIF_COMMAND | TBIF_STATE | TBIF_IMAGE + }; + + SetBkMode(DrawInfo->nmcd.hdc, TRANSPARENT); + + ULONG currentIndex = (ULONG)SendMessage( + DrawInfo->nmcd.hdr.hwndFrom, + TB_COMMANDTOINDEX, + DrawInfo->nmcd.dwItemSpec, + 0 + ); + BOOLEAN isHighlighted = SendMessage( + DrawInfo->nmcd.hdr.hwndFrom, + TB_GETHOTITEM, + 0, + 0 + ) == currentIndex; + BOOLEAN isMouseDown = SendMessage( + DrawInfo->nmcd.hdr.hwndFrom, + TB_ISBUTTONPRESSED, + DrawInfo->nmcd.dwItemSpec, + 0 + ) == 0; + + if (SendMessage( + DrawInfo->nmcd.hdr.hwndFrom, + TB_GETBUTTONINFO, + (ULONG)DrawInfo->nmcd.dwItemSpec, + (LPARAM)&buttonInfo + ) == -1) + { + break; + } + + //if (isMouseDown) + //{ + // SetTextColor(DrawInfo->nmcd.hdc, PhpThemeWindowTextColor); + // SetDCBrushColor(DrawInfo->nmcd.hdc, RGB(0xff, 0xff, 0xff)); + // FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + //} + + if (isHighlighted) + { + //INT stateId; + + //// Themed background + + //if (node->Selected) + //{ + // if (i == Context->HotNodeIndex) + // stateId = TREIS_HOTSELECTED; + // else if (!Context->HasFocus) + // stateId = TREIS_SELECTEDNOTFOCUS; + // else + // stateId = TREIS_SELECTED; + //} + //else + //{ + // if (i == Context->HotNodeIndex) + // stateId = TREIS_HOT; + // else + // stateId = INT_MAX; + //} + + //// Themed background + + //if (stateId != INT_MAX) + { + //if (!Context->FixedColumnVisible) + //{ + // rowRect.left = Context->NormalLeft - hScrollPosition; + //} + + } + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->nmcd.hdc, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->nmcd.hdc, PhpThemeWindowBackgroundColor); + FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->nmcd.hdc, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->nmcd.hdc, RGB(128, 128, 128)); + FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + break; + } + } + else + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->nmcd.hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(DrawInfo->nmcd.hdc, GetSysColor(COLOR_3DFACE));// RGB(0xff, 0xff, 0xff)); + FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->nmcd.hdc, PhpThemeWindowTextColor); + SetDCBrushColor(DrawInfo->nmcd.hdc, RGB(65, 65, 65)); //RGB(28, 28, 28)); // RGB(65, 65, 65)); + FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + break; + } + } + + if (!PhpToolBarFontHandle) + { + NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) }; + + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + { + PhpToolBarFontHandle = CreateFontIndirect(&metrics.lfMessageFont); + } + } + + SelectFont(DrawInfo->nmcd.hdc, PhpToolBarFontHandle); + + if (buttonInfo.iImage != -1) + { + HIMAGELIST toolbarImageList; + + if (toolbarImageList = (HIMAGELIST)SendMessage( + DrawInfo->nmcd.hdr.hwndFrom, + TB_GETIMAGELIST, + 0, + 0 + )) + { + DrawInfo->nmcd.rc.left += 5; + + ImageList_Draw( + toolbarImageList, + buttonInfo.iImage, + DrawInfo->nmcd.hdc, + DrawInfo->nmcd.rc.left, + DrawInfo->nmcd.rc.top + 3, + ILD_NORMAL + ); + } + } + + if (buttonInfo.fsStyle & BTNS_SHOWTEXT) + { + WCHAR buttonText[MAX_PATH] = L""; + + SendMessage( + DrawInfo->nmcd.hdr.hwndFrom, + TB_GETBUTTONTEXT, + (ULONG)DrawInfo->nmcd.dwItemSpec, + (LPARAM)buttonText + ); + + DrawInfo->nmcd.rc.left += 10; + DrawText( + DrawInfo->nmcd.hdc, + buttonText, + (UINT)PhCountStringZ(buttonText), + &DrawInfo->nmcd.rc, + DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX + ); + } + + //DrawInfo->clrText = RGB(0x0, 0xff, 0); + //return TBCDRF_USECDCOLORS | CDRF_NEWFONT; + } + return CDRF_SKIPDEFAULT; + } + + return CDRF_DODEFAULT; +} + +LRESULT CALLBACK PhpThemeWindowDrawListViewGroup( + _In_ LPNMLVCUSTOMDRAW DrawInfo + ) +{ + switch (DrawInfo->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + { + SetBkMode(DrawInfo->nmcd.hdc, TRANSPARENT); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(DrawInfo->nmcd.hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(DrawInfo->nmcd.hdc, PhpThemeWindowTextColor); + //FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(DrawInfo->nmcd.hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(DrawInfo->nmcd.hdc, RGB(128, 128, 128)); + //FillRect(DrawInfo->nmcd.hdc, &DrawInfo->nmcd.rc, GetStockBrush(DC_BRUSH)); + break; + } + + if (!PhpListViewFontHandle) + { + NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) }; + + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + { + metrics.lfMessageFont.lfHeight = PhMultiplyDivideSigned(-11, PhGlobalDpi, 96); + metrics.lfMessageFont.lfWeight = FW_BOLD; + + PhpListViewFontHandle = CreateFontIndirect(&metrics.lfMessageFont); + } + } + + SelectFont(DrawInfo->nmcd.hdc, PhpListViewFontHandle); + + LVGROUP groupInfo = { sizeof(LVGROUP) }; + groupInfo.mask = LVGF_HEADER; + if (ListView_GetGroupInfo( + DrawInfo->nmcd.hdr.hwndFrom, + (ULONG)DrawInfo->nmcd.dwItemSpec, + &groupInfo + ) == -1) + { + break; + } + + SetTextColor(DrawInfo->nmcd.hdc, RGB(255, 69, 0)); + SetDCBrushColor(DrawInfo->nmcd.hdc, RGB(65, 65, 65)); + + DrawInfo->rcText.top += 2; + DrawInfo->rcText.bottom -= 2; + FillRect(DrawInfo->nmcd.hdc, &DrawInfo->rcText, GetStockBrush(DC_BRUSH)); + DrawInfo->rcText.top -= 2; + DrawInfo->rcText.bottom += 2; + + //SetTextColor(DrawInfo->nmcd.hdc, RGB(255, 69, 0)); + //SetDCBrushColor(DrawInfo->nmcd.hdc, RGB(65, 65, 65)); + + DrawInfo->rcText.left += 10; + DrawText( + DrawInfo->nmcd.hdc, + groupInfo.pszHeader, + -1, + &DrawInfo->rcText, + DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX + ); + DrawInfo->rcText.left -= 10; + } + return CDRF_SKIPDEFAULT; + } + + return CDRF_DODEFAULT; +} + +LRESULT CALLBACK PhpThemeWindowSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + WNDPROC oldWndProc; + + if (!(oldWndProc = PhGetWindowContext(hWnd, SHRT_MAX))) + return FALSE; + + switch (uMsg) + { + case WM_DESTROY: + { + PhRemoveWindowContext(hWnd, SHRT_MAX); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + } + break; + case WM_NOTIFY: + { + LPNMHDR data = (LPNMHDR)lParam; + + switch (data->code) + { + case NM_CUSTOMDRAW: + { + LPNMCUSTOMDRAW customDraw = (LPNMCUSTOMDRAW)lParam; + WCHAR className[MAX_PATH]; + + if (!GetClassName(customDraw->hdr.hwndFrom, className, RTL_NUMBER_OF(className))) + className[0] = UNICODE_NULL; + + //dprintf("NM_CUSTOMDRAW: %S\r\n", className); + + if (PhEqualStringZ(className, L"Button", FALSE)) + { + return PhpThemeWindowDrawButton(customDraw); + } + else if (PhEqualStringZ(className, L"ReBarWindow32", FALSE)) + { + return PhThemeWindowDrawRebar(customDraw); + } + else if (PhEqualStringZ(className, L"ToolbarWindow32", FALSE)) + { + return PhThemeWindowDrawToolbar((LPNMTBCUSTOMDRAW)customDraw); + } + else if (PhEqualStringZ(className, L"SysListView32", FALSE)) + { + LPNMLVCUSTOMDRAW listViewCustomDraw = (LPNMLVCUSTOMDRAW)customDraw; + + if (listViewCustomDraw->dwItemType == LVCDI_GROUP) + { + return PhpThemeWindowDrawListViewGroup(listViewCustomDraw); + } + } + } + } + } + break; + case WM_CTLCOLOREDIT: + { + SetBkMode((HDC)wParam, TRANSPARENT); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor((HDC)wParam, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor((HDC)wParam, PhpThemeWindowTextColor); + return (INT_PTR)GetStockBrush(DC_BRUSH); + case 1: // Old colors + SetTextColor((HDC)wParam, PhpThemeWindowTextColor); + SetDCBrushColor((HDC)wParam, RGB(60, 60, 60)); + return (INT_PTR)GetStockBrush(DC_BRUSH); + } + } + break; + case WM_CTLCOLORBTN: + case WM_CTLCOLORDLG: + case WM_CTLCOLORSTATIC: + case WM_CTLCOLORLISTBOX: + { + SetBkMode((HDC)wParam, TRANSPARENT); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor((HDC)wParam, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor((HDC)wParam, PhpThemeWindowTextColor); + return (INT_PTR)GetStockBrush(DC_BRUSH); + case 1: // Old colors + SetTextColor((HDC)wParam, PhpThemeWindowTextColor); + SetDCBrushColor((HDC)wParam, RGB(30, 30, 30)); + return (INT_PTR)GetStockBrush(DC_BRUSH); + } + } + break; + case WM_MEASUREITEM: + if (PhThemeWindowMeasureItem((LPMEASUREITEMSTRUCT)lParam)) + return TRUE; + break; + case WM_DRAWITEM: + if (PhThemeWindowDrawItem((LPDRAWITEMSTRUCT)lParam)) + return TRUE; + break; + } + + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); +} + +LRESULT CALLBACK PhpThemeWindowGroupBoxSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + WNDPROC oldWndProc; + + if (!(oldWndProc = PhGetWindowContext(WindowHandle, SHRT_MAX))) + return FALSE; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(WindowHandle, SHRT_MAX); + } + break; + case WM_ERASEBKGND: + return TRUE; + case WM_PAINT: + { + HDC hdc; + PAINTSTRUCT ps; + SIZE nameSize; + RECT clientRect; + RECT textRect; + ULONG returnLength; + WCHAR text[MAX_PATH]; + + if (!(hdc = BeginPaint(WindowHandle, &ps))) + break; + + GetClientRect(WindowHandle, &clientRect); + + if (!PhpGroupboxFontHandle) // HACK + { + NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) }; + + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + { + metrics.lfMessageFont.lfHeight = PhMultiplyDivideSigned(-11, PhGlobalDpi, 96); + metrics.lfMessageFont.lfWeight = FW_BOLD; + + PhpGroupboxFontHandle = CreateFontIndirect(&metrics.lfMessageFont); + } + } + + if (PhGetWindowTextToBuffer(WindowHandle, PH_GET_WINDOW_TEXT_INTERNAL, text, RTL_NUMBER_OF(text), &returnLength) == ERROR_SUCCESS) + { + HFONT oldFont; + + SetBkMode(hdc, TRANSPARENT); + + oldFont = SelectFont(hdc, PhpGroupboxFontHandle); + GetTextExtentPoint32( + hdc, + text, + returnLength, + &nameSize + ); + + //if (PhpThemeBorderEnable) + //switch (PhpThemeColorMode) + //{ + //case 0: // New colors + // SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + // SetDCBrushColor(hdc, RGB(0xff, 0xff, 0xff)); + // FrameRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + // break; + //case 1: // Old colors + // SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + // SetDCBrushColor(hdc, RGB(0, 0, 0)); // RGB(28, 28, 28)); // RGB(65, 65, 65)); + // FrameRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + // break; + //} + + textRect.left = 0; + textRect.top = 0; + textRect.right = clientRect.right; + textRect.bottom = nameSize.cy; + + SetTextColor(hdc, RGB(255, 69, 0)); + SetDCBrushColor(hdc, RGB(65, 65, 65)); + FillRect(hdc, &textRect, GetStockBrush(DC_BRUSH)); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, RGB(255, 69, 0)); + SetDCBrushColor(hdc, RGB(65, 65, 65)); + //SetTextColor(hdc, RGB(0x0, 0x0, 0x0)); + //SetDCBrushColor(hdc, PhpThemeWindowTextColor); + //FrameRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(hdc, RGB(255, 69, 0)); + SetDCBrushColor(hdc, RGB(65, 65, 65)); + //SetTextColor(hdc, PhpThemeWindowTextColor); + //SetDCBrushColor(hdc, PhpThemeWindowBackgroundColor); + //FrameRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + } + + textRect.left += 10; + DrawText( + hdc, + text, + returnLength, + &textRect, + DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE + ); + textRect.left -= 10; + + SelectFont(hdc, oldFont); + } + + EndPaint(WindowHandle, &ps); + } + return TRUE; + } + + return CallWindowProc(oldWndProc, WindowHandle, uMsg, wParam, lParam); +} + +LRESULT CALLBACK PhpThemeWindowEditSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + WNDPROC oldWndProc; + + if (!(oldWndProc = PhGetWindowContext(WindowHandle, SHRT_MAX))) + return FALSE; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(WindowHandle, SHRT_MAX); + } + break; + case WM_NCPAINT: + { + HDC hdc; + ULONG flags; + RECT windowRect; + HRGN updateRegion; + + updateRegion = (HRGN)wParam; + + if (updateRegion == (HRGN)1) // HRGN_FULL + updateRegion = NULL; + + flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000; + + if (updateRegion) + flags |= DCX_INTERSECTRGN | 0x40000; + + if (hdc = GetDCEx(WindowHandle, updateRegion, flags)) + { + GetWindowRect(WindowHandle, &windowRect); + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + + if (GetFocus() == WindowHandle) + { + SetDCBrushColor(hdc, GetSysColor(COLOR_HOTLIGHT)); + FrameRect(hdc, &windowRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetDCBrushColor(hdc, RGB(65, 65, 65)); + FrameRect(hdc, &windowRect, GetStockBrush(DC_BRUSH)); + } + + ReleaseDC(WindowHandle, hdc); + return 0; + } + } + break; + } + + return CallWindowProc(oldWndProc, WindowHandle, uMsg, wParam, lParam); +} + +LRESULT CALLBACK PhpThemeWindowTabControlWndSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_THEME_WINDOW_TAB_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(WindowHandle, SHRT_MAX))) + return FALSE; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + PhRemoveWindowContext(WindowHandle, SHRT_MAX); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + + PhFree(context); + } + break; + case WM_ERASEBKGND: + { + //ExcludeClipRect(ps.hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom); + } + return TRUE; + case WM_MOUSEMOVE: + { + POINT pt; + INT count; + INT i; + + GetCursorPos(&pt); + MapWindowPoints(NULL, WindowHandle, &pt, 1); + + count = TabCtrl_GetItemCount(WindowHandle); + + for (i = 0; i < count; i++) + { + RECT rect = { 0, 0, 0, 0 }; + TCITEM entry = + { + TCIF_STATE, + 0, + TCIS_HIGHLIGHTED + }; + + TabCtrl_GetItemRect(WindowHandle, i, &rect); + entry.dwState = PtInRect(&rect, pt) ? TCIS_HIGHLIGHTED : 0; + TabCtrl_SetItem(WindowHandle, i, &entry); + } + + if (!context->MouseActive) + { + TRACKMOUSEEVENT trackEvent = + { + sizeof(TRACKMOUSEEVENT), + TME_LEAVE, + WindowHandle, + 0 + }; + + TrackMouseEvent(&trackEvent); + context->MouseActive = TRUE; + } + + InvalidateRect(WindowHandle, NULL, FALSE); + } + break; + case WM_MOUSELEAVE: + { + INT count; + INT i; + + count = TabCtrl_GetItemCount(WindowHandle); + + for (i = 0; i < count; i++) + { + TCITEM entry = + { + TCIF_STATE, + 0, + TCIS_HIGHLIGHTED + }; + + TabCtrl_SetItem(WindowHandle, i, &entry); + } + + InvalidateRect(WindowHandle, NULL, FALSE); + context->MouseActive = FALSE; + } + break; + case WM_PAINT: + { + RECT itemRect; + RECT windowRect; + RECT clientRect; + PAINTSTRUCT ps; + + if (!BeginPaint(WindowHandle, &ps)) + break; + + GetWindowRect(WindowHandle, &windowRect); + GetClientRect(WindowHandle, &clientRect); + + TabCtrl_AdjustRect(WindowHandle, FALSE, &clientRect); // Make sure we don't paint in the client area. + ExcludeClipRect(ps.hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom); + + windowRect.right -= windowRect.left; + windowRect.bottom -= windowRect.top; + windowRect.left = 0; + windowRect.top = 0; + + clientRect.left = windowRect.left; + clientRect.top = windowRect.top; + clientRect.right = windowRect.right; + clientRect.bottom = windowRect.bottom; + + HDC hdc = CreateCompatibleDC(ps.hdc); + HDC hdcTab = CreateCompatibleDC(hdc); + HBITMAP hbm = CreateCompatibleBitmap(ps.hdc, clientRect.right, clientRect.bottom); + SelectBitmap(hdc, hbm); + + SetBkMode(hdc, TRANSPARENT); + SelectFont(hdc, PhpTabControlFontHandle); + + switch (PhpThemeColorMode) + { + case 0: // New colors + //SetTextColor(hdc, RGB(0x0, 0xff, 0x0)); + SetDCBrushColor(hdc, GetSysColor(COLOR_3DFACE));// PhpThemeWindowTextColor); + FillRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + //SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, RGB(65, 65, 65)); //WindowForegroundColor); // RGB(65, 65, 65)); + FillRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + } + + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(NULL, WindowHandle, &pt, 1); + + INT count = TabCtrl_GetItemCount(WindowHandle); + for (INT i = 0; i < count; i++) + { + TabCtrl_GetItemRect(WindowHandle, i, &itemRect); + + if (PtInRect(&itemRect, pt)) + { + OffsetRect(&itemRect, 2, 2); + + switch (PhpThemeColorMode) + { + case 0: // New colors + { + if (TabCtrl_GetCurSel(WindowHandle) == i) + { + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(128, 128, 128)); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, PhpThemeWindowBackgroundColor); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + } + } + break; + case 1: // Old colors + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, RGB(128, 128, 128)); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + break; + } + + //FrameRect(hdc, &itemRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + else + { + OffsetRect(&itemRect, 2, 2); + + switch (PhpThemeColorMode) + { + case 0: // New colors + { + if (TabCtrl_GetCurSel(WindowHandle) == i) + { + SetTextColor(hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(hdc, RGB(0xff, 0xff, 0xff)); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetTextColor(hdc, RGB(0, 0, 0)); + SetDCBrushColor(hdc, GetSysColor(COLOR_3DFACE));// PhpThemeWindowTextColor); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + } + } + break; + case 1: // Old colors + { + if (TabCtrl_GetCurSel(WindowHandle) == i) + { + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, PhpThemeWindowForegroundColor); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, PhpThemeWindowBackgroundColor); + FillRect(hdc, &itemRect, GetStockBrush(DC_BRUSH)); + } + } + break; + } + } + + { + TCITEM tabItem; + WCHAR tabHeaderText[MAX_PATH]; + + memset(tabHeaderText, 0, sizeof(tabHeaderText)); + memset(&tabItem, 0, sizeof(TCITEM)); + + tabItem.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_STATE; + tabItem.dwStateMask = TCIS_BUTTONPRESSED | TCIS_HIGHLIGHTED; + tabItem.cchTextMax = RTL_NUMBER_OF(tabHeaderText); + tabItem.pszText = tabHeaderText; + + if (TabCtrl_GetItem(WindowHandle, i, &tabItem)) + { + DrawText( + hdc, + tabItem.pszText, + (UINT)PhCountStringZ(tabItem.pszText), + &itemRect, + DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX + ); + } + } + } + + BitBlt(ps.hdc, 0, 0, clientRect.right, clientRect.bottom, hdc, 0, 0, SRCCOPY); + + DeleteDC(hdcTab); + DeleteDC(hdc); + DeleteBitmap(hbm); + EndPaint(WindowHandle, &ps); + } + goto DefaultWndProc; + } + + return CallWindowProc(oldWndProc, WindowHandle, uMsg, wParam, lParam); + +DefaultWndProc: + return DefWindowProc(WindowHandle, uMsg, wParam, lParam); +} + +LRESULT CALLBACK PhpThemeWindowHeaderSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_THEME_WINDOW_HEADER_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(WindowHandle, SHRT_MAX))) + return FALSE; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_NCDESTROY: + { + PhRemoveWindowContext(WindowHandle, SHRT_MAX); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + + PhFree(context); + } + break; + //case WM_ERASEBKGND: + // return 1; + case WM_MOUSEMOVE: + { + //POINT pt; + //RECT rcItem; + //TCITEM tcItem; + //tcItem.mask = TCIF_STATE; + //tcItem.dwStateMask = TCIS_HIGHLIGHTED; + //GetCursorPos(&pt); + //MapWindowPoints(NULL, WindowHandle, &pt, 1); + //int nCount = TabCtrl_GetItemCount(WindowHandle); + //for (int i = 0; i < nCount; i++) + //{ + // TabCtrl_GetItemRect(WindowHandle, i, &rcItem); + // tcItem.dwState = PtInRect(&rcItem, pt) ? TCIS_HIGHLIGHTED : 0; + // TabCtrl_SetItem(WindowHandle, i, &tcItem); + //} + + if (!context->MouseActive) + { + TRACKMOUSEEVENT trackEvent = + { + sizeof(TRACKMOUSEEVENT), + TME_LEAVE, + WindowHandle, + 0 + }; + + TrackMouseEvent(&trackEvent); + context->MouseActive = TRUE; + } + + InvalidateRect(WindowHandle, NULL, FALSE); + } + break; + case WM_CONTEXTMENU: + { + LRESULT result = CallWindowProc(oldWndProc, WindowHandle, uMsg, wParam, lParam); + + InvalidateRect(WindowHandle, NULL, TRUE); + + return result; + } + break; + case WM_MOUSELEAVE: + { + // TCITEM tcItem = { TCIF_STATE, 0, TCIS_HIGHLIGHTED }; + //// int nCount = TabCtrl_GetItemCount(WindowHandle); + // for (int i = 0; i < nCount; i++) + // { + // //TabCtrl_SetItem(WindowHandle, i, &tcItem); + // } + + InvalidateRect(WindowHandle, NULL, TRUE); + context->MouseActive = FALSE; + } + break; + case WM_PAINT: + { + //HTHEME hTheme = OpenThemeData(WindowHandle, L"HEADER"); + WCHAR szText[MAX_PATH]; + RECT clientRect; + HDITEM tcItem; + PAINTSTRUCT ps; + + //InvalidateRect(WindowHandle, NULL, FALSE); + BeginPaint(WindowHandle, &ps); + GetClientRect(WindowHandle, &clientRect); + + HDC hdc = CreateCompatibleDC(ps.hdc); + HDC hdcTab = CreateCompatibleDC(hdc); + HBITMAP hbm = CreateCompatibleBitmap(ps.hdc, clientRect.right, clientRect.bottom); + SelectBitmap(hdc, hbm); + + if (!PhpHeaderFontHandle) + { + NONCLIENTMETRICS metrics = { sizeof(metrics) }; + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + { + PhpHeaderFontHandle = CreateFontIndirect(&metrics.lfMessageFont); + } + } + SelectFont(hdc, PhpHeaderFontHandle); + + SetBkMode(hdc, TRANSPARENT); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(hdc, PhpThemeWindowTextColor); + FillRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, RGB(65, 65, 65)); //WindowForegroundColor); // RGB(65, 65, 65)); + FillRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + } + + INT nCount = Header_GetItemCount(WindowHandle); + + for (INT i = 0; i < nCount; i++) + { + RECT headerRect; + POINT pt; + + Header_GetItemRect(WindowHandle, i, &headerRect); + + GetCursorPos(&pt); + MapWindowPoints(NULL, WindowHandle, &pt, 1); + + if (PtInRect(&headerRect, pt)) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, PhpThemeWindowBackgroundColor); + FillRect(hdc, &headerRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, RGB(128, 128, 128)); + FillRect(hdc, &headerRect, GetStockBrush(DC_BRUSH)); + break; + } + + //FrameRect(hdc, &headerRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + else + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(hdc, PhpThemeWindowTextColor); + FillRect(hdc, &headerRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, PhpThemeWindowBackgroundColor); + FillRect(hdc, &headerRect, GetStockBrush(DC_BRUSH)); + break; + } + + //FrameRect(hdc, &headerRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + + ZeroMemory(&tcItem, sizeof(HDITEM)); + tcItem.mask = HDI_TEXT | HDI_IMAGE | HDI_STATE; + tcItem.pszText = szText; + tcItem.cchTextMax = MAX_PATH; + + Header_GetItem(WindowHandle, i, &tcItem); + + DrawText( + hdc, + tcItem.pszText, + -1, + &headerRect, + DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_HIDEPREFIX + ); + } + + BitBlt(ps.hdc, 0, 0, clientRect.right, clientRect.bottom, hdc, 0, 0, SRCCOPY); + + DeleteDC(hdcTab); + DeleteDC(hdc); + DeleteBitmap(hbm); + EndPaint(WindowHandle, &ps); + } + goto DefaultWndProc; + } + + return CallWindowProc(oldWndProc, WindowHandle, uMsg, wParam, lParam); + +DefaultWndProc: + return DefWindowProc(WindowHandle, uMsg, wParam, lParam); +} + +LRESULT CALLBACK PhpThemeWindowStatusbarWndSubclassProc( + _In_ HWND WindowHandle, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPHP_THEME_WINDOW_STATUSBAR_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(WindowHandle, SHRT_MAX))) + return FALSE; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_NCDESTROY: + { + PhRemoveWindowContext(WindowHandle, SHRT_MAX); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + + PhFree(context); + } + break; + case WM_ERASEBKGND: + return FALSE; + case WM_MOUSEMOVE: + { + if (!context->MouseActive) + { + TRACKMOUSEEVENT trackEvent = + { + sizeof(TRACKMOUSEEVENT), + TME_LEAVE, + WindowHandle, + 0 + }; + + TrackMouseEvent(&trackEvent); + context->MouseActive = TRUE; + } + + InvalidateRect(WindowHandle, NULL, FALSE); + } + break; + case WM_MOUSELEAVE: + { + InvalidateRect(WindowHandle, NULL, FALSE); + context->MouseActive = FALSE; + } + break; + case WM_PAINT: + { + RECT clientRect; + PAINTSTRUCT ps; + INT blockCoord[128]; + INT blockCount = (INT)SendMessage(WindowHandle, (UINT)SB_GETPARTS, (WPARAM)ARRAYSIZE(blockCoord), (WPARAM)blockCoord); + + // InvalidateRect(WindowHandle, NULL, FALSE); + BeginPaint(WindowHandle, &ps); + GetClientRect(WindowHandle, &clientRect); + + HDC hdc = CreateCompatibleDC(ps.hdc); + HBITMAP hbm = CreateCompatibleBitmap(ps.hdc, clientRect.right, clientRect.bottom); + SelectBitmap(hdc, hbm); + + SetBkMode(hdc, TRANSPARENT); + + if (!PhpStatusBarFontHandle) + { + NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) }; + + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) + { + PhpStatusBarFontHandle = CreateFontIndirect(&metrics.lfMessageFont); + } + } + + SelectFont(hdc, PhpStatusBarFontHandle); + + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(hdc, GetSysColor(COLOR_3DFACE)); // RGB(0xff, 0xff, 0xff)); + FillRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(65, 65, 65)); //RGB(28, 28, 28)); // RGB(65, 65, 65)); + FillRect(hdc, &clientRect, GetStockBrush(DC_BRUSH)); + break; + } + + for (INT i = 0; i < blockCount; i++) + { + RECT blockRect = { 0, 0, 0, 0 }; + WCHAR buffer[MAX_PATH] = L""; + + if (!SendMessage(WindowHandle, SB_GETRECT, (WPARAM)i, (WPARAM)&blockRect)) + continue; + if (!SendMessage(WindowHandle, SB_GETTEXT, (WPARAM)i, (LPARAM)buffer)) + continue; + + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(NULL, WindowHandle, &pt, 1); + + if (PtInRect(&blockRect, pt)) + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, PhpThemeWindowTextColor); + SetDCBrushColor(hdc, PhpThemeWindowBackgroundColor); + FillRect(hdc, &blockRect, GetStockBrush(DC_BRUSH)); + break; + case 1: // Old colors + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(128, 128, 128)); + FillRect(hdc, &blockRect, GetStockBrush(DC_BRUSH)); + //FrameRect(hdc, &blockRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + break; + } + } + else + { + switch (PhpThemeColorMode) + { + case 0: // New colors + SetTextColor(hdc, RGB(0x0, 0x0, 0x0)); + SetDCBrushColor(hdc, GetSysColor(COLOR_3DFACE)); // RGB(0xff, 0xff, 0xff)); + FillRect(hdc, &blockRect, GetStockBrush(DC_BRUSH)); + //FrameRect(hdc, &blockRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + break; + case 1: // Old colors + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(64, 64, 64)); + FillRect(hdc, &blockRect, GetStockBrush(DC_BRUSH)); + //FrameRect(hdc, &blockRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + break; + } + } + + DrawText( + hdc, + buffer, + -1, + &blockRect, + DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_HIDEPREFIX + ); + } + + { + RECT sizeGripRect; + + sizeGripRect.left = clientRect.right - GetSystemMetrics(SM_CXHSCROLL); + sizeGripRect.top = clientRect.bottom - GetSystemMetrics(SM_CYVSCROLL); + sizeGripRect.right = clientRect.right; + sizeGripRect.bottom = clientRect.bottom; + + if (context->StatusThemeData) + DrawThemeBackground(context->StatusThemeData, hdc, SP_GRIPPER, 0, &sizeGripRect, &sizeGripRect); + else + DrawFrameControl(hdc, &sizeGripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); + } + + BitBlt(ps.hdc, 0, 0, clientRect.right, clientRect.bottom, hdc, 0, 0, SRCCOPY); + + DeleteDC(hdc); + DeleteBitmap(hbm); + EndPaint(WindowHandle, &ps); + } + goto DefaultWndProc; + } + + return CallWindowProc(oldWndProc, WindowHandle, uMsg, wParam, lParam); + +DefaultWndProc: + return DefWindowProc(WindowHandle, uMsg, wParam, lParam); +} diff --git a/phlib/treenew.c b/phlib/treenew.c index 0132a52bb4bb..5153865b6ea8 100644 --- a/phlib/treenew.c +++ b/phlib/treenew.c @@ -1,6645 +1,6844 @@ -/* - * Process Hacker - - * tree new (tree list control) - * - * Copyright (C) 2011-2016 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * The tree new is a tree view with columns. Unlike the old tree list control, which was a wrapper - * around the list view control, this control was written from scratch. - * - * Current issues not included in any comments: - * * Adding, removing or changing columns does not cause invalidation. - * * It is not possible to change a column to make it fixed. The current fixed column must be - * removed and the new fixed column must then be added. - * * When there are no visible normal columns, the space usually occupied by the normal column - * headers is filled with a solid background color. We should catch this and paint the usual - * themed background there instead. - * * It is not possible to update any TN_STYLE_* flags after the control is created. - * - * Possible additions: - * * More flexible mouse input callbacks to allow custom controls inside columns. - * * Allow custom drawn columns to customize their behaviour when TN_FLAG_ITEM_DRAG_SELECT is set - * (e.g. disable drag selection over certain areas). - * * Virtual mode - */ - -#include -#include - -#include -#include -#include - -#include - -#include - -static PVOID ComCtl32Handle; -static LONG SmallIconWidth; -static LONG SmallIconHeight; - -BOOLEAN PhTreeNewInitialization( - VOID - ) -{ - WNDCLASSEX c = { sizeof(c) }; - - c.style = CS_DBLCLKS | CS_GLOBALCLASS; - c.lpfnWndProc = PhTnpWndProc; - c.cbClsExtra = 0; - c.cbWndExtra = sizeof(PVOID); - c.hInstance = PhLibImageBase; - c.hIcon = NULL; - c.hCursor = LoadCursor(NULL, IDC_ARROW); - c.hbrBackground = NULL; - c.lpszMenuName = NULL; - c.lpszClassName = PH_TREENEW_CLASSNAME; - c.hIconSm = NULL; - - if (!RegisterClassEx(&c)) - return FALSE; - - ComCtl32Handle = GetModuleHandle(L"comctl32.dll"); - SmallIconWidth = GetSystemMetrics(SM_CXSMICON); - SmallIconHeight = GetSystemMetrics(SM_CYSMICON); - - return TRUE; -} - -LRESULT CALLBACK PhTnpWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPH_TREENEW_CONTEXT context; - - context = (PPH_TREENEW_CONTEXT)GetWindowLongPtr(hwnd, 0); - - if (uMsg == WM_CREATE) - { - PhTnpCreateTreeNewContext(&context); - SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); - } - - if (!context) - return DefWindowProc(hwnd, uMsg, wParam, lParam); - - if (context->Tracking && (GetAsyncKeyState(VK_ESCAPE) & 0x1)) - { - PhTnpCancelTrack(context); - } - - // Note: if we have suspended restructuring, we *cannot* access any nodes, because all node - // pointers are now invalid. Below, we disable all input. - - switch (uMsg) - { - case WM_CREATE: - { - if (!PhTnpOnCreate(hwnd, context, (CREATESTRUCT *)lParam)) - return -1; - } - return 0; - case WM_NCDESTROY: - { - context->Callback(hwnd, TreeNewDestroying, NULL, NULL, context->CallbackContext); - PhTnpDestroyTreeNewContext(context); - SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); - } - return 0; - case WM_SIZE: - { - PhTnpOnSize(hwnd, context); - } - break; - case WM_ERASEBKGND: - return TRUE; - case WM_PAINT: - { - PhTnpOnPaint(hwnd, context); - } - return 0; - case WM_PRINTCLIENT: - { - if (!context->SuspendUpdateStructure) - PhTnpOnPrintClient(hwnd, context, (HDC)wParam, (ULONG)lParam); - } - return 0; - case WM_NCPAINT: - { - if (PhTnpOnNcPaint(hwnd, context, (HRGN)wParam)) - return 0; - } - break; - case WM_GETFONT: - return (LRESULT)context->Font; - case WM_SETFONT: - { - PhTnpOnSetFont(hwnd, context, (HFONT)wParam, LOWORD(lParam)); - } - break; - case WM_STYLECHANGED: - { - PhTnpOnStyleChanged(hwnd, context, (LONG)wParam, (STYLESTRUCT *)lParam); - } - break; - case WM_SETTINGCHANGE: - { - PhTnpOnSettingChange(hwnd, context); - } - break; - case WM_THEMECHANGED: - { - PhTnpOnThemeChanged(hwnd, context); - } - break; - case WM_GETDLGCODE: - return PhTnpOnGetDlgCode(hwnd, context, (ULONG)wParam, (PMSG)lParam); - case WM_SETFOCUS: - { - context->HasFocus = TRUE; - InvalidateRect(context->Handle, NULL, FALSE); - } - return 0; - case WM_KILLFOCUS: - { - context->HasFocus = FALSE; - InvalidateRect(context->Handle, NULL, FALSE); - } - return 0; - case WM_SETCURSOR: - { - if (PhTnpOnSetCursor(hwnd, context, (HWND)wParam)) - return TRUE; - } - break; - case WM_TIMER: - { - PhTnpOnTimer(hwnd, context, (ULONG)wParam); - } - return 0; - case WM_MOUSEMOVE: - { - if (!context->SuspendUpdateStructure) - PhTnpOnMouseMove(hwnd, context, (ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - else - context->SuspendUpdateMoveMouse = TRUE; - } - break; - case WM_MOUSELEAVE: - { - if (!context->SuspendUpdateStructure) - PhTnpOnMouseLeave(hwnd, context); - } - break; - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_MBUTTONDBLCLK: - { - if (!context->SuspendUpdateStructure) - PhTnpOnXxxButtonXxx(hwnd, context, uMsg, (ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - } - break; - case WM_CAPTURECHANGED: - { - PhTnpOnCaptureChanged(hwnd, context); - } - break; - case WM_KEYDOWN: - { - if (!context->SuspendUpdateStructure) - PhTnpOnKeyDown(hwnd, context, (ULONG)wParam, (ULONG)lParam); - } - break; - case WM_CHAR: - { - if (!context->SuspendUpdateStructure) - PhTnpOnChar(hwnd, context, (ULONG)wParam, (ULONG)lParam); - } - return 0; - case WM_MOUSEWHEEL: - { - PhTnpOnMouseWheel(hwnd, context, (SHORT)HIWORD(wParam), LOWORD(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - } - break; - case WM_MOUSEHWHEEL: - { - PhTnpOnMouseHWheel(hwnd, context, (SHORT)HIWORD(wParam), LOWORD(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - } - break; - case WM_CONTEXTMENU: - { - if (!context->SuspendUpdateStructure) - PhTnpOnContextMenu(hwnd, context, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - } - return 0; - case WM_VSCROLL: - { - PhTnpOnVScroll(hwnd, context, LOWORD(wParam), HIWORD(wParam)); - } - return 0; - case WM_HSCROLL: - { - PhTnpOnHScroll(hwnd, context, LOWORD(wParam), HIWORD(wParam)); - } - return 0; - case WM_NOTIFY: - { - LRESULT result; - - if (PhTnpOnNotify(hwnd, context, (NMHDR *)lParam, &result)) - return result; - } - break; - } - - if (uMsg >= TNM_FIRST && uMsg <= TNM_LAST) - { - return PhTnpOnUserMessage(hwnd, context, uMsg, wParam, lParam); - } - - switch (uMsg) - { - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - { - if (context->TooltipsHandle) - { - MSG message; - - message.hwnd = hwnd; - message.message = uMsg; - message.wParam = wParam; - message.lParam = lParam; - SendMessage(context->TooltipsHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); - } - } - break; - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -BOOLEAN NTAPI PhTnpNullCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - return FALSE; -} - -VOID PhTnpCreateTreeNewContext( - _Out_ PPH_TREENEW_CONTEXT *Context - ) -{ - PPH_TREENEW_CONTEXT context; - - context = PhAllocate(sizeof(PH_TREENEW_CONTEXT)); - memset(context, 0, sizeof(PH_TREENEW_CONTEXT)); - - context->FixedWidthMinimum = 20; - context->RowHeight = 1; // must never be 0 - context->HotNodeIndex = -1; - context->Callback = PhTnpNullCallback; - context->FlatList = PhCreateList(64); - context->TooltipIndex = -1; - context->TooltipId = -1; - context->TooltipColumnId = -1; - context->EnableRedraw = 1; - - *Context = context; -} - -VOID PhTnpDestroyTreeNewContext( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - ULONG i; - - if (Context->Columns) - { - for (i = 0; i < Context->NextId; i++) - { - if (Context->Columns[i]) - PhFree(Context->Columns[i]); - } - - PhFree(Context->Columns); - } - - if (Context->ColumnsByDisplay) - PhFree(Context->ColumnsByDisplay); - - PhDereferenceObject(Context->FlatList); - - if (Context->FontOwned) - DeleteObject(Context->Font); - - if (Context->ThemeData) - CloseThemeData(Context->ThemeData); - - if (Context->SearchString) - PhFree(Context->SearchString); - - if (Context->TooltipText) - PhDereferenceObject(Context->TooltipText); - - if (Context->BufferedContext) - PhTnpDestroyBufferedContext(Context); - - if (Context->SuspendUpdateRegion) - DeleteObject(Context->SuspendUpdateRegion); - - PhFree(Context); -} - -BOOLEAN PhTnpOnCreate( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ CREATESTRUCT *CreateStruct - ) -{ - ULONG headerStyle; - - Context->Handle = hwnd; - Context->InstanceHandle = CreateStruct->hInstance; - Context->Style = CreateStruct->style; - Context->ExtendedStyle = CreateStruct->dwExStyle; - - if (Context->Style & TN_STYLE_DOUBLE_BUFFERED) - Context->DoubleBuffered = TRUE; - if ((Context->Style & TN_STYLE_ANIMATE_DIVIDER) && Context->DoubleBuffered) - Context->AnimateDivider = TRUE; - - headerStyle = HDS_HORZ | HDS_FULLDRAG; - - if (!(Context->Style & TN_STYLE_NO_COLUMN_SORT)) - headerStyle |= HDS_BUTTONS; - if (!(Context->Style & TN_STYLE_NO_COLUMN_HEADER)) - headerStyle |= WS_VISIBLE; - - if (!(Context->FixedHeaderHandle = CreateWindow( - WC_HEADER, - NULL, - WS_CHILD | WS_CLIPSIBLINGS | headerStyle, - 0, - 0, - 0, - 0, - hwnd, - NULL, - CreateStruct->hInstance, - NULL - ))) - { - return FALSE; - } - - if (!(Context->Style & TN_STYLE_NO_COLUMN_REORDER)) - headerStyle |= HDS_DRAGDROP; - - if (!(Context->HeaderHandle = CreateWindow( - WC_HEADER, - NULL, - WS_CHILD | WS_CLIPSIBLINGS | headerStyle, - 0, - 0, - 0, - 0, - hwnd, - NULL, - CreateStruct->hInstance, - NULL - ))) - { - return FALSE; - } - - if (!(Context->VScrollHandle = CreateWindow( - L"SCROLLBAR", - NULL, - WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBS_VERT, - 0, - 0, - 0, - 0, - hwnd, - NULL, - CreateStruct->hInstance, - NULL - ))) - { - return FALSE; - } - - if (!(Context->HScrollHandle = CreateWindow( - L"SCROLLBAR", - NULL, - WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBS_HORZ, - 0, - 0, - 0, - 0, - hwnd, - NULL, - CreateStruct->hInstance, - NULL - ))) - { - return FALSE; - } - - if (!(Context->FillerBoxHandle = CreateWindow( - L"STATIC", - NULL, - WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, - 0, - 0, - 0, - 0, - hwnd, - NULL, - CreateStruct->hInstance, - NULL - ))) - { - return FALSE; - } - - PhTnpSetFont(Context, NULL, FALSE); // use default font - PhTnpUpdateSystemMetrics(Context); - PhTnpInitializeTooltips(Context); - - return TRUE; -} - -VOID PhTnpOnSize( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - GetClientRect(hwnd, &Context->ClientRect); - - if (Context->BufferedContext && ( - Context->BufferedContextRect.right < Context->ClientRect.right || - Context->BufferedContextRect.bottom < Context->ClientRect.bottom)) - { - // Invalidate the buffered context because the client size has increased. - PhTnpDestroyBufferedContext(Context); - } - - PhTnpLayout(Context); - - if (Context->TooltipsHandle) - { - TOOLINFO toolInfo; - - memset(&toolInfo, 0, sizeof(TOOLINFO)); - toolInfo.cbSize = sizeof(TOOLINFO); - toolInfo.hwnd = hwnd; - toolInfo.uId = TNP_TOOLTIPS_ITEM; - toolInfo.rect = Context->ClientRect; - SendMessage(Context->TooltipsHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); - } -} - -VOID PhTnpOnSetFont( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ HFONT Font, - _In_ LOGICAL Redraw - ) -{ - PhTnpSetFont(Context, Font, !!Redraw); - PhTnpLayout(Context); -} - -VOID PhTnpOnStyleChanged( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Type, - _In_ STYLESTRUCT *StyleStruct - ) -{ - if (Type == GWL_EXSTYLE) - Context->ExtendedStyle = StyleStruct->styleNew; -} - -VOID PhTnpOnSettingChange( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - PhTnpUpdateSystemMetrics(Context); - PhTnpUpdateTextMetrics(Context); - PhTnpLayout(Context); -} - -VOID PhTnpOnThemeChanged( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - PhTnpUpdateThemeData(Context); -} - -ULONG PhTnpOnGetDlgCode( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey, - _In_opt_ PMSG Message - ) -{ - ULONG code; - - if (Context->Callback(hwnd, TreeNewGetDialogCode, UlongToPtr(VirtualKey), &code, Context->CallbackContext)) - { - return code; - } - - return DLGC_WANTARROWS | DLGC_WANTCHARS; -} - -VOID PhTnpOnPaint( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - RECT updateRect; - HDC hdc; - PAINTSTRUCT paintStruct; - - if (GetUpdateRect(hwnd, &updateRect, FALSE) && (updateRect.left | updateRect.right | updateRect.top | updateRect.bottom)) - { - if (Context->EnableRedraw <= 0) - { - HRGN updateRegion; - - updateRegion = CreateRectRgn(0, 0, 0, 0); - GetUpdateRgn(hwnd, updateRegion, FALSE); - - if (!Context->SuspendUpdateRegion) - { - Context->SuspendUpdateRegion = updateRegion; - } - else - { - CombineRgn(Context->SuspendUpdateRegion, Context->SuspendUpdateRegion, updateRegion, RGN_OR); - DeleteObject(updateRegion); - } - - // Pretend we painted something; this ensures the update region is validated properly. - if (BeginPaint(hwnd, &paintStruct)) - EndPaint(hwnd, &paintStruct); - - return; - } - - if (Context->DoubleBuffered) - { - if (!Context->BufferedContext) - { - PhTnpCreateBufferedContext(Context); - } - } - - if (hdc = BeginPaint(hwnd, &paintStruct)) - { - updateRect = paintStruct.rcPaint; - - if (Context->BufferedContext) - { - PhTnpPaint(hwnd, Context, Context->BufferedContext, &updateRect); - BitBlt( - hdc, - updateRect.left, - updateRect.top, - updateRect.right - updateRect.left, - updateRect.bottom - updateRect.top, - Context->BufferedContext, - updateRect.left, - updateRect.top, - SRCCOPY - ); - } - else - { - PhTnpPaint(hwnd, Context, hdc, &updateRect); - } - - EndPaint(hwnd, &paintStruct); - } - } -} - -VOID PhTnpOnPrintClient( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ ULONG Flags - ) -{ - PhTnpPaint(hwnd, Context, hdc, &Context->ClientRect); -} - -BOOLEAN PhTnpOnNcPaint( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ HRGN UpdateRegion - ) -{ - PhTnpInitializeThemeData(Context); - - // Themed border - if ((Context->ExtendedStyle & WS_EX_CLIENTEDGE) && Context->ThemeData) - { - HDC hdc; - ULONG flags; - - if (UpdateRegion == HRGN_FULL) - UpdateRegion = NULL; - - // Note the use of undocumented flags below. GetDCEx doesn't work without these. - - flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000; - - if (UpdateRegion) - flags |= DCX_INTERSECTRGN | 0x40000; - - if (hdc = GetDCEx(hwnd, UpdateRegion, flags)) - { - PhTnpDrawThemedBorder(Context, hdc); - ReleaseDC(hwnd, hdc); - return TRUE; - } - } - - return FALSE; -} - -BOOLEAN PhTnpOnSetCursor( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HWND CursorWindowHandle - ) -{ - POINT point; - - PhTnpGetMessagePos(hwnd, &point); - - if (TNP_HIT_TEST_FIXED_DIVIDER(point.x, Context)) - { - if (!Context->DividerCursor) - Context->DividerCursor = LoadCursor(ComCtl32Handle, MAKEINTRESOURCE(106)); // HACK (the divider icon resource has been 106 for quite a while...) - - SetCursor(Context->DividerCursor); - return TRUE; - } - - if (Context->Cursor) - { - SetCursor(Context->Cursor); - return TRUE; - } - - return FALSE; -} - -VOID PhTnpOnTimer( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id - ) -{ - if (Id == TNP_TIMER_ANIMATE_DIVIDER) - { - RECT dividerRect; - - dividerRect.left = Context->FixedWidth; - dividerRect.top = Context->HeaderHeight; - dividerRect.right = Context->FixedWidth + 1; - dividerRect.bottom = Context->ClientRect.bottom; - - if (Context->AnimateDividerFadingIn) - { - Context->DividerHot += TNP_ANIMATE_DIVIDER_INCREMENT; - - if (Context->DividerHot >= 100) - { - Context->DividerHot = 100; - Context->AnimateDividerFadingIn = FALSE; - KillTimer(hwnd, TNP_TIMER_ANIMATE_DIVIDER); - } - - InvalidateRect(hwnd, ÷rRect, FALSE); - } - else if (Context->AnimateDividerFadingOut) - { - if (Context->DividerHot <= TNP_ANIMATE_DIVIDER_DECREMENT) - { - Context->DividerHot = 0; - Context->AnimateDividerFadingOut = FALSE; - KillTimer(hwnd, TNP_TIMER_ANIMATE_DIVIDER); - } - else - { - Context->DividerHot -= TNP_ANIMATE_DIVIDER_DECREMENT; - } - - InvalidateRect(hwnd, ÷rRect, FALSE); - } - } -} - -VOID PhTnpOnMouseMove( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ) -{ - TRACKMOUSEEVENT trackMouseEvent; - - trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); - trackMouseEvent.dwFlags = TME_LEAVE; - trackMouseEvent.hwndTrack = hwnd; - trackMouseEvent.dwHoverTime = 0; - TrackMouseEvent(&trackMouseEvent); - - if (Context->Tracking) - { - ULONG newFixedWidth; - - newFixedWidth = Context->TrackOldFixedWidth + (CursorX - Context->TrackStartX); - PhTnpSetFixedWidth(Context, newFixedWidth); - } - - PhTnpProcessMoveMouse(Context, CursorX, CursorY); -} - -VOID PhTnpOnMouseLeave( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - RECT rect; - - if (Context->HotNodeIndex != -1 && Context->ThemeData) - { - // Update the old hot node because it may have a different non-hot background and plus minus part. - if (PhTnpGetRowRects(Context, Context->HotNodeIndex, Context->HotNodeIndex, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - - Context->HotNodeIndex = -1; - - if (Context->AnimateDivider && Context->FixedDividerVisible) - { - if ((Context->DividerHot != 0 || Context->AnimateDividerFadingIn) && !Context->AnimateDividerFadingOut) - { - // Fade out the divider. - Context->AnimateDividerFadingOut = TRUE; - Context->AnimateDividerFadingIn = FALSE; - SetTimer(Context->Handle, TNP_TIMER_ANIMATE_DIVIDER, TNP_ANIMATE_DIVIDER_INTERVAL, NULL); - } - } -} - -VOID PhTnpOnXxxButtonXxx( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Message, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ) -{ - BOOLEAN startingTracking; - PH_TREENEW_HIT_TEST hitTest; - LOGICAL controlKey; - LOGICAL shiftKey; - RECT rect; - ULONG changedStart; - ULONG changedEnd; - PH_TREENEW_MESSAGE clickMessage; - - // Focus - - if (Message == WM_LBUTTONDOWN || Message == WM_RBUTTONDOWN) - SetFocus(hwnd); - - // Divider tracking - - startingTracking = FALSE; - - switch (Message) - { - case WM_LBUTTONDOWN: - { - if (TNP_HIT_TEST_FIXED_DIVIDER(CursorX, Context)) - { - startingTracking = TRUE; - Context->Tracking = TRUE; - Context->TrackStartX = CursorX; - Context->TrackOldFixedWidth = Context->FixedWidth; - SetCapture(hwnd); - - SetTimer(hwnd, TNP_TIMER_NULL, 100, NULL); // make sure we get messages once in a while so we can detect the escape key - GetAsyncKeyState(VK_ESCAPE); - } - } - break; - case WM_LBUTTONUP: - { - if (Context->Tracking) - { - ReleaseCapture(); - } - } - break; - case WM_RBUTTONDOWN: - { - if (Context->Tracking) - { - PhTnpCancelTrack(Context); - } - } - break; - } - - if (!startingTracking && Context->Tracking) // still OK to process further if the user is only starting to drag the divider - return; - - hitTest.Point.x = CursorX; - hitTest.Point.y = CursorY; - hitTest.InFlags = TN_TEST_COLUMN | TN_TEST_SUBITEM; - PhTnpHitTest(Context, &hitTest); - - controlKey = VirtualKeys & MK_CONTROL; - shiftKey = VirtualKeys & MK_SHIFT; - - // Plus minus glyph - - if ((hitTest.Flags & TN_HIT_ITEM_PLUSMINUS) && Message == WM_LBUTTONDOWN) - { - PhTnpSetExpandedNode(Context, hitTest.Node, !hitTest.Node->Expanded); - } - - // Selection - - if (!(hitTest.Flags & TN_HIT_ITEM_PLUSMINUS) && (Message == WM_LBUTTONDOWN || Message == WM_RBUTTONDOWN)) - { - LOGICAL allowDragSelect; - PH_TREENEW_CELL_PARTS parts; - - PhTnpPopTooltip(Context); - allowDragSelect = TRUE; - - if (hitTest.Flags & TN_HIT_ITEM) - { - allowDragSelect = FALSE; - Context->FocusNode = hitTest.Node; - - if (Context->ExtendedFlags & TN_FLAG_ITEM_DRAG_SELECT) - { - // To allow drag selection to begin even if the cursor is on an item, we check if - // the cursor is on the item icon or text. Exceptions are: - // * When the item is already selected - // * When user is beginning to drag the divider - - if (!hitTest.Node->Selected && !startingTracking) - { - if (PhTnpGetCellParts(Context, hitTest.Node->Index, hitTest.Column, TN_MEASURE_TEXT, &parts)) - { - allowDragSelect = TRUE; - - if ((parts.Flags & TN_PART_ICON) && CursorX >= parts.IconRect.left && CursorX < parts.IconRect.right) - allowDragSelect = FALSE; - - if ((parts.Flags & TN_PART_CONTENT) && (parts.Flags & TN_PART_TEXT)) - { - if (CursorX >= parts.TextRect.left && CursorX < parts.TextRect.right) - allowDragSelect = FALSE; - } - } - } - } - - PhTnpProcessSelectNode(Context, hitTest.Node, controlKey, shiftKey, Message == WM_RBUTTONDOWN); - } - - if (allowDragSelect) - { - BOOLEAN dragSelect; - ULONG indexToSelect; - BOOLEAN selectionProcessed; - BOOLEAN showContextMenu; - - dragSelect = FALSE; - indexToSelect = -1; - selectionProcessed = FALSE; - showContextMenu = FALSE; - - if (!(hitTest.Flags & (TN_HIT_LEFT | TN_HIT_RIGHT | TN_HIT_ABOVE | TN_HIT_BELOW)) && !startingTracking) // don't interfere with divider - { - BOOLEAN result; - ULONG saveIndex; - ULONG saveId; - ULONG cancelledByMessage; - - // Check for drag selection. PhTnpDetectDrag has its own message loop, so we need to - // clear our pointers before we continue or we will have some access violations when - // items get deleted. - - if (hitTest.Node) - saveIndex = hitTest.Node->Index; - else - saveIndex = -1; - - if (hitTest.Column) - saveId = hitTest.Column->Id; - else - saveId = -1; - - result = PhTnpDetectDrag(Context, CursorX, CursorY, TRUE, &cancelledByMessage); - - // Restore the pointers. - - if (saveIndex == -1) - hitTest.Node = NULL; - else if (saveIndex < Context->FlatList->Count) - hitTest.Node = Context->FlatList->Items[saveIndex]; - else - return; - - if (saveId != -1 && !(hitTest.Column = PhTnpLookupColumnById(Context, saveId))) - return; - - if (result) - { - dragSelect = TRUE; - - if ((hitTest.Flags & TN_HIT_ITEM) && (Context->ExtendedFlags & TN_FLAG_ITEM_DRAG_SELECT)) - { - // Include the current node before starting the drag selection, otherwise - // the user will never be able to select the current node. - indexToSelect = hitTest.Node->Index; - } - } - else - { - if ((Message == WM_LBUTTONDOWN && cancelledByMessage == WM_LBUTTONUP) || - (Message == WM_RBUTTONDOWN && cancelledByMessage == WM_RBUTTONUP)) - { - POINT point; - - if ((hitTest.Flags & TN_HIT_ITEM) && (Context->ExtendedFlags & TN_FLAG_ITEM_DRAG_SELECT)) - { - // The user isn't performing a drag selection, so prevent deselection. - selectionProcessed = TRUE; - } - - // The button up message gets consumed by PhTnpDetectDrag, so send the mouse - // event here. - // Check if the cursor stayed in the same place. - - PhTnpGetMessagePos(Context->Handle, &point); - - if (point.x == CursorX && point.y == CursorY) - { - PhTnpSendMouseEvent( - Context, - Message == WM_LBUTTONDOWN ? TreeNewLeftClick : TreeNewRightClick, - CursorX, - CursorY, - hitTest.Node, - hitTest.Column, - VirtualKeys - ); - } - - if (Message == WM_RBUTTONDOWN) - showContextMenu = TRUE; - } - } - } - - if (!selectionProcessed && !controlKey && !shiftKey) - { - // Nothing: deselect everything. - - PhTnpSelectRange(Context, indexToSelect, indexToSelect, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(hwnd, &rect, FALSE); - } - } - - if (dragSelect) - { - PhTnpDragSelect(Context, CursorX, CursorY); - } - - if (showContextMenu) - { - SendMessage(Context->Handle, WM_CONTEXTMENU, (WPARAM)Context->Handle, GetMessagePos()); - } - - return; - } - } - - // Click, double-click - // Note: If TN_FLAG_ITEM_DRAG_SELECT is enabled, the code below that processes WM_xBUTTONDOWN - // and WM_xBUTTONUP messages only takes effect when the user clicks directly on an item's icon - // or text. - - clickMessage = -1; - - if (Message == WM_LBUTTONDOWN || Message == WM_RBUTTONDOWN) - { - if (Context->MouseDownLast != 0 && Context->MouseDownLast != Message) - { - // User pressed one button and pressed the other without letting go of the first one. - // This counts as a click. - - if (Context->MouseDownLast == WM_LBUTTONDOWN) - clickMessage = TreeNewLeftClick; - else - clickMessage = TreeNewRightClick; - } - - Context->MouseDownLast = Message; - Context->MouseDownLocation.x = CursorX; - Context->MouseDownLocation.y = CursorY; - } - else if (Message == WM_LBUTTONUP || Message == WM_RBUTTONUP) - { - if (Context->MouseDownLast != 0 && - Context->MouseDownLocation.x == CursorX && Context->MouseDownLocation.y == CursorY) - { - if (Context->MouseDownLast == WM_LBUTTONDOWN) - clickMessage = TreeNewLeftClick; - else - clickMessage = TreeNewRightClick; - } - - Context->MouseDownLast = 0; - } - else if (Message == WM_LBUTTONDBLCLK) - { - clickMessage = TreeNewLeftDoubleClick; - } - else if (Message == WM_RBUTTONDBLCLK) - { - clickMessage = TreeNewRightDoubleClick; - } - - if (!(hitTest.Flags & TN_HIT_ITEM_PLUSMINUS) && clickMessage != -1) - { - PhTnpSendMouseEvent(Context, clickMessage, CursorX, CursorY, hitTest.Node, hitTest.Column, VirtualKeys); - } -} - -VOID PhTnpOnCaptureChanged( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - Context->Tracking = FALSE; - KillTimer(hwnd, TNP_TIMER_NULL); -} - -VOID PhTnpOnKeyDown( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey, - _In_ ULONG Data - ) -{ - PH_TREENEW_KEY_EVENT keyEvent; - - keyEvent.Handled = FALSE; - keyEvent.VirtualKey = VirtualKey; - keyEvent.Data = Data; - Context->Callback(Context->Handle, TreeNewKeyDown, &keyEvent, NULL, Context->CallbackContext); - - if (keyEvent.Handled) - return; - - if (PhTnpProcessFocusKey(Context, VirtualKey)) - return; - if (PhTnpProcessNodeKey(Context, VirtualKey)) - return; -} - -VOID PhTnpOnChar( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Character, - _In_ ULONG Data - ) -{ - // Make sure the character is printable. - if (Character >= ' ' && Character <= '~') - { - PhTnpProcessSearchKey(Context, Character); - } -} - -VOID PhTnpOnMouseWheel( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ) -{ - // The normal mouse wheel can affect both the vertical scrollbar and the horizontal scrollbar, - // but the vertical scrollbar takes precedence. - if (Context->VScrollVisible) - { - PhTnpProcessMouseVWheel(Context, -Distance); - } - else if (Context->HScrollVisible) - { - PhTnpProcessMouseHWheel(Context, -Distance); - } -} - -VOID PhTnpOnMouseHWheel( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance, - _In_ ULONG VirtualKeys, - _In_ LONG CursorX, - _In_ LONG CursorY - ) -{ - PhTnpProcessMouseHWheel(Context, Distance); -} - -VOID PhTnpOnContextMenu( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorScreenX, - _In_ LONG CursorScreenY - ) -{ - POINT clientPoint; - BOOLEAN keyboardInvoked; - PH_TREENEW_HIT_TEST hitTest; - PH_TREENEW_CONTEXT_MENU contextMenu; - - if (CursorScreenX == -1 && CursorScreenY == -1) - { - ULONG i; - BOOLEAN found; - RECT windowRect; - RECT rect; - - keyboardInvoked = TRUE; - - // Context menu was invoked via keyboard. Display the context menu at the selected item. - - found = FALSE; - - for (i = 0; i < Context->FlatList->Count; i++) - { - if (((PPH_TREENEW_NODE)Context->FlatList->Items[i])->Selected) - { - found = TRUE; - break; - } - } - - if (found && PhTnpGetRowRects(Context, i, i, FALSE, &rect) && - rect.top >= Context->ClientRect.top && rect.top < Context->ClientRect.bottom) - { - clientPoint.x = rect.left + SmallIconWidth / 2; - clientPoint.y = rect.top + Context->RowHeight / 2; - } - else - { - clientPoint.x = 0; - clientPoint.y = 0; - } - - GetWindowRect(hwnd, &windowRect); - CursorScreenX = windowRect.left + clientPoint.x; - CursorScreenY = windowRect.top + clientPoint.y; - } - else - { - keyboardInvoked = FALSE; - - clientPoint.x = CursorScreenX; - clientPoint.y = CursorScreenY; - ScreenToClient(hwnd, &clientPoint); - - if (clientPoint.y < Context->HeaderHeight) - { - // Already handled by TreeNewHeaderRightClick. - return; - } - } - - hitTest.Point = clientPoint; - hitTest.InFlags = TN_TEST_COLUMN; - PhTnpHitTest(Context, &hitTest); - - contextMenu.Location.x = CursorScreenX; - contextMenu.Location.y = CursorScreenY; - contextMenu.ClientLocation = clientPoint; - contextMenu.Node = hitTest.Node; - contextMenu.Column = hitTest.Column; - contextMenu.KeyboardInvoked = keyboardInvoked; - Context->Callback(hwnd, TreeNewContextMenu, &contextMenu, NULL, Context->CallbackContext); -} - -VOID PhTnpOnVScroll( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Request, - _In_ USHORT Position - ) -{ - SCROLLINFO scrollInfo; - LONG oldPosition; - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_ALL; - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - switch (Request) - { - case SB_LINEUP: - scrollInfo.nPos--; - break; - case SB_LINEDOWN: - scrollInfo.nPos++; - break; - case SB_PAGEUP: - scrollInfo.nPos -= scrollInfo.nPage; - break; - case SB_PAGEDOWN: - scrollInfo.nPos += scrollInfo.nPage; - break; - case SB_THUMBPOSITION: - // Touch scrolling seems to give us Position but not nTrackPos. The problem is that Position - // is a 16-bit value, so don't use it if we have too many rows. - if (Context->FlatList->Count <= 0xffff) - scrollInfo.nPos = Position; - break; - case SB_THUMBTRACK: - scrollInfo.nPos = scrollInfo.nTrackPos; - break; - case SB_TOP: - scrollInfo.nPos = 0; - break; - case SB_BOTTOM: - scrollInfo.nPos = MAXINT; - break; - } - - scrollInfo.fMask = SIF_POS; - SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - - if (scrollInfo.nPos != oldPosition) - { - Context->VScrollPosition = scrollInfo.nPos; - PhTnpProcessScroll(Context, scrollInfo.nPos - oldPosition, 0); - } -} - -VOID PhTnpOnHScroll( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Request, - _In_ USHORT Position - ) -{ - SCROLLINFO scrollInfo; - LONG oldPosition; - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_ALL; - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - switch (Request) - { - case SB_LINELEFT: - scrollInfo.nPos -= Context->TextMetrics.tmAveCharWidth; - break; - case SB_LINERIGHT: - scrollInfo.nPos += Context->TextMetrics.tmAveCharWidth; - break; - case SB_PAGELEFT: - scrollInfo.nPos -= scrollInfo.nPage; - break; - case SB_PAGERIGHT: - scrollInfo.nPos += scrollInfo.nPage; - break; - case SB_THUMBPOSITION: - // Touch scrolling seems to give us Position but not nTrackPos. The problem is that Position - // is a 16-bit value, so don't use it if we have too many rows. - if (Context->FlatList->Count <= 0xffff) - scrollInfo.nPos = Position; - break; - case SB_THUMBTRACK: - scrollInfo.nPos = scrollInfo.nTrackPos; - break; - case SB_LEFT: - scrollInfo.nPos = 0; - break; - case SB_RIGHT: - scrollInfo.nPos = MAXINT; - break; - } - - scrollInfo.fMask = SIF_POS; - SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - - if (scrollInfo.nPos != oldPosition) - { - Context->HScrollPosition = scrollInfo.nPos; - PhTnpProcessScroll(Context, 0, scrollInfo.nPos - oldPosition); - } -} - -BOOLEAN PhTnpOnNotify( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ NMHDR *Header, - _Out_ LRESULT *Result - ) -{ - switch (Header->code) - { - case HDN_ITEMCHANGING: - case HDN_ITEMCHANGED: - { - NMHEADER *nmHeader = (NMHEADER *)Header; - - if (Header->code == HDN_ITEMCHANGING && Header->hwndFrom == Context->FixedHeaderHandle) - { - if (nmHeader->pitem->mask & HDI_WIDTH) - { - if (Context->FixedColumnVisible) - { - Context->FixedWidth = nmHeader->pitem->cxy - 1; - - if (Context->FixedWidth < Context->FixedWidthMinimum) - Context->FixedWidth = Context->FixedWidthMinimum; - - Context->NormalLeft = Context->FixedWidth + 1; - nmHeader->pitem->cxy = Context->FixedWidth + 1; - } - else - { - Context->FixedWidth = 0; - Context->NormalLeft = 0; - } - } - } - - if (Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) - { - if (nmHeader->pitem->mask & HDI_WIDTH) - { - // A column has been resized. Update our stored information. - PhTnpUpdateColumnHeaders(Context); - PhTnpUpdateColumnMaps(Context); - - if (Header->code == HDN_ITEMCHANGING) - { - HDITEM item; - - item.mask = HDI_WIDTH | HDI_LPARAM; - - if (Header_GetItem(Header->hwndFrom, nmHeader->iItem, &item)) - { - Context->ResizingColumn = (PPH_TREENEW_COLUMN)item.lParam; - Context->OldColumnWidth = item.cxy; - } - else - { - Context->ResizingColumn = NULL; - Context->OldColumnWidth = -1; - } - } - else if (Header->code == HDN_ITEMCHANGED) - { - if (Context->ResizingColumn) - { - LONG delta; - - delta = nmHeader->pitem->cxy - Context->OldColumnWidth; - - if (delta != 0) - { - PhTnpProcessResizeColumn(Context, Context->ResizingColumn, delta); - Context->Callback(Context->Handle, TreeNewColumnResized, Context->ResizingColumn, NULL, Context->CallbackContext); - } - - Context->ResizingColumn = NULL; - - // Redraw the entire window if we are displaying empty text. - if (Context->FlatList->Count == 0 && Context->EmptyText.Length != 0) - InvalidateRect(Context->Handle, NULL, FALSE); - } - else - { - // An error occurred during HDN_ITEMCHANGED, so redraw the entire window. - InvalidateRect(Context->Handle, NULL, FALSE); - } - } - } - } - } - break; - case HDN_ITEMCLICK: - { - if ((Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) && - !(Context->Style & TN_STYLE_NO_COLUMN_SORT)) - { - NMHEADER *nmHeader = (NMHEADER *)Header; - HDITEM item; - PPH_TREENEW_COLUMN column; - - // A column has been clicked, so update the sort state. - - item.mask = HDI_LPARAM; - - if (Header_GetItem(Header->hwndFrom, nmHeader->iItem, &item)) - { - column = (PPH_TREENEW_COLUMN)item.lParam; - PhTnpProcessSortColumn(Context, column); - } - } - } - break; - case HDN_ENDDRAG: - case NM_RELEASEDCAPTURE: - { - if (Header->hwndFrom == Context->HeaderHandle) - { - // Columns have been re-ordered, so refresh our information. - // Note: The fixed column cannot be re-ordered. - PhTnpUpdateColumnHeaders(Context); - PhTnpUpdateColumnMaps(Context); - Context->Callback(Context->Handle, TreeNewColumnReordered, NULL, NULL, Context->CallbackContext); - InvalidateRect(Context->Handle, NULL, FALSE); - } - } - break; - case HDN_DIVIDERDBLCLICK: - { - if (Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) - { - NMHEADER *nmHeader = (NMHEADER *)Header; - HDITEM item; - - if (Context->SuspendUpdateStructure) - break; - - item.mask = HDI_LPARAM; - - if (Header_GetItem(Header->hwndFrom, nmHeader->iItem, &item)) - { - PhTnpAutoSizeColumnHeader( - Context, - Header->hwndFrom, - (PPH_TREENEW_COLUMN)item.lParam, - 0 - ); - } - } - } - break; - case NM_RCLICK: - { - if (Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) - { - PH_TREENEW_HEADER_MOUSE_EVENT mouseEvent; - ULONG position; - - position = GetMessagePos(); - mouseEvent.ScreenLocation.x = GET_X_LPARAM(position); - mouseEvent.ScreenLocation.y = GET_Y_LPARAM(position); - - mouseEvent.Location = mouseEvent.ScreenLocation; - ScreenToClient(hwnd, &mouseEvent.Location); - mouseEvent.HeaderLocation = mouseEvent.ScreenLocation; - ScreenToClient(Header->hwndFrom, &mouseEvent.HeaderLocation); - mouseEvent.Column = PhTnpHitTestHeader(Context, Header->hwndFrom == Context->FixedHeaderHandle, &mouseEvent.HeaderLocation, NULL); - Context->Callback(hwnd, TreeNewHeaderRightClick, &mouseEvent, NULL, Context->CallbackContext); - } - } - break; - case TTN_GETDISPINFO: - { - if (Header->hwndFrom == Context->TooltipsHandle) - { - NMTTDISPINFO *info = (NMTTDISPINFO *)Header; - POINT point; - - PhTnpGetMessagePos(hwnd, &point); - PhTnpGetTooltipText(Context, &point, &info->lpszText); - } - } - break; - case TTN_SHOW: - { - if (Header->hwndFrom == Context->TooltipsHandle) - { - *Result = PhTnpPrepareTooltipShow(Context); - return TRUE; - } - } - break; - case TTN_POP: - { - if (Header->hwndFrom == Context->TooltipsHandle) - { - PhTnpPrepareTooltipPop(Context); - } - } - break; - } - - return FALSE; -} - -ULONG_PTR PhTnpOnUserMessage( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Message, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam - ) -{ - switch (Message) - { - case TNM_SETCALLBACK: - { - Context->Callback = (PPH_TREENEW_CALLBACK)LParam; - Context->CallbackContext = (PVOID)WParam; - - if (!Context->Callback) - Context->Callback = PhTnpNullCallback; - } - return TRUE; - case TNM_NODESSTRUCTURED: - { - if (Context->EnableRedraw <= 0) - { - Context->SuspendUpdateStructure = TRUE; - Context->SuspendUpdateLayout = TRUE; - InvalidateRect(Context->Handle, NULL, FALSE); - return TRUE; - } - - PhTnpRestructureNodes(Context); - PhTnpLayout(Context); - InvalidateRect(Context->Handle, NULL, FALSE); - } - return TRUE; - case TNM_ADDCOLUMN: - return PhTnpAddColumn(Context, (PPH_TREENEW_COLUMN)LParam); - case TNM_REMOVECOLUMN: - return PhTnpRemoveColumn(Context, (ULONG)WParam); - case TNM_GETCOLUMN: - return PhTnpCopyColumn(Context, (ULONG)WParam, (PPH_TREENEW_COLUMN)LParam); - case TNM_SETCOLUMN: - { - PPH_TREENEW_COLUMN column = (PPH_TREENEW_COLUMN)LParam; - - return PhTnpChangeColumn(Context, (ULONG)WParam, column->Id, column); - } - break; - case TNM_GETCOLUMNORDERARRAY: - { - ULONG count = (ULONG)WParam; - PULONG order = (PULONG)LParam; - ULONG i; - - if (count != Context->NumberOfColumnsByDisplay) - return FALSE; - - for (i = 0; i < count; i++) - { - order[i] = Context->ColumnsByDisplay[i]->Id; - } - } - return TRUE; - case TNM_SETCOLUMNORDERARRAY: - { - ULONG count = (ULONG)WParam; - PULONG order = (PULONG)LParam; - ULONG i; - PULONG newOrder; - PPH_TREENEW_COLUMN column; - - newOrder = PhAllocate(count * sizeof(ULONG)); - - for (i = 0; i < count; i++) - { - if (!(column = PhTnpLookupColumnById(Context, order[i]))) - { - PhFree(newOrder); - return FALSE; - } - - newOrder[i] = column->s.ViewIndex; - } - - if (!Header_SetOrderArray(Context->HeaderHandle, count, newOrder)) - { - PhFree(newOrder); - return FALSE; - } - - PhFree(newOrder); - - PhTnpUpdateColumnHeaders(Context); - PhTnpUpdateColumnMaps(Context); - } - return TRUE; - case TNM_SETCURSOR: - { - Context->Cursor = (HCURSOR)LParam; - } - return TRUE; - case TNM_GETSORT: - { - PULONG sortColumn = (PULONG)WParam; - PPH_SORT_ORDER sortOrder = (PPH_SORT_ORDER)LParam; - - if (sortColumn) - *sortColumn = Context->SortColumn; - if (sortOrder) - *sortOrder = Context->SortOrder; - } - return TRUE; - case TNM_SETSORT: - { - ULONG sortColumn = (ULONG)WParam; - PH_SORT_ORDER sortOrder = (PH_SORT_ORDER)LParam; - PPH_TREENEW_COLUMN column; - - if (sortOrder != NoSortOrder) - { - if (!(column = PhTnpLookupColumnById(Context, sortColumn))) - return FALSE; - } - else - { - sortColumn = 0; - column = NULL; - } - - Context->SortColumn = sortColumn; - Context->SortOrder = sortOrder; - - PhTnpSetColumnHeaderSortIcon(Context, column); - - Context->Callback(Context->Handle, TreeNewSortChanged, NULL, NULL, Context->CallbackContext); - } - return TRUE; - case TNM_SETTRISTATE: - Context->TriState = !!WParam; - return TRUE; - case TNM_ENSUREVISIBLE: - return PhTnpEnsureVisibleNode(Context, ((PPH_TREENEW_NODE)LParam)->Index); - case TNM_SCROLL: - PhTnpScroll(Context, (LONG)WParam, (LONG)LParam); - return TRUE; - case TNM_GETFLATNODECOUNT: - if (!Context->SuspendUpdateStructure) - return (LRESULT)Context->FlatList->Count; - else - return 0; - case TNM_GETFLATNODE: - { - ULONG index = (ULONG)WParam; - - if (index >= Context->FlatList->Count) - return (LRESULT)NULL; - - return (LRESULT)Context->FlatList->Items[index]; - } - break; - case TNM_GETCELLTEXT: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)LParam; - - return PhTnpGetCellText( - Context, - getCellText->Node, - getCellText->Id, - &getCellText->Text - ); - } - break; - case TNM_SETNODEEXPANDED: - PhTnpSetExpandedNode(Context, (PPH_TREENEW_NODE)LParam, !!WParam); - return TRUE; - case TNM_GETMAXID: - return (LRESULT)(Context->NextId - 1); - case TNM_SETMAXID: - { - ULONG maxId = (ULONG)WParam; - - if (Context->NextId < maxId + 1) - { - Context->NextId = maxId + 1; - - if (Context->AllocatedColumns < Context->NextId) - { - PhTnpExpandAllocatedColumns(Context); - } - } - } - return TRUE; - case TNM_INVALIDATENODE: - { - PPH_TREENEW_NODE node = (PPH_TREENEW_NODE)LParam; - RECT rect; - - if (!node->Visible) - return FALSE; - - if (!PhTnpGetRowRects(Context, node->Index, node->Index, TRUE, &rect)) - return FALSE; - - InvalidateRect(hwnd, &rect, FALSE); - } - return TRUE; - case TNM_INVALIDATENODES: - { - RECT rect; - - if (!PhTnpGetRowRects(Context, (ULONG)WParam, (ULONG)LParam, TRUE, &rect)) - return FALSE; - - InvalidateRect(hwnd, &rect, FALSE); - } - return TRUE; - case TNM_GETFIXEDHEADER: - return (LRESULT)Context->FixedHeaderHandle; - case TNM_GETHEADER: - return (LRESULT)Context->HeaderHandle; - case TNM_GETTOOLTIPS: - return (LRESULT)Context->TooltipsHandle; - case TNM_SELECTRANGE: - case TNM_DESELECTRANGE: - { - ULONG flags; - ULONG changedStart; - ULONG changedEnd; - RECT rect; - - flags = 0; - - if (Message == TNM_DESELECTRANGE) - flags |= TN_SELECT_DESELECT; - - PhTnpSelectRange(Context, (ULONG)WParam, (ULONG)LParam, flags, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(hwnd, &rect, FALSE); - } - } - return TRUE; - case TNM_GETCOLUMNCOUNT: - return (LRESULT)Context->NumberOfColumns; - case TNM_SETREDRAW: - PhTnpSetRedraw(Context, !!WParam); - return (LRESULT)Context->EnableRedraw; - case TNM_GETVIEWPARTS: - { - PPH_TREENEW_VIEW_PARTS parts = (PPH_TREENEW_VIEW_PARTS)LParam; - - parts->ClientRect = Context->ClientRect; - parts->HeaderHeight = Context->HeaderHeight; - parts->RowHeight = Context->RowHeight; - parts->VScrollWidth = Context->VScrollVisible ? Context->VScrollWidth : 0; - parts->HScrollHeight = Context->HScrollHeight ? Context->HScrollHeight : 0; - parts->VScrollPosition = Context->VScrollPosition; - parts->HScrollPosition = Context->HScrollPosition; - parts->FixedWidth = Context->FixedWidth; - parts->NormalLeft = Context->NormalLeft; - parts->NormalWidth = Context->TotalViewX; - } - return TRUE; - case TNM_GETFIXEDCOLUMN: - return (LRESULT)Context->FixedColumn; - case TNM_GETFIRSTCOLUMN: - return (LRESULT)Context->FirstColumn; - case TNM_SETFOCUSNODE: - Context->FocusNode = (PPH_TREENEW_NODE)LParam; - return TRUE; - case TNM_SETMARKNODE: - Context->MarkNodeIndex = ((PPH_TREENEW_NODE)LParam)->Index; - return TRUE; - case TNM_SETHOTNODE: - PhTnpSetHotNode(Context, (PPH_TREENEW_NODE)LParam, FALSE); - return TRUE; - case TNM_SETEXTENDEDFLAGS: - Context->ExtendedFlags = (Context->ExtendedFlags & ~(ULONG)WParam) | ((ULONG)LParam & (ULONG)WParam); - return TRUE; - case TNM_GETCALLBACK: - { - PPH_TREENEW_CALLBACK *callback = (PPH_TREENEW_CALLBACK *)LParam; - PVOID *callbackContext = (PVOID *)WParam; - - if (callback) - { - if (Context->Callback != PhTnpNullCallback) - *callback = Context->Callback; - else - *callback = NULL; - } - - if (callbackContext) - { - *callbackContext = Context->CallbackContext; - } - } - return TRUE; - case TNM_HITTEST: - PhTnpHitTest(Context, (PPH_TREENEW_HIT_TEST)LParam); - return TRUE; - case TNM_GETVISIBLECOLUMNCOUNT: - return Context->NumberOfColumnsByDisplay + (Context->FixedColumnVisible ? 1 : 0); - case TNM_AUTOSIZECOLUMN: - { - ULONG id = (ULONG)WParam; - ULONG flags = (ULONG)LParam; - PPH_TREENEW_COLUMN column; - - if (!(column = PhTnpLookupColumnById(Context, id))) - return FALSE; - - if (!column->Visible) - return FALSE; - - PhTnpAutoSizeColumnHeader( - Context, - column->Fixed ? Context->FixedHeaderHandle : Context->HeaderHandle, - column, - flags - ); - } - return TRUE; - case TNM_SETEMPTYTEXT: - { - PPH_STRINGREF text = (PPH_STRINGREF)LParam; - ULONG flags = (ULONG)WParam; - - Context->EmptyText = *text; - } - return TRUE; - case TNM_SETROWHEIGHT: - { - LONG rowHeight = (LONG)WParam; - - if (rowHeight != 0) - { - Context->CustomRowHeight = TRUE; - Context->RowHeight = rowHeight; - } - else - { - Context->CustomRowHeight = FALSE; - PhTnpUpdateTextMetrics(Context); - } - } - return TRUE; - case TNM_ISFLATNODEVALID: - return !Context->SuspendUpdateStructure; - } - - return 0; -} - -VOID PhTnpSetFont( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ HFONT Font, - _In_ BOOLEAN Redraw - ) -{ - if (Context->FontOwned) - { - DeleteObject(Context->Font); - Context->FontOwned = FALSE; - } - - Context->Font = Font; - - if (!Context->Font) - { - LOGFONT logFont; - - if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) - { - Context->Font = CreateFontIndirect(&logFont); - Context->FontOwned = TRUE; - } - } - - SendMessage(Context->FixedHeaderHandle, WM_SETFONT, (WPARAM)Context->Font, Redraw); - SendMessage(Context->HeaderHandle, WM_SETFONT, (WPARAM)Context->Font, Redraw); - - if (Context->TooltipsHandle) - { - SendMessage(Context->TooltipsHandle, WM_SETFONT, (WPARAM)Context->Font, FALSE); - Context->TooltipFont = Context->Font; - } - - PhTnpUpdateTextMetrics(Context); -} - -VOID PhTnpUpdateSystemMetrics( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - Context->VScrollWidth = GetSystemMetrics(SM_CXVSCROLL); - Context->HScrollHeight = GetSystemMetrics(SM_CYHSCROLL); - Context->SystemBorderX = GetSystemMetrics(SM_CXBORDER); - Context->SystemBorderY = GetSystemMetrics(SM_CYBORDER); - Context->SystemEdgeX = GetSystemMetrics(SM_CXEDGE); - Context->SystemEdgeY = GetSystemMetrics(SM_CYEDGE); - Context->SystemDragX = GetSystemMetrics(SM_CXDRAG); - Context->SystemDragY = GetSystemMetrics(SM_CYDRAG); - - if (Context->SystemDragX < 2) - Context->SystemDragX = 2; - if (Context->SystemDragY < 2) - Context->SystemDragY = 2; -} - -VOID PhTnpUpdateTextMetrics( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - HDC hdc; - - if (hdc = GetDC(Context->Handle)) - { - SelectObject(hdc, Context->Font); - GetTextMetrics(hdc, &Context->TextMetrics); - - if (!Context->CustomRowHeight) - { - // Below we try to match the row height as calculated by the list view, even if it - // involves magic numbers. On Vista and above there seems to be extra padding. - - Context->RowHeight = Context->TextMetrics.tmHeight; - - if (Context->Style & TN_STYLE_ICONS) - { - if (Context->RowHeight < SmallIconHeight) - Context->RowHeight = SmallIconHeight; - } - else - { - if (WindowsVersion >= WINDOWS_VISTA && !(Context->Style & TN_STYLE_THIN_ROWS)) - Context->RowHeight += 1; // HACK - } - - Context->RowHeight += 1; // HACK - - if (WindowsVersion >= WINDOWS_VISTA && !(Context->Style & TN_STYLE_THIN_ROWS)) - Context->RowHeight += 2; // HACK - } - - ReleaseDC(Context->Handle, hdc); - } -} - -VOID PhTnpUpdateThemeData( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - Context->ThemeActive = !!IsThemeActive(); - - if (Context->ThemeData) - { - CloseThemeData(Context->ThemeData); - Context->ThemeData = NULL; - } - - Context->ThemeData = OpenThemeData(Context->Handle, L"TREEVIEW"); - - if (Context->ThemeData) - { - Context->ThemeHasItemBackground = !!IsThemePartDefined(Context->ThemeData, TVP_TREEITEM, 0); - Context->ThemeHasGlyph = !!IsThemePartDefined(Context->ThemeData, TVP_GLYPH, 0); - Context->ThemeHasHotGlyph = !!IsThemePartDefined(Context->ThemeData, TVP_HOTGLYPH, 0); - } - else - { - Context->ThemeHasItemBackground = FALSE; - Context->ThemeHasGlyph = FALSE; - Context->ThemeHasHotGlyph = FALSE; - } -} - -VOID PhTnpInitializeThemeData( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - if (!Context->ThemeInitialized) - { - PhTnpUpdateThemeData(Context); - Context->ThemeInitialized = TRUE; - } -} - -VOID PhTnpCancelTrack( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - PhTnpSetFixedWidth(Context, Context->TrackOldFixedWidth); - ReleaseCapture(); -} - -VOID PhTnpLayout( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - RECT clientRect; - - if (Context->EnableRedraw <= 0) - { - Context->SuspendUpdateLayout = TRUE; - return; - } - - clientRect = Context->ClientRect; - - PhTnpUpdateScrollBars(Context); - - // Vertical scroll bar - if (Context->VScrollVisible) - { - MoveWindow( - Context->VScrollHandle, - clientRect.right - Context->VScrollWidth, - 0, - Context->VScrollWidth, - clientRect.bottom - (Context->HScrollVisible ? Context->HScrollHeight : 0), - TRUE - ); - } - - // Horizontal scroll bar - if (Context->HScrollVisible) - { - MoveWindow( - Context->HScrollHandle, - Context->NormalLeft, - clientRect.bottom - Context->HScrollHeight, - clientRect.right - Context->NormalLeft - (Context->VScrollVisible ? Context->VScrollWidth : 0), - Context->HScrollHeight, - TRUE - ); - } - - // Filler box - if (Context->VScrollVisible && Context->HScrollVisible) - { - MoveWindow( - Context->FillerBoxHandle, - clientRect.right - Context->VScrollWidth, - clientRect.bottom - Context->HScrollHeight, - Context->VScrollWidth, - Context->HScrollHeight, - TRUE - ); - } - - PhTnpLayoutHeader(Context); - - // Redraw the entire window if we are displaying empty text. - if (Context->FlatList->Count == 0 && Context->EmptyText.Length != 0) - InvalidateRect(Context->Handle, NULL, FALSE); -} - -VOID PhTnpLayoutHeader( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - RECT rect; - HDLAYOUT hdl; - WINDOWPOS windowPos; - - hdl.prc = ▭ - hdl.pwpos = &windowPos; - - if (!(Context->Style & TN_STYLE_NO_COLUMN_HEADER)) - { - // Fixed portion header control - rect.left = 0; - rect.top = 0; - rect.right = Context->NormalLeft; - rect.bottom = Context->ClientRect.bottom; - Header_Layout(Context->FixedHeaderHandle, &hdl); - SetWindowPos(Context->FixedHeaderHandle, NULL, windowPos.x, windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags); - Context->HeaderHeight = windowPos.cy; - - // Normal portion header control - rect.left = Context->NormalLeft - Context->HScrollPosition; - rect.top = 0; - rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - rect.bottom = Context->ClientRect.bottom; - Header_Layout(Context->HeaderHandle, &hdl); - SetWindowPos(Context->HeaderHandle, NULL, windowPos.x, windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags); - } - else - { - Context->HeaderHeight = 0; - } - - if (Context->TooltipsHandle) - { - TOOLINFO toolInfo; - - memset(&toolInfo, 0, sizeof(TOOLINFO)); - toolInfo.cbSize = sizeof(TOOLINFO); - toolInfo.hwnd = Context->FixedHeaderHandle; - toolInfo.uId = TNP_TOOLTIPS_FIXED_HEADER; - GetClientRect(Context->FixedHeaderHandle, &toolInfo.rect); - SendMessage(Context->TooltipsHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); - - toolInfo.hwnd = Context->HeaderHandle; - toolInfo.uId = TNP_TOOLTIPS_HEADER; - GetClientRect(Context->HeaderHandle, &toolInfo.rect); - SendMessage(Context->TooltipsHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); - } -} - -VOID PhTnpSetFixedWidth( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG FixedWidth - ) -{ - HDITEM item; - - if (Context->FixedColumnVisible) - { - Context->FixedWidth = FixedWidth; - - if (Context->FixedWidth < Context->FixedWidthMinimum) - Context->FixedWidth = Context->FixedWidthMinimum; - - Context->NormalLeft = Context->FixedWidth + 1; - - item.mask = HDI_WIDTH; - item.cxy = Context->FixedWidth + 1; - Header_SetItem(Context->FixedHeaderHandle, 0, &item); - } - else - { - Context->FixedWidth = 0; - Context->NormalLeft = 0; - } -} - -VOID PhTnpSetRedraw( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Redraw - ) -{ - if (Redraw) - Context->EnableRedraw++; - else - Context->EnableRedraw--; - - if (Context->EnableRedraw == 1) - { - if (Context->SuspendUpdateStructure) - { - PhTnpRestructureNodes(Context); - } - - if (Context->SuspendUpdateLayout) - { - PhTnpLayout(Context); - } - - if (Context->SuspendUpdateMoveMouse) - { - POINT point; - - PhTnpGetMessagePos(Context->Handle, &point); - PhTnpProcessMoveMouse(Context, point.x, point.y); - } - - Context->SuspendUpdateStructure = FALSE; - Context->SuspendUpdateLayout = FALSE; - Context->SuspendUpdateMoveMouse = FALSE; - - if (Context->SuspendUpdateRegion) - { - InvalidateRgn(Context->Handle, Context->SuspendUpdateRegion, FALSE); - DeleteObject(Context->SuspendUpdateRegion); - Context->SuspendUpdateRegion = NULL; - } - } -} - -VOID PhTnpSendMouseEvent( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PH_TREENEW_MESSAGE Message, - _In_ LONG CursorX, - _In_ LONG CursorY, - _In_ PPH_TREENEW_NODE Node, - _In_ PPH_TREENEW_COLUMN Column, - _In_ ULONG VirtualKeys - ) -{ - PH_TREENEW_MOUSE_EVENT mouseEvent; - - mouseEvent.Location.x = CursorX; - mouseEvent.Location.y = CursorY; - mouseEvent.Node = Node; - mouseEvent.Column = Column; - mouseEvent.KeyFlags = VirtualKeys; - Context->Callback(Context->Handle, Message, &mouseEvent, NULL, Context->CallbackContext); -} - -PPH_TREENEW_COLUMN PhTnpLookupColumnById( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id - ) -{ - if (Id >= Context->AllocatedColumns) - return NULL; - - return Context->Columns[Id]; -} - -BOOLEAN PhTnpAddColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN Column - ) -{ - PPH_TREENEW_COLUMN realColumn; - - // Check if a column with the same ID already exists. - if (Column->Id < Context->AllocatedColumns && Context->Columns[Column->Id]) - return FALSE; - - if (Context->NextId < Column->Id + 1) - Context->NextId = Column->Id + 1; - - realColumn = PhAllocateCopy(Column, sizeof(PH_TREENEW_COLUMN)); - - if (realColumn->DpiScaleOnAdd) - { - realColumn->Width = PhMultiplyDivide(realColumn->Width, PhGlobalDpi, 96); - realColumn->DpiScaleOnAdd = FALSE; - } - - if (Context->AllocatedColumns < Context->NextId) - { - PhTnpExpandAllocatedColumns(Context); - } - - Context->Columns[Column->Id] = realColumn; - Context->NumberOfColumns++; - - if (realColumn->Fixed) - { - if (Context->FixedColumn) - { - // We already have a fixed column, and we can't have two. Make this new column un-fixed. - realColumn->Fixed = FALSE; - } - else - { - Context->FixedColumn = realColumn; - } - - realColumn->DisplayIndex = 0; - realColumn->s.ViewX = 0; - } - - if (realColumn->Visible) - { - BOOLEAN updateHeaders; - - updateHeaders = FALSE; - - if (!realColumn->Fixed && realColumn->DisplayIndex != Header_GetItemCount(Context->HeaderHandle)) - updateHeaders = TRUE; - - realColumn->s.ViewIndex = PhTnpInsertColumnHeader(Context, realColumn); - - if (updateHeaders) - PhTnpUpdateColumnHeaders(Context); - } - else - { - realColumn->s.ViewIndex = -1; - } - - PhTnpUpdateColumnMaps(Context); - - if (realColumn->Visible) - PhTnpLayout(Context); - - return TRUE; -} - -BOOLEAN PhTnpRemoveColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id - ) -{ - PPH_TREENEW_COLUMN realColumn; - BOOLEAN updateLayout; - - if (!(realColumn = PhTnpLookupColumnById(Context, Id))) - return FALSE; - - updateLayout = FALSE; - - if (realColumn->Visible) - updateLayout = TRUE; - - PhTnpDeleteColumnHeader(Context, realColumn); - Context->Columns[realColumn->Id] = NULL; - PhFree(realColumn); - PhTnpUpdateColumnMaps(Context); - - if (updateLayout) - PhTnpLayout(Context); - - Context->NumberOfColumns--; - - return TRUE; -} - -BOOLEAN PhTnpCopyColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Id, - _Out_ PPH_TREENEW_COLUMN Column - ) -{ - PPH_TREENEW_COLUMN realColumn; - - if (!(realColumn = PhTnpLookupColumnById(Context, Id))) - return FALSE; - - memcpy(Column, realColumn, sizeof(PH_TREENEW_COLUMN)); - - return TRUE; -} - -BOOLEAN PhTnpChangeColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Mask, - _In_ ULONG Id, - _In_ PPH_TREENEW_COLUMN Column - ) -{ - PPH_TREENEW_COLUMN realColumn; - BOOLEAN addingOrRemoving; - - if (!(realColumn = PhTnpLookupColumnById(Context, Id))) - return FALSE; - - addingOrRemoving = FALSE; - - if (Mask & TN_COLUMN_FLAG_VISIBLE) - { - if (realColumn->Visible != Column->Visible) - { - addingOrRemoving = TRUE; - } - } - - if (Mask & TN_COLUMN_FLAG_CUSTOMDRAW) - { - realColumn->CustomDraw = Column->CustomDraw; - } - - if (Mask & TN_COLUMN_FLAG_SORTDESCENDING) - { - realColumn->SortDescending = Column->SortDescending; - } - - if (Mask & (TN_COLUMN_TEXT | TN_COLUMN_WIDTH | TN_COLUMN_ALIGNMENT | TN_COLUMN_DISPLAYINDEX)) - { - BOOLEAN updateHeaders; - BOOLEAN updateMaps; - BOOLEAN updateLayout; - - updateHeaders = FALSE; - updateMaps = FALSE; - updateLayout = FALSE; - - if (Mask & TN_COLUMN_TEXT) - { - realColumn->Text = Column->Text; - } - - if (Mask & TN_COLUMN_WIDTH) - { - realColumn->Width = Column->Width; - updateMaps = TRUE; - } - - if (Mask & TN_COLUMN_ALIGNMENT) - { - realColumn->Alignment = Column->Alignment; - } - - if (Mask & TN_COLUMN_DISPLAYINDEX) - { - realColumn->DisplayIndex = Column->DisplayIndex; - updateHeaders = TRUE; - updateMaps = TRUE; - updateLayout = TRUE; - } - - if (!addingOrRemoving && realColumn->Visible) - { - PhTnpChangeColumnHeader(Context, Mask, realColumn); - - if (updateHeaders) - PhTnpUpdateColumnHeaders(Context); - if (updateMaps) - PhTnpUpdateColumnMaps(Context); - if (updateLayout) - PhTnpLayout(Context); - } - } - - if (Mask & TN_COLUMN_CONTEXT) - { - realColumn->Context = Column->Context; - } - - if (Mask & TN_COLUMN_TEXTFLAGS) - { - realColumn->TextFlags = Column->TextFlags; - } - - if (addingOrRemoving) - { - if (Column->Visible) - { - BOOLEAN updateHeaders; - - updateHeaders = FALSE; - - if (realColumn->Fixed) - { - realColumn->DisplayIndex = 0; - } - else - { - if (Mask & TN_COLUMN_DISPLAYINDEX) - updateHeaders = TRUE; - else - realColumn->DisplayIndex = Header_GetItemCount(Context->HeaderHandle); - } - - realColumn->s.ViewIndex = PhTnpInsertColumnHeader(Context, realColumn); - - if (updateHeaders) - PhTnpUpdateColumnHeaders(Context); - } - else - { - PhTnpDeleteColumnHeader(Context, realColumn); - } - - PhTnpUpdateColumnMaps(Context); - PhTnpLayout(Context); - } - - return TRUE; -} - -VOID PhTnpExpandAllocatedColumns( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - if (Context->Columns) - { - ULONG oldAllocatedColumns; - - oldAllocatedColumns = Context->AllocatedColumns; - Context->AllocatedColumns *= 2; - - if (Context->AllocatedColumns < Context->NextId) - Context->AllocatedColumns = Context->NextId; - - Context->Columns = PhReAllocate( - Context->Columns, - Context->AllocatedColumns * sizeof(PPH_TREENEW_COLUMN) - ); - - // Zero the newly allocated portion. - memset( - &Context->Columns[oldAllocatedColumns], - 0, - (Context->AllocatedColumns - oldAllocatedColumns) * sizeof(PPH_TREENEW_COLUMN) - ); - } - else - { - Context->AllocatedColumns = 16; - - if (Context->AllocatedColumns < Context->NextId) - Context->AllocatedColumns = Context->NextId; - - Context->Columns = PhAllocate( - Context->AllocatedColumns * sizeof(PPH_TREENEW_COLUMN) - ); - memset(Context->Columns, 0, Context->AllocatedColumns * sizeof(PPH_TREENEW_COLUMN)); - } -} - -VOID PhTnpUpdateColumnMaps( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - ULONG i; - LONG x; - - if (Context->AllocatedColumnsByDisplay < Context->NumberOfColumns) - { - if (Context->ColumnsByDisplay) - PhFree(Context->ColumnsByDisplay); - - Context->ColumnsByDisplay = PhAllocate(sizeof(PPH_TREENEW_COLUMN) * Context->NumberOfColumns); - Context->AllocatedColumnsByDisplay = Context->NumberOfColumns; - } - - memset(Context->ColumnsByDisplay, 0, sizeof(PPH_TREENEW_COLUMN) * Context->AllocatedColumnsByDisplay); - - for (i = 0; i < Context->NextId; i++) - { - if (!Context->Columns[i]) - continue; - - if (Context->Columns[i]->Visible && !Context->Columns[i]->Fixed && Context->Columns[i]->DisplayIndex != -1) - { - if (Context->Columns[i]->DisplayIndex >= Context->NumberOfColumns) - PhRaiseStatus(STATUS_INTERNAL_ERROR); - - Context->ColumnsByDisplay[Context->Columns[i]->DisplayIndex] = Context->Columns[i]; - } - } - - x = 0; - - for (i = 0; i < Context->AllocatedColumnsByDisplay; i++) - { - if (!Context->ColumnsByDisplay[i]) - break; - - Context->ColumnsByDisplay[i]->s.ViewX = x; - x += Context->ColumnsByDisplay[i]->Width; - } - - Context->NumberOfColumnsByDisplay = i; - Context->TotalViewX = x; - - if (Context->FixedColumnVisible) - Context->FirstColumn = Context->FixedColumn; - else if (Context->NumberOfColumnsByDisplay != 0) - Context->FirstColumn = Context->ColumnsByDisplay[0]; - else - Context->FirstColumn = NULL; - - if (Context->NumberOfColumnsByDisplay != 0) - Context->LastColumn = Context->ColumnsByDisplay[Context->NumberOfColumnsByDisplay - 1]; - else if (Context->FixedColumnVisible) - Context->LastColumn = Context->FixedColumn; - else - Context->LastColumn = NULL; -} - -LONG PhTnpInsertColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN Column - ) -{ - HDITEM item; - - if (Column->Fixed) - { - if (Column->Width < Context->FixedWidthMinimum) - Column->Width = Context->FixedWidthMinimum; - - Context->FixedWidth = Column->Width; - Context->NormalLeft = Context->FixedWidth + 1; - Context->FixedColumnVisible = TRUE; - - if (!(Context->Style & TN_STYLE_NO_DIVIDER)) - Context->FixedDividerVisible = TRUE; - } - - memset(&item, 0, sizeof(HDITEM)); - item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT | HDI_LPARAM | HDI_ORDER; - item.cxy = Column->Width; - item.pszText = Column->Text; - item.fmt = 0; - item.lParam = (LPARAM)Column; - - if (Column->Fixed) - item.cxy++; - - if (Column->Fixed) - item.iOrder = 0; - else - item.iOrder = Column->DisplayIndex; - - if (Column->Alignment & PH_ALIGN_LEFT) - item.fmt |= HDF_LEFT; - else if (Column->Alignment & PH_ALIGN_RIGHT) - item.fmt |= HDF_RIGHT; - else - item.fmt |= HDF_CENTER; - - if (Column->Id == Context->SortColumn) - { - if (Context->SortOrder == AscendingSortOrder) - item.fmt |= HDF_SORTUP; - else if (Context->SortOrder == DescendingSortOrder) - item.fmt |= HDF_SORTDOWN; - } - - Column->Visible = TRUE; - - if (Column->Fixed) - return Header_InsertItem(Context->FixedHeaderHandle, 0, &item); - else - return Header_InsertItem(Context->HeaderHandle, MAXINT, &item); -} - -VOID PhTnpChangeColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Mask, - _In_ PPH_TREENEW_COLUMN Column - ) -{ - HDITEM item; - - memset(&item, 0, sizeof(HDITEM)); - item.mask = 0; - - if (Mask & TN_COLUMN_TEXT) - { - item.mask |= HDI_TEXT; - item.pszText = Column->Text; - } - - if (Mask & TN_COLUMN_WIDTH) - { - item.mask |= HDI_WIDTH; - item.cxy = Column->Width; - - if (Column->Fixed) - item.cxy++; - } - - if (Mask & TN_COLUMN_ALIGNMENT) - { - item.mask |= HDI_FORMAT; - item.fmt = 0; - - if (Column->Alignment & PH_ALIGN_LEFT) - item.fmt |= HDF_LEFT; - else if (Column->Alignment & PH_ALIGN_RIGHT) - item.fmt |= HDF_RIGHT; - else - item.fmt |= HDF_CENTER; - - if (Column->Id == Context->SortColumn) - { - if (Context->SortOrder == AscendingSortOrder) - item.fmt |= HDF_SORTUP; - else if (Context->SortOrder == DescendingSortOrder) - item.fmt |= HDF_SORTDOWN; - } - } - - if (Mask & TN_COLUMN_DISPLAYINDEX) - { - item.mask |= HDI_ORDER; - - if (Column->Fixed) - item.iOrder = 0; - else - item.iOrder = Column->DisplayIndex; - } - - if (Column->Fixed) - Header_SetItem(Context->FixedHeaderHandle, 0, &item); - else - Header_SetItem(Context->HeaderHandle, Column->s.ViewIndex, &item); -} - -VOID PhTnpDeleteColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _Inout_ PPH_TREENEW_COLUMN Column - ) -{ - if (Column->Fixed) - { - Context->FixedColumn = NULL; - Context->FixedWidth = 0; - Context->NormalLeft = 0; - Context->FixedColumnVisible = FALSE; - Context->FixedDividerVisible = FALSE; - } - - if (Column->Fixed) - Header_DeleteItem(Context->FixedHeaderHandle, Column->s.ViewIndex); - else - Header_DeleteItem(Context->HeaderHandle, Column->s.ViewIndex); - - Column->Visible = FALSE; - Column->s.ViewIndex = -1; - PhTnpUpdateColumnHeaders(Context); -} - -VOID PhTnpUpdateColumnHeaders( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - ULONG count; - ULONG i; - HDITEM item; - PPH_TREENEW_COLUMN column; - - item.mask = HDI_WIDTH | HDI_LPARAM | HDI_ORDER; - - // Fixed column - - if (Context->FixedColumnVisible && Header_GetItem(Context->FixedHeaderHandle, 0, &item)) - { - column = Context->FixedColumn; - column->Width = item.cxy - 1; - } - - // Normal columns - - count = Header_GetItemCount(Context->HeaderHandle); - - if (count != -1) - { - for (i = 0; i < count; i++) - { - if (Header_GetItem(Context->HeaderHandle, i, &item)) - { - column = (PPH_TREENEW_COLUMN)item.lParam; - column->s.ViewIndex = i; - column->Width = item.cxy; - column->DisplayIndex = item.iOrder; - } - } - } -} - -VOID PhTnpProcessResizeColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN Column, - _In_ LONG Delta - ) -{ - RECT rect; - LONG columnLeft; - - if (Column->Fixed) - { - columnLeft = 0; - } - else - { - columnLeft = Context->NormalLeft + Column->s.ViewX - Context->HScrollPosition; - } - - // Scroll the content to the right of the column. - // - // Clip the scroll area to the new width, or the old width if that is further to the left. We - // may have the WS_CLIPCHILDREN style set, so we need to remove the horizontal scrollbar from - // the rectangle, otherwise ScrollWindowEx will want to invalidate the entire region! (The - // horizontal scrollbar is an overlapping child control.) - rect.left = columnLeft + Column->Width; - rect.top = Context->HeaderHeight; - rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - rect.bottom = Context->ClientRect.bottom - (Context->HScrollVisible ? Context->HScrollHeight : 0); - - if (Delta > 0) - rect.left -= Delta; // old width - - // Scroll the window. - ScrollWindowEx( - Context->Handle, - Delta, - 0, - &rect, - &rect, - NULL, - NULL, - SW_INVALIDATE - ); - - UpdateWindow(Context->Handle); // required - - if (Context->HScrollVisible) - { - // We excluded the bottom region - invalidate it now. - rect.top = rect.bottom; - rect.bottom = Context->ClientRect.bottom; - InvalidateRect(Context->Handle, &rect, FALSE); - } - - PhTnpLayout(Context); - - // Redraw the whole column because the content may depend on the width (e.g. text ellipsis). - rect.left = columnLeft; - rect.top = Context->HeaderHeight; - rect.right = columnLeft + Column->Width; - RedrawWindow(Context->Handle, &rect, NULL, RDW_INVALIDATE | RDW_UPDATENOW); // must be RedrawWindow -} - -VOID PhTnpProcessSortColumn( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_COLUMN NewColumn - ) -{ - if (NewColumn->Id == Context->SortColumn) - { - if (Context->TriState) - { - if (!NewColumn->SortDescending) - { - // Ascending -> Descending -> None - - if (Context->SortOrder == AscendingSortOrder) - Context->SortOrder = DescendingSortOrder; - else if (Context->SortOrder == DescendingSortOrder) - Context->SortOrder = NoSortOrder; - else - Context->SortOrder = AscendingSortOrder; - } - else - { - // Descending -> Ascending -> None - - if (Context->SortOrder == DescendingSortOrder) - Context->SortOrder = AscendingSortOrder; - else if (Context->SortOrder == AscendingSortOrder) - Context->SortOrder = NoSortOrder; - else - Context->SortOrder = DescendingSortOrder; - } - } - else - { - if (Context->SortOrder == AscendingSortOrder) - Context->SortOrder = DescendingSortOrder; - else - Context->SortOrder = AscendingSortOrder; - } - } - else - { - Context->SortColumn = NewColumn->Id; - - if (!NewColumn->SortDescending) - Context->SortOrder = AscendingSortOrder; - else - Context->SortOrder = DescendingSortOrder; - } - - PhTnpSetColumnHeaderSortIcon(Context, NewColumn); - - Context->Callback(Context->Handle, TreeNewSortChanged, NULL, NULL, Context->CallbackContext); -} - -BOOLEAN PhTnpSetColumnHeaderSortIcon( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ PPH_TREENEW_COLUMN SortColumnPointer - ) -{ - if (Context->SortOrder == NoSortOrder) - { - PhSetHeaderSortIcon( - Context->FixedHeaderHandle, - -1, - NoSortOrder - ); - PhSetHeaderSortIcon( - Context->HeaderHandle, - -1, - NoSortOrder - ); - - return TRUE; - } - - if (!SortColumnPointer) - { - if (!(SortColumnPointer = PhTnpLookupColumnById(Context, Context->SortColumn))) - return FALSE; - } - - if (SortColumnPointer->Fixed) - { - PhSetHeaderSortIcon( - Context->FixedHeaderHandle, - 0, - Context->SortOrder - ); - PhSetHeaderSortIcon( - Context->HeaderHandle, - -1, - NoSortOrder - ); - } - else - { - PhSetHeaderSortIcon( - Context->FixedHeaderHandle, - -1, - NoSortOrder - ); - PhSetHeaderSortIcon( - Context->HeaderHandle, - SortColumnPointer->s.ViewIndex, - Context->SortOrder - ); - } - - return TRUE; -} - -VOID PhTnpAutoSizeColumnHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HWND HeaderHandle, - _In_ PPH_TREENEW_COLUMN Column, - _In_ ULONG Flags - ) -{ - LONG newWidth; - HDITEM item; - - if (Flags & TN_AUTOSIZE_REMAINING_SPACE) - { - newWidth = Context->ClientRect.right - (Context->TotalViewX - Column->Width); - - if (Context->FixedColumn) - newWidth -= Context->FixedColumn->Width; - if (Context->VScrollVisible) - newWidth -= Context->VScrollWidth; - - if (newWidth <= 0) - return; - } - else - { - ULONG i; - LONG maximumWidth; - PH_TREENEW_CELL_PARTS parts; - LONG width; - - if (Context->FlatList->Count == 0) - return; - if (Column->CustomDraw) - return; - - maximumWidth = 0; - - for (i = 0; i < Context->FlatList->Count; i++) - { - if (PhTnpGetCellParts(Context, i, Column, TN_MEASURE_TEXT, &parts) && - (parts.Flags & TN_PART_CELL) && (parts.Flags & TN_PART_CONTENT) && (parts.Flags & TN_PART_TEXT)) - { - width = parts.TextRect.right - parts.TextRect.left; // text width - width += parts.ContentRect.left - parts.CellRect.left; // left padding - - if (maximumWidth < width) - maximumWidth = width; - } - } - - newWidth = maximumWidth + TNP_CELL_RIGHT_MARGIN; // right padding - - if (Column->Fixed) - newWidth++; - } - - item.mask = HDI_WIDTH; - item.cxy = newWidth; - - Header_SetItem(HeaderHandle, Column->s.ViewIndex, &item); -} - -BOOLEAN PhTnpGetNodeChildren( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ PPH_TREENEW_NODE Node, - _Out_ PPH_TREENEW_NODE **Children, - _Out_ PULONG NumberOfChildren - ) -{ - PH_TREENEW_GET_CHILDREN getChildren; - - getChildren.Flags = 0; - getChildren.Node = Node; - getChildren.Children = NULL; - getChildren.NumberOfChildren = 0; - - if (Context->Callback( - Context->Handle, - TreeNewGetChildren, - &getChildren, - NULL, - Context->CallbackContext - )) - { - *Children = getChildren.Children; - *NumberOfChildren = getChildren.NumberOfChildren; - - return TRUE; - } - - return FALSE; -} - -BOOLEAN PhTnpIsNodeLeaf( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node - ) -{ - PH_TREENEW_IS_LEAF isLeaf; - - isLeaf.Flags = 0; - isLeaf.Node = Node; - isLeaf.IsLeaf = TRUE; - - if (Context->Callback( - Context->Handle, - TreeNewIsLeaf, - &isLeaf, - NULL, - Context->CallbackContext - )) - { - return isLeaf.IsLeaf; - } - - // Doesn't matter, decide when we do the get-children callback. - return FALSE; -} - -BOOLEAN PhTnpGetCellText( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ ULONG Id, - _Out_ PPH_STRINGREF Text - ) -{ - PH_TREENEW_GET_CELL_TEXT getCellText; - - if (Id < Node->TextCacheSize && Node->TextCache[Id].Buffer) - { - *Text = Node->TextCache[Id]; - return TRUE; - } - - getCellText.Flags = 0; - getCellText.Node = Node; - getCellText.Id = Id; - PhInitializeEmptyStringRef(&getCellText.Text); - - if (Context->Callback( - Context->Handle, - TreeNewGetCellText, - &getCellText, - NULL, - Context->CallbackContext - ) && getCellText.Text.Buffer) - { - *Text = getCellText.Text; - - if ((getCellText.Flags & TN_CACHE) && Id < Node->TextCacheSize) - Node->TextCache[Id] = getCellText.Text; - - return TRUE; - } - - return FALSE; -} - -VOID PhTnpRestructureNodes( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - PPH_TREENEW_NODE *children; - ULONG numberOfChildren; - ULONG i; - - if (!PhTnpGetNodeChildren(Context, NULL, &children, &numberOfChildren)) - return; - - // We try to preserve the hot node, the focused node and the selection mark node. At this point - // all node pointers must be regarded as invalid, so we must not follow any pointers. - - Context->FocusNodeFound = FALSE; - - PhClearList(Context->FlatList); - Context->CanAnyExpand = FALSE; - - for (i = 0; i < numberOfChildren; i++) - { - PhTnpInsertNodeChildren(Context, children[i], 0); - } - - if (!Context->FocusNodeFound) - Context->FocusNode = NULL; // focused node is no longer present - - if (Context->HotNodeIndex >= Context->FlatList->Count) // covers -1 case as well - Context->HotNodeIndex = -1; - - if (Context->MarkNodeIndex >= Context->FlatList->Count) - Context->MarkNodeIndex = -1; -} - -VOID PhTnpInsertNodeChildren( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ ULONG Level - ) -{ - PPH_TREENEW_NODE *children; - ULONG numberOfChildren; - ULONG i; - ULONG nextLevel; - - if (Node->Visible) - { - Node->Level = Level; - - Node->Index = Context->FlatList->Count; - PhAddItemList(Context->FlatList, Node); - - if (Context->FocusNode == Node) - Context->FocusNodeFound = TRUE; - - nextLevel = Level + 1; - } - else - { - nextLevel = 0; // children of this node should be level 0 - } - - if (!(Node->s.IsLeaf = PhTnpIsNodeLeaf(Context, Node))) - { - Context->CanAnyExpand = TRUE; - - if (Node->Expanded) - { - if (PhTnpGetNodeChildren(Context, Node, &children, &numberOfChildren)) - { - for (i = 0; i < numberOfChildren; i++) - { - PhTnpInsertNodeChildren(Context, children[i], nextLevel); - } - - if (numberOfChildren == 0) - Node->s.IsLeaf = TRUE; - } - } - } -} - -VOID PhTnpSetExpandedNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ BOOLEAN Expanded - ) -{ - if (Node->Expanded != Expanded) - { - PH_TREENEW_NODE_EVENT nodeEvent; - - memset(&nodeEvent, 0, sizeof(PH_TREENEW_NODE_EVENT)); - Context->Callback(Context->Handle, TreeNewNodeExpanding, Node, &nodeEvent, Context->CallbackContext); - - if (!nodeEvent.Handled) - { - if (!Expanded) - { - ULONG i; - PPH_TREENEW_NODE node; - BOOLEAN changed; - - // Make sure no children are selected - we don't want invisible selected nodes. Note - // that this does not cause any UI changes by itself, since we are hiding the nodes. - - changed = FALSE; - - for (i = Node->Index + 1; i < Context->FlatList->Count; i++) - { - node = Context->FlatList->Items[i]; - - if (node->Level <= Node->Level) - break; // no more children - - if (node->Selected) - { - node->Selected = FALSE; - changed = TRUE; - } - } - - if (changed) - { - Context->Callback(Context->Handle, TreeNewSelectionChanged, NULL, NULL, Context->CallbackContext); - } - } - - Node->Expanded = Expanded; - PhTnpRestructureNodes(Context); - // We need to update the window before the scrollbars get updated in order for the - // scroll processing to work properly. - InvalidateRect(Context->Handle, NULL, FALSE); - UpdateWindow(Context->Handle); - PhTnpLayout(Context); - } - } -} - -BOOLEAN PhTnpGetCellParts( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Index, - _In_opt_ PPH_TREENEW_COLUMN Column, - _In_ ULONG Flags, - _Out_ PPH_TREENEW_CELL_PARTS Parts - ) -{ - PPH_TREENEW_NODE node; - LONG viewWidth; - LONG nodeY; - LONG iconVerticalMargin; - LONG currentX; - - if (Index >= Context->FlatList->Count) - return FALSE; - - node = Context->FlatList->Items[Index]; - nodeY = Context->HeaderHeight + ((LONG)Index - Context->VScrollPosition) * Context->RowHeight; - - Parts->Flags = 0; - Parts->RowRect.left = 0; - Parts->RowRect.right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; - Parts->RowRect.top = nodeY; - Parts->RowRect.bottom = nodeY + Context->RowHeight; - - viewWidth = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - - if (Parts->RowRect.right > viewWidth) - Parts->RowRect.right = viewWidth; - - if (!Column) - return TRUE; - if (!Column->Visible) - return FALSE; - - iconVerticalMargin = (Context->RowHeight - SmallIconHeight) / 2; - - if (Column->Fixed) - { - currentX = 0; - } - else - { - currentX = Context->NormalLeft + Column->s.ViewX - Context->HScrollPosition; - } - - Parts->Flags |= TN_PART_CELL; - Parts->CellRect.left = currentX; - Parts->CellRect.right = currentX + Column->Width; - Parts->CellRect.top = Parts->RowRect.top; - Parts->CellRect.bottom = Parts->RowRect.bottom; - - currentX += TNP_CELL_LEFT_MARGIN; - - if (Column == Context->FirstColumn) - { - currentX += (LONG)node->Level * SmallIconWidth; - - if (Context->CanAnyExpand) - { - if (!node->s.IsLeaf) - { - Parts->Flags |= TN_PART_PLUSMINUS; - Parts->PlusMinusRect.left = currentX; - Parts->PlusMinusRect.right = currentX + SmallIconWidth; - Parts->PlusMinusRect.top = Parts->RowRect.top + iconVerticalMargin; - Parts->PlusMinusRect.bottom = Parts->RowRect.bottom - iconVerticalMargin; - } - - currentX += SmallIconWidth; - } - - if (node->Icon) - { - Parts->Flags |= TN_PART_ICON; - Parts->IconRect.left = currentX; - Parts->IconRect.right = currentX + SmallIconWidth; - Parts->IconRect.top = Parts->RowRect.top + iconVerticalMargin; - Parts->IconRect.bottom = Parts->RowRect.bottom - iconVerticalMargin; - - currentX += SmallIconWidth + TNP_ICON_RIGHT_PADDING; - } - } - - Parts->Flags |= TN_PART_CONTENT; - Parts->ContentRect.left = currentX; - Parts->ContentRect.right = Parts->CellRect.right - TNP_CELL_RIGHT_MARGIN; - Parts->ContentRect.top = Parts->RowRect.top; - Parts->ContentRect.bottom = Parts->RowRect.bottom; - - if (Flags & TN_MEASURE_TEXT) - { - HDC hdc; - PH_STRINGREF text; - HFONT font; - SIZE textSize; - - if (hdc = GetDC(Context->Handle)) - { - PhTnpPrepareRowForDraw(Context, hdc, node); - - if (PhTnpGetCellText(Context, node, Column->Id, &text)) - { - if (node->Font) - font = node->Font; - else - font = Context->Font; - - SelectObject(hdc, font); - - if (GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textSize)) - { - Parts->Flags |= TN_PART_TEXT; - Parts->TextRect.left = currentX; - Parts->TextRect.right = currentX + textSize.cx; - Parts->TextRect.top = Parts->RowRect.top + (Context->RowHeight - textSize.cy) / 2; - Parts->TextRect.bottom = Parts->RowRect.bottom - (Context->RowHeight - textSize.cy) / 2; - - if (Column->TextFlags & DT_CENTER) - { - Parts->TextRect.left = Parts->ContentRect.left / 2 + (Parts->ContentRect.right - textSize.cx) / 2; - Parts->TextRect.right = Parts->ContentRect.left + textSize.cx; - } - else if (Column->TextFlags & DT_RIGHT) - { - Parts->TextRect.right = Parts->ContentRect.right; - Parts->TextRect.left = Parts->TextRect.right - textSize.cx; - } - - Parts->Text = text; - Parts->Font = font; - } - } - - ReleaseDC(Context->Handle, hdc); - } - } - - return TRUE; -} - -BOOLEAN PhTnpGetRowRects( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Start, - _In_ ULONG End, - _In_ BOOLEAN Clip, - _Out_ PRECT Rect - ) -{ - LONG startY; - LONG endY; - LONG viewWidth; - - if (End >= Context->FlatList->Count) - return FALSE; - if (Start > End) - return FALSE; - - startY = Context->HeaderHeight + ((LONG)Start - Context->VScrollPosition) * Context->RowHeight; - endY = Context->HeaderHeight + ((LONG)End - Context->VScrollPosition) * Context->RowHeight; - - Rect->left = 0; - Rect->right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; - Rect->top = startY; - Rect->bottom = endY + Context->RowHeight; - - viewWidth = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - - if (Rect->right > viewWidth) - Rect->right = viewWidth; - - if (Clip) - { - if (Rect->top < Context->HeaderHeight) - Rect->top = Context->HeaderHeight; - if (Rect->bottom > Context->ClientRect.bottom) - Rect->bottom = Context->ClientRect.bottom; - } - - return TRUE; -} - -VOID PhTnpHitTest( - _In_ PPH_TREENEW_CONTEXT Context, - _Inout_ PPH_TREENEW_HIT_TEST HitTest - ) -{ - RECT clientRect; - LONG x; - LONG y; - ULONG index; - PPH_TREENEW_NODE node; - - HitTest->Flags = 0; - HitTest->Node = NULL; - HitTest->Column = NULL; - - clientRect = Context->ClientRect; - x = HitTest->Point.x; - y = HitTest->Point.y; - - if (x < 0) - HitTest->Flags |= TN_HIT_LEFT; - if (x >= clientRect.right) - HitTest->Flags |= TN_HIT_RIGHT; - if (y < 0) - HitTest->Flags |= TN_HIT_ABOVE; - if (y >= clientRect.bottom) - HitTest->Flags |= TN_HIT_BELOW; - - if (HitTest->Flags == 0) - { - if (TNP_HIT_TEST_FIXED_DIVIDER(x, Context)) - { - HitTest->Flags |= TN_HIT_DIVIDER; - } - - if (y >= Context->HeaderHeight && x < Context->FixedWidth + Context->TotalViewX) - { - index = (y - Context->HeaderHeight) / Context->RowHeight + Context->VScrollPosition; - - if (index < Context->FlatList->Count) - { - HitTest->Flags |= TN_HIT_ITEM; - node = Context->FlatList->Items[index]; - HitTest->Node = node; - - if (HitTest->InFlags & TN_TEST_COLUMN) - { - PPH_TREENEW_COLUMN column; - LONG columnX; - - column = NULL; - - if (x < Context->FixedWidth && Context->FixedColumnVisible) - { - column = Context->FixedColumn; - columnX = 0; - } - else - { - LONG currentX; - ULONG i; - PPH_TREENEW_COLUMN currentColumn; - - currentX = Context->NormalLeft - Context->HScrollPosition; - - for (i = 0; i < Context->NumberOfColumnsByDisplay; i++) - { - currentColumn = Context->ColumnsByDisplay[i]; - - if (x >= currentX && x < currentX + currentColumn->Width) - { - column = currentColumn; - columnX = currentX; - break; - } - - currentX += currentColumn->Width; - } - } - - HitTest->Column = column; - - if (column && (HitTest->InFlags & TN_TEST_SUBITEM)) - { - BOOLEAN isFirstColumn; - LONG currentX; - - isFirstColumn = HitTest->Column == Context->FirstColumn; - - currentX = columnX; - currentX += TNP_CELL_LEFT_MARGIN; - - if (isFirstColumn) - { - currentX += (LONG)node->Level * SmallIconWidth; - - if (!node->s.IsLeaf) - { - if (x >= currentX && x < currentX + SmallIconWidth) - HitTest->Flags |= TN_HIT_ITEM_PLUSMINUS; - - currentX += SmallIconWidth; - } - - if (node->Icon) - { - if (x >= currentX && x < currentX + SmallIconWidth) - HitTest->Flags |= TN_HIT_ITEM_ICON; - - currentX += SmallIconWidth + TNP_ICON_RIGHT_PADDING; - } - } - - if (x >= currentX) - { - HitTest->Flags |= TN_HIT_ITEM_CONTENT; - } - } - } - } - } - } -} - -VOID PhTnpSelectRange( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Start, - _In_ ULONG End, - _In_ ULONG Flags, - _Out_opt_ PULONG ChangedStart, - _Out_opt_ PULONG ChangedEnd - ) -{ - ULONG maximum; - ULONG i; - PPH_TREENEW_NODE node; - BOOLEAN targetValue; - ULONG changedStart; - ULONG changedEnd; - - if (Context->FlatList->Count == 0) - return; - - maximum = Context->FlatList->Count - 1; - - if (End > maximum) - { - End = maximum; - } - - if (Start > End) - { - // Start is too big, so the selection range becomes empty. - // Set it to max + 1 so that Reset still works. - Start = maximum + 1; - End = 0; - } - - targetValue = !(Flags & TN_SELECT_DESELECT); - changedStart = maximum; - changedEnd = 0; - - if (Flags & TN_SELECT_RESET) - { - for (i = 0; i < Start; i++) - { - node = Context->FlatList->Items[i]; - - if (node->Selected) - { - node->Selected = FALSE; - - if (changedStart > i) - changedStart = i; - if (changedEnd < i) - changedEnd = i; - } - } - } - - for (i = Start; i <= End; i++) - { - node = Context->FlatList->Items[i]; - - if (!node->Unselectable && ((Flags & TN_SELECT_TOGGLE) || node->Selected != targetValue)) - { - node->Selected = !node->Selected; - - if (changedStart > i) - changedStart = i; - if (changedEnd < i) - changedEnd = i; - } - } - - if (Flags & TN_SELECT_RESET) - { - for (i = End + 1; i <= maximum; i++) - { - node = Context->FlatList->Items[i]; - - if (node->Selected) - { - node->Selected = FALSE; - - if (changedStart > i) - changedStart = i; - if (changedEnd < i) - changedEnd = i; - } - } - } - - if (changedStart <= changedEnd) - { - Context->Callback(Context->Handle, TreeNewSelectionChanged, NULL, NULL, Context->CallbackContext); - } - - if (ChangedStart) - *ChangedStart = changedStart; - if (ChangedEnd) - *ChangedEnd = changedEnd; -} - -VOID PhTnpSetHotNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_opt_ PPH_TREENEW_NODE NewHotNode, - _In_ BOOLEAN NewPlusMinusHot - ) -{ - ULONG newHotNodeIndex; - RECT rowRect; - BOOLEAN needsInvalidate; - - if (NewHotNode) - newHotNodeIndex = NewHotNode->Index; - else - newHotNodeIndex = -1; - - needsInvalidate = FALSE; - - if (Context->HotNodeIndex != newHotNodeIndex) - { - if (Context->HotNodeIndex != -1) - { - if (Context->ThemeData && PhTnpGetRowRects(Context, Context->HotNodeIndex, Context->HotNodeIndex, TRUE, &rowRect)) - { - // Update the old hot node because it may have a different non-hot background and - // plus minus part. - InvalidateRect(Context->Handle, &rowRect, FALSE); - } - } - - Context->HotNodeIndex = newHotNodeIndex; - - if (NewHotNode) - { - needsInvalidate = TRUE; - } - } - - if (NewHotNode) - { - if (NewHotNode->s.PlusMinusHot != NewPlusMinusHot) - { - NewHotNode->s.PlusMinusHot = NewPlusMinusHot; - needsInvalidate = TRUE; - } - - if (needsInvalidate && Context->ThemeData && PhTnpGetRowRects(Context, newHotNodeIndex, newHotNodeIndex, TRUE, &rowRect)) - { - InvalidateRect(Context->Handle, &rowRect, FALSE); - } - } -} - -VOID PhTnpProcessSelectNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPH_TREENEW_NODE Node, - _In_ LOGICAL ControlKey, - _In_ LOGICAL ShiftKey, - _In_ LOGICAL RightButton - ) -{ - ULONG changedStart; - ULONG changedEnd; - RECT rect; - - if (RightButton) - { - // Right button: - // If the current node is selected, then do nothing. This is to allow context menus to - // operate on multiple items. - // If the current node is not selected, select only that node. - - if (!ControlKey && !ShiftKey && !Node->Selected) - { - PhTnpSelectRange(Context, Node->Index, Node->Index, TN_SELECT_RESET, &changedStart, &changedEnd); - Context->MarkNodeIndex = Node->Index; - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - } - else if (ShiftKey && Context->MarkNodeIndex != -1) - { - ULONG start; - ULONG end; - - // Shift key: select a range from the selection mark node to the current node. - - if (Node->Index > Context->MarkNodeIndex) - { - start = Context->MarkNodeIndex; - end = Node->Index; - } - else - { - start = Node->Index; - end = Context->MarkNodeIndex; - } - - PhTnpSelectRange(Context, start, end, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - else if (ControlKey) - { - // Control key: toggle the selection on the current node, and also make it the selection - // mark. - - PhTnpSelectRange(Context, Node->Index, Node->Index, TN_SELECT_TOGGLE, NULL, NULL); - Context->MarkNodeIndex = Node->Index; - - if (PhTnpGetRowRects(Context, Node->Index, Node->Index, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - else - { - // Normal: select the current node, and also make it the selection mark. - - PhTnpSelectRange(Context, Node->Index, Node->Index, TN_SELECT_RESET, &changedStart, &changedEnd); - Context->MarkNodeIndex = Node->Index; - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } -} - -BOOLEAN PhTnpEnsureVisibleNode( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Index - ) -{ - LONG viewTop; - LONG viewBottom; - LONG rowTop; - LONG rowBottom; - LONG deltaY; - LONG deltaRows; - - if (Index >= Context->FlatList->Count) - return FALSE; - - viewTop = Context->HeaderHeight; - viewBottom = Context->ClientRect.bottom - (Context->HScrollVisible ? Context->HScrollHeight : 0); - rowTop = Context->HeaderHeight + ((LONG)Index - Context->VScrollPosition) * Context->RowHeight; - rowBottom = rowTop + Context->RowHeight; - - // Check if the row is fully visible. - if (rowTop >= viewTop && rowBottom <= viewBottom) - return TRUE; - - deltaY = rowTop - viewTop; - - if (deltaY > 0) - { - // The row is below the view area. We want to scroll the row into view at the bottom of the - // screen. We need to round up when dividing to make sure the node becomes fully visible. - deltaY = rowBottom - viewBottom; - deltaRows = (deltaY + Context->RowHeight - 1) / Context->RowHeight; // divide, round up - } - else - { - deltaRows = deltaY / Context->RowHeight; - } - - PhTnpScroll(Context, deltaRows, 0); - - return TRUE; -} - -VOID PhTnpProcessMoveMouse( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorX, - _In_ LONG CursorY - ) -{ - PH_TREENEW_HIT_TEST hitTest; - PPH_TREENEW_NODE hotNode; - - hitTest.Point.x = CursorX; - hitTest.Point.y = CursorY; - hitTest.InFlags = TN_TEST_COLUMN | TN_TEST_SUBITEM; - PhTnpHitTest(Context, &hitTest); - - if (hitTest.Flags & TN_HIT_ITEM) - hotNode = hitTest.Node; - else - hotNode = NULL; - - PhTnpSetHotNode(Context, hotNode, !!(hitTest.Flags & TN_HIT_ITEM_PLUSMINUS)); - - if (Context->AnimateDivider && Context->FixedDividerVisible) - { - if (hitTest.Flags & TN_HIT_DIVIDER) - { - if ((Context->DividerHot < 100 || Context->AnimateDividerFadingOut) && !Context->AnimateDividerFadingIn) - { - // Begin fading in the divider. - Context->AnimateDividerFadingIn = TRUE; - Context->AnimateDividerFadingOut = FALSE; - SetTimer(Context->Handle, TNP_TIMER_ANIMATE_DIVIDER, TNP_ANIMATE_DIVIDER_INTERVAL, NULL); - } - } - else - { - if ((Context->DividerHot != 0 || Context->AnimateDividerFadingIn) && !Context->AnimateDividerFadingOut) - { - Context->AnimateDividerFadingOut = TRUE; - Context->AnimateDividerFadingIn = FALSE; - SetTimer(Context->Handle, TNP_TIMER_ANIMATE_DIVIDER, TNP_ANIMATE_DIVIDER_INTERVAL, NULL); - } - } - } - - if (Context->TooltipsHandle) - { - ULONG index; - ULONG id; - - if (!(hitTest.Flags & TN_HIT_DIVIDER)) - { - index = hitTest.Node ? hitTest.Node->Index : -1; - id = hitTest.Column ? hitTest.Column->Id : -1; - } - else - { - index = -1; - id = -1; - } - - // This pops unnecessarily - when the cell has no tooltip text, and the user is moving the - // mouse over it. However these unnecessary calls seem to fix a certain tooltip bug (move - // the mouse around very quickly over the last column and the blank space to the right, and - // no more tooltips will appear). - if (Context->TooltipIndex != index || Context->TooltipId != id) - { - PhTnpPopTooltip(Context); - } - } -} - -VOID PhTnpProcessMouseVWheel( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance - ) -{ - ULONG wheelScrollLines; - FLOAT linesToScroll; - LONG wholeLinesToScroll; - SCROLLINFO scrollInfo; - LONG oldPosition; - - if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScrollLines, 0)) - wheelScrollLines = 3; - - // If page scrolling is enabled, use the number of visible rows. - if (wheelScrollLines == -1) - wheelScrollLines = (Context->ClientRect.bottom - Context->HeaderHeight - (Context->HScrollVisible ? Context->HScrollHeight : 0)) / Context->RowHeight; - - // Zero the remainder if the direction changed. - if ((Context->VScrollRemainder > 0) != (Distance > 0)) - Context->VScrollRemainder = 0; - - linesToScroll = (FLOAT)wheelScrollLines * Distance / WHEEL_DELTA + Context->VScrollRemainder; - wholeLinesToScroll = (LONG)linesToScroll; - Context->VScrollRemainder = linesToScroll - wholeLinesToScroll; - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_ALL; - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - scrollInfo.nPos += wholeLinesToScroll; - - scrollInfo.fMask = SIF_POS; - SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - - if (scrollInfo.nPos != oldPosition) - { - Context->VScrollPosition = scrollInfo.nPos; - PhTnpProcessScroll(Context, scrollInfo.nPos - oldPosition, 0); - - if (Context->TooltipsHandle) - { - MSG message; - POINT point; - - PhTnpPopTooltip(Context); - PhTnpGetMessagePos(Context->Handle, &point); - - if (point.x >= 0 && point.y >= 0 && point.x < Context->ClientRect.right && point.y < Context->ClientRect.bottom) - { - // Send a fake mouse move message for the new node that the mouse may be hovering over. - message.hwnd = Context->Handle; - message.message = WM_MOUSEMOVE; - message.wParam = 0; - message.lParam = MAKELPARAM(point.x, point.y); - SendMessage(Context->TooltipsHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); - } - } - } -} - -VOID PhTnpProcessMouseHWheel( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG Distance - ) -{ - ULONG wheelScrollChars; - FLOAT pixelsToScroll; - LONG wholePixelsToScroll; - SCROLLINFO scrollInfo; - LONG oldPosition; - - if (!SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &wheelScrollChars, 0)) - wheelScrollChars = 3; - - // Zero the remainder if the direction changed. - if ((Context->HScrollRemainder > 0) != (Distance > 0)) - Context->HScrollRemainder = 0; - - pixelsToScroll = (FLOAT)wheelScrollChars * Context->TextMetrics.tmAveCharWidth * Distance / WHEEL_DELTA + Context->HScrollRemainder; - wholePixelsToScroll = (LONG)pixelsToScroll; - Context->HScrollRemainder = pixelsToScroll - wholePixelsToScroll; - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_ALL; - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - scrollInfo.nPos += wholePixelsToScroll; - - scrollInfo.fMask = SIF_POS; - SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - - if (scrollInfo.nPos != oldPosition) - { - Context->HScrollPosition = scrollInfo.nPos; - PhTnpProcessScroll(Context, 0, scrollInfo.nPos - oldPosition); - } -} - -BOOLEAN PhTnpProcessFocusKey( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey - ) -{ - ULONG count; - ULONG index; - BOOLEAN controlKey; - BOOLEAN shiftKey; - ULONG start; - ULONG end; - ULONG changedStart; - ULONG changedEnd; - RECT rect; - - if (VirtualKey != VK_UP && VirtualKey != VK_DOWN && - VirtualKey != VK_HOME && VirtualKey != VK_END && - VirtualKey != VK_PRIOR && VirtualKey != VK_NEXT) - { - return FALSE; - } - - count = Context->FlatList->Count; - - if (count == 0) - return TRUE; - - // Find the new node to focus. - - switch (VirtualKey) - { - case VK_UP: - { - if (Context->FocusNode && Context->FocusNode->Index > 0) - { - index = Context->FocusNode->Index - 1; - } - else - { - index = 0; - } - } - break; - case VK_DOWN: - { - if (Context->FocusNode) - { - index = Context->FocusNode->Index + 1; - - if (index >= count) - index = count - 1; - } - else - { - index = 0; - } - } - break; - case VK_HOME: - index = 0; - break; - case VK_END: - index = count - 1; - break; - case VK_PRIOR: - case VK_NEXT: - { - LONG rowsPerPage; - - if (Context->FocusNode) - index = Context->FocusNode->Index; - else - index = 0; - - rowsPerPage = Context->ClientRect.bottom - Context->HeaderHeight - (Context->HScrollVisible ? Context->HScrollHeight : 0); - - if (rowsPerPage < 0) - return TRUE; - - rowsPerPage = rowsPerPage / Context->RowHeight - 1; - - if (rowsPerPage < 0) - return TRUE; - - if (VirtualKey == VK_PRIOR) - { - ULONG startOfPageIndex; - - startOfPageIndex = Context->VScrollPosition; - - if (index > startOfPageIndex) - { - index = startOfPageIndex; - } - else - { - // Already at or before the start of the page. Go back a page. - if (index >= (ULONG)rowsPerPage) - index -= rowsPerPage; - else - index = 0; - } - } - else - { - ULONG endOfPageIndex; - - endOfPageIndex = Context->VScrollPosition + rowsPerPage; - - if (endOfPageIndex >= count) - endOfPageIndex = count - 1; - - if (index < endOfPageIndex) - { - index = endOfPageIndex; - } - else - { - // Already at or after the end of the page. Go forward a page. - index += rowsPerPage; - - if (index >= count) - index = count - 1; - } - } - } - break; - } - - // Select the relevant nodes. - - controlKey = GetKeyState(VK_CONTROL) < 0; - shiftKey = GetKeyState(VK_SHIFT) < 0; - - Context->FocusNode = Context->FlatList->Items[index]; - PhTnpSetHotNode(Context, Context->FocusNode, FALSE); - - if (shiftKey && Context->MarkNodeIndex != -1) - { - if (index > Context->MarkNodeIndex) - { - start = Context->MarkNodeIndex; - end = index; - } - else - { - start = index; - end = Context->MarkNodeIndex; - } - - PhTnpSelectRange(Context, start, end, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - else if (!controlKey) - { - Context->MarkNodeIndex = Context->FocusNode->Index; - PhTnpSelectRange(Context, index, index, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - - PhTnpEnsureVisibleNode(Context, index); - PhTnpPopTooltip(Context); - - return TRUE; -} - -BOOLEAN PhTnpProcessNodeKey( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKey - ) -{ - BOOLEAN controlKey; - BOOLEAN shiftKey; - ULONG changedStart; - ULONG changedEnd; - RECT rect; - - if (VirtualKey != VK_SPACE && VirtualKey != VK_LEFT && VirtualKey != VK_RIGHT && VirtualKey != VK_ADD && VirtualKey != VK_OEM_PLUS) - { - return FALSE; - } - - if (!Context->FocusNode) - return TRUE; - - controlKey = GetKeyState(VK_CONTROL) < 0; - shiftKey = GetKeyState(VK_SHIFT) < 0; - - switch (VirtualKey) - { - case VK_SPACE: - { - if (controlKey) - { - // Control key: toggle the selection on the focused node. - - Context->MarkNodeIndex = Context->FocusNode->Index; - PhTnpSelectRange(Context, Context->FocusNode->Index, Context->FocusNode->Index, TN_SELECT_TOGGLE, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - else if (shiftKey) - { - ULONG start; - ULONG end; - - // Shift key: select a range from the selection mark node to the focused node. - - if (Context->FocusNode->Index > Context->MarkNodeIndex) - { - start = Context->MarkNodeIndex; - end = Context->FocusNode->Index; - } - else - { - start = Context->FocusNode->Index; - end = Context->MarkNodeIndex; - } - - PhTnpSelectRange(Context, start, end, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - } - } - break; - case VK_LEFT: - { - ULONG i; - ULONG targetLevel; - PPH_TREENEW_NODE newNode; - - // If the node is expanded, collapse it. Otherwise, select the node's parent. - if (!Context->FocusNode->s.IsLeaf && Context->FocusNode->Expanded) - { - PhTnpSetExpandedNode(Context, Context->FocusNode, FALSE); - } - else if (Context->FocusNode->Level != 0) - { - i = Context->FocusNode->Index; - targetLevel = Context->FocusNode->Level - 1; - - while (i != 0) - { - i--; - newNode = Context->FlatList->Items[i]; - - if (newNode->Level == targetLevel) - { - Context->FocusNode = newNode; - Context->MarkNodeIndex = newNode->Index; - PhTnpEnsureVisibleNode(Context, i); - PhTnpSetHotNode(Context, newNode, FALSE); - PhTnpSelectRange(Context, i, i, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - - PhTnpPopTooltip(Context); - - break; - } - } - } - } - break; - case VK_RIGHT: - { - PPH_TREENEW_NODE newNode; - - if (!Context->FocusNode->s.IsLeaf) - { - // If the node is collapsed, expand it. Otherwise, select the node's first child. - if (!Context->FocusNode->Expanded) - { - PhTnpSetExpandedNode(Context, Context->FocusNode, TRUE); - } - else - { - if (Context->FocusNode->Index + 1 < Context->FlatList->Count) - { - newNode = Context->FlatList->Items[Context->FocusNode->Index + 1]; - - if (newNode->Level == Context->FocusNode->Level + 1) - { - Context->FocusNode = newNode; - Context->MarkNodeIndex = newNode->Index; - PhTnpEnsureVisibleNode(Context, Context->FocusNode->Index + 1); - PhTnpSetHotNode(Context, newNode, FALSE); - PhTnpSelectRange(Context, Context->FocusNode->Index, Context->FocusNode->Index, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - - PhTnpPopTooltip(Context); - } - } - } - } - } - break; - case VK_ADD: - case VK_OEM_PLUS: - { - if ((VirtualKey == VK_ADD && controlKey) || - (VirtualKey == VK_OEM_PLUS && controlKey && shiftKey)) - { - ULONG i; - - if (Context->FixedColumnVisible) - PhTnpAutoSizeColumnHeader(Context, Context->FixedHeaderHandle, Context->FixedColumn, 0); - - for (i = 0; i < Context->NumberOfColumnsByDisplay; i++) - PhTnpAutoSizeColumnHeader(Context, Context->HeaderHandle, Context->ColumnsByDisplay[i], 0); - } - } - break; - } - - return TRUE; -} - -VOID PhTnpProcessSearchKey( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG Character - ) -{ - LONG messageTime; - BOOLEAN newSearch; - PH_TREENEW_SEARCH_EVENT searchEvent; - PPH_TREENEW_NODE foundNode; - ULONG changedStart; - ULONG changedEnd; - RECT rect; - - if (Context->FlatList->Count == 0) - return; - - messageTime = GetMessageTime(); - newSearch = FALSE; - - // Check if the search timed out. - if (messageTime - Context->SearchMessageTime > PH_TREENEW_SEARCH_TIMEOUT) - { - Context->SearchStringCount = 0; - Context->SearchFailed = FALSE; - newSearch = TRUE; - Context->SearchSingleCharMode = TRUE; - } - - Context->SearchMessageTime = messageTime; - - // Append the character to the search buffer. - - if (!Context->SearchString) - { - Context->AllocatedSearchString = 32; - Context->SearchString = PhAllocate(Context->AllocatedSearchString * sizeof(WCHAR)); - newSearch = TRUE; - Context->SearchSingleCharMode = TRUE; - } - - if (Context->SearchStringCount > PH_TREENEW_SEARCH_MAXIMUM_LENGTH) - { - // The search string has become too long. Fail the search. - if (!Context->SearchFailed) - { - MessageBeep(0); - Context->SearchFailed = TRUE; - return; - } - } - else if (Context->SearchStringCount == Context->AllocatedSearchString) - { - Context->AllocatedSearchString *= 2; - Context->SearchString = PhReAllocate(Context->SearchString, Context->AllocatedSearchString * sizeof(WCHAR)); - } - - Context->SearchString[Context->SearchStringCount++] = (WCHAR)Character; - - if (Context->SearchString[Context->SearchStringCount - 1] != Context->SearchString[0]) - { - // The user has stopped typing the same character (or never started). Turn single-character - // search off. - Context->SearchSingleCharMode = FALSE; - } - - searchEvent.FoundIndex = -1; - - if (Context->FocusNode) - { - searchEvent.StartIndex = Context->FocusNode->Index; - - if (newSearch || Context->SearchSingleCharMode) - { - // If it's a new search, start at the next item so the user doesn't find the same item - // again. - searchEvent.StartIndex++; - - if (searchEvent.StartIndex == Context->FlatList->Count) - searchEvent.StartIndex = 0; - } - } - else - { - searchEvent.StartIndex = 0; - } - - searchEvent.String.Buffer = Context->SearchString; - - if (!Context->SearchSingleCharMode) - searchEvent.String.Length = Context->SearchStringCount * sizeof(WCHAR); - else - searchEvent.String.Length = sizeof(WCHAR); - - // Give the user a chance to modify how the search is performed. - if (!Context->Callback(Context->Handle, TreeNewIncrementalSearch, &searchEvent, NULL, Context->CallbackContext)) - { - // Use the default search function. - if (!PhTnpDefaultIncrementalSearch(Context, &searchEvent, TRUE, TRUE)) - { - return; - } - } - - if (searchEvent.FoundIndex == -1 && !Context->SearchFailed) - { - // No search result. Beep to indicate an error, and set the flag so we don't beep again. But - // don't beep if the first character was a space, because that's used for other purposes - // elsewhere (see PhTnpProcessNodeKey). - if (searchEvent.String.Buffer[0] != ' ') - { - MessageBeep(0); - } - - Context->SearchFailed = TRUE; - return; - } - - if (searchEvent.FoundIndex < 0 || searchEvent.FoundIndex >= (LONG)Context->FlatList->Count) - return; - - foundNode = Context->FlatList->Items[searchEvent.FoundIndex]; - Context->FocusNode = foundNode; - PhTnpEnsureVisibleNode(Context, searchEvent.FoundIndex); - PhTnpSetHotNode(Context, foundNode, FALSE); - PhTnpSelectRange(Context, searchEvent.FoundIndex, searchEvent.FoundIndex, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - - PhTnpPopTooltip(Context); -} - -BOOLEAN PhTnpDefaultIncrementalSearch( - _In_ PPH_TREENEW_CONTEXT Context, - _Inout_ PPH_TREENEW_SEARCH_EVENT SearchEvent, - _In_ BOOLEAN Partial, - _In_ BOOLEAN Wrap - ) -{ - LONG startIndex; - LONG currentIndex; - LONG foundIndex; - BOOLEAN firstTime; - - if (Context->FlatList->Count == 0) - return FALSE; - if (!Context->FirstColumn) - return FALSE; - - startIndex = SearchEvent->StartIndex; - currentIndex = startIndex; - foundIndex = -1; - firstTime = TRUE; - - while (TRUE) - { - PH_STRINGREF text; - - if (currentIndex >= (LONG)Context->FlatList->Count) - { - if (Wrap) - currentIndex = 0; - else - break; - } - - // We use the firstTime variable instead of a simpler check because we want to include the - // current item in the search. E.g. the current item is the only item beginning with "Z". If - // the user searches for "Z", we want to return the current item as being found. - if (!firstTime && currentIndex == startIndex) - break; - - if (PhTnpGetCellText(Context, Context->FlatList->Items[currentIndex], Context->FirstColumn->Id, &text)) - { - if (Partial) - { - if (PhStartsWithStringRef(&text, &SearchEvent->String, TRUE)) - { - foundIndex = currentIndex; - break; - } - } - else - { - if (PhEqualStringRef(&text, &SearchEvent->String, TRUE)) - { - foundIndex = currentIndex; - break; - } - } - } - - currentIndex++; - firstTime = FALSE; - } - - SearchEvent->FoundIndex = foundIndex; - - return TRUE; -} - -VOID PhTnpUpdateScrollBars( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - RECT clientRect; - LONG width; - LONG height; - LONG contentWidth; - LONG contentHeight; - SCROLLINFO scrollInfo; - LONG oldPosition; - LONG deltaRows; - LONG deltaX; - LOGICAL oldHScrollVisible; - RECT rect; - - clientRect = Context->ClientRect; - width = clientRect.right - Context->FixedWidth; - height = clientRect.bottom - Context->HeaderHeight; - - contentWidth = Context->TotalViewX; - contentHeight = (LONG)Context->FlatList->Count * Context->RowHeight; - - if (contentHeight > height) - { - // We need a vertical scrollbar, so we can't use that area of the screen for content. - width -= Context->VScrollWidth; - } - - if (contentWidth > width) - { - height -= Context->HScrollHeight; - } - - deltaRows = 0; - deltaX = 0; - - // Vertical scroll bar - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_POS; - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - scrollInfo.fMask = SIF_RANGE | SIF_PAGE; - scrollInfo.nMin = 0; - scrollInfo.nMax = Context->FlatList->Count != 0 ? Context->FlatList->Count - 1 : 0; - scrollInfo.nPage = height / Context->RowHeight; - SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); - - // The scroll position may have changed due to the modified scroll range. - scrollInfo.fMask = SIF_POS; - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - deltaRows = scrollInfo.nPos - oldPosition; - Context->VScrollPosition = scrollInfo.nPos; - - if (contentHeight > height && contentHeight != 0) - { - ShowWindow(Context->VScrollHandle, SW_SHOW); - Context->VScrollVisible = TRUE; - } - else - { - ShowWindow(Context->VScrollHandle, SW_HIDE); - Context->VScrollVisible = FALSE; - } - - // Horizontal scroll bar - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_POS; - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - scrollInfo.fMask = SIF_RANGE | SIF_PAGE; - scrollInfo.nMin = 0; - scrollInfo.nMax = contentWidth != 0 ? contentWidth - 1 : 0; - scrollInfo.nPage = width; - SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); - - scrollInfo.fMask = SIF_POS; - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - deltaX = scrollInfo.nPos - oldPosition; - Context->HScrollPosition = scrollInfo.nPos; - - oldHScrollVisible = Context->HScrollVisible; - - if (contentWidth > width && contentWidth != 0) - { - ShowWindow(Context->HScrollHandle, SW_SHOW); - Context->HScrollVisible = TRUE; - } - else - { - ShowWindow(Context->HScrollHandle, SW_HIDE); - Context->HScrollVisible = FALSE; - } - - if ((Context->HScrollVisible != oldHScrollVisible) && Context->FixedDividerVisible && Context->AnimateDivider) - { - rect.left = Context->FixedWidth; - rect.top = Context->HeaderHeight; - rect.right = Context->FixedWidth + 1; - rect.bottom = Context->ClientRect.bottom; - InvalidateRect(Context->Handle, &rect, FALSE); - } - - if (deltaRows != 0 || deltaX != 0) - PhTnpProcessScroll(Context, deltaRows, deltaX); - - ShowWindow(Context->FillerBoxHandle, (Context->VScrollVisible && Context->HScrollVisible) ? SW_SHOW : SW_HIDE); -} - -VOID PhTnpScroll( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG DeltaRows, - _In_ LONG DeltaX - ) -{ - SCROLLINFO scrollInfo; - LONG oldPosition; - LONG deltaRows; - LONG deltaX; - - deltaRows = 0; - deltaX = 0; - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_POS; - - if (DeltaRows != 0 && Context->VScrollVisible) - { - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - if (DeltaRows == MINLONG) - scrollInfo.nPos = 0; - else if (DeltaRows == MAXLONG) - scrollInfo.nPos = Context->FlatList->Count - 1; - else - scrollInfo.nPos += DeltaRows; - - SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - Context->VScrollPosition = scrollInfo.nPos; - deltaRows = scrollInfo.nPos - oldPosition; - } - - if (DeltaX != 0 && Context->HScrollVisible) - { - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - oldPosition = scrollInfo.nPos; - - if (DeltaX == MINLONG) - scrollInfo.nPos = 0; - else if (DeltaX == MAXLONG) - scrollInfo.nPos = Context->TotalViewX; - else - scrollInfo.nPos += DeltaX; - - SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - Context->HScrollPosition = scrollInfo.nPos; - deltaX = scrollInfo.nPos - oldPosition; - } - - if (deltaRows != 0 || deltaX != 0) - PhTnpProcessScroll(Context, deltaRows, deltaX); -} - -VOID PhTnpProcessScroll( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG DeltaRows, - _In_ LONG DeltaX - ) -{ - RECT rect; - LONG deltaY; - - rect.top = Context->HeaderHeight; - rect.bottom = Context->ClientRect.bottom; - - if (DeltaX == 0) - { - rect.left = 0; - rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - ScrollWindowEx( - Context->Handle, - 0, - -DeltaRows * Context->RowHeight, - &rect, - NULL, - NULL, - NULL, - SW_INVALIDATE - ); - } - else - { - // Don't scroll if there are no rows. This is especially important if the user wants us to - // display empty text. - if (Context->FlatList->Count != 0) - { - deltaY = DeltaRows * Context->RowHeight; - - // If we're scrolling vertically as well, we need to scroll the fixed part and the - // normal part separately. - - if (DeltaRows != 0) - { - rect.left = 0; - rect.right = Context->NormalLeft; - ScrollWindowEx( - Context->Handle, - 0, - -deltaY, - &rect, - &rect, - NULL, - NULL, - SW_INVALIDATE - ); - } - - rect.left = Context->NormalLeft; - rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - ScrollWindowEx( - Context->Handle, - -DeltaX, - -deltaY, - &rect, - &rect, - NULL, - NULL, - SW_INVALIDATE - ); - } - - PhTnpLayoutHeader(Context); - } -} - -BOOLEAN PhTnpCanScroll( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Horizontal, - _In_ BOOLEAN Positive - ) -{ - SCROLLINFO scrollInfo; - - scrollInfo.cbSize = sizeof(SCROLLINFO); - scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - - if (!Horizontal) - GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); - else - GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); - - if (Positive) - { - if (scrollInfo.nPage != 0) - scrollInfo.nMax -= scrollInfo.nPage - 1; - - return scrollInfo.nPos < scrollInfo.nMax; - } - else - { - return scrollInfo.nPos > scrollInfo.nMin; - } -} - -VOID PhTnpPaint( - _In_ HWND hwnd, - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ PRECT PaintRect - ) -{ - RECT viewRect; - LONG vScrollPosition; - LONG hScrollPosition; - LONG firstRowToUpdate; - LONG lastRowToUpdate; - LONG i; - LONG j; - PPH_TREENEW_NODE node; - PPH_TREENEW_COLUMN column; - RECT rowRect; - LONG x; - BOOLEAN fixedUpdate; - LONG normalUpdateLeftX; - LONG normalUpdateRightX; - LONG normalUpdateLeftIndex; - LONG normalUpdateRightIndex; - LONG normalTotalX; - RECT cellRect; - HBRUSH backBrush; - HRGN oldClipRegion; - - PhTnpInitializeThemeData(Context); - - viewRect = Context->ClientRect; - - if (Context->VScrollVisible) - viewRect.right -= Context->VScrollWidth; - - vScrollPosition = Context->VScrollPosition; - hScrollPosition = Context->HScrollPosition; - - // Calculate the indicies of the first and last rows that need painting. These indicies are - // relative to the top of the view area. - - firstRowToUpdate = (PaintRect->top - Context->HeaderHeight) / Context->RowHeight; - lastRowToUpdate = (PaintRect->bottom - 1 - Context->HeaderHeight) / Context->RowHeight; // minus one since bottom is exclusive - - if (firstRowToUpdate < 0) - firstRowToUpdate = 0; - - rowRect.left = 0; - rowRect.top = Context->HeaderHeight + firstRowToUpdate * Context->RowHeight; - rowRect.right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; - rowRect.bottom = rowRect.top + Context->RowHeight; - - // Change the indicies to absolute row indicies. - - firstRowToUpdate += vScrollPosition; - lastRowToUpdate += vScrollPosition; - - if (lastRowToUpdate >= (LONG)Context->FlatList->Count) - lastRowToUpdate = Context->FlatList->Count - 1; // becomes -1 when there are no items, handled correctly by loop below - - // Determine whether the fixed column needs painting, and which normal columns need painting. - - fixedUpdate = FALSE; - - if (Context->FixedColumnVisible && PaintRect->left < Context->FixedWidth) - fixedUpdate = TRUE; - - x = Context->NormalLeft - hScrollPosition; - normalUpdateLeftX = viewRect.right; - normalUpdateLeftIndex = 0; - normalUpdateRightX = 0; - normalUpdateRightIndex = -1; - - for (j = 0; j < (LONG)Context->NumberOfColumnsByDisplay; j++) - { - column = Context->ColumnsByDisplay[j]; - - if (x + column->Width >= Context->NormalLeft && x + column->Width > PaintRect->left && x < PaintRect->right) - { - if (normalUpdateLeftX > x) - { - normalUpdateLeftX = x; - normalUpdateLeftIndex = j; - } - - if (normalUpdateRightX < x + column->Width) - { - normalUpdateRightX = x + column->Width; - normalUpdateRightIndex = j; - } - } - - x += column->Width; - } - - normalTotalX = x; - - if (normalUpdateRightIndex >= (LONG)Context->NumberOfColumnsByDisplay) - normalUpdateRightIndex = Context->NumberOfColumnsByDisplay - 1; - - // Paint the rows. - - SelectObject(hdc, Context->Font); - SetBkMode(hdc, TRANSPARENT); - - for (i = firstRowToUpdate; i <= lastRowToUpdate; i++) - { - node = Context->FlatList->Items[i]; - - // Prepare the row for drawing. - - PhTnpPrepareRowForDraw(Context, hdc, node); - - if (node->Selected && !Context->ThemeHasItemBackground) - { - // Non-themed background - if (Context->HasFocus) - { - SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - backBrush = GetSysColorBrush(COLOR_HIGHLIGHT); - } - else - { - SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); - backBrush = GetSysColorBrush(COLOR_BTNFACE); - } - } - else - { - SetTextColor(hdc, node->s.DrawForeColor); - SetDCBrushColor(hdc, node->s.DrawBackColor); - backBrush = GetStockObject(DC_BRUSH); - } - - FillRect(hdc, &rowRect, backBrush); - - if (Context->ThemeHasItemBackground) - { - INT stateId; - - // Themed background - - if (node->Selected) - { - if (i == Context->HotNodeIndex) - stateId = TREIS_HOTSELECTED; - else if (!Context->HasFocus) - stateId = TREIS_SELECTEDNOTFOCUS; - else - stateId = TREIS_SELECTED; - } - else - { - if (i == Context->HotNodeIndex) - stateId = TREIS_HOT; - else - stateId = -1; - } - - if (stateId != -1) - { - if (!Context->FixedColumnVisible) - { - rowRect.left = Context->NormalLeft - hScrollPosition; - } - - DrawThemeBackground( - Context->ThemeData, - hdc, - TVP_TREEITEM, - stateId, - &rowRect, - PaintRect - ); - } - } - - // Paint the fixed column. - - cellRect.top = rowRect.top; - cellRect.bottom = rowRect.bottom; - - if (fixedUpdate) - { - cellRect.left = 0; - cellRect.right = Context->FixedWidth; - PhTnpDrawCell(Context, hdc, &cellRect, node, Context->FixedColumn, i, -1); - } - - // Paint the normal columns. - - if (normalUpdateLeftX < normalUpdateRightX) - { - cellRect.left = normalUpdateLeftX; - cellRect.right = cellRect.left; - - oldClipRegion = CreateRectRgn(0, 0, 0, 0); - - if (GetClipRgn(hdc, oldClipRegion) != 1) - { - DeleteObject(oldClipRegion); - oldClipRegion = NULL; - } - - IntersectClipRect(hdc, Context->NormalLeft, cellRect.top, viewRect.right, cellRect.bottom); - - for (j = normalUpdateLeftIndex; j <= normalUpdateRightIndex; j++) - { - column = Context->ColumnsByDisplay[j]; - - cellRect.left = cellRect.right; - cellRect.right = cellRect.left + column->Width; - PhTnpDrawCell(Context, hdc, &cellRect, node, column, i, j); - } - - SelectClipRgn(hdc, oldClipRegion); - - if (oldClipRegion) - { - DeleteObject(oldClipRegion); - } - } - - rowRect.top += Context->RowHeight; - rowRect.bottom += Context->RowHeight; - } - - if (lastRowToUpdate == Context->FlatList->Count - 1) // works even if there are no items - { - // Fill the rest of the space on the bottom with the window color. - rowRect.bottom = viewRect.bottom; - FillRect(hdc, &rowRect, GetSysColorBrush(COLOR_WINDOW)); - } - - if (normalTotalX < viewRect.right && viewRect.right > PaintRect->left && normalTotalX < PaintRect->right) - { - // Fill the rest of the space on the right with the window color. - rowRect.left = normalTotalX; - rowRect.top = Context->HeaderHeight; - rowRect.right = viewRect.right; - rowRect.bottom = viewRect.bottom; - FillRect(hdc, &rowRect, GetSysColorBrush(COLOR_WINDOW)); - } - - if (Context->FlatList->Count == 0 && Context->EmptyText.Length != 0) - { - RECT textRect; - - textRect.left = 20; - textRect.top = Context->HeaderHeight + 10; - textRect.right = viewRect.right - 20; - textRect.bottom = viewRect.bottom - 5; - - SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); - DrawText( - hdc, - Context->EmptyText.Buffer, - (ULONG)Context->EmptyText.Length / 2, - &textRect, - DT_NOPREFIX | DT_CENTER | DT_END_ELLIPSIS - ); - } - - if (Context->FixedDividerVisible && Context->FixedWidth >= PaintRect->left && Context->FixedWidth < PaintRect->right) - { - PhTnpDrawDivider(Context, hdc); - } - - if (Context->DragSelectionActive) - { - PhTnpDrawSelectionRectangle(Context, hdc, &Context->DragRect); - } -} - -VOID PhTnpPrepareRowForDraw( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _Inout_ PPH_TREENEW_NODE Node - ) -{ - if (!Node->s.CachedColorValid) - { - PH_TREENEW_GET_NODE_COLOR getNodeColor; - - getNodeColor.Flags = 0; - getNodeColor.Node = Node; - getNodeColor.BackColor = RGB(0xff, 0xff, 0xff); - getNodeColor.ForeColor = RGB(0x00, 0x00, 0x00); - - if (Context->Callback( - Context->Handle, - TreeNewGetNodeColor, - &getNodeColor, - NULL, - Context->CallbackContext - )) - { - Node->BackColor = getNodeColor.BackColor; - Node->ForeColor = getNodeColor.ForeColor; - Node->UseAutoForeColor = !!(getNodeColor.Flags & TN_AUTO_FORECOLOR); - - if (getNodeColor.Flags & TN_CACHE) - Node->s.CachedColorValid = TRUE; - } - else - { - Node->BackColor = getNodeColor.BackColor; - Node->ForeColor = getNodeColor.ForeColor; - } - } - - Node->s.DrawForeColor = Node->ForeColor; - - if (Node->UseTempBackColor) - Node->s.DrawBackColor = Node->TempBackColor; - else - Node->s.DrawBackColor = Node->BackColor; - - if (!Node->s.CachedFontValid) - { - PH_TREENEW_GET_NODE_FONT getNodeFont; - - getNodeFont.Flags = 0; - getNodeFont.Node = Node; - getNodeFont.Font = NULL; - - if (Context->Callback( - Context->Handle, - TreeNewGetNodeFont, - &getNodeFont, - NULL, - Context->CallbackContext - )) - { - Node->Font = getNodeFont.Font; - - if (getNodeFont.Flags & TN_CACHE) - Node->s.CachedFontValid = TRUE; - } - else - { - Node->Font = NULL; - } - } - - if (!Node->s.CachedIconValid) - { - PH_TREENEW_GET_NODE_ICON getNodeIcon; - - getNodeIcon.Flags = 0; - getNodeIcon.Node = Node; - getNodeIcon.Icon = NULL; - - if (Context->Callback( - Context->Handle, - TreeNewGetNodeIcon, - &getNodeIcon, - NULL, - Context->CallbackContext - )) - { - Node->Icon = getNodeIcon.Icon; - - if (getNodeIcon.Flags & TN_CACHE) - Node->s.CachedIconValid = TRUE; - } - else - { - Node->Icon = NULL; - } - } - - if (Node->UseAutoForeColor || Node->UseTempBackColor) - { - if (PhGetColorBrightness(Node->s.DrawBackColor) > 100) // slightly less than half - Node->s.DrawForeColor = RGB(0x00, 0x00, 0x00); - else - Node->s.DrawForeColor = RGB(0xff, 0xff, 0xff); - } -} - -VOID PhTnpDrawCell( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ PRECT CellRect, - _In_ PPH_TREENEW_NODE Node, - _In_ PPH_TREENEW_COLUMN Column, - _In_ LONG RowIndex, - _In_ LONG ColumnIndex - ) -{ - HFONT font; // font to use - HFONT oldFont; - PH_STRINGREF text; // text to draw - RECT textRect; // working rectangle, modified as needed - ULONG textFlags; // DT_* flags - LONG iconVerticalMargin; // top/bottom margin for icons (determined using height of small icon) - - font = Node->Font; - textFlags = Column->TextFlags; - - textRect = *CellRect; - - // Initial margins used by default list view - textRect.left += TNP_CELL_LEFT_MARGIN; - textRect.right -= TNP_CELL_RIGHT_MARGIN; - - // icon margin = (height of row - height of small icon) / 2 - iconVerticalMargin = ((textRect.bottom - textRect.top) - SmallIconHeight) / 2; - - textRect.top += iconVerticalMargin; - textRect.bottom -= iconVerticalMargin; - - if (Column == Context->FirstColumn) - { - BOOLEAN needsClip; - HRGN oldClipRegion; - - textRect.left += Node->Level * SmallIconWidth; - - // The icon may need to be clipped if the column is too small. - needsClip = Column->Width < textRect.left + (Context->CanAnyExpand ? SmallIconWidth : 0) + (Node->Icon ? SmallIconWidth : 0); - - if (needsClip) - { - oldClipRegion = CreateRectRgn(0, 0, 0, 0); - - if (GetClipRgn(hdc, oldClipRegion) != 1) - { - DeleteObject(oldClipRegion); - oldClipRegion = NULL; - } - - // Clip contents to the column. - IntersectClipRect(hdc, CellRect->left, textRect.top, CellRect->right, textRect.bottom); - } - - if (Context->CanAnyExpand) // flag is used so we can avoid indenting when it's a flat list - { - BOOLEAN drewUsingTheme = FALSE; - RECT themeRect; - - if (!Node->s.IsLeaf) - { - // Draw the plus/minus glyph. - - themeRect.left = textRect.left; - themeRect.right = themeRect.left + SmallIconWidth; - themeRect.top = textRect.top; - themeRect.bottom = themeRect.top + SmallIconHeight; - - if (Context->ThemeHasGlyph) - { - INT partId; - INT stateId; - - partId = (RowIndex == Context->HotNodeIndex && Node->s.PlusMinusHot && Context->ThemeHasHotGlyph) ? TVP_HOTGLYPH : TVP_GLYPH; - stateId = Node->Expanded ? GLPS_OPENED : GLPS_CLOSED; - - if (SUCCEEDED(DrawThemeBackground( - Context->ThemeData, - hdc, - partId, - stateId, - &themeRect, - NULL - ))) - drewUsingTheme = TRUE; - } - - if (!drewUsingTheme) - { - ULONG glyphWidth; - ULONG glyphHeight; - RECT glyphRect; - - glyphWidth = SmallIconWidth / 2; - glyphHeight = SmallIconHeight / 2; - - glyphRect.left = textRect.left + (SmallIconWidth - glyphWidth) / 2; - glyphRect.right = glyphRect.left + glyphWidth; - glyphRect.top = textRect.top + (SmallIconHeight - glyphHeight) / 2; - glyphRect.bottom = glyphRect.top + glyphHeight; - - PhTnpDrawPlusMinusGlyph(hdc, &glyphRect, !Node->Expanded); - } - } - - textRect.left += SmallIconWidth; - } - - // Draw the icon. - if (Node->Icon) - { - DrawIconEx( - hdc, - textRect.left, - textRect.top, - Node->Icon, - SmallIconWidth, - SmallIconHeight, - 0, - NULL, - DI_NORMAL - ); - - textRect.left += SmallIconWidth + TNP_ICON_RIGHT_PADDING; - } - - if (needsClip) - { - SelectClipRgn(hdc, oldClipRegion); - - if (oldClipRegion) - DeleteObject(oldClipRegion); - } - - if (textRect.left > textRect.right) - textRect.left = textRect.right; - } - - if (Column->CustomDraw) - { - BOOLEAN result; - PH_TREENEW_CUSTOM_DRAW customDraw; - INT savedDc; - - customDraw.Node = Node; - customDraw.Column = Column; - customDraw.Dc = hdc; - customDraw.CellRect = *CellRect; - customDraw.TextRect = textRect; - - // Fix up the rectangles before giving them to the user. - if (customDraw.CellRect.left > customDraw.CellRect.right) - customDraw.CellRect.left = customDraw.CellRect.right; - if (customDraw.TextRect.left > customDraw.TextRect.right) - customDraw.TextRect.left = customDraw.TextRect.right; - - savedDc = SaveDC(hdc); - result = Context->Callback(Context->Handle, TreeNewCustomDraw, &customDraw, NULL, Context->CallbackContext); - RestoreDC(hdc, savedDc); - - if (result) - return; - } - - if (PhTnpGetCellText(Context, Node, Column->Id, &text)) - { - if (!(textFlags & (DT_PATH_ELLIPSIS | DT_WORD_ELLIPSIS))) - textFlags |= DT_END_ELLIPSIS; - - textFlags |= DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE; - - textRect.top = CellRect->top; - textRect.bottom = CellRect->bottom; - - if (font) - oldFont = SelectObject(hdc, font); - - DrawText( - hdc, - text.Buffer, - (ULONG)text.Length / 2, - &textRect, - textFlags - ); - - if (font) - SelectObject(hdc, oldFont); - } -} - -VOID PhTnpDrawDivider( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc - ) -{ - POINT points[2]; - - if (Context->AnimateDivider) - { - if (Context->DividerHot == 0 && !Context->HScrollVisible) - return; // divider is invisible - - if (Context->DividerHot < 100) - { - BLENDFUNCTION blendFunction; - - // We need to draw and alpha blend the divider. - // We can use the extra column allocated in the buffered context to initially draw the - // divider. - - points[0].x = Context->ClientRect.right; - points[0].y = Context->HeaderHeight; - points[1].x = Context->ClientRect.right; - points[1].y = Context->ClientRect.bottom; - SetDCPenColor(Context->BufferedContext, RGB(0x77, 0x77, 0x77)); - SelectObject(Context->BufferedContext, GetStockObject(DC_PEN)); - Polyline(Context->BufferedContext, points, 2); - - blendFunction.BlendOp = AC_SRC_OVER; - blendFunction.BlendFlags = 0; - blendFunction.AlphaFormat = 0; - - // If the horizontal scroll bar is visible, we need to display a line even if the - // divider is not hot. In this case we increase the base alpha value. - if (!Context->HScrollVisible) - blendFunction.SourceConstantAlpha = (UCHAR)(Context->DividerHot * 255 / 100); - else - blendFunction.SourceConstantAlpha = 55 + (UCHAR)(Context->DividerHot * 2); - - GdiAlphaBlend( - hdc, - Context->FixedWidth, - Context->HeaderHeight, - 1, - Context->ClientRect.bottom - Context->HeaderHeight, - Context->BufferedContext, - Context->ClientRect.right, - Context->HeaderHeight, - 1, - Context->ClientRect.bottom - Context->HeaderHeight, - blendFunction - ); - - return; - } - } - - points[0].x = Context->FixedWidth; - points[0].y = Context->HeaderHeight; - points[1].x = Context->FixedWidth; - points[1].y = Context->ClientRect.bottom; - SetDCPenColor(hdc, RGB(0x77, 0x77, 0x77)); - SelectObject(hdc, GetStockObject(DC_PEN)); - Polyline(hdc, points, 2); -} - -VOID PhTnpDrawPlusMinusGlyph( - _In_ HDC hdc, - _In_ PRECT Rect, - _In_ BOOLEAN Plus - ) -{ - INT savedDc; - ULONG width; - ULONG height; - POINT points[2]; - - savedDc = SaveDC(hdc); - - SelectObject(hdc, GetStockObject(DC_PEN)); - SetDCPenColor(hdc, RGB(0x55, 0x55, 0x55)); - SelectObject(hdc, GetStockObject(DC_BRUSH)); - SetDCBrushColor(hdc, RGB(0xff, 0xff, 0xff)); - - width = Rect->right - Rect->left; - height = Rect->bottom - Rect->top; - - // Draw the rectangle. - Rectangle(hdc, Rect->left, Rect->top, Rect->right + 1, Rect->bottom + 1); - - SetDCPenColor(hdc, RGB(0x00, 0x00, 0x00)); - - // Draw the horizontal line. - points[0].x = Rect->left + 2; - points[0].y = Rect->top + height / 2; - points[1].x = Rect->right - 2 + 1; - points[1].y = points[0].y; - Polyline(hdc, points, 2); - - if (Plus) - { - // Draw the vertical line. - points[0].x = Rect->left + width / 2; - points[0].y = Rect->top + 2; - points[1].x = points[0].x; - points[1].y = Rect->bottom - 2 + 1; - Polyline(hdc, points, 2); - } - - RestoreDC(hdc, savedDc); -} - -VOID PhTnpDrawSelectionRectangle( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc, - _In_ PRECT Rect - ) -{ - RECT rect; - BOOLEAN drewWithAlpha; - - rect = *Rect; - - // MSDN says FrameRect/DrawFocusRect doesn't draw anything if bottom <= top or right <= left. - // That's complete rubbish. - if (rect.right - rect.left == 0 || rect.bottom - rect.top == 0) - return; - - drewWithAlpha = FALSE; - - if (Context->SelectionRectangleAlpha) - { - HDC tempDc; - BITMAPINFOHEADER header; - HBITMAP bitmap; - HBITMAP oldBitmap; - PVOID bits; - RECT tempRect; - BLENDFUNCTION blendFunction; - - tempDc = CreateCompatibleDC(hdc); - - if (tempDc) - { - memset(&header, 0, sizeof(BITMAPINFOHEADER)); - header.biSize = sizeof(BITMAPINFOHEADER); - header.biWidth = 1; - header.biHeight = 1; - header.biPlanes = 1; - header.biBitCount = 24; - bitmap = CreateDIBSection(tempDc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &bits, NULL, 0); - - if (bitmap) - { - // Draw the outline of the selection rectangle. - FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT)); - - // Fill in the selection rectangle. - - oldBitmap = SelectObject(tempDc, bitmap); - tempRect.left = 0; - tempRect.top = 0; - tempRect.right = 1; - tempRect.bottom = 1; - FillRect(tempDc, &tempRect, GetSysColorBrush(COLOR_HOTLIGHT)); - - blendFunction.BlendOp = AC_SRC_OVER; - blendFunction.BlendFlags = 0; - blendFunction.SourceConstantAlpha = 70; - blendFunction.AlphaFormat = 0; - - GdiAlphaBlend( - hdc, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - tempDc, - 0, - 0, - 1, - 1, - blendFunction - ); - - drewWithAlpha = TRUE; - - SelectObject(tempDc, oldBitmap); - DeleteObject(bitmap); - } - - DeleteDC(tempDc); - } - } - - if (!drewWithAlpha) - { - DrawFocusRect(hdc, &rect); - } -} - -VOID PhTnpDrawThemedBorder( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ HDC hdc - ) -{ - RECT windowRect; - RECT clientRect; - LONG sizingBorderWidth; - LONG borderX; - LONG borderY; - - GetWindowRect(Context->Handle, &windowRect); - windowRect.right -= windowRect.left; - windowRect.bottom -= windowRect.top; - windowRect.left = 0; - windowRect.top = 0; - - clientRect.left = windowRect.left + Context->SystemEdgeX; - clientRect.top = windowRect.top + Context->SystemEdgeY; - clientRect.right = windowRect.right - Context->SystemEdgeX; - clientRect.bottom = windowRect.bottom - Context->SystemEdgeY; - - // Make sure we don't paint in the client area. - ExcludeClipRect(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom); - - // Draw the themed border. - DrawThemeBackground(Context->ThemeData, hdc, 0, 0, &windowRect, NULL); - - // Calculate the size of the border we just drew, and fill in the rest of the space if we didn't - // fully paint the region. - - if (SUCCEEDED(GetThemeInt(Context->ThemeData, 0, 0, TMT_SIZINGBORDERWIDTH, &sizingBorderWidth))) - { - borderX = sizingBorderWidth; - borderY = sizingBorderWidth; - } - else - { - borderX = Context->SystemBorderX; - borderY = Context->SystemBorderY; - } - - if (borderX < Context->SystemEdgeX || borderY < Context->SystemEdgeY) - { - windowRect.left += Context->SystemEdgeX - borderX; - windowRect.top += Context->SystemEdgeY - borderY; - windowRect.right -= Context->SystemEdgeX - borderX; - windowRect.bottom -= Context->SystemEdgeY - borderY; - FillRect(hdc, &windowRect, GetSysColorBrush(COLOR_WINDOW)); - } -} - -VOID PhTnpInitializeTooltips( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - TOOLINFO toolInfo; - - Context->TooltipsHandle = CreateWindowEx( - WS_EX_TRANSPARENT, // solves double-click problem - TOOLTIPS_CLASS, - NULL, - WS_POPUP | TTS_NOPREFIX, - 0, - 0, - 0, - 0, - NULL, - NULL, - Context->InstanceHandle, - NULL - ); - - if (!Context->TooltipsHandle) - return; - - // Item tooltips - memset(&toolInfo, 0, sizeof(TOOLINFO)); - toolInfo.cbSize = sizeof(TOOLINFO); - toolInfo.uFlags = TTF_TRANSPARENT; - toolInfo.hwnd = Context->Handle; - toolInfo.uId = TNP_TOOLTIPS_ITEM; - toolInfo.lpszText = LPSTR_TEXTCALLBACK; - toolInfo.lParam = TNP_TOOLTIPS_ITEM; - SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); - - // Fixed column tooltips - toolInfo.uFlags = 0; - toolInfo.hwnd = Context->FixedHeaderHandle; - toolInfo.uId = TNP_TOOLTIPS_FIXED_HEADER; - toolInfo.lpszText = LPSTR_TEXTCALLBACK; - toolInfo.lParam = TNP_TOOLTIPS_FIXED_HEADER; - SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); - - // Normal column tooltips - toolInfo.uFlags = 0; - toolInfo.hwnd = Context->HeaderHandle; - toolInfo.uId = TNP_TOOLTIPS_HEADER; - toolInfo.lpszText = LPSTR_TEXTCALLBACK; - toolInfo.lParam = TNP_TOOLTIPS_HEADER; - SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); - - // Hook the header control window procedures so we can forward mouse messages to the tooltip - // control. - SetWindowSubclass(Context->FixedHeaderHandle, PhTnpHeaderHookWndProc, 0, (ULONG_PTR)Context); - SetWindowSubclass(Context->HeaderHandle, PhTnpHeaderHookWndProc, 0, (ULONG_PTR)Context); - - SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); // no limit - SendMessage(Context->TooltipsHandle, WM_SETFONT, (WPARAM)Context->Font, FALSE); - Context->TooltipFont = Context->Font; -} - -VOID PhTnpGetTooltipText( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ PPOINT Point, - _Out_ PWSTR *Text - ) -{ - PH_TREENEW_HIT_TEST hitTest; - BOOLEAN unfoldingTooltip; - BOOLEAN unfoldingTooltipFromViewCancelled; - PH_TREENEW_CELL_PARTS parts; - LONG viewRight; - PH_TREENEW_GET_CELL_TOOLTIP getCellTooltip; - - hitTest.Point = *Point; - hitTest.InFlags = TN_TEST_COLUMN | TN_TEST_SUBITEM; - PhTnpHitTest(Context, &hitTest); - - if (Context->DragSelectionActive) - return; - if (!(hitTest.Flags & TN_HIT_ITEM)) - return; - if (hitTest.Flags & (TN_HIT_ITEM_PLUSMINUS | TN_HIT_DIVIDER)) - return; - if (!hitTest.Column) - return; - - if (Context->TooltipIndex != hitTest.Node->Index || Context->TooltipId != hitTest.Column->Id) - { - Context->TooltipIndex = hitTest.Node->Index; - Context->TooltipId = hitTest.Column->Id; - - getCellTooltip.Flags = 0; - getCellTooltip.Node = hitTest.Node; - getCellTooltip.Column = hitTest.Column; - getCellTooltip.Unfolding = FALSE; - PhInitializeEmptyStringRef(&getCellTooltip.Text); - getCellTooltip.Font = Context->Font; - getCellTooltip.MaximumWidth = -1; - - unfoldingTooltip = FALSE; - unfoldingTooltipFromViewCancelled = FALSE; - - if (!(Context->ExtendedFlags & TN_FLAG_NO_UNFOLDING_TOOLTIPS) && - PhTnpGetCellParts(Context, hitTest.Node->Index, hitTest.Column, TN_MEASURE_TEXT, &parts) && - (parts.Flags & TN_PART_CONTENT) && (parts.Flags & TN_PART_TEXT)) - { - viewRight = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - - // Use an unfolding tooltip if the text was truncated within the column, or the text - // extends beyond the view area in either direction. - - if (parts.TextRect.left < parts.ContentRect.left || parts.TextRect.right > parts.ContentRect.right) - { - unfoldingTooltip = TRUE; - } - else if ((!hitTest.Column->Fixed && parts.TextRect.left < Context->NormalLeft) || parts.TextRect.right > viewRight) - { - // Only show view-based unfolding tooltips if the mouse is over the text itself. - if (Point->x >= parts.TextRect.left && Point->x < parts.TextRect.right) - unfoldingTooltip = TRUE; - else - unfoldingTooltipFromViewCancelled = TRUE; - } - - if (unfoldingTooltip) - { - getCellTooltip.Unfolding = TRUE; - getCellTooltip.Text = parts.Text; - getCellTooltip.Font = parts.Font; // try to use the same font as the cell - - Context->TooltipRect = parts.TextRect; - } - } - - Context->Callback(Context->Handle, TreeNewGetCellTooltip, &getCellTooltip, NULL, Context->CallbackContext); - - Context->TooltipUnfolding = getCellTooltip.Unfolding; - - if (getCellTooltip.Text.Buffer && getCellTooltip.Text.Length != 0) - { - PhMoveReference(&Context->TooltipText, PhCreateString2(&getCellTooltip.Text)); - } - else - { - PhClearReference(&Context->TooltipText); - - if (unfoldingTooltipFromViewCancelled) - { - // We may need to show the view-based unfolding tooltip if the mouse moves over the - // text in the future. Reset the index and ID to make sure we keep checking. - Context->TooltipIndex = -1; - Context->TooltipId = -1; - } - } - - Context->NewTooltipFont = getCellTooltip.Font; - - if (!Context->NewTooltipFont) - Context->NewTooltipFont = Context->Font; - - if (getCellTooltip.MaximumWidth <= MAXSHORT) // seems to be the maximum value that the tooltip control supports - SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, getCellTooltip.MaximumWidth); - else - SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); - } - - if (Context->TooltipText) - *Text = Context->TooltipText->Buffer; -} - -BOOLEAN PhTnpPrepareTooltipShow( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - RECT rect; - - if (Context->TooltipFont != Context->NewTooltipFont) - { - Context->TooltipFont = Context->NewTooltipFont; - SendMessage(Context->TooltipsHandle, WM_SETFONT, (WPARAM)Context->TooltipFont, FALSE); - } - - if (!Context->TooltipUnfolding) - { - SetWindowPos( - Context->TooltipsHandle, - HWND_TOPMOST, - 0, - 0, - 0, - 0, - SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_HIDEWINDOW - ); - - return FALSE; - } - - rect = Context->TooltipRect; - SendMessage(Context->TooltipsHandle, TTM_ADJUSTRECT, TRUE, (LPARAM)&rect); - MapWindowPoints(Context->Handle, NULL, (POINT *)&rect, 2); - SetWindowPos( - Context->TooltipsHandle, - HWND_TOPMOST, - rect.left, - rect.top, - 0, - 0, - SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW - ); - - return TRUE; -} - -VOID PhTnpPrepareTooltipPop( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - Context->TooltipIndex = -1; - Context->TooltipId = -1; - Context->TooltipColumnId = -1; -} - -VOID PhTnpPopTooltip( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - if (Context->TooltipsHandle) - { - SendMessage(Context->TooltipsHandle, TTM_POP, 0, 0); - PhTnpPrepareTooltipPop(Context); - } -} - -PPH_TREENEW_COLUMN PhTnpHitTestHeader( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Fixed, - _In_ PPOINT Point, - _Out_opt_ PRECT ItemRect - ) -{ - PPH_TREENEW_COLUMN column; - RECT itemRect; - - if (Fixed) - { - if (!Context->FixedColumnVisible) - return NULL; - - column = Context->FixedColumn; - - if (!Header_GetItemRect(Context->FixedHeaderHandle, 0, &itemRect)) - return NULL; - } - else - { - HDHITTESTINFO hitTestInfo; - - hitTestInfo.pt = *Point; - hitTestInfo.flags = 0; - hitTestInfo.iItem = -1; - - if (SendMessage(Context->HeaderHandle, HDM_HITTEST, 0, (LPARAM)&hitTestInfo) != -1 && hitTestInfo.iItem != -1) - { - HDITEM item; - - item.mask = HDI_LPARAM; - - if (!Header_GetItem(Context->HeaderHandle, hitTestInfo.iItem, &item)) - return NULL; - - column = (PPH_TREENEW_COLUMN)item.lParam; - - if (!Header_GetItemRect(Context->HeaderHandle, hitTestInfo.iItem, &itemRect)) - return NULL; - } - else - { - return NULL; - } - } - - if (ItemRect) - *ItemRect = itemRect; - - return column; -} - -VOID PhTnpGetHeaderTooltipText( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ BOOLEAN Fixed, - _In_ PPOINT Point, - _Out_ PWSTR *Text - ) -{ - LOGICAL result; - PPH_TREENEW_COLUMN column; - RECT itemRect; - PWSTR text; - SIZE_T textCount; - HDC hdc; - SIZE textSize; - - column = PhTnpHitTestHeader(Context, Fixed, Point, &itemRect); - - if (!column) - return; - - if (Context->TooltipColumnId != column->Id) - { - // Determine if the tooltip needs to be shown. - - text = column->Text; - textCount = PhCountStringZ(text); - - if (!(hdc = GetDC(Context->Handle))) - return; - - SelectObject(hdc, Context->Font); - - result = GetTextExtentPoint32(hdc, text, (ULONG)textCount, &textSize); - ReleaseDC(Context->Handle, hdc); - - if (!result) - return; - - if (textSize.cx + 6 + 6 <= itemRect.right - itemRect.left) // HACK: Magic values (same as our cell margins?) - return; - - Context->TooltipColumnId = column->Id; - PhMoveReference(&Context->TooltipText, PhCreateStringEx(text, textCount * sizeof(WCHAR))); - } - - *Text = Context->TooltipText->Buffer; - - // Always use the default parameters for column header tooltips. - Context->NewTooltipFont = Context->Font; - Context->TooltipUnfolding = FALSE; - SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, TNP_TOOLTIPS_DEFAULT_MAXIMUM_WIDTH); -} - -LRESULT CALLBACK PhTnpHeaderHookWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_TREENEW_CONTEXT context = (PPH_TREENEW_CONTEXT)dwRefData; - - switch (uMsg) - { - case WM_DESTROY: - RemoveWindowSubclass(hwnd, PhTnpHeaderHookWndProc, uIdSubclass); - break; - case WM_MOUSEMOVE: - { - POINT point; - PPH_TREENEW_COLUMN column; - ULONG id; - - point.x = GET_X_LPARAM(lParam); - point.y = GET_Y_LPARAM(lParam); - column = PhTnpHitTestHeader(context, hwnd == context->FixedHeaderHandle, &point, NULL); - - if (column) - id = column->Id; - else - id = -1; - - if (context->TooltipColumnId != id) - { - PhTnpPopTooltip(context); - } - } - break; - case WM_NOTIFY: - { - NMHDR *header = (NMHDR *)lParam; - - switch (header->code) - { - case TTN_GETDISPINFO: - { - if (header->hwndFrom == context->TooltipsHandle) - { - NMTTDISPINFO *info = (NMTTDISPINFO *)header; - POINT point; - - PhTnpGetMessagePos(hwnd, &point); - PhTnpGetHeaderTooltipText(context, info->lParam == TNP_TOOLTIPS_FIXED_HEADER, &point, &info->lpszText); - } - } - break; - case TTN_SHOW: - { - if (header->hwndFrom == context->TooltipsHandle) - { - return PhTnpPrepareTooltipShow(context); - } - } - break; - case TTN_POP: - { - if (header->hwndFrom == context->TooltipsHandle) - { - PhTnpPrepareTooltipPop(context); - } - } - break; - } - } - break; - } - - switch (uMsg) - { - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - { - if (context->TooltipsHandle) - { - MSG message; - - message.hwnd = hwnd; - message.message = uMsg; - message.wParam = wParam; - message.lParam = lParam; - SendMessage(context->TooltipsHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); - } - } - break; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -BOOLEAN PhTnpDetectDrag( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorX, - _In_ LONG CursorY, - _In_ BOOLEAN DispatchMessages, - _Out_opt_ PULONG CancelledByMessage - ) -{ - RECT dragRect; - MSG msg; - - // Capture mouse input and see if the user moves the mouse beyond the drag rectangle. - - dragRect.left = CursorX - Context->SystemDragX; - dragRect.top = CursorY - Context->SystemDragY; - dragRect.right = CursorX + Context->SystemDragX; - dragRect.bottom = CursorY + Context->SystemDragY; - MapWindowPoints(Context->Handle, NULL, (POINT *)&dragRect, 2); - - SetCapture(Context->Handle); - - if (CancelledByMessage) - *CancelledByMessage = 0; - - do - { - // It seems that GetMessage dispatches nonqueued messages directly from kernel-mode, so we - // have to use PeekMessage and WaitMessage in order to process WM_CAPTURECHANGED messages. - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - switch (msg.message) - { - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - ReleaseCapture(); - - if (CancelledByMessage) - *CancelledByMessage = msg.message; - - break; - case WM_MOUSEMOVE: - if (msg.pt.x < dragRect.left || msg.pt.x >= dragRect.right || - msg.pt.y < dragRect.top || msg.pt.y >= dragRect.bottom) - { - if (IsWindow(Context->Handle)) - return TRUE; - else - return FALSE; - } - break; - default: - if (DispatchMessages) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - break; - } - } - else - { - WaitMessage(); - } - } while (IsWindow(Context->Handle) && GetCapture() == Context->Handle); - - return FALSE; -} - -VOID PhTnpDragSelect( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ LONG CursorX, - _In_ LONG CursorY - ) -{ - MSG msg; - LONG cursorX; - LONG cursorY; - BOOLEAN originFixed; - RECT dragRect; - RECT oldDragRect; - RECT windowRect; - POINT cursorPoint; - BOOLEAN showContextMenu; - - cursorX = CursorX; - cursorY = CursorY; - originFixed = cursorX < Context->FixedWidth; - - dragRect.left = cursorX; - dragRect.top = cursorY; - dragRect.right = cursorX; - dragRect.bottom = cursorY; - oldDragRect = dragRect; - Context->DragRect = dragRect; - Context->DragSelectionActive = TRUE; - - if (Context->DoubleBuffered) - Context->SelectionRectangleAlpha = TRUE; - // TODO: Make sure the monitor's color depth is sufficient for alpha-blended selection - // rectangles. - - GetWindowRect(Context->Handle, &windowRect); - - cursorPoint.x = windowRect.left + cursorX; - cursorPoint.y = windowRect.top + cursorY; - - showContextMenu = FALSE; - - SetCapture(Context->Handle); - - while (TRUE) - { - if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - BOOLEAN leftOrRight; - BOOLEAN aboveOrBelow; - - // If the cursor is outside of the window, generate some messages so the window keeps - // scrolling. - - leftOrRight = cursorPoint.x < windowRect.left || cursorPoint.x > windowRect.right; - aboveOrBelow = cursorPoint.y < windowRect.top || cursorPoint.y > windowRect.bottom; - - if ((Context->VScrollVisible && aboveOrBelow && PhTnpCanScroll(Context, FALSE, cursorPoint.y > windowRect.bottom)) || - (Context->HScrollVisible && leftOrRight && PhTnpCanScroll(Context, TRUE, cursorPoint.x > windowRect.right))) - { - SetCursorPos(cursorPoint.x, cursorPoint.y); - } - else - { - WaitMessage(); - } - - goto EndOfLoop; - } - - cursorPoint = msg.pt; - - switch (msg.message) - { - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - ReleaseCapture(); - goto EndOfLoop; - case WM_RBUTTONUP: - ReleaseCapture(); - showContextMenu = TRUE; - goto EndOfLoop; - case WM_MOUSEMOVE: - { - LONG newCursorX; - LONG newCursorY; - LONG deltaRows; - LONG deltaX; - LONG oldVScrollPosition; - LONG oldHScrollPosition; - LONG newDeltaX; - LONG newDeltaY; - LONG viewLeft; - LONG viewTop; - LONG viewRight; - LONG viewBottom; - LONG temp; - RECT totalRect; - - newCursorX = GET_X_LPARAM(msg.lParam); - newCursorY = GET_Y_LPARAM(msg.lParam); - - // Scroll the window if the cursor is outside of it. - - deltaRows = 0; - deltaX = 0; - - if (Context->VScrollVisible) - { - if (cursorPoint.y < windowRect.top) - deltaRows = -(windowRect.top - cursorPoint.y + Context->RowHeight - 1) / Context->RowHeight; // scroll up - else if (cursorPoint.y >= windowRect.bottom) - deltaRows = (cursorPoint.y - windowRect.bottom + Context->RowHeight - 1) / Context->RowHeight; // scroll down - } - - if (Context->HScrollVisible) - { - if (cursorPoint.x < windowRect.left) - deltaX = -(windowRect.left - cursorPoint.x); // scroll left - else if (cursorPoint.x >= windowRect.right) - deltaX = cursorPoint.x - windowRect.right; // scroll right - } - - oldVScrollPosition = Context->VScrollPosition; - oldHScrollPosition = Context->HScrollPosition; - - if (deltaRows != 0 || deltaX != 0) - PhTnpScroll(Context, deltaRows, deltaX); - - newDeltaX = oldHScrollPosition - Context->HScrollPosition; - newDeltaY = (oldVScrollPosition - Context->VScrollPosition) * Context->RowHeight; - - // Adjust our original drag point for the scrolling. - if (!originFixed) - cursorX += newDeltaX; - cursorY += newDeltaY; - - // Adjust the old drag rectangle for the scrolling. - if (!originFixed) - oldDragRect.left += newDeltaX; - oldDragRect.top += newDeltaY; - if (!originFixed) - oldDragRect.right += newDeltaX; - oldDragRect.bottom += newDeltaY; - - // Ensure that the new cursor position is within the content area. - - viewLeft = Context->FixedColumnVisible ? 0 : -Context->HScrollPosition; - viewTop = Context->HeaderHeight - Context->VScrollPosition; - viewRight = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; - viewBottom = Context->HeaderHeight + ((LONG)Context->FlatList->Count - Context->VScrollPosition) * Context->RowHeight; - - temp = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); - viewRight = max(viewRight, temp); - temp = Context->ClientRect.bottom - ((!Context->FixedColumnVisible && Context->HScrollVisible) ? Context->HScrollHeight : 0); - viewBottom = max(viewBottom, temp); - - if (newCursorX < viewLeft) - newCursorX = viewLeft; - if (newCursorX > viewRight) - newCursorX = viewRight; - if (newCursorY < viewTop) - newCursorY = viewTop; - if (newCursorY > viewBottom) - newCursorY = viewBottom; - - // Create the new drag rectangle. - - if (cursorX < newCursorX) - { - dragRect.left = cursorX; - dragRect.right = newCursorX; - } - else - { - dragRect.left = newCursorX; - dragRect.right = cursorX; - } - - if (cursorY < newCursorY) - { - dragRect.top = cursorY; - dragRect.bottom = newCursorY; - } - else - { - dragRect.top = newCursorY; - dragRect.bottom = cursorY; - } - - // Has anything changed from before? - if (dragRect.left == oldDragRect.left && dragRect.top == oldDragRect.top && - dragRect.right == oldDragRect.right && dragRect.bottom == oldDragRect.bottom) - { - break; - } - - Context->DragRect = dragRect; - - // Process the selection. - totalRect.left = min(dragRect.left, oldDragRect.left); - totalRect.top = min(dragRect.top, oldDragRect.top); - totalRect.right = max(dragRect.right, oldDragRect.right); - totalRect.bottom = max(dragRect.bottom, oldDragRect.bottom); - PhTnpProcessDragSelect(Context, (ULONG)msg.wParam, &oldDragRect, &dragRect, &totalRect); - - // Redraw the drag rectangle. - RedrawWindow(Context->Handle, &totalRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW); - - oldDragRect = dragRect; - } - break; - case WM_MOUSELEAVE: - break; // don't process - case WM_MOUSEWHEEL: - break; // don't process - case WM_KEYDOWN: - if (msg.wParam == VK_ESCAPE) - { - ULONG changedStart; - ULONG changedEnd; - RECT rect; - - PhTnpSelectRange(Context, -1, -1, TN_SELECT_RESET, &changedStart, &changedEnd); - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } - - ReleaseCapture(); - } - break; // don't process - case WM_CHAR: - break; // don't process - default: - TranslateMessage(&msg); - DispatchMessage(&msg); - break; - } - -EndOfLoop: - if (GetCapture() != Context->Handle) - break; - } - - Context->DragSelectionActive = FALSE; - RedrawWindow(Context->Handle, &dragRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW); - - if (showContextMenu) - { - // Display a context menu at the original drag point. - SendMessage(Context->Handle, WM_CONTEXTMENU, (WPARAM)Context->Handle, MAKELPARAM(windowRect.left + CursorX, windowRect.top + CursorY)); - } -} - -VOID PhTnpProcessDragSelect( - _In_ PPH_TREENEW_CONTEXT Context, - _In_ ULONG VirtualKeys, - _In_ PRECT OldRect, - _In_ PRECT NewRect, - _In_ PRECT TotalRect - ) -{ - LONG firstRow; - LONG lastRow; - RECT rowRect; - LONG i; - PPH_TREENEW_NODE node; - LONG changedStart; - LONG changedEnd; - RECT rect; - - // Determine which rows we need to test. The divisions below must be done on positive integers - // to ensure correct rounding. - - firstRow = (TotalRect->top - Context->HeaderHeight + Context->VScrollPosition * Context->RowHeight) / Context->RowHeight; - lastRow = (TotalRect->bottom - 1 - Context->HeaderHeight + Context->VScrollPosition * Context->RowHeight) / Context->RowHeight; - - if (firstRow < 0) - firstRow = 0; - if (lastRow >= (LONG)Context->FlatList->Count) - lastRow = Context->FlatList->Count - 1; - - rowRect.left = 0; - rowRect.top = Context->HeaderHeight + (firstRow - Context->VScrollPosition) * Context->RowHeight; - rowRect.right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; - rowRect.bottom = rowRect.top + Context->RowHeight; - - changedStart = lastRow; - changedEnd = firstRow; - - // Process the rows. - for (i = firstRow; i <= lastRow; i++) - { - BOOLEAN inOldRect; - BOOLEAN inNewRect; - - node = Context->FlatList->Items[i]; - - inOldRect = rowRect.top < OldRect->bottom && rowRect.bottom > OldRect->top && - rowRect.left < OldRect->right && rowRect.right > OldRect->left; - inNewRect = rowRect.top < NewRect->bottom && rowRect.bottom > NewRect->top && - rowRect.left < NewRect->right && rowRect.right > NewRect->left; - - if (VirtualKeys & MK_CONTROL) - { - if (!node->Unselectable && inOldRect != inNewRect) - { - node->Selected = !node->Selected; - - if (changedStart > i) - changedStart = i; - if (changedEnd < i) - changedEnd = i; - } - } - else - { - if (!node->Unselectable && inOldRect != inNewRect) - { - node->Selected = inNewRect; - - if (changedStart > i) - changedStart = i; - if (changedEnd < i) - changedEnd = i; - } - } - - rowRect.top = rowRect.bottom; - rowRect.bottom += Context->RowHeight; - } - - if (changedStart <= changedEnd) - { - Context->Callback(Context->Handle, TreeNewSelectionChanged, NULL, NULL, Context->CallbackContext); - } - - if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) - { - InvalidateRect(Context->Handle, &rect, FALSE); - } -} - -VOID PhTnpCreateBufferedContext( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - HDC hdc; - - if (hdc = GetDC(Context->Handle)) - { - Context->BufferedContext = CreateCompatibleDC(hdc); - - if (!Context->BufferedContext) - return; - - Context->BufferedContextRect = Context->ClientRect; - Context->BufferedBitmap = CreateCompatibleBitmap( - hdc, - Context->BufferedContextRect.right + 1, // leave one extra pixel for divider animation - Context->BufferedContextRect.bottom - ); - - if (!Context->BufferedBitmap) - { - DeleteDC(Context->BufferedContext); - Context->BufferedContext = NULL; - return; - } - - ReleaseDC(Context->Handle, hdc); - Context->BufferedOldBitmap = SelectObject(Context->BufferedContext, Context->BufferedBitmap); - } -} - -VOID PhTnpDestroyBufferedContext( - _In_ PPH_TREENEW_CONTEXT Context - ) -{ - // The original bitmap must be selected back into the context, otherwise the bitmap can't be - // deleted. - SelectObject(Context->BufferedContext, Context->BufferedOldBitmap); - DeleteObject(Context->BufferedBitmap); - DeleteDC(Context->BufferedContext); - - Context->BufferedContext = NULL; - Context->BufferedBitmap = NULL; -} - -VOID PhTnpGetMessagePos( - _In_ HWND hwnd, - _Out_ PPOINT ClientPoint - ) -{ - ULONG position; - POINT point; - - position = GetMessagePos(); - point.x = GET_X_LPARAM(position); - point.y = GET_Y_LPARAM(position); - ScreenToClient(hwnd, &point); - - *ClientPoint = point; -} +/* + * Process Hacker - + * tree new (tree list control) + * + * Copyright (C) 2011-2016 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +/* + * The tree new is a tree view with columns. Unlike the old tree list control, which was a wrapper + * around the list view control, this control was written from scratch. + * + * Current issues not included in any comments: + * * Adding, removing or changing columns does not cause invalidation. + * * It is not possible to change a column to make it fixed. The current fixed column must be + * removed and the new fixed column must then be added. + * * When there are no visible normal columns, the space usually occupied by the normal column + * headers is filled with a solid background color. We should catch this and paint the usual + * themed background there instead. + * * It is not possible to update any TN_STYLE_* flags after the control is created. + * + * Possible additions: + * * More flexible mouse input callbacks to allow custom controls inside columns. + * * Allow custom drawn columns to customize their behaviour when TN_FLAG_ITEM_DRAG_SELECT is set + * (e.g. disable drag selection over certain areas). + * * Virtual mode + */ + +#include +#include + +#include +#include + +#include +#include + +static PVOID ComCtl32Handle; +static LONG SmallIconWidth; +static LONG SmallIconHeight; + +BOOLEAN PhTreeNewInitialization( + VOID + ) +{ + WNDCLASSEX c = { sizeof(c) }; + + c.style = CS_DBLCLKS | CS_GLOBALCLASS; + c.lpfnWndProc = PhTnpWndProc; + c.cbClsExtra = 0; + c.cbWndExtra = sizeof(PVOID); + c.hInstance = PhInstanceHandle; + c.hIcon = NULL; + c.hCursor = LoadCursor(NULL, IDC_ARROW); + c.hbrBackground = NULL; + c.lpszMenuName = NULL; + c.lpszClassName = PH_TREENEW_CLASSNAME; + c.hIconSm = NULL; + + if (!RegisterClassEx(&c)) + return FALSE; + + ComCtl32Handle = PhGetLoaderEntryDllBase(L"comctl32.dll"); + SmallIconWidth = GetSystemMetrics(SM_CXSMICON); + SmallIconHeight = GetSystemMetrics(SM_CYSMICON); + + return TRUE; +} + +LRESULT CALLBACK PhTnpWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_TREENEW_CONTEXT context; + + context = (PPH_TREENEW_CONTEXT)GetWindowLongPtr(hwnd, 0); + + if (uMsg == WM_CREATE) + { + PhTnpCreateTreeNewContext(&context); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)context); + } + + if (!context) + return DefWindowProc(hwnd, uMsg, wParam, lParam); + + if (context->Tracking && (GetAsyncKeyState(VK_ESCAPE) & 0x1)) + { + PhTnpCancelTrack(context); + } + + // Note: if we have suspended restructuring, we *cannot* access any nodes, because all node + // pointers are now invalid. Below, we disable all input. + + switch (uMsg) + { + case WM_CREATE: + { + if (!PhTnpOnCreate(hwnd, context, (CREATESTRUCT *)lParam)) + return -1; + } + return 0; + case WM_NCDESTROY: + { + context->Callback(hwnd, TreeNewDestroying, NULL, NULL, context->CallbackContext); + PhTnpDestroyTreeNewContext(context); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)NULL); + } + return 0; + case WM_SIZE: + { + PhTnpOnSize(hwnd, context); + } + break; + case WM_ERASEBKGND: + return TRUE; + case WM_PAINT: + { + PhTnpOnPaint(hwnd, context); + } + return 0; + case WM_PRINTCLIENT: + { + if (!context->SuspendUpdateStructure) + PhTnpOnPrintClient(hwnd, context, (HDC)wParam, (ULONG)lParam); + } + return 0; + case WM_NCPAINT: + { + if (PhTnpOnNcPaint(hwnd, context, (HRGN)wParam)) + return 0; + } + break; + case WM_GETFONT: + return (LRESULT)context->Font; + case WM_SETFONT: + { + PhTnpOnSetFont(hwnd, context, (HFONT)wParam, LOWORD(lParam)); + } + break; + case WM_STYLECHANGED: + { + PhTnpOnStyleChanged(hwnd, context, (LONG)wParam, (STYLESTRUCT *)lParam); + } + break; + case WM_SETTINGCHANGE: + { + PhTnpOnSettingChange(hwnd, context); + } + break; + case WM_THEMECHANGED: + { + PhTnpOnThemeChanged(hwnd, context); + } + break; + case WM_GETDLGCODE: + return PhTnpOnGetDlgCode(hwnd, context, (ULONG)wParam, (PMSG)lParam); + case WM_SETFOCUS: + { + context->HasFocus = TRUE; + InvalidateRect(context->Handle, NULL, FALSE); + } + return 0; + case WM_KILLFOCUS: + { + //if (!context->ContextMenuActive && !(context->Style & TN_STYLE_ALWAYS_SHOW_SELECTION)) + // PhTnpSelectRange(context, -1, -1, TN_SELECT_RESET, NULL, NULL); + + context->HasFocus = FALSE; + + InvalidateRect(context->Handle, NULL, FALSE); + } + return 0; + case WM_SETCURSOR: + { + if (PhTnpOnSetCursor(hwnd, context, (HWND)wParam)) + return TRUE; + } + break; + case WM_TIMER: + { + PhTnpOnTimer(hwnd, context, (ULONG)wParam); + } + return 0; + case WM_MOUSEMOVE: + { + if (!context->SuspendUpdateStructure) + PhTnpOnMouseMove(hwnd, context, (ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + else + context->SuspendUpdateMoveMouse = TRUE; + } + break; + case WM_MOUSELEAVE: + { + //if (!context->ContextMenuActive && !(context->Style & TN_STYLE_ALWAYS_SHOW_SELECTION)) + //{ + // ULONG changedStart; + // ULONG changedEnd; + // RECT rect; + // + // PhTnpSelectRange(context, -1, -1, TN_SELECT_RESET, &changedStart, &changedEnd); + // + // if (PhTnpGetRowRects(context, changedStart, changedEnd, TRUE, &rect)) + // { + // InvalidateRect(context->Handle, &rect, FALSE); + // } + //} + + if (!context->SuspendUpdateStructure) + PhTnpOnMouseLeave(hwnd, context); + } + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + { + if (!context->SuspendUpdateStructure) + PhTnpOnXxxButtonXxx(hwnd, context, uMsg, (ULONG)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_CAPTURECHANGED: + { + PhTnpOnCaptureChanged(hwnd, context); + } + break; + case WM_KEYDOWN: + { + if (!context->SuspendUpdateStructure) + PhTnpOnKeyDown(hwnd, context, (ULONG)wParam, (ULONG)lParam); + } + break; + case WM_CHAR: + { + if (!context->SuspendUpdateStructure) + PhTnpOnChar(hwnd, context, (ULONG)wParam, (ULONG)lParam); + } + return 0; + case WM_MOUSEWHEEL: + { + PhTnpOnMouseWheel(hwnd, context, (SHORT)HIWORD(wParam), LOWORD(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_MOUSEHWHEEL: + { + PhTnpOnMouseHWheel(hwnd, context, (SHORT)HIWORD(wParam), LOWORD(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_CONTEXTMENU: + { + if (!context->SuspendUpdateStructure) + PhTnpOnContextMenu(hwnd, context, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + return 0; + case WM_VSCROLL: + { + PhTnpOnVScroll(hwnd, context, LOWORD(wParam), HIWORD(wParam)); + } + return 0; + case WM_HSCROLL: + { + PhTnpOnHScroll(hwnd, context, LOWORD(wParam), HIWORD(wParam)); + } + return 0; + case WM_NOTIFY: + { + LRESULT result; + + if (PhTnpOnNotify(hwnd, context, (NMHDR *)lParam, &result)) + return result; + } + break; + case WM_MEASUREITEM: + if (PhThemeWindowMeasureItem((LPMEASUREITEMSTRUCT)lParam)) + return TRUE; + break; + case WM_DRAWITEM: + if (PhThemeWindowDrawItem((LPDRAWITEMSTRUCT)lParam)) + return TRUE; + break; + } + + if (uMsg >= TNM_FIRST && uMsg <= TNM_LAST) + { + return PhTnpOnUserMessage(hwnd, context, uMsg, wParam, lParam); + } + + switch (uMsg) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + { + if (context->TooltipsHandle) + { + MSG message; + + message.hwnd = hwnd; + message.message = uMsg; + message.wParam = wParam; + message.lParam = lParam; + SendMessage(context->TooltipsHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); + } + } + break; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +BOOLEAN NTAPI PhTnpNullCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + return FALSE; +} + +VOID PhTnpCreateTreeNewContext( + _Out_ PPH_TREENEW_CONTEXT *Context + ) +{ + PPH_TREENEW_CONTEXT context; + + context = PhAllocateZero(sizeof(PH_TREENEW_CONTEXT)); + context->FixedWidthMinimum = 20; + context->RowHeight = 1; // must never be 0 + context->HotNodeIndex = -1; + context->Callback = PhTnpNullCallback; + context->FlatList = PhCreateList(64); + context->TooltipIndex = -1; + context->TooltipId = -1; + context->TooltipColumnId = -1; + context->EnableRedraw = 1; + context->DefaultBackColor = GetSysColor(COLOR_WINDOW); // RGB(0xff, 0xff, 0xff) + context->DefaultForeColor = GetSysColor(COLOR_WINDOWTEXT); // RGB(0x00, 0x00, 0x00) + + *Context = context; +} + +VOID PhTnpDestroyTreeNewContext( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + if (Context->Columns) + { + for (ULONG i = 0; i < Context->NextId; i++) + { + if (Context->Columns[i]) + PhFree(Context->Columns[i]); + } + + PhFree(Context->Columns); + } + + if (Context->ColumnsByDisplay) + PhFree(Context->ColumnsByDisplay); + + PhDereferenceObject(Context->FlatList); + + if (Context->FontOwned) + DeleteFont(Context->Font); + + if (Context->ThemeData) + CloseThemeData(Context->ThemeData); + + if (Context->SearchString) + PhFree(Context->SearchString); + + if (Context->TooltipText) + PhDereferenceObject(Context->TooltipText); + + if (Context->BufferedContext) + PhTnpDestroyBufferedContext(Context); + + if (Context->SuspendUpdateRegion) + DeleteRgn(Context->SuspendUpdateRegion); + + PhFree(Context); +} + +BOOLEAN PhTnpOnCreate( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ CREATESTRUCT *CreateStruct + ) +{ + ULONG headerStyle; + PPH_TREENEW_CREATEPARAMS createParamaters; + + Context->Handle = hwnd; + Context->InstanceHandle = CreateStruct->hInstance; + Context->Style = CreateStruct->style; + Context->ExtendedStyle = CreateStruct->dwExStyle; + createParamaters = CreateStruct->lpCreateParams; + + if (Context->Style & TN_STYLE_DOUBLE_BUFFERED) + Context->DoubleBuffered = TRUE; + if ((Context->Style & TN_STYLE_ANIMATE_DIVIDER) && Context->DoubleBuffered) + Context->AnimateDivider = TRUE; + + headerStyle = HDS_HORZ | HDS_FULLDRAG; + + if (!(Context->Style & TN_STYLE_NO_COLUMN_SORT)) + headerStyle |= HDS_BUTTONS; + if (!(Context->Style & TN_STYLE_NO_COLUMN_HEADER)) + headerStyle |= WS_VISIBLE; + + if (Context->Style & TN_STYLE_CUSTOM_COLORS) + { + Context->CustomTextColor = createParamaters->TextColor ? createParamaters->TextColor : RGB(0xff, 0xff, 0xff); + Context->CustomFocusColor = createParamaters->FocusColor ? createParamaters->FocusColor : RGB(0x0, 0x0, 0xff); + Context->CustomSelectedColor = createParamaters->SelectionColor ? createParamaters->SelectionColor : RGB(0x0, 0x0, 0x80); + Context->CustomColors = TRUE; + } + else + { + Context->CustomFocusColor = GetSysColor(COLOR_HOTLIGHT); + Context->CustomSelectedColor = GetSysColor(COLOR_HIGHLIGHT); + } + + if (!(Context->FixedHeaderHandle = CreateWindow( + WC_HEADER, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | headerStyle, + 0, + 0, + 0, + 0, + hwnd, + NULL, + CreateStruct->hInstance, + NULL + ))) + { + return FALSE; + } + + if (!(Context->Style & TN_STYLE_NO_COLUMN_REORDER)) + headerStyle |= HDS_DRAGDROP; + + if (!(Context->HeaderHandle = CreateWindow( + WC_HEADER, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | headerStyle, + 0, + 0, + 0, + 0, + hwnd, + NULL, + CreateStruct->hInstance, + NULL + ))) + { + return FALSE; + } + + if (!(Context->VScrollHandle = CreateWindow( + WC_SCROLLBAR, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBS_VERT, + 0, + 0, + 0, + 0, + hwnd, + NULL, + CreateStruct->hInstance, + NULL + ))) + { + return FALSE; + } + + if (!(Context->HScrollHandle = CreateWindow( + WC_SCROLLBAR, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBS_HORZ, + 0, + 0, + 0, + 0, + hwnd, + NULL, + CreateStruct->hInstance, + NULL + ))) + { + return FALSE; + } + + if (!(Context->FillerBoxHandle = CreateWindow( + WC_STATIC, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, + 0, + 0, + 0, + hwnd, + NULL, + CreateStruct->hInstance, + NULL + ))) + { + return FALSE; + } + + PhTnpSetFont(Context, NULL, FALSE); // use default font + PhTnpUpdateSystemMetrics(Context); + PhTnpInitializeTooltips(Context); + + return TRUE; +} + +VOID PhTnpOnSize( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + GetClientRect(hwnd, &Context->ClientRect); + + if (Context->BufferedContext && ( + Context->BufferedContextRect.right < Context->ClientRect.right || + Context->BufferedContextRect.bottom < Context->ClientRect.bottom)) + { + // Invalidate the buffered context because the client size has increased. + PhTnpDestroyBufferedContext(Context); + } + + PhTnpLayout(Context); + + if (Context->TooltipsHandle) + { + TOOLINFO toolInfo; + + memset(&toolInfo, 0, sizeof(TOOLINFO)); + toolInfo.cbSize = sizeof(TOOLINFO); + toolInfo.hwnd = hwnd; + toolInfo.uId = TNP_TOOLTIPS_ITEM; + toolInfo.rect = Context->ClientRect; + SendMessage(Context->TooltipsHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); + } +} + +VOID PhTnpOnSetFont( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ HFONT Font, + _In_ LOGICAL Redraw + ) +{ + PhTnpSetFont(Context, Font, !!Redraw); + PhTnpLayout(Context); +} + +VOID PhTnpOnStyleChanged( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Type, + _In_ STYLESTRUCT *StyleStruct + ) +{ + if (Type == GWL_EXSTYLE) + Context->ExtendedStyle = StyleStruct->styleNew; +} + +VOID PhTnpOnSettingChange( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + PhTnpUpdateSystemMetrics(Context); + PhTnpUpdateTextMetrics(Context); + PhTnpLayout(Context); +} + +VOID PhTnpOnThemeChanged( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + PhTnpUpdateThemeData(Context); +} + +ULONG PhTnpOnGetDlgCode( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey, + _In_opt_ PMSG Message + ) +{ + ULONG code; + + if (Context->Callback(hwnd, TreeNewGetDialogCode, UlongToPtr(VirtualKey), &code, Context->CallbackContext)) + { + return code; + } + + return DLGC_WANTARROWS | DLGC_WANTCHARS; +} + +VOID PhTnpOnPaint( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + RECT updateRect; + HDC hdc; + PAINTSTRUCT paintStruct; + + if (GetUpdateRect(hwnd, &updateRect, FALSE) && (updateRect.left | updateRect.right | updateRect.top | updateRect.bottom)) + { + if (Context->EnableRedraw <= 0) + { + HRGN updateRegion; + + updateRegion = CreateRectRgn(0, 0, 0, 0); + GetUpdateRgn(hwnd, updateRegion, FALSE); + + if (!Context->SuspendUpdateRegion) + { + Context->SuspendUpdateRegion = updateRegion; + } + else + { + CombineRgn(Context->SuspendUpdateRegion, Context->SuspendUpdateRegion, updateRegion, RGN_OR); + DeleteRgn(updateRegion); + } + + // Pretend we painted something; this ensures the update region is validated properly. + if (BeginPaint(hwnd, &paintStruct)) + EndPaint(hwnd, &paintStruct); + + return; + } + + if (Context->DoubleBuffered) + { + if (!Context->BufferedContext) + { + PhTnpCreateBufferedContext(Context); + } + } + + if (hdc = BeginPaint(hwnd, &paintStruct)) + { + updateRect = paintStruct.rcPaint; + + if (Context->BufferedContext) + { + PhTnpPaint(hwnd, Context, Context->BufferedContext, &updateRect); + BitBlt( + hdc, + updateRect.left, + updateRect.top, + updateRect.right - updateRect.left, + updateRect.bottom - updateRect.top, + Context->BufferedContext, + updateRect.left, + updateRect.top, + SRCCOPY + ); + } + else + { + PhTnpPaint(hwnd, Context, hdc, &updateRect); + } + + EndPaint(hwnd, &paintStruct); + } + } +} + +VOID PhTnpOnPrintClient( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ ULONG Flags + ) +{ + PhTnpPaint(hwnd, Context, hdc, &Context->ClientRect); +} + +BOOLEAN PhTnpOnNcPaint( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ HRGN UpdateRegion + ) +{ + PhTnpInitializeThemeData(Context); + + // Themed border + if ((Context->ExtendedStyle & WS_EX_CLIENTEDGE) && Context->ThemeData) + { + HDC hdc; + ULONG flags; + + if (UpdateRegion == HRGN_FULL) + UpdateRegion = NULL; + + // Note the use of undocumented flags below. GetDCEx doesn't work without these. + + flags = DCX_WINDOW | DCX_LOCKWINDOWUPDATE | 0x10000; + + if (UpdateRegion) + flags |= DCX_INTERSECTRGN | 0x40000; + + if (hdc = GetDCEx(hwnd, UpdateRegion, flags)) + { + PhTnpDrawThemedBorder(Context, hdc); + ReleaseDC(hwnd, hdc); + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN PhTnpOnSetCursor( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HWND CursorWindowHandle + ) +{ + POINT point; + + PhTnpGetMessagePos(hwnd, &point); + + if (TNP_HIT_TEST_FIXED_DIVIDER(point.x, Context)) + { + if (!Context->DividerCursor) + Context->DividerCursor = LoadCursor(ComCtl32Handle, MAKEINTRESOURCE(106)); // HACK (the divider icon resource has been 106 for quite a while...) + + SetCursor(Context->DividerCursor); + return TRUE; + } + + if (Context->Cursor) + { + SetCursor(Context->Cursor); + return TRUE; + } + + return FALSE; +} + +VOID PhTnpOnTimer( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id + ) +{ + if (Id == TNP_TIMER_ANIMATE_DIVIDER) + { + RECT dividerRect; + + dividerRect.left = Context->FixedWidth; + dividerRect.top = Context->HeaderHeight; + dividerRect.right = Context->FixedWidth + 1; + dividerRect.bottom = Context->ClientRect.bottom; + + if (Context->AnimateDividerFadingIn) + { + Context->DividerHot += TNP_ANIMATE_DIVIDER_INCREMENT; + + if (Context->DividerHot >= 100) + { + Context->DividerHot = 100; + Context->AnimateDividerFadingIn = FALSE; + KillTimer(hwnd, TNP_TIMER_ANIMATE_DIVIDER); + } + + InvalidateRect(hwnd, ÷rRect, FALSE); + } + else if (Context->AnimateDividerFadingOut) + { + if (Context->DividerHot <= TNP_ANIMATE_DIVIDER_DECREMENT) + { + Context->DividerHot = 0; + Context->AnimateDividerFadingOut = FALSE; + KillTimer(hwnd, TNP_TIMER_ANIMATE_DIVIDER); + } + else + { + Context->DividerHot -= TNP_ANIMATE_DIVIDER_DECREMENT; + } + + InvalidateRect(hwnd, ÷rRect, FALSE); + } + } +} + +VOID PhTnpOnMouseMove( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ) +{ + TRACKMOUSEEVENT trackMouseEvent; + + trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); + trackMouseEvent.dwFlags = TME_LEAVE; + trackMouseEvent.hwndTrack = hwnd; + trackMouseEvent.dwHoverTime = 0; + TrackMouseEvent(&trackMouseEvent); + + if (Context->Tracking) + { + ULONG newFixedWidth; + + newFixedWidth = Context->TrackOldFixedWidth + (CursorX - Context->TrackStartX); + PhTnpSetFixedWidth(Context, newFixedWidth); + } + + PhTnpProcessMoveMouse(Context, CursorX, CursorY); +} + +VOID PhTnpOnMouseLeave( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + RECT rect; + + if (Context->HotNodeIndex != -1 && Context->ThemeData) + { + // Update the old hot node because it may have a different non-hot background and plus minus part. + if (PhTnpGetRowRects(Context, Context->HotNodeIndex, Context->HotNodeIndex, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + + Context->HotNodeIndex = -1; + + if (Context->AnimateDivider && Context->FixedDividerVisible) + { + if ((Context->DividerHot != 0 || Context->AnimateDividerFadingIn) && !Context->AnimateDividerFadingOut) + { + // Fade out the divider. + Context->AnimateDividerFadingOut = TRUE; + Context->AnimateDividerFadingIn = FALSE; + SetTimer(Context->Handle, TNP_TIMER_ANIMATE_DIVIDER, TNP_ANIMATE_DIVIDER_INTERVAL, NULL); + } + } +} + +VOID PhTnpOnXxxButtonXxx( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Message, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ) +{ + BOOLEAN startingTracking; + PH_TREENEW_HIT_TEST hitTest; + LOGICAL controlKey; + LOGICAL shiftKey; + RECT rect; + ULONG changedStart; + ULONG changedEnd; + PH_TREENEW_MESSAGE clickMessage; + + // Focus + + if (Message == WM_LBUTTONDOWN || Message == WM_RBUTTONDOWN) + SetFocus(hwnd); + + // Divider tracking + + startingTracking = FALSE; + + switch (Message) + { + case WM_LBUTTONDOWN: + { + if (TNP_HIT_TEST_FIXED_DIVIDER(CursorX, Context)) + { + startingTracking = TRUE; + Context->Tracking = TRUE; + Context->TrackStartX = CursorX; + Context->TrackOldFixedWidth = Context->FixedWidth; + SetCapture(hwnd); + + SetTimer(hwnd, TNP_TIMER_NULL, 100, NULL); // make sure we get messages once in a while so we can detect the escape key + GetAsyncKeyState(VK_ESCAPE); + } + } + break; + case WM_LBUTTONUP: + { + if (Context->Tracking) + { + ReleaseCapture(); + } + } + break; + case WM_RBUTTONDOWN: + { + if (Context->Tracking) + { + PhTnpCancelTrack(Context); + } + } + break; + } + + if (!startingTracking && Context->Tracking) // still OK to process further if the user is only starting to drag the divider + return; + + hitTest.Point.x = CursorX; + hitTest.Point.y = CursorY; + hitTest.InFlags = TN_TEST_COLUMN | TN_TEST_SUBITEM; + PhTnpHitTest(Context, &hitTest); + + controlKey = VirtualKeys & MK_CONTROL; + shiftKey = VirtualKeys & MK_SHIFT; + + // Plus minus glyph + + if ((hitTest.Flags & TN_HIT_ITEM_PLUSMINUS) && Message == WM_LBUTTONDOWN) + { + PhTnpSetExpandedNode(Context, hitTest.Node, !hitTest.Node->Expanded); + } + + // Selection + + if (!(hitTest.Flags & TN_HIT_ITEM_PLUSMINUS) && (Message == WM_LBUTTONDOWN || Message == WM_RBUTTONDOWN)) + { + LOGICAL allowDragSelect; + PH_TREENEW_CELL_PARTS parts; + + PhTnpPopTooltip(Context); + allowDragSelect = TRUE; + + if (hitTest.Flags & TN_HIT_ITEM) + { + allowDragSelect = FALSE; + Context->FocusNode = hitTest.Node; + + if (Context->ExtendedFlags & TN_FLAG_ITEM_DRAG_SELECT) + { + // To allow drag selection to begin even if the cursor is on an item, we check if + // the cursor is on the item icon or text. Exceptions are: + // * When the item is already selected + // * When user is beginning to drag the divider + + if (!hitTest.Node->Selected && !startingTracking) + { + if (PhTnpGetCellParts(Context, hitTest.Node->Index, hitTest.Column, TN_MEASURE_TEXT, &parts)) + { + allowDragSelect = TRUE; + + if ((parts.Flags & TN_PART_ICON) && CursorX >= parts.IconRect.left && CursorX < parts.IconRect.right) + allowDragSelect = FALSE; + + if ((parts.Flags & TN_PART_CONTENT) && (parts.Flags & TN_PART_TEXT)) + { + if (CursorX >= parts.TextRect.left && CursorX < parts.TextRect.right) + allowDragSelect = FALSE; + } + } + } + } + + PhTnpProcessSelectNode(Context, hitTest.Node, controlKey, shiftKey, Message == WM_RBUTTONDOWN); + } + + if (allowDragSelect) + { + BOOLEAN dragSelect; + ULONG indexToSelect; + BOOLEAN selectionProcessed; + BOOLEAN showContextMenu; + + dragSelect = FALSE; + indexToSelect = -1; + selectionProcessed = FALSE; + showContextMenu = FALSE; + + if (!(hitTest.Flags & (TN_HIT_LEFT | TN_HIT_RIGHT | TN_HIT_ABOVE | TN_HIT_BELOW)) && !startingTracking) // don't interfere with divider + { + BOOLEAN result; + ULONG saveIndex; + ULONG saveId; + ULONG cancelledByMessage; + + // Check for drag selection. PhTnpDetectDrag has its own message loop, so we need to + // clear our pointers before we continue or we will have some access violations when + // items get deleted. + + if (hitTest.Node) + saveIndex = hitTest.Node->Index; + else + saveIndex = -1; + + if (hitTest.Column) + saveId = hitTest.Column->Id; + else + saveId = -1; + + result = PhTnpDetectDrag(Context, CursorX, CursorY, TRUE, &cancelledByMessage); + + // Restore the pointers. + + if (saveIndex == -1) + hitTest.Node = NULL; + else if (saveIndex < Context->FlatList->Count) + hitTest.Node = Context->FlatList->Items[saveIndex]; + else + return; + + if (saveId != -1 && !(hitTest.Column = PhTnpLookupColumnById(Context, saveId))) + return; + + if (result) + { + dragSelect = TRUE; + + if ((hitTest.Flags & TN_HIT_ITEM) && (Context->ExtendedFlags & TN_FLAG_ITEM_DRAG_SELECT)) + { + // Include the current node before starting the drag selection, otherwise + // the user will never be able to select the current node. + indexToSelect = hitTest.Node->Index; + } + } + else + { + if ((Message == WM_LBUTTONDOWN && cancelledByMessage == WM_LBUTTONUP) || + (Message == WM_RBUTTONDOWN && cancelledByMessage == WM_RBUTTONUP)) + { + POINT point; + + if ((hitTest.Flags & TN_HIT_ITEM) && (Context->ExtendedFlags & TN_FLAG_ITEM_DRAG_SELECT)) + { + // The user isn't performing a drag selection, so prevent deselection. + selectionProcessed = TRUE; + } + + // The button up message gets consumed by PhTnpDetectDrag, so send the mouse + // event here. + // Check if the cursor stayed in the same place. + + PhTnpGetMessagePos(Context->Handle, &point); + + if (point.x == CursorX && point.y == CursorY) + { + PhTnpSendMouseEvent( + Context, + Message == WM_LBUTTONDOWN ? TreeNewLeftClick : TreeNewRightClick, + CursorX, + CursorY, + hitTest.Node, + hitTest.Column, + VirtualKeys + ); + } + + if (Message == WM_RBUTTONDOWN) + showContextMenu = TRUE; + } + } + } + + if (!selectionProcessed && !controlKey && !shiftKey) + { + // Nothing: deselect everything. + + PhTnpSelectRange(Context, indexToSelect, indexToSelect, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + } + + if (dragSelect) + { + PhTnpDragSelect(Context, CursorX, CursorY); + } + + if (showContextMenu) + { + SendMessage(Context->Handle, WM_CONTEXTMENU, (WPARAM)Context->Handle, GetMessagePos()); + } + + return; + } + } + + // Click, double-click + // Note: If TN_FLAG_ITEM_DRAG_SELECT is enabled, the code below that processes WM_xBUTTONDOWN + // and WM_xBUTTONUP messages only takes effect when the user clicks directly on an item's icon + // or text. + + clickMessage = -1; + + if (Message == WM_LBUTTONDOWN || Message == WM_RBUTTONDOWN) + { + if (Context->MouseDownLast != 0 && Context->MouseDownLast != Message) + { + // User pressed one button and pressed the other without letting go of the first one. + // This counts as a click. + + if (Context->MouseDownLast == WM_LBUTTONDOWN) + clickMessage = TreeNewLeftClick; + else + clickMessage = TreeNewRightClick; + } + + Context->MouseDownLast = Message; + Context->MouseDownLocation.x = CursorX; + Context->MouseDownLocation.y = CursorY; + } + else if (Message == WM_LBUTTONUP || Message == WM_RBUTTONUP) + { + if (Context->MouseDownLast != 0 && + Context->MouseDownLocation.x == CursorX && Context->MouseDownLocation.y == CursorY) + { + if (Context->MouseDownLast == WM_LBUTTONDOWN) + clickMessage = TreeNewLeftClick; + else + clickMessage = TreeNewRightClick; + } + + Context->MouseDownLast = 0; + } + else if (Message == WM_LBUTTONDBLCLK) + { + clickMessage = TreeNewLeftDoubleClick; + } + else if (Message == WM_RBUTTONDBLCLK) + { + clickMessage = TreeNewRightDoubleClick; + } + + if (!(hitTest.Flags & TN_HIT_ITEM_PLUSMINUS) && clickMessage != -1) + { + PhTnpSendMouseEvent(Context, clickMessage, CursorX, CursorY, hitTest.Node, hitTest.Column, VirtualKeys); + } +} + +VOID PhTnpOnCaptureChanged( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + Context->Tracking = FALSE; + KillTimer(hwnd, TNP_TIMER_NULL); +} + +VOID PhTnpOnKeyDown( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey, + _In_ ULONG Data + ) +{ + PH_TREENEW_KEY_EVENT keyEvent; + + keyEvent.Handled = FALSE; + keyEvent.VirtualKey = VirtualKey; + keyEvent.Data = Data; + Context->Callback(Context->Handle, TreeNewKeyDown, &keyEvent, NULL, Context->CallbackContext); + + if (keyEvent.Handled) + return; + + if (PhTnpProcessFocusKey(Context, VirtualKey)) + return; + if (PhTnpProcessNodeKey(Context, VirtualKey)) + return; +} + +VOID PhTnpOnChar( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Character, + _In_ ULONG Data + ) +{ + // Make sure the character is printable. + if (Character >= ' ' && Character <= '~') + { + PhTnpProcessSearchKey(Context, Character); + } +} + +VOID PhTnpOnMouseWheel( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ) +{ + // The normal mouse wheel can affect both the vertical scrollbar and the horizontal scrollbar, + // but the vertical scrollbar takes precedence. + if (Context->VScrollVisible) + { + PhTnpProcessMouseVWheel(Context, -Distance); + } + else if (Context->HScrollVisible) + { + PhTnpProcessMouseHWheel(Context, -Distance); + } +} + +VOID PhTnpOnMouseHWheel( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance, + _In_ ULONG VirtualKeys, + _In_ LONG CursorX, + _In_ LONG CursorY + ) +{ + PhTnpProcessMouseHWheel(Context, Distance); +} + +VOID PhTnpOnContextMenu( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorScreenX, + _In_ LONG CursorScreenY + ) +{ + POINT clientPoint; + BOOLEAN keyboardInvoked; + PH_TREENEW_HIT_TEST hitTest; + PH_TREENEW_CONTEXT_MENU contextMenu; + + if (CursorScreenX == -1 && CursorScreenY == -1) + { + ULONG i; + BOOLEAN found; + RECT windowRect; + RECT rect; + + keyboardInvoked = TRUE; + + // Context menu was invoked via keyboard. Display the context menu at the selected item. + + found = FALSE; + + for (i = 0; i < Context->FlatList->Count; i++) + { + if (((PPH_TREENEW_NODE)Context->FlatList->Items[i])->Selected) + { + found = TRUE; + break; + } + } + + if (found && PhTnpGetRowRects(Context, i, i, FALSE, &rect) && + rect.top >= Context->ClientRect.top && rect.top < Context->ClientRect.bottom) + { + clientPoint.x = rect.left + SmallIconWidth / 2; + clientPoint.y = rect.top + Context->RowHeight / 2; + } + else + { + clientPoint.x = 0; + clientPoint.y = 0; + } + + GetWindowRect(hwnd, &windowRect); + CursorScreenX = windowRect.left + clientPoint.x; + CursorScreenY = windowRect.top + clientPoint.y; + } + else + { + keyboardInvoked = FALSE; + + clientPoint.x = CursorScreenX; + clientPoint.y = CursorScreenY; + ScreenToClient(hwnd, &clientPoint); + + if (clientPoint.y < Context->HeaderHeight) + { + // Already handled by TreeNewHeaderRightClick. + return; + } + } + + hitTest.Point = clientPoint; + hitTest.InFlags = TN_TEST_COLUMN; + PhTnpHitTest(Context, &hitTest); + + contextMenu.Location.x = CursorScreenX; + contextMenu.Location.y = CursorScreenY; + contextMenu.ClientLocation = clientPoint; + contextMenu.Node = hitTest.Node; + contextMenu.Column = hitTest.Column; + contextMenu.KeyboardInvoked = keyboardInvoked; + Context->ContextMenuActive = TRUE; + Context->Callback(hwnd, TreeNewContextMenu, &contextMenu, NULL, Context->CallbackContext); + Context->ContextMenuActive = FALSE; +} + +VOID PhTnpOnVScroll( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Request, + _In_ USHORT Position + ) +{ + SCROLLINFO scrollInfo; + LONG oldPosition; + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_ALL; + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + switch (Request) + { + case SB_LINEUP: + scrollInfo.nPos--; + break; + case SB_LINEDOWN: + scrollInfo.nPos++; + break; + case SB_PAGEUP: + scrollInfo.nPos -= scrollInfo.nPage; + break; + case SB_PAGEDOWN: + scrollInfo.nPos += scrollInfo.nPage; + break; + case SB_THUMBPOSITION: + // Touch scrolling seems to give us Position but not nTrackPos. The problem is that Position + // is a 16-bit value, so don't use it if we have too many rows. + if (Context->FlatList->Count <= 0xffff) + scrollInfo.nPos = Position; + break; + case SB_THUMBTRACK: + scrollInfo.nPos = scrollInfo.nTrackPos; + break; + case SB_TOP: + scrollInfo.nPos = 0; + break; + case SB_BOTTOM: + scrollInfo.nPos = MAXINT; + break; + } + + scrollInfo.fMask = SIF_POS; + SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + + if (scrollInfo.nPos != oldPosition) + { + Context->VScrollPosition = scrollInfo.nPos; + PhTnpProcessScroll(Context, scrollInfo.nPos - oldPosition, 0); + } +} + +VOID PhTnpOnHScroll( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Request, + _In_ USHORT Position + ) +{ + SCROLLINFO scrollInfo; + LONG oldPosition; + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_ALL; + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + switch (Request) + { + case SB_LINELEFT: + scrollInfo.nPos -= Context->TextMetrics.tmAveCharWidth; + break; + case SB_LINERIGHT: + scrollInfo.nPos += Context->TextMetrics.tmAveCharWidth; + break; + case SB_PAGELEFT: + scrollInfo.nPos -= scrollInfo.nPage; + break; + case SB_PAGERIGHT: + scrollInfo.nPos += scrollInfo.nPage; + break; + case SB_THUMBPOSITION: + // Touch scrolling seems to give us Position but not nTrackPos. The problem is that Position + // is a 16-bit value, so don't use it if we have too many rows. + if (Context->FlatList->Count <= 0xffff) + scrollInfo.nPos = Position; + break; + case SB_THUMBTRACK: + scrollInfo.nPos = scrollInfo.nTrackPos; + break; + case SB_LEFT: + scrollInfo.nPos = 0; + break; + case SB_RIGHT: + scrollInfo.nPos = MAXINT; + break; + } + + scrollInfo.fMask = SIF_POS; + SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + + if (scrollInfo.nPos != oldPosition) + { + Context->HScrollPosition = scrollInfo.nPos; + PhTnpProcessScroll(Context, 0, scrollInfo.nPos - oldPosition); + } +} + +BOOLEAN PhTnpOnNotify( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ NMHDR *Header, + _Out_ LRESULT *Result + ) +{ + switch (Header->code) + { + case HDN_ITEMCHANGING: + case HDN_ITEMCHANGED: + { + NMHEADER *nmHeader = (NMHEADER *)Header; + + if (Header->code == HDN_ITEMCHANGING && Header->hwndFrom == Context->FixedHeaderHandle) + { + if (nmHeader->pitem->mask & HDI_WIDTH) + { + if (Context->FixedColumnVisible) + { + Context->FixedWidth = nmHeader->pitem->cxy - 1; + + if (Context->FixedWidth < Context->FixedWidthMinimum) + Context->FixedWidth = Context->FixedWidthMinimum; + + Context->NormalLeft = Context->FixedWidth + 1; + nmHeader->pitem->cxy = Context->FixedWidth + 1; + } + else + { + Context->FixedWidth = 0; + Context->NormalLeft = 0; + } + } + } + + if (Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) + { + if (nmHeader->pitem->mask & HDI_WIDTH) + { + // A column has been resized. Update our stored information. + PhTnpUpdateColumnHeaders(Context); + PhTnpUpdateColumnMaps(Context); + + if (Header->code == HDN_ITEMCHANGING) + { + HDITEM item; + + item.mask = HDI_WIDTH | HDI_LPARAM; + + if (Header_GetItem(Header->hwndFrom, nmHeader->iItem, &item)) + { + Context->ResizingColumn = (PPH_TREENEW_COLUMN)item.lParam; + Context->OldColumnWidth = item.cxy; + } + else + { + Context->ResizingColumn = NULL; + Context->OldColumnWidth = -1; + } + } + else if (Header->code == HDN_ITEMCHANGED) + { + if (Context->ResizingColumn) + { + LONG delta; + + delta = nmHeader->pitem->cxy - Context->OldColumnWidth; + + if (delta != 0) + { + PhTnpProcessResizeColumn(Context, Context->ResizingColumn, delta); + Context->Callback(Context->Handle, TreeNewColumnResized, Context->ResizingColumn, NULL, Context->CallbackContext); + } + + Context->ResizingColumn = NULL; + + // Redraw the entire window if we are displaying empty text. + if (Context->FlatList->Count == 0 && Context->EmptyText.Length != 0) + InvalidateRect(Context->Handle, NULL, FALSE); + } + else + { + // An error occurred during HDN_ITEMCHANGED, so redraw the entire window. + InvalidateRect(Context->Handle, NULL, FALSE); + } + } + } + } + } + break; + case HDN_ITEMCLICK: + { + if ((Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) && + !(Context->Style & TN_STYLE_NO_COLUMN_SORT)) + { + NMHEADER *nmHeader = (NMHEADER *)Header; + HDITEM item; + PPH_TREENEW_COLUMN column; + + // A column has been clicked, so update the sort state. + + item.mask = HDI_LPARAM; + + if (Header_GetItem(Header->hwndFrom, nmHeader->iItem, &item)) + { + column = (PPH_TREENEW_COLUMN)item.lParam; + PhTnpProcessSortColumn(Context, column); + } + } + } + break; + case HDN_ENDDRAG: + case NM_RELEASEDCAPTURE: + { + if (Header->hwndFrom == Context->HeaderHandle) + { + // Columns have been re-ordered, so refresh our information. + // Note: The fixed column cannot be re-ordered. + PhTnpUpdateColumnHeaders(Context); + PhTnpUpdateColumnMaps(Context); + Context->Callback(Context->Handle, TreeNewColumnReordered, NULL, NULL, Context->CallbackContext); + InvalidateRect(Context->Handle, NULL, FALSE); + } + } + break; + case HDN_DIVIDERDBLCLICK: + { + if (Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) + { + NMHEADER *nmHeader = (NMHEADER *)Header; + HDITEM item; + + if (Context->SuspendUpdateStructure) + break; + + item.mask = HDI_LPARAM; + + if (Header_GetItem(Header->hwndFrom, nmHeader->iItem, &item)) + { + PhTnpAutoSizeColumnHeader( + Context, + Header->hwndFrom, + (PPH_TREENEW_COLUMN)item.lParam, + 0 + ); + } + } + } + break; + case NM_RCLICK: + { + if (Header->hwndFrom == Context->FixedHeaderHandle || Header->hwndFrom == Context->HeaderHandle) + { + PH_TREENEW_HEADER_MOUSE_EVENT mouseEvent; + ULONG position; + + position = GetMessagePos(); + mouseEvent.ScreenLocation.x = GET_X_LPARAM(position); + mouseEvent.ScreenLocation.y = GET_Y_LPARAM(position); + + mouseEvent.Location = mouseEvent.ScreenLocation; + ScreenToClient(hwnd, &mouseEvent.Location); + mouseEvent.HeaderLocation = mouseEvent.ScreenLocation; + ScreenToClient(Header->hwndFrom, &mouseEvent.HeaderLocation); + mouseEvent.Column = PhTnpHitTestHeader(Context, Header->hwndFrom == Context->FixedHeaderHandle, &mouseEvent.HeaderLocation, NULL); + Context->Callback(hwnd, TreeNewHeaderRightClick, &mouseEvent, NULL, Context->CallbackContext); + } + } + break; + case TTN_GETDISPINFO: + { + if (Header->hwndFrom == Context->TooltipsHandle) + { + NMTTDISPINFO *info = (NMTTDISPINFO *)Header; + POINT point; + + PhTnpGetMessagePos(hwnd, &point); + PhTnpGetTooltipText(Context, &point, &info->lpszText); + } + } + break; + case TTN_SHOW: + { + if (Header->hwndFrom == Context->TooltipsHandle) + { + *Result = PhTnpPrepareTooltipShow(Context); + return TRUE; + } + } + break; + case TTN_POP: + { + if (Header->hwndFrom == Context->TooltipsHandle) + { + PhTnpPrepareTooltipPop(Context); + } + } + break; + } + + return FALSE; +} + +ULONG_PTR PhTnpOnUserMessage( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Message, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam + ) +{ + switch (Message) + { + case TNM_SETCALLBACK: + { + Context->Callback = (PPH_TREENEW_CALLBACK)LParam; + Context->CallbackContext = (PVOID)WParam; + + if (!Context->Callback) + Context->Callback = PhTnpNullCallback; + } + return TRUE; + case TNM_NODESSTRUCTURED: + { + if (Context->EnableRedraw <= 0) + { + Context->SuspendUpdateStructure = TRUE; + Context->SuspendUpdateLayout = TRUE; + InvalidateRect(Context->Handle, NULL, FALSE); + return TRUE; + } + + PhTnpRestructureNodes(Context); + PhTnpLayout(Context); + InvalidateRect(Context->Handle, NULL, FALSE); + } + return TRUE; + case TNM_ADDCOLUMN: + return PhTnpAddColumn(Context, (PPH_TREENEW_COLUMN)LParam); + case TNM_REMOVECOLUMN: + return PhTnpRemoveColumn(Context, (ULONG)WParam); + case TNM_GETCOLUMN: + return PhTnpCopyColumn(Context, (ULONG)WParam, (PPH_TREENEW_COLUMN)LParam); + case TNM_SETCOLUMN: + { + PPH_TREENEW_COLUMN column = (PPH_TREENEW_COLUMN)LParam; + + return PhTnpChangeColumn(Context, (ULONG)WParam, column->Id, column); + } + break; + case TNM_GETCOLUMNORDERARRAY: + { + ULONG count = (ULONG)WParam; + PULONG order = (PULONG)LParam; + ULONG i; + + if (count != Context->NumberOfColumnsByDisplay) + return FALSE; + + for (i = 0; i < count; i++) + { + order[i] = Context->ColumnsByDisplay[i]->Id; + } + } + return TRUE; + case TNM_SETCOLUMNORDERARRAY: + { + ULONG count = (ULONG)WParam; + PULONG order = (PULONG)LParam; + ULONG i; + PULONG newOrder; + PPH_TREENEW_COLUMN column; + + newOrder = PhAllocate(count * sizeof(ULONG)); + + for (i = 0; i < count; i++) + { + if (!(column = PhTnpLookupColumnById(Context, order[i]))) + { + PhFree(newOrder); + return FALSE; + } + + newOrder[i] = column->s.ViewIndex; + } + + if (!Header_SetOrderArray(Context->HeaderHandle, count, newOrder)) + { + PhFree(newOrder); + return FALSE; + } + + PhFree(newOrder); + + PhTnpUpdateColumnHeaders(Context); + PhTnpUpdateColumnMaps(Context); + } + return TRUE; + case TNM_SETCURSOR: + { + Context->Cursor = (HCURSOR)LParam; + } + return TRUE; + case TNM_GETSORT: + { + PULONG sortColumn = (PULONG)WParam; + PPH_SORT_ORDER sortOrder = (PPH_SORT_ORDER)LParam; + + if (sortColumn) + *sortColumn = Context->SortColumn; + if (sortOrder) + *sortOrder = Context->SortOrder; + } + return TRUE; + case TNM_SETSORT: + { + ULONG sortColumn = (ULONG)WParam; + PH_SORT_ORDER sortOrder = (PH_SORT_ORDER)LParam; + PPH_TREENEW_COLUMN column; + + if (sortOrder != NoSortOrder) + { + if (!(column = PhTnpLookupColumnById(Context, sortColumn))) + return FALSE; + } + else + { + sortColumn = 0; + column = NULL; + } + + Context->SortColumn = sortColumn; + Context->SortOrder = sortOrder; + + PhTnpSetColumnHeaderSortIcon(Context, column); + + Context->Callback(Context->Handle, TreeNewSortChanged, NULL, NULL, Context->CallbackContext); + } + return TRUE; + case TNM_SETTRISTATE: + Context->TriState = !!WParam; + return TRUE; + case TNM_ENSUREVISIBLE: + return PhTnpEnsureVisibleNode(Context, ((PPH_TREENEW_NODE)LParam)->Index); + case TNM_SCROLL: + PhTnpScroll(Context, (LONG)WParam, (LONG)LParam); + return TRUE; + case TNM_GETFLATNODECOUNT: + if (!Context->SuspendUpdateStructure) + return (LRESULT)Context->FlatList->Count; + else + return 0; + case TNM_GETFLATNODE: + { + ULONG index = (ULONG)WParam; + + if (index >= Context->FlatList->Count) + return (LRESULT)NULL; + + return (LRESULT)Context->FlatList->Items[index]; + } + break; + case TNM_GETCELLTEXT: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)LParam; + + return PhTnpGetCellText( + Context, + getCellText->Node, + getCellText->Id, + &getCellText->Text + ); + } + break; + case TNM_SETNODEEXPANDED: + PhTnpSetExpandedNode(Context, (PPH_TREENEW_NODE)LParam, !!WParam); + return TRUE; + case TNM_GETMAXID: + return (LRESULT)(Context->NextId - 1); + case TNM_SETMAXID: + { + ULONG maxId = (ULONG)WParam; + + if (Context->NextId < maxId + 1) + { + Context->NextId = maxId + 1; + + if (Context->AllocatedColumns < Context->NextId) + { + PhTnpExpandAllocatedColumns(Context); + } + } + } + return TRUE; + case TNM_INVALIDATENODE: + { + PPH_TREENEW_NODE node = (PPH_TREENEW_NODE)LParam; + RECT rect; + + if (!node->Visible) + return FALSE; + + if (!PhTnpGetRowRects(Context, node->Index, node->Index, TRUE, &rect)) + return FALSE; + + InvalidateRect(hwnd, &rect, FALSE); + } + return TRUE; + case TNM_INVALIDATENODES: + { + RECT rect; + + if (!PhTnpGetRowRects(Context, (ULONG)WParam, (ULONG)LParam, TRUE, &rect)) + return FALSE; + + InvalidateRect(hwnd, &rect, FALSE); + } + return TRUE; + case TNM_GETFIXEDHEADER: + return (LRESULT)Context->FixedHeaderHandle; + case TNM_GETHEADER: + return (LRESULT)Context->HeaderHandle; + case TNM_GETTOOLTIPS: + return (LRESULT)Context->TooltipsHandle; + case TNM_SELECTRANGE: + case TNM_DESELECTRANGE: + { + ULONG flags; + ULONG changedStart; + ULONG changedEnd; + RECT rect; + + flags = 0; + + if (Message == TNM_DESELECTRANGE) + flags |= TN_SELECT_DESELECT; + + PhTnpSelectRange(Context, (ULONG)WParam, (ULONG)LParam, flags, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + } + return TRUE; + case TNM_GETCOLUMNCOUNT: + return (LRESULT)Context->NumberOfColumns; + case TNM_SETREDRAW: + PhTnpSetRedraw(Context, !!WParam); + return (LRESULT)Context->EnableRedraw; + case TNM_GETVIEWPARTS: + { + PPH_TREENEW_VIEW_PARTS parts = (PPH_TREENEW_VIEW_PARTS)LParam; + + parts->ClientRect = Context->ClientRect; + parts->HeaderHeight = Context->HeaderHeight; + parts->RowHeight = Context->RowHeight; + parts->VScrollWidth = Context->VScrollVisible ? Context->VScrollWidth : 0; + parts->HScrollHeight = Context->HScrollHeight ? Context->HScrollHeight : 0; + parts->VScrollPosition = Context->VScrollPosition; + parts->HScrollPosition = Context->HScrollPosition; + parts->FixedWidth = Context->FixedWidth; + parts->NormalLeft = Context->NormalLeft; + parts->NormalWidth = Context->TotalViewX; + } + return TRUE; + case TNM_GETFIXEDCOLUMN: + return (LRESULT)Context->FixedColumn; + case TNM_GETFIRSTCOLUMN: + return (LRESULT)Context->FirstColumn; + case TNM_SETFOCUSNODE: + Context->FocusNode = (PPH_TREENEW_NODE)LParam; + return TRUE; + case TNM_SETMARKNODE: + Context->MarkNodeIndex = ((PPH_TREENEW_NODE)LParam)->Index; + return TRUE; + case TNM_SETHOTNODE: + PhTnpSetHotNode(Context, (PPH_TREENEW_NODE)LParam, FALSE); + return TRUE; + case TNM_SETEXTENDEDFLAGS: + Context->ExtendedFlags = (Context->ExtendedFlags & ~(ULONG)WParam) | ((ULONG)LParam & (ULONG)WParam); + return TRUE; + case TNM_GETCALLBACK: + { + PPH_TREENEW_CALLBACK *callback = (PPH_TREENEW_CALLBACK *)LParam; + PVOID *callbackContext = (PVOID *)WParam; + + if (callback) + { + if (Context->Callback != PhTnpNullCallback) + *callback = Context->Callback; + else + *callback = NULL; + } + + if (callbackContext) + { + *callbackContext = Context->CallbackContext; + } + } + return TRUE; + case TNM_HITTEST: + PhTnpHitTest(Context, (PPH_TREENEW_HIT_TEST)LParam); + return TRUE; + case TNM_GETVISIBLECOLUMNCOUNT: + return Context->NumberOfColumnsByDisplay + (Context->FixedColumnVisible ? 1 : 0); + case TNM_AUTOSIZECOLUMN: + { + ULONG id = (ULONG)WParam; + ULONG flags = (ULONG)LParam; + PPH_TREENEW_COLUMN column; + + if (!(column = PhTnpLookupColumnById(Context, id))) + return FALSE; + + if (!column->Visible) + return FALSE; + + PhTnpAutoSizeColumnHeader( + Context, + column->Fixed ? Context->FixedHeaderHandle : Context->HeaderHandle, + column, + flags + ); + } + return TRUE; + case TNM_SETEMPTYTEXT: + { + PPH_STRINGREF text = (PPH_STRINGREF)LParam; + ULONG flags = (ULONG)WParam; + + Context->EmptyText = *text; + } + return TRUE; + case TNM_SETROWHEIGHT: + { + LONG rowHeight = (LONG)WParam; + + if (rowHeight != 0) + { + Context->CustomRowHeight = TRUE; + Context->RowHeight = rowHeight; + } + else + { + Context->CustomRowHeight = FALSE; + PhTnpUpdateTextMetrics(Context); + } + } + return TRUE; + case TNM_ISFLATNODEVALID: + return !Context->SuspendUpdateStructure; + case TNM_THEMESUPPORT: + Context->ThemeSupport = !!WParam; + return TRUE; + } + + return 0; +} + +VOID PhTnpSetFont( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ HFONT Font, + _In_ BOOLEAN Redraw + ) +{ + if (Context->FontOwned) + { + DeleteFont(Context->Font); + Context->FontOwned = FALSE; + } + + Context->Font = Font; + + if (!Context->Font) + { + LOGFONT logFont; + + if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) + { + Context->Font = CreateFontIndirect(&logFont); + Context->FontOwned = TRUE; + } + } + + SetWindowFont(Context->FixedHeaderHandle, Context->Font, Redraw); + SetWindowFont(Context->HeaderHandle, Context->Font, Redraw); + + if (Context->TooltipsHandle) + { + SetWindowFont(Context->TooltipsHandle, Context->Font, FALSE); + Context->TooltipFont = Context->Font; + } + + PhTnpUpdateTextMetrics(Context); +} + +VOID PhTnpUpdateSystemMetrics( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + Context->VScrollWidth = GetSystemMetrics(SM_CXVSCROLL); + Context->HScrollHeight = GetSystemMetrics(SM_CYHSCROLL); + Context->SystemBorderX = GetSystemMetrics(SM_CXBORDER); + Context->SystemBorderY = GetSystemMetrics(SM_CYBORDER); + Context->SystemEdgeX = GetSystemMetrics(SM_CXEDGE); + Context->SystemEdgeY = GetSystemMetrics(SM_CYEDGE); + Context->SystemDragX = GetSystemMetrics(SM_CXDRAG); + Context->SystemDragY = GetSystemMetrics(SM_CYDRAG); + + if (Context->SystemDragX < 2) + Context->SystemDragX = 2; + if (Context->SystemDragY < 2) + Context->SystemDragY = 2; +} + +VOID PhTnpUpdateTextMetrics( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + HDC hdc; + + if (hdc = GetDC(Context->Handle)) + { + SelectFont(hdc, Context->Font); + GetTextMetrics(hdc, &Context->TextMetrics); + + if (!Context->CustomRowHeight) + { + // Below we try to match the row height as calculated by the list view, even if it + // involves magic numbers. On Vista and above there seems to be extra padding. + + Context->RowHeight = Context->TextMetrics.tmHeight; + + if (Context->Style & TN_STYLE_ICONS) + { + if (Context->RowHeight < SmallIconHeight) + Context->RowHeight = SmallIconHeight; + } + else + { + if (!(Context->Style & TN_STYLE_THIN_ROWS)) + Context->RowHeight += 1; // HACK + } + + Context->RowHeight += 1; // HACK + + if (!(Context->Style & TN_STYLE_THIN_ROWS)) + Context->RowHeight += 2; // HACK + } + + ReleaseDC(Context->Handle, hdc); + } +} + +VOID PhTnpUpdateThemeData( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + Context->DefaultBackColor = GetSysColor(COLOR_WINDOW); + Context->DefaultForeColor = GetSysColor(COLOR_WINDOWTEXT); + Context->ThemeActive = !!IsThemeActive(); + + if (Context->ThemeData) + { + CloseThemeData(Context->ThemeData); + Context->ThemeData = NULL; + } + + Context->ThemeData = OpenThemeData(Context->Handle, VSCLASS_TREEVIEW); + + if (Context->ThemeData) + { + Context->ThemeHasItemBackground = !!IsThemePartDefined(Context->ThemeData, TVP_TREEITEM, 0); + Context->ThemeHasGlyph = !!IsThemePartDefined(Context->ThemeData, TVP_GLYPH, 0); + Context->ThemeHasHotGlyph = !!IsThemePartDefined(Context->ThemeData, TVP_HOTGLYPH, 0); + } + else + { + Context->ThemeHasItemBackground = FALSE; + Context->ThemeHasGlyph = FALSE; + Context->ThemeHasHotGlyph = FALSE; + } +} + +VOID PhTnpInitializeThemeData( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + if (!Context->ThemeInitialized) + { + PhTnpUpdateThemeData(Context); + Context->ThemeInitialized = TRUE; + } +} + +VOID PhTnpCancelTrack( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + PhTnpSetFixedWidth(Context, Context->TrackOldFixedWidth); + ReleaseCapture(); +} + +VOID PhTnpLayout( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + RECT clientRect; + + if (Context->EnableRedraw <= 0) + { + Context->SuspendUpdateLayout = TRUE; + return; + } + + clientRect = Context->ClientRect; + + PhTnpUpdateScrollBars(Context); + + // Vertical scroll bar + if (Context->VScrollVisible) + { + MoveWindow( + Context->VScrollHandle, + clientRect.right - Context->VScrollWidth, + 0, + Context->VScrollWidth, + clientRect.bottom - (Context->HScrollVisible ? Context->HScrollHeight : 0), + TRUE + ); + } + + // Horizontal scroll bar + if (Context->HScrollVisible) + { + MoveWindow( + Context->HScrollHandle, + Context->NormalLeft, + clientRect.bottom - Context->HScrollHeight, + clientRect.right - Context->NormalLeft - (Context->VScrollVisible ? Context->VScrollWidth : 0), + Context->HScrollHeight, + TRUE + ); + } + + // Filler box + if (Context->VScrollVisible && Context->HScrollVisible) + { + MoveWindow( + Context->FillerBoxHandle, + clientRect.right - Context->VScrollWidth, + clientRect.bottom - Context->HScrollHeight, + Context->VScrollWidth, + Context->HScrollHeight, + TRUE + ); + } + + PhTnpLayoutHeader(Context); + + // Redraw the entire window if we are displaying empty text. + if (Context->FlatList->Count == 0 && Context->EmptyText.Length != 0) + InvalidateRect(Context->Handle, NULL, FALSE); +} + +VOID PhTnpLayoutHeader( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + RECT rect; + HDLAYOUT hdl; + WINDOWPOS windowPos; + + hdl.prc = ▭ + hdl.pwpos = &windowPos; + + if (!(Context->Style & TN_STYLE_NO_COLUMN_HEADER)) + { + // Fixed portion header control + rect.left = 0; + rect.top = 0; + rect.right = Context->NormalLeft; + rect.bottom = Context->ClientRect.bottom; + Header_Layout(Context->FixedHeaderHandle, &hdl); + SetWindowPos(Context->FixedHeaderHandle, NULL, windowPos.x, windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags); + Context->HeaderHeight = windowPos.cy; + + // Normal portion header control + rect.left = Context->NormalLeft - Context->HScrollPosition; + rect.top = 0; + rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + rect.bottom = Context->ClientRect.bottom; + Header_Layout(Context->HeaderHandle, &hdl); + SetWindowPos(Context->HeaderHandle, NULL, windowPos.x, windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags); + } + else + { + Context->HeaderHeight = 0; + } + + if (Context->TooltipsHandle) + { + TOOLINFO toolInfo; + + memset(&toolInfo, 0, sizeof(TOOLINFO)); + toolInfo.cbSize = sizeof(TOOLINFO); + toolInfo.hwnd = Context->FixedHeaderHandle; + toolInfo.uId = TNP_TOOLTIPS_FIXED_HEADER; + GetClientRect(Context->FixedHeaderHandle, &toolInfo.rect); + SendMessage(Context->TooltipsHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); + + toolInfo.hwnd = Context->HeaderHandle; + toolInfo.uId = TNP_TOOLTIPS_HEADER; + GetClientRect(Context->HeaderHandle, &toolInfo.rect); + SendMessage(Context->TooltipsHandle, TTM_NEWTOOLRECT, 0, (LPARAM)&toolInfo); + } +} + +VOID PhTnpSetFixedWidth( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG FixedWidth + ) +{ + HDITEM item; + + if (Context->FixedColumnVisible) + { + Context->FixedWidth = FixedWidth; + + if (Context->FixedWidth < Context->FixedWidthMinimum) + Context->FixedWidth = Context->FixedWidthMinimum; + + Context->NormalLeft = Context->FixedWidth + 1; + + item.mask = HDI_WIDTH; + item.cxy = Context->FixedWidth + 1; + Header_SetItem(Context->FixedHeaderHandle, 0, &item); + } + else + { + Context->FixedWidth = 0; + Context->NormalLeft = 0; + } +} + +VOID PhTnpSetRedraw( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Redraw + ) +{ + if (Redraw) + Context->EnableRedraw++; + else + Context->EnableRedraw--; + + if (Context->EnableRedraw == 1) + { + if (Context->SuspendUpdateStructure) + { + PhTnpRestructureNodes(Context); + } + + if (Context->SuspendUpdateLayout) + { + PhTnpLayout(Context); + } + + if (Context->SuspendUpdateMoveMouse) + { + POINT point; + + PhTnpGetMessagePos(Context->Handle, &point); + PhTnpProcessMoveMouse(Context, point.x, point.y); + } + + Context->SuspendUpdateStructure = FALSE; + Context->SuspendUpdateLayout = FALSE; + Context->SuspendUpdateMoveMouse = FALSE; + + if (Context->SuspendUpdateRegion) + { + InvalidateRgn(Context->Handle, Context->SuspendUpdateRegion, FALSE); + DeleteRgn(Context->SuspendUpdateRegion); + Context->SuspendUpdateRegion = NULL; + } + } +} + +VOID PhTnpSendMouseEvent( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PH_TREENEW_MESSAGE Message, + _In_ LONG CursorX, + _In_ LONG CursorY, + _In_ PPH_TREENEW_NODE Node, + _In_ PPH_TREENEW_COLUMN Column, + _In_ ULONG VirtualKeys + ) +{ + PH_TREENEW_MOUSE_EVENT mouseEvent; + + mouseEvent.Location.x = CursorX; + mouseEvent.Location.y = CursorY; + mouseEvent.Node = Node; + mouseEvent.Column = Column; + mouseEvent.KeyFlags = VirtualKeys; + Context->Callback(Context->Handle, Message, &mouseEvent, NULL, Context->CallbackContext); +} + +PPH_TREENEW_COLUMN PhTnpLookupColumnById( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id + ) +{ + if (Id >= Context->AllocatedColumns) + return NULL; + + return Context->Columns[Id]; +} + +BOOLEAN PhTnpAddColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN Column + ) +{ + PPH_TREENEW_COLUMN realColumn; + + // Check if a column with the same ID already exists. + if (Column->Id < Context->AllocatedColumns && Context->Columns[Column->Id]) + return FALSE; + + if (Context->NextId < Column->Id + 1) + Context->NextId = Column->Id + 1; + + realColumn = PhAllocateCopy(Column, sizeof(PH_TREENEW_COLUMN)); + + if (realColumn->DpiScaleOnAdd) + { + realColumn->Width = PhMultiplyDivide(realColumn->Width, PhGlobalDpi, 96); + realColumn->DpiScaleOnAdd = FALSE; + } + + if (Context->AllocatedColumns < Context->NextId) + { + PhTnpExpandAllocatedColumns(Context); + } + + Context->Columns[Column->Id] = realColumn; + Context->NumberOfColumns++; + + if (realColumn->Fixed) + { + if (Context->FixedColumn) + { + // We already have a fixed column, and we can't have two. Make this new column un-fixed. + realColumn->Fixed = FALSE; + } + else + { + Context->FixedColumn = realColumn; + } + + realColumn->DisplayIndex = 0; + realColumn->s.ViewX = 0; + } + + if (realColumn->Visible) + { + BOOLEAN updateHeaders; + + updateHeaders = FALSE; + + if (!realColumn->Fixed && realColumn->DisplayIndex != Header_GetItemCount(Context->HeaderHandle)) + updateHeaders = TRUE; + + realColumn->s.ViewIndex = PhTnpInsertColumnHeader(Context, realColumn); + + if (updateHeaders) + PhTnpUpdateColumnHeaders(Context); + } + else + { + realColumn->s.ViewIndex = -1; + } + + PhTnpUpdateColumnMaps(Context); + + if (realColumn->Visible) + PhTnpLayout(Context); + + return TRUE; +} + +BOOLEAN PhTnpRemoveColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id + ) +{ + PPH_TREENEW_COLUMN realColumn; + BOOLEAN updateLayout; + + if (!(realColumn = PhTnpLookupColumnById(Context, Id))) + return FALSE; + + updateLayout = FALSE; + + if (realColumn->Visible) + updateLayout = TRUE; + + PhTnpDeleteColumnHeader(Context, realColumn); + Context->Columns[realColumn->Id] = NULL; + PhFree(realColumn); + PhTnpUpdateColumnMaps(Context); + + if (updateLayout) + PhTnpLayout(Context); + + Context->NumberOfColumns--; + + return TRUE; +} + +BOOLEAN PhTnpCopyColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Id, + _Out_ PPH_TREENEW_COLUMN Column + ) +{ + PPH_TREENEW_COLUMN realColumn; + + if (!(realColumn = PhTnpLookupColumnById(Context, Id))) + return FALSE; + + memcpy(Column, realColumn, sizeof(PH_TREENEW_COLUMN)); + + return TRUE; +} + +BOOLEAN PhTnpChangeColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Mask, + _In_ ULONG Id, + _In_ PPH_TREENEW_COLUMN Column + ) +{ + PPH_TREENEW_COLUMN realColumn; + BOOLEAN addingOrRemoving; + + if (!(realColumn = PhTnpLookupColumnById(Context, Id))) + return FALSE; + + addingOrRemoving = FALSE; + + if (Mask & TN_COLUMN_FLAG_VISIBLE) + { + if (realColumn->Visible != Column->Visible) + { + addingOrRemoving = TRUE; + } + } + + if (Mask & TN_COLUMN_FLAG_CUSTOMDRAW) + { + realColumn->CustomDraw = Column->CustomDraw; + } + + if (Mask & TN_COLUMN_FLAG_SORTDESCENDING) + { + realColumn->SortDescending = Column->SortDescending; + } + + if (Mask & (TN_COLUMN_TEXT | TN_COLUMN_WIDTH | TN_COLUMN_ALIGNMENT | TN_COLUMN_DISPLAYINDEX)) + { + BOOLEAN updateHeaders; + BOOLEAN updateMaps; + BOOLEAN updateLayout; + + updateHeaders = FALSE; + updateMaps = FALSE; + updateLayout = FALSE; + + if (Mask & TN_COLUMN_TEXT) + { + realColumn->Text = Column->Text; + } + + if (Mask & TN_COLUMN_WIDTH) + { + realColumn->Width = Column->Width; + updateMaps = TRUE; + } + + if (Mask & TN_COLUMN_ALIGNMENT) + { + realColumn->Alignment = Column->Alignment; + } + + if (Mask & TN_COLUMN_DISPLAYINDEX) + { + realColumn->DisplayIndex = Column->DisplayIndex; + updateHeaders = TRUE; + updateMaps = TRUE; + updateLayout = TRUE; + } + + if (!addingOrRemoving && realColumn->Visible) + { + PhTnpChangeColumnHeader(Context, Mask, realColumn); + + if (updateHeaders) + PhTnpUpdateColumnHeaders(Context); + if (updateMaps) + PhTnpUpdateColumnMaps(Context); + if (updateLayout) + PhTnpLayout(Context); + } + } + + if (Mask & TN_COLUMN_CONTEXT) + { + realColumn->Context = Column->Context; + } + + if (Mask & TN_COLUMN_TEXTFLAGS) + { + realColumn->TextFlags = Column->TextFlags; + } + + if (addingOrRemoving) + { + if (Column->Visible) + { + BOOLEAN updateHeaders; + + updateHeaders = FALSE; + + if (realColumn->Fixed) + { + realColumn->DisplayIndex = 0; + } + else + { + if (Mask & TN_COLUMN_DISPLAYINDEX) + updateHeaders = TRUE; + else + realColumn->DisplayIndex = Header_GetItemCount(Context->HeaderHandle); + } + + realColumn->s.ViewIndex = PhTnpInsertColumnHeader(Context, realColumn); + + if (updateHeaders) + PhTnpUpdateColumnHeaders(Context); + } + else + { + PhTnpDeleteColumnHeader(Context, realColumn); + } + + PhTnpUpdateColumnMaps(Context); + PhTnpLayout(Context); + } + + return TRUE; +} + +VOID PhTnpExpandAllocatedColumns( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + if (Context->Columns) + { + ULONG oldAllocatedColumns; + + oldAllocatedColumns = Context->AllocatedColumns; + Context->AllocatedColumns *= 2; + + if (Context->AllocatedColumns < Context->NextId) + Context->AllocatedColumns = Context->NextId; + + Context->Columns = PhReAllocate( + Context->Columns, + Context->AllocatedColumns * sizeof(PPH_TREENEW_COLUMN) + ); + + // Zero the newly allocated portion. + memset( + &Context->Columns[oldAllocatedColumns], + 0, + (Context->AllocatedColumns - oldAllocatedColumns) * sizeof(PPH_TREENEW_COLUMN) + ); + } + else + { + Context->AllocatedColumns = 16; + + if (Context->AllocatedColumns < Context->NextId) + Context->AllocatedColumns = Context->NextId; + + Context->Columns = PhAllocate( + Context->AllocatedColumns * sizeof(PPH_TREENEW_COLUMN) + ); + memset(Context->Columns, 0, Context->AllocatedColumns * sizeof(PPH_TREENEW_COLUMN)); + } +} + +VOID PhTnpUpdateColumnMaps( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + ULONG i; + LONG x; + + if (Context->AllocatedColumnsByDisplay < Context->NumberOfColumns) + { + if (Context->ColumnsByDisplay) + PhFree(Context->ColumnsByDisplay); + + Context->ColumnsByDisplay = PhAllocate(sizeof(PPH_TREENEW_COLUMN) * Context->NumberOfColumns); + Context->AllocatedColumnsByDisplay = Context->NumberOfColumns; + } + + memset(Context->ColumnsByDisplay, 0, sizeof(PPH_TREENEW_COLUMN) * Context->AllocatedColumnsByDisplay); + + for (i = 0; i < Context->NextId; i++) + { + if (!Context->Columns[i]) + continue; + + if (Context->Columns[i]->Visible && !Context->Columns[i]->Fixed && Context->Columns[i]->DisplayIndex != -1) + { + if (Context->Columns[i]->DisplayIndex >= Context->NumberOfColumns) + PhRaiseStatus(STATUS_INTERNAL_ERROR); + + Context->ColumnsByDisplay[Context->Columns[i]->DisplayIndex] = Context->Columns[i]; + } + } + + x = 0; + + for (i = 0; i < Context->AllocatedColumnsByDisplay; i++) + { + if (!Context->ColumnsByDisplay[i]) + break; + + Context->ColumnsByDisplay[i]->s.ViewX = x; + x += Context->ColumnsByDisplay[i]->Width; + } + + Context->NumberOfColumnsByDisplay = i; + Context->TotalViewX = x; + + if (Context->FixedColumnVisible) + Context->FirstColumn = Context->FixedColumn; + else if (Context->NumberOfColumnsByDisplay != 0) + Context->FirstColumn = Context->ColumnsByDisplay[0]; + else + Context->FirstColumn = NULL; + + if (Context->NumberOfColumnsByDisplay != 0) + Context->LastColumn = Context->ColumnsByDisplay[Context->NumberOfColumnsByDisplay - 1]; + else if (Context->FixedColumnVisible) + Context->LastColumn = Context->FixedColumn; + else + Context->LastColumn = NULL; +} + +LONG PhTnpInsertColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN Column + ) +{ + HDITEM item; + + if (Column->Fixed) + { + if (Column->Width < Context->FixedWidthMinimum) + Column->Width = Context->FixedWidthMinimum; + + Context->FixedWidth = Column->Width; + Context->NormalLeft = Context->FixedWidth + 1; + Context->FixedColumnVisible = TRUE; + + if (!(Context->Style & TN_STYLE_NO_DIVIDER)) + Context->FixedDividerVisible = TRUE; + } + + memset(&item, 0, sizeof(HDITEM)); + item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT | HDI_LPARAM | HDI_ORDER; + item.cxy = Column->Width; + item.pszText = Column->Text; + item.fmt = 0; + item.lParam = (LPARAM)Column; + + if (Column->Fixed) + item.cxy++; + + if (Column->Fixed) + item.iOrder = 0; + else + item.iOrder = Column->DisplayIndex; + + if (Column->Alignment & PH_ALIGN_LEFT) + item.fmt |= HDF_LEFT; + else if (Column->Alignment & PH_ALIGN_RIGHT) + item.fmt |= HDF_RIGHT; + else + item.fmt |= HDF_CENTER; + + if (Column->Id == Context->SortColumn) + { + if (Context->SortOrder == AscendingSortOrder) + item.fmt |= HDF_SORTUP; + else if (Context->SortOrder == DescendingSortOrder) + item.fmt |= HDF_SORTDOWN; + } + + Column->Visible = TRUE; + + if (Column->Fixed) + return Header_InsertItem(Context->FixedHeaderHandle, 0, &item); + else + return Header_InsertItem(Context->HeaderHandle, MAXINT, &item); +} + +VOID PhTnpChangeColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Mask, + _In_ PPH_TREENEW_COLUMN Column + ) +{ + HDITEM item; + + memset(&item, 0, sizeof(HDITEM)); + item.mask = 0; + + if (Mask & TN_COLUMN_TEXT) + { + item.mask |= HDI_TEXT; + item.pszText = Column->Text; + } + + if (Mask & TN_COLUMN_WIDTH) + { + item.mask |= HDI_WIDTH; + item.cxy = Column->Width; + + if (Column->Fixed) + item.cxy++; + } + + if (Mask & TN_COLUMN_ALIGNMENT) + { + item.mask |= HDI_FORMAT; + item.fmt = 0; + + if (Column->Alignment & PH_ALIGN_LEFT) + item.fmt |= HDF_LEFT; + else if (Column->Alignment & PH_ALIGN_RIGHT) + item.fmt |= HDF_RIGHT; + else + item.fmt |= HDF_CENTER; + + if (Column->Id == Context->SortColumn) + { + if (Context->SortOrder == AscendingSortOrder) + item.fmt |= HDF_SORTUP; + else if (Context->SortOrder == DescendingSortOrder) + item.fmt |= HDF_SORTDOWN; + } + } + + if (Mask & TN_COLUMN_DISPLAYINDEX) + { + item.mask |= HDI_ORDER; + + if (Column->Fixed) + item.iOrder = 0; + else + item.iOrder = Column->DisplayIndex; + } + + if (Column->Fixed) + Header_SetItem(Context->FixedHeaderHandle, 0, &item); + else + Header_SetItem(Context->HeaderHandle, Column->s.ViewIndex, &item); +} + +VOID PhTnpDeleteColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _Inout_ PPH_TREENEW_COLUMN Column + ) +{ + if (Column->Fixed) + { + Context->FixedColumn = NULL; + Context->FixedWidth = 0; + Context->NormalLeft = 0; + Context->FixedColumnVisible = FALSE; + Context->FixedDividerVisible = FALSE; + } + + if (Column->Fixed) + Header_DeleteItem(Context->FixedHeaderHandle, Column->s.ViewIndex); + else + Header_DeleteItem(Context->HeaderHandle, Column->s.ViewIndex); + + Column->Visible = FALSE; + Column->s.ViewIndex = -1; + PhTnpUpdateColumnHeaders(Context); +} + +VOID PhTnpUpdateColumnHeaders( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + ULONG count; + ULONG i; + HDITEM item; + PPH_TREENEW_COLUMN column; + + item.mask = HDI_WIDTH | HDI_LPARAM | HDI_ORDER; + + // Fixed column + + if (Context->FixedColumnVisible && Header_GetItem(Context->FixedHeaderHandle, 0, &item)) + { + column = Context->FixedColumn; + column->Width = item.cxy - 1; + } + + // Normal columns + + count = Header_GetItemCount(Context->HeaderHandle); + + if (count != -1) + { + for (i = 0; i < count; i++) + { + if (Header_GetItem(Context->HeaderHandle, i, &item)) + { + column = (PPH_TREENEW_COLUMN)item.lParam; + column->s.ViewIndex = i; + column->Width = item.cxy; + column->DisplayIndex = item.iOrder; + } + } + } +} + +VOID PhTnpProcessResizeColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN Column, + _In_ LONG Delta + ) +{ + RECT rect; + LONG columnLeft; + + if (Column->Fixed) + { + columnLeft = 0; + } + else + { + columnLeft = Context->NormalLeft + Column->s.ViewX - Context->HScrollPosition; + } + + // Scroll the content to the right of the column. + // + // Clip the scroll area to the new width, or the old width if that is further to the left. We + // may have the WS_CLIPCHILDREN style set, so we need to remove the horizontal scrollbar from + // the rectangle, otherwise ScrollWindowEx will want to invalidate the entire region! (The + // horizontal scrollbar is an overlapping child control.) + rect.left = columnLeft + Column->Width; + rect.top = Context->HeaderHeight; + rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + rect.bottom = Context->ClientRect.bottom - (Context->HScrollVisible ? Context->HScrollHeight : 0); + + if (Delta > 0) + rect.left -= Delta; // old width + + // Scroll the window. + ScrollWindowEx( + Context->Handle, + Delta, + 0, + &rect, + &rect, + NULL, + NULL, + SW_INVALIDATE + ); + + UpdateWindow(Context->Handle); // required + + if (Context->HScrollVisible) + { + // We excluded the bottom region - invalidate it now. + rect.top = rect.bottom; + rect.bottom = Context->ClientRect.bottom; + InvalidateRect(Context->Handle, &rect, FALSE); + } + + PhTnpLayout(Context); + + // Redraw the whole column because the content may depend on the width (e.g. text ellipsis). + rect.left = columnLeft; + rect.top = Context->HeaderHeight; + rect.right = columnLeft + Column->Width; + RedrawWindow(Context->Handle, &rect, NULL, RDW_INVALIDATE | RDW_UPDATENOW); // must be RedrawWindow +} + +VOID PhTnpProcessSortColumn( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_COLUMN NewColumn + ) +{ + if (NewColumn->Id == Context->SortColumn) + { + if (Context->TriState) + { + if (!NewColumn->SortDescending) + { + // Ascending -> Descending -> None + + if (Context->SortOrder == AscendingSortOrder) + Context->SortOrder = DescendingSortOrder; + else if (Context->SortOrder == DescendingSortOrder) + Context->SortOrder = NoSortOrder; + else + Context->SortOrder = AscendingSortOrder; + } + else + { + // Descending -> Ascending -> None + + if (Context->SortOrder == DescendingSortOrder) + Context->SortOrder = AscendingSortOrder; + else if (Context->SortOrder == AscendingSortOrder) + Context->SortOrder = NoSortOrder; + else + Context->SortOrder = DescendingSortOrder; + } + } + else + { + if (Context->SortOrder == AscendingSortOrder) + Context->SortOrder = DescendingSortOrder; + else + Context->SortOrder = AscendingSortOrder; + } + } + else + { + Context->SortColumn = NewColumn->Id; + + if (!NewColumn->SortDescending) + Context->SortOrder = AscendingSortOrder; + else + Context->SortOrder = DescendingSortOrder; + } + + PhTnpSetColumnHeaderSortIcon(Context, NewColumn); + + Context->Callback(Context->Handle, TreeNewSortChanged, NULL, NULL, Context->CallbackContext); +} + +BOOLEAN PhTnpSetColumnHeaderSortIcon( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ PPH_TREENEW_COLUMN SortColumnPointer + ) +{ + if (Context->SortOrder == NoSortOrder) + { + PhSetHeaderSortIcon( + Context->FixedHeaderHandle, + -1, + NoSortOrder + ); + PhSetHeaderSortIcon( + Context->HeaderHandle, + -1, + NoSortOrder + ); + + return TRUE; + } + + if (!SortColumnPointer) + { + if (!(SortColumnPointer = PhTnpLookupColumnById(Context, Context->SortColumn))) + return FALSE; + } + + if (SortColumnPointer->Fixed) + { + PhSetHeaderSortIcon( + Context->FixedHeaderHandle, + 0, + Context->SortOrder + ); + PhSetHeaderSortIcon( + Context->HeaderHandle, + -1, + NoSortOrder + ); + } + else + { + PhSetHeaderSortIcon( + Context->FixedHeaderHandle, + -1, + NoSortOrder + ); + PhSetHeaderSortIcon( + Context->HeaderHandle, + SortColumnPointer->s.ViewIndex, + Context->SortOrder + ); + } + + return TRUE; +} + +VOID PhTnpAutoSizeColumnHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HWND HeaderHandle, + _In_ PPH_TREENEW_COLUMN Column, + _In_ ULONG Flags + ) +{ + LONG newWidth; + HDITEM item; + + if (Flags & TN_AUTOSIZE_REMAINING_SPACE) + { + newWidth = Context->ClientRect.right - (Context->TotalViewX - Column->Width); + + if (Context->FixedColumn) + newWidth -= Context->FixedColumn->Width; + if (Context->VScrollVisible) + newWidth -= Context->VScrollWidth; + + if (newWidth <= 0) + return; + } + else + { + ULONG i; + LONG maximumWidth; + PH_TREENEW_CELL_PARTS parts; + LONG width; + + if (Context->FlatList->Count == 0) + return; + if (Column->CustomDraw) + return; + + maximumWidth = 0; + + for (i = 0; i < Context->FlatList->Count; i++) + { + if (PhTnpGetCellParts(Context, i, Column, TN_MEASURE_TEXT, &parts) && + (parts.Flags & TN_PART_CELL) && (parts.Flags & TN_PART_CONTENT) && (parts.Flags & TN_PART_TEXT)) + { + width = parts.TextRect.right - parts.TextRect.left; // text width + width += parts.ContentRect.left - parts.CellRect.left; // left padding + + if (maximumWidth < width) + maximumWidth = width; + } + } + + newWidth = maximumWidth + TNP_CELL_RIGHT_MARGIN; // right padding + + if (Column->Fixed) + newWidth++; + + // Check the column header text width. + if (Column->Text) + { + PWSTR text; + SIZE_T textCount; + HDC hdc; + SIZE textSize; + + text = Column->Text; + textCount = PhCountStringZ(text); + + if (hdc = GetDC(Context->Handle)) + { + SelectFont(hdc, Context->Font); + + if (GetTextExtentPoint32(hdc, text, (ULONG)textCount, &textSize)) + { + if (newWidth < textSize.cx + 6 + 6) // HACK: Magic values (same as our cell margins?) + newWidth = textSize.cx + 6 + 6; + } + + ReleaseDC(Context->Handle, hdc); + } + } + } + + item.mask = HDI_WIDTH; + item.cxy = newWidth; + + Header_SetItem(HeaderHandle, Column->s.ViewIndex, &item); +} + +BOOLEAN PhTnpGetNodeChildren( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ PPH_TREENEW_NODE Node, + _Out_ PPH_TREENEW_NODE **Children, + _Out_ PULONG NumberOfChildren + ) +{ + PH_TREENEW_GET_CHILDREN getChildren; + + getChildren.Flags = 0; + getChildren.Node = Node; + getChildren.Children = NULL; + getChildren.NumberOfChildren = 0; + + if (Context->Callback( + Context->Handle, + TreeNewGetChildren, + &getChildren, + NULL, + Context->CallbackContext + )) + { + *Children = getChildren.Children; + *NumberOfChildren = getChildren.NumberOfChildren; + + return TRUE; + } + + return FALSE; +} + +BOOLEAN PhTnpIsNodeLeaf( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node + ) +{ + PH_TREENEW_IS_LEAF isLeaf; + + isLeaf.Flags = 0; + isLeaf.Node = Node; + isLeaf.IsLeaf = TRUE; + + if (Context->Callback( + Context->Handle, + TreeNewIsLeaf, + &isLeaf, + NULL, + Context->CallbackContext + )) + { + return isLeaf.IsLeaf; + } + + // Doesn't matter, decide when we do the get-children callback. + return FALSE; +} + +BOOLEAN PhTnpGetCellText( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ ULONG Id, + _Out_ PPH_STRINGREF Text + ) +{ + PH_TREENEW_GET_CELL_TEXT getCellText; + + if (Id < Node->TextCacheSize && Node->TextCache[Id].Buffer) + { + *Text = Node->TextCache[Id]; + return TRUE; + } + + getCellText.Flags = 0; + getCellText.Node = Node; + getCellText.Id = Id; + PhInitializeEmptyStringRef(&getCellText.Text); + + if (Context->Callback( + Context->Handle, + TreeNewGetCellText, + &getCellText, + NULL, + Context->CallbackContext + ) && getCellText.Text.Buffer) + { + *Text = getCellText.Text; + + if ((getCellText.Flags & TN_CACHE) && Id < Node->TextCacheSize) + Node->TextCache[Id] = getCellText.Text; + + return TRUE; + } + + return FALSE; +} + +VOID PhTnpRestructureNodes( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + PPH_TREENEW_NODE *children; + ULONG numberOfChildren; + ULONG i; + + if (!PhTnpGetNodeChildren(Context, NULL, &children, &numberOfChildren)) + return; + + // We try to preserve the hot node, the focused node and the selection mark node. At this point + // all node pointers must be regarded as invalid, so we must not follow any pointers. + + Context->FocusNodeFound = FALSE; + + PhClearList(Context->FlatList); + Context->CanAnyExpand = FALSE; + + for (i = 0; i < numberOfChildren; i++) + { + PhTnpInsertNodeChildren(Context, children[i], 0); + } + + if (!Context->FocusNodeFound) + Context->FocusNode = NULL; // focused node is no longer present + + if (Context->HotNodeIndex >= Context->FlatList->Count) // covers -1 case as well + Context->HotNodeIndex = -1; + + if (Context->MarkNodeIndex >= Context->FlatList->Count) + Context->MarkNodeIndex = -1; +} + +VOID PhTnpInsertNodeChildren( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ ULONG Level + ) +{ + PPH_TREENEW_NODE *children; + ULONG numberOfChildren; + ULONG i; + ULONG nextLevel; + + if (Node->Visible) + { + Node->Level = Level; + + Node->Index = Context->FlatList->Count; + PhAddItemList(Context->FlatList, Node); + + if (Context->FocusNode == Node) + Context->FocusNodeFound = TRUE; + + nextLevel = Level + 1; + } + else + { + nextLevel = 0; // children of this node should be level 0 + } + + if (!(Node->s.IsLeaf = PhTnpIsNodeLeaf(Context, Node))) + { + Context->CanAnyExpand = TRUE; + + if (Node->Expanded) + { + if (PhTnpGetNodeChildren(Context, Node, &children, &numberOfChildren)) + { + for (i = 0; i < numberOfChildren; i++) + { + PhTnpInsertNodeChildren(Context, children[i], nextLevel); + } + + if (numberOfChildren == 0) + Node->s.IsLeaf = TRUE; + } + } + } +} + +VOID PhTnpSetExpandedNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ BOOLEAN Expanded + ) +{ + if (Node->Expanded != Expanded) + { + PH_TREENEW_NODE_EVENT nodeEvent; + + memset(&nodeEvent, 0, sizeof(PH_TREENEW_NODE_EVENT)); + Context->Callback(Context->Handle, TreeNewNodeExpanding, Node, &nodeEvent, Context->CallbackContext); + + if (!nodeEvent.Handled) + { + if (!Expanded) + { + ULONG i; + PPH_TREENEW_NODE node; + BOOLEAN changed; + + // Make sure no children are selected - we don't want invisible selected nodes. Note + // that this does not cause any UI changes by itself, since we are hiding the nodes. + + changed = FALSE; + + for (i = Node->Index + 1; i < Context->FlatList->Count; i++) + { + node = Context->FlatList->Items[i]; + + if (node->Level <= Node->Level) + break; // no more children + + if (node->Selected) + { + node->Selected = FALSE; + changed = TRUE; + } + } + + if (changed) + { + Context->Callback(Context->Handle, TreeNewSelectionChanged, NULL, NULL, Context->CallbackContext); + } + } + + Node->Expanded = Expanded; + PhTnpRestructureNodes(Context); + // We need to update the window before the scrollbars get updated in order for the + // scroll processing to work properly. + InvalidateRect(Context->Handle, NULL, FALSE); + UpdateWindow(Context->Handle); + PhTnpLayout(Context); + } + } +} + +BOOLEAN PhTnpGetCellParts( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Index, + _In_opt_ PPH_TREENEW_COLUMN Column, + _In_ ULONG Flags, + _Out_ PPH_TREENEW_CELL_PARTS Parts + ) +{ + PPH_TREENEW_NODE node; + LONG viewWidth; + LONG nodeY; + LONG iconVerticalMargin; + LONG currentX; + + if (Index >= Context->FlatList->Count) + return FALSE; + + node = Context->FlatList->Items[Index]; + nodeY = Context->HeaderHeight + ((LONG)Index - Context->VScrollPosition) * Context->RowHeight; + + Parts->Flags = 0; + Parts->RowRect.left = 0; + Parts->RowRect.right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; + Parts->RowRect.top = nodeY; + Parts->RowRect.bottom = nodeY + Context->RowHeight; + + viewWidth = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + + if (Parts->RowRect.right > viewWidth) + Parts->RowRect.right = viewWidth; + + if (!Column) + return TRUE; + if (!Column->Visible) + return FALSE; + + iconVerticalMargin = (Context->RowHeight - SmallIconHeight) / 2; + + if (Column->Fixed) + { + currentX = 0; + } + else + { + currentX = Context->NormalLeft + Column->s.ViewX - Context->HScrollPosition; + } + + Parts->Flags |= TN_PART_CELL; + Parts->CellRect.left = currentX; + Parts->CellRect.right = currentX + Column->Width; + Parts->CellRect.top = Parts->RowRect.top; + Parts->CellRect.bottom = Parts->RowRect.bottom; + + currentX += TNP_CELL_LEFT_MARGIN; + + if (Column == Context->FirstColumn) + { + currentX += (LONG)node->Level * SmallIconWidth; + + if (Context->CanAnyExpand) + { + if (!node->s.IsLeaf) + { + Parts->Flags |= TN_PART_PLUSMINUS; + Parts->PlusMinusRect.left = currentX; + Parts->PlusMinusRect.right = currentX + SmallIconWidth; + Parts->PlusMinusRect.top = Parts->RowRect.top + iconVerticalMargin; + Parts->PlusMinusRect.bottom = Parts->RowRect.bottom - iconVerticalMargin; + } + + currentX += SmallIconWidth; + } + + if (node->Icon) + { + Parts->Flags |= TN_PART_ICON; + Parts->IconRect.left = currentX; + Parts->IconRect.right = currentX + SmallIconWidth; + Parts->IconRect.top = Parts->RowRect.top + iconVerticalMargin; + Parts->IconRect.bottom = Parts->RowRect.bottom - iconVerticalMargin; + + currentX += SmallIconWidth + TNP_ICON_RIGHT_PADDING; + } + } + + Parts->Flags |= TN_PART_CONTENT; + Parts->ContentRect.left = currentX; + Parts->ContentRect.right = Parts->CellRect.right - TNP_CELL_RIGHT_MARGIN; + Parts->ContentRect.top = Parts->RowRect.top; + Parts->ContentRect.bottom = Parts->RowRect.bottom; + + if (Flags & TN_MEASURE_TEXT) + { + HDC hdc; + PH_STRINGREF text; + HFONT font; + SIZE textSize; + + if (hdc = GetDC(Context->Handle)) + { + PhTnpPrepareRowForDraw(Context, hdc, node); + + if (PhTnpGetCellText(Context, node, Column->Id, &text)) + { + if (node->Font) + font = node->Font; + else + font = Context->Font; + + SelectFont(hdc, font); + + if (GetTextExtentPoint32(hdc, text.Buffer, (ULONG)text.Length / sizeof(WCHAR), &textSize)) + { + Parts->Flags |= TN_PART_TEXT; + Parts->TextRect.left = currentX; + Parts->TextRect.right = currentX + textSize.cx; + Parts->TextRect.top = Parts->RowRect.top + (Context->RowHeight - textSize.cy) / 2; + Parts->TextRect.bottom = Parts->RowRect.bottom - (Context->RowHeight - textSize.cy) / 2; + + if (Column->TextFlags & DT_CENTER) + { + Parts->TextRect.left = Parts->ContentRect.left / 2 + (Parts->ContentRect.right - textSize.cx) / 2; + Parts->TextRect.right = Parts->ContentRect.left + textSize.cx; + } + else if (Column->TextFlags & DT_RIGHT) + { + Parts->TextRect.right = Parts->ContentRect.right; + Parts->TextRect.left = Parts->TextRect.right - textSize.cx; + } + + Parts->Text = text; + Parts->Font = font; + } + } + + ReleaseDC(Context->Handle, hdc); + } + } + + return TRUE; +} + +BOOLEAN PhTnpGetRowRects( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Start, + _In_ ULONG End, + _In_ BOOLEAN Clip, + _Out_ PRECT Rect + ) +{ + LONG startY; + LONG endY; + LONG viewWidth; + + if (End >= Context->FlatList->Count) + return FALSE; + if (Start > End) + return FALSE; + + startY = Context->HeaderHeight + ((LONG)Start - Context->VScrollPosition) * Context->RowHeight; + endY = Context->HeaderHeight + ((LONG)End - Context->VScrollPosition) * Context->RowHeight; + + Rect->left = 0; + Rect->right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; + Rect->top = startY; + Rect->bottom = endY + Context->RowHeight; + + viewWidth = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + + if (Rect->right > viewWidth) + Rect->right = viewWidth; + + if (Clip) + { + if (Rect->top < Context->HeaderHeight) + Rect->top = Context->HeaderHeight; + if (Rect->bottom > Context->ClientRect.bottom) + Rect->bottom = Context->ClientRect.bottom; + } + + return TRUE; +} + +VOID PhTnpHitTest( + _In_ PPH_TREENEW_CONTEXT Context, + _Inout_ PPH_TREENEW_HIT_TEST HitTest + ) +{ + RECT clientRect; + LONG x; + LONG y; + ULONG index; + PPH_TREENEW_NODE node; + + HitTest->Flags = 0; + HitTest->Node = NULL; + HitTest->Column = NULL; + + clientRect = Context->ClientRect; + x = HitTest->Point.x; + y = HitTest->Point.y; + + if (x < 0) + HitTest->Flags |= TN_HIT_LEFT; + if (x >= clientRect.right) + HitTest->Flags |= TN_HIT_RIGHT; + if (y < 0) + HitTest->Flags |= TN_HIT_ABOVE; + if (y >= clientRect.bottom) + HitTest->Flags |= TN_HIT_BELOW; + + if (HitTest->Flags == 0) + { + if (TNP_HIT_TEST_FIXED_DIVIDER(x, Context)) + { + HitTest->Flags |= TN_HIT_DIVIDER; + } + + if (y >= Context->HeaderHeight && x < Context->FixedWidth + Context->TotalViewX) + { + index = (y - Context->HeaderHeight) / Context->RowHeight + Context->VScrollPosition; + + if (index < Context->FlatList->Count) + { + HitTest->Flags |= TN_HIT_ITEM; + node = Context->FlatList->Items[index]; + HitTest->Node = node; + + if (HitTest->InFlags & TN_TEST_COLUMN) + { + PPH_TREENEW_COLUMN column; + LONG columnX; + + column = NULL; + + if (x < Context->FixedWidth && Context->FixedColumnVisible) + { + column = Context->FixedColumn; + columnX = 0; + } + else + { + LONG currentX; + ULONG i; + PPH_TREENEW_COLUMN currentColumn; + + currentX = Context->NormalLeft - Context->HScrollPosition; + + for (i = 0; i < Context->NumberOfColumnsByDisplay; i++) + { + currentColumn = Context->ColumnsByDisplay[i]; + + if (x >= currentX && x < currentX + currentColumn->Width) + { + column = currentColumn; + columnX = currentX; + break; + } + + currentX += currentColumn->Width; + } + } + + HitTest->Column = column; + + if (column && (HitTest->InFlags & TN_TEST_SUBITEM)) + { + BOOLEAN isFirstColumn; + LONG currentX; + + isFirstColumn = HitTest->Column == Context->FirstColumn; + + currentX = columnX; + currentX += TNP_CELL_LEFT_MARGIN; + + if (isFirstColumn) + { + currentX += (LONG)node->Level * SmallIconWidth; + + if (!node->s.IsLeaf) + { + if (x >= currentX && x < currentX + SmallIconWidth) + HitTest->Flags |= TN_HIT_ITEM_PLUSMINUS; + + currentX += SmallIconWidth; + } + + if (node->Icon) + { + if (x >= currentX && x < currentX + SmallIconWidth) + HitTest->Flags |= TN_HIT_ITEM_ICON; + + currentX += SmallIconWidth + TNP_ICON_RIGHT_PADDING; + } + } + + if (x >= currentX) + { + HitTest->Flags |= TN_HIT_ITEM_CONTENT; + } + } + } + } + } + } +} + +VOID PhTnpSelectRange( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Start, + _In_ ULONG End, + _In_ ULONG Flags, + _Out_opt_ PULONG ChangedStart, + _Out_opt_ PULONG ChangedEnd + ) +{ + ULONG maximum; + ULONG i; + PPH_TREENEW_NODE node; + BOOLEAN targetValue; + ULONG changedStart; + ULONG changedEnd; + + if (Context->FlatList->Count == 0) + return; + + maximum = Context->FlatList->Count - 1; + + if (End > maximum) + { + End = maximum; + } + + if (Start > End) + { + // Start is too big, so the selection range becomes empty. + // Set it to max + 1 so that Reset still works. + Start = maximum + 1; + End = 0; + } + + targetValue = !(Flags & TN_SELECT_DESELECT); + changedStart = maximum; + changedEnd = 0; + + if (Flags & TN_SELECT_RESET) + { + for (i = 0; i < Start; i++) + { + node = Context->FlatList->Items[i]; + + if (node->Selected) + { + node->Selected = FALSE; + + if (changedStart > i) + changedStart = i; + if (changedEnd < i) + changedEnd = i; + } + } + } + + for (i = Start; i <= End; i++) + { + node = Context->FlatList->Items[i]; + + if (!node->Unselectable && ((Flags & TN_SELECT_TOGGLE) || node->Selected != targetValue)) + { + node->Selected = !node->Selected; + + if (changedStart > i) + changedStart = i; + if (changedEnd < i) + changedEnd = i; + } + } + + if (Flags & TN_SELECT_RESET) + { + for (i = End + 1; i <= maximum; i++) + { + node = Context->FlatList->Items[i]; + + if (node->Selected) + { + node->Selected = FALSE; + + if (changedStart > i) + changedStart = i; + if (changedEnd < i) + changedEnd = i; + } + } + } + + if (changedStart <= changedEnd) + { + Context->Callback(Context->Handle, TreeNewSelectionChanged, NULL, NULL, Context->CallbackContext); + } + + if (ChangedStart) + *ChangedStart = changedStart; + if (ChangedEnd) + *ChangedEnd = changedEnd; +} + +VOID PhTnpSetHotNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_opt_ PPH_TREENEW_NODE NewHotNode, + _In_ BOOLEAN NewPlusMinusHot + ) +{ + ULONG newHotNodeIndex; + RECT rowRect; + BOOLEAN needsInvalidate; + + if (NewHotNode) + newHotNodeIndex = NewHotNode->Index; + else + newHotNodeIndex = -1; + + needsInvalidate = FALSE; + + if (Context->HotNodeIndex != newHotNodeIndex) + { + if (Context->HotNodeIndex != -1) + { + if (Context->ThemeData && PhTnpGetRowRects(Context, Context->HotNodeIndex, Context->HotNodeIndex, TRUE, &rowRect)) + { + // Update the old hot node because it may have a different non-hot background and + // plus minus part. + InvalidateRect(Context->Handle, &rowRect, FALSE); + } + } + + Context->HotNodeIndex = newHotNodeIndex; + + if (NewHotNode) + { + needsInvalidate = TRUE; + } + } + + if (NewHotNode) + { + if (NewHotNode->s.PlusMinusHot != NewPlusMinusHot) + { + NewHotNode->s.PlusMinusHot = NewPlusMinusHot; + needsInvalidate = TRUE; + } + + if (needsInvalidate && Context->ThemeData && PhTnpGetRowRects(Context, newHotNodeIndex, newHotNodeIndex, TRUE, &rowRect)) + { + InvalidateRect(Context->Handle, &rowRect, FALSE); + } + } +} + +VOID PhTnpProcessSelectNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPH_TREENEW_NODE Node, + _In_ LOGICAL ControlKey, + _In_ LOGICAL ShiftKey, + _In_ LOGICAL RightButton + ) +{ + ULONG changedStart; + ULONG changedEnd; + RECT rect; + + if (RightButton) + { + // Right button: + // If the current node is selected, then do nothing. This is to allow context menus to + // operate on multiple items. + // If the current node is not selected, select only that node. + + if (!ControlKey && !ShiftKey && !Node->Selected) + { + PhTnpSelectRange(Context, Node->Index, Node->Index, TN_SELECT_RESET, &changedStart, &changedEnd); + Context->MarkNodeIndex = Node->Index; + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + } + else if (ShiftKey && Context->MarkNodeIndex != -1) + { + ULONG start; + ULONG end; + + // Shift key: select a range from the selection mark node to the current node. + + if (Node->Index > Context->MarkNodeIndex) + { + start = Context->MarkNodeIndex; + end = Node->Index; + } + else + { + start = Node->Index; + end = Context->MarkNodeIndex; + } + + PhTnpSelectRange(Context, start, end, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + else if (ControlKey) + { + // Control key: toggle the selection on the current node, and also make it the selection + // mark. + + PhTnpSelectRange(Context, Node->Index, Node->Index, TN_SELECT_TOGGLE, NULL, NULL); + Context->MarkNodeIndex = Node->Index; + + if (PhTnpGetRowRects(Context, Node->Index, Node->Index, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + else + { + // Normal: select the current node, and also make it the selection mark. + + PhTnpSelectRange(Context, Node->Index, Node->Index, TN_SELECT_RESET, &changedStart, &changedEnd); + Context->MarkNodeIndex = Node->Index; + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } +} + +BOOLEAN PhTnpEnsureVisibleNode( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Index + ) +{ + LONG viewTop; + LONG viewBottom; + LONG rowTop; + LONG rowBottom; + LONG deltaY; + LONG deltaRows; + + if (Index >= Context->FlatList->Count) + return FALSE; + + viewTop = Context->HeaderHeight; + viewBottom = Context->ClientRect.bottom - (Context->HScrollVisible ? Context->HScrollHeight : 0); + rowTop = Context->HeaderHeight + ((LONG)Index - Context->VScrollPosition) * Context->RowHeight; + rowBottom = rowTop + Context->RowHeight; + + // Check if the row is fully visible. + if (rowTop >= viewTop && rowBottom <= viewBottom) + return TRUE; + + deltaY = rowTop - viewTop; + + if (deltaY > 0) + { + // The row is below the view area. We want to scroll the row into view at the bottom of the + // screen. We need to round up when dividing to make sure the node becomes fully visible. + deltaY = rowBottom - viewBottom; + deltaRows = (deltaY + Context->RowHeight - 1) / Context->RowHeight; // divide, round up + } + else + { + deltaRows = deltaY / Context->RowHeight; + } + + PhTnpScroll(Context, deltaRows, 0); + + return TRUE; +} + +VOID PhTnpProcessMoveMouse( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorX, + _In_ LONG CursorY + ) +{ + PH_TREENEW_HIT_TEST hitTest; + PPH_TREENEW_NODE hotNode; + + hitTest.Point.x = CursorX; + hitTest.Point.y = CursorY; + hitTest.InFlags = TN_TEST_COLUMN | TN_TEST_SUBITEM; + PhTnpHitTest(Context, &hitTest); + + if (hitTest.Flags & TN_HIT_ITEM) + hotNode = hitTest.Node; + else + hotNode = NULL; + + PhTnpSetHotNode(Context, hotNode, !!(hitTest.Flags & TN_HIT_ITEM_PLUSMINUS)); + + if (Context->AnimateDivider && Context->FixedDividerVisible) + { + if (hitTest.Flags & TN_HIT_DIVIDER) + { + if ((Context->DividerHot < 100 || Context->AnimateDividerFadingOut) && !Context->AnimateDividerFadingIn) + { + // Begin fading in the divider. + Context->AnimateDividerFadingIn = TRUE; + Context->AnimateDividerFadingOut = FALSE; + SetTimer(Context->Handle, TNP_TIMER_ANIMATE_DIVIDER, TNP_ANIMATE_DIVIDER_INTERVAL, NULL); + } + } + else + { + if ((Context->DividerHot != 0 || Context->AnimateDividerFadingIn) && !Context->AnimateDividerFadingOut) + { + Context->AnimateDividerFadingOut = TRUE; + Context->AnimateDividerFadingIn = FALSE; + SetTimer(Context->Handle, TNP_TIMER_ANIMATE_DIVIDER, TNP_ANIMATE_DIVIDER_INTERVAL, NULL); + } + } + } + + if (Context->TooltipsHandle) + { + ULONG index; + ULONG id; + + if (!(hitTest.Flags & TN_HIT_DIVIDER)) + { + index = hitTest.Node ? hitTest.Node->Index : -1; + id = hitTest.Column ? hitTest.Column->Id : -1; + } + else + { + index = -1; + id = -1; + } + + // This pops unnecessarily - when the cell has no tooltip text, and the user is moving the + // mouse over it. However these unnecessary calls seem to fix a certain tooltip bug (move + // the mouse around very quickly over the last column and the blank space to the right, and + // no more tooltips will appear). + if (Context->TooltipIndex != index || Context->TooltipId != id) + { + PhTnpPopTooltip(Context); + } + } +} + +VOID PhTnpProcessMouseVWheel( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance + ) +{ + ULONG wheelScrollLines; + FLOAT linesToScroll; + LONG wholeLinesToScroll; + SCROLLINFO scrollInfo; + LONG oldPosition; + + if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScrollLines, 0)) + wheelScrollLines = 3; + + // If page scrolling is enabled, use the number of visible rows. + if (wheelScrollLines == -1) + wheelScrollLines = (Context->ClientRect.bottom - Context->HeaderHeight - (Context->HScrollVisible ? Context->HScrollHeight : 0)) / Context->RowHeight; + + // Zero the remainder if the direction changed. + if ((Context->VScrollRemainder > 0) != (Distance > 0)) + Context->VScrollRemainder = 0; + + linesToScroll = (FLOAT)wheelScrollLines * Distance / WHEEL_DELTA + Context->VScrollRemainder; + wholeLinesToScroll = (LONG)linesToScroll; + Context->VScrollRemainder = linesToScroll - wholeLinesToScroll; + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_ALL; + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + scrollInfo.nPos += wholeLinesToScroll; + + scrollInfo.fMask = SIF_POS; + SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + + if (scrollInfo.nPos != oldPosition) + { + Context->VScrollPosition = scrollInfo.nPos; + PhTnpProcessScroll(Context, scrollInfo.nPos - oldPosition, 0); + + if (Context->TooltipsHandle) + { + MSG message; + POINT point; + + PhTnpPopTooltip(Context); + PhTnpGetMessagePos(Context->Handle, &point); + + if (point.x >= 0 && point.y >= 0 && point.x < Context->ClientRect.right && point.y < Context->ClientRect.bottom) + { + // Send a fake mouse move message for the new node that the mouse may be hovering over. + message.hwnd = Context->Handle; + message.message = WM_MOUSEMOVE; + message.wParam = 0; + message.lParam = MAKELPARAM(point.x, point.y); + SendMessage(Context->TooltipsHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); + } + } + } +} + +VOID PhTnpProcessMouseHWheel( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG Distance + ) +{ + ULONG wheelScrollChars; + FLOAT pixelsToScroll; + LONG wholePixelsToScroll; + SCROLLINFO scrollInfo; + LONG oldPosition; + + if (!SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &wheelScrollChars, 0)) + wheelScrollChars = 3; + + // Zero the remainder if the direction changed. + if ((Context->HScrollRemainder > 0) != (Distance > 0)) + Context->HScrollRemainder = 0; + + pixelsToScroll = (FLOAT)wheelScrollChars * Context->TextMetrics.tmAveCharWidth * Distance / WHEEL_DELTA + Context->HScrollRemainder; + wholePixelsToScroll = (LONG)pixelsToScroll; + Context->HScrollRemainder = pixelsToScroll - wholePixelsToScroll; + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_ALL; + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + scrollInfo.nPos += wholePixelsToScroll; + + scrollInfo.fMask = SIF_POS; + SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + + if (scrollInfo.nPos != oldPosition) + { + Context->HScrollPosition = scrollInfo.nPos; + PhTnpProcessScroll(Context, 0, scrollInfo.nPos - oldPosition); + } +} + +BOOLEAN PhTnpProcessFocusKey( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey + ) +{ + ULONG count; + ULONG index; + BOOLEAN controlKey; + BOOLEAN shiftKey; + ULONG start; + ULONG end; + ULONG changedStart; + ULONG changedEnd; + RECT rect; + + if (VirtualKey != VK_UP && VirtualKey != VK_DOWN && + VirtualKey != VK_HOME && VirtualKey != VK_END && + VirtualKey != VK_PRIOR && VirtualKey != VK_NEXT) + { + return FALSE; + } + + count = Context->FlatList->Count; + + if (count == 0) + return TRUE; + + // Find the new node to focus. + + switch (VirtualKey) + { + case VK_UP: + { + if (Context->FocusNode && Context->FocusNode->Index > 0) + { + index = Context->FocusNode->Index - 1; + } + else + { + index = 0; + } + } + break; + case VK_DOWN: + { + if (Context->FocusNode) + { + index = Context->FocusNode->Index + 1; + + if (index >= count) + index = count - 1; + } + else + { + index = 0; + } + } + break; + case VK_HOME: + index = 0; + break; + case VK_END: + index = count - 1; + break; + case VK_PRIOR: + case VK_NEXT: + { + LONG rowsPerPage; + + if (Context->FocusNode) + index = Context->FocusNode->Index; + else + index = 0; + + rowsPerPage = Context->ClientRect.bottom - Context->HeaderHeight - (Context->HScrollVisible ? Context->HScrollHeight : 0); + + if (rowsPerPage < 0) + return TRUE; + + rowsPerPage = rowsPerPage / Context->RowHeight - 1; + + if (rowsPerPage < 0) + return TRUE; + + if (VirtualKey == VK_PRIOR) + { + ULONG startOfPageIndex; + + startOfPageIndex = Context->VScrollPosition; + + if (index > startOfPageIndex) + { + index = startOfPageIndex; + } + else + { + // Already at or before the start of the page. Go back a page. + if (index >= (ULONG)rowsPerPage) + index -= rowsPerPage; + else + index = 0; + } + } + else + { + ULONG endOfPageIndex; + + endOfPageIndex = Context->VScrollPosition + rowsPerPage; + + if (endOfPageIndex >= count) + endOfPageIndex = count - 1; + + if (index < endOfPageIndex) + { + index = endOfPageIndex; + } + else + { + // Already at or after the end of the page. Go forward a page. + index += rowsPerPage; + + if (index >= count) + index = count - 1; + } + } + } + break; + } + + // Select the relevant nodes. + + controlKey = GetKeyState(VK_CONTROL) < 0; + shiftKey = GetKeyState(VK_SHIFT) < 0; + + Context->FocusNode = Context->FlatList->Items[index]; + PhTnpSetHotNode(Context, Context->FocusNode, FALSE); + + if (shiftKey && Context->MarkNodeIndex != -1) + { + if (index > Context->MarkNodeIndex) + { + start = Context->MarkNodeIndex; + end = index; + } + else + { + start = index; + end = Context->MarkNodeIndex; + } + + PhTnpSelectRange(Context, start, end, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + else if (!controlKey) + { + Context->MarkNodeIndex = Context->FocusNode->Index; + PhTnpSelectRange(Context, index, index, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + + PhTnpEnsureVisibleNode(Context, index); + PhTnpPopTooltip(Context); + + return TRUE; +} + +BOOLEAN PhTnpProcessNodeKey( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKey + ) +{ + BOOLEAN controlKey; + BOOLEAN shiftKey; + ULONG changedStart; + ULONG changedEnd; + RECT rect; + + if (VirtualKey != VK_SPACE && VirtualKey != VK_LEFT && VirtualKey != VK_RIGHT && VirtualKey != VK_ADD && VirtualKey != VK_OEM_PLUS) + { + return FALSE; + } + + if (!Context->FocusNode) + return TRUE; + + controlKey = GetKeyState(VK_CONTROL) < 0; + shiftKey = GetKeyState(VK_SHIFT) < 0; + + switch (VirtualKey) + { + case VK_SPACE: + { + if (controlKey) + { + // Control key: toggle the selection on the focused node. + + Context->MarkNodeIndex = Context->FocusNode->Index; + PhTnpSelectRange(Context, Context->FocusNode->Index, Context->FocusNode->Index, TN_SELECT_TOGGLE, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + else if (shiftKey) + { + ULONG start; + ULONG end; + + // Shift key: select a range from the selection mark node to the focused node. + + if (Context->FocusNode->Index > Context->MarkNodeIndex) + { + start = Context->MarkNodeIndex; + end = Context->FocusNode->Index; + } + else + { + start = Context->FocusNode->Index; + end = Context->MarkNodeIndex; + } + + PhTnpSelectRange(Context, start, end, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + } + } + break; + case VK_LEFT: + { + ULONG i; + ULONG targetLevel; + PPH_TREENEW_NODE newNode; + + // If the node is expanded, collapse it. Otherwise, select the node's parent. + if (!Context->FocusNode->s.IsLeaf && Context->FocusNode->Expanded) + { + PhTnpSetExpandedNode(Context, Context->FocusNode, FALSE); + } + else if (Context->FocusNode->Level != 0) + { + i = Context->FocusNode->Index; + targetLevel = Context->FocusNode->Level - 1; + + while (i != 0) + { + i--; + newNode = Context->FlatList->Items[i]; + + if (newNode->Level == targetLevel) + { + Context->FocusNode = newNode; + Context->MarkNodeIndex = newNode->Index; + PhTnpEnsureVisibleNode(Context, i); + PhTnpSetHotNode(Context, newNode, FALSE); + PhTnpSelectRange(Context, i, i, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + + PhTnpPopTooltip(Context); + + break; + } + } + } + } + break; + case VK_RIGHT: + { + PPH_TREENEW_NODE newNode; + + if (!Context->FocusNode->s.IsLeaf) + { + // If the node is collapsed, expand it. Otherwise, select the node's first child. + if (!Context->FocusNode->Expanded) + { + PhTnpSetExpandedNode(Context, Context->FocusNode, TRUE); + } + else + { + if (Context->FocusNode->Index + 1 < Context->FlatList->Count) + { + newNode = Context->FlatList->Items[Context->FocusNode->Index + 1]; + + if (newNode->Level == Context->FocusNode->Level + 1) + { + Context->FocusNode = newNode; + Context->MarkNodeIndex = newNode->Index; + PhTnpEnsureVisibleNode(Context, Context->FocusNode->Index + 1); + PhTnpSetHotNode(Context, newNode, FALSE); + PhTnpSelectRange(Context, Context->FocusNode->Index, Context->FocusNode->Index, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + + PhTnpPopTooltip(Context); + } + } + } + } + } + break; + case VK_ADD: + case VK_OEM_PLUS: + { + if ((VirtualKey == VK_ADD && controlKey) || + (VirtualKey == VK_OEM_PLUS && controlKey && shiftKey)) + { + ULONG i; + + if (Context->FixedColumnVisible) + PhTnpAutoSizeColumnHeader(Context, Context->FixedHeaderHandle, Context->FixedColumn, 0); + + for (i = 0; i < Context->NumberOfColumnsByDisplay; i++) + PhTnpAutoSizeColumnHeader(Context, Context->HeaderHandle, Context->ColumnsByDisplay[i], 0); + } + } + break; + } + + return TRUE; +} + +VOID PhTnpProcessSearchKey( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG Character + ) +{ + LONG messageTime; + BOOLEAN newSearch; + PH_TREENEW_SEARCH_EVENT searchEvent; + PPH_TREENEW_NODE foundNode; + ULONG changedStart; + ULONG changedEnd; + RECT rect; + + if (Context->FlatList->Count == 0) + return; + + messageTime = GetMessageTime(); + newSearch = FALSE; + + // Check if the search timed out. + if (messageTime - Context->SearchMessageTime > PH_TREENEW_SEARCH_TIMEOUT) + { + Context->SearchStringCount = 0; + Context->SearchFailed = FALSE; + newSearch = TRUE; + Context->SearchSingleCharMode = TRUE; + } + + Context->SearchMessageTime = messageTime; + + // Append the character to the search buffer. + + if (!Context->SearchString) + { + Context->AllocatedSearchString = 32; + Context->SearchString = PhAllocate(Context->AllocatedSearchString * sizeof(WCHAR)); + newSearch = TRUE; + Context->SearchSingleCharMode = TRUE; + } + + if (Context->SearchStringCount > PH_TREENEW_SEARCH_MAXIMUM_LENGTH) + { + // The search string has become too long. Fail the search. + if (!Context->SearchFailed) + { + MessageBeep(0); + Context->SearchFailed = TRUE; + return; + } + } + else if (Context->SearchStringCount == Context->AllocatedSearchString) + { + Context->AllocatedSearchString *= 2; + Context->SearchString = PhReAllocate(Context->SearchString, Context->AllocatedSearchString * sizeof(WCHAR)); + } + + Context->SearchString[Context->SearchStringCount++] = (WCHAR)Character; + + if (Context->SearchString[Context->SearchStringCount - 1] != Context->SearchString[0]) + { + // The user has stopped typing the same character (or never started). Turn single-character + // search off. + Context->SearchSingleCharMode = FALSE; + } + + searchEvent.FoundIndex = -1; + + if (Context->FocusNode) + { + searchEvent.StartIndex = Context->FocusNode->Index; + + if (newSearch || Context->SearchSingleCharMode) + { + // If it's a new search, start at the next item so the user doesn't find the same item + // again. + searchEvent.StartIndex++; + + if (searchEvent.StartIndex == Context->FlatList->Count) + searchEvent.StartIndex = 0; + } + } + else + { + searchEvent.StartIndex = 0; + } + + searchEvent.String.Buffer = Context->SearchString; + + if (!Context->SearchSingleCharMode) + searchEvent.String.Length = Context->SearchStringCount * sizeof(WCHAR); + else + searchEvent.String.Length = sizeof(WCHAR); + + // Give the user a chance to modify how the search is performed. + if (!Context->Callback(Context->Handle, TreeNewIncrementalSearch, &searchEvent, NULL, Context->CallbackContext)) + { + // Use the default search function. + if (!PhTnpDefaultIncrementalSearch(Context, &searchEvent, TRUE, TRUE)) + { + return; + } + } + + if (searchEvent.FoundIndex == -1 && !Context->SearchFailed) + { + // No search result. Beep to indicate an error, and set the flag so we don't beep again. But + // don't beep if the first character was a space, because that's used for other purposes + // elsewhere (see PhTnpProcessNodeKey). + if (searchEvent.String.Buffer[0] != ' ') + { + MessageBeep(0); + } + + Context->SearchFailed = TRUE; + return; + } + + if (searchEvent.FoundIndex < 0 || searchEvent.FoundIndex >= (LONG)Context->FlatList->Count) + return; + + foundNode = Context->FlatList->Items[searchEvent.FoundIndex]; + Context->FocusNode = foundNode; + PhTnpEnsureVisibleNode(Context, searchEvent.FoundIndex); + PhTnpSetHotNode(Context, foundNode, FALSE); + PhTnpSelectRange(Context, searchEvent.FoundIndex, searchEvent.FoundIndex, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + + PhTnpPopTooltip(Context); +} + +BOOLEAN PhTnpDefaultIncrementalSearch( + _In_ PPH_TREENEW_CONTEXT Context, + _Inout_ PPH_TREENEW_SEARCH_EVENT SearchEvent, + _In_ BOOLEAN Partial, + _In_ BOOLEAN Wrap + ) +{ + LONG startIndex; + LONG currentIndex; + LONG foundIndex; + BOOLEAN firstTime; + + if (Context->FlatList->Count == 0) + return FALSE; + if (!Context->FirstColumn) + return FALSE; + + startIndex = SearchEvent->StartIndex; + currentIndex = startIndex; + foundIndex = -1; + firstTime = TRUE; + + while (TRUE) + { + PH_STRINGREF text; + + if (currentIndex >= (LONG)Context->FlatList->Count) + { + if (Wrap) + currentIndex = 0; + else + break; + } + + // We use the firstTime variable instead of a simpler check because we want to include the + // current item in the search. E.g. the current item is the only item beginning with "Z". If + // the user searches for "Z", we want to return the current item as being found. + if (!firstTime && currentIndex == startIndex) + break; + + if (PhTnpGetCellText(Context, Context->FlatList->Items[currentIndex], Context->FirstColumn->Id, &text)) + { + if (Partial) + { + if (PhStartsWithStringRef(&text, &SearchEvent->String, TRUE)) + { + foundIndex = currentIndex; + break; + } + } + else + { + if (PhEqualStringRef(&text, &SearchEvent->String, TRUE)) + { + foundIndex = currentIndex; + break; + } + } + } + + currentIndex++; + firstTime = FALSE; + } + + SearchEvent->FoundIndex = foundIndex; + + return TRUE; +} + +VOID PhTnpUpdateScrollBars( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + RECT clientRect; + LONG width; + LONG height; + LONG contentWidth; + LONG contentHeight; + SCROLLINFO scrollInfo; + LONG oldPosition; + LONG deltaRows; + LONG deltaX; + LOGICAL oldHScrollVisible; + RECT rect; + + clientRect = Context->ClientRect; + width = clientRect.right - Context->FixedWidth; + height = clientRect.bottom - Context->HeaderHeight; + + contentWidth = Context->TotalViewX; + contentHeight = (LONG)Context->FlatList->Count * Context->RowHeight; + + if (contentHeight > height) + { + // We need a vertical scrollbar, so we can't use that area of the screen for content. + width -= Context->VScrollWidth; + } + + if (contentWidth > width) + { + height -= Context->HScrollHeight; + } + + deltaRows = 0; + deltaX = 0; + + // Vertical scroll bar + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_POS; + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + scrollInfo.fMask = SIF_RANGE | SIF_PAGE; + scrollInfo.nMin = 0; + scrollInfo.nMax = Context->FlatList->Count != 0 ? Context->FlatList->Count - 1 : 0; + scrollInfo.nPage = height / Context->RowHeight; + SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); + + // The scroll position may have changed due to the modified scroll range. + scrollInfo.fMask = SIF_POS; + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + deltaRows = scrollInfo.nPos - oldPosition; + Context->VScrollPosition = scrollInfo.nPos; + + if (contentHeight > height && contentHeight != 0) + { + ShowWindow(Context->VScrollHandle, SW_SHOW); + Context->VScrollVisible = TRUE; + } + else + { + ShowWindow(Context->VScrollHandle, SW_HIDE); + Context->VScrollVisible = FALSE; + } + + // Horizontal scroll bar + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_POS; + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + scrollInfo.fMask = SIF_RANGE | SIF_PAGE; + scrollInfo.nMin = 0; + scrollInfo.nMax = contentWidth != 0 ? contentWidth - 1 : 0; + scrollInfo.nPage = width; + SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); + + scrollInfo.fMask = SIF_POS; + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + deltaX = scrollInfo.nPos - oldPosition; + Context->HScrollPosition = scrollInfo.nPos; + + oldHScrollVisible = Context->HScrollVisible; + + if (contentWidth > width && contentWidth != 0) + { + ShowWindow(Context->HScrollHandle, SW_SHOW); + Context->HScrollVisible = TRUE; + } + else + { + ShowWindow(Context->HScrollHandle, SW_HIDE); + Context->HScrollVisible = FALSE; + } + + if ((Context->HScrollVisible != oldHScrollVisible) && Context->FixedDividerVisible && Context->AnimateDivider) + { + rect.left = Context->FixedWidth; + rect.top = Context->HeaderHeight; + rect.right = Context->FixedWidth + 1; + rect.bottom = Context->ClientRect.bottom; + InvalidateRect(Context->Handle, &rect, FALSE); + } + + if (deltaRows != 0 || deltaX != 0) + PhTnpProcessScroll(Context, deltaRows, deltaX); + + ShowWindow(Context->FillerBoxHandle, (Context->VScrollVisible && Context->HScrollVisible) ? SW_SHOW : SW_HIDE); +} + +VOID PhTnpScroll( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG DeltaRows, + _In_ LONG DeltaX + ) +{ + SCROLLINFO scrollInfo; + LONG oldPosition; + LONG deltaRows; + LONG deltaX; + + deltaRows = 0; + deltaX = 0; + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_POS; + + if (DeltaRows != 0 && Context->VScrollVisible) + { + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + if (DeltaRows == MINLONG) + scrollInfo.nPos = 0; + else if (DeltaRows == MAXLONG) + scrollInfo.nPos = Context->FlatList->Count - 1; + else + scrollInfo.nPos += DeltaRows; + + SetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo, TRUE); + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + Context->VScrollPosition = scrollInfo.nPos; + deltaRows = scrollInfo.nPos - oldPosition; + } + + if (DeltaX != 0 && Context->HScrollVisible) + { + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + oldPosition = scrollInfo.nPos; + + if (DeltaX == MINLONG) + scrollInfo.nPos = 0; + else if (DeltaX == MAXLONG) + scrollInfo.nPos = Context->TotalViewX; + else + scrollInfo.nPos += DeltaX; + + SetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo, TRUE); + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + Context->HScrollPosition = scrollInfo.nPos; + deltaX = scrollInfo.nPos - oldPosition; + } + + if (deltaRows != 0 || deltaX != 0) + PhTnpProcessScroll(Context, deltaRows, deltaX); +} + +VOID PhTnpProcessScroll( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG DeltaRows, + _In_ LONG DeltaX + ) +{ + RECT rect; + LONG deltaY; + + rect.top = Context->HeaderHeight; + rect.bottom = Context->ClientRect.bottom; + + if (DeltaX == 0) + { + rect.left = 0; + rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + ScrollWindowEx( + Context->Handle, + 0, + -DeltaRows * Context->RowHeight, + &rect, + NULL, + NULL, + NULL, + SW_INVALIDATE + ); + } + else + { + // Don't scroll if there are no rows. This is especially important if the user wants us to + // display empty text. + if (Context->FlatList->Count != 0) + { + deltaY = DeltaRows * Context->RowHeight; + + // If we're scrolling vertically as well, we need to scroll the fixed part and the + // normal part separately. + + if (DeltaRows != 0) + { + rect.left = 0; + rect.right = Context->NormalLeft; + ScrollWindowEx( + Context->Handle, + 0, + -deltaY, + &rect, + &rect, + NULL, + NULL, + SW_INVALIDATE + ); + } + + rect.left = Context->NormalLeft; + rect.right = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + ScrollWindowEx( + Context->Handle, + -DeltaX, + -deltaY, + &rect, + &rect, + NULL, + NULL, + SW_INVALIDATE + ); + } + + PhTnpLayoutHeader(Context); + } +} + +BOOLEAN PhTnpCanScroll( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Horizontal, + _In_ BOOLEAN Positive + ) +{ + SCROLLINFO scrollInfo; + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + + if (!Horizontal) + GetScrollInfo(Context->VScrollHandle, SB_CTL, &scrollInfo); + else + GetScrollInfo(Context->HScrollHandle, SB_CTL, &scrollInfo); + + if (Positive) + { + if (scrollInfo.nPage != 0) + scrollInfo.nMax -= scrollInfo.nPage - 1; + + return scrollInfo.nPos < scrollInfo.nMax; + } + else + { + return scrollInfo.nPos > scrollInfo.nMin; + } +} + +VOID PhTnpPaint( + _In_ HWND hwnd, + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ PRECT PaintRect + ) +{ + RECT viewRect; + LONG vScrollPosition; + LONG hScrollPosition; + LONG firstRowToUpdate; + LONG lastRowToUpdate; + LONG i; + LONG j; + PPH_TREENEW_NODE node; + PPH_TREENEW_COLUMN column; + RECT rowRect; + LONG x; + BOOLEAN fixedUpdate; + LONG normalUpdateLeftX; + LONG normalUpdateRightX; + LONG normalUpdateLeftIndex; + LONG normalUpdateRightIndex; + LONG normalTotalX; + RECT cellRect; + HRGN oldClipRegion; + + PhTnpInitializeThemeData(Context); + + viewRect = Context->ClientRect; + + if (Context->VScrollVisible) + viewRect.right -= Context->VScrollWidth; + + vScrollPosition = Context->VScrollPosition; + hScrollPosition = Context->HScrollPosition; + + // Calculate the indicies of the first and last rows that need painting. These indicies are + // relative to the top of the view area. + + firstRowToUpdate = (PaintRect->top - Context->HeaderHeight) / Context->RowHeight; + lastRowToUpdate = (PaintRect->bottom - 1 - Context->HeaderHeight) / Context->RowHeight; // minus one since bottom is exclusive + + if (firstRowToUpdate < 0) + firstRowToUpdate = 0; + + rowRect.left = 0; + rowRect.top = Context->HeaderHeight + firstRowToUpdate * Context->RowHeight; + rowRect.right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; + rowRect.bottom = rowRect.top + Context->RowHeight; + + // Change the indicies to absolute row indicies. + + firstRowToUpdate += vScrollPosition; + lastRowToUpdate += vScrollPosition; + + if (lastRowToUpdate >= (LONG)Context->FlatList->Count) + lastRowToUpdate = Context->FlatList->Count - 1; // becomes -1 when there are no items, handled correctly by loop below + + // Determine whether the fixed column needs painting, and which normal columns need painting. + + fixedUpdate = FALSE; + + if (Context->FixedColumnVisible && PaintRect->left < Context->FixedWidth) + fixedUpdate = TRUE; + + x = Context->NormalLeft - hScrollPosition; + normalUpdateLeftX = viewRect.right; + normalUpdateLeftIndex = 0; + normalUpdateRightX = 0; + normalUpdateRightIndex = -1; + + for (j = 0; j < (LONG)Context->NumberOfColumnsByDisplay; j++) + { + column = Context->ColumnsByDisplay[j]; + + if (x + column->Width >= Context->NormalLeft && x + column->Width > PaintRect->left && x < PaintRect->right) + { + if (normalUpdateLeftX > x) + { + normalUpdateLeftX = x; + normalUpdateLeftIndex = j; + } + + if (normalUpdateRightX < x + column->Width) + { + normalUpdateRightX = x + column->Width; + normalUpdateRightIndex = j; + } + } + + x += column->Width; + } + + normalTotalX = x; + + if (normalUpdateRightIndex >= (LONG)Context->NumberOfColumnsByDisplay) + normalUpdateRightIndex = Context->NumberOfColumnsByDisplay - 1; + + // Paint the rows. + + SelectFont(hdc, Context->Font); + SetBkMode(hdc, TRANSPARENT); + + for (i = firstRowToUpdate; i <= lastRowToUpdate; i++) + { + node = Context->FlatList->Items[i]; + + // Prepare the row for drawing. + + PhTnpPrepareRowForDraw(Context, hdc, node); + + if (Context->ThemeSupport) + { + HDC tempDc; + BITMAPINFOHEADER header; + HBITMAP bitmap; + HBITMAP oldBitmap; + PVOID bits; + RECT tempRect; + BLENDFUNCTION blendFunction; + + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(30, 30, 30)); + FillRect(hdc, &rowRect, GetStockBrush(DC_BRUSH)); + + if (tempDc = CreateCompatibleDC(hdc)) + { + memset(&header, 0, sizeof(BITMAPINFOHEADER)); + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = 1; + header.biHeight = 1; + header.biPlanes = 1; + header.biBitCount = 24; + bitmap = CreateDIBSection(tempDc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &bits, NULL, 0); + + if (bitmap) + { + // Draw the outline of the selection rectangle. + //FrameRect(hdc, &rowRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + + // Fill in the selection rectangle. + oldBitmap = SelectBitmap(tempDc, bitmap); + tempRect.left = 0; + tempRect.top = 0; + tempRect.right = 1; + tempRect.bottom = 1; + + SetTextColor(tempDc, node->s.DrawForeColor); + + if (node->s.DrawBackColor != 16777215) + { + SetDCBrushColor(tempDc, node->s.DrawBackColor); + FillRect(tempDc, &tempRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetDCBrushColor(tempDc, RGB(30, 30, 30)); + FillRect(tempDc, &tempRect, GetStockBrush(DC_BRUSH)); + } + + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.BlendFlags = 0; + blendFunction.SourceConstantAlpha = 96; + blendFunction.AlphaFormat = 0; + + GdiAlphaBlend( + hdc, + rowRect.left, + rowRect.top, + rowRect.right - rowRect.left, + rowRect.bottom - rowRect.top, + tempDc, + 0, + 0, + 1, + 1, + blendFunction + ); + + SelectBitmap(tempDc, oldBitmap); + DeleteBitmap(bitmap); + } + + DeleteDC(tempDc); + } + } + else + { + if (node->Selected && (Context->CustomColors || !Context->ThemeHasItemBackground)) + { + // Non-themed background + + if (Context->HasFocus) + { + if (Context->CustomColors) + { + SetTextColor(hdc, Context->CustomTextColor); + SetDCBrushColor(hdc, Context->CustomFocusColor); + FillRect(hdc, &rowRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + FillRect(hdc, &rowRect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + } + else + { + if (Context->CustomColors) + { + SetTextColor(hdc, Context->CustomTextColor); + SetDCBrushColor(hdc, Context->CustomSelectedColor); + FillRect(hdc, &rowRect, GetStockBrush(DC_BRUSH)); + } + else + { + SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); + FillRect(hdc, &rowRect, GetSysColorBrush(COLOR_BTNFACE)); + } + } + } + else + { + SetTextColor(hdc, node->s.DrawForeColor); + SetDCBrushColor(hdc, node->s.DrawBackColor); + FillRect(hdc, &rowRect, GetStockBrush(DC_BRUSH)); + } + } + + if (!Context->CustomColors && Context->ThemeHasItemBackground) + { + INT stateId; + + // Themed background + + if (node->Selected) + { + if (i == Context->HotNodeIndex) + stateId = TREIS_HOTSELECTED; + else if (!Context->HasFocus) + stateId = TREIS_SELECTEDNOTFOCUS; + else + stateId = TREIS_SELECTED; + } + else + { + if (i == Context->HotNodeIndex) + stateId = TREIS_HOT; + else + stateId = INT_MAX; + } + + // Themed background + + if (stateId != INT_MAX) + { + if (!Context->FixedColumnVisible) + { + rowRect.left = Context->NormalLeft - hScrollPosition; + } + + DrawThemeBackground( + Context->ThemeData, + hdc, + TVP_TREEITEM, + stateId, + &rowRect, + PaintRect + ); + } + } + + // Paint the fixed column. + + cellRect.top = rowRect.top; + cellRect.bottom = rowRect.bottom; + + if (fixedUpdate) + { + cellRect.left = 0; + cellRect.right = Context->FixedWidth; + PhTnpDrawCell(Context, hdc, &cellRect, node, Context->FixedColumn, i, -1); + } + + // Paint the normal columns. + + if (normalUpdateLeftX < normalUpdateRightX) + { + cellRect.left = normalUpdateLeftX; + cellRect.right = cellRect.left; + + oldClipRegion = CreateRectRgn(0, 0, 0, 0); + + if (GetClipRgn(hdc, oldClipRegion) != 1) + { + DeleteRgn(oldClipRegion); + oldClipRegion = NULL; + } + + IntersectClipRect(hdc, Context->NormalLeft, cellRect.top, viewRect.right, cellRect.bottom); + + for (j = normalUpdateLeftIndex; j <= normalUpdateRightIndex; j++) + { + column = Context->ColumnsByDisplay[j]; + + cellRect.left = cellRect.right; + cellRect.right = cellRect.left + column->Width; + PhTnpDrawCell(Context, hdc, &cellRect, node, column, i, j); + } + + SelectClipRgn(hdc, oldClipRegion); + + if (oldClipRegion) + { + DeleteRgn(oldClipRegion); + } + } + + rowRect.top += Context->RowHeight; + rowRect.bottom += Context->RowHeight; + } + + if (lastRowToUpdate == Context->FlatList->Count - 1) // works even if there are no items + { + // Fill the rest of the space on the bottom with the window color. + rowRect.bottom = viewRect.bottom; + + if (Context->ThemeSupport) + { + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(30, 30, 30)); + FillRect(hdc, &rowRect, GetStockBrush(DC_BRUSH)); + } + else + { + FillRect(hdc, &rowRect, GetSysColorBrush(COLOR_WINDOW)); + } + } + + if (normalTotalX < viewRect.right && viewRect.right > PaintRect->left && normalTotalX < PaintRect->right) + { + // Fill the rest of the space on the right with the window color. + rowRect.left = normalTotalX; + rowRect.top = Context->HeaderHeight; + rowRect.right = viewRect.right; + rowRect.bottom = viewRect.bottom; + + if (Context->ThemeSupport) + { + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + SetDCBrushColor(hdc, RGB(30, 30, 30)); + FillRect(hdc, &rowRect, GetStockBrush(DC_BRUSH)); + } + else + { + FillRect(hdc, &rowRect, GetSysColorBrush(COLOR_WINDOW)); + } + } + + if (Context->FlatList->Count == 0 && Context->EmptyText.Length != 0) + { + RECT textRect; + + textRect.left = 20; + textRect.top = Context->HeaderHeight + 10; + textRect.right = viewRect.right - 20; + textRect.bottom = viewRect.bottom - 5; + + SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + DrawText( + hdc, + Context->EmptyText.Buffer, + (ULONG)Context->EmptyText.Length / 2, + &textRect, + DT_NOPREFIX | DT_CENTER | DT_END_ELLIPSIS + ); + } + + if (Context->FixedDividerVisible && Context->FixedWidth >= PaintRect->left && Context->FixedWidth < PaintRect->right) + { + PhTnpDrawDivider(Context, hdc); + } + + if (Context->DragSelectionActive) + { + PhTnpDrawSelectionRectangle(Context, hdc, &Context->DragRect); + } +} + +VOID PhTnpPrepareRowForDraw( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _Inout_ PPH_TREENEW_NODE Node + ) +{ + if (!Node->s.CachedColorValid) + { + PH_TREENEW_GET_NODE_COLOR getNodeColor; + + getNodeColor.Flags = 0; + getNodeColor.Node = Node; + getNodeColor.BackColor = Context->DefaultBackColor; + getNodeColor.ForeColor = Context->DefaultForeColor; + + if (Context->Callback( + Context->Handle, + TreeNewGetNodeColor, + &getNodeColor, + NULL, + Context->CallbackContext + )) + { + Node->BackColor = getNodeColor.BackColor; + Node->ForeColor = getNodeColor.ForeColor; + Node->UseAutoForeColor = !!(getNodeColor.Flags & TN_AUTO_FORECOLOR); + + if (getNodeColor.Flags & TN_CACHE) + Node->s.CachedColorValid = TRUE; + } + else + { + Node->BackColor = getNodeColor.BackColor; + Node->ForeColor = getNodeColor.ForeColor; + } + } + + Node->s.DrawForeColor = Node->ForeColor; + + if (Node->UseTempBackColor) + Node->s.DrawBackColor = Node->TempBackColor; + else + Node->s.DrawBackColor = Node->BackColor; + + if (!Node->s.CachedFontValid) + { + PH_TREENEW_GET_NODE_FONT getNodeFont; + + getNodeFont.Flags = 0; + getNodeFont.Node = Node; + getNodeFont.Font = NULL; + + if (Context->Callback( + Context->Handle, + TreeNewGetNodeFont, + &getNodeFont, + NULL, + Context->CallbackContext + )) + { + Node->Font = getNodeFont.Font; + + if (getNodeFont.Flags & TN_CACHE) + Node->s.CachedFontValid = TRUE; + } + else + { + Node->Font = NULL; + } + } + + if (!Node->s.CachedIconValid) + { + PH_TREENEW_GET_NODE_ICON getNodeIcon; + + getNodeIcon.Flags = 0; + getNodeIcon.Node = Node; + getNodeIcon.Icon = NULL; + + if (Context->Callback( + Context->Handle, + TreeNewGetNodeIcon, + &getNodeIcon, + NULL, + Context->CallbackContext + )) + { + Node->Icon = getNodeIcon.Icon; + + if (getNodeIcon.Flags & TN_CACHE) + Node->s.CachedIconValid = TRUE; + } + else + { + Node->Icon = NULL; + } + } + + if (Node->UseAutoForeColor || Node->UseTempBackColor) + { + if (PhGetColorBrightness(Node->s.DrawBackColor) > 100) // slightly less than half + Node->s.DrawForeColor = RGB(0x00, 0x00, 0x00); + else + Node->s.DrawForeColor = RGB(0xff, 0xff, 0xff); + } +} + +VOID PhTnpDrawCell( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ PRECT CellRect, + _In_ PPH_TREENEW_NODE Node, + _In_ PPH_TREENEW_COLUMN Column, + _In_ LONG RowIndex, + _In_ LONG ColumnIndex + ) +{ + HFONT font; // font to use + HFONT oldFont; + PH_STRINGREF text; // text to draw + RECT textRect; // working rectangle, modified as needed + ULONG textFlags; // DT_* flags + LONG iconVerticalMargin; // top/bottom margin for icons (determined using height of small icon) + + font = Node->Font; + textFlags = Column->TextFlags; + + textRect = *CellRect; + + // Initial margins used by default list view + textRect.left += TNP_CELL_LEFT_MARGIN; + textRect.right -= TNP_CELL_RIGHT_MARGIN; + + // icon margin = (height of row - height of small icon) / 2 + iconVerticalMargin = ((textRect.bottom - textRect.top) - SmallIconHeight) / 2; + + textRect.top += iconVerticalMargin; + textRect.bottom -= iconVerticalMargin; + + if (Column == Context->FirstColumn) + { + BOOLEAN needsClip; + HRGN oldClipRegion; + + textRect.left += Node->Level * SmallIconWidth; + + // The icon may need to be clipped if the column is too small. + needsClip = Column->Width < textRect.left + (Context->CanAnyExpand ? SmallIconWidth : 0) + (Node->Icon ? SmallIconWidth : 0); + + if (needsClip) + { + oldClipRegion = CreateRectRgn(0, 0, 0, 0); + + if (GetClipRgn(hdc, oldClipRegion) != 1) + { + DeleteRgn(oldClipRegion); + oldClipRegion = NULL; + } + + // Clip contents to the column. + IntersectClipRect(hdc, CellRect->left, textRect.top, CellRect->right, textRect.bottom); + } + + if (Context->CanAnyExpand) // flag is used so we can avoid indenting when it's a flat list + { + BOOLEAN drewUsingTheme = FALSE; + RECT themeRect; + + if (!Node->s.IsLeaf) + { + // Draw the plus/minus glyph. + + themeRect.left = textRect.left; + themeRect.right = themeRect.left + SmallIconWidth; + themeRect.top = textRect.top; + themeRect.bottom = themeRect.top + SmallIconHeight; + + if (Context->ThemeHasGlyph) + { + INT partId; + INT stateId; + + partId = (RowIndex == Context->HotNodeIndex && Node->s.PlusMinusHot && Context->ThemeHasHotGlyph) ? TVP_HOTGLYPH : TVP_GLYPH; + stateId = Node->Expanded ? GLPS_OPENED : GLPS_CLOSED; + + if (SUCCEEDED(DrawThemeBackground( + Context->ThemeData, + hdc, + partId, + stateId, + &themeRect, + NULL + ))) + drewUsingTheme = TRUE; + } + + if (!drewUsingTheme) + { + ULONG glyphWidth; + ULONG glyphHeight; + RECT glyphRect; + + glyphWidth = SmallIconWidth / 2; + glyphHeight = SmallIconHeight / 2; + + glyphRect.left = textRect.left + (SmallIconWidth - glyphWidth) / 2; + glyphRect.right = glyphRect.left + glyphWidth; + glyphRect.top = textRect.top + (SmallIconHeight - glyphHeight) / 2; + glyphRect.bottom = glyphRect.top + glyphHeight; + + PhTnpDrawPlusMinusGlyph(hdc, &glyphRect, !Node->Expanded); + } + } + + textRect.left += SmallIconWidth; + } + + // Draw the icon. + if (Node->Icon) + { + DrawIconEx( + hdc, + textRect.left, + textRect.top, + Node->Icon, + SmallIconWidth, + SmallIconHeight, + 0, + NULL, + DI_NORMAL + ); + + textRect.left += SmallIconWidth + TNP_ICON_RIGHT_PADDING; + } + + if (needsClip) + { + SelectClipRgn(hdc, oldClipRegion); + + if (oldClipRegion) + DeleteRgn(oldClipRegion); + } + + if (textRect.left > textRect.right) + textRect.left = textRect.right; + } + + if (Column->CustomDraw) + { + BOOLEAN result; + PH_TREENEW_CUSTOM_DRAW customDraw; + INT savedDc; + + customDraw.Node = Node; + customDraw.Column = Column; + customDraw.Dc = hdc; + customDraw.CellRect = *CellRect; + customDraw.TextRect = textRect; + + // Fix up the rectangles before giving them to the user. + if (customDraw.CellRect.left > customDraw.CellRect.right) + customDraw.CellRect.left = customDraw.CellRect.right; + if (customDraw.TextRect.left > customDraw.TextRect.right) + customDraw.TextRect.left = customDraw.TextRect.right; + + savedDc = SaveDC(hdc); + result = Context->Callback(Context->Handle, TreeNewCustomDraw, &customDraw, NULL, Context->CallbackContext); + RestoreDC(hdc, savedDc); + + if (result) + return; + } + + if (PhTnpGetCellText(Context, Node, Column->Id, &text)) + { + if (!(textFlags & (DT_PATH_ELLIPSIS | DT_WORD_ELLIPSIS))) + textFlags |= DT_END_ELLIPSIS; + + textFlags |= DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE; + + textRect.top = CellRect->top; + textRect.bottom = CellRect->bottom; + + if (font) + oldFont = SelectFont(hdc, font); + + DrawText( + hdc, + text.Buffer, + (ULONG)text.Length / 2, + &textRect, + textFlags + ); + + if (font) + SelectFont(hdc, oldFont); + } +} + +VOID PhTnpDrawDivider( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc + ) +{ + POINT points[2]; + + if (Context->AnimateDivider) + { + if (Context->DividerHot == 0 && !Context->HScrollVisible) + return; // divider is invisible + + if (Context->DividerHot < 100) + { + BLENDFUNCTION blendFunction; + + // We need to draw and alpha blend the divider. + // We can use the extra column allocated in the buffered context to initially draw the + // divider. + + points[0].x = Context->ClientRect.right; + points[0].y = Context->HeaderHeight; + points[1].x = Context->ClientRect.right; + points[1].y = Context->ClientRect.bottom; + SetDCPenColor(Context->BufferedContext, RGB(0x77, 0x77, 0x77)); + SelectPen(Context->BufferedContext, GetStockPen(DC_PEN)); + Polyline(Context->BufferedContext, points, 2); + + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.BlendFlags = 0; + blendFunction.AlphaFormat = 0; + + // If the horizontal scroll bar is visible, we need to display a line even if the + // divider is not hot. In this case we increase the base alpha value. + if (!Context->HScrollVisible) + blendFunction.SourceConstantAlpha = (UCHAR)(Context->DividerHot * 255 / 100); + else + blendFunction.SourceConstantAlpha = 55 + (UCHAR)(Context->DividerHot * 2); + + GdiAlphaBlend( + hdc, + Context->FixedWidth, + Context->HeaderHeight, + 1, + Context->ClientRect.bottom - Context->HeaderHeight, + Context->BufferedContext, + Context->ClientRect.right, + Context->HeaderHeight, + 1, + Context->ClientRect.bottom - Context->HeaderHeight, + blendFunction + ); + + return; + } + } + + points[0].x = Context->FixedWidth; + points[0].y = Context->HeaderHeight; + points[1].x = Context->FixedWidth; + points[1].y = Context->ClientRect.bottom; + SetDCPenColor(hdc, RGB(0x77, 0x77, 0x77)); + SelectPen(hdc, GetStockPen(DC_PEN)); + Polyline(hdc, points, 2); +} + +VOID PhTnpDrawPlusMinusGlyph( + _In_ HDC hdc, + _In_ PRECT Rect, + _In_ BOOLEAN Plus + ) +{ + INT savedDc; + ULONG width; + ULONG height; + POINT points[2]; + + savedDc = SaveDC(hdc); + + SelectPen(hdc, GetStockPen(DC_PEN)); + SetDCPenColor(hdc, RGB(0x55, 0x55, 0x55)); + SelectBrush(hdc, GetStockBrush(DC_BRUSH)); + SetDCBrushColor(hdc, RGB(0xff, 0xff, 0xff)); + + width = Rect->right - Rect->left; + height = Rect->bottom - Rect->top; + + // Draw the rectangle. + Rectangle(hdc, Rect->left, Rect->top, Rect->right + 1, Rect->bottom + 1); + + SetDCPenColor(hdc, RGB(0x00, 0x00, 0x00)); + + // Draw the horizontal line. + points[0].x = Rect->left + 2; + points[0].y = Rect->top + height / 2; + points[1].x = Rect->right - 2 + 1; + points[1].y = points[0].y; + Polyline(hdc, points, 2); + + if (Plus) + { + // Draw the vertical line. + points[0].x = Rect->left + width / 2; + points[0].y = Rect->top + 2; + points[1].x = points[0].x; + points[1].y = Rect->bottom - 2 + 1; + Polyline(hdc, points, 2); + } + + RestoreDC(hdc, savedDc); +} + +VOID PhTnpDrawSelectionRectangle( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc, + _In_ PRECT Rect + ) +{ + RECT rect; + BOOLEAN drewWithAlpha; + + rect = *Rect; + + // MSDN says FrameRect/DrawFocusRect doesn't draw anything if bottom <= top or right <= left. + // That's complete rubbish. + if (rect.right - rect.left == 0 || rect.bottom - rect.top == 0) + return; + + drewWithAlpha = FALSE; + + if (Context->SelectionRectangleAlpha) + { + HDC tempDc; + BITMAPINFOHEADER header; + HBITMAP bitmap; + HBITMAP oldBitmap; + PVOID bits; + RECT tempRect; + BLENDFUNCTION blendFunction; + + tempDc = CreateCompatibleDC(hdc); + + if (tempDc) + { + memset(&header, 0, sizeof(BITMAPINFOHEADER)); + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = 1; + header.biHeight = 1; + header.biPlanes = 1; + header.biBitCount = 24; + bitmap = CreateDIBSection(tempDc, (BITMAPINFO *)&header, DIB_RGB_COLORS, &bits, NULL, 0); + + if (bitmap) + { + // Draw the outline of the selection rectangle. + FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT)); + + // Fill in the selection rectangle. + + oldBitmap = SelectBitmap(tempDc, bitmap); + tempRect.left = 0; + tempRect.top = 0; + tempRect.right = 1; + tempRect.bottom = 1; + FillRect(tempDc, &tempRect, GetSysColorBrush(COLOR_HOTLIGHT)); + + blendFunction.BlendOp = AC_SRC_OVER; + blendFunction.BlendFlags = 0; + blendFunction.SourceConstantAlpha = 70; + blendFunction.AlphaFormat = 0; + + GdiAlphaBlend( + hdc, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + tempDc, + 0, + 0, + 1, + 1, + blendFunction + ); + + drewWithAlpha = TRUE; + + SelectBitmap(tempDc, oldBitmap); + DeleteBitmap(bitmap); + } + + DeleteDC(tempDc); + } + } + + if (!drewWithAlpha) + { + DrawFocusRect(hdc, &rect); + } +} + +VOID PhTnpDrawThemedBorder( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ HDC hdc + ) +{ + RECT windowRect; + RECT clientRect; + LONG sizingBorderWidth; + LONG borderX; + LONG borderY; + + GetWindowRect(Context->Handle, &windowRect); + windowRect.right -= windowRect.left; + windowRect.bottom -= windowRect.top; + windowRect.left = 0; + windowRect.top = 0; + + clientRect.left = windowRect.left + Context->SystemEdgeX; + clientRect.top = windowRect.top + Context->SystemEdgeY; + clientRect.right = windowRect.right - Context->SystemEdgeX; + clientRect.bottom = windowRect.bottom - Context->SystemEdgeY; + + // Make sure we don't paint in the client area. + ExcludeClipRect(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom); + + // Draw the themed border. + DrawThemeBackground(Context->ThemeData, hdc, 0, 0, &windowRect, NULL); + + // Calculate the size of the border we just drew, and fill in the rest of the space if we didn't + // fully paint the region. + + if (SUCCEEDED(GetThemeInt(Context->ThemeData, 0, 0, TMT_SIZINGBORDERWIDTH, &sizingBorderWidth))) + { + borderX = sizingBorderWidth; + borderY = sizingBorderWidth; + } + else + { + borderX = Context->SystemBorderX; + borderY = Context->SystemBorderY; + } + + if (borderX < Context->SystemEdgeX || borderY < Context->SystemEdgeY) + { + windowRect.left += Context->SystemEdgeX - borderX; + windowRect.top += Context->SystemEdgeY - borderY; + windowRect.right -= Context->SystemEdgeX - borderX; + windowRect.bottom -= Context->SystemEdgeY - borderY; + FillRect(hdc, &windowRect, GetSysColorBrush(COLOR_WINDOW)); + } +} + +VOID PhTnpInitializeTooltips( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + TOOLINFO toolInfo; + + Context->TooltipsHandle = CreateWindowEx( + WS_EX_TRANSPARENT, // solves double-click problem + TOOLTIPS_CLASS, + NULL, + WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, + 0, + 0, + 0, + 0, + NULL, + NULL, + Context->InstanceHandle, + NULL + ); + + if (!Context->TooltipsHandle) + return; + + // Item tooltips + memset(&toolInfo, 0, sizeof(TOOLINFO)); + toolInfo.cbSize = sizeof(TOOLINFO); + toolInfo.uFlags = TTF_TRANSPARENT; + toolInfo.hwnd = Context->Handle; + toolInfo.uId = TNP_TOOLTIPS_ITEM; + toolInfo.lpszText = LPSTR_TEXTCALLBACK; + toolInfo.lParam = TNP_TOOLTIPS_ITEM; + SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + + // Fixed column tooltips + toolInfo.uFlags = 0; + toolInfo.hwnd = Context->FixedHeaderHandle; + toolInfo.uId = TNP_TOOLTIPS_FIXED_HEADER; + toolInfo.lpszText = LPSTR_TEXTCALLBACK; + toolInfo.lParam = TNP_TOOLTIPS_FIXED_HEADER; + SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + + // Normal column tooltips + toolInfo.uFlags = 0; + toolInfo.hwnd = Context->HeaderHandle; + toolInfo.uId = TNP_TOOLTIPS_HEADER; + toolInfo.lpszText = LPSTR_TEXTCALLBACK; + toolInfo.lParam = TNP_TOOLTIPS_HEADER; + SendMessage(Context->TooltipsHandle, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + + // Hook the header control window procedures so we can forward mouse messages to the tooltip control. + Context->HeaderWindowProc = (WNDPROC)GetWindowLongPtr(Context->HeaderHandle, GWLP_WNDPROC); + Context->FixedHeaderWindowProc = (WNDPROC)GetWindowLongPtr(Context->FixedHeaderHandle, GWLP_WNDPROC); + + PhSetWindowContext(Context->HeaderHandle, 0xF, Context); + PhSetWindowContext(Context->FixedHeaderHandle, 0xF, Context); + + SetWindowLongPtr(Context->FixedHeaderHandle, GWLP_WNDPROC, (LONG_PTR)PhTnpHeaderHookWndProc); + SetWindowLongPtr(Context->HeaderHandle, GWLP_WNDPROC, (LONG_PTR)PhTnpHeaderHookWndProc); + + SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); // no limit + SetWindowFont(Context->TooltipsHandle, Context->Font, FALSE); + Context->TooltipFont = Context->Font; +} + +VOID PhTnpGetTooltipText( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ PPOINT Point, + _Out_ PWSTR *Text + ) +{ + PH_TREENEW_HIT_TEST hitTest; + BOOLEAN unfoldingTooltip; + BOOLEAN unfoldingTooltipFromViewCancelled; + PH_TREENEW_CELL_PARTS parts; + LONG viewRight; + PH_TREENEW_GET_CELL_TOOLTIP getCellTooltip; + + hitTest.Point = *Point; + hitTest.InFlags = TN_TEST_COLUMN | TN_TEST_SUBITEM; + PhTnpHitTest(Context, &hitTest); + + if (Context->DragSelectionActive) + return; + if (!(hitTest.Flags & TN_HIT_ITEM)) + return; + if (hitTest.Flags & (TN_HIT_ITEM_PLUSMINUS | TN_HIT_DIVIDER)) + return; + if (!hitTest.Column) + return; + + if (Context->TooltipIndex != hitTest.Node->Index || Context->TooltipId != hitTest.Column->Id) + { + Context->TooltipIndex = hitTest.Node->Index; + Context->TooltipId = hitTest.Column->Id; + + getCellTooltip.Flags = 0; + getCellTooltip.Node = hitTest.Node; + getCellTooltip.Column = hitTest.Column; + getCellTooltip.Unfolding = FALSE; + PhInitializeEmptyStringRef(&getCellTooltip.Text); + getCellTooltip.Font = Context->Font; + getCellTooltip.MaximumWidth = -1; + + unfoldingTooltip = FALSE; + unfoldingTooltipFromViewCancelled = FALSE; + + if (!(Context->ExtendedFlags & TN_FLAG_NO_UNFOLDING_TOOLTIPS) && + PhTnpGetCellParts(Context, hitTest.Node->Index, hitTest.Column, TN_MEASURE_TEXT, &parts) && + (parts.Flags & TN_PART_CONTENT) && (parts.Flags & TN_PART_TEXT)) + { + viewRight = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + + // Use an unfolding tooltip if the text was truncated within the column, or the text + // extends beyond the view area in either direction. + + if (parts.TextRect.left < parts.ContentRect.left || parts.TextRect.right > parts.ContentRect.right) + { + unfoldingTooltip = TRUE; + } + else if ((!hitTest.Column->Fixed && parts.TextRect.left < Context->NormalLeft) || parts.TextRect.right > viewRight) + { + // Only show view-based unfolding tooltips if the mouse is over the text itself. + if (Point->x >= parts.TextRect.left && Point->x < parts.TextRect.right) + unfoldingTooltip = TRUE; + else + unfoldingTooltipFromViewCancelled = TRUE; + } + + if (unfoldingTooltip) + { + getCellTooltip.Unfolding = TRUE; + getCellTooltip.Text = parts.Text; + getCellTooltip.Font = parts.Font; // try to use the same font as the cell + + Context->TooltipRect = parts.TextRect; + } + } + + Context->Callback(Context->Handle, TreeNewGetCellTooltip, &getCellTooltip, NULL, Context->CallbackContext); + + Context->TooltipUnfolding = getCellTooltip.Unfolding; + + if (getCellTooltip.Text.Buffer && getCellTooltip.Text.Length != 0) + { + PhMoveReference(&Context->TooltipText, PhCreateString2(&getCellTooltip.Text)); + } + else + { + PhClearReference(&Context->TooltipText); + + if (unfoldingTooltipFromViewCancelled) + { + // We may need to show the view-based unfolding tooltip if the mouse moves over the + // text in the future. Reset the index and ID to make sure we keep checking. + Context->TooltipIndex = -1; + Context->TooltipId = -1; + } + } + + Context->NewTooltipFont = getCellTooltip.Font; + + if (!Context->NewTooltipFont) + Context->NewTooltipFont = Context->Font; + + if (getCellTooltip.MaximumWidth <= MAXSHORT) // seems to be the maximum value that the tooltip control supports + SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, getCellTooltip.MaximumWidth); + else + SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, MAXSHORT); + } + + if (Context->TooltipText) + *Text = Context->TooltipText->Buffer; +} + +BOOLEAN PhTnpPrepareTooltipShow( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + RECT rect; + + if (Context->TooltipFont != Context->NewTooltipFont) + { + Context->TooltipFont = Context->NewTooltipFont; + SetWindowFont(Context->TooltipsHandle, Context->TooltipFont, FALSE); + } + + if (!Context->TooltipUnfolding) + { + SetWindowPos( + Context->TooltipsHandle, + HWND_TOPMOST, + 0, + 0, + 0, + 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_HIDEWINDOW + ); + + return FALSE; + } + + rect = Context->TooltipRect; + SendMessage(Context->TooltipsHandle, TTM_ADJUSTRECT, TRUE, (LPARAM)&rect); + MapWindowPoints(Context->Handle, NULL, (POINT *)&rect, 2); + SetWindowPos( + Context->TooltipsHandle, + HWND_TOPMOST, + rect.left, + rect.top, + 0, + 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW + ); + + return TRUE; +} + +VOID PhTnpPrepareTooltipPop( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + Context->TooltipIndex = -1; + Context->TooltipId = -1; + Context->TooltipColumnId = -1; +} + +VOID PhTnpPopTooltip( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + if (Context->TooltipsHandle) + { + SendMessage(Context->TooltipsHandle, TTM_POP, 0, 0); + PhTnpPrepareTooltipPop(Context); + } +} + +PPH_TREENEW_COLUMN PhTnpHitTestHeader( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Fixed, + _In_ PPOINT Point, + _Out_opt_ PRECT ItemRect + ) +{ + PPH_TREENEW_COLUMN column; + RECT itemRect; + + if (Fixed) + { + if (!Context->FixedColumnVisible) + return NULL; + + column = Context->FixedColumn; + + if (!Header_GetItemRect(Context->FixedHeaderHandle, 0, &itemRect)) + return NULL; + } + else + { + HDHITTESTINFO hitTestInfo; + + hitTestInfo.pt = *Point; + hitTestInfo.flags = 0; + hitTestInfo.iItem = -1; + + if (SendMessage(Context->HeaderHandle, HDM_HITTEST, 0, (LPARAM)&hitTestInfo) != -1 && hitTestInfo.iItem != -1) + { + HDITEM item; + + item.mask = HDI_LPARAM; + + if (!Header_GetItem(Context->HeaderHandle, hitTestInfo.iItem, &item)) + return NULL; + + column = (PPH_TREENEW_COLUMN)item.lParam; + + if (!Header_GetItemRect(Context->HeaderHandle, hitTestInfo.iItem, &itemRect)) + return NULL; + } + else + { + return NULL; + } + } + + if (ItemRect) + *ItemRect = itemRect; + + return column; +} + +VOID PhTnpGetHeaderTooltipText( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ BOOLEAN Fixed, + _In_ PPOINT Point, + _Out_ PWSTR *Text + ) +{ + LOGICAL result; + PPH_TREENEW_COLUMN column; + RECT itemRect; + PWSTR text; + SIZE_T textCount; + HDC hdc; + SIZE textSize; + + column = PhTnpHitTestHeader(Context, Fixed, Point, &itemRect); + + if (!column) + return; + + if (Context->TooltipColumnId != column->Id) + { + // Determine if the tooltip needs to be shown. + + text = column->Text; + textCount = PhCountStringZ(text); + + if (!(hdc = GetDC(Context->Handle))) + return; + + SelectFont(hdc, Context->Font); + + result = GetTextExtentPoint32(hdc, text, (ULONG)textCount, &textSize); + ReleaseDC(Context->Handle, hdc); + + if (!result) + return; + + if (textSize.cx + 6 + 6 <= itemRect.right - itemRect.left) // HACK: Magic values (same as our cell margins?) + return; + + Context->TooltipColumnId = column->Id; + PhMoveReference(&Context->TooltipText, PhCreateStringEx(text, textCount * sizeof(WCHAR))); + } + + *Text = Context->TooltipText->Buffer; + + // Always use the default parameters for column header tooltips. + Context->NewTooltipFont = Context->Font; + Context->TooltipUnfolding = FALSE; + SendMessage(Context->TooltipsHandle, TTM_SETMAXTIPWIDTH, 0, TNP_TOOLTIPS_DEFAULT_MAXIMUM_WIDTH); +} + +LRESULT CALLBACK PhTnpHeaderHookWndProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_TREENEW_CONTEXT context; + WNDPROC oldWndProc; + + context = PhGetWindowContext(hwnd, 0xF); + + if (hwnd == context->FixedHeaderHandle) + oldWndProc = context->FixedHeaderWindowProc; + else + oldWndProc = context->HeaderWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwnd, 0xF); + } + break; + case WM_MOUSEMOVE: + { + POINT point; + PPH_TREENEW_COLUMN column; + ULONG id; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + column = PhTnpHitTestHeader(context, hwnd == context->FixedHeaderHandle, &point, NULL); + + if (column) + id = column->Id; + else + id = -1; + + if (context->TooltipColumnId != id) + { + PhTnpPopTooltip(context); + } + } + break; + case WM_NOTIFY: + { + NMHDR *header = (NMHDR *)lParam; + + switch (header->code) + { + case TTN_GETDISPINFO: + { + if (header->hwndFrom == context->TooltipsHandle) + { + NMTTDISPINFO *info = (NMTTDISPINFO *)header; + POINT point; + + PhTnpGetMessagePos(hwnd, &point); + PhTnpGetHeaderTooltipText(context, info->lParam == TNP_TOOLTIPS_FIXED_HEADER, &point, &info->lpszText); + } + } + break; + case TTN_SHOW: + { + if (header->hwndFrom == context->TooltipsHandle) + { + return PhTnpPrepareTooltipShow(context); + } + } + break; + case TTN_POP: + { + if (header->hwndFrom == context->TooltipsHandle) + { + PhTnpPrepareTooltipPop(context); + } + } + break; + } + } + break; + } + + switch (uMsg) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + { + if (context->TooltipsHandle) + { + MSG message; + + message.hwnd = hwnd; + message.message = uMsg; + message.wParam = wParam; + message.lParam = lParam; + SendMessage(context->TooltipsHandle, TTM_RELAYEVENT, 0, (LPARAM)&message); + } + } + break; + } + + return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); +} + +BOOLEAN PhTnpDetectDrag( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorX, + _In_ LONG CursorY, + _In_ BOOLEAN DispatchMessages, + _Out_opt_ PULONG CancelledByMessage + ) +{ + RECT dragRect; + MSG msg; + + // Capture mouse input and see if the user moves the mouse beyond the drag rectangle. + + dragRect.left = CursorX - Context->SystemDragX; + dragRect.top = CursorY - Context->SystemDragY; + dragRect.right = CursorX + Context->SystemDragX; + dragRect.bottom = CursorY + Context->SystemDragY; + MapWindowPoints(Context->Handle, NULL, (POINT *)&dragRect, 2); + + SetCapture(Context->Handle); + + if (CancelledByMessage) + *CancelledByMessage = 0; + + do + { + // It seems that GetMessage dispatches nonqueued messages directly from kernel-mode, so we + // have to use PeekMessage and WaitMessage in order to process WM_CAPTURECHANGED messages. + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + switch (msg.message) + { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + ReleaseCapture(); + + if (CancelledByMessage) + *CancelledByMessage = msg.message; + + break; + case WM_MOUSEMOVE: + if (msg.pt.x < dragRect.left || msg.pt.x >= dragRect.right || + msg.pt.y < dragRect.top || msg.pt.y >= dragRect.bottom) + { + if (IsWindow(Context->Handle)) + return TRUE; + else + return FALSE; + } + break; + default: + if (DispatchMessages) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + } + } + else + { + WaitMessage(); + } + } while (IsWindow(Context->Handle) && GetCapture() == Context->Handle); + + return FALSE; +} + +VOID PhTnpDragSelect( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ LONG CursorX, + _In_ LONG CursorY + ) +{ + MSG msg; + LONG cursorX; + LONG cursorY; + BOOLEAN originFixed; + RECT dragRect; + RECT oldDragRect; + RECT windowRect; + POINT cursorPoint; + BOOLEAN showContextMenu; + + cursorX = CursorX; + cursorY = CursorY; + originFixed = cursorX < Context->FixedWidth; + + dragRect.left = cursorX; + dragRect.top = cursorY; + dragRect.right = cursorX; + dragRect.bottom = cursorY; + oldDragRect = dragRect; + Context->DragRect = dragRect; + Context->DragSelectionActive = TRUE; + + if (Context->DoubleBuffered) + Context->SelectionRectangleAlpha = TRUE; + // TODO: Make sure the monitor's color depth is sufficient for alpha-blended selection + // rectangles. + + GetWindowRect(Context->Handle, &windowRect); + + cursorPoint.x = windowRect.left + cursorX; + cursorPoint.y = windowRect.top + cursorY; + + showContextMenu = FALSE; + + SetCapture(Context->Handle); + + while (TRUE) + { + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + BOOLEAN leftOrRight; + BOOLEAN aboveOrBelow; + + // If the cursor is outside of the window, generate some messages so the window keeps + // scrolling. + + leftOrRight = cursorPoint.x < windowRect.left || cursorPoint.x > windowRect.right; + aboveOrBelow = cursorPoint.y < windowRect.top || cursorPoint.y > windowRect.bottom; + + if ((Context->VScrollVisible && aboveOrBelow && PhTnpCanScroll(Context, FALSE, cursorPoint.y > windowRect.bottom)) || + (Context->HScrollVisible && leftOrRight && PhTnpCanScroll(Context, TRUE, cursorPoint.x > windowRect.right))) + { + SetCursorPos(cursorPoint.x, cursorPoint.y); + } + else + { + WaitMessage(); + } + + goto EndOfLoop; + } + + cursorPoint = msg.pt; + + switch (msg.message) + { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + ReleaseCapture(); + goto EndOfLoop; + case WM_RBUTTONUP: + ReleaseCapture(); + showContextMenu = TRUE; + goto EndOfLoop; + case WM_MOUSEMOVE: + { + LONG newCursorX; + LONG newCursorY; + LONG deltaRows; + LONG deltaX; + LONG oldVScrollPosition; + LONG oldHScrollPosition; + LONG newDeltaX; + LONG newDeltaY; + LONG viewLeft; + LONG viewTop; + LONG viewRight; + LONG viewBottom; + LONG temp; + RECT totalRect; + + newCursorX = GET_X_LPARAM(msg.lParam); + newCursorY = GET_Y_LPARAM(msg.lParam); + + // Scroll the window if the cursor is outside of it. + + deltaRows = 0; + deltaX = 0; + + if (Context->VScrollVisible) + { + if (cursorPoint.y < windowRect.top) + deltaRows = -(windowRect.top - cursorPoint.y + Context->RowHeight - 1) / Context->RowHeight; // scroll up + else if (cursorPoint.y >= windowRect.bottom) + deltaRows = (cursorPoint.y - windowRect.bottom + Context->RowHeight - 1) / Context->RowHeight; // scroll down + } + + if (Context->HScrollVisible) + { + if (cursorPoint.x < windowRect.left) + deltaX = -(windowRect.left - cursorPoint.x); // scroll left + else if (cursorPoint.x >= windowRect.right) + deltaX = cursorPoint.x - windowRect.right; // scroll right + } + + oldVScrollPosition = Context->VScrollPosition; + oldHScrollPosition = Context->HScrollPosition; + + if (deltaRows != 0 || deltaX != 0) + PhTnpScroll(Context, deltaRows, deltaX); + + newDeltaX = oldHScrollPosition - Context->HScrollPosition; + newDeltaY = (oldVScrollPosition - Context->VScrollPosition) * Context->RowHeight; + + // Adjust our original drag point for the scrolling. + if (!originFixed) + cursorX += newDeltaX; + cursorY += newDeltaY; + + // Adjust the old drag rectangle for the scrolling. + if (!originFixed) + oldDragRect.left += newDeltaX; + oldDragRect.top += newDeltaY; + if (!originFixed) + oldDragRect.right += newDeltaX; + oldDragRect.bottom += newDeltaY; + + // Ensure that the new cursor position is within the content area. + + viewLeft = Context->FixedColumnVisible ? 0 : -Context->HScrollPosition; + viewTop = Context->HeaderHeight - Context->VScrollPosition; + viewRight = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; + viewBottom = Context->HeaderHeight + ((LONG)Context->FlatList->Count - Context->VScrollPosition) * Context->RowHeight; + + temp = Context->ClientRect.right - (Context->VScrollVisible ? Context->VScrollWidth : 0); + viewRight = max(viewRight, temp); + temp = Context->ClientRect.bottom - ((!Context->FixedColumnVisible && Context->HScrollVisible) ? Context->HScrollHeight : 0); + viewBottom = max(viewBottom, temp); + + if (newCursorX < viewLeft) + newCursorX = viewLeft; + if (newCursorX > viewRight) + newCursorX = viewRight; + if (newCursorY < viewTop) + newCursorY = viewTop; + if (newCursorY > viewBottom) + newCursorY = viewBottom; + + // Create the new drag rectangle. + + if (cursorX < newCursorX) + { + dragRect.left = cursorX; + dragRect.right = newCursorX; + } + else + { + dragRect.left = newCursorX; + dragRect.right = cursorX; + } + + if (cursorY < newCursorY) + { + dragRect.top = cursorY; + dragRect.bottom = newCursorY; + } + else + { + dragRect.top = newCursorY; + dragRect.bottom = cursorY; + } + + // Has anything changed from before? + if (dragRect.left == oldDragRect.left && dragRect.top == oldDragRect.top && + dragRect.right == oldDragRect.right && dragRect.bottom == oldDragRect.bottom) + { + break; + } + + Context->DragRect = dragRect; + + // Process the selection. + totalRect.left = min(dragRect.left, oldDragRect.left); + totalRect.top = min(dragRect.top, oldDragRect.top); + totalRect.right = max(dragRect.right, oldDragRect.right); + totalRect.bottom = max(dragRect.bottom, oldDragRect.bottom); + PhTnpProcessDragSelect(Context, (ULONG)msg.wParam, &oldDragRect, &dragRect, &totalRect); + + // Redraw the drag rectangle. + RedrawWindow(Context->Handle, &totalRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + + oldDragRect = dragRect; + } + break; + case WM_MOUSELEAVE: + break; // don't process + case WM_MOUSEWHEEL: + break; // don't process + case WM_KEYDOWN: + if (msg.wParam == VK_ESCAPE) + { + ULONG changedStart; + ULONG changedEnd; + RECT rect; + + PhTnpSelectRange(Context, -1, -1, TN_SELECT_RESET, &changedStart, &changedEnd); + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } + + ReleaseCapture(); + } + break; // don't process + case WM_CHAR: + break; // don't process + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + +EndOfLoop: + if (GetCapture() != Context->Handle) + break; + } + + Context->DragSelectionActive = FALSE; + RedrawWindow(Context->Handle, &dragRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + + if (showContextMenu) + { + // Display a context menu at the original drag point. + SendMessage(Context->Handle, WM_CONTEXTMENU, (WPARAM)Context->Handle, MAKELPARAM(windowRect.left + CursorX, windowRect.top + CursorY)); + } +} + +VOID PhTnpProcessDragSelect( + _In_ PPH_TREENEW_CONTEXT Context, + _In_ ULONG VirtualKeys, + _In_ PRECT OldRect, + _In_ PRECT NewRect, + _In_ PRECT TotalRect + ) +{ + LONG firstRow; + LONG lastRow; + RECT rowRect; + LONG i; + PPH_TREENEW_NODE node; + LONG changedStart; + LONG changedEnd; + RECT rect; + + // Determine which rows we need to test. The divisions below must be done on positive integers + // to ensure correct rounding. + + firstRow = (TotalRect->top - Context->HeaderHeight + Context->VScrollPosition * Context->RowHeight) / Context->RowHeight; + lastRow = (TotalRect->bottom - 1 - Context->HeaderHeight + Context->VScrollPosition * Context->RowHeight) / Context->RowHeight; + + if (firstRow < 0) + firstRow = 0; + if (lastRow >= (LONG)Context->FlatList->Count) + lastRow = Context->FlatList->Count - 1; + + rowRect.left = 0; + rowRect.top = Context->HeaderHeight + (firstRow - Context->VScrollPosition) * Context->RowHeight; + rowRect.right = Context->NormalLeft + Context->TotalViewX - Context->HScrollPosition; + rowRect.bottom = rowRect.top + Context->RowHeight; + + changedStart = lastRow; + changedEnd = firstRow; + + // Process the rows. + for (i = firstRow; i <= lastRow; i++) + { + BOOLEAN inOldRect; + BOOLEAN inNewRect; + + node = Context->FlatList->Items[i]; + + inOldRect = rowRect.top < OldRect->bottom && rowRect.bottom > OldRect->top && + rowRect.left < OldRect->right && rowRect.right > OldRect->left; + inNewRect = rowRect.top < NewRect->bottom && rowRect.bottom > NewRect->top && + rowRect.left < NewRect->right && rowRect.right > NewRect->left; + + if (VirtualKeys & MK_CONTROL) + { + if (!node->Unselectable && inOldRect != inNewRect) + { + node->Selected = !node->Selected; + + if (changedStart > i) + changedStart = i; + if (changedEnd < i) + changedEnd = i; + } + } + else + { + if (!node->Unselectable && inOldRect != inNewRect) + { + node->Selected = inNewRect; + + if (changedStart > i) + changedStart = i; + if (changedEnd < i) + changedEnd = i; + } + } + + rowRect.top = rowRect.bottom; + rowRect.bottom += Context->RowHeight; + } + + if (changedStart <= changedEnd) + { + Context->Callback(Context->Handle, TreeNewSelectionChanged, NULL, NULL, Context->CallbackContext); + } + + if (PhTnpGetRowRects(Context, changedStart, changedEnd, TRUE, &rect)) + { + InvalidateRect(Context->Handle, &rect, FALSE); + } +} + +VOID PhTnpCreateBufferedContext( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + HDC hdc; + + if (hdc = GetDC(Context->Handle)) + { + Context->BufferedContext = CreateCompatibleDC(hdc); + + if (!Context->BufferedContext) + return; + + Context->BufferedContextRect = Context->ClientRect; + Context->BufferedBitmap = CreateCompatibleBitmap( + hdc, + Context->BufferedContextRect.right + 1, // leave one extra pixel for divider animation + Context->BufferedContextRect.bottom + ); + + if (!Context->BufferedBitmap) + { + DeleteDC(Context->BufferedContext); + Context->BufferedContext = NULL; + return; + } + + ReleaseDC(Context->Handle, hdc); + Context->BufferedOldBitmap = SelectBitmap(Context->BufferedContext, Context->BufferedBitmap); + } +} + +VOID PhTnpDestroyBufferedContext( + _In_ PPH_TREENEW_CONTEXT Context + ) +{ + // The original bitmap must be selected back into the context, otherwise the bitmap can't be + // deleted. + SelectBitmap(Context->BufferedContext, Context->BufferedOldBitmap); + DeleteBitmap(Context->BufferedBitmap); + DeleteDC(Context->BufferedContext); + + Context->BufferedContext = NULL; + Context->BufferedBitmap = NULL; +} + +VOID PhTnpGetMessagePos( + _In_ HWND hwnd, + _Out_ PPOINT ClientPoint + ) +{ + ULONG position; + POINT point; + + position = GetMessagePos(); + point.x = GET_X_LPARAM(position); + point.y = GET_Y_LPARAM(position); + ScreenToClient(hwnd, &point); + + *ClientPoint = point; +} diff --git a/phlib/util.c b/phlib/util.c index 5a923ee1daae..74538a464131 100644 --- a/phlib/util.c +++ b/phlib/util.c @@ -3,7 +3,7 @@ * general support functions * * Copyright (C) 2009-2016 wj32 - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2019 dmex * * This file is part of Process Hacker. * @@ -26,43 +26,21 @@ #include #include #include -#define CINTERFACE -#define COBJMACROS #include -#undef CINTERFACE -#undef COBJMACROS +#include #include #include #include #include +#include + +#include #include "md5.h" #include "sha.h" #include "sha256.h" -// We may want to change this for debugging purposes. -#define PHP_USE_IFILEDIALOG (WINDOWS_HAS_IFILEDIALOG) - -typedef BOOLEAN (NTAPI *_WinStationQueryInformationW)( - _In_opt_ HANDLE ServerHandle, - _In_ ULONG LogonId, - _In_ WINSTATIONINFOCLASS WinStationInformationClass, - _Out_writes_bytes_(WinStationInformationLength) PVOID WinStationInformation, - _In_ ULONG WinStationInformationLength, - _Out_ PULONG ReturnLength - ); - -typedef BOOL (WINAPI *_CreateEnvironmentBlock)( - _Out_ LPVOID *lpEnvironment, - _In_opt_ HANDLE hToken, - _In_ BOOL bInherit - ); - -typedef BOOL (WINAPI *_DestroyEnvironmentBlock)( - _In_ LPVOID lpEnvironment - ); - DECLSPEC_SELECTANY WCHAR *PhSizeUnitNames[7] = { L"B", L"kB", L"MB", L"GB", L"TB", L"PB", L"EB" }; DECLSPEC_SELECTANY ULONG PhMaxSizeUnit = MAXULONG32; @@ -192,38 +170,132 @@ VOID PhCenterWindow( } } -/** - * References an array of objects. - * - * \param Objects An array of objects. - * \param NumberOfObjects The number of elements in \a Objects. - */ -VOID PhReferenceObjects( - _In_reads_(NumberOfObjects) PVOID *Objects, - _In_ ULONG NumberOfObjects +// NLS support +// TODO: Move to seperate file. (dmex) + +// rev from GetSystemDefaultLCID +LCID PhGetSystemDefaultLCID( + VOID ) { - ULONG i; + if (NtQueryDefaultLocale_Import()) + { + LCID localeId; - for (i = 0; i < NumberOfObjects; i++) - PhReferenceObject(Objects[i]); + if (NT_SUCCESS(NtQueryDefaultLocale_Import()(FALSE, &localeId))) + return localeId; + } + + return LOCALE_SYSTEM_DEFAULT; // MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT); } -/** - * Dereferences an array of objects. - * - * \param Objects An array of objects. - * \param NumberOfObjects The number of elements in \a Objects. - */ -VOID PhDereferenceObjects( - _In_reads_(NumberOfObjects) PVOID *Objects, - _In_ ULONG NumberOfObjects +// rev from GetUserDefaultLCID +LCID PhGetUserDefaultLCID( + VOID ) { - ULONG i; + if (NtQueryDefaultLocale_Import()) + { + LCID localeId; + + if (NT_SUCCESS(NtQueryDefaultLocale_Import()(TRUE, &localeId))) + return localeId; + } + + return LOCALE_USER_DEFAULT; // MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT); +} + +// rev from GetThreadLocale +LCID PhGetCurrentThreadLCID( + VOID + ) +{ + PTEB currentTeb; + + currentTeb = NtCurrentTeb(); + + if (!currentTeb->CurrentLocale) + currentTeb->CurrentLocale = PhGetUserDefaultLCID(); + + return currentTeb->CurrentLocale; +} + +// rev from GetSystemDefaultLangID +LANGID PhGetSystemDefaultLangID( + VOID + ) +{ + return LANGIDFROMLCID(PhGetSystemDefaultLCID()); +} + +// rev from GetUserDefaultLangID +LANGID PhGetUserDefaultLangID( + VOID + ) +{ + return LANGIDFROMLCID(PhGetUserDefaultLCID()); +} + +// rev from GetUserDefaultUILanguage +LANGID PhGetUserDefaultUILanguage( + VOID + ) +{ + if (NtQueryDefaultUILanguage_Import()) + { + LANGID languageId; + + if (NT_SUCCESS(NtQueryDefaultUILanguage_Import()(&languageId))) + return languageId; + } + + return MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT); +} + +// rev from GetUserDefaultLocaleName +PPH_STRING PhGetUserDefaultLocaleName( + VOID + ) +{ + UNICODE_STRING localeNameUs; + WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; + + RtlInitEmptyUnicodeString(&localeNameUs, localeName, sizeof(localeName)); + + if (NT_SUCCESS(RtlLcidToLocaleName(PhGetUserDefaultLCID(), &localeNameUs, 0, FALSE))) + { + return PhCreateStringFromUnicodeString(&localeNameUs); + } + + return NULL; +} + +// rev from LCIDToLocaleName +PPH_STRING PhLCIDToLocaleName( + _In_ LCID lcid + ) +{ + UNICODE_STRING localeNameUs; + WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; + + RtlInitEmptyUnicodeString(&localeNameUs, localeName, sizeof(localeName)); + + if (lcid) + { + if (NT_SUCCESS(RtlLcidToLocaleName(lcid, &localeNameUs, 0, FALSE))) + { + return PhCreateStringFromUnicodeString(&localeNameUs); + } + } + else // Return the current user locale when zero is specified. + { + if (NT_SUCCESS(RtlLcidToLocaleName(PhGetUserDefaultLCID(), &localeNameUs, 0, FALSE))) + { + return PhCreateStringFromUnicodeString(&localeNameUs); + } + } - for (i = 0; i < NumberOfObjects; i++) - PhDereferenceObject(Objects[i]); + return NULL; } /** @@ -261,7 +333,7 @@ PPH_STRING PhGetMessage( status = RtlFindMessage( DllHandle, MessageTableId, - GetSystemDefaultLangID(), + PhGetSystemDefaultLangID(), MessageId, &messageEntry ); @@ -282,6 +354,10 @@ PPH_STRING PhGetMessage( if (!NT_SUCCESS(status)) return NULL; + // dmex: We don't support parsing insert sequences. + if (messageEntry->Text[0] == '%') + return NULL; + if (messageEntry->Flags & MESSAGE_RESOURCE_UNICODE) { return PhCreateStringEx((PWCHAR)messageEntry->Text, messageEntry->Length); @@ -304,7 +380,7 @@ PPH_STRING PhGetNtMessage( PPH_STRING message; if (!NT_NTWIN32(Status)) - message = PhGetMessage(GetModuleHandle(L"ntdll.dll"), 0xb, GetUserDefaultLangID(), (ULONG)Status); + message = PhGetMessage(PhGetLoaderEntryDllBase(L"ntdll.dll"), 0xb, PhGetUserDefaultLangID(), (ULONG)Status); else message = PhGetWin32Message(WIN32_FROM_NTSTATUS(Status)); @@ -346,7 +422,7 @@ PPH_STRING PhGetWin32Message( { PPH_STRING message; - message = PhGetMessage(GetModuleHandle(L"kernel32.dll"), 0xb, GetUserDefaultLangID(), Result); + message = PhGetMessage(PhGetLoaderEntryDllBase(L"kernel32.dll"), 0xb, PhGetUserDefaultLangID(), Result); if (message) PhTrimToNullTerminatorString(message); @@ -372,30 +448,19 @@ PPH_STRING PhGetWin32Message( * \return The user's response. */ INT PhShowMessage( - _In_ HWND hWnd, + _In_opt_ HWND hWnd, _In_ ULONG Type, _In_ PWSTR Format, ... ) -{ - va_list argptr; - - va_start(argptr, Format); - - return PhShowMessage_V(hWnd, Type, Format, argptr); -} - -INT PhShowMessage_V( - _In_ HWND hWnd, - _In_ ULONG Type, - _In_ PWSTR Format, - _In_ va_list ArgPtr - ) { INT result; + va_list argptr; PPH_STRING message; - message = PhFormatString_V(Format, ArgPtr); + va_start(argptr, Format); + message = PhFormatString_V(Format, argptr); + va_end(argptr); if (!message) return -1; @@ -406,48 +471,49 @@ INT PhShowMessage_V( return result; } -INT PhShowError2( - _In_ HWND hWnd, +INT PhShowMessage2( + _In_opt_ HWND hWnd, + _In_ ULONG Buttons, + _In_opt_ PWSTR Icon, _In_opt_ PWSTR Title, - _In_opt_ PWSTR Message + _In_ PWSTR Format, + ... ) { - if (TaskDialogIndirect_Import()) - { - INT button; - TASKDIALOGCONFIG config = { sizeof(config) }; + INT result; + va_list argptr; + PPH_STRING message; + TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; - config.hwndParent = hWnd; - config.hInstance = PhLibImageBase; - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); - config.dwCommonButtons = TDCBF_CLOSE_BUTTON; - config.pszWindowTitle = PhApplicationName; - config.pszMainIcon = TD_ERROR_ICON; - config.pszMainInstruction = Title; - config.pszContent = Message; - - if (TaskDialogIndirect_Import()( - &config, - &button, - NULL, - NULL - ) == S_OK) - { - return button == IDCLOSE; - } - else - { - return FALSE; - } + va_start(argptr, Format); + message = PhFormatString_V(Format, argptr); + va_end(argptr); + + if (!message) + return -1; + + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | ((hWnd && IsWindowVisible(hWnd) && !IsMinimized(hWnd)) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); + config.dwCommonButtons = Buttons; + config.hwndParent = hWnd; + config.pszWindowTitle = PhApplicationName; + config.pszMainIcon = Icon; + config.pszMainInstruction = Title; + config.pszContent = message->Buffer; + + if (SUCCEEDED(TaskDialogIndirect( + &config, + &result, + NULL, + NULL + ))) + { + PhDereferenceObject(message); + return result; } else { - return PhShowMessage( - hWnd, - MB_OK | MB_ICONERROR, - Title, - Message - ) == IDOK; + PhDereferenceObject(message); + return -1; } } @@ -488,7 +554,7 @@ PPH_STRING PhGetStatusMessage( * \param Win32Result A Win32 error code, or 0 if there is none. */ VOID PhShowStatus( - _In_ HWND hWnd, + _In_opt_ HWND hWnd, _In_opt_ PWSTR Message, _In_ NTSTATUS Status, _In_opt_ ULONG Win32Result @@ -496,32 +562,30 @@ VOID PhShowStatus( { PPH_STRING statusMessage; - statusMessage = PhGetStatusMessage(Status, Win32Result); - - if (!statusMessage) + if (statusMessage = PhGetStatusMessage(Status, Win32Result)) { if (Message) { - PhShowError(hWnd, L"%s.", Message); + PhShowError2(hWnd, Message, L"%s", statusMessage->Buffer); } else { - PhShowError(hWnd, L"Unable to perform the operation."); + PhShowError(hWnd, L"%s", statusMessage->Buffer); } - return; - } - - if (Message) - { - PhShowError2(hWnd, Message, statusMessage->Buffer); + PhDereferenceObject(statusMessage); } else { - PhShowError(hWnd, L"%s", statusMessage->Buffer); + if (Message) + { + PhShowError(hWnd, L"%s.", Message); + } + else + { + PhShowError(hWnd, L"Unable to perform the operation."); + } } - - PhDereferenceObject(statusMessage); } /** @@ -547,30 +611,16 @@ BOOLEAN PhShowContinueStatus( statusMessage = PhGetStatusMessage(Status, Win32Result); - if (!statusMessage) - { - if (Message) - { - result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"%s.", Message); - } - else - { - result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"Unable to perform the operation."); - } - - return result == IDOK; - } - - if (Message) - { - result = PhShowError2(hWnd, Message, statusMessage->Buffer); - } + if (Message && statusMessage) + result = PhShowMessage2(hWnd, TDCBF_CLOSE_BUTTON, TD_ERROR_ICON, Message, L"%s", statusMessage->Buffer); + else if (Message) + result = PhShowMessage2(hWnd, TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, TD_ERROR_ICON, L"", L"%s", Message); + else if (statusMessage) + result = PhShowMessage2(hWnd, TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, TD_ERROR_ICON, L"", L"%s", statusMessage->Buffer); else - { - result = PhShowMessage(hWnd, MB_ICONERROR | MB_OKCANCEL, L"%s", statusMessage->Buffer); - } + result = PhShowMessage2(hWnd, TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, TD_ERROR_ICON, L"Unable to perform the operation.", L"%s", L""); - PhDereferenceObject(statusMessage); + if (statusMessage) PhDereferenceObject(statusMessage); return result == IDOK; } @@ -597,6 +647,9 @@ BOOLEAN PhShowConfirmMessage( PPH_STRING verb; PPH_STRING verbCaps; PPH_STRING action; + TASKDIALOGCONFIG config = { sizeof(config) }; + TASKDIALOG_BUTTON buttons[2]; + INT button; // Make sure the verb is all lowercase. verb = PhaLowerString(PhaCreateString(Verb)); @@ -608,53 +661,37 @@ BOOLEAN PhShowConfirmMessage( // "terminate", "the process" -> "terminate the process" action = PhaConcatStrings(3, verb->Buffer, L" ", Object); - if (TaskDialogIndirect_Import()) - { - TASKDIALOGCONFIG config = { sizeof(config) }; - TASKDIALOG_BUTTON buttons[2]; - INT button; - - config.hwndParent = hWnd; - config.hInstance = PhLibImageBase; - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | (IsWindowVisible(hWnd) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); - config.pszWindowTitle = PhApplicationName; - config.pszMainIcon = Warning ? TD_WARNING_ICON : NULL; - config.pszMainInstruction = PhaConcatStrings(3, L"Do you want to ", action->Buffer, L"?")->Buffer; + config.hwndParent = hWnd; + config.hInstance = PhInstanceHandle; + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | ((hWnd && IsWindowVisible(hWnd) && !IsMinimized(hWnd)) ? TDF_POSITION_RELATIVE_TO_WINDOW : 0); + config.pszWindowTitle = PhApplicationName; + config.pszMainIcon = Warning ? TD_WARNING_ICON : TD_INFORMATION_ICON; + config.pszMainInstruction = PhaConcatStrings(3, L"Do you want to ", action->Buffer, L"?")->Buffer; - if (Message) - config.pszContent = PhaConcatStrings2(Message, L" Are you sure you want to continue?")->Buffer; + if (Message) + config.pszContent = PhaConcatStrings2(Message, L" Are you sure you want to continue?")->Buffer; - buttons[0].nButtonID = IDYES; - buttons[0].pszButtonText = verbCaps->Buffer; - buttons[1].nButtonID = IDNO; - buttons[1].pszButtonText = L"Cancel"; + buttons[0].nButtonID = IDYES; + buttons[0].pszButtonText = verbCaps->Buffer; + buttons[1].nButtonID = IDNO; + buttons[1].pszButtonText = L"Cancel"; - config.cButtons = 2; - config.pButtons = buttons; - config.nDefaultButton = IDYES; + config.cButtons = 2; + config.pButtons = buttons; + config.nDefaultButton = IDYES; - if (TaskDialogIndirect_Import()( - &config, - &button, - NULL, - NULL - ) == S_OK) - { - return button == IDYES; - } - else - { - return FALSE; - } + if (SUCCEEDED(TaskDialogIndirect( + &config, + &button, + NULL, + NULL + ))) + { + return button == IDYES; } else { - return PhShowMessage( - hWnd, - MB_YESNO | MB_ICONWARNING, - L"Are you sure you want to %s?", - action->Buffer - ) == IDYES; + return FALSE; } } @@ -862,7 +899,7 @@ VOID PhGenerateRandomAlphaString( Buffer[i] = 'A' + (RtlRandomEx(&seed) % 26); } - Buffer[Count - 1] = 0; + Buffer[Count - 1] = UNICODE_NULL; } /** @@ -880,7 +917,7 @@ PPH_STRING PhEllipsisString( ) { if ( - (ULONG)String->Length / 2 <= DesiredCount || + (ULONG)String->Length / sizeof(WCHAR) <= DesiredCount || DesiredCount < 3 ) { @@ -890,8 +927,8 @@ PPH_STRING PhEllipsisString( { PPH_STRING string; - string = PhCreateStringEx(NULL, DesiredCount * 2); - memcpy(string->Buffer, String->Buffer, (DesiredCount - 3) * 2); + string = PhCreateStringEx(NULL, DesiredCount * sizeof(WCHAR)); + memcpy(string->Buffer, String->Buffer, (DesiredCount - 3) * sizeof(WCHAR)); memcpy(&string->Buffer[DesiredCount - 3], L"...", 6); return string; @@ -922,7 +959,7 @@ PPH_STRING PhEllipsisStringPath( return PhEllipsisString(String, DesiredCount); if ( - String->Length / 2 <= DesiredCount || + String->Length / sizeof(WCHAR) <= DesiredCount || DesiredCount < 3 ) { @@ -934,8 +971,8 @@ PPH_STRING PhEllipsisStringPath( ULONG_PTR firstPartCopyLength; ULONG_PTR secondPartCopyLength; - string = PhCreateStringEx(NULL, DesiredCount * 2); - secondPartCopyLength = String->Length / 2 - secondPartIndex; + string = PhCreateStringEx(NULL, DesiredCount * sizeof(WCHAR)); + secondPartCopyLength = String->Length / sizeof(WCHAR) - secondPartIndex; // Check if we have enough space for the entire second part of the string. if (secondPartCopyLength + 3 <= DesiredCount) @@ -947,15 +984,15 @@ PPH_STRING PhEllipsisStringPath( { // No, copy part of both, from the beginning of the first part and the end of the second // part. - firstPartCopyLength = (DesiredCount - 3) / 2; + firstPartCopyLength = (DesiredCount - 3) / sizeof(WCHAR); secondPartCopyLength = DesiredCount - 3 - firstPartCopyLength; - secondPartIndex = String->Length / 2 - secondPartCopyLength; + secondPartIndex = String->Length / sizeof(WCHAR) - secondPartCopyLength; } memcpy( string->Buffer, String->Buffer, - firstPartCopyLength * 2 + firstPartCopyLength * sizeof(WCHAR) ); memcpy( &string->Buffer[firstPartCopyLength], @@ -965,7 +1002,7 @@ PPH_STRING PhEllipsisStringPath( memcpy( &string->Buffer[firstPartCopyLength + 3], &String->Buffer[secondPartIndex], - secondPartCopyLength * 2 + secondPartCopyLength * sizeof(WCHAR) ); return string; @@ -1125,6 +1162,9 @@ LONG PhCompareUnicodeStringZIgnoreMenuPrefix( { WCHAR t; + if (!A || !B) + return -1; + if (!IgnoreCase) { while (TRUE) @@ -1138,7 +1178,7 @@ LONG PhCompareUnicodeStringZIgnoreMenuPrefix( t = *A; - if (t == 0) + if (t == UNICODE_NULL) { if (MatchIfPrefix) return 0; @@ -1166,7 +1206,7 @@ LONG PhCompareUnicodeStringZIgnoreMenuPrefix( t = *A; - if (t == 0) + if (t == UNICODE_NULL) { if (MatchIfPrefix) return 0; @@ -1201,7 +1241,7 @@ PPH_STRING PhFormatDate( ULONG bufferSize; bufferSize = GetDateFormat(LOCALE_USER_DEFAULT, 0, Date, Format, NULL, 0); - string = PhCreateStringEx(NULL, bufferSize * 2); + string = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, Date, Format, string->Buffer, bufferSize)) { @@ -1230,7 +1270,7 @@ PPH_STRING PhFormatTime( ULONG bufferSize; bufferSize = GetTimeFormat(LOCALE_USER_DEFAULT, 0, Time, Format, NULL, 0); - string = PhCreateStringEx(NULL, bufferSize * 2); + string = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, Time, Format, string->Buffer, bufferSize)) { @@ -1262,7 +1302,7 @@ PPH_STRING PhFormatDateTime( timeBufferSize = GetTimeFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, NULL, 0); dateBufferSize = GetDateFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, NULL, 0); - string = PhCreateStringEx(NULL, (timeBufferSize + 1 + dateBufferSize) * 2); + string = PhCreateStringEx(NULL, (timeBufferSize + 1 + dateBufferSize) * sizeof(WCHAR)); if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, DateTime, NULL, &string->Buffer[0], timeBufferSize)) { @@ -1291,7 +1331,7 @@ PPH_STRING PhFormatTimeSpan( { PPH_STRING string; - string = PhCreateStringEx(NULL, PH_TIMESPAN_STR_LEN); + string = PhCreateStringEx(NULL, PH_TIMESPAN_STR_LEN * sizeof(WCHAR)); PhPrintTimeSpan(string->Buffer, Ticks, Mode); PhTrimToNullTerminatorString(string); @@ -1307,7 +1347,6 @@ PPH_STRING PhFormatTimeSpanRelative( _In_ ULONG64 TimeSpan ) { - PH_AUTO_POOL autoPool; PPH_STRING string; DOUBLE days; DOUBLE weeks; @@ -1316,8 +1355,6 @@ PPH_STRING PhFormatTimeSpanRelative( DOUBLE years; DOUBLE centuries; - PhInitializeAutoPool(&autoPool); - days = (DOUBLE)TimeSpan / PH_TICKS_PER_DAY; weeks = days / 7; fortnights = weeks / 2; @@ -1327,23 +1364,23 @@ PPH_STRING PhFormatTimeSpanRelative( if (centuries >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)centuries, (ULONG)centuries == 1 ? L"century" : L"centuries"); + string = PhFormatString(L"%u %s", (ULONG)centuries, (ULONG)centuries == 1 ? L"century" : L"centuries"); } else if (years >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)years, (ULONG)years == 1 ? L"year" : L"years"); + string = PhFormatString(L"%u %s", (ULONG)years, (ULONG)years == 1 ? L"year" : L"years"); } else if (months >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)months, (ULONG)months == 1 ? L"month" : L"months"); + string = PhFormatString(L"%u %s", (ULONG)months, (ULONG)months == 1 ? L"month" : L"months"); } else if (fortnights >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)fortnights, (ULONG)fortnights == 1 ? L"fortnight" : L"fortnights"); + string = PhFormatString(L"%u %s", (ULONG)fortnights, (ULONG)fortnights == 1 ? L"fortnight" : L"fortnights"); } else if (weeks >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)weeks, (ULONG)weeks == 1 ? L"week" : L"weeks"); + string = PhFormatString(L"%u %s", (ULONG)weeks, (ULONG)weeks == 1 ? L"week" : L"weeks"); } else { @@ -1362,45 +1399,72 @@ PPH_STRING PhFormatTimeSpanRelative( if (days >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)days, (ULONG)days == 1 ? L"day" : L"days"); hoursPartial = (ULONG)PH_TICKS_PARTIAL_HOURS(TimeSpan); if (hoursPartial >= 1) { - string = PhaFormatString(L"%s and %u %s", string->Buffer, hoursPartial, hoursPartial == 1 ? L"hour" : L"hours"); + string = PhFormatString( + L"%u %s and %u %s", + (ULONG)days, + (ULONG)days == 1 ? L"day" : L"days", + hoursPartial, + hoursPartial == 1 ? L"hour" : L"hours" + ); + } + else + { + string = PhFormatString(L"%u %s", (ULONG)days, (ULONG)days == 1 ? L"day" : L"days"); } } else if (hours >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)hours, (ULONG)hours == 1 ? L"hour" : L"hours"); minutesPartial = (ULONG)PH_TICKS_PARTIAL_MIN(TimeSpan); if (minutesPartial >= 1) { - string = PhaFormatString(L"%s and %u %s", string->Buffer, (ULONG)minutesPartial, (ULONG)minutesPartial == 1 ? L"minute" : L"minutes"); + string = PhFormatString( + L"%u %s and %u %s", + (ULONG)hours, + (ULONG)hours == 1 ? L"hour" : L"hours", + (ULONG)minutesPartial, + (ULONG)minutesPartial == 1 ? L"minute" : L"minutes" + ); + } + else + { + string = PhFormatString(L"%u %s", (ULONG)hours, (ULONG)hours == 1 ? L"hour" : L"hours"); } } else if (minutes >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)minutes, (ULONG)minutes == 1 ? L"minute" : L"minutes"); secondsPartial = (ULONG)PH_TICKS_PARTIAL_SEC(TimeSpan); if (secondsPartial >= 1) { - string = PhaFormatString(L"%s and %u %s", string->Buffer, (ULONG)secondsPartial, (ULONG)secondsPartial == 1 ? L"second" : L"seconds"); + string = PhFormatString( + L"%u %s and %u %s", + (ULONG)minutes, + (ULONG)minutes == 1 ? L"minute" : L"minutes", + (ULONG)secondsPartial, + (ULONG)secondsPartial == 1 ? L"second" : L"seconds" + ); + } + else + { + string = PhFormatString(L"%u %s", (ULONG)minutes, (ULONG)minutes == 1 ? L"minute" : L"minutes"); } } else if (seconds >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)seconds, (ULONG)seconds == 1 ? L"second" : L"seconds"); + string = PhFormatString(L"%u %s", (ULONG)seconds, (ULONG)seconds == 1 ? L"second" : L"seconds"); } else if (milliseconds >= 1) { - string = PhaFormatString(L"%u %s", (ULONG)milliseconds, (ULONG)milliseconds == 1 ? L"millisecond" : L"milliseconds"); + string = PhFormatString(L"%u %s", (ULONG)milliseconds, (ULONG)milliseconds == 1 ? L"millisecond" : L"milliseconds"); } else { - string = PhaCreateString(L"a very short time"); + string = PhCreateString(L"a very short time"); } } @@ -1409,14 +1473,11 @@ PPH_STRING PhFormatTimeSpanRelative( { // Special vowel case: a hour -> an hour if (string->Buffer[2] != 'h') - string = PhaConcatStrings2(L"a ", &string->Buffer[2]); + PhMoveReference(&string, PhConcatStrings2(L"a ", &string->Buffer[2])); else - string = PhaConcatStrings2(L"an ", &string->Buffer[2]); + PhMoveReference(&string, PhConcatStrings2(L"an ", &string->Buffer[2])); } - PhReferenceObject(string); - PhDeleteAutoPool(&autoPool); - return string; } @@ -1458,13 +1519,13 @@ PPH_STRING PhFormatDecimal( if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimalSeparator, 4)) { decimalSeparator[0] = '.'; - decimalSeparator[1] = 0; + decimalSeparator[1] = UNICODE_NULL; } if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousandSeparator, 4)) { thousandSeparator[0] = ','; - thousandSeparator[1] = 0; + thousandSeparator[1] = UNICODE_NULL; } PhEndInitOnce(&initOnce); @@ -1478,7 +1539,7 @@ PPH_STRING PhFormatDecimal( format.NegativeOrder = 1; bufferSize = GetNumberFormat(LOCALE_USER_DEFAULT, 0, Value, &format, NULL, 0); - string = PhCreateStringEx(NULL, bufferSize * 2); + string = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); if (!GetNumberFormat(LOCALE_USER_DEFAULT, 0, Value, &format, string->Buffer, bufferSize)) { @@ -1514,7 +1575,7 @@ PPH_STRING PhFormatSize( // PhFormat handles this better than the old method. format.Type = SizeFormatType | FormatUseRadix; - format.Radix = (UCHAR)(MaxSizeUnit != -1 ? MaxSizeUnit : PhMaxSizeUnit); + format.Radix = (UCHAR)(MaxSizeUnit != ULONG_MAX ? MaxSizeUnit : PhMaxSizeUnit); format.u.Size = Size; return PhFormat(&format, 1, 0); @@ -1553,37 +1614,32 @@ PVOID PhGetFileVersionInfo( _In_ PWSTR FileName ) { - ULONG versionInfoSize; - ULONG dummy; + PVOID libraryModule; PVOID versionInfo; - versionInfoSize = GetFileVersionInfoSize( + libraryModule = LoadLibraryEx( FileName, - &dummy + NULL, + LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE ); - if (versionInfoSize) - { - versionInfo = PhAllocate(versionInfoSize); - - if (!GetFileVersionInfo( - FileName, - 0, - versionInfoSize, - versionInfo - )) - { - PhFree(versionInfo); + if (!libraryModule) + return NULL; - return NULL; - } - } - else + if (PhLoadResource( + libraryModule, + MAKEINTRESOURCE(VS_VERSION_INFO), + VS_FILE_INFO, + NULL, + &versionInfo + )) { - return NULL; + FreeLibrary(libraryModule); + return versionInfo; } - return versionInfo; + FreeLibrary(libraryModule); + return NULL; } /** @@ -1627,6 +1683,10 @@ PPH_STRING PhGetFileVersionInfoString( { PPH_STRING string; + // Check if the string has a valid length. + if (length <= sizeof(UNICODE_NULL)) + return NULL; + string = PhCreateStringEx((PWCHAR)buffer, length * sizeof(WCHAR)); // length may include the null terminator. PhTrimToNullTerminatorString(string); @@ -1722,13 +1782,13 @@ BOOLEAN PhInitializeImageVersionInfo( // The version information is language-independent and must be read from the root block. if (VerQueryValue(versionInfo, L"\\", &rootBlock, &rootBlockLength) && rootBlockLength != 0) { - PhInitFormatU(&fileVersionFormat[0], rootBlock->dwFileVersionMS >> 16); + PhInitFormatU(&fileVersionFormat[0], HIWORD(rootBlock->dwFileVersionMS)); PhInitFormatC(&fileVersionFormat[1], '.'); - PhInitFormatU(&fileVersionFormat[2], rootBlock->dwFileVersionMS & 0xffff); + PhInitFormatU(&fileVersionFormat[2], LOWORD(rootBlock->dwFileVersionMS)); PhInitFormatC(&fileVersionFormat[3], '.'); - PhInitFormatU(&fileVersionFormat[4], rootBlock->dwFileVersionLS >> 16); + PhInitFormatU(&fileVersionFormat[4], HIWORD(rootBlock->dwFileVersionLS)); PhInitFormatC(&fileVersionFormat[5], '.'); - PhInitFormatU(&fileVersionFormat[6], rootBlock->dwFileVersionLS & 0xffff); + PhInitFormatU(&fileVersionFormat[6], LOWORD(rootBlock->dwFileVersionLS)); ImageVersionInfo->FileVersion = PhFormat(fileVersionFormat, 7, 30); } @@ -1866,64 +1926,229 @@ PPH_STRING PhFormatImageVersionInfo( return PhFinalStringBuilderString(&stringBuilder); } -/** - * Gets an absolute file name. - * - * \param FileName A file name. - * \param IndexOfFileName A variable which receives the index of the base name. - * - * \return An absolute file name, or NULL if the function failed. - */ -PPH_STRING PhGetFullPath( - _In_ PWSTR FileName, - _Out_opt_ PULONG IndexOfFileName +typedef struct _PH_FILE_VERSIONINFO_CACHE_ENTRY +{ + PPH_STRING FileName; + PPH_STRING CompanyName; + PPH_STRING FileDescription; + PPH_STRING FileVersion; + PPH_STRING ProductName; +} PH_FILE_VERSIONINFO_CACHE_ENTRY, *PPH_FILE_VERSIONINFO_CACHE_ENTRY; + +static PPH_HASHTABLE PhpImageVersionInfoCacheHashtable = NULL; +static PH_QUEUED_LOCK PhpImageVersionInfoCacheLock = PH_QUEUED_LOCK_INIT; + +static BOOLEAN PhpImageVersionInfoCacheHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 ) { - PPH_STRING fullPath; - ULONG bufferSize; - ULONG returnLength; - PWSTR filePart; + PPH_FILE_VERSIONINFO_CACHE_ENTRY entry1 = Entry1; + PPH_FILE_VERSIONINFO_CACHE_ENTRY entry2 = Entry2; - bufferSize = 0x80; - fullPath = PhCreateStringEx(NULL, bufferSize * 2); + return PhEqualString(entry1->FileName, entry2->FileName, TRUE); +} - returnLength = RtlGetFullPathName_U(FileName, bufferSize, fullPath->Buffer, &filePart); +static ULONG PhpImageVersionInfoCacheHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PPH_FILE_VERSIONINFO_CACHE_ENTRY entry = Entry; - if (returnLength > bufferSize) - { - PhDereferenceObject(fullPath); - bufferSize = returnLength; - fullPath = PhCreateStringEx(NULL, bufferSize * 2); + return PhHashStringRef(&entry->FileName->sr, TRUE); +} - returnLength = RtlGetFullPathName_U(FileName, bufferSize, fullPath->Buffer, &filePart); - } +BOOLEAN PhInitializeImageVersionInfoCached( + _Out_ PPH_IMAGE_VERSION_INFO ImageVersionInfo, + _In_ PPH_STRING FileName, + _In_ BOOLEAN IsSubsystemProcess + ) +{ + PH_IMAGE_VERSION_INFO versionInfo = { 0 }; + PH_FILE_VERSIONINFO_CACHE_ENTRY newEntry; - if (returnLength == 0) + if (PhpImageVersionInfoCacheHashtable) { - PhDereferenceObject(fullPath); - return NULL; - } + PPH_FILE_VERSIONINFO_CACHE_ENTRY entry; + PH_FILE_VERSIONINFO_CACHE_ENTRY lookupEntry; - PhTrimToNullTerminatorString(fullPath); + lookupEntry.FileName = FileName; - if (IndexOfFileName) - { - if (filePart) - { - // The path points to a file. - *IndexOfFileName = (ULONG)(filePart - fullPath->Buffer); - } - else + PhAcquireQueuedLockShared(&PhpImageVersionInfoCacheLock); + entry = PhFindEntryHashtable(PhpImageVersionInfoCacheHashtable, &lookupEntry); + PhReleaseQueuedLockShared(&PhpImageVersionInfoCacheLock); + + if (entry) { - // The path points to a directory. - *IndexOfFileName = -1; + PhSetReference(&ImageVersionInfo->CompanyName, entry->CompanyName); + PhSetReference(&ImageVersionInfo->FileDescription, entry->FileDescription); + PhSetReference(&ImageVersionInfo->FileVersion, entry->FileVersion); + PhSetReference(&ImageVersionInfo->ProductName, entry->ProductName); + + return TRUE; } } - return fullPath; -} + if (IsSubsystemProcess) + { + if (!PhInitializeLxssImageVersionInfo(&versionInfo, FileName)) + return FALSE; + } + else + { + if (!PhInitializeImageVersionInfo(&versionInfo, FileName->Buffer)) + return FALSE; + } -/** + if (!PhpImageVersionInfoCacheHashtable) + { + PhpImageVersionInfoCacheHashtable = PhCreateHashtable( + sizeof(PH_FILE_VERSIONINFO_CACHE_ENTRY), + PhpImageVersionInfoCacheHashtableEqualFunction, + PhpImageVersionInfoCacheHashtableHashFunction, + 100 + ); + } + + PhSetReference(&newEntry.FileName, FileName); + PhSetReference(&newEntry.CompanyName, versionInfo.CompanyName); + PhSetReference(&newEntry.FileDescription, versionInfo.FileDescription); + PhSetReference(&newEntry.FileVersion, versionInfo.FileVersion); + PhSetReference(&newEntry.ProductName, versionInfo.ProductName); + + PhAcquireQueuedLockExclusive(&PhpImageVersionInfoCacheLock); + PhAddEntryHashtable(PhpImageVersionInfoCacheHashtable, &newEntry); + PhReleaseQueuedLockExclusive(&PhpImageVersionInfoCacheLock); + + ImageVersionInfo->CompanyName = versionInfo.CompanyName; + ImageVersionInfo->FileDescription = versionInfo.FileDescription; + ImageVersionInfo->FileVersion = versionInfo.FileVersion; + ImageVersionInfo->ProductName = versionInfo.ProductName; + return TRUE; +} + +VOID PhFlushImageVersionInfoCache( + VOID + ) +{ + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_FILE_VERSIONINFO_CACHE_ENTRY entry; + + if (!PhpImageVersionInfoCacheHashtable) + return; + + PhAcquireQueuedLockExclusive(&PhpImageVersionInfoCacheLock); + + PhBeginEnumHashtable(PhpImageVersionInfoCacheHashtable, &enumContext); + + while (entry = PhNextEnumHashtable(&enumContext)) + { + if (entry->FileName) PhDereferenceObject(entry->FileName); + if (entry->CompanyName) PhDereferenceObject(entry->CompanyName); + if (entry->FileDescription) PhDereferenceObject(entry->FileDescription); + if (entry->FileVersion) PhDereferenceObject(entry->FileVersion); + if (entry->ProductName) PhDereferenceObject(entry->ProductName); + } + + PhClearReference(&PhpImageVersionInfoCacheHashtable); + + PhpImageVersionInfoCacheHashtable = PhCreateHashtable( + sizeof(PH_FILE_VERSIONINFO_CACHE_ENTRY), + PhpImageVersionInfoCacheHashtableEqualFunction, + PhpImageVersionInfoCacheHashtableHashFunction, + 100 + ); + + PhReleaseQueuedLockExclusive(&PhpImageVersionInfoCacheLock); +} + +/** + * Gets an absolute file name. + * + * \param FileName A file name. + * \param IndexOfFileName A variable which receives the index of the base name. + * + * \return An absolute file name, or NULL if the function failed. + */ +PPH_STRING PhGetFullPath( + _In_ PWSTR FileName, + _Out_opt_ PULONG IndexOfFileName + ) +{ + PPH_STRING fullPath; + + if (!NT_SUCCESS(PhGetFullPathEx(FileName, IndexOfFileName, &fullPath))) + return NULL; + + return fullPath; +} + +NTSTATUS PhGetFullPathEx( + _In_ PWSTR FileName, + _Out_opt_ PULONG IndexOfFileName, + _Out_ PPH_STRING *FullPath + ) +{ + NTSTATUS status; + PPH_STRING fullPath; + ULONG bufferSize; + ULONG returnLength; + PWSTR filePart; + + bufferSize = 0x80; + fullPath = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); + + status = RtlGetFullPathName_UEx( + FileName, + bufferSize, + fullPath->Buffer, + &filePart, + &returnLength + ); + + if (returnLength > bufferSize) + { + PhDereferenceObject(fullPath); + bufferSize = returnLength; + fullPath = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); + + status = RtlGetFullPathName_UEx( + FileName, + bufferSize, + fullPath->Buffer, + &filePart, + &returnLength + ); + } + + if (!NT_SUCCESS(status)) + { + PhDereferenceObject(fullPath); + return status; + } + + PhTrimToNullTerminatorString(fullPath); + + if (IndexOfFileName) + { + if (filePart) + { + // The path points to a file. + *IndexOfFileName = (ULONG)(filePart - fullPath->Buffer); + } + else + { + // The path points to a directory. + *IndexOfFileName = ULONG_MAX; + } + } + + *FullPath = fullPath; + + return status; +} + +/** * Expands environment variables in a string. * * \param String The string. @@ -1977,7 +2202,7 @@ PPH_STRING PhExpandEnvironmentStrings( } string->Length = outputString.Length; - string->Buffer[string->Length / 2] = 0; // make sure there is a null terminator + string->Buffer[string->Length / sizeof(WCHAR)] = UNICODE_NULL; // make sure there is a null terminator return string; } @@ -1994,12 +2219,30 @@ PPH_STRING PhGetBaseName( PH_STRINGREF pathPart; PH_STRINGREF baseNamePart; - if (!PhSplitStringRefAtLastChar(&FileName->sr, '\\', &pathPart, &baseNamePart)) + if (!PhSplitStringRefAtLastChar(&FileName->sr, OBJ_NAME_PATH_SEPARATOR, &pathPart, &baseNamePart)) return PhReferenceObject(FileName); return PhCreateString2(&baseNamePart); } +/** + * Gets the parent directory from a file name. + * + * \param FileName The file name. + */ +PPH_STRING PhGetBaseDirectory( + _In_ PPH_STRING FileName + ) +{ + PH_STRINGREF pathPart; + PH_STRINGREF baseNamePart; + + if (!PhSplitStringRefAtLastChar(&FileName->sr, OBJ_NAME_PATH_SEPARATOR, &pathPart, &baseNamePart)) + return NULL; + + return PhCreateString2(&pathPart); +} + /** * Retrieves the system directory path. */ @@ -2007,43 +2250,21 @@ PPH_STRING PhGetSystemDirectory( VOID ) { + static PH_STRINGREF system32String = PH_STRINGREF_INIT(L"\\System32"); static PPH_STRING cachedSystemDirectory = NULL; - PPH_STRING systemDirectory; - ULONG bufferSize; - ULONG returnLength; + PH_STRINGREF systemRootString; // Use the cached value if possible. - systemDirectory = cachedSystemDirectory; - - if (systemDirectory) + if (systemDirectory = InterlockedCompareExchangePointer(&cachedSystemDirectory, NULL, NULL)) return PhReferenceObject(systemDirectory); - bufferSize = 0x40; - systemDirectory = PhCreateStringEx(NULL, bufferSize * 2); - - returnLength = GetSystemDirectory(systemDirectory->Buffer, bufferSize); - - if (returnLength > bufferSize) - { - PhDereferenceObject(systemDirectory); - bufferSize = returnLength; - systemDirectory = PhCreateStringEx(NULL, bufferSize * 2); - - returnLength = GetSystemDirectory(systemDirectory->Buffer, bufferSize); - } - - if (returnLength == 0) - { - PhDereferenceObject(systemDirectory); - return NULL; - } - - PhTrimToNullTerminatorString(systemDirectory); + PhGetSystemRoot(&systemRootString); + systemDirectory = PhConcatStringRef2(&systemRootString, &system32String); // Try to cache the value. - if (_InterlockedCompareExchangePointer( + if (InterlockedCompareExchangePointer( &cachedSystemDirectory, systemDirectory, NULL @@ -2064,7 +2285,6 @@ VOID PhGetSystemRoot( ) { static PH_STRINGREF systemRoot; - PH_STRINGREF localSystemRoot; SIZE_T count; @@ -2079,7 +2299,7 @@ VOID PhGetSystemRoot( localSystemRoot.Length = count * sizeof(WCHAR); // Make sure the system root string doesn't have a trailing backslash. - if (localSystemRoot.Buffer[count - 1] == '\\') + if (localSystemRoot.Buffer[count - 1] == OBJ_NAME_PATH_SEPARATOR) localSystemRoot.Length -= sizeof(WCHAR); *SystemRoot = localSystemRoot; @@ -2089,106 +2309,6 @@ VOID PhGetSystemRoot( systemRoot.Buffer = localSystemRoot.Buffer; } -/** - * Locates a loader entry in the current process. - * - * \param DllBase The base address of the DLL. Specify NULL if this is not a search criteria. - * \param FullDllName The full name of the DLL. Specify NULL if this is not a search criteria. - * \param BaseDllName The base name of the DLL. Specify NULL if this is not a search criteria. - * - * \remarks This function must be called with the loader lock acquired. The first entry matching all - * of the specified values is returned. - */ -PLDR_DATA_TABLE_ENTRY PhFindLoaderEntry( - _In_opt_ PVOID DllBase, - _In_opt_ PPH_STRINGREF FullDllName, - _In_opt_ PPH_STRINGREF BaseDllName - ) -{ - PLDR_DATA_TABLE_ENTRY result = NULL; - PLDR_DATA_TABLE_ENTRY entry; - PH_STRINGREF fullDllName; - PH_STRINGREF baseDllName; - PLIST_ENTRY listHead; - PLIST_ENTRY listEntry; - - listHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; - listEntry = listHead->Flink; - - while (listEntry != listHead) - { - entry = CONTAINING_RECORD(listEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); - PhUnicodeStringToStringRef(&entry->FullDllName, &fullDllName); - PhUnicodeStringToStringRef(&entry->BaseDllName, &baseDllName); - - if ( - (!DllBase || entry->DllBase == DllBase) && - (!FullDllName || PhEqualStringRef(&fullDllName, FullDllName, TRUE)) && - (!BaseDllName || PhEqualStringRef(&baseDllName, BaseDllName, TRUE)) - ) - { - result = entry; - break; - } - - listEntry = listEntry->Flink; - } - - return result; -} - -/** - * Retrieves the file name of a DLL loaded by the current process. - * - * \param DllHandle The base address of the DLL. - * \param IndexOfFileName A variable which receives the index of the base name of the DLL in the - * returned string. - * - * \return The file name of the DLL, or NULL if the DLL could not be found. - */ -PPH_STRING PhGetDllFileName( - _In_ PVOID DllHandle, - _Out_opt_ PULONG IndexOfFileName - ) -{ - PLDR_DATA_TABLE_ENTRY entry; - PPH_STRING fileName; - PPH_STRING newFileName; - ULONG_PTR indexOfFileName; - - RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); - - entry = PhFindLoaderEntry(DllHandle, NULL, NULL); - - if (entry) - fileName = PhCreateStringFromUnicodeString(&entry->FullDllName); - else - fileName = NULL; - - RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); - - if (!fileName) - return NULL; - - newFileName = PhGetFileName(fileName); - PhDereferenceObject(fileName); - fileName = newFileName; - - if (IndexOfFileName) - { - indexOfFileName = PhFindLastCharInString(fileName, 0, '\\'); - - if (indexOfFileName != -1) - indexOfFileName++; - else - indexOfFileName = 0; - - *IndexOfFileName = (ULONG)indexOfFileName; - } - - return fileName; -} - /** * Retrieves the file name of the current process image. */ @@ -2196,7 +2316,15 @@ PPH_STRING PhGetApplicationFileName( VOID ) { - return PhGetDllFileName(NtCurrentPeb()->ImageBaseAddress, NULL); + //PPH_STRING fileName; + // + //if (NT_SUCCESS(PhGetProcessImageFileNameWin32(NtCurrentProcess(), &fileName))) + //{ + // PhMoveReference(&fileName, PhGetFileName(fileName)); + // return fileName; + //} + + return PhGetDllFileName(PhInstanceHandle, NULL); } /** @@ -2207,13 +2335,20 @@ PPH_STRING PhGetApplicationDirectory( ) { PPH_STRING fileName; - ULONG indexOfFileName; + ULONG_PTR indexOfFileName; PPH_STRING path = NULL; - fileName = PhGetDllFileName(NtCurrentPeb()->ImageBaseAddress, &indexOfFileName); + fileName = PhGetApplicationFileName(); if (fileName) { + indexOfFileName = PhFindLastCharInString(fileName, 0, OBJ_NAME_PATH_SEPARATOR); + + if (indexOfFileName != -1) + indexOfFileName++; + else + indexOfFileName = 0; + if (indexOfFileName != 0) { // Remove the file name from the path. @@ -2241,11 +2376,11 @@ PPH_STRING PhGetKnownLocation( SIZE_T appendPathLength; if (AppendPath) - appendPathLength = PhCountStringZ(AppendPath) * 2; + appendPathLength = PhCountStringZ(AppendPath) * sizeof(WCHAR); else appendPathLength = 0; - path = PhCreateStringEx(NULL, MAX_PATH * 2 + appendPathLength); + path = PhCreateStringEx(NULL, MAX_PATH * sizeof(WCHAR) + appendPathLength); if (SUCCEEDED(SHGetFolderPath( NULL, @@ -2259,7 +2394,7 @@ PPH_STRING PhGetKnownLocation( if (AppendPath) { - memcpy(&path->Buffer[path->Length / 2], AppendPath, appendPathLength + 2); // +2 for null terminator + memcpy(&path->Buffer[path->Length / sizeof(WCHAR)], AppendPath, appendPathLength + sizeof(UNICODE_NULL)); // +2 for null terminator path->Length += appendPathLength; } @@ -2291,11 +2426,11 @@ NTSTATUS PhWaitForMultipleObjectsAndPump( ) { NTSTATUS status; - ULONG startTickCount; - ULONG currentTickCount; - LONG currentTimeout; + ULONG64 startTickCount; + ULONG64 currentTickCount; + ULONG64 currentTimeout; - startTickCount = GetTickCount(); + startTickCount = NtGetTickCount64(); currentTimeout = Timeout; while (TRUE) @@ -2308,7 +2443,10 @@ NTSTATUS PhWaitForMultipleObjectsAndPump( QS_ALLEVENTS ); - if (status >= STATUS_WAIT_0 && status < (NTSTATUS)(STATUS_WAIT_0 + NumberOfHandles)) + if ( + status >= STATUS_WAIT_0 && status < (NTSTATUS)(STATUS_WAIT_0 + NumberOfHandles) || + status >= STATUS_ABANDONED_WAIT_0 && status < (NTSTATUS)(STATUS_ABANDONED_WAIT_0 + NumberOfHandles) + ) { return status; } @@ -2333,10 +2471,10 @@ NTSTATUS PhWaitForMultipleObjectsAndPump( if (Timeout != INFINITE) { - currentTickCount = GetTickCount(); + currentTickCount = NtGetTickCount64(); currentTimeout = Timeout - (currentTickCount - startTickCount); - if (currentTimeout < 0) + if ((LONG64)currentTimeout < 0) return STATUS_TIMEOUT; } } @@ -2460,20 +2598,20 @@ NTSTATUS PhCreateProcess( if (NT_SUCCESS(status)) { if (!(Flags & PH_CREATE_PROCESS_SUSPENDED)) - NtResumeThread(processInfo.Thread, NULL); + NtResumeThread(processInfo.ThreadHandle, NULL); if (ClientId) *ClientId = processInfo.ClientId; if (ProcessHandle) - *ProcessHandle = processInfo.Process; + *ProcessHandle = processInfo.ProcessHandle; else - NtClose(processInfo.Process); + NtClose(processInfo.ProcessHandle); if (ThreadHandle) - *ThreadHandle = processInfo.Thread; + *ThreadHandle = processInfo.ThreadHandle; else - NtClose(processInfo.Thread); + NtClose(processInfo.ThreadHandle); } return status; @@ -2523,7 +2661,10 @@ static const PH_FLAG_MAPPING PhpCreateProcessMappings[] = { PH_CREATE_PROCESS_UNICODE_ENVIRONMENT, CREATE_UNICODE_ENVIRONMENT }, { PH_CREATE_PROCESS_SUSPENDED, CREATE_SUSPENDED }, { PH_CREATE_PROCESS_BREAKAWAY_FROM_JOB, CREATE_BREAKAWAY_FROM_JOB }, - { PH_CREATE_PROCESS_NEW_CONSOLE, CREATE_NEW_CONSOLE } + { PH_CREATE_PROCESS_NEW_CONSOLE, CREATE_NEW_CONSOLE }, + { PH_CREATE_PROCESS_DEBUG, DEBUG_PROCESS }, + { PH_CREATE_PROCESS_DEBUG_ONLY_THIS_PROCESS, DEBUG_ONLY_THIS_PROCESS }, + { PH_CREATE_PROCESS_EXTENDED_STARTUPINFO, EXTENDED_STARTUPINFO_PRESENT } }; FORCEINLINE VOID PhpConvertProcessInformation( @@ -2541,12 +2682,12 @@ FORCEINLINE VOID PhpConvertProcessInformation( if (ProcessHandle) *ProcessHandle = ProcessInfo->hProcess; - else + else if (ProcessInfo->hProcess) NtClose(ProcessInfo->hProcess); if (ThreadHandle) *ThreadHandle = ProcessInfo->hThread; - else + else if (ProcessInfo->hThread) NtClose(ProcessInfo->hThread); } @@ -2580,39 +2721,74 @@ NTSTATUS PhCreateProcessWin32Ex( ) { NTSTATUS status; + PPH_STRING fileName = NULL; PPH_STRING commandLine = NULL; + PPH_STRING currentDirectory = NULL; STARTUPINFO startupInfo; PROCESS_INFORMATION processInfo; ULONG newFlags; - if (CommandLine) // duplicate because CreateProcess modifies the string + if (CommandLine) // duplicate because CreateProcess modifies the string (wj32) commandLine = PhCreateString(CommandLine); - newFlags = 0; - PhMapFlags1(&newFlags, Flags, PhpCreateProcessMappings, sizeof(PhpCreateProcessMappings) / sizeof(PH_FLAG_MAPPING)); - - if (StartupInfo) + if (FileName) + fileName = PhCreateString(FileName); + else { - startupInfo = *StartupInfo; + INT cmdlineArgCount; + PWSTR* cmdlineArgList; + + // Try extract the filename or CreateProcess might execute the wrong executable. (dmex) + if (commandLine && (cmdlineArgList = CommandLineToArgvW(commandLine->Buffer, &cmdlineArgCount))) + { + PhMoveReference(&fileName, PhCreateString(cmdlineArgList[0])); + LocalFree(cmdlineArgList); + } + + if (fileName && !PhDoesFileExistsWin32(fileName->Buffer)) + { + PPH_STRING filePathSr; + + // The user typed a name without a path so attempt to locate the executable. (dmex) + if (filePathSr = PhSearchFilePath(fileName->Buffer, L".exe")) + PhMoveReference(&fileName, filePathSr); + else + PhClearReference(&fileName); + } } + + // Set the current directory to the same location as the target executable + // or CreateProcess uses our current directory. (dmex) + if (CurrentDirectory) + currentDirectory = PhCreateString(CurrentDirectory); else + { + if (!PhIsNullOrEmptyString(fileName)) + currentDirectory = PhGetBaseDirectory(fileName); + } + + newFlags = 0; + PhMapFlags1(&newFlags, Flags, PhpCreateProcessMappings, sizeof(PhpCreateProcessMappings) / sizeof(PH_FLAG_MAPPING)); + + if (!StartupInfo) { memset(&startupInfo, 0, sizeof(STARTUPINFO)); startupInfo.cb = sizeof(STARTUPINFO); } - if (!TokenHandle) + if (TokenHandle) { - if (CreateProcess( - FileName, + if (CreateProcessAsUser( + TokenHandle, + PhGetString(fileName), PhGetString(commandLine), NULL, NULL, !!(Flags & PH_CREATE_PROCESS_INHERIT_HANDLES), newFlags, Environment, - CurrentDirectory, - &startupInfo, + PhGetString(currentDirectory), + StartupInfo ? StartupInfo : &startupInfo, &processInfo )) status = STATUS_SUCCESS; @@ -2621,17 +2797,16 @@ NTSTATUS PhCreateProcessWin32Ex( } else { - if (CreateProcessAsUser( - TokenHandle, - FileName, + if (CreateProcess( + PhGetString(fileName), PhGetString(commandLine), NULL, NULL, !!(Flags & PH_CREATE_PROCESS_INHERIT_HANDLES), newFlags, Environment, - CurrentDirectory, - &startupInfo, + PhGetString(currentDirectory), + StartupInfo ? StartupInfo : &startupInfo, &processInfo )) status = STATUS_SUCCESS; @@ -2639,8 +2814,12 @@ NTSTATUS PhCreateProcessWin32Ex( status = PhGetLastWin32ErrorAsNtStatus(); } + if (fileName) + PhDereferenceObject(fileName); if (commandLine) PhDereferenceObject(commandLine); + if (currentDirectory) + PhDereferenceObject(currentDirectory); if (NT_SUCCESS(status)) { @@ -2675,37 +2854,19 @@ NTSTATUS PhCreateProcessAsUser( _Out_opt_ PHANDLE ThreadHandle ) { - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static _WinStationQueryInformationW WinStationQueryInformationW_I = NULL; - static _CreateEnvironmentBlock CreateEnvironmentBlock_I = NULL; - static _DestroyEnvironmentBlock DestroyEnvironmentBlock_I = NULL; - NTSTATUS status; HANDLE tokenHandle; PVOID defaultEnvironment = NULL; STARTUPINFO startupInfo = { sizeof(startupInfo) }; BOOLEAN needsDuplicate = FALSE; - if (PhBeginInitOnce(&initOnce)) - { - HMODULE winsta; - HMODULE userEnv; - - winsta = LoadLibrary(L"winsta.dll"); - WinStationQueryInformationW_I = PhGetProcedureAddress(winsta, "WinStationQueryInformationW", 0); - - userEnv = LoadLibrary(L"userenv.dll"); - CreateEnvironmentBlock_I = PhGetProcedureAddress(userEnv, "CreateEnvironmentBlock", 0); - DestroyEnvironmentBlock_I = PhGetProcedureAddress(userEnv, "DestroyEnvironmentBlock", 0); - - PhEndInitOnce(&initOnce); - } - if ((Flags & PH_CREATE_PROCESS_USE_PROCESS_TOKEN) && (Flags & PH_CREATE_PROCESS_USE_SESSION_TOKEN)) return STATUS_INVALID_PARAMETER_2; if (!Information->ApplicationName && !Information->CommandLine) return STATUS_INVALID_PARAMETER_MIX; + startupInfo.dwFlags = STARTF_USESHOWWINDOW; + startupInfo.wShowWindow = SW_NORMAL; startupInfo.lpDesktop = Information->DesktopName; // Try to use CreateProcessWithLogonW if we need to load the user profile. @@ -2724,7 +2885,11 @@ NTSTATUS PhCreateProcessAsUser( if (Flags & PH_CREATE_PROCESS_SET_SESSION_ID) { - if (Information->SessionId != NtCurrentPeb()->SessionId) + ULONG sessionId = ULONG_MAX; + + PhGetProcessSessionId(NtCurrentProcess(), &sessionId); + + if (Information->SessionId != sessionId) useWithLogon = FALSE; } @@ -2785,7 +2950,7 @@ NTSTATUS PhCreateProcessAsUser( if (!NT_SUCCESS(status = PhOpenProcess( &processHandle, - ProcessQueryAccess, + PROCESS_QUERY_LIMITED_INFORMATION, Information->ProcessIdWithToken ))) return status; @@ -2808,10 +2973,7 @@ NTSTATUS PhCreateProcessAsUser( WINSTATIONUSERTOKEN userToken; ULONG returnLength; - if (!WinStationQueryInformationW_I) - return STATUS_PROCEDURE_NOT_FOUND; - - if (!WinStationQueryInformationW_I( + if (!WinStationQueryInformationW( NULL, Information->SessionIdWithToken, WinStationUserToken, @@ -2843,15 +3005,8 @@ NTSTATUS PhCreateProcessAsUser( // Check if this is a service logon. if (PhEqualStringZ(Information->DomainName, L"NT AUTHORITY", TRUE)) { - if (PhEqualStringZ(Information->UserName, L"SYSTEM", TRUE)) - { - if (WindowsVersion >= WINDOWS_VISTA) - logonType = LOGON32_LOGON_SERVICE; - else - logonType = LOGON32_LOGON_NEW_CREDENTIALS; // HACK - } - - if (PhEqualStringZ(Information->UserName, L"LOCAL SERVICE", TRUE) || + if (PhEqualStringZ(Information->UserName, L"SYSTEM", TRUE) || + PhEqualStringZ(Information->UserName, L"LOCAL SERVICE", TRUE) || PhEqualStringZ(Information->UserName, L"NETWORK SERVICE", TRUE)) { logonType = LOGON32_LOGON_SERVICE; @@ -2947,9 +3102,9 @@ NTSTATUS PhCreateProcessAsUser( if (!Information->Environment) { - if (CreateEnvironmentBlock_I) + if (CreateEnvironmentBlock) { - CreateEnvironmentBlock_I(&defaultEnvironment, tokenHandle, FALSE); + CreateEnvironmentBlock(&defaultEnvironment, tokenHandle, FALSE); if (defaultEnvironment) Flags |= PH_CREATE_PROCESS_UNICODE_ENVIRONMENT; @@ -2971,8 +3126,8 @@ NTSTATUS PhCreateProcessAsUser( if (defaultEnvironment) { - if (DestroyEnvironmentBlock_I) - DestroyEnvironmentBlock_I(defaultEnvironment); + if (DestroyEnvironmentBlock) + DestroyEnvironmentBlock(defaultEnvironment); } NtClose(tokenHandle); @@ -2980,38 +3135,6 @@ NTSTATUS PhCreateProcessAsUser( return status; } -NTSTATUS PhpGetAccountPrivileges( - _In_ PSID AccountSid, - _Out_ PTOKEN_PRIVILEGES *Privileges - ) -{ - NTSTATUS status; - LSA_HANDLE accountHandle; - PPRIVILEGE_SET accountPrivileges; - PTOKEN_PRIVILEGES privileges; - - status = LsaOpenAccount(PhGetLookupPolicyHandle(), AccountSid, ACCOUNT_VIEW, &accountHandle); - - if (!NT_SUCCESS(status)) - return status; - - status = LsaEnumeratePrivilegesOfAccount(accountHandle, &accountPrivileges); - LsaClose(accountHandle); - - if (!NT_SUCCESS(status)) - return status; - - privileges = PhAllocate(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(LUID_AND_ATTRIBUTES) * accountPrivileges->PrivilegeCount); - privileges->PrivilegeCount = accountPrivileges->PrivilegeCount; - memcpy(privileges->Privileges, accountPrivileges->Privilege, sizeof(LUID_AND_ATTRIBUTES) * accountPrivileges->PrivilegeCount); - - LsaFreeMemory(accountPrivileges); - - *Privileges = privileges; - - return status; -} - /** * Filters a token to create a limited user security context. * @@ -3091,7 +3214,7 @@ NTSTATUS PhFilterTokenForLimitedUser( return status; // Get the privileges of the Users group - the privileges that we are going to allow. - if (!NT_SUCCESS(PhpGetAccountPrivileges(usersSid, &privilegesOfUsers))) + if (!NT_SUCCESS(PhGetAccountPrivileges(usersSid, &privilegesOfUsers))) { // Unsuccessful, so use the default set of privileges. privilegesOfUsers = PhAllocate(FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges) + sizeof(defaultAllowedPrivileges)); @@ -3148,7 +3271,6 @@ NTSTATUS PhFilterTokenForLimitedUser( return status; // Set the integrity level to Low if we're on Vista and above. - if (WINDOWS_HAS_UAC) { lowMandatoryLevelSid = (PSID)lowMandatoryLevelSidBuffer; RtlInitializeSid(lowMandatoryLevelSid, &mandatoryLabelAuthority, 1); @@ -3234,17 +3356,17 @@ VOID PhShellExecute( _In_opt_ PWSTR Parameters ) { - SHELLEXECUTEINFO info = { sizeof(info) }; + SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; info.lpFile = FileName; info.lpParameters = Parameters; + info.fMask = SEE_MASK_FLAG_NO_UI; info.nShow = SW_SHOW; info.hwnd = hWnd; if (!ShellExecuteEx(&info)) { - // It already displays error messages by itself. - //PhShowStatus(hWnd, L"Unable to execute the program", 0, GetLastError()); + PhShowStatus(hWnd, L"Unable to execute the program.", 0, GetLastError()); } } @@ -3277,12 +3399,14 @@ BOOLEAN PhShellExecuteEx( info.lpFile = FileName; info.lpParameters = Parameters; - info.fMask = SEE_MASK_NOCLOSEPROCESS; + info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; info.nShow = ShowWindowType; info.hwnd = hWnd; - if ((Flags & PH_SHELL_EXECUTE_ADMIN) && WINDOWS_HAS_UAC) + if (Flags & PH_SHELL_EXECUTE_ADMIN) info.lpVerb = L"runas"; + if (Flags & PH_SHELL_EXECUTE_NOZONECHECKS) + info.fMask |= SEE_MASK_NOZONECHECKS; if (ShellExecuteEx(&info)) { @@ -3302,7 +3426,7 @@ BOOLEAN PhShellExecuteEx( if (ProcessHandle) *ProcessHandle = info.hProcess; - else + else if (info.hProcess) NtClose(info.hProcess); return TRUE; @@ -3324,19 +3448,19 @@ VOID PhShellExploreFile( _In_ PWSTR FileName ) { - if (SHOpenFolderAndSelectItems_Import() && SHParseDisplayName_Import()) + if (SHOpenFolderAndSelectItems && SHParseDisplayName) { LPITEMIDLIST item; SFGAOF attributes; - if (SUCCEEDED(SHParseDisplayName_Import()(FileName, NULL, &item, 0, &attributes))) + if (SUCCEEDED(SHParseDisplayName(FileName, NULL, &item, 0, &attributes))) { - SHOpenFolderAndSelectItems_Import()(item, 0, NULL, 0); + SHOpenFolderAndSelectItems(item, 0, NULL, 0); CoTaskMemFree(item); } else { - PhShowError(hWnd, L"The location \"%s\" could not be found.", FileName); + PhShowError2(hWnd, L"The location could not be found.", L"%s", FileName); } } else @@ -3364,14 +3488,13 @@ VOID PhShellProperties( info.lpFile = FileName; info.nShow = SW_SHOW; - info.fMask = SEE_MASK_INVOKEIDLIST; + info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI; info.lpVerb = L"properties"; info.hwnd = hWnd; if (!ShellExecuteEx(&info)) { - // It already displays error messages by itself. - //PhShowStatus(hWnd, L"Unable to execute the program", 0, GetLastError()); + PhShowStatus(hWnd, L"Unable to execute the program.", 0, GetLastError()); } } @@ -3412,11 +3535,7 @@ PPH_STRING PhExpandKeyName( if (Computer) { - if (WindowsVersion >= WINDOWS_VISTA) - tempString = PhConcatStrings2(L"Computer\\", keyName->Buffer); - else - tempString = PhConcatStrings2(L"My Computer\\", keyName->Buffer); - + tempString = PhConcatStrings2(L"Computer\\", keyName->Buffer); PhDereferenceObject(keyName); keyName = tempString; } @@ -3435,8 +3554,8 @@ VOID PhShellOpenKey( _In_ PPH_STRING KeyName ) { - static PH_STRINGREF regeditKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit"); - + static PH_STRINGREF regeditKeyNameSr = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit"); + static PH_STRINGREF regeditFileNameSr = PH_STRINGREF_INIT(L"%SystemRoot%\\regedit.exe"); PPH_STRING lastKey; HANDLE regeditKeyHandle; UNICODE_STRING valueName; @@ -3446,7 +3565,7 @@ VOID PhShellOpenKey( ®editKeyHandle, KEY_WRITE, PH_KEY_CURRENT_USER, - ®editKeyName, + ®editKeyNameSr, 0, 0, NULL @@ -3454,27 +3573,27 @@ VOID PhShellOpenKey( return; RtlInitUnicodeString(&valueName, L"LastKey"); - lastKey = PhExpandKeyName(KeyName, TRUE); - NtSetValueKey(regeditKeyHandle, &valueName, 0, REG_SZ, lastKey->Buffer, (ULONG)lastKey->Length + 2); + lastKey = PhExpandKeyName(KeyName, FALSE); + NtSetValueKey(regeditKeyHandle, &valueName, 0, REG_SZ, lastKey->Buffer, (ULONG)lastKey->Length + sizeof(UNICODE_NULL)); PhDereferenceObject(lastKey); - NtClose(regeditKeyHandle); + NtClose(regeditKeyHandle); // Start regedit. If we aren't elevated, request that regedit be elevated. This is so we can get // the consent dialog in the center of the specified window. - regeditFileName = PhGetKnownLocation(CSIDL_WINDOWS, L"\\regedit.exe"); + regeditFileName = PhExpandEnvironmentStrings(®editFileNameSr); - if (!regeditFileName) - regeditFileName = PhCreateString(L"regedit.exe"); + if (PhIsNullOrEmptyString(regeditFileName)) + PhMoveReference(®editFileName, PhCreateString(L"regedit.exe")); - if (!PhGetOwnTokenAttributes().Elevated) + if (PhGetOwnTokenAttributes().Elevated) { - PhShellExecuteEx(hWnd, regeditFileName->Buffer, L"", SW_NORMAL, PH_SHELL_EXECUTE_ADMIN, 0, NULL); + PhShellExecute(hWnd, regeditFileName->Buffer, L""); } else { - PhShellExecute(hWnd, regeditFileName->Buffer, L""); + PhShellExecuteEx(hWnd, regeditFileName->Buffer, L"", SW_NORMAL, PH_SHELL_EXECUTE_ADMIN, 0, NULL); } PhDereferenceObject(regeditFileName); @@ -3509,8 +3628,8 @@ PPH_STRING PhQueryRegistryString( buffer->Type == REG_MULTI_SZ || buffer->Type == REG_EXPAND_SZ) { - if (buffer->DataLength >= sizeof(WCHAR)) - string = PhCreateStringEx((PWCHAR)buffer->Data, buffer->DataLength - sizeof(WCHAR)); + if (buffer->DataLength >= sizeof(UNICODE_NULL)) + string = PhCreateStringEx((PWCHAR)buffer->Data, buffer->DataLength - sizeof(UNICODE_NULL)); else string = PhReferenceEmptyString(); } @@ -3521,6 +3640,67 @@ PPH_STRING PhQueryRegistryString( return string; } +ULONG PhQueryRegistryUlong( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ) +{ + ULONG ulong = ULONG_MAX; + PH_STRINGREF valueName; + PKEY_VALUE_PARTIAL_INFORMATION buffer; + + if (ValueName) + PhInitializeStringRef(&valueName, ValueName); + else + PhInitializeEmptyStringRef(&valueName); + + if (NT_SUCCESS(PhQueryValueKey(KeyHandle, &valueName, KeyValuePartialInformation, &buffer))) + { + if (buffer->Type == REG_DWORD) + { + if (buffer->DataLength == sizeof(ULONG)) + ulong = *(PULONG)buffer->Data; + } + + PhFree(buffer); + } + + return ulong; +} + +ULONG64 PhQueryRegistryUlong64( + _In_ HANDLE KeyHandle, + _In_opt_ PWSTR ValueName + ) +{ + ULONG64 ulong64 = ULLONG_MAX; + PH_STRINGREF valueName; + PKEY_VALUE_PARTIAL_INFORMATION buffer; + + if (ValueName) + PhInitializeStringRef(&valueName, ValueName); + else + PhInitializeEmptyStringRef(&valueName); + + if (NT_SUCCESS(PhQueryValueKey(KeyHandle, &valueName, KeyValuePartialInformation, &buffer))) + { + if (buffer->Type == REG_DWORD) + { + if (buffer->DataLength == sizeof(ULONG)) + ulong64 = *(PULONG)buffer->Data; + } + else if (buffer->Type == REG_QWORD) + { + if (buffer->DataLength == sizeof(ULONG64)) + ulong64 = *(PULONG64)buffer->Data; + } + + PhFree(buffer); + } + + return ulong64; +} + VOID PhMapFlags1( _Inout_ PULONG Value2, _In_ ULONG Value1, @@ -3627,7 +3807,7 @@ UINT_PTR CALLBACK PhpOpenFileNameHookProc( { PhFree(header->lpOFN->lpstrFile); header->lpOFN->nMaxFile = returnLength + 0x200; // pre-allocate some more - header->lpOFN->lpstrFile = PhAllocate(header->lpOFN->nMaxFile * 2); + header->lpOFN->lpstrFile = PhAllocate(header->lpOFN->nMaxFile * sizeof(WCHAR)); returnLength = CommDlg_OpenSave_GetFilePath( header->hdr.hwndFrom, @@ -3651,17 +3831,15 @@ OPENFILENAME *PhpCreateOpenFileName( { OPENFILENAME *ofn; - ofn = PhAllocate(sizeof(OPENFILENAME)); - memset(ofn, 0, sizeof(OPENFILENAME)); - + ofn = PhAllocateZero(sizeof(OPENFILENAME)); ofn->lStructSize = sizeof(OPENFILENAME); ofn->nMaxFile = 0x400; - ofn->lpstrFile = PhAllocate(ofn->nMaxFile * 2); + ofn->lpstrFile = PhAllocate(ofn->nMaxFile * sizeof(WCHAR)); ofn->lpstrFileTitle = NULL; ofn->Flags = OFN_ENABLEHOOK | OFN_EXPLORER; ofn->lpfnHook = PhpOpenFileNameHookProc; - ofn->lpstrFile[0] = 0; + ofn->lpstrFile[0] = UNICODE_NULL; return ofn; } @@ -3727,31 +3905,21 @@ PVOID PhCreateOpenFileDialog( VOID ) { - OPENFILENAME *ofn; - PVOID ofnFileDialog; + IFileDialog *fileDialog; - if (PHP_USE_IFILEDIALOG) + if (SUCCEEDED(CoCreateInstance( + &CLSID_FileOpenDialog, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IFileDialog, + &fileDialog + ))) { - IFileDialog *fileDialog; - - if (SUCCEEDED(CoCreateInstance( - &CLSID_FileOpenDialog, - NULL, - CLSCTX_INPROC_SERVER, - &IID_IFileDialog, - &fileDialog - ))) - { - // The default options are fine. - return PhpCreateFileDialog(FALSE, NULL, fileDialog); - } + // The default options are fine. + return PhpCreateFileDialog(FALSE, NULL, fileDialog); } - ofn = PhpCreateOpenFileName(); - ofnFileDialog = PhpCreateFileDialog(FALSE, ofn, NULL); - PhSetFileDialogOptions(ofnFileDialog, PH_FILEDIALOG_PATHMUSTEXIST | PH_FILEDIALOG_FILEMUSTEXIST | PH_FILEDIALOG_STRICTFILETYPES); - - return ofnFileDialog; + return NULL; } /** @@ -3764,31 +3932,21 @@ PVOID PhCreateSaveFileDialog( VOID ) { - OPENFILENAME *ofn; - PVOID ofnFileDialog; + IFileDialog *fileDialog; - if (PHP_USE_IFILEDIALOG) + if (SUCCEEDED(CoCreateInstance( + &CLSID_FileSaveDialog, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IFileDialog, + &fileDialog + ))) { - IFileDialog *fileDialog; - - if (SUCCEEDED(CoCreateInstance( - &CLSID_FileSaveDialog, - NULL, - CLSCTX_INPROC_SERVER, - &IID_IFileDialog, - &fileDialog - ))) - { - // The default options are fine. - return PhpCreateFileDialog(TRUE, NULL, fileDialog); - } + // The default options are fine. + return PhpCreateFileDialog(TRUE, NULL, fileDialog); } - ofn = PhpCreateOpenFileName(); - ofnFileDialog = PhpCreateFileDialog(TRUE, ofn, NULL); - PhSetFileDialogOptions(ofnFileDialog, PH_FILEDIALOG_PATHMUSTEXIST | PH_FILEDIALOG_OVERWRITEPROMPT | PH_FILEDIALOG_STRICTFILETYPES); - - return ofnFileDialog; + return NULL; } /** @@ -3824,7 +3982,7 @@ VOID PhFreeFileDialog( * occurred. */ BOOLEAN PhShowFileDialog( - _In_ HWND hWnd, + _In_opt_ HWND hWnd, _In_ PVOID FileDialog ) { @@ -3867,7 +4025,8 @@ static const PH_FLAG_MAPPING PhpFileDialogIfdMappings[] = { PH_FILEDIALOG_OVERWRITEPROMPT, FOS_OVERWRITEPROMPT }, { PH_FILEDIALOG_DEFAULTEXPANDED, FOS_DEFAULTNOMINIMODE }, { PH_FILEDIALOG_STRICTFILETYPES, FOS_STRICTFILETYPES }, - { PH_FILEDIALOG_PICKFOLDERS, FOS_PICKFOLDERS } + { PH_FILEDIALOG_PICKFOLDERS, FOS_PICKFOLDERS }, + { PH_FILEDIALOG_NOPATHVALIDATE, FOS_NOVALIDATE }, }; static const PH_FLAG_MAPPING PhpFileDialogOfnMappings[] = @@ -3877,7 +4036,8 @@ static const PH_FLAG_MAPPING PhpFileDialogOfnMappings[] = { PH_FILEDIALOG_FILEMUSTEXIST, OFN_FILEMUSTEXIST }, { PH_FILEDIALOG_SHOWHIDDEN, OFN_FORCESHOWHIDDEN }, { PH_FILEDIALOG_NODEREFERENCELINKS, OFN_NODEREFERENCELINKS }, - { PH_FILEDIALOG_OVERWRITEPROMPT, OFN_OVERWRITEPROMPT } + { PH_FILEDIALOG_OVERWRITEPROMPT, OFN_OVERWRITEPROMPT }, + { PH_FILEDIALOG_NOPATHVALIDATE, OFN_NOVALIDATE } }; /** @@ -4069,7 +4229,7 @@ VOID PhSetFileDialogFilter( if (ofn->lpstrFilter) PhFree((PVOID)ofn->lpstrFilter); - ofn->lpstrFilter = PhAllocateCopy(filterString->Buffer, filterString->Length + 2); + ofn->lpstrFilter = PhAllocateCopy(filterString->Buffer, filterString->Length + sizeof(UNICODE_NULL)); PhDereferenceObject(filterString); } } @@ -4147,8 +4307,8 @@ VOID PhSetFileDialogFileName( PH_STRINGREF pathNamePart; PH_STRINGREF baseNamePart; - if (PhSplitStringRefAtLastChar(&fileName, '\\', &pathNamePart, &baseNamePart) && - SHParseDisplayName_Import() && SHCreateShellItem_Import()) + if (PhSplitStringRefAtLastChar(&fileName, OBJ_NAME_PATH_SEPARATOR, &pathNamePart, &baseNamePart) && + SHParseDisplayName && SHCreateShellItem) { LPITEMIDLIST item; SFGAOF attributes; @@ -4156,9 +4316,9 @@ VOID PhSetFileDialogFileName( pathName = PhCreateString2(&pathNamePart); - if (SUCCEEDED(SHParseDisplayName_Import()(pathName->Buffer, NULL, &item, 0, &attributes))) + if (SUCCEEDED(SHParseDisplayName(pathName->Buffer, NULL, &item, 0, &attributes))) { - SHCreateShellItem_Import()(NULL, NULL, item, &shellItem); + SHCreateShellItem(NULL, NULL, item, &shellItem); CoTaskMemFree(item); } @@ -4189,8 +4349,8 @@ VOID PhSetFileDialogFileName( PhFree(ofn->lpstrFile); ofn->nMaxFile = (ULONG)max(fileName.Length / sizeof(WCHAR) + 1, 0x400); - ofn->lpstrFile = PhAllocate(ofn->nMaxFile * 2); - memcpy(ofn->lpstrFile, fileName.Buffer, fileName.Length + sizeof(WCHAR)); + ofn->lpstrFile = PhAllocate(ofn->nMaxFile * sizeof(WCHAR)); + memcpy(ofn->lpstrFile, fileName.Buffer, fileName.Length + sizeof(UNICODE_NULL)); } } @@ -4533,7 +4693,7 @@ PPH_STRING PhParseCommandLinePart( BOOLEAN inQuote; BOOLEAN endOfValue; - length = CommandLine->Length / 2; + length = CommandLine->Length / sizeof(WCHAR); i = *Index; // This function follows the rules used by CommandLineToArgvW: @@ -4564,7 +4724,7 @@ PPH_STRING PhParseCommandLinePart( if (numberOfBackslashes != 0) { - PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes); + PhAppendCharStringBuilder2(&stringBuilder, OBJ_NAME_PATH_SEPARATOR, numberOfBackslashes); numberOfBackslashes = 0; } @@ -4575,7 +4735,7 @@ PPH_STRING PhParseCommandLinePart( else { numberOfBackslashes /= 2; - PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes); + PhAppendCharStringBuilder2(&stringBuilder, OBJ_NAME_PATH_SEPARATOR, numberOfBackslashes); numberOfBackslashes = 0; } } @@ -4589,7 +4749,7 @@ PPH_STRING PhParseCommandLinePart( default: if (numberOfBackslashes != 0) { - PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes); + PhAppendCharStringBuilder2(&stringBuilder, OBJ_NAME_PATH_SEPARATOR, numberOfBackslashes); numberOfBackslashes = 0; } @@ -4651,7 +4811,7 @@ BOOLEAN PhParseCommandLine( return TRUE; i = 0; - length = CommandLine->Length / 2; + length = CommandLine->Length / sizeof(WCHAR); while (TRUE) { @@ -4698,7 +4858,7 @@ BOOLEAN PhParseCommandLine( optionNameLength = i - originalIndex; optionName.Buffer = &CommandLine->Buffer[originalIndex]; - optionName.Length = optionNameLength * 2; + optionName.Length = optionNameLength * sizeof(WCHAR); // Take care of any pending optional argument. @@ -4792,8 +4952,8 @@ PPH_STRING PhEscapeCommandLinePart( ULONG numberOfBackslashes; - length = (ULONG)String->Length / 2; - PhInitializeStringBuilder(&stringBuilder, String->Length / 2 * 3); + length = (ULONG)String->Length / sizeof(WCHAR); + PhInitializeStringBuilder(&stringBuilder, String->Length / sizeof(WCHAR) * 3); numberOfBackslashes = 0; // Simply replacing " with \" won't work here. See PhParseCommandLinePart for the quoting rules. @@ -4808,7 +4968,7 @@ PPH_STRING PhEscapeCommandLinePart( case '\"': if (numberOfBackslashes != 0) { - PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes * 2); + PhAppendCharStringBuilder2(&stringBuilder, OBJ_NAME_PATH_SEPARATOR, numberOfBackslashes * 2); numberOfBackslashes = 0; } @@ -4818,7 +4978,7 @@ PPH_STRING PhEscapeCommandLinePart( default: if (numberOfBackslashes != 0) { - PhAppendCharStringBuilder2(&stringBuilder, '\\', numberOfBackslashes); + PhAppendCharStringBuilder2(&stringBuilder, OBJ_NAME_PATH_SEPARATOR, numberOfBackslashes); numberOfBackslashes = 0; } @@ -4831,59 +4991,6 @@ PPH_STRING PhEscapeCommandLinePart( return PhFinalStringBuilderString(&stringBuilder); } -BOOLEAN PhpSearchFilePath( - _In_ PWSTR FileName, - _In_opt_ PWSTR Extension, - _Out_writes_(MAX_PATH) PWSTR Buffer - ) -{ - NTSTATUS status; - ULONG result; - UNICODE_STRING fileName; - OBJECT_ATTRIBUTES objectAttributes; - FILE_BASIC_INFORMATION basicInfo; - - result = SearchPath( - NULL, - FileName, - Extension, - MAX_PATH, - Buffer, - NULL - ); - - if (result == 0 || result >= MAX_PATH) - return FALSE; - - // Make sure this is not a directory. - - if (!NT_SUCCESS(RtlDosPathNameToNtPathName_U_WithStatus( - Buffer, - &fileName, - NULL, - NULL - ))) - return FALSE; - - InitializeObjectAttributes( - &objectAttributes, - &fileName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL - ); - - status = NtQueryAttributesFile(&objectAttributes, &basicInfo); - RtlFreeUnicodeString(&fileName); - - if (!NT_SUCCESS(status)) - return FALSE; - if (basicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) - return FALSE; - - return TRUE; -} - /** * Parses a command line string. If the string does not contain quotation marks around the file name * part, the function determines the file name to use. @@ -4908,7 +5015,7 @@ BOOLEAN PhParseCommandLineFuzzy( PH_STRINGREF temp; PH_STRINGREF currentPart; PH_STRINGREF remainingPart; - WCHAR buffer[MAX_PATH]; + PPH_STRING filePathSr; WCHAR originalChar; commandLine = *CommandLine; @@ -4955,9 +5062,9 @@ BOOLEAN PhParseCommandLineFuzzy( tempCommandLine = PhCreateString2(&commandLine); - if (PhpSearchFilePath(tempCommandLine->Buffer, L".exe", buffer)) + if (filePathSr = PhSearchFilePath(tempCommandLine->Buffer, L".exe")) { - *FullFileName = PhCreateString(buffer); + *FullFileName = filePathSr; } else { @@ -4982,16 +5089,15 @@ BOOLEAN PhParseCommandLineFuzzy( // Note that we do not trim whitespace in each part because filenames can contain trailing // whitespace before the extension (e.g. "Internet .exe"). - temp.Buffer = PhAllocate(commandLine.Length + sizeof(WCHAR)); + temp.Buffer = PhAllocate(commandLine.Length + sizeof(UNICODE_NULL)); memcpy(temp.Buffer, commandLine.Buffer, commandLine.Length); - temp.Buffer[commandLine.Length / sizeof(WCHAR)] = 0; + temp.Buffer[commandLine.Length / sizeof(WCHAR)] = UNICODE_NULL; temp.Length = commandLine.Length; remainingPart = temp; while (remainingPart.Length != 0) { BOOLEAN found; - BOOLEAN result; found = PhSplitStringRefAtChar(&remainingPart, ' ', ¤tPart, &remainingPart); @@ -5001,23 +5107,25 @@ BOOLEAN PhParseCommandLineFuzzy( *(remainingPart.Buffer - 1) = 0; } - result = PhpSearchFilePath(temp.Buffer, L".exe", buffer); + filePathSr = PhSearchFilePath(temp.Buffer, L".exe"); if (found) { *(remainingPart.Buffer - 1) = originalChar; } - if (result) + if (filePathSr) { FileName->Buffer = commandLine.Buffer; - FileName->Length = ((PCHAR)currentPart.Buffer - (PCHAR)temp.Buffer) + currentPart.Length; + FileName->Length = (SIZE_T)PTR_SUB_OFFSET(currentPart.Buffer, temp.Buffer) + currentPart.Length; PhTrimStringRef(&remainingPart, &whitespace, PH_TRIM_START_ONLY); *Arguments = remainingPart; if (FullFileName) - *FullFileName = PhCreateString(buffer); + *FullFileName = filePathSr; + else + PhDereferenceObject(filePathSr); PhFree(temp.Buffer); @@ -5035,3 +5143,1484 @@ BOOLEAN PhParseCommandLineFuzzy( return FALSE; } + +PPH_STRING PhSearchFilePath( + _In_ PWSTR FileName, + _In_opt_ PWSTR Extension + ) +{ + PPH_STRING fullPath; + ULONG bufferSize; + ULONG returnLength; + FILE_BASIC_INFORMATION basicInfo; + + bufferSize = MAX_PATH; + fullPath = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); + + returnLength = SearchPath( + NULL, + FileName, + Extension, + (ULONG)fullPath->Length / sizeof(WCHAR), + fullPath->Buffer, + NULL + ); + + if (returnLength == 0 && returnLength <= bufferSize) + goto CleanupExit; + + if (returnLength > bufferSize) + { + bufferSize = returnLength; + PhDereferenceObject(fullPath); + fullPath = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); + + returnLength = SearchPath( + NULL, + FileName, + Extension, + (ULONG)fullPath->Length / sizeof(WCHAR), + fullPath->Buffer, + NULL + ); + } + + if (returnLength == 0 && returnLength <= bufferSize) + goto CleanupExit; + + PhTrimToNullTerminatorString(fullPath); + + // Make sure this is not a directory. + + if (!NT_SUCCESS(PhQueryAttributesFileWin32(fullPath->Buffer, &basicInfo))) + goto CleanupExit; + if (basicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + goto CleanupExit; + + return fullPath; + +CleanupExit: + PhDereferenceObject(fullPath); + return NULL; +} + +PPH_STRING PhCreateCacheFile( + _In_ PPH_STRING FileName + ) +{ + static PH_STRINGREF cacheDirectorySr = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker\\Cache"); + PPH_STRING cacheDirectory; + PPH_STRING cacheFilePath; + PPH_STRING cacheFullFilePath = NULL; + ULONG indexOfFileName = ULONG_MAX; + WCHAR alphastring[16] = L""; + + cacheDirectory = PhExpandEnvironmentStrings(&cacheDirectorySr); + PhGenerateRandomAlphaString(alphastring, ARRAYSIZE(alphastring)); + + cacheFilePath = PhConcatStrings( + 5, + PhGetStringOrEmpty(cacheDirectory), + L"\\", + alphastring, + L"\\", + PhGetStringOrEmpty(FileName) + ); + + if (cacheFullFilePath = PhGetFullPath(PhGetString(cacheFilePath), &indexOfFileName)) + { + PPH_STRING directoryPath; + + if (indexOfFileName != ULONG_MAX && (directoryPath = PhSubstring(cacheFullFilePath, 0, indexOfFileName))) + { + PhCreateDirectory(directoryPath); + PhDereferenceObject(directoryPath); + } + } + + PhDereferenceObject(cacheFilePath); + PhDereferenceObject(cacheDirectory); + + return cacheFullFilePath; +} + +VOID PhClearCacheDirectory( + VOID + ) +{ + static PH_STRINGREF cacheDirectorySr = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker\\Cache"); + PPH_STRING cacheDirectory; + + if (cacheDirectory = PhExpandEnvironmentStrings(&cacheDirectorySr)) + { + PhDeleteDirectory(cacheDirectory); + PhDereferenceObject(cacheDirectory); + } +} + +VOID PhDeleteCacheFile( + _In_ PPH_STRING FileName + ) +{ + PPH_STRING cacheDirectory; + PPH_STRING cacheFullFilePath; + ULONG indexOfFileName = ULONG_MAX; + + if (PhDoesFileExistsWin32(PhGetString(FileName))) + { + PhDeleteFileWin32(PhGetString(FileName)); + } + + if (cacheFullFilePath = PhGetFullPath(PhGetString(FileName), &indexOfFileName)) + { + if (indexOfFileName != ULONG_MAX && (cacheDirectory = PhSubstring(cacheFullFilePath, 0, indexOfFileName))) + { + PhDeleteDirectory(cacheDirectory); + PhDereferenceObject(cacheDirectory); + } + + PhDereferenceObject(cacheFullFilePath); + } +} + +HANDLE PhGetNamespaceHandle( + VOID + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static UNICODE_STRING namespacePathUs = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\ProcessHacker"); + static HANDLE directory = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + static SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; + OBJECT_ATTRIBUTES objectAttributes; + PSECURITY_DESCRIPTOR securityDescriptor; + ULONG sdAllocationLength; + UCHAR administratorsSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2]; + PSID administratorsSid; + PACL dacl; + + // Create the default namespace DACL. + + administratorsSid = (PSID)administratorsSidBuffer; + RtlInitializeSid(administratorsSid, &ntAuthority, 2); + *RtlSubAuthoritySid(administratorsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID; + *RtlSubAuthoritySid(administratorsSid, 1) = DOMAIN_ALIAS_RID_ADMINS; + + sdAllocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH + + (ULONG)sizeof(ACL) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(&PhSeLocalSid) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(administratorsSid) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(&PhSeInteractiveSid); + + securityDescriptor = PhAllocate(sdAllocationLength); + dacl = (PACL)PTR_ADD_OFFSET(securityDescriptor, SECURITY_DESCRIPTOR_MIN_LENGTH); + + RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION); + RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, DIRECTORY_ALL_ACCESS, &PhSeLocalSid); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, DIRECTORY_ALL_ACCESS, administratorsSid); + RtlAddAccessAllowedAce(dacl, ACL_REVISION, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT, &PhSeInteractiveSid); + RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE); + + InitializeObjectAttributes( + &objectAttributes, + &namespacePathUs, + OBJ_OPENIF, + NULL, + securityDescriptor + ); + + NtCreateDirectoryObject( + &directory, + MAXIMUM_ALLOWED, + &objectAttributes + ); + + PhFree(securityDescriptor); + + PhEndInitOnce(&initOnce); + } + + return directory; +} + +// rev from LdrAccessResource (dmex) +NTSTATUS PhAccessResource( + _In_ PVOID DllBase, + _In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, + _Out_opt_ PVOID *ResourceBuffer, + _Out_opt_ ULONG *ResourceLength + ) +{ + PVOID baseAddress; + + if (LDR_IS_DATAFILE(DllBase)) + baseAddress = (PVOID)((ULONG_PTR)DllBase & ~1); + else if (LDR_IS_IMAGEMAPPING(DllBase)) + baseAddress = (PVOID)((ULONG_PTR)DllBase & ~2); + else + baseAddress = DllBase; + + if (ResourceLength) + { + *ResourceLength = ResourceDataEntry->Size; + } + + if (ResourceBuffer) + { + if (LDR_IS_DATAFILE(DllBase)) + { + NTSTATUS status; + + status = PhLoaderEntryImageRvaToVa( + baseAddress, + ResourceDataEntry->OffsetToData, + ResourceBuffer + ); + + return status; + } + else + { + *ResourceBuffer = PTR_ADD_OFFSET( + baseAddress, + ResourceDataEntry->OffsetToData + ); + + return STATUS_SUCCESS; + } + } + + return STATUS_SUCCESS; +} + +BOOLEAN PhLoadResource( + _In_ PVOID DllBase, + _In_ PCWSTR Name, + _In_ PCWSTR Type, + _Out_opt_ ULONG *ResourceLength, + _Out_ PVOID *ResourceBuffer + ) +{ + LDR_RESOURCE_INFO resourceInfo; + PIMAGE_RESOURCE_DATA_ENTRY resourceData; + ULONG resourceLength; + PVOID resourceBuffer; + + resourceInfo.Type = (ULONG_PTR)Type; + resourceInfo.Name = (ULONG_PTR)Name; + resourceInfo.Language = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); + + if (!NT_SUCCESS(LdrFindResource_U(DllBase, &resourceInfo, RESOURCE_DATA_LEVEL, &resourceData))) + return FALSE; + + if (!NT_SUCCESS(LdrAccessResource(DllBase, resourceData, &resourceBuffer, &resourceLength))) + return FALSE; + + if (ResourceLength) + *ResourceLength = resourceLength; + *ResourceBuffer = PhAllocateCopy(resourceBuffer, resourceLength); + + return TRUE; +} + +PPH_STRING PhLoadString( + _In_ PVOID DllBase, + _In_ ULONG ResourceId + ) +{ + PPH_STRING string = NULL; + ULONG resourceLength; + PVOID resourceBuffer; + ULONG stringIndex; + PWSTR stringBuffer; + ULONG i; + + if (!PhLoadResource( + DllBase, + MAKEINTRESOURCE((LOWORD(ResourceId) >> 4) + 1), + RT_STRING, + &resourceLength, + &resourceBuffer + )) + { + return NULL; + } + + stringBuffer = resourceBuffer; + stringIndex = ResourceId & 0x000F; + + for (i = 0; i < stringIndex; i++) // dmex: Copied from ReactOS. + { + stringBuffer += *stringBuffer + 1; + } + + i = min(resourceLength - 1, *stringBuffer); + + if (i > 0) + { + string = PhCreateStringEx(stringBuffer + 1, i * sizeof(WCHAR)); + } + + PhFree(resourceBuffer); + return string; +} + +// rev from LoadMenuW +HMENU PhLoadMenu( + _In_ PVOID DllBase, + _In_ PWSTR MenuName + ) +{ + HMENU menuHandle = NULL; + LPMENUTEMPLATE templateBuffer; + + if (PhLoadResource( + DllBase, + MenuName, + RT_MENU, + NULL, + &templateBuffer + )) + { + menuHandle = LoadMenuIndirect(templateBuffer); + + PhFree(templateBuffer); + } + + return menuHandle; +} + +// rev from SHLoadIndirectString +/** + * Extracts a specified text resource when given that resource in the form of an indirect string (a string that begins with the '@' symbol). + * + * \param SourceString The indirect string from which the resource will be retrieved. + */ +PPH_STRING PhLoadIndirectString( + _In_ PWSTR SourceString + ) +{ + PPH_STRING indirectString = NULL; + + if (SourceString[0] == L'@') + { + PPH_STRING libraryString; + PVOID libraryModule; + PH_STRINGREF sourceRef; + PH_STRINGREF dllNameRef; + PH_STRINGREF dllIndexRef; + ULONG64 index64; + LONG index; + + PhInitializeStringRefLongHint(&sourceRef, SourceString); + PhSkipStringRef(&sourceRef, sizeof(WCHAR)); // Skip the @ character. + + if (!PhSplitStringRefAtChar(&sourceRef, L',', &dllNameRef, &dllIndexRef)) + return NULL; + + if (!PhStringToInteger64(&dllIndexRef, 10, &index64)) + { + // HACK: Services.exe includes custom logic for indirect Service description strings by reading descriptions from inf files, + // these strings use the following format: "@FileName.inf,%SectionKeyName%;DefaultString". + // Return the last token of the service string instead of locating and parsing the inf file with GetPrivateProfileString. + if (PhSplitStringRefAtChar(&sourceRef, L';', &dllNameRef, &dllIndexRef)) // dllIndexRef.Buffer[0] == L'%' + return PhCreateString2(&dllIndexRef); + else + return NULL; + } + + libraryString = PhCreateString2(&dllNameRef); + index = (LONG)index64; + + if (libraryString->Buffer[0] == L'%') + { + PPH_STRING expandedString; + + if (expandedString = PhExpandEnvironmentStrings(&libraryString->sr)) + PhMoveReference(&libraryString, expandedString); + } + + if (libraryModule = LoadLibraryEx(libraryString->Buffer, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + indirectString = PhLoadString(libraryModule, -index); + FreeLibrary(libraryModule); + } + + PhDereferenceObject(libraryString); + } + + return indirectString; +} + +// rev from ExtractIconExW +BOOLEAN PhExtractIcon( + _In_ PWSTR FileName, + _Out_opt_ HICON *IconLarge, + _Out_opt_ HICON *IconSmall + ) +{ + return PhExtractIconEx(FileName, 0, IconLarge, IconSmall); +} + +BOOLEAN PhExtractIconEx( + _In_ PWSTR FileName, + _In_ INT IconIndex, + _Out_opt_ HICON *IconLarge, + _Out_opt_ HICON *IconSmall + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static UINT (WINAPI *PrivateExtractIconExW)( + _In_ PCWSTR FileName, + _In_ INT IconIndex, + _Out_opt_ HICON* IconLarge, + _Out_opt_ HICON* IconSmall, + _In_ UINT IconCount + ) = NULL; + + if (PhBeginInitOnce(&initOnce)) + { + PrivateExtractIconExW = PhGetDllProcedureAddress(L"user32.dll", "PrivateExtractIconExW", 0); + PhEndInitOnce(&initOnce); + } + + if (!PrivateExtractIconExW) + return FALSE; + + return PrivateExtractIconExW(FileName, IconIndex, IconLarge, IconSmall, 1) > 0; // -1 on error or the number of icons. +} + +/** + * Locates a loader entry in the current process. + * + * \param DllBase The base address of the DLL. Specify NULL if this is not a search criteria. + * \param FullDllName The full name of the DLL. Specify NULL if this is not a search criteria. + * \param BaseDllName The base name of the DLL. Specify NULL if this is not a search criteria. + * + * \remarks This function must be called with the loader lock acquired. The first entry matching all + * of the specified values is returned. + */ +PLDR_DATA_TABLE_ENTRY PhFindLoaderEntry( + _In_opt_ PVOID DllBase, + _In_opt_ PPH_STRINGREF FullDllName, + _In_opt_ PPH_STRINGREF BaseDllName + ) +{ + PLDR_DATA_TABLE_ENTRY result = NULL; + PLDR_DATA_TABLE_ENTRY entry; + PH_STRINGREF fullDllName; + PH_STRINGREF baseDllName; + PLIST_ENTRY listHead; + PLIST_ENTRY listEntry; + + listHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + listEntry = listHead->Flink; + + while (listEntry != listHead) + { + entry = CONTAINING_RECORD(listEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + PhUnicodeStringToStringRef(&entry->FullDllName, &fullDllName); + PhUnicodeStringToStringRef(&entry->BaseDllName, &baseDllName); + + if ( + (!DllBase || entry->DllBase == DllBase) && + (!FullDllName || PhEqualStringRef(&fullDllName, FullDllName, TRUE)) && + (!BaseDllName || PhEqualStringRef(&baseDllName, BaseDllName, TRUE)) + ) + { + result = entry; + break; + } + + listEntry = listEntry->Flink; + } + + return result; +} + +/** + * Retrieves the file name of a DLL loaded by the current process. + * + * \param DllBase The base address of the DLL. + * \param IndexOfFileName A variable which receives the index of the base name of the DLL in the + * returned string. + * + * \return The file name of the DLL, or NULL if the DLL could not be found. + */ +PPH_STRING PhGetDllFileName( + _In_ PVOID DllBase, + _Out_opt_ PULONG IndexOfFileName + ) +{ + PLDR_DATA_TABLE_ENTRY entry; + PPH_STRING fileName; + PPH_STRING newFileName; + ULONG_PTR indexOfFileName; + + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + + entry = PhFindLoaderEntry(DllBase, NULL, NULL); + + if (entry) + fileName = PhCreateStringFromUnicodeString(&entry->FullDllName); + else + fileName = NULL; + + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + + if (!fileName) + return NULL; + + newFileName = PhGetFileName(fileName); + PhMoveReference(&fileName, newFileName); + + if (newFileName = PhGetFullPath(fileName->Buffer, NULL)) // HACK PhGetApplicationDirectory (dmex) + { + PhMoveReference(&fileName, newFileName); + } + + if (IndexOfFileName) + { + indexOfFileName = PhFindLastCharInString(fileName, 0, OBJ_NAME_PATH_SEPARATOR); + + if (indexOfFileName != -1) + indexOfFileName++; + else + indexOfFileName = 0; + + *IndexOfFileName = (ULONG)indexOfFileName; + } + + return fileName; +} + +PVOID PhGetLoaderEntryDllBase( + _In_ PWSTR BaseDllName + ) +{ + PH_STRINGREF entryNameSr; + PLDR_DATA_TABLE_ENTRY ldrEntry; + + PhInitializeStringRefLongHint(&entryNameSr, BaseDllName); + + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + ldrEntry = PhFindLoaderEntry(NULL, NULL, &entryNameSr); + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + + if (ldrEntry) + return ldrEntry->DllBase; + else + return NULL; +} + +PVOID PhGetDllBaseProcedureAddress( + _In_ PVOID DllBase, + _In_opt_ PSTR ProcedureName, + _In_opt_ USHORT ProcedureNumber + ) +{ + PIMAGE_NT_HEADERS imageNtHeader; + PIMAGE_DATA_DIRECTORY dataDirectory; + PIMAGE_EXPORT_DIRECTORY exportDirectory; + + if (!NT_SUCCESS(PhGetLoaderEntryImageNtHeaders(DllBase, &imageNtHeader))) + return NULL; + + if (!NT_SUCCESS(PhGetLoaderEntryImageDirectory( + DllBase, + imageNtHeader, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &dataDirectory, + &exportDirectory, + NULL + ))) + return NULL; + + return PhGetLoaderEntryImageExportFunction( + DllBase, + imageNtHeader, + dataDirectory, + exportDirectory, + ProcedureName, + ProcedureNumber + ); +} + +PVOID PhGetDllProcedureAddress( + _In_ PWSTR DllName, + _In_opt_ PSTR ProcedureName, + _In_opt_ USHORT ProcedureNumber + ) +{ + PVOID baseAddress; + + if (!(baseAddress = PhGetLoaderEntryDllBase(DllName))) + return NULL; + + return PhGetDllBaseProcedureAddress( + baseAddress, + ProcedureName, + ProcedureNumber + ); +} + +NTSTATUS PhGetLoaderEntryImageNtHeaders( + _In_ PVOID BaseAddress, + _Out_ PIMAGE_NT_HEADERS *ImageNtHeaders + ) +{ + PIMAGE_DOS_HEADER dosHeader; + PIMAGE_NT_HEADERS ntHeader; + ULONG ntHeadersOffset; + + dosHeader = PTR_ADD_OFFSET(BaseAddress, 0); + + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT; + + ntHeadersOffset = (ULONG)dosHeader->e_lfanew; + + if (ntHeadersOffset == 0 || ntHeadersOffset >= 0x10000000) + return STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT; + + ntHeader = PTR_ADD_OFFSET(BaseAddress, ntHeadersOffset); + + if (ntHeader->Signature != IMAGE_NT_SIGNATURE) + return STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT; + + *ImageNtHeaders = ntHeader; + return STATUS_SUCCESS; +} + +NTSTATUS PhGetLoaderEntryImageEntryPoint( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _Out_ PLDR_INIT_ROUTINE *ImageEntryPoint + ) +{ + if (ImageNtHeader->OptionalHeader.AddressOfEntryPoint == 0) + return STATUS_ENTRYPOINT_NOT_FOUND; + + *ImageEntryPoint = PTR_ADD_OFFSET(BaseAddress, ImageNtHeader->OptionalHeader.AddressOfEntryPoint); + return STATUS_SUCCESS; +} + +NTSTATUS PhGetLoaderEntryImageDirectory( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ ULONG ImageDirectoryIndex, + _Out_ PIMAGE_DATA_DIRECTORY *ImageDataDirectoryEntry, + _Out_ PVOID *ImageDirectoryEntry, + _Out_opt_ SIZE_T *ImageDirectoryLength + ) +{ + PIMAGE_DATA_DIRECTORY directory; + + directory = &ImageNtHeader->OptionalHeader.DataDirectory[ImageDirectoryIndex]; + + if (directory->VirtualAddress == 0 || directory->Size == 0) + return STATUS_INVALID_FILE_FOR_SECTION; + + *ImageDataDirectoryEntry = directory; + *ImageDirectoryEntry = PTR_ADD_OFFSET(BaseAddress, directory->VirtualAddress); + if (ImageDirectoryLength) *ImageDirectoryLength = directory->Size; + + return STATUS_SUCCESS; +} + +NTSTATUS PhGetLoaderEntryImageVaToSection( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ PVOID ImageDirectoryAddress, + _Out_ PVOID *ImageSectionAddress, + _Out_ SIZE_T *ImageSectionLength + ) +{ + SIZE_T directorySectionLength = 0; + PIMAGE_SECTION_HEADER sectionHeader; + PVOID directorySectionAddress = NULL; + ULONG i; + + for (i = 0; i < ImageNtHeader->FileHeader.NumberOfSections; i++) + { + sectionHeader = PTR_ADD_OFFSET(IMAGE_FIRST_SECTION(ImageNtHeader), sizeof(IMAGE_SECTION_HEADER) * i); + + if ( + ((ULONG_PTR)ImageDirectoryAddress >= (ULONG_PTR)PTR_ADD_OFFSET(BaseAddress, sectionHeader->VirtualAddress)) && + ((ULONG_PTR)ImageDirectoryAddress < (ULONG_PTR)PTR_ADD_OFFSET(PTR_ADD_OFFSET(BaseAddress, sectionHeader->VirtualAddress), sectionHeader->SizeOfRawData)) + ) + { + directorySectionLength = sectionHeader->Misc.VirtualSize; + directorySectionAddress = PTR_ADD_OFFSET(BaseAddress, sectionHeader->VirtualAddress); + break; + } + } + + if (directorySectionAddress && directorySectionLength) + { + *ImageSectionAddress = directorySectionAddress; + *ImageSectionLength = directorySectionLength; + return STATUS_SUCCESS; + } + + return STATUS_SECTION_NOT_IMAGE; +} + +NTSTATUS PhLoaderEntryImageRvaToSection( + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ ULONG Rva, + _Out_ PIMAGE_SECTION_HEADER *ImageSection, + _Out_ SIZE_T *ImageSectionLength + ) +{ + SIZE_T directorySectionLength = 0; + PIMAGE_SECTION_HEADER sectionHeader; + PIMAGE_SECTION_HEADER directorySectionHeader = NULL; + ULONG i; + + for (i = 0; i < ImageNtHeader->FileHeader.NumberOfSections; i++) + { + sectionHeader = PTR_ADD_OFFSET(IMAGE_FIRST_SECTION(ImageNtHeader), sizeof(IMAGE_SECTION_HEADER) * i); + + if ( + ((ULONG_PTR)Rva >= (ULONG_PTR)sectionHeader->VirtualAddress) && + ((ULONG_PTR)Rva < (ULONG_PTR)PTR_ADD_OFFSET(sectionHeader->VirtualAddress, sectionHeader->SizeOfRawData)) + ) + { + directorySectionLength = sectionHeader->Misc.VirtualSize; + directorySectionHeader = sectionHeader; + break; + } + } + + if (directorySectionHeader && directorySectionLength) + { + *ImageSection = directorySectionHeader; + *ImageSectionLength = directorySectionLength; + return STATUS_SUCCESS; + } + + return STATUS_SECTION_NOT_IMAGE; +} + +NTSTATUS PhLoaderEntryImageRvaToVa( + _In_ PVOID BaseAddress, + _In_ ULONG Rva, + _Out_ PVOID *Va + ) +{ + NTSTATUS status; + SIZE_T imageSectionSize; + PIMAGE_SECTION_HEADER imageSection; + PIMAGE_NT_HEADERS imageNtHeader; + + status = PhGetLoaderEntryImageNtHeaders( + BaseAddress, + &imageNtHeader + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PhLoaderEntryImageRvaToSection( + imageNtHeader, + Rva, + &imageSection, + &imageSectionSize + ); + + if (!NT_SUCCESS(status)) + return status; + + *Va = PTR_ADD_OFFSET(BaseAddress, PTR_ADD_OFFSET( + PTR_SUB_OFFSET(Rva, imageSection->VirtualAddress), + imageSection->PointerToRawData + )); + + return STATUS_SUCCESS; +} + +PVOID PhGetLoaderEntryImageExportFunction( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader, + _In_ PIMAGE_DATA_DIRECTORY DataDirectory, + _In_ PIMAGE_EXPORT_DIRECTORY ExportDirectory, + _In_opt_ PSTR ExportName, + _In_opt_ USHORT ExportOrdinal + ) +{ + PVOID exportAddress = NULL; + PULONG exportAddressTable; + PULONG exportNameTable; + PUSHORT exportOrdinalTable; + + exportAddressTable = PTR_ADD_OFFSET(BaseAddress, ExportDirectory->AddressOfFunctions); + exportNameTable = PTR_ADD_OFFSET(BaseAddress, ExportDirectory->AddressOfNames); + exportOrdinalTable = PTR_ADD_OFFSET(BaseAddress, ExportDirectory->AddressOfNameOrdinals); + + if (ExportOrdinal) + { + if (ExportOrdinal > ExportDirectory->Base + ExportDirectory->NumberOfFunctions) + return NULL; + + exportAddress = PTR_ADD_OFFSET(BaseAddress, exportAddressTable[ExportOrdinal - ExportDirectory->Base]); + } + else if (ExportName) + { + for (ULONG i = 0; i < ExportDirectory->NumberOfNames; i++) + { + if (PhEqualBytesZ(ExportName, PTR_ADD_OFFSET(BaseAddress, exportNameTable[i]), FALSE)) + { + exportAddress = PTR_ADD_OFFSET(BaseAddress, exportAddressTable[exportOrdinalTable[i]]); + break; + } + } + } + + if (!exportAddress) + return NULL; + + if ( + ((ULONG_PTR)exportAddress >= (ULONG_PTR)ExportDirectory) && + ((ULONG_PTR)exportAddress < (ULONG_PTR)PTR_ADD_OFFSET(ExportDirectory, DataDirectory->Size)) + ) + { + PPH_STRING dllForwarderString; + PH_STRINGREF dllNameRef; + PH_STRINGREF dllProcedureRef; + + // This is a forwarder RVA. + + dllForwarderString = PhZeroExtendToUtf16((PSTR)exportAddress); + + if (PhSplitStringRefAtChar(&dllForwarderString->sr, L'.', &dllNameRef, &dllProcedureRef)) + { + PPH_STRING libraryNameString; + PPH_BYTES libraryFunctionString; + PVOID libraryModule; + + libraryNameString = PhCreateStringEx(dllNameRef.Buffer, dllNameRef.Length); + libraryFunctionString = PhConvertUtf16ToUtf8Ex(dllProcedureRef.Buffer, dllProcedureRef.Length); + + if (libraryModule = LoadLibrary(libraryNameString->Buffer)) + { + if (libraryFunctionString->Buffer[0] == L'#') // This is a forwarder RVA with an ordinal import. + { + ULONG64 importOrdinal; + + PhSkipStringRef(&dllProcedureRef, sizeof(WCHAR)); + + if (PhStringToInteger64(&dllProcedureRef, 10, &importOrdinal)) + exportAddress = PhGetDllBaseProcedureAddress(libraryModule, NULL, (USHORT)importOrdinal); + else + exportAddress = PhGetDllBaseProcedureAddress(libraryModule, libraryFunctionString->Buffer, 0); + } + else + { + exportAddress = PhGetDllBaseProcedureAddress(libraryModule, libraryFunctionString->Buffer, 0); + } + } + + PhDereferenceObject(libraryFunctionString); + PhDereferenceObject(libraryNameString); + } + + PhDereferenceObject(dllForwarderString); + } + + return exportAddress; +} + +static NTSTATUS PhpFixupLoaderEntryImageImports( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeader + ) +{ + NTSTATUS status; + SIZE_T importDirectorySize; + ULONG importDirectoryProtect; + PIMAGE_DATA_DIRECTORY dataDirectory; + PIMAGE_IMPORT_DESCRIPTOR importDirectory; + PVOID importDirectorySectionAddress; + + status = PhGetLoaderEntryImageDirectory( + BaseAddress, + ImageNtHeader, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &dataDirectory, + &importDirectory, + NULL + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetLoaderEntryImageVaToSection( + BaseAddress, + ImageNtHeader, + importDirectory, + &importDirectorySectionAddress, + &importDirectorySize + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtProtectVirtualMemory( + NtCurrentProcess(), + &importDirectorySectionAddress, + &importDirectorySize, + PAGE_READWRITE, + &importDirectoryProtect + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + for (importDirectory = importDirectory; importDirectory->Name; importDirectory++) + { + PSTR importName; + PIMAGE_THUNK_DATA importThunk; + PIMAGE_THUNK_DATA originalThunk; + PVOID importBaseAddress; + + importName = PTR_ADD_OFFSET(BaseAddress, importDirectory->Name); + importThunk = PTR_ADD_OFFSET(BaseAddress, importDirectory->FirstThunk); + originalThunk = PTR_ADD_OFFSET(BaseAddress, importDirectory->OriginalFirstThunk ? importDirectory->OriginalFirstThunk : importDirectory->FirstThunk); + + if (PhEqualBytesZ(importName, "ProcessHacker.exe", FALSE)) + { + importBaseAddress = PhInstanceHandle; + status = STATUS_SUCCESS; + } + else + { + PPH_STRING importNameSr; + + importNameSr = PhZeroExtendToUtf16(importName); + + if (!(importBaseAddress = PhGetLoaderEntryDllBase(importNameSr->Buffer))) + { + if (importBaseAddress = LoadLibrary(importNameSr->Buffer)) + status = STATUS_SUCCESS; + else + status = PhGetLastWin32ErrorAsNtStatus(); + } + + PhDereferenceObject(importNameSr); + } + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + for ( + originalThunk = originalThunk, importThunk = importThunk; + originalThunk->u1.AddressOfData; + originalThunk++, importThunk++ + ) + { + if (IMAGE_SNAP_BY_ORDINAL(originalThunk->u1.Ordinal)) + { + USHORT procedureOrdinal; + PVOID procedureAddress; + + procedureOrdinal = IMAGE_ORDINAL(originalThunk->u1.Ordinal); + procedureAddress = PhGetDllBaseProcedureAddress(importBaseAddress, NULL, procedureOrdinal); + + if (!procedureAddress) + { +#ifdef DEBUG + PPH_STRING fileName; + + if (NT_SUCCESS(PhGetProcessMappedFileName(NtCurrentProcess(), BaseAddress, &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); + PhMoveReference(&fileName, PhGetBaseName(fileName)); + + PhShowError( + NULL, + L"Unable to load plugin.\r\nName: %s\r\nOrdinal: %u\r\nModule: %hs", + PhGetStringOrEmpty(fileName), + procedureOrdinal, + importName + ); + + PhDereferenceObject(fileName); + } +#endif + status = STATUS_ORDINAL_NOT_FOUND; + goto CleanupExit; + } + + importThunk->u1.Function = (ULONG_PTR)procedureAddress; + } + else + { + PIMAGE_IMPORT_BY_NAME importByName; + PVOID procedureAddress; + + importByName = PTR_ADD_OFFSET(BaseAddress, originalThunk->u1.AddressOfData); + procedureAddress = PhGetDllBaseProcedureAddress(importBaseAddress, importByName->Name, 0); + + if (!procedureAddress) + { +#ifdef DEBUG + PPH_STRING fileName; + + if (NT_SUCCESS(PhGetProcessMappedFileName(NtCurrentProcess(), BaseAddress, &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); + PhMoveReference(&fileName, PhGetBaseName(fileName)); + + PhShowError( + NULL, + L"Unable to load plugin.\r\nName: %s\r\nFunction: %hs\r\nModule: %hs", + PhGetStringOrEmpty(fileName), + importByName->Name, + importName + ); + + PhDereferenceObject(fileName); + } +#endif + status = STATUS_PROCEDURE_NOT_FOUND; + goto CleanupExit; + } + + importThunk->u1.Function = (ULONG_PTR)procedureAddress; + } + } + } + + status = NtProtectVirtualMemory( + NtCurrentProcess(), + &importDirectorySectionAddress, + &importDirectorySize, + importDirectoryProtect, + &importDirectoryProtect + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + +CleanupExit: + return status; +} + +static NTSTATUS PhpFixupLoaderEntryImageDelayImports( + _In_ PVOID BaseAddress, + _In_ PIMAGE_NT_HEADERS ImageNtHeaders, + _In_ PSTR ImportDllName + ) +{ + NTSTATUS status; + SIZE_T importDirectorySectionSize; + ULONG importDirectoryProtect; + PIMAGE_DATA_DIRECTORY dataDirectory; + PIMAGE_DELAYLOAD_DESCRIPTOR delayImportDirectory; + PVOID importDirectorySectionAddress; + + status = PhGetLoaderEntryImageDirectory( + BaseAddress, + ImageNtHeaders, + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, + &dataDirectory, + &delayImportDirectory, + NULL + ); + + if (!NT_SUCCESS(status)) + { + if (status == STATUS_INVALID_FILE_FOR_SECTION) + status = STATUS_SUCCESS; + + goto CleanupExit; + } + + status = PhGetLoaderEntryImageVaToSection( + BaseAddress, + ImageNtHeaders, + PTR_ADD_OFFSET(BaseAddress, delayImportDirectory->ImportAddressTableRVA), + &importDirectorySectionAddress, + &importDirectorySectionSize + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = NtProtectVirtualMemory( + NtCurrentProcess(), + &importDirectorySectionAddress, + &importDirectorySectionSize, + PAGE_READWRITE, + &importDirectoryProtect + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + for (delayImportDirectory = delayImportDirectory; delayImportDirectory->DllNameRVA; delayImportDirectory++) + { + PSTR importName; + PVOID* importHandle; + PIMAGE_THUNK_DATA importThunk; + PIMAGE_THUNK_DATA originalThunk; + PVOID importBaseAddress; + BOOLEAN importNeedsFree = FALSE; + + importName = PTR_ADD_OFFSET(BaseAddress, delayImportDirectory->DllNameRVA); + importHandle = PTR_ADD_OFFSET(BaseAddress, delayImportDirectory->ModuleHandleRVA); + importThunk = PTR_ADD_OFFSET(BaseAddress, delayImportDirectory->ImportAddressTableRVA); + originalThunk = PTR_ADD_OFFSET(BaseAddress, delayImportDirectory->ImportNameTableRVA); + + if (PhEqualBytesZ(importName, ImportDllName, TRUE)) + { + if (PhEqualBytesZ(importName, "ProcessHacker.exe", FALSE)) + { + importBaseAddress = PhInstanceHandle; + status = STATUS_SUCCESS; + } + else if (*importHandle) + { + importBaseAddress = *importHandle; + status = STATUS_SUCCESS; + } + else + { + PPH_STRING importNameSr; + + importNameSr = PhZeroExtendToUtf16(importName); + + if (!(importBaseAddress = PhGetLoaderEntryDllBase(importNameSr->Buffer))) + { + if (importBaseAddress = LoadLibrary(importNameSr->Buffer)) + { + importNeedsFree = TRUE; + status = STATUS_SUCCESS; + } + else + { + status = PhGetLastWin32ErrorAsNtStatus(); + } + } + + PhDereferenceObject(importNameSr); + } + + if (!NT_SUCCESS(status)) + break; + + for ( + originalThunk = originalThunk, importThunk = importThunk; + originalThunk->u1.AddressOfData; + originalThunk++, importThunk++ + ) + { + if (IMAGE_SNAP_BY_ORDINAL(originalThunk->u1.Ordinal)) + { + USHORT procedureOrdinal; + PVOID procedureAddress; + + procedureOrdinal = IMAGE_ORDINAL(originalThunk->u1.Ordinal); + procedureAddress = PhGetDllBaseProcedureAddress(importBaseAddress, NULL, procedureOrdinal); + + importThunk->u1.Function = (ULONG_PTR)procedureAddress; + } + else + { + PIMAGE_IMPORT_BY_NAME importByName; + PVOID procedureAddress; + + importByName = PTR_ADD_OFFSET(BaseAddress, originalThunk->u1.AddressOfData); + procedureAddress = PhGetDllBaseProcedureAddress(importBaseAddress, importByName->Name, 0); + + importThunk->u1.Function = (ULONG_PTR)procedureAddress; + } + } + + if ((InterlockedExchangePointer(importHandle, importBaseAddress) == importBaseAddress) && importNeedsFree) + { + FreeLibrary(importBaseAddress); // A different thread has already updated the cache. + } + } + } + + status = NtProtectVirtualMemory( + NtCurrentProcess(), + &importDirectorySectionAddress, + &importDirectorySectionSize, + importDirectoryProtect, + &importDirectoryProtect + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + +CleanupExit: + return status; +} + +NTSTATUS PhLoaderEntryLoadAllImportsForDll( + _In_ PVOID BaseAddress, + _In_ PSTR ImportDllName + ) +{ + NTSTATUS status; + PIMAGE_NT_HEADERS imageNtHeaders; + + status = PhGetLoaderEntryImageNtHeaders( + BaseAddress, + &imageNtHeaders + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PhpFixupLoaderEntryImageDelayImports( + BaseAddress, + imageNtHeaders, + ImportDllName + ); + + return status; +} + +NTSTATUS PhLoadAllImportsForDll( + _In_ PWSTR TargetDllName, + _In_ PSTR ImportDllName + ) +{ + PVOID imageBaseAddress; + + if (!(imageBaseAddress = PhGetLoaderEntryDllBase(TargetDllName))) + return STATUS_INVALID_PARAMETER; + + return PhLoaderEntryLoadAllImportsForDll( + imageBaseAddress, + ImportDllName + ); +} + +// dmex: This function and the other LoaderEntryImage functions don't belong in this file +// and should be moved into mapimg.c at some stage. +NTSTATUS PhLoadPluginImage( + _In_ PPH_STRING FileName, + _Out_opt_ PVOID *BaseAddress + ) +{ + NTSTATUS status; + ULONG imageType; + PVOID imageBaseAddress; + PIMAGE_NT_HEADERS imageHeaders; + PLDR_INIT_ROUTINE imageEntryRoutine; + UNICODE_STRING imageFileNameUs; + + imageType = IMAGE_FILE_EXECUTABLE_IMAGE; + PhStringRefToUnicodeString(&FileName->sr, &imageFileNameUs); + + status = LdrLoadDll( + NULL, + &imageType, + &imageFileNameUs, + &imageBaseAddress + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetLoaderEntryImageNtHeaders( + imageBaseAddress, + &imageHeaders + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhpFixupLoaderEntryImageImports( + imageBaseAddress, + imageHeaders + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + //status = PhLoaderEntryLoadAllImportsForDll(imageBaseAddress, "ProcessHacker.exe"); + status = PhpFixupLoaderEntryImageDelayImports( + imageBaseAddress, + imageHeaders, + "ProcessHacker.exe" + ); + if (!NT_SUCCESS(status)) + goto CleanupExit; + + status = PhGetLoaderEntryImageEntryPoint( + imageBaseAddress, + imageHeaders, + &imageEntryRoutine + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + if (!imageEntryRoutine(imageBaseAddress, DLL_PROCESS_ATTACH, NULL)) + status = STATUS_DLL_INIT_FAILED; + +CleanupExit: + + if (NT_SUCCESS(status)) + { + if (BaseAddress) + *BaseAddress = imageBaseAddress; + } + else + { + LdrUnloadDll(imageBaseAddress); + } + + return status; +} + +PPH_STRING PhGetExportNameFromOrdinal( + _In_ PVOID DllBase, + _In_opt_ USHORT ProcedureNumber + ) +{ + PIMAGE_NT_HEADERS imageNtHeader; + PIMAGE_DATA_DIRECTORY dataDirectory; + PIMAGE_EXPORT_DIRECTORY exportDirectory; + PULONG exportAddressTable; + PULONG exportNameTable; + PUSHORT exportOrdinalTable; + ULONG i; + + if (!NT_SUCCESS(PhGetLoaderEntryImageNtHeaders(DllBase, &imageNtHeader))) + return NULL; + + if (!NT_SUCCESS(PhGetLoaderEntryImageDirectory( + DllBase, + imageNtHeader, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &dataDirectory, + &exportDirectory, + NULL + ))) + return NULL; + + exportAddressTable = PTR_ADD_OFFSET(DllBase, exportDirectory->AddressOfFunctions); + exportNameTable = PTR_ADD_OFFSET(DllBase, exportDirectory->AddressOfNames); + exportOrdinalTable = PTR_ADD_OFFSET(DllBase, exportDirectory->AddressOfNameOrdinals); + + for (i = 0; i < exportDirectory->NumberOfNames; i++) + { + if ((exportOrdinalTable[i] + exportDirectory->Base) == ProcedureNumber) + { + PVOID baseAddress = PTR_ADD_OFFSET(DllBase, exportAddressTable[exportOrdinalTable[i]]); + + if ( + ((ULONG_PTR)baseAddress >= (ULONG_PTR)exportDirectory) && + ((ULONG_PTR)baseAddress < (ULONG_PTR)PTR_ADD_OFFSET(exportDirectory, dataDirectory->Size)) + ) + { + // This is a forwarder RVA. + return PhZeroExtendToUtf16((PSTR)baseAddress); + } + else + { + return PhZeroExtendToUtf16(PTR_ADD_OFFSET(DllBase, exportNameTable[i])); + } + } + } + + return NULL; +} + +PPH_STRING PhGetFileText( + _In_ HANDLE FileHandle + ) +{ + PPH_STRING string = NULL; + PSTR data; + ULONG allocatedLength; + ULONG dataLength; + ULONG returnLength; + IO_STATUS_BLOCK isb; + BYTE buffer[PAGE_SIZE]; + + allocatedLength = sizeof(buffer); + data = PhAllocate(allocatedLength); + dataLength = 0; + + while (NT_SUCCESS(NtReadFile( + FileHandle, + NULL, + NULL, + NULL, + &isb, + buffer, + PAGE_SIZE, + NULL, + NULL + ))) + { + returnLength = (ULONG)isb.Information; + + if (returnLength == 0) + break; + + if (allocatedLength < dataLength + returnLength) + { + allocatedLength *= 2; + data = PhReAllocate(data, allocatedLength); + } + + memcpy(data + dataLength, buffer, returnLength); + + dataLength += returnLength; + } + + if (allocatedLength < dataLength + sizeof(ANSI_NULL)) + { + allocatedLength++; + data = PhReAllocate(data, allocatedLength); + } + + if (dataLength > 0) + { + data[dataLength] = ANSI_NULL; + string = PhConvertUtf8ToUtf16Ex(data, dataLength); + } + + PhFree(data); + + return string; +} + +PPH_STRING PhFileReadAllText( + _In_ PWSTR FileName + ) +{ + PPH_STRING string = NULL; + HANDLE fileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + FileName, + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + string = PhGetFileText(fileHandle); + NtClose(fileHandle); + } + + return string; +} diff --git a/phlib/verify.c b/phlib/verify.c index 17ee9c66bce8..c7246bcdea81 100644 --- a/phlib/verify.c +++ b/phlib/verify.c @@ -1,671 +1,874 @@ -/* - * Process Hacker - - * image verification - * - * Copyright (C) 2009-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include - -_CryptCATAdminCalcHashFromFileHandle CryptCATAdminCalcHashFromFileHandle; -_CryptCATAdminCalcHashFromFileHandle2 CryptCATAdminCalcHashFromFileHandle2; -_CryptCATAdminAcquireContext CryptCATAdminAcquireContext; -_CryptCATAdminAcquireContext2 CryptCATAdminAcquireContext2; -_CryptCATAdminEnumCatalogFromHash CryptCATAdminEnumCatalogFromHash; -_CryptCATCatalogInfoFromContext CryptCATCatalogInfoFromContext; -_CryptCATAdminReleaseCatalogContext CryptCATAdminReleaseCatalogContext; -_CryptCATAdminReleaseContext CryptCATAdminReleaseContext; -_WTHelperProvDataFromStateData WTHelperProvDataFromStateData_I; -_WTHelperGetProvSignerFromChain WTHelperGetProvSignerFromChain_I; -_WinVerifyTrust WinVerifyTrust_I; -_CertNameToStr CertNameToStr_I; -_CertDuplicateCertificateContext CertDuplicateCertificateContext_I; -_CertFreeCertificateContext CertFreeCertificateContext_I; -static PH_INITONCE PhpVerifyInitOnce = PH_INITONCE_INIT; - -static GUID WinTrustActionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; -static GUID DriverActionVerify = DRIVER_ACTION_VERIFY; - -static VOID PhpVerifyInitialization( - VOID - ) -{ - HMODULE wintrust; - HMODULE crypt32; - - wintrust = LoadLibrary(L"wintrust.dll"); - crypt32 = LoadLibrary(L"crypt32.dll"); - - CryptCATAdminCalcHashFromFileHandle = PhGetProcedureAddress(wintrust, "CryptCATAdminCalcHashFromFileHandle", 0); - CryptCATAdminCalcHashFromFileHandle2 = PhGetProcedureAddress(wintrust, "CryptCATAdminCalcHashFromFileHandle2", 0); - CryptCATAdminAcquireContext = PhGetProcedureAddress(wintrust, "CryptCATAdminAcquireContext", 0); - CryptCATAdminAcquireContext2 = PhGetProcedureAddress(wintrust, "CryptCATAdminAcquireContext2", 0); - CryptCATAdminEnumCatalogFromHash = PhGetProcedureAddress(wintrust, "CryptCATAdminEnumCatalogFromHash", 0); - CryptCATCatalogInfoFromContext = PhGetProcedureAddress(wintrust, "CryptCATCatalogInfoFromContext", 0); - CryptCATAdminReleaseCatalogContext = PhGetProcedureAddress(wintrust, "CryptCATAdminReleaseCatalogContext", 0); - CryptCATAdminReleaseContext = PhGetProcedureAddress(wintrust, "CryptCATAdminReleaseContext", 0); - WTHelperProvDataFromStateData_I = PhGetProcedureAddress(wintrust, "WTHelperProvDataFromStateData", 0); - WTHelperGetProvSignerFromChain_I = PhGetProcedureAddress(wintrust, "WTHelperGetProvSignerFromChain", 0); - WinVerifyTrust_I = PhGetProcedureAddress(wintrust, "WinVerifyTrust", 0); - CertNameToStr_I = PhGetProcedureAddress(crypt32, "CertNameToStrW", 0); - CertDuplicateCertificateContext_I = PhGetProcedureAddress(crypt32, "CertDuplicateCertificateContext", 0); - CertFreeCertificateContext_I = PhGetProcedureAddress(crypt32, "CertFreeCertificateContext", 0); -} - -VERIFY_RESULT PhpStatusToVerifyResult( - _In_ LONG Status - ) -{ - switch (Status) - { - case 0: - return VrTrusted; - case TRUST_E_NOSIGNATURE: - return VrNoSignature; - case CERT_E_EXPIRED: - return VrExpired; - case CERT_E_REVOKED: - return VrRevoked; - case TRUST_E_EXPLICIT_DISTRUST: - return VrDistrust; - case CRYPT_E_SECURITY_SETTINGS: - return VrSecuritySettings; - case TRUST_E_BAD_DIGEST: - return VrBadSignature; - default: - return VrSecuritySettings; - } -} - -BOOLEAN PhpGetSignaturesFromStateData( - _In_ HANDLE StateData, - _Out_ PCERT_CONTEXT **Signatures, - _Out_ PULONG NumberOfSignatures - ) -{ - PCRYPT_PROVIDER_DATA provData; - PCRYPT_PROVIDER_SGNR sgnr; - PCERT_CONTEXT *signatures; - ULONG i; - ULONG numberOfSignatures; - ULONG index; - - provData = WTHelperProvDataFromStateData_I(StateData); - - if (!provData) - { - *Signatures = NULL; - *NumberOfSignatures = 0; - return FALSE; - } - - i = 0; - numberOfSignatures = 0; - - while (sgnr = WTHelperGetProvSignerFromChain_I(provData, i, FALSE, 0)) - { - if (sgnr->csCertChain != 0) - numberOfSignatures++; - - i++; - } - - if (numberOfSignatures != 0) - { - signatures = PhAllocate(numberOfSignatures * sizeof(PCERT_CONTEXT)); - i = 0; - index = 0; - - while (sgnr = WTHelperGetProvSignerFromChain_I(provData, i, FALSE, 0)) - { - if (sgnr->csCertChain != 0) - signatures[index++] = (PCERT_CONTEXT)CertDuplicateCertificateContext_I(sgnr->pasCertChain[0].pCert); - - i++; - } - } - else - { - signatures = NULL; - } - - *Signatures = signatures; - *NumberOfSignatures = numberOfSignatures; - - return TRUE; -} - -VOID PhpViewSignerInfo( - _In_ PPH_VERIFY_FILE_INFO Information, - _In_ HANDLE StateData - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static _CryptUIDlgViewSignerInfo cryptUIDlgViewSignerInfo; - - if (PhBeginInitOnce(&initOnce)) - { - HMODULE cryptui = LoadLibrary(L"cryptui.dll"); - - cryptUIDlgViewSignerInfo = PhGetProcedureAddress(cryptui, "CryptUIDlgViewSignerInfoW", 0); - PhEndInitOnce(&initOnce); - } - - if (cryptUIDlgViewSignerInfo) - { - CRYPTUI_VIEWSIGNERINFO_STRUCT viewSignerInfo = { sizeof(CRYPTUI_VIEWSIGNERINFO_STRUCT) }; - PCRYPT_PROVIDER_DATA provData; - PCRYPT_PROVIDER_SGNR sgnr; - - if (!(provData = WTHelperProvDataFromStateData_I(StateData))) - return; - if (!(sgnr = WTHelperGetProvSignerFromChain_I(provData, 0, FALSE, 0))) - return; - - viewSignerInfo.hwndParent = Information->hWnd; - viewSignerInfo.pSignerInfo = sgnr->psSigner; - viewSignerInfo.hMsg = provData->hMsg; - viewSignerInfo.pszOID = szOID_PKIX_KP_CODE_SIGNING; - cryptUIDlgViewSignerInfo(&viewSignerInfo); - } -} - -VERIFY_RESULT PhpVerifyFile( - _In_ PPH_VERIFY_FILE_INFO Information, - _In_ HANDLE FileHandle, - _In_ ULONG UnionChoice, - _In_ PVOID UnionData, - _In_ PGUID ActionId, - _In_opt_ PVOID PolicyCallbackData, - _Out_ PCERT_CONTEXT **Signatures, - _Out_ PULONG NumberOfSignatures - ) -{ - LONG status; - WINTRUST_DATA trustData = { 0 }; - - trustData.cbStruct = sizeof(WINTRUST_DATA); - trustData.pPolicyCallbackData = PolicyCallbackData; - trustData.dwUIChoice = WTD_UI_NONE; - trustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; - trustData.dwUnionChoice = UnionChoice; - trustData.dwStateAction = WTD_STATEACTION_VERIFY; - trustData.dwProvFlags = WTD_SAFER_FLAG; - - trustData.pFile = UnionData; - - if (UnionChoice == WTD_CHOICE_CATALOG) - trustData.pCatalog = UnionData; - - if (Information->Flags & PH_VERIFY_PREVENT_NETWORK_ACCESS) - { - trustData.fdwRevocationChecks = WTD_REVOKE_NONE; - - if (WindowsVersion >= WINDOWS_VISTA) - trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; - else - trustData.dwProvFlags |= WTD_REVOCATION_CHECK_NONE; - } - - status = WinVerifyTrust_I(NULL, ActionId, &trustData); - PhpGetSignaturesFromStateData(trustData.hWVTStateData, Signatures, NumberOfSignatures); - - if (status == 0 && (Information->Flags & PH_VERIFY_VIEW_PROPERTIES)) - PhpViewSignerInfo(Information, trustData.hWVTStateData); - - // Close the state data. - trustData.dwStateAction = WTD_STATEACTION_CLOSE; - WinVerifyTrust_I(NULL, ActionId, &trustData); - - return PhpStatusToVerifyResult(status); -} - -BOOLEAN PhpCalculateFileHash( - _In_ HANDLE FileHandle, - _In_ PWSTR HashAlgorithm, - _Out_ PUCHAR *FileHash, - _Out_ PULONG FileHashLength, - _Out_ HANDLE *CatAdminHandle - ) -{ - HANDLE catAdminHandle; - PUCHAR fileHash; - ULONG fileHashLength; - - if (CryptCATAdminAcquireContext2) - { - if (!CryptCATAdminAcquireContext2(&catAdminHandle, &DriverActionVerify, HashAlgorithm, NULL, 0)) - return FALSE; - } - else - { - if (!CryptCATAdminAcquireContext(&catAdminHandle, &DriverActionVerify, 0)) - return FALSE; - } - - fileHashLength = 32; - fileHash = PhAllocate(fileHashLength); - - if (CryptCATAdminCalcHashFromFileHandle2) - { - if (!CryptCATAdminCalcHashFromFileHandle2(catAdminHandle, FileHandle, &fileHashLength, fileHash, 0)) - { - PhFree(fileHash); - fileHash = PhAllocate(fileHashLength); - - if (!CryptCATAdminCalcHashFromFileHandle2(catAdminHandle, FileHandle, &fileHashLength, fileHash, 0)) - { - CryptCATAdminReleaseContext(catAdminHandle, 0); - PhFree(fileHash); - return FALSE; - } - } - } - else - { - if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &fileHashLength, fileHash, 0)) - { - PhFree(fileHash); - fileHash = PhAllocate(fileHashLength); - - if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &fileHashLength, fileHash, 0)) - { - CryptCATAdminReleaseContext(catAdminHandle, 0); - PhFree(fileHash); - return FALSE; - } - } - } - - *FileHash = fileHash; - *FileHashLength = fileHashLength; - *CatAdminHandle = catAdminHandle; - - return TRUE; -} - -VERIFY_RESULT PhpVerifyFileFromCatalog( - _In_ PPH_VERIFY_FILE_INFO Information, - _In_ HANDLE FileHandle, - _In_opt_ PWSTR HashAlgorithm, - _Out_ PCERT_CONTEXT **Signatures, - _Out_ PULONG NumberOfSignatures - ) -{ - VERIFY_RESULT verifyResult = VrNoSignature; - PCERT_CONTEXT *signatures; - ULONG numberOfSignatures; - WINTRUST_CATALOG_INFO catalogInfo = { 0 }; - LARGE_INTEGER fileSize; - ULONG fileSizeLimit; - PUCHAR fileHash; - ULONG fileHashLength; - PPH_STRING fileHashTag; - HANDLE catAdminHandle; - HANDLE catInfoHandle; - ULONG i; - - *Signatures = NULL; - *NumberOfSignatures = 0; - - if (!NT_SUCCESS(PhGetFileSize(FileHandle, &fileSize))) - return VrNoSignature; - - signatures = NULL; - numberOfSignatures = 0; - - if (Information->FileSizeLimitForHash != -1) - { - fileSizeLimit = PH_VERIFY_DEFAULT_SIZE_LIMIT; - - if (Information->FileSizeLimitForHash != 0) - fileSizeLimit = Information->FileSizeLimitForHash; - - if (fileSize.QuadPart > fileSizeLimit) - return VrNoSignature; - } - - if (PhpCalculateFileHash(FileHandle, HashAlgorithm, &fileHash, &fileHashLength, &catAdminHandle)) - { - fileHashTag = PhBufferToHexStringEx(fileHash, fileHashLength, TRUE); - - // Search the system catalogs. - - catInfoHandle = CryptCATAdminEnumCatalogFromHash( - catAdminHandle, - fileHash, - fileHashLength, - 0, - NULL - ); - - if (catInfoHandle) - { - CATALOG_INFO ci = { 0 }; - DRIVER_VER_INFO verInfo = { 0 }; - - if (CryptCATCatalogInfoFromContext(catInfoHandle, &ci, 0)) - { - // Disable OS version checking by passing in a DRIVER_VER_INFO structure. - verInfo.cbStruct = sizeof(DRIVER_VER_INFO); - - catalogInfo.cbStruct = sizeof(catalogInfo); - catalogInfo.pcwszCatalogFilePath = ci.wszCatalogFile; - catalogInfo.pcwszMemberFilePath = Information->FileName; - catalogInfo.pcwszMemberTag = fileHashTag->Buffer; - catalogInfo.pbCalculatedFileHash = fileHash; - catalogInfo.cbCalculatedFileHash = fileHashLength; - catalogInfo.hCatAdmin = catAdminHandle; - verifyResult = PhpVerifyFile(Information, FileHandle, WTD_CHOICE_CATALOG, &catalogInfo, &DriverActionVerify, &verInfo, &signatures, &numberOfSignatures); - - if (verInfo.pcSignerCertContext) - CertFreeCertificateContext_I(verInfo.pcSignerCertContext); - } - - CryptCATAdminReleaseCatalogContext(catAdminHandle, catInfoHandle, 0); - } - else - { - // Search any user-supplied catalogs. - - for (i = 0; i < Information->NumberOfCatalogFileNames; i++) - { - PhFreeVerifySignatures(signatures, numberOfSignatures); - - catalogInfo.cbStruct = sizeof(catalogInfo); - catalogInfo.pcwszCatalogFilePath = Information->CatalogFileNames[i]; - catalogInfo.pcwszMemberFilePath = Information->FileName; - catalogInfo.pcwszMemberTag = fileHashTag->Buffer; - catalogInfo.pbCalculatedFileHash = fileHash; - catalogInfo.cbCalculatedFileHash = fileHashLength; - catalogInfo.hCatAdmin = catAdminHandle; - verifyResult = PhpVerifyFile(Information, FileHandle, WTD_CHOICE_CATALOG, &catalogInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); - - if (verifyResult == VrTrusted) - break; - } - } - - PhDereferenceObject(fileHashTag); - PhFree(fileHash); - CryptCATAdminReleaseContext(catAdminHandle, 0); - } - - *Signatures = signatures; - *NumberOfSignatures = numberOfSignatures; - - return verifyResult; -} - -NTSTATUS PhVerifyFileEx( - _In_ PPH_VERIFY_FILE_INFO Information, - _Out_ VERIFY_RESULT *VerifyResult, - _Out_opt_ PCERT_CONTEXT **Signatures, - _Out_opt_ PULONG NumberOfSignatures - ) -{ - NTSTATUS status; - HANDLE fileHandle; - VERIFY_RESULT verifyResult; - PCERT_CONTEXT *signatures; - ULONG numberOfSignatures; - WINTRUST_FILE_INFO fileInfo = { 0 }; - - if (PhBeginInitOnce(&PhpVerifyInitOnce)) - { - PhpVerifyInitialization(); - PhEndInitOnce(&PhpVerifyInitOnce); - } - - // Make sure we have successfully imported the required functions. - if ( - !CryptCATAdminCalcHashFromFileHandle || - !CryptCATAdminAcquireContext || - !CryptCATAdminEnumCatalogFromHash || - !CryptCATCatalogInfoFromContext || - !CryptCATAdminReleaseCatalogContext || - !CryptCATAdminReleaseContext || - !WinVerifyTrust_I || - !WTHelperProvDataFromStateData_I || - !WTHelperGetProvSignerFromChain_I || - !CertNameToStr_I || - !CertDuplicateCertificateContext_I || - !CertFreeCertificateContext_I - ) - return STATUS_NOT_SUPPORTED; - - if (!NT_SUCCESS(status = PhCreateFileWin32( - &fileHandle, - Information->FileName, - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - return status; - - fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO); - fileInfo.pcwszFilePath = Information->FileName; - fileInfo.hFile = fileHandle; - - verifyResult = PhpVerifyFile(Information, fileHandle, WTD_CHOICE_FILE, &fileInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); - - if (verifyResult == VrNoSignature) - { - if (CryptCATAdminAcquireContext2 && CryptCATAdminCalcHashFromFileHandle2) - { - PhFreeVerifySignatures(signatures, numberOfSignatures); - verifyResult = PhpVerifyFileFromCatalog(Information, fileHandle, BCRYPT_SHA256_ALGORITHM, &signatures, &numberOfSignatures); - } - - if (verifyResult != VrTrusted) - { - PhFreeVerifySignatures(signatures, numberOfSignatures); - verifyResult = PhpVerifyFileFromCatalog(Information, fileHandle, NULL, &signatures, &numberOfSignatures); - } - } - - *VerifyResult = verifyResult; - - if (Signatures) - *Signatures = signatures; - else - PhFreeVerifySignatures(signatures, numberOfSignatures); - - if (NumberOfSignatures) - *NumberOfSignatures = numberOfSignatures; - - NtClose(fileHandle); - - return STATUS_SUCCESS; -} - -VOID PhFreeVerifySignatures( - _In_ PCERT_CONTEXT *Signatures, - _In_ ULONG NumberOfSignatures - ) -{ - ULONG i; - - if (Signatures) - { - for (i = 0; i < NumberOfSignatures; i++) - CertFreeCertificateContext_I(Signatures[i]); - - PhFree(Signatures); - } -} - -PPH_STRING PhpGetCertNameString( - _In_ PCERT_NAME_BLOB Blob - ) -{ - PPH_STRING string; - ULONG bufferSize; - - // CertNameToStr doesn't give us the correct buffer size unless we don't provide a buffer at - // all. - bufferSize = CertNameToStr_I( - X509_ASN_ENCODING, - Blob, - CERT_X500_NAME_STR, - NULL, - 0 - ); - - string = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); - CertNameToStr_I( - X509_ASN_ENCODING, - Blob, - CERT_X500_NAME_STR, - string->Buffer, - bufferSize - ); - - PhTrimToNullTerminatorString(string); - - return string; -} - -PPH_STRING PhpGetX500Value( - _In_ PPH_STRINGREF String, - _In_ PPH_STRINGREF KeyName - ) -{ - WCHAR keyNamePlusEqualsBuffer[10]; - PH_STRINGREF keyNamePlusEquals; - SIZE_T keyNameLength; - PH_STRINGREF firstPart; - PH_STRINGREF remainingPart; - - keyNameLength = KeyName->Length / sizeof(WCHAR); - assert(!(keyNameLength > sizeof(keyNamePlusEquals) / sizeof(WCHAR) - 1)); - keyNamePlusEquals.Buffer = keyNamePlusEqualsBuffer; - keyNamePlusEquals.Length = (keyNameLength + 1) * sizeof(WCHAR); - - memcpy(keyNamePlusEquals.Buffer, KeyName->Buffer, KeyName->Length); - keyNamePlusEquals.Buffer[keyNameLength] = '='; - - // Find "Key=". - - if (!PhSplitStringRefAtString(String, &keyNamePlusEquals, FALSE, &firstPart, &remainingPart)) - return NULL; - if (remainingPart.Length == 0) - return NULL; - - // Is the value quoted? If so, return the part inside the quotes. - if (remainingPart.Buffer[0] == '"') - { - PhSkipStringRef(&remainingPart, sizeof(WCHAR)); - - if (!PhSplitStringRefAtChar(&remainingPart, '"', &firstPart, &remainingPart)) - return NULL; - - return PhCreateString2(&firstPart); - } - else - { - PhSplitStringRefAtChar(&remainingPart, ',', &firstPart, &remainingPart); - - return PhCreateString2(&firstPart); - } -} - -PPH_STRING PhGetSignerNameFromCertificate( - _In_ PCERT_CONTEXT Certificate - ) -{ - PCERT_INFO certInfo; - PH_STRINGREF keyName; - PPH_STRING name; - PPH_STRING value; - - // Cert context -> Cert info - - certInfo = Certificate->pCertInfo; - - if (!certInfo) - return NULL; - - // Cert info subject -> Subject X.500 string - - name = PhpGetCertNameString(&certInfo->Subject); - - // Subject X.500 string -> CN or OU value - - PhInitializeStringRef(&keyName, L"CN"); - value = PhpGetX500Value(&name->sr, &keyName); - - if (!value) - { - PhInitializeStringRef(&keyName, L"OU"); - value = PhpGetX500Value(&name->sr, &keyName); - } - - PhDereferenceObject(name); - - return value; -} - -/** - * Verifies a file's digital signature. - * - * \param FileName A file name. - * \param SignerName A variable which receives a pointer to a string containing the signer name. You - * must free the string using PhDereferenceObject() when you no longer need it. Note that the signer - * name may be NULL if it is not valid. - * - * \return A VERIFY_RESULT value. - */ -VERIFY_RESULT PhVerifyFile( - _In_ PWSTR FileName, - _Out_opt_ PPH_STRING *SignerName - ) -{ - PH_VERIFY_FILE_INFO info = { 0 }; - VERIFY_RESULT verifyResult; - PCERT_CONTEXT *signatures; - ULONG numberOfSignatures; - - info.FileName = FileName; - info.Flags = PH_VERIFY_PREVENT_NETWORK_ACCESS; - - if (NT_SUCCESS(PhVerifyFileEx(&info, &verifyResult, &signatures, &numberOfSignatures))) - { - if (SignerName) - { - *SignerName = NULL; - - if (numberOfSignatures != 0) - *SignerName = PhGetSignerNameFromCertificate(signatures[0]); - } - - PhFreeVerifySignatures(signatures, numberOfSignatures); - return verifyResult; - } - else - { - if (SignerName) - *SignerName = NULL; - - return VrNoSignature; - } -} +/* + * Process Hacker - + * image verification + * + * Copyright (C) 2009-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define PH_ENABLE_VERIFY_CACHE +#include +#include +#include +#include + +_CryptCATAdminCalcHashFromFileHandle CryptCATAdminCalcHashFromFileHandle; +_CryptCATAdminCalcHashFromFileHandle2 CryptCATAdminCalcHashFromFileHandle2; +_CryptCATAdminAcquireContext CryptCATAdminAcquireContext; +_CryptCATAdminAcquireContext2 CryptCATAdminAcquireContext2; +_CryptCATAdminEnumCatalogFromHash CryptCATAdminEnumCatalogFromHash; +_CryptCATCatalogInfoFromContext CryptCATCatalogInfoFromContext; +_CryptCATAdminReleaseCatalogContext CryptCATAdminReleaseCatalogContext; +_CryptCATAdminReleaseContext CryptCATAdminReleaseContext; +_WTHelperProvDataFromStateData WTHelperProvDataFromStateData_I; +_WTHelperGetProvSignerFromChain WTHelperGetProvSignerFromChain_I; +_WinVerifyTrust WinVerifyTrust_I; +_CertNameToStr CertNameToStr_I; +_CertDuplicateCertificateContext CertDuplicateCertificateContext_I; +_CertFreeCertificateContext CertFreeCertificateContext_I; + +static PH_INITONCE PhpVerifyInitOnce = PH_INITONCE_INIT; +static GUID WinTrustActionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; +static GUID DriverActionVerify = DRIVER_ACTION_VERIFY; + +#ifdef PH_ENABLE_VERIFY_CACHE +static PH_AVL_TREE PhpVerifyCacheSet = PH_AVL_TREE_INIT(PhpVerifyCacheCompareFunction); +static PH_QUEUED_LOCK PhpVerifyCacheLock = PH_QUEUED_LOCK_INIT; +#endif + +static VOID PhpVerifyInitialization( + VOID + ) +{ + HMODULE wintrust; + HMODULE crypt32; + + wintrust = LoadLibrary(L"wintrust.dll"); + crypt32 = LoadLibrary(L"crypt32.dll"); + + if (wintrust) + { + CryptCATAdminCalcHashFromFileHandle = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminCalcHashFromFileHandle", 0); + CryptCATAdminCalcHashFromFileHandle2 = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminCalcHashFromFileHandle2", 0); + CryptCATAdminAcquireContext = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminAcquireContext", 0); + CryptCATAdminAcquireContext2 = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminAcquireContext2", 0); + CryptCATAdminEnumCatalogFromHash = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminEnumCatalogFromHash", 0); + CryptCATCatalogInfoFromContext = PhGetDllBaseProcedureAddress(wintrust, "CryptCATCatalogInfoFromContext", 0); + CryptCATAdminReleaseCatalogContext = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminReleaseCatalogContext", 0); + CryptCATAdminReleaseContext = PhGetDllBaseProcedureAddress(wintrust, "CryptCATAdminReleaseContext", 0); + WTHelperProvDataFromStateData_I = PhGetDllBaseProcedureAddress(wintrust, "WTHelperProvDataFromStateData", 0); + WTHelperGetProvSignerFromChain_I = PhGetDllBaseProcedureAddress(wintrust, "WTHelperGetProvSignerFromChain", 0); + WinVerifyTrust_I = PhGetDllBaseProcedureAddress(wintrust, "WinVerifyTrust", 0); + } + + if (crypt32) + { + CertNameToStr_I = PhGetDllBaseProcedureAddress(crypt32, "CertNameToStrW", 0); + CertDuplicateCertificateContext_I = PhGetDllBaseProcedureAddress(crypt32, "CertDuplicateCertificateContext", 0); + CertFreeCertificateContext_I = PhGetDllBaseProcedureAddress(crypt32, "CertFreeCertificateContext", 0); + } +} + +VERIFY_RESULT PhpStatusToVerifyResult( + _In_ LONG Status + ) +{ + switch (Status) + { + case 0: + return VrTrusted; + case TRUST_E_NOSIGNATURE: + return VrNoSignature; + case CERT_E_EXPIRED: + return VrExpired; + case CERT_E_REVOKED: + return VrRevoked; + case TRUST_E_EXPLICIT_DISTRUST: + return VrDistrust; + case CRYPT_E_SECURITY_SETTINGS: + return VrSecuritySettings; + case TRUST_E_BAD_DIGEST: + return VrBadSignature; + default: + return VrSecuritySettings; + } +} + +BOOLEAN PhpGetSignaturesFromStateData( + _In_ HANDLE StateData, + _Out_ PCERT_CONTEXT **Signatures, + _Out_ PULONG NumberOfSignatures + ) +{ + PCRYPT_PROVIDER_DATA provData; + PCRYPT_PROVIDER_SGNR sgnr; + PCERT_CONTEXT *signatures; + ULONG i; + ULONG numberOfSignatures; + ULONG index; + + provData = WTHelperProvDataFromStateData_I(StateData); + + if (!provData) + { + *Signatures = NULL; + *NumberOfSignatures = 0; + return FALSE; + } + + i = 0; + numberOfSignatures = 0; + + while (sgnr = WTHelperGetProvSignerFromChain_I(provData, i, FALSE, 0)) + { + if (sgnr->csCertChain != 0) + numberOfSignatures++; + + i++; + } + + if (numberOfSignatures != 0) + { + signatures = PhAllocate(numberOfSignatures * sizeof(PCERT_CONTEXT)); + i = 0; + index = 0; + + while (sgnr = WTHelperGetProvSignerFromChain_I(provData, i, FALSE, 0)) + { + if (sgnr->csCertChain != 0) + signatures[index++] = (PCERT_CONTEXT)CertDuplicateCertificateContext_I(sgnr->pasCertChain[0].pCert); + + i++; + } + } + else + { + signatures = NULL; + } + + *Signatures = signatures; + *NumberOfSignatures = numberOfSignatures; + + return TRUE; +} + +VOID PhpViewSignerInfo( + _In_ PPH_VERIFY_FILE_INFO Information, + _In_ HANDLE StateData + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static _CryptUIDlgViewSignerInfo cryptUIDlgViewSignerInfo; + + if (PhBeginInitOnce(&initOnce)) + { + HMODULE cryptui = LoadLibrary(L"cryptui.dll"); + + cryptUIDlgViewSignerInfo = PhGetDllBaseProcedureAddress(cryptui, "CryptUIDlgViewSignerInfoW", 0); + PhEndInitOnce(&initOnce); + } + + if (cryptUIDlgViewSignerInfo) + { + CRYPTUI_VIEWSIGNERINFO_STRUCT viewSignerInfo = { sizeof(CRYPTUI_VIEWSIGNERINFO_STRUCT) }; + PCRYPT_PROVIDER_DATA provData; + PCRYPT_PROVIDER_SGNR sgnr; + + if (!(provData = WTHelperProvDataFromStateData_I(StateData))) + return; + if (!(sgnr = WTHelperGetProvSignerFromChain_I(provData, 0, FALSE, 0))) + return; + + viewSignerInfo.hwndParent = Information->hWnd; + viewSignerInfo.pSignerInfo = sgnr->psSigner; + viewSignerInfo.hMsg = provData->hMsg; + viewSignerInfo.pszOID = szOID_PKIX_KP_CODE_SIGNING; + cryptUIDlgViewSignerInfo(&viewSignerInfo); + } +} + +VERIFY_RESULT PhpVerifyFile( + _In_ PPH_VERIFY_FILE_INFO Information, + _In_ ULONG UnionChoice, + _In_ PVOID UnionData, + _In_ PGUID ActionId, + _In_opt_ PVOID PolicyCallbackData, + _Out_ PCERT_CONTEXT **Signatures, + _Out_ PULONG NumberOfSignatures + ) +{ + LONG status; + WINTRUST_DATA trustData = { 0 }; + + trustData.cbStruct = sizeof(WINTRUST_DATA); + trustData.pPolicyCallbackData = PolicyCallbackData; + trustData.dwUIChoice = WTD_UI_NONE; + trustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; + trustData.dwUnionChoice = UnionChoice; + trustData.dwStateAction = WTD_STATEACTION_VERIFY; + trustData.dwProvFlags = WTD_SAFER_FLAG; + + trustData.pFile = UnionData; + + if (UnionChoice == WTD_CHOICE_CATALOG) + trustData.pCatalog = UnionData; + + if (Information->Flags & PH_VERIFY_PREVENT_NETWORK_ACCESS) + { + trustData.fdwRevocationChecks = WTD_REVOKE_NONE; + trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; + } + + status = WinVerifyTrust_I(INVALID_HANDLE_VALUE, ActionId, &trustData); + PhpGetSignaturesFromStateData(trustData.hWVTStateData, Signatures, NumberOfSignatures); + + if (status == 0 && (Information->Flags & PH_VERIFY_VIEW_PROPERTIES)) + PhpViewSignerInfo(Information, trustData.hWVTStateData); + + // Close the state data. + trustData.dwStateAction = WTD_STATEACTION_CLOSE; + WinVerifyTrust_I(INVALID_HANDLE_VALUE, ActionId, &trustData); + + return PhpStatusToVerifyResult(status); +} + +BOOLEAN PhpCalculateFileHash( + _In_ HANDLE FileHandle, + _In_ PWSTR HashAlgorithm, + _Out_ PUCHAR *FileHash, + _Out_ PULONG FileHashLength, + _Out_ HANDLE *CatAdminHandle + ) +{ + HANDLE catAdminHandle; + PUCHAR fileHash; + ULONG fileHashLength; + + if (CryptCATAdminAcquireContext2) + { + if (!CryptCATAdminAcquireContext2(&catAdminHandle, &DriverActionVerify, HashAlgorithm, NULL, 0)) + return FALSE; + } + else + { + if (!CryptCATAdminAcquireContext(&catAdminHandle, &DriverActionVerify, 0)) + return FALSE; + } + + fileHashLength = 32; + fileHash = PhAllocate(fileHashLength); + + if (CryptCATAdminCalcHashFromFileHandle2) + { + if (!CryptCATAdminCalcHashFromFileHandle2(catAdminHandle, FileHandle, &fileHashLength, fileHash, 0)) + { + PhFree(fileHash); + fileHash = PhAllocate(fileHashLength); + + if (!CryptCATAdminCalcHashFromFileHandle2(catAdminHandle, FileHandle, &fileHashLength, fileHash, 0)) + { + CryptCATAdminReleaseContext(catAdminHandle, 0); + PhFree(fileHash); + return FALSE; + } + } + } + else + { + if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &fileHashLength, fileHash, 0)) + { + PhFree(fileHash); + fileHash = PhAllocate(fileHashLength); + + if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &fileHashLength, fileHash, 0)) + { + CryptCATAdminReleaseContext(catAdminHandle, 0); + PhFree(fileHash); + return FALSE; + } + } + } + + *FileHash = fileHash; + *FileHashLength = fileHashLength; + *CatAdminHandle = catAdminHandle; + + return TRUE; +} + +VERIFY_RESULT PhpVerifyFileFromCatalog( + _In_ PPH_VERIFY_FILE_INFO Information, + _In_ HANDLE FileHandle, + _In_opt_ PWSTR HashAlgorithm, + _Out_ PCERT_CONTEXT **Signatures, + _Out_ PULONG NumberOfSignatures + ) +{ + VERIFY_RESULT verifyResult = VrNoSignature; + PCERT_CONTEXT *signatures; + ULONG numberOfSignatures; + WINTRUST_CATALOG_INFO catalogInfo = { 0 }; + LARGE_INTEGER fileSize; + ULONG fileSizeLimit; + PUCHAR fileHash; + ULONG fileHashLength; + PPH_STRING fileHashTag; + HANDLE catAdminHandle; + HANDLE catInfoHandle; + ULONG i; + + *Signatures = NULL; + *NumberOfSignatures = 0; + + if (!NT_SUCCESS(PhGetFileSize(FileHandle, &fileSize))) + return VrNoSignature; + + signatures = NULL; + numberOfSignatures = 0; + + if (Information->FileSizeLimitForHash != -1) + { + fileSizeLimit = PH_VERIFY_DEFAULT_SIZE_LIMIT; + + if (Information->FileSizeLimitForHash != 0) + fileSizeLimit = Information->FileSizeLimitForHash; + + if (fileSize.QuadPart > fileSizeLimit) + return VrNoSignature; + } + + if (PhpCalculateFileHash(FileHandle, HashAlgorithm, &fileHash, &fileHashLength, &catAdminHandle)) + { + fileHashTag = PhBufferToHexStringEx(fileHash, fileHashLength, TRUE); + + // Search the system catalogs. + + catInfoHandle = CryptCATAdminEnumCatalogFromHash( + catAdminHandle, + fileHash, + fileHashLength, + 0, + NULL + ); + + if (catInfoHandle) + { + CATALOG_INFO ci = { 0 }; + DRIVER_VER_INFO verInfo = { 0 }; + + if (CryptCATCatalogInfoFromContext(catInfoHandle, &ci, 0)) + { + // Disable OS version checking by passing in a DRIVER_VER_INFO structure. + verInfo.cbStruct = sizeof(DRIVER_VER_INFO); + + catalogInfo.cbStruct = sizeof(catalogInfo); + catalogInfo.pcwszCatalogFilePath = ci.wszCatalogFile; + catalogInfo.pcwszMemberFilePath = Information->FileName; + catalogInfo.hMemberFile = FileHandle; + catalogInfo.pcwszMemberTag = fileHashTag->Buffer; + catalogInfo.pbCalculatedFileHash = fileHash; + catalogInfo.cbCalculatedFileHash = fileHashLength; + catalogInfo.hCatAdmin = catAdminHandle; + verifyResult = PhpVerifyFile(Information, WTD_CHOICE_CATALOG, &catalogInfo, &DriverActionVerify, &verInfo, &signatures, &numberOfSignatures); + + if (verInfo.pcSignerCertContext) + CertFreeCertificateContext_I(verInfo.pcSignerCertContext); + } + + CryptCATAdminReleaseCatalogContext(catAdminHandle, catInfoHandle, 0); + } + else + { + // Search any user-supplied catalogs. + + for (i = 0; i < Information->NumberOfCatalogFileNames; i++) + { + PhFreeVerifySignatures(signatures, numberOfSignatures); + + catalogInfo.cbStruct = sizeof(catalogInfo); + catalogInfo.pcwszCatalogFilePath = Information->CatalogFileNames[i]; + catalogInfo.pcwszMemberFilePath = Information->FileName; + catalogInfo.hMemberFile = FileHandle; + catalogInfo.pcwszMemberTag = fileHashTag->Buffer; + catalogInfo.pbCalculatedFileHash = fileHash; + catalogInfo.cbCalculatedFileHash = fileHashLength; + catalogInfo.hCatAdmin = catAdminHandle; + verifyResult = PhpVerifyFile(Information, WTD_CHOICE_CATALOG, &catalogInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); + + if (verifyResult == VrTrusted) + break; + } + } + + PhDereferenceObject(fileHashTag); + PhFree(fileHash); + CryptCATAdminReleaseContext(catAdminHandle, 0); + } + + *Signatures = signatures; + *NumberOfSignatures = numberOfSignatures; + + return verifyResult; +} + +NTSTATUS PhVerifyFileEx( + _In_ PPH_VERIFY_FILE_INFO Information, + _Out_ VERIFY_RESULT *VerifyResult, + _Out_opt_ PCERT_CONTEXT **Signatures, + _Out_opt_ PULONG NumberOfSignatures + ) +{ + NTSTATUS status; + HANDLE fileHandle; + VERIFY_RESULT verifyResult; + PCERT_CONTEXT *signatures; + ULONG numberOfSignatures; + WINTRUST_FILE_INFO fileInfo = { 0 }; + + if (PhBeginInitOnce(&PhpVerifyInitOnce)) + { + PhpVerifyInitialization(); + PhEndInitOnce(&PhpVerifyInitOnce); + } + + // Make sure we have successfully imported the required functions. + if ( + !CryptCATAdminCalcHashFromFileHandle || + !CryptCATAdminAcquireContext || + !CryptCATAdminEnumCatalogFromHash || + !CryptCATCatalogInfoFromContext || + !CryptCATAdminReleaseCatalogContext || + !CryptCATAdminReleaseContext || + !WinVerifyTrust_I || + !WTHelperProvDataFromStateData_I || + !WTHelperGetProvSignerFromChain_I || + !CertNameToStr_I || + !CertDuplicateCertificateContext_I || + !CertFreeCertificateContext_I + ) + return STATUS_NOT_SUPPORTED; + + if (!NT_SUCCESS(status = PhCreateFileWin32( + &fileHandle, + Information->FileName, + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + return status; + + fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO); + fileInfo.pcwszFilePath = Information->FileName; + fileInfo.hFile = fileHandle; + + verifyResult = PhpVerifyFile(Information, WTD_CHOICE_FILE, &fileInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures); + + if (verifyResult == VrNoSignature) + { + if (CryptCATAdminAcquireContext2 && CryptCATAdminCalcHashFromFileHandle2) + { + PhFreeVerifySignatures(signatures, numberOfSignatures); + verifyResult = PhpVerifyFileFromCatalog(Information, fileHandle, BCRYPT_SHA256_ALGORITHM, &signatures, &numberOfSignatures); + } + + if (verifyResult != VrTrusted) + { + PhFreeVerifySignatures(signatures, numberOfSignatures); + verifyResult = PhpVerifyFileFromCatalog(Information, fileHandle, NULL, &signatures, &numberOfSignatures); + } + } + + *VerifyResult = verifyResult; + + if (Signatures) + *Signatures = signatures; + else + PhFreeVerifySignatures(signatures, numberOfSignatures); + + if (NumberOfSignatures) + *NumberOfSignatures = numberOfSignatures; + + NtClose(fileHandle); + + return STATUS_SUCCESS; +} + +VOID PhFreeVerifySignatures( + _In_ PCERT_CONTEXT *Signatures, + _In_ ULONG NumberOfSignatures + ) +{ + ULONG i; + + if (Signatures) + { + for (i = 0; i < NumberOfSignatures; i++) + CertFreeCertificateContext_I(Signatures[i]); + + PhFree(Signatures); + } +} + +PPH_STRING PhpGetCertNameString( + _In_ PCERT_NAME_BLOB Blob + ) +{ + PPH_STRING string; + ULONG bufferSize; + + // CertNameToStr doesn't give us the correct buffer size unless we don't provide a buffer at + // all. + bufferSize = CertNameToStr_I( + X509_ASN_ENCODING, + Blob, + CERT_X500_NAME_STR, + NULL, + 0 + ); + + string = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR)); + CertNameToStr_I( + X509_ASN_ENCODING, + Blob, + CERT_X500_NAME_STR, + string->Buffer, + bufferSize + ); + + PhTrimToNullTerminatorString(string); + + return string; +} + +PPH_STRING PhpGetX500Value( + _In_ PPH_STRINGREF String, + _In_ PPH_STRINGREF KeyName + ) +{ + WCHAR keyNamePlusEqualsBuffer[10]; + PH_STRINGREF keyNamePlusEquals; + SIZE_T keyNameLength; + PH_STRINGREF firstPart; + PH_STRINGREF remainingPart; + + keyNameLength = KeyName->Length / sizeof(WCHAR); + assert(!(keyNameLength > sizeof(keyNamePlusEquals) / sizeof(WCHAR) - 1)); + keyNamePlusEquals.Buffer = keyNamePlusEqualsBuffer; + keyNamePlusEquals.Length = (keyNameLength + 1) * sizeof(WCHAR); + + memcpy(keyNamePlusEquals.Buffer, KeyName->Buffer, KeyName->Length); + keyNamePlusEquals.Buffer[keyNameLength] = '='; + + // Find "Key=". + + if (!PhSplitStringRefAtString(String, &keyNamePlusEquals, FALSE, &firstPart, &remainingPart)) + return NULL; + if (remainingPart.Length == 0) + return NULL; + + // Is the value quoted? If so, return the part inside the quotes. + if (remainingPart.Buffer[0] == '"') + { + PhSkipStringRef(&remainingPart, sizeof(WCHAR)); + + if (!PhSplitStringRefAtChar(&remainingPart, '"', &firstPart, &remainingPart)) + return NULL; + + return PhCreateString2(&firstPart); + } + else + { + PhSplitStringRefAtChar(&remainingPart, ',', &firstPart, &remainingPart); + + return PhCreateString2(&firstPart); + } +} + +PPH_STRING PhGetSignerNameFromCertificate( + _In_ PCERT_CONTEXT Certificate + ) +{ + PCERT_INFO certInfo; + PH_STRINGREF keyName; + PPH_STRING name; + PPH_STRING value; + + // Cert context -> Cert info + + certInfo = Certificate->pCertInfo; + + if (!certInfo) + return NULL; + + // Cert info subject -> Subject X.500 string + + name = PhpGetCertNameString(&certInfo->Subject); + + // Subject X.500 string -> CN or OU value + + PhInitializeStringRef(&keyName, L"CN"); + value = PhpGetX500Value(&name->sr, &keyName); + + if (!value) + { + PhInitializeStringRef(&keyName, L"OU"); + value = PhpGetX500Value(&name->sr, &keyName); + } + + PhDereferenceObject(name); + + return value; +} + +/** + * Verifies a file's digital signature. + * + * \param FileName A file name. + * \param SignerName A variable which receives a pointer to a string containing the signer name. You + * must free the string using PhDereferenceObject() when you no longer need it. Note that the signer + * name may be NULL if it is not valid. + * + * \return A VERIFY_RESULT value. + */ +VERIFY_RESULT PhVerifyFile( + _In_ PWSTR FileName, + _Out_opt_ PPH_STRING *SignerName + ) +{ + PH_VERIFY_FILE_INFO info = { 0 }; + VERIFY_RESULT verifyResult; + PCERT_CONTEXT *signatures; + ULONG numberOfSignatures; + + info.FileName = FileName; + info.Flags = PH_VERIFY_PREVENT_NETWORK_ACCESS; + + if (NT_SUCCESS(PhVerifyFileEx(&info, &verifyResult, &signatures, &numberOfSignatures))) + { + if (SignerName) + { + *SignerName = NULL; + + if (numberOfSignatures != 0) + *SignerName = PhGetSignerNameFromCertificate(signatures[0]); + } + + PhFreeVerifySignatures(signatures, numberOfSignatures); + return verifyResult; + } + else + { + if (SignerName) + *SignerName = NULL; + + return VrNoSignature; + } +} + +INT NTAPI PhpVerifyCacheCompareFunction( + _In_ PPH_AVL_LINKS Links1, + _In_ PPH_AVL_LINKS Links2 + ) +{ + PPH_VERIFY_CACHE_ENTRY entry1 = CONTAINING_RECORD(Links1, PH_VERIFY_CACHE_ENTRY, Links); + PPH_VERIFY_CACHE_ENTRY entry2 = CONTAINING_RECORD(Links2, PH_VERIFY_CACHE_ENTRY, Links); + + return PhCompareString(entry1->FileName, entry2->FileName, TRUE); +} + +VERIFY_RESULT PhVerifyFileWithAdditionalCatalog( + _In_ PPH_VERIFY_FILE_INFO Information, + _In_opt_ PPH_STRING PackageFullName, + _Out_opt_ PPH_STRING *SignerName + ) +{ + static PH_STRINGREF codeIntegrityFileName = PH_STRINGREF_INIT(L"\\AppxMetadata\\CodeIntegrity.cat"); + + VERIFY_RESULT result; + PPH_STRING additionalCatalogFileName = NULL; + PCERT_CONTEXT *signatures; + ULONG numberOfSignatures; + + if (PackageFullName) + { + PPH_STRING packagePath; + + if (packagePath = PhGetPackagePath(PackageFullName)) + { + additionalCatalogFileName = PhConcatStringRef2(&packagePath->sr, &codeIntegrityFileName); + PhDereferenceObject(packagePath); + } + } + + if (additionalCatalogFileName) + { + Information->NumberOfCatalogFileNames = 1; + Information->CatalogFileNames = &additionalCatalogFileName->Buffer; + } + + if (!NT_SUCCESS(PhVerifyFileEx(Information, &result, &signatures, &numberOfSignatures))) + { + result = VrNoSignature; + signatures = NULL; + numberOfSignatures = 0; + } + + if (additionalCatalogFileName) + PhDereferenceObject(additionalCatalogFileName); + + if (SignerName) + { + if (numberOfSignatures != 0) + *SignerName = PhGetSignerNameFromCertificate(signatures[0]); + else + *SignerName = NULL; + } + + PhFreeVerifySignatures(signatures, numberOfSignatures); + + return result; +} + +/** + * Verifies a file's digital signature, using a cached result if possible. + * + * \param FileName A file name. + * \param ProcessItem An associated process item. + * \param SignerName A variable which receives a pointer to a string containing the signer name. You + * must free the string using PhDereferenceObject() when you no longer need it. Note that the signer + * name may be NULL if it is not valid. + * \param CachedOnly Specify TRUE to fail the function when no cached result exists. + * + * \return A VERIFY_RESULT value. + */ +VERIFY_RESULT PhVerifyFileCached( + _In_ PPH_STRING FileName, + _In_opt_ PPH_STRING PackageFullName, + _Out_opt_ PPH_STRING *SignerName, + _In_ BOOLEAN CachedOnly + ) +{ +#ifdef PH_ENABLE_VERIFY_CACHE + PPH_AVL_LINKS links; + PPH_VERIFY_CACHE_ENTRY entry; + PH_VERIFY_CACHE_ENTRY lookupEntry; + + lookupEntry.FileName = FileName; + + PhAcquireQueuedLockShared(&PhpVerifyCacheLock); + links = PhFindElementAvlTree(&PhpVerifyCacheSet, &lookupEntry.Links); + PhReleaseQueuedLockShared(&PhpVerifyCacheLock); + + if (links) + { + entry = CONTAINING_RECORD(links, PH_VERIFY_CACHE_ENTRY, Links); + + if (SignerName) + PhSetReference(SignerName, entry->VerifySignerName); + + return entry->VerifyResult; + } + else + { + VERIFY_RESULT result; + PPH_STRING signerName; + + if (!CachedOnly) + { + PH_VERIFY_FILE_INFO info; + + memset(&info, 0, sizeof(PH_VERIFY_FILE_INFO)); + info.FileName = FileName->Buffer; + info.Flags = PH_VERIFY_PREVENT_NETWORK_ACCESS; + result = PhVerifyFileWithAdditionalCatalog(&info, PackageFullName, &signerName); + + if (result != VrTrusted) + PhClearReference(&signerName); + } + else + { + result = VrUnknown; + signerName = NULL; + } + + if (result != VrUnknown) + { + entry = PhAllocate(sizeof(PH_VERIFY_CACHE_ENTRY)); + entry->FileName = FileName; + entry->VerifyResult = result; + entry->VerifySignerName = signerName; + + PhAcquireQueuedLockExclusive(&PhpVerifyCacheLock); + links = PhAddElementAvlTree(&PhpVerifyCacheSet, &entry->Links); + PhReleaseQueuedLockExclusive(&PhpVerifyCacheLock); + + if (!links) + { + // We successfully added the cache entry. Add references. + + PhReferenceObject(entry->FileName); + + if (entry->VerifySignerName) + PhReferenceObject(entry->VerifySignerName); + } + else + { + // Entry already exists. + PhFree(entry); + } + } + + if (SignerName) + { + *SignerName = signerName; + } + else + { + if (signerName) + PhDereferenceObject(signerName); + } + + return result; + } +#else + VERIFY_RESULT result; + PPH_STRING signerName; + PH_VERIFY_FILE_INFO info; + + memset(&info, 0, sizeof(PH_VERIFY_FILE_INFO)); + info.FileName = FileName->Buffer; + info.Flags = PH_VERIFY_PREVENT_NETWORK_ACCESS; + result = PhVerifyFileWithAdditionalCatalog(&info, PackageFullName, &signerName); + + if (result != VrTrusted) + PhClearReference(&signerName); + + if (SignerName) + { + *SignerName = signerName; + } + else + { + if (signerName) + PhDereferenceObject(signerName); + } + + return result; +#endif +} diff --git a/phlib/workqueue.c b/phlib/workqueue.c index d77e8104f2ee..519dcc753f82 100644 --- a/phlib/workqueue.c +++ b/phlib/workqueue.c @@ -1,506 +1,503 @@ -/* - * Process Hacker - - * thread pool / work queue - * - * Copyright (C) 2009-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -#include - -#include - -static PH_INITONCE PhWorkQueueInitOnce = PH_INITONCE_INIT; -static PH_FREE_LIST PhWorkQueueItemFreeList; -static PH_INITONCE PhGlobalWorkQueueInitOnce = PH_INITONCE_INIT; -static PH_WORK_QUEUE PhGlobalWorkQueue; -#ifdef DEBUG -PPH_LIST PhDbgWorkQueueList; -PH_QUEUED_LOCK PhDbgWorkQueueListLock = PH_QUEUED_LOCK_INIT; -#endif - -/** - * Initializes a work queue. - * - * \param WorkQueue A work queue object. - * \param MinimumThreads The suggested minimum number of threads to keep alive, even when there is - * no work to be performed. - * \param MaximumThreads The suggested maximum number of threads to create. - * \param NoWorkTimeout The number of milliseconds after which threads without work will terminate. - */ -VOID PhInitializeWorkQueue( - _Out_ PPH_WORK_QUEUE WorkQueue, - _In_ ULONG MinimumThreads, - _In_ ULONG MaximumThreads, - _In_ ULONG NoWorkTimeout - ) -{ - if (PhBeginInitOnce(&PhWorkQueueInitOnce)) - { - PhInitializeFreeList(&PhWorkQueueItemFreeList, sizeof(PH_WORK_QUEUE_ITEM), 32); -#ifdef DEBUG - PhDbgWorkQueueList = PhCreateList(4); -#endif - - PhEndInitOnce(&PhWorkQueueInitOnce); - } - - PhInitializeRundownProtection(&WorkQueue->RundownProtect); - WorkQueue->Terminating = FALSE; - - InitializeListHead(&WorkQueue->QueueListHead); - PhInitializeQueuedLock(&WorkQueue->QueueLock); - PhInitializeCondition(&WorkQueue->QueueEmptyCondition); - - WorkQueue->MinimumThreads = MinimumThreads; - WorkQueue->MaximumThreads = MaximumThreads; - WorkQueue->NoWorkTimeout = NoWorkTimeout; - - PhInitializeQueuedLock(&WorkQueue->StateLock); - - WorkQueue->SemaphoreHandle = NULL; - WorkQueue->CurrentThreads = 0; - WorkQueue->BusyCount = 0; - -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&PhDbgWorkQueueListLock); - PhAddItemList(PhDbgWorkQueueList, WorkQueue); - PhReleaseQueuedLockExclusive(&PhDbgWorkQueueListLock); -#endif -} - -/** - * Frees resources used by a work queue. - * - * \param WorkQueue A work queue object. - */ -VOID PhDeleteWorkQueue( - _Inout_ PPH_WORK_QUEUE WorkQueue - ) -{ - PLIST_ENTRY listEntry; - PPH_WORK_QUEUE_ITEM workQueueItem; -#ifdef DEBUG - ULONG index; -#endif - -#ifdef DEBUG - PhAcquireQueuedLockExclusive(&PhDbgWorkQueueListLock); - if ((index = PhFindItemList(PhDbgWorkQueueList, WorkQueue)) != -1) - PhRemoveItemList(PhDbgWorkQueueList, index); - PhReleaseQueuedLockExclusive(&PhDbgWorkQueueListLock); -#endif - - // Wait for all worker threads to exit. - - WorkQueue->Terminating = TRUE; - MemoryBarrier(); - - if (WorkQueue->SemaphoreHandle) - NtReleaseSemaphore(WorkQueue->SemaphoreHandle, WorkQueue->CurrentThreads, NULL); - - PhWaitForRundownProtection(&WorkQueue->RundownProtect); - - // Free all un-executed work items. - - listEntry = WorkQueue->QueueListHead.Flink; - - while (listEntry != &WorkQueue->QueueListHead) - { - workQueueItem = CONTAINING_RECORD(listEntry, PH_WORK_QUEUE_ITEM, ListEntry); - listEntry = listEntry->Flink; - PhpDestroyWorkQueueItem(workQueueItem); - } - - if (WorkQueue->SemaphoreHandle) - NtClose(WorkQueue->SemaphoreHandle); -} - -/** - * Waits for all queued work items to be executed. - * - * \param WorkQueue A work queue object. - */ -VOID PhWaitForWorkQueue( - _Inout_ PPH_WORK_QUEUE WorkQueue - ) -{ - PhAcquireQueuedLockExclusive(&WorkQueue->QueueLock); - - while (!IsListEmpty(&WorkQueue->QueueListHead)) - PhWaitForCondition(&WorkQueue->QueueEmptyCondition, &WorkQueue->QueueLock, NULL); - - PhReleaseQueuedLockExclusive(&WorkQueue->QueueLock); -} - -/** - * Queues a work item to a work queue. - * - * \param WorkQueue A work queue object. - * \param Function A function to execute. - * \param Context A user-defined value to pass to the function. - */ -VOID PhQueueItemWorkQueue( - _Inout_ PPH_WORK_QUEUE WorkQueue, - _In_ PUSER_THREAD_START_ROUTINE Function, - _In_opt_ PVOID Context - ) -{ - PhQueueItemWorkQueueEx(WorkQueue, Function, Context, NULL, NULL); -} - -/** - * Queues a work item to a work queue. - * - * \param WorkQueue A work queue object. - * \param Function A function to execute. - * \param Context A user-defined value to pass to the function. - * \param DeleteFunction A callback function that is executed when the work queue item is about to - * be freed. - * \param Environment Execution environment parameters (e.g. priority). - */ -VOID PhQueueItemWorkQueueEx( - _Inout_ PPH_WORK_QUEUE WorkQueue, - _In_ PUSER_THREAD_START_ROUTINE Function, - _In_opt_ PVOID Context, - _In_opt_ PPH_WORK_QUEUE_ITEM_DELETE_FUNCTION DeleteFunction, - _In_opt_ PPH_WORK_QUEUE_ENVIRONMENT Environment - ) -{ - PPH_WORK_QUEUE_ITEM workQueueItem; - - workQueueItem = PhpCreateWorkQueueItem(Function, Context, DeleteFunction, Environment); - - // Enqueue the work item. - PhAcquireQueuedLockExclusive(&WorkQueue->QueueLock); - InsertTailList(&WorkQueue->QueueListHead, &workQueueItem->ListEntry); - _InterlockedIncrement(&WorkQueue->BusyCount); - PhReleaseQueuedLockExclusive(&WorkQueue->QueueLock); - // Signal the semaphore once to let a worker thread continue. - NtReleaseSemaphore(PhpGetSemaphoreWorkQueue(WorkQueue), 1, NULL); - - PHLIB_INC_STATISTIC(WqWorkItemsQueued); - - // Check if all worker threads are currently busy, and if we can create more threads. - if (WorkQueue->BusyCount >= WorkQueue->CurrentThreads && - WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) - { - // Lock and re-check. - PhAcquireQueuedLockExclusive(&WorkQueue->StateLock); - - if (WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) - PhpCreateWorkQueueThread(WorkQueue); - - PhReleaseQueuedLockExclusive(&WorkQueue->StateLock); - } -} - -VOID PhInitializeWorkQueueEnvironment( - _Out_ PPH_WORK_QUEUE_ENVIRONMENT Environment - ) -{ - PhpGetDefaultWorkQueueEnvironment(Environment); -} - -/** Returns a pointer to the default shared work queue. */ -PPH_WORK_QUEUE PhGetGlobalWorkQueue( - VOID - ) -{ - if (PhBeginInitOnce(&PhGlobalWorkQueueInitOnce)) - { - PhInitializeWorkQueue( - &PhGlobalWorkQueue, - 0, - 3, - 1000 - ); - PhEndInitOnce(&PhGlobalWorkQueueInitOnce); - } - - return &PhGlobalWorkQueue; -} - -VOID PhpGetDefaultWorkQueueEnvironment( - _Out_ PPH_WORK_QUEUE_ENVIRONMENT Environment - ) -{ - memset(Environment, 0, sizeof(PH_WORK_QUEUE_ENVIRONMENT)); - Environment->BasePriority = 0; - Environment->IoPriority = IoPriorityNormal; - Environment->PagePriority = MEMORY_PRIORITY_NORMAL; - Environment->ForceUpdate = FALSE; -} - -VOID PhpUpdateWorkQueueEnvironment( - _Inout_ PPH_WORK_QUEUE_ENVIRONMENT CurrentEnvironment, - _In_ PPH_WORK_QUEUE_ENVIRONMENT NewEnvironment - ) -{ - if (CurrentEnvironment->BasePriority != NewEnvironment->BasePriority || NewEnvironment->ForceUpdate) - { - LONG increment; - - increment = NewEnvironment->BasePriority; - - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, - &increment, sizeof(LONG)))) - { - CurrentEnvironment->BasePriority = NewEnvironment->BasePriority; - } - } - - if (WindowsVersion >= WINDOWS_VISTA) - { - if (CurrentEnvironment->IoPriority != NewEnvironment->IoPriority || NewEnvironment->ForceUpdate) - { - IO_PRIORITY_HINT ioPriority; - - ioPriority = NewEnvironment->IoPriority; - - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, - &ioPriority, sizeof(IO_PRIORITY_HINT)))) - { - CurrentEnvironment->IoPriority = NewEnvironment->IoPriority; - } - } - - if (CurrentEnvironment->PagePriority != NewEnvironment->PagePriority || NewEnvironment->ForceUpdate) - { - ULONG pagePriority; - - pagePriority = NewEnvironment->PagePriority; - - if (NT_SUCCESS(NtSetInformationThread(NtCurrentThread(), ThreadPagePriority, - &pagePriority, sizeof(ULONG)))) - { - CurrentEnvironment->PagePriority = NewEnvironment->PagePriority; - } - } - } -} - -PPH_WORK_QUEUE_ITEM PhpCreateWorkQueueItem( - _In_ PUSER_THREAD_START_ROUTINE Function, - _In_opt_ PVOID Context, - _In_opt_ PPH_WORK_QUEUE_ITEM_DELETE_FUNCTION DeleteFunction, - _In_opt_ PPH_WORK_QUEUE_ENVIRONMENT Environment - ) -{ - PPH_WORK_QUEUE_ITEM workQueueItem; - - workQueueItem = PhAllocateFromFreeList(&PhWorkQueueItemFreeList); - workQueueItem->Function = Function; - workQueueItem->Context = Context; - workQueueItem->DeleteFunction = DeleteFunction; - - if (Environment) - workQueueItem->Environment = *Environment; - else - PhpGetDefaultWorkQueueEnvironment(&workQueueItem->Environment); - - return workQueueItem; -} - -VOID PhpDestroyWorkQueueItem( - _In_ PPH_WORK_QUEUE_ITEM WorkQueueItem - ) -{ - if (WorkQueueItem->DeleteFunction) - WorkQueueItem->DeleteFunction(WorkQueueItem->Function, WorkQueueItem->Context); - - PhFreeToFreeList(&PhWorkQueueItemFreeList, WorkQueueItem); -} - -VOID PhpExecuteWorkQueueItem( - _Inout_ PPH_WORK_QUEUE_ITEM WorkQueueItem - ) -{ - WorkQueueItem->Function(WorkQueueItem->Context); -} - -HANDLE PhpGetSemaphoreWorkQueue( - _Inout_ PPH_WORK_QUEUE WorkQueue - ) -{ - HANDLE semaphoreHandle; - - semaphoreHandle = WorkQueue->SemaphoreHandle; - - if (!semaphoreHandle) - { - NtCreateSemaphore(&semaphoreHandle, SEMAPHORE_ALL_ACCESS, NULL, 0, MAXLONG); - assert(semaphoreHandle); - - if (_InterlockedCompareExchangePointer( - &WorkQueue->SemaphoreHandle, - semaphoreHandle, - NULL - ) != NULL) - { - // Someone else created the semaphore before we did. - NtClose(semaphoreHandle); - semaphoreHandle = WorkQueue->SemaphoreHandle; - } - } - - return semaphoreHandle; -} - -BOOLEAN PhpCreateWorkQueueThread( - _Inout_ PPH_WORK_QUEUE WorkQueue - ) -{ - HANDLE threadHandle; - - // Make sure the structure doesn't get deleted while the thread is running. - if (!PhAcquireRundownProtection(&WorkQueue->RundownProtect)) - return FALSE; - - threadHandle = PhCreateThread(0, PhpWorkQueueThreadStart, WorkQueue); - - if (threadHandle) - { - PHLIB_INC_STATISTIC(WqWorkQueueThreadsCreated); - WorkQueue->CurrentThreads++; - NtClose(threadHandle); - - return TRUE; - } - else - { - PHLIB_INC_STATISTIC(WqWorkQueueThreadsCreateFailed); - PhReleaseRundownProtection(&WorkQueue->RundownProtect); - return FALSE; - } -} - -NTSTATUS PhpWorkQueueThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - PPH_WORK_QUEUE workQueue = (PPH_WORK_QUEUE)Parameter; - PH_WORK_QUEUE_ENVIRONMENT currentEnvironment; - - PhInitializeAutoPool(&autoPool); - PhpGetDefaultWorkQueueEnvironment(¤tEnvironment); - - while (TRUE) - { - NTSTATUS status; - HANDLE semaphoreHandle; - LARGE_INTEGER timeout; - PPH_WORK_QUEUE_ITEM workQueueItem = NULL; - - // Check if we have more threads than the limit. - if (workQueue->CurrentThreads > workQueue->MaximumThreads) - { - BOOLEAN terminate = FALSE; - - // Lock and re-check. - PhAcquireQueuedLockExclusive(&workQueue->StateLock); - - // Check the minimum as well. - if (workQueue->CurrentThreads > workQueue->MaximumThreads && - workQueue->CurrentThreads > workQueue->MinimumThreads) - { - workQueue->CurrentThreads--; - terminate = TRUE; - } - - PhReleaseQueuedLockExclusive(&workQueue->StateLock); - - if (terminate) - break; - } - - semaphoreHandle = PhpGetSemaphoreWorkQueue(workQueue); - - if (!workQueue->Terminating) - { - // Wait for work. - status = NtWaitForSingleObject( - semaphoreHandle, - FALSE, - PhTimeoutFromMilliseconds(&timeout, workQueue->NoWorkTimeout) - ); - } - else - { - status = STATUS_UNSUCCESSFUL; - } - - if (status == STATUS_WAIT_0 && !workQueue->Terminating) - { - PLIST_ENTRY listEntry; - - // Dequeue the work item. - - PhAcquireQueuedLockExclusive(&workQueue->QueueLock); - - listEntry = RemoveHeadList(&workQueue->QueueListHead); - - if (IsListEmpty(&workQueue->QueueListHead)) - PhPulseCondition(&workQueue->QueueEmptyCondition); - - PhReleaseQueuedLockExclusive(&workQueue->QueueLock); - - // Make sure we got work. - if (listEntry != &workQueue->QueueListHead) - { - workQueueItem = CONTAINING_RECORD(listEntry, PH_WORK_QUEUE_ITEM, ListEntry); - - PhpUpdateWorkQueueEnvironment(¤tEnvironment, &workQueueItem->Environment); - PhpExecuteWorkQueueItem(workQueueItem); - _InterlockedDecrement(&workQueue->BusyCount); - - PhpDestroyWorkQueueItem(workQueueItem); - } - } - else - { - BOOLEAN terminate = FALSE; - - // No work arrived before the timeout passed, or we are terminating, or some error - // occurred. Terminate the thread. - - PhAcquireQueuedLockExclusive(&workQueue->StateLock); - - if (workQueue->Terminating || workQueue->CurrentThreads > workQueue->MinimumThreads) - { - workQueue->CurrentThreads--; - terminate = TRUE; - } - - PhReleaseQueuedLockExclusive(&workQueue->StateLock); - - if (terminate) - break; - } - - PhDrainAutoPool(&autoPool); - } - - PhReleaseRundownProtection(&workQueue->RundownProtect); - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} +/* + * Process Hacker - + * thread pool / work queue + * + * Copyright (C) 2009-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include +#include + +#include + +static PH_INITONCE PhWorkQueueInitOnce = PH_INITONCE_INIT; +static PH_FREE_LIST PhWorkQueueItemFreeList; +static PH_INITONCE PhGlobalWorkQueueInitOnce = PH_INITONCE_INIT; +static PH_WORK_QUEUE PhGlobalWorkQueue; +#ifdef DEBUG +PPH_LIST PhDbgWorkQueueList; +PH_QUEUED_LOCK PhDbgWorkQueueListLock = PH_QUEUED_LOCK_INIT; +#endif + +/** + * Initializes a work queue. + * + * \param WorkQueue A work queue object. + * \param MinimumThreads The suggested minimum number of threads to keep alive, even when there is + * no work to be performed. + * \param MaximumThreads The suggested maximum number of threads to create. + * \param NoWorkTimeout The number of milliseconds after which threads without work will terminate. + */ +VOID PhInitializeWorkQueue( + _Out_ PPH_WORK_QUEUE WorkQueue, + _In_ ULONG MinimumThreads, + _In_ ULONG MaximumThreads, + _In_ ULONG NoWorkTimeout + ) +{ + if (PhBeginInitOnce(&PhWorkQueueInitOnce)) + { + PhInitializeFreeList(&PhWorkQueueItemFreeList, sizeof(PH_WORK_QUEUE_ITEM), 32); +#ifdef DEBUG + PhDbgWorkQueueList = PhCreateList(4); +#endif + + PhEndInitOnce(&PhWorkQueueInitOnce); + } + + PhInitializeRundownProtection(&WorkQueue->RundownProtect); + WorkQueue->Terminating = FALSE; + + InitializeListHead(&WorkQueue->QueueListHead); + PhInitializeQueuedLock(&WorkQueue->QueueLock); + PhInitializeCondition(&WorkQueue->QueueEmptyCondition); + + WorkQueue->MinimumThreads = MinimumThreads; + WorkQueue->MaximumThreads = MaximumThreads; + WorkQueue->NoWorkTimeout = NoWorkTimeout; + + PhInitializeQueuedLock(&WorkQueue->StateLock); + + WorkQueue->SemaphoreHandle = NULL; + WorkQueue->CurrentThreads = 0; + WorkQueue->BusyCount = 0; + +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&PhDbgWorkQueueListLock); + PhAddItemList(PhDbgWorkQueueList, WorkQueue); + PhReleaseQueuedLockExclusive(&PhDbgWorkQueueListLock); +#endif +} + +/** + * Frees resources used by a work queue. + * + * \param WorkQueue A work queue object. + */ +VOID PhDeleteWorkQueue( + _Inout_ PPH_WORK_QUEUE WorkQueue + ) +{ + PLIST_ENTRY listEntry; + PPH_WORK_QUEUE_ITEM workQueueItem; +#ifdef DEBUG + ULONG index; +#endif + +#ifdef DEBUG + PhAcquireQueuedLockExclusive(&PhDbgWorkQueueListLock); + if ((index = PhFindItemList(PhDbgWorkQueueList, WorkQueue)) != ULONG_MAX) + PhRemoveItemList(PhDbgWorkQueueList, index); + PhReleaseQueuedLockExclusive(&PhDbgWorkQueueListLock); +#endif + + // Wait for all worker threads to exit. + + WorkQueue->Terminating = TRUE; + MemoryBarrier(); + + if (WorkQueue->SemaphoreHandle) + NtReleaseSemaphore(WorkQueue->SemaphoreHandle, WorkQueue->CurrentThreads, NULL); + + PhWaitForRundownProtection(&WorkQueue->RundownProtect); + + // Free all un-executed work items. + + listEntry = WorkQueue->QueueListHead.Flink; + + while (listEntry != &WorkQueue->QueueListHead) + { + workQueueItem = CONTAINING_RECORD(listEntry, PH_WORK_QUEUE_ITEM, ListEntry); + listEntry = listEntry->Flink; + PhpDestroyWorkQueueItem(workQueueItem); + } + + if (WorkQueue->SemaphoreHandle) + NtClose(WorkQueue->SemaphoreHandle); +} + +/** + * Waits for all queued work items to be executed. + * + * \param WorkQueue A work queue object. + */ +VOID PhWaitForWorkQueue( + _Inout_ PPH_WORK_QUEUE WorkQueue + ) +{ + PhAcquireQueuedLockExclusive(&WorkQueue->QueueLock); + + while (!IsListEmpty(&WorkQueue->QueueListHead)) + PhWaitForCondition(&WorkQueue->QueueEmptyCondition, &WorkQueue->QueueLock, NULL); + + PhReleaseQueuedLockExclusive(&WorkQueue->QueueLock); +} + +/** + * Queues a work item to a work queue. + * + * \param WorkQueue A work queue object. + * \param Function A function to execute. + * \param Context A user-defined value to pass to the function. + */ +VOID PhQueueItemWorkQueue( + _Inout_ PPH_WORK_QUEUE WorkQueue, + _In_ PUSER_THREAD_START_ROUTINE Function, + _In_opt_ PVOID Context + ) +{ + PhQueueItemWorkQueueEx(WorkQueue, Function, Context, NULL, NULL); +} + +/** + * Queues a work item to a work queue. + * + * \param WorkQueue A work queue object. + * \param Function A function to execute. + * \param Context A user-defined value to pass to the function. + * \param DeleteFunction A callback function that is executed when the work queue item is about to + * be freed. + * \param Environment Execution environment parameters (e.g. priority). + */ +VOID PhQueueItemWorkQueueEx( + _Inout_ PPH_WORK_QUEUE WorkQueue, + _In_ PUSER_THREAD_START_ROUTINE Function, + _In_opt_ PVOID Context, + _In_opt_ PPH_WORK_QUEUE_ITEM_DELETE_FUNCTION DeleteFunction, + _In_opt_ PPH_WORK_QUEUE_ENVIRONMENT Environment + ) +{ + PPH_WORK_QUEUE_ITEM workQueueItem; + + workQueueItem = PhpCreateWorkQueueItem(Function, Context, DeleteFunction, Environment); + + // Enqueue the work item. + PhAcquireQueuedLockExclusive(&WorkQueue->QueueLock); + InsertTailList(&WorkQueue->QueueListHead, &workQueueItem->ListEntry); + _InterlockedIncrement(&WorkQueue->BusyCount); + PhReleaseQueuedLockExclusive(&WorkQueue->QueueLock); + // Signal the semaphore once to let a worker thread continue. + NtReleaseSemaphore(PhpGetSemaphoreWorkQueue(WorkQueue), 1, NULL); + + PHLIB_INC_STATISTIC(WqWorkItemsQueued); + + // Check if all worker threads are currently busy, and if we can create more threads. + if (WorkQueue->BusyCount >= WorkQueue->CurrentThreads && + WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) + { + // Lock and re-check. + PhAcquireQueuedLockExclusive(&WorkQueue->StateLock); + + if (WorkQueue->CurrentThreads < WorkQueue->MaximumThreads) + PhpCreateWorkQueueThread(WorkQueue); + + PhReleaseQueuedLockExclusive(&WorkQueue->StateLock); + } +} + +VOID PhInitializeWorkQueueEnvironment( + _Out_ PPH_WORK_QUEUE_ENVIRONMENT Environment + ) +{ + PhpGetDefaultWorkQueueEnvironment(Environment); +} + +/** Returns a pointer to the default shared work queue. */ +PPH_WORK_QUEUE PhGetGlobalWorkQueue( + VOID + ) +{ + if (PhBeginInitOnce(&PhGlobalWorkQueueInitOnce)) + { + PhInitializeWorkQueue( + &PhGlobalWorkQueue, + 0, + 3, + 1000 + ); + PhEndInitOnce(&PhGlobalWorkQueueInitOnce); + } + + return &PhGlobalWorkQueue; +} + +VOID PhpGetDefaultWorkQueueEnvironment( + _Out_ PPH_WORK_QUEUE_ENVIRONMENT Environment + ) +{ + memset(Environment, 0, sizeof(PH_WORK_QUEUE_ENVIRONMENT)); + Environment->BasePriority = THREAD_PRIORITY_NORMAL; + Environment->IoPriority = IoPriorityNormal; + Environment->PagePriority = MEMORY_PRIORITY_NORMAL; + Environment->ForceUpdate = FALSE; +} + +VOID PhpUpdateWorkQueueEnvironment( + _Inout_ PPH_WORK_QUEUE_ENVIRONMENT CurrentEnvironment, + _In_ PPH_WORK_QUEUE_ENVIRONMENT NewEnvironment + ) +{ + if (CurrentEnvironment->BasePriority != NewEnvironment->BasePriority || NewEnvironment->ForceUpdate) + { + LONG increment; + + increment = NewEnvironment->BasePriority; + + if (NT_SUCCESS(PhSetThreadBasePriority(NtCurrentThread(), increment))) + { + CurrentEnvironment->BasePriority = NewEnvironment->BasePriority; + } + } + + if (CurrentEnvironment->IoPriority != NewEnvironment->IoPriority || NewEnvironment->ForceUpdate) + { + IO_PRIORITY_HINT ioPriority; + + ioPriority = NewEnvironment->IoPriority; + + if (NT_SUCCESS(PhSetThreadIoPriority(NtCurrentThread(), ioPriority))) + { + CurrentEnvironment->IoPriority = NewEnvironment->IoPriority; + } + } + + if (CurrentEnvironment->PagePriority != NewEnvironment->PagePriority || NewEnvironment->ForceUpdate) + { + ULONG pagePriority; + + pagePriority = NewEnvironment->PagePriority; + + if (NT_SUCCESS(PhSetThreadPagePriority(NtCurrentThread(), pagePriority))) + { + CurrentEnvironment->PagePriority = NewEnvironment->PagePriority; + } + } +} + +PPH_WORK_QUEUE_ITEM PhpCreateWorkQueueItem( + _In_ PUSER_THREAD_START_ROUTINE Function, + _In_opt_ PVOID Context, + _In_opt_ PPH_WORK_QUEUE_ITEM_DELETE_FUNCTION DeleteFunction, + _In_opt_ PPH_WORK_QUEUE_ENVIRONMENT Environment + ) +{ + PPH_WORK_QUEUE_ITEM workQueueItem; + + workQueueItem = PhAllocateFromFreeList(&PhWorkQueueItemFreeList); + workQueueItem->Function = Function; + workQueueItem->Context = Context; + workQueueItem->DeleteFunction = DeleteFunction; + + if (Environment) + workQueueItem->Environment = *Environment; + else + PhpGetDefaultWorkQueueEnvironment(&workQueueItem->Environment); + + return workQueueItem; +} + +VOID PhpDestroyWorkQueueItem( + _In_ PPH_WORK_QUEUE_ITEM WorkQueueItem + ) +{ + if (WorkQueueItem->DeleteFunction) + WorkQueueItem->DeleteFunction(WorkQueueItem->Function, WorkQueueItem->Context); + + PhFreeToFreeList(&PhWorkQueueItemFreeList, WorkQueueItem); +} + +VOID PhpExecuteWorkQueueItem( + _Inout_ PPH_WORK_QUEUE_ITEM WorkQueueItem + ) +{ + WorkQueueItem->Function(WorkQueueItem->Context); +} + +HANDLE PhpGetSemaphoreWorkQueue( + _Inout_ PPH_WORK_QUEUE WorkQueue + ) +{ + HANDLE semaphoreHandle; + + semaphoreHandle = WorkQueue->SemaphoreHandle; + + if (!semaphoreHandle) + { + NtCreateSemaphore(&semaphoreHandle, SEMAPHORE_ALL_ACCESS, NULL, 0, MAXLONG); + assert(semaphoreHandle); + + if (_InterlockedCompareExchangePointer( + &WorkQueue->SemaphoreHandle, + semaphoreHandle, + NULL + ) != NULL) + { + // Someone else created the semaphore before we did. + NtClose(semaphoreHandle); + semaphoreHandle = WorkQueue->SemaphoreHandle; + } + } + + return semaphoreHandle; +} + +BOOLEAN PhpCreateWorkQueueThread( + _Inout_ PPH_WORK_QUEUE WorkQueue + ) +{ + HANDLE threadHandle; + + // Make sure the structure doesn't get deleted while the thread is running. + if (!PhAcquireRundownProtection(&WorkQueue->RundownProtect)) + return FALSE; + + if (NT_SUCCESS(PhCreateThreadEx( + &threadHandle, + PhpWorkQueueThreadStart, + WorkQueue + ))) + { + PHLIB_INC_STATISTIC(WqWorkQueueThreadsCreated); + WorkQueue->CurrentThreads++; + + NtClose(threadHandle); + return TRUE; + } + else + { + PHLIB_INC_STATISTIC(WqWorkQueueThreadsCreateFailed); + PhReleaseRundownProtection(&WorkQueue->RundownProtect); + return FALSE; + } +} + +NTSTATUS PhpWorkQueueThreadStart( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + PPH_WORK_QUEUE workQueue = (PPH_WORK_QUEUE)Parameter; + PH_WORK_QUEUE_ENVIRONMENT currentEnvironment; + + PhInitializeAutoPool(&autoPool); + PhpGetDefaultWorkQueueEnvironment(¤tEnvironment); + + while (TRUE) + { + NTSTATUS status; + HANDLE semaphoreHandle; + LARGE_INTEGER timeout; + PPH_WORK_QUEUE_ITEM workQueueItem = NULL; + + // Check if we have more threads than the limit. + if (workQueue->CurrentThreads > workQueue->MaximumThreads) + { + BOOLEAN terminate = FALSE; + + // Lock and re-check. + PhAcquireQueuedLockExclusive(&workQueue->StateLock); + + // Check the minimum as well. + if (workQueue->CurrentThreads > workQueue->MaximumThreads && + workQueue->CurrentThreads > workQueue->MinimumThreads) + { + workQueue->CurrentThreads--; + terminate = TRUE; + } + + PhReleaseQueuedLockExclusive(&workQueue->StateLock); + + if (terminate) + break; + } + + semaphoreHandle = PhpGetSemaphoreWorkQueue(workQueue); + + if (!workQueue->Terminating) + { + // Wait for work. + status = NtWaitForSingleObject( + semaphoreHandle, + FALSE, + PhTimeoutFromMilliseconds(&timeout, workQueue->NoWorkTimeout) + ); + } + else + { + status = STATUS_UNSUCCESSFUL; + } + + if (status == STATUS_WAIT_0 && !workQueue->Terminating) + { + PLIST_ENTRY listEntry; + + // Dequeue the work item. + + PhAcquireQueuedLockExclusive(&workQueue->QueueLock); + + listEntry = RemoveHeadList(&workQueue->QueueListHead); + + if (IsListEmpty(&workQueue->QueueListHead)) + PhPulseCondition(&workQueue->QueueEmptyCondition); + + PhReleaseQueuedLockExclusive(&workQueue->QueueLock); + + // Make sure we got work. + if (listEntry != &workQueue->QueueListHead) + { + workQueueItem = CONTAINING_RECORD(listEntry, PH_WORK_QUEUE_ITEM, ListEntry); + + PhpUpdateWorkQueueEnvironment(¤tEnvironment, &workQueueItem->Environment); + PhpExecuteWorkQueueItem(workQueueItem); + _InterlockedDecrement(&workQueue->BusyCount); + + PhpDestroyWorkQueueItem(workQueueItem); + } + } + else + { + BOOLEAN terminate = FALSE; + + // No work arrived before the timeout passed, or we are terminating, or some error + // occurred. Terminate the thread. + + PhAcquireQueuedLockExclusive(&workQueue->StateLock); + + if (workQueue->Terminating || workQueue->CurrentThreads > workQueue->MinimumThreads) + { + workQueue->CurrentThreads--; + terminate = TRUE; + } + + PhReleaseQueuedLockExclusive(&workQueue->StateLock); + + if (terminate) + break; + } + + PhDrainAutoPool(&autoPool); + } + + PhReleaseRundownProtection(&workQueue->RundownProtect); + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} diff --git a/phlib/wslsup.c b/phlib/wslsup.c new file mode 100644 index 000000000000..7bdd5163bdd0 --- /dev/null +++ b/phlib/wslsup.c @@ -0,0 +1,401 @@ +/* + * Process Hacker - + * LXSS support helpers + * + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +BOOLEAN NTAPI PhpWslDistributionNamesCallback( + _In_ HANDLE RootDirectory, + _In_ PKEY_BASIC_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + PhAddItemList(Context, PhCreateStringEx(Information->Name, Information->NameLength)); + return TRUE; +} + +BOOLEAN PhGetWslDistributionFromPath( + _In_ PPH_STRING FileName, + _Out_opt_ PPH_STRING *LxssDistroName, + _Out_opt_ PPH_STRING *LxssDistroPath, + _Out_opt_ PPH_STRING *LxssFileName + ) +{ + static PH_STRINGREF lxssKeyPath = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Lxss"); + PPH_STRING lxssDistributionName = NULL; + PPH_STRING lxssDistroPath = NULL; + PPH_STRING lxssFileName = NULL; + HANDLE keyHandle; + ULONG i; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_CURRENT_USER, + &lxssKeyPath, + 0 + ))) + { + PPH_LIST distributionGuidList; + + distributionGuidList = PhCreateList(1); + PhEnumerateKey(keyHandle, PhpWslDistributionNamesCallback, distributionGuidList); + + for (i = 0; i < distributionGuidList->Count; i++) + { + HANDLE subKeyHandle; + PPH_STRING subKeyName; + + subKeyName = distributionGuidList->Items[i]; + + if (NT_SUCCESS(PhOpenKey( + &subKeyHandle, + KEY_READ, + keyHandle, + &subKeyName->sr, + 0 + ))) + { + PPH_STRING lxssBasePathName = PhQueryRegistryString(subKeyHandle, L"BasePath"); + + if (PhStartsWithString(FileName, lxssBasePathName, TRUE)) + { + lxssDistributionName = PhQueryRegistryString(subKeyHandle, L"DistributionName"); + + if (LxssDistroPath) + { + PhSetReference(&lxssDistroPath, lxssBasePathName); + } + + if (LxssFileName) + { + lxssFileName = PhDuplicateString(FileName); + PhSkipStringRef(&lxssFileName->sr, lxssBasePathName->Length); + PhSkipStringRef(&lxssFileName->sr, sizeof(L"rootfs")); + } + } + + PhDereferenceObject(lxssBasePathName); + NtClose(subKeyHandle); + } + + if (lxssDistributionName) + break; + } + + PhDereferenceObjects(distributionGuidList->Items, distributionGuidList->Count); + PhDereferenceObject(distributionGuidList); + + NtClose(keyHandle); + } + + if (LxssDistroPath) + { + *LxssDistroPath = lxssDistroPath; + } + + if (LxssFileName) + { + if (lxssFileName) + { + for (i = 0; i < lxssFileName->Length / sizeof(WCHAR); i++) + { + if (lxssFileName->Buffer[i] == OBJ_NAME_PATH_SEPARATOR) // RtlNtPathSeperatorString + lxssFileName->Buffer[i] = L'/'; // RtlAlternateDosPathSeperatorString + } + } + + *LxssFileName = lxssFileName; + } + + if (LxssDistroName) + { + if (lxssDistributionName) + { + *LxssDistroName = lxssDistributionName; + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN PhInitializeLxssImageVersionInfo( + _Out_ PPH_IMAGE_VERSION_INFO ImageVersionInfo, + _In_ PPH_STRING FileName + ) +{ + ULONG status; + PPH_STRING lxssCommandLine = NULL; + PPH_STRING lxssPackageName = NULL; + PPH_STRING lxssDistroName; + PPH_STRING lxssDistroPath; + PPH_STRING lxssFileName; + PPH_STRING result; + + if (!PhGetWslDistributionFromPath( + FileName, + &lxssDistroName, + &lxssDistroPath, + &lxssFileName + )) + { + return FALSE; + } + + if (PhIsNullOrEmptyString(lxssDistroName) || PhIsNullOrEmptyString(lxssDistroPath) || PhIsNullOrEmptyString(lxssFileName)) + { + if (lxssDistroName) PhDereferenceObject(lxssDistroName); + if (lxssDistroPath) PhDereferenceObject(lxssDistroPath); + if (lxssFileName) PhDereferenceObject(lxssFileName); + return FALSE; + } + + PhMoveReference(&lxssCommandLine, PhFormatString( + L"rpm -qf %s --queryformat \"%%{VERSION}|%%{VENDOR}|%%{SUMMARY}\"", + lxssFileName->Buffer + )); + + status = PhCreateProcessLxss( + lxssDistroName->Buffer, + lxssCommandLine->Buffer, + NULL, + &result + ); + + if (status == 0) + { + goto ParseResult; + } + + PhMoveReference(&lxssCommandLine, PhConcatStrings2( + L"dpkg -S ", + lxssFileName->Buffer + )); + + status = PhCreateProcessLxss( + lxssDistroName->Buffer, + lxssCommandLine->Buffer, + NULL, + &result + ); + + if (status != 0) + { + PhDereferenceObject(lxssCommandLine); + PhDereferenceObject(lxssDistroName); + PhDereferenceObject(lxssFileName); + return FALSE; + } + else + { + PH_STRINGREF remainingPart; + PH_STRINGREF packagePart; + + if (PhSplitStringRefAtChar(&result->sr, ':', &packagePart, &remainingPart)) + { + lxssPackageName = PhCreateString2(&packagePart); + } + + PhDereferenceObject(result); + } + + if (PhIsNullOrEmptyString(lxssPackageName)) + { + PhDereferenceObject(lxssCommandLine); + PhDereferenceObject(lxssDistroName); + PhDereferenceObject(lxssFileName); + return FALSE; + } + + PhMoveReference(&lxssCommandLine, PhConcatStrings2( + L"dpkg-query -W -f=${Version}|${Maintainer}|${binary:Summary} ", + lxssPackageName->Buffer + )); + + status = PhCreateProcessLxss( + lxssDistroName->Buffer, + lxssCommandLine->Buffer, + NULL, + &result + ); + + if (status != 0) + { + PhDereferenceObject(lxssCommandLine); + PhDereferenceObject(lxssDistroName); + PhDereferenceObject(lxssFileName); + return FALSE; + } + +ParseResult: + if ( + !ImageVersionInfo->FileVersion && + !ImageVersionInfo->CompanyName && + !ImageVersionInfo->FileDescription + ) + { + PH_STRINGREF remainingPart; + PH_STRINGREF companyPart; + PH_STRINGREF descriptionPart; + PH_STRINGREF versionPart; + + remainingPart = PhGetStringRef(result); + PhSplitStringRefAtChar(&remainingPart, '|', &versionPart, &remainingPart); + PhSplitStringRefAtChar(&remainingPart, '|', &companyPart, &remainingPart); + PhSplitStringRefAtChar(&remainingPart, '|', &descriptionPart, &remainingPart); + + if (versionPart.Length != 0) + ImageVersionInfo->FileVersion = PhCreateString2(&versionPart); + if (companyPart.Length != 0) + ImageVersionInfo->CompanyName = PhCreateString2(&companyPart); + if (descriptionPart.Length != 0) + ImageVersionInfo->FileDescription = PhCreateString2(&descriptionPart); + } + + if (result) PhDereferenceObject(result); + if (lxssCommandLine) PhDereferenceObject(lxssCommandLine); + if (lxssPackageName) PhDereferenceObject(lxssPackageName); + if (lxssDistroName) PhDereferenceObject(lxssDistroName); + if (lxssFileName) PhDereferenceObject(lxssFileName); + + return TRUE; +} + +ULONG PhCreateProcessLxss( + _In_ PWSTR LxssDistribution, + _In_ PWSTR LxssCommandLine, + _In_opt_ PWSTR LxssCurrentDirectory, + _Out_ PPH_STRING *Result + ) +{ + NTSTATUS status; + PPH_STRING lxssOutputString; + PPH_STRING lxssCommandLine; + PPH_STRING systemDirectory; + HANDLE processHandle; + HANDLE outputReadHandle, outputWriteHandle; + HANDLE inputReadHandle, inputWriteHandle; + PROCESS_BASIC_INFORMATION basicInfo; + STARTUPINFO startupInfo; + + lxssCommandLine = PhFormatString( + L"wsl.exe -d %s -e %s", + LxssDistribution, + LxssCommandLine + ); + + if (systemDirectory = PhGetSystemDirectory()) + { + PhMoveReference(&lxssCommandLine, PhConcatStrings( + 3, + systemDirectory->Buffer, + L"\\", + lxssCommandLine->Buffer + )); + PhDereferenceObject(systemDirectory); + } + + status = PhCreatePipeEx( + &outputReadHandle, + &outputWriteHandle, + TRUE, + NULL + ); + + if (!NT_SUCCESS(status)) + return status; + + status = PhCreatePipeEx( + &inputReadHandle, + &inputWriteHandle, + TRUE, + NULL + ); + + if (!NT_SUCCESS(status)) + { + NtClose(outputWriteHandle); + NtClose(outputReadHandle); + return status; + } + + memset(&startupInfo, 0, sizeof(STARTUPINFO)); + startupInfo.cb = sizeof(STARTUPINFO); + startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + startupInfo.wShowWindow = SW_HIDE; + startupInfo.hStdInput = inputReadHandle; + startupInfo.hStdOutput = outputWriteHandle; + startupInfo.hStdError = outputWriteHandle; + + status = PhCreateProcessWin32Ex( + NULL, + lxssCommandLine->Buffer, + NULL, + LxssCurrentDirectory, + &startupInfo, + PH_CREATE_PROCESS_INHERIT_HANDLES | PH_CREATE_PROCESS_NEW_CONSOLE, + NULL, + NULL, + &processHandle, + NULL + ); + + if (!NT_SUCCESS(status)) + { + NtClose(outputWriteHandle); + NtClose(outputReadHandle); + NtClose(inputReadHandle); + NtClose(inputWriteHandle); + return status; + } + + // Note: Close the write handles or the child process + // won't exit and ReadFile will block indefinitely. (dmex) + NtClose(inputReadHandle); + NtClose(outputWriteHandle); + + // Read the pipe data. (dmex) + lxssOutputString = PhGetFileText(outputReadHandle); + + // Get the exit code after we finish reading the data from the pipe. (dmex) + if (NT_SUCCESS(status = PhGetProcessBasicInformation(processHandle, &basicInfo))) + { + status = basicInfo.ExitStatus; + } + + // Note: Don't use NTSTATUS now that we have the lxss exit code. (dmex) + if (status == 0) + { + *Result = lxssOutputString; + } + else + { + PhSetReference(&lxssOutputString, NULL); + } + + NtClose(processHandle); + NtClose(outputReadHandle); + NtClose(inputWriteHandle); + + return status; +} diff --git a/phnt/include/ntdbg.h b/phnt/include/ntdbg.h index 2be47c2263d4..cc46c761c063 100644 --- a/phnt/include/ntdbg.h +++ b/phnt/include/ntdbg.h @@ -1,6 +1,122 @@ +/* + * Process Hacker - + * Debugger support functions + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTDBG_H #define _NTDBG_H +// Debugging + +NTSYSAPI +VOID +NTAPI +DbgUserBreakPoint( + VOID + ); + +NTSYSAPI +VOID +NTAPI +DbgBreakPoint( + VOID + ); + +NTSYSAPI +VOID +NTAPI +DbgBreakPointWithStatus( + _In_ ULONG Status + ); + +#define DBG_STATUS_CONTROL_C 1 +#define DBG_STATUS_SYSRQ 2 +#define DBG_STATUS_BUGCHECK_FIRST 3 +#define DBG_STATUS_BUGCHECK_SECOND 4 +#define DBG_STATUS_FATAL 5 +#define DBG_STATUS_DEBUG_CONTROL 6 +#define DBG_STATUS_WORKER 7 + +NTSYSAPI +ULONG +STDAPIVCALLTYPE +DbgPrint( + _In_z_ _Printf_format_string_ PSTR Format, + ... + ); + +NTSYSAPI +ULONG +STDAPIVCALLTYPE +DbgPrintEx( + _In_ ULONG ComponentId, + _In_ ULONG Level, + _In_z_ _Printf_format_string_ PSTR Format, + ... + ); + +NTSYSAPI +ULONG +NTAPI +vDbgPrintEx( + _In_ ULONG ComponentId, + _In_ ULONG Level, + _In_z_ PCH Format, + _In_ va_list arglist + ); + +NTSYSAPI +ULONG +NTAPI +vDbgPrintExWithPrefix( + _In_z_ PCH Prefix, + _In_ ULONG ComponentId, + _In_ ULONG Level, + _In_z_ PCH Format, + _In_ va_list arglist + ); + +NTSYSAPI +NTSTATUS +NTAPI +DbgQueryDebugFilterState( + _In_ ULONG ComponentId, + _In_ ULONG Level + ); + +NTSYSAPI +NTSTATUS +NTAPI +DbgSetDebugFilterState( + _In_ ULONG ComponentId, + _In_ ULONG Level, + _In_ BOOLEAN State + ); + +NTSYSAPI +ULONG +NTAPI +DbgPrompt( + _In_ PCH Prompt, + _Out_writes_bytes_(Length) PCH Response, + _In_ ULONG Length + ); + // Definitions typedef struct _DBGKM_EXCEPTION @@ -93,8 +209,6 @@ typedef struct _DBGUI_WAIT_STATE_CHANGE } StateInfo; } DBGUI_WAIT_STATE_CHANGE, *PDBGUI_WAIT_STATE_CHANGE; -// System calls - #define DEBUG_READ_EVENT 0x0001 #define DEBUG_PROCESS_ASSIGN 0x0002 #define DEBUG_SET_INFORMATION 0x0004 @@ -107,10 +221,13 @@ typedef struct _DBGUI_WAIT_STATE_CHANGE typedef enum _DEBUGOBJECTINFOCLASS { - DebugObjectFlags = 1, + DebugObjectUnusedInformation, + DebugObjectKillProcessOnExitInformation, MaxDebugObjectInfoClass } DEBUGOBJECTINFOCLASS, *PDEBUGOBJECTINFOCLASS; +// System calls + NTSYSCALLAPI NTSTATUS NTAPI @@ -234,14 +351,36 @@ DbgUiIssueRemoteBreakin( _In_ HANDLE Process ); -struct _DEBUG_EVENT; - NTSYSAPI NTSTATUS NTAPI DbgUiConvertStateChangeStructure( _In_ PDBGUI_WAIT_STATE_CHANGE StateChange, - _Out_ struct _DEBUG_EVENT *DebugEvent + _Out_ LPDEBUG_EVENT DebugEvent + ); + +struct _EVENT_FILTER_DESCRIPTOR; + +typedef VOID (NTAPI *PENABLECALLBACK)( + _In_ LPCGUID SourceId, + _In_ ULONG IsEnabled, + _In_ UCHAR Level, + _In_ ULONGLONG MatchAnyKeyword, + _In_ ULONGLONG MatchAllKeyword, + _In_opt_ struct _EVENT_FILTER_DESCRIPTOR *FilterData, + _Inout_opt_ PVOID CallbackContext + ); + +typedef ULONGLONG REGHANDLE, *PREGHANDLE; + +NTSYSAPI +NTSTATUS +NTAPI +EtwEventRegister( + _In_ LPCGUID ProviderId, + _In_opt_ PENABLECALLBACK EnableCallback, + _In_opt_ PVOID CallbackContext, + _Out_ PREGHANDLE RegHandle ); #endif diff --git a/phnt/include/ntexapi.h b/phnt/include/ntexapi.h index 8fad61a03a9d..43c9ef517039 100644 --- a/phnt/include/ntexapi.h +++ b/phnt/include/ntexapi.h @@ -1,3 +1,23 @@ +/* + * Process Hacker - + * Executive support library functions + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTEXAPI_H #define _NTEXAPI_H @@ -12,7 +32,7 @@ NTSTATUS NTAPI NtDelayExecution( _In_ BOOLEAN Alertable, - _In_ PLARGE_INTEGER DelayInterval + _In_opt_ PLARGE_INTEGER DelayInterval ); // Environment values @@ -245,6 +265,27 @@ NtSetDriverEntryOrder( _In_ ULONG Count ); +typedef enum _FILTER_BOOT_OPTION_OPERATION +{ + FilterBootOptionOperationOpenSystemStore, + FilterBootOptionOperationSetElement, + FilterBootOptionOperationDeleteElement, + FilterBootOptionOperationMax +} FILTER_BOOT_OPTION_OPERATION; + +#if (PHNT_VERSION >= PHNT_THRESHOLD) +NTSYSCALLAPI +NTSTATUS +NTAPI +NtFilterBootOption( + _In_ FILTER_BOOT_OPTION_OPERATION FilterOperation, + _In_ ULONG ObjectType, + _In_ ULONG ElementType, + _In_reads_bytes_opt_(DataSize) PVOID Data, + _In_ ULONG DataSize + ); +#endif + #endif // Event @@ -833,7 +874,8 @@ typedef enum _WNF_DATA_SCOPE WnfDataScopeSystem, WnfDataScopeSession, WnfDataScopeUser, - WnfDataScopeProcess + WnfDataScopeProcess, + WnfDataScopeMachine // REDSTONE3 } WNF_DATA_SCOPE; typedef struct _WNF_TYPE_ID @@ -994,14 +1036,14 @@ NtSetWnfProcessNotificationEvent( typedef enum _WORKERFACTORYINFOCLASS { - WorkerFactoryTimeout, - WorkerFactoryRetryTimeout, - WorkerFactoryIdleTimeout, + WorkerFactoryTimeout, // q; s: LARGE_INTEGER + WorkerFactoryRetryTimeout, // q; s: LARGE_INTEGER + WorkerFactoryIdleTimeout, // q; s: LARGE_INTEGER WorkerFactoryBindingCount, - WorkerFactoryThreadMinimum, - WorkerFactoryThreadMaximum, - WorkerFactoryPaused, - WorkerFactoryBasicInformation, + WorkerFactoryThreadMinimum, // q; s: ULONG + WorkerFactoryThreadMaximum, // q; s: ULONG + WorkerFactoryPaused, // ULONG or BOOLEAN + WorkerFactoryBasicInformation, // WORKER_FACTORY_BASIC_INFORMATION WorkerFactoryAdjustThreadGoal, WorkerFactoryCallbackType, WorkerFactoryStackInformation, // 10 @@ -1009,6 +1051,7 @@ typedef enum _WORKERFACTORYINFOCLASS WorkerFactoryTimeoutWaiters, // since THRESHOLD WorkerFactoryFlags, WorkerFactoryThreadSoftMaximum, + WorkerFactoryThreadCpuSets, // since REDSTONE5 MaxWorkerFactoryInfoClass } WORKERFACTORYINFOCLASS, *PWORKERFACTORYINFOCLASS; @@ -1194,7 +1237,6 @@ NtAllocateUuids( // rev // private -// source:http://www.microsoft.com/whdc/system/Sysinternals/MoreThan64proc.mspx typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, // q: SYSTEM_BASIC_INFORMATION @@ -1209,8 +1251,8 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemFlagsInformation, // q: SYSTEM_FLAGS_INFORMATION SystemCallTimeInformation, // not implemented // SYSTEM_CALL_TIME_INFORMATION // 10 SystemModuleInformation, // q: RTL_PROCESS_MODULES - SystemLocksInformation, // q: SYSTEM_LOCK_INFORMATION - SystemStackTraceInformation, + SystemLocksInformation, // q: RTL_PROCESS_LOCKS + SystemStackTraceInformation, // q: RTL_PROCESS_BACKTRACES SystemPagedPoolInformation, // not implemented SystemNonPagedPoolInformation, // not implemented SystemHandleInformation, // q: SYSTEM_HANDLE_INFORMATION @@ -1228,7 +1270,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemTimeAdjustmentInformation, // q: SYSTEM_QUERY_TIME_ADJUST_INFORMATION; s: SYSTEM_SET_TIME_ADJUST_INFORMATION (requires SeSystemtimePrivilege) SystemSummaryMemoryInformation, // not implemented SystemMirrorMemoryInformation, // s (requires license value "Kernel-MemoryMirroringSupported") (requires SeShutdownPrivilege) // 30 - SystemPerformanceTraceInformation, // s + SystemPerformanceTraceInformation, // q; s: (type depends on EVENT_TRACE_INFORMATION_CLASS) SystemObsolete0, // not implemented SystemExceptionInformation, // q: SYSTEM_EXCEPTION_INFORMATION SystemCrashDumpStateInformation, // s (requires SeDebugPrivilege) @@ -1241,12 +1283,12 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemVerifierRemoveDriverInformation, // s (requires SeDebugPrivilege) SystemProcessorIdleInformation, // q: SYSTEM_PROCESSOR_IDLE_INFORMATION SystemLegacyDriverInformation, // q: SYSTEM_LEGACY_DRIVER_INFORMATION - SystemCurrentTimeZoneInformation, // q + SystemCurrentTimeZoneInformation, // q; s: RTL_TIME_ZONE_INFORMATION SystemLookasideInformation, // q: SYSTEM_LOOKASIDE_INFORMATION SystemTimeSlipNotification, // s (requires SeSystemtimePrivilege) SystemSessionCreate, // not implemented SystemSessionDetach, // not implemented - SystemSessionInformation, // not implemented + SystemSessionInformation, // not implemented (SYSTEM_SESSION_INFORMATION) SystemRangeStartInformation, // q: SYSTEM_RANGE_START_INFORMATION // 50 SystemVerifierInformation, // q: SYSTEM_VERIFIER_INFORMATION; s (requires SeDebugPrivilege) SystemVerifierThunkExtend, // s (kernel-mode only) @@ -1259,15 +1301,15 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemComPlusPackage, // q; s SystemNumaAvailableMemory, // 60 SystemProcessorPowerInformation, // q: SYSTEM_PROCESSOR_POWER_INFORMATION - SystemEmulationBasicInformation, // q + SystemEmulationBasicInformation, SystemEmulationProcessorInformation, SystemExtendedHandleInformation, // q: SYSTEM_HANDLE_INFORMATION_EX SystemLostDelayedWriteInformation, // q: ULONG SystemBigPoolInformation, // q: SYSTEM_BIGPOOL_INFORMATION SystemSessionPoolTagInformation, // q: SYSTEM_SESSION_POOLTAG_INFORMATION SystemSessionMappedViewInformation, // q: SYSTEM_SESSION_MAPPED_VIEW_INFORMATION - SystemHotpatchInformation, // q; s - SystemObjectSecurityMode, // q // 70 + SystemHotpatchInformation, // q; s: SYSTEM_HOTPATCH_CODE_INFORMATION + SystemObjectSecurityMode, // q: ULONG // 70 SystemWatchdogTimerHandler, // s (kernel-mode only) SystemWatchdogTimerInformation, // q (kernel-mode only); s (kernel-mode only) SystemLogicalProcessorInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION @@ -1298,7 +1340,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemSystemPartitionInformation, // q: SYSTEM_SYSTEM_PARTITION_INFORMATION SystemSystemDiskInformation, // q: SYSTEM_SYSTEM_DISK_INFORMATION SystemProcessorPerformanceDistribution, // q: SYSTEM_PROCESSOR_PERFORMANCE_DISTRIBUTION // 100 - SystemNumaProximityNodeInformation, // q + SystemNumaProximityNodeInformation, SystemDynamicTimeZoneInformation, // q; s (requires SeTimeZonePrivilege) SystemCodeIntegrityInformation, // q: SYSTEM_CODEINTEGRITY_INFORMATION // SeCodeIntegrityQueryInformation SystemProcessorMicrocodeUpdateInformation, // s @@ -1306,7 +1348,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemVirtualAddressInformation, // q: SYSTEM_VA_LIST_INFORMATION[]; s: SYSTEM_VA_LIST_INFORMATION[] (requires SeIncreaseQuotaPrivilege) // MmQuerySystemVaInformation SystemLogicalProcessorAndGroupInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX // since WIN7 // KeQueryLogicalProcessorRelationship SystemProcessorCycleTimeInformation, // q: SYSTEM_PROCESSOR_CYCLE_TIME_INFORMATION[] - SystemStoreInformation, // q; s // SmQueryStoreInformation + SystemStoreInformation, // q; s: SYSTEM_STORE_INFORMATION // SmQueryStoreInformation SystemRegistryAppendString, // s: SYSTEM_REGISTRY_APPEND_STRING_PARAMETERS // 110 SystemAitSamplingValue, // s: ULONG (requires SeProfileSingleProcessPrivilege) SystemVhdBootInformation, // q: SYSTEM_VHD_BOOT_INFORMATION @@ -1318,16 +1360,16 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemVerifierCountersInformation, // q: SYSTEM_VERIFIER_COUNTERS_INFORMATION SystemPagedPoolInformationEx, // q: SYSTEM_FILECACHE_INFORMATION; s (requires SeIncreaseQuotaPrivilege) (info for WorkingSetTypePagedPool) SystemSystemPtesInformationEx, // q: SYSTEM_FILECACHE_INFORMATION; s (requires SeIncreaseQuotaPrivilege) (info for WorkingSetTypeSystemPtes) // 120 - SystemNodeDistanceInformation, // q + SystemNodeDistanceInformation, SystemAcpiAuditInformation, // q: SYSTEM_ACPI_AUDIT_INFORMATION // HaliQuerySystemInformation -> HalpAuditQueryResults, info class 26 SystemBasicPerformanceInformation, // q: SYSTEM_BASIC_PERFORMANCE_INFORMATION // name:wow64:whNtQuerySystemInformation_SystemBasicPerformanceInformation SystemQueryPerformanceCounterInformation, // q: SYSTEM_QUERY_PERFORMANCE_COUNTER_INFORMATION // since WIN7 SP1 SystemSessionBigPoolInformation, // q: SYSTEM_SESSION_POOLTAG_INFORMATION // since WIN8 SystemBootGraphicsInformation, // q; s: SYSTEM_BOOT_GRAPHICS_INFORMATION (kernel-mode only) - SystemScrubPhysicalMemoryInformation, + SystemScrubPhysicalMemoryInformation, // q; s: MEMORY_SCRUB_INFORMATION SystemBadPageInformation, - SystemProcessorProfileControlArea, - SystemCombinePhysicalMemoryInformation, // 130 + SystemProcessorProfileControlArea, // q; s: SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA + SystemCombinePhysicalMemoryInformation, // s: MEMORY_COMBINE_INFORMATION, MEMORY_COMBINE_INFORMATION_EX, MEMORY_COMBINE_INFORMATION_EX2 // 130 SystemEntropyInterruptTimingCallback, SystemConsoleInformation, // q: SYSTEM_CONSOLE_INFORMATION SystemPlatformBinaryInformation, // q: SYSTEM_PLATFORM_BINARY_INFORMATION @@ -1348,7 +1390,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemFullProcessInformation, // q: SYSTEM_PROCESS_INFORMATION with SYSTEM_PROCESS_INFORMATION_EXTENSION (requires admin) SystemKernelDebuggerInformationEx, // q: SYSTEM_KERNEL_DEBUGGER_INFORMATION_EX SystemBootMetadataInformation, // 150 - SystemSoftRebootInformation, + SystemSoftRebootInformation, // q: ULONG SystemElamCertificateInformation, // s: SYSTEM_ELAM_CERTIFICATE_INFORMATION SystemOfflineDumpConfigInformation, SystemProcessorFeaturesInformation, // q: SYSTEM_PROCESSOR_FEATURES_INFORMATION @@ -1360,13 +1402,13 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemProcessorCycleStatsInformation, // q: SYSTEM_PROCESSOR_CYCLE_STATS_INFORMATION // 160 SystemVmGenerationCountInformation, SystemTrustedPlatformModuleInformation, // q: SYSTEM_TPM_INFORMATION - SystemKernelDebuggerFlags, + SystemKernelDebuggerFlags, // SYSTEM_KERNEL_DEBUGGER_FLAGS SystemCodeIntegrityPolicyInformation, // q: SYSTEM_CODEINTEGRITYPOLICY_INFORMATION SystemIsolatedUserModeInformation, // q: SYSTEM_ISOLATED_USER_MODE_INFORMATION SystemHardwareSecurityTestInterfaceResultsInformation, SystemSingleModuleInformation, // q: SYSTEM_SINGLE_MODULE_INFORMATION SystemAllowedCpuSetsInformation, - SystemDmaProtectionInformation, // q: SYSTEM_DMA_PROTECTION_INFORMATION + SystemVsmProtectionInformation, // q: SYSTEM_VSM_PROTECTION_INFORMATION (previously SystemDmaProtectionInformation) SystemInterruptCpuSetsInformation, // q: SYSTEM_INTERRUPT_CPU_SET_INFORMATION // 170 SystemSecureBootPolicyFullInformation, // q: SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION SystemCodeIntegrityPolicyFullInformation, @@ -1383,13 +1425,30 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemCodeIntegrityCertificateInformation, // q: SYSTEM_CODEINTEGRITY_CERTIFICATE_INFORMATION SystemPhysicalMemoryInformation, // q: SYSTEM_PHYSICAL_MEMORY_INFORMATION // since REDSTONE2 SystemControlFlowTransition, - SystemKernelDebuggingAllowed, - SystemActivityModerationExeState, - SystemActivityModerationUserSettings, + SystemKernelDebuggingAllowed, // s: ULONG + SystemActivityModerationExeState, // SYSTEM_ACTIVITY_MODERATION_EXE_STATE + SystemActivityModerationUserSettings, // SYSTEM_ACTIVITY_MODERATION_USER_SETTINGS SystemCodeIntegrityPoliciesFullInformation, - SystemCodeIntegrityUnlockInformation, // 190 + SystemCodeIntegrityUnlockInformation, // SYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION // 190 SystemIntegrityQuotaInformation, SystemFlushInformation, // q: SYSTEM_FLUSH_INFORMATION + SystemProcessorIdleMaskInformation, // q: ULONG_PTR // since REDSTONE3 + SystemSecureDumpEncryptionInformation, + SystemWriteConstraintInformation, // SYSTEM_WRITE_CONSTRAINT_INFORMATION + SystemKernelVaShadowInformation, // SYSTEM_KERNEL_VA_SHADOW_INFORMATION + SystemHypervisorSharedPageInformation, // SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION // since REDSTONE4 + SystemFirmwareBootPerformanceInformation, + SystemCodeIntegrityVerificationInformation, // SYSTEM_CODEINTEGRITYVERIFICATION_INFORMATION + SystemFirmwarePartitionInformation, // SYSTEM_FIRMWARE_PARTITION_INFORMATION // 200 + SystemSpeculationControlInformation, // SYSTEM_SPECULATION_CONTROL_INFORMATION // (CVE-2017-5715) REDSTONE3 and above. + SystemDmaGuardPolicyInformation, // SYSTEM_DMA_GUARD_POLICY_INFORMATION + SystemEnclaveLaunchControlInformation, // SYSTEM_ENCLAVE_LAUNCH_CONTROL_INFORMATION + SystemWorkloadAllowedCpuSetsInformation, // SYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION // since REDSTONE5 + SystemCodeIntegrityUnlockModeInformation, + SystemLeapSecondInformation, // SYSTEM_LEAP_SECOND_INFORMATION + SystemFlags2Information, // q: SYSTEM_FLAGS_INFORMATION + SystemSecurityModelInformation, // SYSTEM_SECURITY_MODEL_INFORMATION // since 19H1 + SystemCodeIntegritySyntheticCacheInformation, MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS; @@ -1413,7 +1472,7 @@ typedef struct _SYSTEM_PROCESSOR_INFORMATION USHORT ProcessorArchitecture; USHORT ProcessorLevel; USHORT ProcessorRevision; - USHORT ProcessorCount; + USHORT MaximumProcessors; ULONG ProcessorFeatureBits; } SYSTEM_PROCESSOR_INFORMATION, *PSYSTEM_PROCESSOR_INFORMATION; @@ -1521,7 +1580,7 @@ typedef struct _SYSTEM_THREAD_INFORMATION KPRIORITY Priority; LONG BasePriority; ULONG ContextSwitches; - ULONG ThreadState; + KTHREAD_STATE ThreadState; KWAIT_REASON WaitReason; } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; @@ -1576,7 +1635,9 @@ typedef struct _SYSTEM_PROCESS_INFORMATION LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; - SYSTEM_THREAD_INFORMATION Threads[1]; + SYSTEM_THREAD_INFORMATION Threads[1]; // SystemProcessInformation + // SYSTEM_EXTENDED_THREAD_INFORMATION Threads[1]; // SystemExtendedProcessinformation + // SYSTEM_EXTENDED_THREAD_INFORMATION + SYSTEM_PROCESS_INFORMATION_EXTENSION // SystemFullProcessInformation } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef struct _SYSTEM_CALL_COUNT_INFORMATION @@ -1619,30 +1680,46 @@ typedef struct _SYSTEM_CALL_TIME_INFORMATION } SYSTEM_CALL_TIME_INFORMATION, *PSYSTEM_CALL_TIME_INFORMATION; // private -typedef struct _SYSTEM_LOCK_TABLE_ENTRY_INFO +typedef struct _RTL_PROCESS_LOCK_INFORMATION { PVOID Address; USHORT Type; - USHORT Reserved1; - ULONG ExclusiveOwnerThreadId; - ULONG ActiveCount; USHORT CreatorBackTraceIndex; HANDLE OwningThread; LONG LockCount; ULONG ContentionCount; - ULONG Reserved2[2]; ULONG EntryCount; LONG RecursionCount; - ULONG NumberOfSharedWaiters; - ULONG NumberOfExclusiveWaiters; -} SYSTEM_LOCK_TABLE_ENTRY_INFO, *PSYSTEM_LOCK_TABLE_ENTRY_INFO; + ULONG NumberOfWaitingShared; + ULONG NumberOfWaitingExclusive; +} RTL_PROCESS_LOCK_INFORMATION, *PRTL_PROCESS_LOCK_INFORMATION; // private -typedef struct _SYSTEM_LOCK_INFORMATION +typedef struct _RTL_PROCESS_LOCKS { - ULONG Count; - SYSTEM_LOCK_TABLE_ENTRY_INFO Locks[1]; -} SYSTEM_LOCK_INFORMATION, *PSYSTEM_LOCK_INFORMATION; + ULONG NumberOfLocks; + RTL_PROCESS_LOCK_INFORMATION Locks[1]; +} RTL_PROCESS_LOCKS, *PRTL_PROCESS_LOCKS; + +// private +typedef struct _RTL_PROCESS_BACKTRACE_INFORMATION +{ + PCHAR SymbolicBackTrace; + ULONG TraceCount; + USHORT Index; + USHORT Depth; + PVOID BackTrace[32]; +} RTL_PROCESS_BACKTRACE_INFORMATION, *PRTL_PROCESS_BACKTRACE_INFORMATION; + +// private +typedef struct _RTL_PROCESS_BACKTRACES +{ + ULONG CommittedMemory; + ULONG ReservedMemory; + ULONG NumberOfBackTraceLookups; + ULONG NumberOfBackTraces; + RTL_PROCESS_BACKTRACE_INFORMATION BackTraces[1]; +} RTL_PROCESS_BACKTRACES, *PRTL_PROCESS_BACKTRACES; typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { @@ -1774,12 +1851,203 @@ typedef struct _SYSTEM_QUERY_TIME_ADJUST_INFORMATION BOOLEAN Enable; } SYSTEM_QUERY_TIME_ADJUST_INFORMATION, *PSYSTEM_QUERY_TIME_ADJUST_INFORMATION; +typedef struct _SYSTEM_QUERY_TIME_ADJUST_INFORMATION_PRECISE +{ + ULONGLONG TimeAdjustment; + ULONGLONG TimeIncrement; + BOOLEAN Enable; +} SYSTEM_QUERY_TIME_ADJUST_INFORMATION_PRECISE, *PSYSTEM_QUERY_TIME_ADJUST_INFORMATION_PRECISE; + typedef struct _SYSTEM_SET_TIME_ADJUST_INFORMATION { ULONG TimeAdjustment; BOOLEAN Enable; } SYSTEM_SET_TIME_ADJUST_INFORMATION, *PSYSTEM_SET_TIME_ADJUST_INFORMATION; +typedef struct _SYSTEM_SET_TIME_ADJUST_INFORMATION_PRECISE +{ + ULONGLONG TimeAdjustment; + BOOLEAN Enable; +} SYSTEM_SET_TIME_ADJUST_INFORMATION_PRECISE, *PSYSTEM_SET_TIME_ADJUST_INFORMATION_PRECISE; + +typedef enum _EVENT_TRACE_INFORMATION_CLASS +{ + EventTraceKernelVersionInformation, // EVENT_TRACE_VERSION_INFORMATION + EventTraceGroupMaskInformation, // EVENT_TRACE_GROUPMASK_INFORMATION + EventTracePerformanceInformation, // EVENT_TRACE_PERFORMANCE_INFORMATION + EventTraceTimeProfileInformation, // EVENT_TRACE_TIME_PROFILE_INFORMATION + EventTraceSessionSecurityInformation, // EVENT_TRACE_SESSION_SECURITY_INFORMATION + EventTraceSpinlockInformation, // EVENT_TRACE_SPINLOCK_INFORMATION + EventTraceStackTracingInformation, // EVENT_TRACE_SYSTEM_EVENT_INFORMATION + EventTraceExecutiveResourceInformation, // EVENT_TRACE_EXECUTIVE_RESOURCE_INFORMATION + EventTraceHeapTracingInformation, // EVENT_TRACE_HEAP_TRACING_INFORMATION + EventTraceHeapSummaryTracingInformation, // EVENT_TRACE_HEAP_TRACING_INFORMATION + EventTracePoolTagFilterInformation, // EVENT_TRACE_TAG_FILTER_INFORMATION + EventTracePebsTracingInformation, // EVENT_TRACE_SYSTEM_EVENT_INFORMATION + EventTraceProfileConfigInformation, // EVENT_TRACE_PROFILE_COUNTER_INFORMATION + EventTraceProfileSourceListInformation, // EVENT_TRACE_PROFILE_LIST_INFORMATION + EventTraceProfileEventListInformation, // EVENT_TRACE_SYSTEM_EVENT_INFORMATION + EventTraceProfileCounterListInformation, // EVENT_TRACE_PROFILE_COUNTER_INFORMATION + EventTraceStackCachingInformation, // EVENT_TRACE_STACK_CACHING_INFORMATION + EventTraceObjectTypeFilterInformation, // EVENT_TRACE_TAG_FILTER_INFORMATION + EventTraceSoftRestartInformation, // EVENT_TRACE_SOFT_RESTART_INFORMATION + EventTraceLastBranchConfigurationInformation, // REDSTONE3 + EventTraceLastBranchEventListInformation, + EventTraceProfileSourceAddInformation, // EVENT_TRACE_PROFILE_ADD_INFORMATION // REDSTONE4 + EventTraceProfileSourceRemoveInformation, // EVENT_TRACE_PROFILE_REMOVE_INFORMATION + EventTraceProcessorTraceConfigurationInformation, + EventTraceProcessorTraceEventListInformation, + EventTraceCoverageSamplerInformation, // EVENT_TRACE_COVERAGE_SAMPLER_INFORMATION + MaxEventTraceInfoClass +} EVENT_TRACE_INFORMATION_CLASS; + +typedef struct _EVENT_TRACE_VERSION_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG EventTraceKernelVersion; +} EVENT_TRACE_VERSION_INFORMATION, *PEVENT_TRACE_VERSION_INFORMATION; + +typedef struct _PERFINFO_GROUPMASK +{ + ULONG Masks[8]; +} PERFINFO_GROUPMASK, *PPERFINFO_GROUPMASK; + +typedef struct _EVENT_TRACE_GROUPMASK_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + HANDLE TraceHandle; + PERFINFO_GROUPMASK EventTraceGroupMasks; +} EVENT_TRACE_GROUPMASK_INFORMATION, *PEVENT_TRACE_GROUPMASK_INFORMATION; + +typedef struct _EVENT_TRACE_PERFORMANCE_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + LARGE_INTEGER LogfileBytesWritten; +} EVENT_TRACE_PERFORMANCE_INFORMATION, *PEVENT_TRACE_PERFORMANCE_INFORMATION; + +typedef struct _EVENT_TRACE_TIME_PROFILE_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG ProfileInterval; +} EVENT_TRACE_TIME_PROFILE_INFORMATION, *PEVENT_TRACE_TIME_PROFILE_INFORMATION; + +typedef struct _EVENT_TRACE_SESSION_SECURITY_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG SecurityInformation; + HANDLE TraceHandle; + UCHAR SecurityDescriptor[1]; +} EVENT_TRACE_SESSION_SECURITY_INFORMATION, *PEVENT_TRACE_SESSION_SECURITY_INFORMATION; + +typedef struct _EVENT_TRACE_SPINLOCK_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG SpinLockSpinThreshold; + ULONG SpinLockAcquireSampleRate; + ULONG SpinLockContentionSampleRate; + ULONG SpinLockHoldThreshold; +} EVENT_TRACE_SPINLOCK_INFORMATION, *PEVENT_TRACE_SPINLOCK_INFORMATION; + +typedef struct _EVENT_TRACE_SYSTEM_EVENT_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + HANDLE TraceHandle; + ULONG HookId[1]; +} EVENT_TRACE_SYSTEM_EVENT_INFORMATION, *PEVENT_TRACE_SYSTEM_EVENT_INFORMATION; + +typedef struct _EVENT_TRACE_EXECUTIVE_RESOURCE_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG ReleaseSamplingRate; + ULONG ContentionSamplingRate; + ULONG NumberOfExcessiveTimeouts; +} EVENT_TRACE_EXECUTIVE_RESOURCE_INFORMATION, *PEVENT_TRACE_EXECUTIVE_RESOURCE_INFORMATION; + +typedef struct _EVENT_TRACE_HEAP_TRACING_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG ProcessId; +} EVENT_TRACE_HEAP_TRACING_INFORMATION, *PEVENT_TRACE_HEAP_TRACING_INFORMATION; + +typedef struct _EVENT_TRACE_TAG_FILTER_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + HANDLE TraceHandle; + ULONG Filter[1]; +} EVENT_TRACE_TAG_FILTER_INFORMATION, *PEVENT_TRACE_TAG_FILTER_INFORMATION; + +typedef struct _EVENT_TRACE_PROFILE_COUNTER_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + HANDLE TraceHandle; + ULONG ProfileSource[1]; +} EVENT_TRACE_PROFILE_COUNTER_INFORMATION, *PEVENT_TRACE_PROFILE_COUNTER_INFORMATION; + +//typedef struct _PROFILE_SOURCE_INFO +//{ +// ULONG NextEntryOffset; +// ULONG Source; +// ULONG MinInterval; +// ULONG MaxInterval; +// PVOID Reserved; +// WCHAR Description[1]; +//} PROFILE_SOURCE_INFO, *PPROFILE_SOURCE_INFO; + +typedef struct _EVENT_TRACE_PROFILE_LIST_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG Spare; + struct _PROFILE_SOURCE_INFO* Profile[1]; +} EVENT_TRACE_PROFILE_LIST_INFORMATION, *PEVENT_TRACE_PROFILE_LIST_INFORMATION; + +typedef struct _EVENT_TRACE_STACK_CACHING_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + HANDLE TraceHandle; + BOOLEAN Enabled; + UCHAR Reserved[3]; + ULONG CacheSize; + ULONG BucketCount; +} EVENT_TRACE_STACK_CACHING_INFORMATION, *PEVENT_TRACE_STACK_CACHING_INFORMATION; + +typedef struct _EVENT_TRACE_SOFT_RESTART_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + HANDLE TraceHandle; + BOOLEAN PersistTraceBuffers; + WCHAR FileName[1]; +} EVENT_TRACE_SOFT_RESTART_INFORMATION, *PEVENT_TRACE_SOFT_RESTART_INFORMATION; + +typedef struct _EVENT_TRACE_PROFILE_ADD_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + BOOLEAN PerfEvtEventSelect; + BOOLEAN PerfEvtUnitSelect; + ULONG PerfEvtType; + ULONG CpuInfoHierarchy[0x3]; + ULONG InitialInterval; + BOOLEAN AllowsHalt; + BOOLEAN Persist; + WCHAR ProfileSourceDescription[0x1]; +} EVENT_TRACE_PROFILE_ADD_INFORMATION, *PEVENT_TRACE_PROFILE_ADD_INFORMATION; + +typedef struct _EVENT_TRACE_PROFILE_REMOVE_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + KPROFILE_SOURCE ProfileSource; + ULONG CpuInfoHierarchy[0x3]; +} EVENT_TRACE_PROFILE_REMOVE_INFORMATION, *PEVENT_TRACE_PROFILE_REMOVE_INFORMATION; + +typedef struct _EVENT_TRACE_COVERAGE_SAMPLER_INFORMATION +{ + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + BOOLEAN CoverageSamplerInformationClass; + UCHAR MajorVersion; + UCHAR MinorVersion; + UCHAR Reserved; + HANDLE SamplerHandle; +} EVENT_TRACE_COVERAGE_SAMPLER_INFORMATION, *PEVENT_TRACE_COVERAGE_SAMPLER_INFORMATION; + typedef struct _SYSTEM_EXCEPTION_INFORMATION { ULONG AlignmentFixupCount; @@ -1854,10 +2122,49 @@ typedef struct _SYSTEM_RANGE_START_INFORMATION PVOID SystemRangeStart; } SYSTEM_RANGE_START_INFORMATION, *PSYSTEM_RANGE_START_INFORMATION; +typedef struct _SYSTEM_VERIFIER_INFORMATION_LEGACY // pre-19H1 +{ + ULONG NextEntryOffset; + ULONG Level; + UNICODE_STRING DriverName; + + ULONG RaiseIrqls; + ULONG AcquireSpinLocks; + ULONG SynchronizeExecutions; + ULONG AllocationsAttempted; + + ULONG AllocationsSucceeded; + ULONG AllocationsSucceededSpecialPool; + ULONG AllocationsWithNoTag; + ULONG TrimRequests; + + ULONG Trims; + ULONG AllocationsFailed; + ULONG AllocationsFailedDeliberately; + ULONG Loads; + + ULONG Unloads; + ULONG UnTrackedPool; + ULONG CurrentPagedPoolAllocations; + ULONG CurrentNonPagedPoolAllocations; + + ULONG PeakPagedPoolAllocations; + ULONG PeakNonPagedPoolAllocations; + + SIZE_T PagedPoolUsageInBytes; + SIZE_T NonPagedPoolUsageInBytes; + SIZE_T PeakPagedPoolUsageInBytes; + SIZE_T PeakNonPagedPoolUsageInBytes; +} SYSTEM_VERIFIER_INFORMATION_LEGACY, *PSYSTEM_VERIFIER_INFORMATION_LEGACY; + typedef struct _SYSTEM_VERIFIER_INFORMATION { ULONG NextEntryOffset; ULONG Level; + ULONG RuleClasses[2]; + ULONG TriageContext; + ULONG AreAllDriversBeingVerified; + UNICODE_STRING DriverName; ULONG RaiseIrqls; @@ -2002,6 +2309,7 @@ typedef struct _SYSTEM_SESSION_MAPPED_VIEW_INFORMATION SIZE_T NumberOfBytesAvailableContiguous; } SYSTEM_SESSION_MAPPED_VIEW_INFORMATION, *PSYSTEM_SESSION_MAPPED_VIEW_INFORMATION; +#if (PHNT_MODE != PHNT_MODE_KERNEL) // private typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION { @@ -2019,6 +2327,7 @@ typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION ULONG TableBufferLength; UCHAR TableBuffer[1]; } SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION; +#endif // private typedef struct _SYSTEM_MEMORY_LIST_INFORMATION @@ -2074,22 +2383,25 @@ typedef struct _SYSTEM_PROCESS_ID_INFORMATION UNICODE_STRING ImageName; } SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION; -#if (PHNT_MODE == PHNT_MODE_KERNEL) -typedef enum _FIRMWARE_TYPE -{ - FirmwareTypeUnknown, - FirmwareTypeBios, - FirmwareTypeUefi, - FirmwareTypeMax -} FIRMWARE_TYPE, *PFIRMWARE_TYPE; -#endif - // private typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION { GUID BootIdentifier; FIRMWARE_TYPE FirmwareType; - ULONGLONG BootFlags; + union + { + ULONGLONG BootFlags; + struct + { + ULONGLONG DbgMenuOsSelection : 1; // REDSTONE4 + ULONGLONG DbgHiberBoot : 1; + ULONGLONG DbgSoftBoot : 1; + ULONGLONG DbgMeasuredLaunch : 1; + ULONGLONG DbgMeasuredLaunchCapable : 1; // 19H1 + ULONGLONG DbgSystemHiveReplace : 1; + ULONGLONG DbgMeasuredLaunchSmmProtections : 1; + }; + }; } SYSTEM_BOOT_ENVIRONMENT_INFORMATION, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION; // private @@ -2126,13 +2438,20 @@ typedef struct _SYSTEM_SYSTEM_DISK_INFORMATION UNICODE_STRING SystemDisk; } SYSTEM_SYSTEM_DISK_INFORMATION, *PSYSTEM_SYSTEM_DISK_INFORMATION; -// private +// private (Windows 8.1 and above) typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT { - ULONGLONG Hits; // ULONG in WIN8 + ULONGLONG Hits; UCHAR PercentFrequency; } SYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT, *PSYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT; +// private (Windows 7 and Windows 8) +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT_WIN8 +{ + ULONG Hits; + UCHAR PercentFrequency; +} SYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT_WIN8, *PSYSTEM_PROCESSOR_PERFORMANCE_HITCOUNT_WIN8; + // private typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_STATE_DISTRIBUTION { @@ -2176,6 +2495,38 @@ typedef struct _SYSTEM_VA_LIST_INFORMATION SIZE_T AllocationFailures; } SYSTEM_VA_LIST_INFORMATION, *PSYSTEM_VA_LIST_INFORMATION; +// rev +typedef enum _SYSTEM_STORE_INFORMATION_CLASS +{ + SystemStoreCompressionInformation = 22 // q: SYSTEM_STORE_COMPRESSION_INFORMATION +} SYSTEM_STORE_INFORMATION_CLASS; + +// rev +#define SYSTEM_STORE_INFORMATION_VERSION 1 + +// rev +typedef struct _SYSTEM_STORE_INFORMATION +{ + _In_ ULONG Version; + _In_ SYSTEM_STORE_INFORMATION_CLASS StoreInformationClass; + _Inout_ PVOID Data; + _Inout_ ULONG Length; +} SYSTEM_STORE_INFORMATION, *PSYSTEM_STORE_INFORMATION; + +// rev +#define SYSTEM_STORE_COMPRESSION_INFORMATION_VERSION 3 + +// rev +typedef struct _SYSTEM_STORE_COMPRESSION_INFORMATION +{ + ULONG Version; + ULONG CompressionPid; + ULONGLONG CompressionWorkingSetSize; + ULONGLONG CompressSize; + ULONGLONG CompressedSize; + ULONGLONG NonCompressedSize; +} SYSTEM_STORE_COMPRESSION_INFORMATION, *PSYSTEM_STORE_COMPRESSION_INFORMATION; + // private typedef struct _SYSTEM_REGISTRY_APPEND_STRING_PARAMETERS { @@ -2331,6 +2682,90 @@ typedef struct _SYSTEM_BOOT_GRAPHICS_INFORMATION ULONG DisplayRotation; } SYSTEM_BOOT_GRAPHICS_INFORMATION, *PSYSTEM_BOOT_GRAPHICS_INFORMATION; +// private +typedef struct _MEMORY_SCRUB_INFORMATION +{ + HANDLE Handle; + ULONG PagesScrubbed; +} MEMORY_SCRUB_INFORMATION, *PMEMORY_SCRUB_INFORMATION; + +// private +typedef struct _PEBS_DS_SAVE_AREA32 +{ + ULONG BtsBufferBase; + ULONG BtsIndex; + ULONG BtsAbsoluteMaximum; + ULONG BtsInterruptThreshold; + ULONG PebsBufferBase; + ULONG PebsIndex; + ULONG PebsAbsoluteMaximum; + ULONG PebsInterruptThreshold; + ULONG PebsGpCounterReset[8]; + ULONG PebsFixedCounterReset[4]; +} PEBS_DS_SAVE_AREA32, *PPEBS_DS_SAVE_AREA32; + +// private +typedef struct _PEBS_DS_SAVE_AREA64 +{ + ULONGLONG BtsBufferBase; + ULONGLONG BtsIndex; + ULONGLONG BtsAbsoluteMaximum; + ULONGLONG BtsInterruptThreshold; + ULONGLONG PebsBufferBase; + ULONGLONG PebsIndex; + ULONGLONG PebsAbsoluteMaximum; + ULONGLONG PebsInterruptThreshold; + ULONGLONG PebsGpCounterReset[8]; + ULONGLONG PebsFixedCounterReset[4]; +} PEBS_DS_SAVE_AREA64, *PPEBS_DS_SAVE_AREA64; + +// private +typedef union _PEBS_DS_SAVE_AREA +{ + PEBS_DS_SAVE_AREA32 As32Bit; + PEBS_DS_SAVE_AREA64 As64Bit; +} PEBS_DS_SAVE_AREA, *PPEBS_DS_SAVE_AREA; + +// private +typedef struct _PROCESSOR_PROFILE_CONTROL_AREA +{ + PEBS_DS_SAVE_AREA PebsDsSaveArea; +} PROCESSOR_PROFILE_CONTROL_AREA, *PPROCESSOR_PROFILE_CONTROL_AREA; + +// private +typedef struct _SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA +{ + PROCESSOR_PROFILE_CONTROL_AREA ProcessorProfileControlArea; + BOOLEAN Allocate; +} SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA, *PSYSTEM_PROCESSOR_PROFILE_CONTROL_AREA; + +// private +typedef struct _MEMORY_COMBINE_INFORMATION +{ + HANDLE Handle; + ULONG_PTR PagesCombined; +} MEMORY_COMBINE_INFORMATION, *PMEMORY_COMBINE_INFORMATION; + +// rev +#define MEMORY_COMBINE_FLAGS_COMMON_PAGES_ONLY 0x4 + +// private +typedef struct _MEMORY_COMBINE_INFORMATION_EX +{ + HANDLE Handle; + ULONG_PTR PagesCombined; + ULONG Flags; +} MEMORY_COMBINE_INFORMATION_EX, *PMEMORY_COMBINE_INFORMATION_EX; + +// private +typedef struct _MEMORY_COMBINE_INFORMATION_EX2 +{ + HANDLE Handle; + ULONG_PTR PagesCombined; + ULONG Flags; + HANDLE ProcessHandle; +} MEMORY_COMBINE_INFORMATION_EX2, *PMEMORY_COMBINE_INFORMATION_EX2; + // private typedef struct _SYSTEM_CONSOLE_INFORMATION { @@ -2451,9 +2886,21 @@ typedef struct _PROCESS_DISK_COUNTERS } PROCESS_DISK_COUNTERS, *PPROCESS_DISK_COUNTERS; // private +typedef union _ENERGY_STATE_DURATION +{ + union + { + ULONGLONG Value; + ULONG LastChangeTime; + }; + + ULONG Duration : 31; + ULONG IsInState : 1; +} ENERGY_STATE_DURATION, *PENERGY_STATE_DURATION; + typedef struct _PROCESS_ENERGY_VALUES { - ULONGLONG Cycles[2][4]; + ULONGLONG Cycles[4][2]; ULONGLONG DiskEnergy; ULONGLONG NetworkTailEnergy; ULONGLONG MBBTailEnergy; @@ -2461,19 +2908,87 @@ typedef struct _PROCESS_ENERGY_VALUES ULONGLONG MBBTxRxBytes; union { + ENERGY_STATE_DURATION Durations[3]; struct { - ULONG Foreground : 1; + ENERGY_STATE_DURATION ForegroundDuration; + ENERGY_STATE_DURATION DesktopVisibleDuration; + ENERGY_STATE_DURATION PSMForegroundDuration; }; - ULONG WindowInformation; }; - ULONG PixelArea; - LONGLONG PixelReportTimestamp; - ULONGLONG PixelTime; - LONGLONG ForegroundReportTimestamp; - ULONGLONG ForegroundTime; + ULONG CompositionRendered; + ULONG CompositionDirtyGenerated; + ULONG CompositionDirtyPropagated; + ULONG Reserved1; + ULONGLONG AttributedCycles[4][2]; + ULONGLONG WorkOnBehalfCycles[4][2]; } PROCESS_ENERGY_VALUES, *PPROCESS_ENERGY_VALUES; +typedef struct _TIMELINE_BITMAP +{ + ULONGLONG Value; + ULONG EndTime; + ULONG Bitmap; +} TIMELINE_BITMAP, *PTIMELINE_BITMAP; + +typedef struct _PROCESS_ENERGY_VALUES_EXTENSION +{ + union + { + TIMELINE_BITMAP Timelines[14]; // 9 for REDSTONE2, 14 for REDSTONE3/4/5 + struct + { + TIMELINE_BITMAP CpuTimeline; + TIMELINE_BITMAP DiskTimeline; + TIMELINE_BITMAP NetworkTimeline; + TIMELINE_BITMAP MBBTimeline; + TIMELINE_BITMAP ForegroundTimeline; + TIMELINE_BITMAP DesktopVisibleTimeline; + TIMELINE_BITMAP CompositionRenderedTimeline; + TIMELINE_BITMAP CompositionDirtyGeneratedTimeline; + TIMELINE_BITMAP CompositionDirtyPropagatedTimeline; + TIMELINE_BITMAP InputTimeline; // REDSTONE3 + TIMELINE_BITMAP AudioInTimeline; + TIMELINE_BITMAP AudioOutTimeline; + TIMELINE_BITMAP DisplayRequiredTimeline; + TIMELINE_BITMAP KeyboardInputTimeline; + }; + }; + + union // REDSTONE3 + { + ENERGY_STATE_DURATION Durations[5]; + struct + { + ENERGY_STATE_DURATION InputDuration; + ENERGY_STATE_DURATION AudioInDuration; + ENERGY_STATE_DURATION AudioOutDuration; + ENERGY_STATE_DURATION DisplayRequiredDuration; + ENERGY_STATE_DURATION PSMBackgroundDuration; + }; + }; + + ULONG KeyboardInput; + ULONG MouseInput; +} PROCESS_ENERGY_VALUES_EXTENSION, *PPROCESS_ENERGY_VALUES_EXTENSION; + +typedef struct _PROCESS_EXTENDED_ENERGY_VALUES +{ + PROCESS_ENERGY_VALUES Base; + PROCESS_ENERGY_VALUES_EXTENSION Extension; +} PROCESS_EXTENDED_ENERGY_VALUES, *PPROCESS_EXTENDED_ENERGY_VALUES; + +// private +typedef enum _SYSTEM_PROCESS_CLASSIFICATION +{ + SystemProcessClassificationNormal, + SystemProcessClassificationSystem, + SystemProcessClassificationSecureSystem, + SystemProcessClassificationMemCompression, + SystemProcessClassificationRegistry, // REDSTONE4 + SystemProcessClassificationMaximum +} SYSTEM_PROCESS_CLASSIFICATION; + // private typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION { @@ -2485,7 +3000,9 @@ typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION struct { ULONG HasStrongId : 1; - ULONG Spare : 31; + ULONG Classification : 4; // SYSTEM_PROCESS_CLASSIFICATION + ULONG BackgroundActivityModerated : 1; + ULONG Spare : 26; }; }; ULONG UserSidOffset; @@ -2495,6 +3012,7 @@ typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION SIZE_T SharedCommitCharge; // since THRESHOLD2 ULONG JobObjectId; // since REDSTONE ULONG SpareUlong; // since REDSTONE + ULONGLONG ProcessSequenceNumber; } SYSTEM_PROCESS_INFORMATION_EXTENSION, *PSYSTEM_PROCESS_INFORMATION_EXTENSION; // private @@ -2558,7 +3076,7 @@ typedef struct _SYSTEM_HYPERVISOR_DETAIL_INFORMATION // private typedef struct _SYSTEM_PROCESSOR_CYCLE_STATS_INFORMATION { - ULONGLONG Cycles[2][4]; + ULONGLONG Cycles[4][2]; } SYSTEM_PROCESSOR_CYCLE_STATS_INFORMATION, *PSYSTEM_PROCESSOR_CYCLE_STATS_INFORMATION; // private @@ -2568,11 +3086,18 @@ typedef struct _SYSTEM_TPM_INFORMATION } SYSTEM_TPM_INFORMATION, *PSYSTEM_TPM_INFORMATION; // private -typedef struct _SYSTEM_DMA_PROTECTION_INFORMATION +typedef struct _SYSTEM_VSM_PROTECTION_INFORMATION { BOOLEAN DmaProtectionsAvailable; BOOLEAN DmaProtectionsInUse; -} SYSTEM_DMA_PROTECTION_INFORMATION, *PSYSTEM_DMA_PROTECTION_INFORMATION; + BOOLEAN HardwareMbecAvailable; // REDSTONE4 (CVE-2018-3639) +} SYSTEM_VSM_PROTECTION_INFORMATION, *PSYSTEM_VSM_PROTECTION_INFORMATION; + +// private +typedef struct _SYSTEM_KERNEL_DEBUGGER_FLAGS +{ + UCHAR KernelDebuggerIgnoreUmExceptions; +} SYSTEM_KERNEL_DEBUGGER_FLAGS, *PSYSTEM_KERNEL_DEBUGGER_FLAGS; // private typedef struct _SYSTEM_CODEINTEGRITYPOLICY_INFORMATION @@ -2591,9 +3116,11 @@ typedef struct _SYSTEM_ISOLATED_USER_MODE_INFORMATION BOOLEAN HvciStrictMode : 1; BOOLEAN DebugEnabled : 1; BOOLEAN FirmwarePageProtection : 1; - BOOLEAN SpareFlags : 1; + BOOLEAN EncryptionKeyAvailable : 1; + BOOLEAN SpareFlags : 2; BOOLEAN TrustletRunning : 1; - BOOLEAN SpareFlags2 : 1; + BOOLEAN HvciDisableAllowed : 1; + BOOLEAN SpareFlags2 : 6; BOOLEAN Spare0[6]; ULONGLONG Spare1; } SYSTEM_ISOLATED_USER_MODE_INFORMATION, *PSYSTEM_ISOLATED_USER_MODE_INFORMATION; @@ -2625,7 +3152,7 @@ typedef struct _SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION typedef struct _SYSTEM_ROOT_SILO_INFORMATION { ULONG NumberOfSilos; - HANDLE SiloIdList[1]; + ULONG SiloIdList[1]; } SYSTEM_ROOT_SILO_INFORMATION, *PSYSTEM_ROOT_SILO_INFORMATION; // private @@ -2687,6 +3214,7 @@ typedef struct _SYSTEM_MEMORY_USAGE_INFORMATION typedef struct _SYSTEM_CODEINTEGRITY_CERTIFICATE_INFORMATION { HANDLE ImageFile; + ULONG Type; // REDSTONE4 } SYSTEM_CODEINTEGRITY_CERTIFICATE_INFORMATION, *PSYSTEM_CODEINTEGRITY_CERTIFICATE_INFORMATION; // private @@ -2697,6 +3225,60 @@ typedef struct _SYSTEM_PHYSICAL_MEMORY_INFORMATION ULONGLONG HighestPhysicalAddress; } SYSTEM_PHYSICAL_MEMORY_INFORMATION, *PSYSTEM_PHYSICAL_MEMORY_INFORMATION; +// private +typedef enum _SYSTEM_ACTIVITY_MODERATION_STATE +{ + SystemActivityModerationStateSystemManaged, + SystemActivityModerationStateUserManagedAllowThrottling, + SystemActivityModerationStateUserManagedDisableThrottling, + MaxSystemActivityModerationState +} SYSTEM_ACTIVITY_MODERATION_STATE; + +// private - REDSTONE2 +typedef struct _SYSTEM_ACTIVITY_MODERATION_EXE_STATE // REDSTONE3: Renamed SYSTEM_ACTIVITY_MODERATION_INFO +{ + UNICODE_STRING ExePathNt; + SYSTEM_ACTIVITY_MODERATION_STATE ModerationState; +} SYSTEM_ACTIVITY_MODERATION_EXE_STATE, *PSYSTEM_ACTIVITY_MODERATION_EXE_STATE; + +typedef enum _SYSTEM_ACTIVITY_MODERATION_APP_TYPE +{ + SystemActivityModerationAppTypeClassic, + SystemActivityModerationAppTypePackaged, + MaxSystemActivityModerationAppType +} SYSTEM_ACTIVITY_MODERATION_APP_TYPE; + +// private - REDSTONE3 +typedef struct _SYSTEM_ACTIVITY_MODERATION_INFO +{ + UNICODE_STRING Identifier; + SYSTEM_ACTIVITY_MODERATION_STATE ModerationState; + SYSTEM_ACTIVITY_MODERATION_APP_TYPE AppType; +} SYSTEM_ACTIVITY_MODERATION_INFO, *PSYSTEM_ACTIVITY_MODERATION_INFO; + +// private +typedef struct _SYSTEM_ACTIVITY_MODERATION_USER_SETTINGS +{ + HANDLE UserKeyHandle; +} SYSTEM_ACTIVITY_MODERATION_USER_SETTINGS, *PSYSTEM_ACTIVITY_MODERATION_USER_SETTINGS; + +// private +typedef struct _SYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION +{ + union + { + ULONG Flags; + struct + { + ULONG Locked : 1; + ULONG UnlockApplied : 1; // Unlockable field removed 19H1 + ULONG UnlockIdValid : 1; + ULONG Reserved : 29; + }; + }; + UCHAR UnlockId[32]; // REDSTONE4 +} SYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION, *PSYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION; + // private typedef struct _SYSTEM_FLUSH_INFORMATION { @@ -2706,6 +3288,126 @@ typedef struct _SYSTEM_FLUSH_INFORMATION ULONGLONG Reserved[2]; } SYSTEM_FLUSH_INFORMATION, *PSYSTEM_FLUSH_INFORMATION; +// private +typedef struct _SYSTEM_WRITE_CONSTRAINT_INFORMATION +{ + ULONG WriteConstraintPolicy; + ULONG Reserved; +} SYSTEM_WRITE_CONSTRAINT_INFORMATION, *PSYSTEM_WRITE_CONSTRAINT_INFORMATION; + +// private +typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION +{ + union + { + ULONG Flags; + struct + { + ULONG KvaShadowEnabled : 1; + ULONG KvaShadowUserGlobal : 1; + ULONG KvaShadowPcid : 1; + ULONG KvaShadowInvpcid : 1; + ULONG KvaShadowRequired : 1; // REDSTONE4 + ULONG KvaShadowRequiredAvailable : 1; + ULONG InvalidPteBit : 6; + ULONG L1DataCacheFlushSupported : 1; + ULONG L1TerminalFaultMitigationPresent : 1; + ULONG Reserved : 18; + }; + }; +} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, *PSYSTEM_KERNEL_VA_SHADOW_INFORMATION; + +// private +typedef struct _SYSTEM_CODEINTEGRITYVERIFICATION_INFORMATION +{ + HANDLE FileHandle; + ULONG ImageSize; + PVOID Image; +} SYSTEM_CODEINTEGRITYVERIFICATION_INFORMATION, *PSYSTEM_CODEINTEGRITYVERIFICATION_INFORMATION; + +// private +typedef struct _SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION +{ + PVOID HypervisorSharedUserVa; +} SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION, *PSYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION; + +// private +typedef struct _SYSTEM_FIRMWARE_PARTITION_INFORMATION +{ + UNICODE_STRING FirmwarePartition; +} SYSTEM_FIRMWARE_PARTITION_INFORMATION, *PSYSTEM_FIRMWARE_PARTITION_INFORMATION; + +// private +typedef struct _SYSTEM_SPECULATION_CONTROL_INFORMATION +{ + union + { + ULONG Flags; + struct + { + ULONG BpbEnabled : 1; + ULONG BpbDisabledSystemPolicy : 1; + ULONG BpbDisabledNoHardwareSupport : 1; + ULONG SpecCtrlEnumerated : 1; + ULONG SpecCmdEnumerated : 1; + ULONG IbrsPresent : 1; + ULONG StibpPresent : 1; + ULONG SmepPresent : 1; + ULONG SpeculativeStoreBypassDisableAvailable : 1; // REDSTONE4 (CVE-2018-3639) + ULONG SpeculativeStoreBypassDisableSupported : 1; + ULONG SpeculativeStoreBypassDisabledSystemWide : 1; + ULONG SpeculativeStoreBypassDisabledKernel : 1; + ULONG SpeculativeStoreBypassDisableRequired : 1; + ULONG BpbDisabledKernelToUser : 1; + ULONG SpecCtrlRetpolineEnabled : 1; + ULONG SpecCtrlImportOptimizationEnabled : 1; + ULONG EnhancedIbrs : 1; // since 19H1 + ULONG HvL1tfStatusAvailable : 1; + ULONG HvL1tfProcessorNotAffected : 1; + ULONG HvL1tfMigitationEnabled : 1; + ULONG HvL1tfMigitationNotEnabled_Hardware : 1; + ULONG HvL1tfMigitationNotEnabled_LoadOption : 1; + ULONG HvL1tfMigitationNotEnabled_CoreScheduler : 1; + ULONG EnhancedIbrsReported : 1; + ULONG Reserved : 8; + }; + }; +} SYSTEM_SPECULATION_CONTROL_INFORMATION, *PSYSTEM_SPECULATION_CONTROL_INFORMATION; + +// private +typedef struct _SYSTEM_DMA_GUARD_POLICY_INFORMATION +{ + BOOLEAN DmaGuardPolicyEnabled; +} SYSTEM_DMA_GUARD_POLICY_INFORMATION, *PSYSTEM_DMA_GUARD_POLICY_INFORMATION; + +// private +typedef struct _SYSTEM_ENCLAVE_LAUNCH_CONTROL_INFORMATION +{ + UCHAR EnclaveLaunchSigner[32]; +} SYSTEM_ENCLAVE_LAUNCH_CONTROL_INFORMATION, *PSYSTEM_ENCLAVE_LAUNCH_CONTROL_INFORMATION; + +// private +typedef struct _SYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION +{ + ULONGLONG WorkloadClass; + ULONGLONG CpuSets[1]; +} SYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION, *PSYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION; + +// private +typedef struct _SYSTEM_SECURITY_MODEL_INFORMATION +{ + union + { + ULONG SecurityModelFlags; + struct + { + ULONG SModeAdminlessEnabled : 1; + ULONG AllowDeviceOwnerProtectionDowngrade : 1; + ULONG Reserved : 30; + }; + }; +} SYSTEM_SECURITY_MODEL_INFORMATION, *PSYSTEM_SECURITY_MODEL_INFORMATION; + #if (PHNT_MODE != PHNT_MODE_KERNEL) NTSYSCALLAPI @@ -2852,6 +3554,48 @@ typedef struct _SYSDBG_TRIAGE_DUMP PHANDLE Handles; } SYSDBG_TRIAGE_DUMP, *PSYSDBG_TRIAGE_DUMP; +// private +typedef union _SYSDBG_LIVEDUMP_CONTROL_FLAGS +{ + struct + { + ULONG UseDumpStorageStack : 1; + ULONG CompressMemoryPagesData : 1; + ULONG IncludeUserSpaceMemoryPages : 1; + ULONG AbortIfMemoryPressure : 1; // REDSTONE4 + ULONG Reserved : 28; + }; + ULONG AsUlong; +} SYSDBG_LIVEDUMP_CONTROL_FLAGS, *PSYSDBG_LIVEDUMP_CONTROL_FLAGS; + +// private +typedef union _SYSDBG_LIVEDUMP_CONTROL_ADDPAGES +{ + struct + { + ULONG HypervisorPages : 1; + ULONG Reserved : 31; + }; + ULONG AsUlong; +} SYSDBG_LIVEDUMP_CONTROL_ADDPAGES, *PSYSDBG_LIVEDUMP_CONTROL_ADDPAGES; + +#define SYSDBG_LIVEDUMP_CONTROL_VERSION 1 + +// private +typedef struct _SYSDBG_LIVEDUMP_CONTROL +{ + ULONG Version; + ULONG BugCheckCode; + ULONG_PTR BugCheckParam1; + ULONG_PTR BugCheckParam2; + ULONG_PTR BugCheckParam3; + ULONG_PTR BugCheckParam4; + HANDLE DumpFileHandle; + HANDLE CancelEventHandle; + SYSDBG_LIVEDUMP_CONTROL_FLAGS Flags; + SYSDBG_LIVEDUMP_CONTROL_ADDPAGES AddPagesControl; +} SYSDBG_LIVEDUMP_CONTROL, *PSYSDBG_LIVEDUMP_CONTROL; + NTSYSCALLAPI NTSTATUS NTAPI @@ -2894,7 +3638,7 @@ typedef enum _HARDERROR_RESPONSE ResponseContinue } HARDERROR_RESPONSE; -// HARDERROR_MSG not included +#define HARDERROR_OVERRIDE_ERRORMODE 0x10000000 NTSYSCALLAPI NTSTATUS @@ -2954,7 +3698,7 @@ typedef struct _KUSER_SHARED_DATA LONG TimeZoneBiasStamp; ULONG NtBuildNumber; - ULONG NtProductType; + NT_PRODUCT_TYPE NtProductType; BOOLEAN ProductTypeIsValid; UCHAR Reserved0[1]; USHORT NativeProcessorArchitecture; @@ -2988,7 +3732,8 @@ typedef struct _KUSER_SHARED_DATA UCHAR Reserved : 2; }; }; - UCHAR Reserved6[2]; + + USHORT CyclesPerYield; volatile ULONG ActiveConsoleId; @@ -3019,7 +3764,8 @@ typedef struct _KUSER_SHARED_DATA ULONG DbgSecureBootEnabled : 1; ULONG DbgMultiSessionSku : 1; ULONG DbgMultiUsersInSessionSku : 1; - ULONG SpareBits : 22; + ULONG DbgStateSeparationEnabled : 1; + ULONG SpareBits : 21; }; }; ULONG DataFlagsPad[1]; @@ -3052,7 +3798,9 @@ typedef struct _KUSER_SHARED_DATA USHORT UnparkedProcessorCount; ULONG EnclaveFeatureMask[4]; - ULONG Reserved8; + + ULONG TelemetryCoverageRound; + USHORT UserModeGlobalLogger[16]; ULONG ImageFileExecutionOptions; @@ -3083,38 +3831,20 @@ typedef struct _KUSER_SHARED_DATA C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TickCountMultiplier) == 0x4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, InterruptTime) == 0x8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemTime) == 0x14); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TimeZoneBias) == 0x20); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ImageNumberLow) == 0x2c); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ImageNumberHigh) == 0x2e); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtSystemRoot) == 0x30); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, MaxStackTraceDepth) == 0x238); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, CryptoExponent) == 0x23c); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TimeZoneId) == 0x240); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, LargePageMinimum) == 0x244); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtProductType) == 0x264); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ProductTypeIsValid) == 0x268); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtMajorVersion) == 0x26c); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtMinorVersion) == 0x270); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ProcessorFeatures) == 0x274); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved1) == 0x2b4); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved3) == 0x2b8); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TimeSlip) == 0x2bc); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, AlternativeArchitecture) == 0x2c0); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemExpirationDate) == 0x2c8); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SuiteMask) == 0x2d0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, KdDebuggerEnabled) == 0x2d4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ActiveConsoleId) == 0x2d8); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, DismountCount) == 0x2dc); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ComPlusPackage) == 0x2e0); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, LastSystemRITEventTickCount) == 0x2e4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NumberOfPhysicalPages) == 0x2e8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SafeBootMode) == 0x2ec); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TestRetInstruction) == 0x2f8); -C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemCallPad) == 0x310); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TickCount) == 0x320); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TickCountQuad) == 0x320); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, XState) == 0x3d8); -//C_ASSERT(sizeof(KUSER_SHARED_DATA) == 0x708); // Visual Studio has a problem with this +//C_ASSERT(sizeof(KUSER_SHARED_DATA) == 0x70C); // VS2017 has some weird issue with this. #define USER_SHARED_DATA ((KUSER_SHARED_DATA * const)0x7ffe0000) @@ -3331,6 +4061,9 @@ NtAddAtom( ); #if (PHNT_VERSION >= PHNT_WIN8) + +#define ATOM_FLAG_GLOBAL 0x2 + // rev NTSYSCALLAPI NTSTATUS @@ -3341,6 +4074,7 @@ NtAddAtomEx( _Out_opt_ PRTL_ATOM Atom, _In_ ULONG Flags ); + #endif NTSYSCALLAPI @@ -3512,12 +4246,15 @@ NtDisplayString( _In_ PUNICODE_STRING String ); +// Boot graphics + #if (PHNT_VERSION >= PHNT_WIN7) +// rev NTSYSCALLAPI NTSTATUS NTAPI NtDrawText( - _In_ PUNICODE_STRING String + _In_ PUNICODE_STRING Text ); #endif diff --git a/phnt/include/ntgdi.h b/phnt/include/ntgdi.h index d7b47cda4a85..5e85bfab3039 100644 --- a/phnt/include/ntgdi.h +++ b/phnt/include/ntgdi.h @@ -1,3 +1,23 @@ +/* + * Process Hacker - + * Graphics device interface support + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTGDI_H #define _NTGDI_H diff --git a/phnt/include/ntioapi.h b/phnt/include/ntioapi.h index 84e9c53724a2..bcc43a7b2688 100644 --- a/phnt/include/ntioapi.h +++ b/phnt/include/ntioapi.h @@ -1,3 +1,23 @@ +/* + * Process Hacker - + * File management support + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTIOAPI_H #define _NTIOAPI_H @@ -233,15 +253,22 @@ typedef enum _FILE_INFORMATION_CLASS FileLinkInformationBypassAccessCheck, // (kernel-mode only); FILE_LINK_INFORMATION FileVolumeNameInformation, // FILE_VOLUME_NAME_INFORMATION FileIdInformation, // FILE_ID_INFORMATION - FileIdExtdDirectoryInformation, // FILE_ID_EXTD_DIR_INFORMATION + FileIdExtdDirectoryInformation, // FILE_ID_EXTD_DIR_INFORMATION // 60 FileReplaceCompletionInformation, // FILE_COMPLETION_INFORMATION // since WINBLUE FileHardLinkFullIdInformation, // FILE_LINK_ENTRY_FULL_ID_INFORMATION FileIdExtdBothDirectoryInformation, // FILE_ID_EXTD_BOTH_DIR_INFORMATION // since THRESHOLD FileDispositionInformationEx, // FILE_DISPOSITION_INFO_EX // since REDSTONE - FileRenameInformationEx, - FileRenameInformationExBypassAccessCheck, + FileRenameInformationEx, // FILE_RENAME_INFORMATION + FileRenameInformationExBypassAccessCheck, // FILE_RENAME_INFORMATION FileDesiredStorageClassInformation, // FILE_DESIRED_STORAGE_CLASS_INFORMATION // since REDSTONE2 FileStatInformation, // FILE_STAT_INFORMATION + FileMemoryPartitionInformation, // FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3 + FileStatLxInformation, // FILE_STAT_LX_INFORMATION // since REDSTONE4 // 70 + FileCaseSensitiveInformation, // FILE_CASE_SENSITIVE_INFORMATION + FileLinkInformationEx, // FILE_LINK_INFORMATION // since REDSTONE5 + FileLinkInformationExBypassAccessCheck, // FILE_LINK_INFORMATION + FileStorageReserveIdInformation, // FILE_SET_STORAGE_RESERVE_ID_INFORMATION + FileCaseSensitiveInformationForceAccessCheck, // FILE_CASE_SENSITIVE_INFORMATION FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; @@ -679,6 +706,7 @@ typedef struct _FILE_ID_EXTD_BOTH_DIR_INFORMATION WCHAR FileName[1]; } FILE_ID_EXTD_BOTH_DIR_INFORMATION, *PFILE_ID_EXTD_BOTH_DIR_INFORMATION; +// private typedef struct _FILE_STAT_INFORMATION { LARGE_INTEGER FileId; @@ -694,6 +722,49 @@ typedef struct _FILE_STAT_INFORMATION ULONG EffectiveAccess; } FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION; +// private +typedef struct _FILE_MEMORY_PARTITION_INFORMATION +{ + HANDLE OwnerPartitionHandle; + union + { + struct + { + UCHAR NoCrossPartitionAccess; + UCHAR Spare[3]; + }; + ULONG AllFlags; + } Flags; +} FILE_MEMORY_PARTITION_INFORMATION, *PFILE_MEMORY_PARTITION_INFORMATION; + +// private +typedef struct _FILE_STAT_LX_INFORMATION +{ + LARGE_INTEGER FileId; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; + ULONG ReparseTag; + ULONG NumberOfLinks; + ULONG EffectiveAccess; + ULONG LxFlags; + ULONG LxUid; + ULONG LxGid; + ULONG LxMode; + ULONG LxDeviceIdMajor; + ULONG LxDeviceIdMinor; +} FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION; + +// private +typedef struct _FILE_CASE_SENSITIVE_INFORMATION +{ + ULONG Flags; +} FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION; + // NtQueryDirectoryFile types typedef struct _FILE_DIRECTORY_INFORMATION @@ -876,21 +947,17 @@ typedef enum _FSINFOCLASS FileFsFullSizeInformation, // FILE_FS_FULL_SIZE_INFORMATION FileFsObjectIdInformation, // FILE_FS_OBJECTID_INFORMATION FileFsDriverPathInformation, // FILE_FS_DRIVER_PATH_INFORMATION - FileFsVolumeFlagsInformation, // FILE_FS_VOLUME_FLAGS_INFORMATION + FileFsVolumeFlagsInformation, // FILE_FS_VOLUME_FLAGS_INFORMATION // 10 FileFsSectorSizeInformation, // FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8 FileFsDataCopyInformation, // FILE_FS_DATA_COPY_INFORMATION FileFsMetadataSizeInformation, // FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD + FileFsFullSizeInformationEx, // FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5 FileFsMaximumInformation } FSINFOCLASS, *PFSINFOCLASS; // NtQueryVolumeInformation/NtSetVolumeInformation types -typedef struct _FILE_FS_LABEL_INFORMATION -{ - ULONG VolumeLabelLength; - WCHAR VolumeLabel[1]; -} FILE_FS_LABEL_INFORMATION, *PFILE_FS_LABEL_INFORMATION; - +// private typedef struct _FILE_FS_VOLUME_INFORMATION { LARGE_INTEGER VolumeCreationTime; @@ -900,6 +967,14 @@ typedef struct _FILE_FS_VOLUME_INFORMATION WCHAR VolumeLabel[1]; } FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION; +// private +typedef struct _FILE_FS_LABEL_INFORMATION +{ + ULONG VolumeLabelLength; + WCHAR VolumeLabel[1]; +} FILE_FS_LABEL_INFORMATION, * PFILE_FS_LABEL_INFORMATION; + +// private typedef struct _FILE_FS_SIZE_INFORMATION { LARGE_INTEGER TotalAllocationUnits; @@ -919,6 +994,7 @@ typedef struct _FILE_FS_CONTROL_INFORMATION ULONG FileSystemControlFlags; } FILE_FS_CONTROL_INFORMATION, *PFILE_FS_CONTROL_INFORMATION; +// private typedef struct _FILE_FS_FULL_SIZE_INFORMATION { LARGE_INTEGER TotalAllocationUnits; @@ -928,18 +1004,21 @@ typedef struct _FILE_FS_FULL_SIZE_INFORMATION ULONG BytesPerSector; } FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION; +// private typedef struct _FILE_FS_OBJECTID_INFORMATION { UCHAR ObjectId[16]; UCHAR ExtendedInfo[48]; } FILE_FS_OBJECTID_INFORMATION, *PFILE_FS_OBJECTID_INFORMATION; +// private typedef struct _FILE_FS_DEVICE_INFORMATION { DEVICE_TYPE DeviceType; ULONG Characteristics; } FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION; +// private typedef struct _FILE_FS_ATTRIBUTE_INFORMATION { ULONG FileSystemAttributes; @@ -948,6 +1027,7 @@ typedef struct _FILE_FS_ATTRIBUTE_INFORMATION WCHAR FileSystemName[1]; } FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION; +// private typedef struct _FILE_FS_DRIVER_PATH_INFORMATION { BOOLEAN DriverInPath; @@ -955,6 +1035,7 @@ typedef struct _FILE_FS_DRIVER_PATH_INFORMATION WCHAR DriverName[1]; } FILE_FS_DRIVER_PATH_INFORMATION, *PFILE_FS_DRIVER_PATH_INFORMATION; +// private typedef struct _FILE_FS_VOLUME_FLAGS_INFORMATION { ULONG Flags; @@ -983,6 +1064,7 @@ typedef struct _FILE_FS_DATA_COPY_INFORMATION ULONG NumberOfCopies; } FILE_FS_DATA_COPY_INFORMATION, *PFILE_FS_DATA_COPY_INFORMATION; +// private typedef struct _FILE_FS_METADATA_SIZE_INFORMATION { LARGE_INTEGER TotalMetadataAllocationUnits; @@ -990,6 +1072,24 @@ typedef struct _FILE_FS_METADATA_SIZE_INFORMATION ULONG BytesPerSector; } FILE_FS_METADATA_SIZE_INFORMATION, *PFILE_FS_METADATA_SIZE_INFORMATION; +// private +typedef struct _FILE_FS_FULL_SIZE_INFORMATION_EX +{ + ULONGLONG ActualTotalAllocationUnits; + ULONGLONG ActualAvailableAllocationUnits; + ULONGLONG ActualPoolUnavailableAllocationUnits; + ULONGLONG CallerTotalAllocationUnits; + ULONGLONG CallerAvailableAllocationUnits; + ULONGLONG CallerPoolUnavailableAllocationUnits; + ULONGLONG UsedAllocationUnits; + ULONGLONG TotalReservedAllocationUnits; + ULONGLONG VolumeStorageReserveAllocationUnits; + ULONGLONG AvailableCommittedAllocationUnits; + ULONGLONG PoolAvailableAllocationUnits; + ULONG SectorsPerAllocationUnit; + ULONG BytesPerSector; +} FILE_FS_FULL_SIZE_INFORMATION_EX, *PFILE_FS_FULL_SIZE_INFORMATION_EX; + // System calls NTSYSCALLAPI @@ -1072,6 +1172,7 @@ NtFlushBuffersFile( #define FLUSH_FLAGS_FILE_DATA_ONLY 0x00000001 #define FLUSH_FLAGS_NO_SYNC 0x00000002 +#define FLUSH_FLAGS_FILE_DATA_SYNC_ONLY 0x00000004 // REDSTONE1 #if (PHNT_VERSION >= PHNT_WIN8) NTSYSCALLAPI @@ -1097,6 +1198,19 @@ NtQueryInformationFile( _In_ FILE_INFORMATION_CLASS FileInformationClass ); +#if (PHNT_VERSION >= PHNT_REDSTONE2) +NTSYSCALLAPI +NTSTATUS +NTAPI +NtQueryInformationByName( + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_writes_bytes_(Length) PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass + ); +#endif + NTSYSCALLAPI NTSTATUS NTAPI @@ -1377,6 +1491,31 @@ NtNotifyChangeDirectoryFile( _In_ BOOLEAN WatchTree ); +// private +typedef enum _DIRECTORY_NOTIFY_INFORMATION_CLASS +{ + DirectoryNotifyInformation, // FILE_NOTIFY_INFORMATION + DirectoryNotifyExtendedInformation // FILE_NOTIFY_EXTENDED_INFORMATION +} DIRECTORY_NOTIFY_INFORMATION_CLASS, *PDIRECTORY_NOTIFY_INFORMATION_CLASS; + +#if (PHNT_VERSION >= PHNT_REDSTONE3) +NTSYSCALLAPI +NTSTATUS +NTAPI +NtNotifyChangeDirectoryFileEx( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_writes_bytes_(Length) PVOID Buffer, + _In_ ULONG Length, + _In_ ULONG CompletionFilter, + _In_ BOOLEAN WatchTree, + _In_opt_ DIRECTORY_NOTIFY_INFORMATION_CLASS DirectoryNotifyInformationClass + ); +#endif + NTSYSCALLAPI NTSTATUS NTAPI @@ -1432,7 +1571,7 @@ NTAPI NtQueryIoCompletion( _In_ HANDLE IoCompletionHandle, _In_ IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, - _Out_writes_bytes_(IoCompletionInformation) PVOID IoCompletionInformation, + _Out_writes_bytes_(IoCompletionInformationLength) PVOID IoCompletionInformation, _In_ ULONG IoCompletionInformationLength, _Out_opt_ PULONG ReturnLength ); @@ -1667,6 +1806,8 @@ typedef struct _REPARSE_DATA_BUFFER // Named pipe FS control definitions +#define DEVICE_NAMED_PIPE L"\\Device\\NamedPipe\\" + #define FSCTL_PIPE_ASSIGN_EVENT CTL_CODE(FILE_DEVICE_NAMED_PIPE, 0, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_PIPE_DISCONNECT CTL_CODE(FILE_DEVICE_NAMED_PIPE, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_PIPE_LISTEN CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/phnt/include/ntldr.h b/phnt/include/ntldr.h index 2434c2bfe125..92a3b49c324f 100644 --- a/phnt/include/ntldr.h +++ b/phnt/include/ntldr.h @@ -1,3 +1,23 @@ +/* + * Process Hacker - + * Loader support functions + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTLDR_H #define _NTLDR_H @@ -5,6 +25,12 @@ // DLLs +typedef BOOLEAN (NTAPI *PLDR_INIT_ROUTINE)( + _In_ PVOID DllHandle, + _In_ ULONG Reason, + _In_opt_ PVOID Context + ); + // symbols typedef struct _LDR_SERVICE_TAG_RECORD { @@ -76,26 +102,39 @@ typedef enum _LDR_DLL_LOAD_REASON LoadReasonDynamicLoad, LoadReasonAsImageLoad, LoadReasonAsDataLoad, + LoadReasonEnclavePrimary, // REDSTONE3 + LoadReasonEnclaveDependency, LoadReasonUnknown = -1 } LDR_DLL_LOAD_REASON, *PLDR_DLL_LOAD_REASON; #define LDRP_PACKAGED_BINARY 0x00000001 +#define LDRP_STATIC_LINK 0x00000002 #define LDRP_IMAGE_DLL 0x00000004 #define LDRP_LOAD_IN_PROGRESS 0x00001000 +#define LDRP_UNLOAD_IN_PROGRESS 0x00002000 #define LDRP_ENTRY_PROCESSED 0x00004000 +#define LDRP_ENTRY_INSERTED 0x00008000 +#define LDRP_CURRENT_LOAD 0x00010000 +#define LDRP_FAILED_BUILTIN_LOAD 0x00020000 #define LDRP_DONT_CALL_FOR_THREADS 0x00040000 #define LDRP_PROCESS_ATTACH_CALLED 0x00080000 -#define LDRP_PROCESS_ATTACH_FAILED 0x00100000 +#define LDRP_DEBUG_SYMBOLS_LOADED 0x00100000 #define LDRP_IMAGE_NOT_AT_BASE 0x00200000 // Vista and below #define LDRP_COR_IMAGE 0x00400000 -#define LDRP_DONT_RELOCATE 0x00800000 +#define LDRP_DONT_RELOCATE 0x00800000 // LDR_COR_OWNS_UNMAP +#define LDRP_SYSTEM_MAPPED 0x01000000 +#define LDRP_IMAGE_VERIFYING 0x02000000 +#define LDRP_DRIVER_DEPENDENT_DLL 0x04000000 +#define LDRP_ENTRY_NATIVE 0x08000000 #define LDRP_REDIRECTED 0x10000000 +#define LDRP_NON_PAGED_DEBUG_INFO 0x20000000 +#define LDRP_MM_LOADED 0x40000000 #define LDRP_COMPAT_DATABASE_PROCESSED 0x80000000 -// Use the size of the structure as it was in Windows XP. #define LDR_DATA_TABLE_ENTRY_SIZE_WINXP FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, DdagNode) #define LDR_DATA_TABLE_ENTRY_SIZE_WIN7 FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, BaseNameHashValue) #define LDR_DATA_TABLE_ENTRY_SIZE_WIN8 FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, ImplicitPathOptions) +#define LDR_DATA_TABLE_ENTRY_SIZE_WIN10 sizeof(LDR_DATA_TABLE_ENTRY) // symbols typedef struct _LDR_DATA_TABLE_ENTRY @@ -108,7 +147,7 @@ typedef struct _LDR_DATA_TABLE_ENTRY LIST_ENTRY InProgressLinks; }; PVOID DllBase; - PVOID EntryPoint; + PLDR_INIT_ROUTINE EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; @@ -141,7 +180,8 @@ typedef struct _LDR_DATA_TABLE_ENTRY ULONG CorImage : 1; ULONG DontRelocate : 1; ULONG CorILOnly : 1; - ULONG ReservedFlags5 : 3; + ULONG ChpeImage : 1; + ULONG ReservedFlags5 : 2; ULONG Redirected : 1; ULONG ReservedFlags6 : 2; ULONG CompatDatabaseProcessed : 1; @@ -152,7 +192,7 @@ typedef struct _LDR_DATA_TABLE_ENTRY LIST_ENTRY HashLinks; ULONG TimeDateStamp; struct _ACTIVATION_CONTEXT *EntryPointActivationContext; - PVOID Lock; + PVOID Lock; // RtlAcquireSRWLockExclusive PLDR_DDAG_NODE DdagNode; LIST_ENTRY NodeModuleLink; struct _LDRP_LOAD_CONTEXT *LoadContext; @@ -170,11 +210,9 @@ typedef struct _LDR_DATA_TABLE_ENTRY UCHAR SigningLevel; // since REDSTONE2 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; -typedef BOOLEAN (NTAPI *PDLL_INIT_ROUTINE)( - _In_ PVOID DllHandle, - _In_ ULONG Reason, - _In_opt_ PCONTEXT Context - ); +#define LDR_IS_DATAFILE(DllHandle) (((ULONG_PTR)(DllHandle)) & (ULONG_PTR)1) +#define LDR_IS_IMAGEMAPPING(DllHandle) (((ULONG_PTR)(DllHandle)) & (ULONG_PTR)2) +#define LDR_IS_RESOURCE(DllHandle) (LDR_IS_IMAGEMAPPING(DllHandle) || LDR_IS_DATAFILE(DllHandle)) NTSYSAPI NTSTATUS @@ -211,7 +249,7 @@ NTSTATUS NTAPI LdrGetDllHandleEx( _In_ ULONG Flags, - _In_opt_ PCWSTR DllPath, + _In_opt_ PWSTR DllPath, _In_opt_ PULONG DllCharacteristics, _In_ PUNICODE_STRING DllName, _Out_opt_ PVOID *DllHandle @@ -223,7 +261,7 @@ NTSYSAPI NTSTATUS NTAPI LdrGetDllHandleByMapping( - _In_ PVOID Base, + _In_ PVOID BaseAddress, _Out_ PVOID *DllHandle ); #endif @@ -240,6 +278,33 @@ LdrGetDllHandleByName( ); #endif +#if (PHNT_VERSION >= PHNT_WIN8) +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrGetDllFullName( + _In_ PVOID DllHandle, + _Out_ PUNICODE_STRING FullDllName + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrGetDllDirectory( + _Out_ PUNICODE_STRING DllDirectory + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrSetDllDirectory( + _In_ PUNICODE_STRING DllDirectory + ); +#endif + #define LDR_ADDREF_DLL_PIN 0x00000001 NTSYSAPI @@ -277,6 +342,30 @@ LdrGetProcedureAddressEx( ); #endif +NTSYSAPI +NTSTATUS +NTAPI +LdrGetKnownDllSectionHandle( + _In_ PCWSTR DllName, + _In_ BOOLEAN KnownDlls32, + _Out_ PHANDLE Section + ); + +#if (PHNT_VERSION >= PHNT_THRESHOLD) +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrGetProcedureAddressForCaller( + _In_ PVOID DllHandle, + _In_opt_ PANSI_STRING ProcedureName, + _In_opt_ ULONG ProcedureNumber, + _Out_ PVOID *ProcedureAddress, + _In_ ULONG Flags, + _In_ PVOID *Callback + ); +#endif + #define LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS 0x00000001 #define LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY 0x00000002 @@ -469,19 +558,54 @@ LdrUnregisterDllNotification( // end_msdn // rev -typedef struct _LDR_INIT_BLOCK +NTSYSAPI +PUNICODE_STRING +NTAPI +LdrStandardizeSystemPath( + _In_ PUNICODE_STRING SystemPath + ); + +// private +typedef struct _PS_MITIGATION_OPTIONS_MAP +{ + ULONG_PTR Map[2]; +} PS_MITIGATION_OPTIONS_MAP, *PPS_MITIGATION_OPTIONS_MAP; + +// private +typedef struct _PS_MITIGATION_AUDIT_OPTIONS_MAP +{ + ULONG_PTR Map[2]; +} PS_MITIGATION_AUDIT_OPTIONS_MAP, *PPS_MITIGATION_AUDIT_OPTIONS_MAP; + +// private +typedef struct _PS_SYSTEM_DLL_INIT_BLOCK { ULONG Size; - PVOID Unknown1[21]; - PVOID CfgBitmapAddress; - ULONG_PTR CfgBitmapSize; - PVOID Unknown2[2]; -} LDR_INIT_BLOCK, *PLDR_INIT_BLOCK; + ULONG_PTR SystemDllWowRelocation; + ULONG_PTR SystemDllNativeRelocation; + ULONG_PTR Wow64SharedInformation[16]; + ULONG RngData; + union + { + ULONG Flags; + struct + { + ULONG CfgOverride : 1; + ULONG Reserved : 31; + }; + }; + PS_MITIGATION_OPTIONS_MAP MitigationOptionsMap; + ULONG_PTR CfgBitMap; + ULONG_PTR CfgBitMapSize; + ULONG_PTR Wow64CfgBitMap; + ULONG_PTR Wow64CfgBitMapSize; + PS_MITIGATION_AUDIT_OPTIONS_MAP MitigationAuditOptionsMap; // REDSTONE3 +} PS_SYSTEM_DLL_INIT_BLOCK, *PPS_SYSTEM_DLL_INIT_BLOCK; #if (PHNT_VERSION >= PHNT_THRESHOLD) // rev NTSYSAPI -PLDR_INIT_BLOCK +PPS_SYSTEM_DLL_INIT_BLOCK NTAPI LdrSystemDllInitBlock( VOID @@ -525,6 +649,97 @@ LdrGetFileNameFromLoadAsDataTable( #endif +NTSYSAPI +NTSTATUS +NTAPI +LdrDisableThreadCalloutsForDll( + _In_ PVOID DllImageBase + ); + +// Resources + +NTSYSAPI +NTSTATUS +NTAPI +LdrAccessResource( + _In_ PVOID DllHandle, + _In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, + _Out_opt_ PVOID *ResourceBuffer, + _Out_opt_ ULONG *ResourceLength + ); + +typedef struct _LDR_RESOURCE_INFO +{ + ULONG_PTR Type; + ULONG_PTR Name; + ULONG_PTR Language; +} LDR_RESOURCE_INFO, *PLDR_RESOURCE_INFO; + +#define RESOURCE_TYPE_LEVEL 0 +#define RESOURCE_NAME_LEVEL 1 +#define RESOURCE_LANGUAGE_LEVEL 2 +#define RESOURCE_DATA_LEVEL 3 + +NTSYSAPI +NTSTATUS +NTAPI +LdrFindResource_U( + _In_ PVOID DllHandle, + _In_ PLDR_RESOURCE_INFO ResourceInfo, + _In_ ULONG Level, + _Out_ PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrFindResourceDirectory_U( + _In_ PVOID DllHandle, + _In_ PLDR_RESOURCE_INFO ResourceInfo, + _In_ ULONG Level, + _Out_ PIMAGE_RESOURCE_DIRECTORY *ResourceDirectory + ); + +// private +typedef struct _LDR_ENUM_RESOURCE_ENTRY +{ + union + { + ULONG_PTR NameOrId; + PIMAGE_RESOURCE_DIRECTORY_STRING Name; + struct + { + USHORT Id; + USHORT NameIsPresent; + }; + } Path[3]; + PVOID Data; + ULONG Size; + ULONG Reserved; +} LDR_ENUM_RESOURCE_ENTRY, *PLDR_ENUM_RESOURCE_ENTRY; + +#define NAME_FROM_RESOURCE_ENTRY(RootDirectory, Entry) \ + ((Entry)->NameIsString ? (ULONG_PTR)PTR_ADD_OFFSET((RootDirectory), (Entry)->NameOffset) : (Entry)->Id) + +NTSYSAPI +NTSTATUS +NTAPI +LdrEnumResources( + _In_ PVOID DllHandle, + _In_ PLDR_RESOURCE_INFO ResourceInfo, + _In_ ULONG Level, + _Inout_ ULONG *ResourceCount, + _Out_writes_to_opt_(*ResourceCount, *ResourceCount) PLDR_ENUM_RESOURCE_ENTRY Resources + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrFindEntryForAddress( + _In_ PVOID DllHandle, + _Out_ PLDR_DATA_TABLE_ENTRY *Entry + ); + #endif // (PHNT_MODE != PHNT_MODE_KERNEL) // Module information @@ -559,4 +774,187 @@ typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX PVOID DefaultBase; } RTL_PROCESS_MODULE_INFORMATION_EX, *PRTL_PROCESS_MODULE_INFORMATION_EX; +#if (PHNT_MODE != PHNT_MODE_KERNEL) + +NTSYSAPI +NTSTATUS +NTAPI +LdrQueryProcessModuleInformation( + _In_opt_ PRTL_PROCESS_MODULES ModuleInformation, + _In_opt_ ULONG Size, + _Out_ PULONG ReturnedSize + ); + +typedef VOID (NTAPI *PLDR_ENUM_CALLBACK)( + _In_ PLDR_DATA_TABLE_ENTRY ModuleInformation, + _In_ PVOID Parameter, + _Out_ BOOLEAN *Stop + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrEnumerateLoadedModules( + _In_ BOOLEAN ReservedFlag, + _In_ PLDR_ENUM_CALLBACK EnumProc, + _In_ PVOID Context + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrOpenImageFileOptionsKey( + _In_ PUNICODE_STRING SubKey, + _In_ BOOLEAN Wow64, + _Out_ PHANDLE NewKeyHandle + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrQueryImageFileKeyOption( + _In_ HANDLE KeyHandle, + _In_ PCWSTR ValueName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ReturnedLength + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrQueryImageFileExecutionOptions( + _In_ PUNICODE_STRING SubKey, + _In_ PCWSTR ValueName, + _In_ ULONG ValueSize, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ReturnedLength + ); + +NTSYSAPI +NTSTATUS +NTAPI +LdrQueryImageFileExecutionOptionsEx( + _In_ PUNICODE_STRING SubKey, + _In_ PCWSTR ValueName, + _In_ ULONG Type, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ReturnedLength, + _In_ BOOLEAN Wow64 + ); + +// private +typedef struct _DELAYLOAD_PROC_DESCRIPTOR +{ + ULONG ImportDescribedByName; + union + { + PCSTR Name; + ULONG Ordinal; + } Description; +} DELAYLOAD_PROC_DESCRIPTOR, *PDELAYLOAD_PROC_DESCRIPTOR; + +// private +typedef struct _DELAYLOAD_INFO +{ + ULONG Size; + PCIMAGE_DELAYLOAD_DESCRIPTOR DelayloadDescriptor; + PIMAGE_THUNK_DATA ThunkAddress; + PCSTR TargetDllName; + DELAYLOAD_PROC_DESCRIPTOR TargetApiDescriptor; + PVOID TargetModuleBase; + PVOID Unused; + ULONG LastError; +} DELAYLOAD_INFO, *PDELAYLOAD_INFO; + +// private +typedef PVOID (NTAPI *PDELAYLOAD_FAILURE_DLL_CALLBACK)( + _In_ ULONG NotificationReason, + _In_ PDELAYLOAD_INFO DelayloadInfo + ); + +// rev +typedef PVOID (NTAPI *PDELAYLOAD_FAILURE_SYSTEM_ROUTINE)( + _In_ PCSTR DllName, + _In_ PCSTR ProcName + ); + +// rev +NTSYSAPI +PVOID +NTAPI +LdrResolveDelayLoadedAPI( + _In_ PVOID ParentModuleBase, + _In_ PCIMAGE_DELAYLOAD_DESCRIPTOR DelayloadDescriptor, + _In_opt_ PDELAYLOAD_FAILURE_DLL_CALLBACK FailureDllHook, + _In_opt_ PDELAYLOAD_FAILURE_SYSTEM_ROUTINE FailureSystemHook, // kernel32.DelayLoadFailureHook + _Out_ PIMAGE_THUNK_DATA ThunkAddress, + _Reserved_ ULONG Flags + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrResolveDelayLoadsFromDll( + _In_ PVOID ParentBase, + _In_ PCSTR TargetDllName, + _Reserved_ ULONG Flags + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrSetDefaultDllDirectories( + _In_ ULONG DirectoryFlags + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrShutdownProcess( + VOID + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrShutdownThread( + VOID + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +LdrSetImplicitPathOptions( + _In_ ULONG ImplicitPathOptions + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +LdrControlFlowGuardEnforced( + VOID + ); + +#if (PHNT_VERSION >= PHNT_19H1) +// rev +NTSYSAPI +BOOLEAN +NTAPI +LdrIsModuleSxsRedirected( + _In_ PVOID DllHandle + ); +#endif + +#endif // (PHNT_MODE != PHNT_MODE_KERNEL) + #endif diff --git a/phnt/include/ntlpcapi.h b/phnt/include/ntlpcapi.h index 3adcc7021f75..e17fe0ae3e78 100644 --- a/phnt/include/ntlpcapi.h +++ b/phnt/include/ntlpcapi.h @@ -486,7 +486,7 @@ typedef struct _ALPC_HANDLE_ATTR } ALPC_HANDLE_ATTR, *PALPC_HANDLE_ATTR; #define ALPC_SECFLG_CREATE_HANDLE 0x20000 // dbg - +#define ALPC_SECFLG_NOSECTIONHANDLE 0x40000 // private typedef struct _ALPC_SECURITY_ATTR { @@ -880,6 +880,10 @@ AlpcGetHeaderSize( _In_ ULONG Flags ); +#define ALPC_ATTRFLG_ALLOCATEDATTR 0x20000000 +#define ALPC_ATTRFLG_VALIDATTR 0x40000000 +#define ALPC_ATTRFLG_KEEPRUNNINGATTR 0x60000000 + NTSYSAPI NTSTATUS NTAPI diff --git a/phnt/include/ntmisc.h b/phnt/include/ntmisc.h index 7ba023de5fb2..1d32c9ae8ae8 100644 --- a/phnt/include/ntmisc.h +++ b/phnt/include/ntmisc.h @@ -1,18 +1,6 @@ #ifndef _NTMISC_H #define _NTMISC_H -// Boot graphics - -#if (PHNT_VERSION >= PHNT_WIN7) -// rev -NTSYSCALLAPI -NTSTATUS -NTAPI -NtDrawText( - _In_ PUNICODE_STRING Text - ); -#endif - // Filter manager #define FLT_PORT_CONNECT 0x0001 diff --git a/phnt/include/ntmmapi.h b/phnt/include/ntmmapi.h index 9499ee236911..98bc4821a697 100644 --- a/phnt/include/ntmmapi.h +++ b/phnt/include/ntmmapi.h @@ -24,7 +24,7 @@ #define PAGE_ENCLAVE_UNVALIDATED 0x20000000 // Region and section constants - +#if (PHNT_MODE != PHNT_MODE_KERNEL) #define MEM_COMMIT 0x1000 #define MEM_RESERVE 0x2000 #define MEM_DECOMMIT 0x4000 @@ -34,19 +34,26 @@ #define MEM_MAPPED 0x40000 #define MEM_RESET 0x80000 #define MEM_TOP_DOWN 0x100000 +#endif #define MEM_WRITE_WATCH 0x200000 #define MEM_PHYSICAL 0x400000 #define MEM_ROTATE 0x800000 #define MEM_DIFFERENT_IMAGE_BASE_OK 0x800000 +#if (PHNT_MODE != PHNT_MODE_KERNEL) #define MEM_RESET_UNDO 0x1000000 +#endif #define MEM_LARGE_PAGES 0x20000000 #define MEM_4MB_PAGES 0x80000000 +#if (PHNT_MODE != PHNT_MODE_KERNEL) #define SEC_FILE 0x800000 +#endif #define SEC_IMAGE 0x1000000 #define SEC_PROTECTED_IMAGE 0x2000000 +#if (PHNT_MODE != PHNT_MODE_KERNEL) #define SEC_RESERVE 0x4000000 #define SEC_COMMIT 0x8000000 +#endif #define SEC_NOCACHE 0x10000000 #define SEC_WRITECOMBINE 0x40000000 #define SEC_LARGE_PAGES 0x80000000 @@ -55,6 +62,7 @@ #endif +#if (PHNT_MODE != PHNT_MODE_KERNEL) // private typedef enum _MEMORY_INFORMATION_CLASS { @@ -66,21 +74,22 @@ typedef enum _MEMORY_INFORMATION_CLASS MemorySharedCommitInformation, // MEMORY_SHARED_COMMIT_INFORMATION MemoryImageInformation, // MEMORY_IMAGE_INFORMATION MemoryRegionInformationEx, - MemoryPrivilegedBasicInformation + MemoryPrivilegedBasicInformation, + MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3 + MemoryBasicInformationCapped } MEMORY_INFORMATION_CLASS; - -#if (PHNT_MODE == PHNT_MODE_KERNEL) - -typedef struct _MEMORY_BASIC_INFORMATION -{ - PVOID BaseAddress; - PVOID AllocationBase; - ULONG AllocationProtect; - SIZE_T RegionSize; - ULONG State; - ULONG Protect; - ULONG Type; -} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; +#else +#define MemoryBasicInformation 0x0 +#define MemoryWorkingSetInformation 0x1 +#define MemoryMappedFilenameInformation 0x2 +#define MemoryRegionInformation 0x3 +#define MemoryWorkingSetExInformation 0x4 +#define MemorySharedCommitInformation 0x5 +#define MemoryImageInformation 0x6 +#define MemoryRegionInformationEx 0x7 +#define MemoryPrivilegedBasicInformation 0x8 +#define MemoryEnclaveImageInformation 0x9 +#define MemoryBasicInformationCapped 0xA #endif typedef struct _MEMORY_WORKING_SET_BLOCK @@ -118,13 +127,26 @@ typedef struct _MEMORY_REGION_INFORMATION ULONG MappedPageFile : 1; ULONG MappedPhysical : 1; ULONG DirectMapped : 1; - ULONG Reserved : 26; + ULONG SoftwareEnclave : 1; // REDSTONE3 + ULONG PageSize64K : 1; + ULONG PlaceholderReservation : 1; // REDSTONE4 + ULONG Reserved : 23; }; }; SIZE_T RegionSize; SIZE_T CommitSize; + ULONG_PTR PartitionId; // 19H1 } MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; +// private +typedef enum _MEMORY_WORKING_SET_EX_LOCATION +{ + MemoryLocationInvalid, + MemoryLocationResident, + MemoryLocationPagefile, + MemoryLocationReserved +} MEMORY_WORKING_SET_EX_LOCATION; + // private typedef struct _MEMORY_WORKING_SET_EX_BLOCK { @@ -143,8 +165,9 @@ typedef struct _MEMORY_WORKING_SET_EX_BLOCK ULONG_PTR Reserved : 3; ULONG_PTR SharedOriginal : 1; ULONG_PTR Bad : 1; + ULONG_PTR Win32GraphicsProtection : 4; // 19H1 #ifdef _WIN64 - ULONG_PTR ReservedUlong : 32; + ULONG_PTR ReservedUlong : 28; #endif }; struct @@ -196,11 +219,20 @@ typedef struct _MEMORY_IMAGE_INFORMATION { ULONG ImagePartialMap : 1; ULONG ImageNotExecutable : 1; - ULONG Reserved : 30; + ULONG ImageSigningLevel : 4; // REDSTONE3 + ULONG Reserved : 26; }; }; } MEMORY_IMAGE_INFORMATION, *PMEMORY_IMAGE_INFORMATION; +// private +typedef struct _MEMORY_ENCLAVE_IMAGE_INFORMATION +{ + MEMORY_IMAGE_INFORMATION ImageInfo; + UCHAR UniqueID[32]; + UCHAR AuthorID[32]; +} MEMORY_ENCLAVE_IMAGE_INFORMATION, *PMEMORY_ENCLAVE_IMAGE_INFORMATION; + #define MMPFNLIST_ZERO 0 #define MMPFNLIST_FREE 1 #define MMPFNLIST_STANDBY 2 @@ -228,7 +260,7 @@ typedef struct _MEMORY_FRAME_INFORMATION { ULONGLONG UseDescription : 4; // MMPFNUSE_* ULONGLONG ListDescription : 3; // MMPFNLIST_* - ULONGLONG Reserved0 : 1; // reserved for future expansion + ULONGLONG Cold : 1; // 19H1 ULONGLONG Pinned : 1; // 1 - pinned, 0 - not pinned ULONGLONG DontUse : 48; // *_INFORMATION overlay ULONGLONG Priority : 3; // rev @@ -296,8 +328,8 @@ typedef struct _MMPFN_MEMSNAP_INFORMATION typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation, - SectionImageInformation, + SectionBasicInformation, // q; SECTION_BASIC_INFORMATION + SectionImageInformation, // q; SECTION_IMAGE_INFORMATION SectionRelocationInformation, // name:wow64:whNtQuerySection_SectionRelocationInformation SectionOriginalBaseInformation, // PVOID BaseAddress SectionInternalImageInformation, // SECTION_INTERNAL_IMAGE_INFORMATION // since REDSTONE2 @@ -369,10 +401,8 @@ typedef struct _SECTION_INTERNAL_IMAGE_INFORMATION ULONG ExtendedFlags; struct { - ULONG ImageReturnFlowGuardEnabled : 1; - ULONG ImageReturnFlowGuardStrict : 1; ULONG ImageExportSuppressionEnabled : 1; - ULONG Reserved : 29; + ULONG Reserved : 31; }; }; } SECTION_INTERNAL_IMAGE_INFORMATION, *PSECTION_INTERNAL_IMAGE_INFORMATION; @@ -461,7 +491,7 @@ NTSTATUS NTAPI NtQueryVirtualMemory( _In_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, + _In_opt_ PVOID BaseAddress, _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, _Out_writes_bytes_(MemoryInformationLength) PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, @@ -471,12 +501,14 @@ NtQueryVirtualMemory( #endif // begin_private - +#if (PHNT_MODE != PHNT_MODE_KERNEL) typedef enum _VIRTUAL_MEMORY_INFORMATION_CLASS { - VmPrefetchInformation, + VmPrefetchInformation, // ULONG VmPagePriorityInformation, - VmCfgCallTargetInformation + VmCfgCallTargetInformation, // CFG_CALL_TARGET_LIST_INFORMATION // REDSTONE2 + VmPageDirtyStateInformation, // REDSTONE3 + VmImageHotPatchInformation // 19H1 } VIRTUAL_MEMORY_INFORMATION_CLASS; typedef struct _MEMORY_RANGE_ENTRY @@ -485,6 +517,16 @@ typedef struct _MEMORY_RANGE_ENTRY SIZE_T NumberOfBytes; } MEMORY_RANGE_ENTRY, *PMEMORY_RANGE_ENTRY; +typedef struct _CFG_CALL_TARGET_LIST_INFORMATION +{ + ULONG NumberOfEntries; + ULONG Reserved; + PULONG NumberOfEntriesProcessed; + PCFG_CALL_TARGET_INFO CallTargetInfo; + PVOID Section; // since REDSTONE5 + ULONGLONG FileOffset; +} CFG_CALL_TARGET_LIST_INFORMATION, *PCFG_CALL_TARGET_LIST_INFORMATION; +#endif // end_private #if (PHNT_MODE != PHNT_MODE_KERNEL) @@ -544,6 +586,23 @@ NtCreateSection( _In_opt_ HANDLE FileHandle ); +#if (PHNT_VERSION >= PHNT_REDSTONE5) +NTSYSCALLAPI +NTSTATUS +NTAPI +NtCreateSectionEx( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PLARGE_INTEGER MaximumSize, + _In_ ULONG SectionPageProtection, + _In_ ULONG AllocationAttributes, + _In_opt_ HANDLE FileHandle, + _In_ PMEM_EXTENDED_PARAMETER ExtendedParameters, + _In_ ULONG ExtendedParameterCount + ); +#endif + NTSYSCALLAPI NTSTATUS NTAPI @@ -619,6 +678,14 @@ NtAreMappedFilesTheSame( // Partitions +#ifndef MEMORY_PARTITION_QUERY_ACCESS +#define MEMORY_PARTITION_QUERY_ACCESS 0x0001 +#define MEMORY_PARTITION_MODIFY_ACCESS 0x0002 +#define MEMORY_PARTITION_ALL_ACCESS \ + (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \ + MEMORY_PARTITION_QUERY_ACCESS | MEMORY_PARTITION_MODIFY_ACCESS) +#endif + // private typedef enum _MEMORY_PARTITION_INFORMATION_CLASS { @@ -627,7 +694,8 @@ typedef enum _MEMORY_PARTITION_INFORMATION_CLASS SystemMemoryPartitionAddPagefile, // s: MEMORY_PARTITION_PAGEFILE_INFORMATION SystemMemoryPartitionCombineMemory, // q; s: MEMORY_PARTITION_PAGE_COMBINE_INFORMATION SystemMemoryPartitionInitialAddMemory, // q; s: MEMORY_PARTITION_INITIAL_ADD_INFORMATION - SystemMemoryPartitionGetMemoryEvents // MEMORY_PARTITION_MEMORY_EVENTS_INFORMATION // since REDSTONE2 + SystemMemoryPartitionGetMemoryEvents, // MEMORY_PARTITION_MEMORY_EVENTS_INFORMATION // since REDSTONE2 + SystemMemoryPartitionMax } MEMORY_PARTITION_INFORMATION_CLASS; // private @@ -646,10 +714,11 @@ typedef struct _MEMORY_PARTITION_CONFIGURATION_INFORMATION ULONG_PTR ZeroPages; ULONG_PTR FreePages; ULONG_PTR StandbyPages; - ULONG StandbyPageCountByPriority[8]; // since REDSTONE2 - ULONG RepurposedPagesByPriority[8]; - ULONG MaximumCommitLimit; - ULONG DonatedPagesToPartitions; + ULONG_PTR StandbyPageCountByPriority[8]; // since REDSTONE2 + ULONG_PTR RepurposedPagesByPriority[8]; + ULONG_PTR MaximumCommitLimit; + ULONG_PTR DonatedPagesToPartitions; + ULONG PartitionId; // since REDSTONE3 } MEMORY_PARTITION_CONFIGURATION_INFORMATION, *PMEMORY_PARTITION_CONFIGURATION_INFORMATION; // private @@ -704,7 +773,13 @@ typedef struct _MEMORY_PARTITION_MEMORY_EVENTS_INFORMATION ULONG Spare : 31; }; ULONG AllFlags; - }; + } Flags; + + ULONG HandleAttributes; + ULONG DesiredAccess; + HANDLE LowCommitCondition; // \KernelObjects\LowCommitCondition + HANDLE HighCommitCondition; // \KernelObjects\HighCommitCondition + HANDLE MaximumCommitCondition; // \KernelObjects\MaximumCommitCondition } MEMORY_PARTITION_MEMORY_EVENTS_INFORMATION, *PMEMORY_PARTITION_MEMORY_EVENTS_INFORMATION; #if (PHNT_MODE != PHNT_MODE_KERNEL) @@ -856,4 +931,69 @@ NtFlushWriteBuffer( #endif +// Enclave support + +NTSYSAPI +NTSTATUS +NTAPI +NtCreateEnclave( + _In_ HANDLE ProcessHandle, + _Inout_ PVOID* BaseAddress, + _In_ ULONG_PTR ZeroBits, + _In_ SIZE_T Size, + _In_ SIZE_T InitialCommitment, + _In_ ULONG EnclaveType, + _In_reads_bytes_(EnclaveInformationLength) PVOID EnclaveInformation, + _In_ ULONG EnclaveInformationLength, + _Out_opt_ PULONG EnclaveError + ); + +NTSYSAPI +NTSTATUS +NTAPI +NtLoadEnclaveData( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ SIZE_T BufferSize, + _In_ ULONG Protect, + _In_reads_bytes_(PageInformationLength) PVOID PageInformation, + _In_ ULONG PageInformationLength, + _Out_opt_ PSIZE_T NumberOfBytesWritten, + _Out_opt_ PULONG EnclaveError + ); + +NTSYSAPI +NTSTATUS +NTAPI +NtInitializeEnclave( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_reads_bytes_(EnclaveInformationLength) PVOID EnclaveInformation, + _In_ ULONG EnclaveInformationLength, + _Out_opt_ PULONG EnclaveError + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +NtTerminateEnclave( + _In_ PVOID BaseAddress, + _In_ BOOLEAN WaitForThread + ); + +#if (PHNT_MODE != PHNT_MODE_KERNEL) +// rev +NTSYSAPI +NTSTATUS +NTAPI +NtCallEnclave( + _In_ PENCLAVE_ROUTINE Routine, + _In_ PVOID Parameter, + _In_ BOOLEAN WaitForThread, + _Out_opt_ PVOID *ReturnValue + ); +#endif + #endif diff --git a/phnt/include/ntnls.h b/phnt/include/ntnls.h index c50b5d1ff100..dd6778f02df7 100644 --- a/phnt/include/ntnls.h +++ b/phnt/include/ntnls.h @@ -1,3 +1,23 @@ +/* + * Process Hacker - + * National Language Support functions + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTNLS_H #define _NTNLS_H @@ -27,4 +47,10 @@ typedef struct _NLSTABLEINFO PUSHORT LowerCaseTable; } NLSTABLEINFO, *PNLSTABLEINFO; +#if (PHNT_MODE != PHNT_MODE_KERNEL) +NTSYSAPI USHORT NlsAnsiCodePage; +NTSYSAPI BOOLEAN NlsMbCodePageTag; +NTSYSAPI BOOLEAN NlsMbOemCodePageTag; +#endif + #endif diff --git a/phnt/include/ntobapi.h b/phnt/include/ntobapi.h index cf5cdd016314..2a281844528f 100644 --- a/phnt/include/ntobapi.h +++ b/phnt/include/ntobapi.h @@ -38,10 +38,12 @@ typedef enum _OBJECT_INFORMATION_CLASS MaxObjectInfoClass } OBJECT_INFORMATION_CLASS; #else +#define ObjectBasicInformation 0 #define ObjectNameInformation 1 #define ObjectTypesInformation 3 #define ObjectHandleFlagInformation 4 #define ObjectSessionInformation 5 +#define ObjectSessionObjectInformation 6 #endif typedef struct _OBJECT_BASIC_INFORMATION @@ -112,7 +114,7 @@ NTSYSCALLAPI NTSTATUS NTAPI NtQueryObject( - _In_ HANDLE Handle, + _In_opt_ HANDLE Handle, _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, _Out_writes_bytes_opt_(ObjectInformationLength) PVOID ObjectInformation, _In_ ULONG ObjectInformationLength, diff --git a/phnt/include/ntpebteb.h b/phnt/include/ntpebteb.h index 5b6801e5dd9c..bfe23b313976 100644 --- a/phnt/include/ntpebteb.h +++ b/phnt/include/ntpebteb.h @@ -4,6 +4,56 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS *PRTL_USER_PROCESS_PARAMETERS; typedef struct _RTL_CRITICAL_SECTION *PRTL_CRITICAL_SECTION; +// private +typedef struct _ACTIVATION_CONTEXT_STACK +{ + struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame; + LIST_ENTRY FrameListCache; + ULONG Flags; + ULONG NextCookieSequenceNumber; + ULONG StackId; +} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK; + +// private +typedef struct _API_SET_NAMESPACE +{ + ULONG Version; + ULONG Size; + ULONG Flags; + ULONG Count; + ULONG EntryOffset; + ULONG HashOffset; + ULONG HashFactor; +} API_SET_NAMESPACE, *PAPI_SET_NAMESPACE; + +// private +typedef struct _API_SET_HASH_ENTRY +{ + ULONG Hash; + ULONG Index; +} API_SET_HASH_ENTRY, *PAPI_SET_HASH_ENTRY; + +// private +typedef struct _API_SET_NAMESPACE_ENTRY +{ + ULONG Flags; + ULONG NameOffset; + ULONG NameLength; + ULONG HashedLength; + ULONG ValueOffset; + ULONG ValueCount; +} API_SET_NAMESPACE_ENTRY, *PAPI_SET_NAMESPACE_ENTRY; + +// private +typedef struct _API_SET_VALUE_ENTRY +{ + ULONG Flags; + ULONG NameOffset; + ULONG NameLength; + ULONG ValueOffset; + ULONG ValueLength; +} API_SET_VALUE_ENTRY, *PAPI_SET_VALUE_ENTRY; + // symbols typedef struct _PEB { @@ -34,8 +84,8 @@ typedef struct _PEB PVOID SubSystemData; PVOID ProcessHeap; PRTL_CRITICAL_SECTION FastPebLock; - PVOID AtlThunkSListPtr; PVOID IFEOKey; + PSLIST_HEADER AtlThunkSListPtr; union { ULONG CrossProcessFlags; @@ -48,7 +98,8 @@ typedef struct _PEB ULONG ProcessUsingFTH : 1; ULONG ProcessPreviouslyThrottled : 1; ULONG ProcessCurrentlyThrottled : 1; - ULONG ReservedBits0 : 25; + ULONG ProcessImagesHotPatched : 1; // REDSTONE5 + ULONG ReservedBits0 : 24; }; }; union @@ -56,23 +107,25 @@ typedef struct _PEB PVOID KernelCallbackTable; PVOID UserSharedInfoPtr; }; - ULONG SystemReserved[1]; + ULONG SystemReserved; ULONG AtlThunkSListPtr32; - PVOID ApiSetMap; + PAPI_SET_NAMESPACE ApiSetMap; ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[2]; - PVOID ReadOnlySharedMemoryBase; - PVOID HotpatchInformation; + + PVOID ReadOnlySharedMemoryBase; + PVOID SharedData; // HotpatchInformation PVOID *ReadOnlyStaticServerData; - PVOID AnsiCodePageData; - PVOID OemCodePageData; - PVOID UnicodeCaseTableData; + + PVOID AnsiCodePageData; // PCPTABLEINFO + PVOID OemCodePageData; // PCPTABLEINFO + PVOID UnicodeCaseTableData; // PNLSTABLEINFO ULONG NumberOfProcessors; ULONG NtGlobalFlag; - LARGE_INTEGER CriticalSectionTimeout; + ULARGE_INTEGER CriticalSectionTimeout; SIZE_T HeapSegmentReserve; SIZE_T HeapSegmentCommit; SIZE_T HeapDeCommitTotalFreeThreshold; @@ -80,7 +133,7 @@ typedef struct _PEB ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; - PVOID *ProcessHeaps; + PVOID *ProcessHeaps; // PHEAP PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; @@ -108,26 +161,28 @@ typedef struct _PEB ULARGE_INTEGER AppCompatFlags; ULARGE_INTEGER AppCompatFlagsUser; PVOID pShimData; - PVOID AppCompatInfo; + PVOID AppCompatInfo; // APPCOMPAT_EXE_DATA UNICODE_STRING CSDVersion; - PVOID ActivationContextData; - PVOID ProcessAssemblyStorageMap; - PVOID SystemDefaultActivationContextData; - PVOID SystemAssemblyStorageMap; + PVOID ActivationContextData; // ACTIVATION_CONTEXT_DATA + PVOID ProcessAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP + PVOID SystemDefaultActivationContextData; // ACTIVATION_CONTEXT_DATA + PVOID SystemAssemblyStorageMap; // ASSEMBLY_STORAGE_MAP SIZE_T MinimumStackCommit; - PVOID *FlsCallback; - LIST_ENTRY FlsListHead; - PVOID FlsBitmap; - ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; - ULONG FlsHighIndex; + PVOID SparePointers[4]; // 19H1 (previously FlsCallback to FlsHighIndex) + ULONG SpareUlongs[5]; // 19H1 + //PVOID* FlsCallback; + //LIST_ENTRY FlsListHead; + //PVOID FlsBitmap; + //ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; + //ULONG FlsHighIndex; PVOID WerRegistrationData; PVOID WerShipAssertPtr; - PVOID pContextData; + PVOID pUnused; // pContextData PVOID pImageHeaderHash; union { @@ -141,11 +196,39 @@ typedef struct _PEB }; }; ULONGLONG CsrServerReadOnlySharedMemoryBase; - PVOID TppWorkerpListLock; + PRTL_CRITICAL_SECTION TppWorkerpListLock; LIST_ENTRY TppWorkerpList; PVOID WaitOnAddressHashTable[128]; + PVOID TelemetryCoverageHeader; // REDSTONE3 + ULONG CloudFileFlags; + ULONG CloudFileDiagFlags; // REDSTONE4 + CHAR PlaceholderCompatibilityMode; + CHAR PlaceholderCompatibilityModeReserved[7]; + struct _LEAP_SECOND_DATA *LeapSecondData; // REDSTONE5 + union + { + ULONG LeapSecondFlags; + struct + { + ULONG SixtySecondEnabled : 1; + ULONG Reserved : 31; + }; + }; + ULONG NtGlobalFlag2; } PEB, *PPEB; +#ifdef _WIN64 +C_ASSERT(FIELD_OFFSET(PEB, SessionId) == 0x2C0); +//C_ASSERT(sizeof(PEB) == 0x7B0); // REDSTONE3 +//C_ASSERT(sizeof(PEB) == 0x7B8); // REDSTONE4 +C_ASSERT(sizeof(PEB) == 0x7C8); // REDSTONE5 // 19H1 +#else +C_ASSERT(FIELD_OFFSET(PEB, SessionId) == 0x1D4); +//C_ASSERT(sizeof(PEB) == 0x468); // REDSTONE3 +//C_ASSERT(sizeof(PEB) == 0x470); // REDSTONE4 +C_ASSERT(sizeof(PEB) == 0x480); // REDSTONE5 // 19H1 +#endif + #define GDI_BATCH_BUFFER_SIZE 310 typedef struct _GDI_TEB_BATCH @@ -188,17 +271,33 @@ typedef struct _TEB LCID CurrentLocale; ULONG FpSoftwareStatusRegister; PVOID ReservedForDebuggerInstrumentation[16]; - PVOID SystemReserved1[37]; +#ifdef _WIN64 + PVOID SystemReserved1[30]; +#else + PVOID SystemReserved1[26]; +#endif + + CHAR PlaceholderCompatibilityMode; + CHAR PlaceholderReserved[11]; + ULONG ProxiedProcessId; + ACTIVATION_CONTEXT_STACK ActivationStack; + UCHAR WorkingOnBehalfTicket[8]; NTSTATUS ExceptionCode; - PVOID ActivationContextStackPointer; + PACTIVATION_CONTEXT_STACK ActivationContextStackPointer; ULONG_PTR InstrumentationCallbackSp; ULONG_PTR InstrumentationCallbackPreviousPc; ULONG_PTR InstrumentationCallbackPreviousSp; +#ifdef _WIN64 ULONG TxFsContext; +#endif BOOLEAN InstrumentationCallbackDisabled; +#ifndef _WIN64 + UCHAR SpareBytes[23]; + ULONG TxFsContext; +#endif GDI_TEB_BATCH GdiTebBatch; CLIENT_ID RealClientId; HANDLE GdiCachedProcessHandle; diff --git a/phnt/include/ntpfapi.h b/phnt/include/ntpfapi.h index aa635bd09692..0f5c1321f898 100644 --- a/phnt/include/ntpfapi.h +++ b/phnt/include/ntpfapi.h @@ -140,7 +140,7 @@ typedef struct _PF_PRIVSOURCE_INFO ULONG Spare : 28; } PF_PRIVSOURCE_INFO, *PPF_PRIVSOURCE_INFO; -#define PF_PRIVSOURCE_QUERY_REQUEST_VERSION 3 +#define PF_PRIVSOURCE_QUERY_REQUEST_VERSION 8 typedef struct _PF_PRIVSOURCE_QUERY_REQUEST { diff --git a/phnt/include/ntpoapi.h b/phnt/include/ntpoapi.h index 1ad5c053132a..0270f61ce02d 100644 --- a/phnt/include/ntpoapi.h +++ b/phnt/include/ntpoapi.h @@ -56,7 +56,7 @@ typedef struct _COUNTED_REASON_CONTEXT /** \endcond */ #endif -typedef enum +typedef enum _POWER_STATE_HANDLER_TYPE { PowerStateSleeping1 = 0, PowerStateSleeping2 = 1, diff --git a/phnt/include/ntpsapi.h b/phnt/include/ntpsapi.h index 50d71d513272..faa9065b4092 100644 --- a/phnt/include/ntpsapi.h +++ b/phnt/include/ntpsapi.h @@ -44,7 +44,7 @@ #define GDI_HANDLE_BUFFER_SIZE32 34 #define GDI_HANDLE_BUFFER_SIZE64 60 -#ifndef WIN64 +#ifndef _WIN64 #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 #else #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 @@ -55,7 +55,7 @@ typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; -#define FLS_MAXIMUM_AVAILABLE 128 +//#define FLS_MAXIMUM_AVAILABLE 128 #define TLS_MINIMUM_AVAILABLE 64 #define TLS_EXPANSION_SLOTS 1024 @@ -105,7 +105,7 @@ typedef enum _PROCESSINFOCLASS ProcessBasePriority, // s: KPRIORITY ProcessRaisePriority, // s: ULONG ProcessDebugPort, // q: HANDLE - ProcessExceptionPort, // s: HANDLE + ProcessExceptionPort, // s: PROCESS_EXCEPTION_PORT ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN ProcessLdtInformation, // qs: PROCESS_LDT_INFORMATION // 10 ProcessLdtSize, // s: PROCESS_LDT_SIZE @@ -132,12 +132,12 @@ typedef enum _PROCESSINFOCLASS ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enables ProcessIoPriority, // qs: IO_PRIORITY_HINT ProcessExecuteFlags, // qs: ULONG - ProcessResourceManagement, + ProcessResourceManagement, // ProcessTlsInformation // PROCESS_TLS_INFORMATION ProcessCookie, // q: ULONG ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION // since VISTA - ProcessPagePriority, // q: ULONG - ProcessInstrumentationCallback, // 40 + ProcessPagePriority, // q: PAGE_PRIORITY_INFORMATION + ProcessInstrumentationCallback, // qs: PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION // 40 ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[] ProcessImageFileNameWin32, // q: UNICODE_STRING @@ -146,16 +146,16 @@ typedef enum _PROCESSINFOCLASS ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE ProcessGroupInformation, // q: USHORT[] ProcessTokenVirtualizationEnabled, // s: ULONG - ProcessConsoleHostProcess, // q: ULONG_PTR + ProcessConsoleHostProcess, // q: ULONG_PTR // ProcessOwnerInformation ProcessWindowInformation, // q: PROCESS_WINDOW_INFORMATION // 50 ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8 ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION ProcessDynamicFunctionTableInformation, - ProcessHandleCheckingMode, + ProcessHandleCheckingMode, // qs: ULONG; s: 0 disables, otherwise enables ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION ProcessWorkingSetControl, // s: PROCESS_WORKING_SET_CONTROL - ProcessHandleTable, // since WINBLUE + ProcessHandleTable, // q: ULONG[] // since WINBLUE ProcessCheckStackExtentsMode, ProcessCommandLineInformation, // q: UNICODE_STRING // 60 ProcessProtectionInformation, // q: PS_PROTECTION @@ -168,18 +168,35 @@ typedef enum _PROCESSINFOCLASS ProcessSubsystemProcess, ProcessJobMemoryInformation, // PROCESS_JOB_MEMORY_INFO ProcessInPrivate, // since THRESHOLD2 // 70 - ProcessRaiseUMExceptionOnInvalidHandleClose, + ProcessRaiseUMExceptionOnInvalidHandleClose, // qs: ULONG; s: 0 disables, otherwise enables ProcessIumChallengeResponse, ProcessChildProcessInformation, // PROCESS_CHILD_PROCESS_INFORMATION ProcessHighGraphicsPriorityInformation, - ProcessSubsystemInformation, // since REDSTONE2 - ProcessEnergyValues, - ProcessActivityThrottleState, - ProcessActivityThrottlePolicy, + ProcessSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 + ProcessEnergyValues, // PROCESS_ENERGY_VALUES, PROCESS_EXTENDED_ENERGY_VALUES + ProcessActivityThrottleState, // PROCESS_ACTIVITY_THROTTLE_STATE + ProcessActivityThrottlePolicy, // PROCESS_ACTIVITY_THROTTLE_POLICY ProcessWin32kSyscallFilterInformation, - ProcessDisableSystemAllowedCpuSets, - ProcessWakeInformation, - ProcessEnergyTrackingState, + ProcessDisableSystemAllowedCpuSets, // 80 + ProcessWakeInformation, // PROCESS_WAKE_INFORMATION + ProcessEnergyTrackingState, // PROCESS_ENERGY_TRACKING_STATE + ProcessManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3 + ProcessCaptureTrustletLiveDump, + ProcessTelemetryCoverage, + ProcessEnclaveInformation, + ProcessEnableReadWriteVmLogging, // PROCESS_READWRITEVM_LOGGING_INFORMATION + ProcessUptimeInformation, // PROCESS_UPTIME_INFORMATION + ProcessImageSection, // q: HANDLE + ProcessDebugAuthInformation, // since REDSTONE4 // 90 + ProcessSystemResourceManagement, // PROCESS_SYSTEM_RESOURCE_MANAGEMENT + ProcessSequenceNumber, // q: ULONGLONG + ProcessLoaderDetour, // since REDSTONE5 + ProcessSecurityDomainInformation, // PROCESS_SECURITY_DOMAIN_INFORMATION + ProcessCombineSecurityDomainsInformation, // PROCESS_COMBINE_SECURITY_DOMAINS_INFORMATION + ProcessEnableLogging, // PROCESS_LOGGING_INFORMATION + ProcessLeapSecondInformation, // PROCESS_LEAP_SECOND_INFORMATION + ProcessFiberShadowStackAllocation, // PROCESS_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION // since 19H1 + ProcessFreeFiberShadowStackAllocation, // PROCESS_FREE_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION MaxProcessInfoClass } PROCESSINFOCLASS; #endif @@ -230,11 +247,14 @@ typedef enum _THREADINFOCLASS ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40 ThreadActualGroupAffinity, // since THRESHOLD2 ThreadDynamicCodePolicyInfo, - ThreadExplicitCaseSensitivity, + ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables ThreadWorkOnBehalfTicket, - ThreadSubsystemInformation, // since REDSTONE2 + ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 ThreadDbgkWerReportActive, ThreadAttachContainer, + ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3 + ThreadPowerThrottlingState, // THREAD_POWER_THROTTLING_STATE + ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50 MaxThreadInfoClass } THREADINFOCLASS; #endif @@ -344,6 +364,15 @@ typedef struct _POOLED_USAGE_AND_LIMITS SIZE_T PagefileLimit; } POOLED_USAGE_AND_LIMITS, *PPOOLED_USAGE_AND_LIMITS; +#define PROCESS_EXCEPTION_PORT_ALL_STATE_BITS 0x00000003 +#define PROCESS_EXCEPTION_PORT_ALL_STATE_FLAGS ((ULONG_PTR)((1UL << PROCESS_EXCEPTION_PORT_ALL_STATE_BITS) - 1)) + +typedef struct _PROCESS_EXCEPTION_PORT +{ + _In_ HANDLE ExceptionPortHandle; // Handle to the exception port. No particular access required. + _Inout_ ULONG StateFlags; // Miscellaneous state flags to be cached along with the exception port in the kernel. +} PROCESS_EXCEPTION_PORT, *PPROCESS_EXCEPTION_PORT; + typedef struct _PROCESS_ACCESS_TOKEN { HANDLE Token; // needs TOKEN_ASSIGN_PRIMARY access @@ -439,21 +468,29 @@ typedef struct _PROCESS_SESSION_INFORMATION ULONG SessionId; } PROCESS_SESSION_INFORMATION, *PPROCESS_SESSION_INFORMATION; +#define PROCESS_HANDLE_EXCEPTIONS_ENABLED 0x00000001 + +#define PROCESS_HANDLE_RAISE_EXCEPTION_ON_INVALID_HANDLE_CLOSE_DISABLED 0x00000000 +#define PROCESS_HANDLE_RAISE_EXCEPTION_ON_INVALID_HANDLE_CLOSE_ENABLED 0x00000001 + typedef struct _PROCESS_HANDLE_TRACING_ENABLE { - ULONG Flags; // 0 to disable, 1 to enable + ULONG Flags; } PROCESS_HANDLE_TRACING_ENABLE, *PPROCESS_HANDLE_TRACING_ENABLE; +#define PROCESS_HANDLE_TRACING_MAX_SLOTS 0x20000 + typedef struct _PROCESS_HANDLE_TRACING_ENABLE_EX { - ULONG Flags; // 0 to disable, 1 to enable + ULONG Flags; ULONG TotalSlots; } PROCESS_HANDLE_TRACING_ENABLE_EX, *PPROCESS_HANDLE_TRACING_ENABLE_EX; #define PROCESS_HANDLE_TRACING_MAX_STACKS 16 -#define HANDLE_TRACE_DB_OPEN 1 -#define HANDLE_TRACE_DB_CLOSE 2 -#define HANDLE_TRACE_DB_BADREF 3 + +#define PROCESS_HANDLE_TRACE_TYPE_OPEN 1 +#define PROCESS_HANDLE_TRACE_TYPE_CLOSE 2 +#define PROCESS_HANDLE_TRACE_TYPE_BADREF 3 typedef struct _PROCESS_HANDLE_TRACING_ENTRY { @@ -472,6 +509,42 @@ typedef struct _PROCESS_HANDLE_TRACING_QUERY #endif +// private +typedef struct _THREAD_TLS_INFORMATION +{ + ULONG Flags; + PVOID NewTlsData; + PVOID OldTlsData; + HANDLE ThreadId; +} THREAD_TLS_INFORMATION, *PTHREAD_TLS_INFORMATION; + +// private +typedef enum _PROCESS_TLS_INFORMATION_TYPE +{ + ProcessTlsReplaceIndex, + ProcessTlsReplaceVector, + MaxProcessTlsOperation +} PROCESS_TLS_INFORMATION_TYPE, *PPROCESS_TLS_INFORMATION_TYPE; + +// private +typedef struct _PROCESS_TLS_INFORMATION +{ + ULONG Flags; + ULONG OperationType; + ULONG ThreadDataCount; + ULONG TlsIndex; + ULONG PreviousCount; + THREAD_TLS_INFORMATION ThreadData[1]; +} PROCESS_TLS_INFORMATION, *PPROCESS_TLS_INFORMATION; + +// private +typedef struct _PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION +{ + ULONG Version; + ULONG Reserved; + PVOID Callback; +} PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION, *PPROCESS_INSTRUMENTATION_CALLBACK_INFORMATION; + // private typedef struct _PROCESS_STACK_ALLOCATION_INFORMATION { @@ -572,6 +645,10 @@ typedef struct _PROCESS_MITIGATION_POLICY_INFORMATION PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY SignaturePolicy; PROCESS_MITIGATION_FONT_DISABLE_POLICY FontDisablePolicy; PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy; + PROCESS_MITIGATION_SYSTEM_CALL_FILTER_POLICY SystemCallFilterPolicy; + PROCESS_MITIGATION_PAYLOAD_RESTRICTION_POLICY PayloadRestrictionPolicy; + PROCESS_MITIGATION_CHILD_PROCESS_POLICY ChildProcessPolicy; + PROCESS_MITIGATION_SIDE_CHANNEL_ISOLATION_POLICY SideChannelIsolationPolicy; }; } PROCESS_MITIGATION_POLICY_INFORMATION, *PPROCESS_MITIGATION_POLICY_INFORMATION; @@ -624,6 +701,24 @@ typedef enum _PS_PROTECTED_SIGNER PsProtectedSignerMax } PS_PROTECTED_SIGNER; +#define PS_PROTECTED_SIGNER_MASK 0xFF +#define PS_PROTECTED_AUDIT_MASK 0x08 +#define PS_PROTECTED_TYPE_MASK 0x07 + +// vProtectionLevel.Level = PsProtectedValue(PsProtectedSignerCodeGen, FALSE, PsProtectedTypeProtectedLight) +#define PsProtectedValue(aSigner, aAudit, aType) ( \ + ((aSigner & PS_PROTECTED_SIGNER_MASK) << 4) | \ + ((aAudit & PS_PROTECTED_AUDIT_MASK) << 3) | \ + (aType & PS_PROTECTED_TYPE_MASK)\ + ) + +// InitializePsProtection(&vProtectionLevel, PsProtectedSignerCodeGen, FALSE, PsProtectedTypeProtectedLight) +#define InitializePsProtection(aProtectionLevelPtr, aSigner, aAudit, aType) { \ + (aProtectionLevelPtr)->Signer = aSigner; \ + (aProtectionLevelPtr)->Audit = aAudit; \ + (aProtectionLevelPtr)->Type = aType; \ + } + typedef struct _PS_PROTECTION { union @@ -692,9 +787,128 @@ typedef struct _PROCESS_JOB_MEMORY_INFO typedef struct _PROCESS_CHILD_PROCESS_INFORMATION { BOOLEAN ProhibitChildProcesses; - BOOLEAN EnableAutomaticOverride; + //BOOLEAN EnableAutomaticOverride; // REDSTONE2 + BOOLEAN AlwaysAllowSecureChildProcess; // REDSTONE3 + BOOLEAN AuditProhibitChildProcesses; } PROCESS_CHILD_PROCESS_INFORMATION, *PPROCESS_CHILD_PROCESS_INFORMATION; +typedef struct _PROCESS_WAKE_INFORMATION +{ + ULONGLONG NotificationChannel; + ULONG WakeCounters[7]; + struct _JOBOBJECT_WAKE_FILTER* WakeFilter; +} PROCESS_WAKE_INFORMATION, *PPROCESS_WAKE_INFORMATION; + +typedef struct _PROCESS_ENERGY_TRACKING_STATE +{ + ULONG StateUpdateMask; + ULONG StateDesiredValue; + ULONG StateSequence; + ULONG UpdateTag : 1; + WCHAR Tag[64]; +} PROCESS_ENERGY_TRACKING_STATE, *PPROCESS_ENERGY_TRACKING_STATE; + +typedef struct _MANAGE_WRITES_TO_EXECUTABLE_MEMORY +{ + ULONG Version : 8; + ULONG ProcessEnableWriteExceptions : 1; + ULONG ThreadAllowWrites : 1; + ULONG Spare : 22; + PVOID KernelWriteToExecutableSignal; // 19H1 +} MANAGE_WRITES_TO_EXECUTABLE_MEMORY, *PMANAGE_WRITES_TO_EXECUTABLE_MEMORY; + +#define PROCESS_READWRITEVM_LOGGING_ENABLE_READVM 1 +#define PROCESS_READWRITEVM_LOGGING_ENABLE_WRITEVM 2 +#define PROCESS_READWRITEVM_LOGGING_ENABLE_READVM_V 1UL +#define PROCESS_READWRITEVM_LOGGING_ENABLE_WRITEVM_V 2UL + +typedef union _PROCESS_READWRITEVM_LOGGING_INFORMATION +{ + UCHAR Flags; + struct + { + UCHAR EnableReadVmLogging : 1; + UCHAR EnableWriteVmLogging : 1; + UCHAR Unused : 6; + }; +} PROCESS_READWRITEVM_LOGGING_INFORMATION, *PPROCESS_READWRITEVM_LOGGING_INFORMATION; + +typedef struct _PROCESS_UPTIME_INFORMATION +{ + ULONGLONG QueryInterruptTime; + ULONGLONG QueryUnbiasedTime; + ULONGLONG EndInterruptTime; + ULONGLONG TimeSinceCreation; + ULONGLONG Uptime; + ULONGLONG SuspendedTime; + union + { + ULONG HangCount : 4; + ULONG GhostCount : 4; + ULONG Crashed : 1; + ULONG Terminated : 1; + }; +} PROCESS_UPTIME_INFORMATION, *PPROCESS_UPTIME_INFORMATION; + +typedef union _PROCESS_SYSTEM_RESOURCE_MANAGEMENT +{ + ULONG Flags; + struct + { + ULONG Foreground : 1; + ULONG Reserved : 31; + }; +} PROCESS_SYSTEM_RESOURCE_MANAGEMENT, *PPROCESS_SYSTEM_RESOURCE_MANAGEMENT; + +// private +typedef struct _PROCESS_SECURITY_DOMAIN_INFORMATION +{ + ULONGLONG SecurityDomain; +} PROCESS_SECURITY_DOMAIN_INFORMATION, *PPROCESS_SECURITY_DOMAIN_INFORMATION; + +// private +typedef struct _PROCESS_COMBINE_SECURITY_DOMAINS_INFORMATION +{ + HANDLE ProcessHandle; +} PROCESS_COMBINE_SECURITY_DOMAINS_INFORMATION, *PPROCESS_COMBINE_SECURITY_DOMAINS_INFORMATION; + +// private +typedef struct _PROCESS_LOGGING_INFORMATION +{ + ULONG Flags; + struct + { + ULONG EnableReadVmLogging : 1; + ULONG EnableWriteVmLogging : 1; + ULONG EnableProcessSuspendResumeLogging : 1; + ULONG EnableThreadSuspendResumeLogging : 1; + ULONG Reserved : 28; + }; +} PROCESS_LOGGING_INFORMATION, *PPROCESS_LOGGING_INFORMATION; + +// private +typedef struct _PROCESS_LEAP_SECOND_INFORMATION +{ + ULONG Flags; + ULONG Reserved; +} PROCESS_LEAP_SECOND_INFORMATION, *PPROCESS_LEAP_SECOND_INFORMATION; + +// private +typedef struct _PROCESS_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION +{ + ULONGLONG ReserveSize; + ULONGLONG CommitSize; + ULONG PreferredNode; + ULONG Reserved; + PVOID Ssp; +} PROCESS_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION, *PPROCESS_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION; + +// private +typedef struct _PROCESS_FREE_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION +{ + PVOID Ssp; +} PROCESS_FREE_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION, *PPROCESS_FREE_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION; + // end_private #endif @@ -716,7 +930,11 @@ typedef struct _THREAD_LAST_SYSCALL_INFORMATION { PVOID FirstArgument; USHORT SystemCallNumber; - USHORT Reserved; // since REDSTONE2 +#ifdef WIN64 + USHORT Pad[0x3]; // since REDSTONE2 +#else + USHORT Pad[0x1]; // since REDSTONE2 +#endif ULONG64 WaitTime; } THREAD_LAST_SYSCALL_INFORMATION, *PTHREAD_LAST_SYSCALL_INFORMATION; @@ -817,9 +1035,16 @@ typedef struct _THREAD_UMS_INFORMATION THREAD_UMS_INFORMATION_COMMAND Command; PRTL_UMS_COMPLETION_LIST CompletionList; PRTL_UMS_CONTEXT UmsContext; - ULONG Flags; - ULONG IsUmsSchedulerThread; - ULONG IsUmsWorkerThread; + union + { + ULONG Flags; + struct + { + ULONG IsUmsSchedulerThread : 1; + ULONG IsUmsWorkerThread : 1; + ULONG SpareBits : 30; + }; + }; } THREAD_UMS_INFORMATION, *PTHREAD_UMS_INFORMATION; // private @@ -828,6 +1053,24 @@ typedef struct _THREAD_NAME_INFORMATION UNICODE_STRING ThreadName; } THREAD_NAME_INFORMATION, *PTHREAD_NAME_INFORMATION; +#if (PHNT_MODE != PHNT_MODE_KERNEL) +// private +typedef enum _SUBSYSTEM_INFORMATION_TYPE +{ + SubsystemInformationTypeWin32, + SubsystemInformationTypeWSL, + MaxSubsystemInformationType +} SUBSYSTEM_INFORMATION_TYPE; +#endif + +// private +typedef enum _THREAD_WORKLOAD_CLASS +{ + ThreadWorkloadClassDefault, + ThreadWorkloadClassGraphics, + MaxThreadWorkloadClass +} THREAD_WORKLOAD_CLASS; + // Processes #if (PHNT_MODE != PHNT_MODE_KERNEL) @@ -907,6 +1150,12 @@ NtResumeProcess( #define ZwCurrentSession() NtCurrentSession() #define NtCurrentPeb() (NtCurrentTeb()->ProcessEnvironmentBlock) +// Windows 8 and above +#define NtCurrentProcessToken() ((HANDLE)(LONG_PTR)-4) +#define NtCurrentThreadToken() ((HANDLE)(LONG_PTR)-5) +#define NtCurrentEffectiveToken() ((HANDLE)(LONG_PTR)-6) +#define NtCurrentSilo() ((HANDLE)(LONG_PTR)-1) + // Not NT, but useful. #define NtCurrentProcessId() (NtCurrentTeb()->ClientId.UniqueProcess) #define NtCurrentThreadId() (NtCurrentTeb()->ClientId.UniqueThread) @@ -1132,6 +1381,9 @@ NtQueueApcThread( ); #if (PHNT_VERSION >= PHNT_WIN7) + +#define APC_FORCE_THREAD_SIGNAL ((HANDLE)1) // UserApcReserveHandle + NTSYSCALLAPI NTSTATUS NTAPI @@ -1174,12 +1426,11 @@ NtWaitForAlertByThreadId( // Attributes -// begin_rev +// private #define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff -#define PS_ATTRIBUTE_THREAD 0x00010000 // can be used with threads +#define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation #define PS_ATTRIBUTE_INPUT 0x00020000 // input only -#define PS_ATTRIBUTE_UNKNOWN 0x00040000 -// end_rev +#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc. // private typedef enum _PS_ATTRIBUTE_NUM @@ -1201,25 +1452,26 @@ typedef enum _PS_ATTRIBUTE_NUM PsAttributeIdealProcessor, // in PPROCESSOR_NUMBER PsAttributeUmsThread, // ? in PUMS_CREATE_THREAD_ATTRIBUTES PsAttributeMitigationOptions, // in UCHAR - PsAttributeProtectionLevel, + PsAttributeProtectionLevel, // in ULONG PsAttributeSecureProcess, // since THRESHOLD PsAttributeJobList, PsAttributeChildProcessPolicy, // since THRESHOLD2 PsAttributeAllApplicationPackagesPolicy, // since REDSTONE PsAttributeWin32kFilter, PsAttributeSafeOpenPromptOriginClaim, - PsAttributeBnoIsolation, - PsAttributeDesktopAppPolicy, + PsAttributeBnoIsolation, // PS_BNO_ISOLATION_PARAMETERS + PsAttributeDesktopAppPolicy, // in ULONG + PsAttributeChpe, // since REDSTONE3 PsAttributeMax } PS_ATTRIBUTE_NUM; // begin_rev -#define PsAttributeValue(Number, Thread, Input, Unknown) \ +#define PsAttributeValue(Number, Thread, Input, Additive) \ (((Number) & PS_ATTRIBUTE_NUMBER_MASK) | \ ((Thread) ? PS_ATTRIBUTE_THREAD : 0) | \ ((Input) ? PS_ATTRIBUTE_INPUT : 0) | \ - ((Unknown) ? PS_ATTRIBUTE_UNKNOWN : 0)) + ((Additive) ? PS_ATTRIBUTE_ADDITIVE : 0)) #define PS_ATTRIBUTE_PARENT_PROCESS \ PsAttributeValue(PsAttributeParentProcess, FALSE, TRUE, TRUE) @@ -1251,8 +1503,28 @@ typedef enum _PS_ATTRIBUTE_NUM PsAttributeValue(PsAttributePreferredNode, FALSE, TRUE, FALSE) #define PS_ATTRIBUTE_IDEAL_PROCESSOR \ PsAttributeValue(PsAttributeIdealProcessor, TRUE, TRUE, FALSE) +#define PS_ATTRIBUTE_UMS_THREAD \ + PsAttributeValue(PsAttributeUmsThread, TRUE, TRUE, FALSE) #define PS_ATTRIBUTE_MITIGATION_OPTIONS \ PsAttributeValue(PsAttributeMitigationOptions, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_PROTECTION_LEVEL \ + PsAttributeValue(PsAttributeProtectionLevel, FALSE, TRUE, TRUE) +#define PS_ATTRIBUTE_SECURE_PROCESS \ + PsAttributeValue(PsAttributeSecureProcess, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_JOB_LIST \ + PsAttributeValue(PsAttributeJobList, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_CHILD_PROCESS_POLICY \ + PsAttributeValue(PsAttributeChildProcessPolicy, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \ + PsAttributeValue(PsAttributeAllApplicationPackagesPolicy, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_WIN32K_FILTER \ + PsAttributeValue(PsAttributeWin32kFilter, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_SAFE_OPEN_PROMPT_ORIGIN_CLAIM \ + PsAttributeValue(PsAttributeSafeOpenPromptOriginClaim, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_BNO_ISOLATION \ + PsAttributeValue(PsAttributeBnoIsolation, FALSE, TRUE, FALSE) +#define PS_ATTRIBUTE_DESKTOP_APP_POLICY \ + PsAttributeValue(PsAttributeDesktopAppPolicy, FALSE, TRUE, FALSE) // end_rev @@ -1310,6 +1582,52 @@ typedef struct _PS_STD_HANDLE_INFO ULONG StdHandleSubsystemType; } PS_STD_HANDLE_INFO, *PPS_STD_HANDLE_INFO; +// private +typedef struct _PS_BNO_ISOLATION_PARAMETERS +{ + UNICODE_STRING IsolationPrefix; + ULONG HandleCount; + PVOID *Handles; + BOOLEAN IsolationEnabled; +} PS_BNO_ISOLATION_PARAMETERS, *PPS_BNO_ISOLATION_PARAMETERS; + +// private +typedef enum _PS_MITIGATION_OPTION +{ + PS_MITIGATION_OPTION_NX, + PS_MITIGATION_OPTION_SEHOP, + PS_MITIGATION_OPTION_FORCE_RELOCATE_IMAGES, + PS_MITIGATION_OPTION_HEAP_TERMINATE, + PS_MITIGATION_OPTION_BOTTOM_UP_ASLR, + PS_MITIGATION_OPTION_HIGH_ENTROPY_ASLR, + PS_MITIGATION_OPTION_STRICT_HANDLE_CHECKS, + PS_MITIGATION_OPTION_WIN32K_SYSTEM_CALL_DISABLE, + PS_MITIGATION_OPTION_EXTENSION_POINT_DISABLE, + PS_MITIGATION_OPTION_PROHIBIT_DYNAMIC_CODE, + PS_MITIGATION_OPTION_CONTROL_FLOW_GUARD, + PS_MITIGATION_OPTION_BLOCK_NON_MICROSOFT_BINARIES, + PS_MITIGATION_OPTION_FONT_DISABLE, + PS_MITIGATION_OPTION_IMAGE_LOAD_NO_REMOTE, + PS_MITIGATION_OPTION_IMAGE_LOAD_NO_LOW_LABEL, + PS_MITIGATION_OPTION_IMAGE_LOAD_PREFER_SYSTEM32, + PS_MITIGATION_OPTION_RETURN_FLOW_GUARD, + PS_MITIGATION_OPTION_LOADER_INTEGRITY_CONTINUITY, + PS_MITIGATION_OPTION_STRICT_CONTROL_FLOW_GUARD, + PS_MITIGATION_OPTION_RESTRICT_SET_THREAD_CONTEXT, + PS_MITIGATION_OPTION_ROP_STACKPIVOT, // since REDSTONE3 + PS_MITIGATION_OPTION_ROP_CALLER_CHECK, + PS_MITIGATION_OPTION_ROP_SIMEXEC, + PS_MITIGATION_OPTION_EXPORT_ADDRESS_FILTER, + PS_MITIGATION_OPTION_EXPORT_ADDRESS_FILTER_PLUS, + PS_MITIGATION_OPTION_RESTRICT_CHILD_PROCESS_CREATION, + PS_MITIGATION_OPTION_IMPORT_ADDRESS_FILTER, + PS_MITIGATION_OPTION_MODULE_TAMPERING_PROTECTION, + PS_MITIGATION_OPTION_RESTRICT_INDIRECT_BRANCH_PREDICTION, + PS_MITIGATION_OPTION_SPECULATIVE_STORE_BYPASS_DISABLE, // since REDSTONE5 + PS_MITIGATION_OPTION_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY, + PS_MITIGATION_OPTION_CET_SHADOW_STACKS +} PS_MITIGATION_OPTION; + // windows-internals-book:"Chapter 5" typedef enum _PS_CREATE_STATE { @@ -1400,12 +1718,19 @@ typedef struct _PS_CREATE_INFO // end_private -// Extended PROCESS_CREATE_FLAGS_* // begin_rev +#define PROCESS_CREATE_FLAGS_BREAKAWAY 0x00000001 +#define PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT 0x00000002 +#define PROCESS_CREATE_FLAGS_INHERIT_HANDLES 0x00000004 +#define PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE 0x00000008 +#define PROCESS_CREATE_FLAGS_LARGE_PAGES 0x00000010 #define PROCESS_CREATE_FLAGS_LARGE_PAGE_SYSTEM_DLL 0x00000020 +// Extended PROCESS_CREATE_FLAGS_* #define PROCESS_CREATE_FLAGS_PROTECTED_PROCESS 0x00000040 #define PROCESS_CREATE_FLAGS_CREATE_SESSION 0x00000080 // ? #define PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT 0x00000100 +#define PROCESS_CREATE_FLAGS_SUSPENDED 0x00000200 +#define PROCESS_CREATE_FLAGS_EXTENDED_UNKNOWN 0x00000400 // end_rev #if (PHNT_VERSION >= PHNT_VISTA) @@ -1461,6 +1786,160 @@ NtCreateThreadEx( #if (PHNT_MODE != PHNT_MODE_KERNEL) +// JOBOBJECTINFOCLASS +// Note: We don't use an enum since it conflicts with the Windows SDK. +#define JobObjectBasicAccountingInformation 1 // JOBOBJECT_BASIC_ACCOUNTING_INFORMATION +#define JobObjectBasicLimitInformation 2 // JOBOBJECT_BASIC_LIMIT_INFORMATION +#define JobObjectBasicProcessIdList 3 // JOBOBJECT_BASIC_PROCESS_ID_LIST +#define JobObjectBasicUIRestrictions 4 // JOBOBJECT_BASIC_UI_RESTRICTIONS +#define JobObjectSecurityLimitInformation 5 // JOBOBJECT_SECURITY_LIMIT_INFORMATION +#define JobObjectEndOfJobTimeInformation 6 // JOBOBJECT_END_OF_JOB_TIME_INFORMATION +#define JobObjectAssociateCompletionPortInformation 7 // JOBOBJECT_ASSOCIATE_COMPLETION_PORT +#define JobObjectBasicAndIoAccountingInformation 8 // JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION +#define JobObjectExtendedLimitInformation 9 // JOBOBJECT_EXTENDED_LIMIT_INFORMATION +#define JobObjectJobSetInformation 10 // JOBOBJECT_JOBSET_INFORMATION +#define JobObjectGroupInformation 11 // USHORT +#define JobObjectNotificationLimitInformation 12 // JOBOBJECT_NOTIFICATION_LIMIT_INFORMATION +#define JobObjectLimitViolationInformation 13 // JOBOBJECT_LIMIT_VIOLATION_INFORMATION +#define JobObjectGroupInformationEx 14 // GROUP_AFFINITY (ARRAY) +#define JobObjectCpuRateControlInformation 15 // JOBOBJECT_CPU_RATE_CONTROL_INFORMATION +#define JobObjectCompletionFilter 16 +#define JobObjectCompletionCounter 17 +#define JobObjectFreezeInformation 18 // JOBOBJECT_FREEZE_INFORMATION +#define JobObjectExtendedAccountingInformation 19 // JOBOBJECT_EXTENDED_ACCOUNTING_INFORMATION +#define JobObjectWakeInformation 20 // JOBOBJECT_WAKE_INFORMATION +#define JobObjectBackgroundInformation 21 +#define JobObjectSchedulingRankBiasInformation 22 +#define JobObjectTimerVirtualizationInformation 23 +#define JobObjectCycleTimeNotification 24 +#define JobObjectClearEvent 25 +#define JobObjectInterferenceInformation 26 // JOBOBJECT_INTERFERENCE_INFORMATION +#define JobObjectClearPeakJobMemoryUsed 27 +#define JobObjectMemoryUsageInformation 28 // JOBOBJECT_MEMORY_USAGE_INFORMATION // JOBOBJECT_MEMORY_USAGE_INFORMATION_V2 +#define JobObjectSharedCommit 29 +#define JobObjectContainerId 30 +#define JobObjectIoRateControlInformation 31 +#define JobObjectNetRateControlInformation 32 // JOBOBJECT_NET_RATE_CONTROL_INFORMATION +#define JobObjectNotificationLimitInformation2 33 // JOBOBJECT_NOTIFICATION_LIMIT_INFORMATION_2 +#define JobObjectLimitViolationInformation2 34 // JOBOBJECT_LIMIT_VIOLATION_INFORMATION_2 +#define JobObjectCreateSilo 35 +#define JobObjectSiloBasicInformation 36 // SILOOBJECT_BASIC_INFORMATION +#define JobObjectSiloRootDirectory 37 // SILOOBJECT_ROOT_DIRECTORY +#define JobObjectServerSiloBasicInformation 38 // SERVERSILO_BASIC_INFORMATION +#define JobObjectServerSiloUserSharedData 39 // SILO_USER_SHARED_DATA +#define JobObjectServerSiloInitialize 40 +#define JobObjectServerSiloRunningState 41 +#define JobObjectIoAttribution 42 +#define JobObjectMemoryPartitionInformation 43 +#define JobObjectContainerTelemetryId 44 +#define JobObjectSiloSystemRoot 45 +#define JobObjectEnergyTrackingState 46 // JOBOBJECT_ENERGY_TRACKING_STATE +#define JobObjectThreadImpersonationInformation 47 +#define MaxJobObjectInfoClass 48 + +// private +typedef struct _JOBOBJECT_EXTENDED_ACCOUNTING_INFORMATION +{ + JOBOBJECT_BASIC_ACCOUNTING_INFORMATION BasicInfo; + IO_COUNTERS IoInfo; + PROCESS_DISK_COUNTERS DiskIoInfo; + ULONG64 ContextSwitches; + LARGE_INTEGER TotalCycleTime; + ULONG64 ReadyTime; + PROCESS_ENERGY_VALUES EnergyValues; +} JOBOBJECT_EXTENDED_ACCOUNTING_INFORMATION, *PJOBOBJECT_EXTENDED_ACCOUNTING_INFORMATION; + +// private +typedef struct _JOBOBJECT_WAKE_INFORMATION +{ + HANDLE NotificationChannel; + ULONG64 WakeCounters[7]; +} JOBOBJECT_WAKE_INFORMATION, *PJOBOBJECT_WAKE_INFORMATION; + +// private +typedef struct _JOBOBJECT_WAKE_INFORMATION_V1 +{ + HANDLE NotificationChannel; + ULONG64 WakeCounters[4]; +} JOBOBJECT_WAKE_INFORMATION_V1, *PJOBOBJECT_WAKE_INFORMATION_V1; + +// private +typedef struct _JOBOBJECT_INTERFERENCE_INFORMATION +{ + ULONG64 Count; +} JOBOBJECT_INTERFERENCE_INFORMATION, *PJOBOBJECT_INTERFERENCE_INFORMATION; + +// private +typedef struct _JOBOBJECT_WAKE_FILTER +{ + ULONG HighEdgeFilter; + ULONG LowEdgeFilter; +} JOBOBJECT_WAKE_FILTER, *PJOBOBJECT_WAKE_FILTER; + +// private +typedef struct _JOBOBJECT_FREEZE_INFORMATION +{ + union + { + ULONG Flags; + struct + { + ULONG FreezeOperation : 1; + ULONG FilterOperation : 1; + ULONG SwapOperation : 1; + ULONG Reserved : 29; + }; + }; + BOOLEAN Freeze; + BOOLEAN Swap; + UCHAR Reserved0[2]; + JOBOBJECT_WAKE_FILTER WakeFilter; +} JOBOBJECT_FREEZE_INFORMATION, *PJOBOBJECT_FREEZE_INFORMATION; + +// private +typedef struct _JOBOBJECT_MEMORY_USAGE_INFORMATION +{ + ULONG64 JobMemory; + ULONG64 PeakJobMemoryUsed; +} JOBOBJECT_MEMORY_USAGE_INFORMATION, *PJOBOBJECT_MEMORY_USAGE_INFORMATION; + +// private +typedef struct _JOBOBJECT_MEMORY_USAGE_INFORMATION_V2 +{ + JOBOBJECT_MEMORY_USAGE_INFORMATION BasicInfo; + ULONG64 JobSharedMemory; + ULONG64 Reserved[2]; +} JOBOBJECT_MEMORY_USAGE_INFORMATION_V2, *PJOBOBJECT_MEMORY_USAGE_INFORMATION_V2; + +// private +typedef struct _SILO_USER_SHARED_DATA +{ + ULONG64 ServiceSessionId; + ULONG ActiveConsoleId; + LONGLONG ConsoleSessionForegroundProcessId; + NT_PRODUCT_TYPE NtProductType; + ULONG SuiteMask; + ULONG SharedUserSessionId; + BOOLEAN IsMultiSessionSku; + WCHAR NtSystemRoot[260]; + USHORT UserModeGlobalLogger[16]; +} SILO_USER_SHARED_DATA, *PSILO_USER_SHARED_DATA; + +// private +typedef struct _SILOOBJECT_ROOT_DIRECTORY +{ + ULONG ControlFlags; + UNICODE_STRING Path; +} SILOOBJECT_ROOT_DIRECTORY, *PSILOOBJECT_ROOT_DIRECTORY; + +// private +typedef struct _JOBOBJECT_ENERGY_TRACKING_STATE +{ + ULONG64 Value; + ULONG UpdateMask; + ULONG DesiredState; +} JOBOBJECT_ENERGY_TRACKING_STATE, *PJOBOBJECT_ENERGY_TRACKING_STATE; + NTSYSCALLAPI NTSTATUS NTAPI diff --git a/phnt/include/ntregapi.h b/phnt/include/ntregapi.h index d5d874173322..a6e1240898ef 100644 --- a/phnt/include/ntregapi.h +++ b/phnt/include/ntregapi.h @@ -120,6 +120,7 @@ typedef enum _KEY_SET_INFORMATION_CLASS KeySetVirtualizationInformation, // KEY_SET_VIRTUALIZATION_INFORMATION KeySetDebugInformation, KeySetHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION + KeySetLayerInformation, // KEY_SET_LAYER_INFORMATION MaxKeySetInfoClass } KEY_SET_INFORMATION_CLASS; @@ -138,6 +139,15 @@ typedef struct _KEY_HANDLE_TAGS_INFORMATION ULONG HandleTags; } KEY_HANDLE_TAGS_INFORMATION, *PKEY_HANDLE_TAGS_INFORMATION; +typedef struct _KEY_SET_LAYER_INFORMATION +{ + ULONG IsTombstone : 1; + ULONG IsSupersedeLocal : 1; + ULONG IsSupersedeTree : 1; + ULONG ClassIsInherited : 1; + ULONG Reserved : 28; +} KEY_SET_LAYER_INFORMATION, *PKEY_SET_LAYER_INFORMATION; + typedef struct _KEY_CONTROL_FLAGS_INFORMATION { ULONG ControlFlags; @@ -524,6 +534,12 @@ NtUnloadKey( _In_ POBJECT_ATTRIBUTES TargetKey ); +// +// NtUnloadKey2 Flags (from winnt.h) +// +//#define REG_FORCE_UNLOAD 1 +//#define REG_UNLOAD_LEGAL_FLAGS (REG_FORCE_UNLOAD) + NTSYSCALLAPI NTSTATUS NTAPI diff --git a/phnt/include/ntrtl.h b/phnt/include/ntrtl.h index 42f1c47971f1..1528322581af 100644 --- a/phnt/include/ntrtl.h +++ b/phnt/include/ntrtl.h @@ -1,6 +1,29 @@ +/* + * Process Hacker - + * RTL support functions + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTRTL_H #define _NTRTL_H +#define RtlOffsetToPointer(Base, Offset) ((PCHAR)(((PCHAR)(Base)) + ((ULONG_PTR)(Offset)))) +#define RtlPointerToOffset(Base, Pointer) ((ULONG)(((PCHAR)(Pointer)) - ((PCHAR)(Base)))) + // Linked lists FORCEINLINE VOID InitializeListHead( @@ -323,13 +346,13 @@ typedef struct _RTL_SPLAY_LINKS } RTL_SPLAY_LINKS, *PRTL_SPLAY_LINKS; #define RtlInitializeSplayLinks(Links) \ - { \ - PRTL_SPLAY_LINKS _SplayLinks; \ - _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ - _SplayLinks->Parent = _SplayLinks; \ - _SplayLinks->LeftChild = NULL; \ - _SplayLinks->RightChild = NULL; \ - } +{ \ + PRTL_SPLAY_LINKS _SplayLinks; \ + _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ + _SplayLinks->Parent = _SplayLinks; \ + _SplayLinks->LeftChild = NULL; \ + _SplayLinks->RightChild = NULL; \ +} #define RtlParent(Links) ((PRTL_SPLAY_LINKS)(Links)->Parent) #define RtlLeftChild(Links) ((PRTL_SPLAY_LINKS)(Links)->LeftChild) @@ -339,24 +362,24 @@ typedef struct _RTL_SPLAY_LINKS #define RtlIsRightChild(Links) ((RtlRightChild(RtlParent(Links)) == (PRTL_SPLAY_LINKS)(Links))) #define RtlInsertAsLeftChild(ParentLinks, ChildLinks) \ - { \ - PRTL_SPLAY_LINKS _SplayParent; \ - PRTL_SPLAY_LINKS _SplayChild; \ - _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ - _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ - _SplayParent->LeftChild = _SplayChild; \ - _SplayChild->Parent = _SplayParent; \ - } +{ \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->LeftChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ +} #define RtlInsertAsRightChild(ParentLinks, ChildLinks) \ - { \ - PRTL_SPLAY_LINKS _SplayParent; \ - PRTL_SPLAY_LINKS _SplayChild; \ - _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ - _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ - _SplayParent->RightChild = _SplayChild; \ - _SplayChild->Parent = _SplayParent; \ - } +{ \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->RightChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ +} NTSYSAPI PRTL_SPLAY_LINKS @@ -944,7 +967,7 @@ NTSYSAPI VOID NTAPI RtlCheckForOrphanedCriticalSections( - _In_ HANDLE hThread + _In_ HANDLE ThreadHandle ); // Resources @@ -954,11 +977,11 @@ typedef struct _RTL_RESOURCE RTL_CRITICAL_SECTION CriticalSection; HANDLE SharedSemaphore; - ULONG NumberOfWaitingShared; + volatile ULONG NumberOfWaitingShared; HANDLE ExclusiveSemaphore; - ULONG NumberOfWaitingExclusive; + volatile ULONG NumberOfWaitingExclusive; - LONG NumberOfActive; // negative: exclusive acquire; zero: not acquired; positive: shared acquire(s) + volatile LONG NumberOfActive; // negative: exclusive acquire; zero: not acquired; positive: shared acquire(s) HANDLE ExclusiveOwnerThread; ULONG Flags; // RTL_RESOURCE_FLAG_* @@ -1371,6 +1394,29 @@ RtlUpperString( _In_ PSTRING SourceString ); +FORCEINLINE +BOOLEAN +RtlIsNullOrEmptyUnicodeString( + _In_opt_ PUNICODE_STRING String + ) +{ + return !String || String->Length == 0; +} + +FORCEINLINE +VOID +NTAPI +RtlInitEmptyUnicodeString( + _Out_ PUNICODE_STRING DestinationString, + _In_opt_ PWCHAR Buffer, + _In_ USHORT MaximumLength + ) +{ + DestinationString->Buffer = Buffer; + DestinationString->MaximumLength = MaximumLength; + DestinationString->Length = 0; +} + #ifndef PHNT_NO_INLINE_INIT_STRING FORCEINLINE VOID RtlInitUnicodeString( _Out_ PUNICODE_STRING DestinationString, @@ -1378,7 +1424,7 @@ FORCEINLINE VOID RtlInitUnicodeString( ) { if (SourceString) - DestinationString->MaximumLength = (DestinationString->Length = (USHORT)(wcslen(SourceString) * sizeof(WCHAR))) + sizeof(WCHAR); + DestinationString->MaximumLength = (DestinationString->Length = (USHORT)(wcslen(SourceString) * sizeof(WCHAR))) + sizeof(UNICODE_NULL); else DestinationString->MaximumLength = DestinationString->Length = 0; @@ -1520,8 +1566,8 @@ NTSYSAPI BOOLEAN NTAPI RtlPrefixUnicodeString( - _In_ PCUNICODE_STRING String1, - _In_ PCUNICODE_STRING String2, + _In_ PUNICODE_STRING String1, + _In_ PUNICODE_STRING String2, _In_ BOOLEAN CaseInSensitive ); @@ -1531,8 +1577,20 @@ NTSYSAPI BOOLEAN NTAPI RtlSuffixUnicodeString( - _In_ PCUNICODE_STRING String1, - _In_ PCUNICODE_STRING String2, + _In_ PUNICODE_STRING String1, + _In_ PUNICODE_STRING String2, + _In_ BOOLEAN CaseInSensitive + ); +#endif + +#if (PHNT_VERSION >= PHNT_THRESHOLD) +_Must_inspect_result_ +NTSYSAPI +PWCHAR +NTAPI +RtlFindUnicodeSubstring( + _In_ PUNICODE_STRING FullString, + _In_ PUNICODE_STRING SearchString, _In_ BOOLEAN CaseInSensitive ); #endif @@ -1844,7 +1902,7 @@ RtlInitNlsTables( _In_ PUSHORT AnsiNlsBase, _In_ PUSHORT OemNlsBase, _In_ PUSHORT LanguageNlsBase, - _Out_ PNLSTABLEINFO TableInfo + _Out_ PNLSTABLEINFO TableInfo // PCPTABLEINFO? ); NTSYSAPI @@ -1917,6 +1975,19 @@ RtlIsNameInExpression( ); #endif +#if (PHNT_VERSION >= PHNT_REDSTONE4) +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsNameInUnUpcasedExpression( + _In_ PUNICODE_STRING Expression, + _In_ PUNICODE_STRING Name, + _In_ BOOLEAN IgnoreCase, + _In_opt_ PWCH UpcaseTable + ); +#endif + NTSYSAPI BOOLEAN NTAPI @@ -1938,7 +2009,7 @@ NTSTATUS NTAPI RtlDnsHostNameToComputerName( _Out_ PUNICODE_STRING ComputerNameString, - _In_ PCUNICODE_STRING DnsHostNameString, + _In_ PUNICODE_STRING DnsHostNameString, _In_ BOOLEAN AllocateComputerNameString ); @@ -1950,6 +2021,20 @@ RtlStringFromGUID( _Out_ PUNICODE_STRING GuidString ); +#if (PHNT_VERSION >= PHNT_WINBLUE) + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlStringFromGUIDEx( + _In_ PGUID Guid, + _Inout_ PUNICODE_STRING GuidString, + _In_ BOOLEAN AllocateGuidString + ); + +#endif + NTSYSAPI NTSTATUS NTAPI @@ -1959,6 +2044,7 @@ RtlGUIDFromString( ); #if (PHNT_VERSION >= PHNT_VISTA) + NTSYSAPI LONG NTAPI @@ -1966,6 +2052,40 @@ RtlCompareAltitudes( _In_ PUNICODE_STRING Altitude1, _In_ PUNICODE_STRING Altitude2 ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIdnToAscii( + _In_ ULONG Flags, + _In_ PWSTR SourceString, + _In_ LONG SourceStringLength, + _Out_writes_to_(*DestinationStringLength, *DestinationStringLength) PWSTR DestinationString, + _Inout_ PLONG DestinationStringLength + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIdnToUnicode( + _In_ ULONG Flags, + _In_ PWSTR SourceString, + _In_ LONG SourceStringLength, + _Out_writes_to_(*DestinationStringLength, *DestinationStringLength) PWSTR DestinationString, + _Inout_ PLONG DestinationStringLength + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIdnToNameprepUnicode( + _In_ ULONG Flags, + _In_ PWSTR SourceString, + _In_ LONG SourceStringLength, + _Out_writes_to_(*DestinationStringLength, *DestinationStringLength) PWSTR DestinationString, + _Inout_ PLONG DestinationStringLength + ); + #endif // Prefix @@ -2306,6 +2426,13 @@ RtlGetLocaleFileMappingAddress( // PEB +NTSYSAPI +PPEB +NTAPI +RtlGetCurrentPeb( + VOID + ); + NTSYSAPI VOID NTAPI @@ -2407,11 +2534,16 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS UNICODE_STRING RuntimeData; RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; - ULONG EnvironmentSize; - ULONG EnvironmentVersion; + ULONG_PTR EnvironmentSize; + ULONG_PTR EnvironmentVersion; PVOID PackageDependencyData; ULONG ProcessGroupId; ULONG LoaderThreads; + + UNICODE_STRING RedirectionDllName; // REDSTONE4 + UNICODE_STRING HeapPartitionName; // 19H1 + ULONG_PTR DefaultThreadpoolCpuSetMasks; + ULONG DefaultThreadpoolCpuSetMaskCount; } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; #define RTL_USER_PROC_PARAMS_NORMALIZED 0x00000001 @@ -2487,8 +2619,8 @@ RtlDeNormalizeProcessParams( typedef struct _RTL_USER_PROCESS_INFORMATION { ULONG Length; - HANDLE Process; - HANDLE Thread; + HANDLE ProcessHandle; + HANDLE ThreadHandle; CLIENT_ID ClientId; SECTION_IMAGE_INFORMATION ImageInformation; } RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; @@ -2510,6 +2642,17 @@ RtlCreateUserProcess( _Out_ PRTL_USER_PROCESS_INFORMATION ProcessInformation ); +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateUserProcessEx( + _In_ PUNICODE_STRING NtImagePathName, + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + _In_ BOOLEAN InheritHandles, + _Reserved_ ULONG Flags, + _Out_ PRTL_USER_PROCESS_INFORMATION ProcessInformation + ); + #if (PHNT_VERSION >= PHNT_VISTA) DECLSPEC_NORETURN NTSYSAPI @@ -2612,6 +2755,41 @@ RtlSetThreadIsCritical( _In_ BOOLEAN CheckFlag ); +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlValidProcessProtection( + _In_ PS_PROTECTION ProcessProtection + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlTestProtectedAccess( + _In_ PS_PROTECTION Source, + _In_ PS_PROTECTION Target + ); + +#if (PHNT_VERSION >= PHNT_REDSTONE3) +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsCurrentProcess( // NtCompareObjects(NtCurrentProcess(), ProcessHandle) + _In_ HANDLE ProcessHandle + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsCurrentThread( // NtCompareObjects(NtCurrentThread(), ThreadHandle) + _In_ HANDLE ThreadHandle + ); +#endif + // Threads typedef NTSTATUS (NTAPI *PUSER_THREAD_START_ROUTINE)( @@ -2658,6 +2836,18 @@ FORCEINLINE VOID RtlExitUserThread_R( #if (PHNT_VERSION >= PHNT_VISTA) +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsCurrentThreadAttachExempt( + VOID + ); + +#endif + +#if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -2681,6 +2871,29 @@ RtlFreeUserStack( #endif +// Extended thread context + +typedef struct _CONTEXT_CHUNK +{ + LONG Offset; // Offset may be negative. + ULONG Length; +} CONTEXT_CHUNK, *PCONTEXT_CHUNK; + +typedef struct _CONTEXT_EX +{ + CONTEXT_CHUNK All; + CONTEXT_CHUNK Legacy; + CONTEXT_CHUNK XState; +} CONTEXT_EX, *PCONTEXT_EX; + +#define CONTEXT_EX_LENGTH ALIGN_UP_BY(sizeof(CONTEXT_EX), PAGE_SIZE) +#define RTL_CONTEXT_EX_OFFSET(ContextEx, Chunk) ((ContextEx)->Chunk.Offset) +#define RTL_CONTEXT_EX_LENGTH(ContextEx, Chunk) ((ContextEx)->Chunk.Length) +#define RTL_CONTEXT_EX_CHUNK(Base, Layout, Chunk) ((PVOID)((PCHAR)(Base) + RTL_CONTEXT_EX_OFFSET(Layout, Chunk))) +#define RTL_CONTEXT_OFFSET(Context, Chunk) RTL_CONTEXT_EX_OFFSET((PCONTEXT_EX)(Context + 1), Chunk) +#define RTL_CONTEXT_LENGTH(Context, Chunk) RTL_CONTEXT_EX_LENGTH((PCONTEXT_EX)(Context + 1), Chunk) +#define RTL_CONTEXT_CHUNK(Context, Chunk) RTL_CONTEXT_EX_CHUNK((PCONTEXT_EX)(Context + 1), (PCONTEXT_EX)(Context + 1), Chunk) + NTSYSAPI VOID NTAPI @@ -2693,16 +2906,61 @@ RtlInitializeContext( ); NTSYSAPI -NTSTATUS +ULONG NTAPI -RtlRemoteCall( - _In_ HANDLE Process, - _In_ HANDLE Thread, - _In_ PVOID CallSite, - _In_ ULONG ArgumentCount, - _In_opt_ PULONG_PTR Arguments, - _In_ BOOLEAN PassContext, - _In_ BOOLEAN AlreadySuspended +RtlInitializeExtendedContext( + _Out_ PCONTEXT Context, + _In_ ULONG ContextFlags, + _Out_ PCONTEXT_EX* ContextEx + ); + +NTSYSAPI +ULONG +NTAPI +RtlCopyExtendedContext( + _Out_ PCONTEXT_EX Destination, + _In_ ULONG ContextFlags, + _In_ PCONTEXT_EX Source + ); + +NTSYSAPI +ULONG +NTAPI +RtlGetExtendedContextLength( + _In_ ULONG ContextFlags, + _Out_ PULONG ContextLength + ); + +NTSYSAPI +ULONG64 +NTAPI +RtlGetExtendedFeaturesMask( + _In_ PCONTEXT_EX ContextEx + ); + +NTSYSAPI +PVOID +NTAPI +RtlLocateExtendedFeature( + _In_ PCONTEXT_EX ContextEx, + _In_ ULONG FeatureId, + _Out_opt_ PULONG Length + ); + +NTSYSAPI +PCONTEXT +NTAPI +RtlLocateLegacyContext( + _In_ PCONTEXT_EX ContextEx, + _Out_opt_ PULONG Length + ); + +NTSYSAPI +VOID +NTAPI +RtlSetExtendedFeaturesMask( + __out PCONTEXT_EX ContextEx, + _Out_ ULONG64 FeatureMask ); #ifdef _WIN64 @@ -2727,6 +2985,19 @@ RtlWow64SetThreadContext( ); #endif +NTSYSAPI +NTSTATUS +NTAPI +RtlRemoteCall( + _In_ HANDLE Process, + _In_ HANDLE Thread, + _In_ PVOID CallSite, + _In_ ULONG ArgumentCount, + _In_opt_ PULONG_PTR Arguments, + _In_ BOOLEAN PassContext, + _In_ BOOLEAN AlreadySuspended + ); + // Vectored exception handlers NTSYSAPI @@ -2761,6 +3032,42 @@ RtlRemoveVectoredContinueHandler( // Runtime exception handling +typedef ULONG (NTAPI *PRTLP_UNHANDLED_EXCEPTION_FILTER)( + _In_ PEXCEPTION_POINTERS ExceptionInfo + ); + +NTSYSAPI +VOID +NTAPI +RtlSetUnhandledExceptionFilter( + _In_ PRTLP_UNHANDLED_EXCEPTION_FILTER UnhandledExceptionFilter + ); + +// rev +NTSYSAPI +LONG +NTAPI +RtlUnhandledExceptionFilter( + _In_ PEXCEPTION_POINTERS ExceptionPointers + ); + +// rev +NTSYSAPI +LONG +NTAPI +RtlUnhandledExceptionFilter2( + _In_ PEXCEPTION_POINTERS ExceptionPointers, + _In_ ULONG Flags + ); + +// rev +NTSYSAPI +LONG +NTAPI +RtlKnownExceptionFilter( + _In_ PEXCEPTION_POINTERS ExceptionPointers + ); + #ifdef _WIN64 // private @@ -2801,19 +3108,11 @@ RtlGetFunctionTableListHead( // Images -NTSYSAPI -PVOID -NTAPI -RtlPcToFileHeader( - _In_ PVOID PcValue, - _Out_ PVOID *BaseOfImage - ); - NTSYSAPI PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader( - _In_ PVOID Base + _In_ PVOID BaseOfImage ); #define RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK 0x00000001 @@ -2823,7 +3122,7 @@ NTSTATUS NTAPI RtlImageNtHeaderEx( _In_ ULONG Flags, - _In_ PVOID Base, + _In_ PVOID BaseOfImage, _In_ ULONG64 Size, _Out_ PIMAGE_NT_HEADERS *OutHeaders ); @@ -2861,7 +3160,7 @@ PIMAGE_SECTION_HEADER NTAPI RtlImageRvaToSection( _In_ PIMAGE_NT_HEADERS NtHeaders, - _In_ PVOID Base, + _In_ PVOID BaseOfImage, _In_ ULONG Rva ); @@ -2870,11 +3169,38 @@ PVOID NTAPI RtlImageRvaToVa( _In_ PIMAGE_NT_HEADERS NtHeaders, - _In_ PVOID Base, + _In_ PVOID BaseOfImage, _In_ ULONG Rva, _Inout_opt_ PIMAGE_SECTION_HEADER *LastRvaSection ); +#if (PHNT_VERSION >= PHNT_THRESHOLD) + +// rev +NTSYSAPI +PVOID +NTAPI +RtlFindExportedRoutineByName( + _In_ PVOID BaseOfImage, + _In_ PSTR RoutineName + ); + +#endif + +#if (PHNT_VERSION >= PHNT_REDSTONE) + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlGuardCheckLongJumpTarget( + _In_ PVOID PcValue, + _In_ BOOL IsFastFail, + _Out_ PBOOL IsLongJumpTarget + ); + +#endif + // Memory NTSYSAPI @@ -2967,7 +3293,7 @@ NTAPI RtlSetEnvironmentVariable( _In_opt_ PVOID *Environment, _In_ PUNICODE_STRING Name, - _In_ PUNICODE_STRING Value + _In_opt_ PUNICODE_STRING Value ); #if (PHNT_VERSION >= PHNT_VISTA) @@ -3027,9 +3353,13 @@ RtlSetEnvironmentStrings( _In_ SIZE_T NewEnvironmentSize ); -// Current directory and paths +// Directory and path support -typedef struct _RTLP_CURDIR_REF *PRTLP_CURDIR_REF; +typedef struct _RTLP_CURDIR_REF +{ + LONG ReferenceCount; + HANDLE DirectoryHandle; +} RTLP_CURDIR_REF, *PRTLP_CURDIR_REF; typedef struct _RTL_RELATIVE_NAME_U { @@ -3050,13 +3380,36 @@ typedef enum _RTL_PATH_TYPE RtlPathTypeRootLocalDevice } RTL_PATH_TYPE; -NTSYSAPI -RTL_PATH_TYPE +// Data exports (ntdll.lib/ntdllp.lib) + +NTSYSAPI PWSTR RtlNtdllName; +NTSYSAPI UNICODE_STRING RtlDosPathSeperatorsString; +NTSYSAPI UNICODE_STRING RtlAlternateDosPathSeperatorString; +NTSYSAPI UNICODE_STRING RtlNtPathSeperatorString; + +#ifndef PHNT_INLINE_SEPERATOR_STRINGS +#define RtlNtdllName L"ntdll.dll" +#define RtlDosPathSeperatorsString ((UNICODE_STRING)RTL_CONSTANT_STRING(L"\\/")) +#define RtlAlternateDosPathSeperatorString ((UNICODE_STRING)RTL_CONSTANT_STRING(L"/")) +#define RtlNtPathSeperatorString ((UNICODE_STRING)RTL_CONSTANT_STRING(L"\\")) +#endif + +// Path functions + +NTSYSAPI +RTL_PATH_TYPE NTAPI RtlDetermineDosPathNameType_U( _In_ PWSTR DosFileName ); +NTSYSAPI +RTL_PATH_TYPE +NTAPI +RtlDetermineDosPathNameType_Ustr( + _In_ PCUNICODE_STRING DosFileName + ); + NTSYSAPI ULONG NTAPI @@ -3064,6 +3417,13 @@ RtlIsDosDeviceName_U( _In_ PWSTR DosFileName ); +NTSYSAPI +ULONG +NTAPI +RtlIsDosDeviceName_Ustr( + _In_ PUNICODE_STRING DosFileName + ); + NTSYSAPI ULONG NTAPI @@ -3084,7 +3444,7 @@ RtlGetFullPathName_UEx( _In_ ULONG BufferLength, _Out_writes_bytes_(BufferLength) PWSTR Buffer, _Out_opt_ PWSTR *FilePart, - _Out_opt_ RTL_PATH_TYPE *InputPathType + _Out_opt_ ULONG *BytesRequired ); #endif @@ -3148,6 +3508,19 @@ RtlDosPathNameToNtPathName_U_WithStatus( ); #endif +#if (PHNT_VERSION >= PHNT_REDSTONE3) +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlDosLongPathNameToNtPathName_U_WithStatus( + _In_ PWSTR DosFileName, + _Out_ PUNICODE_STRING NtFileName, + _Out_opt_ PWSTR *FilePart, + _Out_opt_ PRTL_RELATIVE_NAME_U RelativeName + ); +#endif + #if (PHNT_VERSION >= PHNT_WS03) NTSYSAPI BOOLEAN @@ -3172,6 +3545,19 @@ RtlDosPathNameToRelativeNtPathName_U_WithStatus( ); #endif +#if (PHNT_VERSION >= PHNT_REDSTONE3) +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlDosLongPathNameToRelativeNtPathName_U_WithStatus( + _In_ PWSTR DosFileName, + _Out_ PUNICODE_STRING NtFileName, + _Out_opt_ PWSTR *FilePart, + _Out_opt_ PRTL_RELATIVE_NAME_U RelativeName + ); +#endif + #if (PHNT_VERSION >= PHNT_WS03) NTSYSAPI VOID @@ -3195,7 +3581,7 @@ RtlDosSearchPath_U( #define RTL_DOS_SEARCH_PATH_FLAG_APPLY_ISOLATION_REDIRECTION 0x00000001 #define RTL_DOS_SEARCH_PATH_FLAG_DISALLOW_DOT_RELATIVE_PATH_SEARCH 0x00000002 -#define RTL_DOS_SEARCH_PATH_FLAG_APPLY_DEFAULT_EXTENSION_WHEN_NOT_RELATIVE_PATH_EVEN_IF_FILE_HAS_EXTENSION 0x00000004) +#define RTL_DOS_SEARCH_PATH_FLAG_APPLY_DEFAULT_EXTENSION_WHEN_NOT_RELATIVE_PATH_EVEN_IF_FILE_HAS_EXTENSION 0x00000004 NTSYSAPI NTSTATUS @@ -3219,6 +3605,118 @@ RtlDoesFileExists_U( _In_ PWSTR FileName ); +NTSYSAPI +NTSTATUS +NTAPI +RtlGetLengthWithoutLastFullDosOrNtPathElement( + _Reserved_ ULONG Flags, + _In_ PUNICODE_STRING PathString, + _Out_ PULONG Length + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetLengthWithoutTrailingPathSeperators( + _Reserved_ ULONG Flags, + _In_ PUNICODE_STRING PathString, + _Out_ PULONG Length + ); + +typedef struct _GENERATE_NAME_CONTEXT +{ + USHORT Checksum; + BOOLEAN CheckSumInserted; + UCHAR NameLength; + WCHAR NameBuffer[8]; + ULONG ExtensionLength; + WCHAR ExtensionBuffer[4]; + ULONG LastIndexValue; +} GENERATE_NAME_CONTEXT, *PGENERATE_NAME_CONTEXT; + +// private +NTSYSAPI +NTSTATUS +NTAPI +RtlGenerate8dot3Name( + _In_ PUNICODE_STRING Name, + _In_ BOOLEAN AllowExtendedCharacters, + _In_ PGENERATE_NAME_CONTEXT Context, + _Out_ PUNICODE_STRING Name8dot3 + ); + +#if (PHNT_VERSION >= PHNT_WIN8) + +// private +NTSYSAPI +NTSTATUS +NTAPI +RtlComputePrivatizedDllName_U( + _In_ PUNICODE_STRING DllName, + _Out_ PUNICODE_STRING RealName, + _Out_ PUNICODE_STRING LocalName + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlGetSearchPath( + _Out_ PWSTR *SearchPath + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlSetSearchPathMode( + _In_ ULONG Flags + ); + +// rev +NTSYSAPI +PWSTR +NTAPI +RtlGetExePath( + VOID + ); + +#endif + +#if (PHNT_VERSION >= PHNT_REDSTONE2) + +// private +NTSYSAPI +PWSTR +NTAPI +RtlGetNtSystemRoot( + VOID + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlAreLongPathsEnabled( + VOID + ); + +#endif + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsThreadWithinLoaderCallout( + VOID + ); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDllShutdownInProgress( + VOID + ); + // Heaps typedef struct _RTL_HEAP_ENTRY @@ -3595,8 +4093,54 @@ RtlWalkHeap( _Inout_ PRTL_HEAP_WALK_ENTRY Entry ); -// rev -#define HeapDebuggingInformation 0x80000002 +// HEAP_INFORMATION_CLASS +#define HeapCompatibilityInformation 0x0 // q; s: ULONG +#define HeapEnableTerminationOnCorruption 0x1 // q; s: NULL +#define HeapExtendedInformation 0x2 // q; s: HEAP_EXTENDED_INFORMATION +#define HeapOptimizeResources 0x3 // q; s: HEAP_OPTIMIZE_RESOURCES_INFORMATION +#define HeapTaggingInformation 0x4 +#define HeapStackDatabase 0x5 +#define HeapDetailedFailureInformation 0x80000001 +#define HeapSetDebuggingInformation 0x80000002 // q; s: HEAP_DEBUGGING_INFORMATION + +typedef enum _HEAP_COMPATIBILITY_MODE +{ + HEAP_COMPATIBILITY_STANDARD = 0UL, + HEAP_COMPATIBILITY_LAL = 1UL, + HEAP_COMPATIBILITY_LFH = 2UL, +} HEAP_COMPATIBILITY_MODE; + +typedef struct _PROCESS_HEAP_INFORMATION +{ + ULONG_PTR ReserveSize; + ULONG_PTR CommitSize; + ULONG NumberOfHeaps; + ULONG_PTR FirstHeapInformationOffset; +} PROCESS_HEAP_INFORMATION, *PPROCESS_HEAP_INFORMATION; + +typedef struct _HEAP_INFORMATION +{ + ULONG_PTR Address; + ULONG Mode; + ULONG_PTR ReserveSize; + ULONG_PTR CommitSize; + ULONG_PTR FirstRegionInformationOffset; + ULONG_PTR NextHeapInformationOffset; +} HEAP_INFORMATION, *PHEAP_INFORMATION; + +typedef struct _HEAP_EXTENDED_INFORMATION +{ + HANDLE Process; + ULONG_PTR Heap; + ULONG Level; + PVOID CallbackRoutine; + PVOID CallbackContext; + union + { + PROCESS_HEAP_INFORMATION ProcessHeapInformation; + HEAP_INFORMATION HeapInformation; + }; +} HEAP_EXTENDED_INFORMATION, *PHEAP_EXTENDED_INFORMATION; // rev typedef NTSTATUS (NTAPI *PRTL_HEAP_LEAK_ENUMERATION_ROUTINE)( @@ -3671,6 +4215,13 @@ RtlDetectHeapLeaks( ); #endif +NTSYSAPI +VOID +NTAPI +RtlFlushHeaps( + VOID + ); + // Memory zones // begin_private @@ -3841,7 +4392,7 @@ RtlSetCurrentTransaction( // LUIDs -FORCEINLINE BOOLEAN RtlIsEqualLuid( +FORCEINLINE BOOLEAN RtlIsEqualLuid( // RtlEqualLuid _In_ PLUID L1, _In_ PLUID L2 ) @@ -3891,6 +4442,45 @@ RtlCopyLuid( _In_ PLUID SourceLuid ); +// ros +NTSYSAPI +VOID +NTAPI +RtlCopyLuidAndAttributesArray( + _In_ ULONG Count, + _In_ PLUID_AND_ATTRIBUTES Src, + _In_ PLUID_AND_ATTRIBUTES Dest + ); + +// Byte swap routines. + +#ifndef PHNT_RTL_BYTESWAP +#define RtlUshortByteSwap(_x) _byteswap_ushort((USHORT)(_x)) +#define RtlUlongByteSwap(_x) _byteswap_ulong((_x)) +#define RtlUlonglongByteSwap(_x) _byteswap_uint64((_x)) +#else +NTSYSAPI +USHORT +FASTCALL +RtlUshortByteSwap( + _In_ USHORT Source + ); + +NTSYSAPI +ULONG +FASTCALL +RtlUlongByteSwap( + _In_ ULONG Source + ); + +NTSYSAPI +ULONGLONG +FASTCALL +RtlUlonglongByteSwap( + _In_ ULONGLONG Source + ); +#endif + // Debugging // private @@ -4031,11 +4621,7 @@ typedef struct _PARSE_MESSAGE_CONTEXT va_list lpvArgStart; } PARSE_MESSAGE_CONTEXT, *PPARSE_MESSAGE_CONTEXT; -#define INIT_PARSE_MESSAGE_CONTEXT(ctx) \ - { \ - (ctx)->fFlags = 0; \ - } - +#define INIT_PARSE_MESSAGE_CONTEXT(ctx) { (ctx)->fFlags = 0; } #define TEST_PARSE_MESSAGE_CONTEXT_FLAG(ctx, flag) ((ctx)->fFlags & (flag)) #define SET_PARSE_MESSAGE_CONTEXT_FLAG(ctx, flag) ((ctx)->fFlags |= (flag)) #define CLEAR_PARSE_MESSAGE_CONTEXT_FLAG(ctx, flag) ((ctx)->fFlags &= ~(flag)) @@ -4107,6 +4693,7 @@ RtlRestoreLastWin32Error( _In_ LONG Win32Error ); +#define RTL_ERRORMODE_FAILCRITICALERRORS 0x0010 #define RTL_ERRORMODE_NOGPFAULTERRORBOX 0x0020 #define RTL_ERRORMODE_NOOPENFILEERRORBOX 0x0040 @@ -4139,6 +4726,19 @@ RtlReportException( ); #endif +#if (PHNT_VERSION >= PHNT_REDSTONE) +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlReportExceptionEx( + _In_ PEXCEPTION_RECORD ExceptionRecord, + _In_ PCONTEXT ContextRecord, + _In_ ULONG Flags, + _In_ PLARGE_INTEGER Timeout + ); +#endif + #if (PHNT_VERSION >= PHNT_VISTA) // private NTSYSAPI @@ -4222,7 +4822,7 @@ NTSYSAPI NTSTATUS NTAPI RtlComputeImportTableHash( - _In_ HANDLE hFile, + _In_ HANDLE FileHandle, _Out_writes_bytes_(16) PCHAR Hash, _In_ ULONG ImportTableHashRevision // must be 1 ); @@ -4300,15 +4900,15 @@ NTSYSAPI PWSTR NTAPI RtlIpv4AddressToStringW( - _In_ struct in_addr *Addr, - _Out_writes_(16) PWSTR S + _In_ const struct in_addr *Address, + _Out_writes_(16) PWSTR AddressString ); NTSYSAPI NTSTATUS NTAPI RtlIpv4AddressToStringExW( - _In_ struct in_addr *Address, + _In_ const struct in_addr *Address, _In_ USHORT Port, _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWSTR AddressString, _Inout_ PULONG AddressStringLength @@ -4318,15 +4918,15 @@ NTSYSAPI PWSTR NTAPI RtlIpv6AddressToStringW( - _In_ struct in6_addr *Addr, - _Out_writes_(65) PWSTR S + _In_ const struct in6_addr *Address, + _Out_writes_(46) PWSTR AddressString ); NTSYSAPI NTSTATUS NTAPI RtlIpv6AddressToStringExW( - _In_ struct in6_addr *Address, + _In_ const struct in6_addr *Address, _In_ ULONG ScopeId, _In_ USHORT Port, _Out_writes_to_(*AddressStringLength, *AddressStringLength) PWSTR AddressString, @@ -4337,17 +4937,17 @@ NTSYSAPI NTSTATUS NTAPI RtlIpv4StringToAddressW( - _In_ PWSTR S, + _In_ PCWSTR AddressString, _In_ BOOLEAN Strict, - _Out_ PWSTR *Terminator, - _Out_ struct in_addr *Addr + _Out_ LPCWSTR *Terminator, + _Out_ struct in_addr *Address ); NTSYSAPI NTSTATUS NTAPI RtlIpv4StringToAddressExW( - _In_ PWSTR AddressString, + _In_ PCWSTR AddressString, _In_ BOOLEAN Strict, _Out_ struct in_addr *Address, _Out_ PUSHORT Port @@ -4357,16 +4957,16 @@ NTSYSAPI NTSTATUS NTAPI RtlIpv6StringToAddressW( - _In_ PWSTR S, - _Out_ PWSTR *Terminator, - _Out_ struct in6_addr *Addr + _In_ PCWSTR AddressString, + _Out_ PCWSTR *Terminator, + _Out_ struct in6_addr *Address ); NTSYSAPI NTSTATUS NTAPI RtlIpv6StringToAddressExW( - _In_ PWSTR AddressString, + _In_ PCWSTR AddressString, _Out_ struct in6_addr *Address, _Out_ PULONG ScopeId, _Out_ PUSHORT Port @@ -4624,6 +5224,20 @@ RtlSetBits( _In_range_(0, BitMapHeader->SizeOfBitMap - StartingIndex) ULONG NumberToSet ); +NTSYSAPI +CCHAR +NTAPI +RtlFindMostSignificantBit( + _In_ ULONGLONG Set + ); + +NTSYSAPI +CCHAR +NTAPI +RtlFindLeastSignificantBit( + _In_ ULONGLONG Set + ); + typedef struct _RTL_BITMAP_RUN { ULONG StartingIndex; @@ -4723,6 +5337,17 @@ RtlFindLastBackwardRunClear( _Out_ PULONG StartingRunIndex ); +#if (PHNT_VERSION >= PHNT_VISTA) + +NTSYSAPI +ULONG +NTAPI +RtlNumberOfSetBitsUlongPtr( + _In_ ULONG_PTR Target + ); + +#endif + #if (PHNT_VERSION >= PHNT_WIN7) // rev @@ -4747,77 +5372,197 @@ RtlInterlockedSetBitRun( #endif -// Handle tables - -typedef struct _RTL_HANDLE_TABLE_ENTRY -{ - union - { - ULONG Flags; // allocated entries have the low bit set - struct _RTL_HANDLE_TABLE_ENTRY *NextFree; - }; -} RTL_HANDLE_TABLE_ENTRY, *PRTL_HANDLE_TABLE_ENTRY; - -#define RTL_HANDLE_ALLOCATED (USHORT)0x0001 - -typedef struct _RTL_HANDLE_TABLE -{ - ULONG MaximumNumberOfHandles; - ULONG SizeOfHandleTableEntry; - ULONG Reserved[2]; - PRTL_HANDLE_TABLE_ENTRY FreeHandles; - PRTL_HANDLE_TABLE_ENTRY CommittedHandles; - PRTL_HANDLE_TABLE_ENTRY UnCommittedHandles; - PRTL_HANDLE_TABLE_ENTRY MaxReservedHandles; -} RTL_HANDLE_TABLE, *PRTL_HANDLE_TABLE; +#if (PHNT_VERSION >= PHNT_WIN8) NTSYSAPI VOID NTAPI -RtlInitializeHandleTable( - _In_ ULONG MaximumNumberOfHandles, - _In_ ULONG SizeOfHandleTableEntry, - _Out_ PRTL_HANDLE_TABLE HandleTable +RtlCopyBitMap( + _In_ PRTL_BITMAP Source, + _In_ PRTL_BITMAP Destination, + _In_range_(0, Destination->SizeOfBitMap - 1) ULONG TargetBit ); NTSYSAPI -NTSTATUS +VOID NTAPI -RtlDestroyHandleTable( - _Inout_ PRTL_HANDLE_TABLE HandleTable +RtlExtractBitMap( + _In_ PRTL_BITMAP Source, + _In_ PRTL_BITMAP Destination, + _In_range_(0, Source->SizeOfBitMap - 1) ULONG TargetBit, + _In_range_(0, Source->SizeOfBitMap) ULONG NumberOfBits ); NTSYSAPI -PRTL_HANDLE_TABLE_ENTRY +ULONG NTAPI -RtlAllocateHandle( - _In_ PRTL_HANDLE_TABLE HandleTable, - _Out_opt_ PULONG HandleIndex +RtlNumberOfClearBitsInRange( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG StartingIndex, + _In_ ULONG Length ); NTSYSAPI -BOOLEAN +ULONG NTAPI -RtlFreeHandle( - _In_ PRTL_HANDLE_TABLE HandleTable, - _In_ PRTL_HANDLE_TABLE_ENTRY Handle +RtlNumberOfSetBitsInRange( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG StartingIndex, + _In_ ULONG Length ); +#endif + + +#if (PHNT_VERSION >= PHNT_THRESHOLD) + +// private +typedef struct _RTL_BITMAP_EX +{ + ULONG64 SizeOfBitMap; + PULONG64 Buffer; +} RTL_BITMAP_EX, *PRTL_BITMAP_EX; + +// rev NTSYSAPI -BOOLEAN +VOID NTAPI -RtlIsValidHandle( - _In_ PRTL_HANDLE_TABLE HandleTable, - _In_ PRTL_HANDLE_TABLE_ENTRY Handle +RtlInitializeBitMapEx( + _Out_ PRTL_BITMAP_EX BitMapHeader, + _In_ PULONG64 BitMapBuffer, + _In_ ULONG64 SizeOfBitMap ); +// rev +_Check_return_ NTSYSAPI BOOLEAN NTAPI -RtlIsValidIndexHandle( - _In_ PRTL_HANDLE_TABLE HandleTable, - _In_ ULONG HandleIndex, - _Out_ PRTL_HANDLE_TABLE_ENTRY *Handle +RtlTestBitEx( + _In_ PRTL_BITMAP_EX BitMapHeader, + _In_range_(<, BitMapHeader->SizeOfBitMap) ULONG64 BitNumber + ); + +#if (PHNT_MODE == PHNT_MODE_KERNEL) +// rev +NTSYSAPI +VOID +NTAPI +RtlClearAllBitsEx( + _In_ PRTL_BITMAP_EX BitMapHeader + ); + +// rev +NTSYSAPI +VOID +NTAPI +RtlClearBitEx( + _In_ PRTL_BITMAP_EX BitMapHeader, + _In_range_(<, BitMapHeader->SizeOfBitMap) ULONG64 BitNumber + ); + +// rev +NTSYSAPI +VOID +NTAPI +RtlSetBitEx( + _In_ PRTL_BITMAP_EX BitMapHeader, + _In_range_(<, BitMapHeader->SizeOfBitMap) ULONG64 BitNumber + ); + +// rev +NTSYSAPI +ULONG64 +NTAPI +RtlFindSetBitsEx( + _In_ PRTL_BITMAP_EX BitMapHeader, + _In_ ULONG64 NumberToFind, + _In_ ULONG64 HintIndex + ); + +NTSYSAPI +ULONG64 +NTAPI +RtlFindSetBitsAndClearEx( + _In_ PRTL_BITMAP_EX BitMapHeader, + _In_ ULONG64 NumberToFind, + _In_ ULONG64 HintIndex + ); +#endif + +#endif + +// Handle tables + +typedef struct _RTL_HANDLE_TABLE_ENTRY +{ + union + { + ULONG Flags; // allocated entries have the low bit set + struct _RTL_HANDLE_TABLE_ENTRY *NextFree; + }; +} RTL_HANDLE_TABLE_ENTRY, *PRTL_HANDLE_TABLE_ENTRY; + +#define RTL_HANDLE_ALLOCATED (USHORT)0x0001 + +typedef struct _RTL_HANDLE_TABLE +{ + ULONG MaximumNumberOfHandles; + ULONG SizeOfHandleTableEntry; + ULONG Reserved[2]; + PRTL_HANDLE_TABLE_ENTRY FreeHandles; + PRTL_HANDLE_TABLE_ENTRY CommittedHandles; + PRTL_HANDLE_TABLE_ENTRY UnCommittedHandles; + PRTL_HANDLE_TABLE_ENTRY MaxReservedHandles; +} RTL_HANDLE_TABLE, *PRTL_HANDLE_TABLE; + +NTSYSAPI +VOID +NTAPI +RtlInitializeHandleTable( + _In_ ULONG MaximumNumberOfHandles, + _In_ ULONG SizeOfHandleTableEntry, + _Out_ PRTL_HANDLE_TABLE HandleTable + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDestroyHandleTable( + _Inout_ PRTL_HANDLE_TABLE HandleTable + ); + +NTSYSAPI +PRTL_HANDLE_TABLE_ENTRY +NTAPI +RtlAllocateHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _Out_opt_ PULONG HandleIndex + ); + +NTSYSAPI +BOOLEAN +NTAPI +RtlFreeHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _In_ PRTL_HANDLE_TABLE_ENTRY Handle + ); + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsValidHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _In_ PRTL_HANDLE_TABLE_ENTRY Handle + ); + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsValidIndexHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _In_ ULONG HandleIndex, + _Out_ PRTL_HANDLE_TABLE_ENTRY *Handle ); // Atom tables @@ -4927,6 +5672,15 @@ RtlEqualSid( _In_ PSID Sid2 ); +_Check_return_ +NTSYSAPI +BOOLEAN +NTAPI +RtlEqualPrefixSid( + _In_ PSID Sid1, + _In_ PSID Sid2 + ); + NTSYSAPI ULONG NTAPI @@ -5018,7 +5772,22 @@ RtlCopySid( _In_ PSID SourceSid ); +// ros +NTSYSAPI +NTSTATUS +NTAPI +RtlCopySidAndAttributesArray( + _In_ ULONG Count, + _In_ PSID_AND_ATTRIBUTES Src, + _In_ ULONG SidAreaSize, + _In_ PSID_AND_ATTRIBUTES Dest, + _In_ PSID SidArea, + _Out_ PSID *RemainingSidArea, + _Out_ PULONG RemainingSidAreaSize + ); + #if (PHNT_VERSION >= PHNT_VISTA) + NTSYSAPI NTSTATUS NTAPI @@ -5027,9 +5796,11 @@ RtlCreateServiceSid( _Out_writes_bytes_opt_(*ServiceSidLength) PSID ServiceSid, _Inout_ PULONG ServiceSidLength ); + #endif #if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -5037,8 +5808,23 @@ NTAPI RtlSidDominates( _In_ PSID Sid1, _In_ PSID Sid2, - _Out_ PBOOLEAN pbDominate + _Out_ PBOOLEAN Dominates + ); + +#endif + +#if (PHNT_VERSION >= PHNT_WINBLUE) + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlSidDominatesForTrust( + _In_ PSID Sid1, + _In_ PSID Sid2, + _Out_ PBOOLEAN DominatesTrust // TokenProcessTrustLevel ); + #endif #if (PHNT_VERSION >= PHNT_VISTA) @@ -5049,11 +5835,9 @@ NTAPI RtlSidEqualLevel( _In_ PSID Sid1, _In_ PSID Sid2, - _Out_ PBOOLEAN pbEqual + _Out_ PBOOLEAN EqualLevel ); -#endif -#if (PHNT_VERSION >= PHNT_VISTA) // private NTSYSAPI NTSTATUS @@ -5061,7 +5845,7 @@ NTAPI RtlSidIsHigherLevel( _In_ PSID Sid1, _In_ PSID Sid2, - _Out_ PBOOLEAN pbHigher + _Out_ PBOOLEAN HigherLevel ); #endif @@ -5123,6 +5907,28 @@ RtlSidHashLookup( ); #endif +#if (PHNT_VERSION >= PHNT_VISTA) +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsElevatedRid( + _In_ PSID_AND_ATTRIBUTES SidAttr + ); +#endif + +#if (PHNT_VERSION >= PHNT_REDSTONE2) +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlDeriveCapabilitySidsFromName( + _Inout_ PUNICODE_STRING UnicodeString, + _Out_ PSID CapabilityGroupSid, + _Out_ PSID CapabilitySid + ); +#endif + // Security Descriptors NTSYSAPI @@ -5588,6 +6394,15 @@ RtlAddMandatoryAce( ); #endif +// Named pipes + +NTSYSAPI +NTSTATUS +NTAPI +RtlDefaultNpAcl( + _Out_ PACL *Acl + ); + // Security objects NTSYSAPI @@ -5781,7 +6596,7 @@ NTSYSAPI NTSTATUS NTAPI RtlRemovePrivileges( - _In_ HANDLE hToken, + _In_ HANDLE TokenHandle, _In_ PULONG PrivilegesToKeep, _In_ ULONG PrivilegeCount ); @@ -5789,20 +6604,21 @@ RtlRemovePrivileges( #if (PHNT_VERSION >= PHNT_WIN8) +// rev NTSYSAPI NTSTATUS NTAPI RtlIsUntrustedObject( _In_opt_ HANDLE Handle, _In_opt_ PVOID Object, - _Out_ PBOOLEAN UntrustedObject + _Out_ PBOOLEAN IsUntrustedObject ); NTSYSAPI ULONG NTAPI RtlQueryValidationRunlevel( - _In_opt_ PCUNICODE_STRING ComponentName + _In_opt_ PUNICODE_STRING ComponentName ); #endif @@ -5869,8 +6685,19 @@ RtlVerifyVersionInfo( _In_ ULONGLONG ConditionMask ); +// rev +NTSYSAPI +VOID +NTAPI +RtlGetNtVersionNumbers( + _Out_opt_ PULONG NtMajorVersion, + _Out_opt_ PULONG NtMinorVersion, + _Out_opt_ PULONG NtBuildNumber + ); + // System information +// rev NTSYSAPI ULONG NTAPI @@ -5878,22 +6705,25 @@ RtlGetNtGlobalFlags( VOID ); +#if (PHNT_VERSION >= PHNT_REDSTONE) +// rev NTSYSAPI BOOLEAN NTAPI RtlGetNtProductType( _Out_ PNT_PRODUCT_TYPE NtProductType ); +#endif -// rev +#if (PHNT_VERSION >= PHNT_REDSTONE2) +// private NTSYSAPI -VOID +ULONG NTAPI -RtlGetNtVersionNumbers( - _Out_opt_ PULONG pNtMajorVersion, - _Out_opt_ PULONG pNtMinorVersion, - _Out_opt_ PULONG pNtBuildNumber +RtlGetSuiteMask( + VOID ); +#endif // Thread pool (old) @@ -5942,6 +6772,42 @@ RtlSetIoCompletionCallback( _In_ ULONG Flags ); +typedef NTSTATUS (NTAPI *PRTL_START_POOL_THREAD)( + _In_ PTHREAD_START_ROUTINE Function, + _In_ PVOID Parameter, + _Out_ PHANDLE ThreadHandle + ); + +typedef NTSTATUS (NTAPI *PRTL_EXIT_POOL_THREAD)( + _In_ NTSTATUS ExitStatus + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetThreadPoolStartFunc( + _In_ PRTL_START_POOL_THREAD StartPoolThread, + _In_ PRTL_EXIT_POOL_THREAD ExitPoolThread + ); + +NTSYSAPI +VOID +NTAPI +RtlUserThreadStart( + _In_ PTHREAD_START_ROUTINE Function, + _In_ PVOID Parameter + ); + +NTSYSAPI +VOID +NTAPI +LdrInitializeThunk( + _In_ PCONTEXT ContextRecord, + _In_ PVOID Parameter + ); + +// Timer support + NTSYSAPI NTSTATUS NTAPI @@ -5956,7 +6822,7 @@ RtlCreateTimer( _In_ HANDLE TimerQueueHandle, _Out_ PHANDLE Handle, _In_ WAITORTIMERCALLBACKFUNC Function, - _In_ PVOID Context, + _In_opt_ PVOID Context, _In_ ULONG DueTime, _In_ ULONG Period, _In_ ULONG Flags @@ -6078,6 +6944,18 @@ RtlQueryRegistryValues( _In_opt_ PVOID Environment ); +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryRegistryValuesEx( + _In_ ULONG RelativeTo, + _In_ PWSTR Path, + _In_ PRTL_QUERY_REGISTRY_TABLE QueryTable, + _In_ PVOID Context, + _In_opt_ PVOID Environment + ); + NTSYSAPI NTSTATUS NTAPI @@ -6099,147 +6977,51 @@ RtlDeleteRegistryValue( _In_ PWSTR ValueName ); -// Debugging +// Thread profiling +#if (PHNT_VERSION >= PHNT_WIN7) + +// rev NTSYSAPI -VOID +NTSTATUS NTAPI -DbgUserBreakPoint( - VOID +RtlEnableThreadProfiling( + _In_ HANDLE ThreadHandle, + _In_ ULONG Flags, + _In_ ULONG64 HardwareCounters, + _Out_ PVOID *PerformanceDataHandle ); +// rev NTSYSAPI -VOID +NTSTATUS NTAPI -DbgBreakPoint( - VOID +RtlDisableThreadProfiling( + _In_ PVOID PerformanceDataHandle ); +// rev NTSYSAPI -VOID +NTSTATUS NTAPI -DbgBreakPointWithStatus( - _In_ ULONG Status +RtlQueryThreadProfiling( + _In_ HANDLE ThreadHandle, + _Out_ PBOOLEAN Enabled ); -#define DBG_STATUS_CONTROL_C 1 -#define DBG_STATUS_SYSRQ 2 -#define DBG_STATUS_BUGCHECK_FIRST 3 -#define DBG_STATUS_BUGCHECK_SECOND 4 -#define DBG_STATUS_FATAL 5 -#define DBG_STATUS_DEBUG_CONTROL 6 -#define DBG_STATUS_WORKER 7 - +// rev NTSYSAPI -ULONG -__cdecl -DbgPrint( - _In_z_ _Printf_format_string_ PSTR Format, - ... +NTSTATUS +NTAPI +RtlReadThreadProfilingData( + _In_ HANDLE PerformanceDataHandle, + _In_ ULONG Flags, + _Out_ PPERFORMANCE_DATA PerformanceData ); -NTSYSAPI -ULONG -__cdecl -DbgPrintEx( - _In_ ULONG ComponentId, - _In_ ULONG Level, - _In_z_ _Printf_format_string_ PSTR Format, - ... - ); +#endif -NTSYSAPI -ULONG -NTAPI -vDbgPrintEx( - _In_ ULONG ComponentId, - _In_ ULONG Level, - _In_z_ PCH Format, - _In_ va_list arglist - ); - -NTSYSAPI -ULONG -NTAPI -vDbgPrintExWithPrefix( - _In_z_ PCH Prefix, - _In_ ULONG ComponentId, - _In_ ULONG Level, - _In_z_ PCH Format, - _In_ va_list arglist - ); - -NTSYSAPI -NTSTATUS -NTAPI -DbgQueryDebugFilterState( - _In_ ULONG ComponentId, - _In_ ULONG Level - ); - -NTSYSAPI -NTSTATUS -NTAPI -DbgSetDebugFilterState( - _In_ ULONG ComponentId, - _In_ ULONG Level, - _In_ BOOLEAN State - ); - -NTSYSAPI -ULONG -NTAPI -DbgPrompt( - _In_ PCH Prompt, - _Out_writes_bytes_(Length) PCH Response, - _In_ ULONG Length - ); - -// Thread profiling - -#if (PHNT_VERSION >= PHNT_WIN7) - -// begin_rev - -NTSYSAPI -NTSTATUS -NTAPI -RtlEnableThreadProfiling( - _In_ HANDLE ThreadHandle, - _In_ ULONG Flags, - _In_ ULONG64 HardwareCounters, - _Out_ PVOID *PerformanceDataHandle - ); - -NTSYSAPI -NTSTATUS -NTAPI -RtlDisableThreadProfiling( - _In_ PVOID PerformanceDataHandle - ); - -NTSYSAPI -NTSTATUS -NTAPI -RtlQueryThreadProfiling( - _In_ HANDLE ThreadHandle, - _Out_ PBOOLEAN Enabled - ); - -NTSYSAPI -NTSTATUS -NTAPI -RtlReadThreadProfilingData( - _In_ HANDLE PerformanceDataHandle, - _In_ ULONG Flags, - _Out_ PPERFORMANCE_DATA PerformanceData - ); - -// end_rev - -#endif - -// WOW64 +// WOW64 NTSYSAPI NTSTATUS @@ -6257,9 +7039,9 @@ NTAPI RtlQueueApcWow64Thread( _In_ HANDLE ThreadHandle, _In_ PPS_APC_ROUTINE ApcRoutine, - _In_ PVOID ApcArgument1, - _In_ PVOID ApcArgument2, - _In_ PVOID ApcArgument3 + _In_opt_ PVOID ApcArgument1, + _In_opt_ PVOID ApcArgument2, + _In_opt_ PVOID ApcArgument3 ); NTSYSAPI @@ -6279,13 +7061,6 @@ RtlWow64EnableFsRedirectionEx( // Misc. -NTSYSAPI -ULONG -NTAPI -RtlGetCurrentProcessorNumber( - VOID - ); - NTSYSAPI ULONG32 NTAPI @@ -6323,13 +7098,58 @@ RtlDecodeSystemPointer( _In_ PVOID Ptr ); +#if (PHNT_VERSION >= PHNT_THRESHOLD) +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlEncodeRemotePointer( + _In_ HANDLE ProcessHandle, + _In_ PVOID Pointer, + _Out_ PVOID *EncodedPointer + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlDecodeRemotePointer( + _In_ HANDLE ProcessHandle, + _In_ PVOID Pointer, + _Out_ PVOID *DecodedPointer + ); +#endif + +// rev NTSYSAPI BOOLEAN NTAPI -RtlIsThreadWithinLoaderCallout( +RtlIsProcessorFeaturePresent( + _In_ ULONG ProcessorFeature + ); + +// rev +NTSYSAPI +ULONG +NTAPI +RtlGetCurrentProcessorNumber( VOID ); +#if (PHNT_VERSION >= PHNT_THRESHOLD) + +// rev +NTSYSAPI +VOID +NTAPI +RtlGetCurrentProcessorNumberEx( + _Out_ PPROCESSOR_NUMBER ProcessorNumber + ); + +#endif + +// Stack support + NTSYSAPI VOID NTAPI @@ -6351,19 +7171,65 @@ RtlGetFrame( VOID ); -typedef ULONG (NTAPI *PRTLP_UNHANDLED_EXCEPTION_FILTER)( - struct _EXCEPTION_POINTERS *ExceptionInfo +#define RTL_WALK_USER_MODE_STACK 0x00000001 +#define RTL_WALK_VALID_FLAGS 0x00000001 +#define RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT 0x00000008 + +// private +NTSYSAPI +ULONG +NTAPI +RtlWalkFrameChain( + _Out_writes_(Count - (Flags >> RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT)) PVOID *Callers, + _In_ ULONG Count, + _In_ ULONG Flags ); +// rev NTSYSAPI VOID NTAPI -RtlSetUnhandledExceptionFilter( - _In_ PRTLP_UNHANDLED_EXCEPTION_FILTER UnhandledExceptionFilter +RtlGetCallersAddress( // Use the intrinsic _ReturnAddress instead. + _Out_ PVOID *CallersAddress, + _Out_ PVOID *CallersCaller ); -// begin_private +#if (PHNT_VERSION >= PHNT_WIN7) + +NTSYSAPI +ULONG64 +NTAPI +RtlGetEnabledExtendedFeatures( + _In_ ULONG64 FeatureMask + ); + +#endif +#if (PHNT_VERSION >= PHNT_REDSTONE4) + +// msdn +NTSYSAPI +ULONG64 +NTAPI +RtlGetEnabledExtendedAndSupervisorFeatures( + _In_ ULONG64 FeatureMask + ); + +// msdn +_Ret_maybenull_ +_Success_(return != NULL) +NTSYSAPI +PVOID +NTAPI +RtlLocateSupervisorFeature( + _In_ PXSAVE_AREA_HEADER XStateHeader, + _In_range_(XSTATE_AVX, MAXIMUM_XSTATE_FEATURES - 1) ULONG FeatureId, + _Out_opt_ PULONG Length + ); + +#endif + +// private typedef union _RTL_ELEVATION_FLAGS { ULONG Flags; @@ -6377,17 +7243,19 @@ typedef union _RTL_ELEVATION_FLAGS } RTL_ELEVATION_FLAGS, *PRTL_ELEVATION_FLAGS; #if (PHNT_VERSION >= PHNT_VISTA) + +// private NTSYSAPI NTSTATUS NTAPI RtlQueryElevationFlags( _Out_ PRTL_ELEVATION_FLAGS Flags ); -#endif -// end_private +#endif #if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -6395,9 +7263,11 @@ NTAPI RtlRegisterThreadWithCsrss( VOID ); + #endif #if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -6405,9 +7275,11 @@ NTAPI RtlLockCurrentThread( VOID ); + #endif #if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -6415,9 +7287,11 @@ NTAPI RtlUnlockCurrentThread( VOID ); + #endif #if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -6425,9 +7299,11 @@ NTAPI RtlLockModuleSection( _In_ PVOID Address ); + #endif #if (PHNT_VERSION >= PHNT_VISTA) + // private NTSYSAPI NTSTATUS @@ -6435,6 +7311,7 @@ NTAPI RtlUnlockModuleSection( _In_ PVOID Address ); + #endif // begin_msdn:"Winternl" @@ -6453,6 +7330,17 @@ typedef struct _RTL_UNLOAD_EVENT_TRACE ULONG Version[2]; } RTL_UNLOAD_EVENT_TRACE, *PRTL_UNLOAD_EVENT_TRACE; +typedef struct _RTL_UNLOAD_EVENT_TRACE32 +{ + ULONG BaseAddress; + ULONG SizeOfImage; + ULONG Sequence; + ULONG TimeDateStamp; + ULONG CheckSum; + WCHAR ImageName[32]; + ULONG Version[2]; +} RTL_UNLOAD_EVENT_TRACE32, *PRTL_UNLOAD_EVENT_TRACE32; + NTSYSAPI PRTL_UNLOAD_EVENT_TRACE NTAPI @@ -6493,26 +7381,658 @@ RtlQueryPerformanceFrequency( ); #endif -#if (PHNT_VERSION >= PHNT_WIN8) +// Image Mitigation + +// rev +typedef enum _IMAGE_MITIGATION_POLICY +{ + ImageDepPolicy, // RTL_IMAGE_MITIGATION_DEP_POLICY + ImageAslrPolicy, // RTL_IMAGE_MITIGATION_ASLR_POLICY + ImageDynamicCodePolicy, // RTL_IMAGE_MITIGATION_DYNAMIC_CODE_POLICY + ImageStrictHandleCheckPolicy, // RTL_IMAGE_MITIGATION_STRICT_HANDLE_CHECK_POLICY + ImageSystemCallDisablePolicy, // RTL_IMAGE_MITIGATION_SYSTEM_CALL_DISABLE_POLICY + ImageMitigationOptionsMask, + ImageExtensionPointDisablePolicy, // RTL_IMAGE_MITIGATION_EXTENSION_POINT_DISABLE_POLICY + ImageControlFlowGuardPolicy, // RTL_IMAGE_MITIGATION_CONTROL_FLOW_GUARD_POLICY + ImageSignaturePolicy, // RTL_IMAGE_MITIGATION_BINARY_SIGNATURE_POLICY + ImageFontDisablePolicy, // RTL_IMAGE_MITIGATION_FONT_DISABLE_POLICY + ImageImageLoadPolicy, // RTL_IMAGE_MITIGATION_IMAGE_LOAD_POLICY + ImagePayloadRestrictionPolicy, // RTL_IMAGE_MITIGATION_PAYLOAD_RESTRICTION_POLICY + ImageChildProcessPolicy, // RTL_IMAGE_MITIGATION_CHILD_PROCESS_POLICY + ImageSehopPolicy, // RTL_IMAGE_MITIGATION_SEHOP_POLICY + ImageHeapPolicy, // RTL_IMAGE_MITIGATION_HEAP_POLICY + MaxImageMitigationPolicy +} IMAGE_MITIGATION_POLICY; + +// rev +typedef union _RTL_IMAGE_MITIGATION_POLICY +{ + struct + { + ULONG64 AuditState : 2; + ULONG64 AuditFlag : 1; + ULONG64 EnableAdditionalAuditingOption : 1; + ULONG64 Reserved : 60; + }; + struct + { + ULONG64 PolicyState : 2; + ULONG64 AlwaysInherit : 1; + ULONG64 EnableAdditionalPolicyOption : 1; + ULONG64 AuditReserved : 60; + }; +} RTL_IMAGE_MITIGATION_POLICY, *PRTL_IMAGE_MITIGATION_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_DEP_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY Dep; +} RTL_IMAGE_MITIGATION_DEP_POLICY, *PRTL_IMAGE_MITIGATION_DEP_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_ASLR_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY ForceRelocateImages; + RTL_IMAGE_MITIGATION_POLICY BottomUpRandomization; + RTL_IMAGE_MITIGATION_POLICY HighEntropyRandomization; +} RTL_IMAGE_MITIGATION_ASLR_POLICY, *PRTL_IMAGE_MITIGATION_ASLR_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_DYNAMIC_CODE_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY BlockDynamicCode; +} RTL_IMAGE_MITIGATION_DYNAMIC_CODE_POLICY, *PRTL_IMAGE_MITIGATION_DYNAMIC_CODE_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_STRICT_HANDLE_CHECK_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY StrictHandleChecks; +} RTL_IMAGE_MITIGATION_STRICT_HANDLE_CHECK_POLICY, *PRTL_IMAGE_MITIGATION_STRICT_HANDLE_CHECK_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_SYSTEM_CALL_DISABLE_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY BlockWin32kSystemCalls; +} RTL_IMAGE_MITIGATION_SYSTEM_CALL_DISABLE_POLICY, *PRTL_IMAGE_MITIGATION_SYSTEM_CALL_DISABLE_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_EXTENSION_POINT_DISABLE_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY DisableExtensionPoints; +} RTL_IMAGE_MITIGATION_EXTENSION_POINT_DISABLE_POLICY, *PRTL_IMAGE_MITIGATION_EXTENSION_POINT_DISABLE_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_CONTROL_FLOW_GUARD_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY ControlFlowGuard; + RTL_IMAGE_MITIGATION_POLICY StrictControlFlowGuard; +} RTL_IMAGE_MITIGATION_CONTROL_FLOW_GUARD_POLICY, *PRTL_IMAGE_MITIGATION_CONTROL_FLOW_GUARD_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_BINARY_SIGNATURE_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY BlockNonMicrosoftSignedBinaries; + RTL_IMAGE_MITIGATION_POLICY EnforceSigningOnModuleDependencies; +} RTL_IMAGE_MITIGATION_BINARY_SIGNATURE_POLICY, *PRTL_IMAGE_MITIGATION_BINARY_SIGNATURE_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_FONT_DISABLE_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY DisableNonSystemFonts; +} RTL_IMAGE_MITIGATION_FONT_DISABLE_POLICY, *PRTL_IMAGE_MITIGATION_FONT_DISABLE_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_IMAGE_LOAD_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY BlockRemoteImageLoads; + RTL_IMAGE_MITIGATION_POLICY BlockLowLabelImageLoads; + RTL_IMAGE_MITIGATION_POLICY PreferSystem32; +} RTL_IMAGE_MITIGATION_IMAGE_LOAD_POLICY, *PRTL_IMAGE_MITIGATION_IMAGE_LOAD_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_PAYLOAD_RESTRICTION_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY EnableExportAddressFilter; + RTL_IMAGE_MITIGATION_POLICY EnableExportAddressFilterPlus; + RTL_IMAGE_MITIGATION_POLICY EnableImportAddressFilter; + RTL_IMAGE_MITIGATION_POLICY EnableRopStackPivot; + RTL_IMAGE_MITIGATION_POLICY EnableRopCallerCheck; + RTL_IMAGE_MITIGATION_POLICY EnableRopSimExec; + WCHAR EafPlusModuleList[512]; // 19H1 +} RTL_IMAGE_MITIGATION_PAYLOAD_RESTRICTION_POLICY, *PRTL_IMAGE_MITIGATION_PAYLOAD_RESTRICTION_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_CHILD_PROCESS_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY DisallowChildProcessCreation; +} RTL_IMAGE_MITIGATION_CHILD_PROCESS_POLICY, *PRTL_IMAGE_MITIGATION_CHILD_PROCESS_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_SEHOP_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY Sehop; +} RTL_IMAGE_MITIGATION_SEHOP_POLICY, *PRTL_IMAGE_MITIGATION_SEHOP_POLICY; + +// rev +typedef struct _RTL_IMAGE_MITIGATION_HEAP_POLICY +{ + RTL_IMAGE_MITIGATION_POLICY TerminateOnHeapErrors; +} RTL_IMAGE_MITIGATION_HEAP_POLICY, *PRTL_IMAGE_MITIGATION_HEAP_POLICY; + +typedef enum _RTL_IMAGE_MITIGATION_OPTION_STATE +{ + RtlMitigationOptionStateNotConfigured, + RtlMitigationOptionStateOn, + RtlMitigationOptionStateOff +} RTL_IMAGE_MITIGATION_OPTION_STATE; + +// rev from PROCESS_MITIGATION_FLAGS +#define RTL_IMAGE_MITIGATION_FLAG_RESET 0x1 +#define RTL_IMAGE_MITIGATION_FLAG_REMOVE 0x2 +#define RTL_IMAGE_MITIGATION_FLAG_OSDEFAULT 0x4 +#define RTL_IMAGE_MITIGATION_FLAG_AUDIT 0x8 + +#if (PHNT_VERSION >= PHNT_REDSTONE3) + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryImageMitigationPolicy( + _In_opt_ PWSTR ImagePath, // NULL for system-wide defaults + _In_ IMAGE_MITIGATION_POLICY Policy, + _In_ ULONG Flags, + _Inout_ PVOID Buffer, + _In_ ULONG BufferSize + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlSetImageMitigationPolicy( + _In_opt_ PWSTR ImagePath, // NULL for system-wide defaults + _In_ IMAGE_MITIGATION_POLICY Policy, + _In_ ULONG Flags, + _Inout_ PVOID Buffer, + _In_ ULONG BufferSize + ); + +#endif + +// session + +// rev +NTSYSAPI +ULONG +NTAPI +RtlGetCurrentServiceSessionId( + VOID + ); +// private NTSYSAPI ULONG NTAPI -RtlCrc32( - _In_reads_bytes_(Size) const void *Buffer, - _In_ size_t Size, - _In_ ULONG InitialCrc +RtlGetActiveConsoleId( + VOID ); +#if (PHNT_VERSION >= PHNT_REDSTONE) +// private NTSYSAPI ULONGLONG NTAPI -RtlCrc64( - _In_reads_bytes_(Size) const void *Buffer, - _In_ size_t Size, - _In_ ULONGLONG InitialCrc +RtlGetConsoleSessionForegroundProcessId( + VOID ); - #endif +// Appcontainer + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlGetTokenNamedObjectPath( + _In_ HANDLE Token, + _In_opt_ PSID Sid, + _Out_ PUNICODE_STRING ObjectPath // RtlFreeUnicodeString + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlGetAppContainerNamedObjectPath( + _In_opt_ HANDLE Token, + _In_opt_ PSID AppContainerSid, + _In_ BOOLEAN RelativePath, + _Out_ PUNICODE_STRING ObjectPath // RtlFreeUnicodeString + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlGetAppContainerParent( + _In_ PSID AppContainerSid, + _Out_ PSID* AppContainerSidParent // RtlFreeSid + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckSandboxedToken( + _In_opt_ HANDLE TokenHandle, + _Out_ PBOOLEAN IsSandboxed + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckTokenCapability( + _In_opt_ HANDLE TokenHandle, + _In_ PSID CapabilitySidToCheck, + _Out_ PBOOLEAN HasCapability + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCapabilityCheck( + _In_opt_ HANDLE TokenHandle, + _In_ PUNICODE_STRING CapabilityName, + _Out_ PBOOLEAN HasCapability + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckTokenMembership( + _In_opt_ HANDLE TokenHandle, + _In_ PSID SidToCheck, + _Out_ PBOOLEAN IsMember + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckTokenMembershipEx( + _In_opt_ HANDLE TokenHandle, + _In_ PSID SidToCheck, + _In_ ULONG Flags, // CTMF_VALID_FLAGS + _Out_ PBOOLEAN IsMember + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryTokenHostIdAsUlong64( + _In_ HANDLE TokenHandle, + _Out_ PULONG64 HostId // (WIN://PKGHOSTID) + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlIsParentOfChildAppContainer( + _In_ PSID ParentAppContainerSid, + _In_ PSID ChildAppContainerSid + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsCapabilitySid( + _In_ PSID Sid + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsPackageSid( + _In_ PSID Sid + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsValidProcessTrustLabelSid( + _In_ PSID Sid + ); + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsStateSeparationEnabled( + VOID + ); + +typedef enum _APPCONTAINER_SID_TYPE +{ + NotAppContainerSidType, + ChildAppContainerSidType, + ParentAppContainerSidType, + InvalidAppContainerSidType, + MaxAppContainerSidType +} APPCONTAINER_SID_TYPE, *PAPPCONTAINER_SID_TYPE; + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlGetAppContainerSidType( + _In_ PSID AppContainerSid, + _Out_ PAPPCONTAINER_SID_TYPE AppContainerSidType + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlFlsAlloc( + _In_ PFLS_CALLBACK_FUNCTION Callback, + _Out_ PULONG FlsIndex + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlFlsFree( + _In_ ULONG FlsIndex + ); + +typedef enum _STATE_LOCATION_TYPE +{ + LocationTypeRegistry, + LocationTypeFileSystem, + LocationTypeMaximum +} STATE_LOCATION_TYPE; + +// private +NTSYSAPI +NTSTATUS +NTAPI +RtlGetPersistedStateLocation( + _In_ PCWSTR SourceID, + _In_opt_ PCWSTR CustomValue, + _In_opt_ PCWSTR DefaultPath, + _In_ STATE_LOCATION_TYPE StateLocationType, + _Out_writes_bytes_to_opt_(BufferLengthIn, *BufferLengthOut) PWCHAR TargetPath, + _In_ ULONG BufferLengthIn, + _Out_opt_ PULONG BufferLengthOut + ); + +// msdn +NTSYSAPI +BOOLEAN +NTAPI +RtlIsCloudFilesPlaceholder( + _In_ ULONG FileAttributes, + _In_ ULONG ReparseTag + ); + +// msdn +NTSYSAPI +BOOLEAN +NTAPI +RtlIsPartialPlaceholder( + _In_ ULONG FileAttributes, + _In_ ULONG ReparseTag + ); + +// msdn +NTSYSAPI +NTSTATUS +NTAPI +RtlIsPartialPlaceholderFileHandle( + _In_ HANDLE FileHandle, + _Out_ PBOOLEAN IsPartialPlaceholder + ); + +// msdn +NTSYSAPI +NTSTATUS +NTAPI +RtlIsPartialPlaceholderFileInfo( + _In_ PVOID InfoBuffer, + _In_ FILE_INFORMATION_CLASS InfoClass, + _Out_ PBOOLEAN IsPartialPlaceholder + ); + +// rev +NTSYSAPI +BOOLEAN +NTAPI +RtlIsNonEmptyDirectoryReparsePointAllowed( + _In_ ULONG ReparseTag + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlAppxIsFileOwnedByTrustedInstaller( + _In_ HANDLE FileHandle, + _Out_ PBOOLEAN IsFileOwnedByTrustedInstaller + ); + +// rev +typedef struct _PS_PKG_CLAIM +{ + ULONGLONG Flags; + ULONGLONG Origin; +} PS_PKG_CLAIM, *PPS_PKG_CLAIM; + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryPackageClaims( + _In_ HANDLE TokenHandle, + _Out_writes_bytes_to_opt_(*PackageSize, *PackageSize) PWSTR PackageFullName, + _Inout_opt_ PSIZE_T PackageSize, + _Out_writes_bytes_to_opt_(*AppIdSize, *AppIdSize) PWSTR AppId, + _Inout_opt_ PSIZE_T AppIdSize, + _Out_opt_ PGUID DynamicId, + _Out_opt_ PPS_PKG_CLAIM PkgClaim, + _Out_opt_ PULONG64 AttributesPresent + ); + +// Protected policies + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryProtectedPolicy( + _In_ PGUID PolicyGuid, + _Out_ PULONG_PTR PolicyValue + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlSetProtectedPolicy( + _In_ PGUID PolicyGuid, + _In_ ULONG_PTR PolicyValue, + _Out_ PULONG_PTR OldPolicyValue + ); + +#if (PHNT_VERSION >= PHNT_REDSTONE) +// private +NTSYSAPI +BOOLEAN +NTAPI +RtlIsMultiSessionSku( + VOID + ); +#endif + +#if (PHNT_VERSION >= PHNT_REDSTONE) +// private +NTSYSAPI +BOOLEAN +NTAPI +RtlIsMultiUsersInSessionSku( + VOID + ); +#endif + +// private +typedef enum _RTL_BSD_ITEM_TYPE +{ + RtlBsdItemVersionNumber, + RtlBsdItemProductType, + RtlBsdItemAabEnabled, + RtlBsdItemAabTimeout, + RtlBsdItemBootGood, + RtlBsdItemBootShutdown, + RtlBsdSleepInProgress, + RtlBsdPowerTransition, + RtlBsdItemBootAttemptCount, + RtlBsdItemBootCheckpoint, + RtlBsdItemBootId, + RtlBsdItemShutdownBootId, + RtlBsdItemReportedAbnormalShutdownBootId, + RtlBsdItemErrorInfo, + RtlBsdItemPowerButtonPressInfo, + RtlBsdItemChecksum, + RtlBsdItemMax +} RTL_BSD_ITEM_TYPE; + +// private +typedef struct _RTL_BSD_ITEM +{ + RTL_BSD_ITEM_TYPE Type; + PVOID DataBuffer; + ULONG DataLength; +} RTL_BSD_ITEM, *PRTL_BSD_ITEM; + +// ros +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateBootStatusDataFile( + VOID + ); + +// ros +NTSYSAPI +NTSTATUS +NTAPI +RtlLockBootStatusData( + _Out_ PHANDLE FileHandle + ); + +// ros +NTSYSAPI +NTSTATUS +NTAPI +RtlUnlockBootStatusData( + _In_ HANDLE FileHandle + ); + +// ros +NTSYSAPI +NTSTATUS +NTAPI +RtlGetSetBootStatusData( + _In_ HANDLE FileHandle, + _In_ BOOLEAN Read, + _In_ RTL_BSD_ITEM_TYPE DataClass, + _In_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ReturnLength + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckBootStatusIntegrity( + _In_ HANDLE FileHandle, + _Out_ PBOOLEAN Verified + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckPortableOperatingSystem( + _Out_ PBOOLEAN IsPortable // VOID + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +RtlSetPortableOperatingSystem( + _In_ BOOLEAN IsPortable + ); + +#if (PHNT_VERSION >= PHNT_THRESHOLD) + +NTSYSAPI +OS_DEPLOYEMENT_STATE_VALUES +NTAPI +RtlOsDeploymentState( + _Reserved_ _In_ ULONG Flags + ); + +#endif + +#if (PHNT_VERSION >= PHNT_VISTA) + +NTSYSAPI +NTSTATUS +NTAPI +RtlFindClosestEncodableLength( + _In_ ULONGLONG SourceLength, + _Out_ PULONGLONG TargetLength + ); + +#endif + +// Memory cache + +typedef NTSTATUS (NTAPI *PRTL_SECURE_MEMORY_CACHE_CALLBACK)( + _In_ PVOID Address, + _In_ SIZE_T Length + ); + +// ros +NTSYSAPI +NTSTATUS +NTAPI +RtlRegisterSecureMemoryCacheCallback( + _In_ PRTL_SECURE_MEMORY_CACHE_CALLBACK Callback + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeregisterSecureMemoryCacheCallback( + _In_ PRTL_SECURE_MEMORY_CACHE_CALLBACK Callback + ); + +// ros +NTSYSAPI +BOOLEAN +NTAPI +RtlFlushSecureMemoryCache( + _In_ PVOID MemoryCache, + _In_opt_ SIZE_T MemoryLength + ); + #endif diff --git a/phnt/include/ntseapi.h b/phnt/include/ntseapi.h index 4d93eb5c91f7..9552bbec0bea 100644 --- a/phnt/include/ntseapi.h +++ b/phnt/include/ntseapi.h @@ -1,3 +1,23 @@ +/* + * Process Hacker - + * Authorization functions + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + #ifndef _NTSEAPI_H #define _NTSEAPI_H @@ -39,8 +59,8 @@ #define SE_INC_WORKING_SET_PRIVILEGE (33L) #define SE_TIME_ZONE_PRIVILEGE (34L) #define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) -#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE - +#define SE_DELEGATE_SESSION_USER_IMPERSONATE_PRIVILEGE (36L) +#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_DELEGATE_SESSION_USER_IMPERSONATE_PRIVILEGE // Authz @@ -65,6 +85,7 @@ #define TOKEN_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT 0x0008 #define TOKEN_SECURITY_ATTRIBUTE_DISABLED 0x0010 #define TOKEN_SECURITY_ATTRIBUTE_MANDATORY 0x0020 +#define TOKEN_SECURITY_ATTRIBUTE_COMPARE_IGNORE 0x0040 #define TOKEN_SECURITY_ATTRIBUTE_VALID_FLAGS ( \ TOKEN_SECURITY_ATTRIBUTE_NON_INHERITABLE | \ @@ -127,6 +148,12 @@ typedef struct _TOKEN_SECURITY_ATTRIBUTES_INFORMATION } Attribute; } TOKEN_SECURITY_ATTRIBUTES_INFORMATION, *PTOKEN_SECURITY_ATTRIBUTES_INFORMATION; +// rev +typedef struct _TOKEN_PROCESS_TRUST_LEVEL +{ + PSID TrustLevelSid; +} TOKEN_PROCESS_TRUST_LEVEL, *PTOKEN_PROCESS_TRUST_LEVEL; + // Tokens NTSYSCALLAPI @@ -284,7 +311,7 @@ NtAdjustGroupsToken( _In_opt_ PTOKEN_GROUPS NewState, _In_opt_ ULONG BufferLength, _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength) PTOKEN_GROUPS PreviousState, - _Out_ PULONG ReturnLength + _Out_opt_ PULONG ReturnLength ); #if (PHNT_VERSION >= PHNT_WIN8) @@ -609,27 +636,4 @@ NtPrivilegedServiceAuditAlarm( _In_ BOOLEAN AccessGranted ); -// Misc. - -typedef enum _FILTER_BOOT_OPTION_OPERATION -{ - FilterBootOptionOperationOpenSystemStore, - FilterBootOptionOperationSetElement, - FilterBootOptionOperationDeleteElement, - FilterBootOptionOperationMax -} FILTER_BOOT_OPTION_OPERATION; - -#if (PHNT_VERSION >= PHNT_THRESHOLD) -NTSYSCALLAPI -NTSTATUS -NTAPI -NtFilterBootOption( - _In_ FILTER_BOOT_OPTION_OPERATION FilterOperation, - _In_ ULONG ObjectType, - _In_ ULONG ElementType, - _In_reads_bytes_opt_(DataSize) PVOID Data, - _In_ ULONG DataSize - ); -#endif - #endif diff --git a/phnt/include/ntsmss.h b/phnt/include/ntsmss.h new file mode 100644 index 000000000000..8f377ac64142 --- /dev/null +++ b/phnt/include/ntsmss.h @@ -0,0 +1,22 @@ +#ifndef _NTSMSS_H +#define _NTSMSS_H + +NTSYSAPI +NTSTATUS +NTAPI +RtlConnectToSm( + _In_ PUNICODE_STRING ApiPortName, + _In_ HANDLE ApiPortHandle, + _In_ DWORD ProcessImageType, + _Out_ PHANDLE SmssConnection + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSendMsgToSm( + _In_ HANDLE ApiPortHandle, + _In_ PPORT_MESSAGE MessageData + ); + +#endif diff --git a/phnt/include/nttp.h b/phnt/include/nttp.h index 09edc590c3da..d0e86614950c 100644 --- a/phnt/include/nttp.h +++ b/phnt/include/nttp.h @@ -46,7 +46,7 @@ VOID NTAPI TpSetPoolMaxThreads( _Inout_ PTP_POOL Pool, - _In_ LONG MaxThreads + _In_ ULONG MaxThreads ); // private @@ -55,7 +55,7 @@ NTSTATUS NTAPI TpSetPoolMinThreads( _Inout_ PTP_POOL Pool, - _In_ LONG MinThreads + _In_ ULONG MinThreads ); #if (PHNT_VERSION >= PHNT_WIN7) @@ -123,7 +123,7 @@ NTAPI TpCallbackReleaseSemaphoreOnCompletion( _Inout_ PTP_CALLBACK_INSTANCE Instance, _In_ HANDLE Semaphore, - _In_ LONG ReleaseCount + _In_ ULONG ReleaseCount ); // winbase:ReleaseMutexWhenCallbackReturns @@ -244,10 +244,23 @@ NTAPI TpSetTimer( _Inout_ PTP_TIMER Timer, _In_opt_ PLARGE_INTEGER DueTime, - _In_ LONG Period, - _In_opt_ LONG WindowLength + _In_ ULONG Period, + _In_opt_ ULONG WindowLength ); +#if (PHNT_VERSION >= PHNT_WIN7) +// winbase:SetThreadpoolTimerEx +NTSYSAPI +NTSTATUS +NTAPI +TpSetTimerEx( + _Inout_ PTP_TIMER Timer, + _In_opt_ PLARGE_INTEGER DueTime, + _In_ ULONG Period, + _In_opt_ ULONG WindowLength + ); +#endif + // winbase:IsThreadpoolTimerSet NTSYSAPI LOGICAL @@ -295,6 +308,19 @@ TpSetWait( _In_opt_ PLARGE_INTEGER Timeout ); +#if (PHNT_VERSION >= PHNT_WIN7) +// winbase:SetThreadpoolWaitEx +NTSYSAPI +NTSTATUS +NTAPI +TpSetWaitEx( + _Inout_ PTP_WAIT Wait, + _In_opt_ HANDLE Handle, + _In_opt_ PLARGE_INTEGER Timeout, + _In_opt_ PVOID Reserved + ); +#endif + // winbase:WaitForThreadpoolWaitCallbacks NTSYSAPI VOID diff --git a/phnt/include/ntwow64.h b/phnt/include/ntwow64.h index 985c5f0a911e..c7b927adba3b 100644 --- a/phnt/include/ntwow64.h +++ b/phnt/include/ntwow64.h @@ -141,7 +141,8 @@ typedef struct _LDR_DATA_TABLE_ENTRY32 ULONG CorImage : 1; ULONG DontRelocate : 1; ULONG CorILOnly : 1; - ULONG ReservedFlags5 : 3; + ULONG ChpeImage : 1; + ULONG ReservedFlags5 : 2; ULONG Redirected : 1; ULONG ReservedFlags6 : 2; ULONG CompatDatabaseProcessed : 1; @@ -167,6 +168,7 @@ typedef struct _LDR_DATA_TABLE_ENTRY32 ULONG ImplicitPathOptions; ULONG ReferenceCount; ULONG DependentLoadFlags; + UCHAR SigningLevel; // since REDSTONE2 } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; typedef struct _CURDIR32 @@ -219,11 +221,16 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS32 UNICODE_STRING32 RuntimeData; RTL_DRIVE_LETTER_CURDIR32 CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; - ULONG EnvironmentSize; - ULONG EnvironmentVersion; + WOW64_POINTER(ULONG_PTR) EnvironmentSize; + WOW64_POINTER(ULONG_PTR) EnvironmentVersion; WOW64_POINTER(PVOID) PackageDependencyData; ULONG ProcessGroupId; ULONG LoaderThreads; + + UNICODE_STRING32 RedirectionDllName; // REDSTONE4 + UNICODE_STRING32 HeapPartitionName; // 19H1 + WOW64_POINTER(ULONG_PTR) DefaultThreadpoolCpuSetMasks; + ULONG DefaultThreadpoolCpuSetMaskCount; } RTL_USER_PROCESS_PARAMETERS32, *PRTL_USER_PROCESS_PARAMETERS32; typedef struct _PEB32 @@ -274,7 +281,7 @@ typedef struct _PEB32 WOW64_POINTER(PVOID) KernelCallbackTable; WOW64_POINTER(PVOID) UserSharedInfoPtr; }; - ULONG SystemReserved[1]; + ULONG SystemReserved; ULONG AtlThunkSListPtr32; WOW64_POINTER(PVOID) ApiSetMap; ULONG TlsExpansionCounter; @@ -337,11 +344,13 @@ typedef struct _PEB32 WOW64_POINTER(SIZE_T) MinimumStackCommit; - WOW64_POINTER(PVOID *) FlsCallback; - LIST_ENTRY32 FlsListHead; - WOW64_POINTER(PVOID) FlsBitmap; - ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; - ULONG FlsHighIndex; + WOW64_POINTER(PVOID) SparePointers[4]; + ULONG SpareUlongs[5]; + //WOW64_POINTER(PVOID *) FlsCallback; + //LIST_ENTRY32 FlsListHead; + //WOW64_POINTER(PVOID) FlsBitmap; + //ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; + //ULONG FlsHighIndex; WOW64_POINTER(PVOID) WerRegistrationData; WOW64_POINTER(PVOID) WerShipAssertPtr; @@ -362,6 +371,11 @@ typedef struct _PEB32 WOW64_POINTER(PVOID) TppWorkerpListLock; LIST_ENTRY32 TppWorkerpList; WOW64_POINTER(PVOID) WaitOnAddressHashTable[128]; + WOW64_POINTER(PVOID) TelemetryCoverageHeader; // REDSTONE3 + ULONG CloudFileFlags; + ULONG CloudFileDiagFlags; // REDSTONE4 + CHAR PlaceholderCompatibilityMode; + CHAR PlaceholderCompatibilityModeReserved[7]; } PEB32, *PPEB32; C_ASSERT(FIELD_OFFSET(PEB32, IFEOKey) == 0x024); @@ -369,7 +383,8 @@ C_ASSERT(FIELD_OFFSET(PEB32, UnicodeCaseTableData) == 0x060); C_ASSERT(FIELD_OFFSET(PEB32, SystemAssemblyStorageMap) == 0x204); C_ASSERT(FIELD_OFFSET(PEB32, pImageHeaderHash) == 0x23c); C_ASSERT(FIELD_OFFSET(PEB32, WaitOnAddressHashTable) == 0x25c); -C_ASSERT(sizeof(PEB32) == 0x460); +//C_ASSERT(sizeof(PEB32) == 0x460); // REDSTONE3 +C_ASSERT(sizeof(PEB32) == 0x470); #define GDI_BATCH_BUFFER_SIZE 310 diff --git a/phnt/include/ntxcapi.h b/phnt/include/ntxcapi.h index 29cfc73f68d9..59d3ec2cce1c 100644 --- a/phnt/include/ntxcapi.h +++ b/phnt/include/ntxcapi.h @@ -41,4 +41,24 @@ NtRaiseException( _In_ BOOLEAN FirstChance ); +__analysis_noreturn +NTSYSCALLAPI +VOID +NTAPI +RtlAssert( + _In_ PVOID VoidFailedAssertion, + _In_ PVOID VoidFileName, + _In_ ULONG LineNumber, + _In_opt_ PSTR MutableMessage + ); + +#define RTL_ASSERT(exp) \ + ((!(exp)) ? (RtlAssert((PVOID)#exp, (PVOID)__FILE__, __LINE__, NULL), FALSE) : TRUE) +#define RTL_ASSERTMSG(msg, exp) \ + ((!(exp)) ? (RtlAssert((PVOID)#exp, (PVOID)__FILE__, __LINE__, msg), FALSE) : TRUE) +#define RTL_SOFT_ASSERT(_exp) \ + ((!(_exp)) ? (DbgPrint("%s(%d): Soft assertion failed\n Expression: %s\n", __FILE__, __LINE__, #_exp), FALSE) : TRUE) +#define RTL_SOFT_ASSERTMSG(_msg, _exp) \ + ((!(_exp)) ? (DbgPrint("%s(%d): Soft assertion failed\n Expression: %s\n Message: %s\n", __FILE__, __LINE__, #_exp, (_msg)), FALSE) : TRUE) + #endif diff --git a/phnt/include/ntzwapi.h b/phnt/include/ntzwapi.h index d8c445aba8ae..48eb7f841507 100644 --- a/phnt/include/ntzwapi.h +++ b/phnt/include/ntzwapi.h @@ -1537,17 +1537,6 @@ ZwExtendSection( _Inout_ PLARGE_INTEGER NewSectionSize ); -NTSYSCALLAPI -NTSTATUS -NTAPI -ZwFilterBootOption( - _In_ FILTER_BOOT_OPTION_OPERATION FilterOperation, - _In_ ULONG ObjectType, - _In_ ULONG ElementType, - _In_reads_bytes_opt_(DataSize) PVOID Data, - _In_ ULONG DataSize - ); - NTSYSCALLAPI NTSTATUS NTAPI diff --git a/phnt/include/phnt.h b/phnt/include/phnt.h index 42bf2a9d8007..fa6ce2302c0e 100644 --- a/phnt/include/phnt.h +++ b/phnt/include/phnt.h @@ -35,6 +35,10 @@ #define PHNT_THRESHOLD2 101 #define PHNT_REDSTONE 102 #define PHNT_REDSTONE2 103 +#define PHNT_REDSTONE3 104 +#define PHNT_REDSTONE4 105 +#define PHNT_REDSTONE5 106 +#define PHNT_REDSTONE6 107 #ifndef PHNT_MODE #define PHNT_MODE PHNT_MODE_USER diff --git a/phnt/include/phnt_ntdef.h b/phnt/include/phnt_ntdef.h index 36cc781b4b97..b3ecedca9fc2 100644 --- a/phnt/include/phnt_ntdef.h +++ b/phnt/include/phnt_ntdef.h @@ -213,6 +213,8 @@ typedef const OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES; #define RTL_CONSTANT_OBJECT_ATTRIBUTES(n, a) { sizeof(OBJECT_ATTRIBUTES), NULL, n, a, NULL, NULL } #define RTL_INIT_OBJECT_ATTRIBUTES(n, a) RTL_CONSTANT_OBJECT_ATTRIBUTES(n, a) +#define OBJ_NAME_PATH_SEPARATOR ((WCHAR)L'\\') + // Portability typedef struct _OBJECT_ATTRIBUTES64 diff --git a/phnt/include/phnt_windows.h b/phnt/include/phnt_windows.h index fbce9252ca61..5d28bc57d79b 100644 --- a/phnt/include/phnt_windows.h +++ b/phnt/include/phnt_windows.h @@ -3,9 +3,28 @@ // This header file provides access to Win32, plus NTSTATUS values and some access mask values. +#ifndef CINTERFACE +#define CINTERFACE +#endif + +#ifndef COBJMACROS +#define COBJMACROS +#endif + +#ifndef INITGUID +#define INITGUID +#endif + +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif + +#ifndef WIN32_NO_STATUS #define WIN32_NO_STATUS +#endif + #include +#include #undef WIN32_NO_STATUS #include #include diff --git a/phnt/include/winsta.h b/phnt/include/winsta.h index 84d7e19e5063..f0cf99d60795 100644 --- a/phnt/include/winsta.h +++ b/phnt/include/winsta.h @@ -1,8 +1,6 @@ #ifndef _WINSTA_H #define _WINSTA_H -// begin_msdn:http://msdn.microsoft.com/en-us/library/cc248779%28PROT.10%29.aspx - // Access rights #define WINSTATION_QUERY 0x00000001 // WinStationQueryInformation @@ -28,6 +26,13 @@ WINSTATION_CONNECT | WINSTATION_DISCONNECT) #define WDPREFIX_LENGTH 12 +#define CALLBACK_LENGTH 50 +#define DLLNAME_LENGTH 32 +#define CDNAME_LENGTH 32 +#define WDNAME_LENGTH 32 +#define PDNAME_LENGTH 32 +#define DEVICENAME_LENGTH 128 +#define MODEMNAME_LENGTH DEVICENAME_LENGTH #define STACK_ADDRESS_LENGTH 128 #define MAX_BR_NAME 65 #define DIRECTORY_LENGTH 256 @@ -104,57 +109,298 @@ typedef struct _SESSIONIDW // private typedef enum _WINSTATIONINFOCLASS { - WinStationCreateData, - WinStationConfiguration, - WinStationPdParams, - WinStationWd, - WinStationPd, - WinStationPrinter, - WinStationClient, + WinStationCreateData, // WINSTATIONCREATE + WinStationConfiguration, // WINSTACONFIGWIRE + USERCONFIG + WinStationPdParams, // PDPARAMS + WinStationWd, // WDCONFIG + WinStationPd, // PDCONFIG2 + PDPARAMS + WinStationPrinter, // Not supported. + WinStationClient, // WINSTATIONCLIENT WinStationModules, - WinStationInformation, + WinStationInformation, // WINSTATIONINFORMATION WinStationTrace, WinStationBeep, WinStationEncryptionOff, WinStationEncryptionPerm, WinStationNtSecurity, - WinStationUserToken, + WinStationUserToken, // WINSTATIONUSERTOKEN WinStationUnused1, - WinStationVideoData, + WinStationVideoData, // WINSTATIONVIDEODATA WinStationInitialProgram, - WinStationCd, + WinStationCd, // CDCONFIG WinStationSystemTrace, WinStationVirtualData, - WinStationClientData, + WinStationClientData, // WINSTATIONCLIENTDATA WinStationSecureDesktopEnter, WinStationSecureDesktopExit, - WinStationLoadBalanceSessionTarget, - WinStationLoadIndicator, - WinStationShadowInfo, - WinStationDigProductId, - WinStationLockedState, - WinStationRemoteAddress, - WinStationIdleTime, - WinStationLastReconnectType, - WinStationDisallowAutoReconnect, + WinStationLoadBalanceSessionTarget, // ULONG + WinStationLoadIndicator, // WINSTATIONLOADINDICATORDATA + WinStationShadowInfo, // WINSTATIONSHADOW + WinStationDigProductId, // WINSTATIONPRODID + WinStationLockedState, // BOOL + WinStationRemoteAddress, // WINSTATIONREMOTEADDRESS + WinStationIdleTime, // ULONG + WinStationLastReconnectType, // ULONG + WinStationDisallowAutoReconnect, // BOOLEAN WinStationMprNotifyInfo, WinStationExecSrvSystemPipe, WinStationSmartCardAutoLogon, WinStationIsAdminLoggedOn, - WinStationReconnectedFromId, - WinStationEffectsPolicy, - WinStationType, - WinStationInformationEx, + WinStationReconnectedFromId, // ULONG + WinStationEffectsPolicy, // ULONG + WinStationType, // ULONG + WinStationInformationEx, // WINSTATIONINFORMATIONEX WinStationValidationInfo } WINSTATIONINFOCLASS; -// WinStationCreateData +// Retrieves general information on the type of terminal server session (protocol) to which the session belongs. typedef struct _WINSTATIONCREATE { ULONG fEnableWinStation : 1; ULONG MaxInstanceCount; } WINSTATIONCREATE, *PWINSTATIONCREATE; +typedef struct _WINSTACONFIGWIRE +{ + WCHAR Comment[61]; // The WinStation descriptive comment. + CHAR OEMId[4]; // Value identifying the OEM implementor of the TermService Listener to which this session (WinStation) belongs. This can be any value defined by the implementer (OEM) of the listener. + VARDATA_WIRE UserConfig; // VARDATA_WIRE structure defining the size and offset of the variable-length user configuration data succeeding it. + VARDATA_WIRE NewFields; // VARDATA_WIRE structure defining the size and offset of the variable-length new data succeeding it. This field is not used and is a placeholder for any new data, if and when added. +} WINSTACONFIGWIRE, *PWINSTACONFIGWIRE; + +typedef enum _CALLBACKCLASS +{ + Callback_Disable, + Callback_Roving, + Callback_Fixed +} CALLBACKCLASS; + +// The SHADOWCLASS enumeration is used to indicate the shadow-related settings for a session running on a terminal server. +typedef enum _SHADOWCLASS +{ + Shadow_Disable, // Shadowing is disabled. + Shadow_EnableInputNotify, // Permission is asked first from the session being shadowed. The shadower is also permitted keyboard and mouse input. + Shadow_EnableInputNoNotify, // Permission is not asked first from the session being shadowed. The shadower is also permitted keyboard and mouse input. + Shadow_EnableNoInputNotify, // Permission is asked first from the session being shadowed. The shadower is not permitted keyboard and mouse input and MUST observe the shadowed session. + Shadow_EnableNoInputNoNotify // Permission is not asked first from the session being shadowed. The shadower is not permitted keyboard and mouse input and MUST observe the shadowed session. +} SHADOWCLASS; + +// For a specific terminal server session, the USERCONFIG structure indicates the user and session configuration. +// https://msdn.microsoft.com/en-us/library/cc248610.aspx +typedef struct _USERCONFIG +{ + ULONG fInheritAutoLogon : 1; + ULONG fInheritResetBroken : 1; + ULONG fInheritReconnectSame : 1; + ULONG fInheritInitialProgram : 1; + ULONG fInheritCallback : 1; + ULONG fInheritCallbackNumber : 1; + ULONG fInheritShadow : 1; + ULONG fInheritMaxSessionTime : 1; + ULONG fInheritMaxDisconnectionTime : 1; + ULONG fInheritMaxIdleTime : 1; + ULONG fInheritAutoClient : 1; + ULONG fInheritSecurity : 1; + ULONG fPromptForPassword : 1; + ULONG fResetBroken : 1; + ULONG fReconnectSame : 1; + ULONG fLogonDisabled : 1; + ULONG fWallPaperDisabled : 1; + ULONG fAutoClientDrives : 1; + ULONG fAutoClientLpts : 1; + ULONG fForceClientLptDef : 1; + ULONG fRequireEncryption : 1; + ULONG fDisableEncryption : 1; + ULONG fUnused1 : 1; + ULONG fHomeDirectoryMapRoot : 1; + ULONG fUseDefaultGina : 1; + ULONG fCursorBlinkDisabled : 1; + ULONG fPublishedApp : 1; + ULONG fHideTitleBar : 1; + ULONG fMaximize : 1; + ULONG fDisableCpm : 1; + ULONG fDisableCdm : 1; + ULONG fDisableCcm : 1; + ULONG fDisableLPT : 1; + ULONG fDisableClip : 1; + ULONG fDisableExe : 1; + ULONG fDisableCam : 1; + ULONG fDisableAutoReconnect : 1; + ULONG ColorDepth : 3; + ULONG fInheritColorDepth : 1; + ULONG fErrorInvalidProfile : 1; + ULONG fPasswordIsScPin : 1; + ULONG fDisablePNPRedir : 1; + WCHAR UserName[USERNAME_LENGTH + 1]; + WCHAR Domain[DOMAIN_LENGTH + 1]; + WCHAR Password[PASSWORD_LENGTH + 1]; + WCHAR WorkDirectory[DIRECTORY_LENGTH + 1]; + WCHAR InitialProgram[INITIALPROGRAM_LENGTH + 1]; + WCHAR CallbackNumber[CALLBACK_LENGTH + 1]; + CALLBACKCLASS Callback; + SHADOWCLASS Shadow; + ULONG MaxConnectionTime; + ULONG MaxDisconnectionTime; + ULONG MaxIdleTime; + ULONG KeyboardLayout; + BYTE MinEncryptionLevel; + WCHAR NWLogonServer[NASIFILESERVER_LENGTH + 1]; + WCHAR PublishedName[MAX_BR_NAME]; + WCHAR WFProfilePath[DIRECTORY_LENGTH + 1]; + WCHAR WFHomeDir[DIRECTORY_LENGTH + 1]; + WCHAR WFHomeDirDrive[4]; +} USERCONFIG, *PUSERCONFIG; + +typedef enum _SDCLASS +{ + SdNone = 0, + SdConsole, + SdNetwork, + SdAsync, + SdOemTransport +} SDCLASS; + +typedef WCHAR DEVICENAME[DEVICENAME_LENGTH + 1]; +typedef WCHAR MODEMNAME[MODEMNAME_LENGTH + 1]; +typedef WCHAR NASISPECIFICNAME[NASISPECIFICNAME_LENGTH + 1]; +typedef WCHAR NASIUSERNAME[NASIUSERNAME_LENGTH + 1]; +typedef WCHAR NASIPASSWORD[NASIPASSWORD_LENGTH + 1]; +typedef WCHAR NASISESIONNAME[NASISESSIONNAME_LENGTH + 1]; +typedef WCHAR NASIFILESERVER[NASIFILESERVER_LENGTH + 1]; +typedef WCHAR WDNAME[WDNAME_LENGTH + 1]; +typedef WCHAR WDPREFIX[WDPREFIX_LENGTH + 1]; +typedef WCHAR CDNAME[CDNAME_LENGTH + 1]; +typedef WCHAR DLLNAME[DLLNAME_LENGTH + 1]; +typedef WCHAR PDNAME[PDNAME_LENGTH + 1]; + +typedef struct _NETWORKCONFIG +{ + LONG LanAdapter; + DEVICENAME NetworkName; + ULONG Flags; +} NETWORKCONFIG, *PNETWORKCONFIG; + +typedef enum _FLOWCONTROLCLASS +{ + FlowControl_None, + FlowControl_Hardware, + FlowControl_Software +} FLOWCONTROLCLASS; + +typedef enum _RECEIVEFLOWCONTROLCLASS +{ + ReceiveFlowControl_None, + ReceiveFlowControl_RTS, + ReceiveFlowControl_DTR, +} RECEIVEFLOWCONTROLCLASS; + +typedef enum _TRANSMITFLOWCONTROLCLASS +{ + TransmitFlowControl_None, + TransmitFlowControl_CTS, + TransmitFlowControl_DSR, +} TRANSMITFLOWCONTROLCLASS; + +typedef enum _ASYNCCONNECTCLASS +{ + Connect_CTS, + Connect_DSR, + Connect_RI, + Connect_DCD, + Connect_FirstChar, + Connect_Perm, +} ASYNCCONNECTCLASS; + +typedef struct _FLOWCONTROLCONFIG +{ + ULONG fEnableSoftwareTx : 1; + ULONG fEnableSoftwareRx : 1; + ULONG fEnableDTR : 1; + ULONG fEnableRTS : 1; + CHAR XonChar; + CHAR XoffChar; + FLOWCONTROLCLASS Type; + RECEIVEFLOWCONTROLCLASS HardwareReceive; + TRANSMITFLOWCONTROLCLASS HardwareTransmit; +} FLOWCONTROLCONFIG, *PFLOWCONTROLCONFIG; + +typedef struct _CONNECTCONFIG +{ + ASYNCCONNECTCLASS Type; + ULONG fEnableBreakDisconnect : 1; +} CONNECTCONFIG, *PCONNECTCONFIG; + +typedef struct _ASYNCCONFIG +{ + DEVICENAME DeviceName; + MODEMNAME ModemName; + ULONG BaudRate; + ULONG Parity; + ULONG StopBits; + ULONG ByteSize; + ULONG fEnableDsrSensitivity : 1; + ULONG fConnectionDriver : 1; + FLOWCONTROLCONFIG FlowControl; + CONNECTCONFIG Connect; +} ASYNCCONFIG, *PASYNCCONFIG; + +typedef struct _NASICONFIG +{ + NASISPECIFICNAME SpecificName; + NASIUSERNAME UserName; + NASIPASSWORD PassWord; + NASISESIONNAME SessionName; + NASIFILESERVER FileServer; + BOOLEAN GlobalSession; +} NASICONFIG, *PNASICONFIG; + +typedef struct _OEMTDCONFIG +{ + LONG Adapter; + DEVICENAME DeviceName; + ULONG Flags; +} OEMTDCONFIG, *POEMTDCONFIG; + +// Retrieves transport protocol driver parameters. +typedef struct _PDPARAMS +{ + SDCLASS SdClass; // Stack driver class. Indicates which one of the union's structures is valid. + union + { + NETWORKCONFIG Network; // Configuration of network drivers. Used if SdClass is SdNetwork. + ASYNCCONFIG Async; // Configuration of async (modem) driver. Used if SdClass is SdAsync. + NASICONFIG Nasi; // Reserved. + OEMTDCONFIG OemTd; // Configuration of OEM transport driver. Used if SdClass is SdOemTransport. + }; +} PDPARAMS, *PPDPARAMS; + +// The WinStation (session) driver configuration. +typedef struct _WDCONFIG +{ + WDNAME WdName; // The descriptive name of the WinStation driver. + DLLNAME WdDLL; // The driver's image name. + DLLNAME WsxDLL; // Used by the Terminal Services service to communicate with the WinStation driver. + ULONG WdFlag; // Driver flags. + ULONG WdInputBufferLength; // Length, in bytes, of the input buffer used by the driver. Defaults to 2048. + DLLNAME CfgDLL; // Configuration DLL used by Terminal Services administrative tools for configuring the driver. + WDPREFIX WdPrefix; // Used as the prefix of the WinStation name generated for the connected sessions with this WinStation driver. +} WDCONFIG, *PWDCONFIG; + +// The protocol driver's software configuration. +typedef struct _PDCONFIG2 +{ + PDNAME PdName; + SDCLASS SdClass; + DLLNAME PdDLL; + ULONG PdFlag; + ULONG OutBufLength; + ULONG OutBufCount; + ULONG OutBufDelay; + ULONG InteractiveDelay; + ULONG PortNumber; + ULONG KeepAliveTimeout; +} PDCONFIG2, *PPDCONFIG2; + // WinStationClient typedef struct _WINSTATIONCLIENT { @@ -275,7 +521,7 @@ typedef struct _PROTOCOLSTATUS ULONG AsyncSignalMask; } PROTOCOLSTATUS, *PPROTOCOLSTATUS; -// WinStationInformation +// Retrieves information on the session. typedef struct _WINSTATIONINFORMATION { WINSTATIONSTATECLASS ConnectState; @@ -291,7 +537,7 @@ typedef struct _WINSTATIONINFORMATION LARGE_INTEGER CurrentTime; } WINSTATIONINFORMATION, *PWINSTATIONINFORMATION; -// WinStationUserToken +// Retrieves the user's token in the session. Caller requires WINSTATION_ALL_ACCESS permission. typedef struct _WINSTATIONUSERTOKEN { HANDLE ProcessId; @@ -299,7 +545,7 @@ typedef struct _WINSTATIONUSERTOKEN HANDLE UserToken; } WINSTATIONUSERTOKEN, *PWINSTATIONUSERTOKEN; -// WinStationVideoData +// Retrieves resolution and color depth of the session. typedef struct _WINSTATIONVIDEODATA { USHORT HResolution; @@ -307,7 +553,73 @@ typedef struct _WINSTATIONVIDEODATA USHORT fColorDepth; } WINSTATIONVIDEODATA, *PWINSTATIONVIDEODATA; -// WinStationDigProductId +typedef enum _CDCLASS +{ + CdNone, // No connection driver. + CdModem, // Connection driver is a modem. + CdClass_Maximum, +} CDCLASS; + +// Connection driver configuration. It is used for connecting via modem to a server. +typedef struct _CDCONFIG +{ + CDCLASS CdClass; // Connection driver type. + CDNAME CdName; // Connection driver descriptive name. + DLLNAME CdDLL; // Connection driver image name. + ULONG CdFlag; // Connection driver flags. Connection driver specific. +} CDCONFIG, *PCDCONFIG; + +// The name has the following form: +// name syntax : xxxyyyy +typedef CHAR CLIENTDATANAME[CLIENTDATANAME_LENGTH + 1]; +typedef CHAR* PCLIENTDATANAME; + +typedef struct _WINSTATIONCLIENTDATA +{ + CLIENTDATANAME DataName; // Identifies the type of data sent in this WINSTATIONCLIENTDATA structure. The definition is dependent on the caller and on the client receiving it. This MUST be a data name following a format similar to that of the CLIENTDATANAME data type. + BOOLEAN fUnicodeData; // TRUE indicates data is in Unicode format; FALSE otherwise. +} WINSTATIONCLIENTDATA, *PWINSTATIONCLIENTDATA; + +typedef enum _LOADFACTORTYPE +{ + ErrorConstraint, // An error occurred while obtaining constraint data. + PagedPoolConstraint, // The amount of paged pool is the constraint. + NonPagedPoolConstraint, // The amount of non-paged pool is the constraint. + AvailablePagesConstraint, // The amount of available pages is the constraint. + SystemPtesConstraint, // The number of system page table entries (PTEs) is the constraint. + CPUConstraint // CPU usage is the constraint. +} LOADFACTORTYPE; + +// The WINSTATIONLOADINDICATORDATA structure defines data used for the load balancing of a server. +typedef struct _WINSTATIONLOADINDICATORDATA +{ + ULONG RemainingSessionCapacity; // The estimated number of additional sessions that can be supported given the CPU constraint. + LOADFACTORTYPE LoadFactor; // Indicates the most constrained current resource. + ULONG TotalSessions; // The total number of sessions. + ULONG DisconnectedSessions; // The number of disconnected sessions. + LARGE_INTEGER IdleCPU; // This is always set to 0. + LARGE_INTEGER TotalCPU; // This is always set to 0. + ULONG RawSessionCapacity; // The raw number of sessions capacity. + ULONG reserved[9]; // Reserved. +} WINSTATIONLOADINDICATORDATA, *PWINSTATIONLOADINDICATORDATA; + +typedef enum _SHADOWSTATECLASS +{ + State_NoShadow, // No shadow operations are currently being performed on this session. + State_Shadowing, // The session is shadowing a different session. The current session is referred to as a shadow client. + State_Shadowed // The session is being shadowed by a different session. The current session is referred to as a shadow target. +} SHADOWSTATECLASS; + +// Retrieves the current shadow state of a session. +typedef struct _WINSTATIONSHADOW +{ + SHADOWSTATECLASS ShadowState; // Specifies the current state of shadowing. + SHADOWCLASS ShadowClass; // Specifies the type of shadowing. + ULONG SessionId; // Specifies the session ID of the session. + ULONG ProtocolType; // Specifies the type of protocol on the session. Can be one of the following values. +} WINSTATIONSHADOW, *PWINSTATIONSHADOW; + +// Retrieves the client product ID and current product ID of the session. typedef struct _WINSTATIONPRODID { WCHAR DigProductId[CLIENT_PRODUCT_ID_LENGTH]; @@ -318,7 +630,7 @@ typedef struct _WINSTATIONPRODID ULONG OuterMostSessionId; } WINSTATIONPRODID, *PWINSTATIONPRODID; -// WinStationRemoteAddress +// Retrieves the remote IP address of the terminal server client in the session. typedef struct _WINSTATIONREMOTEADDRESS { USHORT sin_family; @@ -491,7 +803,7 @@ typedef struct _TS_COUNTER // current session ID. #define LOGONID_CURRENT (-1) -#define SERVERNAME_CURRENT (NULL) +#define SERVERNAME_CURRENT ((PWSTR)NULL) // rev BOOLEAN @@ -511,21 +823,21 @@ WinStationOpenServerW( BOOLEAN WINAPI WinStationCloseServer( - _In_ HANDLE hServer + _In_ HANDLE ServerHandle ); // rev BOOLEAN WINAPI WinStationServerPing( - _In_opt_ HANDLE hServer + _In_opt_ HANDLE ServerHandle ); // rev BOOLEAN WINAPI WinStationGetTermSrvCountersValue( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG Count, _Inout_ PTS_COUNTER Counters // set counter IDs before calling ); @@ -533,7 +845,7 @@ WinStationGetTermSrvCountersValue( BOOLEAN WINAPI WinStationShutdownSystem( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG ShutdownFlags // WSD_* ); @@ -541,7 +853,7 @@ WinStationShutdownSystem( BOOLEAN WINAPI WinStationWaitSystemEvent( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG EventMask, // WEVENT_* _Out_ PULONG EventFlags ); @@ -550,7 +862,7 @@ WinStationWaitSystemEvent( BOOLEAN WINAPI WinStationRegisterConsoleNotification( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ HWND WindowHandle, _In_ ULONG Flags ); @@ -559,7 +871,7 @@ WinStationRegisterConsoleNotification( BOOLEAN WINAPI WinStationUnRegisterConsoleNotification( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ HWND WindowHandle ); @@ -569,7 +881,7 @@ WinStationUnRegisterConsoleNotification( BOOLEAN WINAPI WinStationEnumerateW( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _Out_ PSESSIONIDW *SessionIds, _Out_ PULONG Count ); @@ -577,7 +889,7 @@ WinStationEnumerateW( BOOLEAN WINAPI WinStationQueryInformationW( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ WINSTATIONINFOCLASS WinStationInformationClass, _Out_writes_bytes_(WinStationInformationLength) PVOID pWinStationInformation, @@ -589,7 +901,7 @@ WinStationQueryInformationW( BOOLEAN WINAPI WinStationSetInformationW( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ WINSTATIONINFOCLASS WinStationInformationClass, _In_reads_bytes_(WinStationInformationLength) PVOID pWinStationInformation, @@ -599,7 +911,7 @@ WinStationSetInformationW( BOOLEAN WINAPI WinStationNameFromLogonIdW( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _Out_writes_(WINSTATIONNAME_LENGTH + 1) PWSTR pWinStationName ); @@ -608,7 +920,7 @@ WinStationNameFromLogonIdW( BOOLEAN WINAPI WinStationSendMessageW( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ PWSTR Title, _In_ ULONG TitleLength, @@ -623,7 +935,7 @@ WinStationSendMessageW( BOOLEAN WINAPI WinStationConnectW( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ ULONG TargetSessionId, _In_opt_ PWSTR pPassword, @@ -633,7 +945,7 @@ WinStationConnectW( BOOLEAN WINAPI WinStationDisconnect( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ BOOLEAN bWait ); @@ -642,7 +954,7 @@ WinStationDisconnect( BOOLEAN WINAPI WinStationReset( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ BOOLEAN bWait ); @@ -651,7 +963,7 @@ WinStationReset( BOOLEAN WINAPI WinStationShadow( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ PWSTR TargetServerName, _In_ ULONG TargetSessionId, _In_ UCHAR HotKeyVk, @@ -662,7 +974,7 @@ WinStationShadow( BOOLEAN WINAPI WinStationShadowStop( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG SessionId, _In_ BOOLEAN bWait // ignored ); @@ -673,7 +985,7 @@ WinStationShadowStop( BOOLEAN WINAPI WinStationEnumerateProcesses( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _Out_ PVOID *Processes ); @@ -681,7 +993,7 @@ WinStationEnumerateProcesses( BOOLEAN WINAPI WinStationGetAllProcesses( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG Level, _Out_ PULONG NumberOfProcesses, _Out_ PTS_ALL_PROCESSES_INFO *Processes @@ -700,7 +1012,7 @@ WinStationFreeGAPMemory( BOOLEAN WINAPI WinStationTerminateProcess( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG ProcessId, _In_ ULONG ExitCode ); @@ -708,7 +1020,7 @@ WinStationTerminateProcess( BOOLEAN WINAPI WinStationGetProcessSid( - _In_opt_ HANDLE hServer, + _In_opt_ HANDLE ServerHandle, _In_ ULONG ProcessId, _In_ FILETIME ProcessStartTime, _Out_ PVOID pProcessUserSid, @@ -743,6 +1055,4 @@ _WinStationWaitForConnect( VOID ); -// end_msdn - #endif diff --git a/plugins/CommonUtil/CHANGELOG.txt b/plugins/CommonUtil/CHANGELOG.txt deleted file mode 100644 index 9ccf3bfe5d94..000000000000 --- a/plugins/CommonUtil/CHANGELOG.txt +++ /dev/null @@ -1,3 +0,0 @@ - -1.0 - * Initial release \ No newline at end of file diff --git a/plugins/CommonUtil/CommonUtil.vcxitems b/plugins/CommonUtil/CommonUtil.vcxitems new file mode 100644 index 000000000000..9ff58474fb3e --- /dev/null +++ b/plugins/CommonUtil/CommonUtil.vcxitems @@ -0,0 +1,23 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {bfaabf4a-4b1b-4b28-b6cb-b55ab6c1109f} + CommonUtil + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/CommonUtil/CommonUtil.vcxproj b/plugins/CommonUtil/CommonUtil.vcxproj deleted file mode 100644 index 2f1bb83743f6..000000000000 --- a/plugins/CommonUtil/CommonUtil.vcxproj +++ /dev/null @@ -1,129 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE} - CommonUtil - Win32Proj - CommonUtil - 10.0.15063.0 - - - - DynamicLibrary - Unicode - true - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - true - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - windowscodecs.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/CommonUtil/CommonUtil.vcxproj.filters b/plugins/CommonUtil/CommonUtil.vcxproj.filters deleted file mode 100644 index 4cff34c3c810..000000000000 --- a/plugins/CommonUtil/CommonUtil.vcxproj.filters +++ /dev/null @@ -1,152 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {664acb18-b541-4abb-8e7f-668c4de1cdfe} - - - {8483492d-4b7d-408b-95ac-d47900bfb245} - - - {31d3c520-6bf5-4720-8800-1f420d2e41e8} - - - {3ee330a7-42a6-4544-8739-f7066d4e4d82} - - - {42e6e53a-30a0-4c7f-8c1a-b98aac45b62d} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files\json-c - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - Header Files\json-c - - - - - Resource Files\Images - - - Resource Files\Images - - - Resource Files\Images - - - Resource Files\Images - - - - - Resource Files - - - \ No newline at end of file diff --git a/plugins/CommonUtil/http.c b/plugins/CommonUtil/http.c index c6034aed6a8e..c4af0524103a 100644 --- a/plugins/CommonUtil/http.c +++ b/plugins/CommonUtil/http.c @@ -1,8 +1,8 @@ /* - * Process Hacker Plugins - - * CommonUtil Plugin + * Process Hacker - + * http socket wrapper * - * Copyright (C) 2016 dmex + * Copyright (C) 2017 dmex * * This file is part of Process Hacker. * @@ -18,19 +18,354 @@ * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . - * */ -#include "main.h" -#include -#include +// dmex: This wrapper is utilizing a shared project until stable enough for inclusion with the base program. + +#include +#include +#include #include -PPH_BYTES UtilReadSocketString( - _In_ SOCKET Handle +static const PH_FLAG_MAPPING PhpHttpRequestFlagMappings[] = +{ + { PH_HTTP_FLAG_SECURE, WINHTTP_FLAG_SECURE }, + { PH_HTTP_FLAG_REFRESH, WINHTTP_FLAG_REFRESH } +}; + +static const PH_FLAG_MAPPING PhpHttpHeaderQueryMappings[] = +{ + { PH_HTTP_QUERY_CONTENT_LENGTH, WINHTTP_QUERY_CONTENT_LENGTH }, + { PH_HTTP_QUERY_STATUS_CODE, WINHTTP_QUERY_STATUS_CODE }, +}; + +static const PH_FLAG_MAPPING PhpHttpFeatureMappings[] = +{ + { PH_HTTP_FEATURE_REDIRECTS, WINHTTP_DISABLE_REDIRECTS }, + { PH_HTTP_FEATURE_KEEP_ALIVE, WINHTTP_DISABLE_KEEP_ALIVE }, +}; + +BOOLEAN PhHttpSocketCreate( + _Out_ PPH_HTTP_CONTEXT *HttpContext, + _In_opt_ PWSTR HttpUserAgent + ) +{ + PPH_HTTP_CONTEXT httpContext; + + httpContext = PhAllocate(sizeof(PH_HTTP_CONTEXT)); + memset(httpContext, 0, sizeof(PH_HTTP_CONTEXT)); + + httpContext->SessionHandle = WinHttpOpen( + HttpUserAgent, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0 + ); + + if (!httpContext->SessionHandle) + { + PhFree(httpContext); + return FALSE; + } + + if (WindowsVersion >= WINDOWS_8_1) + { + WinHttpSetOption( + httpContext->SessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG){ WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); + } + + *HttpContext = httpContext; + + return TRUE; +} + +VOID PhHttpSocketDestroy( + _Frees_ptr_opt_ PPH_HTTP_CONTEXT HttpContext + ) +{ + if (HttpContext->RequestHandle) + WinHttpCloseHandle(HttpContext->RequestHandle); + + if (HttpContext->ConnectionHandle) + WinHttpCloseHandle(HttpContext->ConnectionHandle); + + if (HttpContext->SessionHandle) + WinHttpCloseHandle(HttpContext->SessionHandle); + + PhFree(HttpContext); +} + +BOOLEAN PhHttpSocketConnect( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PWSTR ServerName, + _In_ USHORT ServerPort + ) +{ + HttpContext->ConnectionHandle = WinHttpConnect( + HttpContext->SessionHandle, + ServerName, + ServerPort, + 0 + ); + + if (HttpContext->ConnectionHandle) + return TRUE; + else + return FALSE; +} + +BOOLEAN PhHttpSocketBeginRequest( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_opt_ PWSTR Method, + _In_ PWSTR UrlPath, + _In_ ULONG Flags + ) +{ + ULONG httpFlags = 0; + + PhMapFlags1( + &httpFlags, + Flags, + PhpHttpRequestFlagMappings, + RTL_NUMBER_OF(PhpHttpRequestFlagMappings) + ); + + HttpContext->RequestHandle = WinHttpOpenRequest( + HttpContext->ConnectionHandle, + Method, + UrlPath, + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + httpFlags + ); + + if (!HttpContext->RequestHandle) + return FALSE; + + WinHttpSetOption( + HttpContext->RequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG){ WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); + + return TRUE; +} + +BOOLEAN PhHttpSocketSendRequest( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_opt_ PVOID RequestData, + _In_opt_ ULONG RequestDataLength + ) +{ + return !!WinHttpSendRequest( + HttpContext->RequestHandle, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + RequestData, + RequestDataLength, + RequestDataLength, + 0 + ); +} + +BOOLEAN PhHttpSocketEndRequest( + _In_ PPH_HTTP_CONTEXT HttpContext ) { - PSTR data; + return !!WinHttpReceiveResponse(HttpContext->RequestHandle, NULL); +} + +BOOLEAN PhHttpSocketReadData( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PVOID Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG BytesCopied + ) +{ + return !!WinHttpReadData(HttpContext->RequestHandle, Buffer, BufferLength, BytesCopied); +} + +BOOLEAN PhHttpSocketWriteData( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PVOID Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG BytesCopied + ) +{ + return !!WinHttpWriteData(HttpContext->RequestHandle, Buffer, BufferLength, BytesCopied); +} + +BOOLEAN PhHttpSocketAddRequestHeaders( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PWSTR Headers, + _In_opt_ ULONG HeadersLength + ) +{ + return !!WinHttpAddRequestHeaders( + HttpContext->RequestHandle, + Headers, + HeadersLength ? HeadersLength : ULONG_MAX, + WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE + ); +} + +_Check_return_ +_Ret_maybenull_ +PPH_STRING PhHttpSocketQueryHeaderString( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PWSTR HeaderString + ) +{ + ULONG bufferLength = 0; + PPH_STRING stringBuffer; + + if (!WinHttpQueryHeaders( + HttpContext->RequestHandle, + WINHTTP_QUERY_CUSTOM, + HeaderString, + WINHTTP_NO_OUTPUT_BUFFER, + &bufferLength, + WINHTTP_NO_HEADER_INDEX + )) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return NULL; + } + + stringBuffer = PhCreateStringEx(NULL, bufferLength); + + if (!WinHttpQueryHeaders( + HttpContext->RequestHandle, + WINHTTP_QUERY_CUSTOM, + HeaderString, + stringBuffer->Buffer, + &bufferLength, + WINHTTP_NO_HEADER_INDEX + )) + { + PhDereferenceObject(stringBuffer); + return NULL; + } + + return stringBuffer; +} + +BOOLEAN PhHttpSocketQueryHeaderUlong( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ ULONG QueryValue, + _Out_ PULONG HeaderValue + ) +{ + ULONG queryFlags = 0; + ULONG headerValue = 0; + ULONG valueLength = sizeof(ULONG); + + PhMapFlags1( + &queryFlags, + QueryValue, + PhpHttpHeaderQueryMappings, + RTL_NUMBER_OF(PhpHttpHeaderQueryMappings) + ); + + if (WinHttpQueryHeaders( + HttpContext->RequestHandle, + queryFlags | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + &headerValue, + &valueLength, + WINHTTP_NO_HEADER_INDEX + )) + { + *HeaderValue = headerValue; + return TRUE; + } + + return FALSE; +} + +PVOID PhHttpSocketQueryOption( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ BOOLEAN SessionOption, + _In_ ULONG QueryOption + ) +{ + HINTERNET queryHandle; + ULONG bufferLength = 0; + PVOID optionBuffer; + + if (SessionOption) + queryHandle = HttpContext->SessionHandle; + else + queryHandle = HttpContext->RequestHandle; + + if (!WinHttpQueryOption( + queryHandle, + QueryOption, + NULL, + &bufferLength + )) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return NULL; + } + + optionBuffer = PhAllocate(bufferLength); + memset(optionBuffer, 0, bufferLength); + + if (!WinHttpQueryOption( + queryHandle, + QueryOption, + optionBuffer, + &bufferLength + )) + { + PhFree(optionBuffer); + return NULL; + } + + return optionBuffer; +} + +_Check_return_ +_Ret_maybenull_ +PPH_STRING PhHttpSocketQueryOptionString( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ BOOLEAN SessionOption, + _In_ ULONG QueryOption + ) +{ + PVOID optionBuffer; + PPH_STRING stringBuffer = NULL; + + optionBuffer = PhHttpSocketQueryOption( + HttpContext, + SessionOption, + QueryOption + ); + + if (optionBuffer) + { + stringBuffer = PhCreateString(optionBuffer); // FIXME + PhFree(optionBuffer); + } + + return stringBuffer; +} + +PVOID PhHttpSocketDownloadString( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ BOOLEAN Unicode + ) +{ + PSTR data = NULL; + PVOID result = NULL; ULONG allocatedLength; ULONG dataLength; ULONG returnLength; @@ -40,10 +375,7 @@ PPH_BYTES UtilReadSocketString( data = (PSTR)PhAllocate(allocatedLength); dataLength = 0; - // Zero the buffer - memset(buffer, 0, PAGE_SIZE); - - while ((returnLength = recv(Handle, buffer, PAGE_SIZE, 0)) != SOCKET_ERROR) + while (WinHttpReadData(HttpContext->RequestHandle, buffer, PAGE_SIZE, &returnLength)) { if (returnLength == 0) break; @@ -54,10 +386,7 @@ PPH_BYTES UtilReadSocketString( data = (PSTR)PhReAllocate(data, allocatedLength); } - // Copy the returned buffer into our pointer memcpy(data + dataLength, buffer, returnLength); - // Zero the returned buffer for the next loop - //memset(buffer, 0, returnLength); dataLength += returnLength; } @@ -68,56 +397,116 @@ PPH_BYTES UtilReadSocketString( data = (PSTR)PhReAllocate(data, allocatedLength); } - // Ensure that the buffer is null-terminated + // Ensure that the buffer is null-terminated. data[dataLength] = 0; - return PhCreateBytesEx(data, dataLength); + if (Unicode) + result = PhConvertUtf8ToUtf16Ex(data, dataLength); + else + result = PhCreateBytesEx(data, dataLength); + + PhFree(data); + + return result; } -PPH_BYTES UtilReadWinHttpString( - _In_ HINTERNET Handle +BOOLEAN PhHttpSocketSetFeature( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ ULONG Feature, + _In_ BOOLEAN Enable ) { - BYTE buffer[PAGE_SIZE]; - PSTR data; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; + ULONG featureValue = 0; - allocatedLength = sizeof(buffer); - data = (PSTR)PhAllocate(allocatedLength); - dataLength = 0; + PhMapFlags1( + &featureValue, + Feature, + PhpHttpFeatureMappings, + RTL_NUMBER_OF(PhpHttpFeatureMappings) + ); + + return !!WinHttpSetOption( + HttpContext->RequestHandle, + Enable ? WINHTTP_OPTION_ENABLE_FEATURE : WINHTTP_OPTION_DISABLE_FEATURE, + &featureValue, + sizeof(ULONG) + ); +} + + +BOOLEAN PhHttpSocketParseUrl( + _In_ PPH_STRING Url, + _Out_opt_ PPH_STRING *HostPart, + _Out_opt_ PPH_STRING *PathPart, + _Out_opt_ PUSHORT PortPart + ) +{ + URL_COMPONENTS httpParts; - // Zero the buffer - memset(buffer, 0, PAGE_SIZE); + memset(&httpParts, 0, sizeof(URL_COMPONENTS)); + httpParts.dwStructSize = sizeof(URL_COMPONENTS); + httpParts.dwHostNameLength = ULONG_MAX; + httpParts.dwUrlPathLength = ULONG_MAX; - while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength)) + if (!WinHttpCrackUrl( + Url->Buffer, + (ULONG)Url->Length / sizeof(WCHAR), + 0, + &httpParts + )) { - if (returnLength == 0) - break; + return FALSE; + } - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - data = (PSTR)PhReAllocate(data, allocatedLength); - } + if (HostPart && httpParts.dwHostNameLength) + *HostPart = PhCreateStringEx(httpParts.lpszHostName, httpParts.dwHostNameLength * sizeof(WCHAR)); - // Copy the returned buffer into our pointer - memcpy(data + dataLength, buffer, returnLength); - // Zero the returned buffer for the next loop - //memset(buffer, 0, returnLength); + if (PathPart && httpParts.dwUrlPathLength) + *PathPart = PhCreateStringEx(httpParts.lpszUrlPath, httpParts.dwUrlPathLength * sizeof(WCHAR)); - dataLength += returnLength; + if (PortPart) + *PortPart = httpParts.nPort; + + return TRUE; +} + +_Check_return_ +_Ret_maybenull_ +PPH_STRING PhHttpSocketGetErrorMessage( + _In_ ULONG ErrorCode + ) +{ + PPH_STRING message = NULL; + + if (message = PhGetMessage(PhGetDllHandle(L"winhttp.dll"), 0xb, GetUserDefaultLangID(), ErrorCode)) + { + PhTrimToNullTerminatorString(message); } - if (allocatedLength < dataLength + 1) + // Remove any trailing newline + if (message && message->Length >= 2 * sizeof(WCHAR) && + message->Buffer[message->Length / sizeof(WCHAR) - 2] == '\r' && + message->Buffer[message->Length / sizeof(WCHAR) - 1] == '\n') { - allocatedLength++; - data = (PSTR)PhReAllocate(data, allocatedLength); + PhMoveReference(&message, PhCreateStringEx(message->Buffer, message->Length - 2 * sizeof(WCHAR))); } - // Ensure that the buffer is null-terminated - data[dataLength] = 0; + return message; +} + +BOOLEAN PhHttpSocketSetCredentials( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PCWSTR Name, + _In_ PCWSTR Value + ) +{ + return WinHttpSetCredentials( + HttpContext->RequestHandle, + WINHTTP_AUTH_TARGET_SERVER, + WINHTTP_AUTH_SCHEME_BASIC, + Name, + Value, + NULL + ); +} - return PhCreateBytesEx(data, dataLength); -} \ No newline at end of file diff --git a/plugins/CommonUtil/http.h b/plugins/CommonUtil/http.h new file mode 100644 index 000000000000..7bc875bc1e06 --- /dev/null +++ b/plugins/CommonUtil/http.h @@ -0,0 +1,177 @@ +#ifndef _PH_NETIO_H +#define _PH_NETIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _PH_HTTP_CONTEXT +{ + PVOID SessionHandle; + PVOID ConnectionHandle; + PVOID RequestHandle; +} PH_HTTP_CONTEXT, *PPH_HTTP_CONTEXT; + +BOOLEAN +NTAPI +PhHttpSocketCreate( + _Out_ PPH_HTTP_CONTEXT *HttpContext, + _In_opt_ PWSTR HttpUserAgent + ); + +VOID +NTAPI +PhHttpSocketDestroy( + _Frees_ptr_opt_ PPH_HTTP_CONTEXT HttpContext + ); + +#define PH_HTTP_DEFAULT_PORT 0 // use the protocol-specific default +#define PH_HTTP_DEFAULT_HTTP_PORT 80 +#define PH_HTTP_DEFAULT_HTTPS_PORT 443 + +BOOLEAN +NTAPI +PhHttpSocketConnect( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PWSTR ServerName, + _In_ USHORT ServerPort + ); + +#define PH_HTTP_FLAG_SECURE 0x1 +#define PH_HTTP_FLAG_REFRESH 0x2 + +BOOLEAN +NTAPI +PhHttpSocketBeginRequest( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_opt_ PWSTR Method, + _In_ PWSTR UrlPath, + _In_ ULONG Flags + ); + +BOOLEAN +NTAPI +PhHttpSocketSendRequest( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_opt_ PVOID RequestData, + _In_opt_ ULONG RequestDataLength + ); + +BOOLEAN +NTAPI +PhHttpSocketEndRequest( + _In_ PPH_HTTP_CONTEXT HttpContext + ); + +BOOLEAN +NTAPI +PhHttpSocketReadData( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PVOID Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG BytesCopied + ); + +BOOLEAN +NTAPI +PhHttpSocketWriteData( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PVOID Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG BytesCopied + ); + +BOOLEAN +NTAPI +PhHttpSocketAddRequestHeaders( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PWSTR Headers, + _In_opt_ ULONG HeadersLength + ); + +_Check_return_ +_Ret_maybenull_ +PPH_STRING +NTAPI +PhHttpSocketQueryHeaderString( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PWSTR HeaderString + ); + +// status codes +#define PH_HTTP_STATUS_OK 200 // request completed +#define PH_HTTP_STATUS_CREATED 201 +#define PH_HTTP_STATUS_REDIRECT_METHOD 303 // redirection w/ new access method +#define PH_HTTP_STATUS_REDIRECT 302 // object temporarily moved + +// header query flags +#define PH_HTTP_QUERY_CONTENT_LENGTH 0x1 +#define PH_HTTP_QUERY_STATUS_CODE 0x2 + +BOOLEAN +NTAPI +PhHttpSocketQueryHeaderUlong( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ ULONG QueryValue, + _Out_ PULONG HeaderValue + ); + +_Check_return_ +_Ret_maybenull_ +PPH_STRING +NTAPI +PhHttpSocketQueryOptionString( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ BOOLEAN SessionOption, + _In_ ULONG QueryOption + ); + +PVOID +NTAPI +PhHttpSocketDownloadString( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ BOOLEAN Unicode + ); + +#define PH_HTTP_FEATURE_REDIRECTS 0x1 +#define PH_HTTP_FEATURE_KEEP_ALIVE 0x2 + +BOOLEAN +NTAPI +PhHttpSocketSetFeature( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ ULONG Feature, + _In_ BOOLEAN Enable + ); + +BOOLEAN +NTAPI +PhHttpSocketParseUrl( + _In_ PPH_STRING Url, + _Out_opt_ PPH_STRING *Host, + _Out_opt_ PPH_STRING *Path, + _Out_opt_ PUSHORT Port + ); + +_Check_return_ +_Ret_maybenull_ +PPH_STRING +NTAPI +PhHttpSocketGetErrorMessage( + _In_ ULONG ErrorCode + ); + +_Check_return_ +BOOLEAN +NTAPI +PhHttpSocketSetCredentials( + _In_ PPH_HTTP_CONTEXT HttpContext, + _In_ PCWSTR Name, + _In_ PCWSTR Value + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/CommonUtil/json-c/config.h b/plugins/CommonUtil/json-c/config.h deleted file mode 100644 index df46f0a481a7..000000000000 --- a/plugins/CommonUtil/json-c/config.h +++ /dev/null @@ -1,89 +0,0 @@ -#define PACKAGE_STRING "JSON C Library 0.12-20140410" -#define PACKAGE_BUGREPORT "json-c@googlegroups.com" -#define PACKAGE_NAME "JSON C Library" -#define PACKAGE_TARNAME "json-c" -#define PACKAGE_VERSION "0.12-20140410" - - -#define HAVE_SETLOCALE 1 -#define HAVE_LOCALE_H 1 - -#define HAVE_DECL_NAN 1 -#define HAVE_DECL_INFINITY 1 -//#define HAVE_DECL__ISNAN 1 -//#define HAVE_DECL__FINITE 1 - -/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -/* #undef HAVE_DOPRNT */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_LIMITS_H 1 - -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ -#define HAVE_MALLOC 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the `open' function. */ -#define HAVE_OPEN 1 - -/* Define to 1 if your system has a GNU libc compatible `realloc' function, - and to 0 otherwise. */ -#define HAVE_REALLOC 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strdup' function. */ -#define HAVE_STRNDUP 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDARG_H 1 - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYSLOG_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_PARAM_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `vprintf' function. */ -#undef HAVE_VPRINTF - -/* Define to 1 if you have the `vsyslog' function. */ -#undef HAVE_VSYSLOG - -/* Define to 1 if you have the `strncasecmp' function. */ -#undef HAVE_STRNCASECMP - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - diff --git a/plugins/CommonUtil/json-c/json_c_version.h b/plugins/CommonUtil/json-c/json_c_version.h deleted file mode 100644 index eed98a497501..000000000000 --- a/plugins/CommonUtil/json-c/json_c_version.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2012 Eric Haszlakiewicz - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - */ - -#ifndef _json_c_version_h_ -#define _json_c_version_h_ - -#define JSON_C_MAJOR_VERSION 0 -#define JSON_C_MINOR_VERSION 12 -#define JSON_C_MICRO_VERSION 0 -#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ - (JSON_C_MINOR_VERSION << 8) | \ - JSON_C_MICRO_VERSION) -#define JSON_C_VERSION "0.12" - -const char *json_c_version(void); /* Returns JSON_C_VERSION */ -int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ - -#endif diff --git a/plugins/CommonUtil/json-c/json_inttypes.h b/plugins/CommonUtil/json-c/json_inttypes.h deleted file mode 100644 index 9de8d246d9dd..000000000000 --- a/plugins/CommonUtil/json-c/json_inttypes.h +++ /dev/null @@ -1,28 +0,0 @@ - -#ifndef _json_inttypes_h_ -#define _json_inttypes_h_ - -#include "json_config.h" - -#if defined(_MSC_VER) && _MSC_VER <= 1700 - -/* Anything less than Visual Studio C++ 10 is missing stdint.h and inttypes.h */ -typedef __int32 int32_t; -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX ((int32_t)_I32_MAX) -typedef __int64 int64_t; -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX ((int64_t)_I64_MAX) -#define PRId64 "I64d" -#define SCNd64 "I64d" - -#else - -#ifdef JSON_C_HAVE_INTTYPES_H -#include -#endif -/* inttypes.h includes stdint.h */ - -#endif - -#endif diff --git a/plugins/CommonUtil/json-c/json_object.c b/plugins/CommonUtil/json-c/json_object.c deleted file mode 100644 index 57f3f0d237d5..000000000000 --- a/plugins/CommonUtil/json-c/json_object.c +++ /dev/null @@ -1,860 +0,0 @@ -/* - * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ - * - * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. - * Michael Clark - * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "printbuf.h" -#include "linkhash.h" -#include "arraylist.h" -#include "json_inttypes.h" -#include "json_object.h" -#include "json_object_private.h" -#include "json_util.h" -#include "math_compat.h" - -#if !defined(HAVE_STRDUP) && defined(_MSC_VER) - /* MSC has the version as _strdup */ -# define strdup _strdup -#elif !defined(HAVE_STRDUP) -# error You do not have strdup on your system. -#endif /* HAVE_STRDUP */ - -#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) - /* MSC has the version as _snprintf */ -# define snprintf _snprintf -#elif !defined(HAVE_SNPRINTF) -# error You do not have snprintf on your system. -#endif /* HAVE_SNPRINTF */ - -// Don't define this. It's not thread-safe. -/* #define REFCOUNT_DEBUG 1 */ - -const char *json_number_chars = "0123456789.+-eE"; -const char *json_hex_chars = "0123456789abcdefABCDEF"; - -static void json_object_generic_delete(struct json_object* jso); -static struct json_object* json_object_new(enum json_type o_type); - -static json_object_to_json_string_fn json_object_object_to_json_string; -static json_object_to_json_string_fn json_object_boolean_to_json_string; -static json_object_to_json_string_fn json_object_int_to_json_string; -static json_object_to_json_string_fn json_object_double_to_json_string; -static json_object_to_json_string_fn json_object_string_to_json_string; -static json_object_to_json_string_fn json_object_array_to_json_string; - - -/* ref count debugging */ - -#ifdef REFCOUNT_DEBUG - -static struct lh_table *json_object_table; - -static void json_object_init(void) __attribute__ ((constructor)); -static void json_object_init(void) { - MC_DEBUG("json_object_init: creating object table\n"); - json_object_table = lh_kptr_table_new(128, "json_object_table", NULL); -} - -static void json_object_fini(void) __attribute__ ((destructor)); -static void json_object_fini(void) { - struct lh_entry *ent; - if(MC_GET_DEBUG()) { - if (json_object_table->count) { - MC_DEBUG("json_object_fini: %d referenced objects at exit\n", - json_object_table->count); - lh_foreach(json_object_table, ent) { - struct json_object* obj = (struct json_object*)ent->v; - MC_DEBUG("\t%s:%p\n", json_type_to_name(obj->o_type), obj); - } - } - } - MC_DEBUG("json_object_fini: freeing object table\n"); - lh_table_free(json_object_table); -} -#endif /* REFCOUNT_DEBUG */ - - -/* string escaping */ - -static int json_escape_str(struct printbuf *pb, char *str, size_t len) -{ - int pos = 0, start_offset = 0; - unsigned char c; - while (len--) { - c = str[pos]; - switch(c) { - case '\b': - case '\n': - case '\r': - case '\t': - case '\f': - case '"': - case '\\': - case '/': - if(pos - start_offset > 0) - printbuf_memappend(pb, str + start_offset, pos - start_offset); - if(c == '\b') printbuf_memappend(pb, "\\b", 2); - else if(c == '\n') printbuf_memappend(pb, "\\n", 2); - else if(c == '\r') printbuf_memappend(pb, "\\r", 2); - else if(c == '\t') printbuf_memappend(pb, "\\t", 2); - else if(c == '\f') printbuf_memappend(pb, "\\f", 2); - else if(c == '"') printbuf_memappend(pb, "\\\"", 2); - else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); - else if(c == '/') printbuf_memappend(pb, "\\/", 2); - start_offset = ++pos; - break; - default: - if(c < ' ') { - if(pos - start_offset > 0) - printbuf_memappend(pb, str + start_offset, pos - start_offset); - sprintbuf(pb, "\\u00%c%c", - json_hex_chars[c >> 4], - json_hex_chars[c & 0xf]); - start_offset = ++pos; - } else pos++; - } - } - if(pos - start_offset > 0) - printbuf_memappend(pb, str + start_offset, pos - start_offset); - return 0; -} - - -/* reference counting */ - -extern struct json_object* json_object_get(struct json_object *jso) -{ - if(jso) { - jso->_ref_count++; - } - return jso; -} - -int json_object_put(struct json_object *jso) -{ - if(jso) - { - jso->_ref_count--; - if(!jso->_ref_count) - { - if (jso->_user_delete) - jso->_user_delete(jso, jso->_userdata); - jso->_delete(jso); - return 1; - } - } - return 0; -} - - -/* generic object construction and destruction parts */ - -static void json_object_generic_delete(struct json_object* jso) -{ -#ifdef REFCOUNT_DEBUG - MC_DEBUG("json_object_delete_%s: %p\n", - json_type_to_name(jso->o_type), jso); - lh_table_delete(json_object_table, jso); -#endif /* REFCOUNT_DEBUG */ - printbuf_free(jso->_pb); - free(jso); -} - -static struct json_object* json_object_new(enum json_type o_type) -{ - struct json_object *jso; - - jso = (struct json_object*)calloc(sizeof(struct json_object), 1); - if(!jso) return NULL; - jso->o_type = o_type; - jso->_ref_count = 1; - jso->_delete = &json_object_generic_delete; -#ifdef REFCOUNT_DEBUG - lh_table_insert(json_object_table, jso, jso); - MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); -#endif /* REFCOUNT_DEBUG */ - return jso; -} - - -/* type checking functions */ - -int json_object_is_type(struct json_object *jso, enum json_type type) -{ - if (!jso) - return (type == json_type_null); - return (jso->o_type == type); -} - -enum json_type json_object_get_type(struct json_object *jso) -{ - if (!jso) - return json_type_null; - return jso->o_type; -} - -/* set a custom conversion to string */ - -void json_object_set_serializer(json_object *jso, - json_object_to_json_string_fn to_string_func, - void *userdata, - json_object_delete_fn *user_delete) -{ - // First, clean up any previously existing user info - if (jso->_user_delete) - { - jso->_user_delete(jso, jso->_userdata); - } - jso->_userdata = NULL; - jso->_user_delete = NULL; - - if (to_string_func == NULL) - { - // Reset to the standard serialization function - switch(jso->o_type) - { - case json_type_null: - jso->_to_json_string = NULL; - break; - case json_type_boolean: - jso->_to_json_string = &json_object_boolean_to_json_string; - break; - case json_type_double: - jso->_to_json_string = &json_object_double_to_json_string; - break; - case json_type_int: - jso->_to_json_string = &json_object_int_to_json_string; - break; - case json_type_object: - jso->_to_json_string = &json_object_object_to_json_string; - break; - case json_type_array: - jso->_to_json_string = &json_object_array_to_json_string; - break; - case json_type_string: - jso->_to_json_string = &json_object_string_to_json_string; - break; - } - return; - } - - jso->_to_json_string = to_string_func; - jso->_userdata = userdata; - jso->_user_delete = user_delete; -} - - -/* extended conversion to string */ - -char* json_object_to_json_string_ext(struct json_object *jso, int flags) -{ - if (!jso) - return "null"; - - if ((!jso->_pb) && !(jso->_pb = printbuf_new())) - return NULL; - - printbuf_reset(jso->_pb); - - if(jso->_to_json_string(jso, jso->_pb, 0, flags) < 0) - return NULL; - - return jso->_pb->buf; -} - -/* backwards-compatible conversion to string */ - -char* json_object_to_json_string(struct json_object *jso) -{ - return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); -} - -static void indent(struct printbuf *pb, int level, int flags) -{ - if (flags & JSON_C_TO_STRING_PRETTY) - { - printbuf_memset(pb, -1, ' ', level * 2); - } -} - -/* json_object_object */ - -static int json_object_object_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) -{ - int had_children = 0; - struct json_object_iter iter; - - sprintbuf(pb, "{" /*}*/); - if (flags & JSON_C_TO_STRING_PRETTY) - sprintbuf(pb, "\n"); - json_object_object_foreachC(jso, iter) - { - if (had_children) - { - sprintbuf(pb, ","); - if (flags & JSON_C_TO_STRING_PRETTY) - sprintbuf(pb, "\n"); - } - had_children = 1; - if (flags & JSON_C_TO_STRING_SPACED) - sprintbuf(pb, " "); - indent(pb, level+1, flags); - sprintbuf(pb, "\""); - json_escape_str(pb, iter.key, strlen(iter.key)); - if (flags & JSON_C_TO_STRING_SPACED) - sprintbuf(pb, "\": "); - else - sprintbuf(pb, "\":"); - if(iter.val == NULL) - sprintbuf(pb, "null"); - else - iter.val->_to_json_string(iter.val, pb, level+1,flags); - } - if (flags & JSON_C_TO_STRING_PRETTY) - { - if (had_children) - sprintbuf(pb, "\n"); - indent(pb,level,flags); - } - if (flags & JSON_C_TO_STRING_SPACED) - return sprintbuf(pb, /*{*/ " }"); - else - return sprintbuf(pb, /*{*/ "}"); -} - - -static void json_object_lh_entry_free(struct lh_entry *ent) -{ - free(ent->k); - json_object_put((struct json_object*)ent->v); -} - -static void json_object_object_delete(struct json_object* jso) -{ - lh_table_free(jso->o.c_object); - json_object_generic_delete(jso); -} - -struct json_object* json_object_new_object(void) -{ - struct json_object* jso = json_object_new(json_type_object); - - if (!jso) - { - return NULL; - } - - jso->_delete = &json_object_object_delete; - jso->_to_json_string = &json_object_object_to_json_string; - jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, NULL, &json_object_lh_entry_free); - - return jso; -} - -struct lh_table* json_object_get_object(struct json_object *jso) -{ - if(!jso) return NULL; - switch(jso->o_type) { - case json_type_object: - return jso->o.c_object; - default: - return NULL; - } -} - -void json_object_object_add(struct json_object* jso, const char *key, - struct json_object *val) -{ - // We lookup the entry and replace the value, rather than just deleting - // and re-adding it, so the existing key remains valid. - json_object *existing_value = NULL; - struct lh_entry *existing_entry; - existing_entry = lh_table_lookup_entry(jso->o.c_object, (void*)key); - if (!existing_entry) - { - lh_table_insert(jso->o.c_object, strdup(key), val); - return; - } - existing_value = (void *)existing_entry->v; - if (existing_value) - json_object_put(existing_value); - existing_entry->v = val; -} - -int json_object_object_length(struct json_object *jso) -{ - return lh_table_length(jso->o.c_object); -} - -struct json_object* json_object_object_get(struct json_object* jso, const char *key) -{ - struct json_object *result = NULL; - json_object_object_get_ex(jso, key, &result); - return result; -} - -json_bool json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) -{ - if (value != NULL) - *value = NULL; - - if (NULL == jso) - return FALSE; - - switch(jso->o_type) - { - case json_type_object: - return lh_table_lookup_ex(jso->o.c_object, (void*)key, (void**)value); - default: - if (value != NULL) - *value = NULL; - return FALSE; - } -} - -void json_object_object_del(struct json_object* jso, const char *key) -{ - lh_table_delete(jso->o.c_object, key); -} - - -/* json_object_boolean */ - -static int json_object_boolean_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) -{ - if(jso->o.c_boolean) return sprintbuf(pb, "true"); - else return sprintbuf(pb, "false"); -} - -struct json_object* json_object_new_boolean(json_bool b) -{ - struct json_object *jso = json_object_new(json_type_boolean); - if(!jso) return NULL; - jso->_to_json_string = &json_object_boolean_to_json_string; - jso->o.c_boolean = b; - return jso; -} - -json_bool json_object_get_boolean(struct json_object *jso) -{ - if(!jso) return FALSE; - switch(jso->o_type) { - case json_type_boolean: - return jso->o.c_boolean; - case json_type_int: - return (jso->o.c_int64 != 0); - case json_type_double: - return (jso->o.c_double != 0); - case json_type_string: - return (jso->o.c_string.len != 0); - default: - return FALSE; - } -} - - -/* json_object_int */ - -static int json_object_int_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) -{ - return sprintbuf(pb, "%"PRId64, jso->o.c_int64); -} - -struct json_object* json_object_new_int(int32_t i) -{ - struct json_object *jso = json_object_new(json_type_int); - if(!jso) return NULL; - jso->_to_json_string = &json_object_int_to_json_string; - jso->o.c_int64 = i; - return jso; -} - -int32_t json_object_get_int(struct json_object *jso) -{ - int64_t cint64; - enum json_type o_type; - - if(!jso) return 0; - - o_type = jso->o_type; - cint64 = jso->o.c_int64; - - if (o_type == json_type_string) - { - /* - * Parse strings into 64-bit numbers, then use the - * 64-to-32-bit number handling below. - */ - if (json_parse_int64(jso->o.c_string.str, &cint64) != 0) - return 0; /* whoops, it didn't work. */ - o_type = json_type_int; - } - - switch(o_type) { - case json_type_int: - /* Make sure we return the correct values for out of range numbers. */ - if (cint64 <= INT32_MIN) - return INT32_MIN; - else if (cint64 >= INT32_MAX) - return INT32_MAX; - else - return (int32_t)cint64; - case json_type_double: - return (int32_t)jso->o.c_double; - case json_type_boolean: - return jso->o.c_boolean; - default: - return 0; - } -} - -struct json_object* json_object_new_int64(int64_t i) -{ - struct json_object *jso = json_object_new(json_type_int); - if(!jso) return NULL; - jso->_to_json_string = &json_object_int_to_json_string; - jso->o.c_int64 = i; - return jso; -} - -int64_t json_object_get_int64(struct json_object *jso) -{ - int64_t cint; - - if(!jso) return 0; - switch(jso->o_type) { - case json_type_int: - return jso->o.c_int64; - case json_type_double: - return (int64_t)jso->o.c_double; - case json_type_boolean: - return jso->o.c_boolean; - case json_type_string: - if (json_parse_int64(jso->o.c_string.str, &cint) == 0) return cint; - default: - return 0; - } -} - - -/* json_object_double */ - -static int json_object_double_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) -{ - char buf[128], *p, *q; - size_t size; - /* Although JSON RFC does not support - NaN or Infinity as numeric values - ECMA 262 section 9.8.1 defines - how to handle these cases as strings */ - if(isnan(jso->o.c_double)) - size = _snprintf_s(buf, sizeof(buf), _TRUNCATE, "NaN"); - else if(isinf(jso->o.c_double)) - if(jso->o.c_double > 0) - size = _snprintf_s(buf, sizeof(buf), _TRUNCATE, "Infinity"); - else - size = _snprintf_s(buf, sizeof(buf), _TRUNCATE, "-Infinity"); - else - size = _snprintf_s(buf, sizeof(buf), _TRUNCATE, "%.17g", jso->o.c_double); - - p = strchr(buf, ','); - if (p) { - *p = '.'; - } else { - p = strchr(buf, '.'); - } - if (p && (flags & JSON_C_TO_STRING_NOZERO)) { - /* last useful digit, always keep 1 zero */ - p++; - for (q=p ; *q ; q++) { - if (*q!='0') p=q; - } - /* drop trailing zeroes */ - *(++p) = 0; - size = p-buf; - } - printbuf_memappend(pb, buf, size); - return (int)size; -} - -struct json_object* json_object_new_double(double d) -{ - struct json_object *jso = json_object_new(json_type_double); - if (!jso) - return NULL; - jso->_to_json_string = &json_object_double_to_json_string; - jso->o.c_double = d; - return jso; -} - -struct json_object* json_object_new_double_s(double d, const char *ds) -{ - struct json_object *jso = json_object_new_double(d); - if (!jso) - return NULL; - - json_object_set_serializer(jso, json_object_userdata_to_json_string, strdup(ds), json_object_free_userdata); - return jso; -} - -int json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, int level, int flags) -{ - size_t userdata_len = strlen(jso->_userdata); - printbuf_memappend(pb, jso->_userdata, userdata_len); - return (int)userdata_len; -} - -void json_object_free_userdata(struct json_object *jso, void *userdata) -{ - free(userdata); -} - -double json_object_get_double(struct json_object *jso) -{ - double cdouble; - char *errPtr = NULL; - - if(!jso) return 0.0; - switch(jso->o_type) { - case json_type_double: - return jso->o.c_double; - case json_type_int: - return (double)jso->o.c_int64; - case json_type_boolean: - return jso->o.c_boolean; - case json_type_string: - errno = 0; - cdouble = strtod(jso->o.c_string.str,&errPtr); - - /* if conversion stopped at the first character, return 0.0 */ - if (errPtr == jso->o.c_string.str) - return 0.0; - - /* - * Check that the conversion terminated on something sensible - * - * For example, { "pay" : 123AB } would parse as 123. - */ - if (*errPtr != '\0') - return 0.0; - - /* - * If strtod encounters a string which would exceed the - * capacity of a double, it returns +/- HUGE_VAL and sets - * errno to ERANGE. But +/- HUGE_VAL is also a valid result - * from a conversion, so we need to check errno. - * - * Underflow also sets errno to ERANGE, but it returns 0 in - * that case, which is what we will return anyway. - * - * See CERT guideline ERR30-C - */ - if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && - (ERANGE == errno)) - cdouble = 0.0; - return cdouble; - default: - return 0.0; - } -} - - -/* json_object_string */ - -static int json_object_string_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) -{ - sprintbuf(pb, "\""); - json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len); - sprintbuf(pb, "\""); - return 0; -} - -static void json_object_string_delete(struct json_object* jso) -{ - free(jso->o.c_string.str); - json_object_generic_delete(jso); -} - -struct json_object* json_object_new_string(const char *s) -{ - struct json_object *jso = json_object_new(json_type_string); - if(!jso) return NULL; - jso->_delete = &json_object_string_delete; - jso->_to_json_string = &json_object_string_to_json_string; - jso->o.c_string.str = strdup(s); - jso->o.c_string.len = (int)strlen(s); - return jso; -} - -struct json_object* json_object_new_string_len(const char *s, size_t len) -{ - struct json_object *jso = json_object_new(json_type_string); - if(!jso) return NULL; - jso->_delete = &json_object_string_delete; - jso->_to_json_string = &json_object_string_to_json_string; - jso->o.c_string.str = (char*)malloc(len + 1); - memcpy(jso->o.c_string.str, (void *)s, len); - jso->o.c_string.str[len] = '\0'; - jso->o.c_string.len = len; - return jso; -} - -char* json_object_get_string(struct json_object *jso) -{ - if (!jso) - return NULL; - switch (jso->o_type) - { - case json_type_string: - return jso->o.c_string.str; - default: - return json_object_to_json_string(jso); - } -} - -int json_object_get_string_len(struct json_object *jso) { - if(!jso) return 0; - switch(jso->o_type) { - case json_type_string: - return (int)jso->o.c_string.len; - default: - return 0; - } -} - - -/* json_object_array */ - -static int json_object_array_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) -{ - int had_children = 0; - int ii; - sprintbuf(pb, "["); - if (flags & JSON_C_TO_STRING_PRETTY) - sprintbuf(pb, "\n"); - for(ii=0; ii < json_object_array_length(jso); ii++) - { - struct json_object *val; - if (had_children) - { - sprintbuf(pb, ","); - if (flags & JSON_C_TO_STRING_PRETTY) - sprintbuf(pb, "\n"); - } - had_children = 1; - if (flags & JSON_C_TO_STRING_SPACED) - sprintbuf(pb, " "); - indent(pb, level + 1, flags); - val = json_object_array_get_idx(jso, ii); - if(val == NULL) - sprintbuf(pb, "null"); - else - val->_to_json_string(val, pb, level+1, flags); - } - if (flags & JSON_C_TO_STRING_PRETTY) - { - if (had_children) - sprintbuf(pb, "\n"); - indent(pb,level,flags); - } - - if (flags & JSON_C_TO_STRING_SPACED) - return sprintbuf(pb, " ]"); - else - return sprintbuf(pb, "]"); -} - -static void json_object_array_entry_free(void *data) -{ - json_object_put((struct json_object*)data); -} - -static void json_object_array_delete(struct json_object* jso) -{ - array_list_free(jso->o.c_array); - json_object_generic_delete(jso); -} - -struct json_object* json_object_new_array(void) -{ - struct json_object *jso = json_object_new(json_type_array); - if(!jso) return NULL; - jso->_delete = &json_object_array_delete; - jso->_to_json_string = &json_object_array_to_json_string; - jso->o.c_array = array_list_new(&json_object_array_entry_free); - return jso; -} - -struct array_list* json_object_get_array(struct json_object *jso) -{ - if(!jso) return NULL; - switch(jso->o_type) { - case json_type_array: - return jso->o.c_array; - default: - return NULL; - } -} - -void json_object_array_sort(struct json_object *jso, int(__cdecl* sort_fn)(const void *, const void *)) -{ - array_list_sort(jso->o.c_array, sort_fn); -} - -int json_object_array_length(struct json_object *jso) -{ - return array_list_length(jso->o.c_array); -} - -int json_object_array_add(struct json_object *jso,struct json_object *val) -{ - return array_list_add(jso->o.c_array, val); -} - -int json_object_array_put_idx(struct json_object *jso, int idx, - struct json_object *val) -{ - return array_list_put_idx(jso->o.c_array, idx, val); -} - -struct json_object* json_object_array_get_idx(struct json_object *jso, - int idx) -{ - return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); -} - diff --git a/plugins/CommonUtil/json-c/json_object.h b/plugins/CommonUtil/json-c/json_object.h deleted file mode 100644 index c649ab7c7bfb..000000000000 --- a/plugins/CommonUtil/json-c/json_object.h +++ /dev/null @@ -1,611 +0,0 @@ -/* - * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ - * - * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. - * Michael Clark - * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - * - */ - -#ifndef _json_object_h_ -#define _json_object_h_ - -#ifdef __GNUC__ -#define THIS_FUNCTION_IS_DEPRECATED(func) func __attribute__ ((deprecated)) -#elif defined(_MSC_VER) -#define THIS_FUNCTION_IS_DEPRECATED(func) __declspec(deprecated) func -#else -#define THIS_FUNCTION_IS_DEPRECATED(func) func -#endif - -#include "json_inttypes.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define JSON_OBJECT_DEF_HASH_ENTRIES 16 - -/** - * A flag for the json_object_to_json_string_ext() and - * json_object_to_file_ext() functions which causes the output - * to have no extra whitespace or formatting applied. - */ -#define JSON_C_TO_STRING_PLAIN 0 -/** - * A flag for the json_object_to_json_string_ext() and - * json_object_to_file_ext() functions which causes the output to have - * minimal whitespace inserted to make things slightly more readable. - */ -#define JSON_C_TO_STRING_SPACED (1<<0) -/** - * A flag for the json_object_to_json_string_ext() and - * json_object_to_file_ext() functions which causes - * the output to be formatted. - * - * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ - * for an example of the format. - */ -#define JSON_C_TO_STRING_PRETTY (1<<1) -/** - * A flag to drop trailing zero for float values - */ -#define JSON_C_TO_STRING_NOZERO (1<<2) - -#undef FALSE -#define FALSE ((json_bool)0) - -#undef TRUE -#define TRUE ((json_bool)1) - -extern const char *json_number_chars; -extern const char *json_hex_chars; - -/* CAW: added for ANSI C iteration correctness */ -struct json_object_iter -{ - char *key; - struct json_object *val; - struct lh_entry *entry; -}; - -/* forward structure definitions */ - -typedef int json_bool; -typedef struct printbuf printbuf; -typedef struct lh_table lh_table; -typedef struct array_list array_list; -typedef struct json_object json_object, *json_object_ptr; -typedef struct json_object_iter json_object_iter; -typedef struct json_tokener json_tokener; - -/** - * Type of custom user delete functions. See json_object_set_serializer. - */ -typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); - -/** - * Type of a custom serialization function. See json_object_set_serializer. - */ -typedef int (json_object_to_json_string_fn)(struct json_object *jso, - struct printbuf *pb, - int level, - int flags); - -/* supported object types */ - -typedef enum json_type { - /* If you change this, be sure to update json_type_to_name() too */ - json_type_null, - json_type_boolean, - json_type_double, - json_type_int, - json_type_object, - json_type_array, - json_type_string, -} json_type; - -/* reference counting functions */ - -/** - * Increment the reference count of json_object, thereby grabbing shared - * ownership of obj. - * - * @param obj the json_object instance - */ -extern struct json_object* json_object_get(struct json_object *obj); - -/** - * Decrement the reference count of json_object and free if it reaches zero. - * You must have ownership of obj prior to doing this or you will cause an - * imbalance in the reference count. - * - * @param obj the json_object instance - * @returns 1 if the object was freed. - */ -int json_object_put(struct json_object *obj); - -/** - * Check if the json_object is of a given type - * @param obj the json_object instance - * @param type one of: - json_type_null (i.e. obj == NULL), - json_type_boolean, - json_type_double, - json_type_int, - json_type_object, - json_type_array, - json_type_string, - */ -extern int json_object_is_type(struct json_object *obj, enum json_type type); - -/** - * Get the type of the json_object. See also json_type_to_name() to turn this - * into a string suitable, for instance, for logging. - * - * @param obj the json_object instance - * @returns type being one of: - json_type_null (i.e. obj == NULL), - json_type_boolean, - json_type_double, - json_type_int, - json_type_object, - json_type_array, - json_type_string, - */ -extern enum json_type json_object_get_type(struct json_object *obj); - - -/** Stringify object to json format. - * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) - * @param obj the json_object instance - * @returns a string in JSON format - */ -extern char* json_object_to_json_string(struct json_object *obj); - -/** Stringify object to json format - * @param obj the json_object instance - * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants - * @returns a string in JSON format - */ -extern char* json_object_to_json_string_ext(struct json_object *obj, int flags); - -/** - * Set a custom serialization function to be used when this particular object - * is converted to a string by json_object_to_json_string. - * - * If a custom serializer is already set on this object, any existing - * user_delete function is called before the new one is set. - * - * If to_string_func is NULL, the other parameters are ignored - * and the default behaviour is reset. - * - * The userdata parameter is optional and may be passed as NULL. If provided, - * it is passed to to_string_func as-is. This parameter may be NULL even - * if user_delete is non-NULL. - * - * The user_delete parameter is optional and may be passed as NULL, even if - * the userdata parameter is non-NULL. It will be called just before the - * json_object is deleted, after it's reference count goes to zero - * (see json_object_put()). - * If this is not provided, it is up to the caller to free the userdata at - * an appropriate time. (i.e. after the json_object is deleted) - * - * @param jso the object to customize - * @param to_string_func the custom serialization function - * @param userdata an optional opaque cookie - * @param user_delete an optional function from freeing userdata - */ -extern void json_object_set_serializer(json_object *jso, - json_object_to_json_string_fn to_string_func, - void *userdata, - json_object_delete_fn *user_delete); - -/** - * Simply call free on the userdata pointer. - * Can be used with json_object_set_serializer(). - * - * @param jso unused - * @param userdata the pointer that is passed to free(). - */ -json_object_delete_fn json_object_free_userdata; - -/** - * Copy the jso->_userdata string over to pb as-is. - * Can be used with json_object_set_serializer(). - * - * @param jso The object whose _userdata is used. - * @param pb The destination buffer. - * @param level Ignored. - * @param flags Ignored. - */ -json_object_to_json_string_fn json_object_userdata_to_json_string; - - -/* object type methods */ - -/** Create a new empty object with a reference count of 1. The caller of - * this object initially has sole ownership. Remember, when using - * json_object_object_add or json_object_array_put_idx, ownership will - * transfer to the object/array. Call json_object_get if you want to maintain - * shared ownership or also add this object as a child of multiple objects or - * arrays. Any ownerships you acquired but did not transfer must be released - * through json_object_put. - * - * @returns a json_object of type json_type_object - */ -extern struct json_object* json_object_new_object(void); - -/** Get the hashtable of a json_object of type json_type_object - * @param obj the json_object instance - * @returns a linkhash - */ -extern struct lh_table* json_object_get_object(struct json_object *obj); - -/** Get the size of an object in terms of the number of fields it has. - * @param obj the json_object whose length to return - */ -extern int json_object_object_length(struct json_object* obj); - -/** Add an object field to a json_object of type json_type_object - * - * The reference count will *not* be incremented. This is to make adding - * fields to objects in code more compact. If you want to retain a reference - * to an added object, independent of the lifetime of obj, you must wrap the - * passed object with json_object_get. - * - * Upon calling this, the ownership of val transfers to obj. Thus you must - * make sure that you do in fact have ownership over this object. For instance, - * json_object_new_object will give you ownership until you transfer it, - * whereas json_object_object_get does not. - * - * @param obj the json_object instance - * @param key the object field name (a private copy will be duplicated) - * @param val a json_object or NULL member to associate with the given field - */ -extern void json_object_object_add(struct json_object* obj, const char *key, - struct json_object *val); - -/** Get the json_object associate with a given object field - * - * *No* reference counts will be changed. There is no need to manually adjust - * reference counts through the json_object_put/json_object_get methods unless - * you need to have the child (value) reference maintain a different lifetime - * than the owning parent (obj). Ownership of the returned value is retained - * by obj (do not do json_object_put unless you have done a json_object_get). - * If you delete the value from obj (json_object_object_del) and wish to access - * the returned reference afterwards, make sure you have first gotten shared - * ownership through json_object_get (& don't forget to do a json_object_put - * or transfer ownership to prevent a memory leak). - * - * @param obj the json_object instance - * @param key the object field name - * @returns the json_object associated with the given field name - * @deprecated Please use json_object_object_get_ex - */ -THIS_FUNCTION_IS_DEPRECATED(extern struct json_object* json_object_object_get(struct json_object* obj, - const char *key)); - -/** Get the json_object associated with a given object field. - * - * This returns true if the key is found, false in all other cases (including - * if obj isn't a json_type_object). - * - * *No* reference counts will be changed. There is no need to manually adjust - * reference counts through the json_object_put/json_object_get methods unless - * you need to have the child (value) reference maintain a different lifetime - * than the owning parent (obj). Ownership of value is retained by obj. - * - * @param obj the json_object instance - * @param key the object field name - * @param value a pointer where to store a reference to the json_object - * associated with the given field name. - * - * It is safe to pass a NULL value. - * @returns whether or not the key exists - */ -extern json_bool json_object_object_get_ex(struct json_object* obj, - const char *key, - struct json_object **value); - -/** Delete the given json_object field - * - * The reference count will be decremented for the deleted object. If there - * are no more owners of the value represented by this key, then the value is - * freed. Otherwise, the reference to the value will remain in memory. - * - * @param obj the json_object instance - * @param key the object field name - */ -extern void json_object_object_del(struct json_object* obj, const char *key); - -/** - * Iterate through all keys and values of an object. - * - * Adding keys to the object while iterating is NOT allowed. - * - * Deleting an existing key, or replacing an existing key with a - * new value IS allowed. - * - * @param obj the json_object instance - * @param key the local name for the char* key variable defined in the body - * @param val the local name for the json_object* object variable defined in - * the body - */ -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L - -# define json_object_object_foreach(obj,key,val) \ - char *key; \ - struct json_object *val __attribute__((__unused__)); \ - for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ - ({ if(entry ## key) { \ - key = (char*)entry ## key->k; \ - val = (struct json_object*)entry ## key->v; \ - entry_next ## key = entry ## key->next; \ - } ; entry ## key; }); \ - entry ## key = entry_next ## key ) - -#else /* ANSI C or MSC */ - -# define json_object_object_foreach(obj,key,val) \ - char *key;\ - struct json_object *val; \ - struct lh_entry *entry ## key; \ - struct lh_entry *entry_next ## key = NULL; \ - for(entry ## key = json_object_get_object(obj)->head; \ - (entry ## key ? ( \ - key = (char*)entry ## key->k, \ - val = (struct json_object*)entry ## key->v, \ - entry_next ## key = entry ## key->next, \ - entry ## key) : 0); \ - entry ## key = entry_next ## key) - -#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ - -/** Iterate through all keys and values of an object (ANSI C Safe) - * @param obj the json_object instance - * @param iter the object iterator - */ -#define json_object_object_foreachC(obj,iter) \ - for(iter.entry = json_object_get_object(obj)->head; (iter.entry ? (iter.key = (char*)iter.entry->k, iter.val = (struct json_object*)iter.entry->v, iter.entry) : 0); iter.entry = iter.entry->next) - -/* Array type methods */ - -/** Create a new empty json_object of type json_type_array - * @returns a json_object of type json_type_array - */ -extern struct json_object* json_object_new_array(void); - -/** Get the arraylist of a json_object of type json_type_array - * @param obj the json_object instance - * @returns an arraylist - */ -extern struct array_list* json_object_get_array(struct json_object *obj); - -/** Get the length of a json_object of type json_type_array - * @param obj the json_object instance - * @returns an int - */ -extern int json_object_array_length(struct json_object *obj); - -/** Sorts the elements of jso of type json_type_array -* -* Pointers to the json_object pointers will be passed as the two arguments -* to @sort_fn -* -* @param obj the json_object instance -* @param sort_fn a sorting function -*/ -extern void json_object_array_sort(struct json_object *jso, int(__cdecl* sort_fn)(const void *, const void *)); - -/** Add an element to the end of a json_object of type json_type_array - * - * The reference count will *not* be incremented. This is to make adding - * fields to objects in code more compact. If you want to retain a reference - * to an added object you must wrap the passed object with json_object_get - * - * @param obj the json_object instance - * @param val the json_object to be added - */ -extern int json_object_array_add(struct json_object *obj, - struct json_object *val); - -/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) - * - * The reference count will *not* be incremented. This is to make adding - * fields to objects in code more compact. If you want to retain a reference - * to an added object you must wrap the passed object with json_object_get - * - * The reference count of a replaced object will be decremented. - * - * The array size will be automatically be expanded to the size of the - * index if the index is larger than the current size. - * - * @param obj the json_object instance - * @param idx the index to insert the element at - * @param val the json_object to be added - */ -extern int json_object_array_put_idx(struct json_object *obj, int idx, - struct json_object *val); - -/** Get the element at specificed index of the array (a json_object of type json_type_array) - * @param obj the json_object instance - * @param idx the index to get the element at - * @returns the json_object at the specified index (or NULL) - */ -extern struct json_object* json_object_array_get_idx(struct json_object *obj, - int idx); - -/* json_bool type methods */ - -/** Create a new empty json_object of type json_type_boolean - * @param b a json_bool TRUE or FALSE (0 or 1) - * @returns a json_object of type json_type_boolean - */ -extern struct json_object* json_object_new_boolean(json_bool b); - -/** Get the json_bool value of a json_object - * - * The type is coerced to a json_bool if the passed object is not a json_bool. - * integer and double objects will return FALSE if there value is zero - * or TRUE otherwise. If the passed object is a string it will return - * TRUE if it has a non zero length. If any other object type is passed - * TRUE will be returned if the object is not NULL. - * - * @param obj the json_object instance - * @returns a json_bool - */ -extern json_bool json_object_get_boolean(struct json_object *obj); - - -/* int type methods */ - -/** Create a new empty json_object of type json_type_int - * Note that values are stored as 64-bit values internally. - * To ensure the full range is maintained, use json_object_new_int64 instead. - * @param i the integer - * @returns a json_object of type json_type_int - */ -extern struct json_object* json_object_new_int(int32_t i); - - -/** Create a new empty json_object of type json_type_int - * @param i the integer - * @returns a json_object of type json_type_int - */ -extern struct json_object* json_object_new_int64(int64_t i); - - -/** Get the int value of a json_object - * - * The type is coerced to a int if the passed object is not a int. - * double objects will return their integer conversion. Strings will be - * parsed as an integer. If no conversion exists then 0 is returned - * and errno is set to EINVAL. null is equivalent to 0 (no error values set) - * - * Note that integers are stored internally as 64-bit values. - * If the value of too big or too small to fit into 32-bit, INT32_MAX or - * INT32_MIN are returned, respectively. - * - * @param obj the json_object instance - * @returns an int - */ -extern int32_t json_object_get_int(struct json_object *obj); - -/** Get the int value of a json_object - * - * The type is coerced to a int64 if the passed object is not a int64. - * double objects will return their int64 conversion. Strings will be - * parsed as an int64. If no conversion exists then 0 is returned. - * - * NOTE: Set errno to 0 directly before a call to this function to determine - * whether or not conversion was successful (it does not clear the value for - * you). - * - * @param obj the json_object instance - * @returns an int64 - */ -extern int64_t json_object_get_int64(struct json_object *obj); - - -/* double type methods */ - -/** Create a new empty json_object of type json_type_double - * @param d the double - * @returns a json_object of type json_type_double - */ -extern struct json_object* json_object_new_double(double d); - -/** - * Create a new json_object of type json_type_double, using - * the exact serialized representation of the value. - * - * This allows for numbers that would otherwise get displayed - * inefficiently (e.g. 12.3 => "12.300000000000001") to be - * serialized with the more convenient form. - * - * Note: this is used by json_tokener_parse_ex() to allow for - * an exact re-serialization of a parsed object. - * - * An equivalent sequence of calls is: - * @code - * jso = json_object_new_double(d); - * json_object_set_serializer(d, json_object_userdata_to_json_string, - * strdup(ds), json_object_free_userdata) - * @endcode - * - * @param d the numeric value of the double. - * @param ds the string representation of the double. This will be copied. - */ -extern struct json_object* json_object_new_double_s(double d, const char *ds); - -/** Get the double floating point value of a json_object - * - * The type is coerced to a double if the passed object is not a double. - * integer objects will return their double conversion. Strings will be - * parsed as a double. If no conversion exists then 0.0 is returned and - * errno is set to EINVAL. null is equivalent to 0 (no error values set) - * - * If the value is too big to fit in a double, then the value is set to - * the closest infinity with errno set to ERANGE. If strings cannot be - * converted to their double value, then EINVAL is set & NaN is returned. - * - * Arrays of length 0 are interpreted as 0 (with no error flags set). - * Arrays of length 1 are effectively cast to the equivalent object and - * converted using the above rules. All other arrays set the error to - * EINVAL & return NaN. - * - * NOTE: Set errno to 0 directly before a call to this function to - * determine whether or not conversion was successful (it does not clear - * the value for you). - * - * @param obj the json_object instance - * @returns a double floating point number - */ -extern double json_object_get_double(struct json_object *obj); - - -/* string type methods */ - -/** Create a new empty json_object of type json_type_string - * - * A copy of the string is made and the memory is managed by the json_object - * - * @param s the string - * @returns a json_object of type json_type_string - */ -extern struct json_object* json_object_new_string(const char *s); - -extern struct json_object* json_object_new_string_len(const char *s, size_t len); - -/** Get the string value of a json_object - * - * If the passed object is not of type json_type_string then the JSON - * representation of the object is returned. - * - * The returned string memory is managed by the json_object and will - * be freed when the reference count of the json_object drops to zero. - * - * @param obj the json_object instance - * @returns a string - */ -extern char* json_object_get_string(struct json_object *obj); - -/** Get the string length of a json_object - * - * If the passed object is not of type json_type_string then zero - * will be returned. - * - * @param obj the json_object instance - * @returns int - */ -extern int json_object_get_string_len(struct json_object *obj); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/plugins/CommonUtil/json-c/json_util.c b/plugins/CommonUtil/json-c/json_util.c deleted file mode 100644 index fda5d1ec00a7..000000000000 --- a/plugins/CommonUtil/json-c/json_util.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ - * - * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. - * Michael Clark - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - * - */ - -#include "config.h" -#undef realloc - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_TYPES_H -#include -#endif /* HAVE_SYS_TYPES_H */ - -#ifdef HAVE_SYS_STAT_H -#include -#endif /* HAVE_SYS_STAT_H */ - -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ - -#ifdef HAVE_UNISTD_H -# include -#endif /* HAVE_UNISTD_H */ - -#ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN -# include -# include -#endif /* defined(_WIN32) */ - -#if !defined(HAVE_OPEN) && defined(_WIN32) -# define open _open -#endif - -#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) - /* MSC has the version as _snprintf */ -# define snprintf _snprintf -#elif !defined(HAVE_SNPRINTF) -# error You do not have snprintf on your system. -#endif /* HAVE_SNPRINTF */ - -#include "bits.h" -#include "debug.h" -#include "printbuf.h" -#include "json_inttypes.h" -#include "json_object.h" -#include "json_tokener.h" -#include "json_util.h" - -static int sscanf_is_broken = 0; -static int sscanf_is_broken_testdone = 0; -static void sscanf_is_broken_test(void); - -struct json_object* json_object_from_file(const char *filename) -{ - //struct printbuf *pb; - //struct json_object *obj; - //char buf[JSON_FILE_BUF_SIZE]; - //int fd, ret; - - //if((fd = open(filename, O_RDONLY)) < 0) { - // MC_ERROR("json_object_from_file: error opening file %s: %s\n", - // filename, strerror(errno)); - // return NULL; - //} - //if(!(pb = printbuf_new())) { - // close(fd); - // MC_ERROR("json_object_from_file: printbuf_new failed\n"); - // return NULL; - //} - //while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { - // printbuf_memappend(pb, buf, ret); - //} - //close(fd); - //if(ret < 0) { - // MC_ERROR("json_object_from_file: error reading file %s: %s\n", - // filename, strerror(errno)); - // printbuf_free(pb); - // return NULL; - //} - //obj = json_tokener_parse(pb->buf); - //printbuf_free(pb); - return NULL;//obj; -} - -/* extended "format and write to file" function */ - -int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) -{ - //const char *json_str; - //int fd, ret; - //unsigned int wpos, wsize; - - //if(!obj) { - // MC_ERROR("json_object_to_file: object is null\n"); - // return -1; - //} - - //if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { - // MC_ERROR("json_object_to_file: error opening file %s: %s\n", - // filename, strerror(errno)); - // return -1; - //} - - //if(!(json_str = json_object_to_json_string_ext(obj,flags))) { - // close(fd); - // return -1; - //} - - //wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ - //wpos = 0; - //while(wpos < wsize) { - // if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { - // close(fd); - // MC_ERROR("json_object_to_file: error writing file %s: %s\n", - // filename, strerror(errno)); - // return -1; - // } - - // /* because of the above check for ret < 0, we can safely cast and add */ - // wpos += (unsigned int)ret; - //} - - //close(fd); - return 0; -} - -// backwards compatible "format and write to file" function - -int json_object_to_file(const char *filename, struct json_object *obj) -{ - return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); -} - -int json_parse_double(const char *buf, double *retval) -{ - return (sscanf_s(buf, "%lf", retval)==1 ? 0 : 1); -} - -/* - * Not all implementations of sscanf actually work properly. - * Check whether the one we're currently using does, and if - * it's broken, enable the workaround code. - */ -static void sscanf_is_broken_test() -{ - int64_t num64; - int ret_errno, is_int64_min, ret_errno2, is_int64_max; - - sscanf_s(" -01234567890123456789012345", "%" SCNd64, &num64); - ret_errno = errno; - is_int64_min = (num64 == INT64_MIN); - - sscanf_s(" 01234567890123456789012345", "%" SCNd64, &num64); - ret_errno2 = errno; - is_int64_max = (num64 == INT64_MAX); - - if (ret_errno != ERANGE || !is_int64_min || - ret_errno2 != ERANGE || !is_int64_max) - { - MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); - sscanf_is_broken = 1; - } -} - -int json_parse_int64(const char *buf, int64_t *retval) -{ - int64_t num64; - const char *buf_sig_digits; - int orig_has_neg; - int saved_errno; - - if (!sscanf_is_broken_testdone) - { - sscanf_is_broken_test(); - sscanf_is_broken_testdone = 1; - } - - // Skip leading spaces - while (isspace((int)*buf) && *buf) - buf++; - - errno = 0; // sscanf won't always set errno, so initialize - - if (sscanf_s(buf, "%" SCNd64, &num64) != 1) - { - MC_DEBUG("Failed to parse, sscanf != 1\n"); - return 1; - } - - saved_errno = errno; - buf_sig_digits = buf; - orig_has_neg = 0; - if (*buf_sig_digits == '-') - { - buf_sig_digits++; - orig_has_neg = 1; - } - - // Not all sscanf implementations actually work - if (sscanf_is_broken && saved_errno != ERANGE) - { - char buf_cmp[100]; - char *buf_cmp_start = buf_cmp; - int recheck_has_neg = 0; - int buf_cmp_len; - - // Skip leading zeros, but keep at least one digit - while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') - buf_sig_digits++; - if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 - orig_has_neg = 0; // "-0" is the same as just plain "0" - - _snprintf_s(buf_cmp_start, sizeof(buf_cmp), _TRUNCATE, "%" PRId64, num64); - if (*buf_cmp_start == '-') - { - recheck_has_neg = 1; - buf_cmp_start++; - } - // No need to skip leading spaces or zeros here. - - buf_cmp_len = (int)strlen(buf_cmp_start); - /** - * If the sign is different, or - * some of the digits are different, or - * there is another digit present in the original string - * then we have NOT successfully parsed the value. - */ - if (orig_has_neg != recheck_has_neg || - strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || - ((int)strlen(buf_sig_digits) != buf_cmp_len && - isdigit((int)buf_sig_digits[buf_cmp_len]) - ) - ) - { - saved_errno = ERANGE; - } - } - - // Not all sscanf impl's set the value properly when out of range. - // Always do this, even for properly functioning implementations, - // since it shouldn't slow things down much. - if (saved_errno == ERANGE) - { - if (orig_has_neg) - num64 = INT64_MIN; - else - num64 = INT64_MAX; - } - *retval = num64; - return 0; -} - -#ifndef HAVE_REALLOC -void* rpl_realloc(void* p, size_t n) -{ - if (n == 0) - n = 1; - if (p == 0) - return malloc(n); - return realloc(p, n); -} -#endif - -#define NELEM(a) (sizeof(a) / sizeof(a[0])) -static const char* json_type_name[] = { - /* If you change this, be sure to update the enum json_type definition too */ - "null", - "boolean", - "double", - "int", - "object", - "array", - "string", -}; - -const char *json_type_to_name(enum json_type o_type) -{ - int o_type_int = (int)o_type; - if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) - { - MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); - return NULL; - } - return json_type_name[o_type]; -} - diff --git a/plugins/CommonUtil/json-c/json_util.h b/plugins/CommonUtil/json-c/json_util.h deleted file mode 100644 index 1005e58c5b0d..000000000000 --- a/plugins/CommonUtil/json-c/json_util.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ - * - * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. - * Michael Clark - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - * - */ - -#ifndef _json_util_h_ -#define _json_util_h_ - -#include "json_object.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define JSON_FILE_BUF_SIZE 4096 - -/* utility functions */ -extern struct json_object* json_object_from_file(const char *filename); -extern int json_object_to_file(const char *filename, struct json_object *obj); -extern int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags); -extern int json_parse_int64(const char *buf, int64_t *retval); -extern int json_parse_double(const char *buf, double *retval); - - -/** - * Return a string describing the type of the object. - * e.g. "int", or "object", etc... - */ -extern const char *json_type_to_name(enum json_type o_type); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/plugins/CommonUtil/json-c/linkhash.h b/plugins/CommonUtil/json-c/linkhash.h deleted file mode 100644 index 950d09f35d70..000000000000 --- a/plugins/CommonUtil/json-c/linkhash.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ - * - * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. - * Michael Clark - * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - * - */ - -#ifndef _linkhash_h_ -#define _linkhash_h_ - -#include "json_object.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * golden prime used in hash functions - */ -#define LH_PRIME 0x9e370001UL - -/** - * The fraction of filled hash buckets until an insert will cause the table - * to be resized. - * This can range from just above 0 up to 1.0. - */ -#define LH_LOAD_FACTOR 0.66 - -/** - * sentinel pointer value for empty slots - */ -#define LH_EMPTY (void*)-1 - -/** - * sentinel pointer value for freed slots - */ -#define LH_FREED (void*)-2 - -struct lh_entry; - -/** - * callback function prototypes - */ -typedef void (lh_entry_free_fn) (struct lh_entry *e); -/** - * callback function prototypes - */ -typedef unsigned long (lh_hash_fn) (const void *k); -/** - * callback function prototypes - */ -typedef int (lh_equal_fn) (const void *k1, const void *k2); - -/** - * An entry in the hash table - */ -struct lh_entry { - /** - * The key. - */ - void *k; - /** - * The value. - */ - const void *v; - /** - * The next entry - */ - struct lh_entry *next; - /** - * The previous entry. - */ - struct lh_entry *prev; -}; - - -/** - * The hash table structure. - */ -struct lh_table { - /** - * Size of our hash. - */ - int size; - /** - * Numbers of entries. - */ - int count; - - /** - * Number of collisions. - */ - int collisions; - - /** - * Number of resizes. - */ - int resizes; - - /** - * Number of lookups. - */ - int lookups; - - /** - * Number of inserts. - */ - int inserts; - - /** - * Number of deletes. - */ - int deletes; - - /** - * Name of the hash table. - */ - const char *name; - - /** - * The first entry. - */ - struct lh_entry *head; - - /** - * The last entry. - */ - struct lh_entry *tail; - - struct lh_entry *table; - - /** - * A pointer onto the function responsible for freeing an entry. - */ - lh_entry_free_fn *free_fn; - lh_hash_fn *hash_fn; - lh_equal_fn *equal_fn; -}; - - -/** - * Pre-defined hash and equality functions - */ -extern unsigned long lh_ptr_hash(const void *k); -extern int lh_ptr_equal(const void *k1, const void *k2); - -extern unsigned long lh_char_hash(const void *k); -extern int lh_char_equal(const void *k1, const void *k2); - - -/** - * Convenience list iterator. - */ -#define lh_foreach(table, entry) \ -for(entry = table->head; entry; entry = entry->next) - -/** - * lh_foreach_safe allows calling of deletion routine while iterating. - */ -#define lh_foreach_safe(table, entry, tmp) \ -for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) - - - -/** - * Create a new linkhash table. - * @param size initial table size. The table is automatically resized - * although this incurs a performance penalty. - * @param name the table name. - * @param free_fn callback function used to free memory for entries - * when lh_table_free or lh_table_delete is called. - * If NULL is provided, then memory for keys and values - * must be freed by the caller. - * @param hash_fn function used to hash keys. 2 standard ones are defined: - * lh_ptr_hash and lh_char_hash for hashing pointer values - * and C strings respectively. - * @param equal_fn comparison function to compare keys. 2 standard ones defined: - * lh_ptr_hash and lh_char_hash for comparing pointer values - * and C strings respectively. - * @return a pointer onto the linkhash table. - */ -extern struct lh_table* lh_table_new(int size, const char *name, - lh_entry_free_fn *free_fn, - lh_hash_fn *hash_fn, - lh_equal_fn *equal_fn); - -/** - * Convenience function to create a new linkhash - * table with char keys. - * @param size initial table size. - * @param name table name. - * @param free_fn callback function used to free memory for entries. - * @return a pointer onto the linkhash table. - */ -extern struct lh_table* lh_kchar_table_new(int size, const char *name, - lh_entry_free_fn *free_fn); - - -/** - * Convenience function to create a new linkhash - * table with ptr keys. - * @param size initial table size. - * @param name table name. - * @param free_fn callback function used to free memory for entries. - * @return a pointer onto the linkhash table. - */ -extern struct lh_table* lh_kptr_table_new(int size, const char *name, - lh_entry_free_fn *free_fn); - - -/** - * Free a linkhash table. - * If a callback free function is provided then it is called for all - * entries in the table. - * @param t table to free. - */ -extern void lh_table_free(struct lh_table *t); - - -/** - * Insert a record into the table. - * @param t the table to insert into. - * @param k a pointer to the key to insert. - * @param v a pointer to the value to insert. - */ -extern int lh_table_insert(struct lh_table *t, void *k, const void *v); - - -/** - * Lookup a record into the table. - * @param t the table to lookup - * @param k a pointer to the key to lookup - * @return a pointer to the record structure of the value or NULL if it does not exist. - */ -extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); - -/** - * Lookup a record into the table - * @param t the table to lookup - * @param k a pointer to the key to lookup - * @return a pointer to the found value or NULL if it does not exist. - * @deprecated Use lh_table_lookup_ex instead. - */ -THIS_FUNCTION_IS_DEPRECATED(extern const void* lh_table_lookup(struct lh_table *t, const void *k)); - -/** - * Lookup a record in the table - * @param t the table to lookup - * @param k a pointer to the key to lookup - * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). - * @return whether or not the key was found - */ -extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); - -/** - * Delete a record from the table. - * If a callback free function is provided then it is called for the - * for the item being deleted. - * @param t the table to delete from. - * @param e a pointer to the entry to delete. - * @return 0 if the item was deleted. - * @return -1 if it was not found. - */ -extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); - - -/** - * Delete a record from the table. - * If a callback free function is provided then it is called for the - * for the item being deleted. - * @param t the table to delete from. - * @param k a pointer to the key to delete. - * @return 0 if the item was deleted. - * @return -1 if it was not found. - */ -extern int lh_table_delete(struct lh_table *t, const void *k); - -extern int lh_table_length(struct lh_table *t); - -void lh_abort(const char *msg, ...); -void lh_table_resize(struct lh_table *t, int new_size); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/plugins/CommonUtil/json-c/printbuf.h b/plugins/CommonUtil/json-c/printbuf.h deleted file mode 100644 index ed003b61f3c2..000000000000 --- a/plugins/CommonUtil/json-c/printbuf.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ - * - * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. - * Michael Clark - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See COPYING for details. - * - * - * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. - * The copyrights to the contents of this file are licensed under the MIT License - * (http://www.opensource.org/licenses/mit-license.php) - */ - -#ifndef _printbuf_h_ -#define _printbuf_h_ - -#ifdef __cplusplus -extern "C" { -#endif - -struct printbuf { - char *buf; - size_t bpos; - size_t size; -}; - -extern struct printbuf* -printbuf_new(void); - -/* As an optimization, printbuf_memappend_fast is defined as a macro - * that handles copying data if the buffer is large enough; otherwise - * it invokes printbuf_memappend_real() which performs the heavy - * lifting of realloc()ing the buffer and copying data. - * Your code should not use printbuf_memappend directly--use - * printbuf_memappend_fast instead. - */ -extern size_t printbuf_memappend(struct printbuf *p, const char *buf, size_t size); - -__inline void printbuf_memappend_fast(struct printbuf *p, const char *bufptr, size_t bufsize) -{ - if ((p->size - p->bpos) > bufsize) - { - memcpy(p->buf + p->bpos, (bufptr), bufsize); - p->bpos += (int)bufsize; - p->buf[p->bpos] = '\0'; - } - else - { - printbuf_memappend(p, (bufptr), bufsize); - } -} - -#define printbuf_length(p) ((p)->bpos) - -/** - * Set len bytes of the buffer to charvalue, starting at offset offset. - * Similar to calling memset(x, charvalue, len); - * - * The memory allocated for the buffer is extended as necessary. - * - * If offset is -1, this starts at the end of the current data in the buffer. - */ -extern int -printbuf_memset(struct printbuf *pb, size_t offset, int charvalue, size_t len); - -extern int -sprintbuf(struct printbuf *p, const char *msg, ...); - -extern void -printbuf_reset(struct printbuf *p); - -extern void -printbuf_free(struct printbuf *p); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/plugins/CommonUtil/main.c b/plugins/CommonUtil/main.c deleted file mode 100644 index 26bc8af83ebe..000000000000 --- a/plugins/CommonUtil/main.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Process Hacker Plugins - - * CommonUtil Plugin - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - * - */ - -#include "main.h" - -PPH_PLUGIN PluginInstance; -COMMONUTIL_INTERFACE PluginInterface = -{ - COMMONUTIL_INTERFACE_VERSION, - - UtilCreateSearchControl, - UtilLoadImageFromResources, - - UtilCreateJsonParser, - UtilCleanupJsonParser, - UtilGetJsonValueAsString, - UtilGetJsonValueAsUlong, - UtilGetJsonObjectBool, - UtilCreateJsonObject, - UtilGetJsonObject, - UtilGetJsonObjectLength, - UtilJsonAddObject, - UtilCreateJsonArray, - UtilAddJsonArray, - UtilGetJsonArrayString, - UtilGetJsonArrayUlong, - UtilGetArrayLength, - UtilGetObjectArrayIndex, - UtilGetObjectArrayList -}; - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Common Plugin Extensions"; - info->Author = L"dmex"; - info->Description = L"Common Plugin Extensions"; - info->HasOptions = FALSE; - info->Interface = &PluginInterface; - } - break; - } - - return TRUE; -} diff --git a/plugins/CommonUtil/main.h b/plugins/CommonUtil/main.h deleted file mode 100644 index 014638f6d94a..000000000000 --- a/plugins/CommonUtil/main.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Process Hacker Plugins - - * CommonUtil Plugin - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - * - */ - -#ifndef _WCT_H_ -#define _WCT_H_ - -#define CINTERFACE -#define COBJMACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "resource.h" -#include "CommonUtil.h" - -#define PLUGIN_NAME L"ProcessHacker.CommonUtil" - -extern PPH_PLUGIN PluginInstance; - -typedef struct _EDIT_CONTEXT -{ - union - { - ULONG Flags; - struct - { - ULONG Hot : 1; - ULONG Pushed : 1; - ULONG Spare : 30; - }; - }; - - LONG CXWidth; - INT CXBorder; - INT ImageWidth; - INT ImageHeight; - HWND WindowHandle; - HFONT WindowFont; - HICON BitmapActive; - HICON BitmapInactive; - HBRUSH BrushNormal; - HBRUSH BrushPushed; - HBRUSH BrushHot; -} EDIT_CONTEXT, *PEDIT_CONTEXT; - -VOID UtilCreateSearchControl( - _In_ HWND Parent, - _In_ HWND WindowHandle, - _In_ PWSTR BannerText - ); - -HBITMAP UtilLoadImageFromResources( - _In_ PVOID DllBase, - _In_ UINT Width, - _In_ UINT Height, - _In_ PCWSTR Name, - _In_ BOOLEAN RGBAImage - ); - -HICON BitmapToIcon( - _In_ HBITMAP BitmapHandle, - _In_ INT Width, - _In_ INT Height - ); - -PVOID UtilCreateJsonArray( - VOID - ); - -PVOID UtilCreateJsonParser( - _In_ PSTR JsonString - ); - -VOID UtilCleanupJsonParser( - _In_ PVOID Object - ); - -PSTR UtilGetJsonValueAsString( - _In_ PVOID Object, - _In_ PSTR Key - ); - -INT64 UtilGetJsonValueAsUlong( - _In_ PVOID Object, - _In_ PSTR Key - ); - -VOID UtilAddJsonArray( - _In_ PVOID jsonArray, - _In_ PVOID jsonEntry - ); - -PVOID UtilCreateJsonObject( - VOID - ); - -PVOID UtilGetJsonObject( - _In_ PVOID Object, - _In_ PSTR Key - ); - -INT UtilGetJsonObjectLength( - _In_ PVOID Object - ); - -BOOL UtilGetJsonObjectBool( - _In_ PVOID Object, - _In_ PSTR Key - ); - -VOID UtilJsonAddObject( - _In_ PVOID Object, - _In_ PSTR Key, - _In_ PSTR Value - ); - -PSTR UtilGetJsonArrayString( - _In_ PVOID Object - ); - -INT64 UtilGetJsonArrayUlong( - _In_ PVOID Object, - _In_ INT Index - ); - -INT UtilGetArrayLength( - _In_ PVOID Object - ); - -PVOID UtilGetObjectArrayIndex( - _In_ PVOID Object, - _In_ INT Index - ); - -PPH_LIST UtilGetObjectArrayList( - _In_ PVOID Object - ); - -#endif \ No newline at end of file diff --git a/plugins/CommonUtil/png.c b/plugins/CommonUtil/png.c deleted file mode 100644 index 9ed44280287b..000000000000 --- a/plugins/CommonUtil/png.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Process Hacker CommonUtil Plugin - * PNG image control - * - * Copyright (C) 2012-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - * - */ - -#include "main.h" -#include - -HBITMAP UtilLoadImageFromResources( - _In_ PVOID DllBase, - _In_ UINT Width, - _In_ UINT Height, - _In_ PCWSTR Name, - _In_ BOOLEAN RGBAImage - ) -{ - BOOLEAN success = FALSE; - UINT frameCount = 0; - ULONG resourceLength = 0; - HGLOBAL resourceHandle = NULL; - HRSRC resourceHandleSource = NULL; - WICInProcPointer resourceBuffer = NULL; - HDC screenHdc = NULL; - HDC bufferDc = NULL; - BITMAPINFO bitmapInfo = { 0 }; - HBITMAP bitmapHandle = NULL; - PVOID bitmapBuffer = NULL; - IWICStream* wicStream = NULL; - IWICBitmapSource* wicBitmapSource = NULL; - IWICBitmapDecoder* wicDecoder = NULL; - IWICBitmapFrameDecode* wicFrame = NULL; - IWICImagingFactory* wicFactory = NULL; - IWICBitmapScaler* wicScaler = NULL; - WICPixelFormatGUID pixelFormat; - WICRect rect = { 0, 0, Width, Height }; - - // Create the ImagingFactory - if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, &wicFactory))) - goto CleanupExit; - - // Find the resource - if ((resourceHandleSource = FindResource(DllBase, Name, L"PNG")) == NULL) - goto CleanupExit; - - // Get the resource length - resourceLength = SizeofResource(DllBase, resourceHandleSource); - - // Load the resource - if ((resourceHandle = LoadResource(DllBase, resourceHandleSource)) == NULL) - goto CleanupExit; - - if ((resourceBuffer = (WICInProcPointer)LockResource(resourceHandle)) == NULL) - goto CleanupExit; - - // Create the Stream - if (FAILED(IWICImagingFactory_CreateStream(wicFactory, &wicStream))) - goto CleanupExit; - - // Initialize the Stream from Memory - if (FAILED(IWICStream_InitializeFromMemory(wicStream, resourceBuffer, resourceLength))) - goto CleanupExit; - - if (FAILED(IWICImagingFactory_CreateDecoder(wicFactory, &GUID_ContainerFormatPng, NULL, &wicDecoder))) - goto CleanupExit; - - if (FAILED(IWICBitmapDecoder_Initialize(wicDecoder, (IStream*)wicStream, WICDecodeMetadataCacheOnLoad))) - goto CleanupExit; - - // Get the Frame count - if (FAILED(IWICBitmapDecoder_GetFrameCount(wicDecoder, &frameCount)) || frameCount < 1) - goto CleanupExit; - - // Get the Frame - if (FAILED(IWICBitmapDecoder_GetFrame(wicDecoder, 0, &wicFrame))) - goto CleanupExit; - - // Get the WicFrame image format - if (FAILED(IWICBitmapFrameDecode_GetPixelFormat(wicFrame, &pixelFormat))) - goto CleanupExit; - - // Check if the image format is supported: - if (IsEqualGUID(&pixelFormat, RGBAImage ? &GUID_WICPixelFormat32bppPRGBA : &GUID_WICPixelFormat32bppPBGRA)) - { - wicBitmapSource = (IWICBitmapSource*)wicFrame; - } - else - { - IWICFormatConverter* wicFormatConverter = NULL; - - if (FAILED(IWICImagingFactory_CreateFormatConverter(wicFactory, &wicFormatConverter))) - goto CleanupExit; - - if (FAILED(IWICFormatConverter_Initialize( - wicFormatConverter, - (IWICBitmapSource*)wicFrame, - RGBAImage ? &GUID_WICPixelFormat32bppPRGBA : &GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, - NULL, - 0.0, - WICBitmapPaletteTypeCustom - ))) - { - IWICFormatConverter_Release(wicFormatConverter); - goto CleanupExit; - } - - // Convert the image to the correct format: - IWICFormatConverter_QueryInterface(wicFormatConverter, &IID_IWICBitmapSource, &wicBitmapSource); - - // Cleanup the converter. - IWICFormatConverter_Release(wicFormatConverter); - - // Dispose the old frame now that the converted frame is in wicBitmapSource. - IWICBitmapFrameDecode_Release(wicFrame); - } - - bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmapInfo.bmiHeader.biWidth = rect.Width; - bitmapInfo.bmiHeader.biHeight = -((LONG)rect.Height); - bitmapInfo.bmiHeader.biPlanes = 1; - bitmapInfo.bmiHeader.biBitCount = 32; - bitmapInfo.bmiHeader.biCompression = BI_RGB; - - screenHdc = CreateIC(L"DISPLAY", NULL, NULL, NULL); - bufferDc = CreateCompatibleDC(screenHdc); - bitmapHandle = CreateDIBSection(screenHdc, &bitmapInfo, DIB_RGB_COLORS, &bitmapBuffer, NULL, 0); - - // Check if it's the same rect as the requested size. - //if (width != rect.Width || height != rect.Height) - if (FAILED(IWICImagingFactory_CreateBitmapScaler(wicFactory, &wicScaler))) - goto CleanupExit; - if (FAILED(IWICBitmapScaler_Initialize(wicScaler, wicBitmapSource, rect.Width, rect.Height, WICBitmapInterpolationModeFant))) - goto CleanupExit; - if (FAILED(IWICBitmapScaler_CopyPixels(wicScaler, &rect, rect.Width * 4, rect.Width * rect.Height * 4, (PBYTE)bitmapBuffer))) - goto CleanupExit; - - success = TRUE; - -CleanupExit: - - if (wicScaler) - IWICBitmapScaler_Release(wicScaler); - - if (bufferDc) - DeleteDC(bufferDc); - - if (screenHdc) - DeleteDC(screenHdc); - - if (wicBitmapSource) - IWICBitmapSource_Release(wicBitmapSource); - - if (wicStream) - IWICStream_Release(wicStream); - - if (wicDecoder) - IWICBitmapDecoder_Release(wicDecoder); - - if (wicFactory) - IWICImagingFactory_Release(wicFactory); - - if (resourceHandle) - FreeResource(resourceHandle); - - if (success) - { - return bitmapHandle; - } - - DeleteObject(bitmapHandle); - return NULL; -} \ No newline at end of file diff --git a/plugins/CommonUtil/resource.h b/plugins/CommonUtil/resource.h deleted file mode 100644 index 5be361921482..000000000000 Binary files a/plugins/CommonUtil/resource.h and /dev/null differ diff --git a/plugins/CommonUtil/resource.rc b/plugins/CommonUtil/resource.rc deleted file mode 100644 index 8fa22757b0cf..000000000000 Binary files a/plugins/CommonUtil/resource.rc and /dev/null differ diff --git a/plugins/DotNetTools/CHANGELOG.txt b/plugins/DotNetTools/CHANGELOG.txt index 371cc7efe9ae..c30876cbdd5c 100644 --- a/plugins/DotNetTools/CHANGELOG.txt +++ b/plugins/DotNetTools/CHANGELOG.txt @@ -1,26 +1,26 @@ -1.6 - * Fixed .NET assembly tab performance issues - * Added extra .NET memory counters to the .NET performance tab - * Added "Show sizes in bytes" checkbox to the .NET performance tab - * Added right-click menu to the .NET assembly tab - * Removed all Win32 functions for querying .NET process performance - -1.5 - * Rewrite of .NET Performance statistics and AppDomain enumeration - -1.4 - * Process Hacker now displays managed stack traces for 32-bit .NET processes on 64-bit Windows - * Fixed inaccurate stack traces when clicking Refresh - * Added AppDomain column for threads in .NET programs - -1.3 - * Improved .NET assembly enumeration timeout handling - -1.2 - * Fixed inaccurate stack traces for certain .NET programs - -1.1 - * Added managed symbol resolution for thread stacks - -1.0 +1.6 + * Fixed .NET assembly tab performance issues + * Added extra .NET memory counters to the .NET performance tab + * Added "Show sizes in bytes" checkbox to the .NET performance tab + * Added right-click menu to the .NET assembly tab + * Removed all Win32 functions for querying .NET process performance + +1.5 + * Rewrite of .NET Performance statistics and AppDomain enumeration + +1.4 + * Process Hacker now displays managed stack traces for 32-bit .NET processes on 64-bit Windows + * Fixed inaccurate stack traces when clicking Refresh + * Added AppDomain column for threads in .NET programs + +1.3 + * Improved .NET assembly enumeration timeout handling + +1.2 + * Fixed inaccurate stack traces for certain .NET programs + +1.1 + * Added managed symbol resolution for thread stacks + +1.0 * Initial release \ No newline at end of file diff --git a/plugins/DotNetTools/DotNetTools.rc b/plugins/DotNetTools/DotNetTools.rc index 7c8acce56e78..0574b642f188 100644 --- a/plugins/DotNetTools/DotNetTools.rc +++ b/plugins/DotNetTools/DotNetTools.rc @@ -1,182 +1,166 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,6,0,0 - PRODUCTVERSION 1,6,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", ".NET tools plugin for Process Hacker" - VALUE "FileVersion", "1.6" - VALUE "InternalName", "ProcessHacker.DotNetTools" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "DotNetTools.dll" - VALUE "ProductName", ".NET tools plugin for Process Hacker" - VALUE "ProductVersion", "1.6" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_PROCDOTNETPERF DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION ".NET performance" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_APPDOMAINS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,246,59 - LTEXT "Categories:",IDC_STATIC,7,72,38,8 - COMBOBOX IDC_CATEGORIES,49,70,204,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_COUNTERS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,88,246,154 - CONTROL "Show sizes in bytes",IDC_DOTNET_PERF_SHOWBYTES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,244,78,10 -END - -IDD_PROCDOTNETASM DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION ".NET assemblies" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL ".NET assemblies",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0x1a,7,7,246,246,WS_EX_CLIENTEDGE -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_PROCDOTNETPERF, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_PROCDOTNETASM, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_PROCDOTNETASM AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCDOTNETPERF AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_ASSEMBLY_MENU MENU -BEGIN - POPUP "CLR" - BEGIN - MENUITEM "Open &file location", ID_CLR_OPENFILELOCATION - MENUITEM "&Copy\aCtrl+C", ID_CLR_COPY - END -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,6,0,0 + PRODUCTVERSION 1,6,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "wj32" + VALUE "FileDescription", ".NET tools plugin for Process Hacker" + VALUE "FileVersion", "1.6" + VALUE "InternalName", "ProcessHacker.DotNetTools" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "DotNetTools.dll" + VALUE "ProductName", ".NET tools plugin for Process Hacker" + VALUE "ProductVersion", "1.6" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROCDOTNETPERF DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION ".NET performance" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_COUNTERS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,3,256,255 +END + +IDD_PROCDOTNETASM DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION ".NET assemblies" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL ".NET assemblies",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0x1a,2,19,256,239,WS_EX_CLIENTEDGE + PUSHBUTTON "Options",IDC_OPTIONS,2,2,50,14 + EDITTEXT IDC_SEARCHEDIT,114,3,143,14,ES_AUTOHSCROLL + PUSHBUTTON "Refresh",IDC_REFRESH,53,2,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PROCDOTNETPERF, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 3 + BOTTOMMARGIN, 258 + END + + IDD_PROCDOTNETASM, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 258 + TOPMARGIN, 3 + BOTTOMMARGIN, 258 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_PROCDOTNETASM AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_PROCDOTNETPERF AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/DotNetTools/DotNetTools.vcxproj b/plugins/DotNetTools/DotNetTools.vcxproj index 1c691037b766..5c635ebbb2ea 100644 --- a/plugins/DotNetTools/DotNetTools.vcxproj +++ b/plugins/DotNetTools/DotNetTools.vcxproj @@ -1,104 +1,104 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {2B3235B0-31E1-44DA-81A1-183F40517E47} - DotNetTools - Win32Proj - DotNetTools - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2B3235B0-31E1-44DA-81A1-183F40517E47} + DotNetTools + Win32Proj + DotNetTools + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + + advapi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + advapi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + advapi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + advapi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/DotNetTools/asmpage.c b/plugins/DotNetTools/asmpage.c index a66d44dbb4c2..a05a18e0bae7 100644 --- a/plugins/DotNetTools/asmpage.c +++ b/plugins/DotNetTools/asmpage.c @@ -1,1314 +1,1687 @@ -/* - * Process Hacker .NET Tools - - * .NET Assemblies property page - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" -#include "clretw.h" -#include -#include - -#define DNATNC_STRUCTURE 0 -#define DNATNC_ID 1 -#define DNATNC_FLAGS 2 -#define DNATNC_PATH 3 -#define DNATNC_NATIVEPATH 4 -#define DNATNC_MAXIMUM 5 - -#define DNA_TYPE_CLR 1 -#define DNA_TYPE_APPDOMAIN 2 -#define DNA_TYPE_ASSEMBLY 3 - -#define UPDATE_MSG (WM_APP + 1) - -typedef struct _DNA_NODE -{ - PH_TREENEW_NODE Node; - - struct _DNA_NODE *Parent; - PPH_LIST Children; - - PH_STRINGREF TextCache[DNATNC_MAXIMUM]; - - ULONG Type; - BOOLEAN IsFakeClr; - - union - { - struct - { - USHORT ClrInstanceID; - PPH_STRING DisplayName; - } Clr; - struct - { - ULONG64 AppDomainID; - PPH_STRING DisplayName; - } AppDomain; - struct - { - ULONG64 AssemblyID; - PPH_STRING FullyQualifiedAssemblyName; - } Assembly; - } u; - - PH_STRINGREF StructureText; - PPH_STRING IdText; - PPH_STRING FlagsText; - PPH_STRING PathText; - PPH_STRING NativePathText; -} DNA_NODE, *PDNA_NODE; - -typedef struct _ASMPAGE_CONTEXT -{ - HWND WindowHandle; - PPH_PROCESS_ITEM ProcessItem; - ULONG ClrVersions; - PDNA_NODE ClrV2Node; - - HWND TnHandle; - PPH_STRING TnErrorMessage; - PPH_LIST NodeList; - PPH_LIST NodeRootList; -} ASMPAGE_CONTEXT, *PASMPAGE_CONTEXT; - -typedef struct _ASMPAGE_QUERY_CONTEXT -{ - HANDLE WindowHandle; - - HANDLE ProcessId; - ULONG ClrVersions; - PDNA_NODE ClrV2Node; - - BOOLEAN TraceClrV2; - ULONG TraceResult; - LONG TraceHandleActive; - TRACEHANDLE TraceHandle; - - PPH_LIST NodeList; - PPH_LIST NodeRootList; -} ASMPAGE_QUERY_CONTEXT, *PASMPAGE_QUERY_CONTEXT; - -typedef struct _FLAG_DEFINITION -{ - PWSTR Name; - ULONG Flag; -} FLAG_DEFINITION, *PFLAG_DEFINITION; - -typedef ULONG (__stdcall *_EnableTraceEx)( - _In_ LPCGUID ProviderId, - _In_opt_ LPCGUID SourceId, - _In_ TRACEHANDLE TraceHandle, - _In_ ULONG IsEnabled, - _In_ UCHAR Level, - _In_ ULONGLONG MatchAnyKeyword, - _In_ ULONGLONG MatchAllKeyword, - _In_ ULONG EnableProperty, - _In_opt_ PEVENT_FILTER_DESCRIPTOR EnableFilterDesc - ); - -VOID DestroyDotNetTraceQuery( - _In_ PASMPAGE_QUERY_CONTEXT Context - ); - -INT_PTR CALLBACK DotNetAsmPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static UNICODE_STRING DotNetLoggerName = RTL_CONSTANT_STRING(L"PhDnLogger"); -static GUID ClrRuntimeProviderGuid = { 0xe13c0d23, 0xccbc, 0x4e12, { 0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4 } }; -static GUID ClrRundownProviderGuid = { 0xa669021c, 0xc450, 0x4609, { 0xa0, 0x35, 0x5a, 0xf5, 0x9a, 0xf4, 0xdf, 0x18 } }; - -static FLAG_DEFINITION AppDomainFlagsMap[] = -{ - { L"Default", 0x1 }, - { L"Executable", 0x2 }, - { L"Shared", 0x4 } -}; - -static FLAG_DEFINITION AssemblyFlagsMap[] = -{ - { L"DomainNeutral", 0x1 }, - { L"Dynamic", 0x2 }, - { L"Native", 0x4 }, - { L"Collectible", 0x8 } -}; - -static FLAG_DEFINITION ModuleFlagsMap[] = -{ - { L"DomainNeutral", 0x1 }, - { L"Native", 0x2 }, - { L"Dynamic", 0x4 }, - { L"Manifest", 0x8 } -}; - -static FLAG_DEFINITION StartupModeMap[] = -{ - { L"ManagedExe", 0x1 }, - { L"HostedCLR", 0x2 }, - { L"IjwDll", 0x4 }, - { L"ComActivated", 0x8 }, - { L"Other", 0x10 } -}; - -static FLAG_DEFINITION StartupFlagsMap[] = -{ - { L"CONCURRENT_GC", 0x1 }, - { L"LOADER_OPTIMIZATION_SINGLE_DOMAIN", 0x2 }, - { L"LOADER_OPTIMIZATION_MULTI_DOMAIN", 0x4 }, - { L"LOADER_SAFEMODE", 0x10 }, - { L"LOADER_SETPREFERENCE", 0x100 }, - { L"SERVER_GC", 0x1000 }, - { L"HOARD_GC_VM", 0x2000 }, - { L"SINGLE_VERSION_HOSTING_INTERFACE", 0x4000 }, - { L"LEGACY_IMPERSONATION", 0x10000 }, - { L"DISABLE_COMMITTHREADSTACK", 0x20000 }, - { L"ALWAYSFLOW_IMPERSONATION", 0x40000 }, - { L"TRIM_GC_COMMIT", 0x80000 }, - { L"ETW", 0x100000 }, - { L"SERVER_BUILD", 0x200000 }, - { L"ARM", 0x400000 } -}; - -VOID AddAsmPageToPropContext( - _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext - ) -{ - PhAddProcessPropPage( - PropContext->PropContext, - PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDOTNETASM), DotNetAsmPageDlgProc, NULL) - ); -} - -PPH_STRING FlagsToString( - _In_ ULONG Flags, - _In_ PFLAG_DEFINITION Map, - _In_ ULONG SizeOfMap - ) -{ - PH_STRING_BUILDER sb; - ULONG i; - - PhInitializeStringBuilder(&sb, 100); - - for (i = 0; i < SizeOfMap / sizeof(FLAG_DEFINITION); i++) - { - if (Flags & Map[i].Flag) - { - PhAppendStringBuilder2(&sb, Map[i].Name); - PhAppendStringBuilder2(&sb, L", "); - } - } - - if (sb.String->Length != 0) - PhRemoveEndStringBuilder(&sb, 2); - - return PhFinalStringBuilderString(&sb); -} - -PDNA_NODE AddNode( - _Inout_ PASMPAGE_QUERY_CONTEXT Context - ) -{ - PDNA_NODE node; - - node = PhAllocate(sizeof(DNA_NODE)); - memset(node, 0, sizeof(DNA_NODE)); - PhInitializeTreeNewNode(&node->Node); - - memset(node->TextCache, 0, sizeof(PH_STRINGREF) * DNATNC_MAXIMUM); - node->Node.TextCache = node->TextCache; - node->Node.TextCacheSize = DNATNC_MAXIMUM; - - node->Children = PhCreateList(1); - - PhAddItemList(Context->NodeList, node); - - return node; -} - -VOID DestroyNode( - _In_ PDNA_NODE Node - ) -{ - PhDereferenceObject(Node->Children); - - if (Node->Type == DNA_TYPE_CLR) - { - if (Node->u.Clr.DisplayName) PhDereferenceObject(Node->u.Clr.DisplayName); - } - else if (Node->Type == DNA_TYPE_APPDOMAIN) - { - if (Node->u.AppDomain.DisplayName) PhDereferenceObject(Node->u.AppDomain.DisplayName); - } - else if (Node->Type == DNA_TYPE_ASSEMBLY) - { - if (Node->u.Assembly.FullyQualifiedAssemblyName) PhDereferenceObject(Node->u.Assembly.FullyQualifiedAssemblyName); - } - - if (Node->IdText) PhDereferenceObject(Node->IdText); - if (Node->FlagsText) PhDereferenceObject(Node->FlagsText); - if (Node->PathText) PhDereferenceObject(Node->PathText); - if (Node->NativePathText) PhDereferenceObject(Node->NativePathText); - - PhFree(Node); -} - -PDNA_NODE AddFakeClrNode( - _In_ PASMPAGE_QUERY_CONTEXT Context, - _In_ PWSTR DisplayName - ) -{ - PDNA_NODE node; - - node = AddNode(Context); - node->Type = DNA_TYPE_CLR; - node->IsFakeClr = TRUE; - node->u.Clr.ClrInstanceID = 0; - node->u.Clr.DisplayName = NULL; - PhInitializeStringRef(&node->StructureText, DisplayName); - - PhAddItemList(Context->NodeRootList, node); - - return node; -} - -PDNA_NODE FindClrNode( - _In_ PASMPAGE_QUERY_CONTEXT Context, - _In_ USHORT ClrInstanceID - ) -{ - ULONG i; - - for (i = 0; i < Context->NodeRootList->Count; i++) - { - PDNA_NODE node = Context->NodeRootList->Items[i]; - - if (!node->IsFakeClr && node->u.Clr.ClrInstanceID == ClrInstanceID) - return node; - } - - return NULL; -} - -PDNA_NODE FindAppDomainNode( - _In_ PDNA_NODE ClrNode, - _In_ ULONG64 AppDomainID - ) -{ - ULONG i; - - for (i = 0; i < ClrNode->Children->Count; i++) - { - PDNA_NODE node = ClrNode->Children->Items[i]; - - if (node->u.AppDomain.AppDomainID == AppDomainID) - return node; - } - - return NULL; -} - -PDNA_NODE FindAssemblyNode( - _In_ PDNA_NODE AppDomainNode, - _In_ ULONG64 AssemblyID - ) -{ - ULONG i; - - for (i = 0; i < AppDomainNode->Children->Count; i++) - { - PDNA_NODE node = AppDomainNode->Children->Items[i]; - - if (node->u.Assembly.AssemblyID == AssemblyID) - return node; - } - - return NULL; -} - -PDNA_NODE FindAssemblyNode2( - _In_ PDNA_NODE ClrNode, - _In_ ULONG64 AssemblyID - ) -{ - ULONG i; - ULONG j; - - for (i = 0; i < ClrNode->Children->Count; i++) - { - PDNA_NODE appDomainNode = ClrNode->Children->Items[i]; - - for (j = 0; j < appDomainNode->Children->Count; j++) - { - PDNA_NODE assemblyNode = appDomainNode->Children->Items[j]; - - if (assemblyNode->u.Assembly.AssemblyID == AssemblyID) - return assemblyNode; - } - } - - return NULL; -} - -static int __cdecl AssemblyNodeNameCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PDNA_NODE node1 = *(PDNA_NODE *)elem1; - PDNA_NODE node2 = *(PDNA_NODE *)elem2; - - return PhCompareStringRef(&node1->StructureText, &node2->StructureText, TRUE); -} - -PDNA_NODE DotNetAsmGetSelectedEntry( - _In_ PASMPAGE_CONTEXT Context - ) -{ - if (Context->NodeList) - { - for (ULONG i = 0; i < Context->NodeList->Count; i++) - { - PDNA_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - { - return node; - } - } - } - - return NULL; -} - -VOID DotNetAsmShowContextMenu( - _In_ PASMPAGE_CONTEXT Context, - _In_ POINT Location - ) -{ - PDNA_NODE node; - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - - if (!(node = DotNetAsmGetSelectedEntry(Context))) - return; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_ASSEMBLY_MENU), 0); - - if (PhIsNullOrEmptyString(node->PathText) || !RtlDoesFileExists_U(node->PathText->Buffer)) - { - PhSetFlagsEMenuItem(menu, ID_CLR_OPENFILELOCATION, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - } - - selectedItem = PhShowEMenu( - menu, - Context->WindowHandle, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - Location.x, - Location.y - ); - - if (selectedItem && selectedItem->Id != -1) - { - switch (selectedItem->Id) - { - case ID_CLR_OPENFILELOCATION: - { - if (!PhIsNullOrEmptyString(node->PathText) && RtlDoesFileExists_U(node->PathText->Buffer)) - { - PhShellExploreFile(Context->WindowHandle, node->PathText->Buffer); - } - } - break; - case ID_CLR_COPY: - { - PPH_STRING text; - - text = PhGetTreeNewText(Context->TnHandle, 0); - PhSetClipboardString(Context->TnHandle, &text->sr); - PhDereferenceObject(text); - } - break; - } - } - - PhDestroyEMenu(menu); -} - -BOOLEAN NTAPI DotNetAsmTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PASMPAGE_CONTEXT context; - PDNA_NODE node; - - context = Context; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - node = (PDNA_NODE)getChildren->Node; - - if (!node) - { - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items; - getChildren->NumberOfChildren = context->NodeRootList->Count; - } - else - { - if (node->Type == DNA_TYPE_APPDOMAIN || node == context->ClrV2Node) - { - // Sort the assemblies. - qsort(node->Children->Items, node->Children->Count, sizeof(PVOID), AssemblyNodeNameCompareFunction); - } - - getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; - getChildren->NumberOfChildren = node->Children->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - node = (PDNA_NODE)isLeaf->Node; - - isLeaf->IsLeaf = node->Children->Count == 0; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - - node = (PDNA_NODE)getCellText->Node; - - switch (getCellText->Id) - { - case DNATNC_STRUCTURE: - getCellText->Text = node->StructureText; - break; - case DNATNC_ID: - getCellText->Text = PhGetStringRef(node->IdText); - break; - case DNATNC_FLAGS: - getCellText->Text = PhGetStringRef(node->FlagsText); - break; - case DNATNC_PATH: - getCellText->Text = PhGetStringRef(node->PathText); - break; - case DNATNC_NATIVEPATH: - getCellText->Text = PhGetStringRef(node->NativePathText); - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - - node = (PDNA_NODE)getCellTooltip->Node; - - if (getCellTooltip->Column->Id != 0 || node->Type != DNA_TYPE_ASSEMBLY) - return FALSE; - - if (!PhIsNullOrEmptyString(node->u.Assembly.FullyQualifiedAssemblyName)) - { - getCellTooltip->Text = node->u.Assembly.FullyQualifiedAssemblyName->sr; - getCellTooltip->Unfolding = FALSE; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->WindowHandle, WM_COMMAND, ID_COPY, 0); - break; - } - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; - - DotNetAsmShowContextMenu(context, mouseEvent->Location); - } - return TRUE; - } - - return FALSE; -} - -ULONG StartDotNetTrace( - _Out_ PTRACEHANDLE SessionHandle, - _Out_ PEVENT_TRACE_PROPERTIES *Properties - ) -{ - ULONG result; - ULONG bufferSize; - PEVENT_TRACE_PROPERTIES properties; - TRACEHANDLE sessionHandle; - - bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + DotNetLoggerName.Length + sizeof(WCHAR); - properties = PhAllocate(bufferSize); - memset(properties, 0, sizeof(EVENT_TRACE_PROPERTIES)); - - properties->Wnode.BufferSize = bufferSize; - properties->Wnode.ClientContext = 2; // System time clock resolution - properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; - properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY; - properties->LogFileNameOffset = 0; - properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); - - result = StartTrace(&sessionHandle, DotNetLoggerName.Buffer, properties); - - if (result == ERROR_SUCCESS) - { - *SessionHandle = sessionHandle; - *Properties = properties; - - return ERROR_SUCCESS; - } - else if (result == ERROR_ALREADY_EXISTS) - { - // Session already exists, so use that. Get the existing session handle. - - result = ControlTrace(0, DotNetLoggerName.Buffer, properties, EVENT_TRACE_CONTROL_QUERY); - - if (result != ERROR_SUCCESS) - { - PhFree(properties); - return result; - } - - *SessionHandle = properties->Wnode.HistoricalContext; - *Properties = properties; - - return ERROR_SUCCESS; - } - else - { - PhFree(properties); - - return result; - } -} - -ULONG NTAPI DotNetBufferCallback( - _In_ PEVENT_TRACE_LOGFILE Buffer - ) -{ - return TRUE; -} - -VOID NTAPI DotNetEventCallback( - _In_ PEVENT_RECORD EventRecord - ) -{ - PASMPAGE_QUERY_CONTEXT context = EventRecord->UserContext; - PEVENT_HEADER eventHeader = &EventRecord->EventHeader; - PEVENT_DESCRIPTOR eventDescriptor = &eventHeader->EventDescriptor; - - if (UlongToHandle(eventHeader->ProcessId) == context->ProcessId) - { - // .NET 4.0+ - - switch (eventDescriptor->Id) - { - case RuntimeInformationDCStart: - { - PRuntimeInformationRundown data = EventRecord->UserData; - PDNA_NODE node; - PPH_STRING startupFlagsString; - PPH_STRING startupModeString; - - // Check for duplicates. - if (FindClrNode(context, data->ClrInstanceID)) - break; - - node = AddNode(context); - node->Type = DNA_TYPE_CLR; - node->u.Clr.ClrInstanceID = data->ClrInstanceID; - node->u.Clr.DisplayName = PhFormatString(L"CLR v%u.%u.%u.%u", data->VMMajorVersion, data->VMMinorVersion, data->VMBuildNumber, data->VMQfeNumber); - node->StructureText = node->u.Clr.DisplayName->sr; - node->IdText = PhFormatString(L"%u", data->ClrInstanceID); - - startupFlagsString = FlagsToString(data->StartupFlags, StartupFlagsMap, sizeof(StartupFlagsMap)); - startupModeString = FlagsToString(data->StartupMode, StartupModeMap, sizeof(StartupModeMap)); - - if (startupFlagsString->Length != 0 && startupModeString->Length != 0) - { - node->FlagsText = PhConcatStrings(3, startupFlagsString->Buffer, L", ", startupModeString->Buffer); - PhDereferenceObject(startupFlagsString); - PhDereferenceObject(startupModeString); - } - else if (startupFlagsString->Length != 0) - { - node->FlagsText = startupFlagsString; - PhDereferenceObject(startupModeString); - } - else if (startupModeString->Length != 0) - { - node->FlagsText = startupModeString; - PhDereferenceObject(startupFlagsString); - } - - if (data->CommandLine[0]) - node->PathText = PhCreateString(data->CommandLine); - - PhAddItemList(context->NodeRootList, node); - } - break; - case AppDomainDCStart_V1: - { - PAppDomainLoadUnloadRundown_V1 data = EventRecord->UserData; - SIZE_T appDomainNameLength; - USHORT clrInstanceID; - PDNA_NODE parentNode; - PDNA_NODE node; - - appDomainNameLength = PhCountStringZ(data->AppDomainName) * sizeof(WCHAR); - clrInstanceID = *(PUSHORT)((PCHAR)data + FIELD_OFFSET(AppDomainLoadUnloadRundown_V1, AppDomainName) + appDomainNameLength + sizeof(WCHAR) + sizeof(ULONG)); - - // Find the CLR node to add the AppDomain node to. - parentNode = FindClrNode(context, clrInstanceID); - - if (parentNode) - { - // Check for duplicates. - if (FindAppDomainNode(parentNode, data->AppDomainID)) - break; - - node = AddNode(context); - node->Type = DNA_TYPE_APPDOMAIN; - node->u.AppDomain.AppDomainID = data->AppDomainID; - node->u.AppDomain.DisplayName = PhConcatStrings2(L"AppDomain: ", data->AppDomainName); - node->StructureText = node->u.AppDomain.DisplayName->sr; - node->IdText = PhFormatString(L"%I64u", data->AppDomainID); - node->FlagsText = FlagsToString(data->AppDomainFlags, AppDomainFlagsMap, sizeof(AppDomainFlagsMap)); - - PhAddItemList(parentNode->Children, node); - } - } - break; - case AssemblyDCStart_V1: - { - PAssemblyLoadUnloadRundown_V1 data = EventRecord->UserData; - SIZE_T fullyQualifiedAssemblyNameLength; - USHORT clrInstanceID; - PDNA_NODE parentNode; - PDNA_NODE node; - PH_STRINGREF remainingPart; - - fullyQualifiedAssemblyNameLength = PhCountStringZ(data->FullyQualifiedAssemblyName) * sizeof(WCHAR); - clrInstanceID = *(PUSHORT)((PCHAR)data + FIELD_OFFSET(AssemblyLoadUnloadRundown_V1, FullyQualifiedAssemblyName) + fullyQualifiedAssemblyNameLength + sizeof(WCHAR)); - - // Find the AppDomain node to add the Assembly node to. - - parentNode = FindClrNode(context, clrInstanceID); - - if (parentNode) - parentNode = FindAppDomainNode(parentNode, data->AppDomainID); - - if (parentNode) - { - // Check for duplicates. - if (FindAssemblyNode(parentNode, data->AssemblyID)) - break; - - node = AddNode(context); - node->Type = DNA_TYPE_ASSEMBLY; - node->u.Assembly.AssemblyID = data->AssemblyID; - node->u.Assembly.FullyQualifiedAssemblyName = PhCreateStringEx(data->FullyQualifiedAssemblyName, fullyQualifiedAssemblyNameLength); - - // Display only the assembly name, not the whole fully qualified name. - if (!PhSplitStringRefAtChar(&node->u.Assembly.FullyQualifiedAssemblyName->sr, ',', &node->StructureText, &remainingPart)) - node->StructureText = node->u.Assembly.FullyQualifiedAssemblyName->sr; - - node->IdText = PhFormatString(L"%I64u", data->AssemblyID); - node->FlagsText = FlagsToString(data->AssemblyFlags, AssemblyFlagsMap, sizeof(AssemblyFlagsMap)); - - PhAddItemList(parentNode->Children, node); - } - } - break; - case ModuleDCStart_V1: - { - PModuleLoadUnloadRundown_V1 data = EventRecord->UserData; - PWSTR moduleILPath; - SIZE_T moduleILPathLength; - PWSTR moduleNativePath; - SIZE_T moduleNativePathLength; - USHORT clrInstanceID; - PDNA_NODE node; - - moduleILPath = data->ModuleILPath; - moduleILPathLength = PhCountStringZ(moduleILPath) * sizeof(WCHAR); - moduleNativePath = (PWSTR)((PCHAR)moduleILPath + moduleILPathLength + sizeof(WCHAR)); - moduleNativePathLength = PhCountStringZ(moduleNativePath) * sizeof(WCHAR); - clrInstanceID = *(PUSHORT)((PCHAR)moduleNativePath + moduleNativePathLength + sizeof(WCHAR)); - - // Find the Assembly node to set the path on. - - node = FindClrNode(context, clrInstanceID); - - if (node) - node = FindAssemblyNode2(node, data->AssemblyID); - - if (node) - { - PhMoveReference(&node->PathText, PhCreateStringEx(moduleILPath, moduleILPathLength)); - - if (moduleNativePathLength != 0) - PhMoveReference(&node->NativePathText, PhCreateStringEx(moduleNativePath, moduleNativePathLength)); - } - } - break; - case DCStartComplete_V1: - { - if (_InterlockedExchange(&context->TraceHandleActive, 0) == 1) - { - CloseTrace(context->TraceHandle); - } - } - break; - } - - // .NET 2.0 - - if (eventDescriptor->Id == 0) - { - switch (eventDescriptor->Opcode) - { - case CLR_MODULEDCSTART_OPCODE: - { - PModuleLoadUnloadRundown_V1 data = EventRecord->UserData; - PWSTR moduleILPath; - SIZE_T moduleILPathLength; - PWSTR moduleNativePath; - SIZE_T moduleNativePathLength; - PDNA_NODE node; - ULONG_PTR indexOfBackslash; - ULONG_PTR indexOfLastDot; - - moduleILPath = data->ModuleILPath; - moduleILPathLength = PhCountStringZ(moduleILPath) * sizeof(WCHAR); - moduleNativePath = (PWSTR)((PCHAR)moduleILPath + moduleILPathLength + sizeof(WCHAR)); - moduleNativePathLength = PhCountStringZ(moduleNativePath) * sizeof(WCHAR); - - if (context->ClrV2Node && (moduleILPathLength != 0 || moduleNativePathLength != 0)) - { - node = AddNode(context); - node->Type = DNA_TYPE_ASSEMBLY; - node->FlagsText = FlagsToString(data->ModuleFlags, ModuleFlagsMap, sizeof(ModuleFlagsMap)); - node->PathText = PhCreateStringEx(moduleILPath, moduleILPathLength); - - if (moduleNativePathLength != 0) - node->NativePathText = PhCreateStringEx(moduleNativePath, moduleNativePathLength); - - // Use the name between the last backslash and the last dot for the structure column text. - // (E.g. C:\...\AcmeSoft.BigLib.dll -> AcmeSoft.BigLib) - - indexOfBackslash = PhFindLastCharInString(node->PathText, 0, '\\'); - indexOfLastDot = PhFindLastCharInString(node->PathText, 0, '.'); - - if (indexOfBackslash != -1) - { - node->StructureText.Buffer = node->PathText->Buffer + indexOfBackslash + 1; - - if (indexOfLastDot != -1 && indexOfLastDot > indexOfBackslash) - { - node->StructureText.Length = (indexOfLastDot - indexOfBackslash - 1) * sizeof(WCHAR); - } - else - { - node->StructureText.Length = node->PathText->Length - indexOfBackslash * sizeof(WCHAR) - sizeof(WCHAR); - } - } - else - { - node->StructureText = node->PathText->sr; - } - - PhAddItemList(context->ClrV2Node->Children, node); - } - } - break; - case CLR_METHODDC_DCSTARTCOMPLETE_OPCODE: - { - if (_InterlockedExchange(&context->TraceHandleActive, 0) == 1) - { - CloseTrace(context->TraceHandle); - } - } - break; - } - } - } -} - -ULONG ProcessDotNetTrace( - _In_ PASMPAGE_QUERY_CONTEXT Context - ) -{ - ULONG result; - TRACEHANDLE traceHandle; - EVENT_TRACE_LOGFILE logFile; - - memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); - logFile.LoggerName = DotNetLoggerName.Buffer; - logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; - logFile.BufferCallback = DotNetBufferCallback; - logFile.EventRecordCallback = DotNetEventCallback; - logFile.Context = Context; - - traceHandle = OpenTrace(&logFile); - - if (traceHandle == INVALID_PROCESSTRACE_HANDLE) - return GetLastError(); - - Context->TraceHandleActive = 1; - Context->TraceHandle = traceHandle; - result = ProcessTrace(&traceHandle, 1, NULL, NULL); - - if (_InterlockedExchange(&Context->TraceHandleActive, 0) == 1) - { - CloseTrace(traceHandle); - } - - return result; -} - -NTSTATUS UpdateDotNetTraceInfoThreadStart( - _In_ PVOID Parameter - ) -{ - static _EnableTraceEx EnableTraceEx_I = NULL; - PASMPAGE_QUERY_CONTEXT context = Parameter; - TRACEHANDLE sessionHandle; - PEVENT_TRACE_PROPERTIES properties; - PGUID guidToEnable; - - if (!EnableTraceEx_I) - EnableTraceEx_I = PhGetModuleProcAddress(L"advapi32.dll", "EnableTraceEx"); - if (!EnableTraceEx_I) - return ERROR_NOT_SUPPORTED; - - context->TraceResult = StartDotNetTrace(&sessionHandle, &properties); - - if (context->TraceResult != 0) - return context->TraceResult; - - if (!context->TraceClrV2) - guidToEnable = &ClrRundownProviderGuid; - else - guidToEnable = &ClrRuntimeProviderGuid; - - EnableTraceEx_I( - guidToEnable, - NULL, - sessionHandle, - 1, - TRACE_LEVEL_INFORMATION, - CLR_LOADER_KEYWORD | CLR_STARTENUMERATION_KEYWORD, - 0, - 0, - NULL - ); - - context->TraceResult = ProcessDotNetTrace(context); - - ControlTrace(sessionHandle, NULL, properties, EVENT_TRACE_CONTROL_STOP); - PhFree(properties); - - return context->TraceResult; -} - -ULONG UpdateDotNetTraceInfoWithTimeout( - _In_ PASMPAGE_QUERY_CONTEXT Context, - _In_ BOOLEAN ClrV2, - _In_opt_ PLARGE_INTEGER Timeout - ) -{ - HANDLE threadHandle; - BOOLEAN timeout = FALSE; - - // ProcessDotNetTrace is not guaranteed to complete within any period of time, because - // the target process might terminate before it writes the DCStartComplete_V1 event. - // If the timeout is reached, the trace handle is closed, forcing ProcessTrace to stop - // processing. - - Context->TraceClrV2 = ClrV2; - Context->TraceResult = 0; - Context->TraceHandleActive = 0; - Context->TraceHandle = 0; - - threadHandle = PhCreateThread(0, UpdateDotNetTraceInfoThreadStart, Context); - - if (NtWaitForSingleObject(threadHandle, FALSE, Timeout) != STATUS_WAIT_0) - { - // Timeout has expired. Stop the trace processing if it's still active. - // BUG: This assumes that the thread is in ProcessTrace. It might still be - // setting up though! - if (_InterlockedExchange(&Context->TraceHandleActive, 0) == 1) - { - CloseTrace(Context->TraceHandle); - timeout = TRUE; - } - - NtWaitForSingleObject(threadHandle, FALSE, NULL); - } - - NtClose(threadHandle); - - if (timeout) - return ERROR_TIMEOUT; - - return Context->TraceResult; -} - -NTSTATUS DotNetTraceQueryThreadStart( - _In_ PVOID Parameter - ) -{ - LARGE_INTEGER timeout; - PASMPAGE_QUERY_CONTEXT context = Parameter; - BOOLEAN timeoutReached = FALSE; - BOOLEAN nonClrNode = FALSE; - ULONG i; - ULONG result = 0; - - if (context->ClrVersions & PH_CLR_VERSION_1_0) - { - AddFakeClrNode(context, L"CLR v1.0.3705"); // what PE displays - } - - if (context->ClrVersions & PH_CLR_VERSION_1_1) - { - AddFakeClrNode(context, L"CLR v1.1.4322"); - } - - timeout.QuadPart = -10 * PH_TIMEOUT_SEC; - - if (context->ClrVersions & PH_CLR_VERSION_2_0) - { - context->ClrV2Node = AddFakeClrNode(context, L"CLR v2.0.50727"); - result = UpdateDotNetTraceInfoWithTimeout(context, TRUE, &timeout); - - if (result == ERROR_TIMEOUT) - { - timeoutReached = TRUE; - result = ERROR_SUCCESS; - } - } - - if (context->ClrVersions & PH_CLR_VERSION_4_ABOVE) - { - result = UpdateDotNetTraceInfoWithTimeout(context, FALSE, &timeout); - - if (result == ERROR_TIMEOUT) - { - timeoutReached = TRUE; - result = ERROR_SUCCESS; - } - } - // If we reached the timeout, check whether we got any data back. - if (timeoutReached) - { - for (i = 0; i < context->NodeList->Count; i++) - { - PDNA_NODE node = context->NodeList->Items[i]; - - if (node->Type != DNA_TYPE_CLR) - { - nonClrNode = TRUE; - break; - } - } - - if (!nonClrNode) - result = ERROR_TIMEOUT; - } - - // If the process properties window has been closed, bail and cleanup. - // IsWindow should be safe from being called on this thread: - // https://blogs.msdn.microsoft.com/oldnewthing/20070717-00/?p=25983 - if (IsWindow(context->WindowHandle)) - { - PostMessage(context->WindowHandle, UPDATE_MSG, result, (LPARAM)context); - } - else - { - DestroyDotNetTraceQuery(context); - } - - return STATUS_SUCCESS; -} - -VOID CreateDotNetTraceQueryThread( - _In_ HWND WindowHandle, - _In_ ULONG ClrVersions, - _In_ HANDLE ProcessId - ) -{ - HANDLE threadHandle; - PASMPAGE_QUERY_CONTEXT context; - - context = PhAllocate(sizeof(ASMPAGE_QUERY_CONTEXT)); - memset(context, 0, sizeof(ASMPAGE_QUERY_CONTEXT)); - - context->WindowHandle = WindowHandle; - context->ClrVersions = ClrVersions; - context->ProcessId = ProcessId; - context->NodeList = PhCreateList(64); - context->NodeRootList = PhCreateList(2); - - if (threadHandle = PhCreateThread(0, DotNetTraceQueryThreadStart, context)) - { - NtClose(threadHandle); - } - else - { - DestroyDotNetTraceQuery(context); - } -} - -VOID DestroyDotNetTraceQuery( - _In_ PASMPAGE_QUERY_CONTEXT Context - ) -{ - if (Context->NodeList) - { - PhClearReference(&Context->NodeList); - } - - if (Context->NodeRootList) - { - PhClearReference(&Context->NodeRootList); - } - - PhFree(Context); -} - -BOOLEAN IsProcessSuspended( - _In_ HANDLE ProcessId - ) -{ - PVOID processes; - PSYSTEM_PROCESS_INFORMATION process; - - if (NT_SUCCESS(PhEnumProcesses(&processes))) - { - if (process = PhFindProcessInformation(processes, ProcessId)) - return PhGetProcessIsSuspended(process); - - PhFree(processes); - } - - return FALSE; -} - -INT_PTR CALLBACK DotNetAsmPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - PASMPAGE_CONTEXT context; - - if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) - { - context = propPageContext->Context; - } - else - { - return FALSE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_STRING settings; - HWND tnHandle; - - context = PhAllocate(sizeof(ASMPAGE_CONTEXT)); - memset(context, 0, sizeof(ASMPAGE_CONTEXT)); - propPageContext->Context = context; - context->WindowHandle = hwndDlg; - context->ProcessItem = processItem; - - context->ClrVersions = 0; - PhGetProcessIsDotNetEx(processItem->ProcessId, NULL, 0, NULL, &context->ClrVersions); - - tnHandle = GetDlgItem(hwndDlg, IDC_LIST); - context->TnHandle = tnHandle; - - TreeNew_SetCallback(tnHandle, DotNetAsmTreeNewCallback, context); - TreeNew_SetExtendedFlags(tnHandle, TN_FLAG_ITEM_DRAG_SELECT, TN_FLAG_ITEM_DRAG_SELECT); - PhSetControlTheme(tnHandle, L"explorer"); - SendMessage(TreeNew_GetTooltips(tnHandle), TTM_SETMAXTIPWIDTH, 0, MAXSHORT); - PhAddTreeNewColumn(tnHandle, DNATNC_STRUCTURE, TRUE, L"Structure", 240, PH_ALIGN_LEFT, -2, 0); - PhAddTreeNewColumn(tnHandle, DNATNC_ID, TRUE, L"ID", 50, PH_ALIGN_RIGHT, 0, DT_RIGHT); - PhAddTreeNewColumn(tnHandle, DNATNC_FLAGS, TRUE, L"Flags", 120, PH_ALIGN_LEFT, 1, 0); - PhAddTreeNewColumn(tnHandle, DNATNC_PATH, TRUE, L"Path", 600, PH_ALIGN_LEFT, 2, 0); // don't use path ellipsis - the user already has the base file name - PhAddTreeNewColumn(tnHandle, DNATNC_NATIVEPATH, TRUE, L"Native image path", 600, PH_ALIGN_LEFT, 3, 0); - - settings = PhGetStringSetting(SETTING_NAME_ASM_TREE_LIST_COLUMNS); - PhCmLoadSettings(tnHandle, &settings->sr); - PhDereferenceObject(settings); - - PhMoveReference(&context->TnErrorMessage, PhCreateString(L"Loading .NET assemblies...")); - TreeNew_SetEmptyText(tnHandle, &context->TnErrorMessage->sr, 0); - - if ( - !IsProcessSuspended(processItem->ProcessId) || - PhShowMessage(hwndDlg, MB_ICONWARNING | MB_YESNO, L".NET assembly enumeration may not work properly because the process is currently suspended. Do you want to continue?") == IDYES - ) - { - CreateDotNetTraceQueryThread( - hwndDlg, - context->ClrVersions, - processItem->ProcessId - ); - } - else - { - PhMoveReference(&context->TnErrorMessage, - PhCreateString(L"Unable to start the event tracing session because the process is suspended.") - ); - TreeNew_SetEmptyText(tnHandle, &context->TnErrorMessage->sr, 0); - InvalidateRect(tnHandle, NULL, FALSE); - } - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_DESTROY: - { - PPH_STRING settings; - ULONG i; - - settings = PhCmSaveSettings(context->TnHandle); - PhSetStringSetting2(SETTING_NAME_ASM_TREE_LIST_COLUMNS, &settings->sr); - PhDereferenceObject(settings); - - if (context->NodeList) - { - for (i = 0; i < context->NodeList->Count; i++) - DestroyNode(context->NodeList->Items[i]); - - PhDereferenceObject(context->NodeList); - } - - if (context->NodeRootList) - PhDereferenceObject(context->NodeRootList); - - PhClearReference(&context->TnErrorMessage); - PhFree(context); - - PhPropPageDlgProcDestroy(hwndDlg); - } - break; - case WM_SHOWWINDOW: - { - PPH_LAYOUT_ITEM dialogItem; - - if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) - { - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); - PhEndPropPageLayout(hwndDlg, propPageContext); - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_COPY: - { - PPH_STRING text; - - text = PhGetTreeNewText(context->TnHandle, 0); - PhSetClipboardString(context->TnHandle, &text->sr); - PhDereferenceObject(text); - } - break; - } - } - break; - case UPDATE_MSG: - { - ULONG result = (ULONG)wParam; - PASMPAGE_QUERY_CONTEXT queryContext = (PASMPAGE_QUERY_CONTEXT)lParam; - - if (result == 0) - { - PhSwapReference(&context->NodeList, queryContext->NodeList); - PhSwapReference(&context->NodeRootList, queryContext->NodeRootList); - - DestroyDotNetTraceQuery(queryContext); - - TreeNew_NodesStructured(context->TnHandle); - } - else - { - PhMoveReference(&context->TnErrorMessage, - PhConcatStrings2(L"Unable to start the event tracing session: ", PhGetStringOrDefault(PhGetWin32Message(result), L"Unknown error")) - ); - TreeNew_SetEmptyText(context->TnHandle, &context->TnErrorMessage->sr, 0); - InvalidateRect(context->TnHandle, NULL, FALSE); - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker .NET Tools - + * .NET Assemblies property page + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2016-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "clretw.h" +#include + +#define DNATNC_STRUCTURE 0 +#define DNATNC_ID 1 +#define DNATNC_FLAGS 2 +#define DNATNC_PATH 3 +#define DNATNC_NATIVEPATH 4 +#define DNATNC_MAXIMUM 5 + +#define DNA_TYPE_CLR 1 +#define DNA_TYPE_APPDOMAIN 2 +#define DNA_TYPE_ASSEMBLY 3 + +#define DN_ASM_UPDATE_MSG (WM_APP + 1) + +typedef struct _DNA_NODE +{ + PH_TREENEW_NODE Node; + + struct _DNA_NODE *Parent; + PPH_LIST Children; + + PH_STRINGREF TextCache[DNATNC_MAXIMUM]; + + ULONG Type; + BOOLEAN IsFakeClr; + + union + { + struct + { + USHORT ClrInstanceID; + ULONG StartupFlags; + PPH_STRING DisplayName; + } Clr; + struct + { + ULONG64 AppDomainID; + ULONG AppDomainFlags; + PPH_STRING DisplayName; + } AppDomain; + struct + { + ULONG64 AssemblyID; + ULONG AssemblyFlags; + PPH_STRING FullyQualifiedAssemblyName; + } Assembly; + } u; + + PH_STRINGREF StructureText; + PPH_STRING IdText; + PPH_STRING FlagsText; + PPH_STRING PathText; + PPH_STRING NativePathText; +} DNA_NODE, *PDNA_NODE; + +typedef struct _ASMPAGE_CONTEXT +{ + HWND WindowHandle; + HWND SearchBoxHandle; + HWND TreeNewHandle; + + PPH_STRING SearchBoxText; + PPH_STRING TreeErrorMessage; + + PPH_PROCESS_ITEM ProcessItem; + PDNA_NODE ClrV2Node; + + union + { + ULONG Flags; + struct + { + ULONG EnableStateHighlighting : 1; + ULONG HideDynamicModules : 1; + ULONG HighlightDynamicModules : 1; + ULONG HideNativeModules : 1; + ULONG HighlightNativeModules : 1; + ULONG Spare : 27; + }; + }; + + PPH_LIST NodeList; + PPH_LIST NodeRootList; + PPH_TN_FILTER_ENTRY TreeFilterEntry; + PH_TN_FILTER_SUPPORT TreeFilterSupport; +} ASMPAGE_CONTEXT, *PASMPAGE_CONTEXT; + +typedef struct _ASMPAGE_QUERY_CONTEXT +{ + HANDLE WindowHandle; + + HANDLE ProcessId; + ULONG ClrVersions; + PDNA_NODE ClrV2Node; + + BOOLEAN TraceClrV2; + ULONG TraceResult; + LONG TraceHandleActive; + TRACEHANDLE TraceHandle; + + PPH_LIST NodeList; + PPH_LIST NodeRootList; +} ASMPAGE_QUERY_CONTEXT, *PASMPAGE_QUERY_CONTEXT; + +typedef struct _FLAG_DEFINITION +{ + PWSTR Name; + ULONG Flag; +} FLAG_DEFINITION, *PFLAG_DEFINITION; + +VOID DestroyDotNetTraceQuery( + _In_ PASMPAGE_QUERY_CONTEXT Context + ); + +INT_PTR CALLBACK DotNetAsmPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static UNICODE_STRING DotNetLoggerName = RTL_CONSTANT_STRING(L"PhDnLogger"); +static GUID ClrRuntimeProviderGuid = { 0xe13c0d23, 0xccbc, 0x4e12, { 0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4 } }; +static GUID ClrRundownProviderGuid = { 0xa669021c, 0xc450, 0x4609, { 0xa0, 0x35, 0x5a, 0xf5, 0x9a, 0xf4, 0xdf, 0x18 } }; + +static FLAG_DEFINITION AppDomainFlagsMap[] = +{ + { L"Default", 0x1 }, + { L"Executable", 0x2 }, + { L"Shared", 0x4 } +}; + +static FLAG_DEFINITION AssemblyFlagsMap[] = +{ + { L"DomainNeutral", 0x1 }, + { L"Dynamic", 0x2 }, + { L"Native", 0x4 }, + { L"Collectible", 0x8 } +}; + +static FLAG_DEFINITION ModuleFlagsMap[] = +{ + { L"DomainNeutral", 0x1 }, + { L"Native", 0x2 }, + { L"Dynamic", 0x4 }, + { L"Manifest", 0x8 } +}; + +static FLAG_DEFINITION StartupModeMap[] = +{ + { L"ManagedExe", 0x1 }, + { L"HostedCLR", 0x2 }, + { L"IjwDll", 0x4 }, + { L"ComActivated", 0x8 }, + { L"Other", 0x10 } +}; + +static FLAG_DEFINITION StartupFlagsMap[] = +{ + { L"CONCURRENT_GC", 0x1 }, + { L"LOADER_OPTIMIZATION_SINGLE_DOMAIN", 0x2 }, + { L"LOADER_OPTIMIZATION_MULTI_DOMAIN", 0x4 }, + { L"LOADER_SAFEMODE", 0x10 }, + { L"LOADER_SETPREFERENCE", 0x100 }, + { L"SERVER_GC", 0x1000 }, + { L"HOARD_GC_VM", 0x2000 }, + { L"SINGLE_VERSION_HOSTING_INTERFACE", 0x4000 }, + { L"LEGACY_IMPERSONATION", 0x10000 }, + { L"DISABLE_COMMITTHREADSTACK", 0x20000 }, + { L"ALWAYSFLOW_IMPERSONATION", 0x40000 }, + { L"TRIM_GC_COMMIT", 0x80000 }, + { L"ETW", 0x100000 }, + { L"SERVER_BUILD", 0x200000 }, + { L"ARM", 0x400000 } +}; + +VOID AddAsmPageToPropContext( + _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext + ) +{ + PhAddProcessPropPage( + PropContext->PropContext, + PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDOTNETASM), DotNetAsmPageDlgProc, NULL) + ); +} + +PPH_STRING FlagsToString( + _In_ ULONG Flags, + _In_ PFLAG_DEFINITION Map, + _In_ ULONG SizeOfMap + ) +{ + PH_STRING_BUILDER sb; + ULONG i; + + PhInitializeStringBuilder(&sb, 100); + + for (i = 0; i < SizeOfMap / sizeof(FLAG_DEFINITION); i++) + { + if (Flags & Map[i].Flag) + { + PhAppendStringBuilder2(&sb, Map[i].Name); + PhAppendStringBuilder2(&sb, L", "); + } + } + + if (sb.String->Length != 0) + PhRemoveEndStringBuilder(&sb, 2); + + return PhFinalStringBuilderString(&sb); +} + +PDNA_NODE AddNode( + _Inout_ PASMPAGE_QUERY_CONTEXT Context + ) +{ + PDNA_NODE node; + + node = PhAllocateZero(sizeof(DNA_NODE)); + PhInitializeTreeNewNode(&node->Node); + + memset(node->TextCache, 0, sizeof(PH_STRINGREF) * DNATNC_MAXIMUM); + node->Node.TextCache = node->TextCache; + node->Node.TextCacheSize = DNATNC_MAXIMUM; + + node->Children = PhCreateList(1); + + PhAddItemList(Context->NodeList, node); + + return node; +} + +VOID DotNetAsmDestroyNode( + _In_ PDNA_NODE Node + ) +{ + if (Node->Children) PhDereferenceObject(Node->Children); + + if (Node->Type == DNA_TYPE_CLR) + { + if (Node->u.Clr.DisplayName) PhDereferenceObject(Node->u.Clr.DisplayName); + } + else if (Node->Type == DNA_TYPE_APPDOMAIN) + { + if (Node->u.AppDomain.DisplayName) PhDereferenceObject(Node->u.AppDomain.DisplayName); + } + else if (Node->Type == DNA_TYPE_ASSEMBLY) + { + if (Node->u.Assembly.FullyQualifiedAssemblyName) PhDereferenceObject(Node->u.Assembly.FullyQualifiedAssemblyName); + } + + if (Node->IdText) PhDereferenceObject(Node->IdText); + if (Node->FlagsText) PhDereferenceObject(Node->FlagsText); + if (Node->PathText) PhDereferenceObject(Node->PathText); + if (Node->NativePathText) PhDereferenceObject(Node->NativePathText); + + PhFree(Node); +} + +PDNA_NODE AddFakeClrNode( + _In_ PASMPAGE_QUERY_CONTEXT Context, + _In_ PWSTR DisplayName + ) +{ + PDNA_NODE node; + + node = AddNode(Context); + node->Type = DNA_TYPE_CLR; + node->IsFakeClr = TRUE; + node->u.Clr.ClrInstanceID = 0; + node->u.Clr.DisplayName = NULL; + PhInitializeStringRef(&node->StructureText, DisplayName); + + PhAddItemList(Context->NodeRootList, node); + + return node; +} + +PDNA_NODE FindClrNode( + _In_ PASMPAGE_QUERY_CONTEXT Context, + _In_ USHORT ClrInstanceID + ) +{ + for (ULONG i = 0; i < Context->NodeRootList->Count; i++) + { + PDNA_NODE node = Context->NodeRootList->Items[i]; + + if (!node->IsFakeClr && node->u.Clr.ClrInstanceID == ClrInstanceID) + return node; + } + + return NULL; +} + +PDNA_NODE FindAppDomainNode( + _In_ PDNA_NODE ClrNode, + _In_ ULONG64 AppDomainID + ) +{ + for (ULONG i = 0; i < ClrNode->Children->Count; i++) + { + PDNA_NODE node = ClrNode->Children->Items[i]; + + if (node->u.AppDomain.AppDomainID == AppDomainID) + return node; + } + + return NULL; +} + +PDNA_NODE FindAssemblyNode( + _In_ PDNA_NODE AppDomainNode, + _In_ ULONG64 AssemblyID + ) +{ + for (ULONG i = 0; i < AppDomainNode->Children->Count; i++) + { + PDNA_NODE node = AppDomainNode->Children->Items[i]; + + if (node->u.Assembly.AssemblyID == AssemblyID) + return node; + } + + return NULL; +} + +PDNA_NODE FindAssemblyNode2( + _In_ PDNA_NODE ClrNode, + _In_ ULONG64 AssemblyID + ) +{ + for (ULONG i = 0; i < ClrNode->Children->Count; i++) + { + PDNA_NODE appDomainNode = ClrNode->Children->Items[i]; + + for (ULONG j = 0; j < appDomainNode->Children->Count; j++) + { + PDNA_NODE assemblyNode = appDomainNode->Children->Items[j]; + + if (assemblyNode->u.Assembly.AssemblyID == AssemblyID) + return assemblyNode; + } + } + + return NULL; +} + +static int __cdecl AssemblyNodeNameCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PDNA_NODE node1 = *(PDNA_NODE *)elem1; + PDNA_NODE node2 = *(PDNA_NODE *)elem2; + + return PhCompareStringRef(&node1->StructureText, &node2->StructureText, TRUE); +} +VOID DotNetAsmExpandAllTreeNodes( + _In_ PASMPAGE_CONTEXT Context, + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PPH_MODULE_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID DotNetAsmDestroyTreeNodes( + _In_ PASMPAGE_CONTEXT Context + ) +{ + if (Context->NodeList) + { + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DotNetAsmDestroyNode(Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeList); + } + + if (Context->NodeRootList) + PhDereferenceObject(Context->NodeRootList); +} + +VOID DotNetAsmClearTreeNodes( + _In_ PASMPAGE_CONTEXT Context + ) +{ + if (Context->NodeList) + { + for (ULONG i = 0; i < Context->NodeList->Count; i++) + DotNetAsmDestroyNode(Context->NodeList->Items[i]); + + PhClearList(Context->NodeList); + } + + if (Context->NodeRootList) + { + PhClearList(Context->NodeRootList); + } + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +PDNA_NODE DotNetAsmGetSelectedTreeNode( + _In_ PASMPAGE_CONTEXT Context + ) +{ + if (Context->NodeList) + { + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PDNA_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + return node; + } + } + } + + return NULL; +} + +VOID DotNetAsmShowContextMenu( + _In_ PASMPAGE_CONTEXT Context, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent + ) +{ + PDNA_NODE node; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + + if (!(node = DotNetAsmGetSelectedTreeNode(Context))) + return; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_CLR_INSPECT, L"&Inspect", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_CLR_OPENFILELOCATION, L"Open &file location", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_CLR_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, ID_CLR_COPY, Context->TreeNewHandle, ContextMenuEvent->Column); + + if (PhIsNullOrEmptyString(node->PathText) || !PhDoesFileExistsWin32(PhGetString(node->PathText))) + { + PhSetFlagsEMenuItem(menu, ID_CLR_INSPECT, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhSetFlagsEMenuItem(menu, ID_CLR_OPENFILELOCATION, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + } + + selectedItem = PhShowEMenu( + menu, + Context->WindowHandle, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + ContextMenuEvent->Location.x, + ContextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + + if (!handled) + { + switch (selectedItem->Id) + { + case ID_CLR_INSPECT: + { + if (!PhIsNullOrEmptyString(node->PathText) && PhDoesFileExistsWin32(PhGetString(node->PathText))) + { + PhShellExecuteUserString( + Context->WindowHandle, + L"ProgramInspectExecutables", + node->PathText->Buffer, + FALSE, + L"Make sure the PE Viewer executable file is present." + ); + } + } + break; + case ID_CLR_OPENFILELOCATION: + { + if (!PhIsNullOrEmptyString(node->PathText) && PhDoesFileExistsWin32(PhGetString(node->PathText))) + { + PhShellExploreFile(Context->WindowHandle, node->PathText->Buffer); + } + } + break; + case ID_CLR_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(Context->TreeNewHandle, 0); + PhSetClipboardString(Context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + } + + PhDestroyEMenu(menu); +} + +BOOLEAN NTAPI DotNetAsmTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PASMPAGE_CONTEXT context = Context; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + PDNA_NODE node = (PDNA_NODE)getChildren->Node; + + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items; + getChildren->NumberOfChildren = context->NodeRootList->Count; + } + else + { + if (node->Type == DNA_TYPE_APPDOMAIN || node == context->ClrV2Node) + { + // Sort the assemblies. + qsort(node->Children->Items, node->Children->Count, sizeof(PVOID), AssemblyNodeNameCompareFunction); + } + + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + PDNA_NODE node = (PDNA_NODE)isLeaf->Node; + + isLeaf->IsLeaf = node->Children->Count == 0; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PDNA_NODE node = (PDNA_NODE)getCellText->Node; + + switch (getCellText->Id) + { + case DNATNC_STRUCTURE: + getCellText->Text = node->StructureText; + break; + case DNATNC_ID: + getCellText->Text = PhGetStringRef(node->IdText); + break; + case DNATNC_FLAGS: + getCellText->Text = PhGetStringRef(node->FlagsText); + break; + case DNATNC_PATH: + getCellText->Text = PhGetStringRef(node->PathText); + break; + case DNATNC_NATIVEPATH: + getCellText->Text = PhGetStringRef(node->NativePathText); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + PDNA_NODE node = (PDNA_NODE)getNodeColor->Node; + + switch (node->Type) + { + case DNA_TYPE_CLR: + case DNA_TYPE_APPDOMAIN: + //getNodeColor->BackColor = PhGetIntegerSetting(L"ColorDotNet"); + break; + case DNA_TYPE_ASSEMBLY: + { + if (context->HighlightDynamicModules && (node->u.Assembly.AssemblyFlags & 0x2) == 0x2) + { + getNodeColor->BackColor = PhGetIntegerSetting(L"ColorPacked"); + } + else if (context->HighlightNativeModules && (node->u.Assembly.AssemblyFlags & 0x4) == 0x4) + { + getNodeColor->BackColor = PhGetIntegerSetting(L"ColorSystemProcesses"); + } + else + { + //getNodeColor->BackColor = PhGetIntegerSetting(L"ColorDotNet"); + } + } + break; + } + + getNodeColor->Flags = TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + PDNA_NODE node = (PDNA_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0 || node->Type != DNA_TYPE_ASSEMBLY) + return FALSE; + + if (!PhIsNullOrEmptyString(node->u.Assembly.FullyQualifiedAssemblyName)) + { + getCellTooltip->Text = node->u.Assembly.FullyQualifiedAssemblyName->sr; + getCellTooltip->Unfolding = FALSE; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->WindowHandle, WM_COMMAND, ID_COPY, 0); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + DotNetAsmShowContextMenu(context, contextMenuEvent); + } + return TRUE; + } + + return FALSE; +} + +VOID DotNetAsmLoadSettingsTreeList( + _Inout_ PASMPAGE_CONTEXT Context + ) +{ + ULONG flags; + PPH_STRING settings; + //PH_INTEGER_PAIR sortSettings; + + flags = PhGetIntegerSetting(SETTING_NAME_ASM_TREE_LIST_FLAGS); + settings = PhGetStringSetting(SETTING_NAME_ASM_TREE_LIST_COLUMNS); + //sortSettings = PhGetIntegerPairSetting(SETTING_NAME_ASM_TREE_LIST_SORT); + + Context->Flags = flags; + PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); + //TreeNew_SetSort(Context->TreeNewHandle, (ULONG)sortSettings.X, (PH_SORT_ORDER)sortSettings.Y); + + PhDereferenceObject(settings); +} + +VOID DotNetAsmSaveSettingsTreeList( + _Inout_ PASMPAGE_CONTEXT Context + ) +{ + PPH_STRING settings; + //PH_INTEGER_PAIR sortSettings; + //ULONG sortColumn; + //PH_SORT_ORDER sortOrder; + + settings = PhCmSaveSettings(Context->TreeNewHandle); + //TreeNew_GetSort(Context->TreeNewHandle, &sortColumn, &sortOrder); + //sortSettings.X = sortColumn; + //sortSettings.Y = sortOrder; + + PhSetIntegerSetting(SETTING_NAME_ASM_TREE_LIST_FLAGS, Context->Flags); + PhSetStringSetting2(SETTING_NAME_ASM_TREE_LIST_COLUMNS, &settings->sr); + //PhSetIntegerPairSetting(SETTING_NAME_ASM_TREE_LIST_SORT, sortSettings); + + PhDereferenceObject(settings); +} + +VOID DotNetAsmSetOptionsTreeList( + _Inout_ PASMPAGE_CONTEXT Context, + _In_ ULONG Options + ) +{ + switch (Options) + { + case DN_ASM_MENU_HIDE_DYNAMIC_OPTION: + Context->HideDynamicModules = !Context->HideDynamicModules; + break; + case DN_ASM_MENU_HIGHLIGHT_DYNAMIC_OPTION: + Context->HighlightDynamicModules = !Context->HighlightDynamicModules; + break; + case DN_ASM_MENU_HIDE_NATIVE_OPTION: + Context->HideNativeModules = !Context->HideNativeModules; + break; + case DN_ASM_MENU_HIGHLIGHT_NATIVE_OPTION: + Context->HighlightNativeModules = !Context->HighlightNativeModules; + break; + } +} + +static ULONG StartDotNetTrace( + _Out_ PTRACEHANDLE SessionHandle, + _Out_ PEVENT_TRACE_PROPERTIES *Properties + ) +{ + ULONG result; + ULONG bufferSize; + PEVENT_TRACE_PROPERTIES properties; + TRACEHANDLE sessionHandle; + + bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + DotNetLoggerName.Length + sizeof(WCHAR); + properties = PhAllocateZero(bufferSize); + + properties->Wnode.BufferSize = bufferSize; + properties->Wnode.ClientContext = 2; // System time clock resolution + properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY; + properties->LogFileNameOffset = 0; + properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + result = StartTrace(&sessionHandle, DotNetLoggerName.Buffer, properties); + + if (result == ERROR_SUCCESS) + { + *SessionHandle = sessionHandle; + *Properties = properties; + + return ERROR_SUCCESS; + } + else if (result == ERROR_ALREADY_EXISTS) + { + // Session already exists, so use that. Get the existing session handle. + + result = ControlTrace(0, DotNetLoggerName.Buffer, properties, EVENT_TRACE_CONTROL_QUERY); + + if (result != ERROR_SUCCESS) + { + PhFree(properties); + return result; + } + + *SessionHandle = properties->Wnode.HistoricalContext; + *Properties = properties; + + return ERROR_SUCCESS; + } + else + { + PhFree(properties); + + return result; + } +} + +static ULONG NTAPI DotNetBufferCallback( + _In_ PEVENT_TRACE_LOGFILE Buffer + ) +{ + return TRUE; +} + +static VOID NTAPI DotNetEventCallback( + _In_ PEVENT_RECORD EventRecord + ) +{ + PASMPAGE_QUERY_CONTEXT context = EventRecord->UserContext; + PEVENT_HEADER eventHeader = &EventRecord->EventHeader; + PEVENT_DESCRIPTOR eventDescriptor = &eventHeader->EventDescriptor; + + if (UlongToHandle(eventHeader->ProcessId) == context->ProcessId) + { + // .NET 4.0+ + + switch (eventDescriptor->Id) + { + case RuntimeInformationDCStart: + { + PRuntimeInformationRundown data = EventRecord->UserData; + PDNA_NODE node; + PPH_STRING startupFlagsString; + PPH_STRING startupModeString; + + // Check for duplicates. + if (FindClrNode(context, data->ClrInstanceID)) + break; + + node = AddNode(context); + node->Type = DNA_TYPE_CLR; + node->u.Clr.ClrInstanceID = data->ClrInstanceID; + node->u.Clr.StartupFlags = data->StartupFlags; + node->u.Clr.DisplayName = PhFormatString(L"CLR v%hu.%hu.%hu.%hu", data->VMMajorVersion, data->VMMinorVersion, data->VMBuildNumber, data->VMQfeNumber); + node->StructureText = node->u.Clr.DisplayName->sr; + node->IdText = PhFormatUInt64(data->ClrInstanceID, FALSE); + + startupFlagsString = FlagsToString(data->StartupFlags, StartupFlagsMap, sizeof(StartupFlagsMap)); + startupModeString = FlagsToString(data->StartupMode, StartupModeMap, sizeof(StartupModeMap)); + + if (startupFlagsString->Length != 0 && startupModeString->Length != 0) + { + node->FlagsText = PhConcatStrings(3, startupFlagsString->Buffer, L", ", startupModeString->Buffer); + PhDereferenceObject(startupFlagsString); + PhDereferenceObject(startupModeString); + } + else if (startupFlagsString->Length != 0) + { + node->FlagsText = startupFlagsString; + PhDereferenceObject(startupModeString); + } + else if (startupModeString->Length != 0) + { + node->FlagsText = startupModeString; + PhDereferenceObject(startupFlagsString); + } + + if (data->CommandLine[0]) + node->PathText = PhCreateString(data->CommandLine); + + PhAddItemList(context->NodeRootList, node); + } + break; + case AppDomainDCStart_V1: + { + PAppDomainLoadUnloadRundown_V1 data = EventRecord->UserData; + SIZE_T appDomainNameLength; + USHORT clrInstanceID; + PDNA_NODE parentNode; + PDNA_NODE node; + + appDomainNameLength = PhCountStringZ(data->AppDomainName) * sizeof(WCHAR); + clrInstanceID = *(PUSHORT)PTR_ADD_OFFSET(data, FIELD_OFFSET(AppDomainLoadUnloadRundown_V1, AppDomainName) + appDomainNameLength + sizeof(WCHAR) + sizeof(ULONG)); + + // Find the CLR node to add the AppDomain node to. + parentNode = FindClrNode(context, clrInstanceID); + + if (parentNode) + { + // Check for duplicates. + if (FindAppDomainNode(parentNode, data->AppDomainID)) + break; + + node = AddNode(context); + node->Type = DNA_TYPE_APPDOMAIN; + node->u.AppDomain.AppDomainID = data->AppDomainID; + node->u.AppDomain.AppDomainFlags = data->AppDomainFlags; + node->u.AppDomain.DisplayName = PhConcatStrings2(L"AppDomain: ", data->AppDomainName); + node->StructureText = node->u.AppDomain.DisplayName->sr; + node->IdText = PhFormatUInt64(data->AppDomainID, FALSE); + node->FlagsText = FlagsToString(data->AppDomainFlags, AppDomainFlagsMap, sizeof(AppDomainFlagsMap)); + + PhAddItemList(parentNode->Children, node); + } + } + break; + case AssemblyDCStart_V1: + { + PAssemblyLoadUnloadRundown_V1 data = EventRecord->UserData; + SIZE_T fullyQualifiedAssemblyNameLength; + USHORT clrInstanceID; + PDNA_NODE parentNode; + PDNA_NODE node; + PH_STRINGREF remainingPart; + + fullyQualifiedAssemblyNameLength = PhCountStringZ(data->FullyQualifiedAssemblyName) * sizeof(WCHAR); + clrInstanceID = *(PUSHORT)PTR_ADD_OFFSET(data, FIELD_OFFSET(AssemblyLoadUnloadRundown_V1, FullyQualifiedAssemblyName) + fullyQualifiedAssemblyNameLength + sizeof(WCHAR)); + + // Find the AppDomain node to add the Assembly node to. + + parentNode = FindClrNode(context, clrInstanceID); + + if (parentNode) + parentNode = FindAppDomainNode(parentNode, data->AppDomainID); + + if (parentNode) + { + // Check for duplicates. + if (FindAssemblyNode(parentNode, data->AssemblyID)) + break; + + node = AddNode(context); + node->Type = DNA_TYPE_ASSEMBLY; + node->u.Assembly.AssemblyID = data->AssemblyID; + node->u.Assembly.AssemblyFlags = data->AssemblyFlags; + node->u.Assembly.FullyQualifiedAssemblyName = PhCreateStringEx(data->FullyQualifiedAssemblyName, fullyQualifiedAssemblyNameLength); + + // Display only the assembly name, not the whole fully qualified name. + if (!PhSplitStringRefAtChar(&node->u.Assembly.FullyQualifiedAssemblyName->sr, ',', &node->StructureText, &remainingPart)) + node->StructureText = node->u.Assembly.FullyQualifiedAssemblyName->sr; + + node->IdText = PhFormatUInt64(data->AssemblyID, FALSE); + node->FlagsText = FlagsToString(data->AssemblyFlags, AssemblyFlagsMap, sizeof(AssemblyFlagsMap)); + + PhAddItemList(parentNode->Children, node); + } + } + break; + case ModuleDCStart_V1: + { + PModuleLoadUnloadRundown_V1 data = EventRecord->UserData; + PWSTR moduleILPath; + SIZE_T moduleILPathLength; + PWSTR moduleNativePath; + SIZE_T moduleNativePathLength; + USHORT clrInstanceID; + PDNA_NODE node; + + moduleILPath = data->ModuleILPath; + moduleILPathLength = PhCountStringZ(moduleILPath) * sizeof(WCHAR); + moduleNativePath = (PWSTR)PTR_ADD_OFFSET(moduleILPath, moduleILPathLength + sizeof(WCHAR)); + moduleNativePathLength = PhCountStringZ(moduleNativePath) * sizeof(WCHAR); + clrInstanceID = *(PUSHORT)PTR_ADD_OFFSET(moduleNativePath, moduleNativePathLength + sizeof(WCHAR)); + + // Find the Assembly node to set the path on. + + node = FindClrNode(context, clrInstanceID); + + if (node) + node = FindAssemblyNode2(node, data->AssemblyID); + + if (node) + { + PhMoveReference(&node->PathText, PhCreateStringEx(moduleILPath, moduleILPathLength)); + + if (moduleNativePathLength != 0) + PhMoveReference(&node->NativePathText, PhCreateStringEx(moduleNativePath, moduleNativePathLength)); + } + } + break; + case DCStartComplete_V1: + { + if (_InterlockedExchange(&context->TraceHandleActive, 0) == 1) + { + CloseTrace(context->TraceHandle); + } + } + break; + } + + // .NET 2.0 + + if (eventDescriptor->Id == 0) + { + switch (eventDescriptor->Opcode) + { + case CLR_MODULEDCSTART_OPCODE: + { + PModuleLoadUnloadRundown_V1 data = EventRecord->UserData; + PWSTR moduleILPath; + SIZE_T moduleILPathLength; + PWSTR moduleNativePath; + SIZE_T moduleNativePathLength; + PDNA_NODE node; + ULONG_PTR indexOfBackslash; + ULONG_PTR indexOfLastDot; + + moduleILPath = data->ModuleILPath; + moduleILPathLength = PhCountStringZ(moduleILPath) * sizeof(WCHAR); + moduleNativePath = (PWSTR)PTR_ADD_OFFSET(moduleILPath, moduleILPathLength + sizeof(WCHAR)); + moduleNativePathLength = PhCountStringZ(moduleNativePath) * sizeof(WCHAR); + + if (context->ClrV2Node && (moduleILPathLength != 0 || moduleNativePathLength != 0)) + { + node = AddNode(context); + node->Type = DNA_TYPE_ASSEMBLY; + node->FlagsText = FlagsToString(data->ModuleFlags, ModuleFlagsMap, sizeof(ModuleFlagsMap)); + node->PathText = PhCreateStringEx(moduleILPath, moduleILPathLength); + + if (moduleNativePathLength != 0) + node->NativePathText = PhCreateStringEx(moduleNativePath, moduleNativePathLength); + + // Use the name between the last backslash and the last dot for the structure column text. + // (E.g. C:\...\AcmeSoft.BigLib.dll -> AcmeSoft.BigLib) + + indexOfBackslash = PhFindLastCharInString(node->PathText, 0, OBJ_NAME_PATH_SEPARATOR); + indexOfLastDot = PhFindLastCharInString(node->PathText, 0, '.'); + + if (indexOfBackslash != -1) + { + node->StructureText.Buffer = node->PathText->Buffer + indexOfBackslash + 1; + + if (indexOfLastDot != -1 && indexOfLastDot > indexOfBackslash) + { + node->StructureText.Length = (indexOfLastDot - indexOfBackslash - 1) * sizeof(WCHAR); + } + else + { + node->StructureText.Length = node->PathText->Length - indexOfBackslash * sizeof(WCHAR) - sizeof(WCHAR); + } + } + else + { + node->StructureText = node->PathText->sr; + } + + PhAddItemList(context->ClrV2Node->Children, node); + } + } + break; + case CLR_METHODDC_DCSTARTCOMPLETE_OPCODE: + { + if (_InterlockedExchange(&context->TraceHandleActive, 0) == 1) + { + CloseTrace(context->TraceHandle); + } + } + break; + } + } + } +} + +static ULONG ProcessDotNetTrace( + _In_ PASMPAGE_QUERY_CONTEXT Context + ) +{ + ULONG result; + TRACEHANDLE traceHandle; + EVENT_TRACE_LOGFILE logFile; + + memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); + logFile.LoggerName = DotNetLoggerName.Buffer; + logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; + logFile.BufferCallback = DotNetBufferCallback; + logFile.EventRecordCallback = DotNetEventCallback; + logFile.Context = Context; + + traceHandle = OpenTrace(&logFile); + + if (traceHandle == INVALID_PROCESSTRACE_HANDLE) + return GetLastError(); + + Context->TraceHandleActive = 1; + Context->TraceHandle = traceHandle; + result = ProcessTrace(&traceHandle, 1, NULL, NULL); + + if (_InterlockedExchange(&Context->TraceHandleActive, 0) == 1) + { + CloseTrace(traceHandle); + } + + return result; +} + +NTSTATUS UpdateDotNetTraceInfoThreadStart( + _In_ PVOID Parameter + ) +{ + PASMPAGE_QUERY_CONTEXT context = Parameter; + TRACEHANDLE sessionHandle; + PEVENT_TRACE_PROPERTIES properties; + PGUID guidToEnable; + + context->TraceResult = StartDotNetTrace(&sessionHandle, &properties); + + if (context->TraceResult != 0) + return context->TraceResult; + + if (context->TraceClrV2) + guidToEnable = &ClrRuntimeProviderGuid; + else + guidToEnable = &ClrRundownProviderGuid; + + EnableTraceEx( + guidToEnable, + NULL, + sessionHandle, + 1, + TRACE_LEVEL_INFORMATION, + CLR_LOADER_KEYWORD | CLR_STARTENUMERATION_KEYWORD, + 0, + 0, + NULL + ); + + context->TraceResult = ProcessDotNetTrace(context); + + ControlTrace(sessionHandle, NULL, properties, EVENT_TRACE_CONTROL_STOP); + PhFree(properties); + + return context->TraceResult; +} + +ULONG UpdateDotNetTraceInfoWithTimeout( + _In_ PASMPAGE_QUERY_CONTEXT Context, + _In_ BOOLEAN ClrV2, + _In_opt_ PLARGE_INTEGER Timeout + ) +{ + HANDLE threadHandle; + BOOLEAN timeout = FALSE; + + // ProcessDotNetTrace is not guaranteed to complete within any period of time, because + // the target process might terminate before it writes the DCStartComplete_V1 event. + // If the timeout is reached, the trace handle is closed, forcing ProcessTrace to stop + // processing. + + Context->TraceClrV2 = ClrV2; + Context->TraceResult = 0; + Context->TraceHandleActive = 0; + Context->TraceHandle = 0; + + if (!NT_SUCCESS(PhCreateThreadEx(&threadHandle, UpdateDotNetTraceInfoThreadStart, Context))) + return ERROR_INVALID_HANDLE; + + if (NtWaitForSingleObject(threadHandle, FALSE, Timeout) != STATUS_WAIT_0) + { + // Timeout has expired. Stop the trace processing if it's still active. + // BUG: This assumes that the thread is in ProcessTrace. It might still be + // setting up though! + if (_InterlockedExchange(&Context->TraceHandleActive, 0) == 1) + { + CloseTrace(Context->TraceHandle); + timeout = TRUE; + } + + NtWaitForSingleObject(threadHandle, FALSE, NULL); + } + + NtClose(threadHandle); + + if (timeout) + return ERROR_TIMEOUT; + + return Context->TraceResult; +} + +NTSTATUS DotNetTraceQueryThreadStart( + _In_ PVOID Parameter + ) +{ + LARGE_INTEGER timeout; + PASMPAGE_QUERY_CONTEXT context = Parameter; + BOOLEAN timeoutReached = FALSE; + BOOLEAN nonClrNode = FALSE; + ULONG i; + ULONG result = 0; + + if (context->ClrVersions & PH_CLR_VERSION_1_0) + { + AddFakeClrNode(context, L"CLR v1.0.3705"); // what PE displays + } + + if (context->ClrVersions & PH_CLR_VERSION_1_1) + { + AddFakeClrNode(context, L"CLR v1.1.4322"); + } + + timeout.QuadPart = -(LONGLONG)UInt32x32To64(10, PH_TIMEOUT_SEC); + + if (context->ClrVersions & PH_CLR_VERSION_2_0) + { + context->ClrV2Node = AddFakeClrNode(context, L"CLR v2.0.50727"); + result = UpdateDotNetTraceInfoWithTimeout(context, TRUE, &timeout); + + if (result == ERROR_TIMEOUT) + { + timeoutReached = TRUE; + result = ERROR_SUCCESS; + } + } + + if ((context->ClrVersions & PH_CLR_VERSION_4_ABOVE) || (context->ClrVersions & PH_CLR_JIT_PRESENT)) // PH_CLR_JIT_PRESENT CoreCLR support. (dmex) + { + result = UpdateDotNetTraceInfoWithTimeout(context, FALSE, &timeout); + + if (result == ERROR_TIMEOUT) + { + timeoutReached = TRUE; + result = ERROR_SUCCESS; + } + } + + // If we reached the timeout, check whether we got any data back. + if (timeoutReached) + { + for (i = 0; i < context->NodeList->Count; i++) + { + PDNA_NODE node = context->NodeList->Items[i]; + + if (node->Type != DNA_TYPE_CLR) + { + nonClrNode = TRUE; + break; + } + } + + if (!nonClrNode) + result = ERROR_TIMEOUT; + } + + if (IsWindow(context->WindowHandle)) + PostMessage(context->WindowHandle, DN_ASM_UPDATE_MSG, result, (LPARAM)context); + else + DestroyDotNetTraceQuery(context); + + return STATUS_SUCCESS; +} + +VOID CreateDotNetTraceQueryThread( + _In_ PASMPAGE_CONTEXT Context, + _In_ HANDLE ProcessId + ) +{ + PASMPAGE_QUERY_CONTEXT context; + + context = PhAllocateZero(sizeof(ASMPAGE_QUERY_CONTEXT)); + context->WindowHandle = Context->WindowHandle; + context->ProcessId = ProcessId; + context->NodeList = PhCreateList(64); + context->NodeRootList = PhCreateList(2); + + PhGetProcessIsDotNetEx( + ProcessId, + NULL, + 0, + NULL, + &context->ClrVersions + ); + + if (!NT_SUCCESS(PhCreateThread2(DotNetTraceQueryThreadStart, context))) + { + DestroyDotNetTraceQuery(context); + } +} + +VOID DestroyDotNetTraceQuery( + _In_ PASMPAGE_QUERY_CONTEXT Context + ) +{ + if (Context->NodeRootList) + { + PhClearList(Context->NodeRootList); + PhDereferenceObject(Context->NodeRootList); + } + + if (Context->NodeList) + { + PhClearList(Context->NodeList); + PhDereferenceObject(Context->NodeList); + } + + PhFree(Context); +} + +BOOLEAN IsProcessSuspended( + _In_ HANDLE ProcessId + ) +{ + PVOID processes; + PSYSTEM_PROCESS_INFORMATION process; + + if (NT_SUCCESS(PhEnumProcesses(&processes))) + { + if (process = PhFindProcessInformation(processes, ProcessId)) + return PhGetProcessIsSuspended(process); + + PhFree(processes); + } + + return FALSE; +} + +VOID DotNetAsmRefreshTraceQuery( + _In_ PASMPAGE_CONTEXT Context + ) +{ + PhMoveReference(&Context->TreeErrorMessage, PhCreateString(L"Loading .NET assemblies...")); + TreeNew_SetEmptyText(Context->TreeNewHandle, &Context->TreeErrorMessage->sr, 0); + TreeNew_NodesStructured(Context->TreeNewHandle); + + if (!IsProcessSuspended(Context->ProcessItem->ProcessId) || PhShowMessage( + Context->WindowHandle, + MB_ICONWARNING | MB_YESNO, + L".NET assembly enumeration may not work properly because the process is currently suspended. Do you want to continue?" + ) == IDYES) + { + CreateDotNetTraceQueryThread(Context, Context->ProcessItem->ProcessId); + } + else + { + PhMoveReference( + &Context->TreeErrorMessage, + PhCreateString(L"Unable to start the event tracing session because the process is suspended.") + ); + TreeNew_SetEmptyText(Context->TreeNewHandle, &Context->TreeErrorMessage->sr, 0); + TreeNew_NodesStructured(Context->TreeNewHandle); + } +} + +BOOLEAN WordMatchStringRef( + _Inout_ PASMPAGE_CONTEXT Context, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = PhGetStringRef(Context->SearchBoxText); + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length != 0) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN WordMatchStringZ( + _Inout_ PASMPAGE_CONTEXT Context, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + + return WordMatchStringRef(Context, &text); +} + +BOOLEAN DotNetAsmTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PASMPAGE_CONTEXT context = Context; + PDNA_NODE node = (PDNA_NODE)Node; + + if (context->HideDynamicModules && node->Type == DNA_TYPE_ASSEMBLY && (node->u.Assembly.AssemblyFlags & 0x2) == 0x2) + return FALSE; + if (context->HideNativeModules && node->Type == DNA_TYPE_ASSEMBLY && (node->u.Assembly.AssemblyFlags & 0x4) == 0x4) + return FALSE; + + if (PhIsNullOrEmptyString(context->SearchBoxText)) + return TRUE; + + if (!PhIsNullOrEmptyString(node->IdText)) + { + if (WordMatchStringRef(context, &node->IdText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(node->FlagsText)) + { + if (WordMatchStringRef(context, &node->FlagsText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(node->PathText)) + { + if (WordMatchStringRef(context, &node->PathText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(node->NativePathText)) + { + if (WordMatchStringRef(context, &node->NativePathText->sr)) + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK DotNetAsmPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PASMPAGE_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + context = propPageContext->Context = PhAllocateZero(sizeof(ASMPAGE_CONTEXT)); + context->WindowHandle = hwndDlg; + context->ProcessItem = processItem; + context->SearchBoxHandle = GetDlgItem(hwndDlg, IDC_SEARCHEDIT); + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhCreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Assemblies (Ctrl+K)"); + + context->NodeList = PhCreateList(64); + context->NodeRootList = PhCreateList(2); + TreeNew_SetCallback(context->TreeNewHandle, DotNetAsmTreeNewCallback, context); + TreeNew_SetExtendedFlags(context->TreeNewHandle, TN_FLAG_ITEM_DRAG_SELECT, TN_FLAG_ITEM_DRAG_SELECT); + PhSetControlTheme(context->TreeNewHandle, L"explorer"); + SendMessage(TreeNew_GetTooltips(context->TreeNewHandle), TTM_SETMAXTIPWIDTH, 0, MAXSHORT); + PhAddTreeNewColumn(context->TreeNewHandle, DNATNC_STRUCTURE, TRUE, L"Structure", 240, PH_ALIGN_LEFT, -2, 0); + PhAddTreeNewColumn(context->TreeNewHandle, DNATNC_ID, FALSE, L"ID", 50, PH_ALIGN_RIGHT, 1, DT_RIGHT); + PhAddTreeNewColumn(context->TreeNewHandle, DNATNC_FLAGS, FALSE, L"Flags", 120, PH_ALIGN_LEFT, 2, 0); + PhAddTreeNewColumn(context->TreeNewHandle, DNATNC_PATH, TRUE, L"File name", 600, PH_ALIGN_LEFT, 3, DT_PATH_ELLIPSIS); + PhAddTreeNewColumn(context->TreeNewHandle, DNATNC_NATIVEPATH, FALSE, L"Native image path", 600, PH_ALIGN_LEFT, 4, DT_PATH_ELLIPSIS); + DotNetAsmLoadSettingsTreeList(context); + + context->SearchBoxText = PhReferenceEmptyString(); + + PhInitializeTreeNewFilterSupport( + &context->TreeFilterSupport, + context->TreeNewHandle, + context->NodeList + ); + + context->TreeFilterEntry = PhAddTreeNewFilter( + &context->TreeFilterSupport, + DotNetAsmTreeFilterCallback, + context + ); + + DotNetAsmRefreshTraceQuery(context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhRemoveTreeNewFilter(&context->TreeFilterSupport, context->TreeFilterEntry); + PhDeleteTreeNewFilterSupport(&context->TreeFilterSupport); + if (context->SearchBoxText) PhDereferenceObject(context->SearchBoxText); + + DotNetAsmSaveSettingsTreeList(context); + DotNetAsmDestroyTreeNodes(context); + + PhDereferenceObject(context->TreeErrorMessage); + PhDereferenceObject(context->SearchBoxText); + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + PPH_LAYOUT_ITEM dialogItem; + + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, context->SearchBoxHandle, dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddPropPageLayoutItem(hwndDlg, context->TreeNewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)context->TreeNewHandle); + return TRUE; + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != context->SearchBoxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchBoxHandle)); + + if (!PhEqualString(context->SearchBoxText, newSearchboxText, FALSE)) + { + DotNetAsmExpandAllTreeNodes(context, TRUE); + + PhSwapReference(&context->SearchBoxText, newSearchboxText); + PhApplyTreeNewFilters(&context->TreeFilterSupport); + TreeNew_NodesStructured(context->TreeNewHandle); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case ID_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; + case IDC_REFRESH: + { + DotNetAsmRefreshTraceQuery(context); + } + break; + case IDC_OPTIONS: + { + RECT rect; + PPH_EMENU menu; + PPH_EMENU_ITEM dynamicItem; + PPH_EMENU_ITEM nativeItem; + PPH_EMENU_ITEM highlightDynamicItem; + PPH_EMENU_ITEM highlightNativeItem; + PPH_EMENU_ITEM selectedItem; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_OPTIONS), &rect); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, dynamicItem = PhCreateEMenuItem(0, DN_ASM_MENU_HIDE_DYNAMIC_OPTION, L"Hide dynamic", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, nativeItem = PhCreateEMenuItem(0, DN_ASM_MENU_HIDE_NATIVE_OPTION, L"Hide native", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, highlightDynamicItem = PhCreateEMenuItem(0, DN_ASM_MENU_HIGHLIGHT_DYNAMIC_OPTION, L"Highlight dynamic", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, highlightNativeItem = PhCreateEMenuItem(0, DN_ASM_MENU_HIGHLIGHT_NATIVE_OPTION, L"Highlight native", NULL, NULL), ULONG_MAX); + + if (context->HideDynamicModules) + dynamicItem->Flags |= PH_EMENU_CHECKED; + if (context->HighlightDynamicModules) + highlightDynamicItem->Flags |= PH_EMENU_CHECKED; + if (context->HideNativeModules) + nativeItem->Flags |= PH_EMENU_CHECKED; + if (context->HighlightNativeModules) + highlightNativeItem->Flags |= PH_EMENU_CHECKED; + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rect.left, + rect.bottom + ); + + if (selectedItem && selectedItem->Id) + { + DotNetAsmSetOptionsTreeList(context, selectedItem->Id); + PhApplyTreeNewFilters(&context->TreeFilterSupport); + TreeNew_NodesStructured(context->TreeNewHandle); + } + + PhDestroyEMenu(menu); + } + break; + } + } + break; + case DN_ASM_UPDATE_MSG: + { + ULONG result = (ULONG)wParam; + PASMPAGE_QUERY_CONTEXT queryContext = (PASMPAGE_QUERY_CONTEXT)lParam; + + if (result == 0) + { + DotNetAsmClearTreeNodes(context); + + if (queryContext->NodeRootList) + { + PhAddItemsList( + context->NodeRootList, + queryContext->NodeRootList->Items, + queryContext->NodeRootList->Count + ); + } + + if (queryContext->NodeList) + { + PhAddItemsList( + context->NodeList, + queryContext->NodeList->Items, + queryContext->NodeList->Count + ); + } + + PhMoveReference(&context->TreeErrorMessage, PhCreateString(L"There are no assemblies to display.")); + TreeNew_SetEmptyText(context->TreeNewHandle, &context->TreeErrorMessage->sr, 0); + + PhApplyTreeNewFilters(&context->TreeFilterSupport); + TreeNew_NodesStructured(context->TreeNewHandle); + } + else + { + PPH_STRING errorMessage = PhGetWin32Message(result); + + PhMoveReference(&context->TreeErrorMessage, PhConcatStrings2( + L"Unable to start the event tracing session: ", + PhGetStringOrDefault(errorMessage, L"Unknown error") + )); + TreeNew_SetEmptyText(context->TreeNewHandle, &context->TreeErrorMessage->sr, 0); + TreeNew_NodesStructured(context->TreeNewHandle); + + PhClearReference(&errorMessage); + } + + DestroyDotNetTraceQuery(queryContext); + } + break; + } + + return FALSE; +} + diff --git a/plugins/DotNetTools/clr/dbgappdomain.h b/plugins/DotNetTools/clr/dbgappdomain.h index e29ffdd764a0..86e59ff21d81 100644 --- a/plugins/DotNetTools/clr/dbgappdomain.h +++ b/plugins/DotNetTools/clr/dbgappdomain.h @@ -1,139 +1,139 @@ -/* - * Process Hacker .NET Tools - - * .NET Process IPC definitions - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the current folder for more information. -//----------------------------------------------------------------------------- -// -// dmex: This header has been highly modified. -// Original: https://github.com/dotnet/coreclr/blob/master/src/debug/inc/dbgappdomain.h - -#ifndef _DBG_APPDOMAIN_H_ -#define _DBG_APPDOMAIN_H_ - -#ifndef _WIN64 -#include -#endif -typedef struct _AppDomainInfo -{ - ULONG Id; // unique identifier - INT NameLengthInBytes; - PWSTR AppDomainName; - PVOID AppDomains; -} AppDomainInfo; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct _AppDomainInfo_Wow64 -{ - ULONG Id; // unique identifier - INT NameLengthInBytes; - ULONG AppDomainName; - ULONG AppDomains; -} AppDomainInfo_Wow64; -#include - -// AppDomain publishing server support: -// Information about all appdomains in the process will be maintained -// in the shared memory block for use by the debugger, etc. -#ifndef _WIN64 -#include -#endif -typedef struct _AppDomainEnumerationIPCBlock -{ - HANDLE Mutex; // lock for serialization while manipulating AppDomain list. - - INT TotalSlots; // Number of slots in AppDomainListElement array - INT NumOfUsedSlots; - INT LastFreedSlot; - INT SizeInBytes; // Size of AppDomainInfo in bytes - - INT ProcessNameLengthInBytes; // We can use psapi!GetModuleFileNameEx to get the module name. - PVOID ProcessName; // This provides an alternative. - - PVOID ListOfAppDomains; - BOOL LockInvalid; -} AppDomainEnumerationIPCBlock; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct _AppDomainEnumerationIPCBlock_Wow64 -{ - ULONG Mutex; // lock for serialization while manipulating AppDomain list. - - INT TotalSlots; // Number of slots in AppDomainListElement array - INT NumOfUsedSlots; - INT LastFreedSlot; - INT SizeInBytes; // Size of AppDomainInfo in bytes - - INT ProcessNameLengthInBytes; // We can use psapi!GetModuleFileNameEx to get the module name. - ULONG ProcessName; // This provides an alternative. - - ULONG ListOfAppDomains; - BOOL LockInvalid; -} AppDomainEnumerationIPCBlock_Wow64; -#include - - -// Enforce the AppDomain IPC block binary layout. -C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, Id) == 0x0); -C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, NameLengthInBytes) == 0x4); -C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, AppDomainName) == 0x8); -C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, AppDomains) == 0xC); - -#if defined(_WIN64) -C_ASSERT(FIELD_OFFSET(AppDomainInfo, Id) == 0x0); -C_ASSERT(FIELD_OFFSET(AppDomainInfo, NameLengthInBytes) == 0x4); -C_ASSERT(FIELD_OFFSET(AppDomainInfo, AppDomainName) == 0x8); -C_ASSERT(FIELD_OFFSET(AppDomainInfo, AppDomains) == 0x10); -#endif - -// Enforce the AppDomain IPC block binary layout. -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, Mutex) == 0x0); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, TotalSlots) == 0x4); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, NumOfUsedSlots) == 0x8); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, LastFreedSlot) == 0xC); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, SizeInBytes) == 0x10); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, ProcessNameLengthInBytes) == 0x14); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, ProcessName) == 0x18); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, ListOfAppDomains) == 0x1C); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, LockInvalid) == 0x20); - -#if defined(_WIN64) -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, Mutex) == 0x0); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, TotalSlots) == 0x8); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, NumOfUsedSlots) == 0xC); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, LastFreedSlot) == 0x10); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, SizeInBytes) == 0x14); -C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, ProcessNameLengthInBytes) == 0x18); -// TODO: Why does Visual Studio have issues with these entries... -//C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, ProcessName) == 0x20); -//C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, ListOfAppDomains) == 0x28); -//C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, LockInvalid) == 0x30); -#endif - +/* + * Process Hacker .NET Tools - + * .NET Process IPC definitions + * + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the current folder for more information. +//----------------------------------------------------------------------------- +// +// dmex: This header has been highly modified. +// Original: https://github.com/dotnet/coreclr/blob/master/src/debug/inc/dbgappdomain.h + +#ifndef _DBG_APPDOMAIN_H_ +#define _DBG_APPDOMAIN_H_ + +#ifndef _WIN64 +#include +#endif +typedef struct _AppDomainInfo +{ + ULONG Id; // unique identifier + INT NameLengthInBytes; + PWSTR AppDomainName; + PVOID AppDomains; +} AppDomainInfo; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct _AppDomainInfo_Wow64 +{ + ULONG Id; // unique identifier + INT NameLengthInBytes; + ULONG AppDomainName; + ULONG AppDomains; +} AppDomainInfo_Wow64; +#include + +// AppDomain publishing server support: +// Information about all appdomains in the process will be maintained +// in the shared memory block for use by the debugger, etc. +#ifndef _WIN64 +#include +#endif +typedef struct _AppDomainEnumerationIPCBlock +{ + HANDLE Mutex; // lock for serialization while manipulating AppDomain list. + + INT TotalSlots; // Number of slots in AppDomainListElement array + INT NumOfUsedSlots; + INT LastFreedSlot; + INT SizeInBytes; // Size of AppDomainInfo in bytes + + INT ProcessNameLengthInBytes; // We can use psapi!GetModuleFileNameEx to get the module name. + PVOID ProcessName; // This provides an alternative. + + PVOID ListOfAppDomains; + BOOL LockInvalid; +} AppDomainEnumerationIPCBlock; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct _AppDomainEnumerationIPCBlock_Wow64 +{ + ULONG Mutex; // lock for serialization while manipulating AppDomain list. + + INT TotalSlots; // Number of slots in AppDomainListElement array + INT NumOfUsedSlots; + INT LastFreedSlot; + INT SizeInBytes; // Size of AppDomainInfo in bytes + + INT ProcessNameLengthInBytes; // We can use psapi!GetModuleFileNameEx to get the module name. + ULONG ProcessName; // This provides an alternative. + + ULONG ListOfAppDomains; + BOOL LockInvalid; +} AppDomainEnumerationIPCBlock_Wow64; +#include + + +// Enforce the AppDomain IPC block binary layout. +C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, Id) == 0x0); +C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, NameLengthInBytes) == 0x4); +C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, AppDomainName) == 0x8); +C_ASSERT(FIELD_OFFSET(AppDomainInfo_Wow64, AppDomains) == 0xC); + +#if defined(_WIN64) +C_ASSERT(FIELD_OFFSET(AppDomainInfo, Id) == 0x0); +C_ASSERT(FIELD_OFFSET(AppDomainInfo, NameLengthInBytes) == 0x4); +C_ASSERT(FIELD_OFFSET(AppDomainInfo, AppDomainName) == 0x8); +C_ASSERT(FIELD_OFFSET(AppDomainInfo, AppDomains) == 0x10); +#endif + +// Enforce the AppDomain IPC block binary layout. +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, Mutex) == 0x0); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, TotalSlots) == 0x4); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, NumOfUsedSlots) == 0x8); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, LastFreedSlot) == 0xC); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, SizeInBytes) == 0x10); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, ProcessNameLengthInBytes) == 0x14); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, ProcessName) == 0x18); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, ListOfAppDomains) == 0x1C); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock_Wow64, LockInvalid) == 0x20); + +#if defined(_WIN64) +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, Mutex) == 0x0); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, TotalSlots) == 0x8); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, NumOfUsedSlots) == 0xC); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, LastFreedSlot) == 0x10); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, SizeInBytes) == 0x14); +C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, ProcessNameLengthInBytes) == 0x18); +// TODO: Why does Visual Studio have issues with these entries... +//C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, ProcessName) == 0x20); +//C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, ListOfAppDomains) == 0x28); +//C_ASSERT(FIELD_OFFSET(AppDomainEnumerationIPCBlock, LockInvalid) == 0x30); +#endif + #endif _DBG_APPDOMAIN_H_ \ No newline at end of file diff --git a/plugins/DotNetTools/clr/ipcenums.h b/plugins/DotNetTools/clr/ipcenums.h index 4f8431abbda4..1d9434111933 100644 --- a/plugins/DotNetTools/clr/ipcenums.h +++ b/plugins/DotNetTools/clr/ipcenums.h @@ -1,77 +1,77 @@ -/* - * Process Hacker .NET Tools - - * .NET Process IPC definitions - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the current folder for more information. -//----------------------------------------------------------------------------- -// IPCEnums.h -// -// Define various enums used by IPCMan. -//----------------------------------------------------------------------------- -// -// dmex: This header has been highly modified. -// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcenums.h - -#ifndef _IPC_ENUMS_H_ -#define _IPC_ENUMS_H_ - -//----------------------------------------------------------------------------- -// Each IPC client for an IPC block has one entry. -//----------------------------------------------------------------------------- -typedef enum _EIPCClient -{ - eIPC_PerfCounters = 0, - - // MAX used for arrays, insert above this. - eIPC_MAX -} EIPCClient; - -//----------------------------------------------------------------------------- -// Each IPC client for a LegacyPrivate block (debugging, perf counters, etc) has one entry. -//----------------------------------------------------------------------------- -typedef enum _ELegacyPrivateIPCClient -{ - eLegacyPrivateIPC_PerfCounters = 0, - eLegacyPrivateIPC_Obsolete_Debugger = 1, - eLegacyPrivateIPC_AppDomain = 2, - eLegacyPrivateIPC_Obsolete_Service = 3, - eLegacyPrivateIPC_Obsolete_ClassDump = 4, - eLegacyPrivateIPC_Obsolete_MiniDump = 5, - eLegacyPrivateIPC_InstancePath = 6, - - // MAX used for arrays, insert above this. - eLegacyPrivateIPC_MAX -} ELegacyPrivateIPCClient; - -//----------------------------------------------------------------------------- -// Each IPC client for a LegacyPublic block has one entry. -//----------------------------------------------------------------------------- -typedef enum _ELegacyPublicIPCClient -{ - eLegacyPublicIPC_PerfCounters = 0, - - // MAX used for arrays, insert above this. - eLegacyPublicIPC_MAX -} ELegacyPublicIPCClient; - +/* + * Process Hacker .NET Tools - + * .NET Process IPC definitions + * + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the current folder for more information. +//----------------------------------------------------------------------------- +// IPCEnums.h +// +// Define various enums used by IPCMan. +//----------------------------------------------------------------------------- +// +// dmex: This header has been highly modified. +// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcenums.h + +#ifndef _IPC_ENUMS_H_ +#define _IPC_ENUMS_H_ + +//----------------------------------------------------------------------------- +// Each IPC client for an IPC block has one entry. +//----------------------------------------------------------------------------- +typedef enum _EIPCClient +{ + eIPC_PerfCounters = 0, + + // MAX used for arrays, insert above this. + eIPC_MAX +} EIPCClient; + +//----------------------------------------------------------------------------- +// Each IPC client for a LegacyPrivate block (debugging, perf counters, etc) has one entry. +//----------------------------------------------------------------------------- +typedef enum _ELegacyPrivateIPCClient +{ + eLegacyPrivateIPC_PerfCounters = 0, + eLegacyPrivateIPC_Obsolete_Debugger = 1, + eLegacyPrivateIPC_AppDomain = 2, + eLegacyPrivateIPC_Obsolete_Service = 3, + eLegacyPrivateIPC_Obsolete_ClassDump = 4, + eLegacyPrivateIPC_Obsolete_MiniDump = 5, + eLegacyPrivateIPC_InstancePath = 6, + + // MAX used for arrays, insert above this. + eLegacyPrivateIPC_MAX +} ELegacyPrivateIPCClient; + +//----------------------------------------------------------------------------- +// Each IPC client for a LegacyPublic block has one entry. +//----------------------------------------------------------------------------- +typedef enum _ELegacyPublicIPCClient +{ + eLegacyPublicIPC_PerfCounters = 0, + + // MAX used for arrays, insert above this. + eLegacyPublicIPC_MAX +} ELegacyPublicIPCClient; + #endif _IPC_ENUMS_H_ \ No newline at end of file diff --git a/plugins/DotNetTools/clr/ipcheader.h b/plugins/DotNetTools/clr/ipcheader.h index ba8318907f61..f62b87ade0db 100644 --- a/plugins/DotNetTools/clr/ipcheader.h +++ b/plugins/DotNetTools/clr/ipcheader.h @@ -1,545 +1,545 @@ -/* - * Process Hacker .NET Tools - - * .NET Process IPC definitions - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the current folder for more information. -//----------------------------------------------------------------------------- -// IPCHeader.h -// -// Define the LegacyPrivate header format for IPC memory mapped files. -//----------------------------------------------------------------------------- -// -// dmex: This header has been highly modified. -// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcheader.h - -#ifndef _IPC_HEADER_H_ -#define _IPC_HEADER_H_ - -#include "perfcounterdefs.h" -#include "ipcenums.h" - -// Current version of the IPC Block -const USHORT VER_IPC_BLOCK = 4; -// Legacy version of the IPC Block -const USHORT VER_LEGACYPRIVATE_IPC_BLOCK = 2; -const USHORT VER_LEGACYPUBLIC_IPC_BLOCK = 3; - -//----------------------------------------------------------------------------- -// Entry in the IPC Directory. Ensure binary compatibility across versions -// if we add (or remove) entries. If we remove an block, the entry should -// be EMPTY_ENTRY_OFFSET -//----------------------------------------------------------------------------- - -const ULONG EMPTY_ENTRY_OFFSET = 0xFFFFFFFF; -const ULONG EMPTY_ENTRY_SIZE = 0; - -typedef struct IPCEntry -{ - ULONG Offset; // offset of the IPC Block from the end of the Full IPC Header - ULONG Size; // size (in bytes) of the block -} IPCEntry; - -// Newer versions of the CLR use Flags field -const USHORT IPC_FLAG_USES_FLAGS = 0x1; -const USHORT IPC_FLAG_INITIALIZED = 0x2; -const USHORT IPC_FLAG_X86 = 0x4; - -// In hindsight, we should have made the offsets be absolute, but we made them -// relative to the end of the FullIPCHeader. -// The problem is that as future versions added new Entries to the directory, -// the header size grew. -// Thus we make IPCEntry::Offset is relative to IPC_ENTRY_OFFSET_BASE, which -// corresponds to sizeof(PrivateIPCHeader) for an v1.0 /v1.1 build. -const ULONG IPC_ENTRY_OFFSET_BASE_X86 = 0x14; -const ULONG IPC_ENTRY_OFFSET_BASE_X64 = 0x0; - - -/****************************************************************************** - * The CLR opens memory mapped files to expose perfcounter values and other - * information to other processes. Historically there have been three memory - * mapped files: the BlockTable, the LegacyPrivateBlock, and the - * LegacyPublicBlock. - * - * BlockTable - The block table was designed to work with multiple runtimes - * running side by side in the same process (SxS in-proc). We have defined - * semantics using interlocked operations that allow a runtime to allocate - * a block from the block table in a thread-safe manner. - * - * LegacyPrivateBlock - The legacy private block was used by older versions - * of the runtime to expose various information to the debugger. The - * legacy private block is not compatible with in-proc SxS, and thus it - * must be removed in the near future. Currently it is being used to expose - * information about AppDomains to the debugger. We will need to keep the - * code that knows how to read the legacy private block as long as we - * continue to support .NET 3.5 SP1. - * - * LegacyPublicBlock - The legacy public block was used by older versions - * of the runtime to expose perfcounter values. The legacy public block is - * not compatible with in-proc SxS, and thus it has been removed. We will - * need to keep the code that knows how to read the legacy public block as - * long as we continue to support .NET 3.5 SP1. - ******************************************************************************/ - -/**************************************** BLOCK TABLE ****************************************/ - -#define IPC_BLOCK_TABLE_SIZE 65536 -#define IPC_BLOCK_SIZE 2048 -#define IPC_NUM_BLOCKS_IN_TABLE 32 - -C_ASSERT(IPC_BLOCK_TABLE_SIZE == IPC_NUM_BLOCKS_IN_TABLE * IPC_BLOCK_SIZE); - -typedef struct _IPCHeader -{ - // Chunk header - volatile LONG Counter; // *** Volatile *** value of 0 is special; means that this block has never been touched before by a writer - ULONG RuntimeId; // value of 0 is special; means that chunk is currently free (runtime ids are always greater than 0) - ULONG Reserved1; - ULONG Reserved2; - - // Standard header - USHORT Version; // version of the IPC Block - USHORT Flags; // flags field - ULONG blockSize; // Size of the entire shared memory block - USHORT BuildYear; // stamp for year built - USHORT BuildNumber; // stamp for Month/Day built - ULONG NumEntries; // Number of entries in the table - - // Directory - IPCEntry EntryTable[eIPC_MAX]; // entry describing each client's block -} IPCHeader; - -#define SXSPUBLIC_IPC_SIZE_NO_PADDING (sizeof(IPCHeader) + sizeof(PerfCounterIPCControlBlock)) -#define SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING (sizeof(IPCHeader) + sizeof(PerfCounterIPCControlBlock_Wow64)) - -#define SXSPUBLIC_IPC_PAD_SIZE (IPC_BLOCK_SIZE - SXSPUBLIC_IPC_SIZE_NO_PADDING) -#define SXSPUBLIC_WOW64_IPC_PAD_SIZE (IPC_BLOCK_SIZE - SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING) - -#ifndef _WIN64 -#include -#endif -typedef struct _IPCControlBlock -{ - // Header - IPCHeader Header; - - // Client blocks - PerfCounterIPCControlBlock PerfIpcBlock; - - // Padding - BYTE Padding[SXSPUBLIC_IPC_PAD_SIZE]; -} IPCControlBlock; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct _IPCControlBlock_Wow64 -{ - // Header - IPCHeader Header; - - // Client blocks - PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; - - // Padding - BYTE Padding[SXSPUBLIC_WOW64_IPC_PAD_SIZE]; -} IPCControlBlock_Wow64; -#include - -C_ASSERT(sizeof(IPCControlBlock) == IPC_BLOCK_SIZE); -C_ASSERT(sizeof(IPCControlBlock_Wow64) == IPC_BLOCK_SIZE); - -#ifndef _WIN64 -#include -#endif -typedef struct IPCControlBlockTable -{ - IPCControlBlock Blocks[IPC_NUM_BLOCKS_IN_TABLE]; -} IPCControlBlockTable; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct IPCControlBlockTable_Wow64 -{ - IPCControlBlock_Wow64 Blocks[IPC_NUM_BLOCKS_IN_TABLE]; -} IPCControlBlockTable_Wow64; -#include - -C_ASSERT(sizeof(IPCControlBlockTable) == IPC_BLOCK_TABLE_SIZE); -C_ASSERT(sizeof(IPCControlBlockTable_Wow64) == IPC_BLOCK_TABLE_SIZE); - - -#ifndef _WIN64 -#include -#endif -typedef struct LegacyPrivateIPCHeader -{ - USHORT Version; // version of the IPC Block - USHORT Flags; // flags field - ULONG BlockSize; // Size of the entire shared memory block - HINSTANCE hInstance; // Instance of module that created this header - USHORT BuildYear; // stamp for year built - USHORT BuildNumber; // stamp for Month/Day built - ULONG NumEntries; // Number of entries in the table -} LegacyPrivateIPCHeader; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct LegacyPrivateIPCHeader_Wow64 -{ - USHORT Version; // version of the IPC Block - USHORT Flags; // flags field - ULONG BlockSize; // Size of the entire shared memory block - ULONG hInstance; // Instance of module that created this header - USHORT BuildYear; // stamp for year built - USHORT BuildNumber; // stamp for Month/Day built - ULONG NumEntries; // Number of entries in the table -} LegacyPrivateIPCHeader_Wow64; -#include - -#ifndef _WIN64 -#include -#endif -typedef struct FullIPCHeaderLegacyPrivate -{ - // Header - LegacyPrivateIPCHeader Header; - - // Directory - IPCEntry EntryTable[eLegacyPrivateIPC_MAX]; // entry describing each client's block -} FullIPCHeaderLegacyPrivate; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct FullIPCHeaderLegacyPrivate_Wow64 -{ - // Header - LegacyPrivateIPCHeader_Wow64 Header; - - // Directory - IPCEntry EntryTable[eLegacyPrivateIPC_MAX]; // entry describing each client's block -} FullIPCHeaderLegacyPrivate_Wow64; -#include - -#ifndef _WIN64 -#include -#endif -typedef struct FullIPCHeaderLegacyPublic -{ - // Header - LegacyPrivateIPCHeader Header; - - // Directory - IPCEntry EntryTable[eLegacyPublicIPC_MAX]; // entry describing each client's block -} FullIPCHeaderLegacyPublic; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct FullIPCHeaderLegacyPublic_Wow64 -{ - // Header - LegacyPrivateIPCHeader_Wow64 Header; - - // Directory - IPCEntry EntryTable[eLegacyPublicIPC_MAX]; // entry describing each client's block -} FullIPCHeaderLegacyPublic_Wow64; -#include - - -//----------------------------------------------------------------------------- -// LegacyPrivate (per process) IPC Block for COM+ apps -//----------------------------------------------------------------------------- -#ifndef _WIN64 -#include -#endif -typedef struct _LegacyPrivateIPCControlBlock -{ - FullIPCHeaderLegacyPrivate FullIPCHeader; - - // Client blocks - PerfCounterIPCControlBlock PerfIpcBlock; // no longer used but kept for compat - AppDomainEnumerationIPCBlock AppDomainBlock; - WCHAR InstancePath[MAX_PATH]; -} LegacyPrivateIPCControlBlock; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct _LegacyPrivateIPCControlBlock_Wow64 -{ - FullIPCHeaderLegacyPrivate_Wow64 FullIPCHeader; - - // Client blocks - PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; // no longer used but kept for compat - AppDomainEnumerationIPCBlock_Wow64 AppDomainBlock; - WCHAR InstancePath[MAX_PATH]; -} LegacyPrivateIPCControlBlock_Wow64; -#include - - -//----------------------------------------------------------------------------- -// LegacyPublic (per process) IPC Block for CLR apps -//----------------------------------------------------------------------------- -#ifndef _WIN64 -#include -#endif -typedef struct LegacyPublicIPCControlBlock -{ - FullIPCHeaderLegacyPublic FullIPCHeaderLegacyPublic; - - // Client blocks - PerfCounterIPCControlBlock PerfIpcBlock; -} LegacyPublicIPCControlBlock; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct _LegacyPublicIPCControlBlock_Wow64 -{ - FullIPCHeaderLegacyPublic_Wow64 FullIPCHeaderLegacyPublic; - - // Client blocks - PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; -} LegacyPublicIPCControlBlock_Wow64; -#include - - -//class IPCHeaderLockHolder -//{ -// LONG Counter; -// BOOL HaveLock; -// IPCHeader & Header; -// -// public: -// -// IPCHeaderLockHolder(IPCHeader & header) : HaveLock(FALSE), Header(header) {} -// -// BOOL TryGetLock() -// { -// _ASSERTE(!HaveLock); -// LONG oldCounter = Header.Counter; -// if ((oldCounter & 1) != 0) -// return FALSE; -// Counter = oldCounter + 1; -// if (InterlockedCompareExchange((LONG *)(&(Header.Counter)), Counter, oldCounter) != oldCounter) -// return FALSE; -// HaveLock = TRUE; -// -// return TRUE; -// } -// -// BOOL TryGetLock(ULONG numRetries) -// { -// ULONG dwSwitchCount = 0; -// -// for (;;) -// { -// if (TryGetLock()) -// return TRUE; -// -// if (numRetries == 0) -// return FALSE; -// -// --numRetries; -// __SwitchToThread(0, ++dwSwitchCount); -// } -// } -// -// void FreeLock() -// { -// _ASSERTE(HaveLock); -// _ASSERTE(Header.Counter == Counter); -// ++Counter; -// Counter = (Counter == 0) ? 2 : Counter; -// Header.Counter = Counter; -// HaveLock = FALSE; -// } -// -// ~IPCHeaderLockHolder() -// { -// if (HaveLock) -// FreeLock(); -// } -//}; -// -//class IPCHeaderReadHelper -//{ -// IPCHeader CachedHeader; -// IPCHeader * pUnreliableHeader; -// BOOL IsOpen; -// -// public: -// -// IPCHeaderReadHelper() : pUnreliableHeader(NULL), IsOpen(FALSE) {} -// -// BOOL TryOpenHeader(IPCHeader * header) -// { -// _ASSERTE(!IsOpen); -// -// pUnreliableHeader = header; -// -// // Read the counter and the runtime ID from the header -// CachedHeader.Counter = pUnreliableHeader->Counter; -// if ((CachedHeader.Counter & 1) != 0) -// return FALSE; -// CachedHeader.RuntimeId = pUnreliableHeader->RuntimeId; -// -// // If runtime ID is 0, then this block is not allocated by -// // a runtime, and thus there is no further work to do -// if (CachedHeader.RuntimeId == 0) -// { -// IsOpen = TRUE; -// return TRUE; -// } -// -// // Read the rest of the values from the header -// CachedHeader.Reserved1 = pUnreliableHeader->Reserved1; -// CachedHeader.Reserved2 = pUnreliableHeader->Reserved2; -// CachedHeader.Version = pUnreliableHeader->Version; -// CachedHeader.Flags = pUnreliableHeader->Flags; -// CachedHeader.blockSize = pUnreliableHeader->blockSize; -// CachedHeader.BuildYear = pUnreliableHeader->BuildYear; -// CachedHeader.BuildNumber = pUnreliableHeader->BuildNumber; -// CachedHeader.numEntries = pUnreliableHeader->numEntries; -// -// // Verify that the header did not change during the read -// LONG counter = pUnreliableHeader->Counter; -// if (CachedHeader.Counter != counter) -// return FALSE; -// -// // Since we know we got a clean read of numEntries, we -// // should be able to assert this with confidence -// if (CachedHeader.numEntries == 0) -// { -// _ASSERTE(!"numEntries from IPCBlock is zero"); -// return FALSE; -// } -// else if (CachedHeader.numEntries > eIPC_MAX) -// { -// _ASSERTE(!"numEntries from IPCBlock is too big"); -// return FALSE; -// } -// -// if (CachedHeader.blockSize == 0) -// { -// _ASSERTE(!"blockSize from IPCBlock is zero"); -// return FALSE; -// } -// else if (CachedHeader.blockSize > IPC_BLOCK_SIZE) -// { -// _ASSERTE(!"blockSize from IPCBlock is too big"); -// return FALSE; -// } -// -// // Copy the table -// for (ULONG i = 0; i < CachedHeader.numEntries; ++i) -// { -// CachedHeader.EntryTable[i].Offset = pUnreliableHeader->EntryTable[i].Offset; -// CachedHeader.EntryTable[i].Size = pUnreliableHeader->EntryTable[i].Size; -// if (i == eIPC_PerfCounters) -// { -// if(!((SIZE_T)CachedHeader.EntryTable[i].Offset < IPC_BLOCK_SIZE) && ((SIZE_T)CachedHeader.EntryTable[i].Offset + CachedHeader.EntryTable[i].Size <= IPC_BLOCK_SIZE)) -// { -// _ASSERTE(!"PerfCounter section offset + size is too large"); -// return FALSE; -// } -// } -// } -// -// // If eIPC_MAX > numEntries, then mark the left over -// // slots in EntryTable as "empty". -// for (ULONG i = CachedHeader.numEntries; i < eIPC_MAX; ++i) -// { -// CachedHeader.EntryTable[i].Offset = EMPTY_ENTRY_OFFSET; -// CachedHeader.EntryTable[i].Size = EMPTY_ENTRY_SIZE; -// } -// -// // Verify again that the header did not change during the read -// counter = pUnreliableHeader->Counter; -// if (CachedHeader.Counter != counter) -// return FALSE; -// -// IsOpen = TRUE; -// return TRUE; -// } -// -// -// BOOL HeaderHasChanged() -// { -// _ASSERTE(IsOpen); -// LONG counter = pUnreliableHeader->Counter; -// return (CachedHeader.Counter != counter) ? TRUE : FALSE; -// } -// -// BOOL IsSentinal() -// { -// _ASSERTE(IsOpen); -// return (CachedHeader.Counter == 0); -// } -// -// -// BOOL UseWow64Structs() -// { -// _ASSERTE(IsOpen); -//#if !defined(_TARGET_X86_) -// return ((CachedHeader.Flags & IPC_FLAG_X86) != 0) ? TRUE : FALSE; -//#else -// return FALSE; -//#endif -// } -// -// void * GetUnreliableSection(EIPCClient eClient) -// { -// if (!IsOpen) -// { -// _ASSERTE(!"IPCHeaderReadHelper is not open"); -// return NULL; -// } -// -// if (eClient < 0 || eClient >= eIPC_MAX) -// { -// _ASSERTE(!"eClient is out of bounds"); -// return NULL; -// } -// -// if (CachedHeader.EntryTable[eClient].Offset == EMPTY_ENTRY_OFFSET) -// { -// _ASSERTE(!"Section is empty"); -// return NULL; -// } -// -// return (BYTE*)pUnreliableHeader + (SIZE_T)CachedHeader.EntryTable[eClient].Offset; -// } -//}; - -#endif // _IPC_HEADER_H_ \ No newline at end of file +/* + * Process Hacker .NET Tools - + * .NET Process IPC definitions + * + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the current folder for more information. +//----------------------------------------------------------------------------- +// IPCHeader.h +// +// Define the LegacyPrivate header format for IPC memory mapped files. +//----------------------------------------------------------------------------- +// +// dmex: This header has been highly modified. +// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcheader.h + +#ifndef _IPC_HEADER_H_ +#define _IPC_HEADER_H_ + +#include "perfcounterdefs.h" +#include "ipcenums.h" + +// Current version of the IPC Block +#define VER_IPC_BLOCK 0x4 +// Legacy version of the IPC Block +#define VER_LEGACYPRIVATE_IPC_BLOCK 0x2 +#define VER_LEGACYPUBLIC_IPC_BLOCK 0x3 + +//----------------------------------------------------------------------------- +// Entry in the IPC Directory. Ensure binary compatibility across versions +// if we add (or remove) entries. If we remove an block, the entry should +// be EMPTY_ENTRY_OFFSET +//----------------------------------------------------------------------------- + +const ULONG EMPTY_ENTRY_OFFSET = 0xFFFFFFFF; +const ULONG EMPTY_ENTRY_SIZE = 0; + +typedef struct IPCEntry +{ + ULONG Offset; // offset of the IPC Block from the end of the Full IPC Header + ULONG Size; // size (in bytes) of the block +} IPCEntry; + +// Newer versions of the CLR use Flags field +#define IPC_FLAG_USES_FLAGS 0x1 +#define IPC_FLAG_INITIALIZED 0x2 +#define IPC_FLAG_X86 0x4 + +// In hindsight, we should have made the offsets be absolute, but we made them +// relative to the end of the FullIPCHeader. +// The problem is that as future versions added new Entries to the directory, +// the header size grew. +// Thus we make IPCEntry::Offset is relative to IPC_ENTRY_OFFSET_BASE, which +// corresponds to sizeof(PrivateIPCHeader) for an v1.0 /v1.1 build. +#define IPC_ENTRY_OFFSET_BASE_X86 0x14 +#define IPC_ENTRY_OFFSET_BASE_X64 0x0 + + +/****************************************************************************** + * The CLR opens memory mapped files to expose perfcounter values and other + * information to other processes. Historically there have been three memory + * mapped files: the BlockTable, the LegacyPrivateBlock, and the + * LegacyPublicBlock. + * + * BlockTable - The block table was designed to work with multiple runtimes + * running side by side in the same process (SxS in-proc). We have defined + * semantics using interlocked operations that allow a runtime to allocate + * a block from the block table in a thread-safe manner. + * + * LegacyPrivateBlock - The legacy private block was used by older versions + * of the runtime to expose various information to the debugger. The + * legacy private block is not compatible with in-proc SxS, and thus it + * must be removed in the near future. Currently it is being used to expose + * information about AppDomains to the debugger. We will need to keep the + * code that knows how to read the legacy private block as long as we + * continue to support .NET 3.5 SP1. + * + * LegacyPublicBlock - The legacy public block was used by older versions + * of the runtime to expose perfcounter values. The legacy public block is + * not compatible with in-proc SxS, and thus it has been removed. We will + * need to keep the code that knows how to read the legacy public block as + * long as we continue to support .NET 3.5 SP1. + ******************************************************************************/ + +/**************************************** BLOCK TABLE ****************************************/ + +#define IPC_BLOCK_TABLE_SIZE 65536 +#define IPC_BLOCK_SIZE 2048 +#define IPC_NUM_BLOCKS_IN_TABLE 32 + +C_ASSERT(IPC_BLOCK_TABLE_SIZE == IPC_NUM_BLOCKS_IN_TABLE * IPC_BLOCK_SIZE); + +typedef struct _IPCHeader +{ + // Chunk header + volatile LONG Counter; // *** Volatile *** value of 0 is special; means that this block has never been touched before by a writer + ULONG RuntimeId; // value of 0 is special; means that chunk is currently free (runtime ids are always greater than 0) + ULONG Reserved1; + ULONG Reserved2; + + // Standard header + USHORT Version; // version of the IPC Block + USHORT Flags; // flags field + ULONG blockSize; // Size of the entire shared memory block + USHORT BuildYear; // stamp for year built + USHORT BuildNumber; // stamp for Month/Day built + ULONG NumEntries; // Number of entries in the table + + // Directory + IPCEntry EntryTable[eIPC_MAX]; // entry describing each client's block +} IPCHeader; + +#define SXSPUBLIC_IPC_SIZE_NO_PADDING (sizeof(IPCHeader) + sizeof(PerfCounterIPCControlBlock)) +#define SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING (sizeof(IPCHeader) + sizeof(PerfCounterIPCControlBlock_Wow64)) + +#define SXSPUBLIC_IPC_PAD_SIZE (IPC_BLOCK_SIZE - SXSPUBLIC_IPC_SIZE_NO_PADDING) +#define SXSPUBLIC_WOW64_IPC_PAD_SIZE (IPC_BLOCK_SIZE - SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING) + +#ifndef _WIN64 +#include +#endif +typedef struct _IPCControlBlock +{ + // Header + IPCHeader Header; + + // Client blocks + PerfCounterIPCControlBlock PerfIpcBlock; + + // Padding + BYTE Padding[SXSPUBLIC_IPC_PAD_SIZE]; +} IPCControlBlock; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct _IPCControlBlock_Wow64 +{ + // Header + IPCHeader Header; + + // Client blocks + PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; + + // Padding + BYTE Padding[SXSPUBLIC_WOW64_IPC_PAD_SIZE]; +} IPCControlBlock_Wow64; +#include + +C_ASSERT(sizeof(IPCControlBlock) == IPC_BLOCK_SIZE); +C_ASSERT(sizeof(IPCControlBlock_Wow64) == IPC_BLOCK_SIZE); + +#ifndef _WIN64 +#include +#endif +typedef struct IPCControlBlockTable +{ + IPCControlBlock Blocks[IPC_NUM_BLOCKS_IN_TABLE]; +} IPCControlBlockTable; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct IPCControlBlockTable_Wow64 +{ + IPCControlBlock_Wow64 Blocks[IPC_NUM_BLOCKS_IN_TABLE]; +} IPCControlBlockTable_Wow64; +#include + +C_ASSERT(sizeof(IPCControlBlockTable) == IPC_BLOCK_TABLE_SIZE); +C_ASSERT(sizeof(IPCControlBlockTable_Wow64) == IPC_BLOCK_TABLE_SIZE); + + +#ifndef _WIN64 +#include +#endif +typedef struct LegacyPrivateIPCHeader +{ + USHORT Version; // version of the IPC Block + USHORT Flags; // flags field + ULONG BlockSize; // Size of the entire shared memory block + HINSTANCE hInstance; // Instance of module that created this header + USHORT BuildYear; // stamp for year built + USHORT BuildNumber; // stamp for Month/Day built + ULONG NumEntries; // Number of entries in the table +} LegacyPrivateIPCHeader; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct LegacyPrivateIPCHeader_Wow64 +{ + USHORT Version; // version of the IPC Block + USHORT Flags; // flags field + ULONG BlockSize; // Size of the entire shared memory block + ULONG hInstance; // Instance of module that created this header + USHORT BuildYear; // stamp for year built + USHORT BuildNumber; // stamp for Month/Day built + ULONG NumEntries; // Number of entries in the table +} LegacyPrivateIPCHeader_Wow64; +#include + +#ifndef _WIN64 +#include +#endif +typedef struct FullIPCHeaderLegacyPrivate +{ + // Header + LegacyPrivateIPCHeader Header; + + // Directory + IPCEntry EntryTable[eLegacyPrivateIPC_MAX]; // entry describing each client's block +} FullIPCHeaderLegacyPrivate; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct FullIPCHeaderLegacyPrivate_Wow64 +{ + // Header + LegacyPrivateIPCHeader_Wow64 Header; + + // Directory + IPCEntry EntryTable[eLegacyPrivateIPC_MAX]; // entry describing each client's block +} FullIPCHeaderLegacyPrivate_Wow64; +#include + +#ifndef _WIN64 +#include +#endif +typedef struct FullIPCHeaderLegacyPublic +{ + // Header + LegacyPrivateIPCHeader Header; + + // Directory + IPCEntry EntryTable[eLegacyPublicIPC_MAX]; // entry describing each client's block +} FullIPCHeaderLegacyPublic; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct FullIPCHeaderLegacyPublic_Wow64 +{ + // Header + LegacyPrivateIPCHeader_Wow64 Header; + + // Directory + IPCEntry EntryTable[eLegacyPublicIPC_MAX]; // entry describing each client's block +} FullIPCHeaderLegacyPublic_Wow64; +#include + + +//----------------------------------------------------------------------------- +// LegacyPrivate (per process) IPC Block for COM+ apps +//----------------------------------------------------------------------------- +#ifndef _WIN64 +#include +#endif +typedef struct _LegacyPrivateIPCControlBlock +{ + FullIPCHeaderLegacyPrivate FullIPCHeader; + + // Client blocks + PerfCounterIPCControlBlock PerfIpcBlock; // no longer used but kept for compat + AppDomainEnumerationIPCBlock AppDomainBlock; + WCHAR InstancePath[MAX_PATH]; +} LegacyPrivateIPCControlBlock; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct _LegacyPrivateIPCControlBlock_Wow64 +{ + FullIPCHeaderLegacyPrivate_Wow64 FullIPCHeader; + + // Client blocks + PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; // no longer used but kept for compat + AppDomainEnumerationIPCBlock_Wow64 AppDomainBlock; + WCHAR InstancePath[MAX_PATH]; +} LegacyPrivateIPCControlBlock_Wow64; +#include + + +//----------------------------------------------------------------------------- +// LegacyPublic (per process) IPC Block for CLR apps +//----------------------------------------------------------------------------- +#ifndef _WIN64 +#include +#endif +typedef struct LegacyPublicIPCControlBlock +{ + FullIPCHeaderLegacyPublic FullIPCHeaderLegacyPublic; + + // Client blocks + PerfCounterIPCControlBlock PerfIpcBlock; +} LegacyPublicIPCControlBlock; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct _LegacyPublicIPCControlBlock_Wow64 +{ + FullIPCHeaderLegacyPublic_Wow64 FullIPCHeaderLegacyPublic; + + // Client blocks + PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; +} LegacyPublicIPCControlBlock_Wow64; +#include + + +//class IPCHeaderLockHolder +//{ +// LONG Counter; +// BOOL HaveLock; +// IPCHeader & Header; +// +// public: +// +// IPCHeaderLockHolder(IPCHeader & header) : HaveLock(FALSE), Header(header) {} +// +// BOOL TryGetLock() +// { +// _ASSERTE(!HaveLock); +// LONG oldCounter = Header.Counter; +// if ((oldCounter & 1) != 0) +// return FALSE; +// Counter = oldCounter + 1; +// if (InterlockedCompareExchange((LONG *)(&(Header.Counter)), Counter, oldCounter) != oldCounter) +// return FALSE; +// HaveLock = TRUE; +// +// return TRUE; +// } +// +// BOOL TryGetLock(ULONG numRetries) +// { +// ULONG dwSwitchCount = 0; +// +// for (;;) +// { +// if (TryGetLock()) +// return TRUE; +// +// if (numRetries == 0) +// return FALSE; +// +// --numRetries; +// __SwitchToThread(0, ++dwSwitchCount); +// } +// } +// +// void FreeLock() +// { +// _ASSERTE(HaveLock); +// _ASSERTE(Header.Counter == Counter); +// ++Counter; +// Counter = (Counter == 0) ? 2 : Counter; +// Header.Counter = Counter; +// HaveLock = FALSE; +// } +// +// ~IPCHeaderLockHolder() +// { +// if (HaveLock) +// FreeLock(); +// } +//}; +// +//class IPCHeaderReadHelper +//{ +// IPCHeader CachedHeader; +// IPCHeader * pUnreliableHeader; +// BOOL IsOpen; +// +// public: +// +// IPCHeaderReadHelper() : pUnreliableHeader(NULL), IsOpen(FALSE) {} +// +// BOOL TryOpenHeader(IPCHeader * header) +// { +// _ASSERTE(!IsOpen); +// +// pUnreliableHeader = header; +// +// // Read the counter and the runtime ID from the header +// CachedHeader.Counter = pUnreliableHeader->Counter; +// if ((CachedHeader.Counter & 1) != 0) +// return FALSE; +// CachedHeader.RuntimeId = pUnreliableHeader->RuntimeId; +// +// // If runtime ID is 0, then this block is not allocated by +// // a runtime, and thus there is no further work to do +// if (CachedHeader.RuntimeId == 0) +// { +// IsOpen = TRUE; +// return TRUE; +// } +// +// // Read the rest of the values from the header +// CachedHeader.Reserved1 = pUnreliableHeader->Reserved1; +// CachedHeader.Reserved2 = pUnreliableHeader->Reserved2; +// CachedHeader.Version = pUnreliableHeader->Version; +// CachedHeader.Flags = pUnreliableHeader->Flags; +// CachedHeader.blockSize = pUnreliableHeader->blockSize; +// CachedHeader.BuildYear = pUnreliableHeader->BuildYear; +// CachedHeader.BuildNumber = pUnreliableHeader->BuildNumber; +// CachedHeader.numEntries = pUnreliableHeader->numEntries; +// +// // Verify that the header did not change during the read +// LONG counter = pUnreliableHeader->Counter; +// if (CachedHeader.Counter != counter) +// return FALSE; +// +// // Since we know we got a clean read of numEntries, we +// // should be able to assert this with confidence +// if (CachedHeader.numEntries == 0) +// { +// _ASSERTE(!"numEntries from IPCBlock is zero"); +// return FALSE; +// } +// else if (CachedHeader.numEntries > eIPC_MAX) +// { +// _ASSERTE(!"numEntries from IPCBlock is too big"); +// return FALSE; +// } +// +// if (CachedHeader.blockSize == 0) +// { +// _ASSERTE(!"blockSize from IPCBlock is zero"); +// return FALSE; +// } +// else if (CachedHeader.blockSize > IPC_BLOCK_SIZE) +// { +// _ASSERTE(!"blockSize from IPCBlock is too big"); +// return FALSE; +// } +// +// // Copy the table +// for (ULONG i = 0; i < CachedHeader.numEntries; ++i) +// { +// CachedHeader.EntryTable[i].Offset = pUnreliableHeader->EntryTable[i].Offset; +// CachedHeader.EntryTable[i].Size = pUnreliableHeader->EntryTable[i].Size; +// if (i == eIPC_PerfCounters) +// { +// if(!((SIZE_T)CachedHeader.EntryTable[i].Offset < IPC_BLOCK_SIZE) && ((SIZE_T)CachedHeader.EntryTable[i].Offset + CachedHeader.EntryTable[i].Size <= IPC_BLOCK_SIZE)) +// { +// _ASSERTE(!"PerfCounter section offset + size is too large"); +// return FALSE; +// } +// } +// } +// +// // If eIPC_MAX > numEntries, then mark the left over +// // slots in EntryTable as "empty". +// for (ULONG i = CachedHeader.numEntries; i < eIPC_MAX; ++i) +// { +// CachedHeader.EntryTable[i].Offset = EMPTY_ENTRY_OFFSET; +// CachedHeader.EntryTable[i].Size = EMPTY_ENTRY_SIZE; +// } +// +// // Verify again that the header did not change during the read +// counter = pUnreliableHeader->Counter; +// if (CachedHeader.Counter != counter) +// return FALSE; +// +// IsOpen = TRUE; +// return TRUE; +// } +// +// +// BOOL HeaderHasChanged() +// { +// _ASSERTE(IsOpen); +// LONG counter = pUnreliableHeader->Counter; +// return (CachedHeader.Counter != counter) ? TRUE : FALSE; +// } +// +// BOOL IsSentinal() +// { +// _ASSERTE(IsOpen); +// return (CachedHeader.Counter == 0); +// } +// +// +// BOOL UseWow64Structs() +// { +// _ASSERTE(IsOpen); +//#if !defined(_TARGET_X86_) +// return ((CachedHeader.Flags & IPC_FLAG_X86) != 0) ? TRUE : FALSE; +//#else +// return FALSE; +//#endif +// } +// +// void * GetUnreliableSection(EIPCClient eClient) +// { +// if (!IsOpen) +// { +// _ASSERTE(!"IPCHeaderReadHelper is not open"); +// return NULL; +// } +// +// if (eClient < 0 || eClient >= eIPC_MAX) +// { +// _ASSERTE(!"eClient is out of bounds"); +// return NULL; +// } +// +// if (CachedHeader.EntryTable[eClient].Offset == EMPTY_ENTRY_OFFSET) +// { +// _ASSERTE(!"Section is empty"); +// return NULL; +// } +// +// return (BYTE*)pUnreliableHeader + (SIZE_T)CachedHeader.EntryTable[eClient].Offset; +// } +//}; + +#endif // _IPC_HEADER_H_ diff --git a/plugins/DotNetTools/clr/ipcshared.h b/plugins/DotNetTools/clr/ipcshared.h index 25d67e851bf9..2650f1377479 100644 --- a/plugins/DotNetTools/clr/ipcshared.h +++ b/plugins/DotNetTools/clr/ipcshared.h @@ -1,57 +1,57 @@ -/* - * Process Hacker .NET Tools - - * .NET Process IPC definitions - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the current folder for more information. -//----------------------------------------------------------------------------- -// IPCShared.h -// -// Shared LegacyPrivate utility functions for IPC operations. -//----------------------------------------------------------------------------- -// -// dmex: This header has been highly modified. -// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcshared.h - -#ifndef _IPC_SHARED_H_ -#define _IPC_SHARED_H_ - -// This is the name of the file backed session's name on the LS (debuggee) -// Name of the LegacyPrivate (per-process) block. %lu resolved to a PID -#define CorLegacyPrivateIPCBlock L"Cor_Private_IPCBlock_%lu" -#define CorLegacyPrivateIPCBlockTempV4 L"Cor_Private_IPCBlock_v4_%lu" -#define CorLegacyPublicIPCBlock L"Cor_Public_IPCBlock_%lu" -#define CorSxSPublicIPCBlock L"Cor_SxSPublic_IPCBlock_%lu" -#define CorSxSBoundaryDescriptor L"Cor_CLR_IPCBlock_%lu" -#define CorSxSWriterPrivateNamespacePrefix L"Cor_CLR_WRITER" -#define CorSxSReaderPrivateNamespacePrefix L"Cor_CLR_READER" -#define CorSxSVistaPublicIPCBlock L"Cor_SxSPublic_IPCBlock" - -#define CorLegacyPrivateIPCBlock_RS L"CLR_PRIVATE_RS_IPCBlock_%lu" -#define CorLegacyPrivateIPCBlock_RSTempV4 L"CLR_PRIVATE_RS_IPCBlock_v4_%lu" -#define CorLegacyPublicIPCBlock_RS L"CLR_PUBLIC_IPCBlock_%lu" -#define CorSxSPublicIPCBlock_RS L"CLR_SXSPUBLIC_IPCBlock_%lu" - -#define CorSxSPublicInstanceName L"%s_p%lu_r%lu" -#define CorSxSPublicInstanceNameWhidbey L"%s_p%lu" - +/* + * Process Hacker .NET Tools - + * .NET Process IPC definitions + * + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the current folder for more information. +//----------------------------------------------------------------------------- +// IPCShared.h +// +// Shared LegacyPrivate utility functions for IPC operations. +//----------------------------------------------------------------------------- +// +// dmex: This header has been highly modified. +// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcshared.h + +#ifndef _IPC_SHARED_H_ +#define _IPC_SHARED_H_ + +// This is the name of the file backed session's name on the LS (debuggee) +// Name of the LegacyPrivate (per-process) block. %lu resolved to a PID +#define CorLegacyPrivateIPCBlock L"Cor_Private_IPCBlock_%lu" +#define CorLegacyPrivateIPCBlockTempV4 L"Cor_Private_IPCBlock_v4_%lu" +#define CorLegacyPublicIPCBlock L"Cor_Public_IPCBlock_%lu" +#define CorSxSPublicIPCBlock L"Cor_SxSPublic_IPCBlock_%lu" +#define CorSxSBoundaryDescriptor L"Cor_CLR_IPCBlock_%lu" +#define CorSxSWriterPrivateNamespacePrefix L"Cor_CLR_WRITER" +#define CorSxSReaderPrivateNamespacePrefix L"Cor_CLR_READER" +#define CorSxSVistaPublicIPCBlock L"Cor_SxSPublic_IPCBlock" + +#define CorLegacyPrivateIPCBlock_RS L"CLR_PRIVATE_RS_IPCBlock_%lu" +#define CorLegacyPrivateIPCBlock_RSTempV4 L"CLR_PRIVATE_RS_IPCBlock_v4_%lu" +#define CorLegacyPublicIPCBlock_RS L"CLR_PUBLIC_IPCBlock_%lu" +#define CorSxSPublicIPCBlock_RS L"CLR_SXSPUBLIC_IPCBlock_%lu" + +#define CorSxSPublicInstanceName L"%s_p%lu_r%lu" +#define CorSxSPublicInstanceNameWhidbey L"%s_p%lu" + #endif _IPC_SHARED_H_ \ No newline at end of file diff --git a/plugins/DotNetTools/clr/perfcounterdefs.h b/plugins/DotNetTools/clr/perfcounterdefs.h index bba5e237bfd2..f3249dfc6580 100644 --- a/plugins/DotNetTools/clr/perfcounterdefs.h +++ b/plugins/DotNetTools/clr/perfcounterdefs.h @@ -1,333 +1,333 @@ -/* - * Process Hacker .NET Tools - - * .NET Process IPC definitions - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the current folder for more information. -//----------------------------------------------------------------------------- -// PerfCounterDefs.h -// -// Internal Interface for CLR to use Performance counters. -//----------------------------------------------------------------------------- -// -// dmex: This header has been highly modified. -// Original: https://github.com/dotnet/coreclr/blob/master/src/inc/perfcounterdefs.h - -#ifndef _PERF_COUNTERS_H_ -#define _PERF_COUNTERS_H_ - -//----------------------------------------------------------------------------- -// Name of global IPC block -#define SHARED_PERF_IPC_NAME "SharedPerfIPCBlock" - -//----------------------------------------------------------------------------- -// Attributes for the IPC block -#define PERF_ATTR_ON 0x0001 // Are we even updating any counters? -#define PERF_ATTR_GLOBAL 0x0002 // Is this a global or private block? - -//............................................................................. -// Tri Counter. Support for the common trio of counters (Total, Current, and Instantaneous). -typedef struct _TRICOUNT -{ - ULONG Current; // Current, has +, - - ULONG Total; // Total, has only + -} TRICOUNT; - -//............................................................................. -// Interlocked Tri Counter. Support for the common trio of counters (Total, Current, and Instantaneous). -typedef struct _TRICOUNT_IL -{ - ULONG Current; // Current, has +, - - ULONG Total; // Total, has only + -} TRICOUNT_IL; - - -//............................................................................. -// Dual Counter. Support for the (Total and Instantaneous (rate)). Helpful in cases -// where the current value is always same as the total value. ie. the counter is never -// decremented. -//............................................................................. -typedef struct _DUALCOUNT -{ - ULONG Total; -} DUALCOUNT; - -//----------------------------------------------------------------------------- -// Format for the Perf Counter IPC Block -// IPC block is broken up into sections. This marks it easier to marshall -// into different perfmon objects -// -//............................................................................. -// Naming convention (by prefix): -// c - Raw count of something. -// cb- count of bytes -// time - time value. -// depth - stack depth -//----------------------------------------------------------------------------- - -#define MAX_TRACKED_GENS 3 // number of generations we track - - -#ifndef _WIN64 -#include -#endif -typedef struct _Perf_GC -{ - size_t cGenCollections[MAX_TRACKED_GENS]; // count of collects per gen - size_t cbPromotedMem[MAX_TRACKED_GENS - 1]; // count of promoted memory - size_t cbPromotedFinalizationMem; // count of memory promoted due to finalization - size_t cProcessID; // process ID - size_t cGenHeapSize[MAX_TRACKED_GENS]; // size of heaps per gen - size_t cTotalCommittedBytes; // total number of committed bytes. - size_t cTotalReservedBytes; // bytes reserved via VirtualAlloc - size_t cLrgObjSize; // size of Large Object Heap - size_t cSurviveFinalize; // count of instances surviving from finalizing - size_t cHandles; // count of GC handles - size_t cbAlloc; // bytes allocated - size_t cbLargeAlloc; // bytes allocated for Large Objects - size_t cInducedGCs; // number of explicit GCs - ULONG timeInGC; // Time in GC - ULONG timeInGCBase; // must follow time in GC counter - size_t cPinnedObj; // # of Pinned Objects - size_t cSinkBlocks; // # of sink blocks -} Perf_GC; -#ifndef _WIN64 -#include -#endif - -// Perf_GC_Wow64 mimics in a 64 bit process, the layout of Perf_GC in a 32 bit process -// It does this by replacing all size_t by ULONG -#include -typedef struct _Perf_GC_Wow64 -{ - ULONG cGenCollections[MAX_TRACKED_GENS]; // count of collects per gen - ULONG cbPromotedMem[MAX_TRACKED_GENS - 1]; // count of promoted memory - ULONG cbPromotedFinalizationMem; // count of memory promoted due to finalization - ULONG cProcessID; // process ID - ULONG cGenHeapSize[MAX_TRACKED_GENS]; // size of heaps per gen - ULONG cTotalCommittedBytes; // total number of committed bytes. - ULONG cTotalReservedBytes; // bytes reserved via VirtualAlloc - ULONG cLrgObjSize; // size of Large Object Heap - ULONG cSurviveFinalize; // count of instances surviving from finalizing - ULONG cHandles; // count of GC handles - ULONG cbAlloc; // bytes allocated - ULONG cbLargeAlloc; // bytes allocated for Large Objects - ULONG cInducedGCs; // number of explicit GCs - ULONG timeInGC; // Time in GC - ULONG timeInGCBase; // must follow time in GC counter - ULONG cPinnedObj; // # of Pinned Objects - ULONG cSinkBlocks; // # of sink blocks -} Perf_GC_Wow64; -#include - - - -#ifndef _WIN64 -#include -#endif -typedef struct Perf_Loading -{ - TRICOUNT cClassesLoaded; - TRICOUNT_IL cAppDomains; // Current # of AppDomains - TRICOUNT cAssemblies; // Current # of Assemblies - UNALIGNED LONGLONG timeLoading; // % time loading - ULONG cAsmSearchLen; // Avg search length for assemblies - DUALCOUNT cLoadFailures; // Classes Failed to load - size_t cbLoaderHeapSize; // Total size of heap used by the loader - DUALCOUNT cAppDomainsUnloaded; // Rate at which app domains are unloaded -} Perf_Loading; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct Perf_Loading_Wow64 -{ - TRICOUNT cClassesLoaded; - TRICOUNT_IL cAppDomains; // Current # of AppDomains - TRICOUNT cAssemblies; // Current # of Assemblies - UNALIGNED LONGLONG timeLoading; // % time loading - ULONG cAsmSearchLen; // Avg search length for assemblies - DUALCOUNT cLoadFailures; // Classes Failed to load - ULONG cbLoaderHeapSize; // Total size of heap used by the loader - DUALCOUNT cAppDomainsUnloaded; // Rate at which app domains are unloaded -} Perf_Loading_Wow64; -#include - - -#ifndef _WIN64 -#include -#endif -typedef struct Perf_Jit -{ - ULONG cMethodsJitted; // number of methods jitted - TRICOUNT cbILJitted; // IL jitted stats - //DUALCOUNT cbPitched; // Total bytes pitched - ULONG cJitFailures; // # of standard Jit failures - ULONG timeInJit; // Time in JIT since last sample - ULONG timeInJitBase; // Time in JIT base counter -} Perf_Jit; -#ifndef _WIN64 -#include -#endif - -#ifndef _WIN64 -#include -#endif -typedef struct Perf_Excep -{ - DUALCOUNT cThrown; // Number of Exceptions thrown - ULONG cFiltersExecuted; // Number of Filters executed - ULONG cFinallysExecuted; // Number of Finallys executed - ULONG cThrowToCatchStackDepth; // Delta from throw to catch site on stack -} Perf_Excep; -#ifndef _WIN64 -#include -#endif - -#ifndef _WIN64 -#include -#endif -typedef struct Perf_Interop -{ - ULONG cCCW; // Number of CCWs - ULONG cStubs; // Number of stubs - ULONG cMarshalling; // # of time marshalling args and return values. - ULONG cTLBImports; // Number of tlbs we import - ULONG cTLBExports; // Number of tlbs we export -} Perf_Interop; -#ifndef _WIN64 -#include -#endif - -#ifndef _WIN64 -#include -#endif -typedef struct Perf_LocksAndThreads -{ - // Locks - DUALCOUNT cContention; // # of times in AwareLock::EnterEpilogue() - TRICOUNT cQueueLength; // Lenght of queue - // Threads - ULONG cCurrentThreadsLogical; // Number (created - destroyed) of logical threads - ULONG cCurrentThreadsPhysical; // Number (created - destroyed) of OS threads - TRICOUNT cRecognizedThreads; // # of Threads execute in runtime's control -} Perf_LocksAndThreads; -#ifndef _WIN64 -#include -#endif - - -// IMPORTANT!!!!!!!: The first two fields in the struct have to be together -// and be the first two fields in the struct. The managed code in ChannelServices.cs -// depends on this. -#ifndef _WIN64 -#include -#endif -typedef struct Perf_Contexts -{ - // Contexts & Remoting - DUALCOUNT cRemoteCalls; // # of remote calls - ULONG cChannels; // Number of current channels - ULONG cProxies; // Number of context proxies. - ULONG cClasses; // # of Context-bound classes - ULONG cObjAlloc; // # of context bound objects allocated - ULONG cContexts; // The current number of contexts. -} Perf_Contexts; -#ifndef _WIN64 -#include -#endif - - -#ifndef _WIN64 -#include -#endif -typedef struct Perf_Security -{ - ULONG cTotalRTChecks; // Total runtime checks - UNALIGNED LONGLONG timeAuthorize; // % time authenticating - ULONG cLinkChecks; // link time checks - ULONG timeRTchecks; // % time in Runtime checks - ULONG timeRTchecksBase; // % time in Runtime checks base counter - ULONG stackWalkDepth; // depth of stack for security checks -} Perf_Security; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct Perf_Security_Wow64 -{ - ULONG cTotalRTChecks; // Total runtime checks - UNALIGNED LONGLONG timeAuthorize; // % time authenticating - ULONG cLinkChecks; // link time checks - ULONG timeRTchecks; // % time in Runtime checks - ULONG timeRTchecksBase; // % time in Runtime checks base counter - ULONG stackWalkDepth; // depth of stack for security checks -} Perf_Security_Wow64; -#include - - -#ifndef _WIN64 -#include -#endif -typedef struct _PerfCounterIPCControlBlock -{ - // Versioning info - USHORT Bytes; // size of this entire block - USHORT Attributes; // attributes for this block - - // Counter Sections - Perf_GC GC; - Perf_Contexts Context; - Perf_Interop Interop; - Perf_Loading Loading; - Perf_Excep Excep; - Perf_LocksAndThreads LocksAndThreads; - Perf_Jit Jit; - Perf_Security Security; -} PerfCounterIPCControlBlock; -#ifndef _WIN64 -#include -#endif - -#include -typedef struct _PerfCounterIPCControlBlock_Wow64 -{ - // Versioning info - USHORT Bytes; // size of this entire block - USHORT Attributes; // attributes for this block - - // Counter Sections - Perf_GC_Wow64 GC; - Perf_Contexts Context; - Perf_Interop Interop; - Perf_Loading_Wow64 Loading; - Perf_Excep Excep; - Perf_LocksAndThreads LocksAndThreads; - Perf_Jit Jit; - Perf_Security_Wow64 Security; -} PerfCounterIPCControlBlock_Wow64; -#include - - -#endif _PERF_COUNTERS_H_ \ No newline at end of file +/* + * Process Hacker .NET Tools - + * .NET Process IPC definitions + * + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the current folder for more information. +//----------------------------------------------------------------------------- +// PerfCounterDefs.h +// +// Internal Interface for CLR to use Performance counters. +//----------------------------------------------------------------------------- +// +// dmex: This header has been highly modified. +// Original: https://github.com/dotnet/coreclr/blob/master/src/inc/perfcounterdefs.h + +#ifndef _PERF_COUNTERS_H_ +#define _PERF_COUNTERS_H_ + +//----------------------------------------------------------------------------- +// Name of global IPC block +#define SHARED_PERF_IPC_NAME "SharedPerfIPCBlock" + +//----------------------------------------------------------------------------- +// Attributes for the IPC block +#define PERF_ATTR_ON 0x0001 // Are we even updating any counters? +#define PERF_ATTR_GLOBAL 0x0002 // Is this a global or private block? + +//............................................................................. +// Tri Counter. Support for the common trio of counters (Total, Current, and Instantaneous). +typedef struct _TRICOUNT +{ + ULONG Current; // Current, has +, - + ULONG Total; // Total, has only + +} TRICOUNT; + +//............................................................................. +// Interlocked Tri Counter. Support for the common trio of counters (Total, Current, and Instantaneous). +typedef struct _TRICOUNT_IL +{ + ULONG Current; // Current, has +, - + ULONG Total; // Total, has only + +} TRICOUNT_IL; + + +//............................................................................. +// Dual Counter. Support for the (Total and Instantaneous (rate)). Helpful in cases +// where the current value is always same as the total value. ie. the counter is never +// decremented. +//............................................................................. +typedef struct _DUALCOUNT +{ + ULONG Total; +} DUALCOUNT; + +//----------------------------------------------------------------------------- +// Format for the Perf Counter IPC Block +// IPC block is broken up into sections. This marks it easier to marshall +// into different perfmon objects +// +//............................................................................. +// Naming convention (by prefix): +// c - Raw count of something. +// cb- count of bytes +// time - time value. +// depth - stack depth +//----------------------------------------------------------------------------- + +#define MAX_TRACKED_GENS 3 // number of generations we track + + +#ifndef _WIN64 +#include +#endif +typedef struct _Perf_GC +{ + size_t cGenCollections[MAX_TRACKED_GENS]; // count of collects per gen + size_t cbPromotedMem[MAX_TRACKED_GENS - 1]; // count of promoted memory + size_t cbPromotedFinalizationMem; // count of memory promoted due to finalization + size_t cProcessID; // process ID + size_t cGenHeapSize[MAX_TRACKED_GENS]; // size of heaps per gen + size_t cTotalCommittedBytes; // total number of committed bytes. + size_t cTotalReservedBytes; // bytes reserved via VirtualAlloc + size_t cLrgObjSize; // size of Large Object Heap + size_t cSurviveFinalize; // count of instances surviving from finalizing + size_t cHandles; // count of GC handles + size_t cbAlloc; // bytes allocated + size_t cbLargeAlloc; // bytes allocated for Large Objects + size_t cInducedGCs; // number of explicit GCs + ULONG timeInGC; // Time in GC + ULONG timeInGCBase; // must follow time in GC counter + size_t cPinnedObj; // # of Pinned Objects + size_t cSinkBlocks; // # of sink blocks +} Perf_GC; +#ifndef _WIN64 +#include +#endif + +// Perf_GC_Wow64 mimics in a 64 bit process, the layout of Perf_GC in a 32 bit process +// It does this by replacing all size_t by ULONG +#include +typedef struct _Perf_GC_Wow64 +{ + ULONG cGenCollections[MAX_TRACKED_GENS]; // count of collects per gen + ULONG cbPromotedMem[MAX_TRACKED_GENS - 1]; // count of promoted memory + ULONG cbPromotedFinalizationMem; // count of memory promoted due to finalization + ULONG cProcessID; // process ID + ULONG cGenHeapSize[MAX_TRACKED_GENS]; // size of heaps per gen + ULONG cTotalCommittedBytes; // total number of committed bytes. + ULONG cTotalReservedBytes; // bytes reserved via VirtualAlloc + ULONG cLrgObjSize; // size of Large Object Heap + ULONG cSurviveFinalize; // count of instances surviving from finalizing + ULONG cHandles; // count of GC handles + ULONG cbAlloc; // bytes allocated + ULONG cbLargeAlloc; // bytes allocated for Large Objects + ULONG cInducedGCs; // number of explicit GCs + ULONG timeInGC; // Time in GC + ULONG timeInGCBase; // must follow time in GC counter + ULONG cPinnedObj; // # of Pinned Objects + ULONG cSinkBlocks; // # of sink blocks +} Perf_GC_Wow64; +#include + + + +#ifndef _WIN64 +#include +#endif +typedef struct Perf_Loading +{ + TRICOUNT cClassesLoaded; + TRICOUNT_IL cAppDomains; // Current # of AppDomains + TRICOUNT cAssemblies; // Current # of Assemblies + UNALIGNED LONGLONG timeLoading; // % time loading + ULONG cAsmSearchLen; // Avg search length for assemblies + DUALCOUNT cLoadFailures; // Classes Failed to load + size_t cbLoaderHeapSize; // Total size of heap used by the loader + DUALCOUNT cAppDomainsUnloaded; // Rate at which app domains are unloaded +} Perf_Loading; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct Perf_Loading_Wow64 +{ + TRICOUNT cClassesLoaded; + TRICOUNT_IL cAppDomains; // Current # of AppDomains + TRICOUNT cAssemblies; // Current # of Assemblies + UNALIGNED LONGLONG timeLoading; // % time loading + ULONG cAsmSearchLen; // Avg search length for assemblies + DUALCOUNT cLoadFailures; // Classes Failed to load + ULONG cbLoaderHeapSize; // Total size of heap used by the loader + DUALCOUNT cAppDomainsUnloaded; // Rate at which app domains are unloaded +} Perf_Loading_Wow64; +#include + + +#ifndef _WIN64 +#include +#endif +typedef struct Perf_Jit +{ + ULONG cMethodsJitted; // number of methods jitted + TRICOUNT cbILJitted; // IL jitted stats + //DUALCOUNT cbPitched; // Total bytes pitched + ULONG cJitFailures; // # of standard Jit failures + ULONG timeInJit; // Time in JIT since last sample + ULONG timeInJitBase; // Time in JIT base counter +} Perf_Jit; +#ifndef _WIN64 +#include +#endif + +#ifndef _WIN64 +#include +#endif +typedef struct Perf_Excep +{ + DUALCOUNT cThrown; // Number of Exceptions thrown + ULONG cFiltersExecuted; // Number of Filters executed + ULONG cFinallysExecuted; // Number of Finallys executed + ULONG cThrowToCatchStackDepth; // Delta from throw to catch site on stack +} Perf_Excep; +#ifndef _WIN64 +#include +#endif + +#ifndef _WIN64 +#include +#endif +typedef struct Perf_Interop +{ + ULONG cCCW; // Number of CCWs + ULONG cStubs; // Number of stubs + ULONG cMarshalling; // # of time marshalling args and return values. + ULONG cTLBImports; // Number of tlbs we import + ULONG cTLBExports; // Number of tlbs we export +} Perf_Interop; +#ifndef _WIN64 +#include +#endif + +#ifndef _WIN64 +#include +#endif +typedef struct Perf_LocksAndThreads +{ + // Locks + DUALCOUNT cContention; // # of times in AwareLock::EnterEpilogue() + TRICOUNT cQueueLength; // Lenght of queue + // Threads + ULONG cCurrentThreadsLogical; // Number (created - destroyed) of logical threads + ULONG cCurrentThreadsPhysical; // Number (created - destroyed) of OS threads + TRICOUNT cRecognizedThreads; // # of Threads execute in runtime's control +} Perf_LocksAndThreads; +#ifndef _WIN64 +#include +#endif + + +// IMPORTANT!!!!!!!: The first two fields in the struct have to be together +// and be the first two fields in the struct. The managed code in ChannelServices.cs +// depends on this. +#ifndef _WIN64 +#include +#endif +typedef struct Perf_Contexts +{ + // Contexts & Remoting + DUALCOUNT cRemoteCalls; // # of remote calls + ULONG cChannels; // Number of current channels + ULONG cProxies; // Number of context proxies. + ULONG cClasses; // # of Context-bound classes + ULONG cObjAlloc; // # of context bound objects allocated + ULONG cContexts; // The current number of contexts. +} Perf_Contexts; +#ifndef _WIN64 +#include +#endif + + +#ifndef _WIN64 +#include +#endif +typedef struct Perf_Security +{ + ULONG cTotalRTChecks; // Total runtime checks + UNALIGNED LONGLONG timeAuthorize; // % time authenticating + ULONG cLinkChecks; // link time checks + ULONG timeRTchecks; // % time in Runtime checks + ULONG timeRTchecksBase; // % time in Runtime checks base counter + ULONG stackWalkDepth; // depth of stack for security checks +} Perf_Security; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct Perf_Security_Wow64 +{ + ULONG cTotalRTChecks; // Total runtime checks + UNALIGNED LONGLONG timeAuthorize; // % time authenticating + ULONG cLinkChecks; // link time checks + ULONG timeRTchecks; // % time in Runtime checks + ULONG timeRTchecksBase; // % time in Runtime checks base counter + ULONG stackWalkDepth; // depth of stack for security checks +} Perf_Security_Wow64; +#include + + +#ifndef _WIN64 +#include +#endif +typedef struct _PerfCounterIPCControlBlock +{ + // Versioning info + USHORT Bytes; // size of this entire block + USHORT Attributes; // attributes for this block + + // Counter Sections + Perf_GC GC; + Perf_Contexts Context; + Perf_Interop Interop; + Perf_Loading Loading; + Perf_Excep Exceptions; + Perf_LocksAndThreads LocksAndThreads; + Perf_Jit Jit; + Perf_Security Security; +} PerfCounterIPCControlBlock; +#ifndef _WIN64 +#include +#endif + +#include +typedef struct _PerfCounterIPCControlBlock_Wow64 +{ + // Versioning info + USHORT Bytes; // size of this entire block + USHORT Attributes; // attributes for this block + + // Counter Sections + Perf_GC_Wow64 GC; + Perf_Contexts Context; + Perf_Interop Interop; + Perf_Loading_Wow64 Loading; + Perf_Excep Exceptions; + Perf_LocksAndThreads LocksAndThreads; + Perf_Jit Jit; + Perf_Security_Wow64 Security; +} PerfCounterIPCControlBlock_Wow64; +#include + + +#endif _PERF_COUNTERS_H_ diff --git a/plugins/DotNetTools/clretw.h b/plugins/DotNetTools/clretw.h index e82772d64571..289802e15ff2 100644 --- a/plugins/DotNetTools/clretw.h +++ b/plugins/DotNetTools/clretw.h @@ -1,146 +1,146 @@ -/* - * Process Hacker .NET Tools - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef CLRETW_H -#define CLRETW_H - -// Keywords - -#define CLR_LOADER_KEYWORD 0x8 -#define CLR_STARTENUMERATION_KEYWORD 0x40 - -// Event IDs - -#define DCStartComplete_V1 145 -#define ModuleDCStart_V1 153 -#define AssemblyDCStart_V1 155 -#define AppDomainDCStart_V1 157 -#define RuntimeInformationDCStart 187 - -// Opcodes - -#define CLR_METHODDC_DCSTARTCOMPLETE_OPCODE 14 -#define CLR_MODULEDCSTART_OPCODE 35 - -// Bit maps - -// AppDomainFlags -#define AppDomainFlags_Default 0x1 -#define AppDomainFlags_Executable 0x2 -#define AppDomainFlags_Shared 0x4 - -// AssemblyFlags -#define AssemblyFlags_DomainNeutral 0x1 -#define AssemblyFlags_Dynamic 0x2 -#define AssemblyFlags_Native 0x4 -#define AssemblyFlags_Collectible 0x8 - -// ModuleFlags -#define ModuleFlags_DomainNeutral 0x1 -#define ModuleFlags_Native 0x2 -#define ModuleFlags_Dynamic 0x4 -#define ModuleFlags_Manifest 0x8 - -// StartupMode -#define StartupMode_ManagedExe 0x1 -#define StartupMode_HostedCLR 0x2 -#define StartupMode_IjwDll 0x4 -#define StartupMode_ComActivated 0x8 -#define StartupMode_Other 0x10 - -// StartupFlags -#define StartupFlags_CONCURRENT_GC 0x1 -#define StartupFlags_LOADER_OPTIMIZATION_SINGLE_DOMAIN 0x2 -#define StartupFlags_LOADER_OPTIMIZATION_MULTI_DOMAIN 0x4 -#define StartupFlags_LOADER_SAFEMODE 0x10 -#define StartupFlags_LOADER_SETPREFERENCE 0x100 -#define StartupFlags_SERVER_GC 0x1000 -#define StartupFlags_HOARD_GC_VM 0x2000 -#define StartupFlags_SINGLE_VERSION_HOSTING_INTERFACE 0x4000 -#define StartupFlags_LEGACY_IMPERSONATION 0x10000 -#define StartupFlags_DISABLE_COMMITTHREADSTACK 0x20000 -#define StartupFlags_ALWAYSFLOW_IMPERSONATION 0x40000 -#define StartupFlags_TRIM_GC_COMMIT 0x80000 -#define StartupFlags_ETW 0x100000 -#define StartupFlags_SERVER_BUILD 0x200000 -#define StartupFlags_ARM 0x400000 - -// Templates - -#include - -typedef struct _DCStartEnd -{ - USHORT ClrInstanceID; -} DCStartEnd, *PDCStartEnd; - -typedef struct _ModuleLoadUnloadRundown_V1 -{ - ULONG64 ModuleID; - ULONG64 AssemblyID; - ULONG ModuleFlags; // ModuleFlags - ULONG Reserved1; - WCHAR ModuleILPath[1]; - // WCHAR ModuleNativePath[1]; - // USHORT ClrInstanceID; -} ModuleLoadUnloadRundown_V1, *PModuleLoadUnloadRundown_V1; - -typedef struct _AssemblyLoadUnloadRundown_V1 -{ - ULONG64 AssemblyID; - ULONG64 AppDomainID; - ULONG64 BindingID; - ULONG AssemblyFlags; // AssemblyFlags - WCHAR FullyQualifiedAssemblyName[1]; - // USHORT ClrInstanceID; -} AssemblyLoadUnloadRundown_V1, *PAssemblyLoadUnloadRundown_V1; - -typedef struct _AppDomainLoadUnloadRundown_V1 -{ - ULONG64 AppDomainID; - ULONG AppDomainFlags; // AppDomainFlags - WCHAR AppDomainName[1]; - // ULONG AppDomainIndex; - // USHORT ClrInstanceID; -} AppDomainLoadUnloadRundown_V1, *PAppDomainLoadUnloadRundown_V1; - -typedef struct _RuntimeInformationRundown -{ - USHORT ClrInstanceID; - USHORT Sku; - USHORT BclMajorVersion; - USHORT BclMinorVersion; - USHORT BclBuildNumber; - USHORT BclQfeNumber; - USHORT VMMajorVersion; - USHORT VMMinorVersion; - USHORT VMBuildNumber; - USHORT VMQfeNumber; - ULONG StartupFlags; // StartupFlags - UCHAR StartupMode; // StartupMode - WCHAR CommandLine[1]; - // GUID ComObjectGuid; - // WCHAR RuntimeDllPath[1]; -} RuntimeInformationRundown, *PRuntimeInformationRundown; - -#include - -#endif +/* + * Process Hacker .NET Tools + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef CLRETW_H +#define CLRETW_H + +// Keywords + +#define CLR_LOADER_KEYWORD 0x8 +#define CLR_STARTENUMERATION_KEYWORD 0x40 + +// Event IDs + +#define DCStartComplete_V1 145 +#define ModuleDCStart_V1 153 +#define AssemblyDCStart_V1 155 +#define AppDomainDCStart_V1 157 +#define RuntimeInformationDCStart 187 + +// Opcodes + +#define CLR_METHODDC_DCSTARTCOMPLETE_OPCODE 14 +#define CLR_MODULEDCSTART_OPCODE 35 + +// Bit maps + +// AppDomainFlags +#define AppDomainFlags_Default 0x1 +#define AppDomainFlags_Executable 0x2 +#define AppDomainFlags_Shared 0x4 + +// AssemblyFlags +#define AssemblyFlags_DomainNeutral 0x1 +#define AssemblyFlags_Dynamic 0x2 +#define AssemblyFlags_Native 0x4 +#define AssemblyFlags_Collectible 0x8 + +// ModuleFlags +#define ModuleFlags_DomainNeutral 0x1 +#define ModuleFlags_Native 0x2 +#define ModuleFlags_Dynamic 0x4 +#define ModuleFlags_Manifest 0x8 + +// StartupMode +#define StartupMode_ManagedExe 0x1 +#define StartupMode_HostedCLR 0x2 +#define StartupMode_IjwDll 0x4 +#define StartupMode_ComActivated 0x8 +#define StartupMode_Other 0x10 + +// StartupFlags +#define StartupFlags_CONCURRENT_GC 0x1 +#define StartupFlags_LOADER_OPTIMIZATION_SINGLE_DOMAIN 0x2 +#define StartupFlags_LOADER_OPTIMIZATION_MULTI_DOMAIN 0x4 +#define StartupFlags_LOADER_SAFEMODE 0x10 +#define StartupFlags_LOADER_SETPREFERENCE 0x100 +#define StartupFlags_SERVER_GC 0x1000 +#define StartupFlags_HOARD_GC_VM 0x2000 +#define StartupFlags_SINGLE_VERSION_HOSTING_INTERFACE 0x4000 +#define StartupFlags_LEGACY_IMPERSONATION 0x10000 +#define StartupFlags_DISABLE_COMMITTHREADSTACK 0x20000 +#define StartupFlags_ALWAYSFLOW_IMPERSONATION 0x40000 +#define StartupFlags_TRIM_GC_COMMIT 0x80000 +#define StartupFlags_ETW 0x100000 +#define StartupFlags_SERVER_BUILD 0x200000 +#define StartupFlags_ARM 0x400000 + +// Templates + +#include + +typedef struct _DCStartEnd +{ + USHORT ClrInstanceID; +} DCStartEnd, *PDCStartEnd; + +typedef struct _ModuleLoadUnloadRundown_V1 +{ + ULONG64 ModuleID; + ULONG64 AssemblyID; + ULONG ModuleFlags; // ModuleFlags + ULONG Reserved1; + WCHAR ModuleILPath[1]; + // WCHAR ModuleNativePath[1]; + // USHORT ClrInstanceID; +} ModuleLoadUnloadRundown_V1, *PModuleLoadUnloadRundown_V1; + +typedef struct _AssemblyLoadUnloadRundown_V1 +{ + ULONG64 AssemblyID; + ULONG64 AppDomainID; + ULONG64 BindingID; + ULONG AssemblyFlags; // AssemblyFlags + WCHAR FullyQualifiedAssemblyName[1]; + // USHORT ClrInstanceID; +} AssemblyLoadUnloadRundown_V1, *PAssemblyLoadUnloadRundown_V1; + +typedef struct _AppDomainLoadUnloadRundown_V1 +{ + ULONG64 AppDomainID; + ULONG AppDomainFlags; // AppDomainFlags + WCHAR AppDomainName[1]; + // ULONG AppDomainIndex; + // USHORT ClrInstanceID; +} AppDomainLoadUnloadRundown_V1, *PAppDomainLoadUnloadRundown_V1; + +typedef struct _RuntimeInformationRundown +{ + USHORT ClrInstanceID; + USHORT Sku; + USHORT BclMajorVersion; + USHORT BclMinorVersion; + USHORT BclBuildNumber; + USHORT BclQfeNumber; + USHORT VMMajorVersion; + USHORT VMMinorVersion; + USHORT VMBuildNumber; + USHORT VMQfeNumber; + ULONG StartupFlags; // StartupFlags + UCHAR StartupMode; // StartupMode + WCHAR CommandLine[1]; + // GUID ComObjectGuid; + // WCHAR RuntimeDllPath[1]; +} RuntimeInformationRundown, *PRuntimeInformationRundown; + +#include + +#endif diff --git a/plugins/DotNetTools/clrsup.c b/plugins/DotNetTools/clrsup.c index d5c33ae85e76..29b9ab434274 100644 --- a/plugins/DotNetTools/clrsup.c +++ b/plugins/DotNetTools/clrsup.c @@ -1,575 +1,575 @@ -/* - * Process Hacker .NET Tools - - * CLR data access functions - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" -#include "clrsup.h" - -static GUID IID_ICLRDataTarget_I = { 0x3e11ccee, 0xd08b, 0x43e5, { 0xaf, 0x01, 0x32, 0x71, 0x7a, 0x64, 0xda, 0x03 } }; -static GUID IID_IXCLRDataProcess = { 0x5c552ab6, 0xfc09, 0x4cb3, { 0x8e, 0x36, 0x22, 0xfa, 0x03, 0xc7, 0x98, 0xb7 } }; - -static ICLRDataTargetVtbl DnCLRDataTarget_VTable = -{ - DnCLRDataTarget_QueryInterface, - DnCLRDataTarget_AddRef, - DnCLRDataTarget_Release, - DnCLRDataTarget_GetMachineType, - DnCLRDataTarget_GetPointerSize, - DnCLRDataTarget_GetImageBase, - DnCLRDataTarget_ReadVirtual, - DnCLRDataTarget_WriteVirtual, - DnCLRDataTarget_GetTLSValue, - DnCLRDataTarget_SetTLSValue, - DnCLRDataTarget_GetCurrentThreadID, - DnCLRDataTarget_GetThreadContext, - DnCLRDataTarget_SetThreadContext, - DnCLRDataTarget_Request -}; - -PCLR_PROCESS_SUPPORT CreateClrProcessSupport( - _In_ HANDLE ProcessId - ) -{ - PCLR_PROCESS_SUPPORT support; - ICLRDataTarget *dataTarget; - IXCLRDataProcess *dataProcess; - - dataTarget = DnCLRDataTarget_Create(ProcessId); - - if (!dataTarget) - return NULL; - - dataProcess = NULL; - CreateXCLRDataProcess(ProcessId, dataTarget, &dataProcess); - ICLRDataTarget_Release(dataTarget); - - if (!dataProcess) - return NULL; - - support = PhAllocate(sizeof(CLR_PROCESS_SUPPORT)); - support->DataProcess = dataProcess; - - return support; -} - -VOID FreeClrProcessSupport( - _In_ PCLR_PROCESS_SUPPORT Support - ) -{ - IXCLRDataProcess_Release(Support->DataProcess); - PhFree(Support); -} - -PPH_STRING GetRuntimeNameByAddressClrProcess( - _In_ PCLR_PROCESS_SUPPORT Support, - _In_ ULONG64 Address, - _Out_opt_ PULONG64 Displacement - ) -{ - PPH_STRING buffer; - ULONG bufferLength; - ULONG returnLength; - ULONG64 displacement; - - bufferLength = 33; - buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2); - - returnLength = 0; - - if (!SUCCEEDED(IXCLRDataProcess_GetRuntimeNameByAddress( - Support->DataProcess, - Address, - 0, - bufferLength, - &returnLength, - buffer->Buffer, - &displacement - ))) - { - PhDereferenceObject(buffer); - return NULL; - } - - // Try again if our buffer was too small. - if (returnLength > bufferLength) - { - PhDereferenceObject(buffer); - bufferLength = returnLength; - buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2); - - if (!SUCCEEDED(IXCLRDataProcess_GetRuntimeNameByAddress( - Support->DataProcess, - Address, - 0, - bufferLength, - &returnLength, - buffer->Buffer, - &displacement - ))) - { - PhDereferenceObject(buffer); - return NULL; - } - } - - if (Displacement) - *Displacement = displacement; - - buffer->Length = (returnLength - 1) * 2; - - return buffer; -} - -PPH_STRING GetNameXClrDataAppDomain( - _In_ PVOID AppDomain - ) -{ - IXCLRDataAppDomain *appDomain; - PPH_STRING buffer; - ULONG bufferLength; - ULONG returnLength; - - appDomain = AppDomain; - - bufferLength = 33; - buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2); - - returnLength = 0; - - if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer))) - { - PhDereferenceObject(buffer); - return NULL; - } - - // Try again if our buffer was too small. - if (returnLength > bufferLength) - { - PhDereferenceObject(buffer); - bufferLength = returnLength; - buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2); - - if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer))) - { - PhDereferenceObject(buffer); - return NULL; - } - } - - buffer->Length = (returnLength - 1) * 2; - - return buffer; -} - -PVOID LoadMscordacwks( - _In_ BOOLEAN IsClrV4 - ) -{ - PVOID dllBase; - PH_STRINGREF systemRootString; - PH_STRINGREF mscordacwksPathString; - PPH_STRING mscordacwksFileName; - - LoadLibrary(L"mscoree.dll"); - - PhGetSystemRoot(&systemRootString); - - if (IsClrV4) - { -#ifdef _WIN64 - PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v4.0.30319\\mscordacwks.dll"); -#else - PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v4.0.30319\\mscordacwks.dll"); -#endif - } - else - { -#ifdef _WIN64 - PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v2.0.50727\\mscordacwks.dll"); -#else - PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v2.0.50727\\mscordacwks.dll"); -#endif - } - - mscordacwksFileName = PhConcatStringRef2(&systemRootString, &mscordacwksPathString); - dllBase = LoadLibrary(mscordacwksFileName->Buffer); - PhDereferenceObject(mscordacwksFileName); - - return dllBase; -} - -HRESULT CreateXCLRDataProcess( - _In_ HANDLE ProcessId, - _In_ ICLRDataTarget *Target, - _Out_ struct IXCLRDataProcess **DataProcess - ) -{ - ULONG flags; - BOOLEAN clrV4; - HMODULE dllBase; - HRESULT (__stdcall *clrDataCreateInstance)(REFIID, ICLRDataTarget *, void **); - - clrV4 = FALSE; - - if (NT_SUCCESS(PhGetProcessIsDotNetEx(ProcessId, NULL, 0, NULL, &flags))) - { - if (flags & PH_CLR_VERSION_4_ABOVE) - clrV4 = TRUE; - } - - // Load the correct version of mscordacwks.dll. - - if (clrV4) - { - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static HMODULE mscordacwksDllBase; - - if (PhBeginInitOnce(&initOnce)) - { - mscordacwksDllBase = LoadMscordacwks(TRUE); - PhEndInitOnce(&initOnce); - } - - dllBase = mscordacwksDllBase; - } - else - { - static PH_INITONCE initOnce = PH_INITONCE_INIT; - static HMODULE mscordacwksDllBase; - - if (PhBeginInitOnce(&initOnce)) - { - mscordacwksDllBase = LoadMscordacwks(FALSE); - PhEndInitOnce(&initOnce); - } - - dllBase = mscordacwksDllBase; - } - - if (!dllBase) - return E_FAIL; - - clrDataCreateInstance = PhGetProcedureAddress(dllBase, "CLRDataCreateInstance", 0); - - if (!clrDataCreateInstance) - return E_FAIL; - - return clrDataCreateInstance(&IID_IXCLRDataProcess, Target, DataProcess); -} - -ICLRDataTarget *DnCLRDataTarget_Create( - _In_ HANDLE ProcessId - ) -{ - DnCLRDataTarget *dataTarget; - HANDLE processHandle; - BOOLEAN isWow64; - - if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId))) - return NULL; - -#ifdef _WIN64 - if (!NT_SUCCESS(PhGetProcessIsWow64(processHandle, &isWow64))) - { - NtClose(processHandle); - return NULL; - } -#else - isWow64 = FALSE; -#endif - - dataTarget = PhAllocate(sizeof(DnCLRDataTarget)); - dataTarget->VTable = &DnCLRDataTarget_VTable; - dataTarget->RefCount = 1; - - dataTarget->ProcessId = ProcessId; - dataTarget->ProcessHandle = processHandle; - dataTarget->IsWow64 = isWow64; - - return (ICLRDataTarget *)dataTarget; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_QueryInterface( - _In_ ICLRDataTarget *This, - _In_ REFIID Riid, - _Out_ PVOID *Object - ) -{ - if ( - IsEqualIID(Riid, &IID_IUnknown) || - IsEqualIID(Riid, &IID_ICLRDataTarget_I) - ) - { - DnCLRDataTarget_AddRef(This); - *Object = This; - return S_OK; - } - - *Object = NULL; - return E_NOINTERFACE; -} - -ULONG STDMETHODCALLTYPE DnCLRDataTarget_AddRef( - _In_ ICLRDataTarget *This - ) -{ - DnCLRDataTarget *this = (DnCLRDataTarget *)This; - - this->RefCount++; - - return this->RefCount; -} - -ULONG STDMETHODCALLTYPE DnCLRDataTarget_Release( - _In_ ICLRDataTarget *This - ) -{ - DnCLRDataTarget *this = (DnCLRDataTarget *)This; - - this->RefCount--; - - if (this->RefCount == 0) - { - NtClose(this->ProcessHandle); - - PhFree(this); - - return 0; - } - - return this->RefCount; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetMachineType( - _In_ ICLRDataTarget *This, - _Out_ ULONG32 *machineType - ) -{ - DnCLRDataTarget *this = (DnCLRDataTarget *)This; - -#ifdef _WIN64 - if (!this->IsWow64) - *machineType = IMAGE_FILE_MACHINE_AMD64; - else - *machineType = IMAGE_FILE_MACHINE_I386; -#else - *machineType = IMAGE_FILE_MACHINE_I386; -#endif - - return S_OK; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetPointerSize( - _In_ ICLRDataTarget *This, - _Out_ ULONG32 *pointerSize - ) -{ - DnCLRDataTarget *this = (DnCLRDataTarget *)This; - -#ifdef _WIN64 - if (!this->IsWow64) -#endif - *pointerSize = sizeof(PVOID); -#ifdef _WIN64 - else - *pointerSize = sizeof(ULONG); -#endif - - return S_OK; -} - -BOOLEAN NTAPI PhpGetImageBaseCallback( - _In_ PLDR_DATA_TABLE_ENTRY Module, - _In_opt_ PVOID Context - ) -{ - PPHP_GET_IMAGE_BASE_CONTEXT context = Context; - - if (RtlEqualUnicodeString(&Module->FullDllName, &context->ImagePath, TRUE) || - RtlEqualUnicodeString(&Module->BaseDllName, &context->ImagePath, TRUE)) - { - context->BaseAddress = Module->DllBase; - return FALSE; - } - - return TRUE; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetImageBase( - _In_ ICLRDataTarget *This, - _In_ LPCWSTR imagePath, - _Out_ CLRDATA_ADDRESS *baseAddress - ) -{ - DnCLRDataTarget *this = (DnCLRDataTarget *)This; - PHP_GET_IMAGE_BASE_CONTEXT context; - - RtlInitUnicodeString(&context.ImagePath, (PWSTR)imagePath); - context.BaseAddress = NULL; - PhEnumProcessModules(this->ProcessHandle, PhpGetImageBaseCallback, &context); - -#ifdef _WIN64 - if (this->IsWow64) - PhEnumProcessModules32(this->ProcessHandle, PhpGetImageBaseCallback, &context); -#endif - - if (context.BaseAddress) - { - *baseAddress = (CLRDATA_ADDRESS)context.BaseAddress; - - return S_OK; - } - else - { - return E_FAIL; - } -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_ReadVirtual( - _In_ ICLRDataTarget *This, - _In_ CLRDATA_ADDRESS address, - _Out_ BYTE *buffer, - _In_ ULONG32 bytesRequested, - _Out_ ULONG32 *bytesRead - ) -{ - DnCLRDataTarget *this = (DnCLRDataTarget *)This; - NTSTATUS status; - SIZE_T numberOfBytesRead; - - if (NT_SUCCESS(status = NtReadVirtualMemory( - this->ProcessHandle, - (PVOID)address, - buffer, - bytesRequested, - &numberOfBytesRead - ))) - { - *bytesRead = (ULONG32)numberOfBytesRead; - - return S_OK; - } - else - { - ULONG result; - - result = RtlNtStatusToDosError(status); - - return HRESULT_FROM_WIN32(result); - } -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_WriteVirtual( - _In_ ICLRDataTarget *This, - _In_ CLRDATA_ADDRESS address, - _In_ BYTE *buffer, - _In_ ULONG32 bytesRequested, - _Out_ ULONG32 *bytesWritten - ) -{ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetTLSValue( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 index, - _Out_ CLRDATA_ADDRESS *value - ) -{ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetTLSValue( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 index, - _In_ CLRDATA_ADDRESS value - ) -{ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetCurrentThreadID( - _In_ ICLRDataTarget *This, - _Out_ ULONG32 *threadID - ) -{ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetThreadContext( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 contextFlags, - _In_ ULONG32 contextSize, - _Out_ BYTE *context - ) -{ - NTSTATUS status; - HANDLE threadHandle; - CONTEXT buffer; - - if (contextSize < sizeof(CONTEXT)) - return E_INVALIDARG; - - memset(&buffer, 0, sizeof(CONTEXT)); - buffer.ContextFlags = contextFlags; - - if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, UlongToHandle(threadID)))) - { - status = NtGetContextThread(threadHandle, &buffer); - NtClose(threadHandle); - } - - if (NT_SUCCESS(status)) - { - memcpy(context, &buffer, sizeof(CONTEXT)); - - return S_OK; - } - else - { - return HRESULT_FROM_WIN32(RtlNtStatusToDosError(status)); - } -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetThreadContext( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 contextSize, - _In_ BYTE *context - ) -{ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_Request( - _In_ ICLRDataTarget *This, - _In_ ULONG32 reqCode, - _In_ ULONG32 inBufferSize, - _In_ BYTE *inBuffer, - _In_ ULONG32 outBufferSize, - _Out_ BYTE *outBuffer - ) -{ - return E_NOTIMPL; -} +/* + * Process Hacker .NET Tools - + * CLR data access functions + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "clrsup.h" + +static GUID IID_ICLRDataTarget_I = { 0x3e11ccee, 0xd08b, 0x43e5, { 0xaf, 0x01, 0x32, 0x71, 0x7a, 0x64, 0xda, 0x03 } }; +static GUID IID_IXCLRDataProcess = { 0x5c552ab6, 0xfc09, 0x4cb3, { 0x8e, 0x36, 0x22, 0xfa, 0x03, 0xc7, 0x98, 0xb7 } }; + +static ICLRDataTargetVtbl DnCLRDataTarget_VTable = +{ + DnCLRDataTarget_QueryInterface, + DnCLRDataTarget_AddRef, + DnCLRDataTarget_Release, + DnCLRDataTarget_GetMachineType, + DnCLRDataTarget_GetPointerSize, + DnCLRDataTarget_GetImageBase, + DnCLRDataTarget_ReadVirtual, + DnCLRDataTarget_WriteVirtual, + DnCLRDataTarget_GetTLSValue, + DnCLRDataTarget_SetTLSValue, + DnCLRDataTarget_GetCurrentThreadID, + DnCLRDataTarget_GetThreadContext, + DnCLRDataTarget_SetThreadContext, + DnCLRDataTarget_Request +}; + +PCLR_PROCESS_SUPPORT CreateClrProcessSupport( + _In_ HANDLE ProcessId + ) +{ + PCLR_PROCESS_SUPPORT support; + ICLRDataTarget *dataTarget; + IXCLRDataProcess *dataProcess; + + dataTarget = DnCLRDataTarget_Create(ProcessId); + + if (!dataTarget) + return NULL; + + dataProcess = NULL; + CreateXCLRDataProcess(ProcessId, dataTarget, &dataProcess); + ICLRDataTarget_Release(dataTarget); + + if (!dataProcess) + return NULL; + + support = PhAllocate(sizeof(CLR_PROCESS_SUPPORT)); + support->DataProcess = dataProcess; + + return support; +} + +VOID FreeClrProcessSupport( + _In_ PCLR_PROCESS_SUPPORT Support + ) +{ + IXCLRDataProcess_Release(Support->DataProcess); + PhFree(Support); +} + +PPH_STRING GetRuntimeNameByAddressClrProcess( + _In_ PCLR_PROCESS_SUPPORT Support, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement + ) +{ + PPH_STRING buffer; + ULONG bufferLength; + ULONG returnLength; + ULONG64 displacement; + + bufferLength = 33; + buffer = PhCreateStringEx(NULL, (bufferLength - 1) * sizeof(WCHAR)); + + returnLength = 0; + + if (!SUCCEEDED(IXCLRDataProcess_GetRuntimeNameByAddress( + Support->DataProcess, + Address, + 0, + bufferLength, + &returnLength, + buffer->Buffer, + &displacement + ))) + { + PhDereferenceObject(buffer); + return NULL; + } + + // Try again if our buffer was too small. + if (returnLength > bufferLength) + { + PhDereferenceObject(buffer); + bufferLength = returnLength; + buffer = PhCreateStringEx(NULL, (bufferLength - 1) * sizeof(WCHAR)); + + if (!SUCCEEDED(IXCLRDataProcess_GetRuntimeNameByAddress( + Support->DataProcess, + Address, + 0, + bufferLength, + &returnLength, + buffer->Buffer, + &displacement + ))) + { + PhDereferenceObject(buffer); + return NULL; + } + } + + if (Displacement) + *Displacement = displacement; + + buffer->Length = (returnLength - 1) * sizeof(WCHAR); + + return buffer; +} + +PPH_STRING GetNameXClrDataAppDomain( + _In_ PVOID AppDomain + ) +{ + IXCLRDataAppDomain *appDomain; + PPH_STRING buffer; + ULONG bufferLength; + ULONG returnLength; + + appDomain = AppDomain; + + bufferLength = 33; + buffer = PhCreateStringEx(NULL, (bufferLength - 1) * sizeof(WCHAR)); + + returnLength = 0; + + if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer))) + { + PhDereferenceObject(buffer); + return NULL; + } + + // Try again if our buffer was too small. + if (returnLength > bufferLength) + { + PhDereferenceObject(buffer); + bufferLength = returnLength; + buffer = PhCreateStringEx(NULL, (bufferLength - 1) * sizeof(WCHAR)); + + if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer))) + { + PhDereferenceObject(buffer); + return NULL; + } + } + + buffer->Length = (returnLength - 1) * sizeof(WCHAR); + + return buffer; +} + +PVOID LoadMscordacwks( + _In_ BOOLEAN IsClrV4 + ) +{ + PVOID dllBase; + PH_STRINGREF systemRootString; + PH_STRINGREF mscordacwksPathString; + PPH_STRING mscordacwksFileName; + + LoadLibrary(L"mscoree.dll"); + + PhGetSystemRoot(&systemRootString); + + if (IsClrV4) + { +#ifdef _WIN64 + PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v4.0.30319\\mscordacwks.dll"); +#else + PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v4.0.30319\\mscordacwks.dll"); +#endif + } + else + { +#ifdef _WIN64 + PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v2.0.50727\\mscordacwks.dll"); +#else + PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v2.0.50727\\mscordacwks.dll"); +#endif + } + + mscordacwksFileName = PhConcatStringRef2(&systemRootString, &mscordacwksPathString); + dllBase = LoadLibrary(mscordacwksFileName->Buffer); + PhDereferenceObject(mscordacwksFileName); + + return dllBase; +} + +HRESULT CreateXCLRDataProcess( + _In_ HANDLE ProcessId, + _In_ ICLRDataTarget *Target, + _Out_ struct IXCLRDataProcess **DataProcess + ) +{ + ULONG flags; + BOOLEAN clrV4; + HMODULE dllBase; + PFN_CLRDataCreateInstance clrDataCreateInstance; + + clrV4 = FALSE; + + if (NT_SUCCESS(PhGetProcessIsDotNetEx(ProcessId, NULL, 0, NULL, &flags))) + { + if (flags & PH_CLR_VERSION_4_ABOVE) + clrV4 = TRUE; + } + + // Load the correct version of mscordacwks.dll. + + if (clrV4) + { + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static HMODULE mscordacwksDllBase; + + if (PhBeginInitOnce(&initOnce)) + { + mscordacwksDllBase = LoadMscordacwks(TRUE); + PhEndInitOnce(&initOnce); + } + + dllBase = mscordacwksDllBase; + } + else + { + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static HMODULE mscordacwksDllBase; + + if (PhBeginInitOnce(&initOnce)) + { + mscordacwksDllBase = LoadMscordacwks(FALSE); + PhEndInitOnce(&initOnce); + } + + dllBase = mscordacwksDllBase; + } + + if (!dllBase) + return E_FAIL; + + clrDataCreateInstance = PhGetProcedureAddress(dllBase, "CLRDataCreateInstance", 0); + + if (!clrDataCreateInstance) + return E_FAIL; + + return clrDataCreateInstance(&IID_IXCLRDataProcess, Target, DataProcess); +} + +ICLRDataTarget *DnCLRDataTarget_Create( + _In_ HANDLE ProcessId + ) +{ + DnCLRDataTarget *dataTarget; + HANDLE processHandle; + BOOLEAN isWow64; + + if (!NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, ProcessId))) + return NULL; + +#ifdef _WIN64 + if (!NT_SUCCESS(PhGetProcessIsWow64(processHandle, &isWow64))) + { + NtClose(processHandle); + return NULL; + } +#else + isWow64 = FALSE; +#endif + + dataTarget = PhAllocate(sizeof(DnCLRDataTarget)); + dataTarget->VTable = &DnCLRDataTarget_VTable; + dataTarget->RefCount = 1; + + dataTarget->ProcessId = ProcessId; + dataTarget->ProcessHandle = processHandle; + dataTarget->IsWow64 = isWow64; + + return (ICLRDataTarget *)dataTarget; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_QueryInterface( + _In_ ICLRDataTarget *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ) +{ + if ( + IsEqualIID(Riid, &IID_IUnknown) || + IsEqualIID(Riid, &IID_ICLRDataTarget_I) + ) + { + DnCLRDataTarget_AddRef(This); + *Object = This; + return S_OK; + } + + *Object = NULL; + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE DnCLRDataTarget_AddRef( + _In_ ICLRDataTarget *This + ) +{ + DnCLRDataTarget *this = (DnCLRDataTarget *)This; + + this->RefCount++; + + return this->RefCount; +} + +ULONG STDMETHODCALLTYPE DnCLRDataTarget_Release( + _In_ ICLRDataTarget *This + ) +{ + DnCLRDataTarget *this = (DnCLRDataTarget *)This; + + this->RefCount--; + + if (this->RefCount == 0) + { + NtClose(this->ProcessHandle); + + PhFree(this); + + return 0; + } + + return this->RefCount; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetMachineType( + _In_ ICLRDataTarget *This, + _Out_ ULONG32 *machineType + ) +{ + DnCLRDataTarget *this = (DnCLRDataTarget *)This; + +#ifdef _WIN64 + if (!this->IsWow64) + *machineType = IMAGE_FILE_MACHINE_AMD64; + else + *machineType = IMAGE_FILE_MACHINE_I386; +#else + *machineType = IMAGE_FILE_MACHINE_I386; +#endif + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetPointerSize( + _In_ ICLRDataTarget *This, + _Out_ ULONG32 *pointerSize + ) +{ + DnCLRDataTarget *this = (DnCLRDataTarget *)This; + +#ifdef _WIN64 + if (!this->IsWow64) +#endif + *pointerSize = sizeof(PVOID); +#ifdef _WIN64 + else + *pointerSize = sizeof(ULONG); +#endif + + return S_OK; +} + +BOOLEAN NTAPI PhpGetImageBaseCallback( + _In_ PLDR_DATA_TABLE_ENTRY Module, + _In_opt_ PVOID Context + ) +{ + PPHP_GET_IMAGE_BASE_CONTEXT context = Context; + + if (RtlEqualUnicodeString(&Module->FullDllName, &context->ImagePath, TRUE) || + RtlEqualUnicodeString(&Module->BaseDllName, &context->ImagePath, TRUE)) + { + context->BaseAddress = Module->DllBase; + return FALSE; + } + + return TRUE; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetImageBase( + _In_ ICLRDataTarget *This, + _In_ LPCWSTR imagePath, + _Out_ CLRDATA_ADDRESS *baseAddress + ) +{ + DnCLRDataTarget *this = (DnCLRDataTarget *)This; + PHP_GET_IMAGE_BASE_CONTEXT context; + + RtlInitUnicodeString(&context.ImagePath, (PWSTR)imagePath); + context.BaseAddress = NULL; + PhEnumProcessModules(this->ProcessHandle, PhpGetImageBaseCallback, &context); + +#ifdef _WIN64 + if (this->IsWow64) + PhEnumProcessModules32(this->ProcessHandle, PhpGetImageBaseCallback, &context); +#endif + + if (context.BaseAddress) + { + *baseAddress = (CLRDATA_ADDRESS)context.BaseAddress; + + return S_OK; + } + else + { + return E_FAIL; + } +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_ReadVirtual( + _In_ ICLRDataTarget *This, + _In_ CLRDATA_ADDRESS address, + _Out_ BYTE *buffer, + _In_ ULONG32 bytesRequested, + _Out_ ULONG32 *bytesRead + ) +{ + DnCLRDataTarget *this = (DnCLRDataTarget *)This; + NTSTATUS status; + SIZE_T numberOfBytesRead; + + if (NT_SUCCESS(status = NtReadVirtualMemory( + this->ProcessHandle, + (PVOID)address, + buffer, + bytesRequested, + &numberOfBytesRead + ))) + { + *bytesRead = (ULONG32)numberOfBytesRead; + + return S_OK; + } + else + { + ULONG result; + + result = RtlNtStatusToDosError(status); + + return HRESULT_FROM_WIN32(result); + } +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_WriteVirtual( + _In_ ICLRDataTarget *This, + _In_ CLRDATA_ADDRESS address, + _In_ BYTE *buffer, + _In_ ULONG32 bytesRequested, + _Out_ ULONG32 *bytesWritten + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetTLSValue( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 index, + _Out_ CLRDATA_ADDRESS *value + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetTLSValue( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 index, + _In_ CLRDATA_ADDRESS value + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetCurrentThreadID( + _In_ ICLRDataTarget *This, + _Out_ ULONG32 *threadID + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetThreadContext( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 contextFlags, + _In_ ULONG32 contextSize, + _Out_ BYTE *context + ) +{ + NTSTATUS status; + HANDLE threadHandle; + CONTEXT buffer; + + if (contextSize < sizeof(CONTEXT)) + return E_INVALIDARG; + + memset(&buffer, 0, sizeof(CONTEXT)); + buffer.ContextFlags = contextFlags; + + if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, UlongToHandle(threadID)))) + { + status = NtGetContextThread(threadHandle, &buffer); + NtClose(threadHandle); + } + + if (NT_SUCCESS(status)) + { + memcpy(context, &buffer, sizeof(CONTEXT)); + + return S_OK; + } + else + { + return HRESULT_FROM_WIN32(RtlNtStatusToDosError(status)); + } +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetThreadContext( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 contextSize, + _In_ BYTE *context + ) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_Request( + _In_ ICLRDataTarget *This, + _In_ ULONG32 reqCode, + _In_ ULONG32 inBufferSize, + _In_ BYTE *inBuffer, + _In_ ULONG32 outBufferSize, + _Out_ BYTE *outBuffer + ) +{ + return E_NOTIMPL; +} diff --git a/plugins/DotNetTools/clrsup.h b/plugins/DotNetTools/clrsup.h index 27cc978e4f1d..8acdb28ee26e 100644 --- a/plugins/DotNetTools/clrsup.h +++ b/plugins/DotNetTools/clrsup.h @@ -1,645 +1,641 @@ -/* - * Process Hacker .NET Tools - - * CLR data access functions - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef CLRSUP_H -#define CLRSUP_H - -#define CINTERFACE -#define COBJMACROS -#include "clr/clrdata.h" -#undef CINTERFACE -#undef COBJMACROS - -// General interfaces - -typedef struct _CLR_PROCESS_SUPPORT -{ - struct IXCLRDataProcess *DataProcess; -} CLR_PROCESS_SUPPORT, *PCLR_PROCESS_SUPPORT; - -PCLR_PROCESS_SUPPORT CreateClrProcessSupport( - _In_ HANDLE ProcessId - ); - -VOID FreeClrProcessSupport( - _In_ PCLR_PROCESS_SUPPORT Support - ); - -PPH_STRING GetRuntimeNameByAddressClrProcess( - _In_ PCLR_PROCESS_SUPPORT Support, - _In_ ULONG64 Address, - _Out_opt_ PULONG64 Displacement - ); - -PPH_STRING GetNameXClrDataAppDomain( - _In_ PVOID AppDomain - ); - -PVOID LoadMscordacwks( - _In_ BOOLEAN IsClrV4 - ); - -HRESULT CreateXCLRDataProcess( - _In_ HANDLE ProcessId, - _In_ ICLRDataTarget *Target, - _Out_ struct IXCLRDataProcess **DataProcess - ); - -// xclrdata - -typedef ULONG64 CLRDATA_ENUM; - -typedef struct IXCLRDataProcess IXCLRDataProcess; -typedef struct IXCLRDataAppDomain IXCLRDataAppDomain; -typedef struct IXCLRDataTask IXCLRDataTask; -typedef struct IXCLRDataStackWalk IXCLRDataStackWalk; -typedef struct IXCLRDataFrame IXCLRDataFrame; - -typedef struct IXCLRDataProcessVtbl -{ - HRESULT (STDMETHODCALLTYPE *QueryInterface)( - _In_ IXCLRDataProcess *This, - _In_ REFIID riid, - _Outptr_ void **ppvObject - ); - - ULONG (STDMETHODCALLTYPE *AddRef)( - _In_ IXCLRDataProcess *This - ); - - ULONG (STDMETHODCALLTYPE *Release)( - _In_ IXCLRDataProcess *This - ); - - HRESULT (STDMETHODCALLTYPE *Flush)( - _In_ IXCLRDataProcess *This - ); - - HRESULT (STDMETHODCALLTYPE *StartEnumTasks)( - _In_ IXCLRDataProcess *This, - _Out_ CLRDATA_ENUM *handle - ); - - HRESULT (STDMETHODCALLTYPE *EnumTask)( - _In_ IXCLRDataProcess *This, - _Inout_ CLRDATA_ENUM *handle, - _Out_ IXCLRDataTask **task - ); - - HRESULT (STDMETHODCALLTYPE *EndEnumTasks)( - _In_ IXCLRDataProcess *This, - _In_ CLRDATA_ENUM handle - ); - - HRESULT (STDMETHODCALLTYPE *GetTaskByOSThreadID)( - _In_ IXCLRDataProcess *This, - _In_ ULONG32 osThreadID, - _Out_ IXCLRDataTask **task - ); - - PVOID GetTaskByUniqueID; - PVOID GetFlags; - PVOID IsSameObject; - PVOID GetManagedObject; - PVOID GetDesiredExecutionState; - PVOID SetDesiredExecutionState; - PVOID GetAddressType; - - HRESULT (STDMETHODCALLTYPE *GetRuntimeNameByAddress)( - _In_ IXCLRDataProcess *This, - _In_ CLRDATA_ADDRESS address, - _In_ ULONG32 flags, - _In_ ULONG32 bufLen, - _Out_ ULONG32 *nameLen, - _Out_ WCHAR *nameBuf, - _Out_ CLRDATA_ADDRESS *displacement - ); - - // ... -} IXCLRDataProcessVtbl; - -typedef struct IXCLRDataProcess -{ - struct IXCLRDataProcessVtbl *lpVtbl; -} IXCLRDataProcess; - -#define IXCLRDataProcess_QueryInterface(This, riid, ppvObject) \ - ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) - -#define IXCLRDataProcess_AddRef(This) \ - ((This)->lpVtbl->AddRef(This)) - -#define IXCLRDataProcess_Release(This) \ - ((This)->lpVtbl->Release(This)) - -#define IXCLRDataProcess_GetRuntimeNameByAddress(This, address, flags, bufLen, nameLen, nameBuf, displacement) \ - ((This)->lpVtbl->GetRuntimeNameByAddress(This, address, flags, bufLen, nameLen, nameBuf, displacement)) - -#define IXCLRDataProcess_Flush(This) \ - ((This)->lpVtbl->Flush(This)) - -#define IXCLRDataProcess_StartEnumTasks(This, handle) \ - ((This)->lpVtbl->StartEnumTasks(This, handle)) - -#define IXCLRDataProcess_EnumTask(This, handle, task) \ - ((This)->lpVtbl->EnumTask(This, handle, task)) - -#define IXCLRDataProcess_EndEnumTasks(This, handle) \ - ((This)->lpVtbl->EndEnumTasks(This, handle)) - -#define IXCLRDataProcess_GetTaskByOSThreadID(This, osThreadID, task) \ - ((This)->lpVtbl->GetTaskByOSThreadID(This, osThreadID, task)) - -typedef struct IXCLRDataAppDomainVtbl -{ - HRESULT (STDMETHODCALLTYPE *QueryInterface)( - _In_ IXCLRDataAppDomain *This, - _In_ REFIID riid, - _Outptr_ void **ppvObject - ); - - ULONG (STDMETHODCALLTYPE *AddRef)( - _In_ IXCLRDataAppDomain *This - ); - - ULONG (STDMETHODCALLTYPE *Release)( - _In_ IXCLRDataAppDomain *This - ); - - HRESULT (STDMETHODCALLTYPE *GetProcess)( - _In_ IXCLRDataAppDomain *This, - _Out_ IXCLRDataProcess **process - ); - - HRESULT (STDMETHODCALLTYPE *GetName)( - _In_ IXCLRDataAppDomain *This, - _In_ ULONG32 bufLen, - _Out_ ULONG32 *nameLen, - _Out_ WCHAR *name - ); - - HRESULT (STDMETHODCALLTYPE *GetUniqueID)( - _In_ IXCLRDataAppDomain *This, - _Out_ ULONG64 *id - ); - - // ... -} IXCLRDataAppDomainVtbl; - -typedef struct IXCLRDataAppDomain -{ - struct IXCLRDataAppDomainVtbl *lpVtbl; -} IXCLRDataAppDomain; - -#define IXCLRDataAppDomain_QueryInterface(This, riid, ppvObject) \ - ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) - -#define IXCLRDataAppDomain_AddRef(This) \ - ((This)->lpVtbl->AddRef(This)) - -#define IXCLRDataAppDomain_Release(This) \ - ((This)->lpVtbl->Release(This)) - -#define IXCLRDataAppDomain_GetProcess(This, process) \ - ((This)->lpVtbl->GetProcess(This, process)) - -#define IXCLRDataAppDomain_GetName(This, bufLen, nameLen, name) \ - ((This)->lpVtbl->GetName(This, bufLen, nameLen, name)) - -#define IXCLRDataAppDomain_GetUniqueID(This, id) \ - ((This)->lpVtbl->GetUniqueID(This, id)) - -typedef struct IXCLRDataTaskVtbl -{ - HRESULT (STDMETHODCALLTYPE *QueryInterface)( - _In_ IXCLRDataTask *This, - _In_ REFIID riid, - _Outptr_ void **ppvObject - ); - - ULONG (STDMETHODCALLTYPE *AddRef)( - _In_ IXCLRDataTask *This - ); - - ULONG (STDMETHODCALLTYPE *Release)( - _In_ IXCLRDataTask *This - ); - - HRESULT (STDMETHODCALLTYPE *GetProcess)( - _In_ IXCLRDataTask *This, - _Out_ IXCLRDataProcess **process - ); - - HRESULT (STDMETHODCALLTYPE *GetCurrentAppDomain)( - _In_ IXCLRDataTask *This, - _Out_ IXCLRDataAppDomain **appDomain - ); - - HRESULT (STDMETHODCALLTYPE *GetUniqueID)( - _In_ IXCLRDataTask *This, - _Out_ ULONG64 *id - ); - - HRESULT (STDMETHODCALLTYPE *GetFlags)( - _In_ IXCLRDataTask *This, - _Out_ ULONG32 *flags - ); - - PVOID IsSameObject; - PVOID GetManagedObject; - PVOID GetDesiredExecutionState; - PVOID SetDesiredExecutionState; - - HRESULT (STDMETHODCALLTYPE *CreateStackWalk)( - _In_ IXCLRDataTask *This, - _In_ ULONG32 flags, - _Out_ IXCLRDataStackWalk **stackWalk - ); - - HRESULT (STDMETHODCALLTYPE *GetOSThreadID)( - _In_ IXCLRDataTask *This, - _Out_ ULONG32 *id - ); - - PVOID GetContext; - PVOID SetContext; - PVOID GetCurrentExceptionState; - PVOID Request; - - HRESULT (STDMETHODCALLTYPE *GetName)( - _In_ IXCLRDataTask *This, - _In_ ULONG32 bufLen, - _Out_ ULONG32 *nameLen, - _Out_ WCHAR *name - ); - - PVOID GetLastExceptionState; -} IXCLRDataTaskVtbl; - -typedef struct IXCLRDataTask -{ - struct IXCLRDataTaskVtbl *lpVtbl; -} IXCLRDataTask; - -#define IXCLRDataTask_QueryInterface(This, riid, ppvObject) \ - ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) - -#define IXCLRDataTask_AddRef(This) \ - ((This)->lpVtbl->AddRef(This)) - -#define IXCLRDataTask_Release(This) \ - ((This)->lpVtbl->Release(This)) - -#define IXCLRDataTask_GetProcess(This, process) \ - ((This)->lpVtbl->GetProcess(This, process)) - -#define IXCLRDataTask_GetCurrentAppDomain(This, appDomain) \ - ((This)->lpVtbl->GetCurrentAppDomain(This, appDomain)) - -#define IXCLRDataTask_GetUniqueID(This, id) \ - ((This)->lpVtbl->GetUniqueID(This, id)) - -#define IXCLRDataTask_GetFlags(This, flags) \ - ((This)->lpVtbl->GetFlags(This, flags)) - -#define IXCLRDataTask_CreateStackWalk(This, flags, stackWalk) \ - ((This)->lpVtbl->CreateStackWalk(This, flags, stackWalk)) - -#define IXCLRDataTask_GetOSThreadID(This, id) \ - ((This)->lpVtbl->GetOSThreadID(This, id)) - -#define IXCLRDataTask_GetName(This, bufLen, nameLen, name) \ - ((This)->lpVtbl->GetName(This, bufLen, nameLen, name)) - -typedef enum -{ - CLRDATA_SIMPFRAME_UNRECOGNIZED = 0x1, - CLRDATA_SIMPFRAME_MANAGED_METHOD = 0x2, - CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE = 0x4, - CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE = 0x8 -} CLRDataSimpleFrameType; - -typedef enum -{ - CLRDATA_DETFRAME_UNRECOGNIZED, - CLRDATA_DETFRAME_UNKNOWN_STUB, - CLRDATA_DETFRAME_CLASS_INIT, - CLRDATA_DETFRAME_EXCEPTION_FILTER, - CLRDATA_DETFRAME_SECURITY, - CLRDATA_DETFRAME_CONTEXT_POLICY, - CLRDATA_DETFRAME_INTERCEPTION, - CLRDATA_DETFRAME_PROCESS_START, - CLRDATA_DETFRAME_THREAD_START, - CLRDATA_DETFRAME_TRANSITION_TO_MANAGED, - CLRDATA_DETFRAME_TRANSITION_TO_UNMANAGED, - CLRDATA_DETFRAME_COM_INTEROP_STUB, - CLRDATA_DETFRAME_DEBUGGER_EVAL, - CLRDATA_DETFRAME_CONTEXT_SWITCH, - CLRDATA_DETFRAME_FUNC_EVAL, - CLRDATA_DETFRAME_FINALLY -} CLRDataDetailedFrameType; - -typedef enum -{ - CLRDATA_STACK_SET_UNWIND_CONTEXT = 0x00000000, - CLRDATA_STACK_SET_CURRENT_CONTEXT = 0x00000001 -} CLRDataStackSetContextFlag; - -typedef struct IXCLRDataStackWalkVtbl -{ - HRESULT (STDMETHODCALLTYPE *QueryInterface)( - _In_ IXCLRDataStackWalk *This, - _In_ REFIID riid, - _Outptr_ void **ppvObject - ); - - ULONG (STDMETHODCALLTYPE *AddRef)( - _In_ IXCLRDataStackWalk *This - ); - - ULONG (STDMETHODCALLTYPE *Release)( - _In_ IXCLRDataStackWalk *This - ); - - HRESULT (STDMETHODCALLTYPE *GetContext)( - _In_ IXCLRDataStackWalk *This, - _In_ ULONG32 contextFlags, - _In_ ULONG32 contextBufSize, - _Out_ ULONG32 *contextSize, - _Out_ BYTE *contextBuf - ); - - PVOID SetContext; - - HRESULT (STDMETHODCALLTYPE *Next)( - _In_ IXCLRDataStackWalk *This - ); - - HRESULT (STDMETHODCALLTYPE *GetStackSizeSkipped)( - _In_ IXCLRDataStackWalk *This, - _Out_ ULONG64 *stackSizeSkipped - ); - - HRESULT (STDMETHODCALLTYPE *GetFrameType)( - _In_ IXCLRDataStackWalk *This, - _Out_ CLRDataSimpleFrameType *simpleType, - _Out_ CLRDataDetailedFrameType *detailedType - ); - - HRESULT (STDMETHODCALLTYPE *GetFrame)( - _In_ IXCLRDataStackWalk *This, - _Out_ PVOID *frame - ); - - HRESULT (STDMETHODCALLTYPE *Request)( - _In_ IXCLRDataStackWalk *This, - _In_ ULONG32 reqCode, - _In_ ULONG32 inBufferSize, - _In_ BYTE *inBuffer, - _In_ ULONG32 outBufferSize, - _Out_ BYTE *outBuffer - ); - - HRESULT (STDMETHODCALLTYPE *SetContext2)( - _In_ IXCLRDataStackWalk *This, - _In_ ULONG32 flags, - _In_ ULONG32 contextSize, - _In_ BYTE *context - ); -} IXCLRDataStackWalkVtbl; - -typedef struct IXCLRDataStackWalk -{ - struct IXCLRDataStackWalkVtbl *lpVtbl; -} IXCLRDataStackWalk; - -#define IXCLRDataStackWalk_QueryInterface(This, riid, ppvObject) \ - ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) - -#define IXCLRDataStackWalk_AddRef(This) \ - ((This)->lpVtbl->AddRef(This)) - -#define IXCLRDataStackWalk_Release(This) \ - ((This)->lpVtbl->Release(This)) - -#define IXCLRDataStackWalk_GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf) \ - ((This)->lpVtbl->GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf)) - -#define IXCLRDataStackWalk_Next(This) \ - ((This)->lpVtbl->Next(This)) - -#define IXCLRDataStackWalk_GetStackSizeSkipped(This, stackSizeSkipped) \ - ((This)->lpVtbl->GetStackSizeSkipped(This, stackSizeSkipped)) - -#define IXCLRDataStackWalk_GetFrameType(This, simpleType, detailedType) \ - ((This)->lpVtbl->GetFrameType(This, simpleType, detailedType)) - -#define IXCLRDataStackWalk_GetFrame(This, frame) \ - ((This)->lpVtbl->GetFrame(This, frame)) - -#define IXCLRDataStackWalk_Request(This, reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) \ - ((This)->lpVtbl->SetContext2(This, reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer)) - -#define IXCLRDataStackWalk_SetContext2(This, flags, contextSize, context) \ - ((This)->lpVtbl->SetContext2(This, flags, contextSize, context)) - -typedef struct IXCLRDataFrameVtbl -{ - HRESULT (STDMETHODCALLTYPE *QueryInterface)( - _In_ IXCLRDataFrame *This, - _In_ REFIID riid, - _Outptr_ void **ppvObject - ); - - ULONG (STDMETHODCALLTYPE *AddRef)( - _In_ IXCLRDataFrame *This - ); - - ULONG (STDMETHODCALLTYPE *Release)( - _In_ IXCLRDataFrame *This - ); - - HRESULT (STDMETHODCALLTYPE *GetFrameType)( - _In_ IXCLRDataFrame *This, - _Out_ CLRDataSimpleFrameType *simpleType, - _Out_ CLRDataDetailedFrameType *detailedType - ); - - HRESULT (STDMETHODCALLTYPE *GetContext)( - _In_ IXCLRDataFrame *This, - _In_ ULONG32 contextFlags, - _In_ ULONG32 contextBufSize, - _Out_ ULONG32 *contextSize, - _Out_ BYTE *contextBuf - ); - - PVOID GetAppDomain; - PVOID GetNumArguments; - PVOID GetArgumentByIndex; - PVOID GetNumLocalVariables; - PVOID GetLocalVariableByIndex; - - HRESULT (STDMETHODCALLTYPE *GetCodeName)( - _In_ IXCLRDataFrame *This, - _In_ ULONG32 *flags, - _In_ ULONG32 *bufLen, - _Out_ ULONG32 *nameLen, - _Out_ WCHAR *nameBuf - ); -} IXCLRDataFrameVtbl; - -typedef struct IXCLRDataFrame -{ - IXCLRDataFrameVtbl *lpVtbl; -} IXCLRDataFrame; - -#define IXCLRDataFrame_QueryInterface(This, riid, ppvObject) \ - ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) - -#define IXCLRDataFrame_AddRef(This) \ - ((This)->lpVtbl->AddRef(This)) - -#define IXCLRDataFrame_Release(This) \ - ((This)->lpVtbl->Release(This)) - -#define IXCLRDataFrame_GetFrameType(This, simpleType, detailedType) \ - ((This)->lpVtbl->GetFrameType(This, simpleType, detailedType)) - -#define IXCLRDataFrame_GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf) \ - ((This)->lpVtbl->GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf)) - -#define IXCLRDataFrame_GetCodeName(This, flags, bufLen, nameLen, nameBuf) \ - ((This)->lpVtbl->GetCodeName(This, flags, bufLen, nameLen, nameBuf)) - -// DnCLRDataTarget - -typedef struct -{ - ICLRDataTargetVtbl *VTable; - - ULONG RefCount; - - HANDLE ProcessId; - HANDLE ProcessHandle; - BOOLEAN IsWow64; -} DnCLRDataTarget; - -ICLRDataTarget *DnCLRDataTarget_Create( - _In_ HANDLE ProcessId - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_QueryInterface( - _In_ ICLRDataTarget *This, - _In_ REFIID Riid, - _Out_ PVOID *Object - ); - -ULONG STDMETHODCALLTYPE DnCLRDataTarget_AddRef( - _In_ ICLRDataTarget *This - ); - -ULONG STDMETHODCALLTYPE DnCLRDataTarget_Release( - _In_ ICLRDataTarget *This - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetMachineType( - _In_ ICLRDataTarget *This, - _Out_ ULONG32 *machineType - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetPointerSize( - _In_ ICLRDataTarget *This, - _Out_ ULONG32 *pointerSize - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetImageBase( - _In_ ICLRDataTarget *This, - _In_ LPCWSTR imagePath, - _Out_ CLRDATA_ADDRESS *baseAddress - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_ReadVirtual( - _In_ ICLRDataTarget *This, - _In_ CLRDATA_ADDRESS address, - _Out_ BYTE *buffer, - _In_ ULONG32 bytesRequested, - _Out_ ULONG32 *bytesRead - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_WriteVirtual( - _In_ ICLRDataTarget *This, - _In_ CLRDATA_ADDRESS address, - _In_ BYTE *buffer, - _In_ ULONG32 bytesRequested, - _Out_ ULONG32 *bytesWritten - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetTLSValue( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 index, - _Out_ CLRDATA_ADDRESS *value - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetTLSValue( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 index, - _In_ CLRDATA_ADDRESS value - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetCurrentThreadID( - _In_ ICLRDataTarget *This, - _Out_ ULONG32 *threadID - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetThreadContext( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 contextFlags, - _In_ ULONG32 contextSize, - _Out_ BYTE *context - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetThreadContext( - _In_ ICLRDataTarget *This, - _In_ ULONG32 threadID, - _In_ ULONG32 contextSize, - _In_ BYTE *context - ); - -HRESULT STDMETHODCALLTYPE DnCLRDataTarget_Request( - _In_ ICLRDataTarget *This, - _In_ ULONG32 reqCode, - _In_ ULONG32 inBufferSize, - _In_ BYTE *inBuffer, - _In_ ULONG32 outBufferSize, - _Out_ BYTE *outBuffer - ); - -typedef struct _PHP_GET_IMAGE_BASE_CONTEXT -{ - UNICODE_STRING ImagePath; - PVOID BaseAddress; -} PHP_GET_IMAGE_BASE_CONTEXT, *PPHP_GET_IMAGE_BASE_CONTEXT; - -#endif +/* + * Process Hacker .NET Tools - + * CLR data access functions + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef CLRSUP_H +#define CLRSUP_H + +#include "clr/clrdata.h" + +// General interfaces + +typedef struct _CLR_PROCESS_SUPPORT +{ + struct IXCLRDataProcess *DataProcess; +} CLR_PROCESS_SUPPORT, *PCLR_PROCESS_SUPPORT; + +PCLR_PROCESS_SUPPORT CreateClrProcessSupport( + _In_ HANDLE ProcessId + ); + +VOID FreeClrProcessSupport( + _In_ PCLR_PROCESS_SUPPORT Support + ); + +PPH_STRING GetRuntimeNameByAddressClrProcess( + _In_ PCLR_PROCESS_SUPPORT Support, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement + ); + +PPH_STRING GetNameXClrDataAppDomain( + _In_ PVOID AppDomain + ); + +PVOID LoadMscordacwks( + _In_ BOOLEAN IsClrV4 + ); + +HRESULT CreateXCLRDataProcess( + _In_ HANDLE ProcessId, + _In_ ICLRDataTarget *Target, + _Out_ struct IXCLRDataProcess **DataProcess + ); + +// xclrdata + +typedef ULONG64 CLRDATA_ENUM; + +typedef struct IXCLRDataProcess IXCLRDataProcess; +typedef struct IXCLRDataAppDomain IXCLRDataAppDomain; +typedef struct IXCLRDataTask IXCLRDataTask; +typedef struct IXCLRDataStackWalk IXCLRDataStackWalk; +typedef struct IXCLRDataFrame IXCLRDataFrame; + +typedef struct IXCLRDataProcessVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + _In_ IXCLRDataProcess *This, + _In_ REFIID riid, + _Outptr_ void **ppvObject + ); + + ULONG (STDMETHODCALLTYPE *AddRef)( + _In_ IXCLRDataProcess *This + ); + + ULONG (STDMETHODCALLTYPE *Release)( + _In_ IXCLRDataProcess *This + ); + + HRESULT (STDMETHODCALLTYPE *Flush)( + _In_ IXCLRDataProcess *This + ); + + HRESULT (STDMETHODCALLTYPE *StartEnumTasks)( + _In_ IXCLRDataProcess *This, + _Out_ CLRDATA_ENUM *handle + ); + + HRESULT (STDMETHODCALLTYPE *EnumTask)( + _In_ IXCLRDataProcess *This, + _Inout_ CLRDATA_ENUM *handle, + _Out_ IXCLRDataTask **task + ); + + HRESULT (STDMETHODCALLTYPE *EndEnumTasks)( + _In_ IXCLRDataProcess *This, + _In_ CLRDATA_ENUM handle + ); + + HRESULT (STDMETHODCALLTYPE *GetTaskByOSThreadID)( + _In_ IXCLRDataProcess *This, + _In_ ULONG32 osThreadID, + _Out_ IXCLRDataTask **task + ); + + PVOID GetTaskByUniqueID; + PVOID GetFlags; + PVOID IsSameObject; + PVOID GetManagedObject; + PVOID GetDesiredExecutionState; + PVOID SetDesiredExecutionState; + PVOID GetAddressType; + + HRESULT (STDMETHODCALLTYPE *GetRuntimeNameByAddress)( + _In_ IXCLRDataProcess *This, + _In_ CLRDATA_ADDRESS address, + _In_ ULONG32 flags, + _In_ ULONG32 bufLen, + _Out_ ULONG32 *nameLen, + _Out_ WCHAR *nameBuf, + _Out_ CLRDATA_ADDRESS *displacement + ); + + // ... +} IXCLRDataProcessVtbl; + +typedef struct IXCLRDataProcess +{ + struct IXCLRDataProcessVtbl *lpVtbl; +} IXCLRDataProcess; + +#define IXCLRDataProcess_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) + +#define IXCLRDataProcess_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) + +#define IXCLRDataProcess_Release(This) \ + ((This)->lpVtbl->Release(This)) + +#define IXCLRDataProcess_GetRuntimeNameByAddress(This, address, flags, bufLen, nameLen, nameBuf, displacement) \ + ((This)->lpVtbl->GetRuntimeNameByAddress(This, address, flags, bufLen, nameLen, nameBuf, displacement)) + +#define IXCLRDataProcess_Flush(This) \ + ((This)->lpVtbl->Flush(This)) + +#define IXCLRDataProcess_StartEnumTasks(This, handle) \ + ((This)->lpVtbl->StartEnumTasks(This, handle)) + +#define IXCLRDataProcess_EnumTask(This, handle, task) \ + ((This)->lpVtbl->EnumTask(This, handle, task)) + +#define IXCLRDataProcess_EndEnumTasks(This, handle) \ + ((This)->lpVtbl->EndEnumTasks(This, handle)) + +#define IXCLRDataProcess_GetTaskByOSThreadID(This, osThreadID, task) \ + ((This)->lpVtbl->GetTaskByOSThreadID(This, osThreadID, task)) + +typedef struct IXCLRDataAppDomainVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + _In_ IXCLRDataAppDomain *This, + _In_ REFIID riid, + _Outptr_ void **ppvObject + ); + + ULONG (STDMETHODCALLTYPE *AddRef)( + _In_ IXCLRDataAppDomain *This + ); + + ULONG (STDMETHODCALLTYPE *Release)( + _In_ IXCLRDataAppDomain *This + ); + + HRESULT (STDMETHODCALLTYPE *GetProcess)( + _In_ IXCLRDataAppDomain *This, + _Out_ IXCLRDataProcess **process + ); + + HRESULT (STDMETHODCALLTYPE *GetName)( + _In_ IXCLRDataAppDomain *This, + _In_ ULONG32 bufLen, + _Out_ ULONG32 *nameLen, + _Out_ WCHAR *name + ); + + HRESULT (STDMETHODCALLTYPE *GetUniqueID)( + _In_ IXCLRDataAppDomain *This, + _Out_ ULONG64 *id + ); + + // ... +} IXCLRDataAppDomainVtbl; + +typedef struct IXCLRDataAppDomain +{ + struct IXCLRDataAppDomainVtbl *lpVtbl; +} IXCLRDataAppDomain; + +#define IXCLRDataAppDomain_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) + +#define IXCLRDataAppDomain_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) + +#define IXCLRDataAppDomain_Release(This) \ + ((This)->lpVtbl->Release(This)) + +#define IXCLRDataAppDomain_GetProcess(This, process) \ + ((This)->lpVtbl->GetProcess(This, process)) + +#define IXCLRDataAppDomain_GetName(This, bufLen, nameLen, name) \ + ((This)->lpVtbl->GetName(This, bufLen, nameLen, name)) + +#define IXCLRDataAppDomain_GetUniqueID(This, id) \ + ((This)->lpVtbl->GetUniqueID(This, id)) + +typedef struct IXCLRDataTaskVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + _In_ IXCLRDataTask *This, + _In_ REFIID riid, + _Outptr_ void **ppvObject + ); + + ULONG (STDMETHODCALLTYPE *AddRef)( + _In_ IXCLRDataTask *This + ); + + ULONG (STDMETHODCALLTYPE *Release)( + _In_ IXCLRDataTask *This + ); + + HRESULT (STDMETHODCALLTYPE *GetProcess)( + _In_ IXCLRDataTask *This, + _Out_ IXCLRDataProcess **process + ); + + HRESULT (STDMETHODCALLTYPE *GetCurrentAppDomain)( + _In_ IXCLRDataTask *This, + _Out_ IXCLRDataAppDomain **appDomain + ); + + HRESULT (STDMETHODCALLTYPE *GetUniqueID)( + _In_ IXCLRDataTask *This, + _Out_ ULONG64 *id + ); + + HRESULT (STDMETHODCALLTYPE *GetFlags)( + _In_ IXCLRDataTask *This, + _Out_ ULONG32 *flags + ); + + PVOID IsSameObject; + PVOID GetManagedObject; + PVOID GetDesiredExecutionState; + PVOID SetDesiredExecutionState; + + HRESULT (STDMETHODCALLTYPE *CreateStackWalk)( + _In_ IXCLRDataTask *This, + _In_ ULONG32 flags, + _Out_ IXCLRDataStackWalk **stackWalk + ); + + HRESULT (STDMETHODCALLTYPE *GetOSThreadID)( + _In_ IXCLRDataTask *This, + _Out_ ULONG32 *id + ); + + PVOID GetContext; + PVOID SetContext; + PVOID GetCurrentExceptionState; + PVOID Request; + + HRESULT (STDMETHODCALLTYPE *GetName)( + _In_ IXCLRDataTask *This, + _In_ ULONG32 bufLen, + _Out_ ULONG32 *nameLen, + _Out_ WCHAR *name + ); + + PVOID GetLastExceptionState; +} IXCLRDataTaskVtbl; + +typedef struct IXCLRDataTask +{ + struct IXCLRDataTaskVtbl *lpVtbl; +} IXCLRDataTask; + +#define IXCLRDataTask_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) + +#define IXCLRDataTask_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) + +#define IXCLRDataTask_Release(This) \ + ((This)->lpVtbl->Release(This)) + +#define IXCLRDataTask_GetProcess(This, process) \ + ((This)->lpVtbl->GetProcess(This, process)) + +#define IXCLRDataTask_GetCurrentAppDomain(This, appDomain) \ + ((This)->lpVtbl->GetCurrentAppDomain(This, appDomain)) + +#define IXCLRDataTask_GetUniqueID(This, id) \ + ((This)->lpVtbl->GetUniqueID(This, id)) + +#define IXCLRDataTask_GetFlags(This, flags) \ + ((This)->lpVtbl->GetFlags(This, flags)) + +#define IXCLRDataTask_CreateStackWalk(This, flags, stackWalk) \ + ((This)->lpVtbl->CreateStackWalk(This, flags, stackWalk)) + +#define IXCLRDataTask_GetOSThreadID(This, id) \ + ((This)->lpVtbl->GetOSThreadID(This, id)) + +#define IXCLRDataTask_GetName(This, bufLen, nameLen, name) \ + ((This)->lpVtbl->GetName(This, bufLen, nameLen, name)) + +typedef enum +{ + CLRDATA_SIMPFRAME_UNRECOGNIZED = 0x1, + CLRDATA_SIMPFRAME_MANAGED_METHOD = 0x2, + CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE = 0x4, + CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE = 0x8 +} CLRDataSimpleFrameType; + +typedef enum +{ + CLRDATA_DETFRAME_UNRECOGNIZED, + CLRDATA_DETFRAME_UNKNOWN_STUB, + CLRDATA_DETFRAME_CLASS_INIT, + CLRDATA_DETFRAME_EXCEPTION_FILTER, + CLRDATA_DETFRAME_SECURITY, + CLRDATA_DETFRAME_CONTEXT_POLICY, + CLRDATA_DETFRAME_INTERCEPTION, + CLRDATA_DETFRAME_PROCESS_START, + CLRDATA_DETFRAME_THREAD_START, + CLRDATA_DETFRAME_TRANSITION_TO_MANAGED, + CLRDATA_DETFRAME_TRANSITION_TO_UNMANAGED, + CLRDATA_DETFRAME_COM_INTEROP_STUB, + CLRDATA_DETFRAME_DEBUGGER_EVAL, + CLRDATA_DETFRAME_CONTEXT_SWITCH, + CLRDATA_DETFRAME_FUNC_EVAL, + CLRDATA_DETFRAME_FINALLY +} CLRDataDetailedFrameType; + +typedef enum +{ + CLRDATA_STACK_SET_UNWIND_CONTEXT = 0x00000000, + CLRDATA_STACK_SET_CURRENT_CONTEXT = 0x00000001 +} CLRDataStackSetContextFlag; + +typedef struct IXCLRDataStackWalkVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + _In_ IXCLRDataStackWalk *This, + _In_ REFIID riid, + _Outptr_ void **ppvObject + ); + + ULONG (STDMETHODCALLTYPE *AddRef)( + _In_ IXCLRDataStackWalk *This + ); + + ULONG (STDMETHODCALLTYPE *Release)( + _In_ IXCLRDataStackWalk *This + ); + + HRESULT (STDMETHODCALLTYPE *GetContext)( + _In_ IXCLRDataStackWalk *This, + _In_ ULONG32 contextFlags, + _In_ ULONG32 contextBufSize, + _Out_ ULONG32 *contextSize, + _Out_ BYTE *contextBuf + ); + + PVOID SetContext; + + HRESULT (STDMETHODCALLTYPE *Next)( + _In_ IXCLRDataStackWalk *This + ); + + HRESULT (STDMETHODCALLTYPE *GetStackSizeSkipped)( + _In_ IXCLRDataStackWalk *This, + _Out_ ULONG64 *stackSizeSkipped + ); + + HRESULT (STDMETHODCALLTYPE *GetFrameType)( + _In_ IXCLRDataStackWalk *This, + _Out_ CLRDataSimpleFrameType *simpleType, + _Out_ CLRDataDetailedFrameType *detailedType + ); + + HRESULT (STDMETHODCALLTYPE *GetFrame)( + _In_ IXCLRDataStackWalk *This, + _Out_ PVOID *frame + ); + + HRESULT (STDMETHODCALLTYPE *Request)( + _In_ IXCLRDataStackWalk *This, + _In_ ULONG32 reqCode, + _In_ ULONG32 inBufferSize, + _In_ BYTE *inBuffer, + _In_ ULONG32 outBufferSize, + _Out_ BYTE *outBuffer + ); + + HRESULT (STDMETHODCALLTYPE *SetContext2)( + _In_ IXCLRDataStackWalk *This, + _In_ ULONG32 flags, + _In_ ULONG32 contextSize, + _In_ BYTE *context + ); +} IXCLRDataStackWalkVtbl; + +typedef struct IXCLRDataStackWalk +{ + struct IXCLRDataStackWalkVtbl *lpVtbl; +} IXCLRDataStackWalk; + +#define IXCLRDataStackWalk_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) + +#define IXCLRDataStackWalk_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) + +#define IXCLRDataStackWalk_Release(This) \ + ((This)->lpVtbl->Release(This)) + +#define IXCLRDataStackWalk_GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf) \ + ((This)->lpVtbl->GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf)) + +#define IXCLRDataStackWalk_Next(This) \ + ((This)->lpVtbl->Next(This)) + +#define IXCLRDataStackWalk_GetStackSizeSkipped(This, stackSizeSkipped) \ + ((This)->lpVtbl->GetStackSizeSkipped(This, stackSizeSkipped)) + +#define IXCLRDataStackWalk_GetFrameType(This, simpleType, detailedType) \ + ((This)->lpVtbl->GetFrameType(This, simpleType, detailedType)) + +#define IXCLRDataStackWalk_GetFrame(This, frame) \ + ((This)->lpVtbl->GetFrame(This, frame)) + +#define IXCLRDataStackWalk_Request(This, reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) \ + ((This)->lpVtbl->SetContext2(This, reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer)) + +#define IXCLRDataStackWalk_SetContext2(This, flags, contextSize, context) \ + ((This)->lpVtbl->SetContext2(This, flags, contextSize, context)) + +typedef struct IXCLRDataFrameVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + _In_ IXCLRDataFrame *This, + _In_ REFIID riid, + _Outptr_ void **ppvObject + ); + + ULONG (STDMETHODCALLTYPE *AddRef)( + _In_ IXCLRDataFrame *This + ); + + ULONG (STDMETHODCALLTYPE *Release)( + _In_ IXCLRDataFrame *This + ); + + HRESULT (STDMETHODCALLTYPE *GetFrameType)( + _In_ IXCLRDataFrame *This, + _Out_ CLRDataSimpleFrameType *simpleType, + _Out_ CLRDataDetailedFrameType *detailedType + ); + + HRESULT (STDMETHODCALLTYPE *GetContext)( + _In_ IXCLRDataFrame *This, + _In_ ULONG32 contextFlags, + _In_ ULONG32 contextBufSize, + _Out_ ULONG32 *contextSize, + _Out_ BYTE *contextBuf + ); + + PVOID GetAppDomain; + PVOID GetNumArguments; + PVOID GetArgumentByIndex; + PVOID GetNumLocalVariables; + PVOID GetLocalVariableByIndex; + + HRESULT (STDMETHODCALLTYPE *GetCodeName)( + _In_ IXCLRDataFrame *This, + _In_ ULONG32 *flags, + _In_ ULONG32 *bufLen, + _Out_ ULONG32 *nameLen, + _Out_ WCHAR *nameBuf + ); +} IXCLRDataFrameVtbl; + +typedef struct IXCLRDataFrame +{ + IXCLRDataFrameVtbl *lpVtbl; +} IXCLRDataFrame; + +#define IXCLRDataFrame_QueryInterface(This, riid, ppvObject) \ + ((This)->lpVtbl->QueryInterface(This, riid, ppvObject)) + +#define IXCLRDataFrame_AddRef(This) \ + ((This)->lpVtbl->AddRef(This)) + +#define IXCLRDataFrame_Release(This) \ + ((This)->lpVtbl->Release(This)) + +#define IXCLRDataFrame_GetFrameType(This, simpleType, detailedType) \ + ((This)->lpVtbl->GetFrameType(This, simpleType, detailedType)) + +#define IXCLRDataFrame_GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf) \ + ((This)->lpVtbl->GetContext(This, contextFlags, contextBufSize, contextSize, contextBuf)) + +#define IXCLRDataFrame_GetCodeName(This, flags, bufLen, nameLen, nameBuf) \ + ((This)->lpVtbl->GetCodeName(This, flags, bufLen, nameLen, nameBuf)) + +// DnCLRDataTarget + +typedef struct +{ + ICLRDataTargetVtbl *VTable; + + ULONG RefCount; + + HANDLE ProcessId; + HANDLE ProcessHandle; + BOOLEAN IsWow64; +} DnCLRDataTarget; + +ICLRDataTarget *DnCLRDataTarget_Create( + _In_ HANDLE ProcessId + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_QueryInterface( + _In_ ICLRDataTarget *This, + _In_ REFIID Riid, + _Out_ PVOID *Object + ); + +ULONG STDMETHODCALLTYPE DnCLRDataTarget_AddRef( + _In_ ICLRDataTarget *This + ); + +ULONG STDMETHODCALLTYPE DnCLRDataTarget_Release( + _In_ ICLRDataTarget *This + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetMachineType( + _In_ ICLRDataTarget *This, + _Out_ ULONG32 *machineType + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetPointerSize( + _In_ ICLRDataTarget *This, + _Out_ ULONG32 *pointerSize + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetImageBase( + _In_ ICLRDataTarget *This, + _In_ LPCWSTR imagePath, + _Out_ CLRDATA_ADDRESS *baseAddress + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_ReadVirtual( + _In_ ICLRDataTarget *This, + _In_ CLRDATA_ADDRESS address, + _Out_ BYTE *buffer, + _In_ ULONG32 bytesRequested, + _Out_ ULONG32 *bytesRead + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_WriteVirtual( + _In_ ICLRDataTarget *This, + _In_ CLRDATA_ADDRESS address, + _In_ BYTE *buffer, + _In_ ULONG32 bytesRequested, + _Out_ ULONG32 *bytesWritten + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetTLSValue( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 index, + _Out_ CLRDATA_ADDRESS *value + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetTLSValue( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 index, + _In_ CLRDATA_ADDRESS value + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetCurrentThreadID( + _In_ ICLRDataTarget *This, + _Out_ ULONG32 *threadID + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetThreadContext( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 contextFlags, + _In_ ULONG32 contextSize, + _Out_ BYTE *context + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetThreadContext( + _In_ ICLRDataTarget *This, + _In_ ULONG32 threadID, + _In_ ULONG32 contextSize, + _In_ BYTE *context + ); + +HRESULT STDMETHODCALLTYPE DnCLRDataTarget_Request( + _In_ ICLRDataTarget *This, + _In_ ULONG32 reqCode, + _In_ ULONG32 inBufferSize, + _In_ BYTE *inBuffer, + _In_ ULONG32 outBufferSize, + _Out_ BYTE *outBuffer + ); + +typedef struct _PHP_GET_IMAGE_BASE_CONTEXT +{ + UNICODE_STRING ImagePath; + PVOID BaseAddress; +} PHP_GET_IMAGE_BASE_CONTEXT, *PPHP_GET_IMAGE_BASE_CONTEXT; + +#endif diff --git a/plugins/DotNetTools/counters.c b/plugins/DotNetTools/counters.c index bce3983bf243..4bf0a4dbf289 100644 --- a/plugins/DotNetTools/counters.c +++ b/plugins/DotNetTools/counters.c @@ -1,929 +1,902 @@ -/* - * Process Hacker .NET Tools - - * IPC support functions - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" -#include "clr/dbgappdomain.h" -#include "clr/ipcheader.h" -#include "clr/ipcshared.h" - -PPH_STRING GeneratePrivateName(_In_ HANDLE ProcessId) -{ - return PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPrivateIPCBlock, HandleToUlong(ProcessId)); -} - -PPH_STRING GeneratePrivateNameV4(_In_ HANDLE ProcessId) -{ - return PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPrivateIPCBlockTempV4, HandleToUlong(ProcessId)); -} - -PPH_STRING GenerateLegacyPublicName(_In_ HANDLE ProcessId) -{ - return PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPublicIPCBlock, HandleToUlong(ProcessId)); -} - -PPH_STRING GenerateBoundaryDescriptorName(_In_ HANDLE ProcessId) -{ - return PhaFormatString(CorSxSBoundaryDescriptor, HandleToUlong(ProcessId)); -} - -PVOID GetLegacyBlockTableEntry( - _In_ BOOLEAN Wow64, - _In_ PVOID IpcBlockAddress, - _In_ ULONG EntryId - ) -{ - if (Wow64) - { - LegacyPrivateIPCControlBlock_Wow64* IpcBlock = IpcBlockAddress; - - // skip over directory (variable size) - ULONG offsetBase = IPC_ENTRY_OFFSET_BASE_X86 + IpcBlock->FullIPCHeader.Header.NumEntries * sizeof(IPCEntry); - // Directory has offset in bytes of block - ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset; - - return ((PBYTE)IpcBlock) + offsetBase + offsetEntry; - } - else - { - LegacyPrivateIPCControlBlock* IpcBlock = IpcBlockAddress; - - // skip over directory (variable size) - ULONG offsetBase = IPC_ENTRY_OFFSET_BASE_X64 + IpcBlock->FullIPCHeader.Header.NumEntries * sizeof(IPCEntry); - // Directory has offset in bytes of block - ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset; - - return ((PBYTE)IpcBlock) + offsetBase + offsetEntry; - } -} - -PVOID GetPerfIpcBlock_V2( - _In_ BOOLEAN Wow64, - _In_ PVOID BlockTableAddress - ) -{ - if (Wow64) - { - LegacyPublicIPCControlBlock_Wow64* ipcLegacyBlockTable_Wow64 = BlockTableAddress; - - if (ipcLegacyBlockTable_Wow64->FullIPCHeaderLegacyPublic.Header.Version > VER_LEGACYPUBLIC_IPC_BLOCK) - { - return NULL; - } - - return &ipcLegacyBlockTable_Wow64->PerfIpcBlock; - } - else - { - LegacyPublicIPCControlBlock* ipcLegacyBlockTable = BlockTableAddress; - - if (ipcLegacyBlockTable->FullIPCHeaderLegacyPublic.Header.Version > VER_IPC_BLOCK) - { - return NULL; - } - - return &ipcLegacyBlockTable->PerfIpcBlock; - } -} - -PVOID GetPerfIpcBlock_V4( - _In_ BOOLEAN Wow64, - _In_ PVOID BlockTableAddress - ) -{ - if (Wow64) - { - IPCControlBlockTable_Wow64* ipcBlockTable_Wow64 = BlockTableAddress; - - if (ipcBlockTable_Wow64->Blocks->Header.Version > VER_IPC_BLOCK) - { - return NULL; - } - - return &ipcBlockTable_Wow64->Blocks->PerfIpcBlock; - } - else - { - IPCControlBlockTable_Wow64* ipcBlockTable = BlockTableAddress; - - if (ipcBlockTable->Blocks->Header.Version > VER_IPC_BLOCK) - { - return NULL; - } - - return &ipcBlockTable->Blocks->PerfIpcBlock; - } -} - -PPH_LIST EnumerateAppDomainIpcBlock( - _In_ HANDLE ProcessHandle, - _In_ AppDomainEnumerationIPCBlock* AppDomainIpcBlock - ) -{ - LARGE_INTEGER timeout; - SIZE_T appDomainInfoBlockLength; - HANDLE legacyPrivateBlockMutexHandle = NULL; - AppDomainEnumerationIPCBlock tempBlock; - AppDomainInfo* appDomainInfoBlock = NULL; - PPH_LIST appDomainsList = PhCreateList(1); - - // If the mutex isn't filled in, the CLR is either starting up or shutting down - if (!AppDomainIpcBlock->Mutex) - { - goto CleanupExit; - } - - // Dup the valid mutex handle into this process. - if (!NT_SUCCESS(NtDuplicateObject( - ProcessHandle, - AppDomainIpcBlock->Mutex, - NtCurrentProcess(), - &legacyPrivateBlockMutexHandle, - GENERIC_ALL, - 0, - DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES - ))) - { - goto CleanupExit; - } - - // Acquire the mutex, only waiting two seconds. - // We can't actually gaurantee that the target put a mutex object in here. - if (NtWaitForSingleObject( - legacyPrivateBlockMutexHandle, - FALSE, - PhTimeoutFromMilliseconds(&timeout, 2000) - ) == STATUS_WAIT_0) - { - // Make sure the mutex handle is still valid. If its not, then we lost a shutdown race. - if (!AppDomainIpcBlock->Mutex) - { - goto CleanupExit; - } - } - else - { - // Again, landing here is most probably a shutdown race. - goto CleanupExit; - } - - // Beware: If the target pid is not properly honoring the mutex, the data in the IPC block may still shift underneath us. - // If we get here, then hMutex is held by this process. - - // Make a copy of the IPC block so that we can gaurantee that it's not changing on us. - memcpy(&tempBlock, AppDomainIpcBlock, sizeof(AppDomainEnumerationIPCBlock)); - - // It's possible the process will not have any appdomains. - if ((tempBlock.ListOfAppDomains == NULL) != (tempBlock.SizeInBytes == 0)) - { - goto CleanupExit; - } - - // All the data in the IPC block is signed integers. They should never be negative, - // so check that now. - if ((tempBlock.TotalSlots < 0) || - (tempBlock.NumOfUsedSlots < 0) || - (tempBlock.LastFreedSlot < 0) || - (tempBlock.SizeInBytes < 0) || - (tempBlock.ProcessNameLengthInBytes < 0)) - { - goto CleanupExit; - } - - // Allocate memory to read the remote process' memory into - appDomainInfoBlockLength = tempBlock.SizeInBytes; - - // Check other invariants. - if (appDomainInfoBlockLength != tempBlock.TotalSlots * sizeof(AppDomainInfo)) - { - goto CleanupExit; - } - - appDomainInfoBlock = (AppDomainInfo*)PhAllocate(appDomainInfoBlockLength); - memset(appDomainInfoBlock, 0, appDomainInfoBlockLength); - - if (!NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - tempBlock.ListOfAppDomains, - appDomainInfoBlock, - appDomainInfoBlockLength, - NULL - ))) - { - PhFree(appDomainInfoBlock); - goto CleanupExit; - } - - // Collect all the AppDomain names into a list of strings. - for (INT i = 0; i < tempBlock.NumOfUsedSlots; i++) - { - SIZE_T appDomainNameLength; - PVOID appDomainName; - - if (!appDomainInfoBlock[i].AppDomainName) - continue; - - // Should be positive, and at least have a null-terminator character. - if (appDomainInfoBlock[i].NameLengthInBytes <= 1) - continue; - - // Make sure buffer has right geometry. - if (appDomainInfoBlock[i].NameLengthInBytes < 0) - continue; - - // If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow. - appDomainNameLength = appDomainInfoBlock[i].NameLengthInBytes / sizeof(WCHAR); - - if ((appDomainNameLength * sizeof(WCHAR)) != appDomainInfoBlock[i].NameLengthInBytes) - continue; - - // It should at least have 1 char for the null terminator. - if (appDomainNameLength < 1) - continue; - - // We know the string is a well-formed null-terminated string, - // but beyond that, we can't verify that the data is actually truthful. - appDomainName = PhAllocate(appDomainInfoBlock[i].NameLengthInBytes + 1); - memset(appDomainName, 0, appDomainInfoBlock[i].NameLengthInBytes + 1); - - if (!NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - appDomainInfoBlock[i].AppDomainName, - appDomainName, - appDomainInfoBlock[i].NameLengthInBytes, - NULL - ))) - { - PhFree(appDomainName); - continue; - } - - PhAddItemList(appDomainsList, appDomainName); - } - -CleanupExit: - if (appDomainInfoBlock) - { - PhFree(appDomainInfoBlock); - } - - if (legacyPrivateBlockMutexHandle) - { - NtReleaseMutant(legacyPrivateBlockMutexHandle, NULL); - NtClose(legacyPrivateBlockMutexHandle); - } - - return appDomainsList; -} - -PPH_LIST EnumerateAppDomainIpcBlockWow64( - _In_ HANDLE ProcessHandle, - _In_ AppDomainEnumerationIPCBlock_Wow64* AppDomainIpcBlock - ) -{ - LARGE_INTEGER timeout; - SIZE_T appDomainInfoBlockLength; - HANDLE legacyPrivateBlockMutexHandle = NULL; - AppDomainEnumerationIPCBlock_Wow64 tempBlock; - AppDomainInfo_Wow64* appDomainInfoBlock = NULL; - PPH_LIST appDomainsList = PhCreateList(1); - - // If the mutex isn't filled in, the CLR is either starting up or shutting down - if (!AppDomainIpcBlock->Mutex) - { - goto CleanupExit; - } - - // Dup the valid mutex handle into this process. - if (!NT_SUCCESS(NtDuplicateObject( - ProcessHandle, - UlongToHandle(AppDomainIpcBlock->Mutex), - NtCurrentProcess(), - &legacyPrivateBlockMutexHandle, - GENERIC_ALL, - 0, - DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES - ))) - { - goto CleanupExit; - } - - // Acquire the mutex, only waiting two seconds. - // We can't actually gaurantee that the target put a mutex object in here. - if (NtWaitForSingleObject( - legacyPrivateBlockMutexHandle, - FALSE, - PhTimeoutFromMilliseconds(&timeout, 2000) - ) == STATUS_WAIT_0) - { - // Make sure the mutex handle is still valid. If its not, then we lost a shutdown race. - if (!AppDomainIpcBlock->Mutex) - { - goto CleanupExit; - } - } - else - { - // Again, landing here is most probably a shutdown race. - goto CleanupExit; - } - - // Beware: If the target pid is not properly honoring the mutex, the data in the IPC block may still shift underneath us. - // If we get here, then hMutex is held by this process. - - // Make a copy of the IPC block so that we can gaurantee that it's not changing on us. - memcpy(&tempBlock, AppDomainIpcBlock, sizeof(AppDomainEnumerationIPCBlock_Wow64)); - - // It's possible the process will not have any appdomains. - if ((tempBlock.ListOfAppDomains == 0) != (tempBlock.SizeInBytes == 0)) - { - goto CleanupExit; - } - - // All the data in the IPC block is signed integers. They should never be negative, - // so check that now. - if ((tempBlock.TotalSlots < 0) || - (tempBlock.NumOfUsedSlots < 0) || - (tempBlock.LastFreedSlot < 0) || - (tempBlock.SizeInBytes < 0) || - (tempBlock.ProcessNameLengthInBytes < 0)) - { - goto CleanupExit; - } - - // Allocate memory to read the remote process' memory into - appDomainInfoBlockLength = tempBlock.SizeInBytes; - - // Check other invariants. - if (appDomainInfoBlockLength != tempBlock.TotalSlots * sizeof(AppDomainInfo_Wow64)) - { - goto CleanupExit; - } - - appDomainInfoBlock = (AppDomainInfo_Wow64*)PhAllocate(appDomainInfoBlockLength); - memset(appDomainInfoBlock, 0, appDomainInfoBlockLength); - - if (!NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - UlongToPtr(tempBlock.ListOfAppDomains), - appDomainInfoBlock, - appDomainInfoBlockLength, - NULL - ))) - { - PhFree(appDomainInfoBlock); - goto CleanupExit; - } - - // Collect all the AppDomain names into a list of strings. - for (INT i = 0; i < tempBlock.NumOfUsedSlots; i++) - { - SIZE_T appDomainNameLength; - PVOID appDomainName; - - if (!appDomainInfoBlock[i].AppDomainName) - continue; - - // Should be positive, and at least have a null-terminator character. - if (appDomainInfoBlock[i].NameLengthInBytes <= 1) - continue; - - // Make sure buffer has right geometry. - if (appDomainInfoBlock[i].NameLengthInBytes < 0) - continue; - - // If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow. - appDomainNameLength = appDomainInfoBlock[i].NameLengthInBytes / sizeof(WCHAR); - - if ((appDomainNameLength * sizeof(WCHAR)) != appDomainInfoBlock[i].NameLengthInBytes) - continue; - - // It should at least have 1 char for the null terminator. - if (appDomainNameLength < 1) - continue; - - // We know the string is a well-formed null-terminated string, - // but beyond that, we can't verify that the data is actually truthful. - appDomainName = PhAllocate(appDomainInfoBlock[i].NameLengthInBytes + 1); - memset(appDomainName, 0, appDomainInfoBlock[i].NameLengthInBytes + 1); - - if (!NT_SUCCESS(NtReadVirtualMemory( - ProcessHandle, - UlongToPtr(appDomainInfoBlock[i].AppDomainName), - appDomainName, - appDomainInfoBlock[i].NameLengthInBytes, - NULL - ))) - { - PhFree(appDomainName); - continue; - } - - PhAddItemList(appDomainsList, appDomainName); - } - -CleanupExit: - - if (appDomainInfoBlock) - { - PhFree(appDomainInfoBlock); - } - - if (legacyPrivateBlockMutexHandle) - { - NtReleaseMutant(legacyPrivateBlockMutexHandle, NULL); - NtClose(legacyPrivateBlockMutexHandle); - } - - return appDomainsList; -} - -BOOLEAN OpenDotNetPublicControlBlock_V2( - _In_ HANDLE ProcessId, - _Out_ HANDLE* BlockTableHandle, - _Out_ PVOID* BlockTableAddress - ) -{ - BOOLEAN result = FALSE; - HANDLE blockTableHandle = NULL; - PVOID blockTableAddress = NULL; - UNICODE_STRING sectionNameUs; - OBJECT_ATTRIBUTES objectAttributes; - LARGE_INTEGER sectionOffset = { 0 }; - SIZE_T viewSize = 0; - - if (!PhStringRefToUnicodeString(&GenerateLegacyPublicName(ProcessId)->sr, §ionNameUs)) - return FALSE; - - InitializeObjectAttributes( - &objectAttributes, - §ionNameUs, - 0, - NULL, - NULL - ); - - if (!NT_SUCCESS(NtOpenSection( - &blockTableHandle, - SECTION_MAP_READ, - &objectAttributes - ))) - { - return FALSE; - } - - if (NT_SUCCESS(NtMapViewOfSection( - blockTableHandle, - NtCurrentProcess(), - &blockTableAddress, - 0, - viewSize, - §ionOffset, - &viewSize, - ViewShare, - 0, - PAGE_READONLY - ))) - { - *BlockTableHandle = blockTableHandle; - *BlockTableAddress = blockTableAddress; - - return TRUE; - } - - if (blockTableHandle) - NtClose(blockTableHandle); - - if (blockTableAddress) - NtUnmapViewOfSection(NtCurrentProcess(), blockTableAddress); - - return FALSE; -} - -BOOLEAN OpenDotNetPublicControlBlock_V4( - _In_ BOOLEAN IsImmersive, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId, - _Out_ HANDLE* BlockTableHandle, - _Out_ PVOID* BlockTableAddress - ) -{ - BOOLEAN result = FALSE; - PVOID boundaryDescriptorHandle = NULL; - HANDLE privateNamespaceHandle = NULL; - HANDLE blockTableHandle = NULL; - HANDLE tokenHandle = NULL; - PSID everyoneSIDHandle = NULL; - PVOID blockTableAddress = NULL; - LARGE_INTEGER sectionOffset = { 0 }; - SIZE_T viewSize = 0; - UNICODE_STRING prefixNameUs; - UNICODE_STRING sectionNameUs; - UNICODE_STRING boundaryNameUs; - OBJECT_ATTRIBUTES namespaceObjectAttributes; - OBJECT_ATTRIBUTES sectionObjectAttributes; - PTOKEN_APPCONTAINER_INFORMATION appContainerInfo = NULL; - SID_IDENTIFIER_AUTHORITY SIDWorldAuth = SECURITY_WORLD_SID_AUTHORITY; - - if (!PhStringRefToUnicodeString(&GenerateBoundaryDescriptorName(ProcessId)->sr, &boundaryNameUs)) - goto CleanupExit; - - if (!(boundaryDescriptorHandle = RtlCreateBoundaryDescriptor(&boundaryNameUs, 0))) - goto CleanupExit; - - if (!NT_SUCCESS(RtlAllocateAndInitializeSid(&SIDWorldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSIDHandle))) - goto CleanupExit; - - if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&boundaryDescriptorHandle, everyoneSIDHandle))) - goto CleanupExit; - - if (WINDOWS_HAS_IMMERSIVE && IsImmersive) - { - if (NT_SUCCESS(NtOpenProcessToken(&tokenHandle, TOKEN_QUERY, ProcessHandle))) - { - ULONG returnLength = 0; - - if (NtQueryInformationToken( - tokenHandle, - TokenAppContainerSid, - NULL, - 0, - &returnLength - ) != STATUS_BUFFER_TOO_SMALL) - { - goto CleanupExit; - } - - appContainerInfo = PhAllocate(returnLength); - - if (!NT_SUCCESS(NtQueryInformationToken( - tokenHandle, - TokenAppContainerSid, - appContainerInfo, - returnLength, - &returnLength - ))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&boundaryDescriptorHandle, appContainerInfo->TokenAppContainer))) - goto CleanupExit; - } - } - - RtlInitUnicodeString(&prefixNameUs, CorSxSReaderPrivateNamespacePrefix); - InitializeObjectAttributes( - &namespaceObjectAttributes, - &prefixNameUs, - OBJ_CASE_INSENSITIVE, - boundaryDescriptorHandle, - NULL - ); - - if (!NT_SUCCESS(NtOpenPrivateNamespace( - &privateNamespaceHandle, - MAXIMUM_ALLOWED, - &namespaceObjectAttributes, - boundaryDescriptorHandle - ))) - { - goto CleanupExit; - } - - RtlInitUnicodeString(§ionNameUs, CorSxSVistaPublicIPCBlock); - InitializeObjectAttributes( - §ionObjectAttributes, - §ionNameUs, - OBJ_CASE_INSENSITIVE, - privateNamespaceHandle, - NULL - ); - - if (!NT_SUCCESS(NtOpenSection( - &blockTableHandle, - SECTION_MAP_READ, - §ionObjectAttributes - ))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(NtMapViewOfSection( - blockTableHandle, - NtCurrentProcess(), - &blockTableAddress, - 0, - viewSize, - §ionOffset, - &viewSize, - ViewShare, - 0, - PAGE_READONLY - ))) - { - goto CleanupExit; - } - - *BlockTableHandle = blockTableHandle; - *BlockTableAddress = blockTableAddress; - - result = TRUE; - -CleanupExit: - - if (!result) - { - if (blockTableHandle) - { - NtClose(blockTableHandle); - } - - if (blockTableAddress) - { - NtUnmapViewOfSection(NtCurrentProcess(), blockTableAddress); - } - - *BlockTableHandle = NULL; - *BlockTableAddress = NULL; - } - - if (tokenHandle) - { - NtClose(tokenHandle); - } - - if (appContainerInfo) - { - PhFree(appContainerInfo); - } - - if (privateNamespaceHandle) - { - NtClose(privateNamespaceHandle); - } - - if (everyoneSIDHandle) - { - RtlFreeSid(everyoneSIDHandle); - } - - if (boundaryDescriptorHandle) - { - RtlDeleteBoundaryDescriptor(boundaryDescriptorHandle); - } - - return result; -} - -PPH_LIST QueryDotNetAppDomainsForPid_V2( - _In_ BOOLEAN Wow64, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId - ) -{ - LARGE_INTEGER sectionOffset = { 0 }; - SIZE_T viewSize = 0; - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING sectionNameUs; - HANDLE legacyPrivateBlockHandle = NULL; - PVOID ipcControlBlockTable = NULL; - PPH_LIST appDomainsList = NULL; - - if (!PhStringRefToUnicodeString(&GeneratePrivateName(ProcessId)->sr, §ionNameUs)) - goto CleanupExit; - - InitializeObjectAttributes( - &objectAttributes, - §ionNameUs, - 0, - NULL, - NULL - ); - - if (!NT_SUCCESS(NtOpenSection( - &legacyPrivateBlockHandle, - SECTION_MAP_READ, - &objectAttributes - ))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(NtMapViewOfSection( - legacyPrivateBlockHandle, - NtCurrentProcess(), - &ipcControlBlockTable, - 0, - viewSize, - §ionOffset, - &viewSize, - ViewShare, - 0, - PAGE_READONLY - ))) - { - goto CleanupExit; - } - - if (Wow64) - { - LegacyPrivateIPCControlBlock_Wow64* legacyPrivateBlock; - AppDomainEnumerationIPCBlock_Wow64* appDomainEnumBlock; - - legacyPrivateBlock = (LegacyPrivateIPCControlBlock_Wow64*)ipcControlBlockTable; - - // NOTE: .NET 2.0 processes do not have the IPC_FLAG_INITIALIZED flag. - - // Check the IPCControlBlock version is valid. - if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) - { - goto CleanupExit; - } - - appDomainEnumBlock = GetLegacyBlockTableEntry( - Wow64, - ipcControlBlockTable, - eLegacyPrivateIPC_AppDomain - ); - - appDomainsList = EnumerateAppDomainIpcBlockWow64( - ProcessHandle, - appDomainEnumBlock - ); - } - else - { - LegacyPrivateIPCControlBlock* legacyPrivateBlock; - AppDomainEnumerationIPCBlock* appDomainEnumBlock; - - legacyPrivateBlock = (LegacyPrivateIPCControlBlock*)ipcControlBlockTable; - - // NOTE: .NET 2.0 processes do not have the IPC_FLAG_INITIALIZED flag. - - // Check the IPCControlBlock version is valid. - if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) - { - goto CleanupExit; - } - - appDomainEnumBlock = GetLegacyBlockTableEntry( - Wow64, - ipcControlBlockTable, - eLegacyPrivateIPC_AppDomain - ); - - appDomainsList = EnumerateAppDomainIpcBlock( - ProcessHandle, - appDomainEnumBlock - ); - } - -CleanupExit: - - if (ipcControlBlockTable) - { - NtUnmapViewOfSection(NtCurrentProcess(), ipcControlBlockTable); - } - - if (legacyPrivateBlockHandle) - { - NtClose(legacyPrivateBlockHandle); - } - - return appDomainsList; -} - -PPH_LIST QueryDotNetAppDomainsForPid_V4( - _In_ BOOLEAN Wow64, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId - ) -{ - HANDLE legacyPrivateBlockHandle = NULL; - PVOID ipcControlBlockTable = NULL; - LARGE_INTEGER sectionOffset = { 0 }; - SIZE_T viewSize = 0; - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING sectionNameUs; - PPH_LIST appDomainsList = NULL; - - if (!PhStringRefToUnicodeString(&GeneratePrivateNameV4(ProcessId)->sr, §ionNameUs)) - goto CleanupExit; - - InitializeObjectAttributes( - &objectAttributes, - §ionNameUs, - 0, - NULL, - NULL - ); - - if (!NT_SUCCESS(NtOpenSection( - &legacyPrivateBlockHandle, - SECTION_MAP_READ, - &objectAttributes - ))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(NtMapViewOfSection( - legacyPrivateBlockHandle, - NtCurrentProcess(), - &ipcControlBlockTable, - 0, - viewSize, - §ionOffset, - &viewSize, - ViewShare, - 0, - PAGE_READONLY - ))) - { - goto CleanupExit; - } - - if (Wow64) - { - LegacyPrivateIPCControlBlock_Wow64* legacyPrivateBlock; - AppDomainEnumerationIPCBlock_Wow64* appDomainEnumBlock; - - legacyPrivateBlock = (LegacyPrivateIPCControlBlock_Wow64*)ipcControlBlockTable; - appDomainEnumBlock = &legacyPrivateBlock->AppDomainBlock; - - // Check the IPCControlBlock is initialized. - if ((legacyPrivateBlock->FullIPCHeader.Header.Flags & IPC_FLAG_INITIALIZED) != IPC_FLAG_INITIALIZED) - { - goto CleanupExit; - } - - // Check the IPCControlBlock version is valid. - if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) - { - goto CleanupExit; - } - - appDomainsList = EnumerateAppDomainIpcBlockWow64( - ProcessHandle, - appDomainEnumBlock - ); - } - else - { - LegacyPrivateIPCControlBlock* legacyPrivateBlock; - AppDomainEnumerationIPCBlock* appDomainEnumBlock; - - legacyPrivateBlock = (LegacyPrivateIPCControlBlock*)ipcControlBlockTable; - appDomainEnumBlock = &legacyPrivateBlock->AppDomainBlock; - - // Check the IPCControlBlock is initialized. - if ((legacyPrivateBlock->FullIPCHeader.Header.Flags & IPC_FLAG_INITIALIZED) != IPC_FLAG_INITIALIZED) - { - goto CleanupExit; - } - - // Check the IPCControlBlock version is valid. - if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) - { - goto CleanupExit; - } - - appDomainsList = EnumerateAppDomainIpcBlock( - ProcessHandle, - appDomainEnumBlock - ); - } - -CleanupExit: - - if (ipcControlBlockTable) - { - NtUnmapViewOfSection(NtCurrentProcess(), ipcControlBlockTable); - } - - if (legacyPrivateBlockHandle) - { - NtClose(legacyPrivateBlockHandle); - } - - return appDomainsList; -} +/* + * Process Hacker .NET Tools - + * IPC support functions + * + * Copyright (C) 2015-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "clr/dbgappdomain.h" +#include "clr/ipcheader.h" +#include "clr/ipcshared.h" + +PVOID GetLegacyBlockTableEntry( + _In_ BOOLEAN Wow64, + _In_ PVOID IpcBlockAddress, + _In_ ULONG EntryId + ) +{ + if (Wow64) + { + LegacyPrivateIPCControlBlock_Wow64* IpcBlock = IpcBlockAddress; + + // skip over directory (variable size) + ULONG offsetBase = IPC_ENTRY_OFFSET_BASE_X86 + IpcBlock->FullIPCHeader.Header.NumEntries * sizeof(IPCEntry); + // Directory has offset in bytes of block + ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset; + + return PTR_ADD_OFFSET(IpcBlock, offsetBase + offsetEntry); + } + else + { + LegacyPrivateIPCControlBlock* IpcBlock = IpcBlockAddress; + + // skip over directory (variable size) + ULONG offsetBase = IPC_ENTRY_OFFSET_BASE_X64 + IpcBlock->FullIPCHeader.Header.NumEntries * sizeof(IPCEntry); + // Directory has offset in bytes of block + ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset; + + return PTR_ADD_OFFSET(IpcBlock, offsetBase + offsetEntry); + } +} + +PVOID GetPerfIpcBlock_V2( + _In_ BOOLEAN Wow64, + _In_ PVOID BlockTableAddress + ) +{ + if (Wow64) + { + LegacyPublicIPCControlBlock_Wow64* ipcLegacyBlockTable_Wow64 = BlockTableAddress; + + if (ipcLegacyBlockTable_Wow64->FullIPCHeaderLegacyPublic.Header.Version > VER_LEGACYPUBLIC_IPC_BLOCK) + { + return NULL; + } + + return &ipcLegacyBlockTable_Wow64->PerfIpcBlock; + } + else + { + LegacyPublicIPCControlBlock* ipcLegacyBlockTable = BlockTableAddress; + + if (ipcLegacyBlockTable->FullIPCHeaderLegacyPublic.Header.Version > VER_IPC_BLOCK) + { + return NULL; + } + + return &ipcLegacyBlockTable->PerfIpcBlock; + } +} + +PVOID GetPerfIpcBlock_V4( + _In_ BOOLEAN Wow64, + _In_ PVOID BlockTableAddress + ) +{ + if (Wow64) + { + IPCControlBlockTable_Wow64* ipcBlockTable_Wow64 = BlockTableAddress; + + if (ipcBlockTable_Wow64->Blocks->Header.Version > VER_IPC_BLOCK) + { + return NULL; + } + + return &ipcBlockTable_Wow64->Blocks->PerfIpcBlock; + } + else + { + IPCControlBlockTable_Wow64* ipcBlockTable = BlockTableAddress; + + if (ipcBlockTable->Blocks->Header.Version > VER_IPC_BLOCK) + { + return NULL; + } + + return &ipcBlockTable->Blocks->PerfIpcBlock; + } +} + +PPH_LIST EnumerateAppDomainIpcBlock( + _In_ HANDLE ProcessHandle, + _In_ AppDomainEnumerationIPCBlock* AppDomainIpcBlock + ) +{ + LARGE_INTEGER timeout; + SIZE_T appDomainInfoBlockLength; + HANDLE legacyPrivateBlockMutexHandle = NULL; + AppDomainEnumerationIPCBlock tempBlock; + AppDomainInfo* appDomainInfoBlock = NULL; + PPH_LIST appDomainsList = PhCreateList(1); + + // If the mutex isn't filled in, the CLR is either starting up or shutting down + if (!AppDomainIpcBlock->Mutex) + { + goto CleanupExit; + } + + // Dup the valid mutex handle into this process. + if (!NT_SUCCESS(NtDuplicateObject( + ProcessHandle, + AppDomainIpcBlock->Mutex, + NtCurrentProcess(), + &legacyPrivateBlockMutexHandle, + GENERIC_ALL, + 0, + DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES + ))) + { + goto CleanupExit; + } + + // Acquire the mutex, only waiting two seconds. + // We can't actually gaurantee that the target put a mutex object in here. + if (NtWaitForSingleObject( + legacyPrivateBlockMutexHandle, + FALSE, + PhTimeoutFromMilliseconds(&timeout, 2000) + ) == STATUS_WAIT_0) + { + // Make sure the mutex handle is still valid. If its not, then we lost a shutdown race. + if (!AppDomainIpcBlock->Mutex) + { + goto CleanupExit; + } + } + else + { + // Again, landing here is most probably a shutdown race. + goto CleanupExit; + } + + // Beware: If the target pid is not properly honoring the mutex, the data in the IPC block may still shift underneath us. + // If we get here, then hMutex is held by this process. + + // Make a copy of the IPC block so that we can gaurantee that it's not changing on us. + memcpy(&tempBlock, AppDomainIpcBlock, sizeof(AppDomainEnumerationIPCBlock)); + + // It's possible the process will not have any appdomains. + if ((tempBlock.ListOfAppDomains == NULL) != (tempBlock.SizeInBytes == 0)) + { + goto CleanupExit; + } + + // All the data in the IPC block is signed integers. They should never be negative, + // so check that now. + if ((tempBlock.TotalSlots < 0) || + (tempBlock.NumOfUsedSlots < 0) || + (tempBlock.LastFreedSlot < 0) || + (tempBlock.SizeInBytes < 0) || + (tempBlock.ProcessNameLengthInBytes < 0)) + { + goto CleanupExit; + } + + // Allocate memory to read the remote process' memory into + appDomainInfoBlockLength = tempBlock.SizeInBytes; + + // Check other invariants. + if (appDomainInfoBlockLength != tempBlock.TotalSlots * sizeof(AppDomainInfo)) + { + goto CleanupExit; + } + + appDomainInfoBlock = PhAllocate(appDomainInfoBlockLength); + memset(appDomainInfoBlock, 0, appDomainInfoBlockLength); + + if (!NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + tempBlock.ListOfAppDomains, + appDomainInfoBlock, + appDomainInfoBlockLength, + NULL + ))) + { + PhFree(appDomainInfoBlock); + goto CleanupExit; + } + + // Collect all the AppDomain names into a list of strings. + for (INT i = 0; i < tempBlock.NumOfUsedSlots; i++) + { + SIZE_T appDomainNameLength; + PVOID appDomainName; + + if (!appDomainInfoBlock[i].AppDomainName) + continue; + + // Should be positive, and at least have a null-terminator character. + if (appDomainInfoBlock[i].NameLengthInBytes <= 1) + continue; + + // Make sure buffer has right geometry. + if (appDomainInfoBlock[i].NameLengthInBytes < 0) + continue; + + // If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow. + appDomainNameLength = appDomainInfoBlock[i].NameLengthInBytes / sizeof(WCHAR); + + if ((appDomainNameLength * sizeof(WCHAR)) != appDomainInfoBlock[i].NameLengthInBytes) + continue; + + // It should at least have 1 char for the null terminator. + if (appDomainNameLength < 1) + continue; + + // We know the string is a well-formed null-terminated string, + // but beyond that, we can't verify that the data is actually truthful. + appDomainName = PhAllocate(appDomainInfoBlock[i].NameLengthInBytes + 1); + memset(appDomainName, 0, appDomainInfoBlock[i].NameLengthInBytes + 1); + + if (!NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + appDomainInfoBlock[i].AppDomainName, + appDomainName, + appDomainInfoBlock[i].NameLengthInBytes, + NULL + ))) + { + PhFree(appDomainName); + continue; + } + + PhAddItemList(appDomainsList, appDomainName); + } + +CleanupExit: + if (appDomainInfoBlock) + { + PhFree(appDomainInfoBlock); + } + + if (legacyPrivateBlockMutexHandle) + { + NtReleaseMutant(legacyPrivateBlockMutexHandle, NULL); + NtClose(legacyPrivateBlockMutexHandle); + } + + return appDomainsList; +} + +PPH_LIST EnumerateAppDomainIpcBlockWow64( + _In_ HANDLE ProcessHandle, + _In_ AppDomainEnumerationIPCBlock_Wow64* AppDomainIpcBlock + ) +{ + LARGE_INTEGER timeout; + SIZE_T appDomainInfoBlockLength; + HANDLE legacyPrivateBlockMutexHandle = NULL; + AppDomainEnumerationIPCBlock_Wow64 tempBlock; + AppDomainInfo_Wow64* appDomainInfoBlock = NULL; + PPH_LIST appDomainsList = PhCreateList(1); + + // If the mutex isn't filled in, the CLR is either starting up or shutting down + if (!AppDomainIpcBlock->Mutex) + { + goto CleanupExit; + } + + // Dup the valid mutex handle into this process. + if (!NT_SUCCESS(NtDuplicateObject( + ProcessHandle, + UlongToHandle(AppDomainIpcBlock->Mutex), + NtCurrentProcess(), + &legacyPrivateBlockMutexHandle, + GENERIC_ALL, + 0, + DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES + ))) + { + goto CleanupExit; + } + + // Acquire the mutex, only waiting two seconds. + // We can't actually gaurantee that the target put a mutex object in here. + if (NtWaitForSingleObject( + legacyPrivateBlockMutexHandle, + FALSE, + PhTimeoutFromMilliseconds(&timeout, 2000) + ) == STATUS_WAIT_0) + { + // Make sure the mutex handle is still valid. If its not, then we lost a shutdown race. + if (!AppDomainIpcBlock->Mutex) + { + goto CleanupExit; + } + } + else + { + // Again, landing here is most probably a shutdown race. + goto CleanupExit; + } + + // Beware: If the target pid is not properly honoring the mutex, the data in the IPC block may still shift underneath us. + // If we get here, then hMutex is held by this process. + + // Make a copy of the IPC block so that we can gaurantee that it's not changing on us. + memcpy(&tempBlock, AppDomainIpcBlock, sizeof(AppDomainEnumerationIPCBlock_Wow64)); + + // It's possible the process will not have any appdomains. + if ((tempBlock.ListOfAppDomains == 0) != (tempBlock.SizeInBytes == 0)) + { + goto CleanupExit; + } + + // All the data in the IPC block is signed integers. They should never be negative, + // so check that now. + if ((tempBlock.TotalSlots < 0) || + (tempBlock.NumOfUsedSlots < 0) || + (tempBlock.LastFreedSlot < 0) || + (tempBlock.SizeInBytes < 0) || + (tempBlock.ProcessNameLengthInBytes < 0)) + { + goto CleanupExit; + } + + // Allocate memory to read the remote process' memory into + appDomainInfoBlockLength = tempBlock.SizeInBytes; + + // Check other invariants. + if (appDomainInfoBlockLength != tempBlock.TotalSlots * sizeof(AppDomainInfo_Wow64)) + { + goto CleanupExit; + } + + appDomainInfoBlock = PhAllocate(appDomainInfoBlockLength); + memset(appDomainInfoBlock, 0, appDomainInfoBlockLength); + + if (!NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + UlongToPtr(tempBlock.ListOfAppDomains), + appDomainInfoBlock, + appDomainInfoBlockLength, + NULL + ))) + { + PhFree(appDomainInfoBlock); + goto CleanupExit; + } + + // Collect all the AppDomain names into a list of strings. + for (INT i = 0; i < tempBlock.NumOfUsedSlots; i++) + { + SIZE_T appDomainNameLength; + PVOID appDomainName; + + if (!appDomainInfoBlock[i].AppDomainName) + continue; + + // Should be positive, and at least have a null-terminator character. + if (appDomainInfoBlock[i].NameLengthInBytes <= 1) + continue; + + // Make sure buffer has right geometry. + if (appDomainInfoBlock[i].NameLengthInBytes < 0) + continue; + + // If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow. + appDomainNameLength = appDomainInfoBlock[i].NameLengthInBytes / sizeof(WCHAR); + + if ((appDomainNameLength * sizeof(WCHAR)) != appDomainInfoBlock[i].NameLengthInBytes) + continue; + + // It should at least have 1 char for the null terminator. + if (appDomainNameLength < 1) + continue; + + // We know the string is a well-formed null-terminated string, + // but beyond that, we can't verify that the data is actually truthful. + appDomainName = PhAllocate(appDomainInfoBlock[i].NameLengthInBytes + 1); + memset(appDomainName, 0, appDomainInfoBlock[i].NameLengthInBytes + 1); + + if (!NT_SUCCESS(NtReadVirtualMemory( + ProcessHandle, + UlongToPtr(appDomainInfoBlock[i].AppDomainName), + appDomainName, + appDomainInfoBlock[i].NameLengthInBytes, + NULL + ))) + { + PhFree(appDomainName); + continue; + } + + PhAddItemList(appDomainsList, appDomainName); + } + +CleanupExit: + + if (appDomainInfoBlock) + { + PhFree(appDomainInfoBlock); + } + + if (legacyPrivateBlockMutexHandle) + { + NtReleaseMutant(legacyPrivateBlockMutexHandle, NULL); + NtClose(legacyPrivateBlockMutexHandle); + } + + return appDomainsList; +} + +BOOLEAN OpenDotNetPublicControlBlock_V2( + _In_ HANDLE ProcessId, + _Out_ PVOID* BlockTableAddress + ) +{ + BOOLEAN result = FALSE; + HANDLE blockTableHandle = NULL; + PVOID blockTableAddress = NULL; + UNICODE_STRING sectionNameUs; + OBJECT_ATTRIBUTES objectAttributes; + LARGE_INTEGER sectionOffset = { 0 }; + SIZE_T viewSize = 0; + + if (!PhStringRefToUnicodeString(&PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPublicIPCBlock, HandleToUlong(ProcessId))->sr, §ionNameUs)) + return FALSE; + + InitializeObjectAttributes( + &objectAttributes, + §ionNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (!NT_SUCCESS(NtOpenSection( + &blockTableHandle, + SECTION_MAP_READ, + &objectAttributes + ))) + { + return FALSE; + } + + if (NT_SUCCESS(NtMapViewOfSection( + blockTableHandle, + NtCurrentProcess(), + &blockTableAddress, + 0, + viewSize, + §ionOffset, + &viewSize, + ViewShare, + 0, + PAGE_READONLY + ))) + { + *BlockTableAddress = blockTableAddress; + + NtClose(blockTableHandle); + return TRUE; + } + + if (blockTableHandle) + NtClose(blockTableHandle); + + if (blockTableAddress) + NtUnmapViewOfSection(NtCurrentProcess(), blockTableAddress); + + return FALSE; +} + +BOOLEAN OpenDotNetPublicControlBlock_V4( + _In_ BOOLEAN IsImmersive, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _Out_ PVOID* BlockTableAddress + ) +{ + BOOLEAN result = FALSE; + PVOID boundaryDescriptorHandle = NULL; + HANDLE privateNamespaceHandle = NULL; + HANDLE blockTableHandle = NULL; + HANDLE tokenHandle = NULL; + PSID everyoneSIDHandle = NULL; + PVOID blockTableAddress = NULL; + LARGE_INTEGER sectionOffset = { 0 }; + SIZE_T viewSize = 0; + UNICODE_STRING prefixNameUs; + UNICODE_STRING sectionNameUs; + UNICODE_STRING boundaryNameUs; + OBJECT_ATTRIBUTES namespaceObjectAttributes; + OBJECT_ATTRIBUTES sectionObjectAttributes; + PTOKEN_APPCONTAINER_INFORMATION appContainerInfo = NULL; + SID_IDENTIFIER_AUTHORITY SIDWorldAuth = SECURITY_WORLD_SID_AUTHORITY; + + if (!PhStringRefToUnicodeString(&PhaFormatString(CorSxSBoundaryDescriptor, HandleToUlong(ProcessId))->sr, &boundaryNameUs)) + goto CleanupExit; + + if (!(boundaryDescriptorHandle = RtlCreateBoundaryDescriptor(&boundaryNameUs, 0))) + goto CleanupExit; + + if (!NT_SUCCESS(RtlAllocateAndInitializeSid(&SIDWorldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSIDHandle))) + goto CleanupExit; + + if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&boundaryDescriptorHandle, everyoneSIDHandle))) + goto CleanupExit; + + if (WINDOWS_HAS_IMMERSIVE && IsImmersive) + { + if (NT_SUCCESS(PhOpenProcessToken(ProcessHandle, TOKEN_QUERY, &tokenHandle))) + { + if (!NT_SUCCESS(PhQueryTokenVariableSize(tokenHandle, TokenAppContainerSid, &appContainerInfo))) + goto CleanupExit; + + if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&boundaryDescriptorHandle, appContainerInfo->TokenAppContainer))) + goto CleanupExit; + } + } + + RtlInitUnicodeString(&prefixNameUs, CorSxSReaderPrivateNamespacePrefix); + InitializeObjectAttributes( + &namespaceObjectAttributes, + &prefixNameUs, + OBJ_CASE_INSENSITIVE, + boundaryDescriptorHandle, + NULL + ); + + if (!NT_SUCCESS(NtOpenPrivateNamespace( + &privateNamespaceHandle, + MAXIMUM_ALLOWED, + &namespaceObjectAttributes, + boundaryDescriptorHandle + ))) + { + goto CleanupExit; + } + + RtlInitUnicodeString(§ionNameUs, CorSxSVistaPublicIPCBlock); + InitializeObjectAttributes( + §ionObjectAttributes, + §ionNameUs, + OBJ_CASE_INSENSITIVE, + privateNamespaceHandle, + NULL + ); + + if (!NT_SUCCESS(NtOpenSection( + &blockTableHandle, + SECTION_MAP_READ, + §ionObjectAttributes + ))) + { + goto CleanupExit; + } + + if (!NT_SUCCESS(NtMapViewOfSection( + blockTableHandle, + NtCurrentProcess(), + &blockTableAddress, + 0, + viewSize, + §ionOffset, + &viewSize, + ViewShare, + 0, + PAGE_READONLY + ))) + { + goto CleanupExit; + } + + *BlockTableAddress = blockTableAddress; + + result = TRUE; + +CleanupExit: + + if (!result) + { + if (blockTableAddress) + { + NtUnmapViewOfSection(NtCurrentProcess(), blockTableAddress); + } + + *BlockTableAddress = NULL; + } + + if (blockTableHandle) + { + NtClose(blockTableHandle); + } + + if (tokenHandle) + { + NtClose(tokenHandle); + } + + if (appContainerInfo) + { + PhFree(appContainerInfo); + } + + if (privateNamespaceHandle) + { + NtClose(privateNamespaceHandle); + } + + if (everyoneSIDHandle) + { + RtlFreeSid(everyoneSIDHandle); + } + + if (boundaryDescriptorHandle) + { + RtlDeleteBoundaryDescriptor(boundaryDescriptorHandle); + } + + return result; +} + +PPH_LIST QueryDotNetAppDomainsForPid_V2( + _In_ BOOLEAN Wow64, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId + ) +{ + PPH_LIST appDomainsList = NULL; + PPH_STRING legacyPrivateBlockName = NULL; + HANDLE legacyPrivateBlockHandle = NULL; + PVOID ipcControlBlockTable = NULL; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING sectionNameUs; + LARGE_INTEGER sectionOffset; + SIZE_T viewSize; + + legacyPrivateBlockName = PhFormatString( + L"\\BaseNamedObjects\\" CorLegacyPrivateIPCBlock, + HandleToUlong(ProcessId) + ); + + if (!PhStringRefToUnicodeString(&legacyPrivateBlockName->sr, §ionNameUs)) + goto CleanupExit; + + InitializeObjectAttributes( + &objectAttributes, + §ionNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (!NT_SUCCESS(NtOpenSection( + &legacyPrivateBlockHandle, + SECTION_MAP_READ, + &objectAttributes + ))) + { + goto CleanupExit; + } + + viewSize = 0; + sectionOffset.QuadPart = 0; + + if (!NT_SUCCESS(NtMapViewOfSection( + legacyPrivateBlockHandle, + NtCurrentProcess(), + &ipcControlBlockTable, + 0, + viewSize, + §ionOffset, + &viewSize, + ViewShare, + 0, + PAGE_READONLY + ))) + { + goto CleanupExit; + } + + if (Wow64) + { + LegacyPrivateIPCControlBlock_Wow64* legacyPrivateBlock; + AppDomainEnumerationIPCBlock_Wow64* appDomainEnumBlock; + + legacyPrivateBlock = (LegacyPrivateIPCControlBlock_Wow64*)ipcControlBlockTable; + + // NOTE: .NET 2.0 processes do not have the IPC_FLAG_INITIALIZED flag. + + // Check the IPCControlBlock version is valid. + if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) + { + goto CleanupExit; + } + + appDomainEnumBlock = GetLegacyBlockTableEntry( + Wow64, + ipcControlBlockTable, + eLegacyPrivateIPC_AppDomain + ); + + appDomainsList = EnumerateAppDomainIpcBlockWow64( + ProcessHandle, + appDomainEnumBlock + ); + } + else + { + LegacyPrivateIPCControlBlock* legacyPrivateBlock; + AppDomainEnumerationIPCBlock* appDomainEnumBlock; + + legacyPrivateBlock = (LegacyPrivateIPCControlBlock*)ipcControlBlockTable; + + // NOTE: .NET 2.0 processes do not have the IPC_FLAG_INITIALIZED flag. + + // Check the IPCControlBlock version is valid. + if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) + { + goto CleanupExit; + } + + appDomainEnumBlock = GetLegacyBlockTableEntry( + Wow64, + ipcControlBlockTable, + eLegacyPrivateIPC_AppDomain + ); + + appDomainsList = EnumerateAppDomainIpcBlock( + ProcessHandle, + appDomainEnumBlock + ); + } + +CleanupExit: + + if (ipcControlBlockTable) + { + NtUnmapViewOfSection(NtCurrentProcess(), ipcControlBlockTable); + } + + if (legacyPrivateBlockHandle) + { + NtClose(legacyPrivateBlockHandle); + } + + PhDereferenceObject(legacyPrivateBlockName); + + return appDomainsList; +} + +PPH_LIST QueryDotNetAppDomainsForPid_V4( + _In_ BOOLEAN Wow64, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId + ) +{ + PPH_LIST appDomainsList = NULL; + PPH_STRING legacyPrivateBlockName = NULL; + HANDLE legacyPrivateBlockHandle = NULL; + PVOID ipcControlBlockTable = NULL; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING sectionNameUs; + LARGE_INTEGER sectionOffset; + SIZE_T viewSize; + + legacyPrivateBlockName = PhaFormatString( + L"\\BaseNamedObjects\\" CorLegacyPrivateIPCBlockTempV4, + HandleToUlong(ProcessId) + ); + + if (!PhStringRefToUnicodeString(&legacyPrivateBlockName->sr, §ionNameUs)) + goto CleanupExit; + + InitializeObjectAttributes( + &objectAttributes, + §ionNameUs, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + if (!NT_SUCCESS(NtOpenSection( + &legacyPrivateBlockHandle, + SECTION_MAP_READ, + &objectAttributes + ))) + { + goto CleanupExit; + } + + viewSize = 0; + sectionOffset.QuadPart = 0; + + if (!NT_SUCCESS(NtMapViewOfSection( + legacyPrivateBlockHandle, + NtCurrentProcess(), + &ipcControlBlockTable, + 0, + viewSize, + §ionOffset, + &viewSize, + ViewShare, + 0, + PAGE_READONLY + ))) + { + goto CleanupExit; + } + + if (Wow64) + { + LegacyPrivateIPCControlBlock_Wow64* legacyPrivateBlock; + AppDomainEnumerationIPCBlock_Wow64* appDomainEnumBlock; + + legacyPrivateBlock = (LegacyPrivateIPCControlBlock_Wow64*)ipcControlBlockTable; + appDomainEnumBlock = &legacyPrivateBlock->AppDomainBlock; + + // Check the IPCControlBlock is initialized. + if ((legacyPrivateBlock->FullIPCHeader.Header.Flags & IPC_FLAG_INITIALIZED) != IPC_FLAG_INITIALIZED) + { + goto CleanupExit; + } + + // Check the IPCControlBlock version is valid. + if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) + { + goto CleanupExit; + } + + appDomainsList = EnumerateAppDomainIpcBlockWow64( + ProcessHandle, + appDomainEnumBlock + ); + } + else + { + LegacyPrivateIPCControlBlock* legacyPrivateBlock; + AppDomainEnumerationIPCBlock* appDomainEnumBlock; + + legacyPrivateBlock = (LegacyPrivateIPCControlBlock*)ipcControlBlockTable; + appDomainEnumBlock = &legacyPrivateBlock->AppDomainBlock; + + // Check the IPCControlBlock is initialized. + if ((legacyPrivateBlock->FullIPCHeader.Header.Flags & IPC_FLAG_INITIALIZED) != IPC_FLAG_INITIALIZED) + { + goto CleanupExit; + } + + // Check the IPCControlBlock version is valid. + if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK) + { + goto CleanupExit; + } + + appDomainsList = EnumerateAppDomainIpcBlock( + ProcessHandle, + appDomainEnumBlock + ); + } + +CleanupExit: + + if (ipcControlBlockTable) + { + NtUnmapViewOfSection(NtCurrentProcess(), ipcControlBlockTable); + } + + if (legacyPrivateBlockHandle) + { + NtClose(legacyPrivateBlockHandle); + } + + return appDomainsList; +} diff --git a/plugins/DotNetTools/dn.h b/plugins/DotNetTools/dn.h index 23fd34947f91..f0f70e6c4e30 100644 --- a/plugins/DotNetTools/dn.h +++ b/plugins/DotNetTools/dn.h @@ -1,144 +1,148 @@ -/* - * Process Hacker .NET Tools - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef DN_H -#define DN_H - -#define CINTERFACE -#define COBJMACROS -#include -#include - -#include "resource.h" - -#define PLUGIN_NAME L"ProcessHacker.DotNetTools" -#define SETTING_NAME_ASM_TREE_LIST_COLUMNS (PLUGIN_NAME L".AsmTreeListColumns") -#define SETTING_NAME_DOT_NET_CATEGORY_INDEX (PLUGIN_NAME L".DotNetCategoryIndex") -#define SETTING_NAME_DOT_NET_COUNTERS_COLUMNS (PLUGIN_NAME L".DotNetListColumns") -#define SETTING_NAME_DOT_NET_SHOW_BYTE_SIZE (PLUGIN_NAME L".DotNetShowByteSizes") - -#define MSG_UPDATE (WM_APP + 1) - -extern PPH_PLUGIN PluginInstance; - -typedef struct _DN_THREAD_ITEM -{ - PPH_THREAD_ITEM ThreadItem; - - BOOLEAN ClrDataValid; - PPH_STRING AppDomainText; -} DN_THREAD_ITEM, *PDN_THREAD_ITEM; - -// counters - -PVOID GetPerfIpcBlock_V2( - _In_ BOOLEAN Wow64, - _In_ PVOID BlockTableAddress - ); - -PVOID GetPerfIpcBlock_V4( - _In_ BOOLEAN Wow64, - _In_ PVOID BlockTableAddress - ); - -BOOLEAN OpenDotNetPublicControlBlock_V2( - _In_ HANDLE ProcessId, - _Out_ HANDLE* BlockTableHandle, - _Out_ PVOID* BlockTableAddress - ); - -BOOLEAN OpenDotNetPublicControlBlock_V4( - _In_ BOOLEAN IsImmersive, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId, - _Out_ HANDLE* BlockTableHandle, - _Out_ PVOID* BlockTableAddress - ); - -PPH_LIST QueryDotNetAppDomainsForPid_V2( - _In_ BOOLEAN Wow64, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId - ); - -PPH_LIST QueryDotNetAppDomainsForPid_V4( - _In_ BOOLEAN Wow64, - _In_ HANDLE ProcessHandle, - _In_ HANDLE ProcessId - ); - -// asmpage - -VOID AddAsmPageToPropContext( - _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext - ); - -// perfpage - -VOID AddPerfPageToPropContext( - _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext - ); - -// stackext - -VOID ProcessThreadStackControl( - _In_ PPH_PLUGIN_THREAD_STACK_CONTROL Control - ); - -VOID PredictAddressesFromClrData( - _In_ struct _CLR_PROCESS_SUPPORT *Support, - _In_ HANDLE ThreadId, - _In_ PVOID PcAddress, - _In_ PVOID FrameAddress, - _In_ PVOID StackAddress, - _Out_ PVOID *PredictedEip, - _Out_ PVOID *PredictedEbp, - _Out_ PVOID *PredictedEsp - ); - -// svcext - -VOID DispatchPhSvcRequest( - _In_ PVOID Parameter - ); - -// treeext - -VOID InitializeTreeNewObjectExtensions( - VOID - ); - -VOID DispatchTreeNewMessage( - __in PVOID Parameter - ); - -#define DNTHTNC_APPDOMAIN 1 - -VOID ThreadTreeNewInitializing( - __in PVOID Parameter - ); - -VOID ThreadTreeNewUninitializing( - __in PVOID Parameter - ); - -#endif +/* + * Process Hacker .NET Tools + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef DN_H +#define DN_H + +#include +#include + +#include "resource.h" + +#define PLUGIN_NAME L"ProcessHacker.DotNetTools" +#define SETTING_NAME_ASM_TREE_LIST_COLUMNS (PLUGIN_NAME L".AsmTreeListColumns") +#define SETTING_NAME_ASM_TREE_LIST_FLAGS (PLUGIN_NAME L".AsmTreeListFlags") +//#define SETTING_NAME_ASM_TREE_LIST_SORT (PLUGIN_NAME L".AsmTreeListSort") +#define SETTING_NAME_DOT_NET_CATEGORY_INDEX (PLUGIN_NAME L".DotNetCategoryIndex") +#define SETTING_NAME_DOT_NET_COUNTERS_COLUMNS (PLUGIN_NAME L".DotNetListColumns") +#define SETTING_NAME_DOT_NET_COUNTERS_SORTCOLUMN (PLUGIN_NAME L".DotNetListSort") +#define SETTING_NAME_DOT_NET_COUNTERS_GROUPSTATES (PLUGIN_NAME L".DotNetListGroupStates") + +#define MSG_UPDATE (WM_APP + 1) + +extern PPH_PLUGIN PluginInstance; + +#define DN_ASM_MENU_HIDE_DYNAMIC_OPTION 1 +#define DN_ASM_MENU_HIGHLIGHT_DYNAMIC_OPTION 2 +#define DN_ASM_MENU_HIDE_NATIVE_OPTION 3 +#define DN_ASM_MENU_HIGHLIGHT_NATIVE_OPTION 4 + +typedef struct _DN_THREAD_ITEM +{ + PPH_THREAD_ITEM ThreadItem; + + BOOLEAN ClrDataValid; + PPH_STRING AppDomainText; +} DN_THREAD_ITEM, *PDN_THREAD_ITEM; + +// counters + +PVOID GetPerfIpcBlock_V2( + _In_ BOOLEAN Wow64, + _In_ PVOID BlockTableAddress + ); + +PVOID GetPerfIpcBlock_V4( + _In_ BOOLEAN Wow64, + _In_ PVOID BlockTableAddress + ); + +BOOLEAN OpenDotNetPublicControlBlock_V2( + _In_ HANDLE ProcessId, + _Out_ PVOID* BlockTableAddress + ); + +BOOLEAN OpenDotNetPublicControlBlock_V4( + _In_ BOOLEAN IsImmersive, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId, + _Out_ PVOID* BlockTableAddress + ); + +PPH_LIST QueryDotNetAppDomainsForPid_V2( + _In_ BOOLEAN Wow64, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId + ); + +PPH_LIST QueryDotNetAppDomainsForPid_V4( + _In_ BOOLEAN Wow64, + _In_ HANDLE ProcessHandle, + _In_ HANDLE ProcessId + ); + +// asmpage + +VOID AddAsmPageToPropContext( + _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext + ); + +// perfpage + +VOID AddPerfPageToPropContext( + _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext + ); + +// stackext + +VOID ProcessThreadStackControl( + _In_ PPH_PLUGIN_THREAD_STACK_CONTROL Control + ); + +VOID PredictAddressesFromClrData( + _In_ struct _CLR_PROCESS_SUPPORT *Support, + _In_ HANDLE ThreadId, + _In_ PVOID PcAddress, + _In_ PVOID FrameAddress, + _In_ PVOID StackAddress, + _Out_ PVOID *PredictedEip, + _Out_ PVOID *PredictedEbp, + _Out_ PVOID *PredictedEsp + ); + +// svcext + +VOID DispatchPhSvcRequest( + _In_ PVOID Parameter + ); + +// treeext + +VOID InitializeTreeNewObjectExtensions( + VOID + ); + +VOID DispatchTreeNewMessage( + __in PVOID Parameter + ); + +#define DNTHTNC_APPDOMAIN 1 + +VOID ThreadTreeNewInitializing( + __in PVOID Parameter + ); + +VOID ThreadTreeNewUninitializing( + __in PVOID Parameter + ); + +#endif diff --git a/plugins/DotNetTools/main.c b/plugins/DotNetTools/main.c index 80118c7720f0..9d8364d9e650 100644 --- a/plugins/DotNetTools/main.c +++ b/plugins/DotNetTools/main.c @@ -1,335 +1,328 @@ -/* - * Process Hacker .NET Tools - - * main program - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" - -PPH_PLUGIN PluginInstance; -static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginTreeNewMessageCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginPhSvcRequestCallbackRegistration; -static PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ModuleMenuInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ThreadTreeNewInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ThreadTreeNewUninitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ThreadStackControlCallbackRegistration; - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI UnloadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - default: - NOTHING; - break; - } -} - -VOID NTAPI TreeNewMessageCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - DispatchTreeNewMessage(Parameter); -} - -VOID NTAPI PhSvcRequestCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - DispatchPhSvcRequest(Parameter); -} - -VOID NTAPI ThreadTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ThreadTreeNewInitializing(Parameter); -} - -VOID NTAPI ThreadTreeNewUninitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ThreadTreeNewUninitializing(Parameter); -} - -VOID NTAPI ProcessPropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; - BOOLEAN isDotNet; - - if (NT_SUCCESS(PhGetProcessIsDotNet(propContext->ProcessItem->ProcessId, &isDotNet))) - { - if (isDotNet) - { - AddAsmPageToPropContext(propContext); - AddPerfPageToPropContext(propContext); - } - - if (propContext->ProcessItem->IsDotNet != isDotNet) - propContext->ProcessItem->UpdateIsDotNet = TRUE; // force a refresh - } -} - -VOID NTAPI ProcessMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI ThreadMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI ModuleMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI ProcessTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI ThreadStackControlCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ProcessThreadStackControl(Parameter); -} - -VOID NTAPI ThreadItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PDN_THREAD_ITEM dnThread = Extension; - - memset(dnThread, 0, sizeof(DN_THREAD_ITEM)); - dnThread->ThreadItem = Object; -} - -VOID NTAPI ThreadItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PDN_THREAD_ITEM dnThread = Extension; - - PhClearReference(&dnThread->AppDomainText); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { StringSettingType, SETTING_NAME_ASM_TREE_LIST_COLUMNS, L"" }, - { IntegerSettingType, SETTING_NAME_DOT_NET_CATEGORY_INDEX, L"5" }, - { StringSettingType, SETTING_NAME_DOT_NET_COUNTERS_COLUMNS, L"" }, - { IntegerSettingType, SETTING_NAME_DOT_NET_SHOW_BYTE_SIZE, L"1" } - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L".NET Tools"; - info->Author = L"dmex, wj32"; - info->Description = L"Adds .NET performance counters, assembly information, thread stack support, and more."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1111"; - info->HasOptions = FALSE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackUnload), - UnloadCallback, - NULL, - &PluginUnloadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - //PhRegisterCallback( - // PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - // MenuItemCallback, - // NULL, - // &PluginMenuItemCallbackRegistration - // ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), - TreeNewMessageCallback, - NULL, - &PluginTreeNewMessageCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackPhSvcRequest), - PhSvcRequestCallback, - NULL, - &PluginPhSvcRequestCallbackRegistration - ); - - //PhRegisterCallback( - // PhGetGeneralCallback(GeneralCallbackMainWindowShowing), - // MainWindowShowingCallback, - // NULL, - // &MainWindowShowingCallbackRegistration - // ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), - ProcessPropertiesInitializingCallback, - NULL, - &ProcessPropertiesInitializingCallbackRegistration - ); - //PhRegisterCallback( - // PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), - // ProcessMenuInitializingCallback, - // NULL, - // &ProcessMenuInitializingCallbackRegistration - // ); - //PhRegisterCallback( - // PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing), - // ThreadMenuInitializingCallback, - // NULL, - // &ThreadMenuInitializingCallbackRegistration - // ); - //PhRegisterCallback( - // PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), - // ModuleMenuInitializingCallback, - // NULL, - // &ModuleMenuInitializingCallbackRegistration - // ); - //PhRegisterCallback( - // PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), - // ProcessTreeNewInitializingCallback, - // NULL, - // &ProcessTreeNewInitializingCallbackRegistration - // ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackThreadTreeNewInitializing), - ThreadTreeNewInitializingCallback, - NULL, - &ThreadTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackThreadTreeNewUninitializing), - ThreadTreeNewUninitializingCallback, - NULL, - &ThreadTreeNewUninitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackThreadStackControl), - ThreadStackControlCallback, - NULL, - &ThreadStackControlCallbackRegistration - ); - - PhPluginSetObjectExtension( - PluginInstance, - EmThreadItemType, - sizeof(DN_THREAD_ITEM), - ThreadItemCreateCallback, - ThreadItemDeleteCallback - ); - InitializeTreeNewObjectExtensions(); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - } - - return TRUE; -} +/* + * Process Hacker .NET Tools - + * main program + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2015-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginTreeNewMessageCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginPhSvcRequestCallbackRegistration; +PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ModuleMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ThreadTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ThreadTreeNewUninitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ThreadStackControlCallbackRegistration; + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI UnloadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + switch (menuItem->Id) + { + default: + NOTHING; + break; + } +} + +VOID NTAPI TreeNewMessageCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + DispatchTreeNewMessage(Parameter); +} + +VOID NTAPI PhSvcRequestCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + DispatchPhSvcRequest(Parameter); +} + +VOID NTAPI ThreadTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ThreadTreeNewInitializing(Parameter); +} + +VOID NTAPI ThreadTreeNewUninitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ThreadTreeNewUninitializing(Parameter); +} + +VOID NTAPI ProcessPropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; + BOOLEAN isDotNet = FALSE; + ULONG flags = 0; + + if (NT_SUCCESS(PhGetProcessIsDotNetEx(propContext->ProcessItem->ProcessId, NULL, 0, &isDotNet, &flags))) + { + if (isDotNet) + { + AddAsmPageToPropContext(propContext); + AddPerfPageToPropContext(propContext); + } + else if (flags & PH_CLR_JIT_PRESENT) // CoreCLR support. (dmex) + { + isDotNet = TRUE; + AddAsmPageToPropContext(propContext); + } + + if (propContext->ProcessItem->IsDotNet != isDotNet) + propContext->ProcessItem->UpdateIsDotNet = TRUE; // force a refresh + } +} + +VOID NTAPI ProcessMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI ThreadMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI ModuleMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI ProcessTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI ThreadStackControlCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ProcessThreadStackControl(Parameter); +} + +VOID NTAPI ThreadItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PDN_THREAD_ITEM dnThread = Extension; + + memset(dnThread, 0, sizeof(DN_THREAD_ITEM)); + dnThread->ThreadItem = Object; +} + +VOID NTAPI ThreadItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PDN_THREAD_ITEM dnThread = Extension; + + PhClearReference(&dnThread->AppDomainText); +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { StringSettingType, SETTING_NAME_ASM_TREE_LIST_COLUMNS, L"" }, + { IntegerSettingType, SETTING_NAME_ASM_TREE_LIST_FLAGS, L"0" }, + { IntegerSettingType, SETTING_NAME_DOT_NET_CATEGORY_INDEX, L"5" }, + { StringSettingType, SETTING_NAME_DOT_NET_COUNTERS_COLUMNS, L"" }, + { StringSettingType, SETTING_NAME_DOT_NET_COUNTERS_SORTCOLUMN, L"" }, + { StringSettingType, SETTING_NAME_DOT_NET_COUNTERS_GROUPSTATES, L"" } + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L".NET Tools"; + info->Author = L"dmex, wj32"; + info->Description = L"Adds .NET performance counters, assembly information, thread stack support, and more."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1111"; + info->HasOptions = FALSE; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackUnload), + UnloadCallback, + NULL, + &PluginUnloadCallbackRegistration + ); + //PhRegisterCallback( + // PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + // MenuItemCallback, + // NULL, + // &PluginMenuItemCallbackRegistration + // ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), + TreeNewMessageCallback, + NULL, + &PluginTreeNewMessageCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackPhSvcRequest), + PhSvcRequestCallback, + NULL, + &PluginPhSvcRequestCallbackRegistration + ); + + //PhRegisterCallback( + // PhGetGeneralCallback(GeneralCallbackMainWindowShowing), + // MainWindowShowingCallback, + // NULL, + // &MainWindowShowingCallbackRegistration + // ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), + ProcessPropertiesInitializingCallback, + NULL, + &ProcessPropertiesInitializingCallbackRegistration + ); + //PhRegisterCallback( + // PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), + // ProcessMenuInitializingCallback, + // NULL, + // &ProcessMenuInitializingCallbackRegistration + // ); + //PhRegisterCallback( + // PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing), + // ThreadMenuInitializingCallback, + // NULL, + // &ThreadMenuInitializingCallbackRegistration + // ); + //PhRegisterCallback( + // PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), + // ModuleMenuInitializingCallback, + // NULL, + // &ModuleMenuInitializingCallbackRegistration + // ); + //PhRegisterCallback( + // PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), + // ProcessTreeNewInitializingCallback, + // NULL, + // &ProcessTreeNewInitializingCallbackRegistration + // ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackThreadTreeNewInitializing), + ThreadTreeNewInitializingCallback, + NULL, + &ThreadTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackThreadTreeNewUninitializing), + ThreadTreeNewUninitializingCallback, + NULL, + &ThreadTreeNewUninitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackThreadStackControl), + ThreadStackControlCallback, + NULL, + &ThreadStackControlCallbackRegistration + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmThreadItemType, + sizeof(DN_THREAD_ITEM), + ThreadItemCreateCallback, + ThreadItemDeleteCallback + ); + InitializeTreeNewObjectExtensions(); + + PhAddSettings(settings, RTL_NUMBER_OF(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/DotNetTools/perfpage.c b/plugins/DotNetTools/perfpage.c index a86c0a565b99..add61a24f978 100644 --- a/plugins/DotNetTools/perfpage.c +++ b/plugins/DotNetTools/perfpage.c @@ -1,950 +1,1862 @@ -/* - * Process Hacker .NET Tools - - * .NET Performance property page - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2015 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - - -#include "dn.h" -#include "clr\perfcounterdefs.h" - -typedef enum _DOTNET_CATEGORY -{ - // .NET CLR Exceptions (Runtime statistics on CLR exception handling) - DOTNET_CATEGORY_EXCEPTIONS, - - // .NET CLR Interop (Stats for CLR interop) - DOTNET_CATEGORY_INTEROP, - - // .NET CLR Jit (Stats for CLR Jit) - DOTNET_CATEGORY_JIT, - - // .NET CLR Loading (Statistics for CLR Class Loader) - DOTNET_CATEGORY_LOADING, - - // .NET CLR LocksAndThreads (Stats for CLR Locks and Threads) - DOTNET_CATEGORY_LOCKSANDTHREADS, - - // .NET CLR Memory (Counters for CLR Garbage Collected heap) - DOTNET_CATEGORY_MEMORY, - - // .NET CLR Remoting (Stats for CLR Remoting) - DOTNET_CATEGORY_REMOTING, - - // .NET CLR Security (Stats for CLR Security) - DOTNET_CATEGORY_SECURITY -} DOTNET_CATEGORY; - -typedef struct _PERFPAGE_CONTEXT -{ - HWND WindowHandle; - PPH_PROCESS_ITEM ProcessItem; - BOOLEAN Enabled; - - HWND AppDomainsLv; - HWND CountersLv; - HWND CategoriesCb; - - BOOLEAN ControlBlockValid; - BOOLEAN ClrV4; - BOOLEAN IsWow64; - BOOLEAN ShowByteSize; - DOTNET_CATEGORY CategoryIndex; - HANDLE ProcessHandle; - HANDLE BlockTableHandle; - PVOID BlockTableAddress; - - PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -} PERFPAGE_CONTEXT, *PPERFPAGE_CONTEXT; - -static PWSTR DotNetCategoryStrings[] = -{ - L".NET CLR Exceptions", - L".NET CLR Interop", - L".NET CLR Jit", - L".NET CLR Loading", - L".NET CLR LocksAndThreads", - L".NET CLR Memory", - L".NET CLR Remoting", - L".NET CLR Security" -}; - -PPH_STRING FormatByteValue( - _In_ PPERFPAGE_CONTEXT Context, - _In_ ULONG64 Value - ) -{ - if (Context->ShowByteSize) - return PhaFormatUInt64(Value, TRUE); - - return PhaFormatSize(Value, -1); -} - -VOID NTAPI ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPERFPAGE_CONTEXT context = Context; - - if (context->WindowHandle) - { - PostMessage(context->WindowHandle, MSG_UPDATE, 0, 0); - } -} - -VOID UpdateCategoryValues( - _In_ HWND hwndDlg, - _In_ PPERFPAGE_CONTEXT Context - ) -{ - ListView_DeleteAllItems(Context->CountersLv); - - switch (Context->CategoryIndex) - { - case DOTNET_CATEGORY_EXCEPTIONS: - { - // This counter displays the total number of exceptions thrown since the start of the application. These include both .NET exceptions and unmanaged exceptions that get converted into .NET exceptions e.g. null pointer reference exception in unmanaged code would get re-thrown in managed code as a .NET System.NullReferenceException; this counter includes both handled and unhandled exceptions.Exceptions that are re-thrown would get counted again. Exceptions should only occur in rare situations and not in the normal control flow of the program. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Exceps Thrown", NULL); - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Filters Executed", NULL); - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Finallys Executed", NULL); - - // Reserved for future use. - //PhAddListViewItem(Context->CountersLv, MAXINT, L" Delta from throw to catch site on stack", NULL); - - // # of Exceps Thrown / sec - // This counter displays the number of exceptions thrown per second.These include both.NET exceptions and unmanaged exceptions that get converted into.NET exceptions e.g.null pointer reference exception in unmanaged code would get re - thrown in managed code as a.NET System.NullReferenceException; this counter includes both handled and unhandled exceptions.Exceptions should only occur in rare situations and not in the normal control flow of the program; this counter was designed as an indicator of potential performance problems due to large(> 100s) rate of exceptions thrown.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // # of Filters / sec - // This counter displays the number of.NET exception filters executed per second.An exception filter evaluates whether an exception should be handled or not.This counter tracks the rate of exception filters evaluated; irrespective of whether the exception was handled or not.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // # of Finallys / sec - // This counter displays the number of finally blocks executed per second.A finally block is guaranteed to be executed regardless of how the try block was exited.Only the finally blocks that are executed for an exception are counted; finally blocks on normal code paths are not counted by this counter.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Throw To Catch Depth / sec - // This counter displays the number of stack frames traversed from the frame that threw the.NET exception to the frame that handled the exception per second.This counter resets to 0 when an exception handler is entered; so nested exceptions would show the handler to handler stack depth.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval.* / - } - break; - case DOTNET_CATEGORY_INTEROP: - { - // This counter displays the current number of Com-Callable-Wrappers (CCWs). A CCW is a proxy for the .NET managed object being referenced from unmanaged COM client(s). This counter was designed to indicate the number of managed objects being referenced by unmanaged COM code. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of CCWs", NULL); - - // This counter displays the current number of stubs created by the CLR. Stubs are responsible for marshalling arguments and return values from managed to unmanaged code and vice versa; during a COM Interop call or PInvoke call. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Stubs", NULL); - - // This counter displays the total number of times arguments and return values have been marshaled from managed to unmanaged code and vice versa since the start of the application. This counter is not incremented if the stubs are inlined. (Stubs are responsible for marshalling arguments and return values). Stubs usually get inlined if the marshalling overhead is small. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Marshalling", NULL); - - // Reserved for future use. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of TLB imports / sec", NULL); - - // Reserved for future use. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of TLB exports / sec", NULL); - } - break; - case DOTNET_CATEGORY_JIT: - { - // This counter displays the total number of methods compiled Just-In-Time (JIT) by the CLR JIT compiler since the start of the application. This counter does not include the pre-jitted methods. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Methods Jitted", NULL); - - // This counter displays the total IL bytes jitted since the start of the application. This counter is exactly equivalent to the "Total # of IL Bytes Jitted" counter. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of IL Bytes Jitted", NULL); - - // This counter displays the total IL bytes jitted since the start of the application. This counter is exactly equivalent to the "# of IL Bytes Jitted" counter. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total # of IL Bytes Jitted", NULL); - - // This counter displays the peak number of methods the JIT compiler has failed to JIT since the start of the application. This failure can occur if the IL cannot be verified or if there was an internal error in the JIT compiler. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Jit Failures", NULL); - - // This counter displays the percentage of elapsed time spent in JIT compilation since the last JIT compilation phase. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase is the phase when a method and its dependencies are being compiled. - PhAddListViewItem(Context->CountersLv, MAXINT, L"% Time in Jit", NULL); - - // IL Bytes Jitted / sec - // This counter displays the rate at which IL bytes are jitted per second. This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - } - break; - case DOTNET_CATEGORY_LOADING: - { - // This counter displays the current number of classes loaded in all Assemblies. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Current Classes Loaded", NULL); - - // This counter displays the cumulative number of classes loaded in all Assemblies since the start of this application. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Classes Loaded", NULL); - - // This counter displays the current number of AppDomains loaded in this application. AppDomains (application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications running in the same process. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Current Appdomains", NULL); - - // This counter displays the peak number of AppDomains loaded since the start of this application. AppDomains (application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications running in the same process. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Appdomains", NULL); - - // This counter displays the current number of Assemblies loaded across all AppDomains in this application. If the Assembly is loaded as domain - neutral from multiple AppDomains then this counter is incremented once only. Assemblies can be loaded as domain - neutral when their code can be shared by all AppDomains or they can be loaded as domain - specific when their code is private to the AppDomain. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Current Assemblies", NULL); - - // This counter displays the total number of Assemblies loaded since the start of this application. If the Assembly is loaded as domain - neutral from multiple AppDomains then this counter is incremented once only. Assemblies can be loaded as domain - neutral when their code can be shared by all AppDomains or they can be loaded as domain - specific when their code is private to the AppDomain. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Assemblies", NULL); - - // Reserved for future use. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Assembly Search Length", NULL); - - // This counter displays the peak number of classes that have failed to load since the start of the application.These load failures could be due to many reasons like inadequate security or illegal format.Full details can be found in the profiling services help. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total # of Load Failures", NULL); - - // This counter displays the current size(in bytes) of the memory committed by the class loader across all AppDomains. (Committed memory is the physical memory for which space has been reserved on the disk paging file.) - PhAddListViewItem(Context->CountersLv, MAXINT, L"Bytes in Loader Heap", NULL); - - // This counter displays the total number of AppDomains unloaded since the start of the application. If an AppDomain is loaded and unloaded multiple times this counter would count each of those unloads as separate. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Appdomains Unloaded", NULL); - - // Reserved for future use. - //PhAddListViewItem(Context->CountersLv, MAXINT, L"% Time Loading", NULL); - - // Rate of Load Failures - // This counter displays the number of classes that failed to load per second. This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. These load failures could be due to many reasons like inadequate security or illegal format. Full details can be found in the profiling services help. - - // Rate of appdomains unloaded - // This counter displays the number of AppDomains unloaded per second.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Rate of Classes Loaded - // This counter displays the number of classes loaded per second in all Assemblies.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Rate of appdomains - // This counter displays the number of AppDomains loaded per second.AppDomains(application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications running in the same process.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Rate of Assemblies - // This counter displays the number of Assemblies loaded across all AppDomains per second.If the Assembly is loaded as domain - neutral from multiple AppDomains then this counter is incremented once only.Assemblies can be loaded as domain - neutral when their code can be shared by all AppDomains or they can be loaded as domain - specific when their code is private to the AppDomain.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - } - break; - case DOTNET_CATEGORY_LOCKSANDTHREADS: - { - // This counter displays the total number of times threads in the CLR have attempted to acquire a managed lock unsuccessfully. Managed locks can be acquired in many ways; by the "lock" statement in C# or by calling System.Monitor.Enter or by using MethodImplOptions.Synchronized custom attribute. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total # of Contentions", NULL); - - // This counter displays the total number of threads currently waiting to acquire some managed lock in the application. This counter is not an average over time; it displays the last observed value. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Current Queue Length", NULL); - - // This counter displays the total number of threads that waited to acquire some managed lock since the start of the application. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Queue Length Peak", NULL); - - // This counter displays the number of current.NET thread objects in the application.A.NET thread object is created either by new System.Threading.Thread or when an unmanaged thread enters the managed environment. This counters maintains the count of both running and stopped threads. This counter is not an average over time; it just displays the last observed value. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Current Logical Threads", NULL); - - // This counter displays the number of native OS threads created and owned by the CLR to act as underlying threads for .NET thread objects. This counters value does not include the threads used by the CLR in its internal operations; it is a subset of the threads in the OS process. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Current Physical Threads", NULL); - - // This counter displays the number of threads that are currently recognized by the CLR; they have a corresponding .NET thread object associated with them. These threads are not created by the CLR; they are created outside the CLR but have since run inside the CLR at least once. Only unique threads are tracked; threads with same thread ID re-entering the CLR or recreated after thread exit are not counted twice. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Current Recognized Threads", NULL); - - // This counter displays the total number of threads that have been recognized by the CLR since the start of this application; these threads have a corresponding .NET thread object associated with them. These threads are not created by the CLR; they are created outside the CLR but have since run inside the CLR at least once. Only unique threads are tracked; threads with same thread ID re-entering the CLR or recreated after thread exit are not counted twice. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Total Recognized Threads", NULL); - - // Contention Rate / sec - // Rate at which threads in the runtime attempt to acquire a managed lock unsuccessfully.Managed locks can be acquired in many ways; by the "lock" statement in C# or by calling System.Monitor.Enter or by using MethodImplOptions.Synchronized custom attribute. - - // Queue Length / sec - // This counter displays the number of threads per second waiting to acquire some lock in the application. This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // rate of recognized threads / sec - // This counter displays the number of threads per second that have been recognized by the CLR; these threads have a corresponding .NET thread object associated with them. These threads are not created by the CLR; they are created outside the CLR but have since run inside the CLR at least once. Only unique threads are tracked; threads with same thread ID re-entering the CLR or recreated after thread exit are not counted twice. This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - } - break; - case DOTNET_CATEGORY_MEMORY: - { - // This counter displays the number of times the generation 0 objects (youngest; most recently allocated) are garbage collected (Gen 0 GC) since the start of the application. Gen 0 GC occurs when the available memory in generation 0 is not sufficient to satisfy an allocation request. This counter is incremented at the end of a Gen 0 GC. Higher generation GCs include all lower generation GCs.This counter is explicitly incremented when a higher generation (Gen 1 or Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Gen 0 Collections", NULL); - - // This counter displays the number of times the generation 1 objects are garbage collected since the start of the application. The counter is incremented at the end of a Gen 1 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Gen 1 Collections", NULL); - - // This counter displays the number of times the generation 2 objects(older) are garbage collected since the start of the application.The counter is incremented at the end of a Gen 2 GC(also called full GC)._Global_ counter value is not accurate and should be ignored.This counter displays the last observed value. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Gen 2 Collections", NULL); - - // This counter displays the bytes of memory that survive garbage collection(GC) and are promoted from generation 0 to generation 1; objects that are promoted just because they are waiting to be finalized are not included in this counter.This counter displays the value observed at the end of the last GC; its not a cumulative counter. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Promoted Memory from Gen 0", NULL); - - // This counter displays the bytes of memory that survive garbage collection(GC) and are promoted from generation 1 to generation 2; objects that are promoted just because they are waiting to be finalized are not included in this counter.This counter displays the value observed at the end of the last GC; its not a cumulative counter.This counter is reset to 0 if the last GC was a Gen 0 GC only. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Promoted Memory from Gen 1", NULL); - - // This counter displays the bytes of memory that are promoted from generation 0 to generation 1 just because they are waiting to be finalized.This counter displays the value observed at the end of the last GC; its not a cumulative counter. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Promoted Finalization-Memory from Gen 0", NULL); - - // Reserved for future use. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Process ID", NULL); - - // This counter displays the maximum bytes that can be allocated in generation 0 (Gen 0); its does not indicate the current number of bytes allocated in Gen 0. A Gen 0 GC is triggered when the allocations since the last GC exceed this size.The Gen 0 size is tuned by the Garbage Collector and can change during the execution of the application.At the end of a Gen 0 collection the size of the Gen 0 heap is infact 0 bytes; this counter displays the size(in bytes) of allocations that would trigger the next Gen 0 GC.This counter is updated at the end of a GC; its not updated on every allocation. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Gen 0 Heap Size", NULL); - - // This counter displays the current number of bytes in generation 1 (Gen 1); this counter does not display the maximum size of Gen 1. Objects are not directly allocated in this generation; they are promoted from previous Gen 0 GCs.This counter is updated at the end of a GC; its not updated on every allocation. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Gen 1 Heap Size", NULL); - - // This counter displays the current number of bytes in generation 2 (Gen 2).Objects are not directly allocated in this generation; they are promoted from Gen 1 during previous Gen 1 GCs.This counter is updated at the end of a GC; its not updated on every allocation. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Gen 2 Heap Size", NULL); - - // This counter displays the current size of the Large Object Heap in bytes.Objects greater than 20 KBytes are treated as large objects by the Garbage Collector and are directly allocated in a special heap; they are not promoted through the generations.This counter is updated at the end of a GC; its not updated on every allocation. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Large Object Heap Size", NULL); - - // This counter displays the number of garbage collected objects that survive a collection because they are waiting to be finalized.If these objects hold references to other objects then those objects also survive but are not counted by this counter; the "Promoted Finalization-Memory from Gen 0" and "Promoted Finalization-Memory from Gen 1" counters represent all the memory that survived due to finalization.This counter is not a cumulative counter; its updated at the end of every GC with count of the survivors during that particular GC only.This counter was designed to indicate the extra overhead that the application might incur because of finalization. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Finalization Survivors", NULL); - - // This counter displays the current number of GC Handles in use.GCHandles are handles to resources external to the CLR and the managed environment.Handles occupy small amounts of memory in the GCHeap but potentially expensive unmanaged resources. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# GC Handles", NULL); - - // This counter displays the peak number of times a garbage collection was performed because of an explicit call to GC.Collect. Its a good practice to let the GC tune the frequency of its collections. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Induced GC", NULL); - - // % Time in GC is the percentage of elapsed time that was spent in performing a garbage collection(GC) since the last GC cycle. This counter is usually an indicator of the work done by the Garbage Collector on behalf of the application to collect and compact memory.This counter is updated only at the end of every GC and the counter value reflects the last observed value; its not an average. - PhAddListViewItem(Context->CountersLv, MAXINT, L"% Time in GC", NULL); - - // This counter is the sum of four other counters; Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size. This counter indicates the current memory allocated in bytes on the GC Heaps. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Bytes in all Heaps", NULL); - - // This counter displays the amount of virtual memory(in bytes) currently committed by the Garbage Collector. (Committed memory is the physical memory for which space has been reserved on the disk paging file). - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Total Committed Bytes", NULL); - - // This counter displays the amount of virtual memory(in bytes) currently reserved by the Garbage Collector. (Reserved memory is the virtual memory space reserved for the application but no disk or main memory pages have been used.) - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Total Reserved Bytes", NULL); - - // This counter displays the number of pinned objects encountered in the last GC. This counter tracks the pinned objects only in the heaps that were garbage collected e.g. A Gen 0 GC would cause enumeration of pinned objects in the generation 0 heap only. A pinned object is one that the Garbage Collector cannot move in memory. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Pinned Objects", NULL); - - // This counter displays the current number of sync blocks in use. Sync blocks are per-object data structures allocated for storing synchronization information. Sync blocks hold weak references to managed objects and need to be scanned by the Garbage Collector. Sync blocks are not limited to storing synchronization information and can also store COM interop metadata. This counter was designed to indicate performance problems with heavy use of synchronization primitives. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of Sink Blocks in use", NULL); - - // Reserved for future use. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Bytes Allocated (since start)", NULL); - - // Reserved for future use. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Bytes Allocated for Large Objects (since start)", NULL); - - // Gen 0 Promoted Bytes / Sec - // This counter displays the bytes per second that are promoted from generation 0 (youngest)to generation 1; objects that are promoted just because they are waiting to be finalized are not included in this counter.Memory is promoted when it survives a garbage collection.This counter was designed as an indicator of relatively long - lived objects being created per sec.This counter displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Gen 1 Promoted Bytes / Sec - // This counter displays the bytes per second that are promoted from generation 1 to generation 2 (oldest); objects that are promoted just because they are waiting to be finalized are not included in this counter.Memory is promoted when it survives a garbage collection.Nothing is promoted from generation 2 since it is the oldest.This counter was designed as an indicator of very long - lived objects being created per sec.This counter displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Promoted Finalization - Memory from Gen 1 - // This counter displays the bytes of memory that are promoted from generation 1 to generation 2 just because they are waiting to be finalized.This counter displays the value observed at the end of the last GC; its not a cumulative counter.This counter is reset to 0 if the last GC was a Gen 0 GC only. - - // Allocated Bytes / sec - // This counter displays the rate of bytes per second allocated on the GC Heap.This counter is updated at the end of every GC; not at each allocation.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - } - break; - case DOTNET_CATEGORY_REMOTING: - { - // This counter displays the total number of remote procedure calls invoked since the start of this application. A remote procedure call is a call on any object outside the callers AppDomain. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Remote Calls", NULL); - - // This counter displays the total number of remoting channels registered across all AppDomains since the start of the application. Channels are used to transport messages to and from remote objects. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Channels", NULL); - - // This counter displays the total number of remoting proxy objects created in this process since the start of the process. Proxy object acts as a representative of the remote objects and ensures that all calls made on the proxy are forwarded to the correct remote object instance. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Context Proxies", NULL); - - // This counter displays the current number of context-bound classes loaded. Classes that can be bound to a context are called context-bound classes; context-bound classes are marked with Context Attributes which provide usage rules for synchronization; thread affinity; transactions etc. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Context-Bound Classes Loaded", NULL); - - // This counter displays the current number of remoting contexts in the application. A context is a boundary containing a collection of objects with the same usage rules like synchronization; thread affinity; transactions etc. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Contexts", NULL); - - PhAddListViewItem(Context->CountersLv, MAXINT, L"# of context bound objects allocated", NULL); - - // Remote Calls / sec - // This counter displays the number of remote procedure calls invoked per second. A remote procedure call is a call on any object outside the callers AppDomain. This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - - // Context - Bound Objects Alloc / sec - // This counter displays the number of context - bound objects allocated per second. Instances of classes that can be bound to a context are called context - bound objects; context - bound classes are marked with Context Attributes which provide usage rules for synchronization; thread affinity; transactions etc.This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. - } - break; - case DOTNET_CATEGORY_SECURITY: - { - // This counter displays the total number of runtime Code Access Security(CAS) checks performed since the start of the application. Runtime CAS checks are performed when a caller makes a call to a callee demanding a particular permission; the runtime check is made on every call by the caller; the check is done by examining the current thread stack of the caller. This counter used together with "Stack Walk Depth" is indicative of performance penalty for security checks. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Total Runtime Checks", NULL); - - // This counter displays the total number of linktime Code Access Security(CAS) checks since the start of the application. Linktime CAS checks are performed when a caller makes a call to a callee demanding a particular permission at JIT compile time; linktime check is performed once per caller. This count is not indicative of serious performance issues; its indicative of the security system activity. - PhAddListViewItem(Context->CountersLv, MAXINT, L"# Link Time Checks", NULL); - - // This counter displays the percentage of elapsed time spent in performing runtime Code Access Security(CAS) checks since the last such check. CAS allows code to be trusted to varying degrees and enforces these varying levels of trust depending on code identity. This counter is updated at the end of a runtime security check; it represents the last observed value; its not an average. - PhAddListViewItem(Context->CountersLv, MAXINT, L"% Time in RT checks", NULL); - - // This counter displays the depth of the stack during that last runtime Code Access Security check.Runtime Code Access Security check is performed by crawling the stack. This counter is not an average; it just displays the last observed value. - PhAddListViewItem(Context->CountersLv, MAXINT, L"Stack Walk Depth", NULL); - - // % Time Sig.Authenticating - // Reserved for future use. - } - break; - } -} - -VOID AddProcessAppDomains( - _In_ HWND hwndDlg, - _In_ PPERFPAGE_CONTEXT Context - ) -{ - SendMessage(Context->AppDomainsLv, WM_SETREDRAW, FALSE, 0); - ListView_DeleteAllItems(Context->AppDomainsLv); - - if (Context->ClrV4) - { - PPH_LIST processAppDomains = QueryDotNetAppDomainsForPid_V4( - Context->IsWow64, - Context->ProcessHandle, - Context->ProcessItem->ProcessId - ); - - if (processAppDomains) - { - for (ULONG i = 0; i < processAppDomains->Count; i++) - { - PhAddListViewItem(Context->AppDomainsLv, MAXINT, processAppDomains->Items[i], NULL); - PhFree(processAppDomains->Items[i]); - } - - PhDereferenceObject(processAppDomains); - } - } - else - { - PPH_LIST processAppDomains = QueryDotNetAppDomainsForPid_V2( - Context->IsWow64, - Context->ProcessHandle, - Context->ProcessItem->ProcessId - ); - - if (processAppDomains) - { - for (ULONG i = 0; i < processAppDomains->Count; i++) - { - PhAddListViewItem(Context->AppDomainsLv, MAXINT, processAppDomains->Items[i], NULL); - PhFree(processAppDomains->Items[i]); - } - - PhDereferenceObject(processAppDomains); - } - } - - SendMessage(Context->AppDomainsLv, WM_SETREDRAW, TRUE, 0); -} - -VOID UpdateCounterData( - _In_ HWND hwndDlg, - _In_ PPERFPAGE_CONTEXT Context - ) -{ - PVOID perfStatBlock = NULL; - Perf_GC dotNetPerfGC; - Perf_Contexts dotNetPerfContext; - Perf_Interop dotNetPerfInterop; - Perf_Loading dotNetPerfLoading; - Perf_Excep dotNetPerfExcep; - Perf_LocksAndThreads dotNetPerfLocksAndThreads; - Perf_Jit dotNetPerfJit; - Perf_Security dotNetPerfSecurity; - - if (Context->ClrV4) - { - perfStatBlock = GetPerfIpcBlock_V4( - Context->IsWow64, - Context->BlockTableAddress - ); - } - else - { - perfStatBlock = GetPerfIpcBlock_V2( - Context->IsWow64, - Context->BlockTableAddress - ); - } - - if (!perfStatBlock) - return; - - if (Context->IsWow64) - { - PerfCounterIPCControlBlock_Wow64* perfBlock = perfStatBlock; - Perf_GC_Wow64 dotNetPerfGC_Wow64 = perfBlock->GC; - Perf_Loading_Wow64 dotNetPerfLoading_Wow64 = perfBlock->Loading; - Perf_Security_Wow64 dotNetPerfSecurity_Wow64 = perfBlock->Security; - - // Thunk the Wow64 structures into their 64bit versions (or 32bit version on x86). - - dotNetPerfGC.cGenCollections[0] = dotNetPerfGC_Wow64.cGenCollections[0]; - dotNetPerfGC.cGenCollections[1] = dotNetPerfGC_Wow64.cGenCollections[1]; - dotNetPerfGC.cGenCollections[2] = dotNetPerfGC_Wow64.cGenCollections[2]; - dotNetPerfGC.cbPromotedMem[0] = dotNetPerfGC_Wow64.cbPromotedMem[0]; - dotNetPerfGC.cbPromotedMem[1] = dotNetPerfGC_Wow64.cbPromotedMem[1]; - dotNetPerfGC.cbPromotedFinalizationMem = dotNetPerfGC_Wow64.cbPromotedFinalizationMem; - dotNetPerfGC.cProcessID = dotNetPerfGC_Wow64.cProcessID; - dotNetPerfGC.cGenHeapSize[0] = dotNetPerfGC_Wow64.cGenHeapSize[0]; - dotNetPerfGC.cGenHeapSize[1] = dotNetPerfGC_Wow64.cGenHeapSize[1]; - dotNetPerfGC.cGenHeapSize[2] = dotNetPerfGC_Wow64.cGenHeapSize[2]; - dotNetPerfGC.cTotalCommittedBytes = dotNetPerfGC_Wow64.cTotalCommittedBytes; - dotNetPerfGC.cTotalReservedBytes = dotNetPerfGC_Wow64.cTotalReservedBytes; - dotNetPerfGC.cLrgObjSize = dotNetPerfGC_Wow64.cLrgObjSize; - dotNetPerfGC.cSurviveFinalize = dotNetPerfGC_Wow64.cSurviveFinalize; - dotNetPerfGC.cHandles = dotNetPerfGC_Wow64.cHandles; - dotNetPerfGC.cbAlloc = dotNetPerfGC_Wow64.cbAlloc; - dotNetPerfGC.cbLargeAlloc = dotNetPerfGC_Wow64.cbLargeAlloc; - dotNetPerfGC.cInducedGCs = dotNetPerfGC_Wow64.cInducedGCs; - dotNetPerfGC.timeInGC = dotNetPerfGC_Wow64.timeInGC; - dotNetPerfGC.timeInGCBase = dotNetPerfGC_Wow64.timeInGCBase; - dotNetPerfGC.cPinnedObj = dotNetPerfGC_Wow64.cPinnedObj; - dotNetPerfGC.cSinkBlocks = dotNetPerfGC_Wow64.cSinkBlocks; - - dotNetPerfContext = perfBlock->Context; - dotNetPerfInterop = perfBlock->Interop; - - dotNetPerfLoading.cClassesLoaded.Current = dotNetPerfLoading_Wow64.cClassesLoaded.Current; - dotNetPerfLoading.cClassesLoaded.Total = dotNetPerfLoading_Wow64.cClassesLoaded.Total; - dotNetPerfLoading.cAppDomains.Current = dotNetPerfLoading_Wow64.cAppDomains.Current; - dotNetPerfLoading.cAppDomains.Total = dotNetPerfLoading_Wow64.cAppDomains.Total; - dotNetPerfLoading.cAssemblies.Current = dotNetPerfLoading_Wow64.cAssemblies.Current; - dotNetPerfLoading.cAssemblies.Total = dotNetPerfLoading_Wow64.cAssemblies.Total; - dotNetPerfLoading.timeLoading = dotNetPerfLoading_Wow64.timeLoading; - dotNetPerfLoading.cAsmSearchLen = dotNetPerfLoading_Wow64.cAsmSearchLen; - dotNetPerfLoading.cLoadFailures.Total = dotNetPerfLoading_Wow64.cLoadFailures.Total; - dotNetPerfLoading.cbLoaderHeapSize = dotNetPerfLoading_Wow64.cbLoaderHeapSize; - dotNetPerfLoading.cAppDomainsUnloaded = dotNetPerfLoading_Wow64.cAppDomainsUnloaded; - - dotNetPerfExcep = perfBlock->Excep; - dotNetPerfLocksAndThreads = perfBlock->LocksAndThreads; - dotNetPerfJit = perfBlock->Jit; - - dotNetPerfSecurity.cTotalRTChecks = dotNetPerfSecurity_Wow64.cTotalRTChecks; - dotNetPerfSecurity.timeAuthorize = dotNetPerfSecurity_Wow64.timeAuthorize; - dotNetPerfSecurity.cLinkChecks = dotNetPerfSecurity_Wow64.cLinkChecks; - dotNetPerfSecurity.timeRTchecks = dotNetPerfSecurity_Wow64.timeRTchecks; - dotNetPerfSecurity.timeRTchecksBase = dotNetPerfSecurity_Wow64.timeRTchecksBase; - dotNetPerfSecurity.stackWalkDepth = dotNetPerfSecurity_Wow64.stackWalkDepth; - } - else - { - PerfCounterIPCControlBlock* perfBlock = perfStatBlock; - - dotNetPerfGC = perfBlock->GC; - dotNetPerfContext = perfBlock->Context; - dotNetPerfInterop = perfBlock->Interop; - dotNetPerfLoading = perfBlock->Loading; - dotNetPerfExcep = perfBlock->Excep; - dotNetPerfLocksAndThreads = perfBlock->LocksAndThreads; - dotNetPerfJit = perfBlock->Jit; - dotNetPerfSecurity = perfBlock->Security; - } - - switch (Context->CategoryIndex) - { - case DOTNET_CATEGORY_EXCEPTIONS: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfExcep.cThrown.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfExcep.cFiltersExecuted, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, PhaFormatUInt64(dotNetPerfExcep.cFinallysExecuted, TRUE)->Buffer); - //PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfExcep.cThrowToCatchStackDepth, TRUE)->Buffer); - } - break; - case DOTNET_CATEGORY_INTEROP: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfInterop.cCCW, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfInterop.cStubs, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, PhaFormatUInt64(dotNetPerfInterop.cMarshalling, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfInterop.cTLBImports, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 4, 1, PhaFormatUInt64(dotNetPerfInterop.cTLBExports, TRUE)->Buffer); - } - break; - case DOTNET_CATEGORY_JIT: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfJit.cMethodsJitted, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, FormatByteValue(Context, dotNetPerfJit.cbILJitted.Current)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, FormatByteValue(Context, dotNetPerfJit.cbILJitted.Total)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfJit.cJitFailures, TRUE)->Buffer); - - if (dotNetPerfJit.timeInJitBase != 0) - { - PH_FORMAT format; - WCHAR formatBuffer[10]; - - // TODO: TimeInJit is always above 100% for some processes ?? - // SeeAlso: https://github.com/dotnet/coreclr/blob/master/src/gc/gcee.cpp#L324 - PhInitFormatF(&format, (dotNetPerfJit.timeInJit << 8) * 100 / (FLOAT)(dotNetPerfJit.timeInJitBase << 8), 2); - - if (PhFormatToBuffer(&format, 1, formatBuffer, sizeof(formatBuffer), NULL)) - PhSetListViewSubItem(Context->CountersLv, 4, 1, formatBuffer); - } - else - { - PhSetListViewSubItem(Context->CountersLv, 4, 1, L"0.00"); - } - } - break; - case DOTNET_CATEGORY_LOADING: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfLoading.cClassesLoaded.Current, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfLoading.cClassesLoaded.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, PhaFormatUInt64(dotNetPerfLoading.cAppDomains.Current, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfLoading.cAppDomains.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 4, 1, PhaFormatUInt64(dotNetPerfLoading.cAssemblies.Current, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 5, 1, PhaFormatUInt64(dotNetPerfLoading.cAssemblies.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 6, 1, PhaFormatUInt64(dotNetPerfLoading.cAsmSearchLen, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 7, 1, PhaFormatUInt64(dotNetPerfLoading.cLoadFailures.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 8, 1, FormatByteValue(Context, dotNetPerfLoading.cbLoaderHeapSize)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 9, 1, PhaFormatUInt64(dotNetPerfLoading.cAppDomainsUnloaded.Total, TRUE)->Buffer); - //PhSetListViewSubItem(Context->CountersLv, 10, 1, PhaFormatUInt64(dotNetPerfLoading.timeLoading, TRUE)->Buffer); - } - break; - case DOTNET_CATEGORY_LOCKSANDTHREADS: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cContention.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cQueueLength.Current, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cQueueLength.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cCurrentThreadsLogical, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 4, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cCurrentThreadsPhysical, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 5, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cRecognizedThreads.Current, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 6, 1, PhaFormatUInt64(dotNetPerfLocksAndThreads.cRecognizedThreads.Total, TRUE)->Buffer); - } - break; - case DOTNET_CATEGORY_MEMORY: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfGC.cGenCollections[0], TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfGC.cGenCollections[1], TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, PhaFormatUInt64(dotNetPerfGC.cGenCollections[2], TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 3, 1, FormatByteValue(Context, dotNetPerfGC.cbPromotedMem[0])->Buffer); - PhSetListViewSubItem(Context->CountersLv, 4, 1, FormatByteValue(Context, dotNetPerfGC.cbPromotedMem[1])->Buffer); - PhSetListViewSubItem(Context->CountersLv, 5, 1, FormatByteValue(Context, dotNetPerfGC.cbPromotedFinalizationMem)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 6, 1, PhaFormatUInt64(dotNetPerfGC.cProcessID, FALSE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 7, 1, FormatByteValue(Context, dotNetPerfGC.cGenHeapSize[0])->Buffer); - PhSetListViewSubItem(Context->CountersLv, 8, 1, FormatByteValue(Context, dotNetPerfGC.cGenHeapSize[1])->Buffer); - PhSetListViewSubItem(Context->CountersLv, 9, 1, FormatByteValue(Context, dotNetPerfGC.cGenHeapSize[2])->Buffer); - PhSetListViewSubItem(Context->CountersLv, 10, 1, FormatByteValue(Context, dotNetPerfGC.cLrgObjSize)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 11, 1, PhaFormatUInt64(dotNetPerfGC.cSurviveFinalize, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 12, 1, PhaFormatUInt64(dotNetPerfGC.cHandles, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 13, 1, PhaFormatUInt64(dotNetPerfGC.cInducedGCs, TRUE)->Buffer); - - if (dotNetPerfGC.timeInGCBase != 0) - { - PH_FORMAT format; - WCHAR formatBuffer[10]; - - PhInitFormatF(&format, (FLOAT)dotNetPerfGC.timeInGC * 100 / (FLOAT)dotNetPerfGC.timeInGCBase, 2); - - if (PhFormatToBuffer(&format, 1, formatBuffer, sizeof(formatBuffer), NULL)) - PhSetListViewSubItem(Context->CountersLv, 14, 1, formatBuffer); - } - else - { - PhSetListViewSubItem(Context->CountersLv, 14, 1, L"0.00"); - } - - // The CLR source says this value should be "Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size", - // but Perflib only counts Gen 1, Gen 2 and cLrgObjSize... For now, just do what perflib does. - PhSetListViewSubItem(Context->CountersLv, 15, 1, FormatByteValue(Context, dotNetPerfGC.cGenHeapSize[1] + dotNetPerfGC.cGenHeapSize[2] + dotNetPerfGC.cLrgObjSize)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 16, 1, FormatByteValue(Context, dotNetPerfGC.cTotalCommittedBytes)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 17, 1, FormatByteValue(Context, dotNetPerfGC.cTotalReservedBytes)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 18, 1, PhaFormatUInt64(dotNetPerfGC.cPinnedObj, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 19, 1, PhaFormatUInt64(dotNetPerfGC.cSinkBlocks, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 20, 1, FormatByteValue(Context, dotNetPerfGC.cbAlloc)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 21, 1, FormatByteValue(Context, dotNetPerfGC.cbLargeAlloc)->Buffer); - } - break; - case DOTNET_CATEGORY_REMOTING: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfContext.cRemoteCalls.Total, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfContext.cChannels, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 2, 1, PhaFormatUInt64(dotNetPerfContext.cProxies, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfContext.cClasses, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 4, 1, PhaFormatUInt64(dotNetPerfContext.cContexts, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 5, 1, PhaFormatUInt64(dotNetPerfContext.cObjAlloc, TRUE)->Buffer); - } - break; - case DOTNET_CATEGORY_SECURITY: - { - PhSetListViewSubItem(Context->CountersLv, 0, 1, PhaFormatUInt64(dotNetPerfSecurity.cTotalRTChecks, TRUE)->Buffer); - PhSetListViewSubItem(Context->CountersLv, 1, 1, PhaFormatUInt64(dotNetPerfSecurity.cLinkChecks, TRUE)->Buffer); - - if (dotNetPerfSecurity.timeRTchecksBase != 0) - { - PH_FORMAT format; - WCHAR formatBuffer[10]; - - PhInitFormatF(&format, (FLOAT)dotNetPerfSecurity.timeRTchecks * 100 / (FLOAT)dotNetPerfSecurity.timeRTchecksBase, 2); - - if (PhFormatToBuffer(&format, 1, formatBuffer, sizeof(formatBuffer), NULL)) - PhSetListViewSubItem(Context->CountersLv, 2, 1, formatBuffer); - } - else - { - PhSetListViewSubItem(Context->CountersLv, 2, 1, L"0.00"); - } - - PhSetListViewSubItem(Context->CountersLv, 3, 1, PhaFormatUInt64(dotNetPerfSecurity.stackWalkDepth, TRUE)->Buffer); - } - break; - } -} - -INT_PTR CALLBACK DotNetPerfPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - PPERFPAGE_CONTEXT context; - - if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) - { - context = propPageContext->Context; - } - else - { - return FALSE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - context = PhAllocate(sizeof(PERFPAGE_CONTEXT)); - memset(context, 0, sizeof(PERFPAGE_CONTEXT)); - - propPageContext->Context = context; - context->WindowHandle = hwndDlg; - context->ProcessItem = processItem; - context->Enabled = TRUE; - - context->AppDomainsLv = GetDlgItem(hwndDlg, IDC_APPDOMAINS); - context->CountersLv = GetDlgItem(hwndDlg, IDC_COUNTERS); - context->CategoriesCb = GetDlgItem(hwndDlg, IDC_CATEGORIES); - - PhSetListViewStyle(context->AppDomainsLv, FALSE, TRUE); - PhSetControlTheme(context->AppDomainsLv, L"explorer"); - PhAddListViewColumn(context->AppDomainsLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Application domain"); - - PhSetListViewStyle(context->CountersLv, FALSE, TRUE); - PhSetControlTheme(context->CountersLv, L"explorer"); - PhAddListViewColumn(context->CountersLv, 0, 0, 0, LVCFMT_LEFT, 250, L"Counter"); - PhAddListViewColumn(context->CountersLv, 1, 1, 1, LVCFMT_RIGHT, 140, L"Value"); - PhLoadListViewColumnsFromSetting(SETTING_NAME_DOT_NET_COUNTERS_COLUMNS, context->CountersLv); - - if (PhGetIntegerSetting(SETTING_NAME_DOT_NET_SHOW_BYTE_SIZE)) - { - context->ShowByteSize = TRUE; - Button_SetCheck(GetDlgItem(hwndDlg, IDC_DOTNET_PERF_SHOWBYTES), TRUE); - } - - PhAddComboBoxStrings(context->CategoriesCb, DotNetCategoryStrings, ARRAYSIZE(DotNetCategoryStrings)); - context->CategoryIndex = PhGetIntegerSetting(SETTING_NAME_DOT_NET_CATEGORY_INDEX); - ComboBox_SetCurSel(context->CategoriesCb, context->CategoryIndex); - -#ifdef _WIN64 - context->IsWow64 = !!context->ProcessItem->IsWow64; -#else - // HACK: Work-around for Appdomain enumeration on 32bit. - context->IsWow64 = TRUE; -#endif - - if (NT_SUCCESS(PhOpenProcess( - &context->ProcessHandle, - PROCESS_VM_READ | ProcessQueryAccess | PROCESS_DUP_HANDLE | SYNCHRONIZE, - context->ProcessItem->ProcessId - ))) - { - ULONG flags = 0; - - if (NT_SUCCESS(PhGetProcessIsDotNetEx( - context->ProcessItem->ProcessId, - context->ProcessHandle, - context->ProcessItem->IsImmersive == 1 ? 0 : PH_CLR_USE_SECTION_CHECK, - NULL, - &flags - ))) - { - if (flags & PH_CLR_VERSION_4_ABOVE) - { - context->ClrV4 = TRUE; - } - } - - // Skip AppDomain enumeration of 'Modern' .NET applications as they don't expose the CLR 'Private IPC' block. - if (!context->ProcessItem->IsImmersive) - { - AddProcessAppDomains(hwndDlg, context); - } - } - - if (context->ClrV4) - { - if (OpenDotNetPublicControlBlock_V4( - !!context->ProcessItem->IsImmersive, - context->ProcessHandle, - context->ProcessItem->ProcessId, - &context->BlockTableHandle, - &context->BlockTableAddress - )) - { - context->ControlBlockValid = TRUE; - } - } - else - { - if (OpenDotNetPublicControlBlock_V2( - context->ProcessItem->ProcessId, - &context->BlockTableHandle, - &context->BlockTableAddress - )) - { - context->ControlBlockValid = TRUE; - } - } - - if (context->ControlBlockValid) - { - UpdateCategoryValues(hwndDlg, context); - UpdateCounterData(hwndDlg, context); - } - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessesUpdated), - ProcessesUpdatedCallback, - context, - &context->ProcessesUpdatedCallbackRegistration - ); - } - break; - case WM_DESTROY: - { - PhUnregisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessesUpdated), - &context->ProcessesUpdatedCallbackRegistration - ); - - if (context->BlockTableAddress) - { - NtUnmapViewOfSection(NtCurrentProcess(), context->BlockTableAddress); - } - - if (context->BlockTableHandle) - { - NtClose(context->BlockTableHandle); - } - - if (context->ProcessHandle) - { - NtClose(context->ProcessHandle); - } - - PhSaveListViewColumnsToSetting(SETTING_NAME_DOT_NET_COUNTERS_COLUMNS, context->CountersLv); - - PhFree(context); - PhPropPageDlgProcDestroy(hwndDlg); - } - break; - case WM_SHOWWINDOW: - { - PPH_LAYOUT_ITEM dialogItem; - - if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) - { - PhAddPropPageLayoutItem(hwndDlg, context->AppDomainsLv, dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, context->CategoriesCb, dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddPropPageLayoutItem(hwndDlg, context->CountersLv, dialogItem, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_DOTNET_PERF_SHOWBYTES), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); - PhEndPropPageLayout(hwndDlg, propPageContext); - } - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDC_CATEGORIES: - { - if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) - { - context->CategoryIndex = ComboBox_GetCurSel(context->CategoriesCb); - PhSetIntegerSetting(SETTING_NAME_DOT_NET_CATEGORY_INDEX, context->CategoryIndex); - - if (context->ControlBlockValid) - { - UpdateCategoryValues(hwndDlg, context); - UpdateCounterData(hwndDlg, context); - } - } - } - break; - case IDC_DOTNET_PERF_SHOWBYTES: - { - if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED) - { - context->ShowByteSize = !context->ShowByteSize; - PhSetIntegerSetting(SETTING_NAME_DOT_NET_SHOW_BYTE_SIZE, context->ShowByteSize); - - if (context->ControlBlockValid) - { - UpdateCategoryValues(hwndDlg, context); - UpdateCounterData(hwndDlg, context); - } - } - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_SETACTIVE: - context->Enabled = TRUE; - break; - case PSN_KILLACTIVE: - context->Enabled = FALSE; - break; - } - - PhHandleListViewNotifyForCopy(lParam, context->AppDomainsLv); - PhHandleListViewNotifyForCopy(lParam, context->CountersLv); - } - break; - case MSG_UPDATE: - { - if (context->Enabled && context->ControlBlockValid) - { - UpdateCounterData(hwndDlg, context); - } - } - break; - } - - return FALSE; -} - -VOID AddPerfPageToPropContext( - _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext - ) -{ - PhAddProcessPropPage( - PropContext->PropContext, - PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDOTNETPERF), DotNetPerfPageDlgProc, NULL) - ); -} \ No newline at end of file +/* + * Process Hacker .NET Tools - + * .NET Performance property page + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2015-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "clr\perfcounterdefs.h" + +PWSTR DotNetCategoryStrings[] = +{ + L".NET CLR Exceptions", + L".NET CLR Interop", + L".NET CLR Jit", + L".NET CLR Loading", + L".NET CLR LocksAndThreads", + L".NET CLR Memory", + L".NET CLR Remoting", + L".NET CLR Security" +}; + +typedef enum _DOTNET_CATEGORY +{ + // .NET CLR Exceptions (Runtime statistics on CLR exception handling) + DOTNET_CATEGORY_EXCEPTIONS, + // .NET CLR Interop (Stats for CLR interop) + DOTNET_CATEGORY_INTEROP, + // .NET CLR Jit (Stats for CLR Jit) + DOTNET_CATEGORY_JIT, + // .NET CLR Loading (Statistics for CLR Class Loader) + DOTNET_CATEGORY_LOADING, + // .NET CLR LocksAndThreads (Stats for CLR Locks and Threads) + DOTNET_CATEGORY_LOCKSANDTHREADS, + // .NET CLR Memory (Counters for CLR Garbage Collected heap) + DOTNET_CATEGORY_MEMORY, + // .NET CLR Remoting (Stats for CLR Remoting) + DOTNET_CATEGORY_REMOTING, + // .NET CLR Security (Stats for CLR Security) + DOTNET_CATEGORY_SECURITY +} DOTNET_CATEGORY; + +typedef enum _DOTNET_INDEX +{ + DOTNET_INDEX_EXCEPTIONS_THROWNCOUNT, + DOTNET_INDEX_EXCEPTIONS_FILTERSCOUNT, + DOTNET_INDEX_EXCEPTIONS_FINALLYCOUNT, + + DOTNET_INDEX_INTEROP_CCWCOUNT, + DOTNET_INDEX_INTEROP_STUBCOUNT, + DOTNET_INDEX_INTEROP_MARSHALCOUNT, + DOTNET_INDEX_INTEROP_TLBIMPORTPERSEC, + DOTNET_INDEX_INTEROP_TLBEXPORTPERSEC, + + DOTNET_INDEX_JIT_ILMETHODSJITTED, + DOTNET_INDEX_JIT_ILBYTESJITTED, + DOTNET_INDEX_JIT_ILTOTALBYTESJITTED, + DOTNET_INDEX_JIT_FAILURES, + DOTNET_INDEX_JIT_TIME, + + DOTNET_INDEX_LOADING_CURRENTLOADED, + DOTNET_INDEX_LOADING_TOTALLOADED, + DOTNET_INDEX_LOADING_CURRENTAPPDOMAINS, + DOTNET_INDEX_LOADING_TOTALAPPDOMAINS, + DOTNET_INDEX_LOADING_CURRENTASSEMBLIES, + DOTNET_INDEX_LOADING_TOTALASSEMBLIES, + DOTNET_INDEX_LOADING_ASSEMBLYSEARCHLENGTH, + DOTNET_INDEX_LOADING_TOTALLOADFAILURES, + DOTNET_INDEX_LOADING_BYTESINLOADERHEAP, + DOTNET_INDEX_LOADING_TOTALAPPDOMAINSUNLOADED, + + DOTNET_INDEX_LOCKSANDTHREADS_TOTALLOCKS, + DOTNET_INDEX_LOCKSANDTHREADS_TOTALQUEUELENGTH, + DOTNET_INDEX_LOCKSANDTHREADS_QUEUELENGTHPEAK, + DOTNET_INDEX_LOCKSANDTHREADS_CURRENTLOGICAL, + DOTNET_INDEX_LOCKSANDTHREADS_CURRENTPHYSICAL, + DOTNET_INDEX_LOCKSANDTHREADS_CURRENTRECOGNIZED, + DOTNET_INDEX_LOCKSANDTHREADS_TOTALRECOGNIZED, + + DOTNET_INDEX_MEMORY_GENZEROCOLLECTIONS, + DOTNET_INDEX_MEMORY_GENONECOLLECTIONS, + DOTNET_INDEX_MEMORY_GENTWOCOLLECTIONS, + DOTNET_INDEX_MEMORY_PROMOTEDFROMGENZERO, + DOTNET_INDEX_MEMORY_PROMOTEDFROMGENONE, + DOTNET_INDEX_MEMORY_PROMOTEDFINALFROMGENZERO, + DOTNET_INDEX_MEMORY_PROCESSID, + DOTNET_INDEX_MEMORY_GENZEROHEAPSIZE, + DOTNET_INDEX_MEMORY_GENONEHEAPSIZE, + DOTNET_INDEX_MEMORY_GENTWOHEAPSIZE, + DOTNET_INDEX_MEMORY_LOHSIZE, + DOTNET_INDEX_MEMORY_FINALSURVIVORS, + DOTNET_INDEX_MEMORY_GCHANDLES, + DOTNET_INDEX_MEMORY_INDUCEDGC, + DOTNET_INDEX_MEMORY_TIMEINGC, + DOTNET_INDEX_MEMORY_BYTESINALLHEAPS, + DOTNET_INDEX_MEMORY_TOTALCOMMITTED, + DOTNET_INDEX_MEMORY_TOTALRESERVED, + DOTNET_INDEX_MEMORY_TOTALPINNED, + DOTNET_INDEX_MEMORY_TOTALSINKS, + DOTNET_INDEX_MEMORY_TOTALBYTESSINCESTART, + DOTNET_INDEX_MEMORY_TOTALLOHBYTESSINCESTART, + + DOTNET_INDEX_REMOTING_TOTALREMOTECALLS, + DOTNET_INDEX_REMOTING_CHANNELS, + DOTNET_INDEX_REMOTING_CONTEXTPROXIES, + DOTNET_INDEX_REMOTING_CONTEXTCLASSESLOADED, + DOTNET_INDEX_REMOTING_CONTEXTS, + DOTNET_INDEX_REMOTING_CONTEXTSALLOCATED, + + DOTNET_INDEX_SECURITY_TOTALRUNTIMECHECKS, + DOTNET_INDEX_SECURITY_LINKTIMECHECKS, + DOTNET_INDEX_SECURITY_TIMEINRTCHECKS, + DOTNET_INDEX_SECURITY_STACKWALKDEPTH, + + DOTNET_INDEX_MAXIMUM +} DOTNET_INDEX; + +typedef struct _PERFPAGE_CONTEXT +{ + HWND WindowHandle; + HWND CountersListViewHandle; + PPH_PROCESS_ITEM ProcessItem; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN Enabled : 1; + BOOLEAN ControlBlockValid : 1; + BOOLEAN ClrV4 : 1; + BOOLEAN IsWow64 : 1; + BOOLEAN Spare : 4; + }; + }; + + HANDLE ProcessHandle; + PVOID BlockTableAddress; + PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; + Perf_GC DotNetPerfGC; + Perf_Contexts DotNetPerfContext; + Perf_Interop DotNetPerfInterop; + Perf_Loading DotNetPerfLoading; + Perf_Excep DotNetPerfExceptions; + Perf_LocksAndThreads DotNetPerfLocksAndThreads; + Perf_Jit DotNetPerfJit; + Perf_Security DotNetPerfSecurity; +} PERFPAGE_CONTEXT, *PPERFPAGE_CONTEXT; + +VOID NTAPI DotNetPerfProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPERFPAGE_CONTEXT context = Context; + + if (context->WindowHandle) + { + PostMessage(context->WindowHandle, MSG_UPDATE, 0, 0); + } +} + +VOID DotNetPerfAddListViewGroups( + _In_ HWND ListViewHandle + ) +{ + ListView_EnableGroupView(ListViewHandle, TRUE); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_EXCEPTIONS, L".NET CLR Exceptions"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_INTEROP, L".NET CLR Interop"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_JIT, L".NET CLR Jit"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_LOADING, L".NET CLR Loading"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, L".NET CLR LocksAndThreads"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_MEMORY, L".NET CLR Memory"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_REMOTING, L".NET CLR Remoting"); + PhAddListViewGroup(ListViewHandle, DOTNET_CATEGORY_SECURITY, L".NET CLR Security"); + + // This counter displays the total number of exceptions thrown since the start of the application. + // These include both .NET exceptions and unmanaged exceptions that get converted into .NET exceptions e.g. null pointer reference exception in unmanaged code + // would get re-thrown in managed code as a .NET System.NullReferenceException; this counter includes both handled and unhandled exceptions.Exceptions + // that are re-thrown would get counted again. Exceptions should only occur in rare situations and not in the normal control flow of the program. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_EXCEPTIONS, DOTNET_INDEX_EXCEPTIONS_THROWNCOUNT, L"# of Exceptions Thrown", UlongToPtr(DOTNET_INDEX_EXCEPTIONS_THROWNCOUNT)); + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_EXCEPTIONS, DOTNET_INDEX_EXCEPTIONS_FILTERSCOUNT, L"# of Filters Executed", UlongToPtr(DOTNET_INDEX_EXCEPTIONS_FILTERSCOUNT)); + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_EXCEPTIONS, DOTNET_INDEX_EXCEPTIONS_FINALLYCOUNT, L"# of Finallys Executed", UlongToPtr(DOTNET_INDEX_EXCEPTIONS_FINALLYCOUNT)); + + // Reserved for future use. + //PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_EXCEPTIONS, , L"Delta from throw to catch site on stack", NULL); + + // # of Exceps Thrown / sec + // This counter displays the number of exceptions thrown per second. + // These include both .NET exceptions and unmanaged exceptions that get converted into .NET exceptions e.g.null pointer reference exception in unmanaged code would get + // re-thrown in managed code as a .NET System.NullReferenceException; this counter includes both handled and unhandled exceptions. + // Exceptions should only occur in rare situations and not in the normal control flow of the program; + // this counter was designed as an indicator of potential performance problems due to large (> 100s) rate of exceptions thrown. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // # of Filters / sec + // This counter displays the number of.NET exception filters executed per second.An exception filter evaluates whether an exception should be handled or not. + // This counter tracks the rate of exception filters evaluated; irrespective of whether the exception was handled or not. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // # of Finallys / sec + // This counter displays the number of finally blocks executed per second. + // A finally block is guaranteed to be executed regardless of how the try block was exited. + // Only the finally blocks that are executed for an exception are counted; finally blocks on normal code paths are not counted by this counter. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Throw To Catch Depth / sec + // This counter displays the number of stack frames traversed from the frame that threw the .NET exception to the frame that handled the exception per second. + // This counter resets to 0 when an exception handler is entered; so nested exceptions would show the handler to handler stack depth. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // This counter displays the current number of Com-Callable-Wrappers (CCWs). + // A CCW is a proxy for the .NET managed object being referenced from unmanaged COM client(s). + // This counter was designed to indicate the number of managed objects being referenced by unmanaged COM code. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_INTEROP, DOTNET_INDEX_INTEROP_CCWCOUNT, L"# of CCWs", UlongToPtr(DOTNET_INDEX_INTEROP_CCWCOUNT)); + + // This counter displays the current number of stubs created by the CLR. + // Stubs are responsible for marshalling arguments and return values from managed to unmanaged code and vice versa; during a COM Interop call or PInvoke call. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_INTEROP, DOTNET_INDEX_INTEROP_STUBCOUNT, L"# of Stubs", UlongToPtr(DOTNET_INDEX_INTEROP_STUBCOUNT)); + + // This counter displays the total number of times arguments and return values have been marshaled from managed to unmanaged code + // and vice versa since the start of the application. This counter is not incremented if the stubs are inlined. + // (Stubs are responsible for marshalling arguments and return values). Stubs usually get inlined if the marshalling overhead is small. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_INTEROP, DOTNET_INDEX_INTEROP_MARSHALCOUNT, L"# of Marshalling", UlongToPtr(DOTNET_INDEX_INTEROP_MARSHALCOUNT)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_INTEROP, DOTNET_INDEX_INTEROP_TLBIMPORTPERSEC, L"# of TLB imports / sec", UlongToPtr(DOTNET_INDEX_INTEROP_TLBIMPORTPERSEC)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_INTEROP, DOTNET_INDEX_INTEROP_TLBEXPORTPERSEC, L"# of TLB exports / sec", UlongToPtr(DOTNET_INDEX_INTEROP_TLBEXPORTPERSEC)); + + // This counter displays the total number of methods compiled Just-In-Time (JIT) by the CLR JIT compiler since the start of the application. + // This counter does not include the pre-jitted methods. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_JIT, DOTNET_INDEX_JIT_ILMETHODSJITTED, L"# of Methods Jitted", UlongToPtr(DOTNET_INDEX_JIT_ILMETHODSJITTED)); + + // This counter displays the total IL bytes jitted since the start of the application. + // This counter is exactly equivalent to the "Total # of IL Bytes Jitted" counter. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_JIT, DOTNET_INDEX_JIT_ILBYTESJITTED, L"# of IL Bytes Jitted", UlongToPtr(DOTNET_INDEX_JIT_ILBYTESJITTED)); + + // This counter displays the total IL bytes jitted since the start of the application. + // This counter is exactly equivalent to the "# of IL Bytes Jitted" counter. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_JIT, DOTNET_INDEX_JIT_ILTOTALBYTESJITTED, L"Total # of IL Bytes Jitted", UlongToPtr(DOTNET_INDEX_JIT_ILTOTALBYTESJITTED)); + + // This counter displays the peak number of methods the JIT compiler has failed to JIT since the start of the application. + // This failure can occur if the IL cannot be verified or if there was an internal error in the JIT compiler. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_JIT, DOTNET_INDEX_JIT_FAILURES, L"Jit Failures", UlongToPtr(DOTNET_INDEX_JIT_FAILURES)); + + // This counter displays the percentage of elapsed time spent in JIT compilation since the last JIT compilation phase. + // This counter is updated at the end of every JIT compilation phase. A JIT compilation phase is the phase when a method and its dependencies are being compiled. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_JIT, DOTNET_INDEX_JIT_TIME, L"% Time in Jit", UlongToPtr(DOTNET_INDEX_JIT_TIME)); + + // IL Bytes Jitted / sec + // This counter displays the rate at which IL bytes are jitted per second. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // This counter displays the current number of classes loaded in all Assemblies. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_CURRENTLOADED, L"Current Classes Loaded", UlongToPtr(DOTNET_INDEX_LOADING_CURRENTLOADED)); + + // This counter displays the cumulative number of classes loaded in all Assemblies since the start of this application. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_TOTALLOADED, L"Total Classes Loaded", UlongToPtr(DOTNET_INDEX_LOADING_TOTALLOADED)); + + // This counter displays the current number of AppDomains loaded in this application. + // AppDomains (application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications running in the same process. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_CURRENTAPPDOMAINS, L"Current Appdomains", UlongToPtr(DOTNET_INDEX_LOADING_CURRENTAPPDOMAINS)); + + // This counter displays the peak number of AppDomains loaded since the start of this application. + // AppDomains (application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications running in the same process. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_TOTALAPPDOMAINS, L"Total Appdomains", UlongToPtr(DOTNET_INDEX_LOADING_TOTALAPPDOMAINS)); + + // This counter displays the current number of Assemblies loaded across all AppDomains in this application. + // If the Assembly is loaded as domain - neutral from multiple AppDomains then this counter is incremented once only. + // Assemblies can be loaded as domain - neutral when their code can be shared by all AppDomains or they can be loaded as domain - specific when their code is private to the AppDomain. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_CURRENTASSEMBLIES, L"Current Assemblies", UlongToPtr(DOTNET_INDEX_LOADING_CURRENTASSEMBLIES)); + + // This counter displays the total number of Assemblies loaded since the start of this application. + // If the Assembly is loaded as domain - neutral from multiple AppDomains then this counter is incremented once only. + // Assemblies can be loaded as domain - neutral when their code can be shared by all AppDomains or they can be loaded as domain - specific when their code is private to the AppDomain. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_TOTALASSEMBLIES, L"Total Assemblies", UlongToPtr(DOTNET_INDEX_LOADING_TOTALASSEMBLIES)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_ASSEMBLYSEARCHLENGTH, L"Assembly Search Length", UlongToPtr(DOTNET_INDEX_LOADING_ASSEMBLYSEARCHLENGTH)); + + // This counter displays the peak number of classes that have failed to load since the start of the application. + // These load failures could be due to many reasons like inadequate security or illegal format.Full details can be found in the profiling services help. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_TOTALLOADFAILURES, L"Total # of Load Failures", UlongToPtr(DOTNET_INDEX_LOADING_TOTALLOADFAILURES)); + + // This counter displays the current size(in bytes) of the memory committed by the class loader across all AppDomains. + // (Committed memory is the physical memory for which space has been reserved on the disk paging file.) + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_BYTESINLOADERHEAP, L"Bytes in Loader Heap", UlongToPtr(DOTNET_INDEX_LOADING_BYTESINLOADERHEAP)); + + // This counter displays the total number of AppDomains unloaded since the start of the application. + //If an AppDomain is loaded and unloaded multiple times this counter would count each of those unloads as separate. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, DOTNET_INDEX_LOADING_TOTALAPPDOMAINSUNLOADED, L"Total Appdomains Unloaded", UlongToPtr(DOTNET_INDEX_LOADING_TOTALAPPDOMAINSUNLOADED)); + + // Reserved for future use. + //PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOADING, , L"% Time Loading", UlongToPtr()); + + // Rate of Load Failures + // This counter displays the number of classes that failed to load per second. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // These load failures could be due to many reasons like inadequate security or illegal format. Full details can be found in the profiling services help. + // TODO: We need to count the delta. + + // Rate of appdomains unloaded + // This counter displays the number of AppDomains unloaded per second. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Rate of Classes Loaded + // This counter displays the number of classes loaded per second in all Assemblies. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Rate of appdomains + // This counter displays the number of AppDomains loaded per second. + // AppDomains(application domains) provide a secure and versatile unit of processing that the CLR can use to provide isolation between applications + // running in the same process. This counter is not an average over time; it displays the difference between the values observed in the last two samples + // divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Rate of Assemblies + // This counter displays the number of Assemblies loaded across all AppDomains per second. + // If the Assembly is loaded as domain - neutral from multiple AppDomains then this counter is incremented once only.Assemblies can be loaded as + // domain - neutral when their code can be shared by all AppDomains or they can be loaded as domain - specific when their code is private to the AppDomain. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // This counter displays the total number of times threads in the CLR have attempted to acquire a managed lock unsuccessfully. + // Managed locks can be acquired in many ways; by the "lock" statement in C# or by calling System.Monitor.Enter or by using MethodImplOptions.Synchronized custom attribute. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_TOTALLOCKS, L"Total # of Contentions", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_TOTALLOCKS)); + + // This counter displays the total number of threads currently waiting to acquire some managed lock in the application. + // This counter is not an average over time; it displays the last observed value. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_TOTALQUEUELENGTH, L"Current Queue Length", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_TOTALQUEUELENGTH)); + + // This counter displays the total number of threads that waited to acquire some managed lock since the start of the application. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_QUEUELENGTHPEAK, L"Queue Length Peak", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_QUEUELENGTHPEAK)); + + // This counter displays the number of current.NET thread objects in the application. + // A.NET thread object is created either by new System.Threading.Thread or when an unmanaged thread enters the managed environment. + // This counters maintains the count of both running and stopped threads. This counter is not an average over time; it just displays the last observed value. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_CURRENTLOGICAL, L"# of Current Logical Threads", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_CURRENTLOGICAL)); + + // This counter displays the number of native OS threads created and owned by the CLR to act as underlying threads for .NET thread objects. + // This counters value does not include the threads used by the CLR in its internal operations; it is a subset of the threads in the OS process. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_CURRENTPHYSICAL, L"# of Current Physical Threads", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_CURRENTPHYSICAL)); + + // This counter displays the number of threads that are currently recognized by the CLR; they have a corresponding .NET thread object associated with them. + // These threads are not created by the CLR; they are created outside the CLR but have since run inside the CLR at least once. + // Only unique threads are tracked; threads with same thread ID re-entering the CLR or recreated after thread exit are not counted twice. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_CURRENTRECOGNIZED, L"# of Current Recognized Threads", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_CURRENTRECOGNIZED)); + + // This counter displays the total number of threads that have been recognized by the CLR since the start of this application; + // these threads have a corresponding .NET thread object associated with them. + // These threads are not created by the CLR; they are created outside the CLR but have since run inside the CLR at least once. + // Only unique threads are tracked; threads with same thread ID re-entering the CLR or recreated after thread exit are not counted twice. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_LOCKSANDTHREADS, DOTNET_INDEX_LOCKSANDTHREADS_TOTALRECOGNIZED, L"# of Total Recognized Threads", UlongToPtr(DOTNET_INDEX_LOCKSANDTHREADS_TOTALRECOGNIZED)); + + // Contention Rate / sec + // Rate at which threads in the runtime attempt to acquire a managed lock unsuccessfully. + // Managed locks can be acquired in many ways; by the "lock" statement in C# or by calling System.Monitor.Enter or by using MethodImplOptions.Synchronized custom attribute. + // TODO: We need to count the delta. + + // Queue Length / sec + // This counter displays the number of threads per second waiting to acquire some lock in the application. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // rate of recognized threads / sec + // This counter displays the number of threads per second that have been recognized by the CLR; these threads have a corresponding .NET thread object associated with them. + // These threads are not created by the CLR; they are created outside the CLR but have since run inside the CLR at least once. + // Only unique threads are tracked; threads with same thread ID re-entering the CLR or recreated after thread exit are not counted twice. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // This counter displays the number of times the generation 0 objects (youngest; most recently allocated) are garbage collected (Gen 0 GC) since the start of the application. + // Gen 0 GC occurs when the available memory in generation 0 is not sufficient to satisfy an allocation request. + // This counter is incremented at the end of a Gen 0 GC. Higher generation GCs include all lower generation GCs. + // This counter is explicitly incremented when a higher generation (Gen 1 or Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. + // This counter displays the last observed value. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GENZEROCOLLECTIONS, L"# Gen 0 Collections", UlongToPtr(DOTNET_INDEX_MEMORY_GENZEROCOLLECTIONS)); + + // This counter displays the number of times the generation 1 objects are garbage collected since the start of the application. + // The counter is incremented at the end of a Gen 1 GC. Higher generation GCs include all lower generation GCs. + // This counter is explicitly incremented when a higher generation (Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. + // This counter displays the last observed value. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GENONECOLLECTIONS, L"# Gen 1 Collections", UlongToPtr(DOTNET_INDEX_MEMORY_GENONECOLLECTIONS)); + + // This counter displays the number of times the generation 2 objects(older) are garbage collected since the start of the application. + // The counter is incremented at the end of a Gen 2 GC(also called full GC)._Global_ counter value is not accurate and should be ignored. + // This counter displays the last observed value. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GENTWOCOLLECTIONS, L"# Gen 2 Collections", UlongToPtr(DOTNET_INDEX_MEMORY_GENTWOCOLLECTIONS)); + + // This counter displays the bytes of memory that survive garbage collection(GC) and are promoted from generation 0 to generation 1; + // objects that are promoted just because they are waiting to be finalized are not included in this counter. + // This counter displays the value observed at the end of the last GC; its not a cumulative counter. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_PROMOTEDFROMGENZERO, L"Promoted Memory from Gen 0", UlongToPtr(DOTNET_INDEX_MEMORY_PROMOTEDFROMGENZERO)); + + // This counter displays the bytes of memory that survive garbage collection(GC) and are promoted from generation 1 to generation 2; + // objects that are promoted just because they are waiting to be finalized are not included in this counter. + // This counter displays the value observed at the end of the last GC; its not a cumulative counter. + // This counter is reset to 0 if the last GC was a Gen 0 GC only. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_PROMOTEDFROMGENONE, L"Promoted Memory from Gen 1", UlongToPtr(DOTNET_INDEX_MEMORY_PROMOTEDFROMGENONE)); + + // This counter displays the bytes of memory that are promoted from generation 0 to generation 1 just because they are waiting to be finalized. + // This counter displays the value observed at the end of the last GC; its not a cumulative counter. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_PROMOTEDFINALFROMGENZERO, L"Promoted Finalization-Memory from Gen 0", UlongToPtr(DOTNET_INDEX_MEMORY_PROMOTEDFINALFROMGENZERO)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_PROCESSID, L"Process ID", UlongToPtr(DOTNET_INDEX_MEMORY_PROCESSID)); + + // This counter displays the maximum bytes that can be allocated in generation 0 (Gen 0); + // its does not indicate the current number of bytes allocated in Gen 0. + // A Gen 0 GC is triggered when the allocations since the last GC exceed this size. + // The Gen 0 size is tuned by the Garbage Collector and can change during the execution of the application. + // At the end of a Gen 0 collection the size of the Gen 0 heap is infact 0 bytes; this counter displays the size(in bytes) of allocations that would trigger + // the next Gen 0 GC.This counter is updated at the end of a GC; its not updated on every allocation. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GENZEROHEAPSIZE, L"Gen 0 Heap Size", UlongToPtr(DOTNET_INDEX_MEMORY_GENZEROHEAPSIZE)); + + // This counter displays the current number of bytes in generation 1 (Gen 1); + // this counter does not display the maximum size of Gen 1. Objects are not directly allocated in this generation; + // they are promoted from previous Gen 0 GCs.This counter is updated at the end of a GC; its not updated on every allocation. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GENONEHEAPSIZE, L"Gen 1 Heap Size", UlongToPtr(DOTNET_INDEX_MEMORY_GENONEHEAPSIZE)); + + // This counter displays the current number of bytes in generation 2 (Gen 2). + // Objects are not directly allocated in this generation; they are promoted from Gen 1 during previous Gen 1 GCs. + // This counter is updated at the end of a GC; its not updated on every allocation. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GENTWOHEAPSIZE, L"Gen 2 Heap Size", UlongToPtr(DOTNET_INDEX_MEMORY_GENTWOHEAPSIZE)); + + // This counter displays the current size of the Large Object Heap in bytes. + // Objects greater than 20 KBytes are treated as large objects by the Garbage Collector and are directly allocated in a special heap; they are not promoted through the generations. + // This counter is updated at the end of a GC; its not updated on every allocation. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_LOHSIZE, L"Large Object Heap Size", UlongToPtr(DOTNET_INDEX_MEMORY_LOHSIZE)); + + // This counter displays the number of garbage collected objects that survive a collection because they are waiting to be finalized. + // If these objects hold references to other objects then those objects also survive but are not counted by this counter; the "Promoted Finalization-Memory from Gen 0" + // and "Promoted Finalization-Memory from Gen 1" counters represent all the memory that survived due to finalization. + // This counter is not a cumulative counter; its updated at the end of every GC with count of the survivors during that particular GC only. + // This counter was designed to indicate the extra overhead that the application might incur because of finalization. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_FINALSURVIVORS, L"Finalization Survivors", UlongToPtr(DOTNET_INDEX_MEMORY_FINALSURVIVORS)); + + // This counter displays the current number of GC Handles in-use. + // GCHandles are handles to resources external to the CLR and the managed environment. + // Handles occupy small amounts of memory in the GCHeap but potentially expensive unmanaged resources. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_GCHANDLES, L"# GC Handles", UlongToPtr(DOTNET_INDEX_MEMORY_GCHANDLES)); + + // This counter displays the peak number of times a garbage collection was performed because of an explicit call to GC.Collect. + // Its a good practice to let the GC tune the frequency of its collections. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_INDUCEDGC, L"# Induced GC", UlongToPtr(DOTNET_INDEX_MEMORY_INDUCEDGC)); + + // % Time in GC is the percentage of elapsed time that was spent in performing a garbage collection(GC) since the last GC cycle. + // This counter is usually an indicator of the work done by the Garbage Collector on behalf of the application to collect and compact memory. + // This counter is updated only at the end of every GC and the counter value reflects the last observed value; its not an average. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TIMEINGC, L"% Time in GC", UlongToPtr(DOTNET_INDEX_MEMORY_TIMEINGC)); + + // This counter is the sum of four other counters; Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size. + // This counter indicates the current memory allocated in bytes on the GC Heaps. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_BYTESINALLHEAPS, L"# Bytes in all Heaps", UlongToPtr(DOTNET_INDEX_MEMORY_BYTESINALLHEAPS)); + + // This counter displays the amount of virtual memory(in bytes) currently committed by the Garbage Collector. + // (Committed memory is the physical memory for which space has been reserved on the disk paging file). + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TOTALCOMMITTED, L"# Total Committed Bytes", UlongToPtr(DOTNET_INDEX_MEMORY_TOTALCOMMITTED)); + + // This counter displays the amount of virtual memory(in bytes) currently reserved by the Garbage Collector. + // (Reserved memory is the virtual memory space reserved for the application but no disk or main memory pages have been used.) + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TOTALRESERVED, L"# Total Reserved Bytes", UlongToPtr(DOTNET_INDEX_MEMORY_TOTALRESERVED)); + + // This counter displays the number of pinned objects encountered in the last GC. + // This counter tracks the pinned objects only in the heaps that were garbage collected e.g. A Gen 0 GC would cause enumeration of pinned objects in the generation 0 heap only. + // A pinned object is one that the Garbage Collector cannot move in memory. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TOTALPINNED, L"# of Pinned Objects", UlongToPtr(DOTNET_INDEX_MEMORY_TOTALPINNED)); + + // This counter displays the current number of sync blocks in use. Sync blocks are per-object data structures allocated for storing synchronization information. + // Sync blocks hold weak references to managed objects and need to be scanned by the Garbage Collector. + // Sync blocks are not limited to storing synchronization information and can also store COM interop metadata. + // This counter was designed to indicate performance problems with heavy use of synchronization primitives. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TOTALSINKS, L"# of Sink Blocks in use", UlongToPtr(DOTNET_INDEX_MEMORY_TOTALSINKS)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TOTALBYTESSINCESTART, L"Total Bytes Allocated (since start)", UlongToPtr(DOTNET_INDEX_MEMORY_TOTALBYTESSINCESTART)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_MEMORY, DOTNET_INDEX_MEMORY_TOTALLOHBYTESSINCESTART, L"Total Bytes Allocated for Large Objects (since start)", UlongToPtr(DOTNET_INDEX_MEMORY_TOTALLOHBYTESSINCESTART)); + + // Gen 0 Promoted Bytes / Sec + // This counter displays the bytes per second that are promoted from generation 0 (youngest)to generation 1; + // objects that are promoted just because they are waiting to be finalized are not included in this counter. + // Memory is promoted when it survives a garbage collection.This counter was designed as an indicator of relatively long-lived objects being created per sec. + // This counter displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Gen 1 Promoted Bytes / Sec + // This counter displays the bytes per second that are promoted from generation 1 to generation 2 (oldest); + // objects that are promoted just because they are waiting to be finalized are not included in this counter. + // Memory is promoted when it survives a garbage collection. + // Nothing is promoted from generation 2 since it is the oldest. + // This counter was designed as an indicator of very long-lived objects being created per sec. + // This counter displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Promoted Finalization - Memory from Gen 1 + // This counter displays the bytes of memory that are promoted from generation 1 to generation 2 just because they are waiting to be finalized. + // This counter displays the value observed at the end of the last GC; its not a cumulative counter.This counter is reset to 0 if the last GC was a Gen 0 GC only. + // TODO: We need to count the delta. + + // Allocated Bytes / sec + // This counter displays the rate of bytes per second allocated on the GC Heap. + // This counter is updated at the end of every GC; not at each allocation. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // This counter displays the total number of remote procedure calls invoked since the start of this application. + // A remote procedure call is a call on any object outside the callers AppDomain. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_REMOTING, DOTNET_INDEX_REMOTING_TOTALREMOTECALLS, L"Total Remote Calls", UlongToPtr(DOTNET_INDEX_REMOTING_TOTALREMOTECALLS)); + + // This counter displays the total number of remoting channels registered across all AppDomains since the start of the application. + // Channels are used to transport messages to and from remote objects. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_REMOTING, DOTNET_INDEX_REMOTING_CHANNELS, L"Channels", UlongToPtr(DOTNET_INDEX_REMOTING_CHANNELS)); + + // This counter displays the total number of remoting proxy objects created in this process since the start of the process. + // Proxy object acts as a representative of the remote objects and ensures that all calls made on the proxy are forwarded to the correct remote object instance. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_REMOTING, DOTNET_INDEX_REMOTING_CONTEXTPROXIES, L"Context Proxies", UlongToPtr(DOTNET_INDEX_REMOTING_CONTEXTPROXIES)); + + // This counter displays the current number of context-bound classes loaded. + // Classes that can be bound to a context are called context-bound classes; context-bound classes are marked with Context Attributes + // which provide usage rules for synchronization; thread affinity; transactions etc. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_REMOTING, DOTNET_INDEX_REMOTING_CONTEXTCLASSESLOADED, L"Context-Bound Classes Loaded", UlongToPtr(DOTNET_INDEX_REMOTING_CONTEXTCLASSESLOADED)); + + // This counter displays the current number of remoting contexts in the application. + // A context is a boundary containing a collection of objects with the same usage rules like synchronization; thread affinity; transactions etc. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_REMOTING, DOTNET_INDEX_REMOTING_CONTEXTS, L"Contexts", UlongToPtr(DOTNET_INDEX_REMOTING_CONTEXTS)); + + // Reserved for future use. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_REMOTING, DOTNET_INDEX_REMOTING_CONTEXTSALLOCATED, L"# of context bound objects allocated", UlongToPtr(DOTNET_INDEX_REMOTING_CONTEXTSALLOCATED)); + + // Remote Calls / sec + // This counter displays the number of remote procedure calls invoked per second. + // A remote procedure call is a call on any object outside the callers AppDomain. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // Context - Bound Objects Alloc / sec + // This counter displays the number of context - bound objects allocated per second. + // Instances of classes that can be bound to a context are called context - bound objects; context - bound classes are marked with Context Attributes + // which provide usage rules for synchronization; thread affinity; transactions etc. + // This counter is not an average over time; it displays the difference between the values observed in the last two samples divided by the duration of the sample interval. + // TODO: We need to count the delta. + + // This counter displays the total number of runtime Code Access Security(CAS) checks performed since the start of the application. + // Runtime CAS checks are performed when a caller makes a call to a callee demanding a particular permission; + // the runtime check is made on every call by the caller; the check is done by examining the current thread stack of the caller. + // This counter used together with "Stack Walk Depth" is indicative of performance penalty for security checks. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_SECURITY, DOTNET_INDEX_SECURITY_TOTALRUNTIMECHECKS, L"Total Runtime Checks", UlongToPtr(DOTNET_INDEX_SECURITY_TOTALRUNTIMECHECKS)); + + // This counter displays the total number of linktime Code Access Security(CAS) checks since the start of the application. + // Linktime CAS checks are performed when a caller makes a call to a callee demanding a particular permission at JIT compile time; linktime check is performed once per caller. + // This count is not indicative of serious performance issues; its indicative of the security system activity. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_SECURITY, DOTNET_INDEX_SECURITY_LINKTIMECHECKS, L"# Link Time Checks", UlongToPtr(DOTNET_INDEX_SECURITY_LINKTIMECHECKS)); + + // This counter displays the percentage of elapsed time spent in performing runtime Code Access Security(CAS) checks since the last such check. + // CAS allows code to be trusted to varying degrees and enforces these varying levels of trust depending on code identity. + // This counter is updated at the end of a runtime security check; it represents the last observed value; its not an average. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_SECURITY, DOTNET_INDEX_SECURITY_TIMEINRTCHECKS, L"% Time in RT checks", UlongToPtr(DOTNET_INDEX_SECURITY_TIMEINRTCHECKS)); + + // This counter displays the depth of the stack during that last runtime Code Access Security check. + // Runtime Code Access Security check is performed by crawling the stack. + // This counter is not an average; it just displays the last observed value. + PhAddListViewGroupItem(ListViewHandle, DOTNET_CATEGORY_SECURITY, DOTNET_INDEX_SECURITY_STACKWALKDEPTH, L"Stack Walk Depth", UlongToPtr(DOTNET_INDEX_SECURITY_STACKWALKDEPTH)); + + // % Time Sig.Authenticating + // Reserved for future use. +} + +/*VOID DotNetPerfAddProcessAppDomains( + _In_ HWND hwndDlg, + _In_ PPERFPAGE_CONTEXT Context + ) +{ + ExtendedListView_SetRedraw(Context->AppDomainsListViewHandle, FALSE); + ListView_DeleteAllItems(Context->AppDomainsListViewHandle); + + if (Context->ClrV4) + { + PPH_LIST processAppDomains = QueryDotNetAppDomainsForPid_V4( + Context->IsWow64, + Context->ProcessHandle, + Context->ProcessItem->ProcessId + ); + + if (processAppDomains) + { + for (ULONG i = 0; i < processAppDomains->Count; i++) + { + PhAddListViewItem(Context->AppDomainsListViewHandle, MAXINT, processAppDomains->Items[i], NULL); + PhFree(processAppDomains->Items[i]); + } + + PhDereferenceObject(processAppDomains); + } + } + else + { + PPH_LIST processAppDomains = QueryDotNetAppDomainsForPid_V2( + Context->IsWow64, + Context->ProcessHandle, + Context->ProcessItem->ProcessId + ); + + if (processAppDomains) + { + for (ULONG i = 0; i < processAppDomains->Count; i++) + { + PhAddListViewItem(Context->AppDomainsListViewHandle, MAXINT, processAppDomains->Items[i], NULL); + PhFree(processAppDomains->Items[i]); + } + + PhDereferenceObject(processAppDomains); + } + } + + ExtendedListView_SetRedraw(Context->AppDomainsListViewHandle, TRUE); +}*/ + +VOID DotNetPerfUpdateCounterData( + _In_ HWND hwndDlg, + _In_ PPERFPAGE_CONTEXT Context + ) +{ + PVOID perfStatBlock; + + if (Context->ClrV4) + { + perfStatBlock = GetPerfIpcBlock_V4( + Context->IsWow64, + Context->BlockTableAddress + ); + } + else + { + perfStatBlock = GetPerfIpcBlock_V2( + Context->IsWow64, + Context->BlockTableAddress + ); + } + + if (!perfStatBlock) + return; + + if (Context->IsWow64) + { + PerfCounterIPCControlBlock_Wow64* perfBlock = perfStatBlock; + Perf_GC_Wow64 dotNetPerfGC_Wow64 = perfBlock->GC; + Perf_Loading_Wow64 dotNetPerfLoading_Wow64 = perfBlock->Loading; + Perf_Security_Wow64 dotNetPerfSecurity_Wow64 = perfBlock->Security; + + // Thunk the Wow64 structures into their 64bit versions (or 32bit version on x86). + + Context->DotNetPerfGC.cGenCollections[0] = dotNetPerfGC_Wow64.cGenCollections[0]; + Context->DotNetPerfGC.cGenCollections[1] = dotNetPerfGC_Wow64.cGenCollections[1]; + Context->DotNetPerfGC.cGenCollections[2] = dotNetPerfGC_Wow64.cGenCollections[2]; + Context->DotNetPerfGC.cbPromotedMem[0] = dotNetPerfGC_Wow64.cbPromotedMem[0]; + Context->DotNetPerfGC.cbPromotedMem[1] = dotNetPerfGC_Wow64.cbPromotedMem[1]; + Context->DotNetPerfGC.cbPromotedFinalizationMem = dotNetPerfGC_Wow64.cbPromotedFinalizationMem; + Context->DotNetPerfGC.cProcessID = dotNetPerfGC_Wow64.cProcessID; + Context->DotNetPerfGC.cGenHeapSize[0] = dotNetPerfGC_Wow64.cGenHeapSize[0]; + Context->DotNetPerfGC.cGenHeapSize[1] = dotNetPerfGC_Wow64.cGenHeapSize[1]; + Context->DotNetPerfGC.cGenHeapSize[2] = dotNetPerfGC_Wow64.cGenHeapSize[2]; + Context->DotNetPerfGC.cTotalCommittedBytes = dotNetPerfGC_Wow64.cTotalCommittedBytes; + Context->DotNetPerfGC.cTotalReservedBytes = dotNetPerfGC_Wow64.cTotalReservedBytes; + Context->DotNetPerfGC.cLrgObjSize = dotNetPerfGC_Wow64.cLrgObjSize; + Context->DotNetPerfGC.cSurviveFinalize = dotNetPerfGC_Wow64.cSurviveFinalize; + Context->DotNetPerfGC.cHandles = dotNetPerfGC_Wow64.cHandles; + Context->DotNetPerfGC.cbAlloc = dotNetPerfGC_Wow64.cbAlloc; + Context->DotNetPerfGC.cbLargeAlloc = dotNetPerfGC_Wow64.cbLargeAlloc; + Context->DotNetPerfGC.cInducedGCs = dotNetPerfGC_Wow64.cInducedGCs; + Context->DotNetPerfGC.timeInGC = dotNetPerfGC_Wow64.timeInGC; + Context->DotNetPerfGC.timeInGCBase = dotNetPerfGC_Wow64.timeInGCBase; + Context->DotNetPerfGC.cPinnedObj = dotNetPerfGC_Wow64.cPinnedObj; + Context->DotNetPerfGC.cSinkBlocks = dotNetPerfGC_Wow64.cSinkBlocks; + + Context->DotNetPerfContext = perfBlock->Context; + Context->DotNetPerfInterop = perfBlock->Interop; + + Context->DotNetPerfLoading.cClassesLoaded.Current = dotNetPerfLoading_Wow64.cClassesLoaded.Current; + Context->DotNetPerfLoading.cClassesLoaded.Total = dotNetPerfLoading_Wow64.cClassesLoaded.Total; + Context->DotNetPerfLoading.cAppDomains.Current = dotNetPerfLoading_Wow64.cAppDomains.Current; + Context->DotNetPerfLoading.cAppDomains.Total = dotNetPerfLoading_Wow64.cAppDomains.Total; + Context->DotNetPerfLoading.cAssemblies.Current = dotNetPerfLoading_Wow64.cAssemblies.Current; + Context->DotNetPerfLoading.cAssemblies.Total = dotNetPerfLoading_Wow64.cAssemblies.Total; + Context->DotNetPerfLoading.timeLoading = dotNetPerfLoading_Wow64.timeLoading; + Context->DotNetPerfLoading.cAsmSearchLen = dotNetPerfLoading_Wow64.cAsmSearchLen; + Context->DotNetPerfLoading.cLoadFailures.Total = dotNetPerfLoading_Wow64.cLoadFailures.Total; + Context->DotNetPerfLoading.cbLoaderHeapSize = dotNetPerfLoading_Wow64.cbLoaderHeapSize; + Context->DotNetPerfLoading.cAppDomainsUnloaded = dotNetPerfLoading_Wow64.cAppDomainsUnloaded; + + Context->DotNetPerfExceptions = perfBlock->Exceptions; + Context->DotNetPerfLocksAndThreads = perfBlock->LocksAndThreads; + Context->DotNetPerfJit = perfBlock->Jit; + + Context->DotNetPerfSecurity.cTotalRTChecks = dotNetPerfSecurity_Wow64.cTotalRTChecks; + Context->DotNetPerfSecurity.timeAuthorize = dotNetPerfSecurity_Wow64.timeAuthorize; + Context->DotNetPerfSecurity.cLinkChecks = dotNetPerfSecurity_Wow64.cLinkChecks; + Context->DotNetPerfSecurity.timeRTchecks = dotNetPerfSecurity_Wow64.timeRTchecks; + Context->DotNetPerfSecurity.timeRTchecksBase = dotNetPerfSecurity_Wow64.timeRTchecksBase; + Context->DotNetPerfSecurity.stackWalkDepth = dotNetPerfSecurity_Wow64.stackWalkDepth; + } + else + { + PerfCounterIPCControlBlock* perfBlock = perfStatBlock; + + Context->DotNetPerfGC = perfBlock->GC; + Context->DotNetPerfContext = perfBlock->Context; + Context->DotNetPerfInterop = perfBlock->Interop; + Context->DotNetPerfLoading = perfBlock->Loading; + Context->DotNetPerfExceptions = perfBlock->Exceptions; + Context->DotNetPerfLocksAndThreads = perfBlock->LocksAndThreads; + Context->DotNetPerfJit = perfBlock->Jit; + Context->DotNetPerfSecurity = perfBlock->Security; + } + + // The ListView doesn't send LVN_GETDISPINFO (or redraw properly) when not focused so we'll force a redraw. (dmex) + ListView_RedrawItems(Context->CountersListViewHandle, 0, DOTNET_INDEX_MAXIMUM); + UpdateWindow(Context->CountersListViewHandle); +} + +INT_PTR CALLBACK DotNetPerfPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PPERFPAGE_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + context = propPageContext->Context = PhAllocateZero(sizeof(PERFPAGE_CONTEXT)); + context->WindowHandle = hwndDlg; + context->ProcessItem = processItem; + context->CountersListViewHandle = GetDlgItem(hwndDlg, IDC_COUNTERS); + context->Enabled = TRUE; + + PhSetListViewStyle(context->CountersListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->CountersListViewHandle, L"explorer"); + PhAddListViewColumn(context->CountersListViewHandle, 0, 0, 0, LVCFMT_LEFT, 250, L"Counter"); + PhAddListViewColumn(context->CountersListViewHandle, 1, 1, 1, LVCFMT_RIGHT, 140, L"Value"); + PhSetExtendedListView(context->CountersListViewHandle); + + DotNetPerfAddListViewGroups(context->CountersListViewHandle); + PhLoadListViewColumnsFromSetting(SETTING_NAME_DOT_NET_COUNTERS_COLUMNS, context->CountersListViewHandle); + PhLoadListViewSortColumnsFromSetting(SETTING_NAME_DOT_NET_COUNTERS_SORTCOLUMN, context->CountersListViewHandle); + PhLoadListViewGroupStatesFromSetting(SETTING_NAME_DOT_NET_COUNTERS_GROUPSTATES, context->CountersListViewHandle); + +#ifdef _WIN64 + context->IsWow64 = !!context->ProcessItem->IsWow64; +#else + // HACK: Work-around for Appdomain enumeration on 32bit. + context->IsWow64 = TRUE; +#endif + + if (NT_SUCCESS(PhOpenProcess( + &context->ProcessHandle, + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE | SYNCHRONIZE, + context->ProcessItem->ProcessId + ))) + { + ULONG flags = 0; + + if (NT_SUCCESS(PhGetProcessIsDotNetEx( + context->ProcessItem->ProcessId, + context->ProcessHandle, + context->ProcessItem->IsImmersive == 1 ? 0 : PH_CLR_USE_SECTION_CHECK, + NULL, + &flags + ))) + { + if (flags & PH_CLR_VERSION_4_ABOVE) + { + context->ClrV4 = TRUE; + } + } + } + + if (context->ClrV4) + { + if (OpenDotNetPublicControlBlock_V4( + !!context->ProcessItem->IsImmersive, + context->ProcessHandle, + context->ProcessItem->ProcessId, + &context->BlockTableAddress + )) + { + context->ControlBlockValid = TRUE; + } + } + else + { + if (OpenDotNetPublicControlBlock_V2( + context->ProcessItem->ProcessId, + &context->BlockTableAddress + )) + { + context->ControlBlockValid = TRUE; + } + } + + if (context->ControlBlockValid) + { + DotNetPerfUpdateCounterData(hwndDlg, context); + } + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + DotNetPerfProcessesUpdatedCallback, + context, + &context->ProcessesUpdatedCallbackRegistration + ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhUnregisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + &context->ProcessesUpdatedCallbackRegistration + ); + + if (context->BlockTableAddress) + { + NtUnmapViewOfSection(NtCurrentProcess(), context->BlockTableAddress); + } + + if (context->ProcessHandle) + { + NtClose(context->ProcessHandle); + } + + PhSaveListViewGroupStatesToSetting(SETTING_NAME_DOT_NET_COUNTERS_GROUPSTATES, context->CountersListViewHandle); + PhSaveListViewSortColumnsToSetting(SETTING_NAME_DOT_NET_COUNTERS_SORTCOLUMN, context->CountersListViewHandle); + PhSaveListViewColumnsToSetting(SETTING_NAME_DOT_NET_COUNTERS_COLUMNS, context->CountersListViewHandle); + + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + PPH_LAYOUT_ITEM dialogItem; + + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, context->CountersListViewHandle, dialogItem, PH_ANCHOR_ALL); + PhEndPropPageLayout(hwndDlg, propPageContext); + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + PhHandleListViewNotifyBehaviors(lParam, context->CountersListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + + switch (header->code) + { + case PSN_SETACTIVE: + context->Enabled = TRUE; + break; + case PSN_KILLACTIVE: + context->Enabled = FALSE; + break; + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)context->CountersListViewHandle); + return TRUE; + case LVN_GETDISPINFO: + { + NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header; + + if (!context->ControlBlockValid) // Don't show statistics when the CLR data is invalid. (dmex) + break; + + if (dispInfo->item.iSubItem == 1) + { + if (dispInfo->item.mask & LVIF_TEXT) + { + switch (PtrToUlong((PVOID)dispInfo->item.lParam)) + { + case DOTNET_INDEX_EXCEPTIONS_THROWNCOUNT: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfExceptions.cThrown.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_EXCEPTIONS_FILTERSCOUNT: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfExceptions.cFiltersExecuted); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_EXCEPTIONS_FINALLYCOUNT: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfExceptions.cFinallysExecuted); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_INTEROP_CCWCOUNT: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfInterop.cCCW); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_INTEROP_STUBCOUNT: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfInterop.cStubs); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_INTEROP_MARSHALCOUNT: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfInterop.cMarshalling); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_INTEROP_TLBIMPORTPERSEC: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfInterop.cTLBImports); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_INTEROP_TLBEXPORTPERSEC: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfInterop.cTLBExports); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_JIT_ILMETHODSJITTED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfJit.cMethodsJitted); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_JIT_ILBYTESJITTED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfJit.cbILJitted.Current); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_JIT_ILTOTALBYTESJITTED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfJit.cbILJitted.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_JIT_FAILURES: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfJit.cJitFailures); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_JIT_TIME: + { + if (context->DotNetPerfJit.timeInJitBase != 0) + { + PH_FORMAT format[1]; + WCHAR formatBuffer[10]; + + // TODO: perlib never shows the TimeInJit value and it can sometimes show values above 100% ??? + // SeeAlso: https://github.com/dotnet/coreclr/blob/master/src/gc/gcee.cpp#L324 + PhInitFormatF(&format[0], (context->DotNetPerfJit.timeInJit << 8) * 100 / (FLOAT)(context->DotNetPerfJit.timeInJitBase << 8), 2); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + else + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, L"0.00", _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_LOADING_CURRENTLOADED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cClassesLoaded.Current); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_TOTALLOADED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cClassesLoaded.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_CURRENTAPPDOMAINS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cAppDomains.Current); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_TOTALAPPDOMAINS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cAppDomains.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_CURRENTASSEMBLIES: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cAssemblies.Current); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_TOTALASSEMBLIES: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cAssemblies.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_ASSEMBLYSEARCHLENGTH: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cAsmSearchLen); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_TOTALLOADFAILURES: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cLoadFailures.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_BYTESINLOADERHEAP: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfLoading.cbLoaderHeapSize); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOADING_TOTALAPPDOMAINSUNLOADED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLoading.cAppDomainsUnloaded.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_LOCKSANDTHREADS_TOTALLOCKS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cContention.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOCKSANDTHREADS_TOTALQUEUELENGTH: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cQueueLength.Current); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOCKSANDTHREADS_QUEUELENGTHPEAK: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cQueueLength.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOCKSANDTHREADS_CURRENTLOGICAL: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cCurrentThreadsLogical); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOCKSANDTHREADS_CURRENTPHYSICAL: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cCurrentThreadsPhysical); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOCKSANDTHREADS_CURRENTRECOGNIZED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cRecognizedThreads.Current); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_LOCKSANDTHREADS_TOTALRECOGNIZED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfLocksAndThreads.cRecognizedThreads.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_MEMORY_GENZEROCOLLECTIONS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cGenCollections[0]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_GENONECOLLECTIONS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cGenCollections[1]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_GENTWOCOLLECTIONS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cGenCollections[2]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_PROMOTEDFROMGENZERO: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cbPromotedMem[0]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_PROMOTEDFROMGENONE: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cbPromotedMem[1]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_PROMOTEDFINALFROMGENZERO: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cbPromotedFinalizationMem); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_PROCESSID: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64U(&format[0], context->DotNetPerfGC.cProcessID); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_GENZEROHEAPSIZE: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cGenHeapSize[0]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_GENONEHEAPSIZE: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cGenHeapSize[1]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_GENTWOHEAPSIZE: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cGenHeapSize[2]); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_LOHSIZE: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cLrgObjSize); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_FINALSURVIVORS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cSurviveFinalize); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_GCHANDLES: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cHandles); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_INDUCEDGC: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cInducedGCs); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TIMEINGC: + { + if (context->DotNetPerfGC.timeInGCBase != 0) + { + PH_FORMAT format[1]; + WCHAR formatBuffer[10]; + + PhInitFormatF(&format[0], (FLOAT)context->DotNetPerfGC.timeInGC * 100 / (FLOAT)context->DotNetPerfGC.timeInGCBase, 2); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + else + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, L"0.00", _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_BYTESINALLHEAPS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + // The CLR source says this value should be "Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size" + // but perflib only counts the total for Gen 1, Gen 2 and cLrgObjSize ignoring Gen 0 ??? + + PhInitFormatSize( + &format[0], + context->DotNetPerfGC.cGenHeapSize[1] + + context->DotNetPerfGC.cGenHeapSize[2] + + context->DotNetPerfGC.cLrgObjSize + ); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TOTALCOMMITTED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cTotalCommittedBytes); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TOTALRESERVED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cTotalReservedBytes); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TOTALPINNED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cPinnedObj); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TOTALSINKS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfGC.cSinkBlocks); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TOTALBYTESSINCESTART: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cbAlloc); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_MEMORY_TOTALLOHBYTESSINCESTART: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[0x100]; + + PhInitFormatSize(&format[0], context->DotNetPerfGC.cbLargeAlloc); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_REMOTING_TOTALREMOTECALLS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfContext.cRemoteCalls.Total); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_REMOTING_CHANNELS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfContext.cChannels); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_REMOTING_CONTEXTPROXIES: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfContext.cProxies); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_REMOTING_CONTEXTCLASSESLOADED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfContext.cClasses); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_REMOTING_CONTEXTS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfContext.cContexts); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_REMOTING_CONTEXTSALLOCATED: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfContext.cObjAlloc); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + + case DOTNET_INDEX_SECURITY_TOTALRUNTIMECHECKS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfSecurity.cTotalRTChecks); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_SECURITY_LINKTIMECHECKS: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfSecurity.cLinkChecks); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + case DOTNET_INDEX_SECURITY_TIMEINRTCHECKS: + { + if (context->DotNetPerfSecurity.timeRTchecksBase != 0) + { + PH_FORMAT format[1]; + WCHAR formatBuffer[10]; + + PhInitFormatF(&format[0], (FLOAT)context->DotNetPerfSecurity.timeRTchecks * 100 / (FLOAT)context->DotNetPerfSecurity.timeRTchecksBase, 2); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + else + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, L"0.00", _TRUNCATE); + } + } + break; + case DOTNET_INDEX_SECURITY_STACKWALKDEPTH: + { + PH_FORMAT format[1]; + WCHAR formatBuffer[PH_INT64_STR_LEN_1]; + + PhInitFormatI64UGroupDigits(&format[0], context->DotNetPerfSecurity.stackWalkDepth); + + if (PhFormatToBuffer(format, RTL_NUMBER_OF(format), formatBuffer, sizeof(formatBuffer), NULL)) + { + wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, formatBuffer, _TRUNCATE); + } + } + break; + } + } + } + } + break; + } + } + break; + case MSG_UPDATE: + { + if (context->Enabled && context->ControlBlockValid) + { + DotNetPerfUpdateCounterData(hwndDlg, context); + } + } + break; + case WM_SIZE: + { + //ExtendedListView_SetColumnWidth(context->AppDomainsListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->CountersListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->CountersListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_CLR_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, ID_CLR_COPY, context->CountersListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + //if (!handled && PhPluginsEnabled) + // handled = PhPluginTriggerEMenuItem(&menuInfo, item); + + if (!handled) + { + switch (item->Id) + { + case ID_CLR_COPY: + { + PhCopyListView(context->CountersListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} + +VOID AddPerfPageToPropContext( + _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT PropContext + ) +{ + PhAddProcessPropPage( + PropContext->PropContext, + PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDOTNETPERF), DotNetPerfPageDlgProc, NULL) + ); +} diff --git a/plugins/DotNetTools/resource.h b/plugins/DotNetTools/resource.h index bffca0871ee0..e10bf043fe59 100644 --- a/plugins/DotNetTools/resource.h +++ b/plugins/DotNetTools/resource.h @@ -1,26 +1,26 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by DotNetTools.rc -// -#define IDD_PROCDOTNETPERF 101 -#define ID_COPY 101 -#define IDD_PROCDOTNETASM 102 -#define IDR_ASSEMBLY_MENU 104 -#define IDC_APPDOMAINS 1001 -#define IDC_CATEGORIES 1002 -#define IDC_COUNTERS 1003 -#define IDC_LIST 1005 -#define IDC_DOTNET_PERF_SHOWBYTES 1006 -#define ID_CLR_OPENFILELOCATION 40001 -#define ID_CLR_COPY 40002 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 105 -#define _APS_NEXT_COMMAND_VALUE 40003 -#define _APS_NEXT_CONTROL_VALUE 1007 -#define _APS_NEXT_SYMED_VALUE 102 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by DotNetTools.rc +// +#define IDD_PROCDOTNETPERF 101 +#define IDD_PROCDOTNETASM 102 +#define ID_CLR_OPENFILELOCATION 103 +#define ID_CLR_COPY 104 +#define ID_CLR_INSPECT 105 +#define ID_COPY 106 +#define IDC_OPTIONS 1002 +#define IDC_COUNTERS 1003 +#define IDC_REFRESH 1003 +#define IDC_LIST 1005 +#define IDC_SEARCHEDIT 1035 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_CONTROL_VALUE 1007 +#define _APS_NEXT_SYMED_VALUE 107 +#endif +#endif diff --git a/plugins/DotNetTools/stackext.c b/plugins/DotNetTools/stackext.c index c5973a935dc0..626d29d7b2e0 100644 --- a/plugins/DotNetTools/stackext.c +++ b/plugins/DotNetTools/stackext.c @@ -1,321 +1,321 @@ -/* - * Process Hacker .NET Tools - - * thread stack extensions - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" -#include "clrsup.h" -#include "svcext.h" -#include - -typedef struct _THREAD_STACK_CONTEXT -{ - PCLR_PROCESS_SUPPORT Support; - HANDLE ProcessId; - HANDLE ThreadId; - HANDLE ThreadHandle; - - PVOID PredictedEip; - PVOID PredictedEbp; - PVOID PredictedEsp; - -#ifdef _WIN64 - BOOLEAN IsWow64; - BOOLEAN ConnectedToPhSvc; -#endif -} THREAD_STACK_CONTEXT, *PTHREAD_STACK_CONTEXT; - -static PPH_HASHTABLE ContextHashtable; -static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT; -static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT; - -PTHREAD_STACK_CONTEXT FindThreadStackContext( - _In_ PVOID UniqueKey - ) -{ - PTHREAD_STACK_CONTEXT context; - PVOID *item; - - PhAcquireQueuedLockExclusive(&ContextHashtableLock); - - item = PhFindItemSimpleHashtable(ContextHashtable, UniqueKey); - - if (item) - context = *item; - else - context = NULL; - - PhReleaseQueuedLockExclusive(&ContextHashtableLock); - - return context; -} - -VOID ProcessThreadStackControl( - _In_ PPH_PLUGIN_THREAD_STACK_CONTROL Control - ) -{ - if (PhBeginInitOnce(&ContextHashtableInitOnce)) - { - ContextHashtable = PhCreateSimpleHashtable(4); - PhEndInitOnce(&ContextHashtableInitOnce); - } - - switch (Control->Type) - { - case PluginThreadStackInitializing: - { - PTHREAD_STACK_CONTEXT context; -#if _WIN64 - HANDLE processHandle; -#endif - - context = PhAllocate(sizeof(THREAD_STACK_CONTEXT)); - memset(context, 0, sizeof(THREAD_STACK_CONTEXT)); - context->ProcessId = Control->u.Initializing.ProcessId; - context->ThreadId = Control->u.Initializing.ThreadId; - context->ThreadHandle = Control->u.Initializing.ThreadHandle; - -#if _WIN64 - if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, Control->u.Initializing.ProcessId))) - { - PhGetProcessIsWow64(processHandle, &context->IsWow64); - NtClose(processHandle); - } -#endif - - PhAcquireQueuedLockExclusive(&ContextHashtableLock); - PhAddItemSimpleHashtable(ContextHashtable, Control->UniqueKey, context); - PhReleaseQueuedLockExclusive(&ContextHashtableLock); - } - break; - case PluginThreadStackUninitializing: - { - PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); - - if (!context) - return; - - PhFree(context); - - PhAcquireQueuedLockExclusive(&ContextHashtableLock); - PhRemoveItemSimpleHashtable(ContextHashtable, Control->UniqueKey); - PhReleaseQueuedLockExclusive(&ContextHashtableLock); - } - break; - case PluginThreadStackResolveSymbol: - { - PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); - PPH_STRING managedSymbol = NULL; - ULONG64 displacement; - - if (!context) - return; - - if (context->Support) - { -#ifndef _WIN64 - PVOID predictedEip; - PVOID predictedEbp; - PVOID predictedEsp; - - predictedEip = context->PredictedEip; - predictedEbp = context->PredictedEbp; - predictedEsp = context->PredictedEsp; - - PredictAddressesFromClrData( - context->Support, - context->ThreadId, - Control->u.ResolveSymbol.StackFrame->PcAddress, - Control->u.ResolveSymbol.StackFrame->FrameAddress, - Control->u.ResolveSymbol.StackFrame->StackAddress, - &context->PredictedEip, - &context->PredictedEbp, - &context->PredictedEsp - ); - - // Fix up dbghelp EBP with real EBP given by the CLR data routines. - if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip) - { - Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp; - Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp; - } -#endif - - managedSymbol = GetRuntimeNameByAddressClrProcess( - context->Support, - (ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress, - &displacement - ); - } -#ifdef _WIN64 - else if (context->IsWow64 && context->ConnectedToPhSvc) - { - PVOID predictedEip; - PVOID predictedEbp; - PVOID predictedEsp; - - predictedEip = context->PredictedEip; - predictedEbp = context->PredictedEbp; - predictedEsp = context->PredictedEsp; - - CallPredictAddressesFromClrData( - context->ProcessId, - context->ThreadId, - Control->u.ResolveSymbol.StackFrame->PcAddress, - Control->u.ResolveSymbol.StackFrame->FrameAddress, - Control->u.ResolveSymbol.StackFrame->StackAddress, - &context->PredictedEip, - &context->PredictedEbp, - &context->PredictedEsp - ); - - // Fix up dbghelp EBP with real EBP given by the CLR data routines. - if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip) - { - Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp; - Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp; - } - - managedSymbol = CallGetRuntimeNameByAddress( - context->ProcessId, - (ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress, - &displacement - ); - } -#endif - - if (managedSymbol) - { - if (displacement != 0) - PhMoveReference(&managedSymbol, PhFormatString(L"%s + 0x%I64x", managedSymbol->Buffer, displacement)); - - if (Control->u.ResolveSymbol.Symbol) - PhMoveReference(&managedSymbol, PhFormatString(L"%s <-- %s", managedSymbol->Buffer, Control->u.ResolveSymbol.Symbol->Buffer)); - - PhMoveReference(&Control->u.ResolveSymbol.Symbol, managedSymbol); - } - } - break; - case PluginThreadStackBeginDefaultWalkStack: - { - PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); - BOOLEAN isDotNet; - - if (!context) - return; - - if (!NT_SUCCESS(PhGetProcessIsDotNet(context->ProcessId, &isDotNet)) || !isDotNet) - return; - - context->Support = CreateClrProcessSupport(context->ProcessId); - -#ifdef _WIN64 - if (context->IsWow64) - context->ConnectedToPhSvc = PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE); -#endif - } - break; - case PluginThreadStackEndDefaultWalkStack: - { - PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); - - if (!context) - return; - - if (context->Support) - { - FreeClrProcessSupport(context->Support); - context->Support = NULL; - } - -#ifdef _WIN64 - if (context->ConnectedToPhSvc) - { - PhUiDisconnectFromPhSvc(); - context->ConnectedToPhSvc = FALSE; - } -#endif - } - break; - } -} - -VOID PredictAddressesFromClrData( - _In_ PCLR_PROCESS_SUPPORT Support, - _In_ HANDLE ThreadId, - _In_ PVOID PcAddress, - _In_ PVOID FrameAddress, - _In_ PVOID StackAddress, - _Out_ PVOID *PredictedEip, - _Out_ PVOID *PredictedEbp, - _Out_ PVOID *PredictedEsp - ) -{ -#ifdef _WIN64 - *PredictedEip = NULL; - *PredictedEbp = NULL; - *PredictedEsp = NULL; -#else - IXCLRDataTask *task; - - *PredictedEip = NULL; - *PredictedEbp = NULL; - *PredictedEsp = NULL; - - if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID( - Support->DataProcess, - HandleToUlong(ThreadId), - &task - ))) - { - IXCLRDataStackWalk *stackWalk; - - if (SUCCEEDED(IXCLRDataTask_CreateStackWalk(task, 0xf, &stackWalk))) - { - HRESULT result; - BOOLEAN firstTime = TRUE; - CONTEXT context; - ULONG contextSize; - - memset(&context, 0, sizeof(CONTEXT)); - context.ContextFlags = CONTEXT_CONTROL; - context.Eip = PtrToUlong(PcAddress); - context.Ebp = PtrToUlong(FrameAddress); - context.Esp = PtrToUlong(StackAddress); - - result = IXCLRDataStackWalk_SetContext2(stackWalk, CLRDATA_STACK_SET_CURRENT_CONTEXT, sizeof(CONTEXT), (BYTE *)&context); - - if (SUCCEEDED(result = IXCLRDataStackWalk_Next(stackWalk)) && result != S_FALSE) - { - if (SUCCEEDED(IXCLRDataStackWalk_GetContext(stackWalk, CONTEXT_CONTROL, sizeof(CONTEXT), &contextSize, (BYTE *)&context))) - { - *PredictedEip = UlongToPtr(context.Eip); - *PredictedEbp = UlongToPtr(context.Ebp); - *PredictedEsp = UlongToPtr(context.Esp); - } - } - - IXCLRDataStackWalk_Release(stackWalk); - } - - IXCLRDataTask_Release(task); - } -#endif -} +/* + * Process Hacker .NET Tools - + * thread stack extensions + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "clrsup.h" +#include "svcext.h" +#include + +typedef struct _THREAD_STACK_CONTEXT +{ + PCLR_PROCESS_SUPPORT Support; + HANDLE ProcessId; + HANDLE ThreadId; + HANDLE ThreadHandle; + + PVOID PredictedEip; + PVOID PredictedEbp; + PVOID PredictedEsp; + +#ifdef _WIN64 + BOOLEAN IsWow64; + BOOLEAN ConnectedToPhSvc; +#endif +} THREAD_STACK_CONTEXT, *PTHREAD_STACK_CONTEXT; + +static PPH_HASHTABLE ContextHashtable; +static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT; +static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT; + +PTHREAD_STACK_CONTEXT FindThreadStackContext( + _In_ PVOID UniqueKey + ) +{ + PTHREAD_STACK_CONTEXT context; + PVOID *item; + + PhAcquireQueuedLockExclusive(&ContextHashtableLock); + + item = PhFindItemSimpleHashtable(ContextHashtable, UniqueKey); + + if (item) + context = *item; + else + context = NULL; + + PhReleaseQueuedLockExclusive(&ContextHashtableLock); + + return context; +} + +VOID ProcessThreadStackControl( + _In_ PPH_PLUGIN_THREAD_STACK_CONTROL Control + ) +{ + if (PhBeginInitOnce(&ContextHashtableInitOnce)) + { + ContextHashtable = PhCreateSimpleHashtable(4); + PhEndInitOnce(&ContextHashtableInitOnce); + } + + switch (Control->Type) + { + case PluginThreadStackInitializing: + { + PTHREAD_STACK_CONTEXT context; +#if _WIN64 + HANDLE processHandle; +#endif + + context = PhAllocate(sizeof(THREAD_STACK_CONTEXT)); + memset(context, 0, sizeof(THREAD_STACK_CONTEXT)); + context->ProcessId = Control->u.Initializing.ProcessId; + context->ThreadId = Control->u.Initializing.ThreadId; + context->ThreadHandle = Control->u.Initializing.ThreadHandle; + +#if _WIN64 + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, Control->u.Initializing.ProcessId))) + { + PhGetProcessIsWow64(processHandle, &context->IsWow64); + NtClose(processHandle); + } +#endif + + PhAcquireQueuedLockExclusive(&ContextHashtableLock); + PhAddItemSimpleHashtable(ContextHashtable, Control->UniqueKey, context); + PhReleaseQueuedLockExclusive(&ContextHashtableLock); + } + break; + case PluginThreadStackUninitializing: + { + PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); + + if (!context) + return; + + PhFree(context); + + PhAcquireQueuedLockExclusive(&ContextHashtableLock); + PhRemoveItemSimpleHashtable(ContextHashtable, Control->UniqueKey); + PhReleaseQueuedLockExclusive(&ContextHashtableLock); + } + break; + case PluginThreadStackResolveSymbol: + { + PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); + PPH_STRING managedSymbol = NULL; + ULONG64 displacement; + + if (!context) + return; + + if (context->Support) + { +#ifndef _WIN64 + PVOID predictedEip; + PVOID predictedEbp; + PVOID predictedEsp; + + predictedEip = context->PredictedEip; + predictedEbp = context->PredictedEbp; + predictedEsp = context->PredictedEsp; + + PredictAddressesFromClrData( + context->Support, + context->ThreadId, + Control->u.ResolveSymbol.StackFrame->PcAddress, + Control->u.ResolveSymbol.StackFrame->FrameAddress, + Control->u.ResolveSymbol.StackFrame->StackAddress, + &context->PredictedEip, + &context->PredictedEbp, + &context->PredictedEsp + ); + + // Fix up dbghelp EBP with real EBP given by the CLR data routines. + if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip) + { + Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp; + Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp; + } +#endif + + managedSymbol = GetRuntimeNameByAddressClrProcess( + context->Support, + (ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress, + &displacement + ); + } +#ifdef _WIN64 + else if (context->IsWow64 && context->ConnectedToPhSvc) + { + PVOID predictedEip; + PVOID predictedEbp; + PVOID predictedEsp; + + predictedEip = context->PredictedEip; + predictedEbp = context->PredictedEbp; + predictedEsp = context->PredictedEsp; + + CallPredictAddressesFromClrData( + context->ProcessId, + context->ThreadId, + Control->u.ResolveSymbol.StackFrame->PcAddress, + Control->u.ResolveSymbol.StackFrame->FrameAddress, + Control->u.ResolveSymbol.StackFrame->StackAddress, + &context->PredictedEip, + &context->PredictedEbp, + &context->PredictedEsp + ); + + // Fix up dbghelp EBP with real EBP given by the CLR data routines. + if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip) + { + Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp; + Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp; + } + + managedSymbol = CallGetRuntimeNameByAddress( + context->ProcessId, + (ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress, + &displacement + ); + } +#endif + + if (managedSymbol) + { + if (displacement != 0) + PhMoveReference(&managedSymbol, PhFormatString(L"%s + 0x%I64x", managedSymbol->Buffer, displacement)); + + if (Control->u.ResolveSymbol.Symbol) + PhMoveReference(&managedSymbol, PhFormatString(L"%s <-- %s", managedSymbol->Buffer, Control->u.ResolveSymbol.Symbol->Buffer)); + + PhMoveReference(&Control->u.ResolveSymbol.Symbol, managedSymbol); + } + } + break; + case PluginThreadStackBeginDefaultWalkStack: + { + PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); + BOOLEAN isDotNet; + + if (!context) + return; + + if (!NT_SUCCESS(PhGetProcessIsDotNet(context->ProcessId, &isDotNet)) || !isDotNet) + return; + + context->Support = CreateClrProcessSupport(context->ProcessId); + +#ifdef _WIN64 + if (context->IsWow64) + context->ConnectedToPhSvc = PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE); +#endif + } + break; + case PluginThreadStackEndDefaultWalkStack: + { + PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey); + + if (!context) + return; + + if (context->Support) + { + FreeClrProcessSupport(context->Support); + context->Support = NULL; + } + +#ifdef _WIN64 + if (context->ConnectedToPhSvc) + { + PhUiDisconnectFromPhSvc(); + context->ConnectedToPhSvc = FALSE; + } +#endif + } + break; + } +} + +VOID PredictAddressesFromClrData( + _In_ PCLR_PROCESS_SUPPORT Support, + _In_ HANDLE ThreadId, + _In_ PVOID PcAddress, + _In_ PVOID FrameAddress, + _In_ PVOID StackAddress, + _Out_ PVOID *PredictedEip, + _Out_ PVOID *PredictedEbp, + _Out_ PVOID *PredictedEsp + ) +{ +#ifdef _WIN64 + *PredictedEip = NULL; + *PredictedEbp = NULL; + *PredictedEsp = NULL; +#else + IXCLRDataTask *task; + + *PredictedEip = NULL; + *PredictedEbp = NULL; + *PredictedEsp = NULL; + + if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID( + Support->DataProcess, + HandleToUlong(ThreadId), + &task + ))) + { + IXCLRDataStackWalk *stackWalk; + + if (SUCCEEDED(IXCLRDataTask_CreateStackWalk(task, 0xf, &stackWalk))) + { + HRESULT result; + BOOLEAN firstTime = TRUE; + CONTEXT context; + ULONG contextSize; + + memset(&context, 0, sizeof(CONTEXT)); + context.ContextFlags = CONTEXT_CONTROL; + context.Eip = PtrToUlong(PcAddress); + context.Ebp = PtrToUlong(FrameAddress); + context.Esp = PtrToUlong(StackAddress); + + result = IXCLRDataStackWalk_SetContext2(stackWalk, CLRDATA_STACK_SET_CURRENT_CONTEXT, sizeof(CONTEXT), (BYTE *)&context); + + if (SUCCEEDED(result = IXCLRDataStackWalk_Next(stackWalk)) && result != S_FALSE) + { + if (SUCCEEDED(IXCLRDataStackWalk_GetContext(stackWalk, CONTEXT_CONTROL, sizeof(CONTEXT), &contextSize, (BYTE *)&context))) + { + *PredictedEip = UlongToPtr(context.Eip); + *PredictedEbp = UlongToPtr(context.Ebp); + *PredictedEsp = UlongToPtr(context.Esp); + } + } + + IXCLRDataStackWalk_Release(stackWalk); + } + + IXCLRDataTask_Release(task); + } +#endif +} diff --git a/plugins/DotNetTools/svcext.c b/plugins/DotNetTools/svcext.c index 2cd5f14b9608..0a0f2fb13bf4 100644 --- a/plugins/DotNetTools/svcext.c +++ b/plugins/DotNetTools/svcext.c @@ -1,209 +1,317 @@ -/* - * Process Hacker .NET Tools - - * phsvc extensions - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" -#include "svcext.h" -#include "clrsup.h" - -PPH_STRING CallGetRuntimeNameByAddress( - _In_ HANDLE ProcessId, - _In_ ULONG64 Address, - _Out_opt_ PULONG64 Displacement - ) -{ - NTSTATUS status; - PH_PLUGIN_PHSVC_CLIENT client; - DN_API_GETRUNTIMENAMEBYADDRESS in; - DN_API_GETRUNTIMENAMEBYADDRESS out; - ULONG bufferSize; - PVOID buffer; - PPH_STRING name = NULL; - - if (!PhPluginQueryPhSvc(&client)) - return NULL; - - in.i.ProcessId = HandleToUlong(ProcessId); - in.i.Address = Address; - - bufferSize = 0x1000; - - if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Name))) - return NULL; - - status = PhPluginCallPhSvc(PluginInstance, DnGetRuntimeNameByAddressApiNumber, &in, sizeof(in), &out, sizeof(out)); - - if (status == STATUS_BUFFER_OVERFLOW) - { - client.FreeHeap(buffer); - bufferSize = out.o.NameLength; - - if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Name))) - return NULL; - - status = PhPluginCallPhSvc(PluginInstance, DnGetRuntimeNameByAddressApiNumber, &in, sizeof(in), &out, sizeof(out)); - } - - if (NT_SUCCESS(status)) - { - name = PhCreateStringEx(buffer, out.o.NameLength); - - if (Displacement) - *Displacement = out.o.Displacement; - } - - client.FreeHeap(buffer); - - return name; -} - -NTSTATUS DispatchGetRuntimeNameByAddress( - _In_ PPH_PLUGIN_PHSVC_REQUEST Request, - _In_ PDN_API_GETRUNTIMENAMEBYADDRESS In, - _Out_ PDN_API_GETRUNTIMENAMEBYADDRESS Out - ) -{ - NTSTATUS status = STATUS_SUCCESS; - PVOID nameBuffer; - PCLR_PROCESS_SUPPORT support; - PPH_STRING name; - - if (!NT_SUCCESS(status = Request->ProbeBuffer(&In->i.Name, sizeof(WCHAR), FALSE, &nameBuffer))) - return status; - - support = CreateClrProcessSupport(UlongToHandle(In->i.ProcessId)); - - if (!support) - return STATUS_UNSUCCESSFUL; - - name = GetRuntimeNameByAddressClrProcess(support, In->i.Address, &Out->o.Displacement); - - if (!name) - { - status = STATUS_UNSUCCESSFUL; - goto CleanupExit; - } - - memcpy(nameBuffer, name->Buffer, min(name->Length, In->i.Name.Length)); - Out->o.NameLength = (ULONG)name->Length; - - if (In->i.Name.Length < name->Length) - status = STATUS_BUFFER_OVERFLOW; - -CleanupExit: - FreeClrProcessSupport(support); - - return status; -} - -VOID CallPredictAddressesFromClrData( - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId, - _In_ PVOID PcAddress, - _In_ PVOID FrameAddress, - _In_ PVOID StackAddress, - _Out_ PVOID *PredictedEip, - _Out_ PVOID *PredictedEbp, - _Out_ PVOID *PredictedEsp - ) -{ - PH_PLUGIN_PHSVC_CLIENT client; - DN_API_PREDICTADDRESSESFROMCLRDATA in; - DN_API_PREDICTADDRESSESFROMCLRDATA out; - - *PredictedEip = NULL; - *PredictedEbp = NULL; - *PredictedEsp = NULL; - - if (!PhPluginQueryPhSvc(&client)) - return; - - in.i.ProcessId = HandleToUlong(ProcessId); - in.i.ThreadId = HandleToUlong(ThreadId); - in.i.PcAddress = PtrToUlong(PcAddress); - in.i.FrameAddress = PtrToUlong(FrameAddress); - in.i.StackAddress = PtrToUlong(StackAddress); - - if (NT_SUCCESS(PhPluginCallPhSvc(PluginInstance, DnPredictAddressesFromClrDataApiNumber, &in, sizeof(in), &out, sizeof(out)))) - { - *PredictedEip = UlongToPtr(out.o.PredictedEip); - *PredictedEbp = UlongToPtr(out.o.PredictedEbp); - *PredictedEsp = UlongToPtr(out.o.PredictedEsp); - } -} - -NTSTATUS DispatchPredictAddressesFromClrData( - _In_ PPH_PLUGIN_PHSVC_REQUEST Request, - _In_ PDN_API_PREDICTADDRESSESFROMCLRDATA In, - _Out_ PDN_API_PREDICTADDRESSESFROMCLRDATA Out - ) -{ - PCLR_PROCESS_SUPPORT support; - PVOID predictedEip; - PVOID predictedEbp; - PVOID predictedEsp; - - support = CreateClrProcessSupport(UlongToHandle(In->i.ProcessId)); - - if (!support) - return STATUS_UNSUCCESSFUL; - - PredictAddressesFromClrData( - support, - UlongToHandle(In->i.ThreadId), - UlongToPtr(In->i.PcAddress), - UlongToPtr(In->i.FrameAddress), - UlongToPtr(In->i.StackAddress), - &predictedEip, - &predictedEbp, - &predictedEsp - ); - FreeClrProcessSupport(support); - - Out->o.PredictedEip = PtrToUlong(predictedEip); - Out->o.PredictedEbp = PtrToUlong(predictedEbp); - Out->o.PredictedEsp = PtrToUlong(predictedEsp); - - return STATUS_SUCCESS; -} - -VOID DispatchPhSvcRequest( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_PHSVC_REQUEST request = Parameter; - PVOID inBuffer; - - // InBuffer can alias OutBuffer, so make a copy of InBuffer. - inBuffer = PhAllocateCopy(request->InBuffer, request->InLength); - - switch (request->SubId) - { - case DnGetRuntimeNameByAddressApiNumber: - request->ReturnStatus = DispatchGetRuntimeNameByAddress(request, inBuffer, request->OutBuffer); - break; - case DnPredictAddressesFromClrDataApiNumber: - request->ReturnStatus = DispatchPredictAddressesFromClrData(request, inBuffer, request->OutBuffer); - break; - } - - PhFree(inBuffer); -} +/* + * Process Hacker .NET Tools - + * phsvc extensions + * + * Copyright (C) 2015 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "svcext.h" +#include "clrsup.h" + +PPH_STRING CallGetRuntimeNameByAddress( + _In_ HANDLE ProcessId, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement + ) +{ + NTSTATUS status; + PH_PLUGIN_PHSVC_CLIENT client; + DN_API_GETRUNTIMENAMEBYADDRESS in; + DN_API_GETRUNTIMENAMEBYADDRESS out; + ULONG bufferSize; + PVOID buffer; + PPH_STRING name = NULL; + + if (!PhPluginQueryPhSvc(&client)) + return NULL; + + in.i.ProcessId = HandleToUlong(ProcessId); + in.i.Address = Address; + + bufferSize = 0x1000; + + if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Name))) + return NULL; + + status = PhPluginCallPhSvc(PluginInstance, DnGetRuntimeNameByAddressApiNumber, &in, sizeof(in), &out, sizeof(out)); + + if (status == STATUS_BUFFER_OVERFLOW) + { + client.FreeHeap(buffer); + bufferSize = out.o.NameLength; + + if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Name))) + return NULL; + + status = PhPluginCallPhSvc(PluginInstance, DnGetRuntimeNameByAddressApiNumber, &in, sizeof(in), &out, sizeof(out)); + } + + if (NT_SUCCESS(status)) + { + name = PhCreateStringEx(buffer, out.o.NameLength); + + if (Displacement) + *Displacement = out.o.Displacement; + } + + client.FreeHeap(buffer); + + return name; +} + +NTSTATUS DispatchGetRuntimeNameByAddress( + _In_ PPH_PLUGIN_PHSVC_REQUEST Request, + _In_ PDN_API_GETRUNTIMENAMEBYADDRESS In, + _Out_ PDN_API_GETRUNTIMENAMEBYADDRESS Out + ) +{ + NTSTATUS status; + PVOID nameBuffer; + PCLR_PROCESS_SUPPORT support; + PPH_STRING name; + + if (!NT_SUCCESS(status = Request->ProbeBuffer(&In->i.Name, sizeof(WCHAR), FALSE, &nameBuffer))) + return status; + + support = CreateClrProcessSupport(UlongToHandle(In->i.ProcessId)); + + if (!support) + return STATUS_UNSUCCESSFUL; + + name = GetRuntimeNameByAddressClrProcess(support, In->i.Address, &Out->o.Displacement); + + if (!name) + { + status = STATUS_UNSUCCESSFUL; + goto CleanupExit; + } + + memcpy(nameBuffer, name->Buffer, min(name->Length, In->i.Name.Length)); + Out->o.NameLength = (ULONG)name->Length; + + if (In->i.Name.Length < name->Length) + status = STATUS_BUFFER_OVERFLOW; + +CleanupExit: + FreeClrProcessSupport(support); + + return status; +} + +VOID CallPredictAddressesFromClrData( + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId, + _In_ PVOID PcAddress, + _In_ PVOID FrameAddress, + _In_ PVOID StackAddress, + _Out_ PVOID *PredictedEip, + _Out_ PVOID *PredictedEbp, + _Out_ PVOID *PredictedEsp + ) +{ + PH_PLUGIN_PHSVC_CLIENT client; + DN_API_PREDICTADDRESSESFROMCLRDATA in; + DN_API_PREDICTADDRESSESFROMCLRDATA out; + + *PredictedEip = NULL; + *PredictedEbp = NULL; + *PredictedEsp = NULL; + + if (!PhPluginQueryPhSvc(&client)) + return; + + in.i.ProcessId = HandleToUlong(ProcessId); + in.i.ThreadId = HandleToUlong(ThreadId); + in.i.PcAddress = PtrToUlong(PcAddress); + in.i.FrameAddress = PtrToUlong(FrameAddress); + in.i.StackAddress = PtrToUlong(StackAddress); + + if (NT_SUCCESS(PhPluginCallPhSvc(PluginInstance, DnPredictAddressesFromClrDataApiNumber, &in, sizeof(in), &out, sizeof(out)))) + { + *PredictedEip = UlongToPtr(out.o.PredictedEip); + *PredictedEbp = UlongToPtr(out.o.PredictedEbp); + *PredictedEsp = UlongToPtr(out.o.PredictedEsp); + } +} + +NTSTATUS DispatchPredictAddressesFromClrData( + _In_ PPH_PLUGIN_PHSVC_REQUEST Request, + _In_ PDN_API_PREDICTADDRESSESFROMCLRDATA In, + _Out_ PDN_API_PREDICTADDRESSESFROMCLRDATA Out + ) +{ + PCLR_PROCESS_SUPPORT support; + PVOID predictedEip; + PVOID predictedEbp; + PVOID predictedEsp; + + support = CreateClrProcessSupport(UlongToHandle(In->i.ProcessId)); + + if (!support) + return STATUS_UNSUCCESSFUL; + + PredictAddressesFromClrData( + support, + UlongToHandle(In->i.ThreadId), + UlongToPtr(In->i.PcAddress), + UlongToPtr(In->i.FrameAddress), + UlongToPtr(In->i.StackAddress), + &predictedEip, + &predictedEbp, + &predictedEsp + ); + FreeClrProcessSupport(support); + + Out->o.PredictedEip = PtrToUlong(predictedEip); + Out->o.PredictedEbp = PtrToUlong(predictedEbp); + Out->o.PredictedEsp = PtrToUlong(predictedEsp); + + return STATUS_SUCCESS; +} + +PPH_STRING CallGetClrThreadAppDomain( + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId + ) +{ + NTSTATUS status; + PH_PLUGIN_PHSVC_CLIENT client; + DN_API_GETWOW64THREADAPPDOMAIN in; + DN_API_GETWOW64THREADAPPDOMAIN out; + ULONG bufferSize; + PVOID buffer; + PPH_STRING name = NULL; + + if (!PhPluginQueryPhSvc(&client)) + return NULL; + + in.i.ProcessId = HandleToUlong(ProcessId); + in.i.ThreadId = HandleToUlong(ThreadId); + + bufferSize = 0x1000; + + if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Name))) + return NULL; + + status = PhPluginCallPhSvc(PluginInstance, DnGetGetClrWow64ThreadAppDomainApiNumber, &in, sizeof(in), &out, sizeof(out)); + + if (status == STATUS_BUFFER_OVERFLOW) + { + client.FreeHeap(buffer); + bufferSize = out.o.NameLength; + + if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Name))) + return NULL; + + status = PhPluginCallPhSvc(PluginInstance, DnGetGetClrWow64ThreadAppDomainApiNumber, &in, sizeof(in), &out, sizeof(out)); + } + + if (NT_SUCCESS(status)) + { + name = PhCreateStringEx(buffer, out.o.NameLength); + } + + client.FreeHeap(buffer); + + return name; +} + +NTSTATUS DispatchGetClrThreadAppDomain( + _In_ PPH_PLUGIN_PHSVC_REQUEST Request, + _In_ PDN_API_GETWOW64THREADAPPDOMAIN In, + _Out_ PDN_API_GETWOW64THREADAPPDOMAIN Out + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PVOID nameBuffer; + PCLR_PROCESS_SUPPORT support; + IXCLRDataProcess *process; + IXCLRDataTask *task; + IXCLRDataAppDomain *appDomain; + PPH_STRING AppDomainText = NULL; + + if (!NT_SUCCESS(status = Request->ProbeBuffer(&In->i.Name, sizeof(WCHAR), FALSE, &nameBuffer))) + return status; + + if (!(support = CreateClrProcessSupport(UlongToHandle(In->i.ProcessId)))) + return STATUS_UNSUCCESSFUL; + + if (!(process = support->DataProcess)) + { + status = STATUS_UNSUCCESSFUL; + goto CleanupExit; + } + + if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID(process, In->i.ThreadId, &task))) + { + if (SUCCEEDED(IXCLRDataTask_GetCurrentAppDomain(task, &appDomain))) + { + AppDomainText = GetNameXClrDataAppDomain(appDomain); + IXCLRDataAppDomain_Release(appDomain); + } + + IXCLRDataTask_Release(task); + } + + if (!AppDomainText) + { + status = STATUS_UNSUCCESSFUL; + goto CleanupExit; + } + + memcpy(nameBuffer, AppDomainText->Buffer, min(AppDomainText->Length, In->i.Name.Length)); + Out->o.NameLength = (ULONG)AppDomainText->Length; + + if (In->i.Name.Length < AppDomainText->Length) + status = STATUS_BUFFER_OVERFLOW; + +CleanupExit: + + if (support) + FreeClrProcessSupport(support); + + return status; +} + +VOID DispatchPhSvcRequest( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_PHSVC_REQUEST request = Parameter; + PVOID inBuffer; + + // InBuffer can alias OutBuffer, so make a copy of InBuffer. + inBuffer = PhAllocateCopy(request->InBuffer, request->InLength); + + switch (request->SubId) + { + case DnGetRuntimeNameByAddressApiNumber: + request->ReturnStatus = DispatchGetRuntimeNameByAddress(request, inBuffer, request->OutBuffer); + break; + case DnPredictAddressesFromClrDataApiNumber: + request->ReturnStatus = DispatchPredictAddressesFromClrData(request, inBuffer, request->OutBuffer); + break; + case DnGetGetClrWow64ThreadAppDomainApiNumber: + request->ReturnStatus = DispatchGetClrThreadAppDomain(request, inBuffer, request->OutBuffer); + break; + } + + PhFree(inBuffer); +} diff --git a/plugins/DotNetTools/svcext.h b/plugins/DotNetTools/svcext.h index fc5d63df0374..31d16927a63f 100644 --- a/plugins/DotNetTools/svcext.h +++ b/plugins/DotNetTools/svcext.h @@ -1,87 +1,108 @@ -/* - * Process Hacker .NET Tools - - * phsvc extensions - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef SVCEXT_H -#define SVCEXT_H - -// API - -typedef enum _DN_API_NUMBER -{ - DnGetRuntimeNameByAddressApiNumber = 1, - DnPredictAddressesFromClrDataApiNumber = 2 -} DN_API_NUMBER; - -typedef union _DN_API_GETRUNTIMENAMEBYADDRESS -{ - struct - { - ULONG ProcessId; - ULONG Reserved; - ULONG64 Address; - PH_RELATIVE_STRINGREF Name; // out - } i; - struct - { - ULONG64 Displacement; - ULONG NameLength; - } o; -} DN_API_GETRUNTIMENAMEBYADDRESS, *PDN_API_GETRUNTIMENAMEBYADDRESS; - -typedef union _DN_API_PREDICTADDRESSESFROMCLRDATA -{ - struct - { - ULONG ProcessId; - ULONG ThreadId; - ULONG PcAddress; - ULONG FrameAddress; - ULONG StackAddress; - } i; - struct - { - ULONG PredictedEip; - ULONG PredictedEbp; - ULONG PredictedEsp; - } o; -} DN_API_PREDICTADDRESSESFROMCLRDATA, *PDN_API_PREDICTADDRESSESFROMCLRDATA; - -// Calls - -PPH_STRING CallGetRuntimeNameByAddress( - _In_ HANDLE ProcessId, - _In_ ULONG64 Address, - _Out_opt_ PULONG64 Displacement - ); - -VOID CallPredictAddressesFromClrData( - _In_ HANDLE ProcessId, - _In_ HANDLE ThreadId, - _In_ PVOID PcAddress, - _In_ PVOID FrameAddress, - _In_ PVOID StackAddress, - _Out_ PVOID *PredictedEip, - _Out_ PVOID *PredictedEbp, - _Out_ PVOID *PredictedEsp - ); - +/* + * Process Hacker .NET Tools - + * phsvc extensions + * + * Copyright (C) 2015 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef SVCEXT_H +#define SVCEXT_H + +// API + +typedef enum _DN_API_NUMBER +{ + DnGetRuntimeNameByAddressApiNumber = 1, + DnPredictAddressesFromClrDataApiNumber = 2, + DnGetGetClrWow64ThreadAppDomainApiNumber = 3 +} DN_API_NUMBER; + +typedef union _DN_API_GETRUNTIMENAMEBYADDRESS +{ + struct + { + ULONG ProcessId; + ULONG Reserved; + ULONG64 Address; + PH_RELATIVE_STRINGREF Name; // out + } i; + struct + { + ULONG64 Displacement; + ULONG NameLength; + } o; +} DN_API_GETRUNTIMENAMEBYADDRESS, *PDN_API_GETRUNTIMENAMEBYADDRESS; + +typedef union _DN_API_PREDICTADDRESSESFROMCLRDATA +{ + struct + { + ULONG ProcessId; + ULONG ThreadId; + ULONG PcAddress; + ULONG FrameAddress; + ULONG StackAddress; + } i; + struct + { + ULONG PredictedEip; + ULONG PredictedEbp; + ULONG PredictedEsp; + } o; +} DN_API_PREDICTADDRESSESFROMCLRDATA, *PDN_API_PREDICTADDRESSESFROMCLRDATA; + +typedef union _DN_API_GETWOW64THREADAPPDOMAIN +{ + struct + { + ULONG ProcessId; + ULONG ThreadId; + PH_RELATIVE_STRINGREF Name; // out + } i; + struct + { + ULONG NameLength; + } o; +} DN_API_GETWOW64THREADAPPDOMAIN, *PDN_API_GETWOW64THREADAPPDOMAIN; + +// Calls + +PPH_STRING CallGetRuntimeNameByAddress( + _In_ HANDLE ProcessId, + _In_ ULONG64 Address, + _Out_opt_ PULONG64 Displacement + ); + +VOID CallPredictAddressesFromClrData( + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId, + _In_ PVOID PcAddress, + _In_ PVOID FrameAddress, + _In_ PVOID StackAddress, + _Out_ PVOID *PredictedEip, + _Out_ PVOID *PredictedEbp, + _Out_ PVOID *PredictedEsp + ); + +PPH_STRING CallGetClrThreadAppDomain( + _In_ HANDLE ProcessId, + _In_ HANDLE ThreadId + ); + #endif \ No newline at end of file diff --git a/plugins/DotNetTools/treeext.c b/plugins/DotNetTools/treeext.c index 74aea6f14b14..5890f60fdebe 100644 --- a/plugins/DotNetTools/treeext.c +++ b/plugins/DotNetTools/treeext.c @@ -1,296 +1,373 @@ -/* - * Process Hacker .NET Tools - - * thread list extensions - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "dn.h" -#include "clrsup.h" - -VOID NTAPI ThreadsContextCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ); - -VOID NTAPI ThreadsContextDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ); - -VOID ThreadTreeNewMessage( - _In_ PVOID Parameter - ); - -LONG ThreadTreeNewSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ); - -#define THREAD_TREE_CONTEXT_TYPE 1 - -typedef struct _THREAD_TREE_CONTEXT -{ - ULONG Type; - HANDLE ProcessId; - PH_CALLBACK_REGISTRATION AddedCallbackRegistration; - PCLR_PROCESS_SUPPORT Support; -} THREAD_TREE_CONTEXT, *PTHREAD_TREE_CONTEXT; - -static PPH_HASHTABLE ContextHashtable; -static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT; -static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT; - -VOID InitializeTreeNewObjectExtensions( - VOID - ) -{ - PhPluginSetObjectExtension( - PluginInstance, - EmThreadsContextType, - sizeof(THREAD_TREE_CONTEXT), - ThreadsContextCreateCallback, - ThreadsContextDeleteCallback - ); -} - -VOID AddTreeNewColumn( - _In_ PPH_PLUGIN_TREENEW_INFORMATION TreeNewInfo, - _In_ PVOID Context, - _In_ ULONG SubId, - _In_ BOOLEAN Visible, - _In_ PWSTR Text, - _In_ ULONG Width, - _In_ ULONG Alignment, - _In_ ULONG TextFlags, - _In_ BOOLEAN SortDescending, - _In_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction - ) -{ - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Visible = Visible; - column.SortDescending = SortDescending; - column.Text = Text; - column.Width = Width; - column.Alignment = Alignment; - column.DisplayIndex = TreeNew_GetVisibleColumnCount(TreeNewInfo->TreeNewHandle); - column.TextFlags = TextFlags; - - PhPluginAddTreeNewColumn( - PluginInstance, - TreeNewInfo->CmData, - &column, - SubId, - Context, - SortFunction - ); -} - -VOID DispatchTreeNewMessage( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - - switch (*(PULONG)message->Context) - { - case THREAD_TREE_CONTEXT_TYPE: - ThreadTreeNewMessage(Parameter); - break; - } -} - -static VOID ThreadAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_THREAD_ITEM threadItem = Parameter; - PDN_THREAD_ITEM dnThread; - PTHREAD_TREE_CONTEXT context = Context; - - dnThread = PhPluginGetObjectExtension(PluginInstance, threadItem, EmThreadItemType); - memset(dnThread, 0, sizeof(DN_THREAD_ITEM)); - dnThread->ThreadItem = threadItem; -} - -VOID NTAPI ThreadsContextCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_THREADS_CONTEXT threadsContext = Object; - PTHREAD_TREE_CONTEXT context = Extension; - - memset(context, 0, sizeof(THREAD_TREE_CONTEXT)); - context->Type = THREAD_TREE_CONTEXT_TYPE; - context->ProcessId = threadsContext->Provider->ProcessId; - - PhRegisterCallback( - &threadsContext->Provider->ThreadAddedEvent, - ThreadAddedHandler, - context, - &context->AddedCallbackRegistration - ); -} - -VOID NTAPI ThreadsContextDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_THREADS_CONTEXT threadsContext = Object; - PTHREAD_TREE_CONTEXT context = Extension; - - PhUnregisterCallback( - &threadsContext->Provider->ThreadAddedEvent, - &context->AddedCallbackRegistration - ); - - if (context->Support) - FreeClrProcessSupport(context->Support); -} - -VOID ThreadTreeNewInitializing( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PPH_THREADS_CONTEXT threadsContext; - PTHREAD_TREE_CONTEXT context; - BOOLEAN isDotNet; - - threadsContext = info->SystemContext; - context = PhPluginGetObjectExtension(PluginInstance, threadsContext, EmThreadsContextType); - - if (NT_SUCCESS(PhGetProcessIsDotNet(threadsContext->Provider->ProcessId, &isDotNet)) && isDotNet) - { - PCLR_PROCESS_SUPPORT support; - - support = CreateClrProcessSupport(threadsContext->Provider->ProcessId); - - if (!support) - return; - - context->Support = support; - - AddTreeNewColumn(info, context, DNTHTNC_APPDOMAIN, TRUE, L"AppDomain", 120, PH_ALIGN_LEFT, 0, FALSE, ThreadTreeNewSortFunction); - } -} - -VOID ThreadTreeNewUninitializing( - _In_ PVOID Parameter - ) -{ - NOTHING; -} - -VOID UpdateThreadClrData( - _In_ PTHREAD_TREE_CONTEXT Context, - _Inout_ PDN_THREAD_ITEM DnThread - ) -{ - if (!DnThread->ClrDataValid) - { - IXCLRDataProcess *process; - IXCLRDataTask *task; - IXCLRDataAppDomain *appDomain; - - if (Context->Support) - process = Context->Support->DataProcess; - else - return; - - if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID(process, HandleToUlong(DnThread->ThreadItem->ThreadId), &task))) - { - if (SUCCEEDED(IXCLRDataTask_GetCurrentAppDomain(task, &appDomain))) - { - DnThread->AppDomainText = GetNameXClrDataAppDomain(appDomain); - IXCLRDataAppDomain_Release(appDomain); - } - - IXCLRDataTask_Release(task); - } - - DnThread->ClrDataValid = TRUE; - } -} - -VOID ThreadTreeNewMessage( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - PTHREAD_TREE_CONTEXT context = message->Context; - - if (message->Message == TreeNewGetCellText) - { - PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; - PPH_THREAD_NODE threadNode = (PPH_THREAD_NODE)getCellText->Node; - PDN_THREAD_ITEM dnThread; - - dnThread = PhPluginGetObjectExtension(PluginInstance, threadNode->ThreadItem, EmThreadItemType); - - switch (message->SubId) - { - case DNTHTNC_APPDOMAIN: - UpdateThreadClrData(context, dnThread); - getCellText->Text = PhGetStringRef(dnThread->AppDomainText); - break; - } - } -} - -LONG ThreadTreeNewSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PTHREAD_TREE_CONTEXT context = Context; - LONG result; - PPH_THREAD_NODE node1 = Node1; - PPH_THREAD_NODE node2 = Node2; - PDN_THREAD_ITEM dnThread1; - PDN_THREAD_ITEM dnThread2; - - dnThread1 = PhPluginGetObjectExtension(PluginInstance, node1->ThreadItem, EmThreadItemType); - dnThread2 = PhPluginGetObjectExtension(PluginInstance, node2->ThreadItem, EmThreadItemType); - - result = 0; - - switch (SubId) - { - case DNTHTNC_APPDOMAIN: - UpdateThreadClrData(context, dnThread1); - UpdateThreadClrData(context, dnThread2); - result = PhCompareStringWithNull(dnThread1->AppDomainText, dnThread2->AppDomainText, TRUE); - break; - } - - return result; -} +/* + * Process Hacker .NET Tools - + * thread list extensions + * + * Copyright (C) 2015 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "dn.h" +#include "clrsup.h" +#include "svcext.h" + +VOID NTAPI ThreadsContextCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ); + +VOID NTAPI ThreadsContextDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ); + +VOID ThreadTreeNewMessage( + _In_ PVOID Parameter + ); + +LONG ThreadTreeNewSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ); + +#define THREAD_TREE_CONTEXT_TYPE 1 + +typedef struct _THREAD_TREE_CONTEXT +{ + ULONG Type; + HANDLE ProcessId; +#if _WIN64 + BOOLEAN IsWow64; + BOOLEAN ConnectedToPhSvc; +#endif + PH_CALLBACK_REGISTRATION AddedCallbackRegistration; + PH_CALLBACK_REGISTRATION RemovedCallbackRegistration; + PCLR_PROCESS_SUPPORT Support; +} THREAD_TREE_CONTEXT, *PTHREAD_TREE_CONTEXT; + +static PPH_HASHTABLE ContextHashtable; +static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT; +static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT; + +VOID InitializeTreeNewObjectExtensions( + VOID + ) +{ + PhPluginSetObjectExtension( + PluginInstance, + EmThreadsContextType, + sizeof(THREAD_TREE_CONTEXT), + ThreadsContextCreateCallback, + ThreadsContextDeleteCallback + ); +} + +VOID AddTreeNewColumn( + _In_ PPH_PLUGIN_TREENEW_INFORMATION TreeNewInfo, + _In_ PVOID Context, + _In_ ULONG SubId, + _In_ BOOLEAN Visible, + _In_ PWSTR Text, + _In_ ULONG Width, + _In_ ULONG Alignment, + _In_ ULONG TextFlags, + _In_ BOOLEAN SortDescending, + _In_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction + ) +{ + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Visible = Visible; + column.SortDescending = SortDescending; + column.Text = Text; + column.Width = Width; + column.Alignment = Alignment; + column.DisplayIndex = TreeNew_GetVisibleColumnCount(TreeNewInfo->TreeNewHandle); + column.TextFlags = TextFlags; + + PhPluginAddTreeNewColumn( + PluginInstance, + TreeNewInfo->CmData, + &column, + SubId, + Context, + SortFunction + ); +} + +VOID DispatchTreeNewMessage( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + + switch (*(PULONG)message->Context) + { + case THREAD_TREE_CONTEXT_TYPE: + ThreadTreeNewMessage(Parameter); + break; + } +} + +static VOID ThreadAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_ITEM threadItem = Parameter; + PDN_THREAD_ITEM dnThread; + PTHREAD_TREE_CONTEXT context = Context; + + dnThread = PhPluginGetObjectExtension(PluginInstance, threadItem, EmThreadItemType); + memset(dnThread, 0, sizeof(DN_THREAD_ITEM)); + dnThread->ThreadItem = threadItem; +} + +static VOID ThreadRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_THREAD_ITEM threadItem = Parameter; + PDN_THREAD_ITEM dnThread; + PTHREAD_TREE_CONTEXT context = Context; + + dnThread = PhPluginGetObjectExtension(PluginInstance, threadItem, EmThreadItemType); + + if (dnThread) + { + PhClearReference(&dnThread->AppDomainText); + } +} + +VOID NTAPI ThreadsContextCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_THREADS_CONTEXT threadsContext = Object; + PTHREAD_TREE_CONTEXT context = Extension; + + memset(context, 0, sizeof(THREAD_TREE_CONTEXT)); + context->Type = THREAD_TREE_CONTEXT_TYPE; + context->ProcessId = threadsContext->Provider->ProcessId; + + PhRegisterCallback( + &threadsContext->Provider->ThreadAddedEvent, + ThreadAddedHandler, + context, + &context->AddedCallbackRegistration + ); + + PhRegisterCallback( + &threadsContext->Provider->ThreadRemovedEvent, + ThreadRemovedHandler, + context, + &context->RemovedCallbackRegistration + ); +} + +VOID NTAPI ThreadsContextDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_THREADS_CONTEXT threadsContext = Object; + PTHREAD_TREE_CONTEXT context = Extension; + + PhUnregisterCallback( + &threadsContext->Provider->ThreadAddedEvent, + &context->AddedCallbackRegistration + ); + + PhUnregisterCallback( + &threadsContext->Provider->ThreadRemovedEvent, + &context->RemovedCallbackRegistration + ); + + if (context->Support) + FreeClrProcessSupport(context->Support); + +#if _WIN64 + if (context->ConnectedToPhSvc) + { + PhUiDisconnectFromPhSvc(); + context->ConnectedToPhSvc = FALSE; + } +#endif +} + +VOID ThreadTreeNewInitializing( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PPH_THREADS_CONTEXT threadsContext; + PTHREAD_TREE_CONTEXT context; + BOOLEAN isDotNet; + + threadsContext = info->SystemContext; + context = PhPluginGetObjectExtension(PluginInstance, threadsContext, EmThreadsContextType); + + if (NT_SUCCESS(PhGetProcessIsDotNet(threadsContext->Provider->ProcessId, &isDotNet)) && isDotNet) + { +#if _WIN64 + HANDLE processHandle; + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, threadsContext->Provider->ProcessId))) + { + PhGetProcessIsWow64(processHandle, &context->IsWow64); + NtClose(processHandle); + } + + if (context->IsWow64) + { + context->ConnectedToPhSvc = PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE); + } + else +#endif + { + PCLR_PROCESS_SUPPORT support; + + support = CreateClrProcessSupport(threadsContext->Provider->ProcessId); + + if (!support) + return; + + context->Support = support; + } + + AddTreeNewColumn(info, context, DNTHTNC_APPDOMAIN, TRUE, L"AppDomain", 120, PH_ALIGN_LEFT, 0, FALSE, ThreadTreeNewSortFunction); + } +} + +VOID ThreadTreeNewUninitializing( + _In_ PVOID Parameter + ) +{ + NOTHING; +} + +VOID UpdateThreadClrData( + _In_ PTHREAD_TREE_CONTEXT Context, + _Inout_ PDN_THREAD_ITEM DnThread + ) +{ + if (!DnThread->ClrDataValid) + { + PhClearReference(&DnThread->AppDomainText); + +#if _WIN64 + if (Context->IsWow64) + { + if (Context->ConnectedToPhSvc) + { + DnThread->AppDomainText = CallGetClrThreadAppDomain(Context->ProcessId, DnThread->ThreadItem->ThreadId); + } + } + else +#endif + { + if (Context->Support) + { + IXCLRDataProcess *process; + IXCLRDataTask *task; + IXCLRDataAppDomain *appDomain; + + process = Context->Support->DataProcess; + + if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID(process, HandleToUlong(DnThread->ThreadItem->ThreadId), &task))) + { + if (SUCCEEDED(IXCLRDataTask_GetCurrentAppDomain(task, &appDomain))) + { + DnThread->AppDomainText = GetNameXClrDataAppDomain(appDomain); + IXCLRDataAppDomain_Release(appDomain); + } + + IXCLRDataTask_Release(task); + } + } + } + + DnThread->ClrDataValid = TRUE; + } +} + +VOID ThreadTreeNewMessage( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + PTHREAD_TREE_CONTEXT context = message->Context; + + if (message->Message == TreeNewGetCellText) + { + PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; + PPH_THREAD_NODE threadNode = (PPH_THREAD_NODE)getCellText->Node; + PDN_THREAD_ITEM dnThread; + + dnThread = PhPluginGetObjectExtension(PluginInstance, threadNode->ThreadItem, EmThreadItemType); + + switch (message->SubId) + { + case DNTHTNC_APPDOMAIN: + UpdateThreadClrData(context, dnThread); + getCellText->Text = PhGetStringRef(dnThread->AppDomainText); + break; + } + } +} + +LONG ThreadTreeNewSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PTHREAD_TREE_CONTEXT context = Context; + LONG result; + PPH_THREAD_NODE node1 = Node1; + PPH_THREAD_NODE node2 = Node2; + PDN_THREAD_ITEM dnThread1; + PDN_THREAD_ITEM dnThread2; + + dnThread1 = PhPluginGetObjectExtension(PluginInstance, node1->ThreadItem, EmThreadItemType); + dnThread2 = PhPluginGetObjectExtension(PluginInstance, node2->ThreadItem, EmThreadItemType); + + result = 0; + + switch (SubId) + { + case DNTHTNC_APPDOMAIN: + UpdateThreadClrData(context, dnThread1); + UpdateThreadClrData(context, dnThread2); + result = PhCompareStringWithNullSortOrder(dnThread1->AppDomainText, dnThread2->AppDomainText, SortOrder, TRUE); + break; + } + + return result; +} diff --git a/plugins/ExtendedNotifications/CHANGELOG.txt b/plugins/ExtendedNotifications/CHANGELOG.txt index 7aa9f64a6466..6ac5c90393ec 100644 --- a/plugins/ExtendedNotifications/CHANGELOG.txt +++ b/plugins/ExtendedNotifications/CHANGELOG.txt @@ -1,11 +1,11 @@ -1.3 - * Added Growl support - -1.2 - * Added ability to log events to a file - -1.1 - * Fixed memory leak - -1.0 - * Initial release +1.3 + * Added Growl support + +1.2 + * Added ability to log events to a file + +1.1 + * Fixed memory leak + +1.0 + * Initial release diff --git a/plugins/ExtendedNotifications/ExtendedNotifications.rc b/plugins/ExtendedNotifications/ExtendedNotifications.rc index 63a61c3bfdc9..0c7e73170777 100644 --- a/plugins/ExtendedNotifications/ExtendedNotifications.rc +++ b/plugins/ExtendedNotifications/ExtendedNotifications.rc @@ -1,214 +1,214 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,0,0 - PRODUCTVERSION 1,3,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "Extended Notifications for Process Hacker" - VALUE "FileVersion", "1.3" - VALUE "InternalName", "ProcessHacker.ExtendedNotifications" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ExtendedNotifications.dll" - VALUE "ProductName", "Extended Notifications for Process Hacker" - VALUE "ProductVersion", "1.3" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_PROCESSES DIALOGEX 0, 0, 255, 229 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Processes" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "You can configure processes for which notifications are displayed. Wildcards can be used, and ordering is considered.",IDC_STATIC,7,7,241,19 - LISTBOX IDC_LIST,7,30,187,118,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Move up",IDC_MOVEUP,198,30,50,14 - PUSHBUTTON "Move down",IDC_MOVEDOWN,198,47,50,14 - EDITTEXT IDC_TEXT,7,152,147,12,ES_AUTOHSCROLL - CONTROL "Include",IDC_INCLUDE,"Button",BS_AUTORADIOBUTTON,163,153,39,10 - CONTROL "Exclude",IDC_EXCLUDE,"Button",BS_AUTORADIOBUTTON,207,153,41,10 - PUSHBUTTON "Add/Update",IDC_ADD,145,168,50,14 - PUSHBUTTON "Remove",IDC_REMOVE,198,168,50,14 - LTEXT "Examples:\nnote*.exe\nC:\\Windows\\system32\\cmd.exe\nC:\\Windows\\*",IDC_STATIC,7,186,241,36 -END - -IDD_SERVICES DIALOGEX 0, 0, 255, 229 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Services" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "You can configure services for which notifications are displayed. Wildcards can be used, and ordering is considered.",IDC_STATIC,7,7,241,19 - LISTBOX IDC_LIST,7,30,187,118,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Move up",IDC_MOVEUP,198,30,50,14 - PUSHBUTTON "Move down",IDC_MOVEDOWN,198,47,50,14 - EDITTEXT IDC_TEXT,7,152,147,12,ES_AUTOHSCROLL - CONTROL "Include",IDC_INCLUDE,"Button",BS_AUTORADIOBUTTON,163,153,39,10 - CONTROL "Exclude",IDC_EXCLUDE,"Button",BS_AUTORADIOBUTTON,207,153,41,10 - PUSHBUTTON "Add/Update",IDC_ADD,145,168,50,14 - PUSHBUTTON "Remove",IDC_REMOVE,198,168,50,14 - LTEXT "Examples:\nWdi*\nseclogon",IDC_STATIC,7,186,241,36 -END - -IDD_LOGGING DIALOGEX 0, 0, 255, 229 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Logging" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "File",IDC_STATIC,7,7,241,53 - LTEXT "Log all events to this file (leave blank to disable this feature):",IDC_STATIC,13,18,196,8 - EDITTEXT IDC_LOGFILENAME,13,29,178,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,194,28,50,14 - LTEXT "Changes will require a restart of Process Hacker.",IDC_STATIC,14,45,157,8 -END - -IDD_GROWL DIALOGEX 0, 0, 255, 229 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Growl" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Send notifications to Growl",IDC_ENABLEGROWL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,101,10 - LTEXT "gntp-send license:",IDC_STATIC,7,23,60,8 - EDITTEXT IDC_LICENSE,7,34,241,188,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_PROCESSES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 248 - TOPMARGIN, 7 - BOTTOMMARGIN, 222 - END - - IDD_SERVICES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 248 - TOPMARGIN, 7 - BOTTOMMARGIN, 222 - END - - IDD_LOGGING, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 248 - TOPMARGIN, 7 - BOTTOMMARGIN, 222 - END - - IDD_GROWL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 248 - TOPMARGIN, 7 - BOTTOMMARGIN, 222 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_GROWL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,3,0,0 + PRODUCTVERSION 1,3,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "wj32" + VALUE "FileDescription", "Extended Notifications for Process Hacker" + VALUE "FileVersion", "1.3" + VALUE "InternalName", "ProcessHacker.ExtendedNotifications" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "ExtendedNotifications.dll" + VALUE "ProductName", "Extended Notifications for Process Hacker" + VALUE "ProductVersion", "1.3" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROCESSES DIALOGEX 0, 0, 255, 229 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Processes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "You can configure processes for which notifications are displayed. Wildcards can be used, and ordering is considered.",IDC_STATIC,7,7,241,19 + LISTBOX IDC_LIST,7,30,187,118,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Move up",IDC_MOVEUP,198,30,50,14 + PUSHBUTTON "Move down",IDC_MOVEDOWN,198,47,50,14 + EDITTEXT IDC_TEXT,7,152,147,12,ES_AUTOHSCROLL + CONTROL "Include",IDC_INCLUDE,"Button",BS_AUTORADIOBUTTON,163,153,39,10 + CONTROL "Exclude",IDC_EXCLUDE,"Button",BS_AUTORADIOBUTTON,207,153,41,10 + PUSHBUTTON "Add/Update",IDC_ADD,145,168,50,14 + PUSHBUTTON "Remove",IDC_REMOVE,198,168,50,14 + LTEXT "Examples:\nnote*.exe\nC:\\Windows\\system32\\cmd.exe\nC:\\Windows\\*",IDC_INFO,7,186,241,36 +END + +IDD_SERVICES DIALOGEX 0, 0, 255, 229 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Services" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "You can configure services for which notifications are displayed. Wildcards can be used, and ordering is considered.",IDC_STATIC,7,7,241,19 + LISTBOX IDC_LIST,7,30,187,118,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Move up",IDC_MOVEUP,198,30,50,14 + PUSHBUTTON "Move down",IDC_MOVEDOWN,198,47,50,14 + EDITTEXT IDC_TEXT,7,152,147,12,ES_AUTOHSCROLL + CONTROL "Include",IDC_INCLUDE,"Button",BS_AUTORADIOBUTTON,163,153,39,10 + CONTROL "Exclude",IDC_EXCLUDE,"Button",BS_AUTORADIOBUTTON,207,153,41,10 + PUSHBUTTON "Add/Update",IDC_ADD,145,168,50,14 + PUSHBUTTON "Remove",IDC_REMOVE,198,168,50,14 + LTEXT "Examples:\nWdi*\nseclogon",IDC_INFO,7,186,241,36 +END + +IDD_LOGGING DIALOGEX 0, 0, 255, 69 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Logging" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "File",IDC_INFO,7,7,241,53 + LTEXT "Log all events to this file (leave blank to disable this feature):",IDC_STATIC,13,18,196,8 + EDITTEXT IDC_LOGFILENAME,13,29,178,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,194,28,50,14 + LTEXT "Changes will require a restart of Process Hacker.",IDC_STATIC,14,45,157,8 +END + +IDD_GROWL DIALOGEX 0, 0, 255, 229 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Growl" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Send notifications to Growl",IDC_ENABLEGROWL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,101,10 + LTEXT "gntp-send license:",IDC_STATIC,7,23,60,8 + EDITTEXT IDC_LICENSE,7,34,241,188,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PROCESSES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 222 + END + + IDD_SERVICES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 222 + END + + IDD_LOGGING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 62 + END + + IDD_GROWL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 222 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_GROWL AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj b/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj index 7ad9d1b93021..d8d8c7d64ed8 100644 --- a/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj +++ b/plugins/ExtendedNotifications/ExtendedNotifications.vcxproj @@ -1,99 +1,99 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {80E791B8-AC98-407E-8FF9-5154AF50E887} - ExtendedNotifications - Win32Proj - ExtendedNotifications - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - ws2_32.lib;%(AdditionalDependencies) - ws2_32.dll;%(DelayLoadDLLs) - - - - - ws2_32.lib;%(AdditionalDependencies) - ws2_32.dll;%(DelayLoadDLLs) - - - - - ws2_32.lib;%(AdditionalDependencies) - ws2_32.dll;%(DelayLoadDLLs) - - - - - ws2_32.lib;%(AdditionalDependencies) - ws2_32.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {80E791B8-AC98-407E-8FF9-5154AF50E887} + ExtendedNotifications + Win32Proj + ExtendedNotifications + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + ws2_32.lib;%(AdditionalDependencies) + user32.dll;ws2_32.dll;%(DelayLoadDLLs) + + + + + ws2_32.lib;%(AdditionalDependencies) + user32.dll;ws2_32.dll;%(DelayLoadDLLs) + + + + + ws2_32.lib;%(AdditionalDependencies) + user32.dll;ws2_32.dll;%(DelayLoadDLLs) + + + + + ws2_32.lib;%(AdditionalDependencies) + user32.dll;ws2_32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ExtendedNotifications/extnoti.h b/plugins/ExtendedNotifications/extnoti.h index d3661c27ff34..1f72045458ca 100644 --- a/plugins/ExtendedNotifications/extnoti.h +++ b/plugins/ExtendedNotifications/extnoti.h @@ -1,30 +1,30 @@ -#ifndef EXTNOTI_H -#define EXTNOTI_H - -#define PLUGIN_NAME L"ProcessHacker.ExtendedNotifications" -#define SETTING_NAME_ENABLE_GROWL (PLUGIN_NAME L".EnableGrowl") -#define SETTING_NAME_LOG_FILENAME (PLUGIN_NAME L".LogFileName") -#define SETTING_NAME_PROCESS_LIST (PLUGIN_NAME L".ProcessList") -#define SETTING_NAME_SERVICE_LIST (PLUGIN_NAME L".ServiceList") - -// main - -typedef enum _FILTER_TYPE -{ - FilterInclude, - FilterExclude -} FILTER_TYPE; - -typedef struct _FILTER_ENTRY -{ - FILTER_TYPE Type; - PPH_STRING Filter; -} FILTER_ENTRY, *PFILTER_ENTRY; - -// filelog - -VOID FileLogInitialization( - VOID - ); - -#endif +#ifndef EXTNOTI_H +#define EXTNOTI_H + +#define PLUGIN_NAME L"ProcessHacker.ExtendedNotifications" +#define SETTING_NAME_ENABLE_GROWL (PLUGIN_NAME L".EnableGrowl") +#define SETTING_NAME_LOG_FILENAME (PLUGIN_NAME L".LogFileName") +#define SETTING_NAME_PROCESS_LIST (PLUGIN_NAME L".ProcessList") +#define SETTING_NAME_SERVICE_LIST (PLUGIN_NAME L".ServiceList") + +// main + +typedef enum _FILTER_TYPE +{ + FilterInclude, + FilterExclude +} FILTER_TYPE; + +typedef struct _FILTER_ENTRY +{ + FILTER_TYPE Type; + PPH_STRING Filter; +} FILTER_ENTRY, *PFILTER_ENTRY; + +// filelog + +VOID FileLogInitialization( + VOID + ); + +#endif diff --git a/plugins/ExtendedNotifications/filelog.c b/plugins/ExtendedNotifications/filelog.c index b77492940100..8b98c8672560 100644 --- a/plugins/ExtendedNotifications/filelog.c +++ b/plugins/ExtendedNotifications/filelog.c @@ -1,79 +1,80 @@ -/* - * Process Hacker Extended Notifications - - * file logging - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include "extnoti.h" - -VOID NTAPI LoggedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -PPH_FILE_STREAM LogFileStream = NULL; -PH_CALLBACK_REGISTRATION LoggedCallbackRegistration; - -VOID FileLogInitialization( - VOID - ) -{ - NTSTATUS status; - PPH_STRING fileName; - - fileName = PhaGetStringSetting(SETTING_NAME_LOG_FILENAME); - - if (fileName->Length != 0) - { - status = PhCreateFileStream( - &LogFileStream, - fileName->Buffer, - FILE_GENERIC_WRITE, - FILE_SHARE_READ, - FILE_OPEN_IF, - PH_FILE_STREAM_APPEND | PH_FILE_STREAM_UNBUFFERED - ); - - if (NT_SUCCESS(status)) - { - PhRegisterCallback( - &PhLoggedCallback, - LoggedCallback, - NULL, - &LoggedCallbackRegistration - ); - } - } -} - -VOID NTAPI LoggedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_LOG_ENTRY logEntry = Parameter; - - PhWriteStringFormatAsUtf8FileStream( - LogFileStream, - L"%s: %s\r\n", - PhaFormatDateTime(NULL)->Buffer, - PH_AUTO_T(PH_STRING, PhFormatLogEntry(logEntry))->Buffer - ); -} +/* + * Process Hacker Extended Notifications - + * file logging + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include "extnoti.h" + +VOID NTAPI LoggedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +PPH_FILE_STREAM LogFileStream = NULL; +PH_CALLBACK_REGISTRATION LoggedCallbackRegistration; + +VOID FileLogInitialization( + VOID + ) +{ + NTSTATUS status; + PPH_STRING fileName; + + fileName = PhaGetStringSetting(SETTING_NAME_LOG_FILENAME); + + if (!PhIsNullOrEmptyString(fileName)) + { + status = PhCreateFileStream( + &LogFileStream, + fileName->Buffer, + FILE_GENERIC_WRITE, + FILE_SHARE_READ, + FILE_OPEN_IF, + PH_FILE_STREAM_APPEND | PH_FILE_STREAM_UNBUFFERED + ); + + if (NT_SUCCESS(status)) + { + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackLoggedEvent), + LoggedCallback, + NULL, + &LoggedCallbackRegistration + ); + } + } +} + +VOID NTAPI LoggedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_LOG_ENTRY logEntry = Parameter; + + PhWriteStringFormatAsUtf8FileStream( + LogFileStream, + L"%s: %s\r\n", + PhaFormatDateTime(NULL)->Buffer, + PH_AUTO_T(PH_STRING, PhFormatLogEntry(logEntry))->Buffer + ); +} diff --git a/plugins/ExtendedNotifications/gntp-send/LICENSE.txt b/plugins/ExtendedNotifications/gntp-send/LICENSE.txt index 40b0fa4c4916..a7e6a122d3be 100644 --- a/plugins/ExtendedNotifications/gntp-send/LICENSE.txt +++ b/plugins/ExtendedNotifications/gntp-send/LICENSE.txt @@ -1,26 +1,26 @@ -[The "BSD licence"] -Copyright (c) 2009-2010 Yasuhiro Matsumoto -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +[The "BSD licence"] +Copyright (c) 2009-2010 Yasuhiro Matsumoto +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/plugins/ExtendedNotifications/gntp-send/growl.c b/plugins/ExtendedNotifications/gntp-send/growl.c index 86858a5b9b0f..4812ddaf061f 100644 --- a/plugins/ExtendedNotifications/gntp-send/growl.c +++ b/plugins/ExtendedNotifications/gntp-send/growl.c @@ -1,537 +1,537 @@ -#define _CRT_RAND_S -#include -#include -#include -#include -#include -#include - -#include "md5.h" -#include "tcp.h" -#include "growl.h" - -static const char hex_table[] = "0123456789ABCDEF"; -static char* string_to_hex_alloc(const char* str, int len) { - int n, l; - char* tmp = (char*)PhAllocateSafe(len * 2 + 1); - if (tmp) { - memset(tmp, 0, len * 2 + 1); - for (l = 0, n = 0; l < len; l++) { - tmp[n++] = hex_table[(str[l] & 0xF0) >> 4]; - tmp[n++] = hex_table[str[l] & 0x0F]; - } - } - return tmp; -} - -int growl_init_ = 0; - -/* dmex: modified to use latest Winsock version */ -int growl_init() -{ - if (growl_init_ == 0) - { -#ifdef _WIN32 - WSADATA wsaData; - if (WSAStartup(WINSOCK_VERSION, &wsaData) != 0) - { - return -1; - } -#endif - - growl_init_ = 1; - } - return 1; -} - - -void growl_shutdown() -{ - if (growl_init_ == 1) - { -#ifdef _WIN32 - WSACleanup(); -#endif - } -} - -/* dmex: modified with enhancements */ -PPH_BYTES gen_salt_alloc(int count) -{ - PPH_STRING saltString; - PPH_BYTES saltBytes; - - saltString = PhCreateStringEx(NULL, count * 2 + 2); - PhGenerateRandomAlphaString(saltString->Buffer, (ULONG)saltString->Length / sizeof(WCHAR)); - saltBytes = PhConvertUtf16ToUtf8(saltString->Buffer); - PhDereferenceObject(saltString); - - return saltBytes; -} - -char* gen_password_hash_alloc(const char* password, const char* salt) { - md5_context md5ctx; - char md5tmp[20]; - char* md5digest; - - memset(md5tmp, 0, sizeof(md5tmp)); - md5_starts(&md5ctx); - md5_update(&md5ctx, (uint8_t*)password, (uint32_t)strlen(password)); - md5_update(&md5ctx, (uint8_t*)salt, (uint32_t)strlen(salt)); - md5_finish(&md5ctx, (uint8_t*)md5tmp); - - md5_starts(&md5ctx); - md5_update(&md5ctx, (uint8_t*)md5tmp, 16); - md5_finish(&md5ctx, (uint8_t*)md5tmp); - md5digest = string_to_hex_alloc(md5tmp, 16); - - return md5digest; -} - -char *growl_generate_authheader_alloc(const char*const password) -{ - PPH_BYTES salt; - char* salthash; - char* keyhash; - char* authheader = NULL; - - if (password) { - salt = gen_salt_alloc(8); - if (salt) { - keyhash = gen_password_hash_alloc(password, salt->Buffer); - if (keyhash) { - salthash = string_to_hex_alloc(salt->Buffer, 8); - if (salthash) { - authheader = (char*)PhAllocateSafe(strlen(keyhash) + strlen(salthash) + 7); - if (authheader) { - sprintf(authheader, " MD5:%s.%s", keyhash, salthash); - } - PhFree(salthash); - } - PhFree(keyhash); - } - PhDereferenceObject(salt); - } - } - - return authheader; -} - -/* dmex: modified to use INVALID_SOCKET */ -int growl_tcp_register( const char *const server , const char *const appname , const char **const notifications , const int notifications_count , - const char *const password, const char* const icon ) -{ - SOCKET sock = INVALID_SOCKET; - int i=0; - char *authheader; - char *iconid = NULL; - FILE *iconfile = NULL; - size_t iconsize; - uint8_t buffer[1024]; - - growl_init(); - authheader = growl_generate_authheader_alloc(password); - sock = growl_tcp_open(server); - if (sock == INVALID_SOCKET) goto leave; - if (icon) { - size_t bytes_read; - md5_context md5ctx; - char md5tmp[20]; - iconfile = fopen(icon, "rb"); - if (iconfile) { - fseek(iconfile, 0, SEEK_END); - iconsize = ftell(iconfile); - fseek(iconfile, 0, SEEK_SET); - memset(md5tmp, 0, sizeof(md5tmp)); - md5_starts(&md5ctx); - while (!feof(iconfile)) { - bytes_read = fread(buffer, 1, 1024, iconfile); - if (bytes_read) md5_update(&md5ctx, buffer, (uint32_t)bytes_read); - } - fseek(iconfile, 0, SEEK_SET); - md5_finish(&md5ctx, md5tmp); - iconid = string_to_hex_alloc(md5tmp, 16); - } - } - - growl_tcp_write(sock, "GNTP/1.0 REGISTER NONE %s", authheader ? authheader : ""); - growl_tcp_write(sock, "Application-Name: %s", appname); - if(iconid) - { - growl_tcp_write(sock, "Application-Icon: x-growl-resource://%s", iconid); - } - else if(icon) - { - growl_tcp_write(sock, "Application-Icon: %s", icon ); - } - growl_tcp_write(sock, "Notifications-Count: %d", notifications_count); - growl_tcp_write(sock, "" ); - - for(i=0;i +#include +#include +#include +#include +#include + +#include "md5.h" +#include "tcp.h" +#include "growl.h" + +static const char hex_table[] = "0123456789ABCDEF"; +static char* string_to_hex_alloc(const char* str, int len) { + int n, l; + char* tmp = (char*)PhAllocateSafe(len * 2 + 1); + if (tmp) { + memset(tmp, 0, len * 2 + 1); + for (l = 0, n = 0; l < len; l++) { + tmp[n++] = hex_table[(str[l] & 0xF0) >> 4]; + tmp[n++] = hex_table[str[l] & 0x0F]; + } + } + return tmp; +} + +int growl_init_ = 0; + +/* dmex: modified to use latest Winsock version */ +int growl_init() +{ + if (growl_init_ == 0) + { +#ifdef _WIN32 + WSADATA wsaData; + if (WSAStartup(WINSOCK_VERSION, &wsaData) != 0) + { + return -1; + } +#endif + + growl_init_ = 1; + } + return 1; +} + + +void growl_shutdown() +{ + if (growl_init_ == 1) + { +#ifdef _WIN32 + WSACleanup(); +#endif + } +} + +/* dmex: modified with enhancements */ +PPH_BYTES gen_salt_alloc(int count) +{ + PPH_STRING saltString; + PPH_BYTES saltBytes; + + saltString = PhCreateStringEx(NULL, count * 2 + 2); + PhGenerateRandomAlphaString(saltString->Buffer, (ULONG)saltString->Length / sizeof(WCHAR)); + saltBytes = PhConvertUtf16ToUtf8(saltString->Buffer); + PhDereferenceObject(saltString); + + return saltBytes; +} + +char* gen_password_hash_alloc(const char* password, const char* salt) { + md5_context md5ctx; + char md5tmp[20]; + char* md5digest; + + memset(md5tmp, 0, sizeof(md5tmp)); + md5_starts(&md5ctx); + md5_update(&md5ctx, (uint8_t*)password, (uint32_t)strlen(password)); + md5_update(&md5ctx, (uint8_t*)salt, (uint32_t)strlen(salt)); + md5_finish(&md5ctx, (uint8_t*)md5tmp); + + md5_starts(&md5ctx); + md5_update(&md5ctx, (uint8_t*)md5tmp, 16); + md5_finish(&md5ctx, (uint8_t*)md5tmp); + md5digest = string_to_hex_alloc(md5tmp, 16); + + return md5digest; +} + +char *growl_generate_authheader_alloc(const char*const password) +{ + PPH_BYTES salt; + char* salthash; + char* keyhash; + char* authheader = NULL; + + if (password) { + salt = gen_salt_alloc(8); + if (salt) { + keyhash = gen_password_hash_alloc(password, salt->Buffer); + if (keyhash) { + salthash = string_to_hex_alloc(salt->Buffer, 8); + if (salthash) { + authheader = (char*)PhAllocateSafe(strlen(keyhash) + strlen(salthash) + 7); + if (authheader) { + sprintf(authheader, " MD5:%s.%s", keyhash, salthash); + } + PhFree(salthash); + } + PhFree(keyhash); + } + PhDereferenceObject(salt); + } + } + + return authheader; +} + +/* dmex: modified to use INVALID_SOCKET */ +int growl_tcp_register( const char *const server , const char *const appname , const char **const notifications , const int notifications_count , + const char *const password, const char* const icon ) +{ + SOCKET sock = INVALID_SOCKET; + int i=0; + char *authheader; + char *iconid = NULL; + FILE *iconfile = NULL; + size_t iconsize; + uint8_t buffer[1024]; + + growl_init(); + authheader = growl_generate_authheader_alloc(password); + sock = growl_tcp_open(server); + if (sock == INVALID_SOCKET) goto leave; + if (icon) { + size_t bytes_read; + md5_context md5ctx; + char md5tmp[20]; + iconfile = fopen(icon, "rb"); + if (iconfile) { + fseek(iconfile, 0, SEEK_END); + iconsize = ftell(iconfile); + fseek(iconfile, 0, SEEK_SET); + memset(md5tmp, 0, sizeof(md5tmp)); + md5_starts(&md5ctx); + while (!feof(iconfile)) { + bytes_read = fread(buffer, 1, 1024, iconfile); + if (bytes_read) md5_update(&md5ctx, buffer, (uint32_t)bytes_read); + } + fseek(iconfile, 0, SEEK_SET); + md5_finish(&md5ctx, md5tmp); + iconid = string_to_hex_alloc(md5tmp, 16); + } + } + + growl_tcp_write(sock, "GNTP/1.0 REGISTER NONE %s", authheader ? authheader : ""); + growl_tcp_write(sock, "Application-Name: %s", appname); + if(iconid) + { + growl_tcp_write(sock, "Application-Icon: x-growl-resource://%s", iconid); + } + else if(icon) + { + growl_tcp_write(sock, "Application-Icon: %s", icon ); + } + growl_tcp_write(sock, "Notifications-Count: %d", notifications_count); + growl_tcp_write(sock, "" ); + + for(i=0;i - -typedef struct { - PH_HASH_CONTEXT hc; -} md5_context; - -__forceinline void md5_starts(md5_context *ctx) -{ - PhInitializeHash(&ctx->hc, Md5HashAlgorithm); -} - -__forceinline void md5_update(md5_context *ctx, const uint8_t *input, uint32_t length) -{ - PhUpdateHash(&ctx->hc, (PVOID)input, length); -} - -__forceinline void md5_finish(md5_context *ctx, uint8_t digest[16]) -{ - if (!PhFinalHash(&ctx->hc, digest, 16, NULL)) - PhRaiseStatus(STATUS_INTERNAL_ERROR); -} - -#endif /* _MD5_H_ */ +#ifndef _MD5_H_ +#define _MD5_H_ + +#include + +typedef struct { + PH_HASH_CONTEXT hc; +} md5_context; + +__forceinline void md5_starts(md5_context *ctx) +{ + PhInitializeHash(&ctx->hc, Md5HashAlgorithm); +} + +__forceinline void md5_update(md5_context *ctx, const uint8_t *input, uint32_t length) +{ + PhUpdateHash(&ctx->hc, (PVOID)input, length); +} + +__forceinline void md5_finish(md5_context *ctx, uint8_t digest[16]) +{ + if (!PhFinalHash(&ctx->hc, digest, 16, NULL)) + PhRaiseStatus(STATUS_INTERNAL_ERROR); +} + +#endif /* _MD5_H_ */ diff --git a/plugins/ExtendedNotifications/gntp-send/tcp.c b/plugins/ExtendedNotifications/gntp-send/tcp.c index 8340ddc3397d..dc9d7377b7fa 100644 --- a/plugins/ExtendedNotifications/gntp-send/tcp.c +++ b/plugins/ExtendedNotifications/gntp-send/tcp.c @@ -1,185 +1,197 @@ -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "tcp.h" - -int growl_tcp_parse_hostname( const char *const server , int default_port , struct sockaddr_in *const sockaddr ); - -void growl_tcp_write_raw( SOCKET sock, const unsigned char * data, const int data_length ) -{ - send(sock, data, data_length, 0); -} - -void growl_tcp_write( SOCKET sock , const char *const format , ... ) -{ - int length; - char *output; - char *stop; - - va_list ap; - - va_start( ap , format ); - length = vsnprintf( NULL , 0 , format , ap ); - va_end(ap); - - va_start(ap,format); - output = (char*)PhAllocateSafe(length+1); - if (!output) { - va_end(ap); - return; - } - vsnprintf( output , length+1 , format , ap ); - va_end(ap); - - while ((stop = strstr(output, "\r\n"))) strcpy(stop, stop + 1); - - send( sock , output , length , 0 ); - send( sock , "\r\n" , 2 , 0 ); - - PhFree(output); -} - -char *growl_tcp_read(SOCKET sock) { - const int growsize = 80; - char c = 0; - char* line = (char*) PhAllocateSafe(growsize); - if (line) { - int len = growsize, pos = 0; - char* newline; - while (line) { - if (recv(sock, &c, 1, 0) <= 0) break; - if (c == '\r') continue; - if (c == '\n') break; - line[pos++] = c; - if (pos >= len) { - len += growsize; - newline = (char*) realloc(line, len); - if (!newline) { - PhFree(line); - return NULL; - } - line = newline; - } - } - line[pos] = 0; - } - return line; -} - -/* dmex: modified to use INVALID_SOCKET and SOCKET_ERROR */ -SOCKET growl_tcp_open(const char* server) { - SOCKET sock = INVALID_SOCKET; -#ifdef _WIN32 - char on; -#else - int on; -#endif - struct sockaddr_in serv_addr; - - if( growl_tcp_parse_hostname( server , 23053 , &serv_addr ) == -1 ) { - return INVALID_SOCKET; - } - - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - perror("create socket"); - return INVALID_SOCKET; - } - - if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { - perror("connect"); - closesocket(sock); // dmex: fixed handle leaking on error - return INVALID_SOCKET; - } - - on = 1; - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) { - perror("setsockopt"); - closesocket(sock); // dmex: fixed handle leaking on error - return INVALID_SOCKET; - } - - return sock; -} - -/* dmex: modified to use INVALID_SOCKET */ -void growl_tcp_close(SOCKET sock) { -#ifdef _WIN32 - if (sock != INVALID_SOCKET) closesocket(sock); -#else - if (sock > 0) close(sock); -#endif -} - -int growl_tcp_parse_hostname( const char *const server , int default_port , struct sockaddr_in *const sockaddr ) -{ - char *hostname = PhDuplicateBytesZSafe((PSTR)server); - char *port = strchr( hostname, ':' ); - struct hostent* host_ent; - if( port != NULL ) - { - *port = '\0'; - port++; - default_port = atoi(port); - } - - host_ent = gethostbyname(hostname); - if( host_ent == NULL ) - { - perror("gethostbyname"); - PhFree(hostname); - return -1; - } - - // dmex: fixed wrong sizeof argument - memset( sockaddr , 0 , sizeof(struct sockaddr_in) ); - sockaddr->sin_family = AF_INET; - memcpy( &sockaddr->sin_addr , host_ent->h_addr , host_ent->h_length ); - sockaddr->sin_port = htons(default_port); - - PhFree(hostname); - return 0; -} - -int growl_tcp_datagram( const char *server , const unsigned char *data , const int data_length ) -{ - int result; - struct sockaddr_in serv_addr; - SOCKET sock = 0; - - if( growl_tcp_parse_hostname( server , 9887 , &serv_addr ) == -1 ) - { - return -1; - } - - sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if( sock == INVALID_SOCKET ) - { - return -1; - } - - if( sendto(sock, (char*)data , data_length , 0 , (struct sockaddr*)&serv_addr , sizeof(serv_addr) ) > 0 ) - { - result = 0; - } - else - { - result = 1; - } - - closesocket(sock); - return result; -} +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#else +#include +#include +#include +#include +#include +#endif + +#include "tcp.h" + +int growl_tcp_parse_hostname( const char *const server , int default_port , struct sockaddr_in *const sockaddr ); + +void growl_tcp_write_raw( SOCKET sock, const unsigned char * data, const int data_length ) +{ + send(sock, data, data_length, 0); +} + +void growl_tcp_write( SOCKET sock , const char *const format , ... ) +{ + int length; + char *output; + char *stop; + + va_list ap; + + va_start( ap , format ); + length = vsnprintf( NULL , 0 , format , ap ); + va_end(ap); + + va_start(ap,format); + output = (char*)PhAllocateSafe(length+1); + if (!output) { + va_end(ap); + return; + } + vsnprintf( output , length+1 , format , ap ); + va_end(ap); + + while ((stop = strstr(output, "\r\n"))) strcpy(stop, stop + 1); + + send( sock , output , length , 0 ); + send( sock , "\r\n" , 2 , 0 ); + + PhFree(output); +} + +char *growl_tcp_read(SOCKET sock) { + const int growsize = 80; + char c = 0; + char* line = (char*) PhAllocateSafe(growsize); + if (line) { + int len = growsize, pos = 0; + char* newline; + while (line) { + if (recv(sock, &c, 1, 0) <= 0) break; + if (c == '\r') continue; + if (c == '\n') break; + line[pos++] = c; + if (pos >= len) { + len += growsize; + newline = (char*) realloc(line, len); + if (!newline) { + PhFree(line); + return NULL; + } + line = newline; + } + } + line[pos] = 0; + } + return line; +} + +/* dmex: modified to use INVALID_SOCKET and SOCKET_ERROR */ +SOCKET growl_tcp_open(const char* server) { + SOCKET sock = INVALID_SOCKET; +#ifdef _WIN32 + char on; +#else + int on; +#endif + struct sockaddr_in serv_addr; + + if( growl_tcp_parse_hostname( server , 23053 , &serv_addr ) == -1 ) { + return INVALID_SOCKET; + } + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + //perror("create socket"); + return INVALID_SOCKET; + } + + if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { + //perror("connect"); + closesocket(sock); // dmex: fixed handle leaking on error + return INVALID_SOCKET; + } + + on = 1; + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) { + //perror("setsockopt"); + closesocket(sock); // dmex: fixed handle leaking on error + return INVALID_SOCKET; + } + + return sock; +} + +/* dmex: modified to use INVALID_SOCKET */ +void growl_tcp_close(SOCKET sock) { +#ifdef _WIN32 + if (sock != INVALID_SOCKET) closesocket(sock); +#else + if (sock > 0) close(sock); +#endif +} + +/* dmex: modified to use getaddrinfo */ +int growl_tcp_parse_hostname( const char *const server , int default_port , struct sockaddr_in *const sockaddr ) +{ + struct addrinfo *result = NULL; + struct addrinfo hints = { 0 }; + char *hostname = PhDuplicateBytesZSafe((PSTR)server); + char *port = strchr( hostname, ':' ); + + if (port) + { + *port = '\0'; + port++; + default_port = atoi(port); + } + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (getaddrinfo(hostname, NULL, &hints, &result) != 0) + { + PhFree(hostname); + return -1; + } + + // dmex: Copy the first result. + memset(sockaddr, 0, sizeof(struct sockaddr_in)); + sockaddr->sin_family = result->ai_family; + sockaddr->sin_port = _byteswap_ushort(default_port); + memcpy_s( + &sockaddr->sin_addr, + sizeof(sockaddr->sin_addr), + &((struct sockaddr_in*)result->ai_addr)->sin_addr, + sizeof(((struct sockaddr_in*)result->ai_addr)->sin_addr) + ); + + freeaddrinfo(result); + + PhFree(hostname); + return 0; +} + +int growl_tcp_datagram( const char *server , const unsigned char *data , const int data_length ) +{ + int result; + struct sockaddr_in serv_addr; + SOCKET sock = 0; + + if( growl_tcp_parse_hostname( server , 9887 , &serv_addr ) == -1 ) + { + return -1; + } + + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if( sock == INVALID_SOCKET ) + { + return -1; + } + + if( sendto(sock, (char*)data , data_length , 0 , (struct sockaddr*)&serv_addr , sizeof(serv_addr) ) > 0 ) + { + result = 0; + } + else + { + result = 1; + } + + closesocket(sock); + return result; +} diff --git a/plugins/ExtendedNotifications/gntp-send/tcp.h b/plugins/ExtendedNotifications/gntp-send/tcp.h index eaf7f418fe3a..4e7e799fb9d1 100644 --- a/plugins/ExtendedNotifications/gntp-send/tcp.h +++ b/plugins/ExtendedNotifications/gntp-send/tcp.h @@ -1,14 +1,14 @@ -#ifndef _TCP_H_ -#define _TCP_H_ - -#ifdef _MSC_VER -#define __attribute__(x) -#endif -void growl_tcp_write_raw( SOCKET sock, const unsigned char * data, const int data_length ); -void growl_tcp_write( SOCKET sock , const char *const format , ... ) __attribute__ ((format (printf, 2, 3))); -char* growl_tcp_read(SOCKET sock); -SOCKET growl_tcp_open(const char* server); -void growl_tcp_close(SOCKET sock); -int growl_tcp_datagram( const char *server , const unsigned char *data , const int data_length ); - -#endif /* _TCP_H_ */ +#ifndef _TCP_H_ +#define _TCP_H_ + +#ifdef _MSC_VER +#define __attribute__(x) +#endif +void growl_tcp_write_raw( SOCKET sock, const unsigned char * data, const int data_length ); +void growl_tcp_write( SOCKET sock , const char *const format , ... ) __attribute__ ((format (printf, 2, 3))); +char* growl_tcp_read(SOCKET sock); +SOCKET growl_tcp_open(const char* server); +void growl_tcp_close(SOCKET sock); +int growl_tcp_datagram( const char *server , const unsigned char *data , const int data_length ); + +#endif /* _TCP_H_ */ diff --git a/plugins/ExtendedNotifications/main.c b/plugins/ExtendedNotifications/main.c index 34a0178a3e5f..a7b01133b27a 100644 --- a/plugins/ExtendedNotifications/main.c +++ b/plugins/ExtendedNotifications/main.c @@ -1,1112 +1,1097 @@ -/* - * Process Hacker Extended Notifications - - * main program - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include -#include "extnoti.h" -#include "resource.h" -#include "gntp-send/growl.h" - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI NotifyEventCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID RegisterGrowl( - _In_ BOOLEAN Force - ); - -VOID NotifyGrowl( - _In_ PPH_PLUGIN_NOTIFY_EVENT NotifyEvent - ); - -NTSTATUS NTAPI RegisterGrowlCallback( - _In_ PVOID Parameter - ); - -INT_PTR CALLBACK ProcessesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK ServicesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK LoggingDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK GrowlDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION NotifyEventCallbackRegistration; - -PPH_LIST ProcessFilterList; -PPH_LIST ServiceFilterList; - -PSTR GrowlNotifications[] = -{ - "Process Created", - "Process Terminated", - "Service Created", - "Service Deleted", - "Service Started", - "Service Stopped" -}; - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Extended Notifications"; - info->Author = L"wj32"; - info->Description = L"Filters notifications."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1112"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNotifyEvent), - NotifyEventCallback, - NULL, - &NotifyEventCallbackRegistration - ); - - { - static PH_SETTING_CREATE settings[] = - { - { IntegerSettingType, SETTING_NAME_ENABLE_GROWL, L"0" }, - { StringSettingType, SETTING_NAME_LOG_FILENAME, L"" }, - { StringSettingType, SETTING_NAME_PROCESS_LIST, L"\\i*" }, - { StringSettingType, SETTING_NAME_SERVICE_LIST, L"\\i*" } - }; - - PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); - } - - ProcessFilterList = PhCreateList(10); - ServiceFilterList = PhCreateList(10); - } - break; - } - - return TRUE; -} - -VOID FreeFilterEntry( - _In_ PFILTER_ENTRY Entry - ) -{ - PhDereferenceObject(Entry->Filter); - PhFree(Entry); -} - -VOID ClearFilterList( - _Inout_ PPH_LIST FilterList - ) -{ - ULONG i; - - for (i = 0; i < FilterList->Count; i++) - FreeFilterEntry(FilterList->Items[i]); - - PhClearList(FilterList); -} - -VOID CopyFilterList( - _Inout_ PPH_LIST Destination, - _In_ PPH_LIST Source - ) -{ - ULONG i; - - for (i = 0; i < Source->Count; i++) - { - PFILTER_ENTRY entry = Source->Items[i]; - PFILTER_ENTRY newEntry; - - newEntry = PhAllocate(sizeof(FILTER_ENTRY)); - newEntry->Type = entry->Type; - newEntry->Filter = entry->Filter; - PhReferenceObject(newEntry->Filter); - - PhAddItemList(Destination, newEntry); - } -} - -VOID LoadFilterList( - _Inout_ PPH_LIST FilterList, - _In_ PPH_STRING String - ) -{ - PH_STRING_BUILDER stringBuilder; - SIZE_T length; - SIZE_T i; - PFILTER_ENTRY entry; - - length = String->Length / 2; - PhInitializeStringBuilder(&stringBuilder, 20); - - entry = NULL; - - for (i = 0; i < length; i++) - { - if (String->Buffer[i] == '\\') - { - if (i != length - 1) - { - i++; - - switch (String->Buffer[i]) - { - case 'i': - case 'e': - if (entry) - { - entry->Filter = PhFinalStringBuilderString(&stringBuilder); - PhAddItemList(FilterList, entry); - PhInitializeStringBuilder(&stringBuilder, 20); - } - - entry = PhAllocate(sizeof(FILTER_ENTRY)); - entry->Type = String->Buffer[i] == 'i' ? FilterInclude : FilterExclude; - - break; - - default: - PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); - break; - } - } - else - { - // Trailing backslash. Just ignore it. - break; - } - } - else - { - PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); - } - } - - if (entry) - { - entry->Filter = PhFinalStringBuilderString(&stringBuilder); - PhAddItemList(FilterList, entry); - } - else - { - PhDeleteStringBuilder(&stringBuilder); - } -} - -PPH_STRING SaveFilterList( - _Inout_ PPH_LIST FilterList - ) -{ - PH_STRING_BUILDER stringBuilder; - SIZE_T i; - SIZE_T j; - WCHAR temp[2]; - - PhInitializeStringBuilder(&stringBuilder, 100); - - temp[0] = '\\'; - - for (i = 0; i < FilterList->Count; i++) - { - PFILTER_ENTRY entry = FilterList->Items[i]; - SIZE_T length; - - // Write the entry type. - - temp[1] = entry->Type == FilterInclude ? 'i' : 'e'; - - PhAppendStringBuilderEx(&stringBuilder, temp, 4); - - // Write the filter string. - - length = entry->Filter->Length / 2; - - for (j = 0; j < length; j++) - { - if (entry->Filter->Buffer[j] == '\\') // escape backslashes - { - temp[1] = entry->Filter->Buffer[j]; - PhAppendStringBuilderEx(&stringBuilder, temp, 4); - } - else - { - PhAppendCharStringBuilder(&stringBuilder, entry->Filter->Buffer[j]); - } - } - } - - return PhFinalStringBuilderString(&stringBuilder); -} - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - LoadFilterList(ProcessFilterList, PhaGetStringSetting(SETTING_NAME_PROCESS_LIST)); - LoadFilterList(ServiceFilterList, PhaGetStringSetting(SETTING_NAME_SERVICE_LIST)); - - FileLogInitialization(); - - if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) - { - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), RegisterGrowlCallback, NULL); - } -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[4]; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = (HWND)Parameter; - propSheetHeader.pszCaption = L"Extended Notifications"; - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - // Processes - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_PROCESSES); - propSheetPage.pfnDlgProc = ProcessesDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - // Services - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SERVICES); - propSheetPage.pfnDlgProc = ServicesDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - // Logging - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_LOGGING); - propSheetPage.pfnDlgProc = LoggingDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - // Growl - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_GROWL); - propSheetPage.pfnDlgProc = GrowlDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - PhModalPropertySheet(&propSheetHeader); -} - -BOOLEAN MatchFilterList( - _In_ PPH_LIST FilterList, - _In_ PPH_STRING String, - _Out_ FILTER_TYPE *FilterType - ) -{ - ULONG i; - BOOLEAN isFileName; - - isFileName = PhFindCharInString(String, 0, '\\') != -1; - - for (i = 0; i < FilterList->Count; i++) - { - PFILTER_ENTRY entry = FilterList->Items[i]; - - if (isFileName && PhFindCharInString(entry->Filter, 0, '\\') == -1) - continue; // ignore filters without backslashes if we're matching a file name - - if (entry->Filter->Length == 2 && entry->Filter->Buffer[0] == '*') // shortcut - { - *FilterType = entry->Type; - return TRUE; - } - - if (PhMatchWildcards(entry->Filter->Buffer, String->Buffer, TRUE)) - { - *FilterType = entry->Type; - return TRUE; - } - } - - return FALSE; -} - -VOID NTAPI NotifyEventCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_NOTIFY_EVENT notifyEvent = Parameter; - PPH_PROCESS_ITEM processItem; - PPH_SERVICE_ITEM serviceItem; - FILTER_TYPE filterType; - BOOLEAN found = FALSE; - - filterType = FilterExclude; - - switch (notifyEvent->Type) - { - case PH_NOTIFY_PROCESS_CREATE: - case PH_NOTIFY_PROCESS_DELETE: - processItem = notifyEvent->Parameter; - - if (processItem->FileName) - found = MatchFilterList(ProcessFilterList, processItem->FileName, &filterType); - - if (!found) - MatchFilterList(ProcessFilterList, processItem->ProcessName, &filterType); - - break; - - case PH_NOTIFY_SERVICE_CREATE: - case PH_NOTIFY_SERVICE_DELETE: - case PH_NOTIFY_SERVICE_START: - case PH_NOTIFY_SERVICE_STOP: - serviceItem = notifyEvent->Parameter; - - MatchFilterList(ServiceFilterList, serviceItem->Name, &filterType); - - break; - } - - if (filterType == FilterExclude) - notifyEvent->Handled = TRUE; // pretend we handled the notification to prevent it from displaying - - if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) - NotifyGrowl(notifyEvent); -} - -VOID RegisterGrowl( - _In_ BOOLEAN Force - ) -{ - static BOOLEAN registered = FALSE; - - if (!Force && registered) - return; - - growl_tcp_register("127.0.0.1", "Process Hacker", GrowlNotifications, sizeof(GrowlNotifications) / sizeof(PSTR), NULL, NULL); - - registered = TRUE; -} - -VOID NotifyGrowl( - _In_ PPH_PLUGIN_NOTIFY_EVENT NotifyEvent - ) -{ - PSTR notification; - PPH_STRING title; - PPH_BYTES titleUtf8; - PPH_STRING message; - PPH_BYTES messageUtf8; - PPH_PROCESS_ITEM processItem; - PPH_SERVICE_ITEM serviceItem; - PPH_PROCESS_ITEM parentProcessItem; - - if (NotifyEvent->Handled) - return; - - switch (NotifyEvent->Type) - { - case PH_NOTIFY_PROCESS_CREATE: - processItem = NotifyEvent->Parameter; - notification = GrowlNotifications[0]; - title = processItem->ProcessName; - - parentProcessItem = PhReferenceProcessItemForParent( - processItem->ParentProcessId, - processItem->ProcessId, - &processItem->CreateTime - ); - - message = PhaFormatString( - L"The process %s (%lu) was started by %s.", - processItem->ProcessName->Buffer, - HandleToUlong(processItem->ProcessId), - parentProcessItem ? parentProcessItem->ProcessName->Buffer : L"an unknown process" - ); - - if (parentProcessItem) - PhDereferenceObject(parentProcessItem); - - break; - case PH_NOTIFY_PROCESS_DELETE: - processItem = NotifyEvent->Parameter; - notification = GrowlNotifications[1]; - title = processItem->ProcessName; - - message = PhaFormatString(L"The process %s (%lu) was terminated.", - processItem->ProcessName->Buffer, - HandleToUlong(processItem->ProcessId) - ); - - break; - case PH_NOTIFY_SERVICE_CREATE: - serviceItem = NotifyEvent->Parameter; - notification = GrowlNotifications[2]; - title = serviceItem->DisplayName; - - message = PhaFormatString(L"The service %s (%s) has been created.", - serviceItem->Name->Buffer, - serviceItem->DisplayName->Buffer - ); - - break; - case PH_NOTIFY_SERVICE_DELETE: - serviceItem = NotifyEvent->Parameter; - notification = GrowlNotifications[3]; - title = serviceItem->DisplayName; - - message = PhaFormatString(L"The service %s (%s) has been deleted.", - serviceItem->Name->Buffer, - serviceItem->DisplayName->Buffer - ); - - break; - case PH_NOTIFY_SERVICE_START: - serviceItem = NotifyEvent->Parameter; - notification = GrowlNotifications[4]; - title = serviceItem->DisplayName; - - message = PhaFormatString(L"The service %s (%s) has been started.", - serviceItem->Name->Buffer, - serviceItem->DisplayName->Buffer - ); - - break; - case PH_NOTIFY_SERVICE_STOP: - serviceItem = NotifyEvent->Parameter; - notification = GrowlNotifications[5]; - title = serviceItem->DisplayName; - - message = PhaFormatString(L"The service %s (%s) has been stopped.", - serviceItem->Name->Buffer, - serviceItem->DisplayName->Buffer - ); - - break; - default: - return; - } - - titleUtf8 = PH_AUTO(PhConvertUtf16ToUtf8Ex(title->Buffer, title->Length)); - messageUtf8 = PH_AUTO(PhConvertUtf16ToUtf8Ex(message->Buffer, message->Length)); - - RegisterGrowl(TRUE); - - if (growl_tcp_notify("127.0.0.1", "Process Hacker", notification, titleUtf8->Buffer, messageUtf8->Buffer, NULL, NULL, NULL) == 0) - NotifyEvent->Handled = TRUE; -} - -NTSTATUS NTAPI RegisterGrowlCallback( - _In_ PVOID Parameter - ) -{ - RegisterGrowl(FALSE); - - return STATUS_SUCCESS; -} - -PPH_STRING FormatFilterEntry( - _In_ PFILTER_ENTRY Entry - ) -{ - return PhConcatStrings2(Entry->Type == FilterInclude ? L"[Include] " : L"[Exclude] ", Entry->Filter->Buffer); -} - -VOID AddEntriesToListBox( - _In_ HWND ListBox, - _In_ PPH_LIST FilterList - ) -{ - ULONG i; - - for (i = 0; i < FilterList->Count; i++) - { - PFILTER_ENTRY entry = FilterList->Items[i]; - - ListBox_AddString(ListBox, PH_AUTO_T(PH_STRING, FormatFilterEntry(entry))->Buffer); - } -} - -PPH_LIST EditingProcessFilterList; -PPH_LIST EditingServiceFilterList; - -LRESULT CALLBACK TextBoxSubclassProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - switch (uMsg) - { - case WM_NCDESTROY: - RemoveWindowSubclass(hWnd, TextBoxSubclassProc, uIdSubclass); - break; - case WM_GETDLGCODE: - { - if (wParam == VK_RETURN) - return DLGC_WANTALLKEYS; - } - break; - case WM_CHAR: - { - if (wParam == VK_RETURN) - { - SendMessage(GetParent(hWnd), WM_COMMAND, IDC_TEXT_RETURN, 0); - return 0; - } - } - break; - } - - return DefSubclassProc(hWnd, uMsg, wParam, lParam); -} - -VOID FixControlStates( - _In_ HWND hwndDlg, - _In_ HWND ListBox - ) -{ - ULONG i; - ULONG count; - - i = ListBox_GetCurSel(ListBox); - count = ListBox_GetCount(ListBox); - - EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), i != LB_ERR); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), i != LB_ERR && i != 0); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), i != LB_ERR && i != count - 1); -} - -INT_PTR HandleCommonMessages( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ HWND ListBox, - _In_ PPH_LIST FilterList - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - SetWindowSubclass(GetDlgItem(hwndDlg, IDC_TEXT), TextBoxSubclassProc, 0, 0); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_INCLUDE), BST_CHECKED); - - FixControlStates(hwndDlg, ListBox); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_LIST: - { - if (HIWORD(wParam) == LBN_SELCHANGE) - { - ULONG i; - - i = ListBox_GetCurSel(ListBox); - - if (i != LB_ERR) - { - PFILTER_ENTRY entry; - - entry = FilterList->Items[i]; - SetDlgItemText(hwndDlg, IDC_TEXT, entry->Filter->Buffer); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_INCLUDE), - entry->Type == FilterInclude ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_EXCLUDE), - entry->Type == FilterExclude ? BST_CHECKED : BST_UNCHECKED); - } - - FixControlStates(hwndDlg, ListBox); - } - } - break; - case IDC_ADD: - case IDC_TEXT_RETURN: - { - ULONG i; - PPH_STRING string; - PFILTER_ENTRY entry = NULL; - FILTER_TYPE type; - PPH_STRING entryString; - - string = PhGetWindowText(GetDlgItem(hwndDlg, IDC_TEXT)); - - if (string->Length == 0) - { - PhDereferenceObject(string); - return FALSE; - } - - for (i = 0; i < FilterList->Count; i++) - { - entry = FilterList->Items[i]; - - if (PhEqualString(entry->Filter, string, TRUE)) - break; - } - - type = Button_GetCheck(GetDlgItem(hwndDlg, IDC_INCLUDE)) == BST_CHECKED ? FilterInclude : FilterExclude; - - if (i == FilterList->Count) - { - // No existing entry, so add a new one. - - entry = PhAllocate(sizeof(FILTER_ENTRY)); - entry->Type = type; - entry->Filter = string; - PhInsertItemList(FilterList, 0, entry); - - entryString = FormatFilterEntry(entry); - ListBox_InsertString(ListBox, 0, entryString->Buffer); - PhDereferenceObject(entryString); - - ListBox_SetCurSel(ListBox, 0); - } - else - { - entry->Type = type; - PhDereferenceObject(entry->Filter); - entry->Filter = string; - - ListBox_DeleteString(ListBox, i); - entryString = FormatFilterEntry(entry); - ListBox_InsertString(ListBox, i, entryString->Buffer); - PhDereferenceObject(entryString); - - ListBox_SetCurSel(ListBox, i); - } - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_TEXT), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_TEXT), 0, -1); - - FixControlStates(hwndDlg, ListBox); - } - break; - case IDC_REMOVE: - { - ULONG i; - PFILTER_ENTRY entry; - - i = ListBox_GetCurSel(ListBox); - - if (i != LB_ERR) - { - entry = FilterList->Items[i]; - FreeFilterEntry(entry); - PhRemoveItemList(FilterList, i); - ListBox_DeleteString(ListBox, i); - - if (i >= FilterList->Count) - i = FilterList->Count - 1; - - ListBox_SetCurSel(ListBox, i); - - FixControlStates(hwndDlg, ListBox); - } - } - break; - case IDC_MOVEUP: - { - ULONG i; - PFILTER_ENTRY entry; - PPH_STRING entryString; - - i = ListBox_GetCurSel(ListBox); - - if (i != LB_ERR && i != 0) - { - entry = FilterList->Items[i]; - - PhRemoveItemList(FilterList, i); - PhInsertItemList(FilterList, i - 1, entry); - - ListBox_DeleteString(ListBox, i); - entryString = FormatFilterEntry(entry); - ListBox_InsertString(ListBox, i - 1, entryString->Buffer); - PhDereferenceObject(entryString); - - i--; - ListBox_SetCurSel(ListBox, i); - - FixControlStates(hwndDlg, ListBox); - } - } - break; - case IDC_MOVEDOWN: - { - ULONG i; - PFILTER_ENTRY entry; - PPH_STRING entryString; - - i = ListBox_GetCurSel(ListBox); - - if (i != LB_ERR && i != FilterList->Count - 1) - { - entry = FilterList->Items[i]; - - PhRemoveItemList(FilterList, i); - PhInsertItemList(FilterList, i + 1, entry); - - ListBox_DeleteString(ListBox, i); - entryString = FormatFilterEntry(entry); - ListBox_InsertString(ListBox, i + 1, entryString->Buffer); - PhDereferenceObject(entryString); - - i++; - ListBox_SetCurSel(ListBox, i); - - FixControlStates(hwndDlg, ListBox); - } - } - break; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK ProcessesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - INT_PTR result; - - if (result = HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, - GetDlgItem(hwndDlg, IDC_LIST), EditingProcessFilterList)) - return result; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - - EditingProcessFilterList = PhCreateList(ProcessFilterList->Count + 10); - CopyFilterList(EditingProcessFilterList, ProcessFilterList); - - AddEntriesToListBox(GetDlgItem(hwndDlg, IDC_LIST), EditingProcessFilterList); - } - break; - case WM_DESTROY: - { - ClearFilterList(EditingProcessFilterList); - PhDereferenceObject(EditingProcessFilterList); - EditingProcessFilterList = NULL; - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - NOTHING; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - PPH_STRING string; - - ClearFilterList(ProcessFilterList); - CopyFilterList(ProcessFilterList, EditingProcessFilterList); - - string = SaveFilterList(ProcessFilterList); - PhSetStringSetting2(SETTING_NAME_PROCESS_LIST, &string->sr); - PhDereferenceObject(string); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK ServicesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - if (HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, - GetDlgItem(hwndDlg, IDC_LIST), EditingServiceFilterList)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - EditingServiceFilterList = PhCreateList(ServiceFilterList->Count + 10); - CopyFilterList(EditingServiceFilterList, ServiceFilterList); - - AddEntriesToListBox(GetDlgItem(hwndDlg, IDC_LIST), EditingServiceFilterList); - } - break; - case WM_DESTROY: - { - ClearFilterList(EditingServiceFilterList); - PhDereferenceObject(EditingServiceFilterList); - EditingServiceFilterList = NULL; - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - NOTHING; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - PPH_STRING string; - - ClearFilterList(ServiceFilterList); - CopyFilterList(ServiceFilterList, EditingServiceFilterList); - - string = SaveFilterList(ServiceFilterList); - PhSetStringSetting2(SETTING_NAME_SERVICE_LIST, &string->sr); - PhDereferenceObject(string); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK LoggingDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - SetDlgItemText(hwndDlg, IDC_LOGFILENAME, ((PPH_STRING)PH_AUTO(PhGetStringSetting(SETTING_NAME_LOG_FILENAME)))->Buffer); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Log files (*.txt;*.log)", L"*.txt;*.log" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateSaveFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_LOGFILENAME))); - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - SetDlgItemText(hwndDlg, IDC_LOGFILENAME, fileName->Buffer); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_APPLY: - { - PhSetStringSetting2(SETTING_NAME_LOG_FILENAME, &PhaGetDlgItemText(hwndDlg, IDC_LOGFILENAME)->sr); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK GrowlDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - SetDlgItemText(hwndDlg, IDC_LICENSE, PH_AUTO_T(PH_STRING, PhConvertUtf8ToUtf16(gntp_send_license_text))->Buffer); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGROWL), PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL) ? BST_CHECKED : BST_UNCHECKED); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_ENABLEGROWL)); - } - return TRUE; - case PSN_APPLY: - { - PhSetIntegerSetting(SETTING_NAME_ENABLE_GROWL, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGROWL)) == BST_CHECKED); - - if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) - RegisterGrowl(FALSE); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Notifications - + * main program + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#include "extnoti.h" +#include "resource.h" +#include "gntp-send/growl.h" + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI NotifyEventCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID RegisterGrowl( + _In_ BOOLEAN Force + ); + +VOID NotifyGrowl( + _In_ PPH_PLUGIN_NOTIFY_EVENT NotifyEvent + ); + +NTSTATUS NTAPI RegisterGrowlCallback( + _In_ PVOID Parameter + ); + +INT_PTR CALLBACK ProcessesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK ServicesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK LoggingDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK GrowlDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +PH_CALLBACK_REGISTRATION NotifyEventCallbackRegistration; + +PPH_LIST ProcessFilterList; +PPH_LIST ServiceFilterList; + +PSTR GrowlNotifications[] = +{ + "Process Created", + "Process Terminated", + "Service Created", + "Service Deleted", + "Service Started", + "Service Stopped" +}; + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Extended Notifications"; + info->Author = L"wj32"; + info->Description = L"Filters notifications."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1112"; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNotifyEvent), + NotifyEventCallback, + NULL, + &NotifyEventCallbackRegistration + ); + + { + static PH_SETTING_CREATE settings[] = + { + { IntegerSettingType, SETTING_NAME_ENABLE_GROWL, L"0" }, + { StringSettingType, SETTING_NAME_LOG_FILENAME, L"" }, + { StringSettingType, SETTING_NAME_PROCESS_LIST, L"\\i*" }, + { StringSettingType, SETTING_NAME_SERVICE_LIST, L"\\i*" } + }; + + PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); + } + + ProcessFilterList = PhCreateList(10); + ServiceFilterList = PhCreateList(10); + } + break; + } + + return TRUE; +} + +VOID FreeFilterEntry( + _In_ PFILTER_ENTRY Entry + ) +{ + PhDereferenceObject(Entry->Filter); + PhFree(Entry); +} + +VOID ClearFilterList( + _Inout_ PPH_LIST FilterList + ) +{ + ULONG i; + + for (i = 0; i < FilterList->Count; i++) + FreeFilterEntry(FilterList->Items[i]); + + PhClearList(FilterList); +} + +VOID CopyFilterList( + _Inout_ PPH_LIST Destination, + _In_ PPH_LIST Source + ) +{ + ULONG i; + + for (i = 0; i < Source->Count; i++) + { + PFILTER_ENTRY entry = Source->Items[i]; + PFILTER_ENTRY newEntry; + + newEntry = PhAllocate(sizeof(FILTER_ENTRY)); + newEntry->Type = entry->Type; + newEntry->Filter = entry->Filter; + PhReferenceObject(newEntry->Filter); + + PhAddItemList(Destination, newEntry); + } +} + +VOID LoadFilterList( + _Inout_ PPH_LIST FilterList, + _In_ PPH_STRING String + ) +{ + PH_STRING_BUILDER stringBuilder; + SIZE_T length; + SIZE_T i; + PFILTER_ENTRY entry; + + length = String->Length / 2; + PhInitializeStringBuilder(&stringBuilder, 20); + + entry = NULL; + + for (i = 0; i < length; i++) + { + if (String->Buffer[i] == '\\') + { + if (i != length - 1) + { + i++; + + switch (String->Buffer[i]) + { + case 'i': + case 'e': + if (entry) + { + entry->Filter = PhFinalStringBuilderString(&stringBuilder); + PhAddItemList(FilterList, entry); + PhInitializeStringBuilder(&stringBuilder, 20); + } + + entry = PhAllocate(sizeof(FILTER_ENTRY)); + entry->Type = String->Buffer[i] == 'i' ? FilterInclude : FilterExclude; + + break; + + default: + PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); + break; + } + } + else + { + // Trailing backslash. Just ignore it. + break; + } + } + else + { + PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]); + } + } + + if (entry) + { + entry->Filter = PhFinalStringBuilderString(&stringBuilder); + PhAddItemList(FilterList, entry); + } + else + { + PhDeleteStringBuilder(&stringBuilder); + } +} + +PPH_STRING SaveFilterList( + _Inout_ PPH_LIST FilterList + ) +{ + PH_STRING_BUILDER stringBuilder; + SIZE_T i; + SIZE_T j; + WCHAR temp[2]; + + PhInitializeStringBuilder(&stringBuilder, 100); + + temp[0] = '\\'; + + for (i = 0; i < FilterList->Count; i++) + { + PFILTER_ENTRY entry = FilterList->Items[i]; + SIZE_T length; + + // Write the entry type. + + temp[1] = entry->Type == FilterInclude ? 'i' : 'e'; + + PhAppendStringBuilderEx(&stringBuilder, temp, 4); + + // Write the filter string. + + length = entry->Filter->Length / 2; + + for (j = 0; j < length; j++) + { + if (entry->Filter->Buffer[j] == '\\') // escape backslashes + { + temp[1] = entry->Filter->Buffer[j]; + PhAppendStringBuilderEx(&stringBuilder, temp, 4); + } + else + { + PhAppendCharStringBuilder(&stringBuilder, entry->Filter->Buffer[j]); + } + } + } + + return PhFinalStringBuilderString(&stringBuilder); +} + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + LoadFilterList(ProcessFilterList, PhaGetStringSetting(SETTING_NAME_PROCESS_LIST)); + LoadFilterList(ServiceFilterList, PhaGetStringSetting(SETTING_NAME_SERVICE_LIST)); + + FileLogInitialization(); + + if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) + { + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), RegisterGrowlCallback, NULL); + } +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"Notifications - Processes", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_PROCESSES), + ProcessesDlgProc, + NULL + ); + optionsEntry->CreateSection( + L"Notifications - Services", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_SERVICES), + ServicesDlgProc, + NULL + ); + optionsEntry->CreateSection( + L"Notifications - Logging", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_LOGGING), + LoggingDlgProc, + NULL + ); + optionsEntry->CreateSection( + L"Notifications - Growl", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_GROWL), + GrowlDlgProc, + NULL + ); +} + +BOOLEAN MatchFilterList( + _In_ PPH_LIST FilterList, + _In_ PPH_STRING String, + _Out_ FILTER_TYPE *FilterType + ) +{ + ULONG i; + BOOLEAN isFileName; + + isFileName = PhFindCharInString(String, 0, OBJ_NAME_PATH_SEPARATOR) != -1; + + for (i = 0; i < FilterList->Count; i++) + { + PFILTER_ENTRY entry = FilterList->Items[i]; + + if (isFileName && PhFindCharInString(entry->Filter, 0, '\\') == -1) + continue; // ignore filters without backslashes if we're matching a file name + + if (entry->Filter->Length == 2 && entry->Filter->Buffer[0] == '*') // shortcut + { + *FilterType = entry->Type; + return TRUE; + } + + if (PhMatchWildcards(entry->Filter->Buffer, String->Buffer, TRUE)) + { + *FilterType = entry->Type; + return TRUE; + } + } + + return FALSE; +} + +VOID NTAPI NotifyEventCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_NOTIFY_EVENT notifyEvent = Parameter; + PPH_PROCESS_ITEM processItem; + PPH_SERVICE_ITEM serviceItem; + FILTER_TYPE filterType; + BOOLEAN found = FALSE; + + filterType = FilterExclude; + + switch (notifyEvent->Type) + { + case PH_NOTIFY_PROCESS_CREATE: + case PH_NOTIFY_PROCESS_DELETE: + processItem = notifyEvent->Parameter; + + if (processItem->FileName) + found = MatchFilterList(ProcessFilterList, processItem->FileName, &filterType); + + if (!found) + MatchFilterList(ProcessFilterList, processItem->ProcessName, &filterType); + + break; + + case PH_NOTIFY_SERVICE_CREATE: + case PH_NOTIFY_SERVICE_DELETE: + case PH_NOTIFY_SERVICE_START: + case PH_NOTIFY_SERVICE_STOP: + serviceItem = notifyEvent->Parameter; + + MatchFilterList(ServiceFilterList, serviceItem->Name, &filterType); + + break; + } + + if (filterType == FilterExclude) + notifyEvent->Handled = TRUE; // pretend we handled the notification to prevent it from displaying + + if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) + NotifyGrowl(notifyEvent); +} + +VOID RegisterGrowl( + _In_ BOOLEAN Force + ) +{ + static BOOLEAN registered = FALSE; + + if (!Force && registered) + return; + + growl_tcp_register("127.0.0.1", "Process Hacker", GrowlNotifications, sizeof(GrowlNotifications) / sizeof(PSTR), NULL, NULL); + + registered = TRUE; +} + +VOID NotifyGrowl( + _In_ PPH_PLUGIN_NOTIFY_EVENT NotifyEvent + ) +{ + PSTR notification; + PPH_STRING title; + PPH_BYTES titleUtf8; + PPH_STRING message; + PPH_BYTES messageUtf8; + PPH_PROCESS_ITEM processItem; + PPH_SERVICE_ITEM serviceItem; + PPH_PROCESS_ITEM parentProcessItem; + + if (NotifyEvent->Handled) + return; + + switch (NotifyEvent->Type) + { + case PH_NOTIFY_PROCESS_CREATE: + processItem = NotifyEvent->Parameter; + notification = GrowlNotifications[0]; + title = processItem->ProcessName; + + parentProcessItem = PhReferenceProcessItemForParent(processItem); + + message = PhaFormatString( + L"The process %s (%lu) was started by %s.", + processItem->ProcessName->Buffer, + HandleToUlong(processItem->ProcessId), + parentProcessItem ? parentProcessItem->ProcessName->Buffer : L"an unknown process" + ); + + if (parentProcessItem) + PhDereferenceObject(parentProcessItem); + + break; + case PH_NOTIFY_PROCESS_DELETE: + processItem = NotifyEvent->Parameter; + notification = GrowlNotifications[1]; + title = processItem->ProcessName; + + message = PhaFormatString(L"The process %s (%lu) was terminated.", + processItem->ProcessName->Buffer, + HandleToUlong(processItem->ProcessId) + ); + + break; + case PH_NOTIFY_SERVICE_CREATE: + serviceItem = NotifyEvent->Parameter; + notification = GrowlNotifications[2]; + title = serviceItem->DisplayName; + + message = PhaFormatString(L"The service %s (%s) has been created.", + serviceItem->Name->Buffer, + serviceItem->DisplayName->Buffer + ); + + break; + case PH_NOTIFY_SERVICE_DELETE: + serviceItem = NotifyEvent->Parameter; + notification = GrowlNotifications[3]; + title = serviceItem->DisplayName; + + message = PhaFormatString(L"The service %s (%s) has been deleted.", + serviceItem->Name->Buffer, + serviceItem->DisplayName->Buffer + ); + + break; + case PH_NOTIFY_SERVICE_START: + serviceItem = NotifyEvent->Parameter; + notification = GrowlNotifications[4]; + title = serviceItem->DisplayName; + + message = PhaFormatString(L"The service %s (%s) has been started.", + serviceItem->Name->Buffer, + serviceItem->DisplayName->Buffer + ); + + break; + case PH_NOTIFY_SERVICE_STOP: + serviceItem = NotifyEvent->Parameter; + notification = GrowlNotifications[5]; + title = serviceItem->DisplayName; + + message = PhaFormatString(L"The service %s (%s) has been stopped.", + serviceItem->Name->Buffer, + serviceItem->DisplayName->Buffer + ); + + break; + default: + return; + } + + titleUtf8 = PH_AUTO(PhConvertUtf16ToUtf8Ex(title->Buffer, title->Length)); + messageUtf8 = PH_AUTO(PhConvertUtf16ToUtf8Ex(message->Buffer, message->Length)); + + RegisterGrowl(TRUE); + + if (growl_tcp_notify("127.0.0.1", "Process Hacker", notification, titleUtf8->Buffer, messageUtf8->Buffer, NULL, NULL, NULL) == 0) + NotifyEvent->Handled = TRUE; +} + +NTSTATUS NTAPI RegisterGrowlCallback( + _In_ PVOID Parameter + ) +{ + RegisterGrowl(FALSE); + + return STATUS_SUCCESS; +} + +PPH_STRING FormatFilterEntry( + _In_ PFILTER_ENTRY Entry + ) +{ + return PhConcatStrings2(Entry->Type == FilterInclude ? L"[Include] " : L"[Exclude] ", Entry->Filter->Buffer); +} + +VOID AddEntriesToListBox( + _In_ HWND ListBox, + _In_ PPH_LIST FilterList + ) +{ + ULONG i; + + for (i = 0; i < FilterList->Count; i++) + { + PFILTER_ENTRY entry = FilterList->Items[i]; + + ListBox_AddString(ListBox, PH_AUTO_T(PH_STRING, FormatFilterEntry(entry))->Buffer); + } +} + +PPH_LIST EditingProcessFilterList; +PPH_LIST EditingServiceFilterList; + +LRESULT CALLBACK TextBoxSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + WNDPROC oldWndProc = PhGetWindowContext(hWnd, UCHAR_MAX); + + if (!oldWndProc) + return 0; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, UCHAR_MAX); + } + break; + case WM_GETDLGCODE: + { + if (wParam == VK_RETURN) + return DLGC_WANTALLKEYS; + } + break; + case WM_CHAR: + { + if (wParam == VK_RETURN) + { + SendMessage(GetParent(hWnd), WM_COMMAND, IDC_TEXT_RETURN, 0); + return 0; + } + } + break; + } + + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); +} + +VOID FixControlStates( + _In_ HWND hwndDlg, + _In_ HWND ListBox + ) +{ + ULONG i; + ULONG count; + + i = ListBox_GetCurSel(ListBox); + count = ListBox_GetCount(ListBox); + + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), i != LB_ERR); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEUP), i != LB_ERR && i != 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOVEDOWN), i != LB_ERR && i != count - 1); +} + +INT_PTR HandleCommonMessages( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ HWND ListBox, + _In_ PPH_LIST FilterList + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND textBoxHandle; + WNDPROC oldWndProc; + + textBoxHandle = GetDlgItem(hwndDlg, IDC_TEXT); + oldWndProc = (WNDPROC)GetWindowLongPtr(textBoxHandle, GWLP_WNDPROC); + PhSetWindowContext(textBoxHandle, UCHAR_MAX, oldWndProc); + SetWindowLongPtr(textBoxHandle, GWLP_WNDPROC, (LONG_PTR)TextBoxSubclassProc); + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_INCLUDE), BST_CHECKED); + + FixControlStates(hwndDlg, ListBox); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_LIST: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE) + { + ULONG i; + + i = ListBox_GetCurSel(ListBox); + + if (i != LB_ERR) + { + PFILTER_ENTRY entry; + + entry = FilterList->Items[i]; + PhSetDialogItemText(hwndDlg, IDC_TEXT, entry->Filter->Buffer); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_INCLUDE), + entry->Type == FilterInclude ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_EXCLUDE), + entry->Type == FilterExclude ? BST_CHECKED : BST_UNCHECKED); + } + + FixControlStates(hwndDlg, ListBox); + } + } + break; + case IDC_ADD: + case IDC_TEXT_RETURN: + { + ULONG i; + PPH_STRING string; + PFILTER_ENTRY entry = NULL; + FILTER_TYPE type; + PPH_STRING entryString; + + string = PhGetWindowText(GetDlgItem(hwndDlg, IDC_TEXT)); + + if (string->Length == 0) + { + PhDereferenceObject(string); + return FALSE; + } + + for (i = 0; i < FilterList->Count; i++) + { + entry = FilterList->Items[i]; + + if (PhEqualString(entry->Filter, string, TRUE)) + break; + } + + type = Button_GetCheck(GetDlgItem(hwndDlg, IDC_INCLUDE)) == BST_CHECKED ? FilterInclude : FilterExclude; + + if (i == FilterList->Count) + { + // No existing entry, so add a new one. + + entry = PhAllocate(sizeof(FILTER_ENTRY)); + entry->Type = type; + entry->Filter = string; + PhInsertItemList(FilterList, 0, entry); + + entryString = FormatFilterEntry(entry); + ListBox_InsertString(ListBox, 0, entryString->Buffer); + PhDereferenceObject(entryString); + + ListBox_SetCurSel(ListBox, 0); + } + else + { + entry->Type = type; + PhDereferenceObject(entry->Filter); + entry->Filter = string; + + ListBox_DeleteString(ListBox, i); + entryString = FormatFilterEntry(entry); + ListBox_InsertString(ListBox, i, entryString->Buffer); + PhDereferenceObject(entryString); + + ListBox_SetCurSel(ListBox, i); + } + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_TEXT)); + Edit_SetSel(GetDlgItem(hwndDlg, IDC_TEXT), 0, -1); + + FixControlStates(hwndDlg, ListBox); + } + break; + case IDC_REMOVE: + { + ULONG i; + PFILTER_ENTRY entry; + + i = ListBox_GetCurSel(ListBox); + + if (i != LB_ERR) + { + entry = FilterList->Items[i]; + FreeFilterEntry(entry); + PhRemoveItemList(FilterList, i); + ListBox_DeleteString(ListBox, i); + + if (i >= FilterList->Count) + i = FilterList->Count - 1; + + ListBox_SetCurSel(ListBox, i); + + FixControlStates(hwndDlg, ListBox); + } + } + break; + case IDC_MOVEUP: + { + ULONG i; + PFILTER_ENTRY entry; + PPH_STRING entryString; + + i = ListBox_GetCurSel(ListBox); + + if (i != LB_ERR && i != 0) + { + entry = FilterList->Items[i]; + + PhRemoveItemList(FilterList, i); + PhInsertItemList(FilterList, i - 1, entry); + + ListBox_DeleteString(ListBox, i); + entryString = FormatFilterEntry(entry); + ListBox_InsertString(ListBox, i - 1, entryString->Buffer); + PhDereferenceObject(entryString); + + i--; + ListBox_SetCurSel(ListBox, i); + + FixControlStates(hwndDlg, ListBox); + } + } + break; + case IDC_MOVEDOWN: + { + ULONG i; + PFILTER_ENTRY entry; + PPH_STRING entryString; + + i = ListBox_GetCurSel(ListBox); + + if (i != LB_ERR && i != FilterList->Count - 1) + { + entry = FilterList->Items[i]; + + PhRemoveItemList(FilterList, i); + PhInsertItemList(FilterList, i + 1, entry); + + ListBox_DeleteString(ListBox, i); + entryString = FormatFilterEntry(entry); + ListBox_InsertString(ListBox, i + 1, entryString->Buffer); + PhDereferenceObject(entryString); + + i++; + ListBox_SetCurSel(ListBox, i); + + FixControlStates(hwndDlg, ListBox); + } + } + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK ProcessesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + INT_PTR result; + + if (result = HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, + GetDlgItem(hwndDlg, IDC_LIST), EditingProcessFilterList)) + return result; + + switch (uMsg) + { + case WM_INITDIALOG: + { + EditingProcessFilterList = PhCreateList(ProcessFilterList->Count + 10); + CopyFilterList(EditingProcessFilterList, ProcessFilterList); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_MOVEUP), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_MOVEDOWN), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_INCLUDE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_EXCLUDE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_ADD), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_REMOVE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_INFO), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + + AddEntriesToListBox(GetDlgItem(hwndDlg, IDC_LIST), EditingProcessFilterList); + } + break; + case WM_DESTROY: + { + PPH_STRING string; + + ClearFilterList(ProcessFilterList); + CopyFilterList(ProcessFilterList, EditingProcessFilterList); + + string = SaveFilterList(ProcessFilterList); + PhSetStringSetting2(SETTING_NAME_PROCESS_LIST, &string->sr); + PhDereferenceObject(string); + + ClearFilterList(EditingProcessFilterList); + PhDereferenceObject(EditingProcessFilterList); + EditingProcessFilterList = NULL; + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK ServicesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + INT_PTR result; + + if (result = HandleCommonMessages(hwndDlg, uMsg, wParam, lParam, + GetDlgItem(hwndDlg, IDC_LIST), EditingServiceFilterList)) + return result; + + switch (uMsg) + { + case WM_INITDIALOG: + { + EditingServiceFilterList = PhCreateList(ServiceFilterList->Count + 10); + CopyFilterList(EditingServiceFilterList, ServiceFilterList); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_MOVEUP), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_MOVEDOWN), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_INCLUDE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_EXCLUDE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_ADD), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_REMOVE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_INFO), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + + AddEntriesToListBox(GetDlgItem(hwndDlg, IDC_LIST), EditingServiceFilterList); + } + break; + case WM_DESTROY: + { + PPH_STRING string; + + ClearFilterList(ServiceFilterList); + CopyFilterList(ServiceFilterList, EditingServiceFilterList); + + string = SaveFilterList(ServiceFilterList); + PhSetStringSetting2(SETTING_NAME_SERVICE_LIST, &string->sr); + PhDereferenceObject(string); + + ClearFilterList(EditingServiceFilterList); + PhDereferenceObject(EditingServiceFilterList); + EditingServiceFilterList = NULL; + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK LoggingDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSetDialogItemText(hwndDlg, IDC_LOGFILENAME, PhaGetStringSetting(SETTING_NAME_LOG_FILENAME)->Buffer); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_INFO), NULL, PH_ANCHOR_TOP | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LOGFILENAME), NULL, PH_ANCHOR_TOP | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_BROWSE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + } + break; + case WM_DESTROY: + { + PhSetStringSetting2(SETTING_NAME_LOG_FILENAME, &PhaGetDlgItemText(hwndDlg, IDC_LOGFILENAME)->sr); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Log files (*.txt;*.log)", L"*.txt;*.log" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateSaveFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_LOGFILENAME))); + PhSetFileDialogFileName(fileDialog, fileName->Buffer); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + PhSetDialogItemText(hwndDlg, IDC_LOGFILENAME, fileName->Buffer); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK GrowlDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSetDialogItemText(hwndDlg, IDC_LICENSE, PH_AUTO_T(PH_STRING, PhConvertUtf8ToUtf16(gntp_send_license_text))->Buffer); + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGROWL), PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL) ? BST_CHECKED : BST_UNCHECKED); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LICENSE), NULL, PH_ANCHOR_ALL); + } + break; + case WM_DESTROY: + { + PhSetIntegerSetting(SETTING_NAME_ENABLE_GROWL, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGROWL)) == BST_CHECKED); + + if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GROWL)) + RegisterGrowl(FALSE); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedNotifications/resource.h b/plugins/ExtendedNotifications/resource.h index 2aee867c7e6a..10606d451840 100644 --- a/plugins/ExtendedNotifications/resource.h +++ b/plugins/ExtendedNotifications/resource.h @@ -1,33 +1,34 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by ExtendedNotifications.rc -// -#define IDD_PROCESSES 101 -#define IDC_TEXT_RETURN 101 -#define IDD_SERVICES 102 -#define IDD_LOGGING 103 -#define IDD_GROWL 104 -#define IDC_INCLUDE 1006 -#define IDC_EXCLUDE 1007 -#define IDC_REMOVE 1008 -#define IDC_ADD 1009 -#define IDC_MOVEUP 1010 -#define IDC_MOVEDOWN 1011 -#define IDC_LIST 1012 -#define IDC_TEXT 1013 -#define IDC_EDIT1 1014 -#define IDC_LOGFILENAME 1014 -#define IDC_LICENSE 1014 -#define IDC_BROWSE 1015 -#define IDC_ENABLEGROWL 1016 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 108 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1017 -#define _APS_NEXT_SYMED_VALUE 102 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ExtendedNotifications.rc +// +#define IDD_PROCESSES 101 +#define IDC_TEXT_RETURN 101 +#define IDD_SERVICES 102 +#define IDD_LOGGING 103 +#define IDD_GROWL 104 +#define IDC_INCLUDE 1006 +#define IDC_EXCLUDE 1007 +#define IDC_REMOVE 1008 +#define IDC_ADD 1009 +#define IDC_MOVEUP 1010 +#define IDC_MOVEDOWN 1011 +#define IDC_LIST 1012 +#define IDC_TEXT 1013 +#define IDC_EDIT1 1014 +#define IDC_LOGFILENAME 1014 +#define IDC_LICENSE 1014 +#define IDC_BROWSE 1015 +#define IDC_ENABLEGROWL 1016 +#define IDC_INFO 1017 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1018 +#define _APS_NEXT_SYMED_VALUE 102 +#endif +#endif diff --git a/plugins/ExtendedServices/CHANGELOG.txt b/plugins/ExtendedServices/CHANGELOG.txt index d67de79b3774..680ed4c8a80c 100644 --- a/plugins/ExtendedServices/CHANGELOG.txt +++ b/plugins/ExtendedServices/CHANGELOG.txt @@ -1,38 +1,38 @@ -1.10 - * Added service protection and SID information - * Added auto-elevation when saving recovery information, triggers and other service settings - -1.9 - * Added new trigger data types - -1.8 - * Fixed some bugs relating to Windows 8 - -1.7 - * Added new triggers for Windows 8 - * Fixed bug when restarting services - -1.6 - * Disabled editing of required privileges for drivers - -1.5 - * Added support for editing triggers - * Added support for editing preshutdown time-out - * Added support for editing required privileges - * Added elevation support for restarting services - -1.4 - * Improved Services submenu when there is only one service - -1.3 - * Added Services submenu for processes - -1.2 - * Added Other tab - -1.1 - * Added Restart menu item for services - * Fixed service handle leak - -1.0 - * Initial release +1.10 + * Added service protection and SID information + * Added auto-elevation when saving recovery information, triggers and other service settings + +1.9 + * Added new trigger data types + +1.8 + * Fixed some bugs relating to Windows 8 + +1.7 + * Added new triggers for Windows 8 + * Fixed bug when restarting services + +1.6 + * Disabled editing of required privileges for drivers + +1.5 + * Added support for editing triggers + * Added support for editing preshutdown time-out + * Added support for editing required privileges + * Added elevation support for restarting services + +1.4 + * Improved Services submenu when there is only one service + +1.3 + * Added Services submenu for processes + +1.2 + * Added Other tab + +1.1 + * Added Restart menu item for services + * Fixed service handle leak + +1.0 + * Initial release diff --git a/plugins/ExtendedServices/ExtendedServices.rc b/plugins/ExtendedServices/ExtendedServices.rc index 8a8257313de0..b3ffb34c5fca 100644 --- a/plugins/ExtendedServices/ExtendedServices.rc +++ b/plugins/ExtendedServices/ExtendedServices.rc @@ -1,349 +1,348 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,10,0,0 - PRODUCTVERSION 1,10,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "Extended Services for Process Hacker" - VALUE "FileVersion", "1.10" - VALUE "InternalName", "ProcessHacker.ExtendedServices" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ExtendedServices.dll" - VALUE "ProductName", "Extended Services for Process Hacker" - VALUE "ProductVersion", "1.10" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_SRVLIST DIALOGEX 0, 0, 282, 183 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "List" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Static",IDC_SERVICES_LAYOUT,7,19,268,157,NOT WS_VISIBLE - LTEXT "Static",IDC_MESSAGE,7,7,268,8 -END - -IDD_SRVRECOVERY DIALOGEX 0, 0, 282, 183 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Recovery" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "First failure:",IDC_STATIC,7,8,40,8 - COMBOBOX IDC_FIRSTFAILURE,85,7,103,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Second failure:",IDC_STATIC,7,24,49,8 - COMBOBOX IDC_SECONDFAILURE,85,23,103,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Subsequent failures:",IDC_STATIC,7,40,67,8 - COMBOBOX IDC_SUBSEQUENTFAILURES,85,39,103,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Reset fail count after:",IDC_STATIC,7,56,72,8 - EDITTEXT IDC_RESETFAILCOUNT,85,55,40,12,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "days",IDC_STATIC,130,56,16,8 - LTEXT "Restart service after:",IDC_RESTARTSERVICEAFTER_LABEL,7,72,70,8 - EDITTEXT IDC_RESTARTSERVICEAFTER,85,71,40,12,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "minutes",IDC_RESTARTSERVICEAFTER_MINUTES,130,72,26,8 - CONTROL "Enable actions for stops with errors",IDC_ENABLEFORERRORSTOPS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,87,129,10 - PUSHBUTTON "Restart computer options...",IDC_RESTARTCOMPUTEROPTIONS,7,100,114,14 - GROUPBOX "Run program",IDC_RUNPROGRAM_GROUP,7,118,268,45 - LTEXT "Program:",IDC_RUNPROGRAM_LABEL,15,132,30,8 - EDITTEXT IDC_RUNPROGRAM,50,131,165,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,220,130,50,14 - LTEXT "Append /fail=%1% to pass the fail count to the program.",IDC_RUNPROGRAM_INFO,15,148,186,8 -END - -IDD_RESTARTCOMP DIALOGEX 0, 0, 245, 137 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Dialog" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Restart the computer after:",IDC_STATIC,7,8,90,8 - EDITTEXT IDC_RESTARTCOMPAFTER,102,7,40,12,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "minutes",IDC_STATIC,147,8,26,8 - CONTROL "Before restarting, send this message to computers on the network:",IDC_ENABLERESTARTMESSAGE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,23,231,10 - EDITTEXT IDC_RESTARTMESSAGE,7,36,231,76,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL - PUSHBUTTON "Use default message",IDC_USEDEFAULTMESSAGE,7,116,79,14 - DEFPUSHBUTTON "OK",IDOK,134,116,50,14 - PUSHBUTTON "Cancel",IDCANCEL,188,116,50,14 -END - -IDD_SRVRECOVERY2 DIALOGEX 0, 0, 282, 183 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Recovery" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CTEXT "If this service fails, the computer will restart.\nRecovery actions are not supported for this service.",IDC_STATIC,51,80,179,21 -END - -IDD_SRVPROGRESS DIALOGEX 0, 0, 204, 61 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Progress" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,147,40,50,14 - LTEXT "Progress",IDC_MESSAGE,7,7,190,8,SS_ENDELLIPSIS - CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7,21,190,14 -END - -IDD_SRVOTHER DIALOGEX 0, 0, 282, 218 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Other" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Preshutdown timeout:",IDC_STATIC,7,8,72,8 - EDITTEXT IDC_PRESHUTDOWNTIMEOUT,85,7,75,12,ES_AUTOHSCROLL - LTEXT "milliseconds",IDC_STATIC,166,8,38,8 - LTEXT "Service SID:",IDC_STATIC,7,24,40,8 - EDITTEXT IDC_SERVICESID,64,23,211,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "SID type:",IDC_STATIC,7,40,32,8 - COMBOBOX IDC_SIDTYPE,64,39,122,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Protection:",IDC_STATIC,7,57,36,8 - COMBOBOX IDC_PROTECTION,64,56,122,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Required privileges:",IDC_STATIC,7,77,64,8 - CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,88,268,79 - PUSHBUTTON "Add...",IDC_ADD,171,171,50,14 - PUSHBUTTON "Remove",IDC_REMOVE,225,171,50,14 -END - -IDD_OPTIONS DIALOGEX 0, 0, 215, 54 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Enable Services submenu for processes",IDC_ENABLESERVICESMENU, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,142,10 - PUSHBUTTON "Close",IDOK,158,33,50,14 -END - -IDD_SRVTRIGGER DIALOGEX 0, 0, 330, 196 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Trigger" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Type:",IDC_STATIC,7,9,20,8 - COMBOBOX IDC_TYPE,52,7,271,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Subtype:",IDC_STATIC,7,26,30,8 - COMBOBOX IDC_SUBTYPE,52,24,271,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - EDITTEXT IDC_SUBTYPECUSTOM,52,41,271,12,ES_AUTOHSCROLL - LTEXT "Action:",IDC_STATIC,7,59,24,8 - COMBOBOX IDC_ACTION,52,58,72,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Data items:",IDC_STATIC,7,76,38,8 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,52,79,217,90 - PUSHBUTTON "New...",IDC_NEW,273,79,50,14 - PUSHBUTTON "Edit...",IDC_EDIT,273,96,50,14 - PUSHBUTTON "Delete",IDC_DELETE,273,113,50,14 - DEFPUSHBUTTON "OK",IDOK,219,175,50,14 - PUSHBUTTON "Cancel",IDCANCEL,273,175,50,14 -END - -IDD_VALUE DIALOGEX 0, 0, 267, 167 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Edit Data" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Values (one per line):",IDC_STATIC,7,7,69,8 - EDITTEXT IDC_VALUES,7,18,253,124,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL - DEFPUSHBUTTON "OK",IDOK,156,146,50,14 - PUSHBUTTON "Cancel",IDCANCEL,210,146,50,14 -END - -IDD_SRVTRIGGERS DIALOGEX 0, 0, 282, 218 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Triggers" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_TRIGGERS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,268,186 - PUSHBUTTON "New...",IDC_NEW,117,197,50,14 - PUSHBUTTON "Edit...",IDC_EDIT,171,197,50,14 - PUSHBUTTON "Delete",IDC_DELETE,225,197,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_SRVLIST, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 176 - END - - IDD_SRVRECOVERY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 176 - END - - IDD_RESTARTCOMP, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 238 - TOPMARGIN, 7 - BOTTOMMARGIN, 130 - END - - IDD_SRVRECOVERY2, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 176 - END - - IDD_SRVPROGRESS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 197 - TOPMARGIN, 7 - BOTTOMMARGIN, 54 - END - - IDD_SRVOTHER, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 211 - END - - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 208 - TOPMARGIN, 7 - BOTTOMMARGIN, 47 - END - - IDD_SRVTRIGGER, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 323 - TOPMARGIN, 7 - BOTTOMMARGIN, 189 - END - - IDD_VALUE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 260 - TOPMARGIN, 7 - BOTTOMMARGIN, 160 - END - - IDD_SRVTRIGGERS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 275 - TOPMARGIN, 7 - BOTTOMMARGIN, 211 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,10,0,0 + PRODUCTVERSION 1,10,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "wj32" + VALUE "FileDescription", "Extended Services for Process Hacker" + VALUE "FileVersion", "1.10" + VALUE "InternalName", "ProcessHacker.ExtendedServices" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "ExtendedServices.dll" + VALUE "ProductName", "Extended Services for Process Hacker" + VALUE "ProductVersion", "1.10" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SRVLIST DIALOGEX 0, 0, 282, 183 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "List" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Static",IDC_SERVICES_LAYOUT,7,19,268,157,NOT WS_VISIBLE + LTEXT "Static",IDC_MESSAGE,7,7,268,8 +END + +IDD_SRVRECOVERY DIALOGEX 0, 0, 282, 183 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Recovery" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "First failure:",IDC_STATIC,7,8,40,8 + COMBOBOX IDC_FIRSTFAILURE,85,7,103,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Second failure:",IDC_STATIC,7,24,49,8 + COMBOBOX IDC_SECONDFAILURE,85,23,103,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Subsequent failures:",IDC_STATIC,7,40,67,8 + COMBOBOX IDC_SUBSEQUENTFAILURES,85,39,103,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Reset fail count after:",IDC_STATIC,7,56,72,8 + EDITTEXT IDC_RESETFAILCOUNT,85,55,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "days",IDC_STATIC,130,56,16,8 + LTEXT "Restart service after:",IDC_RESTARTSERVICEAFTER_LABEL,7,72,70,8 + EDITTEXT IDC_RESTARTSERVICEAFTER,85,71,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "minutes",IDC_RESTARTSERVICEAFTER_MINUTES,130,72,26,8 + CONTROL "Enable actions for stops with errors",IDC_ENABLEFORERRORSTOPS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,87,129,10 + PUSHBUTTON "Restart computer options...",IDC_RESTARTCOMPUTEROPTIONS,7,100,114,14 + GROUPBOX "Run program",IDC_RUNPROGRAM_GROUP,7,118,268,45 + LTEXT "Program:",IDC_RUNPROGRAM_LABEL,15,132,30,8 + EDITTEXT IDC_RUNPROGRAM,50,131,165,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,220,130,50,14 + LTEXT "Append /fail=%1% to pass the fail count to the program.",IDC_RUNPROGRAM_INFO,15,148,186,8 +END + +IDD_RESTARTCOMP DIALOGEX 0, 0, 245, 137 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Restart the computer after:",IDC_STATIC,7,8,90,8 + EDITTEXT IDC_RESTARTCOMPAFTER,102,7,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "minutes",IDC_STATIC,147,8,26,8 + CONTROL "Before restarting, send this message to computers on the network:",IDC_ENABLERESTARTMESSAGE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,23,231,10 + EDITTEXT IDC_RESTARTMESSAGE,7,36,231,76,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL + PUSHBUTTON "Use default message",IDC_USEDEFAULTMESSAGE,7,116,79,14 + DEFPUSHBUTTON "OK",IDOK,134,116,50,14 + PUSHBUTTON "Cancel",IDCANCEL,188,116,50,14 +END + +IDD_SRVRECOVERY2 DIALOGEX 0, 0, 282, 183 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Recovery" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CTEXT "If this service fails, the computer will restart.\nRecovery actions are not supported for this service.",IDC_STATIC,51,80,179,21 +END + +IDD_SRVPROGRESS DIALOGEX 0, 0, 204, 61 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Progress" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,147,40,50,14 + LTEXT "Progress",IDC_MESSAGE,7,7,190,8,SS_ENDELLIPSIS + CONTROL "",IDC_PROGRESS,"msctls_progress32",0x0,7,21,190,14 +END + +IDD_SRVOTHER DIALOGEX 0, 0, 282, 218 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Other" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Preshutdown timeout:",IDC_STATIC,7,8,72,8 + EDITTEXT IDC_PRESHUTDOWNTIMEOUT,85,7,75,12,ES_AUTOHSCROLL + LTEXT "milliseconds",IDC_STATIC,166,8,38,8 + LTEXT "Service SID:",IDC_STATIC,7,24,40,8 + EDITTEXT IDC_SERVICESID,64,23,211,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "SID type:",IDC_STATIC,7,40,32,8 + COMBOBOX IDC_SIDTYPE,64,39,122,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Protection:",IDC_STATIC,7,57,36,8 + COMBOBOX IDC_PROTECTION,64,56,122,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Required privileges:",IDC_STATIC,7,77,64,8 + CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,88,268,79 + PUSHBUTTON "Add...",IDC_ADD,171,171,50,14 + PUSHBUTTON "Remove",IDC_REMOVE,225,171,50,14 +END + +IDD_OPTIONS DIALOGEX 0, 0, 167, 33 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Enable Services submenu for processes",IDC_ENABLESERVICESMENU, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,142,10 +END + +IDD_SRVTRIGGER DIALOGEX 0, 0, 330, 196 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Trigger" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Type:",IDC_STATIC,7,9,20,8 + COMBOBOX IDC_TYPE,52,7,271,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Subtype:",IDC_STATIC,7,26,30,8 + COMBOBOX IDC_SUBTYPE,52,24,271,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_SUBTYPECUSTOM,52,41,271,12,ES_AUTOHSCROLL + LTEXT "Action:",IDC_STATIC,7,59,24,8 + COMBOBOX IDC_ACTION,52,58,72,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Data items:",IDC_STATIC,7,76,38,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,52,79,217,90 + PUSHBUTTON "New...",IDC_NEW,273,79,50,14 + PUSHBUTTON "Edit...",IDC_EDIT,273,96,50,14 + PUSHBUTTON "Delete",IDC_DELETE,273,113,50,14 + DEFPUSHBUTTON "OK",IDOK,219,175,50,14 + PUSHBUTTON "Cancel",IDCANCEL,273,175,50,14 +END + +IDD_VALUE DIALOGEX 0, 0, 267, 167 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit Data" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Values (one per line):",IDC_STATIC,7,7,69,8 + EDITTEXT IDC_VALUES,7,18,253,124,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + DEFPUSHBUTTON "OK",IDOK,156,146,50,14 + PUSHBUTTON "Cancel",IDCANCEL,210,146,50,14 +END + +IDD_SRVTRIGGERS DIALOGEX 0, 0, 282, 218 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Triggers" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_TRIGGERS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,268,186 + PUSHBUTTON "New...",IDC_NEW,117,197,50,14 + PUSHBUTTON "Edit...",IDC_EDIT,171,197,50,14 + PUSHBUTTON "Delete",IDC_DELETE,225,197,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_SRVLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_SRVRECOVERY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_RESTARTCOMP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 238 + TOPMARGIN, 7 + BOTTOMMARGIN, 130 + END + + IDD_SRVRECOVERY2, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_SRVPROGRESS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 54 + END + + IDD_SRVOTHER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 211 + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 160 + TOPMARGIN, 7 + BOTTOMMARGIN, 26 + END + + IDD_SRVTRIGGER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 323 + TOPMARGIN, 7 + BOTTOMMARGIN, 189 + END + + IDD_VALUE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 260 + TOPMARGIN, 7 + BOTTOMMARGIN, 160 + END + + IDD_SRVTRIGGERS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 211 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_OPTIONS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/ExtendedServices/ExtendedServices.vcxproj b/plugins/ExtendedServices/ExtendedServices.vcxproj index e56beba514e7..1bf18d1db0a0 100644 --- a/plugins/ExtendedServices/ExtendedServices.vcxproj +++ b/plugins/ExtendedServices/ExtendedServices.vcxproj @@ -1,75 +1,94 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD} - ExtendedServices - Win32Proj - ExtendedServices - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD} + ExtendedServices + Win32Proj + ExtendedServices + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + advapi32.dll;comctl32.dll;user32.dll;%(DelayLoadDLLs) + + + + + advapi32.dll;comctl32.dll;user32.dll;%(DelayLoadDLLs) + + + + + advapi32.dll;comctl32.dll;user32.dll;%(DelayLoadDLLs) + + + + + advapi32.dll;comctl32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ExtendedServices/ExtendedServices.vcxproj.filters b/plugins/ExtendedServices/ExtendedServices.vcxproj.filters index 5b0693c97f97..836add93a586 100644 --- a/plugins/ExtendedServices/ExtendedServices.vcxproj.filters +++ b/plugins/ExtendedServices/ExtendedServices.vcxproj.filters @@ -27,9 +27,6 @@ Source Files - - Source Files - Source Files diff --git a/plugins/ExtendedServices/depend.c b/plugins/ExtendedServices/depend.c index b28b234085c2..5c2c09ebfaa7 100644 --- a/plugins/ExtendedServices/depend.c +++ b/plugins/ExtendedServices/depend.c @@ -1,344 +1,360 @@ -/* - * Process Hacker Extended Services - - * dependencies and dependents - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -typedef struct _SERVICE_LIST_CONTEXT -{ - HWND ServiceListHandle; - PH_LAYOUT_MANAGER LayoutManager; -} SERVICE_LIST_CONTEXT, *PSERVICE_LIST_CONTEXT; - -LPENUM_SERVICE_STATUS EsEnumDependentServices( - _In_ SC_HANDLE ServiceHandle, - _In_opt_ ULONG State, - _Out_ PULONG Count - ) -{ - LOGICAL result; - PVOID buffer; - ULONG bufferSize; - ULONG returnLength; - ULONG servicesReturned; - - if (!State) - State = SERVICE_STATE_ALL; - - bufferSize = 0x800; - buffer = PhAllocate(bufferSize); - - if (!(result = EnumDependentServices( - ServiceHandle, - State, - buffer, - bufferSize, - &returnLength, - &servicesReturned - ))) - { - if (GetLastError() == ERROR_MORE_DATA) - { - PhFree(buffer); - bufferSize = returnLength; - buffer = PhAllocate(bufferSize); - - result = EnumDependentServices( - ServiceHandle, - State, - buffer, - bufferSize, - &returnLength, - &servicesReturned - ); - } - - if (!result) - { - PhFree(buffer); - return NULL; - } - } - - *Count = servicesReturned; - - return buffer; -} - -VOID EspLayoutServiceListControl( - _In_ HWND hwndDlg, - _In_ HWND ServiceListHandle - ) -{ - RECT rect; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), &rect); - MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); - - MoveWindow( - ServiceListHandle, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - TRUE - ); -} - -INT_PTR CALLBACK EspServiceDependenciesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_LIST_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = PhAllocate(sizeof(SERVICE_LIST_CONTEXT)); - memset(context, 0, sizeof(SERVICE_LIST_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_LIST_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; - HWND serviceListHandle; - PPH_LIST serviceList; - SC_HANDLE serviceHandle; - ULONG win32Result = 0; - BOOLEAN success = FALSE; - PPH_SERVICE_ITEM *services; - - SetDlgItemText(hwndDlg, IDC_MESSAGE, L"This service depends on the following services:"); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), NULL, PH_ANCHOR_ALL); - - if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) - { - LPQUERY_SERVICE_CONFIG serviceConfig; - - if (serviceConfig = PhGetServiceConfig(serviceHandle)) - { - PWSTR dependency; - PPH_SERVICE_ITEM dependencyService; - - dependency = serviceConfig->lpDependencies; - serviceList = PH_AUTO(PhCreateList(8)); - success = TRUE; - - if (dependency) - { - ULONG dependencyLength; - - while (TRUE) - { - dependencyLength = (ULONG)PhCountStringZ(dependency); - - if (dependencyLength == 0) - break; - - if (dependency[0] == SC_GROUP_IDENTIFIER) - goto ContinueLoop; - - if (dependencyService = PhReferenceServiceItem(dependency)) - PhAddItemList(serviceList, dependencyService); - -ContinueLoop: - dependency += dependencyLength + 1; - } - } - - services = PhAllocateCopy(serviceList->Items, sizeof(PPH_SERVICE_ITEM) * serviceList->Count); - - serviceListHandle = PhCreateServiceListControl(hwndDlg, services, serviceList->Count); - context->ServiceListHandle = serviceListHandle; - EspLayoutServiceListControl(hwndDlg, serviceListHandle); - ShowWindow(serviceListHandle, SW_SHOW); - - PhFree(serviceConfig); - } - else - { - win32Result = GetLastError(); - } - - CloseServiceHandle(serviceHandle); - } - else - { - win32Result = GetLastError(); - } - - if (!success) - { - SetDlgItemText(hwndDlg, IDC_SERVICES_LAYOUT, PhaConcatStrings2(L"Unable to enumerate dependencies: ", - ((PPH_STRING)PH_AUTO(PhGetWin32Message(win32Result)))->Buffer)->Buffer); - ShowWindow(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), SW_SHOW); - } - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&context->LayoutManager); - PhFree(context); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - - if (context->ServiceListHandle) - EspLayoutServiceListControl(hwndDlg, context->ServiceListHandle); - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK EspServiceDependentsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_LIST_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = PhAllocate(sizeof(SERVICE_LIST_CONTEXT)); - memset(context, 0, sizeof(SERVICE_LIST_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_LIST_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; - HWND serviceListHandle; - PPH_LIST serviceList; - SC_HANDLE serviceHandle; - ULONG win32Result = 0; - BOOLEAN success = FALSE; - PPH_SERVICE_ITEM *services; - - SetDlgItemText(hwndDlg, IDC_MESSAGE, L"The following services depend on this service:"); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), NULL, PH_ANCHOR_ALL); - - if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_ENUMERATE_DEPENDENTS)) - { - LPENUM_SERVICE_STATUS dependentServices; - ULONG numberOfDependentServices; - - if (dependentServices = EsEnumDependentServices(serviceHandle, 0, &numberOfDependentServices)) - { - ULONG i; - PPH_SERVICE_ITEM dependentService; - - serviceList = PH_AUTO(PhCreateList(8)); - success = TRUE; - - for (i = 0; i < numberOfDependentServices; i++) - { - if (dependentService = PhReferenceServiceItem(dependentServices[i].lpServiceName)) - PhAddItemList(serviceList, dependentService); - } - - services = PhAllocateCopy(serviceList->Items, sizeof(PPH_SERVICE_ITEM) * serviceList->Count); - - serviceListHandle = PhCreateServiceListControl(hwndDlg, services, serviceList->Count); - context->ServiceListHandle = serviceListHandle; - EspLayoutServiceListControl(hwndDlg, serviceListHandle); - ShowWindow(serviceListHandle, SW_SHOW); - - PhFree(dependentServices); - } - else - { - win32Result = GetLastError(); - } - - CloseServiceHandle(serviceHandle); - } - else - { - win32Result = GetLastError(); - } - - if (!success) - { - SetDlgItemText(hwndDlg, IDC_SERVICES_LAYOUT, PhaConcatStrings2(L"Unable to enumerate dependents: ", - ((PPH_STRING)PH_AUTO(PhGetWin32Message(win32Result)))->Buffer)->Buffer); - ShowWindow(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), SW_SHOW); - } - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&context->LayoutManager); - PhFree(context); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - - if (context->ServiceListHandle) - EspLayoutServiceListControl(hwndDlg, context->ServiceListHandle); - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Services - + * dependencies and dependents + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +typedef struct _SERVICE_LIST_CONTEXT +{ + HWND ServiceListHandle; + PH_LAYOUT_MANAGER LayoutManager; +} SERVICE_LIST_CONTEXT, *PSERVICE_LIST_CONTEXT; + +LPENUM_SERVICE_STATUS EsEnumDependentServices( + _In_ SC_HANDLE ServiceHandle, + _In_opt_ ULONG State, + _Out_ PULONG Count + ) +{ + LOGICAL result; + PVOID buffer; + ULONG bufferSize; + ULONG returnLength; + ULONG servicesReturned; + + if (!State) + State = SERVICE_STATE_ALL; + + bufferSize = 0x800; + buffer = PhAllocate(bufferSize); + + if (!(result = EnumDependentServices( + ServiceHandle, + State, + buffer, + bufferSize, + &returnLength, + &servicesReturned + ))) + { + if (GetLastError() == ERROR_MORE_DATA) + { + PhFree(buffer); + bufferSize = returnLength; + buffer = PhAllocate(bufferSize); + + result = EnumDependentServices( + ServiceHandle, + State, + buffer, + bufferSize, + &returnLength, + &servicesReturned + ); + } + + if (!result) + { + PhFree(buffer); + return NULL; + } + } + + *Count = servicesReturned; + + return buffer; +} + +VOID EspLayoutServiceListControl( + _In_ HWND hwndDlg, + _In_ HWND ServiceListHandle + ) +{ + RECT rect; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), &rect); + MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); + + MoveWindow( + ServiceListHandle, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + TRUE + ); +} + +INT_PTR CALLBACK EspServiceDependenciesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_LIST_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(SERVICE_LIST_CONTEXT)); + memset(context, 0, sizeof(SERVICE_LIST_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; + HWND serviceListHandle; + PPH_LIST serviceList; + SC_HANDLE serviceHandle; + ULONG win32Result = ERROR_SUCCESS; + BOOLEAN success = FALSE; + PPH_SERVICE_ITEM *services; + + PhSetDialogItemText(hwndDlg, IDC_MESSAGE, L"This service depends on the following services:"); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), NULL, PH_ANCHOR_ALL); + + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_CONFIG)) + { + LPQUERY_SERVICE_CONFIG serviceConfig; + + if (serviceConfig = PhGetServiceConfig(serviceHandle)) + { + PWSTR dependency; + PPH_SERVICE_ITEM dependencyService; + + dependency = serviceConfig->lpDependencies; + serviceList = PH_AUTO(PhCreateList(8)); + success = TRUE; + + if (dependency) + { + ULONG dependencyLength; + + while (TRUE) + { + dependencyLength = (ULONG)PhCountStringZ(dependency); + + if (dependencyLength == 0) + break; + + if (dependency[0] == SC_GROUP_IDENTIFIER) + goto ContinueLoop; + + if (dependencyService = PhReferenceServiceItem(dependency)) + PhAddItemList(serviceList, dependencyService); + +ContinueLoop: + dependency += dependencyLength + 1; + } + } + + services = PhAllocateCopy(serviceList->Items, sizeof(PPH_SERVICE_ITEM) * serviceList->Count); + + serviceListHandle = PhCreateServiceListControl(hwndDlg, services, serviceList->Count); + context->ServiceListHandle = serviceListHandle; + EspLayoutServiceListControl(hwndDlg, serviceListHandle); + ShowWindow(serviceListHandle, SW_SHOW); + + PhFree(serviceConfig); + } + else + { + win32Result = GetLastError(); + } + + CloseServiceHandle(serviceHandle); + } + else + { + win32Result = GetLastError(); + } + + if (!success) + { + PPH_STRING errorMessage = PhGetWin32Message(win32Result); + + PhSetDialogItemText(hwndDlg, IDC_SERVICES_LAYOUT, PhaConcatStrings2( + L"Unable to enumerate dependencies: ", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + )->Buffer); + + ShowWindow(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), SW_SHOW); + PhClearReference(&errorMessage); + } + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + PhFree(context); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + + if (context->ServiceListHandle) + EspLayoutServiceListControl(hwndDlg, context->ServiceListHandle); + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK EspServiceDependentsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_LIST_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(SERVICE_LIST_CONTEXT)); + memset(context, 0, sizeof(SERVICE_LIST_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; + HWND serviceListHandle; + PPH_LIST serviceList; + SC_HANDLE serviceHandle; + ULONG win32Result = ERROR_SUCCESS; + BOOLEAN success = FALSE; + PPH_SERVICE_ITEM *services; + + PhSetDialogItemText(hwndDlg, IDC_MESSAGE, L"The following services depend on this service:"); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), NULL, PH_ANCHOR_ALL); + + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_ENUMERATE_DEPENDENTS)) + { + LPENUM_SERVICE_STATUS dependentServices; + ULONG numberOfDependentServices; + + if (dependentServices = EsEnumDependentServices(serviceHandle, 0, &numberOfDependentServices)) + { + ULONG i; + PPH_SERVICE_ITEM dependentService; + + serviceList = PH_AUTO(PhCreateList(8)); + success = TRUE; + + for (i = 0; i < numberOfDependentServices; i++) + { + if (dependentService = PhReferenceServiceItem(dependentServices[i].lpServiceName)) + PhAddItemList(serviceList, dependentService); + } + + services = PhAllocateCopy(serviceList->Items, sizeof(PPH_SERVICE_ITEM) * serviceList->Count); + + serviceListHandle = PhCreateServiceListControl(hwndDlg, services, serviceList->Count); + context->ServiceListHandle = serviceListHandle; + EspLayoutServiceListControl(hwndDlg, serviceListHandle); + ShowWindow(serviceListHandle, SW_SHOW); + + PhFree(dependentServices); + } + else + { + win32Result = GetLastError(); + } + + CloseServiceHandle(serviceHandle); + } + else + { + win32Result = GetLastError(); + } + + if (!success) + { + PPH_STRING errorMessage = PhGetWin32Message(win32Result); + + PhSetDialogItemText(hwndDlg, IDC_SERVICES_LAYOUT, PhaConcatStrings2( + L"Unable to enumerate dependents: ", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + )->Buffer); + + ShowWindow(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), SW_SHOW); + PhClearReference(&errorMessage); + } + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + PhFree(context); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + + if (context->ServiceListHandle) + EspLayoutServiceListControl(hwndDlg, context->ServiceListHandle); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedServices/extsrv.h b/plugins/ExtendedServices/extsrv.h index 654d5db3637d..9c4d768a6f75 100644 --- a/plugins/ExtendedServices/extsrv.h +++ b/plugins/ExtendedServices/extsrv.h @@ -1,128 +1,122 @@ -#ifndef ES_EXTSRV_H -#define ES_EXTSRV_H - -#include -#include - -#include "resource.h" - -// main - -extern PPH_PLUGIN PluginInstance; - -#define PLUGIN_NAME L"ProcessHacker.ExtendedServices" -#define SETTING_NAME_ENABLE_SERVICES_MENU (PLUGIN_NAME L".EnableServicesMenu") - -#define SIP(String, Integer) { (String), (PVOID)(Integer) } - -// depend - -LPENUM_SERVICE_STATUS EsEnumDependentServices( - _In_ SC_HANDLE ServiceHandle, - _In_opt_ ULONG State, - _Out_ PULONG Count - ); - -INT_PTR CALLBACK EspServiceDependenciesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK EspServiceDependentsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// options - -VOID EsShowOptionsDialog( - _In_ HWND ParentWindowHandle - ); - -// other - -typedef NTSTATUS (NTAPI *_RtlCreateServiceSid)( - _In_ PUNICODE_STRING ServiceName, - _Out_writes_bytes_opt_(*ServiceSidLength) PSID ServiceSid, - _Inout_ PULONG ServiceSidLength - ); - -INT_PTR CALLBACK EspServiceOtherDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// recovery - -INT_PTR CALLBACK EspServiceRecoveryDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK EspServiceRecovery2DlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// srvprgrs - -VOID EsRestartServiceWithProgress( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM ServiceItem, - _In_ SC_HANDLE ServiceHandle - ); - -// trigger - -struct _ES_TRIGGER_CONTEXT; - -struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext( - _In_ PPH_SERVICE_ITEM ServiceItem, - _In_ HWND WindowHandle, - _In_ HWND TriggersLv - ); - -VOID EsDestroyServiceTriggerContext( - _In_ struct _ES_TRIGGER_CONTEXT *Context - ); - -VOID EsLoadServiceTriggerInfo( - _In_ struct _ES_TRIGGER_CONTEXT *Context, - _In_ SC_HANDLE ServiceHandle - ); - -BOOLEAN EsSaveServiceTriggerInfo( - _In_ struct _ES_TRIGGER_CONTEXT *Context, - _Out_ PULONG Win32Result - ); - -#define ES_TRIGGER_EVENT_NEW 1 -#define ES_TRIGGER_EVENT_EDIT 2 -#define ES_TRIGGER_EVENT_DELETE 3 -#define ES_TRIGGER_EVENT_SELECTIONCHANGED 4 - -VOID EsHandleEventServiceTrigger( - _In_ struct _ES_TRIGGER_CONTEXT *Context, - _In_ ULONG Event - ); - -// triggpg - -INT_PTR CALLBACK EspServiceTriggersDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -#endif +#ifndef ES_EXTSRV_H +#define ES_EXTSRV_H + +#include +#include + +#include "resource.h" + +// main + +extern PPH_PLUGIN PluginInstance; + +#define PLUGIN_NAME L"ProcessHacker.ExtendedServices" +#define SETTING_NAME_ENABLE_SERVICES_MENU (PLUGIN_NAME L".EnableServicesMenu") + +#define SIP(String, Integer) { (String), (PVOID)(Integer) } + +// depend + +LPENUM_SERVICE_STATUS EsEnumDependentServices( + _In_ SC_HANDLE ServiceHandle, + _In_opt_ ULONG State, + _Out_ PULONG Count + ); + +INT_PTR CALLBACK EspServiceDependenciesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK EspServiceDependentsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// other + +typedef NTSTATUS (NTAPI *_RtlCreateServiceSid)( + _In_ PUNICODE_STRING ServiceName, + _Out_writes_bytes_opt_(*ServiceSidLength) PSID ServiceSid, + _Inout_ PULONG ServiceSidLength + ); + +INT_PTR CALLBACK EspServiceOtherDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// recovery + +INT_PTR CALLBACK EspServiceRecoveryDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK EspServiceRecovery2DlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// srvprgrs + +VOID EsRestartServiceWithProgress( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ SC_HANDLE ServiceHandle + ); + +// trigger + +struct _ES_TRIGGER_CONTEXT; + +struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext( + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ HWND WindowHandle, + _In_ HWND TriggersLv + ); + +VOID EsDestroyServiceTriggerContext( + _In_ struct _ES_TRIGGER_CONTEXT *Context + ); + +VOID EsLoadServiceTriggerInfo( + _In_ struct _ES_TRIGGER_CONTEXT *Context, + _In_ SC_HANDLE ServiceHandle + ); + +BOOLEAN EsSaveServiceTriggerInfo( + _In_ struct _ES_TRIGGER_CONTEXT *Context, + _Out_ PULONG Win32Result + ); + +#define ES_TRIGGER_EVENT_NEW 1 +#define ES_TRIGGER_EVENT_EDIT 2 +#define ES_TRIGGER_EVENT_DELETE 3 +#define ES_TRIGGER_EVENT_SELECTIONCHANGED 4 + +VOID EsHandleEventServiceTrigger( + _In_ struct _ES_TRIGGER_CONTEXT *Context, + _In_ ULONG Event + ); + +// triggpg + +INT_PTR CALLBACK EspServiceTriggersDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#endif diff --git a/plugins/ExtendedServices/main.c b/plugins/ExtendedServices/main.c index edd8f293477c..3cbf85bfb1e2 100644 --- a/plugins/ExtendedServices/main.c +++ b/plugins/ExtendedServices/main.c @@ -1,481 +1,450 @@ -/* - * Process Hacker Extended Services - - * main program - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -PPH_PLUGIN PluginInstance; -static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ServiceMenuInitializingCallbackRegistration; - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - // Nothing -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EsShowOptionsDialog((HWND)Parameter); -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case ID_SERVICE_GOTOSERVICE: - { - ProcessHacker_SelectTabPage(PhMainWndHandle, 1); - ProcessHacker_SelectServiceItem(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); - } - break; - case ID_SERVICE_START: - { - PhUiStartService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); - } - break; - case ID_SERVICE_CONTINUE: - { - PhUiContinueService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); - } - break; - case ID_SERVICE_PAUSE: - { - PhUiPauseService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); - } - break; - case ID_SERVICE_STOP: - { - PhUiStopService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); - } - break; - case ID_SERVICE_RESTART: - { - PPH_SERVICE_ITEM serviceItem = menuItem->Context; - SC_HANDLE serviceHandle; - ULONG win32Result = 0; - - if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_STATUS)) - { - EsRestartServiceWithProgress(PhMainWndHandle, serviceItem, serviceHandle); - CloseServiceHandle(serviceHandle); - } - else - { - win32Result = GetLastError(); - } - - if (win32Result != 0) - { - PhShowStatus( - PhMainWndHandle, - PhaFormatString(L"Unable to restart %s", serviceItem->Name->Buffer)->Buffer, - 0, - win32Result - ); - } - } - break; - } -} - -static int __cdecl ServiceForServicesMenuCompare( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1; - PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2; - - return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); -} - -VOID NTAPI ProcessMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - - if ( - PhGetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU) && - menuInfo->u.Process.NumberOfProcesses == 1 && - menuInfo->u.Process.Processes[0]->ServiceList && - menuInfo->u.Process.Processes[0]->ServiceList->Count != 0 - ) - { - PPH_PROCESS_ITEM processItem; - PPH_EMENU_ITEM servicesMenuItem = NULL; - ULONG enumerationKey; - PPH_SERVICE_ITEM serviceItem; - PPH_LIST serviceList; - ULONG i; - PPH_EMENU_ITEM priorityMenuItem; - ULONG insertIndex; - - processItem = menuInfo->u.Process.Processes[0]; - - // Create a service list so we can sort it. - - serviceList = PH_AUTO(PhCreateList(processItem->ServiceList->Count)); - enumerationKey = 0; - - PhAcquireQueuedLockShared(&processItem->ServiceListLock); - - while (PhEnumPointerList(processItem->ServiceList, &enumerationKey, &serviceItem)) - { - PhReferenceObject(serviceItem); - // We need to use the service item when the user chooses a menu item. - PH_AUTO(serviceItem); - PhAddItemList(serviceList, serviceItem); - } - - PhReleaseQueuedLockShared(&processItem->ServiceListLock); - - // Sort the service list. - qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForServicesMenuCompare); - - // If there is only one service: - // * We use the text "Service (Xxx)". - // * There are no extra submenus. - if (serviceList->Count != 1) - { - servicesMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Services", NULL); - } - - // Create and add a menu item for each service. - - for (i = 0; i < serviceList->Count; i++) - { - PPH_STRING escapedName; - PPH_EMENU_ITEM serviceMenuItem; - PPH_EMENU_ITEM startMenuItem; - PPH_EMENU_ITEM continueMenuItem; - PPH_EMENU_ITEM pauseMenuItem; - PPH_EMENU_ITEM stopMenuItem; - - serviceItem = serviceList->Items[i]; - escapedName = PH_AUTO(PhEscapeStringForMenuPrefix(&serviceItem->Name->sr)); - - if (serviceList->Count == 1) - { - // "Service (Xxx)" - escapedName = PhaFormatString(L"Service (%s)", escapedName->Buffer); - } - - serviceMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, escapedName->Buffer, NULL); - - if (serviceList->Count == 1) - { - // Make this the root submenu that we will insert. - servicesMenuItem = serviceMenuItem; - } - - PhInsertEMenuItem(serviceMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_GOTOSERVICE, L"Go to service", serviceItem), -1); - PhInsertEMenuItem(serviceMenuItem, startMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_START, L"Start", serviceItem), -1); - PhInsertEMenuItem(serviceMenuItem, continueMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_CONTINUE, L"Continue", serviceItem), -1); - PhInsertEMenuItem(serviceMenuItem, pauseMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_PAUSE, L"Pause", serviceItem), -1); - PhInsertEMenuItem(serviceMenuItem, stopMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_STOP, L"Stop", serviceItem), -1); - - // Massive copy and paste from mainwnd.c. - // == START == - -#define SET_MENU_ITEM_ENABLED(MenuItem, Enabled) if (!(Enabled)) (MenuItem)->Flags |= PH_EMENU_DISABLED; - - switch (serviceItem->State) - { - case SERVICE_RUNNING: - { - SET_MENU_ITEM_ENABLED(startMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(pauseMenuItem, - serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); - SET_MENU_ITEM_ENABLED(stopMenuItem, - serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); - } - break; - case SERVICE_PAUSED: - { - SET_MENU_ITEM_ENABLED(startMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(continueMenuItem, - serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); - SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(stopMenuItem, - serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); - } - break; - case SERVICE_STOPPED: - { - SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE); - } - break; - case SERVICE_START_PENDING: - case SERVICE_CONTINUE_PENDING: - case SERVICE_PAUSE_PENDING: - case SERVICE_STOP_PENDING: - { - SET_MENU_ITEM_ENABLED(startMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE); - SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE); - } - break; - } - - if (!(serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE)) - { - PhDestroyEMenuItem(continueMenuItem); - PhDestroyEMenuItem(pauseMenuItem); - } - - // == END == - - if (serviceList->Count != 1) - PhInsertEMenuItem(servicesMenuItem, serviceMenuItem, -1); - } - - // Insert our Services menu after the I/O Priority menu. - - priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"I/O Priority", 0); - - if (!priorityMenuItem) - priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Priority", 0); - - if (priorityMenuItem) - insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, priorityMenuItem) + 1; - else - insertIndex = 0; - - PhInsertEMenuItem(menuInfo->Menu, servicesMenuItem, insertIndex); - } -} - -NTAPI ServicePropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; - PROPSHEETPAGE propSheetPage; - PPH_SERVICE_ITEM serviceItem; - - serviceItem = objectProperties->Parameter; - - // Recovery - if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.lParam = (LPARAM)serviceItem; - - if (!(serviceItem->Flags & SERVICE_RUNS_IN_SYSTEM_PROCESS)) - { - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY); - propSheetPage.pfnDlgProc = EspServiceRecoveryDlgProc; - } - else - { - // Services which run in system processes don't support failure actions. - // Create a different page with a message saying this. - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY2); - propSheetPage.pfnDlgProc = EspServiceRecovery2DlgProc; - } - - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } - - // Dependencies - if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST); - propSheetPage.pszTitle = L"Dependencies"; - propSheetPage.pfnDlgProc = EspServiceDependenciesDlgProc; - propSheetPage.lParam = (LPARAM)serviceItem; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } - - // Dependents - if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST); - propSheetPage.pszTitle = L"Dependents"; - propSheetPage.pfnDlgProc = EspServiceDependentsDlgProc; - propSheetPage.lParam = (LPARAM)serviceItem; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } - - // Other - if (WindowsVersion >= WINDOWS_7 && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVTRIGGERS); - propSheetPage.pszTitle = L"Triggers"; - propSheetPage.pfnDlgProc = EspServiceTriggersDlgProc; - propSheetPage.lParam = (LPARAM)serviceItem; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } - - // Other - if (WindowsVersion >= WINDOWS_VISTA && objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVOTHER); - propSheetPage.pszTitle = L"Other"; - propSheetPage.pfnDlgProc = EspServiceOtherDlgProc; - propSheetPage.lParam = (LPARAM)serviceItem; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } -} - -VOID NTAPI ServiceMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_EMENU_ITEM menuItem; - ULONG indexOfMenuItem; - - if ( - menuInfo->u.Service.NumberOfServices == 1 && - (menuInfo->u.Service.Services[0]->State == SERVICE_RUNNING || menuInfo->u.Service.Services[0]->State == SERVICE_PAUSED) - ) - { - // Insert our Restart menu item after the Stop menu item. - - menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"Stop", 0); - - if (menuItem) - indexOfMenuItem = PhIndexOfEMenuItem(menuInfo->Menu, menuItem); - else - indexOfMenuItem = -1; - - PhInsertEMenuItem( - menuInfo->Menu, - PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_RESTART, L"Restart", menuInfo->u.Service.Services[0]), - indexOfMenuItem + 1 - ); - } -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { IntegerSettingType, SETTING_NAME_ENABLE_SERVICES_MENU, L"1" } - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Extended Services"; - info->Author = L"wj32"; - info->Description = L"Extends service management capabilities."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1113"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), - ProcessMenuInitializingCallback, - NULL, - &ProcessMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), - ServicePropertiesInitializingCallback, - NULL, - &ServicePropertiesInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceMenuInitializing), - ServiceMenuInitializingCallback, - NULL, - &ServiceMenuInitializingCallbackRegistration - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - } - - return TRUE; -} \ No newline at end of file +/* + * Process Hacker Extended Services - + * main program + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ServiceMenuInitializingCallbackRegistration; + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + switch (menuItem->Id) + { + case ID_SERVICE_GOTOSERVICE: + { + ProcessHacker_SelectTabPage(PhMainWndHandle, 1); + ProcessHacker_SelectServiceItem(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + } + break; + case ID_SERVICE_START: + { + PhUiStartService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + } + break; + case ID_SERVICE_CONTINUE: + { + PhUiContinueService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + } + break; + case ID_SERVICE_PAUSE: + { + PhUiPauseService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + } + break; + case ID_SERVICE_STOP: + { + PhUiStopService(PhMainWndHandle, (PPH_SERVICE_ITEM)menuItem->Context); + } + break; + case ID_SERVICE_RESTART: + { + PPH_SERVICE_ITEM serviceItem = menuItem->Context; + SC_HANDLE serviceHandle; + ULONG win32Result = ERROR_SUCCESS; + + if (serviceHandle = PhOpenService(serviceItem->Name->Buffer, SERVICE_QUERY_STATUS)) + { + EsRestartServiceWithProgress(PhMainWndHandle, serviceItem, serviceHandle); + CloseServiceHandle(serviceHandle); + } + else + { + win32Result = GetLastError(); + } + + if (win32Result != ERROR_SUCCESS) + { + PhShowStatus( + PhMainWndHandle, + PhaFormatString(L"Unable to restart %s", serviceItem->Name->Buffer)->Buffer, + 0, + win32Result + ); + } + } + break; + } +} + +static int __cdecl ServiceForServicesMenuCompare( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1; + PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2; + + return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); +} + +VOID NTAPI ProcessMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + + if ( + PhGetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU) && + menuInfo->u.Process.NumberOfProcesses == 1 && + menuInfo->u.Process.Processes[0]->ServiceList && + menuInfo->u.Process.Processes[0]->ServiceList->Count != 0 + ) + { + PPH_PROCESS_ITEM processItem; + PPH_EMENU_ITEM servicesMenuItem = NULL; + ULONG enumerationKey; + PPH_SERVICE_ITEM serviceItem; + PPH_LIST serviceList; + ULONG i; + PPH_EMENU_ITEM priorityMenuItem; + ULONG insertIndex; + + processItem = menuInfo->u.Process.Processes[0]; + + // Create a service list so we can sort it. + + serviceList = PH_AUTO(PhCreateList(processItem->ServiceList->Count)); + enumerationKey = 0; + + PhAcquireQueuedLockShared(&processItem->ServiceListLock); + + while (PhEnumPointerList(processItem->ServiceList, &enumerationKey, &serviceItem)) + { + PhReferenceObject(serviceItem); + // We need to use the service item when the user chooses a menu item. + PH_AUTO(serviceItem); + PhAddItemList(serviceList, serviceItem); + } + + PhReleaseQueuedLockShared(&processItem->ServiceListLock); + + // Sort the service list. + qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForServicesMenuCompare); + + // If there is only one service: + // * We use the text "Service (Xxx)". + // * There are no extra submenus. + if (serviceList->Count != 1) + { + servicesMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Ser&vices", NULL); + } + + // Create and add a menu item for each service. + + for (i = 0; i < serviceList->Count; i++) + { + PPH_STRING escapedName; + PPH_EMENU_ITEM serviceMenuItem; + PPH_EMENU_ITEM startMenuItem; + PPH_EMENU_ITEM continueMenuItem; + PPH_EMENU_ITEM pauseMenuItem; + PPH_EMENU_ITEM stopMenuItem; + + serviceItem = serviceList->Items[i]; + escapedName = PH_AUTO(PhEscapeStringForMenuPrefix(&serviceItem->Name->sr)); + + if (serviceList->Count == 1) + { + // "Service (Xxx)" + escapedName = PhaFormatString(L"Ser&vice (%s)", escapedName->Buffer); + } + + serviceMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, escapedName->Buffer, NULL); + + if (serviceList->Count == 1) + { + // Make this the root submenu that we will insert. + servicesMenuItem = serviceMenuItem; + } + + PhInsertEMenuItem(serviceMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_GOTOSERVICE, L"&Go to service", serviceItem), ULONG_MAX); + PhInsertEMenuItem(serviceMenuItem, startMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_START, L"Sta&rt", serviceItem), ULONG_MAX); + PhInsertEMenuItem(serviceMenuItem, continueMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_CONTINUE, L"&Continue", serviceItem), ULONG_MAX); + PhInsertEMenuItem(serviceMenuItem, pauseMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_PAUSE, L"&Pause", serviceItem), ULONG_MAX); + PhInsertEMenuItem(serviceMenuItem, stopMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_STOP, L"St&op", serviceItem), ULONG_MAX); + + // Massive copy and paste from mainwnd.c. + // == START == + +#define SET_MENU_ITEM_ENABLED(MenuItem, Enabled) if (!(Enabled)) (MenuItem)->Flags |= PH_EMENU_DISABLED; + + switch (serviceItem->State) + { + case SERVICE_RUNNING: + { + SET_MENU_ITEM_ENABLED(startMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(pauseMenuItem, + serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); + SET_MENU_ITEM_ENABLED(stopMenuItem, + serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); + } + break; + case SERVICE_PAUSED: + { + SET_MENU_ITEM_ENABLED(startMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(continueMenuItem, + serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE); + SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(stopMenuItem, + serviceItem->ControlsAccepted & SERVICE_ACCEPT_STOP); + } + break; + case SERVICE_STOPPED: + { + SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE); + } + break; + case SERVICE_START_PENDING: + case SERVICE_CONTINUE_PENDING: + case SERVICE_PAUSE_PENDING: + case SERVICE_STOP_PENDING: + { + SET_MENU_ITEM_ENABLED(startMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(continueMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(pauseMenuItem, FALSE); + SET_MENU_ITEM_ENABLED(stopMenuItem, FALSE); + } + break; + } + + if (!(serviceItem->ControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE)) + { + PhDestroyEMenuItem(continueMenuItem); + PhDestroyEMenuItem(pauseMenuItem); + } + + // == END == + + if (serviceList->Count != 1) + PhInsertEMenuItem(servicesMenuItem, serviceMenuItem, ULONG_MAX); + } + + // Insert our Services menu after the I/O Priority menu. + + priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"I/O Priority", 0); + + if (!priorityMenuItem) + priorityMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Priority", 0); + + if (priorityMenuItem) + insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, priorityMenuItem) + 1; + else + insertIndex = 0; + + PhInsertEMenuItem(menuInfo->Menu, servicesMenuItem, insertIndex); + } +} + +NTAPI ServicePropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; + PROPSHEETPAGE propSheetPage; + PPH_SERVICE_ITEM serviceItem; + + serviceItem = objectProperties->Parameter; + + // Recovery + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.lParam = (LPARAM)serviceItem; + + if (!(serviceItem->Flags & SERVICE_RUNS_IN_SYSTEM_PROCESS)) + { + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY); + propSheetPage.pfnDlgProc = EspServiceRecoveryDlgProc; + } + else + { + // Services which run in system processes don't support failure actions. + // Create a different page with a message saying this. + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVRECOVERY2); + propSheetPage.pfnDlgProc = EspServiceRecovery2DlgProc; + } + + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } + + // Dependencies + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST); + propSheetPage.pszTitle = L"Dependencies"; + propSheetPage.pfnDlgProc = EspServiceDependenciesDlgProc; + propSheetPage.lParam = (LPARAM)serviceItem; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } + + // Dependents + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVLIST); + propSheetPage.pszTitle = L"Dependents"; + propSheetPage.pfnDlgProc = EspServiceDependentsDlgProc; + propSheetPage.lParam = (LPARAM)serviceItem; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } + + // Other + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVTRIGGERS); + propSheetPage.pszTitle = L"Triggers"; + propSheetPage.pfnDlgProc = EspServiceTriggersDlgProc; + propSheetPage.lParam = (LPARAM)serviceItem; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } + + // Other + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVOTHER); + propSheetPage.pszTitle = L"Other"; + propSheetPage.pfnDlgProc = EspServiceOtherDlgProc; + propSheetPage.lParam = (LPARAM)serviceItem; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } +} + +VOID NTAPI ServiceMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_EMENU_ITEM menuItem; + ULONG indexOfMenuItem; + + if ( + menuInfo->u.Service.NumberOfServices == 1 && + (menuInfo->u.Service.Services[0]->State == SERVICE_RUNNING || menuInfo->u.Service.Services[0]->State == SERVICE_PAUSED) + ) + { + // Insert our Restart menu item after the Stop menu item. + + menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"Stop", 0); + + if (menuItem) + indexOfMenuItem = PhIndexOfEMenuItem(menuInfo->Menu, menuItem); + else + indexOfMenuItem = -1; + + PhInsertEMenuItem( + menuInfo->Menu, + PhPluginCreateEMenuItem(PluginInstance, 0, ID_SERVICE_RESTART, L"R&estart", menuInfo->u.Service.Services[0]), + indexOfMenuItem + 1 + ); + } +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { IntegerSettingType, SETTING_NAME_ENABLE_SERVICES_MENU, L"1" } + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Extended Services"; + info->Author = L"wj32"; + info->Description = L"Extends service management capabilities."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1113"; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), + ProcessMenuInitializingCallback, + NULL, + &ProcessMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), + ServicePropertiesInitializingCallback, + NULL, + &ServicePropertiesInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceMenuInitializing), + ServiceMenuInitializingCallback, + NULL, + &ServiceMenuInitializingCallbackRegistration + ); + + PhAddSettings(settings, ARRAYSIZE(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/ExtendedServices/options.c b/plugins/ExtendedServices/options.c deleted file mode 100644 index 42bc796d0951..000000000000 --- a/plugins/ExtendedServices/options.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Process Hacker Extended Services - - * options dialog - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLESERVICESMENU), PhGetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU) ? BST_CHECKED : BST_UNCHECKED); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PhSetIntegerSetting(SETTING_NAME_ENABLE_SERVICES_MENU, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLESERVICESMENU)) == BST_CHECKED); - - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - } - - return FALSE; -} - -VOID EsShowOptionsDialog( - _In_ HWND ParentWindowHandle - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - ParentWindowHandle, - OptionsDlgProc - ); -} \ No newline at end of file diff --git a/plugins/ExtendedServices/other.c b/plugins/ExtendedServices/other.c index 59b33fbce9df..97c961fd16f8 100644 --- a/plugins/ExtendedServices/other.c +++ b/plugins/ExtendedServices/other.c @@ -1,732 +1,757 @@ -/* - * Process Hacker Extended Services - - * other information - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -typedef struct _SERVICE_OTHER_CONTEXT -{ - PPH_SERVICE_ITEM ServiceItem; - - struct - { - ULONG Ready : 1; - ULONG Dirty : 1; - ULONG PreshutdownTimeoutValid : 1; - ULONG RequiredPrivilegesValid : 1; - ULONG SidTypeValid : 1; - ULONG LaunchProtectedValid : 1; - }; - HWND PrivilegesLv; - PPH_LIST PrivilegeList; - - ULONG OriginalLaunchProtected; -} SERVICE_OTHER_CONTEXT, *PSERVICE_OTHER_CONTEXT; - -static _RtlCreateServiceSid RtlCreateServiceSid_I = NULL; - -static PH_KEY_VALUE_PAIR EspServiceSidTypePairs[] = -{ - SIP(L"None", SERVICE_SID_TYPE_NONE), - SIP(L"Restricted", SERVICE_SID_TYPE_RESTRICTED), - SIP(L"Unrestricted", SERVICE_SID_TYPE_UNRESTRICTED) -}; - -static PH_KEY_VALUE_PAIR EspServiceLaunchProtectedPairs[] = -{ - SIP(L"None", SERVICE_LAUNCH_PROTECTED_NONE), - SIP(L"Full (Windows)", SERVICE_LAUNCH_PROTECTED_WINDOWS), - SIP(L"Light (Windows)", SERVICE_LAUNCH_PROTECTED_WINDOWS_LIGHT), - SIP(L"Light (Antimalware)", SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT) -}; - -static WCHAR *EspServiceSidTypeStrings[3] = { L"None", L"Restricted", L"Unrestricted" }; -static WCHAR *EspServiceLaunchProtectedStrings[4] = { L"None", L"Full (Windows)", L"Light (Windows)", L"Light (Antimalware)" }; - -PWSTR EspGetServiceSidTypeString( - _In_ ULONG SidType - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs( - EspServiceSidTypePairs, - sizeof(EspServiceSidTypePairs), - SidType, - &string - )) - return string; - else - return L"Unknown"; -} - -ULONG EspGetServiceSidTypeInteger( - _In_ PWSTR SidType - ) -{ - ULONG integer; - - if (PhFindIntegerSiKeyValuePairs( - EspServiceSidTypePairs, - sizeof(EspServiceSidTypePairs), - SidType, - &integer - )) - return integer; - else - return -1; -} - -PWSTR EspGetServiceLaunchProtectedString( - _In_ ULONG LaunchProtected - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs( - EspServiceLaunchProtectedPairs, - sizeof(EspServiceLaunchProtectedPairs), - LaunchProtected, - &string - )) - return string; - else - return L"Unknown"; -} - -ULONG EspGetServiceLaunchProtectedInteger( - _In_ PWSTR LaunchProtected - ) -{ - ULONG integer; - - if (PhFindIntegerSiKeyValuePairs( - EspServiceLaunchProtectedPairs, - sizeof(EspServiceLaunchProtectedPairs), - LaunchProtected, - &integer - )) - return integer; - else - return -1; -} - -NTSTATUS EspLoadOtherInfo( - _In_ HWND hwndDlg, - _In_ PSERVICE_OTHER_CONTEXT Context - ) -{ - NTSTATUS status = STATUS_SUCCESS; - SC_HANDLE serviceHandle; - ULONG returnLength; - SERVICE_PRESHUTDOWN_INFO preshutdownInfo; - LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; - SERVICE_SID_INFO sidInfo; - SERVICE_LAUNCH_PROTECTED_INFO launchProtectedInfo; - - if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) - return NTSTATUS_FROM_WIN32(GetLastError()); - - // Preshutdown timeout - - if (QueryServiceConfig2(serviceHandle, - SERVICE_CONFIG_PRESHUTDOWN_INFO, - (PBYTE)&preshutdownInfo, - sizeof(SERVICE_PRESHUTDOWN_INFO), - &returnLength - )) - { - SetDlgItemInt(hwndDlg, IDC_PRESHUTDOWNTIMEOUT, preshutdownInfo.dwPreshutdownTimeout, FALSE); - Context->PreshutdownTimeoutValid = TRUE; - } - - // Required privileges - - if (requiredPrivilegesInfo = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO)) - { - PWSTR privilege; - ULONG privilegeLength; - INT lvItemIndex; - PH_STRINGREF privilegeSr; - PPH_STRING privilegeString; - PPH_STRING displayName; - - privilege = requiredPrivilegesInfo->pmszRequiredPrivileges; - - if (privilege) - { - while (TRUE) - { - privilegeLength = (ULONG)PhCountStringZ(privilege); - - if (privilegeLength == 0) - break; - - privilegeString = PhCreateStringEx(privilege, privilegeLength * sizeof(WCHAR)); - PhAddItemList(Context->PrivilegeList, privilegeString); - - lvItemIndex = PhAddListViewItem(Context->PrivilegesLv, MAXINT, privilege, privilegeString); - privilegeSr.Buffer = privilege; - privilegeSr.Length = privilegeLength * sizeof(WCHAR); - - if (PhLookupPrivilegeDisplayName(&privilegeSr, &displayName)) - { - PhSetListViewSubItem(Context->PrivilegesLv, lvItemIndex, 1, displayName->Buffer); - PhDereferenceObject(displayName); - } - - privilege += privilegeLength + 1; - } - } - - ExtendedListView_SortItems(Context->PrivilegesLv); - - PhFree(requiredPrivilegesInfo); - Context->RequiredPrivilegesValid = TRUE; - } - - // SID type - - if (QueryServiceConfig2(serviceHandle, - SERVICE_CONFIG_SERVICE_SID_INFO, - (PBYTE)&sidInfo, - sizeof(SERVICE_SID_INFO), - &returnLength - )) - { - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SIDTYPE), - EspGetServiceSidTypeString(sidInfo.dwServiceSidType), FALSE); - Context->SidTypeValid = TRUE; - } - - // Launch protected - - if (QueryServiceConfig2(serviceHandle, - SERVICE_CONFIG_LAUNCH_PROTECTED, - (PBYTE)&launchProtectedInfo, - sizeof(SERVICE_LAUNCH_PROTECTED_INFO), - &returnLength - )) - { - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_PROTECTION), - EspGetServiceLaunchProtectedString(launchProtectedInfo.dwLaunchProtected), FALSE); - Context->LaunchProtectedValid = TRUE; - Context->OriginalLaunchProtected = launchProtectedInfo.dwLaunchProtected; - } - - CloseServiceHandle(serviceHandle); - - return status; -} - -PPH_STRING EspGetServiceSidString( - _In_ PPH_STRINGREF ServiceName - ) -{ - PSID serviceSid = NULL; - UNICODE_STRING serviceNameUs; - ULONG serviceSidLength = 0; - PPH_STRING sidString = NULL; - - if (!RtlCreateServiceSid_I) - { - if (!(RtlCreateServiceSid_I = PhGetModuleProcAddress(L"ntdll.dll", "RtlCreateServiceSid"))) - return NULL; - } - - PhStringRefToUnicodeString(ServiceName, &serviceNameUs); - - if (RtlCreateServiceSid_I(&serviceNameUs, serviceSid, &serviceSidLength) == STATUS_BUFFER_TOO_SMALL) - { - serviceSid = PhAllocate(serviceSidLength); - - if (NT_SUCCESS(RtlCreateServiceSid_I(&serviceNameUs, serviceSid, &serviceSidLength))) - sidString = PhSidToStringSid(serviceSid); - - PhFree(serviceSid); - } - - return sidString; -} - -BOOLEAN EspChangeServiceConfig2( - _In_ PWSTR ServiceName, - _In_opt_ SC_HANDLE ServiceHandle, - _In_ ULONG InfoLevel, - _In_ PVOID Info - ) -{ - if (ServiceHandle) - { - return !!ChangeServiceConfig2(ServiceHandle, InfoLevel, Info); - } - else - { - NTSTATUS status; - - if (NT_SUCCESS(status = PhSvcCallChangeServiceConfig2(ServiceName, InfoLevel, Info))) - { - return TRUE; - } - else - { - SetLastError(PhNtStatusToDosError(status)); - return FALSE; - } - } -} - -static int __cdecl PrivilegeNameCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PWSTR string1 = *(PWSTR *)elem1; - PWSTR string2 = *(PWSTR *)elem2; - - return PhCompareStringZ(string1, string2, TRUE); -} - -INT_PTR CALLBACK EspServiceOtherDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_OTHER_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = PhAllocate(sizeof(SERVICE_OTHER_CONTEXT)); - memset(context, 0, sizeof(SERVICE_OTHER_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_OTHER_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; - HWND privilegesLv; - - context->ServiceItem = serviceItem; - - context->PrivilegesLv = privilegesLv = GetDlgItem(hwndDlg, IDC_PRIVILEGES); - PhSetListViewStyle(privilegesLv, FALSE, TRUE); - PhSetControlTheme(privilegesLv, L"explorer"); - PhAddListViewColumn(privilegesLv, 0, 0, 0, LVCFMT_LEFT, 140, L"Name"); - PhAddListViewColumn(privilegesLv, 1, 1, 1, LVCFMT_LEFT, 220, L"Display name"); - PhSetExtendedListView(privilegesLv); - - context->PrivilegeList = PhCreateList(32); - - if (context->ServiceItem->Type == SERVICE_KERNEL_DRIVER || context->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) - { - // Drivers don't support required privileges. - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE); - } - - EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), FALSE); - - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_SIDTYPE), - EspServiceSidTypeStrings, sizeof(EspServiceSidTypeStrings) / sizeof(PWSTR)); - PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_PROTECTION), - EspServiceLaunchProtectedStrings, sizeof(EspServiceLaunchProtectedStrings) / sizeof(PWSTR)); - - if (WindowsVersion < WINDOWS_8_1) - EnableWindow(GetDlgItem(hwndDlg, IDC_PROTECTION), FALSE); - - SetDlgItemText(hwndDlg, IDC_SERVICESID, - PhGetStringOrDefault(PH_AUTO(EspGetServiceSidString(&serviceItem->Name->sr)), L"N/A")); - - status = EspLoadOtherInfo(hwndDlg, context); - - if (!NT_SUCCESS(status)) - { - PhShowWarning(hwndDlg, L"Unable to query service information: %s", - ((PPH_STRING)PH_AUTO(PhGetNtMessage(status)))->Buffer); - } - - context->Ready = TRUE; - } - break; - case WM_DESTROY: - { - if (context->PrivilegeList) - { - PhDereferenceObjects(context->PrivilegeList->Items, context->PrivilegeList->Count); - PhDereferenceObject(context->PrivilegeList); - } - - PhFree(context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_ADD: - { - NTSTATUS status; - LSA_HANDLE policyHandle; - LSA_ENUMERATION_HANDLE enumContext; - PPOLICY_PRIVILEGE_DEFINITION buffer; - ULONG count; - ULONG i; - PPH_LIST choices; - PPH_STRING selectedChoice = NULL; - - choices = PH_AUTO(PhCreateList(100)); - - if (!NT_SUCCESS(status = PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL))) - { - PhShowStatus(hwndDlg, L"Unable to open LSA policy", status, 0); - break; - } - - enumContext = 0; - - while (TRUE) - { - status = LsaEnumeratePrivileges( - policyHandle, - &enumContext, - &buffer, - 0x100, - &count - ); - - if (status == STATUS_NO_MORE_ENTRIES) - break; - if (!NT_SUCCESS(status)) - break; - - for (i = 0; i < count; i++) - { - PhAddItemList(choices, PhaCreateStringEx(buffer[i].Name.Buffer, buffer[i].Name.Length)->Buffer); - } - - LsaFreeMemory(buffer); - } - - LsaClose(policyHandle); - - qsort(choices->Items, choices->Count, sizeof(PWSTR), PrivilegeNameCompareFunction); - - while (PhaChoiceDialog( - hwndDlg, - L"Add privilege", - L"Select a privilege to add:", - (PWSTR *)choices->Items, - choices->Count, - NULL, - PH_CHOICE_DIALOG_CHOICE, - &selectedChoice, - NULL, - NULL - )) - { - BOOLEAN found = FALSE; - PPH_STRING privilegeString; - INT lvItemIndex; - PPH_STRING displayName; - - // Check for duplicates. - for (i = 0; i < context->PrivilegeList->Count; i++) - { - if (PhEqualString(context->PrivilegeList->Items[i], selectedChoice, FALSE)) - { - found = TRUE; - break; - } - } - - if (found) - { - if (PhShowMessage( - hwndDlg, - MB_OKCANCEL | MB_ICONERROR, - L"The selected privilege has already been added." - ) == IDOK) - { - continue; - } - else - { - break; - } - } - - PhSetReference(&privilegeString, selectedChoice); - PhAddItemList(context->PrivilegeList, privilegeString); - - lvItemIndex = PhAddListViewItem(context->PrivilegesLv, MAXINT, privilegeString->Buffer, privilegeString); - - if (PhLookupPrivilegeDisplayName(&privilegeString->sr, &displayName)) - { - PhSetListViewSubItem(context->PrivilegesLv, lvItemIndex, 1, displayName->Buffer); - PhDereferenceObject(displayName); - } - - ExtendedListView_SortItems(context->PrivilegesLv); - - context->Dirty = TRUE; - context->RequiredPrivilegesValid = TRUE; - - break; - } - } - break; - case IDC_REMOVE: - { - INT lvItemIndex; - PPH_STRING privilegeString; - ULONG index; - - lvItemIndex = ListView_GetNextItem(context->PrivilegesLv, -1, LVNI_SELECTED); - - if (lvItemIndex != -1 && PhGetListViewItemParam(context->PrivilegesLv, lvItemIndex, (PVOID *)&privilegeString)) - { - index = PhFindItemList(context->PrivilegeList, privilegeString); - - if (index != -1) - { - PhDereferenceObject(privilegeString); - PhRemoveItemList(context->PrivilegeList, index); - PhRemoveListViewItem(context->PrivilegesLv, lvItemIndex); - - context->Dirty = TRUE; - context->RequiredPrivilegesValid = TRUE; - } - } - } - break; - } - - switch (HIWORD(wParam)) - { - case EN_CHANGE: - case CBN_SELCHANGE: - { - if (context->Ready) - { - context->Dirty = TRUE; - - switch (LOWORD(wParam)) - { - case IDC_PRESHUTDOWNTIMEOUT: - context->PreshutdownTimeoutValid = TRUE; - break; - case IDC_SIDTYPE: - context->SidTypeValid = TRUE; - break; - case IDC_PROTECTION: - context->LaunchProtectedValid = TRUE; - break; - } - } - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_KILLACTIVE: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); - } - return TRUE; - case PSN_APPLY: - { - SC_HANDLE serviceHandle = NULL; - ULONG win32Result = 0; - BOOLEAN connectedToPhSvc = FALSE; - PPH_STRING launchProtectedString; - ULONG launchProtected; - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - - launchProtectedString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_PROTECTION))); - launchProtected = EspGetServiceLaunchProtectedInteger(launchProtectedString->Buffer); - - if (context->LaunchProtectedValid && launchProtected != 0 && launchProtected != context->OriginalLaunchProtected) - { - if (PhShowMessage( - hwndDlg, - MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2, - L"Setting service protection will prevent the service from being controlled, modified, or deleted. Do you want to continue?" - ) == IDNO) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - return TRUE; - } - } - - if (context->Dirty) - { - SERVICE_PRESHUTDOWN_INFO preshutdownInfo; - SERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; - SERVICE_SID_INFO sidInfo; - SERVICE_LAUNCH_PROTECTED_INFO launchProtectedInfo; - - if (!(serviceHandle = PhOpenService(context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG))) - { - win32Result = GetLastError(); - - if (win32Result == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) - { - // Elevate using phsvc. - if (PhUiConnectToPhSvc(hwndDlg, FALSE)) - { - win32Result = 0; - connectedToPhSvc = TRUE; - } - else - { - // User cancelled elevation. - win32Result = ERROR_CANCELLED; - goto Done; - } - } - else - { - goto Done; - } - } - - if (context->PreshutdownTimeoutValid) - { - preshutdownInfo.dwPreshutdownTimeout = GetDlgItemInt(hwndDlg, IDC_PRESHUTDOWNTIMEOUT, NULL, FALSE); - - if (!EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, - SERVICE_CONFIG_PRESHUTDOWN_INFO, &preshutdownInfo)) - { - win32Result = GetLastError(); - } - } - - if (context->RequiredPrivilegesValid) - { - PH_STRING_BUILDER sb; - ULONG i; - - PhInitializeStringBuilder(&sb, 100); - - for (i = 0; i < context->PrivilegeList->Count; i++) - { - PhAppendStringBuilder(&sb, &((PPH_STRING)context->PrivilegeList->Items[i])->sr); - PhAppendCharStringBuilder(&sb, 0); - } - - requiredPrivilegesInfo.pmszRequiredPrivileges = sb.String->Buffer; - - if (win32Result == 0 && !EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, - SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO, &requiredPrivilegesInfo)) - { - win32Result = GetLastError(); - } - - PhDeleteStringBuilder(&sb); - } - - if (context->SidTypeValid) - { - PPH_STRING sidTypeString; - - sidTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_SIDTYPE))); - sidInfo.dwServiceSidType = EspGetServiceSidTypeInteger(sidTypeString->Buffer); - - if (win32Result == 0 && !EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, - SERVICE_CONFIG_SERVICE_SID_INFO, &sidInfo)) - { - win32Result = GetLastError(); - } - } - - if (context->LaunchProtectedValid) - { - launchProtectedInfo.dwLaunchProtected = launchProtected; - - if (!EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, - SERVICE_CONFIG_LAUNCH_PROTECTED, &launchProtectedInfo)) - { - // For now, ignore errors here. - // win32Result = GetLastError(); - } - } - -Done: - if (connectedToPhSvc) - PhUiDisconnectFromPhSvc(); - if (serviceHandle) - CloseServiceHandle(serviceHandle); - - if (win32Result != 0) - { - if (win32Result == ERROR_CANCELLED || PhShowMessage( - hwndDlg, - MB_ICONERROR | MB_RETRYCANCEL, - L"Unable to change service information: %s", - ((PPH_STRING)PH_AUTO(PhGetWin32Message(win32Result)))->Buffer - ) == IDRETRY) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } - } - } - - return TRUE; - } - break; - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == context->PrivilegesLv) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), ListView_GetSelectedCount(context->PrivilegesLv) == 1); - } - } - break; - } - } - break; - } - - return FALSE; -} \ No newline at end of file +/* + * Process Hacker Extended Services - + * other information + * + * Copyright (C) 2010-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +typedef struct _SERVICE_OTHER_CONTEXT +{ + PPH_SERVICE_ITEM ServiceItem; + + struct + { + ULONG Ready : 1; + ULONG Dirty : 1; + ULONG PreshutdownTimeoutValid : 1; + ULONG RequiredPrivilegesValid : 1; + ULONG SidTypeValid : 1; + ULONG LaunchProtectedValid : 1; + }; + HWND PrivilegesLv; + PPH_LIST PrivilegeList; + PH_LAYOUT_MANAGER LayoutManager; + ULONG OriginalLaunchProtected; +} SERVICE_OTHER_CONTEXT, *PSERVICE_OTHER_CONTEXT; + +static _RtlCreateServiceSid RtlCreateServiceSid_I = NULL; + +static PH_KEY_VALUE_PAIR EspServiceSidTypePairs[] = +{ + SIP(L"None", SERVICE_SID_TYPE_NONE), + SIP(L"Restricted", SERVICE_SID_TYPE_RESTRICTED), + SIP(L"Unrestricted", SERVICE_SID_TYPE_UNRESTRICTED) +}; + +static PH_KEY_VALUE_PAIR EspServiceLaunchProtectedPairs[] = +{ + SIP(L"None", SERVICE_LAUNCH_PROTECTED_NONE), + SIP(L"Full (Windows)", SERVICE_LAUNCH_PROTECTED_WINDOWS), + SIP(L"Light (Windows)", SERVICE_LAUNCH_PROTECTED_WINDOWS_LIGHT), + SIP(L"Light (Antimalware)", SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT) +}; + +static WCHAR *EspServiceSidTypeStrings[3] = { L"None", L"Restricted", L"Unrestricted" }; +static WCHAR *EspServiceLaunchProtectedStrings[4] = { L"None", L"Full (Windows)", L"Light (Windows)", L"Light (Antimalware)" }; + +PWSTR EspGetServiceSidTypeString( + _In_ ULONG SidType + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs( + EspServiceSidTypePairs, + sizeof(EspServiceSidTypePairs), + SidType, + &string + )) + return string; + else + return L"Unknown"; +} + +ULONG EspGetServiceSidTypeInteger( + _In_ PWSTR SidType + ) +{ + ULONG integer; + + if (PhFindIntegerSiKeyValuePairs( + EspServiceSidTypePairs, + sizeof(EspServiceSidTypePairs), + SidType, + &integer + )) + return integer; + else + return ULONG_MAX; +} + +PWSTR EspGetServiceLaunchProtectedString( + _In_ ULONG LaunchProtected + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs( + EspServiceLaunchProtectedPairs, + sizeof(EspServiceLaunchProtectedPairs), + LaunchProtected, + &string + )) + return string; + else + return L"Unknown"; +} + +ULONG EspGetServiceLaunchProtectedInteger( + _In_ PWSTR LaunchProtected + ) +{ + ULONG integer; + + if (PhFindIntegerSiKeyValuePairs( + EspServiceLaunchProtectedPairs, + sizeof(EspServiceLaunchProtectedPairs), + LaunchProtected, + &integer + )) + return integer; + else + return ULONG_MAX; +} + +NTSTATUS EspLoadOtherInfo( + _In_ HWND hwndDlg, + _In_ PSERVICE_OTHER_CONTEXT Context + ) +{ + NTSTATUS status = STATUS_SUCCESS; + SC_HANDLE serviceHandle; + ULONG returnLength; + SERVICE_PRESHUTDOWN_INFO preshutdownInfo; + LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; + SERVICE_SID_INFO sidInfo; + SERVICE_LAUNCH_PROTECTED_INFO launchProtectedInfo; + + if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) + return NTSTATUS_FROM_WIN32(GetLastError()); + + // Preshutdown timeout + + if (QueryServiceConfig2(serviceHandle, + SERVICE_CONFIG_PRESHUTDOWN_INFO, + (PBYTE)&preshutdownInfo, + sizeof(SERVICE_PRESHUTDOWN_INFO), + &returnLength + )) + { + PhSetDialogItemValue(hwndDlg, IDC_PRESHUTDOWNTIMEOUT, preshutdownInfo.dwPreshutdownTimeout, FALSE); + Context->PreshutdownTimeoutValid = TRUE; + } + + // Required privileges + + if (requiredPrivilegesInfo = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO)) + { + PWSTR privilege; + ULONG privilegeLength; + INT lvItemIndex; + PH_STRINGREF privilegeSr; + PPH_STRING privilegeString; + PPH_STRING displayName; + + privilege = requiredPrivilegesInfo->pmszRequiredPrivileges; + + if (privilege) + { + while (TRUE) + { + privilegeLength = (ULONG)PhCountStringZ(privilege); + + if (privilegeLength == 0) + break; + + privilegeString = PhCreateStringEx(privilege, privilegeLength * sizeof(WCHAR)); + PhAddItemList(Context->PrivilegeList, privilegeString); + + lvItemIndex = PhAddListViewItem(Context->PrivilegesLv, MAXINT, privilege, privilegeString); + privilegeSr.Buffer = privilege; + privilegeSr.Length = privilegeLength * sizeof(WCHAR); + + if (PhLookupPrivilegeDisplayName(&privilegeSr, &displayName)) + { + PhSetListViewSubItem(Context->PrivilegesLv, lvItemIndex, 1, displayName->Buffer); + PhDereferenceObject(displayName); + } + + privilege += privilegeLength + 1; + } + } + + ExtendedListView_SortItems(Context->PrivilegesLv); + + PhFree(requiredPrivilegesInfo); + Context->RequiredPrivilegesValid = TRUE; + } + + // SID type + + if (QueryServiceConfig2(serviceHandle, + SERVICE_CONFIG_SERVICE_SID_INFO, + (PBYTE)&sidInfo, + sizeof(SERVICE_SID_INFO), + &returnLength + )) + { + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SIDTYPE), + EspGetServiceSidTypeString(sidInfo.dwServiceSidType), FALSE); + Context->SidTypeValid = TRUE; + } + + // Launch protected + + if (QueryServiceConfig2(serviceHandle, + SERVICE_CONFIG_LAUNCH_PROTECTED, + (PBYTE)&launchProtectedInfo, + sizeof(SERVICE_LAUNCH_PROTECTED_INFO), + &returnLength + )) + { + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_PROTECTION), + EspGetServiceLaunchProtectedString(launchProtectedInfo.dwLaunchProtected), FALSE); + Context->LaunchProtectedValid = TRUE; + Context->OriginalLaunchProtected = launchProtectedInfo.dwLaunchProtected; + } + + CloseServiceHandle(serviceHandle); + + return status; +} + +PPH_STRING EspGetServiceSidString( + _In_ PPH_STRINGREF ServiceName + ) +{ + PSID serviceSid = NULL; + UNICODE_STRING serviceNameUs; + ULONG serviceSidLength = 0; + PPH_STRING sidString = NULL; + + if (!RtlCreateServiceSid_I) + { + if (!(RtlCreateServiceSid_I = PhGetModuleProcAddress(L"ntdll.dll", "RtlCreateServiceSid"))) + return NULL; + } + + PhStringRefToUnicodeString(ServiceName, &serviceNameUs); + + if (RtlCreateServiceSid_I(&serviceNameUs, serviceSid, &serviceSidLength) == STATUS_BUFFER_TOO_SMALL) + { + serviceSid = PhAllocate(serviceSidLength); + + if (NT_SUCCESS(RtlCreateServiceSid_I(&serviceNameUs, serviceSid, &serviceSidLength))) + sidString = PhSidToStringSid(serviceSid); + + PhFree(serviceSid); + } + + return sidString; +} + +BOOLEAN EspChangeServiceConfig2( + _In_ PWSTR ServiceName, + _In_opt_ SC_HANDLE ServiceHandle, + _In_ ULONG InfoLevel, + _In_ PVOID Info + ) +{ + if (ServiceHandle) + { + return !!ChangeServiceConfig2(ServiceHandle, InfoLevel, Info); + } + else + { + NTSTATUS status; + + if (NT_SUCCESS(status = PhSvcCallChangeServiceConfig2(ServiceName, InfoLevel, Info))) + { + return TRUE; + } + else + { + SetLastError(PhNtStatusToDosError(status)); + return FALSE; + } + } +} + +static int __cdecl PrivilegeNameCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PWSTR string1 = *(PWSTR *)elem1; + PWSTR string2 = *(PWSTR *)elem2; + + return PhCompareStringZ(string1, string2, TRUE); +} + +INT_PTR CALLBACK EspServiceOtherDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_OTHER_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(SERVICE_OTHER_CONTEXT)); + memset(context, 0, sizeof(SERVICE_OTHER_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; + HWND privilegesLv; + + context->ServiceItem = serviceItem; + + context->PrivilegesLv = privilegesLv = GetDlgItem(hwndDlg, IDC_PRIVILEGES); + PhSetListViewStyle(privilegesLv, FALSE, TRUE); + PhSetControlTheme(privilegesLv, L"explorer"); + PhAddListViewColumn(privilegesLv, 0, 0, 0, LVCFMT_LEFT, 140, L"Name"); + PhAddListViewColumn(privilegesLv, 1, 1, 1, LVCFMT_LEFT, 220, L"Display name"); + PhSetExtendedListView(privilegesLv); + + context->PrivilegeList = PhCreateList(32); + + if (context->ServiceItem->Type == SERVICE_KERNEL_DRIVER || context->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) + { + // Drivers don't support required privileges. + EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE); + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), FALSE); + + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_SIDTYPE), + EspServiceSidTypeStrings, sizeof(EspServiceSidTypeStrings) / sizeof(PWSTR)); + PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_PROTECTION), + EspServiceLaunchProtectedStrings, sizeof(EspServiceLaunchProtectedStrings) / sizeof(PWSTR)); + + if (WindowsVersion < WINDOWS_8_1) + EnableWindow(GetDlgItem(hwndDlg, IDC_PROTECTION), FALSE); + + PhSetDialogItemText(hwndDlg, IDC_SERVICESID, + PhGetStringOrDefault(PH_AUTO(EspGetServiceSidString(&serviceItem->Name->sr)), L"N/A")); + + status = EspLoadOtherInfo(hwndDlg, context); + + if (!NT_SUCCESS(status)) + { + PPH_STRING errorMessage = PhGetNtMessage(status); + + PhShowWarning( + hwndDlg, + L"Unable to query service information: %s", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + ); + + PhClearReference(&errorMessage); + } + + context->Ready = TRUE; + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PRIVILEGES), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ADD), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REMOVE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + if (context->PrivilegeList) + { + PhDereferenceObjects(context->PrivilegeList->Items, context->PrivilegeList->Count); + PhDereferenceObject(context->PrivilegeList); + } + + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_ADD: + { + NTSTATUS status; + LSA_HANDLE policyHandle; + LSA_ENUMERATION_HANDLE enumContext; + PPOLICY_PRIVILEGE_DEFINITION buffer; + ULONG count; + ULONG i; + PPH_LIST choices; + PPH_STRING selectedChoice = NULL; + + choices = PH_AUTO(PhCreateList(100)); + + if (!NT_SUCCESS(status = PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL))) + { + PhShowStatus(hwndDlg, L"Unable to open LSA policy", status, 0); + break; + } + + enumContext = 0; + + while (TRUE) + { + status = LsaEnumeratePrivileges( + policyHandle, + &enumContext, + &buffer, + 0x100, + &count + ); + + if (status == STATUS_NO_MORE_ENTRIES) + break; + if (!NT_SUCCESS(status)) + break; + + for (i = 0; i < count; i++) + { + PhAddItemList(choices, PhaCreateStringEx(buffer[i].Name.Buffer, buffer[i].Name.Length)->Buffer); + } + + LsaFreeMemory(buffer); + } + + LsaClose(policyHandle); + + qsort(choices->Items, choices->Count, sizeof(PWSTR), PrivilegeNameCompareFunction); + + while (PhaChoiceDialog( + hwndDlg, + L"Add privilege", + L"Select a privilege to add:", + (PWSTR *)choices->Items, + choices->Count, + NULL, + PH_CHOICE_DIALOG_CHOICE, + &selectedChoice, + NULL, + NULL + )) + { + BOOLEAN found = FALSE; + PPH_STRING privilegeString; + INT lvItemIndex; + PPH_STRING displayName; + + // Check for duplicates. + for (i = 0; i < context->PrivilegeList->Count; i++) + { + if (PhEqualString(context->PrivilegeList->Items[i], selectedChoice, FALSE)) + { + found = TRUE; + break; + } + } + + if (found) + { + if (PhShowMessage( + hwndDlg, + MB_OKCANCEL | MB_ICONERROR, + L"The selected privilege has already been added." + ) == IDOK) + { + continue; + } + else + { + break; + } + } + + PhSetReference(&privilegeString, selectedChoice); + PhAddItemList(context->PrivilegeList, privilegeString); + + lvItemIndex = PhAddListViewItem(context->PrivilegesLv, MAXINT, privilegeString->Buffer, privilegeString); + + if (PhLookupPrivilegeDisplayName(&privilegeString->sr, &displayName)) + { + PhSetListViewSubItem(context->PrivilegesLv, lvItemIndex, 1, displayName->Buffer); + PhDereferenceObject(displayName); + } + + ExtendedListView_SortItems(context->PrivilegesLv); + + context->Dirty = TRUE; + context->RequiredPrivilegesValid = TRUE; + + break; + } + } + break; + case IDC_REMOVE: + { + INT lvItemIndex; + PPH_STRING privilegeString; + ULONG index; + + lvItemIndex = ListView_GetNextItem(context->PrivilegesLv, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(context->PrivilegesLv, lvItemIndex, (PVOID *)&privilegeString)) + { + index = PhFindItemList(context->PrivilegeList, privilegeString); + + if (index != ULONG_MAX) + { + PhDereferenceObject(privilegeString); + PhRemoveItemList(context->PrivilegeList, index); + PhRemoveListViewItem(context->PrivilegesLv, lvItemIndex); + + context->Dirty = TRUE; + context->RequiredPrivilegesValid = TRUE; + } + } + } + break; + } + + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + case CBN_SELCHANGE: + { + if (context->Ready) + { + context->Dirty = TRUE; + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_PRESHUTDOWNTIMEOUT: + context->PreshutdownTimeoutValid = TRUE; + break; + case IDC_SIDTYPE: + context->SidTypeValid = TRUE; + break; + case IDC_PROTECTION: + context->LaunchProtectedValid = TRUE; + break; + } + } + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_KILLACTIVE: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); + } + return TRUE; + case PSN_APPLY: + { + SC_HANDLE serviceHandle = NULL; + ULONG win32Result = ERROR_SUCCESS; + BOOLEAN connectedToPhSvc = FALSE; + PPH_STRING launchProtectedString; + ULONG launchProtected; + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + + launchProtectedString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_PROTECTION))); + launchProtected = EspGetServiceLaunchProtectedInteger(launchProtectedString->Buffer); + + if (context->LaunchProtectedValid && launchProtected != 0 && launchProtected != context->OriginalLaunchProtected) + { + if (PhShowMessage( + hwndDlg, + MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2, + L"Setting service protection will prevent the service from being controlled, modified, or deleted. Do you want to continue?" + ) == IDNO) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + return TRUE; + } + } + + if (context->Dirty) + { + SERVICE_PRESHUTDOWN_INFO preshutdownInfo; + SERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; + SERVICE_SID_INFO sidInfo; + SERVICE_LAUNCH_PROTECTED_INFO launchProtectedInfo; + + if (!(serviceHandle = PhOpenService(context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG))) + { + win32Result = GetLastError(); + + if (win32Result == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) + { + // Elevate using phsvc. + if (PhUiConnectToPhSvc(hwndDlg, FALSE)) + { + win32Result = ERROR_SUCCESS; + connectedToPhSvc = TRUE; + } + else + { + // User cancelled elevation. + win32Result = ERROR_CANCELLED; + goto Done; + } + } + else + { + goto Done; + } + } + + if (context->PreshutdownTimeoutValid) + { + preshutdownInfo.dwPreshutdownTimeout = PhGetDialogItemValue(hwndDlg, IDC_PRESHUTDOWNTIMEOUT); + + if (!EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, + SERVICE_CONFIG_PRESHUTDOWN_INFO, &preshutdownInfo)) + { + win32Result = GetLastError(); + } + } + + if (context->RequiredPrivilegesValid) + { + PH_STRING_BUILDER sb; + ULONG i; + + PhInitializeStringBuilder(&sb, 100); + + for (i = 0; i < context->PrivilegeList->Count; i++) + { + PhAppendStringBuilder(&sb, &((PPH_STRING)context->PrivilegeList->Items[i])->sr); + PhAppendCharStringBuilder(&sb, 0); + } + + requiredPrivilegesInfo.pmszRequiredPrivileges = sb.String->Buffer; + + if (win32Result == ERROR_SUCCESS && !EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, + SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO, &requiredPrivilegesInfo)) + { + win32Result = GetLastError(); + } + + PhDeleteStringBuilder(&sb); + } + + if (context->SidTypeValid) + { + PPH_STRING sidTypeString; + + sidTypeString = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_SIDTYPE))); + sidInfo.dwServiceSidType = EspGetServiceSidTypeInteger(sidTypeString->Buffer); + + if (win32Result == ERROR_SUCCESS && !EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, + SERVICE_CONFIG_SERVICE_SID_INFO, &sidInfo)) + { + win32Result = GetLastError(); + } + } + + if (context->LaunchProtectedValid) + { + launchProtectedInfo.dwLaunchProtected = launchProtected; + + if (!EspChangeServiceConfig2(context->ServiceItem->Name->Buffer, serviceHandle, + SERVICE_CONFIG_LAUNCH_PROTECTED, &launchProtectedInfo)) + { + // For now, ignore errors here. + // win32Result = GetLastError(); + } + } + +Done: + if (connectedToPhSvc) + PhUiDisconnectFromPhSvc(); + if (serviceHandle) + CloseServiceHandle(serviceHandle); + + if (win32Result != ERROR_SUCCESS) + { + PPH_STRING errorMessage = PhGetWin32Message(win32Result); + + if (win32Result == ERROR_CANCELLED || PhShowMessage( + hwndDlg, + MB_ICONERROR | MB_RETRYCANCEL, + L"Unable to change service information: %s", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + ) == IDRETRY) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + + PhClearReference(&errorMessage); + } + } + + return TRUE; + } + break; + case LVN_ITEMCHANGED: + { + if (header->hwndFrom == context->PrivilegesLv) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), ListView_GetSelectedCount(context->PrivilegesLv) == 1); + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedServices/recovery.c b/plugins/ExtendedServices/recovery.c index 553095aaa1ee..8b66feb99c95 100644 --- a/plugins/ExtendedServices/recovery.c +++ b/plugins/ExtendedServices/recovery.c @@ -1,709 +1,722 @@ -/* - * Process Hacker Extended Services - - * recovery information - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -typedef struct _SERVICE_RECOVERY_CONTEXT -{ - PPH_SERVICE_ITEM ServiceItem; - - ULONG NumberOfActions; - BOOLEAN EnableFlagCheckBox; - ULONG RebootAfter; // in ms - PPH_STRING RebootMessage; - - BOOLEAN Ready; - BOOLEAN Dirty; -} SERVICE_RECOVERY_CONTEXT, *PSERVICE_RECOVERY_CONTEXT; - -static PH_KEY_VALUE_PAIR ServiceActionPairs[] = -{ - SIP(L"Take no action", SC_ACTION_NONE), - SIP(L"Restart the service", SC_ACTION_RESTART), - SIP(L"Run a program", SC_ACTION_RUN_COMMAND), - SIP(L"Restart the computer", SC_ACTION_REBOOT) -}; - -INT_PTR CALLBACK RestartComputerDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EspAddServiceActionStrings( - _In_ HWND ComboBoxHandle - ) -{ - ULONG i; - - for (i = 0; i < sizeof(ServiceActionPairs) / sizeof(PH_KEY_VALUE_PAIR); i++) - ComboBox_AddString(ComboBoxHandle, (PWSTR)ServiceActionPairs[i].Key); - - PhSelectComboBoxString(ComboBoxHandle, (PWSTR)ServiceActionPairs[0].Key, FALSE); -} - -SC_ACTION_TYPE EspStringToServiceAction( - _In_ PWSTR String - ) -{ - ULONG integer; - - if (PhFindIntegerSiKeyValuePairs(ServiceActionPairs, sizeof(ServiceActionPairs), String, &integer)) - return integer; - else - return 0; -} - -PWSTR EspServiceActionToString( - _In_ SC_ACTION_TYPE ActionType - ) -{ - PWSTR string; - - if (PhFindStringSiKeyValuePairs(ServiceActionPairs, sizeof(ServiceActionPairs), ActionType, &string)) - return string; - else - return NULL; -} - -SC_ACTION_TYPE ComboBoxToServiceAction( - _In_ HWND ComboBoxHandle - ) -{ - PPH_STRING string; - - string = PH_AUTO(PhGetComboBoxString(ComboBoxHandle, ComboBox_GetCurSel(ComboBoxHandle))); - - if (!string) - return SC_ACTION_NONE; - - return EspStringToServiceAction(string->Buffer); -} - -VOID ServiceActionToComboBox( - _In_ HWND ComboBoxHandle, - _In_ SC_ACTION_TYPE ActionType - ) -{ - PWSTR string; - - if (string = EspServiceActionToString(ActionType)) - PhSelectComboBoxString(ComboBoxHandle, string, FALSE); - else - PhSelectComboBoxString(ComboBoxHandle, (PWSTR)ServiceActionPairs[0].Key, FALSE); -} - -VOID EspFixControls( - _In_ HWND hwndDlg, - _In_ PSERVICE_RECOVERY_CONTEXT Context - ) -{ - SC_ACTION_TYPE action1; - SC_ACTION_TYPE action2; - SC_ACTION_TYPE actionS; - BOOLEAN enableRestart; - BOOLEAN enableReboot; - BOOLEAN enableCommand; - - action1 = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE)); - action2 = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SECONDFAILURE)); - actionS = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), Context->EnableFlagCheckBox); - - enableRestart = action1 == SC_ACTION_RESTART || action2 == SC_ACTION_RESTART || actionS == SC_ACTION_RESTART; - enableReboot = action1 == SC_ACTION_REBOOT || action2 == SC_ACTION_REBOOT || actionS == SC_ACTION_REBOOT; - enableCommand = action1 == SC_ACTION_RUN_COMMAND || action2 == SC_ACTION_RUN_COMMAND || actionS == SC_ACTION_RUN_COMMAND; - - EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER_LABEL), enableRestart); - EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER), enableRestart); - EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER_MINUTES), enableRestart); - - EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTCOMPUTEROPTIONS), enableReboot); - - EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_GROUP), enableCommand); - EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_LABEL), enableCommand); - EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM), enableCommand); - EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE), enableCommand); - EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_INFO), enableCommand); -} - -NTSTATUS EspLoadRecoveryInfo( - _In_ HWND hwndDlg, - _In_ PSERVICE_RECOVERY_CONTEXT Context - ) -{ - NTSTATUS status = STATUS_SUCCESS; - SC_HANDLE serviceHandle; - LPSERVICE_FAILURE_ACTIONS failureActions; - SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; - SC_ACTION_TYPE lastType; - ULONG returnLength; - ULONG i; - - if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) - return NTSTATUS_FROM_WIN32(GetLastError()); - - if (!(failureActions = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS))) - { - CloseServiceHandle(serviceHandle); - return NTSTATUS_FROM_WIN32(GetLastError()); - } - - // Failure action types - - Context->NumberOfActions = failureActions->cActions; - - if (failureActions->cActions != 0 && failureActions->cActions != 3) - status = STATUS_SOME_NOT_MAPPED; - - // If failure actions are not defined for a particular fail count, the - // last failure action is used. Here we duplicate this behaviour when there - // are fewer than 3 failure actions. - lastType = SC_ACTION_NONE; - - ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE), - failureActions->cActions >= 1 ? (lastType = failureActions->lpsaActions[0].Type) : lastType); - ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SECONDFAILURE), - failureActions->cActions >= 2 ? (lastType = failureActions->lpsaActions[1].Type) : lastType); - ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES), - failureActions->cActions >= 3 ? (lastType = failureActions->lpsaActions[2].Type) : lastType); - - // Reset fail count after - - SetDlgItemInt(hwndDlg, IDC_RESETFAILCOUNT, failureActions->dwResetPeriod / (60 * 60 * 24), FALSE); // s to days - - // Restart service after - - SetDlgItemText(hwndDlg, IDC_RESTARTSERVICEAFTER, L"1"); - - for (i = 0; i < failureActions->cActions; i++) - { - if (failureActions->lpsaActions[i].Type == SC_ACTION_RESTART) - { - if (failureActions->lpsaActions[i].Delay != 0) - { - SetDlgItemInt(hwndDlg, IDC_RESTARTSERVICEAFTER, - failureActions->lpsaActions[i].Delay / (1000 * 60), FALSE); // ms to min - } - - break; - } - } - - // Enable actions for stops with errors - - // This is Vista and above only. - if (WindowsVersion >= WINDOWS_VISTA && QueryServiceConfig2( - serviceHandle, - SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, - (BYTE *)&failureActionsFlag, - sizeof(SERVICE_FAILURE_ACTIONS_FLAG), - &returnLength - )) - { - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), - failureActionsFlag.fFailureActionsOnNonCrashFailures ? BST_CHECKED : BST_UNCHECKED); - Context->EnableFlagCheckBox = TRUE; - } - else - { - Context->EnableFlagCheckBox = FALSE; - } - - // Restart computer options - - Context->RebootAfter = 1 * 1000 * 60; - - for (i = 0; i < failureActions->cActions; i++) - { - if (failureActions->lpsaActions[i].Type == SC_ACTION_REBOOT) - { - if (failureActions->lpsaActions[i].Delay != 0) - Context->RebootAfter = failureActions->lpsaActions[i].Delay; - - break; - } - } - - if (failureActions->lpRebootMsg && failureActions->lpRebootMsg[0] != 0) - PhMoveReference(&Context->RebootMessage, PhCreateString(failureActions->lpRebootMsg)); - else - PhClearReference(&Context->RebootMessage); - - // Run program - - SetDlgItemText(hwndDlg, IDC_RUNPROGRAM, failureActions->lpCommand); - - PhFree(failureActions); - CloseServiceHandle(serviceHandle); - - return status; -} - -INT_PTR CALLBACK EspServiceRecoveryDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_RECOVERY_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = PhAllocate(sizeof(SERVICE_RECOVERY_CONTEXT)); - memset(context, 0, sizeof(SERVICE_RECOVERY_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_RECOVERY_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; - - context->ServiceItem = serviceItem; - - EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE)); - EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_SECONDFAILURE)); - EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES)); - - status = EspLoadRecoveryInfo(hwndDlg, context); - - if (status == STATUS_SOME_NOT_MAPPED) - { - if (context->NumberOfActions > 3) - { - PhShowWarning( - hwndDlg, - L"The service has %lu failure actions configured, but this program only supports editing 3. " - L"If you save the recovery information using this program, the additional failure actions will be lost.", - context->NumberOfActions - ); - } - } - else if (!NT_SUCCESS(status)) - { - SetDlgItemText(hwndDlg, IDC_RESETFAILCOUNT, L"0"); - - if (WindowsVersion >= WINDOWS_VISTA) - { - context->EnableFlagCheckBox = TRUE; - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), TRUE); - } - - PhShowWarning(hwndDlg, L"Unable to query service recovery information: %s", - ((PPH_STRING)PH_AUTO(PhGetNtMessage(status)))->Buffer); - } - - EspFixControls(hwndDlg, context); - - context->Ready = TRUE; - } - break; - case WM_DESTROY: - { - PhClearReference(&context->RebootMessage); - PhFree(context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_FIRSTFAILURE: - case IDC_SECONDFAILURE: - case IDC_SUBSEQUENTFAILURES: - { - if (HIWORD(wParam) == CBN_SELCHANGE) - { - EspFixControls(hwndDlg, context); - } - } - break; - case IDC_RESTARTCOMPUTEROPTIONS: - { - DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_RESTARTCOMP), - hwndDlg, - RestartComputerDlgProc, - (LPARAM)context - ); - } - break; - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Executable files (*.exe;*.cmd;*.bat)", L"*.exe;*.cmd;*.bat" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - fileName = PhaGetDlgItemText(hwndDlg, IDC_RUNPROGRAM); - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - SetDlgItemText(hwndDlg, IDC_RUNPROGRAM, fileName->Buffer); - } - - PhFreeFileDialog(fileDialog); - } - break; - case IDC_ENABLEFORERRORSTOPS: - { - context->Dirty = TRUE; - } - break; - } - - switch (HIWORD(wParam)) - { - case EN_CHANGE: - case CBN_SELCHANGE: - { - if (context->Ready) - context->Dirty = TRUE; - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_KILLACTIVE: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); - } - return TRUE; - case PSN_APPLY: - { - NTSTATUS status; - PPH_SERVICE_ITEM serviceItem = context->ServiceItem; - SC_HANDLE serviceHandle; - ULONG restartServiceAfter; - SERVICE_FAILURE_ACTIONS failureActions; - SC_ACTION actions[3]; - ULONG i; - BOOLEAN enableRestart = FALSE; - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - - if (!context->Dirty) - { - return TRUE; - } - - // Build the failure actions structure. - - failureActions.dwResetPeriod = GetDlgItemInt(hwndDlg, IDC_RESETFAILCOUNT, NULL, FALSE) * 60 * 60 * 24; - failureActions.lpRebootMsg = PhGetStringOrEmpty(context->RebootMessage); - failureActions.lpCommand = PhaGetDlgItemText(hwndDlg, IDC_RUNPROGRAM)->Buffer; - failureActions.cActions = 3; - failureActions.lpsaActions = actions; - - actions[0].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE)); - actions[1].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SECONDFAILURE)); - actions[2].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES)); - - restartServiceAfter = GetDlgItemInt(hwndDlg, IDC_RESTARTSERVICEAFTER, NULL, FALSE) * 1000 * 60; - - for (i = 0; i < 3; i++) - { - switch (actions[i].Type) - { - case SC_ACTION_RESTART: - actions[i].Delay = restartServiceAfter; - enableRestart = TRUE; - break; - case SC_ACTION_REBOOT: - actions[i].Delay = context->RebootAfter; - break; - case SC_ACTION_RUN_COMMAND: - actions[i].Delay = 0; - break; - } - } - - // Try to save the changes. - - serviceHandle = PhOpenService( - serviceItem->Name->Buffer, - SERVICE_CHANGE_CONFIG | (enableRestart ? SERVICE_START : 0) // SC_ACTION_RESTART requires SERVICE_START - ); - - if (serviceHandle) - { - if (ChangeServiceConfig2( - serviceHandle, - SERVICE_CONFIG_FAILURE_ACTIONS, - &failureActions - )) - { - if (context->EnableFlagCheckBox) - { - SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; - - failureActionsFlag.fFailureActionsOnNonCrashFailures = - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS)) == BST_CHECKED; - - ChangeServiceConfig2( - serviceHandle, - SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, - &failureActionsFlag - ); - } - - CloseServiceHandle(serviceHandle); - } - else - { - CloseServiceHandle(serviceHandle); - goto ErrorCase; - } - } - else - { - if (GetLastError() == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) - { - // Elevate using phsvc. - if (PhUiConnectToPhSvc(hwndDlg, FALSE)) - { - if (NT_SUCCESS(status = PhSvcCallChangeServiceConfig2( - serviceItem->Name->Buffer, - SERVICE_CONFIG_FAILURE_ACTIONS, - &failureActions - ))) - { - if (context->EnableFlagCheckBox) - { - SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; - - failureActionsFlag.fFailureActionsOnNonCrashFailures = - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS)) == BST_CHECKED; - - PhSvcCallChangeServiceConfig2( - serviceItem->Name->Buffer, - SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, - &failureActionsFlag - ); - } - } - - PhUiDisconnectFromPhSvc(); - - if (!NT_SUCCESS(status)) - { - SetLastError(PhNtStatusToDosError(status)); - goto ErrorCase; - } - } - else - { - // User cancelled elevation. - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } - } - else - { - goto ErrorCase; - } - } - - return TRUE; -ErrorCase: - if (PhShowMessage( - hwndDlg, - MB_ICONERROR | MB_RETRYCANCEL, - L"Unable to change service recovery information: %s", - ((PPH_STRING)PH_AUTO(PhGetWin32Message(GetLastError())))->Buffer - ) == IDRETRY) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK EspServiceRecovery2DlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return FALSE; -} - -INT_PTR CALLBACK RestartComputerDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_RECOVERY_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PSERVICE_RECOVERY_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_RECOVERY_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetDlgItemInt(hwndDlg, IDC_RESTARTCOMPAFTER, context->RebootAfter / (1000 * 60), FALSE); // ms to min - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), context->RebootMessage ? BST_CHECKED : BST_UNCHECKED); - SetDlgItemText(hwndDlg, IDC_RESTARTMESSAGE, PhGetString(context->RebootMessage)); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_RESTARTCOMPAFTER), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_RESTARTCOMPAFTER), 0, -1); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - context->RebootAfter = GetDlgItemInt(hwndDlg, IDC_RESTARTCOMPAFTER, NULL, FALSE) * 1000 * 60; - - if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE)) == BST_CHECKED) - PhMoveReference(&context->RebootMessage, PhGetWindowText(GetDlgItem(hwndDlg, IDC_RESTARTMESSAGE))); - else - PhClearReference(&context->RebootMessage); - - context->Dirty = TRUE; - - EndDialog(hwndDlg, IDOK); - } - break; - case IDC_USEDEFAULTMESSAGE: - { - PPH_STRING message; - PWSTR computerName; - ULONG bufferSize; - BOOLEAN allocated = TRUE; - - // Get the computer name. - - bufferSize = 64; - computerName = PhAllocate((bufferSize + 1) * sizeof(WCHAR)); - - if (!GetComputerName(computerName, &bufferSize)) - { - PhFree(computerName); - computerName = PhAllocate((bufferSize + 1) * sizeof(WCHAR)); - - if (!GetComputerName(computerName, &bufferSize)) - { - PhFree(computerName); - computerName = L"(unknown)"; - allocated = FALSE; - } - } - - // This message is exactly the same as the one in the Services console, - // except the double spaces are replaced by single spaces. - message = PhaFormatString( - L"Your computer is connected to the computer named %s. " - L"The %s service on %s has ended unexpectedly. " - L"%s will restart automatically, and then you can reestablish the connection.", - computerName, - context->ServiceItem->Name->Buffer, - computerName, - computerName - ); - SetDlgItemText(hwndDlg, IDC_RESTARTMESSAGE, message->Buffer); - - if (allocated) - PhFree(computerName); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), BST_CHECKED); - } - break; - case IDC_RESTARTMESSAGE: - { - if (HIWORD(wParam) == EN_CHANGE) - { - // A zero length restart message disables it, so we might as well uncheck the box. - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), - GetWindowTextLength(GetDlgItem(hwndDlg, IDC_RESTARTMESSAGE)) != 0 ? BST_CHECKED : BST_UNCHECKED); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Services - + * recovery information + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +typedef struct _SERVICE_RECOVERY_CONTEXT +{ + PPH_SERVICE_ITEM ServiceItem; + + ULONG NumberOfActions; + BOOLEAN EnableFlagCheckBox; + ULONG RebootAfter; // in ms + PPH_STRING RebootMessage; + + BOOLEAN Ready; + BOOLEAN Dirty; +} SERVICE_RECOVERY_CONTEXT, *PSERVICE_RECOVERY_CONTEXT; + +static PH_KEY_VALUE_PAIR ServiceActionPairs[] = +{ + SIP(L"Take no action", SC_ACTION_NONE), + SIP(L"Restart the service", SC_ACTION_RESTART), + SIP(L"Run a program", SC_ACTION_RUN_COMMAND), + SIP(L"Restart the computer", SC_ACTION_REBOOT) +}; + +INT_PTR CALLBACK RestartComputerDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EspAddServiceActionStrings( + _In_ HWND ComboBoxHandle + ) +{ + ULONG i; + + for (i = 0; i < sizeof(ServiceActionPairs) / sizeof(PH_KEY_VALUE_PAIR); i++) + ComboBox_AddString(ComboBoxHandle, (PWSTR)ServiceActionPairs[i].Key); + + PhSelectComboBoxString(ComboBoxHandle, (PWSTR)ServiceActionPairs[0].Key, FALSE); +} + +SC_ACTION_TYPE EspStringToServiceAction( + _In_ PWSTR String + ) +{ + ULONG integer; + + if (PhFindIntegerSiKeyValuePairs(ServiceActionPairs, sizeof(ServiceActionPairs), String, &integer)) + return integer; + else + return 0; +} + +PWSTR EspServiceActionToString( + _In_ SC_ACTION_TYPE ActionType + ) +{ + PWSTR string; + + if (PhFindStringSiKeyValuePairs(ServiceActionPairs, sizeof(ServiceActionPairs), ActionType, &string)) + return string; + else + return NULL; +} + +SC_ACTION_TYPE ComboBoxToServiceAction( + _In_ HWND ComboBoxHandle + ) +{ + PPH_STRING string; + + string = PH_AUTO(PhGetComboBoxString(ComboBoxHandle, ComboBox_GetCurSel(ComboBoxHandle))); + + if (!string) + return SC_ACTION_NONE; + + return EspStringToServiceAction(string->Buffer); +} + +VOID ServiceActionToComboBox( + _In_ HWND ComboBoxHandle, + _In_ SC_ACTION_TYPE ActionType + ) +{ + PWSTR string; + + if (string = EspServiceActionToString(ActionType)) + PhSelectComboBoxString(ComboBoxHandle, string, FALSE); + else + PhSelectComboBoxString(ComboBoxHandle, (PWSTR)ServiceActionPairs[0].Key, FALSE); +} + +VOID EspFixControls( + _In_ HWND hwndDlg, + _In_ PSERVICE_RECOVERY_CONTEXT Context + ) +{ + SC_ACTION_TYPE action1; + SC_ACTION_TYPE action2; + SC_ACTION_TYPE actionS; + BOOLEAN enableRestart; + BOOLEAN enableReboot; + BOOLEAN enableCommand; + + action1 = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE)); + action2 = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SECONDFAILURE)); + actionS = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES)); + + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), Context->EnableFlagCheckBox); + + enableRestart = action1 == SC_ACTION_RESTART || action2 == SC_ACTION_RESTART || actionS == SC_ACTION_RESTART; + enableReboot = action1 == SC_ACTION_REBOOT || action2 == SC_ACTION_REBOOT || actionS == SC_ACTION_REBOOT; + enableCommand = action1 == SC_ACTION_RUN_COMMAND || action2 == SC_ACTION_RUN_COMMAND || actionS == SC_ACTION_RUN_COMMAND; + + EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER_LABEL), enableRestart); + EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER), enableRestart); + EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTSERVICEAFTER_MINUTES), enableRestart); + + EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTCOMPUTEROPTIONS), enableReboot); + + EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_GROUP), enableCommand); + EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_LABEL), enableCommand); + EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM), enableCommand); + EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE), enableCommand); + EnableWindow(GetDlgItem(hwndDlg, IDC_RUNPROGRAM_INFO), enableCommand); +} + +NTSTATUS EspLoadRecoveryInfo( + _In_ HWND hwndDlg, + _In_ PSERVICE_RECOVERY_CONTEXT Context + ) +{ + NTSTATUS status = STATUS_SUCCESS; + SC_HANDLE serviceHandle; + LPSERVICE_FAILURE_ACTIONS failureActions; + SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; + SC_ACTION_TYPE lastType; + ULONG returnLength; + ULONG i; + + if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) + return NTSTATUS_FROM_WIN32(GetLastError()); + + if (!(failureActions = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS))) + { + CloseServiceHandle(serviceHandle); + return NTSTATUS_FROM_WIN32(GetLastError()); + } + + // Failure action types + + Context->NumberOfActions = failureActions->cActions; + + if (failureActions->cActions != 0 && failureActions->cActions != 3) + status = STATUS_SOME_NOT_MAPPED; + + // If failure actions are not defined for a particular fail count, the + // last failure action is used. Here we duplicate this behaviour when there + // are fewer than 3 failure actions. + lastType = SC_ACTION_NONE; + + ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE), + failureActions->cActions >= 1 ? (lastType = failureActions->lpsaActions[0].Type) : lastType); + ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SECONDFAILURE), + failureActions->cActions >= 2 ? (lastType = failureActions->lpsaActions[1].Type) : lastType); + ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES), + failureActions->cActions >= 3 ? (lastType = failureActions->lpsaActions[2].Type) : lastType); + + // Reset fail count after + + PhSetDialogItemValue(hwndDlg, IDC_RESETFAILCOUNT, failureActions->dwResetPeriod / (60 * 60 * 24), FALSE); // s to days + + // Restart service after + + PhSetDialogItemText(hwndDlg, IDC_RESTARTSERVICEAFTER, L"1"); + + for (i = 0; i < failureActions->cActions; i++) + { + if (failureActions->lpsaActions[i].Type == SC_ACTION_RESTART) + { + if (failureActions->lpsaActions[i].Delay != 0) + { + PhSetDialogItemValue(hwndDlg, IDC_RESTARTSERVICEAFTER, + failureActions->lpsaActions[i].Delay / (1000 * 60), FALSE); // ms to min + } + + break; + } + } + + // Enable actions for stops with errors + + if (QueryServiceConfig2( + serviceHandle, + SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, + (BYTE *)&failureActionsFlag, + sizeof(SERVICE_FAILURE_ACTIONS_FLAG), + &returnLength + )) + { + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), + failureActionsFlag.fFailureActionsOnNonCrashFailures ? BST_CHECKED : BST_UNCHECKED); + Context->EnableFlagCheckBox = TRUE; + } + else + { + Context->EnableFlagCheckBox = FALSE; + } + + // Restart computer options + + Context->RebootAfter = 1 * 1000 * 60; + + for (i = 0; i < failureActions->cActions; i++) + { + if (failureActions->lpsaActions[i].Type == SC_ACTION_REBOOT) + { + if (failureActions->lpsaActions[i].Delay != 0) + Context->RebootAfter = failureActions->lpsaActions[i].Delay; + + break; + } + } + + if (failureActions->lpRebootMsg && failureActions->lpRebootMsg[0] != 0) + PhMoveReference(&Context->RebootMessage, PhCreateString(failureActions->lpRebootMsg)); + else + PhClearReference(&Context->RebootMessage); + + // Run program + + PhSetDialogItemText(hwndDlg, IDC_RUNPROGRAM, failureActions->lpCommand); + + PhFree(failureActions); + CloseServiceHandle(serviceHandle); + + return status; +} + +INT_PTR CALLBACK EspServiceRecoveryDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_RECOVERY_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(SERVICE_RECOVERY_CONTEXT)); + memset(context, 0, sizeof(SERVICE_RECOVERY_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; + + context->ServiceItem = serviceItem; + + EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE)); + EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_SECONDFAILURE)); + EspAddServiceActionStrings(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES)); + + status = EspLoadRecoveryInfo(hwndDlg, context); + + if (status == STATUS_SOME_NOT_MAPPED) + { + if (context->NumberOfActions > 3) + { + PhShowWarning( + hwndDlg, + L"The service has %lu failure actions configured, but this program only supports editing 3. " + L"If you save the recovery information using this program, the additional failure actions will be lost.", + context->NumberOfActions + ); + } + } + else if (!NT_SUCCESS(status)) + { + PPH_STRING errorMessage = PhGetNtMessage(status); + + PhSetDialogItemText(hwndDlg, IDC_RESETFAILCOUNT, L"0"); + + context->EnableFlagCheckBox = TRUE; + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), TRUE); + + PhShowWarning( + hwndDlg, + L"Unable to query service recovery information: %s", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + ); + + PhClearReference(&errorMessage); + } + + EspFixControls(hwndDlg, context); + + context->Ready = TRUE; + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhClearReference(&context->RebootMessage); + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_FIRSTFAILURE: + case IDC_SECONDFAILURE: + case IDC_SUBSEQUENTFAILURES: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + { + EspFixControls(hwndDlg, context); + } + } + break; + case IDC_RESTARTCOMPUTEROPTIONS: + { + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_RESTARTCOMP), + hwndDlg, + RestartComputerDlgProc, + (LPARAM)context + ); + } + break; + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Executable files (*.exe;*.cmd;*.bat)", L"*.exe;*.cmd;*.bat" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + fileName = PhaGetDlgItemText(hwndDlg, IDC_RUNPROGRAM); + PhSetFileDialogFileName(fileDialog, fileName->Buffer); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + PhSetDialogItemText(hwndDlg, IDC_RUNPROGRAM, fileName->Buffer); + } + + PhFreeFileDialog(fileDialog); + } + break; + case IDC_ENABLEFORERRORSTOPS: + { + context->Dirty = TRUE; + } + break; + } + + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + case CBN_SELCHANGE: + { + if (context->Ready) + context->Dirty = TRUE; + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_KILLACTIVE: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); + } + return TRUE; + case PSN_APPLY: + { + NTSTATUS status; + PPH_SERVICE_ITEM serviceItem = context->ServiceItem; + SC_HANDLE serviceHandle; + ULONG restartServiceAfter; + SERVICE_FAILURE_ACTIONS failureActions; + SC_ACTION actions[3]; + ULONG i; + BOOLEAN enableRestart = FALSE; + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + + if (!context->Dirty) + { + return TRUE; + } + + // Build the failure actions structure. + + failureActions.dwResetPeriod = PhGetDialogItemValue(hwndDlg, IDC_RESETFAILCOUNT) * 60 * 60 * 24; + failureActions.lpRebootMsg = PhGetStringOrEmpty(context->RebootMessage); + failureActions.lpCommand = PhaGetDlgItemText(hwndDlg, IDC_RUNPROGRAM)->Buffer; + failureActions.cActions = 3; + failureActions.lpsaActions = actions; + + actions[0].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE)); + actions[1].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SECONDFAILURE)); + actions[2].Type = ComboBoxToServiceAction(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES)); + + restartServiceAfter = PhGetDialogItemValue(hwndDlg, IDC_RESTARTSERVICEAFTER) * 1000 * 60; + + for (i = 0; i < 3; i++) + { + switch (actions[i].Type) + { + case SC_ACTION_RESTART: + actions[i].Delay = restartServiceAfter; + enableRestart = TRUE; + break; + case SC_ACTION_REBOOT: + actions[i].Delay = context->RebootAfter; + break; + case SC_ACTION_RUN_COMMAND: + actions[i].Delay = 0; + break; + } + } + + // Try to save the changes. + + serviceHandle = PhOpenService( + serviceItem->Name->Buffer, + SERVICE_CHANGE_CONFIG | (enableRestart ? SERVICE_START : 0) // SC_ACTION_RESTART requires SERVICE_START + ); + + if (serviceHandle) + { + if (ChangeServiceConfig2( + serviceHandle, + SERVICE_CONFIG_FAILURE_ACTIONS, + &failureActions + )) + { + if (context->EnableFlagCheckBox) + { + SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; + + failureActionsFlag.fFailureActionsOnNonCrashFailures = + Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS)) == BST_CHECKED; + + ChangeServiceConfig2( + serviceHandle, + SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, + &failureActionsFlag + ); + } + + CloseServiceHandle(serviceHandle); + } + else + { + CloseServiceHandle(serviceHandle); + goto ErrorCase; + } + } + else + { + if (GetLastError() == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) + { + // Elevate using phsvc. + if (PhUiConnectToPhSvc(hwndDlg, FALSE)) + { + if (NT_SUCCESS(status = PhSvcCallChangeServiceConfig2( + serviceItem->Name->Buffer, + SERVICE_CONFIG_FAILURE_ACTIONS, + &failureActions + ))) + { + if (context->EnableFlagCheckBox) + { + SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; + + failureActionsFlag.fFailureActionsOnNonCrashFailures = + Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS)) == BST_CHECKED; + + PhSvcCallChangeServiceConfig2( + serviceItem->Name->Buffer, + SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, + &failureActionsFlag + ); + } + } + + PhUiDisconnectFromPhSvc(); + + if (!NT_SUCCESS(status)) + { + SetLastError(PhNtStatusToDosError(status)); + goto ErrorCase; + } + } + else + { + // User cancelled elevation. + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + } + else + { + goto ErrorCase; + } + } + + return TRUE; +ErrorCase: + { + PPH_STRING errorMessage = PhGetWin32Message(GetLastError()); + + if (PhShowMessage( + hwndDlg, + MB_ICONERROR | MB_RETRYCANCEL, + L"Unable to change service recovery information: %s", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + ) == IDRETRY) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + + PhClearReference(&errorMessage); + } + } + return TRUE; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK EspServiceRecovery2DlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return FALSE; +} + +INT_PTR CALLBACK RestartComputerDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_RECOVERY_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PSERVICE_RECOVERY_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetDialogItemValue(hwndDlg, IDC_RESTARTCOMPAFTER, context->RebootAfter / (1000 * 60), FALSE); // ms to min + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), context->RebootMessage ? BST_CHECKED : BST_UNCHECKED); + PhSetDialogItemText(hwndDlg, IDC_RESTARTMESSAGE, PhGetString(context->RebootMessage)); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_RESTARTCOMPAFTER)); + Edit_SetSel(GetDlgItem(hwndDlg, IDC_RESTARTCOMPAFTER), 0, -1); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + context->RebootAfter = PhGetDialogItemValue(hwndDlg, IDC_RESTARTCOMPAFTER) * 1000 * 60; + + if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE)) == BST_CHECKED) + PhMoveReference(&context->RebootMessage, PhGetWindowText(GetDlgItem(hwndDlg, IDC_RESTARTMESSAGE))); + else + PhClearReference(&context->RebootMessage); + + context->Dirty = TRUE; + + EndDialog(hwndDlg, IDOK); + } + break; + case IDC_USEDEFAULTMESSAGE: + { + PPH_STRING message; + PWSTR computerName; + ULONG bufferSize; + BOOLEAN allocated = TRUE; + + // Get the computer name. + + bufferSize = 64; + computerName = PhAllocate((bufferSize + 1) * sizeof(WCHAR)); + + if (!GetComputerName(computerName, &bufferSize)) + { + PhFree(computerName); + computerName = PhAllocate((bufferSize + 1) * sizeof(WCHAR)); + + if (!GetComputerName(computerName, &bufferSize)) + { + PhFree(computerName); + computerName = L"(unknown)"; + allocated = FALSE; + } + } + + // This message is exactly the same as the one in the Services console, + // except the double spaces are replaced by single spaces. + message = PhaFormatString( + L"Your computer is connected to the computer named %s. " + L"The %s service on %s has ended unexpectedly. " + L"%s will restart automatically, and then you can reestablish the connection.", + computerName, + context->ServiceItem->Name->Buffer, + computerName, + computerName + ); + PhSetDialogItemText(hwndDlg, IDC_RESTARTMESSAGE, message->Buffer); + + if (allocated) + PhFree(computerName); + + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), BST_CHECKED); + } + break; + case IDC_RESTARTMESSAGE: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) + { + // A zero length restart message disables it, so we might as well uncheck the box. + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLERESTARTMESSAGE), + PhGetWindowTextLength(GetDlgItem(hwndDlg, IDC_RESTARTMESSAGE)) != 0 ? BST_CHECKED : BST_UNCHECKED); + } + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedServices/resource.h b/plugins/ExtendedServices/resource.h index 4473b8eb0e67..b54d8cd6f2f8 100644 --- a/plugins/ExtendedServices/resource.h +++ b/plugins/ExtendedServices/resource.h @@ -1,73 +1,73 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by ExtendedServices.rc -// -#define IDD_SVCLIST 101 -#define IDD_SRVLIST 101 -#define ID_SERVICE_RESTART 101 -#define IDD_SRVRECOVERY 102 -#define ID_SERVICE_START 102 -#define IDD_RESTARTCOMP 103 -#define ID_SERVICE_STOP 103 -#define IDD_SRVRECOVERY2 104 -#define ID_SERVICE_GOTOSERVICE 104 -#define IDD_SRVPROGRESS 105 -#define ID_SERVICE_CONTINUE 105 -#define IDD_SRVOTHER 106 -#define ID_SERVICE_PAUSE 106 -#define IDD_OPTIONS 107 -#define IDD_SRVTRIGGER 108 -#define IDD_VALUE 109 -#define IDD_SRVTRIGGERS 110 -#define IDC_SERVICES_LAYOUT 1001 -#define IDC_MESSAGE 1002 -#define IDC_FIRSTFAILURE 1003 -#define IDC_SECONDFAILURE 1004 -#define IDC_SUBSEQUENTFAILURES 1005 -#define IDC_RESETFAILCOUNT 1006 -#define IDC_RESTARTSERVICEAFTER 1007 -#define IDC_RESTARTSERVICEAFTER_LABEL 1008 -#define IDC_RESTARTSERVICEAFTER_MINUTES 1009 -#define IDC_ENABLEFORERRORSTOPS 1011 -#define IDC_RESTARTCOMPUTEROPTIONS 1012 -#define IDC_RUNPROGRAM 1014 -#define IDC_BROWSE 1015 -#define IDC_RESTARTCOMPAFTER 1016 -#define IDC_ENABLERESTARTMESSAGE 1017 -#define IDC_RESTARTMESSAGE 1018 -#define IDC_RUNPROGRAM_GROUP 1019 -#define IDC_RUNPROGRAM_LABEL 1020 -#define IDC_RUNPROGRAM_INFO 1021 -#define IDC_USEDEFAULTMESSAGE 1022 -#define IDC_PROGRESS 1023 -#define IDC_PRESHUTDOWNTIMEOUT 1024 -#define IDC_PRIVILEGES 1025 -#define IDC_TRIGGERS 1026 -#define IDC_TRIGGERS_LABEL 1027 -#define IDC_ENABLESERVICESMENU 1028 -#define IDC_TYPE 1029 -#define IDC_SUBTYPE 1030 -#define IDC_SUBTYPECUSTOM 1031 -#define IDC_ACTION 1032 -#define IDC_LIST 1033 -#define IDC_NEW 1034 -#define IDC_EDIT 1035 -#define IDC_DELETE 1037 -#define IDC_VALUES 1038 -#define IDC_REMOVE 1042 -#define IDC_ADD 1043 -#define IDC_SERVICESID 1044 -#define IDC_SIDTYPE 1045 -#define IDC_COMBO2 1046 -#define IDC_PROTECTION 1046 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 111 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1047 -#define _APS_NEXT_SYMED_VALUE 107 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ExtendedServices.rc +// +#define IDD_SVCLIST 101 +#define IDD_SRVLIST 101 +#define ID_SERVICE_RESTART 101 +#define IDD_SRVRECOVERY 102 +#define ID_SERVICE_START 102 +#define IDD_RESTARTCOMP 103 +#define ID_SERVICE_STOP 103 +#define IDD_SRVRECOVERY2 104 +#define ID_SERVICE_GOTOSERVICE 104 +#define IDD_SRVPROGRESS 105 +#define ID_SERVICE_CONTINUE 105 +#define IDD_SRVOTHER 106 +#define ID_SERVICE_PAUSE 106 +#define IDD_OPTIONS 107 +#define IDD_SRVTRIGGER 108 +#define IDD_VALUE 109 +#define IDD_SRVTRIGGERS 110 +#define IDC_SERVICES_LAYOUT 1001 +#define IDC_MESSAGE 1002 +#define IDC_FIRSTFAILURE 1003 +#define IDC_SECONDFAILURE 1004 +#define IDC_SUBSEQUENTFAILURES 1005 +#define IDC_RESETFAILCOUNT 1006 +#define IDC_RESTARTSERVICEAFTER 1007 +#define IDC_RESTARTSERVICEAFTER_LABEL 1008 +#define IDC_RESTARTSERVICEAFTER_MINUTES 1009 +#define IDC_ENABLEFORERRORSTOPS 1011 +#define IDC_RESTARTCOMPUTEROPTIONS 1012 +#define IDC_RUNPROGRAM 1014 +#define IDC_BROWSE 1015 +#define IDC_RESTARTCOMPAFTER 1016 +#define IDC_ENABLERESTARTMESSAGE 1017 +#define IDC_RESTARTMESSAGE 1018 +#define IDC_RUNPROGRAM_GROUP 1019 +#define IDC_RUNPROGRAM_LABEL 1020 +#define IDC_RUNPROGRAM_INFO 1021 +#define IDC_USEDEFAULTMESSAGE 1022 +#define IDC_PROGRESS 1023 +#define IDC_PRESHUTDOWNTIMEOUT 1024 +#define IDC_PRIVILEGES 1025 +#define IDC_TRIGGERS 1026 +#define IDC_TRIGGERS_LABEL 1027 +#define IDC_ENABLESERVICESMENU 1028 +#define IDC_TYPE 1029 +#define IDC_SUBTYPE 1030 +#define IDC_SUBTYPECUSTOM 1031 +#define IDC_ACTION 1032 +#define IDC_LIST 1033 +#define IDC_NEW 1034 +#define IDC_EDIT 1035 +#define IDC_DELETE 1037 +#define IDC_VALUES 1038 +#define IDC_REMOVE 1042 +#define IDC_ADD 1043 +#define IDC_SERVICESID 1044 +#define IDC_SIDTYPE 1045 +#define IDC_COMBO2 1046 +#define IDC_PROTECTION 1046 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1047 +#define _APS_NEXT_SYMED_VALUE 107 +#endif +#endif diff --git a/plugins/ExtendedServices/srvprgrs.c b/plugins/ExtendedServices/srvprgrs.c index 116957accf64..0a505130c7eb 100644 --- a/plugins/ExtendedServices/srvprgrs.c +++ b/plugins/ExtendedServices/srvprgrs.c @@ -1,151 +1,155 @@ -/* - * Process Hacker Extended Services - - * progress dialog - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -typedef struct _RESTART_SERVICE_CONTEXT -{ - PPH_SERVICE_ITEM ServiceItem; - SC_HANDLE ServiceHandle; - BOOLEAN Starting; - BOOLEAN DisableTimer; -} RESTART_SERVICE_CONTEXT, *PRESTART_SERVICE_CONTEXT; - -INT_PTR CALLBACK EspRestartServiceDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PRESTART_SERVICE_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PRESTART_SERVICE_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PRESTART_SERVICE_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - // TODO: Use the progress information. - PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); - SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); - - SetDlgItemText(hwndDlg, IDC_MESSAGE, PhaFormatString(L"Attempting to stop %s...", context->ServiceItem->Name->Buffer)->Buffer); - - if (PhUiStopService(hwndDlg, context->ServiceItem)) - { - SetTimer(hwndDlg, 1, 250, NULL); - } - else - { - EndDialog(hwndDlg, IDCANCEL); - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - EndDialog(hwndDlg, IDCANCEL); - } - break; - } - } - break; - case WM_TIMER: - { - if (wParam == 1 && !context->DisableTimer) - { - SERVICE_STATUS serviceStatus; - - if (QueryServiceStatus(context->ServiceHandle, &serviceStatus)) - { - if (!context->Starting && serviceStatus.dwCurrentState == SERVICE_STOPPED) - { - // The service is stopped, so start the service now. - - SetDlgItemText(hwndDlg, IDC_MESSAGE, - PhaFormatString(L"Attempting to start %s...", context->ServiceItem->Name->Buffer)->Buffer); - context->DisableTimer = TRUE; - - if (PhUiStartService(hwndDlg, context->ServiceItem)) - { - context->DisableTimer = FALSE; - context->Starting = TRUE; - } - else - { - EndDialog(hwndDlg, IDCANCEL); - } - } - else if (context->Starting && serviceStatus.dwCurrentState == SERVICE_RUNNING) - { - EndDialog(hwndDlg, IDOK); - } - } - } - } - break; - } - - return FALSE; -} - -VOID EsRestartServiceWithProgress( - _In_ HWND hWnd, - _In_ PPH_SERVICE_ITEM ServiceItem, - _In_ SC_HANDLE ServiceHandle - ) -{ - RESTART_SERVICE_CONTEXT context; - - context.ServiceItem = ServiceItem; - context.ServiceHandle = ServiceHandle; - context.Starting = FALSE; - context.DisableTimer = FALSE; - - DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_SRVPROGRESS), - hWnd, - EspRestartServiceDlgProc, - (LPARAM)&context - ); +/* + * Process Hacker Extended Services - + * progress dialog + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +typedef struct _RESTART_SERVICE_CONTEXT +{ + PPH_SERVICE_ITEM ServiceItem; + SC_HANDLE ServiceHandle; + BOOLEAN Starting; + BOOLEAN DisableTimer; +} RESTART_SERVICE_CONTEXT, *PRESTART_SERVICE_CONTEXT; + +INT_PTR CALLBACK EspRestartServiceDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PRESTART_SERVICE_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PRESTART_SERVICE_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + // TODO: Use the progress information. + PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE); + SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75); + + PhSetDialogItemText(hwndDlg, IDC_MESSAGE, PhaFormatString(L"Attempting to stop %s...", context->ServiceItem->Name->Buffer)->Buffer); + + if (PhUiStopService(hwndDlg, context->ServiceItem)) + { + SetTimer(hwndDlg, 1, 250, NULL); + } + else + { + EndDialog(hwndDlg, IDCANCEL); + } + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + { + KillTimer(hwndDlg, 1); + + EndDialog(hwndDlg, IDCANCEL); + } + break; + } + } + break; + case WM_TIMER: + { + if (wParam == 1 && !context->DisableTimer) + { + SERVICE_STATUS serviceStatus; + + if (QueryServiceStatus(context->ServiceHandle, &serviceStatus)) + { + if (!context->Starting && serviceStatus.dwCurrentState == SERVICE_STOPPED) + { + // The service is stopped, so start the service now. + + PhSetDialogItemText(hwndDlg, IDC_MESSAGE, + PhaFormatString(L"Attempting to start %s...", context->ServiceItem->Name->Buffer)->Buffer); + context->DisableTimer = TRUE; + + if (PhUiStartService(hwndDlg, context->ServiceItem)) + { + context->DisableTimer = FALSE; + context->Starting = TRUE; + } + else + { + EndDialog(hwndDlg, IDCANCEL); + } + } + else if (context->Starting && serviceStatus.dwCurrentState == SERVICE_RUNNING) + { + EndDialog(hwndDlg, IDOK); + } + } + } + } + break; + } + + return FALSE; +} + +VOID EsRestartServiceWithProgress( + _In_ HWND hWnd, + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ SC_HANDLE ServiceHandle + ) +{ + RESTART_SERVICE_CONTEXT context; + + context.ServiceItem = ServiceItem; + context.ServiceHandle = ServiceHandle; + context.Starting = FALSE; + context.DisableTimer = FALSE; + + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_SRVPROGRESS), + hWnd, + EspRestartServiceDlgProc, + (LPARAM)&context + ); } \ No newline at end of file diff --git a/plugins/ExtendedServices/trigger.c b/plugins/ExtendedServices/trigger.c index b60fe1134721..33360c2dc952 100644 --- a/plugins/ExtendedServices/trigger.c +++ b/plugins/ExtendedServices/trigger.c @@ -1,1720 +1,1724 @@ -/* - * Process Hacker Extended Services - - * trigger editor - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -typedef struct _ES_TRIGGER_DATA -{ - ULONG Type; - union - { - PPH_STRING String; - struct - { - PVOID Binary; - ULONG BinaryLength; - }; - UCHAR Byte; - ULONG64 UInt64; - }; -} ES_TRIGGER_DATA, *PES_TRIGGER_DATA; - -typedef struct _ES_TRIGGER_INFO -{ - ULONG Type; - PGUID Subtype; - ULONG Action; - PPH_LIST DataList; - GUID SubtypeBuffer; -} ES_TRIGGER_INFO, *PES_TRIGGER_INFO; - -typedef struct _ES_TRIGGER_CONTEXT -{ - PPH_SERVICE_ITEM ServiceItem; - HWND WindowHandle; - HWND TriggersLv; - BOOLEAN Dirty; - ULONG InitialNumberOfTriggers; - PPH_LIST InfoList; - - // Trigger dialog box - PES_TRIGGER_INFO EditingInfo; - ULONG LastSelectedType; - PPH_STRING LastCustomSubType; - - // Value dialog box - PPH_STRING EditingValue; -} ES_TRIGGER_CONTEXT, *PES_TRIGGER_CONTEXT; - -typedef struct _TYPE_ENTRY -{ - ULONG Type; - PWSTR Name; -} TYPE_ENTRY, PTYPE_ENTRY; - -typedef struct _SUBTYPE_ENTRY -{ - ULONG Type; - PGUID Guid; - PWSTR Name; -} SUBTYPE_ENTRY, PSUBTYPE_ENTRY; - -typedef struct _ETW_PUBLISHER_ENTRY -{ - PPH_STRING PublisherName; - GUID Guid; -} ETW_PUBLISHER_ENTRY, *PETW_PUBLISHER_ENTRY; - -INT_PTR CALLBACK EspServiceTriggerDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK ValueDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static GUID NetworkManagerFirstIpAddressArrivalGuid = { 0x4f27f2de, 0x14e2, 0x430b, { 0xa5, 0x49, 0x7c, 0xd4, 0x8c, 0xbc, 0x82, 0x45 } }; -static GUID NetworkManagerLastIpAddressRemovalGuid = { 0xcc4ba62a, 0x162e, 0x4648, { 0x84, 0x7a, 0xb6, 0xbd, 0xf9, 0x93, 0xe3, 0x35 } }; -static GUID DomainJoinGuid = { 0x1ce20aba, 0x9851, 0x4421, { 0x94, 0x30, 0x1d, 0xde, 0xb7, 0x66, 0xe8, 0x09 } }; -static GUID DomainLeaveGuid = { 0xddaf516e, 0x58c2, 0x4866, { 0x95, 0x74, 0xc3, 0xb6, 0x15, 0xd4, 0x2e, 0xa1 } }; -static GUID FirewallPortOpenGuid = { 0xb7569e07, 0x8421, 0x4ee0, { 0xad, 0x10, 0x86, 0x91, 0x5a, 0xfd, 0xad, 0x09 } }; -static GUID FirewallPortCloseGuid = { 0xa144ed38, 0x8e12, 0x4de4, { 0x9d, 0x96, 0xe6, 0x47, 0x40, 0xb1, 0xa5, 0x24 } }; -static GUID MachinePolicyPresentGuid = { 0x659fcae6, 0x5bdb, 0x4da9, { 0xb1, 0xff, 0xca, 0x2a, 0x17, 0x8d, 0x46, 0xe0 } }; -static GUID UserPolicyPresentGuid = { 0x54fb46c8, 0xf089, 0x464c, { 0xb1, 0xfd, 0x59, 0xd1, 0xb6, 0x2c, 0x3b, 0x50 } }; -static GUID RpcInterfaceEventGuid = { 0xbc90d167, 0x9470, 0x4139, { 0xa9, 0xba, 0xbe, 0x0b, 0xbb, 0xf5, 0xb7, 0x4d } }; -static GUID NamedPipeEventGuid = { 0x1f81d131, 0x3fac, 0x4537, { 0x9e, 0x0c, 0x7e, 0x7b, 0x0c, 0x2f, 0x4b, 0x55 } }; -static GUID SubTypeUnknownGuid; // dummy - -static TYPE_ENTRY TypeEntries[] = -{ - { SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL, L"Device interface arrival" }, - { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, L"IP address availability" }, - { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, L"Domain join" }, - { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, L"Firewall port event" }, - { SERVICE_TRIGGER_TYPE_GROUP_POLICY, L"Group policy" }, - { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, L"Network endpoint" }, - { SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE, L"Custom system state change" }, - { SERVICE_TRIGGER_TYPE_CUSTOM, L"Custom" } -}; - -static SUBTYPE_ENTRY SubTypeEntries[] = -{ - { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, NULL, L"IP address" }, - { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerFirstIpAddressArrivalGuid, L"IP address: First arrival" }, - { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerLastIpAddressRemovalGuid, L"IP address: Last removal" }, - { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &SubTypeUnknownGuid, L"IP address: Unknown" }, - { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, NULL, L"Domain" }, - { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainJoinGuid, L"Domain: Join" }, - { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainLeaveGuid, L"Domain: Leave" }, - { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &SubTypeUnknownGuid, L"Domain: Unknown" }, - { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, NULL, L"Firewall port" }, - { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortOpenGuid, L"Firewall port: Open" }, - { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortCloseGuid, L"Firewall port: Close" }, - { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &SubTypeUnknownGuid, L"Firewall port: Unknown" }, - { SERVICE_TRIGGER_TYPE_GROUP_POLICY, NULL, L"Group policy change" }, - { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &MachinePolicyPresentGuid, L"Group policy change: Machine" }, - { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &UserPolicyPresentGuid, L"Group policy change: User" }, - { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &SubTypeUnknownGuid, L"Group policy change: Unknown" }, - { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, NULL, L"Network endpoint" }, - { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &RpcInterfaceEventGuid, L"Network endpoint: RPC interface" }, - { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &NamedPipeEventGuid, L"Network endpoint: Named pipe" }, - { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &SubTypeUnknownGuid, L"Network endpoint: Unknown" } -}; - -static PH_STRINGREF PublishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\"); - -PES_TRIGGER_DATA EspCreateTriggerData( - _In_opt_ PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM DataItem - ) -{ - PES_TRIGGER_DATA data; - - data = PhAllocate(sizeof(ES_TRIGGER_DATA)); - memset(data, 0, sizeof(ES_TRIGGER_DATA)); - - if (DataItem) - { - data->Type = DataItem->dwDataType; - - if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) - { - if (DataItem->pData && DataItem->cbData >= 2) - data->String = PhCreateStringEx((PWSTR)DataItem->pData, DataItem->cbData - 2); // exclude final null terminator - else - data->String = PhReferenceEmptyString(); - } - else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) - { - data->BinaryLength = DataItem->cbData; - data->Binary = PhAllocateCopy(DataItem->pData, DataItem->cbData); - } - else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL) - { - if (DataItem->cbData == sizeof(UCHAR)) - data->Byte = *(PUCHAR)DataItem->pData; - } - else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL) - { - if (DataItem->cbData == sizeof(ULONG64)) - data->UInt64 = *(PULONG64)DataItem->pData; - } - } - - return data; -} - -PES_TRIGGER_DATA EspCloneTriggerData( - _In_ PES_TRIGGER_DATA Data - ) -{ - PES_TRIGGER_DATA newData; - - newData = PhAllocateCopy(Data, sizeof(ES_TRIGGER_DATA)); - - if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) - { - if (newData->String) - newData->String = PhDuplicateString(newData->String); - } - else if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) - { - if (newData->Binary) - newData->Binary = PhAllocateCopy(newData->Binary, newData->BinaryLength); - } - - return newData; -} - -VOID EspDestroyTriggerData( - _In_ PES_TRIGGER_DATA Data - ) -{ - if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) - { - if (Data->String) - PhDereferenceObject(Data->String); - } - else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) - { - if (Data->Binary) - PhFree(Data->Binary); - } - - PhFree(Data); -} - -PES_TRIGGER_INFO EspCreateTriggerInfo( - _In_opt_ PSERVICE_TRIGGER Trigger - ) -{ - PES_TRIGGER_INFO info; - - info = PhAllocate(sizeof(ES_TRIGGER_INFO)); - memset(info, 0, sizeof(ES_TRIGGER_INFO)); - - if (Trigger) - { - info->Type = Trigger->dwTriggerType; - - if (Trigger->pTriggerSubtype) - { - info->SubtypeBuffer = *Trigger->pTriggerSubtype; - info->Subtype = &info->SubtypeBuffer; - } - - info->Action = Trigger->dwAction; - - if ( - info->Type == SERVICE_TRIGGER_TYPE_CUSTOM || - info->Type == SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL || - info->Type == SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT || - info->Type == SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT - ) - { - ULONG i; - - info->DataList = PhCreateList(Trigger->cDataItems); - - for (i = 0; i < Trigger->cDataItems; i++) - { - PhAddItemList(info->DataList, EspCreateTriggerData(&Trigger->pDataItems[i])); - } - } - } - - return info; -} - -PES_TRIGGER_INFO EspCloneTriggerInfo( - _In_ PES_TRIGGER_INFO Info - ) -{ - PES_TRIGGER_INFO newInfo; - - newInfo = PhAllocateCopy(Info, sizeof(ES_TRIGGER_INFO)); - - if (newInfo->Subtype == &Info->SubtypeBuffer) - newInfo->Subtype = &newInfo->SubtypeBuffer; - - if (newInfo->DataList) - { - ULONG i; - - newInfo->DataList = PhCreateList(Info->DataList->AllocatedCount); - newInfo->DataList->Count = Info->DataList->Count; - - for (i = 0; i < Info->DataList->Count; i++) - newInfo->DataList->Items[i] = EspCloneTriggerData(Info->DataList->Items[i]); - } - - return newInfo; -} - -VOID EspDestroyTriggerInfo( - _In_ PES_TRIGGER_INFO Info - ) -{ - if (Info->DataList) - { - ULONG i; - - for (i = 0; i < Info->DataList->Count; i++) - { - EspDestroyTriggerData(Info->DataList->Items[i]); - } - - PhDereferenceObject(Info->DataList); - } - - PhFree(Info); -} - -VOID EspClearTriggerInfoList( - _In_ PPH_LIST List - ) -{ - ULONG i; - - for (i = 0; i < List->Count; i++) - { - EspDestroyTriggerInfo(List->Items[i]); - } - - PhClearList(List); -} - -struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext( - _In_ PPH_SERVICE_ITEM ServiceItem, - _In_ HWND WindowHandle, - _In_ HWND TriggersLv - ) -{ - PES_TRIGGER_CONTEXT context; - - context = PhAllocate(sizeof(ES_TRIGGER_CONTEXT)); - memset(context, 0, sizeof(ES_TRIGGER_CONTEXT)); - context->ServiceItem = ServiceItem; - context->WindowHandle = WindowHandle; - context->TriggersLv = TriggersLv; - context->InfoList = PhCreateList(4); - - PhSetListViewStyle(TriggersLv, FALSE, TRUE); - PhSetControlTheme(TriggersLv, L"explorer"); - PhAddListViewColumn(TriggersLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Trigger"); - PhAddListViewColumn(TriggersLv, 1, 1, 1, LVCFMT_LEFT, 60, L"Action"); - PhSetExtendedListView(TriggersLv); - - EnableWindow(GetDlgItem(WindowHandle, IDC_EDIT), FALSE); - EnableWindow(GetDlgItem(WindowHandle, IDC_DELETE), FALSE); - - return context; -} - -VOID EsDestroyServiceTriggerContext( - _In_ struct _ES_TRIGGER_CONTEXT *Context - ) -{ - ULONG i; - - for (i = 0; i < Context->InfoList->Count; i++) - { - EspDestroyTriggerInfo(Context->InfoList->Items[i]); - } - - PhDereferenceObject(Context->InfoList); - PhFree(Context); -} - -PPH_STRING EspLookupEtwPublisherName( - _In_ PGUID Guid - ) -{ - PPH_STRING guidString; - PPH_STRING keyName; - HANDLE keyHandle; - PPH_STRING publisherName = NULL; - - // Copied from ProcessHacker\hndlinfo.c. - - guidString = PhFormatGuid(Guid); - - keyName = PhConcatStringRef2(&PublishersKeyName, &guidString->sr); - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName->sr, - 0 - ))) - { - publisherName = PhQueryRegistryString(keyHandle, NULL); - - if (publisherName && publisherName->Length == 0) - { - PhDereferenceObject(publisherName); - publisherName = NULL; - } - - NtClose(keyHandle); - } - - PhDereferenceObject(keyName); - - if (publisherName) - { - PhDereferenceObject(guidString); - return publisherName; - } - else - { - return guidString; - } -} - -BOOLEAN EspEnumerateEtwPublishers( - _Out_ PETW_PUBLISHER_ENTRY *Entries, - _Out_ PULONG NumberOfEntries - ) -{ - NTSTATUS status; - HANDLE publishersKeyHandle; - ULONG index; - PKEY_BASIC_INFORMATION buffer; - ULONG bufferSize; - PETW_PUBLISHER_ENTRY entries; - ULONG numberOfEntries; - ULONG allocatedEntries; - - if (!NT_SUCCESS(PhOpenKey( - &publishersKeyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &PublishersKeyName, - 0 - ))) - { - return FALSE; - } - - numberOfEntries = 0; - allocatedEntries = 256; - entries = PhAllocate(allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY)); - - index = 0; - bufferSize = 0x100; - buffer = PhAllocate(0x100); - - while (TRUE) - { - status = NtEnumerateKey( - publishersKeyHandle, - index, - KeyBasicInformation, - buffer, - bufferSize, - &bufferSize - ); - - if (NT_SUCCESS(status)) - { - UNICODE_STRING nameUs; - PH_STRINGREF name; - HANDLE keyHandle; - GUID guid; - PPH_STRING publisherName; - - nameUs.Buffer = buffer->Name; - nameUs.Length = (USHORT)buffer->NameLength; - name.Buffer = buffer->Name; - name.Length = buffer->NameLength; - - // Make sure this is a valid publisher key. - if (NT_SUCCESS(RtlGUIDFromString(&nameUs, &guid))) - { - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - publishersKeyHandle, - &name, - 0 - ))) - { - publisherName = PhQueryRegistryString(keyHandle, NULL); - - if (publisherName) - { - if (publisherName->Length != 0) - { - PETW_PUBLISHER_ENTRY entry; - - if (numberOfEntries == allocatedEntries) - { - allocatedEntries *= 2; - entries = PhReAllocate(entries, allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY)); - } - - entry = &entries[numberOfEntries++]; - entry->PublisherName = publisherName; - entry->Guid = guid; - } - else - { - PhDereferenceObject(publisherName); - } - } - - NtClose(keyHandle); - } - } - - index++; - } - else if (status == STATUS_NO_MORE_ENTRIES) - { - break; - } - else if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) - { - PhFree(buffer); - buffer = PhAllocate(bufferSize); - } - else - { - break; - } - } - - PhFree(buffer); - NtClose(publishersKeyHandle); - - *Entries = entries; - *NumberOfEntries = numberOfEntries; - - return TRUE; -} - -BOOLEAN EspLookupEtwPublisherGuid( - _In_ PPH_STRINGREF PublisherName, - _Out_ PGUID Guid - ) -{ - BOOLEAN result; - PETW_PUBLISHER_ENTRY entries; - ULONG numberOfEntries; - ULONG i; - - if (!EspEnumerateEtwPublishers(&entries, &numberOfEntries)) - return FALSE; - - result = FALSE; - - for (i = 0; i < numberOfEntries; i++) - { - if (!result && PhEqualStringRef(&entries[i].PublisherName->sr, PublisherName, TRUE)) - { - *Guid = entries[i].Guid; - result = TRUE; - } - - PhDereferenceObject(entries[i].PublisherName); - } - - PhFree(entries); - - return result; -} - -VOID EspFormatTriggerInfo( - _In_ PES_TRIGGER_INFO Info, - _Out_ PWSTR *TriggerString, - _Out_ PWSTR *ActionString, - _Out_ PPH_STRING *StringUsed - ) -{ - PPH_STRING stringUsed = NULL; - PWSTR triggerString = NULL; - PWSTR actionString; - ULONG i; - BOOLEAN typeFound; - BOOLEAN subTypeFound; - - switch (Info->Type) - { - case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL: - { - PPH_STRING guidString; - - if (!Info->Subtype) - { - triggerString = L"Device interface arrival"; - } - else - { - guidString = PhFormatGuid(Info->Subtype); - stringUsed = PhConcatStrings2(L"Device interface arrival: ", guidString->Buffer); - triggerString = stringUsed->Buffer; - } - } - break; - case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE: - { - PPH_STRING guidString; - - if (!Info->Subtype) - { - triggerString = L"Custom system state change"; - } - else - { - guidString = PhFormatGuid(Info->Subtype); - stringUsed = PhConcatStrings2(L"Custom system state change: ", guidString->Buffer); - triggerString = stringUsed->Buffer; - } - } - break; - case SERVICE_TRIGGER_TYPE_CUSTOM: - { - if (Info->Subtype) - { - PPH_STRING publisherName; - - // Try to lookup the publisher name from the GUID. - publisherName = EspLookupEtwPublisherName(Info->Subtype); - stringUsed = PhConcatStrings2(L"Custom: ", publisherName->Buffer); - PhDereferenceObject(publisherName); - triggerString = stringUsed->Buffer; - } - else - { - triggerString = L"Custom"; - } - } - break; - default: - { - typeFound = FALSE; - subTypeFound = FALSE; - - for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) - { - if (SubTypeEntries[i].Type == Info->Type) - { - typeFound = TRUE; - - if (!Info->Subtype && !SubTypeEntries[i].Guid) - { - subTypeFound = TRUE; - triggerString = SubTypeEntries[i].Name; - break; - } - else if (Info->Subtype && SubTypeEntries[i].Guid && memcmp(Info->Subtype, SubTypeEntries[i].Guid, sizeof(GUID)) == 0) - { - subTypeFound = TRUE; - triggerString = SubTypeEntries[i].Name; - break; - } - else if (!subTypeFound && SubTypeEntries[i].Guid == &SubTypeUnknownGuid) - { - triggerString = SubTypeEntries[i].Name; - break; - } - } - } - - if (!typeFound) - { - triggerString = L"Unknown"; - } - } - break; - } - - switch (Info->Action) - { - case SERVICE_TRIGGER_ACTION_SERVICE_START: - actionString = L"Start"; - break; - case SERVICE_TRIGGER_ACTION_SERVICE_STOP: - actionString = L"Stop"; - break; - default: - actionString = L"Unknown"; - break; - } - - *TriggerString = triggerString; - *ActionString = actionString; - *StringUsed = stringUsed; -} - -VOID EsLoadServiceTriggerInfo( - _In_ struct _ES_TRIGGER_CONTEXT *Context, - _In_ SC_HANDLE ServiceHandle - ) -{ - PSERVICE_TRIGGER_INFO triggerInfo; - ULONG i; - - EspClearTriggerInfoList(Context->InfoList); - - if (triggerInfo = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_TRIGGER_INFO)) - { - for (i = 0; i < triggerInfo->cTriggers; i++) - { - PSERVICE_TRIGGER trigger = &triggerInfo->pTriggers[i]; - PES_TRIGGER_INFO info; - PWSTR triggerString; - PWSTR actionString; - PPH_STRING stringUsed; - INT lvItemIndex; - - info = EspCreateTriggerInfo(trigger); - PhAddItemList(Context->InfoList, info); - - EspFormatTriggerInfo(info, &triggerString, &actionString, &stringUsed); - - lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, info); - PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString); - - if (stringUsed) - PhDereferenceObject(stringUsed); - } - - Context->InitialNumberOfTriggers = triggerInfo->cTriggers; - - ExtendedListView_SortItems(Context->TriggersLv); - - PhFree(triggerInfo); - } -} - -BOOLEAN EsSaveServiceTriggerInfo( - _In_ struct _ES_TRIGGER_CONTEXT *Context, - _Out_ PULONG Win32Result - ) -{ - BOOLEAN result = TRUE; - PH_AUTO_POOL autoPool; - SC_HANDLE serviceHandle; - SERVICE_TRIGGER_INFO triggerInfo; - ULONG i; - ULONG j; - - if (!Context->Dirty) - return TRUE; - - // Do not try to change trigger information if we didn't have any triggers before and we don't - // have any now. ChangeServiceConfig2 returns an error in this situation. - if (Context->InitialNumberOfTriggers == 0 && Context->InfoList->Count == 0) - return TRUE; - - PhInitializeAutoPool(&autoPool); - - memset(&triggerInfo, 0, sizeof(SERVICE_TRIGGER_INFO)); - triggerInfo.cTriggers = Context->InfoList->Count; - - // pTriggers needs to be NULL when there are no triggers. - if (Context->InfoList->Count != 0) - { - triggerInfo.pTriggers = PH_AUTO(PhCreateAlloc(Context->InfoList->Count * sizeof(SERVICE_TRIGGER))); - memset(triggerInfo.pTriggers, 0, Context->InfoList->Count * sizeof(SERVICE_TRIGGER)); - - for (i = 0; i < Context->InfoList->Count; i++) - { - PSERVICE_TRIGGER trigger = &triggerInfo.pTriggers[i]; - PES_TRIGGER_INFO info = Context->InfoList->Items[i]; - - trigger->dwTriggerType = info->Type; - trigger->dwAction = info->Action; - trigger->pTriggerSubtype = info->Subtype; - - if (info->DataList && info->DataList->Count != 0) - { - trigger->cDataItems = info->DataList->Count; - trigger->pDataItems = PH_AUTO(PhCreateAlloc(info->DataList->Count * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM))); - - for (j = 0; j < info->DataList->Count; j++) - { - PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem = &trigger->pDataItems[j]; - PES_TRIGGER_DATA data = info->DataList->Items[j]; - - dataItem->dwDataType = data->Type; - - if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) - { - dataItem->cbData = (ULONG)data->String->Length + 2; // include null terminator - dataItem->pData = (PBYTE)data->String->Buffer; - } - else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) - { - dataItem->cbData = data->BinaryLength; - dataItem->pData = data->Binary; - } - else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL) - { - dataItem->cbData = sizeof(UCHAR); - dataItem->pData = (PBYTE)&data->Byte; - } - else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL) - { - dataItem->cbData = sizeof(ULONG64); - dataItem->pData = (PBYTE)&data->UInt64; - } - } - } - } - } - - if (serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG)) - { - if (!ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo)) - { - result = FALSE; - *Win32Result = GetLastError(); - } - - CloseServiceHandle(serviceHandle); - } - else - { - result = FALSE; - *Win32Result = GetLastError(); - - if (*Win32Result == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) - { - // Elevate using phsvc. - if (PhUiConnectToPhSvc(Context->WindowHandle, FALSE)) - { - NTSTATUS status; - - result = TRUE; - - if (!NT_SUCCESS(status = PhSvcCallChangeServiceConfig2(Context->ServiceItem->Name->Buffer, - SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo))) - { - result = FALSE; - *Win32Result = PhNtStatusToDosError(status); - } - - PhUiDisconnectFromPhSvc(); - } - else - { - // User cancelled elevation. - *Win32Result = ERROR_CANCELLED; - } - } - } - - PhDeleteAutoPool(&autoPool); - - return result; -} - -LOGICAL EspSetListViewItemParam( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ PVOID Param - ) -{ - LVITEM item; - - item.mask = LVIF_PARAM; - item.iItem = Index; - item.iSubItem = 0; - item.lParam = (LPARAM)Param; - - return ListView_SetItem(ListViewHandle, &item); -} - -VOID EsHandleEventServiceTrigger( - _In_ struct _ES_TRIGGER_CONTEXT *Context, - _In_ ULONG Event - ) -{ - switch (Event) - { - case ES_TRIGGER_EVENT_NEW: - { - Context->EditingInfo = EspCreateTriggerInfo(NULL); - Context->EditingInfo->Type = SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY; - Context->EditingInfo->SubtypeBuffer = NetworkManagerFirstIpAddressArrivalGuid; - Context->EditingInfo->Subtype = &Context->EditingInfo->SubtypeBuffer; - Context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START; - - if (DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_SRVTRIGGER), - Context->WindowHandle, - EspServiceTriggerDlgProc, - (LPARAM)Context - ) == IDOK) - { - PWSTR triggerString; - PWSTR actionString; - PPH_STRING stringUsed; - INT lvItemIndex; - - Context->Dirty = TRUE; - PhAddItemList(Context->InfoList, Context->EditingInfo); - - EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed); - - lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, Context->EditingInfo); - PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString); - - if (stringUsed) - PhDereferenceObject(stringUsed); - } - else - { - EspDestroyTriggerInfo(Context->EditingInfo); - } - - Context->EditingInfo = NULL; - } - break; - case ES_TRIGGER_EVENT_EDIT: - { - INT lvItemIndex; - PES_TRIGGER_INFO info; - ULONG index; - - lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED); - - if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info)) - { - index = PhFindItemList(Context->InfoList, info); - - if (index != -1) - { - Context->EditingInfo = EspCloneTriggerInfo(info); - - if (DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_SRVTRIGGER), - Context->WindowHandle, - EspServiceTriggerDlgProc, - (LPARAM)Context - ) == IDOK) - { - PWSTR triggerString; - PWSTR actionString; - PPH_STRING stringUsed; - - Context->Dirty = TRUE; - EspDestroyTriggerInfo(Context->InfoList->Items[index]); - Context->InfoList->Items[index] = Context->EditingInfo; - - EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed); - PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 0, triggerString); - PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString); - - if (stringUsed) - PhDereferenceObject(stringUsed); - - EspSetListViewItemParam(Context->TriggersLv, lvItemIndex, Context->EditingInfo); - } - else - { - EspDestroyTriggerInfo(Context->EditingInfo); - } - - Context->EditingInfo = NULL; - } - } - } - break; - case ES_TRIGGER_EVENT_DELETE: - { - INT lvItemIndex; - PES_TRIGGER_INFO info; - ULONG index; - - lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED); - - if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info)) - { - index = PhFindItemList(Context->InfoList, info); - - if (index != -1) - { - EspDestroyTriggerInfo(info); - PhRemoveItemList(Context->InfoList, index); - PhRemoveListViewItem(Context->TriggersLv, lvItemIndex); - } - } - - Context->Dirty = TRUE; - } - break; - case ES_TRIGGER_EVENT_SELECTIONCHANGED: - { - ULONG selectedCount; - - selectedCount = ListView_GetSelectedCount(Context->TriggersLv); - - EnableWindow(GetDlgItem(Context->WindowHandle, IDC_EDIT), selectedCount == 1); - EnableWindow(GetDlgItem(Context->WindowHandle, IDC_DELETE), selectedCount == 1); - } - break; - } -} - -ULONG EspTriggerTypeStringToInteger( - _In_ PWSTR String - ) -{ - ULONG i; - - for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++) - { - if (PhEqualStringZ(TypeEntries[i].Name, String, FALSE)) - return TypeEntries[i].Type; - } - - return 0; -} - -static int __cdecl EtwPublisherByNameCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PETW_PUBLISHER_ENTRY entry1 = (PETW_PUBLISHER_ENTRY)elem1; - PETW_PUBLISHER_ENTRY entry2 = (PETW_PUBLISHER_ENTRY)elem2; - - return PhCompareString(entry1->PublisherName, entry2->PublisherName, TRUE); -} - -VOID EspFixServiceTriggerControls( - _In_ HWND hwndDlg, - _In_ PES_TRIGGER_CONTEXT Context - ) -{ - HWND typeComboBox; - HWND subTypeComboBox; - ULONG i; - PPH_STRING selectedTypeString; - ULONG type; - PPH_STRING selectedSubTypeString; - - typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE); - subTypeComboBox = GetDlgItem(hwndDlg, IDC_SUBTYPE); - - selectedTypeString = PhGetWindowText(typeComboBox); - type = EspTriggerTypeStringToInteger(selectedTypeString->Buffer); - PhDereferenceObject(selectedTypeString); - - if (Context->LastSelectedType != type) - { - // Change the contents of the subtype combo box based on the type. - - ComboBox_ResetContent(subTypeComboBox); - - switch (type) - { - case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL: - { - ComboBox_AddString(subTypeComboBox, L"Custom"); - } - break; - case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE: - { - ComboBox_AddString(subTypeComboBox, L"Custom"); - } - break; - case SERVICE_TRIGGER_TYPE_CUSTOM: - { - PETW_PUBLISHER_ENTRY entries; - ULONG numberOfEntries; - ULONG i; - - ComboBox_AddString(subTypeComboBox, L"Custom"); - - // Display a list of publishers. - if (EspEnumerateEtwPublishers(&entries, &numberOfEntries)) - { - // Sort the list by name. - qsort(entries, numberOfEntries, sizeof(ETW_PUBLISHER_ENTRY), EtwPublisherByNameCompareFunction); - - for (i = 0; i < numberOfEntries; i++) - { - ComboBox_AddString(subTypeComboBox, entries[i].PublisherName->Buffer); - PhDereferenceObject(entries[i].PublisherName); - } - - PhFree(entries); - } - } - break; - default: - for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) - { - if (SubTypeEntries[i].Type == type && SubTypeEntries[i].Guid && SubTypeEntries[i].Guid != &SubTypeUnknownGuid) - { - ComboBox_AddString(subTypeComboBox, SubTypeEntries[i].Name); - } - } - break; - } - - ComboBox_SetCurSel(subTypeComboBox, 0); - - Context->LastSelectedType = type; - } - - selectedSubTypeString = PhGetWindowText(subTypeComboBox); - - if (PhEqualString2(selectedSubTypeString, L"Custom", FALSE)) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), TRUE); - SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, Context->LastCustomSubType->Buffer); - } - else - { - if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM))) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), FALSE); - PhMoveReference(&Context->LastCustomSubType, PhGetWindowText(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM))); - SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, L""); - } - } - - PhDereferenceObject(selectedSubTypeString); -} - -PPH_STRING EspConvertNullsToNewLines( - _In_ PPH_STRING String - ) -{ - PH_STRING_BUILDER sb; - ULONG i; - - PhInitializeStringBuilder(&sb, String->Length); - - for (i = 0; i < (ULONG)String->Length / 2; i++) - { - if (String->Buffer[i] == 0) - { - PhAppendStringBuilderEx(&sb, L"\r\n", 4); - continue; - } - - PhAppendCharStringBuilder(&sb, String->Buffer[i]); - } - - return PhFinalStringBuilderString(&sb); -} - -PPH_STRING EspConvertNewLinesToNulls( - _In_ PPH_STRING String - ) -{ - PPH_STRING text; - SIZE_T count; - SIZE_T i; - - text = PhCreateStringEx(NULL, String->Length + 2); // plus one character for an extra null terminator (see below) - text->Length = 0; - count = 0; - - for (i = 0; i < String->Length / 2; i++) - { - // Lines are terminated by "\r\n". - if (String->Buffer[i] == '\r') - { - continue; - } - - if (String->Buffer[i] == '\n') - { - text->Buffer[count++] = 0; - continue; - } - - text->Buffer[count++] = String->Buffer[i]; - } - - if (count != 0) - { - // Make sure we have an extra null terminator at the end, as required of multistrings. - if (text->Buffer[count - 1] != 0) - text->Buffer[count++] = 0; - } - - text->Length = count * 2; - - return text; -} - -PPH_STRING EspConvertNullsToSpaces( - _In_ PPH_STRING String - ) -{ - PPH_STRING text; - SIZE_T j; - - text = PhDuplicateString(String); - - for (j = 0; j < text->Length / 2; j++) - { - if (text->Buffer[j] == 0) - text->Buffer[j] = ' '; - } - - return text; -} - -VOID EspFormatTriggerData( - _In_ PES_TRIGGER_DATA Data, - _Out_ PPH_STRING *Text - ) -{ - if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) - { - // This check works for both normal strings and multistrings. - if (Data->String->Length != 0) - { - // Prepare the text for display by replacing null characters with spaces. - *Text = EspConvertNullsToSpaces(Data->String); - } - else - { - *Text = PhCreateString(L"(empty string)"); - } - } - else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) - { - *Text = PhCreateString(L"(binary data)"); - } - else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL) - { - *Text = PhFormatString(L"(level) %u", Data->Byte); - } - else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY) - { - *Text = PhFormatString(L"(keyword any) 0x%I64x", Data->UInt64); - } - else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL) - { - *Text = PhFormatString(L"(keyword all) 0x%I64x", Data->UInt64); - } - else - { - *Text = PhCreateString(L"(unknown type)"); - } -} - -INT_PTR CALLBACK EspServiceTriggerDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PES_TRIGGER_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PES_TRIGGER_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND typeComboBox; - HWND actionComboBox; - HWND lvHandle; - ULONG i; - - context->LastSelectedType = 0; - - if (context->EditingInfo->Subtype) - context->LastCustomSubType = PhFormatGuid(context->EditingInfo->Subtype); - else - context->LastCustomSubType = PhReferenceEmptyString(); - - typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE); - actionComboBox = GetDlgItem(hwndDlg, IDC_ACTION); - - for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++) - { - ComboBox_AddString(typeComboBox, TypeEntries[i].Name); - - if (TypeEntries[i].Type == context->EditingInfo->Type) - { - PhSelectComboBoxString(typeComboBox, TypeEntries[i].Name, FALSE); - } - } - - ComboBox_AddString(actionComboBox, L"Start"); - ComboBox_AddString(actionComboBox, L"Stop"); - ComboBox_SetCurSel(actionComboBox, context->EditingInfo->Action == SERVICE_TRIGGER_ACTION_SERVICE_START ? 0 : 1); - - EspFixServiceTriggerControls(hwndDlg, context); - - if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM) - { - for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) - { - if ( - SubTypeEntries[i].Type == context->EditingInfo->Type && - SubTypeEntries[i].Guid && - context->EditingInfo->Subtype && - memcmp(SubTypeEntries[i].Guid, context->EditingInfo->Subtype, sizeof(GUID)) == 0 - ) - { - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), SubTypeEntries[i].Name, FALSE); - break; - } - } - } - else - { - if (context->EditingInfo->Subtype) - { - PPH_STRING publisherName; - - // Try to select the publisher name in the subtype list. - publisherName = EspLookupEtwPublisherName(context->EditingInfo->Subtype); - PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), publisherName->Buffer, FALSE); - PhDereferenceObject(publisherName); - } - } - - // Call a second time since the state of the custom subtype text box may have changed. - EspFixServiceTriggerControls(hwndDlg, context); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 280, L"Data"); - - if (context->EditingInfo->DataList) - { - for (i = 0; i < context->EditingInfo->DataList->Count; i++) - { - PES_TRIGGER_DATA data; - PPH_STRING text; - INT lvItemIndex; - - data = context->EditingInfo->DataList->Items[i]; - - EspFormatTriggerData(data, &text); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data); - PhDereferenceObject(text); - } - } - - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE); - } - break; - case WM_DESTROY: - { - PhClearReference(&context->LastCustomSubType); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_TYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) - { - EspFixServiceTriggerControls(hwndDlg, context); - } - break; - case IDC_SUBTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) - { - EspFixServiceTriggerControls(hwndDlg, context); - } - break; - case IDC_NEW: - { - HWND lvHandle; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - context->EditingValue = PhReferenceEmptyString(); - - if (DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_VALUE), - hwndDlg, - ValueDlgProc, - (LPARAM)context - ) == IDOK) - { - PES_TRIGGER_DATA data; - PPH_STRING text; - INT lvItemIndex; - - data = EspCreateTriggerData(NULL); - data->Type = SERVICE_TRIGGER_DATA_TYPE_STRING; - data->String = EspConvertNewLinesToNulls(context->EditingValue); - - if (!context->EditingInfo->DataList) - context->EditingInfo->DataList = PhCreateList(4); - - PhAddItemList(context->EditingInfo->DataList, data); - - EspFormatTriggerData(data, &text); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data); - PhDereferenceObject(text); - } - - PhClearReference(&context->EditingValue); - } - break; - case IDC_EDIT: - { - HWND lvHandle; - INT lvItemIndex; - PES_TRIGGER_DATA data; - ULONG index; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED); - - if ( - lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data) && - data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING // editing binary values is not supported - ) - { - index = PhFindItemList(context->EditingInfo->DataList, data); - - if (index != -1) - { - context->EditingValue = EspConvertNullsToNewLines(data->String); - - if (DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_VALUE), - hwndDlg, - ValueDlgProc, - (LPARAM)context - ) == IDOK) - { - PPH_STRING text; - - PhMoveReference(&data->String, EspConvertNewLinesToNulls(context->EditingValue)); - - EspFormatTriggerData(data, &text); - PhSetListViewSubItem(lvHandle, lvItemIndex, 0, text->Buffer); - PhDereferenceObject(text); - } - - PhClearReference(&context->EditingValue); - } - } - } - break; - case IDC_DELETE: - { - HWND lvHandle; - INT lvItemIndex; - PES_TRIGGER_DATA data; - ULONG index; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED); - - if (lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data)) - { - index = PhFindItemList(context->EditingInfo->DataList, data); - - if (index != -1) - { - EspDestroyTriggerData(data); - PhRemoveItemList(context->EditingInfo->DataList, index); - PhRemoveListViewItem(lvHandle, lvItemIndex); - } - } - } - break; - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PH_AUTO_POOL autoPool; - PPH_STRING typeString; - PPH_STRING subTypeString; - PPH_STRING customSubTypeString; - PPH_STRING actionString; - ULONG i; - - PhInitializeAutoPool(&autoPool); - - typeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE); - subTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPE); - customSubTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM); - actionString = PhaGetDlgItemText(hwndDlg, IDC_ACTION); - - for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++) - { - if (PhEqualStringZ(TypeEntries[i].Name, typeString->Buffer, FALSE)) - { - context->EditingInfo->Type = TypeEntries[i].Type; - break; - } - } - - if (!PhEqualString2(subTypeString, L"Custom", FALSE)) - { - if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM) - { - for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) - { - if ( - SubTypeEntries[i].Type == context->EditingInfo->Type && - PhEqualString2(subTypeString, SubTypeEntries[i].Name, FALSE) - ) - { - context->EditingInfo->SubtypeBuffer = *SubTypeEntries[i].Guid; - context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer; - break; - } - } - } - else - { - if (!EspLookupEtwPublisherGuid(&subTypeString->sr, &context->EditingInfo->SubtypeBuffer)) - { - PhShowError(hwndDlg, L"Unable to find the ETW publisher GUID."); - goto DoNotClose; - } - - context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer; - } - } - else - { - UNICODE_STRING guidString; - - PhStringRefToUnicodeString(&customSubTypeString->sr, &guidString); - - // Trim whitespace. - - while (guidString.Length != 0 && *guidString.Buffer == ' ') - { - guidString.Buffer++; - guidString.Length -= 2; - } - - while (guidString.Length != 0 && guidString.Buffer[guidString.Length / 2 - 1] == ' ') - { - guidString.Length -= 2; - } - - if (NT_SUCCESS(RtlGUIDFromString(&guidString, &context->EditingInfo->SubtypeBuffer))) - { - context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer; - } - else - { - PhShowError(hwndDlg, L"The custom subtype is invalid. Please ensure that the string is a valid GUID: \"{x-x-x-x-x}\"."); - goto DoNotClose; - } - } - - if (PhEqualString2(actionString, L"Start", FALSE)) - context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START; - else - context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_STOP; - - if ( - context->EditingInfo->DataList && - context->EditingInfo->DataList->Count != 0 && - context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL && - context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT && - context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT && - context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM - ) - { - // This trigger has data items, but the trigger type doesn't allow them. - if (PhShowMessage( - hwndDlg, - MB_OKCANCEL | MB_ICONWARNING, - L"The trigger type \"%s\" does not allow data items to be configured. " - L"If you continue, they will be removed.", - typeString->Buffer - ) != IDOK) - { - goto DoNotClose; - } - - for (i = 0; i < context->EditingInfo->DataList->Count; i++) - { - EspDestroyTriggerData(context->EditingInfo->DataList->Items[i]); - } - - PhClearReference(&context->EditingInfo->DataList); - } - - EndDialog(hwndDlg, IDOK); - -DoNotClose: - PhDeleteAutoPool(&autoPool); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - HWND lvHandle; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - switch (header->code) - { - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == lvHandle) - { - if (ListView_GetSelectedCount(lvHandle) == 1) - { - PES_TRIGGER_DATA data = PhGetSelectedListViewItemParam(GetDlgItem(hwndDlg, IDC_LIST)); - - // Editing binary data is not supported. - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), data && data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE); - } - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE); - } - } - } - break; - case NM_DBLCLK: - { - if (header->hwndFrom == lvHandle) - { - SendMessage(hwndDlg, WM_COMMAND, IDC_EDIT, 0); - } - } - break; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK ValueDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PES_TRIGGER_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PES_TRIGGER_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - SetDlgItemText(hwndDlg, IDC_VALUES, context->EditingValue->Buffer); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_VALUES), TRUE); - Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUES), 0, -1); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - PhMoveReference(&context->EditingValue, PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUES))); - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Services - + * trigger editor + * + * Copyright (C) 2011-2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +typedef struct _ES_TRIGGER_DATA +{ + ULONG Type; + union + { + PPH_STRING String; + struct + { + PVOID Binary; + ULONG BinaryLength; + }; + UCHAR Byte; + ULONG64 UInt64; + }; +} ES_TRIGGER_DATA, *PES_TRIGGER_DATA; + +typedef struct _ES_TRIGGER_INFO +{ + ULONG Type; + PGUID Subtype; + ULONG Action; + PPH_LIST DataList; + GUID SubtypeBuffer; +} ES_TRIGGER_INFO, *PES_TRIGGER_INFO; + +typedef struct _ES_TRIGGER_CONTEXT +{ + PPH_SERVICE_ITEM ServiceItem; + HWND WindowHandle; + HWND TriggersLv; + BOOLEAN Dirty; + ULONG InitialNumberOfTriggers; + PPH_LIST InfoList; + + // Trigger dialog box + PES_TRIGGER_INFO EditingInfo; + ULONG LastSelectedType; + PPH_STRING LastCustomSubType; + + // Value dialog box + PPH_STRING EditingValue; +} ES_TRIGGER_CONTEXT, *PES_TRIGGER_CONTEXT; + +typedef struct _TYPE_ENTRY +{ + ULONG Type; + PWSTR Name; +} TYPE_ENTRY, PTYPE_ENTRY; + +typedef struct _SUBTYPE_ENTRY +{ + ULONG Type; + PGUID Guid; + PWSTR Name; +} SUBTYPE_ENTRY, PSUBTYPE_ENTRY; + +typedef struct _ETW_PUBLISHER_ENTRY +{ + PPH_STRING PublisherName; + GUID Guid; +} ETW_PUBLISHER_ENTRY, *PETW_PUBLISHER_ENTRY; + +INT_PTR CALLBACK EspServiceTriggerDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK ValueDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static GUID NetworkManagerFirstIpAddressArrivalGuid = { 0x4f27f2de, 0x14e2, 0x430b, { 0xa5, 0x49, 0x7c, 0xd4, 0x8c, 0xbc, 0x82, 0x45 } }; +static GUID NetworkManagerLastIpAddressRemovalGuid = { 0xcc4ba62a, 0x162e, 0x4648, { 0x84, 0x7a, 0xb6, 0xbd, 0xf9, 0x93, 0xe3, 0x35 } }; +static GUID DomainJoinGuid = { 0x1ce20aba, 0x9851, 0x4421, { 0x94, 0x30, 0x1d, 0xde, 0xb7, 0x66, 0xe8, 0x09 } }; +static GUID DomainLeaveGuid = { 0xddaf516e, 0x58c2, 0x4866, { 0x95, 0x74, 0xc3, 0xb6, 0x15, 0xd4, 0x2e, 0xa1 } }; +static GUID FirewallPortOpenGuid = { 0xb7569e07, 0x8421, 0x4ee0, { 0xad, 0x10, 0x86, 0x91, 0x5a, 0xfd, 0xad, 0x09 } }; +static GUID FirewallPortCloseGuid = { 0xa144ed38, 0x8e12, 0x4de4, { 0x9d, 0x96, 0xe6, 0x47, 0x40, 0xb1, 0xa5, 0x24 } }; +static GUID MachinePolicyPresentGuid = { 0x659fcae6, 0x5bdb, 0x4da9, { 0xb1, 0xff, 0xca, 0x2a, 0x17, 0x8d, 0x46, 0xe0 } }; +static GUID UserPolicyPresentGuid = { 0x54fb46c8, 0xf089, 0x464c, { 0xb1, 0xfd, 0x59, 0xd1, 0xb6, 0x2c, 0x3b, 0x50 } }; +static GUID RpcInterfaceEventGuid = { 0xbc90d167, 0x9470, 0x4139, { 0xa9, 0xba, 0xbe, 0x0b, 0xbb, 0xf5, 0xb7, 0x4d } }; +static GUID NamedPipeEventGuid = { 0x1f81d131, 0x3fac, 0x4537, { 0x9e, 0x0c, 0x7e, 0x7b, 0x0c, 0x2f, 0x4b, 0x55 } }; +static GUID SubTypeUnknownGuid; // dummy + +static TYPE_ENTRY TypeEntries[] = +{ + { SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL, L"Device interface arrival" }, + { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, L"IP address availability" }, + { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, L"Domain join" }, + { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, L"Firewall port event" }, + { SERVICE_TRIGGER_TYPE_GROUP_POLICY, L"Group policy" }, + { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, L"Network endpoint" }, + { SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE, L"Custom system state change" }, + { SERVICE_TRIGGER_TYPE_CUSTOM, L"Custom" } +}; + +static SUBTYPE_ENTRY SubTypeEntries[] = +{ + { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, NULL, L"IP address" }, + { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerFirstIpAddressArrivalGuid, L"IP address: First arrival" }, + { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerLastIpAddressRemovalGuid, L"IP address: Last removal" }, + { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &SubTypeUnknownGuid, L"IP address: Unknown" }, + { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, NULL, L"Domain" }, + { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainJoinGuid, L"Domain: Join" }, + { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainLeaveGuid, L"Domain: Leave" }, + { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &SubTypeUnknownGuid, L"Domain: Unknown" }, + { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, NULL, L"Firewall port" }, + { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortOpenGuid, L"Firewall port: Open" }, + { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortCloseGuid, L"Firewall port: Close" }, + { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &SubTypeUnknownGuid, L"Firewall port: Unknown" }, + { SERVICE_TRIGGER_TYPE_GROUP_POLICY, NULL, L"Group policy change" }, + { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &MachinePolicyPresentGuid, L"Group policy change: Machine" }, + { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &UserPolicyPresentGuid, L"Group policy change: User" }, + { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &SubTypeUnknownGuid, L"Group policy change: Unknown" }, + { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, NULL, L"Network endpoint" }, + { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &RpcInterfaceEventGuid, L"Network endpoint: RPC interface" }, + { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &NamedPipeEventGuid, L"Network endpoint: Named pipe" }, + { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &SubTypeUnknownGuid, L"Network endpoint: Unknown" } +}; + +static PH_STRINGREF PublishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\"); + +PES_TRIGGER_DATA EspCreateTriggerData( + _In_opt_ PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM DataItem + ) +{ + PES_TRIGGER_DATA data; + + data = PhAllocate(sizeof(ES_TRIGGER_DATA)); + memset(data, 0, sizeof(ES_TRIGGER_DATA)); + + if (DataItem) + { + data->Type = DataItem->dwDataType; + + if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) + { + if (DataItem->pData && DataItem->cbData >= 2) + data->String = PhCreateStringEx((PWSTR)DataItem->pData, DataItem->cbData - 2); // exclude final null terminator + else + data->String = PhReferenceEmptyString(); + } + else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) + { + data->BinaryLength = DataItem->cbData; + data->Binary = PhAllocateCopy(DataItem->pData, DataItem->cbData); + } + else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL) + { + if (DataItem->cbData == sizeof(UCHAR)) + data->Byte = *(PUCHAR)DataItem->pData; + } + else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL) + { + if (DataItem->cbData == sizeof(ULONG64)) + data->UInt64 = *(PULONG64)DataItem->pData; + } + } + + return data; +} + +PES_TRIGGER_DATA EspCloneTriggerData( + _In_ PES_TRIGGER_DATA Data + ) +{ + PES_TRIGGER_DATA newData; + + newData = PhAllocateCopy(Data, sizeof(ES_TRIGGER_DATA)); + + if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) + { + if (newData->String) + newData->String = PhDuplicateString(newData->String); + } + else if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) + { + if (newData->Binary) + newData->Binary = PhAllocateCopy(newData->Binary, newData->BinaryLength); + } + + return newData; +} + +VOID EspDestroyTriggerData( + _In_ PES_TRIGGER_DATA Data + ) +{ + if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) + { + if (Data->String) + PhDereferenceObject(Data->String); + } + else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) + { + if (Data->Binary) + PhFree(Data->Binary); + } + + PhFree(Data); +} + +PES_TRIGGER_INFO EspCreateTriggerInfo( + _In_opt_ PSERVICE_TRIGGER Trigger + ) +{ + PES_TRIGGER_INFO info; + + info = PhAllocate(sizeof(ES_TRIGGER_INFO)); + memset(info, 0, sizeof(ES_TRIGGER_INFO)); + + if (Trigger) + { + info->Type = Trigger->dwTriggerType; + + if (Trigger->pTriggerSubtype) + { + info->SubtypeBuffer = *Trigger->pTriggerSubtype; + info->Subtype = &info->SubtypeBuffer; + } + + info->Action = Trigger->dwAction; + + if ( + info->Type == SERVICE_TRIGGER_TYPE_CUSTOM || + info->Type == SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL || + info->Type == SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT || + info->Type == SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT + ) + { + ULONG i; + + info->DataList = PhCreateList(Trigger->cDataItems); + + for (i = 0; i < Trigger->cDataItems; i++) + { + PhAddItemList(info->DataList, EspCreateTriggerData(&Trigger->pDataItems[i])); + } + } + } + + return info; +} + +PES_TRIGGER_INFO EspCloneTriggerInfo( + _In_ PES_TRIGGER_INFO Info + ) +{ + PES_TRIGGER_INFO newInfo; + + newInfo = PhAllocateCopy(Info, sizeof(ES_TRIGGER_INFO)); + + if (newInfo->Subtype == &Info->SubtypeBuffer) + newInfo->Subtype = &newInfo->SubtypeBuffer; + + if (newInfo->DataList) + { + ULONG i; + + newInfo->DataList = PhCreateList(Info->DataList->AllocatedCount); + newInfo->DataList->Count = Info->DataList->Count; + + for (i = 0; i < Info->DataList->Count; i++) + newInfo->DataList->Items[i] = EspCloneTriggerData(Info->DataList->Items[i]); + } + + return newInfo; +} + +VOID EspDestroyTriggerInfo( + _In_ PES_TRIGGER_INFO Info + ) +{ + if (Info->DataList) + { + ULONG i; + + for (i = 0; i < Info->DataList->Count; i++) + { + EspDestroyTriggerData(Info->DataList->Items[i]); + } + + PhDereferenceObject(Info->DataList); + } + + PhFree(Info); +} + +VOID EspClearTriggerInfoList( + _In_ PPH_LIST List + ) +{ + ULONG i; + + for (i = 0; i < List->Count; i++) + { + EspDestroyTriggerInfo(List->Items[i]); + } + + PhClearList(List); +} + +struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext( + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ HWND WindowHandle, + _In_ HWND TriggersLv + ) +{ + PES_TRIGGER_CONTEXT context; + + context = PhAllocate(sizeof(ES_TRIGGER_CONTEXT)); + memset(context, 0, sizeof(ES_TRIGGER_CONTEXT)); + context->ServiceItem = ServiceItem; + context->WindowHandle = WindowHandle; + context->TriggersLv = TriggersLv; + context->InfoList = PhCreateList(4); + + PhSetListViewStyle(TriggersLv, FALSE, TRUE); + PhSetControlTheme(TriggersLv, L"explorer"); + PhAddListViewColumn(TriggersLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Trigger"); + PhAddListViewColumn(TriggersLv, 1, 1, 1, LVCFMT_LEFT, 60, L"Action"); + PhSetExtendedListView(TriggersLv); + + EnableWindow(GetDlgItem(WindowHandle, IDC_EDIT), FALSE); + EnableWindow(GetDlgItem(WindowHandle, IDC_DELETE), FALSE); + + return context; +} + +VOID EsDestroyServiceTriggerContext( + _In_ struct _ES_TRIGGER_CONTEXT *Context + ) +{ + ULONG i; + + for (i = 0; i < Context->InfoList->Count; i++) + { + EspDestroyTriggerInfo(Context->InfoList->Items[i]); + } + + PhDereferenceObject(Context->InfoList); + PhFree(Context); +} + +PPH_STRING EspLookupEtwPublisherName( + _In_ PGUID Guid + ) +{ + PPH_STRING guidString; + PPH_STRING keyName; + HANDLE keyHandle; + PPH_STRING publisherName = NULL; + + // Copied from ProcessHacker\hndlinfo.c. + + guidString = PhFormatGuid(Guid); + + keyName = PhConcatStringRef2(&PublishersKeyName, &guidString->sr); + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) + { + publisherName = PhQueryRegistryString(keyHandle, NULL); + + if (publisherName && publisherName->Length == 0) + { + PhDereferenceObject(publisherName); + publisherName = NULL; + } + + NtClose(keyHandle); + } + + PhDereferenceObject(keyName); + + if (publisherName) + { + PhDereferenceObject(guidString); + return publisherName; + } + else + { + return guidString; + } +} + +BOOLEAN EspEnumerateEtwPublishers( + _Out_ PETW_PUBLISHER_ENTRY *Entries, + _Out_ PULONG NumberOfEntries + ) +{ + NTSTATUS status; + HANDLE publishersKeyHandle; + ULONG index; + PKEY_BASIC_INFORMATION buffer; + ULONG bufferSize; + PETW_PUBLISHER_ENTRY entries; + ULONG numberOfEntries; + ULONG allocatedEntries; + + if (!NT_SUCCESS(PhOpenKey( + &publishersKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &PublishersKeyName, + 0 + ))) + { + return FALSE; + } + + numberOfEntries = 0; + allocatedEntries = 256; + entries = PhAllocate(allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY)); + + index = 0; + bufferSize = 0x100; + buffer = PhAllocate(0x100); + + while (TRUE) + { + status = NtEnumerateKey( + publishersKeyHandle, + index, + KeyBasicInformation, + buffer, + bufferSize, + &bufferSize + ); + + if (NT_SUCCESS(status)) + { + UNICODE_STRING nameUs; + PH_STRINGREF name; + HANDLE keyHandle; + GUID guid; + PPH_STRING publisherName; + + nameUs.Buffer = buffer->Name; + nameUs.Length = (USHORT)buffer->NameLength; + name.Buffer = buffer->Name; + name.Length = buffer->NameLength; + + // Make sure this is a valid publisher key. + if (NT_SUCCESS(RtlGUIDFromString(&nameUs, &guid))) + { + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + publishersKeyHandle, + &name, + 0 + ))) + { + publisherName = PhQueryRegistryString(keyHandle, NULL); + + if (publisherName) + { + if (publisherName->Length != 0) + { + PETW_PUBLISHER_ENTRY entry; + + if (numberOfEntries == allocatedEntries) + { + allocatedEntries *= 2; + entries = PhReAllocate(entries, allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY)); + } + + entry = &entries[numberOfEntries++]; + entry->PublisherName = publisherName; + entry->Guid = guid; + } + else + { + PhDereferenceObject(publisherName); + } + } + + NtClose(keyHandle); + } + } + + index++; + } + else if (status == STATUS_NO_MORE_ENTRIES) + { + break; + } + else if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + } + else + { + break; + } + } + + PhFree(buffer); + NtClose(publishersKeyHandle); + + *Entries = entries; + *NumberOfEntries = numberOfEntries; + + return TRUE; +} + +BOOLEAN EspLookupEtwPublisherGuid( + _In_ PPH_STRINGREF PublisherName, + _Out_ PGUID Guid + ) +{ + BOOLEAN result; + PETW_PUBLISHER_ENTRY entries; + ULONG numberOfEntries; + ULONG i; + + if (!EspEnumerateEtwPublishers(&entries, &numberOfEntries)) + return FALSE; + + result = FALSE; + + for (i = 0; i < numberOfEntries; i++) + { + if (!result && PhEqualStringRef(&entries[i].PublisherName->sr, PublisherName, TRUE)) + { + *Guid = entries[i].Guid; + result = TRUE; + } + + PhDereferenceObject(entries[i].PublisherName); + } + + PhFree(entries); + + return result; +} + +VOID EspFormatTriggerInfo( + _In_ PES_TRIGGER_INFO Info, + _Out_ PWSTR *TriggerString, + _Out_ PWSTR *ActionString, + _Out_ PPH_STRING *StringUsed + ) +{ + PPH_STRING stringUsed = NULL; + PWSTR triggerString = NULL; + PWSTR actionString; + ULONG i; + BOOLEAN typeFound; + BOOLEAN subTypeFound; + + switch (Info->Type) + { + case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL: + { + PPH_STRING guidString; + + if (!Info->Subtype) + { + triggerString = L"Device interface arrival"; + } + else + { + guidString = PhFormatGuid(Info->Subtype); + stringUsed = PhConcatStrings2(L"Device interface arrival: ", guidString->Buffer); + triggerString = stringUsed->Buffer; + } + } + break; + case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE: + { + PPH_STRING guidString; + + if (!Info->Subtype) + { + triggerString = L"Custom system state change"; + } + else + { + guidString = PhFormatGuid(Info->Subtype); + stringUsed = PhConcatStrings2(L"Custom system state change: ", guidString->Buffer); + triggerString = stringUsed->Buffer; + } + } + break; + case SERVICE_TRIGGER_TYPE_CUSTOM: + { + if (Info->Subtype) + { + PPH_STRING publisherName; + + // Try to lookup the publisher name from the GUID. + publisherName = EspLookupEtwPublisherName(Info->Subtype); + stringUsed = PhConcatStrings2(L"Custom: ", publisherName->Buffer); + PhDereferenceObject(publisherName); + triggerString = stringUsed->Buffer; + } + else + { + triggerString = L"Custom"; + } + } + break; + default: + { + typeFound = FALSE; + subTypeFound = FALSE; + + for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) + { + if (SubTypeEntries[i].Type == Info->Type) + { + typeFound = TRUE; + + if (!Info->Subtype && !SubTypeEntries[i].Guid) + { + subTypeFound = TRUE; + triggerString = SubTypeEntries[i].Name; + break; + } + else if (Info->Subtype && SubTypeEntries[i].Guid && IsEqualGUID(Info->Subtype, SubTypeEntries[i].Guid)) + { + subTypeFound = TRUE; + triggerString = SubTypeEntries[i].Name; + break; + } + else if (!subTypeFound && SubTypeEntries[i].Guid == &SubTypeUnknownGuid) + { + triggerString = SubTypeEntries[i].Name; + break; + } + } + } + + if (!typeFound) + { + triggerString = L"Unknown"; + } + } + break; + } + + switch (Info->Action) + { + case SERVICE_TRIGGER_ACTION_SERVICE_START: + actionString = L"Start"; + break; + case SERVICE_TRIGGER_ACTION_SERVICE_STOP: + actionString = L"Stop"; + break; + default: + actionString = L"Unknown"; + break; + } + + *TriggerString = triggerString; + *ActionString = actionString; + *StringUsed = stringUsed; +} + +VOID EsLoadServiceTriggerInfo( + _In_ struct _ES_TRIGGER_CONTEXT *Context, + _In_ SC_HANDLE ServiceHandle + ) +{ + PSERVICE_TRIGGER_INFO triggerInfo; + ULONG i; + + EspClearTriggerInfoList(Context->InfoList); + + if (triggerInfo = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_TRIGGER_INFO)) + { + for (i = 0; i < triggerInfo->cTriggers; i++) + { + PSERVICE_TRIGGER trigger = &triggerInfo->pTriggers[i]; + PES_TRIGGER_INFO info; + PWSTR triggerString; + PWSTR actionString; + PPH_STRING stringUsed; + INT lvItemIndex; + + info = EspCreateTriggerInfo(trigger); + PhAddItemList(Context->InfoList, info); + + EspFormatTriggerInfo(info, &triggerString, &actionString, &stringUsed); + + lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, info); + PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString); + + if (stringUsed) + PhDereferenceObject(stringUsed); + } + + Context->InitialNumberOfTriggers = triggerInfo->cTriggers; + + ExtendedListView_SortItems(Context->TriggersLv); + + PhFree(triggerInfo); + } +} + +BOOLEAN EsSaveServiceTriggerInfo( + _In_ struct _ES_TRIGGER_CONTEXT *Context, + _Out_ PULONG Win32Result + ) +{ + BOOLEAN result = TRUE; + PH_AUTO_POOL autoPool; + SC_HANDLE serviceHandle; + SERVICE_TRIGGER_INFO triggerInfo; + ULONG i; + ULONG j; + + if (!Context->Dirty) + return TRUE; + + // Do not try to change trigger information if we didn't have any triggers before and we don't + // have any now. ChangeServiceConfig2 returns an error in this situation. + if (Context->InitialNumberOfTriggers == 0 && Context->InfoList->Count == 0) + return TRUE; + + PhInitializeAutoPool(&autoPool); + + memset(&triggerInfo, 0, sizeof(SERVICE_TRIGGER_INFO)); + triggerInfo.cTriggers = Context->InfoList->Count; + + // pTriggers needs to be NULL when there are no triggers. + if (Context->InfoList->Count != 0) + { + triggerInfo.pTriggers = PH_AUTO(PhCreateAlloc(Context->InfoList->Count * sizeof(SERVICE_TRIGGER))); + memset(triggerInfo.pTriggers, 0, Context->InfoList->Count * sizeof(SERVICE_TRIGGER)); + + for (i = 0; i < Context->InfoList->Count; i++) + { + PSERVICE_TRIGGER trigger = &triggerInfo.pTriggers[i]; + PES_TRIGGER_INFO info = Context->InfoList->Items[i]; + + trigger->dwTriggerType = info->Type; + trigger->dwAction = info->Action; + trigger->pTriggerSubtype = info->Subtype; + + if (info->DataList && info->DataList->Count != 0) + { + trigger->cDataItems = info->DataList->Count; + trigger->pDataItems = PH_AUTO(PhCreateAlloc(info->DataList->Count * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM))); + + for (j = 0; j < info->DataList->Count; j++) + { + PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem = &trigger->pDataItems[j]; + PES_TRIGGER_DATA data = info->DataList->Items[j]; + + dataItem->dwDataType = data->Type; + + if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) + { + dataItem->cbData = (ULONG)data->String->Length + 2; // include null terminator + dataItem->pData = (PBYTE)data->String->Buffer; + } + else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) + { + dataItem->cbData = data->BinaryLength; + dataItem->pData = data->Binary; + } + else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL) + { + dataItem->cbData = sizeof(UCHAR); + dataItem->pData = (PBYTE)&data->Byte; + } + else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL) + { + dataItem->cbData = sizeof(ULONG64); + dataItem->pData = (PBYTE)&data->UInt64; + } + } + } + } + } + + if (serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG)) + { + if (!ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo)) + { + result = FALSE; + *Win32Result = GetLastError(); + } + + CloseServiceHandle(serviceHandle); + } + else + { + result = FALSE; + *Win32Result = GetLastError(); + + if (*Win32Result == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated) + { + // Elevate using phsvc. + if (PhUiConnectToPhSvc(Context->WindowHandle, FALSE)) + { + NTSTATUS status; + + result = TRUE; + + if (!NT_SUCCESS(status = PhSvcCallChangeServiceConfig2(Context->ServiceItem->Name->Buffer, + SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo))) + { + result = FALSE; + *Win32Result = PhNtStatusToDosError(status); + } + + PhUiDisconnectFromPhSvc(); + } + else + { + // User cancelled elevation. + *Win32Result = ERROR_CANCELLED; + } + } + } + + PhDeleteAutoPool(&autoPool); + + return result; +} + +LOGICAL EspSetListViewItemParam( + _In_ HWND ListViewHandle, + _In_ INT Index, + _In_ PVOID Param + ) +{ + LVITEM item; + + item.mask = LVIF_PARAM; + item.iItem = Index; + item.iSubItem = 0; + item.lParam = (LPARAM)Param; + + return ListView_SetItem(ListViewHandle, &item); +} + +VOID EsHandleEventServiceTrigger( + _In_ struct _ES_TRIGGER_CONTEXT *Context, + _In_ ULONG Event + ) +{ + switch (Event) + { + case ES_TRIGGER_EVENT_NEW: + { + Context->EditingInfo = EspCreateTriggerInfo(NULL); + Context->EditingInfo->Type = SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY; + Context->EditingInfo->SubtypeBuffer = NetworkManagerFirstIpAddressArrivalGuid; + Context->EditingInfo->Subtype = &Context->EditingInfo->SubtypeBuffer; + Context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START; + + if (DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_SRVTRIGGER), + Context->WindowHandle, + EspServiceTriggerDlgProc, + (LPARAM)Context + ) == IDOK) + { + PWSTR triggerString; + PWSTR actionString; + PPH_STRING stringUsed; + INT lvItemIndex; + + Context->Dirty = TRUE; + PhAddItemList(Context->InfoList, Context->EditingInfo); + + EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed); + + lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, Context->EditingInfo); + PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString); + + if (stringUsed) + PhDereferenceObject(stringUsed); + } + else + { + EspDestroyTriggerInfo(Context->EditingInfo); + } + + Context->EditingInfo = NULL; + } + break; + case ES_TRIGGER_EVENT_EDIT: + { + INT lvItemIndex; + PES_TRIGGER_INFO info; + ULONG index; + + lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info)) + { + index = PhFindItemList(Context->InfoList, info); + + if (index != ULONG_MAX) + { + Context->EditingInfo = EspCloneTriggerInfo(info); + + if (DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_SRVTRIGGER), + Context->WindowHandle, + EspServiceTriggerDlgProc, + (LPARAM)Context + ) == IDOK) + { + PWSTR triggerString; + PWSTR actionString; + PPH_STRING stringUsed; + + Context->Dirty = TRUE; + EspDestroyTriggerInfo(Context->InfoList->Items[index]); + Context->InfoList->Items[index] = Context->EditingInfo; + + EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed); + PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 0, triggerString); + PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString); + + if (stringUsed) + PhDereferenceObject(stringUsed); + + EspSetListViewItemParam(Context->TriggersLv, lvItemIndex, Context->EditingInfo); + } + else + { + EspDestroyTriggerInfo(Context->EditingInfo); + } + + Context->EditingInfo = NULL; + } + } + } + break; + case ES_TRIGGER_EVENT_DELETE: + { + INT lvItemIndex; + PES_TRIGGER_INFO info; + ULONG index; + + lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info)) + { + index = PhFindItemList(Context->InfoList, info); + + if (index != ULONG_MAX) + { + EspDestroyTriggerInfo(info); + PhRemoveItemList(Context->InfoList, index); + PhRemoveListViewItem(Context->TriggersLv, lvItemIndex); + } + } + + Context->Dirty = TRUE; + } + break; + case ES_TRIGGER_EVENT_SELECTIONCHANGED: + { + ULONG selectedCount; + + selectedCount = ListView_GetSelectedCount(Context->TriggersLv); + + EnableWindow(GetDlgItem(Context->WindowHandle, IDC_EDIT), selectedCount == 1); + EnableWindow(GetDlgItem(Context->WindowHandle, IDC_DELETE), selectedCount == 1); + } + break; + } +} + +ULONG EspTriggerTypeStringToInteger( + _In_ PWSTR String + ) +{ + ULONG i; + + for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++) + { + if (PhEqualStringZ(TypeEntries[i].Name, String, FALSE)) + return TypeEntries[i].Type; + } + + return 0; +} + +static int __cdecl EtwPublisherByNameCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PETW_PUBLISHER_ENTRY entry1 = (PETW_PUBLISHER_ENTRY)elem1; + PETW_PUBLISHER_ENTRY entry2 = (PETW_PUBLISHER_ENTRY)elem2; + + return PhCompareString(entry1->PublisherName, entry2->PublisherName, TRUE); +} + +VOID EspFixServiceTriggerControls( + _In_ HWND hwndDlg, + _In_ PES_TRIGGER_CONTEXT Context + ) +{ + HWND typeComboBox; + HWND subTypeComboBox; + ULONG i; + PPH_STRING selectedTypeString; + ULONG type; + PPH_STRING selectedSubTypeString; + + typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE); + subTypeComboBox = GetDlgItem(hwndDlg, IDC_SUBTYPE); + + selectedTypeString = PhGetWindowText(typeComboBox); + type = EspTriggerTypeStringToInteger(selectedTypeString->Buffer); + PhDereferenceObject(selectedTypeString); + + if (Context->LastSelectedType != type) + { + // Change the contents of the subtype combo box based on the type. + + ComboBox_ResetContent(subTypeComboBox); + + switch (type) + { + case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL: + { + ComboBox_AddString(subTypeComboBox, L"Custom"); + } + break; + case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE: + { + ComboBox_AddString(subTypeComboBox, L"Custom"); + } + break; + case SERVICE_TRIGGER_TYPE_CUSTOM: + { + PETW_PUBLISHER_ENTRY entries; + ULONG numberOfEntries; + ULONG i; + + ComboBox_AddString(subTypeComboBox, L"Custom"); + + // Display a list of publishers. + if (EspEnumerateEtwPublishers(&entries, &numberOfEntries)) + { + // Sort the list by name. + qsort(entries, numberOfEntries, sizeof(ETW_PUBLISHER_ENTRY), EtwPublisherByNameCompareFunction); + + for (i = 0; i < numberOfEntries; i++) + { + ComboBox_AddString(subTypeComboBox, entries[i].PublisherName->Buffer); + PhDereferenceObject(entries[i].PublisherName); + } + + PhFree(entries); + } + } + break; + default: + for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) + { + if (SubTypeEntries[i].Type == type && SubTypeEntries[i].Guid && SubTypeEntries[i].Guid != &SubTypeUnknownGuid) + { + ComboBox_AddString(subTypeComboBox, SubTypeEntries[i].Name); + } + } + break; + } + + ComboBox_SetCurSel(subTypeComboBox, 0); + + Context->LastSelectedType = type; + } + + selectedSubTypeString = PhGetWindowText(subTypeComboBox); + + if (PhEqualString2(selectedSubTypeString, L"Custom", FALSE)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), TRUE); + PhSetDialogItemText(hwndDlg, IDC_SUBTYPECUSTOM, Context->LastCustomSubType->Buffer); + } + else + { + if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM))) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), FALSE); + PhMoveReference(&Context->LastCustomSubType, PhGetWindowText(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM))); + PhSetDialogItemText(hwndDlg, IDC_SUBTYPECUSTOM, L""); + } + } + + PhDereferenceObject(selectedSubTypeString); +} + +PPH_STRING EspConvertNullsToNewLines( + _In_ PPH_STRING String + ) +{ + PH_STRING_BUILDER sb; + ULONG i; + + PhInitializeStringBuilder(&sb, String->Length); + + for (i = 0; i < (ULONG)String->Length / 2; i++) + { + if (String->Buffer[i] == 0) + { + PhAppendStringBuilderEx(&sb, L"\r\n", 4); + continue; + } + + PhAppendCharStringBuilder(&sb, String->Buffer[i]); + } + + return PhFinalStringBuilderString(&sb); +} + +PPH_STRING EspConvertNewLinesToNulls( + _In_ PPH_STRING String + ) +{ + PPH_STRING text; + SIZE_T count; + SIZE_T i; + + text = PhCreateStringEx(NULL, String->Length + 2); // plus one character for an extra null terminator (see below) + text->Length = 0; + count = 0; + + for (i = 0; i < String->Length / 2; i++) + { + // Lines are terminated by "\r\n". + if (String->Buffer[i] == '\r') + { + continue; + } + + if (String->Buffer[i] == '\n') + { + text->Buffer[count++] = 0; + continue; + } + + text->Buffer[count++] = String->Buffer[i]; + } + + if (count != 0) + { + // Make sure we have an extra null terminator at the end, as required of multistrings. + if (text->Buffer[count - 1] != 0) + text->Buffer[count++] = 0; + } + + text->Length = count * 2; + + return text; +} + +PPH_STRING EspConvertNullsToSpaces( + _In_ PPH_STRING String + ) +{ + PPH_STRING text; + SIZE_T j; + + text = PhDuplicateString(String); + + for (j = 0; j < text->Length / 2; j++) + { + if (text->Buffer[j] == 0) + text->Buffer[j] = ' '; + } + + return text; +} + +VOID EspFormatTriggerData( + _In_ PES_TRIGGER_DATA Data, + _Out_ PPH_STRING *Text + ) +{ + if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING) + { + // This check works for both normal strings and multistrings. + if (Data->String->Length != 0) + { + // Prepare the text for display by replacing null characters with spaces. + *Text = EspConvertNullsToSpaces(Data->String); + } + else + { + *Text = PhCreateString(L"(empty string)"); + } + } + else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY) + { + *Text = PhCreateString(L"(binary data)"); + } + else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL) + { + *Text = PhFormatString(L"(level) %u", Data->Byte); + } + else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY) + { + *Text = PhFormatString(L"(keyword any) 0x%I64x", Data->UInt64); + } + else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL) + { + *Text = PhFormatString(L"(keyword all) 0x%I64x", Data->UInt64); + } + else + { + *Text = PhCreateString(L"(unknown type)"); + } +} + +INT_PTR CALLBACK EspServiceTriggerDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PES_TRIGGER_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PES_TRIGGER_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND typeComboBox; + HWND actionComboBox; + HWND lvHandle; + ULONG i; + + context->LastSelectedType = 0; + + if (context->EditingInfo->Subtype) + context->LastCustomSubType = PhFormatGuid(context->EditingInfo->Subtype); + else + context->LastCustomSubType = PhReferenceEmptyString(); + + typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE); + actionComboBox = GetDlgItem(hwndDlg, IDC_ACTION); + + for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++) + { + ComboBox_AddString(typeComboBox, TypeEntries[i].Name); + + if (TypeEntries[i].Type == context->EditingInfo->Type) + { + PhSelectComboBoxString(typeComboBox, TypeEntries[i].Name, FALSE); + } + } + + ComboBox_AddString(actionComboBox, L"Start"); + ComboBox_AddString(actionComboBox, L"Stop"); + ComboBox_SetCurSel(actionComboBox, context->EditingInfo->Action == SERVICE_TRIGGER_ACTION_SERVICE_START ? 0 : 1); + + EspFixServiceTriggerControls(hwndDlg, context); + + if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM) + { + for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) + { + if ( + SubTypeEntries[i].Type == context->EditingInfo->Type && + SubTypeEntries[i].Guid && + context->EditingInfo->Subtype && + IsEqualGUID(SubTypeEntries[i].Guid, context->EditingInfo->Subtype) + ) + { + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), SubTypeEntries[i].Name, FALSE); + break; + } + } + } + else + { + if (context->EditingInfo->Subtype) + { + PPH_STRING publisherName; + + // Try to select the publisher name in the subtype list. + publisherName = EspLookupEtwPublisherName(context->EditingInfo->Subtype); + PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), publisherName->Buffer, FALSE); + PhDereferenceObject(publisherName); + } + } + + // Call a second time since the state of the custom subtype text box may have changed. + EspFixServiceTriggerControls(hwndDlg, context); + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 280, L"Data"); + + if (context->EditingInfo->DataList) + { + for (i = 0; i < context->EditingInfo->DataList->Count; i++) + { + PES_TRIGGER_DATA data; + PPH_STRING text; + INT lvItemIndex; + + data = context->EditingInfo->DataList->Items[i]; + + EspFormatTriggerData(data, &text); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data); + PhDereferenceObject(text); + } + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhClearReference(&context->LastCustomSubType); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_TYPE: + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + { + EspFixServiceTriggerControls(hwndDlg, context); + } + break; + case IDC_SUBTYPE: + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + { + EspFixServiceTriggerControls(hwndDlg, context); + } + break; + case IDC_NEW: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->EditingValue = PhReferenceEmptyString(); + + if (DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_VALUE), + hwndDlg, + ValueDlgProc, + (LPARAM)context + ) == IDOK) + { + PES_TRIGGER_DATA data; + PPH_STRING text; + INT lvItemIndex; + + data = EspCreateTriggerData(NULL); + data->Type = SERVICE_TRIGGER_DATA_TYPE_STRING; + data->String = EspConvertNewLinesToNulls(context->EditingValue); + + if (!context->EditingInfo->DataList) + context->EditingInfo->DataList = PhCreateList(4); + + PhAddItemList(context->EditingInfo->DataList, data); + + EspFormatTriggerData(data, &text); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data); + PhDereferenceObject(text); + } + + PhClearReference(&context->EditingValue); + } + break; + case IDC_EDIT: + { + HWND lvHandle; + INT lvItemIndex; + PES_TRIGGER_DATA data; + ULONG index; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED); + + if ( + lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data) && + data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING // editing binary values is not supported + ) + { + index = PhFindItemList(context->EditingInfo->DataList, data); + + if (index != -1) + { + context->EditingValue = EspConvertNullsToNewLines(data->String); + + if (DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_VALUE), + hwndDlg, + ValueDlgProc, + (LPARAM)context + ) == IDOK) + { + PPH_STRING text; + + PhMoveReference(&data->String, EspConvertNewLinesToNulls(context->EditingValue)); + + EspFormatTriggerData(data, &text); + PhSetListViewSubItem(lvHandle, lvItemIndex, 0, text->Buffer); + PhDereferenceObject(text); + } + + PhClearReference(&context->EditingValue); + } + } + } + break; + case IDC_DELETE: + { + HWND lvHandle; + INT lvItemIndex; + PES_TRIGGER_DATA data; + ULONG index; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED); + + if (lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data)) + { + index = PhFindItemList(context->EditingInfo->DataList, data); + + if (index != -1) + { + EspDestroyTriggerData(data); + PhRemoveItemList(context->EditingInfo->DataList, index); + PhRemoveListViewItem(lvHandle, lvItemIndex); + } + } + } + break; + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + { + PH_AUTO_POOL autoPool; + PPH_STRING typeString; + PPH_STRING subTypeString; + PPH_STRING customSubTypeString; + PPH_STRING actionString; + ULONG i; + + PhInitializeAutoPool(&autoPool); + + typeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE); + subTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPE); + customSubTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM); + actionString = PhaGetDlgItemText(hwndDlg, IDC_ACTION); + + for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++) + { + if (PhEqualStringZ(TypeEntries[i].Name, typeString->Buffer, FALSE)) + { + context->EditingInfo->Type = TypeEntries[i].Type; + break; + } + } + + if (!PhEqualString2(subTypeString, L"Custom", FALSE)) + { + if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM) + { + for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++) + { + if ( + SubTypeEntries[i].Type == context->EditingInfo->Type && + PhEqualString2(subTypeString, SubTypeEntries[i].Name, FALSE) + ) + { + context->EditingInfo->SubtypeBuffer = *SubTypeEntries[i].Guid; + context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer; + break; + } + } + } + else + { + if (!EspLookupEtwPublisherGuid(&subTypeString->sr, &context->EditingInfo->SubtypeBuffer)) + { + PhShowError(hwndDlg, L"Unable to find the ETW publisher GUID."); + goto DoNotClose; + } + + context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer; + } + } + else + { + UNICODE_STRING guidString; + + PhStringRefToUnicodeString(&customSubTypeString->sr, &guidString); + + // Trim whitespace. + + while (guidString.Length != 0 && *guidString.Buffer == ' ') + { + guidString.Buffer++; + guidString.Length -= 2; + } + + while (guidString.Length != 0 && guidString.Buffer[guidString.Length / 2 - 1] == ' ') + { + guidString.Length -= 2; + } + + if (NT_SUCCESS(RtlGUIDFromString(&guidString, &context->EditingInfo->SubtypeBuffer))) + { + context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer; + } + else + { + PhShowError(hwndDlg, L"The custom subtype is invalid. Please ensure that the string is a valid GUID: \"{x-x-x-x-x}\"."); + goto DoNotClose; + } + } + + if (PhEqualString2(actionString, L"Start", FALSE)) + context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START; + else + context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_STOP; + + if ( + context->EditingInfo->DataList && + context->EditingInfo->DataList->Count != 0 && + context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL && + context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT && + context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT && + context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM + ) + { + // This trigger has data items, but the trigger type doesn't allow them. + if (PhShowMessage( + hwndDlg, + MB_OKCANCEL | MB_ICONWARNING, + L"The trigger type \"%s\" does not allow data items to be configured. " + L"If you continue, they will be removed.", + typeString->Buffer + ) != IDOK) + { + goto DoNotClose; + } + + for (i = 0; i < context->EditingInfo->DataList->Count; i++) + { + EspDestroyTriggerData(context->EditingInfo->DataList->Items[i]); + } + + PhClearReference(&context->EditingInfo->DataList); + } + + EndDialog(hwndDlg, IDOK); + +DoNotClose: + PhDeleteAutoPool(&autoPool); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + + switch (header->code) + { + case LVN_ITEMCHANGED: + { + if (header->hwndFrom == lvHandle) + { + if (ListView_GetSelectedCount(lvHandle) == 1) + { + PES_TRIGGER_DATA data = PhGetSelectedListViewItemParam(GetDlgItem(hwndDlg, IDC_LIST)); + + // Editing binary data is not supported. + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), data && data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING); + EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE); + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE); + } + } + } + break; + case NM_DBLCLK: + { + if (header->hwndFrom == lvHandle) + { + SendMessage(hwndDlg, WM_COMMAND, IDC_EDIT, 0); + } + } + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK ValueDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PES_TRIGGER_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PES_TRIGGER_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSetDialogItemText(hwndDlg, IDC_VALUES, context->EditingValue->Buffer); + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDC_VALUES)); + Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUES), 0, -1); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDOK: + PhMoveReference(&context->EditingValue, PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUES))); + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedServices/triggpg.c b/plugins/ExtendedServices/triggpg.c index d8861dd6e4e8..1216d106ee63 100644 --- a/plugins/ExtendedServices/triggpg.c +++ b/plugins/ExtendedServices/triggpg.c @@ -1,182 +1,216 @@ -/* - * Process Hacker Extended Services - - * triggers page - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "extsrv.h" - -typedef struct _SERVICE_TRIGGERS_CONTEXT -{ - PPH_SERVICE_ITEM ServiceItem; - HWND TriggersLv; - struct _ES_TRIGGER_CONTEXT *TriggerContext; -} SERVICE_TRIGGERS_CONTEXT, *PSERVICE_TRIGGERS_CONTEXT; - -NTSTATUS EspLoadTriggerInfo( - _In_ HWND hwndDlg, - _In_ PSERVICE_TRIGGERS_CONTEXT Context - ) -{ - NTSTATUS status = STATUS_SUCCESS; - SC_HANDLE serviceHandle; - - if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) - return NTSTATUS_FROM_WIN32(GetLastError()); - - EsLoadServiceTriggerInfo(Context->TriggerContext, serviceHandle); - CloseServiceHandle(serviceHandle); - - return status; -} - -INT_PTR CALLBACK EspServiceTriggersDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_TRIGGERS_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = PhAllocate(sizeof(SERVICE_TRIGGERS_CONTEXT)); - memset(context, 0, sizeof(SERVICE_TRIGGERS_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_TRIGGERS_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - NTSTATUS status; - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; - HWND triggersLv; - - context->ServiceItem = serviceItem; - context->TriggersLv = triggersLv = GetDlgItem(hwndDlg, IDC_TRIGGERS); - context->TriggerContext = EsCreateServiceTriggerContext( - context->ServiceItem, - hwndDlg, - triggersLv - ); - - status = EspLoadTriggerInfo(hwndDlg, context); - - if (!NT_SUCCESS(status)) - { - PhShowWarning(hwndDlg, L"Unable to query service trigger information: %s", - ((PPH_STRING)PH_AUTO(PhGetNtMessage(status)))->Buffer); - } - } - break; - case WM_DESTROY: - { - EsDestroyServiceTriggerContext(context->TriggerContext); - PhFree(context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_NEW: - if (context->TriggerContext) - EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_NEW); - break; - case IDC_EDIT: - if (context->TriggerContext) - EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT); - break; - case IDC_DELETE: - if (context->TriggerContext) - EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_DELETE); - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_KILLACTIVE: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); - } - return TRUE; - case PSN_APPLY: - { - ULONG win32Result = 0; - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - - if (!EsSaveServiceTriggerInfo(context->TriggerContext, &win32Result)) - { - if (win32Result == ERROR_CANCELLED || (PhShowMessage( - hwndDlg, - MB_ICONERROR | MB_RETRYCANCEL, - L"Unable to change service trigger information: %s", - ((PPH_STRING)PH_AUTO(PhGetWin32Message(win32Result)))->Buffer - ) == IDRETRY)) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); - } - } - - return TRUE; - } - break; - case LVN_ITEMCHANGED: - { - if (header->hwndFrom == context->TriggersLv && context->TriggerContext) - { - EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_SELECTIONCHANGED); - } - } - break; - case NM_DBLCLK: - { - if (header->hwndFrom == context->TriggersLv && context->TriggerContext) - { - EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT); - } - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Services - + * triggers page + * + * Copyright (C) 2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "extsrv.h" + +typedef struct _SERVICE_TRIGGERS_CONTEXT +{ + PPH_SERVICE_ITEM ServiceItem; + HWND TriggersLv; + PH_LAYOUT_MANAGER LayoutManager; + struct _ES_TRIGGER_CONTEXT *TriggerContext; +} SERVICE_TRIGGERS_CONTEXT, *PSERVICE_TRIGGERS_CONTEXT; + +NTSTATUS EspLoadTriggerInfo( + _In_ HWND hwndDlg, + _In_ PSERVICE_TRIGGERS_CONTEXT Context + ) +{ + NTSTATUS status = STATUS_SUCCESS; + SC_HANDLE serviceHandle; + + if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) + return NTSTATUS_FROM_WIN32(GetLastError()); + + EsLoadServiceTriggerInfo(Context->TriggerContext, serviceHandle); + CloseServiceHandle(serviceHandle); + + return status; +} + +INT_PTR CALLBACK EspServiceTriggersDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_TRIGGERS_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(SERVICE_TRIGGERS_CONTEXT)); + memset(context, 0, sizeof(SERVICE_TRIGGERS_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; + HWND triggersLv; + + context->ServiceItem = serviceItem; + context->TriggersLv = triggersLv = GetDlgItem(hwndDlg, IDC_TRIGGERS); + context->TriggerContext = EsCreateServiceTriggerContext( + context->ServiceItem, + hwndDlg, + triggersLv + ); + + status = EspLoadTriggerInfo(hwndDlg, context); + + if (!NT_SUCCESS(status)) + { + PPH_STRING errorMessage = PhGetNtMessage(status); + + PhShowWarning( + hwndDlg, + L"Unable to query service trigger information: %s", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + ); + + PhClearReference(&errorMessage); + } + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_TRIGGERS), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_NEW), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_EDIT), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_DELETE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + EsDestroyServiceTriggerContext(context->TriggerContext); + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_NEW: + if (context->TriggerContext) + EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_NEW); + break; + case IDC_EDIT: + if (context->TriggerContext) + EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT); + break; + case IDC_DELETE: + if (context->TriggerContext) + EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_DELETE); + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_KILLACTIVE: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); + } + return TRUE; + case PSN_APPLY: + { + ULONG win32Result = ERROR_SUCCESS; + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + + if (!EsSaveServiceTriggerInfo(context->TriggerContext, &win32Result)) + { + if (win32Result == ERROR_CANCELLED) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + else + { + PPH_STRING errorMessage = PhGetWin32Message(win32Result); + + if (PhShowMessage( + hwndDlg, + MB_ICONERROR | MB_RETRYCANCEL, + L"Unable to change service trigger information: %s", + PhGetStringOrDefault(errorMessage, L"Unknown error.") + ) == IDRETRY) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + + PhClearReference(&errorMessage); + } + } + + return TRUE; + } + break; + case LVN_ITEMCHANGED: + { + if (header->hwndFrom == context->TriggersLv && context->TriggerContext) + { + EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_SELECTIONCHANGED); + } + } + break; + case NM_DBLCLK: + { + if (header->hwndFrom == context->TriggersLv && context->TriggerContext) + { + EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT); + } + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/CHANGELOG.txt b/plugins/ExtendedTools/CHANGELOG.txt index f654379850be..1ceff4a21f3f 100644 --- a/plugins/ExtendedTools/CHANGELOG.txt +++ b/plugins/ExtendedTools/CHANGELOG.txt @@ -1,68 +1,68 @@ -1.17 - * Removed legacy code - -1.16 - * Fixed "No process" disk event bug - -1.15 - * Added tray icon mini info window support - * Improved automatic GPU node selection - -1.14 - * Added Disk and Network graphs for all processes - -1.13 - * Added GPU graphs for all processes - * Can now use the search box in the Disk tab - * Improved kernel logger handling on Windows 8 - -1.12 - * Improved support for multiple GPUs (again) - * GPU column now respects "Include CPU usage of children" option - -1.11 - * Fixed network graph scaling - -1.10 - * Improved support for multiple GPUs - -1.9 - * Added GPU node selection - * Fixed incorrect GPU usage calculation - -1.8 - * Added support for new system information window - * Added Disk, Network and GPU tray icons - * Added support for custom fonts in the Disk tab - -1.7 - * Added GPU monitoring - * Added rate columns for disk and network I/O - -1.6 - * Updated disk monitoring for Windows 8 - * Updated memory list information for Windows 8 - -1.5 - * Added Disk tab - * Added Hard Faults, Hard Faults Delta and Peak Threads columns - to process tree list - * Added Firewall Status column to network list - -1.4 - * Added ETW columns for processes and network connections - -1.3 - * Fixed WS Watch refresh bug - -1.2 - * Added WS Watch - * Added display of standby repurposed pages - * Added Empty commands for memory lists - * Fixed Disk and Network page crash - -1.1 - * Fixed ETW crash - -1.0 +1.17 + * Removed legacy code + +1.16 + * Fixed "No process" disk event bug + +1.15 + * Added tray icon mini info window support + * Improved automatic GPU node selection + +1.14 + * Added Disk and Network graphs for all processes + +1.13 + * Added GPU graphs for all processes + * Can now use the search box in the Disk tab + * Improved kernel logger handling on Windows 8 + +1.12 + * Improved support for multiple GPUs (again) + * GPU column now respects "Include CPU usage of children" option + +1.11 + * Fixed network graph scaling + +1.10 + * Improved support for multiple GPUs + +1.9 + * Added GPU node selection + * Fixed incorrect GPU usage calculation + +1.8 + * Added support for new system information window + * Added Disk, Network and GPU tray icons + * Added support for custom fonts in the Disk tab + +1.7 + * Added GPU monitoring + * Added rate columns for disk and network I/O + +1.6 + * Updated disk monitoring for Windows 8 + * Updated memory list information for Windows 8 + +1.5 + * Added Disk tab + * Added Hard Faults, Hard Faults Delta and Peak Threads columns + to process tree list + * Added Firewall Status column to network list + +1.4 + * Added ETW columns for processes and network connections + +1.3 + * Fixed WS Watch refresh bug + +1.2 + * Added WS Watch + * Added display of standby repurposed pages + * Added Empty commands for memory lists + * Fixed Disk and Network page crash + +1.1 + * Fixed ETW crash + +1.0 * Initial release \ No newline at end of file diff --git a/plugins/ExtendedTools/ExtendedTools.rc b/plugins/ExtendedTools/ExtendedTools.rc index 3917a0c1aa93..d449814ffe2b 100644 --- a/plugins/ExtendedTools/ExtendedTools.rc +++ b/plugins/ExtendedTools/ExtendedTools.rc @@ -1,543 +1,511 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,17,0,0 - PRODUCTVERSION 1,17,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "Extended Tools plugin for Process Hacker" - VALUE "FileVersion", "1.17" - VALUE "InternalName", "ProcessHacker.ExtendedTools" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ExtendedTools.dll" - VALUE "ProductName", "Extended Tools plugin for Process Hacker" - VALUE "ProductVersion", "1.17" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_UNLOADEDDLLS DIALOGEX 0, 0, 342, 265 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Unloaded Modules" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,328,233 - PUSHBUTTON "Refresh",IDC_REFRESH,7,244,50,14 - DEFPUSHBUTTON "Close",IDOK,285,244,50,14 -END - -IDD_OBJALPCPORT DIALOGEX 0, 0, 160, 101 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "ALPC Port" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Sequence number: Unknown",IDC_SEQUENCENUMBER,7,7,146,8 - LTEXT "Port context: Unknown",IDC_PORTCONTEXT,7,19,146,8 -END - -IDD_OBJTPWORKERFACTORY DIALOGEX 0, 0, 186, 101 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Worker Factory" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Worker thread start: Unknown",IDC_WORKERTHREADSTART,7,7,172,8,SS_ENDELLIPSIS - LTEXT "Worker thread context: Unknown",IDC_WORKERTHREADCONTEXT,7,18,172,8,SS_ENDELLIPSIS -END - -IDD_MODSERVICES DIALOGEX 0, 0, 276, 209 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Services" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Static",IDC_MESSAGE,7,7,262,8 - LTEXT "Static",IDC_SERVICES_LAYOUT,7,22,262,162,NOT WS_VISIBLE - DEFPUSHBUTTON "Close",IDOK,219,188,50,14 -END - -IDD_PROCDISKNET DIALOGEX 0, 0, 265, 158 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Disk and Network" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Disk",IDC_GROUPDISK,7,7,251,69 - GROUPBOX "Network",IDC_GROUPNETWORK,7,81,251,69 -END - -IDD_SYSINFO_DISKPANEL DIALOGEX 0, 0, 146, 58 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Disk I/O",IDC_STATIC,0,0,145,58 - LTEXT "Reads delta",IDC_STATIC,8,11,40,8 - LTEXT "Read bytes delta",IDC_STATIC,8,22,56,8 - LTEXT "Writes delta",IDC_STATIC,8,33,40,8 - LTEXT "Write bytes delta",IDC_STATIC,8,44,57,8 - RTEXT "Static",IDC_ZREADSDELTA_V,84,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZREADBYTESDELTA_V,78,22,60,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITESDELTA_V,84,33,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,82,44,56,8,SS_ENDELLIPSIS -END - -IDD_OPTIONS DIALOGEX 0, 0, 215, 77 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Changes to settings will require a restart of Process Hacker.",IDC_STATIC,7,6,193,8 - CONTROL "Enable Disk and Network monitoring",IDC_ENABLEETWMONITOR, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,20,130,10 - CONTROL "Enable GPU monitoring",IDC_ENABLEGPUMONITOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,46,88,10 - PUSHBUTTON "Close",IDOK,158,56,50,14 - CONTROL "Enable Disk and Network graphs",IDC_ENABLESYSINFOGRAPHS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,33,130,10 -END - -IDD_WSWATCH DIALOGEX 0, 0, 325, 266 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "WS Watch" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Working set watch allows you to monitor page faults that occur in a process. You must enable WS watch for the process to start the monitoring. Once WS watch is enabled, it cannot be disabled.",IDC_STATIC,7,7,311,27 - PUSHBUTTON "Enable",IDC_ENABLE,7,37,50,14 - LTEXT "WS watch is enabled.",IDC_WSWATCHENABLED,63,40,119,8,NOT WS_VISIBLE - LTEXT "Page faults:",IDC_STATIC,7,54,40,8 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,65,311,175 - DEFPUSHBUTTON "Close",IDOK,268,245,50,14 -END - -IDD_SYSINFO_GPU DIALOGEX 0, 0, 315, 186 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "GPU" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "GPU",IDC_TITLE,0,0,72,21 - RTEXT "GPU name",IDC_GPUNAME,83,4,232,16,SS_WORDELLIPSIS - LTEXT "Panel layout",IDC_PANEL_LAYOUT,0,130,315,55,NOT WS_VISIBLE - LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,102,NOT WS_VISIBLE - LTEXT "GPU:",IDC_GPU_L,73,0,17,8 - LTEXT "Dedicated memory:",IDC_DEDICATED_L,73,5,63,8 - LTEXT "Shared memory:",IDC_SHARED_L,74,11,54,8 -END - -IDD_SYSINFO_GPUPANEL DIALOGEX 0, 0, 246, 54 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Dedicated memory",IDC_STATIC,0,0,118,36 - LTEXT "Current",IDC_STATIC,8,11,26,8 - LTEXT "Limit",IDC_STATIC,8,22,15,8 - RTEXT "Static",IDC_ZDEDICATEDCURRENT_V,55,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZDEDICATEDLIMIT_V,55,22,54,8,SS_ENDELLIPSIS - GROUPBOX "Shared memory",IDC_STATIC,122,0,124,36 - LTEXT "Current",IDC_STATIC,130,11,26,8 - LTEXT "Limit",IDC_STATIC,130,23,15,8 - RTEXT "Static",IDC_ZSHAREDCURRENT_V,182,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSHAREDLIMIT_V,182,23,54,8,SS_ENDELLIPSIS - PUSHBUTTON "Nodes",IDC_NODES,152,40,50,14 - LTEXT "Select nodes used for GPU usage calculations:",IDC_STATIC,0,43,148,8 -END - -IDD_PROCGPU DIALOGEX 0, 0, 369, 237 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "GPU" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "GPU",IDC_GROUPGPU,7,7,355,69 - GROUPBOX "Dedicated memory",IDC_GROUPMEM,7,81,355,69 - GROUPBOX "Shared memory",IDC_GROUPSHARED,7,160,355,69 -END - -IDD_SYSINFO_DISK DIALOGEX 0, 0, 315, 186 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Disk" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Disk",IDC_TITLE,0,0,72,21 - LTEXT "Panel layout",IDC_PANEL_LAYOUT,0,124,315,61,NOT WS_VISIBLE - LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,99,NOT WS_VISIBLE -END - -IDD_SYSINFO_NETPANEL DIALOGEX 0, 0, 145, 58 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Network I/O",IDC_STATIC,0,0,145,58 - LTEXT "Receives delta",IDC_STATIC,8,11,48,8 - LTEXT "Receive bytes delta",IDC_STATIC,8,22,65,8 - LTEXT "Sends delta",IDC_STATIC,8,33,39,8 - LTEXT "Send bytes delta",IDC_STATIC,8,44,56,8 - RTEXT "Static",IDC_ZRECEIVESDELTA_V,84,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZRECEIVEBYTESDELTA_V,78,22,60,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSENDSDELTA_V,84,33,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSENDBYTESDELTA_V,82,44,56,8,SS_ENDELLIPSIS -END - -IDD_SYSINFO_NET DIALOGEX 0, 0, 315, 186 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Network" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Network",IDC_TITLE,0,0,72,21 - LTEXT "Panel layout",IDC_PANEL_LAYOUT,0,124,315,61,NOT WS_VISIBLE - LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,99,NOT WS_VISIBLE -END - -IDD_GPUNODES DIALOGEX 0, 0, 317, 183 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "GPU Nodes" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Select the nodes that will be included in GPU usage calculations.",IDC_INSTRUCTION,7,7,204,8 - LTEXT "Graph layout",IDC_LAYOUT,7,21,303,137,NOT WS_VISIBLE - CONTROL "Example checkbox",IDC_EXAMPLE,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,7,166,75,10 - DEFPUSHBUTTON "OK",IDOK,260,162,50,14 -END - -IDD_PROCGPU_PANEL DIALOGEX 0, 0, 248, 46 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Memory",IDC_STATIC,2,0,118,46 - GROUPBOX "Nodes",IDC_STATIC,124,0,124,46 - LTEXT "Running time",IDC_STATIC,132,11,44,8 - LTEXT "Context switches",IDC_STATIC,132,22,57,8 - RTEXT "Static",IDC_ZRUNNINGTIME_V,184,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCONTEXTSWITCHES_V,200,22,38,8,SS_ENDELLIPSIS - LTEXT "Total segments",IDC_STATIC,10,11,50,8 - RTEXT "Static",IDC_ZTOTALSEGMENTS_V,73,11,38,8,SS_ENDELLIPSIS - LTEXT "Total nodes",IDC_STATIC,132,33,39,8 - RTEXT "Static",IDC_ZTOTALNODES_V,200,33,38,8,SS_ENDELLIPSIS - PUSHBUTTON "More",IDC_GPUDETAILS,7,27,50,14 -END - -IDD_DISKTABERROR DIALOGEX 0, 0, 309, 176 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU -EXSTYLE WS_EX_TRANSPARENT -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Disk monitoring requires Process Hacker to be restarted with administrative privileges.",IDC_ERROR,16,14,275,8 - PUSHBUTTON "Restart",IDC_RESTART,20,28,50,14 -END - -IDD_PROCDISKNET_PANEL DIALOGEX 0, 0, 248, 82 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Disk I/O",IDC_STATIC,2,0,118,82 - LTEXT "Reads",IDC_STATIC,10,11,21,8 - LTEXT "Read bytes",IDC_STATIC,10,22,38,8 - LTEXT "Read bytes delta",IDC_STATIC,10,33,56,8 - LTEXT "Writes",IDC_STATIC,10,44,22,8 - LTEXT "Write bytes",IDC_STATIC,10,55,38,8 - LTEXT "Write bytes delta",IDC_STATIC,10,66,57,8 - RTEXT "Static",IDC_ZREADS_V,57,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZREADBYTES_V,73,22,38,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZREADBYTESDELTA_V,73,33,38,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITES_V,57,44,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITEBYTES_V,73,55,38,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,73,66,38,8,SS_ENDELLIPSIS - GROUPBOX "Network I/O",IDC_STATIC,123,0,124,82 - LTEXT "Receives",IDC_STATIC,132,11,30,8 - LTEXT "Receive bytes",IDC_STATIC,132,23,46,8 - LTEXT "Receive bytes delta",IDC_STATIC,132,34,65,8 - LTEXT "Sends",IDC_STATIC,132,46,20,8 - LTEXT "Send bytes",IDC_STATIC,132,56,37,8 - LTEXT "Send bytes delta",IDC_STATIC,132,67,56,8 - RTEXT "Static",IDC_ZRECEIVES_V,184,11,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZRECEIVEBYTES_V,200,23,38,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZRECEIVEBYTESDELTA_V,200,34,38,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSENDS_V,184,46,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSENDBYTES_V,200,56,38,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSENDBYTESDELTA_V,200,67,38,8,SS_ENDELLIPSIS -END - -IDD_PROCGPU_DETAILS DIALOGEX 0, 0, 181, 155 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "GPU Details" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "Close",IDCANCEL,124,134,50,14 - GROUPBOX "System memory",IDC_STATIC,7,7,167,125 - LTEXT "Dedicated",IDC_STATIC,15,19,33,8 - LTEXT "Shared",IDC_STATIC,15,30,24,8 - RTEXT "Static",IDC_ZDEDICATEDCOMMITTED_V,111,19,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSHAREDCOMMITTED_V,127,30,38,8,SS_ENDELLIPSIS - LTEXT "Total allocated",IDC_STATIC,15,41,48,8 - LTEXT "Total reserved",IDC_STATIC,15,51,50,8 - LTEXT "Write combined allocated",IDC_STATIC,15,62,83,8 - LTEXT "Write combined reserved",IDC_STATIC,15,73,84,8 - RTEXT "Static",IDC_ZTOTALALLOCATED_V,111,41,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZTOTALRESERVED_V,111,51,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITECOMBINEDALLOCATED_V,111,62,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZWRITECOMBINEDRESERVED_V,111,73,54,8,SS_ENDELLIPSIS - LTEXT "Cached allocated",IDC_STATIC,15,84,56,8 - LTEXT "Cached reserved",IDC_STATIC,15,95,58,8 - LTEXT "Section allocated",IDC_STATIC,15,106,56,8 - LTEXT "Section reserved",IDC_STATIC,15,117,57,8 - RTEXT "Static",IDC_ZCACHEDALLOCATED_V,111,84,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZCACHEDRESERVED_V,111,95,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSECTIONALLOCATED_V,111,106,54,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_ZSECTIONRESERVED_V,111,117,54,8,SS_ENDELLIPSIS -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_UNLOADEDDLLS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 335 - TOPMARGIN, 7 - BOTTOMMARGIN, 258 - END - - IDD_OBJALPCPORT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 153 - TOPMARGIN, 7 - BOTTOMMARGIN, 94 - END - - IDD_OBJTPWORKERFACTORY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 94 - END - - IDD_MODSERVICES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 269 - TOPMARGIN, 7 - BOTTOMMARGIN, 202 - END - - IDD_PROCDISKNET, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 258 - TOPMARGIN, 7 - BOTTOMMARGIN, 151 - END - - IDD_SYSINFO_DISKPANEL, DIALOG - BEGIN - END - - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 208 - TOPMARGIN, 7 - BOTTOMMARGIN, 70 - END - - IDD_WSWATCH, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 318 - TOPMARGIN, 7 - BOTTOMMARGIN, 259 - END - - IDD_SYSINFO_GPU, DIALOG - BEGIN - END - - IDD_SYSINFO_GPUPANEL, DIALOG - BEGIN - END - - IDD_PROCGPU, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 362 - TOPMARGIN, 7 - BOTTOMMARGIN, 230 - END - - IDD_SYSINFO_DISK, DIALOG - BEGIN - END - - IDD_SYSINFO_NETPANEL, DIALOG - BEGIN - END - - IDD_SYSINFO_NET, DIALOG - BEGIN - END - - IDD_GPUNODES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 310 - TOPMARGIN, 7 - BOTTOMMARGIN, 176 - END - - IDD_PROCGPU_PANEL, DIALOG - BEGIN - END - - IDD_DISKTABERROR, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 302 - TOPMARGIN, 7 - BOTTOMMARGIN, 169 - END - - IDD_PROCDISKNET_PANEL, DIALOG - BEGIN - END - - IDD_PROCGPU_DETAILS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 174 - TOPMARGIN, 7 - BOTTOMMARGIN, 148 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_DISK MENU -BEGIN - POPUP "Disk" - BEGIN - MENUITEM "&Go to process", ID_DISK_GOTOPROCESS - MENUITEM SEPARATOR - MENUITEM "Open &file location\aEnter", ID_DISK_OPENFILELOCATION - MENUITEM "P&roperties", ID_DISK_PROPERTIES - MENUITEM "&Copy\aCtrl+C", ID_DISK_COPY - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OBJTPWORKERFACTORY AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCGPU_DETAILS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PROCGPU_PANEL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,17,0,0 + PRODUCTVERSION 1,17,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "wj32" + VALUE "FileDescription", "Extended Tools plugin for Process Hacker" + VALUE "FileVersion", "1.17" + VALUE "InternalName", "ProcessHacker.ExtendedTools" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "ExtendedTools.dll" + VALUE "ProductName", "Extended Tools plugin for Process Hacker" + VALUE "ProductVersion", "1.17" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_UNLOADEDDLLS DIALOGEX 0, 0, 342, 265 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Unloaded Modules" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,328,233 + PUSHBUTTON "Refresh",IDC_REFRESH,7,244,50,14 + DEFPUSHBUTTON "Close",IDOK,285,244,50,14 +END + +IDD_OBJTPWORKERFACTORY DIALOGEX 0, 0, 186, 101 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Worker Factory" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Worker thread start: Unknown",IDC_WORKERTHREADSTART,7,7,172,8,SS_ENDELLIPSIS + LTEXT "Worker thread context: Unknown",IDC_WORKERTHREADCONTEXT,7,18,172,8,SS_ENDELLIPSIS +END + +IDD_MODSERVICES DIALOGEX 0, 0, 276, 209 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Services" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Static",IDC_MESSAGE,7,7,262,8 + LTEXT "Static",IDC_SERVICES_LAYOUT,7,22,262,162,NOT WS_VISIBLE + DEFPUSHBUTTON "Close",IDOK,219,188,50,14 +END + +IDD_PROCDISKNET DIALOGEX 0, 0, 265, 158 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Disk and Network" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Disk",IDC_GROUPDISK,7,7,251,69,0,WS_EX_TRANSPARENT + GROUPBOX "Network",IDC_GROUPNETWORK,7,81,251,69,0,WS_EX_TRANSPARENT +END + +IDD_SYSINFO_DISKPANEL DIALOGEX 0, 0, 146, 58 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Disk I/O",IDC_STATIC,0,0,145,58 + LTEXT "Reads delta",IDC_STATIC,8,11,40,8 + LTEXT "Read bytes delta",IDC_STATIC,8,22,56,8 + LTEXT "Writes delta",IDC_STATIC,8,33,40,8 + LTEXT "Write bytes delta",IDC_STATIC,8,44,57,8 + RTEXT "Static",IDC_ZREADSDELTA_V,84,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZREADBYTESDELTA_V,78,22,60,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITESDELTA_V,84,33,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,82,44,56,8,SS_ENDELLIPSIS +END + +IDD_OPTIONS DIALOGEX 0, 0, 215, 77 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Changes to settings will require a restart of Process Hacker.",IDC_STATIC,7,6,193,8 + CONTROL "Enable Disk and Network monitoring",IDC_ENABLEETWMONITOR, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,20,130,10 + CONTROL "Enable GPU monitoring",IDC_ENABLEGPUMONITOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,46,88,10 + CONTROL "Enable Disk and Network graphs",IDC_ENABLESYSINFOGRAPHS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,33,130,10 +END + +IDD_WSWATCH DIALOGEX 0, 0, 325, 266 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "WS Watch" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Working set watch allows you to monitor page faults that occur in a process. You must enable WS watch for the process to start the monitoring. Once WS watch is enabled, it cannot be disabled.",IDC_STATIC,7,7,311,27 + PUSHBUTTON "Enable",IDC_ENABLE,7,37,50,14 + LTEXT "WS watch is enabled.",IDC_WSWATCHENABLED,63,40,119,8,NOT WS_VISIBLE + LTEXT "Page faults:",IDC_STATIC,7,54,40,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,65,311,177 + DEFPUSHBUTTON "Close",IDOK,269,245,50,14 +END + +IDD_SYSINFO_GPU DIALOGEX 0, 0, 315, 186 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "GPU" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "GPU",IDC_TITLE,0,0,72,21 + RTEXT "GPU name",IDC_GPUNAME,83,4,232,16,SS_WORDELLIPSIS + LTEXT "Panel layout",IDC_PANEL_LAYOUT,0,146,315,39,NOT WS_VISIBLE + LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,121,NOT WS_VISIBLE + LTEXT "GPU:",IDC_GPU_L,73,0,17,8 + LTEXT "Dedicated memory:",IDC_DEDICATED_L,73,5,63,8 + LTEXT "Shared memory:",IDC_SHARED_L,74,11,54,8 +END + +IDD_SYSINFO_GPUPANEL DIALOGEX 0, 0, 297, 38 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Dedicated memory",IDC_STATIC,0,0,118,35 + LTEXT "Current",IDC_STATIC,8,11,26,8 + LTEXT "Limit",IDC_STATIC,8,22,15,8 + RTEXT "Static",IDC_ZDEDICATEDCURRENT_V,56,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZDEDICATEDLIMIT_V,56,22,54,8,SS_ENDELLIPSIS + GROUPBOX "Shared memory",IDC_STATIC,122,0,119,35 + LTEXT "Current",IDC_STATIC,130,11,26,8 + LTEXT "Limit",IDC_STATIC,130,23,15,8 + RTEXT "Static",IDC_ZSHAREDCURRENT_V,182,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSHAREDLIMIT_V,182,23,54,8,SS_ENDELLIPSIS + PUSHBUTTON "Nodes",IDC_NODES,244,4,50,14 + PUSHBUTTON "Details",IDC_DETAILS,244,21,50,14 +END + +IDD_PROCGPU DIALOGEX 0, 0, 369, 237 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "GPU" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "GPU",IDC_GROUPGPU,7,7,355,53,0,WS_EX_TRANSPARENT + GROUPBOX "Dedicated memory",IDC_GROUPMEM,7,63,355,53,0,WS_EX_TRANSPARENT + GROUPBOX "Shared memory",IDC_GROUPSHARED,7,119,355,53,0,WS_EX_TRANSPARENT + GROUPBOX "Commit memory",IDC_GROUPCOMMIT,7,175,355,53,0,WS_EX_TRANSPARENT +END + +IDD_SYSINFO_DISK DIALOGEX 0, 0, 315, 186 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Disk" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Disk",IDC_TITLE,0,0,72,21 + LTEXT "Panel layout",IDC_PANEL_LAYOUT,0,124,315,61,NOT WS_VISIBLE + LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,99,NOT WS_VISIBLE +END + +IDD_SYSINFO_NETPANEL DIALOGEX 0, 0, 145, 58 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Network I/O",IDC_STATIC,0,0,145,58 + LTEXT "Receives delta",IDC_STATIC,8,11,48,8 + LTEXT "Receive bytes delta",IDC_STATIC,8,22,65,8 + LTEXT "Sends delta",IDC_STATIC,8,33,39,8 + LTEXT "Send bytes delta",IDC_STATIC,8,44,56,8 + RTEXT "Static",IDC_ZRECEIVESDELTA_V,84,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZRECEIVEBYTESDELTA_V,78,22,60,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSENDSDELTA_V,84,33,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSENDBYTESDELTA_V,82,44,56,8,SS_ENDELLIPSIS +END + +IDD_SYSINFO_NET DIALOGEX 0, 0, 315, 186 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Network" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Network",IDC_TITLE,0,0,72,21 + LTEXT "Panel layout",IDC_PANEL_LAYOUT,0,124,315,61,NOT WS_VISIBLE + LTEXT "Graph layout",IDC_GRAPH_LAYOUT,0,22,315,99,NOT WS_VISIBLE +END + +IDD_GPUNODES DIALOGEX 0, 0, 317, 183 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "GPU Nodes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Graph layout",IDC_LAYOUT,3,3,311,177,NOT WS_VISIBLE +END + +IDD_PROCGPU_PANEL DIALOGEX 0, 0, 248, 46 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Memory",IDC_STATIC,2,0,118,46 + GROUPBOX "Nodes",IDC_STATIC,124,0,124,46 + LTEXT "Running time",IDC_STATIC,132,11,44,8 + LTEXT "Context switches",IDC_STATIC,132,22,57,8 + RTEXT "Static",IDC_ZRUNNINGTIME_V,184,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZCONTEXTSWITCHES_V,200,22,38,8,SS_ENDELLIPSIS + LTEXT "Total segments",IDC_STATIC,10,11,50,8 + RTEXT "Static",IDC_ZTOTALSEGMENTS_V,73,11,38,8,SS_ENDELLIPSIS + LTEXT "Total nodes",IDC_STATIC,132,33,39,8 + RTEXT "Static",IDC_ZTOTALNODES_V,200,33,38,8,SS_ENDELLIPSIS + PUSHBUTTON "More",IDC_GPUDETAILS,7,27,50,14 +END + +IDD_DISKTABERROR DIALOGEX 0, 0, 309, 176 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_TRANSPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Disk monitoring requires Process Hacker to be restarted with administrative privileges.",IDC_ERROR,16,14,275,8 + PUSHBUTTON "Restart",IDC_RESTART,20,28,50,14 +END + +IDD_PROCDISKNET_PANEL DIALOGEX 0, 0, 248, 82 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Disk I/O",IDC_STATIC,2,0,118,82 + LTEXT "Reads",IDC_STATIC,10,11,21,8 + LTEXT "Read bytes",IDC_STATIC,10,22,38,8 + LTEXT "Read bytes delta",IDC_STATIC,10,33,56,8 + LTEXT "Writes",IDC_STATIC,10,44,22,8 + LTEXT "Write bytes",IDC_STATIC,10,55,38,8 + LTEXT "Write bytes delta",IDC_STATIC,10,66,57,8 + RTEXT "Static",IDC_ZREADS_V,57,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZREADBYTES_V,73,22,38,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZREADBYTESDELTA_V,73,33,38,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITES_V,57,44,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITEBYTES_V,73,55,38,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITEBYTESDELTA_V,73,66,38,8,SS_ENDELLIPSIS + GROUPBOX "Network I/O",IDC_STATIC,123,0,124,82 + LTEXT "Receives",IDC_STATIC,132,11,30,8 + LTEXT "Receive bytes",IDC_STATIC,132,23,46,8 + LTEXT "Receive bytes delta",IDC_STATIC,132,34,65,8 + LTEXT "Sends",IDC_STATIC,132,45,20,8 + LTEXT "Send bytes",IDC_STATIC,132,56,37,8 + LTEXT "Send bytes delta",IDC_STATIC,132,67,56,8 + RTEXT "Static",IDC_ZRECEIVES_V,184,11,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZRECEIVEBYTES_V,200,23,38,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZRECEIVEBYTESDELTA_V,200,34,38,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSENDS_V,184,45,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSENDBYTES_V,200,56,38,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSENDBYTESDELTA_V,200,67,38,8,SS_ENDELLIPSIS +END + +IDD_PROCGPU_DETAILS DIALOGEX 0, 0, 181, 155 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "GPU Details" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Close",IDCANCEL,124,134,50,14 + GROUPBOX "System memory",IDC_STATIC,7,7,167,125 + LTEXT "Dedicated",IDC_STATIC,15,19,33,8 + LTEXT "Shared",IDC_STATIC,15,30,24,8 + RTEXT "Static",IDC_ZDEDICATEDCOMMITTED_V,111,19,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSHAREDCOMMITTED_V,127,30,38,8,SS_ENDELLIPSIS + LTEXT "Total allocated",IDC_STATIC,15,41,48,8 + LTEXT "Total reserved",IDC_STATIC,15,51,50,8 + LTEXT "Write combined allocated",IDC_STATIC,15,62,83,8 + LTEXT "Write combined reserved",IDC_STATIC,15,73,84,8 + RTEXT "Static",IDC_ZTOTALALLOCATED_V,111,41,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZTOTALRESERVED_V,111,51,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITECOMBINEDALLOCATED_V,111,62,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZWRITECOMBINEDRESERVED_V,111,73,54,8,SS_ENDELLIPSIS + LTEXT "Cached allocated",IDC_STATIC,15,84,56,8 + LTEXT "Cached reserved",IDC_STATIC,15,95,58,8 + LTEXT "Section allocated",IDC_STATIC,15,106,56,8 + LTEXT "Section reserved",IDC_STATIC,15,117,57,8 + RTEXT "Static",IDC_ZCACHEDALLOCATED_V,111,84,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZCACHEDRESERVED_V,111,95,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSECTIONALLOCATED_V,111,106,54,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_ZSECTIONRESERVED_V,111,117,54,8,SS_ENDELLIPSIS +END + +IDD_SYSINFO_GPUDETAILS DIALOGEX 0, 0, 309, 210 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "GPU Adapter Details" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_GPULIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_TABSTOP,0,0,309,210 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_UNLOADEDDLLS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 335 + TOPMARGIN, 7 + BOTTOMMARGIN, 258 + END + + IDD_OBJTPWORKERFACTORY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 94 + END + + IDD_MODSERVICES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 269 + TOPMARGIN, 7 + BOTTOMMARGIN, 202 + END + + IDD_PROCDISKNET, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 258 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END + + IDD_SYSINFO_DISKPANEL, DIALOG + BEGIN + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 208 + TOPMARGIN, 7 + BOTTOMMARGIN, 70 + END + + IDD_WSWATCH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 318 + TOPMARGIN, 7 + BOTTOMMARGIN, 259 + END + + IDD_SYSINFO_GPU, DIALOG + BEGIN + END + + IDD_SYSINFO_GPUPANEL, DIALOG + BEGIN + BOTTOMMARGIN, 36 + END + + IDD_PROCGPU, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 362 + TOPMARGIN, 7 + BOTTOMMARGIN, 230 + END + + IDD_SYSINFO_DISK, DIALOG + BEGIN + END + + IDD_SYSINFO_NETPANEL, DIALOG + BEGIN + END + + IDD_SYSINFO_NET, DIALOG + BEGIN + END + + IDD_GPUNODES, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 314 + TOPMARGIN, 3 + BOTTOMMARGIN, 180 + END + + IDD_PROCGPU_PANEL, DIALOG + BEGIN + END + + IDD_DISKTABERROR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 302 + TOPMARGIN, 7 + BOTTOMMARGIN, 169 + END + + IDD_PROCDISKNET_PANEL, DIALOG + BEGIN + END + + IDD_PROCGPU_DETAILS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 174 + TOPMARGIN, 7 + BOTTOMMARGIN, 148 + END + + IDD_SYSINFO_GPUDETAILS, DIALOG + BEGIN + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_DISK MENU +BEGIN + POPUP "Disk" + BEGIN + MENUITEM "&Go to process", ID_DISK_GOTOPROCESS + MENUITEM SEPARATOR + MENUITEM "Open &file location\aEnter", ID_DISK_OPENFILELOCATION + MENUITEM "P&roperties", ID_DISK_PROPERTIES + MENUITEM "&Copy\aCtrl+C", ID_DISK_COPY + END +END + + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/ExtendedTools/ExtendedTools.vcxproj b/plugins/ExtendedTools/ExtendedTools.vcxproj index 478dc51ff4c3..3ffc4f1cf357 100644 --- a/plugins/ExtendedTools/ExtendedTools.vcxproj +++ b/plugins/ExtendedTools/ExtendedTools.vcxproj @@ -1,118 +1,123 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {3437FD16-A3BF-4938-883A-EB0DF1584A2A} - ExtendedTools - Win32Proj - ExtendedTools - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - cfgmgr32.lib;%(AdditionalDependencies) - - - - - cfgmgr32.lib;%(AdditionalDependencies) - - - - - cfgmgr32.lib;%(AdditionalDependencies) - - - - - cfgmgr32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {3437FD16-A3BF-4938-883A-EB0DF1584A2A} + ExtendedTools + Win32Proj + ExtendedTools + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + cfgmgr32.lib;%(AdditionalDependencies) + advapi32.dll;comctl32.dll;cfgmgr32.dll;gdi32.dll;ole32.dll;oleaut32.dll;user32.dll;%(DelayLoadDLLs) + + + + + cfgmgr32.lib;%(AdditionalDependencies) + advapi32.dll;comctl32.dll;cfgmgr32.dll;gdi32.dll;ole32.dll;oleaut32.dll;user32.dll;%(DelayLoadDLLs) + + + + + cfgmgr32.lib;%(AdditionalDependencies) + advapi32.dll;comctl32.dll;cfgmgr32.dll;gdi32.dll;ole32.dll;oleaut32.dll;user32.dll;%(DelayLoadDLLs) + + + + + cfgmgr32.lib;%(AdditionalDependencies) + advapi32.dll;comctl32.dll;cfgmgr32.dll;gdi32.dll;ole32.dll;oleaut32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ExtendedTools/ExtendedTools.vcxproj.filters b/plugins/ExtendedTools/ExtendedTools.vcxproj.filters index 7938f409aef9..8f88f9af3cc5 100644 --- a/plugins/ExtendedTools/ExtendedTools.vcxproj.filters +++ b/plugins/ExtendedTools/ExtendedTools.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + {d3096a42-7c1f-4edf-a5d2-d086f5e5b174} + @@ -57,31 +60,34 @@ Source Files - - Source Files - - + Source Files - + Source Files - + Source Files - - Source Files + + Source Files\Video - - Source Files + + Source Files\Video - Source Files + Source Files\Video - - Source Files + + Source Files\Video - + + Source Files\Video + + + Source Files\Video + + Source Files diff --git a/plugins/ExtendedTools/d3dkmt.h b/plugins/ExtendedTools/d3dkmt.h index 863b3b9a3ee5..cbf352d38dcd 100644 --- a/plugins/ExtendedTools/d3dkmt.h +++ b/plugins/ExtendedTools/d3dkmt.h @@ -1,496 +1,2049 @@ -#ifndef _D3DKMT_H -#define _D3DKMT_H - -// D3D definitions - -typedef ULONG D3DKMT_HANDLE; - -typedef struct _D3DKMT_OPENADAPTERFROMDEVICENAME -{ - _In_ PCWSTR pDeviceName; - _Out_ D3DKMT_HANDLE hAdapter; - _Out_ LUID AdapterLuid; -} D3DKMT_OPENADAPTERFROMDEVICENAME; - -typedef struct _D3DKMT_CLOSEADAPTER -{ - _In_ D3DKMT_HANDLE hAdapter; -} D3DKMT_CLOSEADAPTER; - -typedef enum _D3DKMT_QUERYRESULT_PREEMPTION_ATTEMPT_RESULT -{ - D3DKMT_PreemptionAttempt = 0, - D3DKMT_PreemptionAttemptSuccess = 1, - D3DKMT_PreemptionAttemptMissNoCommand = 2, - D3DKMT_PreemptionAttemptMissNotEnabled = 3, - D3DKMT_PreemptionAttemptMissNextFence = 4, - D3DKMT_PreemptionAttemptMissPagingCommand = 5, - D3DKMT_PreemptionAttemptMissSplittedCommand = 6, - D3DKMT_PreemptionAttemptMissFenceCommand= 7, - D3DKMT_PreemptionAttemptMissRenderPendingFlip = 8, - D3DKMT_PreemptionAttemptMissNotMakingProgress = 9, - D3DKMT_PreemptionAttemptMissLessPriority = 10, - D3DKMT_PreemptionAttemptMissRemainingQuantum = 11, - D3DKMT_PreemptionAttemptMissRemainingPreemptionQuantum = 12, - D3DKMT_PreemptionAttemptMissAlreadyPreempting = 13, - D3DKMT_PreemptionAttemptMissGlobalBlock = 14, - D3DKMT_PreemptionAttemptMissAlreadyRunning = 15, - D3DKMT_PreemptionAttemptStatisticsMax -} D3DKMT_QUERYRESULT_PREEMPTION_ATTEMPT_RESULT; - -typedef enum _D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE -{ - D3DKMT_ClientRenderBuffer = 0, - D3DKMT_ClientPagingBuffer = 1, - D3DKMT_SystemPagingBuffer = 2, - D3DKMT_SystemPreemptionBuffer = 3, - D3DKMT_DmaPacketTypeMax -} D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE; - -typedef enum _D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE -{ - D3DKMT_RenderCommandBuffer = 0, - D3DKMT_DeferredCommandBuffer = 1, - D3DKMT_SystemCommandBuffer = 2, - D3DKMT_MmIoFlipCommandBuffer = 3, - D3DKMT_WaitCommandBuffer = 4, - D3DKMT_SignalCommandBuffer = 5, - D3DKMT_DeviceCommandBuffer = 6, - D3DKMT_SoftwareCommandBuffer = 7, - D3DKMT_QueuePacketTypeMax -} D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE; - -typedef enum _D3DKMT_QUERYSTATISTICS_ALLOCATION_PRIORITY_CLASS -{ - D3DKMT_AllocationPriorityClassMinimum = 0, - D3DKMT_AllocationPriorityClassLow = 1, - D3DKMT_AllocationPriorityClassNormal = 2, - D3DKMT_AllocationPriorityClassHigh = 3, - D3DKMT_AllocationPriorityClassMaximum = 4, - D3DKMT_MaxAllocationPriorityClass -} D3DKMT_QUERYSTATISTICS_ALLOCATION_PRIORITY_CLASS; - -#define D3DKMT_QUERYSTATISTICS_SEGMENT_PREFERENCE_MAX 5 - -typedef struct _D3DKMT_QUERYSTATISTICS_COUNTER -{ - ULONG Count; - ULONGLONG Bytes; -} D3DKMT_QUERYSTATISTICS_COUNTER; - -typedef struct _D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE_INFORMATION -{ - ULONG PacketSubmited; - ULONG PacketCompleted; - ULONG PacketPreempted; - ULONG PacketFaulted; -} D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE_INFORMATION -{ - ULONG PacketSubmited; - ULONG PacketCompleted; -} D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_PACKET_INFORMATION -{ - D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE_INFORMATION QueuePacket[D3DKMT_QueuePacketTypeMax]; - D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE_INFORMATION DmaPacket[D3DKMT_DmaPacketTypeMax]; -} D3DKMT_QUERYSTATISTICS_PACKET_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_PREEMPTION_INFORMATION -{ - ULONG PreemptionCounter[D3DKMT_PreemptionAttemptStatisticsMax]; -} D3DKMT_QUERYSTATISTICS_PREEMPTION_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION -{ - LARGE_INTEGER RunningTime; // 100ns - ULONG ContextSwitch; - D3DKMT_QUERYSTATISTICS_PREEMPTION_INFORMATION PreemptionStatistics; - D3DKMT_QUERYSTATISTICS_PACKET_INFORMATION PacketStatistics; - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_NODE_INFORMATION -{ - D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION GlobalInformation; // global - D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION SystemInformation; // system thread - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_NODE_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION -{ - ULONG Frame; - ULONG CancelledFrame; - ULONG QueuedPresent; - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_VIDPNSOURCE_INFORMATION -{ - D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION GlobalInformation; // global - D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION SystemInformation; // system thread - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_VIDPNSOURCE_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATSTICS_REFERENCE_DMA_BUFFER -{ - ULONG NbCall; - ULONG NbAllocationsReferenced; - ULONG MaxNbAllocationsReferenced; - ULONG NbNULLReference; - ULONG NbWriteReference; - ULONG NbRenamedAllocationsReferenced; - ULONG NbIterationSearchingRenamedAllocation; - ULONG NbLockedAllocationReferenced; - ULONG NbAllocationWithValidPrepatchingInfoReferenced; - ULONG NbAllocationWithInvalidPrepatchingInfoReferenced; - ULONG NbDMABufferSuccessfullyPrePatched; - ULONG NbPrimariesReferencesOverflow; - ULONG NbAllocationWithNonPreferredResources; - ULONG NbAllocationInsertedInMigrationTable; -} D3DKMT_QUERYSTATSTICS_REFERENCE_DMA_BUFFER; - -typedef struct _D3DKMT_QUERYSTATSTICS_RENAMING -{ - ULONG NbAllocationsRenamed; - ULONG NbAllocationsShrinked; - ULONG NbRenamedBuffer; - ULONG MaxRenamingListLength; - ULONG NbFailuresDueToRenamingLimit; - ULONG NbFailuresDueToCreateAllocation; - ULONG NbFailuresDueToOpenAllocation; - ULONG NbFailuresDueToLowResource; - ULONG NbFailuresDueToNonRetiredLimit; -} D3DKMT_QUERYSTATSTICS_RENAMING; - -typedef struct _D3DKMT_QUERYSTATSTICS_PREPRATION -{ - ULONG BroadcastStall; - ULONG NbDMAPrepared; - ULONG NbDMAPreparedLongPath; - ULONG ImmediateHighestPreparationPass; - D3DKMT_QUERYSTATISTICS_COUNTER AllocationsTrimmed; -} D3DKMT_QUERYSTATSTICS_PREPRATION; - -typedef struct _D3DKMT_QUERYSTATSTICS_PAGING_FAULT -{ - D3DKMT_QUERYSTATISTICS_COUNTER Faults; - D3DKMT_QUERYSTATISTICS_COUNTER FaultsFirstTimeAccess; - D3DKMT_QUERYSTATISTICS_COUNTER FaultsReclaimed; - D3DKMT_QUERYSTATISTICS_COUNTER FaultsMigration; - D3DKMT_QUERYSTATISTICS_COUNTER FaultsIncorrectResource; - D3DKMT_QUERYSTATISTICS_COUNTER FaultsLostContent; - D3DKMT_QUERYSTATISTICS_COUNTER FaultsEvicted; - D3DKMT_QUERYSTATISTICS_COUNTER AllocationsMEM_RESET; - D3DKMT_QUERYSTATISTICS_COUNTER AllocationsUnresetSuccess; - D3DKMT_QUERYSTATISTICS_COUNTER AllocationsUnresetFail; - ULONG AllocationsUnresetSuccessRead; - ULONG AllocationsUnresetFailRead; - - D3DKMT_QUERYSTATISTICS_COUNTER Evictions; - D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToPreparation; - D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToLock; - D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToClose; - D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToPurge; - D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToSuspendCPUAccess; -} D3DKMT_QUERYSTATSTICS_PAGING_FAULT; - -typedef struct _D3DKMT_QUERYSTATSTICS_PAGING_TRANSFER -{ - ULONGLONG BytesFilled; - ULONGLONG BytesDiscarded; - ULONGLONG BytesMappedIntoAperture; - ULONGLONG BytesUnmappedFromAperture; - ULONGLONG BytesTransferredFromMdlToMemory; - ULONGLONG BytesTransferredFromMemoryToMdl; - ULONGLONG BytesTransferredFromApertureToMemory; - ULONGLONG BytesTransferredFromMemoryToAperture; -} D3DKMT_QUERYSTATSTICS_PAGING_TRANSFER; - -typedef struct _D3DKMT_QUERYSTATSTICS_SWIZZLING_RANGE -{ - ULONG NbRangesAcquired; - ULONG NbRangesReleased; -} D3DKMT_QUERYSTATSTICS_SWIZZLING_RANGE; - -typedef struct _D3DKMT_QUERYSTATSTICS_LOCKS -{ - ULONG NbLocks; - ULONG NbLocksWaitFlag; - ULONG NbLocksDiscardFlag; - ULONG NbLocksNoOverwrite; - ULONG NbLocksNoReadSync; - ULONG NbLocksLinearization; - ULONG NbComplexLocks; -} D3DKMT_QUERYSTATSTICS_LOCKS; - -typedef struct _D3DKMT_QUERYSTATSTICS_ALLOCATIONS -{ - D3DKMT_QUERYSTATISTICS_COUNTER Created; - D3DKMT_QUERYSTATISTICS_COUNTER Destroyed; - D3DKMT_QUERYSTATISTICS_COUNTER Opened; - D3DKMT_QUERYSTATISTICS_COUNTER Closed; - D3DKMT_QUERYSTATISTICS_COUNTER MigratedSuccess; - D3DKMT_QUERYSTATISTICS_COUNTER MigratedFail; - D3DKMT_QUERYSTATISTICS_COUNTER MigratedAbandoned; -} D3DKMT_QUERYSTATSTICS_ALLOCATIONS; - -typedef struct _D3DKMT_QUERYSTATSTICS_TERMINATIONS -{ - D3DKMT_QUERYSTATISTICS_COUNTER TerminatedShared; - D3DKMT_QUERYSTATISTICS_COUNTER TerminatedNonShared; - D3DKMT_QUERYSTATISTICS_COUNTER DestroyedShared; - D3DKMT_QUERYSTATISTICS_COUNTER DestroyedNonShared; -} D3DKMT_QUERYSTATSTICS_TERMINATIONS; - -typedef struct _D3DKMT_QUERYSTATISTICS_ADAPTER_INFORMATION -{ - ULONG NbSegments; - ULONG NodeCount; - ULONG VidPnSourceCount; - - ULONG VSyncEnabled; - ULONG TdrDetectedCount; - - LONGLONG ZeroLengthDmaBuffers; - ULONGLONG RestartedPeriod; - - D3DKMT_QUERYSTATSTICS_REFERENCE_DMA_BUFFER ReferenceDmaBuffer; - D3DKMT_QUERYSTATSTICS_RENAMING Renaming; - D3DKMT_QUERYSTATSTICS_PREPRATION Preparation; - D3DKMT_QUERYSTATSTICS_PAGING_FAULT PagingFault; - D3DKMT_QUERYSTATSTICS_PAGING_TRANSFER PagingTransfer; - D3DKMT_QUERYSTATSTICS_SWIZZLING_RANGE SwizzlingRange; - D3DKMT_QUERYSTATSTICS_LOCKS Locks; - D3DKMT_QUERYSTATSTICS_ALLOCATIONS Allocations; - D3DKMT_QUERYSTATSTICS_TERMINATIONS Terminations; - - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_ADAPTER_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_SYSTEM_MEMORY -{ - ULONGLONG BytesAllocated; - ULONGLONG BytesReserved; - ULONG SmallAllocationBlocks; - ULONG LargeAllocationBlocks; - ULONGLONG WriteCombinedBytesAllocated; - ULONGLONG WriteCombinedBytesReserved; - ULONGLONG CachedBytesAllocated; - ULONGLONG CachedBytesReserved; - ULONGLONG SectionBytesAllocated; - ULONGLONG SectionBytesReserved; -} D3DKMT_QUERYSTATISTICS_SYSTEM_MEMORY; - -typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_INFORMATION -{ - ULONG NodeCount; - ULONG VidPnSourceCount; - - D3DKMT_QUERYSTATISTICS_SYSTEM_MEMORY SystemMemory; - - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_PROCESS_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_DMA_BUFFER -{ - D3DKMT_QUERYSTATISTICS_COUNTER Size; - ULONG AllocationListBytes; - ULONG PatchLocationListBytes; -} D3DKMT_QUERYSTATISTICS_DMA_BUFFER; - -typedef struct _D3DKMT_QUERYSTATISTICS_COMMITMENT_DATA -{ - ULONG64 TotalBytesEvictedFromProcess; - ULONG64 BytesBySegmentPreference[D3DKMT_QUERYSTATISTICS_SEGMENT_PREFERENCE_MAX]; -} D3DKMT_QUERYSTATISTICS_COMMITMENT_DATA; - -typedef struct _D3DKMT_QUERYSTATISTICS_POLICY -{ - ULONGLONG PreferApertureForRead[D3DKMT_MaxAllocationPriorityClass]; - ULONGLONG PreferAperture[D3DKMT_MaxAllocationPriorityClass]; - ULONGLONG MemResetOnPaging; - ULONGLONG RemovePagesFromWorkingSetOnPaging; - ULONGLONG MigrationEnabled; -} D3DKMT_QUERYSTATISTICS_POLICY; - -typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER_INFORMATION -{ - ULONG NbSegments; - ULONG NodeCount; - ULONG VidPnSourceCount; - - ULONG VirtualMemoryUsage; - - D3DKMT_QUERYSTATISTICS_DMA_BUFFER DmaBuffer; - D3DKMT_QUERYSTATISTICS_COMMITMENT_DATA CommitmentData; - D3DKMT_QUERYSTATISTICS_POLICY _Policy; - - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_MEMORY -{ - ULONGLONG TotalBytesEvicted; - ULONG AllocsCommitted; - ULONG AllocsResident; -} D3DKMT_QUERYSTATISTICS_MEMORY; - -typedef struct _D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 -{ - ULONG CommitLimit; - ULONG BytesCommitted; - ULONG BytesResident; - - D3DKMT_QUERYSTATISTICS_MEMORY Memory; - - ULONG Aperture; // boolean - - ULONGLONG TotalBytesEvictedByPriority[D3DKMT_MaxAllocationPriorityClass]; - - ULONG64 SystemMemoryEndAddress; - struct - { - ULONG64 PreservedDuringStandby : 1; - ULONG64 PreservedDuringHibernate : 1; - ULONG64 PartiallyPreservedDuringHibernate : 1; - ULONG64 Reserved : 61; - } PowerFlags; - - ULONG64 Reserved[7]; -} D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1; - -typedef struct _D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION -{ - ULONGLONG CommitLimit; - ULONGLONG BytesCommitted; - ULONGLONG BytesResident; - - D3DKMT_QUERYSTATISTICS_MEMORY Memory; - - ULONG Aperture; // boolean - - ULONGLONG TotalBytesEvictedByPriority[D3DKMT_MaxAllocationPriorityClass]; - - ULONG64 SystemMemoryEndAddress; - struct - { - ULONG64 PreservedDuringStandby : 1; - ULONG64 PreservedDuringHibernate : 1; - ULONG64 PartiallyPreservedDuringHibernate : 1; - ULONG64 Reserved : 61; - } PowerFlags; - - ULONG64 Reserved[6]; -} D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION; - -typedef struct _D3DKMT_QUERYSTATISTICS_VIDEO_MEMORY -{ - ULONG AllocsCommitted; - D3DKMT_QUERYSTATISTICS_COUNTER AllocsResidentInP[D3DKMT_QUERYSTATISTICS_SEGMENT_PREFERENCE_MAX]; - D3DKMT_QUERYSTATISTICS_COUNTER AllocsResidentInNonPreferred; - ULONGLONG TotalBytesEvictedDueToPreparation; -} D3DKMT_QUERYSTATISTICS_VIDEO_MEMORY; - -typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_POLICY -{ - ULONGLONG UseMRU; -} D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_POLICY; - -typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_INFORMATION -{ - ULONGLONG BytesCommitted; - ULONGLONG MaximumWorkingSet; - ULONGLONG MinimumWorkingSet; - - ULONG NbReferencedAllocationEvictedInPeriod; - - D3DKMT_QUERYSTATISTICS_VIDEO_MEMORY VideoMemory; - D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_POLICY _Policy; - - ULONG64 Reserved[8]; -} D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_INFORMATION; - -typedef enum _D3DKMT_QUERYSTATISTICS_TYPE -{ - D3DKMT_QUERYSTATISTICS_ADAPTER = 0, - D3DKMT_QUERYSTATISTICS_PROCESS = 1, - D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER = 2, - D3DKMT_QUERYSTATISTICS_SEGMENT = 3, - D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT = 4, - D3DKMT_QUERYSTATISTICS_NODE = 5, - D3DKMT_QUERYSTATISTICS_PROCESS_NODE = 6, - D3DKMT_QUERYSTATISTICS_VIDPNSOURCE = 7, - D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE = 8 -} D3DKMT_QUERYSTATISTICS_TYPE; - -typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT -{ - ULONG SegmentId; -} D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT; - -typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_NODE -{ - ULONG NodeId; -} D3DKMT_QUERYSTATISTICS_QUERY_NODE; - -typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE -{ - ULONG VidPnSourceId; -} D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE; - -typedef union _D3DKMT_QUERYSTATISTICS_RESULT -{ - D3DKMT_QUERYSTATISTICS_ADAPTER_INFORMATION AdapterInformation; - D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 SegmentInformationV1; // WIN7 - D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION SegmentInformation; // WIN8 - D3DKMT_QUERYSTATISTICS_NODE_INFORMATION NodeInformation; - D3DKMT_QUERYSTATISTICS_VIDPNSOURCE_INFORMATION VidPnSourceInformation; - D3DKMT_QUERYSTATISTICS_PROCESS_INFORMATION ProcessInformation; - D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER_INFORMATION ProcessAdapterInformation; - D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_INFORMATION ProcessSegmentInformation; - D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION ProcessNodeInformation; - D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION ProcessVidPnSourceInformation; -} D3DKMT_QUERYSTATISTICS_RESULT; - -typedef struct _D3DKMT_QUERYSTATISTICS -{ - _In_ D3DKMT_QUERYSTATISTICS_TYPE Type; - _In_ LUID AdapterLuid; - _In_opt_ HANDLE hProcess; - _Out_ D3DKMT_QUERYSTATISTICS_RESULT QueryResult; - - union - { - _In_ D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT QuerySegment; - _In_ D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT QueryProcessSegment; - _In_ D3DKMT_QUERYSTATISTICS_QUERY_NODE QueryNode; - _In_ D3DKMT_QUERYSTATISTICS_QUERY_NODE QueryProcessNode; - _In_ D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE QueryVidPnSource; - _In_ D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE QueryProcessVidPnSource; - }; -} D3DKMT_QUERYSTATISTICS; - -// Function pointers - -// https://msdn.microsoft.com/en-us/library/ff547033.aspx -_Check_return_ -NTSTATUS D3DKMTOpenAdapterFromDeviceName( - _Inout_ D3DKMT_OPENADAPTERFROMDEVICENAME *pData - ); - -// https://msdn.microsoft.com/en-us/library/ff546787.aspx -_Check_return_ -NTSTATUS D3DKMTCloseAdapter( - _In_ const D3DKMT_CLOSEADAPTER *pData - ); - -// rev -_Check_return_ -NTSTATUS D3DKMTQueryStatistics( - _Inout_ const D3DKMT_QUERYSTATISTICS *pData - ); - -#endif +#ifndef _D3DKMT_H +#define _D3DKMT_H + +// D3D kernel-mode definitions + +typedef UINT32 D3DKMT_HANDLE; + +typedef enum _KMTQUERYADAPTERINFOTYPE +{ + KMTQAITYPE_UMDRIVERPRIVATE = 0, + KMTQAITYPE_UMDRIVERNAME = 1, // D3DKMT_UMDFILENAMEINFO + KMTQAITYPE_UMOPENGLINFO = 2, // D3DKMT_OPENGLINFO + KMTQAITYPE_GETSEGMENTSIZE = 3, // D3DKMT_SEGMENTSIZEINFO + KMTQAITYPE_ADAPTERGUID = 4, // GUID + KMTQAITYPE_FLIPQUEUEINFO = 5, // D3DKMT_FLIPQUEUEINFO + KMTQAITYPE_ADAPTERADDRESS = 6, // D3DKMT_ADAPTERADDRESS + KMTQAITYPE_SETWORKINGSETINFO = 7, // D3DKMT_WORKINGSETINFO + KMTQAITYPE_ADAPTERREGISTRYINFO = 8, // D3DKMT_ADAPTERREGISTRYINFO + KMTQAITYPE_CURRENTDISPLAYMODE = 9, // D3DKMT_CURRENTDISPLAYMODE + KMTQAITYPE_MODELIST = 10, // D3DKMT_DISPLAYMODE (array) + KMTQAITYPE_CHECKDRIVERUPDATESTATUS = 11, + KMTQAITYPE_VIRTUALADDRESSINFO = 12, // D3DKMT_VIRTUALADDRESSINFO + KMTQAITYPE_DRIVERVERSION = 13, // D3DKMT_DRIVERVERSION + KMTQAITYPE_ADAPTERTYPE = 15, // D3DKMT_ADAPTERTYPE // since WIN8 + KMTQAITYPE_OUTPUTDUPLCONTEXTSCOUNT = 16, // D3DKMT_OUTPUTDUPLCONTEXTSCOUNT + KMTQAITYPE_WDDM_1_2_CAPS = 17, // D3DKMT_WDDM_1_2_CAPS + KMTQAITYPE_UMD_DRIVER_VERSION = 18, // D3DKMT_UMD_DRIVER_VERSION + KMTQAITYPE_DIRECTFLIP_SUPPORT = 19, // D3DKMT_DIRECTFLIP_SUPPORT + KMTQAITYPE_MULTIPLANEOVERLAY_SUPPORT = 20, // D3DKMT_MULTIPLANEOVERLAY_SUPPORT // since WDDM1_3 + KMTQAITYPE_DLIST_DRIVER_NAME = 21, // D3DKMT_DLIST_DRIVER_NAME + KMTQAITYPE_WDDM_1_3_CAPS = 22, // D3DKMT_WDDM_1_3_CAPS + KMTQAITYPE_MULTIPLANEOVERLAY_HUD_SUPPORT = 23, // D3DKMT_MULTIPLANEOVERLAY_HUD_SUPPORT + KMTQAITYPE_WDDM_2_0_CAPS = 24, // D3DKMT_WDDM_2_0_CAPS // since WDDM2_0 + KMTQAITYPE_NODEMETADATA = 25, // D3DKMT_NODEMETADATA + KMTQAITYPE_CPDRIVERNAME = 26, // D3DKMT_CPDRIVERNAME + KMTQAITYPE_XBOX = 27, // D3DKMT_XBOX + KMTQAITYPE_INDEPENDENTFLIP_SUPPORT = 28, // D3DKMT_INDEPENDENTFLIP_SUPPORT + KMTQAITYPE_MIRACASTCOMPANIONDRIVERNAME = 29, // D3DKMT_MIRACASTCOMPANIONDRIVERNAME + KMTQAITYPE_PHYSICALADAPTERCOUNT = 30, // D3DKMT_PHYSICAL_ADAPTER_COUNT + KMTQAITYPE_PHYSICALADAPTERDEVICEIDS = 31, // D3DKMT_QUERY_DEVICE_IDS + KMTQAITYPE_DRIVERCAPS_EXT = 32, // D3DKMT_DRIVERCAPS_EXT + KMTQAITYPE_QUERY_MIRACAST_DRIVER_TYPE = 33, // D3DKMT_QUERY_MIRACAST_DRIVER_TYPE + KMTQAITYPE_QUERY_GPUMMU_CAPS = 34, // D3DKMT_QUERY_GPUMMU_CAPS + KMTQAITYPE_QUERY_MULTIPLANEOVERLAY_DECODE_SUPPORT = 35, // D3DKMT_MULTIPLANEOVERLAY_DECODE_SUPPORT + KMTQAITYPE_QUERY_HW_PROTECTION_TEARDOWN_COUNT = 36, // UINT32 + KMTQAITYPE_QUERY_ISBADDRIVERFORHWPROTECTIONDISABLED = 37, // D3DKMT_ISBADDRIVERFORHWPROTECTIONDISABLED + KMTQAITYPE_MULTIPLANEOVERLAY_SECONDARY_SUPPORT = 38, // D3DKMT_MULTIPLANEOVERLAY_SECONDARY_SUPPORT + KMTQAITYPE_INDEPENDENTFLIP_SECONDARY_SUPPORT = 39, // D3DKMT_INDEPENDENTFLIP_SECONDARY_SUPPORT + KMTQAITYPE_PANELFITTER_SUPPORT = 40, // D3DKMT_PANELFITTER_SUPPORT // since WDDM2_1 + KMTQAITYPE_PHYSICALADAPTERPNPKEY = 41, // D3DKMT_QUERY_PHYSICAL_ADAPTER_PNP_KEY // since WDDM2_2 + KMTQAITYPE_GETSEGMENTGROUPSIZE = 42, // D3DKMT_SEGMENTGROUPSIZEINFO + KMTQAITYPE_MPO3DDI_SUPPORT = 43, // D3DKMT_MPO3DDI_SUPPORT + KMTQAITYPE_HWDRM_SUPPORT = 44, // D3DKMT_HWDRM_SUPPORT + KMTQAITYPE_MPOKERNELCAPS_SUPPORT = 45, // D3DKMT_MPOKERNELCAPS_SUPPORT + KMTQAITYPE_MULTIPLANEOVERLAY_STRETCH_SUPPORT = 46, // D3DKMT_MULTIPLANEOVERLAY_STRETCH_SUPPORT + KMTQAITYPE_GET_DEVICE_VIDPN_OWNERSHIP_INFO = 47, // D3DKMT_GET_DEVICE_VIDPN_OWNERSHIP_INFO + KMTQAITYPE_QUERYREGISTRY = 48, // D3DDDI_QUERYREGISTRY_INFO // since WDDM2_4 + KMTQAITYPE_KMD_DRIVER_VERSION = 49, // D3DKMT_KMD_DRIVER_VERSION + KMTQAITYPE_BLOCKLIST_KERNEL = 50, // D3DKMT_BLOCKLIST_INFO ?? + KMTQAITYPE_BLOCKLIST_RUNTIME = 51, // D3DKMT_BLOCKLIST_INFO ?? + KMTQAITYPE_ADAPTERGUID_RENDER = 52, // GUID + KMTQAITYPE_ADAPTERADDRESS_RENDER = 53, // D3DKMT_ADAPTERADDRESS + KMTQAITYPE_ADAPTERREGISTRYINFO_RENDER = 54, // D3DKMT_ADAPTERREGISTRYINFO + KMTQAITYPE_CHECKDRIVERUPDATESTATUS_RENDER = 55, + KMTQAITYPE_DRIVERVERSION_RENDER = 56, // D3DKMT_DRIVERVERSION + KMTQAITYPE_ADAPTERTYPE_RENDER = 57, // D3DKMT_ADAPTERTYPE + KMTQAITYPE_WDDM_1_2_CAPS_RENDER = 58, // D3DKMT_WDDM_1_2_CAPS + KMTQAITYPE_WDDM_1_3_CAPS_RENDER = 59, // D3DKMT_WDDM_1_3_CAPS + KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID = 60, // D3DKMT_QUERY_ADAPTER_UNIQUE_GUID + KMTQAITYPE_NODEPERFDATA = 61, // D3DKMT_NODE_PERFDATA + KMTQAITYPE_ADAPTERPERFDATA = 62, // D3DKMT_ADAPTER_PERFDATA + KMTQAITYPE_ADAPTERPERFDATA_CAPS = 63, // D3DKMT_ADAPTER_PERFDATACAPS + KMTQUITYPE_GPUVERSION = 64, // D3DKMT_GPUVERSION + KMTQAITYPE_DRIVER_DESCRIPTION = 65, // D3DKMT_DRIVER_DESCRIPTION // since WDDM2_6 + KMTQAITYPE_DRIVER_DESCRIPTION_RENDER = 66, // D3DKMT_DRIVER_DESCRIPTION + KMTQAITYPE_SCANOUT_CAPS = 67, // D3DKMT_QUERY_SCANOUT_CAPS +} KMTQUERYADAPTERINFOTYPE; + +typedef enum _KMTUMDVERSION +{ + KMTUMDVERSION_DX9, + KMTUMDVERSION_DX10, + KMTUMDVERSION_DX11, + KMTUMDVERSION_DX12, + NUM_KMTUMDVERSIONS +} KMTUMDVERSION; + +// The D3DKMT_UMDFILENAMEINFO structure contains the name of an OpenGL ICD that is based on the specified version of the DirectX runtime. +typedef struct _D3DKMT_UMDFILENAMEINFO +{ + _In_ KMTUMDVERSION Version; // A KMTUMDVERSION-typed value that indicates the version of the DirectX runtime to retrieve the name of an OpenGL ICD for. + _Out_ WCHAR UmdFileName[MAX_PATH]; // A string that contains the name of the OpenGL ICD. +} D3DKMT_UMDFILENAMEINFO; + +// The D3DKMT_OPENGLINFO structure describes OpenGL installable client driver (ICD) information. +typedef struct _D3DKMT_OPENGLINFO +{ + _Out_ WCHAR UmdOpenGlIcdFileName[MAX_PATH]; // An array of wide characters that contains the file name of the OpenGL ICD. + _Out_ ULONG Version; // The version of the OpenGL ICD. + _In_ ULONG Flags; // This member is currently unused. +} D3DKMT_OPENGLINFO; + +// The D3DKMT_SEGMENTSIZEINFO structure describes the size, in bytes, of memory and aperture segments. +typedef struct _D3DKMT_SEGMENTSIZEINFO +{ + _Out_ ULONGLONG DedicatedVideoMemorySize; // The size, in bytes, of memory that is dedicated from video memory. + _Out_ ULONGLONG DedicatedSystemMemorySize; // The size, in bytes, of memory that is dedicated from system memory. + _Out_ ULONGLONG SharedSystemMemorySize; // The size, in bytes, of memory from system memory that can be shared by many users. +} D3DKMT_SEGMENTSIZEINFO; + +// The D3DKMT_FLIPINFOFLAGS structure identifies flipping capabilities of the display miniport driver. +typedef struct _D3DKMT_FLIPINFOFLAGS +{ + UINT32 FlipInterval : 1; // A UINT value that specifies whether the display miniport driver natively supports the scheduling of a flip command to take effect after two, three or four vertical syncs occur. + UINT32 Reserved : 31; +} D3DKMT_FLIPINFOFLAGS; + +// The D3DKMT_FLIPQUEUEINFO structure describes information about the graphics adapter's queue of flip operations. +typedef struct _D3DKMT_FLIPQUEUEINFO +{ + _Out_ UINT32 MaxHardwareFlipQueueLength; // The maximum number of flip operations that can be queued for hardware-flip queuing. + _Out_ UINT32 MaxSoftwareFlipQueueLength; // The maximum number of flip operations that can be queued for software-flip queuing on hardware that supports memory mapped I/O (MMIO)-based flips. + _Out_ D3DKMT_FLIPINFOFLAGS FlipFlags; // indicates, in bit-field flags, flipping capabilities. +} D3DKMT_FLIPQUEUEINFO; + +// The D3DKMT_ADAPTERADDRESS structure describes the physical location of the graphics adapter. +typedef struct _D3DKMT_ADAPTERADDRESS +{ + _Out_ UINT32 BusNumber; // The number of the bus that the graphics adapter's physical device is located on. + _Out_ UINT32 DeviceNumber; // The index of the graphics adapter's physical device on the bus. + _Out_ UINT32 FunctionNumber; // The function number of the graphics adapter on the physical device. +} D3DKMT_ADAPTERADDRESS; + +typedef struct _D3DKMT_WORKINGSETFLAGS +{ + UINT32 UseDefault : 1; // A UINT value that specifies whether the display miniport driver uses the default working set. + UINT32 Reserved : 31; // This member is reserved and should be set to zero. +} D3DKMT_WORKINGSETFLAGS; + +// The D3DKMT_WORKINGSETINFO structure describes information about the graphics adapter's working set. +typedef struct _D3DKMT_WORKINGSETINFO +{ + _Out_ D3DKMT_WORKINGSETFLAGS Flags; // A D3DKMT_WORKINGSETFLAGS structure that indicates, in bit-field flags, working-set properties. + _Out_ ULONG MinimumWorkingSetPercentile; // The minimum working-set percentile. + _Out_ ULONG MaximumWorkingSetPercentile; // The maximum working-set percentile. +} D3DKMT_WORKINGSETINFO; + +// The D3DKMT_ADAPTERREGISTRYINFO structure contains registry information about the graphics adapter. +typedef struct _D3DKMT_ADAPTERREGISTRYINFO +{ + _Out_ WCHAR AdapterString[MAX_PATH]; // A string that contains the name of the graphics adapter. + _Out_ WCHAR BiosString[MAX_PATH]; // A string that contains the name of the BIOS for the graphics adapter. + _Out_ WCHAR DacType[MAX_PATH]; // A string that contains the DAC type for the graphics adapter. + _Out_ WCHAR ChipType[MAX_PATH]; // A string that contains the chip type for the graphics adapter. +} D3DKMT_ADAPTERREGISTRYINFO; + +/* Formats +* Most of these names have the following convention: +* A = Alpha +* R = Red +* G = Green +* B = Blue +* X = Unused Bits +* P = Palette +* L = Luminance +* U = dU coordinate for BumpMap +* V = dV coordinate for BumpMap +* S = Stencil +* D = Depth (e.g. Z or W buffer) +* C = Computed from other channels (typically on certain read operations) +* +* Further, the order of the pieces are from MSB first; hence +* D3DFMT_A8L8 indicates that the high byte of this two byte +* format is alpha. +* +* D16 indicates: +* - An integer 16-bit value. +* - An app-lockable surface. +* +* All Depth/Stencil formats except D3DFMT_D16_LOCKABLE indicate: +* - no particular bit ordering per pixel, and +* - are not app lockable, and +* - the driver is allowed to consume more than the indicated +* number of bits per Depth channel (but not Stencil channel). +*/ +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((ULONG)(BYTE)(ch0) | ((ULONG)(BYTE)(ch1) << 8) | \ + ((ULONG)(BYTE)(ch2) << 16) | ((ULONG)(BYTE)(ch3) << 24 )) +#endif + +typedef enum _D3DDDIFORMAT +{ + D3DDDIFMT_UNKNOWN = 0, + D3DDDIFMT_R8G8B8 = 20, + D3DDDIFMT_A8R8G8B8 = 21, + D3DDDIFMT_X8R8G8B8 = 22, + D3DDDIFMT_R5G6B5 = 23, + D3DDDIFMT_X1R5G5B5 = 24, + D3DDDIFMT_A1R5G5B5 = 25, + D3DDDIFMT_A4R4G4B4 = 26, + D3DDDIFMT_R3G3B2 = 27, + D3DDDIFMT_A8 = 28, + D3DDDIFMT_A8R3G3B2 = 29, + D3DDDIFMT_X4R4G4B4 = 30, + D3DDDIFMT_A2B10G10R10 = 31, + D3DDDIFMT_A8B8G8R8 = 32, + D3DDDIFMT_X8B8G8R8 = 33, + D3DDDIFMT_G16R16 = 34, + D3DDDIFMT_A2R10G10B10 = 35, + D3DDDIFMT_A16B16G16R16 = 36, + D3DDDIFMT_A8P8 = 40, + D3DDDIFMT_P8 = 41, + D3DDDIFMT_L8 = 50, + D3DDDIFMT_A8L8 = 51, + D3DDDIFMT_A4L4 = 52, + D3DDDIFMT_V8U8 = 60, + D3DDDIFMT_L6V5U5 = 61, + D3DDDIFMT_X8L8V8U8 = 62, + D3DDDIFMT_Q8W8V8U8 = 63, + D3DDDIFMT_V16U16 = 64, + D3DDDIFMT_W11V11U10 = 65, + D3DDDIFMT_A2W10V10U10 = 67, + + D3DDDIFMT_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'), + D3DDDIFMT_R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'), + D3DDDIFMT_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'), + D3DDDIFMT_G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'), + D3DDDIFMT_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'), + D3DDDIFMT_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'), + D3DDDIFMT_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'), + D3DDDIFMT_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'), + D3DDDIFMT_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'), + + D3DDDIFMT_D16_LOCKABLE = 70, + D3DDDIFMT_D32 = 71, + D3DDDIFMT_D15S1 = 73, + D3DDDIFMT_D24S8 = 75, + D3DDDIFMT_D24X8 = 77, + D3DDDIFMT_D24X4S4 = 79, + D3DDDIFMT_D16 = 80, + D3DDDIFMT_D32F_LOCKABLE = 82, + D3DDDIFMT_D24FS8 = 83, + D3DDDIFMT_D32_LOCKABLE = 84, + D3DDDIFMT_S8_LOCKABLE = 85, + D3DDDIFMT_S1D15 = 72, + D3DDDIFMT_S8D24 = 74, + D3DDDIFMT_X8D24 = 76, + D3DDDIFMT_X4S4D24 = 78, + D3DDDIFMT_L16 = 81, + D3DDDIFMT_G8R8 = 91, // WDDM1_3 + D3DDDIFMT_R8 = 92, // WDDM1_3 + D3DDDIFMT_VERTEXDATA = 100, + D3DDDIFMT_INDEX16 = 101, + D3DDDIFMT_INDEX32 = 102, + D3DDDIFMT_Q16W16V16U16 = 110, + + D3DDDIFMT_MULTI2_ARGB8 = MAKEFOURCC('M', 'E', 'T', '1'), + + // Floating point surface formats + + // s10e5 formats (16-bits per channel) + D3DDDIFMT_R16F = 111, + D3DDDIFMT_G16R16F = 112, + D3DDDIFMT_A16B16G16R16F = 113, + + // IEEE s23e8 formats (32-bits per channel) + D3DDDIFMT_R32F = 114, + D3DDDIFMT_G32R32F = 115, + D3DDDIFMT_A32B32G32R32F = 116, + + D3DDDIFMT_CxV8U8 = 117, + + // Monochrome 1 bit per pixel format + D3DDDIFMT_A1 = 118, + + // 2.8 biased fixed point + D3DDDIFMT_A2B10G10R10_XR_BIAS = 119, + + // Decode compressed buffer formats + D3DDDIFMT_DXVACOMPBUFFER_BASE = 150, + D3DDDIFMT_PICTUREPARAMSDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 0, // 150 + D3DDDIFMT_MACROBLOCKDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 1, // 151 + D3DDDIFMT_RESIDUALDIFFERENCEDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 2, // 152 + D3DDDIFMT_DEBLOCKINGDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 3, // 153 + D3DDDIFMT_INVERSEQUANTIZATIONDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 4, // 154 + D3DDDIFMT_SLICECONTROLDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 5, // 155 + D3DDDIFMT_BITSTREAMDATA = D3DDDIFMT_DXVACOMPBUFFER_BASE + 6, // 156 + D3DDDIFMT_MOTIONVECTORBUFFER = D3DDDIFMT_DXVACOMPBUFFER_BASE + 7, // 157 + D3DDDIFMT_FILMGRAINBUFFER = D3DDDIFMT_DXVACOMPBUFFER_BASE + 8, // 158 + D3DDDIFMT_DXVA_RESERVED9 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 9, // 159 + D3DDDIFMT_DXVA_RESERVED10 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 10, // 160 + D3DDDIFMT_DXVA_RESERVED11 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 11, // 161 + D3DDDIFMT_DXVA_RESERVED12 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 12, // 162 + D3DDDIFMT_DXVA_RESERVED13 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 13, // 163 + D3DDDIFMT_DXVA_RESERVED14 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 14, // 164 + D3DDDIFMT_DXVA_RESERVED15 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 15, // 165 + D3DDDIFMT_DXVA_RESERVED16 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 16, // 166 + D3DDDIFMT_DXVA_RESERVED17 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 17, // 167 + D3DDDIFMT_DXVA_RESERVED18 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 18, // 168 + D3DDDIFMT_DXVA_RESERVED19 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 19, // 169 + D3DDDIFMT_DXVA_RESERVED20 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 20, // 170 + D3DDDIFMT_DXVA_RESERVED21 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 21, // 171 + D3DDDIFMT_DXVA_RESERVED22 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 22, // 172 + D3DDDIFMT_DXVA_RESERVED23 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 23, // 173 + D3DDDIFMT_DXVA_RESERVED24 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 24, // 174 + D3DDDIFMT_DXVA_RESERVED25 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 25, // 175 + D3DDDIFMT_DXVA_RESERVED26 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 26, // 176 + D3DDDIFMT_DXVA_RESERVED27 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 27, // 177 + D3DDDIFMT_DXVA_RESERVED28 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 28, // 178 + D3DDDIFMT_DXVA_RESERVED29 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 29, // 179 + D3DDDIFMT_DXVA_RESERVED30 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 30, // 180 + D3DDDIFMT_DXVA_RESERVED31 = D3DDDIFMT_DXVACOMPBUFFER_BASE + 31, // 181 + D3DDDIFMT_DXVACOMPBUFFER_MAX = D3DDDIFMT_DXVA_RESERVED31, + + D3DDDIFMT_BINARYBUFFER = 199, + + D3DDDIFMT_FORCE_UINT = 0x7fffffff +} D3DDDIFORMAT; + +typedef struct _D3DDDI_RATIONAL +{ + UINT32 Numerator; + UINT32 Denominator; +} D3DDDI_RATIONAL; + +typedef enum _D3DDDI_VIDEO_SIGNAL_SCANLINE_ORDERING +{ + D3DDDI_VSSLO_UNINITIALIZED = 0, + D3DDDI_VSSLO_PROGRESSIVE = 1, + D3DDDI_VSSLO_INTERLACED_UPPERFIELDFIRST = 2, + D3DDDI_VSSLO_INTERLACED_LOWERFIELDFIRST = 3, + D3DDDI_VSSLO_OTHER = 255 +} D3DDDI_VIDEO_SIGNAL_SCANLINE_ORDERING; + +typedef enum _D3DDDI_ROTATION +{ + D3DDDI_ROTATION_IDENTITY = 1, // No rotation. + D3DDDI_ROTATION_90 = 2, // Rotated 90 degrees. + D3DDDI_ROTATION_180 = 3, // Rotated 180 degrees. + D3DDDI_ROTATION_270 = 4 // Rotated 270 degrees. +} D3DDDI_ROTATION; + +typedef enum _D3DKMDT_MODE_PRUNING_REASON +{ + D3DKMDT_MPR_UNINITIALIZED = 0, // mode was pruned or is supported because of: + D3DKMDT_MPR_ALLCAPS = 1, // all of the monitor caps (only used to imply lack of support - for support, specific reason is always indicated) + D3DKMDT_MPR_DESCRIPTOR_MONITOR_SOURCE_MODE = 2, // monitor source mode in the monitor descriptor + D3DKMDT_MPR_DESCRIPTOR_MONITOR_FREQUENCY_RANGE = 3, // monitor frequency range in the monitor descriptor + D3DKMDT_MPR_DESCRIPTOR_OVERRIDE_MONITOR_SOURCE_MODE = 4, // monitor source mode in the monitor descriptor override + D3DKMDT_MPR_DESCRIPTOR_OVERRIDE_MONITOR_FREQUENCY_RANGE = 5, // monitor frequency range in the monitor descriptor override + D3DKMDT_MPR_DEFAULT_PROFILE_MONITOR_SOURCE_MODE = 6, // monitor source mode in the default monitor profile + D3DKMDT_MPR_DRIVER_RECOMMENDED_MONITOR_SOURCE_MODE = 7, // monitor source mode recommended by the driver + D3DKMDT_MPR_MONITOR_FREQUENCY_RANGE_OVERRIDE = 8, // monitor frequency range override + D3DKMDT_MPR_CLONE_PATH_PRUNED = 9, // Mode is pruned because other path(s) in clone cluster has(have) no mode supported by monitor + D3DKMDT_MPR_MAXVALID = 10 +} D3DKMDT_MODE_PRUNING_REASON; + +// This structure takes 8 bytes. +// The unnamed UINT of size 0 forces alignment of the structure to +// make it exactly occupy 8 bytes, see MSDN docs on C++ bitfields +// for more details +typedef struct _D3DKMDT_DISPLAYMODE_FLAGS +{ + UINT32 ValidatedAgainstMonitorCaps : 1; + UINT32 RoundedFakeMode : 1; + UINT32 : 0; + D3DKMDT_MODE_PRUNING_REASON ModePruningReason : 4; + UINT32 Stereo : 1; // since WIN8 + UINT32 AdvancedScanCapable : 1; + UINT32 PreferredTiming : 1; // since WDDM2_0 + UINT32 PhysicalModeSupported : 1; + UINT32 Reserved : 24; +} D3DKMDT_DISPLAYMODE_FLAGS; + +// The D3DKMT_DISPLAYMODE structure describes a display mode. +typedef struct _D3DKMT_DISPLAYMODE +{ + _Out_ UINT32 Width; // The screen width of the display mode, in pixels. + _Out_ UINT32 Height; // The screen height of the display mode, in pixels. + _Out_ D3DDDIFORMAT Format; // A D3DDDIFORMAT-typed value that indicates the pixel format of the display mode. + _Out_ UINT32 IntegerRefreshRate; // A UINT value that indicates the refresh rate of the display mode. + _Out_ D3DDDI_RATIONAL RefreshRate; // A D3DDDI_RATIONAL structure that indicates the refresh rate of the display mode. + _Out_ D3DDDI_VIDEO_SIGNAL_SCANLINE_ORDERING ScanLineOrdering; // A D3DDDI_VIDEO_SIGNAL_SCANLINE_ORDERING-typed value that indicates how scan lines are ordered in the display mode. + _Out_ D3DDDI_ROTATION DisplayOrientation; // A D3DDDI_ROTATION-typed value that identifies the orientation of the display mode. + _Out_ UINT32 DisplayFixedOutput; // The fixed output of the display mode. + _Out_ D3DKMDT_DISPLAYMODE_FLAGS Flags; // A D3DKMDT_DISPLAYMODE_FLAGS structure that indicates information about the display mode. +} D3DKMT_DISPLAYMODE; + +// The D3DKMT_CURRENTDISPLAYMODE structure describes the current display mode of the specified video source. +typedef struct _D3DKMT_CURRENTDISPLAYMODE +{ + _In_ UINT32 VidPnSourceId; // The zero-based identification number of the video present source in a path of a video present network (VidPN) topology that the display mode applies to. + _Out_ D3DKMT_DISPLAYMODE DisplayMode; // A D3DKMT_DISPLAYMODE structure that represents the current display mode. +} D3DKMT_CURRENTDISPLAYMODE; + +// private +typedef struct _D3DKMT_VIRTUALADDRESSFLAGS +{ + UINT32 VirtualAddressSupported : 1; + UINT32 Reserved : 31; +} D3DKMT_VIRTUALADDRESSFLAGS; + +// private +typedef struct _D3DKMT_VIRTUALADDRESSINFO +{ + D3DKMT_VIRTUALADDRESSFLAGS VirtualAddressFlags; +} D3DKMT_VIRTUALADDRESSINFO; + +// The D3DKMT_DRIVERVERSION enumeration type contains values that indicate the version of the display driver model that the display miniport driver supports. +typedef enum D3DKMT_DRIVERVERSION +{ + KMT_DRIVERVERSION_WDDM_1_0 = 1000, // The display miniport driver supports the Windows Vista display driver model (WDDM) without Windows 7 features. + KMT_DRIVERVERSION_WDDM_1_1_PRERELEASE = 1102, // The display miniport driver supports the Windows Vista display driver model with prereleased Windows 7 features. + KMT_DRIVERVERSION_WDDM_1_1 = 1105, // The display miniport driver supports the Windows Vista display driver model with released Windows 7 features. + KMT_DRIVERVERSION_WDDM_1_2 = 1200, // The display miniport driver supports the Windows Vista display driver model with released Windows 8 features. Supported starting with Windows 8. + KMT_DRIVERVERSION_WDDM_1_3 = 1300, // The display miniport driver supports the Windows display driver model with released Windows 8.1 features. Supported starting with Windows 8.1. + KMT_DRIVERVERSION_WDDM_2_0 = 2000, // The display miniport driver supports the Windows display driver model with released Windows 10 features. Supported starting with Windows 10. + KMT_DRIVERVERSION_WDDM_2_1 = 2100, // 1607 + KMT_DRIVERVERSION_WDDM_2_2 = 2200, // 1703 + KMT_DRIVERVERSION_WDDM_2_3 = 2300, // 1709 + KMT_DRIVERVERSION_WDDM_2_4 = 2400, // 1803 + KMT_DRIVERVERSION_WDDM_2_5 = 2500, // 1809 + KMT_DRIVERVERSION_WDDM_2_6 = 2600, // 19H1 +} D3DKMT_DRIVERVERSION; + +// Specifies the type of display device that the graphics adapter supports. +typedef struct _D3DKMT_ADAPTERTYPE +{ + union + { + struct + { + UINT32 RenderSupported : 1; + UINT32 DisplaySupported : 1; + UINT32 SoftwareDevice : 1; + UINT32 PostDevice : 1; + UINT32 HybridDiscrete : 1; // since WDDM1_3 + UINT32 HybridIntegrated : 1; + UINT32 IndirectDisplayDevice : 1; + UINT32 Paravirtualized : 1; // since WDDM2_3 + UINT32 ACGSupported : 1; + UINT32 SupportSetTimingsFromVidPn : 1; + UINT32 Detachable : 1; + UINT32 Reserved : 21; + }; + UINT32 Value; + }; +} D3DKMT_ADAPTERTYPE; + +// Specifies the number of current Desktop Duplication API (DDA) clients that are attached to a given video present network (VidPN). +typedef struct _D3DKMT_OUTPUTDUPLCONTEXTSCOUNT +{ + UINT32 VidPnSourceId; // The ID of the video present network (VidPN). + UINT32 OutputDuplicationCount; // The number of current DDA clients that are attached to the VidPN specified by the VidPnSourceId member. +} D3DKMT_OUTPUTDUPLCONTEXTSCOUNT; + +typedef enum _D3DKMDT_GRAPHICS_PREEMPTION_GRANULARITY +{ + D3DKMDT_GRAPHICS_PREEMPTION_NONE = 0, + D3DKMDT_GRAPHICS_PREEMPTION_DMA_BUFFER_BOUNDARY = 100, + D3DKMDT_GRAPHICS_PREEMPTION_PRIMITIVE_BOUNDARY = 200, + D3DKMDT_GRAPHICS_PREEMPTION_TRIANGLE_BOUNDARY = 300, + D3DKMDT_GRAPHICS_PREEMPTION_PIXEL_BOUNDARY = 400, + D3DKMDT_GRAPHICS_PREEMPTION_SHADER_BOUNDARY = 500, +} D3DKMDT_GRAPHICS_PREEMPTION_GRANULARITY; + +typedef enum _D3DKMDT_COMPUTE_PREEMPTION_GRANULARITY +{ + D3DKMDT_COMPUTE_PREEMPTION_NONE = 0, + D3DKMDT_COMPUTE_PREEMPTION_DMA_BUFFER_BOUNDARY = 100, + D3DKMDT_COMPUTE_PREEMPTION_DISPATCH_BOUNDARY = 200, + D3DKMDT_COMPUTE_PREEMPTION_THREAD_GROUP_BOUNDARY = 300, + D3DKMDT_COMPUTE_PREEMPTION_THREAD_BOUNDARY = 400, + D3DKMDT_COMPUTE_PREEMPTION_SHADER_BOUNDARY = 500, +} D3DKMDT_COMPUTE_PREEMPTION_GRANULARITY; + +typedef struct _D3DKMDT_PREEMPTION_CAPS +{ + D3DKMDT_GRAPHICS_PREEMPTION_GRANULARITY GraphicsPreemptionGranularity; + D3DKMDT_COMPUTE_PREEMPTION_GRANULARITY ComputePreemptionGranularity; +} D3DKMDT_PREEMPTION_CAPS; + +typedef struct _D3DKMT_WDDM_1_2_CAPS +{ + D3DKMDT_PREEMPTION_CAPS PreemptionCaps; + union + { + struct + { + UINT32 SupportNonVGA : 1; + UINT32 SupportSmoothRotation : 1; + UINT32 SupportPerEngineTDR : 1; + UINT32 SupportKernelModeCommandBuffer : 1; + UINT32 SupportCCD : 1; + UINT32 SupportSoftwareDeviceBitmaps : 1; + UINT32 SupportGammaRamp : 1; + UINT32 SupportHWCursor : 1; + UINT32 SupportHWVSync : 1; + UINT32 SupportSurpriseRemovalInHibernation : 1; + UINT32 Reserved : 22; + }; + UINT32 Value; + }; +} D3DKMT_WDDM_1_2_CAPS; + +// Indicates the version number of the user-mode driver. +typedef struct _D3DKMT_UMD_DRIVER_VERSION +{ + LARGE_INTEGER DriverVersion; // The user-mode driver version. +} D3DKMT_UMD_DRIVER_VERSION; + +// Indicates whether the user-mode driver supports Direct Flip operations, in which video memory is seamlessly flipped between an application's managed primary allocations and the Desktop Window Manager (DWM) managed primary allocations. +typedef struct _D3DKMT_DIRECTFLIP_SUPPORT +{ + BOOL Supported; // If TRUE, the driver supports Direct Flip operations. Otherwise, the driver does not support Direct Flip operations. +} D3DKMT_DIRECTFLIP_SUPPORT; + +typedef struct _D3DKMT_MULTIPLANEOVERLAY_SUPPORT +{ + BOOL Supported; +} D3DKMT_MULTIPLANEOVERLAY_SUPPORT; + +typedef struct _D3DKMT_DLIST_DRIVER_NAME +{ + _Out_ WCHAR DListFileName[MAX_PATH]; // DList driver file name +} D3DKMT_DLIST_DRIVER_NAME; + +typedef struct _D3DKMT_WDDM_1_3_CAPS +{ + union + { + struct + { + UINT32 SupportMiracast : 1; + UINT32 IsHybridIntegratedGPU : 1; + UINT32 IsHybridDiscreteGPU : 1; + UINT32 SupportPowerManagementPStates : 1; + UINT32 SupportVirtualModes : 1; + UINT32 SupportCrossAdapterResource : 1; + UINT32 Reserved : 26; + }; + UINT32 Value; + }; +} D3DKMT_WDDM_1_3_CAPS; + +typedef struct _D3DKMT_MULTIPLANEOVERLAY_HUD_SUPPORT +{ + UINT32 VidPnSourceId; // Not yet used. + BOOL Update; + BOOL KernelSupported; + BOOL HudSupported; +} D3DKMT_MULTIPLANEOVERLAY_HUD_SUPPORT; + +typedef struct _D3DKMT_WDDM_2_0_CAPS +{ + union + { + struct + { + UINT32 Support64BitAtomics : 1; + UINT32 GpuMmuSupported : 1; + UINT32 IoMmuSupported : 1; + UINT32 FlipOverwriteSupported : 1; // since WDDM2_4 + UINT32 SupportContextlessPresent : 1; + UINT32 Reserved : 27; + }; + UINT32 Value; + }; +} D3DKMT_WDDM_2_0_CAPS; + +// Indicates the type of engine on a GPU node. +typedef enum _DXGK_ENGINE_TYPE +{ + DXGK_ENGINE_TYPE_OTHER = 0, // This value is used for proprietary or unique functionality that is not exposed by typical adapters, as well as for an engine that performs work that doesn't fall under another category. + DXGK_ENGINE_TYPE_3D = 1, // The adapter's 3-D processing engine. All adapters that are not a display-only device have one 3-D engine. + DXGK_ENGINE_TYPE_VIDEO_DECODE = 2, // The engine that handles video decoding, including decompression of video frames from an input stream into typical YUV surfaces. The workload packets for an H.264 video codec workload test must appear on either the decode engine or the 3-D engine. + DXGK_ENGINE_TYPE_VIDEO_ENCODE = 3, // The engine that handles video encoding, including compression of typical video frames into an encoded video format. + DXGK_ENGINE_TYPE_VIDEO_PROCESSING = 4, // The engine that is responsible for any video processing that is done after a video input stream is decoded. Such processing can include RGB surface conversion, filtering, stretching, color correction, deinterlacing, or other steps that are required before the final image is rendered to the display screen. The workload packets for workload tests must appear on either the video processing engine or the 3-D engine. + DXGK_ENGINE_TYPE_SCENE_ASSEMBLY = 5, // The engine that performs vertex processing of 3-D workloads as a preliminary pass prior to the remainder of the 3-D rendering. This engine also stores vertices in bins that tile-based rendering engines use. + DXGK_ENGINE_TYPE_COPY = 6, // The engine that is a copy engine used for moving data. This engine can perform subresource updates, blitting, paging, or other similar data handling. The workload packets for calls to CopySubresourceRegion or UpdateSubResource methods of Direct3D 10 and Direct3D 11 must appear on either the copy engine or the 3-D engine. + DXGK_ENGINE_TYPE_OVERLAY = 7, // The virtual engine that is used for synchronized flipping of overlays in Direct3D 9. + DXGK_ENGINE_TYPE_CRYPTO, + DXGK_ENGINE_TYPE_MAX +} DXGK_ENGINE_TYPE; + +#define DXGK_MAX_METADATA_NAME_LENGTH 32 + +#include +typedef struct _D3DKMT_NODEMETADATA +{ + union + { + _In_ UINT32 NodeOrdinalAndAdapterIndex; + struct + { + UINT32 NodeOrdinal : 16; + UINT32 AdapterIndex : 16; + }; + }; + _Out_ DXGK_ENGINE_TYPE EngineType; + _Out_ WCHAR FriendlyName[DXGK_MAX_METADATA_NAME_LENGTH]; + _Out_ UINT32 Reserved; + _Out_ BOOLEAN GpuMmuSupported; // Indicates whether the graphics engines of the node support the GpuMmu model. // since WDDM2_0 + _Out_ BOOLEAN IoMmuSupported; // Indicates whether the graphics engines of the node support the SVM model. +} D3DKMT_NODEMETADATA; +#include + +typedef struct _D3DKMT_CPDRIVERNAME +{ + WCHAR ContentProtectionFileName[MAX_PATH]; +} D3DKMT_CPDRIVERNAME; + +typedef struct _D3DKMT_XBOX +{ + BOOL IsXBOX; +} D3DKMT_XBOX; + +typedef struct _D3DKMT_INDEPENDENTFLIP_SUPPORT +{ + BOOL Supported; +} D3DKMT_INDEPENDENTFLIP_SUPPORT; + +typedef struct _D3DKMT_MIRACASTCOMPANIONDRIVERNAME +{ + WCHAR MiracastCompanionDriverName[MAX_PATH]; +} D3DKMT_MIRACASTCOMPANIONDRIVERNAME; + +typedef struct _D3DKMT_PHYSICAL_ADAPTER_COUNT +{ + UINT32 Count; +} D3DKMT_PHYSICAL_ADAPTER_COUNT; + +typedef struct _D3DKMT_DEVICE_IDS +{ + UINT32 VendorID; + UINT32 DeviceID; + UINT32 SubVendorID; + UINT32 SubSystemID; + UINT32 RevisionID; + UINT32 BusType; +} D3DKMT_DEVICE_IDS; + +typedef struct _D3DKMT_QUERY_DEVICE_IDS +{ + _In_ UINT32 PhysicalAdapterIndex; + _Out_ D3DKMT_DEVICE_IDS DeviceIds; +} D3DKMT_QUERY_DEVICE_IDS; + +typedef struct _D3DKMT_DRIVERCAPS_EXT +{ + union + { + struct + { + UINT32 VirtualModeSupport : 1; + UINT32 Reserved : 31; + }; + UINT32 Value; + }; +} D3DKMT_DRIVERCAPS_EXT; + +typedef enum _D3DKMT_MIRACAST_DRIVER_TYPE +{ + D3DKMT_MIRACAST_DRIVER_NOT_SUPPORTED = 0, + D3DKMT_MIRACAST_DRIVER_IHV = 1, + D3DKMT_MIRACAST_DRIVER_MS = 2, +} D3DKMT_MIRACAST_DRIVER_TYPE; + +typedef struct _D3DKMT_QUERY_MIRACAST_DRIVER_TYPE +{ + D3DKMT_MIRACAST_DRIVER_TYPE MiracastDriverType; +} D3DKMT_QUERY_MIRACAST_DRIVER_TYPE; + +typedef struct _D3DKMT_GPUMMU_CAPS +{ + union + { + struct + { + UINT32 ReadOnlyMemorySupported : 1; + UINT32 NoExecuteMemorySupported : 1; + UINT32 CacheCoherentMemorySupported : 1; + UINT32 Reserved : 29; + }; + UINT32 Value; + } Flags; + UINT32 VirtualAddressBitCount; +} D3DKMT_GPUMMU_CAPS; + +typedef struct _D3DKMT_QUERY_GPUMMU_CAPS +{ + _In_ UINT32 PhysicalAdapterIndex; + _Out_ D3DKMT_GPUMMU_CAPS Caps; +} D3DKMT_QUERY_GPUMMU_CAPS; + +typedef struct _D3DKMT_MULTIPLANEOVERLAY_DECODE_SUPPORT +{ + BOOL Supported; +} D3DKMT_MULTIPLANEOVERLAY_DECODE_SUPPORT; + +typedef struct _D3DKMT_ISBADDRIVERFORHWPROTECTIONDISABLED +{ + BOOL Disabled; +} D3DKMT_ISBADDRIVERFORHWPROTECTIONDISABLED; + +typedef struct _D3DKMT_MULTIPLANEOVERLAY_SECONDARY_SUPPORT +{ + BOOL Supported; +} D3DKMT_MULTIPLANEOVERLAY_SECONDARY_SUPPORT; + +typedef struct _D3DKMT_INDEPENDENTFLIP_SECONDARY_SUPPORT +{ + BOOL Supported; +} D3DKMT_INDEPENDENTFLIP_SECONDARY_SUPPORT; + +typedef struct _D3DKMT_PANELFITTER_SUPPORT +{ + BOOL Supported; +} D3DKMT_PANELFITTER_SUPPORT; + +typedef enum _D3DKMT_PNP_KEY_TYPE +{ + D3DKMT_PNP_KEY_HARDWARE = 1, + D3DKMT_PNP_KEY_SOFTWARE = 2 +} D3DKMT_PNP_KEY_TYPE; + +// A structure that holds information to query the physical adapter PNP key. +typedef struct _D3DKMT_QUERY_PHYSICAL_ADAPTER_PNP_KEY +{ + _In_ UINT32 PhysicalAdapterIndex; // The physical adapter index. + _In_ D3DKMT_PNP_KEY_TYPE PnPKeyType; // The type of the PNP key being queried. + _Field_size_opt_(*pCchDest) WCHAR *pDest; // A WCHAR value respresenting the pDest. + _Inout_ UINT32 *pCchDest; // A UINT value representing the pCchDest. +} D3DKMT_QUERY_PHYSICAL_ADAPTER_PNP_KEY; + +// A structure that holds information about the segment group size. +typedef struct _D3DKMT_SEGMENTGROUPSIZEINFO +{ + _In_ UINT32 PhysicalAdapterIndex; // An index to the physical adapter. + _Out_ D3DKMT_SEGMENTSIZEINFO LegacyInfo; // Legacy segment size info. + _Out_ ULONGLONG LocalMemory; // The size of local memory. + _Out_ ULONGLONG NonLocalMemory; // The size of non-local memory. + _Out_ ULONGLONG NonBudgetMemory; // The size of non-budget memory. +} D3DKMT_SEGMENTGROUPSIZEINFO; + +// A structure that holds the support status. +typedef struct _D3DKMT_MPO3DDI_SUPPORT +{ + BOOL Supported; // Indicates whether support exists. +} D3DKMT_MPO3DDI_SUPPORT; + +typedef struct _D3DKMT_HWDRM_SUPPORT +{ + BOOLEAN Supported; +} D3DKMT_HWDRM_SUPPORT; + +typedef struct _D3DKMT_MPOKERNELCAPS_SUPPORT +{ + BOOL Supported; +} D3DKMT_MPOKERNELCAPS_SUPPORT; + +typedef struct _D3DKMT_MULTIPLANEOVERLAY_STRETCH_SUPPORT +{ + UINT32 VidPnSourceId; + BOOL Update; + BOOL Supported; +} D3DKMT_MULTIPLANEOVERLAY_STRETCH_SUPPORT; + +typedef struct _D3DKMT_GET_DEVICE_VIDPN_OWNERSHIP_INFO +{ + _In_ D3DKMT_HANDLE hDevice; // Indentifies the device + _Out_ BOOLEAN bFailedDwmAcquireVidPn; // True if Dwm Acquire VidPn failed due to another Dwm device having ownership +} D3DKMT_GET_DEVICE_VIDPN_OWNERSHIP_INFO; + +// Contains information to query for registry flags. +typedef struct _D3DDDI_QUERYREGISTRY_FLAGS +{ + union + { + struct + { + UINT32 TranslatePath : 1; + UINT32 MutableValue : 1; + UINT32 Reserved : 30; + }; + UINT32 Value; + }; +} D3DDDI_QUERYREGISTRY_FLAGS; + +typedef enum _D3DDDI_QUERYREGISTRY_TYPE +{ + D3DDDI_QUERYREGISTRY_SERVICEKEY = 0, // HKLM\System\CurrentControlSet\Services\nvlddmkm + D3DDDI_QUERYREGISTRY_ADAPTERKEY = 1, // HKLM\System\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000 + D3DDDI_QUERYREGISTRY_DRIVERSTOREPATH = 2, + D3DDDI_QUERYREGISTRY_DRIVERIMAGEPATH = 3, // REDSTONE5 + D3DDDI_QUERYREGISTRY_MAX, +} D3DDDI_QUERYREGISTRY_TYPE; + +typedef enum _D3DDDI_QUERYREGISTRY_STATUS +{ + D3DDDI_QUERYREGISTRY_STATUS_SUCCESS = 0, + D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW = 1, + D3DDDI_QUERYREGISTRY_STATUS_FAIL = 2, + D3DDDI_QUERYREGISTRY_STATUS_MAX, +} D3DDDI_QUERYREGISTRY_STATUS; + +// Contains information about the query registry. +// PrivateDriverSize must be sizeof(D3DDDI_QUERYREGISTRY_INFO) + (size of the the key value in bytes) +typedef struct _D3DDDI_QUERYREGISTRY_INFO +{ + _In_ D3DDDI_QUERYREGISTRY_TYPE QueryType; + _In_ D3DDDI_QUERYREGISTRY_FLAGS QueryFlags; + _In_ WCHAR ValueName[MAX_PATH]; // The name of the registry value. + _In_ ULONG ValueType; // REG_XX types (https://msdn.microsoft.com/en-us/library/windows/desktop/ms724884.aspx) + _In_ ULONG PhysicalAdapterIndex; // The physical adapter index in a LDA chain. + _Out_ ULONG OutputValueSize;// Number of bytes written to the output value or required in case of D3DDDI_QUERYREGISTRY_STATUS_BUFFER_OVERFLOW. + _Out_ D3DDDI_QUERYREGISTRY_STATUS Status; + union + { + _Out_ ULONG OutputDword; + _Out_ UINT64 OutputQword; + _Out_ WCHAR OutputString[1]; + _Out_ BYTE OutputBinary[1]; + }; +} D3DDDI_QUERYREGISTRY_INFO; + +// Contains the kernel mode driver version. +typedef struct _D3DKMT_KMD_DRIVER_VERSION +{ + LARGE_INTEGER DriverVersion; // The driver version. +} D3DKMT_KMD_DRIVER_VERSION; + +typedef struct _D3DKMT_BLOCKLIST_INFO +{ + UINT32 Size; // The size of the block list. + WCHAR BlockList[1]; // The block list. +} D3DKMT_BLOCKLIST_INFO; + +// Used to query for a unique guid. +typedef struct _D3DKMT_QUERY_ADAPTER_UNIQUE_GUID +{ + WCHAR AdapterUniqueGUID[40]; +} D3DKMT_QUERY_ADAPTER_UNIQUE_GUID; + +// Represents performance data collected per engine from an adapter on an interval basis. +typedef struct _D3DKMT_NODE_PERFDATA +{ + _In_ UINT32 NodeOrdinal; // Node ordinal of the requested engine. + _In_ UINT32 PhysicalAdapterIndex; // The physical adapter index in a LDA chain. + _Out_ ULONGLONG Frequency; // Clock frequency of the requested engine, represented in hertz. + _Out_ ULONGLONG MaxFrequency; // The max frequency the engine can normally reach in hertz while not overclocked. + _Out_ ULONGLONG MaxFrequencyOC; // The max frequency the engine can reach with it�s current overclock in hertz. + _Out_ ULONG Voltage; // Voltage of the engine in milli volts mV + _Out_ ULONG VoltageMax; // The max voltage of the engine in milli volts while not overclocked. + _Out_ ULONG VoltageMaxOC; // The max voltage of the engine while overclocked in milli volts. + _Out_ ULONGLONG MaxTransitionLatency; // Max transition latency to change the frequency in 100 nanoseconds // REDSTONE5 +} D3DKMT_NODE_PERFDATA; + +// Represents performance data collected per adapter on an interval basis. +typedef struct _D3DKMT_ADAPTER_PERFDATA +{ + _In_ UINT32 PhysicalAdapterIndex; // The physical adapter index in a LDA chain. + _Out_ ULONGLONG MemoryFrequency; // Clock frequency of the memory in hertz + _Out_ ULONGLONG MaxMemoryFrequency; // Max clock frequency of the memory while not overclocked, represented in hertz. + _Out_ ULONGLONG MaxMemoryFrequencyOC; // Clock frequency of the memory while overclocked in hertz. + _Out_ ULONGLONG MemoryBandwidth; // Amount of memory transferred in bytes + _Out_ ULONGLONG PCIEBandwidth; // Amount of memory transferred over PCI-E in bytes + _Out_ ULONG FanRPM; // Fan rpm + _Out_ ULONG Power; // Power draw of the adapter in tenths of a percentage + _Out_ ULONG Temperature; // Temperature in deci-Celsius 1 = 0.1C + _Out_ UCHAR PowerStateOverride; // Overrides dxgkrnls power view of linked adapters. +} D3DKMT_ADAPTER_PERFDATA; + +// Represents data capabilities that are static and queried once per GPU during initialization. +typedef struct _D3DKMT_ADAPTER_PERFDATACAPS +{ + _In_ UINT32 PhysicalAdapterIndex; // The physical adapter index in a LDA chain. + _Out_ ULONGLONG MaxMemoryBandwidth; // Max memory bandwidth in bytes for 1 second + _Out_ ULONGLONG MaxPCIEBandwidth; // Max pcie bandwidth in bytes for 1 second + _Out_ ULONG MaxFanRPM; // Max fan rpm + _Out_ ULONG TemperatureMax; // Max temperature before damage levels + _Out_ ULONG TemperatureWarning; // The temperature level where throttling begins. +} D3DKMT_ADAPTER_PERFDATACAPS; + +#define DXGK_MAX_GPUVERSION_NAME_LENGTH 32 + +// Used to collect the bios version and gpu architecture name once during GPU initialization. +typedef struct _D3DKMT_GPUVERSION +{ + _In_ UINT32 PhysicalAdapterIndex; // The physical adapter index in a LDA chain. + _Out_ WCHAR BiosVersion[DXGK_MAX_GPUVERSION_NAME_LENGTH]; // The current bios of the adapter. + _Out_ WCHAR GpuArchitecture[DXGK_MAX_GPUVERSION_NAME_LENGTH]; // The gpu architecture of the adapter. +} D3DKMT_GPUVERSION; + +// Describes the kernel mode display driver. +typedef struct _D3DKMT_DRIVER_DESCRIPTION +{ + WCHAR DriverDescription[4096]; // out: Pointer to a string of characters that represent the driver description. +} D3DKMT_DRIVER_DESCRIPTION; + +typedef struct _D3DKMT_QUERY_SCANOUT_CAPS +{ + ULONG VidPnSourceId; + UINT Caps; +} D3DKMT_QUERY_SCANOUT_CAPS; + +// Describes the mapping of the given name of a device to a graphics adapter handle and monitor output. +typedef struct _D3DKMT_OPENADAPTERFROMDEVICENAME +{ + _In_ PWSTR DeviceName; // A Null-terminated string that contains the name of the device from which to open an adapter instance. + _Out_ D3DKMT_HANDLE AdapterHandle; // A handle to the graphics adapter for the device that DeviceName specifies. + _Out_ LUID AdapterLuid; // The locally unique identifier (LUID) of the graphics adapter for the device that DeviceName specifies. +} D3DKMT_OPENADAPTERFROMDEVICENAME; + +// Describes the mapping of the given name of a GDI device to a graphics adapter handle and monitor output. +typedef struct _D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME +{ + _In_ WCHAR DeviceName[32]; // A Unicode string that contains the name of the GDI device from which to open an adapter instance. + _Out_ D3DKMT_HANDLE AdapterHandle; // A handle to the graphics adapter for the GDI device that DeviceName specifies. + _Out_ LUID AdapterLuid; // The locally unique identifier (LUID) of the graphics adapter for the GDI device that DeviceName specifies. + _Out_ ULONG VidPnSourceId; // The zero-based identification number of the video present source. +} D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME; + +// Describes the mapping of a device context handle (HDC) to a graphics adapter handle and monitor output. +typedef struct _D3DKMT_OPENADAPTERFROMHDC +{ + _In_ HDC hDc; // The HDC for the graphics adapter and monitor output that are retrieved. + _Out_ D3DKMT_HANDLE AdapterHandle; // A handle to the graphics adapter for the HDC that hDc specifies. + _Out_ LUID AdapterLuid; // The locally unique identifier (LUID) of the graphics adapter for the HDC that hDc specifies. + _Out_ ULONG VidPnSourceId; // The zero-based identification number of the video present source. +} D3DKMT_OPENADAPTERFROMHDC; + +// Describes the mapping of the given locally unique identifier (LUID) of a device to a graphics adapter handle. +typedef struct _D3DKMT_OPENADAPTERFROMLUID +{ + _In_ LUID AdapterLuid; + _Out_ D3DKMT_HANDLE AdapterHandle; +} D3DKMT_OPENADAPTERFROMLUID; + +// Supplies configuration information about a graphics adapter. +typedef struct _D3DKMT_ADAPTERINFO +{ + D3DKMT_HANDLE AdapterHandle; // A handle to the adapter. + LUID AdapterLuid; // A LUID that serves as an identifier for the adapter. + ULONG NumOfSources; // The number of video present sources supported by the adapter. + BOOL bPresentMoveRegionsPreferred; // If TRUE, the adapter prefers move regions. +} D3DKMT_ADAPTERINFO; + +#define MAX_ENUM_ADAPTERS 16 + +// Supplies information for enumerating all graphics adapters on the system. +typedef struct _D3DKMT_ENUMADAPTERS +{ + _In_ ULONG NumAdapters; // The number of graphics adapters. + _Out_ D3DKMT_ADAPTERINFO Adapters[MAX_ENUM_ADAPTERS]; // An array of D3DKMT_ADAPTERINFO structures that supply configuration information for each adapter. +} D3DKMT_ENUMADAPTERS; + +// Supplies information for enumerating all graphics adapters on the system. +typedef struct _D3DKMT_ENUMADAPTERS2 +{ + _Inout_ ULONG NumAdapters; // On input, the count of the pAdapters array buffer. On output, the number of adapters enumerated. + _Out_ D3DKMT_ADAPTERINFO* Adapters; // Array of enumerated adapters containing NumAdapters elements. +} D3DKMT_ENUMADAPTERS2; + +// The D3DKMT_CLOSEADAPTER structure specifies the graphics adapter to close. +typedef struct _D3DKMT_CLOSEADAPTER +{ + _In_ D3DKMT_HANDLE AdapterHandle; // A handle to the graphics adapter to close. +} D3DKMT_CLOSEADAPTER; + +typedef struct _D3DKMT_QUERYADAPTERINFO +{ + _In_ D3DKMT_HANDLE AdapterHandle; + _In_ KMTQUERYADAPTERINFOTYPE Type; + _Inout_bytecount_(PrivateDriverDataSize) PVOID PrivateDriverData; + _Out_ UINT32 PrivateDriverDataSize; +} D3DKMT_QUERYADAPTERINFO; + +typedef enum _D3DKMT_QUERYRESULT_PREEMPTION_ATTEMPT_RESULT +{ + D3DKMT_PreemptionAttempt = 0, + D3DKMT_PreemptionAttemptSuccess = 1, + D3DKMT_PreemptionAttemptMissNoCommand = 2, + D3DKMT_PreemptionAttemptMissNotEnabled = 3, + D3DKMT_PreemptionAttemptMissNextFence = 4, + D3DKMT_PreemptionAttemptMissPagingCommand = 5, + D3DKMT_PreemptionAttemptMissSplittedCommand = 6, + D3DKMT_PreemptionAttemptMissFenceCommand= 7, + D3DKMT_PreemptionAttemptMissRenderPendingFlip = 8, + D3DKMT_PreemptionAttemptMissNotMakingProgress = 9, + D3DKMT_PreemptionAttemptMissLessPriority = 10, + D3DKMT_PreemptionAttemptMissRemainingQuantum = 11, + D3DKMT_PreemptionAttemptMissRemainingPreemptionQuantum = 12, + D3DKMT_PreemptionAttemptMissAlreadyPreempting = 13, + D3DKMT_PreemptionAttemptMissGlobalBlock = 14, + D3DKMT_PreemptionAttemptMissAlreadyRunning = 15, + D3DKMT_PreemptionAttemptStatisticsMax +} D3DKMT_QUERYRESULT_PREEMPTION_ATTEMPT_RESULT; + +typedef enum _D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE +{ + D3DKMT_ClientRenderBuffer = 0, + D3DKMT_ClientPagingBuffer = 1, + D3DKMT_SystemPagingBuffer = 2, + D3DKMT_SystemPreemptionBuffer = 3, + D3DKMT_DmaPacketTypeMax +} D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE; + +typedef enum _D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE +{ + D3DKMT_RenderCommandBuffer = 0, + D3DKMT_DeferredCommandBuffer = 1, + D3DKMT_SystemCommandBuffer = 2, + D3DKMT_MmIoFlipCommandBuffer = 3, + D3DKMT_WaitCommandBuffer = 4, + D3DKMT_SignalCommandBuffer = 5, + D3DKMT_DeviceCommandBuffer = 6, + D3DKMT_SoftwareCommandBuffer = 7, + D3DKMT_QueuePacketTypeMax +} D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE; + +typedef enum _D3DKMT_QUERYSTATISTICS_ALLOCATION_PRIORITY_CLASS +{ + D3DKMT_AllocationPriorityClassMinimum = 0, + D3DKMT_AllocationPriorityClassLow = 1, + D3DKMT_AllocationPriorityClassNormal = 2, + D3DKMT_AllocationPriorityClassHigh = 3, + D3DKMT_AllocationPriorityClassMaximum = 4, + D3DKMT_MaxAllocationPriorityClass +} D3DKMT_QUERYSTATISTICS_ALLOCATION_PRIORITY_CLASS; + +#define D3DKMT_QUERYSTATISTICS_SEGMENT_PREFERENCE_MAX 5 + +typedef struct _D3DKMT_QUERYSTATISTICS_COUNTER +{ + ULONG Count; + ULONGLONG Bytes; +} D3DKMT_QUERYSTATISTICS_COUNTER; + +typedef struct _D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE_INFORMATION +{ + ULONG PacketSubmited; + ULONG PacketCompleted; + ULONG PacketPreempted; + ULONG PacketFaulted; +} D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE_INFORMATION +{ + ULONG PacketSubmited; + ULONG PacketCompleted; +} D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_PACKET_INFORMATION +{ + D3DKMT_QUERYSTATISTICS_QUEUE_PACKET_TYPE_INFORMATION QueuePacket[D3DKMT_QueuePacketTypeMax]; + D3DKMT_QUERYSTATISTICS_DMA_PACKET_TYPE_INFORMATION DmaPacket[D3DKMT_DmaPacketTypeMax]; +} D3DKMT_QUERYSTATISTICS_PACKET_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_PREEMPTION_INFORMATION +{ + ULONG PreemptionCounter[D3DKMT_PreemptionAttemptStatisticsMax]; +} D3DKMT_QUERYSTATISTICS_PREEMPTION_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION +{ + LARGE_INTEGER RunningTime; // 100ns + ULONG ContextSwitch; + D3DKMT_QUERYSTATISTICS_PREEMPTION_INFORMATION PreemptionStatistics; + D3DKMT_QUERYSTATISTICS_PACKET_INFORMATION PacketStatistics; + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_NODE_INFORMATION +{ + D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION GlobalInformation; // global + D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION SystemInformation; // system thread + //ULONG NodeId; // Win10 + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_NODE_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION +{ + ULONG Frame; + ULONG CancelledFrame; + ULONG QueuedPresent; + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_VIDPNSOURCE_INFORMATION +{ + D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION GlobalInformation; // global + D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION SystemInformation; // system thread + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_VIDPNSOURCE_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATSTICS_REFERENCE_DMA_BUFFER +{ + ULONG NbCall; + ULONG NbAllocationsReferenced; + ULONG MaxNbAllocationsReferenced; + ULONG NbNULLReference; + ULONG NbWriteReference; + ULONG NbRenamedAllocationsReferenced; + ULONG NbIterationSearchingRenamedAllocation; + ULONG NbLockedAllocationReferenced; + ULONG NbAllocationWithValidPrepatchingInfoReferenced; + ULONG NbAllocationWithInvalidPrepatchingInfoReferenced; + ULONG NbDMABufferSuccessfullyPrePatched; + ULONG NbPrimariesReferencesOverflow; + ULONG NbAllocationWithNonPreferredResources; + ULONG NbAllocationInsertedInMigrationTable; +} D3DKMT_QUERYSTATSTICS_REFERENCE_DMA_BUFFER; + +typedef struct _D3DKMT_QUERYSTATSTICS_RENAMING +{ + ULONG NbAllocationsRenamed; + ULONG NbAllocationsShrinked; + ULONG NbRenamedBuffer; + ULONG MaxRenamingListLength; + ULONG NbFailuresDueToRenamingLimit; + ULONG NbFailuresDueToCreateAllocation; + ULONG NbFailuresDueToOpenAllocation; + ULONG NbFailuresDueToLowResource; + ULONG NbFailuresDueToNonRetiredLimit; +} D3DKMT_QUERYSTATSTICS_RENAMING; + +typedef struct _D3DKMT_QUERYSTATSTICS_PREPRATION +{ + ULONG BroadcastStall; + ULONG NbDMAPrepared; + ULONG NbDMAPreparedLongPath; + ULONG ImmediateHighestPreparationPass; + D3DKMT_QUERYSTATISTICS_COUNTER AllocationsTrimmed; +} D3DKMT_QUERYSTATSTICS_PREPRATION; + +typedef struct _D3DKMT_QUERYSTATSTICS_PAGING_FAULT +{ + D3DKMT_QUERYSTATISTICS_COUNTER Faults; + D3DKMT_QUERYSTATISTICS_COUNTER FaultsFirstTimeAccess; + D3DKMT_QUERYSTATISTICS_COUNTER FaultsReclaimed; + D3DKMT_QUERYSTATISTICS_COUNTER FaultsMigration; + D3DKMT_QUERYSTATISTICS_COUNTER FaultsIncorrectResource; + D3DKMT_QUERYSTATISTICS_COUNTER FaultsLostContent; + D3DKMT_QUERYSTATISTICS_COUNTER FaultsEvicted; + D3DKMT_QUERYSTATISTICS_COUNTER AllocationsMEM_RESET; + D3DKMT_QUERYSTATISTICS_COUNTER AllocationsUnresetSuccess; + D3DKMT_QUERYSTATISTICS_COUNTER AllocationsUnresetFail; + ULONG AllocationsUnresetSuccessRead; + ULONG AllocationsUnresetFailRead; + + D3DKMT_QUERYSTATISTICS_COUNTER Evictions; + D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToPreparation; + D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToLock; + D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToClose; + D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToPurge; + D3DKMT_QUERYSTATISTICS_COUNTER EvictionsDueToSuspendCPUAccess; +} D3DKMT_QUERYSTATSTICS_PAGING_FAULT; + +typedef struct _D3DKMT_QUERYSTATSTICS_PAGING_TRANSFER +{ + ULONGLONG BytesFilled; + ULONGLONG BytesDiscarded; + ULONGLONG BytesMappedIntoAperture; + ULONGLONG BytesUnmappedFromAperture; + ULONGLONG BytesTransferredFromMdlToMemory; + ULONGLONG BytesTransferredFromMemoryToMdl; + ULONGLONG BytesTransferredFromApertureToMemory; + ULONGLONG BytesTransferredFromMemoryToAperture; +} D3DKMT_QUERYSTATSTICS_PAGING_TRANSFER; + +typedef struct _D3DKMT_QUERYSTATSTICS_SWIZZLING_RANGE +{ + ULONG NbRangesAcquired; + ULONG NbRangesReleased; +} D3DKMT_QUERYSTATSTICS_SWIZZLING_RANGE; + +typedef struct _D3DKMT_QUERYSTATSTICS_LOCKS +{ + ULONG NbLocks; + ULONG NbLocksWaitFlag; + ULONG NbLocksDiscardFlag; + ULONG NbLocksNoOverwrite; + ULONG NbLocksNoReadSync; + ULONG NbLocksLinearization; + ULONG NbComplexLocks; +} D3DKMT_QUERYSTATSTICS_LOCKS; + +typedef struct _D3DKMT_QUERYSTATSTICS_ALLOCATIONS +{ + D3DKMT_QUERYSTATISTICS_COUNTER Created; + D3DKMT_QUERYSTATISTICS_COUNTER Destroyed; + D3DKMT_QUERYSTATISTICS_COUNTER Opened; + D3DKMT_QUERYSTATISTICS_COUNTER Closed; + D3DKMT_QUERYSTATISTICS_COUNTER MigratedSuccess; + D3DKMT_QUERYSTATISTICS_COUNTER MigratedFail; + D3DKMT_QUERYSTATISTICS_COUNTER MigratedAbandoned; +} D3DKMT_QUERYSTATSTICS_ALLOCATIONS; + +typedef struct _D3DKMT_QUERYSTATSTICS_TERMINATIONS +{ + D3DKMT_QUERYSTATISTICS_COUNTER TerminatedShared; + D3DKMT_QUERYSTATISTICS_COUNTER TerminatedNonShared; + D3DKMT_QUERYSTATISTICS_COUNTER DestroyedShared; + D3DKMT_QUERYSTATISTICS_COUNTER DestroyedNonShared; +} D3DKMT_QUERYSTATSTICS_TERMINATIONS; + +typedef struct _D3DKMT_QUERYSTATISTICS_ADAPTER_INFORMATION +{ + ULONG NbSegments; + ULONG NodeCount; + ULONG VidPnSourceCount; + + ULONG VSyncEnabled; + ULONG TdrDetectedCount; + + LONGLONG ZeroLengthDmaBuffers; + ULONGLONG RestartedPeriod; + + D3DKMT_QUERYSTATSTICS_REFERENCE_DMA_BUFFER ReferenceDmaBuffer; + D3DKMT_QUERYSTATSTICS_RENAMING Renaming; + D3DKMT_QUERYSTATSTICS_PREPRATION Preparation; + D3DKMT_QUERYSTATSTICS_PAGING_FAULT PagingFault; + D3DKMT_QUERYSTATSTICS_PAGING_TRANSFER PagingTransfer; + D3DKMT_QUERYSTATSTICS_SWIZZLING_RANGE SwizzlingRange; + D3DKMT_QUERYSTATSTICS_LOCKS Locks; + D3DKMT_QUERYSTATSTICS_ALLOCATIONS Allocations; + D3DKMT_QUERYSTATSTICS_TERMINATIONS Terminations; + + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_ADAPTER_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_SYSTEM_MEMORY +{ + ULONGLONG BytesAllocated; + ULONGLONG BytesReserved; + ULONG SmallAllocationBlocks; + ULONG LargeAllocationBlocks; + ULONGLONG WriteCombinedBytesAllocated; + ULONGLONG WriteCombinedBytesReserved; + ULONGLONG CachedBytesAllocated; + ULONGLONG CachedBytesReserved; + ULONGLONG SectionBytesAllocated; + ULONGLONG SectionBytesReserved; +} D3DKMT_QUERYSTATISTICS_SYSTEM_MEMORY; + +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_INFORMATION +{ + ULONG NodeCount; + ULONG VidPnSourceCount; + + D3DKMT_QUERYSTATISTICS_SYSTEM_MEMORY SystemMemory; + + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_PROCESS_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_DMA_BUFFER +{ + D3DKMT_QUERYSTATISTICS_COUNTER Size; + ULONG AllocationListBytes; + ULONG PatchLocationListBytes; +} D3DKMT_QUERYSTATISTICS_DMA_BUFFER; + +typedef struct _D3DKMT_QUERYSTATISTICS_COMMITMENT_DATA +{ + ULONG64 TotalBytesEvictedFromProcess; + ULONG64 BytesBySegmentPreference[D3DKMT_QUERYSTATISTICS_SEGMENT_PREFERENCE_MAX]; +} D3DKMT_QUERYSTATISTICS_COMMITMENT_DATA; + +typedef struct _D3DKMT_QUERYSTATISTICS_POLICY +{ + ULONGLONG PreferApertureForRead[D3DKMT_MaxAllocationPriorityClass]; + ULONGLONG PreferAperture[D3DKMT_MaxAllocationPriorityClass]; + ULONGLONG MemResetOnPaging; + ULONGLONG RemovePagesFromWorkingSetOnPaging; + ULONGLONG MigrationEnabled; +} D3DKMT_QUERYSTATISTICS_POLICY; + +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER_INFORMATION +{ + ULONG NbSegments; + ULONG NodeCount; + ULONG VidPnSourceCount; + + ULONG VirtualMemoryUsage; + + D3DKMT_QUERYSTATISTICS_DMA_BUFFER DmaBuffer; + D3DKMT_QUERYSTATISTICS_COMMITMENT_DATA CommitmentData; + D3DKMT_QUERYSTATISTICS_POLICY _Policy; + + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_MEMORY +{ + ULONGLONG TotalBytesEvicted; + ULONG AllocsCommitted; + ULONG AllocsResident; +} D3DKMT_QUERYSTATISTICS_MEMORY; + +typedef struct _D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 +{ + ULONG CommitLimit; + ULONG BytesCommitted; + ULONG BytesResident; + + D3DKMT_QUERYSTATISTICS_MEMORY Memory; + + ULONG Aperture; // boolean + + ULONGLONG TotalBytesEvictedByPriority[D3DKMT_MaxAllocationPriorityClass]; + + ULONG64 SystemMemoryEndAddress; + struct + { + ULONG64 PreservedDuringStandby : 1; + ULONG64 PreservedDuringHibernate : 1; + ULONG64 PartiallyPreservedDuringHibernate : 1; + ULONG64 Reserved : 61; + } PowerFlags; + + ULONG64 Reserved[7]; +} D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1; + +typedef struct _D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION +{ + ULONGLONG CommitLimit; + ULONGLONG BytesCommitted; + ULONGLONG BytesResident; + + D3DKMT_QUERYSTATISTICS_MEMORY Memory; + + ULONG Aperture; // boolean + + ULONGLONG TotalBytesEvictedByPriority[D3DKMT_MaxAllocationPriorityClass]; + + ULONG64 SystemMemoryEndAddress; + struct + { + ULONG64 PreservedDuringStandby : 1; + ULONG64 PreservedDuringHibernate : 1; + ULONG64 PartiallyPreservedDuringHibernate : 1; + ULONG64 Reserved : 61; + } PowerFlags; + + ULONG64 Reserved[6]; +} D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION; + +typedef struct _D3DKMT_QUERYSTATISTICS_VIDEO_MEMORY +{ + ULONG AllocsCommitted; + D3DKMT_QUERYSTATISTICS_COUNTER AllocsResidentInP[D3DKMT_QUERYSTATISTICS_SEGMENT_PREFERENCE_MAX]; + D3DKMT_QUERYSTATISTICS_COUNTER AllocsResidentInNonPreferred; + ULONGLONG TotalBytesEvictedDueToPreparation; +} D3DKMT_QUERYSTATISTICS_VIDEO_MEMORY; + +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_POLICY +{ + ULONGLONG UseMRU; +} D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_POLICY; + +typedef struct _D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_INFORMATION +{ + ULONGLONG BytesCommitted; + ULONGLONG MaximumWorkingSet; + ULONGLONG MinimumWorkingSet; + + ULONG NbReferencedAllocationEvictedInPeriod; + + D3DKMT_QUERYSTATISTICS_VIDEO_MEMORY VideoMemory; + D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_POLICY _Policy; + + ULONG64 Reserved[8]; +} D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_INFORMATION; + +typedef enum _D3DKMT_QUERYSTATISTICS_TYPE +{ + D3DKMT_QUERYSTATISTICS_ADAPTER, + D3DKMT_QUERYSTATISTICS_PROCESS, + D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER, + D3DKMT_QUERYSTATISTICS_SEGMENT, + D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT, + D3DKMT_QUERYSTATISTICS_NODE, + D3DKMT_QUERYSTATISTICS_PROCESS_NODE, + D3DKMT_QUERYSTATISTICS_VIDPNSOURCE, + D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE, + D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_GROUP, + D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER +} D3DKMT_QUERYSTATISTICS_TYPE; + +typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT +{ + ULONG SegmentId; +} D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT; + +typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_NODE +{ + ULONG NodeId; +} D3DKMT_QUERYSTATISTICS_QUERY_NODE; + +typedef struct _D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE +{ + ULONG VidPnSourceId; +} D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE; + +typedef union _D3DKMT_QUERYSTATISTICS_RESULT +{ + D3DKMT_QUERYSTATISTICS_ADAPTER_INFORMATION AdapterInformation; + // D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER_INFORMATION PhysAdapterInformation; + D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION_V1 SegmentInformationV1; // WIN7 + D3DKMT_QUERYSTATISTICS_SEGMENT_INFORMATION SegmentInformation; // WIN8 + D3DKMT_QUERYSTATISTICS_NODE_INFORMATION NodeInformation; + D3DKMT_QUERYSTATISTICS_VIDPNSOURCE_INFORMATION VidPnSourceInformation; + D3DKMT_QUERYSTATISTICS_PROCESS_INFORMATION ProcessInformation; + D3DKMT_QUERYSTATISTICS_PROCESS_ADAPTER_INFORMATION ProcessAdapterInformation; + D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_INFORMATION ProcessSegmentInformation; + D3DKMT_QUERYSTATISTICS_PROCESS_NODE_INFORMATION ProcessNodeInformation; + D3DKMT_QUERYSTATISTICS_PROCESS_VIDPNSOURCE_INFORMATION ProcessVidPnSourceInformation; + // D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT_GROUP_INFORMATION ProcessSegmentGroupInformation; +} D3DKMT_QUERYSTATISTICS_RESULT; + +typedef struct _D3DKMT_QUERYSTATISTICS +{ + _In_ D3DKMT_QUERYSTATISTICS_TYPE Type; + _In_ LUID AdapterLuid; + _In_opt_ HANDLE ProcessHandle; + _Out_ D3DKMT_QUERYSTATISTICS_RESULT QueryResult; + + union + { + _In_ D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT QuerySegment; + _In_ D3DKMT_QUERYSTATISTICS_QUERY_SEGMENT QueryProcessSegment; + _In_ D3DKMT_QUERYSTATISTICS_QUERY_NODE QueryNode; + _In_ D3DKMT_QUERYSTATISTICS_QUERY_NODE QueryProcessNode; + _In_ D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE QueryVidPnSource; + _In_ D3DKMT_QUERYSTATISTICS_QUERY_VIDPNSOURCE QueryProcessVidPnSource; + }; +} D3DKMT_QUERYSTATISTICS; + +typedef enum _D3DKMT_MEMORY_SEGMENT_GROUP +{ + D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL = 0, + D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL = 1 +} D3DKMT_MEMORY_SEGMENT_GROUP; + +typedef struct _D3DKMT_QUERYVIDEOMEMORYINFO +{ + _In_opt_ HANDLE ProcessHandle; // A handle to a process. If NULL, the current process is used. The process handle must be opened with PROCESS_QUERY_INFORMATION privileges. + _In_ D3DKMT_HANDLE AdapterHandle; // The adapter to query for this process + _In_ D3DKMT_MEMORY_SEGMENT_GROUP MemorySegmentGroup; // The memory segment group to query. + _Out_ UINT64 Budget; // Total memory the application may use + _Out_ UINT64 CurrentUsage; // Current memory usage of the device + _Out_ UINT64 CurrentReservation; // Current reservation of the device + _Out_ UINT64 AvailableForReservation; // Total that the device may reserve + _In_ UINT32 PhysicalAdapterIndex; // Zero based physical adapter index in the LDA configuration. +} D3DKMT_QUERYVIDEOMEMORYINFO; + +typedef enum _D3DKMT_ESCAPETYPE +{ + D3DKMT_ESCAPE_DRIVERPRIVATE = 0, + D3DKMT_ESCAPE_VIDMM = 1, // D3DKMT_VIDMM_ESCAPE + D3DKMT_ESCAPE_TDRDBGCTRL = 2, // D3DKMT_TDRDBGCTRLTYPE + D3DKMT_ESCAPE_VIDSCH = 3, // D3DKMT_VIDSCH_ESCAPE + D3DKMT_ESCAPE_DEVICE = 4, // D3DKMT_DEVICE_ESCAPE + D3DKMT_ESCAPE_DMM = 5, // D3DKMT_DMM_ESCAPE + D3DKMT_ESCAPE_DEBUG_SNAPSHOT = 6, // D3DKMT_DEBUG_SNAPSHOT_ESCAPE + // unused (7 was previously used to set driver update in-progress status, D3DKMT_ESCAPE_SETDRIVERUPDATESTATUS) + D3DKMT_ESCAPE_DRT_TEST = 8, + D3DKMT_ESCAPE_DIAGNOSTICS = 9, // since WIN8 + D3DKMT_ESCAPE_OUTPUTDUPL_SNAPSHOT = 10, + D3DKMT_ESCAPE_OUTPUTDUPL_DIAGNOSTICS = 11, + D3DKMT_ESCAPE_BDD_PNP = 12, + D3DKMT_ESCAPE_BDD_FALLBACK = 13, + D3DKMT_ESCAPE_ACTIVATE_SPECIFIC_DIAG = 14, // D3DKMT_ACTIVATE_SPECIFIC_DIAG_ESCAPE + D3DKMT_ESCAPE_MODES_PRUNED_OUT = 15, + D3DKMT_ESCAPE_WHQL_INFO = 16, // UINT32 ?? + D3DKMT_ESCAPE_BRIGHTNESS = 17, + D3DKMT_ESCAPE_EDID_CACHE = 18, // UINT32 ?? + D3DKMT_ESCAPE_GENERIC_ADAPTER_DIAG_INFO = 19, + D3DKMT_ESCAPE_MIRACAST_DISPLAY_REQUEST = 20, // since WDDM1_3 + D3DKMT_ESCAPE_HISTORY_BUFFER_STATUS = 21, + // 22 can be reused for future needs as it was never exposed for external purposes + D3DKMT_ESCAPE_MIRACAST_ADAPTER_DIAG_INFO = 23, + D3DKMT_ESCAPE_FORCE_BDDFALLBACK_HEADLESS = 24, // since WDDM2_0 + D3DKMT_ESCAPE_REQUEST_MACHINE_CRASH = 25, // D3DKMT_REQUEST_MACHINE_CRASH_ESCAPE + D3DKMT_ESCAPE_HMD_GET_EDID_BASE_BLOCK = 26, + D3DKMT_ESCAPE_SOFTGPU_ENABLE_DISABLE_HMD = 27, + D3DKMT_ESCAPE_PROCESS_VERIFIER_OPTION = 28, + D3DKMT_ESCAPE_ADAPTER_VERIFIER_OPTION = 29, + D3DKMT_ESCAPE_IDD_REQUEST = 30, // since WDDM2_1 + D3DKMT_ESCAPE_DOD_SET_DIRTYRECT_MODE = 31, + D3DKMT_ESCAPE_LOG_CODEPOINT_PACKET = 32, + D3DKMT_ESCAPE_LOG_USERMODE_DAIG_PACKET = 33, // since WDDM2_2 + D3DKMT_ESCAPE_GET_EXTERNAL_DIAGNOSTICS = 34, + // unused (35 previously was D3DKMT_ESCAPE_GET_PREFERRED_MODE) + D3DKMT_ESCAPE_GET_DISPLAY_CONFIGURATIONS = 36, // since WDDM2_3 + D3DKMT_ESCAPE_QUERY_IOMMU_STATUS = 37, // since WDDM2_4 + D3DKMT_ESCAPE_CCD_DATABASE = 38, // since WDDM2_6 + + D3DKMT_ESCAPE_WIN32K_START = 1024, + D3DKMT_ESCAPE_WIN32K_HIP_DEVICE_INFO = 1024, + D3DKMT_ESCAPE_WIN32K_QUERY_CD_ROTATION_BLOCK = 1025, + D3DKMT_ESCAPE_WIN32K_DPI_INFO = 1026, // Use hContext for the desired hdev // since WDDM1_3 + D3DKMT_ESCAPE_WIN32K_PRESENTER_VIEW_INFO = 1027, + D3DKMT_ESCAPE_WIN32K_SYSTEM_DPI = 1028, + D3DKMT_ESCAPE_WIN32K_BDD_FALLBACK = 1029, // since WDDM2_0 + D3DKMT_ESCAPE_WIN32K_DDA_TEST_CTL = 1030, + D3DKMT_ESCAPE_WIN32K_USER_DETECTED_BLACK_SCREEN = 1031, + D3DKMT_ESCAPE_WIN32K_HMD_ENUM = 1032, + D3DKMT_ESCAPE_WIN32K_HMD_CONTROL = 1033, + D3DKMT_ESCAPE_WIN32K_LPMDISPLAY_CONTROL = 1034, +} D3DKMT_ESCAPETYPE; + +typedef enum _D3DKMT_VIDMMESCAPETYPE +{ + D3DKMT_VIDMMESCAPETYPE_SETFAULT = 0, + D3DKMT_VIDMMESCAPETYPE_RUN_COHERENCY_TEST = 1, + D3DKMT_VIDMMESCAPETYPE_RUN_UNMAP_TO_DUMMY_PAGE_TEST = 2, + D3DKMT_VIDMMESCAPETYPE_APERTURE_CORRUPTION_CHECK = 3, + D3DKMT_VIDMMESCAPETYPE_SUSPEND_CPU_ACCESS_TEST = 4, + D3DKMT_VIDMMESCAPETYPE_EVICT = 5, + D3DKMT_VIDMMESCAPETYPE_EVICT_BY_NT_HANDLE = 6, + D3DKMT_VIDMMESCAPETYPE_GET_VAD_INFO = 7, + D3DKMT_VIDMMESCAPETYPE_SET_BUDGET = 8, + D3DKMT_VIDMMESCAPETYPE_SUSPEND_PROCESS = 9, + D3DKMT_VIDMMESCAPETYPE_RESUME_PROCESS = 10, + D3DKMT_VIDMMESCAPETYPE_GET_BUDGET = 11, + D3DKMT_VIDMMESCAPETYPE_SET_TRIM_INTERVALS = 12, + D3DKMT_VIDMMESCAPETYPE_EVICT_BY_CRITERIA = 13, + D3DKMT_VIDMMESCAPETYPE_WAKE = 14, + D3DKMT_VIDMMESCAPETYPE_DEFRAG = 15, +} D3DKMT_VIDMMESCAPETYPE; + +typedef struct _D3DKMT_VAD_DESC +{ + _In_ UINT32 VadIndex; // 0xFFFFFFFF to use the VAD address + _In_ UINT64 VadAddress; + _Out_ UINT32 NumMappedRanges; + _Out_ UINT32 VadType; // 0 - reserved, 1 - Mapped + _Out_ UINT64 StartAddress; + _Out_ UINT64 EndAddress; +} D3DKMT_VAD_DESC; + +typedef struct _D3DKMT_VA_RANGE_DESC +{ + _In_ UINT64 VadAddress; + _In_ UINT32 VaRangeIndex; + _In_ UINT32 PhysicalAdapterIndex; + _Out_ UINT64 StartAddress; + _Out_ UINT64 EndAddress; + _Out_ UINT64 DriverProtection; + _Out_ UINT32 OwnerType; // VIDMM_VAD_OWNER_TYPE + _Out_ UINT64 pOwner; + _Out_ UINT64 OwnerOffset; + _Out_ UINT32 Protection; // D3DDDIGPUVIRTUALADDRESS_PROTECTION_TYPE +} D3DKMT_VA_RANGE_DESC; + +typedef struct _D3DKMT_PAGE_TABLE_LEVEL_DESC +{ + UINT32 IndexBitCount; + UINT64 IndexMask; + UINT64 IndexShift; + UINT64 LowerLevelsMask; + UINT64 EntryCoverageInPages; +} D3DKMT_PAGE_TABLE_LEVEL_DESC; + +#define DXGK_MAX_PAGE_TABLE_LEVEL_COUNT 6 +#define DXGK_MIN_PAGE_TABLE_LEVEL_COUNT 2 + +typedef struct _DXGK_ESCAPE_GPUMMUCAPS +{ + BOOLEAN ReadOnlyMemorySupported; + BOOLEAN NoExecuteMemorySupported; + BOOLEAN ZeroInPteSupported; + BOOLEAN CacheCoherentMemorySupported; + BOOLEAN LargePageSupported; + BOOLEAN DualPteSupported; + BOOLEAN AllowNonAlignedLargePageAddress; + UINT32 VirtualAddressBitCount; + UINT32 PageTableLevelCount; + D3DKMT_PAGE_TABLE_LEVEL_DESC PageTableLevelDesk[DXGK_MAX_PAGE_TABLE_LEVEL_COUNT]; +} DXGK_ESCAPE_GPUMMUCAPS; + +typedef struct _D3DKMT_GET_GPUMMU_CAPS +{ + UINT32 PhysicalAdapterIndex; // In + DXGK_ESCAPE_GPUMMUCAPS GpuMmuCaps; // Out +} D3DKMT_GET_GPUMMU_CAPS; + +typedef enum _DXGK_PTE_PAGE_SIZE +{ + DXGK_PTE_PAGE_TABLE_PAGE_4KB = 0, + DXGK_PTE_PAGE_TABLE_PAGE_64KB = 1, +} DXGK_PTE_PAGE_SIZE; + +// Page Table Entry structure. Contains segment/physical address pointing to a page +typedef struct _DXGK_PTE +{ + union + { + struct + { + ULONGLONG Valid : 1; + ULONGLONG Zero : 1; + ULONGLONG CacheCoherent : 1; + ULONGLONG ReadOnly : 1; + ULONGLONG NoExecute : 1; + ULONGLONG Segment : 5; + ULONGLONG LargePage : 1; + ULONGLONG PhysicalAdapterIndex : 6; + ULONGLONG PageTablePageSize : 2; // DXGK_PTE_PAGE_SIZE + ULONGLONG SystemReserved0 : 1; + ULONGLONG Reserved : 44; + }; + ULONGLONG Flags; + }; + union + { + ULONGLONG PageAddress; // High 52 bits of 64 bit physical address. Low 12 bits are zero. + ULONGLONG PageTableAddress; // High 52 bits of 64 bit physical address. Low 12 bits are zero. + }; +} DXGK_PTE; + +#define D3DKMT_GET_PTE_MAX 64 + +typedef struct _D3DKMT_GET_PTE +{ + _In_ UINT32 PhysicalAdapterIndex; + _In_ UINT32 PageTableLevel; + _In_ UINT32 PageTableIndex[DXGK_MAX_PAGE_TABLE_LEVEL_COUNT]; + _In_ BOOLEAN b64KBPte; // Valid only when dual PTEs are supported. Out - PT is 64KB. + _In_ UINT32 NumPtes; // Number of PTEs to fill. Out - number of filled PTEs + _Out_ DXGK_PTE Pte[D3DKMT_GET_PTE_MAX]; + _Out_ UINT32 NumValidEntries; +} D3DKMT_GET_PTE; + +#define D3DKMT_MAX_SEGMENT_COUNT 32 + +typedef struct _D3DKMT_SEGMENT_CAPS +{ + UINT64 Size; + UINT32 PageSize; + ULONG SegmentId; + BOOLEAN bAperture; + BOOLEAN bReservedSysMem; + D3DKMT_MEMORY_SEGMENT_GROUP BudgetGroup; +} D3DKMT_SEGMENT_CAPS; + +typedef struct _D3DKMT_GET_SEGMENT_CAPS +{ + _In_ UINT32 PhysicalAdapterIndex; + _Out_ UINT32 NumSegments; + _Out_ D3DKMT_SEGMENT_CAPS SegmentCaps[D3DKMT_MAX_SEGMENT_COUNT]; +} D3DKMT_GET_SEGMENT_CAPS; + +typedef struct _D3DKMT_EVICTION_CRITERIA +{ + UINT64 MinimumSize; + UINT64 MaximumSize; + struct + { + union + { + struct + { + UINT32 Primary : 1; + UINT32 Reserved : 31; + } Flags; + UINT32 Value; + }; + }; +} D3DKMT_EVICTION_CRITERIA; + +typedef enum _D3DKMT_VAD_ESCAPE_COMMAND +{ + D3DKMT_VAD_ESCAPE_GETNUMVADS, + D3DKMT_VAD_ESCAPE_GETVAD, + D3DKMT_VAD_ESCAPE_GETVADRANGE, + D3DKMT_VAD_ESCAPE_GET_PTE, + D3DKMT_VAD_ESCAPE_GET_GPUMMU_CAPS, + D3DKMT_VAD_ESCAPE_GET_SEGMENT_CAPS, +} D3DKMT_VAD_ESCAPE_COMMAND; + +typedef enum _D3DKMT_DEFRAG_ESCAPE_OPERATION +{ + D3DKMT_DEFRAG_ESCAPE_GET_FRAGMENTATION_STATS = 0, + D3DKMT_DEFRAG_ESCAPE_DEFRAG_UPWARD = 1, + D3DKMT_DEFRAG_ESCAPE_DEFRAG_DOWNWARD = 2, + D3DKMT_DEFRAG_ESCAPE_DEFRAG_PASS = 3, + D3DKMT_DEFRAG_ESCAPE_VERIFY_TRANSFER = 4, +} D3DKMT_DEFRAG_ESCAPE_OPERATION; + +typedef struct _D3DKMT_VIDMM_ESCAPE +{ + D3DKMT_VIDMMESCAPETYPE Type; + union + { + struct + { + union + { + struct + { + ULONG ProbeAndLock : 1; + ULONG SplitPoint : 1; + ULONG NoDemotion : 1; + ULONG SwizzlingAperture : 1; + ULONG PagingPathLockSubRange : 1; + ULONG PagingPathLockMinRange : 1; + ULONG ComplexLock : 1; + ULONG FailVARotation : 1; + ULONG NoWriteCombined : 1; + ULONG NoPrePatching : 1; + ULONG AlwaysRepatch : 1; + ULONG ExpectPreparationFailure : 1; + ULONG FailUserModeVAMapping : 1; + ULONG NeverDiscardOfferedAllocation : 1; // since WIN8 + ULONG AlwaysDiscardOfferedAllocation : 1; + ULONG Reserved : 17; + }; + ULONG Value; + }; + } SetFault; + struct + { + D3DKMT_HANDLE ResourceHandle; + D3DKMT_HANDLE AllocationHandle; + HANDLE hProcess; // 0 to evict memory for the current process, otherwise it is a process handle from OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId). + } Evict; + struct + { + UINT64 NtHandle; // Used by D3DKMT_VIDMMESCAPETYPE_EVICT_BY_NT_HANDLE + } EvictByNtHandle; + struct + { + union + { + struct + { + UINT32 NumVads; + } GetNumVads; + D3DKMT_VAD_DESC GetVad; + D3DKMT_VA_RANGE_DESC GetVadRange; + D3DKMT_GET_GPUMMU_CAPS GetGpuMmuCaps; + D3DKMT_GET_PTE GetPte; + D3DKMT_GET_SEGMENT_CAPS GetSegmentCaps; + }; + _In_ D3DKMT_VAD_ESCAPE_COMMAND Command; + _Out_ NTSTATUS Status; + } GetVads; + struct + { + ULONGLONG LocalMemoryBudget; + ULONGLONG SystemMemoryBudget; + } SetBudget; + struct + { + HANDLE hProcess; + BOOL bAllowWakeOnSubmission; + } SuspendProcess; + struct + { + HANDLE hProcess; + } ResumeProcess; + struct + { + UINT64 NumBytesToTrim; + } GetBudget; + struct + { + ULONG MinTrimInterval; // In 100ns units + ULONG MaxTrimInterval; // In 100ns units + ULONG IdleTrimInterval; // In 100ns units + } SetTrimIntervals; + D3DKMT_EVICTION_CRITERIA EvictByCriteria; + struct + { + BOOL bFlush; + } Wake; + struct + { + D3DKMT_DEFRAG_ESCAPE_OPERATION Operation; + UINT32 SegmentId; + ULONGLONG TotalCommitted; + ULONGLONG TotalFree; + ULONGLONG LargestGapBefore; + ULONGLONG LargestGapAfter; + } Defrag; + }; +} D3DKMT_VIDMM_ESCAPE; + +typedef enum _D3DKMT_TDRDBGCTRLTYPE +{ + D3DKMT_TDRDBGCTRLTYPE_FORCETDR = 0, // Simulate a TDR + D3DKMT_TDRDBGCTRLTYPE_DISABLEBREAK = 1, // Disable DebugBreak on timeout + D3DKMT_TDRDBGCTRLTYPE_ENABLEBREAK = 2, // Enable DebugBreak on timeout + D3DKMT_TDRDBGCTRLTYPE_UNCONDITIONAL = 3, // Disables all safety conditions (e.g. check for consecutive recoveries) + D3DKMT_TDRDBGCTRLTYPE_VSYNCTDR = 4, // Simulate a Vsync TDR + D3DKMT_TDRDBGCTRLTYPE_GPUTDR = 5, // Simulate a GPU TDR + D3DKMT_TDRDBGCTRLTYPE_FORCEDODTDR = 6, // Simulate a Display Only Present TDR // since WIN8 + D3DKMT_TDRDBGCTRLTYPE_FORCEDODVSYNCTDR = 7, // Simulate a Display Only Vsync TDR + D3DKMT_TDRDBGCTRLTYPE_ENGINETDR = 8, // Simulate an engine TDR +} D3DKMT_TDRDBGCTRLTYPE; + +typedef enum _D3DKMT_VIDSCHESCAPETYPE +{ + D3DKMT_VIDSCHESCAPETYPE_PREEMPTIONCONTROL = 0, // Enable/Disable preemption + D3DKMT_VIDSCHESCAPETYPE_SUSPENDSCHEDULER = 1, // Suspend/Resume scheduler (obsolate) + D3DKMT_VIDSCHESCAPETYPE_TDRCONTROL = 2, // Tdr control + D3DKMT_VIDSCHESCAPETYPE_SUSPENDRESUME = 3, // Suspend/Resume scheduler + D3DKMT_VIDSCHESCAPETYPE_ENABLECONTEXTDELAY = 4, // Enable/Disable context delay // since WIN8 + D3DKMT_VIDSCHESCAPETYPE_CONFIGURE_TDR_LIMIT = 5, // Configure TdrLimitCount and TdrLimitTime + D3DKMT_VIDSCHESCAPETYPE_VGPU_RESET = 6, // Trigger VGPU reset + D3DKMT_VIDSCHESCAPETYPE_PFN_CONTROL = 7, // Periodic frame notification control +} D3DKMT_VIDSCHESCAPETYPE; + +typedef enum _D3DKMT_ESCAPE_PFN_CONTROL_COMMAND +{ + D3DKMT_ESCAPE_PFN_CONTROL_DEFAULT, + D3DKMT_ESCAPE_PFN_CONTROL_FORCE_CPU, + D3DKMT_ESCAPE_PFN_CONTROL_FORCE_GPU +} D3DKMT_ESCAPE_PFN_CONTROL_COMMAND; + +typedef struct _D3DKMT_VIDSCH_ESCAPE +{ + D3DKMT_VIDSCHESCAPETYPE Type; + union + { + BOOL PreemptionControl; // enable/disable preemption + BOOL EnableContextDelay; // enable/disable context delay // since WIN8 + struct + { + ULONG TdrControl; // control tdr + union + { + ULONG NodeOrdinal; // valid if TdrControl is set to D3DKMT_TDRDBGCTRLTYPE_ENGINETDR + }; + } TdrControl2; + BOOL SuspendScheduler; // suspend/resume scheduler (obsolate) // since Vista + ULONG TdrControl; // control tdr + ULONG SuspendTime; // time period to suspend. + struct + { + UINT Count; + UINT Time; // In seconds + } TdrLimit; + D3DKMT_ESCAPE_PFN_CONTROL_COMMAND PfnControl; // periodic frame notification control + }; +} D3DKMT_VIDSCH_ESCAPE; + +typedef enum _D3DKMT_DEVICEESCAPE_TYPE +{ + D3DKMT_DEVICEESCAPE_VIDPNFROMALLOCATION = 0, + D3DKMT_DEVICEESCAPE_RESTOREGAMMA = 1, +} D3DKMT_DEVICEESCAPE_TYPE; + +typedef struct _D3DKMT_DEVICE_ESCAPE +{ + D3DKMT_DEVICEESCAPE_TYPE Type; + union + { + struct + { + _In_ D3DKMT_HANDLE hPrimaryAllocation; // Primary allocation handle + _Out_ UINT32 VidPnSourceId; // VidPnSoureId of primary allocation + } VidPnFromAllocation; + }; +} D3DKMT_DEVICE_ESCAPE; + +typedef enum _D3DKMT_DMMESCAPETYPE +{ + D3DKMT_DMMESCAPETYPE_UNINITIALIZED = 0, + D3DKMT_DMMESCAPETYPE_GET_SUMMARY_INFO = 1, + D3DKMT_DMMESCAPETYPE_GET_VIDEO_PRESENT_SOURCES_INFO = 2, + D3DKMT_DMMESCAPETYPE_GET_VIDEO_PRESENT_TARGETS_INFO = 3, + D3DKMT_DMMESCAPETYPE_GET_ACTIVEVIDPN_INFO = 4, + D3DKMT_DMMESCAPETYPE_GET_MONITORS_INFO = 5, + D3DKMT_DMMESCAPETYPE_RECENTLY_COMMITTED_VIDPNS_INFO = 6, + D3DKMT_DMMESCAPETYPE_RECENT_MODECHANGE_REQUESTS_INFO = 7, + D3DKMT_DMMESCAPETYPE_RECENTLY_RECOMMENDED_VIDPNS_INFO = 8, + D3DKMT_DMMESCAPETYPE_RECENT_MONITOR_PRESENCE_EVENTS_INFO = 9, + D3DKMT_DMMESCAPETYPE_ACTIVEVIDPN_SOURCEMODESET_INFO = 10, + D3DKMT_DMMESCAPETYPE_ACTIVEVIDPN_COFUNCPATHMODALITY_INFO = 11, + D3DKMT_DMMESCAPETYPE_GET_LASTCLIENTCOMMITTEDVIDPN_INFO = 12, + D3DKMT_DMMESCAPETYPE_GET_VERSION_INFO = 13, + D3DKMT_DMMESCAPETYPE_VIDPN_MGR_DIAGNOSTICS = 14 +} D3DKMT_DMMESCAPETYPE; + +typedef struct _D3DKMT_DMM_ESCAPE +{ + _In_ D3DKMT_DMMESCAPETYPE Type; + _In_ SIZE_T ProvidedBufferSize; // actual size of Data[] array, in bytes. + _Out_ SIZE_T MinRequiredBufferSize; // minimum required size of Data[] array to contain requested data. + _Out_writes_bytes_(ProvidedBufferSize) UCHAR Data[1]; +} D3DKMT_DMM_ESCAPE; + +typedef struct _D3DKMT_DEBUG_SNAPSHOT_ESCAPE +{ + ULONG Length; // out: Actual length of the snapshot written in Buffer + BYTE Buffer[1]; // out: Buffer to place snapshot +} D3DKMT_DEBUG_SNAPSHOT_ESCAPE; + +typedef enum _D3DKMT_ACTIVATE_SPECIFIC_DIAG_TYPE +{ + D3DKMT_ACTIVATE_SPECIFIC_DIAG_TYPE_EXTRA_CCD_DATABASE_INFO = 0, + D3DKMT_ACTIVATE_SPECIFIC_DIAG_TYPE_MODES_PRUNED = 15, +}D3DKMT_ACTIVATE_SPECIFIC_DIAG_TYPE; + +typedef struct _D3DKMT_ACTIVATE_SPECIFIC_DIAG_ESCAPE +{ + D3DKMT_ACTIVATE_SPECIFIC_DIAG_TYPE Type; // The escape type that needs to be (de)activated + BOOL Activate; // FALSE means deactivate +} D3DKMT_ACTIVATE_SPECIFIC_DIAG_ESCAPE; + +typedef struct _D3DKMT_REQUEST_MACHINE_CRASH_ESCAPE +{ + ULONG_PTR Param1; + ULONG_PTR Param2; + ULONG_PTR Param3; +} D3DKMT_REQUEST_MACHINE_CRASH_ESCAPE; + +typedef struct _D3DDDI_ESCAPEFLAGS +{ + union + { + struct + { + UINT32 HardwareAccess : 1; + UINT32 DeviceStatusQuery : 1; // since WDDM1_3 + UINT32 ChangeFrameLatency : 1; + UINT32 NoAdapterSynchronization : 1; // since WDDM2_0 + UINT32 Reserved : 1; // Used internally by DisplayOnly present // since WDDM2_2 + UINT32 VirtualMachineData : 1; // Cannot be set from user mode + UINT32 Reserved2 :26; + }; + UINT32 Value; + }; +} D3DDDI_ESCAPEFLAGS; + +// The D3DKMT_ESCAPE structure describes information that is exchanged with the display miniport driver. +typedef struct _D3DKMT_ESCAPE +{ + _In_ D3DKMT_HANDLE AdapterHandle; + _In_opt_ D3DKMT_HANDLE DeviceHandle; + _In_ D3DKMT_ESCAPETYPE Type; + _In_ D3DDDI_ESCAPEFLAGS Flags; + _Inout_ PVOID PrivateDriverData; + _In_ UINT32 PrivateDriverDataSize; + _In_opt_ D3DKMT_HANDLE ContextHandle; +} D3DKMT_ESCAPE; + +// Function pointers + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTOpenAdapterFromDeviceName( + _Inout_ CONST D3DKMT_OPENADAPTERFROMDEVICENAME *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTOpenAdapterFromGdiDisplayName( + _Inout_ CONST D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTOpenAdapterFromHdc( + _Inout_ CONST D3DKMT_OPENADAPTERFROMHDC *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTOpenAdapterFromLuid( + _Inout_ CONST D3DKMT_OPENADAPTERFROMLUID *pAdapter + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTEnumAdapters( + _Inout_ CONST D3DKMT_ENUMADAPTERS *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTEnumAdapters2( + _Inout_ CONST D3DKMT_ENUMADAPTERS2 *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTCloseAdapter( + _In_ CONST D3DKMT_CLOSEADAPTER *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTQueryAdapterInfo( + _Inout_ CONST D3DKMT_QUERYADAPTERINFO *pData + ); + +// rev +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTQueryStatistics( + _Inout_ CONST D3DKMT_QUERYSTATISTICS *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTQueryVideoMemoryInfo( + _Inout_ CONST D3DKMT_QUERYVIDEOMEMORYINFO *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTEscape( + _Inout_ CONST D3DKMT_ESCAPE *pData + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTSetProcessSchedulingPriorityClass( + _In_ HANDLE, + _In_ enum D3DKMT_SCHEDULINGPRIORITYCLASS + ); + +NTSYSAPI +NTSTATUS +NTAPI +D3DKMTGetProcessSchedulingPriorityClass( + _In_ HANDLE, + _Out_ enum D3DKMT_SCHEDULINGPRIORITYCLASS* + ); + +#endif diff --git a/plugins/ExtendedTools/disktab.c b/plugins/ExtendedTools/disktab.c index 232acf3dd7ca..400b107fb447 100644 --- a/plugins/ExtendedTools/disktab.c +++ b/plugins/ExtendedTools/disktab.c @@ -1,1172 +1,1198 @@ -/* - * Process Hacker Extended Tools - - * ETW disk monitoring - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "etwmon.h" -#include -#include "disktabp.h" - -static PPH_MAIN_TAB_PAGE DiskPage; -static BOOLEAN DiskTreeNewCreated = FALSE; -static HWND DiskTreeNewHandle; -static ULONG DiskTreeNewSortColumn; -static PH_SORT_ORDER DiskTreeNewSortOrder; - -static PPH_HASHTABLE DiskNodeHashtable; // hashtable of all nodes -static PPH_LIST DiskNodeList; // list of all nodes - -static PH_CALLBACK_REGISTRATION DiskItemAddedRegistration; -static PH_CALLBACK_REGISTRATION DiskItemModifiedRegistration; -static PH_CALLBACK_REGISTRATION DiskItemRemovedRegistration; -static PH_CALLBACK_REGISTRATION DiskItemsUpdatedRegistration; -static BOOLEAN DiskNeedsRedraw = FALSE; - -static PH_TN_FILTER_SUPPORT FilterSupport; -static PTOOLSTATUS_INTERFACE ToolStatusInterface; -static PH_CALLBACK_REGISTRATION SearchChangedRegistration; - -VOID EtInitializeDiskTab( - VOID - ) -{ - PH_MAIN_TAB_PAGE page; - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(TOOLSTATUS_PLUGIN_NAME)) - { - ToolStatusInterface = PhGetPluginInformation(toolStatusPlugin)->Interface; - - if (ToolStatusInterface->Version < TOOLSTATUS_INTERFACE_VERSION) - ToolStatusInterface = NULL; - } - - memset(&page, 0, sizeof(PH_MAIN_TAB_PAGE)); - PhInitializeStringRef(&page.Name, L"Disk"); - page.Callback = EtpDiskPageCallback; - DiskPage = ProcessHacker_CreateTabPage(PhMainWndHandle, &page); - - if (ToolStatusInterface) - { - PTOOLSTATUS_TAB_INFO tabInfo; - - tabInfo = ToolStatusInterface->RegisterTabInfo(DiskPage->Index); - tabInfo->BannerText = L"Search Disk"; - tabInfo->ActivateContent = EtpToolStatusActivateContent; - tabInfo->GetTreeNewHandle = EtpToolStatusGetTreeNewHandle; - } -} - -BOOLEAN EtpDiskPageCallback( - _In_ struct _PH_MAIN_TAB_PAGE *Page, - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MainTabPageCreateWindow: - { - HWND hwnd; - - if (EtEtwEnabled) - { - ULONG thinRows; - - thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0; - hwnd = CreateWindow( - PH_TREENEW_CLASSNAME, - NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows, - 0, - 0, - 3, - 3, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - - if (!hwnd) - return FALSE; - } - else - { - *(HWND *)Parameter1 = CreateDialog( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_DISKTABERROR), - PhMainWndHandle, - EtpDiskTabErrorDialogProc - ); - return TRUE; - } - - DiskTreeNewCreated = TRUE; - - DiskNodeHashtable = PhCreateHashtable( - sizeof(PET_DISK_NODE), - EtpDiskNodeHashtableEqualFunction, - EtpDiskNodeHashtableHashFunction, - 100 - ); - DiskNodeList = PhCreateList(100); - - EtInitializeDiskTreeList(hwnd); - - PhRegisterCallback( - &EtDiskItemAddedEvent, - EtpDiskItemAddedHandler, - NULL, - &DiskItemAddedRegistration - ); - PhRegisterCallback( - &EtDiskItemModifiedEvent, - EtpDiskItemModifiedHandler, - NULL, - &DiskItemModifiedRegistration - ); - PhRegisterCallback( - &EtDiskItemRemovedEvent, - EtpDiskItemRemovedHandler, - NULL, - &DiskItemRemovedRegistration - ); - PhRegisterCallback( - &EtDiskItemsUpdatedEvent, - EtpDiskItemsUpdatedHandler, - NULL, - &DiskItemsUpdatedRegistration - ); - - SetCursor(LoadCursor(NULL, IDC_WAIT)); - EtInitializeDiskInformation(); - SetCursor(LoadCursor(NULL, IDC_ARROW)); - - *(HWND *)Parameter1 = hwnd; - } - return TRUE; - case MainTabPageLoadSettings: - { - // Nothing - } - return TRUE; - case MainTabPageSaveSettings: - { - // Nothing - } - return TRUE; - case MainTabPageExportContent: - { - PPH_MAIN_TAB_PAGE_EXPORT_CONTENT exportContent = Parameter1; - - if (!EtEtwEnabled) - return FALSE; - - EtWriteDiskList(exportContent->FileStream, exportContent->Mode); - } - return TRUE; - case MainTabPageFontChanged: - { - HFONT font = (HFONT)Parameter1; - - if (DiskTreeNewHandle) - SendMessage(DiskTreeNewHandle, WM_SETFONT, (WPARAM)Parameter1, TRUE); - } - break; - } - - return FALSE; -} - -BOOLEAN EtpDiskNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PET_DISK_NODE diskNode1 = *(PET_DISK_NODE *)Entry1; - PET_DISK_NODE diskNode2 = *(PET_DISK_NODE *)Entry2; - - return diskNode1->DiskItem == diskNode2->DiskItem; -} - -ULONG EtpDiskNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashIntPtr((ULONG_PTR)(*(PET_DISK_NODE *)Entry)->DiskItem); -} - -VOID EtInitializeDiskTreeList( - _In_ HWND hwnd - ) -{ - DiskTreeNewHandle = hwnd; - PhSetControlTheme(DiskTreeNewHandle, L"explorer"); - SendMessage(TreeNew_GetTooltips(DiskTreeNewHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, 0x7fff); - - TreeNew_SetCallback(hwnd, EtpDiskTreeNewCallback, NULL); - - TreeNew_SetRedraw(hwnd, FALSE); - - // Default columns - PhAddTreeNewColumn(hwnd, ETDSTNC_NAME, TRUE, L"Name", 100, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumn(hwnd, ETDSTNC_FILE, TRUE, L"File", 400, PH_ALIGN_LEFT, 1, DT_PATH_ELLIPSIS); - PhAddTreeNewColumnEx(hwnd, ETDSTNC_READRATEAVERAGE, TRUE, L"Read rate average", 70, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, ETDSTNC_WRITERATEAVERAGE, TRUE, L"Write rate average", 70, PH_ALIGN_RIGHT, 3, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, ETDSTNC_TOTALRATEAVERAGE, TRUE, L"Total rate average", 70, PH_ALIGN_RIGHT, 4, DT_RIGHT, TRUE); - PhAddTreeNewColumnEx(hwnd, ETDSTNC_IOPRIORITY, TRUE, L"I/O priority", 70, PH_ALIGN_LEFT, 5, 0, TRUE); - PhAddTreeNewColumnEx(hwnd, ETDSTNC_RESPONSETIME, TRUE, L"Response time (ms)", 70, PH_ALIGN_RIGHT, 6, 0, TRUE); - - TreeNew_SetRedraw(hwnd, TRUE); - - TreeNew_SetSort(hwnd, ETDSTNC_TOTALRATEAVERAGE, DescendingSortOrder); - - EtLoadSettingsDiskTreeList(); - - PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, DiskNodeList); - - if (ToolStatusInterface) - { - PhRegisterCallback(ToolStatusInterface->SearchChangedEvent, EtpSearchChangedHandler, NULL, &SearchChangedRegistration); - PhAddTreeNewFilter(&FilterSupport, EtpSearchDiskListFilterCallback, NULL); - } -} - -VOID EtLoadSettingsDiskTreeList( - VOID - ) -{ - PH_INTEGER_PAIR sortSettings; - - PhCmLoadSettings(DiskTreeNewHandle, &PhaGetStringSetting(SETTING_NAME_DISK_TREE_LIST_COLUMNS)->sr); - - sortSettings = PhGetIntegerPairSetting(SETTING_NAME_DISK_TREE_LIST_SORT); - TreeNew_SetSort(DiskTreeNewHandle, (ULONG)sortSettings.X, (PH_SORT_ORDER)sortSettings.Y); -} - -VOID EtSaveSettingsDiskTreeList( - VOID - ) -{ - PPH_STRING settings; - PH_INTEGER_PAIR sortSettings; - ULONG sortColumn; - PH_SORT_ORDER sortOrder; - - if (!DiskTreeNewCreated) - return; - - settings = PH_AUTO(PhCmSaveSettings(DiskTreeNewHandle)); - PhSetStringSetting2(SETTING_NAME_DISK_TREE_LIST_COLUMNS, &settings->sr); - - TreeNew_GetSort(DiskTreeNewHandle, &sortColumn, &sortOrder); - sortSettings.X = sortColumn; - sortSettings.Y = sortOrder; - PhSetIntegerPairSetting(SETTING_NAME_DISK_TREE_LIST_SORT, sortSettings); -} - -PET_DISK_NODE EtAddDiskNode( - _In_ PET_DISK_ITEM DiskItem - ) -{ - PET_DISK_NODE diskNode; - - diskNode = PhAllocate(sizeof(ET_DISK_NODE)); - memset(diskNode, 0, sizeof(ET_DISK_NODE)); - PhInitializeTreeNewNode(&diskNode->Node); - - PhSetReference(&diskNode->DiskItem, DiskItem); - - memset(diskNode->TextCache, 0, sizeof(PH_STRINGREF) * ETDSTNC_MAXIMUM); - diskNode->Node.TextCache = diskNode->TextCache; - diskNode->Node.TextCacheSize = ETDSTNC_MAXIMUM; - - diskNode->ProcessNameText = EtpGetDiskItemProcessName(DiskItem); - - PhAddEntryHashtable(DiskNodeHashtable, &diskNode); - PhAddItemList(DiskNodeList, diskNode); - - if (FilterSupport.NodeList) - diskNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &diskNode->Node); - - TreeNew_NodesStructured(DiskTreeNewHandle); - - return diskNode; -} - -PET_DISK_NODE EtFindDiskNode( - _In_ PET_DISK_ITEM DiskItem - ) -{ - ET_DISK_NODE lookupDiskNode; - PET_DISK_NODE lookupDiskNodePtr = &lookupDiskNode; - PET_DISK_NODE *diskNode; - - lookupDiskNode.DiskItem = DiskItem; - - diskNode = (PET_DISK_NODE *)PhFindEntryHashtable( - DiskNodeHashtable, - &lookupDiskNodePtr - ); - - if (diskNode) - return *diskNode; - else - return NULL; -} - -VOID EtRemoveDiskNode( - _In_ PET_DISK_NODE DiskNode - ) -{ - ULONG index; - - // Remove from the hashtable/list and cleanup. - - PhRemoveEntryHashtable(DiskNodeHashtable, &DiskNode); - - if ((index = PhFindItemList(DiskNodeList, DiskNode)) != -1) - PhRemoveItemList(DiskNodeList, index); - - if (DiskNode->ProcessNameText) PhDereferenceObject(DiskNode->ProcessNameText); - if (DiskNode->ReadRateAverageText) PhDereferenceObject(DiskNode->ReadRateAverageText); - if (DiskNode->WriteRateAverageText) PhDereferenceObject(DiskNode->WriteRateAverageText); - if (DiskNode->TotalRateAverageText) PhDereferenceObject(DiskNode->TotalRateAverageText); - if (DiskNode->ResponseTimeText) PhDereferenceObject(DiskNode->ResponseTimeText); - if (DiskNode->TooltipText) PhDereferenceObject(DiskNode->TooltipText); - - PhDereferenceObject(DiskNode->DiskItem); - - PhFree(DiskNode); - - TreeNew_NodesStructured(DiskTreeNewHandle); -} - -VOID EtUpdateDiskNode( - _In_ PET_DISK_NODE DiskNode - ) -{ - memset(DiskNode->TextCache, 0, sizeof(PH_STRINGREF) * ETDSTNC_MAXIMUM); - - PhInvalidateTreeNewNode(&DiskNode->Node, TN_CACHE_ICON); - TreeNew_NodesStructured(DiskTreeNewHandle); -} - -#define SORT_FUNCTION(Column) EtpDiskTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl EtpDiskTreeNewCompare##Column( \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PET_DISK_NODE node1 = *(PET_DISK_NODE *)_elem1; \ - PET_DISK_NODE node2 = *(PET_DISK_NODE *)_elem2; \ - PET_DISK_ITEM diskItem1 = node1->DiskItem; \ - PET_DISK_ITEM diskItem2 = node2->DiskItem; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = PhCompareString(diskItem1->FileNameWin32, diskItem2->FileNameWin32, TRUE); \ - \ - return PhModifySort(sortResult, DiskTreeNewSortOrder); \ -} - -BEGIN_SORT_FUNCTION(Process) -{ - sortResult = PhCompareString(node1->ProcessNameText, node2->ProcessNameText, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(File) -{ - sortResult = PhCompareString(diskItem1->FileNameWin32, diskItem2->FileNameWin32, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ReadRateAverage) -{ - sortResult = uint64cmp(diskItem1->ReadAverage, diskItem2->ReadAverage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(WriteRateAverage) -{ - sortResult = uint64cmp(diskItem1->WriteAverage, diskItem2->WriteAverage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(TotalRateAverage) -{ - sortResult = uint64cmp(diskItem1->ReadAverage + diskItem1->WriteAverage, diskItem2->ReadAverage + diskItem2->WriteAverage); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(IoPriority) -{ - sortResult = uintcmp(diskItem1->IoPriority, diskItem2->IoPriority); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(ResponseTime) -{ - sortResult = singlecmp(diskItem1->ResponseTimeAverage, diskItem2->ResponseTimeAverage); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI EtpDiskTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PET_DISK_NODE node; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Process), - SORT_FUNCTION(File), - SORT_FUNCTION(ReadRateAverage), - SORT_FUNCTION(WriteRateAverage), - SORT_FUNCTION(TotalRateAverage), - SORT_FUNCTION(IoPriority), - SORT_FUNCTION(ResponseTime) - }; - int (__cdecl *sortFunction)(const void *, const void *); - - if (DiskTreeNewSortColumn < ETDSTNC_MAXIMUM) - sortFunction = sortFunctions[DiskTreeNewSortColumn]; - else - sortFunction = NULL; - - if (sortFunction) - { - qsort(DiskNodeList->Items, DiskNodeList->Count, sizeof(PVOID), sortFunction); - } - - getChildren->Children = (PPH_TREENEW_NODE *)DiskNodeList->Items; - getChildren->NumberOfChildren = DiskNodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - PET_DISK_ITEM diskItem; - - node = (PET_DISK_NODE)getCellText->Node; - diskItem = node->DiskItem; - - switch (getCellText->Id) - { - case ETDSTNC_NAME: - getCellText->Text = node->ProcessNameText->sr; - break; - case ETDSTNC_FILE: - getCellText->Text = diskItem->FileNameWin32->sr; - break; - case ETDSTNC_READRATEAVERAGE: - EtFormatRate(diskItem->ReadAverage, &node->ReadRateAverageText, &getCellText->Text); - break; - case ETDSTNC_WRITERATEAVERAGE: - EtFormatRate(diskItem->WriteAverage, &node->WriteRateAverageText, &getCellText->Text); - break; - case ETDSTNC_TOTALRATEAVERAGE: - EtFormatRate(diskItem->ReadAverage + diskItem->WriteAverage, &node->TotalRateAverageText, &getCellText->Text); - break; - case ETDSTNC_IOPRIORITY: - switch (diskItem->IoPriority) - { - case IoPriorityVeryLow: - PhInitializeStringRef(&getCellText->Text, L"Very Low"); - break; - case IoPriorityLow: - PhInitializeStringRef(&getCellText->Text, L"Low"); - break; - case IoPriorityNormal: - PhInitializeStringRef(&getCellText->Text, L"Normal"); - break; - case IoPriorityHigh: - PhInitializeStringRef(&getCellText->Text, L"High"); - break; - case IoPriorityCritical: - PhInitializeStringRef(&getCellText->Text, L"Critical"); - break; - default: - PhInitializeStringRef(&getCellText->Text, L"Unknown"); - break; - } - break; - case ETDSTNC_RESPONSETIME: - { - PH_FORMAT format; - - PhInitFormatF(&format, diskItem->ResponseTimeAverage, 0); - PhMoveReference(&node->ResponseTimeText, PhFormat(&format, 1, 0)); - getCellText->Text = node->ResponseTimeText->sr; - } - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeIcon: - { - PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; - - node = (PET_DISK_NODE)getNodeIcon->Node; - - if (node->DiskItem->ProcessIcon) - { - getNodeIcon->Icon = node->DiskItem->ProcessIcon->Icon; - } - else - { - PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL); - } - - getNodeIcon->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetCellTooltip: - { - PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; - PPH_PROCESS_NODE processNode; - - node = (PET_DISK_NODE)getCellTooltip->Node; - - if (getCellTooltip->Column->Id != 0) - return FALSE; - - if (!node->TooltipText) - { - if (processNode = PhFindProcessNode(node->DiskItem->ProcessId)) - { - PPH_TREENEW_CALLBACK callback; - PVOID callbackContext; - PPH_TREENEW_COLUMN fixedColumn; - PH_TREENEW_GET_CELL_TOOLTIP fakeGetCellTooltip; - - // HACK: Get the tooltip text by using the treenew callback of the process tree. - if (TreeNew_GetCallback(ProcessTreeNewHandle, &callback, &callbackContext) && - (fixedColumn = TreeNew_GetFixedColumn(ProcessTreeNewHandle))) - { - fakeGetCellTooltip.Flags = 0; - fakeGetCellTooltip.Node = &processNode->Node; - fakeGetCellTooltip.Column = fixedColumn; - fakeGetCellTooltip.Unfolding = FALSE; - PhInitializeEmptyStringRef(&fakeGetCellTooltip.Text); - fakeGetCellTooltip.Font = getCellTooltip->Font; - fakeGetCellTooltip.MaximumWidth = getCellTooltip->MaximumWidth; - - if (callback(ProcessTreeNewHandle, TreeNewGetCellTooltip, &fakeGetCellTooltip, NULL, callbackContext)) - { - node->TooltipText = PhCreateString2(&fakeGetCellTooltip.Text); - } - } - } - } - - if (!PhIsNullOrEmptyString(node->TooltipText)) - { - getCellTooltip->Text = node->TooltipText->sr; - getCellTooltip->Unfolding = FALSE; - getCellTooltip->MaximumWidth = -1; - } - else - { - return FALSE; - } - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &DiskTreeNewSortColumn, &DiskTreeNewSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - EtHandleDiskCommand(ID_DISK_COPY); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - TreeNew_SelectRange(DiskTreeNewHandle, 0, -1); - break; - case VK_RETURN: - EtHandleDiskCommand(ID_DISK_OPENFILELOCATION); - break; - } - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = AscendingSortOrder; - PhInitializeTreeNewColumnMenu(&data); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewLeftDoubleClick: - { - EtHandleDiskCommand(ID_DISK_OPENFILELOCATION); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; - - EtShowDiskContextMenu(mouseEvent->Location); - } - return TRUE; - case TreeNewDestroying: - { - EtSaveSettingsDiskTreeList(); - } - return TRUE; - } - - return FALSE; -} - -PPH_STRING EtpGetDiskItemProcessName( - _In_ PET_DISK_ITEM DiskItem - ) -{ - PH_FORMAT format[4]; - - if (!DiskItem->ProcessId) - return PhCreateString(L"No process"); - - PhInitFormatS(&format[1], L" ("); - PhInitFormatU(&format[2], HandleToUlong(DiskItem->ProcessId)); - PhInitFormatC(&format[3], ')'); - - if (DiskItem->ProcessName) - PhInitFormatSR(&format[0], DiskItem->ProcessName->sr); - else - PhInitFormatS(&format[0], L"Unknown process"); - - return PhFormat(format, 4, 96); -} - -PET_DISK_ITEM EtGetSelectedDiskItem( - VOID - ) -{ - PET_DISK_ITEM diskItem = NULL; - ULONG i; - - for (i = 0; i < DiskNodeList->Count; i++) - { - PET_DISK_NODE node = DiskNodeList->Items[i]; - - if (node->Node.Selected) - { - diskItem = node->DiskItem; - break; - } - } - - return diskItem; -} - -VOID EtGetSelectedDiskItems( - _Out_ PET_DISK_ITEM **DiskItems, - _Out_ PULONG NumberOfDiskItems - ) -{ - PPH_LIST list; - ULONG i; - - list = PhCreateList(2); - - for (i = 0; i < DiskNodeList->Count; i++) - { - PET_DISK_NODE node = DiskNodeList->Items[i]; - - if (node->Node.Selected) - { - PhAddItemList(list, node->DiskItem); - } - } - - *DiskItems = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); - *NumberOfDiskItems = list->Count; - - PhDereferenceObject(list); -} - -VOID EtDeselectAllDiskNodes( - VOID - ) -{ - TreeNew_DeselectRange(DiskTreeNewHandle, 0, -1); -} - -VOID EtSelectAndEnsureVisibleDiskNode( - _In_ PET_DISK_NODE DiskNode - ) -{ - EtDeselectAllDiskNodes(); - - if (!DiskNode->Node.Visible) - return; - - TreeNew_SetFocusNode(DiskTreeNewHandle, &DiskNode->Node); - TreeNew_SetMarkNode(DiskTreeNewHandle, &DiskNode->Node); - TreeNew_SelectRange(DiskTreeNewHandle, DiskNode->Node.Index, DiskNode->Node.Index); - TreeNew_EnsureVisible(DiskTreeNewHandle, &DiskNode->Node); -} - -VOID EtCopyDiskList( - VOID - ) -{ - PPH_STRING text; - - text = PhGetTreeNewText(DiskTreeNewHandle, 0); - PhSetClipboardString(DiskTreeNewHandle, &text->sr); - PhDereferenceObject(text); -} - -VOID EtWriteDiskList( - _Inout_ PPH_FILE_STREAM FileStream, - _In_ ULONG Mode - ) -{ - PPH_LIST lines; - ULONG i; - - lines = PhGetGenericTreeNewLines(DiskTreeNewHandle, Mode); - - for (i = 0; i < lines->Count; i++) - { - PPH_STRING line; - - line = lines->Items[i]; - PhWriteStringAsUtf8FileStream(FileStream, &line->sr); - PhDereferenceObject(line); - PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); - } - - PhDereferenceObject(lines); -} - -VOID EtHandleDiskCommand( - _In_ ULONG Id - ) -{ - switch (Id) - { - case ID_DISK_GOTOPROCESS: - { - PET_DISK_ITEM diskItem = EtGetSelectedDiskItem(); - PPH_PROCESS_NODE processNode; - - if (diskItem) - { - PhReferenceObject(diskItem); - - if (diskItem->ProcessRecord) - { - // Check if this is really the process that we want, or if it's just a case of PID re-use. - if ((processNode = PhFindProcessNode(diskItem->ProcessId)) && - processNode->ProcessItem->CreateTime.QuadPart == diskItem->ProcessRecord->CreateTime.QuadPart) - { - ProcessHacker_SelectTabPage(PhMainWndHandle, 0); - PhSelectAndEnsureVisibleProcessNode(processNode); - } - else - { - PhShowProcessRecordDialog(PhMainWndHandle, diskItem->ProcessRecord); - } - } - else - { - PhShowError(PhMainWndHandle, L"The process does not exist."); - } - - PhDereferenceObject(diskItem); - } - } - break; - case ID_DISK_OPENFILELOCATION: - { - PET_DISK_ITEM diskItem = EtGetSelectedDiskItem(); - - if (diskItem) - { - PhShellExploreFile(PhMainWndHandle, diskItem->FileNameWin32->Buffer); - } - } - break; - case ID_DISK_COPY: - { - EtCopyDiskList(); - } - break; - case ID_DISK_PROPERTIES: - { - PET_DISK_ITEM diskItem = EtGetSelectedDiskItem(); - - if (diskItem) - { - PhShellProperties(PhMainWndHandle, diskItem->FileNameWin32->Buffer); - } - } - break; - } -} - -VOID EtpInitializeDiskMenu( - _In_ PPH_EMENU Menu, - _In_ PET_DISK_ITEM *DiskItems, - _In_ ULONG NumberOfDiskItems - ) -{ - PPH_EMENU_ITEM item; - - if (NumberOfDiskItems == 0) - { - PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - } - else if (NumberOfDiskItems == 1) - { - PPH_PROCESS_ITEM processItem; - - // If we have a process record and the process has terminated, we can only show - // process properties. - if (DiskItems[0]->ProcessRecord) - { - if (processItem = PhReferenceProcessItemForRecord(DiskItems[0]->ProcessRecord)) - { - PhDereferenceObject(processItem); - } - else - { - if (item = PhFindEMenuItem(Menu, 0, NULL, ID_DISK_GOTOPROCESS)) - { - item->Text = L"Process Properties"; - item->Flags &= ~PH_EMENU_TEXT_OWNED; - } - } - } - } - else - { - PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - PhEnableEMenuItem(Menu, ID_DISK_COPY, TRUE); - } -} - -VOID EtShowDiskContextMenu( - _In_ POINT Location - ) -{ - PET_DISK_ITEM *diskItems; - ULONG numberOfDiskItems; - - EtGetSelectedDiskItems(&diskItems, &numberOfDiskItems); - - if (numberOfDiskItems != 0) - { - PPH_EMENU menu; - PPH_EMENU_ITEM item; - - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_DISK), 0); - PhSetFlagsEMenuItem(menu, ID_DISK_OPENFILELOCATION, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); - - EtpInitializeDiskMenu(menu, diskItems, numberOfDiskItems); - - item = PhShowEMenu( - menu, - PhMainWndHandle, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - Location.x, - Location.y - ); - - if (item) - { - EtHandleDiskCommand(item->Id); - } - - PhDestroyEMenu(menu); - } - - PhFree(diskItems); -} - -VOID NTAPI EtpDiskItemAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PET_DISK_ITEM diskItem = (PET_DISK_ITEM)Parameter; - - PhReferenceObject(diskItem); - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemAdded, diskItem); -} - -VOID NTAPI EtpDiskItemModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemModified, (PET_DISK_ITEM)Parameter); -} - -VOID NTAPI EtpDiskItemRemovedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemRemoved, (PET_DISK_ITEM)Parameter); -} - -VOID NTAPI EtpDiskItemsUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemsUpdated, NULL); -} - -VOID NTAPI EtpOnDiskItemAdded( - _In_ PVOID Parameter - ) -{ - PET_DISK_ITEM diskItem = Parameter; - PET_DISK_NODE diskNode; - - if (!DiskNeedsRedraw) - { - TreeNew_SetRedraw(DiskTreeNewHandle, FALSE); - DiskNeedsRedraw = TRUE; - } - - diskNode = EtAddDiskNode(diskItem); - PhDereferenceObject(diskItem); -} - -VOID NTAPI EtpOnDiskItemModified( - _In_ PVOID Parameter - ) -{ - PET_DISK_ITEM diskItem = Parameter; - - EtUpdateDiskNode(EtFindDiskNode(diskItem)); -} - -VOID NTAPI EtpOnDiskItemRemoved( - _In_ PVOID Parameter - ) -{ - PET_DISK_ITEM diskItem = Parameter; - - if (!DiskNeedsRedraw) - { - TreeNew_SetRedraw(DiskTreeNewHandle, FALSE); - DiskNeedsRedraw = TRUE; - } - - EtRemoveDiskNode(EtFindDiskNode(diskItem)); -} - -VOID NTAPI EtpOnDiskItemsUpdated( - _In_ PVOID Parameter - ) -{ - ULONG i; - - if (DiskNeedsRedraw) - { - TreeNew_SetRedraw(DiskTreeNewHandle, TRUE); - DiskNeedsRedraw = FALSE; - } - - // Text invalidation - - for (i = 0; i < DiskNodeList->Count; i++) - { - PET_DISK_NODE node = DiskNodeList->Items[i]; - - // The name and file name never change, so we don't invalidate that. - memset(&node->TextCache[2], 0, sizeof(PH_STRINGREF) * (ETDSTNC_MAXIMUM - 2)); - // Always get the newest tooltip text from the process tree. - PhClearReference(&node->TooltipText); - } - - InvalidateRect(DiskTreeNewHandle, NULL, FALSE); -} - -VOID NTAPI EtpSearchChangedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (!EtEtwEnabled) - return; - - PhApplyTreeNewFilters(&FilterSupport); -} - -BOOLEAN NTAPI EtpSearchDiskListFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ) -{ - PET_DISK_NODE diskNode = (PET_DISK_NODE)Node; - PTOOLSTATUS_WORD_MATCH wordMatch = ToolStatusInterface->WordMatch; - - if (PhIsNullOrEmptyString(ToolStatusInterface->GetSearchboxText())) - return TRUE; - - if (wordMatch(&diskNode->ProcessNameText->sr)) - return TRUE; - - if (wordMatch(&diskNode->DiskItem->FileNameWin32->sr)) - return TRUE; - - return FALSE; -} - -VOID NTAPI EtpToolStatusActivateContent( - _In_ BOOLEAN Select - ) -{ - SetFocus(DiskTreeNewHandle); - - if (Select) - { - if (TreeNew_GetFlatNodeCount(DiskTreeNewHandle) > 0) - EtSelectAndEnsureVisibleDiskNode((PET_DISK_NODE)TreeNew_GetFlatNode(DiskTreeNewHandle, 0)); - } -} - -HWND NTAPI EtpToolStatusGetTreeNewHandle( - VOID - ) -{ - return DiskTreeNewHandle; -} - -INT_PTR CALLBACK EtpDiskTabErrorDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - if (!PhGetOwnTokenAttributes().Elevated) - { - SendMessage(GetDlgItem(hwndDlg, IDC_RESTART), BCM_SETSHIELD, 0, TRUE); - } - else - { - SetDlgItemText(hwndDlg, IDC_ERROR, L"Unable to start the kernel event tracing session."); - ShowWindow(GetDlgItem(hwndDlg, IDC_RESTART), SW_HIDE); - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_RESTART: - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - - if (PhShellProcessHacker( - PhMainWndHandle, - L"-v -selecttab Disk", - SW_SHOW, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - )) - { - ProcessHacker_Destroy(PhMainWndHandle); - } - else - { - ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); - } - - break; - } - } - break; - case WM_CTLCOLORBTN: - case WM_CTLCOLORSTATIC: - { - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * ETW disk monitoring + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwmon.h" +#include +#include "disktabp.h" + +static PPH_MAIN_TAB_PAGE DiskPage = NULL; +static BOOLEAN DiskTreeNewCreated = FALSE; +static HWND DiskTreeNewHandle = NULL; +static ULONG DiskTreeNewSortColumn = 0; +static PH_SORT_ORDER DiskTreeNewSortOrder = NoSortOrder; + +static PPH_HASHTABLE DiskNodeHashtable = NULL; // hashtable of all nodes +static PPH_LIST DiskNodeList = NULL; // list of all nodes + +static PH_PROVIDER_EVENT_QUEUE EtpDiskEventQueue; +static PH_CALLBACK_REGISTRATION DiskItemAddedRegistration; +static PH_CALLBACK_REGISTRATION DiskItemModifiedRegistration; +static PH_CALLBACK_REGISTRATION DiskItemRemovedRegistration; +static PH_CALLBACK_REGISTRATION DiskItemsUpdatedRegistration; + +static PH_TN_FILTER_SUPPORT FilterSupport; +static PTOOLSTATUS_INTERFACE ToolStatusInterface; +static PH_CALLBACK_REGISTRATION SearchChangedRegistration; + +VOID EtInitializeDiskTab( + VOID + ) +{ + PH_MAIN_TAB_PAGE page; + PPH_PLUGIN toolStatusPlugin; + + if (toolStatusPlugin = PhFindPlugin(TOOLSTATUS_PLUGIN_NAME)) + { + ToolStatusInterface = PhGetPluginInformation(toolStatusPlugin)->Interface; + + if (ToolStatusInterface->Version < TOOLSTATUS_INTERFACE_VERSION) + ToolStatusInterface = NULL; + } + + memset(&page, 0, sizeof(PH_MAIN_TAB_PAGE)); + PhInitializeStringRef(&page.Name, L"Disk"); + page.Callback = EtpDiskPageCallback; + DiskPage = ProcessHacker_CreateTabPage(PhMainWndHandle, &page); + + if (ToolStatusInterface) + { + PTOOLSTATUS_TAB_INFO tabInfo; + + tabInfo = ToolStatusInterface->RegisterTabInfo(DiskPage->Index); + tabInfo->BannerText = L"Search Disk"; + tabInfo->ActivateContent = EtpToolStatusActivateContent; + tabInfo->GetTreeNewHandle = EtpToolStatusGetTreeNewHandle; + } +} + +BOOLEAN EtpDiskPageCallback( + _In_ struct _PH_MAIN_TAB_PAGE *Page, + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MainTabPageCreateWindow: + { + HWND hwnd; + + if (EtEtwEnabled) + { + ULONG thinRows; + ULONG treelistBorder; + + thinRows = PhGetIntegerSetting(L"ThinRows") ? TN_STYLE_THIN_ROWS : 0; + treelistBorder = PhGetIntegerSetting(L"TreeListBorderEnable") ? WS_BORDER : 0; + hwnd = CreateWindow( + PH_TREENEW_CLASSNAME, + NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TN_STYLE_ICONS | TN_STYLE_DOUBLE_BUFFERED | thinRows | treelistBorder, + 0, + 0, + 3, + 3, + PhMainWndHandle, + NULL, + NULL, + NULL + ); + + if (!hwnd) + return FALSE; + } + else + { + *(HWND *)Parameter1 = CreateDialog( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_DISKTABERROR), + PhMainWndHandle, + EtpDiskTabErrorDialogProc + ); + return TRUE; + } + + DiskTreeNewCreated = TRUE; + + DiskNodeHashtable = PhCreateHashtable( + sizeof(PET_DISK_NODE), + EtpDiskNodeHashtableEqualFunction, + EtpDiskNodeHashtableHashFunction, + 100 + ); + DiskNodeList = PhCreateList(100); + + EtInitializeDiskTreeList(hwnd); + + PhInitializeProviderEventQueue(&EtpDiskEventQueue, 100); + + PhRegisterCallback( + &EtDiskItemAddedEvent, + EtpDiskItemAddedHandler, + NULL, + &DiskItemAddedRegistration + ); + PhRegisterCallback( + &EtDiskItemModifiedEvent, + EtpDiskItemModifiedHandler, + NULL, + &DiskItemModifiedRegistration + ); + PhRegisterCallback( + &EtDiskItemRemovedEvent, + EtpDiskItemRemovedHandler, + NULL, + &DiskItemRemovedRegistration + ); + PhRegisterCallback( + &EtDiskItemsUpdatedEvent, + EtpDiskItemsUpdatedHandler, + NULL, + &DiskItemsUpdatedRegistration + ); + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + EtInitializeDiskInformation(); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + *(HWND *)Parameter1 = hwnd; + } + return TRUE; + case MainTabPageLoadSettings: + { + // Nothing + } + return TRUE; + case MainTabPageSaveSettings: + { + // Nothing + } + return TRUE; + case MainTabPageSelected: + { + BOOLEAN selected = (BOOLEAN)Parameter1; + + if (selected) + { + EtDiskEnabled = TRUE; + } + else + { + EtDiskEnabled = FALSE; + } + } + break; + case MainTabPageExportContent: + { + PPH_MAIN_TAB_PAGE_EXPORT_CONTENT exportContent = Parameter1; + + if (!EtEtwEnabled) + return FALSE; + + EtWriteDiskList(exportContent->FileStream, exportContent->Mode); + } + return TRUE; + case MainTabPageFontChanged: + { + HFONT font = (HFONT)Parameter1; + + if (DiskTreeNewHandle) + SetWindowFont(DiskTreeNewHandle, font, TRUE); + } + break; + } + + return FALSE; +} + +BOOLEAN EtpDiskNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PET_DISK_NODE diskNode1 = *(PET_DISK_NODE *)Entry1; + PET_DISK_NODE diskNode2 = *(PET_DISK_NODE *)Entry2; + + return diskNode1->DiskItem == diskNode2->DiskItem; +} + +ULONG EtpDiskNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PET_DISK_NODE *)Entry)->DiskItem); +} + +VOID EtInitializeDiskTreeList( + _In_ HWND hwnd + ) +{ + DiskTreeNewHandle = hwnd; + PhSetControlTheme(DiskTreeNewHandle, L"explorer"); + SendMessage(TreeNew_GetTooltips(DiskTreeNewHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, 0x7fff); + + TreeNew_SetCallback(hwnd, EtpDiskTreeNewCallback, NULL); + + TreeNew_SetRedraw(hwnd, FALSE); + + // Default columns + PhAddTreeNewColumn(hwnd, ETDSTNC_NAME, TRUE, L"Name", 100, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(hwnd, ETDSTNC_FILE, TRUE, L"File", 400, PH_ALIGN_LEFT, 1, DT_PATH_ELLIPSIS); + PhAddTreeNewColumnEx(hwnd, ETDSTNC_READRATEAVERAGE, TRUE, L"Read rate average", 70, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, ETDSTNC_WRITERATEAVERAGE, TRUE, L"Write rate average", 70, PH_ALIGN_RIGHT, 3, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, ETDSTNC_TOTALRATEAVERAGE, TRUE, L"Total rate average", 70, PH_ALIGN_RIGHT, 4, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, ETDSTNC_IOPRIORITY, TRUE, L"I/O priority", 70, PH_ALIGN_LEFT, 5, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, ETDSTNC_RESPONSETIME, TRUE, L"Response time (ms)", 70, PH_ALIGN_RIGHT, 6, 0, TRUE); + + TreeNew_SetRedraw(hwnd, TRUE); + + TreeNew_SetSort(hwnd, ETDSTNC_TOTALRATEAVERAGE, DescendingSortOrder); + + EtLoadSettingsDiskTreeList(); + + PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, DiskNodeList); + + if (ToolStatusInterface) + { + PhRegisterCallback(ToolStatusInterface->SearchChangedEvent, EtpSearchChangedHandler, NULL, &SearchChangedRegistration); + PhAddTreeNewFilter(&FilterSupport, EtpSearchDiskListFilterCallback, NULL); + } +} + +VOID EtLoadSettingsDiskTreeList( + VOID + ) +{ + PH_INTEGER_PAIR sortSettings; + + PhCmLoadSettings(DiskTreeNewHandle, &PhaGetStringSetting(SETTING_NAME_DISK_TREE_LIST_COLUMNS)->sr); + + sortSettings = PhGetIntegerPairSetting(SETTING_NAME_DISK_TREE_LIST_SORT); + TreeNew_SetSort(DiskTreeNewHandle, (ULONG)sortSettings.X, (PH_SORT_ORDER)sortSettings.Y); +} + +VOID EtSaveSettingsDiskTreeList( + VOID + ) +{ + PPH_STRING settings; + PH_INTEGER_PAIR sortSettings; + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + if (!DiskTreeNewCreated) + return; + + settings = PH_AUTO(PhCmSaveSettings(DiskTreeNewHandle)); + PhSetStringSetting2(SETTING_NAME_DISK_TREE_LIST_COLUMNS, &settings->sr); + + TreeNew_GetSort(DiskTreeNewHandle, &sortColumn, &sortOrder); + sortSettings.X = sortColumn; + sortSettings.Y = sortOrder; + PhSetIntegerPairSetting(SETTING_NAME_DISK_TREE_LIST_SORT, sortSettings); +} + +PET_DISK_NODE EtAddDiskNode( + _In_ PET_DISK_ITEM DiskItem + ) +{ + PET_DISK_NODE diskNode; + + diskNode = PhAllocate(sizeof(ET_DISK_NODE)); + memset(diskNode, 0, sizeof(ET_DISK_NODE)); + PhInitializeTreeNewNode(&diskNode->Node); + + PhSetReference(&diskNode->DiskItem, DiskItem); + + memset(diskNode->TextCache, 0, sizeof(PH_STRINGREF) * ETDSTNC_MAXIMUM); + diskNode->Node.TextCache = diskNode->TextCache; + diskNode->Node.TextCacheSize = ETDSTNC_MAXIMUM; + + diskNode->ProcessNameText = EtpGetDiskItemProcessName(DiskItem); + + PhAddEntryHashtable(DiskNodeHashtable, &diskNode); + PhAddItemList(DiskNodeList, diskNode); + + if (FilterSupport.NodeList) + diskNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &diskNode->Node); + + TreeNew_NodesStructured(DiskTreeNewHandle); + + return diskNode; +} + +PET_DISK_NODE EtFindDiskNode( + _In_ PET_DISK_ITEM DiskItem + ) +{ + ET_DISK_NODE lookupDiskNode; + PET_DISK_NODE lookupDiskNodePtr = &lookupDiskNode; + PET_DISK_NODE *diskNode; + + lookupDiskNode.DiskItem = DiskItem; + + diskNode = (PET_DISK_NODE *)PhFindEntryHashtable( + DiskNodeHashtable, + &lookupDiskNodePtr + ); + + if (diskNode) + return *diskNode; + else + return NULL; +} + +VOID EtRemoveDiskNode( + _In_ PET_DISK_NODE DiskNode + ) +{ + ULONG index; + + // Remove from the hashtable/list and cleanup. + + PhRemoveEntryHashtable(DiskNodeHashtable, &DiskNode); + + if ((index = PhFindItemList(DiskNodeList, DiskNode)) != ULONG_MAX) + PhRemoveItemList(DiskNodeList, index); + + if (DiskNode->ProcessNameText) PhDereferenceObject(DiskNode->ProcessNameText); + if (DiskNode->ReadRateAverageText) PhDereferenceObject(DiskNode->ReadRateAverageText); + if (DiskNode->WriteRateAverageText) PhDereferenceObject(DiskNode->WriteRateAverageText); + if (DiskNode->TotalRateAverageText) PhDereferenceObject(DiskNode->TotalRateAverageText); + if (DiskNode->ResponseTimeText) PhDereferenceObject(DiskNode->ResponseTimeText); + if (DiskNode->TooltipText) PhDereferenceObject(DiskNode->TooltipText); + + PhDereferenceObject(DiskNode->DiskItem); + + PhFree(DiskNode); + + TreeNew_NodesStructured(DiskTreeNewHandle); +} + +VOID EtUpdateDiskNode( + _In_ PET_DISK_NODE DiskNode + ) +{ + memset(DiskNode->TextCache, 0, sizeof(PH_STRINGREF) * ETDSTNC_MAXIMUM); + + PhClearReference(&DiskNode->TooltipText); + + PhInvalidateTreeNewNode(&DiskNode->Node, TN_CACHE_ICON); + TreeNew_NodesStructured(DiskTreeNewHandle); +} + +VOID EtTickDiskNodes( + VOID + ) +{ + // Text invalidation + + for (ULONG i = 0; i < DiskNodeList->Count; i++) + { + PET_DISK_NODE node = DiskNodeList->Items[i]; + + // The name and file name never change, so we don't invalidate that. + memset(&node->TextCache[2], 0, sizeof(PH_STRINGREF) * (ETDSTNC_MAXIMUM - 2)); + + // Always get the newest tooltip text from the process tree. + PhClearReference(&node->TooltipText); + } + + InvalidateRect(DiskTreeNewHandle, NULL, FALSE); +} + +#define SORT_FUNCTION(Column) EtpDiskTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl EtpDiskTreeNewCompare##Column( \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PET_DISK_NODE node1 = *(PET_DISK_NODE *)_elem1; \ + PET_DISK_NODE node2 = *(PET_DISK_NODE *)_elem2; \ + PET_DISK_ITEM diskItem1 = node1->DiskItem; \ + PET_DISK_ITEM diskItem2 = node2->DiskItem; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + if (sortResult == 0) \ + sortResult = PhCompareString(diskItem1->FileNameWin32, diskItem2->FileNameWin32, TRUE); \ + \ + return PhModifySort(sortResult, DiskTreeNewSortOrder); \ +} + +BEGIN_SORT_FUNCTION(Process) +{ + sortResult = PhCompareString(node1->ProcessNameText, node2->ProcessNameText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(File) +{ + sortResult = PhCompareString(diskItem1->FileNameWin32, diskItem2->FileNameWin32, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ReadRateAverage) +{ + sortResult = uint64cmp(diskItem1->ReadAverage, diskItem2->ReadAverage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(WriteRateAverage) +{ + sortResult = uint64cmp(diskItem1->WriteAverage, diskItem2->WriteAverage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(TotalRateAverage) +{ + sortResult = uint64cmp(diskItem1->ReadAverage + diskItem1->WriteAverage, diskItem2->ReadAverage + diskItem2->WriteAverage); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(IoPriority) +{ + sortResult = uintcmp(diskItem1->IoPriority, diskItem2->IoPriority); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(ResponseTime) +{ + sortResult = singlecmp(diskItem1->ResponseTimeAverage, diskItem2->ResponseTimeAverage); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI EtpDiskTreeNewCallback( + _In_ HWND WindowHandle, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PET_DISK_NODE node; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Process), + SORT_FUNCTION(File), + SORT_FUNCTION(ReadRateAverage), + SORT_FUNCTION(WriteRateAverage), + SORT_FUNCTION(TotalRateAverage), + SORT_FUNCTION(IoPriority), + SORT_FUNCTION(ResponseTime) + }; + int (__cdecl *sortFunction)(const void *, const void *); + + if (DiskTreeNewSortColumn < ETDSTNC_MAXIMUM) + sortFunction = sortFunctions[DiskTreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort(DiskNodeList->Items, DiskNodeList->Count, sizeof(PVOID), sortFunction); + } + + getChildren->Children = (PPH_TREENEW_NODE *)DiskNodeList->Items; + getChildren->NumberOfChildren = DiskNodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + PET_DISK_ITEM diskItem; + + node = (PET_DISK_NODE)getCellText->Node; + diskItem = node->DiskItem; + + switch (getCellText->Id) + { + case ETDSTNC_NAME: + getCellText->Text = PhGetStringRef(node->ProcessNameText); + break; + case ETDSTNC_FILE: + getCellText->Text = PhGetStringRef(diskItem->FileNameWin32); + break; + case ETDSTNC_READRATEAVERAGE: + EtFormatRate(diskItem->ReadAverage, &node->ReadRateAverageText, &getCellText->Text); + break; + case ETDSTNC_WRITERATEAVERAGE: + EtFormatRate(diskItem->WriteAverage, &node->WriteRateAverageText, &getCellText->Text); + break; + case ETDSTNC_TOTALRATEAVERAGE: + EtFormatRate(diskItem->ReadAverage + diskItem->WriteAverage, &node->TotalRateAverageText, &getCellText->Text); + break; + case ETDSTNC_IOPRIORITY: + switch (diskItem->IoPriority) + { + case IoPriorityVeryLow: + PhInitializeStringRef(&getCellText->Text, L"Very Low"); + break; + case IoPriorityLow: + PhInitializeStringRef(&getCellText->Text, L"Low"); + break; + case IoPriorityNormal: + PhInitializeStringRef(&getCellText->Text, L"Normal"); + break; + case IoPriorityHigh: + PhInitializeStringRef(&getCellText->Text, L"High"); + break; + case IoPriorityCritical: + PhInitializeStringRef(&getCellText->Text, L"Critical"); + break; + default: + PhInitializeStringRef(&getCellText->Text, L"Unknown"); + break; + } + break; + case ETDSTNC_RESPONSETIME: + { + PH_FORMAT format; + + PhInitFormatF(&format, diskItem->ResponseTimeAverage, 0); + PhMoveReference(&node->ResponseTimeText, PhFormat(&format, 1, 0)); + getCellText->Text = node->ResponseTimeText->sr; + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeIcon: + { + PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1; + + node = (PET_DISK_NODE)getNodeIcon->Node; + + if (node->DiskItem->ProcessIconValid && node->DiskItem->ProcessIcon) + { + getNodeIcon->Icon = node->DiskItem->ProcessIcon; + } + else + { + PhGetStockApplicationIcon(&getNodeIcon->Icon, NULL); + } + + getNodeIcon->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetCellTooltip: + { + PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1; + PPH_PROCESS_NODE processNode; + + node = (PET_DISK_NODE)getCellTooltip->Node; + + if (getCellTooltip->Column->Id != 0) + return FALSE; + + if (!node->TooltipText) + { + if (processNode = PhFindProcessNode(node->DiskItem->ProcessId)) + { + PPH_TREENEW_CALLBACK callback; + PVOID callbackContext; + PPH_TREENEW_COLUMN fixedColumn; + PH_TREENEW_GET_CELL_TOOLTIP fakeGetCellTooltip; + + // HACK: Get the tooltip text by using the treenew callback of the process tree. + if (TreeNew_GetCallback(ProcessTreeNewHandle, &callback, &callbackContext) && + (fixedColumn = TreeNew_GetFixedColumn(ProcessTreeNewHandle))) + { + fakeGetCellTooltip.Flags = 0; + fakeGetCellTooltip.Node = &processNode->Node; + fakeGetCellTooltip.Column = fixedColumn; + fakeGetCellTooltip.Unfolding = FALSE; + PhInitializeEmptyStringRef(&fakeGetCellTooltip.Text); + fakeGetCellTooltip.Font = getCellTooltip->Font; + fakeGetCellTooltip.MaximumWidth = getCellTooltip->MaximumWidth; + + if (callback(ProcessTreeNewHandle, TreeNewGetCellTooltip, &fakeGetCellTooltip, NULL, callbackContext)) + { + node->TooltipText = PhCreateString2(&fakeGetCellTooltip.Text); + } + } + } + } + + if (!PhIsNullOrEmptyString(node->TooltipText)) + { + getCellTooltip->Text = node->TooltipText->sr; + getCellTooltip->Unfolding = FALSE; + getCellTooltip->MaximumWidth = ULONG_MAX; + } + else + { + return FALSE; + } + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(WindowHandle, &DiskTreeNewSortColumn, &DiskTreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(WindowHandle); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + EtHandleDiskCommand(WindowHandle, ID_DISK_COPY); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + TreeNew_SelectRange(DiskTreeNewHandle, 0, -1); + break; + case VK_RETURN: + EtHandleDiskCommand(WindowHandle, ID_DISK_OPENFILELOCATION); + break; + } + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = WindowHandle; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, WindowHandle, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + case TreeNewLeftDoubleClick: + { + EtHandleDiskCommand(WindowHandle, ID_DISK_OPENFILELOCATION); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + EtShowDiskContextMenu(WindowHandle, contextMenuEvent); + } + return TRUE; + case TreeNewDestroying: + { + EtSaveSettingsDiskTreeList(); + } + return TRUE; + } + + return FALSE; +} + +PPH_STRING EtpGetDiskItemProcessName( + _In_ PET_DISK_ITEM DiskItem + ) +{ + PH_FORMAT format[4]; + + if (!DiskItem->ProcessId) + return PhCreateString(L"No process"); + + PhInitFormatS(&format[1], L" ("); + PhInitFormatU(&format[2], HandleToUlong(DiskItem->ProcessId)); + PhInitFormatC(&format[3], ')'); + + if (DiskItem->ProcessName) + PhInitFormatSR(&format[0], DiskItem->ProcessName->sr); + else + PhInitFormatS(&format[0], L"Unknown process"); + + return PhFormat(format, 4, 96); +} + +PET_DISK_ITEM EtGetSelectedDiskItem( + VOID + ) +{ + PET_DISK_ITEM diskItem = NULL; + ULONG i; + + for (i = 0; i < DiskNodeList->Count; i++) + { + PET_DISK_NODE node = DiskNodeList->Items[i]; + + if (node->Node.Selected) + { + diskItem = node->DiskItem; + break; + } + } + + return diskItem; +} + +VOID EtGetSelectedDiskItems( + _Out_ PET_DISK_ITEM **DiskItems, + _Out_ PULONG NumberOfDiskItems + ) +{ + PPH_LIST list; + ULONG i; + + list = PhCreateList(2); + + for (i = 0; i < DiskNodeList->Count; i++) + { + PET_DISK_NODE node = DiskNodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node->DiskItem); + } + } + + *DiskItems = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfDiskItems = list->Count; + + PhDereferenceObject(list); +} + +VOID EtDeselectAllDiskNodes( + VOID + ) +{ + TreeNew_DeselectRange(DiskTreeNewHandle, 0, -1); +} + +VOID EtSelectAndEnsureVisibleDiskNode( + _In_ PET_DISK_NODE DiskNode + ) +{ + EtDeselectAllDiskNodes(); + + if (!DiskNode->Node.Visible) + return; + + TreeNew_SetFocusNode(DiskTreeNewHandle, &DiskNode->Node); + TreeNew_SetMarkNode(DiskTreeNewHandle, &DiskNode->Node); + TreeNew_SelectRange(DiskTreeNewHandle, DiskNode->Node.Index, DiskNode->Node.Index); + TreeNew_EnsureVisible(DiskTreeNewHandle, &DiskNode->Node); +} + +VOID EtCopyDiskList( + VOID + ) +{ + PPH_STRING text; + + text = PhGetTreeNewText(DiskTreeNewHandle, 0); + PhSetClipboardString(DiskTreeNewHandle, &text->sr); + PhDereferenceObject(text); +} + +VOID EtWriteDiskList( + _Inout_ PPH_FILE_STREAM FileStream, + _In_ ULONG Mode + ) +{ + PPH_LIST lines; + ULONG i; + + lines = PhGetGenericTreeNewLines(DiskTreeNewHandle, Mode); + + for (i = 0; i < lines->Count; i++) + { + PPH_STRING line; + + line = lines->Items[i]; + PhWriteStringAsUtf8FileStream(FileStream, &line->sr); + PhDereferenceObject(line); + PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n"); + } + + PhDereferenceObject(lines); +} + +VOID EtHandleDiskCommand( + _In_ HWND WindowHandle, + _In_ ULONG Id + ) +{ + switch (Id) + { + case ID_DISK_GOTOPROCESS: + { + PET_DISK_ITEM diskItem = EtGetSelectedDiskItem(); + PPH_PROCESS_NODE processNode; + + if (diskItem) + { + PhReferenceObject(diskItem); + + if (diskItem->ProcessRecord) + { + // Check if this is really the process that we want, or if it's just a case of PID re-use. + if ((processNode = PhFindProcessNode(diskItem->ProcessId)) && + processNode->ProcessItem->CreateTime.QuadPart == diskItem->ProcessRecord->CreateTime.QuadPart) + { + ProcessHacker_SelectTabPage(PhMainWndHandle, 0); + PhSelectAndEnsureVisibleProcessNode(processNode); + } + else + { + PhShowProcessRecordDialog(PhMainWndHandle, diskItem->ProcessRecord); + } + } + else + { + PhShowError(PhMainWndHandle, L"The process does not exist."); + } + + PhDereferenceObject(diskItem); + } + } + break; + case ID_DISK_OPENFILELOCATION: + { + PET_DISK_ITEM diskItem = EtGetSelectedDiskItem(); + + if (diskItem) + { + PhShellExploreFile(PhMainWndHandle, diskItem->FileNameWin32->Buffer); + } + } + break; + case ID_DISK_COPY: + { + EtCopyDiskList(); + } + break; + case ID_DISK_PROPERTIES: + { + PET_DISK_ITEM diskItem = EtGetSelectedDiskItem(); + + if (diskItem) + { + PhShellProperties(PhMainWndHandle, diskItem->FileNameWin32->Buffer); + } + } + break; + } +} + +VOID EtpInitializeDiskMenu( + _In_ PPH_EMENU Menu, + _In_ PET_DISK_ITEM *DiskItems, + _In_ ULONG NumberOfDiskItems + ) +{ + PPH_EMENU_ITEM item; + + if (NumberOfDiskItems == 0) + { + PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + } + else if (NumberOfDiskItems == 1) + { + PPH_PROCESS_ITEM processItem; + + // If we have a process record and the process has terminated, we can only show + // process properties. + if (DiskItems[0]->ProcessRecord) + { + if (processItem = PhReferenceProcessItemForRecord(DiskItems[0]->ProcessRecord)) + { + PhDereferenceObject(processItem); + } + else + { + if (item = PhFindEMenuItem(Menu, 0, NULL, ID_DISK_GOTOPROCESS)) + { + item->Text = L"Process Properties"; + item->Flags &= ~PH_EMENU_TEXT_OWNED; + } + } + } + } + else + { + PhSetFlagsAllEMenuItems(Menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhEnableEMenuItem(Menu, ID_DISK_COPY, TRUE); + } +} + +VOID EtShowDiskContextMenu( + _In_ HWND TreeWindowHandle, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent + ) +{ + PET_DISK_ITEM *diskItems; + ULONG numberOfDiskItems; + + EtGetSelectedDiskItems(&diskItems, &numberOfDiskItems); + + if (numberOfDiskItems != 0) + { + PPH_EMENU menu; + PPH_EMENU_ITEM item; + + menu = PhCreateEMenu(); + PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_DISK), 0); + PhInsertCopyCellEMenuItem(menu, ID_DISK_COPY, TreeWindowHandle, ContextMenuEvent->Column); + PhSetFlagsEMenuItem(menu, ID_DISK_OPENFILELOCATION, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); + + EtpInitializeDiskMenu(menu, diskItems, numberOfDiskItems); + + item = PhShowEMenu( + menu, + TreeWindowHandle, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + ContextMenuEvent->Location.x, + ContextMenuEvent->Location.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(item); + + if (!handled) + EtHandleDiskCommand(TreeWindowHandle, item->Id); + } + + PhDestroyEMenu(menu); + } + + PhFree(diskItems); +} + +VOID NTAPI EtpDiskItemAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PET_DISK_ITEM diskItem = (PET_DISK_ITEM)Parameter; + + PhReferenceObject(diskItem); + PhPushProviderEventQueue(&EtpDiskEventQueue, ProviderAddedEvent, Parameter, EtRunCount); +} + +VOID NTAPI EtpDiskItemModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PhPushProviderEventQueue(&EtpDiskEventQueue, ProviderModifiedEvent, Parameter, EtRunCount); +} + +VOID NTAPI EtpDiskItemRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PhPushProviderEventQueue(&EtpDiskEventQueue, ProviderRemovedEvent, Parameter, EtRunCount); +} + +VOID NTAPI EtpDiskItemsUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ProcessHacker_Invoke(PhMainWndHandle, EtpOnDiskItemsUpdated, EtRunCount); +} + +VOID NTAPI EtpOnDiskItemsUpdated( + _In_ ULONG RunId + ) +{ + PPH_PROVIDER_EVENT events; + ULONG count; + ULONG i; + + events = PhFlushProviderEventQueue(&EtpDiskEventQueue, RunId, &count); + + if (events) + { + TreeNew_SetRedraw(DiskTreeNewHandle, FALSE); + + for (i = 0; i < count; i++) + { + PH_PROVIDER_EVENT_TYPE type = PH_PROVIDER_EVENT_TYPE(events[i]); + PET_DISK_ITEM diskItem = PH_PROVIDER_EVENT_OBJECT(events[i]); + + switch (type) + { + case ProviderAddedEvent: + EtAddDiskNode(diskItem); + PhDereferenceObject(diskItem); + break; + case ProviderModifiedEvent: + EtUpdateDiskNode(EtFindDiskNode(diskItem)); + break; + case ProviderRemovedEvent: + EtRemoveDiskNode(EtFindDiskNode(diskItem)); + break; + } + } + + PhFree(events); + } + + EtTickDiskNodes(); + + if (count != 0) + TreeNew_SetRedraw(DiskTreeNewHandle, TRUE); +} + +VOID NTAPI EtpSearchChangedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (!EtEtwEnabled) + return; + + PhApplyTreeNewFilters(&FilterSupport); +} + +BOOLEAN NTAPI EtpSearchDiskListFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PET_DISK_NODE diskNode = (PET_DISK_NODE)Node; + PTOOLSTATUS_WORD_MATCH wordMatch = ToolStatusInterface->WordMatch; + + if (PhIsNullOrEmptyString(ToolStatusInterface->GetSearchboxText())) + return TRUE; + + if (wordMatch(&diskNode->ProcessNameText->sr)) + return TRUE; + + if (wordMatch(&diskNode->DiskItem->FileNameWin32->sr)) + return TRUE; + + return FALSE; +} + +VOID NTAPI EtpToolStatusActivateContent( + _In_ BOOLEAN Select + ) +{ + SetFocus(DiskTreeNewHandle); + + if (Select) + { + if (TreeNew_GetFlatNodeCount(DiskTreeNewHandle) > 0) + EtSelectAndEnsureVisibleDiskNode((PET_DISK_NODE)TreeNew_GetFlatNode(DiskTreeNewHandle, 0)); + } +} + +HWND NTAPI EtpToolStatusGetTreeNewHandle( + VOID + ) +{ + return DiskTreeNewHandle; +} + +INT_PTR CALLBACK EtpDiskTabErrorDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + if (!PhGetOwnTokenAttributes().Elevated) + { + Button_SetElevationRequiredState(GetDlgItem(hwndDlg, IDC_RESTART), TRUE); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_ERROR, L"Unable to start the kernel event tracing session."); + ShowWindow(GetDlgItem(hwndDlg, IDC_RESTART), SW_HIDE); + } + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_RESTART: + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + + if (PhShellProcessHacker( + PhMainWndHandle, + L"-v -selecttab Disk", + SW_SHOW, + PH_SHELL_EXECUTE_ADMIN, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, + 0, + NULL + )) + { + ProcessHacker_Destroy(PhMainWndHandle); + } + else + { + ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); + } + + break; + } + } + break; + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: + { + SetBkMode((HDC)wParam, TRANSPARENT); + return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/disktabp.h b/plugins/ExtendedTools/disktabp.h index 23f259290799..85adb65d21c6 100644 --- a/plugins/ExtendedTools/disktabp.h +++ b/plugins/ExtendedTools/disktabp.h @@ -1,174 +1,143 @@ -#ifndef DISKTABP_H -#define DISKTABP_H - -BOOLEAN EtpDiskPageCallback( - _In_ struct _PH_MAIN_TAB_PAGE *Page, - _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID NTAPI EtpDiskTabSelectionChangedCallback( - _In_ PVOID Parameter1, - _In_ PVOID Parameter2, - _In_ PVOID Parameter3, - _In_ PVOID Context - ); - -VOID NTAPI EtpDiskTabSaveContentCallback( - _In_ PVOID Parameter1, - _In_ PVOID Parameter2, - _In_ PVOID Parameter3, - _In_ PVOID Context - ); - -VOID NTAPI EtpDiskTabFontChangedCallback( - _In_ PVOID Parameter1, - _In_ PVOID Parameter2, - _In_ PVOID Parameter3, - _In_ PVOID Context - ); - -BOOLEAN EtpDiskNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG EtpDiskNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID EtInitializeDiskTreeList( - _In_ HWND hwnd - ); - -PET_DISK_NODE EtAddDiskNode( - _In_ PET_DISK_ITEM DiskItem - ); - -PET_DISK_NODE EtFindDiskNode( - _In_ PET_DISK_ITEM DiskItem - ); - -VOID EtRemoveDiskNode( - _In_ PET_DISK_NODE DiskNode - ); - -VOID EtUpdateDiskNode( - _In_ PET_DISK_NODE DiskNode - ); - -BOOLEAN NTAPI EtpDiskTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -PPH_STRING EtpGetDiskItemProcessName( - _In_ PET_DISK_ITEM DiskItem - ); - -PET_DISK_ITEM EtGetSelectedDiskItem( - VOID - ); - -VOID EtGetSelectedDiskItems( - _Out_ PET_DISK_ITEM **DiskItems, - _Out_ PULONG NumberOfDiskItems - ); - -VOID EtDeselectAllDiskNodes( - VOID - ); - -VOID EtSelectAndEnsureVisibleDiskNode( - _In_ PET_DISK_NODE DiskNode - ); - -VOID EtCopyDiskList( - VOID - ); - -VOID EtWriteDiskList( - _Inout_ PPH_FILE_STREAM FileStream, - _In_ ULONG Mode - ); - -VOID EtHandleDiskCommand( - _In_ ULONG Id - ); - -VOID EtpInitializeDiskMenu( - _In_ PPH_EMENU Menu, - _In_ PET_DISK_ITEM *DiskItems, - _In_ ULONG NumberOfDiskItems - ); - -VOID EtShowDiskContextMenu( - _In_ POINT Location - ); - -VOID NTAPI EtpDiskItemAddedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI EtpDiskItemModifiedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI EtpDiskItemRemovedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI EtpDiskItemsUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI EtpOnDiskItemAdded( - _In_ PVOID Parameter - ); - -VOID NTAPI EtpOnDiskItemModified( - _In_ PVOID Parameter - ); - -VOID NTAPI EtpOnDiskItemRemoved( - _In_ PVOID Parameter - ); - -VOID NTAPI EtpOnDiskItemsUpdated( - _In_ PVOID Parameter - ); - -VOID NTAPI EtpSearchChangedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -BOOLEAN NTAPI EtpSearchDiskListFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -VOID NTAPI EtpToolStatusActivateContent( - _In_ BOOLEAN Select - ); - -HWND NTAPI EtpToolStatusGetTreeNewHandle( - VOID - ); - -INT_PTR CALLBACK EtpDiskTabErrorDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -#endif +#ifndef DISKTABP_H +#define DISKTABP_H + +BOOLEAN EtpDiskPageCallback( + _In_ struct _PH_MAIN_TAB_PAGE *Page, + _In_ PH_MAIN_TAB_PAGE_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +BOOLEAN EtpDiskNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG EtpDiskNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID EtInitializeDiskTreeList( + _In_ HWND hwnd + ); + +PET_DISK_NODE EtAddDiskNode( + _In_ PET_DISK_ITEM DiskItem + ); + +PET_DISK_NODE EtFindDiskNode( + _In_ PET_DISK_ITEM DiskItem + ); + +VOID EtRemoveDiskNode( + _In_ PET_DISK_NODE DiskNode + ); + +VOID EtUpdateDiskNode( + _In_ PET_DISK_NODE DiskNode + ); + +BOOLEAN NTAPI EtpDiskTreeNewCallback( + _In_ HWND WindowHandle, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +PPH_STRING EtpGetDiskItemProcessName( + _In_ PET_DISK_ITEM DiskItem + ); + +PET_DISK_ITEM EtGetSelectedDiskItem( + VOID + ); + +VOID EtGetSelectedDiskItems( + _Out_ PET_DISK_ITEM **DiskItems, + _Out_ PULONG NumberOfDiskItems + ); + +VOID EtDeselectAllDiskNodes( + VOID + ); + +VOID EtSelectAndEnsureVisibleDiskNode( + _In_ PET_DISK_NODE DiskNode + ); + +VOID EtCopyDiskList( + VOID + ); + +VOID EtWriteDiskList( + _Inout_ PPH_FILE_STREAM FileStream, + _In_ ULONG Mode + ); + +VOID EtHandleDiskCommand( + _In_ HWND WindowHandle, + _In_ ULONG Id + ); + +VOID EtpInitializeDiskMenu( + _In_ PPH_EMENU Menu, + _In_ PET_DISK_ITEM *DiskItems, + _In_ ULONG NumberOfDiskItems + ); + +VOID EtShowDiskContextMenu( + _In_ HWND TreeWindowHandle, + _In_ PPH_TREENEW_CONTEXT_MENU ContextMenuEvent + ); + +VOID NTAPI EtpDiskItemAddedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI EtpDiskItemModifiedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI EtpDiskItemRemovedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI EtpDiskItemsUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI EtpOnDiskItemsUpdated( + _In_ ULONG RunId + ); + +VOID NTAPI EtpSearchChangedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +BOOLEAN NTAPI EtpSearchDiskListFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +VOID NTAPI EtpToolStatusActivateContent( + _In_ BOOLEAN Select + ); + +HWND NTAPI EtpToolStatusGetTreeNewHandle( + VOID + ); + +INT_PTR CALLBACK EtpDiskTabErrorDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#endif diff --git a/plugins/ExtendedTools/etwdisk.c b/plugins/ExtendedTools/etwdisk.c index 7980ce675e79..237a1d09c3d0 100644 --- a/plugins/ExtendedTools/etwdisk.c +++ b/plugins/ExtendedTools/etwdisk.c @@ -1,536 +1,543 @@ -/* - * Process Hacker Extended Tools - - * ETW disk monitoring - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "etwmon.h" - -typedef struct _ETP_DISK_PACKET -{ - SLIST_ENTRY ListEntry; - ET_ETW_DISK_EVENT Event; - PPH_STRING FileName; -} ETP_DISK_PACKET, *PETP_DISK_PACKET; - -VOID NTAPI EtpDiskItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ); - -BOOLEAN NTAPI EtpDiskHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI EtpDiskHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID NTAPI EtpDiskProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -BOOLEAN EtDiskEnabled = FALSE; - -PPH_OBJECT_TYPE EtDiskItemType; -PPH_HASHTABLE EtDiskHashtable; -PH_QUEUED_LOCK EtDiskHashtableLock = PH_QUEUED_LOCK_INIT; -LIST_ENTRY EtDiskAgeListHead; -PH_CALLBACK_DECLARE(EtDiskItemAddedEvent); -PH_CALLBACK_DECLARE(EtDiskItemModifiedEvent); -PH_CALLBACK_DECLARE(EtDiskItemRemovedEvent); -PH_CALLBACK_DECLARE(EtDiskItemsUpdatedEvent); - -PH_FREE_LIST EtDiskPacketFreeList; -SLIST_HEADER EtDiskPacketListHead; -PPH_HASHTABLE EtFileNameHashtable; -PH_QUEUED_LOCK EtFileNameHashtableLock = PH_QUEUED_LOCK_INIT; - -static LARGE_INTEGER EtpPerformanceFrequency; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; - -VOID EtInitializeDiskInformation( - VOID - ) -{ - LARGE_INTEGER performanceCounter; - - EtDiskItemType = PhCreateObjectType(L"DiskItem", 0, EtpDiskItemDeleteProcedure); - EtDiskHashtable = PhCreateHashtable( - sizeof(PET_DISK_ITEM), - EtpDiskHashtableEqualFunction, - EtpDiskHashtableHashFunction, - 128 - ); - InitializeListHead(&EtDiskAgeListHead); - - PhInitializeFreeList(&EtDiskPacketFreeList, sizeof(ETP_DISK_PACKET), 64); - RtlInitializeSListHead(&EtDiskPacketListHead); - EtFileNameHashtable = PhCreateSimpleHashtable(128); - - NtQueryPerformanceCounter(&performanceCounter, &EtpPerformanceFrequency); - - EtDiskEnabled = TRUE; - - // Collect all existing file names. - EtStartEtwRundown(); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - EtpDiskProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); -} - -PET_DISK_ITEM EtCreateDiskItem( - VOID - ) -{ - PET_DISK_ITEM diskItem; - - diskItem = PhCreateObject(sizeof(ET_DISK_ITEM), EtDiskItemType); - memset(diskItem, 0, sizeof(ET_DISK_ITEM)); - - return diskItem; -} - -VOID NTAPI EtpDiskItemDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PET_DISK_ITEM diskItem = Object; - - if (diskItem->FileName) PhDereferenceObject(diskItem->FileName); - if (diskItem->FileNameWin32) PhDereferenceObject(diskItem->FileNameWin32); - if (diskItem->ProcessName) PhDereferenceObject(diskItem->ProcessName); - if (diskItem->ProcessIcon) EtProcIconDereferenceProcessIcon(diskItem->ProcessIcon); - if (diskItem->ProcessRecord) PhDereferenceProcessRecord(diskItem->ProcessRecord); -} - -BOOLEAN NTAPI EtpDiskHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PET_DISK_ITEM diskItem1 = *(PET_DISK_ITEM *)Entry1; - PET_DISK_ITEM diskItem2 = *(PET_DISK_ITEM *)Entry2; - - return diskItem1->ProcessId == diskItem2->ProcessId && PhEqualString(diskItem1->FileName, diskItem2->FileName, TRUE); -} - -ULONG NTAPI EtpDiskHashtableHashFunction( - _In_ PVOID Entry - ) -{ - PET_DISK_ITEM diskItem = *(PET_DISK_ITEM *)Entry; - - return (HandleToUlong(diskItem->ProcessId) / 4) ^ PhHashStringRef(&diskItem->FileName->sr, TRUE); -} - -PET_DISK_ITEM EtReferenceDiskItem( - _In_ HANDLE ProcessId, - _In_ PPH_STRING FileName - ) -{ - ET_DISK_ITEM lookupDiskItem; - PET_DISK_ITEM lookupDiskItemPtr = &lookupDiskItem; - PET_DISK_ITEM *diskItemPtr; - PET_DISK_ITEM diskItem; - - lookupDiskItem.ProcessId = ProcessId; - lookupDiskItem.FileName = FileName; - - PhAcquireQueuedLockShared(&EtDiskHashtableLock); - - diskItemPtr = (PET_DISK_ITEM *)PhFindEntryHashtable( - EtDiskHashtable, - &lookupDiskItemPtr - ); - - if (diskItemPtr) - PhSetReference(&diskItem, *diskItemPtr); - else - diskItem = NULL; - - PhReleaseQueuedLockShared(&EtDiskHashtableLock); - - return diskItem; -} - -VOID EtpRemoveDiskItem( - _In_ PET_DISK_ITEM DiskItem - ) -{ - RemoveEntryList(&DiskItem->AgeListEntry); - PhRemoveEntryHashtable(EtDiskHashtable, &DiskItem); - PhDereferenceObject(DiskItem); -} - -VOID EtDiskProcessDiskEvent( - _In_ PET_ETW_DISK_EVENT Event - ) -{ - PETP_DISK_PACKET packet; - - if (!EtDiskEnabled) - return; - - packet = PhAllocateFromFreeList(&EtDiskPacketFreeList); - memcpy(&packet->Event, Event, sizeof(ET_ETW_DISK_EVENT)); - packet->FileName = EtFileObjectToFileName(Event->FileObject); - RtlInterlockedPushEntrySList(&EtDiskPacketListHead, &packet->ListEntry); -} - -VOID EtDiskProcessFileEvent( - _In_ PET_ETW_FILE_EVENT Event - ) -{ - PH_KEY_VALUE_PAIR pair; - PPH_KEY_VALUE_PAIR realPair; - - if (!EtDiskEnabled) - return; - - if (Event->Type == EtEtwFileCreateType || Event->Type == EtEtwFileRundownType) - { - pair.Key = Event->FileObject; - pair.Value = NULL; - - PhAcquireQueuedLockExclusive(&EtFileNameHashtableLock); - - realPair = PhAddEntryHashtableEx(EtFileNameHashtable, &pair, NULL); - PhMoveReference(&realPair->Value, PhCreateString2(&Event->FileName)); - - PhReleaseQueuedLockExclusive(&EtFileNameHashtableLock); - } - else if (Event->Type == EtEtwFileDeleteType) - { - pair.Key = Event->FileObject; - - PhAcquireQueuedLockExclusive(&EtFileNameHashtableLock); - - realPair = PhFindEntryHashtable(EtFileNameHashtable, &pair); - - if (realPair) - { - PhDereferenceObject(realPair->Value); - PhRemoveEntryHashtable(EtFileNameHashtable, &pair); - } - - PhReleaseQueuedLockExclusive(&EtFileNameHashtableLock); - } -} - -PPH_STRING EtFileObjectToFileName( - _In_ PVOID FileObject - ) -{ - PH_KEY_VALUE_PAIR pair; - PPH_KEY_VALUE_PAIR realPair; - PPH_STRING fileName; - - pair.Key = FileObject; - fileName = NULL; - - PhAcquireQueuedLockShared(&EtFileNameHashtableLock); - - realPair = PhFindEntryHashtable(EtFileNameHashtable, &pair); - - if (realPair) - PhSetReference(&fileName, realPair->Value); - - PhReleaseQueuedLockShared(&EtFileNameHashtableLock); - - return fileName; -} - -VOID EtpProcessDiskPacket( - _In_ PETP_DISK_PACKET Packet, - _In_ ULONG RunId - ) -{ - PET_ETW_DISK_EVENT diskEvent; - PET_DISK_ITEM diskItem; - BOOLEAN added = FALSE; - - diskEvent = &Packet->Event; - - // We only process non-zero read/write events. - if (diskEvent->Type != EtEtwDiskReadType && diskEvent->Type != EtEtwDiskWriteType) - return; - if (diskEvent->TransferSize == 0) - return; - - // Ignore packets with no file name - this is useless to the user. - if (!Packet->FileName) - return; - - diskItem = EtReferenceDiskItem(diskEvent->ClientId.UniqueProcess, Packet->FileName); - - if (!diskItem) - { - PPH_PROCESS_ITEM processItem; - - // Disk item not found (or the address was re-used), create it. - - diskItem = EtCreateDiskItem(); - - diskItem->ProcessId = diskEvent->ClientId.UniqueProcess; - PhSetReference(&diskItem->FileName, Packet->FileName); - diskItem->FileNameWin32 = PhGetFileName(diskItem->FileName); - - if (processItem = PhReferenceProcessItem(diskItem->ProcessId)) - { - PhSetReference(&diskItem->ProcessName, processItem->ProcessName); - diskItem->ProcessIcon = EtProcIconReferenceSmallProcessIcon(EtGetProcessBlock(processItem)); - diskItem->ProcessRecord = processItem->Record; - PhReferenceProcessRecord(diskItem->ProcessRecord); - - PhDereferenceObject(processItem); - } - - // Add the disk item to the age list. - diskItem->AddTime = RunId; - diskItem->FreshTime = RunId; - InsertHeadList(&EtDiskAgeListHead, &diskItem->AgeListEntry); - - // Add the disk item to the hashtable. - PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); - PhAddEntryHashtable(EtDiskHashtable, &diskItem); - PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); - - // Raise the disk item added event. - PhInvokeCallback(&EtDiskItemAddedEvent, diskItem); - added = TRUE; - } - - // The I/O priority number needs to be decoded. - - diskItem->IoPriority = (diskEvent->IrpFlags >> 17) & 7; - - if (diskItem->IoPriority == 0) - diskItem->IoPriority = IoPriorityNormal; - else - diskItem->IoPriority--; - - // Accumulate statistics for this update period. - - if (diskEvent->Type == EtEtwDiskReadType) - diskItem->ReadDelta += diskEvent->TransferSize; - else - diskItem->WriteDelta += diskEvent->TransferSize; - - if (EtpPerformanceFrequency.QuadPart != 0) - { - // Convert the response time to milliseconds. - diskItem->ResponseTimeTotal += (FLOAT)diskEvent->HighResResponseTime * 1000 / EtpPerformanceFrequency.QuadPart; - diskItem->ResponseTimeCount++; - } - - if (!added) - { - if (diskItem->FreshTime != RunId) - { - diskItem->FreshTime = RunId; - RemoveEntryList(&diskItem->AgeListEntry); - InsertHeadList(&EtDiskAgeListHead, &diskItem->AgeListEntry); - } - - PhDereferenceObject(diskItem); - } -} - -ULONG64 EtpCalculateAverage( - _In_ PULONG64 Buffer, - _In_ ULONG BufferSize, - _In_ ULONG BufferPosition, - _In_ ULONG BufferCount, - _In_ ULONG NumberToConsider - ) -{ - ULONG64 sum; - ULONG i; - ULONG count; - - sum = 0; - i = BufferPosition; - - if (NumberToConsider > BufferCount) - NumberToConsider = BufferCount; - - if (NumberToConsider == 0) - return 0; - - count = NumberToConsider; - - do - { - sum += Buffer[i]; - i++; - - if (i == BufferSize) - i = 0; - } while (--count != 0); - - return sum / NumberToConsider; -} - -VOID NTAPI EtpDiskProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - static ULONG runCount = 0; - - PSLIST_ENTRY listEntry; - PLIST_ENTRY ageListEntry; - - // Process incoming disk event packets. - - listEntry = RtlInterlockedFlushSList(&EtDiskPacketListHead); - - while (listEntry) - { - PETP_DISK_PACKET packet; - - packet = CONTAINING_RECORD(listEntry, ETP_DISK_PACKET, ListEntry); - listEntry = listEntry->Next; - - EtpProcessDiskPacket(packet, runCount); - - if (packet->FileName) - PhDereferenceObject(packet->FileName); - - PhFreeToFreeList(&EtDiskPacketFreeList, packet); - } - - // Remove old entries. - - ageListEntry = EtDiskAgeListHead.Blink; - - while (ageListEntry != &EtDiskAgeListHead) - { - PET_DISK_ITEM diskItem; - - diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); - ageListEntry = ageListEntry->Blink; - - if (runCount - diskItem->FreshTime < HISTORY_SIZE) // must compare like this to avoid overflow/underflow problems - break; - - PhInvokeCallback(&EtDiskItemRemovedEvent, diskItem); - - PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); - EtpRemoveDiskItem(diskItem); - PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); - } - - // Update existing items. - - ageListEntry = EtDiskAgeListHead.Flink; - - while (ageListEntry != &EtDiskAgeListHead) - { - PET_DISK_ITEM diskItem; - - diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); - - // Update statistics. - - if (diskItem->HistoryPosition != 0) - diskItem->HistoryPosition--; - else - diskItem->HistoryPosition = HISTORY_SIZE - 1; - - diskItem->ReadHistory[diskItem->HistoryPosition] = diskItem->ReadDelta; - diskItem->WriteHistory[diskItem->HistoryPosition] = diskItem->WriteDelta; - - if (diskItem->HistoryCount < HISTORY_SIZE) - diskItem->HistoryCount++; - - if (diskItem->ResponseTimeCount != 0) - { - diskItem->ResponseTimeAverage = (FLOAT)diskItem->ResponseTimeTotal / diskItem->ResponseTimeCount; - - // Reset the total once in a while to avoid the number getting too large (and thus losing precision). - if (diskItem->ResponseTimeCount >= 1000) - { - diskItem->ResponseTimeTotal = diskItem->ResponseTimeAverage; - diskItem->ResponseTimeCount = 1; - } - } - - diskItem->ReadTotal += diskItem->ReadDelta; - diskItem->WriteTotal += diskItem->WriteDelta; - diskItem->ReadDelta = 0; - diskItem->WriteDelta = 0; - diskItem->ReadAverage = EtpCalculateAverage(diskItem->ReadHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); - diskItem->WriteAverage = EtpCalculateAverage(diskItem->WriteHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); - - if (diskItem->AddTime != runCount) - { - BOOLEAN modified = FALSE; - PPH_PROCESS_ITEM processItem; - - if (!diskItem->ProcessName || !diskItem->ProcessIcon || !diskItem->ProcessRecord) - { - if (processItem = PhReferenceProcessItem(diskItem->ProcessId)) - { - if (!diskItem->ProcessName) - { - PhSetReference(&diskItem->ProcessName, processItem->ProcessName); - modified = TRUE; - } - - if (!diskItem->ProcessIcon) - { - diskItem->ProcessIcon = EtProcIconReferenceSmallProcessIcon(EtGetProcessBlock(processItem)); - - if (diskItem->ProcessIcon) - modified = TRUE; - } - - if (!diskItem->ProcessRecord) - { - diskItem->ProcessRecord = processItem->Record; - PhReferenceProcessRecord(diskItem->ProcessRecord); - } - - PhDereferenceObject(processItem); - } - } - - if (modified) - { - // Raise the disk item modified event. - PhInvokeCallback(&EtDiskItemModifiedEvent, diskItem); - } - } - - ageListEntry = ageListEntry->Flink; - } - - PhInvokeCallback(&EtDiskItemsUpdatedEvent, NULL); - runCount++; -} +/* + * Process Hacker Extended Tools - + * ETW disk monitoring + * + * Copyright (C) 2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwmon.h" + +typedef struct _ETP_DISK_PACKET +{ + SLIST_ENTRY ListEntry; + ET_ETW_DISK_EVENT Event; + PPH_STRING FileName; +} ETP_DISK_PACKET, *PETP_DISK_PACKET; + +VOID NTAPI EtpDiskItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +BOOLEAN NTAPI EtpDiskHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI EtpDiskHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID NTAPI EtpDiskProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +BOOLEAN EtDiskEnabled = FALSE; +ULONG EtRunCount = 0; + +PPH_OBJECT_TYPE EtDiskItemType = NULL; +PPH_HASHTABLE EtDiskHashtable = NULL; +PH_QUEUED_LOCK EtDiskHashtableLock = PH_QUEUED_LOCK_INIT; +LIST_ENTRY EtDiskAgeListHead = { &EtDiskAgeListHead, &EtDiskAgeListHead }; +PH_CALLBACK_DECLARE(EtDiskItemAddedEvent); +PH_CALLBACK_DECLARE(EtDiskItemModifiedEvent); +PH_CALLBACK_DECLARE(EtDiskItemRemovedEvent); +PH_CALLBACK_DECLARE(EtDiskItemsUpdatedEvent); + +PH_FREE_LIST EtDiskPacketFreeList; +SLIST_HEADER EtDiskPacketListHead; +PPH_HASHTABLE EtFileNameHashtable = NULL; +PH_QUEUED_LOCK EtFileNameHashtableLock = PH_QUEUED_LOCK_INIT; + +static LARGE_INTEGER EtpPerformanceFrequency; +static PH_CALLBACK_REGISTRATION EtpProcessesUpdatedCallbackRegistration; + +VOID EtInitializeDiskInformation( + VOID + ) +{ + LARGE_INTEGER performanceCounter; + + EtDiskItemType = PhCreateObjectType(L"DiskItem", 0, EtpDiskItemDeleteProcedure); + EtDiskHashtable = PhCreateHashtable( + sizeof(PET_DISK_ITEM), + EtpDiskHashtableEqualFunction, + EtpDiskHashtableHashFunction, + 128 + ); + + PhInitializeFreeList(&EtDiskPacketFreeList, sizeof(ETP_DISK_PACKET), 64); + RtlInitializeSListHead(&EtDiskPacketListHead); + EtFileNameHashtable = PhCreateSimpleHashtable(128); + + NtQueryPerformanceCounter(&performanceCounter, &EtpPerformanceFrequency); + + EtDiskEnabled = TRUE; + + // Collect all existing file names. + EtStartEtwRundown(); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + EtpDiskProcessesUpdatedCallback, + NULL, + &EtpProcessesUpdatedCallbackRegistration + ); +} + +PET_DISK_ITEM EtCreateDiskItem( + VOID + ) +{ + PET_DISK_ITEM diskItem; + + diskItem = PhCreateObject(sizeof(ET_DISK_ITEM), EtDiskItemType); + memset(diskItem, 0, sizeof(ET_DISK_ITEM)); + + return diskItem; +} + +VOID NTAPI EtpDiskItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PET_DISK_ITEM diskItem = Object; + + if (diskItem->FileName) PhDereferenceObject(diskItem->FileName); + if (diskItem->FileNameWin32) PhDereferenceObject(diskItem->FileNameWin32); + if (diskItem->ProcessName) PhDereferenceObject(diskItem->ProcessName); + if (diskItem->ProcessRecord) PhDereferenceProcessRecord(diskItem->ProcessRecord); + + // NOTE: Dereferencing the ProcessItem will destroy the DiskItem->ProcessIcon handle. + if (diskItem->ProcessItem) PhDereferenceObject(diskItem->ProcessItem); +} + +BOOLEAN NTAPI EtpDiskHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PET_DISK_ITEM diskItem1 = *(PET_DISK_ITEM *)Entry1; + PET_DISK_ITEM diskItem2 = *(PET_DISK_ITEM *)Entry2; + + return diskItem1->ProcessId == diskItem2->ProcessId && PhEqualString(diskItem1->FileName, diskItem2->FileName, TRUE); +} + +ULONG NTAPI EtpDiskHashtableHashFunction( + _In_ PVOID Entry + ) +{ + PET_DISK_ITEM diskItem = *(PET_DISK_ITEM *)Entry; + + return (HandleToUlong(diskItem->ProcessId) / 4) ^ PhHashStringRef(&diskItem->FileName->sr, TRUE); +} + +PET_DISK_ITEM EtReferenceDiskItem( + _In_ HANDLE ProcessId, + _In_ PPH_STRING FileName + ) +{ + ET_DISK_ITEM lookupDiskItem; + PET_DISK_ITEM lookupDiskItemPtr = &lookupDiskItem; + PET_DISK_ITEM *diskItemPtr; + PET_DISK_ITEM diskItem; + + lookupDiskItem.ProcessId = ProcessId; + lookupDiskItem.FileName = FileName; + + PhAcquireQueuedLockShared(&EtDiskHashtableLock); + + diskItemPtr = (PET_DISK_ITEM *)PhFindEntryHashtable( + EtDiskHashtable, + &lookupDiskItemPtr + ); + + if (diskItemPtr) + PhSetReference(&diskItem, *diskItemPtr); + else + diskItem = NULL; + + PhReleaseQueuedLockShared(&EtDiskHashtableLock); + + return diskItem; +} + +VOID EtpRemoveDiskItem( + _In_ PET_DISK_ITEM DiskItem + ) +{ + RemoveEntryList(&DiskItem->AgeListEntry); + PhRemoveEntryHashtable(EtDiskHashtable, &DiskItem); + PhDereferenceObject(DiskItem); +} + +VOID EtDiskProcessDiskEvent( + _In_ PET_ETW_DISK_EVENT Event + ) +{ + PETP_DISK_PACKET packet; + + if (!EtDiskEnabled) + return; + + packet = PhAllocateFromFreeList(&EtDiskPacketFreeList); + memcpy(&packet->Event, Event, sizeof(ET_ETW_DISK_EVENT)); + packet->FileName = EtFileObjectToFileName(Event->FileObject); + RtlInterlockedPushEntrySList(&EtDiskPacketListHead, &packet->ListEntry); +} + +VOID EtDiskProcessFileEvent( + _In_ PET_ETW_FILE_EVENT Event + ) +{ + PH_KEY_VALUE_PAIR pair; + PPH_KEY_VALUE_PAIR realPair; + + if (!EtDiskEnabled) + return; + + if (Event->Type == EtEtwFileCreateType || Event->Type == EtEtwFileRundownType) + { + pair.Key = Event->FileObject; + pair.Value = NULL; + + PhAcquireQueuedLockExclusive(&EtFileNameHashtableLock); + + realPair = PhAddEntryHashtableEx(EtFileNameHashtable, &pair, NULL); + PhMoveReference(&realPair->Value, PhCreateString2(&Event->FileName)); + + PhReleaseQueuedLockExclusive(&EtFileNameHashtableLock); + } + else if (Event->Type == EtEtwFileDeleteType) + { + pair.Key = Event->FileObject; + + PhAcquireQueuedLockExclusive(&EtFileNameHashtableLock); + + realPair = PhFindEntryHashtable(EtFileNameHashtable, &pair); + + if (realPair) + { + PhDereferenceObject(realPair->Value); + PhRemoveEntryHashtable(EtFileNameHashtable, &pair); + } + + PhReleaseQueuedLockExclusive(&EtFileNameHashtableLock); + } +} + +PPH_STRING EtFileObjectToFileName( + _In_ PVOID FileObject + ) +{ + PH_KEY_VALUE_PAIR pair; + PPH_KEY_VALUE_PAIR realPair; + PPH_STRING fileName; + + pair.Key = FileObject; + fileName = NULL; + + PhAcquireQueuedLockShared(&EtFileNameHashtableLock); + + realPair = PhFindEntryHashtable(EtFileNameHashtable, &pair); + + if (realPair) + PhSetReference(&fileName, realPair->Value); + + PhReleaseQueuedLockShared(&EtFileNameHashtableLock); + + return fileName; +} + +VOID EtpProcessDiskPacket( + _In_ PETP_DISK_PACKET Packet, + _In_ ULONG RunId + ) +{ + PET_ETW_DISK_EVENT diskEvent; + PET_DISK_ITEM diskItem; + BOOLEAN added = FALSE; + + diskEvent = &Packet->Event; + + // We only process non-zero read/write events. + if (diskEvent->Type != EtEtwDiskReadType && diskEvent->Type != EtEtwDiskWriteType) + return; + if (diskEvent->TransferSize == 0) + return; + + // Ignore packets with no file name - this is useless to the user. + if (!Packet->FileName) + return; + + diskItem = EtReferenceDiskItem(diskEvent->ClientId.UniqueProcess, Packet->FileName); + + if (!diskItem) + { + PPH_PROCESS_ITEM processItem; + + // Disk item not found (or the address was re-used), create it. + + diskItem = EtCreateDiskItem(); + + diskItem->ProcessId = diskEvent->ClientId.UniqueProcess; + PhSetReference(&diskItem->FileName, Packet->FileName); + diskItem->FileNameWin32 = PhGetFileName(diskItem->FileName); + + if (processItem = PhReferenceProcessItem(diskItem->ProcessId)) + { + diskItem->ProcessItem = processItem; + PhSetReference(&diskItem->ProcessName, processItem->ProcessName); + + PhReferenceProcessRecord(processItem->Record); + diskItem->ProcessRecord = processItem->Record; + + if (!diskItem->ProcessIconValid && PhTestEvent(&processItem->Stage1Event)) + { + diskItem->ProcessIcon = processItem->SmallIcon; + diskItem->ProcessIconValid = TRUE; + } + + // NOTE: We dereference processItem in EtpDiskItemDeleteProcedure. (dmex) + } + + // Add the disk item to the age list. + diskItem->AddTime = RunId; + diskItem->FreshTime = RunId; + InsertHeadList(&EtDiskAgeListHead, &diskItem->AgeListEntry); + + // Add the disk item to the hashtable. + PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); + PhAddEntryHashtable(EtDiskHashtable, &diskItem); + PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); + + // Raise the disk item added event. + PhInvokeCallback(&EtDiskItemAddedEvent, diskItem); + added = TRUE; + } + + // The I/O priority number needs to be decoded. + + diskItem->IoPriority = (diskEvent->IrpFlags >> 17) & 7; + + if (diskItem->IoPriority == 0) + diskItem->IoPriority = IoPriorityNormal; + else + diskItem->IoPriority--; + + // Accumulate statistics for this update period. + + if (diskEvent->Type == EtEtwDiskReadType) + diskItem->ReadDelta += diskEvent->TransferSize; + else + diskItem->WriteDelta += diskEvent->TransferSize; + + if (EtpPerformanceFrequency.QuadPart != 0) + { + // Convert the response time to milliseconds. + diskItem->ResponseTimeTotal += (FLOAT)diskEvent->HighResResponseTime * 1000 / EtpPerformanceFrequency.QuadPart; + diskItem->ResponseTimeCount++; + } + + if (!added) + { + if (diskItem->FreshTime != RunId) + { + diskItem->FreshTime = RunId; + RemoveEntryList(&diskItem->AgeListEntry); + InsertHeadList(&EtDiskAgeListHead, &diskItem->AgeListEntry); + } + + PhDereferenceObject(diskItem); + } +} + +ULONG64 EtpCalculateAverage( + _In_ PULONG64 Buffer, + _In_ ULONG BufferSize, + _In_ ULONG BufferPosition, + _In_ ULONG BufferCount, + _In_ ULONG NumberToConsider + ) +{ + ULONG64 sum; + ULONG i; + ULONG count; + + sum = 0; + i = BufferPosition; + + if (NumberToConsider > BufferCount) + NumberToConsider = BufferCount; + + if (NumberToConsider == 0) + return 0; + + count = NumberToConsider; + + do + { + sum += Buffer[i]; + i++; + + if (i == BufferSize) + i = 0; + } while (--count != 0); + + return sum / NumberToConsider; +} + +VOID NTAPI EtpDiskProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PSLIST_ENTRY listEntry; + PLIST_ENTRY ageListEntry; + + // Process incoming disk event packets. + + listEntry = RtlInterlockedFlushSList(&EtDiskPacketListHead); + + while (listEntry) + { + PETP_DISK_PACKET packet; + + packet = CONTAINING_RECORD(listEntry, ETP_DISK_PACKET, ListEntry); + listEntry = listEntry->Next; + + EtpProcessDiskPacket(packet, EtRunCount); + + if (packet->FileName) + PhDereferenceObject(packet->FileName); + + PhFreeToFreeList(&EtDiskPacketFreeList, packet); + } + + // Remove old entries. + + ageListEntry = EtDiskAgeListHead.Blink; + + while (ageListEntry != &EtDiskAgeListHead) + { + PET_DISK_ITEM diskItem; + + diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); + ageListEntry = ageListEntry->Blink; + + if (EtRunCount - diskItem->FreshTime < HISTORY_SIZE) // must compare like this to avoid overflow/underflow problems + break; + + PhInvokeCallback(&EtDiskItemRemovedEvent, diskItem); + + PhAcquireQueuedLockExclusive(&EtDiskHashtableLock); + EtpRemoveDiskItem(diskItem); + PhReleaseQueuedLockExclusive(&EtDiskHashtableLock); + } + + // Update existing items. + + ageListEntry = EtDiskAgeListHead.Flink; + + while (ageListEntry != &EtDiskAgeListHead) + { + PET_DISK_ITEM diskItem; + + diskItem = CONTAINING_RECORD(ageListEntry, ET_DISK_ITEM, AgeListEntry); + + // Update statistics. + + if (diskItem->HistoryPosition != 0) + diskItem->HistoryPosition--; + else + diskItem->HistoryPosition = HISTORY_SIZE - 1; + + diskItem->ReadHistory[diskItem->HistoryPosition] = diskItem->ReadDelta; + diskItem->WriteHistory[diskItem->HistoryPosition] = diskItem->WriteDelta; + + if (diskItem->HistoryCount < HISTORY_SIZE) + diskItem->HistoryCount++; + + if (diskItem->ResponseTimeCount != 0) + { + diskItem->ResponseTimeAverage = (FLOAT)diskItem->ResponseTimeTotal / diskItem->ResponseTimeCount; + + // Reset the total once in a while to avoid the number getting too large (and thus losing precision). + if (diskItem->ResponseTimeCount >= 1000) + { + diskItem->ResponseTimeTotal = diskItem->ResponseTimeAverage; + diskItem->ResponseTimeCount = 1; + } + } + + diskItem->ReadTotal += diskItem->ReadDelta; + diskItem->WriteTotal += diskItem->WriteDelta; + diskItem->ReadDelta = 0; + diskItem->WriteDelta = 0; + diskItem->ReadAverage = EtpCalculateAverage(diskItem->ReadHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); + diskItem->WriteAverage = EtpCalculateAverage(diskItem->WriteHistory, HISTORY_SIZE, diskItem->HistoryPosition, diskItem->HistoryCount, HISTORY_SIZE); + + if (diskItem->AddTime != EtRunCount) + { + BOOLEAN modified = FALSE; + + if (!diskItem->ProcessItem) + { + diskItem->ProcessItem = PhReferenceProcessItem(diskItem->ProcessId); + // NOTE: We dereference processItem in EtpDiskItemDeleteProcedure. (dmex) + } + + if (diskItem->ProcessItem) + { + if (!diskItem->ProcessName) + { + PhSetReference(&diskItem->ProcessName, diskItem->ProcessItem->ProcessName); + modified = TRUE; + } + + if (!diskItem->ProcessIconValid && PhTestEvent(&diskItem->ProcessItem->Stage1Event)) + { + diskItem->ProcessIcon = diskItem->ProcessItem->SmallIcon; + diskItem->ProcessIconValid = TRUE; + modified = TRUE; + } + + if (!diskItem->ProcessRecord) + { + PhReferenceProcessRecord(diskItem->ProcessItem->Record); + diskItem->ProcessRecord = diskItem->ProcessItem->Record; + modified = TRUE; + } + } + + if (modified) + { + // Raise the disk item modified event. + PhInvokeCallback(&EtDiskItemModifiedEvent, diskItem); + } + } + + ageListEntry = ageListEntry->Flink; + } + + PhInvokeCallback(&EtDiskItemsUpdatedEvent, NULL); + EtRunCount++; +} diff --git a/plugins/ExtendedTools/etwmini.c b/plugins/ExtendedTools/etwmini.c index 03c1c7013307..367c8dc61033 100644 --- a/plugins/ExtendedTools/etwmini.c +++ b/plugins/ExtendedTools/etwmini.c @@ -1,264 +1,264 @@ -/* - * Process Hacker Extended Tools - - * ETW mini information section - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "etwmini.h" - -VOID EtEtwMiniInformationInitializing( - _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers - ) -{ - PH_MINIINFO_LIST_SECTION section; - - memset(§ion, 0, sizeof(PH_MINIINFO_LIST_SECTION)); - section.Callback = EtpDiskListSectionCallback; - Pointers->CreateListSection(L"Disk", 0, §ion); - - memset(§ion, 0, sizeof(PH_MINIINFO_LIST_SECTION)); - section.Callback = EtpNetworkListSectionCallback; - - Pointers->CreateListSection(L"Network", 0, §ion); -} - -BOOLEAN EtpDiskListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - { - PH_FORMAT format[4]; - - PhInitFormatS(&format[0], L"Disk R: "); - PhInitFormatSize(&format[1], EtDiskReadDelta.Delta); - format[1].Type |= FormatUsePrecision; - format[1].Precision = 0; - PhInitFormatS(&format[2], L" W: "); - PhInitFormatSize(&format[3], EtDiskWriteDelta.Delta); - format[3].Type |= FormatUsePrecision; - format[3].Precision = 0; - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PH_AUTO(PhFormat(format, 4, 50))); - } - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), EtpDiskListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - ULONG64 diskReadDelta = 0; - ULONG64 diskWriteDelta = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - { - PPH_PROCESS_ITEM processItem = processes->Items[i]; - PET_PROCESS_BLOCK block = EtGetProcessBlock(processItem); - diskReadDelta += block->DiskReadRawDelta.Delta; - diskWriteDelta += block->DiskWriteRawDelta.Delta; - } - - assignSortData->SortData->UserData[0] = diskReadDelta; - assignSortData->SortData->UserData[1] = diskWriteDelta; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), EtpDiskListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - ULONG64 diskReadDelta = getUsageText->SortData->UserData[0]; - ULONG64 diskWriteDelta = getUsageText->SortData->UserData[1]; - PH_FORMAT format[1]; - - PhInitFormatSize(&format[0], diskReadDelta + diskWriteDelta); - PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16)); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl EtpDiskListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - int result; - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - PET_PROCESS_BLOCK block1 = EtGetProcessBlock(node1->ProcessItem); - PET_PROCESS_BLOCK block2 = EtGetProcessBlock(node2->ProcessItem); - ULONG64 total1 = block1->DiskReadRawDelta.Delta + block1->DiskWriteRawDelta.Delta; - ULONG64 total2 = block2->DiskReadRawDelta.Delta + block2->DiskWriteRawDelta.Delta; - - result = uint64cmp(total2, total1); - - if (result == 0) - result = uint64cmp(block2->DiskReadRaw + block2->DiskWriteRaw, block1->DiskReadRaw + block1->DiskWriteRaw); - if (result == 0) - result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); - - return result; -} - -int __cdecl EtpDiskListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]); -} - -BOOLEAN EtpNetworkListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - { - PH_FORMAT format[4]; - - PhInitFormatS(&format[0], L"Network R: "); - PhInitFormatSize(&format[1], EtNetworkReceiveDelta.Delta); - format[1].Type |= FormatUsePrecision; - format[1].Precision = 0; - PhInitFormatS(&format[2], L" S: "); - PhInitFormatSize(&format[3], EtNetworkSendDelta.Delta); - format[3].Type |= FormatUsePrecision; - format[3].Precision = 0; - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PH_AUTO(PhFormat(format, 4, 50))); - } - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), EtpNetworkListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - ULONG64 networkReceiveDelta = 0; - ULONG64 networkSendDelta = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - { - PPH_PROCESS_ITEM processItem = processes->Items[i]; - PET_PROCESS_BLOCK block = EtGetProcessBlock(processItem); - networkReceiveDelta += block->NetworkReceiveRawDelta.Delta; - networkSendDelta += block->NetworkSendRawDelta.Delta; - } - - assignSortData->SortData->UserData[0] = networkReceiveDelta; - assignSortData->SortData->UserData[1] = networkSendDelta; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), EtpNetworkListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - ULONG64 networkReceiveDelta = getUsageText->SortData->UserData[0]; - ULONG64 networkSendDelta = getUsageText->SortData->UserData[1]; - PH_FORMAT format[1]; - - PhInitFormatSize(&format[0], networkReceiveDelta + networkSendDelta); - PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16)); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl EtpNetworkListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - int result; - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - PET_PROCESS_BLOCK block1 = EtGetProcessBlock(node1->ProcessItem); - PET_PROCESS_BLOCK block2 = EtGetProcessBlock(node2->ProcessItem); - ULONG64 total1 = block1->NetworkReceiveRawDelta.Delta + block1->NetworkSendRawDelta.Delta; - ULONG64 total2 = block2->NetworkReceiveRawDelta.Delta + block2->NetworkSendRawDelta.Delta; - - result = uint64cmp(total2, total1); - - if (result == 0) - result = uint64cmp(block2->NetworkReceiveRaw + block2->NetworkSendRaw, block1->NetworkReceiveRaw + block1->NetworkSendRaw); - if (result == 0) - result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); - - return result; -} - -int __cdecl EtpNetworkListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]); -} +/* + * Process Hacker Extended Tools - + * ETW mini information section + * + * Copyright (C) 2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwmini.h" + +VOID EtEtwMiniInformationInitializing( + _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers + ) +{ + PH_MINIINFO_LIST_SECTION section; + + memset(§ion, 0, sizeof(PH_MINIINFO_LIST_SECTION)); + section.Callback = EtpDiskListSectionCallback; + Pointers->CreateListSection(L"Disk", 0, §ion); + + memset(§ion, 0, sizeof(PH_MINIINFO_LIST_SECTION)); + section.Callback = EtpNetworkListSectionCallback; + + Pointers->CreateListSection(L"Network", 0, §ion); +} + +BOOLEAN EtpDiskListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + { + PH_FORMAT format[4]; + + PhInitFormatS(&format[0], L"Disk R: "); + PhInitFormatSize(&format[1], EtDiskReadDelta.Delta); + format[1].Type |= FormatUsePrecision; + format[1].Precision = 0; + PhInitFormatS(&format[2], L" W: "); + PhInitFormatSize(&format[3], EtDiskWriteDelta.Delta); + format[3].Type |= FormatUsePrecision; + format[3].Precision = 0; + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PH_AUTO(PhFormat(format, 4, 50))); + } + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), EtpDiskListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + ULONG64 diskReadDelta = 0; + ULONG64 diskWriteDelta = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + { + PPH_PROCESS_ITEM processItem = processes->Items[i]; + PET_PROCESS_BLOCK block = EtGetProcessBlock(processItem); + diskReadDelta += block->DiskReadRawDelta.Delta; + diskWriteDelta += block->DiskWriteRawDelta.Delta; + } + + assignSortData->SortData->UserData[0] = diskReadDelta; + assignSortData->SortData->UserData[1] = diskWriteDelta; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), EtpDiskListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + ULONG64 diskReadDelta = getUsageText->SortData->UserData[0]; + ULONG64 diskWriteDelta = getUsageText->SortData->UserData[1]; + PH_FORMAT format[1]; + + PhInitFormatSize(&format[0], diskReadDelta + diskWriteDelta); + PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16)); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl EtpDiskListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + int result; + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + PET_PROCESS_BLOCK block1 = EtGetProcessBlock(node1->ProcessItem); + PET_PROCESS_BLOCK block2 = EtGetProcessBlock(node2->ProcessItem); + ULONG64 total1 = block1->DiskReadRawDelta.Delta + block1->DiskWriteRawDelta.Delta; + ULONG64 total2 = block2->DiskReadRawDelta.Delta + block2->DiskWriteRawDelta.Delta; + + result = uint64cmp(total2, total1); + + if (result == 0) + result = uint64cmp(block2->DiskReadRaw + block2->DiskWriteRaw, block1->DiskReadRaw + block1->DiskWriteRaw); + if (result == 0) + result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); + + return result; +} + +int __cdecl EtpDiskListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]); +} + +BOOLEAN EtpNetworkListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + { + PH_FORMAT format[4]; + + PhInitFormatS(&format[0], L"Network R: "); + PhInitFormatSize(&format[1], EtNetworkReceiveDelta.Delta); + format[1].Type |= FormatUsePrecision; + format[1].Precision = 0; + PhInitFormatS(&format[2], L" S: "); + PhInitFormatSize(&format[3], EtNetworkSendDelta.Delta); + format[3].Type |= FormatUsePrecision; + format[3].Precision = 0; + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PH_AUTO(PhFormat(format, 4, 50))); + } + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), EtpNetworkListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + ULONG64 networkReceiveDelta = 0; + ULONG64 networkSendDelta = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + { + PPH_PROCESS_ITEM processItem = processes->Items[i]; + PET_PROCESS_BLOCK block = EtGetProcessBlock(processItem); + networkReceiveDelta += block->NetworkReceiveRawDelta.Delta; + networkSendDelta += block->NetworkSendRawDelta.Delta; + } + + assignSortData->SortData->UserData[0] = networkReceiveDelta; + assignSortData->SortData->UserData[1] = networkSendDelta; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), EtpNetworkListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + ULONG64 networkReceiveDelta = getUsageText->SortData->UserData[0]; + ULONG64 networkSendDelta = getUsageText->SortData->UserData[1]; + PH_FORMAT format[1]; + + PhInitFormatSize(&format[0], networkReceiveDelta + networkSendDelta); + PhMoveReference(&getUsageText->Line1, PhFormat(format, 1, 16)); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl EtpNetworkListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + int result; + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + PET_PROCESS_BLOCK block1 = EtGetProcessBlock(node1->ProcessItem); + PET_PROCESS_BLOCK block2 = EtGetProcessBlock(node2->ProcessItem); + ULONG64 total1 = block1->NetworkReceiveRawDelta.Delta + block1->NetworkSendRawDelta.Delta; + ULONG64 total2 = block2->NetworkReceiveRawDelta.Delta + block2->NetworkSendRawDelta.Delta; + + result = uint64cmp(total2, total1); + + if (result == 0) + result = uint64cmp(block2->NetworkReceiveRaw + block2->NetworkSendRaw, block1->NetworkReceiveRaw + block1->NetworkSendRaw); + if (result == 0) + result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); + + return result; +} + +int __cdecl EtpNetworkListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return uint64cmp(data2->UserData[0] + data2->UserData[1], data1->UserData[0] + data1->UserData[1]); +} diff --git a/plugins/ExtendedTools/etwmini.h b/plugins/ExtendedTools/etwmini.h index 6237b81e68b9..0c394a4f64a3 100644 --- a/plugins/ExtendedTools/etwmini.h +++ b/plugins/ExtendedTools/etwmini.h @@ -1,38 +1,38 @@ -#ifndef ETWMINI_H -#define ETWMINI_H - -BOOLEAN EtpDiskListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl EtpDiskListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl EtpDiskListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -BOOLEAN EtpNetworkListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl EtpNetworkListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl EtpNetworkListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - +#ifndef ETWMINI_H +#define ETWMINI_H + +BOOLEAN EtpDiskListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl EtpDiskListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl EtpDiskListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +BOOLEAN EtpNetworkListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl EtpNetworkListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl EtpNetworkListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + #endif \ No newline at end of file diff --git a/plugins/ExtendedTools/etwmon.c b/plugins/ExtendedTools/etwmon.c index e106bb123f85..e29fcb65ac76 100644 --- a/plugins/ExtendedTools/etwmon.c +++ b/plugins/ExtendedTools/etwmon.c @@ -1,562 +1,590 @@ -/* - * Process Hacker Extended Tools - - * ETW monitoring - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "etwmon.h" - -ULONG NTAPI EtpEtwBufferCallback( - _In_ PEVENT_TRACE_LOGFILE Buffer - ); - -VOID NTAPI EtpEtwEventCallback( - _In_ PEVENT_RECORD EventRecord - ); - -NTSTATUS EtpEtwMonitorThreadStart( - _In_ PVOID Parameter - ); - -ULONG EtpStopEtwRundownSession( - VOID - ); - -ULONG NTAPI EtpRundownEtwBufferCallback( - _In_ PEVENT_TRACE_LOGFILE Buffer - ); - -VOID NTAPI EtpRundownEtwEventCallback( - _In_ PEVENT_RECORD EventRecord - ); - -NTSTATUS EtpRundownEtwMonitorThreadStart( - _In_ PVOID Parameter - ); - -static GUID ProcessHackerGuid = { 0x1288c53b, 0xaf35, 0x481b, { 0xb6, 0xb5, 0xa0, 0x5c, 0x39, 0x87, 0x2e, 0xd } }; -static GUID SystemTraceControlGuid_I = { 0x9e814aad, 0x3204, 0x11d2, { 0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39 } }; -static GUID KernelRundownGuid_I = { 0x3b9c9951, 0x3480, 0x4220, { 0x93, 0x77, 0x9c, 0x8e, 0x51, 0x84, 0xf5, 0xcd } }; -static GUID DiskIoGuid_I = { 0x3d6fa8d4, 0xfe05, 0x11d0, { 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c } }; -static GUID FileIoGuid_I = { 0x90cbdc39, 0x4a3e, 0x11d1, { 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3 } }; -static GUID TcpIpGuid_I = { 0x9a280ac0, 0xc8e0, 0x11d1, { 0x84, 0xe2, 0x00, 0xc0, 0x4f, 0xb9, 0x98, 0xa2 } }; -static GUID UdpIpGuid_I = { 0xbf3a50c5, 0xa9c9, 0x4988, { 0xa0, 0x05, 0x2d, 0xf0, 0xb7, 0xc8, 0x0f, 0x80 } }; - -// ETW tracing layer - -BOOLEAN EtEtwEnabled; -static UNICODE_STRING EtpSharedKernelLoggerName = RTL_CONSTANT_STRING(KERNEL_LOGGER_NAME); -static UNICODE_STRING EtpPrivateKernelLoggerName = RTL_CONSTANT_STRING(L"PhEtKernelLogger"); -static TRACEHANDLE EtpSessionHandle; -static PUNICODE_STRING EtpActualKernelLoggerName; -static PGUID EtpActualSessionGuid; -static PEVENT_TRACE_PROPERTIES EtpTraceProperties; -static BOOLEAN EtpEtwActive; -static BOOLEAN EtpStartedSession; -static BOOLEAN EtpEtwExiting; -static HANDLE EtpEtwMonitorThreadHandle; - -// ETW rundown layer - -static UNICODE_STRING EtpRundownLoggerName = RTL_CONSTANT_STRING(L"PhEtRundownLogger"); -static TRACEHANDLE EtpRundownSessionHandle; -static PEVENT_TRACE_PROPERTIES EtpRundownTraceProperties; -static BOOLEAN EtpRundownActive; -static HANDLE EtpRundownEtwMonitorThreadHandle; - -VOID EtEtwMonitorInitialization( - VOID - ) -{ - if (PhGetOwnTokenAttributes().Elevated && PhGetIntegerSetting(SETTING_NAME_ENABLE_ETW_MONITOR)) - { - EtStartEtwSession(); - - if (EtEtwEnabled) - EtpEtwMonitorThreadHandle = PhCreateThread(0, EtpEtwMonitorThreadStart, NULL); - } -} - -VOID EtEtwMonitorUninitialization( - VOID - ) -{ - if (EtEtwEnabled) - { - EtpEtwExiting = TRUE; - EtStopEtwSession(); - } - - if (EtpRundownActive) - { - EtpStopEtwRundownSession(); - } -} - -VOID EtStartEtwSession( - VOID - ) -{ - ULONG result; - ULONG bufferSize; - - if (WindowsVersion >= WINDOWS_8) - { - EtpActualKernelLoggerName = &EtpPrivateKernelLoggerName; - EtpActualSessionGuid = &ProcessHackerGuid; - } - else - { - EtpActualKernelLoggerName = &EtpSharedKernelLoggerName; - EtpActualSessionGuid = &SystemTraceControlGuid_I; - } - - bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + EtpActualKernelLoggerName->Length + sizeof(WCHAR); - - if (!EtpTraceProperties) - EtpTraceProperties = PhAllocate(bufferSize); - - memset(EtpTraceProperties, 0, sizeof(EVENT_TRACE_PROPERTIES)); - - EtpTraceProperties->Wnode.BufferSize = bufferSize; - EtpTraceProperties->Wnode.Guid = *EtpActualSessionGuid; - EtpTraceProperties->Wnode.ClientContext = 1; - EtpTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; - EtpTraceProperties->MinimumBuffers = 1; - EtpTraceProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; - EtpTraceProperties->FlushTimer = 1; - EtpTraceProperties->EnableFlags = EVENT_TRACE_FLAG_DISK_IO | EVENT_TRACE_FLAG_DISK_FILE_IO | EVENT_TRACE_FLAG_NETWORK_TCPIP; - EtpTraceProperties->LogFileNameOffset = 0; - EtpTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); - - if (WindowsVersion >= WINDOWS_8) - EtpTraceProperties->LogFileMode |= EVENT_TRACE_SYSTEM_LOGGER_MODE; - - result = StartTrace(&EtpSessionHandle, EtpActualKernelLoggerName->Buffer, EtpTraceProperties); - - if (result == ERROR_SUCCESS) - { - EtEtwEnabled = TRUE; - EtpEtwActive = TRUE; - EtpStartedSession = TRUE; - } - else if (result == ERROR_ALREADY_EXISTS) - { - EtEtwEnabled = TRUE; - EtpEtwActive = TRUE; - EtpStartedSession = FALSE; - // The session already exists. - //result = ControlTrace(0, EtpActualKernelLoggerName->Buffer, EtpTraceProperties, EVENT_TRACE_CONTROL_UPDATE); - } - else - { - EtpEtwActive = FALSE; - EtpStartedSession = FALSE; - } -} - -ULONG EtpControlEtwSession( - _In_ ULONG ControlCode - ) -{ - // If we have a session handle, we use that instead of the logger name. - - EtpTraceProperties->LogFileNameOffset = 0; // make sure it is 0, otherwise ControlTrace crashes - - return ControlTrace( - EtpStartedSession ? EtpSessionHandle : 0, - EtpStartedSession ? NULL : EtpActualKernelLoggerName->Buffer, - EtpTraceProperties, - ControlCode - ); -} - -VOID EtStopEtwSession( - VOID - ) -{ - if (EtEtwEnabled) - EtpControlEtwSession(EVENT_TRACE_CONTROL_STOP); -} - -VOID EtFlushEtwSession( - VOID - ) -{ - if (EtEtwEnabled) - EtpControlEtwSession(EVENT_TRACE_CONTROL_FLUSH); -} - -ULONG NTAPI EtpEtwBufferCallback( - _In_ PEVENT_TRACE_LOGFILE Buffer - ) -{ - return !EtpEtwExiting; -} - -VOID NTAPI EtpEtwEventCallback( - _In_ PEVENT_RECORD EventRecord - ) -{ - if (memcmp(&EventRecord->EventHeader.ProviderId, &DiskIoGuid_I, sizeof(GUID)) == 0) - { - // DiskIo - - ET_ETW_DISK_EVENT diskEvent; - - memset(&diskEvent, 0, sizeof(ET_ETW_DISK_EVENT)); - diskEvent.Type = -1; - - switch (EventRecord->EventHeader.EventDescriptor.Opcode) - { - case EVENT_TRACE_TYPE_IO_READ: - diskEvent.Type = EtEtwDiskReadType; - break; - case EVENT_TRACE_TYPE_IO_WRITE: - diskEvent.Type = EtEtwDiskWriteType; - break; - default: - break; - } - - if (diskEvent.Type != -1) - { - DiskIo_TypeGroup1 *data = EventRecord->UserData; - - if (WindowsVersion >= WINDOWS_8) - { - diskEvent.ClientId.UniqueThread = UlongToHandle(data->IssuingThreadId); - diskEvent.ClientId.UniqueProcess = EtThreadIdToProcessId(diskEvent.ClientId.UniqueThread); - } - else - { - if (EventRecord->EventHeader.ProcessId != -1) - { - diskEvent.ClientId.UniqueProcess = UlongToHandle(EventRecord->EventHeader.ProcessId); - diskEvent.ClientId.UniqueThread = UlongToHandle(EventRecord->EventHeader.ThreadId); - } - } - - diskEvent.IrpFlags = data->IrpFlags; - diskEvent.TransferSize = data->TransferSize; - diskEvent.FileObject = (PVOID)data->FileObject; - diskEvent.HighResResponseTime = data->HighResResponseTime; - - EtProcessDiskEvent(&diskEvent); - EtDiskProcessDiskEvent(&diskEvent); - } - } - else if (memcmp(&EventRecord->EventHeader.ProviderId, &FileIoGuid_I, sizeof(GUID)) == 0) - { - // FileIo - - ET_ETW_FILE_EVENT fileEvent; - - memset(&fileEvent, 0, sizeof(ET_ETW_FILE_EVENT)); - fileEvent.Type = -1; - - switch (EventRecord->EventHeader.EventDescriptor.Opcode) - { - case 0: // Name - fileEvent.Type = EtEtwFileNameType; - break; - case 32: // FileCreate - fileEvent.Type = EtEtwFileCreateType; - break; - case 35: // FileDelete - fileEvent.Type = EtEtwFileDeleteType; - break; - default: - break; - } - - if (fileEvent.Type != -1) - { - FileIo_Name *data = EventRecord->UserData; - - fileEvent.FileObject = (PVOID)data->FileObject; - PhInitializeStringRef(&fileEvent.FileName, data->FileName); - - EtDiskProcessFileEvent(&fileEvent); - } - } - else if ( - memcmp(&EventRecord->EventHeader.ProviderId, &TcpIpGuid_I, sizeof(GUID)) == 0 || - memcmp(&EventRecord->EventHeader.ProviderId, &UdpIpGuid_I, sizeof(GUID)) == 0 - ) - { - // TcpIp/UdpIp - - ET_ETW_NETWORK_EVENT networkEvent; - - memset(&networkEvent, 0, sizeof(ET_ETW_NETWORK_EVENT)); - networkEvent.Type = -1; - - switch (EventRecord->EventHeader.EventDescriptor.Opcode) - { - case EVENT_TRACE_TYPE_SEND: // send - networkEvent.Type = EtEtwNetworkSendType; - networkEvent.ProtocolType = PH_IPV4_NETWORK_TYPE; - break; - case EVENT_TRACE_TYPE_RECEIVE: // receive - networkEvent.Type = EtEtwNetworkReceiveType; - networkEvent.ProtocolType = PH_IPV4_NETWORK_TYPE; - break; - case EVENT_TRACE_TYPE_SEND + 16: // send ipv6 - networkEvent.Type = EtEtwNetworkSendType; - networkEvent.ProtocolType = PH_IPV6_NETWORK_TYPE; - break; - case EVENT_TRACE_TYPE_RECEIVE + 16: // receive ipv6 - networkEvent.Type = EtEtwNetworkReceiveType; - networkEvent.ProtocolType = PH_IPV6_NETWORK_TYPE; - break; - } - - if (memcmp(&EventRecord->EventHeader.ProviderId, &TcpIpGuid_I, sizeof(GUID)) == 0) - networkEvent.ProtocolType |= PH_TCP_PROTOCOL_TYPE; - else - networkEvent.ProtocolType |= PH_UDP_PROTOCOL_TYPE; - - if (networkEvent.Type != -1) - { - PH_IP_ENDPOINT source; - PH_IP_ENDPOINT destination; - - if (networkEvent.ProtocolType & PH_IPV4_NETWORK_TYPE) - { - TcpIpOrUdpIp_IPV4_Header *data = EventRecord->UserData; - - networkEvent.ClientId.UniqueProcess = UlongToHandle(data->PID); - networkEvent.TransferSize = data->size; - - source.Address.Type = PH_IPV4_NETWORK_TYPE; - source.Address.Ipv4 = data->saddr; - source.Port = _byteswap_ushort(data->sport); - destination.Address.Type = PH_IPV4_NETWORK_TYPE; - destination.Address.Ipv4 = data->daddr; - destination.Port = _byteswap_ushort(data->dport); - } - else if (networkEvent.ProtocolType & PH_IPV6_NETWORK_TYPE) - { - TcpIpOrUdpIp_IPV6_Header *data = EventRecord->UserData; - - networkEvent.ClientId.UniqueProcess = UlongToHandle(data->PID); - networkEvent.TransferSize = data->size; - - source.Address.Type = PH_IPV6_NETWORK_TYPE; - source.Address.In6Addr = data->saddr; - source.Port = _byteswap_ushort(data->sport); - destination.Address.Type = PH_IPV6_NETWORK_TYPE; - destination.Address.In6Addr = data->daddr; - destination.Port = _byteswap_ushort(data->dport); - } - - networkEvent.LocalEndpoint = source; - - if (networkEvent.ProtocolType & PH_TCP_PROTOCOL_TYPE) - networkEvent.RemoteEndpoint = destination; - - EtProcessNetworkEvent(&networkEvent); - } - } -} - -NTSTATUS EtpEtwMonitorThreadStart( - _In_ PVOID Parameter - ) -{ - ULONG result; - EVENT_TRACE_LOGFILE logFile; - TRACEHANDLE traceHandle; - - // See comment in EtEtwProcessesUpdatedCallback. - if (WindowsVersion >= WINDOWS_8) - EtUpdateProcessInformation(); - - memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); - logFile.LoggerName = EtpActualKernelLoggerName->Buffer; - logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; - logFile.BufferCallback = EtpEtwBufferCallback; - logFile.EventRecordCallback = EtpEtwEventCallback; - - while (TRUE) - { - result = ERROR_SUCCESS; - traceHandle = OpenTrace(&logFile); - - if (traceHandle != INVALID_PROCESSTRACE_HANDLE) - { - while (!EtpEtwExiting && (result = ProcessTrace(&traceHandle, 1, NULL, NULL)) == ERROR_SUCCESS) - NOTHING; - - CloseTrace(traceHandle); - } - - if (EtpEtwExiting) - break; - - if (result == ERROR_WMI_INSTANCE_NOT_FOUND) - { - // The session was stopped by another program. Try to start it again. - EtStartEtwSession(); - } - - // Some error occurred, so sleep for a while before trying again. - // Don't sleep if we just successfully started a session, though. - if (!EtpEtwActive) - Sleep(250); - } - - return STATUS_SUCCESS; -} - -ULONG EtStartEtwRundown( - VOID - ) -{ - ULONG result; - ULONG bufferSize; - - bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + EtpRundownLoggerName.Length + sizeof(WCHAR); - - if (!EtpRundownTraceProperties) - EtpRundownTraceProperties = PhAllocate(bufferSize); - - memset(EtpRundownTraceProperties, 0, sizeof(EVENT_TRACE_PROPERTIES)); - - EtpRundownTraceProperties->Wnode.BufferSize = bufferSize; - EtpRundownTraceProperties->Wnode.ClientContext = 1; - EtpRundownTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; - EtpRundownTraceProperties->MinimumBuffers = 1; - EtpRundownTraceProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; - EtpRundownTraceProperties->FlushTimer = 1; - EtpRundownTraceProperties->LogFileNameOffset = 0; - EtpRundownTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); - - result = StartTrace(&EtpRundownSessionHandle, EtpRundownLoggerName.Buffer, EtpRundownTraceProperties); - - if (result == ERROR_ALREADY_EXISTS) - { - EtpStopEtwRundownSession(); - // ControlTrace (called from EtpStopEtwRundownSession) screws up the structure. - EtpRundownTraceProperties->Wnode.BufferSize = bufferSize; - EtpRundownTraceProperties->LogFileNameOffset = 0; - EtpRundownTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); - result = StartTrace(&EtpRundownSessionHandle, EtpRundownLoggerName.Buffer, EtpRundownTraceProperties); - } - - if (result != ERROR_SUCCESS) - return result; - - result = EnableTraceEx(&KernelRundownGuid_I, NULL, EtpRundownSessionHandle, 1, 0, 0x10, 0, 0, NULL); - - if (result != ERROR_SUCCESS) - { - EtpStopEtwRundownSession(); - return result; - } - - EtpRundownActive = TRUE; - EtpRundownEtwMonitorThreadHandle = PhCreateThread(0, EtpRundownEtwMonitorThreadStart, NULL); - - return result; -} - -ULONG EtpStopEtwRundownSession( - VOID - ) -{ - EtpRundownTraceProperties->LogFileNameOffset = 0; - return ControlTrace(0, EtpRundownLoggerName.Buffer, EtpRundownTraceProperties, EVENT_TRACE_CONTROL_STOP); -} - -ULONG NTAPI EtpRundownEtwBufferCallback( - _In_ PEVENT_TRACE_LOGFILE Buffer - ) -{ - return !EtpEtwExiting; -} - -VOID NTAPI EtpRundownEtwEventCallback( - _In_ PEVENT_RECORD EventRecord - ) -{ - // TODO: Find a way to call CloseTrace when the enumeration finishes so we can - // stop the trace cleanly. - - if (memcmp(&EventRecord->EventHeader.ProviderId, &FileIoGuid_I, sizeof(GUID)) == 0) - { - // FileIo - - ET_ETW_FILE_EVENT fileEvent; - - memset(&fileEvent, 0, sizeof(ET_ETW_FILE_EVENT)); - fileEvent.Type = -1; - - switch (EventRecord->EventHeader.EventDescriptor.Opcode) - { - case 36: // FileRundown - fileEvent.Type = EtEtwFileRundownType; - break; - default: - break; - } - - if (fileEvent.Type != -1) - { - FileIo_Name *data = EventRecord->UserData; - - fileEvent.FileObject = (PVOID)data->FileObject; - PhInitializeStringRef(&fileEvent.FileName, data->FileName); - - EtDiskProcessFileEvent(&fileEvent); - } - } -} - -NTSTATUS EtpRundownEtwMonitorThreadStart( - _In_ PVOID Parameter - ) -{ - EVENT_TRACE_LOGFILE logFile; - TRACEHANDLE traceHandle; - - memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); - logFile.LoggerName = EtpRundownLoggerName.Buffer; - logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; - logFile.BufferCallback = EtpRundownEtwBufferCallback; - logFile.EventRecordCallback = EtpRundownEtwEventCallback; - logFile.Context = &traceHandle; - - traceHandle = OpenTrace(&logFile); - - if (traceHandle != INVALID_PROCESSTRACE_HANDLE) - { - ProcessTrace(&traceHandle, 1, NULL, NULL); - - if (traceHandle != 0) - CloseTrace(traceHandle); - } - - NtClose(EtpRundownEtwMonitorThreadHandle); - EtpRundownEtwMonitorThreadHandle = NULL; - - return STATUS_SUCCESS; -} +/* + * Process Hacker Extended Tools - + * ETW monitoring + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwmon.h" + +ULONG NTAPI EtpEtwBufferCallback( + _In_ PEVENT_TRACE_LOGFILE Buffer + ); + +VOID NTAPI EtpEtwEventCallback( + _In_ PEVENT_RECORD EventRecord + ); + +NTSTATUS EtpEtwMonitorThreadStart( + _In_ PVOID Parameter + ); + +ULONG EtpStopEtwRundownSession( + VOID + ); + +ULONG NTAPI EtpRundownEtwBufferCallback( + _In_ PEVENT_TRACE_LOGFILE Buffer + ); + +VOID NTAPI EtpRundownEtwEventCallback( + _In_ PEVENT_RECORD EventRecord + ); + +NTSTATUS EtpRundownEtwMonitorThreadStart( + _In_ PVOID Parameter + ); + +static GUID ProcessHackerGuid = { 0x1288c53b, 0xaf35, 0x481b, { 0xb6, 0xb5, 0xa0, 0x5c, 0x39, 0x87, 0x2e, 0xd } }; +static GUID SystemTraceControlGuid_I = { 0x9e814aad, 0x3204, 0x11d2, { 0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39 } }; +static GUID KernelRundownGuid_I = { 0x3b9c9951, 0x3480, 0x4220, { 0x93, 0x77, 0x9c, 0x8e, 0x51, 0x84, 0xf5, 0xcd } }; +static GUID DiskIoGuid_I = { 0x3d6fa8d4, 0xfe05, 0x11d0, { 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c } }; +static GUID FileIoGuid_I = { 0x90cbdc39, 0x4a3e, 0x11d1, { 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3 } }; +static GUID TcpIpGuid_I = { 0x9a280ac0, 0xc8e0, 0x11d1, { 0x84, 0xe2, 0x00, 0xc0, 0x4f, 0xb9, 0x98, 0xa2 } }; +static GUID UdpIpGuid_I = { 0xbf3a50c5, 0xa9c9, 0x4988, { 0xa0, 0x05, 0x2d, 0xf0, 0xb7, 0xc8, 0x0f, 0x80 } }; + +// ETW tracing layer + +BOOLEAN EtEtwEnabled; +static UNICODE_STRING EtpSharedKernelLoggerName = RTL_CONSTANT_STRING(KERNEL_LOGGER_NAME); +static UNICODE_STRING EtpPrivateKernelLoggerName = RTL_CONSTANT_STRING(L"PhEtKernelLogger"); +static TRACEHANDLE EtpSessionHandle; +static PUNICODE_STRING EtpActualKernelLoggerName; +static PGUID EtpActualSessionGuid; +static PEVENT_TRACE_PROPERTIES EtpTraceProperties; +static BOOLEAN EtpEtwActive; +static BOOLEAN EtpStartedSession; +static BOOLEAN EtpEtwExiting; + +// ETW rundown layer + +static UNICODE_STRING EtpRundownLoggerName = RTL_CONSTANT_STRING(L"PhEtRundownLogger"); +static TRACEHANDLE EtpRundownSessionHandle; +static PEVENT_TRACE_PROPERTIES EtpRundownTraceProperties; +static BOOLEAN EtpRundownActive; + +VOID EtEtwMonitorInitialization( + VOID + ) +{ + if (PhGetOwnTokenAttributes().Elevated && PhGetIntegerSetting(SETTING_NAME_ENABLE_ETW_MONITOR)) + { + EtStartEtwSession(); + + if (EtEtwEnabled) + { + PhCreateThread2(EtpEtwMonitorThreadStart, NULL); + } + } +} + +VOID EtEtwMonitorUninitialization( + VOID + ) +{ + if (EtEtwEnabled) + { + EtpEtwExiting = TRUE; + EtStopEtwSession(); + } + + if (EtpRundownActive) + { + EtpStopEtwRundownSession(); + } +} + +VOID EtStartEtwSession( + VOID + ) +{ + ULONG result; + ULONG bufferSize; + + if (WindowsVersion >= WINDOWS_8) + { + EtpActualKernelLoggerName = &EtpPrivateKernelLoggerName; + EtpActualSessionGuid = &ProcessHackerGuid; + } + else + { + EtpActualKernelLoggerName = &EtpSharedKernelLoggerName; + EtpActualSessionGuid = &SystemTraceControlGuid_I; + } + + bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + EtpActualKernelLoggerName->Length + sizeof(WCHAR); + + if (!EtpTraceProperties) + EtpTraceProperties = PhAllocate(bufferSize); + + memset(EtpTraceProperties, 0, sizeof(EVENT_TRACE_PROPERTIES)); + + EtpTraceProperties->Wnode.BufferSize = bufferSize; + EtpTraceProperties->Wnode.Guid = *EtpActualSessionGuid; + EtpTraceProperties->Wnode.ClientContext = 1; + EtpTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + EtpTraceProperties->MinimumBuffers = 1; + EtpTraceProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + EtpTraceProperties->FlushTimer = 1; + EtpTraceProperties->EnableFlags = EVENT_TRACE_FLAG_DISK_IO | EVENT_TRACE_FLAG_DISK_FILE_IO | EVENT_TRACE_FLAG_NETWORK_TCPIP; + EtpTraceProperties->LogFileNameOffset = 0; + EtpTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + if (WindowsVersion >= WINDOWS_8) + EtpTraceProperties->LogFileMode |= EVENT_TRACE_SYSTEM_LOGGER_MODE; + + result = StartTrace(&EtpSessionHandle, EtpActualKernelLoggerName->Buffer, EtpTraceProperties); + + if (result == ERROR_SUCCESS) + { + EtEtwEnabled = TRUE; + EtpEtwActive = TRUE; + EtpStartedSession = TRUE; + } + else if (result == ERROR_ALREADY_EXISTS) + { + EtEtwEnabled = TRUE; + EtpEtwActive = TRUE; + EtpStartedSession = FALSE; + // The session already exists. + //result = ControlTrace(0, EtpActualKernelLoggerName->Buffer, EtpTraceProperties, EVENT_TRACE_CONTROL_UPDATE); + } + else + { + EtpEtwActive = FALSE; + EtpStartedSession = FALSE; + } +} + +ULONG EtpControlEtwSession( + _In_ ULONG ControlCode + ) +{ + // If we have a session handle, we use that instead of the logger name. + + EtpTraceProperties->LogFileNameOffset = 0; // make sure it is 0, otherwise ControlTrace crashes + + return ControlTrace( + EtpStartedSession ? EtpSessionHandle : 0, + EtpStartedSession ? NULL : EtpActualKernelLoggerName->Buffer, + EtpTraceProperties, + ControlCode + ); +} + +VOID EtStopEtwSession( + VOID + ) +{ + if (EtEtwEnabled) + EtpControlEtwSession(EVENT_TRACE_CONTROL_STOP); +} + +VOID EtFlushEtwSession( + VOID + ) +{ + if (EtEtwEnabled) + EtpControlEtwSession(EVENT_TRACE_CONTROL_FLUSH); +} + +ULONG NTAPI EtpEtwBufferCallback( + _In_ PEVENT_TRACE_LOGFILE Buffer + ) +{ + return !EtpEtwExiting; +} + +VOID NTAPI EtpEtwEventCallback( + _In_ PEVENT_RECORD EventRecord + ) +{ + if (IsEqualGUID(&EventRecord->EventHeader.ProviderId, &DiskIoGuid_I)) + { + // DiskIo + + ET_ETW_DISK_EVENT diskEvent; + + memset(&diskEvent, 0, sizeof(ET_ETW_DISK_EVENT)); + diskEvent.Type = ULONG_MAX; + + switch (EventRecord->EventHeader.EventDescriptor.Opcode) + { + case EVENT_TRACE_TYPE_IO_READ: + diskEvent.Type = EtEtwDiskReadType; + break; + case EVENT_TRACE_TYPE_IO_WRITE: + diskEvent.Type = EtEtwDiskWriteType; + break; + default: + break; + } + + if (diskEvent.Type != ULONG_MAX) + { + DiskIo_TypeGroup1 *data = EventRecord->UserData; + + if (WindowsVersion >= WINDOWS_8) + { + diskEvent.ClientId.UniqueThread = UlongToHandle(data->IssuingThreadId); + diskEvent.ClientId.UniqueProcess = EtThreadIdToProcessId(diskEvent.ClientId.UniqueThread); + } + else + { + if (EventRecord->EventHeader.ProcessId != ULONG_MAX) + { + diskEvent.ClientId.UniqueProcess = UlongToHandle(EventRecord->EventHeader.ProcessId); + diskEvent.ClientId.UniqueThread = UlongToHandle(EventRecord->EventHeader.ThreadId); + } + } + + diskEvent.IrpFlags = data->IrpFlags; + diskEvent.TransferSize = data->TransferSize; + diskEvent.FileObject = (PVOID)data->FileObject; + diskEvent.HighResResponseTime = data->HighResResponseTime; + + EtProcessDiskEvent(&diskEvent); + EtDiskProcessDiskEvent(&diskEvent); + } + } + else if (IsEqualGUID(&EventRecord->EventHeader.ProviderId, &FileIoGuid_I)) + { + // FileIo + + ET_ETW_FILE_EVENT fileEvent; + + memset(&fileEvent, 0, sizeof(ET_ETW_FILE_EVENT)); + fileEvent.Type = ULONG_MAX; + + switch (EventRecord->EventHeader.EventDescriptor.Opcode) + { + case 0: // Name + fileEvent.Type = EtEtwFileNameType; + break; + case 32: // FileCreate + fileEvent.Type = EtEtwFileCreateType; + break; + case 35: // FileDelete + fileEvent.Type = EtEtwFileDeleteType; + break; + default: + break; + } + + if (fileEvent.Type != ULONG_MAX) + { + if (PhIsExecutingInWow64()) + { + FileIo_Name_Wow64 *dataWow64 = EventRecord->UserData; + + fileEvent.FileObject = (PVOID)dataWow64->FileObject; + PhInitializeStringRef(&fileEvent.FileName, dataWow64->FileName); + } + else + { + FileIo_Name *data = EventRecord->UserData; + + fileEvent.FileObject = (PVOID)data->FileObject; + PhInitializeStringRef(&fileEvent.FileName, data->FileName); + } + + EtDiskProcessFileEvent(&fileEvent); + } + } + else if ( + IsEqualGUID(&EventRecord->EventHeader.ProviderId, &TcpIpGuid_I) || + IsEqualGUID(&EventRecord->EventHeader.ProviderId, &UdpIpGuid_I) + ) + { + // TcpIp/UdpIp + + ET_ETW_NETWORK_EVENT networkEvent; + + memset(&networkEvent, 0, sizeof(ET_ETW_NETWORK_EVENT)); + networkEvent.Type = ULONG_MAX; + + switch (EventRecord->EventHeader.EventDescriptor.Opcode) + { + case EVENT_TRACE_TYPE_SEND: // send + networkEvent.Type = EtEtwNetworkSendType; + networkEvent.ProtocolType = PH_IPV4_NETWORK_TYPE; + break; + case EVENT_TRACE_TYPE_RECEIVE: // receive + networkEvent.Type = EtEtwNetworkReceiveType; + networkEvent.ProtocolType = PH_IPV4_NETWORK_TYPE; + break; + case EVENT_TRACE_TYPE_SEND + 16: // send ipv6 + networkEvent.Type = EtEtwNetworkSendType; + networkEvent.ProtocolType = PH_IPV6_NETWORK_TYPE; + break; + case EVENT_TRACE_TYPE_RECEIVE + 16: // receive ipv6 + networkEvent.Type = EtEtwNetworkReceiveType; + networkEvent.ProtocolType = PH_IPV6_NETWORK_TYPE; + break; + } + + if (IsEqualGUID(&EventRecord->EventHeader.ProviderId, &TcpIpGuid_I)) + networkEvent.ProtocolType |= PH_TCP_PROTOCOL_TYPE; + else + networkEvent.ProtocolType |= PH_UDP_PROTOCOL_TYPE; + + if (networkEvent.Type != ULONG_MAX) + { + PH_IP_ENDPOINT source; + PH_IP_ENDPOINT destination; + + if (networkEvent.ProtocolType & PH_IPV4_NETWORK_TYPE) + { + TcpIpOrUdpIp_IPV4_Header *data = EventRecord->UserData; + + networkEvent.ClientId.UniqueProcess = UlongToHandle(data->PID); + networkEvent.TransferSize = data->size; + + source.Address.Type = PH_IPV4_NETWORK_TYPE; + source.Address.Ipv4 = data->saddr.s_addr; + source.Port = _byteswap_ushort(data->sport); + destination.Address.Type = PH_IPV4_NETWORK_TYPE; + destination.Address.Ipv4 = data->daddr.s_addr; + destination.Port = _byteswap_ushort(data->dport); + } + else if (networkEvent.ProtocolType & PH_IPV6_NETWORK_TYPE) + { + TcpIpOrUdpIp_IPV6_Header *data = EventRecord->UserData; + + networkEvent.ClientId.UniqueProcess = UlongToHandle(data->PID); + networkEvent.TransferSize = data->size; + + source.Address.Type = PH_IPV6_NETWORK_TYPE; + source.Address.In6Addr = data->saddr; + source.Port = _byteswap_ushort(data->sport); + destination.Address.Type = PH_IPV6_NETWORK_TYPE; + destination.Address.In6Addr = data->daddr; + destination.Port = _byteswap_ushort(data->dport); + } + + // Note: The endpoints are swapped for incoming UDP packets. The destination endpoint + // corresponds to the local socket not the source endpoint. (DavidXanatos) + if ((networkEvent.ProtocolType & PH_UDP_PROTOCOL_TYPE) != 0 && + networkEvent.Type == EtEtwNetworkReceiveType) + { + PH_IP_ENDPOINT swapsource = source; + source = destination; + destination = swapsource; + } + + networkEvent.LocalEndpoint = source; + + if (networkEvent.ProtocolType & PH_TCP_PROTOCOL_TYPE) + networkEvent.RemoteEndpoint = destination; + + EtProcessNetworkEvent(&networkEvent); + } + } +} + +NTSTATUS EtpEtwMonitorThreadStart( + _In_ PVOID Parameter + ) +{ + ULONG result; + EVENT_TRACE_LOGFILE logFile; + TRACEHANDLE traceHandle; + + // See comment in EtEtwProcessesUpdatedCallback. + if (WindowsVersion >= WINDOWS_8) + EtUpdateProcessInformation(); + + memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); + logFile.LoggerName = EtpActualKernelLoggerName->Buffer; + logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; + logFile.BufferCallback = EtpEtwBufferCallback; + logFile.EventRecordCallback = EtpEtwEventCallback; + + while (TRUE) + { + result = ERROR_SUCCESS; + traceHandle = OpenTrace(&logFile); + + if (traceHandle != INVALID_PROCESSTRACE_HANDLE) + { + while (!EtpEtwExiting && (result = ProcessTrace(&traceHandle, 1, NULL, NULL)) == ERROR_SUCCESS) + NOTHING; + + CloseTrace(traceHandle); + } + + if (EtpEtwExiting) + break; + + if (result == ERROR_WMI_INSTANCE_NOT_FOUND) + { + // The session was stopped by another program. Try to start it again. + EtStartEtwSession(); + } + + // Some error occurred, so sleep for a while before trying again. + // Don't sleep if we just successfully started a session, though. + if (!EtpEtwActive) + PhDelayExecution(250); + } + + return STATUS_SUCCESS; +} + +ULONG EtStartEtwRundown( + VOID + ) +{ + ULONG result; + ULONG bufferSize; + + bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + EtpRundownLoggerName.Length + sizeof(WCHAR); + + if (!EtpRundownTraceProperties) + EtpRundownTraceProperties = PhAllocate(bufferSize); + + memset(EtpRundownTraceProperties, 0, sizeof(EVENT_TRACE_PROPERTIES)); + + EtpRundownTraceProperties->Wnode.BufferSize = bufferSize; + EtpRundownTraceProperties->Wnode.ClientContext = 1; + EtpRundownTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + EtpRundownTraceProperties->MinimumBuffers = 1; + EtpRundownTraceProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + EtpRundownTraceProperties->FlushTimer = 1; + EtpRundownTraceProperties->LogFileNameOffset = 0; + EtpRundownTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + result = StartTrace(&EtpRundownSessionHandle, EtpRundownLoggerName.Buffer, EtpRundownTraceProperties); + + if (result == ERROR_ALREADY_EXISTS) + { + EtpStopEtwRundownSession(); + // ControlTrace (called from EtpStopEtwRundownSession) screws up the structure. + EtpRundownTraceProperties->Wnode.BufferSize = bufferSize; + EtpRundownTraceProperties->LogFileNameOffset = 0; + EtpRundownTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + result = StartTrace(&EtpRundownSessionHandle, EtpRundownLoggerName.Buffer, EtpRundownTraceProperties); + } + + if (result != ERROR_SUCCESS) + return result; + + result = EnableTraceEx(&KernelRundownGuid_I, NULL, EtpRundownSessionHandle, 1, 0, 0x10, 0, 0, NULL); + + if (result != ERROR_SUCCESS) + { + EtpStopEtwRundownSession(); + return result; + } + + EtpRundownActive = TRUE; + PhCreateThread2(EtpRundownEtwMonitorThreadStart, NULL); + + return result; +} + +ULONG EtpStopEtwRundownSession( + VOID + ) +{ + EtpRundownTraceProperties->LogFileNameOffset = 0; + return ControlTrace(0, EtpRundownLoggerName.Buffer, EtpRundownTraceProperties, EVENT_TRACE_CONTROL_STOP); +} + +ULONG NTAPI EtpRundownEtwBufferCallback( + _In_ PEVENT_TRACE_LOGFILE Buffer + ) +{ + return !EtpEtwExiting; +} + +VOID NTAPI EtpRundownEtwEventCallback( + _In_ PEVENT_RECORD EventRecord + ) +{ + // TODO: Find a way to call CloseTrace when the enumeration finishes so we can + // stop the trace cleanly. + + if (IsEqualGUID(&EventRecord->EventHeader.ProviderId, &FileIoGuid_I)) + { + // FileIo + + ET_ETW_FILE_EVENT fileEvent; + + memset(&fileEvent, 0, sizeof(ET_ETW_FILE_EVENT)); + fileEvent.Type = ULONG_MAX; + + switch (EventRecord->EventHeader.EventDescriptor.Opcode) + { + case 36: // FileRundown + fileEvent.Type = EtEtwFileRundownType; + break; + default: + break; + } + + if (fileEvent.Type != ULONG_MAX) + { + if (PhIsExecutingInWow64()) + { + FileIo_Name_Wow64 *dataWow64 = EventRecord->UserData; + + fileEvent.FileObject = (PVOID)dataWow64->FileObject; + PhInitializeStringRef(&fileEvent.FileName, dataWow64->FileName); + } + else + { + FileIo_Name *data = EventRecord->UserData; + + fileEvent.FileObject = (PVOID)data->FileObject; + PhInitializeStringRef(&fileEvent.FileName, data->FileName); + } + + EtDiskProcessFileEvent(&fileEvent); + } + } +} + +NTSTATUS EtpRundownEtwMonitorThreadStart( + _In_ PVOID Parameter + ) +{ + EVENT_TRACE_LOGFILE logFile; + TRACEHANDLE traceHandle; + + memset(&logFile, 0, sizeof(EVENT_TRACE_LOGFILE)); + logFile.LoggerName = EtpRundownLoggerName.Buffer; + logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; + logFile.BufferCallback = EtpRundownEtwBufferCallback; + logFile.EventRecordCallback = EtpRundownEtwEventCallback; + logFile.Context = &traceHandle; + + traceHandle = OpenTrace(&logFile); + + if (traceHandle != INVALID_PROCESSTRACE_HANDLE) + { + ProcessTrace(&traceHandle, 1, NULL, NULL); + + if (traceHandle != INVALID_PROCESSTRACE_HANDLE) + CloseTrace(traceHandle); + } + + return STATUS_SUCCESS; +} diff --git a/plugins/ExtendedTools/etwmon.h b/plugins/ExtendedTools/etwmon.h index cb9bca0aee9d..860bcc4f57f7 100644 --- a/plugins/ExtendedTools/etwmon.h +++ b/plugins/ExtendedTools/etwmon.h @@ -1,140 +1,146 @@ -#ifndef ETWMON_H -#define ETWMON_H - -#include - -typedef struct -{ - ULONG DiskNumber; - ULONG IrpFlags; - ULONG TransferSize; - ULONG ResponseTime; - ULONG64 ByteOffset; - ULONG_PTR FileObject; - ULONG_PTR Irp; - ULONG64 HighResResponseTime; - ULONG IssuingThreadId; // since WIN8 (ETW_DISKIO_READWRITE_V3) -} DiskIo_TypeGroup1; - -typedef struct -{ - ULONG_PTR FileObject; - WCHAR FileName[1]; -} FileIo_Name; - -typedef struct -{ - ULONG PID; - ULONG size; - ULONG daddr; - ULONG saddr; - USHORT dport; - USHORT sport; -} TcpIpOrUdpIp_IPV4_Header; - -typedef struct -{ - ULONG PID; - ULONG size; - IN6_ADDR daddr; - IN6_ADDR saddr; - USHORT dport; - USHORT sport; -} TcpIpOrUdpIp_IPV6_Header; - -// etwmon - -VOID EtEtwMonitorInitialization( - VOID - ); - -VOID EtEtwMonitorUninitialization( - VOID - ); - -VOID EtStartEtwSession( - VOID - ); - -VOID EtStopEtwSession( - VOID - ); - -VOID EtFlushEtwSession( - VOID - ); - -ULONG EtStartEtwRundown( - VOID - ); - -// etwstat - -typedef enum _ET_ETW_EVENT_TYPE -{ - EtEtwDiskReadType = 1, - EtEtwDiskWriteType, - EtEtwFileNameType, - EtEtwFileCreateType, - EtEtwFileDeleteType, - EtEtwFileRundownType, - EtEtwNetworkReceiveType, - EtEtwNetworkSendType -} ET_ETW_EVENT_TYPE; - -typedef struct _ET_ETW_DISK_EVENT -{ - ET_ETW_EVENT_TYPE Type; - CLIENT_ID ClientId; - ULONG IrpFlags; - ULONG TransferSize; - PVOID FileObject; - ULONG64 HighResResponseTime; -} ET_ETW_DISK_EVENT, *PET_ETW_DISK_EVENT; - -typedef struct _ET_ETW_FILE_EVENT -{ - ET_ETW_EVENT_TYPE Type; - PVOID FileObject; - PH_STRINGREF FileName; -} ET_ETW_FILE_EVENT, *PET_ETW_FILE_EVENT; - -typedef struct _ET_ETW_NETWORK_EVENT -{ - ET_ETW_EVENT_TYPE Type; - CLIENT_ID ClientId; - ULONG ProtocolType; - ULONG TransferSize; - PH_IP_ENDPOINT LocalEndpoint; - PH_IP_ENDPOINT RemoteEndpoint; -} ET_ETW_NETWORK_EVENT, *PET_ETW_NETWORK_EVENT; - -// etwstat - -VOID EtProcessDiskEvent( - _In_ PET_ETW_DISK_EVENT Event - ); - -VOID EtProcessNetworkEvent( - _In_ PET_ETW_NETWORK_EVENT Event - ); - -VOID EtUpdateProcessInformation( - VOID - ); - -HANDLE EtThreadIdToProcessId( - _In_ HANDLE ThreadId - ); - -// etwdisk - -VOID EtDiskProcessDiskEvent( - _In_ PET_ETW_DISK_EVENT Event - ); - -VOID EtDiskProcessFileEvent( - _In_ PET_ETW_FILE_EVENT Event - ); - -#endif +#ifndef ETWMON_H +#define ETWMON_H + +#include + +typedef struct +{ + ULONG DiskNumber; + ULONG IrpFlags; + ULONG TransferSize; + ULONG ResponseTime; + ULONG64 ByteOffset; + ULONG_PTR FileObject; + ULONG_PTR Irp; + ULONG64 HighResResponseTime; + ULONG IssuingThreadId; // since WIN8 (ETW_DISKIO_READWRITE_V3) +} DiskIo_TypeGroup1; + +typedef struct +{ + ULONG_PTR FileObject; + WCHAR FileName[1]; +} FileIo_Name; + +typedef struct +{ + ULONGLONG FileObject; + WCHAR FileName[1]; +} FileIo_Name_Wow64; + +typedef struct +{ + ULONG PID; + ULONG size; + IN_ADDR daddr; + IN_ADDR saddr; + USHORT dport; + USHORT sport; +} TcpIpOrUdpIp_IPV4_Header; + +typedef struct +{ + ULONG PID; + ULONG size; + IN6_ADDR daddr; + IN6_ADDR saddr; + USHORT dport; + USHORT sport; +} TcpIpOrUdpIp_IPV6_Header; + +// etwmon + +VOID EtEtwMonitorInitialization( + VOID + ); + +VOID EtEtwMonitorUninitialization( + VOID + ); + +VOID EtStartEtwSession( + VOID + ); + +VOID EtStopEtwSession( + VOID + ); + +VOID EtFlushEtwSession( + VOID + ); + +ULONG EtStartEtwRundown( + VOID + ); + +// etwstat + +typedef enum _ET_ETW_EVENT_TYPE +{ + EtEtwDiskReadType = 1, + EtEtwDiskWriteType, + EtEtwFileNameType, + EtEtwFileCreateType, + EtEtwFileDeleteType, + EtEtwFileRundownType, + EtEtwNetworkReceiveType, + EtEtwNetworkSendType +} ET_ETW_EVENT_TYPE; + +typedef struct _ET_ETW_DISK_EVENT +{ + ET_ETW_EVENT_TYPE Type; + CLIENT_ID ClientId; + ULONG IrpFlags; + ULONG TransferSize; + PVOID FileObject; + ULONG64 HighResResponseTime; +} ET_ETW_DISK_EVENT, *PET_ETW_DISK_EVENT; + +typedef struct _ET_ETW_FILE_EVENT +{ + ET_ETW_EVENT_TYPE Type; + PVOID FileObject; + PH_STRINGREF FileName; +} ET_ETW_FILE_EVENT, *PET_ETW_FILE_EVENT; + +typedef struct _ET_ETW_NETWORK_EVENT +{ + ET_ETW_EVENT_TYPE Type; + CLIENT_ID ClientId; + ULONG ProtocolType; + ULONG TransferSize; + PH_IP_ENDPOINT LocalEndpoint; + PH_IP_ENDPOINT RemoteEndpoint; +} ET_ETW_NETWORK_EVENT, *PET_ETW_NETWORK_EVENT; + +// etwstat + +VOID EtProcessDiskEvent( + _In_ PET_ETW_DISK_EVENT Event + ); + +VOID EtProcessNetworkEvent( + _In_ PET_ETW_NETWORK_EVENT Event + ); + +VOID EtUpdateProcessInformation( + VOID + ); + +HANDLE EtThreadIdToProcessId( + _In_ HANDLE ThreadId + ); + +// etwdisk + +VOID EtDiskProcessDiskEvent( + _In_ PET_ETW_DISK_EVENT Event + ); + +VOID EtDiskProcessFileEvent( + _In_ PET_ETW_FILE_EVENT Event + ); + +#endif diff --git a/plugins/ExtendedTools/etwprprp.c b/plugins/ExtendedTools/etwprprp.c index 5d2ca9a1843b..07a36ccd62d9 100644 --- a/plugins/ExtendedTools/etwprprp.c +++ b/plugins/ExtendedTools/etwprprp.c @@ -1,615 +1,619 @@ -/* - * Process Hacker Extended Tools - - * ETW process properties page - * - * Copyright (C) 2010-2011 wj32 - * Copyright (C) 2015 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; -static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; - -typedef struct _ET_DISKNET_CONTEXT -{ - HWND WindowHandle; - PET_PROCESS_BLOCK Block; - PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - BOOLEAN Enabled; - - PH_LAYOUT_MANAGER LayoutManager; - - HWND DiskGroupBox; - HWND NetworkGroupBox; - - HWND DiskGraphHandle; - HWND NetworkGraphHandle; - HWND PanelHandle; - - ULONG64 CurrentDiskRead; - ULONG64 CurrentDiskWrite; - ULONG64 CurrentNetworkSend; - ULONG64 CurrentNetworkReceive; - - PH_GRAPH_STATE DiskGraphState; - PH_GRAPH_STATE NetworkGraphState; - - PH_CIRCULAR_BUFFER_ULONG64 DiskReadHistory; - PH_CIRCULAR_BUFFER_ULONG64 DiskWriteHistory; - PH_CIRCULAR_BUFFER_ULONG64 NetworkSendHistory; - PH_CIRCULAR_BUFFER_ULONG64 NetworkReceiveHistory; -} ET_DISKNET_CONTEXT, *PET_DISKNET_CONTEXT; - -INT_PTR CALLBACK EtwDiskNetworkPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return FALSE; -} - -VOID EtwDiskNetworkCreateGraphs( - _In_ PET_DISKNET_CONTEXT Context - ) -{ - Context->DiskGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - Context->WindowHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(Context->DiskGraphHandle, TRUE); - - Context->NetworkGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - Context->WindowHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(Context->NetworkGraphHandle, TRUE); -} - -VOID EtwDiskNetworkCreatePanel( - _In_ PET_DISKNET_CONTEXT Context - ) -{ - RECT margin; - - Context->PanelHandle = CreateDialogParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_PROCDISKNET_PANEL), - Context->WindowHandle, - EtwDiskNetworkPanelDialogProc, - (LPARAM)Context - ); - - SetWindowPos( - Context->PanelHandle, - NULL, - 10, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER - ); - - ShowWindow(Context->PanelHandle, SW_SHOW); - - margin.left = 0; - margin.top = 0; - margin.right = 0; - margin.bottom = 10; - MapDialogRect(Context->WindowHandle, &margin); - - PhAddLayoutItemEx( - &Context->LayoutManager, - Context->PanelHandle, - NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT, - margin - ); - - SendMessage(Context->WindowHandle, WM_SIZE, 0, 0); -} - -VOID EtwDiskNetworkLayoutGraphs( - _In_ PET_DISKNET_CONTEXT Context - ) -{ - HDWP deferHandle; - RECT clientRect; - RECT panelRect; - RECT margin = { ET_SCALE_DPI(13), ET_SCALE_DPI(13), ET_SCALE_DPI(13), ET_SCALE_DPI(13) }; - RECT innerMargin = { ET_SCALE_DPI(10), ET_SCALE_DPI(20), ET_SCALE_DPI(10), ET_SCALE_DPI(10) }; - LONG between = ET_SCALE_DPI(3); - ULONG graphWidth; - ULONG graphHeight; - - PhLayoutManagerLayout(&Context->LayoutManager); - - Context->DiskGraphState.Valid = FALSE; - Context->NetworkGraphState.Valid = FALSE; - - GetClientRect(Context->WindowHandle, &clientRect); - - // Limit the rectangle bottom to the top of the panel. - GetWindowRect(Context->PanelHandle, &panelRect); - MapWindowPoints(NULL, Context->WindowHandle, (PPOINT)&panelRect, 2); - clientRect.bottom = panelRect.top + 10; // +10 removing extra spacing - - graphWidth = clientRect.right - margin.left - margin.right; - graphHeight = (clientRect.bottom - margin.top - margin.bottom - between * 2) / 2; - - deferHandle = BeginDeferWindowPos(4); - - deferHandle = DeferWindowPos(deferHandle, Context->DiskGroupBox, NULL, margin.left, margin.top, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); - deferHandle = DeferWindowPos( - deferHandle, - Context->DiskGraphHandle, - NULL, - margin.left + innerMargin.left, - margin.top + innerMargin.top, - graphWidth - innerMargin.left - innerMargin.right, - graphHeight - innerMargin.top - innerMargin.bottom, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - deferHandle = DeferWindowPos(deferHandle, Context->NetworkGroupBox, NULL, margin.left, margin.top + graphHeight + between, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); - deferHandle = DeferWindowPos( - deferHandle, - Context->NetworkGraphHandle, - NULL, - margin.left + innerMargin.left, - margin.top + graphHeight + between + innerMargin.top, - graphWidth - innerMargin.left - innerMargin.right, - graphHeight - innerMargin.top - innerMargin.bottom, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - EndDeferWindowPos(deferHandle); -} - -VOID EtwDiskNetworkUpdateGraphs( - _In_ PET_DISKNET_CONTEXT Context - ) -{ - Context->DiskGraphState.Valid = FALSE; - Context->DiskGraphState.TooltipIndex = -1; - Graph_MoveGrid(Context->DiskGraphHandle, 1); - Graph_Draw(Context->DiskGraphHandle); - Graph_UpdateTooltip(Context->DiskGraphHandle); - InvalidateRect(Context->DiskGraphHandle, NULL, FALSE); - - Context->NetworkGraphState.Valid = FALSE; - Context->NetworkGraphState.TooltipIndex = -1; - Graph_MoveGrid(Context->NetworkGraphHandle, 1); - Graph_Draw(Context->NetworkGraphHandle); - Graph_UpdateTooltip(Context->NetworkGraphHandle); - InvalidateRect(Context->NetworkGraphHandle, NULL, FALSE); -} - -VOID EtwDiskNetworkUpdatePanel( - _Inout_ PET_DISKNET_CONTEXT Context - ) -{ - PET_PROCESS_BLOCK block = Context->Block; - - SetDlgItemText(Context->PanelHandle, IDC_ZREADS_V, PhaFormatUInt64(block->DiskReadCount, TRUE)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZREADBYTES_V, PhaFormatSize(block->DiskReadRawDelta.Value, -1)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZREADBYTESDELTA_V, PhaFormatSize(block->DiskReadRawDelta.Delta, -1)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZWRITES_V, PhaFormatUInt64(block->DiskWriteCount, TRUE)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZWRITEBYTES_V, PhaFormatSize(block->DiskWriteRawDelta.Value, -1)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(block->DiskWriteRawDelta.Delta, -1)->Buffer); - - SetDlgItemText(Context->PanelHandle, IDC_ZRECEIVES_V, PhaFormatUInt64(block->NetworkReceiveCount, TRUE)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZRECEIVEBYTES_V, PhaFormatSize(block->NetworkReceiveRawDelta.Value, -1)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZRECEIVEBYTESDELTA_V, PhaFormatSize(block->NetworkReceiveRawDelta.Delta, -1)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZSENDS_V, PhaFormatUInt64(block->NetworkSendCount, TRUE)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZSENDBYTES_V, PhaFormatSize(block->NetworkSendRawDelta.Value, -1)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZSENDBYTESDELTA_V, PhaFormatSize(block->NetworkSendRawDelta.Delta, -1)->Buffer); -} - -VOID EtwDiskNetworkUpdateInfo( - _In_ PET_DISKNET_CONTEXT Context - ) -{ - PET_PROCESS_BLOCK block = Context->Block; - - Context->CurrentDiskRead = block->DiskReadRawDelta.Delta; - Context->CurrentDiskWrite = block->DiskWriteRawDelta.Delta; - Context->CurrentNetworkSend = block->NetworkSendRawDelta.Delta; - Context->CurrentNetworkReceive = block->NetworkReceiveRawDelta.Delta; - - PhAddItemCircularBuffer_ULONG64(&Context->DiskReadHistory, Context->CurrentDiskRead); - PhAddItemCircularBuffer_ULONG64(&Context->DiskWriteHistory, Context->CurrentDiskWrite); - PhAddItemCircularBuffer_ULONG64(&Context->NetworkSendHistory, Context->CurrentNetworkSend); - PhAddItemCircularBuffer_ULONG64(&Context->NetworkReceiveHistory, Context->CurrentNetworkReceive); -} - -VOID NTAPI EtwDiskNetworkUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PET_DISKNET_CONTEXT context = Context; - - if (!context->Enabled) - return; - - if (context->WindowHandle) - { - PostMessage(context->WindowHandle, UPDATE_MSG, 0, 0); - } -} - -INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - PET_DISKNET_CONTEXT context; - - if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) - { - context = propPageContext->Context; - } - else - { - return FALSE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG sampleCount; - - sampleCount = PhGetIntegerSetting(L"SampleCount"); - - context = PhAllocate(sizeof(ET_DISKNET_CONTEXT)); - memset(context, 0, sizeof(ET_DISKNET_CONTEXT)); - - context->WindowHandle = hwndDlg; - context->Block = EtGetProcessBlock(processItem); - context->Enabled = TRUE; - context->DiskGroupBox = GetDlgItem(hwndDlg, IDC_GROUPDISK); - context->NetworkGroupBox = GetDlgItem(hwndDlg, IDC_GROUPNETWORK); - propPageContext->Context = context; - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - - PhInitializeGraphState(&context->DiskGraphState); - PhInitializeGraphState(&context->NetworkGraphState); - - PhInitializeCircularBuffer_ULONG64(&context->DiskReadHistory, sampleCount); - PhInitializeCircularBuffer_ULONG64(&context->DiskWriteHistory, sampleCount); - PhInitializeCircularBuffer_ULONG64(&context->NetworkSendHistory, sampleCount); - PhInitializeCircularBuffer_ULONG64(&context->NetworkReceiveHistory, sampleCount); - - EtwDiskNetworkCreateGraphs(context); - EtwDiskNetworkCreatePanel(context); - EtwDiskNetworkUpdateInfo(context); - EtwDiskNetworkUpdatePanel(context); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - EtwDiskNetworkUpdateHandler, - context, - &context->ProcessesUpdatedRegistration - ); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&context->LayoutManager); - - PhDeleteGraphState(&context->DiskGraphState); - PhDeleteGraphState(&context->NetworkGraphState); - - PhDeleteCircularBuffer_ULONG64(&context->DiskReadHistory); - PhDeleteCircularBuffer_ULONG64(&context->DiskWriteHistory); - PhDeleteCircularBuffer_ULONG64(&context->NetworkSendHistory); - PhDeleteCircularBuffer_ULONG64(&context->NetworkReceiveHistory); - - if (context->DiskGraphHandle) - DestroyWindow(context->DiskGraphHandle); - if (context->NetworkGraphHandle) - DestroyWindow(context->NetworkGraphHandle); - if (context->PanelHandle) - DestroyWindow(context->PanelHandle); - - PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); - PhFree(context); - - PhPropPageDlgProcDestroy(hwndDlg); - } - break; - case WM_SHOWWINDOW: - { - if (PhBeginPropPageLayout(hwndDlg, propPageContext)) - PhEndPropPageLayout(hwndDlg, propPageContext); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_SETACTIVE: - context->Enabled = TRUE; - break; - case PSN_KILLACTIVE: - context->Enabled = FALSE; - break; - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - if (header->hwndFrom == context->DiskGraphHandle) - { - if (PhGetIntegerSetting(L"GraphShowText")) - { - HDC hdc; - - PhMoveReference(&context->DiskGraphState.Text, PhFormatString( - L"R: %s, W: %s", - PhaFormatSize(context->CurrentDiskRead, -1)->Buffer, - PhaFormatSize(context->CurrentDiskWrite, -1)->Buffer - )); - - hdc = Graph_GetBufferedContext(context->DiskGraphHandle); - SelectObject(hdc, PhApplicationFont); - PhSetGraphText(hdc, drawInfo, &context->DiskGraphState.Text->sr, - &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); - } - else - { - drawInfo->Text.Buffer = NULL; - } - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - PhGraphStateGetDrawInfo(&context->DiskGraphState, getDrawInfo, context->DiskReadHistory.Count); - - if (!context->DiskGraphState.Valid) - { - FLOAT max = 0; - - for (ULONG i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - context->DiskGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->DiskReadHistory, i); - context->DiskGraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->DiskWriteHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - // Minimum scaling of 1 MB. - //if (max < 1024 * 1024) - // max = 1024 * 1024; - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - context->DiskGraphState.Data1, - max, - drawInfo->LineDataCount - ); - PhDivideSinglesBySingle( - context->DiskGraphState.Data2, - max, - drawInfo->LineDataCount - ); - } - - drawInfo->LabelYFunction = PhSiSizeLabelYFunction; - drawInfo->LabelYFunctionParameter = max; - - context->DiskGraphState.Valid = TRUE; - } - } - else if (header->hwndFrom == context->NetworkGraphHandle) - { - if (PhGetIntegerSetting(L"GraphShowText")) - { - HDC hdc; - - PhMoveReference(&context->NetworkGraphState.Text, PhFormatString( - L"R: %s, S: %s", - PhaFormatSize(context->CurrentNetworkReceive, -1)->Buffer, - PhaFormatSize(context->CurrentNetworkSend, -1)->Buffer - )); - - hdc = Graph_GetBufferedContext(context->NetworkGraphHandle); - SelectObject(hdc, PhApplicationFont); - PhSetGraphText(hdc, drawInfo, &context->NetworkGraphState.Text->sr, - &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); - } - else - { - drawInfo->Text.Buffer = NULL; - } - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - PhGraphStateGetDrawInfo(&context->NetworkGraphState, getDrawInfo, context->NetworkSendHistory.Count); - - if (!context->NetworkGraphState.Valid) - { - FLOAT max = 0; - - for (ULONG i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - context->NetworkGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->NetworkReceiveHistory, i); - context->NetworkGraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->NetworkSendHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - // Minimum scaling of 1 MB. - //if (max < 1024 * 1024) - // max = 1024 * 1024; - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - context->NetworkGraphState.Data1, - max, - drawInfo->LineDataCount - ); - PhDivideSinglesBySingle( - context->NetworkGraphState.Data2, - max, - drawInfo->LineDataCount - ); - } - - drawInfo->LabelYFunction = PhSiSizeLabelYFunction; - drawInfo->LabelYFunctionParameter = max; - - context->NetworkGraphState.Valid = TRUE; - } - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (header->hwndFrom == context->DiskGraphHandle) - { - if (context->DiskGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG64 diskRead = PhGetItemCircularBuffer_ULONG64( - &context->DiskReadHistory, - getTooltipText->Index - ); - - ULONG64 diskWrite = PhGetItemCircularBuffer_ULONG64( - &context->DiskWriteHistory, - getTooltipText->Index - ); - - PhMoveReference(&context->DiskGraphState.TooltipText, PhFormatString( - L"R: %s\nW: %s\n%s", - PhaFormatSize(diskRead, -1)->Buffer, - PhaFormatSize(diskWrite, -1)->Buffer, - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = context->DiskGraphState.TooltipText->sr; - } - else if (header->hwndFrom == context->NetworkGraphHandle) - { - if (context->NetworkGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG64 networkSend = PhGetItemCircularBuffer_ULONG64( - &context->NetworkSendHistory, - getTooltipText->Index - ); - - ULONG64 networkReceive = PhGetItemCircularBuffer_ULONG64( - &context->NetworkReceiveHistory, - getTooltipText->Index - ); - - PhMoveReference(&context->NetworkGraphState.TooltipText, PhFormatString( - L"S: %s\nR: %s\n%s", - PhaFormatSize(networkSend, -1)->Buffer, - PhaFormatSize(networkReceive, -1)->Buffer, - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = context->NetworkGraphState.TooltipText->sr; - } - } - } - break; - } - } - break; - case UPDATE_MSG: - { - if (context->Enabled) - { - EtwDiskNetworkUpdateInfo(context); - EtwDiskNetworkUpdateGraphs(context); - EtwDiskNetworkUpdatePanel(context); - } - } - break; - case WM_SIZE: - { - EtwDiskNetworkLayoutGraphs(context); - } - break; - } - - return FALSE; -} - -VOID EtProcessEtwPropertiesInitializing( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; - - if (EtEtwEnabled) - { - PhAddProcessPropPage( - propContext->PropContext, - PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDISKNET), EtwDiskNetworkPageDlgProc, NULL) - ); - } -} \ No newline at end of file +/* + * Process Hacker Extended Tools - + * ETW process properties page + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2015 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; +static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; + +typedef struct _ET_DISKNET_CONTEXT +{ + HWND WindowHandle; + PET_PROCESS_BLOCK Block; + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + BOOLEAN Enabled; + + PH_LAYOUT_MANAGER LayoutManager; + + HWND DiskGroupBox; + HWND NetworkGroupBox; + + HWND DiskGraphHandle; + HWND NetworkGraphHandle; + HWND PanelHandle; + + ULONG64 CurrentDiskRead; + ULONG64 CurrentDiskWrite; + ULONG64 CurrentNetworkSend; + ULONG64 CurrentNetworkReceive; + + PH_GRAPH_STATE DiskGraphState; + PH_GRAPH_STATE NetworkGraphState; + + PH_CIRCULAR_BUFFER_ULONG64 DiskReadHistory; + PH_CIRCULAR_BUFFER_ULONG64 DiskWriteHistory; + PH_CIRCULAR_BUFFER_ULONG64 NetworkSendHistory; + PH_CIRCULAR_BUFFER_ULONG64 NetworkReceiveHistory; +} ET_DISKNET_CONTEXT, *PET_DISKNET_CONTEXT; + +INT_PTR CALLBACK EtwDiskNetworkPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return FALSE; +} + +VOID EtwDiskNetworkCreateGraphs( + _In_ PET_DISKNET_CONTEXT Context + ) +{ + Context->DiskGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS, + 0, + 0, + 3, + 3, + Context->WindowHandle, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(Context->DiskGraphHandle, TRUE); + + Context->NetworkGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS, + 0, + 0, + 3, + 3, + Context->WindowHandle, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(Context->NetworkGraphHandle, TRUE); +} + +VOID EtwDiskNetworkCreatePanel( + _In_ PET_DISKNET_CONTEXT Context + ) +{ + RECT margin; + + Context->PanelHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_PROCDISKNET_PANEL), + Context->WindowHandle, + EtwDiskNetworkPanelDialogProc, + (LPARAM)Context + ); + + SetWindowPos( + Context->PanelHandle, + NULL, + 10, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER + ); + + ShowWindow(Context->PanelHandle, SW_SHOW); + + margin.left = 0; + margin.top = 0; + margin.right = 0; + margin.bottom = 10; + MapDialogRect(Context->WindowHandle, &margin); + + PhAddLayoutItemEx( + &Context->LayoutManager, + Context->PanelHandle, + NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT, + margin + ); + + SendMessage(Context->WindowHandle, WM_SIZE, 0, 0); +} + +VOID EtwDiskNetworkLayoutGraphs( + _In_ PET_DISKNET_CONTEXT Context + ) +{ + HDWP deferHandle; + RECT clientRect; + RECT panelRect; + RECT margin = { PH_SCALE_DPI(13), PH_SCALE_DPI(13), PH_SCALE_DPI(13), PH_SCALE_DPI(13) }; + RECT innerMargin = { PH_SCALE_DPI(10), PH_SCALE_DPI(20), PH_SCALE_DPI(10), PH_SCALE_DPI(10) }; + LONG between = PH_SCALE_DPI(3); + ULONG graphWidth; + ULONG graphHeight; + + PhLayoutManagerLayout(&Context->LayoutManager); + + Context->DiskGraphState.Valid = FALSE; + Context->NetworkGraphState.Valid = FALSE; + + GetClientRect(Context->WindowHandle, &clientRect); + + // Limit the rectangle bottom to the top of the panel. + GetWindowRect(Context->PanelHandle, &panelRect); + MapWindowPoints(NULL, Context->WindowHandle, (PPOINT)&panelRect, 2); + clientRect.bottom = panelRect.top + 10; // +10 removing extra spacing + + graphWidth = clientRect.right - margin.left - margin.right; + graphHeight = (clientRect.bottom - margin.top - margin.bottom - between * 2) / 2; + + deferHandle = BeginDeferWindowPos(4); + + deferHandle = DeferWindowPos(deferHandle, Context->DiskGroupBox, NULL, margin.left, margin.top, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); + deferHandle = DeferWindowPos( + deferHandle, + Context->DiskGraphHandle, + NULL, + margin.left + innerMargin.left, + margin.top + innerMargin.top, + graphWidth - innerMargin.left - innerMargin.right, + graphHeight - innerMargin.top - innerMargin.bottom, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + deferHandle = DeferWindowPos(deferHandle, Context->NetworkGroupBox, NULL, margin.left, margin.top + graphHeight + between, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); + deferHandle = DeferWindowPos( + deferHandle, + Context->NetworkGraphHandle, + NULL, + margin.left + innerMargin.left, + margin.top + graphHeight + between + innerMargin.top, + graphWidth - innerMargin.left - innerMargin.right, + graphHeight - innerMargin.top - innerMargin.bottom, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + EndDeferWindowPos(deferHandle); +} + +VOID EtwDiskNetworkUpdateGraphs( + _In_ PET_DISKNET_CONTEXT Context + ) +{ + Context->DiskGraphState.Valid = FALSE; + Context->DiskGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(Context->DiskGraphHandle, 1); + Graph_Draw(Context->DiskGraphHandle); + Graph_UpdateTooltip(Context->DiskGraphHandle); + InvalidateRect(Context->DiskGraphHandle, NULL, FALSE); + + Context->NetworkGraphState.Valid = FALSE; + Context->NetworkGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(Context->NetworkGraphHandle, 1); + Graph_Draw(Context->NetworkGraphHandle); + Graph_UpdateTooltip(Context->NetworkGraphHandle); + InvalidateRect(Context->NetworkGraphHandle, NULL, FALSE); +} + +VOID EtwDiskNetworkUpdatePanel( + _Inout_ PET_DISKNET_CONTEXT Context + ) +{ + PET_PROCESS_BLOCK block = Context->Block; + + PhSetDialogItemText(Context->PanelHandle, IDC_ZREADS_V, PhaFormatUInt64(block->DiskReadCount, TRUE)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZREADBYTES_V, PhaFormatSize(block->DiskReadRawDelta.Value, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZREADBYTESDELTA_V, PhaFormatSize(block->DiskReadRawDelta.Delta, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZWRITES_V, PhaFormatUInt64(block->DiskWriteCount, TRUE)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZWRITEBYTES_V, PhaFormatSize(block->DiskWriteRawDelta.Value, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(block->DiskWriteRawDelta.Delta, ULONG_MAX)->Buffer); + + PhSetDialogItemText(Context->PanelHandle, IDC_ZRECEIVES_V, PhaFormatUInt64(block->NetworkReceiveCount, TRUE)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZRECEIVEBYTES_V, PhaFormatSize(block->NetworkReceiveRawDelta.Value, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZRECEIVEBYTESDELTA_V, PhaFormatSize(block->NetworkReceiveRawDelta.Delta, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZSENDS_V, PhaFormatUInt64(block->NetworkSendCount, TRUE)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZSENDBYTES_V, PhaFormatSize(block->NetworkSendRawDelta.Value, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZSENDBYTESDELTA_V, PhaFormatSize(block->NetworkSendRawDelta.Delta, ULONG_MAX)->Buffer); +} + +VOID EtwDiskNetworkUpdateInfo( + _In_ PET_DISKNET_CONTEXT Context + ) +{ + PET_PROCESS_BLOCK block = Context->Block; + + Context->CurrentDiskRead = block->DiskReadRawDelta.Delta; + Context->CurrentDiskWrite = block->DiskWriteRawDelta.Delta; + Context->CurrentNetworkSend = block->NetworkSendRawDelta.Delta; + Context->CurrentNetworkReceive = block->NetworkReceiveRawDelta.Delta; + + PhAddItemCircularBuffer_ULONG64(&Context->DiskReadHistory, Context->CurrentDiskRead); + PhAddItemCircularBuffer_ULONG64(&Context->DiskWriteHistory, Context->CurrentDiskWrite); + PhAddItemCircularBuffer_ULONG64(&Context->NetworkSendHistory, Context->CurrentNetworkSend); + PhAddItemCircularBuffer_ULONG64(&Context->NetworkReceiveHistory, Context->CurrentNetworkReceive); +} + +VOID NTAPI EtwDiskNetworkUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PET_DISKNET_CONTEXT context = Context; + + if (!context->Enabled) + return; + + if (context->WindowHandle) + { + PostMessage(context->WindowHandle, ET_WM_UPDATE, 0, 0); + } +} + +INT_PTR CALLBACK EtwDiskNetworkPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PET_DISKNET_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG sampleCount; + + // We have already set the group boxes to have WS_EX_TRANSPARENT to fix + // the drawing issue that arises when using WS_CLIPCHILDREN. However + // in removing the flicker from the graphs the group boxes will now flicker. + // It's a good tradeoff since no one stares at the group boxes. + PhSetWindowStyle(hwndDlg, WS_CLIPCHILDREN, WS_CLIPCHILDREN); + + sampleCount = PhGetIntegerSetting(L"SampleCount"); + + context = PhAllocateZero(sizeof(ET_DISKNET_CONTEXT)); + context->WindowHandle = hwndDlg; + context->Block = EtGetProcessBlock(processItem); + context->Enabled = TRUE; + context->DiskGroupBox = GetDlgItem(hwndDlg, IDC_GROUPDISK); + context->NetworkGroupBox = GetDlgItem(hwndDlg, IDC_GROUPNETWORK); + propPageContext->Context = context; + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + + PhInitializeGraphState(&context->DiskGraphState); + PhInitializeGraphState(&context->NetworkGraphState); + + PhInitializeCircularBuffer_ULONG64(&context->DiskReadHistory, sampleCount); + PhInitializeCircularBuffer_ULONG64(&context->DiskWriteHistory, sampleCount); + PhInitializeCircularBuffer_ULONG64(&context->NetworkSendHistory, sampleCount); + PhInitializeCircularBuffer_ULONG64(&context->NetworkReceiveHistory, sampleCount); + + EtwDiskNetworkCreateGraphs(context); + EtwDiskNetworkCreatePanel(context); + EtwDiskNetworkUpdateInfo(context); + EtwDiskNetworkUpdatePanel(context); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + EtwDiskNetworkUpdateHandler, + context, + &context->ProcessesUpdatedRegistration + ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + PhDeleteGraphState(&context->DiskGraphState); + PhDeleteGraphState(&context->NetworkGraphState); + + PhDeleteCircularBuffer_ULONG64(&context->DiskReadHistory); + PhDeleteCircularBuffer_ULONG64(&context->DiskWriteHistory); + PhDeleteCircularBuffer_ULONG64(&context->NetworkSendHistory); + PhDeleteCircularBuffer_ULONG64(&context->NetworkReceiveHistory); + + if (context->DiskGraphHandle) + DestroyWindow(context->DiskGraphHandle); + if (context->NetworkGraphHandle) + DestroyWindow(context->NetworkGraphHandle); + if (context->PanelHandle) + DestroyWindow(context->PanelHandle); + + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &context->ProcessesUpdatedRegistration); + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + if (PhBeginPropPageLayout(hwndDlg, propPageContext)) + PhEndPropPageLayout(hwndDlg, propPageContext); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_SETACTIVE: + context->Enabled = TRUE; + break; + case PSN_KILLACTIVE: + context->Enabled = FALSE; + break; + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + if (header->hwndFrom == context->DiskGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + + PhMoveReference(&context->DiskGraphState.Text, PhFormatString( + L"R: %s, W: %s", + PhaFormatSize(context->CurrentDiskRead, ULONG_MAX)->Buffer, + PhaFormatSize(context->CurrentDiskWrite, ULONG_MAX)->Buffer + )); + + hdc = Graph_GetBufferedContext(context->DiskGraphHandle); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &context->DiskGraphState.Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + PhGraphStateGetDrawInfo(&context->DiskGraphState, getDrawInfo, context->DiskReadHistory.Count); + + if (!context->DiskGraphState.Valid) + { + FLOAT max = 0; + + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + context->DiskGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->DiskReadHistory, i); + context->DiskGraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->DiskWriteHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + // Minimum scaling of 1 MB. + //if (max < 1024 * 1024) + // max = 1024 * 1024; + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + context->DiskGraphState.Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + context->DiskGraphState.Data2, + max, + drawInfo->LineDataCount + ); + } + + drawInfo->LabelYFunction = PhSiSizeLabelYFunction; + drawInfo->LabelYFunctionParameter = max; + + context->DiskGraphState.Valid = TRUE; + } + } + else if (header->hwndFrom == context->NetworkGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + + PhMoveReference(&context->NetworkGraphState.Text, PhFormatString( + L"R: %s, S: %s", + PhaFormatSize(context->CurrentNetworkReceive, ULONG_MAX)->Buffer, + PhaFormatSize(context->CurrentNetworkSend, ULONG_MAX)->Buffer + )); + + hdc = Graph_GetBufferedContext(context->NetworkGraphHandle); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &context->NetworkGraphState.Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + PhGraphStateGetDrawInfo(&context->NetworkGraphState, getDrawInfo, context->NetworkSendHistory.Count); + + if (!context->NetworkGraphState.Valid) + { + FLOAT max = 0; + + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + context->NetworkGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->NetworkReceiveHistory, i); + context->NetworkGraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->NetworkSendHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + // Minimum scaling of 1 MB. + //if (max < 1024 * 1024) + // max = 1024 * 1024; + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + context->NetworkGraphState.Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + context->NetworkGraphState.Data2, + max, + drawInfo->LineDataCount + ); + } + + drawInfo->LabelYFunction = PhSiSizeLabelYFunction; + drawInfo->LabelYFunctionParameter = max; + + context->NetworkGraphState.Valid = TRUE; + } + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (header->hwndFrom == context->DiskGraphHandle) + { + if (context->DiskGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG64 diskRead = PhGetItemCircularBuffer_ULONG64( + &context->DiskReadHistory, + getTooltipText->Index + ); + + ULONG64 diskWrite = PhGetItemCircularBuffer_ULONG64( + &context->DiskWriteHistory, + getTooltipText->Index + ); + + PhMoveReference(&context->DiskGraphState.TooltipText, PhFormatString( + L"R: %s\nW: %s\n%s", + PhaFormatSize(diskRead, ULONG_MAX)->Buffer, + PhaFormatSize(diskWrite, ULONG_MAX)->Buffer, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(context->DiskGraphState.TooltipText); + } + else if (header->hwndFrom == context->NetworkGraphHandle) + { + if (context->NetworkGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG64 networkSend = PhGetItemCircularBuffer_ULONG64( + &context->NetworkSendHistory, + getTooltipText->Index + ); + + ULONG64 networkReceive = PhGetItemCircularBuffer_ULONG64( + &context->NetworkReceiveHistory, + getTooltipText->Index + ); + + PhMoveReference(&context->NetworkGraphState.TooltipText, PhFormatString( + L"S: %s\nR: %s\n%s", + PhaFormatSize(networkSend, ULONG_MAX)->Buffer, + PhaFormatSize(networkReceive, ULONG_MAX)->Buffer, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(context->NetworkGraphState.TooltipText); + } + } + } + break; + } + } + break; + case ET_WM_UPDATE: + { + if (context->Enabled) + { + EtwDiskNetworkUpdateInfo(context); + EtwDiskNetworkUpdateGraphs(context); + EtwDiskNetworkUpdatePanel(context); + } + } + break; + case WM_SIZE: + { + EtwDiskNetworkLayoutGraphs(context); + } + break; + } + + return FALSE; +} + +VOID EtProcessEtwPropertiesInitializing( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; + + if (EtEtwEnabled) + { + PhAddProcessPropPage( + propContext->PropContext, + PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDISKNET), EtwDiskNetworkPageDlgProc, NULL) + ); + } +} diff --git a/plugins/ExtendedTools/etwstat.c b/plugins/ExtendedTools/etwstat.c index 8238528249a8..0d07799b5230 100644 --- a/plugins/ExtendedTools/etwstat.c +++ b/plugins/ExtendedTools/etwstat.c @@ -1,419 +1,441 @@ -/* - * Process Hacker Extended Tools - - * ETW statistics collection - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "etwmon.h" - -VOID NTAPI EtEtwProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI EtEtwNetworkItemsUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -static PH_CALLBACK_REGISTRATION EtpProcessesUpdatedCallbackRegistration; -static PH_CALLBACK_REGISTRATION EtpNetworkItemsUpdatedCallbackRegistration; - -ULONG EtpDiskReadRaw; -ULONG EtpDiskWriteRaw; -ULONG EtpNetworkReceiveRaw; -ULONG EtpNetworkSendRaw; - -ULONG EtDiskReadCount; -ULONG EtDiskWriteCount; -ULONG EtNetworkReceiveCount; -ULONG EtNetworkSendCount; - -PH_UINT32_DELTA EtDiskReadDelta; -PH_UINT32_DELTA EtDiskWriteDelta; -PH_UINT32_DELTA EtNetworkReceiveDelta; -PH_UINT32_DELTA EtNetworkSendDelta; - -PH_UINT32_DELTA EtDiskReadCountDelta; -PH_UINT32_DELTA EtDiskWriteCountDelta; -PH_UINT32_DELTA EtNetworkReceiveCountDelta; -PH_UINT32_DELTA EtNetworkSendCountDelta; - -PH_CIRCULAR_BUFFER_ULONG EtDiskReadHistory; -PH_CIRCULAR_BUFFER_ULONG EtDiskWriteHistory; -PH_CIRCULAR_BUFFER_ULONG EtNetworkReceiveHistory; -PH_CIRCULAR_BUFFER_ULONG EtNetworkSendHistory; -PH_CIRCULAR_BUFFER_ULONG EtMaxDiskHistory; // ID of max. disk usage process -PH_CIRCULAR_BUFFER_ULONG EtMaxNetworkHistory; // ID of max. network usage process - -PVOID EtpProcessInformation; -PH_QUEUED_LOCK EtpProcessInformationLock = PH_QUEUED_LOCK_INIT; - -VOID EtEtwStatisticsInitialization( - VOID - ) -{ - EtEtwMonitorInitialization(); - - if (EtEtwEnabled) - { - ULONG sampleCount; - - sampleCount = PhGetIntegerSetting(L"SampleCount"); - PhInitializeCircularBuffer_ULONG(&EtDiskReadHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtDiskWriteHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtNetworkReceiveHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtNetworkSendHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtMaxDiskHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtMaxNetworkHistory, sampleCount); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - EtEtwProcessesUpdatedCallback, - NULL, - &EtpProcessesUpdatedCallbackRegistration - ); - PhRegisterCallback( - &PhNetworkItemsUpdatedEvent, - EtEtwNetworkItemsUpdatedCallback, - NULL, - &EtpNetworkItemsUpdatedCallbackRegistration - ); - } -} - -VOID EtEtwStatisticsUninitialization( - VOID - ) -{ - EtEtwMonitorUninitialization(); -} - -VOID EtProcessDiskEvent( - _In_ PET_ETW_DISK_EVENT Event - ) -{ - PPH_PROCESS_ITEM processItem; - PET_PROCESS_BLOCK block; - - if (Event->Type == EtEtwDiskReadType) - { - EtpDiskReadRaw += Event->TransferSize; - EtDiskReadCount++; - } - else - { - EtpDiskWriteRaw += Event->TransferSize; - EtDiskWriteCount++; - } - - if (processItem = PhReferenceProcessItem(Event->ClientId.UniqueProcess)) - { - block = EtGetProcessBlock(processItem); - - if (Event->Type == EtEtwDiskReadType) - { - block->DiskReadRaw += Event->TransferSize; - block->DiskReadCount++; - } - else - { - block->DiskWriteRaw += Event->TransferSize; - block->DiskWriteCount++; - } - - PhDereferenceObject(processItem); - } -} - -VOID EtProcessNetworkEvent( - _In_ PET_ETW_NETWORK_EVENT Event - ) -{ - PPH_PROCESS_ITEM processItem; - PET_PROCESS_BLOCK block; - PPH_NETWORK_ITEM networkItem; - PET_NETWORK_BLOCK networkBlock; - - if (Event->Type == EtEtwNetworkReceiveType) - { - EtpNetworkReceiveRaw += Event->TransferSize; - EtNetworkReceiveCount++; - } - else - { - EtpNetworkSendRaw += Event->TransferSize; - EtNetworkSendCount++; - } - - // Note: there is always the possibility of us receiving the event too early, - // before the process item or network item is created. So events may be lost. - - if (processItem = PhReferenceProcessItem(Event->ClientId.UniqueProcess)) - { - block = EtGetProcessBlock(processItem); - - if (Event->Type == EtEtwNetworkReceiveType) - { - block->NetworkReceiveRaw += Event->TransferSize; - block->NetworkReceiveCount++; - } - else - { - block->NetworkSendRaw += Event->TransferSize; - block->NetworkSendCount++; - } - - PhDereferenceObject(processItem); - } - - if (networkItem = PhReferenceNetworkItem( - Event->ProtocolType, - &Event->LocalEndpoint, - &Event->RemoteEndpoint, - Event->ClientId.UniqueProcess - )) - { - networkBlock = EtGetNetworkBlock(networkItem); - - if (Event->Type == EtEtwNetworkReceiveType) - { - networkBlock->ReceiveRaw += Event->TransferSize; - networkBlock->ReceiveCount++; - } - else - { - networkBlock->SendRaw += Event->TransferSize; - networkBlock->SendCount++; - } - - PhDereferenceObject(networkItem); - } -} - -VOID NTAPI EtEtwProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - static ULONG runCount = 0; // MUST keep in sync with runCount in process provider - - PLIST_ENTRY listEntry; - ULONG64 maxDiskValue = 0; - PET_PROCESS_BLOCK maxDiskBlock = NULL; - ULONG64 maxNetworkValue = 0; - PET_PROCESS_BLOCK maxNetworkBlock = NULL; - - // Since Windows 8, we no longer get the correct process/thread IDs in the - // event headers for disk events. We need to update our process information since - // etwmon uses our EtThreadIdToProcessId function. - if (WindowsVersion >= WINDOWS_8) - EtUpdateProcessInformation(); - - // ETW is extremely lazy when it comes to flushing buffers, so we must do it - // manually. - EtFlushEtwSession(); - - // Update global statistics. - - PhUpdateDelta(&EtDiskReadDelta, EtpDiskReadRaw); - PhUpdateDelta(&EtDiskWriteDelta, EtpDiskWriteRaw); - PhUpdateDelta(&EtNetworkReceiveDelta, EtpNetworkReceiveRaw); - PhUpdateDelta(&EtNetworkSendDelta, EtpNetworkSendRaw); - - PhUpdateDelta(&EtDiskReadCountDelta, EtDiskReadCount); - PhUpdateDelta(&EtDiskWriteCountDelta, EtDiskWriteCount); - PhUpdateDelta(&EtNetworkReceiveCountDelta, EtNetworkReceiveCount); - PhUpdateDelta(&EtNetworkSendCountDelta, EtNetworkSendCount); - - // Update per-process statistics. - // Note: no lock is needed because we only ever modify the list on this same thread. - - listEntry = EtProcessBlockListHead.Flink; - - while (listEntry != &EtProcessBlockListHead) - { - PET_PROCESS_BLOCK block; - - block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); - - PhUpdateDelta(&block->DiskReadDelta, block->DiskReadCount); - PhUpdateDelta(&block->DiskReadRawDelta, block->DiskReadRaw); - PhUpdateDelta(&block->DiskWriteDelta, block->DiskWriteCount); - PhUpdateDelta(&block->DiskWriteRawDelta, block->DiskWriteRaw); - PhUpdateDelta(&block->NetworkReceiveDelta, block->NetworkReceiveCount); - PhUpdateDelta(&block->NetworkReceiveRawDelta, block->NetworkReceiveRaw); - PhUpdateDelta(&block->NetworkSendDelta, block->NetworkSendCount); - PhUpdateDelta(&block->NetworkSendRawDelta, block->NetworkSendRaw); - - if (maxDiskValue < block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta) - { - maxDiskValue = block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta; - maxDiskBlock = block; - } - - if (maxNetworkValue < block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta) - { - maxNetworkValue = block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta; - maxNetworkBlock = block; - } - - listEntry = listEntry->Flink; - } - - // Update history buffers. - - if (runCount != 0) - { - PhAddItemCircularBuffer_ULONG(&EtDiskReadHistory, EtDiskReadDelta.Delta); - PhAddItemCircularBuffer_ULONG(&EtDiskWriteHistory, EtDiskWriteDelta.Delta); - PhAddItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, EtNetworkReceiveDelta.Delta); - PhAddItemCircularBuffer_ULONG(&EtNetworkSendHistory, EtNetworkSendDelta.Delta); - - if (maxDiskBlock) - { - PhAddItemCircularBuffer_ULONG(&EtMaxDiskHistory, HandleToUlong(maxDiskBlock->ProcessItem->ProcessId)); - PhReferenceProcessRecordForStatistics(maxDiskBlock->ProcessItem->Record); - } - else - { - PhAddItemCircularBuffer_ULONG(&EtMaxDiskHistory, 0); - } - - if (maxNetworkBlock) - { - PhAddItemCircularBuffer_ULONG(&EtMaxNetworkHistory, HandleToUlong(maxNetworkBlock->ProcessItem->ProcessId)); - PhReferenceProcessRecordForStatistics(maxNetworkBlock->ProcessItem->Record); - } - else - { - PhAddItemCircularBuffer_ULONG(&EtMaxNetworkHistory, 0); - } - } - - runCount++; -} - -static VOID NTAPI EtpInvalidateNetworkNode( - _In_ PVOID Parameter - ) -{ - PPH_NETWORK_ITEM networkItem = Parameter; - PPH_NETWORK_NODE networkNode; - - if (networkNode = PhFindNetworkNode(networkItem)) - TreeNew_InvalidateNode(NetworkTreeNewHandle, &networkNode->Node); - - PhDereferenceObject(networkItem); -} - -VOID NTAPI EtEtwNetworkItemsUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PLIST_ENTRY listEntry; - - // ETW is flushed in the processes-updated callback above. This may cause us the network - // blocks to all fall one update interval behind, however. - - // Update per-connection statistics. - // Note: no lock is needed because we only ever modify the list on this same thread. - - listEntry = EtNetworkBlockListHead.Flink; - - while (listEntry != &EtNetworkBlockListHead) - { - PET_NETWORK_BLOCK block; - PH_UINT64_DELTA oldDeltas[4]; - - block = CONTAINING_RECORD(listEntry, ET_NETWORK_BLOCK, ListEntry); - - memcpy(oldDeltas, block->Deltas, sizeof(block->Deltas)); - - PhUpdateDelta(&block->ReceiveDelta, block->ReceiveCount); - PhUpdateDelta(&block->ReceiveRawDelta, block->ReceiveRaw); - PhUpdateDelta(&block->SendDelta, block->SendCount); - PhUpdateDelta(&block->SendRawDelta, block->SendRaw); - - if (memcmp(oldDeltas, block->Deltas, sizeof(block->Deltas))) - { - // Values have changed. Invalidate the network node. - PhReferenceObject(block->NetworkItem); - ProcessHacker_Invoke(PhMainWndHandle, EtpInvalidateNetworkNode, block->NetworkItem); - } - - listEntry = listEntry->Flink; - } -} - -VOID EtUpdateProcessInformation( - VOID - ) -{ - PhAcquireQueuedLockExclusive(&EtpProcessInformationLock); - - if (EtpProcessInformation) - { - PhFree(EtpProcessInformation); - EtpProcessInformation = NULL; - } - - PhEnumProcesses(&EtpProcessInformation); - - PhReleaseQueuedLockExclusive(&EtpProcessInformationLock); -} - -HANDLE EtThreadIdToProcessId( - _In_ HANDLE ThreadId - ) -{ - PSYSTEM_PROCESS_INFORMATION process; - ULONG i; - HANDLE processId; - - PhAcquireQueuedLockShared(&EtpProcessInformationLock); - - if (!EtpProcessInformation) - { - PhReleaseQueuedLockShared(&EtpProcessInformationLock); - return SYSTEM_PROCESS_ID; - } - - process = PH_FIRST_PROCESS(EtpProcessInformation); - - do - { - for (i = 0; i < process->NumberOfThreads; i++) - { - if (process->Threads[i].ClientId.UniqueThread == ThreadId) - { - processId = process->UniqueProcessId; - PhReleaseQueuedLockShared(&EtpProcessInformationLock); - - return processId; - } - } - } while (process = PH_NEXT_PROCESS(process)); - - PhReleaseQueuedLockShared(&EtpProcessInformationLock); - - return SYSTEM_PROCESS_ID; -} +/* + * Process Hacker Extended Tools - + * ETW statistics collection + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwmon.h" + +VOID NTAPI EtEtwProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +VOID NTAPI EtEtwNetworkItemsUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +static PH_CALLBACK_REGISTRATION EtpProcessesUpdatedCallbackRegistration; +static PH_CALLBACK_REGISTRATION EtpNetworkItemsUpdatedCallbackRegistration; + +ULONG EtpDiskReadRaw; +ULONG EtpDiskWriteRaw; +ULONG EtpNetworkReceiveRaw; +ULONG EtpNetworkSendRaw; + +ULONG EtDiskReadCount; +ULONG EtDiskWriteCount; +ULONG EtNetworkReceiveCount; +ULONG EtNetworkSendCount; + +PH_UINT32_DELTA EtDiskReadDelta; +PH_UINT32_DELTA EtDiskWriteDelta; +PH_UINT32_DELTA EtNetworkReceiveDelta; +PH_UINT32_DELTA EtNetworkSendDelta; + +PH_UINT32_DELTA EtDiskReadCountDelta; +PH_UINT32_DELTA EtDiskWriteCountDelta; +PH_UINT32_DELTA EtNetworkReceiveCountDelta; +PH_UINT32_DELTA EtNetworkSendCountDelta; + +PH_CIRCULAR_BUFFER_ULONG EtDiskReadHistory; +PH_CIRCULAR_BUFFER_ULONG EtDiskWriteHistory; +PH_CIRCULAR_BUFFER_ULONG EtNetworkReceiveHistory; +PH_CIRCULAR_BUFFER_ULONG EtNetworkSendHistory; +PH_CIRCULAR_BUFFER_ULONG EtMaxDiskHistory; // ID of max. disk usage process +PH_CIRCULAR_BUFFER_ULONG EtMaxNetworkHistory; // ID of max. network usage process + +PVOID EtpProcessInformation; +PH_QUEUED_LOCK EtpProcessInformationLock = PH_QUEUED_LOCK_INIT; + +VOID EtEtwStatisticsInitialization( + VOID + ) +{ + EtEtwMonitorInitialization(); + + if (EtEtwEnabled) + { + ULONG sampleCount; + + sampleCount = PhGetIntegerSetting(L"SampleCount"); + PhInitializeCircularBuffer_ULONG(&EtDiskReadHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&EtDiskWriteHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&EtNetworkReceiveHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&EtNetworkSendHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&EtMaxDiskHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&EtMaxNetworkHistory, sampleCount); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + EtEtwProcessesUpdatedCallback, + NULL, + &EtpProcessesUpdatedCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdatedEvent), + EtEtwNetworkItemsUpdatedCallback, + NULL, + &EtpNetworkItemsUpdatedCallbackRegistration + ); + } +} + +VOID EtEtwStatisticsUninitialization( + VOID + ) +{ + EtEtwMonitorUninitialization(); +} + +VOID EtProcessDiskEvent( + _In_ PET_ETW_DISK_EVENT Event + ) +{ + PPH_PROCESS_ITEM processItem; + PET_PROCESS_BLOCK block; + + if (Event->Type == EtEtwDiskReadType) + { + EtpDiskReadRaw += Event->TransferSize; + EtDiskReadCount++; + } + else + { + EtpDiskWriteRaw += Event->TransferSize; + EtDiskWriteCount++; + } + + if (processItem = PhReferenceProcessItem(Event->ClientId.UniqueProcess)) + { + block = EtGetProcessBlock(processItem); + + if (Event->Type == EtEtwDiskReadType) + { + block->DiskReadRaw += Event->TransferSize; + block->DiskReadCount++; + } + else + { + block->DiskWriteRaw += Event->TransferSize; + block->DiskWriteCount++; + } + + PhDereferenceObject(processItem); + } +} + +VOID EtProcessNetworkEvent( + _In_ PET_ETW_NETWORK_EVENT Event + ) +{ + PPH_PROCESS_ITEM processItem; + PET_PROCESS_BLOCK block; + PPH_NETWORK_ITEM networkItem; + PET_NETWORK_BLOCK networkBlock; + + if (Event->Type == EtEtwNetworkReceiveType) + { + EtpNetworkReceiveRaw += Event->TransferSize; + EtNetworkReceiveCount++; + } + else + { + EtpNetworkSendRaw += Event->TransferSize; + EtNetworkSendCount++; + } + + // Note: there is always the possibility of us receiving the event too early, + // before the process item or network item is created. So events may be lost. + + if (processItem = PhReferenceProcessItem(Event->ClientId.UniqueProcess)) + { + block = EtGetProcessBlock(processItem); + + if (Event->Type == EtEtwNetworkReceiveType) + { + block->NetworkReceiveRaw += Event->TransferSize; + block->NetworkReceiveCount++; + } + else + { + block->NetworkSendRaw += Event->TransferSize; + block->NetworkSendCount++; + } + + PhDereferenceObject(processItem); + } + + networkItem = PhReferenceNetworkItem( + Event->ProtocolType, + &Event->LocalEndpoint, + &Event->RemoteEndpoint, + Event->ClientId.UniqueProcess + ); + + if (!networkItem && Event->ProtocolType & PH_UDP_PROTOCOL_TYPE) + { + // Note: ETW generates UDP events with the LocalEndpoint set to the LAN endpoint address + // of the local adapter the packet was sent or recieved but GetExtendedUdpTable + // returns some UDP connections with endpoints set to in4addr_any/in6addr_any (zero). (dmex) + + if (Event->ProtocolType & PH_IPV4_NETWORK_TYPE) + memset(&Event->LocalEndpoint.Address.InAddr, 0, sizeof(IN_ADDR)); // same as in4addr_any + else + memset(&Event->LocalEndpoint.Address.In6Addr, 0, sizeof(IN6_ADDR)); // same as in6addr_any + + networkItem = PhReferenceNetworkItem( + Event->ProtocolType, + &Event->LocalEndpoint, + &Event->RemoteEndpoint, + Event->ClientId.UniqueProcess + ); + } + + if (networkItem) + { + networkBlock = EtGetNetworkBlock(networkItem); + + if (Event->Type == EtEtwNetworkReceiveType) + { + networkBlock->ReceiveRaw += Event->TransferSize; + networkBlock->ReceiveCount++; + } + else + { + networkBlock->SendRaw += Event->TransferSize; + networkBlock->SendCount++; + } + + PhDereferenceObject(networkItem); + } +} + +VOID NTAPI EtEtwProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + static ULONG runCount = 0; // MUST keep in sync with runCount in process provider + + PLIST_ENTRY listEntry; + ULONG64 maxDiskValue = 0; + PET_PROCESS_BLOCK maxDiskBlock = NULL; + ULONG64 maxNetworkValue = 0; + PET_PROCESS_BLOCK maxNetworkBlock = NULL; + + // Since Windows 8, we no longer get the correct process/thread IDs in the + // event headers for disk events. We need to update our process information since + // etwmon uses our EtThreadIdToProcessId function. + if (WindowsVersion >= WINDOWS_8) + EtUpdateProcessInformation(); + + // ETW is extremely lazy when it comes to flushing buffers, so we must do it + // manually. + EtFlushEtwSession(); + + // Update global statistics. + + PhUpdateDelta(&EtDiskReadDelta, EtpDiskReadRaw); + PhUpdateDelta(&EtDiskWriteDelta, EtpDiskWriteRaw); + PhUpdateDelta(&EtNetworkReceiveDelta, EtpNetworkReceiveRaw); + PhUpdateDelta(&EtNetworkSendDelta, EtpNetworkSendRaw); + + PhUpdateDelta(&EtDiskReadCountDelta, EtDiskReadCount); + PhUpdateDelta(&EtDiskWriteCountDelta, EtDiskWriteCount); + PhUpdateDelta(&EtNetworkReceiveCountDelta, EtNetworkReceiveCount); + PhUpdateDelta(&EtNetworkSendCountDelta, EtNetworkSendCount); + + // Update per-process statistics. + // Note: no lock is needed because we only ever modify the list on this same thread. + + listEntry = EtProcessBlockListHead.Flink; + + while (listEntry != &EtProcessBlockListHead) + { + PET_PROCESS_BLOCK block; + + block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); + + PhUpdateDelta(&block->DiskReadDelta, block->DiskReadCount); + PhUpdateDelta(&block->DiskReadRawDelta, block->DiskReadRaw); + PhUpdateDelta(&block->DiskWriteDelta, block->DiskWriteCount); + PhUpdateDelta(&block->DiskWriteRawDelta, block->DiskWriteRaw); + PhUpdateDelta(&block->NetworkReceiveDelta, block->NetworkReceiveCount); + PhUpdateDelta(&block->NetworkReceiveRawDelta, block->NetworkReceiveRaw); + PhUpdateDelta(&block->NetworkSendDelta, block->NetworkSendCount); + PhUpdateDelta(&block->NetworkSendRawDelta, block->NetworkSendRaw); + + if (maxDiskValue < block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta) + { + maxDiskValue = block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta; + maxDiskBlock = block; + } + + if (maxNetworkValue < block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta) + { + maxNetworkValue = block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta; + maxNetworkBlock = block; + } + + listEntry = listEntry->Flink; + } + + // Update history buffers. + + if (runCount != 0) + { + PhAddItemCircularBuffer_ULONG(&EtDiskReadHistory, EtDiskReadDelta.Delta); + PhAddItemCircularBuffer_ULONG(&EtDiskWriteHistory, EtDiskWriteDelta.Delta); + PhAddItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, EtNetworkReceiveDelta.Delta); + PhAddItemCircularBuffer_ULONG(&EtNetworkSendHistory, EtNetworkSendDelta.Delta); + + if (maxDiskBlock) + { + PhAddItemCircularBuffer_ULONG(&EtMaxDiskHistory, HandleToUlong(maxDiskBlock->ProcessItem->ProcessId)); + PhReferenceProcessRecordForStatistics(maxDiskBlock->ProcessItem->Record); + } + else + { + PhAddItemCircularBuffer_ULONG(&EtMaxDiskHistory, 0); + } + + if (maxNetworkBlock) + { + PhAddItemCircularBuffer_ULONG(&EtMaxNetworkHistory, HandleToUlong(maxNetworkBlock->ProcessItem->ProcessId)); + PhReferenceProcessRecordForStatistics(maxNetworkBlock->ProcessItem->Record); + } + else + { + PhAddItemCircularBuffer_ULONG(&EtMaxNetworkHistory, 0); + } + } + + runCount++; +} + +static VOID NTAPI EtpInvalidateNetworkNode( + _In_ PVOID Parameter + ) +{ + PPH_NETWORK_ITEM networkItem = Parameter; + PPH_NETWORK_NODE networkNode; + + if (networkNode = PhFindNetworkNode(networkItem)) + TreeNew_InvalidateNode(NetworkTreeNewHandle, &networkNode->Node); + + PhDereferenceObject(networkItem); +} + +VOID NTAPI EtEtwNetworkItemsUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PLIST_ENTRY listEntry; + + // ETW is flushed in the processes-updated callback above. This may cause us the network + // blocks to all fall one update interval behind, however. + + // Update per-connection statistics. + // Note: no lock is needed because we only ever modify the list on this same thread. + + listEntry = EtNetworkBlockListHead.Flink; + + while (listEntry != &EtNetworkBlockListHead) + { + PET_NETWORK_BLOCK block; + PH_UINT64_DELTA oldDeltas[4]; + + block = CONTAINING_RECORD(listEntry, ET_NETWORK_BLOCK, ListEntry); + + memcpy(oldDeltas, block->Deltas, sizeof(block->Deltas)); + + PhUpdateDelta(&block->ReceiveDelta, block->ReceiveCount); + PhUpdateDelta(&block->ReceiveRawDelta, block->ReceiveRaw); + PhUpdateDelta(&block->SendDelta, block->SendCount); + PhUpdateDelta(&block->SendRawDelta, block->SendRaw); + + if (memcmp(oldDeltas, block->Deltas, sizeof(block->Deltas))) + { + // Values have changed. Invalidate the network node. + PhReferenceObject(block->NetworkItem); + ProcessHacker_Invoke(PhMainWndHandle, EtpInvalidateNetworkNode, block->NetworkItem); + } + + listEntry = listEntry->Flink; + } +} + +VOID EtUpdateProcessInformation( + VOID + ) +{ + PhAcquireQueuedLockExclusive(&EtpProcessInformationLock); + + if (EtpProcessInformation) + { + PhFree(EtpProcessInformation); + EtpProcessInformation = NULL; + } + + PhEnumProcesses(&EtpProcessInformation); + + PhReleaseQueuedLockExclusive(&EtpProcessInformationLock); +} + +HANDLE EtThreadIdToProcessId( + _In_ HANDLE ThreadId + ) +{ + PSYSTEM_PROCESS_INFORMATION process; + ULONG i; + HANDLE processId; + + PhAcquireQueuedLockShared(&EtpProcessInformationLock); + + if (!EtpProcessInformation) + { + PhReleaseQueuedLockShared(&EtpProcessInformationLock); + return SYSTEM_PROCESS_ID; + } + + process = PH_FIRST_PROCESS(EtpProcessInformation); + + do + { + for (i = 0; i < process->NumberOfThreads; i++) + { + if (process->Threads[i].ClientId.UniqueThread == ThreadId) + { + processId = process->UniqueProcessId; + PhReleaseQueuedLockShared(&EtpProcessInformationLock); + + return processId; + } + } + } while (process = PH_NEXT_PROCESS(process)); + + PhReleaseQueuedLockShared(&EtpProcessInformationLock); + + return SYSTEM_PROCESS_ID; +} diff --git a/plugins/ExtendedTools/etwsys.c b/plugins/ExtendedTools/etwsys.c index 2cc5b4b14515..e10c96abba22 100644 --- a/plugins/ExtendedTools/etwsys.c +++ b/plugins/ExtendedTools/etwsys.c @@ -1,811 +1,811 @@ -/* - * Process Hacker Extended Tools - - * ETW system information section - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "etwsys.h" - -static PPH_SYSINFO_SECTION DiskSection; -static HWND DiskDialog; -static PH_LAYOUT_MANAGER DiskLayoutManager; -static HWND DiskGraphHandle; -static PH_GRAPH_STATE DiskGraphState; -static HWND DiskPanel; - -static PPH_SYSINFO_SECTION NetworkSection; -static HWND NetworkDialog; -static PH_LAYOUT_MANAGER NetworkLayoutManager; -static HWND NetworkGraphHandle; -static PH_GRAPH_STATE NetworkGraphState; -static HWND NetworkPanel; - -VOID EtEtwSystemInformationInitializing( - _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers - ) -{ - PH_SYSINFO_SECTION section; - - memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); - PhInitializeStringRef(§ion.Name, L"Disk"); - section.Flags = 0; - section.Callback = EtpDiskSysInfoSectionCallback; - - DiskSection = Pointers->CreateSection(§ion); - - memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); - PhInitializeStringRef(§ion.Name, L"Network"); - section.Flags = 0; - section.Callback = EtpNetworkSysInfoSectionCallback; - - NetworkSection = Pointers->CreateSection(§ion); -} - -BOOLEAN EtpDiskSysInfoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case SysInfoDestroy: - { - if (DiskDialog) - { - PhDeleteGraphState(&DiskGraphState); - DiskDialog = NULL; - } - } - return TRUE; - case SysInfoTick: - { - if (DiskDialog) - { - EtpUpdateDiskGraph(); - EtpUpdateDiskPanel(); - } - } - return TRUE; - case SysInfoCreateDialog: - { - PPH_SYSINFO_CREATE_DIALOG createDialog = Parameter1; - - createDialog->Instance = PluginInstance->DllBase; - createDialog->Template = MAKEINTRESOURCE(IDD_SYSINFO_DISK); - createDialog->DialogProc = EtpDiskDialogProc; - } - return TRUE; - case SysInfoGraphGetDrawInfo: - { - PPH_GRAPH_DRAW_INFO drawInfo = Parameter1; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; - Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, EtDiskReadHistory.Count); - - if (!Section->GraphState.Valid) - { - ULONG i; - FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - Section->GraphState.Data1[i] = data1 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); - Section->GraphState.Data2[i] = data2 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - Section->GraphState.Data1, - max, - drawInfo->LineDataCount - ); - PhDivideSinglesBySingle( - Section->GraphState.Data2, - max, - drawInfo->LineDataCount - ); - } - - drawInfo->LabelYFunction = PhSiSizeLabelYFunction; - drawInfo->LabelYFunctionParameter = max; - - Section->GraphState.Valid = TRUE; - } - } - return TRUE; - case SysInfoGraphGetTooltipText: - { - PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT getTooltipText = Parameter1; - ULONG64 diskRead; - ULONG64 diskWrite; - - diskRead = PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, getTooltipText->Index); - diskWrite = PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, getTooltipText->Index); - - PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( - L"R: %s\nW: %s%s\n%s", - PhaFormatSize(diskRead, -1)->Buffer, - PhaFormatSize(diskWrite, -1)->Buffer, - PhGetStringOrEmpty(EtpGetMaxDiskString(getTooltipText->Index)), - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - getTooltipText->Text = Section->GraphState.TooltipText->sr; - } - return TRUE; - case SysInfoGraphDrawPanel: - { - PPH_SYSINFO_DRAW_PANEL drawPanel = Parameter1; - - drawPanel->Title = PhCreateString(L"Disk"); - drawPanel->SubTitle = PhFormatString( - L"R: %s\nW: %s", - PhaFormatSize(EtDiskReadDelta.Delta, -1)->Buffer, - PhaFormatSize(EtDiskWriteDelta.Delta, -1)->Buffer - ); - } - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK EtpDiskDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_LAYOUT_ITEM graphItem; - PPH_LAYOUT_ITEM panelItem; - - PhInitializeGraphState(&DiskGraphState); - - DiskDialog = hwndDlg; - PhInitializeLayoutManager(&DiskLayoutManager, hwndDlg); - graphItem = PhAddLayoutItem(&DiskLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); - panelItem = PhAddLayoutItem(&DiskLayoutManager, GetDlgItem(hwndDlg, IDC_PANEL_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - SendMessage(GetDlgItem(hwndDlg, IDC_TITLE), WM_SETFONT, (WPARAM)DiskSection->Parameters->LargeFont, FALSE); - - DiskPanel = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_SYSINFO_DISKPANEL), hwndDlg, EtpDiskPanelDialogProc); - ShowWindow(DiskPanel, SW_SHOW); - PhAddLayoutItemEx(&DiskLayoutManager, DiskPanel, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM, panelItem->Margin); - - DiskGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - hwndDlg, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(DiskGraphHandle, TRUE); - - PhAddLayoutItemEx(&DiskLayoutManager, DiskGraphHandle, NULL, PH_ANCHOR_ALL, graphItem->Margin); - - EtpUpdateDiskGraph(); - EtpUpdateDiskPanel(); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&DiskLayoutManager); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&DiskLayoutManager); - } - break; - case WM_NOTIFY: - { - NMHDR *header = (NMHDR *)lParam; - - if (header->hwndFrom == DiskGraphHandle) - { - EtpNotifyDiskGraph(header); - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK EtpDiskPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return FALSE; -} - -VOID EtpNotifyDiskGraph( - _In_ NMHDR *Header - ) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; - DiskSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - - PhGraphStateGetDrawInfo( - &DiskGraphState, - getDrawInfo, - EtDiskReadHistory.Count - ); - - if (!DiskGraphState.Valid) - { - ULONG i; - FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - DiskGraphState.Data1[i] = data1 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); - DiskGraphState.Data2[i] = data2 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - DiskGraphState.Data1, - max, - drawInfo->LineDataCount - ); - PhDivideSinglesBySingle( - DiskGraphState.Data2, - max, - drawInfo->LineDataCount - ); - } - - drawInfo->LabelYFunction = PhSiSizeLabelYFunction; - drawInfo->LabelYFunctionParameter = max; - - DiskGraphState.Valid = TRUE; - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (DiskGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG64 diskRead; - ULONG64 diskWrite; - - diskRead = PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, getTooltipText->Index); - diskWrite = PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, getTooltipText->Index); - - PhMoveReference(&DiskGraphState.TooltipText, PhFormatString( - L"R: %s\nW: %s%s\n%s", - PhaFormatSize(diskRead, -1)->Buffer, - PhaFormatSize(diskWrite, -1)->Buffer, - PhGetStringOrEmpty(EtpGetMaxDiskString(getTooltipText->Index)), - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = DiskGraphState.TooltipText->sr; - } - } - break; - case GCN_MOUSEEVENT: - { - PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; - PPH_PROCESS_RECORD record; - - record = NULL; - - if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) - { - record = EtpReferenceMaxDiskRecord(mouseEvent->Index); - } - - if (record) - { - PhShowProcessRecordDialog(DiskDialog, record); - PhDereferenceProcessRecord(record); - } - } - break; - } -} - -VOID EtpUpdateDiskGraph( - VOID - ) -{ - DiskGraphState.Valid = FALSE; - DiskGraphState.TooltipIndex = -1; - Graph_MoveGrid(DiskGraphHandle, 1); - Graph_Draw(DiskGraphHandle); - Graph_UpdateTooltip(DiskGraphHandle); - InvalidateRect(DiskGraphHandle, NULL, FALSE); -} - -VOID EtpUpdateDiskPanel( - VOID - ) -{ - SetDlgItemText(DiskPanel, IDC_ZREADSDELTA_V, PhaFormatUInt64(EtDiskReadCountDelta.Delta, TRUE)->Buffer); - SetDlgItemText(DiskPanel, IDC_ZREADBYTESDELTA_V, PhaFormatSize(EtDiskReadDelta.Delta, -1)->Buffer); - SetDlgItemText(DiskPanel, IDC_ZWRITESDELTA_V, PhaFormatUInt64(EtDiskWriteCountDelta.Delta, TRUE)->Buffer); - SetDlgItemText(DiskPanel, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(EtDiskWriteDelta.Delta, -1)->Buffer); -} - -PPH_PROCESS_RECORD EtpReferenceMaxDiskRecord( - _In_ LONG Index - ) -{ - LARGE_INTEGER time; - ULONG maxProcessId; - - maxProcessId = PhGetItemCircularBuffer_ULONG(&EtMaxDiskHistory, Index); - - if (!maxProcessId) - return NULL; - - PhGetStatisticsTime(NULL, Index, &time); - time.QuadPart += PH_TICKS_PER_SEC - 1; - - return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); -} - -PPH_STRING EtpGetMaxDiskString( - _In_ LONG Index - ) -{ - PPH_PROCESS_RECORD maxProcessRecord; - PPH_STRING maxUsageString = NULL; - - if (maxProcessRecord = EtpReferenceMaxDiskRecord(Index)) - { - maxUsageString = PhaFormatString( - L"\n%s (%lu)", - maxProcessRecord->ProcessName->Buffer, - HandleToUlong(maxProcessRecord->ProcessId) - ); - PhDereferenceProcessRecord(maxProcessRecord); - } - - return maxUsageString; -} - -BOOLEAN EtpNetworkSysInfoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case SysInfoDestroy: - { - if (NetworkDialog) - { - PhDeleteGraphState(&NetworkGraphState); - NetworkDialog = NULL; - } - } - return TRUE; - case SysInfoTick: - { - if (NetworkDialog) - { - EtpUpdateNetworkGraph(); - EtpUpdateNetworkPanel(); - } - } - return TRUE; - case SysInfoCreateDialog: - { - PPH_SYSINFO_CREATE_DIALOG createDialog = Parameter1; - - createDialog->Instance = PluginInstance->DllBase; - createDialog->Template = MAKEINTRESOURCE(IDD_SYSINFO_NET); - createDialog->DialogProc = EtpNetworkDialogProc; - } - return TRUE; - case SysInfoGraphGetDrawInfo: - { - PPH_GRAPH_DRAW_INFO drawInfo = Parameter1; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; - Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, EtNetworkReceiveHistory.Count); - - if (!Section->GraphState.Valid) - { - ULONG i; - FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - Section->GraphState.Data1[i] = data1 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); - Section->GraphState.Data2[i] = data2 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - Section->GraphState.Data1, - max, - drawInfo->LineDataCount - ); - PhDivideSinglesBySingle( - Section->GraphState.Data2, - max, - drawInfo->LineDataCount - ); - } - - drawInfo->LabelYFunction = PhSiSizeLabelYFunction; - drawInfo->LabelYFunctionParameter = max; - - Section->GraphState.Valid = TRUE; - } - } - return TRUE; - case SysInfoGraphGetTooltipText: - { - PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT getTooltipText = Parameter1; - ULONG64 networkReceive; - ULONG64 networkSend; - - networkReceive = PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, getTooltipText->Index); - networkSend = PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, getTooltipText->Index); - - PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( - L"R: %s\nS: %s%s\n%s", - PhaFormatSize(networkReceive, -1)->Buffer, - PhaFormatSize(networkSend, -1)->Buffer, - PhGetStringOrEmpty(EtpGetMaxNetworkString(getTooltipText->Index)), - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - getTooltipText->Text = Section->GraphState.TooltipText->sr; - } - return TRUE; - case SysInfoGraphDrawPanel: - { - PPH_SYSINFO_DRAW_PANEL drawPanel = Parameter1; - - drawPanel->Title = PhCreateString(L"Network"); - drawPanel->SubTitle = PhFormatString( - L"R: %s\nS: %s", - PhaFormatSize(EtNetworkReceiveDelta.Delta, -1)->Buffer, - PhaFormatSize(EtNetworkSendDelta.Delta, -1)->Buffer - ); - } - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK EtpNetworkDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_LAYOUT_ITEM graphItem; - PPH_LAYOUT_ITEM panelItem; - - PhInitializeGraphState(&NetworkGraphState); - - NetworkDialog = hwndDlg; - PhInitializeLayoutManager(&NetworkLayoutManager, hwndDlg); - graphItem = PhAddLayoutItem(&NetworkLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); - panelItem = PhAddLayoutItem(&NetworkLayoutManager, GetDlgItem(hwndDlg, IDC_PANEL_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - SendMessage(GetDlgItem(hwndDlg, IDC_TITLE), WM_SETFONT, (WPARAM)NetworkSection->Parameters->LargeFont, FALSE); - - NetworkPanel = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_SYSINFO_NETPANEL), hwndDlg, EtpNetworkPanelDialogProc); - ShowWindow(NetworkPanel, SW_SHOW); - PhAddLayoutItemEx(&NetworkLayoutManager, NetworkPanel, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM, panelItem->Margin); - - NetworkGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - hwndDlg, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(NetworkGraphHandle, TRUE); - - PhAddLayoutItemEx(&NetworkLayoutManager, NetworkGraphHandle, NULL, PH_ANCHOR_ALL, graphItem->Margin); - - EtpUpdateNetworkGraph(); - EtpUpdateNetworkPanel(); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&NetworkLayoutManager); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&NetworkLayoutManager); - } - break; - case WM_NOTIFY: - { - NMHDR *header = (NMHDR *)lParam; - - if (header->hwndFrom == NetworkGraphHandle) - { - EtpNotifyNetworkGraph(header); - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK EtpNetworkPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - return FALSE; -} - -VOID EtpNotifyNetworkGraph( - _In_ NMHDR *Header - ) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; - NetworkSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - - PhGraphStateGetDrawInfo( - &NetworkGraphState, - getDrawInfo, - EtNetworkReceiveHistory.Count - ); - - if (!NetworkGraphState.Valid) - { - ULONG i; - FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - FLOAT data2; - - NetworkGraphState.Data1[i] = data1 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); - NetworkGraphState.Data2[i] = data2 = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); - - if (max < data1 + data2) - max = data1 + data2; - } - - if (max != 0) - { - // Scale the data. - - PhDivideSinglesBySingle( - NetworkGraphState.Data1, - max, - drawInfo->LineDataCount - ); - PhDivideSinglesBySingle( - NetworkGraphState.Data2, - max, - drawInfo->LineDataCount - ); - } - - drawInfo->LabelYFunction = PhSiSizeLabelYFunction; - drawInfo->LabelYFunctionParameter = max; - - NetworkGraphState.Valid = TRUE; - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (NetworkGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG64 networkReceive; - ULONG64 networkSend; - - networkReceive = PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, getTooltipText->Index); - networkSend = PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, getTooltipText->Index); - - PhMoveReference(&NetworkGraphState.TooltipText, PhFormatString( - L"R: %s\nS: %s%s\n%s", - PhaFormatSize(networkReceive, -1)->Buffer, - PhaFormatSize(networkSend, -1)->Buffer, - PhGetStringOrEmpty(EtpGetMaxNetworkString(getTooltipText->Index)), - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = NetworkGraphState.TooltipText->sr; - } - } - break; - case GCN_MOUSEEVENT: - { - PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; - PPH_PROCESS_RECORD record; - - record = NULL; - - if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) - { - record = EtpReferenceMaxNetworkRecord(mouseEvent->Index); - } - - if (record) - { - PhShowProcessRecordDialog(NetworkDialog, record); - PhDereferenceProcessRecord(record); - } - } - break; - } -} - -VOID EtpUpdateNetworkGraph( - VOID - ) -{ - NetworkGraphState.Valid = FALSE; - NetworkGraphState.TooltipIndex = -1; - Graph_MoveGrid(NetworkGraphHandle, 1); - Graph_Draw(NetworkGraphHandle); - Graph_UpdateTooltip(NetworkGraphHandle); - InvalidateRect(NetworkGraphHandle, NULL, FALSE); -} - -VOID EtpUpdateNetworkPanel( - VOID - ) -{ - SetDlgItemText(NetworkPanel, IDC_ZRECEIVESDELTA_V, PhaFormatUInt64(EtNetworkReceiveCountDelta.Delta, TRUE)->Buffer); - SetDlgItemText(NetworkPanel, IDC_ZRECEIVEBYTESDELTA_V, PhaFormatSize(EtNetworkReceiveDelta.Delta, -1)->Buffer); - SetDlgItemText(NetworkPanel, IDC_ZSENDSDELTA_V, PhaFormatUInt64(EtNetworkSendCountDelta.Delta, TRUE)->Buffer); - SetDlgItemText(NetworkPanel, IDC_ZSENDBYTESDELTA_V, PhaFormatSize(EtNetworkSendDelta.Delta, -1)->Buffer); -} - -PPH_PROCESS_RECORD EtpReferenceMaxNetworkRecord( - _In_ LONG Index - ) -{ - LARGE_INTEGER time; - ULONG maxProcessId; - - maxProcessId = PhGetItemCircularBuffer_ULONG(&EtMaxNetworkHistory, Index); - - if (!maxProcessId) - return NULL; - - PhGetStatisticsTime(NULL, Index, &time); - time.QuadPart += PH_TICKS_PER_SEC - 1; - - return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); -} - -PPH_STRING EtpGetMaxNetworkString( - _In_ LONG Index - ) -{ - PPH_PROCESS_RECORD maxProcessRecord; - PPH_STRING maxUsageString = NULL; - - if (maxProcessRecord = EtpReferenceMaxNetworkRecord(Index)) - { - maxUsageString = PhaFormatString( - L"\n%s (%lu)", - maxProcessRecord->ProcessName->Buffer, - HandleToUlong(maxProcessRecord->ProcessId) - ); - PhDereferenceProcessRecord(maxProcessRecord); - } - - return maxUsageString; -} +/* + * Process Hacker Extended Tools - + * ETW system information section + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwsys.h" + +static PPH_SYSINFO_SECTION DiskSection; +static HWND DiskDialog; +static PH_LAYOUT_MANAGER DiskLayoutManager; +static HWND DiskGraphHandle; +static PH_GRAPH_STATE DiskGraphState; +static HWND DiskPanel; + +static PPH_SYSINFO_SECTION NetworkSection; +static HWND NetworkDialog; +static PH_LAYOUT_MANAGER NetworkLayoutManager; +static HWND NetworkGraphHandle; +static PH_GRAPH_STATE NetworkGraphState; +static HWND NetworkPanel; + +VOID EtEtwSystemInformationInitializing( + _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers + ) +{ + PH_SYSINFO_SECTION section; + + memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); + PhInitializeStringRef(§ion.Name, L"Disk"); + section.Flags = 0; + section.Callback = EtpDiskSysInfoSectionCallback; + + DiskSection = Pointers->CreateSection(§ion); + + memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); + PhInitializeStringRef(§ion.Name, L"Network"); + section.Flags = 0; + section.Callback = EtpNetworkSysInfoSectionCallback; + + NetworkSection = Pointers->CreateSection(§ion); +} + +BOOLEAN EtpDiskSysInfoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case SysInfoDestroy: + { + if (DiskDialog) + { + PhDeleteGraphState(&DiskGraphState); + DiskDialog = NULL; + } + } + return TRUE; + case SysInfoTick: + { + if (DiskDialog) + { + EtpUpdateDiskGraph(); + EtpUpdateDiskPanel(); + } + } + return TRUE; + case SysInfoCreateDialog: + { + PPH_SYSINFO_CREATE_DIALOG createDialog = Parameter1; + + createDialog->Instance = PluginInstance->DllBase; + createDialog->Template = MAKEINTRESOURCE(IDD_SYSINFO_DISK); + createDialog->DialogProc = EtpDiskDialogProc; + } + return TRUE; + case SysInfoGraphGetDrawInfo: + { + PPH_GRAPH_DRAW_INFO drawInfo = Parameter1; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; + Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, EtDiskReadHistory.Count); + + if (!Section->GraphState.Valid) + { + ULONG i; + FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + Section->GraphState.Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); + Section->GraphState.Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + Section->GraphState.Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + Section->GraphState.Data2, + max, + drawInfo->LineDataCount + ); + } + + drawInfo->LabelYFunction = PhSiSizeLabelYFunction; + drawInfo->LabelYFunctionParameter = max; + + Section->GraphState.Valid = TRUE; + } + } + return TRUE; + case SysInfoGraphGetTooltipText: + { + PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT getTooltipText = Parameter1; + ULONG64 diskRead; + ULONG64 diskWrite; + + diskRead = PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, getTooltipText->Index); + diskWrite = PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, getTooltipText->Index); + + PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( + L"R: %s\nW: %s%s\n%s", + PhaFormatSize(diskRead, ULONG_MAX)->Buffer, + PhaFormatSize(diskWrite, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(EtpGetMaxDiskString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + getTooltipText->Text = Section->GraphState.TooltipText->sr; + } + return TRUE; + case SysInfoGraphDrawPanel: + { + PPH_SYSINFO_DRAW_PANEL drawPanel = Parameter1; + + drawPanel->Title = PhCreateString(L"Disk"); + drawPanel->SubTitle = PhFormatString( + L"R: %s\nW: %s", + PhaFormatSize(EtDiskReadDelta.Delta, ULONG_MAX)->Buffer, + PhaFormatSize(EtDiskWriteDelta.Delta, ULONG_MAX)->Buffer + ); + } + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK EtpDiskDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_LAYOUT_ITEM graphItem; + PPH_LAYOUT_ITEM panelItem; + + PhInitializeGraphState(&DiskGraphState); + + DiskDialog = hwndDlg; + PhInitializeLayoutManager(&DiskLayoutManager, hwndDlg); + graphItem = PhAddLayoutItem(&DiskLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); + panelItem = PhAddLayoutItem(&DiskLayoutManager, GetDlgItem(hwndDlg, IDC_PANEL_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + SetWindowFont(GetDlgItem(hwndDlg, IDC_TITLE), DiskSection->Parameters->LargeFont, FALSE); + + DiskPanel = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_SYSINFO_DISKPANEL), hwndDlg, EtpDiskPanelDialogProc); + ShowWindow(DiskPanel, SW_SHOW); + PhAddLayoutItemEx(&DiskLayoutManager, DiskPanel, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM, panelItem->Margin); + + DiskGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + hwndDlg, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(DiskGraphHandle, TRUE); + + PhAddLayoutItemEx(&DiskLayoutManager, DiskGraphHandle, NULL, PH_ANCHOR_ALL, graphItem->Margin); + + EtpUpdateDiskGraph(); + EtpUpdateDiskPanel(); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&DiskLayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&DiskLayoutManager); + } + break; + case WM_NOTIFY: + { + NMHDR *header = (NMHDR *)lParam; + + if (header->hwndFrom == DiskGraphHandle) + { + EtpNotifyDiskGraph(header); + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK EtpDiskPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return FALSE; +} + +VOID EtpNotifyDiskGraph( + _In_ NMHDR *Header + ) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; + DiskSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + + PhGraphStateGetDrawInfo( + &DiskGraphState, + getDrawInfo, + EtDiskReadHistory.Count + ); + + if (!DiskGraphState.Valid) + { + ULONG i; + FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + DiskGraphState.Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); + DiskGraphState.Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + DiskGraphState.Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + DiskGraphState.Data2, + max, + drawInfo->LineDataCount + ); + } + + drawInfo->LabelYFunction = PhSiSizeLabelYFunction; + drawInfo->LabelYFunctionParameter = max; + + DiskGraphState.Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (DiskGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG64 diskRead; + ULONG64 diskWrite; + + diskRead = PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, getTooltipText->Index); + diskWrite = PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, getTooltipText->Index); + + PhMoveReference(&DiskGraphState.TooltipText, PhFormatString( + L"R: %s\nW: %s%s\n%s", + PhaFormatSize(diskRead, ULONG_MAX)->Buffer, + PhaFormatSize(diskWrite, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(EtpGetMaxDiskString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = DiskGraphState.TooltipText->sr; + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record; + + record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = EtpReferenceMaxDiskRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(DiskDialog, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +VOID EtpUpdateDiskGraph( + VOID + ) +{ + DiskGraphState.Valid = FALSE; + DiskGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(DiskGraphHandle, 1); + Graph_Draw(DiskGraphHandle); + Graph_UpdateTooltip(DiskGraphHandle); + InvalidateRect(DiskGraphHandle, NULL, FALSE); +} + +VOID EtpUpdateDiskPanel( + VOID + ) +{ + PhSetDialogItemText(DiskPanel, IDC_ZREADSDELTA_V, PhaFormatUInt64(EtDiskReadCountDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(DiskPanel, IDC_ZREADBYTESDELTA_V, PhaFormatSize(EtDiskReadDelta.Delta, ULONG_MAX)->Buffer); + PhSetDialogItemText(DiskPanel, IDC_ZWRITESDELTA_V, PhaFormatUInt64(EtDiskWriteCountDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(DiskPanel, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(EtDiskWriteDelta.Delta, ULONG_MAX)->Buffer); +} + +PPH_PROCESS_RECORD EtpReferenceMaxDiskRecord( + _In_ LONG Index + ) +{ + LARGE_INTEGER time; + ULONG maxProcessId; + + maxProcessId = PhGetItemCircularBuffer_ULONG(&EtMaxDiskHistory, Index); + + if (!maxProcessId) + return NULL; + + PhGetStatisticsTime(NULL, Index, &time); + time.QuadPart += PH_TICKS_PER_SEC - 1; + + return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); +} + +PPH_STRING EtpGetMaxDiskString( + _In_ LONG Index + ) +{ + PPH_PROCESS_RECORD maxProcessRecord; + PPH_STRING maxUsageString = NULL; + + if (maxProcessRecord = EtpReferenceMaxDiskRecord(Index)) + { + maxUsageString = PhaFormatString( + L"\n%s (%lu)", + maxProcessRecord->ProcessName->Buffer, + HandleToUlong(maxProcessRecord->ProcessId) + ); + PhDereferenceProcessRecord(maxProcessRecord); + } + + return maxUsageString; +} + +BOOLEAN EtpNetworkSysInfoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case SysInfoDestroy: + { + if (NetworkDialog) + { + PhDeleteGraphState(&NetworkGraphState); + NetworkDialog = NULL; + } + } + return TRUE; + case SysInfoTick: + { + if (NetworkDialog) + { + EtpUpdateNetworkGraph(); + EtpUpdateNetworkPanel(); + } + } + return TRUE; + case SysInfoCreateDialog: + { + PPH_SYSINFO_CREATE_DIALOG createDialog = Parameter1; + + createDialog->Instance = PluginInstance->DllBase; + createDialog->Template = MAKEINTRESOURCE(IDD_SYSINFO_NET); + createDialog->DialogProc = EtpNetworkDialogProc; + } + return TRUE; + case SysInfoGraphGetDrawInfo: + { + PPH_GRAPH_DRAW_INFO drawInfo = Parameter1; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; + Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, EtNetworkReceiveHistory.Count); + + if (!Section->GraphState.Valid) + { + ULONG i; + FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + Section->GraphState.Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); + Section->GraphState.Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + Section->GraphState.Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + Section->GraphState.Data2, + max, + drawInfo->LineDataCount + ); + } + + drawInfo->LabelYFunction = PhSiSizeLabelYFunction; + drawInfo->LabelYFunctionParameter = max; + + Section->GraphState.Valid = TRUE; + } + } + return TRUE; + case SysInfoGraphGetTooltipText: + { + PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT getTooltipText = Parameter1; + ULONG64 networkReceive; + ULONG64 networkSend; + + networkReceive = PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, getTooltipText->Index); + networkSend = PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, getTooltipText->Index); + + PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( + L"R: %s\nS: %s%s\n%s", + PhaFormatSize(networkReceive, ULONG_MAX)->Buffer, + PhaFormatSize(networkSend, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(EtpGetMaxNetworkString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + getTooltipText->Text = Section->GraphState.TooltipText->sr; + } + return TRUE; + case SysInfoGraphDrawPanel: + { + PPH_SYSINFO_DRAW_PANEL drawPanel = Parameter1; + + drawPanel->Title = PhCreateString(L"Network"); + drawPanel->SubTitle = PhFormatString( + L"R: %s\nS: %s", + PhaFormatSize(EtNetworkReceiveDelta.Delta, ULONG_MAX)->Buffer, + PhaFormatSize(EtNetworkSendDelta.Delta, ULONG_MAX)->Buffer + ); + } + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK EtpNetworkDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_LAYOUT_ITEM graphItem; + PPH_LAYOUT_ITEM panelItem; + + PhInitializeGraphState(&NetworkGraphState); + + NetworkDialog = hwndDlg; + PhInitializeLayoutManager(&NetworkLayoutManager, hwndDlg); + graphItem = PhAddLayoutItem(&NetworkLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); + panelItem = PhAddLayoutItem(&NetworkLayoutManager, GetDlgItem(hwndDlg, IDC_PANEL_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + SetWindowFont(GetDlgItem(hwndDlg, IDC_TITLE), NetworkSection->Parameters->LargeFont, FALSE); + + NetworkPanel = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_SYSINFO_NETPANEL), hwndDlg, EtpNetworkPanelDialogProc); + ShowWindow(NetworkPanel, SW_SHOW); + PhAddLayoutItemEx(&NetworkLayoutManager, NetworkPanel, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM, panelItem->Margin); + + NetworkGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + hwndDlg, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(NetworkGraphHandle, TRUE); + + PhAddLayoutItemEx(&NetworkLayoutManager, NetworkGraphHandle, NULL, PH_ANCHOR_ALL, graphItem->Margin); + + EtpUpdateNetworkGraph(); + EtpUpdateNetworkPanel(); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&NetworkLayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&NetworkLayoutManager); + } + break; + case WM_NOTIFY: + { + NMHDR *header = (NMHDR *)lParam; + + if (header->hwndFrom == NetworkGraphHandle) + { + EtpNotifyNetworkGraph(header); + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK EtpNetworkPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + return FALSE; +} + +VOID EtpNotifyNetworkGraph( + _In_ NMHDR *Header + ) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y | PH_GRAPH_LABEL_MAX_Y | PH_GRAPH_USE_LINE_2; + NetworkSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + + PhGraphStateGetDrawInfo( + &NetworkGraphState, + getDrawInfo, + EtNetworkReceiveHistory.Count + ); + + if (!NetworkGraphState.Valid) + { + ULONG i; + FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + NetworkGraphState.Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); + NetworkGraphState.Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + NetworkGraphState.Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + NetworkGraphState.Data2, + max, + drawInfo->LineDataCount + ); + } + + drawInfo->LabelYFunction = PhSiSizeLabelYFunction; + drawInfo->LabelYFunctionParameter = max; + + NetworkGraphState.Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (NetworkGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG64 networkReceive; + ULONG64 networkSend; + + networkReceive = PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, getTooltipText->Index); + networkSend = PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, getTooltipText->Index); + + PhMoveReference(&NetworkGraphState.TooltipText, PhFormatString( + L"R: %s\nS: %s%s\n%s", + PhaFormatSize(networkReceive, ULONG_MAX)->Buffer, + PhaFormatSize(networkSend, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(EtpGetMaxNetworkString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = NetworkGraphState.TooltipText->sr; + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record; + + record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = EtpReferenceMaxNetworkRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(NetworkDialog, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +VOID EtpUpdateNetworkGraph( + VOID + ) +{ + NetworkGraphState.Valid = FALSE; + NetworkGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(NetworkGraphHandle, 1); + Graph_Draw(NetworkGraphHandle); + Graph_UpdateTooltip(NetworkGraphHandle); + InvalidateRect(NetworkGraphHandle, NULL, FALSE); +} + +VOID EtpUpdateNetworkPanel( + VOID + ) +{ + PhSetDialogItemText(NetworkPanel, IDC_ZRECEIVESDELTA_V, PhaFormatUInt64(EtNetworkReceiveCountDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(NetworkPanel, IDC_ZRECEIVEBYTESDELTA_V, PhaFormatSize(EtNetworkReceiveDelta.Delta, ULONG_MAX)->Buffer); + PhSetDialogItemText(NetworkPanel, IDC_ZSENDSDELTA_V, PhaFormatUInt64(EtNetworkSendCountDelta.Delta, TRUE)->Buffer); + PhSetDialogItemText(NetworkPanel, IDC_ZSENDBYTESDELTA_V, PhaFormatSize(EtNetworkSendDelta.Delta, ULONG_MAX)->Buffer); +} + +PPH_PROCESS_RECORD EtpReferenceMaxNetworkRecord( + _In_ LONG Index + ) +{ + LARGE_INTEGER time; + ULONG maxProcessId; + + maxProcessId = PhGetItemCircularBuffer_ULONG(&EtMaxNetworkHistory, Index); + + if (!maxProcessId) + return NULL; + + PhGetStatisticsTime(NULL, Index, &time); + time.QuadPart += PH_TICKS_PER_SEC - 1; + + return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); +} + +PPH_STRING EtpGetMaxNetworkString( + _In_ LONG Index + ) +{ + PPH_PROCESS_RECORD maxProcessRecord; + PPH_STRING maxUsageString = NULL; + + if (maxProcessRecord = EtpReferenceMaxNetworkRecord(Index)) + { + maxUsageString = PhaFormatString( + L"\n%s (%lu)", + maxProcessRecord->ProcessName->Buffer, + HandleToUlong(maxProcessRecord->ProcessId) + ); + PhDereferenceProcessRecord(maxProcessRecord); + } + + return maxUsageString; +} diff --git a/plugins/ExtendedTools/etwsys.h b/plugins/ExtendedTools/etwsys.h index f8d53024baa0..b33ac171d018 100644 --- a/plugins/ExtendedTools/etwsys.h +++ b/plugins/ExtendedTools/etwsys.h @@ -1,90 +1,90 @@ -#ifndef ETWSYS_H -#define ETWSYS_H - -// Disk section - -BOOLEAN EtpDiskSysInfoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -INT_PTR CALLBACK EtpDiskDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK EtpDiskPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtpNotifyDiskGraph( - _In_ NMHDR *Header - ); - -VOID EtpUpdateDiskGraph( - VOID - ); - -VOID EtpUpdateDiskPanel( - VOID - ); - -PPH_PROCESS_RECORD EtpReferenceMaxDiskRecord( - _In_ LONG Index - ); - -PPH_STRING EtpGetMaxDiskString( - _In_ LONG Index - ); - -// Network section - -BOOLEAN EtpNetworkSysInfoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -INT_PTR CALLBACK EtpNetworkDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK EtpNetworkPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtpNotifyNetworkGraph( - _In_ NMHDR *Header - ); - -VOID EtpUpdateNetworkGraph( - VOID - ); - -VOID EtpUpdateNetworkPanel( - VOID - ); - -PPH_PROCESS_RECORD EtpReferenceMaxNetworkRecord( - _In_ LONG Index - ); - -PPH_STRING EtpGetMaxNetworkString( - _In_ LONG Index - ); - -#endif +#ifndef ETWSYS_H +#define ETWSYS_H + +// Disk section + +BOOLEAN EtpDiskSysInfoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +INT_PTR CALLBACK EtpDiskDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK EtpDiskPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EtpNotifyDiskGraph( + _In_ NMHDR *Header + ); + +VOID EtpUpdateDiskGraph( + VOID + ); + +VOID EtpUpdateDiskPanel( + VOID + ); + +PPH_PROCESS_RECORD EtpReferenceMaxDiskRecord( + _In_ LONG Index + ); + +PPH_STRING EtpGetMaxDiskString( + _In_ LONG Index + ); + +// Network section + +BOOLEAN EtpNetworkSysInfoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +INT_PTR CALLBACK EtpNetworkDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK EtpNetworkPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EtpNotifyNetworkGraph( + _In_ NMHDR *Header + ); + +VOID EtpUpdateNetworkGraph( + VOID + ); + +VOID EtpUpdateNetworkPanel( + VOID + ); + +PPH_PROCESS_RECORD EtpReferenceMaxNetworkRecord( + _In_ LONG Index + ); + +PPH_STRING EtpGetMaxNetworkString( + _In_ LONG Index + ); + +#endif diff --git a/plugins/ExtendedTools/exttools.h b/plugins/ExtendedTools/exttools.h index ceacc3710c80..a62bc84b1d96 100644 --- a/plugins/ExtendedTools/exttools.h +++ b/plugins/ExtendedTools/exttools.h @@ -1,549 +1,630 @@ -#ifndef EXTTOOLS_H -#define EXTTOOLS_H - -#include -#include -#include - -#include "resource.h" - -extern PPH_PLUGIN PluginInstance; -extern LIST_ENTRY EtProcessBlockListHead; -extern LIST_ENTRY EtNetworkBlockListHead; -extern HWND ProcessTreeNewHandle; -extern HWND NetworkTreeNewHandle; - -#define PLUGIN_NAME L"ProcessHacker.ExtendedTools" -#define SETTING_NAME_DISK_TREE_LIST_COLUMNS (PLUGIN_NAME L".DiskTreeListColumns") -#define SETTING_NAME_DISK_TREE_LIST_SORT (PLUGIN_NAME L".DiskTreeListSort") -#define SETTING_NAME_ENABLE_ETW_MONITOR (PLUGIN_NAME L".EnableEtwMonitor") -#define SETTING_NAME_ENABLE_GPU_MONITOR (PLUGIN_NAME L".EnableGpuMonitor") -#define SETTING_NAME_ENABLE_SYSINFO_GRAPHS (PLUGIN_NAME L".EnableSysInfoGraphs") -#define SETTING_NAME_GPU_NODE_BITMAP (PLUGIN_NAME L".GpuNodeBitmap") -#define SETTING_NAME_GPU_LAST_NODE_COUNT (PLUGIN_NAME L".GpuLastNodeCount") - -#define ET_SCALE_DPI(Value) PhMultiplyDivide(Value, PhGlobalDpi, 96) - -// Graph update message - -#define UPDATE_MSG (WM_APP + 1) - -// Process icon - -typedef struct _ET_PROCESS_ICON -{ - LONG RefCount; - HICON Icon; -} ET_PROCESS_ICON, *PET_PROCESS_ICON; - -// Disk item - -#define HISTORY_SIZE 60 - -typedef struct _ET_DISK_ITEM -{ - LIST_ENTRY AgeListEntry; - ULONG AddTime; - ULONG FreshTime; - - HANDLE ProcessId; - PPH_STRING FileName; - PPH_STRING FileNameWin32; - - PPH_STRING ProcessName; - PET_PROCESS_ICON ProcessIcon; - PPH_PROCESS_RECORD ProcessRecord; - - ULONG IoPriority; - ULONG ResponseTimeCount; - FLOAT ResponseTimeTotal; // in milliseconds - FLOAT ResponseTimeAverage; - - ULONG64 ReadTotal; - ULONG64 WriteTotal; - ULONG64 ReadDelta; - ULONG64 WriteDelta; - ULONG64 ReadAverage; - ULONG64 WriteAverage; - - ULONG64 ReadHistory[HISTORY_SIZE]; - ULONG64 WriteHistory[HISTORY_SIZE]; - ULONG HistoryCount; - ULONG HistoryPosition; -} ET_DISK_ITEM, *PET_DISK_ITEM; - -// Disk node - -#define ETDSTNC_NAME 0 -#define ETDSTNC_FILE 1 -#define ETDSTNC_READRATEAVERAGE 2 -#define ETDSTNC_WRITERATEAVERAGE 3 -#define ETDSTNC_TOTALRATEAVERAGE 4 -#define ETDSTNC_IOPRIORITY 5 -#define ETDSTNC_RESPONSETIME 6 -#define ETDSTNC_MAXIMUM 7 - -typedef struct _ET_DISK_NODE -{ - PH_TREENEW_NODE Node; - - PET_DISK_ITEM DiskItem; - - PH_STRINGREF TextCache[ETDSTNC_MAXIMUM]; - - PPH_STRING ProcessNameText; - PPH_STRING ReadRateAverageText; - PPH_STRING WriteRateAverageText; - PPH_STRING TotalRateAverageText; - PPH_STRING ResponseTimeText; - - PPH_STRING TooltipText; -} ET_DISK_NODE, *PET_DISK_NODE; - -// Process tree columns - -#define ETPRTNC_DISKREADS 1 -#define ETPRTNC_DISKWRITES 2 -#define ETPRTNC_DISKREADBYTES 3 -#define ETPRTNC_DISKWRITEBYTES 4 -#define ETPRTNC_DISKTOTALBYTES 5 -#define ETPRTNC_DISKREADSDELTA 6 -#define ETPRTNC_DISKWRITESDELTA 7 -#define ETPRTNC_DISKREADBYTESDELTA 8 -#define ETPRTNC_DISKWRITEBYTESDELTA 9 -#define ETPRTNC_DISKTOTALBYTESDELTA 10 -#define ETPRTNC_NETWORKRECEIVES 11 -#define ETPRTNC_NETWORKSENDS 12 -#define ETPRTNC_NETWORKRECEIVEBYTES 13 -#define ETPRTNC_NETWORKSENDBYTES 14 -#define ETPRTNC_NETWORKTOTALBYTES 15 -#define ETPRTNC_NETWORKRECEIVESDELTA 16 -#define ETPRTNC_NETWORKSENDSDELTA 17 -#define ETPRTNC_NETWORKRECEIVEBYTESDELTA 18 -#define ETPRTNC_NETWORKSENDBYTESDELTA 19 -#define ETPRTNC_NETWORKTOTALBYTESDELTA 20 -#define ETPRTNC_HARDFAULTS 21 -#define ETPRTNC_HARDFAULTSDELTA 22 -#define ETPRTNC_PEAKTHREADS 23 -#define ETPRTNC_GPU 24 -#define ETPRTNC_GPUDEDICATEDBYTES 25 -#define ETPRTNC_GPUSHAREDBYTES 26 -#define ETPRTNC_DISKREADRATE 27 -#define ETPRTNC_DISKWRITERATE 28 -#define ETPRTNC_DISKTOTALRATE 29 -#define ETPRTNC_NETWORKRECEIVERATE 30 -#define ETPRTNC_NETWORKSENDRATE 31 -#define ETPRTNC_NETWORKTOTALRATE 32 -#define ETPRTNC_MAXIMUM 32 - -// Network list columns - -#define ETNETNC_RECEIVES 1 -#define ETNETNC_SENDS 2 -#define ETNETNC_RECEIVEBYTES 3 -#define ETNETNC_SENDBYTES 4 -#define ETNETNC_TOTALBYTES 5 -#define ETNETNC_RECEIVESDELTA 6 -#define ETNETNC_SENDSDELTA 7 -#define ETNETNC_RECEIVEBYTESDELTA 8 -#define ETNETNC_SENDBYTESDELTA 9 -#define ETNETNC_TOTALBYTESDELTA 10 -#define ETNETNC_FIREWALLSTATUS 11 -#define ETNETNC_RECEIVERATE 12 -#define ETNETNC_SENDRATE 13 -#define ETNETNC_TOTALRATE 14 -#define ETNETNC_MAXIMUM 14 - -// Firewall status - -typedef enum _ET_FIREWALL_STATUS -{ - FirewallUnknownStatus, - FirewallAllowedNotRestricted, - FirewallAllowedRestricted, - FirewallNotAllowedNotRestricted, - FirewallNotAllowedRestricted, - FirewallMaximumStatus -} ET_FIREWALL_STATUS; - -// Object extensions - -typedef struct _ET_PROCESS_BLOCK -{ - LIST_ENTRY ListEntry; - PPH_PROCESS_ITEM ProcessItem; - - ULONG64 DiskReadCount; - ULONG64 DiskWriteCount; - ULONG64 NetworkReceiveCount; - ULONG64 NetworkSendCount; - - ULONG64 DiskReadRaw; - ULONG64 DiskWriteRaw; - ULONG64 NetworkReceiveRaw; - ULONG64 NetworkSendRaw; - - PH_UINT64_DELTA DiskReadDelta; - PH_UINT64_DELTA DiskReadRawDelta; - PH_UINT64_DELTA DiskWriteDelta; - PH_UINT64_DELTA DiskWriteRawDelta; - PH_UINT64_DELTA NetworkReceiveDelta; - PH_UINT64_DELTA NetworkReceiveRawDelta; - PH_UINT64_DELTA NetworkSendDelta; - PH_UINT64_DELTA NetworkSendRawDelta; - - PH_UINT64_DELTA GpuRunningTimeDelta; - FLOAT GpuNodeUsage; - ULONG64 GpuDedicatedUsage; - ULONG64 GpuSharedUsage; - - PH_UINT32_DELTA HardFaultsDelta; - - PH_QUEUED_LOCK TextCacheLock; - PPH_STRING TextCache[ETPRTNC_MAXIMUM + 1]; - BOOLEAN TextCacheValid[ETPRTNC_MAXIMUM + 1]; - - PET_PROCESS_ICON SmallProcessIcon; -} ET_PROCESS_BLOCK, *PET_PROCESS_BLOCK; - -typedef struct _ET_NETWORK_BLOCK -{ - LIST_ENTRY ListEntry; - PPH_NETWORK_ITEM NetworkItem; - - ULONG64 ReceiveCount; - ULONG64 SendCount; - ULONG64 ReceiveRaw; - ULONG64 SendRaw; - - union - { - struct - { - PH_UINT64_DELTA ReceiveDelta; - PH_UINT64_DELTA ReceiveRawDelta; - PH_UINT64_DELTA SendDelta; - PH_UINT64_DELTA SendRawDelta; - }; - PH_UINT64_DELTA Deltas[4]; - }; - - ET_FIREWALL_STATUS FirewallStatus; - BOOLEAN FirewallStatusValid; - - PH_QUEUED_LOCK TextCacheLock; - PPH_STRING TextCache[ETNETNC_MAXIMUM + 1]; - BOOLEAN TextCacheValid[ETNETNC_MAXIMUM + 1]; -} ET_NETWORK_BLOCK, *PET_NETWORK_BLOCK; - -// main - -PET_PROCESS_BLOCK EtGetProcessBlock( - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -PET_NETWORK_BLOCK EtGetNetworkBlock( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -// utils - -VOID EtFormatRate( - _In_ ULONG64 ValuePerPeriod, - _Inout_ PPH_STRING *Buffer, - _Out_opt_ PPH_STRINGREF String - ); - -// etwmon - -extern BOOLEAN EtEtwEnabled; - -// etwstat - -extern ULONG EtDiskReadCount; -extern ULONG EtDiskWriteCount; -extern ULONG EtNetworkReceiveCount; -extern ULONG EtNetworkSendCount; - -extern PH_UINT32_DELTA EtDiskReadDelta; -extern PH_UINT32_DELTA EtDiskWriteDelta; -extern PH_UINT32_DELTA EtNetworkReceiveDelta; -extern PH_UINT32_DELTA EtNetworkSendDelta; - -extern PH_UINT32_DELTA EtDiskReadCountDelta; -extern PH_UINT32_DELTA EtDiskWriteCountDelta; -extern PH_UINT32_DELTA EtNetworkReceiveCountDelta; -extern PH_UINT32_DELTA EtNetworkSendCountDelta; - -extern PH_CIRCULAR_BUFFER_ULONG EtDiskReadHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtDiskWriteHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtNetworkReceiveHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtNetworkSendHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtMaxDiskHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtMaxNetworkHistory; - -VOID EtEtwStatisticsInitialization( - VOID - ); - -VOID EtEtwStatisticsUninitialization( - VOID - ); - -// etwdisk - -extern BOOLEAN EtDiskEnabled; - -extern PPH_OBJECT_TYPE EtDiskItemType; -extern PH_CALLBACK EtDiskItemAddedEvent; -extern PH_CALLBACK EtDiskItemModifiedEvent; -extern PH_CALLBACK EtDiskItemRemovedEvent; -extern PH_CALLBACK EtDiskItemsUpdatedEvent; - -VOID EtInitializeDiskInformation( - VOID - ); - -PET_DISK_ITEM EtCreateDiskItem( - VOID - ); - -PET_DISK_ITEM EtReferenceDiskItem( - _In_ HANDLE ProcessId, - _In_ PPH_STRING FileName - ); - -PPH_STRING EtFileObjectToFileName( - _In_ PVOID FileObject - ); - -// procicon - -PET_PROCESS_ICON EtProcIconCreateProcessIcon( - _In_ HICON Icon - ); - -VOID EtProcIconReferenceProcessIcon( - _Inout_ PET_PROCESS_ICON ProcessIcon - ); - -VOID EtProcIconDereferenceProcessIcon( - _Inout_ PET_PROCESS_ICON ProcessIcon - ); - -PET_PROCESS_ICON EtProcIconReferenceSmallProcessIcon( - _Inout_ PET_PROCESS_BLOCK Block - ); - -VOID EtProcIconNotifyProcessDelete( - _Inout_ PET_PROCESS_BLOCK Block - ); - -// etwprprp - -VOID EtProcessEtwPropertiesInitializing( - _In_ PVOID Parameter - ); - -// disktab - -VOID EtInitializeDiskTab( - VOID - ); - -VOID EtLoadSettingsDiskTreeList( - VOID - ); - -VOID EtSaveSettingsDiskTreeList( - VOID - ); - -// gpumon - -extern BOOLEAN EtGpuEnabled; - -extern ULONG EtGpuTotalNodeCount; -extern ULONG EtGpuTotalSegmentCount; -extern ULONG64 EtGpuDedicatedLimit; -extern ULONG64 EtGpuSharedLimit; -extern RTL_BITMAP EtGpuNodeBitMap; - -extern PH_UINT64_DELTA EtClockTotalRunningTimeDelta; -extern LARGE_INTEGER EtClockTotalRunningTimeFrequency; -extern PH_UINT64_DELTA EtGpuTotalRunningTimeDelta; -extern PH_UINT64_DELTA EtGpuSystemRunningTimeDelta; -extern FLOAT EtGpuNodeUsage; -extern PH_CIRCULAR_BUFFER_FLOAT EtGpuNodeHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtMaxGpuNodeHistory; // ID of max. GPU usage process -extern PH_CIRCULAR_BUFFER_FLOAT EtMaxGpuNodeUsageHistory; - -extern PPH_UINT64_DELTA EtGpuNodesTotalRunningTimeDelta; -extern PPH_CIRCULAR_BUFFER_FLOAT EtGpuNodesHistory; - -extern ULONG64 EtGpuDedicatedUsage; -extern ULONG64 EtGpuSharedUsage; -extern PH_CIRCULAR_BUFFER_ULONG EtGpuDedicatedHistory; -extern PH_CIRCULAR_BUFFER_ULONG EtGpuSharedHistory; - -VOID EtGpuMonitorInitialization( - VOID - ); - -typedef struct _ET_PROCESS_GPU_STATISTICS -{ - ULONG SegmentCount; - ULONG NodeCount; - - ULONG64 DedicatedCommitted; - ULONG64 SharedCommitted; - - ULONG64 BytesAllocated; - ULONG64 BytesReserved; - ULONG64 WriteCombinedBytesAllocated; - ULONG64 WriteCombinedBytesReserved; - ULONG64 CachedBytesAllocated; - ULONG64 CachedBytesReserved; - ULONG64 SectionBytesAllocated; - ULONG64 SectionBytesReserved; - - ULONG64 RunningTime; - ULONG64 ContextSwitches; -} ET_PROCESS_GPU_STATISTICS, *PET_PROCESS_GPU_STATISTICS; - -VOID EtSaveGpuMonitorSettings( - VOID - ); - -ULONG EtGetGpuAdapterCount( - VOID - ); - -ULONG EtGetGpuAdapterIndexFromNodeIndex( - _In_ ULONG NodeIndex - ); - -PPH_STRING EtGetGpuAdapterDescription( - _In_ ULONG Index - ); - -VOID EtAllocateGpuNodeBitMap( - _Out_ PRTL_BITMAP BitMap - ); - -VOID EtUpdateGpuNodeBitMap( - _In_ PRTL_BITMAP NewBitMap - ); - -VOID EtQueryProcessGpuStatistics( - _In_ HANDLE ProcessHandle, - _Out_ PET_PROCESS_GPU_STATISTICS Statistics - ); - -// gpuprprp - -VOID EtProcessGpuPropertiesInitializing( - _In_ PVOID Parameter - ); - -// treeext - -VOID EtProcessTreeNewInitializing( - _In_ PVOID Parameter - ); - -VOID EtProcessTreeNewMessage( - _In_ PVOID Parameter - ); - -VOID EtNetworkTreeNewInitializing( - _In_ PVOID Parameter - ); - -VOID EtNetworkTreeNewMessage( - _In_ PVOID Parameter - ); - -ET_FIREWALL_STATUS EtQueryFirewallStatus( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -// etwsys - -VOID EtEtwSystemInformationInitializing( - _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers - ); - -// etwmini - -VOID EtEtwMiniInformationInitializing( - _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers - ); - -// gpunodes - -VOID EtShowGpuNodesDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_SYSINFO_PARAMETERS Parameters - ); - -// gpusys - -VOID EtGpuSystemInformationInitializing( - _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers - ); - -// gpumini - -VOID EtGpuMiniInformationInitializing( - _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers - ); - -// iconext - -VOID EtRegisterNotifyIcons( - VOID - ); - -// modsrv - -VOID EtShowModuleServicesDialog( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId, - _In_ PWSTR ModuleName - ); - -// objprp - -VOID EtHandlePropertiesInitializing( - _In_ PVOID Parameter - ); - -// options - -VOID EtShowOptionsDialog( - _In_ HWND ParentWindowHandle - ); - -// thrdact - -BOOLEAN EtUiCancelIoThread( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM Thread - ); - -// unldll - -VOID EtShowUnloadedDllsDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -// wswatch - -VOID EtShowWsWatchDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ); - -#endif +#ifndef EXTTOOLS_H +#define EXTTOOLS_H + +#include +#include +#include +#include + +#include "resource.h" + +#include +#include "d3dkmt.h" + +extern PPH_PLUGIN PluginInstance; +extern LIST_ENTRY EtProcessBlockListHead; +extern LIST_ENTRY EtNetworkBlockListHead; +extern HWND ProcessTreeNewHandle; +extern HWND NetworkTreeNewHandle; +extern ULONG ProcessesUpdatedCount; + +#define PLUGIN_NAME L"ProcessHacker.ExtendedTools" +#define SETTING_NAME_DISK_TREE_LIST_COLUMNS (PLUGIN_NAME L".DiskTreeListColumns") +#define SETTING_NAME_DISK_TREE_LIST_SORT (PLUGIN_NAME L".DiskTreeListSort") +#define SETTING_NAME_ENABLE_ETW_MONITOR (PLUGIN_NAME L".EnableEtwMonitor") +#define SETTING_NAME_ENABLE_GPU_MONITOR (PLUGIN_NAME L".EnableGpuMonitor") +#define SETTING_NAME_ENABLE_SYSINFO_GRAPHS (PLUGIN_NAME L".EnableSysInfoGraphs") +#define SETTING_NAME_GPU_NODE_BITMAP (PLUGIN_NAME L".GpuNodeBitmap") +#define SETTING_NAME_GPU_LAST_NODE_COUNT (PLUGIN_NAME L".GpuLastNodeCount") +#define SETTING_NAME_UNLOADED_WINDOW_POSITION (PLUGIN_NAME L".TracertWindowPosition") +#define SETTING_NAME_UNLOADED_WINDOW_SIZE (PLUGIN_NAME L".TracertWindowSize") +#define SETTING_NAME_UNLOADED_COLUMNS (PLUGIN_NAME L".UnloadedListColumns") +#define SETTING_NAME_MODULE_SERVICES_WINDOW_POSITION (PLUGIN_NAME L".ModuleServiceWindowPosition") +#define SETTING_NAME_MODULE_SERVICES_WINDOW_SIZE (PLUGIN_NAME L".ModuleServiceWindowSize") +#define SETTING_NAME_MODULE_SERVICES_COLUMNS (PLUGIN_NAME L".ModuleServiceListColumns") +#define SETTING_NAME_GPU_NODES_WINDOW_POSITION (PLUGIN_NAME L".GpuNodesWindowPosition") +#define SETTING_NAME_GPU_NODES_WINDOW_SIZE (PLUGIN_NAME L".GpuNodesWindowSize") +#define SETTING_NAME_WSWATCH_WINDOW_POSITION (PLUGIN_NAME L".WsWatchWindowPosition") +#define SETTING_NAME_WSWATCH_WINDOW_SIZE (PLUGIN_NAME L".WsWatchWindowSize") +#define SETTING_NAME_WSWATCH_COLUMNS (PLUGIN_NAME L".WsWatchListColumns") +#define SETTING_NAME_TRAYICON_GUIDS (PLUGIN_NAME L".TrayIconGuids") + +// Window messages +#define ET_WM_SHOWDIALOG (WM_APP + 1) +#define ET_WM_UPDATE (WM_APP + 2) + +// phsvc extensions + +typedef enum _ET_PHSVC_API_NUMBER +{ + EtPhSvcGetProcessUnloadedDllsApiNumber = 1 +} ET_PHSVC_API_NUMBER; + +typedef union _ET_PHSVC_API_GETWOW64THREADAPPDOMAIN +{ + struct + { + ULONG ProcessId; + PH_RELATIVE_STRINGREF Data; // out + } i; + struct + { + ULONG DataLength; + } o; +} ET_PHSVC_API_GETWOW64THREADAPPDOMAIN, *PET_PHSVC_API_GETWOW64THREADAPPDOMAIN; + +VOID DispatchPhSvcRequest( + _In_ PVOID Parameter + ); + +NTSTATUS CallGetProcessUnloadedDlls( + _In_ HANDLE ProcessId, + _Out_ PPH_STRING *UnloadedDlls + ); + +// Disk item + +#define HISTORY_SIZE 60 + +typedef struct _ET_DISK_ITEM +{ + LIST_ENTRY AgeListEntry; + ULONG AddTime; + ULONG FreshTime; + + HANDLE ProcessId; + PPH_STRING FileName; + PPH_STRING FileNameWin32; + PPH_STRING ProcessName; + + PPH_PROCESS_ITEM ProcessItem; + HICON ProcessIcon; + BOOLEAN ProcessIconValid; + + PPH_PROCESS_RECORD ProcessRecord; + + ULONG IoPriority; + ULONG ResponseTimeCount; + FLOAT ResponseTimeTotal; // in milliseconds + FLOAT ResponseTimeAverage; + + ULONG64 ReadTotal; + ULONG64 WriteTotal; + ULONG64 ReadDelta; + ULONG64 WriteDelta; + ULONG64 ReadAverage; + ULONG64 WriteAverage; + + ULONG64 ReadHistory[HISTORY_SIZE]; + ULONG64 WriteHistory[HISTORY_SIZE]; + ULONG HistoryCount; + ULONG HistoryPosition; +} ET_DISK_ITEM, *PET_DISK_ITEM; + +// Disk node + +#define ETDSTNC_NAME 0 +#define ETDSTNC_FILE 1 +#define ETDSTNC_READRATEAVERAGE 2 +#define ETDSTNC_WRITERATEAVERAGE 3 +#define ETDSTNC_TOTALRATEAVERAGE 4 +#define ETDSTNC_IOPRIORITY 5 +#define ETDSTNC_RESPONSETIME 6 +#define ETDSTNC_MAXIMUM 7 + +typedef struct _ET_DISK_NODE +{ + PH_TREENEW_NODE Node; + + PET_DISK_ITEM DiskItem; + + PH_STRINGREF TextCache[ETDSTNC_MAXIMUM]; + + PPH_STRING ProcessNameText; + PPH_STRING ReadRateAverageText; + PPH_STRING WriteRateAverageText; + PPH_STRING TotalRateAverageText; + PPH_STRING ResponseTimeText; + + PPH_STRING TooltipText; +} ET_DISK_NODE, *PET_DISK_NODE; + +// Process tree columns + +#define ETPRTNC_DISKREADS 1 +#define ETPRTNC_DISKWRITES 2 +#define ETPRTNC_DISKREADBYTES 3 +#define ETPRTNC_DISKWRITEBYTES 4 +#define ETPRTNC_DISKTOTALBYTES 5 +#define ETPRTNC_DISKREADSDELTA 6 +#define ETPRTNC_DISKWRITESDELTA 7 +#define ETPRTNC_DISKREADBYTESDELTA 8 +#define ETPRTNC_DISKWRITEBYTESDELTA 9 +#define ETPRTNC_DISKTOTALBYTESDELTA 10 +#define ETPRTNC_NETWORKRECEIVES 11 +#define ETPRTNC_NETWORKSENDS 12 +#define ETPRTNC_NETWORKRECEIVEBYTES 13 +#define ETPRTNC_NETWORKSENDBYTES 14 +#define ETPRTNC_NETWORKTOTALBYTES 15 +#define ETPRTNC_NETWORKRECEIVESDELTA 16 +#define ETPRTNC_NETWORKSENDSDELTA 17 +#define ETPRTNC_NETWORKRECEIVEBYTESDELTA 18 +#define ETPRTNC_NETWORKSENDBYTESDELTA 19 +#define ETPRTNC_NETWORKTOTALBYTESDELTA 20 +#define ETPRTNC_HARDFAULTS 21 +#define ETPRTNC_HARDFAULTSDELTA 22 +#define ETPRTNC_PEAKTHREADS 23 +#define ETPRTNC_GPU 24 +#define ETPRTNC_GPUDEDICATEDBYTES 25 +#define ETPRTNC_GPUSHAREDBYTES 26 +#define ETPRTNC_DISKREADRATE 27 +#define ETPRTNC_DISKWRITERATE 28 +#define ETPRTNC_DISKTOTALRATE 29 +#define ETPRTNC_NETWORKRECEIVERATE 30 +#define ETPRTNC_NETWORKSENDRATE 31 +#define ETPRTNC_NETWORKTOTALRATE 32 +#define ETPRTNC_MAXIMUM 32 + +// Network list columns + +#define ETNETNC_RECEIVES 1 +#define ETNETNC_SENDS 2 +#define ETNETNC_RECEIVEBYTES 3 +#define ETNETNC_SENDBYTES 4 +#define ETNETNC_TOTALBYTES 5 +#define ETNETNC_RECEIVESDELTA 6 +#define ETNETNC_SENDSDELTA 7 +#define ETNETNC_RECEIVEBYTESDELTA 8 +#define ETNETNC_SENDBYTESDELTA 9 +#define ETNETNC_TOTALBYTESDELTA 10 +#define ETNETNC_FIREWALLSTATUS 11 +#define ETNETNC_RECEIVERATE 12 +#define ETNETNC_SENDRATE 13 +#define ETNETNC_TOTALRATE 14 +#define ETNETNC_MAXIMUM 14 + +typedef enum _ET_PROCESS_STATISTICS_CATEGORY +{ + ET_PROCESS_STATISTICS_CATEGORY_GPU, + ET_PROCESS_STATISTICS_CATEGORY_DISK, + ET_PROCESS_STATISTICS_CATEGORY_NETWORK, + ET_PROCESS_STATISTICS_CATEGORY_MAX +} ET_PROCESS_STATISTICS_CATEGORY; + +typedef enum _ET_PROCESS_STATISTICS_INDEX +{ + ET_PROCESS_STATISTICS_INDEX_RUNNINGTIME, + ET_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES, + //ET_PROCESS_STATISTICS_INDEX_TOTALNODES, + //ET_PROCESS_STATISTICS_INDEX_TOTALSEGMENTS, + ET_PROCESS_STATISTICS_INDEX_TOTALDEDICATED, + ET_PROCESS_STATISTICS_INDEX_TOTALSHARED, + ET_PROCESS_STATISTICS_INDEX_TOTALCOMMIT, + + ET_PROCESS_STATISTICS_INDEX_DISKREADS, + ET_PROCESS_STATISTICS_INDEX_DISKREADBYTES, + ET_PROCESS_STATISTICS_INDEX_DISKREADBYTESDELTA, + ET_PROCESS_STATISTICS_INDEX_DISKWRITES, + ET_PROCESS_STATISTICS_INDEX_DISKWRITEBYTES, + ET_PROCESS_STATISTICS_INDEX_DISKWRITEBYTESDELTA, + + ET_PROCESS_STATISTICS_INDEX_NETWORKREADS, + ET_PROCESS_STATISTICS_INDEX_NETWORKREADBYTES, + ET_PROCESS_STATISTICS_INDEX_NETWORKREADBYTESDELTA, + ET_PROCESS_STATISTICS_INDEX_NETWORKWRITES, + ET_PROCESS_STATISTICS_INDEX_NETWORKWRITEBYTES, + ET_PROCESS_STATISTICS_INDEX_NETWORKWRITEBYTESDELTA, + + ET_PROCESS_STATISTICS_INDEX_MAX +} ET_PROCESS_STATISTICS_INDEX; + +// Firewall status + +typedef enum _ET_FIREWALL_STATUS +{ + FirewallUnknownStatus, + FirewallAllowedNotRestricted, + FirewallAllowedRestricted, + FirewallNotAllowedNotRestricted, + FirewallNotAllowedRestricted, + FirewallMaximumStatus +} ET_FIREWALL_STATUS; + +// Object extensions + +typedef struct _ET_PROCESS_BLOCK +{ + LIST_ENTRY ListEntry; + PPH_PROCESS_ITEM ProcessItem; + + ULONG64 DiskReadCount; + ULONG64 DiskWriteCount; + ULONG64 NetworkReceiveCount; + ULONG64 NetworkSendCount; + + ULONG64 DiskReadRaw; + ULONG64 DiskWriteRaw; + ULONG64 NetworkReceiveRaw; + ULONG64 NetworkSendRaw; + + PH_UINT64_DELTA DiskReadDelta; + PH_UINT64_DELTA DiskReadRawDelta; + PH_UINT64_DELTA DiskWriteDelta; + PH_UINT64_DELTA DiskWriteRawDelta; + PH_UINT64_DELTA NetworkReceiveDelta; + PH_UINT64_DELTA NetworkReceiveRawDelta; + PH_UINT64_DELTA NetworkSendDelta; + PH_UINT64_DELTA NetworkSendRawDelta; + + PH_UINT64_DELTA GpuRunningTimeDelta; + //PPH_UINT64_DELTA GpuTotalRunningTimeDelta; + //PPH_CIRCULAR_BUFFER_FLOAT GpuTotalNodesHistory; + + ULONG GpuNodeCount; + ULONG GpuSegmentCount; + FLOAT GpuNodeUsage; + ULONG64 GpuDedicatedUsage; + ULONG64 GpuSharedUsage; + ULONG64 GpuCommitUsage; + ULONG64 GpuContextSwitches; + + PH_UINT32_DELTA HardFaultsDelta; + + PH_QUEUED_LOCK TextCacheLock; + PPH_STRING TextCache[ETPRTNC_MAXIMUM + 1]; + BOOLEAN TextCacheValid[ETPRTNC_MAXIMUM + 1]; + ULONG ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_MAX + 1]; + ULONG ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_MAX + 1]; +} ET_PROCESS_BLOCK, *PET_PROCESS_BLOCK; + +typedef struct _ET_NETWORK_BLOCK +{ + LIST_ENTRY ListEntry; + PPH_NETWORK_ITEM NetworkItem; + + ULONG64 ReceiveCount; + ULONG64 SendCount; + ULONG64 ReceiveRaw; + ULONG64 SendRaw; + + union + { + struct + { + PH_UINT64_DELTA ReceiveDelta; + PH_UINT64_DELTA ReceiveRawDelta; + PH_UINT64_DELTA SendDelta; + PH_UINT64_DELTA SendRawDelta; + }; + PH_UINT64_DELTA Deltas[4]; + }; + + ET_FIREWALL_STATUS FirewallStatus; + BOOLEAN FirewallStatusValid; + + PH_QUEUED_LOCK TextCacheLock; + PPH_STRING TextCache[ETNETNC_MAXIMUM + 1]; + BOOLEAN TextCacheValid[ETNETNC_MAXIMUM + 1]; +} ET_NETWORK_BLOCK, *PET_NETWORK_BLOCK; + +// main + +PET_PROCESS_BLOCK EtGetProcessBlock( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +PET_NETWORK_BLOCK EtGetNetworkBlock( + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +// utils + +VOID EtFormatRate( + _In_ ULONG64 ValuePerPeriod, + _Inout_ PPH_STRING *Buffer, + _Out_opt_ PPH_STRINGREF String + ); + +// etwmon + +extern BOOLEAN EtEtwEnabled; + +// etwstat + +extern ULONG EtDiskReadCount; +extern ULONG EtDiskWriteCount; +extern ULONG EtNetworkReceiveCount; +extern ULONG EtNetworkSendCount; + +extern PH_UINT32_DELTA EtDiskReadDelta; +extern PH_UINT32_DELTA EtDiskWriteDelta; +extern PH_UINT32_DELTA EtNetworkReceiveDelta; +extern PH_UINT32_DELTA EtNetworkSendDelta; + +extern PH_UINT32_DELTA EtDiskReadCountDelta; +extern PH_UINT32_DELTA EtDiskWriteCountDelta; +extern PH_UINT32_DELTA EtNetworkReceiveCountDelta; +extern PH_UINT32_DELTA EtNetworkSendCountDelta; + +extern PH_CIRCULAR_BUFFER_ULONG EtDiskReadHistory; +extern PH_CIRCULAR_BUFFER_ULONG EtDiskWriteHistory; +extern PH_CIRCULAR_BUFFER_ULONG EtNetworkReceiveHistory; +extern PH_CIRCULAR_BUFFER_ULONG EtNetworkSendHistory; +extern PH_CIRCULAR_BUFFER_ULONG EtMaxDiskHistory; +extern PH_CIRCULAR_BUFFER_ULONG EtMaxNetworkHistory; + +VOID EtEtwStatisticsInitialization( + VOID + ); + +VOID EtEtwStatisticsUninitialization( + VOID + ); + +// etwdisk + +extern BOOLEAN EtDiskEnabled; +extern ULONG EtRunCount; + +extern PPH_OBJECT_TYPE EtDiskItemType; +extern PH_CALLBACK EtDiskItemAddedEvent; +extern PH_CALLBACK EtDiskItemModifiedEvent; +extern PH_CALLBACK EtDiskItemRemovedEvent; +extern PH_CALLBACK EtDiskItemsUpdatedEvent; + +VOID EtInitializeDiskInformation( + VOID + ); + +PET_DISK_ITEM EtCreateDiskItem( + VOID + ); + +PET_DISK_ITEM EtReferenceDiskItem( + _In_ HANDLE ProcessId, + _In_ PPH_STRING FileName + ); + +PPH_STRING EtFileObjectToFileName( + _In_ PVOID FileObject + ); + +// etwprprp + +VOID EtProcessEtwPropertiesInitializing( + _In_ PVOID Parameter + ); + +// disktab + +VOID EtInitializeDiskTab( + VOID + ); + +VOID EtLoadSettingsDiskTreeList( + VOID + ); + +VOID EtSaveSettingsDiskTreeList( + VOID + ); + +// gpumon + +extern BOOLEAN EtGpuEnabled; +extern PPH_LIST EtpGpuAdapterList; + +extern ULONG EtGpuTotalNodeCount; +extern ULONG EtGpuTotalSegmentCount; +extern ULONG64 EtGpuDedicatedLimit; +extern ULONG64 EtGpuSharedLimit; + +extern PH_UINT64_DELTA EtClockTotalRunningTimeDelta; +extern LARGE_INTEGER EtClockTotalRunningTimeFrequency; +extern FLOAT EtGpuNodeUsage; +extern PH_CIRCULAR_BUFFER_FLOAT EtGpuNodeHistory; +extern PH_CIRCULAR_BUFFER_ULONG EtMaxGpuNodeHistory; // ID of max. GPU usage process +extern PH_CIRCULAR_BUFFER_FLOAT EtMaxGpuNodeUsageHistory; + +extern PPH_UINT64_DELTA EtGpuNodesTotalRunningTimeDelta; +extern PPH_CIRCULAR_BUFFER_FLOAT EtGpuNodesHistory; + +extern ULONG64 EtGpuDedicatedUsage; +extern ULONG64 EtGpuSharedUsage; +extern PH_CIRCULAR_BUFFER_ULONG64 EtGpuDedicatedHistory; +extern PH_CIRCULAR_BUFFER_ULONG64 EtGpuSharedHistory; + +VOID EtGpuMonitorInitialization( + VOID + ); + +NTSTATUS EtQueryAdapterInformation( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ KMTQUERYADAPTERINFOTYPE InformationClass, + _Out_writes_bytes_opt_(InformationLength) PVOID Information, + _In_ UINT32 InformationLength + ); + +BOOLEAN EtCloseAdapterHandle( + _In_ D3DKMT_HANDLE AdapterHandle + ); + +typedef struct _ET_PROCESS_GPU_STATISTICS +{ + ULONG SegmentCount; + ULONG NodeCount; + + ULONG64 DedicatedCommitted; + ULONG64 SharedCommitted; + + ULONG64 BytesAllocated; + ULONG64 BytesReserved; + ULONG64 WriteCombinedBytesAllocated; + ULONG64 WriteCombinedBytesReserved; + ULONG64 CachedBytesAllocated; + ULONG64 CachedBytesReserved; + ULONG64 SectionBytesAllocated; + ULONG64 SectionBytesReserved; + + ULONG64 RunningTime; + ULONG64 ContextSwitches; +} ET_PROCESS_GPU_STATISTICS, *PET_PROCESS_GPU_STATISTICS; + +VOID EtSaveGpuMonitorSettings( + VOID + ); + +ULONG EtGetGpuAdapterCount( + VOID + ); + +ULONG EtGetGpuAdapterIndexFromNodeIndex( + _In_ ULONG NodeIndex + ); + +PPH_STRING EtGetGpuAdapterNodeDescription( + _In_ ULONG Index, + _In_ ULONG NodeIndex + ); + +PPH_STRING EtGetGpuAdapterDescription( + _In_ ULONG Index + ); + +VOID EtQueryProcessGpuStatistics( + _In_ HANDLE ProcessHandle, + _Out_ PET_PROCESS_GPU_STATISTICS Statistics + ); + +// gpudetails + +VOID EtShowGpuDetailsDialog( + _In_ HWND ParentWindowHandle + ); + +// gpuprprp + +VOID EtProcessGpuPropertiesInitializing( + _In_ PVOID Parameter + ); + +// treeext + +VOID EtProcessTreeNewInitializing( + _In_ PVOID Parameter + ); + +VOID EtProcessTreeNewMessage( + _In_ PVOID Parameter + ); + +VOID EtNetworkTreeNewInitializing( + _In_ PVOID Parameter + ); + +VOID EtNetworkTreeNewMessage( + _In_ PVOID Parameter + ); + +ET_FIREWALL_STATUS EtQueryFirewallStatus( + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +// etwsys + +VOID EtEtwSystemInformationInitializing( + _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers + ); + +// etwmini + +VOID EtEtwMiniInformationInitializing( + _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers + ); + +// gpunodes + +VOID EtShowGpuNodesDialog( + _In_ HWND ParentWindowHandle + ); + +// gpusys + +VOID EtGpuSystemInformationInitializing( + _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers + ); + +// gpumini + +VOID EtGpuMiniInformationInitializing( + _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers + ); + +// iconext + +VOID EtLoadTrayIconGuids( + VOID + ); + +VOID EtRegisterNotifyIcons( + _In_ PPH_TRAY_ICON_POINTERS Pointers + ); + +VOID EtRegisterToolbarGraphs( + VOID + ); + +// modsrv + +VOID EtShowModuleServicesDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId, + _In_ PPH_STRING ModuleName + ); + +// objprp + +VOID EtHandlePropertiesInitializing( + _In_ PVOID Parameter + ); + +// options + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// thrdact + +BOOLEAN EtUiCancelIoThread( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM Thread + ); + +// unldll + +VOID EtShowUnloadedDllsDialog( + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +// wswatch + +VOID EtShowWsWatchDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ); + +#endif diff --git a/plugins/ExtendedTools/gpudetails.c b/plugins/ExtendedTools/gpudetails.c new file mode 100644 index 000000000000..dedc812db667 --- /dev/null +++ b/plugins/ExtendedTools/gpudetails.c @@ -0,0 +1,402 @@ +/* + * Process Hacker Extended Tools - + * GPU details window + * + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "gpumon.h" + +static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; +static PH_LAYOUT_MANAGER LayoutManager; + +typedef enum _GPUADAPTER_DETAILS_INDEX +{ + GPUADAPTER_DETAILS_INDEX_PHYSICALLOCTION, + GPUADAPTER_DETAILS_INDEX_DRIVERDATE, + GPUADAPTER_DETAILS_INDEX_DRIVERVERSION, + GPUADAPTER_DETAILS_INDEX_WDDMVERSION, + GPUADAPTER_DETAILS_INDEX_VENDORID, + GPUADAPTER_DETAILS_INDEX_DEVICEID, + GPUADAPTER_DETAILS_INDEX_TOTALMEMORY, + GPUADAPTER_DETAILS_INDEX_RESERVEDMEMORY, + GPUADAPTER_DETAILS_INDEX_MEMORYFREQUENCY, + GPUADAPTER_DETAILS_INDEX_MEMORYBANDWIDTH, + GPUADAPTER_DETAILS_INDEX_PCIEBANDWIDTH, + GPUADAPTER_DETAILS_INDEX_FANRPM, + GPUADAPTER_DETAILS_INDEX_POWERUSAGE, + GPUADAPTER_DETAILS_INDEX_TEMPERATURE, +} GPUADAPTER_DETAILS_INDEX; + +VOID EtpGpuDetailsAddListViewItemGroups( + _In_ HWND ListViewHandle, + _In_ INT DiskGroupId) +{ + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_PHYSICALLOCTION, L"Physical Location", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_DRIVERDATE, L"Driver Date", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_DRIVERVERSION, L"Driver Version", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, L"WDDM Version", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_VENDORID, L"Vendor ID", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_DEVICEID, L"Device ID", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_TOTALMEMORY, L"Total Memory", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_RESERVEDMEMORY, L"Reserved Memory", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_MEMORYFREQUENCY, L"Memory Frequency", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_MEMORYBANDWIDTH, L"Memory Bandwidth", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_PCIEBANDWIDTH, L"PCIE Bandwidth", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_FANRPM, L"Fan RPM", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_POWERUSAGE, L"Power Usage", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, GPUADAPTER_DETAILS_INDEX_TEMPERATURE, L"Temperature", NULL); +} + +VOID EtpQueryAdapterDeviceProperties( + _In_ PWSTR DeviceName, + _In_ HWND ListViewHandle) +{ + PPH_STRING driverDate; + PPH_STRING driverVersion; + PPH_STRING locationInfo; + ULONG64 installedMemory; + + if (EtQueryDeviceProperties(DeviceName, NULL, &driverDate, &driverVersion, &locationInfo, &installedMemory)) + { + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_DRIVERDATE, 1, PhGetStringOrEmpty(driverDate)); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_DRIVERVERSION, 1, PhGetStringOrEmpty(driverVersion)); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_PHYSICALLOCTION, 1, PhGetStringOrEmpty(locationInfo)); + + if (installedMemory != ULLONG_MAX) + { + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_TOTALMEMORY, 1, PhaFormatSize(installedMemory, ULONG_MAX)->Buffer); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_RESERVEDMEMORY, 1, PhaFormatSize(installedMemory - EtGpuDedicatedLimit, ULONG_MAX)->Buffer); + } + + PhClearReference(&locationInfo); + PhClearReference(&driverVersion); + PhClearReference(&driverDate); + } +} + +VOID EtpQueryAdapterRegistryInfo( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ HWND ListViewHandle) +{ + D3DKMT_ADAPTERREGISTRYINFO adapterInfo; + + memset(&adapterInfo, 0, sizeof(D3DKMT_ADAPTERREGISTRYINFO)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_ADAPTERREGISTRYINFO, + &adapterInfo, + sizeof(D3DKMT_ADAPTERREGISTRYINFO) + ))) + { + NOTHING; + } +} + +VOID EtpQueryAdapterDriverModel( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ HWND ListViewHandle) +{ + D3DKMT_DRIVERVERSION wddmversion; + + memset(&wddmversion, 0, sizeof(D3DKMT_DRIVERVERSION)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_DRIVERVERSION, + &wddmversion, + sizeof(D3DKMT_DRIVERVERSION) + ))) + { + switch (wddmversion) + { + case KMT_DRIVERVERSION_WDDM_1_0: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 1.0"); + break; + case KMT_DRIVERVERSION_WDDM_1_1_PRERELEASE: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 1.1 (pre-release)"); + break; + case KMT_DRIVERVERSION_WDDM_1_1: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 1.1"); + break; + case KMT_DRIVERVERSION_WDDM_1_2: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 1.2"); + break; + case KMT_DRIVERVERSION_WDDM_1_3: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 1.3"); + break; + case KMT_DRIVERVERSION_WDDM_2_0: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.0"); + break; + case KMT_DRIVERVERSION_WDDM_2_1: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.1"); + break; + case KMT_DRIVERVERSION_WDDM_2_2: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.2"); + break; + case KMT_DRIVERVERSION_WDDM_2_3: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.3"); + break; + case KMT_DRIVERVERSION_WDDM_2_4: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.4"); + break; + case KMT_DRIVERVERSION_WDDM_2_5: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.5"); + break; + case KMT_DRIVERVERSION_WDDM_2_6: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"WDDM 2.6"); + break; + default: + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_WDDMVERSION, 1, L"ERROR"); + break; + } + } +} + +VOID EtpQueryAdapterDriverVersion( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ HWND ListViewHandle) +{ + D3DKMT_UMD_DRIVER_VERSION driverUserVersion; + D3DKMT_KMD_DRIVER_VERSION driverKernelVersion; + + memset(&driverUserVersion, 0, sizeof(D3DKMT_UMD_DRIVER_VERSION)); + memset(&driverKernelVersion, 0, sizeof(D3DKMT_KMD_DRIVER_VERSION)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_UMD_DRIVER_VERSION, + &driverUserVersion, + sizeof(D3DKMT_UMD_DRIVER_VERSION) + ))) + { + PPH_STRING driverVersionString = PhFormatString( + L"%hu.%hu.%hu.%hu", + HIWORD(driverUserVersion.DriverVersion.HighPart), + LOWORD(driverUserVersion.DriverVersion.HighPart), + HIWORD(driverUserVersion.DriverVersion.LowPart), + LOWORD(driverUserVersion.DriverVersion.LowPart) + ); + PhDereferenceObject(driverVersionString); + } + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_KMD_DRIVER_VERSION, + &driverKernelVersion, + sizeof(D3DKMT_KMD_DRIVER_VERSION) + ))) + { + PPH_STRING driverVersionString = PhFormatString( + L"%hu.%hu.%hu.%hu", + HIWORD(driverKernelVersion.DriverVersion.HighPart), + LOWORD(driverKernelVersion.DriverVersion.HighPart), + HIWORD(driverKernelVersion.DriverVersion.LowPart), + LOWORD(driverKernelVersion.DriverVersion.LowPart) + ); + PhDereferenceObject(driverVersionString); + } +} + +VOID EtpQueryAdapterDeviceIds( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ HWND ListViewHandle) +{ + D3DKMT_QUERY_DEVICE_IDS adapterDeviceId; + + memset(&adapterDeviceId, 0, sizeof(D3DKMT_QUERY_DEVICE_IDS)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_PHYSICALADAPTERDEVICEIDS, + &adapterDeviceId, + sizeof(D3DKMT_QUERY_DEVICE_IDS) + ))) + { + WCHAR value[PH_PTR_STR_LEN_1]; + + PhPrintPointer(value, UlongToPtr(adapterDeviceId.DeviceIds.VendorID)); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_VENDORID, 1, value); + + PhPrintPointer(value, UlongToPtr(adapterDeviceId.DeviceIds.DeviceID)); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_DEVICEID, 1, value); + + //PhPrintPointer(value, UlongToPtr(adapterDeviceId.DeviceIds.SubVendorID)); + //PhPrintPointer(value, UlongToPtr(adapterDeviceId.DeviceIds.SubSystemID)); + //PhPrintPointer(value, UlongToPtr(adapterDeviceId.DeviceIds.RevisionID)); + //PhPrintPointer(value, UlongToPtr(adapterDeviceId.DeviceIds.BusType)); + } +} + +VOID EtpQueryAdapterPerfInfo( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ HWND ListViewHandle) +{ + D3DKMT_ADAPTER_PERFDATA adapterPerfData; + + memset(&adapterPerfData, 0, sizeof(D3DKMT_ADAPTER_PERFDATA)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_ADAPTERPERFDATA, + &adapterPerfData, + sizeof(D3DKMT_ADAPTER_PERFDATA) + ))) + { + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_MEMORYFREQUENCY, 1, PhaFormatString(L"%lu MHz", adapterPerfData.MemoryFrequency / 1000 / 1000)->Buffer); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_MEMORYBANDWIDTH, 1, PhaFormatSize(adapterPerfData.MemoryBandwidth, ULONG_MAX)->Buffer); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_PCIEBANDWIDTH, 1, PhaFormatSize(adapterPerfData.PCIEBandwidth, ULONG_MAX)->Buffer); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_FANRPM, 1, PhaFormatUInt64(adapterPerfData.FanRPM, FALSE)->Buffer); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_POWERUSAGE, 1, PhaFormatString(L"%lu%%", adapterPerfData.Power * 100 / 1000)->Buffer); + PhSetListViewSubItem(ListViewHandle, GPUADAPTER_DETAILS_INDEX_TEMPERATURE, 1, PhaFormatString(L"%lu\u00b0C", adapterPerfData.Temperature * 100 / 1000)->Buffer); + } +} + +VOID EtpGpuDetailsEnumAdapters( + _In_ HWND ListViewHandle + ) +{ + PETP_GPU_ADAPTER gpuAdapter; + D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName; + + for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + memset(&openAdapterFromDeviceName, 0, sizeof(D3DKMT_OPENADAPTERFROMDEVICENAME)); + openAdapterFromDeviceName.DeviceName = PhGetString(gpuAdapter->DeviceInterface); + + if (!NT_SUCCESS(D3DKMTOpenAdapterFromDeviceName(&openAdapterFromDeviceName))) + continue; + + if (!ListView_HasGroup(ListViewHandle, i)) + { + if (PhAddListViewGroup(ListViewHandle, i, PhGetString(gpuAdapter->Description)) == MAXINT) + { + EtCloseAdapterHandle(openAdapterFromDeviceName.AdapterHandle); + continue; + } + + EtpGpuDetailsAddListViewItemGroups(ListViewHandle, i); + } + + EtpQueryAdapterDeviceProperties(openAdapterFromDeviceName.DeviceName, ListViewHandle); + //EtpQueryAdapterRegistryInfo(openAdapterFromDeviceName.AdapterHandle, ListViewHandle); + EtpQueryAdapterDriverModel(openAdapterFromDeviceName.AdapterHandle, ListViewHandle); + //EtpQueryAdapterDriverVersion(openAdapterFromDeviceName.AdapterHandle, ListViewHandle); + EtpQueryAdapterDeviceIds(openAdapterFromDeviceName.AdapterHandle, ListViewHandle); + //EtQueryAdapterFeatureLevel(openAdapterFromDeviceName.AdapterLuid); + EtpQueryAdapterPerfInfo(openAdapterFromDeviceName.AdapterHandle, ListViewHandle); + + EtCloseAdapterHandle(openAdapterFromDeviceName.AdapterHandle); + } +} + +static VOID ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PostMessage((HWND)Context, ET_WM_UPDATE, 0, 0); +} + +INT_PTR CALLBACK EtpGpuDetailsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND listViewHandle = GetDlgItem(hwndDlg, IDC_GPULIST); + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetListViewStyle(listViewHandle, FALSE, TRUE); + PhSetControlTheme(listViewHandle, L"explorer"); + PhAddListViewColumn(listViewHandle, 0, 0, 0, LVCFMT_LEFT, 230, L"Property"); + PhAddListViewColumn(listViewHandle, 1, 1, 1, LVCFMT_LEFT, 200, L"Value"); + PhSetExtendedListView(listViewHandle); + ListView_EnableGroupView(listViewHandle, TRUE); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, listViewHandle, NULL, PH_ANCHOR_ALL); + + EtpGpuDetailsEnumAdapters(listViewHandle); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + ProcessesUpdatedCallback, + hwndDlg, + &ProcessesUpdatedCallbackRegistration + ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &ProcessesUpdatedCallbackRegistration); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDOK); + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case ET_WM_UPDATE: + { + EtpGpuDetailsEnumAdapters(GetDlgItem(hwndDlg, IDC_GPULIST)); + } + break; + } + + return FALSE; +} + +VOID EtShowGpuDetailsDialog( + _In_ HWND ParentWindowHandle + ) +{ + DialogBox( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_SYSINFO_GPUDETAILS), + ParentWindowHandle, + EtpGpuDetailsDlgProc + ); +} diff --git a/plugins/ExtendedTools/gpumini.c b/plugins/ExtendedTools/gpumini.c index ae20f1a6a744..db599455a68c 100644 --- a/plugins/ExtendedTools/gpumini.c +++ b/plugins/ExtendedTools/gpumini.c @@ -1,129 +1,129 @@ -/* - * Process Hacker Extended Tools - - * GPU mini information section - * - * Copyright (C) 2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "gpumini.h" - -VOID EtGpuMiniInformationInitializing( - _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers - ) -{ - PH_MINIINFO_LIST_SECTION section; - - memset(§ion, 0, sizeof(PH_MINIINFO_LIST_SECTION)); - section.Callback = EtpGpuListSectionCallback; - Pointers->CreateListSection(L"GPU", 0, §ion); -} - -BOOLEAN EtpGpuListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case MiListSectionTick: - { - ListSection->Section->Parameters->SetSectionText(ListSection->Section, - PhaFormatString(L"GPU %.2f%%", EtGpuNodeUsage * 100)); - } - break; - case MiListSectionSortProcessList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_PROCESS_NODE), EtpGpuListSectionProcessCompareFunction); - } - return TRUE; - case MiListSectionAssignSortData: - { - PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; - PPH_LIST processes = assignSortData->ProcessGroup->Processes; - FLOAT gpuUsage = 0; - ULONG i; - - for (i = 0; i < processes->Count; i++) - { - PPH_PROCESS_ITEM processItem = processes->Items[i]; - PET_PROCESS_BLOCK block = EtGetProcessBlock(processItem); - gpuUsage += block->GpuNodeUsage; - } - - *(PFLOAT)assignSortData->SortData->UserData = gpuUsage; - } - return TRUE; - case MiListSectionSortGroupList: - { - PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; - - qsort(sortList->List->Items, sortList->List->Count, - sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), EtpGpuListSectionNodeCompareFunction); - } - return TRUE; - case MiListSectionGetUsageText: - { - PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; - PPH_LIST processes = getUsageText->ProcessGroup->Processes; - FLOAT gpuUsage = *(PFLOAT)getUsageText->SortData->UserData; - - PhMoveReference(&getUsageText->Line1, PhFormatString(L"%.2f%%", gpuUsage * 100)); - } - return TRUE; - } - - return FALSE; -} - -int __cdecl EtpGpuListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - int result; - PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; - PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; - PET_PROCESS_BLOCK block1 = EtGetProcessBlock(node1->ProcessItem); - PET_PROCESS_BLOCK block2 = EtGetProcessBlock(node2->ProcessItem); - - result = singlecmp(block2->GpuNodeUsage, block1->GpuNodeUsage); - - if (result == 0) - result = uint64cmp(block2->GpuRunningTimeDelta.Value, block1->GpuRunningTimeDelta.Value); - if (result == 0) - result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); - - return result; -} - -int __cdecl EtpGpuListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ) -{ - PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; - PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; - - return singlecmp(*(PFLOAT)data2->UserData, *(PFLOAT)data1->UserData); -} +/* + * Process Hacker Extended Tools - + * GPU mini information section + * + * Copyright (C) 2015 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "gpumini.h" + +VOID EtGpuMiniInformationInitializing( + _In_ PPH_PLUGIN_MINIINFO_POINTERS Pointers + ) +{ + PH_MINIINFO_LIST_SECTION section; + + memset(§ion, 0, sizeof(PH_MINIINFO_LIST_SECTION)); + section.Callback = EtpGpuListSectionCallback; + Pointers->CreateListSection(L"GPU", 0, §ion); +} + +BOOLEAN EtpGpuListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case MiListSectionTick: + { + ListSection->Section->Parameters->SetSectionText(ListSection->Section, + PhaFormatString(L"GPU %.2f%%", EtGpuNodeUsage * 100)); + } + break; + case MiListSectionSortProcessList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_PROCESS_NODE), EtpGpuListSectionProcessCompareFunction); + } + return TRUE; + case MiListSectionAssignSortData: + { + PPH_MINIINFO_LIST_SECTION_ASSIGN_SORT_DATA assignSortData = Parameter1; + PPH_LIST processes = assignSortData->ProcessGroup->Processes; + FLOAT gpuUsage = 0; + ULONG i; + + for (i = 0; i < processes->Count; i++) + { + PPH_PROCESS_ITEM processItem = processes->Items[i]; + PET_PROCESS_BLOCK block = EtGetProcessBlock(processItem); + gpuUsage += block->GpuNodeUsage; + } + + *(PFLOAT)assignSortData->SortData->UserData = gpuUsage; + } + return TRUE; + case MiListSectionSortGroupList: + { + PPH_MINIINFO_LIST_SECTION_SORT_LIST sortList = Parameter1; + + qsort(sortList->List->Items, sortList->List->Count, + sizeof(PPH_MINIINFO_LIST_SECTION_SORT_DATA), EtpGpuListSectionNodeCompareFunction); + } + return TRUE; + case MiListSectionGetUsageText: + { + PPH_MINIINFO_LIST_SECTION_GET_USAGE_TEXT getUsageText = Parameter1; + PPH_LIST processes = getUsageText->ProcessGroup->Processes; + FLOAT gpuUsage = *(PFLOAT)getUsageText->SortData->UserData; + + PhMoveReference(&getUsageText->Line1, PhFormatString(L"%.2f%%", gpuUsage * 100)); + } + return TRUE; + } + + return FALSE; +} + +int __cdecl EtpGpuListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + int result; + PPH_PROCESS_NODE node1 = *(PPH_PROCESS_NODE *)elem1; + PPH_PROCESS_NODE node2 = *(PPH_PROCESS_NODE *)elem2; + PET_PROCESS_BLOCK block1 = EtGetProcessBlock(node1->ProcessItem); + PET_PROCESS_BLOCK block2 = EtGetProcessBlock(node2->ProcessItem); + + result = singlecmp(block2->GpuNodeUsage, block1->GpuNodeUsage); + + if (result == 0) + result = uint64cmp(block2->GpuRunningTimeDelta.Value, block1->GpuRunningTimeDelta.Value); + if (result == 0) + result = singlecmp(node2->ProcessItem->CpuUsage, node1->ProcessItem->CpuUsage); + + return result; +} + +int __cdecl EtpGpuListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ) +{ + PPH_MINIINFO_LIST_SECTION_SORT_DATA data1 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem1; + PPH_MINIINFO_LIST_SECTION_SORT_DATA data2 = *(PPH_MINIINFO_LIST_SECTION_SORT_DATA *)elem2; + + return singlecmp(*(PFLOAT)data2->UserData, *(PFLOAT)data1->UserData); +} diff --git a/plugins/ExtendedTools/gpumini.h b/plugins/ExtendedTools/gpumini.h index 2375e83f1807..d3cb6828f640 100644 --- a/plugins/ExtendedTools/gpumini.h +++ b/plugins/ExtendedTools/gpumini.h @@ -1,21 +1,21 @@ -#ifndef GPUMINI_H -#define GPUMINI_H - -BOOLEAN EtpGpuListSectionCallback( - _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, - _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -int __cdecl EtpGpuListSectionProcessCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - -int __cdecl EtpGpuListSectionNodeCompareFunction( - _In_ const void *elem1, - _In_ const void *elem2 - ); - +#ifndef GPUMINI_H +#define GPUMINI_H + +BOOLEAN EtpGpuListSectionCallback( + _In_ struct _PH_MINIINFO_LIST_SECTION *ListSection, + _In_ PH_MINIINFO_LIST_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +int __cdecl EtpGpuListSectionProcessCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + +int __cdecl EtpGpuListSectionNodeCompareFunction( + _In_ const void *elem1, + _In_ const void *elem2 + ); + #endif \ No newline at end of file diff --git a/plugins/ExtendedTools/gpumon.c b/plugins/ExtendedTools/gpumon.c index 1615f380d6a6..95743c4cc419 100644 --- a/plugins/ExtendedTools/gpumon.c +++ b/plugins/ExtendedTools/gpumon.c @@ -1,825 +1,1198 @@ -/* - * Process Hacker Extended Tools - - * GPU monitoring - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#define INITGUID -#include "exttools.h" -#include -#include -#include -#include "d3dkmt.h" -#include "gpumon.h" - -BOOLEAN EtGpuEnabled; -static PPH_LIST EtpGpuAdapterList; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; - -ULONG EtGpuTotalNodeCount; -ULONG EtGpuTotalSegmentCount; -ULONG64 EtGpuDedicatedLimit; -ULONG64 EtGpuSharedLimit; -ULONG EtGpuNextNodeIndex = 0; -RTL_BITMAP EtGpuNodeBitMap; -PULONG EtGpuNodeBitMapBuffer; -ULONG EtGpuNodeBitMapBitsSet; -PULONG EtGpuNewNodeBitMapBuffer; - -PH_UINT64_DELTA EtClockTotalRunningTimeDelta; -LARGE_INTEGER EtClockTotalRunningTimeFrequency; -PH_UINT64_DELTA EtGpuTotalRunningTimeDelta; -PH_UINT64_DELTA EtGpuSystemRunningTimeDelta; -FLOAT EtGpuNodeUsage; -PH_CIRCULAR_BUFFER_FLOAT EtGpuNodeHistory; -PH_CIRCULAR_BUFFER_ULONG EtMaxGpuNodeHistory; // ID of max. GPU usage process -PH_CIRCULAR_BUFFER_FLOAT EtMaxGpuNodeUsageHistory; - -PPH_UINT64_DELTA EtGpuNodesTotalRunningTimeDelta; -PPH_CIRCULAR_BUFFER_FLOAT EtGpuNodesHistory; - -ULONG64 EtGpuDedicatedUsage; -ULONG64 EtGpuSharedUsage; -PH_CIRCULAR_BUFFER_ULONG EtGpuDedicatedHistory; -PH_CIRCULAR_BUFFER_ULONG EtGpuSharedHistory; - -VOID EtGpuMonitorInitialization( - VOID - ) -{ - if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR)) - { - EtpGpuAdapterList = PhCreateList(4); - - if (EtpInitializeD3DStatistics()) - EtGpuEnabled = TRUE; - } - - if (EtGpuEnabled) - { - ULONG sampleCount; - ULONG i; - ULONG j; - PPH_STRING bitmapString; - D3DKMT_QUERYSTATISTICS queryStatistics; - - sampleCount = PhGetIntegerSetting(L"SampleCount"); - PhInitializeCircularBuffer_FLOAT(&EtGpuNodeHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtMaxGpuNodeHistory, sampleCount); - PhInitializeCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtGpuDedicatedHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&EtGpuSharedHistory, sampleCount); - - EtGpuNodesTotalRunningTimeDelta = PhAllocate(sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); - memset(EtGpuNodesTotalRunningTimeDelta, 0, sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); - EtGpuNodesHistory = PhAllocate(sizeof(PH_CIRCULAR_BUFFER_FLOAT) * EtGpuTotalNodeCount); - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - PhInitializeCircularBuffer_FLOAT(&EtGpuNodesHistory[i], sampleCount); - } - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - EtGpuProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); - - // Load the node bitmap. - - bitmapString = PhGetStringSetting(SETTING_NAME_GPU_NODE_BITMAP); - - if (!(bitmapString->Length & 3) && bitmapString->Length / 4 <= BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)) - { - PhHexStringToBuffer(&bitmapString->sr, (PUCHAR)EtGpuNodeBitMapBuffer); - EtGpuNodeBitMapBitsSet = RtlNumberOfSetBits(&EtGpuNodeBitMap); - } - - PhDereferenceObject(bitmapString); - - // Fix up the node bitmap if the current node count differs from what we've seen. - if (EtGpuTotalNodeCount != PhGetIntegerSetting(SETTING_NAME_GPU_LAST_NODE_COUNT)) - { - ULONG maxContextSwitch = 0; - ULONG maxContextSwitchNodeIndex = 0; - - RtlClearAllBits(&EtGpuNodeBitMap); - EtGpuNodeBitMapBitsSet = 0; - - for (i = 0; i < EtpGpuAdapterList->Count; i++) - { - PETP_GPU_ADAPTER gpuAdapter = EtpGpuAdapterList->Items[i]; - - for (j = 0; j < gpuAdapter->NodeCount; j++) - { - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_NODE; - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - queryStatistics.QueryNode.NodeId = j; - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - // The numbers below are quite arbitrary. - if (queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart != 0 && - queryStatistics.QueryResult.NodeInformation.GlobalInformation.ContextSwitch > 10000) - { - RtlSetBits(&EtGpuNodeBitMap, gpuAdapter->FirstNodeIndex + j, 1); - EtGpuNodeBitMapBitsSet++; - } - - if (maxContextSwitch < queryStatistics.QueryResult.NodeInformation.GlobalInformation.ContextSwitch) - { - maxContextSwitch = queryStatistics.QueryResult.NodeInformation.GlobalInformation.ContextSwitch; - maxContextSwitchNodeIndex = gpuAdapter->FirstNodeIndex + j; - } - } - } - } - - // Just in case - if (EtGpuNodeBitMapBitsSet == 0) - { - RtlSetBits(&EtGpuNodeBitMap, maxContextSwitchNodeIndex, 1); - EtGpuNodeBitMapBitsSet = 1; - } - - PhSetIntegerSetting(SETTING_NAME_GPU_LAST_NODE_COUNT, EtGpuTotalNodeCount); - } - } -} - -BOOLEAN EtpInitializeD3DStatistics( - VOID - ) -{ - PWSTR deviceInterfaceList; - ULONG deviceInterfaceListLength = 0; - PWSTR deviceInterface; - D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName; - D3DKMT_QUERYSTATISTICS queryStatistics; - D3DKMT_CLOSEADAPTER closeAdapter; - - if (CM_Get_Device_Interface_List_Size( - &deviceInterfaceListLength, - (PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL, - NULL, - CM_GET_DEVICE_INTERFACE_LIST_PRESENT - ) != CR_SUCCESS) - { - return FALSE; - } - - deviceInterfaceList = PhAllocate(deviceInterfaceListLength * sizeof(WCHAR)); - memset(deviceInterfaceList, 0, deviceInterfaceListLength * sizeof(WCHAR)); - - if (CM_Get_Device_Interface_List( - (PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL, - NULL, - deviceInterfaceList, - deviceInterfaceListLength, - CM_GET_DEVICE_INTERFACE_LIST_PRESENT - ) != CR_SUCCESS) - { - PhFree(deviceInterfaceList); - return FALSE; - } - - for (deviceInterface = deviceInterfaceList; *deviceInterface; deviceInterface += PhCountStringZ(deviceInterface) + 1) - { - memset(&openAdapterFromDeviceName, 0, sizeof(D3DKMT_OPENADAPTERFROMDEVICENAME)); - openAdapterFromDeviceName.pDeviceName = deviceInterface; - - if (NT_SUCCESS(D3DKMTOpenAdapterFromDeviceName(&openAdapterFromDeviceName))) - { - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER; - queryStatistics.AdapterLuid = openAdapterFromDeviceName.AdapterLuid; - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - PETP_GPU_ADAPTER gpuAdapter; - ULONG i; - - gpuAdapter = EtpAllocateGpuAdapter(queryStatistics.QueryResult.AdapterInformation.NbSegments); - gpuAdapter->AdapterLuid = openAdapterFromDeviceName.AdapterLuid; - gpuAdapter->Description = EtpQueryDeviceDescription(deviceInterface); - gpuAdapter->NodeCount = queryStatistics.QueryResult.AdapterInformation.NodeCount; - gpuAdapter->SegmentCount = queryStatistics.QueryResult.AdapterInformation.NbSegments; - RtlInitializeBitMap(&gpuAdapter->ApertureBitMap, gpuAdapter->ApertureBitMapBuffer, queryStatistics.QueryResult.AdapterInformation.NbSegments); - - PhAddItemList(EtpGpuAdapterList, gpuAdapter); - EtGpuTotalNodeCount += gpuAdapter->NodeCount; - EtGpuTotalSegmentCount += gpuAdapter->SegmentCount; - - gpuAdapter->FirstNodeIndex = EtGpuNextNodeIndex; - EtGpuNextNodeIndex += gpuAdapter->NodeCount; - - for (i = 0; i < gpuAdapter->SegmentCount; i++) - { - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - queryStatistics.QuerySegment.SegmentId = i; - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - ULONG64 commitLimit; - ULONG aperture; - - if (WindowsVersion >= WINDOWS_8) - { - commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit; - aperture = queryStatistics.QueryResult.SegmentInformation.Aperture; - } - else - { - commitLimit = queryStatistics.QueryResult.SegmentInformationV1.CommitLimit; - aperture = queryStatistics.QueryResult.SegmentInformationV1.Aperture; - } - - if (aperture) - EtGpuSharedLimit += commitLimit; - else - EtGpuDedicatedLimit += commitLimit; - - if (aperture) - RtlSetBits(&gpuAdapter->ApertureBitMap, i, 1); - } - } - } - - memset(&closeAdapter, 0, sizeof(D3DKMT_CLOSEADAPTER)); - closeAdapter.hAdapter = openAdapterFromDeviceName.hAdapter; - D3DKMTCloseAdapter(&closeAdapter); - } - } - - PhFree(deviceInterfaceList); - - EtGpuNodeBitMapBuffer = PhAllocate(BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)); - RtlInitializeBitMap(&EtGpuNodeBitMap, EtGpuNodeBitMapBuffer, EtGpuTotalNodeCount); - RtlSetBits(&EtGpuNodeBitMap, 0, 1); - EtGpuNodeBitMapBitsSet = 1; - - return TRUE; -} - -PETP_GPU_ADAPTER EtpAllocateGpuAdapter( - _In_ ULONG NumberOfSegments - ) -{ - PETP_GPU_ADAPTER adapter; - SIZE_T sizeNeeded; - - sizeNeeded = FIELD_OFFSET(ETP_GPU_ADAPTER, ApertureBitMapBuffer); - sizeNeeded += BYTES_NEEDED_FOR_BITS(NumberOfSegments); - - adapter = PhAllocate(sizeNeeded); - memset(adapter, 0, sizeNeeded); - - return adapter; -} - -PPH_STRING EtpQueryDeviceDescription( - _In_ PWSTR DeviceInterface - ) -{ - CONFIGRET result; - PPH_STRING string; - ULONG bufferSize; - DEVPROPTYPE devicePropertyType; - DEVINST deviceInstanceHandle; - ULONG deviceInstanceIdLength = MAX_DEVICE_ID_LEN; - WCHAR deviceInstanceId[MAX_DEVICE_ID_LEN]; - - if (CM_Get_Device_Interface_Property( - DeviceInterface, - &DEVPKEY_Device_InstanceId, - &devicePropertyType, - (PBYTE)deviceInstanceId, - &deviceInstanceIdLength, - 0 - ) != CR_SUCCESS) - { - return NULL; - } - - if (CM_Locate_DevNode( - &deviceInstanceHandle, - deviceInstanceId, - CM_LOCATE_DEVNODE_NORMAL - ) != CR_SUCCESS) - { - return NULL; - } - - bufferSize = 0x40; - string = PhCreateStringEx(NULL, bufferSize); - - if ((result = CM_Get_DevNode_Property( // CM_Get_DevNode_Registry_Property with CM_DRP_DEVICEDESC?? - deviceInstanceHandle, - &DEVPKEY_Device_DeviceDesc, - &devicePropertyType, - (PBYTE)string->Buffer, - &bufferSize, - 0 - )) != CR_SUCCESS) - { - PhDereferenceObject(string); - string = PhCreateStringEx(NULL, bufferSize); - - result = CM_Get_DevNode_Property( - deviceInstanceHandle, - &DEVPKEY_Device_DeviceDesc, - &devicePropertyType, - (PBYTE)string->Buffer, - &bufferSize, - 0 - ); - } - - if (result != CR_SUCCESS) - { - PhDereferenceObject(string); - return NULL; - } - - PhTrimToNullTerminatorString(string); - - return string; -} - -VOID EtpUpdateSegmentInformation( - _In_opt_ PET_PROCESS_BLOCK Block - ) -{ - ULONG i; - ULONG j; - PETP_GPU_ADAPTER gpuAdapter; - D3DKMT_QUERYSTATISTICS queryStatistics; - ULONG64 dedicatedUsage; - ULONG64 sharedUsage; - - if (Block && !Block->ProcessItem->QueryHandle) - return; - - dedicatedUsage = 0; - sharedUsage = 0; - - for (i = 0; i < EtpGpuAdapterList->Count; i++) - { - gpuAdapter = EtpGpuAdapterList->Items[i]; - - for (j = 0; j < gpuAdapter->SegmentCount; j++) - { - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - - if (Block) - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT; - else - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; - - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - - if (Block) - { - queryStatistics.hProcess = Block->ProcessItem->QueryHandle; - queryStatistics.QueryProcessSegment.SegmentId = j; - } - else - { - queryStatistics.QuerySegment.SegmentId = j; - } - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - if (Block) - { - ULONG64 bytesCommitted; - - if (WindowsVersion >= WINDOWS_8) - { - bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; - } - else - { - bytesCommitted = (ULONG)queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; - } - - if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) - sharedUsage += bytesCommitted; - else - dedicatedUsage += bytesCommitted; - } - else - { - ULONG64 bytesCommitted; - - if (WindowsVersion >= WINDOWS_8) - { - bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesResident; - } - else - { - bytesCommitted = queryStatistics.QueryResult.SegmentInformationV1.BytesResident; - } - - if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) - sharedUsage += bytesCommitted; - else - dedicatedUsage += bytesCommitted; - } - } - } - } - - if (Block) - { - Block->GpuDedicatedUsage = dedicatedUsage; - Block->GpuSharedUsage = sharedUsage; - } - else - { - EtGpuDedicatedUsage = dedicatedUsage; - EtGpuSharedUsage = sharedUsage; - } -} - -VOID EtpUpdateNodeInformation( - _In_opt_ PET_PROCESS_BLOCK Block - ) -{ - ULONG i; - ULONG j; - PETP_GPU_ADAPTER gpuAdapter; - D3DKMT_QUERYSTATISTICS queryStatistics; - ULONG64 totalRunningTime; - ULONG64 systemRunningTime; - - if (Block && !Block->ProcessItem->QueryHandle) - return; - - totalRunningTime = 0; - systemRunningTime = 0; - - for (i = 0; i < EtpGpuAdapterList->Count; i++) - { - gpuAdapter = EtpGpuAdapterList->Items[i]; - - for (j = 0; j < gpuAdapter->NodeCount; j++) - { - if (Block && !RtlCheckBit(&EtGpuNodeBitMap, gpuAdapter->FirstNodeIndex + j)) - continue; - - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - - if (Block) - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_NODE; - else - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_NODE; - - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - - if (Block) - { - queryStatistics.hProcess = Block->ProcessItem->QueryHandle; - queryStatistics.QueryProcessNode.NodeId = j; - } - else - { - queryStatistics.QueryNode.NodeId = j; - } - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - if (Block) - { - totalRunningTime += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; - } - else - { - ULONG nodeIndex; - - nodeIndex = gpuAdapter->FirstNodeIndex + j; - - PhUpdateDelta(&EtGpuNodesTotalRunningTimeDelta[nodeIndex], queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart); - - if (RtlCheckBit(&EtGpuNodeBitMap, gpuAdapter->FirstNodeIndex + j)) - { - totalRunningTime += queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart; - systemRunningTime += queryStatistics.QueryResult.NodeInformation.SystemInformation.RunningTime.QuadPart; - } - } - } - } - } - - if (Block) - { - PhUpdateDelta(&Block->GpuRunningTimeDelta, totalRunningTime); - } - else - { - LARGE_INTEGER performanceCounter; - - NtQueryPerformanceCounter(&performanceCounter, &EtClockTotalRunningTimeFrequency); - PhUpdateDelta(&EtClockTotalRunningTimeDelta, performanceCounter.QuadPart); - PhUpdateDelta(&EtGpuTotalRunningTimeDelta, totalRunningTime); - PhUpdateDelta(&EtGpuSystemRunningTimeDelta, systemRunningTime); - } -} - -VOID NTAPI EtGpuProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - static ULONG runCount = 0; // MUST keep in sync with runCount in process provider - - DOUBLE elapsedTime; // total GPU node elapsed time in micro-seconds - ULONG i; - PLIST_ENTRY listEntry; - FLOAT maxNodeValue = 0; - PET_PROCESS_BLOCK maxNodeBlock = NULL; - - // Update global statistics. - - EtpUpdateSegmentInformation(NULL); - EtpUpdateNodeInformation(NULL); - - elapsedTime = (DOUBLE)EtClockTotalRunningTimeDelta.Delta * 10000000 / EtClockTotalRunningTimeFrequency.QuadPart; - - if (elapsedTime != 0) - EtGpuNodeUsage = (FLOAT)(EtGpuTotalRunningTimeDelta.Delta / (elapsedTime * EtGpuNodeBitMapBitsSet)); - else - EtGpuNodeUsage = 0; - - if (EtGpuNodeUsage > 1) - EtGpuNodeUsage = 1; - - // Do the update of the node bitmap if needed. - if (EtGpuNewNodeBitMapBuffer) - { - PULONG newBuffer; - - newBuffer = _InterlockedExchangePointer(&EtGpuNewNodeBitMapBuffer, NULL); - - if (newBuffer) - { - PhFree(EtGpuNodeBitMap.Buffer); - EtGpuNodeBitMap.Buffer = newBuffer; - EtGpuNodeBitMapBuffer = newBuffer; - EtGpuNodeBitMapBitsSet = RtlNumberOfSetBits(&EtGpuNodeBitMap); - EtSaveGpuMonitorSettings(); - } - } - - // Update per-process statistics. - // Note: no lock is needed because we only ever modify the list on this same thread. - - listEntry = EtProcessBlockListHead.Flink; - - while (listEntry != &EtProcessBlockListHead) - { - PET_PROCESS_BLOCK block; - - block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); - - EtpUpdateSegmentInformation(block); - EtpUpdateNodeInformation(block); - - if (elapsedTime != 0) - { - block->GpuNodeUsage = (FLOAT)(block->GpuRunningTimeDelta.Delta / (elapsedTime * EtGpuNodeBitMapBitsSet)); - - if (block->GpuNodeUsage > 1) - block->GpuNodeUsage = 1; - } - - if (maxNodeValue < block->GpuNodeUsage) - { - maxNodeValue = block->GpuNodeUsage; - maxNodeBlock = block; - } - - listEntry = listEntry->Flink; - } - - // Update history buffers. - - if (runCount != 0) - { - PhAddItemCircularBuffer_FLOAT(&EtGpuNodeHistory, EtGpuNodeUsage); - PhAddItemCircularBuffer_ULONG(&EtGpuDedicatedHistory, (ULONG)(EtGpuDedicatedUsage / PAGE_SIZE)); - PhAddItemCircularBuffer_ULONG(&EtGpuSharedHistory, (ULONG)(EtGpuSharedUsage / PAGE_SIZE)); - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - FLOAT usage; - - usage = (FLOAT)(EtGpuNodesTotalRunningTimeDelta[i].Delta / elapsedTime); - - if (usage > 1) - usage = 1; - - PhAddItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], usage); - } - - if (maxNodeBlock) - { - PhAddItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, HandleToUlong(maxNodeBlock->ProcessItem->ProcessId)); - PhAddItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, maxNodeBlock->GpuNodeUsage); - PhReferenceProcessRecordForStatistics(maxNodeBlock->ProcessItem->Record); - } - else - { - PhAddItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, 0); - PhAddItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, 0); - } - } - - runCount++; -} - -VOID EtSaveGpuMonitorSettings( - VOID - ) -{ - PPH_STRING string; - - string = PhBufferToHexString((PUCHAR)EtGpuNodeBitMapBuffer, BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount)); - PhSetStringSetting2(SETTING_NAME_GPU_NODE_BITMAP, &string->sr); - PhDereferenceObject(string); -} - -ULONG EtGetGpuAdapterCount( - VOID - ) -{ - return EtpGpuAdapterList->Count; -} - -ULONG EtGetGpuAdapterIndexFromNodeIndex( - _In_ ULONG NodeIndex - ) -{ - ULONG i; - PETP_GPU_ADAPTER gpuAdapter; - - for (i = 0; i < EtpGpuAdapterList->Count; i++) - { - gpuAdapter = EtpGpuAdapterList->Items[i]; - - if (NodeIndex >= gpuAdapter->FirstNodeIndex && NodeIndex < gpuAdapter->FirstNodeIndex + gpuAdapter->NodeCount) - return i; - } - - return -1; -} - -PPH_STRING EtGetGpuAdapterDescription( - _In_ ULONG Index - ) -{ - PPH_STRING description; - - if (Index >= EtpGpuAdapterList->Count) - return NULL; - - description = ((PETP_GPU_ADAPTER)EtpGpuAdapterList->Items[Index])->Description; - - if (description) - { - PhReferenceObject(description); - return description; - } - else - { - return NULL; - } -} - -VOID EtAllocateGpuNodeBitMap( - _Out_ PRTL_BITMAP BitMap - ) -{ - SIZE_T numberOfBytes; - - numberOfBytes = BYTES_NEEDED_FOR_BITS(EtGpuTotalNodeCount); - - BitMap->Buffer = PhAllocate(numberOfBytes); - BitMap->SizeOfBitMap = EtGpuTotalNodeCount; - memset(BitMap->Buffer, 0, numberOfBytes); -} - -VOID EtUpdateGpuNodeBitMap( - _In_ PRTL_BITMAP NewBitMap - ) -{ - PULONG buffer; - - buffer = _InterlockedExchangePointer(&EtGpuNewNodeBitMapBuffer, NewBitMap->Buffer); - - if (buffer) - PhFree(buffer); -} - -VOID EtQueryProcessGpuStatistics( - _In_ HANDLE ProcessHandle, - _Out_ PET_PROCESS_GPU_STATISTICS Statistics - ) -{ - NTSTATUS status; - ULONG i; - ULONG j; - PETP_GPU_ADAPTER gpuAdapter; - D3DKMT_QUERYSTATISTICS queryStatistics; - - memset(Statistics, 0, sizeof(ET_PROCESS_GPU_STATISTICS)); - - for (i = 0; i < EtpGpuAdapterList->Count; i++) - { - gpuAdapter = EtpGpuAdapterList->Items[i]; - - Statistics->SegmentCount += gpuAdapter->SegmentCount; - Statistics->NodeCount += gpuAdapter->NodeCount; - - for (j = 0; j < gpuAdapter->SegmentCount; j++) - { - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT; - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - queryStatistics.hProcess = ProcessHandle; - queryStatistics.QueryProcessSegment.SegmentId = j; - - if (NT_SUCCESS(status = D3DKMTQueryStatistics(&queryStatistics))) - { - ULONG64 bytesCommitted; - - if (WindowsVersion >= WINDOWS_8) - { - bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; - } - else - { - bytesCommitted = (ULONG)queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; - } - - if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) - Statistics->SharedCommitted += bytesCommitted; - else - Statistics->DedicatedCommitted += bytesCommitted; - } - } - - for (j = 0; j < gpuAdapter->NodeCount; j++) - { - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_NODE; - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - queryStatistics.hProcess = ProcessHandle; - queryStatistics.QueryProcessNode.NodeId = j; - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - Statistics->RunningTime += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; - Statistics->ContextSwitches += queryStatistics.QueryResult.ProcessNodeInformation.ContextSwitch; - } - } - - memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); - queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS; - queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; - queryStatistics.hProcess = ProcessHandle; - - if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) - { - Statistics->BytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesAllocated; - Statistics->BytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesReserved; - Statistics->WriteCombinedBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.WriteCombinedBytesAllocated; - Statistics->WriteCombinedBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.WriteCombinedBytesReserved; - Statistics->CachedBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.CachedBytesAllocated; - Statistics->CachedBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.CachedBytesReserved; - Statistics->SectionBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.SectionBytesAllocated; - Statistics->SectionBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.SectionBytesReserved; - } - } -} +/* + * Process Hacker Extended Tools - + * GPU monitoring + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2016-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include +#include +#include "gpumon.h" + +BOOLEAN EtGpuEnabled; +PPH_LIST EtpGpuAdapterList; +static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; + +ULONG EtGpuTotalNodeCount = 0; +ULONG EtGpuTotalSegmentCount = 0; +ULONG EtGpuNextNodeIndex = 0; + +PH_UINT64_DELTA EtClockTotalRunningTimeDelta = { 0, 0 }; +LARGE_INTEGER EtClockTotalRunningTimeFrequency = { 0 }; + +FLOAT EtGpuNodeUsage = 0; +PH_CIRCULAR_BUFFER_FLOAT EtGpuNodeHistory; +PH_CIRCULAR_BUFFER_ULONG EtMaxGpuNodeHistory; // ID of max. GPU usage process +PH_CIRCULAR_BUFFER_FLOAT EtMaxGpuNodeUsageHistory; + +PPH_UINT64_DELTA EtGpuNodesTotalRunningTimeDelta; +PPH_CIRCULAR_BUFFER_FLOAT EtGpuNodesHistory; + +ULONG64 EtGpuDedicatedLimit = 0; +ULONG64 EtGpuDedicatedUsage = 0; +ULONG64 EtGpuSharedLimit = 0; +ULONG64 EtGpuSharedUsage = 0; +PH_CIRCULAR_BUFFER_ULONG64 EtGpuDedicatedHistory; +PH_CIRCULAR_BUFFER_ULONG64 EtGpuSharedHistory; + +VOID EtGpuMonitorInitialization( + VOID + ) +{ + if (PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR)) + { + EtpGpuAdapterList = PhCreateList(4); + + if (EtpInitializeD3DStatistics()) + EtGpuEnabled = TRUE; + } + + if (EtGpuEnabled) + { + ULONG sampleCount; + ULONG i; + + sampleCount = PhGetIntegerSetting(L"SampleCount"); + PhInitializeCircularBuffer_FLOAT(&EtGpuNodeHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&EtMaxGpuNodeHistory, sampleCount); + PhInitializeCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, sampleCount); + PhInitializeCircularBuffer_ULONG64(&EtGpuDedicatedHistory, sampleCount); + PhInitializeCircularBuffer_ULONG64(&EtGpuSharedHistory, sampleCount); + + EtGpuNodesTotalRunningTimeDelta = PhAllocateZero(sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); + EtGpuNodesHistory = PhAllocate(sizeof(PH_CIRCULAR_BUFFER_FLOAT) * EtGpuTotalNodeCount); + + for (i = 0; i < EtGpuTotalNodeCount; i++) + { + PhInitializeCircularBuffer_FLOAT(&EtGpuNodesHistory[i], sampleCount); + } + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + EtGpuProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + } +} + +NTSTATUS EtQueryAdapterInformation( + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ KMTQUERYADAPTERINFOTYPE InformationClass, + _Out_writes_bytes_opt_(InformationLength) PVOID Information, + _In_ UINT32 InformationLength + ) +{ + D3DKMT_QUERYADAPTERINFO queryAdapterInfo; + + memset(&queryAdapterInfo, 0, sizeof(D3DKMT_QUERYADAPTERINFO)); + + queryAdapterInfo.AdapterHandle = AdapterHandle; + queryAdapterInfo.Type = InformationClass; + queryAdapterInfo.PrivateDriverData = Information; + queryAdapterInfo.PrivateDriverDataSize = InformationLength; + + return D3DKMTQueryAdapterInfo(&queryAdapterInfo); +} + +BOOLEAN EtCloseAdapterHandle( + _In_ D3DKMT_HANDLE AdapterHandle + ) +{ + D3DKMT_CLOSEADAPTER closeAdapter; + + memset(&closeAdapter, 0, sizeof(D3DKMT_CLOSEADAPTER)); + closeAdapter.AdapterHandle = AdapterHandle; + + return NT_SUCCESS(D3DKMTCloseAdapter(&closeAdapter)); +} + +D3DKMT_DRIVERVERSION EtpGetGpuWddmVersion( + _In_ D3DKMT_HANDLE AdapterHandle + ) +{ + D3DKMT_DRIVERVERSION driverVersion; + + memset(&driverVersion, 0, sizeof(D3DKMT_DRIVERVERSION)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_DRIVERVERSION, + &driverVersion, + sizeof(D3DKMT_ADAPTERTYPE) + ))) + { + return driverVersion; + } + + return KMT_DRIVERVERSION_WDDM_1_0; +} + +BOOLEAN EtpIsGpuSoftwareDevice( + _In_ D3DKMT_HANDLE AdapterHandle + ) +{ + D3DKMT_ADAPTERTYPE adapterType; + + memset(&adapterType, 0, sizeof(D3DKMT_ADAPTERTYPE)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_ADAPTERTYPE, + &adapterType, + sizeof(D3DKMT_ADAPTERTYPE) + ))) + { + if (adapterType.SoftwareDevice) // adapterType.HybridIntegrated + { + return TRUE; + } + } + + return FALSE; +} + +PPH_STRING EtpGetNodeEngineTypeString( + _In_ D3DKMT_NODEMETADATA NodeMetaData + ) +{ + switch (NodeMetaData.EngineType) + { + case DXGK_ENGINE_TYPE_OTHER: + return PhCreateString(NodeMetaData.FriendlyName); + case DXGK_ENGINE_TYPE_3D: + return PhCreateString(L"3D"); + case DXGK_ENGINE_TYPE_VIDEO_DECODE: + return PhCreateString(L"Video Decode"); + case DXGK_ENGINE_TYPE_VIDEO_ENCODE: + return PhCreateString(L"Video Encode"); + case DXGK_ENGINE_TYPE_VIDEO_PROCESSING: + return PhCreateString(L"Video Processing"); + case DXGK_ENGINE_TYPE_SCENE_ASSEMBLY: + return PhCreateString(L"Scene Assembly"); + case DXGK_ENGINE_TYPE_COPY: + return PhCreateString(L"Copy"); + case DXGK_ENGINE_TYPE_OVERLAY: + return PhCreateString(L"Overlay"); + case DXGK_ENGINE_TYPE_CRYPTO: + return PhCreateString(L"Crypto"); + } + + return PhFormatString(L"ERROR (%lu)", NodeMetaData.EngineType); +} + +PPH_STRING EtpQueryDeviceProperty( + _In_ DEVINST DeviceHandle, + _In_ CONST DEVPROPKEY *DeviceProperty + ) +{ + CONFIGRET result; + PBYTE buffer; + ULONG bufferSize; + DEVPROPTYPE propertyType; + + bufferSize = 0x80; + buffer = PhAllocate(bufferSize); + propertyType = DEVPROP_TYPE_EMPTY; + + if ((result = CM_Get_DevNode_Property( + DeviceHandle, + DeviceProperty, + &propertyType, + buffer, + &bufferSize, + 0 + )) != CR_SUCCESS) + { + PhFree(buffer); + buffer = PhAllocate(bufferSize); + + result = CM_Get_DevNode_Property( + DeviceHandle, + DeviceProperty, + &propertyType, + buffer, + &bufferSize, + 0 + ); + } + + if (result != CR_SUCCESS) + { + PhFree(buffer); + return NULL; + } + + switch (propertyType) + { + case DEVPROP_TYPE_STRING: + { + PPH_STRING string; + + string = PhCreateStringEx((PWCHAR)buffer, bufferSize); + PhTrimToNullTerminatorString(string); + + PhFree(buffer); + return string; + } + break; + case DEVPROP_TYPE_FILETIME: + { + PPH_STRING string; + FILETIME newFileTime; + SYSTEMTIME systemTime; + + FileTimeToLocalFileTime((PFILETIME)buffer, &newFileTime); + FileTimeToSystemTime(&newFileTime, &systemTime); + + string = PhFormatDate(&systemTime, NULL); + + PhFree(buffer); + return string; + } + break; + case DEVPROP_TYPE_UINT32: + { + PPH_STRING string; + + string = PhFormatUInt64(*(PULONG)buffer, FALSE); + + PhFree(buffer); + return string; + } + break; + case DEVPROP_TYPE_UINT64: + { + PPH_STRING string; + + string = PhFormatUInt64(*(PULONG64)buffer, FALSE); + + PhFree(buffer); + return string; + } + break; + } + + return NULL; +} + +PPH_STRING EtpQueryDeviceRegistryProperty( + _In_ DEVINST DeviceHandle, + _In_ ULONG DeviceProperty + ) +{ + CONFIGRET result; + PPH_STRING string; + ULONG bufferSize; + DEVPROPTYPE devicePropertyType; + + bufferSize = 0x80; + string = PhCreateStringEx(NULL, bufferSize); + + if ((result = CM_Get_DevNode_Registry_Property( + DeviceHandle, + DeviceProperty, + &devicePropertyType, + (PBYTE)string->Buffer, + &bufferSize, + 0 + )) != CR_SUCCESS) + { + PhDereferenceObject(string); + string = PhCreateStringEx(NULL, bufferSize); + + result = CM_Get_DevNode_Registry_Property( + DeviceHandle, + DeviceProperty, + &devicePropertyType, + (PBYTE)string->Buffer, + &bufferSize, + 0 + ); + } + + if (result != CR_SUCCESS) + { + PhDereferenceObject(string); + return NULL; + } + + PhTrimToNullTerminatorString(string); + + return string; +} + +// Note: MSDN states this value must be created by video devices BUT Task Manager +// doesn't query this value and I currently don't know where it's getting the gpu memory information. +// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/registering-hardware-information +ULONG64 EtpQueryGpuInstalledMemory( + _In_ DEVINST DeviceHandle + ) +{ + ULONG64 installedMemory = ULLONG_MAX; + HKEY keyHandle; + + if (CM_Open_DevInst_Key( + DeviceHandle, + KEY_READ, + 0, + RegDisposition_OpenExisting, + &keyHandle, + CM_REGISTRY_SOFTWARE + ) == CR_SUCCESS) + { + installedMemory = PhQueryRegistryUlong64(keyHandle, L"HardwareInformation.qwMemorySize"); + + if (installedMemory == ULLONG_MAX) + installedMemory = PhQueryRegistryUlong(keyHandle, L"HardwareInformation.MemorySize"); + + if (installedMemory == ULONG_MAX) // HACK + installedMemory = ULLONG_MAX; + + NtClose(keyHandle); + } + + return installedMemory; +} + +BOOLEAN EtQueryDeviceProperties( + _In_ PWSTR DeviceInterface, + _Out_opt_ PPH_STRING *Description, + _Out_opt_ PPH_STRING *DriverDate, + _Out_opt_ PPH_STRING *DriverVersion, + _Out_opt_ PPH_STRING *LocationInfo, + _Out_opt_ ULONG64 *InstalledMemory + ) +{ + DEVPROPTYPE devicePropertyType; + DEVINST deviceInstanceHandle; + ULONG deviceInstanceIdLength = MAX_DEVICE_ID_LEN; + WCHAR deviceInstanceId[MAX_DEVICE_ID_LEN]; + + if (CM_Get_Device_Interface_Property( + DeviceInterface, + &DEVPKEY_Device_InstanceId, + &devicePropertyType, + (PBYTE)deviceInstanceId, + &deviceInstanceIdLength, + 0 + ) != CR_SUCCESS) + { + return FALSE; + } + + if (CM_Locate_DevNode( + &deviceInstanceHandle, + deviceInstanceId, + CM_LOCATE_DEVNODE_NORMAL + ) != CR_SUCCESS) + { + return FALSE; + } + + if (Description) + *Description = EtpQueryDeviceProperty(deviceInstanceHandle, &DEVPKEY_Device_DeviceDesc); + if (DriverDate) + *DriverDate = EtpQueryDeviceProperty(deviceInstanceHandle, &DEVPKEY_Device_DriverDate); + if (DriverVersion) + *DriverVersion = EtpQueryDeviceProperty(deviceInstanceHandle, &DEVPKEY_Device_DriverVersion); + if (LocationInfo) + *LocationInfo = EtpQueryDeviceProperty(deviceInstanceHandle, &DEVPKEY_Device_LocationInfo); + if (InstalledMemory) + *InstalledMemory = EtpQueryGpuInstalledMemory(deviceInstanceHandle); + // EtpQueryDeviceProperty(deviceInstanceHandle, &DEVPKEY_Device_Manufacturer); + + // Undocumented device properties (Win10 only) + //DEFINE_DEVPROPKEY(DEVPKEY_Gpu_Luid, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2); // DEVPROP_TYPE_UINT64 + //DEFINE_DEVPROPKEY(DEVPKEY_Gpu_PhysicalAdapterIndex, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 3); // DEVPROP_TYPE_UINT32 + + return TRUE; +} + +D3D_FEATURE_LEVEL EtQueryAdapterFeatureLevel( + _In_ LUID AdapterLuid + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + static PFN_D3D11_CREATE_DEVICE D3D11CreateDevice_I = NULL; + static HRESULT (WINAPI *CreateDXGIFactory1_I)(_In_ REFIID riid, _Out_ PVOID *ppFactory) = NULL; + D3D_FEATURE_LEVEL d3dFeatureLevelResult = 0; + IDXGIFactory1 *dxgiFactory; + IDXGIAdapter* dxgiAdapter; + + if (PhBeginInitOnce(&initOnce)) + { + LoadLibrary(L"dxgi.dll"); + LoadLibrary(L"d3d11.dll"); + CreateDXGIFactory1_I = PhGetModuleProcAddress(L"dxgi.dll", "CreateDXGIFactory1"); + D3D11CreateDevice_I = PhGetModuleProcAddress(L"d3d11.dll", "D3D11CreateDevice"); + + PhEndInitOnce(&initOnce); + } + + if (!CreateDXGIFactory1_I || !SUCCEEDED(CreateDXGIFactory1_I(&IID_IDXGIFactory1, &dxgiFactory))) + return 0; + + for (UINT i = 0; i < 25; i++) + { + DXGI_ADAPTER_DESC dxgiAdapterDescription; + + if (!SUCCEEDED(IDXGIFactory1_EnumAdapters(dxgiFactory, i, &dxgiAdapter))) + break; + + if (SUCCEEDED(IDXGIAdapter_GetDesc(dxgiAdapter, &dxgiAdapterDescription))) + { + if (RtlIsEqualLuid(&dxgiAdapterDescription.AdapterLuid, &AdapterLuid)) + { + D3D_FEATURE_LEVEL d3dFeatureLevel[] = + { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + D3D_FEATURE_LEVEL d3ddeviceFeatureLevel; + ID3D11Device* d3d11device; + + if (D3D11CreateDevice_I && SUCCEEDED(D3D11CreateDevice_I( + dxgiAdapter, + D3D_DRIVER_TYPE_UNKNOWN, + NULL, + 0, + d3dFeatureLevel, + RTL_NUMBER_OF(d3dFeatureLevel), + D3D11_SDK_VERSION, + &d3d11device, + &d3ddeviceFeatureLevel, + NULL + ))) + { + d3dFeatureLevelResult = d3ddeviceFeatureLevel; + ID3D11Device_Release(d3d11device); + IDXGIAdapter_Release(dxgiAdapter); + break; + } + } + } + + IDXGIAdapter_Release(dxgiAdapter); + } + + IDXGIFactory1_Release(dxgiFactory); + return d3dFeatureLevelResult; +} + +PETP_GPU_ADAPTER EtpAddDisplayAdapter( + _In_ PWSTR DeviceInterface, + _In_ D3DKMT_HANDLE AdapterHandle, + _In_ LUID AdapterLuid, + _In_ ULONG NumberOfSegments, + _In_ ULONG NumberOfNodes + ) +{ + PETP_GPU_ADAPTER adapter; + SIZE_T sizeNeeded; + + sizeNeeded = FIELD_OFFSET(ETP_GPU_ADAPTER, ApertureBitMapBuffer); + sizeNeeded += BYTES_NEEDED_FOR_BITS(NumberOfSegments); + + adapter = PhAllocateZero(sizeNeeded); + adapter->DeviceInterface = PhCreateString(DeviceInterface); + adapter->AdapterLuid = AdapterLuid; + adapter->NodeCount = NumberOfNodes; + adapter->SegmentCount = NumberOfSegments; + RtlInitializeBitMap(&adapter->ApertureBitMap, adapter->ApertureBitMapBuffer, NumberOfSegments); + + { + PPH_STRING description; + + if (EtQueryDeviceProperties(DeviceInterface, &description, NULL, NULL, NULL, NULL)) + { + adapter->Description = description; + } + } + + if (WindowsVersion >= WINDOWS_10_RS4) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3 + { + adapter->NodeNameList = PhCreateList(adapter->NodeCount); + + for (ULONG i = 0; i < adapter->NodeCount; i++) + { + D3DKMT_NODEMETADATA metaDataInfo; + + memset(&metaDataInfo, 0, sizeof(D3DKMT_NODEMETADATA)); + metaDataInfo.NodeOrdinal = i; + + if (NT_SUCCESS(EtQueryAdapterInformation( + AdapterHandle, + KMTQAITYPE_NODEMETADATA, + &metaDataInfo, + sizeof(D3DKMT_NODEMETADATA) + ))) + { + PhAddItemList(adapter->NodeNameList, EtpGetNodeEngineTypeString(metaDataInfo)); + } + else + { + PhAddItemList(adapter->NodeNameList, PhReferenceEmptyString()); + } + } + } + + PhAddItemList(EtpGpuAdapterList, adapter); + + return adapter; +} + +BOOLEAN EtpInitializeD3DStatistics( + VOID + ) +{ + PPH_LIST deviceAdapterList; + PWSTR deviceInterfaceList; + ULONG deviceInterfaceListLength = 0; + PWSTR deviceInterface; + D3DKMT_OPENADAPTERFROMDEVICENAME openAdapterFromDeviceName; + D3DKMT_QUERYSTATISTICS queryStatistics; + + if (CM_Get_Device_Interface_List_Size( + &deviceInterfaceListLength, + (PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL, + NULL, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT + ) != CR_SUCCESS) + { + return FALSE; + } + + deviceInterfaceList = PhAllocate(deviceInterfaceListLength * sizeof(WCHAR)); + memset(deviceInterfaceList, 0, deviceInterfaceListLength * sizeof(WCHAR)); + + if (CM_Get_Device_Interface_List( + (PGUID)&GUID_DISPLAY_DEVICE_ARRIVAL, + NULL, + deviceInterfaceList, + deviceInterfaceListLength, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT + ) != CR_SUCCESS) + { + PhFree(deviceInterfaceList); + return FALSE; + } + + deviceAdapterList = PhCreateList(10); + + for (deviceInterface = deviceInterfaceList; *deviceInterface; deviceInterface += PhCountStringZ(deviceInterface) + 1) + { + PhAddItemList(deviceAdapterList, deviceInterface); + } + + for (ULONG i = 0; i < deviceAdapterList->Count; i++) + { + memset(&openAdapterFromDeviceName, 0, sizeof(D3DKMT_OPENADAPTERFROMDEVICENAME)); + openAdapterFromDeviceName.DeviceName = deviceAdapterList->Items[i]; + + if (!NT_SUCCESS(D3DKMTOpenAdapterFromDeviceName(&openAdapterFromDeviceName))) + continue; + + if (WindowsVersion >= WINDOWS_10_RS4 && deviceAdapterList->Count > 1) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3 + { + if (EtpIsGpuSoftwareDevice(openAdapterFromDeviceName.AdapterHandle)) + { + EtCloseAdapterHandle(openAdapterFromDeviceName.AdapterHandle); + continue; + } + } + + if (WindowsVersion >= WINDOWS_10_RS4) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3 + { + D3DKMT_SEGMENTSIZEINFO segmentInfo; + + memset(&segmentInfo, 0, sizeof(D3DKMT_SEGMENTSIZEINFO)); + + if (NT_SUCCESS(EtQueryAdapterInformation( + openAdapterFromDeviceName.AdapterHandle, + KMTQAITYPE_GETSEGMENTSIZE, + &segmentInfo, + sizeof(D3DKMT_SEGMENTSIZEINFO) + ))) + { + EtGpuDedicatedLimit += segmentInfo.DedicatedVideoMemorySize; + EtGpuSharedLimit += segmentInfo.SharedSystemMemorySize; + } + } + + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER; + queryStatistics.AdapterLuid = openAdapterFromDeviceName.AdapterLuid; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + PETP_GPU_ADAPTER gpuAdapter; + + gpuAdapter = EtpAddDisplayAdapter( + openAdapterFromDeviceName.DeviceName, + openAdapterFromDeviceName.AdapterHandle, + openAdapterFromDeviceName.AdapterLuid, + queryStatistics.QueryResult.AdapterInformation.NbSegments, + queryStatistics.QueryResult.AdapterInformation.NodeCount + ); + + gpuAdapter->FirstNodeIndex = EtGpuNextNodeIndex; + EtGpuTotalNodeCount += gpuAdapter->NodeCount; + EtGpuTotalSegmentCount += gpuAdapter->SegmentCount; + EtGpuNextNodeIndex += gpuAdapter->NodeCount; + + for (ULONG ii = 0; ii < gpuAdapter->SegmentCount; ii++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.QuerySegment.SegmentId = ii; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + ULONG64 commitLimit; + ULONG aperture; + + if (WindowsVersion >= WINDOWS_8) + { + commitLimit = queryStatistics.QueryResult.SegmentInformation.CommitLimit; + aperture = queryStatistics.QueryResult.SegmentInformation.Aperture; + } + else + { + commitLimit = queryStatistics.QueryResult.SegmentInformationV1.CommitLimit; + aperture = queryStatistics.QueryResult.SegmentInformationV1.Aperture; + } + + if (WindowsVersion < WINDOWS_10_RS4) // Note: Changed to RS4 due to reports of BSODs on LTSB versions of RS3 + { + if (aperture) + EtGpuSharedLimit += commitLimit; + else + EtGpuDedicatedLimit += commitLimit; + } + + if (aperture) + RtlSetBits(&gpuAdapter->ApertureBitMap, ii, 1); + } + } + } + + EtCloseAdapterHandle(openAdapterFromDeviceName.AdapterHandle); + } + + PhDereferenceObject(deviceAdapterList); + PhFree(deviceInterfaceList); + + if (EtGpuTotalNodeCount == 0) + return FALSE; + + return TRUE; +} + +PETP_GPU_ADAPTER EtpAllocateGpuAdapter( + _In_ ULONG NumberOfSegments + ) +{ + PETP_GPU_ADAPTER adapter; + SIZE_T sizeNeeded; + + sizeNeeded = FIELD_OFFSET(ETP_GPU_ADAPTER, ApertureBitMapBuffer); + sizeNeeded += BYTES_NEEDED_FOR_BITS(NumberOfSegments); + + adapter = PhAllocate(sizeNeeded); + memset(adapter, 0, sizeNeeded); + + return adapter; +} + +VOID EtpUpdateProcessSegmentInformation( + _In_ PET_PROCESS_BLOCK Block + ) +{ + PETP_GPU_ADAPTER gpuAdapter; + D3DKMT_QUERYSTATISTICS queryStatistics; + ULONG64 dedicatedUsage; + ULONG64 sharedUsage; + ULONG64 commitUsage; + ULONG segmentCount; + + if (!Block->ProcessItem->QueryHandle) + return; + + dedicatedUsage = 0; + sharedUsage = 0; + commitUsage = 0; + segmentCount = 0; + + for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + for (ULONG j = 0; j < gpuAdapter->SegmentCount; j++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.ProcessHandle = Block->ProcessItem->QueryHandle; + queryStatistics.QueryProcessSegment.SegmentId = j; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + ULONG64 bytesCommitted; + + if (WindowsVersion >= WINDOWS_8) + bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; + else + bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; + + if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) + sharedUsage += bytesCommitted; + else + dedicatedUsage += bytesCommitted; + } + + segmentCount++; + } + } + + for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.ProcessHandle = Block->ProcessItem->QueryHandle; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + commitUsage += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesAllocated; + } + } + + Block->GpuDedicatedUsage = dedicatedUsage; + Block->GpuSharedUsage = sharedUsage; + Block->GpuCommitUsage = commitUsage; + Block->GpuSegmentCount = segmentCount; +} + +VOID EtpUpdateSystemSegmentInformation( + VOID + ) +{ + PETP_GPU_ADAPTER gpuAdapter; + D3DKMT_QUERYSTATISTICS queryStatistics; + ULONG64 dedicatedUsage; + ULONG64 sharedUsage; + + dedicatedUsage = 0; + sharedUsage = 0; + + for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + for (ULONG j = 0; j < gpuAdapter->SegmentCount; j++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.QuerySegment.SegmentId = j; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + ULONG64 bytesCommitted; + + if (WindowsVersion >= WINDOWS_8) + bytesCommitted = queryStatistics.QueryResult.SegmentInformation.BytesResident; + else + bytesCommitted = queryStatistics.QueryResult.SegmentInformationV1.BytesResident; + + if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) + sharedUsage += bytesCommitted; + else + dedicatedUsage += bytesCommitted; + } + } + } + + EtGpuDedicatedUsage = dedicatedUsage; + EtGpuSharedUsage = sharedUsage; +} + +VOID EtpUpdateProcessNodeInformation( + _In_ PET_PROCESS_BLOCK Block + ) +{ + PETP_GPU_ADAPTER gpuAdapter; + D3DKMT_QUERYSTATISTICS queryStatistics; + ULONG64 totalRunningTime; + ULONG64 totalContextSwitches; + ULONG totalNodes; + + if (!Block->ProcessItem->QueryHandle) + return; + + totalRunningTime = 0; + totalContextSwitches = 0; + totalNodes = 0; + + for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + for (ULONG j = 0; j < gpuAdapter->NodeCount; j++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_NODE; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.ProcessHandle = Block->ProcessItem->QueryHandle; + queryStatistics.QueryProcessNode.NodeId = j; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + //ULONG64 runningTime; + //runningTime = queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; + //PhUpdateDelta(&Block->GpuTotalRunningTimeDelta[j], runningTime); + + totalRunningTime += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; + totalContextSwitches += queryStatistics.QueryResult.ProcessNodeInformation.ContextSwitch; + } + + totalNodes++; + } + } + + PhUpdateDelta(&Block->GpuRunningTimeDelta, totalRunningTime); + Block->GpuContextSwitches = totalContextSwitches; + Block->GpuNodeCount = totalNodes; +} + +VOID EtpUpdateSystemNodeInformation( + VOID + ) +{ + PETP_GPU_ADAPTER gpuAdapter; + D3DKMT_QUERYSTATISTICS queryStatistics; + LARGE_INTEGER performanceCounter; + + for (ULONG i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + for (ULONG j = 0; j < gpuAdapter->NodeCount; j++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_NODE; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.QueryNode.NodeId = j; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + ULONG64 runningTime; + //ULONG64 systemRunningTime; + + runningTime = queryStatistics.QueryResult.NodeInformation.GlobalInformation.RunningTime.QuadPart; + //systemRunningTime = queryStatistics.QueryResult.NodeInformation.SystemInformation.RunningTime.QuadPart; + + PhUpdateDelta(&EtGpuNodesTotalRunningTimeDelta[gpuAdapter->FirstNodeIndex + j], runningTime); + } + } + } + + NtQueryPerformanceCounter(&performanceCounter, &EtClockTotalRunningTimeFrequency); + PhUpdateDelta(&EtClockTotalRunningTimeDelta, performanceCounter.QuadPart); +} + +VOID NTAPI EtGpuProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + static ULONG runCount = 0; // MUST keep in sync with runCount in process provider + DOUBLE elapsedTime; // total GPU node elapsed time in micro-seconds + FLOAT tempGpuUsage; + ULONG i; + PLIST_ENTRY listEntry; + FLOAT maxNodeValue = 0; + PET_PROCESS_BLOCK maxNodeBlock = NULL; + + // Update global statistics. + EtpUpdateSystemSegmentInformation(); + EtpUpdateSystemNodeInformation(); + + // Update global gpu usage. + tempGpuUsage = 0; + elapsedTime = (DOUBLE)(EtClockTotalRunningTimeDelta.Delta * 10000000ULL / EtClockTotalRunningTimeFrequency.QuadPart); + + if (elapsedTime != 0) + { + for (i = 0; i < EtGpuTotalNodeCount; i++) + { + FLOAT usage = (FLOAT)(EtGpuNodesTotalRunningTimeDelta[i].Delta / elapsedTime); + + if (usage > 1) + usage = 1; + + if (usage > tempGpuUsage) + tempGpuUsage = usage; + } + } + + EtGpuNodeUsage = tempGpuUsage; + + // Update per-process statistics. + // Note: no lock is needed because we only ever modify the list on this same thread. + + listEntry = EtProcessBlockListHead.Flink; + + while (listEntry != &EtProcessBlockListHead) + { + PET_PROCESS_BLOCK block; + + block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); + + EtpUpdateProcessSegmentInformation(block); + EtpUpdateProcessNodeInformation(block); + + if (elapsedTime != 0) + { + block->GpuNodeUsage = (FLOAT)(block->GpuRunningTimeDelta.Delta / elapsedTime); + + // HACK + if (block->GpuNodeUsage > EtGpuNodeUsage) + block->GpuNodeUsage = EtGpuNodeUsage; + + //for (i = 0; i < EtGpuTotalNodeCount; i++) + //{ + // FLOAT usage = (FLOAT)(block->GpuTotalRunningTimeDelta[i].Delta / elapsedTime); + // + // if (usage > block->GpuNodeUsage) + // { + // block->GpuNodeUsage = usage; + // } + //} + + if (block->GpuNodeUsage > 1) + block->GpuNodeUsage = 1; + } + + if (maxNodeValue < block->GpuNodeUsage) + { + maxNodeValue = block->GpuNodeUsage; + maxNodeBlock = block; + } + + listEntry = listEntry->Flink; + } + + // Update history buffers. + + if (runCount != 0) + { + PhAddItemCircularBuffer_FLOAT(&EtGpuNodeHistory, EtGpuNodeUsage); + PhAddItemCircularBuffer_ULONG64(&EtGpuDedicatedHistory, EtGpuDedicatedUsage); + PhAddItemCircularBuffer_ULONG64(&EtGpuSharedHistory, EtGpuSharedUsage); + + if (elapsedTime != 0) + { + for (i = 0; i < EtGpuTotalNodeCount; i++) + { + FLOAT usage; + + usage = (FLOAT)(EtGpuNodesTotalRunningTimeDelta[i].Delta / elapsedTime); + + if (usage > 1) + usage = 1; + + PhAddItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], usage); + } + } + else + { + for (i = 0; i < EtGpuTotalNodeCount; i++) + PhAddItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], 0); + } + + if (maxNodeBlock) + { + PhAddItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, HandleToUlong(maxNodeBlock->ProcessItem->ProcessId)); + PhAddItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, maxNodeBlock->GpuNodeUsage); + PhReferenceProcessRecordForStatistics(maxNodeBlock->ProcessItem->Record); + } + else + { + PhAddItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, 0); + PhAddItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, 0); + } + } + + runCount++; +} + +ULONG EtGetGpuAdapterCount( + VOID + ) +{ + return EtpGpuAdapterList->Count; +} + +ULONG EtGetGpuAdapterIndexFromNodeIndex( + _In_ ULONG NodeIndex + ) +{ + ULONG i; + PETP_GPU_ADAPTER gpuAdapter; + + for (i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + if (NodeIndex >= gpuAdapter->FirstNodeIndex && NodeIndex < gpuAdapter->FirstNodeIndex + gpuAdapter->NodeCount) + return i; + } + + return ULONG_MAX; +} + +PPH_STRING EtGetGpuAdapterNodeDescription( + _In_ ULONG Index, + _In_ ULONG NodeIndex + ) +{ + PETP_GPU_ADAPTER gpuAdapter; + + if (Index >= EtpGpuAdapterList->Count) + return NULL; + + gpuAdapter = EtpGpuAdapterList->Items[Index]; + + if (!gpuAdapter->NodeNameList) + return NULL; + + return gpuAdapter->NodeNameList->Items[NodeIndex - gpuAdapter->FirstNodeIndex]; +} + +PPH_STRING EtGetGpuAdapterDescription( + _In_ ULONG Index + ) +{ + PPH_STRING description; + + if (Index >= EtpGpuAdapterList->Count) + return NULL; + + description = ((PETP_GPU_ADAPTER)EtpGpuAdapterList->Items[Index])->Description; + + if (description) + { + PhReferenceObject(description); + return description; + } + else + { + return NULL; + } +} + +VOID EtQueryProcessGpuStatistics( + _In_ HANDLE ProcessHandle, + _Out_ PET_PROCESS_GPU_STATISTICS Statistics + ) +{ + ULONG i; + ULONG j; + PETP_GPU_ADAPTER gpuAdapter; + D3DKMT_QUERYSTATISTICS queryStatistics; + + memset(Statistics, 0, sizeof(ET_PROCESS_GPU_STATISTICS)); + + for (i = 0; i < EtpGpuAdapterList->Count; i++) + { + gpuAdapter = EtpGpuAdapterList->Items[i]; + + Statistics->SegmentCount += gpuAdapter->SegmentCount; + Statistics->NodeCount += gpuAdapter->NodeCount; + + for (j = 0; j < gpuAdapter->SegmentCount; j++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.ProcessHandle = ProcessHandle; + queryStatistics.QueryProcessSegment.SegmentId = j; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + ULONG64 bytesCommitted; + + if (WindowsVersion >= WINDOWS_8) + bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; + else + bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInformation.BytesCommitted; + + if (RtlCheckBit(&gpuAdapter->ApertureBitMap, j)) + Statistics->SharedCommitted += bytesCommitted; + else + Statistics->DedicatedCommitted += bytesCommitted; + } + } + + for (j = 0; j < gpuAdapter->NodeCount; j++) + { + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_NODE; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.ProcessHandle = ProcessHandle; + queryStatistics.QueryProcessNode.NodeId = j; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + Statistics->RunningTime += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime.QuadPart; + Statistics->ContextSwitches += queryStatistics.QueryResult.ProcessNodeInformation.ContextSwitch; + } + } + + memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS)); + queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS; + queryStatistics.AdapterLuid = gpuAdapter->AdapterLuid; + queryStatistics.ProcessHandle = ProcessHandle; + + if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) + { + Statistics->BytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesAllocated; + Statistics->BytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesReserved; + Statistics->WriteCombinedBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.WriteCombinedBytesAllocated; + Statistics->WriteCombinedBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.WriteCombinedBytesReserved; + Statistics->CachedBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.CachedBytesAllocated; + Statistics->CachedBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.CachedBytesReserved; + Statistics->SectionBytesAllocated += queryStatistics.QueryResult.ProcessInformation.SystemMemory.SectionBytesAllocated; + Statistics->SectionBytesReserved += queryStatistics.QueryResult.ProcessInformation.SystemMemory.SectionBytesReserved; + } + } +} diff --git a/plugins/ExtendedTools/gpumon.h b/plugins/ExtendedTools/gpumon.h index 3643a0bf7a62..11fd82ec2f01 100644 --- a/plugins/ExtendedTools/gpumon.h +++ b/plugins/ExtendedTools/gpumon.h @@ -1,43 +1,49 @@ -#ifndef GPUMON_H -#define GPUMON_H - -// Macros - -#define BYTES_NEEDED_FOR_BITS(Bits) ((((Bits) + sizeof(ULONG) * 8 - 1) / 8) & ~(SIZE_T)(sizeof(ULONG) - 1)) // divide round up - -// Structures - -typedef struct _ETP_GPU_ADAPTER -{ - LUID AdapterLuid; - PPH_STRING Description; - ULONG SegmentCount; - ULONG NodeCount; - ULONG FirstNodeIndex; - - BOOLEAN HasActivity; - - RTL_BITMAP ApertureBitMap; - ULONG ApertureBitMapBuffer[1]; -} ETP_GPU_ADAPTER, *PETP_GPU_ADAPTER; - -// Functions - -BOOLEAN EtpInitializeD3DStatistics( - VOID - ); - -PETP_GPU_ADAPTER EtpAllocateGpuAdapter( - _In_ ULONG NumberOfSegments - ); - -PPH_STRING EtpQueryDeviceDescription( - _In_ PWSTR DeviceInterface - ); - -VOID NTAPI EtGpuProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -#endif +#ifndef GPUMON_H +#define GPUMON_H + +// Macros + +#define BYTES_NEEDED_FOR_BITS(Bits) ((((Bits) + sizeof(ULONG) * 8 - 1) / 8) & ~(SIZE_T)(sizeof(ULONG) - 1)) // divide round up + +// Structures + +typedef struct _ETP_GPU_ADAPTER +{ + LUID AdapterLuid; + ULONG SegmentCount; + ULONG NodeCount; + ULONG FirstNodeIndex; + + PPH_STRING DeviceInterface; + PPH_STRING Description; + PPH_LIST NodeNameList; + + RTL_BITMAP ApertureBitMap; + ULONG ApertureBitMapBuffer[1]; +} ETP_GPU_ADAPTER, *PETP_GPU_ADAPTER; + +// Functions + +BOOLEAN EtpInitializeD3DStatistics( + VOID + ); + +PETP_GPU_ADAPTER EtpAllocateGpuAdapter( + _In_ ULONG NumberOfSegments + ); + +BOOLEAN EtQueryDeviceProperties( + _In_ PWSTR DeviceInterface, + _Out_opt_ PPH_STRING *Description, + _Out_opt_ PPH_STRING *DriverDate, + _Out_opt_ PPH_STRING *DriverVersion, + _Out_opt_ PPH_STRING *LocationInfo, + _Out_opt_ ULONG64 *InstalledMemory + ); + +VOID NTAPI EtGpuProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +#endif diff --git a/plugins/ExtendedTools/gpunodes.c b/plugins/ExtendedTools/gpunodes.c index 65fb6e141816..5b4549ffcf7a 100644 --- a/plugins/ExtendedTools/gpunodes.c +++ b/plugins/ExtendedTools/gpunodes.c @@ -1,430 +1,465 @@ -/* - * Process Hacker Extended Tools - - * GPU nodes window - * - * Copyright (C) 2011-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -#define GRAPH_PADDING 5 -#define CHECKBOX_PADDING 3 - -INT_PTR CALLBACK EtpGpuNodesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -static HWND WindowHandle; -static RECT MinimumSize; -static PH_LAYOUT_MANAGER LayoutManager; -static RECT LayoutMargin; -static HWND *GraphHandle; -static HWND *CheckBoxHandle; -static PPH_GRAPH_STATE GraphState; -static PPH_SYSINFO_PARAMETERS SysInfoParameters; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; - -VOID EtShowGpuNodesDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_SYSINFO_PARAMETERS Parameters - ) -{ - SysInfoParameters = Parameters; - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_GPUNODES), - ParentWindowHandle, - EtpGpuNodesDlgProc - ); -} - -static VOID ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PostMessage(WindowHandle, UPDATE_MSG, 0, 0); -} - -VOID EtpLoadNodeBitMap( - VOID - ) -{ - ULONG i; - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - Button_SetCheck( - CheckBoxHandle[i], - RtlCheckBit(&EtGpuNodeBitMap, i) ? BST_CHECKED : BST_UNCHECKED - ); - } -} - -VOID EtpSaveNodeBitMap( - VOID - ) -{ - RTL_BITMAP newBitMap; - ULONG i; - - EtAllocateGpuNodeBitMap(&newBitMap); - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - if (Button_GetCheck(CheckBoxHandle[i]) == BST_CHECKED) - RtlSetBits(&newBitMap, i, 1); - } - - if (RtlNumberOfSetBits(&newBitMap) == 0) - RtlSetBits(&newBitMap, 0, 1); - - EtUpdateGpuNodeBitMap(&newBitMap); -} - -INT_PTR CALLBACK EtpGpuNodesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG i; - HFONT font; - PPH_STRING nodeString; - RECT labelRect; - RECT tempRect; - ULONG numberOfRows; - ULONG numberOfColumns; - - WindowHandle = hwndDlg; - - PhInitializeLayoutManager(&LayoutManager, hwndDlg); - PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - LayoutMargin = PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_ALL)->Margin; - - PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration); - - GraphHandle = PhAllocate(sizeof(HWND) * EtGpuTotalNodeCount); - CheckBoxHandle = PhAllocate(sizeof(HWND) * EtGpuTotalNodeCount); - GraphState = PhAllocate(sizeof(PH_GRAPH_STATE) * EtGpuTotalNodeCount); - - font = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0); - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - nodeString = PhFormatString(L"Node %lu", i); - - GraphHandle[i] = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - hwndDlg, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(GraphHandle[i], TRUE); - CheckBoxHandle[i] = CreateWindow( - WC_BUTTON, - nodeString->Buffer, - WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX, - 0, - 0, - 3, - 3, - hwndDlg, - NULL, - NULL, - NULL - ); - SendMessage(CheckBoxHandle[i], WM_SETFONT, (WPARAM)font, FALSE); - PhInitializeGraphState(&GraphState[i]); - - PhDereferenceObject(nodeString); - } - - // Calculate the minimum size. - - numberOfRows = (ULONG)sqrt(EtGpuTotalNodeCount); - numberOfColumns = (EtGpuTotalNodeCount + numberOfRows - 1) / numberOfRows; - - MinimumSize.left = 0; - MinimumSize.top = 0; - MinimumSize.right = 55; - MinimumSize.bottom = 60; - MapDialogRect(hwndDlg, &MinimumSize); - MinimumSize.right += (MinimumSize.right + GRAPH_PADDING) * numberOfColumns; - MinimumSize.bottom += (MinimumSize.bottom + GRAPH_PADDING) * numberOfRows; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_INSTRUCTION), &labelRect); - MapWindowPoints(NULL, hwndDlg, (POINT *)&labelRect, 2); - labelRect.right += GetSystemMetrics(SM_CXFRAME) * 2; - - tempRect.left = 0; - tempRect.top = 0; - tempRect.right = 7; - tempRect.bottom = 0; - MapDialogRect(hwndDlg, &tempRect); - labelRect.right += tempRect.right; - - if (MinimumSize.right < labelRect.right) - MinimumSize.right = labelRect.right; - - SetWindowPos(hwndDlg, NULL, 0, 0, MinimumSize.right, MinimumSize.bottom, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); - - // Note: This dialog must be centered after all other graphs and controls have been added. - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - EtpLoadNodeBitMap(); - } - break; - case WM_DESTROY: - { - ULONG i; - - EtpSaveNodeBitMap(); - - PhUnregisterCallback(&PhProcessesUpdatedEvent, &ProcessesUpdatedCallbackRegistration); - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - PhDeleteGraphState(&GraphState[i]); - } - - PhFree(GraphHandle); - PhFree(CheckBoxHandle); - PhFree(GraphState); - - PhDeleteLayoutManager(&LayoutManager); - } - break; - case WM_SIZE: - { - HDWP deferHandle; - RECT clientRect; - RECT checkBoxRect; - ULONG numberOfRows = (ULONG)sqrt(EtGpuTotalNodeCount); - ULONG numberOfColumns = (EtGpuTotalNodeCount + numberOfRows - 1) / numberOfRows; - ULONG numberOfYPaddings = numberOfRows - 1; - ULONG numberOfXPaddings = numberOfColumns - 1; - ULONG cellHeight; - ULONG y; - ULONG cellWidth; - ULONG x; - ULONG i; - - PhLayoutManagerLayout(&LayoutManager); - - deferHandle = BeginDeferWindowPos(EtGpuTotalNodeCount * 2); - - GetClientRect(hwndDlg, &clientRect); - GetClientRect(GetDlgItem(hwndDlg, IDC_EXAMPLE), &checkBoxRect); - cellHeight = (clientRect.bottom - LayoutMargin.top - LayoutMargin.bottom - GRAPH_PADDING * numberOfYPaddings) / numberOfRows; - y = LayoutMargin.top; - i = 0; - - for (ULONG row = 0; row < numberOfRows; ++row) - { - // Give the last row the remaining space; the height we calculated might be off by a few - // pixels due to integer division. - if (row == numberOfRows - 1) - cellHeight = clientRect.bottom - LayoutMargin.bottom - y; - - cellWidth = (clientRect.right - LayoutMargin.left - LayoutMargin.right - GRAPH_PADDING * numberOfXPaddings) / numberOfColumns; - x = LayoutMargin.left; - - for (ULONG column = 0; column < numberOfColumns; column++) - { - // Give the last cell the remaining space; the width we calculated might be off by a few - // pixels due to integer division. - if (column == numberOfColumns - 1) - cellWidth = clientRect.right - LayoutMargin.right - x; - - if (i < EtGpuTotalNodeCount) - { - deferHandle = DeferWindowPos( - deferHandle, - GraphHandle[i], - NULL, - x, - y, - cellWidth, - cellHeight - checkBoxRect.bottom - CHECKBOX_PADDING, - SWP_NOACTIVATE | SWP_NOZORDER - ); - deferHandle = DeferWindowPos( - deferHandle, - CheckBoxHandle[i], - NULL, - x, - y + cellHeight - checkBoxRect.bottom, - cellWidth, - checkBoxRect.bottom, - SWP_NOACTIVATE | SWP_NOZORDER - ); - i++; - } - x += cellWidth + GRAPH_PADDING; - } - - y += cellHeight + GRAPH_PADDING; - } - - EndDeferWindowPos(deferHandle); - } - break; - case WM_SIZING: - { - PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - { - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - case WM_NOTIFY: - { - NMHDR *header = (NMHDR *)lParam; - ULONG i; - - switch (header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - SysInfoParameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - if (header->hwndFrom == GraphHandle[i]) - { - PhGraphStateGetDrawInfo( - &GraphState[i], - getDrawInfo, - EtGpuNodesHistory[i].Count - ); - - if (!GraphState[i].Valid) - { - PhCopyCircularBuffer_FLOAT(&EtGpuNodesHistory[i], GraphState[i].Data1, drawInfo->LineDataCount); - GraphState[i].Valid = TRUE; - } - - break; - } - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - if (header->hwndFrom == GraphHandle[i]) - { - if (GraphState[i].TooltipIndex != getTooltipText->Index) - { - FLOAT gpu; - ULONG adapterIndex; - PPH_STRING adapterDescription; - - gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], getTooltipText->Index); - adapterIndex = EtGetGpuAdapterIndexFromNodeIndex(i); - - if (adapterIndex != -1) - { - adapterDescription = EtGetGpuAdapterDescription(adapterIndex); - - if (adapterDescription && adapterDescription->Length == 0) - PhClearReference(&adapterDescription); - - if (!adapterDescription) - adapterDescription = PhFormatString(L"Adapter %lu", adapterIndex); - } - else - { - adapterDescription = PhCreateString(L"Unknown Adapter"); - } - - PhMoveReference(&GraphState[i].TooltipText, PhFormatString( - L"Node %lu on %s\n%.2f%%\n%s", - i, - adapterDescription->Buffer, - gpu * 100, - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - PhDereferenceObject(adapterDescription); - } - - getTooltipText->Text = GraphState[i].TooltipText->sr; - - break; - } - } - } - } - break; - } - } - break; - case UPDATE_MSG: - { - ULONG i; - - for (i = 0; i < EtGpuTotalNodeCount; i++) - { - GraphState[i].Valid = FALSE; - GraphState[i].TooltipIndex = -1; - Graph_MoveGrid(GraphHandle[i], 1); - Graph_Draw(GraphHandle[i]); - Graph_UpdateTooltip(GraphHandle[i]); - InvalidateRect(GraphHandle[i], NULL, FALSE); - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * GPU nodes window + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +#define GRAPH_PADDING 5 +static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; +static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; + +INT_PTR CALLBACK EtpGpuNodesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +static RECT MinimumSize; +static PH_LAYOUT_MANAGER LayoutManager; +static RECT LayoutMargin; +static HWND *GraphHandle; +static PPH_GRAPH_STATE GraphState; +static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; + +static HANDLE EtGpuNodesThreadHandle = NULL; +static HWND EtGpuNodesWindowHandle = NULL; +static PH_EVENT EtGpuNodesInitializedEvent = PH_EVENT_INIT; + +NTSTATUS EtpGpuNodesDialogThreadStart( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + EtGpuNodesWindowHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_GPUNODES), + NULL, + EtpGpuNodesDlgProc, + (LPARAM)Parameter + ); + + PhSetEvent(&EtGpuNodesInitializedEvent); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(EtGpuNodesWindowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + PhResetEvent(&EtGpuNodesInitializedEvent); + + if (EtGpuNodesThreadHandle) + NtClose(EtGpuNodesThreadHandle); + + EtGpuNodesThreadHandle = NULL; + EtGpuNodesWindowHandle = NULL; + + return STATUS_SUCCESS; +} + +VOID EtShowGpuNodesDialog( + _In_ HWND ParentWindowHandle + ) +{ + if (!EtGpuNodesThreadHandle) + { + if (!NT_SUCCESS(PhCreateThreadEx(&EtGpuNodesThreadHandle, EtpGpuNodesDialogThreadStart, ParentWindowHandle))) + { + PhShowError(PhMainWndHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&EtGpuNodesInitializedEvent, NULL); + } + + PostMessage(EtGpuNodesWindowHandle, ET_WM_SHOWDIALOG, 0, 0); +} + +static VOID ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PostMessage((HWND)Context, ET_WM_UPDATE, 0, 0); +} + +INT_PTR CALLBACK EtpGpuNodesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG i; + ULONG numberOfRows; + ULONG numberOfColumns; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + LayoutMargin = PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_ALL)->Margin; + + GraphHandle = PhAllocate(sizeof(HWND) * EtGpuTotalNodeCount); + GraphState = PhAllocate(sizeof(PH_GRAPH_STATE) * EtGpuTotalNodeCount); + + for (i = 0; i < EtGpuTotalNodeCount; i++) + { + GraphHandle[i] = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + hwndDlg, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(GraphHandle[i], TRUE); + PhInitializeGraphState(&GraphState[i]); + } + + // Calculate the minimum size. + + numberOfRows = (ULONG)sqrt(EtGpuTotalNodeCount); + numberOfColumns = (EtGpuTotalNodeCount + numberOfRows - 1) / numberOfRows; + + MinimumSize.left = 0; + MinimumSize.top = 0; + MinimumSize.right = 55; + MinimumSize.bottom = 60; + MapDialogRect(hwndDlg, &MinimumSize); + MinimumSize.right += (MinimumSize.right + GRAPH_PADDING) * numberOfColumns; + MinimumSize.bottom += (MinimumSize.bottom + GRAPH_PADDING) * numberOfRows; + + SetWindowPos(hwndDlg, NULL, 0, 0, MinimumSize.right, MinimumSize.bottom, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); + + // Note: This dialog must be centered after all other graphs and controls have been added. + if (PhGetIntegerPairSetting(SETTING_NAME_GPU_NODES_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_GPU_NODES_WINDOW_POSITION, SETTING_NAME_GPU_NODES_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, (HWND)lParam); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + ProcessesUpdatedCallback, + hwndDlg, + &ProcessesUpdatedCallbackRegistration + ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(SETTING_NAME_GPU_NODES_WINDOW_POSITION, SETTING_NAME_GPU_NODES_WINDOW_SIZE, hwndDlg); + + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &ProcessesUpdatedCallbackRegistration); + + for (ULONG i = 0; i < EtGpuTotalNodeCount; i++) + { + PhDeleteGraphState(&GraphState[i]); + } + + PhFree(GraphHandle); + PhFree(GraphState); + + PhDeleteLayoutManager(&LayoutManager); + + PostQuitMessage(0); + } + break; + case WM_SIZE: + { + HDWP deferHandle; + RECT clientRect; + ULONG numberOfRows = (ULONG)sqrt(EtGpuTotalNodeCount); + ULONG numberOfColumns = (EtGpuTotalNodeCount + numberOfRows - 1) / numberOfRows; + ULONG numberOfYPaddings = numberOfRows - 1; + ULONG numberOfXPaddings = numberOfColumns - 1; + ULONG cellHeight; + ULONG y; + ULONG cellWidth; + ULONG x; + ULONG i; + + PhLayoutManagerLayout(&LayoutManager); + + deferHandle = BeginDeferWindowPos(EtGpuTotalNodeCount); + + GetClientRect(hwndDlg, &clientRect); + cellHeight = (clientRect.bottom - LayoutMargin.top - LayoutMargin.bottom - GRAPH_PADDING * numberOfYPaddings) / numberOfRows; + y = LayoutMargin.top; + i = 0; + + for (ULONG row = 0; row < numberOfRows; ++row) + { + // Give the last row the remaining space; the height we calculated might be off by a few + // pixels due to integer division. + if (row == numberOfRows - 1) + cellHeight = clientRect.bottom - LayoutMargin.bottom - y; + + cellWidth = (clientRect.right - LayoutMargin.left - LayoutMargin.right - GRAPH_PADDING * numberOfXPaddings) / numberOfColumns; + x = LayoutMargin.left; + + for (ULONG column = 0; column < numberOfColumns; column++) + { + // Give the last cell the remaining space; the width we calculated might be off by a few + // pixels due to integer division. + if (column == numberOfColumns - 1) + cellWidth = clientRect.right - LayoutMargin.right - x; + + if (i < EtGpuTotalNodeCount) + { + deferHandle = DeferWindowPos( + deferHandle, + GraphHandle[i], + NULL, + x, + y, + cellWidth, + cellHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + i++; + } + x += cellWidth + GRAPH_PADDING; + } + + y += cellHeight + GRAPH_PADDING; + } + + EndDeferWindowPos(deferHandle); + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + } + } + break; + case WM_NOTIFY: + { + NMHDR *header = (NMHDR *)lParam; + ULONG i; + + switch (header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); + + for (i = 0; i < EtGpuTotalNodeCount; i++) + { + if (header->hwndFrom == GraphHandle[i]) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + FLOAT gpu; + ULONG adapterIndex; + PPH_STRING engineName = NULL; + + gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], 0); + + if ((adapterIndex = EtGetGpuAdapterIndexFromNodeIndex(i)) != ULONG_MAX) + engineName = EtGetGpuAdapterNodeDescription(adapterIndex, i); + + if (!PhIsNullOrEmptyString(engineName)) + { + PhMoveReference(&GraphState[i].Text, PhFormatString( + L"%.2f%% (%s)", + gpu * 100, + engineName->Buffer + )); + } + else + { + PhMoveReference(&GraphState[i].Text, PhFormatString( + L"%.2f%% (Node %lu)", + gpu * 100, + i + )); + } + + hdc = Graph_GetBufferedContext(GraphHandle[i]); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &GraphState[i].Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + PhGraphStateGetDrawInfo( + &GraphState[i], + getDrawInfo, + EtGpuNodesHistory[i].Count + ); + + if (!GraphState[i].Valid) + { + PhCopyCircularBuffer_FLOAT(&EtGpuNodesHistory[i], GraphState[i].Data1, drawInfo->LineDataCount); + GraphState[i].Valid = TRUE; + } + + break; + } + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + for (i = 0; i < EtGpuTotalNodeCount; i++) + { + if (header->hwndFrom == GraphHandle[i]) + { + if (GraphState[i].TooltipIndex != getTooltipText->Index) + { + FLOAT gpu; + ULONG adapterIndex; + PPH_STRING adapterEngineName = NULL; + PPH_STRING adapterDescription; + + gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodesHistory[i], getTooltipText->Index); + adapterIndex = EtGetGpuAdapterIndexFromNodeIndex(i); + + if (adapterIndex != ULONG_MAX) + { + adapterEngineName = EtGetGpuAdapterNodeDescription(adapterIndex, i); + adapterDescription = EtGetGpuAdapterDescription(adapterIndex); + + if (adapterDescription && adapterDescription->Length == 0) + PhClearReference(&adapterDescription); + + if (!adapterDescription) + adapterDescription = PhFormatString(L"Adapter %lu", adapterIndex); + } + else + { + adapterDescription = PhCreateString(L"Unknown Adapter"); + } + + if (!PhIsNullOrEmptyString(adapterEngineName)) + { + PhMoveReference(&GraphState[i].TooltipText, PhFormatString( + L"Node %lu (%s) on %s\n%.2f%%\n%s", + i, + adapterEngineName->Buffer, + adapterDescription->Buffer, + gpu * 100, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + else + { + PhMoveReference(&GraphState[i].TooltipText, PhFormatString( + L"Node %lu on %s\n%.2f%%\n%s", + i, + adapterDescription->Buffer, + gpu * 100, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + + PhDereferenceObject(adapterDescription); + } + + getTooltipText->Text = GraphState[i].TooltipText->sr; + + break; + } + } + } + } + break; + } + } + break; + case ET_WM_SHOWDIALOG: + { + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); + + SetForegroundWindow(hwndDlg); + } + break; + case ET_WM_UPDATE: + { + for (ULONG i = 0; i < EtGpuTotalNodeCount; i++) + { + GraphState[i].Valid = FALSE; + GraphState[i].TooltipIndex = ULONG_MAX; + Graph_MoveGrid(GraphHandle[i], 1); + Graph_Draw(GraphHandle[i]); + Graph_UpdateTooltip(GraphHandle[i]); + InvalidateRect(GraphHandle[i], NULL, FALSE); + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/gpuprprp.c b/plugins/ExtendedTools/gpuprprp.c index 0cc87551e9de..d617bb7e6aa3 100644 --- a/plugins/ExtendedTools/gpuprprp.c +++ b/plugins/ExtendedTools/gpuprprp.c @@ -1,767 +1,896 @@ -/* - * Process Hacker Extended Tools - - * GPU process properties page - * - * Copyright (C) 2011 wj32 - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -typedef struct _ET_GPU_CONTEXT -{ - HWND WindowHandle; - HWND PanelHandle; - HWND DetailsHandle; - PET_PROCESS_BLOCK Block; - PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - BOOLEAN Enabled; - PH_LAYOUT_MANAGER LayoutManager; - - HWND GpuGroupBox; - HWND MemGroupBox; - HWND SharedGroupBox; - - HWND GpuGraphHandle; - HWND MemGraphHandle; - HWND SharedGraphHandle; - - FLOAT CurrentGpuUsage; - ULONG CurrentMemUsage; - ULONG CurrentMemSharedUsage; - - PH_GRAPH_STATE GpuGraphState; - PH_GRAPH_STATE MemoryGraphState; - PH_GRAPH_STATE MemorySharedGraphState; - - PH_CIRCULAR_BUFFER_FLOAT GpuHistory; - PH_CIRCULAR_BUFFER_ULONG MemoryHistory; - PH_CIRCULAR_BUFFER_ULONG MemorySharedHistory; -} ET_GPU_CONTEXT, *PET_GPU_CONTEXT; - -static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; -static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; - -VOID GpuPropUpdatePanel( - _Inout_ PET_GPU_CONTEXT Context - ); - -INT_PTR CALLBACK GpuDetailsDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PET_GPU_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PET_GPU_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PET_GPU_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - { - context->DetailsHandle = NULL; - RemoveProp(hwndDlg, L"Context"); - } - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - context->DetailsHandle = hwndDlg; - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - GpuPropUpdatePanel(context); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lparam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK GpuPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PET_GPU_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PET_GPU_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PET_GPU_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - { - RemoveProp(hwndDlg, L"Context"); - } - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lparam)) - { - case IDC_GPUDETAILS: - { - DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_PROCGPU_DETAILS), - hwndDlg, - GpuDetailsDialogProc, - (LPARAM)context - ); - } - break; - } - } - break; - } - - return FALSE; -} - -VOID GpuPropCreateGraphs( - _In_ PET_GPU_CONTEXT Context - ) -{ - Context->GpuGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - Context->WindowHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(Context->GpuGraphHandle, TRUE); - - Context->MemGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - Context->WindowHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(Context->MemGraphHandle, TRUE); - - Context->SharedGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - Context->WindowHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(Context->SharedGraphHandle, TRUE); -} - -VOID GpuPropCreatePanel( - _In_ PET_GPU_CONTEXT Context - ) -{ - RECT margin; - - Context->PanelHandle = CreateDialogParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_PROCGPU_PANEL), - Context->WindowHandle, - GpuPanelDialogProc, - (LPARAM)Context - ); - - SetWindowPos( - Context->PanelHandle, - NULL, - 10, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER - ); - - ShowWindow(Context->PanelHandle, SW_SHOW); - - margin.left = 0; - margin.top = 0; - margin.right = 0; - margin.bottom = 10; - MapDialogRect(Context->WindowHandle, &margin); - - PhAddLayoutItemEx( - &Context->LayoutManager, - Context->PanelHandle, - NULL, - PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT, - margin - ); - - SendMessage(Context->WindowHandle, WM_SIZE, 0, 0); -} - -VOID GpuPropLayoutGraphs( - _In_ PET_GPU_CONTEXT Context - ) -{ - HDWP deferHandle; - RECT clientRect; - RECT panelRect; - RECT margin = { ET_SCALE_DPI(13), ET_SCALE_DPI(13), ET_SCALE_DPI(13), ET_SCALE_DPI(13) }; - RECT innerMargin = { ET_SCALE_DPI(10), ET_SCALE_DPI(20), ET_SCALE_DPI(10), ET_SCALE_DPI(10) }; - LONG between = ET_SCALE_DPI(3); - ULONG graphWidth; - ULONG graphHeight; - - PhLayoutManagerLayout(&Context->LayoutManager); - - Context->GpuGraphState.Valid = FALSE; - Context->MemoryGraphState.Valid = FALSE; - Context->MemorySharedGraphState.Valid = FALSE; - - GetClientRect(Context->WindowHandle, &clientRect); - - // Limit the rectangle bottom to the top of the panel. - GetWindowRect(Context->PanelHandle, &panelRect); - MapWindowPoints(NULL, Context->WindowHandle, (PPOINT)&panelRect, 2); - clientRect.bottom = panelRect.top + 10; // +10 removing extra spacing - - graphWidth = clientRect.right - margin.left - margin.right; - graphHeight = (clientRect.bottom - margin.top - margin.bottom - between * 2) / 3; - - deferHandle = BeginDeferWindowPos(6); - - deferHandle = DeferWindowPos(deferHandle, Context->GpuGroupBox, NULL, margin.left, margin.top, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); - deferHandle = DeferWindowPos( - deferHandle, - Context->GpuGraphHandle, - NULL, - margin.left + innerMargin.left, - margin.top + innerMargin.top, - graphWidth - innerMargin.left - innerMargin.right, - graphHeight - innerMargin.top - innerMargin.bottom, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - deferHandle = DeferWindowPos(deferHandle, Context->MemGroupBox, NULL, margin.left, margin.top + graphHeight + between, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); - deferHandle = DeferWindowPos( - deferHandle, - Context->MemGraphHandle, - NULL, - margin.left + innerMargin.left, - margin.top + graphHeight + between + innerMargin.top, - graphWidth - innerMargin.left - innerMargin.right, - graphHeight - innerMargin.top - innerMargin.bottom, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - deferHandle = DeferWindowPos(deferHandle, Context->SharedGroupBox, NULL, margin.left, margin.top + (graphHeight + between) * 2, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); - deferHandle = DeferWindowPos( - deferHandle, - Context->SharedGraphHandle, - NULL, - margin.left + innerMargin.left, - margin.top + (graphHeight + between) * 2 + innerMargin.top, - graphWidth - innerMargin.left - innerMargin.right, - graphHeight - innerMargin.top - innerMargin.bottom, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - EndDeferWindowPos(deferHandle); -} - -VOID GpuPropUpdateGraphs( - _In_ PET_GPU_CONTEXT Context - ) -{ - Context->GpuGraphState.Valid = FALSE; - Context->GpuGraphState.TooltipIndex = -1; - Graph_MoveGrid(Context->GpuGraphHandle, 1); - Graph_Draw(Context->GpuGraphHandle); - Graph_UpdateTooltip(Context->GpuGraphHandle); - InvalidateRect(Context->GpuGraphHandle, NULL, FALSE); - - Context->MemoryGraphState.Valid = FALSE; - Context->MemoryGraphState.TooltipIndex = -1; - Graph_MoveGrid(Context->MemGraphHandle, 1); - Graph_Draw(Context->MemGraphHandle); - Graph_UpdateTooltip(Context->MemGraphHandle); - InvalidateRect(Context->MemGraphHandle, NULL, FALSE); - - Context->MemorySharedGraphState.Valid = FALSE; - Context->MemorySharedGraphState.TooltipIndex = -1; - Graph_MoveGrid(Context->SharedGraphHandle, 1); - Graph_Draw(Context->SharedGraphHandle); - Graph_UpdateTooltip(Context->SharedGraphHandle); - InvalidateRect(Context->SharedGraphHandle, NULL, FALSE); -} - -VOID GpuPropUpdatePanel( - _Inout_ PET_GPU_CONTEXT Context - ) -{ - ET_PROCESS_GPU_STATISTICS statistics; - WCHAR runningTimeString[PH_TIMESPAN_STR_LEN_1] = L"N/A"; - - if (Context->Block->ProcessItem->QueryHandle) - EtQueryProcessGpuStatistics(Context->Block->ProcessItem->QueryHandle, &statistics); - else - memset(&statistics, 0, sizeof(ET_PROCESS_GPU_STATISTICS)); - - PhPrintTimeSpan(runningTimeString, statistics.RunningTime * 10, PH_TIMESPAN_HMSM); - - SetDlgItemText(Context->PanelHandle, IDC_ZRUNNINGTIME_V, runningTimeString); - SetDlgItemText(Context->PanelHandle, IDC_ZCONTEXTSWITCHES_V, PhaFormatUInt64(statistics.ContextSwitches, TRUE)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZTOTALNODES_V, PhaFormatUInt64(statistics.NodeCount, TRUE)->Buffer); - SetDlgItemText(Context->PanelHandle, IDC_ZTOTALSEGMENTS_V, PhaFormatUInt64(statistics.SegmentCount, TRUE)->Buffer); - - if (Context->DetailsHandle) - { - // Note: no lock is needed because we only ever update the 'details' dialog text on this same thread. - SetDlgItemText(Context->DetailsHandle, IDC_ZDEDICATEDCOMMITTED_V, PhaFormatSize(statistics.DedicatedCommitted, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZSHAREDCOMMITTED_V, PhaFormatSize(statistics.SharedCommitted, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZTOTALALLOCATED_V, PhaFormatSize(statistics.BytesAllocated, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZTOTALRESERVED_V, PhaFormatSize(statistics.BytesReserved, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZWRITECOMBINEDALLOCATED_V, PhaFormatSize(statistics.WriteCombinedBytesAllocated, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZWRITECOMBINEDRESERVED_V, PhaFormatSize(statistics.WriteCombinedBytesReserved, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZCACHEDALLOCATED_V, PhaFormatSize(statistics.CachedBytesAllocated, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZCACHEDRESERVED_V, PhaFormatSize(statistics.CachedBytesReserved, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZSECTIONALLOCATED_V, PhaFormatSize(statistics.SectionBytesAllocated, -1)->Buffer); - SetDlgItemText(Context->DetailsHandle, IDC_ZSECTIONRESERVED_V, PhaFormatSize(statistics.SectionBytesReserved, -1)->Buffer); - } -} - -VOID GpuPropUpdateInfo( - _In_ PET_GPU_CONTEXT Context - ) -{ - PET_PROCESS_BLOCK block = Context->Block; - - Context->CurrentGpuUsage = block->GpuNodeUsage; - Context->CurrentMemUsage = (ULONG)(block->GpuDedicatedUsage / PAGE_SIZE); - Context->CurrentMemSharedUsage = (ULONG)(block->GpuSharedUsage / PAGE_SIZE); - - PhAddItemCircularBuffer_FLOAT(&Context->GpuHistory, Context->CurrentGpuUsage); - PhAddItemCircularBuffer_ULONG(&Context->MemoryHistory, Context->CurrentMemUsage); - PhAddItemCircularBuffer_ULONG(&Context->MemorySharedHistory, Context->CurrentMemSharedUsage); -} - -VOID NTAPI ProcessesUpdatedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PET_GPU_CONTEXT context = Context; - - if (!context->Enabled) - return; - - if (context->WindowHandle) - { - PostMessage(context->WindowHandle, UPDATE_MSG, 0, 0); - } -} - -INT_PTR CALLBACK EtpGpuPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - PET_GPU_CONTEXT context; - - if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) - { - context = propPageContext->Context; - } - else - { - return FALSE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG sampleCount; - - sampleCount = PhGetIntegerSetting(L"SampleCount"); - - context = PhAllocate(sizeof(ET_GPU_CONTEXT)); - memset(context, 0, sizeof(ET_GPU_CONTEXT)); - - context->WindowHandle = hwndDlg; - context->Block = EtGetProcessBlock(processItem); - context->Enabled = TRUE; - context->GpuGroupBox = GetDlgItem(hwndDlg, IDC_GROUPGPU); - context->MemGroupBox = GetDlgItem(hwndDlg, IDC_GROUPMEM); - context->SharedGroupBox = GetDlgItem(hwndDlg, IDC_GROUPSHARED); - propPageContext->Context = context; - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - - PhInitializeGraphState(&context->GpuGraphState); - PhInitializeGraphState(&context->MemoryGraphState); - PhInitializeGraphState(&context->MemorySharedGraphState); - - PhInitializeCircularBuffer_FLOAT(&context->GpuHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&context->MemoryHistory, sampleCount); - PhInitializeCircularBuffer_ULONG(&context->MemorySharedHistory, sampleCount); - - GpuPropCreateGraphs(context); - GpuPropCreatePanel(context); - GpuPropUpdateInfo(context); - GpuPropUpdatePanel(context); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - ProcessesUpdatedHandler, - context, - &context->ProcessesUpdatedRegistration - ); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&context->LayoutManager); - - PhDeleteGraphState(&context->GpuGraphState); - PhDeleteGraphState(&context->MemoryGraphState); - PhDeleteGraphState(&context->MemorySharedGraphState); - - PhDeleteCircularBuffer_FLOAT(&context->GpuHistory); - PhDeleteCircularBuffer_ULONG(&context->MemoryHistory); - PhDeleteCircularBuffer_ULONG(&context->MemorySharedHistory); - - if (context->GpuGraphHandle) - DestroyWindow(context->GpuGraphHandle); - if (context->MemGraphHandle) - DestroyWindow(context->MemGraphHandle); - if (context->SharedGraphHandle) - DestroyWindow(context->SharedGraphHandle); - if (context->PanelHandle) - DestroyWindow(context->PanelHandle); - - PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); - PhFree(context); - - PhPropPageDlgProcDestroy(hwndDlg); - } - break; - case WM_SHOWWINDOW: - { - if (PhBeginPropPageLayout(hwndDlg, propPageContext)) - PhEndPropPageLayout(hwndDlg, propPageContext); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_SETACTIVE: - context->Enabled = TRUE; - break; - case PSN_KILLACTIVE: - context->Enabled = FALSE; - break; - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - if (header->hwndFrom == context->GpuGraphHandle) - { - if (PhGetIntegerSetting(L"GraphShowText")) - { - HDC hdc; - - PhMoveReference(&context->GpuGraphState.Text, PhFormatString( - L"%.2f%%", - context->CurrentGpuUsage * 100 - )); - - hdc = Graph_GetBufferedContext(context->GpuGraphHandle); - SelectObject(hdc, PhApplicationFont); - PhSetGraphText(hdc, drawInfo, &context->GpuGraphState.Text->sr, - &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); - } - else - { - drawInfo->Text.Buffer = NULL; - } - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); - PhGraphStateGetDrawInfo(&context->GpuGraphState, getDrawInfo, context->GpuHistory.Count); - - if (!context->GpuGraphState.Valid) - { - PhCopyCircularBuffer_FLOAT(&context->GpuHistory, context->GpuGraphState.Data1, drawInfo->LineDataCount); - context->GpuGraphState.Valid = TRUE; - } - } - else if (header->hwndFrom == context->MemGraphHandle) - { - if (PhGetIntegerSetting(L"GraphShowText")) - { - HDC hdc; - - PhMoveReference(&context->MemoryGraphState.Text, PhFormatString( - L"%s", - PhaFormatSize(UInt32x32To64(context->CurrentMemUsage, PAGE_SIZE), -1)->Buffer - )); - - hdc = Graph_GetBufferedContext(context->MemGraphHandle); - SelectObject(hdc, PhApplicationFont); - PhSetGraphText( - hdc, - drawInfo, - &context->MemoryGraphState.Text->sr, - &NormalGraphTextMargin, - &NormalGraphTextPadding, - PH_ALIGN_TOP | PH_ALIGN_LEFT - ); - } - else - { - drawInfo->Text.Buffer = NULL; - } - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPhysical"), 0); - PhGraphStateGetDrawInfo( - &context->MemoryGraphState, - getDrawInfo, - context->MemoryHistory.Count - ); - - if (!context->MemoryGraphState.Valid) - { - ULONG i = 0; - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - context->MemoryGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->MemoryHistory, i); - } - - if (EtGpuDedicatedLimit != 0) - { - PhDivideSinglesBySingle( - context->MemoryGraphState.Data1, - (FLOAT)EtGpuDedicatedLimit / PAGE_SIZE, - drawInfo->LineDataCount - ); - } - - context->MemoryGraphState.Valid = TRUE; - } - } - else if (header->hwndFrom == context->SharedGraphHandle) - { - if (PhGetIntegerSetting(L"GraphShowText")) - { - HDC hdc; - - PhMoveReference(&context->MemorySharedGraphState.Text, PhFormatString( - L"%s", - PhaFormatSize(UInt32x32To64(context->CurrentMemSharedUsage, PAGE_SIZE), -1)->Buffer - )); - - hdc = Graph_GetBufferedContext(context->SharedGraphHandle); - SelectObject(hdc, PhApplicationFont); - PhSetGraphText(hdc, drawInfo, &context->MemorySharedGraphState.Text->sr, - &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); - } - else - { - drawInfo->Text.Buffer = NULL; - } - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); - PhGraphStateGetDrawInfo( - &context->MemorySharedGraphState, - getDrawInfo, - context->MemorySharedHistory.Count - ); - - if (!context->MemorySharedGraphState.Valid) - { - ULONG i = 0; - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - context->MemorySharedGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->MemorySharedHistory, i); - } - - if (EtGpuSharedLimit != 0) - { - PhDivideSinglesBySingle( - context->MemorySharedGraphState.Data1, - (FLOAT)EtGpuSharedLimit / PAGE_SIZE, - drawInfo->LineDataCount - ); - } - - context->MemorySharedGraphState.Valid = TRUE; - } - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (header->hwndFrom == context->GpuGraphHandle) - { - if (context->GpuGraphState.TooltipIndex != getTooltipText->Index) - { - FLOAT gpuUsage = PhGetItemCircularBuffer_FLOAT( - &context->GpuHistory, - getTooltipText->Index - ); - - PhMoveReference(&context->GpuGraphState.TooltipText, PhFormatString( - L"%.2f%%", - gpuUsage * 100 - )); - } - - getTooltipText->Text = context->GpuGraphState.TooltipText->sr; - } - else if (header->hwndFrom == context->MemGraphHandle) - { - if (context->MemoryGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG gpuMemory = PhGetItemCircularBuffer_ULONG( - &context->MemoryHistory, - getTooltipText->Index - ); - - PhMoveReference(&context->MemoryGraphState.TooltipText, - PhFormatSize(UInt32x32To64(gpuMemory, PAGE_SIZE), -1) - ); - } - - getTooltipText->Text = context->MemoryGraphState.TooltipText->sr; - } - else if (header->hwndFrom == context->SharedGraphHandle) - { - if (context->MemorySharedGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG gpuSharedMemory = PhGetItemCircularBuffer_ULONG( - &context->MemorySharedHistory, - getTooltipText->Index - ); - - PhMoveReference(&context->MemorySharedGraphState.TooltipText, - PhFormatSize(UInt32x32To64(gpuSharedMemory, PAGE_SIZE), -1) - ); - } - - getTooltipText->Text = context->MemorySharedGraphState.TooltipText->sr; - } - } - } - break; - } - } - break; - case UPDATE_MSG: - { - if (context->Enabled) - { - GpuPropUpdateInfo(context); - GpuPropUpdateGraphs(context); - GpuPropUpdatePanel(context); - } - } - break; - case WM_SIZE: - { - GpuPropLayoutGraphs(context); - } - break; - } - - return FALSE; -} - -VOID EtProcessGpuPropertiesInitializing( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; - - if (EtGpuEnabled) - { - PhAddProcessPropPage( - propContext->PropContext, - PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCGPU), EtpGpuPageDlgProc, NULL) - ); - } -} \ No newline at end of file +/* + * Process Hacker Extended Tools - + * GPU process properties page + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2015-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +typedef struct _ET_GPU_CONTEXT +{ + HWND WindowHandle; + HWND PanelHandle; + HWND DetailsHandle; + PET_PROCESS_BLOCK Block; + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + BOOLEAN Enabled; + PH_LAYOUT_MANAGER LayoutManager; + + HWND GpuGroupBox; + HWND MemGroupBox; + HWND SharedGroupBox; + HWND CommittedGroupBox; + + HWND GpuGraphHandle; + HWND MemGraphHandle; + HWND SharedGraphHandle; + HWND CommittedGraphHandle; + + FLOAT CurrentGpuUsage; + ULONG CurrentMemUsage; + ULONG CurrentMemSharedUsage; + ULONG CurrentCommitUsage; + + PH_GRAPH_STATE GpuGraphState; + PH_GRAPH_STATE MemoryGraphState; + PH_GRAPH_STATE MemorySharedGraphState; + PH_GRAPH_STATE GpuCommittedGraphState; + + PH_CIRCULAR_BUFFER_FLOAT GpuHistory; + PH_CIRCULAR_BUFFER_ULONG MemoryHistory; + PH_CIRCULAR_BUFFER_ULONG MemorySharedHistory; + PH_CIRCULAR_BUFFER_ULONG GpuCommittedHistory; +} ET_GPU_CONTEXT, *PET_GPU_CONTEXT; + +static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; +static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; + +VOID GpuPropUpdatePanel( + _Inout_ PET_GPU_CONTEXT Context + ); + +INT_PTR CALLBACK GpuDetailsDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PET_GPU_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PET_GPU_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + context->DetailsHandle = NULL; + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + context->DetailsHandle = hwndDlg; + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + GpuPropUpdatePanel(context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lparam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK GpuPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PET_GPU_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PET_GPU_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lparam)) + { + case IDC_GPUDETAILS: + { + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_PROCGPU_DETAILS), + hwndDlg, + GpuDetailsDialogProc, + (LPARAM)context + ); + } + break; + } + } + break; + } + + return FALSE; +} + +VOID GpuPropCreateGraphs( + _In_ PET_GPU_CONTEXT Context + ) +{ + Context->GpuGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS, + 0, + 0, + 3, + 3, + Context->WindowHandle, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(Context->GpuGraphHandle, TRUE); + + Context->MemGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS, + 0, + 0, + 3, + 3, + Context->WindowHandle, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(Context->MemGraphHandle, TRUE); + + Context->SharedGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS, + 0, + 0, + 3, + 3, + Context->WindowHandle, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(Context->SharedGraphHandle, TRUE); + + Context->CommittedGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS, + 0, + 0, + 3, + 3, + Context->WindowHandle, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(Context->CommittedGraphHandle, TRUE); +} + +VOID GpuPropCreatePanel( + _In_ PET_GPU_CONTEXT Context + ) +{ + RECT margin; + + Context->PanelHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_PROCGPU_PANEL), + Context->WindowHandle, + GpuPanelDialogProc, + (LPARAM)Context + ); + + SetWindowPos( + Context->PanelHandle, + NULL, + 10, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER + ); + + ShowWindow(Context->PanelHandle, SW_SHOW); + + margin.left = 0; + margin.top = 0; + margin.right = 0; + margin.bottom = 10; + MapDialogRect(Context->WindowHandle, &margin); + + PhAddLayoutItemEx( + &Context->LayoutManager, + Context->PanelHandle, + NULL, + PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT, + margin + ); + + SendMessage(Context->WindowHandle, WM_SIZE, 0, 0); +} + +VOID GpuPropLayoutGraphs( + _In_ PET_GPU_CONTEXT Context + ) +{ + HDWP deferHandle; + RECT clientRect; + RECT panelRect; + RECT margin = { PH_SCALE_DPI(13), PH_SCALE_DPI(13), PH_SCALE_DPI(13), PH_SCALE_DPI(13) }; + RECT innerMargin = { PH_SCALE_DPI(10), PH_SCALE_DPI(20), PH_SCALE_DPI(10), PH_SCALE_DPI(10) }; + LONG between = PH_SCALE_DPI(3); + ULONG graphWidth; + ULONG graphHeight; + + PhLayoutManagerLayout(&Context->LayoutManager); + + Context->GpuGraphState.Valid = FALSE; + Context->MemoryGraphState.Valid = FALSE; + Context->MemorySharedGraphState.Valid = FALSE; + + GetClientRect(Context->WindowHandle, &clientRect); + + // Limit the rectangle bottom to the top of the panel. + GetWindowRect(Context->PanelHandle, &panelRect); + MapWindowPoints(NULL, Context->WindowHandle, (PPOINT)&panelRect, 2); + clientRect.bottom = panelRect.top + 10; // +10 removing extra spacing + + graphWidth = clientRect.right - margin.left - margin.right; + graphHeight = (clientRect.bottom - margin.top - margin.bottom - between * 4) / 4; + + deferHandle = BeginDeferWindowPos(8); + + deferHandle = DeferWindowPos(deferHandle, Context->GpuGroupBox, NULL, margin.left, margin.top, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); + deferHandle = DeferWindowPos( + deferHandle, + Context->GpuGraphHandle, + NULL, + margin.left + innerMargin.left, + margin.top + innerMargin.top, + graphWidth - innerMargin.left - innerMargin.right, + graphHeight - innerMargin.top - innerMargin.bottom, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + deferHandle = DeferWindowPos(deferHandle, Context->MemGroupBox, NULL, margin.left, margin.top + graphHeight + between, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); + deferHandle = DeferWindowPos( + deferHandle, + Context->MemGraphHandle, + NULL, + margin.left + innerMargin.left, + margin.top + graphHeight + between + innerMargin.top, + graphWidth - innerMargin.left - innerMargin.right, + graphHeight - innerMargin.top - innerMargin.bottom, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + deferHandle = DeferWindowPos(deferHandle, Context->SharedGroupBox, NULL, margin.left, margin.top + (graphHeight + between) * 2, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); + deferHandle = DeferWindowPos( + deferHandle, + Context->SharedGraphHandle, + NULL, + margin.left + innerMargin.left, + margin.top + (graphHeight + between) * 2 + innerMargin.top, + graphWidth - innerMargin.left - innerMargin.right, + graphHeight - innerMargin.top - innerMargin.bottom, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + deferHandle = DeferWindowPos(deferHandle, Context->CommittedGroupBox, NULL, margin.left, margin.top + (graphHeight + between) * 3, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER); + deferHandle = DeferWindowPos( + deferHandle, + Context->CommittedGraphHandle, + NULL, + margin.left + innerMargin.left, + margin.top + (graphHeight + between) * 3 + innerMargin.top, + graphWidth - innerMargin.left - innerMargin.right, + graphHeight - innerMargin.top - innerMargin.bottom, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + EndDeferWindowPos(deferHandle); +} + +VOID GpuPropUpdateGraphs( + _In_ PET_GPU_CONTEXT Context + ) +{ + Context->GpuGraphState.Valid = FALSE; + Context->GpuGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(Context->GpuGraphHandle, 1); + Graph_Draw(Context->GpuGraphHandle); + Graph_UpdateTooltip(Context->GpuGraphHandle); + InvalidateRect(Context->GpuGraphHandle, NULL, FALSE); + + Context->MemoryGraphState.Valid = FALSE; + Context->MemoryGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(Context->MemGraphHandle, 1); + Graph_Draw(Context->MemGraphHandle); + Graph_UpdateTooltip(Context->MemGraphHandle); + InvalidateRect(Context->MemGraphHandle, NULL, FALSE); + + Context->MemorySharedGraphState.Valid = FALSE; + Context->MemorySharedGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(Context->SharedGraphHandle, 1); + Graph_Draw(Context->SharedGraphHandle); + Graph_UpdateTooltip(Context->SharedGraphHandle); + InvalidateRect(Context->SharedGraphHandle, NULL, FALSE); + + Context->GpuCommittedGraphState.Valid = FALSE; + Context->GpuCommittedGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(Context->CommittedGraphHandle, 1); + Graph_Draw(Context->CommittedGraphHandle); + Graph_UpdateTooltip(Context->CommittedGraphHandle); + InvalidateRect(Context->CommittedGraphHandle, NULL, FALSE); +} + +VOID GpuPropUpdatePanel( + _Inout_ PET_GPU_CONTEXT Context + ) +{ + PET_PROCESS_BLOCK block = Context->Block; + WCHAR runningTimeString[PH_TIMESPAN_STR_LEN_1] = L"N/A"; + + PhPrintTimeSpan(runningTimeString, block->GpuRunningTimeDelta.Value * 10, PH_TIMESPAN_HMSM); + PhSetDialogItemText(Context->PanelHandle, IDC_ZRUNNINGTIME_V, runningTimeString); + PhSetDialogItemText(Context->PanelHandle, IDC_ZCONTEXTSWITCHES_V, PhaFormatUInt64(block->GpuContextSwitches, TRUE)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZTOTALNODES_V, PhaFormatUInt64(block->GpuNodeCount, TRUE)->Buffer); + PhSetDialogItemText(Context->PanelHandle, IDC_ZTOTALSEGMENTS_V, PhaFormatUInt64(block->GpuSegmentCount, TRUE)->Buffer); + + if (Context->DetailsHandle) + { + ET_PROCESS_GPU_STATISTICS processGpuStatistics; + + if (Context->Block->ProcessItem->QueryHandle) + EtQueryProcessGpuStatistics(Context->Block->ProcessItem->QueryHandle, &processGpuStatistics); + else + memset(&processGpuStatistics, 0, sizeof(ET_PROCESS_GPU_STATISTICS)); + + PhSetDialogItemText(Context->DetailsHandle, IDC_ZDEDICATEDCOMMITTED_V, PhaFormatSize(processGpuStatistics.DedicatedCommitted, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZSHAREDCOMMITTED_V, PhaFormatSize(processGpuStatistics.SharedCommitted, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZTOTALALLOCATED_V, PhaFormatSize(processGpuStatistics.BytesAllocated, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZTOTALRESERVED_V, PhaFormatSize(processGpuStatistics.BytesReserved, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZWRITECOMBINEDALLOCATED_V, PhaFormatSize(processGpuStatistics.WriteCombinedBytesAllocated, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZWRITECOMBINEDRESERVED_V, PhaFormatSize(processGpuStatistics.WriteCombinedBytesReserved, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZCACHEDALLOCATED_V, PhaFormatSize(processGpuStatistics.CachedBytesAllocated, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZCACHEDRESERVED_V, PhaFormatSize(processGpuStatistics.CachedBytesReserved, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZSECTIONALLOCATED_V, PhaFormatSize(processGpuStatistics.SectionBytesAllocated, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->DetailsHandle, IDC_ZSECTIONRESERVED_V, PhaFormatSize(processGpuStatistics.SectionBytesReserved, ULONG_MAX)->Buffer); + } +} + +VOID GpuPropUpdateInfo( + _In_ PET_GPU_CONTEXT Context + ) +{ + PET_PROCESS_BLOCK block = Context->Block; + + Context->CurrentGpuUsage = block->GpuNodeUsage; + Context->CurrentMemUsage = (ULONG)(block->GpuDedicatedUsage / PAGE_SIZE); + Context->CurrentMemSharedUsage = (ULONG)(block->GpuSharedUsage / PAGE_SIZE); + Context->CurrentCommitUsage = (ULONG)(block->GpuCommitUsage / PAGE_SIZE); + + PhAddItemCircularBuffer_FLOAT(&Context->GpuHistory, Context->CurrentGpuUsage); + PhAddItemCircularBuffer_ULONG(&Context->MemoryHistory, Context->CurrentMemUsage); + PhAddItemCircularBuffer_ULONG(&Context->MemorySharedHistory, Context->CurrentMemSharedUsage); + PhAddItemCircularBuffer_ULONG(&Context->GpuCommittedHistory, Context->CurrentCommitUsage); +} + +VOID NTAPI ProcessesUpdatedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PET_GPU_CONTEXT context = Context; + + if (!context->Enabled) + return; + + if (context->WindowHandle) + { + PostMessage(context->WindowHandle, ET_WM_UPDATE, 0, 0); + } +} + +INT_PTR CALLBACK EtpGpuPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PET_GPU_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG sampleCount; + + // We have already set the group boxes to have WS_EX_TRANSPARENT to fix + // the drawing issue that arises when using WS_CLIPCHILDREN. However + // in removing the flicker from the graphs the group boxes will now flicker. + // It's a good tradeoff since no one stares at the group boxes. + PhSetWindowStyle(hwndDlg, WS_CLIPCHILDREN, WS_CLIPCHILDREN); + + sampleCount = PhGetIntegerSetting(L"SampleCount"); + + context = PhAllocate(sizeof(ET_GPU_CONTEXT)); + memset(context, 0, sizeof(ET_GPU_CONTEXT)); + + context->WindowHandle = hwndDlg; + context->Block = EtGetProcessBlock(processItem); + context->Enabled = TRUE; + context->GpuGroupBox = GetDlgItem(hwndDlg, IDC_GROUPGPU); + context->MemGroupBox = GetDlgItem(hwndDlg, IDC_GROUPMEM); + context->SharedGroupBox = GetDlgItem(hwndDlg, IDC_GROUPSHARED); + context->CommittedGroupBox = GetDlgItem(hwndDlg, IDC_GROUPCOMMIT); + propPageContext->Context = context; + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + + PhInitializeGraphState(&context->GpuGraphState); + PhInitializeGraphState(&context->MemoryGraphState); + PhInitializeGraphState(&context->MemorySharedGraphState); + PhInitializeGraphState(&context->GpuCommittedGraphState); + + PhInitializeCircularBuffer_FLOAT(&context->GpuHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&context->MemoryHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&context->MemorySharedHistory, sampleCount); + PhInitializeCircularBuffer_ULONG(&context->GpuCommittedHistory, sampleCount); + + GpuPropCreateGraphs(context); + GpuPropCreatePanel(context); + GpuPropUpdateInfo(context); + GpuPropUpdatePanel(context); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + ProcessesUpdatedHandler, + context, + &context->ProcessesUpdatedRegistration + ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + PhDeleteGraphState(&context->GpuGraphState); + PhDeleteGraphState(&context->MemoryGraphState); + PhDeleteGraphState(&context->MemorySharedGraphState); + PhDeleteGraphState(&context->GpuCommittedGraphState); + + PhDeleteCircularBuffer_FLOAT(&context->GpuHistory); + PhDeleteCircularBuffer_ULONG(&context->MemoryHistory); + PhDeleteCircularBuffer_ULONG(&context->MemorySharedHistory); + PhDeleteCircularBuffer_ULONG(&context->GpuCommittedHistory); + + if (context->GpuGraphHandle) + DestroyWindow(context->GpuGraphHandle); + if (context->MemGraphHandle) + DestroyWindow(context->MemGraphHandle); + if (context->SharedGraphHandle) + DestroyWindow(context->SharedGraphHandle); + if (context->CommittedGraphHandle) + DestroyWindow(context->CommittedGraphHandle); + if (context->PanelHandle) + DestroyWindow(context->PanelHandle); + + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &context->ProcessesUpdatedRegistration); + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + if (PhBeginPropPageLayout(hwndDlg, propPageContext)) + PhEndPropPageLayout(hwndDlg, propPageContext); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_SETACTIVE: + context->Enabled = TRUE; + break; + case PSN_KILLACTIVE: + context->Enabled = FALSE; + break; + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + if (header->hwndFrom == context->GpuGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + + PhMoveReference(&context->GpuGraphState.Text, PhFormatString( + L"%.2f%%", + context->CurrentGpuUsage * 100 + )); + + hdc = Graph_GetBufferedContext(context->GpuGraphHandle); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &context->GpuGraphState.Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); + PhGraphStateGetDrawInfo(&context->GpuGraphState, getDrawInfo, context->GpuHistory.Count); + + if (!context->GpuGraphState.Valid) + { + PhCopyCircularBuffer_FLOAT(&context->GpuHistory, context->GpuGraphState.Data1, drawInfo->LineDataCount); + context->GpuGraphState.Valid = TRUE; + } + } + else if (header->hwndFrom == context->MemGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + + PhMoveReference(&context->MemoryGraphState.Text, PhFormatString( + L"%s", + PhaFormatSize(UInt32x32To64(context->CurrentMemUsage, PAGE_SIZE), ULONG_MAX)->Buffer + )); + + hdc = Graph_GetBufferedContext(context->MemGraphHandle); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText( + hdc, + drawInfo, + &context->MemoryGraphState.Text->sr, + &NormalGraphTextMargin, + &NormalGraphTextPadding, + PH_ALIGN_TOP | PH_ALIGN_LEFT + ); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPhysical"), 0); + PhGraphStateGetDrawInfo( + &context->MemoryGraphState, + getDrawInfo, + context->MemoryHistory.Count + ); + + if (!context->MemoryGraphState.Valid) + { + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + context->MemoryGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->MemoryHistory, i); + } + + if (EtGpuDedicatedLimit != 0) + { + PhDivideSinglesBySingle( + context->MemoryGraphState.Data1, + (FLOAT)EtGpuDedicatedLimit / PAGE_SIZE, + drawInfo->LineDataCount + ); + } + + context->MemoryGraphState.Valid = TRUE; + } + } + else if (header->hwndFrom == context->SharedGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + + PhMoveReference(&context->MemorySharedGraphState.Text, PhFormatString( + L"%s", + PhaFormatSize(UInt32x32To64(context->CurrentMemSharedUsage, PAGE_SIZE), ULONG_MAX)->Buffer + )); + + hdc = Graph_GetBufferedContext(context->SharedGraphHandle); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &context->MemorySharedGraphState.Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); + PhGraphStateGetDrawInfo( + &context->MemorySharedGraphState, + getDrawInfo, + context->MemorySharedHistory.Count + ); + + if (!context->MemorySharedGraphState.Valid) + { + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + context->MemorySharedGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->MemorySharedHistory, i); + } + + if (EtGpuSharedLimit != 0) + { + PhDivideSinglesBySingle( + context->MemorySharedGraphState.Data1, + (FLOAT)EtGpuSharedLimit / PAGE_SIZE, + drawInfo->LineDataCount + ); + } + + context->MemorySharedGraphState.Valid = TRUE; + } + } + else if (header->hwndFrom == context->CommittedGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc; + + PhMoveReference(&context->GpuCommittedGraphState.Text, PhFormatString( + L"%s", + PhaFormatSize(UInt32x32To64(context->CurrentCommitUsage, PAGE_SIZE), ULONG_MAX)->Buffer + )); + + hdc = Graph_GetBufferedContext(context->CommittedGraphHandle); + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &context->GpuCommittedGraphState.Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); + PhGraphStateGetDrawInfo( + &context->GpuCommittedGraphState, + getDrawInfo, + context->GpuCommittedHistory.Count + ); + + if (!context->GpuCommittedGraphState.Valid) + { + ULONG i; + static FLOAT max = 1024 * 1024; // minimum scaling + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + + context->GpuCommittedGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->GpuCommittedHistory, i); + + if (max < data1) + max = data1; + } + + // Scale the data. + PhDivideSinglesBySingle( + context->GpuCommittedGraphState.Data1, + max, + drawInfo->LineDataCount + ); + + context->GpuCommittedGraphState.Valid = TRUE; + } + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (header->hwndFrom == context->GpuGraphHandle) + { + if (context->GpuGraphState.TooltipIndex != getTooltipText->Index) + { + FLOAT gpuUsage = PhGetItemCircularBuffer_FLOAT( + &context->GpuHistory, + getTooltipText->Index + ); + + PhMoveReference(&context->GpuGraphState.TooltipText, PhFormatString( + L"%.2f%%\n%s", + gpuUsage * 100, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer) + ); + } + + getTooltipText->Text = PhGetStringRef(context->GpuGraphState.TooltipText); + } + else if (header->hwndFrom == context->MemGraphHandle) + { + if (context->MemoryGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG gpuMemory = PhGetItemCircularBuffer_ULONG( + &context->MemoryHistory, + getTooltipText->Index + ); + + PhMoveReference(&context->MemoryGraphState.TooltipText, PhFormatString( + L"%s\n%s", + PhFormatSize(UInt32x32To64(gpuMemory, PAGE_SIZE), ULONG_MAX)->Buffer, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer) + ); + } + + getTooltipText->Text = PhGetStringRef(context->MemoryGraphState.TooltipText); + } + else if (header->hwndFrom == context->SharedGraphHandle) + { + if (context->MemorySharedGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG gpuSharedMemory = PhGetItemCircularBuffer_ULONG( + &context->MemorySharedHistory, + getTooltipText->Index + ); + + PhMoveReference(&context->MemorySharedGraphState.TooltipText, PhFormatString( + L"%s\n%s", + PhFormatSize(UInt32x32To64(gpuSharedMemory, PAGE_SIZE), ULONG_MAX)->Buffer, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer) + ); + } + + getTooltipText->Text = PhGetStringRef(context->MemorySharedGraphState.TooltipText); + } + else if (header->hwndFrom == context->CommittedGraphHandle) + { + if (context->GpuCommittedGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG gpuCommitMemory = PhGetItemCircularBuffer_ULONG( + &context->GpuCommittedHistory, + getTooltipText->Index + ); + + PhMoveReference(&context->GpuCommittedGraphState.TooltipText, PhFormatString( + L"%s\n%s", + PhFormatSize(UInt32x32To64(gpuCommitMemory, PAGE_SIZE), ULONG_MAX)->Buffer, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer) + ); + } + + getTooltipText->Text = PhGetStringRef(context->GpuCommittedGraphState.TooltipText); + } + } + } + break; + } + } + break; + case ET_WM_UPDATE: + { + if (context->Enabled) + { + GpuPropUpdateInfo(context); + GpuPropUpdateGraphs(context); + GpuPropUpdatePanel(context); + } + } + break; + case WM_SIZE: + { + GpuPropLayoutGraphs(context); + } + break; + } + + return FALSE; +} + +VOID EtProcessGpuPropertiesInitializing( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; + + if (EtGpuEnabled) + { + PhAddProcessPropPage( + propContext->PropContext, + PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCGPU), EtpGpuPageDlgProc, NULL) + ); + } +} diff --git a/plugins/ExtendedTools/gpusys.c b/plugins/ExtendedTools/gpusys.c index b57e88e2ad71..ab0e6ac14417 100644 --- a/plugins/ExtendedTools/gpusys.c +++ b/plugins/ExtendedTools/gpusys.c @@ -1,722 +1,743 @@ -/* - * Process Hacker Extended Tools - - * GPU system information section - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include "gpusys.h" - -static PPH_SYSINFO_SECTION GpuSection; -static HWND GpuDialog; -static PH_LAYOUT_MANAGER GpuLayoutManager; -static RECT GpuGraphMargin; -static HWND GpuGraphHandle; -static PH_GRAPH_STATE GpuGraphState; -static HWND DedicatedGraphHandle; -static PH_GRAPH_STATE DedicatedGraphState; -static HWND SharedGraphHandle; -static PH_GRAPH_STATE SharedGraphState; -static HWND GpuPanel; - -VOID EtGpuSystemInformationInitializing( - _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers - ) -{ - PH_SYSINFO_SECTION section; - - memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); - PhInitializeStringRef(§ion.Name, L"GPU"); - section.Flags = 0; - section.Callback = EtpGpuSysInfoSectionCallback; - - GpuSection = Pointers->CreateSection(§ion); -} - -BOOLEAN EtpGpuSysInfoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ) -{ - switch (Message) - { - case SysInfoDestroy: - { - if (GpuDialog) - { - EtpUninitializeGpuDialog(); - GpuDialog = NULL; - } - } - return TRUE; - case SysInfoTick: - { - if (GpuDialog) - { - EtpTickGpuDialog(); - } - } - return TRUE; - case SysInfoCreateDialog: - { - PPH_SYSINFO_CREATE_DIALOG createDialog = Parameter1; - - createDialog->Instance = PluginInstance->DllBase; - createDialog->Template = MAKEINTRESOURCE(IDD_SYSINFO_GPU); - createDialog->DialogProc = EtpGpuDialogProc; - } - return TRUE; - case SysInfoGraphGetDrawInfo: - { - PPH_GRAPH_DRAW_INFO drawInfo = Parameter1; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); - PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, EtGpuNodeHistory.Count); - - if (!Section->GraphState.Valid) - { - PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, Section->GraphState.Data1, drawInfo->LineDataCount); - Section->GraphState.Valid = TRUE; - } - } - return TRUE; - case SysInfoGraphGetTooltipText: - { - PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT getTooltipText = Parameter1; - FLOAT gpu; - - gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodeHistory, getTooltipText->Index); - - PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( - L"%.2f%%%s\n%s", - gpu * 100, - PhGetStringOrEmpty(EtpGetMaxNodeString(getTooltipText->Index)), - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - getTooltipText->Text = Section->GraphState.TooltipText->sr; - } - return TRUE; - case SysInfoGraphDrawPanel: - { - PPH_SYSINFO_DRAW_PANEL drawPanel = Parameter1; - - drawPanel->Title = PhCreateString(L"GPU"); - drawPanel->SubTitle = PhFormatString(L"%.2f%%", EtGpuNodeUsage * 100); - } - return TRUE; - } - - return FALSE; -} - -VOID EtpInitializeGpuDialog( - VOID - ) -{ - PhInitializeGraphState(&GpuGraphState); - PhInitializeGraphState(&DedicatedGraphState); - PhInitializeGraphState(&SharedGraphState); -} - -VOID EtpUninitializeGpuDialog( - VOID - ) -{ - PhDeleteGraphState(&GpuGraphState); - PhDeleteGraphState(&DedicatedGraphState); - PhDeleteGraphState(&SharedGraphState); -} - -VOID EtpTickGpuDialog( - VOID - ) -{ - EtpUpdateGpuGraphs(); - EtpUpdateGpuPanel(); -} - -INT_PTR CALLBACK EtpGpuDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_LAYOUT_ITEM graphItem; - PPH_LAYOUT_ITEM panelItem; - - EtpInitializeGpuDialog(); - - GpuDialog = hwndDlg; - PhInitializeLayoutManager(&GpuLayoutManager, hwndDlg); - PhAddLayoutItem(&GpuLayoutManager, GetDlgItem(hwndDlg, IDC_GPUNAME), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); - graphItem = PhAddLayoutItem(&GpuLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); - GpuGraphMargin = graphItem->Margin; - panelItem = PhAddLayoutItem(&GpuLayoutManager, GetDlgItem(hwndDlg, IDC_PANEL_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - - SendMessage(GetDlgItem(hwndDlg, IDC_TITLE), WM_SETFONT, (WPARAM)GpuSection->Parameters->LargeFont, FALSE); - SendMessage(GetDlgItem(hwndDlg, IDC_GPUNAME), WM_SETFONT, (WPARAM)GpuSection->Parameters->MediumFont, FALSE); - - SetDlgItemText(hwndDlg, IDC_GPUNAME, ((PPH_STRING)PH_AUTO(EtpGetGpuNameString()))->Buffer); - - GpuPanel = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_SYSINFO_GPUPANEL), hwndDlg, EtpGpuPanelDialogProc); - ShowWindow(GpuPanel, SW_SHOW); - PhAddLayoutItemEx(&GpuLayoutManager, GpuPanel, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM, panelItem->Margin); - - EtpCreateGpuGraphs(); - EtpUpdateGpuGraphs(); - EtpUpdateGpuPanel(); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&GpuLayoutManager); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&GpuLayoutManager); - EtpLayoutGpuGraphs(); - } - break; - case WM_NOTIFY: - { - NMHDR *header = (NMHDR *)lParam; - - if (header->hwndFrom == GpuGraphHandle) - { - EtpNotifyGpuGraph(header); - } - else if (header->hwndFrom == DedicatedGraphHandle) - { - EtpNotifyDedicatedGraph(header); - } - else if (header->hwndFrom == SharedGraphHandle) - { - EtpNotifySharedGraph(header); - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK EtpGpuPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_NODES: - EtShowGpuNodesDialog(GpuDialog, GpuSection->Parameters); - break; - } - } - break; - } - - return FALSE; -} - -VOID EtpCreateGpuGraphs( - VOID - ) -{ - GpuGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - GpuDialog, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(GpuGraphHandle, TRUE); - - DedicatedGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - GpuDialog, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(DedicatedGraphHandle, TRUE); - - SharedGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - GpuDialog, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(SharedGraphHandle, TRUE); -} - -VOID EtpLayoutGpuGraphs( - VOID - ) -{ - RECT clientRect; - RECT labelRect; - ULONG graphWidth; - ULONG graphHeight; - HDWP deferHandle; - ULONG y; - - GetClientRect(GpuDialog, &clientRect); - GetClientRect(GetDlgItem(GpuDialog, IDC_GPU_L), &labelRect); - graphWidth = clientRect.right - GpuGraphMargin.left - GpuGraphMargin.right; - graphHeight = (clientRect.bottom - GpuGraphMargin.top - GpuGraphMargin.bottom - labelRect.bottom * 3 - ET_GPU_PADDING * 5) / 3; - - deferHandle = BeginDeferWindowPos(6); - y = GpuGraphMargin.top; - - deferHandle = DeferWindowPos( - deferHandle, - GetDlgItem(GpuDialog, IDC_GPU_L), - NULL, - GpuGraphMargin.left, - y, - 0, - 0, - SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER - ); - y += labelRect.bottom + ET_GPU_PADDING; - - deferHandle = DeferWindowPos( - deferHandle, - GpuGraphHandle, - NULL, - GpuGraphMargin.left, - y, - graphWidth, - graphHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - y += graphHeight + ET_GPU_PADDING; - - deferHandle = DeferWindowPos( - deferHandle, - GetDlgItem(GpuDialog, IDC_DEDICATED_L), - NULL, - GpuGraphMargin.left, - y, - 0, - 0, - SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER - ); - y += labelRect.bottom + ET_GPU_PADDING; - - deferHandle = DeferWindowPos( - deferHandle, - DedicatedGraphHandle, - NULL, - GpuGraphMargin.left, - y, - graphWidth, - graphHeight, - SWP_NOACTIVATE | SWP_NOZORDER - ); - y += graphHeight + ET_GPU_PADDING; - - deferHandle = DeferWindowPos( - deferHandle, - GetDlgItem(GpuDialog, IDC_SHARED_L), - NULL, - GpuGraphMargin.left, - y, - 0, - 0, - SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER - ); - y += labelRect.bottom + ET_GPU_PADDING; - - deferHandle = DeferWindowPos( - deferHandle, - SharedGraphHandle, - NULL, - GpuGraphMargin.left, - y, - graphWidth, - clientRect.bottom - GpuGraphMargin.bottom - y, - SWP_NOACTIVATE | SWP_NOZORDER - ); - - EndDeferWindowPos(deferHandle); -} - -VOID EtpNotifyGpuGraph( - _In_ NMHDR *Header - ) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - GpuSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); - - PhGraphStateGetDrawInfo( - &GpuGraphState, - getDrawInfo, - EtGpuNodeHistory.Count - ); - - if (!GpuGraphState.Valid) - { - PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, GpuGraphState.Data1, drawInfo->LineDataCount); - GpuGraphState.Valid = TRUE; - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (GpuGraphState.TooltipIndex != getTooltipText->Index) - { - FLOAT gpu; - - gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodeHistory, getTooltipText->Index); - - PhMoveReference(&GpuGraphState.TooltipText, PhFormatString( - L"%.2f%%%s\n%s", - gpu * 100, - PhGetStringOrEmpty(EtpGetMaxNodeString(getTooltipText->Index)), - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = GpuGraphState.TooltipText->sr; - } - } - break; - case GCN_MOUSEEVENT: - { - PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; - PPH_PROCESS_RECORD record; - - record = NULL; - - if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) - { - record = EtpReferenceMaxNodeRecord(mouseEvent->Index); - } - - if (record) - { - PhShowProcessRecordDialog(GpuDialog, record); - PhDereferenceProcessRecord(record); - } - } - break; - } -} - -VOID EtpNotifyDedicatedGraph( - _In_ NMHDR *Header - ) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - ULONG i; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - GpuSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); - - PhGraphStateGetDrawInfo( - &DedicatedGraphState, - getDrawInfo, - EtGpuDedicatedHistory.Count - ); - - if (!DedicatedGraphState.Valid) - { - for (i = 0; i < drawInfo->LineDataCount; i++) - { - DedicatedGraphState.Data1[i] = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtGpuDedicatedHistory, i); - } - - if (EtGpuDedicatedLimit != 0) - { - // Scale the data. - PhDivideSinglesBySingle( - DedicatedGraphState.Data1, - (FLOAT)EtGpuDedicatedLimit / PAGE_SIZE, - drawInfo->LineDataCount - ); - } - - DedicatedGraphState.Valid = TRUE; - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (DedicatedGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG usedPages; - - usedPages = PhGetItemCircularBuffer_ULONG(&EtGpuDedicatedHistory, getTooltipText->Index); - - PhMoveReference(&DedicatedGraphState.TooltipText, PhFormatString( - L"Dedicated Memory: %s\n%s", - PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), -1)->Buffer, - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = DedicatedGraphState.TooltipText->sr; - } - } - break; - } -} - -VOID EtpNotifySharedGraph( - _In_ NMHDR *Header - ) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - ULONG i; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - GpuSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorPhysical"), 0); - - PhGraphStateGetDrawInfo( - &SharedGraphState, - getDrawInfo, - EtGpuSharedHistory.Count - ); - - if (!SharedGraphState.Valid) - { - for (i = 0; i < drawInfo->LineDataCount; i++) - { - SharedGraphState.Data1[i] = - (FLOAT)PhGetItemCircularBuffer_ULONG(&EtGpuSharedHistory, i); - } - - if (EtGpuSharedLimit != 0) - { - // Scale the data. - PhDivideSinglesBySingle( - SharedGraphState.Data1, - (FLOAT)EtGpuSharedLimit / PAGE_SIZE, - drawInfo->LineDataCount - ); - } - - SharedGraphState.Valid = TRUE; - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (SharedGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG usedPages; - - usedPages = PhGetItemCircularBuffer_ULONG(&EtGpuSharedHistory, getTooltipText->Index); - - PhMoveReference(&SharedGraphState.TooltipText, PhFormatString( - L"Shared Memory: %s\n%s", - PhaFormatSize(UInt32x32To64(usedPages, PAGE_SIZE), -1)->Buffer, - ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer - )); - } - - getTooltipText->Text = SharedGraphState.TooltipText->sr; - } - } - break; - } -} - -VOID EtpUpdateGpuGraphs( - VOID - ) -{ - GpuGraphState.Valid = FALSE; - GpuGraphState.TooltipIndex = -1; - Graph_MoveGrid(GpuGraphHandle, 1); - Graph_Draw(GpuGraphHandle); - Graph_UpdateTooltip(GpuGraphHandle); - InvalidateRect(GpuGraphHandle, NULL, FALSE); - - DedicatedGraphState.Valid = FALSE; - DedicatedGraphState.TooltipIndex = -1; - Graph_MoveGrid(DedicatedGraphHandle, 1); - Graph_Draw(DedicatedGraphHandle); - Graph_UpdateTooltip(DedicatedGraphHandle); - InvalidateRect(DedicatedGraphHandle, NULL, FALSE); - - SharedGraphState.Valid = FALSE; - SharedGraphState.TooltipIndex = -1; - Graph_MoveGrid(SharedGraphHandle, 1); - Graph_Draw(SharedGraphHandle); - Graph_UpdateTooltip(SharedGraphHandle); - InvalidateRect(SharedGraphHandle, NULL, FALSE); -} - -VOID EtpUpdateGpuPanel( - VOID - ) -{ - SetDlgItemText(GpuPanel, IDC_ZDEDICATEDCURRENT_V, PhaFormatSize(EtGpuDedicatedUsage, -1)->Buffer); - SetDlgItemText(GpuPanel, IDC_ZDEDICATEDLIMIT_V, PhaFormatSize(EtGpuDedicatedLimit, -1)->Buffer); - - SetDlgItemText(GpuPanel, IDC_ZSHAREDCURRENT_V, PhaFormatSize(EtGpuSharedUsage, -1)->Buffer); - SetDlgItemText(GpuPanel, IDC_ZSHAREDLIMIT_V, PhaFormatSize(EtGpuSharedLimit, -1)->Buffer); -} - -PPH_PROCESS_RECORD EtpReferenceMaxNodeRecord( - _In_ LONG Index - ) -{ - LARGE_INTEGER time; - ULONG maxProcessId; - - maxProcessId = PhGetItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, Index); - - if (!maxProcessId) - return NULL; - - PhGetStatisticsTime(NULL, Index, &time); - time.QuadPart += PH_TICKS_PER_SEC - 1; - - return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); -} - -PPH_STRING EtpGetMaxNodeString( - _In_ LONG Index - ) -{ - PPH_PROCESS_RECORD maxProcessRecord; - FLOAT maxGpuUsage; - PPH_STRING maxUsageString = NULL; - - if (maxProcessRecord = EtpReferenceMaxNodeRecord(Index)) - { - maxGpuUsage = PhGetItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, Index); - - maxUsageString = PhaFormatString( - L"\n%s (%lu): %.2f%%", - maxProcessRecord->ProcessName->Buffer, - HandleToUlong(maxProcessRecord->ProcessId), - maxGpuUsage * 100 - ); - - PhDereferenceProcessRecord(maxProcessRecord); - } - - return maxUsageString; -} - -PPH_STRING EtpGetGpuNameString( - VOID - ) -{ - ULONG i; - ULONG count; - PH_STRING_BUILDER sb; - - count = EtGetGpuAdapterCount(); - PhInitializeStringBuilder(&sb, 100); - - for (i = 0; i < count; i++) - { - PPH_STRING description; - - description = EtGetGpuAdapterDescription(i); - - if (!PhIsNullOrEmptyString(description)) - { - // Ignore "Microsoft Basic Render Driver" unless we don't have any other adapters. - // This does not take into account localization. - if (count == 1 || !PhEqualString2(description, L"Microsoft Basic Render Driver", TRUE)) - { - PhAppendStringBuilder(&sb, &description->sr); - PhAppendStringBuilder2(&sb, L", "); - } - } - - if (description) - PhDereferenceObject(description); - } - - if (sb.String->Length != 0) - PhRemoveEndStringBuilder(&sb, 2); - - return PhFinalStringBuilderString(&sb); -} +/* + * Process Hacker Extended Tools - + * GPU system information section + * + * Copyright (C) 2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "gpusys.h" + +static PPH_SYSINFO_SECTION GpuSection; +static HWND GpuDialog; +static PH_LAYOUT_MANAGER GpuLayoutManager; +static RECT GpuGraphMargin; +static HWND GpuGraphHandle; +static PH_GRAPH_STATE GpuGraphState; +static HWND DedicatedGraphHandle; +static PH_GRAPH_STATE DedicatedGraphState; +static HWND SharedGraphHandle; +static PH_GRAPH_STATE SharedGraphState; +static HWND GpuPanel; + +VOID EtGpuSystemInformationInitializing( + _In_ PPH_PLUGIN_SYSINFO_POINTERS Pointers + ) +{ + PH_SYSINFO_SECTION section; + + memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); + PhInitializeStringRef(§ion.Name, L"GPU"); + section.Flags = 0; + section.Callback = EtpGpuSysInfoSectionCallback; + + GpuSection = Pointers->CreateSection(§ion); +} + +BOOLEAN EtpGpuSysInfoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ) +{ + switch (Message) + { + case SysInfoDestroy: + { + if (GpuDialog) + { + EtpUninitializeGpuDialog(); + GpuDialog = NULL; + } + } + return TRUE; + case SysInfoTick: + { + if (GpuDialog) + { + EtpTickGpuDialog(); + } + } + return TRUE; + case SysInfoCreateDialog: + { + PPH_SYSINFO_CREATE_DIALOG createDialog = Parameter1; + + createDialog->Instance = PluginInstance->DllBase; + createDialog->Template = MAKEINTRESOURCE(IDD_SYSINFO_GPU); + createDialog->DialogProc = EtpGpuDialogProc; + } + return TRUE; + case SysInfoGraphGetDrawInfo: + { + PPH_GRAPH_DRAW_INFO drawInfo = Parameter1; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + Section->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); + PhGetDrawInfoGraphBuffers(&Section->GraphState.Buffers, drawInfo, EtGpuNodeHistory.Count); + + if (!Section->GraphState.Valid) + { + PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, Section->GraphState.Data1, drawInfo->LineDataCount); + Section->GraphState.Valid = TRUE; + } + } + return TRUE; + case SysInfoGraphGetTooltipText: + { + PPH_SYSINFO_GRAPH_GET_TOOLTIP_TEXT getTooltipText = Parameter1; + FLOAT gpu; + + gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodeHistory, getTooltipText->Index); + + PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( + L"%.2f%%%s\n%s", + gpu * 100, + PhGetStringOrEmpty(EtpGetMaxNodeString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + getTooltipText->Text = Section->GraphState.TooltipText->sr; + } + return TRUE; + case SysInfoGraphDrawPanel: + { + PPH_SYSINFO_DRAW_PANEL drawPanel = Parameter1; + + drawPanel->Title = PhCreateString(L"GPU"); + + if (EtGpuDedicatedUsage && EtGpuDedicatedLimit) // Required for Intel IGP devices -dmex + { + drawPanel->SubTitle = PhFormatString( + L"%.2f%%\n%s / %s", + EtGpuNodeUsage * 100, + PhaFormatSize(EtGpuDedicatedUsage, ULONG_MAX)->Buffer, + PhaFormatSize(EtGpuDedicatedLimit, ULONG_MAX)->Buffer + ); + drawPanel->SubTitleOverflow = PhFormatString( + L"%.2f%%\n%s", + EtGpuNodeUsage * 100, + PhaFormatSize(EtGpuDedicatedUsage, ULONG_MAX)->Buffer + ); + } + else + { + drawPanel->SubTitle = PhFormatString( + L"%.2f%%\n", + EtGpuNodeUsage * 100 + ); + } + } + return TRUE; + } + + return FALSE; +} + +VOID EtpInitializeGpuDialog( + VOID + ) +{ + PhInitializeGraphState(&GpuGraphState); + PhInitializeGraphState(&DedicatedGraphState); + PhInitializeGraphState(&SharedGraphState); +} + +VOID EtpUninitializeGpuDialog( + VOID + ) +{ + PhDeleteGraphState(&GpuGraphState); + PhDeleteGraphState(&DedicatedGraphState); + PhDeleteGraphState(&SharedGraphState); +} + +VOID EtpTickGpuDialog( + VOID + ) +{ + EtpUpdateGpuGraphs(); + EtpUpdateGpuPanel(); +} + +INT_PTR CALLBACK EtpGpuDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_LAYOUT_ITEM graphItem; + PPH_LAYOUT_ITEM panelItem; + + EtpInitializeGpuDialog(); + + GpuDialog = hwndDlg; + PhInitializeLayoutManager(&GpuLayoutManager, hwndDlg); + PhAddLayoutItem(&GpuLayoutManager, GetDlgItem(hwndDlg, IDC_GPUNAME), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); + graphItem = PhAddLayoutItem(&GpuLayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); + GpuGraphMargin = graphItem->Margin; + panelItem = PhAddLayoutItem(&GpuLayoutManager, GetDlgItem(hwndDlg, IDC_PANEL_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + SetWindowFont(GetDlgItem(hwndDlg, IDC_TITLE), GpuSection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_GPUNAME), GpuSection->Parameters->MediumFont, FALSE); + + PhSetDialogItemText(hwndDlg, IDC_GPUNAME, ((PPH_STRING)PH_AUTO(EtpGetGpuNameString()))->Buffer); + + GpuPanel = CreateDialog(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_SYSINFO_GPUPANEL), hwndDlg, EtpGpuPanelDialogProc); + ShowWindow(GpuPanel, SW_SHOW); + PhAddLayoutItemEx(&GpuLayoutManager, GpuPanel, NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM, panelItem->Margin); + + EtpCreateGpuGraphs(); + EtpUpdateGpuGraphs(); + EtpUpdateGpuPanel(); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&GpuLayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&GpuLayoutManager); + EtpLayoutGpuGraphs(); + } + break; + case WM_NOTIFY: + { + NMHDR *header = (NMHDR *)lParam; + + if (header->hwndFrom == GpuGraphHandle) + { + EtpNotifyGpuGraph(header); + } + else if (header->hwndFrom == DedicatedGraphHandle) + { + EtpNotifyDedicatedGraph(header); + } + else if (header->hwndFrom == SharedGraphHandle) + { + EtpNotifySharedGraph(header); + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK EtpGpuPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_NODES: + EtShowGpuNodesDialog(GpuDialog); + break; + case IDC_DETAILS: + EtShowGpuDetailsDialog(GpuDialog); + break; + } + } + break; + } + + return FALSE; +} + +VOID EtpCreateGpuGraphs( + VOID + ) +{ + GpuGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + GpuDialog, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(GpuGraphHandle, TRUE); + + DedicatedGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + GpuDialog, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(DedicatedGraphHandle, TRUE); + + SharedGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + GpuDialog, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(SharedGraphHandle, TRUE); +} + +VOID EtpLayoutGpuGraphs( + VOID + ) +{ + RECT clientRect; + RECT labelRect; + ULONG graphWidth; + ULONG graphHeight; + HDWP deferHandle; + ULONG y; + + GetClientRect(GpuDialog, &clientRect); + GetClientRect(GetDlgItem(GpuDialog, IDC_GPU_L), &labelRect); + graphWidth = clientRect.right - GpuGraphMargin.left - GpuGraphMargin.right; + graphHeight = (clientRect.bottom - GpuGraphMargin.top - GpuGraphMargin.bottom - labelRect.bottom * 3 - ET_GPU_PADDING * 5) / 3; + + deferHandle = BeginDeferWindowPos(6); + y = GpuGraphMargin.top; + + deferHandle = DeferWindowPos( + deferHandle, + GetDlgItem(GpuDialog, IDC_GPU_L), + NULL, + GpuGraphMargin.left, + y, + 0, + 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER + ); + y += labelRect.bottom + ET_GPU_PADDING; + + deferHandle = DeferWindowPos( + deferHandle, + GpuGraphHandle, + NULL, + GpuGraphMargin.left, + y, + graphWidth, + graphHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + y += graphHeight + ET_GPU_PADDING; + + deferHandle = DeferWindowPos( + deferHandle, + GetDlgItem(GpuDialog, IDC_DEDICATED_L), + NULL, + GpuGraphMargin.left, + y, + 0, + 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER + ); + y += labelRect.bottom + ET_GPU_PADDING; + + deferHandle = DeferWindowPos( + deferHandle, + DedicatedGraphHandle, + NULL, + GpuGraphMargin.left, + y, + graphWidth, + graphHeight, + SWP_NOACTIVATE | SWP_NOZORDER + ); + y += graphHeight + ET_GPU_PADDING; + + deferHandle = DeferWindowPos( + deferHandle, + GetDlgItem(GpuDialog, IDC_SHARED_L), + NULL, + GpuGraphMargin.left, + y, + 0, + 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER + ); + y += labelRect.bottom + ET_GPU_PADDING; + + deferHandle = DeferWindowPos( + deferHandle, + SharedGraphHandle, + NULL, + GpuGraphMargin.left, + y, + graphWidth, + clientRect.bottom - GpuGraphMargin.bottom - y, + SWP_NOACTIVATE | SWP_NOZORDER + ); + + EndDeferWindowPos(deferHandle); +} + +VOID EtpNotifyGpuGraph( + _In_ NMHDR *Header + ) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + GpuSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); + + PhGraphStateGetDrawInfo( + &GpuGraphState, + getDrawInfo, + EtGpuNodeHistory.Count + ); + + if (!GpuGraphState.Valid) + { + PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, GpuGraphState.Data1, drawInfo->LineDataCount); + GpuGraphState.Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GpuGraphState.TooltipIndex != getTooltipText->Index) + { + FLOAT gpu; + + gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodeHistory, getTooltipText->Index); + + PhMoveReference(&GpuGraphState.TooltipText, PhFormatString( + L"%.2f%%%s\n%s", + gpu * 100, + PhGetStringOrEmpty(EtpGetMaxNodeString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = GpuGraphState.TooltipText->sr; + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record; + + record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = EtpReferenceMaxNodeRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(GpuDialog, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +VOID EtpNotifyDedicatedGraph( + _In_ NMHDR *Header + ) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + ULONG i; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + GpuSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); + + PhGraphStateGetDrawInfo( + &DedicatedGraphState, + getDrawInfo, + EtGpuDedicatedHistory.Count + ); + + if (!DedicatedGraphState.Valid) + { + for (i = 0; i < drawInfo->LineDataCount; i++) + { + DedicatedGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG64(&EtGpuDedicatedHistory, i); + } + + if (EtGpuDedicatedLimit != 0) + { + // Scale the data. + PhDivideSinglesBySingle( + DedicatedGraphState.Data1, + (FLOAT)EtGpuDedicatedLimit, + drawInfo->LineDataCount + ); + } + + DedicatedGraphState.Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (DedicatedGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG64 usedPages; + + usedPages = PhGetItemCircularBuffer_ULONG64(&EtGpuDedicatedHistory, getTooltipText->Index); + + PhMoveReference(&DedicatedGraphState.TooltipText, PhFormatString( + L"Dedicated Memory: %s\n%s", + PhaFormatSize(usedPages, ULONG_MAX)->Buffer, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = DedicatedGraphState.TooltipText->sr; + } + } + break; + } +} + +VOID EtpNotifySharedGraph( + _In_ NMHDR *Header + ) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + ULONG i; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + GpuSection->Parameters->ColorSetupFunction(drawInfo, PhGetIntegerSetting(L"ColorPhysical"), 0); + + PhGraphStateGetDrawInfo( + &SharedGraphState, + getDrawInfo, + EtGpuSharedHistory.Count + ); + + if (!SharedGraphState.Valid) + { + for (i = 0; i < drawInfo->LineDataCount; i++) + { + SharedGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG64(&EtGpuSharedHistory, i); + } + + if (EtGpuSharedLimit != 0) + { + // Scale the data. + PhDivideSinglesBySingle( + SharedGraphState.Data1, + (FLOAT)EtGpuSharedLimit, + drawInfo->LineDataCount + ); + } + + SharedGraphState.Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (SharedGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG64 usedPages; + + usedPages = PhGetItemCircularBuffer_ULONG64(&EtGpuSharedHistory, getTooltipText->Index); + + PhMoveReference(&SharedGraphState.TooltipText, PhFormatString( + L"Shared Memory: %s\n%s", + PhaFormatSize(usedPages, ULONG_MAX)->Buffer, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = SharedGraphState.TooltipText->sr; + } + } + break; + } +} + +VOID EtpUpdateGpuGraphs( + VOID + ) +{ + GpuGraphState.Valid = FALSE; + GpuGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(GpuGraphHandle, 1); + Graph_Draw(GpuGraphHandle); + Graph_UpdateTooltip(GpuGraphHandle); + InvalidateRect(GpuGraphHandle, NULL, FALSE); + + DedicatedGraphState.Valid = FALSE; + DedicatedGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(DedicatedGraphHandle, 1); + Graph_Draw(DedicatedGraphHandle); + Graph_UpdateTooltip(DedicatedGraphHandle); + InvalidateRect(DedicatedGraphHandle, NULL, FALSE); + + SharedGraphState.Valid = FALSE; + SharedGraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(SharedGraphHandle, 1); + Graph_Draw(SharedGraphHandle); + Graph_UpdateTooltip(SharedGraphHandle); + InvalidateRect(SharedGraphHandle, NULL, FALSE); +} + +VOID EtpUpdateGpuPanel( + VOID + ) +{ + PhSetDialogItemText(GpuPanel, IDC_ZDEDICATEDCURRENT_V, PhaFormatSize(EtGpuDedicatedUsage, ULONG_MAX)->Buffer); + PhSetDialogItemText(GpuPanel, IDC_ZDEDICATEDLIMIT_V, PhaFormatSize(EtGpuDedicatedLimit, ULONG_MAX)->Buffer); + PhSetDialogItemText(GpuPanel, IDC_ZSHAREDCURRENT_V, PhaFormatSize(EtGpuSharedUsage, ULONG_MAX)->Buffer); + PhSetDialogItemText(GpuPanel, IDC_ZSHAREDLIMIT_V, PhaFormatSize(EtGpuSharedLimit, ULONG_MAX)->Buffer); +} + +PPH_PROCESS_RECORD EtpReferenceMaxNodeRecord( + _In_ LONG Index + ) +{ + LARGE_INTEGER time; + ULONG maxProcessId; + + maxProcessId = PhGetItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, Index); + + if (!maxProcessId) + return NULL; + + PhGetStatisticsTime(NULL, Index, &time); + time.QuadPart += PH_TICKS_PER_SEC - 1; + + return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); +} + +PPH_STRING EtpGetMaxNodeString( + _In_ LONG Index + ) +{ + PPH_PROCESS_RECORD maxProcessRecord; + FLOAT maxGpuUsage; + PPH_STRING maxUsageString = NULL; + + if (maxProcessRecord = EtpReferenceMaxNodeRecord(Index)) + { + maxGpuUsage = PhGetItemCircularBuffer_FLOAT(&EtMaxGpuNodeUsageHistory, Index); + + maxUsageString = PhaFormatString( + L"\n%s (%lu): %.2f%%", + maxProcessRecord->ProcessName->Buffer, + HandleToUlong(maxProcessRecord->ProcessId), + maxGpuUsage * 100 + ); + + PhDereferenceProcessRecord(maxProcessRecord); + } + + return maxUsageString; +} + +PPH_STRING EtpGetGpuNameString( + VOID + ) +{ + ULONG i; + ULONG count; + PH_STRING_BUILDER sb; + + count = EtGetGpuAdapterCount(); + PhInitializeStringBuilder(&sb, 100); + + for (i = 0; i < count; i++) + { + PPH_STRING description; + + description = EtGetGpuAdapterDescription(i); + + if (!PhIsNullOrEmptyString(description)) + { + // Ignore "Microsoft Basic Render Driver" unless we don't have any other adapters. + // This does not take into account localization. + if (count == 1 || !PhEqualString2(description, L"Microsoft Basic Render Driver", TRUE)) + { + PhAppendStringBuilder(&sb, &description->sr); + PhAppendStringBuilder2(&sb, L", "); + } + } + + if (description) + PhDereferenceObject(description); + } + + if (sb.String->Length != 0) + PhRemoveEndStringBuilder(&sb, 2); + + return PhFinalStringBuilderString(&sb); +} diff --git a/plugins/ExtendedTools/gpusys.h b/plugins/ExtendedTools/gpusys.h index 6162fd142079..9cc138a506e1 100644 --- a/plugins/ExtendedTools/gpusys.h +++ b/plugins/ExtendedTools/gpusys.h @@ -1,79 +1,79 @@ -#ifndef GPUSYS_H -#define GPUSYS_H - -#define ET_GPU_PADDING 3 - -BOOLEAN EtpGpuSysInfoSectionCallback( - _In_ PPH_SYSINFO_SECTION Section, - _In_ PH_SYSINFO_SECTION_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2 - ); - -VOID EtpInitializeGpuDialog( - VOID - ); - -VOID EtpUninitializeGpuDialog( - VOID - ); - -VOID EtpTickGpuDialog( - VOID - ); - -INT_PTR CALLBACK EtpGpuDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK EtpGpuPanelDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtpCreateGpuGraphs( - VOID - ); - -VOID EtpLayoutGpuGraphs( - VOID - ); - -VOID EtpNotifyGpuGraph( - _In_ NMHDR *Header - ); - -VOID EtpNotifyDedicatedGraph( - _In_ NMHDR *Header - ); - -VOID EtpNotifySharedGraph( - _In_ NMHDR *Header - ); - -VOID EtpUpdateGpuGraphs( - VOID - ); - -VOID EtpUpdateGpuPanel( - VOID - ); - -PPH_PROCESS_RECORD EtpReferenceMaxNodeRecord( - _In_ LONG Index - ); - -PPH_STRING EtpGetMaxNodeString( - _In_ LONG Index - ); - -PPH_STRING EtpGetGpuNameString( - VOID - ); - -#endif +#ifndef GPUSYS_H +#define GPUSYS_H + +#define ET_GPU_PADDING 3 + +BOOLEAN EtpGpuSysInfoSectionCallback( + _In_ PPH_SYSINFO_SECTION Section, + _In_ PH_SYSINFO_SECTION_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2 + ); + +VOID EtpInitializeGpuDialog( + VOID + ); + +VOID EtpUninitializeGpuDialog( + VOID + ); + +VOID EtpTickGpuDialog( + VOID + ); + +INT_PTR CALLBACK EtpGpuDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK EtpGpuPanelDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EtpCreateGpuGraphs( + VOID + ); + +VOID EtpLayoutGpuGraphs( + VOID + ); + +VOID EtpNotifyGpuGraph( + _In_ NMHDR *Header + ); + +VOID EtpNotifyDedicatedGraph( + _In_ NMHDR *Header + ); + +VOID EtpNotifySharedGraph( + _In_ NMHDR *Header + ); + +VOID EtpUpdateGpuGraphs( + VOID + ); + +VOID EtpUpdateGpuPanel( + VOID + ); + +PPH_PROCESS_RECORD EtpReferenceMaxNodeRecord( + _In_ LONG Index + ); + +PPH_STRING EtpGetMaxNodeString( + _In_ LONG Index + ); + +PPH_STRING EtpGetGpuNameString( + VOID + ); + +#endif diff --git a/plugins/ExtendedTools/iconext.c b/plugins/ExtendedTools/iconext.c index ce4d0c5ec807..a863806317bf 100644 --- a/plugins/ExtendedTools/iconext.c +++ b/plugins/ExtendedTools/iconext.c @@ -1,466 +1,1203 @@ -/* - * Process Hacker Extended Tools - - * notification icon extensions - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -#define GPU_ICON_ID 1 -#define DISK_ICON_ID 2 -#define NETWORK_ICON_ID 3 - -VOID EtpGpuIconUpdateCallback( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ); - -BOOLEAN EtpGpuIconMessageCallback( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ); - -VOID EtpDiskIconUpdateCallback( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ); - -BOOLEAN EtpDiskIconMessageCallback( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ); - -VOID EtpNetworkIconUpdateCallback( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ); - -BOOLEAN EtpNetworkIconMessageCallback( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ); - -VOID EtRegisterNotifyIcons( - VOID - ) -{ - PH_NF_ICON_REGISTRATION_DATA data; - - data.MessageCallback = NULL; - - data.UpdateCallback = EtpGpuIconUpdateCallback; - data.MessageCallback = EtpGpuIconMessageCallback; - PhPluginRegisterIcon( - PluginInstance, - GPU_ICON_ID, - NULL, - L"GPU history", - PH_NF_ICON_SHOW_MINIINFO | (EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), - &data - ); - - data.UpdateCallback = EtpDiskIconUpdateCallback; - data.MessageCallback = EtpDiskIconMessageCallback; - PhPluginRegisterIcon( - PluginInstance, - DISK_ICON_ID, - NULL, - L"Disk history", - PH_NF_ICON_SHOW_MINIINFO | (EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), - &data - ); - - data.UpdateCallback = EtpNetworkIconUpdateCallback; - data.MessageCallback = EtpNetworkIconMessageCallback; - PhPluginRegisterIcon( - PluginInstance, - NETWORK_ICON_ID, - NULL, - L"Network history", - PH_NF_ICON_SHOW_MINIINFO | (EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE), - &data - ); -} - -VOID EtpGpuIconUpdateCallback( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - 0, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HANDLE maxGpuProcessId; - PPH_PROCESS_ITEM maxGpuProcessItem; - PH_FORMAT format[8]; - - // Icon - - Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, EtGpuNodeHistory.Count); - PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, lineData1, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineColor1 = PhGetIntegerSetting(L"ColorCpuKernel"); - drawInfo.LineBackColor1 = PhHalveColorBrightness(drawInfo.LineColor1); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - *NewIconOrBitmap = bitmap; - *Flags = PH_NF_UPDATE_IS_BITMAP; - - // Text - - if (EtMaxGpuNodeHistory.Count != 0) - maxGpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, 0)); - else - maxGpuProcessId = NULL; - - if (maxGpuProcessId) - maxGpuProcessItem = PhReferenceProcessItem(maxGpuProcessId); - else - maxGpuProcessItem = NULL; - - PhInitFormatS(&format[0], L"GPU Usage: "); - PhInitFormatF(&format[1], EtGpuNodeUsage * 100, 2); - PhInitFormatC(&format[2], '%'); - - if (maxGpuProcessItem) - { - PhInitFormatC(&format[3], '\n'); - PhInitFormatSR(&format[4], maxGpuProcessItem->ProcessName->sr); - PhInitFormatS(&format[5], L": "); - PhInitFormatF(&format[6], EtGetProcessBlock(maxGpuProcessItem)->GpuNodeUsage * 100, 2); - PhInitFormatC(&format[7], '%'); - } - - *NewText = PhFormat(format, maxGpuProcessItem ? 8 : 3, 128); - if (maxGpuProcessItem) PhDereferenceObject(maxGpuProcessItem); -} - -BOOLEAN EtpGpuIconMessageCallback( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ) -{ - switch (LOWORD(LParam)) - { - case PH_NF_MSG_SHOWMINIINFOSECTION: - { - PPH_NF_MSG_SHOWMINIINFOSECTION_DATA data = (PVOID)WParam; - - data->SectionName = L"GPU"; - } - return TRUE; - } - - return FALSE; -} - -VOID EtpDiskIconUpdateCallback( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - PH_GRAPH_USE_LINE_2, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - PFLOAT lineData2; - FLOAT max; - ULONG i; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HANDLE maxDiskProcessId; - PPH_PROCESS_ITEM maxDiskProcessItem; - PH_FORMAT format[6]; - - // Icon - - Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - lineData2 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, EtDiskReadHistory.Count); - max = 1024 * 1024; // minimum scaling of 1 MB. - - for (i = 0; i < lineDataCount; i++) - { - lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); - lineData2[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); - - if (max < lineData1[i] + lineData2[i]) - max = lineData1[i] + lineData2[i]; - } - - PhDivideSinglesBySingle(lineData1, max, lineDataCount); - PhDivideSinglesBySingle(lineData2, max, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineData2 = lineData2; - drawInfo.LineColor1 = PhGetIntegerSetting(L"ColorIoReadOther"); - drawInfo.LineColor2 = PhGetIntegerSetting(L"ColorIoWrite"); - drawInfo.LineBackColor1 = PhHalveColorBrightness(drawInfo.LineColor1); - drawInfo.LineBackColor2 = PhHalveColorBrightness(drawInfo.LineColor2); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - *NewIconOrBitmap = bitmap; - *Flags = PH_NF_UPDATE_IS_BITMAP; - - // Text - - if (EtMaxDiskHistory.Count != 0) - maxDiskProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxDiskHistory, 0)); - else - maxDiskProcessId = NULL; - - if (maxDiskProcessId) - maxDiskProcessItem = PhReferenceProcessItem(maxDiskProcessId); - else - maxDiskProcessItem = NULL; - - PhInitFormatS(&format[0], L"Disk\nR: "); - PhInitFormatSize(&format[1], EtDiskReadDelta.Delta); - PhInitFormatS(&format[2], L"\nW: "); - PhInitFormatSize(&format[3], EtDiskWriteDelta.Delta); - - if (maxDiskProcessItem) - { - PhInitFormatC(&format[4], '\n'); - PhInitFormatSR(&format[5], maxDiskProcessItem->ProcessName->sr); - } - - *NewText = PhFormat(format, maxDiskProcessItem ? 6 : 4, 128); - if (maxDiskProcessItem) PhDereferenceObject(maxDiskProcessItem); -} - -BOOLEAN EtpDiskIconMessageCallback( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ) -{ - switch (LOWORD(LParam)) - { - case PH_NF_MSG_SHOWMINIINFOSECTION: - { - PPH_NF_MSG_SHOWMINIINFOSECTION_DATA data = (PVOID)WParam; - - data->SectionName = L"Disk"; - } - return TRUE; - } - - return FALSE; -} - -VOID EtpNetworkIconUpdateCallback( - _In_ struct _PH_NF_ICON *Icon, - _Out_ PVOID *NewIconOrBitmap, - _Out_ PULONG Flags, - _Out_ PPH_STRING *NewText, - _In_opt_ PVOID Context - ) -{ - static PH_GRAPH_DRAW_INFO drawInfo = - { - 16, - 16, - PH_GRAPH_USE_LINE_2, - 2, - RGB(0x00, 0x00, 0x00), - - 16, - NULL, - NULL, - 0, - 0, - 0, - 0 - }; - ULONG maxDataCount; - ULONG lineDataCount; - PFLOAT lineData1; - PFLOAT lineData2; - FLOAT max; - ULONG i; - HBITMAP bitmap; - PVOID bits; - HDC hdc; - HBITMAP oldBitmap; - HANDLE maxNetworkProcessId; - PPH_PROCESS_ITEM maxNetworkProcessItem; - PH_FORMAT format[6]; - - // Icon - - Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); - maxDataCount = drawInfo.Width / 2 + 1; - lineData1 = _alloca(maxDataCount * sizeof(FLOAT)); - lineData2 = _alloca(maxDataCount * sizeof(FLOAT)); - - lineDataCount = min(maxDataCount, EtNetworkReceiveHistory.Count); - max = 1024 * 1024; // minimum scaling of 1 MB. - - for (i = 0; i < lineDataCount; i++) - { - lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); - lineData2[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); - - if (max < lineData1[i] + lineData2[i]) - max = lineData1[i] + lineData2[i]; - } - - PhDivideSinglesBySingle(lineData1, max, lineDataCount); - PhDivideSinglesBySingle(lineData2, max, lineDataCount); - - drawInfo.LineDataCount = lineDataCount; - drawInfo.LineData1 = lineData1; - drawInfo.LineData2 = lineData2; - drawInfo.LineColor1 = PhGetIntegerSetting(L"ColorIoReadOther"); - drawInfo.LineColor2 = PhGetIntegerSetting(L"ColorIoWrite"); - drawInfo.LineBackColor1 = PhHalveColorBrightness(drawInfo.LineColor1); - drawInfo.LineBackColor2 = PhHalveColorBrightness(drawInfo.LineColor2); - - if (bits) - PhDrawGraphDirect(hdc, bits, &drawInfo); - - SelectObject(hdc, oldBitmap); - *NewIconOrBitmap = bitmap; - *Flags = PH_NF_UPDATE_IS_BITMAP; - - // Text - - if (EtMaxNetworkHistory.Count != 0) - maxNetworkProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxNetworkHistory, 0)); - else - maxNetworkProcessId = NULL; - - if (maxNetworkProcessId) - maxNetworkProcessItem = PhReferenceProcessItem(maxNetworkProcessId); - else - maxNetworkProcessItem = NULL; - - PhInitFormatS(&format[0], L"Network\nR: "); - PhInitFormatSize(&format[1], EtNetworkReceiveDelta.Delta); - PhInitFormatS(&format[2], L"\nS: "); - PhInitFormatSize(&format[3], EtNetworkSendDelta.Delta); - - if (maxNetworkProcessItem) - { - PhInitFormatC(&format[4], '\n'); - PhInitFormatSR(&format[5], maxNetworkProcessItem->ProcessName->sr); - } - - *NewText = PhFormat(format, maxNetworkProcessItem ? 6 : 4, 128); - if (maxNetworkProcessItem) PhDereferenceObject(maxNetworkProcessItem); -} - -BOOLEAN EtpNetworkIconMessageCallback( - _In_ struct _PH_NF_ICON *Icon, - _In_ ULONG_PTR WParam, - _In_ ULONG_PTR LParam, - _In_opt_ PVOID Context - ) -{ - switch (LOWORD(LParam)) - { - case PH_NF_MSG_SHOWMINIINFOSECTION: - { - PPH_NF_MSG_SHOWMINIINFOSECTION_DATA data = (PVOID)WParam; - - data->SectionName = L"Network"; - } - return TRUE; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * notification icon extensions + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2016-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include "etwsys.h" +#include "gpusys.h" +#include + +typedef enum _ETP_TRAY_ICON_ID +{ + ETP_TRAY_ICON_ID_NONE, + ETP_TRAY_ICON_ID_GPU, + ETP_TRAY_ICON_ID_DISK, + ETP_TRAY_ICON_ID_NETWORK, + ETP_TRAY_ICON_ID_GPUTEXT, + ETP_TRAY_ICON_ID_DISKTEXT, + ETP_TRAY_ICON_ID_NETWORKTEXT, + ETP_TRAY_ICON_ID_MAXIMUM +} ETP_TRAY_ICON_ID; + +typedef enum _ETP_TRAY_ICON_GUID +{ + ETP_TRAY_ICON_GUID_GPU, + ETP_TRAY_ICON_GUID_DISK, + ETP_TRAY_ICON_GUID_NETWORK, + ETP_TRAY_ICON_GUID_GPUTEXT, + ETP_TRAY_ICON_GUID_DISKTEXT, + ETP_TRAY_ICON_GUID_NETWORKTEXT, + ETP_TRAY_ICON_GUID_MAXIMUM +} ETP_TRAY_ICON_GUID; + +VOID EtpGpuIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +BOOLEAN EtpGpuIconMessageCallback( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ); + +VOID EtpDiskIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +BOOLEAN EtpDiskIconMessageCallback( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ); + +VOID EtpNetworkIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +BOOLEAN EtpNetworkIconMessageCallback( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ); + +VOID EtpGpuTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +VOID EtpDiskTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +VOID EtpNetworkTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ); + +GUID EtpTrayIconGuids[ETP_TRAY_ICON_GUID_MAXIMUM]; + +VOID EtLoadTrayIconGuids( + VOID + ) +{ + PPH_STRING settingsString = NULL; + PH_STRINGREF remaining; + ULONG i; + + settingsString = PhGetStringSetting(SETTING_NAME_TRAYICON_GUIDS); + + if (PhIsNullOrEmptyString(settingsString)) + { + PH_STRING_BUILDER iconListBuilder; + PPH_STRING iconGuid; + + PhInitializeStringBuilder(&iconListBuilder, 100); + + for (i = 0; i < RTL_NUMBER_OF(EtpTrayIconGuids); i++) + { + PhGenerateGuid(&EtpTrayIconGuids[i]); + + if (iconGuid = PhFormatGuid(&EtpTrayIconGuids[i])) + { + PhAppendFormatStringBuilder( + &iconListBuilder, + L"%s|", + iconGuid->Buffer + ); + PhDereferenceObject(iconGuid); + } + } + + if (iconListBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&iconListBuilder, 1); + + PhMoveReference(&settingsString, PhFinalStringBuilderString(&iconListBuilder)); + PhSetStringSetting2(SETTING_NAME_TRAYICON_GUIDS, &settingsString->sr); + PhDereferenceObject(settingsString); + } + else + { + remaining = PhGetStringRef(settingsString); + + for (i = 0; i < RTL_NUMBER_OF(EtpTrayIconGuids); i++) + { + PH_STRINGREF guidPart; + UNICODE_STRING guidStringUs; + GUID guid; + + if (remaining.Length == 0) + continue; + + PhSplitStringRefAtChar(&remaining, '|', &guidPart, &remaining); + + if (guidPart.Length == 0) + continue; + + if (!PhStringRefToUnicodeString(&guidPart, &guidStringUs)) + continue; + + if (!NT_SUCCESS(RtlGUIDFromString(&guidStringUs, &guid))) + PhGenerateGuid(&EtpTrayIconGuids[i]); + else + EtpTrayIconGuids[i] = guid; + } + + PhDereferenceObject(settingsString); + } +} + +VOID EtRegisterNotifyIcons( + _In_ PPH_TRAY_ICON_POINTERS Pointers + ) +{ + PH_NF_ICON_REGISTRATION_DATA data; + + data.MessageCallback = NULL; + + data.UpdateCallback = EtpGpuIconUpdateCallback; + data.MessageCallback = EtpGpuIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + ETP_TRAY_ICON_ID_GPU, + EtpTrayIconGuids[ETP_TRAY_ICON_GUID_GPU], + NULL, + L"&GPU history", + EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpDiskIconUpdateCallback; + data.MessageCallback = EtpDiskIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + ETP_TRAY_ICON_ID_DISK, + EtpTrayIconGuids[ETP_TRAY_ICON_GUID_DISK], + NULL, + L"&Disk history", + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpNetworkIconUpdateCallback; + data.MessageCallback = EtpNetworkIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + ETP_TRAY_ICON_ID_NETWORK, + EtpTrayIconGuids[ETP_TRAY_ICON_GUID_NETWORK], + NULL, + L"&Network history", + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpGpuTextIconUpdateCallback; + data.MessageCallback = EtpGpuIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + ETP_TRAY_ICON_ID_GPUTEXT, + EtpTrayIconGuids[ETP_TRAY_ICON_GUID_GPUTEXT], + NULL, + L"&GPU usage (Text)", + EtGpuEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpDiskTextIconUpdateCallback; + data.MessageCallback = EtpDiskIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + ETP_TRAY_ICON_ID_DISKTEXT, + EtpTrayIconGuids[ETP_TRAY_ICON_GUID_DISKTEXT], + NULL, + L"&Disk usage (Text)", + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); + + data.UpdateCallback = EtpNetworkTextIconUpdateCallback; + data.MessageCallback = EtpNetworkIconMessageCallback; + Pointers->RegisterTrayIcon( + PluginInstance, + ETP_TRAY_ICON_ID_NETWORKTEXT, + EtpTrayIconGuids[ETP_TRAY_ICON_GUID_NETWORKTEXT], + NULL, + L"&Network usage (Text)", + EtEtwEnabled ? 0 : PH_NF_ICON_UNAVAILABLE, + &data + ); +} + +VOID EtpGpuIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 2, + RGB(0x00, 0x00, 0x00), + + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxGpuProcessId; + PPH_PROCESS_ITEM maxGpuProcessItem; + PH_FORMAT format[8]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, EtGpuNodeHistory.Count); + PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, lineData1, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineColor1 = PhGetIntegerSetting(L"ColorCpuKernel"); + drawInfo.LineBackColor1 = PhHalveColorBrightness(drawInfo.LineColor1); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (EtMaxGpuNodeHistory.Count != 0) + maxGpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, 0)); + else + maxGpuProcessId = NULL; + + if (maxGpuProcessId) + maxGpuProcessItem = PhReferenceProcessItem(maxGpuProcessId); + else + maxGpuProcessItem = NULL; + + PhInitFormatS(&format[0], L"GPU Usage: "); + PhInitFormatF(&format[1], EtGpuNodeUsage * 100, 2); + PhInitFormatC(&format[2], '%'); + + if (maxGpuProcessItem) + { + PhInitFormatC(&format[3], '\n'); + PhInitFormatSR(&format[4], maxGpuProcessItem->ProcessName->sr); + PhInitFormatS(&format[5], L": "); + PhInitFormatF(&format[6], EtGetProcessBlock(maxGpuProcessItem)->GpuNodeUsage * 100, 2); + PhInitFormatC(&format[7], '%'); + } + + *NewText = PhFormat(format, maxGpuProcessItem ? 8 : 3, 128); + if (maxGpuProcessItem) PhDereferenceObject(maxGpuProcessItem); + + _freea(lineData1); +} + +BOOLEAN EtpGpuIconMessageCallback( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ) +{ + switch (LOWORD(LParam)) + { + case PH_NF_MSG_SHOWMINIINFOSECTION: + { + PPH_NF_MSG_SHOWMINIINFOSECTION_DATA data = (PVOID)WParam; + + data->SectionName = L"GPU"; + } + return TRUE; + } + + return FALSE; +} + +VOID EtpDiskIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + PH_GRAPH_USE_LINE_2, + 2, + RGB(0x00, 0x00, 0x00), + + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + PFLOAT lineData2; + FLOAT max; + ULONG i; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxDiskProcessId; + PPH_PROCESS_ITEM maxDiskProcessItem; + PH_FORMAT format[6]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + lineData2 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, EtDiskReadHistory.Count); + max = 1024 * 1024; // minimum scaling of 1 MB. + + for (i = 0; i < lineDataCount; i++) + { + lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); + lineData2[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); + + if (max < lineData1[i] + lineData2[i]) + max = lineData1[i] + lineData2[i]; + } + + PhDivideSinglesBySingle(lineData1, max, lineDataCount); + PhDivideSinglesBySingle(lineData2, max, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineData2 = lineData2; + drawInfo.LineColor1 = PhGetIntegerSetting(L"ColorIoReadOther"); + drawInfo.LineColor2 = PhGetIntegerSetting(L"ColorIoWrite"); + drawInfo.LineBackColor1 = PhHalveColorBrightness(drawInfo.LineColor1); + drawInfo.LineBackColor2 = PhHalveColorBrightness(drawInfo.LineColor2); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (EtMaxDiskHistory.Count != 0) + maxDiskProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxDiskHistory, 0)); + else + maxDiskProcessId = NULL; + + if (maxDiskProcessId) + maxDiskProcessItem = PhReferenceProcessItem(maxDiskProcessId); + else + maxDiskProcessItem = NULL; + + PhInitFormatS(&format[0], L"Disk\nR: "); + PhInitFormatSize(&format[1], EtDiskReadDelta.Delta); + PhInitFormatS(&format[2], L"\nW: "); + PhInitFormatSize(&format[3], EtDiskWriteDelta.Delta); + + if (maxDiskProcessItem) + { + PhInitFormatC(&format[4], '\n'); + PhInitFormatSR(&format[5], maxDiskProcessItem->ProcessName->sr); + } + + *NewText = PhFormat(format, maxDiskProcessItem ? 6 : 4, 128); + if (maxDiskProcessItem) PhDereferenceObject(maxDiskProcessItem); + + _freea(lineData2); + _freea(lineData1); +} + +BOOLEAN EtpDiskIconMessageCallback( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ) +{ + switch (LOWORD(LParam)) + { + case PH_NF_MSG_SHOWMINIINFOSECTION: + { + PPH_NF_MSG_SHOWMINIINFOSECTION_DATA data = (PVOID)WParam; + + data->SectionName = L"Disk"; + } + return TRUE; + } + + return FALSE; +} + +VOID EtpNetworkIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + PH_GRAPH_USE_LINE_2, + 2, + RGB(0x00, 0x00, 0x00), + + 16, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + ULONG maxDataCount; + ULONG lineDataCount; + PFLOAT lineData1; + PFLOAT lineData2; + FLOAT max; + ULONG i; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxNetworkProcessId; + PPH_PROCESS_ITEM maxNetworkProcessItem; + PH_FORMAT format[6]; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + maxDataCount = drawInfo.Width / 2 + 1; + lineData1 = _malloca(maxDataCount * sizeof(FLOAT)); + lineData2 = _malloca(maxDataCount * sizeof(FLOAT)); + + lineDataCount = min(maxDataCount, EtNetworkReceiveHistory.Count); + max = 1024 * 1024; // minimum scaling of 1 MB. + + for (i = 0; i < lineDataCount; i++) + { + lineData1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); + lineData2[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); + + if (max < lineData1[i] + lineData2[i]) + max = lineData1[i] + lineData2[i]; + } + + PhDivideSinglesBySingle(lineData1, max, lineDataCount); + PhDivideSinglesBySingle(lineData2, max, lineDataCount); + + drawInfo.LineDataCount = lineDataCount; + drawInfo.LineData1 = lineData1; + drawInfo.LineData2 = lineData2; + drawInfo.LineColor1 = PhGetIntegerSetting(L"ColorIoReadOther"); + drawInfo.LineColor2 = PhGetIntegerSetting(L"ColorIoWrite"); + drawInfo.LineBackColor1 = PhHalveColorBrightness(drawInfo.LineColor1); + drawInfo.LineBackColor2 = PhHalveColorBrightness(drawInfo.LineColor2); + + if (bits) + PhDrawGraphDirect(hdc, bits, &drawInfo); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (EtMaxNetworkHistory.Count != 0) + maxNetworkProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxNetworkHistory, 0)); + else + maxNetworkProcessId = NULL; + + if (maxNetworkProcessId) + maxNetworkProcessItem = PhReferenceProcessItem(maxNetworkProcessId); + else + maxNetworkProcessItem = NULL; + + PhInitFormatS(&format[0], L"Network\nR: "); + PhInitFormatSize(&format[1], EtNetworkReceiveDelta.Delta); + PhInitFormatS(&format[2], L"\nS: "); + PhInitFormatSize(&format[3], EtNetworkSendDelta.Delta); + + if (maxNetworkProcessItem) + { + PhInitFormatC(&format[4], '\n'); + PhInitFormatSR(&format[5], maxNetworkProcessItem->ProcessName->sr); + } + + *NewText = PhFormat(format, maxNetworkProcessItem ? 6 : 4, 128); + if (maxNetworkProcessItem) PhDereferenceObject(maxNetworkProcessItem); + + _freea(lineData2); + _freea(lineData1); +} + +BOOLEAN EtpNetworkIconMessageCallback( + _In_ struct _PH_NF_ICON *Icon, + _In_ ULONG_PTR WParam, + _In_ ULONG_PTR LParam, + _In_opt_ PVOID Context + ) +{ + switch (LOWORD(LParam)) + { + case PH_NF_MSG_SHOWMINIINFOSECTION: + { + PPH_NF_MSG_SHOWMINIINFOSECTION_DATA data = (PVOID)WParam; + + data->SectionName = L"Network"; + } + return TRUE; + } + + return FALSE; +} + +// Text + +VOID EtpGpuTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 0, + RGB(0x00, 0x00, 0x00), + 0, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxGpuProcessId; + PPH_PROCESS_ITEM maxGpuProcessItem; + PH_FORMAT format[8]; + PPH_STRING text; + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + PhInitFormatF(&format[0], (FLOAT)EtGpuNodeUsage * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhGetIntegerSetting(L"ColorCpuKernel"); + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (EtMaxGpuNodeHistory.Count != 0) + maxGpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxGpuNodeHistory, 0)); + else + maxGpuProcessId = NULL; + + if (maxGpuProcessId) + maxGpuProcessItem = PhReferenceProcessItem(maxGpuProcessId); + else + maxGpuProcessItem = NULL; + + PhInitFormatS(&format[0], L"GPU Usage: "); + PhInitFormatF(&format[1], EtGpuNodeUsage * 100, 2); + PhInitFormatC(&format[2], '%'); + + if (maxGpuProcessItem) + { + PhInitFormatC(&format[3], '\n'); + PhInitFormatSR(&format[4], maxGpuProcessItem->ProcessName->sr); + PhInitFormatS(&format[5], L": "); + PhInitFormatF(&format[6], EtGetProcessBlock(maxGpuProcessItem)->GpuNodeUsage * 100, 2); + PhInitFormatC(&format[7], '%'); + } + + *NewText = PhFormat(format, maxGpuProcessItem ? 8 : 3, 128); + if (maxGpuProcessItem) PhDereferenceObject(maxGpuProcessItem); +} + +VOID EtpDiskTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 0, + RGB(0x00, 0x00, 0x00), + 0, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxDiskProcessId; + PPH_PROCESS_ITEM maxDiskProcessItem; + PH_FORMAT format[6]; + PPH_STRING text; + static ULONG64 maxValue = 100000 * 1024; // minimum scaling of 100 MB. + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + if (maxValue < (EtDiskReadDelta.Delta + EtDiskWriteDelta.Delta)) + maxValue = (EtDiskReadDelta.Delta + EtDiskWriteDelta.Delta); + + PhInitFormatF(&format[0], (FLOAT)(EtDiskReadDelta.Delta + EtDiskWriteDelta.Delta) / maxValue * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhGetIntegerSetting(L"ColorIoReadOther"); // ColorIoWrite + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (EtMaxDiskHistory.Count != 0) + maxDiskProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxDiskHistory, 0)); + else + maxDiskProcessId = NULL; + + if (maxDiskProcessId) + maxDiskProcessItem = PhReferenceProcessItem(maxDiskProcessId); + else + maxDiskProcessItem = NULL; + + PhInitFormatS(&format[0], L"Disk\nR: "); + PhInitFormatSize(&format[1], EtDiskReadDelta.Delta); + PhInitFormatS(&format[2], L"\nW: "); + PhInitFormatSize(&format[3], EtDiskWriteDelta.Delta); + + if (maxDiskProcessItem) + { + PhInitFormatC(&format[4], '\n'); + PhInitFormatSR(&format[5], maxDiskProcessItem->ProcessName->sr); + } + + *NewText = PhFormat(format, maxDiskProcessItem ? 6 : 4, 128); + if (maxDiskProcessItem) PhDereferenceObject(maxDiskProcessItem); +} + +VOID EtpNetworkTextIconUpdateCallback( + _In_ struct _PH_NF_ICON *Icon, + _Out_ PVOID *NewIconOrBitmap, + _Out_ PULONG Flags, + _Out_ PPH_STRING *NewText, + _In_opt_ PVOID Context + ) +{ + static PH_GRAPH_DRAW_INFO drawInfo = + { + 16, + 16, + 0, + 0, + RGB(0x00, 0x00, 0x00), + 0, + NULL, + NULL, + 0, + 0, + 0, + 0 + }; + HBITMAP bitmap; + PVOID bits; + HDC hdc; + HBITMAP oldBitmap; + HANDLE maxNetworkProcessId; + PPH_PROCESS_ITEM maxNetworkProcessItem; + PH_FORMAT format[6]; + PPH_STRING text; + static ULONG64 maxValue = 1024 * 1024; // minimum scaling of 1 MB. + + // Icon + + Icon->Pointers->BeginBitmap(&drawInfo.Width, &drawInfo.Height, &bitmap, &bits, &hdc, &oldBitmap); + + if (maxValue < (EtNetworkReceiveDelta.Delta + EtNetworkSendDelta.Delta)) + maxValue = (EtNetworkReceiveDelta.Delta + EtNetworkSendDelta.Delta); + + PhInitFormatF(&format[0], (FLOAT)(EtNetworkReceiveDelta.Delta + EtNetworkSendDelta.Delta) / maxValue * 100, 0); + text = PhFormat(format, 1, 10); + + drawInfo.TextColor = PhGetIntegerSetting(L"ColorIoReadOther"); // ColorIoWrite + PhDrawTrayIconText(hdc, bits, &drawInfo, &text->sr); + PhDereferenceObject(text); + + SelectBitmap(hdc, oldBitmap); + *NewIconOrBitmap = bitmap; + *Flags = PH_NF_UPDATE_IS_BITMAP; + + // Text + + if (EtMaxNetworkHistory.Count != 0) + maxNetworkProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&EtMaxNetworkHistory, 0)); + else + maxNetworkProcessId = NULL; + + if (maxNetworkProcessId) + maxNetworkProcessItem = PhReferenceProcessItem(maxNetworkProcessId); + else + maxNetworkProcessItem = NULL; + + PhInitFormatS(&format[0], L"Network\nR: "); + PhInitFormatSize(&format[1], EtNetworkReceiveDelta.Delta); + PhInitFormatS(&format[2], L"\nS: "); + PhInitFormatSize(&format[3], EtNetworkSendDelta.Delta); + + if (maxNetworkProcessItem) + { + PhInitFormatC(&format[4], '\n'); + PhInitFormatSR(&format[5], maxNetworkProcessItem->ProcessName->sr); + } + + *NewText = PhFormat(format, maxNetworkProcessItem ? 6 : 4, 128); + if (maxNetworkProcessItem) PhDereferenceObject(maxNetworkProcessItem); +} + +// Toolbar graphs + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(EtpToolbarGpuHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); + + if (ProcessesUpdatedCount <= 2) + return; + + PhGraphStateGetDrawInfo(GraphState, getDrawInfo, EtGpuNodeHistory.Count); + + if (!GraphState->Valid) + { + PhCopyCircularBuffer_FLOAT(&EtGpuNodeHistory, GraphState->Data1, drawInfo->LineDataCount); + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + FLOAT gpu; + + gpu = PhGetItemCircularBuffer_FLOAT(&EtGpuNodeHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"%.2f%%%s\n%s", + gpu * 100, + PhGetStringOrEmpty(EtpGetMaxNodeString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record; + + record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = EtpReferenceMaxNodeRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(PhMainWndHandle, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(EtpToolbarDiskHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_LINE_2; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + + if (ProcessesUpdatedCount <= 2) + return; + + PhGraphStateGetDrawInfo( + GraphState, + getDrawInfo, + EtDiskReadHistory.Count + ); + + if (!GraphState->Valid) + { + ULONG i; + FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + GraphState->Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, i); + GraphState->Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + GraphState->Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + GraphState->Data2, + max, + drawInfo->LineDataCount + ); + } + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + ULONG64 diskRead; + ULONG64 diskWrite; + + diskRead = PhGetItemCircularBuffer_ULONG(&EtDiskReadHistory, getTooltipText->Index); + diskWrite = PhGetItemCircularBuffer_ULONG(&EtDiskWriteHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"R: %s\nW: %s%s\n%s", + PhaFormatSize(diskRead, ULONG_MAX)->Buffer, + PhaFormatSize(diskWrite, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(EtpGetMaxDiskString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record; + + record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = EtpReferenceMaxDiskRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(PhMainWndHandle, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(EtpToolbarNetworkHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_LINE_2; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + + if (ProcessesUpdatedCount <= 2) + return; + + PhGraphStateGetDrawInfo( + GraphState, + getDrawInfo, + EtNetworkReceiveHistory.Count + ); + + if (!GraphState->Valid) + { + ULONG i; + FLOAT max = 1024 * 1024; // Minimum scaling of 1 MB + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + FLOAT data2; + + GraphState->Data1[i] = data1 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, i); + GraphState->Data2[i] = data2 = + (FLOAT)PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, i); + + if (max < data1 + data2) + max = data1 + data2; + } + + if (max != 0) + { + // Scale the data. + + PhDivideSinglesBySingle( + GraphState->Data1, + max, + drawInfo->LineDataCount + ); + PhDivideSinglesBySingle( + GraphState->Data2, + max, + drawInfo->LineDataCount + ); + } + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + ULONG64 networkReceive; + ULONG64 networkSend; + + networkReceive = PhGetItemCircularBuffer_ULONG(&EtNetworkReceiveHistory, getTooltipText->Index); + networkSend = PhGetItemCircularBuffer_ULONG(&EtNetworkSendHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"R: %s\nS: %s%s\n%s", + PhaFormatSize(networkReceive, ULONG_MAX)->Buffer, + PhaFormatSize(networkSend, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(EtpGetMaxNetworkString(getTooltipText->Index)), + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record; + + record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = EtpReferenceMaxNetworkRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(PhMainWndHandle, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +VOID EtRegisterToolbarGraphs( + VOID + ) +{ + PPH_PLUGIN toolStatusPlugin; + PTOOLSTATUS_INTERFACE ToolStatusInterface = NULL; + + if (toolStatusPlugin = PhFindPlugin(TOOLSTATUS_PLUGIN_NAME)) + { + ToolStatusInterface = PhGetPluginInformation(toolStatusPlugin)->Interface; + + if (ToolStatusInterface->Version < TOOLSTATUS_INTERFACE_VERSION) + ToolStatusInterface = NULL; + } + + if (ToolStatusInterface) + { + ToolStatusInterface->RegisterToolbarGraph( + PluginInstance, + 5, + L"GPU history", + EtGpuEnabled ? 0 : TOOLSTATUS_GRAPH_UNAVAILABLE, + NULL, + EtpToolbarGpuHistoryGraphMessageCallback + ); + + ToolStatusInterface->RegisterToolbarGraph( + PluginInstance, + 6, + L"Disk history", + EtEtwEnabled ? 0 : TOOLSTATUS_GRAPH_UNAVAILABLE, + NULL, + EtpToolbarDiskHistoryGraphMessageCallback + ); + + ToolStatusInterface->RegisterToolbarGraph( + PluginInstance, + 7, + L"Network history", + EtEtwEnabled ? 0 : TOOLSTATUS_GRAPH_UNAVAILABLE, + NULL, + EtpToolbarNetworkHistoryGraphMessageCallback + ); + } +} diff --git a/plugins/ExtendedTools/main.c b/plugins/ExtendedTools/main.c index 4bf9c82f8661..9442acb41e0a 100644 --- a/plugins/ExtendedTools/main.c +++ b/plugins/ExtendedTools/main.c @@ -1,617 +1,815 @@ -/* - * Process Hacker Extended Tools - - * main program - * - * Copyright (C) 2010-2015 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -PPH_PLUGIN PluginInstance; -LIST_ENTRY EtProcessBlockListHead; -LIST_ENTRY EtNetworkBlockListHead; -HWND ProcessTreeNewHandle; -HWND NetworkTreeNewHandle; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginTreeNewMessageCallbackRegistration; -PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION HandlePropertiesInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ModuleMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION SystemInformationInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION MiniInformationInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -PH_CALLBACK_REGISTRATION NetworkItemsUpdatedCallbackRegistration; - -static HANDLE ModuleProcessId; - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EtEtwStatisticsInitialization(); - EtGpuMonitorInitialization(); - - EtRegisterNotifyIcons(); -} - -VOID NTAPI UnloadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EtSaveSettingsDiskTreeList(); - EtEtwStatisticsUninitialization(); -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EtShowOptionsDialog((HWND)Parameter); -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case ID_PROCESS_UNLOADEDMODULES: - { - EtShowUnloadedDllsDialog(PhMainWndHandle, menuItem->Context); - } - break; - case ID_PROCESS_WSWATCH: - { - EtShowWsWatchDialog(PhMainWndHandle, menuItem->Context); - } - break; - case ID_THREAD_CANCELIO: - { - EtUiCancelIoThread(menuItem->OwnerWindow, menuItem->Context); - } - break; - case ID_MODULE_SERVICES: - { - EtShowModuleServicesDialog( - menuItem->OwnerWindow, - ModuleProcessId, - ((PPH_MODULE_ITEM)menuItem->Context)->Name->Buffer - ); - } - break; - } -} - -VOID NTAPI TreeNewMessageCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - - if (message->TreeNewHandle == ProcessTreeNewHandle) - EtProcessTreeNewMessage(Parameter); - else if (message->TreeNewHandle == NetworkTreeNewHandle) - EtNetworkTreeNewMessage(Parameter); -} - -VOID NTAPI MainWindowShowingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EtInitializeDiskTab(); -} - -VOID NTAPI ProcessPropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EtProcessGpuPropertiesInitializing(Parameter); - EtProcessEtwPropertiesInitializing(Parameter); -} - -VOID NTAPI HandlePropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - EtHandlePropertiesInitializing(Parameter); -} - -VOID NTAPI ProcessMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_PROCESS_ITEM processItem; - ULONG flags; - PPH_EMENU_ITEM miscMenu; - - if (menuInfo->u.Process.NumberOfProcesses == 1) - processItem = menuInfo->u.Process.Processes[0]; - else - processItem = NULL; - - flags = 0; - - if (!processItem) - flags = PH_EMENU_DISABLED; - - miscMenu = PhFindEMenuItem(menuInfo->Menu, 0, L"Miscellaneous", 0); - - if (miscMenu) - { - PhInsertEMenuItem(miscMenu, PhPluginCreateEMenuItem(PluginInstance, flags, ID_PROCESS_UNLOADEDMODULES, L"Unloaded modules", processItem), -1); - PhInsertEMenuItem(miscMenu, PhPluginCreateEMenuItem(PluginInstance, flags, ID_PROCESS_WSWATCH, L"WS watch", processItem), -1); - } -} - -VOID NTAPI ThreadMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_THREAD_ITEM threadItem; - ULONG insertIndex; - PPH_EMENU_ITEM menuItem; - - if (menuInfo->u.Thread.NumberOfThreads == 1) - threadItem = menuInfo->u.Thread.Threads[0]; - else - threadItem = NULL; - - if (menuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Resume", 0)) - insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; - else - insertIndex = 0; - - PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_THREAD_CANCELIO, - L"Cancel I/O", threadItem), insertIndex); - - if (!threadItem) menuItem->Flags |= PH_EMENU_DISABLED; -} - -VOID NTAPI ModuleMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_PROCESS_ITEM processItem; - BOOLEAN addMenuItem; - PPH_MODULE_ITEM moduleItem; - ULONG insertIndex; - PPH_EMENU_ITEM menuItem; - - addMenuItem = FALSE; - - if (processItem = PhReferenceProcessItem(menuInfo->u.Module.ProcessId)) - { - if (processItem->ServiceList && processItem->ServiceList->Count != 0) - addMenuItem = TRUE; - - PhDereferenceObject(processItem); - } - - if (!addMenuItem) - return; - - if (menuInfo->u.Module.NumberOfModules == 1) - moduleItem = menuInfo->u.Module.Modules[0]; - else - moduleItem = NULL; - - if (menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"Inspect", 0)) - insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; - else - insertIndex = 0; - - ModuleProcessId = menuInfo->u.Module.ProcessId; - - PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_MODULE_SERVICES, - L"Services", moduleItem), insertIndex); - - if (!moduleItem) menuItem->Flags |= PH_EMENU_DISABLED; -} - -VOID NTAPI ProcessTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; - - ProcessTreeNewHandle = treeNewInfo->TreeNewHandle; - EtProcessTreeNewInitializing(Parameter); -} - -VOID NTAPI NetworkTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; - - NetworkTreeNewHandle = treeNewInfo->TreeNewHandle; - EtNetworkTreeNewInitializing(Parameter); -} - -VOID NTAPI SystemInformationInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (EtGpuEnabled) - EtGpuSystemInformationInitializing(Parameter); - if (EtEtwEnabled && !!PhGetIntegerSetting(SETTING_NAME_ENABLE_SYSINFO_GRAPHS)) - EtEtwSystemInformationInitializing(Parameter); -} - -VOID NTAPI MiniInformationInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (EtGpuEnabled) - EtGpuMiniInformationInitializing(Parameter); - if (EtEtwEnabled) - EtEtwMiniInformationInitializing(Parameter); -} - -VOID NTAPI ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PLIST_ENTRY listEntry; - - // Note: no lock is needed because we only ever modify the list on this same thread. - - listEntry = EtProcessBlockListHead.Flink; - - while (listEntry != &EtProcessBlockListHead) - { - PET_PROCESS_BLOCK block; - - block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); - - PhUpdateDelta(&block->HardFaultsDelta, block->ProcessItem->HardFaultCount); - - // Invalidate all text. - - PhAcquireQueuedLockExclusive(&block->TextCacheLock); - memset(block->TextCacheValid, 0, sizeof(block->TextCacheValid)); - PhReleaseQueuedLockExclusive(&block->TextCacheLock); - - listEntry = listEntry->Flink; - } -} - -VOID NTAPI NetworkItemsUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PLIST_ENTRY listEntry; - - // Note: no lock is needed because we only ever modify the list on this same thread. - - listEntry = EtNetworkBlockListHead.Flink; - - while (listEntry != &EtNetworkBlockListHead) - { - PET_NETWORK_BLOCK block; - - block = CONTAINING_RECORD(listEntry, ET_NETWORK_BLOCK, ListEntry); - - // Invalidate all text. - - PhAcquireQueuedLockExclusive(&block->TextCacheLock); - memset(block->TextCacheValid, 0, sizeof(block->TextCacheValid)); - PhReleaseQueuedLockExclusive(&block->TextCacheLock); - - listEntry = listEntry->Flink; - } -} - -PET_PROCESS_BLOCK EtGetProcessBlock( - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - return PhPluginGetObjectExtension(PluginInstance, ProcessItem, EmProcessItemType); -} - -PET_NETWORK_BLOCK EtGetNetworkBlock( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - return PhPluginGetObjectExtension(PluginInstance, NetworkItem, EmNetworkItemType); -} - -VOID EtInitializeProcessBlock( - _Out_ PET_PROCESS_BLOCK Block, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - memset(Block, 0, sizeof(ET_PROCESS_BLOCK)); - Block->ProcessItem = ProcessItem; - PhInitializeQueuedLock(&Block->TextCacheLock); - InsertTailList(&EtProcessBlockListHead, &Block->ListEntry); -} - -VOID EtDeleteProcessBlock( - _In_ PET_PROCESS_BLOCK Block - ) -{ - ULONG i; - - EtProcIconNotifyProcessDelete(Block); - - for (i = 1; i <= ETPRTNC_MAXIMUM; i++) - { - PhClearReference(&Block->TextCache[i]); - } - - RemoveEntryList(&Block->ListEntry); -} - -VOID EtInitializeNetworkBlock( - _Out_ PET_NETWORK_BLOCK Block, - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - memset(Block, 0, sizeof(ET_NETWORK_BLOCK)); - Block->NetworkItem = NetworkItem; - PhInitializeQueuedLock(&Block->TextCacheLock); - InsertTailList(&EtNetworkBlockListHead, &Block->ListEntry); -} - -VOID EtDeleteNetworkBlock( - _In_ PET_NETWORK_BLOCK Block - ) -{ - ULONG i; - - for (i = 1; i <= ETNETNC_MAXIMUM; i++) - { - PhClearReference(&Block->TextCache[i]); - } - - RemoveEntryList(&Block->ListEntry); -} - -VOID NTAPI ProcessItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - EtInitializeProcessBlock(Extension, Object); -} - -VOID NTAPI ProcessItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - EtDeleteProcessBlock(Extension); -} - -VOID NTAPI NetworkItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - EtInitializeNetworkBlock(Extension, Object); -} - -VOID NTAPI NetworkItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - EtDeleteNetworkBlock(Extension); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Extended Tools"; - info->Author = L"wj32"; - info->Description = L"Extended functionality for Windows 7 and above, including ETW monitoring, GPU monitoring and a Disk tab."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1114"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackUnload), - UnloadCallback, - NULL, - &PluginUnloadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), - TreeNewMessageCallback, - NULL, - &PluginTreeNewMessageCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainWindowShowing), - MainWindowShowingCallback, - NULL, - &MainWindowShowingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), - ProcessPropertiesInitializingCallback, - NULL, - &ProcessPropertiesInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackHandlePropertiesInitializing), - HandlePropertiesInitializingCallback, - NULL, - &HandlePropertiesInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), - ProcessMenuInitializingCallback, - NULL, - &ProcessMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing), - ThreadMenuInitializingCallback, - NULL, - &ThreadMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), - ModuleMenuInitializingCallback, - NULL, - &ModuleMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), - ProcessTreeNewInitializingCallback, - NULL, - &ProcessTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), - NetworkTreeNewInitializingCallback, - NULL, - &NetworkTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackSystemInformationInitializing), - SystemInformationInitializingCallback, - NULL, - &SystemInformationInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMiniInformationInitializing), - MiniInformationInitializingCallback, - NULL, - &MiniInformationInitializingCallbackRegistration - ); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - ProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); - PhRegisterCallback( - &PhNetworkItemsUpdatedEvent, - NetworkItemsUpdatedCallback, - NULL, - &NetworkItemsUpdatedCallbackRegistration - ); - - InitializeListHead(&EtProcessBlockListHead); - InitializeListHead(&EtNetworkBlockListHead); - - PhPluginSetObjectExtension( - PluginInstance, - EmProcessItemType, - sizeof(ET_PROCESS_BLOCK), - ProcessItemCreateCallback, - ProcessItemDeleteCallback - ); - PhPluginSetObjectExtension( - PluginInstance, - EmNetworkItemType, - sizeof(ET_NETWORK_BLOCK), - NetworkItemCreateCallback, - NetworkItemDeleteCallback - ); - - { - static PH_SETTING_CREATE settings[] = - { - { StringSettingType, SETTING_NAME_DISK_TREE_LIST_COLUMNS, L"" }, - { IntegerPairSettingType, SETTING_NAME_DISK_TREE_LIST_SORT, L"4,2" }, // 4, DescendingSortOrder - { IntegerSettingType, SETTING_NAME_ENABLE_ETW_MONITOR, L"1" }, - { IntegerSettingType, SETTING_NAME_ENABLE_GPU_MONITOR, L"1" }, - { IntegerSettingType, SETTING_NAME_ENABLE_SYSINFO_GRAPHS, L"1" }, - { StringSettingType, SETTING_NAME_GPU_NODE_BITMAP, L"01000000" }, - { IntegerSettingType, SETTING_NAME_GPU_LAST_NODE_COUNT, L"0" } - }; - - PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); - } - } - break; - } - - return TRUE; -} \ No newline at end of file +/* + * Process Hacker Extended Tools - + * main program + * + * Copyright (C) 2010-2015 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +PPH_PLUGIN PluginInstance = NULL; +HWND ProcessTreeNewHandle = NULL; +HWND NetworkTreeNewHandle = NULL; +LIST_ENTRY EtProcessBlockListHead = { &EtProcessBlockListHead, &EtProcessBlockListHead }; +LIST_ENTRY EtNetworkBlockListHead = { &EtNetworkBlockListHead, &EtNetworkBlockListHead }; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginTreeNewMessageCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginPhSvcRequestCallbackRegistration; +PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION HandlePropertiesInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ModuleMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION SystemInformationInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION MiniInformationInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION TrayIconsInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessItemsUpdatedCallbackRegistration; +PH_CALLBACK_REGISTRATION NetworkItemsUpdatedCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessStatsEventCallbackRegistration; + +ULONG ProcessesUpdatedCount = 0; +static HANDLE ModuleProcessId = NULL; + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtEtwStatisticsInitialization(); + EtGpuMonitorInitialization(); +} + +VOID NTAPI UnloadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtSaveSettingsDiskTreeList(); + EtEtwStatisticsUninitialization(); +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"ExtendedTools", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + switch (menuItem->Id) + { + case ID_PROCESS_UNLOADEDMODULES: + { + EtShowUnloadedDllsDialog(menuItem->Context); + } + break; + case ID_PROCESS_WSWATCH: + { + EtShowWsWatchDialog(menuItem->OwnerWindow, menuItem->Context); + } + break; + case ID_THREAD_CANCELIO: + { + EtUiCancelIoThread(menuItem->OwnerWindow, menuItem->Context); + } + break; + case ID_MODULE_SERVICES: + { + EtShowModuleServicesDialog( + menuItem->OwnerWindow, + ModuleProcessId, + ((PPH_MODULE_ITEM)menuItem->Context)->Name + ); + } + break; + } +} + +VOID NTAPI TreeNewMessageCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + + if (message->TreeNewHandle == ProcessTreeNewHandle) + EtProcessTreeNewMessage(Parameter); + else if (message->TreeNewHandle == NetworkTreeNewHandle) + EtNetworkTreeNewMessage(Parameter); +} + +VOID NTAPI PhSvcRequestCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + DispatchPhSvcRequest(Parameter); +} + +VOID NTAPI MainWindowShowingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtInitializeDiskTab(); + EtRegisterToolbarGraphs(); +} + +VOID NTAPI ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (ProcessesUpdatedCount <= 2) + { + ProcessesUpdatedCount++; + return; + } +} + +VOID NTAPI ProcessPropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtProcessGpuPropertiesInitializing(Parameter); + EtProcessEtwPropertiesInitializing(Parameter); +} + +VOID NTAPI HandlePropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtHandlePropertiesInitializing(Parameter); +} + +VOID NTAPI ProcessMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_PROCESS_ITEM processItem; + ULONG flags; + PPH_EMENU_ITEM miscMenu; + + if (menuInfo->u.Process.NumberOfProcesses == 1) + processItem = menuInfo->u.Process.Processes[0]; + else + processItem = NULL; + + flags = 0; + + if (!processItem) + flags = PH_EMENU_DISABLED; + + miscMenu = PhFindEMenuItem(menuInfo->Menu, 0, L"Miscellaneous", 0); + + if (miscMenu) + { + PhInsertEMenuItem(miscMenu, PhPluginCreateEMenuItem(PluginInstance, flags, ID_PROCESS_UNLOADEDMODULES, L"&Unloaded modules", processItem), ULONG_MAX); + PhInsertEMenuItem(miscMenu, PhPluginCreateEMenuItem(PluginInstance, flags, ID_PROCESS_WSWATCH, L"&WS watch", processItem), ULONG_MAX); + } +} + +VOID NTAPI ThreadMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_THREAD_ITEM threadItem; + ULONG insertIndex; + PPH_EMENU_ITEM menuItem; + + if (menuInfo->u.Thread.NumberOfThreads == 1) + threadItem = menuInfo->u.Thread.Threads[0]; + else + threadItem = NULL; + + if (menuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Resume", 0)) + insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; + else + insertIndex = 0; + + menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_THREAD_CANCELIO, L"Ca&ncel I/O", threadItem); + PhInsertEMenuItem(menuInfo->Menu, menuItem, insertIndex); + + if (!threadItem) menuItem->Flags |= PH_EMENU_DISABLED; +} + +VOID NTAPI ModuleMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_PROCESS_ITEM processItem; + BOOLEAN addMenuItem; + PPH_MODULE_ITEM moduleItem; + ULONG insertIndex; + PPH_EMENU_ITEM menuItem; + + addMenuItem = FALSE; + + if (processItem = PhReferenceProcessItem(menuInfo->u.Module.ProcessId)) + { + if (processItem->ServiceList && processItem->ServiceList->Count != 0) + addMenuItem = TRUE; + + PhDereferenceObject(processItem); + } + + if (!addMenuItem) + return; + + if (menuInfo->u.Module.NumberOfModules == 1) + moduleItem = menuInfo->u.Module.Modules[0]; + else + moduleItem = NULL; + + if (menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"Inspect", 0)) + insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; + else + insertIndex = 0; + + ModuleProcessId = menuInfo->u.Module.ProcessId; + + menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_MODULE_SERVICES, L"Ser&vices", moduleItem); + PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), insertIndex); + PhInsertEMenuItem(menuInfo->Menu, menuItem, insertIndex + 1); + PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), insertIndex + 2); + + if (!moduleItem) menuItem->Flags |= PH_EMENU_DISABLED; +} + +VOID NTAPI ProcessTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; + + ProcessTreeNewHandle = treeNewInfo->TreeNewHandle; + EtProcessTreeNewInitializing(Parameter); +} + +VOID NTAPI NetworkTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; + + NetworkTreeNewHandle = treeNewInfo->TreeNewHandle; + EtNetworkTreeNewInitializing(Parameter); +} + +VOID NTAPI SystemInformationInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (EtGpuEnabled) + EtGpuSystemInformationInitializing(Parameter); + if (EtEtwEnabled && !!PhGetIntegerSetting(SETTING_NAME_ENABLE_SYSINFO_GRAPHS)) + EtEtwSystemInformationInitializing(Parameter); +} + +VOID NTAPI MiniInformationInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (EtGpuEnabled) + EtGpuMiniInformationInitializing(Parameter); + if (EtEtwEnabled) + EtEtwMiniInformationInitializing(Parameter); +} + +VOID NTAPI TrayIconsInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + EtLoadTrayIconGuids(); + EtRegisterNotifyIcons(Parameter); +} + +VOID NTAPI ProcessItemsUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PLIST_ENTRY listEntry; + + // Note: no lock is needed because we only ever modify the list on this same thread. + + listEntry = EtProcessBlockListHead.Flink; + + while (listEntry != &EtProcessBlockListHead) + { + PET_PROCESS_BLOCK block; + + block = CONTAINING_RECORD(listEntry, ET_PROCESS_BLOCK, ListEntry); + + PhUpdateDelta(&block->HardFaultsDelta, block->ProcessItem->HardFaultCount); + + // Invalidate all text. + + PhAcquireQueuedLockExclusive(&block->TextCacheLock); + memset(block->TextCacheValid, 0, sizeof(block->TextCacheValid)); + PhReleaseQueuedLockExclusive(&block->TextCacheLock); + + listEntry = listEntry->Flink; + } +} + +VOID NTAPI NetworkItemsUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PLIST_ENTRY listEntry; + + // Note: no lock is needed because we only ever modify the list on this same thread. + + listEntry = EtNetworkBlockListHead.Flink; + + while (listEntry != &EtNetworkBlockListHead) + { + PET_NETWORK_BLOCK block; + + block = CONTAINING_RECORD(listEntry, ET_NETWORK_BLOCK, ListEntry); + + // Invalidate all text. + + PhAcquireQueuedLockExclusive(&block->TextCacheLock); + memset(block->TextCacheValid, 0, sizeof(block->TextCacheValid)); + PhReleaseQueuedLockExclusive(&block->TextCacheLock); + + listEntry = listEntry->Flink; + } +} + +VOID NTAPI ProcessStatsEventCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_PROCESS_STATS_EVENT event = Parameter; + + if (event->Version) + return; + + switch (event->Type) + { + case 1: + { + HWND listViewHandle = event->Parameter; + PET_PROCESS_BLOCK block; + + if (!(block = EtGetProcessBlock(event->ProcessItem))) + break; + + block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU] = PhAddListViewGroup( + listViewHandle, (INT)ListView_GetGroupCount(listViewHandle), L"GPU"); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_RUNNINGTIME] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Running time", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Context switches", NULL); + //block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALNODES] = PhAddListViewGroupItem( + // listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Total Nodes", NULL); + //block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALSEGMENTS] = PhAddListViewGroupItem( + // listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Total Segments", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALDEDICATED] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Total Dedicated", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALSHARED] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Total Shared", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALCOMMIT] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_GPU], MAXINT, L"Total Commit", NULL); + + block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK] = PhAddListViewGroup( + listViewHandle, (INT)ListView_GetGroupCount(listViewHandle), L"Disk I/O"); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKREADS] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK], MAXINT, L"Reads", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKREADBYTES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK], MAXINT, L"Read bytes", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKREADBYTESDELTA] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK], MAXINT, L"Read bytes delta", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKWRITES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK], MAXINT, L"Writes", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKWRITEBYTES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK], MAXINT, L"Write bytes", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKWRITEBYTESDELTA] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_DISK], MAXINT, L"Write bytes delta", NULL); + + block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK] = PhAddListViewGroup( + listViewHandle, (INT)ListView_GetGroupCount(listViewHandle), L"Network I/O"); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKREADS] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK], MAXINT, L"Receives", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKREADBYTES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK], MAXINT, L"Receive bytes", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKREADBYTESDELTA] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK], MAXINT, L"Receive bytes delta", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKWRITES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK], MAXINT, L"Sends", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKWRITEBYTES] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK], MAXINT, L"Send bytes", NULL); + block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKWRITEBYTESDELTA] = PhAddListViewGroupItem( + listViewHandle, block->ListViewGroupCache[ET_PROCESS_STATISTICS_CATEGORY_NETWORK], MAXINT, L"Send bytes delta", NULL); + } + break; + case 2: + { + HWND listViewHandle = event->Parameter; + PET_PROCESS_BLOCK block; + WCHAR runningTimeString[PH_TIMESPAN_STR_LEN_1] = L"N/A"; + + if (!(block = EtGetProcessBlock(event->ProcessItem))) + break; + + PhPrintTimeSpan(runningTimeString, block->GpuRunningTimeDelta.Value * 10, PH_TIMESPAN_HMSM); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_RUNNINGTIME], 1, + runningTimeString); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_CONTEXTSWITCHES], 1, + PhaFormatUInt64(block->GpuContextSwitches, TRUE)->Buffer); + //PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALNODES], 1, + // PhaFormatUInt64(gpuStatistics.NodeCount, TRUE)->Buffer); + //PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALSEGMENTS], 1, + // PhaFormatUInt64(gpuStatistics.SegmentCount, TRUE)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALDEDICATED], 1, + PhaFormatSize(block->GpuDedicatedUsage, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALSHARED], 1, + PhaFormatSize(block->GpuSharedUsage, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_TOTALCOMMIT], 1, + PhaFormatSize(block->GpuCommitUsage, ULONG_MAX)->Buffer); + + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKREADS], 1, + PhaFormatUInt64(block->DiskReadCount, TRUE)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKREADBYTES], 1, + PhaFormatSize(block->DiskReadRawDelta.Value, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKREADBYTESDELTA], 1, + PhaFormatSize(block->DiskReadRawDelta.Delta, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKWRITES], 1, + PhaFormatUInt64(block->DiskWriteCount, TRUE)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKWRITEBYTES], 1, + PhaFormatSize(block->DiskWriteRawDelta.Value, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_DISKWRITEBYTESDELTA], 1, + PhaFormatSize(block->DiskWriteRawDelta.Delta, ULONG_MAX)->Buffer); + + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKREADS], 1, + PhaFormatUInt64(block->NetworkReceiveCount, TRUE)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKREADBYTES], 1, + PhaFormatSize(block->NetworkReceiveRawDelta.Value, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKREADBYTESDELTA], 1, + PhaFormatSize(block->NetworkReceiveRawDelta.Delta, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKWRITES], 1, + PhaFormatUInt64(block->NetworkSendCount, TRUE)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKWRITEBYTES], 1, + PhaFormatSize(block->NetworkSendRawDelta.Value, ULONG_MAX)->Buffer); + PhSetListViewSubItem(listViewHandle, block->ListViewRowCache[ET_PROCESS_STATISTICS_INDEX_NETWORKWRITEBYTESDELTA], 1, + PhaFormatSize(block->NetworkSendRawDelta.Delta, ULONG_MAX)->Buffer); + } + break; + } +} + +PET_PROCESS_BLOCK EtGetProcessBlock( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + return PhPluginGetObjectExtension(PluginInstance, ProcessItem, EmProcessItemType); +} + +PET_NETWORK_BLOCK EtGetNetworkBlock( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + return PhPluginGetObjectExtension(PluginInstance, NetworkItem, EmNetworkItemType); +} + +VOID EtInitializeProcessBlock( + _Out_ PET_PROCESS_BLOCK Block, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + memset(Block, 0, sizeof(ET_PROCESS_BLOCK)); + Block->ProcessItem = ProcessItem; + PhInitializeQueuedLock(&Block->TextCacheLock); + + //Block->GpuTotalRunningTimeDelta = PhAllocate(sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); + //memset(Block->GpuTotalRunningTimeDelta, 0, sizeof(PH_UINT64_DELTA) * EtGpuTotalNodeCount); + //Block->GpuTotalNodesHistory = PhAllocate(sizeof(PH_CIRCULAR_BUFFER_FLOAT) * EtGpuTotalNodeCount); + + InsertTailList(&EtProcessBlockListHead, &Block->ListEntry); +} + +VOID EtDeleteProcessBlock( + _In_ PET_PROCESS_BLOCK Block + ) +{ + for (ULONG i = 1; i <= ETPRTNC_MAXIMUM; i++) + { + PhClearReference(&Block->TextCache[i]); + } + + RemoveEntryList(&Block->ListEntry); +} + +VOID EtInitializeNetworkBlock( + _Out_ PET_NETWORK_BLOCK Block, + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + memset(Block, 0, sizeof(ET_NETWORK_BLOCK)); + Block->NetworkItem = NetworkItem; + PhInitializeQueuedLock(&Block->TextCacheLock); + InsertTailList(&EtNetworkBlockListHead, &Block->ListEntry); +} + +VOID EtDeleteNetworkBlock( + _In_ PET_NETWORK_BLOCK Block + ) +{ + for (ULONG i = 1; i <= ETNETNC_MAXIMUM; i++) + { + PhClearReference(&Block->TextCache[i]); + } + + RemoveEntryList(&Block->ListEntry); +} + +VOID NTAPI ProcessItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + EtInitializeProcessBlock(Extension, Object); +} + +VOID NTAPI ProcessItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + EtDeleteProcessBlock(Extension); +} + +VOID NTAPI NetworkItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + EtInitializeNetworkBlock(Extension, Object); +} + +VOID NTAPI NetworkItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + EtDeleteNetworkBlock(Extension); +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { StringSettingType, SETTING_NAME_DISK_TREE_LIST_COLUMNS, L"" }, + { IntegerPairSettingType, SETTING_NAME_DISK_TREE_LIST_SORT, L"4,2" }, // 4, DescendingSortOrder + { IntegerSettingType, SETTING_NAME_ENABLE_ETW_MONITOR, L"1" }, + { IntegerSettingType, SETTING_NAME_ENABLE_GPU_MONITOR, L"1" }, + { IntegerSettingType, SETTING_NAME_ENABLE_SYSINFO_GRAPHS, L"1" }, + { StringSettingType, SETTING_NAME_GPU_NODE_BITMAP, L"01000000" }, + { IntegerSettingType, SETTING_NAME_GPU_LAST_NODE_COUNT, L"0" }, + { IntegerPairSettingType, SETTING_NAME_UNLOADED_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_UNLOADED_WINDOW_SIZE, L"@96|350,270" }, + { StringSettingType, SETTING_NAME_UNLOADED_COLUMNS, L"" }, + { IntegerPairSettingType, SETTING_NAME_MODULE_SERVICES_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_MODULE_SERVICES_WINDOW_SIZE, L"@96|850,490" }, + { StringSettingType, SETTING_NAME_MODULE_SERVICES_COLUMNS, L"" }, + { IntegerPairSettingType, SETTING_NAME_GPU_NODES_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_GPU_NODES_WINDOW_SIZE, L"@96|850,490" }, + { IntegerPairSettingType, SETTING_NAME_WSWATCH_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_WSWATCH_WINDOW_SIZE, L"@96|325,266" }, + { StringSettingType, SETTING_NAME_WSWATCH_COLUMNS, L"" }, + { StringSettingType, SETTING_NAME_TRAYICON_GUIDS, L"" }, + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Extended Tools"; + info->Author = L"dmex, wj32"; + info->Description = L"Extended functionality for Windows 7 and above, including ETW monitoring, GPU monitoring and a Disk tab."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1114"; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackUnload), + UnloadCallback, + NULL, + &PluginUnloadCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), + TreeNewMessageCallback, + NULL, + &PluginTreeNewMessageCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackPhSvcRequest), + PhSvcRequestCallback, + NULL, + &PluginPhSvcRequestCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainWindowShowing), + MainWindowShowingCallback, + NULL, + &MainWindowShowingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + ProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), + ProcessPropertiesInitializingCallback, + NULL, + &ProcessPropertiesInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackHandlePropertiesInitializing), + HandlePropertiesInitializingCallback, + NULL, + &HandlePropertiesInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), + ProcessMenuInitializingCallback, + NULL, + &ProcessMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing), + ThreadMenuInitializingCallback, + NULL, + &ThreadMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), + ModuleMenuInitializingCallback, + NULL, + &ModuleMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), + ProcessTreeNewInitializingCallback, + NULL, + &ProcessTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), + NetworkTreeNewInitializingCallback, + NULL, + &NetworkTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackSystemInformationInitializing), + SystemInformationInitializingCallback, + NULL, + &SystemInformationInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMiniInformationInitializing), + MiniInformationInitializingCallback, + NULL, + &MiniInformationInitializingCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackTrayIconsInitializing), + TrayIconsInitializingCallback, + NULL, + &TrayIconsInitializingCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + ProcessItemsUpdatedCallback, + NULL, + &ProcessItemsUpdatedCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNetworkProviderUpdatedEvent), + NetworkItemsUpdatedCallback, + NULL, + &NetworkItemsUpdatedCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessStatsNotifyEvent), + ProcessStatsEventCallback, + NULL, + &ProcessStatsEventCallbackRegistration + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmProcessItemType, + sizeof(ET_PROCESS_BLOCK), + ProcessItemCreateCallback, + ProcessItemDeleteCallback + ); + PhPluginSetObjectExtension( + PluginInstance, + EmNetworkItemType, + sizeof(ET_NETWORK_BLOCK), + NetworkItemCreateCallback, + NetworkItemDeleteCallback + ); + + PhAddSettings(settings, RTL_NUMBER_OF(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/ExtendedTools/modsrv.c b/plugins/ExtendedTools/modsrv.c index 1a73346f4624..ee0d2967ef12 100644 --- a/plugins/ExtendedTools/modsrv.c +++ b/plugins/ExtendedTools/modsrv.c @@ -1,173 +1,292 @@ -/* - * Process Hacker Extended Tools - - * services referencing module - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include - -typedef struct _MODULE_SERVICES_CONTEXT -{ - HANDLE ProcessId; - PWSTR ModuleName; -} MODULE_SERVICES_CONTEXT, *PMODULE_SERVICES_CONTEXT; - -INT_PTR CALLBACK EtpModuleServicesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtShowModuleServicesDialog( - _In_ HWND ParentWindowHandle, - _In_ HANDLE ProcessId, - _In_ PWSTR ModuleName - ) -{ - MODULE_SERVICES_CONTEXT context; - - context.ProcessId = ProcessId; - context.ModuleName = ModuleName; - - DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_MODSERVICES), - ParentWindowHandle, - EtpModuleServicesDlgProc, - (LPARAM)&context - ); -} - -INT_PTR CALLBACK EtpModuleServicesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PMODULE_SERVICES_CONTEXT context = (PMODULE_SERVICES_CONTEXT)lParam; - ULONG win32Result; - PQUERY_TAG_INFORMATION I_QueryTagInformation; - TAG_INFO_NAMES_REFERENCING_MODULE namesReferencingModule; - PPH_LIST serviceList; - PPH_SERVICE_ITEM *serviceItems; - HWND serviceListHandle; - RECT rect; - PPH_PROCESS_ITEM processItem; - PPH_STRING message; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - I_QueryTagInformation = PhGetModuleProcAddress(L"advapi32.dll", "I_QueryTagInformation"); - - if (!I_QueryTagInformation) - { - PhShowError(hwndDlg, L"Unable to query services because the feature is not supported by the operating system."); - EndDialog(hwndDlg, IDCANCEL); - return FALSE; - } - - memset(&namesReferencingModule, 0, sizeof(TAG_INFO_NAMES_REFERENCING_MODULE)); - namesReferencingModule.InParams.dwPid = HandleToUlong(context->ProcessId); - namesReferencingModule.InParams.pszModule = context->ModuleName; - - win32Result = I_QueryTagInformation(NULL, eTagInfoLevelNamesReferencingModule, &namesReferencingModule); - - if (win32Result == ERROR_NO_MORE_ITEMS) - win32Result = 0; - - if (win32Result != 0) - { - PhShowStatus(hwndDlg, L"Unable to query services", 0, win32Result); - EndDialog(hwndDlg, IDCANCEL); - return FALSE; - } - - serviceList = PhCreateList(16); - - if (namesReferencingModule.OutParams.pmszNames) - { - PPH_SERVICE_ITEM serviceItem; - PWSTR serviceName; - ULONG nameLength; - - serviceName = namesReferencingModule.OutParams.pmszNames; - - while (TRUE) - { - nameLength = (ULONG)PhCountStringZ(serviceName); - - if (nameLength == 0) - break; - - if (serviceItem = PhReferenceServiceItem(serviceName)) - PhAddItemList(serviceList, serviceItem); - - serviceName += nameLength + 1; - } - - LocalFree(namesReferencingModule.OutParams.pmszNames); - } - - serviceItems = PhAllocateCopy(serviceList->Items, serviceList->Count * sizeof(PPH_SERVICE_ITEM)); - PhDereferenceObject(serviceList); - serviceListHandle = PhCreateServiceListControl(hwndDlg, serviceItems, serviceList->Count); - - // Position the control. - GetWindowRect(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), &rect); - MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); - MoveWindow(serviceListHandle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, FALSE); - - ShowWindow(serviceListHandle, SW_SHOW); - - if (processItem = PhReferenceProcessItem(context->ProcessId)) - { - message = PhFormatString(L"Services referencing %s in %s:", context->ModuleName, processItem->ProcessName->Buffer); - PhDereferenceObject(processItem); - } - else - { - message = PhFormatString(L"Services referencing %s:", context->ModuleName); - } - - SetDlgItemText(hwndDlg, IDC_MESSAGE, message->Buffer); - PhDereferenceObject(message); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * services referencing module + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include + +typedef struct _MODULE_SERVICES_CONTEXT +{ + HWND ParentWindowHandle; + HWND ServiceListHandle; + HANDLE ProcessId; + PPH_STRING ModuleName; + PH_LAYOUT_MANAGER LayoutManager; +} MODULE_SERVICES_CONTEXT, *PMODULE_SERVICES_CONTEXT; + +INT_PTR CALLBACK EtpModuleServicesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +NTSTATUS EtpModuleServicesDialogThreadStart( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + HWND windowHandle; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + windowHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_MODSERVICES), + NULL, + EtpModuleServicesDlgProc, + (LPARAM)Parameter + ); + + ShowWindow(windowHandle, SW_SHOW); + SetForegroundWindow(windowHandle); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(windowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + +VOID EtShowModuleServicesDialog( + _In_ HWND ParentWindowHandle, + _In_ HANDLE ProcessId, + _In_ PPH_STRING ModuleName + ) +{ + PMODULE_SERVICES_CONTEXT context; + + context = PhAllocateZero(sizeof(MODULE_SERVICES_CONTEXT)); + context->ParentWindowHandle = ParentWindowHandle; + context->ProcessId = ProcessId; + context->ModuleName = PhReferenceObject(ModuleName); + + PhCreateThread2(EtpModuleServicesDialogThreadStart, context); +} + +ULONG PhpQueryModuleServiceReferences( + _In_ HWND WindowHandle, + _In_ PMODULE_SERVICES_CONTEXT Context, + _Out_ PPH_LIST *ServiceList + ) +{ + ULONG win32Result; + PQUERY_TAG_INFORMATION I_QueryTagInformation; + TAG_INFO_NAMES_REFERENCING_MODULE namesReferencingModule; + PPH_LIST serviceList; + + if (!(I_QueryTagInformation = PhGetModuleProcAddress(L"advapi32.dll", "I_QueryTagInformation"))) + return ERROR_SUCCESS; + + memset(&namesReferencingModule, 0, sizeof(TAG_INFO_NAMES_REFERENCING_MODULE)); + namesReferencingModule.InParams.dwPid = HandleToUlong(Context->ProcessId); + namesReferencingModule.InParams.pszModule = PhGetString(Context->ModuleName); + + win32Result = I_QueryTagInformation(NULL, eTagInfoLevelNamesReferencingModule, &namesReferencingModule); + + if (win32Result == ERROR_NO_MORE_ITEMS) + win32Result = ERROR_SUCCESS; + + if (win32Result != ERROR_SUCCESS) + return win32Result; + + serviceList = PhCreateList(16); + + if (namesReferencingModule.OutParams.pmszNames) + { + PPH_SERVICE_ITEM serviceItem; + PWSTR serviceName; + ULONG nameLength; + + serviceName = namesReferencingModule.OutParams.pmszNames; + + while (TRUE) + { + nameLength = (ULONG)PhCountStringZ(serviceName); + + if (nameLength == 0) + break; + + if (serviceItem = PhReferenceServiceItem(serviceName)) + PhAddItemList(serviceList, serviceItem); + + serviceName += nameLength + 1; + } + + LocalFree(namesReferencingModule.OutParams.pmszNames); + } + + *ServiceList = serviceList; + + //if (serviceList->Count == 0) + //{ + // PhShowInformation2(GetParent(WindowHandle), L"", L"This module was not referenced by a service."); + // EndDialog(WindowHandle, IDCANCEL); + // return NULL; + //} + + return win32Result; +} + +INT_PTR CALLBACK EtpModuleServicesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PMODULE_SERVICES_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PMODULE_SERVICES_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (context->LayoutManager.List) // HACK (dmex) + PhDeleteLayoutManager(&context->LayoutManager); + + PhDereferenceObject(context->ModuleName); + PhFree(context); + + PostQuitMessage(0); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG win32Result; + PPH_LIST serviceList; + PPH_SERVICE_ITEM *serviceItems; + RECT rect; + + if ((win32Result = PhpQueryModuleServiceReferences(hwndDlg, context, &serviceList)) != STATUS_SUCCESS) + { + PhShowStatus( + (IsWindowVisible(context->ParentWindowHandle) && !IsMinimized(context->ParentWindowHandle)) ? context->ParentWindowHandle : NULL, + L"Unable to query module references.", 0, win32Result + ); + DestroyWindow(hwndDlg); + return FALSE; + } + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + serviceItems = PhAllocateCopy(serviceList->Items, serviceList->Count * sizeof(PPH_SERVICE_ITEM)); + context->ServiceListHandle = PhCreateServiceListControl(hwndDlg, serviceItems, serviceList->Count); + SendMessage(context->ServiceListHandle, WM_PH_SET_LIST_VIEW_SETTINGS, 0, (LPARAM)SETTING_NAME_MODULE_SERVICES_COLUMNS); + PhDereferenceObject(serviceList); + + { + PPH_PROCESS_ITEM processItem; + PPH_STRING message; + + if (processItem = PhReferenceProcessItem(context->ProcessId)) + { + message = PhFormatString( + L"Services referencing %s in %s (%lu):", + PhGetString(context->ModuleName), + PhGetStringOrEmpty(processItem->ProcessName), + HandleToUlong(processItem->ProcessId) + ); + PhDereferenceObject(processItem); + } + else + { + message = PhFormatString(L"Services referencing %s:", PhGetString(context->ModuleName)); + } + + PhSetDialogItemText(hwndDlg, IDC_MESSAGE, message->Buffer); + PhDereferenceObject(message); + } + + // Position the control. + GetWindowRect(GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), &rect); + MapWindowPoints(NULL, hwndDlg, (POINT *)&rect, 2); + MoveWindow(context->ServiceListHandle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, FALSE); + ShowWindow(context->ServiceListHandle, SW_SHOW); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SERVICES_LAYOUT), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, context->ServiceListHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + + if (PhGetIntegerPairSetting(SETTING_NAME_MODULE_SERVICES_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_MODULE_SERVICES_WINDOW_POSITION, SETTING_NAME_MODULE_SERVICES_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + { + // NOTE: Don't save placement during WM_DESTROY since the dialog won't be created after an error querying service references. (dmex) + PhSaveWindowPlacementToSetting(SETTING_NAME_MODULE_SERVICES_WINDOW_POSITION, SETTING_NAME_MODULE_SERVICES_WINDOW_SIZE, hwndDlg); + + DestroyWindow(hwndDlg); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/objprp.c b/plugins/ExtendedTools/objprp.c index 8cf21194bdc6..8ac70e6b4788 100644 --- a/plugins/ExtendedTools/objprp.c +++ b/plugins/ExtendedTools/objprp.c @@ -1,312 +1,249 @@ -/* - * Process Hacker Extended Tools - - * handle properties extensions - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include - -typedef struct _COMMON_PAGE_CONTEXT -{ - PPH_HANDLE_ITEM HandleItem; - HANDLE ProcessId; -} COMMON_PAGE_CONTEXT, *PCOMMON_PAGE_CONTEXT; - -HPROPSHEETPAGE EtpCommonCreatePage( - _In_ PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT Context, - _In_ PWSTR Template, - _In_ DLGPROC DlgProc - ); - -INT CALLBACK EtpCommonPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ); - -INT_PTR CALLBACK EtpAlpcPortPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK EtpTpWorkerFactoryPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtHandlePropertiesInitializing( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; - PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT context = objectProperties->Parameter; - - if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - HPROPSHEETPAGE page = NULL; - - if (PhEqualString2(context->HandleItem->TypeName, L"ALPC Port", TRUE)) - { - page = EtpCommonCreatePage( - context, - MAKEINTRESOURCE(IDD_OBJALPCPORT), - EtpAlpcPortPageDlgProc - ); - } - else if (PhEqualString2(context->HandleItem->TypeName, L"TpWorkerFactory", TRUE)) - { - page = EtpCommonCreatePage( - context, - MAKEINTRESOURCE(IDD_OBJTPWORKERFACTORY), - EtpTpWorkerFactoryPageDlgProc - ); - } - - // Insert our page into the second slot. - - if (page) - { - if (objectProperties->NumberOfPages > 1) - { - memmove(&objectProperties->Pages[2], &objectProperties->Pages[1], - (objectProperties->NumberOfPages - 1) * sizeof(HPROPSHEETPAGE)); - } - - objectProperties->Pages[1] = page; - objectProperties->NumberOfPages++; - } - } -} - -static HPROPSHEETPAGE EtpCommonCreatePage( - _In_ PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT Context, - _In_ PWSTR Template, - _In_ DLGPROC DlgProc - ) -{ - HPROPSHEETPAGE propSheetPageHandle; - PROPSHEETPAGE propSheetPage; - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = PhCreateAlloc(sizeof(COMMON_PAGE_CONTEXT)); - memset(pageContext, 0, sizeof(COMMON_PAGE_CONTEXT)); - pageContext->HandleItem = Context->HandleItem; - pageContext->ProcessId = Context->ProcessId; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USECALLBACK; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = Template; - propSheetPage.pfnDlgProc = DlgProc; - propSheetPage.lParam = (LPARAM)pageContext; - propSheetPage.pfnCallback = EtpCommonPropPageProc; - - propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); - PhDereferenceObject(pageContext); // already got a ref from above call - - return propSheetPageHandle; -} - -INT CALLBACK EtpCommonPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ) -{ - PCOMMON_PAGE_CONTEXT pageContext; - - pageContext = (PCOMMON_PAGE_CONTEXT)ppsp->lParam; - - if (uMsg == PSPCB_ADDREF) - PhReferenceObject(pageContext); - else if (uMsg == PSPCB_RELEASE) - PhDereferenceObject(pageContext); - - return 1; -} - -static NTSTATUS EtpDuplicateHandleFromProcess( - _Out_ PHANDLE Handle, - _In_ ACCESS_MASK DesiredAccess, - _In_ PCOMMON_PAGE_CONTEXT Context - ) -{ - NTSTATUS status; - HANDLE processHandle; - - if (!NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_DUP_HANDLE, - Context->ProcessId - ))) - return status; - - status = NtDuplicateObject( - processHandle, - Context->HandleItem->Handle, - NtCurrentProcess(), - Handle, - DesiredAccess, - 0, - 0 - ); - NtClose(processHandle); - - return status; -} - -INT_PTR CALLBACK EtpAlpcPortPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PCOMMON_PAGE_CONTEXT context = (PCOMMON_PAGE_CONTEXT)propSheetPage->lParam; - HANDLE portHandle; - - if (NT_SUCCESS(EtpDuplicateHandleFromProcess(&portHandle, READ_CONTROL, context))) - { - ALPC_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(NtAlpcQueryInformation( - portHandle, - AlpcBasicInformation, - &basicInfo, - sizeof(ALPC_BASIC_INFORMATION), - NULL - ))) - { - PH_FORMAT format[2]; - PPH_STRING string; - - PhInitFormatS(&format[0], L"Sequence Number: "); - PhInitFormatD(&format[1], basicInfo.SequenceNo); - format[1].Type |= FormatGroupDigits; - - string = PhFormat(format, 2, 128); - SetDlgItemText(hwndDlg, IDC_SEQUENCENUMBER, string->Buffer); - PhDereferenceObject(string); - - SetDlgItemText(hwndDlg, IDC_PORTCONTEXT, - PhaFormatString(L"Port Context: 0x%Ix", basicInfo.PortContext)->Buffer); - } - - NtClose(portHandle); - } - } - break; - } - - return FALSE; -} - -static BOOLEAN NTAPI EnumGenericModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - if (Module->Type == PH_MODULE_TYPE_MODULE || Module->Type == PH_MODULE_TYPE_WOW64_MODULE) - { - PhLoadModuleSymbolProvider(Context, Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, Module->Size); - } - - return TRUE; -} - -INT_PTR CALLBACK EtpTpWorkerFactoryPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PCOMMON_PAGE_CONTEXT context = (PCOMMON_PAGE_CONTEXT)propSheetPage->lParam; - HANDLE workerFactoryHandle; - - if (NT_SUCCESS(EtpDuplicateHandleFromProcess(&workerFactoryHandle, WORKER_FACTORY_QUERY_INFORMATION, context))) - { - WORKER_FACTORY_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(NtQueryInformationWorkerFactory( - workerFactoryHandle, - WorkerFactoryBasicInformation, - &basicInfo, - sizeof(WORKER_FACTORY_BASIC_INFORMATION), - NULL - ))) - { - PPH_SYMBOL_PROVIDER symbolProvider; - PPH_STRING symbol = NULL; - - symbolProvider = PhCreateSymbolProvider(basicInfo.ProcessId); - PhLoadSymbolProviderOptions(symbolProvider); - - if (symbolProvider->IsRealHandle) - { - PhEnumGenericModules(basicInfo.ProcessId, symbolProvider->ProcessHandle, - 0, EnumGenericModulesCallback, symbolProvider); - - symbol = PhGetSymbolFromAddress(symbolProvider, (ULONG64)basicInfo.StartRoutine, - NULL, NULL, NULL, NULL); - } - - PhDereferenceObject(symbolProvider); - - if (symbol) - { - SetDlgItemText(hwndDlg, IDC_WORKERTHREADSTART, - PhaFormatString(L"Worker Thread Start: %s", symbol->Buffer)->Buffer); - PhDereferenceObject(symbol); - } - else - { - SetDlgItemText(hwndDlg, IDC_WORKERTHREADSTART, - PhaFormatString(L"Worker Thread Start: 0x%Ix", basicInfo.StartRoutine)->Buffer); - } - - SetDlgItemText(hwndDlg, IDC_WORKERTHREADCONTEXT, - PhaFormatString(L"Worker Thread Context: 0x%Ix", basicInfo.StartParameter)->Buffer); - } - - NtClose(workerFactoryHandle); - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * handle properties extensions + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include + +typedef struct _COMMON_PAGE_CONTEXT +{ + PPH_HANDLE_ITEM HandleItem; + HANDLE ProcessId; +} COMMON_PAGE_CONTEXT, *PCOMMON_PAGE_CONTEXT; + +HPROPSHEETPAGE EtpCommonCreatePage( + _In_ PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT Context, + _In_ PWSTR Template, + _In_ DLGPROC DlgProc + ); + +INT CALLBACK EtpCommonPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +INT_PTR CALLBACK EtpTpWorkerFactoryPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EtHandlePropertiesInitializing( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; + PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT context = objectProperties->Parameter; + + if (PhIsNullOrEmptyString(context->HandleItem->TypeName)) + return; + + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + HPROPSHEETPAGE page = NULL; + + if (PhEqualString2(context->HandleItem->TypeName, L"TpWorkerFactory", TRUE)) + { + page = EtpCommonCreatePage( + context, + MAKEINTRESOURCE(IDD_OBJTPWORKERFACTORY), + EtpTpWorkerFactoryPageDlgProc + ); + } + + // Insert our page into the second slot. + + if (page) + { + if (objectProperties->NumberOfPages > 1) + { + memmove(&objectProperties->Pages[2], &objectProperties->Pages[1], + (objectProperties->NumberOfPages - 1) * sizeof(HPROPSHEETPAGE)); + } + + objectProperties->Pages[1] = page; + objectProperties->NumberOfPages++; + } + } +} + +static HPROPSHEETPAGE EtpCommonCreatePage( + _In_ PPH_PLUGIN_HANDLE_PROPERTIES_CONTEXT Context, + _In_ PWSTR Template, + _In_ DLGPROC DlgProc + ) +{ + HPROPSHEETPAGE propSheetPageHandle; + PROPSHEETPAGE propSheetPage; + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = PhCreateAlloc(sizeof(COMMON_PAGE_CONTEXT)); + memset(pageContext, 0, sizeof(COMMON_PAGE_CONTEXT)); + pageContext->HandleItem = Context->HandleItem; + pageContext->ProcessId = Context->ProcessId; + + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USECALLBACK; + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.pszTemplate = Template; + propSheetPage.pfnDlgProc = DlgProc; + propSheetPage.lParam = (LPARAM)pageContext; + propSheetPage.pfnCallback = EtpCommonPropPageProc; + + propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); + PhDereferenceObject(pageContext); // already got a ref from above call + + return propSheetPageHandle; +} + +INT CALLBACK EtpCommonPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) +{ + PCOMMON_PAGE_CONTEXT pageContext; + + pageContext = (PCOMMON_PAGE_CONTEXT)ppsp->lParam; + + if (uMsg == PSPCB_ADDREF) + PhReferenceObject(pageContext); + else if (uMsg == PSPCB_RELEASE) + PhDereferenceObject(pageContext); + + return 1; +} + +static NTSTATUS EtpDuplicateHandleFromProcess( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCOMMON_PAGE_CONTEXT Context + ) +{ + NTSTATUS status; + HANDLE processHandle; + + if (!NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_DUP_HANDLE, + Context->ProcessId + ))) + return status; + + status = NtDuplicateObject( + processHandle, + Context->HandleItem->Handle, + NtCurrentProcess(), + Handle, + DesiredAccess, + 0, + 0 + ); + NtClose(processHandle); + + return status; +} + +static BOOLEAN NTAPI EnumGenericModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + if (Module->Type == PH_MODULE_TYPE_MODULE || Module->Type == PH_MODULE_TYPE_WOW64_MODULE) + { + PhLoadModuleSymbolProvider(Context, Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, Module->Size); + } + + return TRUE; +} + +INT_PTR CALLBACK EtpTpWorkerFactoryPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PCOMMON_PAGE_CONTEXT context = (PCOMMON_PAGE_CONTEXT)propSheetPage->lParam; + HANDLE workerFactoryHandle; + + if (NT_SUCCESS(EtpDuplicateHandleFromProcess(&workerFactoryHandle, WORKER_FACTORY_QUERY_INFORMATION, context))) + { + WORKER_FACTORY_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(NtQueryInformationWorkerFactory( + workerFactoryHandle, + WorkerFactoryBasicInformation, + &basicInfo, + sizeof(WORKER_FACTORY_BASIC_INFORMATION), + NULL + ))) + { + PPH_SYMBOL_PROVIDER symbolProvider; + PPH_STRING symbol = NULL; + + symbolProvider = PhCreateSymbolProvider(basicInfo.ProcessId); + PhLoadSymbolProviderOptions(symbolProvider); + + if (symbolProvider->IsRealHandle) + { + PhEnumGenericModules(basicInfo.ProcessId, symbolProvider->ProcessHandle, + 0, EnumGenericModulesCallback, symbolProvider); + + symbol = PhGetSymbolFromAddress(symbolProvider, (ULONG64)basicInfo.StartRoutine, + NULL, NULL, NULL, NULL); + } + + PhDereferenceObject(symbolProvider); + + if (symbol) + { + PhSetDialogItemText(hwndDlg, IDC_WORKERTHREADSTART, + PhaFormatString(L"Worker Thread Start: %s", symbol->Buffer)->Buffer); + PhDereferenceObject(symbol); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_WORKERTHREADSTART, + PhaFormatString(L"Worker Thread Start: 0x%Ix", basicInfo.StartRoutine)->Buffer); + } + + PhSetDialogItemText(hwndDlg, IDC_WORKERTHREADCONTEXT, + PhaFormatString(L"Worker Thread Context: 0x%Ix", basicInfo.StartParameter)->Buffer); + } + + NtClose(workerFactoryHandle); + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/options.c b/plugins/ExtendedTools/options.c index 20e0c3323788..0b9c5b36b0fa 100644 --- a/plugins/ExtendedTools/options.c +++ b/plugins/ExtendedTools/options.c @@ -1,87 +1,51 @@ -/* - * Process Hacker Extended Tools - - * options dialog - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtShowOptionsDialog( - _In_ HWND ParentWindowHandle - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - ParentWindowHandle, - OptionsDlgProc - ); -} - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEETWMONITOR), PhGetIntegerSetting(SETTING_NAME_ENABLE_ETW_MONITOR) ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGPUMONITOR), PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR) ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLESYSINFOGRAPHS), PhGetIntegerSetting(SETTING_NAME_ENABLE_SYSINFO_GRAPHS) ? BST_CHECKED : BST_UNCHECKED); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PhSetIntegerSetting(SETTING_NAME_ENABLE_ETW_MONITOR, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEETWMONITOR)) == BST_CHECKED); - PhSetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGPUMONITOR)) == BST_CHECKED); - PhSetIntegerSetting(SETTING_NAME_ENABLE_SYSINFO_GRAPHS, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLESYSINFOGRAPHS)) == BST_CHECKED); - - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * options dialog + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEETWMONITOR), PhGetIntegerSetting(SETTING_NAME_ENABLE_ETW_MONITOR) ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGPUMONITOR), PhGetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR) ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLESYSINFOGRAPHS), PhGetIntegerSetting(SETTING_NAME_ENABLE_SYSINFO_GRAPHS) ? BST_CHECKED : BST_UNCHECKED); + } + break; + case WM_DESTROY: + { + PhSetIntegerSetting(SETTING_NAME_ENABLE_ETW_MONITOR, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEETWMONITOR)) == BST_CHECKED); + PhSetIntegerSetting(SETTING_NAME_ENABLE_GPU_MONITOR, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLEGPUMONITOR)) == BST_CHECKED); + PhSetIntegerSetting(SETTING_NAME_ENABLE_SYSINFO_GRAPHS, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLESYSINFOGRAPHS)) == BST_CHECKED); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/procicon.c b/plugins/ExtendedTools/procicon.c deleted file mode 100644 index 9724a62da3f0..000000000000 --- a/plugins/ExtendedTools/procicon.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Process Hacker Extended Tools - - * process icon duplication - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -PET_PROCESS_ICON EtProcIconCreateProcessIcon( - _In_ HICON Icon - ) -{ - PET_PROCESS_ICON processIcon; - - processIcon = PhAllocate(sizeof(ET_PROCESS_ICON)); - processIcon->RefCount = 1; - processIcon->Icon = CopyIcon(Icon); - - return processIcon; -} - -VOID EtProcIconReferenceProcessIcon( - _Inout_ PET_PROCESS_ICON ProcessIcon - ) -{ - _InterlockedIncrement(&ProcessIcon->RefCount); -} - -VOID EtProcIconDereferenceProcessIcon( - _Inout_ PET_PROCESS_ICON ProcessIcon - ) -{ - if (_InterlockedDecrement(&ProcessIcon->RefCount) == 0) - { - DestroyIcon(ProcessIcon->Icon); - PhFree(ProcessIcon); - } -} - -PET_PROCESS_ICON EtProcIconReferenceSmallProcessIcon( - _Inout_ PET_PROCESS_BLOCK Block - ) -{ - PET_PROCESS_ICON smallProcessIcon; - - smallProcessIcon = Block->SmallProcessIcon; - - if (!smallProcessIcon && PhTestEvent(&Block->ProcessItem->Stage1Event)) - { - smallProcessIcon = EtProcIconCreateProcessIcon(Block->ProcessItem->SmallIcon); - - if (_InterlockedCompareExchangePointer( - &Block->SmallProcessIcon, - smallProcessIcon, - NULL - ) != NULL) - { - EtProcIconDereferenceProcessIcon(smallProcessIcon); - smallProcessIcon = Block->SmallProcessIcon; - } - } - - if (smallProcessIcon) - { - EtProcIconReferenceProcessIcon(smallProcessIcon); - } - - return smallProcessIcon; -} - -VOID EtProcIconNotifyProcessDelete( - _Inout_ PET_PROCESS_BLOCK Block - ) -{ - if (Block->SmallProcessIcon) - { - EtProcIconDereferenceProcessIcon(Block->SmallProcessIcon); - } -} diff --git a/plugins/ExtendedTools/resource.h b/plugins/ExtendedTools/resource.h index 4e73128faf6f..a550c87db3b5 100644 --- a/plugins/ExtendedTools/resource.h +++ b/plugins/ExtendedTools/resource.h @@ -1,114 +1,115 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by ExtendedTools.rc -// -#define ID_PROCESS_UNLOADEDMODULES 101 -#define IDD_UNLOADEDDLLS 102 -#define IDD_OBJALPCPORT 103 -#define ID_THREAD_CANCELIO 104 -#define IDD_OBJTPWORKERFACTORY 105 -#define ID_MODULE_SERVICES 106 -#define IDD_MODSERVICES 107 -#define ID_VIEW_MEMORYLISTS 108 -#define IDD_PROCDISKNET 110 -#define ID_VIEW_DISKANDNETWORK 111 -#define ID_PROCESS_WSWATCH 112 -#define IDD_SYSINFO_DISKPANEL 113 -#define IDD_OPTIONS 114 -#define IDD_WSWATCH 115 -#define IDD_SYSINFO_GPU 117 -#define IDR_DISK 118 -#define ID_VIEW_GPUINFORMATION 119 -#define IDD_SYSINFO_GPUPANEL 120 -#define IDD_PROCGPU 121 -#define IDD_SYSINFO_DISK 122 -#define IDD_GPUNODES 123 -#define IDD_SYSINFO_NETPANEL 124 -#define IDD_SYSINFO_NET 125 -#define IDD_PROCGPU_PANEL 126 -#define IDD_DISKTABRESTART 127 -#define IDD_DISKTABERROR 128 -#define IDD_PROCDISKNET_PANEL 129 -#define IDD_PROCGPU_DETAILS 131 -#define IDC_LIST 1001 -#define IDC_REFRESH 1002 -#define IDC_SEQUENCENUMBER 1003 -#define IDC_PORTCONTEXT 1004 -#define IDC_WORKERTHREADSTART 1005 -#define IDC_WORKERTHREADCONTEXT 1006 -#define IDC_SERVICES_LAYOUT 1007 -#define IDC_MESSAGE 1008 -#define IDC_ZREADS_V 1023 -#define IDC_ZREADBYTES_V 1024 -#define IDC_ZREADBYTESDELTA_V 1025 -#define IDC_ZWRITES_V 1026 -#define IDC_ZWRITEBYTES_V 1027 -#define IDC_ZWRITEBYTESDELTA_V 1028 -#define IDC_ZRECEIVES_V 1029 -#define IDC_ZRECEIVEBYTES_V 1030 -#define IDC_ZRECEIVEBYTESDELTA_V 1031 -#define IDC_ZSENDS_V 1032 -#define IDC_ZSENDBYTES_V 1033 -#define IDC_ZSENDBYTESDELTA_V 1034 -#define IDC_ENABLEETWMONITOR 1035 -#define IDC_ENABLE 1036 -#define IDC_WSWATCHENABLED 1037 -#define IDC_NODES 1048 -#define IDC_ENABLEGPUMONITOR 1049 -#define IDC_EXAMPLE 1050 -#define IDC_ENABLESYSINFOGRAPHS 1050 -#define IDC_GROUPGPU 1051 -#define IDC_GROUPMEM 1052 -#define IDC_GROUPSHARED 1053 -#define IDC_GROUPDEDICATED 1054 -#define IDC_ZDEDICATEDCURRENT_V 1055 -#define IDC_ZDEDICATEDLIMIT_V 1056 -#define IDC_ZSHAREDCURRENT_V 1057 -#define IDC_ZSHAREDLIMIT_V 1058 -#define IDC_ZDEDICATEDCOMMITTED_V 1059 -#define IDC_ZRUNNINGTIME_V 1060 -#define IDC_ZCONTEXTSWITCHES_V 1061 -#define IDC_ZTOTALNODES_V 1062 -#define IDC_ZCACHEDALLOCATED_V 1063 -#define IDC_ZCACHEDRESERVED_V 1064 -#define IDC_ZSHAREDCOMMITTED_V 1065 -#define IDC_ZTOTALALLOCATED_V 1066 -#define IDC_ZTOTALRESERVED_V 1067 -#define IDC_ZWRITECOMBINEDALLOCATED_V 1068 -#define IDC_ZWRITECOMBINEDRESERVED_V 1069 -#define IDC_ZSECTIONALLOCATED_V 1070 -#define IDC_ZSECTIONRESERVED_V 1071 -#define IDC_ZTOTALSEGMENTS_V 1072 -#define IDC_TITLE 1073 -#define IDC_GRAPH_LAYOUT 1074 -#define IDC_LAYOUT 1075 -#define IDC_GPUNAME 1076 -#define IDC_GPU_L 1077 -#define IDC_DEDICATED_L 1078 -#define IDC_SHARED_L 1079 -#define IDC_ZRECEIVESDELTA_V 1080 -#define IDC_ZSENDSDELTA_V 1081 -#define IDC_ZWRITESDELTA_V 1082 -#define IDC_ZREADSDELTA_V 1083 -#define IDC_INSTRUCTION 1084 -#define IDC_PANEL_LAYOUT 1085 -#define IDC_RESTART 1086 -#define IDC_ERROR 1087 -#define IDC_GROUPDISK 1088 -#define IDC_GROUPNETWORK 1089 -#define IDC_GPUDETAILS 1090 -#define ID_DISK_GOTOPROCESS 40005 -#define ID_DISK_COPY 40006 -#define ID_DISK_PROPERTIES 40007 -#define ID_DISK_OPENFILELOCATION 40008 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 135 -#define _APS_NEXT_COMMAND_VALUE 40009 -#define _APS_NEXT_CONTROL_VALUE 1091 -#define _APS_NEXT_SYMED_VALUE 130 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ExtendedTools.rc +// +#define ID_PROCESS_UNLOADEDMODULES 101 +#define IDD_UNLOADEDDLLS 102 +#define ID_THREAD_CANCELIO 104 +#define IDD_OBJTPWORKERFACTORY 105 +#define ID_MODULE_SERVICES 106 +#define IDD_MODSERVICES 107 +#define ID_VIEW_MEMORYLISTS 108 +#define IDD_PROCDISKNET 110 +#define ID_VIEW_DISKANDNETWORK 111 +#define ID_PROCESS_WSWATCH 112 +#define IDD_SYSINFO_DISKPANEL 113 +#define IDD_OPTIONS 114 +#define IDD_WSWATCH 115 +#define IDD_SYSINFO_GPU 117 +#define IDR_DISK 118 +#define ID_VIEW_GPUINFORMATION 119 +#define IDD_SYSINFO_GPUPANEL 120 +#define IDD_PROCGPU 121 +#define IDD_SYSINFO_DISK 122 +#define IDD_GPUNODES 123 +#define IDD_SYSINFO_NETPANEL 124 +#define IDD_SYSINFO_NET 125 +#define IDD_PROCGPU_PANEL 126 +#define IDD_DISKTABERROR 128 +#define IDD_PROCDISKNET_PANEL 129 +#define IDD_PROCGPU_DETAILS 131 +#define IDC_UTILIZATION 132 +#define IDC_SPEED 133 +#define IDD_SYSINFO_GPUDETAILS 141 +#define IDC_LIST 1001 +#define IDC_REFRESH 1002 +#define IDC_WORKERTHREADSTART 1005 +#define IDC_WORKERTHREADCONTEXT 1006 +#define IDC_SERVICES_LAYOUT 1007 +#define IDC_MESSAGE 1008 +#define IDC_ZREADS_V 1023 +#define IDC_ZREADBYTES_V 1024 +#define IDC_ZREADBYTESDELTA_V 1025 +#define IDC_ZWRITES_V 1026 +#define IDC_ZWRITEBYTES_V 1027 +#define IDC_ZWRITEBYTESDELTA_V 1028 +#define IDC_ZRECEIVES_V 1029 +#define IDC_ZRECEIVEBYTES_V 1030 +#define IDC_ZRECEIVEBYTESDELTA_V 1031 +#define IDC_ZSENDS_V 1032 +#define IDC_ZSENDBYTES_V 1033 +#define IDC_ZSENDBYTESDELTA_V 1034 +#define IDC_ENABLEETWMONITOR 1035 +#define IDC_ENABLE 1036 +#define IDC_WSWATCHENABLED 1037 +#define IDC_NODES 1048 +#define IDC_ENABLEGPUMONITOR 1049 +#define IDC_ENABLESYSINFOGRAPHS 1050 +#define IDC_GROUPGPU 1051 +#define IDC_GROUPMEM 1052 +#define IDC_GROUPSHARED 1053 +#define IDC_GROUPDEDICATED 1054 +#define IDC_GROUPCOMMIT 1054 +#define IDC_ZDEDICATEDCURRENT_V 1055 +#define IDC_ZDEDICATEDLIMIT_V 1056 +#define IDC_ZSHAREDCURRENT_V 1057 +#define IDC_ZSHAREDLIMIT_V 1058 +#define IDC_ZDEDICATEDCOMMITTED_V 1059 +#define IDC_ZRUNNINGTIME_V 1060 +#define IDC_ZCONTEXTSWITCHES_V 1061 +#define IDC_ZTOTALNODES_V 1062 +#define IDC_ZCACHEDALLOCATED_V 1063 +#define IDC_ZCACHEDRESERVED_V 1064 +#define IDC_ZSHAREDCOMMITTED_V 1065 +#define IDC_ZTOTALALLOCATED_V 1066 +#define IDC_ZTOTALRESERVED_V 1067 +#define IDC_ZWRITECOMBINEDALLOCATED_V 1068 +#define IDC_ZWRITECOMBINEDRESERVED_V 1069 +#define IDC_ZSECTIONALLOCATED_V 1070 +#define IDC_ZSECTIONRESERVED_V 1071 +#define IDC_ZTOTALSEGMENTS_V 1072 +#define IDC_TITLE 1073 +#define IDC_GRAPH_LAYOUT 1074 +#define IDC_LAYOUT 1075 +#define IDC_GPUNAME 1076 +#define IDC_GPU_L 1077 +#define IDC_DEDICATED_L 1078 +#define IDC_SHARED_L 1079 +#define IDC_ZRECEIVESDELTA_V 1080 +#define IDC_ZSENDSDELTA_V 1081 +#define IDC_ZWRITESDELTA_V 1082 +#define IDC_ZREADSDELTA_V 1083 +#define IDC_PANEL_LAYOUT 1085 +#define IDC_RESTART 1086 +#define IDC_ERROR 1087 +#define IDC_GROUPDISK 1088 +#define IDC_GROUPNETWORK 1089 +#define IDC_GPUDETAILS 1090 +#define IDC_BUTTON1 1092 +#define IDC_DETAILS 1092 +#define IDC_GPULIST 1093 +#define ID_DISK_GOTOPROCESS 40005 +#define ID_DISK_COPY 40006 +#define ID_DISK_PROPERTIES 40007 +#define ID_DISK_OPENFILELOCATION 40008 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 144 +#define _APS_NEXT_COMMAND_VALUE 40009 +#define _APS_NEXT_CONTROL_VALUE 1094 +#define _APS_NEXT_SYMED_VALUE 134 +#endif +#endif diff --git a/plugins/ExtendedTools/svcext.c b/plugins/ExtendedTools/svcext.c new file mode 100644 index 000000000000..ee129fd13dcb --- /dev/null +++ b/plugins/ExtendedTools/svcext.c @@ -0,0 +1,137 @@ +/* + * Process Hacker Extended Tools - + * phsvc extensions + * + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +NTSTATUS CallGetProcessUnloadedDlls( + _In_ HANDLE ProcessId, + _Out_ PPH_STRING *UnloadedDlls + ) +{ + NTSTATUS status; + PH_PLUGIN_PHSVC_CLIENT client; + ET_PHSVC_API_GETWOW64THREADAPPDOMAIN in; + ET_PHSVC_API_GETWOW64THREADAPPDOMAIN out; + ULONG bufferSize; + PVOID buffer; + + if (!PhPluginQueryPhSvc(&client)) + return STATUS_FAIL_CHECK; + + in.i.ProcessId = HandleToUlong(ProcessId); + bufferSize = 0x1000; + + if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Data))) + return STATUS_FAIL_CHECK; + + status = PhPluginCallPhSvc(PluginInstance, EtPhSvcGetProcessUnloadedDllsApiNumber, &in, sizeof(in), &out, sizeof(out)); + + if (status == STATUS_BUFFER_OVERFLOW) + { + client.FreeHeap(buffer); + bufferSize = out.o.DataLength; + + if (!(buffer = client.CreateString(NULL, bufferSize, &in.i.Data))) + return STATUS_FAIL_CHECK; + + status = PhPluginCallPhSvc(PluginInstance, EtPhSvcGetProcessUnloadedDllsApiNumber, &in, sizeof(in), &out, sizeof(out)); + } + + if (NT_SUCCESS(status)) + { + *UnloadedDlls = PhCreateStringEx(buffer, out.o.DataLength); + } + + client.FreeHeap(buffer); + + return status; +} + +NTSTATUS DispatchGetProcessUnloadedDlls( + _In_ PPH_PLUGIN_PHSVC_REQUEST Request, + _In_ PET_PHSVC_API_GETWOW64THREADAPPDOMAIN In, + _Out_ PET_PHSVC_API_GETWOW64THREADAPPDOMAIN Out + ) +{ + NTSTATUS status; + PVOID dataBuffer; + ULONG capturedElementSize; + ULONG capturedElementCount; + PVOID capturedEventTrace = NULL; + PPH_STRING eventHexBufferText = NULL; + + if (!NT_SUCCESS(status = Request->ProbeBuffer(&In->i.Data, sizeof(WCHAR), FALSE, &dataBuffer))) + return status; + + status = PhGetProcessUnloadedDlls( + UlongToHandle(In->i.ProcessId), + &capturedEventTrace, + &capturedElementSize, + &capturedElementCount + ); + + if (!NT_SUCCESS(status)) + goto CleanupExit; + + eventHexBufferText = PhBufferToHexString( + capturedEventTrace, + capturedElementSize * capturedElementCount + ); + + if (!eventHexBufferText) + { + status = STATUS_UNSUCCESSFUL; + goto CleanupExit; + } + + memcpy(dataBuffer, eventHexBufferText->Buffer, min(eventHexBufferText->Length, In->i.Data.Length)); + Out->o.DataLength = (ULONG)eventHexBufferText->Length; + + if (In->i.Data.Length < eventHexBufferText->Length) + status = STATUS_BUFFER_OVERFLOW; + +CleanupExit: + if (eventHexBufferText) + PhDereferenceObject(eventHexBufferText); + + return status; +} + +VOID DispatchPhSvcRequest( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_PHSVC_REQUEST request = Parameter; + PVOID inBuffer; + + // InBuffer can alias OutBuffer, so make a copy of InBuffer. + inBuffer = PhAllocateCopy(request->InBuffer, request->InLength); + + switch (request->SubId) + { + case EtPhSvcGetProcessUnloadedDllsApiNumber: + request->ReturnStatus = DispatchGetProcessUnloadedDlls(request, inBuffer, request->OutBuffer); + break; + } + + PhFree(inBuffer); +} diff --git a/plugins/ExtendedTools/thrdact.c b/plugins/ExtendedTools/thrdact.c index bcbaef9d4e98..f30be0defb88 100644 --- a/plugins/ExtendedTools/thrdact.c +++ b/plugins/ExtendedTools/thrdact.c @@ -1,64 +1,64 @@ -/* - * Process Hacker Extended Tools - - * thread actions extensions - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -BOOLEAN EtUiCancelIoThread( - _In_ HWND hWnd, - _In_ PPH_THREAD_ITEM Thread - ) -{ - NTSTATUS status; - BOOLEAN cont = FALSE; - HANDLE threadHandle; - IO_STATUS_BLOCK isb; - - if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( - hWnd, - L"end", - L"I/O for the selected thread", - NULL, - FALSE - )) - cont = TRUE; - - if (!cont) - return FALSE; - - if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_TERMINATE, Thread->ThreadId))) - { - status = NtCancelSynchronousIoFile(threadHandle, NULL, &isb); - } - - if (status == STATUS_NOT_FOUND) - { - PhShowInformation(hWnd, L"There is no synchronous I/O to cancel."); - return FALSE; - } - else if (!NT_SUCCESS(status)) - { - PhShowStatus(hWnd, L"Unable to cancel synchronous I/O", status, 0); - return FALSE; - } - - return TRUE; -} +/* + * Process Hacker Extended Tools - + * thread actions extensions + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +BOOLEAN EtUiCancelIoThread( + _In_ HWND hWnd, + _In_ PPH_THREAD_ITEM Thread + ) +{ + NTSTATUS status; + BOOLEAN cont = FALSE; + HANDLE threadHandle; + IO_STATUS_BLOCK isb; + + if (!PhGetIntegerSetting(L"EnableWarnings") || PhShowConfirmMessage( + hWnd, + L"end", + L"I/O for the selected thread", + NULL, + FALSE + )) + cont = TRUE; + + if (!cont) + return FALSE; + + if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_TERMINATE, Thread->ThreadId))) + { + status = NtCancelSynchronousIoFile(threadHandle, NULL, &isb); + } + + if (status == STATUS_NOT_FOUND) + { + PhShowInformation(hWnd, L"There is no synchronous I/O to cancel."); + return FALSE; + } + else if (!NT_SUCCESS(status)) + { + PhShowStatus(hWnd, L"Unable to cancel synchronous I/O", status, 0); + return FALSE; + } + + return TRUE; +} diff --git a/plugins/ExtendedTools/treeext.c b/plugins/ExtendedTools/treeext.c index 20a4cc0847c2..4e702f0cd063 100644 --- a/plugins/ExtendedTools/treeext.c +++ b/plugins/ExtendedTools/treeext.c @@ -1,783 +1,950 @@ -/* - * Process Hacker Extended Tools - - * process and network tree support - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#define CINTERFACE -#define COBJMACROS -#include - -LONG EtpProcessTreeNewSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ); - -LONG EtpNetworkTreeNewSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ); - -typedef struct _COLUMN_INFO -{ - ULONG SubId; - PWSTR Text; - ULONG Width; - ULONG Alignment; - ULONG TextFlags; - BOOLEAN SortDescending; -} COLUMN_INFO, *PCOLUMN_INFO; - -static ULONG ProcessTreeListSortColumn; -static PH_SORT_ORDER ProcessTreeListSortOrder; - -static GUID IID_INetFwMgr_I = { 0xf7898af5, 0xcac4, 0x4632, { 0xa2, 0xec, 0xda, 0x06, 0xe5, 0x11, 0x1a, 0xf2 } }; -static GUID CLSID_NetFwMgr_I = { 0x304ce942, 0x6e39, 0x40d8, { 0x94, 0x3a, 0xb9, 0x13, 0xc4, 0x0c, 0x9c, 0xd4 } }; - -VOID EtpAddTreeNewColumn( - _In_ PPH_PLUGIN_TREENEW_INFORMATION TreeNewInfo, - _In_ ULONG SubId, - _In_ PWSTR Text, - _In_ ULONG Width, - _In_ ULONG Alignment, - _In_ ULONG TextFlags, - _In_ BOOLEAN SortDescending, - _In_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction - ) -{ - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.SortDescending = SortDescending; - column.Text = Text; - column.Width = Width; - column.Alignment = Alignment; - column.TextFlags = TextFlags; - - PhPluginAddTreeNewColumn( - PluginInstance, - TreeNewInfo->CmData, - &column, - SubId, - NULL, - SortFunction - ); -} - -VOID EtProcessTreeNewInitializing( - _In_ PVOID Parameter - ) -{ - static COLUMN_INFO columns[] = - { - { ETPRTNC_DISKREADS, L"Disk reads", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKWRITES, L"Disk writes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKREADBYTES, L"Disk read bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKWRITEBYTES, L"Disk write bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKTOTALBYTES, L"Disk total bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKREADSDELTA, L"Disk reads delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKWRITESDELTA, L"Disk writes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKREADBYTESDELTA, L"Disk read bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKWRITEBYTESDELTA, L"Disk write bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKTOTALBYTESDELTA, L"Disk total bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKRECEIVES, L"Network receives", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKSENDS, L"Network sends", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKRECEIVEBYTES, L"Network receive bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKSENDBYTES, L"Network send bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKTOTALBYTES, L"Network total bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKRECEIVESDELTA, L"Network receives delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKSENDSDELTA, L"Network sends delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKRECEIVEBYTESDELTA, L"Network receive bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKSENDBYTESDELTA, L"Network send bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKTOTALBYTESDELTA, L"Network total bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_HARDFAULTS, L"Hard faults", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_HARDFAULTSDELTA, L"Hard faults delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_PEAKTHREADS, L"Peak threads", 45, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_GPU, L"GPU", 45, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_GPUDEDICATEDBYTES, L"GPU dedicated bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_GPUSHAREDBYTES, L"GPU shared bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKREADRATE, L"Disk read rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKWRITERATE, L"Disk write rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_DISKTOTALRATE, L"Disk total rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKRECEIVERATE, L"Network receive rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKSENDRATE, L"Network send rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETPRTNC_NETWORKTOTALRATE, L"Network total rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE } - }; - - PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; - ULONG i; - - for (i = 0; i < sizeof(columns) / sizeof(COLUMN_INFO); i++) - { - EtpAddTreeNewColumn(treeNewInfo, columns[i].SubId, columns[i].Text, columns[i].Width, columns[i].Alignment, - columns[i].TextFlags, columns[i].SortDescending, EtpProcessTreeNewSortFunction); - } - - PhPluginEnableTreeNewNotify(PluginInstance, treeNewInfo->CmData); -} - -FLOAT EtpCalculateInclusiveGpuUsage( - _In_ PPH_PROCESS_NODE ProcessNode - ) -{ - FLOAT gpuUsage; - ULONG i; - - gpuUsage = EtGetProcessBlock(ProcessNode->ProcessItem)->GpuNodeUsage; - - for (i = 0; i < ProcessNode->Children->Count; i++) - { - gpuUsage += EtpCalculateInclusiveGpuUsage(ProcessNode->Children->Items[i]); - } - - return gpuUsage; -} - -VOID EtProcessTreeNewMessage( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - PPH_PROCESS_NODE processNode; - PET_PROCESS_BLOCK block; - - if (message->Message == TreeNewGetCellText) - { - PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; - PPH_STRING text; - - processNode = (PPH_PROCESS_NODE)getCellText->Node; - block = EtGetProcessBlock(processNode->ProcessItem); - - PhAcquireQueuedLockExclusive(&block->TextCacheLock); - - if (block->TextCacheValid[message->SubId]) - { - if (block->TextCache[message->SubId]) - getCellText->Text = block->TextCache[message->SubId]->sr; - } - else - { - text = NULL; - - switch (message->SubId) - { - case ETPRTNC_DISKREADS: - if (block->DiskReadCount != 0) - text = PhFormatUInt64(block->DiskReadCount, TRUE); - break; - case ETPRTNC_DISKWRITES: - if (block->DiskWriteCount != 0) - text = PhFormatUInt64(block->DiskWriteCount, TRUE); - break; - case ETPRTNC_DISKREADBYTES: - if (block->DiskReadRaw != 0) - text = PhFormatSize(block->DiskReadRaw, -1); - break; - case ETPRTNC_DISKWRITEBYTES: - if (block->DiskWriteRaw != 0) - text = PhFormatSize(block->DiskWriteRaw, -1); - break; - case ETPRTNC_DISKTOTALBYTES: - if (block->DiskReadRaw + block->DiskWriteRaw != 0) - text = PhFormatSize(block->DiskReadRaw + block->DiskWriteRaw, -1); - break; - case ETPRTNC_DISKREADSDELTA: - if (block->DiskReadDelta.Delta != 0) - text = PhFormatUInt64(block->DiskReadDelta.Delta, TRUE); - break; - case ETPRTNC_DISKWRITESDELTA: - if (block->DiskWriteDelta.Delta != 0) - text = PhFormatUInt64(block->DiskWriteDelta.Delta, TRUE); - break; - case ETPRTNC_DISKREADBYTESDELTA: - if (block->DiskReadRawDelta.Delta != 0) - text = PhFormatSize(block->DiskReadRawDelta.Delta, -1); - break; - case ETPRTNC_DISKWRITEBYTESDELTA: - if (block->DiskWriteRawDelta.Delta != 0) - text = PhFormatSize(block->DiskWriteRawDelta.Delta, -1); - break; - case ETPRTNC_DISKTOTALBYTESDELTA: - if (block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta != 0) - text = PhFormatSize(block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta, -1); - break; - case ETPRTNC_NETWORKRECEIVES: - if (block->NetworkReceiveCount != 0) - text = PhFormatUInt64(block->NetworkReceiveCount, TRUE); - break; - case ETPRTNC_NETWORKSENDS: - if (block->NetworkSendCount != 0) - text = PhFormatUInt64(block->NetworkSendCount, TRUE); - break; - case ETPRTNC_NETWORKRECEIVEBYTES: - if (block->NetworkReceiveRaw != 0) - text = PhFormatSize(block->NetworkReceiveRaw, -1); - break; - case ETPRTNC_NETWORKSENDBYTES: - if (block->NetworkSendRaw != 0) - text = PhFormatSize(block->NetworkSendRaw, -1); - break; - case ETPRTNC_NETWORKTOTALBYTES: - if (block->NetworkReceiveRaw + block->NetworkSendRaw != 0) - text = PhFormatSize(block->NetworkReceiveRaw + block->NetworkSendRaw, -1); - break; - case ETPRTNC_NETWORKRECEIVESDELTA: - if (block->NetworkReceiveDelta.Delta != 0) - text = PhFormatUInt64(block->NetworkReceiveDelta.Delta, TRUE); - break; - case ETPRTNC_NETWORKSENDSDELTA: - if (block->NetworkSendDelta.Delta != 0) - text = PhFormatUInt64(block->NetworkSendDelta.Delta, TRUE); - break; - case ETPRTNC_NETWORKRECEIVEBYTESDELTA: - if (block->NetworkReceiveRawDelta.Delta != 0) - text = PhFormatSize(block->NetworkReceiveRawDelta.Delta, -1); - break; - case ETPRTNC_NETWORKSENDBYTESDELTA: - if (block->NetworkSendRawDelta.Delta != 0) - text = PhFormatSize(block->NetworkSendRawDelta.Delta, -1); - break; - case ETPRTNC_NETWORKTOTALBYTESDELTA: - if (block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta != 0) - text = PhFormatSize(block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta, -1); - break; - case ETPRTNC_HARDFAULTS: - text = PhFormatUInt64(block->HardFaultsDelta.Value, TRUE); - break; - case ETPRTNC_HARDFAULTSDELTA: - if (block->HardFaultsDelta.Delta != 0) - text = PhFormatUInt64(block->HardFaultsDelta.Delta, TRUE); - break; - case ETPRTNC_PEAKTHREADS: - text = PhFormatUInt64(block->ProcessItem->PeakNumberOfThreads, TRUE); - break; - case ETPRTNC_GPU: - { - FLOAT gpuUsage; - - if (!PhGetIntegerSetting(L"PropagateCpuUsage") || processNode->Node.Expanded || ProcessTreeListSortOrder != NoSortOrder) - { - gpuUsage = block->GpuNodeUsage * 100; - } - else - { - gpuUsage = EtpCalculateInclusiveGpuUsage(processNode) * 100; - } - - if (gpuUsage >= 0.01) - { - PH_FORMAT format; - - PhInitFormatF(&format, gpuUsage, 2); - text = PhFormat(&format, 1, 0); - } - } - break; - case ETPRTNC_GPUDEDICATEDBYTES: - if (block->GpuDedicatedUsage != 0) - text = PhFormatSize(block->GpuDedicatedUsage, -1); - break; - case ETPRTNC_GPUSHAREDBYTES: - if (block->GpuSharedUsage != 0) - text = PhFormatSize(block->GpuSharedUsage, -1); - break; - case ETPRTNC_DISKREADRATE: - if (block->DiskReadRawDelta.Delta != 0) - EtFormatRate(block->DiskReadRawDelta.Delta, &text, NULL); - break; - case ETPRTNC_DISKWRITERATE: - if (block->DiskWriteRawDelta.Delta != 0) - EtFormatRate(block->DiskWriteRawDelta.Delta, &text, NULL); - break; - case ETPRTNC_DISKTOTALRATE: - if (block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta != 0) - EtFormatRate(block->DiskReadRawDelta.Delta + block->DiskWriteRawDelta.Delta, &text, NULL); - break; - case ETPRTNC_NETWORKRECEIVERATE: - if (block->NetworkReceiveRawDelta.Delta != 0) - EtFormatRate(block->NetworkReceiveRawDelta.Delta, &text, NULL); - break; - case ETPRTNC_NETWORKSENDRATE: - if (block->NetworkSendRawDelta.Delta != 0) - EtFormatRate(block->NetworkSendRawDelta.Delta, &text, NULL); - break; - case ETPRTNC_NETWORKTOTALRATE: - if (block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta != 0) - EtFormatRate(block->NetworkReceiveRawDelta.Delta + block->NetworkSendRawDelta.Delta, &text, NULL); - break; - } - - if (text) - { - getCellText->Text = text->sr; - } - - PhMoveReference(&block->TextCache[message->SubId], text); - block->TextCacheValid[message->SubId] = TRUE; - } - - PhReleaseQueuedLockExclusive(&block->TextCacheLock); - } - else if (message->Message == TreeNewSortChanged) - { - TreeNew_GetSort(message->TreeNewHandle, &ProcessTreeListSortColumn, &ProcessTreeListSortOrder); - } - else if (message->Message == TreeNewNodeExpanding) - { - processNode = message->Parameter1; - block = EtGetProcessBlock(processNode->ProcessItem); - - if (PhGetIntegerSetting(L"PropagateCpuUsage")) - block->TextCacheValid[ETPRTNC_GPU] = FALSE; - } -} - -LONG EtpProcessTreeNewSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - LONG result; - PPH_PROCESS_NODE node1 = Node1; - PPH_PROCESS_NODE node2 = Node2; - PET_PROCESS_BLOCK block1; - PET_PROCESS_BLOCK block2; - - block1 = EtGetProcessBlock(node1->ProcessItem); - block2 = EtGetProcessBlock(node2->ProcessItem); - - result = 0; - - switch (SubId) - { - case ETPRTNC_DISKREADS: - result = uint64cmp(block1->DiskReadCount, block2->DiskReadCount); - break; - case ETPRTNC_DISKWRITES: - result = uint64cmp(block1->DiskWriteCount, block2->DiskWriteCount); - break; - case ETPRTNC_DISKREADBYTES: - result = uint64cmp(block1->DiskReadRaw, block2->DiskReadRaw); - break; - case ETPRTNC_DISKWRITEBYTES: - result = uint64cmp(block1->DiskWriteRaw, block2->DiskWriteRaw); - break; - case ETPRTNC_DISKTOTALBYTES: - result = uint64cmp(block1->DiskReadRaw + block1->DiskWriteRaw, block2->DiskReadRaw + block2->DiskWriteRaw); - break; - case ETPRTNC_DISKREADSDELTA: - result = uint64cmp(block1->DiskReadDelta.Delta, block2->DiskReadDelta.Delta); - break; - case ETPRTNC_DISKWRITESDELTA: - result = uint64cmp(block1->DiskWriteDelta.Delta, block2->DiskWriteDelta.Delta); - break; - case ETPRTNC_DISKREADBYTESDELTA: - result = uint64cmp(block1->DiskReadRawDelta.Delta, block2->DiskReadRawDelta.Delta); - break; - case ETPRTNC_DISKWRITEBYTESDELTA: - result = uint64cmp(block1->DiskWriteRawDelta.Delta, block2->DiskWriteRawDelta.Delta); - break; - case ETPRTNC_DISKTOTALBYTESDELTA: - result = uint64cmp(block1->DiskReadRawDelta.Delta + block1->DiskWriteRawDelta.Delta, block2->DiskReadRawDelta.Delta + block2->DiskWriteRawDelta.Delta); - break; - case ETPRTNC_NETWORKRECEIVES: - result = uint64cmp(block1->NetworkReceiveCount, block2->NetworkReceiveCount); - break; - case ETPRTNC_NETWORKSENDS: - result = uint64cmp(block1->NetworkSendCount, block2->NetworkSendCount); - break; - case ETPRTNC_NETWORKRECEIVEBYTES: - result = uint64cmp(block1->NetworkReceiveRaw, block2->NetworkReceiveRaw); - break; - case ETPRTNC_NETWORKSENDBYTES: - result = uint64cmp(block1->NetworkSendRaw, block2->NetworkSendRaw); - break; - case ETPRTNC_NETWORKTOTALBYTES: - result = uint64cmp(block1->NetworkReceiveRaw + block1->NetworkSendRaw, block2->NetworkReceiveRaw + block2->NetworkSendRaw); - break; - case ETPRTNC_NETWORKRECEIVESDELTA: - result = uint64cmp(block1->NetworkReceiveDelta.Delta, block2->NetworkReceiveDelta.Delta); - break; - case ETPRTNC_NETWORKSENDSDELTA: - result = uint64cmp(block1->NetworkSendDelta.Delta, block2->NetworkSendDelta.Delta); - break; - case ETPRTNC_NETWORKRECEIVEBYTESDELTA: - result = uint64cmp(block1->NetworkReceiveRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta); - break; - case ETPRTNC_NETWORKSENDBYTESDELTA: - result = uint64cmp(block1->NetworkSendRawDelta.Delta, block2->NetworkSendRawDelta.Delta); - break; - case ETPRTNC_NETWORKTOTALBYTESDELTA: - result = uint64cmp(block1->NetworkReceiveRawDelta.Delta + block1->NetworkSendRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta + block2->NetworkSendRawDelta.Delta); - break; - case ETPRTNC_HARDFAULTS: - result = uintcmp(block1->HardFaultsDelta.Value, block2->HardFaultsDelta.Value); - break; - case ETPRTNC_HARDFAULTSDELTA: - result = uintcmp(block1->HardFaultsDelta.Delta, block2->HardFaultsDelta.Delta); - break; - case ETPRTNC_PEAKTHREADS: - result = uintcmp(block1->ProcessItem->PeakNumberOfThreads, block2->ProcessItem->PeakNumberOfThreads); - break; - case ETPRTNC_GPU: - result = singlecmp(block1->GpuNodeUsage, block2->GpuNodeUsage); - break; - case ETPRTNC_GPUDEDICATEDBYTES: - result = uint64cmp(block1->GpuDedicatedUsage, block2->GpuDedicatedUsage); - break; - case ETPRTNC_GPUSHAREDBYTES: - result = uint64cmp(block1->GpuSharedUsage, block2->GpuSharedUsage); - break; - case ETPRTNC_DISKREADRATE: - result = uint64cmp(block1->DiskReadRawDelta.Delta, block2->DiskReadRawDelta.Delta); - break; - case ETPRTNC_DISKWRITERATE: - result = uint64cmp(block1->DiskWriteRawDelta.Delta, block2->DiskWriteRawDelta.Delta); - break; - case ETPRTNC_DISKTOTALRATE: - result = uint64cmp(block1->DiskReadRawDelta.Delta + block1->DiskWriteRawDelta.Delta, block2->DiskReadRawDelta.Delta + block2->DiskWriteRawDelta.Delta); - break; - case ETPRTNC_NETWORKRECEIVERATE: - result = uint64cmp(block1->NetworkReceiveRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta); - break; - case ETPRTNC_NETWORKSENDRATE: - result = uint64cmp(block1->NetworkSendRawDelta.Delta, block2->NetworkSendRawDelta.Delta); - break; - case ETPRTNC_NETWORKTOTALRATE: - result = uint64cmp(block1->NetworkReceiveRawDelta.Delta + block1->NetworkSendRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta + block2->NetworkSendRawDelta.Delta); - break; - } - - return result; -} - -VOID EtNetworkTreeNewInitializing( - _In_ PVOID Parameter - ) -{ - static COLUMN_INFO columns[] = - { - { ETNETNC_RECEIVES, L"Receives", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_SENDS, L"Sends", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_RECEIVEBYTES, L"Receive bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_SENDBYTES, L"Send bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_TOTALBYTES, L"Total bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_RECEIVESDELTA, L"Receives delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_SENDSDELTA, L"Sends delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_RECEIVEBYTESDELTA, L"Receive bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_SENDBYTESDELTA, L"Send bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_TOTALBYTESDELTA, L"Total bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_FIREWALLSTATUS, L"Firewall status", 170, PH_ALIGN_LEFT, 0, FALSE }, - { ETNETNC_RECEIVERATE, L"Receive rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_SENDRATE, L"Send rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, - { ETNETNC_TOTALRATE, L"Total rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE } - }; - - PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; - ULONG i; - - for (i = 0; i < sizeof(columns) / sizeof(COLUMN_INFO); i++) - { - EtpAddTreeNewColumn(treeNewInfo, columns[i].SubId, columns[i].Text, columns[i].Width, columns[i].Alignment, - columns[i].TextFlags, columns[i].SortDescending, EtpNetworkTreeNewSortFunction); - } -} - -VOID EtpUpdateFirewallStatus( - _Inout_ PET_NETWORK_BLOCK Block - ) -{ - if (!Block->FirewallStatusValid) - { - Block->FirewallStatus = EtQueryFirewallStatus(Block->NetworkItem); - Block->FirewallStatusValid = TRUE; - } -} - -VOID EtNetworkTreeNewMessage( - _In_ PVOID Parameter - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - - if (message->Message == TreeNewGetCellText) - { - PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; - PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)getCellText->Node; - PET_NETWORK_BLOCK block; - PPH_STRING text; - - block = EtGetNetworkBlock(networkNode->NetworkItem); - - PhAcquireQueuedLockExclusive(&block->TextCacheLock); - - if (block->TextCacheValid[message->SubId]) - { - if (block->TextCache[message->SubId]) - getCellText->Text = block->TextCache[message->SubId]->sr; - } - else - { - text = NULL; - - switch (message->SubId) - { - case ETNETNC_RECEIVES: - if (block->ReceiveCount != 0) - text = PhFormatUInt64(block->ReceiveCount, TRUE); - break; - case ETNETNC_SENDS: - if (block->SendCount != 0) - text = PhFormatUInt64(block->SendCount, TRUE); - break; - case ETNETNC_RECEIVEBYTES: - if (block->ReceiveRaw != 0) - text = PhFormatSize(block->ReceiveRaw, -1); - break; - case ETNETNC_SENDBYTES: - if (block->SendRaw != 0) - text = PhFormatSize(block->SendRaw, -1); - break; - case ETNETNC_TOTALBYTES: - if (block->ReceiveRaw + block->SendRaw != 0) - text = PhFormatSize(block->ReceiveRaw + block->SendRaw, -1); - break; - case ETNETNC_RECEIVESDELTA: - if (block->ReceiveDelta.Delta != 0) - text = PhFormatUInt64(block->ReceiveDelta.Delta, TRUE); - break; - case ETNETNC_SENDSDELTA: - if (block->SendDelta.Delta != 0) - text = PhFormatUInt64(block->SendDelta.Delta, TRUE); - break; - case ETNETNC_RECEIVEBYTESDELTA: - if (block->ReceiveRawDelta.Delta != 0) - text = PhFormatSize(block->ReceiveRawDelta.Delta, -1); - break; - case ETNETNC_SENDBYTESDELTA: - if (block->SendRawDelta.Delta != 0) - text = PhFormatSize(block->SendRawDelta.Delta, -1); - break; - case ETNETNC_TOTALBYTESDELTA: - if (block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta != 0) - text = PhFormatSize(block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta, -1); - break; - case ETNETNC_FIREWALLSTATUS: - { - static PPH_STRING strings[FirewallMaximumStatus]; - static PH_INITONCE initOnce = PH_INITONCE_INIT; - - if (PhBeginInitOnce(&initOnce)) - { - strings[FirewallUnknownStatus] = NULL; - strings[FirewallAllowedNotRestricted] = PhCreateString(L"Allowed, not restricted"); - strings[FirewallAllowedRestricted] = PhCreateString(L"Allowed, restricted"); - strings[FirewallNotAllowedNotRestricted] = PhCreateString(L"Not allowed, not restricted"); - strings[FirewallNotAllowedRestricted] = PhCreateString(L"Not allowed, restricted"); - PhEndInitOnce(&initOnce); - } - - EtpUpdateFirewallStatus(block); - - if (block->FirewallStatus < FirewallMaximumStatus) - PhSetReference(&text, strings[block->FirewallStatus]); - } - break; - case ETNETNC_RECEIVERATE: - if (block->ReceiveRawDelta.Delta != 0) - EtFormatRate(block->ReceiveRawDelta.Delta, &text, NULL); - break; - case ETNETNC_SENDRATE: - if (block->SendRawDelta.Delta != 0) - EtFormatRate(block->SendRawDelta.Delta, &text, NULL); - break; - case ETNETNC_TOTALRATE: - if (block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta != 0) - EtFormatRate(block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta, &text, NULL); - break; - } - - if (text) - { - getCellText->Text = text->sr; - } - - PhMoveReference(&block->TextCache[message->SubId], text); - block->TextCacheValid[message->SubId] = TRUE; - } - - PhReleaseQueuedLockExclusive(&block->TextCacheLock); - } -} - -LONG EtpNetworkTreeNewSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - LONG result; - PPH_NETWORK_NODE node1 = Node1; - PPH_NETWORK_NODE node2 = Node2; - PET_NETWORK_BLOCK block1; - PET_NETWORK_BLOCK block2; - - block1 = EtGetNetworkBlock(node1->NetworkItem); - block2 = EtGetNetworkBlock(node2->NetworkItem); - - result = 0; - - switch (SubId) - { - case ETNETNC_RECEIVES: - result = uint64cmp(block1->ReceiveCount, block2->ReceiveCount); - break; - case ETNETNC_SENDS: - result = uint64cmp(block1->SendCount, block2->SendCount); - break; - case ETNETNC_RECEIVEBYTES: - result = uint64cmp(block1->ReceiveRaw, block2->ReceiveRaw); - break; - case ETNETNC_SENDBYTES: - result = uint64cmp(block1->SendRaw, block2->SendRaw); - break; - case ETNETNC_TOTALBYTES: - result = uint64cmp(block1->ReceiveRaw + block1->SendRaw, block2->ReceiveRaw + block2->SendRaw); - break; - case ETNETNC_RECEIVESDELTA: - result = uint64cmp(block1->ReceiveDelta.Delta, block2->ReceiveDelta.Delta); - break; - case ETNETNC_SENDSDELTA: - result = uint64cmp(block1->SendDelta.Delta, block2->SendDelta.Delta); - break; - case ETNETNC_RECEIVEBYTESDELTA: - result = uint64cmp(block1->ReceiveRawDelta.Delta, block2->ReceiveRawDelta.Delta); - break; - case ETNETNC_SENDBYTESDELTA: - result = uint64cmp(block1->SendRawDelta.Delta, block2->SendRawDelta.Delta); - break; - case ETNETNC_TOTALBYTESDELTA: - result = uint64cmp(block1->ReceiveRawDelta.Delta + block1->SendRawDelta.Delta, block2->ReceiveRawDelta.Delta + block2->SendRawDelta.Delta); - break; - case ETNETNC_FIREWALLSTATUS: - EtpUpdateFirewallStatus(block1); - EtpUpdateFirewallStatus(block2); - result = intcmp(block1->FirewallStatus, block2->FirewallStatus); - break; - case ETNETNC_RECEIVERATE: - result = uint64cmp(block1->ReceiveRawDelta.Delta, block2->ReceiveRawDelta.Delta); - break; - case ETNETNC_SENDRATE: - result = uint64cmp(block1->SendRawDelta.Delta, block2->SendRawDelta.Delta); - break; - case ETNETNC_TOTALRATE: - result = uint64cmp(block1->ReceiveRawDelta.Delta + block1->SendRawDelta.Delta, block2->ReceiveRawDelta.Delta + block2->SendRawDelta.Delta); - break; - } - - return result; -} - -ET_FIREWALL_STATUS EtQueryFirewallStatus( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - static INetFwMgr* manager = NULL; - ET_FIREWALL_STATUS result; - PPH_PROCESS_ITEM processItem; - BSTR imageFileNameBStr; - BSTR localAddressBStr; - VARIANT allowed; - VARIANT restricted; - - if (!manager) - { - if (!SUCCEEDED(CoCreateInstance(&CLSID_NetFwMgr_I, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr_I, &manager))) - return FirewallUnknownStatus; - - if (!manager) - return FirewallUnknownStatus; - } - - processItem = PhReferenceProcessItem(NetworkItem->ProcessId); - - if (!processItem) - return FirewallUnknownStatus; - - if (!processItem->FileName) - { - PhDereferenceObject(processItem); - return FirewallUnknownStatus; - } - - result = FirewallUnknownStatus; - - if (imageFileNameBStr = SysAllocStringLen(processItem->FileName->Buffer, (ULONG)processItem->FileName->Length / sizeof(WCHAR))) - { - localAddressBStr = NULL; - - if (!PhIsNullIpAddress(&NetworkItem->LocalEndpoint.Address)) - localAddressBStr = SysAllocString(NetworkItem->LocalAddressString); - - if (SUCCEEDED(INetFwMgr_IsPortAllowed( - manager, - imageFileNameBStr, - (NetworkItem->ProtocolType & PH_IPV6_NETWORK_TYPE) ? NET_FW_IP_VERSION_V6 : NET_FW_IP_VERSION_V4, - NetworkItem->LocalEndpoint.Port, - localAddressBStr, - (NetworkItem->ProtocolType & PH_UDP_PROTOCOL_TYPE) ? NET_FW_IP_PROTOCOL_UDP : NET_FW_IP_PROTOCOL_TCP, - &allowed, - &restricted - ))) - { - if (allowed.boolVal) - { - if (restricted.boolVal) - result = FirewallAllowedRestricted; - else - result = FirewallAllowedNotRestricted; - } - else - { - if (restricted.boolVal) - result = FirewallNotAllowedRestricted; - else - result = FirewallNotAllowedNotRestricted; - } - } - - if (localAddressBStr) - SysFreeString(localAddressBStr); - - SysFreeString(imageFileNameBStr); - } - - PhDereferenceObject(processItem); - - return result; -} +/* + * Process Hacker Extended Tools - + * process and network tree support + * + * Copyright (C) 2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include + +LONG EtpProcessTreeNewSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ); + +LONG EtpNetworkTreeNewSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ); + +typedef struct _COLUMN_INFO +{ + ULONG SubId; + PWSTR Text; + ULONG Width; + ULONG Alignment; + ULONG TextFlags; + BOOLEAN SortDescending; +} COLUMN_INFO, *PCOLUMN_INFO; + +typedef enum _PHP_AGGREGATE_TYPE +{ + AggregateTypeFloat, + AggregateTypeInt32, + AggregateTypeInt64, + AggregateTypeIntPtr +} PHP_AGGREGATE_TYPE; + +typedef enum _PHP_AGGREGATE_LOCATION +{ + AggregateLocationProcessNode, + AggregateLocationProcessItem +} PHP_AGGREGATE_LOCATION; + +static ULONG ProcessTreeListSortColumn; +static PH_SORT_ORDER ProcessTreeListSortOrder; + +static GUID IID_INetFwMgr_I = { 0xf7898af5, 0xcac4, 0x4632, { 0xa2, 0xec, 0xda, 0x06, 0xe5, 0x11, 0x1a, 0xf2 } }; +static GUID CLSID_NetFwMgr_I = { 0x304ce942, 0x6e39, 0x40d8, { 0x94, 0x3a, 0xb9, 0x13, 0xc4, 0x0c, 0x9c, 0xd4 } }; + +VOID EtpAddTreeNewColumn( + _In_ PPH_PLUGIN_TREENEW_INFORMATION TreeNewInfo, + _In_ ULONG SubId, + _In_ PWSTR Text, + _In_ ULONG Width, + _In_ ULONG Alignment, + _In_ ULONG TextFlags, + _In_ BOOLEAN SortDescending, + _In_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction + ) +{ + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.SortDescending = SortDescending; + column.Text = Text; + column.Width = Width; + column.Alignment = Alignment; + column.TextFlags = TextFlags; + + PhPluginAddTreeNewColumn( + PluginInstance, + TreeNewInfo->CmData, + &column, + SubId, + NULL, + SortFunction + ); +} + +VOID EtProcessTreeNewInitializing( + _In_ PVOID Parameter + ) +{ + static COLUMN_INFO columns[] = + { + { ETPRTNC_DISKREADS, L"Disk reads", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKWRITES, L"Disk writes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKREADBYTES, L"Disk read bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKWRITEBYTES, L"Disk write bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKTOTALBYTES, L"Disk total bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKREADSDELTA, L"Disk reads delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKWRITESDELTA, L"Disk writes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKREADBYTESDELTA, L"Disk read bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKWRITEBYTESDELTA, L"Disk write bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKTOTALBYTESDELTA, L"Disk total bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKRECEIVES, L"Network receives", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKSENDS, L"Network sends", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKRECEIVEBYTES, L"Network receive bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKSENDBYTES, L"Network send bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKTOTALBYTES, L"Network total bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKRECEIVESDELTA, L"Network receives delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKSENDSDELTA, L"Network sends delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKRECEIVEBYTESDELTA, L"Network receive bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKSENDBYTESDELTA, L"Network send bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKTOTALBYTESDELTA, L"Network total bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_HARDFAULTS, L"Hard faults", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_HARDFAULTSDELTA, L"Hard faults delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_PEAKTHREADS, L"Peak threads", 45, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_GPU, L"GPU", 45, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_GPUDEDICATEDBYTES, L"GPU dedicated bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_GPUSHAREDBYTES, L"GPU shared bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKREADRATE, L"Disk read rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKWRITERATE, L"Disk write rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_DISKTOTALRATE, L"Disk total rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKRECEIVERATE, L"Network receive rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKSENDRATE, L"Network send rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETPRTNC_NETWORKTOTALRATE, L"Network total rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE } + }; + + PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; + ULONG i; + + for (i = 0; i < sizeof(columns) / sizeof(COLUMN_INFO); i++) + { + EtpAddTreeNewColumn(treeNewInfo, columns[i].SubId, columns[i].Text, columns[i].Width, columns[i].Alignment, + columns[i].TextFlags, columns[i].SortDescending, EtpProcessTreeNewSortFunction); + } + + PhPluginEnableTreeNewNotify(PluginInstance, treeNewInfo->CmData); +} + +// Copied from ProcessHacker\proctree.c +FORCEINLINE PVOID PhpFieldForAggregate( + _In_ PPH_PROCESS_NODE ProcessNode, + _In_ PHP_AGGREGATE_LOCATION Location, + _In_ SIZE_T FieldOffset + ) +{ + PVOID object; + + switch (Location) + { + case AggregateLocationProcessNode: + object = PhPluginGetObjectExtension(PluginInstance, ProcessNode->ProcessItem, EmProcessNodeType); + break; + case AggregateLocationProcessItem: + object = PhPluginGetObjectExtension(PluginInstance, ProcessNode->ProcessItem, EmProcessItemType); + break; + default: + PhRaiseStatus(STATUS_INVALID_PARAMETER); + } + + return PTR_ADD_OFFSET(object, FieldOffset); +} + +// Copied from ProcessHacker\proctree.c +FORCEINLINE VOID PhpAccumulateField( + _Inout_ PVOID Accumulator, + _In_ PVOID Value, + _In_ PHP_AGGREGATE_TYPE Type + ) +{ + switch (Type) + { + case AggregateTypeFloat: + *(PFLOAT)Accumulator += *(PFLOAT)Value; + break; + case AggregateTypeInt32: + *(PULONG)Accumulator += *(PULONG)Value; + break; + case AggregateTypeInt64: + *(PULONG64)Accumulator += *(PULONG64)Value; + break; + case AggregateTypeIntPtr: + *(PULONG_PTR)Accumulator += *(PULONG_PTR)Value; + break; + } +} + +// Copied from ProcessHacker\proctree.c +static VOID PhpAggregateField( + _In_ PPH_PROCESS_NODE ProcessNode, + _In_ PHP_AGGREGATE_TYPE Type, + _In_ PHP_AGGREGATE_LOCATION Location, + _In_ SIZE_T FieldOffset, + _Inout_ PVOID AggregatedValue + ) +{ + ULONG i; + + PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type); + + for (i = 0; i < ProcessNode->Children->Count; i++) + { + PhpAggregateField(ProcessNode->Children->Items[i], Type, Location, FieldOffset, AggregatedValue); + } +} + +// Copied from ProcessHacker\proctree.c +static VOID PhpAggregateFieldIfNeeded( + _In_ PPH_PROCESS_NODE ProcessNode, + _In_ PHP_AGGREGATE_TYPE Type, + _In_ PHP_AGGREGATE_LOCATION Location, + _In_ SIZE_T FieldOffset, + _Inout_ PVOID AggregatedValue + ) +{ + if (!PhGetIntegerSetting(L"PropagateCpuUsage") || ProcessNode->Node.Expanded || ProcessTreeListSortOrder != NoSortOrder) + { + PhpAccumulateField(AggregatedValue, PhpFieldForAggregate(ProcessNode, Location, FieldOffset), Type); + } + else + { + PhpAggregateField(ProcessNode, Type, Location, FieldOffset, AggregatedValue); + } +} + +VOID EtProcessTreeNewMessage( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + PPH_PROCESS_NODE processNode; + PET_PROCESS_BLOCK block; + + if (message->Message == TreeNewGetCellText) + { + PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; + PPH_STRING text; + + processNode = (PPH_PROCESS_NODE)getCellText->Node; + block = EtGetProcessBlock(processNode->ProcessItem); + + PhAcquireQueuedLockExclusive(&block->TextCacheLock); + + if (block->TextCacheValid[message->SubId]) + { + if (block->TextCache[message->SubId]) + getCellText->Text = block->TextCache[message->SubId]->sr; + } + else + { + text = NULL; + + switch (message->SubId) + { + case ETPRTNC_DISKREADS: + if (block->DiskReadCount != 0) + text = PhFormatUInt64(block->DiskReadCount, TRUE); + break; + case ETPRTNC_DISKWRITES: + if (block->DiskWriteCount != 0) + text = PhFormatUInt64(block->DiskWriteCount, TRUE); + break; + case ETPRTNC_DISKREADBYTES: + if (block->DiskReadRaw != 0) + text = PhFormatSize(block->DiskReadRaw, ULONG_MAX); + break; + case ETPRTNC_DISKWRITEBYTES: + if (block->DiskWriteRaw != 0) + text = PhFormatSize(block->DiskWriteRaw, ULONG_MAX); + break; + case ETPRTNC_DISKTOTALBYTES: + { + ULONG64 number = 0; + + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, DiskReadRaw), &number); + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, DiskWriteRaw), &number); + + if (number != 0) + text = PhFormatSize(number, ULONG_MAX); + } + break; + case ETPRTNC_DISKREADSDELTA: + if (block->DiskReadDelta.Delta != 0) + text = PhFormatUInt64(block->DiskReadDelta.Delta, TRUE); + break; + case ETPRTNC_DISKWRITESDELTA: + if (block->DiskWriteDelta.Delta != 0) + text = PhFormatUInt64(block->DiskWriteDelta.Delta, TRUE); + break; + case ETPRTNC_DISKREADBYTESDELTA: + if (block->DiskReadRawDelta.Delta != 0) + text = PhFormatSize(block->DiskReadRawDelta.Delta, ULONG_MAX); + break; + case ETPRTNC_DISKWRITEBYTESDELTA: + if (block->DiskWriteRawDelta.Delta != 0) + text = PhFormatSize(block->DiskWriteRawDelta.Delta, ULONG_MAX); + break; + case ETPRTNC_DISKTOTALBYTESDELTA: + { + ULONG64 number = 0; + + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, DiskReadRawDelta), &number); + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, DiskWriteRawDelta), &number); + + if (number != 0) + text = PhFormatSize(number, ULONG_MAX); + } + break; + case ETPRTNC_NETWORKRECEIVES: + if (block->NetworkReceiveCount != 0) + text = PhFormatUInt64(block->NetworkReceiveCount, TRUE); + break; + case ETPRTNC_NETWORKSENDS: + if (block->NetworkSendCount != 0) + text = PhFormatUInt64(block->NetworkSendCount, TRUE); + break; + case ETPRTNC_NETWORKRECEIVEBYTES: + if (block->NetworkReceiveRaw != 0) + text = PhFormatSize(block->NetworkReceiveRaw, ULONG_MAX); + break; + case ETPRTNC_NETWORKSENDBYTES: + if (block->NetworkSendRaw != 0) + text = PhFormatSize(block->NetworkSendRaw, ULONG_MAX); + break; + case ETPRTNC_NETWORKTOTALBYTES: + { + ULONG64 number = 0; + + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, NetworkReceiveRaw), &number); + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, NetworkSendRaw), &number); + + if (number != 0) + text = PhFormatSize(number, ULONG_MAX); + } + break; + case ETPRTNC_NETWORKRECEIVESDELTA: + if (block->NetworkReceiveDelta.Delta != 0) + text = PhFormatUInt64(block->NetworkReceiveDelta.Delta, TRUE); + break; + case ETPRTNC_NETWORKSENDSDELTA: + if (block->NetworkSendDelta.Delta != 0) + text = PhFormatUInt64(block->NetworkSendDelta.Delta, TRUE); + break; + case ETPRTNC_NETWORKRECEIVEBYTESDELTA: + if (block->NetworkReceiveRawDelta.Delta != 0) + text = PhFormatSize(block->NetworkReceiveRawDelta.Delta, ULONG_MAX); + break; + case ETPRTNC_NETWORKSENDBYTESDELTA: + if (block->NetworkSendRawDelta.Delta != 0) + text = PhFormatSize(block->NetworkSendRawDelta.Delta, ULONG_MAX); + break; + case ETPRTNC_NETWORKTOTALBYTESDELTA: + { + ULONG64 number = 0; + + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, NetworkReceiveRawDelta.Delta), &number); + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, NetworkSendRawDelta.Delta), &number); + + if (number != 0) + text = PhFormatSize(number, ULONG_MAX); + } + break; + case ETPRTNC_HARDFAULTS: + text = PhFormatUInt64(block->HardFaultsDelta.Value, TRUE); + break; + case ETPRTNC_HARDFAULTSDELTA: + if (block->HardFaultsDelta.Delta != 0) + text = PhFormatUInt64(block->HardFaultsDelta.Delta, TRUE); + break; + case ETPRTNC_PEAKTHREADS: + text = PhFormatUInt64(block->ProcessItem->PeakNumberOfThreads, TRUE); + break; + case ETPRTNC_GPU: + { + FLOAT gpuUsage = 0; + + PhpAggregateFieldIfNeeded( + processNode, + AggregateTypeFloat, + AggregateLocationProcessItem, + FIELD_OFFSET(ET_PROCESS_BLOCK, GpuNodeUsage), + &gpuUsage + ); + gpuUsage *= 100; + + if (gpuUsage >= 0.01) + { + PH_FORMAT format; + + PhInitFormatF(&format, gpuUsage, 2); + text = PhFormat(&format, 1, 0); + } + } + break; + case ETPRTNC_GPUDEDICATEDBYTES: + { + ULONG64 gpuDedicatedUsage = 0; + + PhpAggregateFieldIfNeeded( + processNode, + AggregateTypeInt64, + AggregateLocationProcessItem, + FIELD_OFFSET(ET_PROCESS_BLOCK, GpuDedicatedUsage), + &gpuDedicatedUsage + ); + + if (gpuDedicatedUsage != 0) + text = PhFormatSize(gpuDedicatedUsage, ULONG_MAX); + } + break; + case ETPRTNC_GPUSHAREDBYTES: + { + ULONG64 gpuSharedUsage = 0; + + PhpAggregateFieldIfNeeded( + processNode, + AggregateTypeInt64, + AggregateLocationProcessItem, + FIELD_OFFSET(ET_PROCESS_BLOCK, GpuSharedUsage), + &gpuSharedUsage + ); + + if (gpuSharedUsage != 0) + text = PhFormatSize(gpuSharedUsage, ULONG_MAX); + } + break; + case ETPRTNC_DISKREADRATE: + if (block->DiskReadRawDelta.Delta != 0) + EtFormatRate(block->DiskReadRawDelta.Delta, &text, NULL); + break; + case ETPRTNC_DISKWRITERATE: + if (block->DiskWriteRawDelta.Delta != 0) + EtFormatRate(block->DiskWriteRawDelta.Delta, &text, NULL); + break; + case ETPRTNC_DISKTOTALRATE: + { + ULONG64 number = 0; + + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, DiskReadRawDelta.Delta), &number); + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, DiskWriteRawDelta.Delta), &number); + + if (number != 0) + EtFormatRate(number, &text, NULL); + } + break; + case ETPRTNC_NETWORKRECEIVERATE: + if (block->NetworkReceiveRawDelta.Delta != 0) + EtFormatRate(block->NetworkReceiveRawDelta.Delta, &text, NULL); + break; + case ETPRTNC_NETWORKSENDRATE: + if (block->NetworkSendRawDelta.Delta != 0) + EtFormatRate(block->NetworkSendRawDelta.Delta, &text, NULL); + break; + case ETPRTNC_NETWORKTOTALRATE: + { + ULONG64 number = 0; + + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, NetworkReceiveRawDelta.Delta), &number); + PhpAggregateFieldIfNeeded(processNode, AggregateTypeInt64, AggregateLocationProcessItem, FIELD_OFFSET(ET_PROCESS_BLOCK, NetworkSendRawDelta.Delta), &number); + + if (number != 0) + EtFormatRate(number, &text, NULL); + } + break; + } + + if (text) + { + getCellText->Text = text->sr; + } + + PhMoveReference(&block->TextCache[message->SubId], text); + block->TextCacheValid[message->SubId] = TRUE; + } + + PhReleaseQueuedLockExclusive(&block->TextCacheLock); + } + else if (message->Message == TreeNewSortChanged) + { + TreeNew_GetSort(message->TreeNewHandle, &ProcessTreeListSortColumn, &ProcessTreeListSortOrder); + } + else if (message->Message == TreeNewNodeExpanding) + { + processNode = message->Parameter1; + block = EtGetProcessBlock(processNode->ProcessItem); + + if (PhGetIntegerSetting(L"PropagateCpuUsage")) + { + block->TextCacheValid[ETPRTNC_DISKTOTALBYTES] = FALSE; + block->TextCacheValid[ETPRTNC_NETWORKTOTALBYTES] = FALSE; + + block->TextCacheValid[ETPRTNC_DISKTOTALBYTESDELTA] = FALSE; + block->TextCacheValid[ETPRTNC_NETWORKTOTALBYTESDELTA] = FALSE; + + block->TextCacheValid[ETPRTNC_GPU] = FALSE; + block->TextCacheValid[ETPRTNC_GPUDEDICATEDBYTES] = FALSE; + block->TextCacheValid[ETPRTNC_GPUSHAREDBYTES] = FALSE; + + block->TextCacheValid[ETPRTNC_DISKTOTALRATE] = FALSE; + block->TextCacheValid[ETPRTNC_NETWORKTOTALRATE] = FALSE; + } + } +} + +LONG EtpProcessTreeNewSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + LONG result; + PPH_PROCESS_NODE node1 = Node1; + PPH_PROCESS_NODE node2 = Node2; + PET_PROCESS_BLOCK block1; + PET_PROCESS_BLOCK block2; + + block1 = EtGetProcessBlock(node1->ProcessItem); + block2 = EtGetProcessBlock(node2->ProcessItem); + + result = 0; + + switch (SubId) + { + case ETPRTNC_DISKREADS: + result = uint64cmp(block1->DiskReadCount, block2->DiskReadCount); + break; + case ETPRTNC_DISKWRITES: + result = uint64cmp(block1->DiskWriteCount, block2->DiskWriteCount); + break; + case ETPRTNC_DISKREADBYTES: + result = uint64cmp(block1->DiskReadRaw, block2->DiskReadRaw); + break; + case ETPRTNC_DISKWRITEBYTES: + result = uint64cmp(block1->DiskWriteRaw, block2->DiskWriteRaw); + break; + case ETPRTNC_DISKTOTALBYTES: + result = uint64cmp(block1->DiskReadRaw + block1->DiskWriteRaw, block2->DiskReadRaw + block2->DiskWriteRaw); + break; + case ETPRTNC_DISKREADSDELTA: + result = uint64cmp(block1->DiskReadDelta.Delta, block2->DiskReadDelta.Delta); + break; + case ETPRTNC_DISKWRITESDELTA: + result = uint64cmp(block1->DiskWriteDelta.Delta, block2->DiskWriteDelta.Delta); + break; + case ETPRTNC_DISKREADBYTESDELTA: + result = uint64cmp(block1->DiskReadRawDelta.Delta, block2->DiskReadRawDelta.Delta); + break; + case ETPRTNC_DISKWRITEBYTESDELTA: + result = uint64cmp(block1->DiskWriteRawDelta.Delta, block2->DiskWriteRawDelta.Delta); + break; + case ETPRTNC_DISKTOTALBYTESDELTA: + result = uint64cmp(block1->DiskReadRawDelta.Delta + block1->DiskWriteRawDelta.Delta, block2->DiskReadRawDelta.Delta + block2->DiskWriteRawDelta.Delta); + break; + case ETPRTNC_NETWORKRECEIVES: + result = uint64cmp(block1->NetworkReceiveCount, block2->NetworkReceiveCount); + break; + case ETPRTNC_NETWORKSENDS: + result = uint64cmp(block1->NetworkSendCount, block2->NetworkSendCount); + break; + case ETPRTNC_NETWORKRECEIVEBYTES: + result = uint64cmp(block1->NetworkReceiveRaw, block2->NetworkReceiveRaw); + break; + case ETPRTNC_NETWORKSENDBYTES: + result = uint64cmp(block1->NetworkSendRaw, block2->NetworkSendRaw); + break; + case ETPRTNC_NETWORKTOTALBYTES: + result = uint64cmp(block1->NetworkReceiveRaw + block1->NetworkSendRaw, block2->NetworkReceiveRaw + block2->NetworkSendRaw); + break; + case ETPRTNC_NETWORKRECEIVESDELTA: + result = uint64cmp(block1->NetworkReceiveDelta.Delta, block2->NetworkReceiveDelta.Delta); + break; + case ETPRTNC_NETWORKSENDSDELTA: + result = uint64cmp(block1->NetworkSendDelta.Delta, block2->NetworkSendDelta.Delta); + break; + case ETPRTNC_NETWORKRECEIVEBYTESDELTA: + result = uint64cmp(block1->NetworkReceiveRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta); + break; + case ETPRTNC_NETWORKSENDBYTESDELTA: + result = uint64cmp(block1->NetworkSendRawDelta.Delta, block2->NetworkSendRawDelta.Delta); + break; + case ETPRTNC_NETWORKTOTALBYTESDELTA: + result = uint64cmp(block1->NetworkReceiveRawDelta.Delta + block1->NetworkSendRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta + block2->NetworkSendRawDelta.Delta); + break; + case ETPRTNC_HARDFAULTS: + result = uintcmp(block1->HardFaultsDelta.Value, block2->HardFaultsDelta.Value); + break; + case ETPRTNC_HARDFAULTSDELTA: + result = uintcmp(block1->HardFaultsDelta.Delta, block2->HardFaultsDelta.Delta); + break; + case ETPRTNC_PEAKTHREADS: + result = uintcmp(block1->ProcessItem->PeakNumberOfThreads, block2->ProcessItem->PeakNumberOfThreads); + break; + case ETPRTNC_GPU: + result = singlecmp(block1->GpuNodeUsage, block2->GpuNodeUsage); + break; + case ETPRTNC_GPUDEDICATEDBYTES: + result = uint64cmp(block1->GpuDedicatedUsage, block2->GpuDedicatedUsage); + break; + case ETPRTNC_GPUSHAREDBYTES: + result = uint64cmp(block1->GpuSharedUsage, block2->GpuSharedUsage); + break; + case ETPRTNC_DISKREADRATE: + result = uint64cmp(block1->DiskReadRawDelta.Delta, block2->DiskReadRawDelta.Delta); + break; + case ETPRTNC_DISKWRITERATE: + result = uint64cmp(block1->DiskWriteRawDelta.Delta, block2->DiskWriteRawDelta.Delta); + break; + case ETPRTNC_DISKTOTALRATE: + result = uint64cmp(block1->DiskReadRawDelta.Delta + block1->DiskWriteRawDelta.Delta, block2->DiskReadRawDelta.Delta + block2->DiskWriteRawDelta.Delta); + break; + case ETPRTNC_NETWORKRECEIVERATE: + result = uint64cmp(block1->NetworkReceiveRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta); + break; + case ETPRTNC_NETWORKSENDRATE: + result = uint64cmp(block1->NetworkSendRawDelta.Delta, block2->NetworkSendRawDelta.Delta); + break; + case ETPRTNC_NETWORKTOTALRATE: + result = uint64cmp(block1->NetworkReceiveRawDelta.Delta + block1->NetworkSendRawDelta.Delta, block2->NetworkReceiveRawDelta.Delta + block2->NetworkSendRawDelta.Delta); + break; + } + + return result; +} + +VOID EtNetworkTreeNewInitializing( + _In_ PVOID Parameter + ) +{ + static COLUMN_INFO columns[] = + { + { ETNETNC_RECEIVES, L"Receives", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_SENDS, L"Sends", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_RECEIVEBYTES, L"Receive bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_SENDBYTES, L"Send bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_TOTALBYTES, L"Total bytes", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_RECEIVESDELTA, L"Receives delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_SENDSDELTA, L"Sends delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_RECEIVEBYTESDELTA, L"Receive bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_SENDBYTESDELTA, L"Send bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_TOTALBYTESDELTA, L"Total bytes delta", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_FIREWALLSTATUS, L"Firewall status", 170, PH_ALIGN_LEFT, 0, FALSE }, + { ETNETNC_RECEIVERATE, L"Receive rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_SENDRATE, L"Send rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE }, + { ETNETNC_TOTALRATE, L"Total rate", 70, PH_ALIGN_RIGHT, DT_RIGHT, TRUE } + }; + + PPH_PLUGIN_TREENEW_INFORMATION treeNewInfo = Parameter; + ULONG i; + + for (i = 0; i < sizeof(columns) / sizeof(COLUMN_INFO); i++) + { + EtpAddTreeNewColumn(treeNewInfo, columns[i].SubId, columns[i].Text, columns[i].Width, columns[i].Alignment, + columns[i].TextFlags, columns[i].SortDescending, EtpNetworkTreeNewSortFunction); + } +} + +VOID EtpUpdateFirewallStatus( + _Inout_ PET_NETWORK_BLOCK Block + ) +{ + if (!Block->FirewallStatusValid) + { + Block->FirewallStatus = EtQueryFirewallStatus(Block->NetworkItem); + Block->FirewallStatusValid = TRUE; + } +} + +VOID EtNetworkTreeNewMessage( + _In_ PVOID Parameter + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + + if (message->Message == TreeNewGetCellText) + { + PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; + PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)getCellText->Node; + PET_NETWORK_BLOCK block; + PPH_STRING text; + + block = EtGetNetworkBlock(networkNode->NetworkItem); + + PhAcquireQueuedLockExclusive(&block->TextCacheLock); + + if (block->TextCacheValid[message->SubId]) + { + if (block->TextCache[message->SubId]) + getCellText->Text = block->TextCache[message->SubId]->sr; + } + else + { + text = NULL; + + switch (message->SubId) + { + case ETNETNC_RECEIVES: + if (block->ReceiveCount != 0) + text = PhFormatUInt64(block->ReceiveCount, TRUE); + break; + case ETNETNC_SENDS: + if (block->SendCount != 0) + text = PhFormatUInt64(block->SendCount, TRUE); + break; + case ETNETNC_RECEIVEBYTES: + if (block->ReceiveRaw != 0) + text = PhFormatSize(block->ReceiveRaw, ULONG_MAX); + break; + case ETNETNC_SENDBYTES: + if (block->SendRaw != 0) + text = PhFormatSize(block->SendRaw, ULONG_MAX); + break; + case ETNETNC_TOTALBYTES: + if (block->ReceiveRaw + block->SendRaw != 0) + text = PhFormatSize(block->ReceiveRaw + block->SendRaw, ULONG_MAX); + break; + case ETNETNC_RECEIVESDELTA: + if (block->ReceiveDelta.Delta != 0) + text = PhFormatUInt64(block->ReceiveDelta.Delta, TRUE); + break; + case ETNETNC_SENDSDELTA: + if (block->SendDelta.Delta != 0) + text = PhFormatUInt64(block->SendDelta.Delta, TRUE); + break; + case ETNETNC_RECEIVEBYTESDELTA: + if (block->ReceiveRawDelta.Delta != 0) + text = PhFormatSize(block->ReceiveRawDelta.Delta, ULONG_MAX); + break; + case ETNETNC_SENDBYTESDELTA: + if (block->SendRawDelta.Delta != 0) + text = PhFormatSize(block->SendRawDelta.Delta, ULONG_MAX); + break; + case ETNETNC_TOTALBYTESDELTA: + if (block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta != 0) + text = PhFormatSize(block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta, ULONG_MAX); + break; + case ETNETNC_FIREWALLSTATUS: + { + static PPH_STRING strings[FirewallMaximumStatus]; + static PH_INITONCE initOnce = PH_INITONCE_INIT; + + if (PhBeginInitOnce(&initOnce)) + { + strings[FirewallUnknownStatus] = NULL; + strings[FirewallAllowedNotRestricted] = PhCreateString(L"Allowed, not restricted"); + strings[FirewallAllowedRestricted] = PhCreateString(L"Allowed, restricted"); + strings[FirewallNotAllowedNotRestricted] = PhCreateString(L"Not allowed, not restricted"); + strings[FirewallNotAllowedRestricted] = PhCreateString(L"Not allowed, restricted"); + PhEndInitOnce(&initOnce); + } + + EtpUpdateFirewallStatus(block); + + if (block->FirewallStatus < FirewallMaximumStatus) + PhSetReference(&text, strings[block->FirewallStatus]); + } + break; + case ETNETNC_RECEIVERATE: + if (block->ReceiveRawDelta.Delta != 0) + EtFormatRate(block->ReceiveRawDelta.Delta, &text, NULL); + break; + case ETNETNC_SENDRATE: + if (block->SendRawDelta.Delta != 0) + EtFormatRate(block->SendRawDelta.Delta, &text, NULL); + break; + case ETNETNC_TOTALRATE: + if (block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta != 0) + EtFormatRate(block->ReceiveRawDelta.Delta + block->SendRawDelta.Delta, &text, NULL); + break; + } + + if (text) + { + getCellText->Text = text->sr; + } + + PhMoveReference(&block->TextCache[message->SubId], text); + block->TextCacheValid[message->SubId] = TRUE; + } + + PhReleaseQueuedLockExclusive(&block->TextCacheLock); + } +} + +LONG EtpNetworkTreeNewSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + LONG result; + PPH_NETWORK_NODE node1 = Node1; + PPH_NETWORK_NODE node2 = Node2; + PET_NETWORK_BLOCK block1; + PET_NETWORK_BLOCK block2; + + block1 = EtGetNetworkBlock(node1->NetworkItem); + block2 = EtGetNetworkBlock(node2->NetworkItem); + + result = 0; + + switch (SubId) + { + case ETNETNC_RECEIVES: + result = uint64cmp(block1->ReceiveCount, block2->ReceiveCount); + break; + case ETNETNC_SENDS: + result = uint64cmp(block1->SendCount, block2->SendCount); + break; + case ETNETNC_RECEIVEBYTES: + result = uint64cmp(block1->ReceiveRaw, block2->ReceiveRaw); + break; + case ETNETNC_SENDBYTES: + result = uint64cmp(block1->SendRaw, block2->SendRaw); + break; + case ETNETNC_TOTALBYTES: + result = uint64cmp(block1->ReceiveRaw + block1->SendRaw, block2->ReceiveRaw + block2->SendRaw); + break; + case ETNETNC_RECEIVESDELTA: + result = uint64cmp(block1->ReceiveDelta.Delta, block2->ReceiveDelta.Delta); + break; + case ETNETNC_SENDSDELTA: + result = uint64cmp(block1->SendDelta.Delta, block2->SendDelta.Delta); + break; + case ETNETNC_RECEIVEBYTESDELTA: + result = uint64cmp(block1->ReceiveRawDelta.Delta, block2->ReceiveRawDelta.Delta); + break; + case ETNETNC_SENDBYTESDELTA: + result = uint64cmp(block1->SendRawDelta.Delta, block2->SendRawDelta.Delta); + break; + case ETNETNC_TOTALBYTESDELTA: + result = uint64cmp(block1->ReceiveRawDelta.Delta + block1->SendRawDelta.Delta, block2->ReceiveRawDelta.Delta + block2->SendRawDelta.Delta); + break; + case ETNETNC_FIREWALLSTATUS: + EtpUpdateFirewallStatus(block1); + EtpUpdateFirewallStatus(block2); + result = intcmp(block1->FirewallStatus, block2->FirewallStatus); + break; + case ETNETNC_RECEIVERATE: + result = uint64cmp(block1->ReceiveRawDelta.Delta, block2->ReceiveRawDelta.Delta); + break; + case ETNETNC_SENDRATE: + result = uint64cmp(block1->SendRawDelta.Delta, block2->SendRawDelta.Delta); + break; + case ETNETNC_TOTALRATE: + result = uint64cmp(block1->ReceiveRawDelta.Delta + block1->SendRawDelta.Delta, block2->ReceiveRawDelta.Delta + block2->SendRawDelta.Delta); + break; + } + + return result; +} + +ET_FIREWALL_STATUS EtQueryFirewallStatus( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + static INetFwMgr* manager = NULL; + ET_FIREWALL_STATUS result; + PPH_PROCESS_ITEM processItem; + BSTR imageFileNameBStr; + BSTR localAddressBStr; + VARIANT allowed; + VARIANT restricted; + + if (!manager) + { + if (!SUCCEEDED(CoCreateInstance(&CLSID_NetFwMgr_I, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr_I, &manager))) + return FirewallUnknownStatus; + + if (!manager) + return FirewallUnknownStatus; + } + + processItem = PhReferenceProcessItem(NetworkItem->ProcessId); + + if (!processItem) + return FirewallUnknownStatus; + + if (!processItem->FileName) + { + PhDereferenceObject(processItem); + return FirewallUnknownStatus; + } + + result = FirewallUnknownStatus; + + if (imageFileNameBStr = SysAllocStringLen(processItem->FileName->Buffer, (ULONG)processItem->FileName->Length / sizeof(WCHAR))) + { + localAddressBStr = NULL; + + if (!PhIsNullIpAddress(&NetworkItem->LocalEndpoint.Address)) + localAddressBStr = SysAllocString(NetworkItem->LocalAddressString); + + memset(&allowed, 0, sizeof(VARIANT)); // VariantInit + memset(&restricted, 0, sizeof(VARIANT)); // VariantInit + + if (SUCCEEDED(INetFwMgr_IsPortAllowed( + manager, + imageFileNameBStr, + (NetworkItem->ProtocolType & PH_IPV6_NETWORK_TYPE) ? NET_FW_IP_VERSION_V6 : NET_FW_IP_VERSION_V4, + NetworkItem->LocalEndpoint.Port, + localAddressBStr, + (NetworkItem->ProtocolType & PH_UDP_PROTOCOL_TYPE) ? NET_FW_IP_PROTOCOL_UDP : NET_FW_IP_PROTOCOL_TCP, + &allowed, + &restricted + ))) + { + if (allowed.boolVal) + { + if (restricted.boolVal) + result = FirewallAllowedRestricted; + else + result = FirewallAllowedNotRestricted; + } + else + { + if (restricted.boolVal) + result = FirewallNotAllowedRestricted; + else + result = FirewallNotAllowedNotRestricted; + } + } + + if (localAddressBStr) + SysFreeString(localAddressBStr); + + SysFreeString(imageFileNameBStr); + } + + PhDereferenceObject(processItem); + + return result; +} diff --git a/plugins/ExtendedTools/unldll.c b/plugins/ExtendedTools/unldll.c index e58672399a45..60cb62ac1453 100644 --- a/plugins/ExtendedTools/unldll.c +++ b/plugins/ExtendedTools/unldll.c @@ -1,357 +1,486 @@ -/* - * Process Hacker Extended Tools - - * unloaded DLLs display - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -typedef struct _UNLOADED_DLLS_CONTEXT -{ - PPH_PROCESS_ITEM ProcessItem; - HWND ListViewHandle; - PVOID CapturedEventTrace; -} UNLOADED_DLLS_CONTEXT, *PUNLOADED_DLLS_CONTEXT; - -INT_PTR CALLBACK EtpUnloadedDllsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtShowUnloadedDllsDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - UNLOADED_DLLS_CONTEXT context; - - context.ProcessItem = ProcessItem; - context.CapturedEventTrace = NULL; - - DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_UNLOADEDDLLS), - ParentWindowHandle, - EtpUnloadedDllsDlgProc, - (LPARAM)&context - ); - - if (context.CapturedEventTrace) - PhFree(context.CapturedEventTrace); -} - -BOOLEAN EtpRefreshUnloadedDlls( - _In_ HWND hwndDlg, - _In_ PUNLOADED_DLLS_CONTEXT Context - ) -{ - NTSTATUS status; - PULONG elementSize; - PULONG elementCount; - PVOID eventTrace; - HANDLE processHandle = NULL; - ULONG eventTraceSize; - ULONG capturedElementSize; - ULONG capturedElementCount; - PVOID capturedEventTracePointer; - PVOID capturedEventTrace = NULL; - ULONG i; - PVOID currentEvent; - HWND lvHandle; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - ListView_DeleteAllItems(lvHandle); - - RtlGetUnloadEventTraceEx(&elementSize, &elementCount, &eventTrace); - - if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_VM_READ, Context->ProcessItem->ProcessId))) - goto CleanupExit; - - // We have the pointers for the unload event trace information. - // Since ntdll is loaded at the same base address across all processes, - // we can read the information in. - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - processHandle, - elementSize, - &capturedElementSize, - sizeof(ULONG), - NULL - ))) - goto CleanupExit; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - processHandle, - elementCount, - &capturedElementCount, - sizeof(ULONG), - NULL - ))) - goto CleanupExit; - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - processHandle, - eventTrace, - &capturedEventTracePointer, - sizeof(PVOID), - NULL - ))) - goto CleanupExit; - - if (!capturedEventTracePointer) - goto CleanupExit; // no events - - if (capturedElementCount > 0x4000) - capturedElementCount = 0x4000; - - eventTraceSize = capturedElementSize * capturedElementCount; - - capturedEventTrace = PhAllocateSafe(eventTraceSize); - - if (!capturedEventTrace) - { - status = STATUS_NO_MEMORY; - goto CleanupExit; - } - - if (!NT_SUCCESS(status = NtReadVirtualMemory( - processHandle, - capturedEventTracePointer, - capturedEventTrace, - eventTraceSize, - NULL - ))) - goto CleanupExit; - - currentEvent = capturedEventTrace; - - ExtendedListView_SetRedraw(lvHandle, FALSE); - - for (i = 0; i < capturedElementCount; i++) - { - PRTL_UNLOAD_EVENT_TRACE rtlEvent = currentEvent; - INT lvItemIndex; - WCHAR buffer[128]; - PPH_STRING string; - LARGE_INTEGER time; - SYSTEMTIME systemTime; - - if (!rtlEvent->BaseAddress) - break; - - PhPrintUInt32(buffer, rtlEvent->Sequence); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, buffer, rtlEvent); - - // Name - if (PhCopyStringZ(rtlEvent->ImageName, sizeof(rtlEvent->ImageName) / sizeof(WCHAR), - buffer, sizeof(buffer) / sizeof(WCHAR), NULL)) - { - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, buffer); - } - - // Base Address - PhPrintPointer(buffer, rtlEvent->BaseAddress); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, buffer); - - // Size - string = PhFormatSize(rtlEvent->SizeOfImage, -1); - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, string->Buffer); - PhDereferenceObject(string); - - // Time Stamp - RtlSecondsSince1970ToTime(rtlEvent->TimeDateStamp, &time); - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - string = PhFormatDateTime(&systemTime); - PhSetListViewSubItem(lvHandle, lvItemIndex, 4, string->Buffer); - PhDereferenceObject(string); - - // Checksum - PhPrintPointer(buffer, UlongToPtr(rtlEvent->CheckSum)); - PhSetListViewSubItem(lvHandle, lvItemIndex, 5, buffer); - - currentEvent = PTR_ADD_OFFSET(currentEvent, capturedElementSize); - } - - ExtendedListView_SortItems(lvHandle); - ExtendedListView_SetRedraw(lvHandle, TRUE); - - if (Context->CapturedEventTrace) - PhFree(Context->CapturedEventTrace); - - Context->CapturedEventTrace = capturedEventTrace; - -CleanupExit: - - if (processHandle) - NtClose(processHandle); - - if (NT_SUCCESS(status)) - { - return TRUE; - } - else - { - PhShowStatus(hwndDlg, L"Unable to retrieve unload event trace information", status, 0); - return FALSE; - } -} - -static INT NTAPI EtpNumberCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PRTL_UNLOAD_EVENT_TRACE item1 = Item1; - PRTL_UNLOAD_EVENT_TRACE item2 = Item2; - - return uintcmp(item1->Sequence, item2->Sequence); -} - -static INT NTAPI EtpBaseAddressCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PRTL_UNLOAD_EVENT_TRACE item1 = Item1; - PRTL_UNLOAD_EVENT_TRACE item2 = Item2; - - return uintptrcmp((ULONG_PTR)item1->BaseAddress, (ULONG_PTR)item2->BaseAddress); -} - -static INT NTAPI EtpSizeCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PRTL_UNLOAD_EVENT_TRACE item1 = Item1; - PRTL_UNLOAD_EVENT_TRACE item2 = Item2; - - return uintptrcmp(item1->SizeOfImage, item2->SizeOfImage); -} - -static INT NTAPI EtpTimeStampCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PRTL_UNLOAD_EVENT_TRACE item1 = Item1; - PRTL_UNLOAD_EVENT_TRACE item2 = Item2; - - return uintcmp(item1->TimeDateStamp, item2->TimeDateStamp); -} - -static INT NTAPI EtpCheckSumCompareFunction( - _In_ PVOID Item1, - _In_ PVOID Item2, - _In_opt_ PVOID Context - ) -{ - PRTL_UNLOAD_EVENT_TRACE item1 = Item1; - PRTL_UNLOAD_EVENT_TRACE item2 = Item2; - - return uintcmp(item1->CheckSum, item2->CheckSum); -} - -INT_PTR CALLBACK EtpUnloadedDllsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PUNLOADED_DLLS_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PUNLOADED_DLLS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PUNLOADED_DLLS_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"No."); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 120, L"Name"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Base Address"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 60, L"Size"); - PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 100, L"Time Stamp"); - PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 60, L"Checksum"); - - PhSetExtendedListView(lvHandle); - ExtendedListView_SetCompareFunction(lvHandle, 0, EtpNumberCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 2, EtpBaseAddressCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 3, EtpSizeCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 4, EtpTimeStampCompareFunction); - ExtendedListView_SetCompareFunction(lvHandle, 5, EtpCheckSumCompareFunction); - - if (!EtpRefreshUnloadedDlls(hwndDlg, context)) - { - EndDialog(hwndDlg, IDCANCEL); - return FALSE; - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_REFRESH: - EtpRefreshUnloadedDlls(hwndDlg, context); - break; - } - } - break; - case WM_NOTIFY: - { - PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle); - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * unloaded DLLs display + * + * Copyright (C) 2010-2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +typedef struct _UNLOADED_DLLS_CONTEXT +{ + BOOLEAN IsWow64; + HWND ListViewHandle; + PPH_PROCESS_ITEM ProcessItem; + PH_LAYOUT_MANAGER LayoutManager; + PVOID CapturedEventTrace; +} UNLOADED_DLLS_CONTEXT, *PUNLOADED_DLLS_CONTEXT; + +INT_PTR CALLBACK EtpUnloadedDllsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EtShowUnloadedDllsDialog( + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PhReferenceObject(ProcessItem); + + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_UNLOADEDDLLS), + NULL, + EtpUnloadedDllsDlgProc, + (LPARAM)ProcessItem + ); +} + +NTSTATUS EtpRefreshUnloadedDlls( + _In_ HWND hwndDlg, + _In_ PUNLOADED_DLLS_CONTEXT Context + ) +{ + NTSTATUS status; +#ifdef _WIN64 + BOOLEAN isWow64; +#endif + ULONG capturedElementSize; + ULONG capturedElementCount; + PVOID capturedEventTrace = NULL; + ULONG i; + PVOID currentEvent; + +#ifdef _WIN64 + if (!Context->ProcessItem->QueryHandle) + return STATUS_FAIL_CHECK; + + if (!NT_SUCCESS(status = PhGetProcessIsWow64(Context->ProcessItem->QueryHandle, &isWow64))) + return status; + + Context->IsWow64 = isWow64; + + if (isWow64) + { + PPH_STRING eventTraceString; + + if (!PhUiConnectToPhSvcEx(hwndDlg, Wow64PhSvcMode, FALSE)) + return STATUS_FAIL_CHECK; + + if (!NT_SUCCESS(status = CallGetProcessUnloadedDlls(Context->ProcessItem->ProcessId, &eventTraceString))) + { + PhUiDisconnectFromPhSvc(); + return status; + } + + capturedEventTrace = PhAllocate(sizeof(RTL_UNLOAD_EVENT_TRACE32) * RTL_UNLOAD_EVENT_TRACE_NUMBER); + memset(capturedEventTrace, 0, sizeof(RTL_UNLOAD_EVENT_TRACE32) * RTL_UNLOAD_EVENT_TRACE_NUMBER); + + if (!PhHexStringToBuffer(&eventTraceString->sr, (PUCHAR)capturedEventTrace)) + { + PhUiDisconnectFromPhSvc(); + + PhFree(capturedEventTrace); + + return STATUS_FAIL_CHECK; + } + + PhUiDisconnectFromPhSvc(); + + ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); + ListView_DeleteAllItems(Context->ListViewHandle); + + for (i = 0; i < RTL_UNLOAD_EVENT_TRACE_NUMBER; i++) + { + PRTL_UNLOAD_EVENT_TRACE32 rtlEvent = PTR_ADD_OFFSET(capturedEventTrace, sizeof(RTL_UNLOAD_EVENT_TRACE32) * i); + INT lvItemIndex; + WCHAR buffer[128]; + PPH_STRING string; + LARGE_INTEGER time; + SYSTEMTIME systemTime; + + if (!rtlEvent->BaseAddress) + break; + + PhPrintUInt32(buffer, rtlEvent->Sequence); + lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, buffer, rtlEvent); + + // Name + if (PhCopyStringZ(rtlEvent->ImageName, RTL_NUMBER_OF(rtlEvent->ImageName), buffer, RTL_NUMBER_OF(buffer), NULL)) + { + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, buffer); + } + + // Base Address + PhPrintPointer(buffer, (PVOID)(ULONG_PTR)rtlEvent->BaseAddress); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, buffer); + + // Size + string = PhFormatSize(rtlEvent->SizeOfImage, -1); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 3, string->Buffer); + PhDereferenceObject(string); + + // Time Stamp + RtlSecondsSince1970ToTime(rtlEvent->TimeDateStamp, &time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + string = PhFormatDateTime(&systemTime); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 4, string->Buffer); + PhDereferenceObject(string); + + // Checksum + PhPrintPointer(buffer, UlongToPtr(rtlEvent->CheckSum)); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 5, buffer); + } + + ExtendedListView_SortItems(Context->ListViewHandle); + ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); + + PhDereferenceObject(eventTraceString); + } + else + { +#endif + status = PhGetProcessUnloadedDlls( + Context->ProcessItem->ProcessId, + &capturedEventTrace, + &capturedElementSize, + &capturedElementCount + ); + + if (!NT_SUCCESS(status)) + { + PhShowStatus(NULL, L"Unable to retrieve unload event trace information.", status, 0); + return FALSE; + } + + currentEvent = capturedEventTrace; + + ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); + ListView_DeleteAllItems(Context->ListViewHandle); + + for (i = 0; i < capturedElementCount; i++) + { + PRTL_UNLOAD_EVENT_TRACE rtlEvent = currentEvent; + INT lvItemIndex; + WCHAR buffer[128]; + PPH_STRING string; + LARGE_INTEGER time; + SYSTEMTIME systemTime; + + if (!rtlEvent->BaseAddress) + break; + + PhPrintUInt32(buffer, rtlEvent->Sequence); + lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, buffer, rtlEvent); + + // Name + if (PhCopyStringZ(rtlEvent->ImageName, RTL_NUMBER_OF(rtlEvent->ImageName), buffer, RTL_NUMBER_OF(buffer), NULL)) + { + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, buffer); + } + + // Base Address + PhPrintPointer(buffer, rtlEvent->BaseAddress); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 2, buffer); + + // Size + string = PhFormatSize(rtlEvent->SizeOfImage, -1); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 3, string->Buffer); + PhDereferenceObject(string); + + // Time Stamp + RtlSecondsSince1970ToTime(rtlEvent->TimeDateStamp, &time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + string = PhFormatDateTime(&systemTime); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 4, string->Buffer); + PhDereferenceObject(string); + + // Checksum + PhPrintPointer(buffer, UlongToPtr(rtlEvent->CheckSum)); + PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 5, buffer); + + currentEvent = PTR_ADD_OFFSET(currentEvent, capturedElementSize); + } + + ExtendedListView_SortItems(Context->ListViewHandle); + ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); + +#ifdef _WIN64 + } +#endif + + if (Context->CapturedEventTrace) + PhFree(Context->CapturedEventTrace); + + Context->CapturedEventTrace = capturedEventTrace; + + return NT_SUCCESS(status); +} + +static INT NTAPI EtpNumberCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ +#ifdef _WIN64 + PUNLOADED_DLLS_CONTEXT context = Context; + + if (context->IsWow64) + { + PRTL_UNLOAD_EVENT_TRACE32 item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE32 item2 = Item2; + + return uintcmp(item1->Sequence, item2->Sequence); + } + else +#endif + { + PRTL_UNLOAD_EVENT_TRACE item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE item2 = Item2; + + return uintcmp(item1->Sequence, item2->Sequence); + } +} + +static INT NTAPI EtpBaseAddressCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ +#ifdef _WIN64 + PUNLOADED_DLLS_CONTEXT context = Context; + + if (context->IsWow64) + { + PRTL_UNLOAD_EVENT_TRACE32 item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE32 item2 = Item2; + + return uintptrcmp((ULONG_PTR)item1->BaseAddress, (ULONG_PTR)item2->BaseAddress); + } + else +#endif + { + PRTL_UNLOAD_EVENT_TRACE item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE item2 = Item2; + + return uintptrcmp((ULONG_PTR)item1->BaseAddress, (ULONG_PTR)item2->BaseAddress); + } +} + +static INT NTAPI EtpSizeCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ +#ifdef _WIN64 + PUNLOADED_DLLS_CONTEXT context = Context; + + if (context->IsWow64) + { + PRTL_UNLOAD_EVENT_TRACE32 item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE32 item2 = Item2; + + return uintptrcmp(item1->SizeOfImage, item2->SizeOfImage); + } + else +#endif + { + PRTL_UNLOAD_EVENT_TRACE item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE item2 = Item2; + + return uintptrcmp(item1->SizeOfImage, item2->SizeOfImage); + } +} + +static INT NTAPI EtpTimeStampCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ +#ifdef _WIN64 + PUNLOADED_DLLS_CONTEXT context = Context; + + if (context->IsWow64) + { + PRTL_UNLOAD_EVENT_TRACE32 item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE32 item2 = Item2; + + return uintcmp(item1->TimeDateStamp, item2->TimeDateStamp); + } + else +#endif + { + PRTL_UNLOAD_EVENT_TRACE item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE item2 = Item2; + + return uintcmp(item1->TimeDateStamp, item2->TimeDateStamp); + } +} + +static INT NTAPI EtpCheckSumCompareFunction( + _In_ PVOID Item1, + _In_ PVOID Item2, + _In_opt_ PVOID Context + ) +{ +#ifdef _WIN64 + PUNLOADED_DLLS_CONTEXT context = Context; + + if (context->IsWow64) + { + PRTL_UNLOAD_EVENT_TRACE32 item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE32 item2 = Item2; + + return uintcmp(item1->CheckSum, item2->CheckSum); + } + else +#endif + { + PRTL_UNLOAD_EVENT_TRACE item1 = Item1; + PRTL_UNLOAD_EVENT_TRACE item2 = Item2; + + return uintcmp(item1->CheckSum, item2->CheckSum); + } +} + +INT_PTR CALLBACK EtpUnloadedDllsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PUNLOADED_DLLS_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocateZero(sizeof(UNLOADED_DLLS_CONTEXT)); + context->ProcessItem = (PPH_PROCESS_ITEM)lParam; + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + NTSTATUS status; + HWND lvHandle; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"No."); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 120, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Base Address"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 60, L"Size"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 100, L"Time Stamp"); + PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 60, L"Checksum"); + + PhSetExtendedListView(lvHandle); + ExtendedListView_SetCompareFunction(lvHandle, 0, EtpNumberCompareFunction); + ExtendedListView_SetCompareFunction(lvHandle, 2, EtpBaseAddressCompareFunction); + ExtendedListView_SetCompareFunction(lvHandle, 3, EtpSizeCompareFunction); + ExtendedListView_SetCompareFunction(lvHandle, 4, EtpTimeStampCompareFunction); + ExtendedListView_SetCompareFunction(lvHandle, 5, EtpCheckSumCompareFunction); + ExtendedListView_SetContext(lvHandle, context); + PhLoadListViewColumnsFromSetting(SETTING_NAME_UNLOADED_COLUMNS, lvHandle); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, lvHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + if (PhGetIntegerPairSetting(SETTING_NAME_UNLOADED_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_UNLOADED_WINDOW_POSITION, SETTING_NAME_UNLOADED_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); // GetParent(hwndDlg) + + if (!NT_SUCCESS(status = EtpRefreshUnloadedDlls(hwndDlg, context))) + { + PhShowStatus(NULL, L"Unable to retrieve unload event trace information.", status, 0); + EndDialog(hwndDlg, IDCANCEL); + return FALSE; + } + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(SETTING_NAME_UNLOADED_COLUMNS, context->ListViewHandle); + PhSaveWindowPlacementToSetting(SETTING_NAME_UNLOADED_WINDOW_POSITION, SETTING_NAME_UNLOADED_WINDOW_SIZE, hwndDlg); + + PhDeleteLayoutManager(&context->LayoutManager); + + if (context->CapturedEventTrace) + PhFree(context->CapturedEventTrace); + + PhDereferenceObject(context->ProcessItem); + + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_REFRESH: + EtpRefreshUnloadedDlls(hwndDlg, context); + break; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtendedTools/utils.c b/plugins/ExtendedTools/utils.c index d0e1c4da868d..2d5f23af7c81 100644 --- a/plugins/ExtendedTools/utils.c +++ b/plugins/ExtendedTools/utils.c @@ -1,48 +1,48 @@ -/* - * Process Hacker Extended Tools - - * utility functions - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" - -VOID EtFormatRate( - _In_ ULONG64 ValuePerPeriod, - _Inout_ PPH_STRING *Buffer, - _Out_opt_ PPH_STRINGREF String - ) -{ - ULONG64 number; - - number = ValuePerPeriod; - number *= 1000; - number /= PhGetIntegerSetting(L"UpdateInterval"); - - if (number != 0) - { - PH_FORMAT format[2]; - - PhInitFormatSize(&format[0], number); - PhInitFormatS(&format[1], L"/s"); - PhMoveReference(Buffer, PhFormat(format, 2, 0)); - - if (String) - *String = (*Buffer)->sr; - } -} +/* + * Process Hacker Extended Tools - + * utility functions + * + * Copyright (C) 2011 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" + +VOID EtFormatRate( + _In_ ULONG64 ValuePerPeriod, + _Inout_ PPH_STRING *Buffer, + _Out_opt_ PPH_STRINGREF String + ) +{ + ULONG64 number; + + number = ValuePerPeriod; + number *= 1000; + number /= PhGetIntegerSetting(L"UpdateInterval"); + + if (number != 0) + { + PH_FORMAT format[2]; + + PhInitFormatSize(&format[0], number); + PhInitFormatS(&format[1], L"/s"); + PhMoveReference(Buffer, PhFormat(format, 2, 0)); + + if (String) + *String = (*Buffer)->sr; + } +} diff --git a/plugins/ExtendedTools/wswatch.c b/plugins/ExtendedTools/wswatch.c index 2047c505d813..26029ec1f2b1 100644 --- a/plugins/ExtendedTools/wswatch.c +++ b/plugins/ExtendedTools/wswatch.c @@ -1,571 +1,597 @@ -/* - * Process Hacker Extended Tools - - * working set watch - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "exttools.h" -#include -#include - -typedef struct _WS_WATCH_CONTEXT -{ - LONG RefCount; - - PPH_PROCESS_ITEM ProcessItem; - HWND WindowHandle; - HWND ListViewHandle; - BOOLEAN Enabled; - BOOLEAN Destroying; - PPH_HASHTABLE Hashtable; - HANDLE ProcessHandle; - PVOID Buffer; - ULONG BufferSize; - - PPH_SYMBOL_PROVIDER SymbolProvider; - HANDLE LoadingSymbolsForProcessId; - SINGLE_LIST_ENTRY ResultListHead; - PH_QUEUED_LOCK ResultListLock; -} WS_WATCH_CONTEXT, *PWS_WATCH_CONTEXT; - -typedef struct _SYMBOL_LOOKUP_RESULT -{ - SINGLE_LIST_ENTRY ListEntry; - PWS_WATCH_CONTEXT Context; - PVOID Address; - PPH_STRING Symbol; -} SYMBOL_LOOKUP_RESULT, *PSYMBOL_LOOKUP_RESULT; - -VOID EtpReferenceWsWatchContext( - _Inout_ PWS_WATCH_CONTEXT Context - ); - -VOID EtpDereferenceWsWatchContext( - _Inout_ PWS_WATCH_CONTEXT Context - ); - -INT_PTR CALLBACK EtpWsWatchDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID EtShowWsWatchDialog( - _In_ HWND ParentWindowHandle, - _In_ PPH_PROCESS_ITEM ProcessItem - ) -{ - PWS_WATCH_CONTEXT context; - - context = PhAllocate(sizeof(WS_WATCH_CONTEXT)); - memset(context, 0, sizeof(WS_WATCH_CONTEXT)); - context->RefCount = 1; - context->ProcessItem = ProcessItem; - - DialogBoxParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_WSWATCH), - ParentWindowHandle, - EtpWsWatchDlgProc, - (LPARAM)context - ); - EtpDereferenceWsWatchContext(context); -} - -static VOID EtpReferenceWsWatchContext( - _Inout_ PWS_WATCH_CONTEXT Context - ) -{ - _InterlockedIncrement(&Context->RefCount); -} - -static VOID EtpDereferenceWsWatchContext( - _Inout_ PWS_WATCH_CONTEXT Context - ) -{ - if (_InterlockedDecrement(&Context->RefCount) == 0) - { - PSINGLE_LIST_ENTRY listEntry; - - if (Context->SymbolProvider) - PhDereferenceObject(Context->SymbolProvider); - - // Free all unused results. - - PhAcquireQueuedLockExclusive(&Context->ResultListLock); - - listEntry = Context->ResultListHead.Next; - - while (listEntry) - { - PSYMBOL_LOOKUP_RESULT result; - - result = CONTAINING_RECORD(listEntry, SYMBOL_LOOKUP_RESULT, ListEntry); - listEntry = listEntry->Next; - PhDereferenceObject(result->Symbol); - PhFree(result); - } - - PhReleaseQueuedLockExclusive(&Context->ResultListLock); - - PhFree(Context); - } -} - -static NTSTATUS EtpSymbolLookupFunction( - _In_ PVOID Parameter - ) -{ - PSYMBOL_LOOKUP_RESULT result; - - result = Parameter; - - // Don't bother looking up the symbol if the window has already closed. - if (result->Context->Destroying) - { - EtpDereferenceWsWatchContext(result->Context); - PhFree(result); - return STATUS_SUCCESS; - } - - result->Symbol = PhGetSymbolFromAddress( - result->Context->SymbolProvider, - (ULONG64)result->Address, - NULL, - NULL, - NULL, - NULL - ); - - // Fail if we don't have a symbol. - if (!result->Symbol) - { - EtpDereferenceWsWatchContext(result->Context); - PhFree(result); - return STATUS_SUCCESS; - } - - PhAcquireQueuedLockExclusive(&result->Context->ResultListLock); - PushEntryList(&result->Context->ResultListHead, &result->ListEntry); - PhReleaseQueuedLockExclusive(&result->Context->ResultListLock); - EtpDereferenceWsWatchContext(result->Context); - - return STATUS_SUCCESS; -} - -static VOID EtpQueueSymbolLookup( - _In_ PWS_WATCH_CONTEXT Context, - _In_ PVOID Address - ) -{ - PSYMBOL_LOOKUP_RESULT result; - - result = PhAllocate(sizeof(SYMBOL_LOOKUP_RESULT)); - result->Context = Context; - result->Address = Address; - EtpReferenceWsWatchContext(Context); - - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), EtpSymbolLookupFunction, result); -} - -static PPH_STRING EtpGetBasicSymbol( - _In_ PPH_SYMBOL_PROVIDER SymbolProvider, - _In_ ULONG64 Address - ) -{ - ULONG64 modBase; - PPH_STRING fileName = NULL; - PPH_STRING baseName = NULL; - PPH_STRING symbol; - - modBase = PhGetModuleFromAddress(SymbolProvider, Address, &fileName); - - if (!fileName) - { - symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2); - PhPrintPointer(symbol->Buffer, (PVOID)Address); - PhTrimToNullTerminatorString(symbol); - } - else - { - PH_FORMAT format[3]; - - baseName = PhGetBaseName(fileName); - - PhInitFormatSR(&format[0], baseName->sr); - PhInitFormatS(&format[1], L"+0x"); - PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase)); - - symbol = PhFormat(format, 3, baseName->Length + 6 + 32); - } - - if (fileName) - PhDereferenceObject(fileName); - if (baseName) - PhDereferenceObject(baseName); - - return symbol; -} - -static VOID EtpProcessSymbolLookupResults( - _In_ HWND hwndDlg, - _In_ PWS_WATCH_CONTEXT Context - ) -{ - PSINGLE_LIST_ENTRY listEntry; - - // Remove all results. - PhAcquireQueuedLockExclusive(&Context->ResultListLock); - listEntry = Context->ResultListHead.Next; - Context->ResultListHead.Next = NULL; - PhReleaseQueuedLockExclusive(&Context->ResultListLock); - - // Update the list view with the results. - while (listEntry) - { - PSYMBOL_LOOKUP_RESULT result; - - result = CONTAINING_RECORD(listEntry, SYMBOL_LOOKUP_RESULT, ListEntry); - listEntry = listEntry->Next; - - PhSetListViewSubItem( - Context->ListViewHandle, - PhFindListViewItemByParam(Context->ListViewHandle, -1, result->Address), - 0, - result->Symbol->Buffer - ); - - PhDereferenceObject(result->Symbol); - PhFree(result); - } -} - -static BOOLEAN EtpUpdateWsWatch( - _In_ HWND hwndDlg, - _In_ PWS_WATCH_CONTEXT Context - ) -{ - NTSTATUS status; - BOOLEAN result; - ULONG returnLength; - PPROCESS_WS_WATCH_INFORMATION_EX wsWatchInfo; - - // Query WS watch information. - - if (!Context->Buffer) - return FALSE; - - status = NtQueryInformationProcess( - Context->ProcessHandle, - ProcessWorkingSetWatchEx, - Context->Buffer, - Context->BufferSize, - &returnLength - ); - - if (status == STATUS_UNSUCCESSFUL) - { - // WS Watch is not enabled. - return FALSE; - } - - if (status == STATUS_NO_MORE_ENTRIES) - { - // There were no new faults, but we still need to process symbol lookup results. - result = TRUE; - goto SkipBuffer; - } - - if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) - { - PhFree(Context->Buffer); - Context->Buffer = PhAllocate(returnLength); - Context->BufferSize = returnLength; - - status = NtQueryInformationProcess( - Context->ProcessHandle, - ProcessWorkingSetWatchEx, - Context->Buffer, - Context->BufferSize, - &returnLength - ); - } - - if (!NT_SUCCESS(status)) - { - // Error related to the buffer size. Try again later. - result = FALSE; - goto SkipBuffer; - } - - // Update the hashtable and list view. - - ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); - - wsWatchInfo = Context->Buffer; - - while (wsWatchInfo->BasicInfo.FaultingPc) - { - PVOID *entry; - WCHAR buffer[PH_INT32_STR_LEN_1]; - INT lvItemIndex; - ULONG newCount; - - // Update the count in the entry for this instruction pointer, or add a new entry if it doesn't exist. - - entry = PhFindItemSimpleHashtable(Context->Hashtable, wsWatchInfo->BasicInfo.FaultingPc); - - if (entry) - { - newCount = PtrToUlong(*entry) + 1; - *entry = UlongToPtr(newCount); - lvItemIndex = PhFindListViewItemByParam(Context->ListViewHandle, -1, wsWatchInfo->BasicInfo.FaultingPc); - } - else - { - PPH_STRING basicSymbol; - - newCount = 1; - PhAddItemSimpleHashtable(Context->Hashtable, wsWatchInfo->BasicInfo.FaultingPc, UlongToPtr(1)); - - // Get a basic symbol name (module+offset). - basicSymbol = EtpGetBasicSymbol(Context->SymbolProvider, (ULONG64)wsWatchInfo->BasicInfo.FaultingPc); - - lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, basicSymbol->Buffer, wsWatchInfo->BasicInfo.FaultingPc); - PhDereferenceObject(basicSymbol); - - // Queue a full symbol lookup. - EtpQueueSymbolLookup(Context, wsWatchInfo->BasicInfo.FaultingPc); - } - - // Update the count in the list view item. - PhPrintUInt32(buffer, newCount); - PhSetListViewSubItem( - Context->ListViewHandle, - lvItemIndex, - 1, - buffer - ); - - wsWatchInfo++; - } - - ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); - result = TRUE; - -SkipBuffer: - EtpProcessSymbolLookupResults(hwndDlg, Context); - ExtendedListView_SortItems(Context->ListViewHandle); - - return result; -} - -static BOOLEAN NTAPI EnumGenericModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PWS_WATCH_CONTEXT context = Context; - - // If we're loading kernel module symbols for a process other than - // System, ignore modules which are in user space. This may happen - // in Windows 7. - if ( - context->LoadingSymbolsForProcessId == SYSTEM_PROCESS_ID && - (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress - ) - return TRUE; - - PhLoadModuleSymbolProvider(context->SymbolProvider, Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, Module->Size); - - return TRUE; -} - -INT_PTR CALLBACK EtpWsWatchDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWS_WATCH_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PWS_WATCH_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PWS_WATCH_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - context->WindowHandle = hwndDlg; - context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 340, L"Instruction"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Count"); - PhSetExtendedListView(lvHandle); - ExtendedListView_SetSort(lvHandle, 1, DescendingSortOrder); - - context->Hashtable = PhCreateSimpleHashtable(64); - context->BufferSize = 0x2000; - context->Buffer = PhAllocate(context->BufferSize); - - PhInitializeQueuedLock(&context->ResultListLock); - context->SymbolProvider = PhCreateSymbolProvider(context->ProcessItem->ProcessId); - PhLoadSymbolProviderOptions(context->SymbolProvider); - - if (!context->SymbolProvider || !context->SymbolProvider->IsRealHandle) - { - PhShowError(hwndDlg, L"Unable to open the process."); - EndDialog(hwndDlg, IDCANCEL); - break; - } - - context->ProcessHandle = context->SymbolProvider->ProcessHandle; - - // Load symbols for both process and kernel modules. - context->LoadingSymbolsForProcessId = context->ProcessItem->ProcessId; - PhEnumGenericModules( - NULL, - context->ProcessHandle, - 0, - EnumGenericModulesCallback, - context - ); - context->LoadingSymbolsForProcessId = SYSTEM_PROCESS_ID; - PhEnumGenericModules( - SYSTEM_PROCESS_ID, - NULL, - 0, - EnumGenericModulesCallback, - context - ); - - context->Enabled = EtpUpdateWsWatch(hwndDlg, context); - - if (context->Enabled) - { - // WS Watch is already enabled for the process. Enable updating. - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE), FALSE); - ShowWindow(GetDlgItem(hwndDlg, IDC_WSWATCHENABLED), SW_SHOW); - SetTimer(hwndDlg, 1, 1000, NULL); - } - else - { - // WS Watch has not yet been enabled for the process. - } - } - break; - case WM_DESTROY: - { - context->Destroying = TRUE; - - PhDereferenceObject(context->Hashtable); - - if (context->Buffer) - { - PhFree(context->Buffer); - context->Buffer = NULL; - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, IDOK); - break; - case IDC_ENABLE: - { - NTSTATUS status; - HANDLE processHandle; - - if (NT_SUCCESS(status = PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - context->ProcessItem->ProcessId - ))) - { - status = NtSetInformationProcess( - processHandle, - ProcessWorkingSetWatchEx, - NULL, - 0 - ); - NtClose(processHandle); - } - - if (NT_SUCCESS(status)) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE), FALSE); - ShowWindow(GetDlgItem(hwndDlg, IDC_WSWATCHENABLED), SW_SHOW); - SetTimer(hwndDlg, 1, 1000, NULL); - } - else - { - PhShowStatus(hwndDlg, L"Unable to enable WS watch", status, 0); - } - } - break; - } - } - break; - case WM_NOTIFY: - { - PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle); - } - break; - case WM_TIMER: - { - switch (wParam) - { - case 1: - { - EtpUpdateWsWatch(hwndDlg, context); - } - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Extended Tools - + * working set watch + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "exttools.h" +#include +#include + +typedef struct _WS_WATCH_CONTEXT +{ + LONG RefCount; + + PPH_PROCESS_ITEM ProcessItem; + HWND WindowHandle; + HWND ListViewHandle; + BOOLEAN Enabled; + BOOLEAN Destroying; + PPH_HASHTABLE Hashtable; + HANDLE ProcessHandle; + PVOID Buffer; + ULONG BufferSize; + + PH_LAYOUT_MANAGER LayoutManager; + + PPH_SYMBOL_PROVIDER SymbolProvider; + HANDLE LoadingSymbolsForProcessId; + SINGLE_LIST_ENTRY ResultListHead; + PH_QUEUED_LOCK ResultListLock; +} WS_WATCH_CONTEXT, *PWS_WATCH_CONTEXT; + +typedef struct _SYMBOL_LOOKUP_RESULT +{ + SINGLE_LIST_ENTRY ListEntry; + PWS_WATCH_CONTEXT Context; + PVOID Address; + PPH_STRING Symbol; +} SYMBOL_LOOKUP_RESULT, *PSYMBOL_LOOKUP_RESULT; + +VOID EtpReferenceWsWatchContext( + _Inout_ PWS_WATCH_CONTEXT Context + ); + +VOID EtpDereferenceWsWatchContext( + _Inout_ PWS_WATCH_CONTEXT Context + ); + +INT_PTR CALLBACK EtpWsWatchDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +VOID EtShowWsWatchDialog( + _In_ HWND ParentWindowHandle, + _In_ PPH_PROCESS_ITEM ProcessItem + ) +{ + PWS_WATCH_CONTEXT context; + + context = PhAllocateZero(sizeof(WS_WATCH_CONTEXT)); + context->RefCount = 1; + context->ProcessItem = PhReferenceObject(ProcessItem); + + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_WSWATCH), + !!PhGetIntegerSetting(L"ForceNoParent") ? NULL : ParentWindowHandle, + EtpWsWatchDlgProc, + (LPARAM)context + ); +} + +static VOID EtpReferenceWsWatchContext( + _Inout_ PWS_WATCH_CONTEXT Context + ) +{ + _InterlockedIncrement(&Context->RefCount); +} + +static VOID EtpDereferenceWsWatchContext( + _Inout_ PWS_WATCH_CONTEXT Context + ) +{ + if (_InterlockedDecrement(&Context->RefCount) == 0) + { + PSINGLE_LIST_ENTRY listEntry; + + if (Context->SymbolProvider) + PhDereferenceObject(Context->SymbolProvider); + + // Free all unused results. + + PhAcquireQueuedLockExclusive(&Context->ResultListLock); + + listEntry = Context->ResultListHead.Next; + + while (listEntry) + { + PSYMBOL_LOOKUP_RESULT result; + + result = CONTAINING_RECORD(listEntry, SYMBOL_LOOKUP_RESULT, ListEntry); + listEntry = listEntry->Next; + PhDereferenceObject(result->Symbol); + PhFree(result); + } + + PhReleaseQueuedLockExclusive(&Context->ResultListLock); + + PhDereferenceObject(Context->ProcessItem); + + PhFree(Context); + } +} + +static NTSTATUS EtpSymbolLookupFunction( + _In_ PVOID Parameter + ) +{ + PSYMBOL_LOOKUP_RESULT result; + + result = Parameter; + + // Don't bother looking up the symbol if the window has already closed. + if (result->Context->Destroying) + { + EtpDereferenceWsWatchContext(result->Context); + PhFree(result); + return STATUS_SUCCESS; + } + + result->Symbol = PhGetSymbolFromAddress( + result->Context->SymbolProvider, + (ULONG64)result->Address, + NULL, + NULL, + NULL, + NULL + ); + + // Fail if we don't have a symbol. + if (!result->Symbol) + { + EtpDereferenceWsWatchContext(result->Context); + PhFree(result); + return STATUS_SUCCESS; + } + + PhAcquireQueuedLockExclusive(&result->Context->ResultListLock); + PushEntryList(&result->Context->ResultListHead, &result->ListEntry); + PhReleaseQueuedLockExclusive(&result->Context->ResultListLock); + EtpDereferenceWsWatchContext(result->Context); + + return STATUS_SUCCESS; +} + +static VOID EtpQueueSymbolLookup( + _In_ PWS_WATCH_CONTEXT Context, + _In_ PVOID Address + ) +{ + PSYMBOL_LOOKUP_RESULT result; + + result = PhAllocate(sizeof(SYMBOL_LOOKUP_RESULT)); + result->Context = Context; + result->Address = Address; + EtpReferenceWsWatchContext(Context); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), EtpSymbolLookupFunction, result); +} + +static PPH_STRING EtpGetBasicSymbol( + _In_ PPH_SYMBOL_PROVIDER SymbolProvider, + _In_ ULONG64 Address + ) +{ + ULONG64 modBase; + PPH_STRING fileName = NULL; + PPH_STRING baseName = NULL; + PPH_STRING symbol; + + modBase = PhGetModuleFromAddress(SymbolProvider, Address, &fileName); + + if (!fileName) + { + symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * sizeof(WCHAR)); + PhPrintPointer(symbol->Buffer, (PVOID)Address); + PhTrimToNullTerminatorString(symbol); + } + else + { + PH_FORMAT format[3]; + + baseName = PhGetBaseName(fileName); + + PhInitFormatSR(&format[0], baseName->sr); + PhInitFormatS(&format[1], L"+0x"); + PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase)); + + symbol = PhFormat(format, 3, baseName->Length + 6 + 32); + } + + if (fileName) + PhDereferenceObject(fileName); + if (baseName) + PhDereferenceObject(baseName); + + return symbol; +} + +static VOID EtpProcessSymbolLookupResults( + _In_ HWND hwndDlg, + _In_ PWS_WATCH_CONTEXT Context + ) +{ + PSINGLE_LIST_ENTRY listEntry; + + // Remove all results. + PhAcquireQueuedLockExclusive(&Context->ResultListLock); + listEntry = Context->ResultListHead.Next; + Context->ResultListHead.Next = NULL; + PhReleaseQueuedLockExclusive(&Context->ResultListLock); + + // Update the list view with the results. + while (listEntry) + { + PSYMBOL_LOOKUP_RESULT result; + + result = CONTAINING_RECORD(listEntry, SYMBOL_LOOKUP_RESULT, ListEntry); + listEntry = listEntry->Next; + + PhSetListViewSubItem( + Context->ListViewHandle, + PhFindListViewItemByParam(Context->ListViewHandle, -1, result->Address), + 0, + result->Symbol->Buffer + ); + + PhDereferenceObject(result->Symbol); + PhFree(result); + } +} + +static BOOLEAN EtpUpdateWsWatch( + _In_ HWND hwndDlg, + _In_ PWS_WATCH_CONTEXT Context + ) +{ + NTSTATUS status; + BOOLEAN result; + ULONG returnLength; + PPROCESS_WS_WATCH_INFORMATION_EX wsWatchInfo; + + // Query WS watch information. + + if (!Context->Buffer) + return FALSE; + + status = NtQueryInformationProcess( + Context->ProcessHandle, + ProcessWorkingSetWatchEx, + Context->Buffer, + Context->BufferSize, + &returnLength + ); + + if (status == STATUS_UNSUCCESSFUL) + { + // WS Watch is not enabled. + return FALSE; + } + + if (status == STATUS_NO_MORE_ENTRIES) + { + // There were no new faults, but we still need to process symbol lookup results. + result = TRUE; + goto SkipBuffer; + } + + if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) + { + PhFree(Context->Buffer); + Context->Buffer = PhAllocate(returnLength); + Context->BufferSize = returnLength; + + status = NtQueryInformationProcess( + Context->ProcessHandle, + ProcessWorkingSetWatchEx, + Context->Buffer, + Context->BufferSize, + &returnLength + ); + } + + if (!NT_SUCCESS(status)) + { + // Error related to the buffer size. Try again later. + result = FALSE; + goto SkipBuffer; + } + + // Update the hashtable and list view. + + ExtendedListView_SetRedraw(Context->ListViewHandle, FALSE); + + wsWatchInfo = Context->Buffer; + + while (wsWatchInfo->BasicInfo.FaultingPc) + { + PVOID *entry; + WCHAR buffer[PH_INT32_STR_LEN_1]; + INT lvItemIndex; + ULONG newCount; + + // Update the count in the entry for this instruction pointer, or add a new entry if it doesn't exist. + + entry = PhFindItemSimpleHashtable(Context->Hashtable, wsWatchInfo->BasicInfo.FaultingPc); + + if (entry) + { + newCount = PtrToUlong(*entry) + 1; + *entry = UlongToPtr(newCount); + lvItemIndex = PhFindListViewItemByParam(Context->ListViewHandle, -1, wsWatchInfo->BasicInfo.FaultingPc); + } + else + { + PPH_STRING basicSymbol; + + newCount = 1; + PhAddItemSimpleHashtable(Context->Hashtable, wsWatchInfo->BasicInfo.FaultingPc, UlongToPtr(1)); + + // Get a basic symbol name (module+offset). + basicSymbol = EtpGetBasicSymbol(Context->SymbolProvider, (ULONG64)wsWatchInfo->BasicInfo.FaultingPc); + + lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, basicSymbol->Buffer, wsWatchInfo->BasicInfo.FaultingPc); + PhDereferenceObject(basicSymbol); + + // Queue a full symbol lookup. + EtpQueueSymbolLookup(Context, wsWatchInfo->BasicInfo.FaultingPc); + } + + // Update the count in the list view item. + PhPrintUInt32(buffer, newCount); + PhSetListViewSubItem( + Context->ListViewHandle, + lvItemIndex, + 1, + buffer + ); + + wsWatchInfo++; + } + + ExtendedListView_SetRedraw(Context->ListViewHandle, TRUE); + result = TRUE; + +SkipBuffer: + EtpProcessSymbolLookupResults(hwndDlg, Context); + ExtendedListView_SortItems(Context->ListViewHandle); + + return result; +} + +static BOOLEAN NTAPI EnumGenericModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PWS_WATCH_CONTEXT context = Context; + + // If we're loading kernel module symbols for a process other than + // System, ignore modules which are in user space. This may happen + // in Windows 7. + if ( + context->LoadingSymbolsForProcessId == SYSTEM_PROCESS_ID && + (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress + ) + return TRUE; + + PhLoadModuleSymbolProvider(context->SymbolProvider, Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, Module->Size); + + return TRUE; +} + +INT_PTR CALLBACK EtpWsWatchDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PWS_WATCH_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PWS_WATCH_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + context->WindowHandle = hwndDlg; + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 340, L"Instruction"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Count"); + PhSetExtendedListView(context->ListViewHandle); + PhLoadListViewColumnsFromSetting(SETTING_NAME_WSWATCH_COLUMNS, context->ListViewHandle); + ExtendedListView_SetSort(context->ListViewHandle, 1, DescendingSortOrder); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + if (PhGetIntegerPairSetting(SETTING_NAME_WSWATCH_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_WSWATCH_WINDOW_POSITION, SETTING_NAME_WSWATCH_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + context->Hashtable = PhCreateSimpleHashtable(64); + context->BufferSize = 0x2000; + context->Buffer = PhAllocate(context->BufferSize); + + PhInitializeQueuedLock(&context->ResultListLock); + context->SymbolProvider = PhCreateSymbolProvider(context->ProcessItem->ProcessId); + PhLoadSymbolProviderOptions(context->SymbolProvider); + + if (!context->SymbolProvider || !context->SymbolProvider->IsRealHandle) + { + PhShowError(hwndDlg, L"Unable to open the process."); + EndDialog(hwndDlg, IDCANCEL); + break; + } + + context->ProcessHandle = context->SymbolProvider->ProcessHandle; + + // Load symbols for both process and kernel modules. + context->LoadingSymbolsForProcessId = context->ProcessItem->ProcessId; + PhEnumGenericModules( + NULL, + context->ProcessHandle, + 0, + EnumGenericModulesCallback, + context + ); + context->LoadingSymbolsForProcessId = SYSTEM_PROCESS_ID; + PhEnumGenericModules( + SYSTEM_PROCESS_ID, + NULL, + 0, + EnumGenericModulesCallback, + context + ); + + context->Enabled = EtpUpdateWsWatch(hwndDlg, context); + + if (context->Enabled) + { + // WS Watch is already enabled for the process. Enable updating. + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE), FALSE); + ShowWindow(GetDlgItem(hwndDlg, IDC_WSWATCHENABLED), SW_SHOW); + SetTimer(hwndDlg, 1, 1000, NULL); + } + else + { + // WS Watch has not yet been enabled for the process. + } + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + context->Destroying = TRUE; + + PhSaveListViewColumnsToSetting(SETTING_NAME_WSWATCH_COLUMNS, context->ListViewHandle); + PhSaveWindowPlacementToSetting(SETTING_NAME_WSWATCH_WINDOW_POSITION, SETTING_NAME_WSWATCH_WINDOW_SIZE, hwndDlg); + + PhDereferenceObject(context->Hashtable); + + if (context->Buffer) + { + PhFree(context->Buffer); + context->Buffer = NULL; + } + + PhDeleteLayoutManager(&context->LayoutManager); + + EtpDereferenceWsWatchContext(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDC_ENABLE: + { + NTSTATUS status; + HANDLE processHandle; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + context->ProcessItem->ProcessId + ))) + { + status = NtSetInformationProcess( + processHandle, + ProcessWorkingSetWatchEx, + NULL, + 0 + ); + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE), FALSE); + ShowWindow(GetDlgItem(hwndDlg, IDC_WSWATCHENABLED), SW_SHOW); + SetTimer(hwndDlg, 1, 1000, NULL); + } + else + { + PhShowStatus(hwndDlg, L"Unable to enable WS watch", status, 0); + } + } + break; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_TIMER: + { + switch (wParam) + { + case 1: + { + EtpUpdateWsWatch(hwndDlg, context); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ExtraPlugins/CHANGELOG.txt b/plugins/ExtraPlugins/CHANGELOG.txt deleted file mode 100644 index 18d7d7804f0c..000000000000 --- a/plugins/ExtraPlugins/CHANGELOG.txt +++ /dev/null @@ -1,4 +0,0 @@ -1.0 - * Initial release - - diff --git a/plugins/ExtraPlugins/ExtraPlugins.rc b/plugins/ExtraPlugins/ExtraPlugins.rc deleted file mode 100644 index 1a87df91cf08..000000000000 --- a/plugins/ExtraPlugins/ExtraPlugins.rc +++ /dev/null @@ -1,170 +0,0 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000904b0" - BEGIN - VALUE "CompanyName", "dmex" - VALUE "FileDescription", "Extra plugins for Process Hacker" - VALUE "FileVersion", "1.0.0.0" - VALUE "InternalName", "dmex.ExtraPlugins" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ExtraPlugins.dll" - VALUE "ProductName", "Extra plugins for Process Hacker" - VALUE "ProductVersion", "1.0.0.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x9, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_PLUGINS_DIALOG, DIALOG - BEGIN - LEFTMARGIN, 2 - RIGHTMARGIN, 637 - TOPMARGIN, 2 - BOTTOMMARGIN, 309 - END - - IDD_DISABLED_DIALOG, DIALOG - BEGIN - LEFTMARGIN, 2 - RIGHTMARGIN, 307 - TOPMARGIN, 2 - BOTTOMMARGIN, 176 - HORZGUIDE, 2 - END -END -#endif // APSTUDIO_INVOKED - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_PLUGINS_DIALOG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DISABLED_DIALOG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_PLUGINS_DIALOG DIALOGEX 0, 0, 639, 313 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Plugin Manager" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Installed",IDC_INSTALLED,2,2,65,14,NOT WS_TABSTOP - PUSHBUTTON "Browse",IDC_BROWSE,73,2,64,14,NOT WS_TABSTOP - PUSHBUTTON "Updates (0)",IDC_UPDATES,143,2,65,14,NOT WS_TABSTOP - EDITTEXT IDC_SEARCHBOX,483,2,153,14,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE - CONTROL "",IDC_PLUGINTREE,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,2,18,635,275,WS_EX_CLIENTEDGE - PUSHBUTTON "Close",IDOK,587,295,50,14 - PUSHBUTTON "Disabled Plugins (0)",IDC_DISABLED,2,295,75,14,NOT WS_TABSTOP - PUSHBUTTON "Clean up",IDC_CLEANUP,81,295,75,14,NOT WS_VISIBLE -END - -IDD_DISABLED_DIALOG DIALOGEX 0, 0, 309, 178 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Disabled Plugins" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "OK",IDOK,257,162,50,14 - CONTROL "",IDC_LIST_DISABLED,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,2,15,305,145 - LTEXT "Changes may require a restart to take effect.",IDC_INSTRUCTION,3,3,154,8 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// PNG -// - -IDB_SETTINGS_PNG PNG "resources\\cog_edit_modern.png" - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - diff --git a/plugins/ExtraPlugins/ExtraPlugins.vcxproj b/plugins/ExtraPlugins/ExtraPlugins.vcxproj deleted file mode 100644 index d42305f9cc4d..000000000000 --- a/plugins/ExtraPlugins/ExtraPlugins.vcxproj +++ /dev/null @@ -1,108 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5} - ExtraPlugins - Win32Proj - ExtraPlugins - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - windowscodecs.lib;bcrypt.lib;winhttp.lib;uxtheme.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - windowscodecs.lib;bcrypt.lib;winhttp.lib;uxtheme.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - windowscodecs.lib;bcrypt.lib;winhttp.lib;uxtheme.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - windowscodecs.lib;bcrypt.lib;winhttp.lib;uxtheme.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/ExtraPlugins/ExtraPlugins.vcxproj.filters b/plugins/ExtraPlugins/ExtraPlugins.vcxproj.filters deleted file mode 100644 index 8cd8cd03c4e7..000000000000 --- a/plugins/ExtraPlugins/ExtraPlugins.vcxproj.filters +++ /dev/null @@ -1,97 +0,0 @@ - - - - - {802108be-ae96-47c3-8d93-884ed6dd096a} - - - {3e65ffb8-3f3e-40d7-b3ca-d55cae8edb16} - - - {b98dd849-bbfe-4b41-8c48-f65680da5c00} - - - {d13aeaca-3136-44f8-8525-ac088e4170ad} - - - {297fc08d-cce6-41c6-8e77-63411ddd185b} - - - {59006979-4faf-455c-b58c-3f81e8ff9272} - - - {2ff6c860-c958-4bcc-8147-4093cad157e5} - - - {77191a45-2eba-438a-8ad2-853e25615850} - - - {50861cd8-df66-4551-a743-f08b54011d1e} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files\zip - - - Source Files - - - Source Files\pages - - - Source Files\pages - - - Source Files\pages - - - Source Files\pages - - - Source Files - - - Source Files - - - Source Files - - - Source Files\pages - - - - - Header Files - - - Header Files - - - Header Files\zip - - - - - Resource Files - - - - - Resource Files\Images - - - - - - \ No newline at end of file diff --git a/plugins/ExtraPlugins/cloud.c b/plugins/ExtraPlugins/cloud.c deleted file mode 100644 index 68dc3343b6ac..000000000000 --- a/plugins/ExtraPlugins/cloud.c +++ /dev/null @@ -1,372 +0,0 @@ -/* -* Process Hacker Extra Plugins - -* Plugin Manager -* -* Copyright (C) 2016-2017 dmex -* -* This file is part of Process Hacker. -* -* Process Hacker is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Process Hacker is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Process Hacker. If not, see . -*/ - -#include "main.h" -#include "miniz\miniz.h" - -ULONGLONG ParseVersionString( - _In_ PPH_STRING Version - ) -{ - PH_STRINGREF remainingPart, majorPart, minorPart, revisionPart, reservedPart; - ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0, reservedInteger = 0; - - PhInitializeStringRef(&remainingPart, PhGetString(Version)); - PhSplitStringRefAtChar(&remainingPart, '.', &majorPart, &remainingPart); - PhSplitStringRefAtChar(&remainingPart, '.', &minorPart, &remainingPart); - PhSplitStringRefAtChar(&remainingPart, '.', &revisionPart, &remainingPart); - PhSplitStringRefAtChar(&remainingPart, '.', &reservedPart, &remainingPart); - - PhStringToInteger64(&majorPart, 10, &majorInteger); - PhStringToInteger64(&minorPart, 10, &minorInteger); - PhStringToInteger64(&revisionPart, 10, &revisionInteger); - PhStringToInteger64(&reservedPart, 10, &reservedInteger); - - return MAKE_VERSION_ULONGLONG(majorInteger, minorInteger, reservedInteger, revisionInteger); -} - -NTSTATUS QueryPluginsCallbackThread( - _In_ PVOID Parameter - ) -{ - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - ULONG xmlStringBufferLength = 0; - PSTR xmlStringBuffer = NULL; - PVOID rootJsonObject; - PWCT_CONTEXT context = Parameter; - - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); - - if (!(httpSessionHandle = WinHttpOpen( - L"ExtraPlugins_1.0", - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { - goto CleanupExit; - } - - if (!(httpConnectionHandle = WinHttpConnect( - httpSessionHandle, - L"wj32.org", - INTERNET_DEFAULT_HTTP_PORT, - 0 - ))) - { - goto CleanupExit; - } - - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - L"/processhacker/plugins/list.php", - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH - ))) - { - goto CleanupExit; - } - - if (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { - goto CleanupExit; - } - - if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) - goto CleanupExit; - - if (!ReadRequestString(httpRequestHandle, &xmlStringBuffer, &xmlStringBufferLength)) - goto CleanupExit; - - if (!(rootJsonObject = CreateJsonParser(xmlStringBuffer))) - goto CleanupExit; - - for (INT i = 0; i < JsonGetArrayLength(rootJsonObject); i++) - { - PVOID jvalue; - PPLUGIN_NODE entry; - SYSTEMTIME time = { 0 }; - SYSTEMTIME localTime = { 0 }; - PH_STRINGREF pluginBaseName; - PPH_STRING pluginDllPath; - - entry = PhCreateAlloc(sizeof(PLUGIN_NODE)); - memset(entry, 0, sizeof(PLUGIN_NODE)); - - jvalue = JsonGetObjectArrayIndex(rootJsonObject, i); - entry->Id = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_id")); - entry->InternalName = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_internal_name")); - entry->Name = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_name")); - entry->Version = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_version")); - entry->Author = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_author")); - entry->Description = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_description")); - entry->IconUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_icon")); - entry->Requirements = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_requirements")); - entry->FeedbackUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_feedback")); - entry->Screenshots = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_screenshots")); - entry->AddedTime = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_datetime_added")); - entry->UpdatedTime = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_datetime_updated")); - entry->Download_count = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_download_count")); - entry->Download_link_32 = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_download_link_32")); - entry->Download_link_64 = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_download_link_64")); - entry->SHA2_32 = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_hash_32")); - entry->SHA2_64 = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_hash_64")); - entry->HASH_32 = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_signed_32")); - entry->HASH_64 = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_signed_64")); - entry->FileName = PhConvertUtf8ToUtf16(GetJsonValueAsString(jvalue, "plugin_filename")); - - swscanf( - PhGetString(entry->UpdatedTime), - L"%hu-%hu-%hu %hu:%hu:%hu", - &time.wYear, - &time.wMonth, - &time.wDay, - &time.wHour, - &time.wMinute, - &time.wSecond - ); - - if (SystemTimeToTzSpecificLocalTime(NULL, &time, &localTime)) - { - entry->UpdatedTime = PhFormatDateTime(&localTime); - } - - PPH_STRING directory = PhGetApplicationDirectory(); - pluginDllPath = PhConcatStrings(3, PhGetString(directory), L"Plugins\\", PhGetString(entry->FileName)); - PhDereferenceObject(directory); - - PhInitializeStringRefLongHint(&pluginBaseName, PhGetString(entry->FileName)); - - if (PhIsPluginDisabled(&pluginBaseName)) - goto CleanupExit; - - if (RtlDoesFileExists_U(PhGetString(pluginDllPath))) - { - ULONG versionSize; - PVOID versionInfo; - PUSHORT languageInfo; - UINT language; - UINT bufferSize = 0; - PWSTR buffer = NULL; - PPH_STRING internalName = NULL; - PPH_STRING version = NULL; - - entry->FilePath = PhCreateString2(&pluginDllPath->sr); - - versionSize = GetFileVersionInfoSize(PhGetString(entry->FilePath), NULL); - versionInfo = PhAllocate(versionSize); - memset(versionInfo, 0, versionSize); - - if (GetFileVersionInfo(PhGetString(entry->FilePath), 0, versionSize, versionInfo)) - { - if (VerQueryValue(versionInfo, L"\\", &buffer, &bufferSize)) - { - VS_FIXEDFILEINFO* info = (VS_FIXEDFILEINFO*)buffer; - - if (info->dwSignature == 0xfeef04bd) - { - version = PhFormatString( - L"%lu.%lu.%lu.%lu", - (info->dwFileVersionMS >> 16) & 0xffff, - (info->dwFileVersionMS >> 0) & 0xffff, - (info->dwFileVersionLS >> 16) & 0xffff, - (info->dwFileVersionLS >> 0) & 0xffff - ); - } - } - - if (VerQueryValue(versionInfo, L"\\VarFileInfo\\Translation", &languageInfo, &language)) - { - PPH_STRING internalNameString = PhFormatString( - L"\\StringFileInfo\\%04x%04x\\InternalName", - languageInfo[0], - languageInfo[1] - ); - - if (VerQueryValue(versionInfo, PhGetStringOrEmpty(internalNameString), &buffer, &bufferSize)) - { - internalName = PhCreateStringEx(buffer, bufferSize * sizeof(WCHAR)); - } - - PhDereferenceObject(internalNameString); - } - } - - PhFree(versionInfo); - - if (entry->PluginInstance = (PPHAPP_PLUGIN)PhFindPlugin(PhGetString(entry->InternalName))) - { - ULONGLONG currentVersion = ParseVersionString(version); - ULONGLONG latestVersion = ParseVersionString(entry->Version); - - entry->PluginOptions = entry->PluginInstance->Information.HasOptions; - - if (currentVersion < latestVersion) - { - entry->State = PLUGIN_STATE_UPDATE; - PostMessage(context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry); - } - } - else - { - entry->State = PLUGIN_STATE_RESTART; - PostMessage(context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry); - } - } - else - { - entry->State = PLUGIN_STATE_REMOTE; - PostMessage(context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry); - } - } - -CleanupExit: - - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); - - if (xmlStringBuffer) - PhFree(xmlStringBuffer); - - PostMessage(context->DialogHandle, ID_UPDATE_COUNT, 0, 0); - - return STATUS_SUCCESS; -} - -NTSTATUS SetupExtractBuild( - _In_ PVOID Parameter - ) -{ - static PH_STRINGREF pluginsDirectory = PH_STRINGREF_INIT(L"plugins\\"); - mz_bool status = MZ_FALSE; - mz_zip_archive zip_archive; - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter; - - memset(&zip_archive, 0, sizeof(zip_archive)); - - // TODO: Move existing folder items. - - if (!(status = mz_zip_reader_init_file(&zip_archive, PhGetStringOrEmpty(context->SetupFilePath), 0))) - { - goto error; - } - - for (ULONG i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) - { - mz_zip_archive_file_stat stat; - - if (!mz_zip_reader_file_stat(&zip_archive, i, &stat)) - { - continue; - } - - if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) - { - PPH_STRING directory; - PPH_STRING fileName; - PPH_STRING fullSetupPath; - PPH_STRING extractPath; - ULONG indexOfFileName = -1; - - fileName = PhConvertUtf8ToUtf16(stat.m_filename); - directory = PhGetApplicationDirectory(); - extractPath = PhConcatStringRef3(&directory->sr, &pluginsDirectory, &fileName->sr); - fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(extractPath), &indexOfFileName); - - SHCreateDirectoryEx(NULL, PhGetStringOrEmpty(fullSetupPath), NULL); - - PhDereferenceObject(fullSetupPath); - PhDereferenceObject(extractPath); - PhDereferenceObject(directory); - PhDereferenceObject(fileName); - } - else - { - PPH_STRING directory; - PPH_STRING fileName; - PPH_STRING fullSetupPath; - PPH_STRING extractPath; - PPH_STRING directoryPath; - PPH_STRING fileNameString; - ULONG indexOfFileName = -1; - - fileName = PhConvertUtf8ToUtf16(stat.m_filename); - directory = PhGetApplicationDirectory(); - extractPath = PhConcatStringRef3(&directory->sr, &pluginsDirectory, &fileName->sr); - fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(extractPath), &indexOfFileName); - fileNameString = PhConcatStrings(2, fullSetupPath->Buffer, L".bak"); - - if (indexOfFileName != -1) - { - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, PhGetStringOrEmpty(directoryPath), NULL); - PhDereferenceObject(directoryPath); - } - } - - if (RtlDoesFileExists_U(PhGetStringOrEmpty(fullSetupPath))) - { - MoveFileEx(PhGetString(fullSetupPath), PhGetString(fileNameString), MOVEFILE_REPLACE_EXISTING); - } - - if (!mz_zip_reader_extract_to_file(&zip_archive, i, PhGetString(fullSetupPath), 0)) - { - goto error; - } - - PhDereferenceObject(fileNameString); - PhDereferenceObject(fullSetupPath); - PhDereferenceObject(extractPath); - PhDereferenceObject(directory); - PhDereferenceObject(fileName); - } - } - - mz_zip_reader_end(&zip_archive); - return STATUS_SUCCESS; - -error: - mz_zip_reader_end(&zip_archive); - return STATUS_FAIL_CHECK; -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/dialog.c b/plugins/ExtraPlugins/dialog.c deleted file mode 100644 index c272e21a2e9f..000000000000 --- a/plugins/ExtraPlugins/dialog.c +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Process Hacker Extra Plugins - - * Plugin Manager - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "main.h" - -BOOLEAN WordMatchStringRef( - _In_ PWCT_CONTEXT Context, - _In_ PPH_STRINGREF Text - ) -{ - PH_STRINGREF part; - PH_STRINGREF remainingPart; - - remainingPart = Context->SearchboxText->sr; - - while (remainingPart.Length) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length) - { - if (PhFindStringInStringRef(Text, &part, TRUE) != -1) - return TRUE; - } - } - - return FALSE; -} - -BOOLEAN WordMatchStringZ( - _In_ PWCT_CONTEXT Context, - _In_ PWSTR Text - ) -{ - PH_STRINGREF text; - - PhInitializeStringRef(&text, Text); - return WordMatchStringRef(Context, &text); -} - -VOID UpdateTreeView( - _In_ PWCT_CONTEXT Context - ) -{ - //PhMoveReference(&Context->TreeText, PhCreateString(L"Loading Plugins...")); - //TreeNew_SetEmptyText(Context->TreeNewHandle, &Context->TreeText->sr, 0); - - PhApplyTreeNewFilters(GetPluginListFilterSupport(Context)); - TreeNew_AutoSizeColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); -} - -VOID UpdateMenuView(_In_ PWCT_CONTEXT Context, UINT Id) -{ - HWND active = Context->PluginMenuActive; - Context->PluginMenuActiveId = Id; - Context->PluginMenuActive = GetDlgItem(Context->DialogHandle, Id); - RedrawWindow(active, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE | RDW_FRAME); -} - -LRESULT DrawButton( - _In_ PWCT_CONTEXT Context, - _In_ LPARAM lParam - ) -{ - LPNMTVCUSTOMDRAW drawInfo = (LPNMTVCUSTOMDRAW)lParam; - BOOLEAN isGrayed = (drawInfo->nmcd.uItemState & CDIS_GRAYED) == CDIS_GRAYED; - BOOLEAN isChecked = (drawInfo->nmcd.uItemState & CDIS_CHECKED) == CDIS_CHECKED; - BOOLEAN isDisabled = (drawInfo->nmcd.uItemState & CDIS_DISABLED) == CDIS_DISABLED; - BOOLEAN isSelected = (drawInfo->nmcd.uItemState & CDIS_SELECTED) == CDIS_SELECTED; - BOOLEAN isHighlighted = (drawInfo->nmcd.uItemState & CDIS_HOT) == CDIS_HOT; - BOOLEAN isFocused = (drawInfo->nmcd.uItemState & CDIS_FOCUS) == CDIS_FOCUS; - - if (drawInfo->nmcd.dwDrawStage == CDDS_PREPAINT) - { - HPEN PenActive = CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); - //HPEN PenNormal = CreatePen(PS_SOLID, 1, RGB(0, 0xff, 0)); - HBRUSH BrushNormal = GetSysColorBrush(COLOR_3DFACE); - //HBRUSH BrushSelected = CreateSolidBrush(RGB(0xff, 0xff, 0xff)); - HBRUSH BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); - PPH_STRING windowText = PH_AUTO(PhGetWindowText(drawInfo->nmcd.hdr.hwndFrom)); - - SetBkMode(drawInfo->nmcd.hdc, TRANSPARENT); - - if (isHighlighted || Context->PluginMenuActiveId == drawInfo->nmcd.hdr.idFrom) - { - FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushNormal); - - SelectObject(drawInfo->nmcd.hdc, PenActive); - SelectObject(drawInfo->nmcd.hdc, Context->BoldFontHandle); - } - else if (isSelected) - { - FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushPushed); - - SelectObject(drawInfo->nmcd.hdc, PenActive); - SelectObject(drawInfo->nmcd.hdc, Context->BoldFontHandle); - } - else - { - FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushNormal); - } - - DrawText( - drawInfo->nmcd.hdc, - windowText->Buffer, - (ULONG)windowText->Length / 2, - &drawInfo->nmcd.rc, - DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE - ); - - MoveToEx(drawInfo->nmcd.hdc, drawInfo->nmcd.rc.left, drawInfo->nmcd.rc.bottom, 0); - LineTo(drawInfo->nmcd.hdc, drawInfo->nmcd.rc.right, drawInfo->nmcd.rc.bottom); - - DeletePen(PenActive); - DeleteBrush(BrushNormal); - DeleteBrush(BrushPushed); - return CDRF_SKIPDEFAULT; - } - - return CDRF_DODEFAULT; -} - -BOOLEAN ProcessTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ) -{ - PWCT_CONTEXT context = Context; - PPLUGIN_NODE node = (PPLUGIN_NODE)Node; - - // Hide plugins from different tabs - if (node->State != context->Type) - return FALSE; - - if (PhIsNullOrEmptyString(context->SearchboxText)) - return TRUE; - - if (!PhIsNullOrEmptyString(node->InternalName)) - { - if (WordMatchStringRef(context, &node->InternalName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(node->Id)) - { - if (WordMatchStringRef(context, &node->Id->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(node->Name)) - { - if (WordMatchStringRef(context, &node->Name->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(node->Version)) - { - if (WordMatchStringRef(context, &node->Version->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(node->Author)) - { - if (WordMatchStringRef(context, &node->Author->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(node->Description)) - { - if (WordMatchStringRef(context, &node->Description->sr)) - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK CloudPluginsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWCT_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT)); - memset(context, 0, sizeof(WCT_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PWCT_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - { - PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg); - PhDeleteLayoutManager(&context->LayoutManager); - PhUnregisterDialog(hwndDlg); - RemoveProp(hwndDlg, L"Context"); - PhFree(context); - - PostQuitMessage(0); - } - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - context->DialogHandle = hwndDlg; - context->PluginMenuActiveId = IDC_INSTALLED; - context->PluginMenuActive = GetDlgItem(hwndDlg, IDC_INSTALLED); - context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_PLUGINTREE); - context->SearchHandle = GetDlgItem(hwndDlg, IDC_SEARCHBOX); - context->SearchboxText = PhReferenceEmptyString(); - - CreateSearchControl(hwndDlg, context->SearchHandle, L"Search Plugins (Ctrl+K)"); - context->NormalFontHandle = CommonCreateFont(-14, FW_NORMAL, NULL); - context->BoldFontHandle = CommonCreateFont(-16, FW_BOLD, NULL); - - PhCenterWindow(hwndDlg, PhMainWndHandle); - InitializePluginsTree(context, hwndDlg, context->TreeNewHandle); - PhAddTreeNewFilter(GetPluginListFilterSupport(context), ProcessTreeFilterCallback, context); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, context->SearchHandle, NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_CLEANUP), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_DISABLED), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); - PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg); - - EnumerateLoadedPlugins(context); - SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount())->Buffer); - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), QueryPluginsCallbackThread, context); - UpdateTreeView(context); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_INSTALLED), TRUE); - } - break; - case WM_SIZE: - PhLayoutManagerLayout(&context->LayoutManager); - TreeNew_AutoSizeColumn(context->TreeNewHandle, TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE); - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case EN_CHANGE: - { - PPH_STRING newSearchboxText; - - newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchHandle)); - - if (!PhEqualString(context->SearchboxText, newSearchboxText, FALSE)) - { - // Cache the current search text for our callback. - PhSwapReference(&context->SearchboxText, newSearchboxText); - - if (!PhIsNullOrEmptyString(context->SearchboxText)) - { - // Expand the nodes to ensure that they will be visible to the user. - } - - PhApplyTreeNewFilters(GetPluginListFilterSupport(context)); - } - } - break; - } - - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - //case ID_SEARCH_CLEAR: - // { - // if (context->SearchHandle) - // { - // SetFocus(context->SearchHandle); - // Static_SetText(context->SearchHandle, L""); - - // UpdateTreeView(context); - // } - // } - // break; - case IDC_INSTALLED: - { - context->Type = PLUGIN_STATE_LOCAL; - - UpdateMenuView(context, IDC_INSTALLED); - UpdateTreeView(context); - } - break; - case IDC_BROWSE: - { - context->Type = PLUGIN_STATE_REMOTE; - - UpdateMenuView(context, IDC_BROWSE); - UpdateTreeView(context); - } - break; - case IDC_UPDATES: - { - context->Type = PLUGIN_STATE_UPDATE; - - UpdateMenuView(context, IDC_UPDATES); - UpdateTreeView(context); - } - break; - case WM_ACTION: - { - PPLUGIN_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(context)) - { - if (selectedNode->State == PLUGIN_STATE_LOCAL) - { - if (selectedNode->PluginInstance && selectedNode->PluginInstance->Information.HasOptions) - { - PhInvokeCallback(PhGetPluginCallback((PPH_PLUGIN)selectedNode->PluginInstance, PluginCallbackShowOptions), hwndDlg); - } - } - else if (selectedNode->State == PLUGIN_STATE_REMOTE) - { - if (PhGetOwnTokenAttributes().Elevated) - { - if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL)) - { - - } - } - } - else if (selectedNode->State == PLUGIN_STATE_UPDATE) - { - if (PhGetOwnTokenAttributes().Elevated) - { - if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL)) - { - - } - } - } - } - } - break; - case ID_WCTSHOWCONTEXTMENU: - { - POINT cursorPos; - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - PPLUGIN_NODE selectedNode; - - GetCursorPos(&cursorPos); - - if (!(selectedNode = WeGetSelectedWindowNode(context))) - break; - - menu = PhCreateEMenu(); - - if (selectedNode->State == PLUGIN_STATE_LOCAL) - { - HICON shieldIcon; - - shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0); - selectedItem = PhCreateEMenuItem(0, ID_MENU_UNINSTALL, L"Uninstall", NULL, NULL); - - if (shieldIcon) - { - selectedItem->Bitmap = PhIconToBitmap( - shieldIcon, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON) - ); - DestroyIcon(shieldIcon); - } - - PhInsertEMenuItem(menu, selectedItem, -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_DISABLE, L"Disable", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_PROPERTIES, L"Options", NULL, NULL), -1); - - if (!selectedNode->PluginInstance || !selectedNode->PluginInstance->Information.HasOptions) - { - PhSetFlagsEMenuItem(menu, ID_MENU_PROPERTIES, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - } - - if (!PhGetOwnTokenAttributes().Elevated) - { - PhSetFlagsEMenuItem(menu, ID_MENU_UNINSTALL, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - } - } - else - { - HICON shieldIcon; - - shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0); - selectedItem = PhCreateEMenuItem(0, ID_MENU_INSTALL, PhaFormatString(L"Install %s plugin", PhGetStringOrEmpty(selectedNode->Name))->Buffer, NULL, NULL); - - if (shieldIcon) - { - selectedItem->Bitmap = PhIconToBitmap( - shieldIcon, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON) - ); - DestroyIcon(shieldIcon); - } - - PhInsertEMenuItem(menu, selectedItem, -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_DISABLE, L"Disable", NULL, NULL), -1); - - if (!PhGetOwnTokenAttributes().Elevated) - { - PhSetFlagsEMenuItem(menu, ID_MENU_INSTALL, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - } - } - - selectedItem = PhShowEMenu( - menu, - hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - cursorPos.x, - cursorPos.y - ); - - if (selectedItem && selectedItem->Id != -1) - { - switch (selectedItem->Id) - { - case ID_MENU_INSTALL: - { - if (!PhGetOwnTokenAttributes().Elevated) - { - //SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; - //info.lpFile = L"ProcessHacker.exe"; - //info.lpParameters = L"-plugin dmex.ExtraPlugins:INSTALL -plugin dmex.ExtraPlugins:hex64value"; - //info.lpVerb = L"runas"; - //info.nShow = SW_SHOW; - //info.hwnd = hwndDlg; - - //if (!ShellExecuteEx(&info)) - //{ - - //} - } - else - { - PPLUGIN_NODE existingNode; - - if (selectedNode->State == PLUGIN_STATE_LOCAL) - { - if (existingNode = FindTreeNode( - context, - PLUGIN_STATE_REMOTE, - selectedNode->InternalName - )) - { - if (StartInitialCheck(hwndDlg, existingNode, PLUGIN_ACTION_INSTALL)) - { - - } - } - - UpdateTreeView(context); - } - else - { - if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL)) - { - - } - - if (existingNode = FindTreeNode( - context, - PLUGIN_STATE_REMOTE, - selectedNode->InternalName - )) - { - selectedNode->State = PLUGIN_STATE_RESTART; - } - - UpdateTreeView(context); - } - } - } - break; - case ID_MENU_UNINSTALL: - { - if (!PhGetOwnTokenAttributes().Elevated) - { - //SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; - //info.lpFile = L"ProcessHacker.exe"; - //info.lpParameters = L"-plugin dmex.ExtraPlugins:UNINSTALL -plugin dmex.ExtraPlugins:hex64value"; - //info.lpVerb = L"runas"; - //info.nShow = SW_SHOW; - //info.hwnd = hwndDlg; - // - //if (!ShellExecuteEx(&info)) - //{ - // - //} - } - else - { - PPLUGIN_NODE selectedNode; - PPLUGIN_NODE existingNode; - - if (selectedNode = WeGetSelectedWindowNode(context)) - { - if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_UNINSTALL)) - { - if (existingNode = FindTreeNode( - context, - PLUGIN_STATE_LOCAL, - selectedNode->InternalName - )) - { - existingNode->State = PLUGIN_STATE_RESTART; - } - } - - UpdateTreeView(context); - } - } - } - break; - case ID_MENU_DISABLE: - { - PPH_STRING baseName; - - baseName = PhGetBaseName(selectedNode->FileName); - - PhSetPluginDisabled(&baseName->sr, TRUE); - - selectedNode->State = PLUGIN_STATE_RESTART; - UpdateTreeView(context); - - SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), - PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount())) - ); - } - break; - case ID_MENU_PROPERTIES: - { - if (selectedNode->PluginInstance) - { - PhInvokeCallback(PhGetPluginCallback((PPH_PLUGIN)selectedNode->PluginInstance, PluginCallbackShowOptions), hwndDlg); - } - } - break; - } - } - } - break; - case IDCANCEL: - case IDOK: - { - //ShowUpdateDialog(hwndDlg, PLUGIN_ACTION_RESTART); - DestroyWindow(hwndDlg); - } - break; - case IDC_DISABLED: - { - ShowDisabledPluginsDialog(hwndDlg); - - PluginsClearTree(context); - EnumerateLoadedPlugins(context); - SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount()))); - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), QueryPluginsCallbackThread, context); - UpdateTreeView(context); - - SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), - PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount())) - ); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case NM_CUSTOMDRAW: - { - if (header->idFrom == IDC_INSTALLED || header->idFrom == IDC_BROWSE || header->idFrom == IDC_UPDATES) - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, DrawButton(context, lParam)); - return TRUE; - } - } - break; - } - } - break; - case ID_UPDATE_ADD: - { - PPLUGIN_NODE entry = (PPLUGIN_NODE)lParam; - - TreeNew_SetRedraw(context->TreeNewHandle, FALSE); - - PluginsAddTreeNode(context, entry); - UpdateTreeView(context); - - TreeNew_SetRedraw(context->TreeNewHandle, TRUE); - } - break; - case ID_UPDATE_COUNT: - { - //ULONG count = 0; - // - //for (ULONG i = 0; i < context->TreeContext.NodeList->Count; i++) - //{ - // PPLUGIN_NODE windowNode = context->TreeContext.NodeList->Items[i]; - // - // if (windowNode->State == PLUGIN_STATE_UPDATE) - // { - // count++; - // } - //} - // - //SetWindowText(GetDlgItem(hwndDlg, IDC_UPDATES), PhGetString(PhaFormatString(L"Updates (%lu)", count))); - } - break; - } - - return FALSE; -} - -NTSTATUS CloudDialogThread( - _In_ PVOID Parameter - ) -{ - BOOL result; - MSG message; - HWND cloudDialogHandle; - PH_AUTO_POOL autoPool; - - PhInitializeAutoPool(&autoPool); - - cloudDialogHandle = CreateDialog( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_PLUGINS_DIALOG), - NULL, - CloudPluginsDlgProc - ); - - if (IsIconic(cloudDialogHandle)) - ShowWindow(cloudDialogHandle, SW_RESTORE); - else - ShowWindow(cloudDialogHandle, SW_SHOW); - - SetForegroundWindow(cloudDialogHandle); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (!IsDialogMessage(cloudDialogHandle, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - - -VOID ShowPluginManagerDialog( - VOID - ) -{ - HANDLE threadHandle; - - if (threadHandle = PhCreateThread(0, CloudDialogThread, NULL)) - { - NtClose(threadHandle); - } -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/disabled.c b/plugins/ExtraPlugins/disabled.c deleted file mode 100644 index ff4405df5234..000000000000 --- a/plugins/ExtraPlugins/disabled.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Process Hacker Extra Plugins - - * Plugin Manager - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "main.h" - -VOID PhAddDisabledPlugins( - _In_ PPLUGIN_DISABLED_CONTEXT Context - ) -{ - PPH_STRING disabled; - PH_STRINGREF remainingPart; - PH_STRINGREF part; - PPH_STRING displayText; - INT lvItemIndex; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - remainingPart = disabled->sr; - - while (remainingPart.Length) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length) - { - displayText = PhCreateString2(&part); - - PhAcquireQueuedLockExclusive(&Context->ListLock); - lvItemIndex = PhAddListViewItem(Context->ListViewHandle, MAXINT, PhGetString(displayText), displayText); - PhReleaseQueuedLockExclusive(&Context->ListLock); - - ListView_SetItemState(Context->ListViewHandle, lvItemIndex, ITEM_CHECKED, LVIS_STATEIMAGEMASK); - } - } - - PhDereferenceObject(disabled); -} - -INT_PTR CALLBACK DisabledPluginsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PPLUGIN_DISABLED_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PPLUGIN_DISABLED_CONTEXT)PhAllocate(sizeof(PLUGIN_DISABLED_CONTEXT)); - memset(context, 0, sizeof(PLUGIN_DISABLED_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PPLUGIN_DISABLED_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - { - PhDeleteLayoutManager(&context->LayoutManager); - PhUnregisterDialog(hwndDlg); - RemoveProp(hwndDlg, L"Context"); - PhFree(context); - } - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - context->DialogHandle = hwndDlg; - context->ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST_DISABLED); - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); - ListView_SetExtendedListViewStyleEx(context->ListViewHandle, - LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER, - LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); - PhSetControlTheme(context->ListViewHandle, L"explorer"); - PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 400, L"Property"); - PhSetExtendedListView(context->ListViewHandle); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); - - PhAddDisabledPlugins(context); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDOK), TRUE); - } - break; - case WM_SIZE: - PhLayoutManagerLayout(&context->LayoutManager); - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDCANCEL: - case IDOK: - { - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - if (header->code == LVN_ITEMCHANGED) - { - LPNM_LISTVIEW listView = (LPNM_LISTVIEW)lParam; - - if (!PhTryAcquireReleaseQueuedLockExclusive(&context->ListLock)) - break; - - if (listView->uChanged & LVIF_STATE) - { - switch (listView->uNewState & LVIS_STATEIMAGEMASK) - { - case 0x2000: // checked - { - PPH_STRING param = (PPH_STRING)listView->lParam; - - PhSetPluginDisabled(¶m->sr, TRUE); - - /*if (existingNode = FindTreeNode( - &context->TreeContext, - PLUGIN_STATE_LOCAL, - selectedNode->InternalName - )) - { - existingNode->State = PLUGIN_STATE_RESTART; - }*/ - - //entry->State = PLUGIN_STATE_LOCAL; - - //context->OptionsChanged = TRUE; - } - break; - case 0x1000: // unchecked - { - PPH_STRING param = (PPH_STRING)listView->lParam; - - PhSetPluginDisabled(¶m->sr, FALSE); - - //context->OptionsChanged = TRUE; - } - break; - } - } - } - } - break; - } - - return FALSE; -} - -VOID ShowDisabledPluginsDialog( - _In_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_DIALOG1), - Parent, - DisabledPluginsDlgProc - ); -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/main.c b/plugins/ExtraPlugins/main.c deleted file mode 100644 index 24daa6d24447..000000000000 --- a/plugins/ExtraPlugins/main.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Process Hacker Extra Plugins - - * Plugin Manager - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "main.h" - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; - -BOOLEAN LastCleanupExpired( - VOID - ) -{ - ULONG64 lastUpdateTimeTicks = 0; - LARGE_INTEGER currentUpdateTimeTicks; - PPH_STRING lastUpdateTimeString; - - PhQuerySystemTime(¤tUpdateTimeTicks); - - lastUpdateTimeString = PhGetStringSetting(SETTING_NAME_LAST_CLEANUP); - PhStringToInteger64(&lastUpdateTimeString->sr, 0, &lastUpdateTimeTicks); - PhDereferenceObject(lastUpdateTimeString); - - if (currentUpdateTimeTicks.QuadPart - lastUpdateTimeTicks >= 25 * PH_TICKS_PER_DAY) - { - PPH_STRING currentUpdateTimeString = PhFormatUInt64(currentUpdateTimeTicks.QuadPart, FALSE); - - PhSetStringSetting2(SETTING_NAME_LAST_CLEANUP, ¤tUpdateTimeString->sr); - - PhDereferenceObject(currentUpdateTimeString); - return TRUE; - } - - return FALSE; -} - -BOOLEAN PluginsCleanupDirectoryCallback( - _In_ PFILE_DIRECTORY_INFORMATION Information, - _In_opt_ PVOID Context - ) -{ - PH_STRINGREF baseName; - PPH_STRING fileName; - PPH_STRING rootDirectoryPath; - - rootDirectoryPath = Context; - baseName.Buffer = Information->FileName; - baseName.Length = Information->FileNameLength; - - if (PhEqualStringRef2(&baseName, L".", TRUE) || PhEqualStringRef2(&baseName, L"..", TRUE)) - return TRUE; - - if (Information->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - HANDLE pluginsDirectoryHandle; - PPH_STRING directoryPath; - PPH_STRING PluginsDirectoryPath; - - directoryPath = PhCreateString2(&baseName); - PluginsDirectoryPath = PhConcatStrings(4, rootDirectoryPath->Buffer, L"\\", directoryPath->Buffer, L"\\"); - - if (NT_SUCCESS(PhCreateFileWin32( - &pluginsDirectoryHandle, - PluginsDirectoryPath->Buffer, - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ, - FILE_OPEN, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*"); - - PhEnumDirectoryFile(pluginsDirectoryHandle, &pattern, PluginsCleanupDirectoryCallback, PluginsDirectoryPath); - NtClose(pluginsDirectoryHandle); - } - } - else if (PhEndsWithStringRef2(&baseName, L".bak", TRUE)) - { - NTSTATUS status; - HANDLE fileHandle; - FILE_DISPOSITION_INFORMATION dispositionInfo; - IO_STATUS_BLOCK isb; - - fileName = PhCreateStringEx(NULL, rootDirectoryPath->Length + Information->FileNameLength); - memcpy(fileName->Buffer, rootDirectoryPath->Buffer, rootDirectoryPath->Length); - memcpy(&fileName->Buffer[rootDirectoryPath->Length / 2], Information->FileName, Information->FileNameLength); - - if (NT_SUCCESS(status = PhCreateFileWin32( - &fileHandle, - fileName->Buffer, - FILE_GENERIC_WRITE | DELETE, - 0, - 0, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - dispositionInfo.DeleteFile = TRUE; - - NtSetInformationFile( - fileHandle, - &isb, - &dispositionInfo, - sizeof(FILE_DISPOSITION_INFORMATION), - FileDispositionInformation - ); - - NtClose(fileHandle); - } - - PhDereferenceObject(fileName); - } - - return TRUE; -} - -VOID PluginsCleanup( - VOID - ) -{ - static UNICODE_STRING pluginsPattern = RTL_CONSTANT_STRING(L"*"); - HANDLE pluginsDirectoryHandle; - PPH_STRING pluginsDirectory; - PPH_STRING PluginsDirectoryPath; - - pluginsDirectory = PhGetStringSetting(L"PluginsDirectory"); - - if (RtlDetermineDosPathNameType_U(PhGetString(pluginsDirectory)) == RtlPathTypeRelative) - { - PluginsDirectoryPath = PhConcatStrings( - 4, - PhGetString(PhGetApplicationDirectory()), - L"\\", - PhGetStringOrEmpty(pluginsDirectory), - L"\\" - ); - PhDereferenceObject(pluginsDirectory); - } - else - { - PluginsDirectoryPath = pluginsDirectory; - } - - if (NT_SUCCESS(PhCreateFileWin32( - &pluginsDirectoryHandle, - PhGetStringOrEmpty(PluginsDirectoryPath), - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ, - FILE_OPEN, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - PhEnumDirectoryFile( - pluginsDirectoryHandle, - &pluginsPattern, - PluginsCleanupDirectoryCallback, - PluginsDirectoryPath - ); - - NtClose(pluginsDirectoryHandle); - } -} - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_LIST pluginArgvList = Parameter; - - if (pluginArgvList) - { - for (ULONG i = 0; i < pluginArgvList->Count; i++) - { - PPH_STRING pluginCommandParam = pluginArgvList->Items[i]; - - if (PhEqualString2(pluginCommandParam, L"INSTALL", TRUE)) - { - //PPH_STRING directory; - //PPH_STRING path; - // - //directory = PH_AUTO(PhGetApplicationDirectory()); - //path = PhaCreateString(L"\\plugins"); - //path = PH_AUTO(PhConcatStringRef2(&directory->sr, &path->sr)); - // - //if (MoveFileWithProgress( - // L"new.plugin", - // path->Buffer, - // NULL, - // NULL, - // MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING - // )) - //{ - // - //} - - //NtTerminateProcess(NtCurrentProcess(), EXIT_SUCCESS); - } - else if (PhEqualString2(pluginCommandParam, L"UNINSTALL", TRUE)) - { - //NtTerminateProcess(NtCurrentProcess(), EXIT_SUCCESS); - } - } - } - else - { - PluginsCleanup(); - } -} - -VOID NTAPI UnloadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_EMENU_ITEM pluginMenu; - - if (menuInfo->u.MainMenu.SubMenuIndex != 0) - return; - - if (pluginMenu = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_DESCEND, NULL, PHAPP_ID_HACKER_PLUGINS)) - { - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, pluginMenu->Id, L"Plugins... (Beta)", NULL), PhIndexOfEMenuItem(menuInfo->Menu, pluginMenu)); - PhRemoveEMenuItem(menuInfo->Menu, pluginMenu, 0); - } -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case PHAPP_ID_HACKER_PLUGINS: - ShowPluginManagerDialog(); - break; - } -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - //ShowOptionsDialog((HWND)Parameter); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { StringSettingType, SETTING_NAME_TREE_LIST_COLUMNS, L"" }, - { StringSettingType, SETTING_NAME_LAST_CLEANUP, L"" }, - { IntegerPairSettingType, SETTING_NAME_WINDOW_POSITION, L"100,100" }, - { ScalableIntegerPairSettingType, SETTING_NAME_WINDOW_SIZE, L"@96|690,540" } - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Extra Plugins Manager"; - info->Author = L"dmex"; - info->Description = L"Process Hacker plugins from the community."; - info->HasOptions = FALSE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackUnload), - UnloadCallback, - NULL, - &PluginUnloadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), - MainMenuInitializingCallback, - NULL, - &MainMenuInitializingCallbackRegistration - ); - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - } - - return TRUE; -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/main.h b/plugins/ExtraPlugins/main.h deleted file mode 100644 index aaa69f001f96..000000000000 --- a/plugins/ExtraPlugins/main.h +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Process Hacker Extra Plugins - - * Plugin Manager - * - * Copyright (C) 2013 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef _WCT_H_ -#define _WCT_H_ - -#define CINTERFACE -#define COBJMACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "resource.h" - -#define IDD_WCT_MENUITEM 1000 -#define PH_UPDATEISERRORED (WM_APP + 501) -#define PH_UPDATEAVAILABLE (WM_APP + 502) -#define PH_UPDATENEWER (WM_APP + 504) -#define PH_UPDATESUCCESS (WM_APP + 505) -#define PH_UPDATEFAILURE (WM_APP + 506) -#define WM_SHOWDIALOG (WM_APP + 550) -#define WM_ACTION (WM_APP + 1550) -#define ID_WCTSHOWCONTEXTMENU 19584 - -#define PLUGIN_NAME L"dmex.ExtraPlugins" -#define SETTING_NAME_TREE_LIST_COLUMNS (PLUGIN_NAME L".TreeListColumns") -#define SETTING_NAME_WINDOW_POSITION (PLUGIN_NAME L".WindowPosition") -#define SETTING_NAME_WINDOW_SIZE (PLUGIN_NAME L".WindowSize") -#define SETTING_NAME_LAST_CLEANUP (PLUGIN_NAME L".LastCleanupCheckTime") - -#define ID_UPDATE_ADD (WM_APP + 8002) -#define ID_UPDATE_COUNT (WM_APP + 8003) - -#define MAKE_VERSION_ULONGLONG(major, minor, build, revision) \ - (((ULONGLONG)(major) << 48) | \ - ((ULONGLONG)(minor) << 32) | \ - ((ULONGLONG)(build) << 16) | \ - ((ULONGLONG)(revision) << 0)) - -#define ITEM_CHECKED (INDEXTOSTATEIMAGEMASK(2)) -#define ITEM_UNCHECKED (INDEXTOSTATEIMAGEMASK(1)) - -extern PPH_PLUGIN PluginInstance; - -typedef enum _TREE_PLUGIN_STATE -{ - PLUGIN_STATE_LOCAL, - PLUGIN_STATE_RESTART, - PLUGIN_STATE_REMOTE, - PLUGIN_STATE_UPDATE, -} TREE_PLUGIN_STATE; - -typedef enum _WCT_TREE_COLUMN_ITEM_NAME -{ - TREE_COLUMN_ITEM_NAME, - TREE_COLUMN_ITEM_AUTHOR, - TREE_COLUMN_ITEM_VERSION, - TREE_COLUMN_ITEM_MAXIMUM -} WCT_TREE_COLUMN_ITEM_NAME; - -typedef struct _PHAPP_PLUGIN -{ - // Public - PH_AVL_LINKS Links; - PVOID Reserved; - PVOID DllBase; - // end - - // Private - PPH_STRING FileName; - ULONG Flags; - PH_STRINGREF Name; - PH_PLUGIN_INFORMATION Information; - - PH_CALLBACK Callbacks[PluginCallbackMaximum]; -} PHAPP_PLUGIN, *PPHAPP_PLUGIN; - -typedef struct _PLUGIN_NODE -{ - PH_TREENEW_NODE Node; - - HICON Icon; - TREE_PLUGIN_STATE State; - - BOOLEAN PluginOptions; - PPHAPP_PLUGIN PluginInstance; - - // Local - PPH_STRING FilePath; - PPH_STRING InternalName; - - // Remote - PPH_STRING Id; - PPH_STRING Name; - PPH_STRING Version; - PPH_STRING Author; - PPH_STRING Description; - PPH_STRING IconUrl; - PPH_STRING Requirements; - PPH_STRING FeedbackUrl; - PPH_STRING Screenshots; - PPH_STRING AddedTime; - PPH_STRING UpdatedTime; - PPH_STRING Download_count; - PPH_STRING Download_link_32; - PPH_STRING Download_link_64; - PPH_STRING SHA2_32; - PPH_STRING SHA2_64; - PPH_STRING HASH_32; - PPH_STRING HASH_64; - PPH_STRING FileName; - - PH_STRINGREF TextCache[TREE_COLUMN_ITEM_MAXIMUM]; -} PLUGIN_NODE, *PPLUGIN_NODE; - -typedef struct _WCT_CONTEXT -{ - HWND DialogHandle; - HWND SearchHandle; - HWND TreeNewHandle; - HWND ParentWindowHandle; - - PPH_STRING SearchboxText; - PPH_STRING TreeText; - HFONT NormalFontHandle; - HFONT BoldFontHandle; - HFONT TitleFontHandle; - - TREE_PLUGIN_STATE Type; - HWND PluginMenuActive; - UINT PluginMenuActiveId; - - PH_LAYOUT_MANAGER LayoutManager; - - ULONG TreeNewSortColumn; - PH_SORT_ORDER TreeNewSortOrder; - PH_TN_FILTER_SUPPORT FilterSupport; - PPH_HASHTABLE NodeHashtable; - PPH_LIST NodeList; -} WCT_CONTEXT, *PWCT_CONTEXT; - -// dialog.c -VOID ShowPluginManagerDialog(VOID); - -// disabled.c -VOID ShowDisabledPluginsDialog(_In_ HWND Parent); - -typedef struct _PLUGIN_DISABLED_CONTEXT -{ - PH_QUEUED_LOCK ListLock; - HWND DialogHandle; - HWND ListViewHandle; - PH_LAYOUT_MANAGER LayoutManager; -} PLUGIN_DISABLED_CONTEXT, *PPLUGIN_DISABLED_CONTEXT; - -ULONG PhDisabledPluginsCount(VOID); - -// plugin.c -PWSTR PhGetPluginBaseName(_In_ PPHAPP_PLUGIN Plugin); -BOOLEAN PhIsPluginLoadedByBaseName(_In_ PPH_STRINGREF BaseName); -BOOLEAN PhIsPluginDisabled(_In_ PPH_STRINGREF BaseName); -VOID PhSetPluginDisabled(_In_ PPH_STRINGREF BaseName, _In_ BOOLEAN Disable); - - -VOID InitializePluginsTree( - _In_ PWCT_CONTEXT context, - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle - ); - -VOID DeletePluginsTree( - _In_ PWCT_CONTEXT Context - ); - -struct _PH_TN_FILTER_SUPPORT* GetPluginListFilterSupport( - _In_ PWCT_CONTEXT Context - ); - -VOID PluginsAddTreeNode( - _In_ PWCT_CONTEXT Context, - _In_ PPLUGIN_NODE Entry - ); - -PPLUGIN_NODE FindTreeNode( - _In_ PWCT_CONTEXT Context, - _In_ TREE_PLUGIN_STATE Type, - _In_ PPH_STRING InternalName - ); - -VOID WeRemoveWindowNode( - _In_ PWCT_CONTEXT Context, - _In_ PPLUGIN_NODE WindowNode - ); - -VOID PluginsClearTree( - _In_ PWCT_CONTEXT Context - ); - -PPLUGIN_NODE WeGetSelectedWindowNode( - _In_ PWCT_CONTEXT Context - ); - -VOID WeGetSelectedWindowNodes( - _In_ PWCT_CONTEXT Context, - _Out_ PPLUGIN_NODE **Windows, - _Out_ PULONG NumberOfWindows - ); - -NTSTATUS QueryPluginsCallbackThread(_In_ PVOID Parameter); -VOID EnumerateLoadedPlugins(_In_ PWCT_CONTEXT Context); - -typedef enum _PLUGIN_ACTION -{ - PLUGIN_ACTION_RESTORE, - PLUGIN_ACTION_INSTALL, - PLUGIN_ACTION_UNINSTALL, - PLUGIN_ACTION_RESTART -} PLUGIN_ACTION; - -typedef struct _PH_UPDATER_CONTEXT -{ - BOOLEAN FixedWindowStyles; - HWND DialogHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; - - PLUGIN_ACTION Action; - PPLUGIN_NODE Node; - - PPH_STRING FileDownloadUrl; - PPH_STRING RevVersion; - PPH_STRING Size; - PPH_STRING SetupFilePath; -} PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; - -BOOLEAN ShowUpdateDialog( - _In_ HWND Parent, - _In_ PLUGIN_ACTION Action - ); - -BOOLEAN StartInitialCheck( - _In_ HWND Parent, - _In_ PPLUGIN_NODE Node, - _In_ PLUGIN_ACTION Action - ); - -// page1.c -VOID InstallPluginDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -VOID ShowAvailableDialog(_In_ PPH_UPDATER_CONTEXT Context); -VOID TaskDialogLinkClicked(_In_ PPH_UPDATER_CONTEXT Context); -VOID ShowProgressDialog(_In_ PPH_UPDATER_CONTEXT Context); -VOID ShowPluginUninstallDialog(_In_ PPH_UPDATER_CONTEXT Context); -VOID ShowUpdateFailedDialog( - _In_ PPH_UPDATER_CONTEXT Context, - _In_ BOOLEAN HashFailed, - _In_ BOOLEAN SignatureFailed - ); - -// page6.c -VOID ShowInstallRestartDialog(_In_ PPH_UPDATER_CONTEXT Context); -VOID ShowUninstallRestartDialog(_In_ PPH_UPDATER_CONTEXT Context); - -NTSTATUS UpdateDownloadThread(_In_ PVOID Parameter); -NTSTATUS SetupExtractBuild(_In_ PVOID Parameter); - -BOOLEAN ReadRequestString( - _In_ HINTERNET Handle, - _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, - _Out_ ULONG *DataLength - ); - -// verify.c -typedef struct _UPDATER_HASH_CONTEXT -{ - BCRYPT_ALG_HANDLE SignAlgHandle; - BCRYPT_ALG_HANDLE HashAlgHandle; - BCRYPT_KEY_HANDLE KeyHandle; - BCRYPT_HASH_HANDLE HashHandle; - ULONG HashObjectSize; - ULONG HashSize; - PVOID HashObject; - PVOID Hash; -} UPDATER_HASH_CONTEXT, *PUPDATER_HASH_CONTEXT; - -BOOLEAN UpdaterInitializeHash( - _Out_ PUPDATER_HASH_CONTEXT *Context - ); - -BOOLEAN UpdaterUpdateHash( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_reads_bytes_(Length) PVOID Buffer, - _In_ ULONG Length - ); - -BOOLEAN UpdaterVerifyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_ PPH_STRING Sha2Hash - ); - -BOOLEAN UpdaterVerifySignature( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_ PPH_STRING HexSignature - ); - -VOID UpdaterDestroyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context - ); - -#endif \ No newline at end of file diff --git a/plugins/ExtraPlugins/miniz/miniz.c b/plugins/ExtraPlugins/miniz/miniz.c deleted file mode 100644 index 95830af8f120..000000000000 --- a/plugins/ExtraPlugins/miniz/miniz.c +++ /dev/null @@ -1,4190 +0,0 @@ -/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing - See "unlicense" statement at the end of this file. - Rich Geldreich , last updated Oct. 13, 2013 - Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt - - Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define - MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). - - * Change History - 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug - would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() - (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. - Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - - Merged MZ_FORCEINLINE fix from hdeanclark - - Fix include before config #ifdef, thanks emil.brink - - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can - set it to 1 for real-time compression). - - Merged in some compiler fixes from paulharris's github repro. - - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch - 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). - 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly - "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). - 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. - level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. - 5/28/11 v1.11 - Added statement from unlicense.org - 5/27/11 v1.10 - Substantial compressor optimizations: - - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - - Refactored the compression code for better readability and maintainability. - - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large - drop in throughput on some files). - 5/15/11 v1.09 - Initial stable release. - - * Low-level Deflate/Inflate implementation notes: - - Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or - greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses - approximately as well as zlib. - - Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function - coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory - block large enough to hold the entire file. - - The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. - - * zlib-style API notes: - - miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in - zlib replacement in many apps: - The z_stream struct, optional memory allocation callbacks - deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound - inflateInit/inflateInit2/inflate/inflateEnd - compress, compress2, compressBound, uncompress - CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. - Supports raw deflate streams or standard zlib streams with adler-32 checking. - - Limitations: - The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. - I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but - there are no guarantees that miniz.c pulls this off perfectly. - - * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by - Alex Evans. Supports 1-4 bytes/pixel images. - - * ZIP archive API notes: - - The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to - get the job done with minimal fuss. There are simple API's to retrieve file information, read files from - existing archives, create new archives, append new files to existing archives, or clone archive data from - one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), - or you can specify custom file read/write callbacks. - - - Archive reading: Just call this function to read a single file from a disk archive: - - void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, - size_t *pSize, mz_uint zip_flags); - - For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central - directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - - - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: - - int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); - - The locate operation can optionally check file comments too, which (as one example) can be used to identify - multiple versions of the same file in an archive. This function uses a simple linear search through the central - directory, so it's not very fast. - - Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and - retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - - - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data - to disk and builds an exact image of the central directory in memory. The central directory image is written - all at once at the end of the archive file when the archive is finalized. - - The archive writer can optionally align each file's local header and file data to any power of 2 alignment, - which can be useful when the archive will be read from optical media. Also, the writer supports placing - arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still - readable by any ZIP tool. - - - Archive appending: The simple way to add a single file to an archive is to call this function: - - mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, - const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - - The archive will be created if it doesn't already exist, otherwise it'll be appended to. - Note the appending is done in-place and is not an atomic operation, so if something goes wrong - during the operation it's possible the archive could be left without a central directory (although the local - file headers and file data will be fine, so the archive will be recoverable). - - For more complex archive modification scenarios: - 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to - preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the - compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and - you're done. This is safe but requires a bunch of temporary disk space or heap memory. - - 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), - append new files as needed, then finalize the archive which will write an updated central directory to the - original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a - possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - - - ZIP archive support limitations: - No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. - Requires streams capable of seeking. - - * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the - below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. - - * Important: For best perf. be sure to customize the below macros for your target platform: - #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 - #define MINIZ_LITTLE_ENDIAN 1 - #define MINIZ_HAS_64BIT_REGISTERS 1 - - * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz - uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files - (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). -*/ - -#include "miniz.h" -#include -#include - -typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; -typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; -typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; - -#define MZ_ASSERT(x) assert(x) - -#ifdef MINIZ_NO_MALLOC -#define MZ_MALLOC(x) NULL -#define MZ_FREE(x) (void)x, ((void)0) -#define MZ_REALLOC(p, x) NULL -#else -#define MZ_MALLOC(x) malloc(x) -#define MZ_FREE(x) free(x) -#define MZ_REALLOC(p, x) realloc(p, x) -#endif - -#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) -#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) -#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN -#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) -#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) -#else -#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) -#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) -#endif - -#ifdef _MSC_VER -#define MZ_FORCEINLINE __forceinline -#elif defined(__GNUC__) -#define MZ_FORCEINLINE inline __attribute__((__always_inline__)) -#else -#define MZ_FORCEINLINE inline -#endif - - -// ------------------- zlib-style API's - -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) -{ - mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; - if (!ptr) return MZ_ADLER32_INIT; - while (buf_len) { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - return (s2 << 16) + s1; -} - -// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ -mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len) -{ - static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; - mz_uint32 crcu32 = (mz_uint32)crc; - if (!ptr) return MZ_CRC32_INIT; - crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } - return ~crcu32; -} - -void mz_free(void *p) -{ - MZ_FREE(p); -} - -#ifndef MINIZ_NO_ZLIB_APIS - -static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } -static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } -static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } - -const char *mz_version(void) -{ - return MZ_VERSION; -} - -int mz_deflateInit(mz_streamp pStream, int level) -{ - return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); -} - -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) -{ - tdefl_compressor *pComp; - mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); - - if (!pStream) return MZ_STREAM_ERROR; - if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; - - pStream->data_type = 0; - pStream->adler = MZ_ADLER32_INIT; - pStream->msg = NULL; - pStream->reserved = 0; - pStream->total_in = 0; - pStream->total_out = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - return MZ_MEM_ERROR; - - pStream->state = (struct mz_internal_state *)pComp; - - if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) - { - mz_deflateEnd(pStream); - return MZ_PARAM_ERROR; - } - - return MZ_OK; -} - -int mz_deflateReset(mz_streamp pStream) -{ - if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; - pStream->total_in = pStream->total_out = 0; - tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); - return MZ_OK; -} - -int mz_deflate(mz_streamp pStream, int flush) -{ - size_t in_bytes, out_bytes; - mz_ulong orig_total_in, orig_total_out; - int mz_status = MZ_OK; - - if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; - if (!pStream->avail_out) return MZ_BUF_ERROR; - - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - - if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) - return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; - - orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; - for (; ; ) - { - tdefl_status defl_status; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - - defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); - - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; - pStream->total_out += (mz_uint)out_bytes; - - if (defl_status < 0) - { - mz_status = MZ_STREAM_ERROR; - break; - } - else if (defl_status == TDEFL_STATUS_DONE) - { - mz_status = MZ_STREAM_END; - break; - } - else if (!pStream->avail_out) - break; - else if ((!pStream->avail_in) && (flush != MZ_FINISH)) - { - if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) - break; - return MZ_BUF_ERROR; // Can't make forward progress without some input. - } - } - return mz_status; -} - -int mz_deflateEnd(mz_streamp pStream) -{ - if (!pStream) return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} - -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) -{ - (void)pStream; - // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) - return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); -} - -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) -{ - int status; - mz_stream stream; - memset(&stream, 0, sizeof(stream)); - - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; - - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; - - status = mz_deflateInit(&stream, level); - if (status != MZ_OK) return status; - - status = mz_deflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_deflateEnd(&stream); - return (status == MZ_OK) ? MZ_BUF_ERROR : status; - } - - *pDest_len = stream.total_out; - return mz_deflateEnd(&stream); -} - -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); -} - -mz_ulong mz_compressBound(mz_ulong source_len) -{ - return mz_deflateBound(NULL, source_len); -} - -typedef struct -{ - tinfl_decompressor m_decomp; - mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; - mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; - tinfl_status m_last_status; -} inflate_state; - -int mz_inflateInit2(mz_streamp pStream, int window_bits) -{ - inflate_state *pDecomp; - if (!pStream) return MZ_STREAM_ERROR; - if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; - - pStream->data_type = 0; - pStream->adler = 0; - pStream->msg = NULL; - pStream->total_in = 0; - pStream->total_out = 0; - pStream->reserved = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); - if (!pDecomp) return MZ_MEM_ERROR; - - pStream->state = (struct mz_internal_state *)pDecomp; - - tinfl_init(&pDecomp->m_decomp); - pDecomp->m_dict_ofs = 0; - pDecomp->m_dict_avail = 0; - pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; - pDecomp->m_first_call = 1; - pDecomp->m_has_flushed = 0; - pDecomp->m_window_bits = window_bits; - - return MZ_OK; -} - -int mz_inflateInit(mz_streamp pStream) -{ - return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); -} - -int mz_inflate(mz_streamp pStream, int flush) -{ - inflate_state* pState; - mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; - size_t in_bytes, out_bytes, orig_avail_in; - tinfl_status status; - - if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - - pState = (inflate_state*)pStream->state; - if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; - orig_avail_in = pStream->avail_in; - - first_call = pState->m_first_call; pState->m_first_call = 0; - if (pState->m_last_status < 0) return MZ_DATA_ERROR; - - if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - pState->m_has_flushed |= (flush == MZ_FINISH); - - if ((flush == MZ_FINISH) && (first_call)) - { - // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. - decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); - pState->m_last_status = status; - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; - pStream->adler = tinfl_get_adler32(&pState->m_decomp); - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; - - if (status < 0) - return MZ_DATA_ERROR; - else if (status != TINFL_STATUS_DONE) - { - pState->m_last_status = TINFL_STATUS_FAILED; - return MZ_BUF_ERROR; - } - return MZ_STREAM_END; - } - // flush != MZ_FINISH then we must assume there's more input. - if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; - - if (pState->m_dict_avail) - { - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; - } - - for (; ; ) - { - in_bytes = pStream->avail_in; - out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; - - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); - pState->m_last_status = status; - - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); - - pState->m_dict_avail = (mz_uint)out_bytes; - - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - - if (status < 0) - return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). - else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) - return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. - else if (flush == MZ_FINISH) - { - // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. - if (status == TINFL_STATUS_DONE) - return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; - // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. - else if (!pStream->avail_out) - return MZ_BUF_ERROR; - } - else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) - break; - } - - return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; -} - -int mz_inflateEnd(mz_streamp pStream) -{ - if (!pStream) - return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} - -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - mz_stream stream; - int status; - memset(&stream, 0, sizeof(stream)); - - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; - - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; - - status = mz_inflateInit(&stream); - if (status != MZ_OK) - return status; - - status = mz_inflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_inflateEnd(&stream); - return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; - } - *pDest_len = stream.total_out; - - return mz_inflateEnd(&stream); -} - -const char *mz_error(int err) -{ - static struct { int m_err; const char *m_pDesc; } s_error_descs[] = - { - { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, - { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } - }; - mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; - return NULL; -} - -#endif //MINIZ_NO_ZLIB_APIS - -// ------------------- Low-level Decompression (completely independent from all compression API's) - -#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) -#define TINFL_MEMSET(p, c, l) memset(p, c, l) - -#define TINFL_CR_BEGIN switch(r->m_state) { case 0: -#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END -#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END -#define TINFL_CR_FINISH } - -// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never -// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. -#define TINFL_GET_BYTE(state_index, c) do { \ - if (pIn_buf_cur >= pIn_buf_end) { \ - for ( ; ; ) { \ - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ - TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ - if (pIn_buf_cur < pIn_buf_end) { \ - c = *pIn_buf_cur++; \ - break; \ - } \ - } else { \ - c = 0; \ - break; \ - } \ - } \ - } else c = *pIn_buf_cur++; } MZ_MACRO_END - -#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) -#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END -#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END - -// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. -// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a -// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the -// bit buffer contains >=15 bits (deflate's max. Huffman code size). -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ - do { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ - if (temp >= 0) { \ - code_len = temp >> 9; \ - if ((code_len) && (num_bits >= code_len)) \ - break; \ - } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ - code_len = TINFL_FAST_LOOKUP_BITS; \ - do { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ - } while (num_bits < 15); - -// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read -// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully -// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. -// The slow path is only executed at the very end of the input buffer. -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ - int temp; mz_uint code_len, c; \ - if (num_bits < 15) { \ - if ((pIn_buf_end - pIn_buf_cur) < 2) { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ - } else { \ - bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ - } \ - } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ - code_len = temp >> 9, temp &= 511; \ - else { \ - code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ - } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END - -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) -{ - static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; - static const int s_length_extra[31] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0 }; - static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; - static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; - - tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; - size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - - // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). - if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } - - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; - TINFL_CR_BEGIN - - bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); - counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); - if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); - if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } - } - - do - { - TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; - if (r->m_type == 0) - { - TINFL_SKIP_BITS(5, num_bits & 7); - for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } - if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } - while ((counter) && (num_bits)) - { - TINFL_GET_BITS(51, dist, 8); - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)dist; - counter--; - } - while (counter) - { - size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } - while (pIn_buf_cur >= pIn_buf_end) - { - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) - { - TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); - } - else - { - TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); - } - } - n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); - TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; - } - } - else if (r->m_type == 3) - { - TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); - } - else - { - if (r->m_type == 1) - { - mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; - r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); - for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; - } - else - { - for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } - r->m_table_sizes[2] = 19; - } - for (; (int)r->m_type >= 0; r->m_type--) - { - int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); - for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; - used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; - for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } - if ((65536 != total) && (used_syms > 1)) - { - TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); - } - for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) - { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; - cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); - if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); - for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) - { - tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - else tree_cur = pTable->m_tree[-tree_cur - 1]; - } - tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; - } - if (r->m_type == 2) - { - for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) - { - mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } - if ((dist == 16) && (!counter)) - { - TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); - } - num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; - TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; - } - if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) - { - TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); - } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); - } - } - for (; ; ) - { - mz_uint8 *pSrc; - for (; ; ) - { - if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) - { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); - if (counter >= 256) - break; - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)counter; - } - else - { - int sym2; mz_uint code_len; -#if TINFL_USE_64BIT_BITBUF - if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } -#else - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - counter = sym2; bit_buf >>= code_len; num_bits -= code_len; - if (counter & 256) - break; - -#if !TINFL_USE_64BIT_BITBUF - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - bit_buf >>= code_len; num_bits -= code_len; - - pOut_buf_cur[0] = (mz_uint8)counter; - if (sym2 & 256) - { - pOut_buf_cur++; - counter = sym2; - break; - } - pOut_buf_cur[1] = (mz_uint8)sym2; - pOut_buf_cur += 2; - } - } - if ((counter &= 511) == 256) break; - - num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } - - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); - num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } - - dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - { - TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); - } - - pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); - - if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) - { - while (counter--) - { - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; - } - continue; - } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - else if ((counter >= 9) && (counter <= dist)) - { - const mz_uint8 *pSrc_end = pSrc + (counter & ~7); - do - { - ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; - ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; - pOut_buf_cur += 8; - } while ((pSrc += 8) < pSrc_end); - if ((counter &= 7) < 3) - { - if (counter) - { - pOut_buf_cur[0] = pSrc[0]; - if (counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - continue; - } - } -#endif - do - { - pOut_buf_cur[0] = pSrc[0]; - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur[2] = pSrc[2]; - pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) - { - pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - } - } - } while (!(r->m_final & 1)); - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - } - TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - TINFL_CR_FINISH - - common_exit : - r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; - *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; - if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) - { - const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; - mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; - while (buf_len) - { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) - { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; - } - return status; -} - -// Higher level helper functions. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; - *pOut_len = 0; - tinfl_init(&decomp); - for (; ; ) - { - size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, - (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - src_buf_ofs += src_buf_size; - *pOut_len += dst_buf_size; - if (status == TINFL_STATUS_DONE) break; - new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; - pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); - if (!pNew_buf) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; - } - return pBuf; -} - -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); - status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; -} - -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - int result = 0; - tinfl_decompressor decomp; - mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; - if (!pDict) - return TINFL_STATUS_FAILED; - tinfl_init(&decomp); - for (; ; ) - { - size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); - in_buf_ofs += in_buf_size; - if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) - break; - if (status != TINFL_STATUS_HAS_MORE_OUTPUT) - { - result = (status == TINFL_STATUS_DONE); - break; - } - dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); - } - MZ_FREE(pDict); - *pIn_buf_size = in_buf_ofs; - return result; -} - -// ------------------- Low-level Compression (independent from all decompression API's) - -// Purposely making these tables static for faster init and thread safety. -static const mz_uint16 s_tdefl_len_sym[256] = { - 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, - 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, - 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, - 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, - 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, - 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, - 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, - 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; - -static const mz_uint8 s_tdefl_len_extra[256] = { - 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; - -static const mz_uint8 s_tdefl_small_dist_sym[512] = { - 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, - 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; - -static const mz_uint8 s_tdefl_small_dist_extra[512] = { - 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7 }; - -static const mz_uint8 s_tdefl_large_dist_sym[128] = { - 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, - 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; - -static const mz_uint8 s_tdefl_large_dist_extra[128] = { - 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, - 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; - -// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. -typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; -static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) -{ - mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); - for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } - while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; - for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) - { - const mz_uint32* pHist = &hist[pass << 8]; - mz_uint offsets[256], cur_ofs = 0; - for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } - for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; - { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } - } - return pCur_syms; -} - -// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. -static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) -{ - int root, leaf, next, avbl, used, dpth; - if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } - A[0].m_key += A[1].m_key; root = 0; leaf = 2; - for (next = 1; next < n - 1; next++) - { - if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } - else A[next].m_key = A[leaf++].m_key; - if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } - else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); - } - A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; - avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; - while (avbl > 0) - { - while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } - while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } - avbl = 2 * used; dpth++; used = 0; - } -} - -// Limits canonical Huffman code table's max code size. -enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; -static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) -{ - int i; mz_uint32 total = 0; if (code_list_len <= 1) return; - for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; - for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); - while (total != (1UL << max_code_size)) - { - pNum_codes[max_code_size]--; - for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } - total--; - } -} - -static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) -{ - int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); - if (static_table) - { - for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; - } - else - { - tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; - int num_used_syms = 0; - const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; - for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } - - pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); - - for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; - - tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - - MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); - for (i = 1, j = num_used_syms; i <= code_size_limit; i++) - for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); - } - - next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); - - for (i = 0; i < table_len; i++) - { - mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; - code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); - d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; - } -} - -#define TDEFL_PUT_BITS(b, l) do { \ - mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ - d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ - while (d->m_bits_in >= 8) { \ - if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ - *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ - d->m_bit_buffer >>= 8; \ - d->m_bits_in -= 8; \ - } \ -} MZ_MACRO_END - -#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ - if (rle_repeat_count < 3) { \ - d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ - while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ - } else { \ - d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ -} rle_repeat_count = 0; } } - -#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ - if (rle_z_count < 3) { \ - d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ - } else if (rle_z_count <= 10) { \ - d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ - } else { \ - d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ -} rle_z_count = 0; } } - -static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - -static void tdefl_start_dynamic_block(tdefl_compressor *d) -{ - int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; - mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; - - d->m_huff_count[0][256] = 1; - - tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); - tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); - - for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; - for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; - - memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); - memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); - total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; - - memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); - for (i = 0; i < total_code_sizes_to_pack; i++) - { - mz_uint8 code_size = code_sizes_to_pack[i]; - if (!code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } - } - else - { - TDEFL_RLE_ZERO_CODE_SIZE(); - if (code_size != prev_code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; - } - else if (++rle_repeat_count == 6) - { - TDEFL_RLE_PREV_CODE_SIZE(); - } - } - prev_code_size = code_size; - } - if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } - else { TDEFL_RLE_ZERO_CODE_SIZE(); } - - tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); - - TDEFL_PUT_BITS(2, 2); - - TDEFL_PUT_BITS(num_lit_codes - 257, 5); - TDEFL_PUT_BITS(num_dist_codes - 1, 5); - - for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; - num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); - for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); - - for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) - { - mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); - TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); - if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); - } -} - -static void tdefl_start_static_block(tdefl_compressor *d) -{ - mz_uint i; - mz_uint8 *p = &d->m_huff_code_sizes[0][0]; - - for (i = 0; i <= 143; ++i) *p++ = 8; - for (; i <= 255; ++i) *p++ = 9; - for (; i <= 279; ++i) *p++ = 7; - for (; i <= 287; ++i) *p++ = 8; - - memset(d->m_huff_code_sizes[1], 5, 32); - - tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); - tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); - - TDEFL_PUT_BITS(1, 2); -} - -static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - mz_uint8 *pOutput_buf = d->m_pOutput_buf; - mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; - mz_uint64 bit_buffer = d->m_bit_buffer; - mz_uint bits_in = d->m_bits_in; - -#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - - if (flags & 1) - { - mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - // This sequence coaxes MSVC into using cmov's vs. jmp's. - s0 = s_tdefl_small_dist_sym[match_dist & 511]; - n0 = s_tdefl_small_dist_extra[match_dist & 511]; - s1 = s_tdefl_large_dist_sym[match_dist >> 8]; - n1 = s_tdefl_large_dist_extra[match_dist >> 8]; - sym = (match_dist < 512) ? s0 : s1; - num_extra_bits = (match_dist < 512) ? n0 : n1; - - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else - { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - } - } - } - - if (pOutput_buf >= d->m_pOutput_buf_end) - return MZ_FALSE; - - *(mz_uint64*)pOutput_buf = bit_buffer; - pOutput_buf += (bits_in >> 3); - bit_buffer >>= (bits_in & ~7); - bits_in &= 7; - } - -#undef TDEFL_PUT_BITS_FAST - - d->m_pOutput_buf = pOutput_buf; - d->m_bits_in = 0; - d->m_bit_buffer = 0; - - while (bits_in) - { - mz_uint32 n = MZ_MIN(bits_in, 16); - TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); - bit_buffer >>= n; - bits_in -= n; - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#else -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - if (flags & 1) - { - mz_uint sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - if (match_dist < 512) - { - sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; - } - else - { - sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; - } - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else - { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - } - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS - -static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) -{ - if (static_block) - tdefl_start_static_block(d); - else - tdefl_start_dynamic_block(d); - return tdefl_compress_lz_codes(d); -} - -static int tdefl_flush_block(tdefl_compressor *d, int flush) -{ - mz_uint saved_bit_buf, saved_bits_in; - mz_uint8 *pSaved_output_buf; - mz_bool comp_block_succeeded = MZ_FALSE; - int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; - mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; - - d->m_pOutput_buf = pOutput_buf_start; - d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; - - MZ_ASSERT(!d->m_output_flush_remaining); - d->m_output_flush_ofs = 0; - d->m_output_flush_remaining = 0; - - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); - d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); - - if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) - { - TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); - } - - TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); - - pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; - - if (!use_raw_block) - comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); - - // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. - if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && - ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) - { - mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - TDEFL_PUT_BITS(0, 2); - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) - { - TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); - } - for (i = 0; i < d->m_total_lz_bytes; ++i) - { - TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); - } - } - // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. - else if (!comp_block_succeeded) - { - d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - tdefl_compress_block(d, MZ_TRUE); - } - - if (flush) - { - if (flush == TDEFL_FINISH) - { - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } - } - else - { - mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } - } - } - - MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); - - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; - - if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) - { - if (d->m_pPut_buf_func) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) - return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); - } - else if (pOutput_buf_start == d->m_output_buf) - { - int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); - d->m_out_buf_ofs += bytes_to_copy; - if ((n -= bytes_to_copy) != 0) - { - d->m_output_flush_ofs = bytes_to_copy; - d->m_output_flush_remaining = n; - } - } - else - { - d->m_out_buf_ofs += n; - } - } - - return d->m_output_flush_remaining; -} - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES -#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) -{ - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; - mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for (; ; ) - { - for (; ; ) - { - if (--num_probes_left == 0) return; -#define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; - do {} while ((TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0)); - if (!probe_len) - { - *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; - } - else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) - { - *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; - c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); - } - } -} -#else -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) -{ - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint8 *s = d->m_dict + pos, *p, *q; - mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for (; ; ) - { - for (; ; ) - { - if (--num_probes_left == 0) return; -#define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; - if (probe_len > match_len) - { - *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; - c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; - } - } -} -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN -static mz_bool tdefl_compress_fast(tdefl_compressor *d) -{ - // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. - mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; - mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; - mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - - while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) - { - const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; - mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); - d->m_src_buf_left -= num_bytes_to_process; - lookahead_size += num_bytes_to_process; - - while (num_bytes_to_process) - { - mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); - memcpy(d->m_dict + dst_pos, d->m_pSrc, n); - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); - d->m_pSrc += n; - dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; - num_bytes_to_process -= n; - } - - dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); - if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; - - while (lookahead_size >= 4) - { - mz_uint cur_match_dist, cur_match_len = 1; - mz_uint8 *pCur_dict = d->m_dict + cur_pos; - mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; - mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; - mz_uint probe_pos = d->m_hash[hash]; - d->m_hash[hash] = (mz_uint16)lookahead_pos; - - if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) - { - const mz_uint16 *p = (const mz_uint16 *)pCur_dict; - const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); - mz_uint32 probe_len = 32; - do {} while ((TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0)); - cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); - if (!probe_len) - cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; - - if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) - { - cur_match_len = 1; - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - else - { - mz_uint32 s0, s1; - cur_match_len = MZ_MIN(cur_match_len, lookahead_size); - - MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); - - cur_match_dist--; - - pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); - *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; - pLZ_code_buf += 3; - *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); - - s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; - s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; - d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; - - d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; - } - } - else - { - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - total_lz_bytes += cur_match_len; - lookahead_pos += cur_match_len; - dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; - MZ_ASSERT(lookahead_size >= cur_match_len); - lookahead_size -= cur_match_len; - - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } - } - - while (lookahead_size) - { - mz_uint8 lit = d->m_dict[cur_pos]; - - total_lz_bytes++; - *pLZ_code_buf++ = lit; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - d->m_huff_count[0][lit]++; - - lookahead_pos++; - dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; - lookahead_size--; - - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } - } - } - - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - return MZ_TRUE; -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - -static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) -{ - d->m_total_lz_bytes++; - *d->m_pLZ_code_buf++ = lit; - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - d->m_huff_count[0][lit]++; -} - -static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) -{ - mz_uint32 s0, s1; - - MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); - - d->m_total_lz_bytes += match_len; - - d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); - - match_dist -= 1; - d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); - d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; - - *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - - s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; - d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; - - if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; -} - -static mz_bool tdefl_compress_normal(tdefl_compressor *d) -{ - const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; - tdefl_flush flush = d->m_flush; - - while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) - { - mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; - // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. - if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) - { - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; - mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; - src_buf_left -= num_bytes_to_process; - d->m_lookahead_size += num_bytes_to_process; - while (pSrc != pSrc_end) - { - mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; - } - } - else - { - while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - { - mz_uint8 c = *pSrc++; - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - src_buf_left--; - d->m_dict[dst_pos] = c; - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) - { - mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; - mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - } - } - } - d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); - if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - break; - - // Simple lazy/greedy parsing state machine. - len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) - { - if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) - { - mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; - cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } - if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; - } - } - else - { - tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); - } - if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) - { - cur_match_dist = cur_match_len = 0; - } - if (d->m_saved_match_len) - { - if (cur_match_len > d->m_saved_match_len) - { - tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); - if (cur_match_len >= 128) - { - tdefl_record_match(d, cur_match_len, cur_match_dist); - d->m_saved_match_len = 0; len_to_move = cur_match_len; - } - else - { - d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - } - else - { - tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); - len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; - } - } - else if (!cur_match_dist) - tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); - else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) - { - tdefl_record_match(d, cur_match_len, cur_match_dist); - len_to_move = cur_match_len; - } - else - { - d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - // Move the lookahead forward by len_to_move bytes. - d->m_lookahead_pos += len_to_move; - MZ_ASSERT(d->m_lookahead_size >= len_to_move); - d->m_lookahead_size -= len_to_move; - d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); - // Check if it's time to flush the current LZ codes to the internal output buffer. - if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || - ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) - { - int n; - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - } - } - - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - return MZ_TRUE; -} - -static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) -{ - if (d->m_pIn_buf_size) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - } - - if (d->m_pOut_buf_size) - { - size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); - d->m_output_flush_ofs += (mz_uint)n; - d->m_output_flush_remaining -= (mz_uint)n; - d->m_out_buf_ofs += n; - - *d->m_pOut_buf_size = d->m_out_buf_ofs; - } - - return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; -} - -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) -{ - if (!d) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return TDEFL_STATUS_BAD_PARAM; - } - - d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; - d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; - d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; - d->m_out_buf_ofs = 0; - d->m_flush = flush; - - if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || - (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); - } - d->m_wants_to_finish |= (flush == TDEFL_FINISH); - - if ((d->m_output_flush_remaining) || (d->m_finished)) - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && - ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && - ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) - { - if (!tdefl_compress_fast(d)) - return d->m_prev_return_status; - } - else -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - { - if (!tdefl_compress_normal(d)) - return d->m_prev_return_status; - } - - if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) - d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); - - if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) - { - if (tdefl_flush_block(d, flush) < 0) - return d->m_prev_return_status; - d->m_finished = (flush == TDEFL_FINISH); - if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } - } - - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); -} - -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) -{ - MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); -} - -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; - d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; - d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; - if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); - d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; - d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; - d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; - d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; - d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; - d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; - d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - return TDEFL_STATUS_OKAY; -} - -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) -{ - return d->m_prev_return_status; -} - -mz_uint32 tdefl_get_adler32(tdefl_compressor *d) -{ - return d->m_adler32; -} - -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; - pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; - succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); - succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); - MZ_FREE(pComp); return succeeded; -} - -typedef struct -{ - size_t m_size, m_capacity; - mz_uint8 *m_pBuf; - mz_bool m_expandable; -} tdefl_output_buffer; - -static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) -{ - tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; - size_t new_size = p->m_size + len; - if (new_size > p->m_capacity) - { - size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; - do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); - pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; - p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; - } - memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; - return MZ_TRUE; -} - -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; - out_buf.m_expandable = MZ_TRUE; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; - *pOut_len = out_buf.m_size; return out_buf.m_pBuf; -} - -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_buf) return 0; - out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; - return out_buf.m_size; -} - -#ifndef MINIZ_NO_ZLIB_APIS -static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - -// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) -{ - mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); - if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; - - if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; - else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; - else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; - else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; - else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; - - return comp_flags; -} -#endif //MINIZ_NO_ZLIB_APIS - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) -#endif - -// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at -// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. -// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) -{ - // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. - static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; - if (!pComp) return NULL; - MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } - // write dummy header - for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); - // compress image data - tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); - for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } - if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - // write real header - *pLen_out = out_buf.m_size - 41; - { - static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; - mz_uint8 pnghdr[41] = { 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, - 0,0,(mz_uint8)(w >> 8),(mz_uint8)w,0,0,(mz_uint8)(h >> 8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, - (mz_uint8)(*pLen_out >> 24),(mz_uint8)(*pLen_out >> 16),(mz_uint8)(*pLen_out >> 8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54 }; - c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8*)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); - memcpy(out_buf.m_pBuf, pnghdr, 41); - } - // write footer (IDAT CRC-32, followed by IEND chunk) - if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); - // compute final size of file, grab compressed data buffer and return - *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; -} -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) -{ - // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) - return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); -} - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - -// ------------------- .ZIP archive reading - -#ifndef MINIZ_NO_ARCHIVE_APIS - -#ifdef MINIZ_NO_STDIO -#define MZ_FILE void * -#else -#include -#include -#include - -#if defined(_MSC_VER) || defined(__MINGW64__) -static FILE *mz_fopen(const wchar_t *pFilename, const wchar_t *pMode) -{ - FILE* pFile = NULL; - - errno_t err = _wfopen_s(&pFile, pFilename, pMode); - - return pFile; -} -static FILE *mz_freopen(const wchar_t *pPath, const wchar_t *pMode, FILE *pStream) -{ - FILE* pFile; - - if (_wfreopen_s(&pFile, pPath, pMode, pStream)) - return NULL; - - return pFile; -} -#ifndef MINIZ_NO_TIME -#include -#endif -#define MZ_FILE FILE -#define MZ_FOPEN mz_fopen -#define MZ_FCLOSE fclose -#define MZ_FREAD fread -#define MZ_FWRITE fwrite -#define MZ_FTELL64 _ftelli64 -#define MZ_FSEEK64 _fseeki64 -#define MZ_FILE_STAT_STRUCT _stat -#define MZ_FILE_STAT _wstat -#define MZ_FFLUSH fflush -#define MZ_FREOPEN mz_freopen -#define MZ_DELETE_FILE _wremove -#elif defined(__MINGW32__) -#ifndef MINIZ_NO_TIME -#include -#endif -#define MZ_FILE FILE -#define MZ_FOPEN(f, m) fopen(f, m) -#define MZ_FCLOSE fclose -#define MZ_FREAD fread -#define MZ_FWRITE fwrite -#define MZ_FTELL64 ftello64 -#define MZ_FSEEK64 fseeko64 -#define MZ_FILE_STAT_STRUCT _stat -#define MZ_FILE_STAT _stat -#define MZ_FFLUSH fflush -#define MZ_FREOPEN(f, m, s) freopen(f, m, s) -#define MZ_DELETE_FILE remove -#elif defined(__TINYC__) -#ifndef MINIZ_NO_TIME -#include -#endif -#define MZ_FILE FILE -#define MZ_FOPEN(f, m) fopen(f, m) -#define MZ_FCLOSE fclose -#define MZ_FREAD fread -#define MZ_FWRITE fwrite -#define MZ_FTELL64 ftell -#define MZ_FSEEK64 fseek -#define MZ_FILE_STAT_STRUCT stat -#define MZ_FILE_STAT stat -#define MZ_FFLUSH fflush -#define MZ_FREOPEN(f, m, s) freopen(f, m, s) -#define MZ_DELETE_FILE remove -#elif defined(__GNUC__) && _LARGEFILE64_SOURCE -#ifndef MINIZ_NO_TIME -#include -#endif -#define MZ_FILE FILE -#define MZ_FOPEN(f, m) fopen64(f, m) -#define MZ_FCLOSE fclose -#define MZ_FREAD fread -#define MZ_FWRITE fwrite -#define MZ_FTELL64 ftello64 -#define MZ_FSEEK64 fseeko64 -#define MZ_FILE_STAT_STRUCT stat64 -#define MZ_FILE_STAT stat64 -#define MZ_FFLUSH fflush -#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) -#define MZ_DELETE_FILE remove -#else -#ifndef MINIZ_NO_TIME -#include -#endif -#define MZ_FILE FILE -#define MZ_FOPEN(f, m) fopen(f, m) -#define MZ_FCLOSE fclose -#define MZ_FREAD fread -#define MZ_FWRITE fwrite -#define MZ_FTELL64 ftello -#define MZ_FSEEK64 fseeko -#define MZ_FILE_STAT_STRUCT stat -#define MZ_FILE_STAT stat -#define MZ_FFLUSH fflush -#define MZ_FREOPEN(f, m, s) freopen(f, m, s) -#define MZ_DELETE_FILE remove -#endif // #ifdef _MSC_VER -#endif // #ifdef MINIZ_NO_STDIO - -#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) - -// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. -enum -{ - // ZIP archive identifiers and record sizes - MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, - MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, - // Central directory header record offsets - MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, - MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, - MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, - MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, - // Local directory header offsets - MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, - MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, - MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, - // End of central directory offsets - MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, - MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, -}; - -typedef struct _mz_zip_array -{ - void *m_p; - size_t m_size, m_capacity; - mz_uint m_element_size; -} mz_zip_array; - -struct mz_zip_internal_state_tag -{ - mz_zip_array m_central_dir; - mz_zip_array m_central_dir_offsets; - mz_zip_array m_sorted_central_dir_offsets; - MZ_FILE *m_pFile; - void *m_pMem; - size_t m_mem_size; - size_t m_mem_capacity; -}; - -#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size -#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] - -static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) -{ - pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); - memset(pArray, 0, sizeof(mz_zip_array)); -} - -static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) -{ - void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; - if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } - if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; - pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) -{ - if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) -{ - if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } - pArray->m_size = new_size; - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) -{ - return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) -{ - size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; - memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); - return MZ_TRUE; -} - -#ifndef MINIZ_NO_TIME -static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) -{ - struct tm tm; - memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; - tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; - tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; - return mktime(&tm); -} - -static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) -{ -#ifdef _MSC_VER - struct tm tm_struct; - struct tm *tm = &tm_struct; - errno_t err = localtime_s(tm, &time); - if (err) - { - *pDOS_date = 0; *pDOS_time = 0; - return; - } -#else - struct tm *tm = localtime(&time); -#endif - *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); - *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); -} -#endif - -#ifndef MINIZ_NO_STDIO -static mz_bool mz_zip_get_file_modified_time(const wchar_t *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) -{ -#ifdef MINIZ_NO_TIME - (void)pFilename; *pDOS_date = *pDOS_time = 0; -#else - struct MZ_FILE_STAT_STRUCT file_stat; - // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. - if (MZ_FILE_STAT(pFilename, &file_stat) != 0) - return MZ_FALSE; - mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); -#endif // #ifdef MINIZ_NO_TIME - return MZ_TRUE; -} - -#ifndef MINIZ_NO_TIME - -static mz_bool mz_zip_set_file_times(const wchar_t *pFilename, time_t access_time, time_t modified_time) -{ - struct _utimbuf t; - t.actime = access_time; - t.modtime = modified_time; - - return !_wutime(pFilename, &t); -} -#endif // #ifndef MINIZ_NO_TIME -#endif // #ifndef MINIZ_NO_STDIO - -static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) -{ - (void)flags; - if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; - - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; - - pZip->m_zip_mode = MZ_ZIP_MODE_READING; - pZip->m_archive_size = 0; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; - - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (l_len < r_len) : (l < r); -} - -#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END - -// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) -static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - int start = (size - 2) >> 1, end; - while (start >= 0) - { - int child, root = start; - for (; ; ) - { - if ((child = (root << 1) + 1) >= size) - break; - child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - start--; - } - - end = size - 1; - while (end > 0) - { - int child, root = 0; - MZ_SWAP_UINT32(pIndices[end], pIndices[0]); - for (; ; ) - { - if ((child = (root << 1) + 1) >= end) - break; - child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - end--; - } -} - -static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) -{ - mz_uint cdir_size, num_this_disk, cdir_disk_index; - mz_uint64 cdir_ofs; - mz_int64 cur_file_ofs; - const mz_uint8 *p; - mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; - mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); - // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. - if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - // Find the end of central directory record by scanning the file from the end towards the beginning. - cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); - for (; ; ) - { - int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) - return MZ_FALSE; - for (i = n - 4; i >= 0; --i) - if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) - break; - if (i >= 0) - { - cur_file_ofs += i; - break; - } - if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) - return MZ_FALSE; - cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); - } - // Read and verify the end of central directory record. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || - ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) - return MZ_FALSE; - - num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); - cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); - if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) - return MZ_FALSE; - - if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - - cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); - if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) - return MZ_FALSE; - - pZip->m_central_directory_file_ofs = cdir_ofs; - - if (pZip->m_total_files) - { - mz_uint i, n; - - // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. - if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || - (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) - return MZ_FALSE; - - if (sort_central_dir) - { - if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) - return MZ_FALSE; - } - - if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) - return MZ_FALSE; - - // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). - p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; - for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) - { - mz_uint total_header_size, comp_size, decomp_size, disk_index; - if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) - return MZ_FALSE; - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); - if (sort_central_dir) - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) - return MZ_FALSE; - disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); - if ((disk_index != num_this_disk) && (disk_index != 1)) - return MZ_FALSE; - if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) - return MZ_FALSE; - if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) - return MZ_FALSE; - n -= total_header_size; p += total_header_size; - } - } - - if (sort_central_dir) - mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); - - return MZ_TRUE; -} - -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) -{ - if ((!pZip) || (!pZip->m_pRead)) - return MZ_FALSE; - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} - -static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); - memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); - return s; -} - -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) -{ - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - pZip->m_pRead = mz_zip_mem_read_func; - pZip->m_pIO_opaque = pZip; -#ifdef __cplusplus - pZip->m_pState->m_pMem = const_cast(pMem); -#else - pZip->m_pState->m_pMem = (void *)pMem; -#endif - pZip->m_pState->m_mem_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - - return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); -} - -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const wchar_t *pFilename, mz_uint32 flags) -{ - mz_uint64 file_size; - MZ_FILE* pFile = MZ_FOPEN(pFilename, L"rb"); - - if (!pFile) - return MZ_FALSE; - - if (MZ_FSEEK64(pFile, 0, SEEK_END)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - - file_size = MZ_FTELL64(pFile); - - if (!mz_zip_reader_init_internal(pZip, flags)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - - pZip->m_pRead = mz_zip_file_read_func; - pZip->m_pIO_opaque = pZip; - pZip->m_pState->m_pFile = pFile; - pZip->m_archive_size = file_size; - - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) -{ - return pZip ? pZip->m_total_files : 0; -} - -static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) -{ - if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return NULL; - return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); -} - -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) -{ - mz_uint m_bit_flag; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - return (m_bit_flag & 1); -} - -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) -{ - mz_uint filename_len, external_attr; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - - // First see if the filename ends with a '/' character. - filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_len) - { - if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') - return MZ_TRUE; - } - - // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. - // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. - // FIXME: Remove this check? Is it necessary - we already check the filename. - external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - if ((external_attr & 0x10) != 0) - return MZ_TRUE; - - return MZ_FALSE; -} - -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if ((!p) || (!pStat)) - return MZ_FALSE; - - // Unpack the central directory record. - pStat->m_file_index = file_index; - pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); - pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); - pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); - pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); -#ifndef MINIZ_NO_TIME - pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); -#endif - pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); - pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); - pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - - // Copy as much of the filename and comment as possible. - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); - memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; - - n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); - pStat->m_comment_size = n; - memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; - - return MZ_TRUE; -} - -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_buf_size) - { - n = MZ_MIN(n, filename_buf_size - 1); - memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); - pFilename[n] = '\0'; - } - return n + 1; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) -{ - mz_uint i; - if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) - return 0 == memcmp(pA, pB, len); - for (i = 0; i < len; ++i) - if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) - return MZ_FALSE; - return MZ_TRUE; -} - -static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (int)(l_len - r_len) : (l - r); -} - -static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - const mz_uint filename_len = (mz_uint)strlen(pFilename); - int l = 0, h = size - 1; - while (l <= h) - { - int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); - if (!comp) - return file_index; - else if (comp < 0) - l = m + 1; - else - h = m - 1; - } - return -1; -} - -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) -{ - mz_uint file_index; size_t name_len, comment_len; - - if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return -1; - - if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) - return mz_zip_reader_locate_file_binary_search(pZip, pName); - - name_len = strlen(pName); - - if (name_len > 0xFFFF) - return -1; - - comment_len = pComment ? strlen(pComment) : 0; - - if (comment_len > 0xFFFF) - return -1; - - for (file_index = 0; file_index < pZip->m_total_files; file_index++) - { - const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); - mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); - const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - - if (filename_len < name_len) - continue; - - if (comment_len) - { - mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); - - const char *pFile_comment = pFilename + filename_len + file_extra_len; - - if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) - continue; - } - - if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) - { - int ofs = filename_len - 1; - do - { - if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) - break; - } while (--ofs >= 0); - ofs++; - pFilename += ofs; filename_len -= ofs; - } - - if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) - return file_index; - } - - return -1; -} - -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int status = TINFL_STATUS_DONE; - mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; - mz_zip_archive_file_stat file_stat; - void *pRead_buf; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - tinfl_decompressor inflator; - - if ((buf_size) && (!pBuf)) - return MZ_FALSE; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; - - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; - - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; - - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; - - // Ensure supplied output buffer is large enough. - needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; - if (buf_size < needed_size) - return MZ_FALSE; - - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) - return MZ_FALSE; - return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); - } - - // Decompress the file either directly from memory or from a file input buffer. - tinfl_init(&inflator); - - if (pZip->m_pState->m_pMem) - { - // Read directly from the archive in memory. - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else if (pUser_read_buf) - { - // Use a user provided read buffer. - if (!user_read_buf_size) - return MZ_FALSE; - - pRead_buf = (mz_uint8 *)pUser_read_buf; - read_buf_size = user_read_buf_size; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - else - { - // Temporarily allocate a read buffer. - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#endif - return MZ_FALSE; - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - do - { - size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - out_buf_ofs += out_buf_size; - } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); - - if (status == TINFL_STATUS_DONE) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } - - if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - - return status == TINFL_STATUS_DONE; -} - -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); -} - -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); -} - -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); -} - -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) -{ - mz_uint64 comp_size, uncomp_size, alloc_size; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - void *pBuf; - - if (pSize) - *pSize = 0; - if (!p) - return NULL; - - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - - alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#endif - return NULL; - - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) - return NULL; - - if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return NULL; - } - - if (pSize) - *pSize = (size_t)alloc_size; - - return pBuf; -} - -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - { - if (pSize) *pSize = 0; - return MZ_FALSE; - } - return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); -} - -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; - mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; - mz_zip_archive_file_stat file_stat; - void *pRead_buf = NULL; void *pWrite_buf = NULL; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; - - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; - - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; - - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; - - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; - - // Decompress the file either directly from memory or from a file input buffer. - if (pZip->m_pState->m_pMem) - { - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pState->m_pMem) - { -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#endif - return MZ_FALSE; - - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) - status = TINFL_STATUS_FAILED; - else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); - - cur_file_ofs += file_stat.m_comp_size; - out_buf_ofs += file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - while (comp_remaining) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - - if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); - - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - out_buf_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - } - } - } - else - { - tinfl_decompressor inflator; - tinfl_init(&inflator); - - if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) - status = TINFL_STATUS_FAILED; - else - { - do - { - mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - - if (out_buf_size) - { - if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) - { - status = TINFL_STATUS_FAILED; - break; - } - file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); - if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) - { - status = TINFL_STATUS_FAILED; - break; - } - } - } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); - } - } - - if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } - - if (!pZip->m_pState->m_pMem) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - if (pWrite_buf) - pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); - - return status == TINFL_STATUS_DONE; -} - -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) -{ - (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); -} - -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const wchar_t *pDst_filename, mz_uint flags) -{ - mz_bool status; - mz_zip_archive_file_stat file_stat; - MZ_FILE *pFile; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - pFile = MZ_FOPEN(pDst_filename, L"wb"); - - if (!pFile) - return MZ_FALSE; - - status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); - - if (MZ_FCLOSE(pFile) == EOF) - return MZ_FALSE; - -#ifndef MINIZ_NO_TIME - if (status) - mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); -#endif - return status; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_reader_end(mz_zip_archive *pZip) -{ - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - - if (pZip->m_pState) - { - mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); - -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO - - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - } - - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const wchar_t *pDst_filename, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); -} -#endif - -// ------------------- .ZIP archive writing - -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } -static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } -#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) -#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) - -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) -{ - if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; - - if (pZip->m_file_offset_alignment) - { - // Ensure user specified file offset alignment is a power of 2. - if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) - return MZ_FALSE; - } - - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_archive_size = existing_size; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; - - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} - -static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); -#ifdef _MSC_VER - if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#else - if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#endif - return 0; - if (new_size > pState->m_mem_capacity) - { - void *pNew_block; - size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; - if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) - return 0; - pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; - } - memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); - pState->m_mem_size = (size_t)new_size; - return n; -} - -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) -{ - pZip->m_pWrite = mz_zip_heap_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) - { - if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_mem_capacity = initial_allocation_size; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); -} - -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const wchar_t *pFilename, mz_uint64 size_to_reserve_at_beginning) -{ - MZ_FILE *pFile; - pZip->m_pWrite = mz_zip_file_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (NULL == (pFile = MZ_FOPEN(pFilename, L"wb"))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_pFile = pFile; - if (size_to_reserve_at_beginning) - { - mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); - do - { - size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - cur_ofs += n; size_to_reserve_at_beginning -= n; - } while (size_to_reserve_at_beginning); - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const wchar_t *pFilename) -{ - mz_zip_internal_state *pState; - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - // No sense in trying to write to an archive that's already at the support max size - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - pState = pZip->m_pState; - - if (pState->m_pFile) - { -#ifdef MINIZ_NO_STDIO - pFilename; return MZ_FALSE; -#else - // Archive is being read from stdio - try to reopen as writable. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - if (!pFilename) - return MZ_FALSE; - pZip->m_pWrite = mz_zip_file_write_func; - if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, L"r+b", pState->m_pFile))) - { - // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. - mz_zip_reader_end(pZip); - return MZ_FALSE; - } -#endif // #ifdef MINIZ_NO_STDIO - } - else if (pState->m_pMem) - { - // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - pState->m_mem_capacity = pState->m_mem_size; - pZip->m_pWrite = mz_zip_heap_write_func; - } - // Archive is being read via a user provided read function - make sure the user has specified a write function too. - else if (!pZip->m_pWrite) - return MZ_FALSE; - - // Start writing new files at the archive's current central directory location. - pZip->m_archive_size = pZip->m_central_directory_file_ofs; - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_central_directory_file_ofs = 0; - - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) -{ - return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); -} - -typedef struct -{ - mz_zip_archive *m_pZip; - mz_uint64 m_cur_archive_file_ofs; - mz_uint64 m_comp_size; -} mz_zip_writer_add_state; - -static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) -{ - mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; - if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) - return MZ_FALSE; - pState->m_cur_archive_file_ofs += len; - pState->m_comp_size += len; - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; - size_t orig_central_dir_size = pState->m_central_dir.m_size; - mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - - // No zip64 support yet - if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) - return MZ_FALSE; - - if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) - { - // Try to push the central directory array back into its original state. - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) -{ - // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. - if (*pArchive_name == '/') - return MZ_FALSE; - while (*pArchive_name) - { - if ((*pArchive_name == '\\') || (*pArchive_name == ':')) - return MZ_FALSE; - pArchive_name++; - } - return MZ_TRUE; -} - -static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) -{ - mz_uint32 n; - if (!pZip->m_file_offset_alignment) - return 0; - n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); - return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); -} - -static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) -{ - char buf[4096]; - memset(buf, 0, MZ_MIN(sizeof(buf), n)); - while (n) - { - mz_uint32 s = MZ_MIN(sizeof(buf), n); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) - return MZ_FALSE; - cur_file_ofs += s; n -= s; - } - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) -{ - mz_uint16 method = 0, dos_time = 0, dos_date = 0; - mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - tdefl_compressor *pComp = NULL; - mz_bool store_data_uncompressed; - mz_zip_internal_state *pState; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - - pState = pZip->m_pState; - - if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) - return MZ_FALSE; - // No zip64 support yet - if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - -#ifndef MINIZ_NO_TIME - { - time_t cur_time; time(&cur_time); - mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); - } -#endif // #ifndef MINIZ_NO_TIME - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) - { - // Set DOS Subdirectory attribute bit. - ext_attributes |= 0x10; - // Subdirectories cannot contain data. - if ((buf_size) || (uncomp_size)) - return MZ_FALSE; - } - - // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) - if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) - return MZ_FALSE; - - if ((!store_data_uncompressed) && (buf_size)) - { - if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) - return MZ_FALSE; - } - - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; - - if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - { - uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); - uncomp_size = buf_size; - if (uncomp_size <= 3) - { - level = 0; - store_data_uncompressed = MZ_TRUE; - } - } - - if (store_data_uncompressed) - { - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - - cur_archive_file_ofs += buf_size; - comp_size = buf_size; - - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - method = MZ_DEFLATED; - } - else if (buf_size) - { - mz_zip_writer_add_state state; - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || - (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pComp = NULL; - - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; - - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; - - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; - - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const wchar_t *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) -{ - mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; - mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - MZ_FILE *pSrc_file = NULL; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) - return MZ_FALSE; - - pSrc_file = MZ_FOPEN(pSrc_filename, L"rb"); - if (!pSrc_file) - return MZ_FALSE; - MZ_FSEEK64(pSrc_file, 0, SEEK_END); - uncomp_size = MZ_FTELL64(pSrc_file); - MZ_FSEEK64(pSrc_file, 0, SEEK_SET); - - if (uncomp_size > 0xFFFFFFFF) - { - // No zip64 support yet - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - if (uncomp_size <= 3) - level = 0; - - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; - - if (uncomp_size) - { - mz_uint64 uncomp_remaining = uncomp_size; - void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); - if (!pRead_buf) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - if (!level) - { - while (uncomp_remaining) - { - mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); - if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); - uncomp_remaining -= n; - cur_archive_file_ofs += n; - } - comp_size = uncomp_size; - } - else - { - mz_bool result = MZ_FALSE; - mz_zip_writer_add_state state; - tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - for (; ; ) - { - size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); - tdefl_status status; - - if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) - break; - - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); - uncomp_remaining -= in_buf_size; - - status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); - if (status == TDEFL_STATUS_DONE) - { - result = MZ_TRUE; - break; - } - else if (status != TDEFL_STATUS_OKAY) - break; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - - if (!result) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - } - - MZ_FCLOSE(pSrc_file); pSrc_file = NULL; - - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; - - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; - - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; - - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) -{ - mz_uint n, bit_flags, num_alignment_padding_bytes; - mz_uint64 comp_bytes_remaining, local_dir_header_ofs; - mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - size_t orig_central_dir_size; - mz_zip_internal_state *pState; - void *pBuf; const mz_uint8 *pSrc_central_header; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) - return MZ_FALSE; - pState = pZip->m_pState; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - cur_dst_file_ofs = pZip->m_archive_size; - - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) - return MZ_FALSE; - cur_dst_file_ofs += num_alignment_padding_bytes; - local_dir_header_ofs = cur_dst_file_ofs; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) - return MZ_FALSE; - - while (comp_bytes_remaining) - { - n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_src_file_ofs += n; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_dst_file_ofs += n; - - comp_bytes_remaining -= n; - } - - bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); - if (bit_flags & 8) - { - // Copy data descriptor - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - - n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - - cur_src_file_ofs += n; - cur_dst_file_ofs += n; - } - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - - // no zip64 support yet - if (cur_dst_file_ofs > 0xFFFFFFFF) - return MZ_FALSE; - - orig_central_dir_size = pState->m_central_dir.m_size; - - memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) - return MZ_FALSE; - - n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - if (pState->m_central_dir.m_size > 0xFFFFFFFF) - return MZ_FALSE; - n = (mz_uint32)orig_central_dir_size; - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - pZip->m_total_files++; - pZip->m_archive_size = cur_dst_file_ofs; - - return MZ_TRUE; -} - -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState; - mz_uint64 central_dir_ofs, central_dir_size; - mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - - pState = pZip->m_pState; - - // no zip64 support yet - if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - central_dir_ofs = 0; - central_dir_size = 0; - if (pZip->m_total_files) - { - // Write central directory - central_dir_ofs = pZip->m_archive_size; - central_dir_size = pState->m_central_dir.m_size; - pZip->m_central_directory_file_ofs = central_dir_ofs; - if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) - return MZ_FALSE; - pZip->m_archive_size += central_dir_size; - } - - // Write end of central directory record - MZ_CLEAR_OBJ(hdr); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); - - if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) - return MZ_FALSE; -#ifndef MINIZ_NO_STDIO - if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) - return MZ_FALSE; -#endif // #ifndef MINIZ_NO_STDIO - - pZip->m_archive_size += sizeof(hdr); - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; - return MZ_TRUE; -} - -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) -{ - if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) - return MZ_FALSE; - if (pZip->m_pWrite != mz_zip_heap_write_func) - return MZ_FALSE; - if (!mz_zip_writer_finalize_archive(pZip)) - return MZ_FALSE; - - *pBuf = pZip->m_pState->m_pMem; - *pSize = pZip->m_pState->m_mem_size; - pZip->m_pState->m_pMem = NULL; - pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; - return MZ_TRUE; -} - -mz_bool mz_zip_writer_end(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState; - mz_bool status = MZ_TRUE; - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) - return MZ_FALSE; - - pState = pZip->m_pState; - pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); - -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO - - if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); - pState->m_pMem = NULL; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - return status; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_add_mem_to_archive_file_in_place(const wchar_t *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) -{ - mz_bool status, created_new_archive = MZ_FALSE; - mz_zip_archive zip_archive; - struct MZ_FILE_STAT_STRUCT file_stat; - MZ_CLEAR_OBJ(zip_archive); - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) - { - // Create a new archive. - if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) - return MZ_FALSE; - created_new_archive = MZ_TRUE; - } - else - { - // Append to an existing archive. - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return MZ_FALSE; - if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) - { - mz_zip_reader_end(&zip_archive); - return MZ_FALSE; - } - } - status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); - // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) - if (!mz_zip_writer_finalize_archive(&zip_archive)) - status = MZ_FALSE; - if (!mz_zip_writer_end(&zip_archive)) - status = MZ_FALSE; - if ((!status) && (created_new_archive)) - { - // It's a new archive and something went wrong, so just delete it. - int ignoredStatus = MZ_DELETE_FILE(pZip_filename); - (void)ignoredStatus; - } - return status; -} - -void *mz_zip_extract_archive_file_to_heap(const wchar_t *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) -{ - int file_index; - mz_zip_archive zip_archive; - void *p = NULL; - - if (pSize) - *pSize = 0; - - if ((!pZip_filename) || (!pArchive_name)) - return NULL; - - MZ_CLEAR_OBJ(zip_archive); - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return NULL; - - if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) - p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); - - mz_zip_reader_end(&zip_archive); - return p; -} - -#endif // #ifndef MINIZ_NO_STDIO - -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS - - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE 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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to -*/ diff --git a/plugins/ExtraPlugins/miniz/miniz.h b/plugins/ExtraPlugins/miniz/miniz.h deleted file mode 100644 index 261ac5a6dbac..000000000000 --- a/plugins/ExtraPlugins/miniz/miniz.h +++ /dev/null @@ -1,785 +0,0 @@ -#ifndef MINIZ_HEADER_INCLUDED -#define MINIZ_HEADER_INCLUDED - -#include - -// Defines to completely disable specific portions of miniz.c: -// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. - -// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. -//#define MINIZ_NO_STDIO - -// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or -// get/set file times, and the C run-time funcs that get/set times won't be called. -// The current downside is the times written to your archives will be from 1979. -//#define MINIZ_NO_TIME - -// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_APIS - -// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_WRITING_APIS - -// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. -//#define MINIZ_NO_ZLIB_APIS - -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. -#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. -// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc -// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user -// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. -//#define MINIZ_NO_MALLOC - -#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) -#include -#endif - -#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) -// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. -#define MINIZ_X86_OR_X64_CPU 1 -#endif - -#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU -// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. -#define MINIZ_LITTLE_ENDIAN 1 -#endif - -#if MINIZ_X86_OR_X64_CPU -// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 -#endif - -#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) -// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). -#define MINIZ_HAS_64BIT_REGISTERS 1 -#endif - -// ------------------- Types and macros - -typedef unsigned char mz_uint8; -typedef signed short mz_int16; -typedef unsigned short mz_uint16; -typedef unsigned int mz_uint32; -typedef unsigned int mz_uint; -typedef long long mz_int64; -typedef unsigned long long mz_uint64; -typedef int mz_bool; - -#define MZ_FALSE (0) -#define MZ_TRUE (1) - -// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. -#ifdef _MSC_VER -#define MZ_MACRO_END while (0, 0) -#else -#define MZ_MACRO_END while (0) -#endif - -// ------------------- zlib-style API Definitions. - -// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! -typedef unsigned long mz_ulong; - -// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. -void mz_free(void *p); - -#define MZ_ADLER32_INIT (1) -// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); - -#define MZ_CRC32_INIT (0) -// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. -mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); - -// Compression strategies. -enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; - -// Method -#define MZ_DEFLATED 8 - -#ifndef MINIZ_NO_ZLIB_APIS - -// Heap allocation callbacks. -// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. -typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); -typedef void(*mz_free_func)(void *opaque, void *address); -typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); - -#define MZ_VERSION "9.1.15" -#define MZ_VERNUM 0x91F0 -#define MZ_VER_MAJOR 9 -#define MZ_VER_MINOR 1 -#define MZ_VER_REVISION 15 -#define MZ_VER_SUBREVISION 0 - -// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). -enum -{ - MZ_NO_FLUSH = 0, - MZ_PARTIAL_FLUSH = 1, - MZ_SYNC_FLUSH = 2, - MZ_FULL_FLUSH = 3, - MZ_FINISH = 4, - MZ_BLOCK = 5 -}; - -// Return status codes. MZ_PARAM_ERROR is non-standard. -enum -{ - MZ_OK = 0, - MZ_STREAM_END = 1, - MZ_NEED_DICT = 2, - MZ_ERRNO = -1, - MZ_STREAM_ERROR = -2, - MZ_DATA_ERROR = -3, - MZ_MEM_ERROR = -4, - MZ_BUF_ERROR = -5, - MZ_VERSION_ERROR = -6, - MZ_PARAM_ERROR = -10000 -}; - -// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. -enum -{ - MZ_NO_COMPRESSION = 0, - MZ_BEST_SPEED = 1, - MZ_BEST_COMPRESSION = 9, - MZ_UBER_COMPRESSION = 10, - MZ_DEFAULT_LEVEL = 6, - MZ_DEFAULT_COMPRESSION = -1 -}; - -// Window bits -#define MZ_DEFAULT_WINDOW_BITS 15 - -struct mz_internal_state; - -// Compression/decompression stream struct. -typedef struct mz_stream_s -{ - const unsigned char *next_in; // pointer to next byte to read - unsigned int avail_in; // number of bytes available at next_in - mz_ulong total_in; // total number of bytes consumed so far - - unsigned char *next_out; // pointer to next byte to write - unsigned int avail_out; // number of bytes that can be written to next_out - mz_ulong total_out; // total number of bytes produced so far - - char *msg; // error msg (unused) - struct mz_internal_state *state; // internal state, allocated by zalloc/zfree - - mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) - mz_free_func zfree; // optional heap free function (defaults to free) - void *opaque; // heap alloc function user pointer - - int data_type; // data_type (unused) - mz_ulong adler; // adler32 of the source or uncompressed data - mz_ulong reserved; // not used -} mz_stream; - -typedef mz_stream *mz_streamp; - -// Returns the version string of miniz.c. -const char *mz_version(void); - -// mz_deflateInit() initializes a compressor with default options: -// Parameters: -// pStream must point to an initialized mz_stream struct. -// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. -// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. -// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if the input parameters are bogus. -// MZ_MEM_ERROR on out of memory. -int mz_deflateInit(mz_streamp pStream, int level); - -// mz_deflateInit2() is like mz_deflate(), except with more control: -// Additional parameters: -// method must be MZ_DEFLATED -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) -// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); - -// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). -int mz_deflateReset(mz_streamp pStream); - -// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. -// Return values: -// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). -// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) -int mz_deflate(mz_streamp pStream, int flush); - -// mz_deflateEnd() deinitializes a compressor: -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -int mz_deflateEnd(mz_streamp pStream); - -// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); - -// Single-call compression functions mz_compress() and mz_compress2(): -// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); - -// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). -mz_ulong mz_compressBound(mz_ulong source_len); - -// Initializes a decompressor. -int mz_inflateInit(mz_streamp pStream); - -// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). -int mz_inflateInit2(mz_streamp pStream, int window_bits); - -// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. -// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). -// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. -// Return values: -// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. -// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_DATA_ERROR if the deflate stream is invalid. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again -// with more input data, or with more room in the output buffer (except when using single call decompression, described above). -int mz_inflate(mz_streamp pStream, int flush); - -// Deinitializes a decompressor. -int mz_inflateEnd(mz_streamp pStream); - -// Single-call decompression. -// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); - -// Returns a string description of the specified error code, or NULL if the error code is invalid. -const char *mz_error(int err); - -// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. -#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES -typedef unsigned char Byte; -typedef unsigned int uInt; -typedef mz_ulong uLong; -typedef Byte Bytef; -typedef uInt uIntf; -typedef char charf; -typedef int intf; -typedef void *voidpf; -typedef uLong uLongf; -typedef void *voidp; -typedef void *const voidpc; -#define Z_NULL 0 -#define Z_NO_FLUSH MZ_NO_FLUSH -#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH -#define Z_SYNC_FLUSH MZ_SYNC_FLUSH -#define Z_FULL_FLUSH MZ_FULL_FLUSH -#define Z_FINISH MZ_FINISH -#define Z_BLOCK MZ_BLOCK -#define Z_OK MZ_OK -#define Z_STREAM_END MZ_STREAM_END -#define Z_NEED_DICT MZ_NEED_DICT -#define Z_ERRNO MZ_ERRNO -#define Z_STREAM_ERROR MZ_STREAM_ERROR -#define Z_DATA_ERROR MZ_DATA_ERROR -#define Z_MEM_ERROR MZ_MEM_ERROR -#define Z_BUF_ERROR MZ_BUF_ERROR -#define Z_VERSION_ERROR MZ_VERSION_ERROR -#define Z_PARAM_ERROR MZ_PARAM_ERROR -#define Z_NO_COMPRESSION MZ_NO_COMPRESSION -#define Z_BEST_SPEED MZ_BEST_SPEED -#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION -#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION -#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY -#define Z_FILTERED MZ_FILTERED -#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY -#define Z_RLE MZ_RLE -#define Z_FIXED MZ_FIXED -#define Z_DEFLATED MZ_DEFLATED -#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS -#define alloc_func mz_alloc_func -#define free_func mz_free_func -#define internal_state mz_internal_state -#define z_stream mz_stream -#define deflateInit mz_deflateInit -#define deflateInit2 mz_deflateInit2 -#define deflateReset mz_deflateReset -#define deflate mz_deflate -#define deflateEnd mz_deflateEnd -#define deflateBound mz_deflateBound -#define compress mz_compress -#define compress2 mz_compress2 -#define compressBound mz_compressBound -#define inflateInit mz_inflateInit -#define inflateInit2 mz_inflateInit2 -#define inflate mz_inflate -#define inflateEnd mz_inflateEnd -#define uncompress mz_uncompress -#define crc32 mz_crc32 -#define adler32 mz_adler32 -#define MAX_WBITS 15 -#define MAX_MEM_LEVEL 9 -#define zError mz_error -#define ZLIB_VERSION MZ_VERSION -#define ZLIB_VERNUM MZ_VERNUM -#define ZLIB_VER_MAJOR MZ_VER_MAJOR -#define ZLIB_VER_MINOR MZ_VER_MINOR -#define ZLIB_VER_REVISION MZ_VER_REVISION -#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION -#define zlibVersion mz_version -#define zlib_version mz_version() -#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -#endif // MINIZ_NO_ZLIB_APIS - - - - // ------------------- ZIP archive reading/writing - -#ifndef MINIZ_NO_ARCHIVE_APIS - -enum -{ - MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, - MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, - MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 -}; - -typedef struct _mz_zip_archive_file_stat -{ - mz_uint32 m_file_index; - mz_uint32 m_central_dir_ofs; - mz_uint16 m_version_made_by; - mz_uint16 m_version_needed; - mz_uint16 m_bit_flag; - mz_uint16 m_method; -#ifndef MINIZ_NO_TIME - time_t m_time; -#endif - mz_uint32 m_crc32; - mz_uint64 m_comp_size; - mz_uint64 m_uncomp_size; - mz_uint16 m_internal_attr; - mz_uint32 m_external_attr; - mz_uint64 m_local_header_ofs; - mz_uint32 m_comment_size; - char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; - char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -} mz_zip_archive_file_stat; - -typedef size_t(*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); -typedef size_t(*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); - -struct mz_zip_internal_state_tag; -typedef struct mz_zip_internal_state_tag mz_zip_internal_state; - -typedef enum _mz_zip_mode -{ - MZ_ZIP_MODE_INVALID = 0, - MZ_ZIP_MODE_READING = 1, - MZ_ZIP_MODE_WRITING = 2, - MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 -} mz_zip_mode; - -typedef struct _mz_zip_archive -{ - mz_uint64 m_archive_size; - mz_uint64 m_central_directory_file_ofs; - mz_uint m_total_files; - mz_zip_mode m_zip_mode; - - mz_uint m_file_offset_alignment; - - mz_alloc_func m_pAlloc; - mz_free_func m_pFree; - mz_realloc_func m_pRealloc; - void *m_pAlloc_opaque; - - mz_file_read_func m_pRead; - mz_file_write_func m_pWrite; - void *m_pIO_opaque; - - mz_zip_internal_state *m_pState; - -} mz_zip_archive; - -typedef enum _mz_zip_flags -{ - MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, - MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, - MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, - MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 -} mz_zip_flags; - -// ZIP archive reading - -// Inits a ZIP archive reader. -// These functions read and validate the archive's central directory. -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const wchar_t *pFilename, mz_uint32 flags); -#endif - -// Returns the total number of files in the archive. -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); - -// Returns detailed information about an archive file entry. -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); - -// Determines if an archive file entry is a directory entry. -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); - -// Retrieves the filename of an archive file entry. -// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); - -// Attempts to locates a file in the archive's central directory. -// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH -// Returns -1 if the file cannot be found. -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); - -// Extracts a archive file to a memory buffer using no memory allocation. -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); - -// Extracts a archive file to a memory buffer. -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); - -// Extracts a archive file to a dynamically allocated heap buffer. -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); - -// Extracts a archive file using a callback function to output the file's data. -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); - -#ifndef MINIZ_NO_STDIO -// Extracts a archive file to a disk file and sets its last accessed and modified times. -// This function only extracts files, not archive directory records. -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const wchar_t *pDst_filename, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const wchar_t *pDst_filename, mz_uint flags); -#endif - -// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. -mz_bool mz_zip_reader_end(mz_zip_archive *pZip); - -// ZIP archive writing - -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - - // Inits a ZIP archive writer. -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const wchar_t *pFilename, mz_uint64 size_to_reserve_at_beginning); -#endif - -// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. -// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. -// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). -// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. -// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before -// the archive is finalized the file's central directory will be hosed. -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const wchar_t *pFilename); - -// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. -// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); - -#ifndef MINIZ_NO_STDIO -// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const wchar_t *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); -#endif - -// Adds a file to an archive by fully cloning the data from another archive. -// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); - -// Finalizes the archive by writing the central directory records followed by the end of central directory record. -// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). -// An archive must be manually finalized by calling this function for it to be valid. -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); - -// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. -// Note for the archive to be valid, it must have been finalized before ending. -mz_bool mz_zip_writer_end(mz_zip_archive *pZip); - -// Misc. high-level helper functions: - -// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_add_mem_to_archive_file_in_place(const wchar_t *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - -// Reads a single file from an archive into a heap block. -// Returns NULL on failure. -void *mz_zip_extract_archive_file_to_heap(const wchar_t *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); - -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS - -// ------------------- Low-level Decompression API Definitions - -// Decompression flags used by tinfl_decompress(). -// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. -// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. -// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). -// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. -enum -{ - TINFL_FLAG_PARSE_ZLIB_HEADER = 1, - TINFL_FLAG_HAS_MORE_INPUT = 2, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, - TINFL_FLAG_COMPUTE_ADLER32 = 8 -}; - -// High level decompression functions: -// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. -// On return: -// Function returns a pointer to the decompressed data, or NULL on failure. -// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must call mz_free() on the returned block when it's no longer needed. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. -// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. -#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. -// Returns 1 on success or 0 on failure. -typedef int(*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; - -// Max size of LZ dictionary. -#define TINFL_LZ_DICT_SIZE 32768 - - // Return status. -typedef enum -{ - TINFL_STATUS_BAD_PARAM = -3, - TINFL_STATUS_ADLER32_MISMATCH = -2, - TINFL_STATUS_FAILED = -1, - TINFL_STATUS_DONE = 0, - TINFL_STATUS_NEEDS_MORE_INPUT = 1, - TINFL_STATUS_HAS_MORE_OUTPUT = 2 -} tinfl_status; - -// Initializes the decompressor to its initial state. -#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END -#define tinfl_get_adler32(r) (r)->m_check_adler32 - - // Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. - // This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); - -// Internal/private bits follow. -enum -{ - TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, - TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS -}; - -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - -#if MINIZ_HAS_64BIT_REGISTERS -#define TINFL_USE_64BIT_BITBUF 1 -#endif - -#if TINFL_USE_64BIT_BITBUF -typedef mz_uint64 tinfl_bit_buf_t; -#define TINFL_BITBUF_SIZE (64) -#else -typedef mz_uint32 tinfl_bit_buf_t; -#define TINFL_BITBUF_SIZE (32) -#endif - -struct tinfl_decompressor_tag -{ - mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; - tinfl_bit_buf_t m_bit_buf; - size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; - mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; -}; - -// ------------------- Low-level Compression API Definitions - -// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). -#define TDEFL_LESS_MEMORY 0 - - // tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): - // TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). -enum -{ - TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF -}; - -// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. -// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). -// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. -// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). -// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) -// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. -// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. -// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. -// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). -enum -{ - TDEFL_WRITE_ZLIB_HEADER = 0x01000, - TDEFL_COMPUTE_ADLER32 = 0x02000, - TDEFL_GREEDY_PARSING_FLAG = 0x04000, - TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, - TDEFL_RLE_MATCHES = 0x10000, - TDEFL_FILTER_MATCHES = 0x20000, - TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, - TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 -}; - -// High level compression functions: -// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of source block to compress. -// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must free() the returned block when it's no longer needed. -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. -// Returns 0 on failure. -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// Compresses an image to a compressed PNG file in memory. -// On entry: -// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. -// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. -// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL -// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pLen_out will be set to the size of the PNG image file. -// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); - -// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. -typedef mz_bool(*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); - -// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; - -// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). -#if TDEFL_LESS_MEMORY -enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#else -enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#endif - -// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. -typedef enum -{ - TDEFL_STATUS_BAD_PARAM = -2, - TDEFL_STATUS_PUT_BUF_FAILED = -1, - TDEFL_STATUS_OKAY = 0, - TDEFL_STATUS_DONE = 1, -} tdefl_status; - -// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums -typedef enum -{ - TDEFL_NO_FLUSH = 0, - TDEFL_SYNC_FLUSH = 2, - TDEFL_FULL_FLUSH = 3, - TDEFL_FINISH = 4 -} tdefl_flush; - -// tdefl's compression state structure. -typedef struct -{ - tdefl_put_buf_func_ptr m_pPut_buf_func; - void *m_pPut_buf_user; - mz_uint m_flags, m_max_probes[2]; - int m_greedy_parsing; - mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; - mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; - mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; - mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; - tdefl_status m_prev_return_status; - const void *m_pIn_buf; - void *m_pOut_buf; - size_t *m_pIn_buf_size, *m_pOut_buf_size; - tdefl_flush m_flush; - const mz_uint8 *m_pSrc; - size_t m_src_buf_left, m_out_buf_ofs; - mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; - mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; - mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; - mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; - mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; -} tdefl_compressor; - -// Initializes the compressor. -// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. -// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. -// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. -// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); - -// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. -// tdefl_compress_buffer() always consumes the entire input buffer. -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); - -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); -mz_uint32 tdefl_get_adler32(tdefl_compressor *d); - -// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. -#ifndef MINIZ_NO_ZLIB_APIS - // Create tdefl_compress() flags given zlib-style compression parameters. - // level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) - // window_bits may be -15 (raw deflate) or 15 (zlib) - // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); -#endif // #ifndef MINIZ_NO_ZLIB_APIS - -#endif // MINIZ_HEADER_INCLUDED \ No newline at end of file diff --git a/plugins/ExtraPlugins/plugin.c b/plugins/ExtraPlugins/plugin.c deleted file mode 100644 index 81bb2e63b9e9..000000000000 --- a/plugins/ExtraPlugins/plugin.c +++ /dev/null @@ -1,376 +0,0 @@ -#include "main.h" - -ULONG PhDisabledPluginsCount( - VOID - ) -{ - PPH_STRING disabled; - PH_STRINGREF remainingPart; - PH_STRINGREF part; - ULONG count = 0; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - remainingPart = disabled->sr; - - while (remainingPart.Length) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length) - { - count++; - } - } - - PhDereferenceObject(disabled); - - return count; -} - -// Copied from Process Hacker, plugin.c - -PWSTR PhGetPluginBaseName( - _In_ PPHAPP_PLUGIN Plugin - ) -{ - if (Plugin->FileName) - { - PH_STRINGREF pathNamePart; - PH_STRINGREF baseNamePart; - - if (PhSplitStringRefAtLastChar(&Plugin->FileName->sr, '\\', &pathNamePart, &baseNamePart)) - return baseNamePart.Buffer; - else - return Plugin->FileName->Buffer; - } - else - { - // Fake disabled plugin. - return Plugin->Name.Buffer; - } -} - -BOOLEAN PhIsPluginLoadedByBaseName( - _In_ PPH_STRINGREF BaseName - ) -{ - PPH_AVL_LINKS root; - PPH_AVL_LINKS links; - - root = PluginInstance->Links.Parent; - - while (root) - { - if (!root->Parent) - break; - - root = root->Parent; - } - - for ( - links = PhMinimumElementAvlTree((PPH_AVL_TREE)root); - links; - links = PhSuccessorElementAvlTree(links) - ) - { - PPHAPP_PLUGIN plugin = CONTAINING_RECORD(links, PHAPP_PLUGIN, Links); - PH_STRINGREF pluginBaseName; - - PhInitializeStringRefLongHint(&pluginBaseName, PhGetPluginBaseName(plugin)); - - if (PhEqualStringRef(&pluginBaseName, BaseName, TRUE)) - return TRUE; - } - - return FALSE; -} - -BOOLEAN PhpLocateDisabledPlugin( - _In_ PPH_STRING List, - _In_ PPH_STRINGREF BaseName, - _Out_opt_ PULONG FoundIndex - ) -{ - PH_STRINGREF namePart; - PH_STRINGREF remainingPart; - - remainingPart = List->sr; - - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, '|', &namePart, &remainingPart); - - if (PhEqualStringRef(&namePart, BaseName, TRUE)) - { - if (FoundIndex) - *FoundIndex = (ULONG)(namePart.Buffer - List->Buffer); - - return TRUE; - } - } - - return FALSE; -} - -BOOLEAN PhIsPluginDisabled( - _In_ PPH_STRINGREF BaseName - ) -{ - BOOLEAN found; - PPH_STRING disabled; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - found = PhpLocateDisabledPlugin(disabled, BaseName, NULL); - PhDereferenceObject(disabled); - - return found; -} - -VOID PhSetPluginDisabled( - _In_ PPH_STRINGREF BaseName, - _In_ BOOLEAN Disable - ) -{ - BOOLEAN found; - PPH_STRING disabled; - ULONG foundIndex; - PPH_STRING newDisabled; - - disabled = PhGetStringSetting(L"DisabledPlugins"); - - found = PhpLocateDisabledPlugin(disabled, BaseName, &foundIndex); - - if (Disable && !found) - { - // We need to add the plugin to the disabled list. - - if (disabled->Length != 0) - { - // We have other disabled plugins. Append a pipe character followed by the plugin name. - newDisabled = PhCreateStringEx(NULL, disabled->Length + sizeof(WCHAR) + BaseName->Length); - memcpy(newDisabled->Buffer, disabled->Buffer, disabled->Length); - newDisabled->Buffer[disabled->Length / 2] = '|'; - memcpy(&newDisabled->Buffer[disabled->Length / 2 + 1], BaseName->Buffer, BaseName->Length); - PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); - PhDereferenceObject(newDisabled); - } - else - { - // This is the first disabled plugin. - PhSetStringSetting2(L"DisabledPlugins", BaseName); - } - } - else if (!Disable && found) - { - ULONG removeCount; - - // We need to remove the plugin from the disabled list. - - removeCount = (ULONG)BaseName->Length / 2; - - if (foundIndex + (ULONG)BaseName->Length / 2 < (ULONG)disabled->Length / 2) - { - // Remove the following pipe character as well. - removeCount++; - } - else if (foundIndex != 0) - { - // Remove the preceding pipe character as well. - foundIndex--; - removeCount++; - } - - newDisabled = PhCreateStringEx(NULL, disabled->Length - removeCount * sizeof(WCHAR)); - memcpy(newDisabled->Buffer, disabled->Buffer, foundIndex * sizeof(WCHAR)); - memcpy(&newDisabled->Buffer[foundIndex], &disabled->Buffer[foundIndex + removeCount], - disabled->Length - removeCount * sizeof(WCHAR) - foundIndex * sizeof(WCHAR)); - PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); - PhDereferenceObject(newDisabled); - } - - PhDereferenceObject(disabled); -} - -PPHAPP_PLUGIN PhCreateDisabledPlugin( - _In_ PPH_STRINGREF BaseName - ) -{ - PPHAPP_PLUGIN plugin; - - plugin = PhAllocate(sizeof(PHAPP_PLUGIN)); - memset(plugin, 0, sizeof(PHAPP_PLUGIN)); - - plugin->Name.Length = BaseName->Length; - plugin->Name.Buffer = PhAllocate(BaseName->Length + sizeof(WCHAR)); - memcpy(plugin->Name.Buffer, BaseName->Buffer, BaseName->Length); - plugin->Name.Buffer[BaseName->Length / 2] = 0; - - return plugin; -} - - - - - -PPH_HASHTABLE PluginHashtable = NULL; -PPH_LIST PluginList = NULL; -PH_QUEUED_LOCK PluginListLock = PH_QUEUED_LOCK_INIT; - - -PPH_STRING PluginGetVersionInfo( - _In_ PPH_STRING FileName - ) -{ - ULONG versionSize; - PVOID versionInfo; - PUSHORT languageInfo; - UINT language; - UINT bufferSize = 0; - PWSTR buffer; - PPH_STRING internalName = NULL; - PPH_STRING version = NULL; - - versionSize = GetFileVersionInfoSize(PhGetStringOrEmpty(FileName), NULL); - versionInfo = PhAllocate(versionSize); - memset(versionInfo, 0, versionSize); - - if (GetFileVersionInfo(PhGetStringOrEmpty(FileName), 0, versionSize, versionInfo)) - { - if (VerQueryValue(versionInfo, L"\\", &buffer, &bufferSize)) - { - VS_FIXEDFILEINFO* info = (VS_FIXEDFILEINFO*)buffer; - - if (info->dwSignature == 0xfeef04bd) - { - version = PhFormatString( - L"%lu.%lu.%lu.%lu", - (info->dwFileVersionMS >> 16) & 0xffff, - (info->dwFileVersionMS >> 0) & 0xffff, - (info->dwFileVersionLS >> 16) & 0xffff, - (info->dwFileVersionLS >> 0) & 0xffff - ); - } - } - - if (VerQueryValue(versionInfo, L"\\VarFileInfo\\Translation", &languageInfo, &language)) - { - PPH_STRING internalNameString = PhFormatString( - L"\\StringFileInfo\\%04x%04x\\InternalName", - languageInfo[0], - languageInfo[1] - ); - - if (VerQueryValue(versionInfo, PhGetStringOrEmpty(internalNameString), &buffer, &bufferSize)) - { - internalName = PhCreateStringEx(buffer, bufferSize * sizeof(WCHAR)); - } - - PhDereferenceObject(internalNameString); - } - } - - PhFree(versionInfo); - - return version; -} - - -VOID EnumerateLoadedPlugins( - _In_ PWCT_CONTEXT Context - ) -{ - PPH_AVL_LINKS root; - PPH_AVL_LINKS links; - - root = PluginInstance->Links.Parent; - - while (root) - { - if (!root->Parent) - break; - - root = root->Parent; - } - - for ( - links = PhMinimumElementAvlTree((PPH_AVL_TREE)root); - links; - links = PhSuccessorElementAvlTree(links) - ) - { - HANDLE handle; - IO_STATUS_BLOCK isb; - FILE_BASIC_INFORMATION basic; - PPHAPP_PLUGIN pluginInstance; - PH_STRINGREF pluginBaseName; - PPH_STRING pluginVersion; - SYSTEMTIME utcTime, localTime; - PPLUGIN_NODE entry; - - pluginInstance = CONTAINING_RECORD(links, PHAPP_PLUGIN, Links); - - // HACK: Hide the CommonUtil plugin since it's a required dependency for multiple plugins. - if (PhEqualStringRef2(&pluginInstance->Name, L"ProcessHacker.CommonUtil", TRUE)) - continue; - - PhInitializeStringRefLongHint(&pluginBaseName, PhGetPluginBaseName(pluginInstance)); - - if (PhIsPluginDisabled(&pluginBaseName)) - { - continue; - } - - if (!RtlDoesFileExists_U(PhGetString(pluginInstance->FileName))) - { - continue; - } - - entry = PhCreateAlloc(sizeof(PLUGIN_NODE)); - memset(entry, 0, sizeof(PLUGIN_NODE)); - memset(&basic, 0, sizeof(FILE_BASIC_INFORMATION)); - - if (NT_SUCCESS(PhCreateFileWin32( - &handle, - PhGetString(pluginInstance->FileName), - FILE_GENERIC_READ, - FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - NtQueryInformationFile( - handle, - &isb, - &basic, - sizeof(FILE_BASIC_INFORMATION), - FileBasicInformation - ); - - NtClose(handle); - } - - pluginVersion = PluginGetVersionInfo(pluginInstance->FileName); - - entry->State = PLUGIN_STATE_LOCAL; - entry->PluginInstance = pluginInstance; - entry->InternalName = PhCreateString2(&pluginInstance->Name); - entry->Version = PhSubstring(pluginVersion, 0, pluginVersion->Length / sizeof(WCHAR) - 4); - entry->Name = PhCreateString(pluginInstance->Information.DisplayName); - entry->Author = PhCreateString(pluginInstance->Information.Author); - entry->Description = PhCreateString(pluginInstance->Information.Description); - entry->PluginOptions = pluginInstance->Information.HasOptions; - entry->FilePath = PhCreateString2(&pluginInstance->FileName->sr); - entry->FileName = PhGetBaseName(entry->FilePath); - - PhLargeIntegerToSystemTime(&utcTime, &basic.LastWriteTime); - SystemTimeToTzSpecificLocalTime(NULL, &utcTime, &localTime); - entry->UpdatedTime = PhFormatDateTime(&localTime); - - PhLargeIntegerToSystemTime(&utcTime, &basic.CreationTime); - SystemTimeToTzSpecificLocalTime(NULL, &utcTime, &localTime); - entry->AddedTime = PhFormatDateTime(&localTime); - - PluginsAddTreeNode(Context, entry); - } -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/resource.h b/plugins/ExtraPlugins/resource.h deleted file mode 100644 index 301cf1ec38d5..000000000000 --- a/plugins/ExtraPlugins/resource.h +++ /dev/null @@ -1,38 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by ExtraPlugins.rc -// -#define IDD_PLUGINS_DIALOG 102 -#define IDB_SEARCH_ACTIVE_BMP 105 -#define IDB_SEARCH_ACTIVE 106 -#define IDB_SEARCH_INACTIVE_BMP 107 -#define IDB_SEARCH_INACTIVE 108 -#define IDD_DIALOG1 111 -#define IDD_DISABLED_DIALOG 111 -#define IDB_SETTINGS 113 -#define IDB_SETTINGS_PNG 113 -#define IDC_PLUGINTREE 1001 -#define IDC_INSTALLED 1036 -#define IDC_BROWSE 1037 -#define IDC_UPDATES 1038 -#define IDC_DISABLED 1039 -#define IDC_LIST_DISABLED 1046 -#define IDC_CLEANUP 1057 -#define ID_MENU_INSTALL 4059 -#define ID_MENU_UNINSTALL 40011 -#define ID_MENU_DISABLE 40012 -#define ID_MENU_PROPERTIES 40013 -#define ID_MENU_UNLOAD 40014 -#define IDC_INSTRUCTION 40025 -#define IDC_SEARCHBOX 40026 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 114 -#define _APS_NEXT_COMMAND_VALUE 40028 -#define _APS_NEXT_CONTROL_VALUE 1047 -#define _APS_NEXT_SYMED_VALUE 104 -#endif -#endif diff --git a/plugins/ExtraPlugins/resources/cog_edit_modern.png b/plugins/ExtraPlugins/resources/cog_edit_modern.png deleted file mode 100644 index 8cf8e4dcdd3a..000000000000 Binary files a/plugins/ExtraPlugins/resources/cog_edit_modern.png and /dev/null differ diff --git a/plugins/ExtraPlugins/setup/page3.c b/plugins/ExtraPlugins/setup/page3.c deleted file mode 100644 index 5db142d81c21..000000000000 --- a/plugins/ExtraPlugins/setup/page3.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "..\main.h" - -static TASKDIALOG_BUTTON TaskDialogButtonArray[] = -{ - { IDOK, L"Download" } -}; - -HRESULT CALLBACK ShowAvailableCallbackProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_BUTTON_CLICKED: - { - if ((INT)wParam == IDOK) - { - ShowProgressDialog(context); - return S_FALSE; - } - } - break; - } - - return S_OK; -} - -VOID ShowAvailableDialog( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - TASKDIALOGCONFIG config; - - memset(&config, 0, sizeof(TASKDIALOGCONFIG)); - config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS; - config.dwCommonButtons = TDCBF_CANCEL_BUTTON; - config.hMainIcon = Context->IconLargeHandle; - - config.pszWindowTitle = L"Process Hacker - Plugin Manager"; - config.pszMainInstruction = PhIsNullOrEmptyString(Context->Node->Name) ? PhGetStringOrEmpty(Context->Node->InternalName) : PhGetStringOrEmpty(Context->Node->Name); - config.pszContent = PhaFormatString(L"%s\r\n\r\nAuthor: %s\r\nVersion: %s\r\nUpdated: %s", - PhGetStringOrEmpty(Context->Node->Description), - PhGetStringOrEmpty(Context->Node->Author), - PhGetStringOrEmpty(Context->Node->Version), - PhGetStringOrEmpty(Context->Node->UpdatedTime) - )->Buffer; - //config.pszExpandedInformation = L"View Changelog"; - - config.cxWidth = 200; - config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); - - config.lpCallbackData = (LONG_PTR)Context; - config.pfCallback = ShowAvailableCallbackProc; - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/setup/page4.c b/plugins/ExtraPlugins/setup/page4.c deleted file mode 100644 index 566019d70dec..000000000000 --- a/plugins/ExtraPlugins/setup/page4.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "..\main.h" - -HRESULT CALLBACK ShowProgressCallbackProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_NAVIGATED: - { - SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); - SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); - - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), UpdateDownloadThread, context); - } - break; - case TDN_HYPERLINK_CLICKED: - { - TaskDialogLinkClicked(context); - } - break; - } - - return S_OK; -} - -VOID ShowProgressDialog( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - TASKDIALOGCONFIG config; - - memset(&config, 0, sizeof(TASKDIALOGCONFIG)); - config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS | TDF_SHOW_PROGRESS_BAR; - config.dwCommonButtons = TDCBF_CANCEL_BUTTON; - config.hMainIcon = Context->IconLargeHandle; - - config.pszWindowTitle = L"Process Hacker - Plugin Manager"; - config.pszMainInstruction = L"Downloading 0.0.0.0..."; - config.pszContent = L"Downloaded: ~ of ~ (0%)\r\nSpeed: ~ KB/s"; - //config.pszExpandedInformation = L"View Changelog"; - - config.cxWidth = 200; - config.lpCallbackData = (LONG_PTR)Context; - config.pfCallback = ShowProgressCallbackProc; - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/setup/page5.c b/plugins/ExtraPlugins/setup/page5.c deleted file mode 100644 index e703258e37b6..000000000000 --- a/plugins/ExtraPlugins/setup/page5.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "..\main.h" -#include -#include - -static TASKDIALOG_BUTTON TaskDialogButtonArray[] = -{ - { IDYES, L"Restart" } -}; - -HRESULT CALLBACK RestartTaskDialogCallbackProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_BUTTON_CLICKED: - { - if ((INT)wParam == IDYES) - { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - PhShellProcessHacker( - PhMainWndHandle, - L"-v", - SW_SHOW, - 0, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - ); - //PhShellProcessHacker( - // PhMainWndHandle, - // L"-plugin " PLUGIN_NAME L":INSTALL -plugin " PLUGIN_NAME L":hex64value", - // SW_SHOW, - // 0, - // PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - // 0, - // NULL - // ); - ProcessHacker_Destroy(PhMainWndHandle); - } - } - break; - } - - return S_OK; -} - -HRESULT CALLBACK FinalTaskDialogCallbackProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_NAVIGATED: - { - if (!PhGetOwnTokenAttributes().Elevated) - { - SendMessage(hwndDlg, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE); - } - } - break; - case TDN_BUTTON_CLICKED: - { - if ((INT)wParam == IDRETRY) - { - ShowAvailableDialog(context); - return S_FALSE; - } - } - break; - } - - return S_OK; -} - -VOID ShowInstallRestartDialog( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - TASKDIALOGCONFIG config; - - memset(&config, 0, sizeof(TASKDIALOGCONFIG)); - config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; - config.dwCommonButtons = TDCBF_CLOSE_BUTTON; - config.hMainIcon = Context->IconLargeHandle; - - config.pszWindowTitle = L"Process Hacker - Plugin Manager"; - config.pszMainInstruction = L"Process Hacker needs to restart"; - config.pszContent = L"Changes may require a restart to take effect..."; - - config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); - - config.cxWidth = 200; - config.pfCallback = RestartTaskDialogCallbackProc; - config.lpCallbackData = (LONG_PTR)Context; - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} - -VOID ShowUninstallRestartDialog( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - TASKDIALOGCONFIG config; - - memset(&config, 0, sizeof(TASKDIALOGCONFIG)); - config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; - config.dwCommonButtons = TDCBF_CLOSE_BUTTON; - config.hMainIcon = Context->IconLargeHandle; - - config.pszWindowTitle = L"Process Hacker - Plugin Manager"; - config.pszMainInstruction = L"Process Hacker needs to restart"; - config.pszContent = L"Changes may require a restart to take effect..."; - - config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); - - config.cxWidth = 200; - config.pfCallback = RestartTaskDialogCallbackProc; - config.lpCallbackData = (LONG_PTR)Context; - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} - -VOID ShowUpdateFailedDialog( - _In_ PPH_UPDATER_CONTEXT Context, - _In_ BOOLEAN HashFailed, - _In_ BOOLEAN SignatureFailed - ) -{ - TASKDIALOGCONFIG config; - - memset(&config, 0, sizeof(TASKDIALOGCONFIG)); - config.cbSize = sizeof(TASKDIALOGCONFIG); - //config.pszMainIcon = MAKEINTRESOURCE(65529); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; - config.dwCommonButtons = TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON; - config.hMainIcon = Context->IconLargeHandle; - - config.pszWindowTitle = L"Process Hacker - Plugin Manager"; - config.pszMainInstruction = L"Error downloading plugin files."; - - if (SignatureFailed) - { - config.pszContent = L"Signature check failed. Click Retry to download the plugin again."; - } - else if (HashFailed) - { - config.pszContent = L"Hash check failed. Click Retry to download the plugin again."; - } - else - { - config.pszContent = L"Click Retry to download the plugin again."; - } - - config.cxWidth = 200; - config.pfCallback = FinalTaskDialogCallbackProc; - config.lpCallbackData = (LONG_PTR)Context; - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/setup/uninstall.c b/plugins/ExtraPlugins/setup/uninstall.c deleted file mode 100644 index e7f92310551d..000000000000 --- a/plugins/ExtraPlugins/setup/uninstall.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "..\main.h" -#include -#include - -static TASKDIALOG_BUTTON TaskDialogButtonArray[] = -{ - { IDYES, L"Uninstall" } -}; - -HRESULT CALLBACK TaskDialogUninstallCallbackProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_NAVIGATED: - { - //PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupExtractBuild, context); - } - break; - case TDN_BUTTON_CLICKED: - { - if ((INT)wParam == IDYES) - { - PPH_STRING baseNameString; - PPH_STRING fileNameString; - PPH_STRING bakNameString; - - context->Node->State = PLUGIN_STATE_RESTART; - - fileNameString = PhGetFileName(context->Node->FilePath); - baseNameString = PhGetBaseName(context->Node->FilePath); - bakNameString = PhConcatStrings(2, PhGetString(fileNameString), L".bak"); - - if (RtlDoesFileExists_U(PhGetString(fileNameString))) - { - MoveFileEx(PhGetString(fileNameString), PhGetString(bakNameString), MOVEFILE_REPLACE_EXISTING); - } - - PhDereferenceObject(bakNameString); - PhDereferenceObject(baseNameString); - PhDereferenceObject(fileNameString); - - PostMessage(context->DialogHandle, PH_UPDATENEWER, 0, 0); - return S_FALSE; - } - } - break; - } - - return S_OK; -} - -VOID ShowPluginUninstallDialog( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - PPH_UPDATER_CONTEXT context; - TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; - - context = (PPH_UPDATER_CONTEXT)Context; - - config.cxWidth = 200; - config.pszWindowTitle = L"Process Hacker - Plugin Manager"; - config.pszMainInstruction = PhaFormatString(L"Uninstall %s?", PhIsNullOrEmptyString(Context->Node->Name) ? PhGetStringOrEmpty(Context->Node->InternalName) : PhGetStringOrEmpty(Context->Node->Name))->Buffer; - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; - config.dwCommonButtons = TDCBF_CLOSE_BUTTON; - config.hMainIcon = context->IconLargeHandle; - config.pfCallback = TaskDialogUninstallCallbackProc; - config.lpCallbackData = (LONG_PTR)Context; - config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/setup/updater.c b/plugins/ExtraPlugins/setup/updater.c deleted file mode 100644 index d3b20ea48b02..000000000000 --- a/plugins/ExtraPlugins/setup/updater.c +++ /dev/null @@ -1,867 +0,0 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2011-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "..\main.h" -#include - -PPH_UPDATER_CONTEXT CreateUpdateContext( - _In_ PPLUGIN_NODE Node, - _In_ PLUGIN_ACTION Action - ) -{ - PPH_UPDATER_CONTEXT context; - - context = (PPH_UPDATER_CONTEXT)PhCreateAlloc(sizeof(PH_UPDATER_CONTEXT)); - memset(context, 0, sizeof(PH_UPDATER_CONTEXT)); - - context->Action = Action; - context->Node = Node; - - return context; -} - -VOID FreeUpdateContext( - _In_ _Post_invalid_ PPH_UPDATER_CONTEXT Context - ) -{ - //PhClearReference(&Context->Version); - PhClearReference(&Context->RevVersion); - //PhClearReference(&Context->RelDate); - PhClearReference(&Context->Size); - //PhClearReference(&Context->Signature); - //PhClearReference(&Context->ReleaseNotesUrl); - PhClearReference(&Context->SetupFilePath); - PhClearReference(&Context->FileDownloadUrl); - - PhDereferenceObject(Context); -} - -VOID TaskDialogCreateIcons( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - // Load the Process Hacker window icon - Context->IconLargeHandle = (HICON)LoadImage( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - IMAGE_ICON, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON), - LR_SHARED - ); - Context->IconSmallHandle = (HICON)LoadImage( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON), - LR_SHARED - ); - - // Set the TaskDialog window icons - SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); -} - -VOID TaskDialogLinkClicked( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - //if (!PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) - { - // Launch the ReleaseNotes URL (if it exists) with the default browser - //PhShellExecute(Context->DialogHandle, Context->ReleaseNotesUrl->Buffer, NULL); - } -} - -PPH_STRING UpdaterGetOpaqueXmlNodeText( - _In_ mxml_node_t *xmlNode - ) -{ - if (xmlNode && xmlNode->child && xmlNode->child->type == MXML_OPAQUE && xmlNode->child->value.opaque) - { - return PhConvertUtf8ToUtf16(xmlNode->child->value.opaque); - } - - return PhReferenceEmptyString(); -} - -BOOLEAN LastUpdateCheckExpired( - VOID - ) -{ -#ifdef FORCE_UPDATE_CHECK - return TRUE; -#else - ULONG64 lastUpdateTimeTicks = 0; - LARGE_INTEGER currentUpdateTimeTicks; - //PPH_STRING lastUpdateTimeString; - - // Get the last update check time - //lastUpdateTimeString = PhGetStringSetting(SETTING_NAME_LAST_CHECK); - //PhStringToInteger64(&lastUpdateTimeString->sr, 0, &lastUpdateTimeTicks); - - // Query the current time - PhQuerySystemTime(¤tUpdateTimeTicks); - - // Check if the last update check was more than 7 days ago - if (currentUpdateTimeTicks.QuadPart - lastUpdateTimeTicks >= 7 * PH_TICKS_PER_DAY) - { - PPH_STRING currentUpdateTimeString = PhFormatUInt64(currentUpdateTimeTicks.QuadPart, FALSE); - - // Save the current time - // PhSetStringSetting2(SETTING_NAME_LAST_CHECK, ¤tUpdateTimeString->sr); - - // Cleanup - PhDereferenceObject(currentUpdateTimeString); - // P//hDereferenceObject(lastUpdateTimeString); - return TRUE; - } - - // Cleanup - //PhDereferenceObject(lastUpdateTimeString); - return FALSE; -#endif -} - -PPH_STRING UpdateVersionString( - VOID - ) -{ - ULONG majorVersion; - ULONG minorVersion; - ULONG revisionVersion; - PPH_STRING currentVersion = NULL; - PPH_STRING versionHeader = NULL; - - PhGetPhVersionNumbers( - &majorVersion, - &minorVersion, - NULL, - &revisionVersion - ); - - currentVersion = PhFormatString( - L"%lu.%lu.%lu", - majorVersion, - minorVersion, - revisionVersion - ); - - if (currentVersion) - { - versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer); - PhDereferenceObject(currentVersion); - } - - return versionHeader; -} - -PPH_STRING UpdateWindowsString( - VOID - ) -{ - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion"); - - HANDLE keyHandle = NULL; - PPH_STRING buildLabHeader = NULL; - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName, - 0 - ))) - { - PPH_STRING buildLabString; - - if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLabEx")) - { - buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer); - PhDereferenceObject(buildLabString); - } - else if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLab")) - { - buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer); - PhDereferenceObject(buildLabString); - } - - NtClose(keyHandle); - } - - return buildLabHeader; -} - -//BOOLEAN ParseVersionString( -// _Inout_ PPH_UPDATER_CONTEXT Context -// ) -//{ -// PH_STRINGREF sr, majorPart, minorPart, revisionPart; -// ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; -// -// //PhInitializeStringRef(&sr, Context->VersionString->Buffer); -// PhInitializeStringRef(&revisionPart, Context->RevVersion->Buffer); -// -// if (PhSplitStringRefAtChar(&sr, '.', &majorPart, &minorPart)) -// { -// PhStringToInteger64(&majorPart, 10, &majorInteger); -// PhStringToInteger64(&minorPart, 10, &minorInteger); -// PhStringToInteger64(&revisionPart, 10, &revisionInteger); -// -// //Context->MajorVersion = (ULONG)majorInteger; -// //Context->MinorVersion = (ULONG)minorInteger; -// //Context->RevisionVersion = (ULONG)revisionInteger; -// -// return TRUE; -// } -// -// return FALSE; -//} - -BOOLEAN ReadRequestString( - _In_ HINTERNET Handle, - _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, - _Out_ ULONG *DataLength - ) -{ - PSTR data; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; - BYTE buffer[PAGE_SIZE]; - - allocatedLength = sizeof(buffer); - data = (PSTR)PhAllocate(allocatedLength); - dataLength = 0; - - // Zero the buffer - memset(buffer, 0, PAGE_SIZE); - - while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength)) - { - if (returnLength == 0) - break; - - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - // Copy the returned buffer into our pointer - memcpy(data + dataLength, buffer, returnLength); - // Zero the returned buffer for the next loop - //memset(buffer, 0, returnLength); - - dataLength += returnLength; - } - - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - // Ensure that the buffer is null-terminated. - data[dataLength] = 0; - - *DataLength = dataLength; - *Data = data; - - return TRUE; -} - -NTSTATUS UpdateDownloadThread( - _In_ PVOID Parameter - ) -{ - BOOLEAN downloadSuccess = FALSE; - BOOLEAN hashSuccess = FALSE; - BOOLEAN signatureSuccess = FALSE; - HANDLE tempFileHandle = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - PPH_STRING setupTempPath = NULL; - PPH_STRING downloadHostPath = NULL; - PPH_STRING downloadUrlPath = NULL; - PPH_STRING userAgentString = NULL; - PPH_STRING fullSetupPath = NULL; - PPH_STRING randomGuidString = NULL; - PUPDATER_HASH_CONTEXT hashContext = NULL; - ULONG indexOfFileName = -1; - GUID randomGuid; - URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - LARGE_INTEGER timeNow; - LARGE_INTEGER timeStart; - ULONG64 timeTicks = 0; - ULONG64 timeBitsPerSecond = 0; - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter; - - context->FileDownloadUrl = PhFormatString( - L"https://wj32.org/processhacker/plugins/download.php?id=%s&type=64", - PhGetStringOrEmpty(context->Node->Id) - ); - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request..."); - - // Create a user agent string. - //userAgentString = PhFormatString( - // L"PH_%lu.%lu_%lu", - // context->CurrentMajorVersion, - // context->CurrentMinorVersion, - // context->CurrentRevisionVersion - // ); - //if (PhIsNullOrEmptyString(userAgentString)) - // goto CleanupExit; - - setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - if (PhIsNullOrEmptyString(setupTempPath)) - goto CleanupExit; - if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) - goto CleanupExit; - if (PhIsNullOrEmptyString(setupTempPath)) - goto CleanupExit; - - // Generate random guid for our directory path. - PhGenerateGuid(&randomGuid); - - if (randomGuidString = PhFormatGuid(&randomGuid)) - { - PPH_STRING guidSubString; - - // Strip the left and right curly brackets. - guidSubString = PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2); - - PhMoveReference(&randomGuidString, guidSubString); - } - - // Append the tempath to our string: %TEMP%RandomString\\processhacker-%lu.%lu-setup.exe - // Example: C:\\Users\\dmex\\AppData\\Temp\\ABCD\\processhacker-2.90-setup.exe - context->SetupFilePath = PhFormatString( - L"%s%s\\%s.zip", - PhGetStringOrEmpty(setupTempPath), - PhGetStringOrEmpty(randomGuidString), - PhGetStringOrEmpty(context->Node->InternalName) - ); - if (PhIsNullOrEmptyString(context->SetupFilePath)) - goto CleanupExit; - - // Create the directory if it does not exist. - if (fullSetupPath = PhGetFullPath(PhGetString(context->SetupFilePath), &indexOfFileName)) - { - PPH_STRING directoryPath; - - if (indexOfFileName == -1) - goto CleanupExit; - - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, PhGetString(directoryPath), NULL); - PhDereferenceObject(directoryPath); - } - } - - // Create output file - if (!NT_SUCCESS(PhCreateFileWin32( - &tempFileHandle, - PhGetStringOrEmpty(context->SetupFilePath), - FILE_GENERIC_READ | FILE_GENERIC_WRITE, - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - goto CleanupExit; - } - - // Set lengths to non-zero enabling these params to be cracked. - httpUrlComponents.dwSchemeLength = (ULONG)-1; - httpUrlComponents.dwHostNameLength = (ULONG)-1; - httpUrlComponents.dwUrlPathLength = (ULONG)-1; - - if (!WinHttpCrackUrl( - PhGetStringOrEmpty(context->FileDownloadUrl), - 0, - 0, - &httpUrlComponents - )) - { - goto CleanupExit; - } - - // Create the Host string. - downloadHostPath = PhCreateStringEx(httpUrlComponents.lpszHostName, httpUrlComponents.dwHostNameLength * sizeof(WCHAR)); - // Create the Path string. - downloadUrlPath = PhCreateStringEx(httpUrlComponents.lpszUrlPath, httpUrlComponents.dwUrlPathLength * sizeof(WCHAR)); - - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); - - - // Query the current system proxy - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); - - // Open the HTTP session with the system proxy configuration if available - if (!(httpSessionHandle = WinHttpOpen( - PhGetStringOrEmpty(userAgentString), - proxyConfig.lpszProxy != NULL ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { - goto CleanupExit; - } - - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, - sizeof(ULONG) - ); - } - - if (!(httpConnectionHandle = WinHttpConnect( - httpSessionHandle, - PhGetStringOrEmpty(downloadHostPath), - httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) - { - goto CleanupExit; - } - - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - PhGetStringOrEmpty(downloadUrlPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) - ))) - { - goto CleanupExit; - } - - if (WindowsVersion >= WINDOWS_7) - { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); - } - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); - - if (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { - goto CleanupExit; - } - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Waiting for response..."); - - if (WinHttpReceiveResponse(httpRequestHandle, NULL)) - { - ULONG bytesDownloaded = 0; - ULONG downloadedBytes = 0; - ULONG contentLengthSize = sizeof(ULONG); - ULONG contentLength = 0; - PPH_STRING status; - IO_STATUS_BLOCK isb; - BYTE buffer[PAGE_SIZE]; - - status = PhFormatString(L"Downloading %s...", PhGetString(context->Node->Name)); - - SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0); - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)PhGetString(status)); - - PhDereferenceObject(status); - - // Start the clock. - PhQuerySystemTime(&timeStart); - - if (!WinHttpQueryHeaders( - httpRequestHandle, - WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, - WINHTTP_HEADER_NAME_BY_INDEX, - &contentLength, - &contentLengthSize, - 0 - )) - { - goto CleanupExit; - } - - // Initialize hash algorithm. - if (!UpdaterInitializeHash(&hashContext)) - goto CleanupExit; - - // Zero the buffer. - memset(buffer, 0, PAGE_SIZE); - - // Download the data. - while (WinHttpReadData(httpRequestHandle, buffer, PAGE_SIZE, &bytesDownloaded)) - { - // If we get zero bytes, the file was uploaded or there was an error - if (bytesDownloaded == 0) - break; - - // If the dialog was closed, just cleanup and exit - //if (!UpdateDialogThreadHandle) - // __leave; - - // Update the hash of bytes we downloaded. - UpdaterUpdateHash(hashContext, buffer, bytesDownloaded); - - // Write the downloaded bytes to disk. - if (!NT_SUCCESS(NtWriteFile( - tempFileHandle, - NULL, - NULL, - NULL, - &isb, - buffer, - bytesDownloaded, - NULL, - NULL - ))) - { - goto CleanupExit; - } - - downloadedBytes += (DWORD)isb.Information; - - // Check the number of bytes written are the same we downloaded. - if (bytesDownloaded != isb.Information) - goto CleanupExit; - - // Query the current time - PhQuerySystemTime(&timeNow); - - // Calculate the number of ticks - timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; - timeBitsPerSecond = downloadedBytes / __max(timeTicks, 1); - - // TODO: Update on timer callback. - { - FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100); - PPH_STRING totalLength = PhFormatSize(contentLength, -1); - PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, -1); - PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1); - - PPH_STRING statusMessage = PhFormatString( - L"Downloaded: %s of %s (%.0f%%)\r\nSpeed: %s/s", - totalDownloaded->Buffer, - totalLength->Buffer, - percent, - totalSpeed->Buffer - ); - - SendMessage(context->DialogHandle, TDM_SET_PROGRESS_BAR_POS, (WPARAM)percent, 0); - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)statusMessage->Buffer); - - PhDereferenceObject(statusMessage); - PhDereferenceObject(totalSpeed); - PhDereferenceObject(totalLength); - PhDereferenceObject(totalDownloaded); - } - } - - downloadSuccess = TRUE; - - if (UpdaterVerifyHash(hashContext, context->Node->SHA2_64)) - { - hashSuccess = TRUE; - } - - if (UpdaterVerifySignature(hashContext, context->Node->HASH_64)) - { - signatureSuccess = TRUE; - } - } - -CleanupExit: - - if (hashContext) - UpdaterDestroyHash(hashContext); - - if (tempFileHandle) - NtClose(tempFileHandle); - - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); - - PhClearReference(&randomGuidString); - PhClearReference(&fullSetupPath); - PhClearReference(&setupTempPath); - PhClearReference(&downloadHostPath); - PhClearReference(&downloadUrlPath); - PhClearReference(&userAgentString); - - if (downloadSuccess && hashSuccess && signatureSuccess) - { - if (NT_SUCCESS(SetupExtractBuild(context))) - { - PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0); - } - else - { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); - } - } - else - { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); - } - - return STATUS_SUCCESS; -} - -LRESULT CALLBACK TaskDialogSubclassProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case WM_NCDESTROY: - { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case WM_APP + 1: - { - if (IsIconic(hwndDlg)) - ShowWindow(hwndDlg, SW_RESTORE); - else - ShowWindow(hwndDlg, SW_SHOW); - - SetForegroundWindow(hwndDlg); - } - break; - case PH_UPDATEAVAILABLE: - { - ShowAvailableDialog(context); - } - break; - case PH_UPDATENEWER: - { - ShowUninstallRestartDialog(context); - } - break; - case PH_UPDATESUCCESS: - { - ShowInstallRestartDialog(context); - } - break; - case PH_UPDATEFAILURE: - { - if ((BOOLEAN)wParam) - ShowUpdateFailedDialog(context, TRUE, FALSE); - else if ((BOOLEAN)lParam) - ShowUpdateFailedDialog(context, FALSE, TRUE); - else - ShowUpdateFailedDialog(context, FALSE, FALSE); - } - break; - case PH_UPDATEISERRORED: - { - ShowUpdateFailedDialog(context, FALSE, FALSE); - } - break; - //case WM_PARENTNOTIFY: - // { - // if (wParam == WM_CREATE) - // { - // // uMsg == 49251 for expand/collapse button click - // HWND hwndEdit = CreateWindowEx( - // WS_EX_CLIENTEDGE, - // L"EDIT", - // NULL, - // WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, - // 5, - // 5, - // 390, - // 85, - // (HWND)lParam, // parent window - // 0, - // NULL, - // NULL - // ); - // - // CommonCreateFont(-11, hwndEdit); - // - // // Add text to the window. - // SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)L"TEST"); - // } - // } - // break; - //case WM_NCACTIVATE: - // { - // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) - // { - // if (!context->FixedWindowStyles) - // { - // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); - // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); - // context->FixedWindowStyles = TRUE; - // } - // } - // } - // break; - } - - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); -} - -HRESULT CALLBACK TaskDialogBootstrapCallback( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_CREATED: - { - context->DialogHandle = hwndDlg; - TaskDialogCreateIcons(context); - - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); - - switch (context->Action) - { - case PLUGIN_ACTION_INSTALL: - { - ShowAvailableDialog(context); - } - break; - case PLUGIN_ACTION_UNINSTALL: - { - ShowPluginUninstallDialog(context); - } - break; - case PLUGIN_ACTION_RESTART: - { - ShowUninstallRestartDialog(context); - } - break; - } - } - break; - } - - return S_OK; -} - -BOOLEAN ShowInitialDialog( - _In_ HWND Parent, - _In_ PVOID Context - ) -{ - INT result = 0; - TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_POSITION_RELATIVE_TO_WINDOW; - config.pszContent = L"Initializing..."; - config.lpCallbackData = (LONG_PTR)Context; - config.pfCallback = TaskDialogBootstrapCallback; - config.hwndParent = Parent; - - // Start the TaskDialog bootstrap - TaskDialogIndirect(&config, &result, NULL, NULL); - - return result == IDOK; -} - -BOOLEAN ShowUpdateDialog( - _In_ HWND Parent, - _In_ PLUGIN_ACTION Action - ) -{ - BOOLEAN result; - PH_AUTO_POOL autoPool; - PPH_UPDATER_CONTEXT context; - - context = CreateUpdateContext(NULL, Action); - - PhInitializeAutoPool(&autoPool); - - result = ShowInitialDialog(Parent, context); - - FreeUpdateContext(context); - PhDeleteAutoPool(&autoPool); - - return result; -} - -BOOLEAN StartInitialCheck( - _In_ HWND Parent, - _In_ PPLUGIN_NODE Node, - _In_ PLUGIN_ACTION Action - ) -{ - BOOLEAN result; - PH_AUTO_POOL autoPool; - PPH_UPDATER_CONTEXT context; - - context = CreateUpdateContext(Node, Action); - - PhInitializeAutoPool(&autoPool); - - result = ShowInitialDialog(Parent, context); - - FreeUpdateContext(context); - PhDeleteAutoPool(&autoPool); - - return result; -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/setup/verify.c b/plugins/ExtraPlugins/setup/verify.c deleted file mode 100644 index 25b01fa492e5..000000000000 --- a/plugins/ExtraPlugins/setup/verify.c +++ /dev/null @@ -1,204 +0,0 @@ -#include "..\main.h" - -static UCHAR ExtraPluginsPublicKey[] = -{ - 0x45, 0x43, 0x53, 0x31, 0x20, 0x00, 0x00, 0x00, 0x63, 0x35, 0x3E, 0x56, 0x42, - 0x64, 0xB3, 0x2F, 0xEE, 0x07, 0x85, 0x8A, 0x3D, 0x6E, 0x11, 0x98, 0x78, 0xE2, - 0xC7, 0x25, 0xD4, 0x15, 0x75, 0xD1, 0xDB, 0x63, 0x69, 0x56, 0x71, 0xCD, 0x86, - 0x05, 0xFD, 0xDE, 0xD8, 0x95, 0x89, 0xC4, 0xBC, 0x9B, 0x9E, 0x73, 0xE9, 0x74, - 0x7D, 0xB7, 0xAF, 0x07, 0x34, 0x57, 0x62, 0x72, 0x1A, 0x31, 0x46, 0x3E, 0x91, - 0x8A, 0x0B, 0x29, 0x8C, 0x97, 0xA4, 0x29 -}; - -BOOLEAN UpdaterInitializeHash( - _Out_ PUPDATER_HASH_CONTEXT *Context - ) -{ - ULONG querySize; - PUPDATER_HASH_CONTEXT hashContext; - - hashContext = PhAllocate(sizeof(UPDATER_HASH_CONTEXT)); - memset(hashContext, 0, sizeof(UPDATER_HASH_CONTEXT)); - - if (!NT_SUCCESS(BCryptOpenAlgorithmProvider( - &hashContext->SignAlgHandle, - BCRYPT_ECDSA_P256_ALGORITHM, - NULL, - 0 - ))) - { - goto error; - } - - if (!NT_SUCCESS(BCryptImportKeyPair( - hashContext->SignAlgHandle, - NULL, - BCRYPT_ECCPUBLIC_BLOB, - &hashContext->KeyHandle, - ExtraPluginsPublicKey, - sizeof(ExtraPluginsPublicKey), - 0 - ))) - { - goto error; - } - - if (!NT_SUCCESS(BCryptOpenAlgorithmProvider( - &hashContext->HashAlgHandle, - BCRYPT_SHA256_ALGORITHM, - NULL, - 0 - ))) - { - goto error; - } - - if (!NT_SUCCESS(BCryptGetProperty( - hashContext->HashAlgHandle, - BCRYPT_OBJECT_LENGTH, - (PUCHAR)&hashContext->HashObjectSize, - sizeof(ULONG), - &querySize, - 0 - ))) - { - goto error; - } - - if (!NT_SUCCESS(BCryptGetProperty( - hashContext->HashAlgHandle, - BCRYPT_HASH_LENGTH, - (PUCHAR)&hashContext->HashSize, - sizeof(ULONG), - &querySize, - 0 - ))) - { - goto error; - } - - if (!(hashContext->HashObject = PhAllocate(hashContext->HashObjectSize))) - goto error; - if (!(hashContext->Hash = PhAllocate(hashContext->HashSize))) - goto error; - - if (!NT_SUCCESS(BCryptCreateHash( - hashContext->HashAlgHandle, - &hashContext->HashHandle, - hashContext->HashObject, - hashContext->HashObjectSize, - NULL, - 0, - 0 - ))) - { - goto error; - } - - *Context = hashContext; - return TRUE; - -error: - UpdaterDestroyHash(hashContext); - return FALSE; -} - -BOOLEAN UpdaterUpdateHash( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_reads_bytes_(Length) PVOID Buffer, - _In_ ULONG Length - ) -{ - return NT_SUCCESS(BCryptHashData(Context->HashHandle, Buffer, Length, 0)); -} - -BOOLEAN UpdaterVerifyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_ PPH_STRING Sha2Hash - ) -{ - PPH_STRING sha2HexString; - - // Compute the final hash. - if (!NT_SUCCESS(BCryptFinishHash( - Context->HashHandle, - Context->Hash, - Context->HashSize, - 0 - ))) - { - return FALSE; - } - - if (!(sha2HexString = PhBufferToHexString(Context->Hash, Context->HashSize))) - return FALSE; - - if (!PhEqualString2(sha2HexString, PhGetStringOrEmpty(Sha2Hash), TRUE)) - { - PhDereferenceObject(sha2HexString); - return FALSE; - } - - PhDereferenceObject(sha2HexString); - return TRUE; -} - -BOOLEAN UpdaterVerifySignature( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_ PPH_STRING HexSignature - ) -{ - ULONG signatureLength; - PUCHAR signatureBuffer; - - signatureLength = (ULONG)HexSignature->Length / sizeof(WCHAR) / 2; - signatureBuffer = PhAllocate(signatureLength); - - if (!PhHexStringToBuffer(&HexSignature->sr, signatureBuffer)) - { - PhFree(signatureBuffer); - return FALSE; - } - - if (!NT_SUCCESS(BCryptVerifySignature( - Context->KeyHandle, - NULL, - Context->Hash, - Context->HashSize, - signatureBuffer, - signatureLength, - 0 - ))) - { - PhFree(signatureBuffer); - return FALSE; - } - - PhFree(signatureBuffer); - return TRUE; -} - -VOID UpdaterDestroyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context - ) -{ - if (Context->HashAlgHandle) - BCryptCloseAlgorithmProvider(Context->HashAlgHandle, 0); - - if (Context->SignAlgHandle) - BCryptCloseAlgorithmProvider(Context->SignAlgHandle, 0); - - if (Context->HashHandle) - BCryptDestroyHash(Context->HashHandle); - - if (Context->KeyHandle) - BCryptDestroyKey(Context->KeyHandle); - - if (Context->HashObject) - PhFree(Context->HashObject); - - if (Context->Hash) - PhFree(Context->Hash); - - PhFree(Context); -} \ No newline at end of file diff --git a/plugins/ExtraPlugins/wndtree.c b/plugins/ExtraPlugins/wndtree.c deleted file mode 100644 index 21604c7d70dd..000000000000 --- a/plugins/ExtraPlugins/wndtree.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Process Hacker Extra Plugins - - * Plugin Manager - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "main.h" - -BOOLEAN PluginsNodeHashtableCompareFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); -ULONG PluginsNodeHashtableHashFunction( - _In_ PVOID Entry - ); -VOID DestroyPluginsNode( - _In_ PPLUGIN_NODE Node - ); - -VOID DeletePluginsTree( - _In_ PWCT_CONTEXT Context - ) -{ - PPH_STRING settings = PhCmSaveSettings(Context->TreeNewHandle); - PhSetStringSetting2(SETTING_NAME_TREE_LIST_COLUMNS, &settings->sr); - PhDereferenceObject(settings); - - for (ULONG i = 0; i < Context->NodeList->Count; i++) - { - DestroyPluginsNode(Context->NodeList->Items[i]); - } - - PhDereferenceObject(Context->NodeHashtable); - PhDereferenceObject(Context->NodeList); -} - -struct _PH_TN_FILTER_SUPPORT* GetPluginListFilterSupport( - _In_ PWCT_CONTEXT Context - ) -{ - return &Context->FilterSupport; -} - -BOOLEAN PluginsNodeHashtableCompareFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PPLUGIN_NODE windowNode1 = *(PPLUGIN_NODE *)Entry1; - PPLUGIN_NODE windowNode2 = *(PPLUGIN_NODE *)Entry2; - - return PhEqualString(windowNode1->InternalName, windowNode2->InternalName, TRUE); -} - -ULONG PluginsNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashStringRef(&(*(PPLUGIN_NODE*)Entry)->InternalName->sr, TRUE); -} - -VOID PluginsAddTreeNode( - _In_ PWCT_CONTEXT Context, - _In_ PPLUGIN_NODE Entry - ) -{ - PhInitializeTreeNewNode(&Entry->Node); - - memset(Entry->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM); - Entry->Node.TextCache = Entry->TextCache; - Entry->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM; - - PhAddEntryHashtable(Context->NodeHashtable, &Entry); - PhAddItemList(Context->NodeList, Entry); - - if (Context->FilterSupport.NodeList) - { - Entry->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Entry->Node); - } -} - -PPLUGIN_NODE FindTreeNode( - _In_ PWCT_CONTEXT Context, - _In_ TREE_PLUGIN_STATE State, - _In_ PPH_STRING InternalName - ) -{ - for (ULONG i = 0; i < Context->NodeList->Count; i++) - { - PPLUGIN_NODE entry = Context->NodeList->Items[i]; - - if (entry->State == State && PhEqualString(entry->InternalName, InternalName, TRUE)) - return entry; - } - - return NULL; -} - -VOID WeRemoveWindowNode( - _In_ PWCT_CONTEXT Context, - _In_ PPLUGIN_NODE WindowNode - ) -{ - ULONG index = 0; - - // Remove from hashtable/list and cleanup. - PhRemoveEntryHashtable(Context->NodeHashtable, &WindowNode); - - if ((index = PhFindItemList(Context->NodeList, WindowNode)) != -1) - { - PhRemoveItemList(Context->NodeList, index); - } - - DestroyPluginsNode(WindowNode); -} - -VOID DestroyPluginsNode( - _In_ PPLUGIN_NODE WindowNode - ) -{ - PhDereferenceObject(WindowNode); -} - -#define SORT_FUNCTION(Column) PmPoolTreeNewCompare##Column -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PmPoolTreeNewCompare##Column( \ - _In_ void *_context, \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PPLUGIN_NODE node1 = *(PPLUGIN_NODE *)_elem1; \ - PPLUGIN_NODE node2 = *(PPLUGIN_NODE *)_elem2; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ - \ - return PhModifySort(sortResult, ((PWCT_CONTEXT)_context)->TreeNewSortOrder); \ -} - -BEGIN_SORT_FUNCTION(Name) -{ - sortResult = PhCompareString(node1->Name, node2->Name, FALSE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Author) -{ - sortResult = PhCompareString(node1->Author, node2->Author, FALSE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Version) -{ - sortResult = PhCompareString(node1->Version, node2->Version, FALSE); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI PluginsTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - __in_opt PVOID Parameter1, - __in_opt PVOID Parameter2, - __in_opt PVOID Context - ) -{ - PWCT_CONTEXT context; - PPLUGIN_NODE node; - - context = Context; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - node = (PPLUGIN_NODE)getChildren->Node; - - if (!getChildren->Node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Name), - SORT_FUNCTION(Author), - SORT_FUNCTION(Version) - }; - int (__cdecl *sortFunction)(void *, const void *, const void *); - - if (context->TreeNewSortColumn < TREE_COLUMN_ITEM_MAXIMUM) - sortFunction = sortFunctions[context->TreeNewSortColumn]; - else - sortFunction = NULL; - - if (sortFunction) - { - qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); - } - - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; - getChildren->NumberOfChildren = context->NodeList->Count; - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1; - node = (PPLUGIN_NODE)isLeaf->Node; - - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1; - node = (PPLUGIN_NODE)getCellText->Node; - - switch (getCellText->Id) - { - case TREE_COLUMN_ITEM_NAME: - getCellText->Text = PhGetStringRef(node->Name); - break; - case TREE_COLUMN_ITEM_AUTHOR: - getCellText->Text = PhGetStringRef(node->Author); - break; - case TREE_COLUMN_ITEM_VERSION: - getCellText->Text = PhGetStringRef(node->Version); - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - PPH_TREENEW_GET_NODE_COLOR getNodeColor = (PPH_TREENEW_GET_NODE_COLOR)Parameter1; - node = (PPLUGIN_NODE)getNodeColor->Node; - - getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - case TreeNewNodeExpanding: - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(context->ParentWindowHandle, WM_COMMAND, WM_ACTION, (LPARAM)context); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WCTSHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y)); - } - return TRUE; - case TreeNewHeaderRightClick: - { - PH_TN_COLUMN_MENU_DATA data; - - data.TreeNewHandle = hwnd; - data.MouseEvent = Parameter1; - data.DefaultSortColumn = 0; - data.DefaultSortOrder = AscendingSortOrder; - PhInitializeTreeNewColumnMenu(&data); - - data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); - PhHandleTreeNewColumnMenu(&data); - PhDeleteTreeNewColumnMenu(&data); - } - return TRUE; - case TreeNewCustomDraw: - { - PPH_TREENEW_CUSTOM_DRAW customDraw = Parameter1; - RECT rect = customDraw->CellRect; - node = (PPLUGIN_NODE)customDraw->Node; - - switch (customDraw->Column->Id) - { - case TREE_COLUMN_ITEM_NAME: - { - PH_STRINGREF text; - SIZE nameSize; - SIZE textSize; - - if (node->PluginOptions) - { - if (!node->Icon) - { - HBITMAP bitmapActive; - - if (bitmapActive = LoadImageFromResources(PluginInstance->DllBase, 17, 17, MAKEINTRESOURCE(IDB_SETTINGS_PNG), TRUE)) - { - node->Icon = CommonBitmapToIcon(bitmapActive, 17, 17); - DeleteObject(bitmapActive); - } - } - - if (node->Icon) - { - DrawIconEx( - customDraw->Dc, - rect.left + 5, - rect.top + ((rect.bottom - rect.top) - 17) / 2, - node->Icon, - 17, - 17, - 0, - NULL, - DI_NORMAL - ); - } - } - - rect.left += 19; - rect.left += 8; - - rect.top += 5; - rect.right -= 5; - rect.bottom -= 8; - - // top - SetTextColor(customDraw->Dc, RGB(0x0, 0x0, 0x0)); - SelectObject(customDraw->Dc, context->TitleFontHandle); - text = PhIsNullOrEmptyString(node->Name) ? PhGetStringRef(node->InternalName) : PhGetStringRef(node->Name); - GetTextExtentPoint32(customDraw->Dc, text.Buffer, (ULONG)text.Length / 2, &nameSize); - DrawText(customDraw->Dc, text.Buffer, (ULONG)text.Length / 2, &rect, DT_TOP | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE); - - // bottom - SetTextColor(customDraw->Dc, RGB(0x64, 0x64, 0x64)); - SelectObject(customDraw->Dc, context->NormalFontHandle); - text = PhGetStringRef(node->Description); - GetTextExtentPoint32(customDraw->Dc, text.Buffer, (ULONG)text.Length / 2, &textSize); - DrawText( - customDraw->Dc, - text.Buffer, - (ULONG)text.Length / 2, - &rect, - DT_BOTTOM | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE - ); - } - break; - } - } - return TRUE; - } - - return FALSE; -} - -VOID PluginsClearTree( - _In_ PWCT_CONTEXT Context - ) -{ - for (ULONG i = 0; i < Context->NodeList->Count; i++) - DestroyPluginsNode(Context->NodeList->Items[i]); - - PhClearHashtable(Context->NodeHashtable); - PhClearList(Context->NodeList); -} - -PPLUGIN_NODE WeGetSelectedWindowNode( - _In_ PWCT_CONTEXT Context - ) -{ - for (ULONG i = 0; i < Context->NodeList->Count; i++) - { - PPLUGIN_NODE windowNode = Context->NodeList->Items[i]; - - if (windowNode->Node.Selected) - return windowNode; - } - - return NULL; -} - -VOID WeGetSelectedWindowNodes( - _In_ PWCT_CONTEXT Context, - __out PPLUGIN_NODE **Windows, - __out PULONG NumberOfWindows - ) -{ - PPH_LIST list = PhCreateList(2); - - for (ULONG i = 0; i < Context->NodeList->Count; i++) - { - PPLUGIN_NODE node = (PPLUGIN_NODE)Context->NodeList->Items[i]; - - if (node->Node.Selected) - PhAddItemList(list, node); - } - - *Windows = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); - *NumberOfWindows = list->Count; - - PhDereferenceObject(list); -} - -VOID InitializePluginsTree( - _In_ PWCT_CONTEXT Context, - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle - ) -{ - Context->NodeHashtable = PhCreateHashtable( - sizeof(PPLUGIN_NODE), - PluginsNodeHashtableCompareFunction, - PluginsNodeHashtableHashFunction, - 100 - ); - Context->NodeList = PhCreateList(100); - - Context->ParentWindowHandle = ParentWindowHandle; - Context->TreeNewHandle = TreeNewHandle; - PhSetControlTheme(TreeNewHandle, L"explorer"); - - Context->NormalFontHandle = CommonCreateFont(-10, FW_NORMAL, NULL); - Context->TitleFontHandle = CommonCreateFont(-14, FW_BOLD, NULL); - - TreeNew_SetCallback(TreeNewHandle, PluginsTreeNewCallback, Context); - TreeNew_SetRowHeight(TreeNewHandle, 48); - - PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_NAME, TRUE, L"Plugin", 80, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_NAME, 0, TN_COLUMN_FLAG_CUSTOMDRAW); - PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_AUTHOR, TRUE, L"Author", 80, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_AUTHOR, 0, 0); - PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_VERSION, TRUE, L"Version", 80, PH_ALIGN_CENTER, TREE_COLUMN_ITEM_VERSION, DT_CENTER, 0); - - TreeNew_SetSort(TreeNewHandle, 0, NoSortOrder); - - PPH_STRING settings = PhGetStringSetting(SETTING_NAME_TREE_LIST_COLUMNS); - PhCmLoadSettings(TreeNewHandle, &settings->sr); - PhDereferenceObject(settings); - - PhInitializeTreeNewFilterSupport(&Context->FilterSupport, TreeNewHandle, Context->NodeList); -} \ No newline at end of file diff --git a/plugins/HardwareDevices/HardwareDevices.rc b/plugins/HardwareDevices/HardwareDevices.rc index b036823b8352..7fb0a62c1740 100644 --- a/plugins/HardwareDevices/HardwareDevices.rc +++ b/plugins/HardwareDevices/HardwareDevices.rc @@ -95,34 +95,25 @@ BEGIN CONTROL "Show hidden adapters",IDC_SHOW_HIDDEN_ADAPTERS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,159,87,10 END -IDD_NETADAPTER_DIALOG DIALOGEX 0, 0, 269, 130 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Dialog" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "",IDC_GRAPH_LAYOUT,0,21,269,53,NOT WS_VISIBLE | WS_BORDER - LTEXT "Static",IDC_ADAPTERNAME,0,0,269,21 - LTEXT "Panel layout",IDC_LAYOUT,0,91,268,36,NOT WS_VISIBLE -END - -IDD_NETADAPTER_PANEL DIALOGEX 0, 0, 254, 50 +IDD_NETADAPTER_PANEL DIALOGEX 0, 0, 330, 50 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - LTEXT "Link speed",IDC_STATIC,7,11,35,8 - RTEXT "Static",IDC_LINK_SPEED,50,11,54,8,SS_ENDELLIPSIS - GROUPBOX "Adapter",IDC_STATIC,0,0,111,34 - LTEXT "Link state",IDC_STATIC,7,23,32,8 - RTEXT "Static",IDC_LINK_STATE,50,23,54,8,SS_ENDELLIPSIS - GROUPBOX "Statistics",IDC_STATIC,116,0,136,45 - LTEXT "Bytes sent",IDC_STATIC,125,11,36,8 - LTEXT "Bytes received",IDC_STATIC,125,22,50,8 - RTEXT "Static",IDC_STAT_BSENT,182,11,62,8,SS_ENDELLIPSIS - RTEXT "Static",IDC_STAT_BRECEIVED,182,22,62,8,SS_ENDELLIPSIS - LTEXT "Bytes total",IDC_STATIC,125,33,37,8 - RTEXT "Static",IDC_STAT_BTOTAL,182,33,62,8,SS_ENDELLIPSIS - PUSHBUTTON "Details",IDC_DETAILS,0,35,50,14 + LTEXT "Bytes sent",IDC_STATIC,148,11,36,8 + LTEXT "Bytes received",IDC_STATIC,148,22,50,8 + RTEXT "Static",IDC_STAT_BSENT,205,11,62,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_STAT_BRECEIVED,205,22,62,8,SS_ENDELLIPSIS + LTEXT "Bytes total",IDC_STATIC,148,33,37,8 + RTEXT "Static",IDC_STAT_BTOTAL,205,33,62,8,SS_ENDELLIPSIS + PUSHBUTTON "Details",IDC_DETAILS,278,32,50,14 + LTEXT "Link speed",IDC_STATIC,8,11,53,8,SS_ENDELLIPSIS + LTEXT "Link state",IDC_STATIC,8,22,64,8,SS_ENDELLIPSIS + LTEXT "Bytes delta",IDC_STATIC,8,33,57,8,SS_ENDELLIPSIS + GROUPBOX "Adapter",IDC_STATIC,0,0,135,46 + RTEXT "Static",IDC_LINK_SPEED,65,11,62,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_LINK_STATE,65,22,62,8,SS_ENDELLIPSIS + RTEXT "Static",IDC_STAT_QUEUELENGTH,65,33,62,8,SS_ENDELLIPSIS + GROUPBOX "Statistics",IDC_STATIC,139,0,136,46 END IDD_NETADAPTER_DETAILS DIALOGEX 0, 0, 309, 265 @@ -130,8 +121,7 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS CAPTION "Adapter Details" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - PUSHBUTTON "Close",IDOK,253,244,50,14 - CONTROL "",IDC_DETAILS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,295,235 + CONTROL "",IDC_DETAILS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,0,0,309,265 END IDD_DISKDRIVE_OPTIONS DIALOGEX 0, 0, 265, 177 @@ -158,7 +148,7 @@ IDD_DISKDRIVE_PANEL DIALOGEX 0, 0, 330, 50 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - GROUPBOX "Statistics",IDC_STATIC,139,0,136,44 + GROUPBOX "Statistics",IDC_STATIC,139,0,136,46 LTEXT "Bytes read",IDC_STATIC,148,11,38,8 LTEXT "Bytes written",IDC_STATIC,148,22,45,8 RTEXT "Static",IDC_STAT_BREAD,205,11,62,8,SS_ENDELLIPSIS @@ -167,9 +157,9 @@ BEGIN RTEXT "Static",IDC_STAT_BTOTAL,205,33,62,8,SS_ENDELLIPSIS LTEXT "Active time",IDC_STATIC,8,11,53,8,SS_ENDELLIPSIS LTEXT "Response time",IDC_STATIC,8,22,64,8,SS_ENDELLIPSIS - LTEXT "Queue length",IDC_STATIC,8,33,57,8,SS_ENDELLIPSIS - GROUPBOX "Disk",IDC_STATIC,0,0,135,44 - PUSHBUTTON "Details",IDC_DETAILS,278,30,50,14 + LTEXT "Bytes delta",IDC_STATIC,8,33,57,8,SS_ENDELLIPSIS + GROUPBOX "Disk",IDC_STATIC,0,0,135,46 + PUSHBUTTON "Details",IDC_DETAILS,278,32,50,14 RTEXT "Static",IDC_STAT_ACTIVE,65,11,62,8,SS_ENDELLIPSIS RTEXT "Static",IDC_STAT_RESPONSETIME,65,22,62,8,SS_ENDELLIPSIS RTEXT "Static",IDC_STAT_QUEUELENGTH,65,33,62,8,SS_ENDELLIPSIS @@ -180,7 +170,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM CAPTION "SMART" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL "",IDC_DETAILS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,1,309,264 + CONTROL "",IDC_DETAILS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,0,0,309,265 END IDD_DISKDRIVE_DETAILS_FILESYSTEM DIALOGEX 0, 0, 309, 265 @@ -188,7 +178,19 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM CAPTION "Volume(s)" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL "",IDC_DETAILS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,1,309,264 + CONTROL "",IDC_DETAILS_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,0,0,309,265 +END + +IDD_NETADAPTER_DIALOG DIALOGEX 0, 0, 315, 135 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_GRAPH_LAYOUT,0,21,314,60,NOT WS_VISIBLE | WS_BORDER + LTEXT "Adapter",IDC_ADAPTERTEXT,0,0,105,21 + LTEXT "Panel layout",IDC_LAYOUT,0,98,314,36,NOT WS_VISIBLE + RTEXT "Adapter name",IDC_ADAPTERNAME,107,4,207,16,SS_WORDELLIPSIS END @@ -208,22 +210,14 @@ BEGIN BOTTOMMARGIN, 170 END - IDD_NETADAPTER_DIALOG, DIALOG - BEGIN - END - IDD_NETADAPTER_PANEL, DIALOG BEGIN - RIGHTMARGIN, 253 - TOPMARGIN, 1 + RIGHTMARGIN, 329 + BOTTOMMARGIN, 49 END IDD_NETADAPTER_DETAILS, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 302 - TOPMARGIN, 7 - BOTTOMMARGIN, 258 END IDD_DISKDRIVE_OPTIONS, DIALOG @@ -248,66 +242,20 @@ BEGIN IDD_DISKDRIVE_DETAILS_SMART, DIALOG BEGIN - TOPMARGIN, 1 END IDD_DISKDRIVE_DETAILS_FILESYSTEM, DIALOG BEGIN - TOPMARGIN, 1 END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_NETADAPTER_PANEL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_NETADAPTER_DETAILS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_NETADAPTER_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_NETADAPTER_DIALOG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DISKDRIVE_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DISKDRIVE_DIALOG AFX_DIALOG_LAYOUT -BEGIN - 0 -END -IDD_DISKDRIVE_PANEL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DISKDRIVE_DETAILS_SMART AFX_DIALOG_LAYOUT -BEGIN - 0 + IDD_NETADAPTER_DIALOG, DIALOG + BEGIN + RIGHTMARGIN, 314 + BOTTOMMARGIN, 134 + END END +#endif // APSTUDIO_INVOKED -IDD_DISKDRIVE_DETAILS_FILESYSTEM AFX_DIALOG_LAYOUT -BEGIN - 0 -END #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/plugins/HardwareDevices/HardwareDevices.vcxproj b/plugins/HardwareDevices/HardwareDevices.vcxproj index e61a3c53b8db..0ce216dcb72c 100644 --- a/plugins/HardwareDevices/HardwareDevices.vcxproj +++ b/plugins/HardwareDevices/HardwareDevices.vcxproj @@ -23,28 +23,28 @@ HardwareDevices Win32Proj HardwareDevices - 10.0.15063.0 + 10.0 DynamicLibrary Unicode - v141 + v142 DynamicLibrary Unicode - v141 + v142 DynamicLibrary Unicode - v141 + v142 DynamicLibrary Unicode - v141 + v142 @@ -53,27 +53,27 @@ - cfgmgr32.lib;iphlpapi.lib;uxtheme.lib;%(AdditionalDependencies) - cfgmgr32.dll;iphlpapi.dll;%(DelayLoadDLLs) + cfgmgr32.lib;iphlpapi.lib;%(AdditionalDependencies) + comctl32.dll;cfgmgr32.dll;iphlpapi.dll;user32.dll;%(DelayLoadDLLs) - cfgmgr32.lib;iphlpapi.lib;uxtheme.lib;%(AdditionalDependencies) - cfgmgr32.dll;iphlpapi.dll;%(DelayLoadDLLs) + cfgmgr32.lib;iphlpapi.lib;%(AdditionalDependencies) + comctl32.dll;cfgmgr32.dll;iphlpapi.dll;user32.dll;%(DelayLoadDLLs) - cfgmgr32.lib;iphlpapi.lib;uxtheme.lib;%(AdditionalDependencies) - cfgmgr32.dll;iphlpapi.dll;%(DelayLoadDLLs) + cfgmgr32.lib;iphlpapi.lib;%(AdditionalDependencies) + comctl32.dll;cfgmgr32.dll;iphlpapi.dll;user32.dll;%(DelayLoadDLLs) - cfgmgr32.lib;iphlpapi.lib;uxtheme.lib;%(AdditionalDependencies) - cfgmgr32.dll;iphlpapi.dll;%(DelayLoadDLLs) + cfgmgr32.lib;iphlpapi.lib;%(AdditionalDependencies) + comctl32.dll;cfgmgr32.dll;iphlpapi.dll;user32.dll;%(DelayLoadDLLs) @@ -93,6 +93,7 @@ + diff --git a/plugins/HardwareDevices/HardwareDevices.vcxproj.filters b/plugins/HardwareDevices/HardwareDevices.vcxproj.filters index 2a5f122356f7..f996d7a718ae 100644 --- a/plugins/HardwareDevices/HardwareDevices.vcxproj.filters +++ b/plugins/HardwareDevices/HardwareDevices.vcxproj.filters @@ -24,7 +24,10 @@ Header Files - + + Header Files + + @@ -63,7 +66,9 @@ Source Files\Disk - + + Source Files + diff --git a/plugins/HardwareDevices/adapter.c b/plugins/HardwareDevices/adapter.c index 2011a31b07a5..ad65f26bdf01 100644 --- a/plugins/HardwareDevices/adapter.c +++ b/plugins/HardwareDevices/adapter.c @@ -34,7 +34,7 @@ VOID AdapterEntryDeleteProcedure( PhRemoveItemList(NetworkAdaptersList, PhFindItemList(NetworkAdaptersList, entry)); PhReleaseQueuedLockExclusive(&NetworkAdaptersListLock); - DeleteNetAdapterId(&entry->Id); + DeleteNetAdapterId(&entry->AdapterId); PhClearReference(&entry->AdapterName); PhDeleteCircularBuffer_ULONG64(&entry->InboundBuffer); @@ -65,6 +65,7 @@ VOID NetAdaptersUpdate( ULONG64 networkOutOctets = 0; ULONG64 networkRcvSpeed = 0; ULONG64 networkXmitSpeed = 0; + ULONG64 linkSpeedValue = 0; NDIS_MEDIA_CONNECT_STATE mediaState = MediaConnectStateUnknown; entry = PhReferenceObjectSafe(NetworkAdaptersList->Items[i]); @@ -74,7 +75,15 @@ VOID NetAdaptersUpdate( if (PhGetIntegerSetting(SETTING_NAME_ENABLE_NDIS)) { - if (NT_SUCCESS(NetworkAdapterCreateHandle(&deviceHandle, entry->Id.InterfaceGuid))) + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(entry->AdapterId.InterfaceDevice), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { if (!entry->CheckedDeviceSupport) { @@ -103,31 +112,40 @@ VOID NetAdaptersUpdate( memset(&interfaceStats, 0, sizeof(NDIS_STATISTICS_INFO)); - NetworkAdapterQueryStatistics(deviceHandle, &interfaceStats); - if (NT_SUCCESS(NetworkAdapterQueryLinkState(deviceHandle, &interfaceState))) { mediaState = interfaceState.MediaConnectState; + linkSpeedValue = interfaceState.XmitLinkSpeed; } - - if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV)) - networkInOctets = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_RCV); else - networkInOctets = interfaceStats.ifHCInOctets; + { + NetworkAdapterQueryLinkSpeed(deviceHandle, &linkSpeedValue); + } - if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT)) - networkOutOctets = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_XMIT); + if (NT_SUCCESS(NetworkAdapterQueryStatistics(deviceHandle, &interfaceStats))) + { + if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV)) + networkInOctets = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_RCV); + else + networkInOctets = interfaceStats.ifHCInOctets; + + if (!(interfaceStats.SupportedStatistics & NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT)) + networkOutOctets = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_XMIT); + else + networkOutOctets = interfaceStats.ifHCOutOctets; + } else - networkOutOctets = interfaceStats.ifHCOutOctets; + { + networkInOctets = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_RCV); + networkOutOctets = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_XMIT); + } networkRcvSpeed = networkInOctets - entry->LastInboundValue; networkXmitSpeed = networkOutOctets - entry->LastOutboundValue; // HACK: Pull the Adapter name from the current query. if (!entry->AdapterName) - { - entry->AdapterName = NetworkAdapterQueryName(deviceHandle, entry->Id.InterfaceGuid); - } + entry->AdapterName = NetworkAdapterQueryName(deviceHandle, entry->AdapterId.InterfaceGuid); entry->DevicePresent = TRUE; @@ -137,7 +155,7 @@ VOID NetAdaptersUpdate( { MIB_IF_ROW2 interfaceRow; - if (QueryInterfaceRow(&entry->Id, &interfaceRow)) + if (QueryInterfaceRow(&entry->AdapterId, &interfaceRow)) { networkInOctets = interfaceRow.InOctets; networkOutOctets = interfaceRow.OutOctets; @@ -162,8 +180,17 @@ VOID NetAdaptersUpdate( if (mediaState == MediaConnectStateUnknown) { // We don't want incorrect data when the adapter is disabled. - networkRcvSpeed = 0; - networkXmitSpeed = 0; + entry->HaveFirstSample = FALSE; + } + + if (networkRcvSpeed > linkSpeedValue) + { + entry->HaveFirstSample = FALSE; + } + + if (networkXmitSpeed > linkSpeedValue) + { + entry->HaveFirstSample = FALSE; } if (!entry->HaveFirstSample) @@ -204,6 +231,7 @@ VOID InitializeNetAdapterId( Id->InterfaceIndex = InterfaceIndex; Id->InterfaceLuid = InterfaceLuid; PhSetReference(&Id->InterfaceGuid, InterfaceGuid); + Id->InterfaceDevice = PhConcatStrings2(L"\\\\.\\", InterfaceGuid->Buffer); } VOID CopyNetAdapterId( @@ -224,6 +252,7 @@ VOID DeleteNetAdapterId( ) { PhClearReference(&Id->InterfaceGuid); + PhClearReference(&Id->InterfaceDevice); } BOOLEAN EquivalentNetAdapterId( @@ -246,7 +275,7 @@ PDV_NETADAPTER_ENTRY CreateNetAdapterEntry( entry = PhCreateObject(sizeof(DV_NETADAPTER_ENTRY), NetAdapterEntryType); memset(entry, 0, sizeof(DV_NETADAPTER_ENTRY)); - CopyNetAdapterId(&entry->Id, Id); + CopyNetAdapterId(&entry->AdapterId, Id); PhInitializeCircularBuffer_ULONG64(&entry->InboundBuffer, PhGetIntegerSetting(L"SampleCount")); PhInitializeCircularBuffer_ULONG64(&entry->OutboundBuffer, PhGetIntegerSetting(L"SampleCount")); diff --git a/plugins/HardwareDevices/devices.h b/plugins/HardwareDevices/devices.h index 466df57edf2a..ce4da3a19843 100644 --- a/plugins/HardwareDevices/devices.h +++ b/plugins/HardwareDevices/devices.h @@ -2,8 +2,8 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2015-2016 dmex * Copyright (C) 2016 wj32 + * Copyright (C) 2015-2019 dmex * * This file is part of Process Hacker. * @@ -33,17 +33,11 @@ #define SETTING_NAME_DISK_COUNTERS_COLUMNS (PLUGIN_NAME L".DiskListColumns") #define SETTING_NAME_SMART_COUNTERS_COLUMNS (PLUGIN_NAME L".SmartListColumns") -#define CINTERFACE -#define COBJMACROS #include #include +#include -#include -#include -#include -#include -#include -#include +#include #include #include @@ -69,25 +63,6 @@ PPH_STRING TrimString( _In_ PPH_STRING String ); -INT AddListViewGroup( - _In_ HWND ListViewHandle, - _In_ INT Index, - _In_ PWSTR Text - ); - -INT AddListViewItemGroupId( - _In_ HWND ListViewHandle, - _In_ INT GroupId, - _In_ INT Index, - _In_ PWSTR Text, - _In_opt_ PVOID Param - ); - -ULONG64 QueryRegistryUlong64( - _In_ HANDLE KeyHandle, - _In_ PWSTR ValueName - ); - VOID ShowDeviceMenu( _In_ HWND ParentWindow, _In_ PPH_STRING DeviceInstance @@ -100,11 +75,12 @@ typedef struct _DV_NETADAPTER_ID NET_IFINDEX InterfaceIndex; IF_LUID InterfaceLuid; PPH_STRING InterfaceGuid; + PPH_STRING InterfaceDevice; } DV_NETADAPTER_ID, *PDV_NETADAPTER_ID; typedef struct _DV_NETADAPTER_ENTRY { - DV_NETADAPTER_ID Id; + DV_NETADAPTER_ID AdapterId; PPH_STRING AdapterName; union @@ -143,6 +119,19 @@ typedef struct _DV_NETADAPTER_SYSINFO_CONTEXT PPH_SYSINFO_SECTION SysinfoSection; PH_GRAPH_STATE GraphState; PH_LAYOUT_MANAGER LayoutManager; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN HaveFirstSample : 1; + BOOLEAN Spare : 7; + }; + }; + + ULONG64 LastInboundValue; + ULONG64 LastOutboundValue; } DV_NETADAPTER_SYSINFO_CONTEXT, *PDV_NETADAPTER_SYSINFO_CONTEXT; typedef struct _DV_NETADAPTER_DETAILS_CONTEXT @@ -178,19 +167,16 @@ typedef struct _DV_NETADAPTER_DETAILS_CONTEXT typedef struct _DV_NETADAPTER_CONTEXT { HWND ListViewHandle; - //HIMAGELIST ImageList; + HIMAGELIST ImageList; BOOLEAN OptionsChanged; BOOLEAN UseAlternateMethod; + PH_LAYOUT_MANAGER LayoutManager; } DV_NETADAPTER_CONTEXT, *PDV_NETADAPTER_CONTEXT; VOID NetAdaptersLoadList( VOID ); -VOID ShowOptionsDialog( - _In_ HWND ParentHandle - ); - // adapter.c VOID NetAdaptersInitialize( @@ -306,11 +292,6 @@ typedef ULONG (WINAPI* _GetInterfaceDescriptionFromGuid)( PVOID Unknown2 ); -NTSTATUS NetworkAdapterCreateHandle( - _Out_ PHANDLE DeviceHandle, - _In_ PPH_STRING InterfaceGuid - ); - BOOLEAN NetworkAdapterQuerySupported( _In_ HANDLE DeviceHandle ); @@ -440,8 +421,9 @@ typedef struct _DV_DISK_SYSINFO_CONTEXT typedef struct _DV_DISK_OPTIONS_CONTEXT { HWND ListViewHandle; - //HIMAGELIST ImageList; + HIMAGELIST ImageList; BOOLEAN OptionsChanged; + PH_LAYOUT_MANAGER LayoutManager; } DV_DISK_OPTIONS_CONTEXT, *PDV_DISK_OPTIONS_CONTEXT; VOID DiskDrivesInitialize(VOID); @@ -538,12 +520,12 @@ typedef enum _DISKDRIVE_DETAILS_INDEX DISKDRIVE_DETAILS_INDEX_MFT_READ_BYTES, DISKDRIVE_DETAILS_INDEX_MFT_WRITE_BYTES, - DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READS, - DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITES, - DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READ_BYTES, - DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITE_BYTES, + //DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READS, + //DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITES, + //DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READ_BYTES, + //DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITE_BYTES, - DISKDRIVE_DETAILS_INDEX_BITMAP_READS, + /*DISKDRIVE_DETAILS_INDEX_BITMAP_READS, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES, DISKDRIVE_DETAILS_INDEX_BITMAP_READ_BYTES, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITE_BYTES, @@ -610,9 +592,8 @@ typedef enum _DISKDRIVE_DETAILS_INDEX DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_CLUSTERS, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS, - DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS_CLUSTERS, + DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS_CLUSTERS,*/ - DISKDRIVE_DETAILS_INDEX_LOGFILE_EXCEPTIONS, DISKDRIVE_DETAILS_INDEX_OTHER_EXCEPTIONS } DISKDRIVE_DETAILS_INDEX; @@ -628,11 +609,6 @@ VOID AddRemoveDeviceChangeCallback( // storage.c -NTSTATUS DiskDriveCreateHandle( - _Out_ PHANDLE DeviceHandle, - _In_ PPH_STRING DevicePath - ); - PPH_STRING DiskDriveQueryDosMountPoints( _In_ ULONG DeviceNumber ); @@ -725,6 +701,7 @@ NTSTATUS DiskDriveQueryVolumeAttributes( _Out_ PFILE_FS_ATTRIBUTE_INFORMATION* AttributeInfo ); +// https://en.wikipedia.org/wiki/S.M.A.R.T.#Known_ATA_S.M.A.R.T._attributes typedef enum _SMART_ATTRIBUTE_ID { SMART_ATTRIBUTE_ID_READ_ERROR_RATE = 0x01, @@ -740,7 +717,16 @@ typedef enum _SMART_ATTRIBUTE_ID SMART_ATTRIBUTE_ID_CALIBRATION_RETRY_COUNT = 0x0B, SMART_ATTRIBUTE_ID_POWER_CYCLE_COUNT = 0x0C, SMART_ATTRIBUTE_ID_SOFT_READ_ERROR_RATE = 0x0D, - // Unknown values 14-182 + // TODO: Add values 14-170 + SMART_ATTRIBUTE_ID_SSD_PROGRAM_FAIL_COUNT = 0xAB, + SMART_ATTRIBUTE_ID_SSD_ERASE_FAIL_COUNT = 0xAC, + SMART_ATTRIBUTE_ID_SSD_WEAR_LEVELING_COUNT = 0xAD, + SMART_ATTRIBUTE_ID_UNEXPECTED_POWER_LOSS = 0xAE, + // TODO: Add values 175-176 + SMART_ATTRIBUTE_ID_WEAR_RANGE_DELTA = 0xB1, + // TODO: Add values 178-180 + SMART_ATTRIBUTE_ID_SSD_PROGRAM_FAIL_COUNT_TOTAL = 0xB5, + SMART_ATTRIBUTE_ID_ERASE_FAIL_COUNT = 0xB6, SMART_ATTRIBUTE_ID_SATA_DOWNSHIFT_ERROR_COUNT = 0xB7, SMART_ATTRIBUTE_ID_END_TO_END_ERROR = 0xB8, SMART_ATTRIBUTE_ID_HEAD_STABILITY = 0xB9, @@ -779,16 +765,21 @@ typedef enum _SMART_ATTRIBUTE_ID SMART_ATTRIBUTE_ID_LOAD_IN_TIME = 0xE2, SMART_ATTRIBUTE_ID_TORQUE_AMPLIFICATION_COUNT = 0xE3, SMART_ATTRIBUTE_ID_POWER_OFF_RETTRACT_CYCLE = 0xE4, - // Unknown values 229 + // TODO: Add value 229 SMART_ATTRIBUTE_ID_GMR_HEAD_AMPLITUDE = 0xE6, SMART_ATTRIBUTE_ID_DRIVE_TEMPERATURE = 0xE7, - // Unknown values 232-239 + // TODO: Add value 232 + SMART_ATTRIBUTE_ID_SSD_MEDIA_WEAROUT_HOURS = 0xE9, + SMART_ATTRIBUTE_ID_SSD_ERASE_COUNT = 0xEA, + // TODO: Add values 235-239 SMART_ATTRIBUTE_ID_HEAD_FLYING_HOURS = 0xF0, SMART_ATTRIBUTE_ID_TOTAL_LBA_WRITTEN = 0xF1, SMART_ATTRIBUTE_ID_TOTAL_LBA_READ = 0xF2, - // Unknown values 243-249 + // TODO: Add values 243-249 SMART_ATTRIBUTE_ID_READ_ERROR_RETY_RATE = 0xFA, - // Unknown values 251-253 + SMART_ATTRIBUTE_ID_MIN_SPARES_REMAINING = 0xFB, + SMART_ATTRIBUTE_ID_NEWLY_ADDED_BAD_FLASH_BLOCK = 0xFC, + // TODO: Add value 253 SMART_ATTRIBUTE_ID_FREE_FALL_PROTECTION = 0xFE, } SMART_ATTRIBUTE_ID; @@ -860,4 +851,4 @@ VOID NetAdapterSysInfoInitializing( _In_ _Assume_refs_(1) PDV_NETADAPTER_ENTRY AdapterEntry ); -#endif _DEVICES_H_ \ No newline at end of file +#endif _DEVICES_H_ diff --git a/plugins/HardwareDevices/disk.c b/plugins/HardwareDevices/disk.c index 0b7e04b83255..64e193a9d2f8 100644 --- a/plugins/HardwareDevices/disk.c +++ b/plugins/HardwareDevices/disk.c @@ -69,7 +69,15 @@ VOID DiskDrivesUpdate( if (!entry) continue; - if (NT_SUCCESS(DiskDriveCreateHandle(&deviceHandle, entry->Id.DevicePath))) + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(entry->Id.DevicePath), + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { DISK_PERFORMANCE diskPerformance; @@ -205,13 +213,21 @@ VOID DiskDriveUpdateDeviceInfo( { HANDLE deviceHandle = NULL; - if (!DeviceHandle) + if (DeviceHandle) { - DiskDriveCreateHandle(&deviceHandle, DiskEntry->Id.DevicePath); + deviceHandle = DeviceHandle; } else { - deviceHandle = DeviceHandle; + PhCreateFileWin32( + &deviceHandle, + PhGetString(DiskEntry->Id.DevicePath), + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); } if (deviceHandle) @@ -236,7 +252,7 @@ VOID DiskDriveUpdateDeviceInfo( } } - if (!DeviceHandle) + if (!DeviceHandle && deviceHandle) { NtClose(deviceHandle); } diff --git a/plugins/HardwareDevices/diskdetails.c b/plugins/HardwareDevices/diskdetails.c index 37f18f033e89..5e5692a36dfc 100644 --- a/plugins/HardwareDevices/diskdetails.c +++ b/plugins/HardwareDevices/diskdetails.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2016-2017 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -27,126 +27,76 @@ VOID DiskDriveAddListViewItemGroups( _In_ INT DiskGroupId ) { - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FS_CREATION_TIME, L"Volume creation time", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_SERIAL_NUMBER, L"Volume serial number", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_SYSTEM, L"Volume file system", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FS_VERSION, L"Volume version", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LFS_VERSION, L"LFS version", NULL); - - //AddListViewItemGroupId(Context->ListViewHandle, diskGroupId, MAXINT, L"BytesPerPhysicalSector", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_SIZE, L"Total size", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_FREE, L"Total free", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_SECTORS, L"Total sectors", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_CLUSTERS, L"Total clusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FREE_CLUSTERS, L"Free clusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_RESERVED, L"Reserved clusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_BYTES_PER_SECTOR, L"Bytes per sector", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_BYTES_PER_CLUSTER, L"Bytes per cluster", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_BYTES_PER_RECORD, L"Bytes per file record segment", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_CLUSTERS_PER_RECORD, L"Clusters per File record segment", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_RECORDS, L"MFT records", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_SIZE, L"MFT size", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_START, L"MFT start", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_ZONE, L"MFT Zone clusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_ZONE_SIZE, L"MFT zone size", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_MIRROR_START, L"MFT mirror start", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_READS, L"File reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_WRITES, L"File writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_DISK_READS, L"Disk reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_DISK_WRITES, L"Disk writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_READ_BYTES, L"File read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_WRITE_BYTES, L"File write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_READS, L"Metadata reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_WRITES, L"Metadata writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_DISK_READS, L"Metadata disk reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_DISK_WRITES, L"Metadata disk writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_READ_BYTES, L"Metadata read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_WRITE_BYTES, L"Metadata write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_READS, L"Mft reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITES, L"Mft writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_READ_BYTES, L"Mft read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITE_BYTES, L"Mft write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READS, L"RootIndex reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITES, L"RootIndex writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READ_BYTES, L"RootIndex read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITE_BYTES, L"RootIndex write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_READS, L"Bitmap reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES, L"Bitmap writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_READ_BYTES, L"Bitmap read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITE_BYTES, L"Bitmap write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_READS, L"Mft bitmap reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_WRITES, L"Mft bitmap writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_READ_BYTES, L"Mft bitmap read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_WRITE_BYTES, L"Mft bitmap write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_READS, L"User Index reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_WRITES, L"User Index writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_READ_BYTES, L"User Index read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_WRITE_BYTES, L"User Index write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_READS, L"LogFile reads", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_WRITES, L"LogFile writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_READ_BYTES, L"LogFile read bytes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_WRITE_BYTES, L"LogFile write bytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_WRITE, L"MftWritesUserLevel-Write", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_CREATE, L"MftWritesUserLevel-Create", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_SETINFO, L"MftWritesUserLevel-SetInfo", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_FLUSH, L"MftWritesUserLevel-Flush", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITES_FLUSH_LOGFILE, L"MftWritesFlushForLogFileFull", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITES_LAZY_WRITER, L"MftWritesLazyWriter", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITES_USER_REQUEST, L"MftWritesUserRequest", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES, L"Mft2Writes", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_WRITE_BYTES, L"Mft2WriteBytes", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_WRITE, L"Mft2WritesUserLevel-Write", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_CREATE, L"Mft2WritesUserLevel-Create", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_SETINFO, L"Mft2WritesUserLevel-SetInfo", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_FLUSH, L"Mft2WritesUserLevel-Flush", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES_FLUSH_LOGFILE, L"Mft2WritesFlushForLogFileFull", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES_LAZY_WRITER, L"Mft2WritesLazyWriter", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES_USER_REQUEST, L"Mft2WritesUserRequest", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_FLUSH_LOGFILE, L"BitmapWritesFlushForLogFileFull", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_LAZY_WRITER, L"BitmapWritesLazyWriter", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_USER_REQUEST, L"BitmapWritesUserRequest", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_WRITE, L"BitmapWritesUserLevel-Write", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_CREATE, L"BitmapWritesUserLevel-Create", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_SETINFO, L"BitmapWritesUserLevel-SetInfo", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_FLUSH_LOGFILE, L"MftBitmapWritesFlushForLogFileFull", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_LAZY_WRITER, L"MftBitmapWritesLazyWriter", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_REQUEST, L"MftBitmapWritesUserRequest", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_WRITE, L"MftBitmapWritesUserLevel-Write", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_CREATE, L"MftBitmapWritesUserLevel-Create", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_SETINFO, L"MftBitmapWritesUserLevel-SetInfo", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_FLUSH, L"MftBitmapWritesUserLevel-Flush", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CALLS, L"Allocate-Calls", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CLUSTERS, L"Allocate-Clusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_HINTS, L"Allocate-Hints", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_RUNS_RETURNED, L"Allocate-RunsReturned", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_HITS_HONORED, L"Allocate-HintsHonored", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_HITS_CLUSTERS, L"Allocate-HintsClusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE, L"Allocate-Cache", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_CLUSTERS, L"Allocate-CacheClusters", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS, L"Allocate-CacheMiss", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS_CLUSTERS, L"Allocate-CacheMissClusters", NULL); - - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_EXCEPTIONS, L"LogFileFullExceptions", NULL); - AddListViewItemGroupId(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_OTHER_EXCEPTIONS, L"OtherExceptions", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FS_CREATION_TIME, L"Volume creation time", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_SERIAL_NUMBER, L"Volume serial number", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_SYSTEM, L"Volume file system", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FS_VERSION, L"Volume version", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LFS_VERSION, L"LFS version", NULL); + + //PhAddListViewGroupItem(Context->ListViewHandle, diskGroupId, MAXINT, L"BytesPerPhysicalSector", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_SIZE, L"Total size", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_FREE, L"Total free", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_SECTORS, L"Total sectors", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_CLUSTERS, L"Total clusters", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FREE_CLUSTERS, L"Free clusters", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_RESERVED, L"Reserved clusters", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_BYTES_PER_SECTOR, L"Bytes per sector", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_BYTES_PER_CLUSTER, L"Bytes per cluster", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_BYTES_PER_RECORD, L"Bytes per file record segment", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_TOTAL_CLUSTERS_PER_RECORD, L"Clusters per File record segment", NULL); + + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_RECORDS, L"MFT records", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_SIZE, L"MFT size", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_START, L"MFT start", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_ZONE, L"MFT Zone clusters", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_ZONE_SIZE, L"MFT zone size", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_MIRROR_START, L"MFT mirror start", NULL); + + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_READS, L"File reads", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_WRITES, L"File writes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_DISK_READS, L"Disk reads", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_DISK_WRITES, L"Disk writes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_READ_BYTES, L"File read bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_FILE_WRITE_BYTES, L"File write bytes", NULL); + + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_READS, L"Metadata reads", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_WRITES, L"Metadata writes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_DISK_READS, L"Metadata disk reads", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_DISK_WRITES, L"Metadata disk writes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_READ_BYTES, L"Metadata read bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_METADATA_WRITE_BYTES, L"Metadata write bytes", NULL); + + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_READS, L"Mft reads", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITES, L"Mft writes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_READ_BYTES, L"Mft read bytes", NULL); + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_WRITE_BYTES, L"Mft write bytes", NULL); + + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READS, L"RootIndex reads", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITES, L"RootIndex writes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READ_BYTES, L"RootIndex read bytes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITE_BYTES, L"RootIndex write bytes", NULL); + + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_READS, L"Bitmap reads", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES, L"Bitmap writes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_READ_BYTES, L"Bitmap read bytes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITE_BYTES, L"Bitmap write bytes", NULL); + + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_READS, L"Mft bitmap reads", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_WRITES, L"Mft bitmap writes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_READ_BYTES, L"Mft bitmap read bytes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_WRITE_BYTES, L"Mft bitmap write bytes", NULL); + + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_READS, L"User Index reads", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_WRITES, L"User Index writes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_READ_BYTES, L"User Index read bytes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_USER_INDEX_WRITE_BYTES, L"User Index write bytes", NULL); + + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_READS, L"LogFile reads", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_WRITES, L"LogFile writes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_READ_BYTES, L"LogFile read bytes", NULL); + //PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_LOGFILE_WRITE_BYTES, L"LogFile write bytes", NULL); + + PhAddListViewGroupItem(ListViewHandle, DiskGroupId, DISKDRIVE_DETAILS_INDEX_OTHER_EXCEPTIONS, L"Exceptions", NULL); } VOID DiskDriveQuerySmart( @@ -156,7 +106,15 @@ VOID DiskDriveQuerySmart( PPH_LIST attributes; HANDLE deviceHandle; - if (NT_SUCCESS(DiskDriveCreateHandle(&deviceHandle, Context->PageContext->DiskId.DevicePath))) + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(Context->PageContext->DiskId.DevicePath), + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { if (NT_SUCCESS(DiskDriveQueryImminentFailure(deviceHandle, &attributes))) { @@ -169,20 +127,20 @@ VOID DiskDriveQuerySmart( MAXINT, SmartAttributeGetText(attribute->AttributeId), IntToPtr(attribute->AttributeId) - ); + ); PhSetListViewSubItem( Context->ListViewHandle, lvItemIndex, 1, PhaFormatString(L"%lu", attribute->CurrentValue)->Buffer - ); + ); PhSetListViewSubItem( Context->ListViewHandle, lvItemIndex, 2, PhaFormatString(L"%lu", attribute->WorstValue)->Buffer - ); + ); if (attribute->RawValue) { @@ -191,9 +149,29 @@ VOID DiskDriveQuerySmart( lvItemIndex, 3, PhaFormatString(L"%lu", attribute->RawValue)->Buffer - ); + ); + + PhSetListViewSubItem( + Context->ListViewHandle, + lvItemIndex, + 4, + PhaFormatString(L"%#014x", attribute->RawValue)->Buffer + ); } + //PhSetListViewSubItem( + // Context->ListViewHandle, + // lvItemIndex, + // 5, + // PhaFormatString(L"%lu", attribute->Advisory)->Buffer + // ); + //PhSetListViewSubItem( + // Context->ListViewHandle, + // lvItemIndex, + // 6, + // PhaFormatString(L"%lu", attribute->FailureImminent)->Buffer + // ); + PhFree(attribute); } @@ -245,13 +223,18 @@ VOID DiskDriveQueryFileSystem( if (volumeInfo->VolumeLabelLength > 0) { - diskGroupId = AddListViewGroup(Context->ListViewHandle, i, - PhaFormatString(L"Volume %wc: [%s]", diskEntry->DeviceLetter, PhaCreateStringEx(volumeInfo->VolumeLabel, volumeInfo->VolumeLabelLength)->Buffer)->Buffer); + diskGroupId = PhAddListViewGroup(Context->ListViewHandle, i, PhaFormatString( + L"Volume %wc: [%s]", + diskEntry->DeviceLetter, + PhaCreateStringEx(volumeInfo->VolumeLabel, volumeInfo->VolumeLabelLength)->Buffer + )->Buffer); } else { - diskGroupId = AddListViewGroup(Context->ListViewHandle, i, - PhaFormatString(L"Volume %wc:", diskEntry->DeviceLetter)->Buffer); + diskGroupId = PhAddListViewGroup(Context->ListViewHandle, i, PhaFormatString( + L"Volume %wc:", + diskEntry->DeviceLetter + )->Buffer); } DiskDriveAddListViewItemGroups(Context->ListViewHandle, diskGroupId); @@ -261,7 +244,7 @@ VOID DiskDriveQueryFileSystem( } else { - diskGroupId = AddListViewGroup( + diskGroupId = PhAddListViewGroup( Context->ListViewHandle, i, PhaFormatString(L"Volume %wc:", diskEntry->DeviceLetter)->Buffer @@ -296,11 +279,11 @@ VOID DiskDriveQueryFileSystem( PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_LFS_VERSION, 1, PhaFormatString(L"%lu.%lu", ntfsVolumeInfo.ExtendedVolumeData.LfsMajorVersion, ntfsVolumeInfo.ExtendedVolumeData.LfsMinorVersion)->Buffer); //PhSetListViewSubItem(Context->ListViewHandle, lvItemIndex, 1, - // PhaFormatSize(ntfsVolumeInfo.ExtendedVolumeData.BytesPerPhysicalSector, -1)->Buffer); + // PhaFormatSize(ntfsVolumeInfo.ExtendedVolumeData.BytesPerPhysicalSector, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_SIZE, 1, - PhaFormatSize(ntfsVolumeInfo.VolumeData.NumberSectors.QuadPart * ntfsVolumeInfo.VolumeData.BytesPerSector, -1)->Buffer); + PhaFormatSize(ntfsVolumeInfo.VolumeData.NumberSectors.QuadPart * ntfsVolumeInfo.VolumeData.BytesPerSector, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_FREE, 1, - PhaFormatString(L"%s (%.2f%%)", PhaFormatSize(ntfsVolumeInfo.VolumeData.FreeClusters.QuadPart * ntfsVolumeInfo.VolumeData.BytesPerCluster, -1)->Buffer, (FLOAT)(ntfsVolumeInfo.VolumeData.FreeClusters.QuadPart * 100) / ntfsVolumeInfo.VolumeData.TotalClusters.QuadPart)->Buffer); + PhaFormatString(L"%s (%.2f%%)", PhaFormatSize(ntfsVolumeInfo.VolumeData.FreeClusters.QuadPart * ntfsVolumeInfo.VolumeData.BytesPerCluster, ULONG_MAX)->Buffer, (FLOAT)(ntfsVolumeInfo.VolumeData.FreeClusters.QuadPart * 100) / ntfsVolumeInfo.VolumeData.TotalClusters.QuadPart)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_SECTORS, 1, PhaFormatUInt64(ntfsVolumeInfo.VolumeData.NumberSectors.QuadPart, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_CLUSTERS, 1, @@ -320,13 +303,13 @@ VOID DiskDriveQueryFileSystem( PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_RECORDS, 1, PhaFormatUInt64(ntfsVolumeInfo.VolumeData.MftValidDataLength.QuadPart / ntfsVolumeInfo.VolumeData.BytesPerFileRecordSegment, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_SIZE, 1, - PhaFormatString(L"%s (%.2f%%)", PhaFormatSize(ntfsVolumeInfo.VolumeData.MftValidDataLength.QuadPart, -1)->Buffer, (FLOAT)(ntfsVolumeInfo.VolumeData.MftValidDataLength.QuadPart * 100 / ntfsVolumeInfo.VolumeData.BytesPerCluster) / ntfsVolumeInfo.VolumeData.TotalClusters.QuadPart)->Buffer); + PhaFormatString(L"%s (%.2f%%)", PhaFormatSize(ntfsVolumeInfo.VolumeData.MftValidDataLength.QuadPart, ULONG_MAX)->Buffer, (FLOAT)(ntfsVolumeInfo.VolumeData.MftValidDataLength.QuadPart * 100 / ntfsVolumeInfo.VolumeData.BytesPerCluster) / ntfsVolumeInfo.VolumeData.TotalClusters.QuadPart)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_START, 1, PhaFormatString(L"%I64u", ntfsVolumeInfo.VolumeData.MftStartLcn.QuadPart)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_ZONE, 1, PhaFormatString(L"%I64u - %I64u", ntfsVolumeInfo.VolumeData.MftZoneStart.QuadPart, ntfsVolumeInfo.VolumeData.MftZoneEnd.QuadPart)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_ZONE_SIZE, 1, - PhaFormatString(L"%s (%.2f%%)", PhaFormatSize((ntfsVolumeInfo.VolumeData.MftZoneEnd.QuadPart - ntfsVolumeInfo.VolumeData.MftZoneStart.QuadPart) * ntfsVolumeInfo.VolumeData.BytesPerCluster, -1)->Buffer, (FLOAT)(ntfsVolumeInfo.VolumeData.MftZoneEnd.QuadPart - ntfsVolumeInfo.VolumeData.MftZoneStart.QuadPart) * 100 / ntfsVolumeInfo.VolumeData.TotalClusters.QuadPart)->Buffer); + PhaFormatString(L"%s (%.2f%%)", PhaFormatSize((ntfsVolumeInfo.VolumeData.MftZoneEnd.QuadPart - ntfsVolumeInfo.VolumeData.MftZoneStart.QuadPart) * ntfsVolumeInfo.VolumeData.BytesPerCluster, ULONG_MAX)->Buffer, (FLOAT)(ntfsVolumeInfo.VolumeData.MftZoneEnd.QuadPart - ntfsVolumeInfo.VolumeData.MftZoneStart.QuadPart) * 100 / ntfsVolumeInfo.VolumeData.TotalClusters.QuadPart)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_MIRROR_START, 1, PhaFormatString(L"%I64u", ntfsVolumeInfo.VolumeData.Mft2StartLcn.QuadPart)->Buffer); } @@ -346,9 +329,9 @@ VOID DiskDriveQueryFileSystem( PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_FS_VERSION, 1, PhaFormatString(L"%lu.%lu", refsVolumeInfo.MajorVersion, refsVolumeInfo.MinorVersion)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_SIZE, 1, - PhaFormatSize(refsVolumeInfo.NumberSectors.QuadPart * refsVolumeInfo.BytesPerSector, -1)->Buffer); + PhaFormatSize(refsVolumeInfo.NumberSectors.QuadPart * refsVolumeInfo.BytesPerSector, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_FREE, 1, - PhaFormatString(L"%s (%.2f%%)", PhaFormatSize(refsVolumeInfo.FreeClusters.QuadPart * refsVolumeInfo.BytesPerCluster, -1)->Buffer, (FLOAT)(refsVolumeInfo.FreeClusters.QuadPart * 100) / refsVolumeInfo.TotalClusters.QuadPart)->Buffer); + PhaFormatString(L"%s (%.2f%%)", PhaFormatSize(refsVolumeInfo.FreeClusters.QuadPart * refsVolumeInfo.BytesPerCluster, ULONG_MAX)->Buffer, (FLOAT)(refsVolumeInfo.FreeClusters.QuadPart * 100) / refsVolumeInfo.TotalClusters.QuadPart)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_SECTORS, 1, PhaFormatUInt64(refsVolumeInfo.NumberSectors.QuadPart, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_TOTAL_CLUSTERS, 1, @@ -375,9 +358,9 @@ VOID DiskDriveQueryFileSystem( PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_DISK_WRITES, 1, PhaFormatUInt64(buffer->FileSystemStatistics.UserDiskWrites, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_FILE_READ_BYTES, 1, - PhaFormatSize(buffer->FileSystemStatistics.UserFileReadBytes, -1)->Buffer); + PhaFormatSize(buffer->FileSystemStatistics.UserFileReadBytes, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_FILE_WRITE_BYTES, 1, - PhaFormatSize(buffer->FileSystemStatistics.UserFileWriteBytes, -1)->Buffer); + PhaFormatSize(buffer->FileSystemStatistics.UserFileWriteBytes, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_METADATA_READS, 1, PhaFormatUInt64(buffer->FileSystemStatistics.MetaDataReads, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_METADATA_WRITES, 1, @@ -387,9 +370,9 @@ VOID DiskDriveQueryFileSystem( PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_METADATA_DISK_WRITES, 1, PhaFormatUInt64(buffer->FileSystemStatistics.MetaDataDiskWrites, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_METADATA_READ_BYTES, 1, - PhaFormatSize(buffer->FileSystemStatistics.MetaDataReadBytes, -1)->Buffer); + PhaFormatSize(buffer->FileSystemStatistics.MetaDataReadBytes, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_METADATA_WRITE_BYTES, 1, - PhaFormatSize(buffer->FileSystemStatistics.MetaDataWriteBytes, -1)->Buffer); + PhaFormatSize(buffer->FileSystemStatistics.MetaDataWriteBytes, ULONG_MAX)->Buffer); // NTFS specific @@ -398,131 +381,12 @@ VOID DiskDriveQueryFileSystem( PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_WRITES, 1, PhaFormatUInt64(buffer->NtfsStatistics.MftWrites, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_READ_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.MftReadBytes, -1)->Buffer); + PhaFormatSize(buffer->NtfsStatistics.MftReadBytes, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.MftWriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.RootIndexReads, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITES, 1, - PhaFormatUInt64(buffer->NtfsStatistics.RootIndexWrites, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_READ_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.RootIndexReadBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ROOT_INDEX_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.RootIndexWriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_READS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapReads, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWrites, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_READ_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.BitmapReadBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.BitmapWriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_READS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapReads, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_WRITES, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWrites, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_READ_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.MftBitmapReadBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.MftBitmapWriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_USER_INDEX_READS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.UserIndexReads, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_USER_INDEX_WRITES, 1, - PhaFormatUInt64(buffer->NtfsStatistics.UserIndexWrites, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_USER_INDEX_READ_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.UserIndexReadBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_USER_INDEX_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.UserIndexWriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_LOGFILE_READS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.LogFileReads, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_LOGFILE_WRITES, 1, - PhaFormatUInt64(buffer->NtfsStatistics.LogFileWrites, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_LOGFILE_READ_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.LogFileReadBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_LOGFILE_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.LogFileWriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_WRITE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesUserLevel.Write, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_CREATE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesUserLevel.Create, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_SETINFO, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesUserLevel.SetInfo, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_USER_LEVEL_FLUSH, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesUserLevel.Flush, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_WRITES_FLUSH_LOGFILE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesFlushForLogFileFull, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_WRITES_LAZY_WRITER, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesLazyWriter, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_WRITES_USER_REQUEST, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftWritesUserRequest, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2Writes, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_WRITE_BYTES, 1, - PhaFormatSize(buffer->NtfsStatistics.Mft2WriteBytes, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_WRITE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesUserLevel.Write, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_CREATE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesUserLevel.Create, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_SETINFO, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesUserLevel.SetInfo, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_USER_LEVEL_FLUSH, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesUserLevel.Flush, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES_FLUSH_LOGFILE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesFlushForLogFileFull, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES_LAZY_WRITER, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesLazyWriter, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT2_WRITES_USER_REQUEST, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Mft2WritesUserRequest, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_FLUSH_LOGFILE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWritesFlushForLogFileFull, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_LAZY_WRITER, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWritesLazyWriter, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_USER_REQUEST, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWritesUserRequest, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_WRITE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWritesUserLevel.Write, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_CREATE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWritesUserLevel.Create, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_BITMAP_WRITES_SETINFO, 1, - PhaFormatUInt64(buffer->NtfsStatistics.BitmapWritesUserLevel.SetInfo, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_FLUSH_LOGFILE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesFlushForLogFileFull, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_LAZY_WRITER, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesLazyWriter, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_REQUEST, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesUserRequest, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_WRITE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesUserLevel.Write, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_CREATE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesUserLevel.Create, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_SETINFO, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesUserLevel.SetInfo, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_MFT_BITMAP_USER_LEVEL_FLUSH, 1, - PhaFormatUInt64(buffer->NtfsStatistics.MftBitmapWritesUserLevel.Flush, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CALLS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.Calls, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CLUSTERS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.Clusters, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_HINTS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.Hints, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_RUNS_RETURNED, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.RunsReturned, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_HITS_HONORED, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.HintsHonored, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_HITS_CLUSTERS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.HintsClusters, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.Cache, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_CLUSTERS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.CacheClusters, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.CacheMiss, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_ALLOCATE_CACHE_MISS_CLUSTERS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.Allocate.CacheMissClusters, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_LOGFILE_EXCEPTIONS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.LogFileFullExceptions, TRUE)->Buffer); + PhaFormatSize(buffer->NtfsStatistics.MftWriteBytes, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, DISKDRIVE_DETAILS_INDEX_OTHER_EXCEPTIONS, 1, - PhaFormatUInt64(buffer->NtfsStatistics.OtherExceptions, TRUE)->Buffer); + PhaFormatUInt64(UInt32Add32To64(buffer->NtfsStatistics.LogFileFullExceptions, buffer->NtfsStatistics.OtherExceptions), TRUE)->Buffer); // TODO: Additions for Windows 8.1 and Windows 10... } @@ -563,22 +427,20 @@ INT_PTR CALLBACK DiskDriveFileSystemDetailsDlgProc( if (uMsg == WM_INITDIALOG) { - context = PhAllocate(sizeof(DV_DISK_PAGE_CONTEXT)); - memset(context, 0, sizeof(DV_DISK_PAGE_CONTEXT)); - + context = PhAllocateZero(sizeof(DV_DISK_PAGE_CONTEXT)); context->PageContext = (PCOMMON_PAGE_CONTEXT)propPageContext->Context; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_DISK_PAGE_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, L"Context"); PhSaveListViewColumnsToSetting(SETTING_NAME_DISK_COUNTERS_COLUMNS, context->ListViewHandle); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } } @@ -593,7 +455,14 @@ INT_PTR CALLBACK DiskDriveFileSystemDetailsDlgProc( context->WindowHandle = hwndDlg; context->ListViewHandle = GetDlgItem(hwndDlg, IDC_DETAILS_LIST); - PhCenterWindow(GetParent(hwndDlg), NULL); + PhCenterWindow(GetParent(hwndDlg), NULL); // HACK (dmex) + SendMessage(GetParent(hwndDlg), WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(GetParent(hwndDlg), WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + if (!!PhGetIntegerSetting(L"EnableThemeSupport")) // TODO: Required for compat (dmex) + PhInitializeWindowTheme(GetParent(hwndDlg), !!PhGetIntegerSetting(L"EnableThemeSupport")); + else + PhInitializeWindowTheme(hwndDlg, FALSE); PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); PhSetControlTheme(context->ListViewHandle, L"explorer"); @@ -603,8 +472,6 @@ INT_PTR CALLBACK DiskDriveFileSystemDetailsDlgProc( ListView_EnableGroupView(context->ListViewHandle, TRUE); PhLoadListViewColumnsFromSetting(SETTING_NAME_DISK_COUNTERS_COLUMNS, context->ListViewHandle); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - DiskDriveQueryFileSystem(context); } break; @@ -622,6 +489,66 @@ INT_PTR CALLBACK DiskDriveFileSystemDetailsDlgProc( } } break; + case WM_NOTIFY: + { + PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, PHAPP_IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + if (!PhHandleCopyListViewEMenuItem(item)) + { + switch (item->Id) + { + case PHAPP_IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; } return FALSE; @@ -643,20 +570,18 @@ INT_PTR CALLBACK DiskDriveSmartDetailsDlgProc( if (uMsg == WM_INITDIALOG) { - context = PhAllocate(sizeof(DV_DISK_PAGE_CONTEXT)); - memset(context, 0, sizeof(DV_DISK_PAGE_CONTEXT)); - + context = PhAllocateZero(sizeof(DV_DISK_PAGE_CONTEXT)); context->PageContext = (PCOMMON_PAGE_CONTEXT)propPageContext->Context; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_DISK_PAGE_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhSaveListViewColumnsToSetting(SETTING_NAME_SMART_COUNTERS_COLUMNS, context->ListViewHandle); PhFree(context); @@ -679,13 +604,16 @@ INT_PTR CALLBACK DiskDriveSmartDetailsDlgProc( PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 50, L"Value"); PhAddListViewColumn(context->ListViewHandle, 2, 2, 2, LVCFMT_LEFT, 50, L"Best"); PhAddListViewColumn(context->ListViewHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Raw"); + PhAddListViewColumn(context->ListViewHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Raw (Hex)"); + //PhAddListViewColumn(context->ListViewHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Advisory"); + //PhAddListViewColumn(context->ListViewHandle, 6, 6, 6, LVCFMT_LEFT, 80, L"Failure Imminent"); PhSetExtendedListView(context->ListViewHandle); //ExtendedListView_SetItemColorFunction(context->ListViewHandle, PhpColorItemColorFunction); PhLoadListViewColumnsFromSetting(SETTING_NAME_SMART_COUNTERS_COLUMNS, context->ListViewHandle); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - DiskDriveQuerySmart(context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); } break; case WM_SHOWWINDOW: @@ -702,6 +630,66 @@ INT_PTR CALLBACK DiskDriveSmartDetailsDlgProc( } } break; + case WM_NOTIFY: + { + PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, PHAPP_IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + if (!PhHandleCopyListViewEMenuItem(item)) + { + switch (item->Id) + { + case PHAPP_IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; } return FALSE; @@ -755,12 +743,9 @@ VOID ShowDiskDriveDetailsDialog( _In_ PDV_DISK_SYSINFO_CONTEXT Context ) { - HANDLE dialogThread = NULL; PCOMMON_PAGE_CONTEXT pageContext; - pageContext = PhAllocate(sizeof(COMMON_PAGE_CONTEXT)); - memset(pageContext, 0, sizeof(COMMON_PAGE_CONTEXT)); - + pageContext = PhAllocateZero(sizeof(COMMON_PAGE_CONTEXT)); pageContext->ParentHandle = GetParent(GetParent(Context->WindowHandle)); pageContext->DiskIndex = Context->DiskEntry->DiskIndex; //pageContext->Length = Context->DiskEntry->DiskLength; @@ -768,8 +753,5 @@ VOID ShowDiskDriveDetailsDialog( PhSetReference(&pageContext->DiskName, Context->DiskEntry->DiskName); CopyDiskId(&pageContext->DiskId, &Context->DiskEntry->Id); - if (dialogThread = PhCreateThread(0, ShowDiskDriveDetailsDialogThread, pageContext)) - NtClose(dialogThread); - else - FreeDiskDriveDetailsContext(pageContext); -} \ No newline at end of file + PhCreateThread2(ShowDiskDriveDetailsDialogThread, pageContext); +} diff --git a/plugins/HardwareDevices/diskgraph.c b/plugins/HardwareDevices/diskgraph.c index 422f031bee74..a20d1265d862 100644 --- a/plugins/HardwareDevices/diskgraph.c +++ b/plugins/HardwareDevices/diskgraph.c @@ -38,18 +38,18 @@ VOID DiskDriveUpdatePanel( _Inout_ PDV_DISK_SYSINFO_CONTEXT Context ) { - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BREAD, PhaFormatSize(Context->DiskEntry->BytesReadDelta.Value, -1)->Buffer); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BWRITE, PhaFormatSize(Context->DiskEntry->BytesWrittenDelta.Value, -1)->Buffer); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BTOTAL, PhaFormatSize(Context->DiskEntry->BytesReadDelta.Value + Context->DiskEntry->BytesWrittenDelta.Value, -1)->Buffer); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_BREAD, PhaFormatSize(Context->DiskEntry->BytesReadDelta.Value, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_BWRITE, PhaFormatSize(Context->DiskEntry->BytesWrittenDelta.Value, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_BTOTAL, PhaFormatSize(Context->DiskEntry->BytesReadDelta.Value + Context->DiskEntry->BytesWrittenDelta.Value, ULONG_MAX)->Buffer); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_ACTIVE, + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_ACTIVE, PhaFormatString(L"%.0f%%", Context->DiskEntry->ActiveTime)->Buffer ); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_RESPONSETIME, + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_RESPONSETIME, PhaFormatString(L"%.1f ms", Context->DiskEntry->ResponseTime / PH_TICKS_PER_MS)->Buffer ); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_QUEUELENGTH, - PhaFormatString(L"%lu", Context->DiskEntry->QueueDepth)->Buffer + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_QUEUELENGTH, + PhaFormatString(L"%s/s", PhaFormatSize(Context->DiskEntry->BytesReadDelta.Delta + Context->DiskEntry->BytesWrittenDelta.Delta, ULONG_MAX)->Buffer)->Buffer ); } @@ -58,14 +58,14 @@ VOID UpdateDiskDriveDialog( ) { if (!PhIsNullOrEmptyString(Context->DiskEntry->DiskName)) - SetDlgItemText(Context->WindowHandle, IDC_DISKNAME, PhGetString(Context->DiskEntry->DiskName)); + PhSetDialogItemText(Context->WindowHandle, IDC_DISKNAME, PhGetString(Context->DiskEntry->DiskName)); else - SetDlgItemText(Context->WindowHandle, IDC_DISKNAME, L"Unknown disk"); + PhSetDialogItemText(Context->WindowHandle, IDC_DISKNAME, L"Unknown disk"); if (!PhIsNullOrEmptyString(Context->DiskEntry->DiskIndexName)) - SetDlgItemText(Context->WindowHandle, IDC_DISKMOUNTPATH, PhGetString(Context->DiskEntry->DiskIndexName)); + PhSetDialogItemText(Context->WindowHandle, IDC_DISKMOUNTPATH, PhGetString(Context->DiskEntry->DiskIndexName)); else - SetDlgItemText(Context->WindowHandle, IDC_DISKMOUNTPATH, L"Unknown disk"); + PhSetDialogItemText(Context->WindowHandle, IDC_DISKMOUNTPATH, L"Unknown disk"); DiskDriveUpdateGraphs(Context); DiskDriveUpdatePanel(Context); @@ -115,15 +115,15 @@ INT_PTR CALLBACK DiskDrivePanelDialogProc( { context = (PDV_DISK_SYSINFO_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_DISK_SYSINFO_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_NCDESTROY) { - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -160,11 +160,11 @@ INT_PTR CALLBACK DiskDriveDialogProc( { context = (PDV_DISK_SYSINFO_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_DISK_SYSINFO_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { @@ -177,7 +177,7 @@ INT_PTR CALLBACK DiskDriveDialogProc( if (context->PanelWindowHandle) DestroyWindow(context->PanelWindowHandle); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -201,18 +201,18 @@ INT_PTR CALLBACK DiskDriveDialogProc( graphItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); panelItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - SendMessage(GetDlgItem(hwndDlg, IDC_DISKMOUNTPATH), WM_SETFONT, (WPARAM)context->SysinfoSection->Parameters->LargeFont, FALSE); - SendMessage(GetDlgItem(hwndDlg, IDC_DISKNAME), WM_SETFONT, (WPARAM)context->SysinfoSection->Parameters->MediumFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_DISKMOUNTPATH), context->SysinfoSection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_DISKNAME), context->SysinfoSection->Parameters->MediumFont, FALSE); if (context->DiskEntry->DiskIndexName) - SetDlgItemText(hwndDlg, IDC_DISKMOUNTPATH, context->DiskEntry->DiskIndexName->Buffer); + PhSetDialogItemText(hwndDlg, IDC_DISKMOUNTPATH, context->DiskEntry->DiskIndexName->Buffer); else - SetDlgItemText(hwndDlg, IDC_DISKMOUNTPATH, L"Unknown disk"); + PhSetDialogItemText(hwndDlg, IDC_DISKMOUNTPATH, L"Unknown disk"); if (context->DiskEntry->DiskName) - SetDlgItemText(hwndDlg, IDC_DISKNAME, context->DiskEntry->DiskName->Buffer); + PhSetDialogItemText(hwndDlg, IDC_DISKNAME, context->DiskEntry->DiskName->Buffer); else - SetDlgItemText(hwndDlg, IDC_DISKNAME, L"Unknown disk"); + PhSetDialogItemText(hwndDlg, IDC_DISKNAME, L"Unknown disk"); context->PanelWindowHandle = CreateDialogParam(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_DISKDRIVE_PANEL), hwndDlg, DiskDrivePanelDialogProc, (LPARAM)context); ShowWindow(context->PanelWindowHandle, SW_SHOW); @@ -324,9 +324,9 @@ INT_PTR CALLBACK DiskDriveDialogProc( PhMoveReference(&context->GraphState.TooltipText, PhFormatString( L"R: %s\nW: %s\n%s", - PhaFormatSize(diskReadValue, -1)->Buffer, - PhaFormatSize(diskWriteValue, -1)->Buffer, - ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + PhaFormatSize(diskReadValue, ULONG_MAX)->Buffer, + PhaFormatSize(diskWriteValue, ULONG_MAX)->Buffer, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer )); } @@ -453,9 +453,9 @@ BOOLEAN DiskDriveSectionCallback( PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( L"R: %s\nW: %s\n%s", - PhaFormatSize(diskReadValue, -1)->Buffer, - PhaFormatSize(diskWriteValue, -1)->Buffer, - ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer + PhaFormatSize(diskReadValue, ULONG_MAX)->Buffer, + PhaFormatSize(diskWriteValue, ULONG_MAX)->Buffer, + ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer )); getTooltipText->Text = Section->GraphState.TooltipText->sr; @@ -468,8 +468,8 @@ BOOLEAN DiskDriveSectionCallback( PhSetReference(&drawPanel->Title, context->DiskEntry->DiskIndexName); drawPanel->SubTitle = PhFormatString( L"R: %s\nW: %s", - PhaFormatSize(context->DiskEntry->BytesReadDelta.Delta, -1)->Buffer, - PhaFormatSize(context->DiskEntry->BytesWrittenDelta.Delta, -1)->Buffer + PhaFormatSize(context->DiskEntry->BytesReadDelta.Delta, ULONG_MAX)->Buffer, + PhaFormatSize(context->DiskEntry->BytesWrittenDelta.Delta, ULONG_MAX)->Buffer ); if (!drawPanel->Title) @@ -501,4 +501,4 @@ VOID DiskDriveSysInfoInitializing( section.Name = context->SectionName->sr; context->SysinfoSection = Pointers->CreateSection(§ion); -} \ No newline at end of file +} diff --git a/plugins/HardwareDevices/disknotify.c b/plugins/HardwareDevices/disknotify.c index f212a04d456a..24f24f5a0b94 100644 --- a/plugins/HardwareDevices/disknotify.c +++ b/plugins/HardwareDevices/disknotify.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2018 dmex * * This file is part of Process Hacker. * @@ -21,72 +21,52 @@ */ #include "devices.h" -#include +#include -static BOOLEAN SubclassActive = FALSE; +BOOLEAN MainWndDeviceChangeRegistrationEnabled = FALSE; +PH_CALLBACK_REGISTRATION MainWndDeviceChangeEventRegistration; -LRESULT CALLBACK MainWndDevicesSubclassProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData +VOID NTAPI HardwareDevicesDeviceChangeCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context ) { - // Subclassing the main window just to process drive letter notifications - // is bad and I don't know of any other way to achieve this. - // The IOCTL_MOUNTMGR_CHANGE_NOTIFY callback would have been preferred but - // doesn't work from non-elevated processes. + PMSG message = Parameter; - switch (uMsg) + switch (message->wParam) { - case WM_DEVICECHANGE: + case DBT_DEVICEARRIVAL: // Drive letter added + case DBT_DEVICEREMOVECOMPLETE: // Drive letter removed { - switch (wParam) - { - case DBT_DEVICEARRIVAL: // Drive letter added - case DBT_DEVICEREMOVECOMPLETE: // Drive letter removed - { - DEV_BROADCAST_HDR* deviceBroadcast = (DEV_BROADCAST_HDR*)lParam; - - if (deviceBroadcast->dbch_devicetype == DBT_DEVTYP_VOLUME) - { - //PDEV_BROADCAST_VOLUME deviceVolume = (PDEV_BROADCAST_VOLUME)deviceBroadcast; + DEV_BROADCAST_HDR* deviceBroadcast = (DEV_BROADCAST_HDR*)message->lParam; - PhAcquireQueuedLockShared(&DiskDrivesListLock); + if (deviceBroadcast->dbch_devicetype == DBT_DEVTYP_VOLUME) + { + //PDEV_BROADCAST_VOLUME deviceVolume = (PDEV_BROADCAST_VOLUME)deviceBroadcast; - for (ULONG i = 0; i < DiskDrivesList->Count; i++) - { - PDV_DISK_ENTRY entry; + PhAcquireQueuedLockShared(&DiskDrivesListLock); - entry = PhReferenceObjectSafe(DiskDrivesList->Items[i]); + for (ULONG i = 0; i < DiskDrivesList->Count; i++) + { + PDV_DISK_ENTRY entry; - if (!entry) - continue; + entry = PhReferenceObjectSafe(DiskDrivesList->Items[i]); - // Reset the DiskIndex so we can re-query the index on the next interval update. - entry->DiskIndex = ULONG_MAX; - // Reset the DiskIndexName so we can re-query the name on the next interval update. - PhClearReference(&entry->DiskIndexName); + if (!entry) + continue; - PhDereferenceObjectDeferDelete(entry); - } + // Reset the DiskIndex so we can re-query the index on the next interval update. + entry->DiskIndex = ULONG_MAX; + // Reset the DiskIndexName so we can re-query the name on the next interval update. + PhClearReference(&entry->DiskIndexName); - PhReleaseQueuedLockShared(&DiskDrivesListLock); - } + PhDereferenceObjectDeferDelete(entry); } - } - goto DefaultWndProc; + PhReleaseQueuedLockShared(&DiskDrivesListLock); + } } - break; } - - return DefSubclassProc(hWnd, uMsg, wParam, lParam); - -DefaultWndProc: - return DefWindowProc(hWnd, uMsg, wParam, lParam); } VOID AddRemoveDeviceChangeCallback( @@ -98,22 +78,27 @@ VOID AddRemoveDeviceChangeCallback( return; // Add the subclass only when disks are being monitored, remove when no longer needed. - if (DiskDrivesList->Count != 0) + if (DiskDrivesList->Count) { - if (!SubclassActive) + if (!MainWndDeviceChangeRegistrationEnabled) { - // We have a disk device, subclass the main window to detect drive letter changes. - SetWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0, 0); - SubclassActive = TRUE; + // We have a disk device, register for window events needed to detect drive letter changes. + MainWndDeviceChangeRegistrationEnabled = TRUE; + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackWindowNotifyEvent), + HardwareDevicesDeviceChangeCallback, + NULL, + &MainWndDeviceChangeEventRegistration + ); } } else { - if (SubclassActive) + if (MainWndDeviceChangeRegistrationEnabled) { - // The user has removed the last disk device, remove the subclass. - RemoveWindowSubclass(PhMainWndHandle, MainWndDevicesSubclassProc, 0); - SubclassActive = FALSE; + // Remove the event callback after the user has removed the last disk device. + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackWindowNotifyEvent), &MainWndDeviceChangeEventRegistration); + MainWndDeviceChangeRegistrationEnabled = FALSE; } } -} \ No newline at end of file +} diff --git a/plugins/HardwareDevices/diskoptions.c b/plugins/HardwareDevices/diskoptions.c index 42a404f99c63..5ba73a9173fe 100644 --- a/plugins/HardwareDevices/diskoptions.c +++ b/plugins/HardwareDevices/diskoptions.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2015-2016 dmex + * Copyright (C) 2015-2018 dmex * * This file is part of Process Hacker. * @@ -21,10 +21,7 @@ */ #include "devices.h" -#include - -#define ITEM_CHECKED (INDEXTOSTATEIMAGEMASK(2)) -#define ITEM_UNCHECKED (INDEXTOSTATEIMAGEMASK(1)) +#include typedef struct _DISK_ENUM_ENTRY { @@ -201,7 +198,7 @@ VOID AddDiskDriveToListView( PhMoveReference(&newId->DevicePath, DiskPath); } - lvItemIndex = AddListViewItemGroupId( + lvItemIndex = PhAddListViewGroupItem( Context->ListViewHandle, DiskPresent ? 0 : 1, MAXINT, @@ -210,7 +207,7 @@ VOID AddDiskDriveToListView( ); if (found) - ListView_SetItemState(Context->ListViewHandle, lvItemIndex, ITEM_CHECKED, LVIS_STATEIMAGEMASK); + ListView_SetCheckState(Context->ListViewHandle, lvItemIndex, TRUE); DeleteDiskId(&adapterId); } @@ -219,13 +216,13 @@ VOID FreeListViewDiskDriveEntries( _In_ PDV_DISK_OPTIONS_CONTEXT Context ) { - ULONG index = -1; + ULONG index = ULONG_MAX; while ((index = PhFindListViewItemByFlags( Context->ListViewHandle, index, LVNI_ALL - )) != -1) + )) != ULONG_MAX) { PDV_DISK_ID param; @@ -275,7 +272,7 @@ BOOLEAN QueryDiskDeviceInterfaceDescription( bufferSize = 0x40; deviceDescription = PhCreateStringEx(NULL, bufferSize); - if ((result = CM_Get_DevNode_Property( // CM_Get_DevNode_Registry_Property with CM_DRP_DEVICEDESC?? + if ((result = CM_Get_DevNode_Property( deviceInstanceHandle, &DEVPKEY_Device_FriendlyName, &devicePropertyType, @@ -364,7 +361,15 @@ VOID FindDiskDrives( diskEntry->DeviceName = PhCreateString2(&deviceDescription->sr); diskEntry->DevicePath = PhCreateString(deviceInterface); - if (NT_SUCCESS(DiskDriveCreateHandle(&deviceHandle, diskEntry->DevicePath))) + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(diskEntry->DevicePath), + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { ULONG diskIndex = ULONG_MAX; // Note: Do not initialize to zero @@ -408,6 +413,9 @@ VOID FindDiskDrives( PhDereferenceObject(deviceDescription); } + // Cleanup. + PhFree(deviceInterfaceList); + // Sort the entries qsort(deviceList->Items, deviceList->Count, sizeof(PVOID), DiskEntryCompareFunction); @@ -437,7 +445,7 @@ VOID FindDiskDrives( PhAcquireQueuedLockShared(&DiskDrivesListLock); for (ULONG i = 0; i < DiskDrivesList->Count; i++) { - ULONG index = -1; + ULONG index = ULONG_MAX; BOOLEAN found = FALSE; PDV_DISK_ENTRY entry = PhReferenceObjectSafe(DiskDrivesList->Items[i]); @@ -448,7 +456,7 @@ VOID FindDiskDrives( Context->ListViewHandle, index, LVNI_ALL - )) != -1) + )) != ULONG_MAX) { PDV_DISK_ID param; @@ -542,47 +550,72 @@ PPH_STRING FindDiskDeviceInstance( } } + PhFree(deviceInterfaceList); + return deviceInstanceString; } -//VOID LoadDiskDriveImages( -// _In_ PDV_DISK_OPTIONS_CONTEXT Context -// ) -//{ -// HICON smallIcon = NULL; -// -// Context->ImageList = ImageList_Create( -// GetSystemMetrics(SM_CXSMICON), -// GetSystemMetrics(SM_CYSMICON), -// ILC_COLOR32, -// 1, -// 1 -// ); -// -// // We could use SetupDiLoadClassIcon but this works. -// // Copied from HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e967-e325-11ce-bfc1-08002be10318}\\IconPath -// // The index is only valid on Vista and above. -// ExtractIconEx( -// L"%SystemRoot%\\system32\\imageres.dll", -// -32, -// NULL, -// &smallIcon, -// 1 -// ); -// -// if (smallIcon) -// { -// ImageList_AddIcon(Context->ImageList, smallIcon); -// DestroyIcon(smallIcon); -// -// // Set the imagelist only if the image was loaded. -// ListView_SetImageList( -// Context->ListViewHandle, -// Context->ImageList, -// LVSIL_SMALL -// ); -// } -//} +VOID LoadDiskDriveImages( + _In_ PDV_DISK_OPTIONS_CONTEXT Context + ) +{ + HICON smallIcon; + CONFIGRET result; + ULONG bufferSize; + PPH_STRING deviceDescription; + PH_STRINGREF dllPartSr; + PH_STRINGREF indexPartSr; + ULONG64 index; + DEVPROPTYPE devicePropertyType; + ULONG deviceInstanceIdLength = MAX_DEVICE_ID_LEN; + WCHAR deviceInstanceId[MAX_DEVICE_ID_LEN + 1] = L""; + + bufferSize = 0x40; + deviceDescription = PhCreateStringEx(NULL, bufferSize); + + if ((result = CM_Get_Class_Property( + &GUID_DEVCLASS_DISKDRIVE, + &DEVPKEY_DeviceClass_IconPath, + &devicePropertyType, + (PBYTE)deviceDescription->Buffer, + &bufferSize, + 0 + )) != CR_SUCCESS) + { + PhDereferenceObject(deviceDescription); + deviceDescription = PhCreateStringEx(NULL, bufferSize); + + result = CM_Get_Class_Property( + &GUID_DEVCLASS_DISKDRIVE, + &DEVPKEY_DeviceClass_IconPath, + &devicePropertyType, + (PBYTE)deviceDescription->Buffer, + &bufferSize, + 0 + ); + } + + // %SystemRoot%\System32\setupapi.dll,-53 + PhSplitStringRefAtChar(&deviceDescription->sr, ',', &dllPartSr, &indexPartSr); + PhStringToInteger64(&indexPartSr, 10, &index); + PhMoveReference(&deviceDescription, PhExpandEnvironmentStrings(&dllPartSr)); + + if (PhExtractIconEx(deviceDescription->Buffer, (INT)index, &smallIcon, NULL)) + { + Context->ImageList = ImageList_Create( + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), + ILC_COLOR32, + 1, + 1 + ); + + ImageList_AddIcon(Context->ImageList, smallIcon); + DestroyIcon(smallIcon); + + ListView_SetImageList(Context->ListViewHandle, Context->ImageList, LVSIL_SMALL); + } +} INT_PTR CALLBACK DiskDriveOptionsDlgProc( _In_ HWND hwndDlg, @@ -598,20 +631,22 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( context = (PDV_DISK_OPTIONS_CONTEXT)PhAllocate(sizeof(DV_DISK_OPTIONS_CONTEXT)); memset(context, 0, sizeof(DV_DISK_OPTIONS_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_DISK_OPTIONS_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { + PhDeleteLayoutManager(&context->LayoutManager); + if (context->OptionsChanged) DiskDrivesSaveList(); FreeListViewDiskDriveEntries(context); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } } @@ -623,31 +658,33 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( { case WM_INITDIALOG: { - // Center the property sheet. - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - // Hide the OK button. - ShowWindow(GetDlgItem(GetParent(hwndDlg), IDOK), SW_HIDE); - // Set the Cancel button text. - Button_SetText(GetDlgItem(GetParent(hwndDlg), IDCANCEL), L"Close"); - context->ListViewHandle = GetDlgItem(hwndDlg, IDC_DISKDRIVE_LISTVIEW); PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); ListView_SetExtendedListViewStyleEx(context->ListViewHandle, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); PhSetControlTheme(context->ListViewHandle, L"explorer"); PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 350, L"Disk Drives"); PhSetExtendedListView(context->ListViewHandle); + LoadDiskDriveImages(context); ListView_EnableGroupView(context->ListViewHandle, TRUE); - AddListViewGroup(context->ListViewHandle, 0, L"Connected"); - AddListViewGroup(context->ListViewHandle, 1, L"Disconnected"); + PhAddListViewGroup(context->ListViewHandle, 0, L"Connected"); + PhAddListViewGroup(context->ListViewHandle, 1, L"Disconnected"); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); FindDiskDrives(context); context->OptionsChanged = FALSE; } break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; @@ -663,7 +700,7 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( { switch (listView->uNewState & LVIS_STATEIMAGEMASK) { - case 0x2000: // checked + case INDEXTOSTATEIMAGEMASK(2): // checked { PDV_DISK_ID param = (PDV_DISK_ID)listView->lParam; @@ -678,7 +715,7 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( context->OptionsChanged = TRUE; } break; - case 0x1000: // unchecked + case INDEXTOSTATEIMAGEMASK(1): // unchecked { PDV_DISK_ID param = (PDV_DISK_ID)listView->lParam; @@ -709,4 +746,4 @@ INT_PTR CALLBACK DiskDriveOptionsDlgProc( } return FALSE; -} \ No newline at end of file +} diff --git a/plugins/HardwareDevices/fmifs.h b/plugins/HardwareDevices/fmifs.h new file mode 100644 index 000000000000..bd42899e7d3b --- /dev/null +++ b/plugins/HardwareDevices/fmifs.h @@ -0,0 +1,455 @@ +/******************************************************************** + + fmifs.h + + This header file contains the specification of the interface + between the file manager and fmifs.dll for the purposes of + accomplishing IFS functions. + + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the MIT License. + +********************************************************************/ + +#if !defined(_FMIFS_DEFN_) +#define _FMIFS_DEFN_ + +// +// These are the defines for 'PacketType'. +// +typedef enum _FMIFS_PACKET_TYPE +{ + FmIfsPercentCompleted, + FmIfsFormatReport, + FmIfsInsertDisk, + FmIfsIncompatibleFileSystem, + FmIfsFormattingDestination, + FmIfsIncompatibleMedia, + FmIfsAccessDenied, + FmIfsMediaWriteProtected, + FmIfsCantLock, + FmIfsCantQuickFormat, + FmIfsIoError, + FmIfsFinished, + FmIfsBadLabel, +#if defined(DBLSPACE_ENABLED) + FmIfsDblspaceCreateFailed, + FmIfsDblspaceMountFailed, + FmIfsDblspaceDriveLetterFailed, + FmIfsDblspaceCreated, + FmIfsDblspaceMounted, +#endif + FmIfsCheckOnReboot, + FmIfsTextMessage, + FmIfsHiddenStatus +} FMIFS_PACKET_TYPE, *PFMIFS_PACKET_TYPE; + +typedef struct _FMIFS_PERCENT_COMPLETE_INFORMATION +{ + ULONG PercentCompleted; +} FMIFS_PERCENT_COMPLETE_INFORMATION, *PFMIFS_PERCENT_COMPLETE_INFORMATION; + +typedef struct _FMIFS_FORMAT_REPORT_INFORMATION +{ + ULONG KiloBytesTotalDiskSpace; + ULONG KiloBytesAvailable; +} FMIFS_FORMAT_REPORT_INFORMATION, *PFMIFS_FORMAT_REPORT_INFORMATION; + +// The packet for FmIfsDblspaceCreated is a Unicode string +// giving the name of the Compressed Volume File; it is not +// necessarily zero-terminated. +#define DISK_TYPE_GENERIC 0 +#define DISK_TYPE_SOURCE 1 +#define DISK_TYPE_TARGET 2 +#define DISK_TYPE_SOURCE_AND_TARGET 3 + +typedef struct _FMIFS_INSERT_DISK_INFORMATION +{ + ULONG DiskType; +} FMIFS_INSERT_DISK_INFORMATION, *PFMIFS_INSERT_DISK_INFORMATION; + +typedef struct _FMIFS_IO_ERROR_INFORMATION +{ + ULONG DiskType; + ULONG Head; + ULONG Track; +} FMIFS_IO_ERROR_INFORMATION, *PFMIFS_IO_ERROR_INFORMATION; + +typedef struct _FMIFS_FINISHED_INFORMATION +{ + BOOLEAN Success; +} FMIFS_FINISHED_INFORMATION, *PFMIFS_FINISHED_INFORMATION; + +typedef struct _FMIFS_CHECKONREBOOT_INFORMATION +{ + _Out_ BOOLEAN QueryResult; // TRUE for "yes", FALSE for "no" +} FMIFS_CHECKONREBOOT_INFORMATION, *PFMIFS_CHECKONREBOOT_INFORMATION; + +typedef enum _TEXT_MESSAGE_TYPE +{ + MESSAGE_TYPE_PROGRESS, + MESSAGE_TYPE_RESULTS, + MESSAGE_TYPE_FINAL +} TEXT_MESSAGE_TYPE, *PTEXT_MESSAGE_TYPE; + +typedef struct _FMIFS_TEXT_MESSAGE +{ + _In_ TEXT_MESSAGE_TYPE MessageType; + _In_ PSTR Message; +} FMIFS_TEXT_MESSAGE, *PFMIFS_TEXT_MESSAGE; + +// This is a list of supported floppy media types for format. +typedef enum _FMIFS_MEDIA_TYPE +{ + FmMediaUnknown, + FmMediaF5_160_512, // 5.25", 160KB, 512 bytes/sector + FmMediaF5_180_512, // 5.25", 180KB, 512 bytes/sector + FmMediaF5_320_512, // 5.25", 320KB, 512 bytes/sector + FmMediaF5_320_1024, // 5.25", 320KB, 1024 bytes/sector + FmMediaF5_360_512, // 5.25", 360KB, 512 bytes/sector + FmMediaF3_720_512, // 3.5", 720KB, 512 bytes/sector + FmMediaF5_1Pt2_512, // 5.25", 1.2MB, 512 bytes/sector + FmMediaF3_1Pt44_512, // 3.5", 1.44MB, 512 bytes/sector + FmMediaF3_2Pt88_512, // 3.5", 2.88MB, 512 bytes/sector + FmMediaF3_20Pt8_512, // 3.5", 20.8MB, 512 bytes/sector + FmMediaRemovable, // Removable media other than floppy + FmMediaFixed, + FmMediaF3_120M_512 // 3.5", 120M Floppy +} FMIFS_MEDIA_TYPE, *PFMIFS_MEDIA_TYPE; + +// +// The structure below defines information to be passed into ChkdskEx. +// When new fields are added, the version number will have to be upgraded +// so that only new code will reference those new fields. +// +typedef struct +{ + UCHAR Major; // initial version is 1.0 + UCHAR Minor; + ULONG Flags; + PWSTR PathToCheck; +} FMIFS_CHKDSKEX_PARAM, *PFMIFS_CHKDSKEX_PARAM; + +// +// Internal definitions for Flags field in FMIFS_CHKDSKEX_PARAM +// +#define FMIFS_CHKDSK_RECOVER_FREE_SPACE 0x00000002UL +#define FMIFS_CHKDSK_RECOVER_ALLOC_SPACE 0x00000004UL + +// +// External definitions for Flags field in FMIFS_CHKDSKEX_PARAM +// +// FMIFS_CHKDSK_VERBOSE +// - For FAT, chkdsk will print every filename being processed +// - For NTFS, chkdsk will print clean up messages +// FMIFS_CHKDSK_RECOVER +// - Perform sector checking on free and allocated space +// FMIFS_CHKDSK_EXTEND +// - For NTFS, chkdsk will extend a volume +// FMIFS_CHKDSK_DOWNGRADE (for NT 5 or later but obsolete anyway) +// - For NTFS, this downgrade a volume from most recent NTFS version +// FMIFS_CHKDSK_ENABLE_UPGRADE (for NT 5 or later but obsolete anyway) +// - For NTFS, this upgrades a volume to most recent NTFS version +// FMIFS_CHKDSK_CHECK_IF_DIRTY +// - Perform consistency check only if the volume is dirty +// FMIFS_CHKDSK_FORCE (for NT 5 or later) +// - Forces the volume to dismount first if necessary +// FMIFS_CHKDSK_SKIP_INDEX_SCAN +// - Skip the scanning of each index entry +// FMIFS_CHKDSK_SKIP_CYCLE_SCAN +// - Skip the checking of cycles within the directory tree +// +#define FMIFS_CHKDSK_VERBOSE 0x00000001UL +#define FMIFS_CHKDSK_RECOVER (FMIFS_CHKDSK_RECOVER_FREE_SPACE | FMIFS_CHKDSK_RECOVER_ALLOC_SPACE) +#define FMIFS_CHKDSK_EXTEND 0x00000008UL +#define FMIFS_CHKDSK_DOWNGRADE 0x00000010UL +#define FMIFS_CHKDSK_ENABLE_UPGRADE 0x00000020UL +#define FMIFS_CHKDSK_CHECK_IF_DIRTY 0x00000080UL +#define FMIFS_CHKDSK_FORCE 0x00000100UL +#define FMIFS_CHKDSK_SKIP_INDEX_SCAN 0x00000200UL +#define FMIFS_CHKDSK_SKIP_CYCLE_SCAN 0x00000400UL + +// Function types/interfaces. + +typedef BOOLEAN (NTAPI *FMIFS_CALLBACK)( + _In_ FMIFS_PACKET_TYPE PacketType, + _In_ ULONG PacketLength, + _In_ PVOID PacketData + ); + +typedef VOID (NTAPI *PFMIFS_FORMAT_ROUTINE)( + _In_ PWSTR DriveName, + _In_ FMIFS_MEDIA_TYPE MediaType, + _In_ PWSTR FileSystemName, + _In_ PWSTR Label, + _In_ BOOLEAN Quick, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_FORMATEX_ROUTINE)( + _In_ PWSTR DriveName, + _In_ FMIFS_MEDIA_TYPE MediaType, + _In_ PWSTR FileSystemName, + _In_ PWSTR Label, + _In_ BOOLEAN Quick, + _In_ ULONG ClusterSize, + _In_ FMIFS_CALLBACK Callback + ); + +typedef BOOLEAN (NTAPI *PFMIFS_ENABLECOMP_ROUTINE)( + _In_ PWSTR DriveName, + _In_ USHORT CompressionFormat + ); + +typedef VOID (NTAPI *PFMIFS_CHKDSK_ROUTINE)( + _In_ PWSTR DriveName, + _In_ PWSTR FileSystemName, + _In_ BOOLEAN FixErrors, + _In_ BOOLEAN Verbose, + _In_ BOOLEAN OnlyIfDirty, + _In_ BOOLEAN Recover, + _In_ PWSTR PathToCheck, + _In_ BOOLEAN Extend, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_CHKDSKEX_ROUTINE)( + _In_ PWSTR DriveName, + _In_ PWSTR FileSystemName, + _In_ BOOLEAN FixErrors, + _In_ PFMIFS_CHKDSKEX_PARAM Param, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_EXTEND_ROUTINE)( + _In_ PWSTR DriveName, + _In_ BOOLEAN Verify, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_DISKCOPY_ROUTINE)( + _In_ PWSTR SourceDrive, + _In_ PWSTR DestDrive, + _In_ BOOLEAN Verify, + _In_ FMIFS_CALLBACK Callback + ); + +typedef BOOLEAN (NTAPI *PFMIFS_SETLABEL_ROUTINE)( + _In_ PWSTR DriveName, + _In_ PWSTR Label + ); + +typedef BOOLEAN (NTAPI *PFMIFS_QSUPMEDIA_ROUTINE)( + _In_ PWSTR DriveName, + _Out_opt_ PFMIFS_MEDIA_TYPE MediaTypeArray, + _In_ ULONG NumberOfArrayEntries, + _Out_ PULONG NumberOfMediaTypes + ); + +#if defined(DBLSPACE_ENABLED) +typedef VOID (NTAPI *PFMIFS_DOUBLESPACE_CREATE_ROUTINE)( + _In_ PWSTR HostDriveName, + _In_ ULONG Size, + _In_ PWSTR Label, + _In_ PWSTR NewDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_DOUBLESPACE_DELETE_ROUTINE)( + _In_ PWSTR DblspaceDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_DOUBLESPACE_MOUNT_ROUTINE)( + _In_ PWSTR HostDriveName, + _In_ PWSTR CvfName, + _In_ PWSTR NewDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +typedef VOID (NTAPI *PFMIFS_DOUBLESPACE_DISMOUNT_ROUTINE)( + _In_ PWSTR DblspaceDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +typedef BOOLEAN (NTAPI *PFMIFS_DOUBLESPACE_QUERY_INFO_ROUTINE)( + _In_ PWSTR DosDriveName, + _Out_ PBOOLEAN IsRemovable, + _Out_ PBOOLEAN IsFloppy, + _Out_ PBOOLEAN IsCompressed, + _Out_ PBOOLEAN Error, + _Out_ PWSTR NtDriveName, + _In_ ULONG MaxNtDriveNameLength, + _Out_ PWSTR CvfFileName, + _In_ ULONG MaxCvfFileNameLength, + _Out_ PWSTR HostDriveName, + _In_ ULONG MaxHostDriveNameLength + ); + +typedef BOOLEAN (NTAPI *PFMIFS_DOUBLESPACE_SET_AUTMOUNT_ROUTINE)( + _In_ BOOLEAN EnableAutomount + ); +#endif // DBLSPACE_ENABLED + +NTSYSAPI +VOID +NTAPI +Format( + _In_ PWSTR DriveName, + _In_ FMIFS_MEDIA_TYPE MediaType, + _In_ PWSTR FileSystemName, + _In_ PWSTR Label, + _In_ BOOLEAN Quick, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +FormatEx( + _In_ PWSTR DriveName, + _In_ FMIFS_MEDIA_TYPE MediaType, + _In_ PWSTR FileSystemName, + _In_ PWSTR Label, + _In_ BOOLEAN Quick, + _In_ ULONG ClusterSize, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +BOOLEAN +NTAPI +EnableVolumeCompression( + _In_ PWSTR DriveName, + _In_ USHORT CompressionFormat + ); + +NTSYSAPI +VOID +NTAPI +Chkdsk( + _In_ PWSTR DriveName, + _In_ PWSTR FileSystemName, + _In_ BOOLEAN FixErrors, + _In_ BOOLEAN Verbose, + _In_ BOOLEAN OnlyIfDirty, + _In_ BOOLEAN Recover, + _In_ PWSTR PathToCheck, + _In_ BOOLEAN Extend, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +ChkdskEx( + _In_ PWSTR DriveName, + _In_ PWSTR FileSystemName, + _In_ BOOLEAN FixErrors, + _In_ PFMIFS_CHKDSKEX_PARAM Param, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +Extend( + _In_ PWSTR DriveName, + _In_ BOOLEAN Verify, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +DiskCopy( + _In_ PWSTR SourceDrive, + _In_ PWSTR DestDrive, + _In_ BOOLEAN Verify, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +BOOLEAN +NTAPI +SetLabel( + _In_ PWSTR DriveName, + _In_ PWSTR Label + ); + +NTSYSAPI +BOOLEAN +NTAPI +QuerySupportedMedia( + _In_ PWSTR DriveName, + _Out_opt_ PFMIFS_MEDIA_TYPE MediaTypeArray, + _In_ ULONG NumberOfArrayEntries, + _Out_ PULONG NumberOfMediaTypes + ); + +#if defined(DBLSPACE_ENABLED) +NTSYSAPI +VOID +NTAPI +DoubleSpaceCreate( + _In_ PWSTR HostDriveName, + _In_ ULONG Size, + _In_ PWSTR Label, + _In_ PWSTR NewDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +DoubleSpaceDelete( + _In_ PWSTR DblspaceDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +DoubleSpaceMount( + _In_ PWSTR HostDriveName, + _In_ PWSTR CvfName, + _In_ PWSTR NewDriveName, + _In_ FMIFS_CALLBACK Callback + ); + +NTSYSAPI +VOID +NTAPI +DoubleSpaceDismount( + _In_ PWSTR DblspaceDriveName, + _In_ FMIFS_CALLBACK Callback + ); +#endif + +// Miscellaneous + +NTSYSAPI +BOOLEAN +NTAPI +FmifsQueryDriveInformation( + _In_ PWSTR DosDriveName, + _Out_ PBOOLEAN IsRemovable, + _Out_ PBOOLEAN IsFloppy, + _Out_ PBOOLEAN IsCompressed, + _Out_ PBOOLEAN Error, + _Out_ PWSTR NtDriveName, + _In_ ULONG MaxNtDriveNameLength, + _Out_ PWSTR CvfFileName, + _In_ ULONG MaxCvfFileNameLength, + _Out_ PWSTR HostDriveName, + _In_ ULONG MaxHostDriveNameLength + ); + +NTSYSAPI +BOOLEAN +NTAPI +FmifsSetAutomount( + _In_ BOOLEAN EnableAutomount + ); + +#endif diff --git a/plugins/HardwareDevices/main.c b/plugins/HardwareDevices/main.c index 9b2e7c4a39bd..680f34912ce8 100644 --- a/plugins/HardwareDevices/main.c +++ b/plugins/HardwareDevices/main.c @@ -67,36 +67,23 @@ VOID NTAPI ShowOptionsCallback( _In_opt_ PVOID Context ) { - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[2]; - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP; - propSheetHeader.hwndParent = (HWND)Parameter; - propSheetHeader.pszCaption = L"Hardware Devices Plugin"; - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - // Disk Drives - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DISKDRIVE_OPTIONS); - propSheetPage.pfnDlgProc = DiskDriveOptionsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"Disk Drives", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_DISKDRIVE_OPTIONS), + DiskDriveOptionsDlgProc, + NULL + ); - // Network Adapters - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_NETADAPTER_OPTIONS); - propSheetPage.pfnDlgProc = NetworkAdapterOptionsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - PhModalPropertySheet(&propSheetHeader); + optionsEntry->CreateSection( + L"Network Adapters", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_NETADAPTER_OPTIONS), + NetworkAdapterOptionsDlgProc, + NULL + ); } VOID NTAPI MainWindowShowingCallback( @@ -172,72 +159,87 @@ PPH_STRING TrimString( return PhCreateString2(&sr); } -INT AddListViewGroup( - _In_ HWND ListViewHandle, - _In_ INT GroupId, - _In_ PWSTR Text +BOOLEAN HardwareDeviceEnableDisable( + _In_ HWND ParentWindow, + _In_ PPH_STRING DeviceInstance, + _In_ BOOLEAN Enable ) { - LVGROUP group; + CONFIGRET result; + DEVINST deviceInstanceHandle; - memset(&group, 0, sizeof(LVGROUP)); - group.cbSize = sizeof(LVGROUP); - group.mask = LVGF_HEADER | LVGF_ALIGN | LVGF_STATE | LVGF_GROUPID; - group.uAlign = LVGA_HEADER_LEFT; - group.state = LVGS_COLLAPSIBLE; - group.iGroupId = GroupId; - group.pszHeader = Text; - - return (INT)ListView_InsertGroup(ListViewHandle, MAXINT, &group); -} + result = CM_Locate_DevNode( + &deviceInstanceHandle, + DeviceInstance->Buffer, + CM_LOCATE_DEVNODE_PHANTOM + ); -INT AddListViewItemGroupId( - _In_ HWND ListViewHandle, - _In_ INT GroupId, - _In_ INT Index, - _In_ PWSTR Text, - _In_opt_ PVOID Param - ) -{ - LVITEM item; + if (result != CR_SUCCESS) + { + PhShowStatus(ParentWindow, L"Failed to change the device state.", 0, CM_MapCrToWin32Err(result, ERROR_INVALID_HANDLE_STATE)); + return FALSE; + } - item.mask = LVIF_TEXT | LVIF_GROUPID; - item.iItem = Index; - item.iSubItem = 0; - item.pszText = Text; - item.iGroupId = GroupId; + if (Enable) + result = CM_Enable_DevInst(deviceInstanceHandle, 0); // CM_DISABLE_PERSIST + else + result = CM_Disable_DevInst(deviceInstanceHandle, 0); // CM_DISABLE_PERSIST - if (Param) + if (result != CR_SUCCESS) { - item.mask |= LVIF_PARAM; - item.lParam = (LPARAM)Param; + PhShowStatus(ParentWindow, L"Failed to change the device state.", 0, CM_MapCrToWin32Err(result, ERROR_INVALID_HANDLE_STATE)); + return FALSE; } - return ListView_InsertItem(ListViewHandle, &item); + return TRUE; } -ULONG64 QueryRegistryUlong64( - _In_ HANDLE KeyHandle, - _In_ PWSTR ValueName +BOOLEAN HardwareDeviceRestart( + _In_ HWND ParentWindow, + _In_ PPH_STRING DeviceInstance ) { - ULONG64 value = 0; - PH_STRINGREF valueName; - PKEY_VALUE_PARTIAL_INFORMATION buffer; + CONFIGRET result; + DEVINST deviceInstanceHandle; - PhInitializeStringRef(&valueName, ValueName); + result = CM_Locate_DevNode( + &deviceInstanceHandle, + DeviceInstance->Buffer, + CM_LOCATE_DEVNODE_PHANTOM + ); - if (NT_SUCCESS(PhQueryValueKey(KeyHandle, &valueName, KeyValuePartialInformation, &buffer))) + if (result != CR_SUCCESS) { - if (buffer->Type == REG_DWORD || buffer->Type == REG_QWORD) - { - value = *(ULONG64*)buffer->Data; - } + PhShowStatus(ParentWindow, L"Failed to restart the device.", 0, CM_MapCrToWin32Err(result, ERROR_UNKNOWN_PROPERTY)); + return FALSE; + } + + result = CM_Query_And_Remove_SubTree( + deviceInstanceHandle, + NULL, + NULL, + 0, + CM_REMOVE_NO_RESTART + ); + + if (result != CR_SUCCESS) + { + PhShowStatus(ParentWindow, L"Failed to restart the device.", 0, CM_MapCrToWin32Err(result, ERROR_UNKNOWN_PROPERTY)); + return FALSE; + } + + result = CM_Setup_DevInst( + deviceInstanceHandle, + CM_SETUP_DEVNODE_READY + ); - PhFree(buffer); + if (result != CR_SUCCESS) + { + PhShowStatus(ParentWindow, L"Failed to restart the device.", 0, CM_MapCrToWin32Err(result, ERROR_UNKNOWN_PROPERTY)); + return FALSE; } - return value; + return TRUE; } VOID ShowDeviceMenu( @@ -252,25 +254,33 @@ VOID ShowDeviceMenu( GetCursorPos(&cursorPos); menu = PhCreateEMenu(); - //PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 0, L"Enable", NULL, NULL), -1); - //PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"Disable", NULL, NULL), -1); - //PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"Properties", NULL, NULL), -1); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 0, L"Enable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 1, L"Disable", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 2, L"Restart", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, 3, L"Properties", NULL, NULL), ULONG_MAX); selectedItem = PhShowEMenu( menu, - PhMainWndHandle, + ParentWindow, PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, cursorPos.x, cursorPos.y ); - if (selectedItem && selectedItem->Id != -1) + if (selectedItem && selectedItem->Id != ULONG_MAX) { switch (selectedItem->Id) { + case 0: case 1: + HardwareDeviceEnableDisable(ParentWindow, DeviceInstance, selectedItem->Id == 0); + break; + case 2: + HardwareDeviceRestart(ParentWindow, DeviceInstance); + break; + case 3: { HMODULE devMgrHandle; @@ -286,7 +296,7 @@ VOID ShowDeviceMenu( { if (DeviceProperties_RunDLL_I = PhGetProcedureAddress(devMgrHandle, "DeviceProperties_RunDLLW", 0)) { - // This will sometimes re-throw an RPC error during debugging and can be safely ignored. + // This will sometimes re-throw an RPC error while debugging and can safely be ignored. DeviceProperties_RunDLL_I( GetParent(ParentWindow), NULL, @@ -336,7 +346,6 @@ LOGICAL DllMain( info->Author = L"dmex, wj32"; info->Description = L"Plugin for monitoring hardware devices like Disk drives and Network adapters via the System Information window."; info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1820"; - info->HasOptions = TRUE; PhRegisterCallback( PhGetPluginCallback(PluginInstance, PluginCallbackLoad), @@ -351,7 +360,7 @@ LOGICAL DllMain( &PluginUnloadCallbackRegistration ); PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration @@ -364,7 +373,7 @@ LOGICAL DllMain( ); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration @@ -377,10 +386,10 @@ LOGICAL DllMain( &SystemInformationInitializingCallbackRegistration ); - PhAddSettings(settings, ARRAYSIZE(settings)); + PhAddSettings(settings, RTL_NUMBER_OF(settings)); } break; } return TRUE; -} \ No newline at end of file +} diff --git a/plugins/HardwareDevices/ndis.c b/plugins/HardwareDevices/ndis.c index 78f19832481f..4ed0e0f235f3 100644 --- a/plugins/HardwareDevices/ndis.c +++ b/plugins/HardwareDevices/ndis.c @@ -21,31 +21,17 @@ */ #include "devices.h" +#include #include PVOID IphlpHandle = NULL; _GetInterfaceDescriptionFromGuid GetInterfaceDescriptionFromGuid_I = NULL; -NTSTATUS NetworkAdapterCreateHandle( - _Out_ PHANDLE DeviceHandle, - _In_ PPH_STRING InterfaceGuid - ) -{ - return PhCreateFileWin32( - DeviceHandle, - PhaConcatStrings(2, L"\\\\.\\", InterfaceGuid->Buffer)->Buffer, - FILE_GENERIC_READ, - FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); -} - BOOLEAN NetworkAdapterQuerySupported( _In_ HANDLE DeviceHandle ) { + NTSTATUS status; NDIS_OID opcode; IO_STATUS_BLOCK isb; BOOLEAN ndisQuerySupported = FALSE; @@ -53,15 +39,16 @@ BOOLEAN NetworkAdapterQuerySupported( BOOLEAN adapterStatsSupported = FALSE; BOOLEAN adapterLinkStateSupported = FALSE; BOOLEAN adapterLinkSpeedSupported = FALSE; - PNDIS_OID ndisObjectIdentifiers; + PNDIS_OID objectIdBuffer; + ULONG objectIdBufferLength; + ULONG attempts = 0; opcode = OID_GEN_SUPPORTED_LIST; - // TODO: 4096 objects might be too small... - ndisObjectIdentifiers = PhAllocate(PAGE_SIZE * sizeof(NDIS_OID)); - memset(ndisObjectIdentifiers, 0, PAGE_SIZE * sizeof(NDIS_OID)); + objectIdBufferLength = 2048 * sizeof(NDIS_OID); + objectIdBuffer = PhAllocateZero(objectIdBufferLength); - if (NT_SUCCESS(NtDeviceIoControlFile( + status = NtDeviceIoControlFile( DeviceHandle, NULL, NULL, @@ -70,15 +57,38 @@ BOOLEAN NetworkAdapterQuerySupported( IOCTL_NDIS_QUERY_GLOBAL_STATS, &opcode, sizeof(NDIS_OID), - ndisObjectIdentifiers, - PAGE_SIZE * sizeof(NDIS_OID) - ))) + objectIdBuffer, + objectIdBufferLength + ); + + while (status == STATUS_BUFFER_OVERFLOW && attempts < 8) + { + PhFree(objectIdBuffer); + objectIdBufferLength *= 2; + objectIdBuffer = PhAllocateZero(objectIdBufferLength); + + status = NtDeviceIoControlFile( + DeviceHandle, + NULL, + NULL, + NULL, + &isb, + IOCTL_NDIS_QUERY_GLOBAL_STATS, + &opcode, + sizeof(NDIS_OID), + objectIdBuffer, + objectIdBufferLength + ); + attempts++; + } + + if (NT_SUCCESS(status)) { ndisQuerySupported = TRUE; for (ULONG i = 0; i < (ULONG)isb.Information / sizeof(NDIS_OID); i++) { - NDIS_OID objectId = ndisObjectIdentifiers[i]; + NDIS_OID objectId = objectIdBuffer[i]; switch (objectId) { @@ -98,7 +108,7 @@ BOOLEAN NetworkAdapterQuerySupported( } } - PhFree(ndisObjectIdentifiers); + PhFree(objectIdBuffer); if (!adapterNameSupported) ndisQuerySupported = FALSE; @@ -391,7 +401,7 @@ NTSTATUS NetworkAdapterQueryLinkSpeed( memset(&result, 0, sizeof(NDIS_LINK_SPEED)); - status = NtDeviceIoControlFile( + if (NT_SUCCESS(status = NtDeviceIoControlFile( DeviceHandle, NULL, NULL, @@ -402,9 +412,10 @@ NTSTATUS NetworkAdapterQueryLinkSpeed( sizeof(NDIS_OID), &result, sizeof(result) - ); - - *LinkSpeed = UInt32x32To64(result.XmitLinkSpeed, NDIS_UNIT_OF_MEASUREMENT); + ))) + { + *LinkSpeed = UInt32x32To64(result.XmitLinkSpeed, NDIS_UNIT_OF_MEASUREMENT); + } return status; } @@ -465,7 +476,6 @@ BOOLEAN QueryInterfaceRow( return result; } - PWSTR MediumTypeToString( _In_ NDIS_PHYSICAL_MEDIUM MediumType ) @@ -517,7 +527,6 @@ PWSTR MediumTypeToString( return L"N/A"; } - //BOOLEAN NetworkAdapterQueryInternet( // _Inout_ PDV_NETADAPTER_SYSINFO_CONTEXT Context, // _In_ PPH_STRING IpAddress @@ -525,12 +534,9 @@ PWSTR MediumTypeToString( //{ // // https://technet.microsoft.com/en-us/library/cc766017.aspx // BOOLEAN socketResult = FALSE; -// WSADATA wsadata; // DNS_STATUS dnsQueryStatus = DNS_ERROR_RCODE_NO_ERROR; // PDNS_RECORD dnsQueryRecords = NULL; // -// WSAStartup(WINSOCK_VERSION, &wsadata); -// // __try // { // if ((dnsQueryStatus = DnsQuery( @@ -644,4 +650,4 @@ PWSTR MediumTypeToString( // WSACleanup(); // // return socketResult; -//} \ No newline at end of file +//} diff --git a/plugins/HardwareDevices/netdetails.c b/plugins/HardwareDevices/netdetails.c index 983a7ca071db..3d71617b4880 100644 --- a/plugins/HardwareDevices/netdetails.c +++ b/plugins/HardwareDevices/netdetails.c @@ -2,8 +2,8 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2015-2016 dmex * Copyright (C) 2016 wj32 + * Copyright (C) 2015-2019 dmex * * This file is part of Process Hacker. * @@ -41,58 +41,58 @@ VOID NetAdapterAddListViewItemGroups( ) { ListView_EnableGroupView(ListViewHandle, TRUE); - AddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, L"Adapter"); - AddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, L"Unicast"); - AddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, L"Broadcast"); - AddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, L"Multicast"); - AddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, L"Errors"); - - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_STATE, L"State", NULL); - //AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_CONNECTIVITY, L"Connectivity"); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_IPADDRESS, L"IP address", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_SUBNET, L"Subnet mask", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_GATEWAY, L"Default gateway", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_DNS, L"DNS", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_DOMAIN, L"Domain", NULL); - - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_LINKSPEED, L"Link speed", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_SENT, L"Sent", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_RECEIVED, L"Received", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_TOTAL, L"Total", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_SENDING, L"Sending", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_RECEIVING, L"Receiving", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_UTILIZATION, L"Utilization", NULL); - - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_SENTPKTS, L"Sent packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_RECVPKTS, L"Received packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_TOTALPKTS, L"Total packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_SENT, L"Sent", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVED, L"Received", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_TOTAL, L"Total", NULL); - //AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_SENDING, L"Sending"); - //AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVING, L"Receiving"); - //AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_UTILIZATION, L"Utilization"); - - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_SENTPKTS, L"Sent packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_RECVPKTS, L"Received packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTALPKTS, L"Total packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_SENT, L"Sent", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_RECEIVED, L"Received", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTAL, L"Total", NULL); - - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_SENTPKTS, L"Sent packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_RECVPKTS, L"Received packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTALPKTS, L"Total packets", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_SENT, L"Sent", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_RECEIVED, L"Received", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTAL, L"Total", NULL); - - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_SENTPKTS, L"Send errors", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_RECVPKTS, L"Receive errors", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_TOTALPKTS, L"Total errors", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_SENT, L"Send discards", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_RECEIVED, L"Receive discards", NULL); - AddListViewItemGroupId(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_TOTAL, L"Total discards", NULL); + PhAddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, L"Adapter"); + PhAddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, L"Unicast"); + PhAddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, L"Broadcast"); + PhAddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, L"Multicast"); + PhAddListViewGroup(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, L"Errors"); + + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_STATE, L"State", NULL); + //PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_CONNECTIVITY, L"Connectivity"); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_IPADDRESS, L"IP address", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_SUBNET, L"Subnet mask", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_GATEWAY, L"Default gateway", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_DNS, L"DNS", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_DOMAIN, L"Domain", NULL); + + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_LINKSPEED, L"Link speed", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_SENT, L"Sent", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_RECEIVED, L"Received", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_TOTAL, L"Total", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_SENDING, L"Sending", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_RECEIVING, L"Receiving", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ADAPTER, NETADAPTER_DETAILS_INDEX_UTILIZATION, L"Utilization", NULL); + + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_SENTPKTS, L"Sent packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_RECVPKTS, L"Received packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_TOTALPKTS, L"Total packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_SENT, L"Sent", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVED, L"Received", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_TOTAL, L"Total", NULL); + //PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_SENDING, L"Sending"); + //PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVING, L"Receiving"); + //PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_UNICAST, NETADAPTER_DETAILS_INDEX_UNICAST_UTILIZATION, L"Utilization"); + + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_SENTPKTS, L"Sent packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_RECVPKTS, L"Received packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTALPKTS, L"Total packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_SENT, L"Sent", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_RECEIVED, L"Received", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_BROADCAST, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTAL, L"Total", NULL); + + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_SENTPKTS, L"Sent packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_RECVPKTS, L"Received packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTALPKTS, L"Total packets", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_SENT, L"Sent", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_RECEIVED, L"Received", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_MULTICAST, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTAL, L"Total", NULL); + + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_SENTPKTS, L"Send errors", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_RECVPKTS, L"Receive errors", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_TOTALPKTS, L"Total errors", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_SENT, L"Send discards", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_RECEIVED, L"Receive discards", NULL); + PhAddListViewGroupItem(ListViewHandle, NETADAPTER_DETAILS_CATEGORY_ERRORS, NETADAPTER_DETAILS_INDEX_ERRORS_TOTAL, L"Total discards", NULL); } PVOID NetAdapterGetAddresses( @@ -353,7 +353,15 @@ VOID NetAdapterUpdateDetails( if (PhGetIntegerSetting(SETTING_NAME_ENABLE_NDIS)) { - if (NT_SUCCESS(NetworkAdapterCreateHandle(&deviceHandle, Context->AdapterId.InterfaceGuid))) + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(Context->AdapterId.InterfaceDevice), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { if (!Context->CheckedDeviceSupport) { @@ -440,37 +448,45 @@ VOID NetAdapterUpdateDetails( } PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_STATE, 1, mediaState == MediaConnectStateConnected ? L"Connected" : L"Disconnected"); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_LINKSPEED, 1, PhaFormatString(L"%s/s", PhaFormatSize(interfaceLinkSpeed / BITS_IN_ONE_BYTE, -1)->Buffer)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInOctets + interfaceStats.ifHCOutOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_SENDING, 1, interfaceXmitSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceXmitSpeed, -1)->Buffer)->Buffer : L""); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_RECEIVING, 1, interfaceRcvSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceRcvSpeed, -1)->Buffer)->Buffer : L""); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UTILIZATION, 1, PhaFormatString(L"%.2f%%", (FLOAT)(interfaceRcvSpeed + interfaceXmitSpeed) / (interfaceLinkSpeed / BITS_IN_ONE_BYTE) * 100)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_LINKSPEED, 1, PhaFormatString(L"%s/s", PhaFormatSize(interfaceLinkSpeed / BITS_IN_ONE_BYTE, ULONG_MAX)->Buffer)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInOctets + interfaceStats.ifHCOutOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_SENDING, 1, interfaceXmitSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceXmitSpeed, ULONG_MAX)->Buffer)->Buffer : L""); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_RECEIVING, 1, interfaceRcvSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceRcvSpeed, ULONG_MAX)->Buffer)->Buffer : L""); + + if (interfaceLinkSpeed > 0) + { + FLOAT utilization = (FLOAT) (interfaceRcvSpeed + interfaceXmitSpeed) / (interfaceLinkSpeed / BITS_IN_ONE_BYTE); + + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UTILIZATION, 1, PhaFormatString(L"%.2f%%", 100.0*utilization)->Buffer); + } + else + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UTILIZATION, 1, L"N/A"); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_SENTPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCOutUcastPkts, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_RECVPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCInUcastPkts, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_TOTALPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCInUcastPkts + interfaceStats.ifHCOutUcastPkts, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutUcastOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInUcastOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInUcastOctets + interfaceStats.ifHCOutUcastOctets, -1)->Buffer); - //PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_SENDING, 1, interfaceXmitUnicastSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceXmitUnicastSpeed, -1)->Buffer)->Buffer : L""); - //PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVING, 1, interfaceRcvUnicastSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceRcvUnicastSpeed, -1)->Buffer)->Buffer : L""); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutUcastOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInUcastOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInUcastOctets + interfaceStats.ifHCOutUcastOctets, ULONG_MAX)->Buffer); + //PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_SENDING, 1, interfaceXmitUnicastSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceXmitUnicastSpeed, ULONG_MAX)->Buffer)->Buffer : L""); + //PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_RECEIVING, 1, interfaceRcvUnicastSpeed != 0 ? PhaFormatString(L"%s/s", PhaFormatSize(interfaceRcvUnicastSpeed, ULONG_MAX)->Buffer)->Buffer : L""); //PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_UNICAST_UTILIZATION, 1, PhaFormatString(L"%.2f%%", utilization2)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_SENTPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCOutBroadcastPkts, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_RECVPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCInBroadcastPkts, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTALPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCInBroadcastPkts + interfaceStats.ifHCOutBroadcastPkts, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutBroadcastOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInBroadcastOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInBroadcastOctets + interfaceStats.ifHCOutBroadcastOctets, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutBroadcastOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInBroadcastOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_BROADCAST_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInBroadcastOctets + interfaceStats.ifHCOutBroadcastOctets, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_SENTPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCOutMulticastPkts, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_RECVPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCInMulticastPkts, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTALPKTS, 1, PhaFormatUInt64(interfaceStats.ifHCInMulticastPkts + interfaceStats.ifHCOutMulticastPkts, TRUE)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutMulticastOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInMulticastOctets, -1)->Buffer); - PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInMulticastOctets + interfaceStats.ifHCOutMulticastOctets, -1)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_SENT, 1, PhaFormatSize(interfaceStats.ifHCOutMulticastOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_RECEIVED, 1, PhaFormatSize(interfaceStats.ifHCInMulticastOctets, ULONG_MAX)->Buffer); + PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_MULTICAST_TOTAL, 1, PhaFormatSize(interfaceStats.ifHCInMulticastOctets + interfaceStats.ifHCOutMulticastOctets, ULONG_MAX)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_ERRORS_SENTPKTS, 1, PhaFormatUInt64(interfaceStats.ifOutErrors, TRUE)->Buffer); PhSetListViewSubItem(Context->ListViewHandle, NETADAPTER_DETAILS_INDEX_ERRORS_RECVPKTS, 1, PhaFormatUInt64(interfaceStats.ifInErrors, TRUE)->Buffer); @@ -497,14 +513,14 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( if (uMsg == WM_INITDIALOG) { context = (PDV_NETADAPTER_DETAILS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_NETADAPTER_DETAILS_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } if (!context) @@ -517,26 +533,24 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( context->WindowHandle = hwndDlg; context->ListViewHandle = GetDlgItem(hwndDlg, IDC_DETAILS_LIST); - if (context->AdapterName) - SetWindowText(hwndDlg, context->AdapterName->Buffer); - else - SetWindowText(hwndDlg, L"Unknown network adapter"); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + PhSetWindowText(hwndDlg, PhGetStringOrDefault(context->AdapterName, L"Unknown network adapter")); PhCenterWindow(hwndDlg, context->ParentHandle); PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); PhSetControlTheme(context->ListViewHandle, L"explorer"); - PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 135, L"Property"); - PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 262, L"Value"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 200, L"Property"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 240, L"Value"); PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); NetAdapterAddListViewItemGroups(context->ListViewHandle); PhRegisterCallback( - &PhProcessesUpdatedEvent, + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), NetAdapterProcessesUpdatedHandler, context, &context->ProcessesUpdatedRegistration @@ -552,11 +566,13 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( FALSE, &context->NotifyHandle ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); // HACK } break; case WM_DESTROY: { - PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration); + PhUnregisterCallback(PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), &context->ProcessesUpdatedRegistration); if (context->NotifyHandle) CancelMibChangeNotify2(context->NotifyHandle); @@ -570,7 +586,6 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: - case IDOK: DestroyWindow(hwndDlg); break; } @@ -592,6 +607,71 @@ INT_PTR CALLBACK NetAdapterDetailsDlgProc( case UPDATE_MSG: NetAdapterUpdateDetails(context); break; + case WM_NOTIFY: + { + PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, PHAPP_IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyListViewEMenuItem(item); + + if (!handled) + { + switch (item->Id) + { + case PHAPP_IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; } return FALSE; @@ -653,18 +733,13 @@ VOID ShowNetAdapterDetailsDialog( _In_ PDV_NETADAPTER_SYSINFO_CONTEXT Context ) { - HANDLE dialogThread = NULL; PDV_NETADAPTER_DETAILS_CONTEXT context; - context = PhAllocate(sizeof(DV_NETADAPTER_DETAILS_CONTEXT)); - memset(context, 0, sizeof(DV_NETADAPTER_DETAILS_CONTEXT)); - + context = PhAllocateZero(sizeof(DV_NETADAPTER_DETAILS_CONTEXT)); context->ParentHandle = Context->WindowHandle; + PhSetReference(&context->AdapterName, Context->AdapterEntry->AdapterName); - CopyNetAdapterId(&context->AdapterId, &Context->AdapterEntry->Id); + CopyNetAdapterId(&context->AdapterId, &Context->AdapterEntry->AdapterId); - if (dialogThread = PhCreateThread(0, ShowNetAdapterDetailsDialogThread, context)) - NtClose(dialogThread); - else - FreeNetAdapterDetailsContext(context); -} \ No newline at end of file + PhCreateThread2(ShowNetAdapterDetailsDialogThread, context); +} diff --git a/plugins/HardwareDevices/netgraph.c b/plugins/HardwareDevices/netgraph.c index 83890c9e8fa7..66f2a167bc7e 100644 --- a/plugins/HardwareDevices/netgraph.c +++ b/plugins/HardwareDevices/netgraph.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2015-2016 dmex + * Copyright (C) 2015-2019 dmex * Copyright (C) 2016 wj32 * * This file is part of Process Hacker. @@ -28,7 +28,7 @@ VOID NetAdapterUpdateGraphs( ) { Context->GraphState.Valid = FALSE; - Context->GraphState.TooltipIndex = -1; + Context->GraphState.TooltipIndex = ULONG_MAX; Graph_MoveGrid(Context->GraphHandle, 1); Graph_Draw(Context->GraphHandle); Graph_UpdateTooltip(Context->GraphHandle); @@ -42,13 +42,22 @@ VOID NetAdapterUpdatePanel( ULONG64 inOctetsValue = 0; ULONG64 outOctetsValue = 0; ULONG64 linkSpeedValue = 0; + ULONG64 interfaceRcvSpeed = 0; + ULONG64 interfaceXmitSpeed = 0; NDIS_MEDIA_CONNECT_STATE mediaState = MediaConnectStateUnknown; HANDLE deviceHandle = NULL; if (PhGetIntegerSetting(SETTING_NAME_ENABLE_NDIS)) { - // Create the handle to the network device - if (NT_SUCCESS(NetworkAdapterCreateHandle(&deviceHandle, Context->AdapterEntry->Id.InterfaceGuid))) + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(Context->AdapterEntry->AdapterId.InterfaceDevice), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { if (!Context->AdapterEntry->CheckedDeviceSupport) { @@ -63,7 +72,7 @@ VOID NetAdapterUpdatePanel( if (!Context->AdapterEntry->DeviceSupported) { - // Device is faulty. Close the handle so we can fallback to GetIfEntry. + // Close the handle and fallback to GetIfEntry. NtClose(deviceHandle); deviceHandle = NULL; } @@ -89,13 +98,9 @@ VOID NetAdapterUpdatePanel( } else { - // Note: The above code fails for some drivers that don't implement statistics (even though statistics are mandatory). - // NDIS handles these two OIDs for all miniport drivers and we can use these for those special cases. - // https://msdn.microsoft.com/en-us/library/ff569443.aspx - inOctetsValue = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_RCV); - // https://msdn.microsoft.com/en-us/library/ff569445.aspx + inOctetsValue = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_RCV); outOctetsValue = NetworkAdapterQueryValue(deviceHandle, OID_GEN_BYTES_XMIT); } @@ -115,7 +120,7 @@ VOID NetAdapterUpdatePanel( { MIB_IF_ROW2 interfaceRow; - if (QueryInterfaceRow(&Context->AdapterEntry->Id, &interfaceRow)) + if (QueryInterfaceRow(&Context->AdapterEntry->AdapterId, &interfaceRow)) { inOctetsValue = interfaceRow.InOctets; outOctetsValue = interfaceRow.OutOctets; @@ -124,30 +129,64 @@ VOID NetAdapterUpdatePanel( } } - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BSENT, PhaFormatSize(outOctetsValue, -1)->Buffer); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BRECEIVED, PhaFormatSize(inOctetsValue, -1)->Buffer); - SetDlgItemText(Context->PanelWindowHandle, IDC_STAT_BTOTAL, PhaFormatSize(inOctetsValue + outOctetsValue, -1)->Buffer); + interfaceRcvSpeed = inOctetsValue - Context->LastInboundValue; + interfaceXmitSpeed = outOctetsValue - Context->LastOutboundValue; + Context->LastInboundValue = inOctetsValue; + Context->LastOutboundValue = outOctetsValue; + + //interfaceRcvUnicastSpeed = interfaceStats.ifHCInUcastOctets - Context->LastDetailsInboundUnicastValue; + //interfaceXmitUnicastSpeed = interfaceStats.ifHCOutUcastOctets - Context->LastDetailsIOutboundUnicastValue; + + if (!Context->HaveFirstSample) + { + interfaceRcvSpeed = 0; + interfaceXmitSpeed = 0; + Context->HaveFirstSample = TRUE; + } + + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_BSENT, PhaFormatSize(outOctetsValue, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_BRECEIVED, PhaFormatSize(inOctetsValue, ULONG_MAX)->Buffer); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_BTOTAL, PhaFormatSize(inOctetsValue + outOctetsValue, ULONG_MAX)->Buffer); if (mediaState == MediaConnectStateConnected) { - SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_STATE, L"Connected"); - SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_SPEED, PhaFormatString(L"%s/s", PhaFormatSize(linkSpeedValue / BITS_IN_ONE_BYTE, -1)->Buffer)->Buffer); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_LINK_STATE, L"Connected"); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_LINK_SPEED, PhaFormatString( + L"%s/s", + PhaFormatSize(linkSpeedValue / BITS_IN_ONE_BYTE, ULONG_MAX)->Buffer + )->Buffer); } else { - SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_STATE, L"Disconnected"); - SetDlgItemText(Context->PanelWindowHandle, IDC_LINK_SPEED, L"N/A"); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_LINK_STATE, L"Disconnected"); + PhSetDialogItemText(Context->PanelWindowHandle, IDC_LINK_SPEED, L"N/A"); } + + PhSetDialogItemText(Context->PanelWindowHandle, IDC_STAT_QUEUELENGTH, PhaFormatString( + L"%s/s", + PhaFormatSize(interfaceRcvSpeed + interfaceXmitSpeed, ULONG_MAX)->Buffer)->Buffer + ); } VOID UpdateNetAdapterDialog( _Inout_ PDV_NETADAPTER_SYSINFO_CONTEXT Context ) { + MIB_IF_ROW2 interfaceRow; + if (Context->AdapterEntry->AdapterName) - SetDlgItemText(Context->WindowHandle, IDC_ADAPTERNAME, Context->AdapterEntry->AdapterName->Buffer); + PhSetDialogItemText(Context->WindowHandle, IDC_ADAPTERNAME, Context->AdapterEntry->AdapterName->Buffer); + else + PhSetDialogItemText(Context->WindowHandle, IDC_ADAPTERNAME, L"Unknown network adapter"); + + if (QueryInterfaceRow(&Context->AdapterEntry->AdapterId, &interfaceRow)) + { + PhSetDialogItemText(Context->WindowHandle, IDC_ADAPTERTEXT, interfaceRow.Alias); + } else - SetDlgItemText(Context->WindowHandle, IDC_ADAPTERNAME, L"Unknown network adapter"); + { + PhSetDialogItemText(Context->WindowHandle, IDC_ADAPTERTEXT, L""); + } NetAdapterUpdateGraphs(Context); NetAdapterUpdatePanel(Context); @@ -166,15 +205,15 @@ INT_PTR CALLBACK NetAdapterPanelDialogProc( { context = (PDV_NETADAPTER_SYSINFO_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_NETADAPTER_SYSINFO_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_NCDESTROY) { - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -210,25 +249,15 @@ INT_PTR CALLBACK NetAdapterDialogProc( if (uMsg == WM_INITDIALOG) { context = (PDV_NETADAPTER_SYSINFO_CONTEXT)lParam; - - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_NETADAPTER_SYSINFO_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { - PhDeleteLayoutManager(&context->LayoutManager); - PhDeleteGraphState(&context->GraphState); - - if (context->GraphHandle) - DestroyWindow(context->GraphHandle); - - if (context->PanelWindowHandle) - DestroyWindow(context->PanelWindowHandle); - - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); } } @@ -241,6 +270,7 @@ INT_PTR CALLBACK NetAdapterDialogProc( { PPH_LAYOUT_ITEM graphItem; PPH_LAYOUT_ITEM panelItem; + MIB_IF_ROW2 interfaceRow; context->WindowHandle = hwndDlg; @@ -251,12 +281,20 @@ INT_PTR CALLBACK NetAdapterDialogProc( graphItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_GRAPH_LAYOUT), NULL, PH_ANCHOR_ALL); panelItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LAYOUT), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - SendMessage(GetDlgItem(hwndDlg, IDC_ADAPTERNAME), WM_SETFONT, (WPARAM)context->SysinfoSection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_ADAPTERTEXT), context->SysinfoSection->Parameters->LargeFont, FALSE); + SetWindowFont(GetDlgItem(hwndDlg, IDC_ADAPTERNAME), context->SysinfoSection->Parameters->MediumFont, FALSE); - if (context->AdapterEntry->AdapterName) - SetDlgItemText(hwndDlg, IDC_ADAPTERNAME, context->AdapterEntry->AdapterName->Buffer); + if (QueryInterfaceRow(&context->AdapterEntry->AdapterId, &interfaceRow)) + { + PhSetDialogItemText(hwndDlg, IDC_ADAPTERTEXT, interfaceRow.Alias); + } else - SetDlgItemText(hwndDlg, IDC_ADAPTERNAME, L"Unknown network adapter"); + { + PhSetDialogItemText(hwndDlg, IDC_ADAPTERTEXT, L""); + } + + //SetWindowFont(GetDlgItem(hwndDlg, IDC_ADAPTERNAME), context->SysinfoSection->Parameters->LargeFont, FALSE); + PhSetDialogItemText(hwndDlg, IDC_ADAPTERNAME, PhGetStringOrDefault(context->AdapterEntry->AdapterName, L"Unknown network adapter")); context->PanelWindowHandle = CreateDialogParam(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_NETADAPTER_PANEL), hwndDlg, NetAdapterPanelDialogProc, (LPARAM)context); ShowWindow(context->PanelWindowHandle, SW_SHOW); @@ -282,6 +320,18 @@ INT_PTR CALLBACK NetAdapterDialogProc( UpdateNetAdapterDialog(context); } break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + PhDeleteGraphState(&context->GraphState); + + if (context->GraphHandle) + DestroyWindow(context->GraphHandle); + + if (context->PanelWindowHandle) + DestroyWindow(context->PanelWindowHandle); + } + break; case WM_SIZE: PhLayoutManagerLayout(&context->LayoutManager); break; @@ -367,8 +417,8 @@ INT_PTR CALLBACK NetAdapterDialogProc( PhMoveReference(&context->GraphState.TooltipText, PhFormatString( L"R: %s\nS: %s\n%s", - PhaFormatSize(adapterInboundValue, -1)->Buffer, - PhaFormatSize(adapterOutboundValue, -1)->Buffer, + PhaFormatSize(adapterInboundValue, ULONG_MAX)->Buffer, + PhaFormatSize(adapterOutboundValue, ULONG_MAX)->Buffer, ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer )); } @@ -490,8 +540,8 @@ BOOLEAN NetAdapterSectionCallback( PhMoveReference(&Section->GraphState.TooltipText, PhFormatString( L"R: %s\nS: %s\n%s", - PhaFormatSize(adapterInboundValue, -1)->Buffer, - PhaFormatSize(adapterOutboundValue, -1)->Buffer, + PhaFormatSize(adapterInboundValue, ULONG_MAX)->Buffer, + PhaFormatSize(adapterOutboundValue, ULONG_MAX)->Buffer, ((PPH_STRING)PH_AUTO(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer )); @@ -505,8 +555,8 @@ BOOLEAN NetAdapterSectionCallback( PhSetReference(&drawPanel->Title, context->AdapterEntry->AdapterName); drawPanel->SubTitle = PhFormatString( L"R: %s\nS: %s", - PhaFormatSize(context->AdapterEntry->InboundValue, -1)->Buffer, - PhaFormatSize(context->AdapterEntry->OutboundValue, -1)->Buffer + PhaFormatSize(context->AdapterEntry->InboundValue, ULONG_MAX)->Buffer, + PhaFormatSize(context->AdapterEntry->OutboundValue, ULONG_MAX)->Buffer ); if (!drawPanel->Title) @@ -526,16 +576,14 @@ VOID NetAdapterSysInfoInitializing( PH_SYSINFO_SECTION section; PDV_NETADAPTER_SYSINFO_CONTEXT context; - context = (PDV_NETADAPTER_SYSINFO_CONTEXT)PhAllocate(sizeof(DV_NETADAPTER_SYSINFO_CONTEXT)); - memset(context, 0, sizeof(DV_NETADAPTER_SYSINFO_CONTEXT)); - memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); - + context = PhAllocateZero(sizeof(DV_NETADAPTER_SYSINFO_CONTEXT)); context->AdapterEntry = AdapterEntry; - context->SectionName = PhConcatStrings2(L"NetAdapter ", AdapterEntry->Id.InterfaceGuid->Buffer); - + context->SectionName = PhConcatStrings2(L"NetAdapter ", PhGetStringOrEmpty(AdapterEntry->AdapterId.InterfaceGuid)); + + memset(§ion, 0, sizeof(PH_SYSINFO_SECTION)); section.Context = context; section.Callback = NetAdapterSectionCallback; - section.Name = context->SectionName->sr; + section.Name = PhGetStringRef(context->SectionName); context->SysinfoSection = Pointers->CreateSection(§ion); -} \ No newline at end of file +} diff --git a/plugins/HardwareDevices/netoptions.c b/plugins/HardwareDevices/netoptions.c index 0807ba9a9e3b..b142ec6484dd 100644 --- a/plugins/HardwareDevices/netoptions.c +++ b/plugins/HardwareDevices/netoptions.c @@ -2,8 +2,8 @@ * Process Hacker Plugins - * Hardware Devices Plugin * - * Copyright (C) 2015-2016 dmex * Copyright (C) 2016 wj32 + * Copyright (C) 2015-2018 dmex * * This file is part of Process Hacker. * @@ -21,13 +21,9 @@ * along with Process Hacker. If not, see . */ -#define INITGUID #include "devices.h" -#include #include - -#define ITEM_CHECKED (INDEXTOSTATEIMAGEMASK(2)) -#define ITEM_UNCHECKED (INDEXTOSTATEIMAGEMASK(1)) +#include typedef struct _NET_ENUM_ENTRY { @@ -35,6 +31,7 @@ typedef struct _NET_ENUM_ENTRY IF_LUID DeviceLuid; PPH_STRING DeviceGuid; PPH_STRING DeviceName; + PPH_STRING DeviceInterface; } NET_ENUM_ENTRY, *PNET_ENUM_ENTRY; static int __cdecl AdapterEntryCompareFunction( @@ -111,9 +108,9 @@ VOID NetAdaptersSaveList( PhAppendFormatStringBuilder( &stringBuilder, L"%lu,%I64u,%s,", - entry->Id.InterfaceIndex, // This value is UNSAFE and may change after reboot. - entry->Id.InterfaceLuid.Value, // This value is SAFE and does not change (Vista+). - entry->Id.InterfaceGuid->Buffer + entry->AdapterId.InterfaceIndex, // This value is UNSAFE and will change after reboot. + entry->AdapterId.InterfaceLuid.Value, // This value is SAFE and does not change (Vista+). + entry->AdapterId.InterfaceGuid->Buffer ); } @@ -145,7 +142,7 @@ BOOLEAN FindAdapterEntry( if (!currentEntry) continue; - found = EquivalentNetAdapterId(¤tEntry->Id, Id); + found = EquivalentNetAdapterId(¤tEntry->AdapterId, Id); if (found) { @@ -187,7 +184,7 @@ VOID AddNetworkAdapterToListView( BOOLEAN found = FALSE; PDV_NETADAPTER_ID newId = NULL; - InitializeNetAdapterId(&adapterId, IfIndex, Luid, NULL); + InitializeNetAdapterId(&adapterId, IfIndex, Luid, Guid); for (ULONG i = 0; i < NetworkAdaptersList->Count; i++) { @@ -196,10 +193,10 @@ VOID AddNetworkAdapterToListView( if (!entry) continue; - if (EquivalentNetAdapterId(&entry->Id, &adapterId)) + if (EquivalentNetAdapterId(&entry->AdapterId, &adapterId)) { newId = PhAllocate(sizeof(DV_NETADAPTER_ID)); - CopyNetAdapterId(newId, &entry->Id); + CopyNetAdapterId(newId, &entry->AdapterId); if (entry->UserReference) found = TRUE; @@ -218,7 +215,7 @@ VOID AddNetworkAdapterToListView( PhMoveReference(&newId->InterfaceGuid, Guid); } - lvItemIndex = AddListViewItemGroupId( + lvItemIndex = PhAddListViewGroupItem( Context->ListViewHandle, AdapterPresent ? 0 : 1, MAXINT, @@ -227,7 +224,7 @@ VOID AddNetworkAdapterToListView( ); if (found) - ListView_SetItemState(Context->ListViewHandle, lvItemIndex, ITEM_CHECKED, LVIS_STATEIMAGEMASK); + ListView_SetCheckState(Context->ListViewHandle, lvItemIndex, TRUE); DeleteNetAdapterId(&adapterId); } @@ -299,7 +296,7 @@ BOOLEAN QueryNetworkDeviceInterfaceDescription( // We use our NetworkAdapterQueryName function to query the full adapter name // from the NDIS driver directly, if that fails then we use one of the above properties. - if ((result = CM_Get_DevNode_Property( // CM_Get_DevNode_Registry_Property with CM_DRP_DEVICEDESC?? + if ((result = CM_Get_DevNode_Property( deviceInstanceHandle, WindowsVersion >= WINDOWS_8 ? &DEVPKEY_Device_FriendlyName : &DEVPKEY_Device_DeviceDesc, &devicePropertyType, @@ -381,6 +378,7 @@ VOID FindNetworkAdapters( } else { + static PH_STRINGREF devicePathSr = PH_STRINGREF_INIT(L"\\\\.\\"); PPH_LIST deviceList; PWSTR deviceInterfaceList; ULONG deviceInterfaceListLength = 0; @@ -438,10 +436,19 @@ VOID FindNetworkAdapters( memset(adapterEntry, 0, sizeof(NET_ENUM_ENTRY)); adapterEntry->DeviceGuid = PhQueryRegistryString(keyHandle, L"NetCfgInstanceId"); - adapterEntry->DeviceLuid.Info.IfType = QueryRegistryUlong64(keyHandle, L"*IfType"); - adapterEntry->DeviceLuid.Info.NetLuidIndex = QueryRegistryUlong64(keyHandle, L"NetLuidIndex"); - - if (NT_SUCCESS(NetworkAdapterCreateHandle(&deviceHandle, adapterEntry->DeviceGuid))) + adapterEntry->DeviceInterface = PhConcatStringRef2(&devicePathSr, &adapterEntry->DeviceGuid->sr); + adapterEntry->DeviceLuid.Info.IfType = PhQueryRegistryUlong64(keyHandle, L"*IfType"); + adapterEntry->DeviceLuid.Info.NetLuidIndex = PhQueryRegistryUlong64(keyHandle, L"NetLuidIndex"); + + if (NT_SUCCESS(PhCreateFileWin32( + &deviceHandle, + PhGetString(adapterEntry->DeviceInterface), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { PPH_STRING adapterName; @@ -467,6 +474,9 @@ VOID FindNetworkAdapters( PhClearReference(&deviceDescription); } + // Cleanup. + PhFree(deviceInterfaceList); + // Sort the entries qsort(deviceList->Items, deviceList->Count, sizeof(PVOID), AdapterEntryCompareFunction); @@ -487,6 +497,8 @@ VOID FindNetworkAdapters( if (entry->DeviceName) PhDereferenceObject(entry->DeviceName); + if (entry->DeviceInterface) + PhDereferenceObject(entry->DeviceInterface); // Note: DeviceGuid is disposed by WM_DESTROY. PhFree(entry); @@ -500,7 +512,7 @@ VOID FindNetworkAdapters( for (ULONG i = 0; i < NetworkAdaptersList->Count; i++) { - ULONG index = -1; + ULONG index = ULONG_MAX; BOOLEAN found = FALSE; PDV_NETADAPTER_ENTRY entry = PhReferenceObjectSafe(NetworkAdaptersList->Items[i]); @@ -511,13 +523,13 @@ VOID FindNetworkAdapters( Context->ListViewHandle, index, LVNI_ALL - )) != -1) + )) != ULONG_MAX) { PDV_NETADAPTER_ID param; if (PhGetListViewItemParam(Context->ListViewHandle, index, ¶m)) { - if (EquivalentNetAdapterId(param, &entry->Id)) + if (EquivalentNetAdapterId(param, &entry->AdapterId)) { found = TRUE; } @@ -527,15 +539,26 @@ VOID FindNetworkAdapters( if (!found) { PPH_STRING description; + MIB_IF_ROW2 interfaceRow; + + memset(&interfaceRow, 0, sizeof(MIB_IF_ROW2)); + interfaceRow.InterfaceLuid = entry->AdapterId.InterfaceLuid; + interfaceRow.InterfaceIndex = entry->AdapterId.InterfaceIndex; + + // HACK: Try query the description from the interface entry (if it exists). + if (GetIfEntry2(&interfaceRow) == NO_ERROR) + description = PhCreateString(interfaceRow.Description); + else + description = PhCreateString(L"Unknown network adapter"); - if (description = PhCreateString(L"Unknown network adapter")) + if (description) { AddNetworkAdapterToListView( Context, FALSE, - entry->Id.InterfaceIndex, - entry->Id.InterfaceLuid, - entry->Id.InterfaceGuid, + entry->AdapterId.InterfaceIndex, + entry->AdapterId.InterfaceLuid, + entry->AdapterId.InterfaceGuid, description ); @@ -641,47 +664,89 @@ PPH_STRING FindNetworkDeviceInstance( } } + PhFree(deviceInterfaceList); + return deviceInstanceString; } -//VOID LoadNetworkAdapterImages( -// _In_ PDV_NETADAPTER_CONTEXT Context -// ) -//{ -// HICON smallIcon = NULL; -// -// Context->ImageList = ImageList_Create( -// GetSystemMetrics(SM_CXSMICON), -// GetSystemMetrics(SM_CYSMICON), -// ILC_COLOR32, -// 1, -// 1 -// ); -// -// // We could use SetupDiLoadClassIcon but this works. -// // Copied from HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\\IconPath -// // The path and index hasn't changed since Win2k. -// ExtractIconEx( -// L"%SystemRoot%\\system32\\setupapi.dll", -// -5, -// NULL, -// &smallIcon, -// 1 -// ); -// -// if (smallIcon) -// { -// ImageList_AddIcon(Context->ImageList, smallIcon); -// DestroyIcon(smallIcon); -// -// // Set the imagelist only if the image was loaded. -// ListView_SetImageList( -// Context->ListViewHandle, -// Context->ImageList, -// LVSIL_SMALL -// ); -// } -//} +VOID LoadNetworkAdapterImages( + _In_ PDV_NETADAPTER_CONTEXT Context + ) +{ + HICON smallIcon; + CONFIGRET result; + ULONG deviceIconPathLength; + DEVPROPTYPE deviceIconPathPropertyType; + PPH_STRING deviceIconPath; + + deviceIconPathLength = 0x40; + deviceIconPath = PhCreateStringEx(NULL, deviceIconPathLength); + + if ((result = CM_Get_Class_Property( + &GUID_DEVCLASS_NET, + &DEVPKEY_DeviceClass_IconPath, + &deviceIconPathPropertyType, + (PBYTE)deviceIconPath->Buffer, + &deviceIconPathLength, + 0 + )) != CR_SUCCESS) + { + PhDereferenceObject(deviceIconPath); + deviceIconPath = PhCreateStringEx(NULL, deviceIconPathLength); + + result = CM_Get_Class_Property( + &GUID_DEVCLASS_NET, + &DEVPKEY_DeviceClass_IconPath, + &deviceIconPathPropertyType, + (PBYTE)deviceIconPath->Buffer, + &deviceIconPathLength, + 0 + ); + } + + if (result != CR_SUCCESS) + { + PhDereferenceObject(deviceIconPath); + return; + } + + PhTrimToNullTerminatorString(deviceIconPath); + + { + PPH_STRING dllIconPath; + PH_STRINGREF dllPartSr; + PH_STRINGREF indexPartSr; + ULONG64 index = 0; + + if ( + PhSplitStringRefAtChar(&deviceIconPath->sr, ',', &dllPartSr, &indexPartSr) && + PhStringToInteger64(&indexPartSr, 10, &index) + ) + { + if (dllIconPath = PhExpandEnvironmentStrings(&dllPartSr)) + { + if (PhExtractIconEx(dllIconPath->Buffer, (INT)index, &smallIcon, NULL)) + { + Context->ImageList = ImageList_Create( + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), + ILC_COLOR32, + 1, + 1 + ); + + ImageList_AddIcon(Context->ImageList, smallIcon); + ListView_SetImageList(Context->ListViewHandle, Context->ImageList, LVSIL_SMALL); + DestroyIcon(smallIcon); + } + + PhDereferenceObject(dllIconPath); + } + } + } + + PhDereferenceObject(deviceIconPath); +} INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( _In_ HWND hwndDlg, @@ -694,23 +759,24 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( if (uMsg == WM_INITDIALOG) { - context = (PDV_NETADAPTER_CONTEXT)PhAllocate(sizeof(DV_NETADAPTER_CONTEXT)); - memset(context, 0, sizeof(DV_NETADAPTER_CONTEXT)); + context = PhAllocateZero(sizeof(DV_NETADAPTER_CONTEXT)); - SetProp(hwndDlg, L"Context", (HANDLE)context); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); } else { - context = (PDV_NETADAPTER_CONTEXT)GetProp(hwndDlg, L"Context"); + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); if (uMsg == WM_DESTROY) { + PhDeleteLayoutManager(&context->LayoutManager); + if (context->OptionsChanged) NetAdaptersSaveList(); FreeListViewAdapterEntries(context); - RemoveProp(hwndDlg, L"Context"); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); PhFree(context); } } @@ -728,16 +794,28 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( PhSetControlTheme(context->ListViewHandle, L"explorer"); PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 350, L"Network Adapters"); PhSetExtendedListView(context->ListViewHandle); + LoadNetworkAdapterImages(context); ListView_EnableGroupView(context->ListViewHandle, TRUE); - AddListViewGroup(context->ListViewHandle, 0, L"Connected"); - AddListViewGroup(context->ListViewHandle, 1, L"Disconnected"); + PhAddListViewGroup(context->ListViewHandle, 0, L"Connected"); + PhAddListViewGroup(context->ListViewHandle, 1, L"Disconnected"); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->ListViewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SHOW_HIDDEN_ADAPTERS), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); FindNetworkAdapters(context); context->OptionsChanged = FALSE; } break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); + } + break; case WM_COMMAND: { switch (GET_WM_COMMAND_ID(wParam, lParam)) @@ -759,6 +837,8 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( ListView_DeleteAllItems(context->ListViewHandle); FindNetworkAdapters(context); + + ExtendedListView_SetColumnWidth(context->ListViewHandle, 0, ELVSCW_AUTOSIZE_REMAININGSPACE); } break; } @@ -779,7 +859,7 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( { switch (listView->uNewState & LVIS_STATEIMAGEMASK) { - case 0x2000: // checked + case INDEXTOSTATEIMAGEMASK(2): // checked { PDV_NETADAPTER_ID param = (PDV_NETADAPTER_ID)listView->lParam; @@ -794,7 +874,7 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( context->OptionsChanged = TRUE; } break; - case 0x1000: // unchecked + case INDEXTOSTATEIMAGEMASK(1): // unchecked { PDV_NETADAPTER_ID param = (PDV_NETADAPTER_ID)listView->lParam; @@ -813,7 +893,7 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( if (param = PhGetSelectedListViewItemParam(context->ListViewHandle)) { - if (deviceInstance = FindNetworkDeviceInstance(param->Id.InterfaceGuid)) + if (deviceInstance = FindNetworkDeviceInstance(param->AdapterId.InterfaceGuid)) { ShowDeviceMenu(hwndDlg, deviceInstance); PhDereferenceObject(deviceInstance); @@ -825,4 +905,4 @@ INT_PTR CALLBACK NetworkAdapterOptionsDlgProc( } return FALSE; -} \ No newline at end of file +} diff --git a/plugins/HardwareDevices/prpsh.c b/plugins/HardwareDevices/prpsh.c index 517f1aa9961c..5d27248dcb04 100644 --- a/plugins/HardwareDevices/prpsh.c +++ b/plugins/HardwareDevices/prpsh.c @@ -47,9 +47,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ); INT CALLBACK PvpStandardPropPageProc( @@ -124,7 +122,7 @@ INT CALLBACK PvpPropSheetProc( { if (lParam) { - if (((DLGTEMPLATEEX *)lParam)->signature == 0xffff) + if (((DLGTEMPLATEEX *)lParam)->signature == USHRT_MAX) { ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; } @@ -137,15 +135,16 @@ INT CALLBACK PvpPropSheetProc( break; case PSCB_INITIALIZED: { - PPV_PROPSHEETCONTEXT propSheetContext; + PPV_PROPSHEETCONTEXT context; - propSheetContext = PhAllocate(sizeof(PV_PROPSHEETCONTEXT)); - memset(propSheetContext, 0, sizeof(PV_PROPSHEETCONTEXT)); + context = PhAllocate(sizeof(PV_PROPSHEETCONTEXT)); + memset(context, 0, sizeof(PV_PROPSHEETCONTEXT)); - PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - SetProp(hwndDlg, L"HdContext", (HANDLE)propSheetContext); - SetWindowSubclass(hwndDlg, PvpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, ULONG_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PvpPropSheetWndProc); if (MinimumSize.left == -1) { @@ -170,35 +169,40 @@ PPV_PROPSHEETCONTEXT PvpGetPropSheetContext( _In_ HWND hwnd ) { - return (PPV_PROPSHEETCONTEXT)GetProp(hwnd, L"HdContext"); + return PhGetWindowContext(hwnd, ULONG_MAX); } LRESULT CALLBACK PvpPropSheetWndProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { - PPV_PROPSHEETCONTEXT propSheetContext = (PPV_PROPSHEETCONTEXT)dwRefData; + PPV_PROPSHEETCONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hWnd, ULONG_MAX))) + return 0; + + oldWndProc = context->DefaultWindowProc; switch (uMsg) { - case WM_NCDESTROY: + case WM_DESTROY: { - RemoveWindowSubclass(hWnd, PvpPropSheetWndProc, uIdSubclass); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, ULONG_MAX); PhSaveWindowPlacementToSetting(SETTING_NAME_DISK_POSITION, SETTING_NAME_DISK_SIZE, hWnd); - PhDeleteLayoutManager(&propSheetContext->LayoutManager); + PhDeleteLayoutManager(&context->LayoutManager); - PhFree(propSheetContext); + PhFree(context); } break; case WM_COMMAND: { - switch (LOWORD(wParam)) + switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: // Prevent the OK button from working (even though @@ -212,7 +216,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( { if (!IsIconic(hWnd)) { - PhLayoutManagerLayout(&propSheetContext->LayoutManager); + PhLayoutManagerLayout(&context->LayoutManager); } } break; @@ -223,40 +227,39 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( + _In_ PPV_PROPSHEETCONTEXT PropSheetContext, _In_ HWND hwnd ) { - PPV_PROPSHEETCONTEXT propSheetContext = PvpGetPropSheetContext(hwnd); - - if (!propSheetContext->LayoutInitialized) + if (!PropSheetContext->LayoutInitialized) { HWND tabControlHandle; PPH_LAYOUT_ITEM tabControlItem; PPH_LAYOUT_ITEM tabPageItem; tabControlHandle = PropSheet_GetTabControl(hwnd); - tabControlItem = PhAddLayoutItem(&propSheetContext->LayoutManager, tabControlHandle, + tabControlItem = PhAddLayoutItem(&PropSheetContext->LayoutManager, tabControlHandle, NULL, PH_ANCHOR_ALL | PH_LAYOUT_IMMEDIATE_RESIZE); - tabPageItem = PhAddLayoutItem(&propSheetContext->LayoutManager, tabControlHandle, + tabPageItem = PhAddLayoutItem(&PropSheetContext->LayoutManager, tabControlHandle, NULL, PH_LAYOUT_TAB_CONTROL); // dummy item to fix multiline tab control - propSheetContext->TabPageItem = tabPageItem; + PropSheetContext->TabPageItem = tabPageItem; - PhAddLayoutItem(&propSheetContext->LayoutManager, GetDlgItem(hwnd, IDCANCEL), + PhAddLayoutItem(&PropSheetContext->LayoutManager, GetDlgItem(hwnd, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); // Hide the OK button. ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE); // Set the Cancel button's text to "Close". - SetDlgItemText(hwnd, IDCANCEL, L"Close"); + PhSetDialogItemText(hwnd, IDCANCEL, L"Close"); PhLoadWindowPlacementFromSetting(SETTING_NAME_DISK_POSITION, SETTING_NAME_DISK_SIZE, hwnd); - propSheetContext->LayoutInitialized = TRUE; + PropSheetContext->LayoutInitialized = TRUE; return TRUE; } @@ -385,7 +388,7 @@ PPH_LAYOUT_ITEM PvAddPropPageLayoutItem( propSheetContext = PvpGetPropSheetContext(parent); layoutManager = &propSheetContext->LayoutManager; - PhpInitializePropSheetLayoutStage1(parent); + PhpInitializePropSheetLayoutStage1(propSheetContext, parent); if (ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT) realParentItem = ParentItem; diff --git a/plugins/HardwareDevices/prpsh.h b/plugins/HardwareDevices/prpsh.h index d6417da4f404..f3f0e6d3e7b5 100644 --- a/plugins/HardwareDevices/prpsh.h +++ b/plugins/HardwareDevices/prpsh.h @@ -29,9 +29,10 @@ typedef struct _PV_PROPSHEETCONTEXT { + BOOLEAN LayoutInitialized; + WNDPROC DefaultWindowProc; PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM TabPageItem; - BOOLEAN LayoutInitialized; } PV_PROPSHEETCONTEXT, *PPV_PROPSHEETCONTEXT; typedef struct _PV_PROPCONTEXT @@ -114,10 +115,10 @@ FORCEINLINE BOOLEAN PvPropPageDlgProcHeader( if (uMsg == WM_INITDIALOG) { // Save the context. - SetProp(hwndDlg, L"HdContext", (HANDLE)lParam); + PhSetWindowContext(hwndDlg, ULONG_MAX, (HANDLE)lParam); } - propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, L"HdContext"); + propSheetPage = PhGetWindowContext(hwndDlg, ULONG_MAX); if (!propSheetPage) return FALSE; diff --git a/plugins/HardwareDevices/resource.h b/plugins/HardwareDevices/resource.h index bf9845766341..1abb9e692752 100644 --- a/plugins/HardwareDevices/resource.h +++ b/plugins/HardwareDevices/resource.h @@ -3,14 +3,12 @@ // Used by HardwareDevices.rc // #define IDD_NETADAPTER_OPTIONS 101 -#define IDD_NETADAPTER_DIALOG 102 #define IDC_GRAPH_LAYOUT 103 #define IDD_NETADAPTER_PANEL 104 #define IDD_NETADAPTER_DETAILS 105 #define IDD_DISKDRIVE_DETAILS_FILESYSTEM 109 #define IDC_NETADAPTERS_LISTVIEW 1001 #define IDC_LINK_SPEED 1002 -#define IDC_ADAPTERNAME 1003 #define IDC_LAYOUT 1004 #define IDC_LINK_STATE 1005 #define IDC_STAT_BSENT 1006 @@ -25,8 +23,11 @@ #define IDD_DISKDRIVE_DETAILS_SMART 1017 #define IDC_DISKDRIVE_LISTVIEW 1017 #define IDC_DISKMOUNTPATH 1018 +#define IDD_NETADAPTER_DIALOG 1018 #define IDC_STAT_BREAD 1020 #define IDC_STAT_BWRITE 1021 +#define IDC_ADAPTERNAME 1021 +#define IDC_ADAPTERTEXT 1022 #define IDC_STAT_ACTIVE 1023 #define IDC_STAT_RESPONSETIME 1024 #define IDC_STAT_QUEUELENGTH 1025 @@ -36,9 +37,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 114 +#define _APS_NEXT_RESOURCE_VALUE 117 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1021 +#define _APS_NEXT_CONTROL_VALUE 1023 #define _APS_NEXT_SYMED_VALUE 106 #endif #endif diff --git a/plugins/HardwareDevices/storage.c b/plugins/HardwareDevices/storage.c index 2be01224b59c..d862cdedc192 100644 --- a/plugins/HardwareDevices/storage.c +++ b/plugins/HardwareDevices/storage.c @@ -23,22 +23,6 @@ #include "devices.h" #include -NTSTATUS DiskDriveCreateHandle( - _Out_ PHANDLE DeviceHandle, - _In_ PPH_STRING DevicePath - ) -{ - return PhCreateFileWin32( - DeviceHandle, - DevicePath->Buffer, - FILE_READ_ATTRIBUTES | SYNCHRONIZE, - FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT // FILE_RANDOM_ACCESS - ); -} - ULONG DiskDriveQueryDeviceMap( VOID ) @@ -510,7 +494,7 @@ BOOLEAN DiskDriveQueryDeviceInformation( { PPH_STRING diskVendor; - diskVendor = PH_AUTO(PhConvertMultiByteToUtf16((PBYTE)deviceDescriptor + deviceDescriptor->VendorIdOffset)); + diskVendor = PH_AUTO(PhConvertMultiByteToUtf16(PTR_ADD_OFFSET(deviceDescriptor, deviceDescriptor->VendorIdOffset))); *DiskVendor = TrimString(diskVendor); } @@ -519,7 +503,7 @@ BOOLEAN DiskDriveQueryDeviceInformation( { PPH_STRING diskModel; - diskModel = PH_AUTO(PhConvertMultiByteToUtf16((PBYTE)deviceDescriptor + deviceDescriptor->ProductIdOffset)); + diskModel = PH_AUTO(PhConvertMultiByteToUtf16(PTR_ADD_OFFSET(deviceDescriptor, deviceDescriptor->ProductIdOffset))); *DiskModel = TrimString(diskModel); } @@ -528,7 +512,7 @@ BOOLEAN DiskDriveQueryDeviceInformation( { PPH_STRING diskRevision; - diskRevision = PH_AUTO(PhConvertMultiByteToUtf16((PBYTE)deviceDescriptor + deviceDescriptor->ProductRevisionOffset)); + diskRevision = PH_AUTO(PhConvertMultiByteToUtf16(PTR_ADD_OFFSET(deviceDescriptor, deviceDescriptor->ProductRevisionOffset))); *DiskRevision = TrimString(diskRevision); } @@ -537,7 +521,7 @@ BOOLEAN DiskDriveQueryDeviceInformation( { PPH_STRING diskSerial; - diskSerial = PH_AUTO(PhConvertMultiByteToUtf16((PBYTE)deviceDescriptor + deviceDescriptor->SerialNumberOffset)); + diskSerial = PH_AUTO(PhConvertMultiByteToUtf16(PTR_ADD_OFFSET(deviceDescriptor, deviceDescriptor->SerialNumberOffset))); *DiskSerial = TrimString(diskSerial); } @@ -649,7 +633,7 @@ PPH_STRING DiskDriveQueryGeometry( ))) { // TODO: This doesn't return total capacity like Task Manager. - return PhFormatSize(result.Cylinders.QuadPart * result.TracksPerCylinder * result.SectorsPerTrack * result.BytesPerSector, -1); + return PhFormatSize(result.Cylinders.QuadPart * result.TracksPerCylinder * result.SectorsPerTrack * result.BytesPerSector, ULONG_MAX); } return PhReferenceEmptyString(); @@ -714,7 +698,7 @@ NTSTATUS DiskDriveQueryImminentFailure( for (UCHAR i = 0; i < 30; ++i) { - PSMART_ATTRIBUTE attribute = (PSMART_ATTRIBUTE)(storagePredictFailure.VendorSpecific + i * sizeof(SMART_ATTRIBUTE) + SMART_HEADER_SIZE); + PSMART_ATTRIBUTE attribute = (PSMART_ATTRIBUTE)PTR_ADD_OFFSET(storagePredictFailure.VendorSpecific, i * sizeof(SMART_ATTRIBUTE) + SMART_HEADER_SIZE); // Attribute values 0x00, 0xFE, 0xFF are invalid. // There is no requirement that attributes be in any particular order. @@ -724,9 +708,9 @@ NTSTATUS DiskDriveQueryImminentFailure( attribute->Id != 0xFF ) { - PSMART_ATTRIBUTES info = PhAllocate(sizeof(SMART_ATTRIBUTES)); - memset(info, 0, sizeof(SMART_ATTRIBUTES)); + PSMART_ATTRIBUTES info; + info = PhAllocateZero(sizeof(SMART_ATTRIBUTES)); info->AttributeId = attribute->Id; info->CurrentValue = attribute->CurrentValue; info->WorstValue = attribute->WorstValue; @@ -1499,7 +1483,29 @@ PWSTR SmartAttributeGetText( return L"Read Error Retry Rate"; case SMART_ATTRIBUTE_ID_FREE_FALL_PROTECTION: return L"Free Fall Protection"; + case SMART_ATTRIBUTE_ID_SSD_PROGRAM_FAIL_COUNT: + return L"SSD Program Fail Count"; + case SMART_ATTRIBUTE_ID_SSD_ERASE_FAIL_COUNT: + return L"SSD Erase Fail Count"; + case SMART_ATTRIBUTE_ID_SSD_WEAR_LEVELING_COUNT: + return L"SSD Wear Leveling Count"; + case SMART_ATTRIBUTE_ID_UNEXPECTED_POWER_LOSS: + return L"Unexpected power loss count"; + case SMART_ATTRIBUTE_ID_WEAR_RANGE_DELTA: + return L"Wear Range Delta"; + case SMART_ATTRIBUTE_ID_SSD_PROGRAM_FAIL_COUNT_TOTAL: + return L"Program Fail Count Total"; + case SMART_ATTRIBUTE_ID_ERASE_FAIL_COUNT: + return L"Erase Fail Count"; + case SMART_ATTRIBUTE_ID_SSD_MEDIA_WEAROUT_HOURS: + return L"Media Wearout Indicator"; + case SMART_ATTRIBUTE_ID_SSD_ERASE_COUNT: + return L"Erase count"; + case SMART_ATTRIBUTE_ID_MIN_SPARES_REMAINING: + return L"Minimum Spares Remaining"; + case SMART_ATTRIBUTE_ID_NEWLY_ADDED_BAD_FLASH_BLOCK: + return L"Newly Added Bad Flash Block"; } - return L"Unknown"; -} \ No newline at end of file + return L"BUG BUG BUG"; +} diff --git a/plugins/NetworkTools/CHANGELOG.txt b/plugins/NetworkTools/CHANGELOG.txt index 2cc9983b5553..ce778d88aafb 100644 --- a/plugins/NetworkTools/CHANGELOG.txt +++ b/plugins/NetworkTools/CHANGELOG.txt @@ -1,32 +1,33 @@ -1.8 - * Added custom tracert and whois support - * Added geoip lookup for country name and image - -1.7 - * Added option to specify ping buffer length - * Removed legacy code - -1.6 - * Added PathPing support - -1.5 - * Improved ping graph scaling - -1.4 - * Fixed whois scrolling issue - * Fixed ping threading issues - * Fixed various memory leaks - -1.3 - * Added plugin options for ping/tracert/whois - * Added native ping/tracert/whois support - * Updated UI - -1.2 - * Fixed encoding bug - -1.1 - * Fixed handle leak - -1.0 - * Initial release +1.8 + * Added custom tracert and whois support + * Added geoip lookup for country name and image + * Added Tools menu > Network Tools > options + +1.7 + * Added option to specify ping buffer length + * Removed legacy code + +1.6 + * Added PathPing support + +1.5 + * Improved ping graph scaling + +1.4 + * Fixed whois scrolling issue + * Fixed ping threading issues + * Fixed various memory leaks + +1.3 + * Added plugin options for ping/tracert/whois + * Added native ping/tracert/whois support + * Updated UI + +1.2 + * Fixed encoding bug + +1.1 + * Fixed handle leak + +1.0 + * Initial release diff --git a/plugins/NetworkTools/NetworkTools.rc b/plugins/NetworkTools/NetworkTools.rc index 5916e23269f9..af7943c956f7 100644 --- a/plugins/NetworkTools/NetworkTools.rc +++ b/plugins/NetworkTools/NetworkTools.rc @@ -1,723 +1,702 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,8,0,0 - PRODUCTVERSION 1,8,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "dmex" - VALUE "FileDescription", "Network Tools plugin for Process Hacker" - VALUE "FileVersion", "1.8" - VALUE "InternalName", "ProcessHacker.NetworkTools" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "NetworkTools.dll" - VALUE "ProductName", "Network Tools plugin for Process Hacker" - VALUE "ProductVersion", "1.8" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_WHOIS DIALOGEX 0, 0, 389, 214 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Network Tools" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_NETOUTPUTEDIT,"RICHEDIT50W",WS_VSCROLL | WS_TABSTOP | 0x4,2,2,385,210 -END - -IDD_OPTIONS DIALOGEX 0, 0, 201, 57 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Ping packet length:",IDC_STATIC,9,7,62,8 - EDITTEXT IDC_PINGPACKETLENGTH,7,17,89,14,ES_AUTOHSCROLL | ES_NUMBER - PUSHBUTTON "Close",IDCANCEL,135,36,60,14 - EDITTEXT IDC_MAXHOPS,105,17,89,14,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "Max Tracert Hops:",IDC_STATIC,105,7,60,8 - PUSHBUTTON "Manage GeoIP Database",IDC_GEOIP,6,36,90,14 -END - -IDD_PING DIALOGEX 0, 0, 329, 151 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Dialog" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Pinging X with X bytes of data:",IDC_MAINTEXT,7,7,315,16 - LTEXT "PingGraphLayout",IDC_PING_LAYOUT,7,26,315,64,NOT WS_VISIBLE | WS_BORDER - GROUPBOX "Ping statistics",IDC_ICMP_PANEL,7,92,315,52,0,WS_EX_TRANSPARENT - LTEXT "Average: 0ms",IDC_ICMP_AVG,13,104,73,8 - LTEXT "Pings sent: 0",IDC_PINGS_SENT,91,104,88,8 - LTEXT "Bad replies: 0",IDC_BAD_HASH,187,104,90,8 - LTEXT "Minimum: 0ms",IDC_ICMP_MIN,13,116,73,8 - LTEXT "Pings lost: 0 (0%)",IDC_PINGS_LOST,91,116,88,8 - LTEXT "Anon replies: 0",IDC_ANON_ADDR,188,116,87,8 - LTEXT "Maximum: 0ms",IDC_ICMP_MAX,13,129,73,8 -END - -IDD_TRACERT DIALOGEX 0, 0, 389, 212 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "Dialog" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Tracing route to xyz...",IDC_STATUS,7,5,375,12 - DEFPUSHBUTTON "Close",IDCANCEL,333,191,50,14 - CONTROL "",IDC_LIST_TRACERT,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,7,22,375,166,WS_EX_CLIENTEDGE -END - - -///////////////////////////////////////////////////////////////////////////// -// -// PNG -// - -AD_PNG PNG "resources\\ad.png" - -AE_PNG PNG "resources\\ae.png" - -AF_PNG PNG "resources\\af.png" - -AG_PNG PNG "resources\\ag.png" - -AI_PNG PNG "resources\\ai.png" - -AL_PNG PNG "resources\\al.png" - -AM_PNG PNG "resources\\am.png" - -AN_PNG PNG "resources\\an.png" - -AO_PNG PNG "resources\\ao.png" - -AR_PNG PNG "resources\\ar.png" - -AS_PNG PNG "resources\\as.png" - -AT_PNG PNG "resources\\at.png" - -AU_PNG PNG "resources\\au.png" - -AW_PNG PNG "resources\\aw.png" - -AX_PNG PNG "resources\\ax.png" - -AZ_PNG PNG "resources\\az.png" - -BA_PNG PNG "resources\\ba.png" - -BB_PNG PNG "resources\\bb.png" - -BD_PNG PNG "resources\\bd.png" - -BE_PNG PNG "resources\\be.png" - -BF_PNG PNG "resources\\bf.png" - -BG_PNG PNG "resources\\bg.png" - -BH_PNG PNG "resources\\bh.png" - -BI__PNG PNG "resources\\bi.png" - -BJ_PNG PNG "resources\\bj.png" - -BM_PNG PNG "resources\\bm.png" - -BN_PNG PNG "resources\\bn.png" - -BO_PNG PNG "resources\\bo.png" - -BR_PNG PNG "resources\\br.png" - -BS_PNG PNG "resources\\bs.png" - -BT_PNG PNG "resources\\bt.png" - -BV_PNG PNG "resources\\bv.png" - -BW_PNG PNG "resources\\bw.png" - -BY_PNG PNG "resources\\by.png" - -BZ_PNG PNG "resources\\bz.png" - -CA_PNG PNG "resources\\ca.png" - -AAC_PNG PNG "resources\\catalonia.png" - -CC_PNG PNG "resources\\cc.png" - -CD_PNG PNG "resources\\cd.png" - -CF_PNG PNG "resources\\cf.png" - -CG_PNG PNG "resources\\cg.png" - -CH_PNG PNG "resources\\ch.png" - -CI_PNG PNG "resources\\ci.png" - -CK_PNG PNG "resources\\ck.png" - -CL_PNG PNG "resources\\cl.png" - -CM_PNG PNG "resources\\cm.png" - -CN_PNG PNG "resources\\cn.png" - -CO_PNG PNG "resources\\co.png" - -CR_PNG PNG "resources\\cr.png" - -CS_PNG PNG "resources\\cs.png" - -CU_PNG PNG "resources\\cu.png" - -CV_PNG PNG "resources\\cv.png" - -CX_PNG PNG "resources\\cx.png" - -CY_PNG PNG "resources\\cy.png" - -CZ_PNG PNG "resources\\cz.png" - -DE_PNG PNG "resources\\de.png" - -DJ_PNG PNG "resources\\dj.png" - -DK_PNG PNG "resources\\dk.png" - -DM_PNG PNG "resources\\dm.png" - -DO_PNG PNG "resources\\do.png" - -DZ_PNG PNG "resources\\dz.png" - -EC_PNG PNG "resources\\ec.png" - -EE_PNG PNG "resources\\ee.png" - -EG_PNG PNG "resources\\eg.png" - -EH_PNG PNG "resources\\eh.png" - -AAD_PNG PNG "resources\\england.png" - -ER_PNG PNG "resources\\er.png" - -ES_PNG PNG "resources\\es.png" - -ET_PNG PNG "resources\\et.png" - -AAE_PNG PNG "resources\\europeanunion.png" - -AAF_PNG PNG "resources\\fam.png" - -FI_PNG PNG "resources\\fi.png" - -FJ_PNG PNG "resources\\fj.png" - -FK_PNG PNG "resources\\fk.png" - -FM_PNG PNG "resources\\fm.png" - -FO_PNG PNG "resources\\fo.png" - -FR_PNG PNG "resources\\fr.png" - -GA_PNG PNG "resources\\ga.png" - -GB_PNG PNG "resources\\gb.png" - -GD_PNG PNG "resources\\gd.png" - -GE_PNG PNG "resources\\ge.png" - -GF_PNG PNG "resources\\gf.png" - -GH_PNG PNG "resources\\gh.png" - -GI_PNG PNG "resources\\gi.png" - -GL_PNG PNG "resources\\gl.png" - -GM_PNG PNG "resources\\gm.png" - -GN_PNG PNG "resources\\gn.png" - -GP_PNG PNG "resources\\gp.png" - -GQ_PNG PNG "resources\\gq.png" - -GR_PNG PNG "resources\\gr.png" - -GS_PNG PNG "resources\\gs.png" - -GT_PNG PNG "resources\\gt.png" - -GU_PNG PNG "resources\\gu.png" - -GW_PNG PNG "resources\\gw.png" - -GY_PNG PNG "resources\\gy.png" - -HK_PNG PNG "resources\\hk.png" - -HM_PNG PNG "resources\\hm.png" - -HN_PNG PNG "resources\\hn.png" - -HR_PNG PNG "resources\\hr.png" - -HT_PNG PNG "resources\\ht.png" - -HU_PNG PNG "resources\\hu.png" - -ID_PNG PNG "resources\\id.png" - -IE_PNG PNG "resources\\ie.png" - -IL_PNG PNG "resources\\il.png" - -IN_PNG PNG "resources\\in.png" - -IO_PNG PNG "resources\\io.png" - -IQ_PNG PNG "resources\\iq.png" - -IR_PNG PNG "resources\\ir.png" - -IS_PNG PNG "resources\\is.png" - -IT_PNG PNG "resources\\it.png" - -JM_PNG PNG "resources\\jm.png" - -JO_PNG PNG "resources\\jo.png" - -JP_PNG PNG "resources\\jp.png" - -KE_PNG PNG "resources\\ke.png" - -KG_PNG PNG "resources\\kg.png" - -KH_PNG PNG "resources\\kh.png" - -KI_PNG PNG "resources\\ki.png" - -KM_PNG PNG "resources\\km.png" - -KN_PNG PNG "resources\\kn.png" - -KP_PNG PNG "resources\\kp.png" - -KR_PNG PNG "resources\\kr.png" - -KW_PNG PNG "resources\\kw.png" - -KY_PNG PNG "resources\\ky.png" - -KZ_PNG PNG "resources\\kz.png" - -LA_PNG PNG "resources\\la.png" - -LB_PNG PNG "resources\\lb.png" - -LC_PNG PNG "resources\\lc.png" - -LI_PNG PNG "resources\\li.png" - -LK_PNG PNG "resources\\lk.png" - -LR_PNG PNG "resources\\lr.png" - -LS_PNG PNG "resources\\ls.png" - -LT_PNG PNG "resources\\lt.png" - -LU_PNG PNG "resources\\lu.png" - -LV_PNG PNG "resources\\lv.png" - -LY_PNG PNG "resources\\ly.png" - -MA_PNG PNG "resources\\ma.png" - -MC_PNG PNG "resources\\mc.png" - -MD_PNG PNG "resources\\md.png" - -ME_PNG PNG "resources\\me.png" - -MG_PNG PNG "resources\\mg.png" - -MH_PNG PNG "resources\\mh.png" - -MK_PNG PNG "resources\\mk.png" - -ML_PNG PNG "resources\\ml.png" - -MM_PNG PNG "resources\\mm.png" - -MN_PNG PNG "resources\\mn.png" - -MO_PNG PNG "resources\\mo.png" - -MP_PNG PNG "resources\\mp.png" - -MQ_PNG PNG "resources\\mq.png" - -MR_PNG PNG "resources\\mr.png" - -MS_PNG PNG "resources\\ms.png" - -MT_PNG PNG "resources\\mt.png" - -MU_PNG PNG "resources\\mu.png" - -MV_PNG PNG "resources\\mv.png" - -MW_PNG PNG "resources\\mw.png" - -MX_PNG PNG "resources\\mx.png" - -MY_PNG PNG "resources\\my.png" - -MZ_PNG PNG "resources\\mz.png" - -NA_PNG PNG "resources\\na.png" - -NC_PNG PNG "resources\\nc.png" - -NE_PNG PNG "resources\\ne.png" - -NF_PNG PNG "resources\\nf.png" - -NG_PNG PNG "resources\\ng.png" - -NI_PNG PNG "resources\\ni.png" - -NL_PNG PNG "resources\\nl.png" - -NO_PNG PNG "resources\\no.png" - -NP_PNG PNG "resources\\np.png" - -NR_PNG PNG "resources\\nr.png" - -NU_PNG PNG "resources\\nu.png" - -NZ_PNG PNG "resources\\nz.png" - -OM_PNG PNG "resources\\om.png" - -PA_PNG PNG "resources\\pa.png" - -PE_PNG PNG "resources\\pe.png" - -PF_PNG PNG "resources\\pf.png" - -PG_PNG PNG "resources\\pg.png" - -PH_PNG PNG "resources\\ph.png" - -PK_PNG PNG "resources\\pk.png" - -PL_PNG PNG "resources\\pl.png" - -PM_PNG PNG "resources\\pm.png" - -PN_PNG PNG "resources\\pn.png" - -PR_PNG PNG "resources\\pr.png" - -PS_PNG PNG "resources\\ps.png" - -PT_PNG PNG "resources\\pt.png" - -PW_PNG PNG "resources\\pw.png" - -PY_PNG PNG "resources\\py.png" - -QA_PNG PNG "resources\\qa.png" - -RE_PNG PNG "resources\\re.png" - -RO_PNG PNG "resources\\ro.png" - -RS_PNG PNG "resources\\rs.png" - -RU_PNG PNG "resources\\ru.png" - -RW_PNG PNG "resources\\rw.png" - -SA_PNG PNG "resources\\sa.png" - -SB_PNG PNG "resources\\sb.png" - -SC_PNG PNG "resources\\sc.png" - -AAA_PNG PNG "resources\\scotland.png" - -SD_PNG PNG "resources\\sd.png" - -SE_PNG PNG "resources\\se.png" - -SG_PNG PNG "resources\\sg.png" - -SH_PNG PNG "resources\\sh.png" - -SI_PNG PNG "resources\\si.png" - -SJ_PNG PNG "resources\\sj.png" - -SK_PNG PNG "resources\\sk.png" - -SL_PNG PNG "resources\\sl.png" - -SM_PNG PNG "resources\\sm.png" - -SN_PNG PNG "resources\\sn.png" - -SO_PNG PNG "resources\\so.png" - -SR_PNG PNG "resources\\sr.png" - -ST_PNG PNG "resources\\st.png" - -SV_PNG PNG "resources\\sv.png" - -SY_PNG PNG "resources\\sy.png" - -SZ_PNG PNG "resources\\sz.png" - -TC_PNG PNG "resources\\tc.png" - -TD_PNG PNG "resources\\td.png" - -TF_PNG PNG "resources\\tf.png" - -TG_PNG PNG "resources\\tg.png" - -TH_PNG PNG "resources\\th.png" - -TJ_PNG PNG "resources\\tj.png" - -TK_PNG PNG "resources\\tk.png" - -TL_PNG PNG "resources\\tl.png" - -TM_PNG PNG "resources\\tm.png" - -TN_PNG PNG "resources\\tn.png" - -TO_PNG PNG "resources\\to.png" - -TR_PNG PNG "resources\\tr.png" - -TT_PNG PNG "resources\\tt.png" - -TV_PNG PNG "resources\\tv.png" - -TW_PNG PNG "resources\\tw.png" - -TZ_PNG PNG "resources\\tz.png" - -UA_PNG PNG "resources\\ua.png" - -UG_PNG PNG "resources\\ug.png" - -UM_PNG PNG "resources\\um.png" - -US_PNG PNG "resources\\us.png" - -UY_PNG PNG "resources\\uy.png" - -UZ_PNG PNG "resources\\uz.png" - -VA_PNG PNG "resources\\va.png" - -VC_PNG PNG "resources\\vc.png" - -VE_PNG PNG "resources\\ve.png" - -VG_PNG PNG "resources\\vg.png" - -VI_PNG PNG "resources\\vi.png" - -VN_PNG PNG "resources\\vn.png" - -VU_PNG PNG "resources\\vu.png" - -AAB_PNG PNG "resources\\wales.png" - -WF_PNG PNG "resources\\wf.png" - -WS_PNG PNG "resources\\ws.png" - -YE_PNG PNG "resources\\ye.png" - -YT_PNG PNG "resources\\yt.png" - -ZA_PNG PNG "resources\\za.png" - -ZM_PNG PNG "resources\\zm.png" - -ZW_PNG PNG "resources\\zw.png" - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_WHOIS, DIALOG - BEGIN - LEFTMARGIN, 2 - RIGHTMARGIN, 387 - TOPMARGIN, 2 - BOTTOMMARGIN, 212 - END - - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 194 - TOPMARGIN, 7 - BOTTOMMARGIN, 50 - END - - IDD_PING, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 322 - TOPMARGIN, 7 - BOTTOMMARGIN, 144 - END - - IDD_TRACERT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 382 - TOPMARGIN, 7 - BOTTOMMARGIN, 205 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_WHOIS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_TRACERT AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PING AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,8,0,0 + PRODUCTVERSION 1,8,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "dmex" + VALUE "FileDescription", "Network Tools plugin for Process Hacker" + VALUE "FileVersion", "1.8" + VALUE "InternalName", "ProcessHacker.NetworkTools" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "NetworkTools.dll" + VALUE "ProductName", "Network Tools plugin for Process Hacker" + VALUE "ProductVersion", "1.8" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_WHOIS DIALOGEX 0, 0, 389, 214 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Network Tools" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_NETOUTPUTEDIT,"RICHEDIT50W",WS_VSCROLL | WS_TABSTOP | 0x4,2,2,387,212 +END + +IDD_OPTIONS DIALOGEX 0, 0, 253, 129 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Ping packet length:",IDC_STATIC,9,25,62,8 + EDITTEXT IDC_PINGPACKETLENGTH,7,36,89,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_MAXHOPS,105,36,81,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Max Tracert Hops:",IDC_STATIC,105,25,60,8 + CONTROL "Enable extended TCP statistics",IDC_ENABLE_EXTENDED_TCP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,115,10 + LTEXT "GeoIP database location:",IDC_STATIC,7,60,82,8 + EDITTEXT IDC_DATABASE,7,73,180,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,193,72,50,14 + LTEXT "If a relative path is specified, it is relative to Process Hacker's directory. Environment variables can be used. Changes will take place after Process Hacker is restarted.",IDC_STATIC,7,92,235,26 +END + +IDD_PING DIALOGEX 0, 0, 329, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Pinging X with X bytes of data:",IDC_MAINTEXT,7,7,315,16 + LTEXT "PingGraphLayout",IDC_PING_LAYOUT,7,26,315,64,NOT WS_VISIBLE | WS_BORDER + GROUPBOX "Ping statistics",IDC_ICMP_PANEL,7,92,315,52,0,WS_EX_TRANSPARENT + LTEXT "Average: 0ms",IDC_ICMP_AVG,13,104,73,8 + LTEXT "Pings sent: 0",IDC_PINGS_SENT,91,104,88,8 + LTEXT "Bad replies: 0",IDC_BAD_HASH,187,104,90,8 + LTEXT "Minimum: 0ms",IDC_ICMP_MIN,13,116,73,8 + LTEXT "Pings lost: 0 (0%)",IDC_PINGS_LOST,91,116,88,8 + LTEXT "Anon replies: 0",IDC_ANON_ADDR,188,116,87,8 + LTEXT "Maximum: 0ms",IDC_ICMP_MAX,13,129,73,8 +END + +IDD_TRACERT DIALOGEX 0, 0, 389, 212 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Tracing route to xyz...",IDC_STATUS,7,5,375,12 + DEFPUSHBUTTON "Close",IDCANCEL,333,191,50,14 + CONTROL "",IDC_LIST_TRACERT,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | 0xa,7,22,375,166,WS_EX_CLIENTEDGE + PUSHBUTTON "Refresh",IDC_REFRESH,279,191,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +AD_PNG PNG "resources\\ad.png" + +AE_PNG PNG "resources\\ae.png" + +AF_PNG PNG "resources\\af.png" + +AG_PNG PNG "resources\\ag.png" + +AI_PNG PNG "resources\\ai.png" + +AL_PNG PNG "resources\\al.png" + +AM_PNG PNG "resources\\am.png" + +AN_PNG PNG "resources\\an.png" + +AO_PNG PNG "resources\\ao.png" + +AR_PNG PNG "resources\\ar.png" + +AS_PNG PNG "resources\\as.png" + +AT_PNG PNG "resources\\at.png" + +AU_PNG PNG "resources\\au.png" + +AW_PNG PNG "resources\\aw.png" + +AX_PNG PNG "resources\\ax.png" + +AZ_PNG PNG "resources\\az.png" + +BA_PNG PNG "resources\\ba.png" + +BB_PNG PNG "resources\\bb.png" + +BD_PNG PNG "resources\\bd.png" + +BE_PNG PNG "resources\\be.png" + +BF_PNG PNG "resources\\bf.png" + +BG_PNG PNG "resources\\bg.png" + +BH_PNG PNG "resources\\bh.png" + +BI__PNG PNG "resources\\bi.png" + +BJ_PNG PNG "resources\\bj.png" + +BM_PNG PNG "resources\\bm.png" + +BN_PNG PNG "resources\\bn.png" + +BO_PNG PNG "resources\\bo.png" + +BR_PNG PNG "resources\\br.png" + +BS_PNG PNG "resources\\bs.png" + +BT_PNG PNG "resources\\bt.png" + +BV_PNG PNG "resources\\bv.png" + +BW_PNG PNG "resources\\bw.png" + +BY_PNG PNG "resources\\by.png" + +BZ_PNG PNG "resources\\bz.png" + +CA_PNG PNG "resources\\ca.png" + +AAC_PNG PNG "resources\\catalonia.png" + +CC_PNG PNG "resources\\cc.png" + +CD_PNG PNG "resources\\cd.png" + +CF_PNG PNG "resources\\cf.png" + +CG_PNG PNG "resources\\cg.png" + +CH_PNG PNG "resources\\ch.png" + +CI_PNG PNG "resources\\ci.png" + +CK_PNG PNG "resources\\ck.png" + +CL_PNG PNG "resources\\cl.png" + +CM_PNG PNG "resources\\cm.png" + +CN_PNG PNG "resources\\cn.png" + +CO_PNG PNG "resources\\co.png" + +CR_PNG PNG "resources\\cr.png" + +CS_PNG PNG "resources\\cs.png" + +CU_PNG PNG "resources\\cu.png" + +CV_PNG PNG "resources\\cv.png" + +CX_PNG PNG "resources\\cx.png" + +CY_PNG PNG "resources\\cy.png" + +CZ_PNG PNG "resources\\cz.png" + +DE_PNG PNG "resources\\de.png" + +DJ_PNG PNG "resources\\dj.png" + +DK_PNG PNG "resources\\dk.png" + +DM_PNG PNG "resources\\dm.png" + +DO_PNG PNG "resources\\do.png" + +DZ_PNG PNG "resources\\dz.png" + +EC_PNG PNG "resources\\ec.png" + +EE_PNG PNG "resources\\ee.png" + +EG_PNG PNG "resources\\eg.png" + +EH_PNG PNG "resources\\eh.png" + +AAD_PNG PNG "resources\\england.png" + +ER_PNG PNG "resources\\er.png" + +ES_PNG PNG "resources\\es.png" + +ET_PNG PNG "resources\\et.png" + +AAE_PNG PNG "resources\\europeanunion.png" + +AAF_PNG PNG "resources\\fam.png" + +FI_PNG PNG "resources\\fi.png" + +FJ_PNG PNG "resources\\fj.png" + +FK_PNG PNG "resources\\fk.png" + +FM_PNG PNG "resources\\fm.png" + +FO_PNG PNG "resources\\fo.png" + +FR_PNG PNG "resources\\fr.png" + +GA_PNG PNG "resources\\ga.png" + +GB_PNG PNG "resources\\gb.png" + +GD_PNG PNG "resources\\gd.png" + +GE_PNG PNG "resources\\ge.png" + +GF_PNG PNG "resources\\gf.png" + +GH_PNG PNG "resources\\gh.png" + +GI_PNG PNG "resources\\gi.png" + +GL_PNG PNG "resources\\gl.png" + +GM_PNG PNG "resources\\gm.png" + +GN_PNG PNG "resources\\gn.png" + +GP_PNG PNG "resources\\gp.png" + +GQ_PNG PNG "resources\\gq.png" + +GR_PNG PNG "resources\\gr.png" + +GS_PNG PNG "resources\\gs.png" + +GT_PNG PNG "resources\\gt.png" + +GU_PNG PNG "resources\\gu.png" + +GW_PNG PNG "resources\\gw.png" + +GY_PNG PNG "resources\\gy.png" + +HK_PNG PNG "resources\\hk.png" + +HM_PNG PNG "resources\\hm.png" + +HN_PNG PNG "resources\\hn.png" + +HR_PNG PNG "resources\\hr.png" + +HT_PNG PNG "resources\\ht.png" + +HU_PNG PNG "resources\\hu.png" + +ID_PNG PNG "resources\\id.png" + +IE_PNG PNG "resources\\ie.png" + +IL_PNG PNG "resources\\il.png" + +IN_PNG PNG "resources\\in.png" + +IO_PNG PNG "resources\\io.png" + +IQ_PNG PNG "resources\\iq.png" + +IR_PNG PNG "resources\\ir.png" + +IS_PNG PNG "resources\\is.png" + +IT_PNG PNG "resources\\it.png" + +JM_PNG PNG "resources\\jm.png" + +JO_PNG PNG "resources\\jo.png" + +JP_PNG PNG "resources\\jp.png" + +KE_PNG PNG "resources\\ke.png" + +KG_PNG PNG "resources\\kg.png" + +KH_PNG PNG "resources\\kh.png" + +KI_PNG PNG "resources\\ki.png" + +KM_PNG PNG "resources\\km.png" + +KN_PNG PNG "resources\\kn.png" + +KP_PNG PNG "resources\\kp.png" + +KR_PNG PNG "resources\\kr.png" + +KW_PNG PNG "resources\\kw.png" + +KY_PNG PNG "resources\\ky.png" + +KZ_PNG PNG "resources\\kz.png" + +LA_PNG PNG "resources\\la.png" + +LB_PNG PNG "resources\\lb.png" + +LC_PNG PNG "resources\\lc.png" + +LI_PNG PNG "resources\\li.png" + +LK_PNG PNG "resources\\lk.png" + +LR_PNG PNG "resources\\lr.png" + +LS_PNG PNG "resources\\ls.png" + +LT_PNG PNG "resources\\lt.png" + +LU_PNG PNG "resources\\lu.png" + +LV_PNG PNG "resources\\lv.png" + +LY_PNG PNG "resources\\ly.png" + +MA_PNG PNG "resources\\ma.png" + +MC_PNG PNG "resources\\mc.png" + +MD_PNG PNG "resources\\md.png" + +ME_PNG PNG "resources\\me.png" + +MG_PNG PNG "resources\\mg.png" + +MH_PNG PNG "resources\\mh.png" + +MK_PNG PNG "resources\\mk.png" + +ML_PNG PNG "resources\\ml.png" + +MM_PNG PNG "resources\\mm.png" + +MN_PNG PNG "resources\\mn.png" + +MO_PNG PNG "resources\\mo.png" + +MP_PNG PNG "resources\\mp.png" + +MQ_PNG PNG "resources\\mq.png" + +MR_PNG PNG "resources\\mr.png" + +MS_PNG PNG "resources\\ms.png" + +MT_PNG PNG "resources\\mt.png" + +MU_PNG PNG "resources\\mu.png" + +MV_PNG PNG "resources\\mv.png" + +MW_PNG PNG "resources\\mw.png" + +MX_PNG PNG "resources\\mx.png" + +MY_PNG PNG "resources\\my.png" + +MZ_PNG PNG "resources\\mz.png" + +NA_PNG PNG "resources\\na.png" + +NC_PNG PNG "resources\\nc.png" + +NE_PNG PNG "resources\\ne.png" + +NF_PNG PNG "resources\\nf.png" + +NG_PNG PNG "resources\\ng.png" + +NI_PNG PNG "resources\\ni.png" + +NL_PNG PNG "resources\\nl.png" + +NO_PNG PNG "resources\\no.png" + +NP_PNG PNG "resources\\np.png" + +NR_PNG PNG "resources\\nr.png" + +NU_PNG PNG "resources\\nu.png" + +NZ_PNG PNG "resources\\nz.png" + +OM_PNG PNG "resources\\om.png" + +PA_PNG PNG "resources\\pa.png" + +PE_PNG PNG "resources\\pe.png" + +PF_PNG PNG "resources\\pf.png" + +PG_PNG PNG "resources\\pg.png" + +PH_PNG PNG "resources\\ph.png" + +PK_PNG PNG "resources\\pk.png" + +PL_PNG PNG "resources\\pl.png" + +PM_PNG PNG "resources\\pm.png" + +PN_PNG PNG "resources\\pn.png" + +PR_PNG PNG "resources\\pr.png" + +PS_PNG PNG "resources\\ps.png" + +PT_PNG PNG "resources\\pt.png" + +PW_PNG PNG "resources\\pw.png" + +PY_PNG PNG "resources\\py.png" + +QA_PNG PNG "resources\\qa.png" + +RE_PNG PNG "resources\\re.png" + +RO_PNG PNG "resources\\ro.png" + +RS_PNG PNG "resources\\rs.png" + +RU_PNG PNG "resources\\ru.png" + +RW_PNG PNG "resources\\rw.png" + +SA_PNG PNG "resources\\sa.png" + +SB_PNG PNG "resources\\sb.png" + +SC_PNG PNG "resources\\sc.png" + +AAA_PNG PNG "resources\\scotland.png" + +SD_PNG PNG "resources\\sd.png" + +SE_PNG PNG "resources\\se.png" + +SG_PNG PNG "resources\\sg.png" + +SH_PNG PNG "resources\\sh.png" + +SI_PNG PNG "resources\\si.png" + +SJ_PNG PNG "resources\\sj.png" + +SK_PNG PNG "resources\\sk.png" + +SL_PNG PNG "resources\\sl.png" + +SM_PNG PNG "resources\\sm.png" + +SN_PNG PNG "resources\\sn.png" + +SO_PNG PNG "resources\\so.png" + +SR_PNG PNG "resources\\sr.png" + +ST_PNG PNG "resources\\st.png" + +SV_PNG PNG "resources\\sv.png" + +SY_PNG PNG "resources\\sy.png" + +SZ_PNG PNG "resources\\sz.png" + +TC_PNG PNG "resources\\tc.png" + +TD_PNG PNG "resources\\td.png" + +TF_PNG PNG "resources\\tf.png" + +TG_PNG PNG "resources\\tg.png" + +TH_PNG PNG "resources\\th.png" + +TJ_PNG PNG "resources\\tj.png" + +TK_PNG PNG "resources\\tk.png" + +TL_PNG PNG "resources\\tl.png" + +TM_PNG PNG "resources\\tm.png" + +TN_PNG PNG "resources\\tn.png" + +TO_PNG PNG "resources\\to.png" + +TR_PNG PNG "resources\\tr.png" + +TT_PNG PNG "resources\\tt.png" + +TV_PNG PNG "resources\\tv.png" + +TW_PNG PNG "resources\\tw.png" + +TZ_PNG PNG "resources\\tz.png" + +UA_PNG PNG "resources\\ua.png" + +UG_PNG PNG "resources\\ug.png" + +UM_PNG PNG "resources\\um.png" + +US_PNG PNG "resources\\us.png" + +UY_PNG PNG "resources\\uy.png" + +UZ_PNG PNG "resources\\uz.png" + +VA_PNG PNG "resources\\va.png" + +VC_PNG PNG "resources\\vc.png" + +VE_PNG PNG "resources\\ve.png" + +VG_PNG PNG "resources\\vg.png" + +VI_PNG PNG "resources\\vi.png" + +VN_PNG PNG "resources\\vn.png" + +VU_PNG PNG "resources\\vu.png" + +AAB_PNG PNG "resources\\wales.png" + +WF_PNG PNG "resources\\wf.png" + +WS_PNG PNG "resources\\ws.png" + +YE_PNG PNG "resources\\ye.png" + +YT_PNG PNG "resources\\yt.png" + +ZA_PNG PNG "resources\\za.png" + +ZM_PNG PNG "resources\\zm.png" + +ZW_PNG PNG "resources\\zw.png" + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_WHOIS, DIALOG + BEGIN + LEFTMARGIN, 2 + TOPMARGIN, 2 + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 246 + TOPMARGIN, 7 + BOTTOMMARGIN, 122 + END + + IDD_PING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 322 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_TRACERT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 382 + TOPMARGIN, 7 + BOTTOMMARGIN, 205 + END +END +#endif // APSTUDIO_INVOKED + + + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/NetworkTools/NetworkTools.vcxproj b/plugins/NetworkTools/NetworkTools.vcxproj index 5a770136f286..49031ce6f2d2 100644 --- a/plugins/NetworkTools/NetworkTools.vcxproj +++ b/plugins/NetworkTools/NetworkTools.vcxproj @@ -1,398 +1,403 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E} - NetworkTools - Win32Proj - NetworkTools - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - iphlpapi.dll;winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - - iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - iphlpapi.dll;winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - iphlpapi.dll;winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - iphlpapi.lib;winhttp.lib;ws2_32.lib;uxtheme.lib;%(AdditionalDependencies) - iphlpapi.dll;winhttp.dll;ws2_32.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E} + NetworkTools + Win32Proj + NetworkTools + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + + + + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;%(AdditionalDependencies) + comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;winhttp.dll;ws2_32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;%(AdditionalDependencies) + comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;winhttp.dll;ws2_32.dll;user32.dll;%(DelayLoadDLLs) + + + + + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;%(AdditionalDependencies) + comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;winhttp.dll;ws2_32.dll;user32.dll;%(DelayLoadDLLs) + + + + + dnsapi.lib;iphlpapi.lib;winhttp.lib;ws2_32.lib;%(AdditionalDependencies) + comctl32.dll;dnsapi.dll;gdi32.dll;iphlpapi.dll;winhttp.dll;ws2_32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/NetworkTools/NetworkTools.vcxproj.filters b/plugins/NetworkTools/NetworkTools.vcxproj.filters index eadc9f23f169..316ed1bfa90e 100644 --- a/plugins/NetworkTools/NetworkTools.vcxproj.filters +++ b/plugins/NetworkTools/NetworkTools.vcxproj.filters @@ -129,6 +129,9 @@ Source Files + + Source Files\maxmind + @@ -200,6 +203,9 @@ Header Files + + Header Files\maxminddb + diff --git a/plugins/NetworkTools/country.c b/plugins/NetworkTools/country.c index 147a7ac900ab..2860876ec4e2 100644 --- a/plugins/NetworkTools/country.c +++ b/plugins/NetworkTools/country.c @@ -2,7 +2,7 @@ * Process Hacker Network Tools - * IP Country support * - * Copyright (C) 2016-2017 dmex + * Copyright (C) 2016-2018 dmex * * This file is part of Process Hacker. * @@ -21,23 +21,62 @@ */ #include "nettools.h" +#include #include "maxminddb\maxminddb.h" BOOLEAN GeoDbLoaded = FALSE; BOOLEAN GeoDbExpired = FALSE; +HIMAGELIST GeoImageList = NULL; static MMDB_s GeoDb = { 0 }; -VOID LoadGeoLiteDb( +PPH_STRING NetToolsGetGeoLiteDbPath( VOID ) { - PPH_STRING path; + PPH_STRING databaseFile; PPH_STRING directory; + PPH_STRING path; + + if (!(databaseFile = PhGetExpandStringSetting(SETTING_NAME_DB_LOCATION))) + return NULL; + PhMoveReference(&databaseFile, PhGetBaseName(databaseFile)); directory = PH_AUTO(PhGetApplicationDirectory()); - path = PhConcatStrings(2, PhGetString(directory), L"Plugins\\plugindata\\GeoLite2-Country.mmdb"); + path = PH_AUTO(PhConcatStringRef2(&directory->sr, &databaseFile->sr)); + + if (PhDoesFileExistsWin32(path->Buffer)) + { + return PhReferenceObject(path); + } + else + { + path = PhaGetStringSetting(SETTING_NAME_DB_LOCATION); + path = PH_AUTO(PhExpandEnvironmentStrings(&path->sr)); + + if (RtlDetermineDosPathNameType_U(path->Buffer) == RtlPathTypeRelative) + { + directory = PH_AUTO(PhGetApplicationDirectory()); + path = PH_AUTO(PhConcatStringRef2(&directory->sr, &path->sr)); + } + + return PhReferenceObject(path); + } + + return NULL; +} - if (MMDB_open(PhGetString(path), MMDB_MODE_MMAP, &GeoDb) == MMDB_SUCCESS) +VOID LoadGeoLiteDb( + VOID + ) +{ + PPH_STRING dbpath; + + dbpath = NetToolsGetGeoLiteDbPath(); + + if (PhIsNullOrEmptyString(dbpath)) + return; + + if (MMDB_open(PhGetString(dbpath), MMDB_MODE_MMAP, &GeoDb) == MMDB_SUCCESS) { time_t systemTime; @@ -57,27 +96,153 @@ VOID LoadGeoLiteDb( GeoDbLoaded = TRUE; } + + if (GeoDbLoaded) + { + GeoImageList = ImageList_Create(16, 11, ILC_COLOR32, 20, 20); + } + + PhDereferenceObject(dbpath); } VOID FreeGeoLiteDb( VOID ) { + if (GeoImageList) + { + ImageList_RemoveAll(GeoImageList); + ImageList_Destroy(GeoImageList); + } + if (GeoDbLoaded) { MMDB_close(&GeoDb); } } -BOOLEAN LookupCountryCode( - _In_ PH_IP_ADDRESS RemoteAddress, +BOOLEAN GeoDbGetCityData( + _In_ MMDB_entry_s* GeoDbEntry, + _Out_ DOUBLE *CityLatitude, + _Out_ DOUBLE *CityLongitude + ) +{ + MMDB_entry_data_s mmdb_entry; + DOUBLE cityLatitude = 0.0; + DOUBLE cityLongitude = 0.0; + + if (MMDB_get_value(GeoDbEntry, &mmdb_entry, "location", "latitude", NULL) == MMDB_SUCCESS) + { + if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) + { + cityLatitude = mmdb_entry.double_value; + } + } + + if (MMDB_get_value(GeoDbEntry, &mmdb_entry, "location", "longitude", NULL) == MMDB_SUCCESS) + { + if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) + { + cityLongitude = mmdb_entry.double_value; + } + } + + if (cityLatitude && cityLongitude) + { + *CityLatitude = cityLatitude; + *CityLongitude = cityLongitude; + return TRUE; + } + + return FALSE; +} + +BOOLEAN GeoDbGetCountryData( + _In_ MMDB_entry_s* GeoDbEntry, _Out_ PPH_STRING *CountryCode, _Out_ PPH_STRING *CountryName ) { + MMDB_entry_data_s mmdb_entry; PPH_STRING countryCode = NULL; PPH_STRING countryName = NULL; + + if (MMDB_get_value(GeoDbEntry, &mmdb_entry, "country", "iso_code", NULL) == MMDB_SUCCESS) + { + if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) + { + countryCode = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); + } + } + + if (MMDB_get_value(GeoDbEntry, &mmdb_entry, "country", "names", "en", NULL) == MMDB_SUCCESS) + { + if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) + { + countryName = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); + } + } + + if (countryCode && countryName) + { + *CountryCode = countryCode; + *CountryName = countryName; + return TRUE; + } + + if (countryCode) + PhDereferenceObject(countryCode); + if (countryName) + PhDereferenceObject(countryName); + return FALSE; +} + +BOOLEAN GeoDbGetContinentData( + _In_ MMDB_entry_s* GeoDbEntry, + _Out_ PPH_STRING *ContinentCode, + _Out_ PPH_STRING *ContinentName + ) +{ MMDB_entry_data_s mmdb_entry; + PPH_STRING continentCode = NULL; + PPH_STRING continentName = NULL; + + if (MMDB_get_value(GeoDbEntry, &mmdb_entry, "continent", "code", NULL) == MMDB_SUCCESS) + { + if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) + { + continentCode = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); + } + } + + if (MMDB_get_value(GeoDbEntry, &mmdb_entry, "country", "names", "en", NULL) == MMDB_SUCCESS) + { + if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) + { + continentName = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); + } + } + + if (continentCode && continentName) + { + *ContinentCode = continentCode; + *ContinentName = continentName; + return TRUE; + } + + if (continentCode) + PhDereferenceObject(continentCode); + if (continentName) + PhDereferenceObject(continentName); + return FALSE; +} + +BOOLEAN LookupCountryCode( + _In_ PH_IP_ADDRESS RemoteAddress, + _Out_ PPH_STRING *CountryCode, + _Out_ PPH_STRING *CountryName + ) +{ MMDB_lookup_result_s mmdb_result; INT mmdb_error = 0; @@ -94,6 +259,9 @@ BOOLEAN LookupCountryCode( if (IN4_IS_ADDR_LOOPBACK(&RemoteAddress.InAddr)) return FALSE; + if (IN4_IS_ADDR_RFC1918(&RemoteAddress.InAddr)) + return FALSE; + memset(&ipv4SockAddr, 0, sizeof(SOCKADDR_IN)); memset(&mmdb_result, 0, sizeof(MMDB_lookup_result_s)); @@ -131,56 +299,13 @@ BOOLEAN LookupCountryCode( if (mmdb_error == 0 && mmdb_result.found_entry) { - memset(&mmdb_entry, 0, sizeof(MMDB_entry_data_s)); - - if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "country", "iso_code", NULL) == MMDB_SUCCESS) - { - if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) - { - countryCode = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); - } - } - - if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "country", "names", "en", NULL) == MMDB_SUCCESS) - { - if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) - { - countryName = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); - } - } + if (GeoDbGetCountryData(&mmdb_result.entry, CountryCode, CountryName)) + return TRUE; - //if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "location", "latitude", NULL) == MMDB_SUCCESS) - //{ - // if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) - // { - // *CityLatitude = mmdb_entry.double_value; - // } - //} - - //if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "location", "longitude", NULL) == MMDB_SUCCESS) - //{ - // if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) - // { - // *CityLongitude = mmdb_entry.double_value; - // } - //} - } - - if (countryCode && countryName) - { - *CountryCode = countryCode; - *CountryName = countryName; - return TRUE; - } - - if (countryCode) - { - PhDereferenceObject(countryCode); - } - - if (countryName) - { - PhDereferenceObject(countryName); + // HACK + // We can sometimes get the continent even when the address doesn't have a country. + if (GeoDbGetContinentData(&mmdb_result.entry, CountryCode, CountryName)) + return TRUE; } return FALSE; @@ -192,9 +317,6 @@ BOOLEAN LookupSockInAddr4CountryCode( _Out_ PPH_STRING *CountryName ) { - PPH_STRING countryCode = NULL; - PPH_STRING countryName = NULL; - MMDB_entry_data_s mmdb_entry; MMDB_lookup_result_s mmdb_result; SOCKADDR_IN ipv4SockAddr; INT mmdb_error = 0; @@ -222,56 +344,13 @@ BOOLEAN LookupSockInAddr4CountryCode( if (mmdb_error == 0 && mmdb_result.found_entry) { - memset(&mmdb_entry, 0, sizeof(MMDB_entry_data_s)); - - if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "country", "iso_code", NULL) == MMDB_SUCCESS) - { - if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) - { - countryCode = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); - } - } - - if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "country", "names", "en", NULL) == MMDB_SUCCESS) - { - if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) - { - countryName = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); - } - } - - //if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "location", "latitude", NULL) == MMDB_SUCCESS) - //{ - // if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) - // { - // *CityLatitude = mmdb_entry.double_value; - // } - //} - - //if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "location", "longitude", NULL) == MMDB_SUCCESS) - //{ - // if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) - // { - // *CityLongitude = mmdb_entry.double_value; - // } - //} - } - - if (countryCode && countryName) - { - *CountryCode = countryCode; - *CountryName = countryName; - return TRUE; - } - - if (countryCode) - { - PhDereferenceObject(countryCode); - } + if (GeoDbGetCountryData(&mmdb_result.entry, CountryCode, CountryName)) + return TRUE; - if (countryName) - { - PhDereferenceObject(countryName); + // HACK + // We can sometimes get the continent even when the address doesn't have a country. + if (GeoDbGetContinentData(&mmdb_result.entry, CountryCode, CountryName)) + return TRUE; } return FALSE; @@ -283,9 +362,6 @@ BOOLEAN LookupSockInAddr6CountryCode( _Out_ PPH_STRING *CountryName ) { - PPH_STRING countryCode = NULL; - PPH_STRING countryName = NULL; - MMDB_entry_data_s mmdb_entry; MMDB_lookup_result_s mmdb_result; SOCKADDR_IN6 ipv6SockAddr; INT mmdb_error = 0; @@ -313,56 +389,13 @@ BOOLEAN LookupSockInAddr6CountryCode( if (mmdb_error == 0 && mmdb_result.found_entry) { - memset(&mmdb_entry, 0, sizeof(MMDB_entry_data_s)); + if (GeoDbGetCountryData(&mmdb_result.entry, CountryCode, CountryName)) + return TRUE; - if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "country", "iso_code", NULL) == MMDB_SUCCESS) - { - if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) - { - countryCode = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); - } - } - - if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "country", "names", "en", NULL) == MMDB_SUCCESS) - { - if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_UTF8_STRING) - { - countryName = PhConvertUtf8ToUtf16Ex((PCHAR)mmdb_entry.utf8_string, mmdb_entry.data_size); - } - } - - //if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "location", "latitude", NULL) == MMDB_SUCCESS) - //{ - // if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) - // { - // *CityLatitude = mmdb_entry.double_value; - // } - //} - - //if (MMDB_get_value(&mmdb_result.entry, &mmdb_entry, "location", "longitude", NULL) == MMDB_SUCCESS) - //{ - // if (mmdb_entry.has_data && mmdb_entry.type == MMDB_DATA_TYPE_DOUBLE) - // { - // *CityLongitude = mmdb_entry.double_value; - // } - //} - } - - if (countryCode && countryName) - { - *CountryCode = countryCode; - *CountryName = countryName; - return TRUE; - } - - if (countryCode) - { - PhDereferenceObject(countryCode); - } - - if (countryName) - { - PhDereferenceObject(countryName); + // HACK + // We can sometimes get the continent even when the address doesn't have a country. + if (GeoDbGetContinentData(&mmdb_result.entry, CountryCode, CountryName)) + return TRUE; } return FALSE; @@ -372,92 +405,136 @@ struct { PWSTR CountryCode; INT ResourceID; + INT IconIndex; } CountryResourceTable[] = { - { L"AD", AD_PNG }, { L"AE", AE_PNG }, { L"AF", AF_PNG }, { L"AG", AG_PNG }, - { L"AI", AI_PNG }, { L"AL", AL_PNG }, { L"AM", AM_PNG }, { L"AN", AN_PNG }, - { L"AO", AO_PNG }, { L"AR", AR_PNG }, { L"AS", AS_PNG }, { L"AT", AT_PNG }, - { L"AU", AU_PNG }, { L"AW", AW_PNG }, { L"AX", AX_PNG }, { L"AZ", AZ_PNG }, - { L"BA", BA_PNG }, { L"BB", BB_PNG }, { L"BD", BD_PNG }, { L"BE", BE_PNG }, - { L"BF", BF_PNG }, { L"BG", BG_PNG }, { L"BH", BH_PNG }, { L"BI", BI__PNG }, - { L"BJ", BJ_PNG }, { L"BM", BM_PNG }, { L"BN", BN_PNG }, { L"BO", BO_PNG }, - { L"BR", BR_PNG }, { L"BS", BS_PNG }, { L"BT", BT_PNG }, { L"BV", BV_PNG }, - { L"BW", BW_PNG }, { L"BY", BY_PNG }, { L"BZ", BZ_PNG }, - { L"CA", CA_PNG }, { L"CC", CC_PNG }, { L"CD", CD_PNG }, { L"CF", CF_PNG }, - { L"CG", CG_PNG }, { L"CH", CH_PNG }, { L"CI", CI_PNG }, { L"CK", CK_PNG }, - { L"CL", CL_PNG }, { L"CM", CM_PNG }, { L"CN", CN_PNG }, { L"CO", CO_PNG }, - { L"CR", CR_PNG }, { L"CS", CS_PNG }, { L"CU", CU_PNG }, { L"CV", CV_PNG }, - { L"CX", CX_PNG }, { L"CY", CY_PNG }, { L"CZ", CZ_PNG }, { L"DE", DE_PNG }, - { L"DJ", DJ_PNG }, { L"DK", DK_PNG }, { L"DM", DM_PNG }, { L"DO", DO_PNG }, - { L"DZ", DZ_PNG }, - { L"EC", EC_PNG }, { L"EE", EE_PNG }, { L"EG", EG_PNG }, { L"EH", EH_PNG }, - { L"ER", ER_PNG }, { L"ES", ES_PNG }, { L"ET", ET_PNG }, - { L"FI", FI_PNG }, { L"FJ", FJ_PNG }, { L"FK", FK_PNG }, { L"FO", FO_PNG }, - { L"FR", FR_PNG }, - { L"GA", GA_PNG }, { L"GB", GB_PNG }, { L"GD", GD_PNG }, { L"GE", GE_PNG }, - { L"GF", GF_PNG }, { L"GH", GH_PNG }, { L"GI", GI_PNG }, { L"GL", GL_PNG }, - { L"GM", GM_PNG }, { L"GN", GN_PNG }, { L"GP", GP_PNG }, { L"GQ", GQ_PNG }, - { L"GR", GR_PNG }, { L"GS", GS_PNG }, { L"GT", GT_PNG }, { L"GU", GU_PNG }, - { L"GW", GW_PNG }, { L"GY", GY_PNG }, - { L"HK", HK_PNG }, { L"HM", HM_PNG }, { L"HN", HN_PNG }, { L"HR", HR_PNG }, - { L"HT", HT_PNG }, { L"HU", HU_PNG }, - { L"ID", ID_PNG }, { L"IE", IE_PNG }, { L"IL", IL_PNG }, { L"IN", IN_PNG }, - { L"IO", IO_PNG }, { L"IQ", IQ_PNG }, { L"IR", IR_PNG }, { L"IS", IS_PNG }, - { L"IT", IT_PNG }, - { L"JM", JM_PNG }, { L"JO", JO_PNG }, { L"JP", JP_PNG }, - { L"KE", KE_PNG }, { L"KG", KG_PNG }, { L"KH", KH_PNG }, { L"KI", KI_PNG }, - { L"KM", KM_PNG }, { L"KN", KN_PNG }, { L"KP", KP_PNG }, { L"KR", KR_PNG }, - { L"KW", KW_PNG }, { L"KY", KY_PNG }, { L"KZ", KZ_PNG }, - { L"LA", LA_PNG }, { L"LB", LB_PNG }, { L"LC", LC_PNG }, { L"LI", LI_PNG }, - { L"LK", LK_PNG }, { L"LR", LR_PNG }, { L"LS", LS_PNG }, { L"LT", LT_PNG }, - { L"LU", LU_PNG }, { L"LV", LV_PNG }, { L"LY", LY_PNG }, - { L"MA", MA_PNG }, { L"MC", MC_PNG }, { L"MD", MD_PNG }, { L"ME", ME_PNG }, - { L"MG", MG_PNG }, { L"MH", MH_PNG }, { L"MK", MK_PNG }, { L"ML", ML_PNG }, - { L"MM", MM_PNG }, { L"MN", MN_PNG }, { L"MO", MO_PNG }, { L"MP", MP_PNG }, - { L"MQ", MQ_PNG }, { L"MR", MR_PNG }, { L"MS", MS_PNG }, { L"MT", MT_PNG }, - { L"MU", MU_PNG }, { L"MV", MV_PNG }, { L"MW", MW_PNG }, { L"MX", MX_PNG }, - { L"MY", MY_PNG }, { L"MZ", MZ_PNG }, - { L"NA", NA_PNG }, { L"NC", NC_PNG }, { L"NE", NE_PNG }, { L"NF", NF_PNG }, - { L"NG", NG_PNG }, { L"NI", NI_PNG }, { L"NL", NL_PNG }, { L"NO", NO_PNG }, - { L"NP", NP_PNG }, { L"NR", NR_PNG }, { L"NU", NU_PNG }, { L"NZ", NZ_PNG }, - { L"OM", OM_PNG }, - { L"PA", PA_PNG }, { L"PE", PE_PNG }, { L"PF", PF_PNG }, { L"PG", PG_PNG }, - { L"PH", PH_PNG }, { L"PK", PK_PNG }, { L"PL", PL_PNG }, { L"PM", PM_PNG }, - { L"PN", PN_PNG }, { L"PR", PR_PNG }, { L"PS", PS_PNG }, { L"PT", PT_PNG }, - { L"PW", PW_PNG }, { L"PY", PY_PNG }, - { L"QA", QA_PNG }, - { L"RE", RE_PNG }, { L"RO", RO_PNG }, { L"RS", RS_PNG }, { L"RU", RU_PNG }, - { L"RW", RW_PNG }, - { L"SA", SA_PNG }, { L"SB", SB_PNG }, { L"SC", SC_PNG }, { L"SD", SD_PNG }, - { L"SE", SE_PNG }, { L"SG", SG_PNG }, { L"SH", SH_PNG }, { L"SI", SI_PNG }, - { L"SJ", SJ_PNG }, { L"SK", SK_PNG }, { L"SL", SL_PNG }, { L"SM", SM_PNG }, - { L"SN", SN_PNG }, { L"SO", SO_PNG }, { L"SR", SR_PNG }, { L"ST", ST_PNG }, - { L"SV", SV_PNG }, { L"SY", SY_PNG }, { L"SZ", SZ_PNG }, - { L"TC", TC_PNG }, { L"TD", TD_PNG }, { L"TF", TF_PNG }, { L"TG", TG_PNG }, - { L"TH", TH_PNG }, { L"TJ", TJ_PNG }, { L"TK", TK_PNG }, { L"TL", TL_PNG }, - { L"TM", TM_PNG }, { L"TN", TN_PNG }, { L"TO", TO_PNG }, { L"TR", TR_PNG }, - { L"TT", TT_PNG }, { L"TV", TV_PNG }, { L"TW", TW_PNG }, { L"TZ", TZ_PNG }, - { L"UA", UA_PNG }, { L"UG", UG_PNG }, { L"UM", UM_PNG }, { L"US", US_PNG }, - { L"UY", UY_PNG }, { L"UZ", UZ_PNG }, - { L"VA", VA_PNG }, { L"VC", VC_PNG }, { L"VE", VE_PNG }, { L"VG", VG_PNG }, - { L"VI", VI_PNG }, { L"VN", VN_PNG }, { L"VU", VU_PNG }, - { L"WF", WF_PNG }, { L"WS", WS_PNG }, - { L"YE", YE_PNG }, { L"YT", YT_PNG }, - { L"ZA", ZA_PNG }, { L"ZM", ZM_PNG }, { L"ZW", ZW_PNG } + { L"AD", AD_PNG, INT_MAX }, { L"AE", AE_PNG, INT_MAX }, { L"AF", AF_PNG, INT_MAX }, { L"AG", AG_PNG, INT_MAX }, + { L"AI", AI_PNG, INT_MAX }, { L"AL", AL_PNG, INT_MAX }, { L"AM", AM_PNG, INT_MAX }, { L"AN", AN_PNG, INT_MAX }, + { L"AO", AO_PNG, INT_MAX }, { L"AR", AR_PNG, INT_MAX }, { L"AS", AS_PNG, INT_MAX }, { L"AT", AT_PNG, INT_MAX }, + { L"AU", AU_PNG, INT_MAX }, { L"AW", AW_PNG, INT_MAX }, { L"AX", AX_PNG, INT_MAX }, { L"AZ", AZ_PNG, INT_MAX }, + { L"BA", BA_PNG, INT_MAX }, { L"BB", BB_PNG, INT_MAX }, { L"BD", BD_PNG, INT_MAX }, { L"BE", BE_PNG, INT_MAX }, + { L"BF", BF_PNG, INT_MAX }, { L"BG", BG_PNG, INT_MAX }, { L"BH", BH_PNG, INT_MAX }, { L"BI", BI__PNG, INT_MAX }, + { L"BJ", BJ_PNG, INT_MAX }, { L"BM", BM_PNG, INT_MAX }, { L"BN", BN_PNG, INT_MAX }, { L"BO", BO_PNG, INT_MAX }, + { L"BR", BR_PNG, INT_MAX }, { L"BS", BS_PNG, INT_MAX }, { L"BT", BT_PNG, INT_MAX }, { L"BV", BV_PNG, INT_MAX }, + { L"BW", BW_PNG, INT_MAX }, { L"BY", BY_PNG, INT_MAX }, { L"BZ", BZ_PNG, INT_MAX }, + { L"CA", CA_PNG, INT_MAX }, { L"CC", CC_PNG, INT_MAX }, { L"CD", CD_PNG, INT_MAX }, { L"CF", CF_PNG, INT_MAX }, + { L"CG", CG_PNG, INT_MAX }, { L"CH", CH_PNG, INT_MAX }, { L"CI", CI_PNG, INT_MAX }, { L"CK", CK_PNG, INT_MAX }, + { L"CL", CL_PNG, INT_MAX }, { L"CM", CM_PNG, INT_MAX }, { L"CN", CN_PNG, INT_MAX }, { L"CO", CO_PNG, INT_MAX }, + { L"CR", CR_PNG, INT_MAX }, { L"CS", CS_PNG, INT_MAX }, { L"CU", CU_PNG, INT_MAX }, { L"CV", CV_PNG, INT_MAX }, + { L"CX", CX_PNG, INT_MAX }, { L"CY", CY_PNG, INT_MAX }, { L"CZ", CZ_PNG, INT_MAX }, { L"DE", DE_PNG, INT_MAX }, + { L"DJ", DJ_PNG, INT_MAX }, { L"DK", DK_PNG, INT_MAX }, { L"DM", DM_PNG, INT_MAX }, { L"DO", DO_PNG, INT_MAX }, + { L"DZ", DZ_PNG, INT_MAX }, + { L"EC", EC_PNG, INT_MAX }, { L"EE", EE_PNG, INT_MAX }, { L"EG", EG_PNG, INT_MAX }, { L"EH", EH_PNG, INT_MAX }, + { L"ER", ER_PNG, INT_MAX }, { L"ES", ES_PNG, INT_MAX }, { L"ET", ET_PNG, INT_MAX }, + { L"FI", FI_PNG, INT_MAX }, { L"FJ", FJ_PNG, INT_MAX }, { L"FK", FK_PNG, INT_MAX }, { L"FO", FO_PNG, INT_MAX }, + { L"FR", FR_PNG, INT_MAX }, + { L"GA", GA_PNG, INT_MAX }, { L"GB", GB_PNG, INT_MAX }, { L"GD", GD_PNG, INT_MAX }, { L"GE", GE_PNG, INT_MAX }, + { L"GF", GF_PNG, INT_MAX }, { L"GH", GH_PNG, INT_MAX }, { L"GI", GI_PNG, INT_MAX }, { L"GL", GL_PNG, INT_MAX }, + { L"GM", GM_PNG, INT_MAX }, { L"GN", GN_PNG, INT_MAX }, { L"GP", GP_PNG, INT_MAX }, { L"GQ", GQ_PNG, INT_MAX }, + { L"GR", GR_PNG, INT_MAX }, { L"GS", GS_PNG, INT_MAX }, { L"GT", GT_PNG, INT_MAX }, { L"GU", GU_PNG, INT_MAX }, + { L"GW", GW_PNG, INT_MAX }, { L"GY", GY_PNG, INT_MAX }, + { L"HK", HK_PNG, INT_MAX }, { L"HM", HM_PNG, INT_MAX }, { L"HN", HN_PNG, INT_MAX }, { L"HR", HR_PNG, INT_MAX }, + { L"HT", HT_PNG, INT_MAX }, { L"HU", HU_PNG, INT_MAX }, + { L"ID", ID_PNG, INT_MAX }, { L"IE", IE_PNG, INT_MAX }, { L"IL", IL_PNG, INT_MAX }, { L"IN", IN_PNG, INT_MAX }, + { L"IO", IO_PNG, INT_MAX }, { L"IQ", IQ_PNG, INT_MAX }, { L"IR", IR_PNG, INT_MAX }, { L"IS", IS_PNG, INT_MAX }, + { L"IT", IT_PNG, INT_MAX }, + { L"JM", JM_PNG, INT_MAX }, { L"JO", JO_PNG, INT_MAX }, { L"JP", JP_PNG, INT_MAX }, + { L"KE", KE_PNG, INT_MAX }, { L"KG", KG_PNG, INT_MAX }, { L"KH", KH_PNG, INT_MAX }, { L"KI", KI_PNG, INT_MAX }, + { L"KM", KM_PNG, INT_MAX }, { L"KN", KN_PNG, INT_MAX }, { L"KP", KP_PNG, INT_MAX }, { L"KR", KR_PNG, INT_MAX }, + { L"KW", KW_PNG, INT_MAX }, { L"KY", KY_PNG, INT_MAX }, { L"KZ", KZ_PNG, INT_MAX }, + { L"LA", LA_PNG, INT_MAX }, { L"LB", LB_PNG, INT_MAX }, { L"LC", LC_PNG, INT_MAX }, { L"LI", LI_PNG, INT_MAX }, + { L"LK", LK_PNG, INT_MAX }, { L"LR", LR_PNG, INT_MAX }, { L"LS", LS_PNG, INT_MAX }, { L"LT", LT_PNG, INT_MAX }, + { L"LU", LU_PNG, INT_MAX }, { L"LV", LV_PNG, INT_MAX }, { L"LY", LY_PNG, INT_MAX }, + { L"MA", MA_PNG, INT_MAX }, { L"MC", MC_PNG, INT_MAX }, { L"MD", MD_PNG, INT_MAX }, { L"ME", ME_PNG, INT_MAX }, + { L"MG", MG_PNG, INT_MAX }, { L"MH", MH_PNG, INT_MAX }, { L"MK", MK_PNG, INT_MAX }, { L"ML", ML_PNG, INT_MAX }, + { L"MM", MM_PNG, INT_MAX }, { L"MN", MN_PNG, INT_MAX }, { L"MO", MO_PNG, INT_MAX }, { L"MP", MP_PNG, INT_MAX }, + { L"MQ", MQ_PNG, INT_MAX }, { L"MR", MR_PNG, INT_MAX }, { L"MS", MS_PNG, INT_MAX }, { L"MT", MT_PNG, INT_MAX }, + { L"MU", MU_PNG, INT_MAX }, { L"MV", MV_PNG, INT_MAX }, { L"MW", MW_PNG, INT_MAX }, { L"MX", MX_PNG, INT_MAX }, + { L"MY", MY_PNG, INT_MAX }, { L"MZ", MZ_PNG, INT_MAX }, + { L"NA", NA_PNG, INT_MAX }, { L"NC", NC_PNG, INT_MAX }, { L"NE", NE_PNG, INT_MAX }, { L"NF", NF_PNG, INT_MAX }, + { L"NG", NG_PNG, INT_MAX }, { L"NI", NI_PNG, INT_MAX }, { L"NL", NL_PNG, INT_MAX }, { L"NO", NO_PNG, INT_MAX }, + { L"NP", NP_PNG, INT_MAX }, { L"NR", NR_PNG, INT_MAX }, { L"NU", NU_PNG, INT_MAX }, { L"NZ", NZ_PNG, INT_MAX }, + { L"OM", OM_PNG, INT_MAX }, + { L"PA", PA_PNG, INT_MAX }, { L"PE", PE_PNG, INT_MAX }, { L"PF", PF_PNG, INT_MAX }, { L"PG", PG_PNG, INT_MAX }, + { L"PH", PH_PNG, INT_MAX }, { L"PK", PK_PNG, INT_MAX }, { L"PL", PL_PNG, INT_MAX }, { L"PM", PM_PNG, INT_MAX }, + { L"PN", PN_PNG, INT_MAX }, { L"PR", PR_PNG, INT_MAX }, { L"PS", PS_PNG, INT_MAX }, { L"PT", PT_PNG, INT_MAX }, + { L"PW", PW_PNG, INT_MAX }, { L"PY", PY_PNG, INT_MAX }, + { L"QA", QA_PNG, INT_MAX }, + { L"RE", RE_PNG, INT_MAX }, { L"RO", RO_PNG, INT_MAX }, { L"RS", RS_PNG, INT_MAX }, { L"RU", RU_PNG, INT_MAX }, + { L"RW", RW_PNG, INT_MAX }, + { L"SA", SA_PNG, INT_MAX }, { L"SB", SB_PNG, INT_MAX }, { L"SC", SC_PNG, INT_MAX }, { L"SD", SD_PNG, INT_MAX }, + { L"SE", SE_PNG, INT_MAX }, { L"SG", SG_PNG, INT_MAX }, { L"SH", SH_PNG, INT_MAX }, { L"SI", SI_PNG, INT_MAX }, + { L"SJ", SJ_PNG, INT_MAX }, { L"SK", SK_PNG, INT_MAX }, { L"SL", SL_PNG, INT_MAX }, { L"SM", SM_PNG, INT_MAX }, + { L"SN", SN_PNG, INT_MAX }, { L"SO", SO_PNG, INT_MAX }, { L"SR", SR_PNG, INT_MAX }, { L"ST", ST_PNG, INT_MAX }, + { L"SV", SV_PNG, INT_MAX }, { L"SY", SY_PNG, INT_MAX }, { L"SZ", SZ_PNG, INT_MAX }, + { L"TC", TC_PNG, INT_MAX }, { L"TD", TD_PNG, INT_MAX }, { L"TF", TF_PNG, INT_MAX }, { L"TG", TG_PNG, INT_MAX }, + { L"TH", TH_PNG, INT_MAX }, { L"TJ", TJ_PNG, INT_MAX }, { L"TK", TK_PNG, INT_MAX }, { L"TL", TL_PNG, INT_MAX }, + { L"TM", TM_PNG, INT_MAX }, { L"TN", TN_PNG, INT_MAX }, { L"TO", TO_PNG, INT_MAX }, { L"TR", TR_PNG, INT_MAX }, + { L"TT", TT_PNG, INT_MAX }, { L"TV", TV_PNG, INT_MAX }, { L"TW", TW_PNG, INT_MAX }, { L"TZ", TZ_PNG, INT_MAX }, + { L"UA", UA_PNG, INT_MAX }, { L"UG", UG_PNG, INT_MAX }, { L"UM", UM_PNG, INT_MAX }, { L"US", US_PNG, INT_MAX }, + { L"UY", UY_PNG, INT_MAX }, { L"UZ", UZ_PNG, INT_MAX }, + { L"VA", VA_PNG, INT_MAX }, { L"VC", VC_PNG, INT_MAX }, { L"VE", VE_PNG, INT_MAX }, { L"VG", VG_PNG, INT_MAX }, + { L"VI", VI_PNG, INT_MAX }, { L"VN", VN_PNG, INT_MAX }, { L"VU", VU_PNG, INT_MAX }, + { L"WF", WF_PNG, INT_MAX }, { L"WS", WS_PNG, INT_MAX }, + { L"YE", YE_PNG, INT_MAX }, { L"YT", YT_PNG, INT_MAX }, + { L"ZA", ZA_PNG, INT_MAX }, { L"ZM", ZM_PNG, INT_MAX }, { L"ZW", ZW_PNG, INT_MAX } }; -INT LookupResourceCode( +INT LookupCountryIcon( _In_ PPH_STRING Name ) { + if (!GeoImageList) + return INT_MAX; + for (INT i = 0; i < ARRAYSIZE(CountryResourceTable); i++) { if (PhEqualString2(Name, CountryResourceTable[i].CountryCode, TRUE)) { - return CountryResourceTable[i].ResourceID; + if (CountryResourceTable[i].IconIndex == INT_MAX) + { + HBITMAP countryBitmap; + + if (countryBitmap = PhLoadPngImageFromResource( + PluginInstance->DllBase, + 16, + 11, + MAKEINTRESOURCE(CountryResourceTable[i].ResourceID), + TRUE + )) + { + CountryResourceTable[i].IconIndex = ImageList_Add( + GeoImageList, + countryBitmap, + NULL + ); + DeleteBitmap(countryBitmap); + } + } + + return CountryResourceTable[i].IconIndex; } } - return 0; -} \ No newline at end of file + return INT_MAX; +} + +VOID DrawCountryIcon( + _In_ HDC hdc, + _In_ RECT rect, + _In_ INT Index + ) +{ + if (!GeoImageList) + return; + + ImageList_Draw( + GeoImageList, + Index, + hdc, + rect.left, + rect.top + ((rect.bottom - rect.top) - 11) / 2, + ILD_NORMAL + ); +} diff --git a/plugins/NetworkTools/main.c b/plugins/NetworkTools/main.c index 10922e4581cd..86f3b3e5f5cd 100644 --- a/plugins/NetworkTools/main.c +++ b/plugins/NetworkTools/main.c @@ -1,732 +1,1007 @@ -/* - * Process Hacker Network Tools - - * Main program - * - * Copyright (C) 2010-2011 wj32 - * Copyright (C) 2013-2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "nettools.h" -#include "tracert.h" -#include - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION NetworkMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; -HWND NetworkTreeNewHandle = NULL; - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - LoadGeoLiteDb(); -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ShowOptionsDialog((HWND)Parameter); -} - -HRESULT CALLBACK ElevateActionCallbackProc( - _In_ HWND hwnd, - _In_ UINT uNotification, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - switch (uNotification) - { - case TDN_CREATED: - SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE); - break; - } - - return S_OK; -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = (PPH_PLUGIN_MENU_ITEM)Parameter; - PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)menuItem->Context; - - switch (menuItem->Id) - { - case NETWORK_ACTION_PING: - ShowPingWindow(networkItem); - break; - case NETWORK_ACTION_TRACEROUTE: - ShowTracertWindow(networkItem); - break; - case NETWORK_ACTION_WHOIS: - ShowWhoisWindow(networkItem); - break; - case MAINMENU_ACTION_PING: - { - PH_IP_ENDPOINT RemoteEndpoint; - PPH_STRING selectedChoice = NULL; - - while (PhaChoiceDialog( - menuItem->OwnerWindow, - L"Ping", - L"IP address:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - SETTING_NAME_TRACERT_HISTORY - )) - { - PWSTR terminator = NULL; - - if (NT_SUCCESS(RtlIpv4StringToAddress( - selectedChoice->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - ShowPingWindowFromAddress(RemoteEndpoint); - break; - } - - if (NT_SUCCESS(RtlIpv6StringToAddress( - selectedChoice->Buffer, - &terminator, - &RemoteEndpoint.Address.In6Addr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowPingWindowFromAddress(RemoteEndpoint); - break; - } - } - } - break; - case MAINMENU_ACTION_TRACERT: - { - PH_IP_ENDPOINT RemoteEndpoint; - PPH_STRING selectedChoice = NULL; - - while (PhaChoiceDialog( - menuItem->OwnerWindow, - L"Tracert", - L"IP address:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - SETTING_NAME_TRACERT_HISTORY - )) - { - PWSTR terminator = NULL; - - if (NT_SUCCESS(RtlIpv4StringToAddress( - selectedChoice->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - ShowTracertWindowFromAddress(RemoteEndpoint); - break; - } - - if (NT_SUCCESS(RtlIpv6StringToAddress( - selectedChoice->Buffer, - &terminator, - &RemoteEndpoint.Address.In6Addr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowTracertWindowFromAddress(RemoteEndpoint); - break; - } - } - } - break; - case MAINMENU_ACTION_WHOIS: - { - PH_IP_ENDPOINT RemoteEndpoint; - PPH_STRING selectedChoice = NULL; - - while (PhaChoiceDialog( - menuItem->OwnerWindow, - L"Whois", - L"IP address for Whois:", - NULL, - 0, - NULL, - PH_CHOICE_DIALOG_USER_CHOICE, - &selectedChoice, - NULL, - SETTING_NAME_TRACERT_HISTORY - )) - { - PWSTR terminator = NULL; - - if (NT_SUCCESS(RtlIpv4StringToAddress( - selectedChoice->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - ShowWhoisWindowFromAddress(RemoteEndpoint); - break; - } - - if (NT_SUCCESS(RtlIpv6StringToAddress( - selectedChoice->Buffer, - &terminator, - &RemoteEndpoint.Address.In6Addr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowWhoisWindowFromAddress(RemoteEndpoint); - break; - } - } - } - break; - case MAINMENU_ACTION_GEOIP_UPDATE: - { - if (PhGetOwnTokenAttributes().Elevated) - { - ShowGeoIPUpdateDialog(NULL); - } - else - { - TASKDIALOGCONFIG config = { sizeof(config) }; - TASKDIALOG_BUTTON buttons[1]; - INT button; - - config.dwFlags = TDF_CAN_BE_MINIMIZED; - config.pszWindowTitle = L"Process Hacker"; - config.pszMainIcon = TD_ERROR_ICON; - config.pszMainInstruction = L"Unable to update the GeoIP database."; - config.pszContent = L"You will need to provide administrator permission. " - L"Click Continue to complete this operation."; - config.dwCommonButtons = TDCBF_CANCEL_BUTTON; - - buttons[0].nButtonID = IDYES; - buttons[0].pszButtonText = L"Continue"; - - config.cButtons = 1; - config.pButtons = buttons; - config.nDefaultButton = IDYES; - - config.pfCallback = ElevateActionCallbackProc; - - if (TaskDialogIndirect( - &config, - &button, - NULL, - NULL - ) == S_OK && button == IDYES) - { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - - if (PhShellProcessHacker( - PhMainWndHandle, - NULL, - SW_SHOW, - PH_SHELL_EXECUTE_ADMIN, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - )) - { - ProcessHacker_Destroy(PhMainWndHandle); - } - else - { - ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); - } - } - } - } - break; - } -} - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_EMENU_ITEM networkToolsMenu; - - if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_TOOLS) - return; - - networkToolsMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Network Tools", NULL); - PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_GEOIP_UPDATE, L"GeoIP database update...", NULL), -1); - PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_PING, L"Ping address...", NULL), -1); - PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_TRACERT, L"Traceroute address...", NULL), -1); - PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_WHOIS, L"Whois address...", NULL), -1); - PhInsertEMenuItem(menuInfo->Menu, networkToolsMenu, -1); -} - -VOID NTAPI NetworkMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = (PPH_PLUGIN_MENU_INFORMATION)Parameter; - PPH_NETWORK_ITEM networkItem; - PPH_EMENU_ITEM whoisMenu; - PPH_EMENU_ITEM traceMenu; - PPH_EMENU_ITEM pingMenu; - - if (menuInfo->u.Network.NumberOfNetworkItems == 1) - networkItem = menuInfo->u.Network.NetworkItems[0]; - else - networkItem = NULL; - - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), 0); - PhInsertEMenuItem(menuInfo->Menu, whoisMenu = PhPluginCreateEMenuItem(PluginInstance, 0, NETWORK_ACTION_WHOIS, L"Whois", networkItem), 0); - PhInsertEMenuItem(menuInfo->Menu, traceMenu = PhPluginCreateEMenuItem(PluginInstance, 0, NETWORK_ACTION_TRACEROUTE, L"Traceroute", networkItem), 0); - PhInsertEMenuItem(menuInfo->Menu, pingMenu = PhPluginCreateEMenuItem(PluginInstance, 0, NETWORK_ACTION_PING, L"Ping", networkItem), 0); - - if (networkItem) - { - if (PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address)) - { - whoisMenu->Flags |= PH_EMENU_DISABLED; - traceMenu->Flags |= PH_EMENU_DISABLED; - pingMenu->Flags |= PH_EMENU_DISABLED; - } - else if (networkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - if (IN4_IS_ADDR_LOOPBACK(&networkItem->RemoteEndpoint.Address.InAddr)) - { - whoisMenu->Flags |= PH_EMENU_DISABLED; - traceMenu->Flags |= PH_EMENU_DISABLED; - pingMenu->Flags |= PH_EMENU_DISABLED; - } - } - else - { - if (IN6_IS_ADDR_LOOPBACK(&networkItem->RemoteEndpoint.Address.In6Addr)) - { - whoisMenu->Flags |= PH_EMENU_DISABLED; - traceMenu->Flags |= PH_EMENU_DISABLED; - pingMenu->Flags |= PH_EMENU_DISABLED; - } - } - } - else - { - whoisMenu->Flags |= PH_EMENU_DISABLED; - traceMenu->Flags |= PH_EMENU_DISABLED; - pingMenu->Flags |= PH_EMENU_DISABLED; - } -} - -LONG NTAPI NetworkServiceSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PPH_NETWORK_NODE node1 = Node1; - PPH_NETWORK_NODE node2 = Node2; - PNETWORK_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->NetworkItem, EmNetworkItemType); - PNETWORK_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->NetworkItem, EmNetworkItemType); - - switch (SubId) - { - case NETWORK_COLUMN_ID_REMOTE_COUNTRY: - return PhCompareStringWithNull(extension1->RemoteCountryCode, extension2->RemoteCountryCode, TRUE); - } - - return 0; -} - -VOID NTAPI NetworkTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PH_TREENEW_COLUMN column; - - *(HWND*)Context = info->TreeNewHandle; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"Country"; - column.Width = 140; - column.Alignment = PH_ALIGN_LEFT; - column.CustomDraw = TRUE; // Owner-draw this column to show country flags - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_REMOTE_COUNTRY, NULL, NetworkServiceSortFunction); - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"Local service"; - column.Width = 140; - column.Alignment = PH_ALIGN_LEFT; - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_LOCAL_SERVICE, NULL, NetworkServiceSortFunction); - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"Remote service"; - column.Width = 140; - column.Alignment = PH_ALIGN_LEFT; - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_REMOTE_SERVICE, NULL, NetworkServiceSortFunction); -} - -VOID NTAPI NetworkItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - //PPH_NETWORK_ITEM networkItem = Object; - PNETWORK_EXTENSION extension = Extension; - - memset(extension, 0, sizeof(NETWORK_EXTENSION)); -} - -VOID NTAPI NetworkItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - //PPH_NETWORK_ITEM networkItem = Object; - PNETWORK_EXTENSION extension = Extension; - - PhClearReference(&extension->RemoteCountryCode); - PhClearReference(&extension->RemoteCountryName); - - if (extension->CountryIcon) - DestroyIcon(extension->CountryIcon); -} - -VOID NTAPI NetworkNodeCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_NETWORK_NODE networkNode = Object; - PNETWORK_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, networkNode->NetworkItem, EmNetworkItemType); - - if (!extension->CountryValid) - { - PPH_STRING remoteCountryCode; - PPH_STRING remoteCountryName; - - if (LookupCountryCode( - networkNode->NetworkItem->RemoteEndpoint.Address, - &remoteCountryCode, - &remoteCountryName - )) - { - PhMoveReference(&extension->RemoteCountryCode, remoteCountryCode); - PhMoveReference(&extension->RemoteCountryName, remoteCountryName); - } - - extension->CountryValid = TRUE; - } -} - -VOID UpdateNetworkNode( - _In_ NETWORK_COLUMN_ID ColumnID, - _In_ PPH_NETWORK_NODE Node, - _In_ PNETWORK_EXTENSION Extension - ) -{ - switch (ColumnID) - { - case NETWORK_COLUMN_ID_LOCAL_SERVICE: - { - if (!Extension->LocalValid) - { - for (ULONG x = 0; x < ARRAYSIZE(ResolvedPortsTable); x++) - { - if (Node->NetworkItem->LocalEndpoint.Port == ResolvedPortsTable[x].Port) - { - //PhAppendFormatStringBuilder(&stringBuilder, L"%s,", ResolvedPortsTable[x].Name); - PhMoveReference(&Extension->LocalServiceName, PhCreateString(ResolvedPortsTable[x].Name)); - break; - } - } - - Extension->LocalValid = TRUE; - } - } - break; - case NETWORK_COLUMN_ID_REMOTE_SERVICE: - { - if (!Extension->RemoteValid) - { - for (ULONG x = 0; x < ARRAYSIZE(ResolvedPortsTable); x++) - { - if (Node->NetworkItem->RemoteEndpoint.Port == ResolvedPortsTable[x].Port) - { - //PhAppendFormatStringBuilder(&stringBuilder, L"%s,", ResolvedPortsTable[x].Name); - PhMoveReference(&Extension->RemoteServiceName, PhCreateString(ResolvedPortsTable[x].Name)); - break; - } - } - - Extension->RemoteValid = TRUE; - } - } - break; - } -} - -VOID NTAPI TreeNewMessageCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - - switch (message->Message) - { - case TreeNewGetCellText: - { - if (message->TreeNewHandle == NetworkTreeNewHandle) - { - PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; - PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)getCellText->Node; - PNETWORK_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, networkNode->NetworkItem, EmNetworkItemType); - - UpdateNetworkNode(message->SubId, networkNode, extension); - - switch (message->SubId) - { - case NETWORK_COLUMN_ID_REMOTE_COUNTRY: - getCellText->Text = PhGetStringRef(extension->RemoteCountryName); - break; - case NETWORK_COLUMN_ID_LOCAL_SERVICE: - getCellText->Text = PhGetStringRef(extension->LocalServiceName); - break; - case NETWORK_COLUMN_ID_REMOTE_SERVICE: - getCellText->Text = PhGetStringRef(extension->RemoteServiceName); - break; - } - } - } - break; - case TreeNewCustomDraw: - { - PPH_TREENEW_CUSTOM_DRAW customDraw = message->Parameter1; - PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)customDraw->Node; - PNETWORK_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, networkNode->NetworkItem, EmNetworkItemType); - HDC hdc = customDraw->Dc; - RECT rect = customDraw->CellRect; - - // Check if this is the country column - if (message->SubId != NETWORK_COLUMN_ID_REMOTE_COUNTRY) - break; - - // Check if there's something to draw - if (rect.right - rect.left <= 1) - { - // nothing to draw - break; - } - - // Padding - rect.left += 5; - - // Draw the column data - if (GeoDbLoaded && extension->RemoteCountryCode && extension->RemoteCountryName) - { - if (!extension->CountryIcon) - { - INT resourceCode; - HBITMAP countryBitmap; - - if ((resourceCode = LookupResourceCode(extension->RemoteCountryCode)) != 0) - { - if (countryBitmap = LoadImageFromResources(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) - { - extension->CountryIcon = CommonBitmapToIcon(countryBitmap, 16, 11); - } - } - } - - if (extension->CountryIcon) - { - DrawIconEx( - hdc, - rect.left, - rect.top + ((rect.bottom - rect.top) - 11) / 2, - extension->CountryIcon, - 16, - 11, - 0, - NULL, - DI_NORMAL - ); - - rect.left += 16 + 2; - } - - DrawText( - hdc, - extension->RemoteCountryName->Buffer, - (INT)extension->RemoteCountryName->Length / 2, - &rect, - DT_LEFT | DT_VCENTER | DT_SINGLELINE - ); - } - - if (GeoDbExpired && !extension->CountryIcon) - { - DrawText(hdc, L"Geoip database expired.", -1, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); - } - - if (!GeoDbLoaded) - { - DrawText(hdc, L"Geoip database not found.", -1, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); - } - } - break; - } -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { IntegerPairSettingType, SETTING_NAME_PING_WINDOW_POSITION, L"0,0" }, - { ScalableIntegerPairSettingType, SETTING_NAME_PING_WINDOW_SIZE, L"@96|420,250" }, - { IntegerSettingType, SETTING_NAME_PING_MINIMUM_SCALING, L"1F4" }, // 500ms minimum scaling - { IntegerSettingType, SETTING_NAME_PING_SIZE, L"20" }, // 32 byte packet - { IntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_POSITION, L"0,0" }, - { ScalableIntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_SIZE, L"@96|600,365" }, - { StringSettingType, SETTING_NAME_TRACERT_LIST_COLUMNS, L"" }, - { StringSettingType, SETTING_NAME_TRACERT_HISTORY, L"" }, - { IntegerSettingType, SETTING_NAME_TRACERT_MAX_HOPS, L"30" }, - { IntegerPairSettingType, SETTING_NAME_WHOIS_WINDOW_POSITION, L"0,0" }, - { ScalableIntegerPairSettingType, SETTING_NAME_WHOIS_WINDOW_SIZE, L"@96|600,365" }, - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Network Tools"; - info->Author = L"dmex, wj32"; - info->Description = L"Provides ping, traceroute and whois for network connections."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1117"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), - MainMenuInitializingCallback, - NULL, - &MainMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkMenuInitializing), - NetworkMenuInitializingCallback, - NULL, - &NetworkMenuInitializingCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), - NetworkTreeNewInitializingCallback, - &NetworkTreeNewHandle, - &NetworkTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), - TreeNewMessageCallback, - NULL, - &TreeNewMessageCallbackRegistration - ); - - PhPluginSetObjectExtension( - PluginInstance, - EmNetworkItemType, - sizeof(NETWORK_EXTENSION), - NetworkItemCreateCallback, - NetworkItemDeleteCallback - ); - PhPluginSetObjectExtension( - PluginInstance, - EmNetworkNodeType, - sizeof(NETWORK_EXTENSION), - NetworkNodeCreateCallback, - NULL - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - } - - return TRUE; -} \ No newline at end of file +/* + * Process Hacker Network Tools - + * Main program + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2013-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "nettools.h" + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION NetworkMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; + +HWND NetworkTreeNewHandle = NULL; +BOOLEAN NetworkExtensionEnabled = FALSE; +LIST_ENTRY NetworkExtensionListHead = { &NetworkExtensionListHead, &NetworkExtensionListHead }; +PH_QUEUED_LOCK NetworkExtensionListLock = PH_QUEUED_LOCK_INIT; + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + // HACK: The GetPerTcpConnectionEStats function requires administrative privileges + // but returns success instead of access denied. + if (PhGetOwnTokenAttributes().Elevated) + { + NetworkExtensionEnabled = !!PhGetIntegerSetting(SETTING_NAME_EXTENDED_TCP_STATS); + } + + LoadGeoLiteDb(); +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"NetworkTools", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); +} + +static BOOLEAN ParseNetworkAddress( + _In_ PWSTR AddressString, + _Out_ PPH_IP_ENDPOINT RemoteEndpoint + ) +{ + NET_ADDRESS_INFO addressInfo; + + memset(&addressInfo, 0, sizeof(NET_ADDRESS_INFO)); + + if (ParseNetworkString( + AddressString, + NET_STRING_ANY_ADDRESS | NET_STRING_ANY_SERVICE, + &addressInfo, + NULL, + NULL + ) != ERROR_SUCCESS) + { + return FALSE; + } + + if (addressInfo.Format == NET_ADDRESS_DNS_NAME) + { + BOOLEAN success = FALSE; + PADDRINFOT result; + WSADATA wsaData; + + WSAStartup(WINSOCK_VERSION, &wsaData); + + if (GetAddrInfo(addressInfo.NamedAddress.Address, addressInfo.NamedAddress.Port, NULL, &result) == ERROR_SUCCESS) + { + for (PADDRINFOT i = result; i; i = i->ai_next) + { + if (i->ai_family == AF_INET) + { + RemoteEndpoint->Address.InAddr.s_addr = ((PSOCKADDR_IN)i->ai_addr)->sin_addr.s_addr; + RemoteEndpoint->Port = _byteswap_ushort(((PSOCKADDR_IN)i->ai_addr)->sin_port); + RemoteEndpoint->Address.Type = PH_IPV4_NETWORK_TYPE; + success = TRUE; + break; + } + else if (i->ai_family == AF_INET6) + { + memcpy_s( + RemoteEndpoint->Address.In6Addr.s6_addr, + sizeof(RemoteEndpoint->Address.In6Addr.s6_addr), + ((PSOCKADDR_IN6)i->ai_addr)->sin6_addr.s6_addr, + sizeof(((PSOCKADDR_IN6)i->ai_addr)->sin6_addr.s6_addr) + ); + RemoteEndpoint->Port = _byteswap_ushort(((PSOCKADDR_IN6)i->ai_addr)->sin6_port); + RemoteEndpoint->Address.Type = PH_IPV6_NETWORK_TYPE; + success = TRUE; + break; + } + } + + FreeAddrInfo(result); + } + + WSACleanup(); + + if (success) + return TRUE; + } + + if (addressInfo.Format == NET_ADDRESS_IPV4) + { + RemoteEndpoint->Address.InAddr.s_addr = addressInfo.Ipv4Address.sin_addr.s_addr; + RemoteEndpoint->Port = _byteswap_ushort(addressInfo.Ipv4Address.sin_port); + RemoteEndpoint->Address.Type = PH_IPV4_NETWORK_TYPE; + return TRUE; + } + + if (addressInfo.Format == NET_ADDRESS_IPV6) + { + memcpy_s( + RemoteEndpoint->Address.In6Addr.s6_addr, + sizeof(RemoteEndpoint->Address.In6Addr.s6_addr), + addressInfo.Ipv6Address.sin6_addr.s6_addr, + sizeof(addressInfo.Ipv6Address.sin6_addr.s6_addr) + ); + RemoteEndpoint->Port = _byteswap_ushort(addressInfo.Ipv6Address.sin6_port); + RemoteEndpoint->Address.Type = PH_IPV6_NETWORK_TYPE; + return TRUE; + } + + return FALSE; +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = (PPH_PLUGIN_MENU_ITEM)Parameter; + PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)menuItem->Context; + + switch (menuItem->Id) + { + case NETWORK_ACTION_PING: + ShowPingWindow(networkItem); + break; + case NETWORK_ACTION_TRACEROUTE: + ShowTracertWindow(networkItem); + break; + case NETWORK_ACTION_WHOIS: + ShowWhoisWindow(networkItem); + break; + case MAINMENU_ACTION_PING: + { + PPH_STRING selectedChoice = NULL; + PH_IP_ENDPOINT remoteEndpoint = { 0 }; + + while (PhaChoiceDialog( + menuItem->OwnerWindow, + L"Ping", + L"Hostname or IP address:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + SETTING_NAME_ADDRESS_HISTORY + )) + { + if (ParseNetworkAddress(selectedChoice->Buffer, &remoteEndpoint)) + { + ShowPingWindowFromAddress(remoteEndpoint); + break; + } + } + } + break; + case MAINMENU_ACTION_TRACERT: + { + PPH_STRING selectedChoice = NULL; + PH_IP_ENDPOINT remoteEndpoint = { 0 }; + + while (PhaChoiceDialog( + menuItem->OwnerWindow, + L"Tracert", + L"Hostname or IP address:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + SETTING_NAME_ADDRESS_HISTORY + )) + { + if (ParseNetworkAddress(selectedChoice->Buffer, &remoteEndpoint)) + { + ShowTracertWindowFromAddress(remoteEndpoint); + break; + } + } + } + break; + case MAINMENU_ACTION_WHOIS: + { + PPH_STRING selectedChoice = NULL; + PH_IP_ENDPOINT remoteEndpoint = { 0 }; + + while (PhaChoiceDialog( + menuItem->OwnerWindow, + L"Whois", + L"Hostname or IP address:", + NULL, + 0, + NULL, + PH_CHOICE_DIALOG_USER_CHOICE, + &selectedChoice, + NULL, + SETTING_NAME_ADDRESS_HISTORY + )) + { + if (ParseNetworkAddress(selectedChoice->Buffer, &remoteEndpoint)) + { + ShowWhoisWindowFromAddress(remoteEndpoint); + break; + } + } + } + break; + case MAINMENU_ACTION_GEOIP_UPDATE: + ShowGeoIPUpdateDialog(NULL); + break; + } +} + +VOID NTAPI MainMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_EMENU_ITEM networkToolsMenu; + + if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_TOOLS) + return; + + networkToolsMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"&Network Tools", NULL); + PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_GEOIP_UPDATE, L"&GeoIP database update...", NULL), ULONG_MAX); + PhInsertEMenuItem(networkToolsMenu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_PING, L"&Ping address...", NULL), ULONG_MAX); + PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_TRACERT, L"&Traceroute address...", NULL), ULONG_MAX); + PhInsertEMenuItem(networkToolsMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MAINMENU_ACTION_WHOIS, L"&Whois address...", NULL), ULONG_MAX); + PhInsertEMenuItem(menuInfo->Menu, networkToolsMenu, ULONG_MAX); +} + +VOID NTAPI NetworkMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = (PPH_PLUGIN_MENU_INFORMATION)Parameter; + PPH_NETWORK_ITEM networkItem; + PPH_EMENU_ITEM whoisMenu; + PPH_EMENU_ITEM traceMenu; + PPH_EMENU_ITEM pingMenu; + + if (menuInfo->u.Network.NumberOfNetworkItems == 1) + networkItem = menuInfo->u.Network.NetworkItems[0]; + else + networkItem = NULL; + + PhInsertEMenuItem(menuInfo->Menu, pingMenu = PhPluginCreateEMenuItem(PluginInstance, 0, NETWORK_ACTION_PING, L"&Ping", networkItem), 0); + PhInsertEMenuItem(menuInfo->Menu, traceMenu = PhPluginCreateEMenuItem(PluginInstance, 0, NETWORK_ACTION_TRACEROUTE, L"&Traceroute", networkItem), 1); + PhInsertEMenuItem(menuInfo->Menu, whoisMenu = PhPluginCreateEMenuItem(PluginInstance, 0, NETWORK_ACTION_WHOIS, L"&Whois", networkItem), 2); + PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), 3); + + if (networkItem) + { + if (PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address)) + { + PhSetDisabledEMenuItem(whoisMenu); + PhSetDisabledEMenuItem(traceMenu); + PhSetDisabledEMenuItem(pingMenu); + } + else if (networkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + if ( + IN4_IS_ADDR_UNSPECIFIED(&networkItem->RemoteEndpoint.Address.InAddr) || + IN4_IS_ADDR_LOOPBACK(&networkItem->RemoteEndpoint.Address.InAddr) + ) + { + PhSetDisabledEMenuItem(whoisMenu); + PhSetDisabledEMenuItem(traceMenu); + PhSetDisabledEMenuItem(pingMenu); + } + + if (IN4_IS_ADDR_RFC1918(&networkItem->RemoteEndpoint.Address.InAddr)) + { + PhSetDisabledEMenuItem(whoisMenu); + } + } + else if (networkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + if ( + IN6_IS_ADDR_UNSPECIFIED(&networkItem->RemoteEndpoint.Address.In6Addr) || + IN6_IS_ADDR_LOOPBACK(&networkItem->RemoteEndpoint.Address.In6Addr) + ) + { + PhSetDisabledEMenuItem(whoisMenu); + PhSetDisabledEMenuItem(traceMenu); + PhSetDisabledEMenuItem(pingMenu); + } + } + } + else + { + PhSetDisabledEMenuItem(whoisMenu); + PhSetDisabledEMenuItem(traceMenu); + PhSetDisabledEMenuItem(pingMenu); + } +} + +LONG NTAPI NetworkServiceSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PPH_NETWORK_NODE node1 = Node1; + PPH_NETWORK_NODE node2 = Node2; + PNETWORK_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->NetworkItem, EmNetworkItemType); + PNETWORK_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->NetworkItem, EmNetworkItemType); + + switch (SubId) + { + case NETWORK_COLUMN_ID_REMOTE_COUNTRY: + return PhCompareStringWithNullSortOrder(extension1->RemoteCountryCode, extension2->RemoteCountryCode, SortOrder, TRUE); + case NETWORK_COLUMN_ID_LOCAL_SERVICE: + return PhCompareStringWithNullSortOrder(extension1->LocalServiceName, extension2->LocalServiceName, SortOrder, TRUE); + case NETWORK_COLUMN_ID_REMOTE_SERVICE: + return PhCompareStringWithNullSortOrder(extension1->RemoteServiceName, extension2->RemoteServiceName, SortOrder, TRUE); + case NETWORK_COLUMN_ID_BYTES_IN: + return uint64cmp(extension1->NumberOfBytesIn, extension2->NumberOfBytesIn); + case NETWORK_COLUMN_ID_BYTES_OUT: + return uint64cmp(extension1->NumberOfBytesOut, extension2->NumberOfBytesOut); + case NETWORK_COLUMN_ID_PACKETLOSS: + return uint64cmp(extension1->NumberOfLostPackets, extension2->NumberOfLostPackets); + case NETWORK_COLUMN_ID_LATENCY: + return uint64cmp(extension1->SampleRtt, extension2->SampleRtt); + } + + return 0; +} + +VOID NTAPI NetworkTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PH_TREENEW_COLUMN column; + + *(HWND*)Context = info->TreeNewHandle; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Country"; + column.Width = 140; + column.Alignment = PH_ALIGN_LEFT; + column.CustomDraw = TRUE; // Owner-draw this column to show country flags + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_REMOTE_COUNTRY, NULL, NetworkServiceSortFunction); + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Local service"; + column.Width = 140; + column.Alignment = PH_ALIGN_LEFT; + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_LOCAL_SERVICE, NULL, NetworkServiceSortFunction); + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Remote service"; + column.Width = 140; + column.Alignment = PH_ALIGN_LEFT; + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_REMOTE_SERVICE, NULL, NetworkServiceSortFunction); + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Total bytes in"; + column.Width = 80; + column.Alignment = PH_ALIGN_LEFT; + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_BYTES_IN, NULL, NetworkServiceSortFunction); + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Total bytes out"; + column.Width = 80; + column.Alignment = PH_ALIGN_LEFT; + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_BYTES_OUT, NULL, NetworkServiceSortFunction); + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Packet loss"; + column.Width = 80; + column.Alignment = PH_ALIGN_LEFT; + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_PACKETLOSS, NULL, NetworkServiceSortFunction); + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Latency (ms)"; + column.Width = 80; + column.Alignment = PH_ALIGN_LEFT; + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, NETWORK_COLUMN_ID_LATENCY, NULL, NetworkServiceSortFunction); +} + +VOID NTAPI NetworkItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension +) +{ + PPH_NETWORK_ITEM networkItem = Object; + PNETWORK_EXTENSION extension = Extension; + + memset(extension, 0, sizeof(NETWORK_EXTENSION)); + + extension->NetworkItem = networkItem; + extension->CountryIconIndex = INT_MAX; + + if (NetworkExtensionEnabled) + { + PhAcquireQueuedLockExclusive(&NetworkExtensionListLock); + InsertTailList(&NetworkExtensionListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&NetworkExtensionListLock); + } +} + +VOID NTAPI NetworkItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + //PPH_NETWORK_ITEM networkItem = Object; + PNETWORK_EXTENSION extension = Extension; + + if (NetworkExtensionEnabled) + { + PhAcquireQueuedLockExclusive(&NetworkExtensionListLock); + RemoveEntryList(&extension->ListEntry); + PhReleaseQueuedLockExclusive(&NetworkExtensionListLock); + } + + if (extension->LocalServiceName) + PhDereferenceObject(extension->LocalServiceName); + if (extension->RemoteServiceName) + PhDereferenceObject(extension->RemoteServiceName); + if (extension->RemoteCountryCode) + PhDereferenceObject(extension->RemoteCountryCode); + if (extension->RemoteCountryName) + PhDereferenceObject(extension->RemoteCountryName); + if (extension->BytesIn) + PhDereferenceObject(extension->BytesIn); + if (extension->BytesOut) + PhDereferenceObject(extension->BytesOut); + if (extension->PacketLossText) + PhDereferenceObject(extension->PacketLossText); + if (extension->LatencyText) + PhDereferenceObject(extension->LatencyText); +} + +FORCEINLINE VOID PhpNetworkItemToRow( + _Out_ PMIB_TCPROW Row, + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + Row->dwState = NetworkItem->State; + Row->dwLocalAddr = NetworkItem->LocalEndpoint.Address.Ipv4; + Row->dwLocalPort = _byteswap_ushort((USHORT)NetworkItem->LocalEndpoint.Port); + Row->dwRemoteAddr = NetworkItem->RemoteEndpoint.Address.Ipv4; + Row->dwRemotePort = _byteswap_ushort((USHORT)NetworkItem->RemoteEndpoint.Port); +} + +FORCEINLINE VOID PhpNetworkItemToRow6( + _Out_ PMIB_TCP6ROW Row, + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + Row->State = NetworkItem->State; + memcpy(Row->LocalAddr.u.Byte, NetworkItem->LocalEndpoint.Address.Ipv6, 16); + Row->dwLocalScopeId = NetworkItem->LocalScopeId; + Row->dwLocalPort = _byteswap_ushort((USHORT)NetworkItem->LocalEndpoint.Port); + memcpy(Row->RemoteAddr.u.Byte, NetworkItem->RemoteEndpoint.Address.Ipv6, 16); + Row->dwRemoteScopeId = NetworkItem->RemoteScopeId; + Row->dwRemotePort = _byteswap_ushort((USHORT)NetworkItem->RemoteEndpoint.Port); +} + +VOID NTAPI NetworkNodeCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_NETWORK_NODE networkNode = Object; + PNETWORK_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, networkNode->NetworkItem, EmNetworkItemType); + + if (!extension->CountryValid) + { + PPH_STRING remoteCountryCode; + PPH_STRING remoteCountryName; + + if (LookupCountryCode( + networkNode->NetworkItem->RemoteEndpoint.Address, + &remoteCountryCode, + &remoteCountryName + )) + { + PhMoveReference(&extension->RemoteCountryCode, remoteCountryCode); + PhMoveReference(&extension->RemoteCountryName, remoteCountryName); + } + + extension->CountryValid = TRUE; + } + + if (!extension->StatsEnabled) + { + if (NetworkExtensionEnabled) + { + if (networkNode->NetworkItem->ProtocolType == PH_TCP4_NETWORK_PROTOCOL) + { + MIB_TCPROW tcpRow; + TCP_ESTATS_DATA_RW_v0 dataRw; + TCP_ESTATS_PATH_RW_v0 pathRw; + + dataRw.EnableCollection = TRUE; + pathRw.EnableCollection = TRUE; + + PhpNetworkItemToRow(&tcpRow, networkNode->NetworkItem); + + SetPerTcpConnectionEStats(&tcpRow, TcpConnectionEstatsData, (PUCHAR)&dataRw, 0, sizeof(TCP_ESTATS_DATA_RW_v0), 0); + SetPerTcpConnectionEStats(&tcpRow, TcpConnectionEstatsPath, (PUCHAR)&pathRw, 0, sizeof(TCP_ESTATS_PATH_RW_v0), 0); + } + else if (networkNode->NetworkItem->ProtocolType == PH_TCP6_NETWORK_PROTOCOL) + { + MIB_TCP6ROW tcp6Row; + TCP_ESTATS_DATA_RW_v0 dataRw; + TCP_ESTATS_PATH_RW_v0 pathRw; + + dataRw.EnableCollection = TRUE; + pathRw.EnableCollection = TRUE; + + PhpNetworkItemToRow6(&tcp6Row, networkNode->NetworkItem); + + SetPerTcp6ConnectionEStats(&tcp6Row, TcpConnectionEstatsData, (PUCHAR)&dataRw, 0, sizeof(TCP_ESTATS_DATA_RW_v0), 0); + SetPerTcp6ConnectionEStats(&tcp6Row, TcpConnectionEstatsPath, (PUCHAR)&pathRw, 0, sizeof(TCP_ESTATS_PATH_RW_v0), 0); + } + } + + extension->StatsEnabled = TRUE; + } +} + +VOID UpdateNetworkNode( + _In_ NETWORK_COLUMN_ID ColumnID, + _In_ PPH_NETWORK_NODE Node, + _In_ PNETWORK_EXTENSION Extension + ) +{ + switch (ColumnID) + { + case NETWORK_COLUMN_ID_LOCAL_SERVICE: + { + if (!Extension->LocalValid) + { + for (ULONG x = 0; x < ARRAYSIZE(ResolvedPortsTable); x++) + { + if (Node->NetworkItem->LocalEndpoint.Port == ResolvedPortsTable[x].Port) + { + //PhAppendFormatStringBuilder(&stringBuilder, L"%s,", ResolvedPortsTable[x].Name); + PhMoveReference(&Extension->LocalServiceName, PhCreateString(ResolvedPortsTable[x].Name)); + break; + } + } + + Extension->LocalValid = TRUE; + } + } + break; + case NETWORK_COLUMN_ID_REMOTE_SERVICE: + { + if (!Extension->RemoteValid) + { + for (ULONG x = 0; x < ARRAYSIZE(ResolvedPortsTable); x++) + { + if (Node->NetworkItem->RemoteEndpoint.Port == ResolvedPortsTable[x].Port) + { + //PhAppendFormatStringBuilder(&stringBuilder, L"%s,", ResolvedPortsTable[x].Name); + PhMoveReference(&Extension->RemoteServiceName, PhCreateString(ResolvedPortsTable[x].Name)); + break; + } + } + + Extension->RemoteValid = TRUE; + } + } + break; + case NETWORK_COLUMN_ID_BYTES_IN: + { + if (Extension->NumberOfBytesIn) + PhMoveReference(&Extension->BytesIn, PhFormatSize(Extension->NumberOfBytesIn, ULONG_MAX)); + + if (!NetworkExtensionEnabled && !Extension->BytesIn && PhGetOwnTokenAttributes().Elevated) + PhMoveReference(&Extension->BytesIn, PhCreateString(L"Extended TCP statisitics are disabled")); + } + break; + case NETWORK_COLUMN_ID_BYTES_OUT: + { + if (Extension->NumberOfBytesOut) + PhMoveReference(&Extension->BytesOut, PhFormatSize(Extension->NumberOfBytesOut, ULONG_MAX)); + + if (!NetworkExtensionEnabled && !Extension->BytesOut && PhGetOwnTokenAttributes().Elevated) + PhMoveReference(&Extension->BytesOut, PhCreateString(L"Extended TCP statisitics are disabled")); + } + break; + case NETWORK_COLUMN_ID_PACKETLOSS: + { + if (Extension->NumberOfLostPackets) + PhMoveReference(&Extension->PacketLossText, PhFormatUInt64(Extension->NumberOfLostPackets, TRUE)); + + if (!NetworkExtensionEnabled && !Extension->PacketLossText && PhGetOwnTokenAttributes().Elevated) + PhMoveReference(&Extension->PacketLossText, PhCreateString(L"Extended TCP statisitics are disabled")); + } + break; + case NETWORK_COLUMN_ID_LATENCY: + { + if (Extension->SampleRtt) + PhMoveReference(&Extension->LatencyText, PhFormatUInt64(Extension->SampleRtt, TRUE)); + + if (!NetworkExtensionEnabled && !Extension->LatencyText && PhGetOwnTokenAttributes().Elevated) + PhMoveReference(&Extension->LatencyText, PhCreateString(L"Extended TCP statisitics are disabled")); + } + break; + } +} + +VOID NTAPI TreeNewMessageCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + + switch (message->Message) + { + case TreeNewGetCellText: + { + if (message->TreeNewHandle == NetworkTreeNewHandle) + { + PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; + PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)getCellText->Node; + PNETWORK_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, networkNode->NetworkItem, EmNetworkItemType); + + UpdateNetworkNode(message->SubId, networkNode, extension); + + switch (message->SubId) + { + case NETWORK_COLUMN_ID_REMOTE_COUNTRY: + getCellText->Text = PhGetStringRef(extension->RemoteCountryName); + break; + case NETWORK_COLUMN_ID_LOCAL_SERVICE: + getCellText->Text = PhGetStringRef(extension->LocalServiceName); + break; + case NETWORK_COLUMN_ID_REMOTE_SERVICE: + getCellText->Text = PhGetStringRef(extension->RemoteServiceName); + break; + case NETWORK_COLUMN_ID_BYTES_IN: + getCellText->Text = PhGetStringRef(extension->BytesIn); + break; + case NETWORK_COLUMN_ID_BYTES_OUT: + getCellText->Text = PhGetStringRef(extension->BytesOut); + break; + case NETWORK_COLUMN_ID_PACKETLOSS: + getCellText->Text = PhGetStringRef(extension->PacketLossText); + break; + case NETWORK_COLUMN_ID_LATENCY: + getCellText->Text = PhGetStringRef(extension->LatencyText); + break; + } + } + } + break; + case TreeNewCustomDraw: + { + PPH_TREENEW_CUSTOM_DRAW customDraw = message->Parameter1; + PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)customDraw->Node; + PNETWORK_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, networkNode->NetworkItem, EmNetworkItemType); + HDC hdc = customDraw->Dc; + RECT rect = customDraw->CellRect; + + // Check if this is the country column + if (message->SubId != NETWORK_COLUMN_ID_REMOTE_COUNTRY) + break; + + // Check if there's something to draw + if (rect.right - rect.left <= 1) + { + // nothing to draw + break; + } + + // Padding + rect.left += 5; + + // Draw the column data + if (GeoDbLoaded && !GeoDbExpired && extension->RemoteCountryCode && extension->RemoteCountryName) + { + if (extension->CountryIconIndex == INT_MAX) + extension->CountryIconIndex = LookupCountryIcon(extension->RemoteCountryCode); + + if (extension->CountryIconIndex != INT_MAX) + { + DrawCountryIcon(hdc, rect, extension->CountryIconIndex); + rect.left += 16 + 2; + } + + DrawText( + hdc, + extension->RemoteCountryName->Buffer, + (INT)extension->RemoteCountryName->Length / sizeof(WCHAR), + &rect, + DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE + ); + } + + if (GeoDbExpired) + { + DrawText(hdc, L"Geoip database expired.", -1, &rect, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE); + } + + if (!GeoDbLoaded) + { + DrawText(hdc, L"Geoip database not found.", -1, &rect, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE); + } + } + break; + } +} + +VOID ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + static ULONG ProcessesUpdatedCount = 0; + PLIST_ENTRY listEntry; + + if (!NetworkExtensionEnabled) + return; + + if (ProcessesUpdatedCount < 2) + { + ProcessesUpdatedCount++; + return; + } + + for ( + listEntry = NetworkExtensionListHead.Flink; + listEntry != &NetworkExtensionListHead; + listEntry = listEntry->Flink + ) + { + PNETWORK_EXTENSION extension = CONTAINING_RECORD(listEntry, NETWORK_EXTENSION, ListEntry); + + if (!extension || !extension->StatsEnabled) + continue; + + if (extension->NetworkItem->ProtocolType == PH_TCP4_NETWORK_PROTOCOL) + { + MIB_TCPROW tcpRow; + TCP_ESTATS_DATA_ROD_v0 dataRod; + TCP_ESTATS_PATH_ROD_v0 pathRod; + + PhpNetworkItemToRow(&tcpRow, extension->NetworkItem); + + if (GetPerTcpConnectionEStats( + &tcpRow, + TcpConnectionEstatsData, + NULL, + 0, + 0, + NULL, + 0, + 0, + (PUCHAR)&dataRod, + 0, + sizeof(TCP_ESTATS_DATA_ROD_v0) + ) == ERROR_SUCCESS) + { + extension->NumberOfBytesIn = dataRod.DataBytesIn; + extension->NumberOfBytesOut = dataRod.DataBytesOut; + } + + if (GetPerTcpConnectionEStats( + &tcpRow, + TcpConnectionEstatsPath, + NULL, + 0, + 0, + NULL, + 0, + 0, + (PUCHAR)&pathRod, + 0, + sizeof(TCP_ESTATS_PATH_ROD_v0) + ) == ERROR_SUCCESS) + { + extension->NumberOfLostPackets = UInt32Add32To64(pathRod.FastRetran, pathRod.PktsRetrans); + extension->SampleRtt = pathRod.SampleRtt; + + if (extension->SampleRtt == ULONG_MAX) // HACK + extension->SampleRtt = 0; + } + } + else if (extension->NetworkItem->ProtocolType == PH_TCP6_NETWORK_PROTOCOL) + { + MIB_TCP6ROW tcp6Row; + TCP_ESTATS_DATA_ROD_v0 dataRod; + TCP_ESTATS_PATH_ROD_v0 pathRod; + + PhpNetworkItemToRow6(&tcp6Row, extension->NetworkItem); + + if (GetPerTcp6ConnectionEStats( + &tcp6Row, + TcpConnectionEstatsData, + NULL, + 0, + 0, + NULL, + 0, + 0, + (PUCHAR)&dataRod, + 0, + sizeof(TCP_ESTATS_DATA_ROD_v0) + ) == ERROR_SUCCESS) + { + extension->NumberOfBytesIn = dataRod.DataBytesIn; + extension->NumberOfBytesOut = dataRod.DataBytesOut; + } + + if (GetPerTcp6ConnectionEStats( + &tcp6Row, + TcpConnectionEstatsPath, + NULL, + 0, + 0, + NULL, + 0, + 0, + (PUCHAR)&pathRod, + 0, + sizeof(TCP_ESTATS_PATH_ROD_v0) + ) == ERROR_SUCCESS) + { + extension->NumberOfLostPackets = UInt32Add32To64(pathRod.FastRetran, pathRod.PktsRetrans); + extension->SampleRtt = pathRod.SampleRtt; + + if (extension->SampleRtt == ULONG_MAX) // HACK + extension->SampleRtt = 0; + } + } + } +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { StringSettingType, SETTING_NAME_ADDRESS_HISTORY, L"" }, + { IntegerPairSettingType, SETTING_NAME_PING_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_PING_WINDOW_SIZE, L"@96|420,250" }, + { IntegerSettingType, SETTING_NAME_PING_MINIMUM_SCALING, L"1F4" }, // 500ms minimum scaling + { IntegerSettingType, SETTING_NAME_PING_SIZE, L"20" }, // 32 byte packet + { IntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_TRACERT_WINDOW_SIZE, L"@96|850,490" }, + { StringSettingType, SETTING_NAME_TRACERT_TREE_LIST_COLUMNS, L"" }, + { IntegerPairSettingType, SETTING_NAME_TRACERT_TREE_LIST_SORT, L"0,1" }, + { IntegerSettingType, SETTING_NAME_TRACERT_MAX_HOPS, L"30" }, + { IntegerPairSettingType, SETTING_NAME_WHOIS_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_WHOIS_WINDOW_SIZE, L"@96|600,365" }, + { StringSettingType, SETTING_NAME_DB_LOCATION, L"%APPDATA%\\Process Hacker\\GeoLite2-Country.mmdb" }, + { IntegerSettingType, SETTING_NAME_EXTENDED_TCP_STATS, L"0" } + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Network Tools"; + info->Author = L"dmex, wj32"; + info->Description = L"Provides ping, traceroute and whois for network connections."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1117"; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), + MainMenuInitializingCallback, + NULL, + &MainMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNetworkMenuInitializing), + NetworkMenuInitializingCallback, + NULL, + &NetworkMenuInitializingCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), + NetworkTreeNewInitializingCallback, + &NetworkTreeNewHandle, + &NetworkTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), + TreeNewMessageCallback, + NULL, + &TreeNewMessageCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + ProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmNetworkItemType, + sizeof(NETWORK_EXTENSION), + NetworkItemCreateCallback, + NetworkItemDeleteCallback + ); + PhPluginSetObjectExtension( + PluginInstance, + EmNetworkNodeType, + sizeof(NETWORK_EXTENSION), + NetworkNodeCreateCallback, + NULL + ); + + PhAddSettings(settings, ARRAYSIZE(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/NetworkTools/maxminddb/data-pool.c b/plugins/NetworkTools/maxminddb/data-pool.c new file mode 100644 index 000000000000..48521b64dec3 --- /dev/null +++ b/plugins/NetworkTools/maxminddb/data-pool.c @@ -0,0 +1,180 @@ +#include "data-pool.h" +#include "maxminddb.h" + +#include +#include +#include + +static bool can_multiply(size_t const, size_t const, size_t const); + +// Allocate an MMDB_data_pool_s. It initially has space for size +// MMDB_entry_data_list_s structs. +MMDB_data_pool_s *data_pool_new(size_t const size) +{ + MMDB_data_pool_s *const pool = calloc(1, sizeof(MMDB_data_pool_s)); + if (!pool) { + return NULL; + } + + if (size == 0 || + !can_multiply(SIZE_MAX, size, sizeof(MMDB_entry_data_list_s))) { + data_pool_destroy(pool); + return NULL; + } + pool->size = size; + pool->blocks[0] = calloc(pool->size, sizeof(MMDB_entry_data_list_s)); + if (!pool->blocks[0]) { + data_pool_destroy(pool); + return NULL; + } + pool->blocks[0]->pool = pool; + + pool->sizes[0] = size; + + pool->block = pool->blocks[0]; + + return pool; +} + +// Determine if we can multiply m*n. We can do this if the result will be below +// the given max. max will typically be SIZE_MAX. +// +// We want to know if we'll wrap around. +static bool can_multiply(size_t const max, size_t const m, size_t const n) +{ + if (m == 0) { + return false; + } + + return n <= max / m; +} + +// Clean up the data pool. +void data_pool_destroy(MMDB_data_pool_s *const pool) +{ + if (!pool) { + return; + } + + for (size_t i = 0; i <= pool->index; i++) { + free(pool->blocks[i]); + } + + free(pool); +} + +// Claim a new struct from the pool. Doing this may cause the pool's size to +// grow. +MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool) +{ + if (!pool) { + return NULL; + } + + if (pool->used < pool->size) { + MMDB_entry_data_list_s *const element = pool->block + pool->used; + pool->used++; + return element; + } + + // Take it from a new block of memory. + + size_t const new_index = pool->index + 1; + if (new_index == DATA_POOL_NUM_BLOCKS) { + // See the comment about not growing this on DATA_POOL_NUM_BLOCKS. + return NULL; + } + + if (!can_multiply(SIZE_MAX, pool->size, 2)) { + return NULL; + } + size_t const new_size = pool->size * 2; + + if (!can_multiply(SIZE_MAX, new_size, sizeof(MMDB_entry_data_list_s))) { + return NULL; + } + pool->blocks[new_index] = calloc(new_size, sizeof(MMDB_entry_data_list_s)); + if (!pool->blocks[new_index]) { + return NULL; + } + + // We don't need to set this, but it's useful for introspection in tests. + pool->blocks[new_index]->pool = pool; + + pool->index = new_index; + pool->block = pool->blocks[pool->index]; + + pool->size = new_size; + pool->sizes[pool->index] = pool->size; + + MMDB_entry_data_list_s *const element = pool->block; + pool->used = 1; + return element; +} + +// Turn the structs in the array-like pool into a linked list. +// +// Before calling this function, the list isn't linked up. +MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool) +{ + if (!pool) { + return NULL; + } + + if (pool->index == 0 && pool->used == 0) { + return NULL; + } + + for (size_t i = 0; i <= pool->index; i++) { + MMDB_entry_data_list_s *const block = pool->blocks[i]; + + size_t size = pool->sizes[i]; + if (i == pool->index) { + size = pool->used; + } + + for (size_t j = 0; j < size - 1; j++) { + MMDB_entry_data_list_s *const cur = block + j; + cur->next = block + j + 1; + } + + if (i < pool->index) { + MMDB_entry_data_list_s *const last = block + size - 1; + last->next = pool->blocks[i + 1]; + } + } + + return pool->blocks[0]; +} + +#ifdef TEST_DATA_POOL + +#include +#include + +static void test_can_multiply(void); + +int main(void) +{ + plan(NO_PLAN); + test_can_multiply(); + done_testing(); +} + +static void test_can_multiply(void) +{ + { + ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok"); + } + + { + ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok"); + } + + { + ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)), + "1024 entry_data_list_s's are okay"); + } +} + +#endif diff --git a/plugins/NetworkTools/maxminddb/data-pool.h b/plugins/NetworkTools/maxminddb/data-pool.h new file mode 100644 index 000000000000..25d09923e0cc --- /dev/null +++ b/plugins/NetworkTools/maxminddb/data-pool.h @@ -0,0 +1,52 @@ +#ifndef DATA_POOL_H +#define DATA_POOL_H + +#include "maxminddb.h" + +#include +#include + +// This should be large enough that we never need to grow the array of pointers +// to blocks. 32 is enough. Even starting out of with size 1 (1 struct), the +// 32nd element alone will provide 2**32 structs as we exponentially increase +// the number in each block. Being confident that we do not have to grow the +// array lets us avoid writing code to do that. That code would be risky as it +// would rarely be hit and likely not be well tested. +#define DATA_POOL_NUM_BLOCKS 32 + +// A pool of memory for MMDB_entry_data_list_s structs. This is so we can +// allocate multiple up front rather than one at a time for performance +// reasons. +// +// The order you add elements to it (by calling data_pool_alloc()) ends up as +// the order of the list. +// +// The memory only grows. There is no support for releasing an element you take +// back to the pool. +typedef struct MMDB_data_pool_s { + // Index of the current block we're allocating out of. + size_t index; + + // The size of the current block, counting by structs. + size_t size; + + // How many used in the current block, counting by structs. + size_t used; + + // The current block we're allocating out of. + MMDB_entry_data_list_s *block; + + // The size of each block. + size_t sizes[DATA_POOL_NUM_BLOCKS]; + + // An array of pointers to blocks of memory holding space for list + // elements. + MMDB_entry_data_list_s *blocks[DATA_POOL_NUM_BLOCKS]; +} MMDB_data_pool_s; + +MMDB_data_pool_s *data_pool_new(size_t const); +void data_pool_destroy(MMDB_data_pool_s *const); +MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const); +MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const); + +#endif diff --git a/plugins/NetworkTools/maxminddb/maxminddb.c b/plugins/NetworkTools/maxminddb/maxminddb.c index f5e8d66cb850..a6dbdb5f3a5d 100644 --- a/plugins/NetworkTools/maxminddb/maxminddb.c +++ b/plugins/NetworkTools/maxminddb/maxminddb.c @@ -1,9 +1,7 @@ -#pragma warning(push) -#pragma warning(disable : 4244) - #if HAVE_CONFIG_H #include #endif +#include "data-pool.h" #include "maxminddb.h" #include "maxminddb-compat-util.h" #include @@ -111,8 +109,8 @@ DEBUG_FUNC char *type_num_to_name(uint8_t num) #endif /* None of the values we check on the lhs are bigger than uint32_t, so on - * platforms where SIZE_MAX is a 64-bit integer, this would be a no-op, and it - * makes the compiler complain if we do the check anyway. */ +* platforms where SIZE_MAX is a 64-bit integer, this would be a no-op, and it +* makes the compiler complain if we do the check anyway. */ #if SIZE_MAX == UINT32_MAX #define MAYBE_CHECK_SIZE_OVERFLOW(lhs, rhs, error) \ if ((lhs) > (rhs)) { \ @@ -133,54 +131,60 @@ typedef struct record_info_s { /* This is 128kb */ #define METADATA_BLOCK_MAX_SIZE 131072 +// 64 leads us to allocating 4 KiB on a 64bit system. +#define MMDB_POOL_INIT_SIZE 64 + /* *INDENT-OFF* */ /* --prototypes automatically generated by dev-bin/regen-prototypes.pl - don't remove this comment */ LOCAL int map_file(MMDB_s *const mmdb); LOCAL const uint8_t *find_metadata(const uint8_t *file_content, - ssize_t file_size, uint32_t *metadata_size); + ssize_t file_size, uint32_t *metadata_size); LOCAL int read_metadata(MMDB_s *mmdb); LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb); LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, - uint16_t *value); + uint16_t *value); LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, - uint32_t *value); + uint32_t *value); LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, - uint64_t *value); + uint64_t *value); LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, - char const **value); + char const **value); LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, - MMDB_entry_s *metadata_start); + MMDB_entry_s *metadata_start); LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, - MMDB_entry_s *metadata_start); + MMDB_entry_s *metadata_start); LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses); LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, - sa_family_t address_family, - MMDB_lookup_result_s *result); + sa_family_t address_family, + MMDB_lookup_result_s *result); LOCAL record_info_s record_info_for_database(MMDB_s *mmdb); LOCAL int find_ipv4_start_node(MMDB_s *mmdb); -LOCAL int maybe_populate_result(MMDB_s *mmdb, uint32_t record, - uint16_t netmask, MMDB_lookup_result_s *result); +LOCAL uint8_t maybe_populate_result(MMDB_s *mmdb, uint32_t record, + uint16_t netmask, + MMDB_lookup_result_s *result); LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record); LOCAL uint32_t get_left_28_bit_record(const uint8_t *record); LOCAL uint32_t get_right_28_bit_record(const uint8_t *record); LOCAL uint32_t data_section_offset_for_record(MMDB_s *const mmdb, - uint64_t record); + uint64_t record); LOCAL int path_length(va_list va_path); LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb, - MMDB_entry_data_s *entry_data); + MMDB_entry_data_s *entry_data); LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb, - MMDB_entry_data_s *entry_data); + MMDB_entry_data_s *entry_data); LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data); LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset, - MMDB_entry_data_s *entry_data); + MMDB_entry_data_s *entry_data); LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, - MMDB_entry_data_s *entry_data); + MMDB_entry_data_s *entry_data); LOCAL int get_ext_type(int raw_ext_type); LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, - int ptr_size); -LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, - MMDB_entry_data_list_s *const entry_data_list, - int depth); + int ptr_size); +LOCAL int get_entry_data_list(MMDB_s *mmdb, + uint32_t offset, + MMDB_entry_data_list_s *const entry_data_list, + MMDB_data_pool_s *const pool, + int depth); LOCAL float get_ieee754_float(const uint8_t *restrict p); LOCAL double get_ieee754_double(const uint8_t *restrict p); LOCAL uint32_t get_uint32(const uint8_t *p); @@ -188,7 +192,6 @@ LOCAL uint32_t get_uint24(const uint8_t *p); LOCAL uint32_t get_uint16(const uint8_t *p); LOCAL uint64_t get_uintX(const uint8_t *p, int length); LOCAL int32_t get_sintX(const uint8_t *p, int length); -LOCAL MMDB_entry_data_list_s *new_entry_data_list(void); LOCAL void free_mmdb_struct(MMDB_s *const mmdb); LOCAL void free_languages_metadata(MMDB_s *mmdb); LOCAL void free_descriptions_metadata(MMDB_s *mmdb); @@ -222,7 +225,7 @@ LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size); #define FREE_AND_SET_NULL(p) { free((void *)(p)); (p) = NULL; } -int MMDB_open(const wchar_t* const filename, uint32_t flags, MMDB_s *const mmdb) +int MMDB_open(const wchar_t *const filename, uint32_t flags, MMDB_s *const mmdb) { int status = MMDB_SUCCESS; @@ -243,18 +246,18 @@ int MMDB_open(const wchar_t* const filename, uint32_t flags, MMDB_s *const mmdb) } mmdb->flags = flags; - if (MMDB_SUCCESS != (status = map_file(mmdb)) ) { + if (MMDB_SUCCESS != (status = map_file(mmdb))) { goto cleanup; } #ifdef _WIN32 - WSADATA wsa; - WSAStartup(MAKEWORD(2, 2), &wsa); + //WSADATA wsa; // dmex: disabled since hostname lookup is not used. + //WSAStartup(MAKEWORD(2, 2), &wsa); #endif uint32_t metadata_size = 0; const uint8_t *metadata = find_metadata(mmdb->file_content, mmdb->file_size, - &metadata_size); + &metadata_size); if (NULL == metadata) { status = MMDB_INVALID_METADATA_ERROR; goto cleanup; @@ -274,22 +277,41 @@ int MMDB_open(const wchar_t* const filename, uint32_t flags, MMDB_s *const mmdb) } uint32_t search_tree_size = mmdb->metadata.node_count * - mmdb->full_record_byte_size; + mmdb->full_record_byte_size; mmdb->data_section = mmdb->file_content + search_tree_size - + MMDB_DATA_SECTION_SEPARATOR; + + MMDB_DATA_SECTION_SEPARATOR; if (search_tree_size + MMDB_DATA_SECTION_SEPARATOR > (uint32_t)mmdb->file_size) { status = MMDB_INVALID_METADATA_ERROR; goto cleanup; } - mmdb->data_section_size = mmdb->file_size - search_tree_size - - MMDB_DATA_SECTION_SEPARATOR; + mmdb->data_section_size = (uint32_t)mmdb->file_size - search_tree_size - + MMDB_DATA_SECTION_SEPARATOR; + + // Although it is likely not possible to construct a database with valid + // valid metadata, as parsed above, and a data_section_size less than 3, + // we do this check as later we assume it is at least three when doing + // bound checks. + if (mmdb->data_section_size < 3) { + status = MMDB_INVALID_DATA_ERROR; + goto cleanup; + } + mmdb->metadata_section = metadata; mmdb->ipv4_start_node.node_value = 0; mmdb->ipv4_start_node.netmask = 0; - cleanup: + // We do this immediately as otherwise there is a race to set + // ipv4_start_node.node_value and ipv4_start_node.netmask. + if (mmdb->metadata.ip_version == 6) { + status = find_ipv4_start_node(mmdb); + if (status != MMDB_SUCCESS) { + goto cleanup; + } + } + +cleanup: if (MMDB_SUCCESS != status) { int saved_errno = errno; free_mmdb_struct(mmdb); @@ -302,11 +324,11 @@ int MMDB_open(const wchar_t* const filename, uint32_t flags, MMDB_s *const mmdb) LOCAL int map_file(MMDB_s *const mmdb) { - ssize_t size; + DWORD size; int status = MMDB_SUCCESS; HANDLE mmh = NULL; - HANDLE fd = CreateFileW(mmdb->filename, GENERIC_READ, FILE_SHARE_READ, NULL, // dmex: modified for wchar_t - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE fd = CreateFileW(mmdb->filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fd == INVALID_HANDLE_VALUE) { status = MMDB_FILE_OPEN_ERROR; goto cleanup; @@ -318,7 +340,7 @@ LOCAL int map_file(MMDB_s *const mmdb) } mmh = CreateFileMappingA(fd, NULL, PAGE_READONLY, 0, size, NULL); /* Microsoft documentation for CreateFileMapping indicates this returns - NULL not INVALID_HANDLE_VALUE on error */ + NULL not INVALID_HANDLE_VALUE on error */ if (NULL == mmh) { status = MMDB_IO_ERROR; goto cleanup; @@ -333,7 +355,7 @@ LOCAL int map_file(MMDB_s *const mmdb) mmdb->file_size = size; mmdb->file_content = file_content; - cleanup:; +cleanup:; int saved_errno = errno; if (INVALID_HANDLE_VALUE != fd) { CloseHandle(fd); @@ -353,7 +375,11 @@ LOCAL int map_file(MMDB_s *const mmdb) ssize_t size; int status = MMDB_SUCCESS; - int fd = open(mmdb->filename, O_RDONLY); + int flags = O_RDONLY; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + int fd = open(mmdb->filename, flags); struct stat s; if (fd < 0 || fstat(fd, &s)) { status = MMDB_FILE_OPEN_ERROR; @@ -371,7 +397,8 @@ LOCAL int map_file(MMDB_s *const mmdb) if (MAP_FAILED == file_content) { if (ENOMEM == errno) { status = MMDB_OUT_OF_MEMORY_ERROR; - } else { + } + else { status = MMDB_IO_ERROR; } goto cleanup; @@ -380,7 +407,7 @@ LOCAL int map_file(MMDB_s *const mmdb) mmdb->file_size = size; mmdb->file_content = file_content; - cleanup:; +cleanup:; int saved_errno = errno; if (fd >= 0) { close(fd); @@ -393,27 +420,27 @@ LOCAL int map_file(MMDB_s *const mmdb) #endif LOCAL const uint8_t *find_metadata(const uint8_t *file_content, - ssize_t file_size, uint32_t *metadata_size) + ssize_t file_size, uint32_t *metadata_size) { const ssize_t marker_len = sizeof(METADATA_MARKER) - 1; ssize_t max_size = file_size > - METADATA_BLOCK_MAX_SIZE ? METADATA_BLOCK_MAX_SIZE : - file_size; + METADATA_BLOCK_MAX_SIZE ? METADATA_BLOCK_MAX_SIZE : + file_size; uint8_t *search_area = (uint8_t *)(file_content + (file_size - max_size)); uint8_t *start = search_area; uint8_t *tmp; do { tmp = mmdb_memmem(search_area, max_size, - METADATA_MARKER, marker_len); + METADATA_MARKER, marker_len); if (NULL != tmp) { max_size -= tmp - search_area; search_area = tmp; /* Continue searching just after the marker we just read, in case - * there are multiple markers in the same file. This would be odd - * but is certainly not impossible. */ + * there are multiple markers in the same file. This would be odd + * but is certainly not impossible. */ max_size -= marker_len; search_area += marker_len; } @@ -423,7 +450,7 @@ LOCAL const uint8_t *find_metadata(const uint8_t *file_content, return NULL; } - *metadata_size = max_size; + *metadata_size = (uint32_t)max_size; return search_area; } @@ -431,19 +458,19 @@ LOCAL const uint8_t *find_metadata(const uint8_t *file_content, LOCAL int read_metadata(MMDB_s *mmdb) { /* We need to create a fake MMDB_s struct in order to decode values from - the metadata. The metadata is basically just like the data section, so we - want to use the same functions we use for the data section to get metadata - values. */ + the metadata. The metadata is basically just like the data section, so we + want to use the same functions we use for the data section to get metadata + values. */ MMDB_s metadata_db = make_fake_metadata_db(mmdb); MMDB_entry_s metadata_start = { - .mmdb = &metadata_db, + .mmdb = &metadata_db, .offset = 0 }; int status = value_for_key_as_uint32(&metadata_start, "node_count", - &mmdb->metadata.node_count); + &mmdb->metadata.node_count); if (MMDB_SUCCESS != status) { return status; } @@ -453,7 +480,7 @@ LOCAL int read_metadata(MMDB_s *mmdb) } status = value_for_key_as_uint16(&metadata_start, "record_size", - &mmdb->metadata.record_size); + &mmdb->metadata.record_size); if (MMDB_SUCCESS != status) { return status; } @@ -465,12 +492,12 @@ LOCAL int read_metadata(MMDB_s *mmdb) if (mmdb->metadata.record_size != 24 && mmdb->metadata.record_size != 28 && mmdb->metadata.record_size != 32) { DEBUG_MSGF("bad record size in metadata: %i", - mmdb->metadata.record_size); + mmdb->metadata.record_size); return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; } status = value_for_key_as_uint16(&metadata_start, "ip_version", - &mmdb->metadata.ip_version); + &mmdb->metadata.ip_version); if (MMDB_SUCCESS != status) { return status; } @@ -480,12 +507,12 @@ LOCAL int read_metadata(MMDB_s *mmdb) } if (!(mmdb->metadata.ip_version == 4 || mmdb->metadata.ip_version == 6)) { DEBUG_MSGF("ip_version value in metadata is not 4 or 6 - it was %i", - mmdb->metadata.ip_version); + mmdb->metadata.ip_version); return MMDB_INVALID_METADATA_ERROR; } status = value_for_key_as_string(&metadata_start, "database_type", - &mmdb->metadata.database_type); + &mmdb->metadata.database_type); if (MMDB_SUCCESS != status) { DEBUG_MSG("error finding database_type value in metadata"); return status; @@ -518,7 +545,7 @@ LOCAL int read_metadata(MMDB_s *mmdb) } status = value_for_key_as_uint64(&metadata_start, "build_epoch", - &mmdb->metadata.build_epoch); + &mmdb->metadata.build_epoch); if (MMDB_SUCCESS != status) { return status; } @@ -543,7 +570,7 @@ LOCAL int read_metadata(MMDB_s *mmdb) LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb) { MMDB_s fake_metadata_db = { - .data_section = mmdb->metadata_section, + .data_section = mmdb->metadata_section, .data_section_size = mmdb->metadata_section_size }; @@ -551,7 +578,7 @@ LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb) } LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, - uint16_t *value) + uint16_t *value) { MMDB_entry_data_s entry_data; const char *path[] = { key, NULL }; @@ -561,8 +588,8 @@ LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, } if (MMDB_DATA_TYPE_UINT16 != entry_data.type) { DEBUG_MSGF("expect uint16 for %s but received %s", key, - type_num_to_name( - entry_data.type)); + type_num_to_name( + entry_data.type)); return MMDB_INVALID_METADATA_ERROR; } *value = entry_data.uint16; @@ -570,7 +597,7 @@ LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, } LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, - uint32_t *value) + uint32_t *value) { MMDB_entry_data_s entry_data; const char *path[] = { key, NULL }; @@ -580,8 +607,8 @@ LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, } if (MMDB_DATA_TYPE_UINT32 != entry_data.type) { DEBUG_MSGF("expect uint32 for %s but received %s", key, - type_num_to_name( - entry_data.type)); + type_num_to_name( + entry_data.type)); return MMDB_INVALID_METADATA_ERROR; } *value = entry_data.uint32; @@ -589,7 +616,7 @@ LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, } LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, - uint64_t *value) + uint64_t *value) { MMDB_entry_data_s entry_data; const char *path[] = { key, NULL }; @@ -599,8 +626,8 @@ LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, } if (MMDB_DATA_TYPE_UINT64 != entry_data.type) { DEBUG_MSGF("expect uint64 for %s but received %s", key, - type_num_to_name( - entry_data.type)); + type_num_to_name( + entry_data.type)); return MMDB_INVALID_METADATA_ERROR; } *value = entry_data.uint64; @@ -608,7 +635,7 @@ LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, } LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, - char const **value) + char const **value) { MMDB_entry_data_s entry_data; const char *path[] = { key, NULL }; @@ -618,8 +645,8 @@ LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, } if (MMDB_DATA_TYPE_UTF8_STRING != entry_data.type) { DEBUG_MSGF("expect string for %s but received %s", key, - type_num_to_name( - entry_data.type)); + type_num_to_name( + entry_data.type)); return MMDB_INVALID_METADATA_ERROR; } *value = mmdb_strndup((char *)entry_data.utf8_string, entry_data.data_size); @@ -630,7 +657,7 @@ LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, } LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, - MMDB_entry_s *metadata_start) + MMDB_entry_s *metadata_start) { MMDB_entry_data_s entry_data; @@ -644,7 +671,7 @@ LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, } MMDB_entry_s array_start = { - .mmdb = metadata_db, + .mmdb = metadata_db, .offset = entry_data.offset }; @@ -658,7 +685,7 @@ LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, uint32_t array_size = member->entry_data.data_size; MAYBE_CHECK_SIZE_OVERFLOW(array_size, SIZE_MAX / sizeof(char *), - MMDB_INVALID_METADATA_ERROR); + MMDB_INVALID_METADATA_ERROR); mmdb->metadata.languages.count = 0; mmdb->metadata.languages.names = malloc(array_size * sizeof(char *)); @@ -674,7 +701,7 @@ LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, mmdb->metadata.languages.names[i] = mmdb_strndup((char *)member->entry_data.utf8_string, - member->entry_data.data_size); + member->entry_data.data_size); if (NULL == mmdb->metadata.languages.names[i]) { return MMDB_OUT_OF_MEMORY_ERROR; @@ -690,7 +717,7 @@ LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, } LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, - MMDB_entry_s *metadata_start) + MMDB_entry_s *metadata_start) { MMDB_entry_data_s entry_data; @@ -706,7 +733,7 @@ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, } MMDB_entry_s map_start = { - .mmdb = metadata_db, + .mmdb = metadata_db, .offset = entry_data.offset }; @@ -728,7 +755,7 @@ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, goto cleanup; } MAYBE_CHECK_SIZE_OVERFLOW(map_size, SIZE_MAX / sizeof(MMDB_description_s *), - MMDB_INVALID_METADATA_ERROR); + MMDB_INVALID_METADATA_ERROR); mmdb->metadata.description.descriptions = malloc(map_size * sizeof(MMDB_description_s *)); @@ -758,7 +785,7 @@ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, mmdb->metadata.description.descriptions[i]->language = mmdb_strndup((char *)member->entry_data.utf8_string, - member->entry_data.data_size); + member->entry_data.data_size); if (NULL == mmdb->metadata.description.descriptions[i]->language) { status = MMDB_OUT_OF_MEMORY_ERROR; @@ -774,7 +801,7 @@ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, mmdb->metadata.description.descriptions[i]->description = mmdb_strndup((char *)member->entry_data.utf8_string, - member->entry_data.data_size); + member->entry_data.data_size); if (NULL == mmdb->metadata.description.descriptions[i]->description) { status = MMDB_OUT_OF_MEMORY_ERROR; @@ -782,24 +809,24 @@ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, } } - cleanup: +cleanup: MMDB_free_entry_data_list(first_member); return status; } MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb, - const char *const ipstr, - int *const gai_error, - int *const mmdb_error) + const char *const ipstr, + int *const gai_error, + int *const mmdb_error) { MMDB_lookup_result_s result = { .found_entry = false, - .netmask = 0, - .entry = { - .mmdb = mmdb, - .offset = 0 - } + .netmask = 0, + .entry = { + .mmdb = mmdb, + .offset = 0 + } }; struct addrinfo *addresses = NULL; @@ -819,8 +846,8 @@ MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb, LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses) { struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_flags = AI_NUMERICHOST, + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST, // We set ai_socktype so that we only get one result back .ai_socktype = SOCK_STREAM }; @@ -840,11 +867,11 @@ MMDB_lookup_result_s MMDB_lookup_sockaddr( { MMDB_lookup_result_s result = { .found_entry = false, - .netmask = 0, - .entry = { - .mmdb = mmdb, - .offset = 0 - } + .netmask = 0, + .entry = { + .mmdb = mmdb, + .offset = 0 + } }; uint8_t mapped_address[16], *address; @@ -854,29 +881,31 @@ MMDB_lookup_result_s MMDB_lookup_sockaddr( return result; } address = (uint8_t *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr; - } else { + } + else { if (sockaddr->sa_family == AF_INET6) { address = (uint8_t *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr. s6_addr; - } else { + } + else { address = mapped_address; memset(address, 0, 12); memcpy(address + 12, - &((struct sockaddr_in *)sockaddr)->sin_addr.s_addr, 4); + &((struct sockaddr_in *)sockaddr)->sin_addr.s_addr, 4); } } *mmdb_error = find_address_in_search_tree(mmdb, address, sockaddr->sa_family, - &result); + &result); return result; } LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, - sa_family_t address_family, - MMDB_lookup_result_s *result) + sa_family_t address_family, + MMDB_lookup_result_s *result) { record_info_s record_info = record_info_for_database(mmdb); if (0 == record_info.right_record_offset) { @@ -896,13 +925,13 @@ LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, return mmdb_error; } DEBUG_MSGF("IPv4 start node is %u (netmask %u)", - mmdb->ipv4_start_node.node_value, - mmdb->ipv4_start_node.netmask); + mmdb->ipv4_start_node.node_value, + mmdb->ipv4_start_node.netmask); uint8_t type = maybe_populate_result(mmdb, - mmdb->ipv4_start_node.node_value, - mmdb->ipv4_start_node.netmask, - result); + mmdb->ipv4_start_node.node_value, + mmdb->ipv4_start_node.netmask, + result); if (MMDB_RECORD_TYPE_INVALID == type) { return MMDB_CORRUPT_SEARCH_TREE_ERROR; } @@ -924,7 +953,7 @@ LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, & (1U << (~(max_depth0 - current_bit) & 7)) ? 1 : 0; DEBUG_MSGF("Looking at bit %i - bit's value is %i", current_bit, - bit_is_true); + bit_is_true); DEBUG_MSGF(" current node = %u", value); record_pointer = &search_tree[value * record_info.record_length]; @@ -934,11 +963,13 @@ LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, if (bit_is_true) { record_pointer += record_info.right_record_offset; value = record_info.right_record_getter(record_pointer); - } else { + } + else { value = record_info.left_record_getter(record_pointer); } - uint8_t type = maybe_populate_result(mmdb, value, current_bit, result); + uint8_t type = maybe_populate_result(mmdb, value, (uint16_t)current_bit, + result); if (MMDB_RECORD_TYPE_INVALID == type) { return MMDB_CORRUPT_SEARCH_TREE_ERROR; } @@ -960,7 +991,7 @@ LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, LOCAL record_info_s record_info_for_database(MMDB_s *mmdb) { record_info_s record_info = { - .record_length = mmdb->full_record_byte_size, + .record_length = mmdb->full_record_byte_size, .right_record_offset = 0 }; @@ -968,15 +999,18 @@ LOCAL record_info_s record_info_for_database(MMDB_s *mmdb) record_info.left_record_getter = &get_uint24; record_info.right_record_getter = &get_uint24; record_info.right_record_offset = 3; - } else if (record_info.record_length == 7) { + } + else if (record_info.record_length == 7) { record_info.left_record_getter = &get_left_28_bit_record; record_info.right_record_getter = &get_right_28_bit_record; record_info.right_record_offset = 3; - } else if (record_info.record_length == 8) { + } + else if (record_info.record_length == 8) { record_info.left_record_getter = &get_uint32; record_info.right_record_getter = &get_uint32; record_info.right_record_offset = 4; - } else { + } + else { assert(false); } @@ -986,8 +1020,8 @@ LOCAL record_info_s record_info_for_database(MMDB_s *mmdb) LOCAL int find_ipv4_start_node(MMDB_s *mmdb) { /* In a pathological case of a database with a single node search tree, - * this check will be true even after we've found the IPv4 start node, but - * that doesn't seem worth trying to fix. */ + * this check will be true even after we've found the IPv4 start node, but + * that doesn't seem worth trying to fix. */ if (mmdb->ipv4_start_node.node_value != 0) { return MMDB_SUCCESS; } @@ -997,7 +1031,7 @@ LOCAL int find_ipv4_start_node(MMDB_s *mmdb) const uint8_t *search_tree = mmdb->file_content; uint32_t node_value = 0; const uint8_t *record_pointer; - uint32_t netmask; + uint16_t netmask; for (netmask = 0; netmask < 96; netmask++) { record_pointer = &search_tree[node_value * record_info.record_length]; if (record_pointer + record_info.record_length > mmdb->data_section) { @@ -1005,7 +1039,7 @@ LOCAL int find_ipv4_start_node(MMDB_s *mmdb) } node_value = record_info.left_record_getter(record_pointer); /* This can happen if there's no IPv4 data _or_ if there is a subnet - * with data that contains the entire IPv4 range (like ::/64) */ + * with data that contains the entire IPv4 range (like ::/64) */ if (node_value >= mmdb->metadata.node_count) { break; } @@ -1017,8 +1051,9 @@ LOCAL int find_ipv4_start_node(MMDB_s *mmdb) return MMDB_SUCCESS; } -LOCAL int maybe_populate_result(MMDB_s *mmdb, uint32_t record, - uint16_t netmask, MMDB_lookup_result_s *result) +LOCAL uint8_t maybe_populate_result(MMDB_s *mmdb, uint32_t record, + uint16_t netmask, + MMDB_lookup_result_s *result) { uint8_t type = record_type(mmdb, record); @@ -1043,8 +1078,8 @@ LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record) uint32_t node_count = mmdb->metadata.node_count; /* Ideally we'd check to make sure that a record never points to a - * previously seen value, but that's more complicated. For now, we can - * at least check that we don't end up at the top of the tree again. */ + * previously seen value, but that's more complicated. For now, we can + * at least check that we don't end up at the top of the tree again. */ if (record == 0) { DEBUG_MSG("record has a value of 0"); return MMDB_RECORD_TYPE_INVALID; @@ -1069,7 +1104,7 @@ LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record) LOCAL uint32_t get_left_28_bit_record(const uint8_t *record) { return record[0] * 65536 + record[1] * 256 + record[2] + - ((record[3] & 0xf0) << 20); + ((record[3] & 0xf0) << 20); } LOCAL uint32_t get_right_28_bit_record(const uint8_t *record) @@ -1079,7 +1114,7 @@ LOCAL uint32_t get_right_28_bit_record(const uint8_t *record) } int MMDB_read_node(MMDB_s *const mmdb, uint32_t node_number, - MMDB_search_node_s *const node) + MMDB_search_node_s *const node) { record_info_s record_info = record_info_for_database(mmdb); if (0 == record_info.right_record_offset) { @@ -1105,25 +1140,26 @@ int MMDB_read_node(MMDB_s *const mmdb, uint32_t node_number, // for other data types is a programming error. node->left_record_entry = (struct MMDB_entry_s) { .mmdb = mmdb, - .offset = data_section_offset_for_record(mmdb, node->left_record), + .offset = data_section_offset_for_record(mmdb, node->left_record), }; node->right_record_entry = (struct MMDB_entry_s) { .mmdb = mmdb, - .offset = data_section_offset_for_record(mmdb, node->right_record), + .offset = data_section_offset_for_record(mmdb, node->right_record), }; return MMDB_SUCCESS; } LOCAL uint32_t data_section_offset_for_record(MMDB_s *const mmdb, - uint64_t record) + uint64_t record) { - return record - mmdb->metadata.node_count - MMDB_DATA_SECTION_SEPARATOR; + return (uint32_t)record - mmdb->metadata.node_count - + MMDB_DATA_SECTION_SEPARATOR; } int MMDB_get_value(MMDB_entry_s *const start, - MMDB_entry_data_s *const entry_data, - ...) + MMDB_entry_data_s *const entry_data, + ...) { va_list path; va_start(path, entry_data); @@ -1133,15 +1169,15 @@ int MMDB_get_value(MMDB_entry_s *const start, } int MMDB_vget_value(MMDB_entry_s *const start, - MMDB_entry_data_s *const entry_data, - va_list va_path) + MMDB_entry_data_s *const entry_data, + va_list va_path) { int length = path_length(va_path); const char *path_elem; int i = 0; MAYBE_CHECK_SIZE_OVERFLOW(length, SIZE_MAX / sizeof(const char *) - 1, - MMDB_INVALID_METADATA_ERROR); + MMDB_INVALID_METADATA_ERROR); const char **path = malloc((length + 1) * sizeof(const char *)); if (NULL == path) { @@ -1178,8 +1214,8 @@ LOCAL int path_length(va_list va_path) } int MMDB_aget_value(MMDB_entry_s *const start, - MMDB_entry_data_s *const entry_data, - const char *const *const path) + MMDB_entry_data_s *const entry_data, + const char *const *const path) { MMDB_s *mmdb = start->mmdb; uint32_t offset = start->offset; @@ -1194,8 +1230,8 @@ int MMDB_aget_value(MMDB_entry_s *const start, DEBUG_MSGF("top level element is a %s", type_num_to_name(entry_data->type)); /* Can this happen? It'd probably represent a pathological case under - * normal use, but there's nothing preventing someone from passing an - * invalid MMDB_entry_s struct to this function */ + * normal use, but there's nothing preventing someone from passing an + * invalid MMDB_entry_s struct to this function */ if (!entry_data->has_data) { return MMDB_INVALID_LOOKUP_PATH_ERROR; } @@ -1207,25 +1243,27 @@ int MMDB_aget_value(MMDB_entry_s *const start, DEBUG_MSGF("path elem = %s", path_elem); /* XXX - it'd be good to find a quicker way to skip through these - entries that doesn't involve decoding them - completely. Basically we need to just use the size from the - control byte to advance our pointer rather than calling - decode_one(). */ + entries that doesn't involve decoding them + completely. Basically we need to just use the size from the + control byte to advance our pointer rather than calling + decode_one(). */ if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { int status = lookup_path_in_array(path_elem, mmdb, entry_data); if (MMDB_SUCCESS != status) { memset(entry_data, 0, sizeof(MMDB_entry_data_s)); return status; } - } else if (entry_data->type == MMDB_DATA_TYPE_MAP) { + } + else if (entry_data->type == MMDB_DATA_TYPE_MAP) { int status = lookup_path_in_map(path_elem, mmdb, entry_data); if (MMDB_SUCCESS != status) { memset(entry_data, 0, sizeof(MMDB_entry_data_s)); return status; } - } else { + } + else { /* Once we make the code traverse maps & arrays without calling - * decode_one() we can get rid of this. */ + * decode_one() we can get rid of this. */ memset(entry_data, 0, sizeof(MMDB_entry_data_s)); return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; } @@ -1235,7 +1273,7 @@ int MMDB_aget_value(MMDB_entry_s *const start, } LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb, - MMDB_entry_data_s *entry_data) + MMDB_entry_data_s *entry_data) { uint32_t size = entry_data->data_size; char *first_invalid; @@ -1255,7 +1293,7 @@ LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb, for (int i = 0; i < array_index; i++) { /* We don't want to follow a pointer here. If the next element is a - * pointer we simply skip it and keep going */ + * pointer we simply skip it and keep going */ CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); int status = skip_map_or_array(mmdb, entry_data); if (MMDB_SUCCESS != status) { @@ -1271,7 +1309,7 @@ LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb, } LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb, - MMDB_entry_data_s *entry_data) + MMDB_entry_data_s *entry_data) { uint32_t size = entry_data->data_size; uint32_t offset = entry_data->offset_to_next; @@ -1295,9 +1333,10 @@ LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb, CHECKED_DECODE_ONE_FOLLOW(mmdb, offset_to_value, &value); memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); return MMDB_SUCCESS; - } else { + } + else { /* We don't want to follow a pointer here. If the next element is - * a pointer we simply skip it and keep going */ + * a pointer we simply skip it and keep going */ CHECKED_DECODE_ONE(mmdb, offset_to_value, &value); int status = skip_map_or_array(mmdb, &value); if (MMDB_SUCCESS != status) { @@ -1323,7 +1362,8 @@ LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data) return status; } } - } else if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { + } + else if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { uint32_t size = entry_data->data_size; while (size-- > 0) { CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value @@ -1338,7 +1378,7 @@ LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data) } LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset, - MMDB_entry_data_s *entry_data) + MMDB_entry_data_s *entry_data) { CHECKED_DECODE_ONE(mmdb, offset, entry_data); if (entry_data->type == MMDB_DATA_TYPE_POINTER) { @@ -1351,11 +1391,11 @@ LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset, } /* The pointer could point to any part of the data section but the - * next entry for this particular offset may be the one after the - * pointer, not the one after whatever the pointer points to. This - * depends on whether the pointer points to something that is a simple - * value or a compound value. For a compound value, the next one is - * the one after the pointer result, not the one after the pointer. */ + * next entry for this particular offset may be the one after the + * pointer, not the one after whatever the pointer points to. This + * depends on whether the pointer points to something that is a simple + * value or a compound value. For a compound value, the next one is + * the one after the pointer result, not the one after the pointer. */ if (entry_data->type != MMDB_DATA_TYPE_MAP && entry_data->type != MMDB_DATA_TYPE_ARRAY) { @@ -1379,13 +1419,16 @@ NO_PROTO mmdb_uint128_t get_uint128(const uint8_t *p, int length) #endif LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, - MMDB_entry_data_s *entry_data) + MMDB_entry_data_s *entry_data) { const uint8_t *mem = mmdb->data_section; - if (offset + 1 > mmdb->data_section_size) { + // We subtract rather than add as it possible that offset + 1 + // could overflow for a corrupt database while an underflow + // from data_section_size - 1 should not be possible. + if (offset > mmdb->data_section_size - 1) { DEBUG_MSGF("Offset (%d) past data section (%d)", offset, - mmdb->data_section_size); + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } @@ -1402,10 +1445,11 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, DEBUG_MSGF("Type: %i (%s)", type, type_num_to_name(type)); if (type == MMDB_DATA_TYPE_EXTENDED) { - if (offset + 1 > mmdb->data_section_size) { + // Subtracting 1 to avoid possible overflow on offset + 1 + if (offset > mmdb->data_section_size - 1) { DEBUG_MSGF("Extended type offset (%d) past data section (%d)", - offset, - mmdb->data_section_size); + offset, + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } type = get_ext_type(mem[offset++]); @@ -1415,13 +1459,16 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, entry_data->type = type; if (type == MMDB_DATA_TYPE_POINTER) { - int psize = ((ctrl >> 3) & 3) + 1; + uint8_t psize = ((ctrl >> 3) & 3) + 1; DEBUG_MSGF("Pointer size: %i", psize); - if (offset + psize > mmdb->data_section_size) { + // We check that the offset does not extend past the end of the + // database and that the subtraction of psize did not underflow. + if (offset > mmdb->data_section_size - psize || + mmdb->data_section_size < psize) { DEBUG_MSGF("Pointer offset (%d) past data section (%d)", offset + - psize, - mmdb->data_section_size); + psize, + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } entry_data->pointer = get_ptr_from(ctrl, &mem[offset], psize); @@ -1435,29 +1482,32 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, uint32_t size = ctrl & 31; switch (size) { case 29: - if (offset + 1 > mmdb->data_section_size) { + // We subtract when checking offset to avoid possible overflow + if (offset > mmdb->data_section_size - 1) { DEBUG_MSGF("String end (%d, case 29) past data section (%d)", - offset, - mmdb->data_section_size); + offset, + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } size = 29 + mem[offset++]; break; case 30: - if (offset + 2 > mmdb->data_section_size) { + // We subtract when checking offset to avoid possible overflow + if (offset > mmdb->data_section_size - 2) { DEBUG_MSGF("String end (%d, case 30) past data section (%d)", - offset, - mmdb->data_section_size); + offset, + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } size = 285 + get_uint16(&mem[offset]); offset += 2; break; case 31: - if (offset + 3 > mmdb->data_section_size) { + // We subtract when checking offset to avoid possible overflow + if (offset > mmdb->data_section_size - 3) { DEBUG_MSGF("String end (%d, case 31) past data section (%d)", - offset, - mmdb->data_section_size); + offset, + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } size = 65821 + get_uint24(&mem[offset]); @@ -1482,11 +1532,12 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, return MMDB_SUCCESS; } - // check that the data doesn't extend past the end of the memory - // buffer - if (offset + size > mmdb->data_section_size) { + // Check that the data doesn't extend past the end of the memory + // buffer and that the calculation in doing this did not underflow. + if (offset > mmdb->data_section_size - size || + mmdb->data_section_size < size) { DEBUG_MSGF("Data end (%d) past data section (%d)", offset + size, - mmdb->data_section_size); + mmdb->data_section_size); return MMDB_INVALID_DATA_ERROR; } @@ -1497,28 +1548,32 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, } entry_data->uint16 = (uint16_t)get_uintX(&mem[offset], size); DEBUG_MSGF("uint16 value: %u", entry_data->uint16); - } else if (type == MMDB_DATA_TYPE_UINT32) { + } + else if (type == MMDB_DATA_TYPE_UINT32) { if (size > 4) { DEBUG_MSGF("uint32 of size %d", size); return MMDB_INVALID_DATA_ERROR; } entry_data->uint32 = (uint32_t)get_uintX(&mem[offset], size); DEBUG_MSGF("uint32 value: %u", entry_data->uint32); - } else if (type == MMDB_DATA_TYPE_INT32) { + } + else if (type == MMDB_DATA_TYPE_INT32) { if (size > 4) { DEBUG_MSGF("int32 of size %d", size); return MMDB_INVALID_DATA_ERROR; } entry_data->int32 = get_sintX(&mem[offset], size); DEBUG_MSGF("int32 value: %i", entry_data->int32); - } else if (type == MMDB_DATA_TYPE_UINT64) { + } + else if (type == MMDB_DATA_TYPE_UINT64) { if (size > 8) { DEBUG_MSGF("uint64 of size %d", size); return MMDB_INVALID_DATA_ERROR; } entry_data->uint64 = get_uintX(&mem[offset], size); DEBUG_MSGF("uint64 value: %" PRIu64, entry_data->uint64); - } else if (type == MMDB_DATA_TYPE_UINT128) { + } + else if (type == MMDB_DATA_TYPE_UINT128) { if (size > 16) { DEBUG_MSGF("uint128 of size %d", size); return MMDB_INVALID_DATA_ERROR; @@ -1531,7 +1586,8 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, #else entry_data->uint128 = get_uint128(&mem[offset], size); #endif - } else if (type == MMDB_DATA_TYPE_FLOAT) { + } + else if (type == MMDB_DATA_TYPE_FLOAT) { if (size != 4) { DEBUG_MSGF("float of size %d", size); return MMDB_INVALID_DATA_ERROR; @@ -1539,7 +1595,8 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, size = 4; entry_data->float_value = get_ieee754_float(&mem[offset]); DEBUG_MSGF("float value: %f", entry_data->float_value); - } else if (type == MMDB_DATA_TYPE_DOUBLE) { + } + else if (type == MMDB_DATA_TYPE_DOUBLE) { if (size != 8) { DEBUG_MSGF("double of size %d", size); return MMDB_INVALID_DATA_ERROR; @@ -1547,19 +1604,21 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, size = 8; entry_data->double_value = get_ieee754_double(&mem[offset]); DEBUG_MSGF("double value: %f", entry_data->double_value); - } else if (type == MMDB_DATA_TYPE_UTF8_STRING) { + } + else if (type == MMDB_DATA_TYPE_UTF8_STRING) { entry_data->utf8_string = size == 0 ? "" : (char *)&mem[offset]; entry_data->data_size = size; #ifdef MMDB_DEBUG char *string = mmdb_strndup(entry_data->utf8_string, - size > 50 ? 50 : size); + size > 50 ? 50 : size); if (NULL == string) { abort(); } DEBUG_MSGF("string value: %s", string); free(string); #endif - } else if (type == MMDB_DATA_TYPE_BYTES) { + } + else if (type == MMDB_DATA_TYPE_BYTES) { entry_data->bytes = &mem[offset]; entry_data->data_size = size; } @@ -1575,18 +1634,18 @@ LOCAL int get_ext_type(int raw_ext_type) } LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, - int ptr_size) + int ptr_size) { uint32_t new_offset; switch (ptr_size) { case 1: - new_offset = ( (ctrl & 7) << 8) + ptr[0]; + new_offset = ((ctrl & 7) << 8) + ptr[0]; break; case 2: - new_offset = 2048 + ( (ctrl & 7) << 16 ) + ( ptr[0] << 8) + ptr[1]; + new_offset = 2048 + ((ctrl & 7) << 16) + (ptr[0] << 8) + ptr[1]; break; case 3: - new_offset = 2048 + 524288 + ( (ctrl & 7) << 24 ) + get_uint24(ptr); + new_offset = 2048 + 524288 + ((ctrl & 7) << 24) + get_uint24(ptr); break; case 4: default: @@ -1602,7 +1661,7 @@ int MMDB_get_metadata_as_entry_data_list( MMDB_s metadata_db = make_fake_metadata_db(mmdb); MMDB_entry_s metadata_start = { - .mmdb = &metadata_db, + .mmdb = &metadata_db, .offset = 0 }; @@ -1612,16 +1671,34 @@ int MMDB_get_metadata_as_entry_data_list( int MMDB_get_entry_data_list( MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list) { - *entry_data_list = new_entry_data_list(); - if (NULL == *entry_data_list) { + MMDB_data_pool_s *const pool = data_pool_new(MMDB_POOL_INIT_SIZE); + if (!pool) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + MMDB_entry_data_list_s *const list = data_pool_alloc(pool); + if (!list) { + data_pool_destroy(pool); + return MMDB_OUT_OF_MEMORY_ERROR; + } + + int const status = get_entry_data_list(start->mmdb, start->offset, list, + pool, 0); + + *entry_data_list = data_pool_to_list(pool); + if (!*entry_data_list) { + data_pool_destroy(pool); return MMDB_OUT_OF_MEMORY_ERROR; } - return get_entry_data_list(start->mmdb, start->offset, *entry_data_list, 0); + + return status; } -LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, - MMDB_entry_data_list_s *const entry_data_list, - int depth) +LOCAL int get_entry_data_list(MMDB_s *mmdb, + uint32_t offset, + MMDB_entry_data_list_s *const entry_data_list, + MMDB_data_pool_s *const pool, + int depth) { if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) { DEBUG_MSG("reached the maximum data structure depth"); @@ -1636,8 +1713,8 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, uint32_t next_offset = entry_data_list->entry_data.offset_to_next; uint32_t last_offset; CHECKED_DECODE_ONE(mmdb, last_offset = - entry_data_list->entry_data.pointer, - &entry_data_list->entry_data); + entry_data_list->entry_data.pointer, + &entry_data_list->entry_data); /* Pointers to pointers are illegal under the spec */ if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_POINTER) { @@ -1650,7 +1727,7 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, int status = get_entry_data_list(mmdb, last_offset, entry_data_list, - depth); + pool, depth); if (MMDB_SUCCESS != status) { DEBUG_MSG("get_entry_data_list on pointer failed."); return status; @@ -1663,26 +1740,22 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, { uint32_t array_size = entry_data_list->entry_data.data_size; uint32_t array_offset = entry_data_list->entry_data.offset_to_next; - MMDB_entry_data_list_s *previous = entry_data_list; while (array_size-- > 0) { - MMDB_entry_data_list_s *entry_data_list_to = previous->next = - new_entry_data_list(); - if (NULL == entry_data_list_to) { + MMDB_entry_data_list_s *entry_data_list_to = + data_pool_alloc(pool); + if (!entry_data_list_to) { return MMDB_OUT_OF_MEMORY_ERROR; } int status = get_entry_data_list(mmdb, array_offset, entry_data_list_to, - depth); + pool, depth); if (MMDB_SUCCESS != status) { DEBUG_MSG("get_entry_data_list on array element failed."); return status; } array_offset = entry_data_list_to->entry_data.offset_to_next; - while (previous->next) { - previous = previous->next; - } } entry_data_list->entry_data.offset_to_next = array_offset; @@ -1693,45 +1766,33 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, uint32_t size = entry_data_list->entry_data.data_size; offset = entry_data_list->entry_data.offset_to_next; - MMDB_entry_data_list_s *previous = entry_data_list; while (size-- > 0) { - MMDB_entry_data_list_s *entry_data_list_to = previous->next = - new_entry_data_list(); - if (NULL == entry_data_list_to) { + MMDB_entry_data_list_s *list_key = data_pool_alloc(pool); + if (!list_key) { return MMDB_OUT_OF_MEMORY_ERROR; } int status = - get_entry_data_list(mmdb, offset, entry_data_list_to, - depth); + get_entry_data_list(mmdb, offset, list_key, pool, depth); if (MMDB_SUCCESS != status) { DEBUG_MSG("get_entry_data_list on map key failed."); return status; } - while (previous->next) { - previous = previous->next; - } - - offset = entry_data_list_to->entry_data.offset_to_next; - entry_data_list_to = previous->next = - new_entry_data_list(); + offset = list_key->entry_data.offset_to_next; - if (NULL == entry_data_list_to) { + MMDB_entry_data_list_s *list_value = data_pool_alloc(pool); + if (!list_value) { return MMDB_OUT_OF_MEMORY_ERROR; } - status = get_entry_data_list(mmdb, offset, entry_data_list_to, - depth); + status = get_entry_data_list(mmdb, offset, list_value, pool, + depth); if (MMDB_SUCCESS != status) { DEBUG_MSG("get_entry_data_list on map element failed."); return status; } - - while (previous->next) { - previous = previous->next; - } - offset = entry_data_list_to->entry_data.offset_to_next; + offset = list_value->entry_data.offset_to_next; } entry_data_list->entry_data.offset_to_next = offset; } @@ -1747,7 +1808,9 @@ LOCAL float get_ieee754_float(const uint8_t *restrict p) { volatile float f; uint8_t *q = (void *)&f; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Windows builds don't use autoconf but we can assume they're all + * little-endian. */ +#if MMDB_LITTLE_ENDIAN || _WIN32 q[3] = p[0]; q[2] = p[1]; q[1] = p[2]; @@ -1762,7 +1825,7 @@ LOCAL double get_ieee754_double(const uint8_t *restrict p) { volatile double d; uint8_t *q = (void *)&d; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#if MMDB_LITTLE_ENDIAN || _WIN32 q[7] = p[0]; q[6] = p[1]; q[5] = p[2]; @@ -1808,22 +1871,12 @@ LOCAL int32_t get_sintX(const uint8_t *p, int length) return (int32_t)get_uintX(p, length); } -LOCAL MMDB_entry_data_list_s *new_entry_data_list(void) -{ - /* We need calloc here in order to ensure that the ->next pointer in the - * struct doesn't point to some random address. */ - return calloc(1, sizeof(MMDB_entry_data_list_s)); -} - void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list) { if (entry_data_list == NULL) { return; } - if (entry_data_list->next) { - MMDB_free_entry_data_list(entry_data_list->next); - } - free(entry_data_list); + data_pool_destroy(entry_data_list->pool); } void MMDB_close(MMDB_s *const mmdb) @@ -1844,8 +1897,8 @@ LOCAL void free_mmdb_struct(MMDB_s *const mmdb) #ifdef _WIN32 UnmapViewOfFile(mmdb->file_content); /* Winsock is only initialized if open was successful so we only have - * to cleanup then. */ - WSACleanup(); + * to cleanup then. */ + //WSACleanup(); // dmex: disabled since hostname lookup is not used. #else munmap((void *)mmdb->file_content, mmdb->file_size); #endif @@ -1903,8 +1956,8 @@ const char *MMDB_lib_version(void) } int MMDB_dump_entry_data_list(FILE *const stream, - MMDB_entry_data_list_s *const entry_data_list, - int indent) + MMDB_entry_data_list_s *const entry_data_list, + int indent) { int status; dump_entry_data_list(stream, entry_data_list, indent, &status); @@ -1925,7 +1978,7 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( indent += 2; for (entry_data_list = entry_data_list->next; - size && entry_data_list; size--) { + size && entry_data_list; size--) { if (MMDB_DATA_TYPE_UTF8_STRING != entry_data_list->entry_data.type) { @@ -1934,7 +1987,7 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( } char *key = mmdb_strndup( - (char *)entry_data_list->entry_data.utf8_string, + (char *)entry_data_list->entry_data.utf8_string, entry_data_list->entry_data.data_size); if (NULL == key) { *status = MMDB_OUT_OF_MEMORY_ERROR; @@ -1948,7 +2001,7 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( entry_data_list = entry_data_list->next; entry_data_list = dump_entry_data_list(stream, entry_data_list, indent + 2, - status); + status); if (MMDB_SUCCESS != *status) { return NULL; @@ -1969,10 +2022,10 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( indent += 2; for (entry_data_list = entry_data_list->next; - size && entry_data_list; size--) { + size && entry_data_list; size--) { entry_data_list = dump_entry_data_list(stream, entry_data_list, indent, - status); + status); if (MMDB_SUCCESS != *status) { return NULL; } @@ -1987,7 +2040,7 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( { char *string = mmdb_strndup((char *)entry_data_list->entry_data.utf8_string, - entry_data_list->entry_data.data_size); + entry_data_list->entry_data.data_size); if (NULL == string) { *status = MMDB_OUT_OF_MEMORY_ERROR; return NULL; @@ -2002,7 +2055,7 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( { char *hex_string = bytes_to_hex((uint8_t *)entry_data_list->entry_data.bytes, - entry_data_list->entry_data.data_size); + entry_data_list->entry_data.data_size); if (NULL == hex_string) { *status = MMDB_OUT_OF_MEMORY_ERROR; return NULL; @@ -2018,13 +2071,13 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( case MMDB_DATA_TYPE_DOUBLE: print_indentation(stream, indent); fprintf(stream, "%f \n", - entry_data_list->entry_data.double_value); + entry_data_list->entry_data.double_value); entry_data_list = entry_data_list->next; break; case MMDB_DATA_TYPE_FLOAT: print_indentation(stream, indent); fprintf(stream, "%f \n", - entry_data_list->entry_data.float_value); + entry_data_list->entry_data.float_value); entry_data_list = entry_data_list->next; break; case MMDB_DATA_TYPE_UINT16: @@ -2040,13 +2093,13 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( case MMDB_DATA_TYPE_BOOLEAN: print_indentation(stream, indent); fprintf(stream, "%s \n", - entry_data_list->entry_data.boolean ? "true" : "false"); + entry_data_list->entry_data.boolean ? "true" : "false"); entry_data_list = entry_data_list->next; break; case MMDB_DATA_TYPE_UINT64: print_indentation(stream, indent); fprintf(stream, "%" PRIu64 " \n", - entry_data_list->entry_data.uint64); + entry_data_list->entry_data.uint64); entry_data_list = entry_data_list->next; break; case MMDB_DATA_TYPE_UINT128: @@ -2064,7 +2117,7 @@ LOCAL MMDB_entry_data_list_s *dump_entry_data_list( uint64_t high = entry_data_list->entry_data.uint128 >> 64; uint64_t low = (uint64_t)entry_data_list->entry_data.uint128; fprintf(stream, "0x%016" PRIX64 "%016" PRIX64 " \n", high, - low); + low); #endif entry_data_list = entry_data_list->next; break; @@ -2091,6 +2144,8 @@ LOCAL void print_indentation(FILE *stream, int i) fputs(buffer, stream); } +#pragma warning(push) +#pragma warning(disable : 4996) LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size) { char *hex_string; @@ -2101,17 +2156,13 @@ LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size) return NULL; } - for (uint32_t i = 0; i < size; i++) - { - sprintf_s( - hex_string + (2 * i), - (size * 2) + 1, - "%02X", bytes[i] - ); + for (uint32_t i = 0; i < size; i++) { + sprintf(hex_string + (2 * i), "%02X", bytes[i]); } return hex_string; } +#pragma warning(pop) const char *MMDB_strerror(int error_code) { @@ -2150,5 +2201,3 @@ const char *MMDB_strerror(int error_code) return "Unknown error code"; } } - -#pragma warning(pop) \ No newline at end of file diff --git a/plugins/NetworkTools/maxminddb/maxminddb.h b/plugins/NetworkTools/maxminddb/maxminddb.h index 1dc6e37d5869..0978b05b5d95 100644 --- a/plugins/NetworkTools/maxminddb/maxminddb.h +++ b/plugins/NetworkTools/maxminddb/maxminddb.h @@ -5,8 +5,16 @@ extern "C" { #ifndef MAXMINDDB_H #define MAXMINDDB_H +/* Request POSIX.1-2008. However, we want to remain compatible with + * POSIX.1-2001 (since we have been historically and see no reason to drop + * compatibility). By requesting POSIX.1-2008, we can conditionally use + * features provided by that standard if the implementation provides it. We can + * check for what the implementation provides by checking the _POSIX_VERSION + * macro after including unistd.h. If a feature is in POSIX.1-2008 but not + * POSIX.1-2001, check that macro before using the feature (or check for the + * feature directly if possible). */ #ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L +#define _POSIX_C_SOURCE 200809L #endif #include "maxminddb_config.h" @@ -20,13 +28,13 @@ extern "C" { #include #include /* libmaxminddb package version from configure */ -#define PACKAGE_VERSION "1.2.0" +#define PACKAGE_VERSION "1.3.2" typedef ADDRESS_FAMILY sa_family_t; #if defined(_MSC_VER) /* MSVC doesn't define signed size_t, copy it from configure */ -#define ssize_t int +#define ssize_t SSIZE_T /* MSVC doesn't support restricted pointers */ #define restrict @@ -135,6 +143,7 @@ typedef struct MMDB_entry_data_s { typedef struct MMDB_entry_data_list_s { MMDB_entry_data_s entry_data; struct MMDB_entry_data_list_s *next; + void *pool; } MMDB_entry_data_list_s; typedef struct MMDB_description_s { @@ -167,7 +176,7 @@ typedef struct MMDB_ipv4_start_node_s { typedef struct MMDB_s { uint32_t flags; - const wchar_t* filename; + const wchar_t *filename; ssize_t file_size; const uint8_t *file_content; const uint8_t *data_section; diff --git a/plugins/NetworkTools/nettools.h b/plugins/NetworkTools/nettools.h index a0a36d9222e0..2038f86b1521 100644 --- a/plugins/NetworkTools/nettools.h +++ b/plugins/NetworkTools/nettools.h @@ -1,330 +1,355 @@ -/* - * Process Hacker Network Tools - - * Main header - * - * Copyright (C) 2010-2013 wj32 - * Copyright (C) 2012-2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef _NET_TOOLS_H_ -#define _NET_TOOLS_H_ - -#define CINTERFACE -#define COBJMACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "resource.h" - -#define PLUGIN_NAME L"ProcessHacker.NetworkTools" -#define SETTING_NAME_DB_TYPE (PLUGIN_NAME L".GeoIpType") -#define SETTING_NAME_PING_WINDOW_POSITION (PLUGIN_NAME L".PingWindowPosition") -#define SETTING_NAME_PING_WINDOW_SIZE (PLUGIN_NAME L".PingWindowSize") -#define SETTING_NAME_PING_MINIMUM_SCALING (PLUGIN_NAME L".PingMinScaling") -#define SETTING_NAME_PING_SIZE (PLUGIN_NAME L".PingSize") -#define SETTING_NAME_TRACERT_WINDOW_POSITION (PLUGIN_NAME L".TracertWindowPosition") -#define SETTING_NAME_TRACERT_WINDOW_SIZE (PLUGIN_NAME L".TracertWindowSize") -#define SETTING_NAME_TRACERT_LIST_COLUMNS (PLUGIN_NAME L".TracertListColumns") -#define SETTING_NAME_TRACERT_HISTORY (PLUGIN_NAME L".TracertAddresses") -#define SETTING_NAME_TRACERT_MAX_HOPS (PLUGIN_NAME L".TracertMaxHops") -#define SETTING_NAME_WHOIS_WINDOW_POSITION (PLUGIN_NAME L".WhoisWindowPosition") -#define SETTING_NAME_WHOIS_WINDOW_SIZE (PLUGIN_NAME L".WhoisWindowSize") - -extern PPH_PLUGIN PluginInstance; -extern BOOLEAN GeoDbLoaded; -extern BOOLEAN GeoDbExpired; -extern PPH_STRING SearchboxText; - -// ICMP Packet Length: (msdn: IcmpSendEcho2/Icmp6SendEcho2) -// The buffer must be large enough to hold at least one ICMP_ECHO_REPLY or ICMPV6_ECHO_REPLY structure -// + the number of bytes of data specified in the RequestSize parameter. -// This buffer should also be large enough to also hold 8 more bytes of data (the size of an ICMP error message) -// + space for an IO_STATUS_BLOCK structure. -#define ICMP_BUFFER_SIZE(EchoReplyLength, Buffer) \ - (ULONG)(((EchoReplyLength) + (Buffer)->Length) + 8 + sizeof(IO_STATUS_BLOCK) + MAX_OPT_SIZE) - -#define BITS_IN_ONE_BYTE 8 -#define NDIS_UNIT_OF_MEASUREMENT 100 - -// The ICMPV6_ECHO_REPLY struct doesn't have a field to access the reply data, -// so copy the struct and add an additional Data field. -typedef struct _ICMPV6_ECHO_REPLY2 -{ - IPV6_ADDRESS_EX Address; - ULONG Status; - unsigned int RoundTripTime; - BYTE Data[ANYSIZE_ARRAY]; // custom -} ICMPV6_ECHO_REPLY2, *PICMPV6_ECHO_REPLY2; - -typedef enum _PH_NETWORK_ACTION -{ - NETWORK_ACTION_PING = 1, - NETWORK_ACTION_TRACEROUTE, - NETWORK_ACTION_WHOIS, - NETWORK_ACTION_FINISH, - NETWORK_ACTION_PATHPING, - MAINMENU_ACTION_PING, - MAINMENU_ACTION_TRACERT, - MAINMENU_ACTION_WHOIS, - MAINMENU_ACTION_GEOIP_UPDATE, -} PH_NETWORK_ACTION; - -#define NTM_RECEIVEDTRACE (WM_APP + NETWORK_ACTION_TRACEROUTE) -#define NTM_RECEIVEDWHOIS (WM_APP + NETWORK_ACTION_WHOIS) -#define NTM_RECEIVEDFINISH (WM_APP + NETWORK_ACTION_FINISH) -#define WM_TRACERT_UPDATE (WM_APP + NETWORK_ACTION_TRACEROUTE + 1002) -#define WM_TRACERT_COUNTRY (WM_APP + NETWORK_ACTION_TRACEROUTE) - -#define UPDATE_MENUITEM 1005 -#define PH_UPDATEISERRORED (WM_APP + 501) -#define PH_UPDATEISCURRENT (WM_APP + 503) -#define PH_UPDATENEWER (WM_APP + 504) -#define PH_UPDATESUCCESS (WM_APP + 505) -#define PH_UPDATEFAILURE (WM_APP + 506) -#define WM_SHOWDIALOG (WM_APP + 550) - -typedef struct _NETWORK_PING_CONTEXT -{ - HWND WindowHandle; - HWND StatusHandle; - HWND PingGraphHandle; - HFONT FontHandle; - - ULONG CurrentPingMs; - ULONG MaxPingTimeout; - ULONG HashFailCount; - ULONG UnknownAddrCount; - ULONG PingMinMs; - ULONG PingMaxMs; - ULONG PingSentCount; - ULONG PingRecvCount; - ULONG PingLossCount; - - PH_NETWORK_ACTION Action; - PH_LAYOUT_MANAGER LayoutManager; - PH_WORK_QUEUE PingWorkQueue; - PH_GRAPH_STATE PingGraphState; - PH_CIRCULAR_BUFFER_ULONG PingHistory; - PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; - - PH_IP_ENDPOINT RemoteEndpoint; - WCHAR IpAddressString[INET6_ADDRSTRLEN + 1]; -} NETWORK_PING_CONTEXT, *PNETWORK_PING_CONTEXT; - -// ping.c - -VOID ShowPingWindow( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -VOID ShowPingWindowFromAddress( - _In_ PH_IP_ENDPOINT RemoteEndpoint - ); - -// whois.c - -typedef struct _NETWORK_WHOIS_CONTEXT -{ - HWND WindowHandle; - HWND RichEditHandle; - HFONT FontHandle; - - PH_NETWORK_ACTION Action; - PH_LAYOUT_MANAGER LayoutManager; - - PH_IP_ENDPOINT RemoteEndpoint; - WCHAR IpAddressString[INET6_ADDRSTRLEN + 1]; -} NETWORK_WHOIS_CONTEXT, *PNETWORK_WHOIS_CONTEXT; - -VOID ShowWhoisWindow( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -VOID ShowWhoisWindowFromAddress( - _In_ PH_IP_ENDPOINT RemoteEndpoint - ); - -// tracert.c - -typedef struct _NETWORK_TRACERT_CONTEXT -{ - HWND WindowHandle; - HWND SearchboxHandle; - HWND TreeNewHandle; - HFONT FontHandle; - - BOOLEAN Cancel; - ULONG TreeNewSortColumn; - PH_SORT_ORDER TreeNewSortOrder; - PH_LAYOUT_MANAGER LayoutManager; - PH_WORK_QUEUE WorkQueue; - PPH_HASHTABLE NodeHashtable; - PPH_LIST NodeList; - PPH_LIST NodeRootList; - - PH_IP_ENDPOINT RemoteEndpoint; - WCHAR IpAddressString[INET6_ADDRSTRLEN + 1]; -} NETWORK_TRACERT_CONTEXT, *PNETWORK_TRACERT_CONTEXT; - -VOID ShowTracertWindow( - _In_ PPH_NETWORK_ITEM NetworkItem - ); - -VOID ShowTracertWindowFromAddress( - _In_ PH_IP_ENDPOINT RemoteEndpoint - ); - -// options.c - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ); - -// country.c -typedef struct _NETWORK_EXTENSION -{ - union - { - BOOLEAN Flags; - struct - { - BOOLEAN CountryValid : 1; - BOOLEAN LocalValid : 1; - BOOLEAN RemoteValid : 1; - BOOLEAN Spare : 5; - }; - }; - - HICON CountryIcon; - PPH_STRING LocalServiceName; - PPH_STRING RemoteServiceName; - PPH_STRING RemoteCountryCode; - PPH_STRING RemoteCountryName; -} NETWORK_EXTENSION, *PNETWORK_EXTENSION; - -typedef enum _NETWORK_COLUMN_ID -{ - NETWORK_COLUMN_ID_REMOTE_COUNTRY = 1, - NETWORK_COLUMN_ID_LOCAL_SERVICE = 2, - NETWORK_COLUMN_ID_REMOTE_SERVICE = 3, -} NETWORK_COLUMN_ID; - -// country.c -VOID LoadGeoLiteDb(VOID); -VOID FreeGeoLiteDb(VOID); - -BOOLEAN LookupCountryCode( - _In_ PH_IP_ADDRESS RemoteAddress, - _Out_ PPH_STRING *CountryCode, - _Out_ PPH_STRING *CountryName - ); - -BOOLEAN LookupSockInAddr4CountryCode( - _In_ IN_ADDR RemoteAddress, - _Out_ PPH_STRING *CountryCode, - _Out_ PPH_STRING *CountryName - ); - -BOOLEAN LookupSockInAddr6CountryCode( - _In_ IN6_ADDR RemoteAddress, - _Out_ PPH_STRING *CountryCode, - _Out_ PPH_STRING *CountryName - ); - -INT LookupResourceCode( - _In_ PPH_STRING Name - ); - -typedef struct _PH_UPDATER_CONTEXT -{ - BOOLEAN FixedWindowStyles; - HWND DialogHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; - - PPH_STRING FileDownloadUrl; - PPH_STRING RevVersion; - PPH_STRING Size; - PPH_STRING SetupFilePath; -} PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; - -VOID TaskDialogLinkClicked( - _In_ PPH_UPDATER_CONTEXT Context - ); - -NTSTATUS GeoIPUpdateThread( - _In_ PVOID Parameter - ); - -VOID ShowGeoIPUpdateDialog( - _In_opt_ HWND Parent - ); - -// page1.c -VOID ShowCheckForUpdatesDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// page2.c -VOID ShowCheckingForUpdatesDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// page5.c -VOID ShowInstallRestartDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// Copied from mstcpip.h due to PH sdk conflicts -#define INADDR_ANY (ULONG)0x00000000 -#define INADDR_LOOPBACK 0x7f000001 - -FORCEINLINE -BOOLEAN -IN4_IS_ADDR_UNSPECIFIED(_In_ CONST IN_ADDR *a) -{ - return (BOOLEAN)(a->s_addr == INADDR_ANY); -} - -FORCEINLINE -BOOLEAN -IN4_IS_ADDR_LOOPBACK(_In_ CONST IN_ADDR *a) -{ - return (BOOLEAN)(*((PUCHAR)a) == 0x7f); // 127/8 -} - -// ports.c -typedef struct _RESOLVED_PORT -{ - PWSTR Name; - USHORT Port; -} RESOLVED_PORT; - -RESOLVED_PORT ResolvedPortsTable[6265]; - -#endif \ No newline at end of file +/* + * Process Hacker Network Tools - + * Main header + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2012-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _NET_TOOLS_H_ +#define _NET_TOOLS_H_ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "resource.h" + +#define PLUGIN_NAME L"ProcessHacker.NetworkTools" +#define SETTING_NAME_DB_LOCATION (PLUGIN_NAME L".GeoDbPath") +#define SETTING_NAME_ADDRESS_HISTORY (PLUGIN_NAME L".AddressHistory") +#define SETTING_NAME_PING_WINDOW_POSITION (PLUGIN_NAME L".PingWindowPosition") +#define SETTING_NAME_PING_WINDOW_SIZE (PLUGIN_NAME L".PingWindowSize") +#define SETTING_NAME_PING_MINIMUM_SCALING (PLUGIN_NAME L".PingMinScaling") +#define SETTING_NAME_PING_SIZE (PLUGIN_NAME L".PingSize") +#define SETTING_NAME_TRACERT_WINDOW_POSITION (PLUGIN_NAME L".TracertWindowPosition") +#define SETTING_NAME_TRACERT_WINDOW_SIZE (PLUGIN_NAME L".TracertWindowSize") +#define SETTING_NAME_TRACERT_TREE_LIST_COLUMNS (PLUGIN_NAME L".TracertTreeColumns") +#define SETTING_NAME_TRACERT_TREE_LIST_SORT (PLUGIN_NAME L".TracertTreeSort") +#define SETTING_NAME_TRACERT_MAX_HOPS (PLUGIN_NAME L".TracertMaxHops") +#define SETTING_NAME_WHOIS_WINDOW_POSITION (PLUGIN_NAME L".WhoisWindowPosition") +#define SETTING_NAME_WHOIS_WINDOW_SIZE (PLUGIN_NAME L".WhoisWindowSize") +#define SETTING_NAME_EXTENDED_TCP_STATS (PLUGIN_NAME L".EnableExtendedTcpStats") + +extern PPH_PLUGIN PluginInstance; +extern BOOLEAN GeoDbLoaded; +extern BOOLEAN GeoDbExpired; +extern PPH_STRING SearchboxText; + +// ICMP Packet Length: (msdn: IcmpSendEcho2/Icmp6SendEcho2) +// The buffer must be large enough to hold at least one ICMP_ECHO_REPLY or ICMPV6_ECHO_REPLY structure +// + the number of bytes of data specified in the RequestSize parameter. +// This buffer should also be large enough to also hold 8 more bytes of data (the size of an ICMP error message) +// + space for an IO_STATUS_BLOCK structure. +#define ICMP_BUFFER_SIZE(EchoReplyLength, Buffer) \ + (ULONG)(((EchoReplyLength) + (Buffer)->Length) + 8 + sizeof(IO_STATUS_BLOCK) + MAX_OPT_SIZE) + +#define BITS_IN_ONE_BYTE 8 +#define NDIS_UNIT_OF_MEASUREMENT 100 + +// The ICMPV6_ECHO_REPLY struct doesn't have a field to access the reply data, +// so copy the struct and add an additional Data field. +typedef struct _ICMPV6_ECHO_REPLY2 +{ + IPV6_ADDRESS_EX Address; + ULONG Status; + unsigned int RoundTripTime; + BYTE Data[ANYSIZE_ARRAY]; // custom +} ICMPV6_ECHO_REPLY2, *PICMPV6_ECHO_REPLY2; + +typedef enum _PH_NETWORK_ACTION +{ + NETWORK_ACTION_PING = 1, + NETWORK_ACTION_TRACEROUTE, + NETWORK_ACTION_WHOIS, + NETWORK_ACTION_FINISH, + NETWORK_ACTION_PATHPING, + MAINMENU_ACTION_PING, + MAINMENU_ACTION_TRACERT, + MAINMENU_ACTION_WHOIS, + MAINMENU_ACTION_GEOIP_UPDATE, + MENU_ACTION_COPY, +} PH_NETWORK_ACTION; + +#define UPDATE_MENUITEM 1005 +#define NTM_RECEIVEDTRACE (WM_APP + 1) +#define NTM_RECEIVEDWHOIS (WM_APP + 2) +#define NTM_RECEIVEDFINISH (WM_APP + 3) +#define WM_TRACERT_UPDATE (WM_APP + 4) +#define WM_TRACERT_HOSTNAME (WM_APP + 5) +#define WM_TRACERT_COUNTRY (WM_APP + 6) + +#define PH_SHOWDIALOG (WM_APP + 10) +#define PH_SHOWINSTALL (WM_APP + 11) +#define PH_SHOWERROR (WM_APP + 12) + +typedef struct _NETWORK_PING_CONTEXT +{ + HWND WindowHandle; + HWND StatusHandle; + HWND PingGraphHandle; + HFONT FontHandle; + + ULONG CurrentPingMs; + ULONG MaxPingTimeout; + ULONG HashFailCount; + ULONG UnknownAddrCount; + ULONG PingMinMs; + ULONG PingMaxMs; + ULONG PingSentCount; + ULONG PingRecvCount; + ULONG PingLossCount; + + PH_NETWORK_ACTION Action; + PH_LAYOUT_MANAGER LayoutManager; + PH_WORK_QUEUE PingWorkQueue; + PH_GRAPH_STATE PingGraphState; + PH_CIRCULAR_BUFFER_ULONG PingHistory; + PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration; + + PH_IP_ENDPOINT RemoteEndpoint; + WCHAR IpAddressString[INET6_ADDRSTRLEN + 1]; +} NETWORK_PING_CONTEXT, *PNETWORK_PING_CONTEXT; + +// ping.c + +VOID ShowPingWindow( + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +VOID ShowPingWindowFromAddress( + _In_ PH_IP_ENDPOINT RemoteEndpoint + ); + +// whois.c + +typedef struct _NETWORK_WHOIS_CONTEXT +{ + HWND WindowHandle; + HWND RichEditHandle; + HFONT FontHandle; + + PH_NETWORK_ACTION Action; + PH_LAYOUT_MANAGER LayoutManager; + + PH_IP_ENDPOINT RemoteEndpoint; + WCHAR IpAddressString[INET6_ADDRSTRLEN + 1]; +} NETWORK_WHOIS_CONTEXT, *PNETWORK_WHOIS_CONTEXT; + +// TDM_NAVIGATE_PAGE can not be called from other threads without comctl32.dll throwing access violations +// after navigating to the page and you press keys such as ctrl, alt, home and insert. (dmex) +#define TaskDialogNavigatePage(WindowHandle, Config) \ + assert(HandleToUlong(NtCurrentThreadId()) == GetWindowThreadProcessId(WindowHandle, NULL)); \ + SendMessage(WindowHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)Config); + +VOID ShowWhoisWindow( + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +VOID ShowWhoisWindowFromAddress( + _In_ PH_IP_ENDPOINT RemoteEndpoint + ); + +// tracert.c + +typedef struct _NETWORK_TRACERT_CONTEXT +{ + HWND WindowHandle; + HWND SearchboxHandle; + HWND TreeNewHandle; + HFONT FontHandle; + PH_LAYOUT_MANAGER LayoutManager; + + ULONG MaximumHops; + BOOLEAN Cancel; + PH_WORK_QUEUE WorkQueue; + + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; + PPH_LIST NodeRootList; + + PH_IP_ENDPOINT RemoteEndpoint; + WCHAR IpAddressString[INET6_ADDRSTRLEN + 1]; +} NETWORK_TRACERT_CONTEXT, *PNETWORK_TRACERT_CONTEXT; + +VOID ShowTracertWindow( + _In_ PPH_NETWORK_ITEM NetworkItem + ); + +VOID ShowTracertWindowFromAddress( + _In_ PH_IP_ENDPOINT RemoteEndpoint + ); + +// options.c + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// country.c +typedef struct _NETWORK_EXTENSION +{ + LIST_ENTRY ListEntry; + PPH_NETWORK_ITEM NetworkItem; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN CountryValid : 1; + BOOLEAN LocalValid : 1; + BOOLEAN RemoteValid : 1; + BOOLEAN StatsEnabled : 1; + BOOLEAN Spare : 4; + }; + }; + + INT CountryIconIndex; + PPH_STRING LocalServiceName; + PPH_STRING RemoteServiceName; + PPH_STRING RemoteCountryCode; + PPH_STRING RemoteCountryName; + + ULONG64 NumberOfBytesOut; + ULONG64 NumberOfBytesIn; + ULONG64 NumberOfLostPackets; + ULONG SampleRtt; + + PPH_STRING BytesIn; + PPH_STRING BytesOut; + PPH_STRING PacketLossText; + PPH_STRING LatencyText; +} NETWORK_EXTENSION, *PNETWORK_EXTENSION; + +typedef enum _NETWORK_COLUMN_ID +{ + NETWORK_COLUMN_ID_NONE, + NETWORK_COLUMN_ID_REMOTE_COUNTRY, + NETWORK_COLUMN_ID_LOCAL_SERVICE, + NETWORK_COLUMN_ID_REMOTE_SERVICE, + NETWORK_COLUMN_ID_BYTES_IN, + NETWORK_COLUMN_ID_BYTES_OUT, + NETWORK_COLUMN_ID_PACKETLOSS, + NETWORK_COLUMN_ID_LATENCY +} NETWORK_COLUMN_ID; + +// country.c + +PPH_STRING NetToolsGetGeoLiteDbPath( + VOID + ); +VOID LoadGeoLiteDb( + VOID + ); +VOID FreeGeoLiteDb( + VOID + ); + +BOOLEAN LookupCountryCode( + _In_ PH_IP_ADDRESS RemoteAddress, + _Out_ PPH_STRING *CountryCode, + _Out_ PPH_STRING *CountryName + ); + +BOOLEAN LookupSockInAddr4CountryCode( + _In_ IN_ADDR RemoteAddress, + _Out_ PPH_STRING *CountryCode, + _Out_ PPH_STRING *CountryName + ); + +BOOLEAN LookupSockInAddr6CountryCode( + _In_ IN6_ADDR RemoteAddress, + _Out_ PPH_STRING *CountryCode, + _Out_ PPH_STRING *CountryName + ); + +INT LookupCountryIcon( + _In_ PPH_STRING Name + ); + +VOID DrawCountryIcon( + _In_ HDC hdc, + _In_ RECT rect, + _In_ INT Index + ); + +typedef struct _PH_UPDATER_CONTEXT +{ + HWND DialogHandle; + HICON IconSmallHandle; + HICON IconLargeHandle; + WNDPROC DefaultWindowProc; + + PPH_STRING FileDownloadUrl; +} PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; + +VOID TaskDialogLinkClicked( + _In_ PPH_UPDATER_CONTEXT Context + ); + +NTSTATUS GeoIPUpdateThread( + _In_ PVOID Parameter + ); + +VOID ShowGeoIPUpdateDialog( + _In_opt_ HWND Parent + ); + +// pages.c + +extern PH_EVENT InitializedEvent; + +VOID ShowDbCheckForUpdatesDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +VOID ShowDbCheckingForUpdatesDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +VOID ShowDbInstallRestartDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +VOID ShowDbUpdateFailedDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +// ports.c +typedef struct _RESOLVED_PORT +{ + PWSTR Name; + USHORT Port; +} RESOLVED_PORT; + +RESOLVED_PORT ResolvedPortsTable[6265]; + +#endif diff --git a/plugins/NetworkTools/options.c b/plugins/NetworkTools/options.c index 69cbeda2cebd..58b3628ccb44 100644 --- a/plugins/NetworkTools/options.c +++ b/plugins/NetworkTools/options.c @@ -1,81 +1,101 @@ -/* - * Process Hacker Network Tools - - * options dialog - * - * Copyright (C) 2010-2013 wj32 - * Copyright (C) 2012-2013 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "nettools.h" - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetDlgItemInt(hwndDlg, IDC_PINGPACKETLENGTH, PhGetIntegerSetting(SETTING_NAME_PING_SIZE), FALSE); - SetDlgItemInt(hwndDlg, IDC_MAXHOPS, PhGetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS), FALSE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - PhSetIntegerSetting(SETTING_NAME_PING_SIZE, GetDlgItemInt(hwndDlg, IDC_PINGPACKETLENGTH, NULL, FALSE)); - PhSetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS, GetDlgItemInt(hwndDlg, IDC_MAXHOPS, NULL, FALSE)); - - EndDialog(hwndDlg, IDCANCEL); - } - break; - case IDC_GEOIP: - { - if (PhGetOwnTokenAttributes().Elevated) - { - ShowGeoIPUpdateDialog(NULL); - } - } - break; - } - } - break; - } - - return FALSE; -} - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - (HWND)Parent, - OptionsDlgProc - ); -} \ No newline at end of file +/* + * Process Hacker Network Tools - + * options dialog + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2012-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "nettools.h" + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSetDialogItemValue(hwndDlg, IDC_PINGPACKETLENGTH, PhGetIntegerSetting(SETTING_NAME_PING_SIZE), FALSE); + PhSetDialogItemValue(hwndDlg, IDC_MAXHOPS, PhGetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS), FALSE); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_EXTENDED_TCP), PhGetIntegerSetting(SETTING_NAME_EXTENDED_TCP_STATS) ? BST_CHECKED : BST_UNCHECKED); + + PhSetDialogItemText(hwndDlg, IDC_DATABASE, PhaGetStringSetting(SETTING_NAME_DB_LOCATION)->Buffer); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DATABASE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_BROWSE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + } + break; + case WM_DESTROY: + { + PhSetIntegerSetting(SETTING_NAME_PING_SIZE, PhGetDialogItemValue(hwndDlg, IDC_PINGPACKETLENGTH)); + PhSetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS, PhGetDialogItemValue(hwndDlg, IDC_MAXHOPS)); + PhSetIntegerSetting(SETTING_NAME_EXTENDED_TCP_STATS, Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_EXTENDED_TCP)) == BST_CHECKED); + + PhSetStringSetting2(SETTING_NAME_DB_LOCATION, &PhaGetDlgItemText(hwndDlg, IDC_DATABASE)->sr); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"XML files (*.xml)", L"*.xml" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, RTL_NUMBER_OF(filters)); + + fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_DATABASE))); + PhSetFileDialogFileName(fileDialog, fileName->Buffer); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + PhSetDialogItemText(hwndDlg, IDC_DATABASE, fileName->Buffer); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/NetworkTools/pages.c b/plugins/NetworkTools/pages.c index f2bce861c8ca..75def35df281 100644 --- a/plugins/NetworkTools/pages.c +++ b/plugins/NetworkTools/pages.c @@ -33,7 +33,7 @@ TASKDIALOG_BUTTON DownloadButtonArray[] = { IDOK, L"Download" } }; -HRESULT CALLBACK CheckForUpdatesCallbackProc( +HRESULT CALLBACK CheckForUpdatesDbCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -46,12 +46,13 @@ HRESULT CALLBACK CheckForUpdatesCallbackProc( switch (uMsg) { case TDN_NAVIGATED: + PhSetEvent(&InitializedEvent); break; case TDN_BUTTON_CLICKED: { if ((INT)wParam == IDOK) { - ShowCheckingForUpdatesDialog(context); + ShowDbCheckingForUpdatesDialog(context); return S_FALSE; } } @@ -66,7 +67,7 @@ HRESULT CALLBACK CheckForUpdatesCallbackProc( return S_OK; } -HRESULT CALLBACK CheckingForUpdatesCallbackProc( +HRESULT CALLBACK CheckingForUpdatesDbCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -83,6 +84,7 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + PhReferenceObject(context); PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), GeoIPUpdateThread, context); } break; @@ -91,7 +93,7 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( return S_OK; } -HRESULT CALLBACK RestartTaskDialogCallbackProc( +HRESULT CALLBACK RestartDbTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -126,7 +128,7 @@ HRESULT CALLBACK RestartTaskDialogCallbackProc( return S_OK; } -HRESULT CALLBACK FinalTaskDialogCallbackProc( +HRESULT CALLBACK FinalDbTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -148,7 +150,11 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( break; case TDN_BUTTON_CLICKED: { - + if ((INT)wParam == IDRETRY) + { + ShowDbCheckForUpdatesDialog(context); + return S_FALSE; + } } break; } @@ -156,7 +162,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( return S_OK; } -VOID ShowCheckForUpdatesDialog( +VOID ShowDbCheckForUpdatesDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -170,17 +176,17 @@ VOID ShowCheckForUpdatesDialog( config.cxWidth = 200; config.pButtons = DownloadButtonArray; config.cButtons = ARRAYSIZE(DownloadButtonArray); - config.pfCallback = CheckForUpdatesCallbackProc; + config.pfCallback = CheckForUpdatesDbCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = L"Network Tools - GeoIP Updater"; config.pszMainInstruction = L"Download the latest GeoIP database?"; config.pszContent = L"This product includes GeoLite2 data created by MaxMind, available from http://www.maxmind.com\r\n\r\nSelect download to continue."; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + TaskDialogNavigatePage(Context->DialogHandle, &config); } -VOID ShowCheckingForUpdatesDialog( +VOID ShowDbCheckingForUpdatesDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -192,17 +198,17 @@ VOID ShowCheckingForUpdatesDialog( config.dwCommonButtons = TDCBF_CLOSE_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; - config.pfCallback = CheckingForUpdatesCallbackProc; + config.pfCallback = CheckingForUpdatesDbCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = L"Network Tools - GeoIP Updater"; config.pszMainInstruction = L"Downloading"; config.pszContent = L"Downloaded: ~ of ~ (~%%)\r\nSpeed: ~/s"; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + TaskDialogNavigatePage(Context->DialogHandle, &config); } -VOID ShowInstallRestartDialog( +VOID ShowDbInstallRestartDialog( _In_ PPH_UPDATER_CONTEXT Context ) { @@ -214,7 +220,7 @@ VOID ShowInstallRestartDialog( config.dwCommonButtons = TDCBF_CLOSE_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; - config.pfCallback = RestartTaskDialogCallbackProc; + config.pfCallback = RestartDbTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pButtons = RestartButtonArray; config.cButtons = ARRAYSIZE(RestartButtonArray); @@ -223,12 +229,11 @@ VOID ShowInstallRestartDialog( config.pszMainInstruction = L"The GeoIP database has been installed"; config.pszContent = L"You need to restart Process Hacker for the changes to take effect..."; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + TaskDialogNavigatePage(Context->DialogHandle, &config); } -VOID ShowUpdateFailedDialog( - _In_ PPH_UPDATER_CONTEXT Context, - _In_ BOOLEAN HashFailed +VOID ShowDbUpdateFailedDialog( + _In_ PPH_UPDATER_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -240,20 +245,12 @@ VOID ShowUpdateFailedDialog( config.dwCommonButtons = TDCBF_CLOSE_BUTTON | TDCBF_RETRY_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; - config.pfCallback = FinalTaskDialogCallbackProc; + config.pfCallback = FinalDbTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = L"Network Tools - GeoIP Updater"; config.pszMainInstruction = L"Error downloading GeoIP database."; + config.pszContent = L"Click Retry to download the database again."; - if (HashFailed) - { - config.pszContent = L"Hash check failed. Click Retry to download the database again."; - } - else - { - config.pszContent = L"Click Retry to download the database again."; - } - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file + TaskDialogNavigatePage(Context->DialogHandle, &config); +} diff --git a/plugins/NetworkTools/ping.c b/plugins/NetworkTools/ping.c index c485c8131f33..93ba2c7a0213 100644 --- a/plugins/NetworkTools/ping.c +++ b/plugins/NetworkTools/ping.c @@ -1,607 +1,606 @@ -/* - * Process Hacker Network Tools - - * Ping dialog - * - * Copyright (C) 2015 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "nettools.h" -#include - -#define WM_PING_UPDATE (WM_APP + 151) - -static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; -static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; - -NTSTATUS NetworkPingThreadStart( - _In_ PVOID Parameter - ) -{ - PNETWORK_PING_CONTEXT context = (PNETWORK_PING_CONTEXT)Parameter; - HANDLE icmpHandle = INVALID_HANDLE_VALUE; - ULONG icmpCurrentPingMs = 0; - ULONG icmpReplyCount = 0; - ULONG icmpReplyLength = 0; - PVOID icmpReplyBuffer = NULL; - PPH_BYTES icmpEchoBuffer = NULL; - PPH_STRING icmpRandString = NULL; - IP_OPTION_INFORMATION pingOptions = - { - 255, // Time To Live - 0, // Type Of Service - IP_FLAG_DF, // IP header flags - 0 // Size of options data - }; - //pingOptions.Flags |= IP_FLAG_REVERSE; - - if (icmpRandString = PhCreateStringEx(NULL, PhGetIntegerSetting(SETTING_NAME_PING_SIZE) * 2 + 2)) - { - PhGenerateRandomAlphaString(icmpRandString->Buffer, (ULONG)icmpRandString->Length / sizeof(WCHAR)); - - icmpEchoBuffer = PhConvertUtf16ToMultiByte(icmpRandString->Buffer); - PhDereferenceObject(icmpRandString); - } - - if (context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - SOCKADDR_IN6 icmp6LocalAddr = { 0 }; - SOCKADDR_IN6 icmp6RemoteAddr = { 0 }; - PICMPV6_ECHO_REPLY2 icmp6ReplyStruct = NULL; - - // TODO: Cache handle. - if ((icmpHandle = Icmp6CreateFile()) == INVALID_HANDLE_VALUE) - goto CleanupExit; - - // Set Local IPv6-ANY address. - icmp6LocalAddr.sin6_addr = in6addr_any; - icmp6LocalAddr.sin6_family = AF_INET6; - - // Set Remote IPv6 address. - icmp6RemoteAddr.sin6_addr = context->RemoteEndpoint.Address.In6Addr; - //icmp6RemoteAddr.sin6_port = _byteswap_ushort((USHORT)context->NetworkItem->RemoteEndpoint.Port); - - // Allocate ICMPv6 message. - icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMPV6_ECHO_REPLY), icmpEchoBuffer); - icmpReplyBuffer = PhAllocate(icmpReplyLength); - memset(icmpReplyBuffer, 0, icmpReplyLength); - - InterlockedIncrement(&context->PingSentCount); - - // Send ICMPv6 ping... - icmpReplyCount = Icmp6SendEcho2( - icmpHandle, - NULL, - NULL, - NULL, - &icmp6LocalAddr, - &icmp6RemoteAddr, - icmpEchoBuffer->Buffer, - (USHORT)icmpEchoBuffer->Length, - &pingOptions, - icmpReplyBuffer, - icmpReplyLength, - context->MaxPingTimeout - ); - - icmp6ReplyStruct = (PICMPV6_ECHO_REPLY2)icmpReplyBuffer; - - if (icmp6ReplyStruct->Status == IP_SUCCESS) - { - BOOLEAN icmpPacketSignature = FALSE; - - if (!RtlEqualMemory( - icmp6ReplyStruct->Address.sin6_addr, - context->RemoteEndpoint.Address.In6Addr.u.Word, - sizeof(icmp6ReplyStruct->Address.sin6_addr) - )) - { - InterlockedIncrement(&context->UnknownAddrCount); - } - - icmpPacketSignature = RtlEqualMemory( - icmpEchoBuffer->Buffer, - icmp6ReplyStruct->Data, - icmpEchoBuffer->Length - ); - - if (!icmpPacketSignature) - { - InterlockedIncrement(&context->HashFailCount); - } - } - else - { - InterlockedIncrement(&context->PingLossCount); - } - - icmpCurrentPingMs = icmp6ReplyStruct->RoundTripTime; - } - else - { - IPAddr icmpLocalAddr = 0; - IPAddr icmpRemoteAddr = 0; - BOOLEAN icmpPacketSignature = FALSE; - PICMP_ECHO_REPLY icmpReplyStruct = NULL; - - // TODO: Cache handle. - if ((icmpHandle = IcmpCreateFile()) == INVALID_HANDLE_VALUE) - goto CleanupExit; - - // Set Local IPv4-ANY address. - icmpLocalAddr = in4addr_any.s_addr; - - // Set Remote IPv4 address. - icmpRemoteAddr = context->RemoteEndpoint.Address.InAddr.s_addr; - - // Allocate ICMPv4 message. - icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMP_ECHO_REPLY), icmpEchoBuffer); - icmpReplyBuffer = PhAllocate(icmpReplyLength); - memset(icmpReplyBuffer, 0, icmpReplyLength); - - InterlockedIncrement(&context->PingSentCount); - - // Send ICMPv4 ping... - icmpReplyCount = IcmpSendEcho2Ex( - icmpHandle, - NULL, - NULL, - NULL, - icmpLocalAddr, - icmpRemoteAddr, - NULL, - 0, - &pingOptions, - icmpReplyBuffer, - icmpReplyLength, - context->MaxPingTimeout - ); - - icmpReplyStruct = (PICMP_ECHO_REPLY)icmpReplyBuffer; - - if (icmpReplyStruct->Status == IP_SUCCESS) - { - if (icmpReplyStruct->Address != context->RemoteEndpoint.Address.InAddr.s_addr) - { - InterlockedIncrement(&context->UnknownAddrCount); - } - - if (icmpReplyStruct->DataSize == icmpEchoBuffer->Length) - { - icmpPacketSignature = RtlEqualMemory( - icmpEchoBuffer->Buffer, - icmpReplyStruct->Data, - icmpReplyStruct->DataSize); - } - - if (!icmpPacketSignature) - { - InterlockedIncrement(&context->HashFailCount); - } - } - else - { - InterlockedIncrement(&context->PingLossCount); - } - - icmpCurrentPingMs = icmpReplyStruct->RoundTripTime; - } - - if (context->PingMinMs == 0 || icmpCurrentPingMs < context->PingMinMs) - context->PingMinMs = icmpCurrentPingMs; - if (icmpCurrentPingMs > context->PingMaxMs) - context->PingMaxMs = icmpCurrentPingMs; - - context->CurrentPingMs = icmpCurrentPingMs; - - InterlockedIncrement(&context->PingRecvCount); - - PhAddItemCircularBuffer_ULONG(&context->PingHistory, icmpCurrentPingMs); - -CleanupExit: - - if (icmpEchoBuffer) - { - PhDereferenceObject(icmpEchoBuffer); - } - - if (icmpHandle != INVALID_HANDLE_VALUE) - { - IcmpCloseHandle(icmpHandle); - } - - if (icmpReplyBuffer) - { - PhFree(icmpReplyBuffer); - } - - PostMessage(context->WindowHandle, WM_PING_UPDATE, 0, 0); - - return STATUS_SUCCESS; -} - -VOID NTAPI NetworkPingUpdateHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PNETWORK_PING_CONTEXT context = (PNETWORK_PING_CONTEXT)Context; - - // Queue up the next ping request - PhQueueItemWorkQueue(&context->PingWorkQueue, NetworkPingThreadStart, (PVOID)context); -} - -VOID NetworkPingUpdateGraph( - _In_ PNETWORK_PING_CONTEXT Context - ) -{ - Context->PingGraphState.Valid = FALSE; - Context->PingGraphState.TooltipIndex = -1; - Graph_MoveGrid(Context->PingGraphHandle, 1); - Graph_Draw(Context->PingGraphHandle); - Graph_UpdateTooltip(Context->PingGraphHandle); - InvalidateRect(Context->PingGraphHandle, NULL, FALSE); -} - -INT_PTR CALLBACK NetworkPingWndProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PNETWORK_PING_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PNETWORK_PING_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PNETWORK_PING_CONTEXT)GetProp(hwndDlg, L"Context"); - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_LAYOUT_ITEM panelItem; - - // We have already set the group boxes to have WS_EX_TRANSPARENT to fix - // the drawing issue that arises when using WS_CLIPCHILDREN. However - // in removing the flicker from the graphs the group boxes will now flicker. - // It's a good tradeoff since no one stares at the group boxes. - PhSetWindowStyle(hwndDlg, WS_CLIPCHILDREN, WS_CLIPCHILDREN); - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - context->WindowHandle = hwndDlg; - context->StatusHandle = GetDlgItem(hwndDlg, IDC_MAINTEXT); - context->MaxPingTimeout = PhGetIntegerSetting(SETTING_NAME_PING_MINIMUM_SCALING); - context->FontHandle = CommonCreateFont(-15, FW_MEDIUM, context->StatusHandle); - context->PingGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 3, - 3, - hwndDlg, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(context->PingGraphHandle, TRUE); - - PhInitializeWorkQueue(&context->PingWorkQueue, 0, 20, 5000); - PhInitializeGraphState(&context->PingGraphState); - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhInitializeCircularBuffer_ULONG(&context->PingHistory, PhGetIntegerSetting(L"SampleCount")); - - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_PANEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT| PH_LAYOUT_FORCE_INVALIDATE); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_AVG), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_MIN), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_MAX), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PINGS_SENT), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PINGS_LOST), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_BAD_HASH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ANON_ADDR), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); - panelItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PING_LAYOUT), NULL, PH_ANCHOR_ALL); - PhAddLayoutItemEx(&context->LayoutManager, context->PingGraphHandle, NULL, PH_ANCHOR_ALL, panelItem->Margin); - PhLoadWindowPlacementFromSetting(SETTING_NAME_PING_WINDOW_POSITION, SETTING_NAME_PING_WINDOW_SIZE, hwndDlg); - - SetWindowText(hwndDlg, PhaFormatString(L"Ping %s", context->IpAddressString)->Buffer); - SetWindowText(context->StatusHandle, PhaFormatString(L"Pinging %s with %lu bytes of data...", - context->IpAddressString, - PhGetIntegerSetting(SETTING_NAME_PING_SIZE))->Buffer - ); - - PhRegisterCallback( - &PhProcessesUpdatedEvent, - NetworkPingUpdateHandler, - context, - &context->ProcessesUpdatedRegistration - ); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - return TRUE; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - case IDOK: - DestroyWindow(hwndDlg); - break; - } - } - break; - case WM_DESTROY: - { - PhUnregisterCallback( - &PhProcessesUpdatedEvent, - &context->ProcessesUpdatedRegistration - ); - - PhSaveWindowPlacementToSetting( - SETTING_NAME_PING_WINDOW_POSITION, - SETTING_NAME_PING_WINDOW_SIZE, - hwndDlg - ); - - if (context->PingGraphHandle) - DestroyWindow(context->PingGraphHandle); - - if (context->FontHandle) - DeleteObject(context->FontHandle); - - PhDeleteWorkQueue(&context->PingWorkQueue); - PhDeleteGraphState(&context->PingGraphState); - PhDeleteLayoutManager(&context->LayoutManager); - - RemoveProp(hwndDlg, L"Context"); - PhFree(context); - - PostQuitMessage(0); - } - break; - case WM_SIZE: - PhLayoutManagerLayout(&context->LayoutManager); - break; - case WM_SIZING: - //PhResizingMinimumSize((PRECT)lParam, wParam, 420, 250); - break; - case WM_PING_UPDATE: - { - ULONG maxGraphHeight = 0; - ULONG pingAvgValue = 0; - - NetworkPingUpdateGraph(context); - - for (ULONG i = 0; i < context->PingHistory.Count; i++) - { - maxGraphHeight = maxGraphHeight + PhGetItemCircularBuffer_ULONG(&context->PingHistory, i); - pingAvgValue = maxGraphHeight / context->PingHistory.Count; - } - - SetDlgItemText(hwndDlg, IDC_ICMP_AVG, PhaFormatString( - L"Average: %lums", pingAvgValue)->Buffer); - SetDlgItemText(hwndDlg, IDC_ICMP_MIN, PhaFormatString( - L"Minimum: %lums", context->PingMinMs)->Buffer); - SetDlgItemText(hwndDlg, IDC_ICMP_MAX, PhaFormatString( - L"Maximum: %lums", context->PingMaxMs)->Buffer); - - SetDlgItemText(hwndDlg, IDC_PINGS_SENT, PhaFormatString( - L"Pings sent: %lu", context->PingSentCount)->Buffer); - SetDlgItemText(hwndDlg, IDC_PINGS_LOST, PhaFormatString( - L"Pings lost: %lu (%.0f%%)", context->PingLossCount, - ((FLOAT)context->PingLossCount / context->PingSentCount * 100))->Buffer); - - //SetDlgItemText(hwndDlg, IDC_BAD_HASH, PhaFormatString( - // L"Bad hashes: %lu", context->HashFailCount)->Buffer); - SetDlgItemText(hwndDlg, IDC_ANON_ADDR, PhaFormatString( - L"Anon replies: %lu", context->UnknownAddrCount)->Buffer); - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case GCN_GETDRAWINFO: - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - if (header->hwndFrom == context->PingGraphHandle) - { - if (PhGetIntegerSetting(L"GraphShowText")) - { - HDC hdc = Graph_GetBufferedContext(context->PingGraphHandle); - - PhMoveReference(&context->PingGraphState.Text, - PhFormatString(L"%lu ms", context->CurrentPingMs) - ); - - SelectObject(hdc, PhApplicationFont); - PhSetGraphText(hdc, drawInfo, &context->PingGraphState.Text->sr, - &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); - } - else - { - drawInfo->Text.Buffer = NULL; - } - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); - PhGraphStateGetDrawInfo(&context->PingGraphState, getDrawInfo, context->PingHistory.Count); - - if (!context->PingGraphState.Valid) - { - ULONG i; - FLOAT max = (FLOAT)context->MaxPingTimeout; // minimum scaling - - for (i = 0; i < drawInfo->LineDataCount; i++) - { - FLOAT data1; - - context->PingGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->PingHistory, i); - - if (max < data1) - max = data1; - } - - // Scale the data. - PhDivideSinglesBySingle( - context->PingGraphState.Data1, - max, - drawInfo->LineDataCount - ); - - context->PingGraphState.Valid = TRUE; - } - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (header->hwndFrom == context->PingGraphHandle) - { - if (context->PingGraphState.TooltipIndex != getTooltipText->Index) - { - ULONG pingMs = PhGetItemCircularBuffer_ULONG(&context->PingHistory, getTooltipText->Index); - - PhMoveReference(&context->PingGraphState.TooltipText, PhFormatString( - L"%lu ms\n%s", - pingMs, - PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer) - ); - } - - getTooltipText->Text = context->PingGraphState.TooltipText->sr; - } - } - } - break; - } - } - break; - } - - return FALSE; -} - -NTSTATUS NetworkPingDialogThreadStart( - _In_ PVOID Parameter - ) -{ - BOOL result; - MSG message; - HWND windowHandle; - PH_AUTO_POOL autoPool; - - PhInitializeAutoPool(&autoPool); - - windowHandle = CreateDialogParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_PING), - NULL, - NetworkPingWndProc, - (LPARAM)Parameter - ); - - ShowWindow(windowHandle, SW_SHOW); - SetForegroundWindow(windowHandle); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (!IsDialogMessage(windowHandle, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -VOID ShowPingWindow( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - HANDLE dialogThread; - PNETWORK_PING_CONTEXT context; - - context = (PNETWORK_PING_CONTEXT)PhAllocate(sizeof(NETWORK_PING_CONTEXT)); - memset(context, 0, sizeof(NETWORK_PING_CONTEXT)); - - if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - RtlIpv4AddressToString(&NetworkItem->RemoteEndpoint.Address.InAddr, context->IpAddressString); - else - RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); - - context->RemoteEndpoint = NetworkItem->RemoteEndpoint; - - if (dialogThread = PhCreateThread(0, NetworkPingDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } -} - -VOID ShowPingWindowFromAddress( - _In_ PH_IP_ENDPOINT RemoteEndpoint - ) -{ - HANDLE dialogThread; - PNETWORK_PING_CONTEXT context; - - context = (PNETWORK_PING_CONTEXT)PhAllocate(sizeof(NETWORK_PING_CONTEXT)); - memset(context, 0, sizeof(NETWORK_PING_CONTEXT)); - - if (RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - RtlIpv4AddressToString(&RemoteEndpoint.Address.InAddr, context->IpAddressString); - } - else - { - RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); - } - - context->RemoteEndpoint = RemoteEndpoint; - - if (dialogThread = PhCreateThread(0, NetworkPingDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } -} \ No newline at end of file +/* + * Process Hacker Network Tools - + * Ping dialog + * + * Copyright (C) 2015 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "nettools.h" +#include + +#define WM_PING_UPDATE (WM_APP + 151) + +static RECT NormalGraphTextMargin = { 5, 5, 5, 5 }; +static RECT NormalGraphTextPadding = { 3, 3, 3, 3 }; + +NTSTATUS NetworkPingThreadStart( + _In_ PVOID Parameter + ) +{ + PNETWORK_PING_CONTEXT context = (PNETWORK_PING_CONTEXT)Parameter; + HANDLE icmpHandle = INVALID_HANDLE_VALUE; + ULONG icmpCurrentPingMs = 0; + ULONG icmpReplyCount = 0; + ULONG icmpReplyLength = 0; + PVOID icmpReplyBuffer = NULL; + PPH_BYTES icmpEchoBuffer = NULL; + PPH_STRING icmpRandString = NULL; + IP_OPTION_INFORMATION pingOptions = + { + 255, // Time To Live + 0, // Type Of Service + IP_FLAG_DF, // IP header flags + 0 // Size of options data + }; + //pingOptions.Flags |= IP_FLAG_REVERSE; + + if (icmpRandString = PhCreateStringEx(NULL, PhGetIntegerSetting(SETTING_NAME_PING_SIZE) * sizeof(WCHAR) + sizeof(UNICODE_NULL))) + { + PhGenerateRandomAlphaString(icmpRandString->Buffer, (ULONG)icmpRandString->Length / sizeof(WCHAR)); + + icmpEchoBuffer = PhConvertUtf16ToMultiByte(icmpRandString->Buffer); + PhDereferenceObject(icmpRandString); + } + + if (context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + SOCKADDR_IN6 icmp6LocalAddr = { 0 }; + SOCKADDR_IN6 icmp6RemoteAddr = { 0 }; + PICMPV6_ECHO_REPLY2 icmp6ReplyStruct = NULL; + + // TODO: Cache handle. + if ((icmpHandle = Icmp6CreateFile()) == INVALID_HANDLE_VALUE) + goto CleanupExit; + + // Set Local IPv6-ANY address. + icmp6LocalAddr.sin6_addr = in6addr_any; + icmp6LocalAddr.sin6_family = AF_INET6; + + // Set Remote IPv6 address. + icmp6RemoteAddr.sin6_addr = context->RemoteEndpoint.Address.In6Addr; + //icmp6RemoteAddr.sin6_port = _byteswap_ushort((USHORT)context->NetworkItem->RemoteEndpoint.Port); + + // Allocate ICMPv6 message. + icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMPV6_ECHO_REPLY), icmpEchoBuffer); + icmpReplyBuffer = PhAllocate(icmpReplyLength); + memset(icmpReplyBuffer, 0, icmpReplyLength); + + InterlockedIncrement(&context->PingSentCount); + + // Send ICMPv6 ping... + icmpReplyCount = Icmp6SendEcho2( + icmpHandle, + NULL, + NULL, + NULL, + &icmp6LocalAddr, + &icmp6RemoteAddr, + icmpEchoBuffer->Buffer, + (USHORT)icmpEchoBuffer->Length, + &pingOptions, + icmpReplyBuffer, + icmpReplyLength, + context->MaxPingTimeout + ); + + icmp6ReplyStruct = (PICMPV6_ECHO_REPLY2)icmpReplyBuffer; + + if (icmpReplyCount > 0 && icmp6ReplyStruct->Status == IP_SUCCESS) + { + BOOLEAN icmpPacketSignature = FALSE; + + if (!RtlEqualMemory( + icmp6ReplyStruct->Address.sin6_addr, + context->RemoteEndpoint.Address.In6Addr.u.Word, + sizeof(icmp6ReplyStruct->Address.sin6_addr) + )) + { + InterlockedIncrement(&context->UnknownAddrCount); + } + + icmpPacketSignature = RtlEqualMemory( + icmpEchoBuffer->Buffer, + icmp6ReplyStruct->Data, + icmpEchoBuffer->Length + ); + + if (!icmpPacketSignature) + { + InterlockedIncrement(&context->HashFailCount); + } + } + else + { + InterlockedIncrement(&context->PingLossCount); + } + + icmpCurrentPingMs = icmp6ReplyStruct->RoundTripTime; + } + else + { + IPAddr icmpLocalAddr = 0; + IPAddr icmpRemoteAddr = 0; + BOOLEAN icmpPacketSignature = FALSE; + PICMP_ECHO_REPLY icmpReplyStruct = NULL; + + // TODO: Cache handle. + if ((icmpHandle = IcmpCreateFile()) == INVALID_HANDLE_VALUE) + goto CleanupExit; + + // Set Local IPv4-ANY address. + icmpLocalAddr = in4addr_any.s_addr; + + // Set Remote IPv4 address. + icmpRemoteAddr = context->RemoteEndpoint.Address.InAddr.s_addr; + + // Allocate ICMPv4 message. + icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMP_ECHO_REPLY), icmpEchoBuffer); + icmpReplyBuffer = PhAllocate(icmpReplyLength); + memset(icmpReplyBuffer, 0, icmpReplyLength); + + InterlockedIncrement(&context->PingSentCount); + + // Send ICMPv4 ping... + icmpReplyCount = IcmpSendEcho2Ex( + icmpHandle, + NULL, + NULL, + NULL, + icmpLocalAddr, + icmpRemoteAddr, + NULL, + 0, + &pingOptions, + icmpReplyBuffer, + icmpReplyLength, + context->MaxPingTimeout + ); + + icmpReplyStruct = (PICMP_ECHO_REPLY)icmpReplyBuffer; + + if (icmpReplyCount > 0 && icmpReplyStruct->Status == IP_SUCCESS) + { + if (icmpReplyStruct->Address != context->RemoteEndpoint.Address.InAddr.s_addr) + { + InterlockedIncrement(&context->UnknownAddrCount); + } + + if (icmpReplyStruct->DataSize == icmpEchoBuffer->Length) + { + icmpPacketSignature = RtlEqualMemory( + icmpEchoBuffer->Buffer, + icmpReplyStruct->Data, + icmpReplyStruct->DataSize); + } + + if (!icmpPacketSignature) + { + InterlockedIncrement(&context->HashFailCount); + } + } + else + { + InterlockedIncrement(&context->PingLossCount); + } + + icmpCurrentPingMs = icmpReplyStruct->RoundTripTime; + } + + if (context->PingMinMs == 0 || icmpCurrentPingMs < context->PingMinMs) + context->PingMinMs = icmpCurrentPingMs; + if (icmpCurrentPingMs > context->PingMaxMs) + context->PingMaxMs = icmpCurrentPingMs; + + context->CurrentPingMs = icmpCurrentPingMs; + + InterlockedIncrement(&context->PingRecvCount); + + PhAddItemCircularBuffer_ULONG(&context->PingHistory, icmpCurrentPingMs); + +CleanupExit: + + if (icmpEchoBuffer) + { + PhDereferenceObject(icmpEchoBuffer); + } + + if (icmpHandle != INVALID_HANDLE_VALUE) + { + IcmpCloseHandle(icmpHandle); + } + + if (icmpReplyBuffer) + { + PhFree(icmpReplyBuffer); + } + + PostMessage(context->WindowHandle, WM_PING_UPDATE, 0, 0); + + return STATUS_SUCCESS; +} + +VOID NTAPI NetworkPingUpdateHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PNETWORK_PING_CONTEXT context = (PNETWORK_PING_CONTEXT)Context; + + // Queue up the next ping request + PhQueueItemWorkQueue(&context->PingWorkQueue, NetworkPingThreadStart, (PVOID)context); +} + +VOID NetworkPingUpdateGraph( + _In_ PNETWORK_PING_CONTEXT Context + ) +{ + Context->PingGraphState.Valid = FALSE; + Context->PingGraphState.TooltipIndex = -1; + Graph_MoveGrid(Context->PingGraphHandle, 1); + Graph_Draw(Context->PingGraphHandle); + Graph_UpdateTooltip(Context->PingGraphHandle); + InvalidateRect(Context->PingGraphHandle, NULL, FALSE); +} + +INT_PTR CALLBACK NetworkPingWndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PNETWORK_PING_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PNETWORK_PING_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_LAYOUT_ITEM panelItem; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + // We have already set the group boxes to have WS_EX_TRANSPARENT to fix + // the drawing issue that arises when using WS_CLIPCHILDREN. However + // in removing the flicker from the graphs the group boxes will now flicker. + // It's a good tradeoff since no one stares at the group boxes. + PhSetWindowStyle(hwndDlg, WS_CLIPCHILDREN, WS_CLIPCHILDREN); + + context->WindowHandle = hwndDlg; + context->StatusHandle = GetDlgItem(hwndDlg, IDC_MAINTEXT); + context->MaxPingTimeout = PhGetIntegerSetting(SETTING_NAME_PING_MINIMUM_SCALING); + context->FontHandle = PhCreateCommonFont(-15, FW_MEDIUM, context->StatusHandle); + context->PingGraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 3, + 3, + hwndDlg, + NULL, + NULL, + NULL + ); + Graph_SetTooltip(context->PingGraphHandle, TRUE); + + PhInitializeWorkQueue(&context->PingWorkQueue, 0, 20, 5000); + PhInitializeGraphState(&context->PingGraphState); + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhInitializeCircularBuffer_ULONG(&context->PingHistory, PhGetIntegerSetting(L"SampleCount")); + + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_PANEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT| PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_AVG), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_MIN), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ICMP_MAX), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PINGS_SENT), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PINGS_LOST), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_BAD_HASH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_ANON_ADDR), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); + panelItem = PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_PING_LAYOUT), NULL, PH_ANCHOR_ALL); + PhAddLayoutItemEx(&context->LayoutManager, context->PingGraphHandle, NULL, PH_ANCHOR_ALL, panelItem->Margin); + PhLayoutManagerLayout(&context->LayoutManager); + + if (PhGetIntegerPairSetting(SETTING_NAME_PING_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_PING_WINDOW_POSITION, SETTING_NAME_PING_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + + PhSetWindowText(hwndDlg, PhaFormatString(L"Ping %s", context->IpAddressString)->Buffer); + PhSetWindowText(context->StatusHandle, PhaFormatString(L"Pinging %s with %lu bytes of data...", + context->IpAddressString, + PhGetIntegerSetting(SETTING_NAME_PING_SIZE))->Buffer + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + NetworkPingUpdateHandler, + context, + &context->ProcessesUpdatedRegistration + ); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + } + } + break; + case WM_DESTROY: + { + PhUnregisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + &context->ProcessesUpdatedRegistration + ); + + PhSaveWindowPlacementToSetting( + SETTING_NAME_PING_WINDOW_POSITION, + SETTING_NAME_PING_WINDOW_SIZE, + hwndDlg + ); + + if (context->PingGraphHandle) + DestroyWindow(context->PingGraphHandle); + + if (context->FontHandle) + DeleteFont(context->FontHandle); + + PhDeleteWorkQueue(&context->PingWorkQueue); + PhDeleteGraphState(&context->PingGraphState); + PhDeleteLayoutManager(&context->LayoutManager); + + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); + + PostQuitMessage(0); + } + break; + case WM_SIZE: + PhLayoutManagerLayout(&context->LayoutManager); + break; + case WM_SIZING: + //PhResizingMinimumSize((PRECT)lParam, wParam, 420, 250); + break; + case WM_PING_UPDATE: + { + ULONG maxGraphHeight = 0; + ULONG pingAvgValue = 0; + + NetworkPingUpdateGraph(context); + + for (ULONG i = 0; i < context->PingHistory.Count; i++) + { + maxGraphHeight = maxGraphHeight + PhGetItemCircularBuffer_ULONG(&context->PingHistory, i); + pingAvgValue = maxGraphHeight / context->PingHistory.Count; + } + + PhSetDialogItemText(hwndDlg, IDC_ICMP_AVG, PhaFormatString( + L"Average: %lums", pingAvgValue)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ICMP_MIN, PhaFormatString( + L"Minimum: %lums", context->PingMinMs)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ICMP_MAX, PhaFormatString( + L"Maximum: %lums", context->PingMaxMs)->Buffer); + + PhSetDialogItemText(hwndDlg, IDC_PINGS_SENT, PhaFormatString( + L"Pings sent: %lu", context->PingSentCount)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_PINGS_LOST, PhaFormatString( + L"Pings lost: %lu (%.0f%%)", context->PingLossCount, + ((FLOAT)context->PingLossCount / context->PingSentCount * 100))->Buffer); + + //PhSetDialogItemText(hwndDlg, IDC_BAD_HASH, PhaFormatString( + // L"Bad hashes: %lu", context->HashFailCount)->Buffer); + PhSetDialogItemText(hwndDlg, IDC_ANON_ADDR, PhaFormatString( + L"Anon replies: %lu", context->UnknownAddrCount)->Buffer); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + if (header->hwndFrom == context->PingGraphHandle) + { + if (PhGetIntegerSetting(L"GraphShowText")) + { + HDC hdc = Graph_GetBufferedContext(context->PingGraphHandle); + + PhMoveReference(&context->PingGraphState.Text, + PhFormatString(L"%lu ms", context->CurrentPingMs) + ); + + SelectFont(hdc, PhApplicationFont); + PhSetGraphText(hdc, drawInfo, &context->PingGraphState.Text->sr, + &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT); + } + else + { + drawInfo->Text.Buffer = NULL; + } + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_GRID_Y; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), 0); + PhGraphStateGetDrawInfo(&context->PingGraphState, getDrawInfo, context->PingHistory.Count); + + if (!context->PingGraphState.Valid) + { + ULONG i; + FLOAT max = (FLOAT)context->MaxPingTimeout; // minimum scaling + + for (i = 0; i < drawInfo->LineDataCount; i++) + { + FLOAT data1; + + context->PingGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG(&context->PingHistory, i); + + if (max < data1) + max = data1; + } + + // Scale the data. + PhDivideSinglesBySingle( + context->PingGraphState.Data1, + max, + drawInfo->LineDataCount + ); + + context->PingGraphState.Valid = TRUE; + } + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (header->hwndFrom == context->PingGraphHandle) + { + if (context->PingGraphState.TooltipIndex != getTooltipText->Index) + { + ULONG pingMs = PhGetItemCircularBuffer_ULONG(&context->PingHistory, getTooltipText->Index); + + PhMoveReference(&context->PingGraphState.TooltipText, PhFormatString( + L"%lu ms\n%s", + pingMs, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer) + ); + } + + getTooltipText->Text = context->PingGraphState.TooltipText->sr; + } + } + } + break; + } + } + break; + } + + return FALSE; +} + +NTSTATUS NetworkPingDialogThreadStart( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + HWND windowHandle; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + windowHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_PING), + NULL, + NetworkPingWndProc, + (LPARAM)Parameter + ); + + ShowWindow(windowHandle, SW_SHOW); + SetForegroundWindow(windowHandle); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(windowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + +VOID ShowPingWindow( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PNETWORK_PING_CONTEXT context; + + context = (PNETWORK_PING_CONTEXT)PhAllocate(sizeof(NETWORK_PING_CONTEXT)); + memset(context, 0, sizeof(NETWORK_PING_CONTEXT)); + + if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + RtlIpv4AddressToString(&NetworkItem->RemoteEndpoint.Address.InAddr, context->IpAddressString); + else + RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); + + context->RemoteEndpoint = NetworkItem->RemoteEndpoint; + + PhCreateThread2(NetworkPingDialogThreadStart, (PVOID)context); +} + +VOID ShowPingWindowFromAddress( + _In_ PH_IP_ENDPOINT RemoteEndpoint + ) +{ + PNETWORK_PING_CONTEXT context; + + context = (PNETWORK_PING_CONTEXT)PhAllocate(sizeof(NETWORK_PING_CONTEXT)); + memset(context, 0, sizeof(NETWORK_PING_CONTEXT)); + + if (RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + RtlIpv4AddressToString(&RemoteEndpoint.Address.InAddr, context->IpAddressString); + } + else + { + RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); + } + + context->RemoteEndpoint = RemoteEndpoint; + + PhCreateThread2(NetworkPingDialogThreadStart, (PVOID)context); +} diff --git a/plugins/NetworkTools/resource.h b/plugins/NetworkTools/resource.h index 1747f72aae6f..99edb719b85b 100644 --- a/plugins/NetworkTools/resource.h +++ b/plugins/NetworkTools/resource.h @@ -1,282 +1,286 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by NetworkTools.rc -// -#define AD_PNG 101 -#define AE_PNG 102 -#define AF_PNG 103 -#define AG_PNG 104 -#define AI_PNG 105 -#define AL_PNG 106 -#define AM_PNG 107 -#define AN_PNG 108 -#define AO_PNG 109 -#define AR_PNG 110 -#define AS_PNG 111 -#define AT_PNG 112 -#define AU_PNG 113 -#define AW_PNG 114 -#define AX_PNG 115 -#define AZ_PNG 116 -#define BA_PNG 117 -#define BB_PNG 118 -#define BD_PNG 119 -#define BE_PNG 120 -#define BF_PNG 121 -#define BG_PNG 122 -#define BH_PNG 123 -#define BI__PNG 124 -#define BJ_PNG 125 -#define BM_PNG 126 -#define BN_PNG 127 -#define BO_PNG 128 -#define BR_PNG 129 -#define BS_PNG 130 -#define BT_PNG 131 -#define BV_PNG 132 -#define BW_PNG 133 -#define BY_PNG 134 -#define BZ_PNG 135 -#define CA_PNG 136 -#define AAC_PNG 137 -#define CC_PNG 138 -#define CD_PNG 139 -#define CF_PNG 140 -#define CG_PNG 141 -#define CH_PNG 142 -#define CI_PNG 143 -#define CK_PNG 144 -#define CL_PNG 145 -#define CM_PNG 146 -#define CN_PNG 147 -#define CO_PNG 148 -#define CR_PNG 149 -#define IDD_WHOIS 149 -#define CS_PNG 150 -#define IDD_PING 150 -#define CU_PNG 151 -#define IDD_OPTIONS 151 -#define CV_PNG 152 -#define IDD_TRACERT 152 -#define CX_PNG 153 -#define CY_PNG 154 -#define CZ_PNG 155 -#define DE_PNG 156 -#define DJ_PNG 157 -#define DK_PNG 158 -#define DM_PNG 159 -#define DO_PNG 160 -#define DZ_PNG 161 -#define EC_PNG 162 -#define EE_PNG 163 -#define EG_PNG 164 -#define EH_PNG 165 -#define AAD_PNG 166 -#define ER_PNG 167 -#define ES_PNG 168 -#define ET_PNG 169 -#define AAE_PNG 170 -#define AAF_PNG 171 -#define FI_PNG 172 -#define FJ_PNG 173 -#define FK_PNG 174 -#define FM_PNG 175 -#define FO_PNG 176 -#define FR_PNG 177 -#define GA_PNG 178 -#define GB_PNG 179 -#define GD_PNG 180 -#define GE_PNG 181 -#define GF_PNG 182 -#define GH_PNG 183 -#define GI_PNG 184 -#define GL_PNG 185 -#define GM_PNG 186 -#define GN_PNG 187 -#define GP_PNG 188 -#define GQ_PNG 189 -#define GR_PNG 190 -#define GS_PNG 191 -#define GT_PNG 192 -#define GU_PNG 193 -#define GW_PNG 194 -#define GY_PNG 195 -#define HK_PNG 196 -#define HM_PNG 197 -#define HN_PNG 198 -#define HR_PNG 199 -#define HT_PNG 200 -#define ID_PNG 202 -#define IE_PNG 203 -#define IL_PNG 204 -#define IN_PNG 205 -#define IO_PNG 206 -#define IQ_PNG 207 -#define IR_PNG 208 -#define IS_PNG 209 -#define IT_PNG 210 -#define JM_PNG 211 -#define JO_PNG 212 -#define JP_PNG 213 -#define KE_PNG 214 -#define KG_PNG 215 -#define KH_PNG 216 -#define KI_PNG 217 -#define KM_PNG 218 -#define KN_PNG 219 -#define KP_PNG 220 -#define KR_PNG 221 -#define KW_PNG 222 -#define KY_PNG 223 -#define KZ_PNG 224 -#define LA_PNG 225 -#define LB_PNG 226 -#define LC_PNG 227 -#define LI_PNG 228 -#define LK_PNG 229 -#define LR_PNG 230 -#define LS_PNG 231 -#define LT_PNG 232 -#define LU_PNG 233 -#define LV_PNG 234 -#define LY_PNG 235 -#define MA_PNG 236 -#define MC_PNG 237 -#define MD_PNG 238 -#define ME_PNG 239 -#define MG_PNG 240 -#define MH_PNG 241 -#define MK_PNG 242 -#define ML_PNG 243 -#define MM_PNG 244 -#define MN_PNG 245 -#define MO_PNG 246 -#define MP_PNG 247 -#define MQ_PNG 248 -#define MR_PNG 249 -#define MS_PNG 250 -#define MT_PNG 251 -#define MU_PNG 252 -#define MV_PNG 253 -#define MW_PNG 254 -#define MX_PNG 255 -#define MY_PNG 256 -#define MZ_PNG 257 -#define NA_PNG 258 -#define NC_PNG 259 -#define NE_PNG 260 -#define NF_PNG 261 -#define NG_PNG 262 -#define NI_PNG 263 -#define NL_PNG 264 -#define NO_PNG 265 -#define NP_PNG 266 -#define NR_PNG 267 -#define NU_PNG 268 -#define NZ_PNG 269 -#define OM_PNG 270 -#define PA_PNG 271 -#define PE_PNG 272 -#define PF_PNG 273 -#define PG_PNG 274 -#define PH_PNG 275 -#define PK_PNG 276 -#define PL_PNG 277 -#define PM_PNG 278 -#define PN_PNG 279 -#define PR_PNG 280 -#define PS_PNG 281 -#define PT_PNG 282 -#define PW_PNG 283 -#define PY_PNG 284 -#define QA_PNG 285 -#define RE_PNG 286 -#define RO_PNG 287 -#define RS_PNG 288 -#define RU_PNG 289 -#define RW_PNG 290 -#define SA_PNG 291 -#define SB_PNG 292 -#define SC_PNG 293 -#define AAA_PNG 294 -#define SD_PNG 295 -#define SE_PNG 296 -#define SG_PNG 297 -#define SH_PNG 298 -#define SI_PNG 299 -#define SJ_PNG 300 -#define SK_PNG 301 -#define SL_PNG 302 -#define SM_PNG 303 -#define SN_PNG 304 -#define SO_PNG 305 -#define SR_PNG 306 -#define ST_PNG 307 -#define SV_PNG 308 -#define SY_PNG 309 -#define SZ_PNG 310 -#define TC_PNG 311 -#define TD_PNG 312 -#define TF_PNG 313 -#define TG_PNG 314 -#define TH_PNG 315 -#define TJ_PNG 316 -#define TK_PNG 317 -#define TL_PNG 318 -#define TM_PNG 319 -#define TN_PNG 320 -#define TO_PNG 321 -#define TR_PNG 322 -#define TT_PNG 323 -#define TV_PNG 324 -#define TW_PNG 325 -#define TZ_PNG 326 -#define UA_PNG 327 -#define UG_PNG 328 -#define UM_PNG 329 -#define US_PNG 330 -#define UY_PNG 331 -#define UZ_PNG 332 -#define VA_PNG 333 -#define VC_PNG 334 -#define VE_PNG 335 -#define VG_PNG 336 -#define VI_PNG 337 -#define VN_PNG 338 -#define VU_PNG 339 -#define AAB_PNG 340 -#define WF_PNG 341 -#define WS_PNG 342 -#define YE_PNG 343 -#define YT_PNG 344 -#define ZA_PNG 345 -#define ZM_PNG 346 -#define ZW_PNG 347 -#define HU_PNG 348 -#define IDC_PINGPACKETLENGTH 1008 -#define IDC_NETOUTPUTEDIT 1009 -#define IDC_MAXHOPS 1009 -#define IDC_ICMP_PANEL 1011 -#define IDC_PINGS_LOST 1012 -#define IDC_ICMP_MIN 1013 -#define IDC_ICMP_MAX 1014 -#define IDC_ICMP_AVG 1015 -#define IDC_MAINTEXT 1016 -#define IDC_ANON_ADDR 1017 -#define IDC_PINGS_SENT 1020 -#define IDC_PING_LAYOUT 1021 -#define IDC_BAD_HASH 1022 -#define IDC_STATUS 1023 -#define IDC_GEOIP 1028 -#define IDC_LIST_TRACERT 1030 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 107 -#define _APS_NEXT_COMMAND_VALUE 40006 -#define _APS_NEXT_CONTROL_VALUE 1031 -#define _APS_NEXT_SYMED_VALUE 104 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by NetworkTools.rc +// +#define AD_PNG 101 +#define AE_PNG 102 +#define AF_PNG 103 +#define AG_PNG 104 +#define AI_PNG 105 +#define AL_PNG 106 +#define AM_PNG 107 +#define AN_PNG 108 +#define AO_PNG 109 +#define AR_PNG 110 +#define AS_PNG 111 +#define AT_PNG 112 +#define AU_PNG 113 +#define AW_PNG 114 +#define AX_PNG 115 +#define AZ_PNG 116 +#define BA_PNG 117 +#define BB_PNG 118 +#define BD_PNG 119 +#define BE_PNG 120 +#define BF_PNG 121 +#define BG_PNG 122 +#define BH_PNG 123 +#define BI__PNG 124 +#define BJ_PNG 125 +#define BM_PNG 126 +#define BN_PNG 127 +#define BO_PNG 128 +#define BR_PNG 129 +#define BS_PNG 130 +#define BT_PNG 131 +#define BV_PNG 132 +#define BW_PNG 133 +#define BY_PNG 134 +#define BZ_PNG 135 +#define CA_PNG 136 +#define AAC_PNG 137 +#define CC_PNG 138 +#define CD_PNG 139 +#define CF_PNG 140 +#define CG_PNG 141 +#define CH_PNG 142 +#define CI_PNG 143 +#define CK_PNG 144 +#define CL_PNG 145 +#define CM_PNG 146 +#define CN_PNG 147 +#define CO_PNG 148 +#define CR_PNG 149 +#define IDD_WHOIS 149 +#define CS_PNG 150 +#define IDD_PING 150 +#define CU_PNG 151 +#define IDD_OPTIONS 151 +#define CV_PNG 152 +#define IDD_TRACERT 152 +#define CX_PNG 153 +#define CY_PNG 154 +#define CZ_PNG 155 +#define DE_PNG 156 +#define DJ_PNG 157 +#define DK_PNG 158 +#define DM_PNG 159 +#define DO_PNG 160 +#define DZ_PNG 161 +#define EC_PNG 162 +#define EE_PNG 163 +#define EG_PNG 164 +#define EH_PNG 165 +#define AAD_PNG 166 +#define ER_PNG 167 +#define ES_PNG 168 +#define ET_PNG 169 +#define AAE_PNG 170 +#define AAF_PNG 171 +#define FI_PNG 172 +#define FJ_PNG 173 +#define FK_PNG 174 +#define FM_PNG 175 +#define FO_PNG 176 +#define FR_PNG 177 +#define GA_PNG 178 +#define GB_PNG 179 +#define GD_PNG 180 +#define GE_PNG 181 +#define GF_PNG 182 +#define GH_PNG 183 +#define GI_PNG 184 +#define GL_PNG 185 +#define GM_PNG 186 +#define GN_PNG 187 +#define GP_PNG 188 +#define GQ_PNG 189 +#define GR_PNG 190 +#define GS_PNG 191 +#define GT_PNG 192 +#define GU_PNG 193 +#define GW_PNG 194 +#define GY_PNG 195 +#define HK_PNG 196 +#define HM_PNG 197 +#define HN_PNG 198 +#define HR_PNG 199 +#define HT_PNG 200 +#define ID_PNG 202 +#define IE_PNG 203 +#define IL_PNG 204 +#define IN_PNG 205 +#define IO_PNG 206 +#define IQ_PNG 207 +#define IR_PNG 208 +#define IS_PNG 209 +#define IT_PNG 210 +#define JM_PNG 211 +#define JO_PNG 212 +#define JP_PNG 213 +#define KE_PNG 214 +#define KG_PNG 215 +#define KH_PNG 216 +#define KI_PNG 217 +#define KM_PNG 218 +#define KN_PNG 219 +#define KP_PNG 220 +#define KR_PNG 221 +#define KW_PNG 222 +#define KY_PNG 223 +#define KZ_PNG 224 +#define LA_PNG 225 +#define LB_PNG 226 +#define LC_PNG 227 +#define LI_PNG 228 +#define LK_PNG 229 +#define LR_PNG 230 +#define LS_PNG 231 +#define LT_PNG 232 +#define LU_PNG 233 +#define LV_PNG 234 +#define LY_PNG 235 +#define MA_PNG 236 +#define MC_PNG 237 +#define MD_PNG 238 +#define ME_PNG 239 +#define MG_PNG 240 +#define MH_PNG 241 +#define MK_PNG 242 +#define ML_PNG 243 +#define MM_PNG 244 +#define MN_PNG 245 +#define MO_PNG 246 +#define MP_PNG 247 +#define MQ_PNG 248 +#define MR_PNG 249 +#define MS_PNG 250 +#define MT_PNG 251 +#define MU_PNG 252 +#define MV_PNG 253 +#define MW_PNG 254 +#define MX_PNG 255 +#define MY_PNG 256 +#define MZ_PNG 257 +#define NA_PNG 258 +#define NC_PNG 259 +#define NE_PNG 260 +#define NF_PNG 261 +#define NG_PNG 262 +#define NI_PNG 263 +#define NL_PNG 264 +#define NO_PNG 265 +#define NP_PNG 266 +#define NR_PNG 267 +#define NU_PNG 268 +#define NZ_PNG 269 +#define OM_PNG 270 +#define PA_PNG 271 +#define PE_PNG 272 +#define PF_PNG 273 +#define PG_PNG 274 +#define PH_PNG 275 +#define PK_PNG 276 +#define PL_PNG 277 +#define PM_PNG 278 +#define PN_PNG 279 +#define PR_PNG 280 +#define PS_PNG 281 +#define PT_PNG 282 +#define PW_PNG 283 +#define PY_PNG 284 +#define QA_PNG 285 +#define RE_PNG 286 +#define RO_PNG 287 +#define RS_PNG 288 +#define RU_PNG 289 +#define RW_PNG 290 +#define SA_PNG 291 +#define SB_PNG 292 +#define SC_PNG 293 +#define AAA_PNG 294 +#define SD_PNG 295 +#define SE_PNG 296 +#define SG_PNG 297 +#define SH_PNG 298 +#define SI_PNG 299 +#define SJ_PNG 300 +#define SK_PNG 301 +#define SL_PNG 302 +#define SM_PNG 303 +#define SN_PNG 304 +#define SO_PNG 305 +#define SR_PNG 306 +#define ST_PNG 307 +#define SV_PNG 308 +#define SY_PNG 309 +#define SZ_PNG 310 +#define TC_PNG 311 +#define TD_PNG 312 +#define TF_PNG 313 +#define TG_PNG 314 +#define TH_PNG 315 +#define TJ_PNG 316 +#define TK_PNG 317 +#define TL_PNG 318 +#define TM_PNG 319 +#define TN_PNG 320 +#define TO_PNG 321 +#define TR_PNG 322 +#define TT_PNG 323 +#define TV_PNG 324 +#define TW_PNG 325 +#define TZ_PNG 326 +#define UA_PNG 327 +#define UG_PNG 328 +#define UM_PNG 329 +#define US_PNG 330 +#define UY_PNG 331 +#define UZ_PNG 332 +#define VA_PNG 333 +#define VC_PNG 334 +#define VE_PNG 335 +#define VG_PNG 336 +#define VI_PNG 337 +#define VN_PNG 338 +#define VU_PNG 339 +#define AAB_PNG 340 +#define WF_PNG 341 +#define WS_PNG 342 +#define YE_PNG 343 +#define YT_PNG 344 +#define ZA_PNG 345 +#define ZM_PNG 346 +#define ZW_PNG 347 +#define HU_PNG 348 +#define IDC_DATABASE 1001 +#define IDC_BROWSE 1002 +#define IDC_PINGPACKETLENGTH 1008 +#define IDC_NETOUTPUTEDIT 1009 +#define IDC_MAXHOPS 1009 +#define IDC_ICMP_PANEL 1011 +#define IDC_PINGS_LOST 1012 +#define IDC_ICMP_MIN 1013 +#define IDC_ICMP_MAX 1014 +#define IDC_ICMP_AVG 1015 +#define IDC_MAINTEXT 1016 +#define IDC_ANON_ADDR 1017 +#define IDC_PINGS_SENT 1020 +#define IDC_PING_LAYOUT 1021 +#define IDC_BAD_HASH 1022 +#define IDC_STATUS 1023 +#define IDC_GEOIP 1028 +#define IDC_LIST_TRACERT 1030 +#define IDC_REFRESH 1031 +#define IDC_ENABLE_EXTENDED_TCP 1032 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40006 +#define _APS_NEXT_CONTROL_VALUE 1033 +#define _APS_NEXT_SYMED_VALUE 104 +#endif +#endif diff --git a/plugins/NetworkTools/tracert.c b/plugins/NetworkTools/tracert.c index 967ede48a701..42cc17b3f75d 100644 --- a/plugins/NetworkTools/tracert.c +++ b/plugins/NetworkTools/tracert.c @@ -1,768 +1,930 @@ -/* - * Process Hacker Network Tools - - * Tracert dialog - * - * Copyright (C) 2015-2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "nettools.h" -#include "tracert.h" -#include -#include - -PPH_STRING TracertGetErrorMessage( - _In_ IP_STATUS Result - ) -{ - PPH_STRING message; - ULONG messageLength; - - messageLength = 128; - message = PhCreateStringEx(NULL, 128 * sizeof(WCHAR)); - - if (GetIpErrorString(Result, message->Buffer, &messageLength) == ERROR_INSUFFICIENT_BUFFER) - { - PhDereferenceObject(message); - message = PhCreateStringEx(NULL, messageLength * sizeof(WCHAR)); - - if (GetIpErrorString(Result, message->Buffer, &messageLength) != ERROR_SUCCESS) - { - PhDereferenceObject(message); - message = PhGetWin32Message(Result); - } - } - else - { - message = PhGetWin32Message(Result); - } - - return message; -} - -NTSTATUS TracertHostnameLookupCallback( - _In_ PVOID Parameter - ) -{ - PTRACERT_RESOLVE_WORKITEM resolve = Parameter; - - if (resolve->Type == PH_IPV4_NETWORK_TYPE) - { - if (!GetNameInfo( - (PSOCKADDR)&resolve->SocketAddress, - sizeof(SOCKADDR_IN), - resolve->SocketAddressHostname, - sizeof(resolve->SocketAddressHostname), - NULL, - 0, - NI_NAMEREQD - )) - { - PhMoveReference(&resolve->Node->HostnameString, PhCreateString(resolve->SocketAddressHostname)); - } - else - { - ULONG errorCode = WSAGetLastError(); - - if (errorCode != WSAHOST_NOT_FOUND && errorCode != WSATRY_AGAIN) - { - //PPH_STRING errorMessage = PhGetWin32Message(errorCode); - //PhSetListViewSubItem(work->LvHandle, work->LvItemIndex, HOSTNAME_COLUMN, errorMessage->Buffer); - //PhDereferenceObject(errorMessage); - } - - PhDereferenceObject(resolve); - } - } - else if (resolve->Type == PH_IPV6_NETWORK_TYPE) - { - if (!GetNameInfo( - (PSOCKADDR)&resolve->SocketAddress, - sizeof(SOCKADDR_IN6), - resolve->SocketAddressHostname, - sizeof(resolve->SocketAddressHostname), - NULL, - 0, - NI_NAMEREQD - )) - { - PhMoveReference(&resolve->Node->HostnameString, PhCreateString(resolve->SocketAddressHostname)); - } - else - { - ULONG errorCode = WSAGetLastError(); - - if (errorCode != WSAHOST_NOT_FOUND && errorCode != WSATRY_AGAIN) - { - //PPH_STRING errorMessage = PhGetWin32Message(errorCode); - //PhSetListViewSubItem(work->LvHandle, work->LvItemIndex, HOSTNAME_COLUMN, errorMessage->Buffer); - //PhDereferenceObject(errorMessage); - } - - PhDereferenceObject(resolve); - } - } - - return STATUS_SUCCESS; -} - -VOID TracertQueueHostLookup( - _In_ PNETWORK_TRACERT_CONTEXT Context, - _In_ PTRACERT_ROOT_NODE Node, - _In_ PVOID SocketAddress - ) -{ - PPH_STRING remoteCountryCode; - PPH_STRING remoteCountryName; - ULONG addressStringLength = INET_ADDRSTRLEN; - WCHAR addressString[INET_ADDRSTRLEN] = L""; - - if (Context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - IN_ADDR sockAddrIn; - PTRACERT_RESOLVE_WORKITEM resolve; - - memset(&sockAddrIn, 0, sizeof(IN_ADDR)); - memcpy(&sockAddrIn, SocketAddress, sizeof(IN_ADDR)); - - if (NT_SUCCESS(RtlIpv4AddressToStringEx(&sockAddrIn, 0, addressString, &addressStringLength))) - { - Node->IpAddressString = PhCreateString(addressString); - } - - resolve = PhCreateAlloc(sizeof(TRACERT_RESOLVE_WORKITEM)); - memset(resolve, 0, sizeof(TRACERT_RESOLVE_WORKITEM)); - - resolve->Type = PH_IPV4_NETWORK_TYPE; - resolve->WindowHandle = Context->WindowHandle; - resolve->Node = Node; - - ((PSOCKADDR_IN)&resolve->SocketAddress)->sin_family = AF_INET; - ((PSOCKADDR_IN)&resolve->SocketAddress)->sin_addr = sockAddrIn; - - PhQueueItemWorkQueue(&Context->WorkQueue, TracertHostnameLookupCallback, resolve); - - if (LookupSockInAddr4CountryCode( - sockAddrIn, - &remoteCountryCode, - &remoteCountryName - )) - { - PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); - PhMoveReference(&Node->RemoteCountryName, remoteCountryName); - } - } - else if (Context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - IN6_ADDR sockAddrIn6; - PTRACERT_RESOLVE_WORKITEM resolve; - - memset(&sockAddrIn6, 0, sizeof(IN6_ADDR)); - memcpy(&sockAddrIn6, SocketAddress, sizeof(IN6_ADDR)); - - if (NT_SUCCESS(RtlIpv6AddressToStringEx(&sockAddrIn6, 0, 0, addressString, &addressStringLength))) - { - Node->IpAddressString = PhCreateString(addressString); - } - - resolve = PhCreateAlloc(sizeof(TRACERT_RESOLVE_WORKITEM)); - memset(resolve, 0, sizeof(TRACERT_RESOLVE_WORKITEM)); - - resolve->Type = PH_IPV6_NETWORK_TYPE; - resolve->WindowHandle = Context->WindowHandle; - resolve->Node = Node; - - ((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_family = AF_INET6; - ((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_addr = sockAddrIn6; - - PhQueueItemWorkQueue(&Context->WorkQueue, TracertHostnameLookupCallback, resolve); - - if (LookupSockInAddr6CountryCode( - sockAddrIn6, - &remoteCountryCode, - &remoteCountryName - )) - { - PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); - PhMoveReference(&Node->RemoteCountryName, remoteCountryName); - } - } -} - -NTSTATUS NetworkTracertThreadStart( - _In_ PVOID Parameter - ) -{ - PNETWORK_TRACERT_CONTEXT context = (PNETWORK_TRACERT_CONTEXT)Parameter; - HANDLE icmpHandle = INVALID_HANDLE_VALUE; - SOCKADDR_STORAGE sourceAddress = { 0 }; - SOCKADDR_STORAGE destinationAddress = { 0 }; - ULONG icmpReplyLength = 0; - PVOID icmpReplyBuffer = NULL; - PPH_BYTES icmpEchoBuffer = NULL; - PPH_STRING icmpRandString = NULL; - IP_OPTION_INFORMATION pingOptions = - { - 1, - 0, - IP_FLAG_DF, - 0 - }; - - if (icmpRandString = PhCreateStringEx(NULL, PhGetIntegerSetting(SETTING_NAME_PING_SIZE) * 2 + 2)) - { - PhGenerateRandomAlphaString(icmpRandString->Buffer, (ULONG)icmpRandString->Length / sizeof(WCHAR)); - - icmpEchoBuffer = PhConvertUtf16ToMultiByte(icmpRandString->Buffer); - PhDereferenceObject(icmpRandString); - } - - switch (context->RemoteEndpoint.Address.Type) - { - case PH_IPV4_NETWORK_TYPE: - icmpHandle = IcmpCreateFile(); - break; - case PH_IPV6_NETWORK_TYPE: - icmpHandle = Icmp6CreateFile(); - break; - } - - if (icmpHandle == INVALID_HANDLE_VALUE) - goto CleanupExit; - - if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - ((PSOCKADDR_IN)&destinationAddress)->sin_family = AF_INET; - ((PSOCKADDR_IN)&destinationAddress)->sin_addr = context->RemoteEndpoint.Address.InAddr; - //((PSOCKADDR_IN)&destinationAddress)->sin_port = (USHORT)context->RemoteEndpoint.Port; - } - else if (context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - ((PSOCKADDR_IN6)&destinationAddress)->sin6_family = AF_INET6; - ((PSOCKADDR_IN6)&destinationAddress)->sin6_addr = context->RemoteEndpoint.Address.In6Addr; - //((PSOCKADDR_IN6)&destinationAddress)->sin6_port = (USHORT)context->RemoteEndpoint.Port; - } - - for (ULONG i = 0; i < DEFAULT_MAXIMUM_HOPS; i++) - { - IN_ADDR last4ReplyAddress = in4addr_any; - IN6_ADDR last6ReplyAddress = in6addr_any; - - if (context->Cancel) - break; - - PTRACERT_ROOT_NODE node = AddTracertNode(context, pingOptions.Ttl); - - for (ULONG ii = 0; ii < DEFAULT_MAXIMUM_PINGS; ii++) - { - if (context->Cancel) - break; - - if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMP_ECHO_REPLY), icmpEchoBuffer); - icmpReplyBuffer = PhAllocate(icmpReplyLength); - memset(icmpReplyBuffer, 0, icmpReplyLength); - - if (IcmpSendEcho2Ex( - icmpHandle, - 0, - NULL, - NULL, - ((PSOCKADDR_IN)&sourceAddress)->sin_addr.s_addr, - ((PSOCKADDR_IN)&destinationAddress)->sin_addr.s_addr, - icmpEchoBuffer->Buffer, - (USHORT)icmpEchoBuffer->Length, - &pingOptions, - icmpReplyBuffer, - icmpReplyLength, - DEFAULT_TIMEOUT - )) - { - PICMP_ECHO_REPLY reply4 = (PICMP_ECHO_REPLY)icmpReplyBuffer; - - memcpy(&last4ReplyAddress, &reply4->Address, sizeof(IN_ADDR)); - - TracertQueueHostLookup( - context, - node, - &reply4->Address - ); - - node->PingStatus[ii] = reply4->Status; - node->PingList[ii] = reply4->RoundTripTime; - UpdateTracertNode(context, node); - - if (reply4->Status == IP_HOP_LIMIT_EXCEEDED && reply4->RoundTripTime < MIN_INTERVAL) - { - //LARGE_INTEGER interval; - //NtDelayExecution(FALSE, PhTimeoutFromMilliseconds(&interval, MIN_INTERVAL - reply4->RoundTripTime)); - } - - //if (reply4->Status != IP_REQ_TIMED_OUT) - //{ - // PPH_STRING errorMessage; - // - // if (errorMessage = TracertGetErrorMessage(reply4->Status)) - // { - // node->IpAddressString = errorMessage; - // } - //} - } - else - { - node->PingStatus[ii] = IP_REQ_TIMED_OUT; - UpdateTracertNode(context, node); - } - - PhFree(icmpReplyBuffer); - } - else - { - icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMPV6_ECHO_REPLY), icmpEchoBuffer); - icmpReplyBuffer = PhAllocate(icmpReplyLength); - memset(icmpReplyBuffer, 0, icmpReplyLength); - - if (Icmp6SendEcho2( - icmpHandle, - 0, - NULL, - NULL, - ((PSOCKADDR_IN6)&sourceAddress), - ((PSOCKADDR_IN6)&destinationAddress), - icmpEchoBuffer->Buffer, - (USHORT)icmpEchoBuffer->Length, - &pingOptions, - icmpReplyBuffer, - icmpReplyLength, - DEFAULT_TIMEOUT - )) - { - PICMPV6_ECHO_REPLY reply6 = (PICMPV6_ECHO_REPLY)icmpReplyBuffer; - - memcpy(&last6ReplyAddress, &reply6->Address.sin6_addr, sizeof(IN6_ADDR)); - - TracertQueueHostLookup( - context, - node, - &reply6->Address.sin6_addr - ); - - node->PingStatus[ii] = reply6->Status; - node->PingList[ii] = reply6->RoundTripTime; - UpdateTracertNode(context, node); - - if (reply6->Status == IP_HOP_LIMIT_EXCEEDED) - { - if (reply6->RoundTripTime < MIN_INTERVAL) - { - //LARGE_INTEGER interval; - //NtDelayExecution(FALSE, PhTimeoutFromMilliseconds(&interval, MIN_INTERVAL - reply6->RoundTripTime)); - } - } - } - else - { - node->PingStatus[ii] = IP_REQ_TIMED_OUT; - UpdateTracertNode(context, node); - } - - PhFree(icmpReplyBuffer); - } - } - - if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - if (!memcmp(&last4ReplyAddress, &((PSOCKADDR_IN)&destinationAddress)->sin_addr, sizeof(IN_ADDR))) - break; - } - else if (context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - if (!memcmp(&last6ReplyAddress, &((PSOCKADDR_IN6)&destinationAddress)->sin6_addr, sizeof(IN6_ADDR))) - break; - } - - pingOptions.Ttl++; - } - -CleanupExit: - - if (icmpHandle != INVALID_HANDLE_VALUE) - { - IcmpCloseHandle(icmpHandle); - } - - PhDereferenceObject(context); - - PostMessage(context->WindowHandle, NTM_RECEIVEDFINISH, 0, 0); - return STATUS_SUCCESS; -} - -VOID ShowMenu( - _In_ PNETWORK_TRACERT_CONTEXT Context, - _In_ ULONG Id - ) -{ - switch (Id) - { - case MAINMENU_ACTION_PING: - { - PH_IP_ENDPOINT RemoteEndpoint; - PWSTR terminator = NULL; - PTRACERT_ROOT_NODE node; - - if (node = GetSelectedTracertNode(Context)) - { - if (NT_SUCCESS(RtlIpv4StringToAddress( - node->IpAddressString->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - ShowPingWindowFromAddress(RemoteEndpoint); - break; - } - - if (NT_SUCCESS(RtlIpv6StringToAddress( - node->IpAddressString->Buffer, - &terminator, - &RemoteEndpoint.Address.In6Addr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowPingWindowFromAddress(RemoteEndpoint); - break; - } - } - } - break; - case NETWORK_ACTION_TRACEROUTE: - { - PH_IP_ENDPOINT RemoteEndpoint; - PWSTR terminator = NULL; - PTRACERT_ROOT_NODE node; - - if (node = GetSelectedTracertNode(Context)) - { - if (NT_SUCCESS(RtlIpv4StringToAddress( - node->IpAddressString->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - ShowTracertWindowFromAddress(RemoteEndpoint); - break; - } - - if (NT_SUCCESS(RtlIpv6StringToAddress( - node->IpAddressString->Buffer, - &terminator, - &RemoteEndpoint.Address.In6Addr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowTracertWindowFromAddress(RemoteEndpoint); - break; - } - } - } - break; - case NETWORK_ACTION_WHOIS: - { - PH_IP_ENDPOINT RemoteEndpoint; - PWSTR terminator = NULL; - PTRACERT_ROOT_NODE node; - - if (node = GetSelectedTracertNode(Context)) - { - if (NT_SUCCESS(RtlIpv4StringToAddress( - node->IpAddressString->Buffer, - TRUE, - &terminator, - &RemoteEndpoint.Address.InAddr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; - ShowWhoisWindowFromAddress(RemoteEndpoint); - break; - } - - if (NT_SUCCESS(RtlIpv6StringToAddress( - node->IpAddressString->Buffer, - &terminator, - &RemoteEndpoint.Address.In6Addr - ))) - { - RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; - ShowWhoisWindowFromAddress(RemoteEndpoint); - break; - } - } - } - break; - } -} - -INT_PTR CALLBACK TracertDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PNETWORK_TRACERT_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PNETWORK_TRACERT_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PNETWORK_TRACERT_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - { - context->Cancel = TRUE; - - PhSaveWindowPlacementToSetting(SETTING_NAME_TRACERT_WINDOW_POSITION, SETTING_NAME_TRACERT_WINDOW_SIZE, hwndDlg); - - if (context->FontHandle) - DeleteObject(context->FontHandle); - - DeleteTracertTree(context); - - PhDeleteWorkQueue(&context->WorkQueue); - PhDeleteLayoutManager(&context->LayoutManager); - RemoveProp(hwndDlg, L"Context"); - PhDereferenceObject(context); - - PostQuitMessage(0); - } - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HANDLE tracertThread; - - PhCenterWindow(hwndDlg, PhMainWndHandle); - - Static_SetText(hwndDlg, - PhaFormatString(L"Tracing %s...", context->IpAddressString)->Buffer - ); - Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), - PhaFormatString(L"Tracing route to %s with %lu bytes of data...", context->IpAddressString, PhGetIntegerSetting(SETTING_NAME_PING_SIZE))->Buffer - ); - - context->WindowHandle = hwndDlg; - context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST_TRACERT); - context->FontHandle = CommonCreateFont(-15, FW_MEDIUM, GetDlgItem(hwndDlg, IDC_STATUS)); - - InitializeTracertTree(context); - - PhInitializeWorkQueue(&context->WorkQueue, 0, 40, 5000); - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_STATUS), NULL, PH_ANCHOR_TOP | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); - PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); - PhLoadWindowPlacementFromSetting(SETTING_NAME_TRACERT_WINDOW_POSITION, SETTING_NAME_TRACERT_WINDOW_SIZE, hwndDlg); - - PhReferenceObject(context); - - if (tracertThread = PhCreateThread(0, NetworkTracertThreadStart, (PVOID)context)) - NtClose(tracertThread); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - case TRACERT_SHOWCONTEXTMENU: - { - PPH_EMENU menu; - PTRACERT_ROOT_NODE selectedNode; - PPH_EMENU_ITEM selectedItem; - PPH_TREENEW_MOUSE_EVENT mouseEvent = (PPH_TREENEW_MOUSE_EVENT)lParam; - - if (selectedNode = GetSelectedTracertNode(context)) - { - menu = PhCreateEMenu(); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MAINMENU_ACTION_PING, L"Ping", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, NETWORK_ACTION_TRACEROUTE, L"Traceroute", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, NETWORK_ACTION_WHOIS, L"Whois", NULL, NULL), -1); - - selectedItem = PhShowEMenu( - menu, - hwndDlg, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - mouseEvent->Location.x, - mouseEvent->Location.y - ); - - if (selectedItem && selectedItem->Id != -1) - { - ShowMenu(context, selectedItem->Id); - } - - PhDestroyEMenu(menu); - } - } - break; - } - } - break; - case WM_SIZE: - PhLayoutManagerLayout(&context->LayoutManager); - break; - case NTM_RECEIVEDFINISH: - { - PPH_STRING windowText; - - if (windowText = PH_AUTO(PhGetWindowText(context->WindowHandle))) - { - Static_SetText( - context->WindowHandle, - PhaFormatString(L"%s complete", windowText->Buffer)->Buffer - ); - } - - if (windowText = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_STATUS)))) - { - Static_SetText( - GetDlgItem(hwndDlg, IDC_STATUS), - PhaFormatString(L"%s complete", windowText->Buffer)->Buffer - ); - } - - TreeNew_NodesStructured(context->TreeNewHandle); - } - break; - } - - return FALSE; -} - -NTSTATUS TracertDialogThreadStart( - _In_ PVOID Parameter - ) -{ - BOOL result; - MSG message; - HWND windowHandle; - PH_AUTO_POOL autoPool; - PNETWORK_TRACERT_CONTEXT context = (PNETWORK_TRACERT_CONTEXT)Parameter; - - PhInitializeAutoPool(&autoPool); - - windowHandle = CreateDialogParam( - (HINSTANCE)PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_TRACERT), - NULL, - TracertDlgProc, - (LPARAM)Parameter - ); - - ShowWindow(windowHandle, SW_SHOW); - SetForegroundWindow(windowHandle); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (!IsDialogMessage(context->WindowHandle, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -VOID ShowTracertWindow( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - HANDLE dialogThread; - PNETWORK_TRACERT_CONTEXT context; - - context = (PNETWORK_TRACERT_CONTEXT)PhCreateAlloc(sizeof(NETWORK_TRACERT_CONTEXT)); - memset(context, 0, sizeof(NETWORK_TRACERT_CONTEXT)); - - context->RemoteEndpoint = NetworkItem->RemoteEndpoint; - - if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - RtlIpv4AddressToString(&NetworkItem->RemoteEndpoint.Address.InAddr, context->IpAddressString); - } - else if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); - } - - if (dialogThread = PhCreateThread(0, TracertDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } -} - -VOID ShowTracertWindowFromAddress( - _In_ PH_IP_ENDPOINT RemoteEndpoint - ) -{ - HANDLE dialogThread; - PNETWORK_TRACERT_CONTEXT context; - - context = (PNETWORK_TRACERT_CONTEXT)PhCreateAlloc(sizeof(NETWORK_TRACERT_CONTEXT)); - memset(context, 0, sizeof(NETWORK_TRACERT_CONTEXT)); - - context->RemoteEndpoint = RemoteEndpoint; - - if (RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - RtlIpv4AddressToString(&RemoteEndpoint.Address.InAddr, context->IpAddressString); - } - else if (RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); - } - - if (dialogThread = PhCreateThread(0, TracertDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } -} \ No newline at end of file +/* + * Process Hacker Network Tools - + * Tracert dialog + * + * Copyright (C) 2015-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "nettools.h" +#include "tracert.h" +#include +#include + +PPH_STRING TracertGetErrorMessage( + _In_ IP_STATUS Result + ) +{ + PPH_STRING message; + ULONG messageLength; + + messageLength = 128; + message = PhCreateStringEx(NULL, 128 * sizeof(WCHAR)); + + if (GetIpErrorString(Result, message->Buffer, &messageLength) == ERROR_INSUFFICIENT_BUFFER) + { + PhDereferenceObject(message); + message = PhCreateStringEx(NULL, messageLength * sizeof(WCHAR)); + + if (GetIpErrorString(Result, message->Buffer, &messageLength) != ERROR_SUCCESS) + { + PhDereferenceObject(message); + message = PhGetWin32Message(Result); + } + } + else + { + message = PhGetWin32Message(Result); + } + + return message; +} + +PPH_STRING PhpGetIp4ReverseNameFromAddress( + _In_ IN_ADDR Address + ) +{ + return PhFormatString( + L"%hhu.%hhu.%hhu.%hhu.%s", + Address.s_impno, + Address.s_lh, + Address.s_host, + Address.s_net, + DNS_IP4_REVERSE_DOMAIN_STRING_W + ); +} + +PPH_STRING PhpGetIp6ReverseNameFromAddress( + _In_ IN6_ADDR Address + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, DNS_MAX_NAME_BUFFER_LENGTH); + + for (INT i = sizeof(IN6_ADDR) - 1; i >= 0; i--) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%hhx.%hhx.", + Address.s6_addr[i] & 0xF, + (Address.s6_addr[i] >> 4) & 0xF + ); + } + + PhAppendStringBuilder2(&stringBuilder, DNS_IP6_REVERSE_DOMAIN_STRING_W); + + return PhFinalStringBuilderString(&stringBuilder); +} + +NTSTATUS TracertHostnameLookupCallback( + _In_ PVOID Parameter + ) +{ + PTRACERT_RESOLVE_WORKITEM resolve = Parameter; + DNS_STATUS status; + PPH_STRING addressHostName = NULL; + PPH_STRING addressReverse = NULL; + PDNS_RECORD dnsQueryResults = NULL; + + if (resolve->Type == PH_IPV4_NETWORK_TYPE) + { + addressReverse = PhpGetIp4ReverseNameFromAddress(((PSOCKADDR_IN)&resolve->SocketAddress)->sin_addr); + } + else if (resolve->Type == PH_IPV6_NETWORK_TYPE) + { + addressReverse = PhpGetIp6ReverseNameFromAddress(((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_addr); + } + else + { + return STATUS_INVALID_PARAMETER; + } + + status = DnsQuery( + addressReverse->Buffer, + DNS_TYPE_PTR, + DNS_QUERY_BYPASS_CACHE | DNS_QUERY_NO_HOSTS_FILE, + NULL, + &dnsQueryResults, + NULL + ); + + if (dnsQueryResults) + { + addressHostName = PhCreateString(dnsQueryResults->Data.PTR.pNameHost); // Return the first result (dmex) + DnsRecordListFree(dnsQueryResults, DnsFreeRecordList); + } + + if (addressHostName) + { + SendMessage( + resolve->WindowHandle, + WM_TRACERT_HOSTNAME, + resolve->Index, + (LPARAM)addressHostName + ); + } + else + { + if (status != DNS_ERROR_RCODE_NAME_ERROR) + { + SendMessage( + resolve->WindowHandle, + WM_TRACERT_HOSTNAME, + resolve->Index, + (LPARAM)PhGetWin32Message(status) + ); + } + + PhDereferenceObject(resolve); + } + + PhDereferenceObject(addressReverse); + + return STATUS_SUCCESS; +} + +VOID TracertQueueHostLookup( + _In_ PNETWORK_TRACERT_CONTEXT Context, + _In_ PTRACERT_ROOT_NODE Node, + _In_ PVOID SocketAddress + ) +{ + PPH_STRING remoteCountryCode; + PPH_STRING remoteCountryName; + ULONG addressStringLength = INET6_ADDRSTRLEN; + WCHAR addressString[INET6_ADDRSTRLEN] = L""; + + if (Context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + IN_ADDR sockAddrIn; + PTRACERT_RESOLVE_WORKITEM resolve; + + memset(&sockAddrIn, 0, sizeof(IN_ADDR)); + memcpy(&sockAddrIn, SocketAddress, sizeof(IN_ADDR)); + + if (NT_SUCCESS(RtlIpv4AddressToStringEx(&sockAddrIn, 0, addressString, &addressStringLength))) + { + if (!PhIsNullOrEmptyString(Node->IpAddressString)) + { + // Make sure we don't append the same address. + if (PhFindStringInString(Node->IpAddressString, 0, addressString) == -1) + { + // Some routes can return multiple addresses for the same ping or 'hop', + // so make sure we don't lose this information (as every other tracert tool does) + // and instead append the additional IP address to the node. + PhMoveReference(&Node->IpAddressString, + PhFormatString(L"%s, %s", PhGetString(Node->IpAddressString), addressString) + ); + } + } + else + { + PhMoveReference(&Node->IpAddressString, PhCreateString(addressString)); + } + } + + resolve = PhCreateAlloc(sizeof(TRACERT_RESOLVE_WORKITEM)); + memset(resolve, 0, sizeof(TRACERT_RESOLVE_WORKITEM)); + + resolve->Type = PH_IPV4_NETWORK_TYPE; + resolve->WindowHandle = Context->WindowHandle; + resolve->Index = Node->TTL; + + ((PSOCKADDR_IN)&resolve->SocketAddress)->sin_family = AF_INET; + ((PSOCKADDR_IN)&resolve->SocketAddress)->sin_addr = sockAddrIn; + + PhQueueItemWorkQueue(&Context->WorkQueue, TracertHostnameLookupCallback, resolve); + + if (LookupSockInAddr4CountryCode( + sockAddrIn, + &remoteCountryCode, + &remoteCountryName + )) + { + PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); + PhMoveReference(&Node->RemoteCountryName, remoteCountryName); + } + } + else if (Context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + IN6_ADDR sockAddrIn6; + PTRACERT_RESOLVE_WORKITEM resolve; + + memset(&sockAddrIn6, 0, sizeof(IN6_ADDR)); + memcpy(&sockAddrIn6, SocketAddress, sizeof(IN6_ADDR)); + + if (NT_SUCCESS(RtlIpv6AddressToStringEx(&sockAddrIn6, 0, 0, addressString, &addressStringLength))) + { + if (!PhIsNullOrEmptyString(Node->IpAddressString)) + { + // Make sure we don't append the same address. + if (PhFindStringInString(Node->IpAddressString, 0, addressString) == -1) + { + // Some routes can return multiple addresses for the same TTL, + // so make sure we don't lose this information (as every other tracert tool does) + // and instead append the additional IP address to the node. + PhMoveReference(&Node->IpAddressString, + PhFormatString(L"%s, %s", PhGetString(Node->IpAddressString), addressString) + ); + } + } + else + { + PhMoveReference(&Node->IpAddressString, PhCreateString(addressString)); + } + } + + resolve = PhCreateAlloc(sizeof(TRACERT_RESOLVE_WORKITEM)); + memset(resolve, 0, sizeof(TRACERT_RESOLVE_WORKITEM)); + + resolve->Type = PH_IPV6_NETWORK_TYPE; + resolve->WindowHandle = Context->WindowHandle; + resolve->Index = Node->TTL; + + ((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_family = AF_INET6; + ((PSOCKADDR_IN6)&resolve->SocketAddress)->sin6_addr = sockAddrIn6; + + PhQueueItemWorkQueue(&Context->WorkQueue, TracertHostnameLookupCallback, resolve); + + if (LookupSockInAddr6CountryCode( + sockAddrIn6, + &remoteCountryCode, + &remoteCountryName + )) + { + PhMoveReference(&Node->RemoteCountryCode, remoteCountryCode); + PhMoveReference(&Node->RemoteCountryName, remoteCountryName); + } + } +} + +NTSTATUS NetworkTracertThreadStart( + _In_ PVOID Parameter + ) +{ + PNETWORK_TRACERT_CONTEXT context = (PNETWORK_TRACERT_CONTEXT)Parameter; + HANDLE icmpHandle = INVALID_HANDLE_VALUE; + SOCKADDR_STORAGE sourceAddress = { 0 }; + SOCKADDR_STORAGE destinationAddress = { 0 }; + ULONG icmpReplyCount = 0; + ULONG icmpReplyLength = 0; + PVOID icmpReplyBuffer = NULL; + PPH_BYTES icmpEchoBuffer = NULL; + PPH_STRING icmpRandString = NULL; + IP_OPTION_INFORMATION pingOptions = + { + 1, + 0, + IP_FLAG_DF, + 0 + }; + + if (icmpRandString = PhCreateStringEx(NULL, PhGetIntegerSetting(SETTING_NAME_PING_SIZE) * sizeof(WCHAR) + sizeof(UNICODE_NULL))) + { + PhGenerateRandomAlphaString(icmpRandString->Buffer, (ULONG)icmpRandString->Length / sizeof(WCHAR)); + + icmpEchoBuffer = PhConvertUtf16ToMultiByte(icmpRandString->Buffer); + PhDereferenceObject(icmpRandString); + } + + switch (context->RemoteEndpoint.Address.Type) + { + case PH_IPV4_NETWORK_TYPE: + icmpHandle = IcmpCreateFile(); + break; + case PH_IPV6_NETWORK_TYPE: + icmpHandle = Icmp6CreateFile(); + break; + } + + if (icmpHandle == INVALID_HANDLE_VALUE) + goto CleanupExit; + + if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + ((PSOCKADDR_IN)&destinationAddress)->sin_family = AF_INET; + ((PSOCKADDR_IN)&destinationAddress)->sin_addr = context->RemoteEndpoint.Address.InAddr; + //((PSOCKADDR_IN)&destinationAddress)->sin_port = (USHORT)context->RemoteEndpoint.Port; + } + else if (context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + ((PSOCKADDR_IN6)&destinationAddress)->sin6_family = AF_INET6; + ((PSOCKADDR_IN6)&destinationAddress)->sin6_addr = context->RemoteEndpoint.Address.In6Addr; + //((PSOCKADDR_IN6)&destinationAddress)->sin6_port = (USHORT)context->RemoteEndpoint.Port; + } + + for (ULONG i = 0; i < context->MaximumHops; i++) + { + PTRACERT_ROOT_NODE node; + IN_ADDR last4ReplyAddress = in4addr_any; + IN6_ADDR last6ReplyAddress = in6addr_any; + + if (context->Cancel) + break; + + node = AddTracertNode(context, pingOptions.Ttl); + PhReferenceObject(node); + + for (ULONG ii = 0; ii < DEFAULT_MAXIMUM_PINGS; ii++) + { + if (context->Cancel) + break; + + if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMP_ECHO_REPLY), icmpEchoBuffer); + icmpReplyBuffer = PhAllocate(icmpReplyLength); + memset(icmpReplyBuffer, 0, icmpReplyLength); + + icmpReplyCount = IcmpSendEcho2Ex( + icmpHandle, + 0, + NULL, + NULL, + ((PSOCKADDR_IN)&sourceAddress)->sin_addr.s_addr, + ((PSOCKADDR_IN)&destinationAddress)->sin_addr.s_addr, + icmpEchoBuffer->Buffer, + (USHORT)icmpEchoBuffer->Length, + &pingOptions, + icmpReplyBuffer, + icmpReplyLength, + DEFAULT_TIMEOUT + ); + + if (icmpReplyCount > 0) + { + PICMP_ECHO_REPLY reply4 = (PICMP_ECHO_REPLY)icmpReplyBuffer; + + memcpy_s(&last4ReplyAddress, sizeof(IN_ADDR), &reply4->Address, sizeof(IN_ADDR)); + + TracertQueueHostLookup( + context, + node, + &reply4->Address + ); + + node->PingStatus[ii] = reply4->Status; + node->PingList[ii] = reply4->RoundTripTime; + UpdateTracertNode(context, node); + + if (reply4->Status == IP_HOP_LIMIT_EXCEEDED && reply4->RoundTripTime < MIN_INTERVAL) + { + //PhDelayExecution(MIN_INTERVAL - reply4->RoundTripTime); + } + + //if (reply4->Status != IP_REQ_TIMED_OUT) + //{ + // PPH_STRING errorMessage; + // + // if (errorMessage = TracertGetErrorMessage(reply4->Status)) + // { + // node->IpAddressString = errorMessage; + // } + //} + } + else + { + node->PingStatus[ii] = GetLastError(); // IP_REQ_TIMED_OUT; + UpdateTracertNode(context, node); + } + + PhFree(icmpReplyBuffer); + } + else + { + icmpReplyLength = ICMP_BUFFER_SIZE(sizeof(ICMPV6_ECHO_REPLY), icmpEchoBuffer); + icmpReplyBuffer = PhAllocate(icmpReplyLength); + memset(icmpReplyBuffer, 0, icmpReplyLength); + + icmpReplyCount = Icmp6SendEcho2( + icmpHandle, + 0, + NULL, + NULL, + ((PSOCKADDR_IN6)&sourceAddress), + ((PSOCKADDR_IN6)&destinationAddress), + icmpEchoBuffer->Buffer, + (USHORT)icmpEchoBuffer->Length, + &pingOptions, + icmpReplyBuffer, + icmpReplyLength, + DEFAULT_TIMEOUT + ); + + if (icmpReplyCount > 0) + { + PICMPV6_ECHO_REPLY reply6 = (PICMPV6_ECHO_REPLY)icmpReplyBuffer; + + memcpy(&last6ReplyAddress, &reply6->Address.sin6_addr, sizeof(IN6_ADDR)); + + TracertQueueHostLookup( + context, + node, + &reply6->Address.sin6_addr + ); + + node->PingStatus[ii] = reply6->Status; + node->PingList[ii] = reply6->RoundTripTime; + UpdateTracertNode(context, node); + + if (reply6->Status == IP_HOP_LIMIT_EXCEEDED) + { + if (reply6->RoundTripTime < MIN_INTERVAL) + { + //PhDelayExecution(MIN_INTERVAL - reply6->RoundTripTime); + } + } + } + else + { + node->PingStatus[ii] = GetLastError(); // IP_REQ_TIMED_OUT; + UpdateTracertNode(context, node); + } + + PhFree(icmpReplyBuffer); + } + } + + PhDereferenceObject(node); + + if (context->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + if (RtlEqualMemory(&last4ReplyAddress, &((PSOCKADDR_IN)&destinationAddress)->sin_addr, sizeof(IN_ADDR))) + break; + } + else if (context->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + if (RtlEqualMemory(&last6ReplyAddress, &((PSOCKADDR_IN6)&destinationAddress)->sin6_addr, sizeof(IN6_ADDR))) + break; + } + + pingOptions.Ttl++; + } + +CleanupExit: + + if (icmpHandle != INVALID_HANDLE_VALUE) + IcmpCloseHandle(icmpHandle); + + PostMessage(context->WindowHandle, NTM_RECEIVEDFINISH, 0, 0); + PhDereferenceObject(context); + + if (icmpEchoBuffer) + PhDereferenceObject(icmpEchoBuffer); + + return STATUS_SUCCESS; +} + +VOID TracertMenuActionCallback( + _In_ PNETWORK_TRACERT_CONTEXT Context, + _In_ ULONG Id + ) +{ + switch (Id) + { + case MAINMENU_ACTION_PING: + { + PH_IP_ENDPOINT RemoteEndpoint; + PWSTR terminator = NULL; + PTRACERT_ROOT_NODE node; + + if ((node = GetSelectedTracertNode(Context)) && !PhIsNullOrEmptyString(node->IpAddressString)) + { + if (NT_SUCCESS(RtlIpv4StringToAddress( + node->IpAddressString->Buffer, + TRUE, + &terminator, + &RemoteEndpoint.Address.InAddr + ))) + { + RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; + ShowPingWindowFromAddress(RemoteEndpoint); + break; + } + + if (NT_SUCCESS(RtlIpv6StringToAddress( + node->IpAddressString->Buffer, + &terminator, + &RemoteEndpoint.Address.In6Addr + ))) + { + RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; + ShowPingWindowFromAddress(RemoteEndpoint); + break; + } + } + } + break; + case NETWORK_ACTION_TRACEROUTE: + { + PH_IP_ENDPOINT RemoteEndpoint; + PWSTR terminator = NULL; + PTRACERT_ROOT_NODE node; + + if ((node = GetSelectedTracertNode(Context)) && !PhIsNullOrEmptyString(node->IpAddressString)) + { + if (NT_SUCCESS(RtlIpv4StringToAddress( + node->IpAddressString->Buffer, + TRUE, + &terminator, + &RemoteEndpoint.Address.InAddr + ))) + { + RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; + ShowTracertWindowFromAddress(RemoteEndpoint); + break; + } + + if (NT_SUCCESS(RtlIpv6StringToAddress( + node->IpAddressString->Buffer, + &terminator, + &RemoteEndpoint.Address.In6Addr + ))) + { + RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; + ShowTracertWindowFromAddress(RemoteEndpoint); + break; + } + } + } + break; + case NETWORK_ACTION_WHOIS: + { + PH_IP_ENDPOINT RemoteEndpoint; + PWSTR terminator = NULL; + PTRACERT_ROOT_NODE node; + + if ((node = GetSelectedTracertNode(Context)) && !PhIsNullOrEmptyString(node->IpAddressString)) + { + if (NT_SUCCESS(RtlIpv4StringToAddress( + node->IpAddressString->Buffer, + TRUE, + &terminator, + &RemoteEndpoint.Address.InAddr + ))) + { + RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE; + ShowWhoisWindowFromAddress(RemoteEndpoint); + break; + } + + if (NT_SUCCESS(RtlIpv6StringToAddress( + node->IpAddressString->Buffer, + &terminator, + &RemoteEndpoint.Address.In6Addr + ))) + { + RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE; + ShowWhoisWindowFromAddress(RemoteEndpoint); + break; + } + } + } + break; + case MENU_ACTION_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(Context->TreeNewHandle, 0); + PhSetClipboardString(Context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + break; + } +} + +INT_PTR CALLBACK TracertDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PNETWORK_TRACERT_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PNETWORK_TRACERT_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + context->Cancel = TRUE; + + PhSaveWindowPlacementToSetting(SETTING_NAME_TRACERT_WINDOW_POSITION, SETTING_NAME_TRACERT_WINDOW_SIZE, hwndDlg); + + if (context->FontHandle) + DeleteFont(context->FontHandle); + + DeleteTracertTree(context); + + PhDeleteWorkQueue(&context->WorkQueue); + PhDeleteLayoutManager(&context->LayoutManager); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhDereferenceObject(context); + + PostQuitMessage(0); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + Static_SetText(hwndDlg, + PhaFormatString(L"Tracing %s...", context->IpAddressString)->Buffer + ); + Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), + PhaFormatString(L"Tracing route to %s with %lu bytes of data...", context->IpAddressString, PhGetIntegerSetting(SETTING_NAME_PING_SIZE))->Buffer + ); + + context->WindowHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST_TRACERT); + context->FontHandle = PhCreateCommonFont(-15, FW_MEDIUM, GetDlgItem(hwndDlg, IDC_STATUS)); + + InitializeTracertTree(context); + + PhInitializeWorkQueue(&context->WorkQueue, 0, 40, 5000); + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_STATUS), NULL, PH_ANCHOR_TOP | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT | PH_LAYOUT_FORCE_INVALIDATE); + PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REFRESH), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + + if (PhGetIntegerPairSetting(SETTING_NAME_TRACERT_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_TRACERT_WINDOW_POSITION, SETTING_NAME_TRACERT_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + + EnableWindow(GetDlgItem(hwndDlg, IDC_REFRESH), FALSE); + + PhReferenceObject(context); + PhCreateThread2(NetworkTracertThreadStart, (PVOID)context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + { + context->Cancel = TRUE; + + DestroyWindow(hwndDlg); + } + break; + case IDC_REFRESH: + { + Static_SetText(context->WindowHandle, PhaFormatString( + L"Tracing %s...", + context->IpAddressString + )->Buffer); + Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), PhaFormatString( + L"Tracing route to %s with %lu bytes of data....", + context->IpAddressString, + PhGetIntegerSetting(SETTING_NAME_PING_SIZE) + )->Buffer); + + EnableWindow(GetDlgItem(hwndDlg, IDC_REFRESH), FALSE); + + ClearTracertTree(context); + + PhReferenceObject(context); + PhCreateThread2(NetworkTracertThreadStart, (PVOID)context); + } + break; + case TRACERT_SHOWCONTEXTMENU: + { + PPH_EMENU menu; + PTRACERT_ROOT_NODE selectedNode; + PPH_EMENU_ITEM selectedItem; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + + if (selectedNode = GetSelectedTracertNode(context)) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MAINMENU_ACTION_PING, L"Ping", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, NETWORK_ACTION_TRACEROUTE, L"Traceroute", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, NETWORK_ACTION_WHOIS, L"Whois", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, MENU_ACTION_COPY, L"Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, MENU_ACTION_COPY, context->TreeNewHandle, contextMenuEvent->Column); + + if (PhIsNullOrEmptyString(selectedNode->IpAddressString)) + { + PhSetFlagsEMenuItem(menu, MAINMENU_ACTION_PING, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhSetFlagsEMenuItem(menu, NETWORK_ACTION_TRACEROUTE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhSetFlagsEMenuItem(menu, NETWORK_ACTION_WHOIS, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + } + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + if (!PhHandleCopyCellEMenuItem(selectedItem)) + { + TracertMenuActionCallback(context, selectedItem->Id); + } + } + + PhDestroyEMenu(menu); + } + } + break; + } + } + break; + case WM_SIZE: + PhLayoutManagerLayout(&context->LayoutManager); + break; + case NTM_RECEIVEDFINISH: + { + EnableWindow(GetDlgItem(hwndDlg, IDC_REFRESH), TRUE); + + Static_SetText(context->WindowHandle, PhaFormatString( + L"Tracing %s... complete", + context->IpAddressString + )->Buffer); + Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), PhaFormatString( + L"Tracing route to %s with %lu bytes of data... complete.", + context->IpAddressString, + PhGetIntegerSetting(SETTING_NAME_PING_SIZE) + )->Buffer); + + TreeNew_NodesStructured(context->TreeNewHandle); + } + break; + case WM_TRACERT_HOSTNAME: + { + ULONG index = (ULONG)wParam; + PPH_STRING hostName = (PPH_STRING)lParam; + PTRACERT_ROOT_NODE traceNode; + + if (traceNode = FindTracertNode(context, index)) + { + if (!PhIsNullOrEmptyString(traceNode->HostnameString)) + { + // Make sure we don't append the same hostname. + if (PhFindStringInString(traceNode->HostnameString, 0, PhGetString(hostName)) == -1) + { + // Some routes can return multiple addresses for the same ping or 'hop', + // so make sure we don't lose this information (as every other tracert tool does) + // and instead append the additional hostname to the node. + PhMoveReference(&traceNode->HostnameString, PhFormatString( + L"%s, %s", + PhGetString(traceNode->HostnameString), + PhGetString(hostName) + )); + + UpdateTracertNode(context, traceNode); + } + } + else + { + PhSwapReference(&traceNode->HostnameString, hostName); + + UpdateTracertNode(context, traceNode); + } + } + + PhClearReference(&hostName); + } + break; + } + + return FALSE; +} + +NTSTATUS TracertDialogThreadStart( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + HWND windowHandle; + PH_AUTO_POOL autoPool; + PNETWORK_TRACERT_CONTEXT context = (PNETWORK_TRACERT_CONTEXT)Parameter; + + PhInitializeAutoPool(&autoPool); + + windowHandle = CreateDialogParam( + (HINSTANCE)PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_TRACERT), + NULL, + TracertDlgProc, + (LPARAM)Parameter + ); + + ShowWindow(windowHandle, SW_SHOW); + SetForegroundWindow(windowHandle); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(windowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + +VOID ShowTracertWindow( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PNETWORK_TRACERT_CONTEXT context; + + context = (PNETWORK_TRACERT_CONTEXT)PhCreateAlloc(sizeof(NETWORK_TRACERT_CONTEXT)); + memset(context, 0, sizeof(NETWORK_TRACERT_CONTEXT)); + + context->MaximumHops = PhGetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS); + + RtlCopyMemory( + &context->RemoteEndpoint, + &NetworkItem->RemoteEndpoint, + sizeof(PH_IP_ENDPOINT) + ); + + if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + RtlIpv4AddressToString(&NetworkItem->RemoteEndpoint.Address.InAddr, context->IpAddressString); + } + else if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); + } + + PhCreateThread2(TracertDialogThreadStart, (PVOID)context); +} + +VOID ShowTracertWindowFromAddress( + _In_ PH_IP_ENDPOINT RemoteEndpoint + ) +{ + PNETWORK_TRACERT_CONTEXT context; + + context = (PNETWORK_TRACERT_CONTEXT)PhCreateAlloc(sizeof(NETWORK_TRACERT_CONTEXT)); + memset(context, 0, sizeof(NETWORK_TRACERT_CONTEXT)); + + context->MaximumHops = PhGetIntegerSetting(SETTING_NAME_TRACERT_MAX_HOPS); + + RtlCopyMemory( + &context->RemoteEndpoint, + &RemoteEndpoint, + sizeof(PH_IP_ENDPOINT) + ); + + if (RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + RtlIpv4AddressToString(&RemoteEndpoint.Address.InAddr, context->IpAddressString); + } + else if (RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); + } + + PhCreateThread2(TracertDialogThreadStart, (PVOID)context); +} diff --git a/plugins/NetworkTools/tracert.h b/plugins/NetworkTools/tracert.h index 27bcad79bd48..c73913b2c381 100644 --- a/plugins/NetworkTools/tracert.h +++ b/plugins/NetworkTools/tracert.h @@ -24,7 +24,6 @@ #define _TRACERT_H_ #define DEFAULT_MAXIMUM_PINGS 4 -#define DEFAULT_MAXIMUM_HOPS 40 #define DEFAULT_SEND_SIZE 64 #define DEFAULT_RECEIVE_SIZE ((sizeof(ICMP_ECHO_REPLY) + DEFAULT_SEND_SIZE + MAX_OPT_SIZE)) #define DEFAULT_TIMEOUT 1000 @@ -48,20 +47,19 @@ typedef struct _TRACERT_ROOT_NODE { PH_TREENEW_NODE Node; + ULONG UniqueId; ULONG TTL; ULONG PingStatus[DEFAULT_MAXIMUM_PINGS]; ULONG PingList[DEFAULT_MAXIMUM_PINGS]; PPH_STRING PingString[DEFAULT_MAXIMUM_PINGS]; + PPH_STRING PingMessage[DEFAULT_MAXIMUM_PINGS]; - HICON CountryIcon; - + INT CountryIconIndex; PPH_STRING TtlString; - PPH_STRING CountryString; PPH_STRING HostnameString; PPH_STRING IpAddressString; PPH_STRING RemoteCountryCode; PPH_STRING RemoteCountryName; - PPH_STRING RemoteCityName; PH_STRINGREF TextCache[TREE_COLUMN_ITEM_MAXIMUM]; } TRACERT_ROOT_NODE, *PTRACERT_ROOT_NODE; @@ -71,8 +69,7 @@ typedef struct _TRACERT_RESOLVE_WORKITEM SOCKADDR_STORAGE SocketAddress; HWND WindowHandle; - PTRACERT_ROOT_NODE Node; - + ULONG Index; WCHAR SocketAddressHostname[NI_MAXHOST]; } TRACERT_RESOLVE_WORKITEM, *PTRACERT_RESOLVE_WORKITEM; @@ -105,8 +102,16 @@ TracertGetFilterSupportTreeList( VOID ); +VOID ClearTracertTree( + _In_ PNETWORK_TRACERT_CONTEXT Context + ); + PTRACERT_ROOT_NODE GetSelectedTracertNode( _In_ PNETWORK_TRACERT_CONTEXT Context ); -#endif \ No newline at end of file +PPH_STRING TracertGetErrorMessage( + _In_ IP_STATUS Result + ); + +#endif diff --git a/plugins/NetworkTools/tracetree.c b/plugins/NetworkTools/tracetree.c index 5b7ec58b8463..bcfabb4e84e7 100644 --- a/plugins/NetworkTools/tracetree.c +++ b/plugins/NetworkTools/tracetree.c @@ -24,6 +24,53 @@ #include "tracert.h" #include +PPH_OBJECT_TYPE TracertTreeNodeItemType; + +VOID NTAPI TracertTreeNodeItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PTRACERT_ROOT_NODE tracertNode = Object; + + if (tracertNode->TtlString) + PhDereferenceObject(tracertNode->TtlString); + if (tracertNode->HostnameString) + PhDereferenceObject(tracertNode->HostnameString); + if (tracertNode->IpAddressString) + PhDereferenceObject(tracertNode->IpAddressString); + if (tracertNode->RemoteCountryCode) + PhDereferenceObject(tracertNode->RemoteCountryCode); + if (tracertNode->RemoteCountryName) + PhDereferenceObject(tracertNode->RemoteCountryName); + + for (ULONG i = 0; i < DEFAULT_MAXIMUM_PINGS; i++) + { + if (tracertNode->PingString[i]) + PhDereferenceObject(tracertNode->PingString[i]); + if (tracertNode->PingMessage[i]) + PhDereferenceObject(tracertNode->PingMessage[i]); + } +} + +PTRACERT_ROOT_NODE TracertTreeCreateNode( + VOID + ) +{ + static ULONG NextUniqueId = 1; + PTRACERT_ROOT_NODE tracertNode; + + tracertNode = PhCreateObject(sizeof(TRACERT_ROOT_NODE), TracertTreeNodeItemType); + memset(tracertNode, 0, sizeof(TRACERT_ROOT_NODE)); + + PhInitializeTreeNewNode(&tracertNode->Node); + + tracertNode->UniqueId = NextUniqueId++; // used to stabilize sorting + tracertNode->CountryIconIndex = INT_MAX; + + return tracertNode; +} + #define SORT_FUNCTION(Column) TracertTreeNewCompare##Column #define BEGIN_SORT_FUNCTION(Column) static int __cdecl TracertTreeNewCompare##Column( \ _In_ void *_context, \ @@ -37,7 +84,7 @@ #define END_SORT_FUNCTION \ if (sortResult == 0) \ - sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ + sortResult = uintcmp(node1->UniqueId, node2->UniqueId); \ \ return PhModifySort(sortResult, ((PNETWORK_TRACERT_CONTEXT)_context)->TreeNewSortOrder); \ } @@ -72,15 +119,37 @@ BEGIN_SORT_FUNCTION(Ping4) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(IpAddress) +{ + sortResult = PhCompareStringWithNull(node1->IpAddressString, node2->IpAddressString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Hostname) +{ + sortResult = PhCompareStringWithNull(node1->HostnameString, node2->HostnameString, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Country) +{ + sortResult = PhCompareStringWithNull(node1->RemoteCountryName, node2->RemoteCountryName, TRUE); +} +END_SORT_FUNCTION + VOID TracertLoadSettingsTreeList( _Inout_ PNETWORK_TRACERT_CONTEXT Context ) { PPH_STRING settings; + PH_INTEGER_PAIR sortSettings; - settings = PhGetStringSetting(SETTING_NAME_TRACERT_LIST_COLUMNS); + settings = PhGetStringSetting(SETTING_NAME_TRACERT_TREE_LIST_COLUMNS); PhCmLoadSettings(Context->TreeNewHandle, &settings->sr); PhDereferenceObject(settings); + + sortSettings = PhGetIntegerPairSetting(SETTING_NAME_TRACERT_TREE_LIST_SORT); + TreeNew_SetSort(Context->TreeNewHandle, (ULONG)sortSettings.X, (PH_SORT_ORDER)sortSettings.Y); } VOID TracertSaveSettingsTreeList( @@ -88,10 +157,18 @@ VOID TracertSaveSettingsTreeList( ) { PPH_STRING settings; + PH_INTEGER_PAIR sortSettings; + ULONG sortColumn; + PH_SORT_ORDER sortOrder; settings = PhCmSaveSettings(Context->TreeNewHandle); - PhSetStringSetting2(SETTING_NAME_TRACERT_LIST_COLUMNS, &settings->sr); + PhSetStringSetting2(SETTING_NAME_TRACERT_TREE_LIST_COLUMNS, &settings->sr); PhDereferenceObject(settings); + + TreeNew_GetSort(Context->TreeNewHandle, &sortColumn, &sortOrder); + sortSettings.X = sortColumn; + sortSettings.Y = sortOrder; + PhSetIntegerPairSetting(SETTING_NAME_TRACERT_TREE_LIST_SORT, sortSettings); } BOOLEAN TracertNodeHashtableEqualFunction( @@ -109,24 +186,14 @@ ULONG TracertNodeHashtableHashFunction( _In_ PVOID Entry ) { - return (*(PTRACERT_ROOT_NODE*)Entry)->TTL; + return PhHashInt32((*(PTRACERT_ROOT_NODE*)Entry)->TTL); } VOID DestroyTracertNode( _In_ PTRACERT_ROOT_NODE Node ) { - PhClearReference(&Node->TtlString); - PhClearReference(&Node->CountryString); - PhClearReference(&Node->HostnameString); - PhClearReference(&Node->IpAddressString); - PhClearReference(&Node->RemoteCountryCode); - PhClearReference(&Node->RemoteCountryName); - - for (ULONG i = 0; i < DEFAULT_MAXIMUM_PINGS; i++) - PhClearReference(&Node->PingString[i]); - - PhDereferenceObject(Node); + PhDereferenceObject(Node); } PTRACERT_ROOT_NODE AddTracertNode( @@ -136,10 +203,7 @@ PTRACERT_ROOT_NODE AddTracertNode( { PTRACERT_ROOT_NODE tracertNode; - tracertNode = PhCreateAlloc(sizeof(TRACERT_ROOT_NODE)); - memset(tracertNode, 0, sizeof(TRACERT_ROOT_NODE)); - - PhInitializeTreeNewNode(&tracertNode->Node); + tracertNode = TracertTreeCreateNode(); tracertNode->TTL = TTL; memset(tracertNode->PingStatus, STATUS_FAIL_CHECK, sizeof(tracertNode->PingStatus)); @@ -187,7 +251,7 @@ VOID RemoveTracertNode( PhRemoveEntryHashtable(Context->NodeHashtable, &Node); - if ((index = PhFindItemList(Context->NodeList, Node)) != -1) + if ((index = PhFindItemList(Context->NodeList, Node)) != ULONG_MAX) { PhRemoveItemList(Context->NodeList, index); } @@ -213,8 +277,8 @@ VOID UpdateTracertNodePingText( _In_ ULONG Index ) { - if (Node->PingStatus[Index] == IP_HOP_LIMIT_EXCEEDED || - Node->PingStatus[Index] == IP_SUCCESS) + if (Node->PingStatus[Index] == IP_SUCCESS || + Node->PingStatus[Index] == IP_TTL_EXPIRED_TRANSIT) // IP_HOP_LIMIT_EXCEEDED { if (Node->PingList[Index]) { @@ -223,7 +287,7 @@ VOID UpdateTracertNodePingText( PhFormatString(L"%s ms", PhaFormatUInt64(Node->PingList[Index], TRUE)->Buffer) ); - CellText->Text = Node->PingString[Index]->sr; + CellText->Text = PhGetStringRef(Node->PingString[Index]); } else { @@ -236,7 +300,8 @@ VOID UpdateTracertNodePingText( } else { - PhInitializeEmptyStringRef(&CellText->Text); + Node->PingMessage[Index] = TracertGetErrorMessage(Node->PingStatus[Index]); + CellText->Text = PhGetStringRef(Node->PingMessage[Index]); } } @@ -268,6 +333,9 @@ BOOLEAN NTAPI TracertTreeNewCallback( SORT_FUNCTION(Ping2), SORT_FUNCTION(Ping3), SORT_FUNCTION(Ping4), + SORT_FUNCTION(IpAddress), + SORT_FUNCTION(Hostname), + SORT_FUNCTION(Country), }; int (__cdecl *sortFunction)(void *, const void *, const void *); @@ -319,15 +387,15 @@ BOOLEAN NTAPI TracertTreeNewCallback( case TREE_COLUMN_ITEM_PING4: UpdateTracertNodePingText(node, getCellText, 3); break; - case TREE_COLUMN_ITEM_COUNTRY: - getCellText->Text = PhGetStringRef(node->CountryString); - break; case TREE_COLUMN_ITEM_IPADDR: getCellText->Text = PhGetStringRef(node->IpAddressString); break; case TREE_COLUMN_ITEM_HOSTNAME: getCellText->Text = PhGetStringRef(node->HostnameString); break; + case TREE_COLUMN_ITEM_COUNTRY: + getCellText->Text = PhGetStringRef(node->RemoteCountryName); + break; default: return FALSE; } @@ -352,13 +420,13 @@ BOOLEAN NTAPI TracertTreeNewCallback( return TRUE; case TreeNewContextMenu: { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; SendMessage( context->WindowHandle, WM_COMMAND, TRACERT_SHOWCONTEXTMENU, - (LPARAM)mouseEvent + (LPARAM)contextMenuEvent ); } return TRUE; @@ -401,60 +469,37 @@ BOOLEAN NTAPI TracertTreeNewCallback( rect.left += 5; // Draw the column data - if (GeoDbLoaded && node->RemoteCountryCode && node->RemoteCountryName) + if (GeoDbLoaded && !GeoDbExpired && node->RemoteCountryCode && node->RemoteCountryName) { - if (!node->CountryIcon) - { - INT resourceCode; - - if ((resourceCode = LookupResourceCode(node->RemoteCountryCode)) != 0) - { - HBITMAP countryBitmap; - - if (countryBitmap = LoadImageFromResources(PluginInstance->DllBase, 16, 11, MAKEINTRESOURCE(resourceCode), TRUE)) - { - node->CountryIcon = CommonBitmapToIcon(countryBitmap, 16, 11); - } - } - } + if (node->CountryIconIndex == INT_MAX) + node->CountryIconIndex = LookupCountryIcon(node->RemoteCountryCode); - if (node->CountryIcon) + if (node->CountryIconIndex != INT_MAX) { - DrawIconEx( - hdc, - rect.left, - rect.top + ((rect.bottom - rect.top) - 11) / 2, - node->CountryIcon, - 16, - 11, - 0, - NULL, - DI_NORMAL - ); - + DrawCountryIcon(hdc, rect, node->CountryIconIndex); rect.left += 16 + 2; } DrawText( hdc, - PhGetStringOrEmpty(node->RemoteCountryName), - -1, + node->RemoteCountryName->Buffer, + (INT)node->RemoteCountryName->Length / sizeof(WCHAR), &rect, - DT_LEFT | DT_VCENTER | DT_SINGLELINE + DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE ); } - if (GeoDbExpired && !node->CountryIcon) + if (GeoDbExpired) { - DrawText(hdc, L"Geoip database expired.", -1, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + DrawText(hdc, L"Geoip database expired.", -1, &rect, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE); } if (!GeoDbLoaded) { - DrawText(hdc, L"Geoip database error.", -1, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + DrawText(hdc, L"Geoip database not found.", -1, &rect, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE); } } - break; + return TRUE; } return FALSE; @@ -471,6 +516,8 @@ VOID ClearTracertTree( PhClearHashtable(Context->NodeHashtable); PhClearList(Context->NodeList); + + TreeNew_NodesStructured(Context->TreeNewHandle); } PTRACERT_ROOT_NODE GetSelectedTracertNode( @@ -522,6 +569,14 @@ VOID InitializeTracertTree( _Inout_ PNETWORK_TRACERT_CONTEXT Context ) { + static PH_INITONCE initOnce = PH_INITONCE_INIT; + + if (PhBeginInitOnce(&initOnce)) + { + TracertTreeNodeItemType = PhCreateObjectType(L"TracertTreeNodeItem", 0, TracertTreeNodeItemDeleteProcedure); + PhEndInitOnce(&initOnce); + } + Context->NodeList = PhCreateList(100); Context->NodeHashtable = PhCreateHashtable( sizeof(PTRACERT_ROOT_NODE), @@ -546,7 +601,6 @@ VOID InitializeTracertTree( //for (INT i = 0; i < MAX_PINGS; i++) // PhAddTreeNewColumn(context->TreeNewHandle, i + 1, i + 1, i + 1, LVCFMT_RIGHT, 50, L"Time"); - TreeNew_SetTriState(Context->TreeNewHandle, TRUE); TreeNew_SetSort(Context->TreeNewHandle, TREE_COLUMN_ITEM_TTL, AscendingSortOrder); TracertLoadSettingsTreeList(Context); @@ -565,4 +619,4 @@ VOID DeleteTracertTree( PhDereferenceObject(Context->NodeHashtable); PhDereferenceObject(Context->NodeList); -} \ No newline at end of file +} diff --git a/plugins/NetworkTools/update.c b/plugins/NetworkTools/update.c index b4604b58a20c..ad60b52d1507 100644 --- a/plugins/NetworkTools/update.c +++ b/plugins/NetworkTools/update.c @@ -2,7 +2,7 @@ * Process Hacker Network Tools - * GeoIP database updater * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -29,57 +29,47 @@ HWND UpdateDialogHandle = NULL; HANDLE UpdateDialogThreadHandle = NULL; PH_EVENT InitializedEvent = PH_EVENT_INIT; +PPH_OBJECT_TYPE UpdateContextType = NULL; +PH_INITONCE UpdateContextTypeInitOnce = PH_INITONCE_INIT; -VOID FreeUpdateContext( - _In_ _Post_invalid_ PPH_UPDATER_CONTEXT Context +VOID UpdateContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags ) { - //PhClearReference(&Context->Version); - // PhClearReference(&Context->RevVersion); - //PhClearReference(&Context->RelDate); - // PhClearReference(&Context->Size); - //PhClearReference(&Context->Hash); - // PhClearReference(&Context->Signature); - // PhClearReference(&Context->ReleaseNotesUrl); - //PhClearReference(&Context->SetupFilePath); - // PhClearReference(&Context->SetupFileDownloadUrl); - - if (Context->IconLargeHandle) - DestroyIcon(Context->IconLargeHandle); - - if (Context->IconSmallHandle) - DestroyIcon(Context->IconSmallHandle); - - //PhClearReference(&Context); + PPH_UPDATER_CONTEXT context = Object; + + if (context->FileDownloadUrl) + PhDereferenceObject(context->FileDownloadUrl); +} + +PPH_UPDATER_CONTEXT CreateUpdateContext( + VOID + ) +{ + PPH_UPDATER_CONTEXT context; + + if (PhBeginInitOnce(&UpdateContextTypeInitOnce)) + { + UpdateContextType = PhCreateObjectType(L"GeoIpContextObjectType", 0, UpdateContextDeleteProcedure); + PhEndInitOnce(&UpdateContextTypeInitOnce); + } + + context = PhCreateObject(sizeof(PH_UPDATER_CONTEXT), UpdateContextType); + memset(context, 0, sizeof(PH_UPDATER_CONTEXT)); + + return context; } VOID TaskDialogCreateIcons( _In_ PPH_UPDATER_CONTEXT Context ) { - HICON largeIcon; - HICON smallIcon; - - largeIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); - smallIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON) - ); - - Context->IconLargeHandle = largeIcon; - Context->IconSmallHandle = smallIcon; - - SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); } VOID TaskDialogLinkClicked( @@ -90,7 +80,7 @@ VOID TaskDialogLinkClicked( } PPH_STRING UpdateVersionString( - _In_ PWSTR UserAgent + VOID ) { ULONG majorVersion; @@ -103,7 +93,7 @@ PPH_STRING UpdateVersionString( if (currentVersion = PhFormatString(L"%lu.%lu.%lu", majorVersion, minorVersion, revisionVersion)) { - versionHeader = PhConcatStrings2(UserAgent, currentVersion->Buffer); + versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer); PhDereferenceObject(currentVersion); } @@ -151,153 +141,61 @@ PPH_STRING QueryFwLinkUrl( ) { PPH_STRING redirectUrl = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - PPH_STRING versionHeader = UpdateVersionString(L"ProcessHacker_Build: "); - PPH_STRING windowsHeader = UpdateWindowsString(); + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING versionHeader; + PPH_STRING windowsHeader; - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); - if (!(httpSessionHandle = WinHttpOpen( - NULL, - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { + if (!PhHttpSocketCreate(&httpContext, PhGetString(userAgentString))) goto CleanupExit; - } - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, - sizeof(ULONG) - ); - } - - if (!(httpConnectionHandle = WinHttpConnect( - httpSessionHandle, - L"wj32.org", - INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) - { + if (!PhHttpSocketConnect(httpContext, L"wj32.org", PH_HTTP_DEFAULT_HTTPS_PORT)) goto CleanupExit; - } - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, + if (!PhHttpSocketBeginRequest( + httpContext, NULL, L"/processhacker/fwlink/maxminddb.php", - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE - ))) + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) { goto CleanupExit; } - if (versionHeader) - { - WinHttpAddRequestHeaders( - httpRequestHandle, - versionHeader->Buffer, - (ULONG)versionHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); - } - - if (windowsHeader) + if (versionHeader = UpdateVersionString()) { - WinHttpAddRequestHeaders( - httpRequestHandle, - windowsHeader->Buffer, - (ULONG)windowsHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); + PhHttpSocketAddRequestHeaders(httpContext, versionHeader->Buffer, (ULONG)versionHeader->Length / sizeof(WCHAR)); + PhDereferenceObject(versionHeader); } - if (WindowsVersion >= WINDOWS_7) + if (windowsHeader = UpdateWindowsString()) { - ULONG option = WINHTTP_DISABLE_REDIRECTS; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); - - option = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); + PhHttpSocketAddRequestHeaders(httpContext, windowsHeader->Buffer, (ULONG)windowsHeader->Length / sizeof(WCHAR)); + PhDereferenceObject(windowsHeader); } - if (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { + if (!PhHttpSocketSetFeature(httpContext, PH_HTTP_FEATURE_REDIRECTS, FALSE)) goto CleanupExit; - } - if (WinHttpReceiveResponse(httpRequestHandle, NULL)) - { - ULONG redirectLength = 0; - - if (WinHttpQueryHeaders( - httpRequestHandle, - WINHTTP_QUERY_LOCATION, - WINHTTP_HEADER_NAME_BY_INDEX, - NULL, - &redirectLength, - 0 - )) - { - goto CleanupExit; - } + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) + goto CleanupExit; - if (redirectLength) - { - redirectUrl = PhCreateStringEx(NULL, redirectLength); - - if (!WinHttpQueryHeaders( - httpRequestHandle, - WINHTTP_QUERY_LOCATION, - WINHTTP_HEADER_NAME_BY_INDEX, - redirectUrl->Buffer, - &redirectLength, - 0 - )) - { - PhClearReference(&redirectUrl); - } - } - } + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + redirectUrl = PhHttpSocketQueryHeaderString(httpContext, L"Location"); // WINHTTP_QUERY_LOCATION CleanupExit: - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); + if (httpContext) + PhHttpSocketDestroy(httpContext); - if (versionHeader) - PhDereferenceObject(versionHeader); - - if (windowsHeader) - PhDereferenceObject(windowsHeader); + PhClearReference(&versionString); + PhClearReference(&userAgentString); return redirectUrl; } @@ -308,74 +206,38 @@ NTSTATUS GeoIPUpdateThread( { BOOLEAN success = FALSE; HANDLE tempFileHandle = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; PPH_STRING fwLinkUrl = NULL; - PPH_STRING setupTempPath = NULL; - PPH_STRING downloadHostPath = NULL; - PPH_STRING downloadUrlPath = NULL; - PPH_STRING fullSetupPath = NULL; - PPH_STRING randomGuidString = NULL; - ULONG indexOfFileName = -1; - GUID randomGuid; - URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING zipFilePath = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING httpHostName = NULL; + PPH_STRING httpHostPath = NULL; + PPH_STRING dbpath = NULL; + PPH_BYTES mmdbGzPath = NULL; + gzFile gzfile = NULL; + USHORT httpHostPort = 0; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; ULONG64 timeTicks = 0; ULONG64 timeBitsPerSecond = 0; PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter; - PPH_STRING userAgentString = UpdateVersionString(L"ProcessHacker_"); SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request..."); if (!(fwLinkUrl = QueryFwLinkUrl(context))) goto CleanupExit; - setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) - goto CleanupExit; - if (PhIsNullOrEmptyString(setupTempPath)) - goto CleanupExit; - - // Generate random guid for our directory path - PhGenerateGuid(&randomGuid); - - if (randomGuidString = PhFormatGuid(&randomGuid)) - { - PhMoveReference(&randomGuidString, PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2)); - } - - // Append the tempath to our string: %TEMP%RandomString\\GeoLite2-Country.mmdb.gz - // Example: C:\\Users\\dmex\\AppData\\Temp\\ABCD\\GeoLite2-Country.mmdb.gz - context->SetupFilePath = PhFormatString( - L"%s\\%s\\GeoLite2-Country.mmdb.gz", - PhGetStringOrEmpty(setupTempPath), - PhGetStringOrDefault(randomGuidString, L"NetworkTools") - ); - - // Create the directory if it does not exist - if (fullSetupPath = PhGetFullPath(PhGetString(context->SetupFilePath), &indexOfFileName)) - { - PPH_STRING directoryPath; - - if (indexOfFileName == -1) - goto CleanupExit; + zipFilePath = PhCreateCacheFile(PhaCreateString(L"GeoLite2-Country.mmdb.gz")); - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } - } + if (PhIsNullOrEmptyString(zipFilePath)) + goto CleanupExit; - // Create output file if (!NT_SUCCESS(PhCreateFileWin32( &tempFileHandle, - PhGetString(context->SetupFilePath), + PhGetString(zipFilePath), FILE_GENERIC_READ | FILE_GENERIC_WRITE, - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT @@ -386,110 +248,57 @@ NTSTATUS GeoIPUpdateThread( SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); - // Set lengths to non-zero enabling these params to be cracked. - httpUrlComponents.dwSchemeLength = (ULONG)-1; - httpUrlComponents.dwHostNameLength = (ULONG)-1; - httpUrlComponents.dwUrlPathLength = (ULONG)-1; - - if (!WinHttpCrackUrl( - fwLinkUrl->Buffer, - 0, - 0, - &httpUrlComponents + if (!PhHttpSocketParseUrl( + fwLinkUrl, + &httpHostName, + &httpHostPath, + &httpHostPort )) { goto CleanupExit; } - // Create the Host string. - downloadHostPath = PhCreateStringEx( - httpUrlComponents.lpszHostName, - httpUrlComponents.dwHostNameLength * sizeof(WCHAR) - ); - if (PhIsNullOrEmptyString(downloadHostPath)) - goto CleanupExit; - - // Create the Path string. - downloadUrlPath = PhCreateStringEx( - httpUrlComponents.lpszUrlPath, - httpUrlComponents.dwUrlPathLength * sizeof(WCHAR) - ); - if (PhIsNullOrEmptyString(downloadUrlPath)) - goto CleanupExit; - - // Query the current system proxy - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); - // Open the HTTP session with the system proxy configuration if available - if (!(httpSessionHandle = WinHttpOpen( - PhGetStringOrEmpty(userAgentString), - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) + if (!PhHttpSocketCreate( + &httpContext, + PhGetString(userAgentString) + )) { goto CleanupExit; } - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); - } - - if (!(httpConnectionHandle = WinHttpConnect( - httpSessionHandle, - PhGetStringOrEmpty(downloadHostPath), - httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) + if (!PhHttpSocketConnect( + httpContext, + PhGetString(httpHostName), + httpHostPort + )) { goto CleanupExit; } - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, + if (!PhHttpSocketBeginRequest( + httpContext, NULL, - PhGetStringOrEmpty(downloadUrlPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) - ))) + PhGetString(httpHostPath), + PH_HTTP_FLAG_REFRESH | (httpHostPort == PH_HTTP_DEFAULT_HTTPS_PORT ? PH_HTTP_FLAG_SECURE : 0) + )) { goto CleanupExit; } - if (WindowsVersion >= WINDOWS_7) - { - ULONG option = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &option, sizeof(ULONG)); - } - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); - if (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) goto CleanupExit; - } SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Waiting for response..."); - if (WinHttpReceiveResponse(httpRequestHandle, NULL)) + if (PhHttpSocketEndRequest(httpContext)) { ULONG bytesDownloaded = 0; ULONG downloadedBytes = 0; - ULONG contentLengthSize = sizeof(ULONG); ULONG contentLength = 0; PPH_STRING status; IO_STATUS_BLOCK isb; @@ -502,21 +311,19 @@ NTSTATUS GeoIPUpdateThread( SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)status->Buffer); PhDereferenceObject(status); - if (!WinHttpQueryHeaders( - httpRequestHandle, - WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, - WINHTTP_HEADER_NAME_BY_INDEX, - &contentLength, - &contentLengthSize, - 0 + if (!PhHttpSocketQueryHeaderUlong( + httpContext, + PH_HTTP_QUERY_CONTENT_LENGTH, + &contentLength )) { + //context->ErrorCode = GetLastError(); goto CleanupExit; } PhQuerySystemTime(&timeStart); - while (WinHttpReadData(httpRequestHandle, buffer, PAGE_SIZE, &bytesDownloaded)) + while (PhHttpSocketReadData(httpContext, buffer, PAGE_SIZE, &bytesDownloaded)) { // If we get zero bytes, the file was uploaded or there was an error. if (bytesDownloaded == 0) @@ -541,7 +348,7 @@ NTSTATUS GeoIPUpdateThread( goto CleanupExit; } - downloadedBytes += (DWORD)isb.Information; + downloadedBytes += (ULONG)isb.Information; // Check the number of bytes written are the same we downloaded. if (bytesDownloaded != isb.Information) @@ -557,9 +364,9 @@ NTSTATUS GeoIPUpdateThread( // TODO: Update on timer callback. { FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100); - PPH_STRING totalLength = PhFormatSize(contentLength, -1); - PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, -1); - PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1); + PPH_STRING totalLength = PhFormatSize(contentLength, ULONG_MAX); + PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, ULONG_MAX); + PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, ULONG_MAX); PPH_STRING statusMessage = PhFormatString( L"Downloaded: %s of %s (%.0f%%)\r\nSpeed: %s/s", @@ -579,112 +386,125 @@ NTSTATUS GeoIPUpdateThread( } } - PPH_STRING path; - PPH_STRING directory; - PPH_STRING fullSetupPath; - PPH_BYTES mmdbGzPath; - gzFile file; + { + dbpath = NetToolsGetGeoLiteDbPath(); - directory = PH_AUTO(PhGetApplicationDirectory()); - path = PhConcatStrings(2, PhGetString(directory), L"Plugins\\plugindata\\GeoLite2-Country.mmdb"); - mmdbGzPath = PhConvertUtf16ToUtf8(PhGetString(context->SetupFilePath)); + if (PhIsNullOrEmptyString(dbpath)) + goto CleanupExit; - if (RtlDoesFileExists_U(PhGetString(path))) - { - if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(path)))) + if (PhDoesFileExistsWin32(PhGetString(dbpath))) { - goto CleanupExit; + if (!NT_SUCCESS(PhDeleteFileWin32(PhGetString(dbpath)))) + goto CleanupExit; } - } + else + { + PPH_STRING fullPath; + ULONG indexOfFileName; - if (fullSetupPath = PhGetFullPath(PhGetString(path), &indexOfFileName)) - { - PPH_STRING directoryPath; + // Create the directory if it does not exist. + if (fullPath = PhGetFullPath(dbpath->Buffer, &indexOfFileName)) + { + if (indexOfFileName != ULONG_MAX) + PhCreateDirectory(PhaSubstring(fullPath, 0, indexOfFileName)); - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); + PhDereferenceObject(fullPath); + } } - } - if (file = gzopen(mmdbGzPath->Buffer, "rb")) - { - HANDLE mmdbFileHandle; - - if (NT_SUCCESS(PhCreateFileWin32( - &mmdbFileHandle, - PhGetStringOrEmpty(path), - FILE_GENERIC_READ | FILE_GENERIC_WRITE, - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - IO_STATUS_BLOCK isb; - BYTE buffer[PAGE_SIZE]; + mmdbGzPath = PhConvertUtf16ToUtf8(PhGetString(zipFilePath)); - while (!gzeof(file)) + if (gzfile = gzopen(mmdbGzPath->Buffer, "rb")) + { + HANDLE mmdbFileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &mmdbFileHandle, + PhGetStringOrEmpty(dbpath), + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) { - int bytes = gzread(file, buffer, sizeof(buffer)); - - if (bytes == -1) - goto CleanupExit; - - if (!NT_SUCCESS(NtWriteFile( - mmdbFileHandle, - NULL, - NULL, - NULL, - &isb, - buffer, - bytes, - NULL, - NULL - ))) + IO_STATUS_BLOCK isb; + BYTE buffer[PAGE_SIZE]; + + while (!gzeof(gzfile)) { - goto CleanupExit; + INT bytes = gzread(gzfile, buffer, sizeof(buffer)); + + if (bytes == -1) + { + NtClose(mmdbFileHandle); + goto CleanupExit; + } + + if (!NT_SUCCESS(NtWriteFile( + mmdbFileHandle, + NULL, + NULL, + NULL, + &isb, + buffer, + bytes, + NULL, + NULL + ))) + { + NtClose(mmdbFileHandle); + goto CleanupExit; + } } - } - success = TRUE; + success = TRUE; - NtClose(mmdbFileHandle); - } + NtClose(mmdbFileHandle); + } - gzclose(file); + gzclose(gzfile); + gzfile = NULL; + } } } CleanupExit: + if (gzfile) + gzclose(gzfile); + if (mmdbGzPath) + PhDereferenceObject(mmdbGzPath); + if (dbpath) + PhDereferenceObject(dbpath); + if (tempFileHandle) NtClose(tempFileHandle); - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); + if (httpContext) + PhHttpSocketDestroy(httpContext); - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); + if (userAgentString) + PhDereferenceObject(userAgentString); + if (versionString) + PhDereferenceObject(versionString); - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); - - PhClearReference(&randomGuidString); - PhClearReference(&fullSetupPath); - PhClearReference(&setupTempPath); - PhClearReference(&userAgentString); - - if (success) + if (zipFilePath) { - if (context->DialogHandle) - PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0); + PhDeleteCacheFile(zipFilePath); + PhDereferenceObject(zipFilePath); } - else + + if (context->DialogHandle) { - if (context->DialogHandle) - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); + if (success) + { + PostMessage(context->DialogHandle, PH_SHOWINSTALL, 0, 0); + } + else + { + PostMessage(context->DialogHandle, PH_SHOWERROR, 0, 0); + } } PhDereferenceObject(context); @@ -695,21 +515,28 @@ LRESULT CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; + PPH_UPDATER_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hwndDlg, UCHAR_MAX))) + return 0; + + oldWndProc = context->DefaultWindowProc; switch (uMsg) { - case WM_NCDESTROY: + case WM_DESTROY: { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwndDlg, UCHAR_MAX); + + PhUnregisterWindowCallback(hwndDlg); } break; - case WM_SHOWDIALOG: + case PH_SHOWDIALOG: { if (IsMinimized(hwndDlg)) ShowWindow(hwndDlg, SW_RESTORE); @@ -719,29 +546,19 @@ LRESULT CALLBACK TaskDialogSubclassProc( SetForegroundWindow(hwndDlg); } break; - case PH_UPDATESUCCESS: + case PH_SHOWINSTALL: { - ShowInstallRestartDialog(context); + ShowDbInstallRestartDialog(context); } break; - case PH_UPDATEFAILURE: + case PH_SHOWERROR: { - // if ((BOOLEAN)wParam) - // ShowUpdateFailedDialog(context, TRUE, FALSE); - // else if ((BOOLEAN)lParam) - // ShowUpdateFailedDialog(context, FALSE, TRUE); - //else - //ShowUpdateFailedDialog(context, FALSE, FALSE); - } - break; - case PH_UPDATEISERRORED: - { - //ShowUpdateFailedDialog(context, FALSE, FALSE); + ShowDbUpdateFailedDialog(context); } break; } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); } HRESULT CALLBACK TaskDialogBootstrapCallback( @@ -766,10 +583,14 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( // Create the Taskdialog icons TaskDialogCreateIcons(context); - // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); + PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + + // Subclass the Taskdialog. + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, UCHAR_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)TaskDialogSubclassProc); - ShowCheckForUpdatesDialog(context); + ShowDbCheckForUpdatesDialog(context); } break; } @@ -783,25 +604,25 @@ NTSTATUS GeoIPUpdateDialogThread( { PH_AUTO_POOL autoPool; PPH_UPDATER_CONTEXT context; - INT result = 0; TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; PhInitializeAutoPool(&autoPool); - context = (PPH_UPDATER_CONTEXT)PhCreateAlloc(sizeof(PH_UPDATER_CONTEXT)); - memset(context, 0, sizeof(PH_UPDATER_CONTEXT)); + context = CreateUpdateContext(); - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_POSITION_RELATIVE_TO_WINDOW; + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; config.pszContent = L"Initializing..."; config.lpCallbackData = (LONG_PTR)context; config.pfCallback = TaskDialogBootstrapCallback; config.hwndParent = Parameter; - TaskDialogIndirect(&config, &result, NULL, NULL); + TaskDialogIndirect(&config, NULL, NULL, NULL); - FreeUpdateContext(context); + PhDereferenceObject(context); PhDeleteAutoPool(&autoPool); + PhResetEvent(&InitializedEvent); + return STATUS_SUCCESS; //SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; @@ -852,8 +673,16 @@ VOID ShowGeoIPUpdateDialog( _In_opt_ HWND Parent ) { - HANDLE threadHandle = NULL; + if (!UpdateDialogThreadHandle) + { + if (!NT_SUCCESS(PhCreateThreadEx(&UpdateDialogThreadHandle, GeoIPUpdateDialogThread, NULL))) + { + PhShowError(PhMainWndHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&InitializedEvent, NULL); + } - if (threadHandle = PhCreateThread(0, GeoIPUpdateDialogThread, Parent)) - NtClose(threadHandle); -} \ No newline at end of file + PostMessage(UpdateDialogHandle, PH_SHOWDIALOG, 0, 0); +} diff --git a/plugins/NetworkTools/whois.c b/plugins/NetworkTools/whois.c index c14a847273f1..ea6e810c8ebb 100644 --- a/plugins/NetworkTools/whois.c +++ b/plugins/NetworkTools/whois.c @@ -1,601 +1,619 @@ -/* - * Process Hacker Network Tools - - * Whois dialog - * - * Copyright (C) 2013-2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "nettools.h" -#include -#include - -VOID RichEditSetText( - _In_ HWND RichEditHandle, - _In_ PWSTR Text - ) -{ - SendMessage(RichEditHandle, WM_SETREDRAW, FALSE, 0); - - // Update the Richedit text. - SendMessage(RichEditHandle, EM_REPLACESEL, FALSE, (LPARAM)Text); - // NOTE: EM_LINESCROLL and WM_VSCROLL won't update the scroll position - // if the Richedit control doesn't have keyboard focus (e.g. SetFocus). - SendMessage(RichEditHandle, WM_VSCROLL, SB_TOP, 0); - - // Redraw the Richedit with the new text. - SendMessage(RichEditHandle, WM_SETREDRAW, TRUE, 0); - InvalidateRect(RichEditHandle, NULL, FALSE); -} - -PPH_STRING TrimString( - _In_ PPH_STRING String - ) -{ - static PH_STRINGREF whitespace = PH_STRINGREF_INIT(L" "); - PH_STRINGREF sr = String->sr; - PhTrimStringRef(&sr, &whitespace, 0); - return PhCreateString2(&sr); -} - -PPH_STRING TrimString2( - _In_ PPH_STRING String - ) -{ - static PH_STRINGREF whitespace = PH_STRINGREF_INIT(L"\n\n"); - PH_STRINGREF sr = String->sr; - PhTrimStringRef(&sr, &whitespace, 0); - return PhCreateString2(&sr); -} - -BOOLEAN ReadSocketString( - _In_ SOCKET Handle, - _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, - _Out_ ULONG *DataLength - ) -{ - PSTR data; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; - BYTE buffer[PAGE_SIZE]; - - allocatedLength = sizeof(buffer); - data = (PSTR)PhAllocate(allocatedLength); - dataLength = 0; - - // Zero the buffer - memset(buffer, 0, PAGE_SIZE); - - while ((returnLength = recv(Handle, buffer, PAGE_SIZE, 0)) != SOCKET_ERROR) - { - if (returnLength == 0) - break; - - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - // Copy the returned buffer into our pointer - memcpy(data + dataLength, buffer, returnLength); - // Zero the returned buffer for the next loop - //memset(buffer, 0, returnLength); - - dataLength += returnLength; - } - - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - // Ensure that the buffer is null-terminated. - data[dataLength] = 0; - - *DataLength = dataLength; - *Data = data; - - return TRUE; -} - -BOOLEAN WhoisExtractServerUrl( - _In_ PPH_STRING WhoisResponce, - _Out_ PPH_STRING *WhoisServerAddress - ) -{ - ULONG_PTR whoisServerHostnameIndex; - ULONG_PTR whoisServerHostnameLength; - PPH_STRING whoisServerName; - - if ((whoisServerHostnameIndex = PhFindStringInString(WhoisResponce, 0, L"whois:")) == -1) - return FALSE; - if ((whoisServerHostnameLength = PhFindStringInString(WhoisResponce, whoisServerHostnameIndex, L"\n")) == -1) - return FALSE; - if ((whoisServerHostnameLength = whoisServerHostnameLength - whoisServerHostnameIndex) == 0) - return FALSE; - - whoisServerName = PhSubstring( - WhoisResponce, - whoisServerHostnameIndex + wcslen(L"whois:"), - (ULONG)whoisServerHostnameLength - wcslen(L"whois:") - ); - - *WhoisServerAddress = TrimString(whoisServerName); - - PhDereferenceObject(whoisServerName); - return TRUE; -} - -BOOLEAN WhoisExtractReferralServer( - _In_ PPH_STRING WhoisResponce, - _Out_ PPH_STRING *WhoisServerAddress, - _Out_ PPH_STRING *WhoisServerPort - ) -{ - ULONG_PTR whoisServerHostnameIndex; - ULONG_PTR whoisServerHostnameLength; - PPH_STRING whoisServerName; - PPH_STRING whoisServerHostname; - WCHAR urlProtocal[0x100] = L""; - WCHAR urlHost[0x100] = L""; - WCHAR urlPort[0x100] = L""; - WCHAR urlPath[0x100] = L""; - - if ((whoisServerHostnameIndex = PhFindStringInString(WhoisResponce, 0, L"ReferralServer:")) == -1) - return FALSE; - if ((whoisServerHostnameLength = PhFindStringInString(WhoisResponce, whoisServerHostnameIndex, L"\n")) == -1) - return FALSE; - if ((whoisServerHostnameLength = whoisServerHostnameLength - whoisServerHostnameIndex) == 0) - return FALSE; - - whoisServerName = PhSubstring( - WhoisResponce, - whoisServerHostnameIndex + wcslen(L"ReferralServer:"), - (ULONG)whoisServerHostnameLength - wcslen(L"ReferralServer:") - ); - - whoisServerHostname = TrimString(whoisServerName); - - if (swscanf_s( - whoisServerHostname->Buffer, - L"%[^:]://%[^:]:%[^/]/%s", - urlProtocal, - (UINT)ARRAYSIZE(urlProtocal), - urlHost, - (UINT)ARRAYSIZE(urlHost), - urlPort, - (UINT)ARRAYSIZE(urlPort), - urlPath, - (UINT)ARRAYSIZE(urlPath) - )) - { - *WhoisServerAddress = PhCreateString(urlHost); - - if (PhCountStringZ(urlPort) >= 2) - { - *WhoisServerPort = PhCreateString(urlPort); - } - - PhDereferenceObject(whoisServerName); - PhDereferenceObject(whoisServerHostname); - return TRUE; - } - - PhDereferenceObject(whoisServerName); - PhDereferenceObject(whoisServerHostname); - return FALSE; -} - -BOOLEAN WhoisQueryServer( - _In_ PWSTR WhoisServerAddress, - _In_ PWSTR WhoisServerPort, - _In_ PWSTR WhoisQueryAddress, - _In_ PPH_STRING* response - ) -{ - WSADATA winsockStartup; - PADDRINFOW result = NULL; - PADDRINFOW addrInfo = NULL; - ADDRINFOW hints; - ULONG whoisResponceLength = 0; - PSTR whoisResponce = NULL; - PPH_BYTES whoisQuery = NULL; - - if (!WhoisServerPort) - WhoisServerPort = L"43"; - - if (WSAStartup(WINSOCK_VERSION, &winsockStartup) != ERROR_SUCCESS) - return FALSE; - - memset(&hints, 0, sizeof(ADDRINFOW)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - if (GetAddrInfo(WhoisServerAddress, WhoisServerPort, &hints, &result)) - { - WSACleanup(); - return FALSE; - } - - if (PhEqualStringZ(WhoisServerAddress, L"whois.arin.net", TRUE)) - whoisQuery = FormatAnsiString("n %S\r\n", WhoisQueryAddress); - else - whoisQuery = FormatAnsiString("%S\r\n", WhoisQueryAddress); - - for (addrInfo = result; addrInfo; addrInfo = addrInfo->ai_next) - { - SOCKET socketHandle = socket( - addrInfo->ai_family, - addrInfo->ai_socktype, - addrInfo->ai_protocol - ); - - if (socketHandle == INVALID_SOCKET) - continue; - - if (connect(socketHandle, addrInfo->ai_addr, (INT)addrInfo->ai_addrlen) == SOCKET_ERROR) - { - closesocket(socketHandle); - continue; - } - - if (send(socketHandle, whoisQuery->Buffer, (INT)whoisQuery->Length, 0) == SOCKET_ERROR) - { - closesocket(socketHandle); - continue; - } - - if (ReadSocketString(socketHandle, &whoisResponce, &whoisResponceLength)) - { - closesocket(socketHandle); - break; - } - } - - FreeAddrInfo(result); - WSACleanup(); - PhDereferenceObject(whoisQuery); - - if (whoisResponce) - { - *response = PhConvertUtf8ToUtf16(whoisResponce); - return TRUE; - } - - return FALSE; -} - -NTSTATUS NetworkWhoisThreadStart( - _In_ PVOID Parameter - ) -{ - PNETWORK_WHOIS_CONTEXT context = (PNETWORK_WHOIS_CONTEXT)Parameter; - PH_STRING_BUILDER sb; - PPH_STRING whoisResponse = NULL; - PPH_STRING whoisReferralResponse = NULL; - PPH_STRING whoisServerName = NULL; - PPH_STRING whoisReferralServerName = NULL; - PPH_STRING whoisReferralServerPort = NULL; - - PhInitializeStringBuilder(&sb, 0x100); - - if (!WhoisQueryServer(L"whois.iana.org", L"43", context->IpAddressString, &whoisResponse)) - { - PhAppendFormatStringBuilder(&sb, L"Connection to whois.iana.org failed.\n"); - goto CleanupExit; - } - - if (!WhoisExtractServerUrl(whoisResponse, &whoisServerName)) - { - PhAppendFormatStringBuilder(&sb, L"Error parsing whois.iana.org response:\n%s\n", whoisResponse->Buffer); - goto CleanupExit; - } - - if (WhoisQueryServer( - PhGetString(whoisServerName), - L"43", - context->IpAddressString, - &whoisResponse - )) - { - if (WhoisExtractReferralServer( - whoisResponse, - &whoisReferralServerName, - &whoisReferralServerPort - )) - { - PhAppendFormatStringBuilder( - &sb, - L"%s referred the request to: %s\n", - PhGetString(whoisServerName), - PhGetString(whoisReferralServerName) - ); - - if (WhoisQueryServer( - PhGetString(whoisReferralServerName), - PhGetString(whoisReferralServerPort), - context->IpAddressString, - &whoisReferralResponse - )) - { - PhAppendFormatStringBuilder(&sb, L"\n%s\n", PhGetString(whoisReferralResponse)); - PhAppendFormatStringBuilder(&sb, L"\nOriginal request to %s:\n%s\n", PhGetString(whoisServerName), PhGetString(whoisResponse)); - PostMessage(context->WindowHandle, NTM_RECEIVEDWHOIS, 0, (LPARAM)PhFinalStringBuilderString(&sb)); - goto CleanupExit; - } - } - } - - PhAppendFormatStringBuilder(&sb, L"\n%s", PhGetString(whoisResponse)); - - PostMessage(context->WindowHandle, NTM_RECEIVEDWHOIS, 0, (LPARAM)PhFinalStringBuilderString(&sb)); - -CleanupExit: - PhClearReference(&whoisResponse); - PhClearReference(&whoisReferralResponse); - PhClearReference(&whoisServerName); - PhClearReference(&whoisReferralServerName); - PhClearReference(&whoisReferralServerPort); - - return STATUS_SUCCESS; -} - -INT_PTR CALLBACK NetworkOutputDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PNETWORK_WHOIS_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PNETWORK_WHOIS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PNETWORK_WHOIS_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - { - PhSaveWindowPlacementToSetting(SETTING_NAME_WHOIS_WINDOW_POSITION, SETTING_NAME_WHOIS_WINDOW_SIZE, hwndDlg); - PhDeleteLayoutManager(&context->LayoutManager); - RemoveProp(hwndDlg, L"Context"); - PhDereferenceObject(context); - - PostQuitMessage(0); - } - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HANDLE dialogThread; - - SetWindowText(hwndDlg, PhaFormatString(L"Whois %s...", context->IpAddressString)->Buffer); - PhCenterWindow(hwndDlg, PhMainWndHandle); - - context->WindowHandle = hwndDlg; - context->RichEditHandle = GetDlgItem(hwndDlg, IDC_NETOUTPUTEDIT); - - // Reset the border style for richedit uxtheme borders - PhSetWindowStyle(context->RichEditHandle, WS_BORDER, WS_BORDER); - PhSetWindowExStyle(context->RichEditHandle, WS_EX_CLIENTEDGE, ~WS_EX_CLIENTEDGE); - //SendMessage(context->OutputHandle, EM_SETBKGNDCOLOR, RGB(0, 0, 0), 0); - SendMessage(context->RichEditHandle, EM_SETEVENTMASK, 0, SendMessage(context->RichEditHandle, EM_GETEVENTMASK, 0, 0) | ENM_LINK); - SendMessage(context->RichEditHandle, EM_AUTOURLDETECT, AURL_ENABLEURL, 0); - SendMessage(context->RichEditHandle, EM_SETWORDWRAPMODE, WBF_WORDWRAP, 0); - context->FontHandle = CommonCreateFont(-11, FW_MEDIUM, context->RichEditHandle); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, context->RichEditHandle, NULL, PH_ANCHOR_ALL); - PhLoadWindowPlacementFromSetting(SETTING_NAME_WHOIS_WINDOW_POSITION, SETTING_NAME_WHOIS_WINDOW_SIZE, hwndDlg); - - if (dialogThread = PhCreateThread(0, NetworkWhoisThreadStart, (PVOID)context)) - NtClose(dialogThread); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDCANCEL: - case IDOK: - DestroyWindow(hwndDlg); - break; - } - } - break; - case WM_SIZE: - PhLayoutManagerLayout(&context->LayoutManager); - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case EN_LINK: - { - ENLINK* link = (ENLINK*)lParam; - - if (link->msg == WM_LBUTTONUP) - { - ULONG length; - PWSTR buffer; - TEXTRANGE textRange; - - length = (link->chrg.cpMax - link->chrg.cpMin) * sizeof(WCHAR); - buffer = PhAllocate(length + 1); - memset(buffer, 0, length + 1); - - textRange.chrg = link->chrg; - textRange.lpstrText = buffer; - - if (SendMessage(context->RichEditHandle, EM_GETTEXTRANGE, 0, (LPARAM)&textRange)) - { - if (PhCountStringZ(buffer) > 4) - { - PhShellExecute(context->WindowHandle, buffer, NULL); - } - } - - PhFree(buffer); - } - } - break; - } - } - break; - case NTM_RECEIVEDWHOIS: - { - PPH_STRING whoisString = PH_AUTO((PPH_STRING)lParam); - PPH_STRING trimString = PH_AUTO(TrimString2(whoisString)); - - RichEditSetText(context->RichEditHandle, trimString->Buffer); - } - break; - case WM_TRACERT_UPDATE: - { - PPH_STRING windowText = PH_AUTO(PhGetWindowText(context->WindowHandle)); - - if (windowText) - { - Static_SetText(context->WindowHandle, - PhaFormatString(L"%s Finished.", windowText->Buffer)->Buffer); - } - } - break; - } - - return FALSE; -} - -NTSTATUS NetworkWhoisDialogThreadStart( - _In_ PVOID Parameter - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - BOOL result; - MSG message; - HWND windowHandle; - PH_AUTO_POOL autoPool; - - if (PhBeginInitOnce(&initOnce)) - { - LoadLibrary(L"msftedit.dll"); - PhEndInitOnce(&initOnce); - } - - PhInitializeAutoPool(&autoPool); - - windowHandle = CreateDialogParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_WHOIS), - NULL, - NetworkOutputDlgProc, - (LPARAM)Parameter - ); - - ShowWindow(windowHandle, SW_SHOW); - SetForegroundWindow(windowHandle); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (!IsDialogMessage(windowHandle, &message)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - - -VOID ShowWhoisWindow( - _In_ PPH_NETWORK_ITEM NetworkItem - ) -{ - HANDLE dialogThread; - PNETWORK_WHOIS_CONTEXT context; - - context = (PNETWORK_WHOIS_CONTEXT)PhCreateAlloc(sizeof(NETWORK_WHOIS_CONTEXT)); - memset(context, 0, sizeof(NETWORK_WHOIS_CONTEXT)); - - context->RemoteEndpoint = NetworkItem->RemoteEndpoint; - - if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - RtlIpv4AddressToString(&NetworkItem->RemoteEndpoint.Address.InAddr, context->IpAddressString); - } - else if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); - } - - if (dialogThread = PhCreateThread(0, NetworkWhoisDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } -} - -VOID ShowWhoisWindowFromAddress( - _In_ PH_IP_ENDPOINT RemoteEndpoint - ) -{ - HANDLE dialogThread; - PNETWORK_WHOIS_CONTEXT context; - - context = (PNETWORK_WHOIS_CONTEXT)PhCreateAlloc(sizeof(NETWORK_WHOIS_CONTEXT)); - memset(context, 0, sizeof(NETWORK_WHOIS_CONTEXT)); - - context->RemoteEndpoint = RemoteEndpoint; - - if (RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) - { - RtlIpv4AddressToString(&RemoteEndpoint.Address.InAddr, context->IpAddressString); - } - else if (RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) - { - RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); - } - - if (dialogThread = PhCreateThread(0, NetworkWhoisDialogThreadStart, (PVOID)context)) - { - NtClose(dialogThread); - } -} \ No newline at end of file +/* + * Process Hacker Network Tools - + * Whois dialog + * + * Copyright (C) 2013-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "nettools.h" +#include +#include + +VOID RichEditSetText( + _In_ HWND RichEditHandle, + _In_ PWSTR Text + ) +{ + if (!!PhGetIntegerSetting(L"EnableThemeSupport")) + { + CHARFORMAT cf; + + memset(&cf, 0, sizeof(CHARFORMAT)); + cf.cbSize = sizeof(CHARFORMAT); + cf.dwMask = CFM_COLOR; + + switch (PhGetIntegerSetting(L"GraphColorMode")) + { + case 0: // New colors + cf.crTextColor = RGB(0x0, 0x0, 0x0); + break; + case 1: // Old colors + cf.crTextColor = RGB(0xff, 0xff, 0xff); + break; + } + + SendMessage(RichEditHandle, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf); + } + + SetFocus(RichEditHandle); + SendMessage(RichEditHandle, WM_SETREDRAW, FALSE, 0); + SendMessage(RichEditHandle, EM_SETSEL, -2, -1); + SendMessage(RichEditHandle, EM_REPLACESEL, FALSE, (LPARAM)Text); + SendMessage(RichEditHandle, WM_VSCROLL, SB_TOP, 0); // requires SetFocus() + SendMessage(RichEditHandle, WM_SETREDRAW, TRUE, 0); + + InvalidateRect(RichEditHandle, NULL, FALSE); +} + +PPH_STRING TrimString( + _In_ PPH_STRING String + ) +{ + static PH_STRINGREF whitespace = PH_STRINGREF_INIT(L" "); + PH_STRINGREF sr = String->sr; + PhTrimStringRef(&sr, &whitespace, 0); + return PhCreateString2(&sr); +} + +PPH_STRING TrimString2( + _In_ PPH_STRING String + ) +{ + static PH_STRINGREF whitespace = PH_STRINGREF_INIT(L"\n\n"); + PH_STRINGREF sr = String->sr; + PhTrimStringRef(&sr, &whitespace, 0); + return PhCreateString2(&sr); +} + +BOOLEAN ReadSocketString( + _In_ SOCKET Handle, + _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, + _Out_ ULONG *DataLength + ) +{ + PSTR data; + ULONG allocatedLength; + ULONG dataLength; + ULONG returnLength; + BYTE buffer[PAGE_SIZE]; + + allocatedLength = sizeof(buffer); + data = (PSTR)PhAllocate(allocatedLength); + dataLength = 0; + + // Zero the buffer + memset(buffer, 0, PAGE_SIZE); + + while ((returnLength = recv(Handle, buffer, PAGE_SIZE, 0)) != SOCKET_ERROR) + { + if (returnLength == 0) + break; + + if (allocatedLength < dataLength + returnLength) + { + allocatedLength *= 2; + data = (PSTR)PhReAllocate(data, allocatedLength); + } + + // Copy the returned buffer into our pointer + memcpy(data + dataLength, buffer, returnLength); + // Zero the returned buffer for the next loop + //memset(buffer, 0, returnLength); + + dataLength += returnLength; + } + + if (allocatedLength < dataLength + 1) + { + allocatedLength++; + data = (PSTR)PhReAllocate(data, allocatedLength); + } + + // Ensure that the buffer is null-terminated. + data[dataLength] = 0; + + *DataLength = dataLength; + *Data = data; + + return TRUE; +} + +BOOLEAN WhoisExtractServerUrl( + _In_ PPH_STRING WhoisResponce, + _Out_ PPH_STRING *WhoisServerAddress + ) +{ + ULONG_PTR whoisServerHostnameIndex; + ULONG_PTR whoisServerHostnameLength; + PPH_STRING whoisServerName; + + if ((whoisServerHostnameIndex = PhFindStringInString(WhoisResponce, 0, L"whois:")) == -1) + return FALSE; + if ((whoisServerHostnameLength = PhFindStringInString(WhoisResponce, whoisServerHostnameIndex, L"\n")) == -1) + return FALSE; + if ((whoisServerHostnameLength = whoisServerHostnameLength - whoisServerHostnameIndex) == 0) + return FALSE; + + whoisServerName = PhSubstring( + WhoisResponce, + whoisServerHostnameIndex + wcslen(L"whois:"), + (ULONG)whoisServerHostnameLength - wcslen(L"whois:") + ); + + *WhoisServerAddress = TrimString(whoisServerName); + + PhDereferenceObject(whoisServerName); + return TRUE; +} + +BOOLEAN WhoisExtractReferralServer( + _In_ PPH_STRING WhoisResponce, + _Out_ PPH_STRING *WhoisServerAddress, + _Out_ PPH_STRING *WhoisServerPort + ) +{ + ULONG_PTR whoisServerHostnameIndex; + ULONG_PTR whoisServerHostnameLength; + PPH_STRING whoisServerName; + PPH_STRING whoisServerHostname; + WCHAR urlProtocal[0x100] = L""; + WCHAR urlHost[0x100] = L""; + WCHAR urlPort[0x100] = L""; + WCHAR urlPath[0x100] = L""; + + if ((whoisServerHostnameIndex = PhFindStringInString(WhoisResponce, 0, L"ReferralServer:")) == -1) + return FALSE; + if ((whoisServerHostnameLength = PhFindStringInString(WhoisResponce, whoisServerHostnameIndex, L"\n")) == -1) + return FALSE; + if ((whoisServerHostnameLength = whoisServerHostnameLength - whoisServerHostnameIndex) == 0) + return FALSE; + + whoisServerName = PhSubstring( + WhoisResponce, + whoisServerHostnameIndex + wcslen(L"ReferralServer:"), + (ULONG)whoisServerHostnameLength - wcslen(L"ReferralServer:") + ); + + whoisServerHostname = TrimString(whoisServerName); + + if (swscanf_s( + whoisServerHostname->Buffer, + L"%[^:]://%[^:]:%[^/]/%s", + urlProtocal, + (UINT)ARRAYSIZE(urlProtocal), + urlHost, + (UINT)ARRAYSIZE(urlHost), + urlPort, + (UINT)ARRAYSIZE(urlPort), + urlPath, + (UINT)ARRAYSIZE(urlPath) + )) + { + *WhoisServerAddress = PhCreateString(urlHost); + + if (PhCountStringZ(urlPort) >= 2) + { + *WhoisServerPort = PhCreateString(urlPort); + } + + PhDereferenceObject(whoisServerName); + PhDereferenceObject(whoisServerHostname); + return TRUE; + } + + PhDereferenceObject(whoisServerName); + PhDereferenceObject(whoisServerHostname); + return FALSE; +} + +BOOLEAN WhoisQueryServer( + _In_ PWSTR WhoisServerAddress, + _In_ PWSTR WhoisServerPort, + _In_ PWSTR WhoisQueryAddress, + _In_ PPH_STRING* response + ) +{ + WSADATA winsockStartup; + PADDRINFOW result = NULL; + PADDRINFOW addrInfo = NULL; + ADDRINFOW hints; + ULONG whoisResponceLength = 0; + PSTR whoisResponce = NULL; + PPH_BYTES whoisQuery = NULL; + + if (!WhoisServerPort) + WhoisServerPort = L"43"; + + if (WSAStartup(WINSOCK_VERSION, &winsockStartup) != ERROR_SUCCESS) + return FALSE; + + memset(&hints, 0, sizeof(ADDRINFOW)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (GetAddrInfo(WhoisServerAddress, WhoisServerPort, &hints, &result)) + { + WSACleanup(); + return FALSE; + } + + if (PhEqualStringZ(WhoisServerAddress, L"whois.arin.net", TRUE)) + whoisQuery = FormatAnsiString("n %S\r\n", WhoisQueryAddress); + else + whoisQuery = FormatAnsiString("%S\r\n", WhoisQueryAddress); + + for (addrInfo = result; addrInfo; addrInfo = addrInfo->ai_next) + { + SOCKET socketHandle = socket( + addrInfo->ai_family, + addrInfo->ai_socktype, + addrInfo->ai_protocol + ); + + if (socketHandle == INVALID_SOCKET) + continue; + + if (connect(socketHandle, addrInfo->ai_addr, (INT)addrInfo->ai_addrlen) == SOCKET_ERROR) + { + closesocket(socketHandle); + continue; + } + + if (send(socketHandle, whoisQuery->Buffer, (INT)whoisQuery->Length, 0) == SOCKET_ERROR) + { + closesocket(socketHandle); + continue; + } + + if (ReadSocketString(socketHandle, &whoisResponce, &whoisResponceLength)) + { + closesocket(socketHandle); + break; + } + } + + FreeAddrInfo(result); + WSACleanup(); + PhDereferenceObject(whoisQuery); + + if (whoisResponce) + { + *response = PhConvertUtf8ToUtf16(whoisResponce); + return TRUE; + } + + return FALSE; +} + +NTSTATUS NetworkWhoisThreadStart( + _In_ PVOID Parameter + ) +{ + PNETWORK_WHOIS_CONTEXT context = (PNETWORK_WHOIS_CONTEXT)Parameter; + PH_STRING_BUILDER sb; + PPH_STRING whoisResponse = NULL; + PPH_STRING whoisReferralResponse = NULL; + PPH_STRING whoisServerName = NULL; + PPH_STRING whoisReferralServerName = NULL; + PPH_STRING whoisReferralServerPort = NULL; + + PhInitializeStringBuilder(&sb, 0x100); + + if (!WhoisQueryServer(L"whois.iana.org", L"43", context->IpAddressString, &whoisResponse)) + { + PhAppendFormatStringBuilder(&sb, L"Connection to whois.iana.org failed.\n"); + goto CleanupExit; + } + + if (!WhoisExtractServerUrl(whoisResponse, &whoisServerName)) + { + PhAppendFormatStringBuilder(&sb, L"Error parsing whois.iana.org response:\n%s\n", whoisResponse->Buffer); + goto CleanupExit; + } + + if (WhoisQueryServer( + PhGetString(whoisServerName), + L"43", + context->IpAddressString, + &whoisResponse + )) + { + if (WhoisExtractReferralServer( + whoisResponse, + &whoisReferralServerName, + &whoisReferralServerPort + )) + { + PhAppendFormatStringBuilder( + &sb, + L"%s referred the request to: %s\n", + PhGetString(whoisServerName), + PhGetString(whoisReferralServerName) + ); + + if (WhoisQueryServer( + PhGetString(whoisReferralServerName), + PhGetString(whoisReferralServerPort), + context->IpAddressString, + &whoisReferralResponse + )) + { + PhAppendFormatStringBuilder(&sb, L"\n%s\n", PhGetString(whoisReferralResponse)); + PhAppendFormatStringBuilder(&sb, L"\nOriginal request to %s:\n%s\n", PhGetString(whoisServerName), PhGetString(whoisResponse)); + PostMessage(context->WindowHandle, NTM_RECEIVEDWHOIS, 0, (LPARAM)PhFinalStringBuilderString(&sb)); + goto CleanupExit; + } + } + } + + PhAppendFormatStringBuilder(&sb, L"\n%s", PhGetString(whoisResponse)); + + PostMessage(context->WindowHandle, NTM_RECEIVEDWHOIS, 0, (LPARAM)PhFinalStringBuilderString(&sb)); + +CleanupExit: + PhClearReference(&whoisResponse); + PhClearReference(&whoisReferralResponse); + PhClearReference(&whoisServerName); + PhClearReference(&whoisReferralServerName); + PhClearReference(&whoisReferralServerPort); + + return STATUS_SUCCESS; +} + +INT_PTR CALLBACK NetworkOutputDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PNETWORK_WHOIS_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PNETWORK_WHOIS_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + { + PhSaveWindowPlacementToSetting(SETTING_NAME_WHOIS_WINDOW_POSITION, SETTING_NAME_WHOIS_WINDOW_SIZE, hwndDlg); + PhDeleteLayoutManager(&context->LayoutManager); + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhDereferenceObject(context); + + PostQuitMessage(0); + } + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + SetWindowText(hwndDlg, PhaFormatString(L"Whois %s...", context->IpAddressString)->Buffer); + + context->WindowHandle = hwndDlg; + context->RichEditHandle = GetDlgItem(hwndDlg, IDC_NETOUTPUTEDIT); + + //SendMessage(context->RichEditHandle, EM_SETBKGNDCOLOR, RGB(0, 0, 0), 0); + SendMessage(context->RichEditHandle, EM_SETEVENTMASK, 0, SendMessage(context->RichEditHandle, EM_GETEVENTMASK, 0, 0) | ENM_LINK); + SendMessage(context->RichEditHandle, EM_AUTOURLDETECT, AURL_ENABLEURL, 0); + SendMessage(context->RichEditHandle, EM_SETWORDWRAPMODE, WBF_WORDWRAP, 0); + context->FontHandle = PhCreateCommonFont(-11, FW_MEDIUM, context->RichEditHandle); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, context->RichEditHandle, NULL, PH_ANCHOR_ALL); + + if (PhGetIntegerPairSetting(SETTING_NAME_WHOIS_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_WHOIS_WINDOW_POSITION, SETTING_NAME_WHOIS_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, PhMainWndHandle); + + PhCreateThread2(NetworkWhoisThreadStart, (PVOID)context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + case IDOK: + DestroyWindow(hwndDlg); + break; + } + } + break; + case WM_SIZE: + PhLayoutManagerLayout(&context->LayoutManager); + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case EN_LINK: + { + ENLINK* link = (ENLINK*)lParam; + + if (link->msg == WM_LBUTTONUP) + { + ULONG length; + PWSTR buffer; + TEXTRANGE textRange; + + length = (link->chrg.cpMax - link->chrg.cpMin) * sizeof(WCHAR); + buffer = PhAllocate(length + 1); + memset(buffer, 0, length + 1); + + textRange.chrg = link->chrg; + textRange.lpstrText = buffer; + + if (SendMessage(context->RichEditHandle, EM_GETTEXTRANGE, 0, (LPARAM)&textRange)) + { + if (PhCountStringZ(buffer) > 4) + { + PhShellExecute(context->WindowHandle, buffer, NULL); + } + } + + PhFree(buffer); + } + } + break; + } + } + break; + case NTM_RECEIVEDWHOIS: + { + PPH_STRING whoisString = PH_AUTO((PPH_STRING)lParam); + PPH_STRING trimString = PH_AUTO(TrimString2(whoisString)); + + RichEditSetText(context->RichEditHandle, trimString->Buffer); + } + break; + case WM_TRACERT_UPDATE: + { + PPH_STRING windowText = PH_AUTO(PhGetWindowText(context->WindowHandle)); + + if (windowText) + { + Static_SetText(context->WindowHandle, + PhaFormatString(L"%s Finished.", windowText->Buffer)->Buffer); + } + } + break; + } + + return FALSE; +} + +NTSTATUS NetworkWhoisDialogThreadStart( + _In_ PVOID Parameter + ) +{ + static PH_INITONCE initOnce = PH_INITONCE_INIT; + BOOL result; + MSG message; + HWND windowHandle; + PH_AUTO_POOL autoPool; + + if (PhBeginInitOnce(&initOnce)) + { + LoadLibrary(L"msftedit.dll"); + PhEndInitOnce(&initOnce); + } + + PhInitializeAutoPool(&autoPool); + + windowHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_WHOIS), + NULL, + NetworkOutputDlgProc, + (LPARAM)Parameter + ); + + ShowWindow(windowHandle, SW_SHOW); + SetForegroundWindow(windowHandle); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(windowHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + + +VOID ShowWhoisWindow( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PNETWORK_WHOIS_CONTEXT context; + + context = (PNETWORK_WHOIS_CONTEXT)PhCreateAlloc(sizeof(NETWORK_WHOIS_CONTEXT)); + memset(context, 0, sizeof(NETWORK_WHOIS_CONTEXT)); + + RtlCopyMemory( + &context->RemoteEndpoint, + &NetworkItem->RemoteEndpoint, + sizeof(PH_IP_ENDPOINT) + ); + + if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + RtlIpv4AddressToString(&NetworkItem->RemoteEndpoint.Address.InAddr, context->IpAddressString); + } + else if (NetworkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + RtlIpv6AddressToString(&NetworkItem->RemoteEndpoint.Address.In6Addr, context->IpAddressString); + } + + PhCreateThread2(NetworkWhoisDialogThreadStart, (PVOID)context); +} + +VOID ShowWhoisWindowFromAddress( + _In_ PH_IP_ENDPOINT RemoteEndpoint + ) +{ + PNETWORK_WHOIS_CONTEXT context; + + context = (PNETWORK_WHOIS_CONTEXT)PhCreateAlloc(sizeof(NETWORK_WHOIS_CONTEXT)); + memset(context, 0, sizeof(NETWORK_WHOIS_CONTEXT)); + + RtlCopyMemory( + &context->RemoteEndpoint, + &RemoteEndpoint, + sizeof(PH_IP_ENDPOINT) + ); + + if (RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE) + { + RtlIpv4AddressToString(&RemoteEndpoint.Address.InAddr, context->IpAddressString); + } + else if (RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE) + { + RtlIpv6AddressToString(&RemoteEndpoint.Address.In6Addr, context->IpAddressString); + } + + PhCreateThread2(NetworkWhoisDialogThreadStart, (PVOID)context); +} diff --git a/plugins/OnlineChecks/CHANGELOG.txt b/plugins/OnlineChecks/CHANGELOG.txt index 2f8dfe8fe76d..0cd0f495f4f0 100644 --- a/plugins/OnlineChecks/CHANGELOG.txt +++ b/plugins/OnlineChecks/CHANGELOG.txt @@ -1,29 +1,29 @@ -1.8 - * Added VirusTotal process column - * Removed Comodo CAMAS uploader - -1.7 - * Fixed virusscan.jotti.org uploader - -1.6 - * Updated VirusTotal executable limit to 128 MB - -1.5 - * Added CIMA hash checking - * Added file analyzed prompt - -1.4 - * Added upload progress - * Updated UI - -1.3 - * Updated VirusTotal uploader and added hash checking - -1.2 - * Added Comodo Instant Malware Analysis - -1.1 - * Updated VirusTotal uploader - -1.0 - * Initial release +1.8 + * Added VirusTotal process column + * Removed Comodo CAMAS uploader + +1.7 + * Fixed virusscan.jotti.org uploader + +1.6 + * Updated VirusTotal executable limit to 128 MB + +1.5 + * Added CIMA hash checking + * Added file analyzed prompt + +1.4 + * Added upload progress + * Updated UI + +1.3 + * Updated VirusTotal uploader and added hash checking + +1.2 + * Added Comodo Instant Malware Analysis + +1.1 + * Updated VirusTotal uploader + +1.0 + * Initial release diff --git a/plugins/OnlineChecks/OnlineChecks.rc b/plugins/OnlineChecks/OnlineChecks.rc index b1a0e7c346e2..e5f2b6e780c4 100644 --- a/plugins/OnlineChecks/OnlineChecks.rc +++ b/plugins/OnlineChecks/OnlineChecks.rc @@ -1,151 +1,172 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,8,0,0 - PRODUCTVERSION 1,8,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "dmex" - VALUE "FileDescription", "Online Checks plugin for Process Hacker" - VALUE "FileVersion", "1.8" - VALUE "InternalName", "ProcessHacker.OnlineChecks" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "OnlineChecks.dll" - VALUE "ProductName", "Online Checks plugin for Process Hacker" - VALUE "ProductVersion", "1.8" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_OPTIONS DIALOGEX 0, 0, 185, 103 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Enable VirusTotal scanning",IDC_ENABLE_VIRUSTOTAL, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,101,10 - CONTROL "Enable VirusTotal detection highlighting",IDC_ENABLE_IDC_ENABLE_VIRUSTOTAL_HIGHLIGHT, - "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,20,141,10 - PUSHBUTTON "Close",IDCANCEL,129,82,50,14 - CONTROL "Enable detection actions",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,34,94,10 - EDITTEXT IDC_EDIT1,91,47,37,14,ES_AUTOHSCROLL | WS_DISABLED - LTEXT "Minimum detection ratio:",IDC_STATIC,8,50,79,8 - COMBOBOX IDC_COMBO1,91,65,88,14,CBS_DROPDOWN | CBS_SORT | WS_DISABLED | WS_VSCROLL | WS_TABSTOP - LTEXT "Minimum detection action:",IDC_STATIC,7,67,84,8 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 96 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,8,0,0 + PRODUCTVERSION 1,8,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "dmex" + VALUE "FileDescription", "Online Checks plugin for Process Hacker" + VALUE "FileVersion", "1.8" + VALUE "InternalName", "ProcessHacker.OnlineChecks" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "OnlineChecks.dll" + VALUE "ProductName", "Online Checks plugin for Process Hacker" + VALUE "ProductVersion", "1.8" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOGEX 0, 0, 185, 87 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Enable VirusTotal scanning",IDC_ENABLE_VIRUSTOTAL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,101,10 + CONTROL "Enable VirusTotal detection highlighting",IDC_ENABLE_IDC_ENABLE_VIRUSTOTAL_HIGHLIGHT, + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,20,141,10 + CONTROL "Enable detection actions",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,34,94,10 + EDITTEXT IDC_EDIT1,91,47,37,14,ES_AUTOHSCROLL | WS_DISABLED + LTEXT "Minimum detection ratio:",IDC_STATIC,8,50,79,8 + COMBOBOX IDC_COMBO1,91,65,88,14,CBS_DROPDOWN | CBS_SORT | WS_DISABLED | WS_VSCROLL | WS_TABSTOP + LTEXT "Minimum detection action:",IDC_STATIC,7,67,84,8 +END + +IDD_VIRUSTOTAL DIALOGEX 0, 0, 259, 261 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "VirusTotal" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Refresh",IDC_REFRESH,207,245,50,14 + CONTROL "",IDC_SCANRESULTS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,2,255,241 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 80 + END + + IDD_VIRUSTOTAL, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 257 + TOPMARGIN, 2 + BOTTOMMARGIN, 259 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_OPTIONS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_VIRUSTOTAL AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/OnlineChecks/OnlineChecks.vcxproj b/plugins/OnlineChecks/OnlineChecks.vcxproj index f5190e231071..fef273413f95 100644 --- a/plugins/OnlineChecks/OnlineChecks.vcxproj +++ b/plugins/OnlineChecks/OnlineChecks.vcxproj @@ -1,101 +1,104 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {79D24223-1122-40A9-BC8F-46A2089FE089} - OnlineChecks - Win32Proj - OnlineChecks - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - winhttp.lib;%(AdditionalDependencies) - winhttp.dll;%(DelayLoadDLLs) - - - - - winhttp.lib;%(AdditionalDependencies) - winhttp.dll;%(DelayLoadDLLs) - - - - - winhttp.lib;%(AdditionalDependencies) - winhttp.dll;%(DelayLoadDLLs) - - - - - winhttp.lib;%(AdditionalDependencies) - winhttp.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {79D24223-1122-40A9-BC8F-46A2089FE089} + OnlineChecks + Win32Proj + OnlineChecks + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + + + + winhttp.lib;%(AdditionalDependencies) + comctl32.dll;ole32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + winhttp.lib;%(AdditionalDependencies) + comctl32.dll;ole32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + winhttp.lib;%(AdditionalDependencies) + comctl32.dll;ole32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + winhttp.lib;%(AdditionalDependencies) + comctl32.dll;ole32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/OnlineChecks/db.c b/plugins/OnlineChecks/db.c index 093578f518df..906cf092be13 100644 --- a/plugins/OnlineChecks/db.c +++ b/plugins/OnlineChecks/db.c @@ -24,6 +24,9 @@ PPH_HASHTABLE ProcessObjectDb; PH_QUEUED_LOCK ProcessObjectDbLock = PH_QUEUED_LOCK_INIT; +PH_STRINGREF ProcessObjectDbHash = PH_STRINGREF_INIT(L"386f3b6b3f6c35346c69346c6b343d69396b6b3468386b683d383d356b3e383e38356b343f69393b683d3b3a39386b3c6b3a3a3e696835696e686f6b38683e6e"); +PH_STRINGREF ServiceObjectDbHash = PH_STRINGREF_INIT(L"39666e39663d6e66356e3935666a66626e6e627e6e35623d6a3d6a3d6e6e6e6a6a6a35396a6a3d6e7a357e7e7a35626a663d6e7a3d6a3d6e7a397e3d3d6e6e7e"); +PH_STRINGREF NetworkObjectDbHash = PH_STRINGREF_INIT(L"6e61653c676065676b6b7a393d6a66357a396a6e6e66397a35"); BOOLEAN NTAPI ProcessObjectDbEqualFunction( _In_ PVOID Entry1, @@ -107,8 +110,8 @@ PPROCESS_DB_OBJECT FindProcessDbObject( PPROCESS_DB_OBJECT CreateProcessDbObject( _In_ PPH_STRING FileName, _In_opt_ INT64 Positives, - _In_opt_ PPH_STRING Hash, - _In_opt_ PPH_STRING Result + _In_opt_ INT64 Total, + _In_opt_ PPH_STRING Hash ) { PPROCESS_DB_OBJECT object; @@ -121,10 +124,9 @@ PPROCESS_DB_OBJECT CreateProcessDbObject( PhInitializeStringRefLongHint(&object->FileName, FileName->Buffer); PhReferenceObject(FileName); - PhReferenceObject(Result); object->Positives = Positives; object->Hash = FileName; - object->Result = Result; + object->Result = PhFormatString(L"%lu | %lu", Positives, Total); realObject = PhAddEntryHashtableEx(ProcessObjectDb, &object, &added); diff --git a/plugins/OnlineChecks/db.h b/plugins/OnlineChecks/db.h index 194762118818..f1f4c0464012 100644 --- a/plugins/OnlineChecks/db.h +++ b/plugins/OnlineChecks/db.h @@ -2,7 +2,7 @@ * Process Hacker Online Checks - * database functions * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2017 dmex * * This file is part of Process Hacker. * @@ -23,6 +23,9 @@ #ifndef DB_H #define DB_H +extern PH_STRINGREF ProcessObjectDbHash; +extern PH_STRINGREF ServiceObjectDbHash; + typedef struct _PROCESS_DB_OBJECT { PH_STRINGREF FileName; @@ -55,8 +58,8 @@ PPROCESS_DB_OBJECT FindProcessDbObject( PPROCESS_DB_OBJECT CreateProcessDbObject( _In_ PPH_STRING FileName, _In_opt_ INT64 Positives, - _In_opt_ PPH_STRING Hash, - _In_opt_ PPH_STRING Result + _In_opt_ INT64 Total, + _In_opt_ PPH_STRING Hash ); #endif diff --git a/plugins/OnlineChecks/main.c b/plugins/OnlineChecks/main.c index eeb741df6887..27bdf065fb31 100644 --- a/plugins/OnlineChecks/main.c +++ b/plugins/OnlineChecks/main.c @@ -1,853 +1,875 @@ -/* - * Process Hacker Online Checks - - * Main Program - * - * Copyright (C) 2010-2013 wj32 - * Copyright (C) 2012-2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "onlnchk.h" -#include "db.h" - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessHighlightingColorCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ModuleMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ServiceMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ModulesTreeNewInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; - -BOOLEAN VirusTotalScanningEnabled = FALSE; -ULONG ProcessesUpdatedCount = 0; -LIST_ENTRY ProcessListHead = { &ProcessListHead, &ProcessListHead }; -PH_QUEUED_LOCK ProcessesListLock = PH_QUEUED_LOCK_INIT; - -VOID ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ -#ifdef VIRUSTOTAL_API - static ULONG ProcessesUpdatedCount = 0; - PLIST_ENTRY listEntry; - - if (!VirusTotalScanningEnabled) - return; - - ProcessesUpdatedCount++; - - if (ProcessesUpdatedCount < 2) - return; - - listEntry = ProcessListHead.Flink; - - while (listEntry != &ProcessListHead) - { - PPROCESS_EXTENSION extension; - PPH_STRING filePath = NULL; - - extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); - - if (extension->ProcessItem) - { - filePath = extension->ProcessItem->FileName; - } - else if (extension->ModuleItem) - { - filePath = extension->ModuleItem->FileName; - } - else if (extension->ServiceItem) - { - if (extension->FilePath) - { - filePath = extension->FilePath; - } - else - { - PPH_STRING serviceFileName = NULL; - PPH_STRING serviceBinaryPath = NULL; - - if (NT_SUCCESS(QueryServiceFileName( - &extension->ServiceItem->Name->sr, - &serviceFileName, - &serviceBinaryPath - ))) - { - PhMoveReference(&extension->FilePath, serviceFileName); - if (serviceBinaryPath) PhDereferenceObject(serviceBinaryPath); - } - - filePath = extension->FilePath; - } - } - - if (!PhIsNullOrEmptyString(filePath)) - { - if (!extension->ResultValid) - { - PPROCESS_DB_OBJECT object; - - if (object = FindProcessDbObject(&filePath->sr)) - { - extension->Stage1 = TRUE; - extension->ResultValid = TRUE; - - extension->Positives = object->Positives; - PhSwapReference(&extension->VirusTotalResult, object->Result); - } - } - - if (!extension->Stage1) - { - if (!VirusTotalGetCachedResult(filePath)) - { - VirusTotalAddCacheResult(filePath, extension); - } - - extension->Stage1 = TRUE; - } - } - - listEntry = listEntry->Flink; - } -#endif -} - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (VirusTotalScanningEnabled = !!PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED)) - { - InitializeProcessDb(); - InitializeVirusTotalProcessMonitor(); - } -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ShowOptionsDialog((HWND)Parameter); -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case ENABLE_SERVICE_VIRUSTOTAL: - { - ULONG scanningEnabled = !VirusTotalScanningEnabled; - - PhSetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, scanningEnabled); - - if (VirusTotalScanningEnabled != scanningEnabled) - { - INT result = IDOK; - TASKDIALOGCONFIG config; - - memset(&config, 0, sizeof(TASKDIALOGCONFIG)); - config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION; - config.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; - config.hwndParent = menuItem->OwnerWindow; - config.hMainIcon = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); - config.cxWidth = 180; - config.pszWindowTitle = L"Process Hacker - VirusTotal"; - config.pszMainInstruction = L"VirusTotal scanning requires a restart of Process Hacker."; - config.pszContent = L"Do you want to restart Process Hacker now?"; - - if (SUCCEEDED(TaskDialogIndirect(&config, &result, NULL, NULL)) && result == IDYES) - { - ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); - PhShellProcessHacker( - PhMainWndHandle, - L"-v", - SW_SHOW, - 0, - PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, - 0, - NULL - ); - ProcessHacker_Destroy(PhMainWndHandle); - } - - DestroyIcon(config.hMainIcon); - } - } - break; - case MENUITEM_VIRUSTOTAL_UPLOAD: - UploadToOnlineService(menuItem->Context, MENUITEM_VIRUSTOTAL_UPLOAD); - break; - case MENUITEM_JOTTI_UPLOAD: - UploadToOnlineService(menuItem->Context, MENUITEM_JOTTI_UPLOAD); - break; - case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - if (PhShowFileDialog(menuItem->Context, fileDialog)) - { - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - - UploadToOnlineService(fileName, MENUITEM_VIRUSTOTAL_UPLOAD); - } - - PhFreeFileDialog(fileDialog); - } - break; - } -} - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_EMENU_ITEM onlineMenuItem; - PPH_EMENU_ITEM enableMenuItem; - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - - if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_TOOLS) - return; - - onlineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Online Checks", NULL); - PhInsertEMenuItem(onlineMenuItem, enableMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ENABLE_SERVICE_VIRUSTOTAL, L"Enable VirusTotal scanning", NULL), -1); - PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_FILE, L"Upload file to VirusTotal...", NULL), -1); - PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_QUEUE, L"Upload unknown files to VirusTotal...", NULL), -1); - PhInsertEMenuItem(menuInfo->Menu, onlineMenuItem, -1); - - if (VirusTotalScanningEnabled) - enableMenuItem->Flags |= PH_EMENU_CHECKED; -} - -PPH_EMENU_ITEM CreateSendToMenu( - _In_ PPH_EMENU_ITEM Parent, - _In_ PPH_STRING FileName - ) -{ - PPH_EMENU_ITEM sendToMenu; - PPH_EMENU_ITEM menuItem; - ULONG insertIndex; - - sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Send to", NULL); - PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD, L"virustotal.com", FileName), -1); - PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD, L"virusscan.jotti.org", FileName), -1); - - if (menuItem = PhFindEMenuItem(Parent, PH_EMENU_FIND_STARTSWITH, L"Search online", 0)) - { - insertIndex = PhIndexOfEMenuItem(Parent, menuItem); - PhInsertEMenuItem(Parent, sendToMenu, insertIndex + 1); - PhInsertEMenuItem(Parent, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), insertIndex + 2); - } - else - { - PhInsertEMenuItem(Parent, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(Parent, sendToMenu, -1); - } - - return sendToMenu; -} - -VOID NTAPI ProcessMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_PROCESS_ITEM processItem; - PPH_EMENU_ITEM sendToMenu; - - if (menuInfo->u.Process.NumberOfProcesses == 1) - processItem = menuInfo->u.Process.Processes[0]; - else - processItem = NULL; - - sendToMenu = CreateSendToMenu(menuInfo->Menu, processItem ? processItem->FileName : NULL); - - // Only enable the Send To menu if there is exactly one process selected and it has a file name. - if (!processItem || !processItem->FileName) - { - sendToMenu->Flags |= PH_EMENU_DISABLED; - } -} - -VOID NTAPI ModuleMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_MODULE_ITEM moduleItem; - PPH_EMENU_ITEM sendToMenu; - - if (menuInfo->u.Module.NumberOfModules == 1) - moduleItem = menuInfo->u.Module.Modules[0]; - else - moduleItem = NULL; - - sendToMenu = CreateSendToMenu(menuInfo->Menu, moduleItem ? moduleItem->FileName : NULL); - - if (!moduleItem) - { - sendToMenu->Flags |= PH_EMENU_DISABLED; - } -} - -VOID NTAPI ServiceMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_SERVICE_ITEM serviceItem; - PPH_EMENU_ITEM sendToMenu; - PPH_STRING serviceFileName = NULL; - PPH_STRING serviceBinaryPath = NULL; - - if (menuInfo->u.Service.NumberOfServices == 1) - serviceItem = menuInfo->u.Service.Services[0]; - else - serviceItem = NULL; - - QueryServiceFileName( - &serviceItem->Name->sr, - &serviceFileName, - &serviceBinaryPath - ); - - if (serviceBinaryPath) PhDereferenceObject(serviceBinaryPath); - - // TODO: Possible serviceFileName string memory leak. - sendToMenu = CreateSendToMenu(menuInfo->Menu, serviceFileName ? serviceFileName : NULL); - - if (!serviceItem || !serviceFileName) - { - sendToMenu->Flags |= PH_EMENU_DISABLED; - } -} - -VOID ProcessHighlightingColorCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; - PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)getHighlightingColor->Parameter; - //PPROCESS_DB_OBJECT object; - - if (getHighlightingColor->Handled) - return; - - //if (!PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS)) - // return; - - //LockProcessDb(); - - //if (PhIsNullOrEmptyString(processItem->FileName)) - // return; - - //if ((object = FindProcessDbObject(&processItem->FileName->sr)) && object->Positives) - //{ - // getHighlightingColor->BackColor = RGB(255, 0, 0); - // getHighlightingColor->Cache = TRUE; - // getHighlightingColor->Handled = TRUE; - //} - - //UnlockProcessDb(); -} - -LONG NTAPI VirusTotalProcessNodeSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PPH_PROCESS_NODE node1 = Node1; - PPH_PROCESS_NODE node2 = Node2; - PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType); - PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType); - - return PhCompareStringWithNull(extension1->VirusTotalResult, extension2->VirusTotalResult, TRUE); -} - -LONG NTAPI VirusTotalModuleNodeSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PPH_MODULE_NODE node1 = Node1; - PPH_MODULE_NODE node2 = Node2; - PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ModuleItem, EmModuleItemType); - PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ModuleItem, EmModuleItemType); - - return PhCompareStringWithNull(extension1->VirusTotalResult, extension2->VirusTotalResult, TRUE); -} - -LONG NTAPI VirusTotalServiceNodeSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PPH_SERVICE_NODE node1 = Node1; - PPH_SERVICE_NODE node2 = Node2; - PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ServiceItem, EmServiceItemType); - PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ServiceItem, EmServiceItemType); - - return PhCompareStringWithNull(extension1->VirusTotalResult, extension2->VirusTotalResult, TRUE); -} - -VOID NTAPI ProcessTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"VirusTotal"; - column.Width = 140; - column.Alignment = PH_ALIGN_CENTER; - column.CustomDraw = TRUE; - column.Context = info->TreeNewHandle; // Context - - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_VIUSTOTAL_PROCESS, NULL, VirusTotalProcessNodeSortFunction); -} - -VOID NTAPI ModuleTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"VirusTotal"; - column.Width = 140; - column.Alignment = PH_ALIGN_CENTER; - column.CustomDraw = TRUE; - column.Context = info->TreeNewHandle; // Context - - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_VIUSTOTAL_MODULE, NULL, VirusTotalModuleNodeSortFunction); -} - -VOID NTAPI ServiceTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PH_TREENEW_COLUMN column; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"VirusTotal"; - column.Width = 140; - column.Alignment = PH_ALIGN_CENTER; - column.CustomDraw = TRUE; - column.Context = info->TreeNewHandle; // Context - - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_VIUSTOTAL_SERVICE, NULL, VirusTotalServiceNodeSortFunction); -} - -VOID NTAPI TreeNewMessageCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - - switch (message->Message) - { - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; - - switch (message->SubId) - { - case COLUMN_ID_VIUSTOTAL_PROCESS: - { - PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)getCellText->Node; - PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processNode->ProcessItem, EmProcessItemType); - - getCellText->Text = PhGetStringRef(extension->VirusTotalResult); - } - break; - case COLUMN_ID_VIUSTOTAL_MODULE: - { - PPH_MODULE_NODE moduleNode = (PPH_MODULE_NODE)getCellText->Node; - PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, moduleNode->ModuleItem, EmModuleItemType); - - getCellText->Text = PhGetStringRef(extension->VirusTotalResult); - } - break; - case COLUMN_ID_VIUSTOTAL_SERVICE: - { - PPH_SERVICE_NODE serviceNode = (PPH_SERVICE_NODE)getCellText->Node; - PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, serviceNode->ServiceItem, EmServiceItemType); - - getCellText->Text = PhGetStringRef(extension->VirusTotalResult); - } - break; - } - } - break; - case TreeNewCustomDraw: - { - PPH_TREENEW_CUSTOM_DRAW customDraw = message->Parameter1; - PPROCESS_EXTENSION extension = NULL; - PH_STRINGREF text; - - if (!VirusTotalScanningEnabled) - { - static PH_STRINGREF disabledText = PH_STRINGREF_INIT(L"Scanning disabled"); - - DrawText( - customDraw->Dc, - disabledText.Buffer, - (ULONG)disabledText.Length / 2, - &customDraw->CellRect, - DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE - ); - - return; - } - - switch (message->SubId) - { - case COLUMN_ID_VIUSTOTAL_PROCESS: - { - PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)customDraw->Node; - - extension = PhPluginGetObjectExtension(PluginInstance, processNode->ProcessItem, EmProcessItemType); - } - break; - case COLUMN_ID_VIUSTOTAL_MODULE: - { - PPH_MODULE_NODE moduleNode = (PPH_MODULE_NODE)customDraw->Node; - - extension = PhPluginGetObjectExtension(PluginInstance, moduleNode->ModuleItem, EmModuleItemType); - } - break; - case COLUMN_ID_VIUSTOTAL_SERVICE: - { - PPH_SERVICE_NODE serviceNode = (PPH_SERVICE_NODE)customDraw->Node; - - extension = PhPluginGetObjectExtension(PluginInstance, serviceNode->ServiceItem, EmServiceItemType); - } - break; - } - - if (!extension) - break; - - text = PhGetStringRef(extension->VirusTotalResult); - DrawText( - customDraw->Dc, - text.Buffer, - (ULONG)text.Length / 2, - &customDraw->CellRect, - DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE - ); - } - break; - } -} - -VOID NTAPI ProcessItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_PROCESS_ITEM processItem = Object; - PPROCESS_EXTENSION extension = Extension; - - memset(extension, 0, sizeof(PROCESS_EXTENSION)); - - extension->ProcessItem = processItem; - extension->VirusTotalResult = PhReferenceEmptyString(); - - PhAcquireQueuedLockExclusive(&ProcessesListLock); - InsertTailList(&ProcessListHead, &extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessesListLock); -} - -VOID NTAPI ProcessItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_PROCESS_ITEM processItem = Object; - PPROCESS_EXTENSION extension = Extension; - - PhClearReference(&extension->VirusTotalResult); - - PhAcquireQueuedLockExclusive(&ProcessesListLock); - RemoveEntryList(&extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessesListLock); -} - -VOID NTAPI ModuleItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_MODULE_ITEM moduleItem = Object; - PPROCESS_EXTENSION extension = Extension; - - memset(extension, 0, sizeof(PROCESS_EXTENSION)); - - extension->ModuleItem = moduleItem; - extension->VirusTotalResult = PhReferenceEmptyString(); - - PhAcquireQueuedLockExclusive(&ProcessesListLock); - InsertTailList(&ProcessListHead, &extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessesListLock); -} - -VOID NTAPI ModuleItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_MODULE_ITEM processItem = Object; - PPROCESS_EXTENSION extension = Extension; - - PhClearReference(&extension->VirusTotalResult); - - PhAcquireQueuedLockExclusive(&ProcessesListLock); - RemoveEntryList(&extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessesListLock); -} - -VOID NTAPI ServiceItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_SERVICE_ITEM serviceItem = Object; - PPROCESS_EXTENSION extension = Extension; - - memset(extension, 0, sizeof(PROCESS_EXTENSION)); - - extension->ServiceItem = serviceItem; - extension->VirusTotalResult = PhReferenceEmptyString(); - - PhAcquireQueuedLockExclusive(&ProcessesListLock); - InsertTailList(&ProcessListHead, &extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessesListLock); -} - -VOID NTAPI ServiceItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_SERVICE_ITEM serviceItem = Object; - PPROCESS_EXTENSION extension = Extension; - - PhClearReference(&extension->VirusTotalResult); - - PhAcquireQueuedLockExclusive(&ProcessesListLock); - RemoveEntryList(&extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessesListLock); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, L"0" }, - { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS, L"0" } - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Online Checks"; - info->Author = L"dmex, wj32"; - info->Description = L"Allows files to be checked with online services."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1118"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), - MainMenuInitializingCallback, - NULL, - &MainMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), - ProcessMenuInitializingCallback, - NULL, - &ProcessMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), - ModuleMenuInitializingCallback, - NULL, - &ModuleMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceMenuInitializing), - ServiceMenuInitializingCallback, - NULL, - &ServiceMenuInitializingCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessesUpdated), - ProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), - ProcessHighlightingColorCallback, - NULL, - &ProcessHighlightingColorCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), - ProcessTreeNewInitializingCallback, - NULL, - &ProcessTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackModuleTreeNewInitializing), - ModuleTreeNewInitializingCallback, - NULL, - &ModulesTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), - ServiceTreeNewInitializingCallback, - NULL, - &ServiceTreeNewInitializingCallbackRegistration - ); - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), - TreeNewMessageCallback, - NULL, - &TreeNewMessageCallbackRegistration - ); - - PhPluginSetObjectExtension( - PluginInstance, - EmProcessItemType, - sizeof(PROCESS_EXTENSION), - ProcessItemCreateCallback, - ProcessItemDeleteCallback - ); - - PhPluginSetObjectExtension( - PluginInstance, - EmModuleItemType, - sizeof(PROCESS_EXTENSION), - ModuleItemCreateCallback, - ModuleItemDeleteCallback - ); - - PhPluginSetObjectExtension( - PluginInstance, - EmServiceItemType, - sizeof(PROCESS_EXTENSION), - ServiceItemCreateCallback, - ServiceItemDeleteCallback - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - } - - return TRUE; -} \ No newline at end of file +/* + * Process Hacker Online Checks - + * Main Program + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2012-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "onlnchk.h" +#include "db.h" + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessHighlightingColorCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ModuleMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ServiceMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ModulesTreeNewInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; + +BOOLEAN VirusTotalScanningEnabled = FALSE; +ULONG ProcessesUpdatedCount = 0; +LIST_ENTRY ProcessListHead = { &ProcessListHead, &ProcessListHead }; +PH_QUEUED_LOCK ProcessesListLock = PH_QUEUED_LOCK_INIT; + +VOID ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + static ULONG ProcessesUpdatedCount = 0; + PLIST_ENTRY listEntry; + + if (!VirusTotalScanningEnabled) + return; + + if (ProcessesUpdatedCount < 2) + { + ProcessesUpdatedCount++; + return; + } + + listEntry = ProcessListHead.Flink; + + while (listEntry != &ProcessListHead) + { + PPROCESS_EXTENSION extension; + PPH_STRING filePath = NULL; + + extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); + + if (extension->ProcessItem) + { + filePath = extension->ProcessItem->FileName; + } + else if (extension->ModuleItem) + { + filePath = extension->ModuleItem->FileName; + } + else if (extension->ServiceItem) + { + if (extension->FilePath) + { + filePath = extension->FilePath; + } + else + { + PPH_STRING serviceFileName = NULL; + PPH_STRING serviceBinaryPath = NULL; + + if (NT_SUCCESS(QueryServiceFileName( + &extension->ServiceItem->Name->sr, + &serviceFileName, + &serviceBinaryPath + ))) + { + PhMoveReference(&extension->FilePath, serviceFileName); + if (serviceBinaryPath) PhDereferenceObject(serviceBinaryPath); + } + + filePath = extension->FilePath; + } + } + + if (!PhIsNullOrEmptyString(filePath)) + { + if (!extension->ResultValid) + { + PPROCESS_DB_OBJECT object; + + if (object = FindProcessDbObject(&filePath->sr)) + { + extension->Stage1 = TRUE; + extension->ResultValid = TRUE; + + extension->Positives = object->Positives; + PhSwapReference(&extension->VirusTotalResult, object->Result); + } + } + + if (!extension->Stage1) + { + if (!VirusTotalGetCachedResult(filePath)) + { + VirusTotalAddCacheResult(filePath, extension); + } + + extension->Stage1 = TRUE; + } + } + + listEntry = listEntry->Flink; + } +} + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (VirusTotalScanningEnabled = !!PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED)) + { + InitializeProcessDb(); + InitializeVirusTotalProcessMonitor(); + } +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"OnlineChecks", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + switch (menuItem->Id) + { + case ENABLE_SERVICE_VIRUSTOTAL: + { + ULONG scanningEnabled = !VirusTotalScanningEnabled; + + PhSetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, scanningEnabled); + + if (VirusTotalScanningEnabled != scanningEnabled) + { + INT result = IDOK; + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION; + config.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; + config.hwndParent = menuItem->OwnerWindow; + config.hMainIcon = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + config.cxWidth = 180; + config.pszWindowTitle = L"Process Hacker - VirusTotal"; + config.pszMainInstruction = L"VirusTotal scanning requires a restart of Process Hacker."; + config.pszContent = L"Do you want to restart Process Hacker now?"; + + if (SUCCEEDED(TaskDialogIndirect(&config, &result, NULL, NULL)) && result == IDYES) + { + ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); + PhShellProcessHacker( + PhMainWndHandle, + L"-v", + SW_SHOW, + 0, + PH_SHELL_APP_PROPAGATE_PARAMETERS | PH_SHELL_APP_PROPAGATE_PARAMETERS_IGNORE_VISIBILITY, + 0, + NULL + ); + ProcessHacker_Destroy(PhMainWndHandle); + } + + DestroyIcon(config.hMainIcon); + } + } + break; + case MENUITEM_VIRUSTOTAL_UPLOAD: + UploadToOnlineService(menuItem->Context, MENUITEM_VIRUSTOTAL_UPLOAD); + break; + case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE); + break; + case MENUITEM_JOTTI_UPLOAD: + UploadToOnlineService(menuItem->Context, MENUITEM_JOTTI_UPLOAD); + break; + case MENUITEM_JOTTI_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_JOTTI_UPLOAD_SERVICE); + break; + case MENUITEM_HYBRIDANALYSIS_UPLOAD: + UploadToOnlineService(menuItem->Context, MENUITEM_HYBRIDANALYSIS_UPLOAD); + break; + case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: + UploadServiceToOnlineService(menuItem->Context, MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE); + break; + case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: + case MENUITEM_HYBRIDANALYSIS_UPLOAD_FILE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + if (PhShowFileDialog(menuItem->Context, fileDialog)) + { + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + + switch (menuItem->Id) + { + case MENUITEM_VIRUSTOTAL_UPLOAD_FILE: + UploadToOnlineService(fileName, MENUITEM_VIRUSTOTAL_UPLOAD); + break; + case MENUITEM_HYBRIDANALYSIS_UPLOAD_FILE: + UploadToOnlineService(fileName, MENUITEM_HYBRIDANALYSIS_UPLOAD); + break; + } + } + + PhFreeFileDialog(fileDialog); + } + break; + } +} + +VOID NTAPI MainMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_EMENU_ITEM onlineMenuItem; + PPH_EMENU_ITEM enableMenuItem; + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + + if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_TOOLS) + return; + + onlineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"&Online Checks", NULL); + PhInsertEMenuItem(onlineMenuItem, enableMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ENABLE_SERVICE_VIRUSTOTAL, L"&Enable VirusTotal scanning", NULL), ULONG_MAX); + PhInsertEMenuItem(onlineMenuItem, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD_FILE, L"Upload file to &Hybrid-Analysis...", NULL), ULONG_MAX); + PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_FILE, L"&Upload file to VirusTotal...", NULL), ULONG_MAX); + //PhInsertEMenuItem(onlineMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_QUEUE, L"Upload unknown files to VirusTotal...", NULL), ULONG_MAX); + PhInsertEMenuItem(menuInfo->Menu, onlineMenuItem, ULONG_MAX); + + if (VirusTotalScanningEnabled) + enableMenuItem->Flags |= PH_EMENU_CHECKED; +} + +PPH_EMENU_ITEM CreateSendToMenu( + _In_ BOOLEAN ProcessesMenu, + _In_ PPH_EMENU_ITEM Parent, + _In_ PPH_STRING FileName + ) +{ + PPH_EMENU_ITEM sendToMenu; + PPH_EMENU_ITEM menuItem; + ULONG insertIndex; + + sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD, L"&hybrid-analysis.com", FileName), ULONG_MAX); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD, L"&virustotal.com", FileName), ULONG_MAX); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD, L"virusscan.&jotti.org", FileName), ULONG_MAX); + + if (ProcessesMenu && (menuItem = PhFindEMenuItem(Parent, PH_EMENU_FIND_STARTSWITH, L"Search online", 0))) + { + insertIndex = PhIndexOfEMenuItem(Parent, menuItem); + PhInsertEMenuItem(Parent, sendToMenu, insertIndex + 1); + PhInsertEMenuItem(Parent, PhCreateEMenuSeparator(), insertIndex + 2); + } + else + { + PhInsertEMenuItem(Parent, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(Parent, sendToMenu, ULONG_MAX); + } + + return sendToMenu; +} + +VOID NTAPI ProcessMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_PROCESS_ITEM processItem; + PPH_EMENU_ITEM sendToMenu; + + if (menuInfo->u.Process.NumberOfProcesses == 1) + processItem = menuInfo->u.Process.Processes[0]; + else + processItem = NULL; + + sendToMenu = CreateSendToMenu(TRUE, menuInfo->Menu, processItem ? processItem->FileName : NULL); + + // Only enable the Send To menu if there is exactly one process selected and it has a file name. + if (!processItem || !processItem->FileName) + { + sendToMenu->Flags |= PH_EMENU_DISABLED; + } +} + +VOID NTAPI ModuleMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_MODULE_ITEM moduleItem; + PPH_EMENU_ITEM sendToMenu; + + if (menuInfo->u.Module.NumberOfModules == 1) + moduleItem = menuInfo->u.Module.Modules[0]; + else + moduleItem = NULL; + + sendToMenu = CreateSendToMenu(FALSE, menuInfo->Menu, moduleItem ? moduleItem->FileName : NULL); + + if (!moduleItem) + { + sendToMenu->Flags |= PH_EMENU_DISABLED; + } +} + +VOID NTAPI ServiceMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_SERVICE_ITEM serviceItem; + PPH_EMENU_ITEM sendToMenu; + + if (menuInfo->u.Service.NumberOfServices == 1) + serviceItem = menuInfo->u.Service.Services[0]; + else + serviceItem = NULL; + + sendToMenu = PhPluginCreateEMenuItem(PluginInstance, 0, 0, L"Sen&d to", NULL); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE, L"&hybrid-analysis.com", serviceItem ? serviceItem : NULL), ULONG_MAX); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"&virustotal.com", serviceItem ? serviceItem : NULL), ULONG_MAX); + PhInsertEMenuItem(sendToMenu, PhPluginCreateEMenuItem(PluginInstance, 0, MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.&jotti.org", serviceItem ? serviceItem : NULL), ULONG_MAX); + PhInsertEMenuItem(menuInfo->Menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menuInfo->Menu, sendToMenu, ULONG_MAX); + + if (!serviceItem) + { + sendToMenu->Flags |= PH_EMENU_DISABLED; + } +} + +VOID ProcessHighlightingColorCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; + PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)getHighlightingColor->Parameter; + //PPROCESS_DB_OBJECT object; + + if (getHighlightingColor->Handled) + return; + + //if (!PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS)) + // return; + + //LockProcessDb(); + + //if (PhIsNullOrEmptyString(processItem->FileName)) + // return; + + //if ((object = FindProcessDbObject(&processItem->FileName->sr)) && object->Positives) + //{ + // getHighlightingColor->BackColor = RGB(255, 0, 0); + // getHighlightingColor->Cache = TRUE; + // getHighlightingColor->Handled = TRUE; + //} + + //UnlockProcessDb(); +} + +LONG NTAPI VirusTotalProcessNodeSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PPH_PROCESS_NODE node1 = Node1; + PPH_PROCESS_NODE node2 = Node2; + PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType); + PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType); + + return PhCompareStringWithNullSortOrder(extension1->VirusTotalResult, extension2->VirusTotalResult, SortOrder, TRUE); +} + +LONG NTAPI VirusTotalModuleNodeSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PPH_MODULE_NODE node1 = Node1; + PPH_MODULE_NODE node2 = Node2; + PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ModuleItem, EmModuleItemType); + PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ModuleItem, EmModuleItemType); + + return PhCompareStringWithNullSortOrder(extension1->VirusTotalResult, extension2->VirusTotalResult, SortOrder, TRUE); +} + +LONG NTAPI VirusTotalServiceNodeSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PPH_SERVICE_NODE node1 = Node1; + PPH_SERVICE_NODE node2 = Node2; + PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ServiceItem, EmServiceItemType); + PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ServiceItem, EmServiceItemType); + + return PhCompareStringWithNullSortOrder(extension1->VirusTotalResult, extension2->VirusTotalResult, SortOrder, TRUE); +} + +VOID NTAPI ProcessTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"VirusTotal"; + column.Width = 140; + column.Alignment = PH_ALIGN_CENTER; + column.CustomDraw = TRUE; + column.Context = info->TreeNewHandle; // Context + + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_VIUSTOTAL_PROCESS, NULL, VirusTotalProcessNodeSortFunction); +} + +VOID NTAPI ModuleTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"VirusTotal"; + column.Width = 140; + column.Alignment = PH_ALIGN_CENTER; + column.CustomDraw = TRUE; + column.Context = info->TreeNewHandle; // Context + + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_VIUSTOTAL_MODULE, NULL, VirusTotalModuleNodeSortFunction); +} + +VOID NTAPI ServiceTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PH_TREENEW_COLUMN column; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"VirusTotal"; + column.Width = 140; + column.Alignment = PH_ALIGN_CENTER; + column.CustomDraw = TRUE; + column.Context = info->TreeNewHandle; // Context + + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_VIUSTOTAL_SERVICE, NULL, VirusTotalServiceNodeSortFunction); +} + +VOID NTAPI TreeNewMessageCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + + switch (message->Message) + { + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; + + switch (message->SubId) + { + case COLUMN_ID_VIUSTOTAL_PROCESS: + { + PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)getCellText->Node; + PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processNode->ProcessItem, EmProcessItemType); + + getCellText->Text = PhGetStringRef(extension->VirusTotalResult); + } + break; + case COLUMN_ID_VIUSTOTAL_MODULE: + { + PPH_MODULE_NODE moduleNode = (PPH_MODULE_NODE)getCellText->Node; + PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, moduleNode->ModuleItem, EmModuleItemType); + + getCellText->Text = PhGetStringRef(extension->VirusTotalResult); + } + break; + case COLUMN_ID_VIUSTOTAL_SERVICE: + { + PPH_SERVICE_NODE serviceNode = (PPH_SERVICE_NODE)getCellText->Node; + PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, serviceNode->ServiceItem, EmServiceItemType); + + getCellText->Text = PhGetStringRef(extension->VirusTotalResult); + } + break; + } + } + break; + case TreeNewCustomDraw: + { + PPH_TREENEW_CUSTOM_DRAW customDraw = message->Parameter1; + PPROCESS_EXTENSION extension = NULL; + PH_STRINGREF text; + + if (!VirusTotalScanningEnabled) + { + static PH_STRINGREF disabledText = PH_STRINGREF_INIT(L"Scanning disabled"); + + DrawText( + customDraw->Dc, + disabledText.Buffer, + (ULONG)disabledText.Length / 2, + &customDraw->CellRect, + DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE + ); + + return; + } + + switch (message->SubId) + { + case COLUMN_ID_VIUSTOTAL_PROCESS: + { + PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)customDraw->Node; + + extension = PhPluginGetObjectExtension(PluginInstance, processNode->ProcessItem, EmProcessItemType); + } + break; + case COLUMN_ID_VIUSTOTAL_MODULE: + { + PPH_MODULE_NODE moduleNode = (PPH_MODULE_NODE)customDraw->Node; + + extension = PhPluginGetObjectExtension(PluginInstance, moduleNode->ModuleItem, EmModuleItemType); + } + break; + case COLUMN_ID_VIUSTOTAL_SERVICE: + { + PPH_SERVICE_NODE serviceNode = (PPH_SERVICE_NODE)customDraw->Node; + + extension = PhPluginGetObjectExtension(PluginInstance, serviceNode->ServiceItem, EmServiceItemType); + } + break; + } + + if (!extension) + break; + + //if (extension->Positives > 0) + // SetTextColor(customDraw->Dc, RGB(0xff, 0x0, 0x0)); + + text = PhGetStringRef(extension->VirusTotalResult); + DrawText( + customDraw->Dc, + text.Buffer, + (ULONG)text.Length / sizeof(WCHAR), + &customDraw->CellRect, + DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE + ); + } + break; + } +} + +VOID NTAPI ProcessItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_PROCESS_ITEM processItem = Object; + PPROCESS_EXTENSION extension = Extension; + + memset(extension, 0, sizeof(PROCESS_EXTENSION)); + + extension->ProcessItem = processItem; + + PhAcquireQueuedLockExclusive(&ProcessesListLock); + InsertTailList(&ProcessListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessesListLock); +} + +VOID NTAPI ProcessItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_PROCESS_ITEM processItem = Object; + PPROCESS_EXTENSION extension = Extension; + + PhClearReference(&extension->VirusTotalResult); + + PhAcquireQueuedLockExclusive(&ProcessesListLock); + RemoveEntryList(&extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessesListLock); +} + +VOID NTAPI ModuleItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_MODULE_ITEM moduleItem = Object; + PPROCESS_EXTENSION extension = Extension; + + memset(extension, 0, sizeof(PROCESS_EXTENSION)); + + extension->ModuleItem = moduleItem; + + PhAcquireQueuedLockExclusive(&ProcessesListLock); + InsertTailList(&ProcessListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessesListLock); +} + +VOID NTAPI ModuleItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_MODULE_ITEM processItem = Object; + PPROCESS_EXTENSION extension = Extension; + + PhClearReference(&extension->VirusTotalResult); + + PhAcquireQueuedLockExclusive(&ProcessesListLock); + RemoveEntryList(&extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessesListLock); +} + +VOID NTAPI ServiceItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_SERVICE_ITEM serviceItem = Object; + PPROCESS_EXTENSION extension = Extension; + + memset(extension, 0, sizeof(PROCESS_EXTENSION)); + + extension->ServiceItem = serviceItem; + + PhAcquireQueuedLockExclusive(&ProcessesListLock); + InsertTailList(&ProcessListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessesListLock); +} + +VOID NTAPI ServiceItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_SERVICE_ITEM serviceItem = Object; + PPROCESS_EXTENSION extension = Extension; + + PhClearReference(&extension->VirusTotalResult); + + PhAcquireQueuedLockExclusive(&ProcessesListLock); + RemoveEntryList(&extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessesListLock); +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, L"0" }, + { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS, L"0" }, + { IntegerSettingType, SETTING_NAME_VIRUSTOTAL_DEFAULT_ACTION, L"0" } + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Online Checks"; + info->Author = L"dmex, wj32"; + info->Description = L"Allows files to be checked with online services."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1118"; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), + MainMenuInitializingCallback, + NULL, + &MainMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), + ProcessMenuInitializingCallback, + NULL, + &ProcessMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackModuleMenuInitializing), + ModuleMenuInitializingCallback, + NULL, + &ModuleMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceMenuInitializing), + ServiceMenuInitializingCallback, + NULL, + &ServiceMenuInitializingCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + ProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), + ProcessHighlightingColorCallback, + NULL, + &ProcessHighlightingColorCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), + ProcessTreeNewInitializingCallback, + NULL, + &ProcessTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackModuleTreeNewInitializing), + ModuleTreeNewInitializingCallback, + NULL, + &ModulesTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), + ServiceTreeNewInitializingCallback, + NULL, + &ServiceTreeNewInitializingCallbackRegistration + ); + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), + TreeNewMessageCallback, + NULL, + &TreeNewMessageCallbackRegistration + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmProcessItemType, + sizeof(PROCESS_EXTENSION), + ProcessItemCreateCallback, + ProcessItemDeleteCallback + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmModuleItemType, + sizeof(PROCESS_EXTENSION), + ModuleItemCreateCallback, + ModuleItemDeleteCallback + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmServiceItemType, + sizeof(PROCESS_EXTENSION), + ServiceItemCreateCallback, + ServiceItemDeleteCallback + ); + + PhAddSettings(settings, ARRAYSIZE(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/OnlineChecks/onlnchk.h b/plugins/OnlineChecks/onlnchk.h index ef064d48df00..662c9f214f12 100644 --- a/plugins/OnlineChecks/onlnchk.h +++ b/plugins/OnlineChecks/onlnchk.h @@ -1,269 +1,343 @@ -/* - * Process Hacker Online Checks - - * Main Headers - * - * Copyright (C) 2010-2013 wj32 - * Copyright (C) 2012-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef ONLNCHK_H -#define ONLNCHK_H - -#define CINTERFACE -#define COBJMACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include "resource.h" -#include "db.h" - -#define PLUGIN_NAME L"ProcessHacker.OnlineChecks" -#define SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED (PLUGIN_NAME L".EnableVirusTotalScanning") -#define SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS (PLUGIN_NAME L".VirusTotalHighlightDetection") - -#ifdef VIRUSTOTAL_API -#include "virustotal.h" -#else -#define VIRUSTOTAL_URLPATH L"" -#define VIRUSTOTAL_APIKEY L"" -#endif - -#define UM_UPLOAD (WM_APP + 1) -#define UM_EXISTS (WM_APP + 2) -#define UM_LAUNCH (WM_APP + 3) -#define UM_ERROR (WM_APP + 4) -#define UM_SHOWDIALOG (WM_APP + 5) - -extern PPH_PLUGIN PluginInstance; - -typedef struct _SERVICE_INFO -{ - ULONG Id; - PWSTR HostName; - INTERNET_PORT HostPort; - ULONG HostFlags; - PWSTR UploadObjectName; - PWSTR FileNameFieldName; -} SERVICE_INFO, *PSERVICE_INFO; - -typedef struct _PROCESS_EXTENSION -{ - LIST_ENTRY ListEntry; - - union - { - BOOLEAN Flags; - struct - { - BOOLEAN Stage1 : 1; - BOOLEAN ResultValid : 1; - BOOLEAN Spare : 6; - }; - }; - - INT64 Retries; - INT64 Positives; - PPH_STRING VirusTotalResult; - PPH_STRING FilePath; - - PPH_PROCESS_ITEM ProcessItem; - PPH_MODULE_ITEM ModuleItem; - PPH_SERVICE_ITEM ServiceItem; -} PROCESS_EXTENSION, *PPROCESS_EXTENSION; - -typedef struct _VIRUSTOTAL_FILE_HASH_ENTRY -{ - union - { - BOOLEAN Flags; - struct - { - BOOLEAN Stage1 : 1; - BOOLEAN Processing : 1; - BOOLEAN Processed : 1; - BOOLEAN Found : 1; - BOOLEAN Spare : 5; - }; - }; - - PPROCESS_EXTENSION Extension; - - INT64 Positives; - PPH_STRING FileName; - PPH_STRING FileHash; - PPH_BYTES FileNameAnsi; - PPH_BYTES FileHashAnsi; - PPH_BYTES CreationTime; - PPH_STRING FileResult; -} VIRUSTOTAL_FILE_HASH_ENTRY, *PVIRUSTOTAL_FILE_HASH_ENTRY; - -typedef struct _UPLOAD_CONTEXT -{ - BOOLEAN VtApiUpload; - BOOLEAN FileExists; - ULONG Service; - ULONG ErrorCode; - ULONG TotalFileLength; - - HWND DialogHandle; - HANDLE UploadThreadHandle; - HICON IconLargeHandle; - HICON IconSmallHandle; - HINTERNET HttpHandle; - ITaskbarList3* TaskbarListClass; - - PPH_STRING FileSize; - PPH_STRING ErrorString; - PPH_STRING KeyString; - - PPH_STRING FileName; - PPH_STRING BaseFileName; - PPH_STRING WindowFileName; - PPH_STRING LaunchCommand; - PPH_STRING Detected; - PPH_STRING MaxDetected; - PPH_STRING UploadUrl; - PPH_STRING ReAnalyseUrl; - PPH_STRING FirstAnalysisDate; - PPH_STRING LastAnalysisDate; - PPH_STRING LastAnalysisUrl; - PPH_STRING LastAnalysisAgo; -} UPLOAD_CONTEXT, *PUPLOAD_CONTEXT; - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ); - -NTSTATUS UploadFileThreadStart( - _In_ PVOID Parameter - ); - -NTSTATUS UploadCheckThreadStart( - _In_ PVOID Parameter - ); - -VOID ShowVirusTotalUploadDialog( - _In_ PUPLOAD_CONTEXT Context - ); - -VOID ShowFileFoundDialog( - _In_ PUPLOAD_CONTEXT Context - ); - -VOID ShowVirusTotalProgressDialog( - _In_ PUPLOAD_CONTEXT Context - ); - -VOID VirusTotalShowErrorDialog( - _In_ PUPLOAD_CONTEXT Context - ); - -typedef struct _VIRUSTOTAL_FILE_REPORT_RESULT -{ - PPH_STRING FileName; - PPH_STRING BaseFileName; - - PPH_STRING Total; - PPH_STRING Positives; - PPH_STRING Resource; - PPH_STRING ScanId; - PPH_STRING Md5; - PPH_STRING Sha1; - PPH_STRING Sha256; - PPH_STRING ScanDate; - PPH_STRING Permalink; - PPH_STRING StatusMessage; - PPH_LIST ScanResults; -} VIRUSTOTAL_FILE_REPORT_RESULT, *PVIRUSTOTAL_FILE_REPORT_RESULT; - -PPH_STRING VirusTotalStringToTime( - _In_ PPH_STRING Time - ); - -PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( - _In_ PPH_STRING FileHash - ); - -// upload -#define ENABLE_SERVICE_VIRUSTOTAL 100 -#define MENUITEM_VIRUSTOTAL_QUEUE 101 -#define MENUITEM_VIRUSTOTAL_UPLOAD 102 -#define MENUITEM_VIRUSTOTAL_UPLOAD_FILE 103 -#define MENUITEM_JOTTI_UPLOAD 104 - -VOID UploadToOnlineService( - _In_ PPH_STRING FileName, - _In_ ULONG Service - ); - -typedef enum _NETWORK_COLUMN_ID -{ - COLUMN_ID_VIUSTOTAL_PROCESS = 1, - COLUMN_ID_VIUSTOTAL_MODULE = 2, - COLUMN_ID_VIUSTOTAL_SERVICE = 3 -} NETWORK_COLUMN_ID; - -NTSTATUS HashFileAndResetPosition( - _In_ HANDLE FileHandle, - _In_ PLARGE_INTEGER FileSize, - _In_ PH_HASH_ALGORITHM Algorithm, - _Out_ PPH_STRING *HashString - ); - -typedef struct _VIRUSTOTAL_API_RESULT -{ - BOOLEAN Found; - INT64 Positives; - INT64 Total; - PPH_STRING Permalink; - PPH_STRING FileHash; - PPH_STRING DetectionRatio; -} VIRUSTOTAL_API_RESULT, *PVIRUSTOTAL_API_RESULT; - -extern PPH_LIST VirusTotalList; -extern BOOLEAN VirusTotalScanningEnabled; - -PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalAddCacheResult( - _In_ PPH_STRING FileName, - _In_ PPROCESS_EXTENSION Extension - ); - -PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResult( - _In_ PPH_STRING FileName - ); - -VOID InitializeVirusTotalProcessMonitor( - VOID - ); - -VOID CleanupVirusTotalProcessMonitor( - VOID - ); - -NTSTATUS QueryServiceFileName( - _In_ PPH_STRINGREF ServiceName, - _Out_ PPH_STRING *ServiceFileName, - _Out_ PPH_STRING *ServiceBinaryPath - ); - -#endif +/* + * Process Hacker Online Checks - + * Main Headers + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2012-2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef ONLNCHK_H +#define ONLNCHK_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "resource.h" +#include "db.h" + +#define PLUGIN_NAME L"ProcessHacker.OnlineChecks" +#define SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED (PLUGIN_NAME L".EnableVirusTotalScanning") +#define SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS (PLUGIN_NAME L".VirusTotalHighlightDetection") +#define SETTING_NAME_VIRUSTOTAL_DEFAULT_ACTION (PLUGIN_NAME L".VirusTotalDefautAction") + +#define UM_UPLOAD (WM_APP + 1) +#define UM_EXISTS (WM_APP + 2) +#define UM_LAUNCH (WM_APP + 3) +#define UM_ERROR (WM_APP + 4) +#define UM_SHOWDIALOG (WM_APP + 5) + +extern PPH_PLUGIN PluginInstance; + +typedef struct _SERVICE_INFO +{ + ULONG Id; + PWSTR HostName; + PWSTR UploadObjectName; + PWSTR FileNameFieldName; +} SERVICE_INFO, *PSERVICE_INFO; + +typedef struct _PROCESS_EXTENSION +{ + LIST_ENTRY ListEntry; + + union + { + BOOLEAN Flags; + struct + { + BOOLEAN Stage1 : 1; + BOOLEAN ResultValid : 1; + BOOLEAN Spare : 6; + }; + }; + + INT64 Retries; + INT64 Positives; + PPH_STRING VirusTotalResult; + PPH_STRING FilePath; + + PPH_PROCESS_ITEM ProcessItem; + PPH_MODULE_ITEM ModuleItem; + PPH_SERVICE_ITEM ServiceItem; +} PROCESS_EXTENSION, *PPROCESS_EXTENSION; + +typedef struct _VIRUSTOTAL_FILE_HASH_ENTRY +{ + union + { + BOOLEAN Flags; + struct + { + BOOLEAN Stage1 : 1; + BOOLEAN Processing : 1; + BOOLEAN Processed : 1; + BOOLEAN Found : 1; + BOOLEAN Spare : 5; + }; + }; + + PPROCESS_EXTENSION Extension; + + INT64 Positives; + INT64 Total; + PPH_STRING FileName; + PPH_STRING FileHash; + PPH_BYTES FileNameAnsi; + PPH_BYTES FileHashAnsi; + PPH_BYTES CreationTime; +} VIRUSTOTAL_FILE_HASH_ENTRY, *PVIRUSTOTAL_FILE_HASH_ENTRY; + +typedef struct _UPLOAD_CONTEXT +{ + BOOLEAN VtApiUpload; + BOOLEAN FileExists; + ULONG Service; + ULONG ErrorCode; + ULONG TotalFileLength; + + HWND DialogHandle; + WNDPROC DialogWindowProc; + HANDLE UploadThreadHandle; + HICON IconLargeHandle; + HICON IconSmallHandle; + + ITaskbarList3* TaskbarListClass; + + PPH_STRING FileSize; + PPH_STRING ErrorString; + PPH_STRING FileName; + PPH_STRING BaseFileName; + PPH_STRING LaunchCommand; + PPH_STRING Detected; + PPH_STRING MaxDetected; + PPH_STRING UploadUrl; + PPH_STRING ReAnalyseUrl; + PPH_STRING FirstAnalysisDate; + PPH_STRING LastAnalysisDate; + PPH_STRING LastAnalysisUrl; + PPH_STRING LastAnalysisAgo; + + PPH_STRING FileHash; + +} UPLOAD_CONTEXT, *PUPLOAD_CONTEXT; + +// options.c + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +NTSTATUS UploadFileThreadStart( + _In_ PVOID Parameter + ); + +NTSTATUS UploadCheckThreadStart( + _In_ PVOID Parameter + ); + +NTSTATUS UploadRecheckThreadStart( + _In_ PVOID Parameter + ); + +NTSTATUS ViewReportThreadStart( + _In_ PVOID Parameter + ); + +VOID ShowVirusTotalUploadDialog( + _In_ PUPLOAD_CONTEXT Context + ); + +VOID ShowFileFoundDialog( + _In_ PUPLOAD_CONTEXT Context + ); + +VOID ShowVirusTotalProgressDialog( + _In_ PUPLOAD_CONTEXT Context + ); + +VOID ShowVirusTotalReScanProgressDialog( + _In_ PUPLOAD_CONTEXT Context + ); + +VOID ShowVirusTotalViewReportProgressDialog( + _In_ PUPLOAD_CONTEXT Context + ); + +VOID VirusTotalShowErrorDialog( + _In_ PUPLOAD_CONTEXT Context + ); + +// upload +#define ENABLE_SERVICE_HYBRIDANALYSIS 100 +#define MENUITEM_HYBRIDANALYSIS_QUEUE 101 +#define MENUITEM_HYBRIDANALYSIS_UPLOAD 102 +#define MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE 103 +#define MENUITEM_HYBRIDANALYSIS_UPLOAD_FILE 104 + +#define ENABLE_SERVICE_VIRUSTOTAL 110 +#define MENUITEM_VIRUSTOTAL_QUEUE 111 +#define MENUITEM_VIRUSTOTAL_UPLOAD 112 +#define MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE 113 +#define MENUITEM_VIRUSTOTAL_UPLOAD_FILE 114 + +#define MENUITEM_JOTTI_UPLOAD 120 +#define MENUITEM_JOTTI_UPLOAD_SERVICE 121 + +VOID UploadToOnlineService( + _In_ PPH_STRING FileName, + _In_ ULONG Service + ); + +VOID UploadServiceToOnlineService( + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ ULONG Service + ); + +typedef enum _NETWORK_COLUMN_ID +{ + COLUMN_ID_VIUSTOTAL_PROCESS = 1, + COLUMN_ID_VIUSTOTAL_MODULE = 2, + COLUMN_ID_VIUSTOTAL_SERVICE = 3 +} NETWORK_COLUMN_ID; + +NTSTATUS HashFileAndResetPosition( + _In_ HANDLE FileHandle, + _In_ PLARGE_INTEGER FileSize, + _In_ PH_HASH_ALGORITHM Algorithm, + _Out_ PPH_STRING *HashString + ); + +typedef struct _VIRUSTOTAL_API_RESULT +{ + BOOLEAN Found; + INT64 Positives; + INT64 Total; + PPH_STRING FileHash; +} VIRUSTOTAL_API_RESULT, *PVIRUSTOTAL_API_RESULT; + +extern PPH_LIST VirusTotalList; +extern BOOLEAN VirusTotalScanningEnabled; + +PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalAddCacheResult( + _In_ PPH_STRING FileName, + _In_ PPROCESS_EXTENSION Extension + ); + +PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResult( + _In_ PPH_STRING FileName + ); + +PPH_BYTES VirusTotalGetCachedDbHash( + _In_ PPH_STRINGREF CachedHash + ); + +typedef struct _VT_SYSINT_FILE_REPORT_RESULT +{ + PPH_STRING FileName; + PPH_STRING BaseFileName; + + PPH_STRING Total; + PPH_STRING Positives; + PPH_STRING Resource; + PPH_STRING ScanId; + PPH_STRING Md5; + PPH_STRING Sha1; + PPH_STRING Sha256; + PPH_STRING ScanDate; + PPH_STRING Permalink; + PPH_STRING StatusMessage; + PPH_LIST ScanResults; +} VT_SYSINT_FILE_REPORT_RESULT, *PVT_SYSINT_FILE_REPORT_RESULT; + +PPH_STRING VirusTotalStringToTime( + _In_ PPH_STRING Time + ); + +typedef struct _VIRUSTOTAL_API_RESPONSE +{ + INT64 ResponseCode; + PPH_STRING StatusMessage; + PPH_STRING PermaLink; + PPH_STRING ScanId; +} VIRUSTOTAL_API_RESPONSE, *PVIRUSTOTAL_API_RESPONSE; + +typedef struct _VIRUSTOTAL_FILE_REPORT +{ + INT64 ResponseCode; + PPH_STRING StatusMessage; + PPH_STRING PermaLink; + PPH_STRING ScanId; + + PPH_STRING ScanDate; + PPH_STRING Positives; + PPH_STRING Total; + PPH_LIST ScanResults; +} VIRUSTOTAL_FILE_REPORT, *PVIRUSTOTAL_FILE_REPORT; + +typedef struct _VIRUSTOTAL_FILE_REPORT_RESULT +{ + BOOLEAN Detected; + + PPH_STRING Vendor; + PPH_STRING DetectionName; + PPH_STRING EngineVersion; + PPH_STRING DatabaseDate; +} VIRUSTOTAL_FILE_REPORT_RESULT, *PVIRUSTOTAL_FILE_REPORT_RESULT; + +PVIRUSTOTAL_FILE_REPORT VirusTotalRequestFileReport( + _In_ PPH_STRING FileHash + ); + +VOID VirusTotalFreeFileReport( + _In_ PVIRUSTOTAL_FILE_REPORT FileReport + ); + +PVIRUSTOTAL_API_RESPONSE VirusTotalRequestFileReScan( + _In_ PPH_STRING FileHash + ); + +VOID VirusTotalFreeFileReScan( + _In_ PVIRUSTOTAL_API_RESPONSE FileReScan + ); + +VOID InitializeVirusTotalProcessMonitor( + VOID + ); + +VOID CleanupVirusTotalProcessMonitor( + VOID + ); + +NTSTATUS QueryServiceFileName( + _In_ PPH_STRINGREF ServiceName, + _Out_ PPH_STRING *ServiceFileName, + _Out_ PPH_STRING *ServiceBinaryPath + ); + +#endif diff --git a/plugins/OnlineChecks/options.c b/plugins/OnlineChecks/options.c index 328c96da5b34..6ed1c360e532 100644 --- a/plugins/OnlineChecks/options.c +++ b/plugins/OnlineChecks/options.c @@ -33,42 +33,19 @@ INT_PTR CALLBACK OptionsDlgProc( { case WM_INITDIALOG: { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_VIRUSTOTAL), PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED) ? BST_CHECKED : BST_UNCHECKED); Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_IDC_ENABLE_VIRUSTOTAL_HIGHLIGHT), PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_HIGHLIGHT_DETECTIONS) ? BST_CHECKED : BST_UNCHECKED); } break; - case WM_COMMAND: + case WM_DESTROY: { - switch (LOWORD(wParam)) - { - case IDCANCEL: - { - PhSetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_VIRUSTOTAL)) == BST_CHECKED ? 1 : 0); - - EndDialog(hwndDlg, IDCANCEL); - } - break; - } + PhSetIntegerSetting(SETTING_NAME_VIRUSTOTAL_SCAN_ENABLED, + Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_VIRUSTOTAL)) == BST_CHECKED ? 1 : 0); } break; } return FALSE; } - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - (HWND)Parent, - OptionsDlgProc - ); -} \ No newline at end of file diff --git a/plugins/OnlineChecks/page1.c b/plugins/OnlineChecks/page1.c index 7b8489b72c95..5e37925ba30d 100644 --- a/plugins/OnlineChecks/page1.c +++ b/plugins/OnlineChecks/page1.c @@ -64,8 +64,8 @@ VOID ShowVirusTotalUploadDialog( config.dwCommonButtons = TDCBF_CLOSE_BUTTON; config.hMainIcon = Context->IconLargeHandle; - config.pszWindowTitle = PhaFormatString(L"Processing %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; - config.pszMainInstruction = PhaFormatString(L"Processing %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; + config.pszWindowTitle = PhaFormatString(L"Scanning %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; + config.pszMainInstruction = PhaFormatString(L"Scanning %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; config.cxWidth = 200; config.pfCallback = TaskDialogProcessingCallbackProc; diff --git a/plugins/OnlineChecks/page2.c b/plugins/OnlineChecks/page2.c index aff040808c29..c2714c4a4f2f 100644 --- a/plugins/OnlineChecks/page2.c +++ b/plugins/OnlineChecks/page2.c @@ -46,11 +46,11 @@ HRESULT CALLBACK TaskDialogResultFoundProc( if (context->TaskbarListClass) { ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); - } + } } break; case TDN_BUTTON_CLICKED: - { + { INT buttonID = (INT)wParam; if (buttonID == IDOK) @@ -60,15 +60,27 @@ HRESULT CALLBACK TaskDialogResultFoundProc( } else if (buttonID == IDRETRY) { - if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) - PhShellExecute(hwndDlg, PhGetString(context->ReAnalyseUrl), NULL); +//#ifdef PH_BUILD_API + ShowVirusTotalReScanProgressDialog(context); + return S_FALSE; +//#else +// if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) +// { +// PhShellExecute(hwndDlg, PhGetString(context->ReAnalyseUrl), NULL); +// } +//#endif } else if (buttonID == IDYES) { - if (!PhIsNullOrEmptyString(context->LaunchCommand)) - { - PhShellExecute(hwndDlg, PhGetString(context->LaunchCommand), NULL); - } +//#ifdef PH_BUILD_API + ShowVirusTotalViewReportProgressDialog(context); + return S_FALSE; +//#else +// if (!PhIsNullOrEmptyString(context->LaunchCommand)) +// { +// PhShellExecute(hwndDlg, PhGetString(context->LaunchCommand), NULL); +// } +//#endif } } break; diff --git a/plugins/OnlineChecks/page3.c b/plugins/OnlineChecks/page3.c index c35e7309c7ec..c035932d4c3e 100644 --- a/plugins/OnlineChecks/page3.c +++ b/plugins/OnlineChecks/page3.c @@ -43,7 +43,95 @@ HRESULT CALLBACK TaskDialogProgressCallbackProc( ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_INDETERMINATE); PhReferenceObject(context); - context->UploadThreadHandle = PhCreateThread(0, UploadFileThreadStart, context); + + if (!NT_SUCCESS(PhCreateThreadEx(&context->UploadThreadHandle, UploadFileThreadStart, context))) + { + PhDereferenceObject(context); + } + } + break; + case TDN_BUTTON_CLICKED: + { + if ((INT)wParam == IDCANCEL) + { + if (context->UploadThreadHandle) + { + NtClose(context->UploadThreadHandle); + context->UploadThreadHandle = NULL; + } + } + } + break; + } + + return S_OK; +} + +HRESULT CALLBACK TaskDialogReScanProgressCallbackProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_NAVIGATED: + { + SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + + PhReferenceObject(context); + + if (!NT_SUCCESS(PhCreateThreadEx(&context->UploadThreadHandle, UploadRecheckThreadStart, context))) + { + PhDereferenceObject(context); + } + } + break; + case TDN_BUTTON_CLICKED: + { + if ((INT)wParam == IDCANCEL) + { + if (context->UploadThreadHandle) + { + NtClose(context->UploadThreadHandle); + context->UploadThreadHandle = NULL; + } + } + } + break; + } + + return S_OK; +} + +HRESULT CALLBACK TaskDialogViewReportProgressCallbackProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_NAVIGATED: + { + SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + + PhReferenceObject(context); + + if (!NT_SUCCESS(PhCreateThreadEx(&context->UploadThreadHandle, ViewReportThreadStart, context))) + { + PhDereferenceObject(context); + } } break; case TDN_BUTTON_CLICKED: @@ -85,3 +173,47 @@ VOID ShowVirusTotalProgressDialog( SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } + +VOID ShowVirusTotalReScanProgressDialog( + _In_ PUPLOAD_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_EXPAND_FOOTER_AREA | TDF_ENABLE_HYPERLINKS | TDF_SHOW_PROGRESS_BAR; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.hMainIcon = Context->IconLargeHandle; + + config.pszWindowTitle = PhaFormatString(L"Rescanning %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; + config.pszMainInstruction = PhaFormatString(L"Rescanning %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; + + config.cxWidth = 200; + config.lpCallbackData = (LONG_PTR)Context; + config.pfCallback = TaskDialogReScanProgressCallbackProc; + + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} + +VOID ShowVirusTotalViewReportProgressDialog( + _In_ PUPLOAD_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_EXPAND_FOOTER_AREA | TDF_ENABLE_HYPERLINKS | TDF_SHOW_PROGRESS_BAR; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.hMainIcon = Context->IconLargeHandle; + + config.pszWindowTitle = PhaFormatString(L"Locating analysis for %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; + config.pszMainInstruction = PhaFormatString(L"Locating analysis for %s...", PhGetStringOrEmpty(Context->BaseFileName))->Buffer; + + config.cxWidth = 200; + config.lpCallbackData = (LONG_PTR)Context; + config.pfCallback = TaskDialogViewReportProgressCallbackProc; + + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} diff --git a/plugins/OnlineChecks/resource.h b/plugins/OnlineChecks/resource.h index e0adf0b1da02..bc4a9fd77e37 100644 --- a/plugins/OnlineChecks/resource.h +++ b/plugins/OnlineChecks/resource.h @@ -1,21 +1,25 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by OnlineChecks.rc -// -#define IDD_OPTIONS 102 -#define IDC_ENABLE_VIRUSTOTAL 1001 -#define IDC_ENABLE_IDC_ENABLE_VIRUSTOTAL_HIGHLIGHT 1002 -#define IDC_CHECK1 1010 -#define IDC_EDIT1 1011 -#define IDC_COMBO1 1012 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1013 -#define _APS_NEXT_SYMED_VALUE 103 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by OnlineChecks.rc +// +#define IDD_OPTIONS 102 +#define IDD_VIRUSTOTAL 103 +#define IDC_ENABLE_VIRUSTOTAL 1001 +#define IDC_ENABLE_IDC_ENABLE_VIRUSTOTAL_HIGHLIGHT 1002 +#define IDC_CHECK1 1010 +#define IDC_EDIT1 1011 +#define IDC_COMBO1 1012 +#define IDC_REFRESH 1013 +#define IDC_LIST1 1014 +#define IDC_SCANRESULTS 1014 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1015 +#define _APS_NEXT_SYMED_VALUE 103 +#endif +#endif diff --git a/plugins/OnlineChecks/upload.c b/plugins/OnlineChecks/upload.c index e0d15a71f12b..1efbdde3153e 100644 --- a/plugins/OnlineChecks/upload.c +++ b/plugins/OnlineChecks/upload.c @@ -1,1337 +1,1558 @@ -/* - * Process Hacker Plugins - - * Online Checks Plugin - * - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "onlnchk.h" -#include -#include - -PPH_OBJECT_TYPE UploadContextType = NULL; -SERVICE_INFO UploadServiceInfo[] = -{ - { MENUITEM_VIRUSTOTAL_UPLOAD, L"www.virustotal.com", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"???", L"file" }, - { MENUITEM_JOTTI_UPLOAD, L"virusscan.jotti.org", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, -}; - -BOOL ReadRequestString( - _In_ HINTERNET Handle, - _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, - _Out_ ULONG *DataLength - ) -{ - BYTE buffer[PAGE_SIZE]; - PSTR data; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; - - allocatedLength = sizeof(buffer); - data = (PSTR)PhAllocate(allocatedLength); - dataLength = 0; - - // Zero the buffer - memset(buffer, 0, PAGE_SIZE); - - while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength)) - { - if (returnLength == 0) - break; - - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - // Copy the returned buffer into our pointer - memcpy(data + dataLength, buffer, returnLength); - // Zero the returned buffer for the next loop - //memset(buffer, 0, returnLength); - - dataLength += returnLength; - } - - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - // Ensure that the buffer is null-terminated. - data[dataLength] = 0; - - *DataLength = dataLength; - *Data = data; - - return TRUE; -} - -VOID RaiseUploadError( - _In_ PUPLOAD_CONTEXT Context, - _In_ PWSTR Error, - _In_ ULONG ErrorCode - ) -{ - PWSTR errorMessage = NULL; - PPH_STRING message; - - if (!Context->DialogHandle) - return; - - if (message = PhGetMessage(GetModuleHandle(L"winhttp.dll"), 0xb, GetUserDefaultLangID(), ErrorCode)) - { - PhTrimToNullTerminatorString(message); - } - - // Remove any trailing newline - if (message && message->Length >= 2 * sizeof(WCHAR) && - message->Buffer[message->Length / sizeof(WCHAR) - 2] == '\r' && - message->Buffer[message->Length / sizeof(WCHAR) - 1] == '\n') - { - PhMoveReference(&message, PhCreateStringEx(message->Buffer, message->Length - 2 * sizeof(WCHAR))); - } - - if (message) - { - PhMoveReference(&Context->ErrorString, PhFormatString( - L"[%lu] %s", - ErrorCode, - PhGetString(message) - )); - - PhDereferenceObject(message); - } - else - { - PhMoveReference(&Context->ErrorString, PhFormatString( - L"[%lu] %s", - ErrorCode, - Error - )); - } - - PostMessage(Context->DialogHandle, UM_ERROR, 0, 0); - -} - -PSERVICE_INFO GetUploadServiceInfo( - _In_ ULONG Id - ) -{ - ULONG i; - - for (i = 0; i < ARRAYSIZE(UploadServiceInfo); i++) - { - if (UploadServiceInfo[i].Id == Id) - return &UploadServiceInfo[i]; - } - - return NULL; -} - -VOID UploadContextDeleteProcedure( - _In_ PVOID Object, - _In_ ULONG Flags - ) -{ - PUPLOAD_CONTEXT context = Object; - - if (context->TaskbarListClass) - { - ITaskbarList3_Release(context->TaskbarListClass); - context->TaskbarListClass = NULL; - } - - if (context->UploadThreadHandle) - { - NtClose(context->UploadThreadHandle); - context->UploadThreadHandle = NULL; - } - - if (context->HttpHandle) - { - WinHttpCloseHandle(context->HttpHandle); - context->HttpHandle = NULL; - } - - if (context->IconLargeHandle) - { - DestroyIcon(context->IconLargeHandle); - context->IconLargeHandle = NULL; - } - - if (context->IconSmallHandle) - { - DestroyIcon(context->IconSmallHandle); - context->IconSmallHandle = NULL; - } - - PhClearReference(&context->ErrorString); - PhClearReference(&context->KeyString); - PhClearReference(&context->FileName); - PhClearReference(&context->BaseFileName); - PhClearReference(&context->WindowFileName); - PhClearReference(&context->LaunchCommand); - PhClearReference(&context->Detected); - PhClearReference(&context->MaxDetected); - PhClearReference(&context->UploadUrl); - PhClearReference(&context->ReAnalyseUrl); - PhClearReference(&context->FirstAnalysisDate); - PhClearReference(&context->LastAnalysisDate); - PhClearReference(&context->LastAnalysisUrl); - PhClearReference(&context->LastAnalysisAgo); -} - -VOID TaskDialogFreeContext( - _In_ PUPLOAD_CONTEXT Context - ) -{ - // Reset Taskbar progress state(s) - if (Context->TaskbarListClass) - ITaskbarList3_SetProgressState(Context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); - - if (Context->TaskbarListClass) - ITaskbarList3_SetProgressState(Context->TaskbarListClass, Context->DialogHandle, TBPF_NOPROGRESS); - - PhDereferenceObject(Context); -} - -VOID TaskDialogCreateIcons( - _In_ PUPLOAD_CONTEXT Context - ) -{ - Context->IconLargeHandle = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); - Context->IconSmallHandle = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); - - SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); -} - -PPH_STRING UpdateVersionString( - VOID - ) -{ - ULONG majorVersion; - ULONG minorVersion; - ULONG revisionVersion; - PPH_STRING currentVersion; - PPH_STRING versionHeader = NULL; - - PhGetPhVersionNumbers(&majorVersion, &minorVersion, NULL, &revisionVersion); - - if (currentVersion = PhFormatString(L"%lu.%lu.%lu", majorVersion, minorVersion, revisionVersion)) - { - versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer); - PhDereferenceObject(currentVersion); - } - - return versionHeader; -} - -NTSTATUS HashFileAndResetPosition( - _In_ HANDLE FileHandle, - _In_ PLARGE_INTEGER FileSize, - _In_ PH_HASH_ALGORITHM Algorithm, - _Out_ PPH_STRING *HashString - ) -{ - NTSTATUS status; - IO_STATUS_BLOCK iosb; - PH_HASH_CONTEXT hashContext; - PPH_STRING hashString = NULL; - ULONG64 bytesRemaining; - FILE_POSITION_INFORMATION positionInfo; - LONG priority; - LONG newpriority; - IO_PRIORITY_HINT ioPriority; - IO_PRIORITY_HINT newioPriority; - UCHAR buffer[PAGE_SIZE]; - - bytesRemaining = FileSize->QuadPart; - - newpriority = THREAD_PRIORITY_LOWEST; - newioPriority = IoPriorityVeryLow; - NtQueryInformationThread(NtCurrentThread(), ThreadBasePriority, &priority, sizeof(LONG), NULL); - NtQueryInformationThread(NtCurrentThread(), ThreadIoPriority, &ioPriority, sizeof(IO_PRIORITY_HINT), NULL); - NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, &newpriority, sizeof(LONG)); - NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, &newioPriority, sizeof(IO_PRIORITY_HINT)); - - PhInitializeHash(&hashContext, Algorithm); - - while (bytesRemaining) - { - status = NtReadFile( - FileHandle, - NULL, - NULL, - NULL, - &iosb, - buffer, - sizeof(buffer), - NULL, - NULL - ); - - if (!NT_SUCCESS(status)) - break; - - PhUpdateHash(&hashContext, buffer, (ULONG)iosb.Information); - bytesRemaining -= (ULONG)iosb.Information; - } - - if (status == STATUS_END_OF_FILE) - status = STATUS_SUCCESS; - - if (NT_SUCCESS(status)) - { - UCHAR hash[32]; - - switch (Algorithm) - { - case Md5HashAlgorithm: - PhFinalHash(&hashContext, hash, 16, NULL); - *HashString = PhBufferToHexString(hash, 16); - break; - case Sha1HashAlgorithm: - PhFinalHash(&hashContext, hash, 20, NULL); - *HashString = PhBufferToHexString(hash, 20); - break; - case Sha256HashAlgorithm: - PhFinalHash(&hashContext, hash, 32, NULL); - *HashString = PhBufferToHexString(hash, 32); - break; - } - - positionInfo.CurrentByteOffset.QuadPart = 0; - status = NtSetInformationFile( - FileHandle, - &iosb, - &positionInfo, - sizeof(FILE_POSITION_INFORMATION), - FilePositionInformation - ); - } - - NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, &priority, sizeof(LONG)); - NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, &ioPriority, sizeof(IO_PRIORITY_HINT)); - - return status; -} - -PPH_BYTES PerformSubRequest( - _In_ PUPLOAD_CONTEXT Context, - _In_ PWSTR HostName, - _In_ INTERNET_PORT HostPort, - _In_ ULONG HostFlags, - _In_ PWSTR ObjectName - ) -{ - PPH_BYTES result = NULL; - HINTERNET connectHandle = NULL; - HINTERNET requestHandle = NULL; - - if (!(connectHandle = WinHttpConnect( - Context->HttpHandle, - HostName, - HostPort, - 0 - ))) - { - RaiseUploadError(Context, L"Unable to connect to the service", GetLastError()); - goto CleanupExit; - } - - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, - NULL, - ObjectName, - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | HostFlags - ))) - { - RaiseUploadError(Context, L"Unable to create the request", GetLastError()); - goto CleanupExit; - } - - if (!WinHttpSendRequest( - requestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { - RaiseUploadError(Context, L"Unable to send the request", GetLastError()); - goto CleanupExit; - } - - if (WinHttpReceiveResponse(requestHandle, NULL)) - { - PSTR data; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; - BYTE buffer[PAGE_SIZE]; - - allocatedLength = sizeof(buffer); - data = PhAllocate(allocatedLength); - dataLength = 0; - - while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength)) - { - if (returnLength == 0) - break; - - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - data = PhReAllocate(data, allocatedLength); - } - - memcpy(data + dataLength, buffer, returnLength); - dataLength += returnLength; - } - - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - data = PhReAllocate(data, allocatedLength); - } - - data[dataLength] = 0; - - result = PhCreateBytesEx(data, dataLength); - } - else - { - RaiseUploadError(Context, L"Unable to receive the response", GetLastError()); - goto CleanupExit; - } - -CleanupExit: - - if (requestHandle) - WinHttpCloseHandle(requestHandle); - - if (connectHandle) - WinHttpCloseHandle(connectHandle); - - return result; -} - -NTSTATUS UploadFileThreadStart( - _In_ PVOID Parameter - ) -{ - NTSTATUS status = STATUS_SUCCESS; - ULONG httpStatus = 0; - ULONG httpStatusLength = sizeof(ULONG); - ULONG httpPostSeed = 0; - ULONG totalUploadLength = 0; - ULONG totalUploadedLength = 0; - ULONG totalPostHeaderWritten = 0; - ULONG totalPostFooterWritten = 0; - ULONG totalWriteLength = 0; - LARGE_INTEGER timeNow; - LARGE_INTEGER timeStart; - ULONG64 timeTicks = 0; - ULONG64 timeBitsPerSecond = 0; - HANDLE fileHandle = NULL; - HINTERNET connectHandle = NULL; - HINTERNET requestHandle = NULL; - IO_STATUS_BLOCK isb; - URL_COMPONENTS httpComponents = { sizeof(URL_COMPONENTS) }; - PPH_STRING httpHostName = NULL; - PPH_STRING httpHostPath = NULL; - PSERVICE_INFO serviceInfo = NULL; - PPH_STRING postBoundary = NULL; - PPH_BYTES asciiPostData = NULL; - PPH_BYTES asciiFooterData = NULL; - PH_STRING_BUILDER httpRequestHeaders = { 0 }; - PH_STRING_BUILDER httpPostHeader = { 0 }; - PH_STRING_BUILDER httpPostFooter = { 0 }; - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; - - serviceInfo = GetUploadServiceInfo(context->Service); - - // Set lengths to non-zero enabling these params to be cracked. - httpComponents.dwSchemeLength = (ULONG)-1; - httpComponents.dwHostNameLength = (ULONG)-1; - httpComponents.dwUrlPathLength = (ULONG)-1; - - if (!WinHttpCrackUrl( - PhGetStringOrEmpty(context->UploadUrl), - 0, - 0, - &httpComponents - )) - { - goto CleanupExit; - } - - // Create the Host string. - if (PhIsNullOrEmptyString(httpHostName = PhCreateStringEx( - httpComponents.lpszHostName, - httpComponents.dwHostNameLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - - // Create the Path string. - if (PhIsNullOrEmptyString(httpHostPath = PhCreateStringEx( - httpComponents.lpszUrlPath, - httpComponents.dwUrlPathLength * sizeof(WCHAR) - ))) - { - goto CleanupExit; - } - - if (!NT_SUCCESS(status = PhCreateFileWin32( - &fileHandle, - PhGetStringOrEmpty(context->FileName), - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status)); - goto CleanupExit; - } - - if (!(connectHandle = WinHttpConnect( - context->HttpHandle, - serviceInfo->HostName, - serviceInfo->HostPort, - 0 - ))) - { - RaiseUploadError(context, L"Unable to connect to the service", GetLastError()); - goto CleanupExit; - } - - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, - L"POST", - PhGetStringOrEmpty(httpHostPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | serviceInfo->HostFlags - ))) - { - RaiseUploadError(context, L"Unable to create the request", GetLastError()); - goto CleanupExit; - } - - PhInitializeStringBuilder(&httpRequestHeaders, DOS_MAX_PATH_LENGTH); - PhInitializeStringBuilder(&httpPostHeader, DOS_MAX_PATH_LENGTH); - PhInitializeStringBuilder(&httpPostFooter, DOS_MAX_PATH_LENGTH); - - // build request boundary string - postBoundary = PhFormatString( - L"------------------------%I64u", - (ULONG64)RtlRandomEx(&httpPostSeed) | ((ULONG64)RtlRandomEx(&httpPostSeed) << 31) - ); - - // build request header string - PhAppendFormatStringBuilder( - &httpRequestHeaders, - L"Content-Type: multipart/form-data; boundary=%s\r\n", - postBoundary->Buffer - ); - - if (context->Service == MENUITEM_JOTTI_UPLOAD) - { - // POST boundary header - PhAppendFormatStringBuilder( - &httpPostHeader, - L"\r\n--%s\r\n", - postBoundary->Buffer - ); - PhAppendFormatStringBuilder( - &httpPostHeader, - L"Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n268435456\r\n" - ); - PhAppendFormatStringBuilder( - &httpPostHeader, - L"--%s\r\n", - postBoundary->Buffer - ); - PhAppendFormatStringBuilder( - &httpPostHeader, - L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", - serviceInfo->FileNameFieldName, - PhGetStringOrEmpty(context->BaseFileName) - ); - PhAppendFormatStringBuilder( - &httpPostHeader, - L"Content-Type: application/x-msdownload\r\n\r\n" - ); - - // POST boundary footer - PhAppendFormatStringBuilder( - &httpPostFooter, - L"\r\n--%s--\r\n", - postBoundary->Buffer - ); - } - else - { - // POST boundary header - PhAppendFormatStringBuilder( - &httpPostHeader, - L"--%s\r\n", - postBoundary->Buffer - ); - PhAppendFormatStringBuilder( - &httpPostHeader, - L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", - serviceInfo->FileNameFieldName, - PhGetStringOrEmpty(context->BaseFileName) - ); - PhAppendFormatStringBuilder( - &httpPostHeader, - L"Content-Type: application/octet-stream\r\n\r\n" - ); - - // POST boundary footer - PhAppendFormatStringBuilder( - &httpPostFooter, - L"\r\n--%s--\r\n\r\n", - postBoundary->Buffer - ); - } - - // add headers - if (!WinHttpAddRequestHeaders( - requestHandle, - httpRequestHeaders.String->Buffer, - -1L, - WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD - )) - { - RaiseUploadError(context, L"Unable to add request headers", GetLastError()); - goto CleanupExit; - } - - if (context->Service == MENUITEM_JOTTI_UPLOAD) - { - PPH_STRING ajaxHeader = PhCreateString(L"X-Requested-With: XMLHttpRequest"); - - WinHttpAddRequestHeaders( - requestHandle, - ajaxHeader->Buffer, - (ULONG)ajaxHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); - - PhDereferenceObject(ajaxHeader); - } - - // Calculate the total request length. - totalUploadLength = (ULONG)httpPostHeader.String->Length / sizeof(WCHAR) + context->TotalFileLength + (ULONG)httpPostFooter.String->Length / sizeof(WCHAR); - - // Send the request. - if (!WinHttpSendRequest( - requestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - totalUploadLength, - 0 - )) - { - RaiseUploadError(context, L"Unable to send the request", GetLastError()); - goto CleanupExit; - } - - // Convert to ASCII - asciiPostData = PhConvertUtf16ToAscii(httpPostHeader.String->Buffer, '-'); - asciiFooterData = PhConvertUtf16ToAscii(httpPostFooter.String->Buffer, '-'); - - // Start the clock. - PhQuerySystemTime(&timeStart); - - // Write the header - if (!WinHttpWriteData( - requestHandle, - asciiPostData->Buffer, - (ULONG)asciiPostData->Length, - &totalPostHeaderWritten - )) - { - RaiseUploadError(context, L"Unable to write the post header", GetLastError()); - goto CleanupExit; - } - - PPH_STRING msg = PhFormatString(L"Uploading %s...", PhGetStringOrEmpty(context->BaseFileName)); - SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0); - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)PhGetString(msg)); - PhDereferenceObject(msg); - - if (context->TaskbarListClass) - { - ITaskbarList3_SetProgressState( - context->TaskbarListClass, - PhMainWndHandle, - TBPF_NORMAL - ); - } - - while (TRUE) - { - BYTE buffer[PAGE_SIZE]; - - if (!context->UploadThreadHandle) - goto CleanupExit; - - if (!NT_SUCCESS(status = NtReadFile( - fileHandle, - NULL, - NULL, - NULL, - &isb, - buffer, - PAGE_SIZE, - NULL, - NULL - ))) - { - break; - } - - if (!WinHttpWriteData(requestHandle, buffer, (ULONG)isb.Information, &totalWriteLength)) - { - RaiseUploadError(context, L"Unable to upload the file data", GetLastError()); - goto CleanupExit; - } - - PhQuerySystemTime(&timeNow); - - totalUploadedLength += totalWriteLength; - timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; - timeBitsPerSecond = totalUploadedLength / __max(timeTicks, 1); - - { - FLOAT percent = ((FLOAT)totalUploadedLength / context->TotalFileLength * 100); - PPH_STRING totalLength = PhFormatSize(context->TotalFileLength, -1); - PPH_STRING totalUploaded = PhFormatSize(totalUploadedLength, -1); - PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1); - PPH_STRING statusMessage = PhFormatString( - L"Uploaded: %s of %s (%.0f%%)\r\nSpeed: %s/s", - PhGetStringOrEmpty(totalUploaded), - PhGetStringOrEmpty(totalLength), - percent, - PhGetStringOrEmpty(totalSpeed) - ); - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)statusMessage->Buffer); - SendMessage(context->DialogHandle, TDM_SET_PROGRESS_BAR_POS, (WPARAM)percent, 0); - - if (context->TaskbarListClass) - { - ITaskbarList3_SetProgressValue( - context->TaskbarListClass, - context->DialogHandle, - totalUploadedLength, - context->TotalFileLength - ); - } - - PhDereferenceObject(statusMessage); - PhDereferenceObject(totalSpeed); - PhDereferenceObject(totalUploaded); - PhDereferenceObject(totalLength); - } - } - - // Write the footer bytes - if (!WinHttpWriteData( - requestHandle, - asciiFooterData->Buffer, - (ULONG)asciiFooterData->Length, - &totalPostFooterWritten - )) - { - RaiseUploadError(context, L"Unable to write the post footer", GetLastError()); - goto CleanupExit; - } - - if (!WinHttpReceiveResponse(requestHandle, NULL)) - { - RaiseUploadError(context, L"Unable to receive the response", GetLastError()); - goto CleanupExit; - } - - if (!WinHttpQueryHeaders( - requestHandle, - WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, - NULL, - &httpStatus, - &httpStatusLength, - NULL - )) - { - RaiseUploadError(context, L"Unable to query http headers", GetLastError()); - goto CleanupExit; - } - - if (httpStatus == HTTP_STATUS_OK || httpStatus == HTTP_STATUS_REDIRECT_METHOD || httpStatus == HTTP_STATUS_REDIRECT) - { - switch (context->Service) - { - case MENUITEM_VIRUSTOTAL_UPLOAD: - { - PSTR buffer = NULL; - PSTR redirectUrl; - ULONG bufferLength; - PVOID jsonRootObject; - - if (!ReadRequestString(requestHandle, &buffer, &bufferLength)) - { - RaiseUploadError(context, L"Unable to complete the request", GetLastError()); - goto CleanupExit; - } - - if (jsonRootObject = CreateJsonParser(buffer)) - { - if (!GetJsonValueAsUlong(jsonRootObject, "response_code")) - goto CleanupExit; - - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "resource")); - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "scan_id")); - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "sha256")); - // PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "verbose_msg")); - - if (redirectUrl = GetJsonValueAsString(jsonRootObject, "permalink")) - { - PhMoveReference(&context->LaunchCommand, PhZeroExtendToUtf16(redirectUrl)); - } - - CleanupJsonParser(jsonRootObject); - } - else - { - ULONG httpUrlLength = 0; - - if (!WinHttpQueryOption(requestHandle, WINHTTP_OPTION_URL, NULL, &httpUrlLength)) - { - PPH_STRING httpUrlString = PhCreateStringEx(NULL, httpUrlLength); - - if (WinHttpQueryOption(requestHandle, WINHTTP_OPTION_URL, httpUrlString->Buffer, &httpUrlLength)) - { - PhSwapReference(&context->LaunchCommand, httpUrlString); - } - - PhDereferenceObject(httpUrlString); - } - } - } - break; - case MENUITEM_JOTTI_UPLOAD: - { - PSTR buffer = NULL; - PSTR redirectUrl; - ULONG bufferLength; - PVOID rootJsonObject; - - if (!ReadRequestString(requestHandle, &buffer, &bufferLength)) - { - RaiseUploadError(context, L"Unable to complete the request", GetLastError()); - goto CleanupExit; - } - - if (rootJsonObject = CreateJsonParser(buffer)) - { - if (redirectUrl = GetJsonValueAsString(rootJsonObject, "redirecturl")) - { - PhMoveReference(&context->LaunchCommand, PhFormatString(L"http://virusscan.jotti.org%hs", redirectUrl)); - } - - CleanupJsonParser(rootJsonObject); - } - } - break; - } - } - else - { - RaiseUploadError(context, L"Unable to complete the request", STATUS_FVE_PARTIAL_METADATA); - goto CleanupExit; - } - - if (!context->UploadThreadHandle) - goto CleanupExit; - - if (!PhIsNullOrEmptyString(context->LaunchCommand)) - { - PostMessage(context->DialogHandle, UM_LAUNCH, 0, 0); - } - else - { - RaiseUploadError(context, L"Unable to complete the Launch request (please try again after a few minutes)", ERROR_INVALID_DATA); - } - -CleanupExit: - - // Reset Taskbar progress state(s) - if (context->TaskbarListClass) - { - ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); - ITaskbarList3_SetProgressState(context->TaskbarListClass, context->DialogHandle, TBPF_NOPROGRESS); - } - - if (postBoundary) - PhDereferenceObject(postBoundary); - - if (asciiFooterData) - PhDereferenceObject(asciiFooterData); - - if (asciiPostData) - PhDereferenceObject(asciiPostData); - - if (httpPostFooter.String) - PhDeleteStringBuilder(&httpPostFooter); - - if (httpPostHeader.String) - PhDeleteStringBuilder(&httpPostHeader); - - if (httpRequestHeaders.String) - PhDeleteStringBuilder(&httpRequestHeaders); - - if (fileHandle) - NtClose(fileHandle); - - PhDereferenceObject(context); - - return status; -} - -NTSTATUS UploadCheckThreadStart( - _In_ PVOID Parameter - ) -{ - NTSTATUS status = STATUS_SUCCESS; - BOOLEAN fileExists = FALSE; - BOOLEAN success = FALSE; - LARGE_INTEGER fileSize64; - PPH_BYTES subRequestBuffer = NULL; - HINTERNET connectHandle = NULL; - HINTERNET requestHandle = NULL; - PSERVICE_INFO serviceInfo = NULL; - PPH_STRING hashString = NULL; - PPH_STRING subObjectName = NULL; - HANDLE fileHandle = NULL; - PPH_STRING phVersion = NULL; - PPH_STRING userAgent = NULL; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; - - //context->Extension = VirusTotalGetCachedResult(context->FileName); - serviceInfo = GetUploadServiceInfo(context->Service); - - if (!NT_SUCCESS(status = PhCreateFileWin32( - &fileHandle, - context->FileName->Buffer, - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status)); - goto CleanupExit; - } - - if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize64))) - { - if (context->Service == MENUITEM_VIRUSTOTAL_UPLOAD) - { - if (fileSize64.QuadPart < 32 * 1024 * 1024) - { - context->VtApiUpload = TRUE; - } - - if (fileSize64.QuadPart > 128 * 1024 * 1024) // 128 MB - { - RaiseUploadError(context, L"The file is too large (over 128 MB)", ERROR_FILE_TOO_LARGE); - goto CleanupExit; - } - } - else - { - if (fileSize64.QuadPart > 20 * 1024 * 1024) // 20 MB - { - RaiseUploadError(context, L"The file is too large (over 20 MB)", ERROR_FILE_TOO_LARGE); - goto CleanupExit; - } - } - - context->FileSize = PhFormatSize(fileSize64.QuadPart, -1); - context->TotalFileLength = fileSize64.LowPart; - } - - // Create a user agent string. - phVersion = PhGetPhVersion(); - userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); - - if (!(context->HttpHandle = WinHttpOpen( - userAgent->Buffer, - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { - goto CleanupExit; - } - - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; - - WinHttpSetOption(context->HttpHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); - } - - switch (context->Service) - { - case MENUITEM_VIRUSTOTAL_UPLOAD: - { - PSTR uploadUrl = NULL; - PSTR quote = NULL; - PVOID rootJsonObject; - - if (!NT_SUCCESS(status = HashFileAndResetPosition(fileHandle, &fileSize64, Sha256HashAlgorithm, &hashString))) - { - RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status)); - goto CleanupExit; - } - - subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", hashString->Buffer); - - //if (PhIsNullOrEmptyString(context->KeyString)) - // Create the default launch URL - PhMoveReference(&context->LaunchCommand, PhFormatString( - L"https://www.virustotal.com/file/%s/analysis/", - PhGetString(hashString) - )); - - if (!(subRequestBuffer = PerformSubRequest( - context, - serviceInfo->HostName, - serviceInfo->HostPort, - serviceInfo->HostFlags, - subObjectName->Buffer - ))) - { - goto CleanupExit; - } - - if (rootJsonObject = CreateJsonParser(subRequestBuffer->Buffer)) - { - if (context->FileExists = GetJsonValueAsBool(rootJsonObject, "file_exists")) - { - INT64 detected = 0; - INT64 detectedMax = 0; - PVOID detectionRatio; - - if (detectionRatio = JsonGetObject(rootJsonObject, "detection_ratio")) - { - detected = GetJsonArrayUlong(detectionRatio, 0); - detectedMax = GetJsonArrayUlong(detectionRatio, 1); - } - - context->Detected = PhFormatString(L"%I64d", detected); - context->MaxDetected = PhFormatString(L"%I64d", detectedMax); - context->UploadUrl = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "upload_url")); - context->ReAnalyseUrl = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "reanalyse_url")); - context->LastAnalysisUrl = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "last_analysis_url")); - context->FirstAnalysisDate = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "first_analysis_date")); - context->LastAnalysisDate = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "last_analysis_date")); - context->LastAnalysisAgo = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "last_analysis_ago")); - - PhMoveReference(&context->FirstAnalysisDate, VirusTotalStringToTime(context->FirstAnalysisDate)); - PhMoveReference(&context->LastAnalysisDate, VirusTotalStringToTime(context->LastAnalysisDate)); - - if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) - { - PhMoveReference(&context->ReAnalyseUrl, PhFormatString( - L"%s%s", - L"https://www.virustotal.com/", - PhGetString(context->ReAnalyseUrl) - )); - } - - if (context->VtApiUpload && !PhIsNullOrEmptyString(context->KeyString)) - { - PhMoveReference(&context->UploadUrl, PhFormatString( - L"%s%s?apikey=%s&resource=%s", - L"https://www.virustotal.com/", - L"/vtapi/v2/file/scan", - PhGetString(context->KeyString), - PhGetString(hashString) - )); - } - - if (!PhIsNullOrEmptyString(context->UploadUrl)) - { - success = TRUE; - PostMessage(context->DialogHandle, UM_EXISTS, 0, 0); - } - } - else - { - context->UploadUrl = PhZeroExtendToUtf16(GetJsonValueAsString(rootJsonObject, "upload_url")); - - // No file found... Start the upload. - if (!PhIsNullOrEmptyString(context->UploadUrl)) - { - success = TRUE; - PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); - } - } - - CleanupJsonParser(rootJsonObject); - } - } - break; - case MENUITEM_JOTTI_UPLOAD: - { - // Create the default upload URL - context->UploadUrl = PhFormatString(L"https://virusscan.jotti.org%s", serviceInfo->UploadObjectName); - - // No file found... Start the upload. - if (!PhIsNullOrEmptyString(context->UploadUrl)) - { - success = TRUE; - PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); - } - } - break; - } - -CleanupExit: - - if (context->DialogHandle && !success) - { - PostMessage(context->DialogHandle, UM_ERROR, 0, 0); - } - - PhClearReference(&phVersion); - PhClearReference(&userAgent); - PhClearReference(&hashString); - PhClearReference(&subObjectName); - PhClearReference(&subRequestBuffer); - - if (requestHandle) - WinHttpCloseHandle(requestHandle); - - if (connectHandle) - WinHttpCloseHandle(connectHandle); - - if (fileHandle) - NtClose(fileHandle); - - PhDereferenceObject(context); - - return status; -} - -LRESULT CALLBACK TaskDialogSubclassProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)dwRefData; - - switch (uMsg) - { - case WM_NCDESTROY: - { - TaskDialogFreeContext(context); - - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case UM_SHOWDIALOG: - { - if (IsMinimized(hwndDlg)) - ShowWindow(hwndDlg, SW_RESTORE); - else - ShowWindow(hwndDlg, SW_SHOW); - - SetForegroundWindow(hwndDlg); - } - break; - case UM_UPLOAD: - { - ShowVirusTotalProgressDialog(context); - } - break; - case UM_EXISTS: - { - ShowFileFoundDialog(context); - } - break; - case UM_LAUNCH: - { - if (!PhIsNullOrEmptyString(context->LaunchCommand)) - { - PhShellExecute(hwndDlg, context->LaunchCommand->Buffer, NULL); - } - - PostQuitMessage(0); - } - break; - case UM_ERROR: - { - VirusTotalShowErrorDialog(context); - } - break; - } - - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); -} - -HRESULT CALLBACK TaskDialogBootstrapCallback( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_CREATED: - { - context->DialogHandle = hwndDlg; - - // Center the update window on PH if it's visible else we center on the desktop. - PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); - - // Create the Taskdialog icons - TaskDialogCreateIcons(context); - - if (SUCCEEDED(CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, &context->TaskbarListClass))) - { - if (!SUCCEEDED(ITaskbarList3_HrInit(context->TaskbarListClass))) - { - ITaskbarList3_Release(context->TaskbarListClass); - context->TaskbarListClass = NULL; - } - } - - // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); - - ShowVirusTotalUploadDialog(context); - } - break; - } - - return S_OK; -} - -NTSTATUS ShowUpdateDialogThread( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; - PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; - BOOL checked = FALSE; - - PhInitializeAutoPool(&autoPool); - - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; - config.pszContent = L"Initializing..."; - config.lpCallbackData = (LONG_PTR)context; - config.pfCallback = TaskDialogBootstrapCallback; - - // Start TaskDialog bootstrap - TaskDialogIndirect(&config, NULL, NULL, &checked); - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -VOID UploadToOnlineService( - _In_ PPH_STRING FileName, - _In_ ULONG Service - ) -{ - static PH_INITONCE initOnce = PH_INITONCE_INIT; - - HANDLE dialogThread; - PUPLOAD_CONTEXT context; - - if (PhBeginInitOnce(&initOnce)) - { - UploadContextType = PhCreateObjectType(L"OnlineChecksObjectType", 0, UploadContextDeleteProcedure); - PhEndInitOnce(&initOnce); - } - - context = (PUPLOAD_CONTEXT)PhCreateObject(sizeof(UPLOAD_CONTEXT), UploadContextType); - memset(context, 0, sizeof(UPLOAD_CONTEXT)); - - PhReferenceObject(FileName); - context->Service = Service; - context->FileName = FileName; - context->BaseFileName = PhGetBaseName(context->FileName); - context->KeyString = PhCreateString(VIRUSTOTAL_APIKEY); - - if (dialogThread = PhCreateThread(0, ShowUpdateDialogThread, (PVOID)context)) - { - PostMessage(dialogThread, UM_SHOWDIALOG, 0, 0); - NtClose(dialogThread); - } -} +/* + * Process Hacker Plugins - + * Online Checks Plugin + * + * Copyright (C) 2016-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "onlnchk.h" + +PPH_OBJECT_TYPE UploadContextType = NULL; +PH_INITONCE UploadContextTypeInitOnce = PH_INITONCE_INIT; +SERVICE_INFO UploadServiceInfo[] = +{ + { MENUITEM_HYBRIDANALYSIS_UPLOAD, L"www.hybrid-analysis.com", L"/api/v2/submit/file", L"file" }, + { MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE, L"www.hybrid-analysis.com", L"/api/v2/submit/file", L"file" }, + { MENUITEM_VIRUSTOTAL_UPLOAD, L"www.virustotal.com", L"???", L"file" }, + { MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE, L"www.virustotal.com", L"???", L"file" }, + { MENUITEM_JOTTI_UPLOAD, L"virusscan.jotti.org", L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, + { MENUITEM_JOTTI_UPLOAD_SERVICE, L"virusscan.jotti.org", L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, +}; + +VOID RaiseUploadError( + _In_ PUPLOAD_CONTEXT Context, + _In_ PWSTR Error, + _In_ ULONG ErrorCode + ) +{ + PPH_STRING message; + + if (!Context->DialogHandle) + return; + + if (message = PhHttpSocketGetErrorMessage(ErrorCode)) + { + PhMoveReference(&Context->ErrorString, PhFormatString( + L"[%lu] %s", + ErrorCode, + PhGetString(message) + )); + + PhDereferenceObject(message); + } + else + { + PhMoveReference(&Context->ErrorString, PhFormatString( + L"[%lu] %s", + ErrorCode, + Error + )); + } + + PostMessage(Context->DialogHandle, UM_ERROR, 0, 0); +} + +PSERVICE_INFO GetUploadServiceInfo( + _In_ ULONG Id + ) +{ + ULONG i; + + for (i = 0; i < ARRAYSIZE(UploadServiceInfo); i++) + { + if (UploadServiceInfo[i].Id == Id) + return &UploadServiceInfo[i]; + } + + return NULL; +} + +VOID UploadContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PUPLOAD_CONTEXT context = Object; + + if (context->TaskbarListClass) + { + ITaskbarList3_Release(context->TaskbarListClass); + context->TaskbarListClass = NULL; + } + + if (context->UploadThreadHandle) + { + NtClose(context->UploadThreadHandle); + context->UploadThreadHandle = NULL; + } + + PhClearReference(&context->ErrorString); + PhClearReference(&context->FileName); + PhClearReference(&context->BaseFileName); + PhClearReference(&context->LaunchCommand); + PhClearReference(&context->Detected); + PhClearReference(&context->MaxDetected); + PhClearReference(&context->UploadUrl); + PhClearReference(&context->ReAnalyseUrl); + PhClearReference(&context->FirstAnalysisDate); + PhClearReference(&context->LastAnalysisDate); + PhClearReference(&context->LastAnalysisUrl); + PhClearReference(&context->LastAnalysisAgo); +} + +VOID TaskDialogFreeContext( + _In_ PUPLOAD_CONTEXT Context + ) +{ + // Reset Taskbar progress state(s) + if (Context->TaskbarListClass) + ITaskbarList3_SetProgressState(Context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); + + if (Context->TaskbarListClass) + ITaskbarList3_SetProgressState(Context->TaskbarListClass, Context->DialogHandle, TBPF_NOPROGRESS); + + PhDereferenceObject(Context); +} + +VOID TaskDialogCreateIcons( + _In_ PUPLOAD_CONTEXT Context + ) +{ + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); +} + +PPH_STRING UpdateVersionString( + VOID + ) +{ + ULONG majorVersion; + ULONG minorVersion; + ULONG revisionVersion; + PPH_STRING currentVersion; + PPH_STRING versionHeader = NULL; + + PhGetPhVersionNumbers(&majorVersion, &minorVersion, NULL, &revisionVersion); + + if (currentVersion = PhFormatString(L"%lu.%lu.%lu", majorVersion, minorVersion, revisionVersion)) + { + versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer); + PhDereferenceObject(currentVersion); + } + + return versionHeader; +} + +NTSTATUS HashFileAndResetPosition( + _In_ HANDLE FileHandle, + _In_ PLARGE_INTEGER FileSize, + _In_ PH_HASH_ALGORITHM Algorithm, + _Out_ PPH_STRING *HashString + ) +{ + NTSTATUS status; + IO_STATUS_BLOCK iosb; + PH_HASH_CONTEXT hashContext; + PPH_STRING hashString = NULL; + ULONG64 bytesRemaining; + FILE_POSITION_INFORMATION positionInfo; + LONG priority; + IO_PRIORITY_HINT ioPriority; + BYTE buffer[PAGE_SIZE]; + + bytesRemaining = FileSize->QuadPart; + + PhGetThreadBasePriority(NtCurrentThread(), &priority); + PhGetThreadIoPriority(NtCurrentThread(), &ioPriority); + PhSetThreadBasePriority(NtCurrentThread(), THREAD_PRIORITY_LOWEST); + PhSetThreadIoPriority(NtCurrentThread(), IoPriorityVeryLow); + + PhInitializeHash(&hashContext, Algorithm); + + while (bytesRemaining) + { + status = NtReadFile( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + buffer, + sizeof(buffer), + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + break; + + PhUpdateHash(&hashContext, buffer, (ULONG)iosb.Information); + bytesRemaining -= (ULONG)iosb.Information; + } + + if (status == STATUS_END_OF_FILE) + status = STATUS_SUCCESS; + + if (NT_SUCCESS(status)) + { + BYTE hash[32]; + + switch (Algorithm) + { + case Md5HashAlgorithm: + PhFinalHash(&hashContext, hash, 16, NULL); + *HashString = PhBufferToHexString(hash, 16); + break; + case Sha1HashAlgorithm: + PhFinalHash(&hashContext, hash, 20, NULL); + *HashString = PhBufferToHexString(hash, 20); + break; + case Sha256HashAlgorithm: + PhFinalHash(&hashContext, hash, 32, NULL); + *HashString = PhBufferToHexString(hash, 32); + break; + } + + positionInfo.CurrentByteOffset.QuadPart = 0; + status = NtSetInformationFile( + FileHandle, + &iosb, + &positionInfo, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation + ); + } + + PhSetThreadBasePriority(NtCurrentThread(), priority); + PhSetThreadIoPriority(NtCurrentThread(), ioPriority); + + return status; +} + +PPH_BYTES PerformSubRequest( + _In_ PUPLOAD_CONTEXT Context, + _In_ PWSTR HostName, + _In_ PWSTR ObjectName + ) +{ + PPH_BYTES result = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING phVersion; + PPH_STRING userAgent; + + phVersion = PhGetPhVersion(); + userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); + + if (!PhHttpSocketCreate(&httpContext, userAgent->Buffer)) + { + RaiseUploadError(Context, L"Unable to create the http socket.", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketConnect(httpContext, HostName, PH_HTTP_DEFAULT_HTTPS_PORT)) + { + RaiseUploadError(Context, L"Unable to connect to the service.", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketBeginRequest( + httpContext, + NULL, + ObjectName, + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) + { + RaiseUploadError(Context, L"Unable to create the request.", GetLastError()); + goto CleanupExit; + } + + if ( + Context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD || + Context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE + ) + { + PPH_BYTES serviceHash; + PPH_STRING httpHeader; + + serviceHash = VirusTotalGetCachedDbHash(&ServiceObjectDbHash); + httpHeader = PhFormatString( + L"\x0061\x0070\x0069\x002D\x006B\x0065\x0079:%hs", + serviceHash->Buffer + ); + + PhHttpSocketAddRequestHeaders( + httpContext, + httpHeader->Buffer, + (ULONG)httpHeader->Length / sizeof(WCHAR) + ); + + PhClearReference(&httpHeader); + PhClearReference(&serviceHash); + } + + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) + { + RaiseUploadError(Context, L"Unable to send the request.", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketEndRequest(httpContext)) + { + RaiseUploadError(Context, L"Unable to receive the request.", GetLastError()); + goto CleanupExit; + } + + if (!(result = PhHttpSocketDownloadString(httpContext, FALSE))) + { + RaiseUploadError(Context, L"Unable to download the response.", GetLastError()); + goto CleanupExit; + } + +CleanupExit: + + if (httpContext) + PhHttpSocketDestroy(httpContext); + + PhClearReference(&phVersion); + PhClearReference(&userAgent); + + return result; +} + +NTSTATUS UploadFileThreadStart( + _In_ PVOID Parameter + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG httpStatus = 0; + ULONG httpPostSeed = 0; + ULONG totalUploadLength = 0; + ULONG totalUploadedLength = 0; + ULONG totalPostHeaderWritten = 0; + ULONG totalPostFooterWritten = 0; + ULONG totalWriteLength = 0; + LARGE_INTEGER timeNow; + LARGE_INTEGER timeStart; + ULONG64 timeTicks = 0; + ULONG64 timeBitsPerSecond = 0; + HANDLE fileHandle = NULL; + IO_STATUS_BLOCK isb; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING phVersion = NULL; + PPH_STRING userAgent = NULL; + PPH_STRING httpHostName = NULL; + PPH_STRING httpHostPath = NULL; + USHORT httpHostPort = 0; + PSERVICE_INFO serviceInfo = NULL; + PPH_STRING postBoundary = NULL; + PPH_BYTES asciiPostData = NULL; + PPH_BYTES asciiFooterData = NULL; + PH_STRING_BUILDER httpRequestHeaders = { 0 }; + PH_STRING_BUILDER httpPostHeader = { 0 }; + PH_STRING_BUILDER httpPostFooter = { 0 }; + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; + + serviceInfo = GetUploadServiceInfo(context->Service); + + if (!NT_SUCCESS(status = PhCreateFileWin32( + &fileHandle, + PhGetString(context->FileName), + FILE_GENERIC_READ, + 0, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status)); + goto CleanupExit; + } + + // Create a user agent string. + phVersion = PhGetPhVersion(); + userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); + + if (!PhHttpSocketCreate(&httpContext, userAgent->Buffer)) + { + PhClearReference(&phVersion); + PhClearReference(&userAgent); + goto CleanupExit; + } + + PhClearReference(&phVersion); + PhClearReference(&userAgent); + + if (!PhHttpSocketParseUrl( + context->UploadUrl, + &httpHostName, + &httpHostPath, + &httpHostPort + )) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!PhHttpSocketConnect( + httpContext, + PhGetString(httpHostName), + httpHostPort + )) + { + RaiseUploadError(context, L"Unable to connect to the service", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketBeginRequest( + httpContext, + L"POST", + PhGetString(httpHostPath), + PH_HTTP_FLAG_REFRESH | (httpHostPort == PH_HTTP_DEFAULT_HTTPS_PORT ? PH_HTTP_FLAG_SECURE : 0) + )) + { + RaiseUploadError(context, L"Unable to create the request", GetLastError()); + goto CleanupExit; + } + + PhInitializeStringBuilder(&httpRequestHeaders, DOS_MAX_PATH_LENGTH); + PhInitializeStringBuilder(&httpPostHeader, DOS_MAX_PATH_LENGTH); + PhInitializeStringBuilder(&httpPostFooter, DOS_MAX_PATH_LENGTH); + + // HTTP request boundary string. + postBoundary = PhFormatString( + L"--%I64u", + (ULONG64)RtlRandomEx(&httpPostSeed) | ((ULONG64)RtlRandomEx(&httpPostSeed) << 31) + ); + + if ( + context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD || + context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE + ) + { + USHORT machineType; + USHORT environmentId; + PH_MAPPED_IMAGE mappedImage; + PPH_BYTES serviceHash; + + if (!NT_SUCCESS(status = PhLoadMappedImageEx(NULL, fileHandle, TRUE, &mappedImage))) + { + RaiseUploadError(context, L"Unable to load the image.", RtlNtStatusToDosError(status)); + goto CleanupExit; + } + + switch (mappedImage.Signature) + { + case IMAGE_DOS_SIGNATURE: + machineType = mappedImage.NtHeaders->FileHeader.Machine; + break; + case IMAGE_ELF_SIGNATURE: + machineType = USHRT_MAX; // Windows only supports 64bit ELF. (mappedImage.Header->e_machine) + break; + } + + PhUnloadMappedImage(&mappedImage); + + switch (machineType) + { + case IMAGE_FILE_MACHINE_I386: + environmentId = 110; + break; + case IMAGE_FILE_MACHINE_AMD64: + environmentId = 120; + break; + case USHRT_MAX: // 64bit Linux + environmentId = 300; + break; + default: + { + RaiseUploadError(context, L"File architecture not supported.", STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT); + goto CleanupExit; + } + } + + // HTTP request headers + PhAppendFormatStringBuilder( + &httpRequestHeaders, + L"accept: application/json\r\n" + ); + + serviceHash = VirusTotalGetCachedDbHash(&ServiceObjectDbHash); + PhAppendFormatStringBuilder( + &httpRequestHeaders, + L"\x0061\x0070\x0069\x002D\x006B\x0065\x0079:%hs\r\n", + serviceHash->Buffer + ); + PhClearReference(&serviceHash); + + PhAppendFormatStringBuilder( + &httpRequestHeaders, + L"Content-Type: multipart/form-data; boundary=%s\r\n", + postBoundary->Buffer + ); + + // POST boundary header. + PhAppendFormatStringBuilder( + &httpPostHeader, + L"--%s\r\nContent-Disposition: form-data; name=\"environment_id\"\r\n\r\n%hu\r\n", + postBoundary->Buffer, + environmentId + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"--%s\r\nContent-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n\r\n", + postBoundary->Buffer, + PhGetStringOrEmpty(context->BaseFileName) + ); + + // POST boundary footer. + PhAppendFormatStringBuilder( + &httpPostFooter, + L"\r\n--%s--\r\n", + postBoundary->Buffer + ); + } + else if (context->Service == MENUITEM_JOTTI_UPLOAD) + { + PhAppendFormatStringBuilder( + &httpRequestHeaders, + L"Content-Type: multipart/form-data; boundary=%s\r\n", + postBoundary->Buffer + ); + + // POST boundary header. + PhAppendFormatStringBuilder( + &httpPostHeader, + L"\r\n--%s\r\n", + postBoundary->Buffer + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n268435456\r\n" + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"--%s\r\n", + postBoundary->Buffer + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", + serviceInfo->FileNameFieldName, + PhGetStringOrEmpty(context->BaseFileName) + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"Content-Type: application/x-msdownload\r\n\r\n" + ); + + // POST boundary footer. + PhAppendFormatStringBuilder( + &httpPostFooter, + L"\r\n--%s--\r\n", + postBoundary->Buffer + ); + } + else + { + PhAppendFormatStringBuilder( + &httpRequestHeaders, + L"Content-Type: multipart/form-data; boundary=%s\r\n", + postBoundary->Buffer + ); + + // POST boundary header + PhAppendFormatStringBuilder( + &httpPostHeader, + L"--%s\r\n", + postBoundary->Buffer + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", + serviceInfo->FileNameFieldName, + PhGetStringOrEmpty(context->BaseFileName) + ); + PhAppendFormatStringBuilder( + &httpPostHeader, + L"Content-Type: application/octet-stream\r\n\r\n" + ); + + // POST boundary footer + PhAppendFormatStringBuilder( + &httpPostFooter, + L"\r\n--%s--\r\n\r\n", + postBoundary->Buffer + ); + } + + // add headers + if (!PhHttpSocketAddRequestHeaders( + httpContext, + httpRequestHeaders.String->Buffer, + (ULONG)httpRequestHeaders.String->Length / sizeof(WCHAR) + )) + { + RaiseUploadError(context, L"Unable to add request headers", GetLastError()); + goto CleanupExit; + } + + if (context->Service == MENUITEM_JOTTI_UPLOAD) + { + PPH_STRING ajaxHeader = PhCreateString(L"X-Requested-With: XMLHttpRequest"); + + PhHttpSocketAddRequestHeaders( + httpContext, + ajaxHeader->Buffer, + (ULONG)ajaxHeader->Length / sizeof(WCHAR) + ); + + PhDereferenceObject(ajaxHeader); + } + + // Calculate the total request length. + totalUploadLength = (ULONG)httpPostHeader.String->Length / sizeof(WCHAR) + context->TotalFileLength + (ULONG)httpPostFooter.String->Length / sizeof(WCHAR); + + // Send the request. + if (!PhHttpSocketSendRequest(httpContext, NULL, totalUploadLength)) + { + RaiseUploadError(context, L"Unable to send the request", GetLastError()); + goto CleanupExit; + } + + // Convert to ASCII + asciiPostData = PhConvertUtf16ToAscii(httpPostHeader.String->Buffer, '-'); + asciiFooterData = PhConvertUtf16ToAscii(httpPostFooter.String->Buffer, '-'); + + // Start the clock. + PhQuerySystemTime(&timeStart); + + // Write the header + if (!PhHttpSocketWriteData( + httpContext, + asciiPostData->Buffer, + (ULONG)asciiPostData->Length, + &totalPostHeaderWritten + )) + { + RaiseUploadError(context, L"Unable to write the post header", GetLastError()); + goto CleanupExit; + } + + PPH_STRING msg = PhFormatString(L"Uploading %s...", PhGetStringOrEmpty(context->BaseFileName)); + SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0); + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)PhGetString(msg)); + PhDereferenceObject(msg); + + if (context->TaskbarListClass) + { + ITaskbarList3_SetProgressState( + context->TaskbarListClass, + PhMainWndHandle, + TBPF_NORMAL + ); + } + + while (TRUE) + { + BYTE buffer[PAGE_SIZE]; + + if (!context->UploadThreadHandle) + goto CleanupExit; + + if (!NT_SUCCESS(status = NtReadFile( + fileHandle, + NULL, + NULL, + NULL, + &isb, + buffer, + PAGE_SIZE, + NULL, + NULL + ))) + { + break; + } + + if (!PhHttpSocketWriteData(httpContext, buffer, (ULONG)isb.Information, &totalWriteLength)) + { + RaiseUploadError(context, L"Unable to upload the file data", GetLastError()); + goto CleanupExit; + } + + PhQuerySystemTime(&timeNow); + + totalUploadedLength += totalWriteLength; + timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; + timeBitsPerSecond = totalUploadedLength / __max(timeTicks, 1); + + { + FLOAT percent = ((FLOAT)totalUploadedLength / context->TotalFileLength * 100); + PPH_STRING totalLength = PhFormatSize(context->TotalFileLength, ULONG_MAX); + PPH_STRING totalUploaded = PhFormatSize(totalUploadedLength, ULONG_MAX); + PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, ULONG_MAX); + PPH_STRING statusMessage = PhFormatString( + L"Uploaded: %s of %s (%.0f%%)\r\nSpeed: %s/s", + PhGetStringOrEmpty(totalUploaded), + PhGetStringOrEmpty(totalLength), + percent, + PhGetStringOrEmpty(totalSpeed) + ); + + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)statusMessage->Buffer); + SendMessage(context->DialogHandle, TDM_SET_PROGRESS_BAR_POS, (WPARAM)percent, 0); + + if (context->TaskbarListClass) + { + ITaskbarList3_SetProgressValue( + context->TaskbarListClass, + context->DialogHandle, + totalUploadedLength, + context->TotalFileLength + ); + } + + PhDereferenceObject(statusMessage); + PhDereferenceObject(totalSpeed); + PhDereferenceObject(totalUploaded); + PhDereferenceObject(totalLength); + } + } + + // Write the footer bytes + if (!PhHttpSocketWriteData( + httpContext, + asciiFooterData->Buffer, + (ULONG)asciiFooterData->Length, + &totalPostFooterWritten + )) + { + RaiseUploadError(context, L"Unable to write the post footer", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketEndRequest(httpContext)) + { + RaiseUploadError(context, L"Unable to receive the response", GetLastError()); + goto CleanupExit; + } + + if (!PhHttpSocketQueryHeaderUlong( + httpContext, + PH_HTTP_QUERY_STATUS_CODE, + &httpStatus + )) + { + RaiseUploadError(context, L"Unable to query http headers", GetLastError()); + goto CleanupExit; + } + + if ( + httpStatus == PH_HTTP_STATUS_OK || + httpStatus == PH_HTTP_STATUS_CREATED || + httpStatus == PH_HTTP_STATUS_REDIRECT_METHOD || + httpStatus == PH_HTTP_STATUS_REDIRECT + ) + { + switch (context->Service) + { + case MENUITEM_HYBRIDANALYSIS_UPLOAD: + case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: + { + PPH_BYTES jsonString; + PVOID jsonRootObject; + INT64 errorCode; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + { + RaiseUploadError(context, L"Unable to download the response.", GetLastError()); + goto CleanupExit; + } + + if (jsonRootObject = PhCreateJsonParser(jsonString->Buffer)) + { + errorCode = PhGetJsonValueAsLong64(jsonRootObject, "code"); + + if (errorCode == 0) + { + PPH_STRING jsonHashString; + //PPH_STRING jsonJobIdString; + + jsonHashString = PhGetJsonValueAsString(jsonRootObject, "sha256"); + //jsonJobIdString = PhGetJsonValueAsString(jsonRootObject, "job_id"); + + if (jsonHashString) // && jsonJobIdString) + { + PhMoveReference(&context->LaunchCommand, PhFormatString( + L"https://www.hybrid-analysis.com/sample/%s", // /%s + jsonHashString->Buffer//, + //jsonJobIdString->Buffer + )); + } + + if (jsonHashString) + PhDereferenceObject(jsonHashString); + //if (jsonJobIdString) + // PhDereferenceObject(jsonJobIdString); + } + else + { + RaiseUploadError(context, L"Hybrid Analysis API error.", (ULONG)errorCode); + PhDereferenceObject(jsonString); + goto CleanupExit; + } + + PhFreeJsonParser(jsonRootObject); + } + else + { + RaiseUploadError(context, L"Unable to complete the request.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + goto CleanupExit; + } + + PhDereferenceObject(jsonString); + } + break; + case MENUITEM_VIRUSTOTAL_UPLOAD: + case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: + { + PPH_BYTES jsonString; + PVOID jsonRootObject; + PVOID jsonDataObject; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + { + RaiseUploadError(context, L"Unable to complete the request.", GetLastError()); + goto CleanupExit; + } + + if (!PhIsNullOrEmptyString(context->FileHash)) + { + PVIRUSTOTAL_FILE_REPORT fileReport; + + if (fileReport = VirusTotalRequestFileReport(context->FileHash)) + { + VirusTotalFreeFileReport(fileReport); + } + } + + if (jsonRootObject = PhCreateJsonParser(jsonString->Buffer)) + { + // New interface + if (jsonDataObject = PhGetJsonObject(jsonRootObject, "data")) + { + PPH_STRING analysisId = PhGetJsonValueAsString(jsonDataObject, "id"); + + PhMoveReference(&context->LaunchCommand, PhFormatString( + L"https://www.virustotal.com/#/file-analysis/%s", + analysisId->Buffer + )); + + PhDereferenceObject(analysisId); + } + else + { + // Old interface + if (PhGetJsonValueAsLong64(jsonRootObject, "response_code") == 1) + { + PhMoveReference(&context->LaunchCommand, PhGetJsonValueAsString(jsonRootObject, "permalink")); + } + } + + PhFreeJsonParser(jsonRootObject); + } + + if (PhIsNullOrEmptyString(context->LaunchCommand)) + { + RaiseUploadError(context, L"Unable to complete the request.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + PhDereferenceObject(jsonString); + goto CleanupExit; + } + + PhDereferenceObject(jsonString); + } + break; + case MENUITEM_JOTTI_UPLOAD: + case MENUITEM_JOTTI_UPLOAD_SERVICE: + { + PPH_BYTES jsonString; + PVOID jsonRootObject; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + { + RaiseUploadError(context, L"Unable to complete the request", GetLastError()); + goto CleanupExit; + } + + if (jsonRootObject = PhCreateJsonParser(jsonString->Buffer)) + { + PPH_STRING redirectUrl; + + if (redirectUrl = PhGetJsonValueAsString(jsonRootObject, "redirecturl")) + { + PhMoveReference(&context->LaunchCommand, PhFormatString(L"http://virusscan.jotti.org%s", redirectUrl->Buffer)); + PhDereferenceObject(redirectUrl); + } + + PhFreeJsonParser(jsonRootObject); + } + + PhDereferenceObject(jsonString); + } + break; + } + } + else + { + RaiseUploadError(context, L"Unable to complete the request.", httpStatus); + goto CleanupExit; + } + + if (!context->UploadThreadHandle) + goto CleanupExit; + + if (!PhIsNullOrEmptyString(context->LaunchCommand)) + { + PostMessage(context->DialogHandle, UM_LAUNCH, 0, 0); + } + else + { + RaiseUploadError(context, L"Unable to complete the Launch request (please try again after a few minutes)", ERROR_INVALID_DATA); + } + +CleanupExit: + + if (httpContext) + PhHttpSocketDestroy(httpContext); + + // Reset Taskbar progress state(s) + if (context->TaskbarListClass) + { + ITaskbarList3_SetProgressState(context->TaskbarListClass, PhMainWndHandle, TBPF_NOPROGRESS); + ITaskbarList3_SetProgressState(context->TaskbarListClass, context->DialogHandle, TBPF_NOPROGRESS); + } + + if (postBoundary) + PhDereferenceObject(postBoundary); + + if (asciiFooterData) + PhDereferenceObject(asciiFooterData); + + if (asciiPostData) + PhDereferenceObject(asciiPostData); + + if (httpPostFooter.String) + PhDeleteStringBuilder(&httpPostFooter); + + if (httpPostHeader.String) + PhDeleteStringBuilder(&httpPostHeader); + + if (httpRequestHeaders.String) + PhDeleteStringBuilder(&httpRequestHeaders); + + if (fileHandle) + NtClose(fileHandle); + + PhDereferenceObject(context); + + return status; +} + +NTSTATUS UploadCheckThreadStart( + _In_ PVOID Parameter + ) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN fileExists = FALSE; + LARGE_INTEGER fileSize64; + PPH_BYTES subRequestBuffer = NULL; + PSERVICE_INFO serviceInfo = NULL; + PPH_STRING subObjectName = NULL; + HANDLE fileHandle = NULL; + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; + + //context->Extension = VirusTotalGetCachedResult(context->FileName); + serviceInfo = GetUploadServiceInfo(context->Service); + + if (!NT_SUCCESS(status = PhCreateFileWin32( + &fileHandle, + PhGetString(context->FileName), + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status)); + goto CleanupExit; + } + + if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize64))) + { + if (context->Service == MENUITEM_VIRUSTOTAL_UPLOAD || + context->Service == MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE) + { + if (fileSize64.QuadPart < 32 * 1024 * 1024) + { + context->VtApiUpload = TRUE; + } + + if (fileSize64.QuadPart > 128 * 1024 * 1024) // 128 MB + { + RaiseUploadError(context, L"The file is too large (over 128 MB)", ERROR_FILE_TOO_LARGE); + goto CleanupExit; + } + } + else if ( + context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD || + context->Service == MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE + ) + { + if (fileSize64.QuadPart > 128 * 1024 * 1024) // 128 MB + { + RaiseUploadError(context, L"The file is too large (over 128 MB)", ERROR_FILE_TOO_LARGE); + goto CleanupExit; + } + } + else + { + if (fileSize64.QuadPart > 20 * 1024 * 1024) // 20 MB + { + RaiseUploadError(context, L"The file is too large (over 20 MB)", ERROR_FILE_TOO_LARGE); + goto CleanupExit; + } + } + + context->FileSize = PhFormatSize(fileSize64.QuadPart, ULONG_MAX); + context->TotalFileLength = fileSize64.LowPart; + } + + switch (context->Service) + { + case MENUITEM_HYBRIDANALYSIS_UPLOAD: + case MENUITEM_HYBRIDANALYSIS_UPLOAD_SERVICE: + { + PPH_STRING tempHashString = NULL; + PSTR uploadUrl = NULL; + PSTR quote = NULL; + PVOID rootJsonObject; + + // Create the default upload URL. + context->UploadUrl = PhFormatString( + L"https://%s%s", + serviceInfo->HostName, + serviceInfo->UploadObjectName + ); + + if (!NT_SUCCESS(status = HashFileAndResetPosition(fileHandle, &fileSize64, Sha256HashAlgorithm, &tempHashString))) + { + RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status)); + goto CleanupExit; + } + + context->FileHash = tempHashString; + subObjectName = PhConcatStrings2(L"/api/v2/overview/", PhGetString(context->FileHash)); + + if (!(subRequestBuffer = PerformSubRequest( + context, + serviceInfo->HostName, + subObjectName->Buffer + ))) + { + goto CleanupExit; + } + + if (rootJsonObject = PhCreateJsonParser(subRequestBuffer->Buffer)) + { + PPH_STRING errorMessage = PhGetJsonValueAsString(rootJsonObject, "message"); + + if (context->FileExists = PhIsNullOrEmptyString(errorMessage)) + { + PhMoveReference(&context->LaunchCommand, PhFormatString( + L"https://www.hybrid-analysis.com/sample/%s", + PhGetString(context->FileHash) + )); + + PostMessage(context->DialogHandle, UM_LAUNCH, 0, 0); + } + else + { + PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); + } + + PhClearReference(&errorMessage); + PhFreeJsonParser(rootJsonObject); + } + else + { + RaiseUploadError(context, L"Unable to parse the response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } + } + break; + case MENUITEM_VIRUSTOTAL_UPLOAD: + case MENUITEM_VIRUSTOTAL_UPLOAD_SERVICE: + { + PPH_STRING tempHashString = NULL; + PSTR uploadUrl = NULL; + PSTR quote = NULL; + PVOID rootJsonObject; + + if (!NT_SUCCESS(status = HashFileAndResetPosition(fileHandle, &fileSize64, Sha256HashAlgorithm, &tempHashString))) + { + RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status)); + goto CleanupExit; + } + + context->FileHash = tempHashString; + subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", PhGetString(context->FileHash)); + + if (!(subRequestBuffer = PerformSubRequest( + context, + serviceInfo->HostName, + subObjectName->Buffer + ))) + { + goto CleanupExit; + } + + if (rootJsonObject = PhCreateJsonParser(subRequestBuffer->Buffer)) + { + if (context->FileExists = PhGetJsonObjectBool(rootJsonObject, "file_exists")) + { + INT64 detected = 0; + INT64 detectedMax = 0; + PVOID detectionRatio; + + if (detectionRatio = PhGetJsonObject(rootJsonObject, "detection_ratio")) + { + detected = PhGetJsonArrayLong64(detectionRatio, 0); + detectedMax = PhGetJsonArrayLong64(detectionRatio, 1); + } + + context->Detected = PhFormatUInt64(detected, FALSE); + context->MaxDetected = PhFormatUInt64(detectedMax, FALSE); + context->UploadUrl = PhGetJsonValueAsString(rootJsonObject, "upload_url"); + context->ReAnalyseUrl = PhGetJsonValueAsString(rootJsonObject, "reanalyse_url"); + context->LastAnalysisUrl = PhGetJsonValueAsString(rootJsonObject, "last_analysis_url"); + context->FirstAnalysisDate = PhGetJsonValueAsString(rootJsonObject, "first_analysis_date"); + context->LastAnalysisDate = PhGetJsonValueAsString(rootJsonObject, "last_analysis_date"); + context->LastAnalysisAgo = PhGetJsonValueAsString(rootJsonObject, "last_analysis_ago"); + + PhMoveReference(&context->FirstAnalysisDate, VirusTotalStringToTime(context->FirstAnalysisDate)); + PhMoveReference(&context->LastAnalysisDate, VirusTotalStringToTime(context->LastAnalysisDate)); + + if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) + { + PhMoveReference(&context->ReAnalyseUrl, PhFormatString( + L"%s%s", + L"https://www.virustotal.com/", + PhGetString(context->ReAnalyseUrl) + )); + } + + if (context->VtApiUpload) + { + PPH_BYTES resource = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); + + PhMoveReference(&context->UploadUrl, PhFormatString( + L"%s%s?\x0061\x0070\x0069\x006B\x0065\x0079=%S&resource=%s", + L"https://www.virustotal.com/", + L"/vtapi/v2/file/scan", + resource->Buffer, + PhGetString(context->FileHash) + )); + + PhClearReference(&resource); + } + + PhMoveReference(&context->LaunchCommand, PhFormatString( + L"https://www.virustotal.com/file/%s/analysis/", + PhGetString(context->FileHash) + )); + + if (!PhIsNullOrEmptyString(context->UploadUrl)) + { + PostMessage(context->DialogHandle, UM_EXISTS, 0, 0); + } + else + { + RaiseUploadError(context, L"Unable to parse the UploadUrl.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } + } + else + { + PPH_STRING vt3UploadUrl; + PPH_BYTES vt3UploadRequestBuffer; + PVOID vt3RootJsonObject; + + vt3UploadUrl = PhCreateString(L"/ui/files/upload_url"); + + if (!(vt3UploadRequestBuffer = PerformSubRequest( + context, + serviceInfo->HostName, + vt3UploadUrl->Buffer + ))) + { + PhDereferenceObject(vt3UploadUrl); + goto CleanupExit; + } + + if (vt3RootJsonObject = PhCreateJsonParser(vt3UploadRequestBuffer->Buffer)) + { + context->UploadUrl = PhGetJsonValueAsString(vt3RootJsonObject, "data"); + PhFreeJsonParser(vt3RootJsonObject); + } + + // No file found... Start the upload. + if (!PhIsNullOrEmptyString(context->UploadUrl)) + { + PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); + } + else + { + RaiseUploadError(context, L"Received invalid VT3 response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } + + PhClearReference(&vt3UploadRequestBuffer); + } + + PhFreeJsonParser(rootJsonObject); + } + else + { + RaiseUploadError(context, L"Unable to parse the response.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } + } + break; + case MENUITEM_JOTTI_UPLOAD: + case MENUITEM_JOTTI_UPLOAD_SERVICE: + { + // Create the default upload URL. + context->UploadUrl = PhFormatString(L"https://%s%s", serviceInfo->HostName, serviceInfo->UploadObjectName); + + // Start the upload. + PostMessage(context->DialogHandle, UM_UPLOAD, 0, 0); + } + break; + } + +CleanupExit: + + PhClearReference(&subObjectName); + PhClearReference(&subRequestBuffer); + + if (fileHandle) + NtClose(fileHandle); + + PhDereferenceObject(context); + + return status; +} + +NTSTATUS UploadRecheckThreadStart( + _In_ PVOID Parameter + ) +{ + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; + PVIRUSTOTAL_API_RESPONSE fileRescan; + + if (fileRescan = VirusTotalRequestFileReScan(context->FileHash)) + { + if (fileRescan->ResponseCode == 1) + { + PhSwapReference(&context->ReAnalyseUrl, fileRescan->PermaLink); + + PhShellExecute(NULL, PhGetString(context->ReAnalyseUrl), NULL); + + SendMessage(context->DialogHandle, TDM_CLICK_BUTTON, IDOK, 0); + } + else + { + RaiseUploadError(context, L"VirusTotal ReScan API error.", (ULONG)fileRescan->ResponseCode); + } + + VirusTotalFreeFileReScan(fileRescan); + } + else + { + RaiseUploadError(context, L"VirusTotal ReScan API error.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } + + PhDereferenceObject(context); + + return STATUS_SUCCESS; +} + +NTSTATUS ViewReportThreadStart( + _In_ PVOID Parameter + ) +{ + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; + PVIRUSTOTAL_FILE_REPORT fileReport; + + if (fileReport = VirusTotalRequestFileReport(context->FileHash)) + { + if (fileReport->ResponseCode == 1) + { + PhSwapReference(&context->LaunchCommand, fileReport->PermaLink); + + PhShellExecute(NULL, PhGetString(context->LaunchCommand), NULL); + + SendMessage(context->DialogHandle, TDM_CLICK_BUTTON, IDOK, 0); + } + else + { + RaiseUploadError(context, L"VirusTotal ViewReport API error.", (ULONG)fileReport->ResponseCode); + } + + VirusTotalFreeFileReport(fileReport); + } + else + { + RaiseUploadError(context, L"VirusTotal ViewReport API error.", RtlNtStatusToDosError(STATUS_FAIL_CHECK)); + } + + PhDereferenceObject(context); + + return STATUS_SUCCESS; +} + +LRESULT CALLBACK TaskDialogSubclassProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PUPLOAD_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hwndDlg, 0xF))) + return 0; + + oldWndProc = context->DialogWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwndDlg, 0xF); + + TaskDialogFreeContext(context); + } + break; + case UM_UPLOAD: + { + ShowVirusTotalProgressDialog(context); + } + break; + case UM_EXISTS: + { + switch (PhGetIntegerSetting(SETTING_NAME_VIRUSTOTAL_DEFAULT_ACTION)) + { + //default: + case 1: + { + ShowVirusTotalProgressDialog(context); + } + break; + case 2: + { +//#ifdef PH_BUILD_API + ShowVirusTotalReScanProgressDialog(context); +//#else +// if (!PhIsNullOrEmptyString(context->ReAnalyseUrl)) +// { +// PhShellExecute(hwndDlg, PhGetString(context->ReAnalyseUrl), NULL); +// } +//#endif + } + break; + case 3: + { +//#ifdef PH_BUILD_API + ShowVirusTotalViewReportProgressDialog(context); +//#else +// if (!PhIsNullOrEmptyString(context->LaunchCommand)) +// { +// PhShellExecute(hwndDlg, PhGetString(context->LaunchCommand), NULL); +// } +//#endif + } + break; + default: + { + ShowFileFoundDialog(context); + } + } + } + break; + case UM_LAUNCH: + { + if (!PhIsNullOrEmptyString(context->LaunchCommand)) + { + PhShellExecute(hwndDlg, context->LaunchCommand->Buffer, NULL); + } + + SendMessage(hwndDlg, TDM_CLICK_BUTTON, IDOK, 0); + } + break; + case UM_ERROR: + { + VirusTotalShowErrorDialog(context); + } + break; + } + + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); +} + +HRESULT CALLBACK TaskDialogBootstrapCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_CREATED: + { + context->DialogHandle = hwndDlg; + + // Center the update window on PH if it's visible else we center on the desktop. + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); + + // Create the Taskdialog icons + TaskDialogCreateIcons(context); + + if (SUCCEEDED(CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, &context->TaskbarListClass))) + { + if (!SUCCEEDED(ITaskbarList3_HrInit(context->TaskbarListClass))) + { + ITaskbarList3_Release(context->TaskbarListClass); + context->TaskbarListClass = NULL; + } + } + + context->DialogWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, 0xF, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)TaskDialogSubclassProc); + + ShowVirusTotalUploadDialog(context); + } + break; + } + + return S_OK; +} + +NTSTATUS ShowUploadDialogThread( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; + PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; + BOOL checked = FALSE; + + PhInitializeAutoPool(&autoPool); + + config.hInstance = PluginInstance->DllBase; + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; + config.pszContent = L"Initializing..."; + config.lpCallbackData = (LONG_PTR)context; + config.pfCallback = TaskDialogBootstrapCallback; + + // Start TaskDialog bootstrap + TaskDialogIndirect(&config, NULL, NULL, &checked); + + PhDeleteAutoPool(&autoPool); + + return STATUS_SUCCESS; +} + +VOID UploadToOnlineService( + _In_ PPH_STRING FileName, + _In_ ULONG Service + ) +{ + PUPLOAD_CONTEXT context; + + if (PhBeginInitOnce(&UploadContextTypeInitOnce)) + { + UploadContextType = PhCreateObjectType(L"OnlineChecksObjectType", 0, UploadContextDeleteProcedure); + PhEndInitOnce(&UploadContextTypeInitOnce); + } + + PhReferenceObject(FileName); + + context = PhCreateObjectZero(sizeof(UPLOAD_CONTEXT), UploadContextType); + context->Service = Service; + context->FileName = FileName; + context->BaseFileName = PhGetBaseName(context->FileName); + + PhCreateThread2(ShowUploadDialogThread, (PVOID)context); +} + +VOID UploadServiceToOnlineService( + _In_ PPH_SERVICE_ITEM ServiceItem, + _In_ ULONG Service + ) +{ + NTSTATUS status; + PPH_STRING serviceFileName; + PPH_STRING serviceBinaryPath = NULL; + + if (PhBeginInitOnce(&UploadContextTypeInitOnce)) + { + UploadContextType = PhCreateObjectType(L"OnlineChecksObjectType", 0, UploadContextDeleteProcedure); + PhEndInitOnce(&UploadContextTypeInitOnce); + } + + if (NT_SUCCESS(status = QueryServiceFileName( + &ServiceItem->Name->sr, + &serviceFileName, + &serviceBinaryPath + ))) + { + PUPLOAD_CONTEXT context; + + context = PhCreateObjectZero(sizeof(UPLOAD_CONTEXT), UploadContextType); + context->Service = Service; + context->FileName = serviceFileName; + context->BaseFileName = PhGetBaseName(context->FileName); + + PhCreateThread2(ShowUploadDialogThread, (PVOID)context); + } + else + { + PhShowStatus(PhMainWndHandle, L"Unable to query the service.", status, 0); + } + + if (serviceBinaryPath) + PhDereferenceObject(serviceBinaryPath); +} diff --git a/plugins/OnlineChecks/virustotal.c b/plugins/OnlineChecks/virustotal.c index 430a63d840ee..2e3db21e5575 100644 --- a/plugins/OnlineChecks/virustotal.c +++ b/plugins/OnlineChecks/virustotal.c @@ -86,11 +86,8 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalAddCacheResult( { PVIRUSTOTAL_FILE_HASH_ENTRY result; - result = PhAllocate(sizeof(VIRUSTOTAL_FILE_HASH_ENTRY)); - memset(result, 0, sizeof(VIRUSTOTAL_FILE_HASH_ENTRY)); - - PhReferenceObject(FileName); - result->FileName = FileName; + result = PhAllocateZero(sizeof(VIRUSTOTAL_FILE_HASH_ENTRY)); + result->FileName = PhReferenceObject(FileName); result->FileNameAnsi = PhConvertUtf16ToMultiByte(PhGetString(FileName)); result->Extension = Extension; @@ -101,7 +98,9 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalAddCacheResult( return result; } -VOID VirusTotalRemoveCacheResult(_In_ PPROCESS_EXTENSION Extension) +VOID VirusTotalRemoveCacheResult( + _In_ PPROCESS_EXTENSION Extension + ) { PhAcquireQueuedLockExclusive(&ProcessListLock); @@ -116,7 +115,7 @@ VOID VirusTotalRemoveCacheResult(_In_ PPROCESS_EXTENSION Extension) PhClearReference(&extension->FileNameAnsi); PhClearReference(&extension->FileHashAnsi); PhClearReference(&extension->CreationTime); - PhClearReference(&extension->FileResult); + PhFree(extension); } @@ -177,6 +176,30 @@ PVIRUSTOTAL_FILE_HASH_ENTRY VirusTotalGetCachedResultFromHash( return NULL; } +PPH_BYTES VirusTotalGetCachedDbHash( + _In_ PPH_STRINGREF CachedHash + ) +{ + ULONG length; + PUCHAR buffer; + PPH_BYTES string; + + length = (ULONG)CachedHash->Length / sizeof(WCHAR) / 2; + + buffer = PhAllocate(length + 1); + memset(buffer, 0, length + 1); + + PhHexStringToBuffer(CachedHash, buffer); + + string = PhCreateBytes(buffer); + + for (SIZE_T i = 0; i < string->Length; i++) + string->Buffer[i] = string->Buffer[i] ^ 0x0D06F00D; + + PhFree(buffer); + return string; +} + PPH_LIST VirusTotalJsonToResultList( _In_ PVOID JsonObject ) @@ -185,7 +208,7 @@ PPH_LIST VirusTotalJsonToResultList( ULONG arrayLength; PPH_LIST results; - if (!(arrayLength = JsonGetArrayLength(JsonObject))) + if (!(arrayLength = PhGetJsonArrayLength(JsonObject))) return NULL; results = PhCreateList(arrayLength); @@ -194,26 +217,15 @@ PPH_LIST VirusTotalJsonToResultList( { PVIRUSTOTAL_API_RESULT result; PVOID jsonArrayObject; - PSTR fileLink; - PSTR fileHash; - PSTR fileRatio; - if (!(jsonArrayObject = JsonGetObjectArrayIndex(JsonObject, i))) + if (!(jsonArrayObject = PhGetJsonArrayIndexObject(JsonObject, i))) continue; - result = PhAllocate(sizeof(VIRUSTOTAL_API_RESULT)); - memset(result, 0, sizeof(VIRUSTOTAL_API_RESULT)); - - fileLink = GetJsonValueAsString(jsonArrayObject, "permalink"); - fileHash = GetJsonValueAsString(jsonArrayObject, "hash"); - fileRatio = GetJsonValueAsString(jsonArrayObject, "detection_ratio"); - - result->Found = GetJsonValueAsBool(jsonArrayObject, "found") == TRUE; - result->Positives = GetJsonValueAsUlong(jsonArrayObject, "positives"); - result->Total = GetJsonValueAsUlong(jsonArrayObject, "total"); - result->Permalink = fileLink ? PhZeroExtendToUtf16(fileLink) : PhReferenceEmptyString(); - result->FileHash = fileHash ? PhZeroExtendToUtf16(fileHash) : PhReferenceEmptyString(); - result->DetectionRatio = fileRatio ? PhZeroExtendToUtf16(fileRatio) : PhReferenceEmptyString(); + result = PhAllocateZero(sizeof(VIRUSTOTAL_API_RESULT)); + result->FileHash = PhGetJsonValueAsString(jsonArrayObject, "hash"); + result->Found = PhGetJsonObjectBool(jsonArrayObject, "found") == TRUE; + result->Positives = PhGetJsonValueAsLong64(jsonArrayObject, "positives"); + result->Total = PhGetJsonValueAsLong64(jsonArrayObject, "total"); PhAddItemList(results, result); } @@ -260,348 +272,451 @@ VOID VirusTotalBuildJsonArray( Entry->FileHash = hashString; Entry->FileHashAnsi = PhConvertUtf16ToMultiByte(Entry->FileHash->Buffer); - entry = CreateJsonObject(); - JsonAddObject(entry, "autostart_location", ""); - JsonAddObject(entry, "autostart_entry", ""); - JsonAddObject(entry, "hash", Entry->FileHashAnsi->Buffer); - JsonAddObject(entry, "image_path", Entry->FileNameAnsi->Buffer); - JsonAddObject(entry, "creation_datetime", Entry->CreationTime ? Entry->CreationTime->Buffer : ""); - JsonArrayAddObject(JsonArray, entry); + entry = PhCreateJsonObject(); + PhAddJsonObject(entry, "autostart_location", ""); + PhAddJsonObject(entry, "autostart_entry", ""); + PhAddJsonObject(entry, "hash", Entry->FileHashAnsi->Buffer); + PhAddJsonObject(entry, "image_path", Entry->FileNameAnsi->Buffer); + PhAddJsonObject(entry, "creation_datetime", Entry->CreationTime ? Entry->CreationTime->Buffer : ""); + PhAddJsonArrayObject(JsonArray, entry); } NtClose(fileHandle); } } -PSTR VirusTotalSendHttpRequest( +PPH_BYTES VirusTotalSendHttpRequest( _In_ PPH_BYTES JsonArray ) { - HANDLE fileHandle = INVALID_HANDLE_VALUE; - HINTERNET httpSessionHandle = NULL; - HINTERNET connectHandle = NULL; - HINTERNET requestHandle = NULL; - PSTR subRequestBuffer = NULL; - PPH_STRING phVersion = NULL; - PPH_STRING userAgent = NULL; - PPH_STRING keyString = NULL; - PPH_STRING urlString = NULL; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - - phVersion = PhGetPhVersion(); - userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - -#ifdef _DEBUG - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); -#endif - - if (!(httpSessionHandle = WinHttpOpen( - userAgent->Buffer, - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { + PPH_BYTES subRequestBuffer = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING urlPathString = NULL; + + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); + + if (!PhHttpSocketCreate(&httpContext, PhGetString(userAgentString))) goto CleanupExit; - } - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG gzipFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; + if (!PhHttpSocketConnect(httpContext, L"www.virustotal.com", PH_HTTP_DEFAULT_HTTPS_PORT)) + goto CleanupExit; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &gzipFlags, sizeof(ULONG)); + { + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); + + urlPathString = PhFormatString( + L"%s%s%s%s%S", + L"/partners", + L"/sysinternals", + L"/file-reports", + L"?\x0061\x0070\x0069\x006B\x0065\x0079=", + resourceString->Buffer + ); + + PhClearReference(&resourceString); } - if (!(connectHandle = WinHttpConnect( - httpSessionHandle, - L"www.virustotal.com", - INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) + if (!PhHttpSocketBeginRequest( + httpContext, + L"POST", + urlPathString->Buffer, + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) { goto CleanupExit; } - keyString = PhCreateString(VIRUSTOTAL_APIKEY); - urlString = PhFormatString( - L"/partners/sysinternals/file-reports?apikey=%s", - keyString->Buffer - ); + if (!PhHttpSocketAddRequestHeaders(httpContext, L"Content-Type: application/json", 0)) + goto CleanupExit; - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, - L"POST", - PhGetStringOrEmpty(urlString), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE - ))) - { - PhClearReference(&keyString); - PhClearReference(&urlString); + if (!PhHttpSocketSendRequest(httpContext, JsonArray->Buffer, (ULONG)JsonArray->Length)) goto CleanupExit; - } - PhClearReference(&keyString); - PhClearReference(&urlString); + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + if (!(subRequestBuffer = PhHttpSocketDownloadString(httpContext, FALSE))) + goto CleanupExit; - if (!WinHttpAddRequestHeaders(requestHandle, L"Content-Type: application/json", -1L, 0)) +CleanupExit: + + if (httpContext) + PhHttpSocketDestroy(httpContext); + + PhClearReference(&urlPathString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); + + if (JsonArray) + PhDereferenceObject(JsonArray); + + return subRequestBuffer; +} + +PVIRUSTOTAL_FILE_REPORT VirusTotalRequestFileReport( + _In_ PPH_STRING FileHash + ) +{ + PVIRUSTOTAL_FILE_REPORT result = NULL; + PPH_BYTES jsonString = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING urlPathString = NULL; + PVOID jsonRootObject = NULL; + PVOID jsonScanObject; + + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); + + if (!PhHttpSocketCreate( + &httpContext, + PhGetString(userAgentString) + )) { goto CleanupExit; } - if (!WinHttpSendRequest( - requestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - JsonArray->Buffer, - (ULONG)JsonArray->Length, - (ULONG)JsonArray->Length, - 0 + if (!PhHttpSocketConnect( + httpContext, + L"www.virustotal.com", + PH_HTTP_DEFAULT_HTTPS_PORT )) { goto CleanupExit; } - if (WinHttpReceiveResponse(requestHandle, NULL)) { - BYTE buffer[PAGE_SIZE]; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); + + urlPathString = PhFormatString( + L"%s%s%s%s%s%S%s%s", + L"/vtapi", + L"/v2", + L"/file", + L"/report", + L"?\x0061\x0070\x0069\x006B\x0065\x0079=", + resourceString->Buffer, + L"&resource=", + FileHash->Buffer + ); + + PhClearReference(&resourceString); + } - allocatedLength = sizeof(buffer); - subRequestBuffer = PhAllocate(allocatedLength); - dataLength = 0; + if (!PhHttpSocketBeginRequest( + httpContext, + L"POST", + PhGetString(urlPathString), + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) + { + goto CleanupExit; + } + + if (!PhHttpSocketAddRequestHeaders(httpContext, L"Content-Type: application/json", 0)) + goto CleanupExit; + + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) + goto CleanupExit; + + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + goto CleanupExit; + + if (!(jsonRootObject = PhCreateJsonParser(jsonString->Buffer))) + goto CleanupExit; + + result = PhAllocateZero(sizeof(VIRUSTOTAL_FILE_REPORT)); + result->ResponseCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); + result->PermaLink = PhGetJsonValueAsString(jsonRootObject, "permalink"); + result->ScanDate = PhGetJsonValueAsString(jsonRootObject, "scan_date"); + result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); + result->Total = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "total"), FALSE); + result->Positives = PhFormatUInt64(PhGetJsonValueAsLong64(jsonRootObject, "positives"), FALSE); + //result->Md5 = PhGetJsonValueAsString(jsonRootObject, "md5"); + //result->Sha1 = PhGetJsonValueAsString(jsonRootObject, "sha1"); + //result->Sha256 = PhGetJsonValueAsString(jsonRootObject, "sha256"); + + if (jsonScanObject = PhGetJsonObject(jsonRootObject, "scans")) + { + PPH_LIST jsonArrayList; - while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength)) + if (jsonArrayList = PhGetJsonObjectAsArrayList(jsonScanObject)) { - if (returnLength == 0) - break; + result->ScanResults = PhCreateList(jsonArrayList->Count); - if (allocatedLength < dataLength + returnLength) + for (ULONG i = 0; i < jsonArrayList->Count; i++) { - allocatedLength *= 2; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + PVIRUSTOTAL_FILE_REPORT_RESULT entry; + PJSON_ARRAY_LIST_OBJECT object = jsonArrayList->Items[i]; - memcpy(subRequestBuffer + dataLength, buffer, returnLength); - dataLength += returnLength; - } + entry = PhAllocateZero(sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); + entry->Vendor = PhConvertUtf8ToUtf16(object->Key); + entry->Detected = PhGetJsonObjectBool(object->Entry, "detected"); + entry->EngineVersion = PhGetJsonValueAsString(object->Entry, "version"); + entry->DetectionName = PhGetJsonValueAsString(object->Entry, "result"); + entry->DatabaseDate = PhGetJsonValueAsString(object->Entry, "update"); + PhAddItemList(result->ScanResults, entry); - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + PhFree(object); + } - // Ensure that the buffer is null-terminated. - subRequestBuffer[dataLength] = 0; + PhDereferenceObject(jsonArrayList); + } } CleanupExit: - if (requestHandle) - WinHttpCloseHandle(requestHandle); + if (httpContext) + PhHttpSocketDestroy(httpContext); - if (connectHandle) - WinHttpCloseHandle(connectHandle); + if (jsonRootObject) + PhFreeJsonParser(jsonRootObject); - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); + PhClearReference(&jsonString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); - if (JsonArray) - PhDereferenceObject(JsonArray); - - return subRequestBuffer; + return result; } -PVIRUSTOTAL_FILE_REPORT_RESULT VirusTotalSendHttpFileReportRequest( - _In_ PPH_STRING FileHash +VOID VirusTotalFreeFileReport( + _In_ PVIRUSTOTAL_FILE_REPORT FileReport ) { - NTSTATUS status = STATUS_SUCCESS; - HANDLE fileHandle = INVALID_HANDLE_VALUE; - HINTERNET httpSessionHandle = NULL; - HINTERNET connectHandle = NULL; - HINTERNET requestHandle = NULL; - PSTR subRequestBuffer = NULL; - PPH_STRING phVersion = NULL; - PPH_STRING userAgent = NULL; - PPH_STRING keyString = NULL; - PPH_STRING urlString = NULL; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - - phVersion = PhGetPhVersion(); - userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); - -#ifdef _DEBUG - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); -#endif - - if (!(httpSessionHandle = WinHttpOpen( - userAgent->Buffer, - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) + PhClearReference(&FileReport->StatusMessage); + PhClearReference(&FileReport->PermaLink); + PhClearReference(&FileReport->ScanId); + PhClearReference(&FileReport->ScanDate); + PhClearReference(&FileReport->Positives); + PhClearReference(&FileReport->Total); + + if (FileReport->ScanResults) { - goto CleanupExit; + for (ULONG i = 0; i < FileReport->ScanResults->Count; i++) + { + PVIRUSTOTAL_FILE_REPORT_RESULT object = FileReport->ScanResults->Items[i]; + + PhClearReference(&object->Vendor); + PhClearReference(&object->EngineVersion); + PhClearReference(&object->DetectionName); + PhClearReference(&object->DatabaseDate); + + PhFree(object); + } + + PhClearReference(&FileReport->ScanResults); } - if (WindowsVersion >= WINDOWS_8_1) + PhFree(FileReport); +} + +PVIRUSTOTAL_API_RESPONSE VirusTotalRequestFileReScan( + _In_ PPH_STRING FileHash + ) +{ + PVIRUSTOTAL_API_RESPONSE result = NULL; + PPH_BYTES jsonString = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING urlPathString = NULL; + PVOID jsonRootObject = NULL; + + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); + + if (!PhHttpSocketCreate( + &httpContext, + PhGetString(userAgentString) + )) { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - WinHttpSetOption(httpSessionHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG)); + goto CleanupExit; } - if (!(connectHandle = WinHttpConnect( - httpSessionHandle, + if (!PhHttpSocketConnect( + httpContext, L"www.virustotal.com", - INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) + PH_HTTP_DEFAULT_HTTPS_PORT + )) { goto CleanupExit; } - keyString = PhCreateString(VIRUSTOTAL_APIKEY); - urlString = PhFormatString( - L"/vtapi/v2/file/report?apikey=%s&resource=%s", - keyString->Buffer, - PhGetStringOrEmpty(FileHash) - ); + { + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); + + urlPathString = PhFormatString( + L"%s%s%s%s%s%S%s%s", + L"/vtapi", + L"/v2", + L"/file", + L"/rescan", + L"?\x0061\x0070\x0069\x006B\x0065\x0079=", + resourceString->Buffer, + L"&resource=", + FileHash->Buffer + ); + + PhClearReference(&resourceString); + } - if (!(requestHandle = WinHttpOpenRequest( - connectHandle, + if (!PhHttpSocketBeginRequest( + httpContext, L"POST", - PhGetStringOrEmpty(urlString), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE - ))) + PhGetString(urlPathString), + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) { - PhClearReference(&keyString); - PhClearReference(&urlString); goto CleanupExit; } - PhClearReference(&keyString); - PhClearReference(&urlString); + if (!PhHttpSocketAddRequestHeaders(httpContext, L"Content-Type: application/json", 0)) + goto CleanupExit; - if (!WinHttpAddRequestHeaders(requestHandle, L"Content-Type: application/json", -1L, 0)) + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) goto CleanupExit; - if (!WinHttpSendRequest( - requestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) goto CleanupExit; - } - if (WinHttpReceiveResponse(requestHandle, NULL)) - { - BYTE buffer[PAGE_SIZE]; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; + if (!(jsonRootObject = PhCreateJsonParser(jsonString->Buffer))) + goto CleanupExit; - allocatedLength = sizeof(buffer); - subRequestBuffer = PhAllocate(allocatedLength); - dataLength = 0; + result = PhAllocateZero(sizeof(VIRUSTOTAL_API_RESPONSE)); + result->ResponseCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); + result->PermaLink = PhGetJsonValueAsString(jsonRootObject, "permalink"); + result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); - while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength)) - { - if (returnLength == 0) - break; +CleanupExit: - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + if (httpContext) + PhHttpSocketDestroy(httpContext); - memcpy(subRequestBuffer + dataLength, buffer, returnLength); - dataLength += returnLength; - } + if (jsonRootObject) + PhFreeJsonParser(jsonRootObject); - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - subRequestBuffer = PhReAllocate(subRequestBuffer, allocatedLength); - } + PhClearReference(&jsonString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); - subRequestBuffer[dataLength] = 0; - } + return result; +} -CleanupExit: +VOID VirusTotalFreeFileReScan( + _In_ PVIRUSTOTAL_API_RESPONSE FileReScan + ) +{ + PhClearReference(&FileReScan->StatusMessage); + PhClearReference(&FileReScan->PermaLink); + PhClearReference(&FileReScan->ScanId); - if (requestHandle) - WinHttpCloseHandle(requestHandle); + PhFree(FileReScan); +} - if (connectHandle) - WinHttpCloseHandle(connectHandle); +PVIRUSTOTAL_API_RESPONSE VirusTotalRequestIpAddressReport( + _In_ PPH_STRING IpAddress + ) +{ + PVIRUSTOTAL_API_RESPONSE result = NULL; + PPH_BYTES jsonString = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING versionString = NULL; + PPH_STRING userAgentString = NULL; + PPH_STRING urlPathString = NULL; + PVOID jsonRootObject = NULL; + + versionString = PhGetPhVersion(); + userAgentString = PhConcatStrings2(L"ProcessHacker_", versionString->Buffer); + + if (!PhHttpSocketCreate( + &httpContext, + PhGetString(userAgentString) + )) + { + goto CleanupExit; + } - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); + if (!PhHttpSocketConnect( + httpContext, + L"www.virustotal.com", + PH_HTTP_DEFAULT_HTTPS_PORT + )) + { + goto CleanupExit; + } + { + PPH_BYTES resourceString = VirusTotalGetCachedDbHash(&ProcessObjectDbHash); + + urlPathString = PhFormatString( + L"%s%s%s%s%s%S%s%s", + L"/vtapi", + L"/v2", + L"/ip-address", + L"/report", + L"?\x0061\x0070\x0069\x006B\x0065\x0079=", + resourceString->Buffer, + L"&ip=", + IpAddress->Buffer + ); + + PhClearReference(&resourceString); + } - PVOID jsonRootObject; - PVOID jsonScanObject; - PVIRUSTOTAL_FILE_REPORT_RESULT result; + if (!PhHttpSocketBeginRequest( + httpContext, + L"POST", + PhGetString(urlPathString), + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) + { + goto CleanupExit; + } - if (!(jsonRootObject = CreateJsonParser(subRequestBuffer))) + if (!PhHttpSocketAddRequestHeaders(httpContext, L"Content-Type: application/json", 0)) goto CleanupExit; - if (!GetJsonValueAsUlong(jsonRootObject, "response_code")) + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) goto CleanupExit; - result = PhAllocate(sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); - memset(result, 0, sizeof(VIRUSTOTAL_FILE_REPORT_RESULT)); - - result->Total = PhFormatUInt64(GetJsonValueAsUlong(jsonRootObject, "total"), FALSE); - result->Positives = PhFormatUInt64(GetJsonValueAsUlong(jsonRootObject, "positives"), FALSE); - result->Resource = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "resource")); - result->ScanId = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "scan_id")); - result->Md5 = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "md5")); - result->Sha1 = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "sha1")); - result->Sha256 = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "sha256")); - result->ScanDate = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "scan_date")); - result->Permalink = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "permalink")); - result->StatusMessage = PhZeroExtendToUtf16(GetJsonValueAsString(jsonRootObject, "verbose_msg")); - - if (jsonScanObject = JsonGetObject(jsonRootObject, "scans")) - { - PPH_LIST jsonArrayList; + if (!PhHttpSocketEndRequest(httpContext)) + goto CleanupExit; + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + goto CleanupExit; - if (jsonArrayList = JsonGetObjectArrayList(jsonScanObject)) - { - result->ScanResults = PhCreateList(jsonArrayList->Count); + if (!(jsonRootObject = PhCreateJsonParser(jsonString->Buffer))) + goto CleanupExit; - for (ULONG i = 0; i < jsonArrayList->Count; i++) - { - PJSON_ARRAY_LIST_OBJECT object = jsonArrayList->Items[i]; - //BOOLEAN detected = GetJsonValueAsBool(object->Entry, "detected") == TRUE; - //PSTR version = GetJsonValueAsString(object->Entry, "version"); - //PSTR result = GetJsonValueAsString(object->Entry, "result"); - //PSTR update = GetJsonValueAsString(object->Entry, "update"); + result = PhAllocateZero(sizeof(VIRUSTOTAL_API_RESPONSE)); + //result->ResponseCode = PhGetJsonValueAsLong64(jsonRootObject, "response_code"); + //result->StatusMessage = PhGetJsonValueAsString(jsonRootObject, "verbose_msg"); + //result->PermaLink = PhGetJsonValueAsString(jsonRootObject, "permalink"); + //result->ScanId = PhGetJsonValueAsString(jsonRootObject, "scan_id"); - PhFree(object); - } +CleanupExit: - PhDereferenceObject(jsonArrayList); - } - } + if (httpContext) + PhHttpSocketDestroy(httpContext); + + if (jsonRootObject) + PhFreeJsonParser(jsonRootObject); + + PhClearReference(&jsonString); + PhClearReference(&versionString); + PhClearReference(&userAgentString); return result; } @@ -610,31 +725,25 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( _In_ PVOID Parameter ) { - LONG priority; - IO_PRIORITY_HINT ioPriority; - // TODO: Workqueue support. - priority = THREAD_PRIORITY_LOWEST; - ioPriority = IoPriorityVeryLow; - - NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, &priority, sizeof(LONG)); - NtSetInformationThread(NtCurrentThread(), ThreadIoPriority, &ioPriority, sizeof(IO_PRIORITY_HINT)); + PhSetThreadBasePriority(NtCurrentThread(), THREAD_PRIORITY_LOWEST); + PhSetThreadIoPriority(NtCurrentThread(), IoPriorityVeryLow); - Sleep(10 * 1000); + PhDelayExecution(10 * 1000); do { ULONG i; INT64 resultLength; - PSTR jsonArrayToSendString; - PSTR jsonApiResult = NULL; + PPH_BYTES jsonApiResult = NULL; PVOID jsonArray; PVOID rootJsonObject = NULL; PVOID dataJsonObject; + PPH_STRING jsonArrayToSendString = NULL; PPH_LIST resultTempList = NULL; PPH_LIST virusTotalResults = NULL; - jsonArray = CreateJsonArray(); + jsonArray = PhCreateJsonArray(); resultTempList = PhCreateList(30); PhAcquireQueuedLockExclusive(&ProcessListLock); @@ -658,7 +767,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( if (resultTempList->Count == 0) { - Sleep(30 * 1000); // Wait 30 seconds + PhDelayExecution(30 * 1000); // Wait 30 seconds goto CleanupExit; } @@ -667,19 +776,19 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( VirusTotalBuildJsonArray(resultTempList->Items[i], jsonArray); } - if (!(jsonArrayToSendString = GetJsonArrayString(jsonArray))) + if (!(jsonArrayToSendString = PhGetJsonArrayString(jsonArray))) goto CleanupExit; - if (!(jsonApiResult = VirusTotalSendHttpRequest(PhCreateBytes(jsonArrayToSendString)))) + if (!(jsonApiResult = VirusTotalSendHttpRequest(PhConvertUtf16ToUtf8(jsonArrayToSendString->Buffer)))) goto CleanupExit; - if (!(rootJsonObject = CreateJsonParser(jsonApiResult))) + if (!(rootJsonObject = PhCreateJsonParser(jsonApiResult->Buffer))) goto CleanupExit; - if (!(dataJsonObject = JsonGetObject(rootJsonObject, "data"))) + if (!(dataJsonObject = PhGetJsonObject(rootJsonObject, "data"))) goto CleanupExit; - if (!(resultLength = GetJsonValueAsUlong(rootJsonObject, "result"))) + if (!(resultLength = PhGetJsonValueAsLong64(rootJsonObject, "result"))) goto CleanupExit; if (virusTotalResults = VirusTotalJsonToResultList(dataJsonObject)) @@ -697,24 +806,19 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( entry->Processed = TRUE; entry->Found = result->Found; entry->Positives = result->Positives; - - PhSwapReference(&entry->FileResult, result->DetectionRatio); - + entry->Total = result->Total; + if (!FindProcessDbObject(&entry->FileName->sr)) { CreateProcessDbObject( entry->FileName, entry->Positives, - result->FileHash, - entry->FileResult + entry->Total, + result->FileHash ); } } } - else - { - - } } } @@ -729,7 +833,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( // PhClearReference(&result->Permalink); // PhClearReference(&result->FileHash); // PhClearReference(&result->DetectionRatio); - // + PhFree(result); } @@ -738,17 +842,20 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( if (rootJsonObject) { - CleanupJsonParser(rootJsonObject); + PhFreeJsonParser(rootJsonObject); } + if (jsonArrayToSendString) + PhDereferenceObject(jsonArrayToSendString); + if (jsonArray) { - CleanupJsonParser(jsonArray); + PhFreeJsonParser(jsonArray); } if (jsonApiResult) { - PhFree(jsonApiResult); + PhDereferenceObject(jsonApiResult); } if (resultTempList) @@ -773,7 +880,7 @@ NTSTATUS NTAPI VirusTotalProcessApiThread( PhDereferenceObject(resultTempList); } - Sleep(5 * 1000); // Wait 5 seconds + PhDelayExecution(5 * 1000); // Wait 5 seconds } while (VirusTotalHandle); @@ -785,7 +892,8 @@ VOID InitializeVirusTotalProcessMonitor( ) { VirusTotalList = PhCreateList(100); - VirusTotalHandle = PhCreateThread(0, VirusTotalProcessApiThread, NULL); + + PhCreateThreadEx(&VirusTotalHandle, VirusTotalProcessApiThread, NULL); } VOID CleanupVirusTotalProcessMonitor( @@ -804,7 +912,6 @@ VOID CleanupVirusTotalProcessMonitor( } } - // NOTE: This function does not use the SCM due to major performance issues. // For now just query this information from the registry but it might be out-of-sync // with any recent services changes until the SCM flushes its cache. @@ -815,8 +922,6 @@ NTSTATUS QueryServiceFileName( ) { static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); - static PH_STRINGREF typeKeyName = PH_STRINGREF_INIT(L"Type"); - NTSTATUS status; HANDLE keyHandle; ULONG serviceType = 0; @@ -837,25 +942,8 @@ NTSTATUS QueryServiceFileName( ))) { PPH_STRING serviceImagePath; - PKEY_VALUE_PARTIAL_INFORMATION buffer; - - if (NT_SUCCESS(status = PhQueryValueKey( - keyHandle, - &typeKeyName, - KeyValuePartialInformation, - &buffer - ))) - { - if ( - buffer->Type == REG_DWORD && - buffer->DataLength == sizeof(ULONG) - ) - { - serviceType = *(PULONG)buffer->Data; - } - PhFree(buffer); - } + serviceType = PhQueryRegistryUlong(keyHandle, L"Type"); if (serviceImagePath = PhQueryRegistryString(keyHandle, L"ImagePath")) { @@ -881,7 +969,7 @@ NTSTATUS QueryServiceFileName( if (NT_SUCCESS(status)) { - PhGetServiceDllParameter(ServiceName, &fileName); + PhGetServiceDllParameter(serviceType, ServiceName, &fileName); if (!fileName) { @@ -913,4 +1001,4 @@ NTSTATUS QueryServiceFileName( PhDereferenceObject(keyName); return status; -} \ No newline at end of file +} diff --git a/plugins/Plugins.props b/plugins/Plugins.props index 0f074ebbd868..da18855cce14 100644 --- a/plugins/Plugins.props +++ b/plugins/Plugins.props @@ -19,18 +19,19 @@ - ..\include;..\..\sdk\include;%(AdditionalIncludeDirectories) + $(SolutionDir)include;$(SolutionDir)..\sdk\include;%(AdditionalIncludeDirectories) true Level3 true StdCall + Guard false true ProgramDatabase true - ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + ProcessHacker.lib;ntdll.lib;noenv.obj;noarg.obj;%(AdditionalDependencies) true 6.01 Windows @@ -75,7 +76,7 @@ StreamingSIMDExtensions - ..\..\sdk\lib\i386;%(AdditionalLibraryDirectories) + $(SolutionDir)..\sdk\lib\i386;%(AdditionalLibraryDirectories) MachineX86 @@ -83,7 +84,7 @@ - ..\..\sdk\lib\amd64;%(AdditionalLibraryDirectories) + $(SolutionDir)..\sdk\lib\amd64;%(AdditionalLibraryDirectories) MachineX64 @@ -91,14 +92,14 @@ - WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + WIN32;DEBUG;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalCompilerOptions) - WIN64;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + WIN64;DEBUG;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);$(ExternalCompilerOptions) diff --git a/plugins/Plugins.sln b/plugins/Plugins.sln index 4e75dea003a4..6dd3d8b25eff 100644 --- a/plugins/Plugins.sln +++ b/plugins/Plugins.sln @@ -1,165 +1,146 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 -MinimumVisualStudioVersion = 15.0.26228.4 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{53C3AE07-D96F-4F5C-B407-4195084472CF}" - ProjectSection(SolutionItems) = preProject - include\commonutil.h = include\commonutil.h - Plugins.props = Plugins.props - include\toolstatusintf.h = include\toolstatusintf.h - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SbieSupport", "SbieSupport\SbieSupport.vcxproj", "{EEF1E81D-D286-422A-89E6-C6C8F3BE648A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtendedNotifications", "ExtendedNotifications\ExtendedNotifications.vcxproj", "{80E791B8-AC98-407E-8FF9-5154AF50E887}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToolStatus", "ToolStatus\ToolStatus.vcxproj", "{60B43533-C75E-4741-9E19-C4D581BEF51C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtendedServices", "ExtendedServices\ExtendedServices.vcxproj", "{AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetworkTools", "NetworkTools\NetworkTools.vcxproj", "{B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OnlineChecks", "OnlineChecks\OnlineChecks.vcxproj", "{79D24223-1122-40A9-BC8F-46A2089FE089}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtendedTools", "ExtendedTools\ExtendedTools.vcxproj", "{3437FD16-A3BF-4938-883A-EB0DF1584A2A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowExplorer", "WindowExplorer\WindowExplorer.vcxproj", "{37488DC1-E45F-4626-A87C-3A854A153D1A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DotNetTools", "DotNetTools\DotNetTools.vcxproj", "{2B3235B0-31E1-44DA-81A1-183F40517E47}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Updater\Updater.vcxproj", "{A0C1595C-FA3E-4B7A-936C-306BC6294C5E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserNotes", "UserNotes\UserNotes.vcxproj", "{7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HardwareDevices", "HardwareDevices\HardwareDevices.vcxproj", "{5F0D72C4-8319-4B61-9E13-6084B680EB90}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonUtil", "CommonUtil\CommonUtil.vcxproj", "{C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtraPlugins", "ExtraPlugins\ExtraPlugins.vcxproj", "{96549A3E-D1BD-470E-A5B3-A64803C4DEF5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Debug|Win32.ActiveCfg = Debug|Win32 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Debug|Win32.Build.0 = Debug|Win32 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Debug|x64.ActiveCfg = Debug|x64 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Debug|x64.Build.0 = Debug|x64 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Release|Win32.ActiveCfg = Release|Win32 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Release|Win32.Build.0 = Release|Win32 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Release|x64.ActiveCfg = Release|x64 - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A}.Release|x64.Build.0 = Release|x64 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|Win32.ActiveCfg = Debug|Win32 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|Win32.Build.0 = Debug|Win32 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|x64.ActiveCfg = Debug|x64 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|x64.Build.0 = Debug|x64 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|Win32.ActiveCfg = Release|Win32 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|Win32.Build.0 = Release|Win32 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|x64.ActiveCfg = Release|x64 - {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|x64.Build.0 = Release|x64 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|Win32.ActiveCfg = Debug|Win32 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|Win32.Build.0 = Debug|Win32 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|x64.ActiveCfg = Debug|x64 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|x64.Build.0 = Debug|x64 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|Win32.ActiveCfg = Release|Win32 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|Win32.Build.0 = Release|Win32 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|x64.ActiveCfg = Release|x64 - {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|x64.Build.0 = Release|x64 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|Win32.ActiveCfg = Debug|Win32 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|Win32.Build.0 = Debug|Win32 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|x64.ActiveCfg = Debug|x64 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|x64.Build.0 = Debug|x64 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|Win32.ActiveCfg = Release|Win32 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|Win32.Build.0 = Release|Win32 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|x64.ActiveCfg = Release|x64 - {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|x64.Build.0 = Release|x64 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|Win32.ActiveCfg = Debug|Win32 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|Win32.Build.0 = Debug|Win32 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|x64.ActiveCfg = Debug|x64 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|x64.Build.0 = Debug|x64 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|Win32.ActiveCfg = Release|Win32 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|Win32.Build.0 = Release|Win32 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|x64.ActiveCfg = Release|x64 - {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|x64.Build.0 = Release|x64 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|Win32.ActiveCfg = Debug|Win32 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|Win32.Build.0 = Debug|Win32 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|x64.ActiveCfg = Debug|x64 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|x64.Build.0 = Debug|x64 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|Win32.ActiveCfg = Release|Win32 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|Win32.Build.0 = Release|Win32 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|x64.ActiveCfg = Release|x64 - {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|x64.Build.0 = Release|x64 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|Win32.ActiveCfg = Debug|Win32 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|Win32.Build.0 = Debug|Win32 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|x64.ActiveCfg = Debug|x64 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|x64.Build.0 = Debug|x64 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|Win32.ActiveCfg = Release|Win32 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|Win32.Build.0 = Release|Win32 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|x64.ActiveCfg = Release|x64 - {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|x64.Build.0 = Release|x64 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|Win32.ActiveCfg = Debug|Win32 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|Win32.Build.0 = Debug|Win32 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|x64.ActiveCfg = Debug|x64 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|x64.Build.0 = Debug|x64 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|Win32.ActiveCfg = Release|Win32 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|Win32.Build.0 = Release|Win32 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|x64.ActiveCfg = Release|x64 - {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|x64.Build.0 = Release|x64 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|Win32.ActiveCfg = Debug|Win32 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|Win32.Build.0 = Debug|Win32 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|x64.ActiveCfg = Debug|x64 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|x64.Build.0 = Debug|x64 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|Win32.ActiveCfg = Release|Win32 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|Win32.Build.0 = Release|Win32 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|x64.ActiveCfg = Release|x64 - {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|x64.Build.0 = Release|x64 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|Win32.ActiveCfg = Debug|Win32 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|Win32.Build.0 = Debug|Win32 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|x64.ActiveCfg = Debug|x64 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|x64.Build.0 = Debug|x64 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|Win32.ActiveCfg = Release|Win32 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|Win32.Build.0 = Release|Win32 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|x64.ActiveCfg = Release|x64 - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|x64.Build.0 = Release|x64 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|Win32.Build.0 = Debug|Win32 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|x64.ActiveCfg = Debug|x64 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|x64.Build.0 = Debug|x64 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|Win32.ActiveCfg = Release|Win32 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|Win32.Build.0 = Release|Win32 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|x64.ActiveCfg = Release|x64 - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|x64.Build.0 = Release|x64 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|Win32.ActiveCfg = Debug|Win32 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|Win32.Build.0 = Debug|Win32 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|x64.ActiveCfg = Debug|x64 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|x64.Build.0 = Debug|x64 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|Win32.ActiveCfg = Release|Win32 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|Win32.Build.0 = Release|Win32 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|x64.ActiveCfg = Release|x64 - {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|x64.Build.0 = Release|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|Win32.ActiveCfg = Debug|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|Win32.Build.0 = Debug|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|x64.ActiveCfg = Debug|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|x64.Build.0 = Debug|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|Win32.ActiveCfg = Release|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|Win32.Build.0 = Release|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|x64.ActiveCfg = Release|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|x64.Build.0 = Release|x64 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Debug|Win32.ActiveCfg = Debug|Win32 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Debug|Win32.Build.0 = Debug|Win32 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Debug|x64.ActiveCfg = Debug|x64 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Debug|x64.Build.0 = Debug|x64 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Release|Win32.ActiveCfg = Release|Win32 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Release|Win32.Build.0 = Release|Win32 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Release|x64.ActiveCfg = Release|x64 - {96549A3E-D1BD-470E-A5B3-A64803C4DEF5}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2006 +MinimumVisualStudioVersion = 15.0.26228.4 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{53C3AE07-D96F-4F5C-B407-4195084472CF}" + ProjectSection(SolutionItems) = preProject + include\commonutil.h = include\commonutil.h + Plugins.props = Plugins.props + include\toolstatusintf.h = include\toolstatusintf.h + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtendedNotifications", "ExtendedNotifications\ExtendedNotifications.vcxproj", "{80E791B8-AC98-407E-8FF9-5154AF50E887}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToolStatus", "ToolStatus\ToolStatus.vcxproj", "{60B43533-C75E-4741-9E19-C4D581BEF51C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtendedServices", "ExtendedServices\ExtendedServices.vcxproj", "{AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetworkTools", "NetworkTools\NetworkTools.vcxproj", "{B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OnlineChecks", "OnlineChecks\OnlineChecks.vcxproj", "{79D24223-1122-40A9-BC8F-46A2089FE089}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExtendedTools", "ExtendedTools\ExtendedTools.vcxproj", "{3437FD16-A3BF-4938-883A-EB0DF1584A2A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowExplorer", "WindowExplorer\WindowExplorer.vcxproj", "{37488DC1-E45F-4626-A87C-3A854A153D1A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DotNetTools", "DotNetTools\DotNetTools.vcxproj", "{2B3235B0-31E1-44DA-81A1-183F40517E47}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Updater\Updater.vcxproj", "{A0C1595C-FA3E-4B7A-936C-306BC6294C5E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserNotes", "UserNotes\UserNotes.vcxproj", "{7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HardwareDevices", "HardwareDevices\HardwareDevices.vcxproj", "{5F0D72C4-8319-4B61-9E13-6084B680EB90}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonUtil", "CommonUtil\CommonUtil.vcxitems", "{BFAABF4A-4B1B-4B28-B6CB-B55AB6C1109F}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + CommonUtil\CommonUtil.vcxitems*{79d24223-1122-40a9-bc8f-46a2089fe089}*SharedItemsImports = 4 + CommonUtil\CommonUtil.vcxitems*{a0c1595c-fa3e-4b7a-936c-306bc6294c5e}*SharedItemsImports = 4 + CommonUtil\CommonUtil.vcxitems*{b5e0da09-ea01-4d5a-a9d6-5b22db0c306e}*SharedItemsImports = 4 + CommonUtil\CommonUtil.vcxitems*{bfaabf4a-4b1b-4b28-b6cb-b55ab6c1109f}*SharedItemsImports = 9 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|Win32.ActiveCfg = Debug|Win32 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|Win32.Build.0 = Debug|Win32 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|x64.ActiveCfg = Debug|x64 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Debug|x64.Build.0 = Debug|x64 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|Win32.ActiveCfg = Release|Win32 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|Win32.Build.0 = Release|Win32 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|x64.ActiveCfg = Release|x64 + {80E791B8-AC98-407E-8FF9-5154AF50E887}.Release|x64.Build.0 = Release|x64 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|Win32.ActiveCfg = Debug|Win32 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|Win32.Build.0 = Debug|Win32 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|x64.ActiveCfg = Debug|x64 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Debug|x64.Build.0 = Debug|x64 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|Win32.ActiveCfg = Release|Win32 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|Win32.Build.0 = Release|Win32 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|x64.ActiveCfg = Release|x64 + {60B43533-C75E-4741-9E19-C4D581BEF51C}.Release|x64.Build.0 = Release|x64 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|Win32.ActiveCfg = Debug|Win32 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|Win32.Build.0 = Debug|Win32 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|x64.ActiveCfg = Debug|x64 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Debug|x64.Build.0 = Debug|x64 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|Win32.ActiveCfg = Release|Win32 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|Win32.Build.0 = Release|Win32 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|x64.ActiveCfg = Release|x64 + {AC547368-D861-4B88-B5D4-E3B1F1AEEEDD}.Release|x64.Build.0 = Release|x64 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|Win32.Build.0 = Debug|Win32 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|x64.ActiveCfg = Debug|x64 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Debug|x64.Build.0 = Debug|x64 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|Win32.ActiveCfg = Release|Win32 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|Win32.Build.0 = Release|Win32 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|x64.ActiveCfg = Release|x64 + {B5E0DA09-EA01-4D5A-A9D6-5B22DB0C306E}.Release|x64.Build.0 = Release|x64 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|Win32.ActiveCfg = Debug|Win32 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|Win32.Build.0 = Debug|Win32 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|x64.ActiveCfg = Debug|x64 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Debug|x64.Build.0 = Debug|x64 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|Win32.ActiveCfg = Release|Win32 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|Win32.Build.0 = Release|Win32 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|x64.ActiveCfg = Release|x64 + {79D24223-1122-40A9-BC8F-46A2089FE089}.Release|x64.Build.0 = Release|x64 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|Win32.ActiveCfg = Debug|Win32 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|Win32.Build.0 = Debug|Win32 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|x64.ActiveCfg = Debug|x64 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Debug|x64.Build.0 = Debug|x64 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|Win32.ActiveCfg = Release|Win32 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|Win32.Build.0 = Release|Win32 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|x64.ActiveCfg = Release|x64 + {3437FD16-A3BF-4938-883A-EB0DF1584A2A}.Release|x64.Build.0 = Release|x64 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|Win32.ActiveCfg = Debug|Win32 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|Win32.Build.0 = Debug|Win32 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|x64.ActiveCfg = Debug|x64 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Debug|x64.Build.0 = Debug|x64 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|Win32.ActiveCfg = Release|Win32 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|Win32.Build.0 = Release|Win32 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|x64.ActiveCfg = Release|x64 + {37488DC1-E45F-4626-A87C-3A854A153D1A}.Release|x64.Build.0 = Release|x64 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|Win32.ActiveCfg = Debug|Win32 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|Win32.Build.0 = Debug|Win32 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|x64.ActiveCfg = Debug|x64 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Debug|x64.Build.0 = Debug|x64 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|Win32.ActiveCfg = Release|Win32 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|Win32.Build.0 = Release|Win32 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|x64.ActiveCfg = Release|x64 + {2B3235B0-31E1-44DA-81A1-183F40517E47}.Release|x64.Build.0 = Release|x64 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|Win32.Build.0 = Debug|Win32 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|x64.ActiveCfg = Debug|x64 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Debug|x64.Build.0 = Debug|x64 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|Win32.ActiveCfg = Release|Win32 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|Win32.Build.0 = Release|Win32 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|x64.ActiveCfg = Release|x64 + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E}.Release|x64.Build.0 = Release|x64 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|Win32.ActiveCfg = Debug|Win32 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|Win32.Build.0 = Debug|Win32 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|x64.ActiveCfg = Debug|x64 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Debug|x64.Build.0 = Debug|x64 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|Win32.ActiveCfg = Release|Win32 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|Win32.Build.0 = Release|Win32 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|x64.ActiveCfg = Release|x64 + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF}.Release|x64.Build.0 = Release|x64 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|Win32.Build.0 = Debug|Win32 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|x64.ActiveCfg = Debug|x64 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Debug|x64.Build.0 = Debug|x64 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|Win32.ActiveCfg = Release|Win32 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|Win32.Build.0 = Release|Win32 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|x64.ActiveCfg = Release|x64 + {5F0D72C4-8319-4B61-9E13-6084B680EB90}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F936C56D-6A19-47EB-973F-535DF252210E} + EndGlobalSection +EndGlobal diff --git a/plugins/SamplePlugin/SamplePlugin.sln b/plugins/SamplePlugin/SamplePlugin.sln index 6b056c7dab6b..947d0cd473bf 100644 --- a/plugins/SamplePlugin/SamplePlugin.sln +++ b/plugins/SamplePlugin/SamplePlugin.sln @@ -1,26 +1,26 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SamplePlugin", "SamplePlugin.vcxproj", "{C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|Win32.ActiveCfg = Debug|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|Win32.Build.0 = Debug|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|x64.ActiveCfg = Debug|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|x64.Build.0 = Debug|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|Win32.ActiveCfg = Release|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|Win32.Build.0 = Release|Win32 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|x64.ActiveCfg = Release|x64 - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SamplePlugin", "SamplePlugin.vcxproj", "{C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|Win32.ActiveCfg = Debug|Win32 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|Win32.Build.0 = Debug|Win32 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|x64.ActiveCfg = Debug|x64 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Debug|x64.Build.0 = Debug|x64 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|Win32.ActiveCfg = Release|Win32 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|Win32.Build.0 = Release|Win32 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|x64.ActiveCfg = Release|x64 + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/SamplePlugin/SamplePlugin.vcxproj b/plugins/SamplePlugin/SamplePlugin.vcxproj index b03001956d10..876d3bafa7d9 100644 --- a/plugins/SamplePlugin/SamplePlugin.vcxproj +++ b/plugins/SamplePlugin/SamplePlugin.vcxproj @@ -1,172 +1,172 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE} - SamplePlugin - Win32Proj - 10.0.10586.0 - - - - DynamicLibrary - Unicode - true - v140 - - - DynamicLibrary - Unicode - v140 - - - DynamicLibrary - Unicode - true - v140 - - - DynamicLibrary - Unicode - v140 - - - - - - - - - - - - - - - - - - - $(ProjectDir)\bin\$(Configuration)32\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - true - $(ProjectDir)\bin\$(Configuration)64\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - true - $(ProjectDir)\bin\$(Configuration)32\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(ProjectDir)\bin\$(Configuration)64\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - - - - Disabled - ../../sdk/include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - Level3 - EditAndContinue - StdCall - - - ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) - ../../sdk/lib/i386;%(AdditionalLibraryDirectories) - true - Windows - MachineX86 - - - - - Disabled - ../../sdk/include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - - - ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) - ../../sdk/lib/amd64;%(AdditionalLibraryDirectories) - true - Windows - MachineX64 - - - - - MaxSpeed - true - ../../sdk/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - MultiThreaded - true - Level3 - ProgramDatabase - StdCall - - - ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) - ../../sdk/lib/i386;%(AdditionalLibraryDirectories) - true - Windows - true - true - MachineX86 - true - - - - - MaxSpeed - true - ../../sdk/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - MultiThreaded - true - Level3 - ProgramDatabase - StdCall - - - ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) - ../../sdk/lib/amd64;%(AdditionalLibraryDirectories) - true - Windows - true - true - MachineX64 - true - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {C74D269B-3FCC-4C3E-93C7-1B4A94E7BBEE} + SamplePlugin + Win32Proj + 10.0.10586.0 + + + + DynamicLibrary + Unicode + true + v140 + + + DynamicLibrary + Unicode + v140 + + + DynamicLibrary + Unicode + true + v140 + + + DynamicLibrary + Unicode + v140 + + + + + + + + + + + + + + + + + + + $(ProjectDir)\bin\$(Configuration)32\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + true + $(ProjectDir)\bin\$(Configuration)64\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + true + $(ProjectDir)\bin\$(Configuration)32\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(ProjectDir)\bin\$(Configuration)64\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + + + + Disabled + ../../sdk/include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Level3 + EditAndContinue + StdCall + + + ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + ../../sdk/lib/i386;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + ../../sdk/include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + + + ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + ../../sdk/lib/amd64;%(AdditionalLibraryDirectories) + true + Windows + MachineX64 + + + + + MaxSpeed + true + ../../sdk/include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + + + ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + ../../sdk/lib/i386;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + true + + + + + MaxSpeed + true + ../../sdk/include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + + + ProcessHacker.lib;ntdll.lib;%(AdditionalDependencies) + ../../sdk/lib/amd64;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX64 + true + + + + + + + + \ No newline at end of file diff --git a/plugins/SamplePlugin/main.c b/plugins/SamplePlugin/main.c index 5c18eaae5561..45a9349513df 100644 --- a/plugins/SamplePlugin/main.c +++ b/plugins/SamplePlugin/main.c @@ -1,261 +1,261 @@ -#include - -#define ID_SAMPLE_MENU_ITEM 1 -#define ID_SHOW_ME_SOME_OBJECTS 2 - -VOID LoadCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ); - -VOID ShowOptionsCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ); - -VOID MenuItemCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ); - -VOID MainWindowShowingCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ); - -VOID GetProcessHighlightingColorCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ); - -VOID GetProcessTooltipTextCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ); - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; -PH_CALLBACK_REGISTRATION GetProcessHighlightingColorCallbackRegistration; -PH_CALLBACK_REGISTRATION GetProcessTooltipTextCallbackRegistration; - -LOGICAL DllMain( - __in HINSTANCE Instance, - __in ULONG Reason, - __reserved PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - - // Register your plugin with a unique name, otherwise it will fail. - PluginInstance = PhRegisterPlugin(L"YourName.SamplePlugin", Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Sample Plugin"; - info->Author = L"Someone"; - info->Description = L"Description goes here"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainWindowShowing), - MainWindowShowingCallback, - NULL, - &MainWindowShowingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), - GetProcessHighlightingColorCallback, - NULL, - &GetProcessHighlightingColorCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), - GetProcessTooltipTextCallback, - NULL, - &GetProcessTooltipTextCallbackRegistration - ); - - // Add some settings. Note that we cannot access these settings - // in DllMain. Settings must be added in DllMain. - { - static PH_SETTING_CREATE settings[] = - { - // You must prepend your plugin name to the setting names. - { IntegerSettingType, L"ProcessHacker.SamplePlugin.SomeInteger", L"1234" }, - { StringSettingType, L"ProcessHacker.SamplePlugin.SomeString", L"my string" } - }; - - PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); - } - } - break; - } - - return TRUE; -} - -VOID LoadCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ) -{ - ULONG myInteger; - PPH_STRING myString; - - myInteger = PhGetIntegerSetting(L"ProcessHacker.SamplePlugin.SomeInteger"); - // Do stuff to the integer. Possibly modify the setting. - PhSetIntegerSetting(L"ProcessHacker.SamplePlugin.SomeInteger", myInteger + 100); - - myString = PhGetStringSetting(L"ProcessHacker.SamplePlugin.SomeString"); - // Do stuff to the string. - // Dereference the string when you're done, or memory will be leaked. - PhDereferenceObject(myString); -} - -VOID ShowOptionsCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ) -{ - PhShowError((HWND)Parameter, L"Show some options here."); -} - -BOOLEAN NTAPI EnumDirectoryObjectsCallback( - __in PPH_STRINGREF Name, - __in PPH_STRINGREF TypeName, - __in_opt PVOID Context - ) -{ - INT result; - PPH_STRING name; - PPH_STRING typeName; - - name = PhCreateString2(Name); - typeName = PhCreateString2(TypeName); - result = PhShowMessage( - PhMainWndHandle, - MB_ICONINFORMATION | MB_OKCANCEL, - L"%s: %s", - name->Buffer, - typeName->Buffer - ); - PhDereferenceObject(name); - PhDereferenceObject(typeName); - - return result == IDOK; -} - -VOID MenuItemCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case ID_SAMPLE_MENU_ITEM: - { - PhShowInformation(PhMainWndHandle, L"You clicked the sample menu item!"); - } - break; - case ID_SHOW_ME_SOME_OBJECTS: - { - NTSTATUS status; - HANDLE directoryHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING name; - - // Use the Native API seamlessly alongside Win32. - RtlInitUnicodeString(&name, L"\\"); - InitializeObjectAttributes(&oa, &name, 0, NULL, NULL); - - if (NT_SUCCESS(status = NtOpenDirectoryObject(&directoryHandle, DIRECTORY_QUERY, &oa))) - { - PhEnumDirectoryObjects(directoryHandle, EnumDirectoryObjectsCallback, NULL); - NtClose(directoryHandle); - } - } - break; - } -} - -VOID MainWindowShowingCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ) -{ - // $ won't match anything, so the menu item will get added to the end. - PhPluginAddMenuItem(PluginInstance, PH_MENU_ITEM_LOCATION_TOOLS, L"$", - ID_SAMPLE_MENU_ITEM, L"Sample menu item", NULL); - PhPluginAddMenuItem(PluginInstance, PH_MENU_ITEM_LOCATION_TOOLS, L"$", - ID_SHOW_ME_SOME_OBJECTS, L"Show me some objects", NULL); -} - -VOID GetProcessHighlightingColorCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ) -{ - PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; - PPH_PROCESS_ITEM processItem; - - processItem = getHighlightingColor->Parameter; - - // Optional: if another plugin handled the highlighting, don't override it. - if (getHighlightingColor->Handled) - return; - - // Set the background color of svchost.exe processes to black. - if (PhEqualString2(processItem->ProcessName, L"svchost.exe", TRUE)) - { - getHighlightingColor->BackColor = RGB(0x00, 0x00, 0x00); - getHighlightingColor->Cache = TRUE; - getHighlightingColor->Handled = TRUE; - } -} - -VOID GetProcessTooltipTextCallback( - __in_opt PVOID Parameter, - __in_opt PVOID Context - ) -{ - PPH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText = Parameter; - PPH_PROCESS_ITEM processItem; - - processItem = getTooltipText->Parameter; - - // Put some text into the tooltip. This will go in just before the Notes section. - PhAppendFormatStringBuilder( - getTooltipText->StringBuilder, - L"Sample plugin:\n The process name is: %s\n", - processItem->ProcessName->Buffer - ); -} +#include + +#define ID_SAMPLE_MENU_ITEM 1 +#define ID_SHOW_ME_SOME_OBJECTS 2 + +VOID LoadCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ); + +VOID ShowOptionsCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ); + +VOID MenuItemCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ); + +VOID MainWindowShowingCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ); + +VOID GetProcessHighlightingColorCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ); + +VOID GetProcessTooltipTextCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ); + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; +PH_CALLBACK_REGISTRATION GetProcessHighlightingColorCallbackRegistration; +PH_CALLBACK_REGISTRATION GetProcessTooltipTextCallbackRegistration; + +LOGICAL DllMain( + __in HINSTANCE Instance, + __in ULONG Reason, + __reserved PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + + // Register your plugin with a unique name, otherwise it will fail. + PluginInstance = PhRegisterPlugin(L"YourName.SamplePlugin", Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Sample Plugin"; + info->Author = L"Someone"; + info->Description = L"Description goes here"; + info->HasOptions = TRUE; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainWindowShowing), + MainWindowShowingCallback, + NULL, + &MainWindowShowingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), + GetProcessHighlightingColorCallback, + NULL, + &GetProcessHighlightingColorCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), + GetProcessTooltipTextCallback, + NULL, + &GetProcessTooltipTextCallbackRegistration + ); + + // Add some settings. Note that we cannot access these settings + // in DllMain. Settings must be added in DllMain. + { + static PH_SETTING_CREATE settings[] = + { + // You must prepend your plugin name to the setting names. + { IntegerSettingType, L"ProcessHacker.SamplePlugin.SomeInteger", L"1234" }, + { StringSettingType, L"ProcessHacker.SamplePlugin.SomeString", L"my string" } + }; + + PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); + } + } + break; + } + + return TRUE; +} + +VOID LoadCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ) +{ + ULONG myInteger; + PPH_STRING myString; + + myInteger = PhGetIntegerSetting(L"ProcessHacker.SamplePlugin.SomeInteger"); + // Do stuff to the integer. Possibly modify the setting. + PhSetIntegerSetting(L"ProcessHacker.SamplePlugin.SomeInteger", myInteger + 100); + + myString = PhGetStringSetting(L"ProcessHacker.SamplePlugin.SomeString"); + // Do stuff to the string. + // Dereference the string when you're done, or memory will be leaked. + PhDereferenceObject(myString); +} + +VOID ShowOptionsCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ) +{ + PhShowError((HWND)Parameter, L"Show some options here."); +} + +BOOLEAN NTAPI EnumDirectoryObjectsCallback( + __in PPH_STRINGREF Name, + __in PPH_STRINGREF TypeName, + __in_opt PVOID Context + ) +{ + INT result; + PPH_STRING name; + PPH_STRING typeName; + + name = PhCreateString2(Name); + typeName = PhCreateString2(TypeName); + result = PhShowMessage( + PhMainWndHandle, + MB_ICONINFORMATION | MB_OKCANCEL, + L"%s: %s", + name->Buffer, + typeName->Buffer + ); + PhDereferenceObject(name); + PhDereferenceObject(typeName); + + return result == IDOK; +} + +VOID MenuItemCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + switch (menuItem->Id) + { + case ID_SAMPLE_MENU_ITEM: + { + PhShowInformation(PhMainWndHandle, L"You clicked the sample menu item!"); + } + break; + case ID_SHOW_ME_SOME_OBJECTS: + { + NTSTATUS status; + HANDLE directoryHandle; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING name; + + // Use the Native API seamlessly alongside Win32. + RtlInitUnicodeString(&name, L"\\"); + InitializeObjectAttributes(&oa, &name, 0, NULL, NULL); + + if (NT_SUCCESS(status = NtOpenDirectoryObject(&directoryHandle, DIRECTORY_QUERY, &oa))) + { + PhEnumDirectoryObjects(directoryHandle, EnumDirectoryObjectsCallback, NULL); + NtClose(directoryHandle); + } + } + break; + } +} + +VOID MainWindowShowingCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ) +{ + // $ won't match anything, so the menu item will get added to the end. + PhPluginAddMenuItem(PluginInstance, PH_MENU_ITEM_LOCATION_TOOLS, L"$", + ID_SAMPLE_MENU_ITEM, L"Sample menu item", NULL); + PhPluginAddMenuItem(PluginInstance, PH_MENU_ITEM_LOCATION_TOOLS, L"$", + ID_SHOW_ME_SOME_OBJECTS, L"Show me some objects", NULL); +} + +VOID GetProcessHighlightingColorCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ) +{ + PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; + PPH_PROCESS_ITEM processItem; + + processItem = getHighlightingColor->Parameter; + + // Optional: if another plugin handled the highlighting, don't override it. + if (getHighlightingColor->Handled) + return; + + // Set the background color of svchost.exe processes to black. + if (PhEqualString2(processItem->ProcessName, L"svchost.exe", TRUE)) + { + getHighlightingColor->BackColor = RGB(0x00, 0x00, 0x00); + getHighlightingColor->Cache = TRUE; + getHighlightingColor->Handled = TRUE; + } +} + +VOID GetProcessTooltipTextCallback( + __in_opt PVOID Parameter, + __in_opt PVOID Context + ) +{ + PPH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText = Parameter; + PPH_PROCESS_ITEM processItem; + + processItem = getTooltipText->Parameter; + + // Put some text into the tooltip. This will go in just before the Notes section. + PhAppendFormatStringBuilder( + getTooltipText->StringBuilder, + L"Sample plugin:\n The process name is: %s\n", + processItem->ProcessName->Buffer + ); +} diff --git a/plugins/SbieSupport/SbieSupport.vcxproj b/plugins/SbieSupport/SbieSupport.vcxproj deleted file mode 100644 index 413788b0c8cd..000000000000 --- a/plugins/SbieSupport/SbieSupport.vcxproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {EEF1E81D-D286-422A-89E6-C6C8F3BE648A} - SbieSupport - Win32Proj - SbieSupport - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/SbieSupport/main.c b/plugins/SbieSupport/main.c deleted file mode 100644 index bbd753b09245..000000000000 --- a/plugins/SbieSupport/main.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Process Hacker Sandboxie Support - - * main program - * - * Copyright (C) 2010-2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include "resource.h" -#include "sbiedll.h" - -typedef struct _BOX_INFO -{ - WCHAR BoxName[34]; - PH_STRINGREF IpcRoot; - WCHAR IpcRootBuffer[256]; -} BOX_INFO, *PBOX_INFO; - -typedef struct _BOXED_PROCESS -{ - HANDLE ProcessId; - WCHAR BoxName[34]; -} BOXED_PROCESS, *PBOXED_PROCESS; - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI GetProcessHighlightingColorCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI GetProcessTooltipTextCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI GetIsDotNetDirectoryNamesCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -VOID NTAPI RefreshSandboxieInfo( - _In_opt_ PVOID Context, - _In_ BOOLEAN TimerOrWaitFired - ); - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -PH_CALLBACK_REGISTRATION GetProcessHighlightingColorCallbackRegistration; -PH_CALLBACK_REGISTRATION GetProcessTooltipTextCallbackRegistration; - -P_SbieApi_QueryBoxPath SbieApi_QueryBoxPath; -P_SbieApi_EnumBoxes SbieApi_EnumBoxes; -P_SbieApi_EnumProcessEx SbieApi_EnumProcessEx; -P_SbieDll_KillAll SbieDll_KillAll; - -PPH_HASHTABLE BoxedProcessesHashtable; -PH_QUEUED_LOCK BoxedProcessesLock = PH_QUEUED_LOCK_INIT; -BOOLEAN BoxedProcessesUpdated = FALSE; - -BOX_INFO BoxInfo[16]; -ULONG BoxInfoCount; - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Sandboxie Support"; - info->Author = L"wj32"; - info->Description = L"Provides functionality for sandboxed processes."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1115"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), - MainMenuInitializingCallback, - NULL, - &MainMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessesUpdated), - ProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), - GetProcessHighlightingColorCallback, - NULL, - &GetProcessHighlightingColorCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackGetProcessTooltipText), - GetProcessTooltipTextCallback, - NULL, - &GetProcessTooltipTextCallbackRegistration - ); - - { - static PH_SETTING_CREATE settings[] = - { - { StringSettingType, SETTING_NAME_SBIE_DLL_PATH, L"C:\\Program Files\\Sandboxie\\SbieDll.dll" } - }; - - PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); - } - } - break; - } - - return TRUE; -} - -BOOLEAN NTAPI BoxedProcessesEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - return ((PBOXED_PROCESS)Entry1)->ProcessId == ((PBOXED_PROCESS)Entry2)->ProcessId; -} - -ULONG NTAPI BoxedProcessesHashFunction( - _In_ PVOID Entry - ) -{ - return HandleToUlong(((PBOXED_PROCESS)Entry)->ProcessId) / 4; -} - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_STRING sbieDllPath; - HMODULE module; - HANDLE timerQueueHandle; - HANDLE timerHandle; - - BoxedProcessesHashtable = PhCreateHashtable( - sizeof(BOXED_PROCESS), - BoxedProcessesEqualFunction, - BoxedProcessesHashFunction, - 32 - ); - - sbieDllPath = PhaGetStringSetting(SETTING_NAME_SBIE_DLL_PATH); - module = LoadLibrary(sbieDllPath->Buffer); - - SbieApi_QueryBoxPath = PhGetProcedureAddress(module, SbieApi_QueryBoxPath_Name, 0); - SbieApi_EnumBoxes = PhGetProcedureAddress(module, SbieApi_EnumBoxes_Name, 0); - SbieApi_EnumProcessEx = PhGetProcedureAddress(module, SbieApi_EnumProcessEx_Name, 0); - SbieDll_KillAll = PhGetProcedureAddress(module, SbieDll_KillAll_Name, 0); - - if (NT_SUCCESS(RtlCreateTimerQueue(&timerQueueHandle))) - { - RtlCreateTimer(timerQueueHandle, &timerHandle, RefreshSandboxieInfo, NULL, 0, 4000, 0); - } -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - (HWND)Parameter, - OptionsDlgProc - ); -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case 1: - { - if (PhShowConfirmMessage( - PhMainWndHandle, - L"terminate", - L"all sandboxed processes", - NULL, - FALSE - )) - { - PBOXED_PROCESS boxedProcess; - ULONG enumerationKey = 0; - - // Make sure we have an update-to-date list. - RefreshSandboxieInfo(NULL, FALSE); - - PhAcquireQueuedLockShared(&BoxedProcessesLock); - - while (PhEnumHashtable(BoxedProcessesHashtable, &boxedProcess, &enumerationKey)) - { - HANDLE processHandle; - - if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_TERMINATE, boxedProcess->ProcessId))) - { - PhTerminateProcess(processHandle, STATUS_SUCCESS); - NtClose(processHandle); - } - } - - PhReleaseQueuedLockShared(&BoxedProcessesLock); - } - } - break; - } -} - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - - if (!SbieDll_KillAll) - return; - if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_TOOLS) - return; - - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, 1, L"Terminate sandboxed processes", NULL), -1); -} - -VOID NTAPI ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PBOXED_PROCESS boxedProcess; - ULONG enumerationKey = 0; - - if (BoxedProcessesUpdated) - { - // Invalidate the nodes of boxed processes (so they use the correct highlighting color). - - PhAcquireQueuedLockShared(&BoxedProcessesLock); - - if (BoxedProcessesUpdated) - { - while (PhEnumHashtable(BoxedProcessesHashtable, &boxedProcess, &enumerationKey)) - { - PPH_PROCESS_NODE processNode; - - if (processNode = PhFindProcessNode(boxedProcess->ProcessId)) - PhUpdateProcessNode(processNode); - } - - BoxedProcessesUpdated = FALSE; - } - - PhReleaseQueuedLockShared(&BoxedProcessesLock); - } -} - -VOID NTAPI GetProcessHighlightingColorCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; - BOXED_PROCESS lookupBoxedProcess; - PBOXED_PROCESS boxedProcess; - - PhAcquireQueuedLockShared(&BoxedProcessesLock); - - lookupBoxedProcess.ProcessId = ((PPH_PROCESS_ITEM)getHighlightingColor->Parameter)->ProcessId; - - if (boxedProcess = PhFindEntryHashtable(BoxedProcessesHashtable, &lookupBoxedProcess)) - { - getHighlightingColor->BackColor = RGB(0x33, 0x33, 0x00); - getHighlightingColor->Cache = TRUE; - getHighlightingColor->Handled = TRUE; - } - - PhReleaseQueuedLockShared(&BoxedProcessesLock); -} - -VOID NTAPI GetProcessTooltipTextCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText = Parameter; - BOXED_PROCESS lookupBoxedProcess; - PBOXED_PROCESS boxedProcess; - - PhAcquireQueuedLockShared(&BoxedProcessesLock); - - lookupBoxedProcess.ProcessId = ((PPH_PROCESS_ITEM)getTooltipText->Parameter)->ProcessId; - - if (boxedProcess = PhFindEntryHashtable(BoxedProcessesHashtable, &lookupBoxedProcess)) - { - PhAppendFormatStringBuilder(getTooltipText->StringBuilder, L"Sandboxie:\n Box name: %s\n", boxedProcess->BoxName); - } - - PhReleaseQueuedLockShared(&BoxedProcessesLock); -} - -VOID NTAPI RefreshSandboxieInfo( - _In_opt_ PVOID Context, - _In_ BOOLEAN TimerOrWaitFired - ) -{ - LONG index; - WCHAR boxName[34]; - ULONG pids[512]; - PBOX_INFO boxInfo; - - if (!SbieApi_QueryBoxPath || !SbieApi_EnumBoxes || !SbieApi_EnumProcessEx) - return; - - PhAcquireQueuedLockExclusive(&BoxedProcessesLock); - - PhClearHashtable(BoxedProcessesHashtable); - - BoxInfoCount = 0; - - index = -1; - - while ((index = SbieApi_EnumBoxes(index, boxName)) != -1) - { - if (SbieApi_EnumProcessEx(boxName, TRUE, 0, pids) == 0) - { - ULONG count; - PULONG pid; - - count = pids[0]; - pid = &pids[1]; - - while (count != 0) - { - BOXED_PROCESS boxedProcess; - - boxedProcess.ProcessId = UlongToHandle(*pid); - memcpy(boxedProcess.BoxName, boxName, sizeof(boxName)); - - PhAddEntryHashtable(BoxedProcessesHashtable, &boxedProcess); - - count--; - pid++; - } - } - - if (BoxInfoCount < 16) - { - ULONG filePathLength = 0; - ULONG keyPathLength = 0; - ULONG ipcPathLength = 0; - - boxInfo = &BoxInfo[BoxInfoCount++]; - memcpy(boxInfo->BoxName, boxName, sizeof(boxName)); - - SbieApi_QueryBoxPath( - boxName, - NULL, - NULL, - NULL, - &filePathLength, - &keyPathLength, - &ipcPathLength - ); - - if (ipcPathLength < sizeof(boxInfo->IpcRootBuffer)) - { - boxInfo->IpcRootBuffer[0] = 0; - SbieApi_QueryBoxPath( - boxName, - NULL, - NULL, - boxInfo->IpcRootBuffer, - NULL, - NULL, - &ipcPathLength - ); - - if (boxInfo->IpcRootBuffer[0] != 0) - { - PhInitializeStringRef(&boxInfo->IpcRoot, boxInfo->IpcRootBuffer); - } - else - { - BoxInfoCount--; - } - } - else - { - BoxInfoCount--; - } - } - } - - BoxedProcessesUpdated = TRUE; - - PhReleaseQueuedLockExclusive(&BoxedProcessesLock); -} - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PPH_STRING sbieDllPath; - - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - sbieDllPath = PhaGetStringSetting(SETTING_NAME_SBIE_DLL_PATH); - SetDlgItemText(hwndDlg, IDC_SBIEDLLPATH, sbieDllPath->Buffer); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PhSetStringSetting2(SETTING_NAME_SBIE_DLL_PATH, - &PhaGetDlgItemText(hwndDlg, IDC_SBIEDLLPATH)->sr); - - EndDialog(hwndDlg, IDOK); - } - break; - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"SbieDll.dll", L"SbieDll.dll" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_SBIEDLLPATH))); - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - SetDlgItemText(hwndDlg, IDC_SBIEDLLPATH, fileName->Buffer); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - } - - return FALSE; -} diff --git a/plugins/SbieSupport/resource.h b/plugins/SbieSupport/resource.h deleted file mode 100644 index 343b05f2006b..000000000000 --- a/plugins/SbieSupport/resource.h +++ /dev/null @@ -1,18 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by SbieSupport.rc -// -#define IDD_OPTIONS 101 -#define IDC_SBIEDLLPATH 1001 -#define IDC_BROWSE 1002 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1003 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/plugins/SbieSupport/sbiedll.h b/plugins/SbieSupport/sbiedll.h deleted file mode 100644 index 073349f2e7b8..000000000000 --- a/plugins/SbieSupport/sbiedll.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SBIEDLL_H -#define SBIEDLL_H - -#define PLUGIN_NAME L"ProcessHacker.SbieSupport" -#define SETTING_NAME_SBIE_DLL_PATH (PLUGIN_NAME L".SbieDllPath") - -typedef LONG (__stdcall *P_SbieApi_QueryBoxPath)( - const WCHAR *box_name, // pointer to WCHAR [34] - WCHAR *file_path, - WCHAR *key_path, - WCHAR *ipc_path, - ULONG *file_path_len, - ULONG *key_path_len, - ULONG *ipc_path_len); - -typedef LONG (__stdcall *P_SbieApi_EnumBoxes)( - LONG index, // initialize to -1 - WCHAR *box_name); // pointer to WCHAR [34] - -typedef LONG (__stdcall *P_SbieApi_EnumProcessEx)( - const WCHAR *box_name, // pointer to WCHAR [34] - BOOLEAN all_sessions, - ULONG which_session, - ULONG *boxed_pids); // pointer to ULONG [512] - -typedef BOOLEAN (__stdcall *P_SbieDll_KillAll)( - ULONG session_id, - const WCHAR *box_name); - -#ifdef _WIN64 -#define SbieApi_QueryBoxPath_Name "SbieApi_QueryBoxPath" -#define SbieApi_EnumBoxes_Name "SbieApi_EnumBoxes" -#define SbieApi_EnumProcessEx_Name "SbieApi_EnumProcessEx" -#define SbieDll_KillAll_Name "SbieDll_KillAll" -#else -#define SbieApi_QueryBoxPath_Name "_SbieApi_QueryBoxPath@28" -#define SbieApi_EnumBoxes_Name "_SbieApi_EnumBoxes@8" -#define SbieApi_EnumProcessEx_Name "_SbieApi_EnumProcessEx@16" -#define SbieDll_KillAll_Name "_SbieDll_KillAll@8" -#endif - -#endif diff --git a/plugins/ToolStatus/CHANGELOG.txt b/plugins/ToolStatus/CHANGELOG.txt index 2e1ec94f0175..87015dfac5d5 100644 --- a/plugins/ToolStatus/CHANGELOG.txt +++ b/plugins/ToolStatus/CHANGELOG.txt @@ -1,64 +1,64 @@ -2.4 - * Added 32x32 icons for high DPI displays - * Fixed status bar crash - -2.3 - * Added Auto-hide main menu - * Added CPU, Memory and IO graphs to main window - * Added Right-click menu on the Toolbar - * Added Toolbar customize dialog - * Added Statusbar customize dialog - -2.2 - * Added Modern Toolbar theme - * Added portable Toolbar customization - * Added Toolbar power menu (ported from PH1.x) - * Fixed Toolbar DPI scaling issues - -2.1 - * Fixed Auto-hide Searchbox (Ctrl+K to show) - -2.0 - * Added Toolbar customization - * Added Rebar Chevron support - * Added Auto-hide Searchbox - * Fixed searching multiple processes - -1.9 - * Added PNG images - * Fixed search box fonts - * Fixed plugin settings state (disable/enable) - * Updated Searchbox UI - * Updated Searchbox commands - * Updated plugin settings - -1.8 - * Added search box - -1.7 - * Added option to configure Toolbar display style. - -1.6 - * Fixed Always on Top being reset when using Find Window - * Fixed Esc key when using Find Window - -1.5 - * Added Find Window and Kill - -1.4 - * Added option to resolve ghost windows to the hung windows - they represent - -1.3 - * Fixed layout problems - -1.2 - * Added options - * Status bar panel widths are more stable - -1.1 - * Fixed hanging when using Find Window on hung programs - * Fixed status bar with no selected parts - -1.0 +2.4 + * Added 32x32 icons for high DPI displays + * Fixed status bar crash + +2.3 + * Added Auto-hide main menu + * Added CPU, Memory and IO graphs to main window + * Added Right-click menu on the Toolbar + * Added Toolbar customize dialog + * Added Statusbar customize dialog + +2.2 + * Added Modern Toolbar theme + * Added portable Toolbar customization + * Added Toolbar power menu (ported from PH1.x) + * Fixed Toolbar DPI scaling issues + +2.1 + * Fixed Auto-hide Searchbox (Ctrl+K to show) + +2.0 + * Added Toolbar customization + * Added Rebar Chevron support + * Added Auto-hide Searchbox + * Fixed searching multiple processes + +1.9 + * Added PNG images + * Fixed search box fonts + * Fixed plugin settings state (disable/enable) + * Updated Searchbox UI + * Updated Searchbox commands + * Updated plugin settings + +1.8 + * Added search box + +1.7 + * Added option to configure Toolbar display style. + +1.6 + * Fixed Always on Top being reset when using Find Window + * Fixed Esc key when using Find Window + +1.5 + * Added Find Window and Kill + +1.4 + * Added option to resolve ghost windows to the hung windows + they represent + +1.3 + * Fixed layout problems + +1.2 + * Added options + * Status bar panel widths are more stable + +1.1 + * Fixed hanging when using Find Window on hung programs + * Fixed status bar with no selected parts + +1.0 * Initial release \ No newline at end of file diff --git a/plugins/ToolStatus/ToolStatus.rc b/plugins/ToolStatus/ToolStatus.rc index 9b9ae88f59cb..44efe09f0530 100644 --- a/plugins/ToolStatus/ToolStatus.rc +++ b/plugins/ToolStatus/ToolStatus.rc @@ -1,277 +1,255 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,4,0,0 - PRODUCTVERSION 2,4,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "dmex" - VALUE "FileDescription", "ToolStatus plugin for Process Hacker" - VALUE "FileVersion", "2.4" - VALUE "InternalName", "ProcessHacker.ToolStatus" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ToolStatus.dll" - VALUE "ProductName", "ToolStatus plugin for Process Hacker" - VALUE "ProductVersion", "2.4" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_OPTIONS DIALOGEX 0, 0, 191, 103 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Enable toolbar",IDC_ENABLE_TOOLBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,62,10 - CONTROL "Enable status bar",IDC_ENABLE_STATUSBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,72,10 - CONTROL "Resolve ghost windows to hung windows",IDC_RESOLVEGHOSTWINDOWS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,32,152,10 - CONTROL "Auto-hide main menu (Alt or F10 key toggle)",IDC_ENABLE_AUTOHIDE_MENU, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,44,157,10 - DEFPUSHBUTTON "Close",IDOK,134,82,50,14 - LTEXT "Note: Right-click the toolbar on the main window to customize the icons and graphs.",IDC_STATIC,7,59,157,18 -END - -IDD_CUSTOMIZE_TB DIALOGEX 0, 0, 369, 188 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Customize Toolbar" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Available toolbar buttons:",IDC_STATIC,7,7,85,8 - LISTBOX IDC_AVAILABLE,7,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Add >",IDC_ADD,133,50,50,14 - PUSHBUTTON "< Remove",IDC_REMOVE,133,68,50,14 - LTEXT "Current toolbar buttons:",IDC_STATIC,188,7,81,8 - LISTBOX IDC_CURRENT,187,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Move up",IDC_MOVEUP,312,18,50,14 - PUSHBUTTON "Move down",IDC_MOVEDOWN,312,36,50,14 - LTEXT "Text options:",IDC_STATIC,7,142,44,8 - COMBOBOX IDC_TEXTOPTIONS,56,139,113,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Search box:",IDC_STATIC,7,158,45,8 - COMBOBOX IDC_SEARCHOPTIONS,56,155,113,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Enable modern icons",IDC_ENABLE_MODERN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,139,83,10 - CONTROL "Auto-hide main menu (Alt or F10 key toggle)",IDC_ENABLE_AUTOHIDE_MENU, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,152,157,10 - PUSHBUTTON "Reset",IDC_RESET,258,167,50,14 - DEFPUSHBUTTON "Close",IDCANCEL,312,167,50,14 -END - -IDD_CUSTOMIZE_SB DIALOGEX 0, 0, 373, 142 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Customize Status Bar" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Available status bar entries:",-1,7,7,91,8 - LISTBOX IDC_AVAILABLE,7,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Add >",IDC_ADD,133,50,50,14 - PUSHBUTTON "< Remove",IDC_REMOVE,133,68,50,14 - LTEXT "Current status bar entries:",-1,188,7,87,8 - LISTBOX IDC_CURRENT,187,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Move up",IDC_MOVEUP,316,18,50,14 - PUSHBUTTON "Move down",IDC_MOVEDOWN,316,36,50,14 - PUSHBUTTON "Reset",IDC_RESET,316,103,50,14 - DEFPUSHBUTTON "Close",IDCANCEL,316,121,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 184 - TOPMARGIN, 7 - BOTTOMMARGIN, 96 - END - - IDD_CUSTOMIZE_TB, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 362 - TOPMARGIN, 7 - BOTTOMMARGIN, 181 - END - - IDD_CUSTOMIZE_SB, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 366 - TOPMARGIN, 7 - BOTTOMMARGIN, 135 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Accelerator -// - -IDR_MAINWND_ACCEL ACCELERATORS -BEGIN - "K", ID_SEARCH, VIRTKEY, CONTROL, NOINVERT -END - - -///////////////////////////////////////////////////////////////////////////// -// -// PNG -// - -IDB_APPLICATION_GET_MODERN PNG "resources\\application_get_modern.png" - -IDB_APPLICATION_GO_MODERN PNG "resources\\application_go_modern.png" - -IDB_APPLICATION_MODERN PNG "resources\\application_modern.png" - -IDB_ARROW_REFRESH_MODERN PNG "resources\\arrow_refresh_modern.png" - -IDB_CHART_LINE_MODERN PNG "resources\\chart_line_modern.png" - -IDB_COG_EDIT_MODERN PNG "resources\\cog_edit_modern.png" - -IDB_CROSS_MODERN PNG "resources\\cross_modern.png" - -IDB_FIND_MODERN PNG "resources\\find_modern.png" - -IDB_POWER_MODERN PNG "resources\\lightbulb_off_modern.png" - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_CUSTOMIZE_TB AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_CUSTOMIZE_SB AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_ARROW_REFRESH ICON "resources\\arrow_refresh.ico" - -IDI_COG_EDIT ICON "resources\\cog_edit.ico" - -IDI_FIND ICON "resources\\find.ico" - -IDI_CHART_LINE ICON "resources\\chart_line.ico" - -IDI_TBAPPLICATION ICON "resources\\application.ico" - -IDI_APPLICATION_GO ICON "resources\\application_go.ico" - -IDI_CROSS ICON "resources\\cross.ico" - -IDI_APPLICATION_GET ICON "resources\\application_get.ico" - -IDI_LIGHTBULB_OFF ICON "resources\\lightbulb_off.ico" - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,4,0,0 + PRODUCTVERSION 2,4,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "dmex" + VALUE "FileDescription", "ToolStatus plugin for Process Hacker" + VALUE "FileVersion", "2.4" + VALUE "InternalName", "ProcessHacker.ToolStatus" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "ToolStatus.dll" + VALUE "ProductName", "ToolStatus plugin for Process Hacker" + VALUE "ProductVersion", "2.4" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOGEX 0, 0, 191, 84 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Enable toolbar",IDC_ENABLE_TOOLBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,62,10 + CONTROL "Enable status bar",IDC_ENABLE_STATUSBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,72,10 + CONTROL "Resolve ghost windows to hung windows",IDC_RESOLVEGHOSTWINDOWS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,32,152,10 + CONTROL "Auto-hide main menu (Alt or F10 key toggle)",IDC_ENABLE_AUTOHIDE_MENU, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,44,157,10 + LTEXT "Note: Right-click the toolbar on the main window to customize the icons and graphs.",IDC_STATIC,7,59,157,18 +END + +IDD_CUSTOMIZE_TB DIALOGEX 0, 0, 369, 188 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Customize Toolbar" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Available toolbar buttons:",IDC_STATIC,7,7,85,8 + LISTBOX IDC_AVAILABLE,7,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add >",IDC_ADD,133,50,50,14 + PUSHBUTTON "< Remove",IDC_REMOVE,133,68,50,14 + LTEXT "Current toolbar buttons:",IDC_STATIC,188,7,81,8 + LISTBOX IDC_CURRENT,187,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Move up",IDC_MOVEUP,312,18,50,14 + PUSHBUTTON "Move down",IDC_MOVEDOWN,312,36,50,14 + LTEXT "Text options:",IDC_STATIC,7,142,44,8 + COMBOBOX IDC_TEXTOPTIONS,56,139,113,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Search box:",IDC_STATIC,7,158,45,8 + COMBOBOX IDC_SEARCHOPTIONS,56,155,113,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Enable modern icons",IDC_ENABLE_MODERN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,139,83,10 + CONTROL "Auto-hide main menu (Alt or F10 key toggle)",IDC_ENABLE_AUTOHIDE_MENU, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,152,157,10 + PUSHBUTTON "Reset",IDC_RESET,258,167,50,14 + DEFPUSHBUTTON "Close",IDCANCEL,312,167,50,14 +END + +IDD_CUSTOMIZE_SB DIALOGEX 0, 0, 373, 142 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Customize Status Bar" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Available status bar entries:",-1,7,7,91,8 + LISTBOX IDC_AVAILABLE,7,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add >",IDC_ADD,133,50,50,14 + PUSHBUTTON "< Remove",IDC_REMOVE,133,68,50,14 + LTEXT "Current status bar entries:",-1,188,7,87,8 + LISTBOX IDC_CURRENT,187,18,122,117,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Move up",IDC_MOVEUP,316,18,50,14 + PUSHBUTTON "Move down",IDC_MOVEDOWN,316,36,50,14 + PUSHBUTTON "Reset",IDC_RESET,316,103,50,14 + DEFPUSHBUTTON "Close",IDCANCEL,316,121,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 184 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_CUSTOMIZE_TB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 362 + TOPMARGIN, 7 + BOTTOMMARGIN, 181 + END + + IDD_CUSTOMIZE_SB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 366 + TOPMARGIN, 7 + BOTTOMMARGIN, 135 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINWND_ACCEL ACCELERATORS +BEGIN + "K", ID_SEARCH, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_APPLICATION_GET_MODERN PNG "resources\\application_get_modern.png" + +IDB_APPLICATION_GO_MODERN PNG "resources\\application_go_modern.png" + +IDB_APPLICATION_MODERN PNG "resources\\application_modern.png" + +IDB_ARROW_REFRESH_MODERN PNG "resources\\arrow_refresh_modern.png" + +IDB_CHART_LINE_MODERN PNG "resources\\chart_line_modern.png" + +IDB_COG_EDIT_MODERN PNG "resources\\cog_edit_modern.png" + +IDB_CROSS_MODERN PNG "resources\\cross_modern.png" + +IDB_FIND_MODERN PNG "resources\\find_modern.png" + +IDB_POWER_MODERN PNG "resources\\lightbulb_off_modern.png" + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ARROW_REFRESH ICON "resources\\arrow_refresh.ico" + +IDI_COG_EDIT ICON "resources\\cog_edit.ico" + +IDI_FIND ICON "resources\\find.ico" + +IDI_CHART_LINE ICON "resources\\chart_line.ico" + +IDI_TBAPPLICATION ICON "resources\\application.ico" + +IDI_APPLICATION_GO ICON "resources\\application_go.ico" + +IDI_CROSS ICON "resources\\cross.ico" + +IDI_APPLICATION_GET ICON "resources\\application_get.ico" + +IDI_LIGHTBULB_OFF ICON "resources\\lightbulb_off.ico" + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/ToolStatus/ToolStatus.vcxproj b/plugins/ToolStatus/ToolStatus.vcxproj index 08a9206043e7..45ae6d69d8ee 100644 --- a/plugins/ToolStatus/ToolStatus.vcxproj +++ b/plugins/ToolStatus/ToolStatus.vcxproj @@ -1,116 +1,119 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {60B43533-C75E-4741-9E19-C4D581BEF51C} - ToolStatus - Win32Proj - ToolStatus - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {60B43533-C75E-4741-9E19-C4D581BEF51C} + ToolStatus + Win32Proj + ToolStatus + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + uxtheme.lib;%(AdditionalDependencies) + comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + uxtheme.lib;%(AdditionalDependencies) + comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + uxtheme.lib;%(AdditionalDependencies) + comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + uxtheme.lib;%(AdditionalDependencies) + comctl32.dll;gdi32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/ToolStatus/ToolStatus.vcxproj.filters b/plugins/ToolStatus/ToolStatus.vcxproj.filters index 6d32c767dcd8..ed6e324e7fcc 100644 --- a/plugins/ToolStatus/ToolStatus.vcxproj.filters +++ b/plugins/ToolStatus/ToolStatus.vcxproj.filters @@ -45,9 +45,6 @@ Source Files - - Source Files - diff --git a/plugins/ToolStatus/customizesb.c b/plugins/ToolStatus/customizesb.c index fc78e31d370b..36abceb230b9 100644 --- a/plugins/ToolStatus/customizesb.c +++ b/plugins/ToolStatus/customizesb.c @@ -1,633 +1,634 @@ -/* - * Process Hacker ToolStatus - - * Statusbar Customize Dialog - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" -#include "commonutil.h" - -BOOLEAN CustomizeStatusBarItemExists( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IdCommand - ) -{ - INT index = 0; - INT count = 0; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) - return FALSE; - - for (index = 0; index < count; index++) - { - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index))) - continue; - - if (button->IdCommand == IdCommand) - return TRUE; - } - - return FALSE; -} - -VOID CustomizeInsertStatusBarItem( - _In_ INT Index, - _In_ PBUTTON_CONTEXT Button - ) -{ - PSTATUSBAR_ITEM item; - - item = PhAllocate(sizeof(STATUSBAR_ITEM)); - memset(item, 0, sizeof(STATUSBAR_ITEM)); - - item->Id = Button->IdCommand; - - PhInsertItemList(StatusBarItemList, Index, item); - - StatusBarUpdate(TRUE); -} - -VOID CustomizeAddStatusBarItem( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IndexAvail, - _In_ INT IndexTo - ) -{ - INT count; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->AvailableListHandle)) == LB_ERR) - return; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, IndexAvail))) - return; - - if (!button->IsVirtual) - { - // remove from 'available' list - ListBox_DeleteString(Context->AvailableListHandle, IndexAvail); - - if (IndexAvail == count - 1) - { - ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail - 1); - } - else - { - ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail); - } - - // insert into 'current' list - ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); - - CustomizeInsertStatusBarItem(IndexTo, button); - } - - SendMessage(Context->DialogHandle, WM_COMMAND, MAKEWPARAM(IDC_AVAILABLE, LBN_SELCHANGE), 0); -} - -VOID CustomizeRemoveStatusBarItem( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IndexFrom - ) -{ - PBUTTON_CONTEXT button; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) - return; - - ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); - ListBox_SetCurSel(Context->CurrentListHandle, IndexFrom); - - PhRemoveItemList(StatusBarItemList, IndexFrom); - - if (!button->IsVirtual) - { - INT count = ListBox_GetCount(Context->AvailableListHandle); - - if (count == LB_ERR) - count = 1; - - // insert into 'available' list - ListBox_InsertItemData(Context->AvailableListHandle, count - 1, button); - } - - SendMessage(Context->DialogHandle, WM_COMMAND, MAKEWPARAM(IDC_CURRENT, LBN_SELCHANGE), 0); - - StatusBarUpdate(TRUE); -} - -VOID CustomizeMoveStatusBarItem( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IndexFrom, - _In_ INT IndexTo - ) -{ - INT count; - PBUTTON_CONTEXT button; - - if (IndexFrom == IndexTo) - return; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) - return; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) - return; - - ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); - ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); - ListBox_SetCurSel(Context->CurrentListHandle, IndexTo); - - if (IndexTo <= 0) - { - Button_Enable(Context->MoveUpButtonHandle, FALSE); - } - else - { - Button_Enable(Context->MoveUpButtonHandle, TRUE); - } - - // last item is always separator - if (IndexTo >= (count - 2)) - { - Button_Enable(Context->MoveDownButtonHandle, FALSE); - } - else - { - Button_Enable(Context->MoveDownButtonHandle, TRUE); - } - - PhRemoveItemList(StatusBarItemList, IndexFrom); - - CustomizeInsertStatusBarItem(IndexTo, button); -} - -VOID CustomizeFreeStatusBarItems( - _In_ PCUSTOMIZE_CONTEXT Context - ) -{ - INT index = 0; - INT count = 0; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) != LB_ERR) - { - for (index = 0; index < count; index++) - { - if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index)) - { - PhFree(button); - } - } - } - - if ((count = ListBox_GetCount(Context->AvailableListHandle)) != LB_ERR) - { - for (index = 0; index < count; index++) - { - if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, index)) - { - PhFree(button); - } - } - } -} - -VOID CustomizeLoadStatusBarItems( - _In_ PCUSTOMIZE_CONTEXT Context - ) -{ - ULONG index; - PBUTTON_CONTEXT button; - - CustomizeFreeStatusBarItems(Context); - - ListBox_ResetContent(Context->AvailableListHandle); - ListBox_ResetContent(Context->CurrentListHandle); - - for (index = 0; index < StatusBarItemList->Count; index++) - { - PSTATUSBAR_ITEM item; - - item = StatusBarItemList->Items[index]; - - button = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(button, 0, sizeof(BUTTON_CONTEXT)); - - button->IdCommand = item->Id; - - ListBox_AddItemData(Context->CurrentListHandle, button); - } - - for (index = 0; index < MAX_STATUSBAR_ITEMS; index++) - { - ULONG buttonId = StatusBarItems[index]; - - if (CustomizeStatusBarItemExists(Context, buttonId)) - continue; - - button = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(button, 0, sizeof(BUTTON_CONTEXT)); - - button->IdCommand = buttonId; - - ListBox_AddItemData(Context->AvailableListHandle, button); - } - - // Append separator to the last 'current list' position - button = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(button, 0, sizeof(BUTTON_CONTEXT)); - button->IsVirtual = TRUE; - - index = ListBox_AddItemData(Context->CurrentListHandle, button); - ListBox_SetCurSel(Context->CurrentListHandle, index); - ListBox_SetTopIndex(Context->CurrentListHandle, index); - - // Append separator to the last 'available list' position - button = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(button, 0, sizeof(BUTTON_CONTEXT)); - button->IsVirtual = TRUE; - - index = ListBox_AddItemData(Context->AvailableListHandle, button); - ListBox_SetCurSel(Context->AvailableListHandle, index); - ListBox_SetTopIndex(Context->AvailableListHandle, 0); // NOTE: This is intentional. - - // Disable buttons - Button_Enable(Context->MoveUpButtonHandle, FALSE); - Button_Enable(Context->MoveDownButtonHandle, FALSE); - Button_Enable(Context->AddButtonHandle, FALSE); - Button_Enable(Context->RemoveButtonHandle, FALSE); -} - -INT_PTR CALLBACK CustomizeStatusBarDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCUSTOMIZE_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PCUSTOMIZE_CONTEXT)PhAllocate(sizeof(CUSTOMIZE_CONTEXT)); - memset(context, 0, sizeof(CUSTOMIZE_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PCUSTOMIZE_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_NCDESTROY) - { - RemoveProp(hwndDlg, L"Context"); - PhFree(context); - } - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, PhMainWndHandle); - - context->DialogHandle = hwndDlg; - context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); - context->CurrentListHandle = GetDlgItem(hwndDlg, IDC_CURRENT); - context->MoveUpButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEUP); - context->MoveDownButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN); - context->AddButtonHandle = GetDlgItem(hwndDlg, IDC_ADD); - context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE); - context->FontHandle = CommonDuplicateFont((HFONT)SendMessage(StatusBarHandle, WM_GETFONT, 0, 0)); - - ListBox_SetItemHeight(context->AvailableListHandle, 0, PhMultiplyDivide(22, PhGlobalDpi, 96)); // BitmapHeight - ListBox_SetItemHeight(context->CurrentListHandle, 0, PhMultiplyDivide(22, PhGlobalDpi, 96)); // BitmapHeight - - CustomizeLoadStatusBarItems(context); - - SendMessage(context->DialogHandle, WM_NEXTDLGCTL, (WPARAM)context->CurrentListHandle, TRUE); - } - return TRUE; - case WM_DESTROY: - { - StatusBarSaveSettings(); - - CustomizeFreeStatusBarItems(context); - - if (context->FontHandle) - { - DeleteObject(context->FontHandle); - } - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDC_AVAILABLE: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case LBN_SELCHANGE: - { - INT count; - INT index; - - if ((count = ListBox_GetCount(context->AvailableListHandle)) == LB_ERR) - break; - - if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) - break; - - if (index == (count - 1)) - { - Button_Enable(context->AddButtonHandle, FALSE); - } - else - { - Button_Enable(context->AddButtonHandle, TRUE); - } - } - break; - case LBN_DBLCLK: - { - INT count; - INT index; - INT indexto; - - if ((count = ListBox_GetCount(context->AvailableListHandle)) == LB_ERR) - break; - - if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) - break; - - if ((indexto = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - if (index == (count - 1)) - { - // virtual separator - break; - } - - CustomizeAddStatusBarItem(context, index, indexto); - } - break; - } - } - break; - case IDC_CURRENT: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case LBN_SELCHANGE: - { - INT count; - INT index; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) - break; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - button = (PBUTTON_CONTEXT)ListBox_GetItemData(context->CurrentListHandle, index); - if (button == NULL) - break; - - if (index == 0 && count == 2) - { - // first and last item - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - } - else if (index == (count - 1)) - { - // last item (virtual separator) - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - } - else if (index == (count - 2)) - { - // second last item (last non-virtual item) - Button_Enable(context->MoveUpButtonHandle, TRUE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - } - else if (index == 0) - { - // first item - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, TRUE); - } - else - { - Button_Enable(context->MoveUpButtonHandle, TRUE); - Button_Enable(context->MoveDownButtonHandle, TRUE); - } - - Button_Enable(context->RemoveButtonHandle, !button->IsVirtual); - } - break; - case LBN_DBLCLK: - { - INT count; - INT index; - - if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) - break; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - if (index == (count - 1)) - { - // virtual separator - break; - } - - CustomizeRemoveStatusBarItem(context, index); - } - break; - } - } - break; - case IDC_ADD: - { - INT index; - INT indexto; - - if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) - break; - - if ((indexto = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeAddStatusBarItem(context, index, indexto); - } - break; - case IDC_REMOVE: - { - INT index; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeRemoveStatusBarItem(context, index); - } - break; - case IDC_MOVEUP: - { - INT index; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeMoveStatusBarItem(context, index, index - 1); - } - break; - case IDC_MOVEDOWN: - { - INT index; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeMoveStatusBarItem(context, index, index + 1); - } - break; - case IDC_RESET: - { - // Reset to default settings. - StatusBarResetSettings(); - - // Save as the new defaults. - StatusBarSaveSettings(); - - StatusBarUpdate(TRUE); - - CustomizeLoadStatusBarItems(context); - } - break; - case IDCANCEL: - { - EndDialog(hwndDlg, FALSE); - } - break; - } - } - break; - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT drawInfo = (LPDRAWITEMSTRUCT)lParam; - - if (drawInfo->CtlID == IDC_AVAILABLE || drawInfo->CtlID == IDC_CURRENT) - { - HDC bufferDc; - HBITMAP bufferBitmap; - HBITMAP oldBufferBitmap; - PBUTTON_CONTEXT button; - RECT bufferRect = - { - 0, 0, - drawInfo->rcItem.right - drawInfo->rcItem.left, - drawInfo->rcItem.bottom - drawInfo->rcItem.top - }; - BOOLEAN isSelected = (drawInfo->itemState & ODS_SELECTED) == ODS_SELECTED; - BOOLEAN isFocused = (drawInfo->itemState & ODS_FOCUS) == ODS_FOCUS; - - if (drawInfo->itemID == LB_ERR) - break; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(drawInfo->hwndItem, drawInfo->itemID))) - break; - - bufferDc = CreateCompatibleDC(drawInfo->hDC); - bufferBitmap = CreateCompatibleBitmap(drawInfo->hDC, bufferRect.right, bufferRect.bottom); - - oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); - SelectFont(bufferDc, context->FontHandle); - SetBkMode(bufferDc, TRANSPARENT); - - if (isSelected) - { - FillRect(bufferDc, &bufferRect, GetSysColorBrush(isFocused ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - SetTextColor(bufferDc, GetSysColor(isFocused ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); - //FrameRect(bufferDc, &bufferRect, isFocused ? GetStockBrush(BLACK_BRUSH) : GetSysColorBrush(COLOR_HIGHLIGHT)); - } - else - { - FillRect(bufferDc, &bufferRect, GetSysColorBrush(isFocused ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - SetTextColor(bufferDc, GetSysColor(isFocused ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); - //FrameRect(bufferDc, &bufferRect, isFocused ? GetStockBrush(BLACK_BRUSH) : GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); - } - - if (!button->IsVirtual) - { - bufferRect.left += 5; - DrawText( - bufferDc, - StatusBarGetText(button->IdCommand), - -1, - &bufferRect, - DT_LEFT | DT_VCENTER | DT_SINGLELINE - ); - } - - BitBlt( - drawInfo->hDC, - drawInfo->rcItem.left, - drawInfo->rcItem.top, - drawInfo->rcItem.right, - drawInfo->rcItem.bottom, - bufferDc, - 0, - 0, - SRCCOPY - ); - - SelectBitmap(bufferDc, oldBufferBitmap); - DeleteBitmap(bufferBitmap); - DeleteDC(bufferDc); - - return TRUE; - } - } - break; - } - - return FALSE; -} - -VOID StatusBarShowCustomizeDialog( - VOID - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_CUSTOMIZE_SB), - PhMainWndHandle, - CustomizeStatusBarDialogProc - ); -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * Statusbar Customize Dialog + * + * Copyright (C) 2015-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" +#include "commonutil.h" + +BOOLEAN CustomizeStatusBarItemExists( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IdCommand + ) +{ + INT index = 0; + INT count = 0; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) + return FALSE; + + for (index = 0; index < count; index++) + { + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index))) + continue; + + if (button->IdCommand == IdCommand) + return TRUE; + } + + return FALSE; +} + +VOID CustomizeInsertStatusBarItem( + _In_ INT Index, + _In_ PBUTTON_CONTEXT Button + ) +{ + PhInsertItemList(StatusBarItemList, Index, UlongToPtr(Button->IdCommand)); + + StatusBarUpdate(TRUE); +} + +VOID CustomizeAddStatusBarItem( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IndexAvail, + _In_ INT IndexTo + ) +{ + INT count; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->AvailableListHandle)) == LB_ERR) + return; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, IndexAvail))) + return; + + if (!button->IsVirtual) + { + // remove from 'available' list + ListBox_DeleteString(Context->AvailableListHandle, IndexAvail); + + if (IndexAvail == count - 1) + { + ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail - 1); + } + else + { + ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail); + } + + // insert into 'current' list + ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); + + CustomizeInsertStatusBarItem(IndexTo, button); + } + + SendMessage(Context->DialogHandle, WM_COMMAND, MAKEWPARAM(IDC_AVAILABLE, LBN_SELCHANGE), 0); +} + +VOID CustomizeRemoveStatusBarItem( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IndexFrom + ) +{ + PBUTTON_CONTEXT button; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) + return; + + ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); + ListBox_SetCurSel(Context->CurrentListHandle, IndexFrom); + + PhRemoveItemList(StatusBarItemList, IndexFrom); + + if (!button->IsVirtual) + { + INT count = ListBox_GetCount(Context->AvailableListHandle); + + if (count == LB_ERR) + count = 1; + + // insert into 'available' list + ListBox_InsertItemData(Context->AvailableListHandle, count - 1, button); + } + + SendMessage(Context->DialogHandle, WM_COMMAND, MAKEWPARAM(IDC_CURRENT, LBN_SELCHANGE), 0); + + StatusBarUpdate(TRUE); +} + +VOID CustomizeMoveStatusBarItem( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IndexFrom, + _In_ INT IndexTo + ) +{ + INT count; + PBUTTON_CONTEXT button; + + if (IndexFrom == IndexTo) + return; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) + return; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) + return; + + ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); + ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); + ListBox_SetCurSel(Context->CurrentListHandle, IndexTo); + + if (IndexTo <= 0) + { + Button_Enable(Context->MoveUpButtonHandle, FALSE); + } + else + { + Button_Enable(Context->MoveUpButtonHandle, TRUE); + } + + // last item is always separator + if (IndexTo >= (count - 2)) + { + Button_Enable(Context->MoveDownButtonHandle, FALSE); + } + else + { + Button_Enable(Context->MoveDownButtonHandle, TRUE); + } + + PhRemoveItemList(StatusBarItemList, IndexFrom); + + CustomizeInsertStatusBarItem(IndexTo, button); +} + +VOID CustomizeFreeStatusBarItems( + _In_ PCUSTOMIZE_CONTEXT Context + ) +{ + INT index = 0; + INT count = 0; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) != LB_ERR) + { + for (index = 0; index < count; index++) + { + if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index)) + { + PhFree(button); + } + } + } + + if ((count = ListBox_GetCount(Context->AvailableListHandle)) != LB_ERR) + { + for (index = 0; index < count; index++) + { + if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, index)) + { + PhFree(button); + } + } + } +} + +VOID CustomizeLoadStatusBarItems( + _In_ PCUSTOMIZE_CONTEXT Context + ) +{ + ULONG index; + PBUTTON_CONTEXT button; + + CustomizeFreeStatusBarItems(Context); + + ListBox_ResetContent(Context->AvailableListHandle); + ListBox_ResetContent(Context->CurrentListHandle); + + for (index = 0; index < StatusBarItemList->Count; index++) + { + button = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(button, 0, sizeof(BUTTON_CONTEXT)); + + button->IdCommand = PtrToUlong(StatusBarItemList->Items[index]); + + ListBox_AddItemData(Context->CurrentListHandle, button); + } + + for (index = 0; index < MAX_STATUSBAR_ITEMS; index++) + { + ULONG buttonId = StatusBarItems[index]; + + if (CustomizeStatusBarItemExists(Context, buttonId)) + continue; + + button = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(button, 0, sizeof(BUTTON_CONTEXT)); + + button->IdCommand = buttonId; + + ListBox_AddItemData(Context->AvailableListHandle, button); + } + + // Append separator to the last 'current list' position + button = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(button, 0, sizeof(BUTTON_CONTEXT)); + button->IsVirtual = TRUE; + + index = ListBox_AddItemData(Context->CurrentListHandle, button); + ListBox_SetCurSel(Context->CurrentListHandle, index); + ListBox_SetTopIndex(Context->CurrentListHandle, index); + + // Append separator to the last 'available list' position + button = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(button, 0, sizeof(BUTTON_CONTEXT)); + button->IsVirtual = TRUE; + + index = ListBox_AddItemData(Context->AvailableListHandle, button); + ListBox_SetCurSel(Context->AvailableListHandle, index); + ListBox_SetTopIndex(Context->AvailableListHandle, 0); // NOTE: This is intentional. + + // Disable buttons + Button_Enable(Context->MoveUpButtonHandle, FALSE); + Button_Enable(Context->MoveDownButtonHandle, FALSE); + Button_Enable(Context->AddButtonHandle, FALSE); + Button_Enable(Context->RemoveButtonHandle, FALSE); +} + +INT_PTR CALLBACK CustomizeStatusBarDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCUSTOMIZE_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PCUSTOMIZE_CONTEXT)PhAllocate(sizeof(CUSTOMIZE_CONTEXT)); + memset(context, 0, sizeof(CUSTOMIZE_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_NCDESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); + } + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, PhMainWndHandle); + + context->DialogHandle = hwndDlg; + context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); + context->CurrentListHandle = GetDlgItem(hwndDlg, IDC_CURRENT); + context->MoveUpButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEUP); + context->MoveDownButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN); + context->AddButtonHandle = GetDlgItem(hwndDlg, IDC_ADD); + context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE); + context->FontHandle = PhDuplicateFont(GetWindowFont(StatusBarHandle)); + + ListBox_SetItemHeight(context->AvailableListHandle, 0, PH_SCALE_DPI(22)); // BitmapHeight + ListBox_SetItemHeight(context->CurrentListHandle, 0, PH_SCALE_DPI(22)); // BitmapHeight + + CustomizeLoadStatusBarItems(context); + + PhSetDialogFocus(context->DialogHandle, context->CurrentListHandle); + } + break; + case WM_DESTROY: + { + StatusBarSaveSettings(); + + CustomizeFreeStatusBarItems(context); + + if (context->FontHandle) + { + DeleteFont(context->FontHandle); + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_AVAILABLE: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case LBN_SELCHANGE: + { + INT count; + INT index; + + if ((count = ListBox_GetCount(context->AvailableListHandle)) == LB_ERR) + break; + + if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) + break; + + if (index == (count - 1)) + { + Button_Enable(context->AddButtonHandle, FALSE); + } + else + { + Button_Enable(context->AddButtonHandle, TRUE); + } + } + break; + case LBN_DBLCLK: + { + INT count; + INT index; + INT indexto; + + if ((count = ListBox_GetCount(context->AvailableListHandle)) == LB_ERR) + break; + + if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) + break; + + if ((indexto = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + if (index == (count - 1)) + { + // virtual separator + break; + } + + CustomizeAddStatusBarItem(context, index, indexto); + } + break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->AddButtonHandle, FALSE); + // } + // break; + } + } + break; + case IDC_CURRENT: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case LBN_SELCHANGE: + { + INT count; + INT index; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) + break; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + button = (PBUTTON_CONTEXT)ListBox_GetItemData(context->CurrentListHandle, index); + if (button == NULL) + break; + + if (index == 0 && count == 2) + { + // first and last item + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == (count - 1)) + { + // last item (virtual separator) + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == (count - 2)) + { + // second last item (last non-virtual item) + Button_Enable(context->MoveUpButtonHandle, TRUE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == 0) + { + // first item + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, TRUE); + } + else + { + Button_Enable(context->MoveUpButtonHandle, TRUE); + Button_Enable(context->MoveDownButtonHandle, TRUE); + } + + Button_Enable(context->RemoveButtonHandle, !button->IsVirtual); + } + break; + case LBN_DBLCLK: + { + INT count; + INT index; + + if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) + break; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + if (index == (count - 1)) + { + // virtual separator + break; + } + + CustomizeRemoveStatusBarItem(context, index); + } + break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->MoveUpButtonHandle, FALSE); + // Button_Enable(context->MoveDownButtonHandle, FALSE); + // Button_Enable(context->RemoveButtonHandle, FALSE); + // } + // break; + } + } + break; + case IDC_ADD: + { + INT index; + INT indexto; + + if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) + break; + + if ((indexto = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeAddStatusBarItem(context, index, indexto); + } + break; + case IDC_REMOVE: + { + INT index; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeRemoveStatusBarItem(context, index); + } + break; + case IDC_MOVEUP: + { + INT index; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeMoveStatusBarItem(context, index, index - 1); + } + break; + case IDC_MOVEDOWN: + { + INT index; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeMoveStatusBarItem(context, index, index + 1); + } + break; + case IDC_RESET: + { + // Reset to default settings. + StatusBarResetSettings(); + + // Save as the new defaults. + StatusBarSaveSettings(); + + StatusBarUpdate(TRUE); + + CustomizeLoadStatusBarItems(context); + } + break; + case IDCANCEL: + { + EndDialog(hwndDlg, FALSE); + } + break; + } + } + break; + case WM_DRAWITEM: + { + LPDRAWITEMSTRUCT drawInfo = (LPDRAWITEMSTRUCT)lParam; + + if (drawInfo->CtlID == IDC_AVAILABLE || drawInfo->CtlID == IDC_CURRENT) + { + HDC bufferDc; + HBITMAP bufferBitmap; + HBITMAP oldBufferBitmap; + PBUTTON_CONTEXT button; + RECT bufferRect = + { + 0, 0, + drawInfo->rcItem.right - drawInfo->rcItem.left, + drawInfo->rcItem.bottom - drawInfo->rcItem.top + }; + BOOLEAN isSelected = (drawInfo->itemState & ODS_SELECTED) == ODS_SELECTED; + BOOLEAN isFocused = (drawInfo->itemState & ODS_FOCUS) == ODS_FOCUS; + + if (drawInfo->itemID == LB_ERR) + break; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(drawInfo->hwndItem, drawInfo->itemID))) + break; + + bufferDc = CreateCompatibleDC(drawInfo->hDC); + bufferBitmap = CreateCompatibleBitmap(drawInfo->hDC, bufferRect.right, bufferRect.bottom); + + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); + SelectFont(bufferDc, context->FontHandle); + SetBkMode(bufferDc, TRANSPARENT); + + if (isSelected) + { + FillRect(bufferDc, &bufferRect, GetSysColorBrush(isFocused ? COLOR_HIGHLIGHT : COLOR_WINDOW)); + SetTextColor(bufferDc, GetSysColor(isFocused ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); + //FrameRect(bufferDc, &bufferRect, isFocused ? GetStockBrush(BLACK_BRUSH) : GetSysColorBrush(COLOR_HIGHLIGHT)); + } + else + { + FillRect(bufferDc, &bufferRect, GetSysColorBrush(isFocused ? COLOR_HIGHLIGHT : COLOR_WINDOW)); + SetTextColor(bufferDc, GetSysColor(isFocused ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); + //FrameRect(bufferDc, &bufferRect, isFocused ? GetStockBrush(BLACK_BRUSH) : GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); + } + + if (!button->IsVirtual) + { + bufferRect.left += 5; + DrawText( + bufferDc, + StatusBarGetText(button->IdCommand), + -1, + &bufferRect, + DT_LEFT | DT_VCENTER | DT_SINGLELINE + ); + } + + BitBlt( + drawInfo->hDC, + drawInfo->rcItem.left, + drawInfo->rcItem.top, + drawInfo->rcItem.right, + drawInfo->rcItem.bottom, + bufferDc, + 0, + 0, + SRCCOPY + ); + + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); + DeleteDC(bufferDc); + + return TRUE; + } + } + break; + } + + return FALSE; +} + +VOID StatusBarShowCustomizeDialog( + VOID + ) +{ + DialogBox( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_CUSTOMIZE_SB), + PhMainWndHandle, + CustomizeStatusBarDialogProc + ); +} diff --git a/plugins/ToolStatus/customizetb.c b/plugins/ToolStatus/customizetb.c index 73f7be904bd5..11eb0edae83b 100644 --- a/plugins/ToolStatus/customizetb.c +++ b/plugins/ToolStatus/customizetb.c @@ -1,943 +1,961 @@ -/* - * Process Hacker ToolStatus - - * Toolbar Customize Dialog - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" -#include "commonutil.h" - -static PWSTR CustomizeTextOptionsStrings[] = -{ - L"No text labels", - L"Selective text", - L"Show text labels" -}; - -static PWSTR CustomizeSearchDisplayStrings[] = -{ - L"Always show", - L"Hide when inactive (Ctrl+K)", - // L"Auto-hide" -}; - -static PWSTR CustomizeThemeOptionsStrings[] = -{ - L"None", - L"Black", - L"Blue" -}; - -BOOLEAN CustomizeToolbarItemExists( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IdCommand - ) -{ - INT index = 0; - INT count = 0; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) - return FALSE; - - for (index = 0; index < count; index++) - { - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index))) - continue; - - if (button->IdCommand == IdCommand) - return TRUE; - } - - return FALSE; -} - -VOID CustomizeInsertToolbarButton( - _In_ INT Index, - _In_ PBUTTON_CONTEXT ItemContext - ) -{ - TBBUTTON button; - - memset(&button, 0, sizeof(TBBUTTON)); - - button.idCommand = ItemContext->IdCommand; - button.iBitmap = I_IMAGECALLBACK; - button.fsState = TBSTATE_ENABLED; - button.iString = (INT_PTR)ToolbarGetText(ItemContext->IdCommand); - - if (ItemContext->IsSeparator) - { - button.fsStyle = BTNS_SEP; - } - else - { - switch (DisplayStyle) - { - case TOOLBAR_DISPLAY_STYLE_IMAGEONLY: - button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; - break; - case TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT: - { - switch (button.idCommand) - { - case PHAPP_ID_VIEW_REFRESH: - case PHAPP_ID_HACKER_OPTIONS: - case PHAPP_ID_HACKER_FINDHANDLESORDLLS: - case PHAPP_ID_VIEW_SYSTEMINFORMATION: - button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; - break; - default: - button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; - break; - } - } - break; - case TOOLBAR_DISPLAY_STYLE_ALLTEXT: - button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; - break; - } - } - - SendMessage(ToolBarHandle, TB_INSERTBUTTON, Index, (LPARAM)&button); -} - -VOID CustomizeAddToolbarItem( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IndexAvail, - _In_ INT IndexTo - ) -{ - INT count; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->AvailableListHandle)) == LB_ERR) - return; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, IndexAvail))) - return; - - if (IndexAvail != 0) // index 0 is separator - { - // remove from 'available' list - ListBox_DeleteString(Context->AvailableListHandle, IndexAvail); - - if (IndexAvail == count - 1) - { - ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail - 1); - } - else - { - ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail); - } - } - else - { - button = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(button, 0, sizeof(BUTTON_CONTEXT)); - - button->IsSeparator = TRUE; - button->IsRemovable = TRUE; - } - - // insert into 'current' list - ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); - - CustomizeInsertToolbarButton(IndexTo, button); -} - -VOID CustomizeRemoveToolbarItem( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IndexFrom - ) -{ - PBUTTON_CONTEXT button; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) - return; - - ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); - ListBox_SetCurSel(Context->CurrentListHandle, IndexFrom); - - SendMessage(ToolBarHandle, TB_DELETEBUTTON, IndexFrom, 0); - - if (button->IsSeparator) - { - PhFree(button); - } - else - { - // insert into 'available' list - ListBox_AddItemData(Context->AvailableListHandle, button); - } - - SendMessage(Context->DialogHandle, WM_COMMAND, MAKEWPARAM(IDC_CURRENT, LBN_SELCHANGE), 0); -} - -VOID CustomizeMoveToolbarItem( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT IndexFrom, - _In_ INT IndexTo - ) -{ - INT count; - PBUTTON_CONTEXT button; - - if (IndexFrom == IndexTo) - return; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) - return; - - if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) - return; - - ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); - ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); - ListBox_SetCurSel(Context->CurrentListHandle, IndexTo); - - if (IndexTo <= 0) - { - Button_Enable(Context->MoveUpButtonHandle, FALSE); - } - else - { - Button_Enable(Context->MoveUpButtonHandle, TRUE); - } - - // last item is always separator - if (IndexTo >= (count - 2)) - { - Button_Enable(Context->MoveDownButtonHandle, FALSE); - } - else - { - Button_Enable(Context->MoveDownButtonHandle, TRUE); - } - - SendMessage(ToolBarHandle, TB_DELETEBUTTON, IndexFrom, 0); - - CustomizeInsertToolbarButton(IndexTo, button); -} - -VOID CustomizeFreeToolbarItems( - _In_ PCUSTOMIZE_CONTEXT Context - ) -{ - INT i = 0; - INT count = 0; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) != LB_ERR) - { - for (i = 0; i < count; i++) - { - if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, i)) - { - if (button->IconHandle) - { - DeleteObject(button->IconHandle); - } - - PhFree(button); - } - } - } - - if ((count = ListBox_GetCount(Context->AvailableListHandle)) != LB_ERR) - { - for (i = 0; i < count; i++) - { - if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, i)) - { - if (button->IconHandle) - { - DeleteObject(button->IconHandle); - } - - PhFree(button); - } - } - } -} - -VOID CustomizeLoadToolbarItems( - _In_ PCUSTOMIZE_CONTEXT Context - ) -{ - INT index = 0; - INT count = 0; - PBUTTON_CONTEXT context; - - CustomizeFreeToolbarItems(Context); - - ListBox_ResetContent(Context->AvailableListHandle); - ListBox_ResetContent(Context->CurrentListHandle); - - count = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); - - for (index = 0; index < count; index++) - { - TBBUTTON button; - - memset(&button, 0, sizeof(TBBUTTON)); - - if (SendMessage(ToolBarHandle, TB_GETBUTTON, index, (LPARAM)&button)) - { - context = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(context, 0, sizeof(BUTTON_CONTEXT)); - - context->IsVirtual = FALSE; - context->IsRemovable = TRUE; - context->IdCommand = button.idCommand; - - if (button.fsStyle & BTNS_SEP) - { - context->IsSeparator = TRUE; - } - else - { - context->IconHandle = CustomizeGetToolbarIcon(Context, button.idCommand); - } - - ListBox_AddItemData(Context->CurrentListHandle, context); - } - } - - for (index = 0; index < MAX_TOOLBAR_ITEMS; index++) - { - TBBUTTON button = ToolbarButtons[index]; - - if (button.idCommand == 0) - continue; - - if (CustomizeToolbarItemExists(Context, button.idCommand)) - continue; - - context = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(context, 0, sizeof(BUTTON_CONTEXT)); - - context->IsRemovable = TRUE; - context->IdCommand = button.idCommand; - context->IconHandle = CustomizeGetToolbarIcon(Context, button.idCommand); - - ListBox_AddItemData(Context->AvailableListHandle, context); - } - - // Append separator to the last 'current list' position - context = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(context, 0, sizeof(BUTTON_CONTEXT)); - context->IsSeparator = TRUE; - context->IsVirtual = TRUE; - context->IsRemovable = FALSE; - - index = ListBox_AddItemData(Context->CurrentListHandle, context); - ListBox_SetCurSel(Context->CurrentListHandle, index); - ListBox_SetTopIndex(Context->CurrentListHandle, index); - - // Insert separator into first 'available list' position - context = PhAllocate(sizeof(BUTTON_CONTEXT)); - memset(context, 0, sizeof(BUTTON_CONTEXT)); - context->IsSeparator = TRUE; - context->IsVirtual = FALSE; - context->IsRemovable = FALSE; - - index = ListBox_InsertItemData(Context->AvailableListHandle, 0, context); - ListBox_SetCurSel(Context->AvailableListHandle, index); - ListBox_SetTopIndex(Context->AvailableListHandle, index); - - // Disable buttons - Button_Enable(Context->MoveUpButtonHandle, FALSE); - Button_Enable(Context->MoveDownButtonHandle, FALSE); - Button_Enable(Context->RemoveButtonHandle, FALSE); -} - -VOID CustomizeLoadToolbarSettings( - _In_ PCUSTOMIZE_CONTEXT Context - ) -{ - HWND toolbarCombo = GetDlgItem(Context->DialogHandle, IDC_TEXTOPTIONS); - HWND searchboxCombo = GetDlgItem(Context->DialogHandle, IDC_SEARCHOPTIONS); - - PhAddComboBoxStrings( - toolbarCombo, - CustomizeTextOptionsStrings, - ARRAYSIZE(CustomizeTextOptionsStrings) - ); - PhAddComboBoxStrings( - searchboxCombo, - CustomizeSearchDisplayStrings, - ARRAYSIZE(CustomizeSearchDisplayStrings) - ); - ComboBox_SetCurSel(toolbarCombo, PhGetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE)); - ComboBox_SetCurSel(searchboxCombo, PhGetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE)); - - Button_SetCheck(GetDlgItem(Context->DialogHandle, IDC_ENABLE_MODERN), - ToolStatusConfig.ModernIcons ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(Context->DialogHandle, IDC_ENABLE_AUTOHIDE_MENU), - ToolStatusConfig.AutoHideMenu ? BST_CHECKED : BST_UNCHECKED); - - if (!ToolStatusConfig.SearchBoxEnabled) - { - ComboBox_Enable(searchboxCombo, FALSE); - } -} - -VOID CustomizeResetImages( - _In_ PCUSTOMIZE_CONTEXT Context - ) -{ - INT index = 0; - INT count = 0; - PBUTTON_CONTEXT button; - - if ((count = ListBox_GetCount(Context->CurrentListHandle)) != LB_ERR) - { - for (index = 0; index < count; index++) - { - if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index)) - { - button->IconHandle = CustomizeGetToolbarIcon(Context, button->IdCommand); - } - } - } - - if ((count = ListBox_GetCount(Context->AvailableListHandle)) != LB_ERR) - { - for (index = 0; index < count; index++) - { - if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, index)) - { - button->IconHandle = CustomizeGetToolbarIcon(Context, button->IdCommand); - } - } - } - - InvalidateRect(Context->AvailableListHandle, NULL, TRUE); - InvalidateRect(Context->CurrentListHandle, NULL, TRUE); -} - -HICON CustomizeGetToolbarIcon( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT CommandID - ) -{ - HBITMAP bitmapHandle; - HICON iconHandle; - - bitmapHandle = ToolbarGetImage(CommandID); - iconHandle = CommonBitmapToIcon(bitmapHandle, Context->CXWidth, Context->CXWidth); - - DeleteObject(bitmapHandle); - return iconHandle; -} - -VOID CustomizeResetToolbarImages( - VOID - ) -{ - // Reset the image cache with the new icons. - // TODO: Move function to Toolbar.c - for (INT index = 0; index < ARRAYSIZE(ToolbarButtons); index++) - { - if (ToolbarButtons[index].iBitmap != I_IMAGECALLBACK) - { - HBITMAP bitmap; - - if (bitmap = ToolbarGetImage(ToolbarButtons[index].idCommand)) - { - ImageList_Replace( - ToolBarImageList, - ToolbarButtons[index].iBitmap, - bitmap, - NULL - ); - - DeleteObject(bitmap); - } - } - } -} - -INT_PTR CALLBACK CustomizeToolbarDialogProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PCUSTOMIZE_CONTEXT context = NULL; - - if (uMsg == WM_INITDIALOG) - { - context = (PCUSTOMIZE_CONTEXT)PhAllocate(sizeof(CUSTOMIZE_CONTEXT)); - memset(context, 0, sizeof(CUSTOMIZE_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PCUSTOMIZE_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_NCDESTROY) - { - RemoveProp(hwndDlg, L"Context"); - PhFree(context); - } - } - - if (context == NULL) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, PhMainWndHandle); - - context->DialogHandle = hwndDlg; - context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); - context->CurrentListHandle = GetDlgItem(hwndDlg, IDC_CURRENT); - context->MoveUpButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEUP); - context->MoveDownButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN); - context->AddButtonHandle = GetDlgItem(hwndDlg, IDC_ADD); - context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE); - - context->CXWidth = 16; - context->BrushNormal = GetSysColorBrush(COLOR_WINDOW); - context->BrushHot = CreateSolidBrush(RGB(145, 201, 247)); - context->BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); - context->FontHandle = CommonDuplicateFont((HFONT)SendMessage(ToolBarHandle, WM_GETFONT, 0, 0)); - - ListBox_SetItemHeight(context->AvailableListHandle, 0, context->CXWidth + 6); // BitmapHeight - ListBox_SetItemHeight(context->CurrentListHandle, 0, context->CXWidth + 6); // BitmapHeight - - CustomizeLoadToolbarItems(context); - CustomizeLoadToolbarSettings(context); - - SendMessage(context->DialogHandle, WM_NEXTDLGCTL, (WPARAM)context->CurrentListHandle, TRUE); - } - return TRUE; - case WM_DESTROY: - { - ToolbarSaveButtonSettings(); - ToolbarLoadSettings(); - CustomizeFreeToolbarItems(context); - - if (context->BrushHot) - DeleteObject(context->BrushHot); - - if (context->BrushPushed) - DeleteObject(context->BrushPushed); - - if (context->FontHandle) - DeleteObject(context->FontHandle); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDC_AVAILABLE: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case LBN_DBLCLK: - { - INT index; - INT indexto; - - index = ListBox_GetCurSel(context->AvailableListHandle); - indexto = ListBox_GetCurSel(context->CurrentListHandle); - - if (index == LB_ERR) - break; - - if (indexto == LB_ERR) - break; - - CustomizeAddToolbarItem(context, index, indexto); - } - break; - } - } - break; - case IDC_CURRENT: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case LBN_SELCHANGE: - { - INT count; - INT index; - PBUTTON_CONTEXT itemContext; - - if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) - break; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - if (!(itemContext = (PBUTTON_CONTEXT)ListBox_GetItemData(context->CurrentListHandle, index))) - break; - - if (index == 0 && count == 2) - { - // first and last item - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - } - else if (index == (count - 1)) - { - // last item (virtual separator) - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - } - else if (index == (count - 2)) - { - // second last item (last non-virtual item) - Button_Enable(context->MoveUpButtonHandle, TRUE); - Button_Enable(context->MoveDownButtonHandle, FALSE); - } - else if (index == 0) - { - // first item - Button_Enable(context->MoveUpButtonHandle, FALSE); - Button_Enable(context->MoveDownButtonHandle, TRUE); - } - else - { - Button_Enable(context->MoveUpButtonHandle, TRUE); - Button_Enable(context->MoveDownButtonHandle, TRUE); - } - - Button_Enable(context->RemoveButtonHandle, itemContext->IsRemovable); - } - break; - case LBN_DBLCLK: - { - INT count; - INT index; - - if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) - break; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - if (index == (count - 1)) - { - // virtual separator - break; - } - - CustomizeRemoveToolbarItem(context, index); - } - break; - } - } - break; - case IDC_ADD: - { - INT index; - INT indexto; - - if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) - break; - - if ((indexto = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeAddToolbarItem(context, index, indexto); - } - break; - case IDC_REMOVE: - { - INT index; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeRemoveToolbarItem(context, index); - } - break; - case IDC_MOVEUP: - { - INT index; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeMoveToolbarItem(context, index, index - 1); - } - break; - case IDC_MOVEDOWN: - { - INT index; - - if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) - break; - - CustomizeMoveToolbarItem(context, index, index + 1); - } - break; - case IDC_RESET: - { - // Reset the Toolbar buttons to default settings. - ToolbarResetSettings(); - // Re-load the settings. - ToolbarLoadSettings(); - // Save as the new defaults. - ToolbarSaveButtonSettings(); - - CustomizeLoadToolbarItems(context); - } - break; - case IDC_TEXTOPTIONS: - { - if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) - { - PhSetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE, - (DisplayStyle = (TOOLBAR_DISPLAY_STYLE)ComboBox_GetCurSel(GET_WM_COMMAND_HWND(wParam, lParam)))); - - ToolbarLoadSettings(); - } - } - break; - case IDC_SEARCHOPTIONS: - { - if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) - { - PhSetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE, - (SearchBoxDisplayMode = (SEARCHBOX_DISPLAY_MODE)ComboBox_GetCurSel(GET_WM_COMMAND_HWND(wParam, lParam)))); - - ToolbarLoadSettings(); - } - } - break; - //case IDC_THEMEOPTIONS: - // { - // if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) - // { - // PhSetIntegerSetting(SETTING_NAME_TOOLBAR_THEME, - // (ToolBarTheme = (TOOLBAR_THEME)ComboBox_GetCurSel(GET_WM_COMMAND_HWND(wParam, lParam)))); - // - // switch (ToolBarTheme) - // { - // case TOOLBAR_THEME_NONE: - // { - // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L""); - // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L""); - // } - // break; - // case TOOLBAR_THEME_BLACK: - // { - // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L"Media"); - // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L"Media"); - // } - // break; - // case TOOLBAR_THEME_BLUE: - // { - // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L"Communications"); - // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L"Communications"); - // } - // break; - // } - // } - // } - // break; - case IDC_ENABLE_MODERN: - { - if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED) - { - ToolStatusConfig.ModernIcons = Button_GetCheck(GET_WM_COMMAND_HWND(wParam, lParam)) == BST_CHECKED; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - - CustomizeResetImages(context); - CustomizeResetToolbarImages(); - //CustomizeLoadItems(context); - } - } - break; - case IDC_ENABLE_AUTOHIDE_MENU: - { - if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED) - { - ToolStatusConfig.AutoHideMenu = !ToolStatusConfig.AutoHideMenu; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - if (ToolStatusConfig.AutoHideMenu) - { - SetMenu(PhMainWndHandle, NULL); - } - else - { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); - } - } - } - break; - case IDCANCEL: - { - EndDialog(hwndDlg, FALSE); - } - break; - } - } - break; - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT drawInfo = (LPDRAWITEMSTRUCT)lParam; - - if (drawInfo->CtlID == IDC_AVAILABLE || drawInfo->CtlID == IDC_CURRENT) - { - HDC bufferDc; - HBITMAP bufferBitmap; - HBITMAP oldBufferBitmap; - PBUTTON_CONTEXT itemContext; - RECT bufferRect = - { - 0, 0, - drawInfo->rcItem.right - drawInfo->rcItem.left, - drawInfo->rcItem.bottom - drawInfo->rcItem.top - }; - BOOLEAN isSelected = (drawInfo->itemState & ODS_SELECTED) == ODS_SELECTED; - BOOLEAN isFocused = (drawInfo->itemState & ODS_FOCUS) == ODS_FOCUS; - - if (drawInfo->itemID == LB_ERR) - break; - - if (!(itemContext = (PBUTTON_CONTEXT)ListBox_GetItemData(drawInfo->hwndItem, drawInfo->itemID))) - break; - - bufferDc = CreateCompatibleDC(drawInfo->hDC); - bufferBitmap = CreateCompatibleBitmap(drawInfo->hDC, bufferRect.right, bufferRect.bottom); - - oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); - SelectFont(bufferDc, context->FontHandle); - SetBkMode(bufferDc, TRANSPARENT); - - if (isFocused) - { - FillRect(bufferDc, &bufferRect, context->BrushHot); // leak - //FrameRect(bufferDc, &bufferRect, GetStockBrush(BLACK_BRUSH)); - - if (!itemContext->IsVirtual) - { - SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); - } - else - { - SetTextColor(bufferDc, GetSysColor(COLOR_GRAYTEXT)); - } - } - else - { - FillRect(bufferDc, &bufferRect, context->BrushNormal); - //FrameRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); - - if (!itemContext->IsVirtual) - { - SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); - } - else - { - SetTextColor(bufferDc, GetSysColor(COLOR_GRAYTEXT)); - } - } - - if (itemContext->IconHandle && !itemContext->IsSeparator) - { - DrawIconEx( - bufferDc, - bufferRect.left + 2, - bufferRect.top + ((bufferRect.bottom - bufferRect.top) - context->CXWidth) / 2, - itemContext->IconHandle, - context->CXWidth, - context->CXWidth, - 0, - NULL, - DI_NORMAL - ); - } - - bufferRect.left += context->CXWidth + 4; - - if (itemContext->IdCommand != 0) - { - DrawText( - bufferDc, - ToolbarGetText(itemContext->IdCommand), - -1, - &bufferRect, - DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP - ); - } - else - { - DrawText( - bufferDc, - L"Separator", - -1, - &bufferRect, - DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP - ); - } - - BitBlt( - drawInfo->hDC, - drawInfo->rcItem.left, - drawInfo->rcItem.top, - drawInfo->rcItem.right, - drawInfo->rcItem.bottom, - bufferDc, - 0, - 0, - SRCCOPY - ); - - SelectBitmap(bufferDc, oldBufferBitmap); - DeleteBitmap(bufferBitmap); - DeleteDC(bufferDc); - - return TRUE; - } - } - break; - } - - return FALSE; -} - -VOID ToolBarShowCustomizeDialog( - VOID - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_CUSTOMIZE_TB), - PhMainWndHandle, - CustomizeToolbarDialogProc - ); -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * Toolbar Customize Dialog + * + * Copyright (C) 2015-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" +#include "commonutil.h" + +static PWSTR CustomizeTextOptionsStrings[] = +{ + L"No text labels", + L"Selective text", + L"Show text labels" +}; + +static PWSTR CustomizeSearchDisplayStrings[] = +{ + L"Always show", + L"Hide when inactive (Ctrl+K)", + // L"Auto-hide" +}; + +static PWSTR CustomizeThemeOptionsStrings[] = +{ + L"None", + L"Black", + L"Blue" +}; + +BOOLEAN CustomizeToolbarItemExists( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IdCommand + ) +{ + INT index = 0; + INT count = 0; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) + return FALSE; + + for (index = 0; index < count; index++) + { + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index))) + continue; + + if (button->IdCommand == IdCommand) + return TRUE; + } + + return FALSE; +} + +VOID CustomizeInsertToolbarButton( + _In_ INT Index, + _In_ PBUTTON_CONTEXT ItemContext + ) +{ + TBBUTTON button; + + memset(&button, 0, sizeof(TBBUTTON)); + + button.idCommand = ItemContext->IdCommand; + button.iBitmap = I_IMAGECALLBACK; + button.fsState = TBSTATE_ENABLED; + button.iString = (INT_PTR)ToolbarGetText(ItemContext->IdCommand); + + if (ItemContext->IsSeparator) + { + button.fsStyle = BTNS_SEP; + } + else + { + switch (DisplayStyle) + { + case TOOLBAR_DISPLAY_STYLE_IMAGEONLY: + button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; + break; + case TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT: + { + switch (button.idCommand) + { + case PHAPP_ID_VIEW_REFRESH: + case PHAPP_ID_HACKER_OPTIONS: + case PHAPP_ID_HACKER_FINDHANDLESORDLLS: + case PHAPP_ID_VIEW_SYSTEMINFORMATION: + button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; + break; + default: + button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; + break; + } + } + break; + case TOOLBAR_DISPLAY_STYLE_ALLTEXT: + button.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; + break; + } + } + + SendMessage(ToolBarHandle, TB_INSERTBUTTON, Index, (LPARAM)&button); +} + +VOID CustomizeAddToolbarItem( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IndexAvail, + _In_ INT IndexTo + ) +{ + INT count; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->AvailableListHandle)) == LB_ERR) + return; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, IndexAvail))) + return; + + if (IndexAvail != 0) // index 0 is separator + { + // remove from 'available' list + ListBox_DeleteString(Context->AvailableListHandle, IndexAvail); + + if (IndexAvail == count - 1) + { + ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail - 1); + } + else + { + ListBox_SetCurSel(Context->AvailableListHandle, IndexAvail); + } + } + else + { + button = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(button, 0, sizeof(BUTTON_CONTEXT)); + + button->IsSeparator = TRUE; + button->IsRemovable = TRUE; + } + + // insert into 'current' list + ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); + + CustomizeInsertToolbarButton(IndexTo, button); +} + +VOID CustomizeRemoveToolbarItem( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IndexFrom + ) +{ + PBUTTON_CONTEXT button; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) + return; + + ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); + ListBox_SetCurSel(Context->CurrentListHandle, IndexFrom); + + SendMessage(ToolBarHandle, TB_DELETEBUTTON, IndexFrom, 0); + + if (button->IsSeparator) + { + PhFree(button); + } + else + { + // insert into 'available' list + ListBox_AddItemData(Context->AvailableListHandle, button); + } + + SendMessage(Context->DialogHandle, WM_COMMAND, MAKEWPARAM(IDC_CURRENT, LBN_SELCHANGE), 0); +} + +VOID CustomizeMoveToolbarItem( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT IndexFrom, + _In_ INT IndexTo + ) +{ + INT count; + PBUTTON_CONTEXT button; + + if (IndexFrom == IndexTo) + return; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) == LB_ERR) + return; + + if (!(button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, IndexFrom))) + return; + + ListBox_DeleteString(Context->CurrentListHandle, IndexFrom); + ListBox_InsertItemData(Context->CurrentListHandle, IndexTo, button); + ListBox_SetCurSel(Context->CurrentListHandle, IndexTo); + + if (IndexTo <= 0) + { + Button_Enable(Context->MoveUpButtonHandle, FALSE); + } + else + { + Button_Enable(Context->MoveUpButtonHandle, TRUE); + } + + // last item is always separator + if (IndexTo >= (count - 2)) + { + Button_Enable(Context->MoveDownButtonHandle, FALSE); + } + else + { + Button_Enable(Context->MoveDownButtonHandle, TRUE); + } + + SendMessage(ToolBarHandle, TB_DELETEBUTTON, IndexFrom, 0); + + CustomizeInsertToolbarButton(IndexTo, button); +} + +VOID CustomizeFreeToolbarItems( + _In_ PCUSTOMIZE_CONTEXT Context + ) +{ + INT i = 0; + INT count = 0; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) != LB_ERR) + { + for (i = 0; i < count; i++) + { + if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, i)) + { + if (button->IconHandle) + { + DestroyIcon(button->IconHandle); + } + + PhFree(button); + } + } + } + + if ((count = ListBox_GetCount(Context->AvailableListHandle)) != LB_ERR) + { + for (i = 0; i < count; i++) + { + if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, i)) + { + if (button->IconHandle) + { + DestroyIcon(button->IconHandle); + } + + PhFree(button); + } + } + } +} + +VOID CustomizeLoadToolbarItems( + _In_ PCUSTOMIZE_CONTEXT Context + ) +{ + INT index = 0; + INT count = 0; + PBUTTON_CONTEXT context; + + CustomizeFreeToolbarItems(Context); + + ListBox_ResetContent(Context->AvailableListHandle); + ListBox_ResetContent(Context->CurrentListHandle); + + count = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); + + for (index = 0; index < count; index++) + { + TBBUTTON button; + + memset(&button, 0, sizeof(TBBUTTON)); + + if (SendMessage(ToolBarHandle, TB_GETBUTTON, index, (LPARAM)&button)) + { + context = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(context, 0, sizeof(BUTTON_CONTEXT)); + + context->IsVirtual = FALSE; + context->IsRemovable = TRUE; + context->IdCommand = button.idCommand; + + if (button.fsStyle & BTNS_SEP) + { + context->IsSeparator = TRUE; + } + else + { + context->IconHandle = CustomizeGetToolbarIcon(Context, button.idCommand); + } + + ListBox_AddItemData(Context->CurrentListHandle, context); + } + } + + for (index = 0; index < MAX_TOOLBAR_ITEMS; index++) + { + TBBUTTON button = ToolbarButtons[index]; + + if (button.idCommand == 0) + continue; + + if (CustomizeToolbarItemExists(Context, button.idCommand)) + continue; + + context = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(context, 0, sizeof(BUTTON_CONTEXT)); + + context->IsRemovable = TRUE; + context->IdCommand = button.idCommand; + context->IconHandle = CustomizeGetToolbarIcon(Context, button.idCommand); + + ListBox_AddItemData(Context->AvailableListHandle, context); + } + + // Append separator to the last 'current list' position + context = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(context, 0, sizeof(BUTTON_CONTEXT)); + context->IsSeparator = TRUE; + context->IsVirtual = TRUE; + context->IsRemovable = FALSE; + + index = ListBox_AddItemData(Context->CurrentListHandle, context); + ListBox_SetCurSel(Context->CurrentListHandle, index); + ListBox_SetTopIndex(Context->CurrentListHandle, index); + + // Insert separator into first 'available list' position + context = PhAllocate(sizeof(BUTTON_CONTEXT)); + memset(context, 0, sizeof(BUTTON_CONTEXT)); + context->IsSeparator = TRUE; + context->IsVirtual = FALSE; + context->IsRemovable = FALSE; + + index = ListBox_InsertItemData(Context->AvailableListHandle, 0, context); + ListBox_SetCurSel(Context->AvailableListHandle, index); + ListBox_SetTopIndex(Context->AvailableListHandle, index); + + // Disable buttons + Button_Enable(Context->MoveUpButtonHandle, FALSE); + Button_Enable(Context->MoveDownButtonHandle, FALSE); + Button_Enable(Context->AddButtonHandle, FALSE); + Button_Enable(Context->RemoveButtonHandle, FALSE); +} + +VOID CustomizeLoadToolbarSettings( + _In_ PCUSTOMIZE_CONTEXT Context + ) +{ + HWND toolbarCombo = GetDlgItem(Context->DialogHandle, IDC_TEXTOPTIONS); + HWND searchboxCombo = GetDlgItem(Context->DialogHandle, IDC_SEARCHOPTIONS); + + PhAddComboBoxStrings( + toolbarCombo, + CustomizeTextOptionsStrings, + ARRAYSIZE(CustomizeTextOptionsStrings) + ); + PhAddComboBoxStrings( + searchboxCombo, + CustomizeSearchDisplayStrings, + ARRAYSIZE(CustomizeSearchDisplayStrings) + ); + ComboBox_SetCurSel(toolbarCombo, PhGetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE)); + ComboBox_SetCurSel(searchboxCombo, PhGetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE)); + + Button_SetCheck(GetDlgItem(Context->DialogHandle, IDC_ENABLE_MODERN), + ToolStatusConfig.ModernIcons ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(Context->DialogHandle, IDC_ENABLE_AUTOHIDE_MENU), + ToolStatusConfig.AutoHideMenu ? BST_CHECKED : BST_UNCHECKED); + + if (!ToolStatusConfig.SearchBoxEnabled) + { + ComboBox_Enable(searchboxCombo, FALSE); + } +} + +VOID CustomizeResetImages( + _In_ PCUSTOMIZE_CONTEXT Context + ) +{ + INT index = 0; + INT count = 0; + PBUTTON_CONTEXT button; + + if ((count = ListBox_GetCount(Context->CurrentListHandle)) != LB_ERR) + { + for (index = 0; index < count; index++) + { + if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->CurrentListHandle, index)) + { + button->IconHandle = CustomizeGetToolbarIcon(Context, button->IdCommand); + } + } + } + + if ((count = ListBox_GetCount(Context->AvailableListHandle)) != LB_ERR) + { + for (index = 0; index < count; index++) + { + if (button = (PBUTTON_CONTEXT)ListBox_GetItemData(Context->AvailableListHandle, index)) + { + button->IconHandle = CustomizeGetToolbarIcon(Context, button->IdCommand); + } + } + } + + InvalidateRect(Context->AvailableListHandle, NULL, TRUE); + InvalidateRect(Context->CurrentListHandle, NULL, TRUE); +} + +HICON CustomizeGetToolbarIcon( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT CommandID + ) +{ + HBITMAP bitmapHandle; + HICON iconHandle; + + bitmapHandle = ToolbarGetImage(CommandID); + iconHandle = CommonBitmapToIcon(bitmapHandle, Context->CXWidth, Context->CXWidth); + + DeleteBitmap(bitmapHandle); + return iconHandle; +} + +VOID CustomizeResetToolbarImages( + VOID + ) +{ + // Reset the image cache with the new icons. + // TODO: Move function to Toolbar.c + for (INT index = 0; index < ARRAYSIZE(ToolbarButtons); index++) + { + if (ToolbarButtons[index].iBitmap != I_IMAGECALLBACK) + { + HBITMAP bitmap; + + if (bitmap = ToolbarGetImage(ToolbarButtons[index].idCommand)) + { + ImageList_Replace( + ToolBarImageList, + ToolbarButtons[index].iBitmap, + bitmap, + NULL + ); + + DeleteBitmap(bitmap); + } + } + } +} + +INT_PTR CALLBACK CustomizeToolbarDialogProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PCUSTOMIZE_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = (PCUSTOMIZE_CONTEXT)PhAllocate(sizeof(CUSTOMIZE_CONTEXT)); + memset(context, 0, sizeof(CUSTOMIZE_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_NCDESTROY) + { + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + PhFree(context); + } + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, PhMainWndHandle); + + context->DialogHandle = hwndDlg; + context->AvailableListHandle = GetDlgItem(hwndDlg, IDC_AVAILABLE); + context->CurrentListHandle = GetDlgItem(hwndDlg, IDC_CURRENT); + context->MoveUpButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEUP); + context->MoveDownButtonHandle = GetDlgItem(hwndDlg, IDC_MOVEDOWN); + context->AddButtonHandle = GetDlgItem(hwndDlg, IDC_ADD); + context->RemoveButtonHandle = GetDlgItem(hwndDlg, IDC_REMOVE); + + context->CXWidth = PH_SCALE_DPI(16); + context->BrushNormal = GetSysColorBrush(COLOR_WINDOW); + context->BrushHot = CreateSolidBrush(RGB(145, 201, 247)); + context->BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); + context->FontHandle = PhDuplicateFont(GetWindowFont(ToolBarHandle)); + + ListBox_SetItemHeight(context->AvailableListHandle, 0, context->CXWidth + 6); // BitmapHeight + ListBox_SetItemHeight(context->CurrentListHandle, 0, context->CXWidth + 6); // BitmapHeight + + CustomizeLoadToolbarItems(context); + CustomizeLoadToolbarSettings(context); + + PhSetDialogFocus(context->DialogHandle, context->CurrentListHandle); + } + break; + case WM_DESTROY: + { + ToolbarSaveButtonSettings(); + ToolbarLoadSettings(); + CustomizeFreeToolbarItems(context); + + if (context->BrushHot) + DeleteBrush(context->BrushHot); + + if (context->BrushPushed) + DeleteBrush(context->BrushPushed); + + if (context->FontHandle) + DeleteBrush(context->FontHandle); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_AVAILABLE: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case LBN_SELCHANGE: + { + Button_Enable(context->AddButtonHandle, TRUE); + } + break; + case LBN_DBLCLK: + { + INT index; + INT indexto; + + index = ListBox_GetCurSel(context->AvailableListHandle); + indexto = ListBox_GetCurSel(context->CurrentListHandle); + + if (index == LB_ERR) + break; + + if (indexto == LB_ERR) + break; + + CustomizeAddToolbarItem(context, index, indexto); + } + break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->AddButtonHandle, FALSE); + // } + // break; + } + } + break; + case IDC_CURRENT: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case LBN_SELCHANGE: + { + INT count; + INT index; + PBUTTON_CONTEXT itemContext; + + if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) + break; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + if (!(itemContext = (PBUTTON_CONTEXT)ListBox_GetItemData(context->CurrentListHandle, index))) + break; + + if (index == 0 && count == 2) + { + // first and last item + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == (count - 1)) + { + // last item (virtual separator) + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == (count - 2)) + { + // second last item (last non-virtual item) + Button_Enable(context->MoveUpButtonHandle, TRUE); + Button_Enable(context->MoveDownButtonHandle, FALSE); + } + else if (index == 0) + { + // first item + Button_Enable(context->MoveUpButtonHandle, FALSE); + Button_Enable(context->MoveDownButtonHandle, TRUE); + } + else + { + Button_Enable(context->MoveUpButtonHandle, TRUE); + Button_Enable(context->MoveDownButtonHandle, TRUE); + } + + Button_Enable(context->RemoveButtonHandle, itemContext->IsRemovable); + } + break; + case LBN_DBLCLK: + { + INT count; + INT index; + + if ((count = ListBox_GetCount(context->CurrentListHandle)) == LB_ERR) + break; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + if (index == (count - 1)) + { + // virtual separator + break; + } + + CustomizeRemoveToolbarItem(context, index); + } + break; + //case LBN_KILLFOCUS: + // { + // Button_Enable(context->MoveUpButtonHandle, FALSE); + // Button_Enable(context->MoveDownButtonHandle, FALSE); + // Button_Enable(context->RemoveButtonHandle, FALSE); + // } + // break; + } + } + break; + case IDC_ADD: + { + INT index; + INT indexto; + + if ((index = ListBox_GetCurSel(context->AvailableListHandle)) == LB_ERR) + break; + + if ((indexto = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeAddToolbarItem(context, index, indexto); + } + break; + case IDC_REMOVE: + { + INT index; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeRemoveToolbarItem(context, index); + } + break; + case IDC_MOVEUP: + { + INT index; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeMoveToolbarItem(context, index, index - 1); + } + break; + case IDC_MOVEDOWN: + { + INT index; + + if ((index = ListBox_GetCurSel(context->CurrentListHandle)) == LB_ERR) + break; + + CustomizeMoveToolbarItem(context, index, index + 1); + } + break; + case IDC_RESET: + { + // Reset the Toolbar buttons to default settings. + ToolbarResetSettings(); + // Re-load the settings. + ToolbarLoadSettings(); + // Save as the new defaults. + ToolbarSaveButtonSettings(); + + CustomizeLoadToolbarItems(context); + } + break; + case IDC_TEXTOPTIONS: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + { + PhSetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE, + (DisplayStyle = (TOOLBAR_DISPLAY_STYLE)ComboBox_GetCurSel(GET_WM_COMMAND_HWND(wParam, lParam)))); + + ToolbarLoadSettings(); + } + } + break; + case IDC_SEARCHOPTIONS: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + { + PhSetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE, + (SearchBoxDisplayMode = (SEARCHBOX_DISPLAY_MODE)ComboBox_GetCurSel(GET_WM_COMMAND_HWND(wParam, lParam)))); + + ToolbarLoadSettings(); + } + } + break; + //case IDC_THEMEOPTIONS: + // { + // if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) + // { + // PhSetIntegerSetting(SETTING_NAME_TOOLBAR_THEME, + // (ToolBarTheme = (TOOLBAR_THEME)ComboBox_GetCurSel(GET_WM_COMMAND_HWND(wParam, lParam)))); + // + // switch (ToolBarTheme) + // { + // case TOOLBAR_THEME_NONE: + // { + // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L""); + // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L""); + // } + // break; + // case TOOLBAR_THEME_BLACK: + // { + // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L"Media"); + // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L"Media"); + // } + // break; + // case TOOLBAR_THEME_BLUE: + // { + // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L"Communications"); + // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L"Communications"); + // } + // break; + // } + // } + // } + // break; + case IDC_ENABLE_MODERN: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED) + { + ToolStatusConfig.ModernIcons = Button_GetCheck(GET_WM_COMMAND_HWND(wParam, lParam)) == BST_CHECKED; + + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); + + ToolbarLoadSettings(); + + CustomizeResetImages(context); + CustomizeResetToolbarImages(); + //CustomizeLoadItems(context); + } + } + break; + case IDC_ENABLE_AUTOHIDE_MENU: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED) + { + ToolStatusConfig.AutoHideMenu = !ToolStatusConfig.AutoHideMenu; + + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); + + if (ToolStatusConfig.AutoHideMenu) + { + SetMenu(PhMainWndHandle, NULL); + } + else + { + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); + } + } + } + break; + case IDCANCEL: + { + EndDialog(hwndDlg, FALSE); + } + break; + } + } + break; + case WM_DRAWITEM: + { + LPDRAWITEMSTRUCT drawInfo = (LPDRAWITEMSTRUCT)lParam; + + if (drawInfo->CtlID == IDC_AVAILABLE || drawInfo->CtlID == IDC_CURRENT) + { + HDC bufferDc; + HBITMAP bufferBitmap; + HBITMAP oldBufferBitmap; + PBUTTON_CONTEXT itemContext; + RECT bufferRect = + { + 0, 0, + drawInfo->rcItem.right - drawInfo->rcItem.left, + drawInfo->rcItem.bottom - drawInfo->rcItem.top + }; + BOOLEAN isSelected = (drawInfo->itemState & ODS_SELECTED) == ODS_SELECTED; + BOOLEAN isFocused = (drawInfo->itemState & ODS_FOCUS) == ODS_FOCUS; + + if (drawInfo->itemID == LB_ERR) + break; + + if (!(itemContext = (PBUTTON_CONTEXT)ListBox_GetItemData(drawInfo->hwndItem, drawInfo->itemID))) + break; + + bufferDc = CreateCompatibleDC(drawInfo->hDC); + bufferBitmap = CreateCompatibleBitmap(drawInfo->hDC, bufferRect.right, bufferRect.bottom); + + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); + SelectFont(bufferDc, context->FontHandle); + SetBkMode(bufferDc, TRANSPARENT); + + if (isFocused) + { + FillRect(bufferDc, &bufferRect, context->BrushHot); + //FrameRect(bufferDc, &bufferRect, GetStockBrush(BLACK_BRUSH)); + + if (!itemContext->IsVirtual) + { + SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); + } + else + { + SetTextColor(bufferDc, GetSysColor(COLOR_GRAYTEXT)); + } + } + else + { + FillRect(bufferDc, &bufferRect, context->BrushNormal); + //FrameRect(bufferDc, &bufferRect, GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); + + if (!itemContext->IsVirtual) + { + SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT)); + } + else + { + SetTextColor(bufferDc, GetSysColor(COLOR_GRAYTEXT)); + } + } + + if (itemContext->IconHandle && !itemContext->IsSeparator) + { + DrawIconEx( + bufferDc, + bufferRect.left + 2, + bufferRect.top + ((bufferRect.bottom - bufferRect.top) - context->CXWidth) / 2, + itemContext->IconHandle, + context->CXWidth, + context->CXWidth, + 0, + NULL, + DI_NORMAL + ); + } + + bufferRect.left += context->CXWidth + 4; + + if (itemContext->IdCommand != 0) + { + DrawText( + bufferDc, + ToolbarGetText(itemContext->IdCommand), + -1, + &bufferRect, + DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP + ); + } + else + { + DrawText( + bufferDc, + L"Separator", + -1, + &bufferRect, + DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP + ); + } + + BitBlt( + drawInfo->hDC, + drawInfo->rcItem.left, + drawInfo->rcItem.top, + drawInfo->rcItem.right, + drawInfo->rcItem.bottom, + bufferDc, + 0, + 0, + SRCCOPY + ); + + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); + DeleteDC(bufferDc); + + return TRUE; + } + } + break; + } + + return FALSE; +} + +VOID ToolBarShowCustomizeDialog( + VOID + ) +{ + DialogBox( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_CUSTOMIZE_TB), + PhMainWndHandle, + CustomizeToolbarDialogProc + ); +} diff --git a/plugins/ToolStatus/filter.c b/plugins/ToolStatus/filter.c index 70dc09f3febb..f875ced4733f 100644 --- a/plugins/ToolStatus/filter.c +++ b/plugins/ToolStatus/filter.c @@ -1,670 +1,760 @@ -/* - * Process Hacker ToolStatus - - * search filter callbacks - * - * Copyright (C) 2011-2016 dmex - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" -#include - -BOOLEAN WordMatchStringRef( - _In_ PPH_STRINGREF Text - ) -{ - PH_STRINGREF part; - PH_STRINGREF remainingPart; - - remainingPart = SearchboxText->sr; - - while (remainingPart.Length) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length) - { - if (PhFindStringInStringRef(Text, &part, TRUE) != -1) - return TRUE; - } - } - - return FALSE; -} - -BOOLEAN WordMatchStringZ( - _In_ PWSTR Text - ) -{ - PH_STRINGREF text; - - PhInitializeStringRef(&text, Text); - return WordMatchStringRef(&text); -} - -BOOLEAN ProcessTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ) -{ - PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)Node; - - if (PhIsNullOrEmptyString(SearchboxText)) - return TRUE; - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->ProcessName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->ProcessName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->FileName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->FileName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->CommandLine)) - { - if (WordMatchStringRef(&processNode->ProcessItem->CommandLine->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.CompanyName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.CompanyName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.FileDescription)) - { - if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.FileDescription->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.FileVersion)) - { - if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.FileVersion->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.ProductName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.ProductName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->UserName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->UserName->sr)) - return TRUE; - } - - if (processNode->ProcessItem->IntegrityString) - { - if (WordMatchStringZ(processNode->ProcessItem->IntegrityString)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->JobName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->JobName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->VerifySignerName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->VerifySignerName->sr)) - return TRUE; - } - - if (processNode->ProcessItem->ProcessIdString[0]) - { - if (WordMatchStringZ(processNode->ProcessItem->ProcessIdString)) - return TRUE; - } - - if (processNode->ProcessItem->ParentProcessIdString[0]) - { - if (WordMatchStringZ(processNode->ProcessItem->ParentProcessIdString)) - return TRUE; - } - - if (processNode->ProcessItem->SessionIdString[0]) - { - if (WordMatchStringZ(processNode->ProcessItem->SessionIdString)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(processNode->ProcessItem->PackageFullName)) - { - if (WordMatchStringRef(&processNode->ProcessItem->PackageFullName->sr)) - return TRUE; - } - - if (WordMatchStringZ(PhGetProcessPriorityClassString(processNode->ProcessItem->PriorityClass))) - { - return TRUE; - } - - if (processNode->ProcessItem->VerifyResult != VrUnknown) - { - switch (processNode->ProcessItem->VerifyResult) - { - case VrNoSignature: - if (WordMatchStringZ(L"NoSignature")) - return TRUE; - break; - case VrTrusted: - if (WordMatchStringZ(L"Trusted")) - return TRUE; - break; - case VrExpired: - if (WordMatchStringZ(L"Expired")) - return TRUE; - break; - case VrRevoked: - if (WordMatchStringZ(L"Revoked")) - return TRUE; - break; - case VrDistrust: - if (WordMatchStringZ(L"Distrust")) - return TRUE; - break; - case VrSecuritySettings: - if (WordMatchStringZ(L"SecuritySettings")) - return TRUE; - break; - case VrBadSignature: - if (WordMatchStringZ(L"BadSignature")) - return TRUE; - break; - default: - if (WordMatchStringZ(L"Unknown")) - return TRUE; - break; - } - } - - if (WINDOWS_HAS_UAC && processNode->ProcessItem->ElevationType != TokenElevationTypeDefault) - { - switch (processNode->ProcessItem->ElevationType) - { - case TokenElevationTypeLimited: - if (WordMatchStringZ(L"Limited")) - return TRUE; - break; - case TokenElevationTypeFull: - if (WordMatchStringZ(L"Full")) - return TRUE; - break; - default: - if (WordMatchStringZ(L"Unknown")) - return TRUE; - break; - } - } - - if (WordMatchStringZ(L"IsBeingDebugged") && processNode->ProcessItem->IsBeingDebugged) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsDotNet") && processNode->ProcessItem->IsDotNet) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsElevated") && processNode->ProcessItem->IsElevated) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsInJob") && processNode->ProcessItem->IsInJob) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsInSignificantJob") && processNode->ProcessItem->IsInSignificantJob) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsPacked") && processNode->ProcessItem->IsPacked) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsSuspended") && processNode->ProcessItem->IsSuspended) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsWow64") && processNode->ProcessItem->IsWow64) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsImmersive") && processNode->ProcessItem->IsImmersive) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsProtectedProcess") && processNode->ProcessItem->IsProtectedProcess) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsSecureProcess") && processNode->ProcessItem->IsSecureProcess) - { - return TRUE; - } - - if (WordMatchStringZ(L"IsPicoProcess") && processNode->ProcessItem->IsSubsystemProcess) - { - return TRUE; - } - - if (processNode->ProcessItem->ServiceList && processNode->ProcessItem->ServiceList->Count) - { - ULONG enumerationKey = 0; - PPH_SERVICE_ITEM serviceItem; - PPH_LIST serviceList; - ULONG i; - BOOLEAN matched = FALSE; - - // Copy the service list so we can search it. - serviceList = PhCreateList(processNode->ProcessItem->ServiceList->Count); - - PhAcquireQueuedLockShared(&processNode->ProcessItem->ServiceListLock); - - while (PhEnumPointerList( - processNode->ProcessItem->ServiceList, - &enumerationKey, - &serviceItem - )) - { - PhReferenceObject(serviceItem); - PhAddItemList(serviceList, serviceItem); - } - - PhReleaseQueuedLockShared(&processNode->ProcessItem->ServiceListLock); - - for (i = 0; i < serviceList->Count; i++) - { - PPH_STRING serviceFileName = NULL; - PPH_STRING serviceBinaryPath = NULL; - - serviceItem = serviceList->Items[i]; - - if (!PhIsNullOrEmptyString(serviceItem->Name)) - { - if (WordMatchStringRef(&serviceItem->Name->sr)) - { - matched = TRUE; - break; - } - } - - if (!PhIsNullOrEmptyString(serviceItem->DisplayName)) - { - if (WordMatchStringRef(&serviceItem->DisplayName->sr)) - { - matched = TRUE; - break; - } - } - - if (serviceItem->ProcessId) - { - WCHAR processIdString[PH_INT32_STR_LEN_1]; - - PhPrintUInt32(processIdString, HandleToUlong(serviceItem->ProcessId)); - - if (WordMatchStringZ(processIdString)) - { - matched = TRUE; - break; - } - } - - if (NT_SUCCESS(QueryServiceFileName( - &serviceItem->Name->sr, - &serviceFileName, - &serviceBinaryPath - ))) - { - if (serviceFileName) - { - if (WordMatchStringRef(&serviceFileName->sr)) - { - matched = TRUE; - } - - PhDereferenceObject(serviceFileName); - } - - if (serviceBinaryPath) - { - if (WordMatchStringRef(&serviceBinaryPath->sr)) - { - matched = TRUE; - } - - PhDereferenceObject(serviceBinaryPath); - } - - if (matched) - break; - } - } - - PhDereferenceObjects(serviceList->Items, serviceList->Count); - PhDereferenceObject(serviceList); - - if (matched) - return TRUE; - } - - return FALSE; -} - -BOOLEAN ServiceTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ) -{ - PPH_SERVICE_NODE serviceNode = (PPH_SERVICE_NODE)Node; - PPH_STRING serviceFileName = NULL; - PPH_STRING serviceBinaryPath = NULL; - - if (PhIsNullOrEmptyString(SearchboxText)) - return TRUE; - - if (WordMatchStringZ(PhGetServiceTypeString(serviceNode->ServiceItem->Type))) - return TRUE; - - if (WordMatchStringZ(PhGetServiceStateString(serviceNode->ServiceItem->State))) - return TRUE; - - if (WordMatchStringZ(PhGetServiceStartTypeString(serviceNode->ServiceItem->StartType))) - return TRUE; - - if (WordMatchStringZ(PhGetServiceErrorControlString(serviceNode->ServiceItem->ErrorControl))) - return TRUE; - - if (!PhIsNullOrEmptyString(serviceNode->ServiceItem->Name)) - { - if (WordMatchStringRef(&serviceNode->ServiceItem->Name->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(serviceNode->ServiceItem->DisplayName)) - { - if (WordMatchStringRef(&serviceNode->ServiceItem->DisplayName->sr)) - return TRUE; - } - - if (serviceNode->ServiceItem->ProcessId) - { - PPH_PROCESS_NODE processNode; - WCHAR processIdString[PH_INT32_STR_LEN_1]; - - PhPrintUInt32(processIdString, HandleToUlong(serviceNode->ServiceItem->ProcessId)); - - if (WordMatchStringZ(processIdString)) - return TRUE; - - // Search the process node - if (processNode = PhFindProcessNode(serviceNode->ServiceItem->ProcessId)) - { - if (ProcessTreeFilterCallback(&processNode->Node, NULL)) - return TRUE; - } - } - - if (NT_SUCCESS(QueryServiceFileName( - &serviceNode->ServiceItem->Name->sr, - &serviceFileName, - &serviceBinaryPath - ))) - { - BOOLEAN matched = FALSE; - - if (serviceFileName) - { - if (WordMatchStringRef(&serviceFileName->sr)) - { - matched = TRUE; - } - - PhDereferenceObject(serviceFileName); - } - - if (serviceBinaryPath) - { - if (WordMatchStringRef(&serviceBinaryPath->sr)) - { - matched = TRUE; - } - - PhDereferenceObject(serviceBinaryPath); - } - - if (matched) - return TRUE; - } - - return FALSE; -} - -BOOLEAN NetworkTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ) -{ - PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)Node; - - if (PhIsNullOrEmptyString(SearchboxText)) - return TRUE; - - if (!PhIsNullOrEmptyString(networkNode->NetworkItem->ProcessName)) - { - if (WordMatchStringRef(&networkNode->NetworkItem->ProcessName->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(networkNode->NetworkItem->OwnerName)) - { - if (WordMatchStringRef(&networkNode->NetworkItem->OwnerName->sr)) - return TRUE; - } - - if (networkNode->NetworkItem->LocalAddressString[0]) - { - if (WordMatchStringZ(networkNode->NetworkItem->LocalAddressString)) - return TRUE; - } - - if (networkNode->NetworkItem->LocalPortString[0]) - { - if (WordMatchStringZ(networkNode->NetworkItem->LocalPortString)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(networkNode->NetworkItem->LocalHostString)) - { - if (WordMatchStringRef(&networkNode->NetworkItem->LocalHostString->sr)) - return TRUE; - } - - if (networkNode->NetworkItem->RemoteAddressString[0]) - { - if (WordMatchStringZ(networkNode->NetworkItem->RemoteAddressString)) - return TRUE; - } - - if (networkNode->NetworkItem->RemotePortString[0]) - { - if (WordMatchStringZ(networkNode->NetworkItem->RemotePortString)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(networkNode->NetworkItem->RemoteHostString)) - { - if (WordMatchStringRef(&networkNode->NetworkItem->RemoteHostString->sr)) - return TRUE; - } - - if (WordMatchStringZ(PhGetProtocolTypeName(networkNode->NetworkItem->ProtocolType))) - return TRUE; - - if ((networkNode->NetworkItem->ProtocolType & PH_TCP_PROTOCOL_TYPE) && - WordMatchStringZ(PhGetTcpStateName(networkNode->NetworkItem->State))) - return TRUE; - - if (networkNode->NetworkItem->ProcessId) - { - PPH_PROCESS_NODE processNode; - WCHAR processIdString[PH_INT32_STR_LEN_1]; - - PhPrintUInt32(processIdString, HandleToUlong(networkNode->NetworkItem->ProcessId)); - - if (WordMatchStringZ(processIdString)) - return TRUE; - - // Search the process node - if (processNode = PhFindProcessNode(networkNode->NetworkItem->ProcessId)) - { - if (ProcessTreeFilterCallback(&processNode->Node, NULL)) - return TRUE; - } - } - - return FALSE; -} - -// NOTE: This function does not use the SCM due to major performance issues. -// For now just query this information from the registry but it might be out-of-sync -// with any recent services changes until the SCM flushes its cache. -NTSTATUS QueryServiceFileName( - _In_ PPH_STRINGREF ServiceName, - _Out_ PPH_STRING *ServiceFileName, - _Out_ PPH_STRING *ServiceBinaryPath - ) -{ - static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); - static PH_STRINGREF typeKeyName = PH_STRINGREF_INIT(L"Type"); - - NTSTATUS status; - HANDLE keyHandle; - ULONG serviceType = 0; - PPH_STRING keyName; - PPH_STRING binaryPath; - PPH_STRING fileName; - - keyName = PhConcatStringRef2(&servicesKeyName, ServiceName); - binaryPath = NULL; - fileName = NULL; - - if (NT_SUCCESS(status = PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName->sr, - 0 - ))) - { - PPH_STRING serviceImagePath; - PKEY_VALUE_PARTIAL_INFORMATION buffer; - - if (NT_SUCCESS(status = PhQueryValueKey( - keyHandle, - &typeKeyName, - KeyValuePartialInformation, - &buffer - ))) - { - if ( - buffer->Type == REG_DWORD && - buffer->DataLength == sizeof(ULONG) - ) - { - serviceType = *(PULONG)buffer->Data; - } - - PhFree(buffer); - } - - if (serviceImagePath = PhQueryRegistryString(keyHandle, L"ImagePath")) - { - PPH_STRING expandedString; - - if (expandedString = PhExpandEnvironmentStrings(&serviceImagePath->sr)) - { - binaryPath = expandedString; - PhDereferenceObject(serviceImagePath); - } - else - { - binaryPath = serviceImagePath; - } - } - else - { - status = STATUS_NOT_FOUND; - } - - NtClose(keyHandle); - } - - if (NT_SUCCESS(status)) - { - PhGetServiceDllParameter(ServiceName, &fileName); - - if (!fileName) - { - if (serviceType & SERVICE_WIN32) - { - PH_STRINGREF dummyFileName; - PH_STRINGREF dummyArguments; - - PhParseCommandLineFuzzy(&binaryPath->sr, &dummyFileName, &dummyArguments, &fileName); - - if (!fileName) - PhSwapReference(&fileName, binaryPath); - } - else - { - fileName = PhGetFileName(binaryPath); - } - } - - *ServiceFileName = fileName; - *ServiceBinaryPath = binaryPath; - } - else - { - if (binaryPath) - PhDereferenceObject(binaryPath); - } - - PhDereferenceObject(keyName); - - return status; -} +/* + * Process Hacker ToolStatus - + * search filter callbacks + * + * Copyright (C) 2011-2016 dmex + * Copyright (C) 2010-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" +#include + +BOOLEAN WordMatchStringRef( + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = SearchboxText->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN WordMatchStringZ( + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + return WordMatchStringRef(&text); +} + +BOOLEAN ProcessTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPH_PROCESS_NODE processNode = (PPH_PROCESS_NODE)Node; + + if (PhIsNullOrEmptyString(SearchboxText)) + return TRUE; + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->ProcessName)) + { + if (WordMatchStringRef(&processNode->ProcessItem->ProcessName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->FileName)) + { + if (WordMatchStringRef(&processNode->ProcessItem->FileName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->CommandLine)) + { + if (WordMatchStringRef(&processNode->ProcessItem->CommandLine->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.CompanyName)) + { + if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.CompanyName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.FileDescription)) + { + if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.FileDescription->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.FileVersion)) + { + if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.FileVersion->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->VersionInfo.ProductName)) + { + if (WordMatchStringRef(&processNode->ProcessItem->VersionInfo.ProductName->sr)) + return TRUE; + } + + //if (!PhIsNullOrEmptyString(processNode->ProcessItem->UserName)) + //{ + // if (WordMatchStringRef(&processNode->ProcessItem->UserName->sr)) + // return TRUE; + //} + + if (processNode->ProcessItem->IntegrityString) + { + if (WordMatchStringZ(processNode->ProcessItem->IntegrityString)) + return TRUE; + } + + //if (!PhIsNullOrEmptyString(processNode->ProcessItem->JobName)) + //{ + // if (WordMatchStringRef(&processNode->ProcessItem->JobName->sr)) + // return TRUE; + //} + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->VerifySignerName)) + { + if (WordMatchStringRef(&processNode->ProcessItem->VerifySignerName->sr)) + return TRUE; + } + + if (PH_IS_REAL_PROCESS_ID(processNode->ProcessItem->ProcessId) && processNode->ProcessItem->ProcessIdString[0]) + { + if (WordMatchStringZ(processNode->ProcessItem->ProcessIdString)) + return TRUE; + + // HACK PidHexText from PH_PROCESS_NODE is not exported (dmex) + { + PH_FORMAT format; + SIZE_T returnLength; + PH_STRINGREF processIdHex; + WCHAR pidHexText[PH_PTR_STR_LEN_1]; + + PhInitFormatIX(&format, HandleToUlong(processNode->ProcessItem->ProcessId)); + + if (PhFormatToBuffer(&format, 1, pidHexText, sizeof(pidHexText), &returnLength)) + { + processIdHex.Buffer = pidHexText; + processIdHex.Length = returnLength - sizeof(UNICODE_NULL); + + if (WordMatchStringRef(&processIdHex)) + return TRUE; + } + } + } + + if (processNode->ProcessItem->ParentProcessIdString[0]) + { + if (WordMatchStringZ(processNode->ProcessItem->ParentProcessIdString)) + return TRUE; + } + + if (processNode->ProcessItem->SessionIdString[0]) + { + if (WordMatchStringZ(processNode->ProcessItem->SessionIdString)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(processNode->ProcessItem->PackageFullName)) + { + if (WordMatchStringRef(&processNode->ProcessItem->PackageFullName->sr)) + return TRUE; + } + + if (WordMatchStringZ(PhGetProcessPriorityClassString(processNode->ProcessItem->PriorityClass))) + { + return TRUE; + } + + if (processNode->ProcessItem->VerifyResult != VrUnknown) + { + switch (processNode->ProcessItem->VerifyResult) + { + case VrNoSignature: + if (WordMatchStringZ(L"NoSignature")) + return TRUE; + break; + case VrTrusted: + if (WordMatchStringZ(L"Trusted")) + return TRUE; + break; + case VrExpired: + if (WordMatchStringZ(L"Expired")) + return TRUE; + break; + case VrRevoked: + if (WordMatchStringZ(L"Revoked")) + return TRUE; + break; + case VrDistrust: + if (WordMatchStringZ(L"Distrust")) + return TRUE; + break; + case VrSecuritySettings: + if (WordMatchStringZ(L"SecuritySettings")) + return TRUE; + break; + case VrBadSignature: + if (WordMatchStringZ(L"BadSignature")) + return TRUE; + break; + default: + if (WordMatchStringZ(L"Unknown")) + return TRUE; + break; + } + } + + if (processNode->ProcessItem->ElevationType != TokenElevationTypeDefault) + { + switch (processNode->ProcessItem->ElevationType) + { + case TokenElevationTypeLimited: + if (WordMatchStringZ(L"Limited")) + return TRUE; + break; + case TokenElevationTypeFull: + if (WordMatchStringZ(L"Full")) + return TRUE; + break; + default: + if (WordMatchStringZ(L"Unknown")) + return TRUE; + break; + } + } + + if (WordMatchStringZ(L"IsBeingDebugged") && processNode->ProcessItem->IsBeingDebugged) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsDotNet") && processNode->ProcessItem->IsDotNet) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsElevated") && processNode->ProcessItem->IsElevated) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsInJob") && processNode->ProcessItem->IsInJob) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsInSignificantJob") && processNode->ProcessItem->IsInSignificantJob) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsPacked") && processNode->ProcessItem->IsPacked) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsSuspended") && processNode->ProcessItem->IsSuspended) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsWow64") && processNode->ProcessItem->IsWow64) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsImmersive") && processNode->ProcessItem->IsImmersive) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsProtectedProcess") && processNode->ProcessItem->IsProtectedProcess) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsSecureProcess") && processNode->ProcessItem->IsSecureProcess) + { + return TRUE; + } + + if (WordMatchStringZ(L"IsPicoProcess") && processNode->ProcessItem->IsSubsystemProcess) + { + return TRUE; + } + + if (processNode->ProcessItem->ServiceList && processNode->ProcessItem->ServiceList->Count) + { + ULONG enumerationKey = 0; + PPH_SERVICE_ITEM serviceItem; + PPH_LIST serviceList; + ULONG i; + BOOLEAN matched = FALSE; + + // Copy the service list so we can search it. + serviceList = PhCreateList(processNode->ProcessItem->ServiceList->Count); + + PhAcquireQueuedLockShared(&processNode->ProcessItem->ServiceListLock); + + while (PhEnumPointerList( + processNode->ProcessItem->ServiceList, + &enumerationKey, + &serviceItem + )) + { + PhReferenceObject(serviceItem); + PhAddItemList(serviceList, serviceItem); + } + + PhReleaseQueuedLockShared(&processNode->ProcessItem->ServiceListLock); + + for (i = 0; i < serviceList->Count; i++) + { + PPH_STRING serviceFileName = NULL; + PPH_STRING serviceBinaryPath = NULL; + + serviceItem = serviceList->Items[i]; + + if (!PhIsNullOrEmptyString(serviceItem->Name)) + { + if (WordMatchStringRef(&serviceItem->Name->sr)) + { + matched = TRUE; + break; + } + } + + if (!PhIsNullOrEmptyString(serviceItem->DisplayName)) + { + if (WordMatchStringRef(&serviceItem->DisplayName->sr)) + { + matched = TRUE; + break; + } + } + + if (serviceItem->ProcessId) + { + if (WordMatchStringZ(serviceItem->ProcessIdString)) + { + matched = TRUE; + break; + } + } + + if (NT_SUCCESS(QueryServiceFileName( + &serviceItem->Name->sr, + &serviceFileName, + &serviceBinaryPath + ))) + { + if (serviceFileName) + { + if (WordMatchStringRef(&serviceFileName->sr)) + { + matched = TRUE; + } + + PhDereferenceObject(serviceFileName); + } + + if (serviceBinaryPath) + { + if (WordMatchStringRef(&serviceBinaryPath->sr)) + { + matched = TRUE; + } + + PhDereferenceObject(serviceBinaryPath); + } + + if (matched) + break; + } + } + + PhDereferenceObjects(serviceList->Items, serviceList->Count); + PhDereferenceObject(serviceList); + + if (matched) + return TRUE; + } + + return FALSE; +} + +BOOLEAN ServiceTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPH_SERVICE_NODE serviceNode = (PPH_SERVICE_NODE)Node; + PPH_STRING serviceFileName = NULL; + PPH_STRING serviceBinaryPath = NULL; + + if (PhIsNullOrEmptyString(SearchboxText)) + return TRUE; + + if (WordMatchStringZ(PhGetServiceTypeString(serviceNode->ServiceItem->Type))) + return TRUE; + + if (WordMatchStringZ(PhGetServiceStateString(serviceNode->ServiceItem->State))) + return TRUE; + + if (WordMatchStringZ(PhGetServiceStartTypeString(serviceNode->ServiceItem->StartType))) + return TRUE; + + if (WordMatchStringZ(PhGetServiceErrorControlString(serviceNode->ServiceItem->ErrorControl))) + return TRUE; + + if (!PhIsNullOrEmptyString(serviceNode->ServiceItem->Name)) + { + if (WordMatchStringRef(&serviceNode->ServiceItem->Name->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(serviceNode->ServiceItem->DisplayName)) + { + if (WordMatchStringRef(&serviceNode->ServiceItem->DisplayName->sr)) + return TRUE; + } + + if (serviceNode->ServiceItem->ProcessId) + { + PPH_PROCESS_NODE processNode; + + if (WordMatchStringZ(serviceNode->ServiceItem->ProcessIdString)) + return TRUE; + + // Search the process node + if (processNode = PhFindProcessNode(serviceNode->ServiceItem->ProcessId)) + { + if (ProcessTreeFilterCallback(&processNode->Node, NULL)) + return TRUE; + } + } + + if (!PhIsNullOrEmptyString(serviceNode->ServiceItem->VerifySignerName)) + { + if (WordMatchStringRef(&serviceNode->ServiceItem->VerifySignerName->sr)) + return TRUE; + } + + if (serviceNode->ServiceItem->VerifyResult != VrUnknown) + { + switch (serviceNode->ServiceItem->VerifyResult) + { + case VrNoSignature: + if (WordMatchStringZ(L"NoSignature")) + return TRUE; + break; + case VrTrusted: + if (WordMatchStringZ(L"Trusted")) + return TRUE; + break; + case VrExpired: + if (WordMatchStringZ(L"Expired")) + return TRUE; + break; + case VrRevoked: + if (WordMatchStringZ(L"Revoked")) + return TRUE; + break; + case VrDistrust: + if (WordMatchStringZ(L"Distrust")) + return TRUE; + break; + case VrSecuritySettings: + if (WordMatchStringZ(L"SecuritySettings")) + return TRUE; + break; + case VrBadSignature: + if (WordMatchStringZ(L"BadSignature")) + return TRUE; + break; + default: + if (WordMatchStringZ(L"Unknown")) + return TRUE; + break; + } + } + + if (NT_SUCCESS(QueryServiceFileName( + &serviceNode->ServiceItem->Name->sr, + &serviceFileName, + &serviceBinaryPath + ))) + { + BOOLEAN matched = FALSE; + + if (serviceFileName) + { + if (WordMatchStringRef(&serviceFileName->sr)) + { + matched = TRUE; + } + + PhDereferenceObject(serviceFileName); + } + + if (serviceBinaryPath) + { + if (WordMatchStringRef(&serviceBinaryPath->sr)) + { + matched = TRUE; + } + + PhDereferenceObject(serviceBinaryPath); + } + + if (matched) + return TRUE; + } + + return FALSE; +} + +// copied from ProcessHacker\netlist.c.. +static PPH_STRING PhpNetworkTreeGetNetworkItemProcessName( + _In_ PPH_NETWORK_ITEM NetworkItem + ) +{ + PH_FORMAT format[4]; + + if (!NetworkItem->ProcessId) + return PhaCreateString(L"Waiting connections"); + + PhInitFormatS(&format[1], L" ("); + PhInitFormatU(&format[2], HandleToUlong(NetworkItem->ProcessId)); + PhInitFormatC(&format[3], ')'); + + if (NetworkItem->ProcessName) + PhInitFormatSR(&format[0], NetworkItem->ProcessName->sr); + else + PhInitFormatS(&format[0], L"Unknown process"); + + return PH_AUTO(PhFormat(format, 4, 96)); +} + +BOOLEAN NetworkTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPH_NETWORK_NODE networkNode = (PPH_NETWORK_NODE)Node; + PPH_STRING processNameText; + + if (PhIsNullOrEmptyString(SearchboxText)) + return TRUE; + + // TODO: We need export the PPH_NETWORK_NODE->ProcessNameText field to search + // waiting/unknown network connections... For now just replicate the data here. + processNameText = PhpNetworkTreeGetNetworkItemProcessName(networkNode->NetworkItem); + + if (!PhIsNullOrEmptyString(processNameText)) + { + if (WordMatchStringRef(&processNameText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(networkNode->NetworkItem->ProcessName)) + { + if (WordMatchStringRef(&networkNode->NetworkItem->ProcessName->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(networkNode->NetworkItem->OwnerName)) + { + if (WordMatchStringRef(&networkNode->NetworkItem->OwnerName->sr)) + return TRUE; + } + + if (networkNode->NetworkItem->LocalAddressString[0]) + { + if (WordMatchStringZ(networkNode->NetworkItem->LocalAddressString)) + return TRUE; + } + + if (networkNode->NetworkItem->LocalPortString[0]) + { + if (WordMatchStringZ(networkNode->NetworkItem->LocalPortString)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(networkNode->NetworkItem->LocalHostString)) + { + if (WordMatchStringRef(&networkNode->NetworkItem->LocalHostString->sr)) + return TRUE; + } + + if (networkNode->NetworkItem->RemoteAddressString[0]) + { + if (WordMatchStringZ(networkNode->NetworkItem->RemoteAddressString)) + return TRUE; + } + + if (networkNode->NetworkItem->RemotePortString[0]) + { + if (WordMatchStringZ(networkNode->NetworkItem->RemotePortString)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(networkNode->NetworkItem->RemoteHostString)) + { + if (WordMatchStringRef(&networkNode->NetworkItem->RemoteHostString->sr)) + return TRUE; + } + + if (WordMatchStringZ(PhGetProtocolTypeName(networkNode->NetworkItem->ProtocolType))) + return TRUE; + + if ((networkNode->NetworkItem->ProtocolType & PH_TCP_PROTOCOL_TYPE) && + WordMatchStringZ(PhGetTcpStateName(networkNode->NetworkItem->State))) + return TRUE; + + if (networkNode->NetworkItem->ProcessId) + { + PPH_PROCESS_NODE processNode; + WCHAR processIdString[PH_INT32_STR_LEN_1]; + + PhPrintUInt32(processIdString, HandleToUlong(networkNode->NetworkItem->ProcessId)); + + if (WordMatchStringZ(processIdString)) + return TRUE; + + // Search the process node + if (processNode = PhFindProcessNode(networkNode->NetworkItem->ProcessId)) + { + if (ProcessTreeFilterCallback(&processNode->Node, NULL)) + return TRUE; + } + } + + return FALSE; +} + +// NOTE: This function does not use the SCM due to major performance issues. +// For now just query this information from the registry but it might be out-of-sync +// with any recent services changes until the SCM flushes its cache. +NTSTATUS QueryServiceFileName( + _In_ PPH_STRINGREF ServiceName, + _Out_ PPH_STRING *ServiceFileName, + _Out_ PPH_STRING *ServiceBinaryPath + ) +{ + static PH_STRINGREF servicesKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\"); + static PH_STRINGREF typeKeyName = PH_STRINGREF_INIT(L"Type"); + + NTSTATUS status; + HANDLE keyHandle; + ULONG serviceType = 0; + PPH_STRING keyName; + PPH_STRING binaryPath; + PPH_STRING fileName; + + keyName = PhConcatStringRef2(&servicesKeyName, ServiceName); + binaryPath = NULL; + fileName = NULL; + + if (NT_SUCCESS(status = PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName->sr, + 0 + ))) + { + PPH_STRING serviceImagePath; + PKEY_VALUE_PARTIAL_INFORMATION buffer; + + if (NT_SUCCESS(status = PhQueryValueKey( + keyHandle, + &typeKeyName, + KeyValuePartialInformation, + &buffer + ))) + { + if ( + buffer->Type == REG_DWORD && + buffer->DataLength == sizeof(ULONG) + ) + { + serviceType = *(PULONG)buffer->Data; + } + + PhFree(buffer); + } + + if (serviceImagePath = PhQueryRegistryString(keyHandle, L"ImagePath")) + { + PPH_STRING expandedString; + + if (expandedString = PhExpandEnvironmentStrings(&serviceImagePath->sr)) + { + binaryPath = expandedString; + PhDereferenceObject(serviceImagePath); + } + else + { + binaryPath = serviceImagePath; + } + } + else + { + status = STATUS_NOT_FOUND; + } + + NtClose(keyHandle); + } + + if (NT_SUCCESS(status)) + { + PhGetServiceDllParameter(serviceType, ServiceName, &fileName); + + if (!fileName) + { + if (serviceType & SERVICE_WIN32) + { + PH_STRINGREF dummyFileName; + PH_STRINGREF dummyArguments; + + PhParseCommandLineFuzzy(&binaryPath->sr, &dummyFileName, &dummyArguments, &fileName); + + if (!fileName) + PhSwapReference(&fileName, binaryPath); + } + else + { + fileName = PhGetFileName(binaryPath); + } + } + + *ServiceFileName = fileName; + *ServiceBinaryPath = binaryPath; + } + else + { + if (binaryPath) + PhDereferenceObject(binaryPath); + } + + PhDereferenceObject(keyName); + + return status; +} diff --git a/plugins/ToolStatus/graph.c b/plugins/ToolStatus/graph.c index 856838fb1eb2..73f957c752dc 100644 --- a/plugins/ToolStatus/graph.c +++ b/plugins/ToolStatus/graph.c @@ -1,662 +1,870 @@ -/* - * Process Hacker ToolStatus - - * Toolbar Graph Bands - * - * Copyright (C) 2015-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" - -HWND CpuGraphHandle = NULL; -HWND MemGraphHandle = NULL; -HWND CommitGraphHandle = NULL; -HWND IoGraphHandle = NULL; -static PH_GRAPH_STATE CpuGraphState; -static PH_GRAPH_STATE MemGraphState; -static PH_GRAPH_STATE CommitGraphState; -static PH_GRAPH_STATE IoGraphState; - -VOID ToolbarCreateGraphs(VOID) -{ - UINT height = (UINT)SendMessage(RebarHandle, RB_GETROWHEIGHT, 0, 0); - - if (ToolStatusConfig.CpuGraphEnabled && !CpuGraphHandle) - { - CpuGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 0, - 0, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(CpuGraphHandle, TRUE); - - PhInitializeGraphState(&CpuGraphState); - } - - if (ToolStatusConfig.MemGraphEnabled && !MemGraphHandle) - { - MemGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 0, - 0, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(MemGraphHandle, TRUE); - - PhInitializeGraphState(&MemGraphState); - } - - if (ToolStatusConfig.CommitGraphEnabled && !CommitGraphHandle) - { - CommitGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 0, - 0, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(CommitGraphHandle, TRUE); - - PhInitializeGraphState(&CommitGraphState); - } - - if (ToolStatusConfig.IoGraphEnabled && !IoGraphHandle) - { - IoGraphHandle = CreateWindow( - PH_GRAPH_CLASSNAME, - NULL, - WS_VISIBLE | WS_CHILD | WS_BORDER, - 0, - 0, - 0, - 0, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - Graph_SetTooltip(IoGraphHandle, TRUE); - - PhInitializeGraphState(&IoGraphState); - } - - if (ToolStatusConfig.CpuGraphEnabled) - { - if (!RebarBandExists(REBAR_BAND_ID_CPUGRAPH)) - RebarBandInsert(REBAR_BAND_ID_CPUGRAPH, CpuGraphHandle, 145, height); // 85 - - if (CpuGraphHandle && !IsWindowVisible(CpuGraphHandle)) - ShowWindow(CpuGraphHandle, SW_SHOW); - } - else - { - if (RebarBandExists(REBAR_BAND_ID_CPUGRAPH)) - RebarBandRemove(REBAR_BAND_ID_CPUGRAPH); - - if (CpuGraphHandle) - { - PhDeleteGraphState(&CpuGraphState); - - DestroyWindow(CpuGraphHandle); - CpuGraphHandle = NULL; - } - } - - if (ToolStatusConfig.MemGraphEnabled) - { - if (!RebarBandExists(REBAR_BAND_ID_MEMGRAPH)) - RebarBandInsert(REBAR_BAND_ID_MEMGRAPH, MemGraphHandle, 145, height); // 85 - - if (MemGraphHandle && !IsWindowVisible(MemGraphHandle)) - { - ShowWindow(MemGraphHandle, SW_SHOW); - } - } - else - { - if (RebarBandExists(REBAR_BAND_ID_MEMGRAPH)) - RebarBandRemove(REBAR_BAND_ID_MEMGRAPH); - - if (MemGraphHandle) - { - PhDeleteGraphState(&MemGraphState); - - DestroyWindow(MemGraphHandle); - MemGraphHandle = NULL; - } - } - - if (ToolStatusConfig.CommitGraphEnabled) - { - if (!RebarBandExists(REBAR_BAND_ID_COMMITGRAPH)) - RebarBandInsert(REBAR_BAND_ID_COMMITGRAPH, CommitGraphHandle, 145, height); // 85 - - if (CommitGraphHandle && !IsWindowVisible(CommitGraphHandle)) - { - ShowWindow(CommitGraphHandle, SW_SHOW); - } - } - else - { - if (RebarBandExists(REBAR_BAND_ID_COMMITGRAPH)) - RebarBandRemove(REBAR_BAND_ID_COMMITGRAPH); - - if (CommitGraphHandle) - { - PhDeleteGraphState(&CommitGraphState); - - DestroyWindow(CommitGraphHandle); - CommitGraphHandle = NULL; - } - } - - if (ToolStatusConfig.IoGraphEnabled) - { - if (!RebarBandExists(REBAR_BAND_ID_IOGRAPH)) - RebarBandInsert(REBAR_BAND_ID_IOGRAPH, IoGraphHandle, 145, height); // 85 - - if (IoGraphHandle && !IsWindowVisible(IoGraphHandle)) - { - ShowWindow(IoGraphHandle, SW_SHOW); - } - } - else - { - if (RebarBandExists(REBAR_BAND_ID_IOGRAPH)) - RebarBandRemove(REBAR_BAND_ID_IOGRAPH); - - if (IoGraphHandle) - { - PhDeleteGraphState(&IoGraphState); - - DestroyWindow(IoGraphHandle); - IoGraphHandle = NULL; - } - } -} - -VOID ToolbarUpdateGraphs(VOID) -{ - if (ToolStatusConfig.CpuGraphEnabled && CpuGraphHandle) - { - CpuGraphState.Valid = FALSE; - CpuGraphState.TooltipIndex = -1; - Graph_MoveGrid(CpuGraphHandle, 1); - Graph_Draw(CpuGraphHandle); - Graph_UpdateTooltip(CpuGraphHandle); - InvalidateRect(CpuGraphHandle, NULL, FALSE); - } - - if (ToolStatusConfig.MemGraphEnabled && MemGraphHandle) - { - MemGraphState.Valid = FALSE; - MemGraphState.TooltipIndex = -1; - Graph_MoveGrid(MemGraphHandle, 1); - Graph_Draw(MemGraphHandle); - Graph_UpdateTooltip(MemGraphHandle); - InvalidateRect(MemGraphHandle, NULL, FALSE); - } - - if (ToolStatusConfig.CommitGraphEnabled && CommitGraphHandle) - { - CommitGraphState.Valid = FALSE; - CommitGraphState.TooltipIndex = -1; - Graph_MoveGrid(CommitGraphHandle, 1); - Graph_Draw(CommitGraphHandle); - Graph_UpdateTooltip(CommitGraphHandle); - InvalidateRect(CommitGraphHandle, NULL, FALSE); - } - - if (ToolStatusConfig.IoGraphEnabled && IoGraphHandle) - { - IoGraphState.Valid = FALSE; - IoGraphState.TooltipIndex = -1; - Graph_MoveGrid(IoGraphHandle, 1); - Graph_Draw(IoGraphHandle); - Graph_UpdateTooltip(IoGraphHandle); - InvalidateRect(IoGraphHandle, NULL, FALSE); - } -} - -// -// BEGIN copied from ProcessHacker/sysinfo.c -// - -static PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord( - _In_ LONG Index - ) -{ - LARGE_INTEGER time; - LONG maxProcessIdLong; - HANDLE maxProcessId; - - // Find the process record for the max. CPU process for the particular time. - - maxProcessIdLong = PhGetItemCircularBuffer_ULONG(SystemStatistics.MaxCpuHistory, Index); - - if (!maxProcessIdLong) - return NULL; - - // This must be treated as a signed integer to handle Interrupts correctly. - maxProcessId = LongToHandle(maxProcessIdLong); - - // Note that the time we get has its components beyond seconds cleared. - // For example: - // * At 2.5 seconds a process is started. - // * At 2.75 seconds our process provider is fired, and the process is determined - // to have 75% CPU usage, which happens to be the maximum CPU usage. - // * However the 2.75 seconds is recorded as 2 seconds due to - // RtlTimeToSecondsSince1980. - // * If we call PhFindProcessRecord, it cannot find the process because it was - // started at 2.5 seconds, not 2 seconds or older. - // - // This mean we must add one second minus one tick (100ns) to the time, giving us - // 2.9999999 seconds. This will then make sure we find the process. - PhGetStatisticsTime(NULL, Index, &time); - time.QuadPart += PH_TICKS_PER_SEC - 1; - - return PhFindProcessRecord(maxProcessId, &time); -} - -static PPH_STRING PhSipGetMaxCpuString( - _In_ LONG Index - ) -{ - PPH_PROCESS_RECORD maxProcessRecord; - FLOAT maxCpuUsage; - PPH_STRING maxUsageString = NULL; - - if (maxProcessRecord = PhSipReferenceMaxCpuRecord(Index)) - { - // We found the process record, so now we construct the max. usage string. - maxCpuUsage = PhGetItemCircularBuffer_FLOAT(SystemStatistics.MaxCpuUsageHistory, Index); - - // Make sure we don't try to display the PID of DPCs or Interrupts. - if (!PH_IS_FAKE_PROCESS_ID(maxProcessRecord->ProcessId)) - { - maxUsageString = PhaFormatString( - L"\n%s (%u): %.2f%%", - maxProcessRecord->ProcessName->Buffer, - HandleToUlong(maxProcessRecord->ProcessId), - maxCpuUsage * 100 - ); - } - else - { - maxUsageString = PhaFormatString( - L"\n%s: %.2f%%", - maxProcessRecord->ProcessName->Buffer, - maxCpuUsage * 100 - ); - } - - PhDereferenceProcessRecord(maxProcessRecord); - } - - return maxUsageString; -} - -static PPH_PROCESS_RECORD PhSipReferenceMaxIoRecord( - _In_ LONG Index - ) -{ - LARGE_INTEGER time; - ULONG maxProcessId; - - // Find the process record for the max. I/O process for the particular time. - - maxProcessId = PhGetItemCircularBuffer_ULONG(SystemStatistics.MaxIoHistory, Index); - - if (!maxProcessId) - return NULL; - - // See above for the explanation. - PhGetStatisticsTime(NULL, Index, &time); - time.QuadPart += PH_TICKS_PER_SEC - 1; - - return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); -} - -static PPH_STRING PhSipGetMaxIoString( - _In_ LONG Index - ) -{ - PPH_PROCESS_RECORD maxProcessRecord; - ULONG64 maxIoReadOther; - ULONG64 maxIoWrite; - - PPH_STRING maxUsageString = NULL; - - if (maxProcessRecord = PhSipReferenceMaxIoRecord(Index)) - { - // We found the process record, so now we construct the max. usage string. - maxIoReadOther = PhGetItemCircularBuffer_ULONG64(SystemStatistics.MaxIoReadOtherHistory, Index); - maxIoWrite = PhGetItemCircularBuffer_ULONG64(SystemStatistics.MaxIoWriteHistory, Index); - - if (!PH_IS_FAKE_PROCESS_ID(maxProcessRecord->ProcessId)) - { - maxUsageString = PhaFormatString( - L"\n%s (%u): R+O: %s, W: %s", - maxProcessRecord->ProcessName->Buffer, - HandleToUlong(maxProcessRecord->ProcessId), - PhaFormatSize(maxIoReadOther, -1)->Buffer, - PhaFormatSize(maxIoWrite, -1)->Buffer - ); - } - else - { - maxUsageString = PhaFormatString( - L"\n%s: R+O: %s, W: %s", - maxProcessRecord->ProcessName->Buffer, - PhaFormatSize(maxIoReadOther, -1)->Buffer, - PhaFormatSize(maxIoWrite, -1)->Buffer - ); - } - - PhDereferenceProcessRecord(maxProcessRecord); - } - - return maxUsageString; -} - -// -// END copied from ProcessHacker/sysinfo.c -// - -VOID ToolbarUpdateGraphsInfo(LPNMHDR Header) -{ - switch (Header->code) - { - case GCN_GETDRAWINFO: - { - if (ToolStatusConfig.CpuGraphEnabled && Header->hwndFrom == CpuGraphHandle) - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_LINE_2; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), PhGetIntegerSetting(L"ColorCpuUser")); - - if (ProcessesUpdatedCount < 2) - return; - - PhGraphStateGetDrawInfo(&CpuGraphState, getDrawInfo, SystemStatistics.CpuUserHistory->Count); - - if (!CpuGraphState.Valid) - { - PhCopyCircularBuffer_FLOAT(SystemStatistics.CpuKernelHistory, - CpuGraphState.Data1, drawInfo->LineDataCount); - PhCopyCircularBuffer_FLOAT(SystemStatistics.CpuUserHistory, - CpuGraphState.Data2, drawInfo->LineDataCount); - - CpuGraphState.Valid = TRUE; - } - } - else if (ToolStatusConfig.MemGraphEnabled && Header->hwndFrom == MemGraphHandle) - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPhysical"), 0); - - if (ProcessesUpdatedCount < 2) - return; - - PhGraphStateGetDrawInfo(&MemGraphState, getDrawInfo, SystemStatistics.PhysicalHistory->Count); - - if (!MemGraphState.Valid) - { - for (ULONG i = 0; i < drawInfo->LineDataCount; i++) - { - MemGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(SystemStatistics.PhysicalHistory, i); - } - - PhDivideSinglesBySingle( - MemGraphState.Data1, - (FLOAT)PhSystemBasicInformation.NumberOfPhysicalPages, - drawInfo->LineDataCount - ); - - MemGraphState.Valid = TRUE; - } - } - else if (ToolStatusConfig.CommitGraphEnabled && Header->hwndFrom == CommitGraphHandle) - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); - - if (ProcessesUpdatedCount < 2) - return; - - PhGraphStateGetDrawInfo(&CommitGraphState, getDrawInfo, SystemStatistics.CommitHistory->Count); - - if (!CommitGraphState.Valid) - { - for (ULONG i = 0; i < drawInfo->LineDataCount; i++) - { - CommitGraphState.Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(SystemStatistics.CommitHistory, i); - } - - PhDivideSinglesBySingle( - CommitGraphState.Data1, - (FLOAT)SystemStatistics.Performance->CommitLimit, - drawInfo->LineDataCount - ); - - CommitGraphState.Valid = TRUE; - } - } - else if (ToolStatusConfig.IoGraphEnabled && Header->hwndFrom == IoGraphHandle) - { - PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; - PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; - - drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_LINE_2; - PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); - - if (ProcessesUpdatedCount < 2) - return; - - PhGraphStateGetDrawInfo(&IoGraphState, getDrawInfo, SystemStatistics.IoReadHistory->Count); - - if (!IoGraphState.Valid) - { - FLOAT max = 1024 * 1024; // minimum scaling of 1 MB. - - for (ULONG i = 0; i < drawInfo->LineDataCount; i++) - { - IoGraphState.Data1[i] = - (FLOAT)PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoReadHistory, i) + - (FLOAT)PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoOtherHistory, i); - IoGraphState.Data2[i] = - (FLOAT)PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoWriteHistory, i); - - if (max < IoGraphState.Data1[i] + IoGraphState.Data2[i]) - max = IoGraphState.Data1[i] + IoGraphState.Data2[i]; - } - - PhDivideSinglesBySingle(IoGraphState.Data1, max, drawInfo->LineDataCount); - PhDivideSinglesBySingle(IoGraphState.Data2, max, drawInfo->LineDataCount); - - IoGraphState.Valid = TRUE; - } - } - } - break; - case GCN_GETTOOLTIPTEXT: - { - PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; - - if (getTooltipText->Index < getTooltipText->TotalCount) - { - if (ToolStatusConfig.CpuGraphEnabled && Header->hwndFrom == CpuGraphHandle) - { - if (CpuGraphState.TooltipIndex != getTooltipText->Index) - { - FLOAT cpuKernel; - FLOAT cpuUser; - - cpuKernel = PhGetItemCircularBuffer_FLOAT(SystemStatistics.CpuKernelHistory, getTooltipText->Index); - cpuUser = PhGetItemCircularBuffer_FLOAT(SystemStatistics.CpuUserHistory, getTooltipText->Index); - - PhMoveReference(&CpuGraphState.TooltipText, PhFormatString( - L"%.2f%%%s\n%s", - (cpuKernel + cpuUser) * 100, - PhGetStringOrEmpty(PhSipGetMaxCpuString(getTooltipText->Index)), - PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer - )); - } - - getTooltipText->Text = CpuGraphState.TooltipText->sr; - } - else if (ToolStatusConfig.MemGraphEnabled && Header->hwndFrom == MemGraphHandle) - { - ULONG physicalUsage; - - physicalUsage = PhGetItemCircularBuffer_ULONG(SystemStatistics.PhysicalHistory, getTooltipText->Index); - - PhMoveReference(&MemGraphState.TooltipText, PhFormatString( - L"Physical memory: %s\n%s", - PhaFormatSize(UInt32x32To64(physicalUsage, PAGE_SIZE), -1)->Buffer, - PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer - )); - getTooltipText->Text = MemGraphState.TooltipText->sr; - } - else if (ToolStatusConfig.CommitGraphEnabled && Header->hwndFrom == CommitGraphHandle) - { - ULONG commitUsage; - - commitUsage = PhGetItemCircularBuffer_ULONG(SystemStatistics.CommitHistory, getTooltipText->Index); - - PhMoveReference(&CommitGraphState.TooltipText, PhFormatString( - L"Commit charge: %s\n%s", - PhaFormatSize(UInt32x32To64(commitUsage, PAGE_SIZE), -1)->Buffer, - PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer - )); - getTooltipText->Text = CommitGraphState.TooltipText->sr; - } - else if (ToolStatusConfig.IoGraphEnabled && Header->hwndFrom == IoGraphHandle) - { - ULONG64 ioRead; - ULONG64 ioWrite; - ULONG64 ioOther; - - ioRead = PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoReadHistory, getTooltipText->Index); - ioWrite = PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoWriteHistory, getTooltipText->Index); - ioOther = PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoOtherHistory, getTooltipText->Index); - - PhMoveReference(&IoGraphState.TooltipText, PhFormatString( - L"R: %s\nW: %s\nO: %s%s\n%s", - PhaFormatSize(ioRead, -1)->Buffer, - PhaFormatSize(ioWrite, -1)->Buffer, - PhaFormatSize(ioOther, -1)->Buffer, - PhGetStringOrEmpty(PhSipGetMaxIoString(getTooltipText->Index)), - PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer - )); - getTooltipText->Text = IoGraphState.TooltipText->sr; - } - } - } - break; - case GCN_MOUSEEVENT: - { - PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; - PPH_PROCESS_RECORD record = NULL; - - if (ToolStatusConfig.CpuGraphEnabled && Header->hwndFrom == CpuGraphHandle) - { - if (mouseEvent->Message == WM_RBUTTONUP) - { - ShowCustomizeMenu(); - } - else - { - if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) - { - record = PhSipReferenceMaxCpuRecord(mouseEvent->Index); - } - - if (record) - { - PhShowProcessRecordDialog(PhMainWndHandle, record); - PhDereferenceProcessRecord(record); - } - } - } - else if (ToolStatusConfig.MemGraphEnabled && Header->hwndFrom == MemGraphHandle) - { - if (mouseEvent->Message == WM_RBUTTONUP) - { - ShowCustomizeMenu(); - } - } - else if (ToolStatusConfig.CommitGraphEnabled && Header->hwndFrom == CommitGraphHandle) - { - if (mouseEvent->Message == WM_RBUTTONUP) - { - ShowCustomizeMenu(); - } - } - else if (ToolStatusConfig.IoGraphEnabled && Header->hwndFrom == IoGraphHandle) - { - if (mouseEvent->Message == WM_RBUTTONUP) - { - ShowCustomizeMenu(); - } - else - { - if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) - { - record = PhSipReferenceMaxIoRecord(mouseEvent->Index); - } - - if (record) - { - PhShowProcessRecordDialog(PhMainWndHandle, record); - PhDereferenceProcessRecord(record); - } - } - } - } - break; - } -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * Toolbar Graph Bands + * + * Copyright (C) 2015-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" + +PPH_LIST PhpToolbarGraphList = NULL; +PPH_HASHTABLE PhpToolbarGraphHashtable = NULL; + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(CpuHistoryGraphMessageCallback); +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(PhysicalHistoryGraphMessageCallback); +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(CommitHistoryGraphMessageCallback); +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(IoHistoryGraphMessageCallback); + +VOID ToolbarGraphLoadSettings( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRINGREF remaining; + + settingsString = PhGetStringSetting(SETTING_NAME_TOOLBAR_GRAPH_CONFIG); + remaining = settingsString->sr; + + if (remaining.Length == 0) + return; + + while (remaining.Length != 0) + { + PH_STRINGREF idPart; + PH_STRINGREF flagsPart; + PH_STRINGREF pluginNamePart; + ULONG64 idInteger; + ULONG64 flagsInteger; + + PhSplitStringRefAtChar(&remaining, '|', &idPart, &remaining); + PhSplitStringRefAtChar(&remaining, '|', &flagsPart, &remaining); + PhSplitStringRefAtChar(&remaining, '|', &pluginNamePart, &remaining); + + if (!PhStringToInteger64(&idPart, 10, &idInteger)) + break; + if (!PhStringToInteger64(&flagsPart, 10, &flagsInteger)) + break; + + if (flagsInteger) + { + PPH_TOOLBAR_GRAPH graph; + + if (pluginNamePart.Length) + { + if (graph = ToolbarGraphFindByName(&pluginNamePart, (ULONG)idInteger)) + graph->Flags |= TOOLSTATUS_GRAPH_ENABLED; + } + else + { + if (graph = ToolbarGraphFindById((ULONG)idInteger)) + graph->Flags |= TOOLSTATUS_GRAPH_ENABLED; + } + } + } + + PhDereferenceObject(settingsString); +} + +VOID ToolbarGraphSaveSettings( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRING_BUILDER graphListBuilder; + + PhInitializeStringBuilder(&graphListBuilder, 100); + + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph = PhpToolbarGraphList->Items[i]; + PPH_STRING pluginName; + + if (!(graph->Flags & TOOLSTATUS_GRAPH_ENABLED)) + continue; + + pluginName = graph->Plugin ? PhGetPluginName(graph->Plugin) : NULL; + + PhAppendFormatStringBuilder( + &graphListBuilder, + L"%lu|%lu|%s|", + graph->GraphId, + graph->Flags & TOOLSTATUS_GRAPH_ENABLED ? 1 : 0, + PhGetStringOrEmpty(pluginName) + ); + + if (pluginName) + PhDereferenceObject(pluginName); + } + + if (graphListBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&graphListBuilder, 1); + + settingsString = PhFinalStringBuilderString(&graphListBuilder); + PhSetStringSetting2(SETTING_NAME_TOOLBAR_GRAPH_CONFIG, &settingsString->sr); + PhDereferenceObject(settingsString); +} + +VOID ToolbarGraphsInitialize( + VOID + ) +{ + if (!PhpToolbarGraphList) + { + PhpToolbarGraphList = PhCreateList(10); + PhpToolbarGraphHashtable = PhCreateSimpleHashtable(10); + } + + ToolbarRegisterGraph( + PluginInstance, + 1, + L"CPU history", + 0, + NULL, + CpuHistoryGraphMessageCallback + ); + + ToolbarRegisterGraph( + PluginInstance, + 2, + L"Physical memory history", + 0, + NULL, + PhysicalHistoryGraphMessageCallback + ); + + ToolbarRegisterGraph( + PluginInstance, + 3, + L"Commit charge history", + 0, + NULL, + CommitHistoryGraphMessageCallback + ); + + ToolbarRegisterGraph( + PluginInstance, + 4, + L"I/O history", + 0, + NULL, + IoHistoryGraphMessageCallback + ); +} + +VOID ToolbarRegisterGraph( + _In_ struct _PH_PLUGIN *Plugin, + _In_ ULONG Id, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_opt_ PVOID Context, + _In_opt_ PTOOLSTATUS_GRAPH_MESSAGE_CALLBACK MessageCallback + ) +{ + PPH_TOOLBAR_GRAPH graph; + //PPH_STRING pluginName; + + graph = PhAllocateZero(sizeof(PH_TOOLBAR_GRAPH)); + graph->Plugin = Plugin; + graph->Text = Text; + graph->Flags = Flags; + graph->Context = Context; + graph->MessageCallback = MessageCallback; + graph->GraphId = 1000 + Id; + + //pluginName = PhGetPluginName(graph->Plugin); + //graph->GraphId = (PhHashStringRef(&pluginName->sr, TRUE) & 0xFFFF) + Id; + //PhDereferenceObject(pluginName); + + PhAddItemList(PhpToolbarGraphList, graph); +} + +BOOLEAN ToolbarAddGraph( + _In_ PPH_TOOLBAR_GRAPH Graph + ) +{ + if (!Graph->GraphHandle && RebarHandle && ToolStatusConfig.ToolBarEnabled) + { + UINT rebarHeight = (UINT)SendMessage(RebarHandle, RB_GETROWHEIGHT, 0, 0); + + if (Graph->GraphHandle = CreateWindow( + PH_GRAPH_CLASSNAME, + NULL, + WS_VISIBLE | WS_CHILD | WS_BORDER, + 0, + 0, + 0, + 0, + RebarHandle, + NULL, + NULL, + NULL + )) + { + Graph_SetTooltip(Graph->GraphHandle, TRUE); + PhInitializeGraphState(&Graph->GraphState); + + PhAddItemSimpleHashtable(PhpToolbarGraphHashtable, Graph->GraphHandle, Graph); + + if (!RebarBandExists(Graph->GraphId)) + RebarBandInsert(Graph->GraphId, Graph->GraphHandle, 145, rebarHeight); // height: 85 + + if (!IsWindowVisible(Graph->GraphHandle)) + ShowWindow(Graph->GraphHandle, SW_SHOW); + } + } + + return TRUE; +} + +BOOLEAN ToolbarRemoveGraph( + _In_ PPH_TOOLBAR_GRAPH Graph + ) +{ + if (RebarBandExists(Graph->GraphId)) + RebarBandRemove(Graph->GraphId); + + if (Graph->GraphHandle) + { + PhRemoveItemSimpleHashtable(PhpToolbarGraphHashtable, Graph->GraphHandle); + + PhDeleteGraphState(&Graph->GraphState); + + DestroyWindow(Graph->GraphHandle); + Graph->GraphHandle = NULL; + } + + return TRUE; +} + +VOID ToolbarCreateGraphs( + VOID + ) +{ + ToolbarGraphLoadSettings(); + + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph = PhpToolbarGraphList->Items[i]; + + if (!(graph->Flags & TOOLSTATUS_GRAPH_ENABLED)) + continue; + + ToolbarAddGraph(graph); + } +} + +VOID ToolbarUpdateGraphs( + VOID + ) +{ + if (!ToolStatusConfig.ToolBarEnabled) + return; + + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph = PhpToolbarGraphList->Items[i]; + + if (!(graph->Flags & TOOLSTATUS_GRAPH_ENABLED)) + continue; + + if (!graph->GraphHandle) + continue; + + graph->GraphState.Valid = FALSE; + graph->GraphState.TooltipIndex = ULONG_MAX; + Graph_MoveGrid(graph->GraphHandle, 1); + Graph_Draw(graph->GraphHandle); + //Graph_UpdateTooltip(graph->GraphHandle); + InvalidateRect(graph->GraphHandle, NULL, FALSE); + } +} + +BOOLEAN ToolbarUpdateGraphsInfo( + _In_ HWND WindowHandle, + _In_ LPNMHDR Header + ) +{ + PPH_TOOLBAR_GRAPH graph; + + if (graph = PhFindItemSimpleHashtable2(PhpToolbarGraphHashtable, Header->hwndFrom)) + { + if (Header->code == GCN_MOUSEEVENT) // HACK + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + + if (mouseEvent->Message == WM_RBUTTONUP) + { + ShowCustomizeMenu(WindowHandle); + } + } + + graph->MessageCallback(graph, graph->GraphHandle, &graph->GraphState, Header, NULL); + return TRUE; + } + + return FALSE; +} + +VOID ToolbarSetVisibleGraph( + _In_ PPH_TOOLBAR_GRAPH Graph, + _In_ BOOLEAN Visible + ) +{ + if (Visible) + { + Graph->Flags |= TOOLSTATUS_GRAPH_ENABLED; + ToolbarAddGraph(Graph); + } + else + { + Graph->Flags &= ~TOOLSTATUS_GRAPH_ENABLED; + ToolbarRemoveGraph(Graph); + } +} + +BOOLEAN ToolbarGraphsEnabled( + VOID + ) +{ + BOOLEAN enabled = FALSE; + + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph = PhpToolbarGraphList->Items[i]; + + if (graph->Flags & TOOLSTATUS_GRAPH_ENABLED) + { + enabled = TRUE; + break; + } + } + + return enabled; +} + +PPH_TOOLBAR_GRAPH ToolbarGraphFindById( + _In_ ULONG GraphId + ) +{ + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph = PhpToolbarGraphList->Items[i]; + + if (graph->GraphId == GraphId) + return graph; + } + + return NULL; +} + +PPH_TOOLBAR_GRAPH ToolbarGraphFindByName( + _In_ PPH_STRINGREF PluginName, + _In_ ULONG GraphId + ) +{ + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph = PhpToolbarGraphList->Items[i]; + + if (graph->Plugin) + { + PPH_STRING pluginName; + + pluginName = PhGetPluginName(graph->Plugin); + + if (graph->GraphId == GraphId && + PhEqualStringRef(PluginName, &pluginName->sr, TRUE)) + { + PhDereferenceObject(pluginName); + return graph; + } + + PhDereferenceObject(pluginName); + } + } + + return NULL; +} + +VOID ToolbarGraphCreateMenu( + _In_ PPH_EMENU ParentMenu, + _In_ ULONG MenuId + ) +{ + for (ULONG i = 0; i < PhpToolbarGraphList->Count; i++) + { + PPH_TOOLBAR_GRAPH graph; + PPH_EMENU menuItem; + + graph = PhpToolbarGraphList->Items[i]; + menuItem = PhCreateEMenuItem(0, MenuId, graph->Text, NULL, graph); + + if (graph->Flags & TOOLSTATUS_GRAPH_ENABLED) + { + menuItem->Flags |= PH_EMENU_CHECKED; + } + + if (graph->Flags & TOOLSTATUS_GRAPH_UNAVAILABLE) + { + PPH_STRING newText; + + newText = PhaConcatStrings2(graph->Text, L" (Unavailable)"); + PhModifyEMenuItem(menuItem, PH_EMENU_MODIFY_TEXT, PH_EMENU_TEXT_OWNED, + PhAllocateCopy(newText->Buffer, newText->Length + sizeof(UNICODE_NULL)), NULL); + } + + PhInsertEMenuItem(ParentMenu, menuItem, ULONG_MAX); + } +} + +// +// BEGIN copied from ProcessHacker/sysinfo.c +// + +static PPH_PROCESS_RECORD PhSipReferenceMaxCpuRecord( + _In_ LONG Index + ) +{ + LARGE_INTEGER time; + LONG maxProcessIdLong; + HANDLE maxProcessId; + + // Find the process record for the max. CPU process for the particular time. + + maxProcessIdLong = PhGetItemCircularBuffer_ULONG(SystemStatistics.MaxCpuHistory, Index); + + if (!maxProcessIdLong) + return NULL; + + // This must be treated as a signed integer to handle Interrupts correctly. + maxProcessId = LongToHandle(maxProcessIdLong); + + // Note that the time we get has its components beyond seconds cleared. + // For example: + // * At 2.5 seconds a process is started. + // * At 2.75 seconds our process provider is fired, and the process is determined + // to have 75% CPU usage, which happens to be the maximum CPU usage. + // * However the 2.75 seconds is recorded as 2 seconds due to + // RtlTimeToSecondsSince1980. + // * If we call PhFindProcessRecord, it cannot find the process because it was + // started at 2.5 seconds, not 2 seconds or older. + // + // This means we must add one second minus one tick (100ns) to the time, giving us + // 2.9999999 seconds. This will then make sure we find the process. + PhGetStatisticsTime(NULL, Index, &time); + time.QuadPart += PH_TICKS_PER_SEC - 1; + + return PhFindProcessRecord(maxProcessId, &time); +} + +static PPH_STRING PhSipGetMaxCpuString( + _In_ LONG Index + ) +{ + PPH_PROCESS_RECORD maxProcessRecord; + FLOAT maxCpuUsage; + PPH_STRING maxUsageString = NULL; + + if (maxProcessRecord = PhSipReferenceMaxCpuRecord(Index)) + { + // We found the process record, so now we construct the max. usage string. + maxCpuUsage = PhGetItemCircularBuffer_FLOAT(SystemStatistics.MaxCpuUsageHistory, Index); + + // Make sure we don't try to display the PID of DPCs or Interrupts. + if (!PH_IS_FAKE_PROCESS_ID(maxProcessRecord->ProcessId)) + { + maxUsageString = PhaFormatString( + L"\n%s (%u): %.2f%%", + maxProcessRecord->ProcessName->Buffer, + HandleToUlong(maxProcessRecord->ProcessId), + maxCpuUsage * 100 + ); + } + else + { + maxUsageString = PhaFormatString( + L"\n%s: %.2f%%", + maxProcessRecord->ProcessName->Buffer, + maxCpuUsage * 100 + ); + } + + PhDereferenceProcessRecord(maxProcessRecord); + } + + return maxUsageString; +} + +static PPH_PROCESS_RECORD PhSipReferenceMaxIoRecord( + _In_ LONG Index + ) +{ + LARGE_INTEGER time; + ULONG maxProcessId; + + // Find the process record for the max. I/O process for the particular time. + + maxProcessId = PhGetItemCircularBuffer_ULONG(SystemStatistics.MaxIoHistory, Index); + + if (!maxProcessId) + return NULL; + + // See above for the explanation. + PhGetStatisticsTime(NULL, Index, &time); + time.QuadPart += PH_TICKS_PER_SEC - 1; + + return PhFindProcessRecord(UlongToHandle(maxProcessId), &time); +} + +static PPH_STRING PhSipGetMaxIoString( + _In_ LONG Index + ) +{ + PPH_PROCESS_RECORD maxProcessRecord; + ULONG64 maxIoReadOther; + ULONG64 maxIoWrite; + + PPH_STRING maxUsageString = NULL; + + if (maxProcessRecord = PhSipReferenceMaxIoRecord(Index)) + { + // We found the process record, so now we construct the max. usage string. + maxIoReadOther = PhGetItemCircularBuffer_ULONG64(SystemStatistics.MaxIoReadOtherHistory, Index); + maxIoWrite = PhGetItemCircularBuffer_ULONG64(SystemStatistics.MaxIoWriteHistory, Index); + + if (!PH_IS_FAKE_PROCESS_ID(maxProcessRecord->ProcessId)) + { + maxUsageString = PhaFormatString( + L"\n%s (%u): R+O: %s, W: %s", + maxProcessRecord->ProcessName->Buffer, + HandleToUlong(maxProcessRecord->ProcessId), + PhaFormatSize(maxIoReadOther, ULONG_MAX)->Buffer, + PhaFormatSize(maxIoWrite, ULONG_MAX)->Buffer + ); + } + else + { + maxUsageString = PhaFormatString( + L"\n%s: R+O: %s, W: %s", + maxProcessRecord->ProcessName->Buffer, + PhaFormatSize(maxIoReadOther, ULONG_MAX)->Buffer, + PhaFormatSize(maxIoWrite, ULONG_MAX)->Buffer + ); + } + + PhDereferenceProcessRecord(maxProcessRecord); + } + + return maxUsageString; +} + +// +// END copied from ProcessHacker/sysinfo.c +// + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(CpuHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_LINE_2; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorCpuKernel"), PhGetIntegerSetting(L"ColorCpuUser")); + + if (ProcessesUpdatedCount < 3) + return; + + PhGraphStateGetDrawInfo(GraphState, getDrawInfo, SystemStatistics.CpuUserHistory->Count); + + if (!GraphState->Valid) + { + PhCopyCircularBuffer_FLOAT(SystemStatistics.CpuKernelHistory, GraphState->Data1, drawInfo->LineDataCount); + PhCopyCircularBuffer_FLOAT(SystemStatistics.CpuUserHistory, GraphState->Data2, drawInfo->LineDataCount); + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + FLOAT cpuKernel; + FLOAT cpuUser; + + cpuKernel = PhGetItemCircularBuffer_FLOAT(SystemStatistics.CpuKernelHistory, getTooltipText->Index); + cpuUser = PhGetItemCircularBuffer_FLOAT(SystemStatistics.CpuUserHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"%.2f%%%s\n%s", + (cpuKernel + cpuUser) * 100, + PhGetStringOrEmpty(PhSipGetMaxCpuString(getTooltipText->Index)), + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = PhSipReferenceMaxCpuRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(PhMainWndHandle, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(PhysicalHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPhysical"), 0); + + if (ProcessesUpdatedCount < 3) + return; + + PhGraphStateGetDrawInfo(GraphState, getDrawInfo, SystemStatistics.PhysicalHistory->Count); + + if (!GraphState->Valid) + { + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + GraphState->Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(SystemStatistics.PhysicalHistory, i); + } + + PhDivideSinglesBySingle( + GraphState->Data1, + (FLOAT)PhSystemBasicInformation.NumberOfPhysicalPages, + drawInfo->LineDataCount + ); + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + ULONG physicalUsage; + + physicalUsage = PhGetItemCircularBuffer_ULONG(SystemStatistics.PhysicalHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"Physical memory: %s\n%s", + PhaFormatSize(UInt32x32To64(physicalUsage, PAGE_SIZE), ULONG_MAX)->Buffer, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + NOTHING; + } + break; + } +} + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(CommitHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorPrivate"), 0); + + if (ProcessesUpdatedCount < 3) + return; + + PhGraphStateGetDrawInfo(GraphState, getDrawInfo, SystemStatistics.CommitHistory->Count); + + if (!GraphState->Valid) + { + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + GraphState->Data1[i] = (FLOAT)PhGetItemCircularBuffer_ULONG(SystemStatistics.CommitHistory, i); + } + + PhDivideSinglesBySingle( + GraphState->Data1, + (FLOAT)SystemStatistics.Performance->CommitLimit, + drawInfo->LineDataCount + ); + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + ULONG commitUsage; + + commitUsage = PhGetItemCircularBuffer_ULONG(SystemStatistics.CommitHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"Commit charge: %s\n%s", + PhaFormatSize(UInt32x32To64(commitUsage, PAGE_SIZE), ULONG_MAX)->Buffer, + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + NOTHING; + } + break; + } +} + +TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(IoHistoryGraphMessageCallback) +{ + switch (Header->code) + { + case GCN_GETDRAWINFO: + { + PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)Header; + PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo; + + drawInfo->Flags = PH_GRAPH_USE_GRID_X | PH_GRAPH_USE_LINE_2; + PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite")); + + if (ProcessesUpdatedCount < 3) + return; + + PhGraphStateGetDrawInfo(GraphState, getDrawInfo, SystemStatistics.IoReadHistory->Count); + + if (!GraphState->Valid) + { + FLOAT max = 1024 * 1024; // minimum scaling of 1 MB. + + for (ULONG i = 0; i < drawInfo->LineDataCount; i++) + { + GraphState->Data1[i] = + (FLOAT)PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoReadHistory, i) + + (FLOAT)PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoOtherHistory, i); + GraphState->Data2[i] = + (FLOAT)PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoWriteHistory, i); + + if (max < GraphState->Data1[i] + GraphState->Data2[i]) + max = GraphState->Data1[i] + GraphState->Data2[i]; + } + + PhDivideSinglesBySingle(GraphState->Data1, max, drawInfo->LineDataCount); + PhDivideSinglesBySingle(GraphState->Data2, max, drawInfo->LineDataCount); + + GraphState->Valid = TRUE; + } + } + break; + case GCN_GETTOOLTIPTEXT: + { + PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)Header; + + if (getTooltipText->Index < getTooltipText->TotalCount) + { + if (GraphState->TooltipIndex != getTooltipText->Index) + { + ULONG64 ioRead; + ULONG64 ioWrite; + ULONG64 ioOther; + + ioRead = PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoReadHistory, getTooltipText->Index); + ioWrite = PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoWriteHistory, getTooltipText->Index); + ioOther = PhGetItemCircularBuffer_ULONG64(SystemStatistics.IoOtherHistory, getTooltipText->Index); + + PhMoveReference(&GraphState->TooltipText, PhFormatString( + L"R: %s\nW: %s\nO: %s%s\n%s", + PhaFormatSize(ioRead, ULONG_MAX)->Buffer, + PhaFormatSize(ioWrite, ULONG_MAX)->Buffer, + PhaFormatSize(ioOther, ULONG_MAX)->Buffer, + PhGetStringOrEmpty(PhSipGetMaxIoString(getTooltipText->Index)), + PH_AUTO_T(PH_STRING, PhGetStatisticsTimeString(NULL, getTooltipText->Index))->Buffer + )); + } + + getTooltipText->Text = PhGetStringRef(GraphState->TooltipText); + } + } + break; + case GCN_MOUSEEVENT: + { + PPH_GRAPH_MOUSEEVENT mouseEvent = (PPH_GRAPH_MOUSEEVENT)Header; + PPH_PROCESS_RECORD record = NULL; + + if (mouseEvent->Message == WM_LBUTTONDBLCLK && mouseEvent->Index < mouseEvent->TotalCount) + { + record = PhSipReferenceMaxIoRecord(mouseEvent->Index); + } + + if (record) + { + PhShowProcessRecordDialog(PhMainWndHandle, record); + PhDereferenceProcessRecord(record); + } + } + break; + } +} diff --git a/plugins/ToolStatus/main.c b/plugins/ToolStatus/main.c index 6f3c1f8c530d..22f2d1753d48 100644 --- a/plugins/ToolStatus/main.c +++ b/plugins/ToolStatus/main.c @@ -1,1445 +1,1451 @@ -/* - * Process Hacker ToolStatus - - * main program - * - * Copyright (C) 2011-2016 dmex - * Copyright (C) 2010-2016 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" - -PPH_STRING GetSearchboxText( - VOID - ); - -VOID RegisterTabSearch( - _In_ INT TabIndex, - _In_ PWSTR BannerText - ); - -PTOOLSTATUS_TAB_INFO RegisterTabInfo( - _In_ INT TabIndex - ); - -TOOLSTATUS_CONFIG ToolStatusConfig = { 0 }; -HWND ProcessTreeNewHandle = NULL; -HWND ServiceTreeNewHandle = NULL; -HWND NetworkTreeNewHandle = NULL; -INT SelectedTabIndex; -BOOLEAN UpdateAutomatically = TRUE; -BOOLEAN UpdateGraphs = TRUE; -TOOLBAR_DISPLAY_STYLE DisplayStyle = TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT; -SEARCHBOX_DISPLAY_MODE SearchBoxDisplayMode = SEARCHBOX_DISPLAY_MODE_ALWAYSSHOW; -REBAR_DISPLAY_LOCATION RebarDisplayLocation = REBAR_DISPLAY_LOCATION_TOP; -HWND RebarHandle = NULL; -HWND ToolBarHandle = NULL; -HWND SearchboxHandle = NULL; -HMENU MainMenu = NULL; -HACCEL AcceleratorTable = NULL; -PPH_STRING SearchboxText = NULL; -PH_PLUGIN_SYSTEM_STATISTICS SystemStatistics = { 0 }; -PH_CALLBACK_DECLARE(SearchChangedEvent); -PPH_HASHTABLE TabInfoHashtable; -PPH_TN_FILTER_ENTRY ProcessTreeFilterEntry = NULL; -PPH_TN_FILTER_ENTRY ServiceTreeFilterEntry = NULL; -PPH_TN_FILTER_ENTRY NetworkTreeFilterEntry = NULL; -PPH_PLUGIN PluginInstance = NULL; -TOOLSTATUS_INTERFACE PluginInterface = -{ - TOOLSTATUS_INTERFACE_VERSION, - GetSearchboxText, - WordMatchStringRef, - RegisterTabSearch, - &SearchChangedEvent, - RegisterTabInfo -}; - -static ULONG TargetingMode = 0; -static BOOLEAN TargetingWindow = FALSE; -static BOOLEAN TargetingCurrentWindowDraw = FALSE; -static BOOLEAN TargetingCompleted = FALSE; -static HWND TargetingCurrentWindow = NULL; -static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -static PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -static PH_CALLBACK_REGISTRATION LayoutPaddingCallbackRegistration; -static PH_CALLBACK_REGISTRATION TabPageCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; - -PPH_STRING GetSearchboxText( - VOID - ) -{ - return SearchboxText; -} - -VOID NTAPI ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ProcessesUpdatedCount++; - - if (ProcessesUpdatedCount < 2) - return; - - PhPluginGetSystemStatistics(&SystemStatistics); - - if (UpdateGraphs) - ToolbarUpdateGraphs(); - - if (ToolStatusConfig.StatusBarEnabled) - StatusBarUpdate(FALSE); -} - -VOID NTAPI TreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - *(HWND *)Context = ((PPH_PLUGIN_TREENEW_INFORMATION)Parameter)->TreeNewHandle; -} - -VOID RegisterTabSearch( - _In_ INT TabIndex, - _In_ PWSTR BannerText - ) -{ - PTOOLSTATUS_TAB_INFO tabInfo; - - tabInfo = RegisterTabInfo(TabIndex); - tabInfo->BannerText = BannerText; -} - -PTOOLSTATUS_TAB_INFO RegisterTabInfo( - _In_ INT TabIndex - ) -{ - PTOOLSTATUS_TAB_INFO tabInfoCopy; - PVOID *entry; - - tabInfoCopy = PhCreateAlloc(sizeof(TOOLSTATUS_TAB_INFO)); - memset(tabInfoCopy, 0, sizeof(TOOLSTATUS_TAB_INFO)); - - if (!PhAddItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex), tabInfoCopy)) - { - PhClearReference(&tabInfoCopy); - - if (entry = PhFindItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex))) - tabInfoCopy = *entry; - } - - return tabInfoCopy; -} - -PTOOLSTATUS_TAB_INFO FindTabInfo( - _In_ INT TabIndex - ) -{ - PVOID *entry; - - if (entry = PhFindItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex))) - return *entry; - - return NULL; -} - -HWND GetCurrentTreeNewHandle( - VOID - ) -{ - HWND treeNewHandle = NULL; - - switch (SelectedTabIndex) - { - case 0: - treeNewHandle = ProcessTreeNewHandle; - break; - case 1: - treeNewHandle = ServiceTreeNewHandle; - break; - case 2: - treeNewHandle = NetworkTreeNewHandle; - break; - default: - { - PTOOLSTATUS_TAB_INFO tabInfo; - - if ((tabInfo = FindTabInfo(SelectedTabIndex)) && tabInfo->GetTreeNewHandle) - { - treeNewHandle = tabInfo->GetTreeNewHandle(); - } - } - break; - } - - return treeNewHandle; -} - -VOID ShowCustomizeMenu( - VOID - ) -{ - POINT cursorPos; - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - - GetCursorPos(&cursorPos); - - menu = PhCreateEMenu(); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_MENU, L"Main menu (auto-hide)", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_SEARCHBOX, L"Search box", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_CPU_GRAPH, L"CPU history", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_IO_GRAPH, L"I/O history", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_MEMORY_GRAPH, L"Physical memory history", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_COMMIT_GRAPH, L"Commit charge history", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_TOOLBAR_LOCKUNLOCK, L"Lock the toolbar", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_TOOLBAR_CUSTOMIZE, L"Customize...", NULL, NULL), -1); - - if (ToolStatusConfig.AutoHideMenu) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_MENU, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - if (ToolStatusConfig.SearchBoxEnabled) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_SEARCHBOX, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - if (ToolStatusConfig.CpuGraphEnabled) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_CPU_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - if (ToolStatusConfig.MemGraphEnabled) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_MEMORY_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - if (ToolStatusConfig.CommitGraphEnabled) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_COMMIT_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - if (ToolStatusConfig.IoGraphEnabled) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_IO_GRAPH, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - if (ToolStatusConfig.ToolBarLocked) - { - PhSetFlagsEMenuItem(menu, COMMAND_ID_TOOLBAR_LOCKUNLOCK, PH_EMENU_CHECKED, PH_EMENU_CHECKED); - } - - selectedItem = PhShowEMenu( - menu, - PhMainWndHandle, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - cursorPos.x, - cursorPos.y - ); - - if (selectedItem && selectedItem->Id != -1) - { - switch (selectedItem->Id) - { - case COMMAND_ID_ENABLE_MENU: - { - ToolStatusConfig.AutoHideMenu = !ToolStatusConfig.AutoHideMenu; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - if (ToolStatusConfig.AutoHideMenu) - { - SetMenu(PhMainWndHandle, NULL); - } - else - { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); - } - } - break; - case COMMAND_ID_ENABLE_SEARCHBOX: - { - ToolStatusConfig.SearchBoxEnabled = !ToolStatusConfig.SearchBoxEnabled; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - ReBarSaveLayoutSettings(); - - if (ToolStatusConfig.SearchBoxEnabled) - { - // Adding the Searchbox makes it focused, - // reset the focus back to the main window. - SetFocus(PhMainWndHandle); - } - } - break; - case COMMAND_ID_ENABLE_CPU_GRAPH: - { - ToolStatusConfig.CpuGraphEnabled = !ToolStatusConfig.CpuGraphEnabled; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - ReBarSaveLayoutSettings(); - } - break; - case COMMAND_ID_ENABLE_MEMORY_GRAPH: - { - ToolStatusConfig.MemGraphEnabled = !ToolStatusConfig.MemGraphEnabled; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - ReBarSaveLayoutSettings(); - } - break; - case COMMAND_ID_ENABLE_COMMIT_GRAPH: - { - ToolStatusConfig.CommitGraphEnabled = !ToolStatusConfig.CommitGraphEnabled; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - ReBarSaveLayoutSettings(); - } - break; - case COMMAND_ID_ENABLE_IO_GRAPH: - { - ToolStatusConfig.IoGraphEnabled = !ToolStatusConfig.IoGraphEnabled; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - ReBarSaveLayoutSettings(); - } - break; - case COMMAND_ID_TOOLBAR_LOCKUNLOCK: - { - UINT bandCount; - UINT bandIndex; - - bandCount = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0); - - for (bandIndex = 0; bandIndex < bandCount; bandIndex++) - { - REBARBANDINFO rebarBandInfo = - { - sizeof(REBARBANDINFO), - RBBIM_STYLE - }; - - SendMessage(RebarHandle, RB_GETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo); - - if (!(rebarBandInfo.fStyle & RBBS_GRIPPERALWAYS)) - { - // Removing the RBBS_NOGRIPPER style doesn't remove the gripper padding, - // So we toggle the RBBS_GRIPPERALWAYS style to make the Toolbar remove the padding. - - rebarBandInfo.fStyle |= RBBS_GRIPPERALWAYS; - - SendMessage(RebarHandle, RB_SETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo); - - rebarBandInfo.fStyle &= ~RBBS_GRIPPERALWAYS; - } - - if (rebarBandInfo.fStyle & RBBS_NOGRIPPER) - { - rebarBandInfo.fStyle &= ~RBBS_NOGRIPPER; - } - else - { - rebarBandInfo.fStyle |= RBBS_NOGRIPPER; - } - - SendMessage(RebarHandle, RB_SETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo); - } - - ToolStatusConfig.ToolBarLocked = !ToolStatusConfig.ToolBarLocked; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - } - break; - case COMMAND_ID_TOOLBAR_CUSTOMIZE: - { - ToolBarShowCustomizeDialog(); - } - break; - } - } - - PhDestroyEMenu(menu); -} - -VOID NTAPI TabPageUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - INT tabIndex = PtrToInt(Parameter); - - SelectedTabIndex = tabIndex; - - if (!SearchboxHandle) - return; - - switch (tabIndex) - { - case 0: - Edit_SetCueBannerText(SearchboxHandle, L"Search Processes (Ctrl+K)"); - break; - case 1: - Edit_SetCueBannerText(SearchboxHandle, L"Search Services (Ctrl+K)"); - break; - case 2: - Edit_SetCueBannerText(SearchboxHandle, L"Search Network (Ctrl+K)"); - break; - default: - { - PTOOLSTATUS_TAB_INFO tabInfo; - - if ((tabInfo = FindTabInfo(tabIndex)) && tabInfo->BannerText) - { - Edit_SetCueBannerText(SearchboxHandle, PhaConcatStrings2(tabInfo->BannerText, L" (Ctrl+K)")->Buffer); - } - else - { - // Disable the textbox if we're on an unsupported tab. - Edit_SetCueBannerText(SearchboxHandle, L"Search disabled"); - } - } - break; - } -} - -VOID NTAPI LayoutPaddingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_LAYOUT_PADDING_DATA layoutPadding = Parameter; - - if (RebarHandle && ToolStatusConfig.ToolBarEnabled) - { - RECT rebarRect; - //RECT clientRect; - //INT x, y, cx, cy; - - SendMessage(RebarHandle, WM_SIZE, 0, 0); - - // TODO: GetClientRect with PhMainWndHandle causes crash. - //GetClientRect(PhMainWndHandle, &clientRect); - GetClientRect(RebarHandle, &rebarRect); - - // Adjust the PH client area and exclude the rebar width. - layoutPadding->Padding.top += rebarRect.bottom; - - // TODO: Replace CCS_TOP with CCS_NOPARENTALIGN and use below code - //switch (RebarDisplayLocation) - //{ - //case RebarLocationLeft: - // { - // //x = 0; - // //y = 0; - // //cx = rebarRect.right - rebarRect.left; - // //cy = clientRect.bottom - clientRect.top; - // } - // break; - //case RebarLocationTop: - // { - // //x = 0; - // //y = 0; - // //cx = clientRect.right - clientRect.left; - // //cy = clientRect.bottom - clientRect.top; - // - // // Adjust the PH client area and exclude the rebar height. - // //layoutPadding->Padding.top += rebarRect.bottom; - // } - // break; - //case RebarLocationRight: - // { - // //x = clientRect.right - (rebarRect.right - rebarRect.left); - // //y = 0; - // //cx = rebarRect.right - rebarRect.left; - // //cy = clientRect.bottom - clientRect.top; - // } - // break; - //case RebarLocationBottom: - // { - // //x = 0; - // //y = clientRect.bottom - (rebarRect.bottom - rebarRect.top) - (StatusBarEnabled ? rebarRect.bottom + 1 : 0); - // //cx = clientRect.right - clientRect.left; - // //cy = rebarRect.bottom - rebarRect.top; - // - // // Adjust the PH client area and exclude the rebar width. - // //layoutPadding->Padding.bottom += rebarRect.bottom; - // } - // break; - //} - //MoveWindow(RebarHandle, x, y, cx, cy, TRUE); - - //if (SearchBoxDisplayStyle == SearchBoxDisplayAutoHide) - //{ - // static BOOLEAN isSearchboxVisible = FALSE; - // SIZE idealWidth; - // - // // Query the Toolbar ideal width - // SendMessage(ToolBarHandle, TB_GETIDEALSIZE, FALSE, (LPARAM)&idealWidth); - // - // // Hide the Searcbox band if the window size is too small... - // if (rebarRect.right > idealWidth.cx) - // { - // if (isSearchboxVisible) - // { - // if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - // RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, 180, 20); - // - // isSearchboxVisible = FALSE; - // } - // } - // else - // { - // if (!isSearchboxVisible) - // { - // if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - // RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); - // - // isSearchboxVisible = TRUE; - // } - // } - //} - } - - if (StatusBarHandle && ToolStatusConfig.StatusBarEnabled) - { - RECT statusBarRect; - - SendMessage(StatusBarHandle, WM_SIZE, 0, 0); - - GetClientRect(StatusBarHandle, &statusBarRect); - - // Adjust the PH client area and exclude the StatusBar width. - layoutPadding->Padding.bottom += statusBarRect.bottom; - - //InvalidateRect(StatusBarHandle, NULL, TRUE); - } -} - -BOOLEAN NTAPI MessageLoopFilter( - _In_ PMSG Message, - _In_ PVOID Context - ) -{ - if ( - Message->hwnd == PhMainWndHandle || - IsChild(PhMainWndHandle, Message->hwnd) - ) - { - if (TranslateAccelerator(PhMainWndHandle, AcceleratorTable, Message)) - return TRUE; - - if (Message->message == WM_SYSCHAR && ToolStatusConfig.AutoHideMenu && !GetMenu(PhMainWndHandle)) - { - ULONG key = (ULONG)Message->wParam; - - if (key == 'h' || key == 'v' || key == 't' || key == 'u' || key == 'e') - { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); - SendMessage(PhMainWndHandle, WM_SYSCHAR, Message->wParam, Message->lParam); - return TRUE; - } - } - } - - return FALSE; -} - -VOID DrawWindowBorderForTargeting( - _In_ HWND hWnd - ) -{ - RECT rect; - HDC hdc; - - GetWindowRect(hWnd, &rect); - hdc = GetWindowDC(hWnd); - - if (hdc) - { - INT penWidth; - INT oldDc; - HPEN pen; - HBRUSH brush; - - penWidth = GetSystemMetrics(SM_CXBORDER) * 3; - oldDc = SaveDC(hdc); - - // Get an inversion effect. - SetROP2(hdc, R2_NOT); - - pen = CreatePen(PS_INSIDEFRAME, penWidth, RGB(0x00, 0x00, 0x00)); - SelectPen(hdc, pen); - - brush = GetStockBrush(NULL_BRUSH); - SelectBrush(hdc, brush); - - // Draw the rectangle. - Rectangle(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top); - - // Cleanup. - DeleteObject(pen); - - RestoreDC(hdc, oldDc); - ReleaseDC(hWnd, hdc); - } -} - -LRESULT CALLBACK MainWndSubclassProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - switch (uMsg) - { - case WM_COMMAND: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case EN_CHANGE: - { - PPH_STRING newSearchboxText; - - if (!SearchboxHandle) - break; - - if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle) - break; - - newSearchboxText = PH_AUTO(PhGetWindowText(SearchboxHandle)); - - if (!PhEqualString(SearchboxText, newSearchboxText, FALSE)) - { - // Cache the current search text for our callback. - PhSwapReference(&SearchboxText, newSearchboxText); - - if (!PhIsNullOrEmptyString(SearchboxText)) - { - // Expand the nodes to ensure that they will be visible to the user. - PhExpandAllProcessNodes(TRUE); - PhDeselectAllProcessNodes(); - PhDeselectAllServiceNodes(); - } - - PhApplyTreeNewFilters(PhGetFilterSupportProcessTreeList()); - PhApplyTreeNewFilters(PhGetFilterSupportServiceTreeList()); - PhApplyTreeNewFilters(PhGetFilterSupportNetworkTreeList()); - - PhInvokeCallback(&SearchChangedEvent, SearchboxText); - } - - goto DefaultWndProc; - } - break; - case EN_KILLFOCUS: - { - if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle) - break; - - if (SearchBoxDisplayMode != SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) - break; - - if (SearchboxText->Length == 0) - { - if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); - } - - goto DefaultWndProc; - } - break; - } - - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case PHAPP_ID_ESC_EXIT: - { - // If we're targeting and the user presses the Esc key, cancel the targeting. - // We also make sure the window doesn't get closed, by filtering out the message. - if (TargetingWindow) - { - TargetingWindow = FALSE; - ReleaseCapture(); - - goto DefaultWndProc; - } - } - break; - case ID_SEARCH: - { - // handle keybind Ctrl + K - if (SearchboxHandle && ToolStatusConfig.SearchBoxEnabled) - { - if (SearchBoxDisplayMode == SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) - { - if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PhMultiplyDivide(180, PhGlobalDpi, 96), 22); - - if (!IsWindowVisible(SearchboxHandle)) - ShowWindow(SearchboxHandle, SW_SHOW); - } - - SetFocus(SearchboxHandle); - Edit_SetSel(SearchboxHandle, 0, -1); - } - - goto DefaultWndProc; - } - break; - case PHAPP_ID_VIEW_ALWAYSONTOP: - { - // Let Process Hacker perform the default processing. - DefSubclassProc(hWnd, uMsg, wParam, lParam); - - // Query the settings. - BOOLEAN isAlwaysOnTopEnabled = (BOOLEAN)PhGetIntegerSetting(L"MainWindowAlwaysOnTop"); - - // Set the pressed button state. - SendMessage(ToolBarHandle, TB_PRESSBUTTON, (WPARAM)PHAPP_ID_VIEW_ALWAYSONTOP, (LPARAM)(MAKELONG(isAlwaysOnTopEnabled, 0))); - - goto DefaultWndProc; - } - break; - case PHAPP_ID_UPDATEINTERVAL_FAST: - case PHAPP_ID_UPDATEINTERVAL_NORMAL: - case PHAPP_ID_UPDATEINTERVAL_BELOWNORMAL: - case PHAPP_ID_UPDATEINTERVAL_SLOW: - case PHAPP_ID_UPDATEINTERVAL_VERYSLOW: - { - // Let Process Hacker perform the default processing. - DefSubclassProc(hWnd, uMsg, wParam, lParam); - - StatusBarUpdate(TRUE); - - goto DefaultWndProc; - } - break; - case PHAPP_ID_VIEW_UPDATEAUTOMATICALLY: - { - UpdateAutomatically = !UpdateAutomatically; - - StatusBarUpdate(TRUE); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR hdr = (LPNMHDR)lParam; - - if (RebarHandle && hdr->hwndFrom == RebarHandle) - { - switch (hdr->code) - { - case RBN_HEIGHTCHANGE: - { - // Invoke the LayoutPaddingCallback. - SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); - } - break; - case RBN_CHEVRONPUSHED: - { - LPNMREBARCHEVRON rebar; - ULONG index = 0; - ULONG buttonCount = 0; - RECT toolbarRect; - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - - rebar = (LPNMREBARCHEVRON)lParam; - menu = PhCreateEMenu(); - - GetClientRect(ToolBarHandle, &toolbarRect); - - buttonCount = (ULONG)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); - - for (index = 0; index < buttonCount; index++) - { - RECT buttonRect; - TBBUTTONINFO buttonInfo = - { - sizeof(TBBUTTONINFO), - TBIF_BYINDEX | TBIF_STYLE | TBIF_COMMAND | TBIF_IMAGE - }; - - // Get the client coordinates of the button. - if (SendMessage(ToolBarHandle, TB_GETITEMRECT, index, (LPARAM)&buttonRect) == -1) - break; - - if (buttonRect.right <= toolbarRect.right) - continue; - - // Get extended button information. - if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1) - break; - - if (buttonInfo.fsStyle == BTNS_SEP) - { - // Add separators to menu. - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - } - else - { - PPH_EMENU_ITEM menuItem; - - if (PhGetOwnTokenAttributes().Elevated && buttonInfo.idCommand == PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES) - { - // Don't show the 'Show Details for All Processes' button in the - // dropdown menu when we're elevated. - continue; - } - - // Add buttons to menu. - menuItem = PhCreateEMenuItem(0, buttonInfo.idCommand, ToolbarGetText(buttonInfo.idCommand), NULL, NULL); - - menuItem->Flags |= PH_EMENU_BITMAP_OWNED; - menuItem->Bitmap = ToolbarGetImage(buttonInfo.idCommand); - - switch (buttonInfo.idCommand) - { - case PHAPP_ID_VIEW_ALWAYSONTOP: - { - // Set the pressed state. - if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) - menuItem->Flags |= PH_EMENU_CHECKED; - } - break; - case TIDC_FINDWINDOW: - case TIDC_FINDWINDOWTHREAD: - case TIDC_FINDWINDOWKILL: - { - // Note: These buttons are incompatible with menus. - menuItem->Flags |= PH_EMENU_DISABLED; - } - break; - case TIDC_POWERMENUDROPDOWN: - { - // Create the sub-menu... - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOCK, L"&Lock", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOGOFF, L"Log o&ff", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SLEEP, L"&Sleep", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_HIBERNATE, L"&Hibernate", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTART, L"R&estart", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS, L"Restart to boot &options", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWN, L"Shu&t down", NULL, NULL), -1); - PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID, L"H&ybrid shut down", NULL, NULL), -1); - } - break; - } - - PhInsertEMenuItem(menu, menuItem, -1); - } - } - - MapWindowPoints(RebarHandle, NULL, (LPPOINT)&rebar->rc, 2); - - selectedItem = PhShowEMenu( - menu, - hWnd, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - rebar->rc.left, - rebar->rc.bottom - ); - - if (selectedItem && selectedItem->Id != -1) - { - SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); - } - - PhDestroyEMenu(menu); - } - break; - case RBN_LAYOUTCHANGED: - { - ReBarSaveLayoutSettings(); - } - break; - } - - goto DefaultWndProc; - } - else if (ToolBarHandle && hdr->hwndFrom == ToolBarHandle) - { - switch (hdr->code) - { - case TBN_GETDISPINFO: - { - LPNMTBDISPINFO toolbarDisplayInfo = (LPNMTBDISPINFO)lParam; - - if (toolbarDisplayInfo->dwMask & TBNF_IMAGE) - { - BOOLEAN found = FALSE; - - // Try to find the cached bitmap index. - // NOTE: The TBNF_DI_SETITEM flag below will cache the index so we only get called once. - // However, when adding buttons from the customize dialog we get called a second time, - // so we cache the index in our ToolbarButtons array to prevent ToolBarImageList from growing. - for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++) - { - if (ToolbarButtons[i].idCommand == toolbarDisplayInfo->idCommand) - { - if (ToolbarButtons[i].iBitmap != I_IMAGECALLBACK) - { - found = TRUE; - - // Cache the bitmap index. - toolbarDisplayInfo->dwMask |= TBNF_DI_SETITEM; - // Set the bitmap index. - toolbarDisplayInfo->iImage = ToolbarButtons[i].iBitmap; - } - break; - } - } - - if (!found) - { - // We didn't find a cached bitmap index... - // Load the button bitmap and cache the index. - for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++) - { - if (ToolbarButtons[i].idCommand == toolbarDisplayInfo->idCommand) - { - HBITMAP buttonImage; - - buttonImage = ToolbarGetImage(toolbarDisplayInfo->idCommand); - - // Cache the bitmap index. - toolbarDisplayInfo->dwMask |= TBNF_DI_SETITEM; - // Add the image, cache the value in the ToolbarButtons array, set the bitmap index. - toolbarDisplayInfo->iImage = ToolbarButtons[i].iBitmap = ImageList_Add( - ToolBarImageList, - buttonImage, - NULL - ); - - DeleteObject(buttonImage); - break; - } - } - } - } - } - break; - case TBN_DROPDOWN: - { - LPNMTOOLBAR toolbar = (LPNMTOOLBAR)hdr; - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - - if (toolbar->iItem != TIDC_POWERMENUDROPDOWN) - break; - - menu = PhCreateEMenu(); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOCK, L"&Lock", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOGOFF, L"Log o&ff", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SLEEP, L"&Sleep", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_HIBERNATE, L"&Hibernate", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTART, L"R&estart", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS, L"Restart to boot &options", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWN, L"Shu&t down", NULL, NULL), -1); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID, L"H&ybrid shut down", NULL, NULL), -1); - - MapWindowPoints(ToolBarHandle, NULL, (LPPOINT)&toolbar->rcButton, 2); - - selectedItem = PhShowEMenu( - menu, - hWnd, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_TOP, - toolbar->rcButton.left, - toolbar->rcButton.bottom - ); - - if (selectedItem && selectedItem->Id != -1) - { - SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); - } - - PhDestroyEMenu(menu); - } - return TBDDRET_DEFAULT; - case NM_LDOWN: - { - LPNMCLICK toolbar = (LPNMCLICK)hdr; - ULONG id = (ULONG)toolbar->dwItemSpec; - - if (id == -1) - break; - - if (id == TIDC_FINDWINDOW || id == TIDC_FINDWINDOWTHREAD || id == TIDC_FINDWINDOWKILL) - { - // Direct all mouse events to this window. - SetCapture(hWnd); - - // Set the cursor. - SetCursor(LoadCursor(NULL, IDC_CROSS)); - - // Send the window to the bottom. - SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - - TargetingWindow = TRUE; - TargetingCurrentWindow = NULL; - TargetingCurrentWindowDraw = FALSE; - TargetingCompleted = FALSE; - TargetingMode = id; - - SendMessage(hWnd, WM_MOUSEMOVE, 0, 0); - } - } - break; - case NM_RCLICK: - { - ShowCustomizeMenu(); - } - break; - } - - goto DefaultWndProc; - } - else if (StatusBarHandle && hdr->hwndFrom == StatusBarHandle) - { - switch (hdr->code) - { - case NM_RCLICK: - { - StatusBarShowMenu(); - } - break; - } - - goto DefaultWndProc; - } - else if ( - CpuGraphHandle && hdr->hwndFrom == CpuGraphHandle || - MemGraphHandle && hdr->hwndFrom == MemGraphHandle || - CommitGraphHandle && hdr->hwndFrom == CommitGraphHandle || - IoGraphHandle && hdr->hwndFrom == IoGraphHandle - ) - { - ToolbarUpdateGraphsInfo(hdr); - - goto DefaultWndProc; - } - } - break; - case WM_MOUSEMOVE: - { - if (TargetingWindow) - { - POINT cursorPos; - HWND windowOverMouse; - ULONG processId; - ULONG threadId; - - GetCursorPos(&cursorPos); - windowOverMouse = WindowFromPoint(cursorPos); - - if (TargetingCurrentWindow != windowOverMouse) - { - if (TargetingCurrentWindow && TargetingCurrentWindowDraw) - { - // Invert the old border (to remove it). - DrawWindowBorderForTargeting(TargetingCurrentWindow); - } - - if (windowOverMouse) - { - threadId = GetWindowThreadProcessId(windowOverMouse, &processId); - - // Draw a rectangle over the current window (but not if it's one of our own). - if (UlongToHandle(processId) != NtCurrentProcessId()) - { - DrawWindowBorderForTargeting(windowOverMouse); - TargetingCurrentWindowDraw = TRUE; - } - else - { - TargetingCurrentWindowDraw = FALSE; - } - } - - TargetingCurrentWindow = windowOverMouse; - } - - goto DefaultWndProc; - } - } - break; - case WM_LBUTTONUP: - { - if (TargetingWindow) - { - ULONG processId; - ULONG threadId; - - TargetingCompleted = TRUE; - - // Reset the original cursor. - SetCursor(LoadCursor(NULL, IDC_ARROW)); - - // Bring the window back to the top, and preserve the Always on Top setting. - SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, - 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - - TargetingWindow = FALSE; - ReleaseCapture(); - - if (TargetingCurrentWindow) - { - if (TargetingCurrentWindowDraw) - { - // Remove the border on the window we found. - DrawWindowBorderForTargeting(TargetingCurrentWindow); - } - - if (ToolStatusConfig.ResolveGhostWindows) - { - // This is an undocumented function exported by user32.dll that - // retrieves the hung window represented by a ghost window. - static HWND (WINAPI *HungWindowFromGhostWindow_I)( - _In_ HWND hWnd - ); - - if (!HungWindowFromGhostWindow_I) - HungWindowFromGhostWindow_I = PhGetModuleProcAddress(L"user32.dll", "HungWindowFromGhostWindow"); - - if (HungWindowFromGhostWindow_I) - { - HWND hungWindow = HungWindowFromGhostWindow_I(TargetingCurrentWindow); - - // The call will have failed if the window wasn't actually a ghost - // window. - if (hungWindow) - TargetingCurrentWindow = hungWindow; - } - } - - threadId = GetWindowThreadProcessId(TargetingCurrentWindow, &processId); - - if (threadId && processId && UlongToHandle(processId) != NtCurrentProcessId()) - { - PPH_PROCESS_NODE processNode; - - processNode = PhFindProcessNode(UlongToHandle(processId)); - - if (processNode) - { - ProcessHacker_SelectTabPage(hWnd, 0); - ProcessHacker_SelectProcessNode(hWnd, processNode); - } - - switch (TargetingMode) - { - case TIDC_FINDWINDOWTHREAD: - { - PPH_PROCESS_PROPCONTEXT propContext; - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(UlongToHandle(processId))) - { - if (propContext = PhCreateProcessPropContext(hWnd, processItem)) - { - PhSetSelectThreadIdProcessPropContext(propContext, UlongToHandle(threadId)); - PhShowProcessProperties(propContext); - PhDereferenceObject(propContext); - } - - PhDereferenceObject(processItem); - } - else - { - PhShowError(hWnd, L"The process (PID %lu) does not exist.", processId); - } - } - break; - case TIDC_FINDWINDOWKILL: - { - PPH_PROCESS_ITEM processItem; - - if (processItem = PhReferenceProcessItem(UlongToHandle(processId))) - { - PhUiTerminateProcesses(hWnd, &processItem, 1); - PhDereferenceObject(processItem); - } - else - { - PhShowError(hWnd, L"The process (PID %lu) does not exist.", processId); - } - } - break; - } - } - } - - goto DefaultWndProc; - } - } - break; - case WM_CAPTURECHANGED: - { - if (!TargetingCompleted) - { - // The user cancelled the targeting, probably by pressing the Esc key. - - // Remove the border on the currently selected window. - if (TargetingCurrentWindow) - { - if (TargetingCurrentWindowDraw) - { - // Remove the border on the window we found. - DrawWindowBorderForTargeting(TargetingCurrentWindow); - } - } - - SetWindowPos(PhMainWndHandle, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, - 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - - TargetingCompleted = TRUE; - } - } - break; - case WM_SIZE: - // Resize PH main window client-area. - ProcessHacker_InvalidateLayoutPadding(hWnd); - break; - case WM_SETTINGCHANGE: - // Forward to the Searchbox so we can reinitialize the settings... - SendMessage(SearchboxHandle, WM_SETTINGCHANGE, 0, 0); - break; - case WM_SHOWWINDOW: - { - UpdateGraphs = (BOOLEAN)wParam; - } - break; - case WM_SYSCOMMAND: - { - if ((wParam & 0xFFF0) == SC_KEYMENU && lParam == 0) - { - if (!ToolStatusConfig.AutoHideMenu) - break; - - if (GetMenu(PhMainWndHandle)) - { - SetMenu(PhMainWndHandle, NULL); - } - else - { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); - } - } - else if ((wParam & 0xFFF0) == SC_MINIMIZE) - { - UpdateGraphs = FALSE; - } - else if ((wParam & 0xFFF0) == SC_RESTORE) - { - UpdateGraphs = TRUE; - } - } - break; - case WM_EXITMENULOOP: - { - if (!ToolStatusConfig.AutoHideMenu) - break; - - if (GetMenu(PhMainWndHandle)) - { - SetMenu(PhMainWndHandle, NULL); - } - } - break; - } - - return DefSubclassProc(hWnd, uMsg, wParam, lParam); - -DefaultWndProc: - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - -VOID NTAPI MainWindowShowingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PhRegisterMessageLoopFilter(MessageLoopFilter, NULL); - PhRegisterCallback( - ProcessHacker_GetCallbackLayoutPadding(PhMainWndHandle), - LayoutPaddingCallback, - NULL, - &LayoutPaddingCallbackRegistration - ); - SetWindowSubclass(PhMainWndHandle, MainWndSubclassProc, 0, 0); - - ToolbarLoadSettings(); - ReBarLoadLayoutSettings(); - StatusBarLoadSettings(); - - MainMenu = GetMenu(PhMainWndHandle); - if (ToolStatusConfig.AutoHideMenu) - { - SetMenu(PhMainWndHandle, NULL); - } -} - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ToolStatusConfig.Flags = PhGetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG); - DisplayStyle = (TOOLBAR_DISPLAY_STYLE)PhGetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE); - SearchBoxDisplayMode = (SEARCHBOX_DISPLAY_MODE)PhGetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE); - UpdateGraphs = !PhGetIntegerSetting(L"StartHidden"); -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ShowOptionsDialog(Parameter); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { IntegerSettingType, SETTING_NAME_TOOLSTATUS_CONFIG, L"1F" }, - { IntegerSettingType, SETTING_NAME_TOOLBAR_THEME, L"0" }, - { IntegerSettingType, SETTING_NAME_TOOLBARDISPLAYSTYLE, L"1" }, - { IntegerSettingType, SETTING_NAME_SEARCHBOXDISPLAYMODE, L"0" }, - { StringSettingType, SETTING_NAME_REBAR_CONFIG, L"" }, - { StringSettingType, SETTING_NAME_TOOLBAR_CONFIG, L"" }, - { StringSettingType, SETTING_NAME_STATUSBAR_CONFIG, L"" } - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Toolbar and Status Bar"; - info->Author = L"dmex, wj32"; - info->Description = L"Adds a Toolbar, Status Bar and Search box.\r\n\r\nModern Toolbar icons by http://www.icons8.com"; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1119"; - info->HasOptions = TRUE; - info->Interface = &PluginInterface; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainWindowShowing), - MainWindowShowingCallback, - NULL, - &MainWindowShowingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessesUpdated), - ProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainWindowTabChanged), - TabPageUpdatedCallback, - NULL, - &TabPageCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), - TreeNewInitializingCallback, - &ProcessTreeNewHandle, - &ProcessTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), - TreeNewInitializingCallback, - &ServiceTreeNewHandle, - &ServiceTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), - TreeNewInitializingCallback, - &NetworkTreeNewHandle, - &NetworkTreeNewInitializingCallbackRegistration - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - - AcceleratorTable = LoadAccelerators( - Instance, - MAKEINTRESOURCE(IDR_MAINWND_ACCEL) - ); - - TabInfoHashtable = PhCreateSimpleHashtable(3); - } - break; - } - - return TRUE; -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * main program + * + * Copyright (C) 2010-2016 wj32 + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" + +PPH_STRING GetSearchboxText( + VOID + ); + +VOID RegisterTabSearch( + _In_ INT TabIndex, + _In_ PWSTR BannerText + ); + +PTOOLSTATUS_TAB_INFO RegisterTabInfo( + _In_ INT TabIndex + ); + +TOOLSTATUS_CONFIG ToolStatusConfig = { 0 }; +HWND ProcessTreeNewHandle = NULL; +HWND ServiceTreeNewHandle = NULL; +HWND NetworkTreeNewHandle = NULL; +INT SelectedTabIndex = 0; +ULONG ProcessesUpdatedCount = 0; +BOOLEAN UpdateAutomatically = TRUE; +BOOLEAN UpdateGraphs = TRUE; +TOOLBAR_DISPLAY_STYLE DisplayStyle = TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT; +SEARCHBOX_DISPLAY_MODE SearchBoxDisplayMode = SEARCHBOX_DISPLAY_MODE_ALWAYSSHOW; +REBAR_DISPLAY_LOCATION RebarDisplayLocation = REBAR_DISPLAY_LOCATION_TOP; +HWND RebarHandle = NULL; +HWND ToolBarHandle = NULL; +HWND SearchboxHandle = NULL; +WNDPROC MainWindowHookProc = NULL; +HMENU MainMenu = NULL; +HACCEL AcceleratorTable = NULL; +PPH_STRING SearchboxText = NULL; +PH_PLUGIN_SYSTEM_STATISTICS SystemStatistics = { 0 }; +PH_CALLBACK_DECLARE(SearchChangedEvent); +PPH_HASHTABLE TabInfoHashtable; +PPH_TN_FILTER_ENTRY ProcessTreeFilterEntry = NULL; +PPH_TN_FILTER_ENTRY ServiceTreeFilterEntry = NULL; +PPH_TN_FILTER_ENTRY NetworkTreeFilterEntry = NULL; +PPH_PLUGIN PluginInstance = NULL; +TOOLSTATUS_INTERFACE PluginInterface = +{ + TOOLSTATUS_INTERFACE_VERSION, + GetSearchboxText, + WordMatchStringRef, + RegisterTabSearch, + &SearchChangedEvent, + RegisterTabInfo, + ToolbarRegisterGraph +}; + +static ULONG TargetingMode = 0; +static BOOLEAN TargetingWindow = FALSE; +static BOOLEAN TargetingCurrentWindowDraw = FALSE; +static BOOLEAN TargetingCompleted = FALSE; +static HWND TargetingCurrentWindow = NULL; +static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +static PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; +static PH_CALLBACK_REGISTRATION LayoutPaddingCallbackRegistration; +static PH_CALLBACK_REGISTRATION TabPageCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION NetworkTreeNewInitializingCallbackRegistration; + +PPH_STRING GetSearchboxText( + VOID + ) +{ + return SearchboxText; +} + +VOID NTAPI ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (ProcessesUpdatedCount <= 2) + { + ProcessesUpdatedCount++; + return; + } + + PhPluginGetSystemStatistics(&SystemStatistics); + + if (ToolStatusConfig.ToolBarEnabled && ToolBarHandle && UpdateGraphs) + ToolbarUpdateGraphs(); + + if (ToolStatusConfig.StatusBarEnabled) + StatusBarUpdate(FALSE); +} + +VOID NTAPI TreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + *(HWND *)Context = ((PPH_PLUGIN_TREENEW_INFORMATION)Parameter)->TreeNewHandle; +} + +VOID RegisterTabSearch( + _In_ INT TabIndex, + _In_ PWSTR BannerText + ) +{ + PTOOLSTATUS_TAB_INFO tabInfo; + + tabInfo = RegisterTabInfo(TabIndex); + tabInfo->BannerText = BannerText; +} + +PTOOLSTATUS_TAB_INFO RegisterTabInfo( + _In_ INT TabIndex + ) +{ + PTOOLSTATUS_TAB_INFO tabInfoCopy; + PVOID *entry; + + tabInfoCopy = PhCreateAlloc(sizeof(TOOLSTATUS_TAB_INFO)); + memset(tabInfoCopy, 0, sizeof(TOOLSTATUS_TAB_INFO)); + + if (!PhAddItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex), tabInfoCopy)) + { + PhClearReference(&tabInfoCopy); + + if (entry = PhFindItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex))) + tabInfoCopy = *entry; + } + + return tabInfoCopy; +} + +PTOOLSTATUS_TAB_INFO FindTabInfo( + _In_ INT TabIndex + ) +{ + PVOID *entry; + + if (entry = PhFindItemSimpleHashtable(TabInfoHashtable, IntToPtr(TabIndex))) + return *entry; + + return NULL; +} + +HWND GetCurrentTreeNewHandle( + VOID + ) +{ + HWND treeNewHandle = NULL; + + switch (SelectedTabIndex) + { + case 0: + treeNewHandle = ProcessTreeNewHandle; + break; + case 1: + treeNewHandle = ServiceTreeNewHandle; + break; + case 2: + treeNewHandle = NetworkTreeNewHandle; + break; + default: + { + PTOOLSTATUS_TAB_INFO tabInfo; + + if ((tabInfo = FindTabInfo(SelectedTabIndex)) && tabInfo->GetTreeNewHandle) + { + treeNewHandle = tabInfo->GetTreeNewHandle(); + } + } + break; + } + + return treeNewHandle; +} + +VOID ShowCustomizeMenu( + _In_ HWND WindowHandle + ) +{ + POINT cursorPos; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + + GetCursorPos(&cursorPos); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_MENU, L"Main menu (auto-hide)", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_SEARCHBOX, L"Search box", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + ToolbarGraphCreateMenu(menu, COMMAND_ID_GRAPHS_CUSTOMIZE); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_TOOLBAR_LOCKUNLOCK, L"Lock the toolbar", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_TOOLBAR_CUSTOMIZE, L"Customize...", NULL, NULL), ULONG_MAX); + + if (ToolStatusConfig.AutoHideMenu) + { + PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_MENU, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + } + + if (ToolStatusConfig.SearchBoxEnabled) + { + PhSetFlagsEMenuItem(menu, COMMAND_ID_ENABLE_SEARCHBOX, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + } + + if (ToolStatusConfig.ToolBarLocked) + { + PhSetFlagsEMenuItem(menu, COMMAND_ID_TOOLBAR_LOCKUNLOCK, PH_EMENU_CHECKED, PH_EMENU_CHECKED); + } + + selectedItem = PhShowEMenu( + menu, + WindowHandle, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + cursorPos.x, + cursorPos.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + switch (selectedItem->Id) + { + case COMMAND_ID_ENABLE_MENU: + { + ToolStatusConfig.AutoHideMenu = !ToolStatusConfig.AutoHideMenu; + + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); + + if (ToolStatusConfig.AutoHideMenu) + { + SetMenu(WindowHandle, NULL); + } + else + { + SetMenu(WindowHandle, MainMenu); + DrawMenuBar(WindowHandle); + } + } + break; + case COMMAND_ID_ENABLE_SEARCHBOX: + { + ToolStatusConfig.SearchBoxEnabled = !ToolStatusConfig.SearchBoxEnabled; + + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); + + ToolbarLoadSettings(); + ReBarSaveLayoutSettings(); + + if (ToolStatusConfig.SearchBoxEnabled) + { + // Adding the Searchbox makes it focused, + // reset the focus back to the main window. + SetFocus(WindowHandle); + } + } + break; + case COMMAND_ID_TOOLBAR_LOCKUNLOCK: + { + UINT bandCount; + UINT bandIndex; + + bandCount = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0); + + for (bandIndex = 0; bandIndex < bandCount; bandIndex++) + { + REBARBANDINFO rebarBandInfo = + { + sizeof(REBARBANDINFO), + RBBIM_STYLE + }; + + SendMessage(RebarHandle, RB_GETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo); + + if (!(rebarBandInfo.fStyle & RBBS_GRIPPERALWAYS)) + { + // Removing the RBBS_NOGRIPPER style doesn't remove the gripper padding, + // So we toggle the RBBS_GRIPPERALWAYS style to make the Toolbar remove the padding. + + rebarBandInfo.fStyle |= RBBS_GRIPPERALWAYS; + + SendMessage(RebarHandle, RB_SETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo); + + rebarBandInfo.fStyle &= ~RBBS_GRIPPERALWAYS; + } + + if (rebarBandInfo.fStyle & RBBS_NOGRIPPER) + { + rebarBandInfo.fStyle &= ~RBBS_NOGRIPPER; + } + else + { + rebarBandInfo.fStyle |= RBBS_NOGRIPPER; + } + + SendMessage(RebarHandle, RB_SETBANDINFO, bandIndex, (LPARAM)&rebarBandInfo); + } + + ToolStatusConfig.ToolBarLocked = !ToolStatusConfig.ToolBarLocked; + + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); + + ToolbarLoadSettings(); + } + break; + case COMMAND_ID_TOOLBAR_CUSTOMIZE: + { + ToolBarShowCustomizeDialog(); + } + break; + case COMMAND_ID_GRAPHS_CUSTOMIZE: + { + PPH_TOOLBAR_GRAPH icon; + + if (!selectedItem->Context) + break; + + icon = selectedItem->Context; + ToolbarSetVisibleGraph(icon, !(icon->Flags & PH_NF_ICON_ENABLED)); + + ToolbarGraphSaveSettings(); + ReBarSaveLayoutSettings(); + } + break; + } + } + + PhDestroyEMenu(menu); +} + +VOID NTAPI TabPageUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + INT tabIndex = PtrToInt(Parameter); + + SelectedTabIndex = tabIndex; + + if (!SearchboxHandle) + return; + + switch (tabIndex) + { + case 0: + Edit_SetCueBannerText(SearchboxHandle, L"Search Processes (Ctrl+K)"); + break; + case 1: + Edit_SetCueBannerText(SearchboxHandle, L"Search Services (Ctrl+K)"); + break; + case 2: + Edit_SetCueBannerText(SearchboxHandle, L"Search Network (Ctrl+K)"); + break; + default: + { + PTOOLSTATUS_TAB_INFO tabInfo; + + if ((tabInfo = FindTabInfo(tabIndex)) && tabInfo->BannerText) + { + Edit_SetCueBannerText(SearchboxHandle, PhaConcatStrings2(tabInfo->BannerText, L" (Ctrl+K)")->Buffer); + } + else + { + // Disable the textbox if we're on an unsupported tab. + Edit_SetCueBannerText(SearchboxHandle, L"Search disabled"); + } + } + break; + } +} + +VOID NTAPI LayoutPaddingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_LAYOUT_PADDING_DATA layoutPadding = Parameter; + + if (RebarHandle && ToolStatusConfig.ToolBarEnabled) + { + RECT rebarRect; + //RECT clientRect; + //INT x, y, cx, cy; + + SendMessage(RebarHandle, WM_SIZE, 0, 0); + + // TODO: GetClientRect with PhMainWndHandle causes crash. + //GetClientRect(PhMainWndHandle, &clientRect); + GetClientRect(RebarHandle, &rebarRect); + + // Adjust the PH client area and exclude the rebar width. + layoutPadding->Padding.top += rebarRect.bottom; + + // TODO: Replace CCS_TOP with CCS_NOPARENTALIGN and use below code + //switch (RebarDisplayLocation) + //{ + //case RebarLocationLeft: + // { + // //x = 0; + // //y = 0; + // //cx = rebarRect.right - rebarRect.left; + // //cy = clientRect.bottom - clientRect.top; + // } + // break; + //case RebarLocationTop: + // { + // //x = 0; + // //y = 0; + // //cx = clientRect.right - clientRect.left; + // //cy = clientRect.bottom - clientRect.top; + // + // // Adjust the PH client area and exclude the rebar height. + // //layoutPadding->Padding.top += rebarRect.bottom; + // } + // break; + //case RebarLocationRight: + // { + // //x = clientRect.right - (rebarRect.right - rebarRect.left); + // //y = 0; + // //cx = rebarRect.right - rebarRect.left; + // //cy = clientRect.bottom - clientRect.top; + // } + // break; + //case RebarLocationBottom: + // { + // //x = 0; + // //y = clientRect.bottom - (rebarRect.bottom - rebarRect.top) - (StatusBarEnabled ? rebarRect.bottom + 1 : 0); + // //cx = clientRect.right - clientRect.left; + // //cy = rebarRect.bottom - rebarRect.top; + // + // // Adjust the PH client area and exclude the rebar width. + // //layoutPadding->Padding.bottom += rebarRect.bottom; + // } + // break; + //} + //MoveWindow(RebarHandle, x, y, cx, cy, TRUE); + + //if (SearchBoxDisplayStyle == SearchBoxDisplayAutoHide) + //{ + // static BOOLEAN isSearchboxVisible = FALSE; + // SIZE idealWidth; + // + // // Query the Toolbar ideal width + // SendMessage(ToolBarHandle, TB_GETIDEALSIZE, FALSE, (LPARAM)&idealWidth); + // + // // Hide the Searcbox band if the window size is too small... + // if (rebarRect.right > idealWidth.cx) + // { + // if (isSearchboxVisible) + // { + // if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + // RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, 180, 20); + // + // isSearchboxVisible = FALSE; + // } + // } + // else + // { + // if (!isSearchboxVisible) + // { + // if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + // RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); + // + // isSearchboxVisible = TRUE; + // } + // } + //} + } + + if (StatusBarHandle && ToolStatusConfig.StatusBarEnabled) + { + RECT statusBarRect; + + SendMessage(StatusBarHandle, WM_SIZE, 0, 0); + + GetClientRect(StatusBarHandle, &statusBarRect); + + // Adjust the PH client area and exclude the StatusBar width. + layoutPadding->Padding.bottom += statusBarRect.bottom; + + InvalidateRect(StatusBarHandle, NULL, TRUE); + } +} + +BOOLEAN NTAPI MessageLoopFilter( + _In_ PMSG Message, + _In_ PVOID Context + ) +{ + if ( + Message->hwnd == PhMainWndHandle || + IsChild(PhMainWndHandle, Message->hwnd) + ) + { + if (TranslateAccelerator(PhMainWndHandle, AcceleratorTable, Message)) + return TRUE; + + if (Message->message == WM_SYSCHAR && ToolStatusConfig.AutoHideMenu && !GetMenu(PhMainWndHandle)) + { + ULONG key = (ULONG)Message->wParam; + + if (key == 'h' || key == 'v' || key == 't' || key == 'u' || key == 'e') + { + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); + SendMessage(PhMainWndHandle, WM_SYSCHAR, Message->wParam, Message->lParam); + return TRUE; + } + } + } + + return FALSE; +} + +VOID DrawWindowBorderForTargeting( + _In_ HWND hWnd + ) +{ + RECT rect; + HDC hdc; + + GetWindowRect(hWnd, &rect); + hdc = GetWindowDC(hWnd); + + if (hdc) + { + INT penWidth; + INT oldDc; + HPEN pen; + HBRUSH brush; + + penWidth = GetSystemMetrics(SM_CXBORDER) * 3; + oldDc = SaveDC(hdc); + + // Get an inversion effect. + SetROP2(hdc, R2_NOT); + + pen = CreatePen(PS_INSIDEFRAME, penWidth, RGB(0x00, 0x00, 0x00)); + SelectPen(hdc, pen); + + brush = GetStockBrush(NULL_BRUSH); + SelectBrush(hdc, brush); + + // Draw the rectangle. + Rectangle(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top); + + // Cleanup. + DeletePen(pen); + + RestoreDC(hdc, oldDc); + ReleaseDC(hWnd, hdc); + } +} + +LRESULT CALLBACK MainWndSubclassProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_DESTROY: + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MainWindowHookProc); + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (!SearchboxHandle) + break; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(SearchboxHandle)); + + if (!PhEqualString(SearchboxText, newSearchboxText, FALSE)) + { + // Cache the current search text for our callback. + PhSwapReference(&SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(SearchboxText)) + { + // Expand the nodes to ensure that they will be visible to the user. + PhExpandAllProcessNodes(TRUE); + PhDeselectAllProcessNodes(); + PhDeselectAllServiceNodes(); + } + + PhApplyTreeNewFilters(PhGetFilterSupportProcessTreeList()); + PhApplyTreeNewFilters(PhGetFilterSupportServiceTreeList()); + PhApplyTreeNewFilters(PhGetFilterSupportNetworkTreeList()); + + PhInvokeCallback(&SearchChangedEvent, SearchboxText); + } + } + goto DefaultWndProc; + case EN_KILLFOCUS: + { + if (GET_WM_COMMAND_HWND(wParam, lParam) != SearchboxHandle) + break; + + if (SearchBoxDisplayMode != SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) + break; + + if (SearchboxText->Length == 0) + { + if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); + } + } + goto DefaultWndProc; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case PHAPP_ID_ESC_EXIT: + { + // If we're targeting and the user presses the Esc key, cancel the targeting. + // We also make sure the window doesn't get closed, by filtering out the message. + if (TargetingWindow) + { + TargetingWindow = FALSE; + ReleaseCapture(); + + goto DefaultWndProc; + } + } + break; + case ID_SEARCH: + { + // handle keybind Ctrl + K + if (SearchboxHandle && ToolStatusConfig.SearchBoxEnabled) + { + if (SearchBoxDisplayMode == SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) + { + if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PH_SCALE_DPI(180), 22); + + if (!IsWindowVisible(SearchboxHandle)) + ShowWindow(SearchboxHandle, SW_SHOW); + } + + SetFocus(SearchboxHandle); + Edit_SetSel(SearchboxHandle, 0, -1); + } + } + goto DefaultWndProc; + case PHAPP_ID_VIEW_ALWAYSONTOP: + { + // Let Process Hacker perform the default processing. + CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); + + // Query the settings. + BOOLEAN isAlwaysOnTopEnabled = !!PhGetIntegerSetting(L"MainWindowAlwaysOnTop"); + + // Set the pressed button state. + SendMessage(ToolBarHandle, TB_PRESSBUTTON, (WPARAM)PHAPP_ID_VIEW_ALWAYSONTOP, (LPARAM)(MAKELONG(isAlwaysOnTopEnabled, 0))); + } + goto DefaultWndProc; + case PHAPP_ID_UPDATEINTERVAL_FAST: + case PHAPP_ID_UPDATEINTERVAL_NORMAL: + case PHAPP_ID_UPDATEINTERVAL_BELOWNORMAL: + case PHAPP_ID_UPDATEINTERVAL_SLOW: + case PHAPP_ID_UPDATEINTERVAL_VERYSLOW: + { + // Let Process Hacker perform the default processing. + CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); + + StatusBarUpdate(TRUE); + } + goto DefaultWndProc; + case PHAPP_ID_VIEW_UPDATEAUTOMATICALLY: + { + UpdateAutomatically = !UpdateAutomatically; + + StatusBarUpdate(TRUE); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR hdr = (LPNMHDR)lParam; + + if (RebarHandle && hdr->hwndFrom == RebarHandle) + { + switch (hdr->code) + { + case RBN_HEIGHTCHANGE: + { + // Invoke the LayoutPaddingCallback. + SendMessage(hWnd, WM_SIZE, 0, 0); + } + break; + case RBN_CHEVRONPUSHED: + { + LPNMREBARCHEVRON rebar; + ULONG index = 0; + ULONG buttonCount = 0; + RECT toolbarRect; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + + rebar = (LPNMREBARCHEVRON)lParam; + menu = PhCreateEMenu(); + + GetClientRect(ToolBarHandle, &toolbarRect); + + buttonCount = (ULONG)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); + + for (index = 0; index < buttonCount; index++) + { + RECT buttonRect; + TBBUTTONINFO buttonInfo = + { + sizeof(TBBUTTONINFO), + TBIF_BYINDEX | TBIF_STYLE | TBIF_COMMAND | TBIF_IMAGE + }; + + // Get the client coordinates of the button. + if (SendMessage(ToolBarHandle, TB_GETITEMRECT, index, (LPARAM)&buttonRect) == 0) + break; + + if (buttonRect.right <= toolbarRect.right) + continue; + + // Get extended button information. + if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1) + break; + + if (buttonInfo.fsStyle == BTNS_SEP) + { + // Add separators to menu. + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + } + else + { + PPH_EMENU_ITEM menuItem; + + // Add toolbar buttons to the context menu. + menuItem = PhCreateEMenuItem(0, buttonInfo.idCommand, ToolbarGetText(buttonInfo.idCommand), NULL, NULL); + + // Add the button image to the context menu. + menuItem->Flags |= PH_EMENU_BITMAP_OWNED; + menuItem->Bitmap = ToolbarGetImage(buttonInfo.idCommand); + + switch (buttonInfo.idCommand) + { + case TIDC_FINDWINDOW: + case TIDC_FINDWINDOWTHREAD: + case TIDC_FINDWINDOWKILL: + { + // Note: These buttons are incompatible with the context menu window messages. + menuItem->Flags |= PH_EMENU_DISABLED; + } + break; + case PHAPP_ID_VIEW_ALWAYSONTOP: + { + // Set the pressed state. + if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) + menuItem->Flags |= PH_EMENU_CHECKED; + } + break; + case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: + { + if (PhGetOwnTokenAttributes().Elevated) + { + // Disable the 'Show Details for All Processes' button when we're elevated. + menuItem->Flags |= PH_EMENU_DISABLED; + } + } + break; + case TIDC_POWERMENUDROPDOWN: + { + // Create the sub-menu... + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOCK, L"&Lock", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOGOFF, L"Log o&ff", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SLEEP, L"&Sleep", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_HIBERNATE, L"&Hibernate", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTART, L"R&estart", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS, L"Restart to boot &options", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWN, L"Shu&t down", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menuItem, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID, L"H&ybrid shut down", NULL, NULL), ULONG_MAX); + + if (WindowsVersion < WINDOWS_8) + { + PPH_EMENU_ITEM menuItemRemove; + + if (menuItemRemove = PhFindEMenuItem(menuItem, PH_EMENU_FIND_DESCEND, NULL, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS)) + PhDestroyEMenuItem(menuItemRemove); + if (menuItemRemove = PhFindEMenuItem(menuItem, PH_EMENU_FIND_DESCEND, NULL, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID)) + PhDestroyEMenuItem(menuItemRemove); + } + } + break; + } + + PhInsertEMenuItem(menu, menuItem, ULONG_MAX); + } + } + + MapWindowPoints(RebarHandle, NULL, (LPPOINT)&rebar->rc, 2); + + selectedItem = PhShowEMenu( + menu, + hWnd, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + rebar->rc.left, + rebar->rc.bottom + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); + } + + PhDestroyEMenu(menu); + } + break; + case RBN_LAYOUTCHANGED: + { + ReBarSaveLayoutSettings(); + } + break; + case NM_CUSTOMDRAW: + return CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); // HACK + } + + goto DefaultWndProc; + } + else if (ToolBarHandle && hdr->hwndFrom == ToolBarHandle) + { + switch (hdr->code) + { + case TBN_GETDISPINFO: + { + LPNMTBDISPINFO toolbarDisplayInfo = (LPNMTBDISPINFO)lParam; + + if (toolbarDisplayInfo->dwMask & TBNF_IMAGE) + { + // Try to find the cached bitmap index. + // NOTE: The TBNF_DI_SETITEM flag below will cache the index so we only get called once. + // However, when adding buttons from the customize dialog we get called a second time, + // so we cache the index in our ToolbarButtons array to prevent ToolBarImageList from growing. + for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++) + { + if (ToolbarButtons[i].idCommand == toolbarDisplayInfo->idCommand) + { + // Cache the bitmap index. + toolbarDisplayInfo->dwMask |= TBNF_DI_SETITEM; + // Set the bitmap index. + toolbarDisplayInfo->iImage = ToolbarButtons[i].iBitmap; + break; + } + } + + if (toolbarDisplayInfo->iImage == I_IMAGECALLBACK) + { + // We didn't find a cached bitmap index... + // Load the button bitmap and cache the index. + for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++) + { + if (ToolbarButtons[i].idCommand == toolbarDisplayInfo->idCommand) + { + HBITMAP buttonImage; + + if (buttonImage = ToolbarGetImage(toolbarDisplayInfo->idCommand)) + { + // Cache the bitmap index. + toolbarDisplayInfo->dwMask |= TBNF_DI_SETITEM; + + // Add the image, cache the value in the ToolbarButtons array, set the bitmap index. + toolbarDisplayInfo->iImage = ToolbarButtons[i].iBitmap = ImageList_Add( + ToolBarImageList, + buttonImage, + NULL + ); + + DeleteBitmap(buttonImage); + } + break; + } + } + } + } + } + break; + case TBN_DROPDOWN: + { + LPNMTOOLBAR toolbar = (LPNMTOOLBAR)hdr; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + + if (toolbar->iItem != TIDC_POWERMENUDROPDOWN) + break; + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOCK, L"&Lock", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_LOGOFF, L"Log o&ff", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SLEEP, L"&Sleep", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_HIBERNATE, L"&Hibernate", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTART, L"R&estart", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS, L"Restart to boot &options", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWN, L"Shu&t down", NULL, NULL), ULONG_MAX); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID, L"H&ybrid shut down", NULL, NULL), ULONG_MAX); + + if (WindowsVersion < WINDOWS_8) + { + PPH_EMENU_ITEM menuItemRemove; + + if (menuItemRemove = PhFindEMenuItem(menu, PH_EMENU_FIND_DESCEND, NULL, PHAPP_ID_COMPUTER_RESTARTBOOTOPTIONS)) + PhDestroyEMenuItem(menuItemRemove); + if (menuItemRemove = PhFindEMenuItem(menu, PH_EMENU_FIND_DESCEND, NULL, PHAPP_ID_COMPUTER_SHUTDOWNHYBRID)) + PhDestroyEMenuItem(menuItemRemove); + } + + MapWindowPoints(ToolBarHandle, NULL, (LPPOINT)&toolbar->rcButton, 2); + + selectedItem = PhShowEMenu( + menu, + hWnd, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + toolbar->rcButton.left, + toolbar->rcButton.bottom + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(selectedItem->Id, BN_CLICKED), 0); + } + + PhDestroyEMenu(menu); + } + return TBDDRET_DEFAULT; + case NM_LDOWN: + { + LPNMCLICK toolbar = (LPNMCLICK)hdr; + ULONG id = (ULONG)toolbar->dwItemSpec; + + if (id == ULONG_MAX) + break; + + if (id == TIDC_FINDWINDOW || id == TIDC_FINDWINDOWTHREAD || id == TIDC_FINDWINDOWKILL) + { + // Direct all mouse events to this window. + SetCapture(hWnd); + + // Set the cursor. + SetCursor(LoadCursor(NULL, IDC_CROSS)); + + // Send the window to the bottom. + SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + TargetingWindow = TRUE; + TargetingCurrentWindow = NULL; + TargetingCurrentWindowDraw = FALSE; + TargetingCompleted = FALSE; + TargetingMode = id; + + SendMessage(hWnd, WM_MOUSEMOVE, 0, 0); + } + } + break; + case NM_RCLICK: + { + ShowCustomizeMenu(hWnd); + } + break; + case NM_CUSTOMDRAW: + return CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); // HACK + } + + goto DefaultWndProc; + } + else if (StatusBarHandle && hdr->hwndFrom == StatusBarHandle) + { + switch (hdr->code) + { + case NM_RCLICK: + { + StatusBarShowMenu(); + } + break; + } + + goto DefaultWndProc; + } + else + { + if ( + ToolStatusConfig.ToolBarEnabled && + ToolBarHandle && + ToolbarUpdateGraphsInfo(hWnd, hdr) + ) + { + goto DefaultWndProc; + } + } + } + break; + case WM_MOUSEMOVE: + { + if (TargetingWindow) + { + POINT cursorPos; + HWND windowOverMouse; + ULONG processId; + ULONG threadId; + + GetCursorPos(&cursorPos); + windowOverMouse = WindowFromPoint(cursorPos); + + if (TargetingCurrentWindow != windowOverMouse) + { + if (TargetingCurrentWindow && TargetingCurrentWindowDraw) + { + // Invert the old border (to remove it). + DrawWindowBorderForTargeting(TargetingCurrentWindow); + } + + if (windowOverMouse) + { + threadId = GetWindowThreadProcessId(windowOverMouse, &processId); + + // Draw a rectangle over the current window (but not if it's one of our own). + if (UlongToHandle(processId) != NtCurrentProcessId()) + { + DrawWindowBorderForTargeting(windowOverMouse); + TargetingCurrentWindowDraw = TRUE; + } + else + { + TargetingCurrentWindowDraw = FALSE; + } + } + + TargetingCurrentWindow = windowOverMouse; + } + + goto DefaultWndProc; + } + } + break; + case WM_LBUTTONUP: + { + if (TargetingWindow) + { + ULONG processId; + ULONG threadId; + + TargetingCompleted = TRUE; + + // Reset the original cursor. + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + // Bring the window back to the top, and preserve the Always on Top setting. + SetWindowPos(hWnd, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, + 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + TargetingWindow = FALSE; + ReleaseCapture(); + + if (TargetingCurrentWindow) + { + if (TargetingCurrentWindowDraw) + { + // Remove the border on the window we found. + DrawWindowBorderForTargeting(TargetingCurrentWindow); + } + + if (ToolStatusConfig.ResolveGhostWindows) + { + // This is an undocumented function exported by user32.dll that + // retrieves the hung window represented by a ghost window. + static HWND (WINAPI *HungWindowFromGhostWindow_I)( + _In_ HWND WindowHandle + ); + + if (!HungWindowFromGhostWindow_I) + HungWindowFromGhostWindow_I = PhGetModuleProcAddress(L"user32.dll", "HungWindowFromGhostWindow"); + + if (HungWindowFromGhostWindow_I) + { + HWND hungWindow = HungWindowFromGhostWindow_I(TargetingCurrentWindow); + + // The call will have failed if the window wasn't actually a ghost + // window. + if (hungWindow) + TargetingCurrentWindow = hungWindow; + } + } + + threadId = GetWindowThreadProcessId(TargetingCurrentWindow, &processId); + + if (threadId && processId && UlongToHandle(processId) != NtCurrentProcessId()) + { + PPH_PROCESS_NODE processNode; + + processNode = PhFindProcessNode(UlongToHandle(processId)); + + if (processNode) + { + ProcessHacker_SelectTabPage(hWnd, 0); + ProcessHacker_SelectProcessNode(hWnd, processNode); + } + + switch (TargetingMode) + { + case TIDC_FINDWINDOWTHREAD: + { + PPH_PROCESS_PROPCONTEXT propContext; + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(UlongToHandle(processId))) + { + if (propContext = PhCreateProcessPropContext(hWnd, processItem)) + { + PhSetSelectThreadIdProcessPropContext(propContext, UlongToHandle(threadId)); + PhShowProcessProperties(propContext); + PhDereferenceObject(propContext); + } + + PhDereferenceObject(processItem); + } + else + { + PhShowError(hWnd, L"The process (PID %lu) does not exist.", processId); + } + } + break; + case TIDC_FINDWINDOWKILL: + { + PPH_PROCESS_ITEM processItem; + + if (processItem = PhReferenceProcessItem(UlongToHandle(processId))) + { + PhUiTerminateProcesses(hWnd, &processItem, 1); + PhDereferenceObject(processItem); + } + else + { + PhShowError(hWnd, L"The process (PID %lu) does not exist.", processId); + } + } + break; + } + } + } + + goto DefaultWndProc; + } + } + break; + case WM_CAPTURECHANGED: + { + if (!TargetingCompleted) + { + // The user cancelled the targeting, probably by pressing the Esc key. + + // Remove the border on the currently selected window. + if (TargetingCurrentWindow) + { + if (TargetingCurrentWindowDraw) + { + // Remove the border on the window we found. + DrawWindowBorderForTargeting(TargetingCurrentWindow); + } + } + + SetWindowPos(hWnd, PhGetIntegerSetting(L"MainWindowAlwaysOnTop") ? HWND_TOPMOST : HWND_TOP, + 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + TargetingCompleted = TRUE; + } + } + break; + case WM_SIZE: + // Resize PH main window client-area. + ProcessHacker_InvalidateLayoutPadding(hWnd); + break; + case WM_SETTINGCHANGE: + // Forward to the Searchbox so we can reinitialize the settings... + SendMessage(SearchboxHandle, WM_SETTINGCHANGE, 0, 0); + break; + case WM_SHOWWINDOW: + { + UpdateGraphs = (BOOLEAN)wParam; + } + break; + case WM_SYSCOMMAND: + { + if ((wParam & 0xFFF0) == SC_KEYMENU && lParam == 0) + { + if (!ToolStatusConfig.AutoHideMenu) + break; + + if (GetMenu(hWnd)) + { + SetMenu(hWnd, NULL); + } + else + { + SetMenu(hWnd, MainMenu); + DrawMenuBar(hWnd); + } + } + else if ((wParam & 0xFFF0) == SC_MINIMIZE) + { + UpdateGraphs = FALSE; + } + else if ((wParam & 0xFFF0) == SC_RESTORE) + { + UpdateGraphs = TRUE; + + // TODO: The graphs don't redraw when updating is disabled (e.g. F6) and you maximize then restore the main window. + RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); + } + } + break; + case WM_EXITMENULOOP: + { + if (!ToolStatusConfig.AutoHideMenu) + break; + + if (GetMenu(hWnd)) + { + SetMenu(hWnd, NULL); + } + } + break; + case WM_PH_UPDATE_FONT: + { + HFONT newFont; + + // Let Process Hacker perform the default processing. + CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); + + if (newFont = (HFONT)SendMessage(hWnd, WM_PH_GET_FONT, 0, 0)) + { + if (ToolStatusWindowFont) DeleteFont(ToolStatusWindowFont); + ToolStatusWindowFont = newFont; + + SetWindowFont(ToolBarHandle, ToolStatusWindowFont, TRUE); + SetWindowFont(StatusBarHandle, ToolStatusWindowFont, TRUE); + + ToolbarLoadSettings(); + } + + goto DefaultWndProc; + } + break; + } + + return CallWindowProc(MainWindowHookProc, hWnd, uMsg, wParam, lParam); + +DefaultWndProc: + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +VOID NTAPI MainWindowShowingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PhRegisterMessageLoopFilter(MessageLoopFilter, NULL); + PhRegisterCallback( + ProcessHacker_GetCallbackLayoutPadding(PhMainWndHandle), + LayoutPaddingCallback, + NULL, + &LayoutPaddingCallbackRegistration + ); + + MainWindowHookProc = (WNDPROC)GetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC); + SetWindowLongPtr(PhMainWndHandle, GWLP_WNDPROC, (LONG_PTR)MainWndSubclassProc); + + ToolbarLoadSettings(); + ToolbarCreateGraphs(); + ReBarLoadLayoutSettings(); + StatusBarLoadSettings(); + + MainMenu = GetMenu(PhMainWndHandle); + if (ToolStatusConfig.AutoHideMenu) + { + SetMenu(PhMainWndHandle, NULL); + } +} + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ToolStatusConfig.Flags = PhGetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG); + DisplayStyle = (TOOLBAR_DISPLAY_STYLE)PhGetIntegerSetting(SETTING_NAME_TOOLBARDISPLAYSTYLE); + SearchBoxDisplayMode = (SEARCHBOX_DISPLAY_MODE)PhGetIntegerSetting(SETTING_NAME_SEARCHBOXDISPLAYMODE); + UpdateGraphs = !PhGetIntegerSetting(L"StartHidden"); + + ToolbarGraphsInitialize(); +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"ToolStatus", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { IntegerSettingType, SETTING_NAME_TOOLSTATUS_CONFIG, L"1F" }, + { IntegerSettingType, SETTING_NAME_TOOLBAR_THEME, L"0" }, + { IntegerSettingType, SETTING_NAME_TOOLBARDISPLAYSTYLE, L"1" }, + { IntegerSettingType, SETTING_NAME_SEARCHBOXDISPLAYMODE, L"0" }, + { StringSettingType, SETTING_NAME_REBAR_CONFIG, L"" }, + { StringSettingType, SETTING_NAME_TOOLBAR_CONFIG, L"" }, + { StringSettingType, SETTING_NAME_STATUSBAR_CONFIG, L"" }, + { StringSettingType, SETTING_NAME_TOOLBAR_GRAPH_CONFIG, L"" } + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Toolbar and Status Bar"; + info->Author = L"dmex, wj32"; + info->Description = L"Adds a Toolbar, Status Bar and Search box.\r\n\r\nModern Toolbar icons by http://www.icons8.com"; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1119"; + info->Interface = &PluginInterface; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainWindowShowing), + MainWindowShowingCallback, + NULL, + &MainWindowShowingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessesUpdated), + ProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainWindowTabChanged), + TabPageUpdatedCallback, + NULL, + &TabPageCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), + TreeNewInitializingCallback, + &ProcessTreeNewHandle, + &ProcessTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), + TreeNewInitializingCallback, + &ServiceTreeNewHandle, + &ServiceTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackNetworkTreeNewInitializing), + TreeNewInitializingCallback, + &NetworkTreeNewHandle, + &NetworkTreeNewInitializingCallbackRegistration + ); + + PhAddSettings(settings, ARRAYSIZE(settings)); + + AcceleratorTable = LoadAccelerators( + Instance, + MAKEINTRESOURCE(IDR_MAINWND_ACCEL) + ); + + TabInfoHashtable = PhCreateSimpleHashtable(3); + } + break; + } + + return TRUE; +} diff --git a/plugins/ToolStatus/options.c b/plugins/ToolStatus/options.c index 4245130e9ef9..d2b0ca3129a0 100644 --- a/plugins/ToolStatus/options.c +++ b/plugins/ToolStatus/options.c @@ -1,98 +1,73 @@ -/* - * Process Hacker ToolStatus - - * Plugin Options - * - * Copyright (C) 2011-2016 dmex - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_TOOLBAR), - ToolStatusConfig.ToolBarEnabled ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_STATUSBAR), - ToolStatusConfig.StatusBarEnabled ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_RESOLVEGHOSTWINDOWS), - ToolStatusConfig.ResolveGhostWindows ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_AUTOHIDE_MENU), - ToolStatusConfig.AutoHideMenu ? BST_CHECKED : BST_UNCHECKED); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - ToolStatusConfig.ToolBarEnabled = Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_TOOLBAR)) == BST_CHECKED; - ToolStatusConfig.StatusBarEnabled = Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_STATUSBAR)) == BST_CHECKED; - ToolStatusConfig.ResolveGhostWindows = Button_GetCheck(GetDlgItem(hwndDlg, IDC_RESOLVEGHOSTWINDOWS)) == BST_CHECKED; - ToolStatusConfig.AutoHideMenu = Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_AUTOHIDE_MENU)) == BST_CHECKED; - - PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); - - ToolbarLoadSettings(); - - if (ToolStatusConfig.AutoHideMenu) - { - SetMenu(PhMainWndHandle, NULL); - } - else - { - SetMenu(PhMainWndHandle, MainMenu); - DrawMenuBar(PhMainWndHandle); - } - - EndDialog(hwndDlg, IDOK); - } - break; - } - } - break; - } - - return FALSE; -} - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - Parent, - OptionsDlgProc - ); -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * Plugin Options + * + * Copyright (C) 2011-2016 dmex + * Copyright (C) 2010-2013 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_TOOLBAR), + ToolStatusConfig.ToolBarEnabled ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_STATUSBAR), + ToolStatusConfig.StatusBarEnabled ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_RESOLVEGHOSTWINDOWS), + ToolStatusConfig.ResolveGhostWindows ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_AUTOHIDE_MENU), + ToolStatusConfig.AutoHideMenu ? BST_CHECKED : BST_UNCHECKED); + } + break; + case WM_DESTROY: + { + ToolStatusConfig.ToolBarEnabled = Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_TOOLBAR)) == BST_CHECKED; + ToolStatusConfig.StatusBarEnabled = Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_STATUSBAR)) == BST_CHECKED; + ToolStatusConfig.ResolveGhostWindows = Button_GetCheck(GetDlgItem(hwndDlg, IDC_RESOLVEGHOSTWINDOWS)) == BST_CHECKED; + ToolStatusConfig.AutoHideMenu = Button_GetCheck(GetDlgItem(hwndDlg, IDC_ENABLE_AUTOHIDE_MENU)) == BST_CHECKED; + + PhSetIntegerSetting(SETTING_NAME_TOOLSTATUS_CONFIG, ToolStatusConfig.Flags); + + ToolbarLoadSettings(); + ToolbarCreateGraphs(); + + if (ToolStatusConfig.AutoHideMenu) + { + SetMenu(PhMainWndHandle, NULL); + } + else + { + SetMenu(PhMainWndHandle, MainMenu); + DrawMenuBar(PhMainWndHandle); + } + } + break; + } + + return FALSE; +} diff --git a/plugins/ToolStatus/resource.h b/plugins/ToolStatus/resource.h index 63439645b349..1868d400abcf 100644 --- a/plugins/ToolStatus/resource.h +++ b/plugins/ToolStatus/resource.h @@ -1,57 +1,57 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by ToolStatus.rc -// -#define IDD_OPTIONS 102 -#define IDR_MAINWND_ACCEL 103 -#define IDB_SEARCH_ACTIVE 104 -#define IDB_SEARCH_ACTIVE_BMP 119 -#define IDB_SEARCH_INACTIVE 120 -#define IDB_SEARCH_INACTIVE_BMP 121 -#define IDB_APPLICATION_GET_MODERN 126 -#define IDB_APPLICATION_GO_MODERN 127 -#define IDB_APPLICATION_MODERN 128 -#define IDB_ARROW_REFRESH_MODERN 129 -#define IDB_CHART_LINE_MODERN 130 -#define IDB_COG_EDIT_MODERN 131 -#define IDB_CROSS_MODERN 132 -#define IDB_FIND_MODERN 133 -#define IDB_POWER_MODERN 134 -#define IDD_CUSTOMIZE_TB 135 -#define IDD_CUSTOMIZE_SB 136 -#define IDI_ARROW_REFRESH 137 -#define IDI_COG_EDIT 138 -#define IDI_FIND 139 -#define IDI_CHART_LINE 140 -#define IDI_TBAPPLICATION 141 -#define IDI_APPLICATION_GO 142 -#define IDI_CROSS 143 -#define IDI_APPLICATION_GET 144 -#define IDI_LIGHTBULB_OFF 145 -#define IDC_ENABLE_TOOLBAR 1001 -#define IDC_ENABLE_MODERN 1002 -#define IDC_ENABLE_STATUSBAR 1003 -#define IDC_RESOLVEGHOSTWINDOWS 1004 -#define IDC_AVAILABLE 1005 -#define IDC_ADD 1006 -#define IDC_REMOVE 1007 -#define IDC_CURRENT 1008 -#define IDC_SEARCHOPTIONS 1009 -#define IDC_TEXTOPTIONS 1010 -#define IDC_MOVEUP 1011 -#define IDC_MOVEDOWN 1012 -#define IDC_RESET 1014 -#define IDC_ENABLE_AUTOHIDE_MENU 1015 -#define IDC_ENABLE_AUTOCOMPLETE 1016 -#define ID_SEARCH 40011 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 146 -#define _APS_NEXT_COMMAND_VALUE 40012 -#define _APS_NEXT_CONTROL_VALUE 1017 -#define _APS_NEXT_SYMED_VALUE 11010 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ToolStatus.rc +// +#define IDD_OPTIONS 102 +#define IDR_MAINWND_ACCEL 103 +#define IDB_SEARCH_ACTIVE 104 +#define IDB_SEARCH_ACTIVE_BMP 119 +#define IDB_SEARCH_INACTIVE 120 +#define IDB_SEARCH_INACTIVE_BMP 121 +#define IDB_APPLICATION_GET_MODERN 126 +#define IDB_APPLICATION_GO_MODERN 127 +#define IDB_APPLICATION_MODERN 128 +#define IDB_ARROW_REFRESH_MODERN 129 +#define IDB_CHART_LINE_MODERN 130 +#define IDB_COG_EDIT_MODERN 131 +#define IDB_CROSS_MODERN 132 +#define IDB_FIND_MODERN 133 +#define IDB_POWER_MODERN 134 +#define IDD_CUSTOMIZE_TB 135 +#define IDD_CUSTOMIZE_SB 136 +#define IDI_ARROW_REFRESH 137 +#define IDI_COG_EDIT 138 +#define IDI_FIND 139 +#define IDI_CHART_LINE 140 +#define IDI_TBAPPLICATION 141 +#define IDI_APPLICATION_GO 142 +#define IDI_CROSS 143 +#define IDI_APPLICATION_GET 144 +#define IDI_LIGHTBULB_OFF 145 +#define IDC_ENABLE_TOOLBAR 1001 +#define IDC_ENABLE_MODERN 1002 +#define IDC_ENABLE_STATUSBAR 1003 +#define IDC_RESOLVEGHOSTWINDOWS 1004 +#define IDC_AVAILABLE 1005 +#define IDC_ADD 1006 +#define IDC_REMOVE 1007 +#define IDC_CURRENT 1008 +#define IDC_SEARCHOPTIONS 1009 +#define IDC_TEXTOPTIONS 1010 +#define IDC_MOVEUP 1011 +#define IDC_MOVEDOWN 1012 +#define IDC_RESET 1014 +#define IDC_ENABLE_AUTOHIDE_MENU 1015 +#define IDC_ENABLE_AUTOCOMPLETE 1016 +#define ID_SEARCH 40011 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 146 +#define _APS_NEXT_COMMAND_VALUE 40012 +#define _APS_NEXT_CONTROL_VALUE 1017 +#define _APS_NEXT_SYMED_VALUE 11010 +#endif +#endif diff --git a/plugins/ToolStatus/statusbar.c b/plugins/ToolStatus/statusbar.c index 7bd302f877ed..7cb7ad9e0a28 100644 --- a/plugins/ToolStatus/statusbar.c +++ b/plugins/ToolStatus/statusbar.c @@ -1,581 +1,603 @@ -/* - * Process Hacker ToolStatus - - * statusbar main - * - * Copyright (C) 2011-2016 dmex - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" - -HWND StatusBarHandle = NULL; -ULONG ProcessesUpdatedCount = 0; -ULONG StatusBarMaxWidths[MAX_STATUSBAR_ITEMS]; -// Note: no lock is needed because we only ever modify the list on this same thread. -PPH_LIST StatusBarItemList = NULL; -ULONG StatusBarItems[MAX_STATUSBAR_ITEMS] = -{ - // Default items (displayed) - { ID_STATUS_CPUUSAGE }, - { ID_STATUS_PHYSICALMEMORY }, - { ID_STATUS_FREEMEMORY }, - // Available items (hidden) - { ID_STATUS_COMMITCHARGE }, - { ID_STATUS_NUMBEROFPROCESSES }, - { ID_STATUS_NUMBEROFTHREADS }, - { ID_STATUS_NUMBEROFHANDLES }, - { ID_STATUS_NUMBEROFVISIBLEITEMS, }, - { ID_STATUS_NUMBEROFSELECTEDITEMS, }, - { ID_STATUS_INTERVALSTATUS }, - { ID_STATUS_IO_RO }, - { ID_STATUS_IO_W }, - { ID_STATUS_MAX_CPU_PROCESS }, - { ID_STATUS_MAX_IO_PROCESS }, -}; - -VOID StatusBarLoadDefault( - VOID - ) -{ - if (!StatusBarItemList) - StatusBarItemList = PhCreateList(MAX_DEFAULT_STATUSBAR_ITEMS); - - for (ULONG i = 0; i < MAX_DEFAULT_STATUSBAR_ITEMS; i++) - { - PSTATUSBAR_ITEM item; - - item = PhAllocate(sizeof(STATUSBAR_ITEM)); - memset(item, 0, sizeof(STATUSBAR_ITEM)); - - item->Id = StatusBarItems[i]; - - PhAddItemList(StatusBarItemList, item); - } -} - -VOID StatusBarLoadSettings( - VOID - ) -{ - ULONG64 buttonCount = 0; - PPH_STRING settingsString; - PH_STRINGREF remaining; - PH_STRINGREF part; - - settingsString = PhaGetStringSetting(SETTING_NAME_STATUSBAR_CONFIG); - remaining = settingsString->sr; - - if (remaining.Length == 0) - { - // Load default settings - StatusBarLoadDefault(); - return; - } - - // Query the number of buttons to insert - if (!PhSplitStringRefAtChar(&remaining, '|', &part, &remaining)) - { - // Load default settings - StatusBarLoadDefault(); - return; - } - - if (!PhStringToInteger64(&part, 10, &buttonCount)) - { - // Load default settings - StatusBarLoadDefault(); - return; - } - - StatusBarItemList = PhCreateList((ULONG)buttonCount); - - for (ULONG i = 0; i < (ULONG)buttonCount; i++) - { - PH_STRINGREF idPart; - ULONG64 idInteger; - - if (remaining.Length == 0) - break; - - PhSplitStringRefAtChar(&remaining, '|', &idPart, &remaining); - - if (PhStringToInteger64(&idPart, 10, &idInteger)) - { - PSTATUSBAR_ITEM item; - - item = PhAllocate(sizeof(STATUSBAR_ITEM)); - memset(item, 0, sizeof(STATUSBAR_ITEM)); - - item->Id = (ULONG)idInteger; - - PhInsertItemList(StatusBarItemList, i, item); - } - } -} - -VOID StatusBarSaveSettings( - VOID - ) -{ - PPH_STRING settingsString; - PH_STRING_BUILDER stringBuilder; - - PhInitializeStringBuilder(&stringBuilder, 100); - - PhAppendFormatStringBuilder( - &stringBuilder, - L"%lu|", - StatusBarItemList->Count - ); - - for (ULONG i = 0; i < StatusBarItemList->Count; i++) - { - PSTATUSBAR_ITEM item = StatusBarItemList->Items[i]; - - PhAppendFormatStringBuilder( - &stringBuilder, - L"%lu|", - item->Id - ); - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); - PhSetStringSetting2(SETTING_NAME_STATUSBAR_CONFIG, &settingsString->sr); -} - -VOID StatusBarResetSettings( - VOID - ) -{ - for (ULONG i = 0; i < StatusBarItemList->Count; i++) - { - PhFree(StatusBarItemList->Items[i]); - } - - PhClearList(StatusBarItemList); - - StatusBarLoadDefault(); -} - -PWSTR StatusBarGetText( - _In_ ULONG CommandID - ) -{ - switch (CommandID) - { - case ID_STATUS_CPUUSAGE: - return L"CPU usage"; - case ID_STATUS_PHYSICALMEMORY: - return L"Physical memory"; - case ID_STATUS_NUMBEROFPROCESSES: - return L"Number of processes"; - case ID_STATUS_COMMITCHARGE: - return L"Commit charge"; - case ID_STATUS_FREEMEMORY: - return L"Free physical memory"; - case ID_STATUS_NUMBEROFTHREADS: - return L"Number of threads"; - case ID_STATUS_NUMBEROFHANDLES: - return L"Number of handles"; - case ID_STATUS_NUMBEROFVISIBLEITEMS: - return L"Number of visible items"; - case ID_STATUS_NUMBEROFSELECTEDITEMS: - return L"Number of selected items"; - case ID_STATUS_INTERVALSTATUS: - return L"Interval status"; - case ID_STATUS_IO_RO: - return L"I/O read+other"; - case ID_STATUS_IO_W: - return L"I/O write"; - case ID_STATUS_MAX_CPU_PROCESS: - return L"Max. CPU process"; - case ID_STATUS_MAX_IO_PROCESS: - return L"Max. I/O process"; - } - - return L"ERROR"; -} - -VOID StatusBarShowMenu( - VOID - ) -{ - PPH_EMENU menu; - PPH_EMENU_ITEM selectedItem; - POINT cursorPos; - - GetCursorPos(&cursorPos); - - menu = PhCreateEMenu(); - PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_SEARCHBOX, L"Customize...", NULL, NULL), -1); - - selectedItem = PhShowEMenu( - menu, - PhMainWndHandle, - PH_EMENU_SHOW_LEFTRIGHT, - PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, - cursorPos.x, - cursorPos.y - ); - - if (selectedItem && selectedItem->Id != -1) - { - StatusBarShowCustomizeDialog(); - - StatusBarUpdate(TRUE); - } - - PhDestroyEMenu(menu); -} - -VOID StatusBarUpdate( - _In_ BOOLEAN ResetMaxWidths - ) -{ - static ULONG64 lastTickCount = 0; - - ULONG count; - ULONG i; - HDC hdc; - BOOLEAN resetMaxWidths = FALSE; - PPH_STRING text[MAX_STATUSBAR_ITEMS]; - ULONG widths[MAX_STATUSBAR_ITEMS]; - - if (ProcessesUpdatedCount < 2) - return; - - if (ResetMaxWidths) - resetMaxWidths = TRUE; - - if (!StatusBarItemList || StatusBarItemList->Count == 0) - { - // The status bar doesn't cope well with 0 parts. - widths[0] = -1; - SendMessage(StatusBarHandle, SB_SETPARTS, 1, (LPARAM)widths); - SendMessage(StatusBarHandle, SB_SETTEXT, 0, (LPARAM)L""); - return; - } - - hdc = GetDC(StatusBarHandle); - SelectObject(hdc, (HFONT)SendMessage(StatusBarHandle, WM_GETFONT, 0, 0)); - - // Reset max. widths for Max. CPU Process and Max. I/O Process parts once in a while. - { - LARGE_INTEGER tickCount; - - PhQuerySystemTime(&tickCount); - - if (tickCount.QuadPart - lastTickCount >= 10 * PH_TICKS_PER_SEC) - { - resetMaxWidths = TRUE; - lastTickCount = tickCount.QuadPart; - } - } - - count = 0; - - for (i = 0; i < StatusBarItemList->Count; i++) - { - SIZE size; - ULONG width; - PSTATUSBAR_ITEM item; - - item = StatusBarItemList->Items[i]; - - switch (item->Id) - { - case ID_STATUS_CPUUSAGE: - { - text[count] = PhFormatString( - L"CPU Usage: %.2f%%", - (SystemStatistics.CpuKernelUsage + SystemStatistics.CpuUserUsage) * 100 - ); - } - break; - case ID_STATUS_COMMITCHARGE: - { - ULONG commitUsage = SystemStatistics.Performance->CommittedPages; - FLOAT commitFraction = (FLOAT)commitUsage / SystemStatistics.Performance->CommitLimit * 100; - - text[count] = PhFormatString( - L"Commit charge: %s (%.2f%%)", - PhaFormatSize(UInt32x32To64(commitUsage, PAGE_SIZE), -1)->Buffer, - commitFraction - ); - } - break; - case ID_STATUS_PHYSICALMEMORY: - { - ULONG physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - SystemStatistics.Performance->AvailablePages; - FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages * 100; - - text[count] = PhFormatString( - L"Physical memory: %s (%.2f%%)", - PhaFormatSize(UInt32x32To64(physicalUsage, PAGE_SIZE), -1)->Buffer, - physicalFraction - ); - } - break; - case ID_STATUS_FREEMEMORY: - { - ULONG physicalFree = SystemStatistics.Performance->AvailablePages; - FLOAT physicalFreeFraction = (FLOAT)physicalFree / PhSystemBasicInformation.NumberOfPhysicalPages * 100; - - text[count] = PhFormatString( - L"Free memory: %s (%.2f%%)", - PhaFormatSize(UInt32x32To64(physicalFree, PAGE_SIZE), -1)->Buffer, - physicalFreeFraction - ); - } - break; - case ID_STATUS_NUMBEROFPROCESSES: - { - text[count] = PhConcatStrings2( - L"Processes: ", - PhaFormatUInt64(SystemStatistics.NumberOfProcesses, TRUE)->Buffer - ); - } - break; - case ID_STATUS_NUMBEROFTHREADS: - { - text[count] = PhConcatStrings2( - L"Threads: ", - PhaFormatUInt64(SystemStatistics.NumberOfThreads, TRUE)->Buffer - ); - } - break; - case ID_STATUS_NUMBEROFHANDLES: - { - text[count] = PhConcatStrings2( - L"Handles: ", - PhaFormatUInt64(SystemStatistics.NumberOfHandles, TRUE)->Buffer - ); - } - break; - case ID_STATUS_IO_RO: - { - text[count] = PhConcatStrings2( - L"I/O R+O: ", - PhaFormatSize(SystemStatistics.IoReadDelta.Delta + SystemStatistics.IoOtherDelta.Delta, -1)->Buffer - ); - } - break; - case ID_STATUS_IO_W: - { - text[count] = PhConcatStrings2( - L"I/O W: ", - PhaFormatSize(SystemStatistics.IoWriteDelta.Delta, -1)->Buffer - ); - } - break; - case ID_STATUS_MAX_CPU_PROCESS: - { - PPH_PROCESS_ITEM processItem; - - if (SystemStatistics.MaxCpuProcessId && (processItem = PhReferenceProcessItem(SystemStatistics.MaxCpuProcessId))) - { - if (!PH_IS_FAKE_PROCESS_ID(processItem->ProcessId)) - { - text[count] = PhFormatString( - L"%s (%lu): %.2f%%", - processItem->ProcessName->Buffer, - HandleToUlong(processItem->ProcessId), - processItem->CpuUsage * 100 - ); - } - else - { - text[count] = PhFormatString( - L"%s: %.2f%%", - processItem->ProcessName->Buffer, - processItem->CpuUsage * 100 - ); - } - - PhDereferenceObject(processItem); - } - else - { - text[count] = PhCreateString(L"-"); - } - } - break; - case ID_STATUS_MAX_IO_PROCESS: - { - PPH_PROCESS_ITEM processItem; - - if (SystemStatistics.MaxIoProcessId && (processItem = PhReferenceProcessItem(SystemStatistics.MaxIoProcessId))) - { - if (!PH_IS_FAKE_PROCESS_ID(processItem->ProcessId)) - { - text[count] = PhFormatString( - L"%s (%lu): %s", - processItem->ProcessName->Buffer, - HandleToUlong(processItem->ProcessId), - PhaFormatSize(processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta + processItem->IoOtherDelta.Delta, -1)->Buffer - ); - } - else - { - text[count] = PhFormatString( - L"%s: %s", - processItem->ProcessName->Buffer, - PhaFormatSize(processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta + processItem->IoOtherDelta.Delta, -1)->Buffer - ); - } - - PhDereferenceObject(processItem); - } - else - { - text[count] = PhCreateString(L"-"); - } - } - break; - case ID_STATUS_NUMBEROFVISIBLEITEMS: - { - HWND tnHandle = NULL; - - tnHandle = GetCurrentTreeNewHandle(); - - if (tnHandle) - { - ULONG visibleCount = 0; - - visibleCount = TreeNew_GetFlatNodeCount(tnHandle); - - text[count] = PhFormatString( - L"Visible: %lu", - visibleCount - ); - } - else - { - text[count] = PhCreateString( - L"Visible: N/A" - ); - } - } - break; - case ID_STATUS_NUMBEROFSELECTEDITEMS: - { - HWND tnHandle = NULL; - - tnHandle = GetCurrentTreeNewHandle(); - - if (tnHandle) - { - ULONG visibleCount = 0; - ULONG selectedCount = 0; - - visibleCount = TreeNew_GetFlatNodeCount(tnHandle); - - for (ULONG i = 0; i < visibleCount; i++) - { - if (TreeNew_GetFlatNode(tnHandle, i)->Selected) - selectedCount++; - } - - text[count] = PhFormatString( - L"Selected: %lu", - selectedCount - ); - } - else - { - text[count] = PhCreateString( - L"Selected: N/A" - ); - } - } - break; - case ID_STATUS_INTERVALSTATUS: - { - ULONG interval; - - interval = PhGetIntegerSetting(L"UpdateInterval"); - - if (UpdateAutomatically) - { - switch (interval) - { - case 500: - text[count] = PhCreateString(L"Interval: Fast"); - break; - case 1000: - text[count] = PhCreateString(L"Interval: Normal"); - break; - case 2000: - text[count] = PhCreateString(L"Interval: Below normal"); - break; - case 5000: - text[count] = PhCreateString(L"Interval: Slow"); - break; - case 10000: - text[count] = PhCreateString(L"Interval: Very slow"); - break; - } - } - else - { - text[count] = PhCreateString(L"Interval: Paused"); - } - } - break; - } - - if (resetMaxWidths) - StatusBarMaxWidths[count] = 0; - - if (!GetTextExtentPoint32(hdc, text[count]->Buffer, (ULONG)text[count]->Length / sizeof(WCHAR), &size)) - size.cx = 200; - - if (count != 0) - widths[count] = widths[count - 1]; - else - widths[count] = 0; - - width = size.cx + 10; - - if (width <= StatusBarMaxWidths[count]) - { - width = StatusBarMaxWidths[count]; - } - else - { - StatusBarMaxWidths[count] = width; - } - - widths[count] += width; - - count++; - } - - ReleaseDC(StatusBarHandle, hdc); - - SendMessage(StatusBarHandle, SB_SETPARTS, count, (LPARAM)widths); - - for (i = 0; i < count; i++) - { - SendMessage(StatusBarHandle, SB_SETTEXT, i, (LPARAM)text[i]->Buffer); - PhDereferenceObject(text[i]); - } -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * statusbar main + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2011-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" + +HWND StatusBarHandle = NULL; +ULONG StatusBarMaxWidths[MAX_STATUSBAR_ITEMS]; +// Note: no lock is needed because we only ever modify the list on this same thread. +PPH_LIST StatusBarItemList = NULL; +ULONG StatusBarItems[MAX_STATUSBAR_ITEMS] = +{ + // Default items (displayed) + { ID_STATUS_CPUUSAGE }, + { ID_STATUS_PHYSICALMEMORY }, + { ID_STATUS_FREEMEMORY }, + // Available items (hidden) + { ID_STATUS_COMMITCHARGE }, + { ID_STATUS_NUMBEROFPROCESSES }, + { ID_STATUS_NUMBEROFTHREADS }, + { ID_STATUS_NUMBEROFHANDLES }, + { ID_STATUS_NUMBEROFVISIBLEITEMS, }, + { ID_STATUS_NUMBEROFSELECTEDITEMS, }, + { ID_STATUS_INTERVALSTATUS }, + { ID_STATUS_IO_RO }, + { ID_STATUS_IO_W }, + { ID_STATUS_MAX_CPU_PROCESS }, + { ID_STATUS_MAX_IO_PROCESS }, +}; + +VOID StatusBarLoadDefault( + VOID + ) +{ + if (!StatusBarItemList) + StatusBarItemList = PhCreateList(MAX_DEFAULT_STATUSBAR_ITEMS); + + for (ULONG i = 0; i < MAX_DEFAULT_STATUSBAR_ITEMS; i++) + { + PhAddItemList(StatusBarItemList, UlongToPtr(StatusBarItems[i])); + } +} + +VOID StatusBarLoadSettings( + VOID + ) +{ + ULONG64 buttonCount = 0; + PPH_STRING settingsString; + PH_STRINGREF remaining; + PH_STRINGREF part; + + settingsString = PhaGetStringSetting(SETTING_NAME_STATUSBAR_CONFIG); + remaining = settingsString->sr; + + if (remaining.Length == 0) + { + // Load default settings + StatusBarLoadDefault(); + return; + } + + // Query the number of buttons to insert + if (!PhSplitStringRefAtChar(&remaining, '|', &part, &remaining)) + { + // Load default settings + StatusBarLoadDefault(); + return; + } + + if (!PhStringToInteger64(&part, 10, &buttonCount)) + { + // Load default settings + StatusBarLoadDefault(); + return; + } + + StatusBarItemList = PhCreateList((ULONG)buttonCount); + + for (ULONG i = 0; i < (ULONG)buttonCount; i++) + { + PH_STRINGREF idPart; + ULONG64 idInteger; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '|', &idPart, &remaining); + + if (PhStringToInteger64(&idPart, 10, &idInteger)) + { + PhInsertItemList(StatusBarItemList, i, UlongToPtr((ULONG)idInteger)); + } + } +} + +VOID StatusBarSaveSettings( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu|", + StatusBarItemList->Count + ); + + for (ULONG i = 0; i < StatusBarItemList->Count; i++) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu|", + PtrToUlong(StatusBarItemList->Items[i]) + ); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); + PhSetStringSetting2(SETTING_NAME_STATUSBAR_CONFIG, &settingsString->sr); +} + +VOID StatusBarResetSettings( + VOID + ) +{ + PhClearList(StatusBarItemList); + + StatusBarLoadDefault(); +} + +PWSTR StatusBarGetText( + _In_ ULONG CommandID + ) +{ + switch (CommandID) + { + case ID_STATUS_CPUUSAGE: + return L"CPU usage"; + case ID_STATUS_PHYSICALMEMORY: + return L"Physical memory"; + case ID_STATUS_NUMBEROFPROCESSES: + return L"Number of processes"; + case ID_STATUS_COMMITCHARGE: + return L"Commit charge"; + case ID_STATUS_FREEMEMORY: + return L"Free physical memory"; + case ID_STATUS_NUMBEROFTHREADS: + return L"Number of threads"; + case ID_STATUS_NUMBEROFHANDLES: + return L"Number of handles"; + case ID_STATUS_NUMBEROFVISIBLEITEMS: + return L"Number of visible items"; + case ID_STATUS_NUMBEROFSELECTEDITEMS: + return L"Number of selected items"; + case ID_STATUS_INTERVALSTATUS: + return L"Interval status"; + case ID_STATUS_IO_RO: + return L"I/O read+other"; + case ID_STATUS_IO_W: + return L"I/O write"; + case ID_STATUS_MAX_CPU_PROCESS: + return L"Max. CPU process"; + case ID_STATUS_MAX_IO_PROCESS: + return L"Max. I/O process"; + } + + return L"ERROR"; +} + +VOID StatusBarShowMenu( + VOID + ) +{ + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + POINT cursorPos; + + GetCursorPos(&cursorPos); + + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, COMMAND_ID_ENABLE_SEARCHBOX, L"Customize...", NULL, NULL), ULONG_MAX); + + selectedItem = PhShowEMenu( + menu, + PhMainWndHandle, + PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_BOTTOM, + cursorPos.x, + cursorPos.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + StatusBarShowCustomizeDialog(); + + StatusBarUpdate(TRUE); + } + + PhDestroyEMenu(menu); +} + +VOID StatusBarUpdate( + _In_ BOOLEAN ResetMaxWidths + ) +{ + static ULONG64 lastTickCount = 0; + + ULONG count; + ULONG i; + HDC hdc; + BOOLEAN resetMaxWidths = FALSE; + ULONG widths[MAX_STATUSBAR_ITEMS]; + WCHAR text[MAX_STATUSBAR_ITEMS][0x80]; + + if (ProcessesUpdatedCount <= 2) + return; + + if (ResetMaxWidths) + resetMaxWidths = TRUE; + + if (!StatusBarItemList || StatusBarItemList->Count == 0) + { + // The status bar doesn't cope well with 0 parts. + widths[0] = -1; + SendMessage(StatusBarHandle, SB_SETPARTS, 1, (LPARAM)widths); + SendMessage(StatusBarHandle, SB_SETTEXT, 0, (LPARAM)L""); + return; + } + + hdc = GetDC(StatusBarHandle); + SelectFont(hdc, GetWindowFont(StatusBarHandle)); + + // Reset max. widths for Max. CPU Process and Max. I/O Process parts once in a while. + { + ULONG64 tickCount; + + tickCount = NtGetTickCount64(); + + if (tickCount - lastTickCount >= 10 * 1000) + { + resetMaxWidths = TRUE; + lastTickCount = tickCount; + } + } + + count = 0; + memset(text, 0, sizeof(text)); + + for (i = 0; i < StatusBarItemList->Count; i++) + { + SIZE size; + ULONG width; + + switch (PtrToUlong(StatusBarItemList->Items[i])) + { + case ID_STATUS_CPUUSAGE: + { + FLOAT cpuUsage = SystemStatistics.CpuKernelUsage + SystemStatistics.CpuUserUsage; + PH_FORMAT format[3]; + + PhInitFormatS(&format[0], L"CPU usage: "); + PhInitFormatF(&format[1], cpuUsage * 100, 2); + PhInitFormatS(&format[2], L"%"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_COMMITCHARGE: + { + ULONG commitUsage = SystemStatistics.Performance->CommittedPages; + FLOAT commitFraction = (FLOAT)commitUsage / SystemStatistics.Performance->CommitLimit * 100; + PH_FORMAT format[5]; + + PhInitFormatS(&format[0], L"Commit charge: "); + PhInitFormatSize(&format[1], UInt32x32To64(commitUsage, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], commitFraction, 2); + PhInitFormatS(&format[4], L"%)"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_PHYSICALMEMORY: + { + ULONG physicalUsage = PhSystemBasicInformation.NumberOfPhysicalPages - SystemStatistics.Performance->AvailablePages; + FLOAT physicalFraction = (FLOAT)physicalUsage / PhSystemBasicInformation.NumberOfPhysicalPages * 100; + PH_FORMAT format[5]; + + PhInitFormatS(&format[0], L"Physical memory: "); + PhInitFormatSize(&format[1], UInt32x32To64(physicalUsage, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], physicalFraction, 2); + PhInitFormatS(&format[4], L"%)"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_FREEMEMORY: + { + ULONG physicalFree = SystemStatistics.Performance->AvailablePages; + FLOAT physicalFreeFraction = (FLOAT)physicalFree / PhSystemBasicInformation.NumberOfPhysicalPages * 100; + PH_FORMAT format[5]; + + PhInitFormatS(&format[0], L"Free memory: "); + PhInitFormatSize(&format[1], UInt32x32To64(physicalFree, PAGE_SIZE)); + PhInitFormatS(&format[2], L" ("); + PhInitFormatF(&format[3], physicalFreeFraction, 2); + PhInitFormatS(&format[4], L"%)"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_NUMBEROFPROCESSES: + { + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"Processes: "); + PhInitFormatI64UGroupDigits(&format[1], SystemStatistics.NumberOfProcesses); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_NUMBEROFTHREADS: + { + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"Threads: "); + PhInitFormatI64UGroupDigits(&format[1], SystemStatistics.NumberOfThreads); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_NUMBEROFHANDLES: + { + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"Handles: "); + PhInitFormatI64UGroupDigits(&format[1], SystemStatistics.NumberOfHandles); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_IO_RO: + { + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"I/O R+O: "); + PhInitFormatSize(&format[1], (SystemStatistics.IoReadDelta.Delta + SystemStatistics.IoOtherDelta.Delta)); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_IO_W: + { + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"I/O W: "); + PhInitFormatSize(&format[1], SystemStatistics.IoWriteDelta.Delta); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + case ID_STATUS_MAX_CPU_PROCESS: + { + PPH_PROCESS_ITEM processItem; + + if (SystemStatistics.MaxCpuProcessId && (processItem = PhReferenceProcessItem(SystemStatistics.MaxCpuProcessId))) + { + if (!PH_IS_FAKE_PROCESS_ID(processItem->ProcessId)) + { + PH_FORMAT format[6]; + + PhInitFormatSR(&format[0], processItem->ProcessName->sr); + PhInitFormatS(&format[1], L" ("); + PhInitFormatI64U(&format[2], HandleToUlong(processItem->ProcessId)); + PhInitFormatS(&format[3], L"): "); + PhInitFormatF(&format[4], processItem->CpuUsage * 100, 2); + PhInitFormatS(&format[5], L"%"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + else + { + PH_FORMAT format[4]; + + PhInitFormatSR(&format[0], processItem->ProcessName->sr); + PhInitFormatS(&format[1], L": "); + PhInitFormatF(&format[2], processItem->CpuUsage * 100, 2); + PhInitFormatS(&format[3], L"%)"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + + PhDereferenceObject(processItem); + } + else + { + PH_FORMAT format[1]; + + PhInitFormatS(&format[0], L"-"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + } + break; + case ID_STATUS_MAX_IO_PROCESS: + { + PPH_PROCESS_ITEM processItem; + + if (SystemStatistics.MaxIoProcessId && (processItem = PhReferenceProcessItem(SystemStatistics.MaxIoProcessId))) + { + if (!PH_IS_FAKE_PROCESS_ID(processItem->ProcessId)) + { + PH_FORMAT format[5]; + + PhInitFormatSR(&format[0], processItem->ProcessName->sr); + PhInitFormatS(&format[1], L" ("); + PhInitFormatI64U(&format[2], HandleToUlong(processItem->ProcessId)); + PhInitFormatS(&format[3], L"): "); + PhInitFormatSize(&format[4], processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta + processItem->IoOtherDelta.Delta); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + else + { + PH_FORMAT format[3]; + + PhInitFormatSR(&format[0], processItem->ProcessName->sr); + PhInitFormatS(&format[1], L": "); + PhInitFormatSize(&format[2], processItem->IoReadDelta.Delta + processItem->IoWriteDelta.Delta + processItem->IoOtherDelta.Delta); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + + PhDereferenceObject(processItem); + } + else + { + PH_FORMAT format[1]; + + PhInitFormatS(&format[0], L"-"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + } + break; + case ID_STATUS_NUMBEROFVISIBLEITEMS: + { + HWND tnHandle; + + tnHandle = GetCurrentTreeNewHandle(); + + if (tnHandle) + { + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"Visible: "); + PhInitFormatI64UGroupDigits(&format[1], TreeNew_GetFlatNodeCount(tnHandle)); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + else + { + PH_FORMAT format[1]; + + PhInitFormatS(&format[0], L"Visible: N/A"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + } + break; + case ID_STATUS_NUMBEROFSELECTEDITEMS: + { + HWND tnHandle; + + tnHandle = GetCurrentTreeNewHandle(); + + if (tnHandle) + { + ULONG i; + ULONG visibleCount; + ULONG selectedCount; + PH_FORMAT format[2]; + + visibleCount = TreeNew_GetFlatNodeCount(tnHandle); + selectedCount = 0; + + for (i = 0; i < visibleCount; i++) + { + if (TreeNew_GetFlatNode(tnHandle, i)->Selected) + selectedCount++; + } + + PhInitFormatS(&format[0], L"Selected: "); + PhInitFormatI64UGroupDigits(&format[1], selectedCount); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + else + { + PH_FORMAT format[1]; + + PhInitFormatS(&format[0], L"Selected: N/A"); + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + } + break; + case ID_STATUS_INTERVALSTATUS: + { + PH_FORMAT format[1]; + + if (UpdateAutomatically) + { + switch (PhGetIntegerSetting(L"UpdateInterval")) + { + case 500: + PhInitFormatS(&format[0], L"Interval: Fast"); + break; + case 1000: + PhInitFormatS(&format[0], L"Interval: Normal"); + break; + case 2000: + PhInitFormatS(&format[0], L"Interval: Below normal"); + break; + case 5000: + PhInitFormatS(&format[0], L"Interval: Slow"); + break; + case 10000: + PhInitFormatS(&format[0], L"Interval: Very slow"); + break; + default: + PhInitFormatS(&format[0], L"Interval: N/A"); + break; + } + } + else + { + PhInitFormatS(&format[0], L"Interval: Paused"); + } + + PhFormatToBuffer(format, RTL_NUMBER_OF(format), text[count], sizeof(text[count]), NULL); + } + break; + } + + if (!GetTextExtentPoint32(hdc, text[count], (INT)PhCountStringZ(text[count]), &size)) + size.cx = 200; + + if (count != 0) + widths[count] = widths[count - 1]; + else + widths[count] = 0; + + width = size.cx + 10; + + if (resetMaxWidths) + StatusBarMaxWidths[count] = 0; + + if (width <= StatusBarMaxWidths[count]) + width = StatusBarMaxWidths[count]; + else + StatusBarMaxWidths[count] = width; + + widths[count] += width; + + count++; + } + + ReleaseDC(StatusBarHandle, hdc); + + SendMessage(StatusBarHandle, SB_SETPARTS, count, (LPARAM)widths); + + for (i = 0; i < count; i++) + { + SendMessage(StatusBarHandle, SB_SETTEXT, i, (LPARAM)text[i]); + } +} diff --git a/plugins/ToolStatus/toolbar.c b/plugins/ToolStatus/toolbar.c index aeadee129653..d7fd59dbc891 100644 --- a/plugins/ToolStatus/toolbar.c +++ b/plugins/ToolStatus/toolbar.c @@ -1,852 +1,877 @@ -/* - * Process Hacker ToolStatus - - * main toolbar - * - * Copyright (C) 2011-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "toolstatus.h" -#include "commonutil.h" - -SIZE ToolBarImageSize = { 16, 16 }; -HIMAGELIST ToolBarImageList = NULL; - -TBBUTTON ToolbarButtons[MAX_TOOLBAR_ITEMS] = -{ - // Default toolbar buttons (displayed) - { I_IMAGECALLBACK, PHAPP_ID_VIEW_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, PHAPP_ID_HACKER_OPTIONS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { 0, 0, 0, BTNS_SEP, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, PHAPP_ID_HACKER_FINDHANDLESORDLLS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, PHAPP_ID_VIEW_SYSTEMINFORMATION, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { 0, 0, 0, BTNS_SEP, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, TIDC_FINDWINDOW, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, TIDC_FINDWINDOWTHREAD, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, TIDC_FINDWINDOWKILL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - // Available toolbar buttons (hidden) - { I_IMAGECALLBACK, PHAPP_ID_VIEW_ALWAYSONTOP, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, - { I_IMAGECALLBACK, TIDC_POWERMENUDROPDOWN, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN | BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT,{ 0 }, 0, 0 }, - { I_IMAGECALLBACK, PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT,{ 0 }, 0, 0 }, -}; - -VOID RebarBandInsert( - _In_ UINT BandID, - _In_ HWND HwndChild, - _In_ UINT cxMinChild, - _In_ UINT cyMinChild - ) -{ - UINT index; - REBARBANDINFO rebarBandInfo = - { - sizeof(REBARBANDINFO), - RBBIM_STYLE | RBBIM_ID | RBBIM_CHILD | RBBIM_CHILDSIZE, - RBBS_USECHEVRON // RBBS_NOGRIPPER | RBBS_HIDETITLE | RBBS_TOPALIGN - }; - - rebarBandInfo.wID = BandID; - rebarBandInfo.hwndChild = HwndChild; - rebarBandInfo.cxMinChild = cxMinChild; - rebarBandInfo.cyMinChild = cyMinChild; - - if (ToolStatusConfig.ToolBarLocked) - { - rebarBandInfo.fStyle |= RBBS_NOGRIPPER; - } - - if ((index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, REBAR_BAND_ID_SEARCHBOX, 0)) != -1) - { - SendMessage(RebarHandle, RB_INSERTBAND, (WPARAM)index, (LPARAM)&rebarBandInfo); - } - else - { - SendMessage(RebarHandle, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rebarBandInfo); - } -} - -VOID RebarBandRemove( - _In_ UINT BandID - ) -{ - UINT index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, (WPARAM)BandID, 0); - - if (index == -1) - return; - - SendMessage(RebarHandle, RB_DELETEBAND, (WPARAM)index, 0); -} - -BOOLEAN RebarBandExists( - _In_ UINT BandID - ) -{ - UINT index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, (WPARAM)BandID, 0); - - if (index != -1) - return TRUE; - - return FALSE; -} - -VOID RebarLoadSettings( - VOID - ) -{ - if (ToolStatusConfig.ToolBarEnabled && !ToolBarImageList) - { - ToolBarImageList = ImageList_Create(ToolBarImageSize.cx, ToolBarImageSize.cy, ILC_COLOR32, 0, 0); - } - - if (ToolStatusConfig.ToolBarEnabled && !RebarHandle) - { - REBARINFO rebarInfo = { sizeof(REBARINFO) }; - ULONG toolbarButtonSize; - - RebarHandle = CreateWindowEx( - WS_EX_TOOLWINDOW, - REBARCLASSNAME, - NULL, - WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP | RBS_VARHEIGHT | RBS_AUTOSIZE, // CCS_NOPARENTALIGN | RBS_FIXEDORDER - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - - ToolBarHandle = CreateWindowEx( - 0, - TOOLBARCLASSNAME, - NULL, - WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NODIVIDER | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT | TBSTYLE_TOOLTIPS | TBSTYLE_AUTOSIZE, // TBSTYLE_CUSTOMERASE TBSTYLE_ALTDRAG - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - RebarHandle, - NULL, - NULL, - NULL - ); - - // Set the toolbar info with no imagelist. - SendMessage(RebarHandle, RB_SETBARINFO, 0, (LPARAM)&rebarInfo); - // Set the toolbar struct size. - SendMessage(ToolBarHandle, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); - // Set the toolbar extended toolbar styles. - SendMessage(ToolBarHandle, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DOUBLEBUFFER | TBSTYLE_EX_MIXEDBUTTONS | TBSTYLE_EX_HIDECLIPPEDBUTTONS); - // Configure the toolbar imagelist. - SendMessage(ToolBarHandle, TB_SETIMAGELIST, 0, (LPARAM)ToolBarImageList); - // Add the buttons to the toolbar. - ToolbarLoadButtonSettings(); - // Resize the toolbar. - SendMessage(ToolBarHandle, TB_AUTOSIZE, 0, 0); - // Query the toolbar width and height. - //SendMessage(ToolBarHandle, TB_GETMAXSIZE, 0, (LPARAM)&toolbarSize); - toolbarButtonSize = (ULONG)SendMessage(ToolBarHandle, TB_GETBUTTONSIZE, 0, 0); - - // Enable theming - // SendMessage(RebarHandle, RB_SETWINDOWTHEME, 0, (LPARAM)L"Media"); //Media/Communications/BrowserTabBar/Help - // SendMessage(ToolBarHandle, TB_SETWINDOWTHEME, 0, (LPARAM)L"Media"); //Media/Communications/BrowserTabBar/Help - - // Inset the toolbar into the rebar control. - RebarBandInsert(REBAR_BAND_ID_TOOLBAR, ToolBarHandle, LOWORD(toolbarButtonSize), HIWORD(toolbarButtonSize)); - } - - if (ToolStatusConfig.SearchBoxEnabled && !SearchboxHandle) - { - SearchboxText = PhReferenceEmptyString(); - ProcessTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportProcessTreeList(), (PPH_TN_FILTER_FUNCTION)ProcessTreeFilterCallback, NULL); - ServiceTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), (PPH_TN_FILTER_FUNCTION)ServiceTreeFilterCallback, NULL); - NetworkTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportNetworkTreeList(), (PPH_TN_FILTER_FUNCTION)NetworkTreeFilterCallback, NULL); - - CreateSearchboxControl(); - } - - if (ToolStatusConfig.StatusBarEnabled && !StatusBarHandle) - { - // Create the StatusBar window. - StatusBarHandle = CreateWindowEx( - 0, - STATUSCLASSNAME, - NULL, - WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - PhMainWndHandle, - NULL, - NULL, - NULL - ); - } - - // Hide or show controls (Note: don't unload or remove at runtime). - if (ToolStatusConfig.ToolBarEnabled) - { - if (RebarHandle && !IsWindowVisible(RebarHandle)) - ShowWindow(RebarHandle, SW_SHOW); - } - else - { - if (RebarHandle && IsWindowVisible(RebarHandle)) - ShowWindow(RebarHandle, SW_HIDE); - } - - if (ToolStatusConfig.SearchBoxEnabled && RebarHandle && SearchboxHandle) - { - UINT height = (UINT)SendMessage(RebarHandle, RB_GETROWHEIGHT, 0, 0); - - // Add the Searchbox band into the rebar control. - if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PhMultiplyDivide(180, PhGlobalDpi, 96), height); - - if (!IsWindowVisible(SearchboxHandle)) - ShowWindow(SearchboxHandle, SW_SHOW); - - if (SearchBoxDisplayMode == SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) - { - if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); - } - } - else - { - // Remove the Searchbox band from the rebar control. - if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) - RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); - - if (SearchboxHandle) - { - // Clear search text and reset search filters. - SetFocus(SearchboxHandle); - Static_SetText(SearchboxHandle, L""); - - if (IsWindowVisible(SearchboxHandle)) - ShowWindow(SearchboxHandle, SW_HIDE); - } - } - - if (ToolStatusConfig.StatusBarEnabled) - { - if (StatusBarHandle && !IsWindowVisible(StatusBarHandle)) - ShowWindow(StatusBarHandle, SW_SHOW); - } - else - { - if (StatusBarHandle && IsWindowVisible(StatusBarHandle)) - ShowWindow(StatusBarHandle, SW_HIDE); - } - - ToolbarCreateGraphs(); -} - -VOID ToolbarLoadSettings( - VOID - ) -{ - RebarLoadSettings(); - - if (ToolStatusConfig.ToolBarEnabled && ToolBarHandle) - { - INT index = 0; - INT buttonCount = 0; - - buttonCount = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); - - for (index = 0; index < buttonCount; index++) - { - TBBUTTONINFO buttonInfo = - { - sizeof(TBBUTTONINFO), - TBIF_BYINDEX | TBIF_STYLE | TBIF_COMMAND | TBIF_STATE - }; - - // Get settings for first button - if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1) - break; - - // Skip separator buttons - if (buttonInfo.fsStyle == BTNS_SEP) - continue; - - // Add the button text - buttonInfo.dwMask |= TBIF_TEXT; - buttonInfo.pszText = ToolbarGetText(buttonInfo.idCommand); - - switch (DisplayStyle) - { - case TOOLBAR_DISPLAY_STYLE_IMAGEONLY: - buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; - break; - case TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT: - { - switch (buttonInfo.idCommand) - { - case PHAPP_ID_VIEW_REFRESH: - case PHAPP_ID_HACKER_OPTIONS: - case PHAPP_ID_HACKER_FINDHANDLESORDLLS: - case PHAPP_ID_VIEW_SYSTEMINFORMATION: - buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; - break; - default: - buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; - break; - } - } - break; - case TOOLBAR_DISPLAY_STYLE_ALLTEXT: - buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; - break; - } - - switch (buttonInfo.idCommand) - { - case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: - { - if (WINDOWS_HAS_UAC && PhGetOwnTokenAttributes().Elevated) - { - buttonInfo.fsState |= TBSTATE_HIDDEN; - } - } - break; - case PHAPP_ID_VIEW_ALWAYSONTOP: - { - // Set the pressed state - if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) - { - buttonInfo.fsState |= TBSTATE_PRESSED; - } - } - break; - case TIDC_POWERMENUDROPDOWN: - { - buttonInfo.fsStyle |= BTNS_WHOLEDROPDOWN; - } - break; - } - - // Set updated button info - SendMessage(ToolBarHandle, TB_SETBUTTONINFO, index, (LPARAM)&buttonInfo); - } - - // Resize the toolbar - SendMessage(ToolBarHandle, TB_AUTOSIZE, 0, 0); - } - - if (ToolStatusConfig.ToolBarEnabled && RebarHandle && ToolBarHandle) - { - UINT index; - REBARBANDINFO rebarBandInfo = - { - sizeof(REBARBANDINFO), - RBBIM_IDEALSIZE - }; - - index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, REBAR_BAND_ID_TOOLBAR, 0); - - // Get settings for Rebar band. - if (SendMessage(RebarHandle, RB_GETBANDINFO, index, (LPARAM)&rebarBandInfo) != -1) - { - SIZE idealWidth; - - // Reset the cxIdeal for the Chevron - SendMessage(ToolBarHandle, TB_GETIDEALSIZE, FALSE, (LPARAM)&idealWidth); - - rebarBandInfo.cxIdeal = idealWidth.cx; - - SendMessage(RebarHandle, RB_SETBANDINFO, index, (LPARAM)&rebarBandInfo); - } - } - - // Invoke the LayoutPaddingCallback. - SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); -} - -VOID ToolbarResetSettings( - VOID - ) -{ - // Remove all buttons. - INT buttonCount = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); - - while (buttonCount--) - SendMessage(ToolBarHandle, TB_DELETEBUTTON, (WPARAM)buttonCount, 0); - - // Add the default buttons. - SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); -} - -PWSTR ToolbarGetText( - _In_ INT CommandID - ) -{ - switch (CommandID) - { - case PHAPP_ID_VIEW_REFRESH: - return L"Refresh"; - case PHAPP_ID_HACKER_OPTIONS: - return L"Options"; - case PHAPP_ID_HACKER_FINDHANDLESORDLLS: - return L"Find handles or DLLs"; - case PHAPP_ID_VIEW_SYSTEMINFORMATION: - return L"System information"; - case TIDC_FINDWINDOW: - return L"Find window"; - case TIDC_FINDWINDOWTHREAD: - return L"Find window and thread"; - case TIDC_FINDWINDOWKILL: - return L"Find window and kill"; - case PHAPP_ID_VIEW_ALWAYSONTOP: - return L"Always on top"; - case TIDC_POWERMENUDROPDOWN: - return L"Computer"; - case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: - return L"Show details for all processes"; - } - - return L"ERROR"; -} - -HBITMAP ToolbarLoadImageFromIcon( - _In_ ULONG Width, - _In_ ULONG Height, - _In_ PWSTR Name - ) -{ - HICON icon = PhLoadIcon(PluginInstance->DllBase, Name, 0, Width, Height); - HBITMAP bitmap = PhIconToBitmap(icon, Width, Height); - DestroyIcon(icon); - return bitmap; -} - -HBITMAP ToolbarGetImage( - _In_ INT CommandID - ) -{ - switch (CommandID) - { - case PHAPP_ID_VIEW_REFRESH: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_ARROW_REFRESH_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_ARROW_REFRESH)); - } - - return toolbarBitmap; - } - break; - case PHAPP_ID_HACKER_OPTIONS: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_COG_EDIT_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_COG_EDIT)); - } - - return toolbarBitmap; - } - break; - case PHAPP_ID_HACKER_FINDHANDLESORDLLS: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_FIND_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_FIND)); - } - - return toolbarBitmap; - } - break; - case PHAPP_ID_VIEW_SYSTEMINFORMATION: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CHART_LINE_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_CHART_LINE)); - } - - return toolbarBitmap; - } - break; - case TIDC_FINDWINDOW: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_TBAPPLICATION)); - } - - return toolbarBitmap; - } - break; - case TIDC_FINDWINDOWTHREAD: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GO_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_APPLICATION_GO)); - } - - return toolbarBitmap; - } - break; - case TIDC_FINDWINDOWKILL: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CROSS_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_CROSS)); - } - - return toolbarBitmap; - } - break; - case PHAPP_ID_VIEW_ALWAYSONTOP: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GET_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_APPLICATION_GET)); - } - - return toolbarBitmap; - } - break; - case TIDC_POWERMENUDROPDOWN: - { - HBITMAP toolbarBitmap = NULL; - - if (ToolStatusConfig.ModernIcons) - { - toolbarBitmap = LoadImageFromResources(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_POWER_MODERN), FALSE); - } - else - { - toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_LIGHTBULB_OFF)); - } - - return toolbarBitmap; - } - break; - case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: - { - HICON shieldIcon; - HBITMAP toolbarBitmap = NULL; - - if (shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL, 0, 0)) - { - toolbarBitmap = PhIconToBitmap(shieldIcon, ToolBarImageSize.cx, ToolBarImageSize.cy); - DestroyIcon(shieldIcon); - } - - return toolbarBitmap; - } - break; - } - - return NULL; -} - -VOID ToolbarLoadButtonSettings( - VOID - ) -{ - INT count; - ULONG64 countInteger; - PPH_STRING settingsString; - PTBBUTTON buttonArray; - PH_STRINGREF remaining; - PH_STRINGREF part; - - settingsString = PhaGetStringSetting(SETTING_NAME_TOOLBAR_CONFIG); - remaining = settingsString->sr; - - if (remaining.Length == 0) - { - // Load default settings - SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); - return; - } - - // Query the number of buttons to insert - if (!PhSplitStringRefAtChar(&remaining, '|', &part, &remaining)) - { - // Load default settings - SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); - return; - } - - if (!PhStringToInteger64(&part, 10, &countInteger)) - { - // Load default settings - SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); - return; - } - - count = (INT)countInteger; - - // Allocate the button array - buttonArray = PhAllocate(count * sizeof(TBBUTTON)); - memset(buttonArray, 0, count * sizeof(TBBUTTON)); - - for (INT index = 0; index < count; index++) - { - ULONG64 commandInteger; - PH_STRINGREF commandIdPart; - - if (remaining.Length == 0) - break; - - PhSplitStringRefAtChar(&remaining, '|', &commandIdPart, &remaining); - PhStringToInteger64(&commandIdPart, 10, &commandInteger); - - buttonArray[index].idCommand = (INT)commandInteger; - //buttonArray[index].iBitmap = I_IMAGECALLBACK; - buttonArray[index].fsState = TBSTATE_ENABLED; - - if (commandInteger) - { - buttonArray[index].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; - } - else - { - buttonArray[index].fsStyle = BTNS_SEP; - } - - // Pre-cache the image in the Toolbar array on startup. - for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++) - { - if (ToolbarButtons[i].idCommand == buttonArray[index].idCommand) - { - HBITMAP bitmap; - - bitmap = ToolbarGetImage(ToolbarButtons[i].idCommand); - - // Add the image, cache the value in the ToolbarButtons array, set the bitmap index. - buttonArray[index].iBitmap = ToolbarButtons[i].iBitmap = ImageList_Add( - ToolBarImageList, - bitmap, - NULL - ); - - DeleteObject(bitmap); - break; - } - } - } - - SendMessage(ToolBarHandle, TB_ADDBUTTONS, count, (LPARAM)buttonArray); - - PhFree(buttonArray); -} - -VOID ToolbarSaveButtonSettings( - VOID - ) -{ - INT index = 0; - INT count = 0; - PPH_STRING settingsString; - PH_STRING_BUILDER stringBuilder; - - PhInitializeStringBuilder(&stringBuilder, 100); - - count = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); - - PhAppendFormatStringBuilder( - &stringBuilder, - L"%d|", - count - ); - - for (index = 0; index < count; index++) - { - TBBUTTONINFO buttonInfo = - { - sizeof(TBBUTTONINFO), - TBIF_BYINDEX | TBIF_IMAGE | TBIF_STYLE | TBIF_COMMAND - }; - - if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1) - break; - - PhAppendFormatStringBuilder( - &stringBuilder, - L"%d|", - buttonInfo.idCommand - ); - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); - PhSetStringSetting2(SETTING_NAME_TOOLBAR_CONFIG, &settingsString->sr); -} - -VOID ReBarLoadLayoutSettings( - VOID - ) -{ - UINT index = 0; - UINT count = 0; - PPH_STRING settingsString; - PH_STRINGREF remaining; - - settingsString = PhGetStringSetting(SETTING_NAME_REBAR_CONFIG); - remaining = settingsString->sr; - - if (remaining.Length == 0) - return; - - count = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0); - - for (index = 0; index < count; index++) - { - PH_STRINGREF idPart; - PH_STRINGREF cxPart; - PH_STRINGREF stylePart; - ULONG64 idInteger; - ULONG64 cxInteger; - ULONG64 styleInteger; - UINT oldBandIndex; - REBARBANDINFO rebarBandInfo = - { - sizeof(REBARBANDINFO), - RBBIM_STYLE | RBBIM_SIZE - }; - - if (remaining.Length == 0) - break; - - PhSplitStringRefAtChar(&remaining, '|', &idPart, &remaining); - PhSplitStringRefAtChar(&remaining, '|', &cxPart, &remaining); - PhSplitStringRefAtChar(&remaining, '|', &stylePart, &remaining); - - PhStringToInteger64(&idPart, 10, &idInteger); - PhStringToInteger64(&cxPart, 10, &cxInteger); - PhStringToInteger64(&stylePart, 10, &styleInteger); - - if ((oldBandIndex = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, (UINT)idInteger, 0)) == -1) - break; - - if (oldBandIndex != index) - { - SendMessage(RebarHandle, RB_MOVEBAND, oldBandIndex, index); - } - - if (SendMessage(RebarHandle, RB_GETBANDINFO, index, (LPARAM)&rebarBandInfo)) - { - rebarBandInfo.cx = (UINT)cxInteger; - rebarBandInfo.fStyle |= (UINT)styleInteger; - - SendMessage(RebarHandle, RB_SETBANDINFO, index, (LPARAM)&rebarBandInfo); - } - } -} - -VOID ReBarSaveLayoutSettings( - VOID - ) -{ - UINT index = 0; - UINT count = 0; - PPH_STRING settingsString; - PH_STRING_BUILDER stringBuilder; - - PhInitializeStringBuilder(&stringBuilder, 100); - - count = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0); - - for (index = 0; index < count; index++) - { - REBARBANDINFO rebarBandInfo = - { - sizeof(REBARBANDINFO), - RBBIM_STYLE | RBBIM_SIZE | RBBIM_ID - }; - - SendMessage(RebarHandle, RB_GETBANDINFO, index, (LPARAM)&rebarBandInfo); - - if (rebarBandInfo.fStyle & RBBS_GRIPPERALWAYS) - { - rebarBandInfo.fStyle &= ~RBBS_GRIPPERALWAYS; - } - - if (rebarBandInfo.fStyle & RBBS_NOGRIPPER) - { - rebarBandInfo.fStyle &= ~RBBS_NOGRIPPER; - } - - if (rebarBandInfo.fStyle & RBBS_FIXEDSIZE) - { - rebarBandInfo.fStyle &= ~RBBS_FIXEDSIZE; - } - - PhAppendFormatStringBuilder( - &stringBuilder, - L"%u|%u|%u|", - rebarBandInfo.wID, - rebarBandInfo.cx, - rebarBandInfo.fStyle - ); - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); - PhSetStringSetting2(SETTING_NAME_REBAR_CONFIG, &settingsString->sr); -} \ No newline at end of file +/* + * Process Hacker ToolStatus - + * main toolbar + * + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "toolstatus.h" +#include "commonutil.h" + +SIZE ToolBarImageSize = { 16, 16 }; +HIMAGELIST ToolBarImageList = NULL; +HFONT ToolStatusWindowFont = NULL; +TBBUTTON ToolbarButtons[MAX_TOOLBAR_ITEMS] = +{ + // Default toolbar buttons (displayed) + { I_IMAGECALLBACK, PHAPP_ID_VIEW_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, PHAPP_ID_HACKER_OPTIONS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { 0, 0, 0, BTNS_SEP, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, PHAPP_ID_HACKER_FINDHANDLESORDLLS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, PHAPP_ID_VIEW_SYSTEMINFORMATION, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { 0, 0, 0, BTNS_SEP, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, TIDC_FINDWINDOW, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, TIDC_FINDWINDOWTHREAD, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, TIDC_FINDWINDOWKILL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + // Available toolbar buttons (hidden) + { I_IMAGECALLBACK, PHAPP_ID_VIEW_ALWAYSONTOP, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT, { 0 }, 0, 0 }, + { I_IMAGECALLBACK, TIDC_POWERMENUDROPDOWN, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN | BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT,{ 0 }, 0, 0 }, + { I_IMAGECALLBACK, PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT,{ 0 }, 0, 0 }, +}; + +VOID RebarBandInsert( + _In_ UINT BandID, + _In_ HWND HwndChild, + _In_ UINT cxMinChild, + _In_ UINT cyMinChild + ) +{ + UINT index; + REBARBANDINFO rebarBandInfo = + { + sizeof(REBARBANDINFO), + RBBIM_STYLE | RBBIM_ID | RBBIM_CHILD | RBBIM_CHILDSIZE, + RBBS_USECHEVRON | RBBS_VARIABLEHEIGHT // RBBS_NOGRIPPER | RBBS_HIDETITLE | RBBS_TOPALIGN + }; + + rebarBandInfo.wID = BandID; + rebarBandInfo.hwndChild = HwndChild; + rebarBandInfo.cxMinChild = cxMinChild; + rebarBandInfo.cyMinChild = cyMinChild; + + if (ToolStatusConfig.ToolBarLocked) + { + rebarBandInfo.fStyle |= RBBS_NOGRIPPER; + } + + if ((index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, REBAR_BAND_ID_SEARCHBOX, 0)) != UINT_MAX) + { + SendMessage(RebarHandle, RB_INSERTBAND, (WPARAM)index, (LPARAM)&rebarBandInfo); + } + else + { + SendMessage(RebarHandle, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rebarBandInfo); + } +} + +VOID RebarBandRemove( + _In_ UINT BandID + ) +{ + UINT index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, (WPARAM)BandID, 0); + + if (index == UINT_MAX) + return; + + SendMessage(RebarHandle, RB_DELETEBAND, (WPARAM)index, 0); +} + +BOOLEAN RebarBandExists( + _In_ UINT BandID + ) +{ + UINT index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, (WPARAM)BandID, 0); + + if (index != UINT_MAX) + return TRUE; + + return FALSE; +} + +VOID RebarLoadSettings( + VOID + ) +{ + if (ToolStatusConfig.ToolBarEnabled && !ToolBarImageList) + { + ToolBarImageSize.cx = GetSystemMetrics(SM_CXSMICON); + ToolBarImageSize.cy = GetSystemMetrics(SM_CYSMICON); + ToolBarImageList = ImageList_Create(ToolBarImageSize.cx, ToolBarImageSize.cy, ILC_COLOR32, 0, 0); + + HFONT newFont; + + if (newFont = (HFONT)SendMessage(PhMainWndHandle, WM_PH_GET_FONT, 0, 0)) + { + if (ToolStatusWindowFont) DeleteFont(ToolStatusWindowFont); + ToolStatusWindowFont = newFont; + } + } + + if (ToolStatusConfig.ToolBarEnabled && !RebarHandle) + { + RebarHandle = CreateWindowEx( + WS_EX_TOOLWINDOW, + REBARCLASSNAME, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP | RBS_VARHEIGHT | RBS_AUTOSIZE, // CCS_NOPARENTALIGN + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + PhMainWndHandle, + NULL, + NULL, + NULL + ); + + ToolBarHandle = CreateWindowEx( + 0, + TOOLBARCLASSNAME, + NULL, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NOPARENTALIGN | CCS_NODIVIDER | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT | TBSTYLE_TOOLTIPS | TBSTYLE_AUTOSIZE, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + RebarHandle, + NULL, + NULL, + NULL + ); + + // Set the rebar info with no imagelist. + SendMessage(RebarHandle, RB_SETBARINFO, 0, (LPARAM)&(REBARINFO){ sizeof(REBARINFO) }); + // Set the toolbar struct size. + SendMessage(ToolBarHandle, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); + // Set the toolbar extended toolbar styles. + SendMessage(ToolBarHandle, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DOUBLEBUFFER | TBSTYLE_EX_MIXEDBUTTONS | TBSTYLE_EX_HIDECLIPPEDBUTTONS); + + // Add the buttons to the toolbar. + ToolbarLoadButtonSettings(); + // Configure the toolbar imagelist. + SendMessage(ToolBarHandle, TB_SETIMAGELIST, 0, (LPARAM)ToolBarImageList); + // Configure the toolbar font. + SetWindowFont(ToolBarHandle, ToolStatusWindowFont, FALSE); + // Resize the toolbar. + SendMessage(ToolBarHandle, TB_AUTOSIZE, 0, 0); + + // Inset the toolbar into the rebar control. + ULONG toolbarButtonSize = (ULONG)SendMessage(ToolBarHandle, TB_GETBUTTONSIZE, 0, 0); + RebarBandInsert(REBAR_BAND_ID_TOOLBAR, ToolBarHandle, LOWORD(toolbarButtonSize), HIWORD(toolbarButtonSize)); + } + + if (ToolStatusConfig.SearchBoxEnabled && !SearchboxHandle) + { + SearchboxText = PhReferenceEmptyString(); + ProcessTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportProcessTreeList(), ProcessTreeFilterCallback, NULL); + ServiceTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportServiceTreeList(), ServiceTreeFilterCallback, NULL); + NetworkTreeFilterEntry = PhAddTreeNewFilter(PhGetFilterSupportNetworkTreeList(), NetworkTreeFilterCallback, NULL); + + if (SearchboxHandle = CreateWindowEx( + WS_EX_CLIENTEDGE, + WC_EDIT, + NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | ES_LEFT | ES_AUTOHSCROLL | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + RebarHandle, + NULL, + NULL, + NULL + )) + { + PhCreateSearchControl(RebarHandle, SearchboxHandle, L"Search Processes (Ctrl+K)"); + } + } + + if (ToolStatusConfig.StatusBarEnabled && !StatusBarHandle) + { + StatusBarHandle = CreateWindowEx( + 0, + STATUSCLASSNAME, + NULL, + WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + PhMainWndHandle, + NULL, + NULL, + NULL + ); + + if (StatusBarHandle && PhGetIntegerSetting(L"EnableThemeSupport")) + { + PhInitializeWindowThemeStatusBar(StatusBarHandle); + } + } + + // Hide or show controls (Note: don't unload or remove at runtime). + if (ToolStatusConfig.ToolBarEnabled) + { + if (RebarHandle && !IsWindowVisible(RebarHandle)) + ShowWindow(RebarHandle, SW_SHOW); + } + else + { + if (RebarHandle && IsWindowVisible(RebarHandle)) + ShowWindow(RebarHandle, SW_HIDE); + } + + if (ToolStatusConfig.SearchBoxEnabled && RebarHandle && SearchboxHandle) + { + UINT height = (UINT)SendMessage(RebarHandle, RB_GETROWHEIGHT, 0, 0); + + // Add the Searchbox band into the rebar control. + if (!RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + RebarBandInsert(REBAR_BAND_ID_SEARCHBOX, SearchboxHandle, PH_SCALE_DPI(180), height); + + if (!IsWindowVisible(SearchboxHandle)) + ShowWindow(SearchboxHandle, SW_SHOW); + + if (SearchBoxDisplayMode == SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE) + { + if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); + } + } + else + { + // Remove the Searchbox band from the rebar control. + if (RebarBandExists(REBAR_BAND_ID_SEARCHBOX)) + RebarBandRemove(REBAR_BAND_ID_SEARCHBOX); + + if (SearchboxHandle) + { + // Clear search text and reset search filters. + SetFocus(SearchboxHandle); + Static_SetText(SearchboxHandle, L""); + + if (IsWindowVisible(SearchboxHandle)) + ShowWindow(SearchboxHandle, SW_HIDE); + } + } + + if (ToolStatusConfig.StatusBarEnabled) + { + if (StatusBarHandle && !IsWindowVisible(StatusBarHandle)) + ShowWindow(StatusBarHandle, SW_SHOW); + } + else + { + if (StatusBarHandle && IsWindowVisible(StatusBarHandle)) + ShowWindow(StatusBarHandle, SW_HIDE); + } +} + +VOID ToolbarLoadSettings( + VOID + ) +{ + RebarLoadSettings(); + + if (ToolStatusConfig.ToolBarEnabled && ToolBarHandle) + { + INT index = 0; + INT buttonCount = 0; + + buttonCount = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); + + for (index = 0; index < buttonCount; index++) + { + TBBUTTONINFO buttonInfo = + { + sizeof(TBBUTTONINFO), + TBIF_BYINDEX | TBIF_STYLE | TBIF_COMMAND | TBIF_STATE + }; + + // Get settings for first button + if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1) + break; + + // Skip separator buttons + if (buttonInfo.fsStyle == BTNS_SEP) + continue; + + // Add the button text + buttonInfo.dwMask |= TBIF_TEXT; + buttonInfo.pszText = ToolbarGetText(buttonInfo.idCommand); + + switch (DisplayStyle) + { + case TOOLBAR_DISPLAY_STYLE_IMAGEONLY: + buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; + break; + case TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT: + { + switch (buttonInfo.idCommand) + { + case PHAPP_ID_VIEW_REFRESH: + case PHAPP_ID_HACKER_OPTIONS: + case PHAPP_ID_HACKER_FINDHANDLESORDLLS: + case PHAPP_ID_VIEW_SYSTEMINFORMATION: + buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; + break; + default: + buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; + break; + } + } + break; + case TOOLBAR_DISPLAY_STYLE_ALLTEXT: + buttonInfo.fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE | BTNS_SHOWTEXT; + break; + } + + switch (buttonInfo.idCommand) + { + case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: + { + if (PhGetOwnTokenAttributes().Elevated) + { + buttonInfo.fsState &= ~TBSTATE_ENABLED; + } + } + break; + case PHAPP_ID_VIEW_ALWAYSONTOP: + { + // Set the pressed state + if (PhGetIntegerSetting(L"MainWindowAlwaysOnTop")) + { + buttonInfo.fsState |= TBSTATE_PRESSED; + } + } + break; + case TIDC_POWERMENUDROPDOWN: + { + buttonInfo.fsStyle |= BTNS_WHOLEDROPDOWN; + } + break; + } + + // Set updated button info + SendMessage(ToolBarHandle, TB_SETBUTTONINFO, index, (LPARAM)&buttonInfo); + } + + // Resize the toolbar + SendMessage(ToolBarHandle, TB_AUTOSIZE, 0, 0); + } + + if (ToolStatusConfig.ToolBarEnabled && RebarHandle && ToolBarHandle) + { + UINT index; + REBARBANDINFO rebarBandInfo = + { + sizeof(REBARBANDINFO), + RBBIM_IDEALSIZE + }; + + if ((index = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, REBAR_BAND_ID_TOOLBAR, 0)) != UINT_MAX) + { + // Get settings for Rebar band. + if (SendMessage(RebarHandle, RB_GETBANDINFO, index, (LPARAM)&rebarBandInfo)) + { + SIZE idealWidth = { 0, 0 }; + + // Reset the cxIdeal for the Chevron + if (SendMessage(ToolBarHandle, TB_GETIDEALSIZE, FALSE, (LPARAM)&idealWidth)) + { + rebarBandInfo.cxIdeal = (UINT)idealWidth.cx; + + SendMessage(RebarHandle, RB_SETBANDINFO, index, (LPARAM)&rebarBandInfo); + } + } + } + } + + // Invoke the LayoutPaddingCallback. + SendMessage(PhMainWndHandle, WM_SIZE, 0, 0); +} + +VOID ToolbarResetSettings( + VOID + ) +{ + // Remove all buttons. + INT buttonCount = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); + + while (buttonCount--) + SendMessage(ToolBarHandle, TB_DELETEBUTTON, (WPARAM)buttonCount, 0); + + // Add the default buttons. + SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); +} + +PWSTR ToolbarGetText( + _In_ INT CommandID + ) +{ + switch (CommandID) + { + case PHAPP_ID_VIEW_REFRESH: + return L"Refresh"; + case PHAPP_ID_HACKER_OPTIONS: + return L"Options"; + case PHAPP_ID_HACKER_FINDHANDLESORDLLS: + return L"Find handles or DLLs"; + case PHAPP_ID_VIEW_SYSTEMINFORMATION: + return L"System information"; + case TIDC_FINDWINDOW: + return L"Find window"; + case TIDC_FINDWINDOWTHREAD: + return L"Find window and thread"; + case TIDC_FINDWINDOWKILL: + return L"Find window and kill"; + case PHAPP_ID_VIEW_ALWAYSONTOP: + return L"Always on top"; + case TIDC_POWERMENUDROPDOWN: + return L"Computer"; + case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: + return L"Show details for all processes"; + } + + return L"ERROR"; +} + +HBITMAP ToolbarLoadImageFromIcon( + _In_ ULONG Width, + _In_ ULONG Height, + _In_ PWSTR Name + ) +{ + HICON icon = PhLoadIcon(PluginInstance->DllBase, Name, 0, Width, Height); + HBITMAP bitmap = PhIconToBitmap(icon, Width, Height); + DestroyIcon(icon); + return bitmap; +} + +HBITMAP ToolbarGetImage( + _In_ INT CommandID + ) +{ + switch (CommandID) + { + case PHAPP_ID_VIEW_REFRESH: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_ARROW_REFRESH_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_ARROW_REFRESH)); + } + + return toolbarBitmap; + } + break; + case PHAPP_ID_HACKER_OPTIONS: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_COG_EDIT_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_COG_EDIT)); + } + + return toolbarBitmap; + } + break; + case PHAPP_ID_HACKER_FINDHANDLESORDLLS: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_FIND_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_FIND)); + } + + return toolbarBitmap; + } + break; + case PHAPP_ID_VIEW_SYSTEMINFORMATION: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CHART_LINE_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_CHART_LINE)); + } + + return toolbarBitmap; + } + break; + case TIDC_FINDWINDOW: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_TBAPPLICATION)); + } + + return toolbarBitmap; + } + break; + case TIDC_FINDWINDOWTHREAD: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GO_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_APPLICATION_GO)); + } + + return toolbarBitmap; + } + break; + case TIDC_FINDWINDOWKILL: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_CROSS_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_CROSS)); + } + + return toolbarBitmap; + } + break; + case PHAPP_ID_VIEW_ALWAYSONTOP: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_APPLICATION_GET_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_APPLICATION_GET)); + } + + return toolbarBitmap; + } + break; + case TIDC_POWERMENUDROPDOWN: + { + HBITMAP toolbarBitmap = NULL; + + if (ToolStatusConfig.ModernIcons) + { + toolbarBitmap = PhLoadPngImageFromResource(PluginInstance->DllBase, ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDB_POWER_MODERN), FALSE); + } + else + { + toolbarBitmap = ToolbarLoadImageFromIcon(ToolBarImageSize.cx, ToolBarImageSize.cy, MAKEINTRESOURCE(IDI_LIGHTBULB_OFF)); + } + + return toolbarBitmap; + } + break; + case PHAPP_ID_HACKER_SHOWDETAILSFORALLPROCESSES: + { + HICON shieldIcon; + HBITMAP toolbarBitmap = NULL; + + if (shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL, 0, 0)) + { + toolbarBitmap = PhIconToBitmap(shieldIcon, ToolBarImageSize.cx, ToolBarImageSize.cy); + DestroyIcon(shieldIcon); + } + + return toolbarBitmap; + } + break; + } + + return NULL; +} + +VOID ToolbarLoadButtonSettings( + VOID + ) +{ + INT count; + ULONG64 countInteger; + PPH_STRING settingsString; + PTBBUTTON buttonArray; + PH_STRINGREF remaining; + PH_STRINGREF part; + + settingsString = PhaGetStringSetting(SETTING_NAME_TOOLBAR_CONFIG); + remaining = settingsString->sr; + + if (remaining.Length == 0) + { + // Load default settings + SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); + return; + } + + // Query the number of buttons to insert + if (!PhSplitStringRefAtChar(&remaining, '|', &part, &remaining)) + { + // Load default settings + SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); + return; + } + + if (!PhStringToInteger64(&part, 10, &countInteger)) + { + // Load default settings + SendMessage(ToolBarHandle, TB_ADDBUTTONS, MAX_DEFAULT_TOOLBAR_ITEMS, (LPARAM)ToolbarButtons); + return; + } + + count = (INT)countInteger; + + // Allocate the button array + buttonArray = PhAllocate(count * sizeof(TBBUTTON)); + memset(buttonArray, 0, count * sizeof(TBBUTTON)); + + for (INT index = 0; index < count; index++) + { + ULONG64 commandInteger; + PH_STRINGREF commandIdPart; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '|', &commandIdPart, &remaining); + PhStringToInteger64(&commandIdPart, 10, &commandInteger); + + buttonArray[index].idCommand = (INT)commandInteger; + buttonArray[index].iBitmap = I_IMAGECALLBACK; + buttonArray[index].fsState = TBSTATE_ENABLED; + + if (commandInteger) + { + buttonArray[index].fsStyle = BTNS_BUTTON | BTNS_AUTOSIZE; + } + else + { + buttonArray[index].fsStyle = BTNS_SEP; + } + + // Pre-cache the image in the Toolbar array on startup. + for (INT i = 0; i < ARRAYSIZE(ToolbarButtons); i++) + { + if (ToolbarButtons[i].idCommand == buttonArray[index].idCommand) + { + HBITMAP bitmap; + + if (buttonArray[index].fsStyle & BTNS_SEP) + continue; + + if (bitmap = ToolbarGetImage(ToolbarButtons[i].idCommand)) + { + // Add the image, cache the value in the ToolbarButtons array, set the bitmap index. + buttonArray[index].iBitmap = ToolbarButtons[i].iBitmap = ImageList_Add( + ToolBarImageList, + bitmap, + NULL + ); + + DeleteBitmap(bitmap); + } + break; + } + } + } + + SendMessage(ToolBarHandle, TB_ADDBUTTONS, count, (LPARAM)buttonArray); + + PhFree(buttonArray); +} + +VOID ToolbarSaveButtonSettings( + VOID + ) +{ + INT index = 0; + INT count = 0; + PPH_STRING settingsString; + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + + count = (INT)SendMessage(ToolBarHandle, TB_BUTTONCOUNT, 0, 0); + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%d|", + count + ); + + for (index = 0; index < count; index++) + { + TBBUTTONINFO buttonInfo = + { + sizeof(TBBUTTONINFO), + TBIF_BYINDEX | TBIF_IMAGE | TBIF_STYLE | TBIF_COMMAND + }; + + if (SendMessage(ToolBarHandle, TB_GETBUTTONINFO, index, (LPARAM)&buttonInfo) == -1) + break; + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%d|", + buttonInfo.idCommand + ); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); + PhSetStringSetting2(SETTING_NAME_TOOLBAR_CONFIG, &settingsString->sr); +} + +VOID ReBarLoadLayoutSettings( + VOID + ) +{ + UINT index = 0; + UINT count = 0; + PPH_STRING settingsString; + PH_STRINGREF remaining; + + settingsString = PhGetStringSetting(SETTING_NAME_REBAR_CONFIG); + remaining = settingsString->sr; + + if (remaining.Length == 0) + return; + + count = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0); + + for (index = 0; index < count; index++) + { + PH_STRINGREF idPart; + PH_STRINGREF cxPart; + PH_STRINGREF stylePart; + ULONG64 idInteger; + ULONG64 cxInteger; + ULONG64 styleInteger; + UINT oldBandIndex; + REBARBANDINFO rebarBandInfo = + { + sizeof(REBARBANDINFO), + RBBIM_STYLE | RBBIM_SIZE + }; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, '|', &idPart, &remaining); + PhSplitStringRefAtChar(&remaining, '|', &cxPart, &remaining); + PhSplitStringRefAtChar(&remaining, '|', &stylePart, &remaining); + + PhStringToInteger64(&idPart, 10, &idInteger); + PhStringToInteger64(&cxPart, 10, &cxInteger); + PhStringToInteger64(&stylePart, 10, &styleInteger); + + if ((oldBandIndex = (UINT)SendMessage(RebarHandle, RB_IDTOINDEX, (UINT)idInteger, 0)) == UINT_MAX) + continue; + + if (oldBandIndex != index) + { + SendMessage(RebarHandle, RB_MOVEBAND, oldBandIndex, index); + } + + if (SendMessage(RebarHandle, RB_GETBANDINFO, index, (LPARAM)&rebarBandInfo)) + { + rebarBandInfo.cx = (UINT)cxInteger; + rebarBandInfo.fStyle |= (UINT)styleInteger; + + SendMessage(RebarHandle, RB_SETBANDINFO, index, (LPARAM)&rebarBandInfo); + } + } +} + +VOID ReBarSaveLayoutSettings( + VOID + ) +{ + UINT index = 0; + UINT count = 0; + PPH_STRING settingsString; + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + + count = (UINT)SendMessage(RebarHandle, RB_GETBANDCOUNT, 0, 0); + + for (index = 0; index < count; index++) + { + REBARBANDINFO rebarBandInfo = + { + sizeof(REBARBANDINFO), + RBBIM_STYLE | RBBIM_SIZE | RBBIM_ID + }; + + SendMessage(RebarHandle, RB_GETBANDINFO, index, (LPARAM)&rebarBandInfo); + + if (rebarBandInfo.fStyle & RBBS_GRIPPERALWAYS) + { + rebarBandInfo.fStyle &= ~RBBS_GRIPPERALWAYS; + } + + if (rebarBandInfo.fStyle & RBBS_NOGRIPPER) + { + rebarBandInfo.fStyle &= ~RBBS_NOGRIPPER; + } + + if (rebarBandInfo.fStyle & RBBS_FIXEDSIZE) + { + rebarBandInfo.fStyle &= ~RBBS_FIXEDSIZE; + } + + PhAppendFormatStringBuilder( + &stringBuilder, + L"%u|%u|%u|", + rebarBandInfo.wID, + rebarBandInfo.cx, + rebarBandInfo.fStyle + ); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + settingsString = PH_AUTO(PhFinalStringBuilderString(&stringBuilder)); + PhSetStringSetting2(SETTING_NAME_REBAR_CONFIG, &settingsString->sr); +} diff --git a/plugins/ToolStatus/toolstatus.h b/plugins/ToolStatus/toolstatus.h index 1bf451bf28f0..6273182ad47f 100644 --- a/plugins/ToolStatus/toolstatus.h +++ b/plugins/ToolStatus/toolstatus.h @@ -1,381 +1,440 @@ -/* - * Process Hacker ToolStatus - - * toolstatus header - * - * Copyright (C) 2011-2016 dmex - * Copyright (C) 2010-2013 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef _TOOLSTATUS_H -#define _TOOLSTATUS_H - -#define CINTERFACE -#define COBJMACROS -#define INITGUID -#include -#include -#include -#include -#include - -#include "resource.h" -#include - -#define PLUGIN_NAME TOOLSTATUS_PLUGIN_NAME -#define SETTING_NAME_TOOLSTATUS_CONFIG (PLUGIN_NAME L".Config") -#define SETTING_NAME_REBAR_CONFIG (PLUGIN_NAME L".RebarConfig") -#define SETTING_NAME_TOOLBAR_CONFIG (PLUGIN_NAME L".ToolbarConfig") -#define SETTING_NAME_STATUSBAR_CONFIG (PLUGIN_NAME L".StatusbarConfig") -#define SETTING_NAME_TOOLBAR_THEME (PLUGIN_NAME L".ToolbarTheme") -#define SETTING_NAME_TOOLBARDISPLAYSTYLE (PLUGIN_NAME L".ToolbarDisplayStyle") -#define SETTING_NAME_SEARCHBOXDISPLAYMODE (PLUGIN_NAME L".SearchBoxDisplayMode") - -#define MAX_DEFAULT_TOOLBAR_ITEMS 9 -#define MAX_DEFAULT_STATUSBAR_ITEMS 3 -#define MAX_TOOLBAR_ITEMS 12 -#define MAX_STATUSBAR_ITEMS 14 - -#define TIDC_FINDWINDOW (WM_APP + 1) -#define TIDC_FINDWINDOWTHREAD (WM_APP + 2) -#define TIDC_FINDWINDOWKILL (WM_APP + 3) -#define TIDC_POWERMENUDROPDOWN (WM_APP + 4) - -typedef enum _TOOLBAR_DISPLAY_STYLE -{ - TOOLBAR_DISPLAY_STYLE_IMAGEONLY, - TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT, - TOOLBAR_DISPLAY_STYLE_ALLTEXT -} TOOLBAR_DISPLAY_STYLE; - -typedef enum _TOOLBAR_COMMAND_ID -{ - COMMAND_ID_ENABLE_MENU = 1, - COMMAND_ID_ENABLE_SEARCHBOX, - COMMAND_ID_ENABLE_CPU_GRAPH, - COMMAND_ID_ENABLE_MEMORY_GRAPH, - COMMAND_ID_ENABLE_COMMIT_GRAPH, - COMMAND_ID_ENABLE_IO_GRAPH, - COMMAND_ID_TOOLBAR_LOCKUNLOCK, - COMMAND_ID_TOOLBAR_CUSTOMIZE, -} TOOLBAR_COMMAND_ID; - -//typedef enum _TOOLBAR_THEME -//{ -// TOOLBAR_THEME_NONE, -// TOOLBAR_THEME_BLACK, -// TOOLBAR_THEME_BLUE -//} TOOLBAR_THEME; - -typedef enum _SEARCHBOX_DISPLAY_MODE -{ - SEARCHBOX_DISPLAY_MODE_ALWAYSSHOW, - SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE, - //SEARCHBOX_DISPLAY_MODE_AUTOHIDE -} SEARCHBOX_DISPLAY_MODE; - -typedef enum _REBAR_BAND_ID -{ - REBAR_BAND_ID_TOOLBAR, - REBAR_BAND_ID_SEARCHBOX, - REBAR_BAND_ID_CPUGRAPH, - REBAR_BAND_ID_MEMGRAPH, - REBAR_BAND_ID_COMMITGRAPH, - REBAR_BAND_ID_IOGRAPH -} REBAR_BAND; - -typedef enum _REBAR_DISPLAY_LOCATION -{ - REBAR_DISPLAY_LOCATION_TOP, - REBAR_DISPLAY_LOCATION_LEFT, - REBAR_DISPLAY_LOCATION_BOTTOM, - REBAR_DISPLAY_LOCATION_RIGHT, -} REBAR_DISPLAY_LOCATION; - -typedef union _TOOLSTATUS_CONFIG -{ - ULONG Flags; - struct - { - ULONG ToolBarEnabled : 1; - ULONG SearchBoxEnabled : 1; - ULONG StatusBarEnabled : 1; - ULONG ToolBarLocked : 1; - ULONG ResolveGhostWindows : 1; - - ULONG ModernIcons : 1; - ULONG AutoHideMenu : 1; - ULONG CpuGraphEnabled : 1; - ULONG MemGraphEnabled : 1; - ULONG CommitGraphEnabled : 1; - ULONG IoGraphEnabled : 1; - - ULONG Spare : 21; - }; -} TOOLSTATUS_CONFIG; - -extern TOOLSTATUS_CONFIG ToolStatusConfig; -extern HWND ProcessTreeNewHandle; -extern HWND ServiceTreeNewHandle; -extern HWND NetworkTreeNewHandle; -extern INT SelectedTabIndex; -extern BOOLEAN UpdateAutomatically; -extern BOOLEAN UpdateGraphs; -extern TOOLBAR_DISPLAY_STYLE DisplayStyle; -extern SEARCHBOX_DISPLAY_MODE SearchBoxDisplayMode; -extern REBAR_DISPLAY_LOCATION RebarDisplayLocation; - -extern HWND RebarHandle; -extern HWND ToolBarHandle; -extern HWND SearchboxHandle; - -extern HMENU MainMenu; -extern HACCEL AcceleratorTable; -extern PPH_STRING SearchboxText; -extern PH_PLUGIN_SYSTEM_STATISTICS SystemStatistics; - -extern HIMAGELIST ToolBarImageList; -extern TBBUTTON ToolbarButtons[MAX_TOOLBAR_ITEMS]; - -extern PPH_PLUGIN PluginInstance; -extern PPH_TN_FILTER_ENTRY ProcessTreeFilterEntry; -extern PPH_TN_FILTER_ENTRY ServiceTreeFilterEntry; -extern PPH_TN_FILTER_ENTRY NetworkTreeFilterEntry; - -PTOOLSTATUS_TAB_INFO FindTabInfo( - _In_ INT TabIndex - ); - -// toolbar.c - -VOID RebarBandInsert( - _In_ UINT BandID, - _In_ HWND HwndChild, - _In_ UINT cyMinChild, - _In_ UINT cxMinChild - ); - -VOID RebarBandRemove( - _In_ UINT BandID - ); - -BOOLEAN RebarBandExists( - _In_ UINT BandID - ); - -VOID ToolbarLoadSettings( - VOID - ); - -VOID ToolbarResetSettings( - VOID - ); - -PWSTR ToolbarGetText( - _In_ INT CommandID - ); - -HBITMAP ToolbarGetImage( - _In_ INT CommandID - ); - -VOID ToolbarLoadButtonSettings( - VOID - ); - -VOID ToolbarSaveButtonSettings( - VOID - ); - -VOID ReBarLoadLayoutSettings( - VOID - ); - -VOID ReBarSaveLayoutSettings( - VOID - ); - -// main.c - -HWND GetCurrentTreeNewHandle( - VOID - ); - -VOID ShowCustomizeMenu( - VOID - ); - -// options.c - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ); - -// filter.c - -BOOLEAN WordMatchStringRef( - _In_ PPH_STRINGREF Text - ); - -BOOLEAN ProcessTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); -BOOLEAN ServiceTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); -BOOLEAN NetworkTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ); - -NTSTATUS QueryServiceFileName( - _In_ PPH_STRINGREF ServiceName, - _Out_ PPH_STRING *ServiceFileName, - _Out_ PPH_STRING *ServiceBinaryPath - ); - -// graph.c - -extern HWND CpuGraphHandle; -extern HWND MemGraphHandle; -extern HWND CommitGraphHandle; -extern HWND IoGraphHandle; - -VOID ToolbarCreateGraphs(VOID); -VOID ToolbarUpdateGraphs(VOID); -VOID ToolbarUpdateGraphsInfo(LPNMHDR Header); - -// statusbar.c - -typedef struct _STATUSBAR_ITEM -{ - ULONG Id; -} STATUSBAR_ITEM, *PSTATUSBAR_ITEM; - -extern ULONG ProcessesUpdatedCount; -extern HWND StatusBarHandle; -extern PPH_LIST StatusBarItemList; -extern ULONG StatusBarItems[MAX_STATUSBAR_ITEMS]; - -VOID StatusBarLoadSettings( - VOID - ); - -VOID StatusBarSaveSettings( - VOID - ); - -VOID StatusBarResetSettings( - VOID - ); - -PWSTR StatusBarGetText( - _In_ ULONG CommandID - ); - -VOID StatusBarUpdate( - _In_ BOOLEAN ResetMaxWidths - ); - -VOID StatusBarShowMenu( - VOID - ); - -// customizetb.c - -VOID ToolBarShowCustomizeDialog( - VOID - ); - -// customizesb.c - -typedef enum _ID_STATUS -{ - ID_STATUS_NONE, - ID_STATUS_CPUUSAGE, - ID_STATUS_COMMITCHARGE, - ID_STATUS_PHYSICALMEMORY, - ID_STATUS_NUMBEROFPROCESSES, - ID_STATUS_NUMBEROFTHREADS, - ID_STATUS_NUMBEROFHANDLES, - ID_STATUS_IO_RO, - ID_STATUS_IO_W, - ID_STATUS_MAX_CPU_PROCESS, - ID_STATUS_MAX_IO_PROCESS, - ID_STATUS_NUMBEROFVISIBLEITEMS, - ID_STATUS_NUMBEROFSELECTEDITEMS, - ID_STATUS_INTERVALSTATUS, - ID_STATUS_FREEMEMORY -} ID_STATUS; - -VOID StatusBarShowCustomizeDialog( - VOID - ); - -// Shared by customizetb.c and customizesb.c - -typedef struct _BUTTON_CONTEXT -{ - INT IdCommand; - HICON IconHandle; - - union - { - ULONG Flags; - struct - { - ULONG IsVirtual : 1; - ULONG IsRemovable : 1; - ULONG IsSeparator : 1; - ULONG Spare : 29; - }; - }; -} BUTTON_CONTEXT, *PBUTTON_CONTEXT; - -typedef struct _CUSTOMIZE_CONTEXT -{ - HFONT FontHandle; - HBRUSH BrushNormal; - HBRUSH BrushPushed; - HBRUSH BrushHot; - INT CXWidth; - INT ImageWidth; - INT ImageHeight; - - HWND DialogHandle; - HWND AvailableListHandle; - HWND CurrentListHandle; - HWND MoveUpButtonHandle; - HWND MoveDownButtonHandle; - HWND AddButtonHandle; - HWND RemoveButtonHandle; -} CUSTOMIZE_CONTEXT, *PCUSTOMIZE_CONTEXT; - -HICON CustomizeGetToolbarIcon( - _In_ PCUSTOMIZE_CONTEXT Context, - _In_ INT CommandID - ); - -// searchbox.c - -BOOLEAN CreateSearchboxControl( - VOID - ); - -#endif \ No newline at end of file +/* + * Process Hacker ToolStatus - + * toolstatus header + * + * Copyright (C) 2010-2013 wj32 + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef _TOOLSTATUS_H +#define _TOOLSTATUS_H + +#include +#include +#include +#include + +#include +#include + +#include "resource.h" +#include + +#define PLUGIN_NAME TOOLSTATUS_PLUGIN_NAME +#define SETTING_NAME_TOOLSTATUS_CONFIG (PLUGIN_NAME L".Config") +#define SETTING_NAME_REBAR_CONFIG (PLUGIN_NAME L".RebarConfig") +#define SETTING_NAME_TOOLBAR_CONFIG (PLUGIN_NAME L".ToolbarButtonConfig") +#define SETTING_NAME_TOOLBAR_GRAPH_CONFIG (PLUGIN_NAME L".ToolbarGraphConfig") +#define SETTING_NAME_STATUSBAR_CONFIG (PLUGIN_NAME L".StatusbarConfig") +#define SETTING_NAME_TOOLBAR_THEME (PLUGIN_NAME L".ToolbarTheme") +#define SETTING_NAME_TOOLBARDISPLAYSTYLE (PLUGIN_NAME L".ToolbarDisplayStyle") +#define SETTING_NAME_SEARCHBOXDISPLAYMODE (PLUGIN_NAME L".SearchBoxDisplayMode") + +#define MAX_DEFAULT_TOOLBAR_ITEMS 9 +#define MAX_DEFAULT_STATUSBAR_ITEMS 3 +#define MAX_TOOLBAR_ITEMS 12 +#define MAX_STATUSBAR_ITEMS 14 + +#define TIDC_FINDWINDOW (WM_APP + 1) +#define TIDC_FINDWINDOWTHREAD (WM_APP + 2) +#define TIDC_FINDWINDOWKILL (WM_APP + 3) +#define TIDC_POWERMENUDROPDOWN (WM_APP + 4) + +typedef enum _TOOLBAR_DISPLAY_STYLE +{ + TOOLBAR_DISPLAY_STYLE_IMAGEONLY, + TOOLBAR_DISPLAY_STYLE_SELECTIVETEXT, + TOOLBAR_DISPLAY_STYLE_ALLTEXT +} TOOLBAR_DISPLAY_STYLE; + +typedef enum _TOOLBAR_COMMAND_ID +{ + COMMAND_ID_ENABLE_MENU = 1, + COMMAND_ID_ENABLE_SEARCHBOX, + COMMAND_ID_TOOLBAR_LOCKUNLOCK, + COMMAND_ID_TOOLBAR_CUSTOMIZE, + COMMAND_ID_GRAPHS_CUSTOMIZE, +} TOOLBAR_COMMAND_ID; + +//typedef enum _TOOLBAR_THEME +//{ +// TOOLBAR_THEME_NONE, +// TOOLBAR_THEME_BLACK, +// TOOLBAR_THEME_BLUE +//} TOOLBAR_THEME; + +typedef enum _SEARCHBOX_DISPLAY_MODE +{ + SEARCHBOX_DISPLAY_MODE_ALWAYSSHOW, + SEARCHBOX_DISPLAY_MODE_HIDEINACTIVE, + //SEARCHBOX_DISPLAY_MODE_AUTOHIDE +} SEARCHBOX_DISPLAY_MODE; + +typedef enum _REBAR_BAND_ID +{ + REBAR_BAND_ID_TOOLBAR, + REBAR_BAND_ID_SEARCHBOX, + REBAR_BAND_ID_CPUGRAPH, + REBAR_BAND_ID_MEMGRAPH, + REBAR_BAND_ID_COMMITGRAPH, + REBAR_BAND_ID_IOGRAPH +} REBAR_BAND; + +typedef enum _REBAR_DISPLAY_LOCATION +{ + REBAR_DISPLAY_LOCATION_TOP, + REBAR_DISPLAY_LOCATION_LEFT, + REBAR_DISPLAY_LOCATION_BOTTOM, + REBAR_DISPLAY_LOCATION_RIGHT, +} REBAR_DISPLAY_LOCATION; + +typedef union _TOOLSTATUS_CONFIG +{ + ULONG Flags; + struct + { + ULONG ToolBarEnabled : 1; + ULONG SearchBoxEnabled : 1; + ULONG StatusBarEnabled : 1; + ULONG ToolBarLocked : 1; + ULONG ResolveGhostWindows : 1; + ULONG ModernIcons : 1; + ULONG AutoHideMenu : 1; + ULONG Reserved : 4; + ULONG Spare : 21; + }; +} TOOLSTATUS_CONFIG; + +extern TOOLSTATUS_CONFIG ToolStatusConfig; +extern HWND ProcessTreeNewHandle; +extern HWND ServiceTreeNewHandle; +extern HWND NetworkTreeNewHandle; +extern INT SelectedTabIndex; +extern BOOLEAN UpdateAutomatically; +extern BOOLEAN UpdateGraphs; +extern TOOLBAR_DISPLAY_STYLE DisplayStyle; +extern SEARCHBOX_DISPLAY_MODE SearchBoxDisplayMode; +extern REBAR_DISPLAY_LOCATION RebarDisplayLocation; + +extern HWND RebarHandle; +extern HWND ToolBarHandle; +extern HWND SearchboxHandle; + +extern HMENU MainMenu; +extern HACCEL AcceleratorTable; +extern PPH_STRING SearchboxText; +extern PH_PLUGIN_SYSTEM_STATISTICS SystemStatistics; + +extern HIMAGELIST ToolBarImageList; +extern TBBUTTON ToolbarButtons[MAX_TOOLBAR_ITEMS]; + +extern PPH_PLUGIN PluginInstance; +extern PPH_TN_FILTER_ENTRY ProcessTreeFilterEntry; +extern PPH_TN_FILTER_ENTRY ServiceTreeFilterEntry; +extern PPH_TN_FILTER_ENTRY NetworkTreeFilterEntry; + +PTOOLSTATUS_TAB_INFO FindTabInfo( + _In_ INT TabIndex + ); + +// toolbar.c + +VOID RebarBandInsert( + _In_ UINT BandID, + _In_ HWND HwndChild, + _In_ UINT cyMinChild, + _In_ UINT cxMinChild + ); + +VOID RebarBandRemove( + _In_ UINT BandID + ); + +BOOLEAN RebarBandExists( + _In_ UINT BandID + ); + +VOID ToolbarLoadSettings( + VOID + ); + +VOID ToolbarResetSettings( + VOID + ); + +PWSTR ToolbarGetText( + _In_ INT CommandID + ); + +HBITMAP ToolbarGetImage( + _In_ INT CommandID + ); + +VOID ToolbarLoadButtonSettings( + VOID + ); + +VOID ToolbarSaveButtonSettings( + VOID + ); + +VOID ReBarLoadLayoutSettings( + VOID + ); + +VOID ReBarSaveLayoutSettings( + VOID + ); + +// main.c + +HWND GetCurrentTreeNewHandle( + VOID + ); + +HFONT ToolStatusGetTreeWindowFont( + VOID + ); + +VOID ShowCustomizeMenu( + _In_ HWND WindowHandle + ); + +// options.c + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// filter.c + +BOOLEAN WordMatchStringRef( + _In_ PPH_STRINGREF Text + ); + +BOOLEAN ProcessTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); +BOOLEAN ServiceTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); +BOOLEAN NetworkTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +NTSTATUS QueryServiceFileName( + _In_ PPH_STRINGREF ServiceName, + _Out_ PPH_STRING *ServiceFileName, + _Out_ PPH_STRING *ServiceBinaryPath + ); + +// graph.c + +typedef struct _PH_TOOLBAR_GRAPH +{ + struct _PH_PLUGIN *Plugin; + + ULONG Flags; + ULONG GraphId; + + HWND GraphHandle; + PVOID Context; + PWSTR Text; + + PTOOLSTATUS_GRAPH_MESSAGE_CALLBACK MessageCallback; + PH_GRAPH_STATE GraphState; +} PH_TOOLBAR_GRAPH, *PPH_TOOLBAR_GRAPH; + +VOID ToolbarGraphLoadSettings( + VOID + ); + +VOID ToolbarGraphSaveSettings( + VOID + ); + +VOID ToolbarGraphsInitialize( + VOID + ); + +VOID ToolbarRegisterGraph( + _In_ struct _PH_PLUGIN *Plugin, + _In_ ULONG Id, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_opt_ PVOID Context, + _In_opt_ PTOOLSTATUS_GRAPH_MESSAGE_CALLBACK MessageCallback + ); + +VOID ToolbarCreateGraphs( + VOID + ); + +VOID ToolbarUpdateGraphs( + VOID + ); + +BOOLEAN ToolbarUpdateGraphsInfo( + _In_ HWND WindowHandle, + _In_ LPNMHDR Header + ); + +VOID ToolbarSetVisibleGraph( + _In_ PPH_TOOLBAR_GRAPH Icon, + _In_ BOOLEAN Visible + ); + +BOOLEAN ToolbarGraphsEnabled( + VOID + ); + +PPH_TOOLBAR_GRAPH ToolbarGraphFindById( + _In_ ULONG SubId + ); + +PPH_TOOLBAR_GRAPH ToolbarGraphFindByName( + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ); + +VOID ToolbarGraphCreateMenu( + _In_ PPH_EMENU ParentMenu, + _In_ ULONG MenuId + ); + +// statusbar.c + +extern ULONG ProcessesUpdatedCount; +extern HWND StatusBarHandle; +extern PPH_LIST StatusBarItemList; +extern ULONG StatusBarItems[MAX_STATUSBAR_ITEMS]; + +VOID StatusBarLoadSettings( + VOID + ); + +VOID StatusBarSaveSettings( + VOID + ); + +VOID StatusBarResetSettings( + VOID + ); + +PWSTR StatusBarGetText( + _In_ ULONG CommandID + ); + +VOID StatusBarUpdate( + _In_ BOOLEAN ResetMaxWidths + ); + +VOID StatusBarShowMenu( + VOID + ); + +// customizetb.c + +VOID ToolBarShowCustomizeDialog( + VOID + ); + +// customizesb.c + +typedef enum _ID_STATUS +{ + ID_STATUS_NONE, + ID_STATUS_CPUUSAGE, + ID_STATUS_COMMITCHARGE, + ID_STATUS_PHYSICALMEMORY, + ID_STATUS_NUMBEROFPROCESSES, + ID_STATUS_NUMBEROFTHREADS, + ID_STATUS_NUMBEROFHANDLES, + ID_STATUS_IO_RO, + ID_STATUS_IO_W, + ID_STATUS_MAX_CPU_PROCESS, + ID_STATUS_MAX_IO_PROCESS, + ID_STATUS_NUMBEROFVISIBLEITEMS, + ID_STATUS_NUMBEROFSELECTEDITEMS, + ID_STATUS_INTERVALSTATUS, + ID_STATUS_FREEMEMORY +} ID_STATUS; + +VOID StatusBarShowCustomizeDialog( + VOID + ); + +// Shared by customizetb.c and customizesb.c + +typedef struct _BUTTON_CONTEXT +{ + INT IdCommand; + HICON IconHandle; + + union + { + ULONG Flags; + struct + { + ULONG IsVirtual : 1; + ULONG IsRemovable : 1; + ULONG IsSeparator : 1; + ULONG Spare : 29; + }; + }; +} BUTTON_CONTEXT, *PBUTTON_CONTEXT; + +typedef struct _CUSTOMIZE_CONTEXT +{ + HFONT FontHandle; + HBRUSH BrushNormal; + HBRUSH BrushPushed; + HBRUSH BrushHot; + INT CXWidth; + INT ImageWidth; + INT ImageHeight; + + HWND DialogHandle; + HWND AvailableListHandle; + HWND CurrentListHandle; + HWND MoveUpButtonHandle; + HWND MoveDownButtonHandle; + HWND AddButtonHandle; + HWND RemoveButtonHandle; +} CUSTOMIZE_CONTEXT, *PCUSTOMIZE_CONTEXT; + +HICON CustomizeGetToolbarIcon( + _In_ PCUSTOMIZE_CONTEXT Context, + _In_ INT CommandID + ); + +// searchbox.c + +BOOLEAN CreateSearchboxControl( + VOID + ); + +extern HFONT ToolStatusWindowFont; + +#endif diff --git a/plugins/Updater/CHANGELOG.txt b/plugins/Updater/CHANGELOG.txt index 9cf6f9e3b1df..f1ebcf0295f8 100644 --- a/plugins/Updater/CHANGELOG.txt +++ b/plugins/Updater/CHANGELOG.txt @@ -1,45 +1,45 @@ -2.0 - * Added support for Process Hacker nightly builds - * Improved UI with native TaskDialog - -1.8 - * Removed legacy code - -1.7 - * Ignore v2.40 and above on XP and Vista. - -1.6 - * Fixed background update check interval - -1.5 - * Added dynamic URL support - * Added digital signature verification - -1.4 - * Added PNG images - * Fixed offline crash - * Improved background update check - * Removed BMP images - -1.3 - * Added revision checking - * Fixed invalid xml crash - * Fixed GDI handle leak - * Fixed caching - -1.2 - * Improved UI - * Improved internet connectivity check for Vista and above (INetworkListManager) - * Improved download speed calculation - * Improved time remaining calculation - * Fixed auto-update prompt location if PH minimized - * Fixed install failures if hash check failed - -1.1 - * Added download speed - * Added remaining time - * Fixed buffer zeroing - * Fixed threading memory leak - -1.0 - * Initial release +2.0 + * Added support for Process Hacker nightly builds + * Improved UI with native TaskDialog + +1.8 + * Removed legacy code + +1.7 + * Ignore v2.40 and above on XP and Vista. + +1.6 + * Fixed background update check interval + +1.5 + * Added dynamic URL support + * Added digital signature verification + +1.4 + * Added PNG images + * Fixed offline crash + * Improved background update check + * Removed BMP images + +1.3 + * Added revision checking + * Fixed invalid xml crash + * Fixed GDI handle leak + * Fixed caching + +1.2 + * Improved UI + * Improved internet connectivity check for Vista and above (INetworkListManager) + * Improved download speed calculation + * Improved time remaining calculation + * Fixed auto-update prompt location if PH minimized + * Fixed install failures if hash check failed + +1.1 + * Added download speed + * Added remaining time + * Fixed buffer zeroing + * Fixed threading memory leak + +1.0 + * Initial release diff --git a/plugins/Updater/Updater.rc b/plugins/Updater/Updater.rc index 163e82bf6de4..9310b0b5c26c 100644 --- a/plugins/Updater/Updater.rc +++ b/plugins/Updater/Updater.rc @@ -1,147 +1,152 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#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 - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,0,0,0 - PRODUCTVERSION 2,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "dmex" - VALUE "FileDescription", "Update checker plugin for Process Hacker" - VALUE "FileVersion", "2.0" - VALUE "InternalName", "ProcessHacker.UpdateChecker" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "Updater.dll" - VALUE "ProductName", "Update checker plugin for Process Hacker" - VALUE "ProductVersion", "2.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_OPTIONS DIALOGEX 0, 0, 215, 54 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Updater Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "Check for updates automatically",IDC_AUTOCHECKBOX, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,142,10 - DEFPUSHBUTTON "Close",IDCANCEL,158,33,50,14 - CONTROL "Check for nightly builds",IDC_NIGHTLY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,19,142,10 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 208 - TOPMARGIN, 7 - BOTTOMMARGIN, 47 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#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 + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,0,0,0 + PRODUCTVERSION 2,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "dmex" + VALUE "FileDescription", "Update checker plugin for Process Hacker" + VALUE "FileVersion", "2.0" + VALUE "InternalName", "ProcessHacker.UpdateChecker" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "Updater.dll" + VALUE "ProductName", "Update checker plugin for Process Hacker" + VALUE "ProductVersion", "2.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOGEX 0, 0, 157, 34 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Updater Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Check for updates automatically",IDC_AUTOCHECKBOX, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,142,10 +END + +IDD_TEXT DIALOGEX 0, 0, 309, 176 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Changelog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_TEXT,1,1,307,154,ES_MULTILINE | WS_VSCROLL + DEFPUSHBUTTON "OK",IDCANCEL,258,159,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 150 + TOPMARGIN, 7 + BOTTOMMARGIN, 27 + END + + IDD_TEXT, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 308 + TOPMARGIN, 1 + BOTTOMMARGIN, 173 + END +END +#endif // APSTUDIO_INVOKED + + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/Updater/Updater.vcxproj b/plugins/Updater/Updater.vcxproj index 2e06be53bac9..07b112497684 100644 --- a/plugins/Updater/Updater.vcxproj +++ b/plugins/Updater/Updater.vcxproj @@ -1,100 +1,103 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {A0C1595C-FA3E-4B7A-936C-306BC6294C5E} - Updater - Win32Proj - Updater - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - bcrypt.lib;winhttp.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - bcrypt.lib;winhttp.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - bcrypt.lib;winhttp.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - bcrypt.lib;winhttp.lib;%(AdditionalDependencies) - bcrypt.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A0C1595C-FA3E-4B7A-936C-306BC6294C5E} + Updater + Win32Proj + Updater + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + + + + bcrypt.lib;winhttp.lib;%(AdditionalDependencies) + comctl32.dll;bcrypt.dll;shell32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + bcrypt.lib;winhttp.lib;%(AdditionalDependencies) + comctl32.dll;bcrypt.dll;shell32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + bcrypt.lib;winhttp.lib;%(AdditionalDependencies) + comctl32.dll;bcrypt.dll;shell32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + bcrypt.lib;winhttp.lib;%(AdditionalDependencies) + comctl32.dll;bcrypt.dll;shell32.dll;user32.dll;winhttp.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/Updater/main.c b/plugins/Updater/main.c index 31095e0052cb..129b5a8ab255 100644 --- a/plugins/Updater/main.c +++ b/plugins/Updater/main.c @@ -1,143 +1,147 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2011-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "updater.h" - -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; - -VOID NTAPI MainWindowShowingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - // Check if the user want's us to auto-check for updates. - if (PhGetIntegerSetting(SETTING_NAME_AUTO_CHECK)) - { - // All good, queue up our update check. - StartInitialCheck(); - } -} - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - - // Check this menu is the Help menu - if (menuInfo->u.MainMenu.SubMenuIndex != 4) - return; - - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, UPDATE_MENUITEM, L"Check for updates", NULL), 0); -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - if (menuItem && menuItem->Id == UPDATE_MENUITEM) - { - ShowUpdateDialog(NULL); - } -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ShowOptionsDialog((HWND)Parameter); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { IntegerSettingType, SETTING_NAME_AUTO_CHECK, L"1" }, - { StringSettingType, SETTING_NAME_LAST_CHECK, L"0" }, -#ifdef VIRUSTOTAL_API - { IntegerSettingType, SETTING_NAME_NIGHTLY_BUILD, L"1" } -#else - { IntegerSettingType, SETTING_NAME_NIGHTLY_BUILD, L"0" } -#endif - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Update Checker"; - info->Author = L"dmex"; - info->Description = L"Plugin for checking new Process Hacker releases via the Help menu."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1121"; - info->HasOptions = TRUE; - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainWindowShowing), - MainWindowShowingCallback, - NULL, - &MainWindowShowingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), - MainMenuInitializingCallback, - NULL, - &MainMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - } - - return TRUE; -} \ No newline at end of file +/* + * Process Hacker Plugins - + * Update Checker Plugin + * + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "updater.h" + +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; + +VOID NTAPI MainWindowShowingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + // Check if the user want's us to auto-check for updates. + if (PhGetIntegerSetting(SETTING_NAME_AUTO_CHECK)) + { + // All good, queue up our update check. + StartInitialCheck(); + } +} + +VOID NTAPI MainMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + + // Check this menu is the Help menu + if (menuInfo->u.MainMenu.SubMenuIndex != 4) + return; + + PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, UPDATE_MENUITEM, L"Check for &updates", NULL), 0); +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + if (menuItem && menuItem->Id == UPDATE_MENUITEM) + { + ShowUpdateDialog(NULL); + } +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"Updater", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { IntegerSettingType, SETTING_NAME_AUTO_CHECK, L"1" }, + { StringSettingType, SETTING_NAME_LAST_CHECK, L"0" }, + { IntegerPairSettingType, SETTING_NAME_CHANGELOG_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_CHANGELOG_WINDOW_SIZE, L"@96|420,250" }, + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Update Checker"; + info->Author = L"dmex"; + info->Description = L"Plugin for checking new Process Hacker releases via the Help menu."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1121"; + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainWindowShowing), + MainWindowShowingCallback, + NULL, + &MainWindowShowingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), + MainMenuInitializingCallback, + NULL, + &MainMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + + PhAddSettings(settings, RTL_NUMBER_OF(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/Updater/options.c b/plugins/Updater/options.c index 1d3d06de2cf5..0e3f7ce854d0 100644 --- a/plugins/Updater/options.c +++ b/plugins/Updater/options.c @@ -1,78 +1,115 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2011-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "updater.h" - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - if (PhGetIntegerSetting(SETTING_NAME_AUTO_CHECK)) - Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOCHECKBOX), BST_CHECKED); - - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - Button_SetCheck(GetDlgItem(hwndDlg, IDC_NIGHTLY), BST_CHECKED); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_ID(wParam, lParam)) - { - case IDCANCEL: - { - PhSetIntegerSetting(SETTING_NAME_AUTO_CHECK, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_AUTOCHECKBOX)) == BST_CHECKED); - - PhSetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD, - Button_GetCheck(GetDlgItem(hwndDlg, IDC_NIGHTLY)) == BST_CHECKED); - - EndDialog(hwndDlg, IDCANCEL); - } - break; - } - } - break; - } - - return FALSE; -} - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - Parent, - OptionsDlgProc - ); -} \ No newline at end of file +/* + * Process Hacker Plugins - + * Update Checker Plugin + * + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "updater.h" + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + if (PhGetIntegerSetting(SETTING_NAME_AUTO_CHECK)) + Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOCHECKBOX), BST_CHECKED); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_AUTOCHECKBOX: + { + PhSetIntegerSetting(SETTING_NAME_AUTO_CHECK, + Button_GetCheck(GetDlgItem(hwndDlg, IDC_AUTOCHECKBOX)) == BST_CHECKED); + } + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK TextDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)lParam; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + PhSetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), PhGetString(context->BuildMessage)); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_TEXT), NULL, PH_ANCHOR_ALL); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDCANCEL), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT); + + if (PhGetIntegerPairSetting(SETTING_NAME_CHANGELOG_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_CHANGELOG_WINDOW_POSITION, SETTING_NAME_CHANGELOG_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDCANCEL)); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(SETTING_NAME_CHANGELOG_WINDOW_POSITION, SETTING_NAME_CHANGELOG_WINDOW_SIZE, hwndDlg); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/Updater/page1.c b/plugins/Updater/page1.c index 584691343fa3..cdf3bb303220 100644 --- a/plugins/Updater/page1.c +++ b/plugins/Updater/page1.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -69,7 +69,7 @@ VOID ShowCheckForUpdatesDialog( config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); + config.cButtons = RTL_NUMBER_OF(TaskDialogButtonArray); config.pfCallback = CheckForUpdatesCallbackProc; config.lpCallbackData = (LONG_PTR)Context; @@ -78,5 +78,5 @@ VOID ShowCheckForUpdatesDialog( //config.pszContent = L"The updater will check for new Process Hacker releases and optionally download and install the update.\r\n\r\nClick the check for updates button to continue."; config.pszContent = L"Select \"check for updates\" to continue.\r\n"; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file + TaskDialogNavigatePage(Context->DialogHandle, &config); +} diff --git a/plugins/Updater/page2.c b/plugins/Updater/page2.c index c07909b22af8..9f239ad68e1b 100644 --- a/plugins/Updater/page2.c +++ b/plugins/Updater/page2.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -66,5 +66,5 @@ VOID ShowCheckingForUpdatesDialog( config.pszWindowTitle = L"Process Hacker - Updater"; config.pszMainInstruction = L"Checking for new releases..."; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file + TaskDialogNavigatePage(Context->DialogHandle, &config); +} diff --git a/plugins/Updater/page3.c b/plugins/Updater/page3.c index 6b79f7aaede3..91e2eccddc97 100644 --- a/plugins/Updater/page3.c +++ b/plugins/Updater/page3.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -46,28 +46,15 @@ HRESULT CALLBACK ShowAvailableCallbackProc( { if ((INT)wParam == IDOK) { - if (UpdaterInstalledUsingSetup()) - { - ShowProgressDialog(context); - return S_FALSE; - } - else - { - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - PhShellExecute(hwndDlg, L"https://wj32.org/processhacker/nightly.php", NULL); - } - else - { - PhShellExecute(hwndDlg, L"https://wj32.org/processhacker/downloads.php", NULL); - } - } + ShowProgressDialog(context); + return S_FALSE; } } break; case TDN_HYPERLINK_CLICKED: { TaskDialogLinkClicked(context); + return S_FALSE; } break; } @@ -88,37 +75,16 @@ VOID ShowAvailableDialog( config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); + config.cButtons = RTL_NUMBER_OF(TaskDialogButtonArray); config.lpCallbackData = (LONG_PTR)Context; config.pfCallback = ShowAvailableCallbackProc; - config.pszWindowTitle = L"Process Hacker - Updater"; - - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - config.pszMainInstruction = L"A new Process Hacker nightly build is available"; - config.pszContent = PhaFormatString(L"Build: %lu.%lu.%lu\r\nDownload size: %s", - Context->MajorVersion, - Context->MinorVersion, - Context->RevisionVersion, - PhGetStringOrEmpty(Context->Size) - )->Buffer; - if (PhIsNullOrEmptyString(Context->BuildMessage)) - config.pszExpandedInformation = L"View Changelog"; - else - config.pszExpandedInformation = PhGetStringOrEmpty(Context->BuildMessage); - } - else - { - config.pszMainInstruction = L"A new Process Hacker release is available"; - config.pszContent = PhaFormatString(L"Version: %lu.%lu.%lu\r\nDownload size: %s", - Context->MajorVersion, - Context->MinorVersion, - Context->RevisionVersion, - PhGetStringOrEmpty(Context->Size) - )->Buffer; - config.pszExpandedInformation = L"View Changelog"; - } + config.pszWindowTitle = L"Process Hacker - Updater"; + config.pszMainInstruction = L"A newer build of Process Hacker is available."; + config.pszContent = PhaFormatString(L"Version: %s\r\nDownload size: %s\r\n\r\nView Changelog", + PhGetStringOrEmpty(Context->Version), + PhGetStringOrEmpty(Context->SetupFileLength) + )->Buffer; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file + TaskDialogNavigatePage(Context->DialogHandle, &config); +} diff --git a/plugins/Updater/page4.c b/plugins/Updater/page4.c index 206634cb4383..3717910302af 100644 --- a/plugins/Updater/page4.c +++ b/plugins/Updater/page4.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -46,6 +46,7 @@ HRESULT CALLBACK ShowProgressCallbackProc( case TDN_HYPERLINK_CLICKED: { TaskDialogLinkClicked(context); + return S_FALSE; } break; } @@ -61,7 +62,7 @@ VOID ShowProgressDialog( memset(&config, 0, sizeof(TASKDIALOGCONFIG)); config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_EXPAND_FOOTER_AREA | TDF_ENABLE_HYPERLINKS | TDF_SHOW_PROGRESS_BAR; + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS | TDF_SHOW_PROGRESS_BAR; config.dwCommonButtons = TDCBF_CANCEL_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; @@ -69,17 +70,8 @@ VOID ShowProgressDialog( config.pfCallback = ShowProgressCallbackProc; config.pszWindowTitle = L"Process Hacker - Updater"; - config.pszMainInstruction = PhaFormatString(L"Downloading update %lu.%lu.%lu...", - Context->MajorVersion, - Context->MinorVersion, - Context->RevisionVersion - )->Buffer; + config.pszMainInstruction = PhaFormatString(L"Downloading update %s...", PhGetStringOrEmpty(Context->Version))->Buffer; config.pszContent = L"Downloaded: ~ of ~ (0%)\r\nSpeed: ~ KB/s"; - if (PhIsNullOrEmptyString(Context->BuildMessage)) - config.pszExpandedInformation = L"View Changelog"; - else - config.pszExpandedInformation = PhGetStringOrEmpty(Context->BuildMessage); - - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file + TaskDialogNavigatePage(Context->DialogHandle, &config); +} diff --git a/plugins/Updater/page5.c b/plugins/Updater/page5.c index 58e22e502855..b0ff5ace1ea5 100644 --- a/plugins/Updater/page5.c +++ b/plugins/Updater/page5.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -29,6 +29,70 @@ static TASKDIALOG_BUTTON TaskDialogButtonArray[] = { IDYES, L"Install" } }; +BOOLEAN UpdaterCheckKphInstallState( + VOID + ) +{ + static PH_STRINGREF kph3ServiceKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\KProcessHacker3"); + BOOLEAN kphInstallRequired = FALSE; + HANDLE runKeyHandle; + + if (NT_SUCCESS(PhOpenKey( + &runKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &kph3ServiceKeyName, + 0 + ))) + { + // Make sure we re-install the driver when KPH was installed as a service. + if (PhQueryRegistryUlong(runKeyHandle, L"Start") == SERVICE_SYSTEM_START) + { + kphInstallRequired = TRUE; + } + + NtClose(runKeyHandle); + } + + return kphInstallRequired; +} + +BOOLEAN UpdaterCheckApplicationDirectory( + VOID + ) +{ + HANDLE fileHandle; + PPH_STRING directory; + PPH_STRING file; + + if (UpdaterCheckKphInstallState()) + return FALSE; + + directory = PhGetApplicationDirectory(); + file = PhConcatStrings(2, PhGetStringOrEmpty(directory), L"\\processhacker.update"); + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(file), + FILE_GENERIC_WRITE | DELETE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE + ))) + { + PhDereferenceObject(file); + PhDereferenceObject(directory); + + NtClose(fileHandle); + return TRUE; + } + + PhDereferenceObject(file); + PhDereferenceObject(directory); + return FALSE; +} + HRESULT CALLBACK FinalTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -43,7 +107,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( { case TDN_NAVIGATED: { - if (!PhGetOwnTokenAttributes().Elevated) + if (!UpdaterCheckApplicationDirectory()) { SendMessage(hwndDlg, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, IDYES, TRUE); } @@ -61,15 +125,21 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( else if (buttonId == IDYES) { SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; + PPH_STRING parameters; if (PhIsNullOrEmptyString(context->SetupFilePath)) break; + parameters = PH_AUTO(PhGetApplicationDirectory()); + parameters = PH_AUTO(PhBufferToHexString((PUCHAR)parameters->Buffer, (ULONG)parameters->Length)); + parameters = PH_AUTO(PhConcatStrings(3, L"-update \"", PhGetStringOrEmpty(parameters), L"\"")); + info.lpFile = PhGetStringOrEmpty(context->SetupFilePath); - info.lpParameters = L"-update"; - info.lpVerb = PhGetOwnTokenAttributes().Elevated ? NULL : L"runas"; + info.lpParameters = PhGetString(parameters); + info.lpVerb = UpdaterCheckApplicationDirectory() ? NULL : L"runas"; info.nShow = SW_SHOW; info.hwnd = hwndDlg; + info.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOZONECHECKS; ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle); @@ -79,11 +149,22 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( } else { + ULONG errorCode = GetLastError(); + // Install failed, cancel the shutdown. ProcessHacker_CancelEarlyShutdown(PhMainWndHandle); - // Set button text for next action - //Button_SetText(GetDlgItem(hwndDlg, IDOK), L"Retry"); + // Show error dialog. + if (errorCode != ERROR_CANCELLED) // Ignore UAC decline. + { + PhShowStatus(hwndDlg, L"Unable to execute the setup.", 0, errorCode); + + if (context->StartupCheck) + ShowAvailableDialog(context); + else + ShowCheckForUpdatesDialog(context); + } + return S_FALSE; } } @@ -92,6 +173,7 @@ HRESULT CALLBACK FinalTaskDialogCallbackProc( case TDN_HYPERLINK_CLICKED: { TaskDialogLinkClicked(context); + return S_FALSE; } break; } @@ -114,13 +196,13 @@ VOID ShowUpdateInstallDialog( config.pfCallback = FinalTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pButtons = TaskDialogButtonArray; - config.cButtons = ARRAYSIZE(TaskDialogButtonArray); + config.cButtons = RTL_NUMBER_OF(TaskDialogButtonArray); config.pszWindowTitle = L"Process Hacker - Updater"; - config.pszMainInstruction = L"Ready to install update"; + config.pszMainInstruction = L"Ready to install update?"; config.pszContent = L"The update has been successfully downloaded and verified.\r\n\r\nClick Install to continue."; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + TaskDialogNavigatePage(Context->DialogHandle, &config); } VOID ShowLatestVersionDialog( @@ -128,63 +210,35 @@ VOID ShowLatestVersionDialog( ) { TASKDIALOGCONFIG config; + LARGE_INTEGER time; + SYSTEMTIME systemTime = { 0 }; + PIMAGE_DOS_HEADER imageDosHeader; + PIMAGE_NT_HEADERS imageNtHeader; memset(&config, 0, sizeof(TASKDIALOGCONFIG)); config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS; + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS | TDF_EXPAND_FOOTER_AREA; config.dwCommonButtons = TDCBF_CLOSE_BUTTON; config.hMainIcon = Context->IconLargeHandle; config.cxWidth = 200; config.pfCallback = FinalTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; - - config.pszWindowTitle = L"Process Hacker - Updater"; - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - LARGE_INTEGER time; - SYSTEMTIME systemTime = { 0 }; - PIMAGE_DOS_HEADER imageDosHeader; - PIMAGE_NT_HEADERS imageNtHeader; - - // HACK - imageDosHeader = (PIMAGE_DOS_HEADER)NtCurrentPeb()->ImageBaseAddress; - imageNtHeader = (PIMAGE_NT_HEADERS)PTR_ADD_OFFSET(imageDosHeader, (ULONG)imageDosHeader->e_lfanew); - - RtlSecondsSince1970ToTime(imageNtHeader->FileHeader.TimeDateStamp, &time); - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - - config.pszMainInstruction = L"You're running the latest nightly build"; - config.pszContent = PhaFormatString( - L"Version: v%lu.%lu.%lu\r\nCompiled: %s", - Context->CurrentMajorVersion, - Context->CurrentMinorVersion, - Context->CurrentRevisionVersion, - PhaFormatDateTime(&systemTime)->Buffer - )->Buffer; - - if (PhIsNullOrEmptyString(Context->BuildMessage)) - config.pszExpandedInformation = L"View Changelog"; - else - config.pszExpandedInformation = PhGetStringOrEmpty(Context->BuildMessage); - } - else - { - config.pszMainInstruction = L"You're running the latest version"; - config.pszContent = PhaFormatString( - L"Stable release build: v%lu.%lu.%lu\r\n\r\n", - Context->CurrentMajorVersion, - Context->CurrentMinorVersion, - Context->CurrentRevisionVersion - )->Buffer; - - if (PhIsNullOrEmptyString(Context->BuildMessage)) - config.pszExpandedInformation = L"View Changelog"; - else - config.pszExpandedInformation = PhGetStringOrEmpty(Context->BuildMessage); - } + // HACK + imageDosHeader = (PIMAGE_DOS_HEADER)NtCurrentPeb()->ImageBaseAddress; + imageNtHeader = (PIMAGE_NT_HEADERS)PTR_ADD_OFFSET(imageDosHeader, imageDosHeader->e_lfanew); + RtlSecondsSince1970ToTime(imageNtHeader->FileHeader.TimeDateStamp, &time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + config.pszWindowTitle = L"Process Hacker - Updater"; + config.pszMainInstruction = L"You're running the latest version."; + config.pszContent = PhaFormatString( + L"Version: v%s\r\nCompiled: %s\r\n\r\nView Changelog", + PhGetStringOrEmpty(Context->CurrentVersionString), + PhaFormatDateTime(&systemTime)->Buffer + )->Buffer; + + TaskDialogNavigatePage(Context->DialogHandle, &config); } VOID ShowNewerVersionDialog( @@ -195,38 +249,21 @@ VOID ShowNewerVersionDialog( memset(&config, 0, sizeof(TASKDIALOGCONFIG)); config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_EXPAND_FOOTER_AREA; config.dwCommonButtons = TDCBF_CLOSE_BUTTON; config.hMainIcon = Context->IconLargeHandle; - - config.pszWindowTitle = L"Process Hacker - Updater"; - - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - config.pszMainInstruction = L"You're running the latest nightly build"; - config.pszContent = PhaFormatString( - L"Pre-release build: v%lu.%lu.%lu\r\n", - Context->CurrentMajorVersion, - Context->CurrentMinorVersion, - Context->CurrentRevisionVersion - )->Buffer; - } - else - { - config.pszMainInstruction = L"You're running a pre-release version!"; - config.pszContent = PhaFormatString( - L"Pre-release build: v%lu.%lu.%lu\r\n", - Context->CurrentMajorVersion, - Context->CurrentMinorVersion, - Context->CurrentRevisionVersion - )->Buffer; - } - config.cxWidth = 200; config.pfCallback = FinalTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); + config.pszWindowTitle = L"Process Hacker - Updater"; + config.pszMainInstruction = L"You're running a pre-release build."; + config.pszContent = PhaFormatString( + L"Pre-release build: v%s\r\n", + PhGetStringOrEmpty(Context->CurrentVersionString) + )->Buffer; + + TaskDialogNavigatePage(Context->DialogHandle, &config); } VOID ShowUpdateFailedDialog( @@ -245,7 +282,7 @@ VOID ShowUpdateFailedDialog( config.hMainIcon = Context->IconLargeHandle; config.pszWindowTitle = L"Process Hacker - Updater"; - config.pszMainInstruction = L"Error downloading the update"; + config.pszMainInstruction = L"Error downloading the update."; if (SignatureFailed) { @@ -259,22 +296,12 @@ VOID ShowUpdateFailedDialog( { if (Context->ErrorCode) { - PPH_STRING message; - - message = PhGetMessage( - GetModuleHandle(L"winhttp.dll"), - 0xb, - GetUserDefaultLangID(), - Context->ErrorCode - ); - - //if (PhIsNullOrEmptyString(message)) - // PhMoveReference(&message, PhGetNtMessage(Context->ErrorCode)); - - if (message) - { - config.pszContent = PhaFormatString(L"[%lu] %s", Context->ErrorCode, message->Buffer)->Buffer; - PhDereferenceObject(message); + PPH_STRING errorMessage; + + if (errorMessage = PhHttpSocketGetErrorMessage(Context->ErrorCode)) + { + config.pszContent = PhaFormatString(L"[%lu] %s", Context->ErrorCode, errorMessage->Buffer)->Buffer; + PhDereferenceObject(errorMessage); } else { @@ -291,5 +318,5 @@ VOID ShowUpdateFailedDialog( config.pfCallback = FinalTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; - SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); -} \ No newline at end of file + TaskDialogNavigatePage(Context->DialogHandle, &config); +} diff --git a/plugins/Updater/resource.h b/plugins/Updater/resource.h index 77750932f2ba..67f9ce64bd40 100644 --- a/plugins/Updater/resource.h +++ b/plugins/Updater/resource.h @@ -1,18 +1,20 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Updater.rc -// -#define IDD_OPTIONS 103 -#define IDC_AUTOCHECKBOX 1002 -#define IDC_NIGHTLY 1003 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 105 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1009 -#define _APS_NEXT_SYMED_VALUE 103 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Updater.rc +// +#define IDD_OPTIONS 101 +#define IDD_TEXT 102 +#define IDC_AUTOCHECKBOX 1001 +#define IDC_TEXT 1002 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 + +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 104 +#endif +#endif diff --git a/plugins/Updater/updater.c b/plugins/Updater/updater.c index 4742288974d8..87a2e01c25d0 100644 --- a/plugins/Updater/updater.c +++ b/plugins/Updater/updater.c @@ -1,1295 +1,965 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2011-2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "updater.h" -#include -#include - -HWND UpdateDialogHandle = NULL; -HANDLE UpdateDialogThreadHandle = NULL; -PH_EVENT InitializedEvent = PH_EVENT_INIT; - -PPH_UPDATER_CONTEXT CreateUpdateContext( - _In_ BOOLEAN StartupCheck - ) -{ - PPH_UPDATER_CONTEXT context; - - context = (PPH_UPDATER_CONTEXT)PhCreateAlloc(sizeof(PH_UPDATER_CONTEXT)); - memset(context, 0, sizeof(PH_UPDATER_CONTEXT)); - - PhGetPhVersionNumbers( - &context->CurrentMajorVersion, - &context->CurrentMinorVersion, - NULL, - &context->CurrentRevisionVersion - ); - context->StartupCheck = StartupCheck; - - return context; -} - -VOID FreeUpdateContext( - _In_ _Post_invalid_ PPH_UPDATER_CONTEXT Context - ) -{ - PhClearReference(&Context->Version); - PhClearReference(&Context->RevVersion); - PhClearReference(&Context->RelDate); - PhClearReference(&Context->Size); - PhClearReference(&Context->Hash); - PhClearReference(&Context->Signature); - PhClearReference(&Context->ReleaseNotesUrl); - PhClearReference(&Context->SetupFilePath); - PhClearReference(&Context->SetupFileDownloadUrl); - - if (Context->IconLargeHandle) - DestroyIcon(Context->IconLargeHandle); - - if (Context->IconSmallHandle) - DestroyIcon(Context->IconSmallHandle); - - PhDereferenceObject(Context); -} - -VOID TaskDialogCreateIcons( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - Context->IconLargeHandle = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); - Context->IconSmallHandle = PhLoadIcon( - NtCurrentPeb()->ImageBaseAddress, - MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - PH_LOAD_ICON_SIZE_LARGE, - GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON) - ); - - SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); - SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); -} - -VOID TaskDialogLinkClicked( - _In_ PPH_UPDATER_CONTEXT Context - ) -{ - if (!PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) - { - PhShellExecute(Context->DialogHandle, PhGetStringOrEmpty(Context->ReleaseNotesUrl), NULL); - } -} - -PPH_STRING UpdaterGetOpaqueXmlNodeText( - _In_ mxml_node_t *xmlNode - ) -{ - if (xmlNode && xmlNode->child && xmlNode->child->type == MXML_OPAQUE && xmlNode->child->value.opaque) - { - return PhConvertUtf8ToUtf16(xmlNode->child->value.opaque); - } - - return PhReferenceEmptyString(); -} - -BOOLEAN UpdaterInstalledUsingSetup( - VOID - ) -{ - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Process_Hacker2_is1"); - - HANDLE keyHandle = NULL; - - // Check uninstall entries for the 'Process_Hacker2_is1' registry key. - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName, - 0 - ))) - { - NtClose(keyHandle); - return TRUE; - } - - return FALSE; -} - -BOOLEAN LastUpdateCheckExpired( - VOID - ) -{ - ULONG64 lastUpdateTimeTicks = 0; - LARGE_INTEGER currentUpdateTimeTicks; - PPH_STRING lastUpdateTimeString; - - PhQuerySystemTime(¤tUpdateTimeTicks); - - lastUpdateTimeString = PhGetStringSetting(SETTING_NAME_LAST_CHECK); - PhStringToInteger64(&lastUpdateTimeString->sr, 0, &lastUpdateTimeTicks); - PhDereferenceObject(lastUpdateTimeString); - - if (currentUpdateTimeTicks.QuadPart - lastUpdateTimeTicks >= 7 * PH_TICKS_PER_DAY) - { - PPH_STRING currentUpdateTimeString = PhFormatUInt64(currentUpdateTimeTicks.QuadPart, FALSE); - - PhSetStringSetting2(SETTING_NAME_LAST_CHECK, ¤tUpdateTimeString->sr); - - PhDereferenceObject(currentUpdateTimeString); - return TRUE; - } - - return FALSE; -} - -PPH_STRING UpdateVersionString( - VOID - ) -{ - ULONG majorVersion; - ULONG minorVersion; - ULONG revisionVersion; - PPH_STRING currentVersion; - PPH_STRING versionHeader = NULL; - - PhGetPhVersionNumbers(&majorVersion, &minorVersion, NULL, &revisionVersion); - - if (currentVersion = PhFormatString(L"%lu.%lu.%lu", majorVersion, minorVersion, revisionVersion)) - { - versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer); - PhDereferenceObject(currentVersion); - } - - return versionHeader; -} - -PPH_STRING UpdateWindowsString( - VOID - ) -{ - static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion"); - - HANDLE keyHandle; - PPH_STRING buildLabHeader = NULL; - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_READ, - PH_KEY_LOCAL_MACHINE, - &keyName, - 0 - ))) - { - PPH_STRING buildLabString; - - if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLabEx")) - { - buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer); - PhDereferenceObject(buildLabString); - } - else if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLab")) - { - buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer); - PhDereferenceObject(buildLabString); - } - - NtClose(keyHandle); - } - - return buildLabHeader; -} - -BOOLEAN ParseVersionString( - _Inout_ PPH_UPDATER_CONTEXT Context - ) -{ - PH_STRINGREF remaining, majorPart, minorPart, revisionPart; - ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; - - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->Version)); - - PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); - PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); - PhSplitStringRefAtChar(&remaining, '.', &revisionPart, &remaining); - - PhStringToInteger64(&majorPart, 10, &majorInteger); - PhStringToInteger64(&minorPart, 10, &minorInteger); - PhStringToInteger64(&revisionPart, 10, &revisionInteger); - - Context->MajorVersion = (ULONG)majorInteger; - Context->MinorVersion = (ULONG)minorInteger; - Context->RevisionVersion = (ULONG)revisionInteger; - } - else - { - PhInitializeStringRef(&remaining, PhGetStringOrEmpty(Context->Version)); - PhInitializeStringRef(&revisionPart, PhGetStringOrEmpty(Context->RevVersion)); - - PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); - PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); - - PhStringToInteger64(&majorPart, 10, &majorInteger); - PhStringToInteger64(&minorPart, 10, &minorInteger); - PhStringToInteger64(&revisionPart, 10, &revisionInteger); - - Context->MajorVersion = (ULONG)majorInteger; - Context->MinorVersion = (ULONG)minorInteger; - Context->RevisionVersion = (ULONG)revisionInteger; - } - - return TRUE; -} - -BOOLEAN ReadRequestString( - _In_ HINTERNET Handle, - _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, - _Out_ ULONG *DataLength - ) -{ - PSTR data; - ULONG allocatedLength; - ULONG dataLength; - ULONG returnLength; - BYTE buffer[PAGE_SIZE]; - - allocatedLength = sizeof(buffer); - data = (PSTR)PhAllocate(allocatedLength); - dataLength = 0; - - memset(buffer, 0, PAGE_SIZE); - memset(data, 0, allocatedLength); - - while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength)) - { - if (returnLength == 0) - break; - - if (allocatedLength < dataLength + returnLength) - { - allocatedLength *= 2; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - memcpy(data + dataLength, buffer, returnLength); - - dataLength += returnLength; - } - - if (allocatedLength < dataLength + 1) - { - allocatedLength++; - data = (PSTR)PhReAllocate(data, allocatedLength); - } - - data[dataLength] = 0; - - *DataLength = dataLength; - *Data = data; - - return TRUE; -} - -BOOLEAN QueryUpdateData( - _Inout_ PPH_UPDATER_CONTEXT Context - ) -{ - BOOLEAN success = FALSE; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - ULONG stringBufferLength = 0; - PSTR stringBuffer = NULL; - PVOID jsonObject = NULL; - mxml_node_t* xmlNode = NULL; - PPH_STRING versionHeader = UpdateVersionString(); - PPH_STRING windowsHeader = UpdateWindowsString(); - - // Query the current system proxy - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); - - // Open the HTTP session with the system proxy configuration if available - if (!(httpSessionHandle = WinHttpOpen( - NULL, - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { - Context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, - sizeof(ULONG) - ); - } - - if (!(httpConnectionHandle = WinHttpConnect( - httpSessionHandle, - L"wj32.org", - INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) - { - Context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - L"/processhacker/plugins/nightly.php", - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE - ))) - { - Context->ErrorCode = GetLastError(); - goto CleanupExit; - } - } - else - { - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - L"/processhacker/update.php", - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE - ))) - { - Context->ErrorCode = GetLastError(); - goto CleanupExit; - } - } - - if (WindowsVersion >= WINDOWS_7) - { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); - } - - if (versionHeader) - { - WinHttpAddRequestHeaders( - httpRequestHandle, - versionHeader->Buffer, - (ULONG)versionHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); - } - - if (windowsHeader) - { - WinHttpAddRequestHeaders( - httpRequestHandle, - windowsHeader->Buffer, - (ULONG)windowsHeader->Length / sizeof(WCHAR), - WINHTTP_ADDREQ_FLAG_ADD - ); - } - - if (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { - Context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) - { - Context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (!ReadRequestString(httpRequestHandle, &stringBuffer, &stringBufferLength)) - goto CleanupExit; - - // Check the buffer for valid data - if (stringBuffer == NULL || stringBuffer[0] == '\0') - goto CleanupExit; - - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - if (!(jsonObject = CreateJsonParser(stringBuffer))) - goto CleanupExit; - - Context->Version = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "version")); - Context->RevVersion = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "version")); - Context->RelDate = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "updated")); - Context->Size = PhFormatSize(GetJsonValueAsUlong(jsonObject, "size"), 2); - Context->Hash = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "hash_setup")); - Context->Signature = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "sig")); - Context->ReleaseNotesUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "forum_url")); - Context->SetupFileDownloadUrl = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "setup_url")); - Context->BuildMessage = PhConvertUtf8ToUtf16(GetJsonValueAsString(jsonObject, "message")); - - CleanupJsonParser(jsonObject); - - if (PhIsNullOrEmptyString(Context->Signature)) - goto CleanupExit; - - if (!ParseVersionString(Context)) - goto CleanupExit; - } - else - { - xmlNode = mxmlLoadString(NULL, stringBuffer, MXML_OPAQUE_CALLBACK); - - if (xmlNode == NULL || xmlNode->type != MXML_ELEMENT) - goto CleanupExit; - - Context->Version = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "ver", NULL, NULL, MXML_DESCEND) - ); - Context->RevVersion = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "rev", NULL, NULL, MXML_DESCEND) - ); - Context->RelDate = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "reldate", NULL, NULL, MXML_DESCEND) - ); - Context->Size = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "size", NULL, NULL, MXML_DESCEND) - ); - Context->Hash = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "sha2", NULL, NULL, MXML_DESCEND) - ); - Context->Signature = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "sig", NULL, NULL, MXML_DESCEND) - ); - Context->ReleaseNotesUrl = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "relnotes", NULL, NULL, MXML_DESCEND) - ); - Context->SetupFileDownloadUrl = UpdaterGetOpaqueXmlNodeText( - mxmlFindElement(xmlNode->child, xmlNode, "setupurl", NULL, NULL, MXML_DESCEND) - ); - - if (PhIsNullOrEmptyString(Context->Signature)) - goto CleanupExit; - - if (!ParseVersionString(Context)) - goto CleanupExit; - } - - if (PhIsNullOrEmptyString(Context->Version)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->RevVersion)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->RelDate)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Size)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->Hash)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->ReleaseNotesUrl)) - goto CleanupExit; - if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) - goto CleanupExit; - - success = TRUE; - -CleanupExit: - - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); - - if (xmlNode) - mxmlDelete(xmlNode); - - if (stringBuffer) - PhFree(stringBuffer); - - PhClearReference(&versionHeader); - PhClearReference(&windowsHeader); - - return success; -} - -NTSTATUS UpdateCheckSilentThread( - _In_ PVOID Parameter - ) -{ - PPH_UPDATER_CONTEXT context = NULL; - ULONGLONG currentVersion = 0; - ULONGLONG latestVersion = 0; - - context = CreateUpdateContext(TRUE); - -#ifndef FORCE_UPDATE_CHECK - if (!LastUpdateCheckExpired()) - goto CleanupExit; -#endif - if (!QueryUpdateData(context)) - goto CleanupExit; - - currentVersion = MAKE_VERSION_ULONGLONG( - context->CurrentMajorVersion, - context->CurrentMinorVersion, - context->CurrentRevisionVersion, - 0 - ); - -#ifdef FORCE_UPDATE_CHECK - latestVersion = MAKE_VERSION_ULONGLONG( - 9999, - 9999, - 9999, - 0 - ); -#else - latestVersion = MAKE_VERSION_ULONGLONG( - context->MajorVersion, - context->MinorVersion, - context->RevisionVersion, - 0 - ); -#endif - - // Compare the current version against the latest available version - if (currentVersion < latestVersion) - { - // Don't spam the user the second they open PH, delay dialog creation for 3 seconds. - //Sleep(3000); - - // Check if the user hasn't already opened the dialog. - if (!UpdateDialogHandle) - { - // We have data we're going to cache and pass into the dialog - context->HaveData = TRUE; - - // Show the dialog asynchronously on a new thread. - ShowUpdateDialog(context); - } - } - -CleanupExit: - - if (!context->HaveData) - FreeUpdateContext(context); - - return STATUS_SUCCESS; -} - -NTSTATUS UpdateCheckThread( - _In_ PVOID Parameter - ) -{ - PPH_UPDATER_CONTEXT context = NULL; - ULONGLONG currentVersion = 0; - ULONGLONG latestVersion = 0; - - context = (PPH_UPDATER_CONTEXT)Parameter; - context->ErrorCode = STATUS_SUCCESS; - - // Check if we have cached update data - if (!context->HaveData) - { - context->HaveData = QueryUpdateData(context); - } - - if (!context->HaveData) - { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); - - PhDereferenceObject(context); - return STATUS_SUCCESS; - } - - currentVersion = MAKE_VERSION_ULONGLONG( - context->CurrentMajorVersion, - context->CurrentMinorVersion, - context->CurrentRevisionVersion, - 0 - ); - -#ifdef FORCE_UPDATE_CHECK - latestVersion = MAKE_VERSION_ULONGLONG( - 9999, - 9999, - 9999, - 0 - ); -#else - latestVersion = MAKE_VERSION_ULONGLONG( - context->MajorVersion, - context->MinorVersion, - context->RevisionVersion, - 0 - ); -#endif - - if (currentVersion == latestVersion) - { - // User is running the latest version - PostMessage(context->DialogHandle, PH_UPDATEISCURRENT, 0, 0); - } - else if (currentVersion > latestVersion) - { - // User is running a newer version - PostMessage(context->DialogHandle, PH_UPDATENEWER, 0, 0); - } - else - { - // User is running an older version - PostMessage(context->DialogHandle, PH_UPDATEAVAILABLE, 0, 0); - } - - PhDereferenceObject(context); - return STATUS_SUCCESS; -} - -NTSTATUS UpdateDownloadThread( - _In_ PVOID Parameter - ) -{ - BOOLEAN downloadSuccess = FALSE; - BOOLEAN hashSuccess = FALSE; - BOOLEAN signatureSuccess = FALSE; - HANDLE tempFileHandle = NULL; - HINTERNET httpSessionHandle = NULL; - HINTERNET httpConnectionHandle = NULL; - HINTERNET httpRequestHandle = NULL; - PPH_STRING setupTempPath = NULL; - PPH_STRING downloadHostPath = NULL; - PPH_STRING downloadUrlPath = NULL; - PPH_STRING userAgentString = NULL; - PPH_STRING fullSetupPath = NULL; - PPH_STRING randomGuidString = NULL; - PUPDATER_HASH_CONTEXT hashContext = NULL; - ULONG indexOfFileName = -1; - GUID randomGuid; - URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; - WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; - LARGE_INTEGER timeNow; - LARGE_INTEGER timeStart; - ULONG64 timeTicks = 0; - ULONG64 timeBitsPerSecond = 0; - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter; - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request..."); - - // Create a user agent string. - userAgentString = PhFormatString( - L"PH_%lu.%lu_%lu", - context->CurrentMajorVersion, - context->CurrentMinorVersion, - context->CurrentRevisionVersion - ); - if (PhIsNullOrEmptyString(userAgentString)) - goto CleanupExit; - - setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR)); - if (PhIsNullOrEmptyString(setupTempPath)) - goto CleanupExit; - - if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0) - goto CleanupExit; - if (PhIsNullOrEmptyString(setupTempPath)) - goto CleanupExit; - - // Generate random guid for our directory path. - PhGenerateGuid(&randomGuid); - - if (randomGuidString = PhFormatGuid(&randomGuid)) - { - PPH_STRING guidSubString; - - // Strip the left and right curly brackets. - guidSubString = PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2); - - PhMoveReference(&randomGuidString, guidSubString); - } - - // Append the tempath to our string: %TEMP%RandomString\\processhacker-%lu.%lu-setup.exe - // Example: C:\\Users\\dmex\\AppData\\Temp\\ABCD\\processhacker-2.90-setup.exe - context->SetupFilePath = PhFormatString( - L"%s%s\\processhacker-%lu.%lu-setup.exe", - PhGetStringOrEmpty(setupTempPath), - PhGetStringOrEmpty(randomGuidString), - context->MajorVersion, - context->MinorVersion - ); - if (PhIsNullOrEmptyString(context->SetupFilePath)) - goto CleanupExit; - - // Create the directory if it does not exist. - if (fullSetupPath = PhGetFullPath(PhGetString(context->SetupFilePath), &indexOfFileName)) - { - PPH_STRING directoryPath; - - if (indexOfFileName == -1) - goto CleanupExit; - - if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) - { - SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL); - PhDereferenceObject(directoryPath); - } - } - - // Create output file - if (!NT_SUCCESS(PhCreateFileWin32( - &tempFileHandle, - PhGetString(context->SetupFilePath), - FILE_GENERIC_READ | FILE_GENERIC_WRITE, - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - goto CleanupExit; - } - - // Set lengths to non-zero enabling these params to be cracked. - httpUrlComponents.dwSchemeLength = (ULONG)-1; - httpUrlComponents.dwHostNameLength = (ULONG)-1; - httpUrlComponents.dwUrlPathLength = (ULONG)-1; - - if (!WinHttpCrackUrl( - PhGetStringOrEmpty(context->SetupFileDownloadUrl), - 0, - 0, - &httpUrlComponents - )) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - // Create the Host string. - downloadHostPath = PhCreateStringEx( - httpUrlComponents.lpszHostName, - httpUrlComponents.dwHostNameLength * sizeof(WCHAR) - ); - if (PhIsNullOrEmptyString(downloadHostPath)) - goto CleanupExit; - - // Create the Path string. - downloadUrlPath = PhCreateStringEx( - httpUrlComponents.lpszUrlPath, - httpUrlComponents.dwUrlPathLength * sizeof(WCHAR) - ); - if (PhIsNullOrEmptyString(downloadUrlPath)) - goto CleanupExit; - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); - - // Query the current system proxy - WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); - - // Open the HTTP session with the system proxy configuration if available - if (!(httpSessionHandle = WinHttpOpen( - PhGetStringOrEmpty(userAgentString), - proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - proxyConfig.lpszProxy, - proxyConfig.lpszProxyBypass, - 0 - ))) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (WindowsVersion >= WINDOWS_8_1) - { - ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; - - WinHttpSetOption( - httpSessionHandle, - WINHTTP_OPTION_DECOMPRESSION, - &httpFlags, - sizeof(ULONG) - ); - } - - if (!(httpConnectionHandle = WinHttpConnect( - httpSessionHandle, - PhGetStringOrEmpty(downloadHostPath), - httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, - 0 - ))) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (!(httpRequestHandle = WinHttpOpenRequest( - httpConnectionHandle, - NULL, - PhGetStringOrEmpty(downloadUrlPath), - NULL, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) - ))) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - if (WindowsVersion >= WINDOWS_7) - { - ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG)); - } - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); - - if (!WinHttpSendRequest( - httpRequestHandle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - WINHTTP_NO_REQUEST_DATA, - 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, - 0 - )) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Waiting for response..."); - - if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - else - { - ULONG bytesDownloaded = 0; - ULONG downloadedBytes = 0; - ULONG contentLengthSize = sizeof(ULONG); - ULONG contentLength = 0; - PPH_STRING status; - IO_STATUS_BLOCK isb; - BYTE buffer[PAGE_SIZE]; - - status = PhFormatString(L"Downloading update %lu.%lu.%lu...", - context->MajorVersion, - context->MinorVersion, - context->RevisionVersion - ); - - SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0); - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)status->Buffer); - PhDereferenceObject(status); - - // Start the clock. - PhQuerySystemTime(&timeStart); - - if (!WinHttpQueryHeaders( - httpRequestHandle, - WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, - WINHTTP_HEADER_NAME_BY_INDEX, - &contentLength, - &contentLengthSize, - 0 - )) - { - context->ErrorCode = GetLastError(); - goto CleanupExit; - } - - // Initialize hash algorithm. - if (!(hashContext = UpdaterInitializeHash())) - goto CleanupExit; - - // Zero the buffer. - memset(buffer, 0, PAGE_SIZE); - - // Download the data. - while (WinHttpReadData(httpRequestHandle, buffer, PAGE_SIZE, &bytesDownloaded)) - { - // If we get zero bytes, the file was uploaded or there was an error - if (bytesDownloaded == 0) - break; - - // If the dialog was closed, just cleanup and exit - if (!UpdateDialogThreadHandle) - goto CleanupExit; - - // Update the hash of bytes we downloaded. - UpdaterUpdateHash(hashContext, buffer, bytesDownloaded); - - // Write the downloaded bytes to disk. - if (!NT_SUCCESS(NtWriteFile( - tempFileHandle, - NULL, - NULL, - NULL, - &isb, - buffer, - bytesDownloaded, - NULL, - NULL - ))) - { - goto CleanupExit; - } - - downloadedBytes += (DWORD)isb.Information; - - // Check the number of bytes written are the same we downloaded. - if (bytesDownloaded != isb.Information) - goto CleanupExit; - - // Query the current time - PhQuerySystemTime(&timeNow); - - // Calculate the number of ticks - timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; - timeBitsPerSecond = downloadedBytes / __max(timeTicks, 1); - - // TODO: Update on timer callback. - { - FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100); - PPH_STRING totalLength = PhFormatSize(contentLength, -1); - PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, -1); - PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1); - - PPH_STRING statusMessage = PhFormatString( - L"Downloaded: %s of %s (%.0f%%)\r\nSpeed: %s/s", - PhGetStringOrEmpty(totalDownloaded), - PhGetStringOrEmpty(totalLength), - percent, - PhGetStringOrEmpty(totalSpeed) - ); - - SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)statusMessage->Buffer); - SendMessage(context->DialogHandle, TDM_SET_PROGRESS_BAR_POS, (WPARAM)percent, 0); - - PhDereferenceObject(statusMessage); - PhDereferenceObject(totalSpeed); - PhDereferenceObject(totalLength); - PhDereferenceObject(totalDownloaded); - } - } - - if (UpdaterVerifyHash(hashContext, context->Hash)) - { - hashSuccess = TRUE; - } - - if (UpdaterVerifySignature(hashContext, context->Signature)) - { - signatureSuccess = TRUE; - } - - if (hashSuccess && signatureSuccess) - { - downloadSuccess = TRUE; - } - } - -CleanupExit: - - if (hashContext) - UpdaterDestroyHash(hashContext); - - if (tempFileHandle) - NtClose(tempFileHandle); - - if (httpRequestHandle) - WinHttpCloseHandle(httpRequestHandle); - - if (httpConnectionHandle) - WinHttpCloseHandle(httpConnectionHandle); - - if (httpSessionHandle) - WinHttpCloseHandle(httpSessionHandle); - - PhClearReference(&randomGuidString); - PhClearReference(&fullSetupPath); - PhClearReference(&setupTempPath); - PhClearReference(&downloadHostPath); - PhClearReference(&downloadUrlPath); - PhClearReference(&userAgentString); - - if (UpdateDialogThreadHandle) - { - if (downloadSuccess && hashSuccess && signatureSuccess) - { - PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0); - } - else if (downloadSuccess) - { - PostMessage(context->DialogHandle, PH_UPDATEFAILURE, signatureSuccess, hashSuccess); - } - else - { - PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0); - } - } - - PhDereferenceObject(context); - return STATUS_SUCCESS; -} - -LRESULT CALLBACK TaskDialogSubclassProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case WM_NCDESTROY: - { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case WM_SHOWDIALOG: - { - if (IsMinimized(hwndDlg)) - ShowWindow(hwndDlg, SW_RESTORE); - else - ShowWindow(hwndDlg, SW_SHOW); - - SetForegroundWindow(hwndDlg); - } - break; - case PH_UPDATEAVAILABLE: - { - ShowAvailableDialog(context); - } - break; - case PH_UPDATEISCURRENT: - { - ShowLatestVersionDialog(context); - } - break; - case PH_UPDATENEWER: - { - ShowNewerVersionDialog(context); - } - break; - case PH_UPDATESUCCESS: - { - ShowUpdateInstallDialog(context); - } - break; - case PH_UPDATEFAILURE: - { - if ((BOOLEAN)wParam) - ShowUpdateFailedDialog(context, TRUE, FALSE); - else if ((BOOLEAN)lParam) - ShowUpdateFailedDialog(context, FALSE, TRUE); - else - ShowUpdateFailedDialog(context, FALSE, FALSE); - } - break; - case PH_UPDATEISERRORED: - { - ShowUpdateFailedDialog(context, FALSE, FALSE); - } - break; - //case WM_PARENTNOTIFY: - // { - // if (wParam == WM_CREATE) - // { - // // uMsg == 49251 for expand/collapse button click - // HWND hwndEdit = CreateWindowEx( - // WS_EX_CLIENTEDGE, - // L"EDIT", - // NULL, - // WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, - // 5, - // 5, - // 390, - // 85, - // (HWND)lParam, // parent window - // 0, - // NULL, - // NULL - // ); - // - // CommonCreateFont(-11, hwndEdit); - // - // // Add text to the window. - // SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)L"TEST"); - // } - // } - // break; - //case WM_NCACTIVATE: - // { - // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) - // { - // if (!context->FixedWindowStyles) - // { - // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); - // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); - // context->FixedWindowStyles = TRUE; - // } - // } - // } - // break; - } - - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); -} - -HRESULT CALLBACK TaskDialogBootstrapCallback( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ LONG_PTR dwRefData - ) -{ - PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; - - switch (uMsg) - { - case TDN_CREATED: - { - UpdateDialogHandle = context->DialogHandle = hwndDlg; - - // Center the update window on PH if it's visible else we center on the desktop. - PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); - - // Create the Taskdialog icons - TaskDialogCreateIcons(context); - - // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); - - if (context->StartupCheck) - { - ShowAvailableDialog(context); - } - else - { - ShowCheckForUpdatesDialog(context); - } - } - break; - } - - return S_OK; -} - -NTSTATUS ShowUpdateDialogThread( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - PPH_UPDATER_CONTEXT context; - TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; - - if (Parameter) - context = (PPH_UPDATER_CONTEXT)Parameter; - else - context = CreateUpdateContext(FALSE); - - PhInitializeAutoPool(&autoPool); - - // Start TaskDialog bootstrap - config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; - config.pszContent = L"Initializing..."; - config.lpCallbackData = (LONG_PTR)context; - config.pfCallback = TaskDialogBootstrapCallback; - TaskDialogIndirect(&config, NULL, NULL, NULL); - - FreeUpdateContext(context); - PhDeleteAutoPool(&autoPool); - - if (UpdateDialogThreadHandle) - { - NtClose(UpdateDialogThreadHandle); - UpdateDialogThreadHandle = NULL; - } - - PhResetEvent(&InitializedEvent); - - return STATUS_SUCCESS; -} - -VOID ShowUpdateDialog( - _In_opt_ PPH_UPDATER_CONTEXT Context - ) -{ - if (!UpdateDialogThreadHandle) - { - if (!(UpdateDialogThreadHandle = PhCreateThread(0, ShowUpdateDialogThread, Context))) - { - PhShowStatus(PhMainWndHandle, L"Unable to create the updater window.", 0, GetLastError()); - return; - } - - PhWaitForEvent(&InitializedEvent, NULL); - } - - PostMessage(UpdateDialogHandle, WM_SHOWDIALOG, 0, 0); -} - -VOID StartInitialCheck( - VOID - ) -{ - HANDLE silentCheckThread = NULL; - - if (silentCheckThread = PhCreateThread(0, UpdateCheckSilentThread, NULL)) - NtClose(silentCheckThread); -} \ No newline at end of file +/* + * Process Hacker Plugins - + * Update Checker Plugin + * + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "updater.h" + +HWND UpdateDialogHandle = NULL; +HANDLE UpdateDialogThreadHandle = NULL; +PH_EVENT InitializedEvent = PH_EVENT_INIT; +PPH_OBJECT_TYPE UpdateContextType = NULL; +PH_INITONCE UpdateContextTypeInitOnce = PH_INITONCE_INIT; + +VOID UpdateContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPH_UPDATER_CONTEXT context = Object; + + if (context->CurrentVersionString) + PhDereferenceObject(context->CurrentVersionString); + if (context->Version) + PhDereferenceObject(context->Version); + if (context->RelDate) + PhDereferenceObject(context->RelDate); + if (context->SetupFileDownloadUrl) + PhDereferenceObject(context->SetupFileDownloadUrl); + if (context->SetupFileLength) + PhDereferenceObject(context->SetupFileLength); + if (context->SetupFileHash) + PhDereferenceObject(context->SetupFileHash); + if (context->SetupFileSignature) + PhDereferenceObject(context->SetupFileSignature); + if (context->BuildMessage) + PhDereferenceObject(context->BuildMessage); +} + +PPH_UPDATER_CONTEXT CreateUpdateContext( + _In_ BOOLEAN StartupCheck + ) +{ + PPH_UPDATER_CONTEXT context; + + if (PhBeginInitOnce(&UpdateContextTypeInitOnce)) + { + UpdateContextType = PhCreateObjectType(L"UpdaterContextObjectType", 0, UpdateContextDeleteProcedure); + PhEndInitOnce(&UpdateContextTypeInitOnce); + } + + context = PhCreateObject(sizeof(PH_UPDATER_CONTEXT), UpdateContextType); + memset(context, 0, sizeof(PH_UPDATER_CONTEXT)); + + context->CurrentVersionString = PhGetPhVersion(); + context->StartupCheck = StartupCheck; + + return context; +} + +VOID TaskDialogCreateIcons( + _In_ PPH_UPDATER_CONTEXT Context + ) +{ + Context->IconSmallHandle = PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + Context->IconLargeHandle = PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER)); + + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle); +} + +VOID TaskDialogLinkClicked( + _In_ PPH_UPDATER_CONTEXT Context + ) +{ + if (!PhIsNullOrEmptyString(Context->BuildMessage)) + { + DialogBoxParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_TEXT), + Context->DialogHandle, + TextDlgProc, + (LPARAM)Context + ); + } +} + +BOOLEAN UpdaterInstalledUsingSetup( + VOID + ) +{ + static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ProcessHacker"); + static PH_STRINGREF key2xName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Process_Hacker2_is1"); + + HANDLE keyHandle = NULL; + + // Check uninstall entries for the 'ProcessHacker' registry key. + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName, + 0 + ))) + { + NtClose(keyHandle); + return TRUE; + } + + // Check uninstall entries for the 2.x branch 'Process_Hacker2_is1' registry key. + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &key2xName, + 0 + ))) + { + NtClose(keyHandle); + return TRUE; + } + + return FALSE; +} + +BOOLEAN LastUpdateCheckExpired( + VOID + ) +{ + ULONG64 lastUpdateTimeTicks; + LARGE_INTEGER currentUpdateTimeTicks; + PPH_STRING lastUpdateTimeString; + + PhQuerySystemTime(¤tUpdateTimeTicks); + + lastUpdateTimeString = PhGetStringSetting(SETTING_NAME_LAST_CHECK); + + if (PhIsNullOrEmptyString(lastUpdateTimeString)) + return TRUE; + + if (!PhStringToInteger64(&lastUpdateTimeString->sr, 0, &lastUpdateTimeTicks)) + return TRUE; + + if (currentUpdateTimeTicks.QuadPart - lastUpdateTimeTicks >= 7 * PH_TICKS_PER_DAY) + { + PPH_STRING currentUpdateTimeString; + + currentUpdateTimeString = PhIntegerToString64(currentUpdateTimeTicks.QuadPart, 0, FALSE); + PhSetStringSetting2(SETTING_NAME_LAST_CHECK, ¤tUpdateTimeString->sr); + + PhDereferenceObject(currentUpdateTimeString); + PhDereferenceObject(lastUpdateTimeString); + return TRUE; + } + + PhDereferenceObject(lastUpdateTimeString); + return FALSE; +} + +PPH_STRING UpdateVersionString( + VOID + ) +{ + ULONG majorVersion; + ULONG minorVersion; + ULONG revisionVersion; + PPH_STRING currentVersion; + PPH_STRING versionHeader = NULL; + + PhGetPhVersionNumbers(&majorVersion, &minorVersion, NULL, &revisionVersion); + + if (currentVersion = PhFormatString(L"%lu.%lu.%lu", majorVersion, minorVersion, revisionVersion)) + { + versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer); + PhDereferenceObject(currentVersion); + } + + return versionHeader; +} + +PPH_STRING UpdateWindowsString( + VOID + ) +{ + static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion"); + + HANDLE keyHandle; + PPH_STRING buildLabHeader = NULL; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName, + 0 + ))) + { + PPH_STRING buildLabString; + + if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLabEx")) + { + buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer); + PhDereferenceObject(buildLabString); + } + else if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLab")) + { + buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer); + PhDereferenceObject(buildLabString); + } + + NtClose(keyHandle); + } + + return buildLabHeader; +} + +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString + ) +{ + PH_STRINGREF remaining, majorPart, minorPart, revisionPart; + ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; + + PhInitializeStringRef(&remaining, PhGetStringOrEmpty(VersionString)); + + PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); + PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); + PhSplitStringRefAtChar(&remaining, '.', &revisionPart, &remaining); + + PhStringToInteger64(&majorPart, 10, &majorInteger); + PhStringToInteger64(&minorPart, 10, &minorInteger); + PhStringToInteger64(&revisionPart, 10, &revisionInteger); + + return MAKE_VERSION_ULONGLONG( + (ULONG)majorInteger, + (ULONG)minorInteger, + (ULONG)revisionInteger, + 0 + ); +} + +BOOLEAN QueryUpdateData( + _Inout_ PPH_UPDATER_CONTEXT Context + ) +{ + BOOLEAN success = FALSE; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_BYTES jsonString = NULL; + PVOID jsonObject = NULL; + + if (!PhHttpSocketCreate(&httpContext, NULL)) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!PhHttpSocketConnect( + httpContext, + L"wj32.org", + PH_HTTP_DEFAULT_HTTPS_PORT + )) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!PhHttpSocketBeginRequest( + httpContext, + NULL, + L"/processhacker/nightly.php?phupdater", + PH_HTTP_FLAG_REFRESH | PH_HTTP_FLAG_SECURE + )) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + { + PPH_STRING versionHeader; + PPH_STRING windowsHeader; + + if (versionHeader = UpdateVersionString()) + { + PhHttpSocketAddRequestHeaders(httpContext, versionHeader->Buffer, (ULONG)versionHeader->Length / sizeof(WCHAR)); + PhDereferenceObject(versionHeader); + } + + if (windowsHeader = UpdateWindowsString()) + { + PhHttpSocketAddRequestHeaders(httpContext, windowsHeader->Buffer, (ULONG)windowsHeader->Length / sizeof(WCHAR)); + PhDereferenceObject(windowsHeader); + } + } + + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!PhHttpSocketEndRequest(httpContext)) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!(jsonString = PhHttpSocketDownloadString(httpContext, FALSE))) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!(jsonObject = PhCreateJsonParser(jsonString->Buffer))) + goto CleanupExit; + + Context->Version = PhGetJsonValueAsString(jsonObject, "version"); + Context->RelDate = PhGetJsonValueAsString(jsonObject, "updated"); + Context->SetupFileDownloadUrl = PhGetJsonValueAsString(jsonObject, "setup_url"); + Context->SetupFileLength = PhFormatSize(PhGetJsonValueAsLong64(jsonObject, "setup_length"), 2); + Context->SetupFileHash = PhGetJsonValueAsString(jsonObject, "setup_hash"); + Context->SetupFileSignature = PhGetJsonValueAsString(jsonObject, "setup_sig"); + Context->BuildMessage = PhGetJsonValueAsString(jsonObject, "changelog"); + + Context->CurrentVersion = ParseVersionString(Context->CurrentVersionString); +#ifdef FORCE_LATEST_VERSION + Context->LatestVersion = ParseVersionString(Context->CurrentVersionString); +#else + Context->LatestVersion = ParseVersionString(Context->Version); +#endif + + PhFreeJsonParser(jsonObject); + + if (PhIsNullOrEmptyString(Context->Version)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->RelDate)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileLength)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileHash)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileSignature)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->BuildMessage)) + goto CleanupExit; + + success = TRUE; + +CleanupExit: + + if (httpContext) + PhHttpSocketDestroy(httpContext); + + if (jsonString) + PhDereferenceObject(jsonString); + + if (success && !PhIsNullOrEmptyString(Context->BuildMessage)) + { + PH_STRING_BUILDER sb; + + PhInitializeStringBuilder(&sb, 0x100); + + for (SIZE_T i = 0; i < Context->BuildMessage->Length / sizeof(WCHAR); i++) + { + if (Context->BuildMessage->Data[i] == '\n') + PhAppendStringBuilder2(&sb, L"\r\n"); + else + PhAppendCharStringBuilder(&sb, Context->BuildMessage->Data[i]); + } + + PhMoveReference(&Context->BuildMessage, PhFinalStringBuilderString(&sb)); + } + + return success; +} + +NTSTATUS UpdateCheckSilentThread( + _In_ PVOID Parameter + ) +{ + PPH_UPDATER_CONTEXT context; + + context = CreateUpdateContext(TRUE); + +#ifndef FORCE_UPDATE_CHECK + if (!LastUpdateCheckExpired()) + goto CleanupExit; +#endif + + PhDelayExecution(5 * 1000); + + // Query latest update information from the server. + if (!QueryUpdateData(context)) + goto CleanupExit; + + // Compare the current version against the latest available version + if (context->CurrentVersion < context->LatestVersion) + { + // Check if the user hasn't already opened the dialog. + if (!UpdateDialogHandle) + { + // We have data we're going to cache and pass into the dialog + context->HaveData = TRUE; + + // Show the dialog asynchronously on a new thread. + ShowUpdateDialog(context); + } + } + +CleanupExit: + + if (!context->HaveData) + PhDereferenceObject(context); + + return STATUS_SUCCESS; +} + +NTSTATUS UpdateCheckThread( + _In_ PVOID Parameter + ) +{ + PPH_UPDATER_CONTEXT context; + PH_AUTO_POOL autoPool; + + context = (PPH_UPDATER_CONTEXT)Parameter; + context->ErrorCode = STATUS_SUCCESS; + + PhInitializeAutoPool(&autoPool); + + PhClearCacheDirectory(); // HACK + + // Check if we have cached update data + if (!context->HaveData) + { + context->HaveData = QueryUpdateData(context); + } + + if (!context->HaveData) + { + PostMessage(context->DialogHandle, PH_SHOWERROR, FALSE, FALSE); + + PhDereferenceObject(context); + PhDeleteAutoPool(&autoPool); + return STATUS_SUCCESS; + } + + if (context->CurrentVersion == context->LatestVersion) + { + // User is running the latest version + PostMessage(context->DialogHandle, PH_SHOWLATEST, 0, 0); + } + else if (context->CurrentVersion > context->LatestVersion) + { + // User is running a newer version + PostMessage(context->DialogHandle, PH_SHOWNEWEST, 0, 0); + } + else + { + // User is running an older version + PostMessage(context->DialogHandle, PH_SHOWUPDATE, 0, 0); + } + + PhDereferenceObject(context); + PhDeleteAutoPool(&autoPool); + return STATUS_SUCCESS; +} + +static PPH_STRING UpdaterParseDownloadFileName( + _In_ PPH_STRING DownloadUrlPath + ) +{ + PH_STRINGREF pathPart; + PH_STRINGREF baseNamePart; + PPH_STRING filePath; + PPH_STRING downloadFileName; + + if (!PhSplitStringRefAtLastChar(&DownloadUrlPath->sr, '/', &pathPart, &baseNamePart)) + return NULL; + + downloadFileName = PhCreateString2(&baseNamePart); + filePath = PhCreateCacheFile(downloadFileName); + PhDereferenceObject(downloadFileName); + + return filePath; +} + +NTSTATUS UpdateDownloadThread( + _In_ PVOID Parameter + ) +{ + PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter; + BOOLEAN downloadSuccess = FALSE; + BOOLEAN hashSuccess = FALSE; + BOOLEAN signatureSuccess = FALSE; + HANDLE tempFileHandle = NULL; + PPH_HTTP_CONTEXT httpContext = NULL; + PPH_STRING downloadHostPath = NULL; + PPH_STRING downloadUrlPath = NULL; + PUPDATER_HASH_CONTEXT hashContext = NULL; + USHORT httpPort = 0; + LARGE_INTEGER timeNow; + LARGE_INTEGER timeStart; + ULONG64 timeTicks = 0; + ULONG64 timeBitsPerSecond = 0; + + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request..."); + + if (!PhHttpSocketParseUrl( + context->SetupFileDownloadUrl, + &downloadHostPath, + &downloadUrlPath, + &httpPort + )) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + // Create the local path string. + context->SetupFilePath = UpdaterParseDownloadFileName(downloadUrlPath); + if (PhIsNullOrEmptyString(context->SetupFilePath)) + goto CleanupExit; + + // Create temporary output file. + if (!NT_SUCCESS(PhCreateFileWin32( + &tempFileHandle, + PhGetString(context->SetupFilePath), + FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + goto CleanupExit; + } + + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting..."); + + if (!PhHttpSocketCreate(&httpContext, NULL)) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!PhHttpSocketConnect( + httpContext, + PhGetString(downloadHostPath), + httpPort + )) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!PhHttpSocketBeginRequest( + httpContext, + NULL, + PhGetString(downloadUrlPath), + PH_HTTP_FLAG_REFRESH | (httpPort == PH_HTTP_DEFAULT_HTTPS_PORT ? PH_HTTP_FLAG_SECURE : 0) + )) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request..."); + + if (!PhHttpSocketSendRequest(httpContext, NULL, 0)) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Waiting for response..."); + + if (!PhHttpSocketEndRequest(httpContext)) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + else + { + ULONG bytesDownloaded = 0; + ULONG downloadedBytes = 0; + ULONG contentLength = 0; + PPH_STRING status; + IO_STATUS_BLOCK isb; + BYTE buffer[PAGE_SIZE]; + + status = PhFormatString(L"Downloading update %s...", PhGetStringOrEmpty(context->Version)); + + SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0); + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)status->Buffer); + PhDereferenceObject(status); + + if (!PhHttpSocketQueryHeaderUlong( + httpContext, + PH_HTTP_QUERY_CONTENT_LENGTH, + &contentLength + )) + { + context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + // Initialize hash algorithm. + if (!(hashContext = UpdaterInitializeHash())) + goto CleanupExit; + + // Zero the buffer. + memset(buffer, 0, PAGE_SIZE); + + // Start the clock. + PhQuerySystemTime(&timeStart); + + // Download the data. + while (PhHttpSocketReadData(httpContext, buffer, PAGE_SIZE, &bytesDownloaded)) + { + // If we get zero bytes, the file was uploaded or there was an error + if (bytesDownloaded == 0) + break; + + // If the dialog was closed, just cleanup and exit + if (!UpdateDialogThreadHandle) + goto CleanupExit; + + // Update the hash of bytes we downloaded. + UpdaterUpdateHash(hashContext, buffer, bytesDownloaded); + + // Write the downloaded bytes to disk. + if (!NT_SUCCESS(NtWriteFile( + tempFileHandle, + NULL, + NULL, + NULL, + &isb, + buffer, + bytesDownloaded, + NULL, + NULL + ))) + { + goto CleanupExit; + } + + downloadedBytes += (DWORD)isb.Information; + + // Check the number of bytes written are the same we downloaded. + if (bytesDownloaded != isb.Information) + goto CleanupExit; + + // Query the current time + PhQuerySystemTime(&timeNow); + + // Calculate the number of ticks + timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; + timeBitsPerSecond = downloadedBytes / __max(timeTicks, 1); + + // TODO: Update on timer callback. + { + FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100); + PPH_STRING totalLength = PhFormatSize(contentLength, ULONG_MAX); + PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, ULONG_MAX); + PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, ULONG_MAX); + + PPH_STRING statusMessage = PhFormatString( + L"Downloaded: %s of %s (%.0f%%)\r\nSpeed: %s/s", + PhGetStringOrEmpty(totalDownloaded), + PhGetStringOrEmpty(totalLength), + percent, + PhGetStringOrEmpty(totalSpeed) + ); + + SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)statusMessage->Buffer); + SendMessage(context->DialogHandle, TDM_SET_PROGRESS_BAR_POS, (WPARAM)percent, 0); + + PhDereferenceObject(statusMessage); + PhDereferenceObject(totalSpeed); + PhDereferenceObject(totalLength); + PhDereferenceObject(totalDownloaded); + } + } + + if (UpdaterVerifyHash(hashContext, context->SetupFileHash)) + { + hashSuccess = TRUE; + } + + if (UpdaterVerifySignature(hashContext, context->SetupFileSignature)) + { + signatureSuccess = TRUE; + } + + if (hashSuccess && signatureSuccess) + { + downloadSuccess = TRUE; + } + } + +CleanupExit: + + if (httpContext) + PhHttpSocketDestroy(httpContext); + + if (hashContext) + UpdaterDestroyHash(hashContext); + + if (tempFileHandle) + NtClose(tempFileHandle); + + if (downloadHostPath) + PhDereferenceObject(downloadHostPath); + + if (downloadUrlPath) + PhDereferenceObject(downloadUrlPath); + + if (UpdateDialogThreadHandle) + { + if (downloadSuccess && hashSuccess && signatureSuccess) + { + PostMessage(context->DialogHandle, PH_SHOWINSTALL, 0, 0); + } + else if (downloadSuccess) + { + if (signatureSuccess) + PostMessage(context->DialogHandle, PH_SHOWERROR, TRUE, FALSE); + else if (hashSuccess) + PostMessage(context->DialogHandle, PH_SHOWERROR, FALSE, TRUE); + else + PostMessage(context->DialogHandle, PH_SHOWERROR, FALSE, FALSE); + } + else + { + PostMessage(context->DialogHandle, PH_SHOWERROR, FALSE, FALSE); + } + } + + PhDereferenceObject(context); + return STATUS_SUCCESS; +} + +LRESULT CALLBACK TaskDialogSubclassProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPH_UPDATER_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hwndDlg, UCHAR_MAX))) + return 0; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwndDlg, UCHAR_MAX); + + PhUnregisterWindowCallback(hwndDlg); + } + break; + case PH_SHOWDIALOG: + { + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); + + SetForegroundWindow(hwndDlg); + } + break; + case PH_SHOWLATEST: + { + ShowLatestVersionDialog(context); + } + break; + case PH_SHOWNEWEST: + { + ShowNewerVersionDialog(context); + } + break; + case PH_SHOWUPDATE: + { + ShowAvailableDialog(context); + } + break; + case PH_SHOWINSTALL: + { + ShowUpdateInstallDialog(context); + } + break; + case PH_SHOWERROR: + { + ShowUpdateFailedDialog(context, (BOOLEAN)wParam, (BOOLEAN)lParam); + } + break; + //case WM_PARENTNOTIFY: + // { + // if (wParam == WM_CREATE) + // { + // // uMsg == 49251 for expand/collapse button click + // HWND hwndEdit = CreateWindowEx( + // WS_EX_CLIENTEDGE, + // L"EDIT", + // NULL, + // WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, + // 5, + // 5, + // 390, + // 85, + // (HWND)lParam, // parent window + // 0, + // NULL, + // NULL + // ); + // + // PhCreateCommonFont(-11, hwndEdit); + // + // // Add text to the window. + // SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)L"TEST"); + // } + // } + // break; + //case WM_NCACTIVATE: + // { + // if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) + // { + // if (!context->FixedWindowStyles) + // { + // SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle); + // PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW); + // context->FixedWindowStyles = TRUE; + // } + // } + // } + // break; + } + + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); +} + +HRESULT CALLBACK TaskDialogBootstrapCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_CREATED: + { + UpdateDialogHandle = context->DialogHandle = hwndDlg; + + // Center the update window on PH if it's visible else we center on the desktop. + PhCenterWindow(hwndDlg, (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle)) ? PhMainWndHandle : NULL); + + // Create the Taskdialog icons. + TaskDialogCreateIcons(context); + + PhRegisterWindowCallback(hwndDlg, PH_PLUGIN_WINDOW_EVENT_TYPE_TOPMOST, NULL); + + // Subclass the Taskdialog. + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, UCHAR_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)TaskDialogSubclassProc); + + if (context->StartupCheck) + ShowAvailableDialog(context); + else + ShowCheckForUpdatesDialog(context); + } + break; + } + + return S_OK; +} + +NTSTATUS ShowUpdateDialogThread( + _In_ PVOID Parameter + ) +{ + PH_AUTO_POOL autoPool; + PPH_UPDATER_CONTEXT context; + TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; + + if (Parameter) + context = (PPH_UPDATER_CONTEXT)Parameter; + else + context = CreateUpdateContext(FALSE); + + PhInitializeAutoPool(&autoPool); + + // Start TaskDialog bootstrap + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; + config.hInstance = PluginInstance->DllBase; + config.pszContent = L"Initializing..."; + config.lpCallbackData = (LONG_PTR)context; + config.pfCallback = TaskDialogBootstrapCallback; + TaskDialogIndirect(&config, NULL, NULL, NULL); + + PhDereferenceObject(context); + PhDeleteAutoPool(&autoPool); + + if (UpdateDialogThreadHandle) + { + NtClose(UpdateDialogThreadHandle); + UpdateDialogThreadHandle = NULL; + } + + PhResetEvent(&InitializedEvent); + + return STATUS_SUCCESS; +} + +VOID ShowUpdateDialog( + _In_opt_ PPH_UPDATER_CONTEXT Context + ) +{ + if (!UpdateDialogThreadHandle) + { + if (!NT_SUCCESS(PhCreateThreadEx(&UpdateDialogThreadHandle, ShowUpdateDialogThread, Context))) + { + PhShowError(PhMainWndHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&InitializedEvent, NULL); + } + + PostMessage(UpdateDialogHandle, PH_SHOWDIALOG, 0, 0); +} + +VOID StartInitialCheck( + VOID + ) +{ + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), UpdateCheckSilentThread, NULL); +} diff --git a/plugins/Updater/updater.h b/plugins/Updater/updater.h index 2a5cafa71846..5e8e0a747213 100644 --- a/plugins/Updater/updater.h +++ b/plugins/Updater/updater.h @@ -1,220 +1,244 @@ -/* - * Process Hacker Plugins - - * Update Checker Plugin - * - * Copyright (C) 2011-2015 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef __UPDATER_H__ -#define __UPDATER_H__ - -#define CINTERFACE -#define COBJMACROS -#define INITGUID -#include -#include -#include -#include -#include -#include -#include - -#include "resource.h" - -#define UPDATE_MENUITEM 1001 -#define PH_UPDATEISERRORED (WM_APP + 501) -#define PH_UPDATEAVAILABLE (WM_APP + 502) -#define PH_UPDATEISCURRENT (WM_APP + 503) -#define PH_UPDATENEWER (WM_APP + 504) -#define PH_UPDATESUCCESS (WM_APP + 505) -#define PH_UPDATEFAILURE (WM_APP + 506) -#define WM_SHOWDIALOG (WM_APP + 550) - -#define PLUGIN_NAME L"ProcessHacker.UpdateChecker" -#define SETTING_NAME_AUTO_CHECK (PLUGIN_NAME L".PromptStart") -#define SETTING_NAME_LAST_CHECK (PLUGIN_NAME L".LastUpdateCheckTime") -#define SETTING_NAME_NIGHTLY_BUILD (PLUGIN_NAME L".NightlyBuilds") - -#define MAKE_VERSION_ULONGLONG(major, minor, build, revision) \ - (((ULONGLONG)(major) << 48) | \ - ((ULONGLONG)(minor) << 32) | \ - ((ULONGLONG)(build) << 16) | \ - ((ULONGLONG)(revision) << 0)) - -#ifdef _DEBUG -// Force update checks to succeed (most of the below flags require this to be defined). -//#define FORCE_UPDATE_CHECK -// Force update check to show the current version as the latest version. -//#define FORCE_LATEST_VERSION -#endif - -extern HWND UpdateDialogHandle; -extern PH_EVENT InitializedEvent; -extern PPH_PLUGIN PluginInstance; - -typedef struct _PH_UPDATER_CONTEXT -{ - union - { - BOOLEAN Flags; - struct - { - BOOLEAN StartupCheck : 1; - BOOLEAN HaveData : 1; - BOOLEAN FixedWindowStyles : 1; - BOOLEAN Spare : 5; - }; - }; - - HICON IconSmallHandle; - HICON IconLargeHandle; - - HWND DialogHandle; - - ULONG ErrorCode; - ULONG MinorVersion; - ULONG MajorVersion; - ULONG RevisionVersion; - ULONG CurrentMinorVersion; - ULONG CurrentMajorVersion; - ULONG CurrentRevisionVersion; - PPH_STRING Version; - PPH_STRING RevVersion; - PPH_STRING RelDate; - PPH_STRING Size; - PPH_STRING Hash; - PPH_STRING Signature; - PPH_STRING ReleaseNotesUrl; - PPH_STRING SetupFileDownloadUrl; - PPH_STRING SetupFilePath; - - // Nightly builds only - PPH_STRING BuildMessage; -} PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; - -VOID TaskDialogLinkClicked( - _In_ PPH_UPDATER_CONTEXT Context - ); - -NTSTATUS UpdateCheckThread( - _In_ PVOID Parameter - ); - -NTSTATUS UpdateDownloadThread( - _In_ PVOID Parameter - ); - -// page1.c -VOID ShowCheckForUpdatesDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// page2.c -VOID ShowCheckingForUpdatesDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// page3.c -VOID ShowAvailableDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// page4.c -VOID ShowProgressDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -// page5.c - -VOID ShowUpdateInstallDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -VOID ShowLatestVersionDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -VOID ShowNewerVersionDialog( - _In_ PPH_UPDATER_CONTEXT Context - ); - -VOID ShowUpdateFailedDialog( - _In_ PPH_UPDATER_CONTEXT Context, - _In_ BOOLEAN HashFailed, - _In_ BOOLEAN SignatureFailed - ); - -// updater.c - -VOID ShowUpdateDialog( - _In_opt_ PPH_UPDATER_CONTEXT Context - ); - -VOID StartInitialCheck( - VOID - ); - -BOOLEAN UpdaterInstalledUsingSetup( - VOID - ); - -// options.c - -VOID ShowOptionsDialog( - _In_opt_ HWND Parent - ); - -// verify.c - -typedef struct _UPDATER_HASH_CONTEXT -{ - BCRYPT_ALG_HANDLE SignAlgHandle; - BCRYPT_ALG_HANDLE HashAlgHandle; - BCRYPT_KEY_HANDLE KeyHandle; - BCRYPT_HASH_HANDLE HashHandle; - ULONG HashObjectSize; - ULONG HashSize; - PVOID HashObject; - PVOID Hash; -} UPDATER_HASH_CONTEXT, *PUPDATER_HASH_CONTEXT; - -PUPDATER_HASH_CONTEXT UpdaterInitializeHash( - VOID - ); - -BOOLEAN UpdaterUpdateHash( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_reads_bytes_(Length) PVOID Buffer, - _In_ ULONG Length - ); - -BOOLEAN UpdaterVerifyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_ PPH_STRING Sha2Hash - ); - -BOOLEAN UpdaterVerifySignature( - _Inout_ PUPDATER_HASH_CONTEXT Context, - _In_ PPH_STRING HexSignature - ); - -VOID UpdaterDestroyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context - ); - -#endif \ No newline at end of file +/* + * Process Hacker Plugins - + * Update Checker Plugin + * + * Copyright (C) 2011-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef __UPDATER_H__ +#define __UPDATER_H__ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "resource.h" + +#define UPDATE_MENUITEM 1001 +#define PH_SHOWDIALOG (WM_APP + 501) +#define PH_SHOWLATEST (WM_APP + 502) +#define PH_SHOWNEWEST (WM_APP + 503) +#define PH_SHOWUPDATE (WM_APP + 504) +#define PH_SHOWINSTALL (WM_APP + 505) +#define PH_SHOWERROR (WM_APP + 506) + +#define PLUGIN_NAME L"ProcessHacker.UpdateChecker" +#define SETTING_NAME_AUTO_CHECK (PLUGIN_NAME L".PromptStart") +#define SETTING_NAME_LAST_CHECK (PLUGIN_NAME L".LastUpdateCheckTime") +#define SETTING_NAME_CHANGELOG_WINDOW_POSITION (PLUGIN_NAME L".ChangelogWindowPosition") +#define SETTING_NAME_CHANGELOG_WINDOW_SIZE (PLUGIN_NAME L".ChangelogWindowSize") + +#define MAKE_VERSION_ULONGLONG(major, minor, build, revision) \ + (((ULONGLONG)(major) << 48) | \ + ((ULONGLONG)(minor) << 32) | \ + ((ULONGLONG)(build) << 16) | \ + ((ULONGLONG)(revision) << 0)) + +#ifdef _DEBUG +// Force update checks to succeed (most of the below flags require this to be defined). +//#define FORCE_UPDATE_CHECK +// Force update check to show the current version as the latest version. +//#define FORCE_LATEST_VERSION +#endif + +extern HWND UpdateDialogHandle; +extern PH_EVENT InitializedEvent; +extern PPH_PLUGIN PluginInstance; + +typedef struct _PH_UPDATER_CONTEXT +{ + union + { + BOOLEAN Flags; + struct + { + BOOLEAN StartupCheck : 1; + BOOLEAN HaveData : 1; + BOOLEAN FixedWindowStyles : 1; + BOOLEAN Spare : 5; + }; + }; + + HICON IconSmallHandle; + HICON IconLargeHandle; + + HWND DialogHandle; + WNDPROC DefaultWindowProc; + + ULONG ErrorCode; + ULONG64 CurrentVersion; + ULONG64 LatestVersion; + PPH_STRING SetupFilePath; + PPH_STRING CurrentVersionString; + PPH_STRING Version; + PPH_STRING RelDate; + + PPH_STRING SetupFileLength; + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFileHash; + PPH_STRING SetupFileSignature; + + // Nightly builds only + PPH_STRING BuildMessage; +} PH_UPDATER_CONTEXT, *PPH_UPDATER_CONTEXT; + +// TDM_NAVIGATE_PAGE can not be called from other threads without comctl32.dll throwing access violations +// after navigating to the page and you press keys such as ctrl, alt, home and insert. (dmex) +#define TaskDialogNavigatePage(WindowHandle, Config) \ + assert(HandleToUlong(NtCurrentThreadId()) == GetWindowThreadProcessId(WindowHandle, NULL)); \ + SendMessage(WindowHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)Config); + +VOID TaskDialogLinkClicked( + _In_ PPH_UPDATER_CONTEXT Context + ); + +NTSTATUS UpdateCheckThread( + _In_ PVOID Parameter + ); + +NTSTATUS UpdateDownloadThread( + _In_ PVOID Parameter + ); + +// page1.c +VOID ShowCheckForUpdatesDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +// page2.c +VOID ShowCheckingForUpdatesDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +// page3.c +VOID ShowAvailableDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +// page4.c +VOID ShowProgressDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +// page5.c + +VOID ShowUpdateInstallDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +VOID ShowLatestVersionDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +VOID ShowNewerVersionDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +VOID ShowUpdateFailedDialog( + _In_ PPH_UPDATER_CONTEXT Context, + _In_ BOOLEAN HashFailed, + _In_ BOOLEAN SignatureFailed + ); + +// updater.c + +VOID ShowUpdateDialog( + _In_opt_ PPH_UPDATER_CONTEXT Context + ); + +VOID StartInitialCheck( + VOID + ); + +BOOLEAN UpdaterInstalledUsingSetup( + VOID + ); + +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString + ); + +// options.c + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK TextDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// verify.c + +typedef struct _UPDATER_HASH_CONTEXT +{ + BCRYPT_ALG_HANDLE SignAlgHandle; + BCRYPT_ALG_HANDLE HashAlgHandle; + BCRYPT_KEY_HANDLE KeyHandle; + BCRYPT_HASH_HANDLE HashHandle; + ULONG HashObjectSize; + ULONG HashSize; + PVOID HashObject; + PVOID Hash; +} UPDATER_HASH_CONTEXT, *PUPDATER_HASH_CONTEXT; + +PUPDATER_HASH_CONTEXT UpdaterInitializeHash( + VOID + ); + +BOOLEAN UpdaterUpdateHash( + _Inout_ PUPDATER_HASH_CONTEXT Context, + _In_reads_bytes_(Length) PVOID Buffer, + _In_ ULONG Length + ); + +BOOLEAN UpdaterVerifyHash( + _Inout_ PUPDATER_HASH_CONTEXT Context, + _In_ PPH_STRING Sha2Hash + ); + +BOOLEAN UpdaterVerifySignature( + _Inout_ PUPDATER_HASH_CONTEXT Context, + _In_ PPH_STRING HexSignature + ); + +VOID UpdaterDestroyHash( + _Inout_ PUPDATER_HASH_CONTEXT Context + ); + +// info.c + +VOID ShowLinkDialog( + _In_ PPH_UPDATER_CONTEXT Context + ); + +#endif diff --git a/plugins/Updater/verify.c b/plugins/Updater/verify.c index 80dd999d870c..68af978790fb 100644 --- a/plugins/Updater/verify.c +++ b/plugins/Updater/verify.c @@ -2,7 +2,7 @@ * Process Hacker Plugins - * Update Checker Plugin * - * Copyright (C) 2016 dmex + * Copyright (C) 2016-2019 dmex * * This file is part of Process Hacker. * @@ -52,7 +52,6 @@ PUPDATER_HASH_CONTEXT UpdaterInitializeHash( VOID ) { - BOOLEAN success = FALSE; ULONG querySize; PUPDATER_HASH_CONTEXT hashContext; @@ -70,35 +69,17 @@ PUPDATER_HASH_CONTEXT UpdaterInitializeHash( goto CleanupExit; } - if (PhGetIntegerSetting(SETTING_NAME_NIGHTLY_BUILD)) - { - if (!NT_SUCCESS(BCryptImportKeyPair( - hashContext->SignAlgHandle, - NULL, - BCRYPT_ECCPUBLIC_BLOB, - &hashContext->KeyHandle, - UpdaterTrustedNightlyPublicKey, - sizeof(UpdaterTrustedNightlyPublicKey), - 0 - ))) - { - goto CleanupExit; - } - } - else + if (!NT_SUCCESS(BCryptImportKeyPair( + hashContext->SignAlgHandle, + NULL, + BCRYPT_ECCPUBLIC_BLOB, + &hashContext->KeyHandle, + UpdaterTrustedNightlyPublicKey, + sizeof(UpdaterTrustedNightlyPublicKey), + 0 + ))) { - if (!NT_SUCCESS(BCryptImportKeyPair( - hashContext->SignAlgHandle, - NULL, - BCRYPT_ECCPUBLIC_BLOB, - &hashContext->KeyHandle, - UpdaterTrustedPublicKey, - sizeof(UpdaterTrustedPublicKey), - 0 - ))) - { - goto CleanupExit; - } + goto CleanupExit; } // Open the hash algorithm and allocate memory for the hash object. @@ -251,7 +232,7 @@ BOOLEAN UpdaterVerifySignature( } VOID UpdaterDestroyHash( - _Inout_ PUPDATER_HASH_CONTEXT Context + _Frees_ptr_opt_ PUPDATER_HASH_CONTEXT Context ) { if (Context->HashAlgHandle) @@ -273,4 +254,4 @@ VOID UpdaterDestroyHash( PhFree(Context->Hash); PhFree(Context); -} \ No newline at end of file +} diff --git a/plugins/UserNotes/CHANGELOG.txt b/plugins/UserNotes/CHANGELOG.txt index 2a7350cbfe01..95197b0d1630 100644 --- a/plugins/UserNotes/CHANGELOG.txt +++ b/plugins/UserNotes/CHANGELOG.txt @@ -1,24 +1,24 @@ -1.7 - * Added ability to save process affinity - -1.6 - * Added "Collapse by Default" option for processes - -1.5 - * Added individual process highlighting support - -1.4 - * Added tray icon mini info window support - -1.3 - * Added ability to save I/O priority - -1.2 - * Fixed bug where process priorities were not actually saved - -1.1 - * Added ability to save process priority - * Added "Only for processes with the same command line" option for process comments - -1.0 - * Initial release +1.7 + * Added ability to save process affinity + +1.6 + * Added "Collapse by Default" option for processes + +1.5 + * Added individual process highlighting support + +1.4 + * Added tray icon mini info window support + +1.3 + * Added ability to save I/O priority + +1.2 + * Fixed bug where process priorities were not actually saved + +1.1 + * Added ability to save process priority + * Added "Only for processes with the same command line" option for process comments + +1.0 + * Initial release diff --git a/plugins/UserNotes/UserNotes.rc b/plugins/UserNotes/UserNotes.rc index 58e1a1357383..a1b019805eb3 100644 --- a/plugins/UserNotes/UserNotes.rc +++ b/plugins/UserNotes/UserNotes.rc @@ -1,182 +1,180 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_OPTIONS DIALOGEX 0, 0, 316, 71 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Database location:",IDC_STATIC,7,9,61,8 - EDITTEXT IDC_DATABASE,72,8,183,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,259,7,50,14 - LTEXT "If a relative path is specified, it is relative to Process Hacker's directory. Environment variables can be used. Changes will take place after Process Hacker is restarted.",IDC_STATIC,7,26,302,19 - DEFPUSHBUTTON "OK",IDOK,205,50,50,14 - PUSHBUTTON "Cancel",IDCANCEL,259,50,50,14 -END - -IDD_PROCCOMMENT DIALOGEX 0, 0, 260, 260 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Comment" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - EDITTEXT IDC_COMMENT,7,7,246,229,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL - PUSHBUTTON "Revert",IDC_REVERT,7,239,50,14,WS_DISABLED - CONTROL "Only for processes with the same command line",IDC_MATCHCOMMANDLINE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,86,241,167,10 -END - -IDD_SRVCOMMENT DIALOGEX 0, 0, 252, 179 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Comment" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - EDITTEXT IDC_COMMENT,7,7,238,165,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 309 - TOPMARGIN, 7 - BOTTOMMARGIN, 64 - END - - IDD_PROCCOMMENT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 253 - TOPMARGIN, 7 - BOTTOMMARGIN, 253 - END - - IDD_SRVCOMMENT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 245 - TOPMARGIN, 7 - BOTTOMMARGIN, 172 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,7,0,0 - PRODUCTVERSION 1,7,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "dmex" - VALUE "FileDescription", "User Notes plugin for Process Hacker" - VALUE "FileVersion", "1.7" - VALUE "InternalName", "ProcessHacker.UserNotes" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "UserNotes.dll" - VALUE "ProductName", "User Notes plugin for Process Hacker" - VALUE "ProductVersion", "1.7" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOGEX 0, 0, 317, 54 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Database location:",IDC_STATIC,7,9,61,8 + EDITTEXT IDC_DATABASE,72,8,183,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,260,7,50,14 + LTEXT "If a relative path is specified, it is relative to Process Hacker's directory. Environment variables can be used. Changes will take place after Process Hacker is restarted.",IDC_STATIC,7,26,303,19 +END + +IDD_PROCCOMMENT DIALOGEX 0, 0, 260, 260 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Comment" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_COMMENT,7,7,246,229,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + PUSHBUTTON "Revert",IDC_REVERT,7,239,50,14,WS_DISABLED + CONTROL "Only for processes with the same command line",IDC_MATCHCOMMANDLINE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,86,241,167,10 +END + +IDD_SRVCOMMENT DIALOGEX 0, 0, 252, 179 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Comment" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_COMMENT,7,7,238,165,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + TOPMARGIN, 7 + BOTTOMMARGIN, 47 + END + + IDD_PROCCOMMENT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 253 + TOPMARGIN, 7 + BOTTOMMARGIN, 253 + END + + IDD_SRVCOMMENT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 245 + TOPMARGIN, 7 + BOTTOMMARGIN, 172 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,7,0,0 + PRODUCTVERSION 1,7,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "dmex" + VALUE "FileDescription", "User Notes plugin for Process Hacker" + VALUE "FileVersion", "1.7" + VALUE "InternalName", "ProcessHacker.UserNotes" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "UserNotes.dll" + VALUE "ProductName", "User Notes plugin for Process Hacker" + VALUE "ProductVersion", "1.7" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_OPTIONS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/UserNotes/UserNotes.vcxproj b/plugins/UserNotes/UserNotes.vcxproj index 2e4924d7bd4e..48436c6458ce 100644 --- a/plugins/UserNotes/UserNotes.vcxproj +++ b/plugins/UserNotes/UserNotes.vcxproj @@ -1,90 +1,90 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF} - UserNotes - Win32Proj - UserNotes - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - uxtheme.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {7C38D0AA-572C-4D75-8E4E-D68AF3C051AF} + UserNotes + Win32Proj + UserNotes + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + comctl32.dll;comdlg32.dll;user32.dll;%(DelayLoadDLLs) + + + + + comctl32.dll;comdlg32.dll;user32.dll;%(DelayLoadDLLs) + + + + + comctl32.dll;comdlg32.dll;user32.dll;%(DelayLoadDLLs) + + + + + comctl32.dll;comdlg32.dll;user32.dll;%(DelayLoadDLLs) + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/UserNotes/db.c b/plugins/UserNotes/db.c index 1412d51cfcbb..d18e28e01359 100644 --- a/plugins/UserNotes/db.c +++ b/plugins/UserNotes/db.c @@ -1,447 +1,456 @@ -/* - * Process Hacker User Notes - - * database functions - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "usernotes.h" -#include - -BOOLEAN NTAPI ObjectDbEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG NTAPI ObjectDbHashFunction( - _In_ PVOID Entry - ); - -PPH_HASHTABLE ObjectDb; -PH_QUEUED_LOCK ObjectDbLock = PH_QUEUED_LOCK_INIT; -PPH_STRING ObjectDbPath; - -VOID InitializeDb( - VOID - ) -{ - ObjectDb = PhCreateHashtable( - sizeof(PDB_OBJECT), - ObjectDbEqualFunction, - ObjectDbHashFunction, - 64 - ); -} - -BOOLEAN NTAPI ObjectDbEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PDB_OBJECT object1 = *(PDB_OBJECT *)Entry1; - PDB_OBJECT object2 = *(PDB_OBJECT *)Entry2; - - return object1->Tag == object2->Tag && PhEqualStringRef(&object1->Key, &object2->Key, TRUE); -} - -ULONG NTAPI ObjectDbHashFunction( - _In_ PVOID Entry - ) -{ - PDB_OBJECT object = *(PDB_OBJECT *)Entry; - - return object->Tag + PhHashStringRef(&object->Key, TRUE); -} - -ULONG GetNumberOfDbObjects( - VOID - ) -{ - return ObjectDb->Count; -} - -VOID LockDb( - VOID - ) -{ - PhAcquireQueuedLockExclusive(&ObjectDbLock); -} - -VOID UnlockDb( - VOID - ) -{ - PhReleaseQueuedLockExclusive(&ObjectDbLock); -} - -PDB_OBJECT FindDbObject( - _In_ ULONG Tag, - _In_ PPH_STRINGREF Name - ) -{ - DB_OBJECT lookupObject; - PDB_OBJECT lookupObjectPtr; - PDB_OBJECT *objectPtr; - - lookupObject.Tag = Tag; - lookupObject.Key = *Name; - lookupObjectPtr = &lookupObject; - - objectPtr = PhFindEntryHashtable(ObjectDb, &lookupObjectPtr); - - if (objectPtr) - return *objectPtr; - else - return NULL; -} - -PDB_OBJECT CreateDbObject( - _In_ ULONG Tag, - _In_ PPH_STRINGREF Name, - _In_opt_ PPH_STRING Comment - ) -{ - PDB_OBJECT object; - BOOLEAN added; - PDB_OBJECT *realObject; - - object = PhAllocate(sizeof(DB_OBJECT)); - memset(object, 0, sizeof(DB_OBJECT)); - object->Tag = Tag; - object->Key = *Name; - object->BackColor = ULONG_MAX; - - realObject = PhAddEntryHashtableEx(ObjectDb, &object, &added); - - if (added) - { - object->Name = PhCreateStringEx(Name->Buffer, Name->Length); - object->Key = object->Name->sr; - - if (Comment) - PhSetReference(&object->Comment, Comment); - else - object->Comment = PhReferenceEmptyString(); - } - else - { - PhFree(object); - object = *realObject; - - if (Comment) - PhSwapReference(&object->Comment, Comment); - } - - return object; -} - -VOID DeleteDbObject( - _In_ PDB_OBJECT Object - ) -{ - PhRemoveEntryHashtable(ObjectDb, &Object); - - PhDereferenceObject(Object->Name); - PhDereferenceObject(Object->Comment); - PhFree(Object); -} - -VOID SetDbPath( - _In_ PPH_STRING Path - ) -{ - PhSwapReference(&ObjectDbPath, Path); -} - -PPH_STRING GetOpaqueXmlNodeText( - _In_ mxml_node_t *node - ) -{ - if (node->child && node->child->type == MXML_OPAQUE && node->child->value.opaque) - { - return PhConvertUtf8ToUtf16(node->child->value.opaque); - } - else - { - return PhReferenceEmptyString(); - } -} - -NTSTATUS LoadDb( - VOID - ) -{ - NTSTATUS status; - HANDLE fileHandle; - LARGE_INTEGER fileSize; - mxml_node_t *topNode; - mxml_node_t *currentNode; - - status = PhCreateFileWin32( - &fileHandle, - ObjectDbPath->Buffer, - FILE_GENERIC_READ, - 0, - FILE_SHARE_READ | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - return status; - - if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) - { - // A blank file is OK. There are no objects to load. - NtClose(fileHandle); - return status; - } - - topNode = mxmlLoadFd(NULL, fileHandle, MXML_OPAQUE_CALLBACK); - NtClose(fileHandle); - - if (!topNode) - return STATUS_FILE_CORRUPT_ERROR; - - if (topNode->type != MXML_ELEMENT) - { - mxmlDelete(topNode); - return STATUS_FILE_CORRUPT_ERROR; - } - - LockDb(); - - for (currentNode = topNode->child; currentNode; currentNode = currentNode->next) - { - PDB_OBJECT object = NULL; - PPH_STRING tag = NULL; - PPH_STRING name = NULL; - PPH_STRING priorityClass = NULL; - PPH_STRING ioPriorityPlusOne = NULL; - PPH_STRING comment = NULL; - PPH_STRING backColor = NULL; - PPH_STRING collapse = NULL; - PPH_STRING affinityMask = NULL; - - if (currentNode->type == MXML_ELEMENT && - currentNode->value.element.num_attrs >= 2) - { - for (INT i = 0; i < currentNode->value.element.num_attrs; i++) - { - if (_stricmp(currentNode->value.element.attrs[i].name, "tag") == 0) - PhMoveReference(&tag, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - else if (_stricmp(currentNode->value.element.attrs[i].name, "name") == 0) - PhMoveReference(&name, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - else if (_stricmp(currentNode->value.element.attrs[i].name, "priorityclass") == 0) - PhMoveReference(&priorityClass, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - else if (_stricmp(currentNode->value.element.attrs[i].name, "iopriorityplusone") == 0) - PhMoveReference(&ioPriorityPlusOne, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - else if (_stricmp(currentNode->value.element.attrs[i].name, "backcolor") == 0) - PhMoveReference(&backColor, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - else if (_stricmp(currentNode->value.element.attrs[i].name, "collapse") == 0) - PhMoveReference(&collapse, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - else if (_stricmp(currentNode->value.element.attrs[i].name, "affinity") == 0) - PhMoveReference(&affinityMask, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value)); - } - } - - comment = GetOpaqueXmlNodeText(currentNode); - - if (tag && name && comment) - { - ULONG64 tagInteger; - ULONG64 priorityClassInteger = 0; - ULONG64 ioPriorityPlusOneInteger = 0; - - PhStringToInteger64(&tag->sr, 10, &tagInteger); - - if (priorityClass) - PhStringToInteger64(&priorityClass->sr, 10, &priorityClassInteger); - if (ioPriorityPlusOne) - PhStringToInteger64(&ioPriorityPlusOne->sr, 10, &ioPriorityPlusOneInteger); - - object = CreateDbObject((ULONG)tagInteger, &name->sr, comment); - object->PriorityClass = (ULONG)priorityClassInteger; - object->IoPriorityPlusOne = (ULONG)ioPriorityPlusOneInteger; - } - - // NOTE: These items are handled separately to maintain compatibility with previous versions of the database. - - if (object && backColor) - { - ULONG64 backColorInteger = ULONG_MAX; - - PhStringToInteger64(&backColor->sr, 10, &backColorInteger); - - object->BackColor = (COLORREF)backColorInteger; - } - - if (object && collapse) - { - ULONG64 collapseInteger = 0; - - PhStringToInteger64(&collapse->sr, 10, &collapseInteger); - - object->Collapse = !!collapseInteger; - } - - if (object && affinityMask) - { - ULONG64 affinityInteger = 0; - - PhStringToInteger64(&affinityMask->sr, 10, &affinityInteger); - - object->AffinityMask = (ULONG)affinityInteger; - } - - PhClearReference(&tag); - PhClearReference(&name); - PhClearReference(&priorityClass); - PhClearReference(&ioPriorityPlusOne); - PhClearReference(&comment); - PhClearReference(&backColor); - PhClearReference(&collapse); - PhClearReference(&affinityMask); - } - - UnlockDb(); - - mxmlDelete(topNode); - - return STATUS_SUCCESS; -} - -PPH_BYTES StringRefToUtf8( - _In_ PPH_STRINGREF String - ) -{ - return PH_AUTO(PhConvertUtf16ToUtf8Ex(String->Buffer, String->Length)); -} - -mxml_node_t *CreateObjectElement( - _Inout_ mxml_node_t *ParentNode, - _In_ PPH_STRINGREF Tag, - _In_ PPH_STRINGREF Name, - _In_ PPH_STRINGREF PriorityClass, - _In_ PPH_STRINGREF IoPriorityPlusOne, - _In_ PPH_STRINGREF Comment, - _In_ PPH_STRINGREF BackColor, - _In_ PPH_STRINGREF Collapse, - _In_ PPH_STRINGREF AffinityMask - ) -{ - mxml_node_t *objectNode; - mxml_node_t *textNode; - - // Create the setting element. - objectNode = mxmlNewElement(ParentNode, "object"); - - // Set the attributes. - mxmlElementSetAttr(objectNode, "tag", StringRefToUtf8(Tag)->Buffer); - mxmlElementSetAttr(objectNode, "name", StringRefToUtf8(Name)->Buffer); - mxmlElementSetAttr(objectNode, "priorityclass", StringRefToUtf8(PriorityClass)->Buffer); - mxmlElementSetAttr(objectNode, "iopriorityplusone", StringRefToUtf8(IoPriorityPlusOne)->Buffer); - mxmlElementSetAttr(objectNode, "backcolor", StringRefToUtf8(BackColor)->Buffer); - mxmlElementSetAttr(objectNode, "collapse", StringRefToUtf8(Collapse)->Buffer); - mxmlElementSetAttr(objectNode, "affinity", StringRefToUtf8(AffinityMask)->Buffer); - - // Set the value. - textNode = mxmlNewOpaque(objectNode, StringRefToUtf8(Comment)->Buffer); - - return objectNode; -} - -PPH_STRING UInt64ToBase10String( - _In_ ULONG64 Integer - ) -{ - return PH_AUTO(PhIntegerToString64(Integer, 10, FALSE)); -} - -NTSTATUS SaveDb( - VOID - ) -{ - PH_AUTO_POOL autoPool; - NTSTATUS status; - HANDLE fileHandle; - mxml_node_t *topNode; - ULONG enumerationKey = 0; - PDB_OBJECT *object; - - PhInitializeAutoPool(&autoPool); - - topNode = mxmlNewElement(MXML_NO_PARENT, "objects"); - - LockDb(); - - while (PhEnumHashtable(ObjectDb, (PVOID*)&object, &enumerationKey)) - { - CreateObjectElement( - topNode, - &UInt64ToBase10String((*object)->Tag)->sr, - &(*object)->Name->sr, - &UInt64ToBase10String((*object)->PriorityClass)->sr, - &UInt64ToBase10String((*object)->IoPriorityPlusOne)->sr, - &(*object)->Comment->sr, - &UInt64ToBase10String((*object)->BackColor)->sr, - &UInt64ToBase10String((*object)->Collapse)->sr, - &UInt64ToBase10String((*object)->AffinityMask)->sr - ); - PhDrainAutoPool(&autoPool); - } - - UnlockDb(); - - // Create the directory if it does not exist. - { - PPH_STRING fullPath; - ULONG indexOfFileName; - - if (fullPath = PH_AUTO(PhGetFullPath(ObjectDbPath->Buffer, &indexOfFileName))) - { - if (indexOfFileName != -1) - SHCreateDirectoryEx(NULL, PhaSubstring(fullPath, 0, indexOfFileName)->Buffer, NULL); - } - } - - PhDeleteAutoPool(&autoPool); - - status = PhCreateFileWin32( - &fileHandle, - ObjectDbPath->Buffer, - FILE_GENERIC_WRITE, - 0, - FILE_SHARE_READ, - FILE_OVERWRITE_IF, - FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT - ); - - if (!NT_SUCCESS(status)) - { - mxmlDelete(topNode); - return status; - } - - mxmlSaveFd(topNode, fileHandle, MXML_NO_CALLBACK); - mxmlDelete(topNode); - NtClose(fileHandle); - - return STATUS_SUCCESS; -} +/* + * Process Hacker User Notes - + * database functions + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "usernotes.h" +#include + +BOOLEAN NTAPI ObjectDbEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG NTAPI ObjectDbHashFunction( + _In_ PVOID Entry + ); + +PPH_HASHTABLE ObjectDb; +PH_QUEUED_LOCK ObjectDbLock = PH_QUEUED_LOCK_INIT; +PPH_STRING ObjectDbPath; + +VOID InitializeDb( + VOID + ) +{ + ObjectDb = PhCreateHashtable( + sizeof(PDB_OBJECT), + ObjectDbEqualFunction, + ObjectDbHashFunction, + 64 + ); +} + +BOOLEAN NTAPI ObjectDbEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PDB_OBJECT object1 = *(PDB_OBJECT *)Entry1; + PDB_OBJECT object2 = *(PDB_OBJECT *)Entry2; + + return object1->Tag == object2->Tag && PhEqualStringRef(&object1->Key, &object2->Key, TRUE); +} + +ULONG NTAPI ObjectDbHashFunction( + _In_ PVOID Entry + ) +{ + PDB_OBJECT object = *(PDB_OBJECT *)Entry; + + return object->Tag + PhHashStringRef(&object->Key, TRUE); +} + +ULONG GetNumberOfDbObjects( + VOID + ) +{ + return ObjectDb->Count; +} + +VOID LockDb( + VOID + ) +{ + PhAcquireQueuedLockExclusive(&ObjectDbLock); +} + +VOID UnlockDb( + VOID + ) +{ + PhReleaseQueuedLockExclusive(&ObjectDbLock); +} + +PDB_OBJECT FindDbObject( + _In_ ULONG Tag, + _In_ PPH_STRINGREF Name + ) +{ + DB_OBJECT lookupObject; + PDB_OBJECT lookupObjectPtr; + PDB_OBJECT *objectPtr; + + lookupObject.Tag = Tag; + lookupObject.Key = *Name; + lookupObjectPtr = &lookupObject; + + objectPtr = PhFindEntryHashtable(ObjectDb, &lookupObjectPtr); + + if (objectPtr) + return *objectPtr; + else + return NULL; +} + +PDB_OBJECT CreateDbObject( + _In_ ULONG Tag, + _In_ PPH_STRINGREF Name, + _In_opt_ PPH_STRING Comment + ) +{ + PDB_OBJECT object; + BOOLEAN added; + PDB_OBJECT *realObject; + + object = PhAllocate(sizeof(DB_OBJECT)); + memset(object, 0, sizeof(DB_OBJECT)); + object->Tag = Tag; + object->Key = *Name; + object->BackColor = ULONG_MAX; + + realObject = PhAddEntryHashtableEx(ObjectDb, &object, &added); + + if (added) + { + object->Name = PhCreateStringEx(Name->Buffer, Name->Length); + object->Key = object->Name->sr; + + if (Comment) + PhSetReference(&object->Comment, Comment); + else + object->Comment = PhReferenceEmptyString(); + } + else + { + PhFree(object); + object = *realObject; + + if (Comment) + PhSwapReference(&object->Comment, Comment); + } + + return object; +} + +VOID DeleteDbObject( + _In_ PDB_OBJECT Object + ) +{ + PhRemoveEntryHashtable(ObjectDb, &Object); + + PhDereferenceObject(Object->Name); + PhDereferenceObject(Object->Comment); + PhFree(Object); +} + +VOID SetDbPath( + _In_ PPH_STRING Path + ) +{ + PhSwapReference(&ObjectDbPath, Path); +} + +PPH_STRING GetOpaqueXmlNodeText( + _In_ mxml_node_t *node + ) +{ + PCSTR string; + + if (string = mxmlGetOpaque(node)) + { + return PhConvertUtf8ToUtf16((PSTR)string); + } + else + { + return PhReferenceEmptyString(); + } +} + +NTSTATUS LoadDb( + VOID + ) +{ + NTSTATUS status; + HANDLE fileHandle; + LARGE_INTEGER fileSize; + mxml_node_t *topNode; + mxml_node_t *currentNode; + + status = PhCreateFileWin32( + &fileHandle, + ObjectDbPath->Buffer, + FILE_GENERIC_READ, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + return status; + + if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) + { + // A blank file is OK. There are no objects to load. + NtClose(fileHandle); + return status; + } + + topNode = mxmlLoadFd(NULL, fileHandle, MXML_OPAQUE_CALLBACK); + NtClose(fileHandle); + + if (!topNode) + return STATUS_FILE_CORRUPT_ERROR; + + if (mxmlGetType(topNode) != MXML_ELEMENT) + { + mxmlDelete(topNode); + return STATUS_FILE_CORRUPT_ERROR; + } + + LockDb(); + + for (currentNode = mxmlGetFirstChild(topNode); currentNode; currentNode = mxmlGetNextSibling(currentNode)) + { + PDB_OBJECT object = NULL; + PPH_STRING tag = NULL; + PPH_STRING name = NULL; + PPH_STRING priorityClass = NULL; + PPH_STRING ioPriorityPlusOne = NULL; + PPH_STRING comment = NULL; + PPH_STRING backColor = NULL; + PPH_STRING collapse = NULL; + PPH_STRING affinityMask = NULL; + + if (mxmlElementGetAttrCount(currentNode) >= 2) + { + for (INT i = 0; i < mxmlElementGetAttrCount(currentNode); i++) + { + PSTR elementName; + PSTR elementValue; + + elementValue = (PSTR)mxmlElementGetAttrByIndex(currentNode, i, &elementName); + + if (!(elementName && elementValue)) + continue; + + if (_stricmp(elementName, "tag") == 0) + PhMoveReference(&tag, PhConvertUtf8ToUtf16(elementValue)); + else if (_stricmp(elementName, "name") == 0) + PhMoveReference(&name, PhConvertUtf8ToUtf16(elementValue)); + else if (_stricmp(elementName, "priorityclass") == 0) + PhMoveReference(&priorityClass, PhConvertUtf8ToUtf16(elementValue)); + else if (_stricmp(elementName, "iopriorityplusone") == 0) + PhMoveReference(&ioPriorityPlusOne, PhConvertUtf8ToUtf16(elementValue)); + else if (_stricmp(elementName, "backcolor") == 0) + PhMoveReference(&backColor, PhConvertUtf8ToUtf16(elementValue)); + else if (_stricmp(elementName, "collapse") == 0) + PhMoveReference(&collapse, PhConvertUtf8ToUtf16(elementValue)); + else if (_stricmp(elementName, "affinity") == 0) + PhMoveReference(&affinityMask, PhConvertUtf8ToUtf16(elementValue)); + } + } + + comment = GetOpaqueXmlNodeText(currentNode); + + if (tag && name) + { + ULONG64 tagInteger; + ULONG64 priorityClassInteger = 0; + ULONG64 ioPriorityPlusOneInteger = 0; + + PhStringToInteger64(&tag->sr, 10, &tagInteger); + + if (priorityClass) + PhStringToInteger64(&priorityClass->sr, 10, &priorityClassInteger); + if (ioPriorityPlusOne) + PhStringToInteger64(&ioPriorityPlusOne->sr, 10, &ioPriorityPlusOneInteger); + + object = CreateDbObject((ULONG)tagInteger, &name->sr, comment); + object->PriorityClass = (ULONG)priorityClassInteger; + object->IoPriorityPlusOne = (ULONG)ioPriorityPlusOneInteger; + } + + // NOTE: These items are handled separately to maintain compatibility with previous versions of the database. + + if (object && backColor) + { + ULONG64 backColorInteger = ULONG_MAX; + + PhStringToInteger64(&backColor->sr, 10, &backColorInteger); + + object->BackColor = (COLORREF)backColorInteger; + } + + if (object && collapse) + { + ULONG64 collapseInteger = 0; + + PhStringToInteger64(&collapse->sr, 10, &collapseInteger); + + object->Collapse = !!collapseInteger; + } + + if (object && affinityMask) + { + ULONG64 affinityInteger = 0; + + PhStringToInteger64(&affinityMask->sr, 10, &affinityInteger); + + object->AffinityMask = (ULONG_PTR)affinityInteger; + } + + PhClearReference(&tag); + PhClearReference(&name); + PhClearReference(&priorityClass); + PhClearReference(&ioPriorityPlusOne); + PhClearReference(&comment); + PhClearReference(&backColor); + PhClearReference(&collapse); + PhClearReference(&affinityMask); + } + + UnlockDb(); + + mxmlDelete(topNode); + + return STATUS_SUCCESS; +} + +PPH_BYTES StringRefToUtf8( + _In_ PPH_STRINGREF String + ) +{ + return PH_AUTO(PhConvertUtf16ToUtf8Ex(String->Buffer, String->Length)); +} + +mxml_node_t *CreateObjectElement( + _Inout_ mxml_node_t *ParentNode, + _In_ PPH_STRINGREF Tag, + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF PriorityClass, + _In_ PPH_STRINGREF IoPriorityPlusOne, + _In_ PPH_STRINGREF Comment, + _In_ PPH_STRINGREF BackColor, + _In_ PPH_STRINGREF Collapse, + _In_ PPH_STRINGREF AffinityMask + ) +{ + mxml_node_t *objectNode; + mxml_node_t *textNode; + + // Create the setting element. + objectNode = mxmlNewElement(ParentNode, "object"); + + // Set the attributes. + mxmlElementSetAttr(objectNode, "tag", StringRefToUtf8(Tag)->Buffer); + mxmlElementSetAttr(objectNode, "name", StringRefToUtf8(Name)->Buffer); + mxmlElementSetAttr(objectNode, "priorityclass", StringRefToUtf8(PriorityClass)->Buffer); + mxmlElementSetAttr(objectNode, "iopriorityplusone", StringRefToUtf8(IoPriorityPlusOne)->Buffer); + mxmlElementSetAttr(objectNode, "backcolor", StringRefToUtf8(BackColor)->Buffer); + mxmlElementSetAttr(objectNode, "collapse", StringRefToUtf8(Collapse)->Buffer); + mxmlElementSetAttr(objectNode, "affinity", StringRefToUtf8(AffinityMask)->Buffer); + + // Set the value. + textNode = mxmlNewOpaque(objectNode, StringRefToUtf8(Comment)->Buffer); + + return objectNode; +} + +PPH_STRING UInt64ToBase10String( + _In_ ULONG64 Integer + ) +{ + return PH_AUTO(PhIntegerToString64(Integer, 10, FALSE)); +} + +NTSTATUS SaveDb( + VOID + ) +{ + PH_AUTO_POOL autoPool; + NTSTATUS status; + HANDLE fileHandle; + mxml_node_t *topNode; + ULONG enumerationKey = 0; + PDB_OBJECT *object; + + PhInitializeAutoPool(&autoPool); + + topNode = mxmlNewElement(MXML_NO_PARENT, "objects"); + + LockDb(); + + while (PhEnumHashtable(ObjectDb, (PVOID*)&object, &enumerationKey)) + { + CreateObjectElement( + topNode, + &UInt64ToBase10String((*object)->Tag)->sr, + &(*object)->Name->sr, + &UInt64ToBase10String((*object)->PriorityClass)->sr, + &UInt64ToBase10String((*object)->IoPriorityPlusOne)->sr, + &(*object)->Comment->sr, + &UInt64ToBase10String((*object)->BackColor)->sr, + &UInt64ToBase10String((*object)->Collapse)->sr, + &UInt64ToBase10String((*object)->AffinityMask)->sr + ); + PhDrainAutoPool(&autoPool); + } + + UnlockDb(); + + // Create the directory if it does not exist. + { + PPH_STRING fullPath; + ULONG indexOfFileName; + + if (fullPath = PH_AUTO(PhGetFullPath(ObjectDbPath->Buffer, &indexOfFileName))) + { + if (indexOfFileName != -1) + PhCreateDirectory(PhaSubstring(fullPath, 0, indexOfFileName)); + } + } + + PhDeleteAutoPool(&autoPool); + + status = PhCreateFileWin32( + &fileHandle, + ObjectDbPath->Buffer, + FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(status)) + { + mxmlDelete(topNode); + return status; + } + + mxmlSaveFd(topNode, fileHandle, MXML_NO_CALLBACK); + mxmlDelete(topNode); + NtClose(fileHandle); + + return STATUS_SUCCESS; +} diff --git a/plugins/UserNotes/db.h b/plugins/UserNotes/db.h index bfae0318271e..4c88fe239df5 100644 --- a/plugins/UserNotes/db.h +++ b/plugins/UserNotes/db.h @@ -1,92 +1,92 @@ -/* - * Process Hacker User Notes - - * database functions - * - * Copyright (C) 2011-2015 wj32 - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#ifndef DB_H -#define DB_H - -#define PLUGIN_NAME L"ProcessHacker.UserNotes" -#define SETTING_NAME_DATABASE_PATH (PLUGIN_NAME L".DatabasePath") -#define SETTING_NAME_CUSTOM_COLOR_LIST (PLUGIN_NAME L".ColorCustomList") - -#define FILE_TAG 1 -#define SERVICE_TAG 2 -#define COMMAND_LINE_TAG 3 - -typedef struct _DB_OBJECT -{ - ULONG Tag; - PH_STRINGREF Key; - - PPH_STRING Name; - PPH_STRING Comment; - ULONG PriorityClass; - ULONG IoPriorityPlusOne; - COLORREF BackColor; - BOOLEAN Collapse; - ULONG AffinityMask; -} DB_OBJECT, *PDB_OBJECT; - -VOID InitializeDb( - VOID - ); - -ULONG GetNumberOfDbObjects( - VOID - ); - -VOID LockDb( - VOID - ); - -VOID UnlockDb( - VOID - ); - -PDB_OBJECT FindDbObject( - _In_ ULONG Tag, - _In_ PPH_STRINGREF Name - ); - -PDB_OBJECT CreateDbObject( - _In_ ULONG Tag, - _In_ PPH_STRINGREF Name, - _In_opt_ PPH_STRING Comment - ); - -VOID DeleteDbObject( - _In_ PDB_OBJECT Object - ); - -VOID SetDbPath( - _In_ PPH_STRING Path - ); - -NTSTATUS LoadDb( - VOID - ); - -NTSTATUS SaveDb( - VOID - ); - -#endif +/* + * Process Hacker User Notes - + * database functions + * + * Copyright (C) 2011-2015 wj32 + * Copyright (C) 2016 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef DB_H +#define DB_H + +#define PLUGIN_NAME L"ProcessHacker.UserNotes" +#define SETTING_NAME_DATABASE_PATH (PLUGIN_NAME L".DatabasePath") +#define SETTING_NAME_CUSTOM_COLOR_LIST (PLUGIN_NAME L".ColorCustomList") + +#define FILE_TAG 1 +#define SERVICE_TAG 2 +#define COMMAND_LINE_TAG 3 + +typedef struct _DB_OBJECT +{ + ULONG Tag; + PH_STRINGREF Key; + + PPH_STRING Name; + PPH_STRING Comment; + ULONG PriorityClass; + ULONG IoPriorityPlusOne; + COLORREF BackColor; + BOOLEAN Collapse; + ULONG_PTR AffinityMask; +} DB_OBJECT, *PDB_OBJECT; + +VOID InitializeDb( + VOID + ); + +ULONG GetNumberOfDbObjects( + VOID + ); + +VOID LockDb( + VOID + ); + +VOID UnlockDb( + VOID + ); + +PDB_OBJECT FindDbObject( + _In_ ULONG Tag, + _In_ PPH_STRINGREF Name + ); + +PDB_OBJECT CreateDbObject( + _In_ ULONG Tag, + _In_ PPH_STRINGREF Name, + _In_opt_ PPH_STRING Comment + ); + +VOID DeleteDbObject( + _In_ PDB_OBJECT Object + ); + +VOID SetDbPath( + _In_ PPH_STRING Path + ); + +NTSTATUS LoadDb( + VOID + ); + +NTSTATUS SaveDb( + VOID + ); + +#endif diff --git a/plugins/UserNotes/main.c b/plugins/UserNotes/main.c index e6471f4e8600..9b6dc01fa9fb 100644 --- a/plugins/UserNotes/main.c +++ b/plugins/UserNotes/main.c @@ -1,1892 +1,1967 @@ -/* - * Process Hacker User Notes - - * main program - * - * Copyright (C) 2011-2016 wj32 - * Copyright (C) 2016 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "usernotes.h" -#include -#include - -VOID SearchChangedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ); - -static PPH_PLUGIN PluginInstance; -static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -static PH_CALLBACK_REGISTRATION PluginMenuHookCallbackRegistration; -static PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; -static PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION GetProcessHighlightingColorCallbackRegistration; -static PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION MiListSectionMenuInitializingCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessModifiedCallbackRegistration; -static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; -static PH_CALLBACK_REGISTRATION SearchChangedRegistration; - -static PTOOLSTATUS_INTERFACE ToolStatusInterface = NULL; - -HWND ProcessTreeNewHandle; -LIST_ENTRY ProcessListHead = { &ProcessListHead, &ProcessListHead }; -PH_QUEUED_LOCK ProcessListLock = PH_QUEUED_LOCK_INIT; - -HWND ServiceTreeNewHandle; -LIST_ENTRY ServiceListHead = { &ServiceListHead, &ServiceListHead }; -PH_QUEUED_LOCK ServiceListLock = PH_QUEUED_LOCK_INIT; - -static COLORREF ProcessCustomColors[16] = { 0 }; - -BOOLEAN MatchDbObjectIntent( - _In_ PDB_OBJECT Object, - _In_ ULONG Intent - ) -{ - return (!(Intent & INTENT_PROCESS_COMMENT) || Object->Comment->Length != 0) && - (!(Intent & INTENT_PROCESS_PRIORITY_CLASS) || Object->PriorityClass != 0) && - (!(Intent & INTENT_PROCESS_IO_PRIORITY) || Object->IoPriorityPlusOne != 0) && - (!(Intent & INTENT_PROCESS_HIGHLIGHT) || Object->BackColor != ULONG_MAX) && - (!(Intent & INTENT_PROCESS_COLLAPSE) || Object->Collapse == TRUE) && - (!(Intent & INTENT_PROCESS_AFFINITY) || Object->AffinityMask != 0); -} - -PDB_OBJECT FindDbObjectForProcess( - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ ULONG Intent - ) -{ - PDB_OBJECT object; - - if ( - ProcessItem->CommandLine && - (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && - MatchDbObjectIntent(object, Intent) - ) - { - return object; - } - - if ( - (object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && - MatchDbObjectIntent(object, Intent) - ) - { - return object; - } - - return NULL; -} - -VOID DeleteDbObjectForProcessIfUnused( - _In_ PDB_OBJECT Object - ) -{ - if ( - Object->Comment->Length == 0 && - Object->PriorityClass == 0 && - Object->IoPriorityPlusOne == 0 && - Object->BackColor == ULONG_MAX && - Object->Collapse == FALSE && - Object->AffinityMask == 0 - ) - { - DeleteDbObject(Object); - } -} - -VOID LoadCustomColors( - VOID - ) -{ - PPH_STRING settingsString; - PH_STRINGREF remaining; - PH_STRINGREF part; - - settingsString = PhaGetStringSetting(SETTING_NAME_CUSTOM_COLOR_LIST); - remaining = settingsString->sr; - - if (remaining.Length == 0) - return; - - for (ULONG i = 0; i < ARRAYSIZE(ProcessCustomColors); i++) - { - ULONG64 integer = 0; - - if (remaining.Length == 0) - break; - - PhSplitStringRefAtChar(&remaining, ',', &part, &remaining); - - if (PhStringToInteger64(&part, 10, &integer)) - { - ProcessCustomColors[i] = (COLORREF)integer; - } - } -} - -PPH_STRING SaveCustomColors( - VOID - ) -{ - PH_STRING_BUILDER stringBuilder; - - PhInitializeStringBuilder(&stringBuilder, 100); - - for (ULONG i = 0; i < ARRAYSIZE(ProcessCustomColors); i++) - { - PhAppendFormatStringBuilder( - &stringBuilder, - L"%lu,", - ProcessCustomColors[i] - ); - } - - if (stringBuilder.String->Length != 0) - PhRemoveEndStringBuilder(&stringBuilder, 1); - - return PhFinalStringBuilderString(&stringBuilder); -} - -ULONG GetProcessAffinity( - _In_ HANDLE ProcessId - ) -{ - HANDLE processHandle; - ULONG affinityMask = 0; - PROCESS_BASIC_INFORMATION basicInfo; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - ProcessQueryAccess, - ProcessId - ))) - { - if (NT_SUCCESS(PhGetProcessBasicInformation( - processHandle, - &basicInfo - ))) - { - affinityMask = (ULONG)basicInfo.AffinityMask; - } - - NtClose(processHandle); - } - - return affinityMask; -} - -IO_PRIORITY_HINT GetProcessIoPriority( - _In_ HANDLE ProcessId - ) -{ - HANDLE processHandle; - IO_PRIORITY_HINT ioPriority = -1; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - ProcessQueryAccess, - ProcessId - ))) - { - if (!NT_SUCCESS(PhGetProcessIoPriority( - processHandle, - &ioPriority - ))) - { - ioPriority = -1; - } - - NtClose(processHandle); - } - - return ioPriority; -} - -ULONG GetPriorityClassFromId( - _In_ ULONG Id - ) -{ - switch (Id) - { - case PHAPP_ID_PRIORITY_REALTIME: - return PROCESS_PRIORITY_CLASS_REALTIME; - case PHAPP_ID_PRIORITY_HIGH: - return PROCESS_PRIORITY_CLASS_HIGH; - case PHAPP_ID_PRIORITY_ABOVENORMAL: - return PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; - case PHAPP_ID_PRIORITY_NORMAL: - return PROCESS_PRIORITY_CLASS_NORMAL; - case PHAPP_ID_PRIORITY_BELOWNORMAL: - return PROCESS_PRIORITY_CLASS_BELOW_NORMAL; - case PHAPP_ID_PRIORITY_IDLE: - return PROCESS_PRIORITY_CLASS_IDLE; - } - - return 0; -} - -ULONG GetIoPriorityFromId( - _In_ ULONG Id - ) -{ - switch (Id) - { - case PHAPP_ID_IOPRIORITY_VERYLOW: - return 0; - case PHAPP_ID_IOPRIORITY_LOW: - return 1; - case PHAPP_ID_IOPRIORITY_NORMAL: - return 2; - case PHAPP_ID_IOPRIORITY_HIGH: - return 3; - } - - return -1; -} - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN toolStatusPlugin; - PPH_STRING path; - - if (toolStatusPlugin = PhFindPlugin(TOOLSTATUS_PLUGIN_NAME)) - { - ToolStatusInterface = PhGetPluginInformation(toolStatusPlugin)->Interface; - - if (ToolStatusInterface->Version < TOOLSTATUS_INTERFACE_VERSION) - ToolStatusInterface = NULL; - } - - path = PhaGetStringSetting(SETTING_NAME_DATABASE_PATH); - path = PH_AUTO(PhExpandEnvironmentStrings(&path->sr)); - - LoadCustomColors(); - - if (RtlDetermineDosPathNameType_U(path->Buffer) == RtlPathTypeRelative) - { - PPH_STRING directory; - - directory = PH_AUTO(PhGetApplicationDirectory()); - path = PH_AUTO(PhConcatStringRef2(&directory->sr, &path->sr)); - } - - SetDbPath(path); - LoadDb(); -} - -VOID NTAPI UnloadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - SaveDb(); -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - DialogBox( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_OPTIONS), - (HWND)Parameter, - OptionsDlgProc - ); -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - PDB_OBJECT object; - - if (!processItem) - return; - - switch (menuItem->Id) - { - case PROCESS_PRIORITY_SAVE_ID: - { - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->PriorityClass != 0) - { - object->PriorityClass = 0; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); - object->PriorityClass = processItem->PriorityClass; - } - - UnlockDb(); - SaveDb(); - } - break; - case PROCESS_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID: - { - if (processItem->CommandLine) - { - LockDb(); - - if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->PriorityClass != 0) - { - object->PriorityClass = 0; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); - object->PriorityClass = processItem->PriorityClass; - } - - UnlockDb(); - SaveDb(); - } - } - break; - case PROCESS_IO_PRIORITY_SAVE_ID: - { - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->IoPriorityPlusOne != 0) - { - object->IoPriorityPlusOne = 0; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); - object->IoPriorityPlusOne = GetProcessIoPriority(processItem->ProcessId) + 1; - } - - UnlockDb(); - SaveDb(); - } - break; - case PROCESS_IO_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID: - { - if (processItem->CommandLine) - { - LockDb(); - - if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->IoPriorityPlusOne != 0) - { - object->IoPriorityPlusOne = 0; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); - object->IoPriorityPlusOne = GetProcessIoPriority(processItem->ProcessId) + 1; - } - - UnlockDb(); - SaveDb(); - } - } - break; - case PROCESS_HIGHLIGHT_ID: - { - BOOLEAN highlightPresent = (BOOLEAN)menuItem->Context; - - if (!highlightPresent) - { - CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; - chooseColor.hwndOwner = PhMainWndHandle; - chooseColor.lpCustColors = ProcessCustomColors; - chooseColor.lpfnHook = ColorDlgHookProc; - chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK; - - if (ChooseColor(&chooseColor)) - { - PhSetStringSetting2(SETTING_NAME_CUSTOM_COLOR_LIST, &PH_AUTO_T(PH_STRING, SaveCustomColors())->sr); - - LockDb(); - - object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); - object->BackColor = chooseColor.rgbResult; - - UnlockDb(); - SaveDb(); - } - } - else - { - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->BackColor != ULONG_MAX) - { - object->BackColor = ULONG_MAX; - DeleteDbObjectForProcessIfUnused(object); - } - - UnlockDb(); - SaveDb(); - } - - PhInvalidateAllProcessNodes(); - } - break; - case PROCESS_COLLAPSE_ID: - { - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->Collapse) - { - object->Collapse = FALSE; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); - object->Collapse = TRUE; - } - - UnlockDb(); - SaveDb(); - } - break; - case PROCESS_AFFINITY_SAVE_ID: - { - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->AffinityMask != 0) - { - object->AffinityMask = 0; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); - object->AffinityMask = GetProcessAffinity(processItem->ProcessId); - } - - UnlockDb(); - SaveDb(); - } - break; - case PROCESS_AFFINITY_SAVE_FOR_THIS_COMMAND_LINE_ID: - { - if (processItem->CommandLine) - { - LockDb(); - - if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->AffinityMask != 0) - { - object->AffinityMask = 0; - DeleteDbObjectForProcessIfUnused(object); - } - else - { - object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); - object->AffinityMask = GetProcessAffinity(processItem->ProcessId); - } - - UnlockDb(); - SaveDb(); - } - } - break; - } -} - -VOID NTAPI MenuHookCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo = Parameter; - ULONG id = menuHookInfo->SelectedItem->Id; - - switch (id) - { - case PHAPP_ID_PRIORITY_REALTIME: - case PHAPP_ID_PRIORITY_HIGH: - case PHAPP_ID_PRIORITY_ABOVENORMAL: - case PHAPP_ID_PRIORITY_NORMAL: - case PHAPP_ID_PRIORITY_BELOWNORMAL: - case PHAPP_ID_PRIORITY_IDLE: - { - BOOLEAN changed = FALSE; - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - ULONG i; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - LockDb(); - - for (i = 0; i < numberOfProcesses; i++) - { - PDB_OBJECT object; - - if (object = FindDbObjectForProcess(processes[i], INTENT_PROCESS_PRIORITY_CLASS)) - { - ULONG newPriorityClass = GetPriorityClassFromId(id); - - if (object->PriorityClass != newPriorityClass) - { - object->PriorityClass = newPriorityClass; - changed = TRUE; - } - } - } - - UnlockDb(); - PhFree(processes); - - if (changed) - SaveDb(); - } - break; - case PHAPP_ID_IOPRIORITY_VERYLOW: - case PHAPP_ID_IOPRIORITY_LOW: - case PHAPP_ID_IOPRIORITY_NORMAL: - case PHAPP_ID_IOPRIORITY_HIGH: - { - BOOLEAN changed = FALSE; - PPH_PROCESS_ITEM *processes; - ULONG numberOfProcesses; - ULONG i; - - PhGetSelectedProcessItems(&processes, &numberOfProcesses); - LockDb(); - - for (i = 0; i < numberOfProcesses; i++) - { - PDB_OBJECT object; - - if (object = FindDbObjectForProcess(processes[i], INTENT_PROCESS_IO_PRIORITY)) - { - ULONG newIoPriorityPlusOne = GetIoPriorityFromId(id) + 1; - - if (object->IoPriorityPlusOne != newIoPriorityPlusOne) - { - object->IoPriorityPlusOne = newIoPriorityPlusOne; - changed = TRUE; - } - } - } - - UnlockDb(); - PhFree(processes); - - if (changed) - SaveDb(); - } - break; - case PHAPP_ID_PROCESS_AFFINITY: - { - BOOLEAN changed = FALSE; - ULONG_PTR affinityMask; - ULONG_PTR newAffinityMask; - PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); - - if (!processItem) - break; - - // Don't show the default Process Hacker affinity dialog. - menuHookInfo->Handled = TRUE; - - // Query the current process affinity. - affinityMask = GetProcessAffinity(processItem->ProcessId); - - // Show the affinity dialog (with our values). - if (PhShowProcessAffinityDialog2(PhMainWndHandle, affinityMask, &newAffinityMask)) - { - PDB_OBJECT object; - - LockDb(); - - if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_AFFINITY)) - { - // Update the process affinity in our database (if the database values are different). - if (object->AffinityMask != (ULONG)newAffinityMask) - { - object->AffinityMask = (ULONG)newAffinityMask; - changed = TRUE; - } - } - - UnlockDb(); - - if (changed) - { - SaveDb(); - } - - // Update the process affinity in Windows (if the system values are different). - if (affinityMask != newAffinityMask) - { - HANDLE processHandle; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - processItem->ProcessId - ))) - { - PhSetProcessAffinityMask(processHandle, newAffinityMask); - NtClose(processHandle); - } - } - } - } - break; - } -} - -VOID InvalidateProcessComments( - VOID - ) -{ - PLIST_ENTRY listEntry; - - PhAcquireQueuedLockExclusive(&ProcessListLock); - - listEntry = ProcessListHead.Flink; - - while (listEntry != &ProcessListHead) - { - PPROCESS_EXTENSION extension; - - extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); - - extension->Valid = FALSE; - - listEntry = listEntry->Flink; - } - - PhReleaseQueuedLockExclusive(&ProcessListLock); -} - -VOID UpdateProcessComment( - _In_ PPH_PROCESS_NODE Node, - _In_ PPROCESS_EXTENSION Extension - ) -{ - if (!Extension->Valid) - { - PDB_OBJECT object; - - LockDb(); - - if (object = FindDbObjectForProcess(Node->ProcessItem, INTENT_PROCESS_COMMENT)) - { - PhSwapReference(&Extension->Comment, object->Comment); - } - else - { - PhClearReference(&Extension->Comment); - } - - UnlockDb(); - - Extension->Valid = TRUE; - } -} - -VOID InvalidateServiceComments( - VOID - ) -{ - PLIST_ENTRY listEntry; - - PhAcquireQueuedLockExclusive(&ServiceListLock); - - listEntry = ServiceListHead.Flink; - - while (listEntry != &ServiceListHead) - { - PSERVICE_EXTENSION extension; - - extension = CONTAINING_RECORD(listEntry, SERVICE_EXTENSION, ListEntry); - - extension->Valid = FALSE; - - listEntry = listEntry->Flink; - } - - PhReleaseQueuedLockExclusive(&ServiceListLock); -} - -VOID UpdateServiceComment( - _In_ PPH_SERVICE_NODE Node, - _In_ PSERVICE_EXTENSION Extension - ) -{ - if (!Extension->Valid) - { - PDB_OBJECT object; - - LockDb(); - - if (object = FindDbObject(SERVICE_TAG, &Node->ServiceItem->Name->sr)) - { - PhSwapReference(&Extension->Comment, object->Comment); - } - else - { - PhClearReference(&Extension->Comment); - } - - UnlockDb(); - - Extension->Valid = TRUE; - } -} - -VOID TreeNewMessageCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; - - switch (message->Message) - { - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; - - if (message->TreeNewHandle == ProcessTreeNewHandle) - { - PPH_PROCESS_NODE node; - - node = (PPH_PROCESS_NODE)getCellText->Node; - - switch (message->SubId) - { - case COMMENT_COLUMN_ID: - { - PPROCESS_EXTENSION extension; - - extension = PhPluginGetObjectExtension(PluginInstance, node->ProcessItem, EmProcessItemType); - UpdateProcessComment(node, extension); - getCellText->Text = PhGetStringRef(extension->Comment); - } - break; - } - } - else if (message->TreeNewHandle == ServiceTreeNewHandle) - { - PPH_SERVICE_NODE node; - - node = (PPH_SERVICE_NODE)getCellText->Node; - - switch (message->SubId) - { - case COMMENT_COLUMN_ID: - { - PSERVICE_EXTENSION extension; - - extension = PhPluginGetObjectExtension(PluginInstance, node->ServiceItem, EmServiceItemType); - UpdateServiceComment(node, extension); - getCellText->Text = PhGetStringRef(extension->Comment); - } - break; - } - } - } - break; - } -} - -VOID MainWindowShowingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - if (ToolStatusInterface) - { - PhRegisterCallback(ToolStatusInterface->SearchChangedEvent, SearchChangedHandler, NULL, &SearchChangedRegistration); - } -} - -VOID ProcessPropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; - - PhAddProcessPropPage( - propContext->PropContext, - PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCCOMMENT), ProcessCommentPageDlgProc, NULL) - ); -} - -VOID AddSavePriorityMenuItemsAndHook( - _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, - _In_ PPH_PROCESS_ITEM ProcessItem, - _In_ BOOLEAN UseSelectionForHook - ) -{ - PPH_EMENU_ITEM affinityMenuItem; - PPH_EMENU_ITEM priorityMenuItem; - PPH_EMENU_ITEM ioPriorityMenuItem; - PPH_EMENU_ITEM saveMenuItem; - PPH_EMENU_ITEM saveForCommandLineMenuItem; - PDB_OBJECT object; - - if (affinityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"Affinity", PHAPP_ID_PROCESS_AFFINITY)) - { - // HACK: Change default Affinity menu-item into a drop-down list - PhInsertEMenuItem(affinityMenuItem, PhCreateEMenuItem(0, affinityMenuItem->Id, L"Set &affinity", NULL, NULL), -1); - //PhInsertEMenuItem(affinityMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, PHAPP_ID_PROCESS_AFFINITY, L"Set &affinity", NULL), PhIndexOfEMenuItem(MenuInfo->Menu, affinityMenuItem) + 1); - //PhRemoveEMenuItem(affinityMenuItem, affinityMenuItem, 0); - - // Insert standard menu-items - PhInsertEMenuItem(affinityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(affinityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_AFFINITY_SAVE_ID, PhaFormatString(L"Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), -1); - PhInsertEMenuItem(affinityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_AFFINITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save for this command line", NULL), -1); - - if (!ProcessItem->CommandLine) - saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED; - - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->AffinityMask != 0) - saveMenuItem->Flags |= PH_EMENU_CHECKED; - if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->AffinityMask != 0) - saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED; - - UnlockDb(); - } - - // Priority - if (priorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"Priority", 0)) - { - PhInsertEMenuItem(priorityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(priorityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_PRIORITY_SAVE_ID, PhaFormatString(L"Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), -1); - PhInsertEMenuItem(priorityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save for this command line", NULL), -1); - - if (!ProcessItem->CommandLine) - saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED; - - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->PriorityClass != 0) - saveMenuItem->Flags |= PH_EMENU_CHECKED; - if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->PriorityClass != 0) - saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED; - - UnlockDb(); - } - - // I/O Priority - if (ioPriorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"I/O Priority", 0)) - { - PhInsertEMenuItem(ioPriorityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), -1); - PhInsertEMenuItem(ioPriorityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_IO_PRIORITY_SAVE_ID, PhaFormatString(L"Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), -1); - PhInsertEMenuItem(ioPriorityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_IO_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save for this command line", NULL), -1); - - if (!ProcessItem->CommandLine) - saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED; - - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->IoPriorityPlusOne != 0) - saveMenuItem->Flags |= PH_EMENU_CHECKED; - if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->IoPriorityPlusOne != 0) - saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED; - - UnlockDb(); - } - - PhPluginAddMenuHook(MenuInfo, PluginInstance, UseSelectionForHook ? NULL : ProcessItem->ProcessId); -} - -VOID ProcessMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - BOOLEAN highlightPresent = FALSE; - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_PROCESS_ITEM processItem; - PPH_EMENU_ITEM miscMenuItem; - PPH_EMENU_ITEM collapseMenuItem; - PPH_EMENU_ITEM highlightMenuItem; - PDB_OBJECT object; - - if (menuInfo->u.Process.NumberOfProcesses != 1) - return; - - processItem = menuInfo->u.Process.Processes[0]; - - if (!PH_IS_FAKE_PROCESS_ID(processItem->ProcessId) && processItem->ProcessId != SYSTEM_IDLE_PROCESS_ID && processItem->ProcessId != SYSTEM_PROCESS_ID) - AddSavePriorityMenuItemsAndHook(menuInfo, processItem, TRUE); - - if (!(miscMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Miscellaneous", 0))) - return; - - LockDb(); - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->BackColor != ULONG_MAX) - highlightPresent = TRUE; - UnlockDb(); - - PhInsertEMenuItem(miscMenuItem, collapseMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_COLLAPSE_ID, L"Collapse by default", NULL), 0); - PhInsertEMenuItem(miscMenuItem, highlightMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_HIGHLIGHT_ID, L"Highlight", UlongToPtr(highlightPresent)), 1); - PhInsertEMenuItem(miscMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, NULL, NULL), 2); - - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &menuInfo->u.Process.Processes[0]->ProcessName->sr)) && object->Collapse) - collapseMenuItem->Flags |= PH_EMENU_CHECKED; - if (highlightPresent) - highlightMenuItem->Flags |= PH_EMENU_CHECKED; - - UnlockDb(); -} - -static LONG NTAPI ProcessCommentSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PPH_PROCESS_NODE node1 = Node1; - PPH_PROCESS_NODE node2 = Node2; - PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType); - PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType); - - UpdateProcessComment(node1, extension1); - UpdateProcessComment(node2, extension2); - - return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE); -} - -VOID ProcessTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PH_TREENEW_COLUMN column; - - ProcessTreeNewHandle = info->TreeNewHandle; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"Comment"; - column.Width = 120; - column.Alignment = PH_ALIGN_LEFT; - - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ProcessCommentSortFunction); -} - -VOID GetProcessHighlightingColorCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; - PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)getHighlightingColor->Parameter; - PDB_OBJECT object; - - if (getHighlightingColor->Handled) - return; - - LockDb(); - - if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->BackColor != ULONG_MAX) - { - getHighlightingColor->BackColor = object->BackColor; - getHighlightingColor->Cache = TRUE; - getHighlightingColor->Handled = TRUE; - } - - UnlockDb(); -} - -VOID ServicePropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; - PROPSHEETPAGE propSheetPage; - - if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) - { - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVCOMMENT); - propSheetPage.pszTitle = L"Comment"; - propSheetPage.pfnDlgProc = ServiceCommentPageDlgProc; - propSheetPage.lParam = (LPARAM)objectProperties->Parameter; - objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); - } -} - -LONG NTAPI ServiceCommentSortFunction( - _In_ PVOID Node1, - _In_ PVOID Node2, - _In_ ULONG SubId, - _In_ PVOID Context - ) -{ - PPH_SERVICE_NODE node1 = Node1; - PPH_SERVICE_NODE node2 = Node2; - PSERVICE_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ServiceItem, EmServiceItemType); - PSERVICE_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ServiceItem, EmServiceItemType); - - UpdateServiceComment(node1, extension1); - UpdateServiceComment(node2, extension2); - - return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE); -} - -VOID ServiceTreeNewInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; - PH_TREENEW_COLUMN column; - - ServiceTreeNewHandle = info->TreeNewHandle; - - memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); - column.Text = L"Comment"; - column.Width = 120; - column.Alignment = PH_ALIGN_LEFT; - - PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ServiceCommentSortFunction); -} - -VOID MiListSectionMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_PROCESS_ITEM processItem = menuInfo->u.MiListSection.ProcessGroup->Representative; - - if (PH_IS_FAKE_PROCESS_ID(processItem->ProcessId) || processItem->ProcessId == SYSTEM_IDLE_PROCESS_ID || processItem->ProcessId == SYSTEM_PROCESS_ID) - return; - - AddSavePriorityMenuItemsAndHook(menuInfo, processItem, FALSE); -} - -VOID ProcessModifiedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PROCESS_ITEM processItem = Parameter; - PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processItem, EmProcessItemType); - - extension->Valid = FALSE; -} - -VOID ProcessesUpdatedCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PLIST_ENTRY listEntry; - - if (GetNumberOfDbObjects() == 0) - return; - - PhAcquireQueuedLockExclusive(&ProcessListLock); - LockDb(); - - listEntry = ProcessListHead.Flink; - - while (listEntry != &ProcessListHead) - { - PPROCESS_EXTENSION extension; - PPH_PROCESS_ITEM processItem; - PDB_OBJECT object; - - extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); - processItem = extension->ProcessItem; - - if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_PRIORITY_CLASS)) - { - if (processItem->PriorityClass != object->PriorityClass) - { - HANDLE processHandle; - PROCESS_PRIORITY_CLASS priorityClass; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - processItem->ProcessId - ))) - { - priorityClass.Foreground = FALSE; - priorityClass.PriorityClass = (UCHAR)object->PriorityClass; - NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS)); - - NtClose(processHandle); - } - } - } - - if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_IO_PRIORITY)) - { - if (object->IoPriorityPlusOne != 0) - { - HANDLE processHandle; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - processItem->ProcessId - ))) - { - PhSetProcessIoPriority(processHandle, object->IoPriorityPlusOne - 1); - NtClose(processHandle); - } - } - } - - if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_AFFINITY)) - { - if (object->AffinityMask != 0) - { - HANDLE processHandle; - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - PROCESS_SET_INFORMATION, - processItem->ProcessId - ))) - { - PhSetProcessAffinityMask(processHandle, object->AffinityMask); - NtClose(processHandle); - } - } - } - - listEntry = listEntry->Flink; - } - - UnlockDb(); - PhReleaseQueuedLockExclusive(&ProcessListLock); -} - -VOID SearchChangedHandler( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_STRING searchText = Parameter; - - if (PhIsNullOrEmptyString(searchText)) - { - // ToolStatus expanded all nodes for searching, but the search text just became empty. We - // should re-collapse processes. - - PPH_LIST nodes = PH_AUTO(PhDuplicateProcessNodeList()); - ULONG i; - BOOLEAN changed = FALSE; - - LockDb(); - - for (i = 0; i < nodes->Count; i++) - { - PPH_PROCESS_NODE node = nodes->Items[i]; - PDB_OBJECT object; - - if ((object = FindDbObjectForProcess(node->ProcessItem, INTENT_PROCESS_COLLAPSE)) && object->Collapse) - { - node->Node.Expanded = FALSE; - changed = TRUE; - } - } - - UnlockDb(); - - if (changed) - TreeNew_NodesStructured(ProcessTreeNewHandle); - } -} - -VOID ProcessItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_PROCESS_ITEM processItem = Object; - PPROCESS_EXTENSION extension = Extension; - - memset(extension, 0, sizeof(PROCESS_EXTENSION)); - extension->ProcessItem = processItem; - - PhAcquireQueuedLockExclusive(&ProcessListLock); - InsertTailList(&ProcessListHead, &extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessListLock); -} - -VOID ProcessItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_PROCESS_ITEM processItem = Object; - PPROCESS_EXTENSION extension = Extension; - - PhClearReference(&extension->Comment); - PhAcquireQueuedLockExclusive(&ProcessListLock); - RemoveEntryList(&extension->ListEntry); - PhReleaseQueuedLockExclusive(&ProcessListLock); -} - -VOID ProcessNodeCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_PROCESS_NODE processNode = Object; - PDB_OBJECT object; - - LockDb(); - - if ((object = FindDbObjectForProcess(processNode->ProcessItem, INTENT_PROCESS_COLLAPSE)) && object->Collapse) - processNode->Node.Expanded = FALSE; - - UnlockDb(); -} - -VOID ServiceItemCreateCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_SERVICE_ITEM processItem = Object; - PSERVICE_EXTENSION extension = Extension; - - memset(extension, 0, sizeof(SERVICE_EXTENSION)); - PhAcquireQueuedLockExclusive(&ServiceListLock); - InsertTailList(&ServiceListHead, &extension->ListEntry); - PhReleaseQueuedLockExclusive(&ServiceListLock); -} - -VOID ServiceItemDeleteCallback( - _In_ PVOID Object, - _In_ PH_EM_OBJECT_TYPE ObjectType, - _In_ PVOID Extension - ) -{ - PPH_SERVICE_ITEM processItem = Object; - PSERVICE_EXTENSION extension = Extension; - - PhClearReference(&extension->Comment); - PhAcquireQueuedLockExclusive(&ServiceListLock); - RemoveEntryList(&extension->ListEntry); - PhReleaseQueuedLockExclusive(&ServiceListLock); -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - if (Reason == DLL_PROCESS_ATTACH) - { - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { StringSettingType, SETTING_NAME_DATABASE_PATH, L"%APPDATA%\\Process Hacker 2\\usernotesdb.xml" }, - { StringSettingType, SETTING_NAME_CUSTOM_COLOR_LIST, L"" } - }; - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"User Notes"; - info->Author = L"dmex, wj32"; - info->Description = L"Allows the user to add comments for processes and services, save process priority and affinity, highlight individual processes and show processes collapsed by default."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1120"; - info->HasOptions = TRUE; - - InitializeDb(); - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackUnload), - UnloadCallback, - NULL, - &PluginUnloadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - ShowOptionsCallback, - NULL, - &PluginShowOptionsCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuHook), - MenuHookCallback, - NULL, - &PluginMenuHookCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), - TreeNewMessageCallback, - NULL, - &TreeNewMessageCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainWindowShowing), - MainWindowShowingCallback, - NULL, - &MainWindowShowingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), - ProcessPropertiesInitializingCallback, - NULL, - &ProcessPropertiesInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), - ServicePropertiesInitializingCallback, - NULL, - &ServicePropertiesInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), - ProcessMenuInitializingCallback, - NULL, - &ProcessMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), - ProcessTreeNewInitializingCallback, - NULL, - &ProcessTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), - GetProcessHighlightingColorCallback, - NULL, - &GetProcessHighlightingColorCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), - ServiceTreeNewInitializingCallback, - NULL, - &ServiceTreeNewInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMiListSectionMenuInitializing), - MiListSectionMenuInitializingCallback, - NULL, - &MiListSectionMenuInitializingCallbackRegistration - ); - PhRegisterCallback(&PhProcessModifiedEvent, - ProcessModifiedCallback, - NULL, - &ProcessModifiedCallbackRegistration - ); - PhRegisterCallback( - &PhProcessesUpdatedEvent, - ProcessesUpdatedCallback, - NULL, - &ProcessesUpdatedCallbackRegistration - ); - - PhPluginSetObjectExtension( - PluginInstance, - EmProcessItemType, - sizeof(PROCESS_EXTENSION), - ProcessItemCreateCallback, - ProcessItemDeleteCallback - ); - PhPluginSetObjectExtension( - PluginInstance, - EmProcessNodeType, - sizeof(PROCESS_EXTENSION), - ProcessNodeCreateCallback, - NULL - ); - PhPluginSetObjectExtension( - PluginInstance, - EmServiceItemType, - sizeof(SERVICE_EXTENSION), - ServiceItemCreateCallback, - ServiceItemDeleteCallback - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - - return TRUE; -} - -INT_PTR CALLBACK OptionsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, GetParent(hwndDlg)); - - SetDlgItemText(hwndDlg, IDC_DATABASE, PhaGetStringSetting(SETTING_NAME_DATABASE_PATH)->Buffer); - - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDCANCEL), TRUE); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - case IDOK: - { - PhSetStringSetting2(SETTING_NAME_DATABASE_PATH, - &PhaGetDlgItemText(hwndDlg, IDC_DATABASE)->sr); - - EndDialog(hwndDlg, IDOK); - } - break; - case IDC_BROWSE: - { - static PH_FILETYPE_FILTER filters[] = - { - { L"XML files (*.xml)", L"*.xml" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - PPH_STRING fileName; - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_DATABASE))); - PhSetFileDialogFileName(fileDialog, fileName->Buffer); - - if (PhShowFileDialog(hwndDlg, fileDialog)) - { - fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); - SetDlgItemText(hwndDlg, IDC_DATABASE, fileName->Buffer); - } - - PhFreeFileDialog(fileDialog); - } - break; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK ProcessCommentPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - PPROCESS_COMMENT_PAGE_CONTEXT context; - - if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) - { - context = propPageContext->Context; - } - else - { - return FALSE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - PDB_OBJECT object; - PPH_STRING comment; - - context = PhAllocate(sizeof(PROCESS_COMMENT_PAGE_CONTEXT)); - context->CommentHandle = GetDlgItem(hwndDlg, IDC_COMMENT); - context->RevertHandle = GetDlgItem(hwndDlg, IDC_REVERT); - context->MatchCommandlineHandle = GetDlgItem(hwndDlg, IDC_MATCHCOMMANDLINE); - propPageContext->Context = context; - - // Load the comment. - Edit_LimitText(context->CommentHandle, UNICODE_STRING_MAX_CHARS); - - LockDb(); - - if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_COMMENT)) - { - PhSetReference(&comment, object->Comment); - - if (processItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->Comment->Length != 0) - { - Button_SetCheck(context->MatchCommandlineHandle, BST_CHECKED); - } - } - else - { - comment = PhReferenceEmptyString(); - } - - UnlockDb(); - - Edit_SetText(context->CommentHandle, comment->Buffer); - context->OriginalComment = comment; - - if (!processItem->CommandLine) - EnableWindow(context->MatchCommandlineHandle, FALSE); - } - break; - case WM_DESTROY: - { - PDB_OBJECT object; - PPH_STRING comment; - BOOLEAN matchCommandLine; - BOOLEAN done = FALSE; - - comment = PH_AUTO(PhGetWindowText(context->CommentHandle)); - matchCommandLine = Button_GetCheck(context->MatchCommandlineHandle) == BST_CHECKED; - - if (!processItem->CommandLine) - matchCommandLine = FALSE; - - LockDb(); - - if (processItem->CommandLine && !matchCommandLine) - { - PDB_OBJECT objectForProcessName; - PPH_STRING message = NULL; - - object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr); - objectForProcessName = FindDbObject(FILE_TAG, &processItem->ProcessName->sr); - - if (object && objectForProcessName && object->Comment->Length != 0 && objectForProcessName->Comment->Length != 0 && - !PhEqualString(comment, objectForProcessName->Comment, FALSE)) - { - message = PhaFormatString( - L"Do you want to replace the comment for %s which is currently\n \"%s\"\n" - L"with\n \"%s\"?", - processItem->ProcessName->Buffer, - objectForProcessName->Comment->Buffer, - comment->Buffer - ); - } - - if (object) - { - PhMoveReference(&object->Comment, PhReferenceEmptyString()); - DeleteDbObjectForProcessIfUnused(object); - } - - if (message) - { - // Prevent deadlocks. - UnlockDb(); - - if (MessageBox(hwndDlg, message->Buffer, L"Comment", MB_ICONQUESTION | MB_YESNO) == IDNO) - { - done = TRUE; - } - - LockDb(); - } - } - - if (!done) - { - if (comment->Length != 0) - { - if (matchCommandLine) - CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, comment); - else - CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, comment); - } - else - { - if ( - (!matchCommandLine && (object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr))) || - (matchCommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr))) - ) - { - PhMoveReference(&object->Comment, PhReferenceEmptyString()); - DeleteDbObjectForProcessIfUnused(object); - } - } - } - - UnlockDb(); - - PhDereferenceObject(context->OriginalComment); - PhFree(context); - - PhPropPageDlgProcDestroy(hwndDlg); - - SaveDb(); - InvalidateProcessComments(); - } - break; - case WM_SHOWWINDOW: - { - PPH_LAYOUT_ITEM dialogItem; - - if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) - { - PhAddPropPageLayoutItem(hwndDlg, context->CommentHandle, dialogItem, PH_ANCHOR_ALL); - PhAddPropPageLayoutItem(hwndDlg, context->RevertHandle, dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); - PhAddPropPageLayoutItem(hwndDlg, context->MatchCommandlineHandle, dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); - PhEndPropPageLayout(hwndDlg, propPageContext); - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_COMMENT: - { - if (HIWORD(wParam) == EN_CHANGE) - EnableWindow(context->RevertHandle, TRUE); - } - break; - case IDC_REVERT: - { - Edit_SetText(context->CommentHandle, context->OriginalComment->Buffer); - SendMessage(context->CommentHandle, EM_SETSEL, 0, -1); - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->CommentHandle, TRUE); - EnableWindow(context->RevertHandle, FALSE); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)context->MatchCommandlineHandle); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK ServiceCommentPageDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PSERVICE_COMMENT_PAGE_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = PhAllocate(sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); - memset(context, 0, sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); - - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PSERVICE_COMMENT_PAGE_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; - PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; - PDB_OBJECT object; - PPH_STRING comment; - - context->ServiceItem = serviceItem; - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COMMENT), NULL, PH_ANCHOR_ALL); - - // Load the comment. - - SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETLIMITTEXT, UNICODE_STRING_MAX_CHARS, 0); - - LockDb(); - - if (object = FindDbObject(SERVICE_TAG, &serviceItem->Name->sr)) - comment = object->Comment; - else - comment = PH_AUTO(PhReferenceEmptyString()); - - UnlockDb(); - - SetDlgItemText(hwndDlg, IDC_COMMENT, comment->Buffer); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&context->LayoutManager); - PhFree(context); - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_COMMENT: - { - if (HIWORD(wParam) == EN_CHANGE) - EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE); - } - break; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_KILLACTIVE: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); - } - return TRUE; - case PSN_APPLY: - { - PDB_OBJECT object; - PPH_STRING comment; - - comment = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_COMMENT))); - - LockDb(); - - if (comment->Length != 0) - { - CreateDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr, comment); - } - else - { - if (object = FindDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr)) - DeleteDbObject(object); - } - - UnlockDb(); - - SaveDb(); - InvalidateServiceComments(); - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); - } - return TRUE; - } - } - break; - } - - return FALSE; -} - -UINT_PTR CALLBACK ColorDlgHookProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - PhCenterWindow(hwndDlg, PhMainWndHandle); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - } - - return FALSE; -} \ No newline at end of file +/* + * Process Hacker User Notes - + * main program + * + * Copyright (C) 2011-2016 wj32 + * Copyright (C) 2016-2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "usernotes.h" +#include +#include + +VOID SearchChangedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ); + +static PPH_PLUGIN PluginInstance; +static PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +static PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; +static PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; +static PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +static PH_CALLBACK_REGISTRATION PluginMenuHookCallbackRegistration; +static PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; +static PH_CALLBACK_REGISTRATION MainWindowShowingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessMenuInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION GetProcessHighlightingColorCallbackRegistration; +static PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION MiListSectionMenuInitializingCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessModifiedCallbackRegistration; +static PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration; +static PH_CALLBACK_REGISTRATION SearchChangedRegistration; + +static PTOOLSTATUS_INTERFACE ToolStatusInterface = NULL; + +HWND ProcessTreeNewHandle; +LIST_ENTRY ProcessListHead = { &ProcessListHead, &ProcessListHead }; +PH_QUEUED_LOCK ProcessListLock = PH_QUEUED_LOCK_INIT; + +HWND ServiceTreeNewHandle; +LIST_ENTRY ServiceListHead = { &ServiceListHead, &ServiceListHead }; +PH_QUEUED_LOCK ServiceListLock = PH_QUEUED_LOCK_INIT; + +static COLORREF ProcessCustomColors[16] = { 0 }; + +BOOLEAN MatchDbObjectIntent( + _In_ PDB_OBJECT Object, + _In_ ULONG Intent + ) +{ + return (!(Intent & INTENT_PROCESS_COMMENT) || Object->Comment->Length != 0) && + (!(Intent & INTENT_PROCESS_PRIORITY_CLASS) || Object->PriorityClass != 0) && + (!(Intent & INTENT_PROCESS_IO_PRIORITY) || Object->IoPriorityPlusOne != 0) && + (!(Intent & INTENT_PROCESS_HIGHLIGHT) || Object->BackColor != ULONG_MAX) && + (!(Intent & INTENT_PROCESS_COLLAPSE) || Object->Collapse == TRUE) && + (!(Intent & INTENT_PROCESS_AFFINITY) || Object->AffinityMask != 0); +} + +PDB_OBJECT FindDbObjectForProcess( + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ ULONG Intent + ) +{ + PDB_OBJECT object; + + if ( + ProcessItem->CommandLine && + (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && + MatchDbObjectIntent(object, Intent) + ) + { + return object; + } + + if ( + (object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && + MatchDbObjectIntent(object, Intent) + ) + { + return object; + } + + return NULL; +} + +VOID DeleteDbObjectForProcessIfUnused( + _In_ PDB_OBJECT Object + ) +{ + if ( + Object->Comment->Length == 0 && + Object->PriorityClass == 0 && + Object->IoPriorityPlusOne == 0 && + Object->BackColor == ULONG_MAX && + Object->Collapse == FALSE && + Object->AffinityMask == 0 + ) + { + DeleteDbObject(Object); + } +} + +VOID LoadCustomColors( + VOID + ) +{ + PPH_STRING settingsString; + PH_STRINGREF remaining; + PH_STRINGREF part; + + settingsString = PhaGetStringSetting(SETTING_NAME_CUSTOM_COLOR_LIST); + remaining = settingsString->sr; + + if (remaining.Length == 0) + return; + + for (ULONG i = 0; i < ARRAYSIZE(ProcessCustomColors); i++) + { + ULONG64 integer = 0; + + if (remaining.Length == 0) + break; + + PhSplitStringRefAtChar(&remaining, ',', &part, &remaining); + + if (PhStringToInteger64(&part, 10, &integer)) + { + ProcessCustomColors[i] = (COLORREF)integer; + } + } +} + +PPH_STRING SaveCustomColors( + VOID + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 100); + + for (ULONG i = 0; i < ARRAYSIZE(ProcessCustomColors); i++) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu,", + ProcessCustomColors[i] + ); + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + return PhFinalStringBuilderString(&stringBuilder); +} + +NTSTATUS GetProcessAffinity( + _In_ HANDLE ProcessId, + _Out_ ULONG_PTR *Affinity + ) +{ + NTSTATUS status; + HANDLE processHandle; + ULONG_PTR affinityMask = 0; + PROCESS_BASIC_INFORMATION basicInfo; + + if (NT_SUCCESS(status = PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + ProcessId + ))) + { + if (NT_SUCCESS(status = PhGetProcessBasicInformation( + processHandle, + &basicInfo + ))) + { + affinityMask = basicInfo.AffinityMask; + } + + NtClose(processHandle); + } + + if (NT_SUCCESS(status)) + *Affinity = affinityMask; + + return status; +} + +IO_PRIORITY_HINT GetProcessIoPriority( + _In_ HANDLE ProcessId + ) +{ + HANDLE processHandle; + IO_PRIORITY_HINT ioPriority = ULONG_MAX; + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_QUERY_LIMITED_INFORMATION, + ProcessId + ))) + { + if (!NT_SUCCESS(PhGetProcessIoPriority( + processHandle, + &ioPriority + ))) + { + ioPriority = ULONG_MAX; + } + + NtClose(processHandle); + } + + return ioPriority; +} + +ULONG GetPriorityClassFromId( + _In_ ULONG Id + ) +{ + switch (Id) + { + case PHAPP_ID_PRIORITY_REALTIME: + return PROCESS_PRIORITY_CLASS_REALTIME; + case PHAPP_ID_PRIORITY_HIGH: + return PROCESS_PRIORITY_CLASS_HIGH; + case PHAPP_ID_PRIORITY_ABOVENORMAL: + return PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; + case PHAPP_ID_PRIORITY_NORMAL: + return PROCESS_PRIORITY_CLASS_NORMAL; + case PHAPP_ID_PRIORITY_BELOWNORMAL: + return PROCESS_PRIORITY_CLASS_BELOW_NORMAL; + case PHAPP_ID_PRIORITY_IDLE: + return PROCESS_PRIORITY_CLASS_IDLE; + } + + return 0; +} + +ULONG GetIoPriorityFromId( + _In_ ULONG Id + ) +{ + switch (Id) + { + case PHAPP_ID_IOPRIORITY_VERYLOW: + return 0; + case PHAPP_ID_IOPRIORITY_LOW: + return 1; + case PHAPP_ID_IOPRIORITY_NORMAL: + return 2; + case PHAPP_ID_IOPRIORITY_HIGH: + return 3; + } + + return -1; +} + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + static PH_STRINGREF databaseFile = PH_STRINGREF_INIT(L"usernotesdb.xml"); + PPH_PLUGIN toolStatusPlugin; + PPH_STRING directory; + PPH_STRING path; + + if (toolStatusPlugin = PhFindPlugin(TOOLSTATUS_PLUGIN_NAME)) + { + ToolStatusInterface = PhGetPluginInformation(toolStatusPlugin)->Interface; + + if (ToolStatusInterface->Version < TOOLSTATUS_INTERFACE_VERSION) + ToolStatusInterface = NULL; + } + + directory = PH_AUTO(PhGetApplicationDirectory()); + path = PH_AUTO(PhConcatStringRef2(&directory->sr, &databaseFile)); + + if (PhDoesFileExistsWin32(path->Buffer)) + { + SetDbPath(path); + } + else + { + path = PhaGetStringSetting(SETTING_NAME_DATABASE_PATH); + path = PH_AUTO(PhExpandEnvironmentStrings(&path->sr)); + + if (RtlDetermineDosPathNameType_U(path->Buffer) == RtlPathTypeRelative) + { + directory = PH_AUTO(PhGetApplicationDirectory()); + path = PH_AUTO(PhConcatStringRef2(&directory->sr, &path->sr)); + } + + SetDbPath(path); + } + + LoadDb(); + LoadCustomColors(); +} + +VOID NTAPI UnloadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + SaveDb(); +} + +VOID NTAPI ShowOptionsCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OPTIONS_POINTERS optionsEntry = (PPH_PLUGIN_OPTIONS_POINTERS)Parameter; + + optionsEntry->CreateSection( + L"UserNotes", + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_OPTIONS), + OptionsDlgProc, + NULL + ); +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + PDB_OBJECT object; + + if (!processItem) + return; + + switch (menuItem->Id) + { + case PROCESS_PRIORITY_SAVE_ID: + { + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->PriorityClass != 0) + { + object->PriorityClass = 0; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); + object->PriorityClass = processItem->PriorityClass; + } + + UnlockDb(); + SaveDb(); + } + break; + case PROCESS_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID: + { + if (processItem->CommandLine) + { + LockDb(); + + if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->PriorityClass != 0) + { + object->PriorityClass = 0; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); + object->PriorityClass = processItem->PriorityClass; + } + + UnlockDb(); + SaveDb(); + } + } + break; + case PROCESS_IO_PRIORITY_SAVE_ID: + { + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->IoPriorityPlusOne != 0) + { + object->IoPriorityPlusOne = 0; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + IO_PRIORITY_HINT ioPriority; + + object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); + + if ((ioPriority = GetProcessIoPriority(processItem->ProcessId)) != ULONG_MAX) + { + object->IoPriorityPlusOne = ioPriority + 1; + } + } + + UnlockDb(); + SaveDb(); + } + break; + case PROCESS_IO_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID: + { + if (processItem->CommandLine) + { + LockDb(); + + if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->IoPriorityPlusOne != 0) + { + object->IoPriorityPlusOne = 0; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + IO_PRIORITY_HINT ioPriority; + + object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); + + if ((ioPriority = GetProcessIoPriority(processItem->ProcessId)) != ULONG_MAX) + { + object->IoPriorityPlusOne = ioPriority + 1; + } + } + + UnlockDb(); + SaveDb(); + } + } + break; + case PROCESS_HIGHLIGHT_ID: + { + BOOLEAN highlightPresent = (BOOLEAN)menuItem->Context; + + if (!highlightPresent) + { + CHOOSECOLOR chooseColor = { sizeof(CHOOSECOLOR) }; + chooseColor.hwndOwner = menuItem->OwnerWindow; + chooseColor.lpCustColors = ProcessCustomColors; + chooseColor.lpfnHook = ColorDlgHookProc; + chooseColor.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_SOLIDCOLOR | CC_ENABLEHOOK; + + if (ChooseColor(&chooseColor)) + { + PhSetStringSetting2(SETTING_NAME_CUSTOM_COLOR_LIST, &PH_AUTO_T(PH_STRING, SaveCustomColors())->sr); + + LockDb(); + + object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); + object->BackColor = chooseColor.rgbResult; + + UnlockDb(); + SaveDb(); + } + } + else + { + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->BackColor != ULONG_MAX) + { + object->BackColor = ULONG_MAX; + DeleteDbObjectForProcessIfUnused(object); + } + + UnlockDb(); + SaveDb(); + } + + PhInvalidateAllProcessNodes(); + } + break; + case PROCESS_COLLAPSE_ID: + { + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->Collapse) + { + object->Collapse = FALSE; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); + object->Collapse = TRUE; + } + + UnlockDb(); + SaveDb(); + } + break; + case PROCESS_AFFINITY_SAVE_ID: + { + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->AffinityMask != 0) + { + object->AffinityMask = 0; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + NTSTATUS status; + ULONG_PTR affinityMask; + + if (NT_SUCCESS(status = GetProcessAffinity(processItem->ProcessId, &affinityMask))) + { + object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL); + object->AffinityMask = affinityMask; + } + else + { + PhShowStatus(menuItem->OwnerWindow, L"Unable to query the process affinity.", status, 0); + } + } + + UnlockDb(); + SaveDb(); + } + break; + case PROCESS_AFFINITY_SAVE_FOR_THIS_COMMAND_LINE_ID: + { + if (processItem->CommandLine) + { + LockDb(); + + if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->AffinityMask != 0) + { + object->AffinityMask = 0; + DeleteDbObjectForProcessIfUnused(object); + } + else + { + NTSTATUS status; + ULONG_PTR affinityMask; + + if (NT_SUCCESS(status = GetProcessAffinity(processItem->ProcessId, &affinityMask))) + { + object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL); + object->AffinityMask = affinityMask; + } + else + { + PhShowStatus(menuItem->OwnerWindow, L"Unable to query the process affinity.", status, 0); + } + } + + UnlockDb(); + SaveDb(); + } + } + break; + } +} + +VOID NTAPI MenuHookCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo = Parameter; + ULONG id = menuHookInfo->SelectedItem->Id; + + switch (id) + { + case PHAPP_ID_PRIORITY_REALTIME: + case PHAPP_ID_PRIORITY_HIGH: + case PHAPP_ID_PRIORITY_ABOVENORMAL: + case PHAPP_ID_PRIORITY_NORMAL: + case PHAPP_ID_PRIORITY_BELOWNORMAL: + case PHAPP_ID_PRIORITY_IDLE: + { + BOOLEAN changed = FALSE; + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + ULONG i; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + LockDb(); + + for (i = 0; i < numberOfProcesses; i++) + { + PDB_OBJECT object; + + if (object = FindDbObjectForProcess(processes[i], INTENT_PROCESS_PRIORITY_CLASS)) + { + ULONG newPriorityClass = GetPriorityClassFromId(id); + + if (object->PriorityClass != newPriorityClass) + { + object->PriorityClass = newPriorityClass; + changed = TRUE; + } + } + } + + UnlockDb(); + PhFree(processes); + + if (changed) + SaveDb(); + } + break; + case PHAPP_ID_IOPRIORITY_VERYLOW: + case PHAPP_ID_IOPRIORITY_LOW: + case PHAPP_ID_IOPRIORITY_NORMAL: + case PHAPP_ID_IOPRIORITY_HIGH: + { + BOOLEAN changed = FALSE; + PPH_PROCESS_ITEM *processes; + ULONG numberOfProcesses; + ULONG i; + + PhGetSelectedProcessItems(&processes, &numberOfProcesses); + LockDb(); + + for (i = 0; i < numberOfProcesses; i++) + { + PDB_OBJECT object; + + if (object = FindDbObjectForProcess(processes[i], INTENT_PROCESS_IO_PRIORITY)) + { + ULONG newIoPriorityPlusOne = GetIoPriorityFromId(id) + 1; + + if (object->IoPriorityPlusOne != newIoPriorityPlusOne) + { + object->IoPriorityPlusOne = newIoPriorityPlusOne; + changed = TRUE; + } + } + } + + UnlockDb(); + PhFree(processes); + + if (changed) + SaveDb(); + } + break; + case PHAPP_ID_PROCESS_AFFINITY: + { + NTSTATUS status; + BOOLEAN changed = FALSE; + ULONG_PTR affinityMask; + ULONG_PTR newAffinityMask; + PPH_PROCESS_ITEM processItem = PhGetSelectedProcessItem(); + + if (!processItem) + break; + + // Query the current process affinity. + if (!NT_SUCCESS(status = GetProcessAffinity(processItem->ProcessId, &affinityMask))) + { + // TODO: Fix issue saving affinity for system processes. + break; + } + + // Don't show the default Process Hacker affinity dialog. + menuHookInfo->Handled = TRUE; + + // Show the affinity dialog (with our values). + if (PhShowProcessAffinityDialog2(menuHookInfo->MenuInfo->OwnerWindow, affinityMask, &newAffinityMask)) + { + PDB_OBJECT object; + + LockDb(); + + if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_AFFINITY)) + { + // Update the process affinity in our database (if the database values are different). + if (object->AffinityMask != newAffinityMask) + { + object->AffinityMask = newAffinityMask; + changed = TRUE; + } + } + + UnlockDb(); + + if (changed) + { + SaveDb(); + } + + // Update the process affinity in Windows (if the system values are different). + if (affinityMask != newAffinityMask) + { + HANDLE processHandle; + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + processItem->ProcessId + ))) + { + PhSetProcessAffinityMask(processHandle, newAffinityMask); + NtClose(processHandle); + } + } + } + } + break; + } +} + +VOID InvalidateProcessComments( + VOID + ) +{ + PLIST_ENTRY listEntry; + + PhAcquireQueuedLockExclusive(&ProcessListLock); + + listEntry = ProcessListHead.Flink; + + while (listEntry != &ProcessListHead) + { + PPROCESS_EXTENSION extension; + + extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); + + extension->Valid = FALSE; + + listEntry = listEntry->Flink; + } + + PhReleaseQueuedLockExclusive(&ProcessListLock); +} + +VOID UpdateProcessComment( + _In_ PPH_PROCESS_NODE Node, + _In_ PPROCESS_EXTENSION Extension + ) +{ + if (!Extension->Valid) + { + PDB_OBJECT object; + + LockDb(); + + if (object = FindDbObjectForProcess(Node->ProcessItem, INTENT_PROCESS_COMMENT)) + { + PhSwapReference(&Extension->Comment, object->Comment); + } + else + { + PhClearReference(&Extension->Comment); + } + + UnlockDb(); + + Extension->Valid = TRUE; + } +} + +VOID InvalidateServiceComments( + VOID + ) +{ + PLIST_ENTRY listEntry; + + PhAcquireQueuedLockExclusive(&ServiceListLock); + + listEntry = ServiceListHead.Flink; + + while (listEntry != &ServiceListHead) + { + PSERVICE_EXTENSION extension; + + extension = CONTAINING_RECORD(listEntry, SERVICE_EXTENSION, ListEntry); + + extension->Valid = FALSE; + + listEntry = listEntry->Flink; + } + + PhReleaseQueuedLockExclusive(&ServiceListLock); +} + +VOID UpdateServiceComment( + _In_ PPH_SERVICE_NODE Node, + _In_ PSERVICE_EXTENSION Extension + ) +{ + if (!Extension->Valid) + { + PDB_OBJECT object; + + LockDb(); + + if (object = FindDbObject(SERVICE_TAG, &Node->ServiceItem->Name->sr)) + { + PhSwapReference(&Extension->Comment, object->Comment); + } + else + { + PhClearReference(&Extension->Comment); + } + + UnlockDb(); + + Extension->Valid = TRUE; + } +} + +VOID TreeNewMessageCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; + + switch (message->Message) + { + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; + + if (message->TreeNewHandle == ProcessTreeNewHandle) + { + PPH_PROCESS_NODE node; + + node = (PPH_PROCESS_NODE)getCellText->Node; + + switch (message->SubId) + { + case COMMENT_COLUMN_ID: + { + PPROCESS_EXTENSION extension; + + extension = PhPluginGetObjectExtension(PluginInstance, node->ProcessItem, EmProcessItemType); + UpdateProcessComment(node, extension); + getCellText->Text = PhGetStringRef(extension->Comment); + } + break; + } + } + else if (message->TreeNewHandle == ServiceTreeNewHandle) + { + PPH_SERVICE_NODE node; + + node = (PPH_SERVICE_NODE)getCellText->Node; + + switch (message->SubId) + { + case COMMENT_COLUMN_ID: + { + PSERVICE_EXTENSION extension; + + extension = PhPluginGetObjectExtension(PluginInstance, node->ServiceItem, EmServiceItemType); + UpdateServiceComment(node, extension); + getCellText->Text = PhGetStringRef(extension->Comment); + } + break; + } + } + } + break; + } +} + +VOID MainWindowShowingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + if (ToolStatusInterface) + { + PhRegisterCallback(ToolStatusInterface->SearchChangedEvent, SearchChangedHandler, NULL, &SearchChangedRegistration); + } +} + +VOID ProcessPropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; + + PhAddProcessPropPage( + propContext->PropContext, + PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCCOMMENT), ProcessCommentPageDlgProc, NULL) + ); +} + +VOID AddSavePriorityMenuItemsAndHook( + _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, + _In_ PPH_PROCESS_ITEM ProcessItem, + _In_ BOOLEAN UseSelectionForHook + ) +{ + PPH_EMENU_ITEM affinityMenuItem; + PPH_EMENU_ITEM priorityMenuItem; + PPH_EMENU_ITEM ioPriorityMenuItem; + PPH_EMENU_ITEM saveMenuItem; + PPH_EMENU_ITEM saveForCommandLineMenuItem; + PDB_OBJECT object; + + if (affinityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"Affinity", PHAPP_ID_PROCESS_AFFINITY)) + { + // HACK: Change default Affinity menu-item into a drop-down list + PhInsertEMenuItem(affinityMenuItem, PhCreateEMenuItem(0, affinityMenuItem->Id, L"Set &affinity", NULL, NULL), ULONG_MAX); + //PhInsertEMenuItem(affinityMenuItem, PhPluginCreateEMenuItem(PluginInstance, 0, PHAPP_ID_PROCESS_AFFINITY, L"Set &affinity", NULL), PhIndexOfEMenuItem(MenuInfo->Menu, affinityMenuItem) + 1); + //PhRemoveEMenuItem(affinityMenuItem, affinityMenuItem, 0); + + // Insert standard menu-items + PhInsertEMenuItem(affinityMenuItem, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(affinityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_AFFINITY_SAVE_ID, PhaFormatString(L"&Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), ULONG_MAX); + PhInsertEMenuItem(affinityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_AFFINITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save &for this command line", NULL), ULONG_MAX); + + if (!ProcessItem->CommandLine) + saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED; + + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->AffinityMask != 0) + saveMenuItem->Flags |= PH_EMENU_CHECKED; + if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->AffinityMask != 0) + saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED; + + UnlockDb(); + } + + // Priority + if (priorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"Priority", 0)) + { + PhInsertEMenuItem(priorityMenuItem, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(priorityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_PRIORITY_SAVE_ID, PhaFormatString(L"&Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), ULONG_MAX); + PhInsertEMenuItem(priorityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save &for this command line", NULL), ULONG_MAX); + + if (!ProcessItem->CommandLine) + saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED; + + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->PriorityClass != 0) + saveMenuItem->Flags |= PH_EMENU_CHECKED; + if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->PriorityClass != 0) + saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED; + + UnlockDb(); + } + + // I/O Priority + if (ioPriorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"I/O Priority", 0)) + { + PhInsertEMenuItem(ioPriorityMenuItem, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(ioPriorityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_IO_PRIORITY_SAVE_ID, PhaFormatString(L"&Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), ULONG_MAX); + PhInsertEMenuItem(ioPriorityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_IO_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save &for this command line", NULL), ULONG_MAX); + + if (!ProcessItem->CommandLine) + saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED; + + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->IoPriorityPlusOne != 0) + saveMenuItem->Flags |= PH_EMENU_CHECKED; + if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->IoPriorityPlusOne != 0) + saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED; + + UnlockDb(); + } + + PhPluginAddMenuHook(MenuInfo, PluginInstance, UseSelectionForHook ? NULL : ProcessItem->ProcessId); +} + +VOID ProcessMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + BOOLEAN highlightPresent = FALSE; + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_PROCESS_ITEM processItem; + PPH_EMENU_ITEM miscMenuItem; + PPH_EMENU_ITEM collapseMenuItem; + PPH_EMENU_ITEM highlightMenuItem; + PDB_OBJECT object; + + if (menuInfo->u.Process.NumberOfProcesses != 1) + return; + + processItem = menuInfo->u.Process.Processes[0]; + + if (!PH_IS_FAKE_PROCESS_ID(processItem->ProcessId) && processItem->ProcessId != SYSTEM_IDLE_PROCESS_ID && processItem->ProcessId != SYSTEM_PROCESS_ID) + AddSavePriorityMenuItemsAndHook(menuInfo, processItem, TRUE); + + if (!(miscMenuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Miscellaneous", 0))) + return; + + LockDb(); + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->BackColor != ULONG_MAX) + highlightPresent = TRUE; + UnlockDb(); + + PhInsertEMenuItem(miscMenuItem, collapseMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_COLLAPSE_ID, L"Col&lapse by default", NULL), 0); + PhInsertEMenuItem(miscMenuItem, highlightMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_HIGHLIGHT_ID, L"Highligh&t", UlongToPtr(highlightPresent)), 1); + PhInsertEMenuItem(miscMenuItem, PhCreateEMenuSeparator(), 2); + + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &menuInfo->u.Process.Processes[0]->ProcessName->sr)) && object->Collapse) + collapseMenuItem->Flags |= PH_EMENU_CHECKED; + if (highlightPresent) + highlightMenuItem->Flags |= PH_EMENU_CHECKED; + + UnlockDb(); +} + +static LONG NTAPI ProcessCommentSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PPH_PROCESS_NODE node1 = Node1; + PPH_PROCESS_NODE node2 = Node2; + PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType); + PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType); + + UpdateProcessComment(node1, extension1); + UpdateProcessComment(node2, extension2); + + return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE); +} + +VOID ProcessTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PH_TREENEW_COLUMN column; + + ProcessTreeNewHandle = info->TreeNewHandle; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Comment"; + column.Width = 120; + column.Alignment = PH_ALIGN_LEFT; + + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ProcessCommentSortFunction); +} + +VOID GetProcessHighlightingColorCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_GET_HIGHLIGHTING_COLOR getHighlightingColor = Parameter; + PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)getHighlightingColor->Parameter; + PDB_OBJECT object; + + if (getHighlightingColor->Handled) + return; + + LockDb(); + + if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->BackColor != ULONG_MAX) + { + getHighlightingColor->BackColor = object->BackColor; + getHighlightingColor->Cache = TRUE; + getHighlightingColor->Handled = TRUE; + } + + UnlockDb(); +} + +VOID ServicePropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; + PROPSHEETPAGE propSheetPage; + + if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) + { + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.hInstance = PluginInstance->DllBase; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVCOMMENT); + propSheetPage.pszTitle = L"Comment"; + propSheetPage.pfnDlgProc = ServiceCommentPageDlgProc; + propSheetPage.lParam = (LPARAM)objectProperties->Parameter; + objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); + } +} + +LONG NTAPI ServiceCommentSortFunction( + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ ULONG SubId, + _In_ PH_SORT_ORDER SortOrder, + _In_ PVOID Context + ) +{ + PPH_SERVICE_NODE node1 = Node1; + PPH_SERVICE_NODE node2 = Node2; + PSERVICE_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ServiceItem, EmServiceItemType); + PSERVICE_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ServiceItem, EmServiceItemType); + + UpdateServiceComment(node1, extension1); + UpdateServiceComment(node2, extension2); + + return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE); +} + +VOID ServiceTreeNewInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; + PH_TREENEW_COLUMN column; + + ServiceTreeNewHandle = info->TreeNewHandle; + + memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); + column.Text = L"Comment"; + column.Width = 120; + column.Alignment = PH_ALIGN_LEFT; + + PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ServiceCommentSortFunction); +} + +VOID MiListSectionMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_PROCESS_ITEM processItem = menuInfo->u.MiListSection.ProcessGroup->Representative; + + if (PH_IS_FAKE_PROCESS_ID(processItem->ProcessId) || processItem->ProcessId == SYSTEM_IDLE_PROCESS_ID || processItem->ProcessId == SYSTEM_PROCESS_ID) + return; + + AddSavePriorityMenuItemsAndHook(menuInfo, processItem, FALSE); +} + +VOID ProcessModifiedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PROCESS_ITEM processItem = Parameter; + PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processItem, EmProcessItemType); + + extension->Valid = FALSE; +} + +VOID ProcessesUpdatedCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PLIST_ENTRY listEntry; + + if (GetNumberOfDbObjects() == 0) + return; + + PhAcquireQueuedLockExclusive(&ProcessListLock); + LockDb(); + + listEntry = ProcessListHead.Flink; + + while (listEntry != &ProcessListHead) + { + PPROCESS_EXTENSION extension; + PPH_PROCESS_ITEM processItem; + PDB_OBJECT object; + + extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); + processItem = extension->ProcessItem; + + if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_PRIORITY_CLASS)) + { + if (processItem->PriorityClass != object->PriorityClass) + { + HANDLE processHandle; + PROCESS_PRIORITY_CLASS priorityClass; + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + processItem->ProcessId + ))) + { + priorityClass.Foreground = FALSE; + priorityClass.PriorityClass = (UCHAR)object->PriorityClass; + + PhSetProcessPriority(processHandle, priorityClass); + NtClose(processHandle); + } + } + } + + if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_IO_PRIORITY)) + { + if (object->IoPriorityPlusOne != 0) + { + IO_PRIORITY_HINT ioPriority; + + ioPriority = GetProcessIoPriority(processItem->ProcessId); + + if (ioPriority != ULONG_MAX && ioPriority != object->IoPriorityPlusOne - 1) + { + HANDLE processHandle; + + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + processItem->ProcessId + ))) + { + PhSetProcessIoPriority(processHandle, object->IoPriorityPlusOne - 1); + NtClose(processHandle); + } + } + } + } + + if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_AFFINITY)) + { + if (object->AffinityMask != 0) + { + ULONG_PTR affinityMask; + HANDLE processHandle; + + if (NT_SUCCESS(GetProcessAffinity(processItem->ProcessId, &affinityMask))) + { + if (affinityMask != object->AffinityMask) + { + if (NT_SUCCESS(PhOpenProcess( + &processHandle, + PROCESS_SET_INFORMATION, + processItem->ProcessId + ))) + { + PhSetProcessAffinityMask(processHandle, object->AffinityMask); + NtClose(processHandle); + } + } + } + } + } + + listEntry = listEntry->Flink; + } + + UnlockDb(); + PhReleaseQueuedLockExclusive(&ProcessListLock); +} + +VOID SearchChangedHandler( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_STRING searchText = Parameter; + + if (PhIsNullOrEmptyString(searchText)) + { + // ToolStatus expanded all nodes for searching, but the search text just became empty. We + // should re-collapse processes. + + PPH_LIST nodes = PH_AUTO(PhDuplicateProcessNodeList()); + ULONG i; + BOOLEAN changed = FALSE; + + LockDb(); + + for (i = 0; i < nodes->Count; i++) + { + PPH_PROCESS_NODE node = nodes->Items[i]; + PDB_OBJECT object; + + if ((object = FindDbObjectForProcess(node->ProcessItem, INTENT_PROCESS_COLLAPSE)) && object->Collapse) + { + node->Node.Expanded = FALSE; + changed = TRUE; + } + } + + UnlockDb(); + + if (changed) + TreeNew_NodesStructured(ProcessTreeNewHandle); + } +} + +VOID ProcessItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_PROCESS_ITEM processItem = Object; + PPROCESS_EXTENSION extension = Extension; + + memset(extension, 0, sizeof(PROCESS_EXTENSION)); + extension->ProcessItem = processItem; + + PhAcquireQueuedLockExclusive(&ProcessListLock); + InsertTailList(&ProcessListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessListLock); +} + +VOID ProcessItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_PROCESS_ITEM processItem = Object; + PPROCESS_EXTENSION extension = Extension; + + PhClearReference(&extension->Comment); + PhAcquireQueuedLockExclusive(&ProcessListLock); + RemoveEntryList(&extension->ListEntry); + PhReleaseQueuedLockExclusive(&ProcessListLock); +} + +VOID ProcessNodeCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_PROCESS_NODE processNode = Object; + PDB_OBJECT object; + + LockDb(); + + if ((object = FindDbObjectForProcess(processNode->ProcessItem, INTENT_PROCESS_COLLAPSE)) && object->Collapse) + processNode->Node.Expanded = FALSE; + + UnlockDb(); +} + +VOID ServiceItemCreateCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_SERVICE_ITEM processItem = Object; + PSERVICE_EXTENSION extension = Extension; + + memset(extension, 0, sizeof(SERVICE_EXTENSION)); + PhAcquireQueuedLockExclusive(&ServiceListLock); + InsertTailList(&ServiceListHead, &extension->ListEntry); + PhReleaseQueuedLockExclusive(&ServiceListLock); +} + +VOID ServiceItemDeleteCallback( + _In_ PVOID Object, + _In_ PH_EM_OBJECT_TYPE ObjectType, + _In_ PVOID Extension + ) +{ + PPH_SERVICE_ITEM processItem = Object; + PSERVICE_EXTENSION extension = Extension; + + PhClearReference(&extension->Comment); + PhAcquireQueuedLockExclusive(&ServiceListLock); + RemoveEntryList(&extension->ListEntry); + PhReleaseQueuedLockExclusive(&ServiceListLock); +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + if (Reason == DLL_PROCESS_ATTACH) + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { StringSettingType, SETTING_NAME_DATABASE_PATH, L"%APPDATA%\\Process Hacker\\usernotesdb.xml" }, + { StringSettingType, SETTING_NAME_CUSTOM_COLOR_LIST, L"" } + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"User Notes"; + info->Author = L"dmex, wj32"; + info->Description = L"Allows the user to add comments for processes and services, save process priority and affinity, highlight individual processes and show processes collapsed by default."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1120"; + + InitializeDb(); + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackUnload), + UnloadCallback, + NULL, + &PluginUnloadCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackOptionsWindowInitializing), + ShowOptionsCallback, + NULL, + &PluginShowOptionsCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuHook), + MenuHookCallback, + NULL, + &PluginMenuHookCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), + TreeNewMessageCallback, + NULL, + &TreeNewMessageCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainWindowShowing), + MainWindowShowingCallback, + NULL, + &MainWindowShowingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), + ProcessPropertiesInitializingCallback, + NULL, + &ProcessPropertiesInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), + ServicePropertiesInitializingCallback, + NULL, + &ServicePropertiesInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessMenuInitializing), + ProcessMenuInitializingCallback, + NULL, + &ProcessMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), + ProcessTreeNewInitializingCallback, + NULL, + &ProcessTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackGetProcessHighlightingColor), + GetProcessHighlightingColorCallback, + NULL, + &GetProcessHighlightingColorCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), + ServiceTreeNewInitializingCallback, + NULL, + &ServiceTreeNewInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMiListSectionMenuInitializing), + MiListSectionMenuInitializingCallback, + NULL, + &MiListSectionMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderModifiedEvent), + ProcessModifiedCallback, + NULL, + &ProcessModifiedCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessProviderUpdatedEvent), + ProcessesUpdatedCallback, + NULL, + &ProcessesUpdatedCallbackRegistration + ); + + PhPluginSetObjectExtension( + PluginInstance, + EmProcessItemType, + sizeof(PROCESS_EXTENSION), + ProcessItemCreateCallback, + ProcessItemDeleteCallback + ); + PhPluginSetObjectExtension( + PluginInstance, + EmProcessNodeType, + sizeof(PROCESS_EXTENSION), + ProcessNodeCreateCallback, + NULL + ); + PhPluginSetObjectExtension( + PluginInstance, + EmServiceItemType, + sizeof(SERVICE_EXTENSION), + ServiceItemCreateCallback, + ServiceItemDeleteCallback + ); + + PhAddSettings(settings, ARRAYSIZE(settings)); + } + + return TRUE; +} + +INT_PTR CALLBACK OptionsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + static PH_LAYOUT_MANAGER LayoutManager; + + switch (uMsg) + { + case WM_INITDIALOG: + { + PhSetDialogItemText(hwndDlg, IDC_DATABASE, PhaGetStringSetting(SETTING_NAME_DATABASE_PATH)->Buffer); + + PhInitializeLayoutManager(&LayoutManager, hwndDlg); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_DATABASE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&LayoutManager, GetDlgItem(hwndDlg, IDC_BROWSE), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + + PhSetDialogFocus(hwndDlg, GetDlgItem(hwndDlg, IDCANCEL)); + } + break; + case WM_DESTROY: + { + PhSetStringSetting2(SETTING_NAME_DATABASE_PATH, &PhaGetDlgItemText(hwndDlg, IDC_DATABASE)->sr); + + PhDeleteLayoutManager(&LayoutManager); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_BROWSE: + { + static PH_FILETYPE_FILTER filters[] = + { + { L"XML files (*.xml)", L"*.xml" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + PPH_STRING fileName; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); + + fileName = PH_AUTO(PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_DATABASE))); + PhSetFileDialogFileName(fileDialog, fileName->Buffer); + + if (PhShowFileDialog(hwndDlg, fileDialog)) + { + fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); + PhSetDialogItemText(hwndDlg, IDC_DATABASE, fileName->Buffer); + } + + PhFreeFileDialog(fileDialog); + } + break; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK ProcessCommentPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + PPROCESS_COMMENT_PAGE_CONTEXT context; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + PDB_OBJECT object; + PPH_STRING comment; + + context = propPageContext->Context = PhAllocate(sizeof(PROCESS_COMMENT_PAGE_CONTEXT)); + context->CommentHandle = GetDlgItem(hwndDlg, IDC_COMMENT); + context->RevertHandle = GetDlgItem(hwndDlg, IDC_REVERT); + context->MatchCommandlineHandle = GetDlgItem(hwndDlg, IDC_MATCHCOMMANDLINE); + + // Load the comment. + Edit_LimitText(context->CommentHandle, UNICODE_STRING_MAX_CHARS); + + LockDb(); + + if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_COMMENT)) + { + PhSetReference(&comment, object->Comment); + + if (processItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->Comment->Length != 0) + { + Button_SetCheck(context->MatchCommandlineHandle, BST_CHECKED); + } + } + else + { + comment = PhReferenceEmptyString(); + } + + UnlockDb(); + + Edit_SetText(context->CommentHandle, comment->Buffer); + context->OriginalComment = comment; + + if (!processItem->CommandLine) + EnableWindow(context->MatchCommandlineHandle, FALSE); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PDB_OBJECT object; + PPH_STRING comment; + BOOLEAN matchCommandLine; + BOOLEAN done = FALSE; + + comment = PH_AUTO(PhGetWindowText(context->CommentHandle)); + matchCommandLine = Button_GetCheck(context->MatchCommandlineHandle) == BST_CHECKED; + + if (!processItem->CommandLine) + matchCommandLine = FALSE; + + LockDb(); + + if (processItem->CommandLine && !matchCommandLine) + { + PDB_OBJECT objectForProcessName; + PPH_STRING message = NULL; + + object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr); + objectForProcessName = FindDbObject(FILE_TAG, &processItem->ProcessName->sr); + + if (object && objectForProcessName && object->Comment->Length != 0 && objectForProcessName->Comment->Length != 0 && + !PhEqualString(comment, objectForProcessName->Comment, FALSE)) + { + message = PhaFormatString( + L"Do you want to replace the comment for %s which is currently\n \"%s\"\n" + L"with\n \"%s\"?", + processItem->ProcessName->Buffer, + objectForProcessName->Comment->Buffer, + comment->Buffer + ); + } + + if (object) + { + PhMoveReference(&object->Comment, PhReferenceEmptyString()); + DeleteDbObjectForProcessIfUnused(object); + } + + if (message) + { + // Prevent deadlocks. + UnlockDb(); + + if (MessageBox(hwndDlg, message->Buffer, L"Comment", MB_ICONQUESTION | MB_YESNO) == IDNO) + { + done = TRUE; + } + + LockDb(); + } + } + + if (!done) + { + if (comment->Length != 0) + { + if (matchCommandLine) + CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, comment); + else + CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, comment); + } + else + { + if ( + (!matchCommandLine && (object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr))) || + (matchCommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr))) + ) + { + PhMoveReference(&object->Comment, PhReferenceEmptyString()); + DeleteDbObjectForProcessIfUnused(object); + } + } + } + + UnlockDb(); + + PhDereferenceObject(context->OriginalComment); + PhFree(context); + + SaveDb(); + InvalidateProcessComments(); + } + break; + case WM_SHOWWINDOW: + { + PPH_LAYOUT_ITEM dialogItem; + + if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) + { + PhAddPropPageLayoutItem(hwndDlg, context->CommentHandle, dialogItem, PH_ANCHOR_ALL); + PhAddPropPageLayoutItem(hwndDlg, context->RevertHandle, dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + PhAddPropPageLayoutItem(hwndDlg, context->MatchCommandlineHandle, dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhEndPropPageLayout(hwndDlg, propPageContext); + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_COMMENT: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) + EnableWindow(context->RevertHandle, TRUE); + } + break; + case IDC_REVERT: + { + Edit_SetText(context->CommentHandle, context->OriginalComment->Buffer); + SendMessage(context->CommentHandle, EM_SETSEL, 0, -1); + PhSetDialogFocus(hwndDlg, context->CommentHandle); + EnableWindow(context->RevertHandle, FALSE); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)context->MatchCommandlineHandle); + } + return TRUE; + } + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK ServiceCommentPageDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PSERVICE_COMMENT_PAGE_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = PhAllocate(sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); + memset(context, 0, sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); + + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; + PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; + PDB_OBJECT object; + PPH_STRING comment; + + context->ServiceItem = serviceItem; + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COMMENT), NULL, PH_ANCHOR_ALL); + + // Load the comment. + + SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETLIMITTEXT, UNICODE_STRING_MAX_CHARS, 0); + + LockDb(); + + if (object = FindDbObject(SERVICE_TAG, &serviceItem->Name->sr)) + comment = object->Comment; + else + comment = PH_AUTO(PhReferenceEmptyString()); + + UnlockDb(); + + PhSetDialogItemText(hwndDlg, IDC_COMMENT, comment->Buffer); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + PhFree(context); + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_COMMENT: + { + if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) + EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE); + } + break; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_KILLACTIVE: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); + } + return TRUE; + case PSN_APPLY: + { + PDB_OBJECT object; + PPH_STRING comment; + + comment = PH_AUTO(PhGetWindowText(GetDlgItem(hwndDlg, IDC_COMMENT))); + + LockDb(); + + if (comment->Length != 0) + { + CreateDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr, comment); + } + else + { + if (object = FindDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr)) + DeleteDbObject(object); + } + + UnlockDb(); + + SaveDb(); + InvalidateServiceComments(); + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + } + return TRUE; + } + } + break; + } + + return FALSE; +} + +UINT_PTR CALLBACK ColorDlgHookProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PhCenterWindow(hwndDlg, PhMainWndHandle); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + } + + return FALSE; +} diff --git a/plugins/UserNotes/resource.h b/plugins/UserNotes/resource.h index 16b90406a0f6..7d5001f06322 100644 --- a/plugins/UserNotes/resource.h +++ b/plugins/UserNotes/resource.h @@ -1,25 +1,25 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by UserNotes.rc -// -#define IDD_OPTIONS 101 -#define IDD_COMMENT 102 -#define IDD_PROCCOMMENT 102 -#define IDD_SRVCOMMENT 103 -#define IDC_DATABASE 1001 -#define IDC_BROWSE 1002 -#define IDC_COMMENT 1003 -#define IDC_REVERT 1004 -#define IDC_CHECK1 1005 -#define IDC_MATCHCOMMANDLINE 1005 - -// 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 1006 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by UserNotes.rc +// +#define IDD_OPTIONS 101 +#define IDD_COMMENT 102 +#define IDD_PROCCOMMENT 102 +#define IDD_SRVCOMMENT 103 +#define IDC_DATABASE 1001 +#define IDC_BROWSE 1002 +#define IDC_COMMENT 1003 +#define IDC_REVERT 1004 +#define IDC_CHECK1 1005 +#define IDC_MATCHCOMMANDLINE 1005 + +// 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 1006 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/UserNotes/usernotes.h b/plugins/UserNotes/usernotes.h index 1d323c874c8a..95576b60d95f 100644 --- a/plugins/UserNotes/usernotes.h +++ b/plugins/UserNotes/usernotes.h @@ -26,11 +26,9 @@ #include #include +#include #include - -#include #include -#include #include "db.h" #include "resource.h" diff --git a/plugins/WindowExplorer/CHANGELOG.txt b/plugins/WindowExplorer/CHANGELOG.txt index 6266123ef575..5473e5bb68ef 100644 --- a/plugins/WindowExplorer/CHANGELOG.txt +++ b/plugins/WindowExplorer/CHANGELOG.txt @@ -1,22 +1,22 @@ -1.6 - * Added Searchbox - * Added Window tab to process properties - -1.5 - * Fixed window list not displaying Modern UI windows - -1.4 - * Fixed hook support for low integrity processes - -1.3 - * Fixed hook bugs - -1.2 - * Added more window properties - -1.1 - * Added Always on Top and Opacity - * Renamed Show/Hide and Enable/Disable to Visible and Enabled - -1.0 +1.6 + * Added Searchbox + * Added Window tab to process properties + +1.5 + * Fixed window list not displaying Modern UI windows + +1.4 + * Fixed hook support for low integrity processes + +1.3 + * Fixed hook bugs + +1.2 + * Added more window properties + +1.1 + * Added Always on Top and Opacity + * Renamed Show/Hide and Enable/Disable to Visible and Enabled + +1.0 * Initial release \ No newline at end of file diff --git a/plugins/WindowExplorer/WindowExplorer.rc b/plugins/WindowExplorer/WindowExplorer.rc index 88315ad57173..4e198db14f90 100644 --- a/plugins/WindowExplorer/WindowExplorer.rc +++ b/plugins/WindowExplorer/WindowExplorer.rc @@ -1,308 +1,216 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,6,0,0 - PRODUCTVERSION 1,6,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "Window Explorer plugin for Process Hacker" - VALUE "FileVersion", "1.6" - VALUE "InternalName", "ProcessHacker.WindowExplorer" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "WindowExplorer.dll" - VALUE "ProductName", "Window Explorer plugin for Process Hacker" - VALUE "ProductVersion", "1.6" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_WNDLIST DIALOGEX 0, 0, 447, 309 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_APPWINDOW -CAPTION "Windows" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Refresh",IDC_REFRESH,2,3,50,14 - CONTROL "Windows",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0x2,2,19,443,288,WS_EX_CLIENTEDGE - EDITTEXT IDC_SEARCHEDIT,302,3,143,14,ES_AUTOHSCROLL -END - -IDD_WNDPROPS DIALOGEX 0, 0, 233, 152 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Properties" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Property list for the window:",IDC_STATIC,7,7,92,8 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,20,219,125 -END - -IDD_WNDGENERAL DIALOGEX 0, 0, 233, 147 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Text:",IDC_STATIC,7,8,18,8 - EDITTEXT IDC_TEXT,33,7,193,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Thread:",IDC_STATIC,7,22,26,8 - LTEXT "Static",IDC_THREAD,77,22,149,8,SS_ENDELLIPSIS - LTEXT "Rectangle:",IDC_STATIC,7,33,36,8 - LTEXT "Static",IDC_RECTANGLE,77,33,149,8,SS_ENDELLIPSIS - LTEXT "Normal rectangle:",IDC_STATIC,7,44,60,8 - LTEXT "Static",IDC_NORMALRECTANGLE,77,44,149,8,SS_ENDELLIPSIS - LTEXT "Client rectangle:",IDC_STATIC,7,55,56,8 - LTEXT "Static",IDC_CLIENTRECTANGLE,77,55,149,8,SS_ENDELLIPSIS - LTEXT "Instance handle:",IDC_STATIC,7,66,56,8 - LTEXT "Static",IDC_INSTANCEHANDLE,77,66,149,8,SS_ENDELLIPSIS - LTEXT "Menu handle:",IDC_STATIC,7,77,45,8 - LTEXT "Static",IDC_MENUHANDLE,77,77,149,8,SS_ENDELLIPSIS - LTEXT "User data:",IDC_STATIC,7,88,36,8 - LTEXT "Static",IDC_USERDATA,77,88,149,8,SS_ENDELLIPSIS - LTEXT "Unicode:",IDC_STATIC,7,99,29,8 - LTEXT "Static",IDC_UNICODE,77,99,149,8 - LTEXT "Window proc:",IDC_STATIC,7,110,45,8 - EDITTEXT IDC_WINDOWPROC,75,110,151,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Dialog proc:",IDC_STATIC,7,121,39,8 - EDITTEXT IDC_DIALOGPROC,75,121,151,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Dialog control ID:",IDC_STATIC,7,132,61,8 - LTEXT "Static",IDC_CTRLID,77,132,149,8 -END - -IDD_WNDSTYLES DIALOGEX 0, 0, 233, 140 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Styles" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Styles:",IDC_STATIC,7,7,23,8 - LTEXT "Static",IDC_STYLES,67,7,159,8 - LISTBOX IDC_STYLESLIST,7,19,219,46,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - LTEXT "Extended styles:",IDC_STATIC,7,69,56,8 - LTEXT "Static",IDC_EXTENDEDSTYLES,67,69,159,8 - LISTBOX IDC_EXTENDEDSTYLESLIST,7,81,219,46,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP -END - -IDD_WNDCLASS DIALOGEX 0, 0, 233, 132 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Class" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Name:",IDC_STATIC,7,8,22,8 - EDITTEXT IDC_NAME,33,7,193,12,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Atom:",IDC_STATIC,7,22,20,8 - LTEXT "Static",IDC_ATOM,77,22,149,8,SS_ENDELLIPSIS - LTEXT "Styles:",IDC_STATIC,7,33,23,8 - EDITTEXT IDC_STYLES,75,33,150,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Instance handle:",IDC_STATIC,7,44,56,8 - LTEXT "Static",IDC_INSTANCEHANDLE,77,44,149,8,SS_ENDELLIPSIS - LTEXT "Icon handle:",IDC_STATIC,7,55,42,8 - LTEXT "Static",IDC_ICONHANDLE,77,55,149,8,SS_ENDELLIPSIS - LTEXT "Small icon handle:",IDC_STATIC,7,66,60,8 - LTEXT "Static",IDC_SMALLICONHANDLE,77,66,149,8,SS_ENDELLIPSIS - LTEXT "Cursor handle:",IDC_STATIC,7,77,49,8 - LTEXT "Static",IDC_CURSORHANDLE,77,77,149,8,SS_ENDELLIPSIS - LTEXT "Background brush:",IDC_STATIC,7,88,61,8 - LTEXT "Static",IDC_BACKGROUNDBRUSH,77,88,149,8,SS_ENDELLIPSIS - LTEXT "Menu name:",IDC_STATIC,7,99,41,8 - LTEXT "Static",IDC_MENUNAME,77,99,149,8,SS_ENDELLIPSIS - LTEXT "Window proc:",IDC_STATIC,7,110,45,8 - EDITTEXT IDC_WINDOWPROC,75,110,151,14,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_WNDLIST, DIALOG - BEGIN - LEFTMARGIN, 2 - RIGHTMARGIN, 445 - TOPMARGIN, 3 - BOTTOMMARGIN, 307 - END - - IDD_WNDPROPS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 226 - TOPMARGIN, 7 - BOTTOMMARGIN, 145 - END - - IDD_WNDGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 226 - TOPMARGIN, 7 - BOTTOMMARGIN, 140 - END - - IDD_WNDSTYLES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 226 - TOPMARGIN, 7 - BOTTOMMARGIN, 133 - END - - IDD_WNDCLASS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 226 - TOPMARGIN, 7 - BOTTOMMARGIN, 125 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_WINDOW MENU -BEGIN - POPUP "Window" - BEGIN - MENUITEM "Bring to front", ID_WINDOW_BRINGTOFRONT - MENUITEM "Restore", ID_WINDOW_RESTORE - MENUITEM "Minimize", ID_WINDOW_MINIMIZE - MENUITEM "Maximize", ID_WINDOW_MAXIMIZE - MENUITEM "Close", ID_WINDOW_CLOSE - MENUITEM SEPARATOR - MENUITEM "Visible", ID_WINDOW_VISIBLE - MENUITEM "Enabled", ID_WINDOW_ENABLED - MENUITEM "Always on top", ID_WINDOW_ALWAYSONTOP - POPUP "Opacity" - BEGIN - MENUITEM "10%", ID_OPACITY_10 - MENUITEM "20%", ID_OPACITY_20 - MENUITEM "30%", ID_OPACITY_30 - MENUITEM "40%", ID_OPACITY_40 - MENUITEM "50%", ID_OPACITY_50 - MENUITEM "60%", ID_OPACITY_60 - MENUITEM "70%", ID_OPACITY_70 - MENUITEM "80%", ID_OPACITY_80 - MENUITEM "90%", ID_OPACITY_90 - MENUITEM "Opaque", ID_OPACITY_OPAQUE - END - MENUITEM SEPARATOR - MENUITEM "Highlight", ID_WINDOW_HIGHLIGHT - MENUITEM "Go to thread", ID_WINDOW_GOTOTHREAD - MENUITEM "Properties", ID_WINDOW_PROPERTIES - MENUITEM SEPARATOR - MENUITEM "Copy\aCtrl+C", ID_WINDOW_COPY - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_WNDGENERAL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_WNDLIST AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_WNDCLASS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// 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 + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,6,0,0 + PRODUCTVERSION 1,6,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "wj32" + VALUE "FileDescription", "Window Explorer plugin for Process Hacker" + VALUE "FileVersion", "1.6" + VALUE "InternalName", "ProcessHacker.WindowExplorer" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." + VALUE "OriginalFilename", "WindowExplorer.dll" + VALUE "ProductName", "Window Explorer plugin for Process Hacker" + VALUE "ProductVersion", "1.6" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_WNDLIST DIALOGEX 0, 0, 447, 309 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Windows" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Refresh",IDC_REFRESH,2,3,50,14 + CONTROL "Windows",IDC_LIST,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0x2,2,19,443,288,WS_EX_CLIENTEDGE + EDITTEXT IDC_SEARCHEDIT,302,3,143,14,ES_AUTOHSCROLL +END + +IDD_WNDPROPSTORAGE DIALOGEX 0, 0, 271, 224 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Property Storage" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_TABSTOP,0,0,271,224 +END + +IDD_WNDGENERAL DIALOGEX 0, 0, 271, 224 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_WINDOWINFO,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,0,0,271,224 +END + +IDD_WNDPROPLIST DIALOGEX 0, 0, 271, 224 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Properties" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_TABSTOP,0,0,271,224 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_WNDLIST, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 445 + TOPMARGIN, 3 + BOTTOMMARGIN, 307 + END + + IDD_WNDPROPSTORAGE, DIALOG + BEGIN + BOTTOMMARGIN, 218 + END + + IDD_WNDGENERAL, DIALOG + BEGIN + RIGHTMARGIN, 233 + BOTTOMMARGIN, 147 + END + + IDD_WNDPROPLIST, DIALOG + BEGIN + BOTTOMMARGIN, 218 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_WINDOW MENU +BEGIN + POPUP "Window" + BEGIN + MENUITEM "Bring to front", ID_WINDOW_BRINGTOFRONT + MENUITEM "Restore", ID_WINDOW_RESTORE + MENUITEM "Minimize", ID_WINDOW_MINIMIZE + MENUITEM "Maximize", ID_WINDOW_MAXIMIZE + MENUITEM "Close", ID_WINDOW_CLOSE + MENUITEM SEPARATOR + MENUITEM "Visible", ID_WINDOW_VISIBLE + MENUITEM "Enabled", ID_WINDOW_ENABLED + MENUITEM "Always on top", ID_WINDOW_ALWAYSONTOP + POPUP "Opacity" + BEGIN + MENUITEM "10%", ID_OPACITY_10 + MENUITEM "20%", ID_OPACITY_20 + MENUITEM "30%", ID_OPACITY_30 + MENUITEM "40%", ID_OPACITY_40 + MENUITEM "50%", ID_OPACITY_50 + MENUITEM "60%", ID_OPACITY_60 + MENUITEM "70%", ID_OPACITY_70 + MENUITEM "80%", ID_OPACITY_80 + MENUITEM "90%", ID_OPACITY_90 + MENUITEM "Opaque", ID_OPACITY_OPAQUE + END + MENUITEM SEPARATOR + MENUITEM "Highlight", ID_WINDOW_HIGHLIGHT + MENUITEM "Go to thread", ID_WINDOW_GOTOTHREAD + MENUITEM "Properties", ID_WINDOW_PROPERTIES + MENUITEM SEPARATOR + MENUITEM "Copy\aCtrl+C", ID_WINDOW_COPY + END +END + + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/WindowExplorer/WindowExplorer.vcxproj b/plugins/WindowExplorer/WindowExplorer.vcxproj index bf5b3e58f4a0..467f05833956 100644 --- a/plugins/WindowExplorer/WindowExplorer.vcxproj +++ b/plugins/WindowExplorer/WindowExplorer.vcxproj @@ -1,94 +1,99 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {37488DC1-E45F-4626-A87C-3A854A153D1A} - WindowExplorer - Win32Proj - WindowExplorer - 10.0.15063.0 - - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - DynamicLibrary - Unicode - v141 - - - - - - - - - ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) - - - - - ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) - - - - - ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) - - - - - ProcessHacker.exe;comctl32.dll;%(DelayLoadDLLs) - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {37488DC1-E45F-4626-A87C-3A854A153D1A} + WindowExplorer + Win32Proj + WindowExplorer + 10.0 + + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + + + + + + + comctl32.dll;gdi32.dll;ole32.dll;ProcessHacker.exe;propsys.dll;shell32.dll;user32.dll;%(DelayLoadDLLs) + ProcessHacker.lib;ntdll.lib;propsys.lib;%(AdditionalDependencies) + + + + + comctl32.dll;gdi32.dll;ole32.dll;ProcessHacker.exe;propsys.dll;shell32.dll;user32.dll;%(DelayLoadDLLs) + ProcessHacker.lib;ntdll.lib;propsys.lib;%(AdditionalDependencies) + + + + + comctl32.dll;gdi32.dll;ole32.dll;ProcessHacker.exe;propsys.dll;shell32.dll;user32.dll;%(DelayLoadDLLs) + ProcessHacker.lib;ntdll.lib;propsys.lib;%(AdditionalDependencies) + + + + + comctl32.dll;gdi32.dll;ole32.dll;ProcessHacker.exe;propsys.dll;shell32.dll;user32.dll;%(DelayLoadDLLs) + ProcessHacker.lib;ntdll.lib;propsys.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/WindowExplorer/WindowExplorer.vcxproj.filters b/plugins/WindowExplorer/WindowExplorer.vcxproj.filters index ab33d0a02f9f..248506128b16 100644 --- a/plugins/WindowExplorer/WindowExplorer.vcxproj.filters +++ b/plugins/WindowExplorer/WindowExplorer.vcxproj.filters @@ -30,7 +30,7 @@ Source Files - + Source Files @@ -44,6 +44,9 @@ Header Files + + Header Files + diff --git a/plugins/WindowExplorer/hook.c b/plugins/WindowExplorer/hook.c deleted file mode 100644 index 75d6861b84aa..000000000000 --- a/plugins/WindowExplorer/hook.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Process Hacker Window Explorer - - * hook procedure - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -/* - * Window Explorer uses a hook procedure in order to get window procedure - * and other information that can't be retrieved using GetWindowLongPtr. - * Because WindowExplorer.dll needs to be loaded into processes other - * than Process Hacker, both ProcessHacker.exe and comctl32.dll are set as - * delay-loaded DLLs. The other DLLs that we depend on (gdi32.dll, - * kernel32.dll, ntdll.dll, user32.dll) are all guaranteed to be already - * loaded whenever WindowExplorer.dll needs to be loaded. - */ - -#include "wndexp.h" - -BOOLEAN WepCreateServerObjects( - VOID - ); - -BOOLEAN WepOpenServerObjects( - VOID - ); - -VOID WepCloseServerObjects( - VOID - ); - -VOID WepWriteClientData( - _In_ HWND hwnd - ); - -LRESULT CALLBACK WepCallWndProc( - _In_ int nCode, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -// Shared -ULONG WeServerMessage; -HANDLE WeServerSharedSection; -PWE_HOOK_SHARED_DATA WeServerSharedData; -HANDLE WeServerSharedSectionLock; -HANDLE WeServerSharedSectionEvent; -// Server -HHOOK WeHookHandle = NULL; -// The current message ID is used to detect out-of-sync clients. -ULONG WeCurrentMessageId = 0; - -// Server - -VOID WeHookServerInitialization( - VOID - ) -{ - if (WeHookHandle) - return; - - WeServerMessage = RegisterWindowMessage(WE_SERVER_MESSAGE_NAME); - - if (!WepCreateServerObjects()) - return; - - WeHookHandle = SetWindowsHookEx(WH_CALLWNDPROC, WepCallWndProc, PluginInstance->DllBase, 0); -} - -VOID WeHookServerUninitialization( - VOID - ) -{ - if (WeHookHandle) - { - UnhookWindowsHookEx(WeHookHandle); - WeHookHandle = NULL; - } -} - -BOOLEAN WepCreateServerObjects( - VOID - ) -{ - OBJECT_ATTRIBUTES objectAttributes; - WCHAR buffer[256]; - UNICODE_STRING objectName; - SECURITY_DESCRIPTOR securityDescriptor; - UCHAR saclBuffer[sizeof(ACL) + FIELD_OFFSET(SYSTEM_MANDATORY_LABEL_ACE, SidStart) + FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG)]; - PACL sacl; - UCHAR mandatoryLabelAceBuffer[FIELD_OFFSET(SYSTEM_MANDATORY_LABEL_ACE, SidStart) + FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG)]; - PSYSTEM_MANDATORY_LABEL_ACE mandatoryLabelAce; - PSID sid; - - if (!WeServerSharedSection) - { - LARGE_INTEGER maximumSize; - - WeFormatLocalObjectName(WE_SERVER_SHARED_SECTION_NAME, buffer, &objectName); - InitializeObjectAttributes(&objectAttributes, &objectName, OBJ_CASE_INSENSITIVE, NULL, NULL); - maximumSize.QuadPart = sizeof(WE_HOOK_SHARED_DATA); - - if (!NT_SUCCESS(NtCreateSection( - &WeServerSharedSection, - SECTION_ALL_ACCESS, - &objectAttributes, - &maximumSize, - PAGE_READWRITE, - SEC_COMMIT, - NULL - ))) - { - return FALSE; - } - } - - if (!WeServerSharedData) - { - PVOID viewBase; - SIZE_T viewSize; - - viewBase = NULL; - viewSize = sizeof(WE_HOOK_SHARED_DATA); - - if (!NT_SUCCESS(NtMapViewOfSection( - WeServerSharedSection, - NtCurrentProcess(), - &viewBase, - 0, - 0, - NULL, - &viewSize, - ViewShare, - 0, - PAGE_READWRITE - ))) - { - WepCloseServerObjects(); - return FALSE; - } - - WeServerSharedData = viewBase; - } - - if (!WeServerSharedSectionLock) - { - WeFormatLocalObjectName(WE_SERVER_SHARED_SECTION_LOCK_NAME, buffer, &objectName); - InitializeObjectAttributes(&objectAttributes, &objectName, OBJ_CASE_INSENSITIVE, NULL, NULL); - - if (!NT_SUCCESS(NtCreateMutant( - &WeServerSharedSectionLock, - MUTANT_ALL_ACCESS, - &objectAttributes, - FALSE - ))) - { - WepCloseServerObjects(); - return FALSE; - } - } - - if (!WeServerSharedSectionEvent) - { - WeFormatLocalObjectName(WE_SERVER_SHARED_SECTION_EVENT_NAME, buffer, &objectName); - InitializeObjectAttributes(&objectAttributes, &objectName, OBJ_CASE_INSENSITIVE, NULL, NULL); - - if (!NT_SUCCESS(NtCreateEvent( - &WeServerSharedSectionEvent, - EVENT_ALL_ACCESS, - &objectAttributes, - NotificationEvent, - FALSE - ))) - { - WepCloseServerObjects(); - return FALSE; - } - } - - // If mandatory labels are supported, set it to the lowest possible level. - if (WE_WindowsVersion >= WINDOWS_VISTA) - { - static SID_IDENTIFIER_AUTHORITY mandatoryLabelAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY; - - RtlCreateSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION); - - sacl = (PACL)saclBuffer; - RtlCreateAcl(sacl, sizeof(saclBuffer), ACL_REVISION); - - mandatoryLabelAce = (PSYSTEM_MANDATORY_LABEL_ACE)mandatoryLabelAceBuffer; - mandatoryLabelAce->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE; - mandatoryLabelAce->Header.AceFlags = 0; - mandatoryLabelAce->Header.AceSize = sizeof(mandatoryLabelAceBuffer); - mandatoryLabelAce->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP; - - sid = (PSID)&mandatoryLabelAce->SidStart; - RtlInitializeSid(sid, &mandatoryLabelAuthority, 1); - *RtlSubAuthoritySid(sid, 0) = SECURITY_MANDATORY_LOW_RID; - - if (NT_SUCCESS(RtlAddAce(sacl, ACL_REVISION, MAXULONG32, mandatoryLabelAce, sizeof(mandatoryLabelAceBuffer)))) - { - if (NT_SUCCESS(RtlSetSaclSecurityDescriptor(&securityDescriptor, TRUE, sacl, FALSE))) - { - NtSetSecurityObject(WeServerSharedSection, LABEL_SECURITY_INFORMATION, &securityDescriptor); - NtSetSecurityObject(WeServerSharedSectionLock, LABEL_SECURITY_INFORMATION, &securityDescriptor); - NtSetSecurityObject(WeServerSharedSectionEvent, LABEL_SECURITY_INFORMATION, &securityDescriptor); - } - } - } - - return TRUE; -} - -BOOLEAN WepOpenServerObjects( - VOID - ) -{ - OBJECT_ATTRIBUTES objectAttributes; - WCHAR buffer[256]; - UNICODE_STRING objectName; - - if (!WeServerSharedSection) - { - WeFormatLocalObjectName(WE_SERVER_SHARED_SECTION_NAME, buffer, &objectName); - InitializeObjectAttributes(&objectAttributes, &objectName, OBJ_CASE_INSENSITIVE, NULL, NULL); - - if (!NT_SUCCESS(NtOpenSection( - &WeServerSharedSection, - SECTION_ALL_ACCESS, - &objectAttributes - ))) - { - return FALSE; - } - } - - if (!WeServerSharedData) - { - PVOID viewBase; - SIZE_T viewSize; - - viewBase = NULL; - viewSize = sizeof(WE_HOOK_SHARED_DATA); - - if (!NT_SUCCESS(NtMapViewOfSection( - WeServerSharedSection, - NtCurrentProcess(), - &viewBase, - 0, - 0, - NULL, - &viewSize, - ViewShare, - 0, - PAGE_READWRITE - ))) - { - WepCloseServerObjects(); - return FALSE; - } - - WeServerSharedData = viewBase; - } - - if (!WeServerSharedSectionLock) - { - WeFormatLocalObjectName(WE_SERVER_SHARED_SECTION_LOCK_NAME, buffer, &objectName); - InitializeObjectAttributes(&objectAttributes, &objectName, OBJ_CASE_INSENSITIVE, NULL, NULL); - - if (!NT_SUCCESS(NtOpenMutant( - &WeServerSharedSectionLock, - MUTANT_ALL_ACCESS, - &objectAttributes - ))) - { - WepCloseServerObjects(); - return FALSE; - } - } - - if (!WeServerSharedSectionEvent) - { - WeFormatLocalObjectName(WE_SERVER_SHARED_SECTION_EVENT_NAME, buffer, &objectName); - InitializeObjectAttributes(&objectAttributes, &objectName, OBJ_CASE_INSENSITIVE, NULL, NULL); - - if (!NT_SUCCESS(NtOpenEvent( - &WeServerSharedSectionEvent, - EVENT_ALL_ACCESS, - &objectAttributes - ))) - { - WepCloseServerObjects(); - return FALSE; - } - } - - return TRUE; -} - -VOID WepCloseServerObjects( - VOID - ) -{ - if (WeServerSharedSection) - { - NtClose(WeServerSharedSection); - WeServerSharedSection = NULL; - } - - if (WeServerSharedData) - { - NtUnmapViewOfSection(NtCurrentProcess(), WeServerSharedData); - WeServerSharedData = NULL; - } - - if (WeServerSharedSectionLock) - { - NtClose(WeServerSharedSectionLock); - WeServerSharedSectionLock = NULL; - } - - if (WeServerSharedSectionEvent) - { - NtClose(WeServerSharedSectionEvent); - WeServerSharedSectionEvent = NULL; - } -} - -BOOLEAN WeIsServerActive( - VOID - ) -{ - if (WepOpenServerObjects()) - { - WepCloseServerObjects(); - - return TRUE; - } - else - { - return FALSE; - } -} - -BOOLEAN WeLockServerSharedData( - _Out_ PWE_HOOK_SHARED_DATA *Data - ) -{ - LARGE_INTEGER timeout; - - if (!WeServerSharedSectionLock) - return FALSE; - - timeout.QuadPart = -WE_CLIENT_MESSAGE_TIMEOUT * PH_TIMEOUT_MS; - - if (NtWaitForSingleObject(WeServerSharedSectionLock, FALSE, &timeout) != WAIT_OBJECT_0) - return FALSE; - - *Data = WeServerSharedData; - - return TRUE; -} - -VOID WeUnlockServerSharedData( - VOID - ) -{ - NtReleaseMutant(WeServerSharedSectionLock, NULL); -} - -BOOLEAN WeSendServerRequest( - _In_ HWND hWnd - ) -{ - ULONG threadId; - ULONG processId; - LARGE_INTEGER timeout; - - if (!WeServerSharedData || !WeServerSharedSectionEvent) - return FALSE; - - threadId = GetWindowThreadProcessId(hWnd, &processId); - - if (UlongToHandle(processId) == NtCurrentProcessId()) - { - // We are trying to get information about the server. Call the procedure directly. - WepWriteClientData(hWnd); - return TRUE; - } - - // Call the client and wait for the client to finish. - - WeCurrentMessageId++; - WeServerSharedData->MessageId = WeCurrentMessageId; - NtResetEvent(WeServerSharedSectionEvent, NULL); - - if (!SendNotifyMessage(hWnd, WeServerMessage, (WPARAM)NtCurrentProcessId(), WeCurrentMessageId)) - return FALSE; - - timeout.QuadPart = -WE_CLIENT_MESSAGE_TIMEOUT * PH_TIMEOUT_MS; - - if (NtWaitForSingleObject(WeServerSharedSectionEvent, FALSE, &timeout) != STATUS_WAIT_0) - return FALSE; - - return TRUE; -} - -// Client - -VOID WeHookClientInitialization( - VOID - ) -{ - WeServerMessage = RegisterWindowMessage(WE_SERVER_MESSAGE_NAME); -} - -VOID WeHookClientUninitialization( - VOID - ) -{ - NOTHING; -} - -VOID WepWriteClientData( - _In_ HWND hwnd - ) -{ - WCHAR className[256]; - LOGICAL isUnicode; - - memset(&WeServerSharedData->c, 0, sizeof(WeServerSharedData->c)); - isUnicode = IsWindowUnicode(hwnd); - - if (isUnicode) - { - WeServerSharedData->c.WndProc = GetWindowLongPtrW(hwnd, GWLP_WNDPROC); - WeServerSharedData->c.DlgProc = GetWindowLongPtrW(hwnd, DWLP_DLGPROC); - } - else - { - WeServerSharedData->c.WndProc = GetWindowLongPtrA(hwnd, GWLP_WNDPROC); - WeServerSharedData->c.DlgProc = GetWindowLongPtrA(hwnd, DWLP_DLGPROC); - } - - if (!GetClassName(hwnd, className, sizeof(className) / sizeof(WCHAR))) - className[0] = 0; - - WeServerSharedData->c.ClassInfo.cbSize = sizeof(WNDCLASSEX); - GetClassInfoEx(NULL, className, &WeServerSharedData->c.ClassInfo); - - if (isUnicode) - WeServerSharedData->c.ClassInfo.lpfnWndProc = (PVOID)GetClassLongPtrW(hwnd, GCLP_WNDPROC); - else - WeServerSharedData->c.ClassInfo.lpfnWndProc = (PVOID)GetClassLongPtrA(hwnd, GCLP_WNDPROC); -} - -LRESULT CALLBACK WepCallWndProc( - _In_ int nCode, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LRESULT result; - PCWPSTRUCT info; - - result = CallNextHookEx(NULL, nCode, wParam, lParam); - - info = (PCWPSTRUCT)lParam; - - if (info->message == WeServerMessage) - { - HANDLE serverProcessId; - ULONG messageId; - - serverProcessId = (HANDLE)info->wParam; - messageId = (ULONG)info->lParam; - - if (serverProcessId != NtCurrentProcessId()) - { - if (WepOpenServerObjects()) - { - if (WeServerSharedData->MessageId == messageId) - { - WepWriteClientData(info->hwnd); - NtSetEvent(WeServerSharedSectionEvent, NULL); - } - - WepCloseServerObjects(); - } - } - } - - return result; -} diff --git a/plugins/WindowExplorer/main.c b/plugins/WindowExplorer/main.c index db51c855bcb5..e90c8652e3cd 100644 --- a/plugins/WindowExplorer/main.c +++ b/plugins/WindowExplorer/main.c @@ -1,325 +1,281 @@ -/* - * Process Hacker Window Explorer - - * main program - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "wndexp.h" -#include "resource.h" - -BOOLEAN IsHookClient; -PPH_PLUGIN PluginInstance; -PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; -PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; -PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; -PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; - - -VOID NTAPI LoadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -VOID NTAPI UnloadCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - WeHookServerUninitialization(); -} - -VOID NTAPI ShowOptionsCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - NOTHING; -} - -BOOL CALLBACK WepEnumDesktopProc( - _In_ LPTSTR lpszDesktop, - _In_ LPARAM lParam - ) -{ - PhAddItemList((PPH_LIST)lParam, PhaCreateString(lpszDesktop)->Buffer); - - return TRUE; -} - -VOID NTAPI MenuItemCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_ITEM menuItem = Parameter; - - switch (menuItem->Id) - { - case ID_VIEW_WINDOWS: - { - WE_WINDOW_SELECTOR selector; - - selector.Type = WeWindowSelectorAll; - WeShowWindowsDialog(WE_PhMainWndHandle, &selector); - } - break; - case ID_VIEW_DESKTOPWINDOWS: - { - PPH_LIST desktopNames; - PPH_STRING selectedChoice = NULL; - - desktopNames = PH_AUTO(PhCreateList(4)); - EnumDesktops(GetProcessWindowStation(), WepEnumDesktopProc, (LPARAM)desktopNames); - - if (PhaChoiceDialog( - WE_PhMainWndHandle, - L"Desktop Windows", - L"Display windows for the following desktop:", - (PWSTR *)desktopNames->Items, - desktopNames->Count, - NULL, - PH_CHOICE_DIALOG_CHOICE, - &selectedChoice, - NULL, - NULL - )) - { - WE_WINDOW_SELECTOR selector; - - selector.Type = WeWindowSelectorDesktop; - PhSetReference(&selector.Desktop.DesktopName, selectedChoice); - WeShowWindowsDialog(WE_PhMainWndHandle, &selector); - } - } - break; - case ID_PROCESS_WINDOWS: - { - WE_WINDOW_SELECTOR selector; - - selector.Type = WeWindowSelectorProcess; - selector.Process.ProcessId = ((PPH_PROCESS_ITEM)menuItem->Context)->ProcessId; - WeShowWindowsDialog(WE_PhMainWndHandle, &selector); - } - break; - case ID_THREAD_WINDOWS: - { - WE_WINDOW_SELECTOR selector; - - selector.Type = WeWindowSelectorThread; - selector.Thread.ThreadId = ((PPH_THREAD_ITEM)menuItem->Context)->ThreadId; - WeShowWindowsDialog(WE_PhMainWndHandle, &selector); - } - break; - } -} - -VOID NTAPI MainMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - ULONG insertIndex; - PPH_EMENU_ITEM menuItem; - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - - if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_VIEW) - return; - - if (menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"System Information", 0)) - insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; - else - insertIndex = 0; - - PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_VIEW_WINDOWS, L"Windows", NULL), insertIndex); - - if (PhGetIntegerSetting(SETTING_NAME_SHOW_DESKTOP_WINDOWS)) - { - insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; - - PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, ID_VIEW_DESKTOPWINDOWS, L"Desktop Windows...", NULL), insertIndex); - } -} - -VOID NTAPI ProcessPropertiesInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; - BOOLEAN isGuiProcess = TRUE; - - // enum threads, IsGuiThread, isGuiProcess=TRUE - - if (isGuiProcess) - { - WE_WINDOW_SELECTOR selector; - - selector.Type = WeWindowSelectorProcess; - selector.Process.ProcessId = propContext->ProcessItem->ProcessId; - WeShowWindowsPropPage(propContext, &selector); - } -} - -VOID NTAPI ThreadMenuInitializingCallback( - _In_opt_ PVOID Parameter, - _In_opt_ PVOID Context - ) -{ - PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; - PPH_THREAD_ITEM threadItem; - ULONG insertIndex; - PPH_EMENU_ITEM menuItem; - - if (menuInfo->u.Thread.NumberOfThreads == 1) - threadItem = menuInfo->u.Thread.Threads[0]; - else - threadItem = NULL; - - if (menuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Token", 0)) - insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; - else - insertIndex = 0; - - PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_THREAD_WINDOWS, - L"Windows", threadItem), insertIndex); - - if (!threadItem) menuItem->Flags |= PH_EMENU_DISABLED; -} - -LOGICAL DllMain( - _In_ HINSTANCE Instance, - _In_ ULONG Reason, - _Reserved_ PVOID Reserved - ) -{ - switch (Reason) - { - case DLL_PROCESS_ATTACH: - { - BOOLEAN isClient; - PPH_PLUGIN_INFORMATION info; - PH_SETTING_CREATE settings[] = - { - { IntegerSettingType, SETTING_NAME_SHOW_DESKTOP_WINDOWS, L"1" }, - { StringSettingType, SETTING_NAME_WINDOW_TREE_LIST_COLUMNS, L"" }, - { IntegerPairSettingType, SETTING_NAME_WINDOWS_WINDOW_POSITION, L"100,100" }, - { ScalableIntegerPairSettingType, SETTING_NAME_WINDOWS_WINDOW_SIZE, L"@96|690,540" } - }; - - isClient = FALSE; - - if (!GetModuleHandle(L"ProcessHacker.exe") || !WeGetProcedureAddress("PhLibImageBase")) - { - isClient = TRUE; - } - else - { - // WindowExplorer appears to be loading within Process Hacker. However, if there is - // already a server instance, the the hook will be active, and our DllMain routine - // will most likely be called before the plugin system is even initialized. Attempting - // to register a plugin would result in an access violation, so load as a client for now. - if (WeIsServerActive()) - isClient = TRUE; - } - - if (isClient) - { - // This DLL is being loaded not as a Process Hacker plugin, but as a hook. - IsHookClient = TRUE; - WeHookClientInitialization(); - - break; - } - - PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); - - if (!PluginInstance) - return FALSE; - - info->DisplayName = L"Window Explorer"; - info->Author = L"wj32"; - info->Description = L"View and manipulate windows."; - info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1116"; - info->HasOptions = FALSE; - - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackLoad), - LoadCallback, - NULL, - &PluginLoadCallbackRegistration - ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackUnload), - UnloadCallback, - NULL, - &PluginUnloadCallbackRegistration - ); - //PhRegisterCallback( - // PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), - // ShowOptionsCallback, - // NULL, - // &PluginShowOptionsCallbackRegistration - // ); - PhRegisterCallback( - PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), - MenuItemCallback, - NULL, - &PluginMenuItemCallbackRegistration - ); - - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), - MainMenuInitializingCallback, - NULL, - &MainMenuInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), - ProcessPropertiesInitializingCallback, - NULL, - &ProcessPropertiesInitializingCallbackRegistration - ); - PhRegisterCallback( - PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing), - ThreadMenuInitializingCallback, - NULL, - &ThreadMenuInitializingCallbackRegistration - ); - - PhAddSettings(settings, ARRAYSIZE(settings)); - } - break; - case DLL_PROCESS_DETACH: - { - if (IsHookClient) - { - WeHookClientUninitialization(); - } - } - break; - } - - return TRUE; -} +/* + * Process Hacker Window Explorer - + * main program + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "wndexp.h" +#include "resource.h" + +BOOLEAN IsHookClient; +PPH_PLUGIN PluginInstance; +PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginUnloadCallbackRegistration; +PH_CALLBACK_REGISTRATION PluginMenuItemCallbackRegistration; +PH_CALLBACK_REGISTRATION MainMenuInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; +PH_CALLBACK_REGISTRATION ThreadMenuInitializingCallbackRegistration; + +VOID NTAPI LoadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +VOID NTAPI UnloadCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + NOTHING; +} + +BOOL CALLBACK WepEnumDesktopProc( + _In_ LPTSTR lpszDesktop, + _In_ LPARAM lParam + ) +{ + PhAddItemList((PPH_LIST)lParam, PhaCreateString(lpszDesktop)->Buffer); + + return TRUE; +} + +VOID NTAPI MenuItemCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_ITEM menuItem = Parameter; + + switch (menuItem->Id) + { + case ID_VIEW_WINDOWS: + { + WE_WINDOW_SELECTOR selector; + + selector.Type = WeWindowSelectorAll; + WeShowWindowsDialog(WE_PhMainWndHandle, &selector); + } + break; + case ID_VIEW_DESKTOPWINDOWS: + { + PPH_LIST desktopNames; + PPH_STRING selectedChoice = NULL; + + desktopNames = PH_AUTO(PhCreateList(4)); + EnumDesktops(GetProcessWindowStation(), WepEnumDesktopProc, (LPARAM)desktopNames); + + if (PhaChoiceDialog( + WE_PhMainWndHandle, + L"Desktop Windows", + L"Display windows for the following desktop:", + (PWSTR *)desktopNames->Items, + desktopNames->Count, + NULL, + PH_CHOICE_DIALOG_CHOICE, + &selectedChoice, + NULL, + NULL + )) + { + WE_WINDOW_SELECTOR selector; + + selector.Type = WeWindowSelectorDesktop; + PhSetReference(&selector.Desktop.DesktopName, selectedChoice); + WeShowWindowsDialog(WE_PhMainWndHandle, &selector); + } + } + break; + case ID_PROCESS_WINDOWS: + { + WE_WINDOW_SELECTOR selector; + + selector.Type = WeWindowSelectorProcess; + selector.Process.ProcessId = ((PPH_PROCESS_ITEM)menuItem->Context)->ProcessId; + WeShowWindowsDialog(WE_PhMainWndHandle, &selector); + } + break; + case ID_THREAD_WINDOWS: + { + WE_WINDOW_SELECTOR selector; + + selector.Type = WeWindowSelectorThread; + selector.Thread.ThreadId = ((PPH_THREAD_ITEM)menuItem->Context)->ThreadId; + WeShowWindowsDialog(WE_PhMainWndHandle, &selector); + } + break; + } +} + +VOID NTAPI MainMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + ULONG insertIndex; + PPH_EMENU_ITEM menuItem; + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + + if (menuInfo->u.MainMenu.SubMenuIndex != PH_MENU_ITEM_LOCATION_VIEW) + return; + + if (menuItem = PhFindEMenuItem(menuInfo->Menu, PH_EMENU_FIND_STARTSWITH, L"System Information", 0)) + insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; + else + insertIndex = 0; + + PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_VIEW_WINDOWS, L"&Windows", NULL), insertIndex); + + if (PhGetIntegerSetting(SETTING_NAME_SHOW_DESKTOP_WINDOWS)) + { + insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; + + PhInsertEMenuItem(menuInfo->Menu, PhPluginCreateEMenuItem(PluginInstance, 0, ID_VIEW_DESKTOPWINDOWS, L"Deskto&p Windows...", NULL), insertIndex); + } +} + +VOID NTAPI ProcessPropertiesInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; + + if ( + propContext->ProcessItem->ProcessId != SYSTEM_IDLE_PROCESS_ID && + propContext->ProcessItem->ProcessId != SYSTEM_PROCESS_ID + ) + { + WE_WINDOW_SELECTOR selector; + + selector.Type = WeWindowSelectorProcess; + selector.Process.ProcessId = propContext->ProcessItem->ProcessId; + WeShowWindowsPropPage(propContext, &selector); + } +} + +VOID NTAPI ThreadMenuInitializingCallback( + _In_opt_ PVOID Parameter, + _In_opt_ PVOID Context + ) +{ + PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter; + PPH_THREAD_ITEM threadItem; + ULONG insertIndex; + PPH_EMENU_ITEM menuItem; + + if (menuInfo->u.Thread.NumberOfThreads == 1) + threadItem = menuInfo->u.Thread.Threads[0]; + else + threadItem = NULL; + + if (menuItem = PhFindEMenuItem(menuInfo->Menu, 0, L"Token", 0)) + insertIndex = PhIndexOfEMenuItem(menuInfo->Menu, menuItem) + 1; + else + insertIndex = 0; + + PhInsertEMenuItem(menuInfo->Menu, menuItem = PhPluginCreateEMenuItem(PluginInstance, 0, ID_THREAD_WINDOWS, + L"&Windows", threadItem), insertIndex); + + if (!threadItem) menuItem->Flags |= PH_EMENU_DISABLED; +} + +LOGICAL DllMain( + _In_ HINSTANCE Instance, + _In_ ULONG Reason, + _Reserved_ PVOID Reserved + ) +{ + switch (Reason) + { + case DLL_PROCESS_ATTACH: + { + PPH_PLUGIN_INFORMATION info; + PH_SETTING_CREATE settings[] = + { + { IntegerSettingType, SETTING_NAME_SHOW_DESKTOP_WINDOWS, L"0" }, + { StringSettingType, SETTING_NAME_WINDOW_TREE_LIST_COLUMNS, L"" }, + { IntegerPairSettingType, SETTING_NAME_WINDOWS_WINDOW_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_WINDOWS_WINDOW_SIZE, L"@96|690,540" }, + { StringSettingType, SETTING_NAME_WINDOWS_PROPERTY_COLUMNS, L"" }, + { IntegerPairSettingType, SETTING_NAME_WINDOWS_PROPERTY_POSITION, L"0,0" }, + { ScalableIntegerPairSettingType, SETTING_NAME_WINDOWS_PROPERTY_SIZE, L"@96|690,540" }, + { StringSettingType, SETTING_NAME_WINDOWS_PROPLIST_COLUMNS, L"" }, + { StringSettingType, SETTING_NAME_WINDOWS_PROPSTORAGE_COLUMNS, L"" }, + }; + + PluginInstance = PhRegisterPlugin(PLUGIN_NAME, Instance, &info); + + if (!PluginInstance) + return FALSE; + + info->DisplayName = L"Window Explorer"; + info->Author = L"dmex, wj32"; + info->Description = L"View and manipulate windows."; + info->Url = L"https://wj32.org/processhacker/forums/viewtopic.php?t=1116"; + info->HasOptions = FALSE; + + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackLoad), + LoadCallback, + NULL, + &PluginLoadCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackUnload), + UnloadCallback, + NULL, + &PluginUnloadCallbackRegistration + ); + PhRegisterCallback( + PhGetPluginCallback(PluginInstance, PluginCallbackMenuItem), + MenuItemCallback, + NULL, + &PluginMenuItemCallbackRegistration + ); + + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackMainMenuInitializing), + MainMenuInitializingCallback, + NULL, + &MainMenuInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), + ProcessPropertiesInitializingCallback, + NULL, + &ProcessPropertiesInitializingCallbackRegistration + ); + PhRegisterCallback( + PhGetGeneralCallback(GeneralCallbackThreadMenuInitializing), + ThreadMenuInitializingCallback, + NULL, + &ThreadMenuInitializingCallbackRegistration + ); + + PhAddSettings(settings, RTL_NUMBER_OF(settings)); + } + break; + } + + return TRUE; +} diff --git a/plugins/WindowExplorer/prpsh.c b/plugins/WindowExplorer/prpsh.c new file mode 100644 index 000000000000..5e972623f6cd --- /dev/null +++ b/plugins/WindowExplorer/prpsh.c @@ -0,0 +1,443 @@ +/* + * Process Hacker - + * property sheet + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "wndexp.h" + +BOOLEAN HdPropContextInit = FALSE; +PPH_OBJECT_TYPE PvpPropContextType = NULL; +PPH_OBJECT_TYPE PvpPropPageContextType = NULL; +static RECT MinimumSize = { -1, -1, -1, -1 }; + +VOID NTAPI PvpPropContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +VOID NTAPI PvpPropPageContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ); + +INT CALLBACK PvpPropSheetProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam + ); + +LRESULT CALLBACK PvpPropSheetWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT CALLBACK PvpStandardPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +PPV_PROPCONTEXT HdCreatePropContext( + _In_ PWSTR Caption + ) +{ + PPV_PROPCONTEXT propContext; + PROPSHEETHEADER propSheetHeader; + + if (!PvpPropContextType) + PvpPropContextType = PhCreateObjectType(L"HdPropContext2", 0, PvpPropContextDeleteProcedure); + + propContext = PhCreateObject(sizeof(PV_PROPCONTEXT), PvpPropContextType); + memset(propContext, 0, sizeof(PV_PROPCONTEXT)); + + propContext->PropSheetPages = PhAllocate(sizeof(HPROPSHEETPAGE) * PV_PROPCONTEXT_MAXPAGES); + memset(propContext->PropSheetPages, 0, sizeof(HPROPSHEETPAGE) * PV_PROPCONTEXT_MAXPAGES); + + memset(&propSheetHeader, 0, sizeof(PROPSHEETHEADER)); + propSheetHeader.dwSize = sizeof(PROPSHEETHEADER); + propSheetHeader.dwFlags = + PSH_MODELESS | + PSH_NOAPPLYNOW | + PSH_NOCONTEXTHELP | + PSH_PROPTITLE | + PSH_USECALLBACK; + propSheetHeader.hInstance = PluginInstance->DllBase; + propSheetHeader.hwndParent = NULL; + propSheetHeader.pszCaption = Caption; + propSheetHeader.pfnCallback = PvpPropSheetProc; + propSheetHeader.nPages = 0; + propSheetHeader.nStartPage = 0; + propSheetHeader.phpage = propContext->PropSheetPages; + + memcpy(&propContext->PropSheetHeader, &propSheetHeader, sizeof(PROPSHEETHEADER)); + + return propContext; +} + +VOID NTAPI PvpPropContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPV_PROPCONTEXT propContext = (PPV_PROPCONTEXT)Object; + + PhFree(propContext->PropSheetPages); +} + +INT CALLBACK PvpPropSheetProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam + ) +{ +#define PROPSHEET_ADD_STYLE (WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME); + + switch (uMsg) + { + case PSCB_PRECREATE: + { + if (lParam) + { + if (((DLGTEMPLATEEX *)lParam)->signature == USHRT_MAX) + { + ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; + } + else + { + ((DLGTEMPLATE *)lParam)->style |= PROPSHEET_ADD_STYLE; + } + } + } + break; + case PSCB_INITIALIZED: + { + PPV_PROPSHEETCONTEXT context; + + context = PhAllocate(sizeof(PV_PROPSHEETCONTEXT)); + memset(context, 0, sizeof(PV_PROPSHEETCONTEXT)); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, ULONG_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PvpPropSheetWndProc); + + if (MinimumSize.left == -1) + { + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = 271; + rect.bottom = 224; + MapDialogRect(hwndDlg, &rect); + MinimumSize = rect; + MinimumSize.left = 0; + } + } + break; + } + + return 0; +} + +PPV_PROPSHEETCONTEXT PvpGetPropSheetContext( + _In_ HWND hwnd + ) +{ + return PhGetWindowContext(hwnd, ULONG_MAX); +} + +LRESULT CALLBACK PvpPropSheetWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PPV_PROPSHEETCONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hWnd, ULONG_MAX))) + return 0; + + oldWndProc = context->DefaultWindowProc; + + switch (uMsg) + { + case WM_DESTROY: + { + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, ULONG_MAX); + + PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOWS_PROPERTY_POSITION, SETTING_NAME_WINDOWS_PROPERTY_SIZE, hWnd); + PhDeleteLayoutManager(&context->LayoutManager); + + PhFree(context); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDOK: + // Prevent the OK button from working (even though + // it's already hidden). This prevents the Enter + // key from closing the dialog box. + return 0; + } + } + break; + case WM_SIZE: + { + if (!IsIconic(hWnd)) + { + PhLayoutManagerLayout(&context->LayoutManager); + } + } + break; + case WM_SIZING: + { + PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); + } + break; + } + + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); +} + +BOOLEAN PhpInitializePropSheetLayoutStage1( + _In_ PPV_PROPSHEETCONTEXT PropSheetContext, + _In_ HWND hwnd + ) +{ + if (!PropSheetContext->LayoutInitialized) + { + HWND tabControlHandle; + PPH_LAYOUT_ITEM tabControlItem; + PPH_LAYOUT_ITEM tabPageItem; + + tabControlHandle = PropSheet_GetTabControl(hwnd); + tabControlItem = PhAddLayoutItem(&PropSheetContext->LayoutManager, tabControlHandle, + NULL, PH_ANCHOR_ALL | PH_LAYOUT_IMMEDIATE_RESIZE); + tabPageItem = PhAddLayoutItem(&PropSheetContext->LayoutManager, tabControlHandle, + NULL, PH_LAYOUT_TAB_CONTROL); // dummy item to fix multiline tab control + + PropSheetContext->TabPageItem = tabPageItem; + + PhAddLayoutItem(&PropSheetContext->LayoutManager, GetDlgItem(hwnd, IDCANCEL), + NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + + // Hide the OK button. + ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE); + // Set the Cancel button's text to "Close". + PhSetDialogItemText(hwnd, IDCANCEL, L"Close"); + + if (PhGetIntegerPairSetting(SETTING_NAME_WINDOWS_PROPERTY_POSITION).X != 0) // HACK + PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOWS_PROPERTY_POSITION, SETTING_NAME_WINDOWS_PROPERTY_SIZE, hwnd); + + PropSheetContext->LayoutInitialized = TRUE; + + return TRUE; + } + + return FALSE; +} + +BOOLEAN PvAddPropPage( + _Inout_ PPV_PROPCONTEXT PropContext, + _In_ _Assume_refs_(1) PPV_PROPPAGECONTEXT PropPageContext + ) +{ + HPROPSHEETPAGE propSheetPageHandle; + + if (PropContext->PropSheetHeader.nPages == PV_PROPCONTEXT_MAXPAGES) + return FALSE; + + propSheetPageHandle = CreatePropertySheetPage( + &PropPageContext->PropSheetPage + ); + // CreatePropertySheetPage would have sent PSPCB_ADDREF, + // which would have added a reference. + PhDereferenceObject(PropPageContext); + + PropPageContext->PropContext = PropContext; + PhReferenceObject(PropContext); + + PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = + propSheetPageHandle; + PropContext->PropSheetHeader.nPages++; + + return TRUE; +} + +BOOLEAN PvAddPropPage2( + _Inout_ PPV_PROPCONTEXT PropContext, + _In_ HPROPSHEETPAGE PropSheetPageHandle + ) +{ + if (PropContext->PropSheetHeader.nPages == PV_PROPCONTEXT_MAXPAGES) + return FALSE; + + PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = PropSheetPageHandle; + PropContext->PropSheetHeader.nPages++; + + return TRUE; +} + +PPV_PROPPAGECONTEXT PvCreatePropPageContext( + _In_ LPCWSTR Template, + _In_ DLGPROC DlgProc, + _In_opt_ PVOID Context + ) +{ + return PvCreatePropPageContextEx(PluginInstance->DllBase, Template, DlgProc, Context); +} + +PPV_PROPPAGECONTEXT PvCreatePropPageContextEx( + _In_opt_ PVOID InstanceHandle, + _In_ LPCWSTR Template, + _In_ DLGPROC DlgProc, + _In_opt_ PVOID Context + ) +{ + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvpPropPageContextType) + PvpPropPageContextType = PhCreateObjectType(L"HdPropPageContext2", 0, PvpPropPageContextDeleteProcedure); + + propPageContext = PhCreateObject(sizeof(PV_PROPPAGECONTEXT), PvpPropPageContextType); + memset(propPageContext, 0, sizeof(PV_PROPPAGECONTEXT)); + + propPageContext->PropSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propPageContext->PropSheetPage.dwFlags = PSP_USECALLBACK; + propPageContext->PropSheetPage.hInstance = PluginInstance->DllBase; + propPageContext->PropSheetPage.pszTemplate = Template; + propPageContext->PropSheetPage.pfnDlgProc = DlgProc; + propPageContext->PropSheetPage.lParam = (LPARAM)propPageContext; + propPageContext->PropSheetPage.pfnCallback = PvpStandardPropPageProc; + + propPageContext->Context = Context; + + return propPageContext; +} + +VOID NTAPI PvpPropPageContextDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PPV_PROPPAGECONTEXT propPageContext = (PPV_PROPPAGECONTEXT)Object; + + if (propPageContext->PropContext) + PhDereferenceObject(propPageContext->PropContext); +} + +INT CALLBACK PvpStandardPropPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) +{ + PPV_PROPPAGECONTEXT propPageContext; + + propPageContext = (PPV_PROPPAGECONTEXT)ppsp->lParam; + + if (uMsg == PSPCB_ADDREF) + PhReferenceObject(propPageContext); + else if (uMsg == PSPCB_RELEASE) + PhDereferenceObject(propPageContext); + + return 1; +} + +PPH_LAYOUT_ITEM PvAddPropPageLayoutItem( + _In_ HWND hwnd, + _In_ HWND Handle, + _In_ PPH_LAYOUT_ITEM ParentItem, + _In_ ULONG Anchor + ) +{ + HWND parent; + PPV_PROPSHEETCONTEXT propSheetContext; + PPH_LAYOUT_MANAGER layoutManager; + PPH_LAYOUT_ITEM realParentItem; + PPH_LAYOUT_ITEM item; + + parent = GetParent(hwnd); + propSheetContext = PvpGetPropSheetContext(parent); + layoutManager = &propSheetContext->LayoutManager; + + PhpInitializePropSheetLayoutStage1(propSheetContext, parent); + + if (ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT) + realParentItem = ParentItem; + else + realParentItem = propSheetContext->TabPageItem; + + // Use the HACK if the control is a direct child of the dialog. + if (ParentItem && ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT && + // We detect if ParentItem is the layout item for the dialog + // by looking at its parent. + (ParentItem->ParentItem == &layoutManager->RootItem || + (ParentItem->ParentItem->Anchor & PH_LAYOUT_TAB_CONTROL))) + { + RECT dialogRect; + RECT dialogSize; + RECT margin; + + // MAKE SURE THESE NUMBERS ARE CORRECT. + dialogSize.right = 271; + dialogSize.bottom = 224; + MapDialogRect(hwnd, &dialogSize); + + // Get the original dialog rectangle. + GetWindowRect(hwnd, &dialogRect); + dialogRect.right = dialogRect.left + dialogSize.right; + dialogRect.bottom = dialogRect.top + dialogSize.bottom; + + // Calculate the margin from the original rectangle. + GetWindowRect(Handle, &margin); + margin = PhMapRect(margin, dialogRect); + PhConvertRect(&margin, &dialogRect); + + item = PhAddLayoutItemEx(layoutManager, Handle, realParentItem, Anchor, margin); + } + else + { + item = PhAddLayoutItem(layoutManager, Handle, realParentItem, Anchor); + } + + return item; +} + +VOID PvDoPropPageLayout( + _In_ HWND hwnd + ) +{ + HWND parent; + PPV_PROPSHEETCONTEXT propSheetContext; + + parent = GetParent(hwnd); + propSheetContext = PvpGetPropSheetContext(parent); + PhLayoutManagerLayout(&propSheetContext->LayoutManager); +} diff --git a/plugins/WindowExplorer/prpsh.h b/plugins/WindowExplorer/prpsh.h new file mode 100644 index 000000000000..f3f0e6d3e7b5 --- /dev/null +++ b/plugins/WindowExplorer/prpsh.h @@ -0,0 +1,132 @@ +/* + * Process Hacker - + * property sheet + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +// NOTE: Copied from processhacker2\ProcessHacker\procprp.h + +#ifndef PV_PRP_H +#define PV_PRP_H + +#define PV_PROPCONTEXT_MAXPAGES 20 + +typedef struct _PV_PROPSHEETCONTEXT +{ + BOOLEAN LayoutInitialized; + WNDPROC DefaultWindowProc; + PH_LAYOUT_MANAGER LayoutManager; + PPH_LAYOUT_ITEM TabPageItem; +} PV_PROPSHEETCONTEXT, *PPV_PROPSHEETCONTEXT; + +typedef struct _PV_PROPCONTEXT +{ + PROPSHEETHEADER PropSheetHeader; + HPROPSHEETPAGE *PropSheetPages; +} PV_PROPCONTEXT, *PPV_PROPCONTEXT; + +typedef struct _PV_PROPPAGECONTEXT +{ + PPV_PROPCONTEXT PropContext; + PVOID Context; + PROPSHEETPAGE PropSheetPage; + + BOOLEAN LayoutInitialized; +} PV_PROPPAGECONTEXT, *PPV_PROPPAGECONTEXT; + +VOID HdPropInitialization( + VOID + ); + +PPV_PROPCONTEXT HdCreatePropContext( + _In_ PWSTR Caption + ); + +BOOLEAN PvAddPropPage( + _Inout_ PPV_PROPCONTEXT PropContext, + _In_ _Assume_refs_(1) PPV_PROPPAGECONTEXT PropPageContext + ); + +BOOLEAN PvAddPropPage2( + _Inout_ PPV_PROPCONTEXT PropContext, + _In_ HPROPSHEETPAGE PropSheetPageHandle + ); + +PPV_PROPPAGECONTEXT PvCreatePropPageContext( + _In_ LPCWSTR Template, + _In_ DLGPROC DlgProc, + _In_opt_ PVOID Context + ); + +PPV_PROPPAGECONTEXT PvCreatePropPageContextEx( + _In_opt_ PVOID InstanceHandle, + _In_ LPCWSTR Template, + _In_ DLGPROC DlgProc, + _In_opt_ PVOID Context + ); + +BOOLEAN PvPropPageDlgProcHeader( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam, + _Out_ LPPROPSHEETPAGE *PropSheetPage, + _Out_ PPV_PROPPAGECONTEXT *PropPageContext + ); + +#define PH_PROP_PAGE_TAB_CONTROL_PARENT ((PPH_LAYOUT_ITEM)0x1) + +PPH_LAYOUT_ITEM PvAddPropPageLayoutItem( + _In_ HWND hwnd, + _In_ HWND Handle, + _In_ PPH_LAYOUT_ITEM ParentItem, + _In_ ULONG Anchor + ); + +VOID PvDoPropPageLayout( + _In_ HWND hwnd + ); + +FORCEINLINE BOOLEAN PvPropPageDlgProcHeader( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam, + _Out_ LPPROPSHEETPAGE *PropSheetPage, + _Out_ PPV_PROPPAGECONTEXT *PropPageContext + ) +{ + LPPROPSHEETPAGE propSheetPage; + + if (uMsg == WM_INITDIALOG) + { + // Save the context. + PhSetWindowContext(hwndDlg, ULONG_MAX, (HANDLE)lParam); + } + + propSheetPage = PhGetWindowContext(hwndDlg, ULONG_MAX); + + if (!propSheetPage) + return FALSE; + + *PropSheetPage = propSheetPage; + *PropPageContext = (PPV_PROPPAGECONTEXT)propSheetPage->lParam; + + return TRUE; +} + +#endif diff --git a/plugins/WindowExplorer/resource.h b/plugins/WindowExplorer/resource.h index 055357f62d44..4aff5e6f63cc 100644 --- a/plugins/WindowExplorer/resource.h +++ b/plugins/WindowExplorer/resource.h @@ -1,82 +1,51 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by WindowExplorer.rc -// -#define IDD_WNDPROPS 9 -#define IDD_WNDLIST 101 -#define ID_VIEW_WINDOWS 101 -#define ID_THREAD_WINDOWS 102 -#define ID_PROCESS_WINDOWS 103 -#define IDR_WINDOW 103 -#define ID_SHOWCONTEXTMENU 104 -#define IDD_WNDGENERAL 104 -#define ID_VIEW_DESKTOPWINDOWS 105 -#define IDD_WNDSTYLES 105 -#define IDD_WNDCLASS 106 -#define IDD_WNDGENERAL1 106 -#define IDC_LIST 1001 -#define IDC_REFRESH 1002 -#define IDC_TEXT 1009 -#define IDC_RECTANGLE 1010 -#define IDC_NORMALRECTANGLE 1011 -#define IDC_CLIENTRECTANGLE 1012 -#define IDC_THREAD 1016 -#define IDC_STYLESLIST 1017 -#define IDC_STYLES 1018 -#define IDC_EXTENDEDSTYLES 1019 -#define IDC_EXTENDEDSTYLESLIST 1020 -#define IDC_INSTANCEHANDLE 1021 -#define IDC_MENUHANDLE 1022 -#define IDC_WINDOWPROC 1023 -#define IDC_WINDOWPROC2 1024 -#define IDC_DIALOGPROC 1024 -#define IDC_USERDATA 1025 -#define IDC_NAME 1026 -#define IDC_ATOM 1028 -#define IDC_CURSORHANDLE 1029 -#define IDC_ICONHANDLE 1030 -#define IDC_ICONHANDLE2 1031 -#define IDC_SMALLICONHANDLE 1031 -#define IDC_BACKGROUNDBRUSH 1032 -#define IDC_MENUNAME 1033 -#define IDC_UNICODE 1034 -#define IDC_CTRLID 1035 -#define IDC_SEARCHEDIT 1035 -#define ID_WINDOW_GOTOTHREAD 40001 -#define ID_WINDOW_COPY 40002 -#define ID_WINDOW_PROPERTIES 40003 -#define ID_WINDOW_BRINGTOFRONT 40004 -#define ID_WINDOW_RESTORE 40005 -#define ID_WINDOW_MINIMIZE 40006 -#define ID_WINDOW_MAXIMIZE 40007 -#define ID_WINDOW_CLOSE 40008 -#define ID_WINDOW_SHOW 40009 -#define ID_WINDOW_HIGHLIGHT 40010 -#define ID_WINDOW_SHOWHIDE 40011 -#define ID_WINDOW_ENABLE 40012 -#define ID_WINDOW_ENABLEDISABLE 40013 -#define ID_WINDOW_ALWAYSONTOP 40014 -#define ID_WINDOW_OPACITY 40015 -#define ID_OPACITY_10 40016 -#define ID_OPACITY_20 40017 -#define ID_OPACITY_30 40018 -#define ID_OPACITY_40 40019 -#define ID_OPACITY_50 40020 -#define ID_OPACITY_60 40021 -#define ID_OPACITY_70 40022 -#define ID_OPACITY_80 40023 -#define ID_OPACITY_90 40024 -#define ID_OPACITY_OPAQUE 40025 -#define ID_WINDOW_VISIBLE 40026 -#define ID_WINDOW_ENABLED 40027 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 108 -#define _APS_NEXT_COMMAND_VALUE 40028 -#define _APS_NEXT_CONTROL_VALUE 1036 -#define _APS_NEXT_SYMED_VALUE 106 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by WindowExplorer.rc +// +#define IDD_WNDPROPSTORAGE 9 +#define IDD_WNDLIST 101 +#define ID_VIEW_WINDOWS 101 +#define ID_THREAD_WINDOWS 102 +#define ID_PROCESS_WINDOWS 103 +#define IDR_WINDOW 103 +#define ID_SHOWCONTEXTMENU 104 +#define IDD_WNDGENERAL 104 +#define ID_VIEW_DESKTOPWINDOWS 105 +#define IDD_WNDPROPLIST 105 +#define IDC_LIST 1001 +#define IDC_REFRESH 1002 +#define IDC_SEARCHEDIT 1035 +#define IDC_WINDOWINFO 1036 +#define ID_WINDOW_GOTOTHREAD 40001 +#define ID_WINDOW_COPY 40002 +#define ID_WINDOW_PROPERTIES 40003 +#define ID_WINDOW_BRINGTOFRONT 40004 +#define ID_WINDOW_RESTORE 40005 +#define ID_WINDOW_MINIMIZE 40006 +#define ID_WINDOW_MAXIMIZE 40007 +#define ID_WINDOW_CLOSE 40008 +#define ID_WINDOW_HIGHLIGHT 40010 +#define ID_WINDOW_ALWAYSONTOP 40014 +#define ID_OPACITY_10 40016 +#define ID_OPACITY_20 40017 +#define ID_OPACITY_30 40018 +#define ID_OPACITY_40 40019 +#define ID_OPACITY_50 40020 +#define ID_OPACITY_60 40021 +#define ID_OPACITY_70 40022 +#define ID_OPACITY_80 40023 +#define ID_OPACITY_90 40024 +#define ID_OPACITY_OPAQUE 40025 +#define ID_WINDOW_VISIBLE 40026 +#define ID_WINDOW_ENABLED 40027 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_COMMAND_VALUE 40028 +#define _APS_NEXT_CONTROL_VALUE 1037 +#define _APS_NEXT_SYMED_VALUE 107 +#endif +#endif diff --git a/plugins/WindowExplorer/utils.c b/plugins/WindowExplorer/utils.c index d9b6cfb5dfdb..25abb5743a48 100644 --- a/plugins/WindowExplorer/utils.c +++ b/plugins/WindowExplorer/utils.c @@ -1,104 +1,98 @@ -/* - * Process Hacker Window Explorer - - * utility functions - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "wndexp.h" - -// WARNING: No functions from ProcessHacker.exe should be used in this file! - -PVOID WeGetProcedureAddress( - _In_ PSTR Name - ) -{ - static PVOID imageBase = NULL; - - if (!imageBase) - imageBase = GetModuleHandle(L"ProcessHacker.exe"); - - return (PVOID)GetProcAddress(imageBase, Name); -} - -VOID WeFormatLocalObjectName( - _In_ PWSTR OriginalName, - _Inout_updates_(256) PWCHAR Buffer, - _Out_ PUNICODE_STRING ObjectName - ) -{ - SIZE_T length; - SIZE_T originalNameLength; - - // Sessions other than session 0 require SeCreateGlobalPrivilege. - if (NtCurrentPeb()->SessionId != 0) - { - memcpy(Buffer, L"\\Sessions\\", 10 * sizeof(WCHAR)); - _ultow(NtCurrentPeb()->SessionId, Buffer + 10, 10); - length = wcslen(Buffer); - originalNameLength = wcslen(OriginalName); - memcpy(Buffer + length, OriginalName, (originalNameLength + 1) * sizeof(WCHAR)); - length += originalNameLength; - - ObjectName->Buffer = Buffer; - ObjectName->MaximumLength = (ObjectName->Length = (USHORT)(length * sizeof(WCHAR))) + sizeof(WCHAR); - } - else - { - RtlInitUnicodeString(ObjectName, OriginalName); - } -} - -VOID WeInvertWindowBorder( - _In_ HWND hWnd - ) -{ - RECT rect; - HDC hdc; - - GetWindowRect(hWnd, &rect); - hdc = GetWindowDC(hWnd); - - if (hdc) - { - ULONG penWidth = GetSystemMetrics(SM_CXBORDER) * 3; - INT oldDc; - HPEN pen; - HBRUSH brush; - - oldDc = SaveDC(hdc); - - // Get an inversion effect. - SetROP2(hdc, R2_NOT); - - pen = CreatePen(PS_INSIDEFRAME, penWidth, RGB(0x00, 0x00, 0x00)); - SelectObject(hdc, pen); - - brush = GetStockObject(NULL_BRUSH); - SelectObject(hdc, brush); - - // Draw the rectangle. - Rectangle(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top); - - // Cleanup. - DeleteObject(pen); - - RestoreDC(hdc, oldDc); - ReleaseDC(hWnd, hdc); - } -} +/* + * Process Hacker Window Explorer - + * utility functions + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "wndexp.h" + +// WARNING: No functions from ProcessHacker.exe should be used in this file! + +PVOID WeGetProcedureAddress( + _In_ PSTR Name + ) +{ + return PhGetProcedureAddress(NtCurrentPeb()->ImageBaseAddress, Name, 0); +} + +VOID WeFormatLocalObjectName( + _In_ PWSTR OriginalName, + _Inout_updates_(256) PWCHAR Buffer, + _Out_ PUNICODE_STRING ObjectName + ) +{ + SIZE_T length; + SIZE_T originalNameLength; + + // Sessions other than session 0 require SeCreateGlobalPrivilege. + if (NtCurrentPeb()->SessionId != 0) + { + memcpy(Buffer, L"\\Sessions\\", 10 * sizeof(WCHAR)); + _ultow(NtCurrentPeb()->SessionId, Buffer + 10, 10); + length = wcslen(Buffer); + originalNameLength = wcslen(OriginalName); + memcpy(Buffer + length, OriginalName, (originalNameLength + 1) * sizeof(WCHAR)); + length += originalNameLength; + + ObjectName->Buffer = Buffer; + ObjectName->MaximumLength = (ObjectName->Length = (USHORT)(length * sizeof(WCHAR))) + sizeof(WCHAR); + } + else + { + RtlInitUnicodeString(ObjectName, OriginalName); + } +} + +VOID WeInvertWindowBorder( + _In_ HWND hWnd + ) +{ + RECT rect; + HDC hdc; + + GetWindowRect(hWnd, &rect); + + if (hdc = GetWindowDC(hWnd)) + { + ULONG penWidth = GetSystemMetrics(SM_CXBORDER) * 3; + INT oldDc; + HPEN pen; + HBRUSH brush; + + oldDc = SaveDC(hdc); + + // Get an inversion effect. + SetROP2(hdc, R2_NOT); + + pen = CreatePen(PS_INSIDEFRAME, penWidth, RGB(0x00, 0x00, 0x00)); + SelectPen(hdc, pen); + + brush = GetStockObject(NULL_BRUSH); + SelectBrush(hdc, brush); + + // Draw the rectangle. + Rectangle(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top); + + // Cleanup. + DeletePen(pen); + RestoreDC(hdc, oldDc); + ReleaseDC(hWnd, hdc); + } +} diff --git a/plugins/WindowExplorer/wnddlg.c b/plugins/WindowExplorer/wnddlg.c index 030850942bb2..2df3c40a1a10 100644 --- a/plugins/WindowExplorer/wnddlg.c +++ b/plugins/WindowExplorer/wnddlg.c @@ -1,1193 +1,1311 @@ -/* - * Process Hacker Window Explorer - - * window tree dialog - * - * Copyright (C) 2016 dmex - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "wndexp.h" -#include "resource.h" -#include -#include - -typedef struct _WINDOWS_CONTEXT -{ - HWND TreeNewHandle; - HWND SearchBoxHandle; - WE_WINDOW_TREE_CONTEXT TreeContext; - WE_WINDOW_SELECTOR Selector; - - PH_LAYOUT_MANAGER LayoutManager; - - HWND HighlightingWindow; - ULONG HighlightingWindowCount; -} WINDOWS_CONTEXT, *PWINDOWS_CONTEXT; - -VOID WepShowWindowsDialogCallback( - _In_ PVOID Parameter - ); - -INT_PTR CALLBACK WepWindowsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK WepWindowsPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -VOID WeShowWindowsDialog( - _In_ HWND ParentWindowHandle, - _In_ PWE_WINDOW_SELECTOR Selector - ) -{ - PWINDOWS_CONTEXT context; - - context = PhAllocate(sizeof(WINDOWS_CONTEXT)); - memset(context, 0, sizeof(WINDOWS_CONTEXT)); - memcpy(&context->Selector, Selector, sizeof(WE_WINDOW_SELECTOR)); - - ProcessHacker_Invoke(WE_PhMainWndHandle, WepShowWindowsDialogCallback, context); -} - -VOID WeShowWindowsPropPage( - _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT Context, - _In_ PWE_WINDOW_SELECTOR Selector - ) -{ - PWINDOWS_CONTEXT context; - - context = PhAllocate(sizeof(WINDOWS_CONTEXT)); - memset(context, 0, sizeof(WINDOWS_CONTEXT)); - memcpy(&context->Selector, Selector, sizeof(WE_WINDOW_SELECTOR)); - - PhAddProcessPropPage( - Context->PropContext, - PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_WNDLIST), WepWindowsPageProc, context) - ); -} - -VOID WepShowWindowsDialogCallback( - _In_ PVOID Parameter - ) -{ - HWND hwnd; - PWINDOWS_CONTEXT context = Parameter; - - hwnd = CreateDialogParam( - PluginInstance->DllBase, - MAKEINTRESOURCE(IDD_WNDLIST), - NULL, - WepWindowsDlgProc, - (LPARAM)context - ); - ShowWindow(hwnd, SW_SHOW); -} - -VOID WepDeleteWindowSelector( - _In_ PWE_WINDOW_SELECTOR Selector - ) -{ - switch (Selector->Type) - { - case WeWindowSelectorDesktop: - PhDereferenceObject(Selector->Desktop.DesktopName); - break; - } -} - -VOID WepFillWindowInfo( - _In_ PWE_WINDOW_NODE Node - ) -{ - HWND hwnd; - ULONG threadId; - ULONG processId; - - hwnd = Node->WindowHandle; - - GetClassName(hwnd, Node->WindowClass, sizeof(Node->WindowClass) / sizeof(WCHAR)); - Node->WindowText = PhGetWindowText(hwnd); - - if (!Node->WindowText) - Node->WindowText = PhReferenceEmptyString(); - - threadId = GetWindowThreadProcessId(hwnd, &processId); - Node->ClientId.UniqueProcess = UlongToHandle(processId); - Node->ClientId.UniqueThread = UlongToHandle(threadId); - - Node->WindowVisible = !!IsWindowVisible(hwnd); - Node->HasChildren = !!FindWindowEx(hwnd, NULL, NULL, NULL); -} - -PWE_WINDOW_NODE WepAddChildWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_opt_ PWE_WINDOW_NODE ParentNode, - _In_ HWND hwnd - ) -{ - PWE_WINDOW_NODE childNode; - - childNode = WeAddWindowNode(Context); - childNode->WindowHandle = hwnd; - - WepFillWindowInfo(childNode); - - if (ParentNode) - { - // This is a child node. - childNode->Node.Expanded = TRUE; - childNode->Parent = ParentNode; - - PhAddItemList(ParentNode->Children, childNode); - } - else - { - // This is a root node. - childNode->Node.Expanded = TRUE; - - PhAddItemList(Context->NodeRootList, childNode); - } - - return childNode; -} - -VOID WepAddChildWindows( - _In_ PWINDOWS_CONTEXT Context, - _In_opt_ PWE_WINDOW_NODE ParentNode, - _In_ HWND hwnd, - _In_opt_ HANDLE FilterProcessId, - _In_opt_ HANDLE FilterThreadId - ) -{ - HWND childWindow = NULL; - ULONG i = 0; - - // We use FindWindowEx because EnumWindows doesn't return Metro app windows. - // Set a reasonable limit to prevent infinite loops. - while (i < 0x800 && (childWindow = FindWindowEx(hwnd, childWindow, NULL, NULL))) - { - ULONG processId; - ULONG threadId; - - threadId = GetWindowThreadProcessId(childWindow, &processId); - - if ( - (!FilterProcessId || UlongToHandle(processId) == FilterProcessId) && - (!FilterThreadId || UlongToHandle(threadId) == FilterThreadId) - ) - { - PWE_WINDOW_NODE childNode = WepAddChildWindowNode(&Context->TreeContext, ParentNode, childWindow); - - if (childNode->HasChildren) - { - WepAddChildWindows(Context, childNode, childWindow, NULL, NULL); - } - } - - i++; - } -} - -BOOL CALLBACK WepEnumDesktopWindowsProc( - _In_ HWND hwnd, - _In_ LPARAM lParam - ) -{ - PWINDOWS_CONTEXT context = (PWINDOWS_CONTEXT)lParam; - - WepAddChildWindowNode(&context->TreeContext, NULL, hwnd); - - return TRUE; -} - -VOID WepAddDesktopWindows( - _In_ PWINDOWS_CONTEXT Context, - _In_ PWSTR DesktopName - ) -{ - HDESK desktopHandle; - - if (desktopHandle = OpenDesktop(DesktopName, 0, FALSE, DESKTOP_ENUMERATE)) - { - EnumDesktopWindows(desktopHandle, WepEnumDesktopWindowsProc, (LPARAM)Context); - CloseDesktop(desktopHandle); - } -} - -VOID WepRefreshWindows( - _In_ PWINDOWS_CONTEXT Context - ) -{ - TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); - WeClearWindowTree(&Context->TreeContext); - - switch (Context->Selector.Type) - { - case WeWindowSelectorAll: - { - PWE_WINDOW_NODE desktopNode; - - desktopNode = WeAddWindowNode(&Context->TreeContext); - desktopNode->WindowHandle = GetDesktopWindow(); - WepFillWindowInfo(desktopNode); - - PhAddItemList(Context->TreeContext.NodeRootList, desktopNode); - - WepAddChildWindows(Context, desktopNode, desktopNode->WindowHandle, NULL, NULL); - - desktopNode->HasChildren = TRUE; - } - break; - case WeWindowSelectorThread: - { - WepAddChildWindows(Context, NULL, GetDesktopWindow(), NULL, Context->Selector.Thread.ThreadId); - } - break; - case WeWindowSelectorProcess: - { - WepAddChildWindows(Context, NULL, GetDesktopWindow(), Context->Selector.Process.ProcessId, NULL); - } - break; - case WeWindowSelectorDesktop: - { - WepAddDesktopWindows(Context, Context->Selector.Desktop.DesktopName->Buffer); - } - break; - } - - TreeNew_NodesStructured(Context->TreeNewHandle); - TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); -} - -PPH_STRING WepGetWindowTitleForSelector( - _In_ PWE_WINDOW_SELECTOR Selector - ) -{ - switch (Selector->Type) - { - case WeWindowSelectorAll: - { - return PhCreateString(L"Windows - All"); - } - break; - case WeWindowSelectorThread: - { - return PhFormatString(L"Windows - Thread %lu", HandleToUlong(Selector->Thread.ThreadId)); - } - break; - case WeWindowSelectorProcess: - { - CLIENT_ID clientId; - - clientId.UniqueProcess = Selector->Process.ProcessId; - clientId.UniqueThread = NULL; - - return PhConcatStrings2(L"Windows - ", PH_AUTO_T(PH_STRING, PhGetClientIdName(&clientId))->Buffer); - } - break; - case WeWindowSelectorDesktop: - { - return PhFormatString(L"Windows - Desktop \"%s\"", Selector->Desktop.DesktopName->Buffer); - } - break; - default: - return PhCreateString(L"Windows"); - } -} - -INT_PTR CALLBACK WepWindowsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWINDOWS_CONTEXT context; - - if (uMsg == WM_INITDIALOG) - { - context = (PWINDOWS_CONTEXT)lParam; - SetProp(hwndDlg, L"Context", (HANDLE)context); - } - else - { - context = (PWINDOWS_CONTEXT)GetProp(hwndDlg, L"Context"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"Context"); - } - - if (!context) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PH_RECTANGLE windowRectangle; - - context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); - context->SearchBoxHandle = GetDlgItem(hwndDlg, IDC_SEARCHEDIT); - - SetWindowText(hwndDlg, PH_AUTO_T(PH_STRING, WepGetWindowTitleForSelector(&context->Selector))->Buffer); - - CreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); - - WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); - - PhRegisterDialog(hwndDlg); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SEARCHEDIT), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); - - // Set up the window position and size. - windowRectangle.Position = PhGetIntegerPairSetting(SETTING_NAME_WINDOWS_WINDOW_POSITION); - windowRectangle.Size = PhGetScalableIntegerPairSetting(SETTING_NAME_WINDOWS_WINDOW_SIZE, TRUE).Pair; - PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); - - MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top, windowRectangle.Width, windowRectangle.Height, FALSE); - - // Implement cascading by saving an offsetted rectangle. - windowRectangle.Left += 20; - windowRectangle.Top += 20; - PhSetIntegerPairSetting(SETTING_NAME_WINDOWS_WINDOW_POSITION, windowRectangle.Position); - - WepRefreshWindows(context); - - SendMessage(GetParent(hwndDlg), WM_NEXTDLGCTL, (WPARAM)GetDlgItem(GetParent(hwndDlg), IDCANCEL), TRUE); - } - break; - case WM_DESTROY: - { - PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOWS_WINDOW_POSITION, SETTING_NAME_WINDOWS_WINDOW_SIZE, hwndDlg); - - PhDeleteLayoutManager(&context->LayoutManager); - - PhUnregisterDialog(hwndDlg); - - WeDeleteWindowTree(&context->TreeContext); - WepDeleteWindowSelector(&context->Selector); - PhFree(context); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case EN_CHANGE: - { - PPH_STRING newSearchboxText; - - if (GET_WM_COMMAND_HWND(wParam, lParam) != context->SearchBoxHandle) - break; - - newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchBoxHandle)); - - if (!PhEqualString(context->TreeContext.SearchboxText, newSearchboxText, FALSE)) - { - PhSwapReference(&context->TreeContext.SearchboxText, newSearchboxText); - - if (!PhIsNullOrEmptyString(context->TreeContext.SearchboxText)) - WeExpandAllWindowNodes(&context->TreeContext, TRUE); - - PhApplyTreeNewFilters(&context->TreeContext.FilterSupport); - - TreeNew_NodesStructured(context->TreeNewHandle); - // PhInvokeCallback(&SearchChangedEvent, SearchboxText); - } - } - break; - } - - switch (LOWORD(wParam)) - { - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - case IDC_REFRESH: - WepRefreshWindows(context); - break; - case ID_SHOWCONTEXTMENU: - { - POINT point; - PWE_WINDOW_NODE *windows; - ULONG numberOfWindows; - PPH_EMENU menu; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); - - WeGetSelectedWindowNodes( - &context->TreeContext, - &windows, - &numberOfWindows - ); - - if (numberOfWindows != 0) - { - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_WINDOW), 0); - PhSetFlagsEMenuItem(menu, ID_WINDOW_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); - - if (numberOfWindows == 1) - { - WINDOWPLACEMENT placement = { sizeof(placement) }; - BYTE alpha; - ULONG flags; - ULONG i; - ULONG id; - - // State - - GetWindowPlacement(windows[0]->WindowHandle, &placement); - - if (placement.showCmd == SW_MINIMIZE) - PhSetFlagsEMenuItem(menu, ID_WINDOW_MINIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - else if (placement.showCmd == SW_MAXIMIZE) - PhSetFlagsEMenuItem(menu, ID_WINDOW_MAXIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - else if (placement.showCmd == SW_NORMAL) - PhSetFlagsEMenuItem(menu, ID_WINDOW_RESTORE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - - // Visible - - PhSetFlagsEMenuItem(menu, ID_WINDOW_VISIBLE, PH_EMENU_CHECKED, - (GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_VISIBLE) ? PH_EMENU_CHECKED : 0); - - // Enabled - - PhSetFlagsEMenuItem(menu, ID_WINDOW_ENABLED, PH_EMENU_CHECKED, - !(GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_DISABLED) ? PH_EMENU_CHECKED : 0); - - // Always on Top - - PhSetFlagsEMenuItem(menu, ID_WINDOW_ALWAYSONTOP, PH_EMENU_CHECKED, - (GetWindowLong(windows[0]->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) ? PH_EMENU_CHECKED : 0); - - // Opacity - - if (GetLayeredWindowAttributes(windows[0]->WindowHandle, NULL, &alpha, &flags)) - { - if (!(flags & LWA_ALPHA)) - alpha = 255; - } - else - { - alpha = 255; - } - - if (alpha == 255) - { - id = ID_OPACITY_OPAQUE; - } - else - { - id = 0; - - // Due to integer division, we cannot use simple arithmetic to calculate which menu item to check. - for (i = 0; i < 10; i++) - { - if (alpha == (BYTE)(255 * (i + 1) / 10)) - { - id = ID_OPACITY_10 + i; - break; - } - } - } - - if (id != 0) - { - PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK, - PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK); - } - } - else - { - PhSetFlagsAllEMenuItems(menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - PhSetFlagsEMenuItem(menu, ID_WINDOW_COPY, PH_EMENU_DISABLED, 0); - } - - PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); - PhDestroyEMenu(menu); - } - } - break; - case ID_WINDOW_BRINGTOFRONT: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - WINDOWPLACEMENT placement = { sizeof(placement) }; - - GetWindowPlacement(selectedNode->WindowHandle, &placement); - - if (placement.showCmd == SW_MINIMIZE) - ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); - else - SetForegroundWindow(selectedNode->WindowHandle); - } - } - break; - case ID_WINDOW_RESTORE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); - } - } - break; - case ID_WINDOW_MINIMIZE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ShowWindowAsync(selectedNode->WindowHandle, SW_MINIMIZE); - } - } - break; - case ID_WINDOW_MAXIMIZE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ShowWindowAsync(selectedNode->WindowHandle, SW_MAXIMIZE); - } - } - break; - case ID_WINDOW_CLOSE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - PostMessage(selectedNode->WindowHandle, WM_CLOSE, 0, 0); - } - } - break; - case ID_WINDOW_VISIBLE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - if (IsWindowVisible(selectedNode->WindowHandle)) - { - selectedNode->WindowVisible = FALSE; - ShowWindowAsync(selectedNode->WindowHandle, SW_HIDE); - } - else - { - selectedNode->WindowVisible = TRUE; - ShowWindowAsync(selectedNode->WindowHandle, SW_SHOW); - } - - PhInvalidateTreeNewNode(&selectedNode->Node, TN_CACHE_COLOR); - TreeNew_InvalidateNode(context->TreeNewHandle, &selectedNode->Node); - } - } - break; - case ID_WINDOW_ENABLED: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - EnableWindow(selectedNode->WindowHandle, !IsWindowEnabled(selectedNode->WindowHandle)); - } - } - break; - case ID_WINDOW_ALWAYSONTOP: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - LOGICAL topMost; - - topMost = GetWindowLong(selectedNode->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST; - SetWindowPos(selectedNode->WindowHandle, topMost ? HWND_NOTOPMOST : HWND_TOPMOST, - 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - } - } - break; - case ID_OPACITY_10: - case ID_OPACITY_20: - case ID_OPACITY_30: - case ID_OPACITY_40: - case ID_OPACITY_50: - case ID_OPACITY_60: - case ID_OPACITY_70: - case ID_OPACITY_80: - case ID_OPACITY_90: - case ID_OPACITY_OPAQUE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ULONG opacity; - - opacity = ((ULONG)LOWORD(wParam) - ID_OPACITY_10) + 1; - - if (opacity == 10) - { - // Remove the WS_EX_LAYERED bit since it is not needed. - PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, 0); - RedrawWindow(selectedNode->WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); - } - else - { - // Add the WS_EX_LAYERED bit so opacity will work. - PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED); - SetLayeredWindowAttributes(selectedNode->WindowHandle, 0, (BYTE)(255 * opacity / 10), LWA_ALPHA); - } - } - } - break; - case ID_WINDOW_HIGHLIGHT: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - if (context->HighlightingWindow) - { - if (context->HighlightingWindowCount & 1) - WeInvertWindowBorder(context->HighlightingWindow); - } - - context->HighlightingWindow = selectedNode->WindowHandle; - context->HighlightingWindowCount = 10; - SetTimer(hwndDlg, 9, 100, NULL); - } - } - break; - case ID_WINDOW_GOTOTHREAD: - { - PWE_WINDOW_NODE selectedNode; - PPH_PROCESS_ITEM processItem; - PPH_PROCESS_PROPCONTEXT propContext; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - if (processItem = PhReferenceProcessItem(selectedNode->ClientId.UniqueProcess)) - { - if (propContext = PhCreateProcessPropContext(WE_PhMainWndHandle, processItem)) - { - PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ClientId.UniqueThread); - PhShowProcessProperties(propContext); - PhDereferenceObject(propContext); - } - - PhDereferenceObject(processItem); - } - else - { - PhShowError(hwndDlg, L"The process does not exist."); - } - } - } - break; - case ID_WINDOW_PROPERTIES: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - WeShowWindowProperties(hwndDlg, selectedNode->WindowHandle); - } - break; - case ID_WINDOW_COPY: - { - PPH_STRING text; - - text = PhGetTreeNewText(context->TreeNewHandle, 0); - PhSetClipboardString(hwndDlg, &text->sr); - PhDereferenceObject(text); - } - break; - } - } - break; - case WM_TIMER: - { - switch (wParam) - { - case 9: - { - WeInvertWindowBorder(context->HighlightingWindow); - - if (--context->HighlightingWindowCount == 0) - KillTimer(hwndDlg, 9); - } - break; - } - } - break; - case WM_SIZE: - { - PhLayoutManagerLayout(&context->LayoutManager); - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK WepWindowsPageProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWINDOWS_CONTEXT context; - LPPROPSHEETPAGE propSheetPage; - PPH_PROCESS_PROPPAGECONTEXT propPageContext; - PPH_PROCESS_ITEM processItem; - - if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) - { - context = propPageContext->Context; - } - else - { - return FALSE; - } - - switch (uMsg) - { - case WM_INITDIALOG: - { - context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); - context->SearchBoxHandle = GetDlgItem(hwndDlg, IDC_SEARCHEDIT); - - CreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); - - WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); - - PhRegisterDialog(hwndDlg); - - PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SEARCHEDIT), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); - - WepRefreshWindows(context); - } - break; - case WM_SHOWWINDOW: - { - if (PhBeginPropPageLayout(hwndDlg, propPageContext)) - PhEndPropPageLayout(hwndDlg, propPageContext); - } - break; - case WM_DESTROY: - { - PhDeleteLayoutManager(&context->LayoutManager); - - PhUnregisterDialog(hwndDlg); - - WeDeleteWindowTree(&context->TreeContext); - WepDeleteWindowSelector(&context->Selector); - PhFree(context); - } - break; - case WM_COMMAND: - { - switch (GET_WM_COMMAND_CMD(wParam, lParam)) - { - case EN_CHANGE: - { - PPH_STRING newSearchboxText; - - if (GET_WM_COMMAND_HWND(wParam, lParam) != context->SearchBoxHandle) - break; - - newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchBoxHandle)); - - if (!PhEqualString(context->TreeContext.SearchboxText, newSearchboxText, FALSE)) - { - PhSwapReference(&context->TreeContext.SearchboxText, newSearchboxText); - - if (!PhIsNullOrEmptyString(context->TreeContext.SearchboxText)) - WeExpandAllWindowNodes(&context->TreeContext, TRUE); - - PhApplyTreeNewFilters(&context->TreeContext.FilterSupport); - - TreeNew_NodesStructured(context->TreeNewHandle); - // PhInvokeCallback(&SearchChangedEvent, SearchboxText); - } - } - break; - } - - switch (LOWORD(wParam)) - { - case IDC_REFRESH: - WepRefreshWindows(context); - break; - case ID_SHOWCONTEXTMENU: - { - POINT point; - PWE_WINDOW_NODE *windows; - ULONG numberOfWindows; - PPH_EMENU menu; - - point.x = (SHORT)LOWORD(lParam); - point.y = (SHORT)HIWORD(lParam); - - WeGetSelectedWindowNodes( - &context->TreeContext, - &windows, - &numberOfWindows - ); - - if (numberOfWindows != 0) - { - menu = PhCreateEMenu(); - PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_WINDOW), 0); - PhSetFlagsEMenuItem(menu, ID_WINDOW_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); - - if (numberOfWindows == 1) - { - WINDOWPLACEMENT placement = { sizeof(placement) }; - BYTE alpha; - ULONG flags; - ULONG i; - ULONG id; - - // State - - GetWindowPlacement(windows[0]->WindowHandle, &placement); - - if (placement.showCmd == SW_MINIMIZE) - PhSetFlagsEMenuItem(menu, ID_WINDOW_MINIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - else if (placement.showCmd == SW_MAXIMIZE) - PhSetFlagsEMenuItem(menu, ID_WINDOW_MAXIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - else if (placement.showCmd == SW_NORMAL) - PhSetFlagsEMenuItem(menu, ID_WINDOW_RESTORE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - - // Visible - - PhSetFlagsEMenuItem(menu, ID_WINDOW_VISIBLE, PH_EMENU_CHECKED, - (GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_VISIBLE) ? PH_EMENU_CHECKED : 0); - - // Enabled - - PhSetFlagsEMenuItem(menu, ID_WINDOW_ENABLED, PH_EMENU_CHECKED, - !(GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_DISABLED) ? PH_EMENU_CHECKED : 0); - - // Always on Top - - PhSetFlagsEMenuItem(menu, ID_WINDOW_ALWAYSONTOP, PH_EMENU_CHECKED, - (GetWindowLong(windows[0]->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) ? PH_EMENU_CHECKED : 0); - - // Opacity - - if (GetLayeredWindowAttributes(windows[0]->WindowHandle, NULL, &alpha, &flags)) - { - if (!(flags & LWA_ALPHA)) - alpha = 255; - } - else - { - alpha = 255; - } - - if (alpha == 255) - { - id = ID_OPACITY_OPAQUE; - } - else - { - id = 0; - - // Due to integer division, we cannot use simple arithmetic to calculate which menu item to check. - for (i = 0; i < 10; i++) - { - if (alpha == (BYTE)(255 * (i + 1) / 10)) - { - id = ID_OPACITY_10 + i; - break; - } - } - } - - if (id != 0) - { - PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK, - PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK); - } - } - else - { - PhSetFlagsAllEMenuItems(menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); - PhSetFlagsEMenuItem(menu, ID_WINDOW_COPY, PH_EMENU_DISABLED, 0); - } - - PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y); - PhDestroyEMenu(menu); - } - } - break; - case ID_WINDOW_BRINGTOFRONT: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - WINDOWPLACEMENT placement = { sizeof(placement) }; - - GetWindowPlacement(selectedNode->WindowHandle, &placement); - - if (placement.showCmd == SW_MINIMIZE) - ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); - else - SetForegroundWindow(selectedNode->WindowHandle); - } - } - break; - case ID_WINDOW_RESTORE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); - } - } - break; - case ID_WINDOW_MINIMIZE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ShowWindowAsync(selectedNode->WindowHandle, SW_MINIMIZE); - } - } - break; - case ID_WINDOW_MAXIMIZE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ShowWindowAsync(selectedNode->WindowHandle, SW_MAXIMIZE); - } - } - break; - case ID_WINDOW_CLOSE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - PostMessage(selectedNode->WindowHandle, WM_CLOSE, 0, 0); - } - } - break; - case ID_WINDOW_VISIBLE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - if (IsWindowVisible(selectedNode->WindowHandle)) - { - selectedNode->WindowVisible = FALSE; - ShowWindowAsync(selectedNode->WindowHandle, SW_HIDE); - } - else - { - selectedNode->WindowVisible = TRUE; - ShowWindowAsync(selectedNode->WindowHandle, SW_SHOW); - } - - PhInvalidateTreeNewNode(&selectedNode->Node, TN_CACHE_COLOR); - TreeNew_InvalidateNode(context->TreeNewHandle, &selectedNode->Node); - } - } - break; - case ID_WINDOW_ENABLED: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - EnableWindow(selectedNode->WindowHandle, !IsWindowEnabled(selectedNode->WindowHandle)); - } - } - break; - case ID_WINDOW_ALWAYSONTOP: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - LOGICAL topMost; - - topMost = GetWindowLong(selectedNode->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST; - SetWindowPos(selectedNode->WindowHandle, topMost ? HWND_NOTOPMOST : HWND_TOPMOST, - 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - } - } - break; - case ID_OPACITY_10: - case ID_OPACITY_20: - case ID_OPACITY_30: - case ID_OPACITY_40: - case ID_OPACITY_50: - case ID_OPACITY_60: - case ID_OPACITY_70: - case ID_OPACITY_80: - case ID_OPACITY_90: - case ID_OPACITY_OPAQUE: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - ULONG opacity; - - opacity = ((ULONG)LOWORD(wParam) - ID_OPACITY_10) + 1; - - if (opacity == 10) - { - // Remove the WS_EX_LAYERED bit since it is not needed. - PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, 0); - RedrawWindow(selectedNode->WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); - } - else - { - // Add the WS_EX_LAYERED bit so opacity will work. - PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED); - SetLayeredWindowAttributes(selectedNode->WindowHandle, 0, (BYTE)(255 * opacity / 10), LWA_ALPHA); - } - } - } - break; - case ID_WINDOW_HIGHLIGHT: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - if (context->HighlightingWindow) - { - if (context->HighlightingWindowCount & 1) - WeInvertWindowBorder(context->HighlightingWindow); - } - - context->HighlightingWindow = selectedNode->WindowHandle; - context->HighlightingWindowCount = 10; - SetTimer(hwndDlg, 9, 100, NULL); - } - } - break; - case ID_WINDOW_GOTOTHREAD: - { - PWE_WINDOW_NODE selectedNode; - PPH_PROCESS_ITEM processItem; - PPH_PROCESS_PROPCONTEXT propContext; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - { - if (processItem = PhReferenceProcessItem(selectedNode->ClientId.UniqueProcess)) - { - if (propContext = PhCreateProcessPropContext(WE_PhMainWndHandle, processItem)) - { - PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ClientId.UniqueThread); - PhShowProcessProperties(propContext); - PhDereferenceObject(propContext); - } - - PhDereferenceObject(processItem); - } - else - { - PhShowError(hwndDlg, L"The process does not exist."); - } - } - } - break; - case ID_WINDOW_PROPERTIES: - { - PWE_WINDOW_NODE selectedNode; - - if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) - WeShowWindowProperties(hwndDlg, selectedNode->WindowHandle); - } - break; - case ID_WINDOW_COPY: - { - PPH_STRING text; - - text = PhGetTreeNewText(context->TreeNewHandle, 0); - PhSetClipboardString(hwndDlg, &text->sr); - PhDereferenceObject(text); - } - break; - } - } - break; - case WM_TIMER: - { - switch (wParam) - { - case 9: - { - WeInvertWindowBorder(context->HighlightingWindow); - - if (--context->HighlightingWindowCount == 0) - KillTimer(hwndDlg, 9); - } - break; - } - } - break; - case WM_SIZE: - PhLayoutManagerLayout(&context->LayoutManager); - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_REFRESH)); - return TRUE; - } - } - break; - } - - return FALSE; -} \ No newline at end of file +/* + * Process Hacker Window Explorer - + * window tree dialog + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2016-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "wndexp.h" + +typedef struct _WINDOWS_CONTEXT +{ + HWND TreeNewHandle; + HWND SearchBoxHandle; + WE_WINDOW_TREE_CONTEXT TreeContext; + WE_WINDOW_SELECTOR Selector; + + PH_LAYOUT_MANAGER LayoutManager; + + HWND HighlightingWindow; + ULONG HighlightingWindowCount; +} WINDOWS_CONTEXT, *PWINDOWS_CONTEXT; + +INT_PTR CALLBACK WepWindowsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK WepWindowsPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +HWND WepWindowsDialogHandle = NULL; +HANDLE WepWindowsDialogThreadHandle = NULL; +PH_EVENT WepWindowsInitializedEvent = PH_EVENT_INIT; +PH_STRINGREF WepEmptyWindowsText = PH_STRINGREF_INIT(L"There are no windows to display."); +#define PH_SHOWDIALOG (WM_APP + 501) + +NTSTATUS WepShowWindowsDialogThread( + _In_ PVOID Parameter + ) +{ + BOOL result; + MSG message; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + WepWindowsDialogHandle = CreateDialogParam( + PluginInstance->DllBase, + MAKEINTRESOURCE(IDD_WNDLIST), + NULL, + WepWindowsDlgProc, + (LPARAM)Parameter + ); + + PhSetEvent(&WepWindowsInitializedEvent); + + while (result = GetMessage(&message, NULL, 0, 0)) + { + if (result == -1) + break; + + if (!IsDialogMessage(WepWindowsDialogHandle, &message)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + + PhDrainAutoPool(&autoPool); + } + + PhDeleteAutoPool(&autoPool); + PhResetEvent(&WepWindowsInitializedEvent); + + NtClose(WepWindowsDialogThreadHandle); + WepWindowsDialogThreadHandle = NULL; + WepWindowsDialogHandle = NULL; + + return STATUS_SUCCESS; +} + +VOID WeShowWindowsDialog( + _In_ HWND ParentWindowHandle, + _In_ PWE_WINDOW_SELECTOR Selector + ) +{ + if (!WepWindowsDialogThreadHandle) + { + PWINDOWS_CONTEXT context; + + context = PhAllocateZero(sizeof(WINDOWS_CONTEXT)); + memcpy(&context->Selector, Selector, sizeof(WE_WINDOW_SELECTOR)); + + if (!NT_SUCCESS(PhCreateThreadEx(&WepWindowsDialogThreadHandle, WepShowWindowsDialogThread, context))) + { + PhFree(context); + PhShowError(ParentWindowHandle, L"Unable to create the window."); + return; + } + + PhWaitForEvent(&WepWindowsInitializedEvent, NULL); + } + + PostMessage(WepWindowsDialogHandle, PH_SHOWDIALOG, 0, 0); +} + +VOID WeShowWindowsPropPage( + _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT Context, + _In_ PWE_WINDOW_SELECTOR Selector + ) +{ + PWINDOWS_CONTEXT context; + + context = PhAllocateZero(sizeof(WINDOWS_CONTEXT)); + memcpy(&context->Selector, Selector, sizeof(WE_WINDOW_SELECTOR)); + + PhAddProcessPropPage( + Context->PropContext, + PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_WNDLIST), WepWindowsPageProc, context) + ); +} + +VOID WepDeleteWindowSelector( + _In_ PWE_WINDOW_SELECTOR Selector + ) +{ + switch (Selector->Type) + { + case WeWindowSelectorDesktop: + PhDereferenceObject(Selector->Desktop.DesktopName); + break; + } +} + +VOID WepFillWindowInfo( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ PWE_WINDOW_NODE Node + ) +{ + PPH_STRING windowText; + ULONG threadId; + ULONG processId; + + PhPrintPointer(Node->WindowHandleString, Node->WindowHandle); + GetClassName(Node->WindowHandle, Node->WindowClass, sizeof(Node->WindowClass) / sizeof(WCHAR)); + + if (PhGetWindowTextEx(Node->WindowHandle, PH_GET_WINDOW_TEXT_INTERNAL, &windowText) > 0) + PhMoveReference(&Node->WindowText, windowText); + + if (PhIsNullOrEmptyString(Node->WindowText)) + PhMoveReference(&Node->WindowText, PhReferenceEmptyString()); + + threadId = GetWindowThreadProcessId(Node->WindowHandle, &processId); + Node->ClientId.UniqueProcess = UlongToHandle(processId); + Node->ClientId.UniqueThread = UlongToHandle(threadId); + Node->ThreadString = PhGetClientIdName(&Node->ClientId); + + Node->WindowVisible = !!IsWindowVisible(Node->WindowHandle); + Node->HasChildren = !!FindWindowEx(Node->WindowHandle, NULL, NULL, NULL); + + if (processId) + { + HANDLE processHandle; + PVOID instanceHandle; + PPH_STRING fileName; + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, UlongToHandle(processId)))) + { + if (!(instanceHandle = (PVOID)GetWindowLongPtr(Node->WindowHandle, GWLP_HINSTANCE))) + { + instanceHandle = (PVOID)GetClassLongPtr(Node->WindowHandle, GCLP_HMODULE); + } + + if (instanceHandle) + { + if (NT_SUCCESS(PhGetProcessMappedFileName(processHandle, instanceHandle, &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); + PhMoveReference(&fileName, PhGetBaseName(fileName)); + + PhMoveReference(&Node->ModuleString, fileName); + } + } + + NtClose(processHandle); + } + } + + if (Context->FilterSupport.FilterList) // Note: Apply filter after filling window node data. (dmex) + Node->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Node->Node); +} + +PWE_WINDOW_NODE WepAddChildWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_opt_ PWE_WINDOW_NODE ParentNode, + _In_ HWND hwnd + ) +{ + PWE_WINDOW_NODE childNode; + + childNode = WeAddWindowNode(Context); + childNode->WindowHandle = hwnd; + + WepFillWindowInfo(Context, childNode); + + if (ParentNode) + { + // This is a child node. + childNode->Node.Expanded = TRUE; + childNode->Parent = ParentNode; + + PhAddItemList(ParentNode->Children, childNode); + } + else + { + // This is a root node. + childNode->Node.Expanded = TRUE; + + PhAddItemList(Context->NodeRootList, childNode); + } + + return childNode; +} + +VOID WepAddChildWindows( + _In_ PWINDOWS_CONTEXT Context, + _In_opt_ PWE_WINDOW_NODE ParentNode, + _In_ HWND hwnd, + _In_opt_ HANDLE FilterProcessId, + _In_opt_ HANDLE FilterThreadId + ) +{ + HWND childWindow = NULL; + ULONG i = 0; + + // We use FindWindowEx because EnumWindows doesn't return Metro app windows. + // Set a reasonable limit to prevent infinite loops. + while (i < 0x800 && (childWindow = FindWindowEx(hwnd, childWindow, NULL, NULL))) + { + ULONG processId; + ULONG threadId; + + threadId = GetWindowThreadProcessId(childWindow, &processId); + + if ( + (!FilterProcessId || UlongToHandle(processId) == FilterProcessId) && + (!FilterThreadId || UlongToHandle(threadId) == FilterThreadId) + ) + { + PWE_WINDOW_NODE childNode = WepAddChildWindowNode(&Context->TreeContext, ParentNode, childWindow); + + if (childNode->HasChildren) + { + WepAddChildWindows(Context, childNode, childWindow, NULL, NULL); + } + } + + i++; + } +} + +BOOL CALLBACK WepEnumDesktopWindowsProc( + _In_ HWND hwnd, + _In_ LPARAM lParam + ) +{ + PWINDOWS_CONTEXT context = (PWINDOWS_CONTEXT)lParam; + + WepAddChildWindowNode(&context->TreeContext, NULL, hwnd); + + return TRUE; +} + +VOID WepAddDesktopWindows( + _In_ PWINDOWS_CONTEXT Context, + _In_ PWSTR DesktopName + ) +{ + HDESK desktopHandle; + + if (desktopHandle = OpenDesktop(DesktopName, 0, FALSE, DESKTOP_ENUMERATE)) + { + EnumDesktopWindows(desktopHandle, WepEnumDesktopWindowsProc, (LPARAM)Context); + CloseDesktop(desktopHandle); + } +} + +VOID WepRefreshWindows( + _In_ PWINDOWS_CONTEXT Context + ) +{ + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); + WeClearWindowTree(&Context->TreeContext); + + switch (Context->Selector.Type) + { + case WeWindowSelectorAll: + { + PWE_WINDOW_NODE desktopNode; + + desktopNode = WeAddWindowNode(&Context->TreeContext); + desktopNode->WindowHandle = GetDesktopWindow(); + WepFillWindowInfo(&Context->TreeContext, desktopNode); + + PhAddItemList(Context->TreeContext.NodeRootList, desktopNode); + + WepAddChildWindows(Context, desktopNode, desktopNode->WindowHandle, NULL, NULL); + + desktopNode->HasChildren = TRUE; + } + break; + case WeWindowSelectorThread: + { + WepAddChildWindows(Context, NULL, GetDesktopWindow(), NULL, Context->Selector.Thread.ThreadId); + } + break; + case WeWindowSelectorProcess: + { + WepAddChildWindows(Context, NULL, GetDesktopWindow(), Context->Selector.Process.ProcessId, NULL); + } + break; + case WeWindowSelectorDesktop: + { + WepAddDesktopWindows(Context, Context->Selector.Desktop.DesktopName->Buffer); + } + break; + } + + TreeNew_NodesStructured(Context->TreeNewHandle); + TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); +} + +PPH_STRING WepGetWindowTitleForSelector( + _In_ PWE_WINDOW_SELECTOR Selector + ) +{ + switch (Selector->Type) + { + case WeWindowSelectorAll: + { + return PhCreateString(L"Windows - All"); + } + break; + case WeWindowSelectorThread: + { + return PhFormatString(L"Windows - Thread %lu", HandleToUlong(Selector->Thread.ThreadId)); + } + break; + case WeWindowSelectorProcess: + { + CLIENT_ID clientId; + + clientId.UniqueProcess = Selector->Process.ProcessId; + clientId.UniqueThread = NULL; + + return PhConcatStrings2(L"Windows - ", PH_AUTO_T(PH_STRING, PhGetClientIdName(&clientId))->Buffer); + } + break; + case WeWindowSelectorDesktop: + { + return PhFormatString(L"Windows - Desktop \"%s\"", Selector->Desktop.DesktopName->Buffer); + } + break; + default: + return PhCreateString(L"Windows"); + } +} + +INT_PTR CALLBACK WepWindowsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PWINDOWS_CONTEXT context; + + if (uMsg == WM_INITDIALOG) + { + context = (PWINDOWS_CONTEXT)lParam; + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + + if (uMsg == WM_DESTROY) + PhRemoveWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(WE_PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(WE_PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->SearchBoxHandle = GetDlgItem(hwndDlg, IDC_SEARCHEDIT); + + PhSetWindowText(hwndDlg, PH_AUTO_T(PH_STRING, WepGetWindowTitleForSelector(&context->Selector))->Buffer); + + PhCreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); + WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); + TreeNew_SetEmptyText(context->TreeNewHandle, &WepEmptyWindowsText, 0); + + PhRegisterDialog(hwndDlg); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SEARCHEDIT), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); + + if (PhGetIntegerPairSetting(SETTING_NAME_WINDOWS_WINDOW_POSITION).X != 0) + PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOWS_WINDOW_POSITION, SETTING_NAME_WINDOWS_WINDOW_SIZE, hwndDlg); + else + PhCenterWindow(hwndDlg, WE_PhMainWndHandle); + + WepRefreshWindows(context); + + PhSetDialogFocus(hwndDlg, context->TreeNewHandle); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOWS_WINDOW_POSITION, SETTING_NAME_WINDOWS_WINDOW_SIZE, hwndDlg); + + PhDeleteLayoutManager(&context->LayoutManager); + + PhUnregisterDialog(hwndDlg); + + WeDeleteWindowTree(&context->TreeContext); + WepDeleteWindowSelector(&context->Selector); + PhFree(context); + + PostQuitMessage(0); + } + break; + case PH_SHOWDIALOG: + { + if (IsMinimized(hwndDlg)) + ShowWindow(hwndDlg, SW_RESTORE); + else + ShowWindow(hwndDlg, SW_SHOW); + + SetForegroundWindow(hwndDlg); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != context->SearchBoxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchBoxHandle)); + + if (!PhEqualString(context->TreeContext.SearchboxText, newSearchboxText, FALSE)) + { + PhSwapReference(&context->TreeContext.SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(context->TreeContext.SearchboxText)) + WeExpandAllWindowNodes(&context->TreeContext, TRUE); + + PhApplyTreeNewFilters(&context->TreeContext.FilterSupport); + + TreeNew_NodesStructured(context->TreeNewHandle); + // PhInvokeCallback(&SearchChangedEvent, SearchboxText); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + case IDC_REFRESH: + { + WepRefreshWindows(context); + + PhApplyTreeNewFilters(&context->TreeContext.FilterSupport); + + TreeNew_NodesStructured(context->TreeNewHandle); + } + break; + case ID_SHOWCONTEXTMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PWE_WINDOW_NODE *windows; + ULONG numberOfWindows; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + + WeGetSelectedWindowNodes( + &context->TreeContext, + &windows, + &numberOfWindows + ); + + if (numberOfWindows != 0) + { + menu = PhCreateEMenu(); + PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_WINDOW), 0); + PhInsertCopyCellEMenuItem(menu, ID_WINDOW_COPY, context->TreeNewHandle, contextMenuEvent->Column); + PhSetFlagsEMenuItem(menu, ID_WINDOW_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); + + if (numberOfWindows == 1) + { + WINDOWPLACEMENT placement = { sizeof(placement) }; + BYTE alpha; + ULONG flags; + ULONG i; + ULONG id; + + // State + + GetWindowPlacement(windows[0]->WindowHandle, &placement); + + if (placement.showCmd == SW_MINIMIZE) + PhSetFlagsEMenuItem(menu, ID_WINDOW_MINIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + else if (placement.showCmd == SW_MAXIMIZE) + PhSetFlagsEMenuItem(menu, ID_WINDOW_MAXIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + else if (placement.showCmd == SW_NORMAL) + PhSetFlagsEMenuItem(menu, ID_WINDOW_RESTORE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + + // Visible + + PhSetFlagsEMenuItem(menu, ID_WINDOW_VISIBLE, PH_EMENU_CHECKED, + (GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_VISIBLE) ? PH_EMENU_CHECKED : 0); + + // Enabled + + PhSetFlagsEMenuItem(menu, ID_WINDOW_ENABLED, PH_EMENU_CHECKED, + !(GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_DISABLED) ? PH_EMENU_CHECKED : 0); + + // Always on Top + + PhSetFlagsEMenuItem(menu, ID_WINDOW_ALWAYSONTOP, PH_EMENU_CHECKED, + (GetWindowLong(windows[0]->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) ? PH_EMENU_CHECKED : 0); + + // Opacity + + if (GetLayeredWindowAttributes(windows[0]->WindowHandle, NULL, &alpha, &flags)) + { + if (!(flags & LWA_ALPHA)) + alpha = 255; + } + else + { + alpha = 255; + } + + if (alpha == 255) + { + id = ID_OPACITY_OPAQUE; + } + else + { + id = 0; + + // Due to integer division, we cannot use simple arithmetic to calculate which menu item to check. + for (i = 0; i < 10; i++) + { + if (alpha == (BYTE)(255 * (i + 1) / 10)) + { + id = ID_OPACITY_10 + i; + break; + } + } + } + + if (id != 0) + { + PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK, + PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK); + } + } + else + { + PhSetFlagsAllEMenuItems(menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhSetFlagsEMenuItem(menu, ID_WINDOW_COPY, PH_EMENU_DISABLED, 0); + } + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + + PhDestroyEMenu(menu); + } + } + break; + case ID_WINDOW_BRINGTOFRONT: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + WINDOWPLACEMENT placement = { sizeof(WINDOWPLACEMENT) }; + + if (!GetWindowPlacement(selectedNode->WindowHandle, &placement)) + break; + + if (placement.showCmd == SW_SHOWMINIMIZED || placement.showCmd == SW_MINIMIZE) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); + } + + SetForegroundWindow(selectedNode->WindowHandle); + } + } + break; + case ID_WINDOW_RESTORE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); + } + } + break; + case ID_WINDOW_MINIMIZE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_MINIMIZE); + } + } + break; + case ID_WINDOW_MAXIMIZE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_MAXIMIZE); + } + } + break; + case ID_WINDOW_CLOSE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + PostMessage(selectedNode->WindowHandle, WM_CLOSE, 0, 0); + } + } + break; + case ID_WINDOW_VISIBLE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + if (IsWindowVisible(selectedNode->WindowHandle)) + { + selectedNode->WindowVisible = FALSE; + ShowWindowAsync(selectedNode->WindowHandle, SW_HIDE); + } + else + { + selectedNode->WindowVisible = TRUE; + ShowWindowAsync(selectedNode->WindowHandle, SW_SHOW); + } + + PhInvalidateTreeNewNode(&selectedNode->Node, TN_CACHE_COLOR); + TreeNew_InvalidateNode(context->TreeNewHandle, &selectedNode->Node); + } + } + break; + case ID_WINDOW_ENABLED: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + EnableWindow(selectedNode->WindowHandle, !IsWindowEnabled(selectedNode->WindowHandle)); + } + } + break; + case ID_WINDOW_ALWAYSONTOP: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + LOGICAL topMost; + + topMost = GetWindowLong(selectedNode->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST; + SetWindowPos(selectedNode->WindowHandle, topMost ? HWND_NOTOPMOST : HWND_TOPMOST, + 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + } + } + break; + case ID_OPACITY_10: + case ID_OPACITY_20: + case ID_OPACITY_30: + case ID_OPACITY_40: + case ID_OPACITY_50: + case ID_OPACITY_60: + case ID_OPACITY_70: + case ID_OPACITY_80: + case ID_OPACITY_90: + case ID_OPACITY_OPAQUE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ULONG opacity; + + opacity = ((ULONG)LOWORD(wParam) - ID_OPACITY_10) + 1; + + if (opacity == 10) + { + // Remove the WS_EX_LAYERED bit since it is not needed. + PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, 0); + RedrawWindow(selectedNode->WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); + } + else + { + // Add the WS_EX_LAYERED bit so opacity will work. + PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED); + SetLayeredWindowAttributes(selectedNode->WindowHandle, 0, (BYTE)(255 * opacity / 10), LWA_ALPHA); + } + } + } + break; + case ID_WINDOW_HIGHLIGHT: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + if (context->HighlightingWindow) + { + if (context->HighlightingWindowCount & 1) + WeInvertWindowBorder(context->HighlightingWindow); + } + + context->HighlightingWindow = selectedNode->WindowHandle; + context->HighlightingWindowCount = 10; + SetTimer(hwndDlg, 9, 100, NULL); + } + } + break; + case ID_WINDOW_GOTOTHREAD: + { + PWE_WINDOW_NODE selectedNode; + PPH_PROCESS_ITEM processItem; + PPH_PROCESS_PROPCONTEXT propContext; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + if (processItem = PhReferenceProcessItem(selectedNode->ClientId.UniqueProcess)) + { + if (propContext = PhCreateProcessPropContext(WE_PhMainWndHandle, processItem)) + { + PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ClientId.UniqueThread); + PhShowProcessProperties(propContext); + PhDereferenceObject(propContext); + } + + PhDereferenceObject(processItem); + } + else + { + PhShowError(hwndDlg, L"The process does not exist."); + } + } + } + break; + case ID_WINDOW_PROPERTIES: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + WeShowWindowProperties(hwndDlg, selectedNode->WindowHandle); + } + break; + case ID_WINDOW_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(hwndDlg, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + break; + case WM_TIMER: + { + switch (wParam) + { + case 9: + { + WeInvertWindowBorder(context->HighlightingWindow); + + if (--context->HighlightingWindowCount == 0) + KillTimer(hwndDlg, 9); + } + break; + } + } + break; + case WM_SIZE: + { + PhLayoutManagerLayout(&context->LayoutManager); + } + break; + } + + return FALSE; +} + +INT_PTR CALLBACK WepWindowsPageProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PWINDOWS_CONTEXT context; + PPH_PROCESS_PROPPAGECONTEXT propPageContext; + PPH_PROCESS_ITEM processItem; + + if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &propPageContext, &processItem)) + { + context = propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_LIST); + context->SearchBoxHandle = GetDlgItem(hwndDlg, IDC_SEARCHEDIT); + + PhCreateSearchControl(hwndDlg, context->SearchBoxHandle, L"Search Windows (Ctrl+K)"); + WeInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext); + TreeNew_SetEmptyText(context->TreeNewHandle, &WepEmptyWindowsText, 0); + + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SEARCHEDIT), NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL, PH_ANCHOR_ALL); + + WepRefreshWindows(context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhDeleteLayoutManager(&context->LayoutManager); + + WeDeleteWindowTree(&context->TreeContext); + WepDeleteWindowSelector(&context->Selector); + PhFree(context); + } + break; + case WM_SHOWWINDOW: + { + if (PhBeginPropPageLayout(hwndDlg, propPageContext)) + PhEndPropPageLayout(hwndDlg, propPageContext); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + if (GET_WM_COMMAND_HWND(wParam, lParam) != context->SearchBoxHandle) + break; + + newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchBoxHandle)); + + if (!PhEqualString(context->TreeContext.SearchboxText, newSearchboxText, FALSE)) + { + PhSwapReference(&context->TreeContext.SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(context->TreeContext.SearchboxText)) + WeExpandAllWindowNodes(&context->TreeContext, TRUE); + + PhApplyTreeNewFilters(&context->TreeContext.FilterSupport); + + TreeNew_NodesStructured(context->TreeNewHandle); + // PhInvokeCallback(&SearchChangedEvent, SearchboxText); + } + } + break; + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_REFRESH: + { + WepRefreshWindows(context); + + PhApplyTreeNewFilters(&context->TreeContext.FilterSupport); + + TreeNew_NodesStructured(context->TreeNewHandle); + } + break; + case ID_SHOWCONTEXTMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PWE_WINDOW_NODE *windows; + ULONG numberOfWindows; + PPH_EMENU menu; + PPH_EMENU selectedItem; + + WeGetSelectedWindowNodes( + &context->TreeContext, + &windows, + &numberOfWindows + ); + + if (numberOfWindows != 0) + { + menu = PhCreateEMenu(); + PhLoadResourceEMenuItem(menu, PluginInstance->DllBase, MAKEINTRESOURCE(IDR_WINDOW), 0); + PhInsertCopyCellEMenuItem(menu, ID_WINDOW_COPY, context->TreeNewHandle, contextMenuEvent->Column); + PhSetFlagsEMenuItem(menu, ID_WINDOW_PROPERTIES, PH_EMENU_DEFAULT, PH_EMENU_DEFAULT); + + if (numberOfWindows == 1) + { + WINDOWPLACEMENT placement = { sizeof(placement) }; + BYTE alpha; + ULONG flags; + ULONG i; + ULONG id; + + // State + + GetWindowPlacement(windows[0]->WindowHandle, &placement); + + if (placement.showCmd == SW_MINIMIZE) + PhSetFlagsEMenuItem(menu, ID_WINDOW_MINIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + else if (placement.showCmd == SW_MAXIMIZE) + PhSetFlagsEMenuItem(menu, ID_WINDOW_MAXIMIZE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + else if (placement.showCmd == SW_NORMAL) + PhSetFlagsEMenuItem(menu, ID_WINDOW_RESTORE, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + + // Visible + + PhSetFlagsEMenuItem(menu, ID_WINDOW_VISIBLE, PH_EMENU_CHECKED, + (GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_VISIBLE) ? PH_EMENU_CHECKED : 0); + + // Enabled + + PhSetFlagsEMenuItem(menu, ID_WINDOW_ENABLED, PH_EMENU_CHECKED, + !(GetWindowLong(windows[0]->WindowHandle, GWL_STYLE) & WS_DISABLED) ? PH_EMENU_CHECKED : 0); + + // Always on Top + + PhSetFlagsEMenuItem(menu, ID_WINDOW_ALWAYSONTOP, PH_EMENU_CHECKED, + (GetWindowLong(windows[0]->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) ? PH_EMENU_CHECKED : 0); + + // Opacity + + if (GetLayeredWindowAttributes(windows[0]->WindowHandle, NULL, &alpha, &flags)) + { + if (!(flags & LWA_ALPHA)) + alpha = 255; + } + else + { + alpha = 255; + } + + if (alpha == 255) + { + id = ID_OPACITY_OPAQUE; + } + else + { + id = 0; + + // Due to integer division, we cannot use simple arithmetic to calculate which menu item to check. + for (i = 0; i < 10; i++) + { + if (alpha == (BYTE)(255 * (i + 1) / 10)) + { + id = ID_OPACITY_10 + i; + break; + } + } + } + + if (id != 0) + { + PhSetFlagsEMenuItem(menu, id, PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK, + PH_EMENU_CHECKED | PH_EMENU_RADIOCHECK); + } + } + else + { + PhSetFlagsAllEMenuItems(menu, PH_EMENU_DISABLED, PH_EMENU_DISABLED); + PhSetFlagsEMenuItem(menu, ID_WINDOW_COPY, PH_EMENU_DISABLED, 0); + } + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + } + + PhDestroyEMenu(menu); + } + } + break; + case ID_WINDOW_BRINGTOFRONT: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + WINDOWPLACEMENT placement = { sizeof(placement) }; + + GetWindowPlacement(selectedNode->WindowHandle, &placement); + + if (placement.showCmd == SW_MINIMIZE) + ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); + else + SetForegroundWindow(selectedNode->WindowHandle); + } + } + break; + case ID_WINDOW_RESTORE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_RESTORE); + } + } + break; + case ID_WINDOW_MINIMIZE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_MINIMIZE); + } + } + break; + case ID_WINDOW_MAXIMIZE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ShowWindowAsync(selectedNode->WindowHandle, SW_MAXIMIZE); + } + } + break; + case ID_WINDOW_CLOSE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + PostMessage(selectedNode->WindowHandle, WM_CLOSE, 0, 0); + } + } + break; + case ID_WINDOW_VISIBLE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + if (IsWindowVisible(selectedNode->WindowHandle)) + { + selectedNode->WindowVisible = FALSE; + ShowWindowAsync(selectedNode->WindowHandle, SW_HIDE); + } + else + { + selectedNode->WindowVisible = TRUE; + ShowWindowAsync(selectedNode->WindowHandle, SW_SHOW); + } + + PhInvalidateTreeNewNode(&selectedNode->Node, TN_CACHE_COLOR); + TreeNew_InvalidateNode(context->TreeNewHandle, &selectedNode->Node); + } + } + break; + case ID_WINDOW_ENABLED: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + EnableWindow(selectedNode->WindowHandle, !IsWindowEnabled(selectedNode->WindowHandle)); + } + } + break; + case ID_WINDOW_ALWAYSONTOP: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + LOGICAL topMost; + + topMost = GetWindowLong(selectedNode->WindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST; + SetWindowPos(selectedNode->WindowHandle, topMost ? HWND_NOTOPMOST : HWND_TOPMOST, + 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + } + } + break; + case ID_OPACITY_10: + case ID_OPACITY_20: + case ID_OPACITY_30: + case ID_OPACITY_40: + case ID_OPACITY_50: + case ID_OPACITY_60: + case ID_OPACITY_70: + case ID_OPACITY_80: + case ID_OPACITY_90: + case ID_OPACITY_OPAQUE: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + ULONG opacity; + + opacity = ((ULONG)LOWORD(wParam) - ID_OPACITY_10) + 1; + + if (opacity == 10) + { + // Remove the WS_EX_LAYERED bit since it is not needed. + PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, 0); + RedrawWindow(selectedNode->WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); + } + else + { + // Add the WS_EX_LAYERED bit so opacity will work. + PhSetWindowExStyle(selectedNode->WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED); + SetLayeredWindowAttributes(selectedNode->WindowHandle, 0, (BYTE)(255 * opacity / 10), LWA_ALPHA); + } + } + } + break; + case ID_WINDOW_HIGHLIGHT: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + if (context->HighlightingWindow) + { + if (context->HighlightingWindowCount & 1) + WeInvertWindowBorder(context->HighlightingWindow); + } + + context->HighlightingWindow = selectedNode->WindowHandle; + context->HighlightingWindowCount = 10; + SetTimer(hwndDlg, 9, 100, NULL); + } + } + break; + case ID_WINDOW_GOTOTHREAD: + { + PWE_WINDOW_NODE selectedNode; + PPH_PROCESS_ITEM processItem; + PPH_PROCESS_PROPCONTEXT propContext; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + { + if (processItem = PhReferenceProcessItem(selectedNode->ClientId.UniqueProcess)) + { + if (propContext = PhCreateProcessPropContext(WE_PhMainWndHandle, processItem)) + { + PhSetSelectThreadIdProcessPropContext(propContext, selectedNode->ClientId.UniqueThread); + PhShowProcessProperties(propContext); + PhDereferenceObject(propContext); + } + + PhDereferenceObject(processItem); + } + else + { + PhShowError(hwndDlg, L"The process does not exist."); + } + } + } + break; + case ID_WINDOW_PROPERTIES: + { + PWE_WINDOW_NODE selectedNode; + + if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext)) + WeShowWindowProperties(hwndDlg, selectedNode->WindowHandle); + } + break; + case ID_WINDOW_COPY: + { + PPH_STRING text; + + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(hwndDlg, &text->sr); + PhDereferenceObject(text); + } + break; + } + } + break; + case WM_TIMER: + { + switch (wParam) + { + case 9: + { + WeInvertWindowBorder(context->HighlightingWindow); + + if (--context->HighlightingWindowCount == 0) + KillTimer(hwndDlg, 9); + } + break; + } + } + break; + case WM_SIZE: + PhLayoutManagerLayout(&context->LayoutManager); + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)GetDlgItem(hwndDlg, IDC_REFRESH)); + return TRUE; + } + } + break; + } + + return FALSE; +} diff --git a/plugins/WindowExplorer/wndexp.h b/plugins/WindowExplorer/wndexp.h index 804350ae9030..ba56d60c6004 100644 --- a/plugins/WindowExplorer/wndexp.h +++ b/plugins/WindowExplorer/wndexp.h @@ -1,134 +1,88 @@ -#ifndef WNDEXP_H -#define WNDEXP_H - -#include -#include "wndtree.h" - -extern BOOLEAN IsHookClient; -extern PPH_PLUGIN PluginInstance; - -#define PLUGIN_NAME L"ProcessHacker.WindowExplorer" -#define SETTING_NAME_SHOW_DESKTOP_WINDOWS (PLUGIN_NAME L".ShowDesktopWindows") -#define SETTING_NAME_WINDOW_TREE_LIST_COLUMNS (PLUGIN_NAME L".WindowTreeListColumns") -#define SETTING_NAME_WINDOWS_WINDOW_POSITION (PLUGIN_NAME L".WindowsWindowPosition") -#define SETTING_NAME_WINDOWS_WINDOW_SIZE (PLUGIN_NAME L".WindowsWindowSize") - -// hook - -#define WE_SERVER_MESSAGE_NAME L"WE_ServerMessage" -#define WE_SERVER_SHARED_SECTION_NAME L"\\BaseNamedObjects\\WeSharedSection" -#define WE_SERVER_SHARED_SECTION_LOCK_NAME L"\\BaseNamedObjects\\WeSharedSectionLock" -#define WE_SERVER_SHARED_SECTION_EVENT_NAME L"\\BaseNamedObjects\\WeSharedSectionEvent" -#define WE_CLIENT_MESSAGE_TIMEOUT 2000 - -typedef struct _WE_HOOK_SHARED_DATA -{ - ULONG MessageId; - - struct - { - ULONG_PTR WndProc; - ULONG_PTR DlgProc; - WNDCLASSEX ClassInfo; - } c; -} WE_HOOK_SHARED_DATA, *PWE_HOOK_SHARED_DATA; - -VOID WeHookServerInitialization( - VOID - ); - -VOID WeHookServerUninitialization( - VOID - ); - -BOOLEAN WeIsServerActive( - VOID - ); - -BOOLEAN WeLockServerSharedData( - _Out_ PWE_HOOK_SHARED_DATA *Data - ); - -VOID WeUnlockServerSharedData( - VOID - ); - -BOOLEAN WeSendServerRequest( - _In_ HWND hWnd - ); - -VOID WeHookClientInitialization( - VOID - ); - -VOID WeHookClientUninitialization( - VOID - ); - -// wnddlg - -typedef enum _WE_WINDOW_SELECTOR_TYPE -{ - WeWindowSelectorAll, - WeWindowSelectorProcess, - WeWindowSelectorThread, - WeWindowSelectorDesktop -} WE_WINDOW_SELECTOR_TYPE; - -typedef struct _WE_WINDOW_SELECTOR -{ - WE_WINDOW_SELECTOR_TYPE Type; - union - { - struct - { - HANDLE ProcessId; - } Process; - struct - { - HANDLE ThreadId; - } Thread; - struct - { - PPH_STRING DesktopName; - } Desktop; - }; -} WE_WINDOW_SELECTOR, *PWE_WINDOW_SELECTOR; - -VOID WeShowWindowsDialog( - _In_ HWND ParentWindowHandle, - _In_ PWE_WINDOW_SELECTOR Selector - ); - -VOID WeShowWindowsPropPage( - _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT Context, - _In_ PWE_WINDOW_SELECTOR Selector - ); - -// wndprp - -VOID WeShowWindowProperties( - _In_ HWND ParentWindowHandle, - _In_ HWND WindowHandle - ); - -// utils - -#define WE_PhMainWndHandle (*(HWND *)WeGetProcedureAddress("PhMainWndHandle")) -#define WE_WindowsVersion (*(ULONG *)WeGetProcedureAddress("WindowsVersion")) - -PVOID WeGetProcedureAddress( - _In_ PSTR Name - ); - -VOID WeFormatLocalObjectName( - _In_ PWSTR OriginalName, - _Inout_updates_(256) PWCHAR Buffer, - _Out_ PUNICODE_STRING ObjectName - ); - -VOID WeInvertWindowBorder( - _In_ HWND hWnd - ); - -#endif +#ifndef WNDEXP_H +#define WNDEXP_H + +#include +#include +#include +#include + +#include + +#include "resource.h" +#include "wndtree.h" +#include "prpsh.h" + +extern PPH_PLUGIN PluginInstance; + +#define PLUGIN_NAME L"ProcessHacker.WindowExplorer" +#define SETTING_NAME_SHOW_DESKTOP_WINDOWS (PLUGIN_NAME L".ShowDesktopWindows") +#define SETTING_NAME_WINDOW_TREE_LIST_COLUMNS (PLUGIN_NAME L".WindowTreeListColumns") +#define SETTING_NAME_WINDOWS_WINDOW_POSITION (PLUGIN_NAME L".WindowsWindowPosition") +#define SETTING_NAME_WINDOWS_WINDOW_SIZE (PLUGIN_NAME L".WindowsWindowSize") +#define SETTING_NAME_WINDOWS_PROPERTY_COLUMNS (PLUGIN_NAME L".WindowsPropertyColumns") +#define SETTING_NAME_WINDOWS_PROPERTY_POSITION (PLUGIN_NAME L".WindowsPropertyPosition") +#define SETTING_NAME_WINDOWS_PROPERTY_SIZE (PLUGIN_NAME L".WindowsPropertySize") +#define SETTING_NAME_WINDOWS_PROPLIST_COLUMNS (PLUGIN_NAME L".WindowsPropListColumns") +#define SETTING_NAME_WINDOWS_PROPSTORAGE_COLUMNS (PLUGIN_NAME L".WindowsPropStorageColumns") +// wnddlg + +typedef enum _WE_WINDOW_SELECTOR_TYPE +{ + WeWindowSelectorAll, + WeWindowSelectorProcess, + WeWindowSelectorThread, + WeWindowSelectorDesktop +} WE_WINDOW_SELECTOR_TYPE; + +typedef struct _WE_WINDOW_SELECTOR +{ + WE_WINDOW_SELECTOR_TYPE Type; + union + { + struct + { + HANDLE ProcessId; + } Process; + struct + { + HANDLE ThreadId; + } Thread; + struct + { + PPH_STRING DesktopName; + } Desktop; + }; +} WE_WINDOW_SELECTOR, *PWE_WINDOW_SELECTOR; + +VOID WeShowWindowsDialog( + _In_ HWND ParentWindowHandle, + _In_ PWE_WINDOW_SELECTOR Selector + ); + +VOID WeShowWindowsPropPage( + _In_ PPH_PLUGIN_PROCESS_PROPCONTEXT Context, + _In_ PWE_WINDOW_SELECTOR Selector + ); + +// wndprp + +VOID WeShowWindowProperties( + _In_ HWND ParentWindowHandle, + _In_ HWND WindowHandle + ); + +// utils + +#define WE_PhMainWndHandle (*(HWND *)WeGetProcedureAddress("PhMainWndHandle")) +#define WE_WindowsVersion (*(ULONG *)WeGetProcedureAddress("WindowsVersion")) +#define WE_PhInstanceHandle (*(HINSTANCE *)WeGetProcedureAddress("PhInstanceHandle")) + +PVOID WeGetProcedureAddress( + _In_ PSTR Name + ); + +VOID WeInvertWindowBorder( + _In_ HWND hWnd + ); + +#endif diff --git a/plugins/WindowExplorer/wndprp.c b/plugins/WindowExplorer/wndprp.c index aa71849b3bc5..44d3af50f2f8 100644 --- a/plugins/WindowExplorer/wndprp.c +++ b/plugins/WindowExplorer/wndprp.c @@ -1,1210 +1,1392 @@ -/* - * Process Hacker Window Explorer - - * window properties - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -// Since the main message loop doesn't support property sheets, we need a separate thread to run -// property sheets on. - -#include "wndexp.h" -#include "resource.h" -#include -#include -#include - -#define NUMBER_OF_PAGES 4 -#define WEM_RESOLVE_DONE (WM_APP + 1234) - -typedef struct _WINDOW_PROPERTIES_CONTEXT -{ - LONG RefCount; - - HWND ParentWindowHandle; - - HWND WindowHandle; - CLIENT_ID ClientId; - PH_INITONCE SymbolProviderInitOnce; - PPH_SYMBOL_PROVIDER SymbolProvider; - LIST_ENTRY ResolveListHead; - PH_QUEUED_LOCK ResolveListLock; - - PPH_STRING WndProcSymbol; - ULONG WndProcResolving; - PPH_STRING DlgProcSymbol; - ULONG DlgProcResolving; - PPH_STRING ClassWndProcSymbol; - ULONG ClassWndProcResolving; - - BOOLEAN HookDataValid; - BOOLEAN HookDataSuccess; - ULONG_PTR WndProc; - ULONG_PTR DlgProc; - WNDCLASSEX ClassInfo; -} WINDOW_PROPERTIES_CONTEXT, *PWINDOW_PROPERTIES_CONTEXT; - -typedef struct _SYMBOL_RESOLVE_CONTEXT -{ - LIST_ENTRY ListEntry; - ULONG64 Address; - PPH_STRING Symbol; - PH_SYMBOL_RESOLVE_LEVEL ResolveLevel; - HWND NotifyWindow; - PWINDOW_PROPERTIES_CONTEXT Context; - ULONG Id; -} SYMBOL_RESOLVE_CONTEXT, *PSYMBOL_RESOLVE_CONTEXT; - -typedef struct _STRING_INTEGER_PAIR -{ - PWSTR String; - ULONG Integer; -} STRING_INTEGER_PAIR, *PSTRING_INTEGER_PAIR; - -VOID WepReferenceWindowPropertiesContext( - _Inout_ PWINDOW_PROPERTIES_CONTEXT Context - ); - -VOID WepDereferenceWindowPropertiesContext( - _Inout_ PWINDOW_PROPERTIES_CONTEXT Context - ); - -HWND WepCreateWindowProperties( - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ); - -INT CALLBACK WepPropSheetProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam - ); - -LRESULT CALLBACK WepPropSheetWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -HPROPSHEETPAGE WepCommonCreatePage( - _In_ PWINDOW_PROPERTIES_CONTEXT Context, - _In_ PWSTR Template, - _In_ DLGPROC DlgProc - ); - -INT CALLBACK WepCommonPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ); - -NTSTATUS WepPropertiesThreadStart( - _In_ PVOID Parameter - ); - -INT_PTR CALLBACK WepWindowGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK WepWindowStylesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK WepWindowClassDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK WepWindowPropertiesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -#define DEFINE_PAIR(Symbol) { L#Symbol, Symbol } - -static STRING_INTEGER_PAIR WepStylePairs[] = -{ - DEFINE_PAIR(WS_POPUP), - DEFINE_PAIR(WS_CHILD), - DEFINE_PAIR(WS_MINIMIZE), - DEFINE_PAIR(WS_VISIBLE), - DEFINE_PAIR(WS_DISABLED), - DEFINE_PAIR(WS_CLIPSIBLINGS), - DEFINE_PAIR(WS_CLIPCHILDREN), - DEFINE_PAIR(WS_MAXIMIZE), - DEFINE_PAIR(WS_BORDER), - DEFINE_PAIR(WS_DLGFRAME), - DEFINE_PAIR(WS_VSCROLL), - DEFINE_PAIR(WS_HSCROLL), - DEFINE_PAIR(WS_SYSMENU), - DEFINE_PAIR(WS_THICKFRAME), - DEFINE_PAIR(WS_GROUP), - DEFINE_PAIR(WS_TABSTOP), - DEFINE_PAIR(WS_MINIMIZEBOX), - DEFINE_PAIR(WS_MAXIMIZEBOX) -}; - -static STRING_INTEGER_PAIR WepExtendedStylePairs[] = -{ - DEFINE_PAIR(WS_EX_DLGMODALFRAME), - DEFINE_PAIR(WS_EX_NOPARENTNOTIFY), - DEFINE_PAIR(WS_EX_TOPMOST), - DEFINE_PAIR(WS_EX_ACCEPTFILES), - DEFINE_PAIR(WS_EX_TRANSPARENT), - DEFINE_PAIR(WS_EX_MDICHILD), - DEFINE_PAIR(WS_EX_TOOLWINDOW), - DEFINE_PAIR(WS_EX_WINDOWEDGE), - DEFINE_PAIR(WS_EX_CLIENTEDGE), - DEFINE_PAIR(WS_EX_CONTEXTHELP), - DEFINE_PAIR(WS_EX_RIGHT), - DEFINE_PAIR(WS_EX_RTLREADING), - DEFINE_PAIR(WS_EX_LEFTSCROLLBAR), - DEFINE_PAIR(WS_EX_CONTROLPARENT), - DEFINE_PAIR(WS_EX_STATICEDGE), - DEFINE_PAIR(WS_EX_APPWINDOW), - DEFINE_PAIR(WS_EX_LAYERED), - DEFINE_PAIR(WS_EX_NOINHERITLAYOUT), - DEFINE_PAIR(WS_EX_LAYOUTRTL), - DEFINE_PAIR(WS_EX_COMPOSITED), - DEFINE_PAIR(WS_EX_NOACTIVATE) -}; - -static STRING_INTEGER_PAIR WepClassStylePairs[] = -{ - DEFINE_PAIR(CS_VREDRAW), - DEFINE_PAIR(CS_HREDRAW), - DEFINE_PAIR(CS_DBLCLKS), - DEFINE_PAIR(CS_OWNDC), - DEFINE_PAIR(CS_CLASSDC), - DEFINE_PAIR(CS_PARENTDC), - DEFINE_PAIR(CS_NOCLOSE), - DEFINE_PAIR(CS_SAVEBITS), - DEFINE_PAIR(CS_BYTEALIGNCLIENT), - DEFINE_PAIR(CS_BYTEALIGNWINDOW), - DEFINE_PAIR(CS_GLOBALCLASS), - DEFINE_PAIR(CS_IME), - DEFINE_PAIR(CS_DROPSHADOW) -}; - -HANDLE WePropertiesThreadHandle = NULL; -CLIENT_ID WePropertiesThreadClientId; -PH_EVENT WePropertiesThreadReadyEvent = PH_EVENT_INIT; -PPH_LIST WePropertiesCreateList; -PPH_LIST WePropertiesWindowList; -PH_QUEUED_LOCK WePropertiesCreateLock = PH_QUEUED_LOCK_INIT; - -VOID WeShowWindowProperties( - _In_ HWND ParentWindowHandle, - _In_ HWND WindowHandle - ) -{ - PWINDOW_PROPERTIES_CONTEXT context; - ULONG threadId; - ULONG processId; - - if (!WePropertiesCreateList) - WePropertiesCreateList = PhCreateList(4); - if (!WePropertiesWindowList) - WePropertiesWindowList = PhCreateList(4); - - if (!WePropertiesThreadHandle) - { - WePropertiesThreadHandle = PhCreateThread(0, WepPropertiesThreadStart, NULL); - PhWaitForEvent(&WePropertiesThreadReadyEvent, NULL); - } - - context = PhAllocate(sizeof(WINDOW_PROPERTIES_CONTEXT)); - memset(context, 0, sizeof(WINDOW_PROPERTIES_CONTEXT)); - context->RefCount = 1; - context->ParentWindowHandle = ParentWindowHandle; - context->WindowHandle = WindowHandle; - - processId = 0; - threadId = GetWindowThreadProcessId(WindowHandle, &processId); - context->ClientId.UniqueProcess = UlongToHandle(processId); - context->ClientId.UniqueThread = UlongToHandle(threadId); - PhInitializeInitOnce(&context->SymbolProviderInitOnce); - InitializeListHead(&context->ResolveListHead); - PhInitializeQueuedLock(&context->ResolveListLock); - - // Queue the window for creation and wake up the host thread. - PhAcquireQueuedLockExclusive(&WePropertiesCreateLock); - PhAddItemList(WePropertiesCreateList, context); - PhReleaseQueuedLockExclusive(&WePropertiesCreateLock); - PostThreadMessage(HandleToUlong(WePropertiesThreadClientId.UniqueThread), WM_NULL, 0, 0); -} - -VOID WepReferenceWindowPropertiesContext( - _Inout_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - _InterlockedIncrement(&Context->RefCount); -} - -VOID WepDereferenceWindowPropertiesContext( - _Inout_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - if (_InterlockedDecrement(&Context->RefCount) == 0) - { - PLIST_ENTRY listEntry; - - PhClearReference(&Context->SymbolProvider); - - // Destroy results that have not been processed by any property pages. - - listEntry = Context->ResolveListHead.Flink; - - while (listEntry != &Context->ResolveListHead) - { - PSYMBOL_RESOLVE_CONTEXT resolveContext; - - resolveContext = CONTAINING_RECORD(listEntry, SYMBOL_RESOLVE_CONTEXT, ListEntry); - listEntry = listEntry->Flink; - - PhClearReference(&resolveContext->Symbol); - PhFree(resolveContext); - } - - PhClearReference(&Context->WndProcSymbol); - PhClearReference(&Context->DlgProcSymbol); - PhClearReference(&Context->ClassWndProcSymbol); - - PhFree(Context); - } -} - -static HWND WepCreateWindowProperties( - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - HPROPSHEETPAGE pages[NUMBER_OF_PAGES]; - - propSheetHeader.dwFlags = - PSH_MODELESS | - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE | - PSH_USECALLBACK; - propSheetHeader.hwndParent = Context->ParentWindowHandle; - propSheetHeader.pszCaption = PhaFormatString(L"Window %Ix", Context->WindowHandle)->Buffer; - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - propSheetHeader.pfnCallback = WepPropSheetProc; - - // General - pages[propSheetHeader.nPages++] = WepCommonCreatePage( - Context, - MAKEINTRESOURCE(IDD_WNDGENERAL), - WepWindowGeneralDlgProc - ); - // Styles - pages[propSheetHeader.nPages++] = WepCommonCreatePage( - Context, - MAKEINTRESOURCE(IDD_WNDSTYLES), - WepWindowStylesDlgProc - ); - // Class - pages[propSheetHeader.nPages++] = WepCommonCreatePage( - Context, - MAKEINTRESOURCE(IDD_WNDCLASS), - WepWindowClassDlgProc - ); - // Properties - pages[propSheetHeader.nPages++] = WepCommonCreatePage( - Context, - MAKEINTRESOURCE(IDD_WNDPROPS), - WepWindowPropertiesDlgProc - ); - - return (HWND)PropertySheet(&propSheetHeader); -} - -static INT CALLBACK WepPropSheetProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case PSCB_INITIALIZED: - { - WNDPROC oldWndProc; - HWND refreshButtonHandle; - - oldWndProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); - SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)WepPropSheetWndProc); - SetProp(hwndDlg, L"OldWndProc", (HANDLE)oldWndProc); - - // Hide the Cancel button. - ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE); - // Set the OK button's text to "Close". - SetDlgItemText(hwndDlg, IDOK, L"Close"); - // Add the Refresh button. - refreshButtonHandle = CreateWindow(L"BUTTON", L"Refresh", WS_CHILD | WS_TABSTOP | WS_VISIBLE, 0, 0, 3, 3, hwndDlg, (HMENU)IDC_REFRESH, - PluginInstance->DllBase, NULL); - SendMessage(refreshButtonHandle, WM_SETFONT, (WPARAM)SendMessage(GetDlgItem(hwndDlg, IDOK), WM_GETFONT, 0, 0), FALSE); - } - break; - } - - return 0; -} - -LRESULT CALLBACK WepPropSheetWndProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, L"OldWndProc"); - - switch (uMsg) - { - case WM_DESTROY: - { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); - RemoveProp(hwnd, L"OldWndProc"); - RemoveProp(hwnd, L"Moved"); - } - break; - case WM_SHOWWINDOW: - { - if (!GetProp(hwnd, L"Moved")) - { - // Move the Refresh button to where the OK button is, and move the OK button to - // where the Cancel button is. - // This must be done here because in the prop sheet callback the buttons are not - // in the right places. - PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDOK), GetDlgItem(hwnd, IDC_REFRESH)); - PhCopyControlRectangle(hwnd, GetDlgItem(hwnd, IDCANCEL), GetDlgItem(hwnd, IDOK)); - SetProp(hwnd, L"Moved", (HANDLE)1); - } - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_REFRESH: - { - ULONG i; - HWND pageHandle; - - // Broadcast the message to all property pages. - for (i = 0; i < NUMBER_OF_PAGES; i++) - { - if (pageHandle = PropSheet_IndexToHwnd(hwnd, i)) - SendMessage(pageHandle, WM_COMMAND, IDC_REFRESH, 0); - } - } - break; - } - } - break; - } - - return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam); -} - -static HPROPSHEETPAGE WepCommonCreatePage( - _In_ PWINDOW_PROPERTIES_CONTEXT Context, - _In_ PWSTR Template, - _In_ DLGPROC DlgProc - ) -{ - HPROPSHEETPAGE propSheetPageHandle; - PROPSHEETPAGE propSheetPage; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USECALLBACK; - propSheetPage.hInstance = PluginInstance->DllBase; - propSheetPage.pszTemplate = Template; - propSheetPage.pfnDlgProc = DlgProc; - propSheetPage.lParam = (LPARAM)Context; - propSheetPage.pfnCallback = WepCommonPropPageProc; - - propSheetPageHandle = CreatePropertySheetPage(&propSheetPage); - - return propSheetPageHandle; -} - -static INT CALLBACK WepCommonPropPageProc( - _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ LPPROPSHEETPAGE ppsp - ) -{ - PWINDOW_PROPERTIES_CONTEXT context; - - context = (PWINDOW_PROPERTIES_CONTEXT)ppsp->lParam; - - if (uMsg == PSPCB_ADDREF) - WepReferenceWindowPropertiesContext(context); - else if (uMsg == PSPCB_RELEASE) - WepDereferenceWindowPropertiesContext(context); - - return 1; -} - -NTSTATUS WepPropertiesThreadStart( - _In_ PVOID Parameter - ) -{ - PH_AUTO_POOL autoPool; - BOOL result; - MSG message; - BOOLEAN processed; - ULONG i; - - PhInitializeAutoPool(&autoPool); - - WePropertiesThreadClientId = NtCurrentTeb()->ClientId; - - // Force the creation of the message queue so PostThreadMessage works. - PeekMessage(&message, NULL, WM_USER, WM_USER, PM_NOREMOVE); - PhSetEvent(&WePropertiesThreadReadyEvent); - - while (result = GetMessage(&message, NULL, 0, 0)) - { - if (result == -1) - break; - - if (WePropertiesCreateList->Count != 0) - { - PhAcquireQueuedLockExclusive(&WePropertiesCreateLock); - - for (i = 0; i < WePropertiesCreateList->Count; i++) - { - PWINDOW_PROPERTIES_CONTEXT context; - HWND hwnd; - - context = WePropertiesCreateList->Items[i]; - hwnd = WepCreateWindowProperties(context); - WepDereferenceWindowPropertiesContext(context); - PhAddItemList(WePropertiesWindowList, hwnd); - } - - PhClearList(WePropertiesCreateList); - PhReleaseQueuedLockExclusive(&WePropertiesCreateLock); - } - - processed = FALSE; - - for (i = 0; i < WePropertiesWindowList->Count; i++) - { - if (PropSheet_IsDialogMessage(WePropertiesWindowList->Items[i], &message)) - { - processed = TRUE; - break; - } - } - - if (!processed) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - - // Destroy properties windows when necessary. - for (i = 0; i < WePropertiesWindowList->Count; i++) - { - if (!PropSheet_GetCurrentPageHwnd(WePropertiesWindowList->Items[i])) - { - DestroyWindow(WePropertiesWindowList->Items[i]); - PhRemoveItemList(WePropertiesWindowList, i); - i--; - } - } - - PhDrainAutoPool(&autoPool); - } - - PhDeleteAutoPool(&autoPool); - - return STATUS_SUCCESS; -} - -FORCEINLINE BOOLEAN WepPropPageDlgProcHeader( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ LPARAM lParam, - _Out_opt_ LPPROPSHEETPAGE *PropSheetPage, - _Out_opt_ PWINDOW_PROPERTIES_CONTEXT *Context - ) -{ - LPPROPSHEETPAGE propSheetPage; - - if (uMsg == WM_INITDIALOG) - { - propSheetPage = (LPPROPSHEETPAGE)lParam; - // Save the context. - SetProp(hwndDlg, L"PropSheetPage", (HANDLE)lParam); - } - else - { - propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, L"PropSheetPage"); - - if (uMsg == WM_DESTROY) - RemoveProp(hwndDlg, L"PropSheetPage"); - } - - if (!propSheetPage) - return FALSE; - - if (PropSheetPage) - *PropSheetPage = propSheetPage; - if (Context) - *Context = (PWINDOW_PROPERTIES_CONTEXT)propSheetPage->lParam; - - return TRUE; -} - -static VOID WepEnsureHookDataValid( - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - if (!Context->HookDataValid) - { - PWE_HOOK_SHARED_DATA data; -#ifdef _WIN64 - HANDLE processHandle; - BOOLEAN isWow64 = FALSE; -#endif - - // The desktop window is owned by CSR. The hook will never work on the desktop window. - if (Context->WindowHandle == GetDesktopWindow()) - { - Context->HookDataValid = TRUE; - return; - } - -#ifdef _WIN64 - // We can't use the hook on WOW64 processes. - if (NT_SUCCESS(PhOpenProcess(&processHandle, *(PULONG)WeGetProcedureAddress("ProcessQueryAccess"), Context->ClientId.UniqueProcess))) - { - PhGetProcessIsWow64(processHandle, &isWow64); - NtClose(processHandle); - } - - if (isWow64) - return; -#endif - - WeHookServerInitialization(); - - Context->HookDataSuccess = FALSE; - - if (WeLockServerSharedData(&data)) - { - if (WeSendServerRequest(Context->WindowHandle)) - { - Context->WndProc = data->c.WndProc; - Context->DlgProc = data->c.DlgProc; - memcpy(&Context->ClassInfo, &data->c.ClassInfo, sizeof(WNDCLASSEX)); - Context->HookDataSuccess = TRUE; - } - - WeUnlockServerSharedData(); - } - - Context->HookDataValid = TRUE; - } -} - -static BOOLEAN NTAPI EnumGenericModulesCallback( - _In_ PPH_MODULE_INFO Module, - _In_opt_ PVOID Context - ) -{ - PWINDOW_PROPERTIES_CONTEXT context = Context; - - PhLoadModuleSymbolProvider(context->SymbolProvider, Module->FileName->Buffer, - (ULONG64)Module->BaseAddress, Module->Size); - - return TRUE; -} - -static NTSTATUS WepResolveSymbolFunction( - _In_ PVOID Parameter - ) -{ - PSYMBOL_RESOLVE_CONTEXT context = Parameter; - - if (PhBeginInitOnce(&context->Context->SymbolProviderInitOnce)) - { - PhEnumGenericModules(context->Context->ClientId.UniqueProcess, NULL, 0, EnumGenericModulesCallback, context->Context); - PhEndInitOnce(&context->Context->SymbolProviderInitOnce); - } - - context->Symbol = PhGetSymbolFromAddress( - context->Context->SymbolProvider, - (ULONG64)context->Address, - &context->ResolveLevel, - NULL, - NULL, - NULL - ); - - // Fail if we don't have a symbol. - if (!context->Symbol) - { - WepDereferenceWindowPropertiesContext(context->Context); - PhFree(context); - return STATUS_SUCCESS; - } - - PhAcquireQueuedLockExclusive(&context->Context->ResolveListLock); - InsertHeadList(&context->Context->ResolveListHead, &context->ListEntry); - PhReleaseQueuedLockExclusive(&context->Context->ResolveListLock); - - PostMessage(context->NotifyWindow, WEM_RESOLVE_DONE, 0, (LPARAM)context); - - WepDereferenceWindowPropertiesContext(context->Context); - - return STATUS_SUCCESS; -} - -static VOID WepQueueResolveSymbol( - _In_ PWINDOW_PROPERTIES_CONTEXT Context, - _In_ HWND NotifyWindow, - _In_ ULONG64 Address, - _In_ ULONG Id - ) -{ - PSYMBOL_RESOLVE_CONTEXT resolveContext; - - if (!Context->SymbolProvider) - { - Context->SymbolProvider = PhCreateSymbolProvider(Context->ClientId.UniqueProcess); - PhLoadSymbolProviderOptions(Context->SymbolProvider); - } - - resolveContext = PhAllocate(sizeof(SYMBOL_RESOLVE_CONTEXT)); - resolveContext->Address = Address; - resolveContext->Symbol = NULL; - resolveContext->ResolveLevel = PhsrlInvalid; - resolveContext->NotifyWindow = NotifyWindow; - resolveContext->Context = Context; - WepReferenceWindowPropertiesContext(Context); - resolveContext->Id = Id; - - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), WepResolveSymbolFunction, resolveContext); -} - -static PPH_STRING WepFormatRect( - _In_ PRECT Rect - ) -{ - return PhaFormatString(L"(%d, %d) - (%d, %d) [%dx%d]", - Rect->left, Rect->top, Rect->right, Rect->bottom, - Rect->right - Rect->left, Rect->bottom - Rect->top); -} - -static VOID WepRefreshWindowGeneralInfoSymbols( - _In_ HWND hwndDlg, - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - if (Context->WndProcResolving != 0) - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->WndProc)->Buffer); - else if (Context->WndProcSymbol) - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (%s)", Context->WndProc, Context->WndProcSymbol->Buffer)->Buffer); - else if (Context->WndProc != 0) - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix", Context->WndProc)->Buffer); - else - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, L"Unknown"); - - if (Context->DlgProcResolving != 0) - SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->DlgProc)->Buffer); - else if (Context->DlgProcSymbol) - SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix (%s)", Context->DlgProc, Context->DlgProcSymbol->Buffer)->Buffer); - else if (Context->DlgProc != 0) - SetDlgItemText(hwndDlg, IDC_DIALOGPROC, PhaFormatString(L"0x%Ix", Context->DlgProc)->Buffer); - else if (Context->WndProc != 0) - SetDlgItemText(hwndDlg, IDC_DIALOGPROC, L"N/A"); - else - SetDlgItemText(hwndDlg, IDC_DIALOGPROC, L"Unknown"); -} - -static VOID WepRefreshWindowGeneralInfo( - _In_ HWND hwndDlg, - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - WINDOWINFO windowInfo = { sizeof(WINDOWINFO) }; - WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT) }; - MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; - - SetDlgItemText(hwndDlg, IDC_THREAD, PH_AUTO_T(PH_STRING, PhGetClientIdName(&Context->ClientId))->Buffer); - SetDlgItemText(hwndDlg, IDC_TEXT, PhGetStringOrEmpty(PH_AUTO(PhGetWindowText(Context->WindowHandle)))); - - if (GetWindowInfo(Context->WindowHandle, &windowInfo)) - { - SetDlgItemText(hwndDlg, IDC_RECTANGLE, WepFormatRect(&windowInfo.rcWindow)->Buffer); - SetDlgItemText(hwndDlg, IDC_CLIENTRECTANGLE, WepFormatRect(&windowInfo.rcClient)->Buffer); - } - else - { - SetDlgItemText(hwndDlg, IDC_RECTANGLE, L"N/A"); - SetDlgItemText(hwndDlg, IDC_CLIENTRECTANGLE, L"N/A"); - } - - if (GetWindowPlacement(Context->WindowHandle, &windowPlacement)) - { - // The rectangle is in workspace coordinates. Convert the values back to screen coordinates. - if (GetMonitorInfo(MonitorFromRect(&windowPlacement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo)) - { - windowPlacement.rcNormalPosition.left += monitorInfo.rcWork.left; - windowPlacement.rcNormalPosition.top += monitorInfo.rcWork.top; - windowPlacement.rcNormalPosition.right += monitorInfo.rcWork.left; - windowPlacement.rcNormalPosition.bottom += monitorInfo.rcWork.top; - } - - SetDlgItemText(hwndDlg, IDC_NORMALRECTANGLE, WepFormatRect(&windowPlacement.rcNormalPosition)->Buffer); - } - else - { - SetDlgItemText(hwndDlg, IDC_NORMALRECTANGLE, L"N/A"); - } - - SetDlgItemText(hwndDlg, IDC_INSTANCEHANDLE, PhaFormatString(L"0x%Ix", GetWindowLongPtr(Context->WindowHandle, GWLP_HINSTANCE))->Buffer); - SetDlgItemText(hwndDlg, IDC_MENUHANDLE, PhaFormatString(L"0x%Ix", GetMenu(Context->WindowHandle))->Buffer); - SetDlgItemText(hwndDlg, IDC_USERDATA, PhaFormatString(L"0x%Ix", GetWindowLongPtr(Context->WindowHandle, GWLP_USERDATA))->Buffer); - SetDlgItemText(hwndDlg, IDC_UNICODE, IsWindowUnicode(Context->WindowHandle) ? L"Yes" : L"No"); - SetDlgItemText(hwndDlg, IDC_CTRLID, PhaFormatString(L"%lu", GetWindowLongPtr(Context->WindowHandle, GWLP_ID))->Buffer); - - WepEnsureHookDataValid(Context); - - if (Context->WndProc != 0) - { - Context->WndProcResolving++; - WepQueueResolveSymbol(Context, hwndDlg, Context->WndProc, 1); - } - - if (Context->DlgProc != 0) - { - Context->DlgProcResolving++; - WepQueueResolveSymbol(Context, hwndDlg, Context->DlgProc, 2); - } - - WepRefreshWindowGeneralInfoSymbols(hwndDlg, Context); -} - -INT_PTR CALLBACK WepWindowGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWINDOW_PROPERTIES_CONTEXT context; - - if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - // HACK - PhCenterWindow(GetParent(hwndDlg), GetParent(GetParent(hwndDlg))); - - WepRefreshWindowGeneralInfo(hwndDlg, context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_REFRESH: - context->HookDataValid = FALSE; - PhClearReference(&context->WndProcSymbol); - WepRefreshWindowGeneralInfo(hwndDlg, context); - break; - } - } - break; - case WEM_RESOLVE_DONE: - { - PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam; - - if (resolveContext->Id == 1) - { - PhAcquireQueuedLockExclusive(&context->ResolveListLock); - RemoveEntryList(&resolveContext->ListEntry); - PhReleaseQueuedLockExclusive(&context->ResolveListLock); - - if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) - PhClearReference(&resolveContext->Symbol); - - PhMoveReference(&context->WndProcSymbol, resolveContext->Symbol); - PhFree(resolveContext); - - context->WndProcResolving--; - } - else if (resolveContext->Id == 2) - { - PhAcquireQueuedLockExclusive(&context->ResolveListLock); - RemoveEntryList(&resolveContext->ListEntry); - PhReleaseQueuedLockExclusive(&context->ResolveListLock); - - if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) - PhClearReference(&resolveContext->Symbol); - - PhMoveReference(&context->DlgProcSymbol, resolveContext->Symbol); - PhFree(resolveContext); - - context->DlgProcResolving--; - } - - WepRefreshWindowGeneralInfoSymbols(hwndDlg, context); - } - break; - } - - return FALSE; -} - -static VOID WepRefreshWindowStyles( - _In_ HWND hwndDlg, - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - WINDOWINFO windowInfo = { sizeof(WINDOWINFO) }; - HWND stylesListBox; - HWND extendedStylesListBox; - ULONG i; - - stylesListBox = GetDlgItem(hwndDlg, IDC_STYLESLIST); - extendedStylesListBox = GetDlgItem(hwndDlg, IDC_EXTENDEDSTYLESLIST); - - ListBox_ResetContent(stylesListBox); - ListBox_ResetContent(extendedStylesListBox); - - if (GetWindowInfo(Context->WindowHandle, &windowInfo)) - { - SetDlgItemText(hwndDlg, IDC_STYLES, PhaFormatString(L"0x%x", windowInfo.dwStyle)->Buffer); - SetDlgItemText(hwndDlg, IDC_EXTENDEDSTYLES, PhaFormatString(L"0x%x", windowInfo.dwExStyle)->Buffer); - - for (i = 0; i < sizeof(WepStylePairs) / sizeof(STRING_INTEGER_PAIR); i++) - { - if (windowInfo.dwStyle & WepStylePairs[i].Integer) - { - // Skip irrelevant styles. - - if (WepStylePairs[i].Integer == WS_MAXIMIZEBOX || - WepStylePairs[i].Integer == WS_MINIMIZEBOX) - { - if (windowInfo.dwStyle & WS_CHILD) - continue; - } - - if (WepStylePairs[i].Integer == WS_TABSTOP || - WepStylePairs[i].Integer == WS_GROUP) - { - if (!(windowInfo.dwStyle & WS_CHILD)) - continue; - } - - ListBox_AddString(stylesListBox, WepStylePairs[i].String); - } - } - - for (i = 0; i < sizeof(WepExtendedStylePairs) / sizeof(STRING_INTEGER_PAIR); i++) - { - if (windowInfo.dwExStyle & WepExtendedStylePairs[i].Integer) - { - ListBox_AddString(extendedStylesListBox, WepExtendedStylePairs[i].String); - } - } - } - else - { - SetDlgItemText(hwndDlg, IDC_STYLES, L"N/A"); - SetDlgItemText(hwndDlg, IDC_EXTENDEDSTYLES, L"N/A"); - } -} - -INT_PTR CALLBACK WepWindowStylesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWINDOW_PROPERTIES_CONTEXT context; - - if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - WepRefreshWindowStyles(hwndDlg, context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_REFRESH: - WepRefreshWindowStyles(hwndDlg, context); - break; - } - } - break; - } - - return FALSE; -} - -static VOID WepRefreshWindowClassInfoSymbols( - _In_ HWND hwndDlg, - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - if (Context->ClassWndProcResolving != 0) - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (resolving...)", Context->ClassInfo.lpfnWndProc)->Buffer); - else if (Context->ClassWndProcSymbol) - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix (%s)", Context->ClassInfo.lpfnWndProc, Context->ClassWndProcSymbol->Buffer)->Buffer); - else if (Context->ClassInfo.lpfnWndProc) - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpfnWndProc)->Buffer); - else - SetDlgItemText(hwndDlg, IDC_WINDOWPROC, L"Unknown"); -} - -static VOID WepRefreshWindowClassInfo( - _In_ HWND hwndDlg, - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - WCHAR className[256]; - PH_STRING_BUILDER stringBuilder; - ULONG i; - - if (!GetClassName(Context->WindowHandle, className, sizeof(className) / sizeof(WCHAR))) - className[0] = 0; - - WepEnsureHookDataValid(Context); - - if (!Context->HookDataSuccess) - { - Context->ClassInfo.cbSize = sizeof(WNDCLASSEX); - GetClassInfoEx(NULL, className, &Context->ClassInfo); - } - - SetDlgItemText(hwndDlg, IDC_NAME, className); - SetDlgItemText(hwndDlg, IDC_ATOM, PhaFormatString(L"0x%x", GetClassWord(Context->WindowHandle, GCW_ATOM))->Buffer); - SetDlgItemText(hwndDlg, IDC_INSTANCEHANDLE, PhaFormatString(L"0x%Ix", GetClassLongPtr(Context->WindowHandle, GCLP_HMODULE))->Buffer); - SetDlgItemText(hwndDlg, IDC_ICONHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIcon)->Buffer); - SetDlgItemText(hwndDlg, IDC_SMALLICONHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIconSm)->Buffer); - SetDlgItemText(hwndDlg, IDC_MENUNAME, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpszMenuName)->Buffer); - - PhInitializeStringBuilder(&stringBuilder, 100); - PhAppendFormatStringBuilder(&stringBuilder, L"0x%x (", Context->ClassInfo.style); - - for (i = 0; i < sizeof(WepClassStylePairs) / sizeof(STRING_INTEGER_PAIR); i++) - { - if (Context->ClassInfo.style & WepClassStylePairs[i].Integer) - { - PhAppendStringBuilder2(&stringBuilder, WepClassStylePairs[i].String); - PhAppendStringBuilder2(&stringBuilder, L" | "); - } - } - - if (PhEndsWithString2(stringBuilder.String, L" | ", FALSE)) - { - PhRemoveEndStringBuilder(&stringBuilder, 3); - PhAppendCharStringBuilder(&stringBuilder, ')'); - } - else - { - // No styles. Remove the brackets. - PhRemoveEndStringBuilder(&stringBuilder, 1); - } - - SetDlgItemText(hwndDlg, IDC_STYLES, stringBuilder.String->Buffer); - PhDeleteStringBuilder(&stringBuilder); - - // TODO: Add symbols for these values. - SetDlgItemText(hwndDlg, IDC_CURSORHANDLE, PhaFormatString(L"0x%Ix", Context->ClassInfo.hCursor)->Buffer); - SetDlgItemText(hwndDlg, IDC_BACKGROUNDBRUSH, PhaFormatString(L"0x%Ix", Context->ClassInfo.hbrBackground)->Buffer); - - if (Context->ClassInfo.lpfnWndProc) - { - Context->ClassWndProcResolving++; - WepQueueResolveSymbol(Context, hwndDlg, (ULONG_PTR)Context->ClassInfo.lpfnWndProc, 0); - } - - WepRefreshWindowClassInfoSymbols(hwndDlg, Context); -} - -INT_PTR CALLBACK WepWindowClassDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWINDOW_PROPERTIES_CONTEXT context; - - if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - WepRefreshWindowClassInfo(hwndDlg, context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_REFRESH: - context->HookDataValid = FALSE; - PhClearReference(&context->ClassWndProcSymbol); - WepRefreshWindowClassInfo(hwndDlg, context); - break; - } - } - break; - case WEM_RESOLVE_DONE: - { - PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam; - - PhAcquireQueuedLockExclusive(&context->ResolveListLock); - RemoveEntryList(&resolveContext->ListEntry); - PhReleaseQueuedLockExclusive(&context->ResolveListLock); - - if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) - PhClearReference(&resolveContext->Symbol); - - PhMoveReference(&context->ClassWndProcSymbol, resolveContext->Symbol); - PhFree(resolveContext); - - context->ClassWndProcResolving--; - WepRefreshWindowClassInfoSymbols(hwndDlg, context); - } - break; - } - - return FALSE; -} - -static BOOL CALLBACK EnumPropsExCallback( - _In_ HWND hwnd, - _In_ LPTSTR lpszString, - _In_ HANDLE hData, - _In_ ULONG_PTR dwData - ) -{ - INT lvItemIndex; - PWSTR propName; - WCHAR value[PH_PTR_STR_LEN_1]; - - propName = lpszString; - - if ((ULONG_PTR)lpszString < USHRT_MAX) - { - // This is an integer atom. - propName = PhaFormatString(L"#%lu", (ULONG_PTR)lpszString)->Buffer; - } - - lvItemIndex = PhAddListViewItem((HWND)dwData, MAXINT, propName, NULL); - - PhPrintPointer(value, (PVOID)hData); - PhSetListViewSubItem((HWND)dwData, lvItemIndex, 1, value); - - return TRUE; -} - -static VOID WepRefreshWindowProps( - _In_ HWND hwndDlg, - _In_ HWND ListViewHandle, - _In_ PWINDOW_PROPERTIES_CONTEXT Context - ) -{ - ExtendedListView_SetRedraw(ListViewHandle, FALSE); - ListView_DeleteAllItems(ListViewHandle); - EnumPropsEx(Context->WindowHandle, EnumPropsExCallback, (LPARAM)ListViewHandle); - ExtendedListView_SortItems(ListViewHandle); - ExtendedListView_SetRedraw(ListViewHandle, TRUE); -} - -INT_PTR CALLBACK WepWindowPropertiesDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - PWINDOW_PROPERTIES_CONTEXT context; - - if (!WepPropPageDlgProcHeader(hwndDlg, uMsg, lParam, NULL, &context)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Value"); - PhSetExtendedListView(lvHandle); - - WepRefreshWindowProps(hwndDlg, lvHandle, context); - } - break; - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_REFRESH: - WepRefreshWindowProps(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), context); - break; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker Window Explorer - + * window properties + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "wndexp.h" +#include "resource.h" +#include +#include +#include + +#include +#include +#include + +#define WEM_RESOLVE_DONE (WM_APP + 1234) + +typedef struct _WINDOW_PROPERTIES_CONTEXT +{ + HWND WindowHandle; + HWND ParentWindowHandle; + HWND ListViewHandle; + CLIENT_ID ClientId; + PH_INITONCE SymbolProviderInitOnce; + PPH_SYMBOL_PROVIDER SymbolProvider; + LIST_ENTRY ResolveListHead; + PH_QUEUED_LOCK ResolveListLock; + + PPH_STRING WndProcSymbol; + ULONG WndProcResolving; + PPH_STRING DlgProcSymbol; + ULONG DlgProcResolving; + PPH_STRING ClassWndProcSymbol; + ULONG ClassWndProcResolving; + + ULONG_PTR WndProc; + ULONG_PTR DlgProc; + WNDCLASSEX ClassInfo; +} WINDOW_PROPERTIES_CONTEXT, *PWINDOW_PROPERTIES_CONTEXT; + +typedef struct _SYMBOL_RESOLVE_CONTEXT +{ + LIST_ENTRY ListEntry; + ULONG64 Address; + PPH_STRING Symbol; + PH_SYMBOL_RESOLVE_LEVEL ResolveLevel; + HWND NotifyWindow; + PWINDOW_PROPERTIES_CONTEXT Context; + ULONG Id; +} SYMBOL_RESOLVE_CONTEXT, *PSYMBOL_RESOLVE_CONTEXT; + +typedef struct _STRING_INTEGER_PAIR +{ + PWSTR String; + ULONG Integer; +} STRING_INTEGER_PAIR, *PSTRING_INTEGER_PAIR; + +typedef enum _WINDOW_PROPERTIES_CATEGORY +{ + WINDOW_PROPERTIES_CATEGORY_GENERAL, + WINDOW_PROPERTIES_CATEGORY_CLASS +} WINDOW_PROPERTIES_CATEGORY; + +typedef enum _NETADAPTER_DETAILS_INDEX +{ + WINDOW_PROPERTIES_INDEX_APPID, + WINDOW_PROPERTIES_INDEX_TEXT, + WINDOW_PROPERTIES_INDEX_THREAD, + WINDOW_PROPERTIES_INDEX_RECT, + WINDOW_PROPERTIES_INDEX_NORMALRECT, + WINDOW_PROPERTIES_INDEX_CLIENTRECT, + WINDOW_PROPERTIES_INDEX_INSTANCE, + WINDOW_PROPERTIES_INDEX_MENUHANDLE, + WINDOW_PROPERTIES_INDEX_USERDATA, + WINDOW_PROPERTIES_INDEX_UNICODE, + WINDOW_PROPERTIES_INDEX_WNDPROC, + WINDOW_PROPERTIES_INDEX_DLGPROC, + WINDOW_PROPERTIES_INDEX_DLGCTLID, + WINDOW_PROPERTIES_INDEX_FONTNAME, + WINDOW_PROPERTIES_INDEX_STYLES, + WINDOW_PROPERTIES_INDEX_EXSTYLES, + + WINDOW_PROPERTIES_INDEX_CLASS_NAME, + WINDOW_PROPERTIES_INDEX_CLASS_ATOM, + WINDOW_PROPERTIES_INDEX_CLASS_STYLES, + WINDOW_PROPERTIES_INDEX_CLASS_INSTANCE, + WINDOW_PROPERTIES_INDEX_CLASS_LARGEICON, + WINDOW_PROPERTIES_INDEX_CLASS_SMALLICON, + WINDOW_PROPERTIES_INDEX_CLASS_CURSOR, + WINDOW_PROPERTIES_INDEX_CLASS_BACKBRUSH, + WINDOW_PROPERTIES_INDEX_CLASS_MENUNAME, + WINDOW_PROPERTIES_INDEX_CLASS_WNDPROC +} NETADAPTER_DETAILS_INDEX; + +NTSTATUS WepPropertiesThreadStart( + _In_ PVOID Parameter + ); + +INT_PTR CALLBACK WepWindowGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK WepWindowPropertiesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK WepWindowPropStoreDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#define DEFINE_PAIR(Symbol) { L#Symbol, Symbol } + +static STRING_INTEGER_PAIR WepStylePairs[] = +{ + DEFINE_PAIR(WS_POPUP), + DEFINE_PAIR(WS_CHILD), + DEFINE_PAIR(WS_MINIMIZE), + DEFINE_PAIR(WS_VISIBLE), + DEFINE_PAIR(WS_DISABLED), + DEFINE_PAIR(WS_CLIPSIBLINGS), + DEFINE_PAIR(WS_CLIPCHILDREN), + DEFINE_PAIR(WS_MAXIMIZE), + DEFINE_PAIR(WS_BORDER), + DEFINE_PAIR(WS_DLGFRAME), + DEFINE_PAIR(WS_VSCROLL), + DEFINE_PAIR(WS_HSCROLL), + DEFINE_PAIR(WS_SYSMENU), + DEFINE_PAIR(WS_THICKFRAME), + DEFINE_PAIR(WS_GROUP), + DEFINE_PAIR(WS_TABSTOP), + DEFINE_PAIR(WS_MINIMIZEBOX), + DEFINE_PAIR(WS_MAXIMIZEBOX) +}; + +static STRING_INTEGER_PAIR WepExtendedStylePairs[] = +{ + DEFINE_PAIR(WS_EX_DLGMODALFRAME), + DEFINE_PAIR(WS_EX_NOPARENTNOTIFY), + DEFINE_PAIR(WS_EX_TOPMOST), + DEFINE_PAIR(WS_EX_ACCEPTFILES), + DEFINE_PAIR(WS_EX_TRANSPARENT), + DEFINE_PAIR(WS_EX_MDICHILD), + DEFINE_PAIR(WS_EX_TOOLWINDOW), + DEFINE_PAIR(WS_EX_WINDOWEDGE), + DEFINE_PAIR(WS_EX_CLIENTEDGE), + DEFINE_PAIR(WS_EX_CONTEXTHELP), + DEFINE_PAIR(WS_EX_RIGHT), + DEFINE_PAIR(WS_EX_RTLREADING), + DEFINE_PAIR(WS_EX_LEFTSCROLLBAR), + DEFINE_PAIR(WS_EX_CONTROLPARENT), + DEFINE_PAIR(WS_EX_STATICEDGE), + DEFINE_PAIR(WS_EX_APPWINDOW), + DEFINE_PAIR(WS_EX_LAYERED), + DEFINE_PAIR(WS_EX_NOINHERITLAYOUT), + DEFINE_PAIR(WS_EX_LAYOUTRTL), + DEFINE_PAIR(WS_EX_COMPOSITED), + DEFINE_PAIR(WS_EX_NOACTIVATE) +}; + +static STRING_INTEGER_PAIR WepClassStylePairs[] = +{ + DEFINE_PAIR(CS_VREDRAW), + DEFINE_PAIR(CS_HREDRAW), + DEFINE_PAIR(CS_DBLCLKS), + DEFINE_PAIR(CS_OWNDC), + DEFINE_PAIR(CS_CLASSDC), + DEFINE_PAIR(CS_PARENTDC), + DEFINE_PAIR(CS_NOCLOSE), + DEFINE_PAIR(CS_SAVEBITS), + DEFINE_PAIR(CS_BYTEALIGNCLIENT), + DEFINE_PAIR(CS_BYTEALIGNWINDOW), + DEFINE_PAIR(CS_GLOBALCLASS), + DEFINE_PAIR(CS_IME), + DEFINE_PAIR(CS_DROPSHADOW) +}; + +PPH_OBJECT_TYPE WeWindowItemType = NULL; + +VOID NTAPI WeWindowItemDeleteProcedure( + _In_ PVOID Object, + _In_ ULONG Flags + ) +{ + PWINDOW_PROPERTIES_CONTEXT context = Object; + + PLIST_ENTRY listEntry; + + PhClearReference(&context->SymbolProvider); + + // Destroy results that have not been processed by any property pages. + + listEntry = context->ResolveListHead.Flink; + + while (listEntry != &context->ResolveListHead) + { + PSYMBOL_RESOLVE_CONTEXT resolveContext; + + resolveContext = CONTAINING_RECORD(listEntry, SYMBOL_RESOLVE_CONTEXT, ListEntry); + listEntry = listEntry->Flink; + + PhClearReference(&resolveContext->Symbol); + PhFree(resolveContext); + } + + PhClearReference(&context->WndProcSymbol); + PhClearReference(&context->DlgProcSymbol); + PhClearReference(&context->ClassWndProcSymbol); +} + +VOID WeShowWindowProperties( + _In_ HWND ParentWindowHandle, + _In_ HWND WindowHandle + ) +{ + PWINDOW_PROPERTIES_CONTEXT context; + ULONG threadId; + ULONG processId; + + if (!WeWindowItemType) + WeWindowItemType = PhCreateObjectType(L"WindowItemType", 0, WeWindowItemDeleteProcedure); + + context = PhCreateObjectZero(sizeof(WINDOW_PROPERTIES_CONTEXT), WeWindowItemType); + context->WindowHandle = WindowHandle; + context->ParentWindowHandle = ParentWindowHandle; + + PhInitializeInitOnce(&context->SymbolProviderInitOnce); + InitializeListHead(&context->ResolveListHead); + PhInitializeQueuedLock(&context->ResolveListLock); + + processId = 0; + threadId = GetWindowThreadProcessId(WindowHandle, &processId); + context->ClientId.UniqueProcess = UlongToHandle(processId); + context->ClientId.UniqueThread = UlongToHandle(threadId); + + PhCreateThread2(WepPropertiesThreadStart, context); +} + +NTSTATUS WepPropertiesThreadStart( + _In_ PVOID Parameter + ) +{ + PWINDOW_PROPERTIES_CONTEXT context = Parameter; + PPV_PROPCONTEXT propContext; + PH_AUTO_POOL autoPool; + + PhInitializeAutoPool(&autoPool); + + if (propContext = HdCreatePropContext(PhaFormatString(L"Window %Ix", context->WindowHandle)->Buffer)) + { + PPV_PROPPAGECONTEXT newPage; + + // General + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_WNDGENERAL), + WepWindowGeneralDlgProc, + context); + PvAddPropPage(propContext, newPage); + + // Properties + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_WNDPROPLIST), + WepWindowPropertiesDlgProc, + context); + PvAddPropPage(propContext, newPage); + + // Property store + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_WNDPROPSTORAGE), + WepWindowPropStoreDlgProc, + context); + PvAddPropPage(propContext, newPage); + + PhModalPropertySheet(&propContext->PropSheetHeader); + PhDereferenceObject(propContext); + } + + PhDeleteAutoPool(&autoPool); + + PhDereferenceObject(context); + + return STATUS_SUCCESS; +} + +BOOLEAN NTAPI EnumGenericModulesCallback( + _In_ PPH_MODULE_INFO Module, + _In_opt_ PVOID Context + ) +{ + PWINDOW_PROPERTIES_CONTEXT context = Context; + + PhLoadModuleSymbolProvider(context->SymbolProvider, Module->FileName->Buffer, + (ULONG64)Module->BaseAddress, Module->Size); + + return TRUE; +} + +NTSTATUS WepResolveSymbolFunction( + _In_ PVOID Parameter + ) +{ + PSYMBOL_RESOLVE_CONTEXT context = Parameter; + + if (PhBeginInitOnce(&context->Context->SymbolProviderInitOnce)) + { + PhEnumGenericModules(context->Context->ClientId.UniqueProcess, NULL, 0, EnumGenericModulesCallback, context->Context); + PhEndInitOnce(&context->Context->SymbolProviderInitOnce); + } + + context->Symbol = PhGetSymbolFromAddress( + context->Context->SymbolProvider, + (ULONG64)context->Address, + &context->ResolveLevel, + NULL, + NULL, + NULL + ); + + // Fail if we don't have a symbol. + if (!context->Symbol) + { + PhDereferenceObject(context->Context); + PhFree(context); + return STATUS_SUCCESS; + } + + PhAcquireQueuedLockExclusive(&context->Context->ResolveListLock); + InsertHeadList(&context->Context->ResolveListHead, &context->ListEntry); + PhReleaseQueuedLockExclusive(&context->Context->ResolveListLock); + + PostMessage(context->NotifyWindow, WEM_RESOLVE_DONE, 0, (LPARAM)context); + + PhDereferenceObject(context->Context); + + return STATUS_SUCCESS; +} + +VOID WepQueueResolveSymbol( + _In_ PWINDOW_PROPERTIES_CONTEXT Context, + _In_ HWND NotifyWindow, + _In_ ULONG64 Address, + _In_ ULONG Id + ) +{ + PSYMBOL_RESOLVE_CONTEXT resolveContext; + + if (!Context->SymbolProvider) + { + Context->SymbolProvider = PhCreateSymbolProvider(Context->ClientId.UniqueProcess); + PhLoadSymbolProviderOptions(Context->SymbolProvider); + } + + PhReferenceObject(Context); + + resolveContext = PhAllocateZero(sizeof(SYMBOL_RESOLVE_CONTEXT)); + resolveContext->Address = Address; + resolveContext->Symbol = NULL; + resolveContext->ResolveLevel = PhsrlInvalid; + resolveContext->NotifyWindow = NotifyWindow; + resolveContext->Context = Context; + resolveContext->Id = Id; + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), WepResolveSymbolFunction, resolveContext); +} + +HICON WepGetWindowIcon( + _In_ HWND WindowHandle + ) +{ + static HICON (WINAPI *InternalGetWindowIcon_I)( + _In_ HWND hwnd, + _In_ UINT iconType + ) = NULL; + + if (!InternalGetWindowIcon_I) + InternalGetWindowIcon_I = PhGetModuleProcAddress(L"user32.dll", "InternalGetWindowIcon"); + + if (!InternalGetWindowIcon_I) + return NULL; + + return InternalGetWindowIcon_I(WindowHandle, ICON_BIG); +} + +PPH_STRING WepFormatRect( + _In_ PRECT Rect + ) +{ + return PhaFormatString(L"(%ld, %ld) - (%ld, %ld) [%ldx%ld]", + Rect->left, Rect->top, Rect->right, Rect->bottom, + Rect->right - Rect->left, Rect->bottom - Rect->top); +} + +VOID WepRefreshWindowGeneralInfoSymbols( + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + if (Context->WndProcResolving != 0) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_WNDPROC, 1, PhaFormatString(L"0x%Ix (resolving...)", Context->WndProc)->Buffer); + else if (Context->WndProcSymbol) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_WNDPROC, 1, PhaFormatString(L"0x%Ix (%s)", Context->WndProc, Context->WndProcSymbol->Buffer)->Buffer); + else if (Context->WndProc != 0) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_WNDPROC, 1, PhaFormatString(L"0x%Ix", Context->WndProc)->Buffer); + else + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_WNDPROC, 1, L"Unknown"); + + if (Context->DlgProcResolving != 0) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_DLGPROC, 1, PhaFormatString(L"0x%Ix (resolving...)", Context->DlgProc)->Buffer); + else if (Context->DlgProcSymbol) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_DLGPROC, 1, PhaFormatString(L"0x%Ix (%s)", Context->DlgProc, Context->DlgProcSymbol->Buffer)->Buffer); + else if (Context->DlgProc != 0) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_DLGPROC, 1, PhaFormatString(L"0x%Ix", Context->DlgProc)->Buffer); + else if (Context->WndProc != 0) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_DLGPROC, 1, L"N/A"); + else + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_DLGPROC, 1, L"Unknown"); +} + +VOID WepRefreshWindowGeneralInfo( + _In_ HWND hwndDlg, + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + WINDOWINFO windowInfo = { sizeof(WINDOWINFO) }; + WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT) }; + MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; + HANDLE processHandle; + PPH_STRING fileName = NULL; + PPH_STRING appIdText; + HMENU menuHandle; + PVOID instanceHandle; + PVOID userdataHandle; + ULONG windowId; + HFONT fontHandle; + + menuHandle = GetMenu(Context->WindowHandle); + instanceHandle = (PVOID)GetWindowLongPtr(Context->WindowHandle, GWLP_HINSTANCE); + userdataHandle = (PVOID)GetWindowLongPtr(Context->WindowHandle, GWLP_USERDATA); + windowId = (ULONG)GetWindowLongPtr(Context->WindowHandle, GWLP_ID); + // TODO: GetWindowLongPtr(Context->WindowHandle, GCLP_WNDPROC); + + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_TEXT, 1, PhGetStringOrEmpty(PH_AUTO(PhGetWindowText(Context->WindowHandle)))); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_THREAD, 1, PH_AUTO_T(PH_STRING, PhGetClientIdName(&Context->ClientId))->Buffer); + + if (GetWindowInfo(Context->WindowHandle, &windowInfo)) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_RECT, 1, WepFormatRect(&windowInfo.rcWindow)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLIENTRECT, 1, WepFormatRect(&windowInfo.rcClient)->Buffer); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_RECT, 1, L"N/A"); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLIENTRECT, 1, L"N/A"); + } + + if (GetWindowPlacement(Context->WindowHandle, &windowPlacement)) + { + // The rectangle is in workspace coordinates. Convert the values back to screen coordinates. + if (GetMonitorInfo(MonitorFromRect(&windowPlacement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo)) + { + windowPlacement.rcNormalPosition.left += monitorInfo.rcWork.left; + windowPlacement.rcNormalPosition.top += monitorInfo.rcWork.top; + windowPlacement.rcNormalPosition.right += monitorInfo.rcWork.left; + windowPlacement.rcNormalPosition.bottom += monitorInfo.rcWork.top; + } + + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_NORMALRECT, 1, WepFormatRect(&windowPlacement.rcNormalPosition)->Buffer); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_NORMALRECT, 1, L"N/A"); + } + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, Context->ClientId.UniqueProcess))) + { + if (NT_SUCCESS(PhGetProcessMappedFileName(processHandle, instanceHandle, &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); + PhMoveReference(&fileName, PhGetBaseName(fileName)); + } + + NtClose(processHandle); + } + + if (fileName) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_INSTANCE, 1, PhaFormatString( + L"0x%Ix (%s)", + instanceHandle, + PhGetStringOrEmpty(fileName) + )->Buffer); + PhDereferenceObject(fileName); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_INSTANCE, 1, PhaFormatString( + L"0x%Ix", + instanceHandle + )->Buffer); + } + + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_MENUHANDLE, 1, PhaFormatString(L"0x%Ix", menuHandle)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_USERDATA, 1, PhaFormatString(L"0x%Ix", userdataHandle)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_UNICODE, 1, IsWindowUnicode(Context->WindowHandle) ? L"Yes" : L"No"); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_DLGCTLID, 1, PhaFormatString(L"%lu", windowId)->Buffer); + + if (fontHandle = (HFONT)SendMessage(Context->WindowHandle, WM_GETFONT, 0, 0)) + { + LOGFONT logFont; + + if (GetObject(fontHandle, sizeof(LOGFONT), &logFont)) + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_FONTNAME, 1, logFont.lfFaceName); + else + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_FONTNAME, 1, L"N/A"); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_FONTNAME, 1, L"N/A"); + } + + //ULONG version; + //if (SendMessageTimeout(Context->WindowHandle, CCM_GETVERSION, 0, 0, SMTO_ABORTIFHUNG, 5000, &version)) + //WepQueryProcessWndProc(Context); + + if (Context->WndProc != 0) + { + Context->WndProcResolving++; + WepQueueResolveSymbol(Context, hwndDlg, Context->WndProc, 1); + } + + if (Context->DlgProc != 0) + { + Context->DlgProcResolving++; + WepQueueResolveSymbol(Context, hwndDlg, Context->DlgProc, 2); + } + + WepRefreshWindowGeneralInfoSymbols(ListViewHandle, Context); + + if (PhAppResolverGetAppIdForWindow(Context->WindowHandle, &appIdText)) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_APPID, 1, appIdText->Buffer); + PhDereferenceObject(appIdText); + } +} + +VOID WepRefreshWindowStyles( + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + WINDOWINFO windowInfo = { sizeof(WINDOWINFO) }; + PH_STRING_BUILDER styleStringBuilder; + PH_STRING_BUILDER styleExStringBuilder; + ULONG i; + + if (GetWindowInfo(Context->WindowHandle, &windowInfo)) + { + PhInitializeStringBuilder(&styleStringBuilder, 100); + PhInitializeStringBuilder(&styleExStringBuilder, 100); + + PhAppendFormatStringBuilder(&styleStringBuilder, L"0x%x (", windowInfo.dwStyle); + PhAppendFormatStringBuilder(&styleExStringBuilder, L"0x%x (", windowInfo.dwExStyle); + + for (i = 0; i < RTL_NUMBER_OF(WepStylePairs); i++) + { + if (windowInfo.dwStyle & WepStylePairs[i].Integer) + { + // Skip irrelevant styles. + if (WepStylePairs[i].Integer == WS_MAXIMIZEBOX || + WepStylePairs[i].Integer == WS_MINIMIZEBOX) + { + if (windowInfo.dwStyle & WS_CHILD) + continue; + } + + if (WepStylePairs[i].Integer == WS_TABSTOP || + WepStylePairs[i].Integer == WS_GROUP) + { + if (!(windowInfo.dwStyle & WS_CHILD)) + continue; + } + + PhAppendStringBuilder2(&styleStringBuilder, WepStylePairs[i].String); + PhAppendStringBuilder2(&styleStringBuilder, L", "); + } + } + + if (PhEndsWithString2(styleStringBuilder.String, L", ", FALSE)) + { + PhRemoveEndStringBuilder(&styleStringBuilder, 2); + PhAppendCharStringBuilder(&styleStringBuilder, ')'); + } + else + { + PhRemoveEndStringBuilder(&styleStringBuilder, 1); + } + + for (i = 0; i < RTL_NUMBER_OF(WepExtendedStylePairs); i++) + { + if (windowInfo.dwExStyle & WepExtendedStylePairs[i].Integer) + { + PhAppendStringBuilder2(&styleExStringBuilder, WepExtendedStylePairs[i].String); + PhAppendStringBuilder2(&styleExStringBuilder, L", "); + } + } + + if (PhEndsWithString2(styleExStringBuilder.String, L", ", FALSE)) + { + PhRemoveEndStringBuilder(&styleExStringBuilder, 2); + PhAppendCharStringBuilder(&styleExStringBuilder, ')'); + } + else + { + PhRemoveEndStringBuilder(&styleExStringBuilder, 1); + } + + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_STYLES, 1, PhFinalStringBuilderString(&styleStringBuilder)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_EXSTYLES, 1, PhFinalStringBuilderString(&styleExStringBuilder)->Buffer); + + PhDeleteStringBuilder(&styleStringBuilder); + PhDeleteStringBuilder(&styleExStringBuilder); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_STYLES, 1, L"N/A"); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_EXSTYLES, 1, L"N/A"); + } +} + +VOID WepRefreshClassStyles( + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i; + + PhInitializeStringBuilder(&stringBuilder, 100); + PhAppendFormatStringBuilder(&stringBuilder, L"0x%x (", Context->ClassInfo.style); + + for (i = 0; i < RTL_NUMBER_OF(WepClassStylePairs); i++) + { + if (Context->ClassInfo.style & WepClassStylePairs[i].Integer) + { + PhAppendStringBuilder2(&stringBuilder, WepClassStylePairs[i].String); + PhAppendStringBuilder2(&stringBuilder, L", "); + } + } + + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + { + PhRemoveEndStringBuilder(&stringBuilder, 2); + PhAppendCharStringBuilder(&stringBuilder, ')'); + } + else + { + // No styles. Remove the brackets. + PhRemoveEndStringBuilder(&stringBuilder, 1); + } + + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_STYLES, 1, PhFinalStringBuilderString(&stringBuilder)->Buffer); + PhDeleteStringBuilder(&stringBuilder); +} + +VOID WepRefreshClassModule( + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + HANDLE processHandle; + PPH_STRING fileName = NULL; + PVOID instanceHandle = (PVOID)GetClassLongPtr(Context->WindowHandle, GCLP_HMODULE); + + if (NT_SUCCESS(PhOpenProcess(&processHandle, PROCESS_QUERY_LIMITED_INFORMATION, Context->ClientId.UniqueProcess))) + { + if (NT_SUCCESS(PhGetProcessMappedFileName(processHandle, instanceHandle, &fileName))) + { + PhMoveReference(&fileName, PhGetFileName(fileName)); + PhMoveReference(&fileName, PhGetBaseName(fileName)); + } + + NtClose(processHandle); + } + + if (fileName) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_INSTANCE, 1, PhaFormatString( + L"0x%Ix (%s)", + instanceHandle, + PhGetStringOrEmpty(fileName) + )->Buffer); + PhDereferenceObject(fileName); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_INSTANCE, 1, PhaFormatString( + L"0x%Ix", + instanceHandle + )->Buffer); + } +} + +VOID WepRefreshWindowClassInfoSymbols( + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + if (Context->ClassWndProcResolving != 0) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_WNDPROC, 1, PhaFormatString( + L"0x%Ix (resolving...)", + Context->ClassInfo.lpfnWndProc + )->Buffer); + } + else if (Context->ClassWndProcSymbol) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_WNDPROC, 1, PhaFormatString( + L"0x%Ix (%s)", + Context->ClassInfo.lpfnWndProc, + Context->ClassWndProcSymbol->Buffer + )->Buffer); + } + else if (Context->ClassInfo.lpfnWndProc) + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_WNDPROC, 1, PhaFormatString( + L"0x%Ix", + Context->ClassInfo.lpfnWndProc + )->Buffer); + } + else + { + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_WNDPROC, 1, L"Unknown"); + } +} + +VOID WepRefreshWindowClassInfo( + _In_ HWND hwndDlg, + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + WCHAR className[256]; + + if (!GetClassName(Context->WindowHandle, className, RTL_NUMBER_OF(className))) + className[0] = UNICODE_NULL; + + Context->ClassInfo.cbSize = sizeof(WNDCLASSEX); + GetClassInfoEx(NULL, className, &Context->ClassInfo); + + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_NAME, 1, className); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_ATOM, 1, PhaFormatString(L"0x%x", GetClassWord(Context->WindowHandle, GCW_ATOM))->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_LARGEICON, 1, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIcon)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_SMALLICON, 1, PhaFormatString(L"0x%Ix", Context->ClassInfo.hIconSm)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_MENUNAME, 1, PhaFormatString(L"0x%Ix", Context->ClassInfo.lpszMenuName)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_CURSOR, 1, PhaFormatString(L"0x%Ix", Context->ClassInfo.hCursor)->Buffer); + PhSetListViewSubItem(ListViewHandle, WINDOW_PROPERTIES_INDEX_CLASS_BACKBRUSH, 1, PhaFormatString(L"0x%Ix", Context->ClassInfo.hbrBackground)->Buffer); + + WepRefreshClassStyles(ListViewHandle, Context); + WepRefreshClassModule(ListViewHandle, Context); + + if (Context->ClassInfo.lpfnWndProc) + { + Context->ClassWndProcResolving++; + WepQueueResolveSymbol(Context, hwndDlg, (ULONG_PTR)Context->ClassInfo.lpfnWndProc, 3); + } + + WepRefreshWindowClassInfoSymbols(ListViewHandle, Context); +} + +VOID WepGeneralAddListViewItemGroups( + _In_ HWND ListViewHandle + ) +{ + ListView_EnableGroupView(ListViewHandle, TRUE); + PhAddListViewGroup(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, L"General"); + PhAddListViewGroup(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, L"Class"); + + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_APPID, L"AppId", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_TEXT, L"Text", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_THREAD, L"Thread", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_RECT, L"Rectangle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_NORMALRECT, L"Normal rectangle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_CLIENTRECT, L"Client rectangle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_INSTANCE, L"Instance handle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_MENUHANDLE, L"Menu handle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_USERDATA, L"User data", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_UNICODE, L"Unicode", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_WNDPROC, L"Window procedure", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_DLGPROC, L"Dialog procedure", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_DLGCTLID, L"Dialog control ID", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_FONTNAME, L"Font", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_STYLES, L"Styles", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_GENERAL, WINDOW_PROPERTIES_INDEX_EXSTYLES, L"Extended styles", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_NAME, L"Name", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_ATOM, L"Atom", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_STYLES, L"Styles", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_INSTANCE, L"Instance handle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_LARGEICON, L"Large icon handle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_SMALLICON, L"Small icon handle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_CURSOR, L"Cursor handle", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_BACKBRUSH, L"Background brush", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_MENUNAME, L"Menu name", NULL); + PhAddListViewGroupItem(ListViewHandle, WINDOW_PROPERTIES_CATEGORY_CLASS, WINDOW_PROPERTIES_INDEX_CLASS_WNDPROC, L"Window procedure", NULL); +} + +INT_PTR CALLBACK WepWindowGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PWINDOW_PROPERTIES_CONTEXT context; + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + context = (PWINDOW_PROPERTIES_CONTEXT)propPageContext->Context; + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + context->ListViewHandle = GetDlgItem(hwndDlg, IDC_WINDOWINFO); + + // HACK + SendMessage(GetParent(hwndDlg), WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(WE_PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + SendMessage(GetParent(hwndDlg), WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(WE_PhInstanceHandle, MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER))); + + if (PhGetIntegerPairSetting(SETTING_NAME_WINDOWS_PROPERTY_POSITION).X == 0) // HACK + PhCenterWindow(GetParent(hwndDlg), context->ParentWindowHandle); + + PhSetListViewStyle(context->ListViewHandle, FALSE, TRUE); + PhSetControlTheme(context->ListViewHandle, L"explorer"); + PhAddListViewColumn(context->ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 180, L"Name"); + PhAddListViewColumn(context->ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 200, L"Value"); + PhSetExtendedListView(context->ListViewHandle); + PhLoadListViewColumnsFromSetting(SETTING_NAME_WINDOWS_PROPERTY_COLUMNS, context->ListViewHandle); + + WepGeneralAddListViewItemGroups(context->ListViewHandle); + WepRefreshWindowGeneralInfo(hwndDlg, context->ListViewHandle, context); + WepRefreshWindowStyles(context->ListViewHandle, context); + WepRefreshWindowClassInfo(hwndDlg, context->ListViewHandle, context); + + if (!!PhGetIntegerSetting(L"EnableThemeSupport")) // TODO: Required for compat (dmex) + PhInitializeWindowTheme(GetParent(hwndDlg), !!PhGetIntegerSetting(L"EnableThemeSupport")); + else + PhInitializeWindowTheme(hwndDlg, FALSE); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(SETTING_NAME_WINDOWS_PROPERTY_COLUMNS, context->ListViewHandle); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, context->ListViewHandle, dialogItem, PH_ANCHOR_ALL); + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyBehaviors(lParam, context->ListViewHandle, PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + if ((HWND)wParam == context->ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(context->ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, PHAPP_IDC_COPY, context->ListViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + if (!PhHandleCopyListViewEMenuItem(item)) + { + switch (item->Id) + { + case PHAPP_IDC_COPY: + { + PhCopyListView(context->ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + case WEM_RESOLVE_DONE: + { + PSYMBOL_RESOLVE_CONTEXT resolveContext = (PSYMBOL_RESOLVE_CONTEXT)lParam; + + if (resolveContext->Id == 1) + { + PhAcquireQueuedLockExclusive(&context->ResolveListLock); + RemoveEntryList(&resolveContext->ListEntry); + PhReleaseQueuedLockExclusive(&context->ResolveListLock); + + if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) + PhClearReference(&resolveContext->Symbol); + + PhMoveReference(&context->WndProcSymbol, resolveContext->Symbol); + PhFree(resolveContext); + + context->WndProcResolving--; + } + else if (resolveContext->Id == 2) + { + PhAcquireQueuedLockExclusive(&context->ResolveListLock); + RemoveEntryList(&resolveContext->ListEntry); + PhReleaseQueuedLockExclusive(&context->ResolveListLock); + + if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) + PhClearReference(&resolveContext->Symbol); + + PhMoveReference(&context->DlgProcSymbol, resolveContext->Symbol); + PhFree(resolveContext); + + context->DlgProcResolving--; + } + else if (resolveContext->Id == 3) + { + PhAcquireQueuedLockExclusive(&context->ResolveListLock); + RemoveEntryList(&resolveContext->ListEntry); + PhReleaseQueuedLockExclusive(&context->ResolveListLock); + + if (resolveContext->ResolveLevel != PhsrlModule && resolveContext->ResolveLevel != PhsrlFunction) + PhClearReference(&resolveContext->Symbol); + + PhMoveReference(&context->ClassWndProcSymbol, resolveContext->Symbol); + PhFree(resolveContext); + + context->ClassWndProcResolving--; + } + + WepRefreshWindowGeneralInfoSymbols(context->ListViewHandle, context); + WepRefreshWindowClassInfoSymbols(context->ListViewHandle, context); + } + break; + } + + return FALSE; +} + +BOOL CALLBACK EnumPropsExCallback( + _In_ HWND hwnd, + _In_ PWSTR lpszString, + _In_ HANDLE hData, + _In_ ULONG_PTR dwData + ) +{ + INT lvItemIndex; + WCHAR value[PH_PTR_STR_LEN_1]; + + if ((ULONG_PTR)lpszString < USHRT_MAX) // This is an integer atom. + { + PPH_STRING propName; + + propName = PhFormatString(L"#%lu", (ULONG_PTR)lpszString); + lvItemIndex = PhAddListViewItem((HWND)dwData, MAXINT, propName->Buffer, NULL); + PhDereferenceObject(propName); + } + else + { + lvItemIndex = PhAddListViewItem((HWND)dwData, MAXINT, lpszString, NULL); + } + + PhPrintPointer(value, (PVOID)hData); + PhSetListViewSubItem((HWND)dwData, lvItemIndex, 1, value); + + return TRUE; +} + +VOID WepRefreshWindowProps( + _In_ HWND hwndDlg, + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + ExtendedListView_SetRedraw(ListViewHandle, FALSE); + ListView_DeleteAllItems(ListViewHandle); + + EnumPropsEx(Context->WindowHandle, EnumPropsExCallback, (LPARAM)ListViewHandle); + + ExtendedListView_SortItems(ListViewHandle); + ExtendedListView_SetRedraw(ListViewHandle, TRUE); +} + +INT_PTR CALLBACK WepWindowPropertiesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PWINDOW_PROPERTIES_CONTEXT context; + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + context = (PWINDOW_PROPERTIES_CONTEXT)propPageContext->Context; + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Value"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(SETTING_NAME_WINDOWS_PROPLIST_COLUMNS, lvHandle); + + WepRefreshWindowProps(hwndDlg, lvHandle, context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(SETTING_NAME_WINDOWS_PROPLIST_COLUMNS, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDC_REFRESH: + WepRefreshWindowProps(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), context); + break; + } + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyBehaviors(lParam, GetDlgItem(hwndDlg, IDC_LIST), PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + HWND listViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + + if ((HWND)wParam == listViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(listViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, PHAPP_IDC_COPY, listViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + if (!PhHandleCopyListViewEMenuItem(item)) + { + switch (item->Id) + { + case PHAPP_IDC_COPY: + { + PhCopyListView(listViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} + +VOID WepRefreshWindowPropertyStorage( + _In_ HWND hwndDlg, + _In_ HWND ListViewHandle, + _In_ PWINDOW_PROPERTIES_CONTEXT Context + ) +{ + IPropertyStore *propstore; + ULONG count; + ULONG i; + + ExtendedListView_SetRedraw(ListViewHandle, FALSE); + ListView_DeleteAllItems(ListViewHandle); + + if (SUCCEEDED(SHGetPropertyStoreForWindow(Context->WindowHandle, &IID_IPropertyStore, &propstore))) + { + if (SUCCEEDED(IPropertyStore_GetCount(propstore, &count))) + { + for (i = 0; i < count; i++) + { + PROPERTYKEY propkey; + + if (SUCCEEDED(IPropertyStore_GetAt(propstore, i, &propkey))) + { + INT lvItemIndex; + PROPVARIANT propKeyVariant = { 0 }; + PWSTR propKeyName; + + if (SUCCEEDED(PSGetNameFromPropertyKey(&propkey, &propKeyName))) + { + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, propKeyName, NULL); + CoTaskMemFree(propKeyName); + } + else + { + WCHAR propKeyString[PKEYSTR_MAX]; + + if (SUCCEEDED(PSStringFromPropertyKey(&propkey, propKeyString, RTL_NUMBER_OF(propKeyString)))) + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, propKeyString, NULL); + else + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, L"Unknown", NULL); + } + + if (SUCCEEDED(IPropertyStore_GetValue(propstore, &propkey, &propKeyVariant))) + { + if (SUCCEEDED(PSFormatForDisplayAlloc(&propkey, &propKeyVariant, PDFF_DEFAULT, &propKeyName))) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, propKeyName); + CoTaskMemFree(propKeyName); + } + + //if (SUCCEEDED(PropVariantToStringAlloc(&propKeyVariant, &propKeyName))) + //{ + // PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, propKeyName); + // CoTaskMemFree(propKeyName); + //} + + PropVariantClear(&propKeyVariant); + } + } + } + } + + IPropertyStore_Release(propstore); + } + + ExtendedListView_SortItems(ListViewHandle); + ExtendedListView_SetRedraw(ListViewHandle, TRUE); +} + +INT_PTR CALLBACK WepWindowPropStoreDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + PWINDOW_PROPERTIES_CONTEXT context; + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + context = (PWINDOW_PROPERTIES_CONTEXT)propPageContext->Context; + + if (!context) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 160, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 100, L"Value"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(SETTING_NAME_WINDOWS_PROPSTORAGE_COLUMNS, lvHandle); + + WepRefreshWindowPropertyStorage(hwndDlg, lvHandle, context); + + PhInitializeWindowTheme(hwndDlg, !!PhGetIntegerSetting(L"EnableThemeSupport")); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(SETTING_NAME_WINDOWS_PROPSTORAGE_COLUMNS, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PhHandleListViewNotifyBehaviors(lParam, GetDlgItem(hwndDlg, IDC_LIST), PH_LIST_VIEW_DEFAULT_1_BEHAVIORS); + } + break; + case WM_CONTEXTMENU: + { + HWND listViewHandle = GetDlgItem(hwndDlg, IDC_LIST); + + if ((HWND)wParam == listViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PhGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(listViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, PHAPP_IDC_COPY, L"&Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyListViewEMenuItem(menu, PHAPP_IDC_COPY, listViewHandle); + + item = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + if (!PhHandleCopyListViewEMenuItem(item)) + { + switch (item->Id) + { + case PHAPP_IDC_COPY: + { + PhCopyListView(listViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } + } + break; + } + + return FALSE; +} diff --git a/plugins/WindowExplorer/wndtree.c b/plugins/WindowExplorer/wndtree.c index db75a7180a75..c94d2c220d2d 100644 --- a/plugins/WindowExplorer/wndtree.c +++ b/plugins/WindowExplorer/wndtree.c @@ -1,572 +1,606 @@ -/* - * Process Hacker Window Explorer - - * window treelist - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include "wndexp.h" -#include "resource.h" - -BOOLEAN WepWindowNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ); - -ULONG WepWindowNodeHashtableHashFunction( - _In_ PVOID Entry - ); - -VOID WepDestroyWindowNode( - _In_ PWE_WINDOW_NODE WindowNode - ); - -BOOLEAN NTAPI WepWindowTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ); - -BOOLEAN WordMatchStringRef( - _Inout_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ PPH_STRINGREF Text - ) -{ - PH_STRINGREF part; - PH_STRINGREF remainingPart; - - remainingPart = Context->SearchboxText->sr; - - while (remainingPart.Length != 0) - { - PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); - - if (part.Length != 0) - { - if (PhFindStringInStringRef(Text, &part, TRUE) != -1) - return TRUE; - } - } - - return FALSE; -} - -BOOLEAN WordMatchStringZ( - _Inout_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ PWSTR Text - ) -{ - PH_STRINGREF text; - - PhInitializeStringRef(&text, Text); - - return WordMatchStringRef(Context, &text); -} - -BOOLEAN WeWindowTreeFilterCallback( - _In_ PPH_TREENEW_NODE Node, - _In_opt_ PVOID Context - ) -{ - PWE_WINDOW_TREE_CONTEXT context = Context; - PWE_WINDOW_NODE windowNode = (PWE_WINDOW_NODE)Node; - - if (PhIsNullOrEmptyString(context->SearchboxText)) - return TRUE; - - if (windowNode->WindowClass[0]) - { - if (WordMatchStringZ(context, windowNode->WindowClass)) - return TRUE; - } - - if (windowNode->WindowHandleString[0]) - { - if (WordMatchStringZ(context, windowNode->WindowHandleString)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(windowNode->WindowText)) - { - if (WordMatchStringRef(context, &windowNode->WindowText->sr)) - return TRUE; - } - - if (!PhIsNullOrEmptyString(windowNode->ThreadString)) - { - if (WordMatchStringRef(context, &windowNode->ThreadString->sr)) - return TRUE; - } - - return FALSE; -} - -VOID WeInitializeWindowTree( - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle, - _Out_ PWE_WINDOW_TREE_CONTEXT Context - ) -{ - HWND hwnd; - PPH_STRING settings; - - memset(Context, 0, sizeof(WE_WINDOW_TREE_CONTEXT)); - - Context->NodeHashtable = PhCreateHashtable( - sizeof(PWE_WINDOW_NODE), - WepWindowNodeHashtableEqualFunction, - WepWindowNodeHashtableHashFunction, - 100 - ); - Context->NodeList = PhCreateList(100); - Context->NodeRootList = PhCreateList(30); - - Context->ParentWindowHandle = ParentWindowHandle; - Context->TreeNewHandle = TreeNewHandle; - hwnd = TreeNewHandle; - PhSetControlTheme(hwnd, L"explorer"); - - TreeNew_SetCallback(hwnd, WepWindowTreeNewCallback, Context); - - PhAddTreeNewColumn(hwnd, WEWNTLC_CLASS, TRUE, L"Class", 180, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumn(hwnd, WEWNTLC_HANDLE, TRUE, L"Handle", 70, PH_ALIGN_LEFT, 1, 0); - PhAddTreeNewColumn(hwnd, WEWNTLC_TEXT, TRUE, L"Text", 220, PH_ALIGN_LEFT, 2, 0); - PhAddTreeNewColumn(hwnd, WEWNTLC_THREAD, TRUE, L"Thread", 150, PH_ALIGN_LEFT, 3, 0); - - TreeNew_SetTriState(hwnd, TRUE); - TreeNew_SetSort(hwnd, WEWNTLC_CLASS, NoSortOrder); - - settings = PhGetStringSetting(SETTING_NAME_WINDOW_TREE_LIST_COLUMNS); - PhCmLoadSettings(hwnd, &settings->sr); - PhDereferenceObject(settings); - - Context->SearchboxText = PhReferenceEmptyString(); - - PhInitializeTreeNewFilterSupport( - &Context->FilterSupport, - Context->TreeNewHandle, - Context->NodeList - ); - - Context->TreeFilterEntry = PhAddTreeNewFilter( - &Context->FilterSupport, - WeWindowTreeFilterCallback, - Context - ); -} - -VOID WeDeleteWindowTree( - _In_ PWE_WINDOW_TREE_CONTEXT Context - ) -{ - PPH_STRING settings; - ULONG i; - - PhDeleteTreeNewFilterSupport(&Context->FilterSupport); - - settings = PhCmSaveSettings(Context->TreeNewHandle); - PhSetStringSetting2(SETTING_NAME_WINDOW_TREE_LIST_COLUMNS, &settings->sr); - PhDereferenceObject(settings); - - for (i = 0; i < Context->NodeList->Count; i++) - WepDestroyWindowNode(Context->NodeList->Items[i]); - - PhDereferenceObject(Context->NodeHashtable); - PhDereferenceObject(Context->NodeList); - PhDereferenceObject(Context->NodeRootList); -} - -BOOLEAN WepWindowNodeHashtableEqualFunction( - _In_ PVOID Entry1, - _In_ PVOID Entry2 - ) -{ - PWE_WINDOW_NODE windowNode1 = *(PWE_WINDOW_NODE *)Entry1; - PWE_WINDOW_NODE windowNode2 = *(PWE_WINDOW_NODE *)Entry2; - - return windowNode1->WindowHandle == windowNode2->WindowHandle; -} - -ULONG WepWindowNodeHashtableHashFunction( - _In_ PVOID Entry - ) -{ - return PhHashIntPtr((ULONG_PTR)(*(PWE_WINDOW_NODE *)Entry)->WindowHandle); -} - -PWE_WINDOW_NODE WeAddWindowNode( - _Inout_ PWE_WINDOW_TREE_CONTEXT Context - ) -{ - PWE_WINDOW_NODE windowNode; - - windowNode = PhAllocate(sizeof(WE_WINDOW_NODE)); - memset(windowNode, 0, sizeof(WE_WINDOW_NODE)); - PhInitializeTreeNewNode(&windowNode->Node); - - memset(windowNode->TextCache, 0, sizeof(PH_STRINGREF) * WEWNTLC_MAXIMUM); - windowNode->Node.TextCache = windowNode->TextCache; - windowNode->Node.TextCacheSize = WEWNTLC_MAXIMUM; - - windowNode->Children = PhCreateList(1); - - PhAddEntryHashtable(Context->NodeHashtable, &windowNode); - PhAddItemList(Context->NodeList, windowNode); - - if (Context->FilterSupport.FilterList) - windowNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &windowNode->Node); - - //TreeNew_NodesStructured(Context->TreeNewHandle); - - return windowNode; -} - -PWE_WINDOW_NODE WeFindWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ HWND WindowHandle - ) -{ - WE_WINDOW_NODE lookupWindowNode; - PWE_WINDOW_NODE lookupWindowNodePtr = &lookupWindowNode; - PWE_WINDOW_NODE *windowNode; - - lookupWindowNode.WindowHandle = WindowHandle; - - windowNode = (PWE_WINDOW_NODE *)PhFindEntryHashtable( - Context->NodeHashtable, - &lookupWindowNodePtr - ); - - if (windowNode) - return *windowNode; - else - return NULL; -} - -VOID WeRemoveWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ PWE_WINDOW_NODE WindowNode - ) -{ - ULONG index; - - // Remove from hashtable/list and cleanup. - - PhRemoveEntryHashtable(Context->NodeHashtable, &WindowNode); - - if ((index = PhFindItemList(Context->NodeList, WindowNode)) != -1) - PhRemoveItemList(Context->NodeList, index); - - WepDestroyWindowNode(WindowNode); - - TreeNew_NodesStructured(Context->TreeNewHandle); -} - -VOID WepDestroyWindowNode( - _In_ PWE_WINDOW_NODE WindowNode - ) -{ - PhDereferenceObject(WindowNode->Children); - - if (WindowNode->WindowText) PhDereferenceObject(WindowNode->WindowText); - - if (WindowNode->ThreadString) PhDereferenceObject(WindowNode->ThreadString); - - PhFree(WindowNode); -} - -#define SORT_FUNCTION(Column) WepWindowTreeNewCompare##Column - -#define BEGIN_SORT_FUNCTION(Column) static int __cdecl WepWindowTreeNewCompare##Column( \ - _In_ void *_context, \ - _In_ const void *_elem1, \ - _In_ const void *_elem2 \ - ) \ -{ \ - PWE_WINDOW_NODE node1 = *(PWE_WINDOW_NODE *)_elem1; \ - PWE_WINDOW_NODE node2 = *(PWE_WINDOW_NODE *)_elem2; \ - int sortResult = 0; - -#define END_SORT_FUNCTION \ - return PhModifySort(sortResult, ((PWE_WINDOW_TREE_CONTEXT)_context)->TreeNewSortOrder); \ -} - -BEGIN_SORT_FUNCTION(Class) -{ - sortResult = _wcsicmp(node1->WindowClass, node2->WindowClass); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Handle) -{ - sortResult = uintptrcmp((ULONG_PTR)node1->WindowHandle, (ULONG_PTR)node2->WindowHandle); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Text) -{ - sortResult = PhCompareString(node1->WindowText, node2->WindowText, TRUE); -} -END_SORT_FUNCTION - -BEGIN_SORT_FUNCTION(Thread) -{ - sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueProcess, (ULONG_PTR)node2->ClientId.UniqueProcess); - - if (sortResult == 0) - sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueThread, (ULONG_PTR)node2->ClientId.UniqueThread); -} -END_SORT_FUNCTION - -BOOLEAN NTAPI WepWindowTreeNewCallback( - _In_ HWND hwnd, - _In_ PH_TREENEW_MESSAGE Message, - _In_opt_ PVOID Parameter1, - _In_opt_ PVOID Parameter2, - _In_opt_ PVOID Context - ) -{ - PWE_WINDOW_TREE_CONTEXT context; - PWE_WINDOW_NODE node; - - context = Context; - - switch (Message) - { - case TreeNewGetChildren: - { - PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; - - node = (PWE_WINDOW_NODE)getChildren->Node; - - if (context->TreeNewSortOrder == NoSortOrder) - { - if (!node) - { - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items; - getChildren->NumberOfChildren = context->NodeRootList->Count; - } - else - { - getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; - getChildren->NumberOfChildren = node->Children->Count; - } - } - else - { - if (!node) - { - static PVOID sortFunctions[] = - { - SORT_FUNCTION(Class), - SORT_FUNCTION(Handle), - SORT_FUNCTION(Text), - SORT_FUNCTION(Thread) - }; - int (__cdecl *sortFunction)(void *, const void *, const void *); - - if (context->TreeNewSortColumn < WEWNTLC_MAXIMUM) - sortFunction = sortFunctions[context->TreeNewSortColumn]; - else - sortFunction = NULL; - - if (sortFunction) - { - qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); - } - - getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; - getChildren->NumberOfChildren = context->NodeList->Count; - } - } - } - return TRUE; - case TreeNewIsLeaf: - { - PPH_TREENEW_IS_LEAF isLeaf = Parameter1; - - node = (PWE_WINDOW_NODE)isLeaf->Node; - - if (context->TreeNewSortOrder == NoSortOrder) - isLeaf->IsLeaf = !node->HasChildren; - else - isLeaf->IsLeaf = TRUE; - } - return TRUE; - case TreeNewGetCellText: - { - PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; - - node = (PWE_WINDOW_NODE)getCellText->Node; - - switch (getCellText->Id) - { - case WEWNTLC_CLASS: - PhInitializeStringRef(&getCellText->Text, node->WindowClass); - break; - case WEWNTLC_HANDLE: - PhPrintPointer(node->WindowHandleString, node->WindowHandle); - PhInitializeStringRef(&getCellText->Text, node->WindowHandleString); - break; - case WEWNTLC_TEXT: - getCellText->Text = PhGetStringRef(node->WindowText); - break; - case WEWNTLC_THREAD: - if (!node->ThreadString) - node->ThreadString = PhGetClientIdName(&node->ClientId); - getCellText->Text = PhGetStringRef(node->ThreadString); - break; - default: - return FALSE; - } - - getCellText->Flags = TN_CACHE; - } - return TRUE; - case TreeNewGetNodeColor: - { - PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; - - node = (PWE_WINDOW_NODE)getNodeColor->Node; - - if (!node->WindowVisible) - getNodeColor->ForeColor = RGB(0x55, 0x55, 0x55); - - getNodeColor->Flags = TN_CACHE; - } - return TRUE; - case TreeNewSortChanged: - { - TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); - // Force a rebuild to sort the items. - TreeNew_NodesStructured(hwnd); - } - return TRUE; - case TreeNewKeyDown: - { - PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; - - switch (keyEvent->VirtualKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_COPY, 0); - break; - } - } - return TRUE; - case TreeNewLeftDoubleClick: - { - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_PROPERTIES, 0); - } - return TRUE; - case TreeNewContextMenu: - { - PPH_TREENEW_MOUSE_EVENT mouseEvent = Parameter1; - - SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, MAKELONG(mouseEvent->Location.x, mouseEvent->Location.y)); - } - return TRUE; - } - - return FALSE; -} - -VOID WeClearWindowTree( - _In_ PWE_WINDOW_TREE_CONTEXT Context - ) -{ - ULONG i; - - for (i = 0; i < Context->NodeList->Count; i++) - WepDestroyWindowNode(Context->NodeList->Items[i]); - - PhClearHashtable(Context->NodeHashtable); - PhClearList(Context->NodeList); - PhClearList(Context->NodeRootList); -} - -PWE_WINDOW_NODE WeGetSelectedWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context - ) -{ - PWE_WINDOW_NODE windowNode = NULL; - ULONG i; - - for (i = 0; i < Context->NodeList->Count; i++) - { - windowNode = Context->NodeList->Items[i]; - - if (windowNode->Node.Selected) - return windowNode; - } - - return NULL; -} - -VOID WeGetSelectedWindowNodes( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _Out_ PWE_WINDOW_NODE **Windows, - _Out_ PULONG NumberOfWindows - ) -{ - PPH_LIST list; - ULONG i; - - list = PhCreateList(2); - - for (i = 0; i < Context->NodeList->Count; i++) - { - PWE_WINDOW_NODE node = Context->NodeList->Items[i]; - - if (node->Node.Selected) - { - PhAddItemList(list, node); - } - } - - *Windows = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); - *NumberOfWindows = list->Count; - - PhDereferenceObject(list); -} - -VOID WeExpandAllWindowNodes( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ BOOLEAN Expand - ) -{ - ULONG i; - BOOLEAN needsRestructure = FALSE; - - for (i = 0; i < Context->NodeList->Count; i++) - { - PWE_WINDOW_NODE node = Context->NodeList->Items[i]; - - if (node->Children->Count != 0 && node->Node.Expanded != Expand) - { - node->Node.Expanded = Expand; - needsRestructure = TRUE; - } - } - - if (needsRestructure) - TreeNew_NodesStructured(Context->TreeNewHandle); -} \ No newline at end of file +/* + * Process Hacker Window Explorer - + * window treelist + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include "wndexp.h" +#include "resource.h" + +BOOLEAN WepWindowNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); + +ULONG WepWindowNodeHashtableHashFunction( + _In_ PVOID Entry + ); + +VOID WepDestroyWindowNode( + _In_ PWE_WINDOW_NODE WindowNode + ); + +BOOLEAN NTAPI WepWindowTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ); + +BOOLEAN WordMatchStringRef( + _Inout_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = PhGetStringRef(Context->SearchboxText); + + while (remainingPart.Length != 0) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length != 0) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN WordMatchStringZ( + _Inout_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + + return WordMatchStringRef(Context, &text); +} + +BOOLEAN WeWindowTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PWE_WINDOW_TREE_CONTEXT context = Context; + PWE_WINDOW_NODE windowNode = (PWE_WINDOW_NODE)Node; + + if (PhIsNullOrEmptyString(context->SearchboxText)) + return TRUE; + + if (windowNode->WindowClass[0]) + { + if (WordMatchStringZ(context, windowNode->WindowClass)) + return TRUE; + } + + if (windowNode->WindowHandleString[0]) + { + if (WordMatchStringZ(context, windowNode->WindowHandleString)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(windowNode->WindowText)) + { + if (WordMatchStringRef(context, &windowNode->WindowText->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(windowNode->ThreadString)) + { + if (WordMatchStringRef(context, &windowNode->ThreadString->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(windowNode->ModuleString)) + { + if (WordMatchStringRef(context, &windowNode->ModuleString->sr)) + return TRUE; + } + + return FALSE; +} + +VOID WeInitializeWindowTree( + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle, + _Out_ PWE_WINDOW_TREE_CONTEXT Context + ) +{ + HWND hwnd; + PPH_STRING settings; + + memset(Context, 0, sizeof(WE_WINDOW_TREE_CONTEXT)); + + Context->NodeHashtable = PhCreateHashtable( + sizeof(PWE_WINDOW_NODE), + WepWindowNodeHashtableEqualFunction, + WepWindowNodeHashtableHashFunction, + 100 + ); + Context->NodeList = PhCreateList(100); + Context->NodeRootList = PhCreateList(30); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + hwnd = TreeNewHandle; + PhSetControlTheme(hwnd, L"explorer"); + + TreeNew_SetCallback(hwnd, WepWindowTreeNewCallback, Context); + + PhAddTreeNewColumn(hwnd, WEWNTLC_CLASS, TRUE, L"Class", 180, PH_ALIGN_LEFT, 0, 0); + PhAddTreeNewColumn(hwnd, WEWNTLC_HANDLE, TRUE, L"Handle", 70, PH_ALIGN_LEFT, 1, 0); + PhAddTreeNewColumn(hwnd, WEWNTLC_TEXT, TRUE, L"Text", 220, PH_ALIGN_LEFT, 2, 0); + PhAddTreeNewColumn(hwnd, WEWNTLC_THREAD, TRUE, L"Thread", 150, PH_ALIGN_LEFT, 3, 0); + PhAddTreeNewColumn(hwnd, WEWNTLC_MODULE, TRUE, L"Module", 150, PH_ALIGN_LEFT, 4, 0); + + TreeNew_SetTriState(hwnd, TRUE); + TreeNew_SetSort(hwnd, WEWNTLC_CLASS, NoSortOrder); + + settings = PhGetStringSetting(SETTING_NAME_WINDOW_TREE_LIST_COLUMNS); + PhCmLoadSettings(hwnd, &settings->sr); + PhDereferenceObject(settings); + + Context->SearchboxText = PhReferenceEmptyString(); + + PhInitializeTreeNewFilterSupport( + &Context->FilterSupport, + Context->TreeNewHandle, + Context->NodeList + ); + + Context->TreeFilterEntry = PhAddTreeNewFilter( + &Context->FilterSupport, + WeWindowTreeFilterCallback, + Context + ); +} + +VOID WeDeleteWindowTree( + _In_ PWE_WINDOW_TREE_CONTEXT Context + ) +{ + PPH_STRING settings; + ULONG i; + + PhRemoveTreeNewFilter(&Context->FilterSupport, Context->TreeFilterEntry); + if (Context->SearchboxText) PhDereferenceObject(Context->SearchboxText); + + PhDeleteTreeNewFilterSupport(&Context->FilterSupport); + + settings = PhCmSaveSettings(Context->TreeNewHandle); + PhSetStringSetting2(SETTING_NAME_WINDOW_TREE_LIST_COLUMNS, &settings->sr); + PhDereferenceObject(settings); + + for (i = 0; i < Context->NodeList->Count; i++) + WepDestroyWindowNode(Context->NodeList->Items[i]); + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); + PhDereferenceObject(Context->NodeRootList); +} + +BOOLEAN WepWindowNodeHashtableEqualFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PWE_WINDOW_NODE windowNode1 = *(PWE_WINDOW_NODE *)Entry1; + PWE_WINDOW_NODE windowNode2 = *(PWE_WINDOW_NODE *)Entry2; + + return windowNode1->WindowHandle == windowNode2->WindowHandle; +} + +ULONG WepWindowNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashIntPtr((ULONG_PTR)(*(PWE_WINDOW_NODE *)Entry)->WindowHandle); +} + +PWE_WINDOW_NODE WeAddWindowNode( + _Inout_ PWE_WINDOW_TREE_CONTEXT Context + ) +{ + PWE_WINDOW_NODE windowNode; + + windowNode = PhAllocate(sizeof(WE_WINDOW_NODE)); + memset(windowNode, 0, sizeof(WE_WINDOW_NODE)); + PhInitializeTreeNewNode(&windowNode->Node); + + memset(windowNode->TextCache, 0, sizeof(PH_STRINGREF) * WEWNTLC_MAXIMUM); + windowNode->Node.TextCache = windowNode->TextCache; + windowNode->Node.TextCacheSize = WEWNTLC_MAXIMUM; + + windowNode->Children = PhCreateList(1); + + PhAddEntryHashtable(Context->NodeHashtable, &windowNode); + PhAddItemList(Context->NodeList, windowNode); + + //if (Context->FilterSupport.FilterList) + // windowNode->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &windowNode->Node); + // + //TreeNew_NodesStructured(Context->TreeNewHandle); + + return windowNode; +} + +PWE_WINDOW_NODE WeFindWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ HWND WindowHandle + ) +{ + WE_WINDOW_NODE lookupWindowNode; + PWE_WINDOW_NODE lookupWindowNodePtr = &lookupWindowNode; + PWE_WINDOW_NODE *windowNode; + + lookupWindowNode.WindowHandle = WindowHandle; + + windowNode = (PWE_WINDOW_NODE *)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupWindowNodePtr + ); + + if (windowNode) + return *windowNode; + else + return NULL; +} + +VOID WeRemoveWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ PWE_WINDOW_NODE WindowNode + ) +{ + ULONG index; + + // Remove from hashtable/list and cleanup. + + PhRemoveEntryHashtable(Context->NodeHashtable, &WindowNode); + + if ((index = PhFindItemList(Context->NodeList, WindowNode)) != ULONG_MAX) + PhRemoveItemList(Context->NodeList, index); + + WepDestroyWindowNode(WindowNode); + + TreeNew_NodesStructured(Context->TreeNewHandle); +} + +VOID WepDestroyWindowNode( + _In_ PWE_WINDOW_NODE WindowNode + ) +{ + PhDereferenceObject(WindowNode->Children); + + if (WindowNode->WindowText) PhDereferenceObject(WindowNode->WindowText); + if (WindowNode->ThreadString) PhDereferenceObject(WindowNode->ThreadString); + if (WindowNode->ModuleString) PhDereferenceObject(WindowNode->ModuleString); + + PhFree(WindowNode); +} + +#define SORT_FUNCTION(Column) WepWindowTreeNewCompare##Column + +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl WepWindowTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PWE_WINDOW_NODE node1 = *(PWE_WINDOW_NODE *)_elem1; \ + PWE_WINDOW_NODE node2 = *(PWE_WINDOW_NODE *)_elem2; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + return PhModifySort(sortResult, ((PWE_WINDOW_TREE_CONTEXT)_context)->TreeNewSortOrder); \ +} + +BEGIN_SORT_FUNCTION(Class) +{ + sortResult = _wcsicmp(node1->WindowClass, node2->WindowClass); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Handle) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->WindowHandle, (ULONG_PTR)node2->WindowHandle); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Text) +{ + sortResult = PhCompareString(node1->WindowText, node2->WindowText, TRUE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Thread) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueProcess, (ULONG_PTR)node2->ClientId.UniqueProcess); + + if (sortResult == 0) + sortResult = uintptrcmp((ULONG_PTR)node1->ClientId.UniqueThread, (ULONG_PTR)node2->ClientId.UniqueThread); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Module) +{ + sortResult = PhCompareString(node1->ModuleString, node2->ModuleString, TRUE); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI WepWindowTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PWE_WINDOW_TREE_CONTEXT context; + PWE_WINDOW_NODE node; + + context = Context; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + + node = (PWE_WINDOW_NODE)getChildren->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + { + if (!node) + { + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeRootList->Items; + getChildren->NumberOfChildren = context->NodeRootList->Count; + } + else + { + getChildren->Children = (PPH_TREENEW_NODE *)node->Children->Items; + getChildren->NumberOfChildren = node->Children->Count; + } + } + else + { + if (!node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Class), + SORT_FUNCTION(Handle), + SORT_FUNCTION(Text), + SORT_FUNCTION(Thread), + SORT_FUNCTION(Module) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortColumn < WEWNTLC_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = Parameter1; + + node = (PWE_WINDOW_NODE)isLeaf->Node; + + if (context->TreeNewSortOrder == NoSortOrder) + isLeaf->IsLeaf = !node->HasChildren; + else + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1; + + node = (PWE_WINDOW_NODE)getCellText->Node; + + switch (getCellText->Id) + { + case WEWNTLC_CLASS: + PhInitializeStringRef(&getCellText->Text, node->WindowClass); + break; + case WEWNTLC_HANDLE: + PhInitializeStringRef(&getCellText->Text, node->WindowHandleString); + break; + case WEWNTLC_TEXT: + getCellText->Text = PhGetStringRef(node->WindowText); + break; + case WEWNTLC_THREAD: + getCellText->Text = PhGetStringRef(node->ThreadString); + break; + case WEWNTLC_MODULE: + getCellText->Text = PhGetStringRef(node->ModuleString); + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = Parameter1; + + node = (PWE_WINDOW_NODE)getNodeColor->Node; + + if (!node->WindowVisible) + getNodeColor->ForeColor = RGB(0x55, 0x55, 0x55); + + getNodeColor->Flags = TN_CACHE; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + // Force a rebuild to sort the items. + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + { + PPH_TREENEW_KEY_EVENT keyEvent = Parameter1; + + switch (keyEvent->VirtualKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_COPY, 0); + break; + } + } + return TRUE; + case TreeNewLeftDoubleClick: + { + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_WINDOW_PROPERTIES, 0); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = Parameter1; + + SendMessage(context->ParentWindowHandle, WM_COMMAND, ID_SHOWCONTEXTMENU, (LPARAM)contextMenuEvent); + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + } + + return FALSE; +} + +VOID WeClearWindowTree( + _In_ PWE_WINDOW_TREE_CONTEXT Context + ) +{ + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + WepDestroyWindowNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); + PhClearList(Context->NodeRootList); +} + +PWE_WINDOW_NODE WeGetSelectedWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context + ) +{ + PWE_WINDOW_NODE windowNode = NULL; + ULONG i; + + for (i = 0; i < Context->NodeList->Count; i++) + { + windowNode = Context->NodeList->Items[i]; + + if (windowNode->Node.Selected) + return windowNode; + } + + return NULL; +} + +VOID WeGetSelectedWindowNodes( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _Out_ PWE_WINDOW_NODE **Windows, + _Out_ PULONG NumberOfWindows + ) +{ + PPH_LIST list; + ULONG i; + + list = PhCreateList(2); + + for (i = 0; i < Context->NodeList->Count; i++) + { + PWE_WINDOW_NODE node = Context->NodeList->Items[i]; + + if (node->Node.Selected) + { + PhAddItemList(list, node); + } + } + + *Windows = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfWindows = list->Count; + + PhDereferenceObject(list); +} + +VOID WeExpandAllWindowNodes( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ BOOLEAN Expand + ) +{ + ULONG i; + BOOLEAN needsRestructure = FALSE; + + for (i = 0; i < Context->NodeList->Count; i++) + { + PWE_WINDOW_NODE node = Context->NodeList->Items[i]; + + if (node->Children->Count != 0 && node->Node.Expanded != Expand) + { + node->Node.Expanded = Expand; + needsRestructure = TRUE; + } + } + + if (needsRestructure) + TreeNew_NodesStructured(Context->TreeNewHandle); +} diff --git a/plugins/WindowExplorer/wndtree.h b/plugins/WindowExplorer/wndtree.h index 3157c4d2bd73..d3b418037e2d 100644 --- a/plugins/WindowExplorer/wndtree.h +++ b/plugins/WindowExplorer/wndtree.h @@ -1,98 +1,100 @@ -#ifndef WNDTREE_H -#define WNDTREE_H - -#define WEWNTLC_CLASS 0 -#define WEWNTLC_HANDLE 1 -#define WEWNTLC_TEXT 2 -#define WEWNTLC_THREAD 3 -#define WEWNTLC_MAXIMUM 4 - -typedef struct _WE_WINDOW_NODE -{ - PH_TREENEW_NODE Node; - - struct _WE_WINDOW_NODE *Parent; - PPH_LIST Children; - - union - { - ULONG Flags; - struct - { - ULONG HasChildren : 1; - ULONG WindowVisible : 1; - ULONG Spare : 30; - }; - }; - - PH_STRINGREF TextCache[WEWNTLC_MAXIMUM]; - - HWND WindowHandle; - WCHAR WindowClass[64]; - PPH_STRING WindowText; - CLIENT_ID ClientId; - - WCHAR WindowHandleString[PH_PTR_STR_LEN_1]; - PPH_STRING ThreadString; -} WE_WINDOW_NODE, *PWE_WINDOW_NODE; - -typedef struct _WE_WINDOW_TREE_CONTEXT -{ - HWND ParentWindowHandle; - HWND TreeNewHandle; - ULONG TreeNewSortColumn; - PH_SORT_ORDER TreeNewSortOrder; - - PPH_STRING SearchboxText; - PH_TN_FILTER_SUPPORT FilterSupport; - PPH_TN_FILTER_ENTRY TreeFilterEntry; - - PPH_HASHTABLE NodeHashtable; - PPH_LIST NodeList; - PPH_LIST NodeRootList; -} WE_WINDOW_TREE_CONTEXT, *PWE_WINDOW_TREE_CONTEXT; - -VOID WeInitializeWindowTree( - _In_ HWND ParentWindowHandle, - _In_ HWND TreeNewHandle, - _Out_ PWE_WINDOW_TREE_CONTEXT Context - ); - -VOID WeDeleteWindowTree( - _In_ PWE_WINDOW_TREE_CONTEXT Context - ); - -PWE_WINDOW_NODE WeAddWindowNode( - _Inout_ PWE_WINDOW_TREE_CONTEXT Context - ); - -PWE_WINDOW_NODE WeFindWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ HWND WindowHandle - ); - -VOID WeRemoveWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ PWE_WINDOW_NODE WindowNode - ); - -VOID WeClearWindowTree( - _In_ PWE_WINDOW_TREE_CONTEXT Context - ); - -PWE_WINDOW_NODE WeGetSelectedWindowNode( - _In_ PWE_WINDOW_TREE_CONTEXT Context - ); - -VOID WeGetSelectedWindowNodes( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _Out_ PWE_WINDOW_NODE **Windows, - _Out_ PULONG NumberOfWindows - ); - -VOID WeExpandAllWindowNodes( - _In_ PWE_WINDOW_TREE_CONTEXT Context, - _In_ BOOLEAN Expand - ); - -#endif +#ifndef WNDTREE_H +#define WNDTREE_H + +#define WEWNTLC_CLASS 0 +#define WEWNTLC_HANDLE 1 +#define WEWNTLC_TEXT 2 +#define WEWNTLC_THREAD 3 +#define WEWNTLC_MODULE 4 +#define WEWNTLC_MAXIMUM 5 + +typedef struct _WE_WINDOW_NODE +{ + PH_TREENEW_NODE Node; + + struct _WE_WINDOW_NODE *Parent; + PPH_LIST Children; + + union + { + ULONG Flags; + struct + { + ULONG HasChildren : 1; + ULONG WindowVisible : 1; + ULONG Spare : 30; + }; + }; + + PH_STRINGREF TextCache[WEWNTLC_MAXIMUM]; + + HWND WindowHandle; + WCHAR WindowClass[64]; + PPH_STRING WindowText; + CLIENT_ID ClientId; + + WCHAR WindowHandleString[PH_PTR_STR_LEN_1]; + PPH_STRING ThreadString; + PPH_STRING ModuleString; +} WE_WINDOW_NODE, *PWE_WINDOW_NODE; + +typedef struct _WE_WINDOW_TREE_CONTEXT +{ + HWND ParentWindowHandle; + HWND TreeNewHandle; + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + + PPH_STRING SearchboxText; + PH_TN_FILTER_SUPPORT FilterSupport; + PPH_TN_FILTER_ENTRY TreeFilterEntry; + + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; + PPH_LIST NodeRootList; +} WE_WINDOW_TREE_CONTEXT, *PWE_WINDOW_TREE_CONTEXT; + +VOID WeInitializeWindowTree( + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle, + _Out_ PWE_WINDOW_TREE_CONTEXT Context + ); + +VOID WeDeleteWindowTree( + _In_ PWE_WINDOW_TREE_CONTEXT Context + ); + +PWE_WINDOW_NODE WeAddWindowNode( + _Inout_ PWE_WINDOW_TREE_CONTEXT Context + ); + +PWE_WINDOW_NODE WeFindWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ HWND WindowHandle + ); + +VOID WeRemoveWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ PWE_WINDOW_NODE WindowNode + ); + +VOID WeClearWindowTree( + _In_ PWE_WINDOW_TREE_CONTEXT Context + ); + +PWE_WINDOW_NODE WeGetSelectedWindowNode( + _In_ PWE_WINDOW_TREE_CONTEXT Context + ); + +VOID WeGetSelectedWindowNodes( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _Out_ PWE_WINDOW_NODE **Windows, + _Out_ PULONG NumberOfWindows + ); + +VOID WeExpandAllWindowNodes( + _In_ PWE_WINDOW_TREE_CONTEXT Context, + _In_ BOOLEAN Expand + ); + +#endif diff --git a/plugins/include/commonutil.h b/plugins/include/commonutil.h index 737d9328b42f..075300db272f 100644 --- a/plugins/include/commonutil.h +++ b/plugins/include/commonutil.h @@ -24,549 +24,6 @@ #ifndef _COMMONUTIL_H #define _COMMONUTIL_H -#define COMMONUTIL_PLUGIN_NAME L"ProcessHacker.CommonUtil" -#define COMMONUTIL_INTERFACE_VERSION 1 - -typedef VOID (NTAPI *PUTIL_CREATE_SEARCHBOX_CONTROL)( - _In_ HWND Parent, - _In_ HWND WindowHandle, - _In_ PWSTR BannerText - ); - -typedef HBITMAP (NTAPI *PUTIL_CREATE_IMAGE_FROM_RESOURCE)( - _In_ PVOID DllBase, - _In_ UINT Width, - _In_ UINT Height, - _In_ PCWSTR Name, - _In_ BOOLEAN RGBAImage - ); - -typedef PVOID (NTAPI *PUTIL_CREATE_JSON_PARSER)( - _In_ PSTR JsonString - ); - -typedef VOID (NTAPI *PUTIL_CLEANUP_JSON_PARSER)( - _In_ PVOID Object - ); - -typedef PSTR (NTAPI *PUTIL_GET_JSON_VALUE_STRING)( - _In_ PVOID Object, - _In_ PSTR Key - ); - -typedef INT64 (NTAPI *PUTIL_GET_JSON_VALUE_INT64)( - _In_ PVOID Object, - _In_ PSTR Key - ); - -typedef PVOID (NTAPI *PUTIL_CREATE_JSON_OBJECT)( - VOID - ); - -typedef PVOID (NTAPI *PUTIL_GET_JSON_OBJECT)( - _In_ PVOID Object, - _In_ PSTR Key - ); - -typedef INT (NTAPI *PUTIL_GET_JSON_OBJECT_LENGTH)( - _In_ PVOID Object - ); - -typedef BOOL (NTAPI *PUTIL_GET_JSON_OBJECT_BOOL)( - _In_ PVOID Object, - _In_ PSTR Key - ); - -typedef VOID (NTAPI *PUTIL_ADD_JSON_OBJECT_VALUE)( - _In_ PVOID Object, - _In_ PVOID Entry - ); - -typedef PVOID (NTAPI *PUTIL_CREATE_JSON_ARRAY)( - VOID - ); - -typedef VOID (NTAPI *PUTIL_ADD_JSON_ARRAY_VALUE)( - _In_ PVOID Object, - _In_ PSTR Key, - _In_ PSTR Value - ); - -typedef PSTR (NTAPI *PUTIL_GET_JSON_ARRAY_STRING)( - _In_ PVOID Object - ); - -typedef INT64 (NTAPI *PUTIL_GET_JSON_OBJECT_ARRAY_ULONG)( - _In_ PVOID Object, - _In_ INT Index - ); - -typedef INT (NTAPI *PUTIL_GET_JSON_ARRAY_LENGTH)( - _In_ PVOID Object - ); - -typedef PVOID (NTAPI *PUTIL_GET_JSON_OBJECT_ARRAY_INDEX)( - _In_ PVOID Object, - _In_ INT Index - ); - -typedef struct _JSON_ARRAY_LIST_OBJECT -{ - PSTR Key; - PVOID Entry; -} JSON_ARRAY_LIST_OBJECT, *PJSON_ARRAY_LIST_OBJECT; - -typedef PPH_LIST (NTAPI *PUTIL_GET_JSON_OBJECT_ARRAY_LIST)( - _In_ PVOID Object - ); - -typedef struct _COMMONUTIL_INTERFACE -{ - ULONG Version; - - PUTIL_CREATE_SEARCHBOX_CONTROL CreateSearchControl; - PUTIL_CREATE_IMAGE_FROM_RESOURCE CreateImageFromResource; - - PUTIL_CREATE_JSON_PARSER CreateJsonParser; - PUTIL_CLEANUP_JSON_PARSER CleanupJsonParser; - PUTIL_GET_JSON_VALUE_STRING GetJsonValueAsString; - PUTIL_GET_JSON_VALUE_INT64 GetJsonValueAsUlong; - PUTIL_GET_JSON_OBJECT_BOOL GetJsonValueAsBool; - PUTIL_CREATE_JSON_OBJECT CreateJsonObject; - PUTIL_GET_JSON_OBJECT GetJsonObject; - PUTIL_GET_JSON_OBJECT_LENGTH GetJsonObjectLength; - PUTIL_ADD_JSON_ARRAY_VALUE JsonAddObject; - PUTIL_CREATE_JSON_ARRAY CreateJsonArray; - PUTIL_ADD_JSON_OBJECT_VALUE JsonArrayAddObject; - PUTIL_GET_JSON_ARRAY_STRING GetJsonArrayString; - PUTIL_GET_JSON_OBJECT_ARRAY_ULONG GetJsonArrayUlong; - PUTIL_GET_JSON_ARRAY_LENGTH JsonGetArrayLength; - PUTIL_GET_JSON_OBJECT_ARRAY_INDEX JsonGetObjectArrayIndex; - PUTIL_GET_JSON_OBJECT_ARRAY_LIST JsonGetObjectArrayList; -} COMMONUTIL_INTERFACE, *P_COMMONUTIL_INTERFACE; - -FORCEINLINE -VOID -CreateSearchControl( - _In_ HWND Parent, - _In_ HWND WindowHandle, - _In_ PWSTR BannerText - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - Interface->CreateSearchControl(Parent, WindowHandle, BannerText); - } - } - } -} - -FORCEINLINE -HBITMAP LoadImageFromResources( - _In_ PVOID DllBase, - _In_ UINT Width, - _In_ UINT Height, - _In_ PCWSTR Name, - _In_ BOOLEAN RGBAImage - ) -{ - static PUTIL_CREATE_IMAGE_FROM_RESOURCE createImageFromResource = NULL; - - if (!createImageFromResource) - { - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - createImageFromResource = Interface->CreateImageFromResource; - } - } - } - } - - if (createImageFromResource) - return createImageFromResource(DllBase, Width, Height, Name, RGBAImage); - else - return NULL; -} - -FORCEINLINE -PVOID -CreateJsonParser( - _In_ PSTR JsonString - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->CreateJsonParser(JsonString); - } - } - } - - return NULL; -} - -FORCEINLINE -VOID -CleanupJsonParser( - _In_ PVOID Object - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - Interface->CleanupJsonParser(Object); - } - } - } -} - -FORCEINLINE -PSTR -GetJsonValueAsString( - _In_ PVOID Object, - _In_ PSTR Key - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->GetJsonValueAsString(Object, Key); - } - } - } - - return NULL; -} - -FORCEINLINE -INT64 -GetJsonValueAsUlong( - _In_ PVOID Object, - _In_ PSTR Key - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->GetJsonValueAsUlong(Object, Key); - } - } - } - - return 0; -} - -FORCEINLINE -BOOL -GetJsonValueAsBool( - _In_ PVOID Object, - _In_ PSTR Key - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->GetJsonValueAsBool(Object, Key); - } - } - } - - return FALSE; -} - -FORCEINLINE -PVOID -CreateJsonArray( - VOID - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->CreateJsonArray(); - } - } - } - - return NULL; -} - -FORCEINLINE -PSTR -GetJsonArrayString( - _In_ PVOID Object - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->GetJsonArrayString(Object); - } - } - } - - return NULL; -} - -FORCEINLINE -INT64 -GetJsonArrayUlong( - _In_ PVOID Object, - _In_ INT Index - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->GetJsonArrayUlong(Object, Index); - } - } - } - - return 0; -} - -FORCEINLINE -INT -JsonGetArrayLength( - _In_ PVOID Object - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->JsonGetArrayLength(Object); - } - } - } - - return 0; -} - -FORCEINLINE -PVOID -JsonGetObjectArrayIndex( - _In_ PVOID Object, - _In_ INT Index - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->JsonGetObjectArrayIndex(Object, Index); - } - } - } - - return NULL; -} - -FORCEINLINE -PPH_LIST -JsonGetObjectArrayList( - _In_ PVOID Object - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->JsonGetObjectArrayList(Object); - } - } - } - - return NULL; -} - -FORCEINLINE -PVOID -CreateJsonObject( - VOID - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->CreateJsonObject(); - } - } - } - - return NULL; -} - -FORCEINLINE -PVOID -JsonGetObject( - _In_ PVOID Object, - _In_ PSTR Key - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - return Interface->GetJsonObject(Object, Key); - } - } - } - - return NULL; -} - -FORCEINLINE -VOID -JsonArrayAddObject( - _In_ PVOID Object, - _In_ PVOID Entry - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - Interface->JsonArrayAddObject(Object, Entry); - } - } - } -} - -FORCEINLINE -VOID -JsonAddObject( - _In_ PVOID Object, - _In_ PSTR Key, - _In_ PSTR Value - ) -{ - PPH_PLUGIN toolStatusPlugin; - - if (toolStatusPlugin = PhFindPlugin(COMMONUTIL_PLUGIN_NAME)) - { - P_COMMONUTIL_INTERFACE Interface; - - if (Interface = PhGetPluginInformation(toolStatusPlugin)->Interface) - { - if (Interface->Version <= COMMONUTIL_INTERFACE_VERSION) - { - Interface->JsonAddObject(Object, Key, Value); - } - } - } -} - FORCEINLINE HICON CommonBitmapToIcon( @@ -580,7 +37,7 @@ CommonBitmapToIcon( HBITMAP screenBitmap; ICONINFO iconInfo = { 0 }; - screenDc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenDc = GetDC(NULL); screenBitmap = CreateCompatibleBitmap(screenDc, Width, Height); iconInfo.fIcon = TRUE; @@ -589,8 +46,8 @@ CommonBitmapToIcon( icon = CreateIconIndirect(&iconInfo); - DeleteObject(screenBitmap); - DeleteDC(screenDc); + DeleteBitmap(screenBitmap); + ReleaseDC(NULL, screenDc); return icon; } @@ -646,58 +103,5 @@ FormatAnsiString( return FormatAnsiString_V(Format, argptr); } -FORCEINLINE -HFONT -CommonCreateFont( - _In_ LONG Size, - _In_ INT Weight, - _In_opt_ HWND hwnd - ) -{ - LOGFONT logFont; - - if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) - { - HFONT fontHandle = CreateFont( - -PhMultiplyDivideSigned(Size, PhGlobalDpi, 72), - 0, - 0, - 0, - Weight, - FALSE, - FALSE, - FALSE, - ANSI_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - CLEARTYPE_QUALITY | ANTIALIASED_QUALITY, - DEFAULT_PITCH, - logFont.lfFaceName - ); - - if (hwnd) - { - SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE); - } - - return fontHandle; - } - - return NULL; -} - -FORCEINLINE -HFONT -CommonDuplicateFont( - _In_ HFONT Font - ) -{ - LOGFONT logFont; - - if (GetObject(Font, sizeof(LOGFONT), &logFont)) - return CreateFontIndirect(&logFont); - else - return NULL; -} -#endif \ No newline at end of file +#endif diff --git a/plugins/include/toolstatusintf.h b/plugins/include/toolstatusintf.h index c185b16df030..f68a12f6fb7a 100644 --- a/plugins/include/toolstatusintf.h +++ b/plugins/include/toolstatusintf.h @@ -1,49 +1,79 @@ -#ifndef _TOOLSTATUSINTF_H -#define _TOOLSTATUSINTF_H - -#define TOOLSTATUS_PLUGIN_NAME L"ProcessHacker.ToolStatus" -#define TOOLSTATUS_INTERFACE_VERSION 1 - -typedef PPH_STRING (NTAPI *PTOOLSTATUS_GET_SEARCHBOX_TEXT)( - VOID - ); - -typedef BOOLEAN (NTAPI *PTOOLSTATUS_WORD_MATCH)( - _In_ PPH_STRINGREF Text - ); - -typedef VOID (NTAPI *PTOOLSTATUS_REGISTER_TAB_SEARCH)( - _In_ INT TabIndex, - _In_ PWSTR BannerText - ); - -typedef VOID (NTAPI *PTOOLSTATUS_TAB_ACTIVATE_CONTENT)( - _In_ BOOLEAN Select - ); - -typedef HWND (NTAPI *PTOOLSTATUS_GET_TREENEW_HANDLE)( - VOID - ); - -typedef struct _TOOLSTATUS_TAB_INFO -{ - PWSTR BannerText; - PTOOLSTATUS_TAB_ACTIVATE_CONTENT ActivateContent; - PTOOLSTATUS_GET_TREENEW_HANDLE GetTreeNewHandle; -} TOOLSTATUS_TAB_INFO, *PTOOLSTATUS_TAB_INFO; - -typedef PTOOLSTATUS_TAB_INFO (NTAPI *PTOOLSTATUS_REGISTER_TAB_INFO)( - _In_ INT TabIndex - ); - -typedef struct _TOOLSTATUS_INTERFACE -{ - ULONG Version; - PTOOLSTATUS_GET_SEARCHBOX_TEXT GetSearchboxText; - PTOOLSTATUS_WORD_MATCH WordMatch; - PTOOLSTATUS_REGISTER_TAB_SEARCH RegisterTabSearchDeprecated; - PPH_CALLBACK SearchChangedEvent; - PTOOLSTATUS_REGISTER_TAB_INFO RegisterTabInfo; -} TOOLSTATUS_INTERFACE, *PTOOLSTATUS_INTERFACE; - -#endif +#ifndef _TOOLSTATUSINTF_H +#define _TOOLSTATUSINTF_H + +#define TOOLSTATUS_PLUGIN_NAME L"ProcessHacker.ToolStatus" +#define TOOLSTATUS_INTERFACE_VERSION 1 + +typedef PPH_STRING (NTAPI *PTOOLSTATUS_GET_SEARCHBOX_TEXT)( + VOID + ); + +typedef BOOLEAN (NTAPI *PTOOLSTATUS_WORD_MATCH)( + _In_ PPH_STRINGREF Text + ); + +typedef VOID (NTAPI *PTOOLSTATUS_REGISTER_TAB_SEARCH)( + _In_ INT TabIndex, + _In_ PWSTR BannerText + ); + +typedef VOID (NTAPI *PTOOLSTATUS_TAB_ACTIVATE_CONTENT)( + _In_ BOOLEAN Select + ); + +typedef HWND (NTAPI *PTOOLSTATUS_GET_TREENEW_HANDLE)( + VOID + ); + +typedef struct _TOOLSTATUS_TAB_INFO +{ + PWSTR BannerText; + PTOOLSTATUS_TAB_ACTIVATE_CONTENT ActivateContent; + PTOOLSTATUS_GET_TREENEW_HANDLE GetTreeNewHandle; +} TOOLSTATUS_TAB_INFO, *PTOOLSTATUS_TAB_INFO; + +typedef PTOOLSTATUS_TAB_INFO (NTAPI *PTOOLSTATUS_REGISTER_TAB_INFO)( + _In_ INT TabIndex + ); + +#define TOOLSTATUS_GRAPH_ENABLED 0x1 +#define TOOLSTATUS_GRAPH_UNAVAILABLE 0x2 + +#define TOOLSTATUS_GRAPH_MESSAGE_CALLBACK_DECLARE(ToolStatusGraphCallbackName) \ +VOID ToolStatusGraphCallbackName( \ + _In_ struct _PH_TOOLBAR_GRAPH *Graph, \ + _In_ HWND GraphHandle, \ + _In_ PPH_GRAPH_STATE GraphState, \ + _In_ LPNMHDR Header, \ + _In_opt_ PVOID Context \ + ) + +typedef VOID (NTAPI *PTOOLSTATUS_GRAPH_MESSAGE_CALLBACK)( + _In_ struct _PH_TOOLBAR_GRAPH *Graph, + _In_ HWND GraphHandle, + _In_ PPH_GRAPH_STATE GraphState, + _In_ LPNMHDR Header, + _In_opt_ PVOID Context + ); + +typedef VOID (NTAPI *PTOOLSTATUS_REGISTER_TOOLBAR_GRAPH)( + _In_ struct _PH_PLUGIN *Plugin, + _In_ ULONG Id, + _In_ PWSTR Text, + _In_ ULONG Flags, + _In_opt_ PVOID Context, + _In_opt_ PTOOLSTATUS_GRAPH_MESSAGE_CALLBACK MessageCallback + ); + +typedef struct _TOOLSTATUS_INTERFACE +{ + ULONG Version; + PTOOLSTATUS_GET_SEARCHBOX_TEXT GetSearchboxText; + PTOOLSTATUS_WORD_MATCH WordMatch; + PTOOLSTATUS_REGISTER_TAB_SEARCH RegisterTabSearchDeprecated; + PPH_CALLBACK SearchChangedEvent; + PTOOLSTATUS_REGISTER_TAB_INFO RegisterTabInfo; + PTOOLSTATUS_REGISTER_TOOLBAR_GRAPH RegisterToolbarGraph; +} TOOLSTATUS_INTERFACE, *PTOOLSTATUS_INTERFACE; + +#endif diff --git a/plugins/readme.txt b/plugins/readme.txt index 8a949b95e044..7e0a28756a3e 100644 --- a/plugins/readme.txt +++ b/plugins/readme.txt @@ -1,3 +1,3 @@ -Before compiling these plugins you must have generated the -Process Hacker SDK. You can do this by running makesdk.cmd in +Before compiling these plugins you must have generated the +Process Hacker SDK. You can do this by running makesdk.cmd in build/sdk. \ No newline at end of file diff --git a/tools/CustomBuildTool/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj similarity index 87% rename from tools/CustomBuildTool/CustomBuildTool/CustomBuildTool.csproj rename to tools/CustomBuildTool/CustomBuildTool.csproj index 7e2f0b447a35..ed8ea03ff67c 100644 --- a/tools/CustomBuildTool/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -9,7 +9,7 @@ Properties CustomBuildTool CustomBuildTool - v4.6.2 + v4.7.2 512 true @@ -40,6 +40,7 @@ 4 false false + true AnyCPU @@ -52,6 +53,7 @@ 4 false true + true CustomBuildTool.Program @@ -66,6 +68,7 @@ + @@ -76,24 +79,25 @@ + + + True + True + Resources.resx + - - - - + - Designer - @@ -110,6 +114,12 @@ + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <?xml version='1.0' encoding='utf-8' standalone='yes'?> +<Package + xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" + xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" + xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"> + <Identity + Name="ProcessHacker" + Version="PH_APPX_VERSION" + ProcessorArchitecture="PH_APPX_ARCH" + Publisher="CN=ProcessHacker, O=ProcessHacker, C=AU" /> + <Properties> + <DisplayName>Process Hacker</DisplayName> + <PublisherDisplayName>Process Hacker</PublisherDisplayName> + <Description>ProcessHacker</Description> + <Logo>Assets\ProcessHacker-44.png</Logo> + </Properties> + <Resources> + <Resource Language="en-US" /> + </Resources> + <Dependencies> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.10240.0" MaxVersionTested="10.0.15063.0" /> + </Dependencies> + <Capabilities> + <rescap:Capability Name="runFullTrust"/> + <rescap:Capability Name="allowElevation" /> + </Capabilities> + <Applications> + <Application Id="ProcessHacker" Executable="ProcessHacker.exe" EntryPoint="Windows.FullTrustApplication"> + <uap:VisualElements + BackgroundColor="transparent" + DisplayName="ProcessHacker" + Square150x150Logo="Assets\ProcessHacker-150.png" + Square44x44Logo="Assets\ProcessHacker-44.png" + Description="ProcessHacker"> + <uap:DefaultTile> + <uap:ShowNameOnTiles> + <uap:ShowOn Tile="square150x150Logo" /> + </uap:ShowNameOnTiles> + </uap:DefaultTile> + </uap:VisualElements> + </Application> + </Applications> +</Package> + + \ No newline at end of file diff --git a/tools/CustomBuildTool/CustomBuildTool/Resource Files/ProcessHacker.ico b/tools/CustomBuildTool/Resource Files/ProcessHacker.ico similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Resource Files/ProcessHacker.ico rename to tools/CustomBuildTool/Resource Files/ProcessHacker.ico diff --git a/tools/CustomBuildTool/CustomBuildTool/Resource Files/app.manifest b/tools/CustomBuildTool/Resource Files/app.manifest similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/Resource Files/app.manifest rename to tools/CustomBuildTool/Resource Files/app.manifest diff --git a/tools/CustomBuildTool/Source Files/AppxBuild.cs b/tools/CustomBuildTool/Source Files/AppxBuild.cs new file mode 100644 index 000000000000..bf55db4a4e71 --- /dev/null +++ b/tools/CustomBuildTool/Source Files/AppxBuild.cs @@ -0,0 +1,304 @@ +using System; +using System.IO; +using System.Text; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32; + +namespace CustomBuildTool +{ + public static class AppxBuild + { + public static void BuildAppxPackage(string BuildOutputFolder, string BuildLongVersion, BuildFlags Flags) + { + string error = string.Empty; + string sdkRootPath = string.Empty; + string[] cleanupAppxArray = + { + BuildOutputFolder + "\\AppxManifest32.xml", + BuildOutputFolder + "\\AppxManifest64.xml", + BuildOutputFolder + "\\package32.map", + BuildOutputFolder + "\\package64.map", + BuildOutputFolder + "\\bundle.map", + BuildOutputFolder + "\\ProcessHacker-44.png", + BuildOutputFolder + "\\ProcessHacker-50.png", + BuildOutputFolder + "\\ProcessHacker-150.png" + }; + + Program.PrintColorMessage("Building processhacker-build-package.appxbundle...", ConsoleColor.Cyan); + + using (var view32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) + { + using (var kitsroot = view32.OpenSubKey("Software\\Microsoft\\Windows Kits\\Installed Roots", false)) + { + sdkRootPath = (string)kitsroot.GetValue("WdkBinRootVersioned", string.Empty); + } + } + + if (string.IsNullOrEmpty(sdkRootPath)) + { + Program.PrintColorMessage("[Skipped] Windows SDK", ConsoleColor.Red); + return ; + } + + string makeAppxExePath = Environment.ExpandEnvironmentVariables(sdkRootPath + "\\x64\\MakeAppx.exe"); + string signToolExePath = Environment.ExpandEnvironmentVariables(sdkRootPath + "\\x64\\SignTool.exe"); + + try + { + Win32.ImageResizeFile(44, "ProcessHacker\\resources\\ProcessHacker.png", BuildOutputFolder + "\\ProcessHacker-44.png"); + Win32.ImageResizeFile(50, "ProcessHacker\\resources\\ProcessHacker.png", BuildOutputFolder + "\\ProcessHacker-50.png"); + Win32.ImageResizeFile(150, "ProcessHacker\\resources\\ProcessHacker.png", BuildOutputFolder + "\\ProcessHacker-150.png"); + + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) + { + // create the package manifest + string appxManifestString = Properties.Resources.AppxManifest; + appxManifestString = appxManifestString.Replace("PH_APPX_ARCH", "x86"); + appxManifestString = appxManifestString.Replace("PH_APPX_VERSION", BuildLongVersion); + File.WriteAllText(BuildOutputFolder + "\\AppxManifest32.xml", appxManifestString); + + // create the package mapping file + StringBuilder packageMap32 = new StringBuilder(0x100); + packageMap32.AppendLine("[Files]"); + packageMap32.AppendLine("\"" + BuildOutputFolder + "\\AppxManifest32.xml\" \"AppxManifest.xml\""); + packageMap32.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-44.png\" \"Assets\\ProcessHacker-44.png\""); + packageMap32.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-50.png\" \"Assets\\ProcessHacker-50.png\""); + packageMap32.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); + + var filesToAdd = Directory.GetFiles("bin\\Release32", "*", SearchOption.AllDirectories); + foreach (string filePath in filesToAdd) + { + // Ignore junk files + if (filePath.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".ipdb", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".exp", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".lib", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + packageMap32.AppendLine("\"" + filePath + "\" \"" + filePath.Substring("bin\\Release32\\".Length) + "\""); + } + File.WriteAllText(BuildOutputFolder + "\\package32.map", packageMap32.ToString()); + + // create the package + error = Win32.ShellExecute( + makeAppxExePath, + "pack /o /f " + BuildOutputFolder + "\\package32.map /p " + + BuildOutputFolder + "\\processhacker-build-package-x32.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray, true, Flags); + + // sign the package + error = Win32.ShellExecute( + signToolExePath, + "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + BuildOutputFolder + "\\processhacker-build-package-x32.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray, true, Flags); + } + + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) + { + // create the package manifest + string appxManifestString = Properties.Resources.AppxManifest; + appxManifestString = appxManifestString.Replace("PH_APPX_ARCH", "x64"); + appxManifestString = appxManifestString.Replace("PH_APPX_VERSION", BuildLongVersion); + File.WriteAllText(BuildOutputFolder + "\\AppxManifest64.xml", appxManifestString); + + StringBuilder packageMap64 = new StringBuilder(0x100); + packageMap64.AppendLine("[Files]"); + packageMap64.AppendLine("\"" + BuildOutputFolder + "\\AppxManifest64.xml\" \"AppxManifest.xml\""); + packageMap64.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-44.png\" \"Assets\\ProcessHacker-44.png\""); + packageMap64.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-50.png\" \"Assets\\ProcessHacker-50.png\""); + packageMap64.AppendLine("\"" + BuildOutputFolder + "\\ProcessHacker-150.png\" \"Assets\\ProcessHacker-150.png\""); + + var filesToAdd = Directory.GetFiles("bin\\Release64", "*", SearchOption.AllDirectories); + foreach (string filePath in filesToAdd) + { + // Ignore junk files + if (filePath.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".ipdb", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".exp", StringComparison.OrdinalIgnoreCase) || + filePath.EndsWith(".lib", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + packageMap64.AppendLine("\"" + filePath + "\" \"" + filePath.Substring("bin\\Release64\\".Length) + "\""); + } + File.WriteAllText(BuildOutputFolder + "\\package64.map", packageMap64.ToString()); + + // create the package + error = Win32.ShellExecute( + makeAppxExePath, + "pack /o /f " + BuildOutputFolder + "\\package64.map /p " + + BuildOutputFolder + "\\processhacker-build-package-x64.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray, true, Flags); + + // sign the package + error = Win32.ShellExecute( + signToolExePath, + "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + BuildOutputFolder + "\\processhacker-build-package-x64.appx" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray, true, Flags); + } + + { + // create the appx bundle map + StringBuilder bundleMap = new StringBuilder(0x100); + bundleMap.AppendLine("[Files]"); + bundleMap.AppendLine("\"" + BuildOutputFolder + "\\processhacker-build-package-x32.appx\" \"processhacker-build-package-x32.appx\""); + bundleMap.AppendLine("\"" + BuildOutputFolder + "\\processhacker-build-package-x64.appx\" \"processhacker-build-package-x64.appx\""); + File.WriteAllText(BuildOutputFolder + "\\bundle.map", bundleMap.ToString()); + + if (File.Exists(BuildOutputFolder + "\\processhacker-build-package.appxbundle")) + File.Delete(BuildOutputFolder + "\\processhacker-build-package.appxbundle"); + + // create the appx bundle package + error = Win32.ShellExecute( + makeAppxExePath, + "bundle /f " + BuildOutputFolder + "\\bundle.map /p " + + BuildOutputFolder + "\\processhacker-build-package.appxbundle" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray, true, Flags); + + // sign the appx bundle package + error = Win32.ShellExecute( + signToolExePath, + "sign /v /fd SHA256 /a /f " + BuildOutputFolder + "\\processhacker-appx.pfx /td SHA256 " + + BuildOutputFolder + "\\processhacker-build-package.appxbundle" + ); + Program.PrintColorMessage(Environment.NewLine + error, ConsoleColor.Gray, true, Flags); + } + + foreach (string file in cleanupAppxArray) + { + if (File.Exists(file)) + File.Delete(file); + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + } + } + + public static bool BuildAppxSignature(string BuildOutputFolder) + { + string sdkRootPath = string.Empty; + string[] cleanupAppxArray = + { + BuildOutputFolder + "\\processhacker-appx.pvk", + BuildOutputFolder + "\\processhacker-appx.cer", + BuildOutputFolder + "\\processhacker-appx.pfx" + }; + + Program.PrintColorMessage("Building Appx Signature...", ConsoleColor.Cyan); + + using (var view32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) + { + using (var kitsroot = view32.OpenSubKey("Software\\Microsoft\\Windows Kits\\Installed Roots", false)) + { + sdkRootPath = (string)kitsroot.GetValue("WdkBinRootVersioned", string.Empty); + } + } + + if (string.IsNullOrEmpty(sdkRootPath)) + { + Program.PrintColorMessage("[Skipped] Windows SDK", ConsoleColor.Red); + return false; + } + + string makeCertExePath = Environment.ExpandEnvironmentVariables(sdkRootPath + "\\x64\\MakeCert.exe"); + string pvk2PfxExePath = Environment.ExpandEnvironmentVariables(sdkRootPath + "\\x64\\Pvk2Pfx.exe"); + string certUtilExePath = Win32.SearchFile("certutil.exe"); + + try + { + foreach (string file in cleanupAppxArray) + { + if (File.Exists(file)) + File.Delete(file); + } + + string output = Win32.ShellExecute(makeCertExePath, + "/n " + + "\"CN=ProcessHacker, O=ProcessHacker, C=AU\" " + + "/r /h 0 " + + "/eku \"1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.10.3.13\" " + + "/sv " + + BuildOutputFolder + "\\processhacker-appx.pvk " + + BuildOutputFolder + "\\processhacker-appx.cer " + ); + + if (!string.IsNullOrEmpty(output) && !output.Equals("Succeeded", StringComparison.OrdinalIgnoreCase)) + { + Program.PrintColorMessage("[MakeCert] " + output, ConsoleColor.Red); + return false; + } + + output = Win32.ShellExecute(pvk2PfxExePath, + "/pvk " + BuildOutputFolder + "\\processhacker-appx.pvk " + + "/spc " + BuildOutputFolder + "\\processhacker-appx.cer " + + "/pfx " + BuildOutputFolder + "\\processhacker-appx.pfx " + ); + + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[Pvk2Pfx] " + output, ConsoleColor.Red); + return false; + } + + output = Win32.ShellExecute(certUtilExePath, + "-addStore TrustedPeople " + BuildOutputFolder + "\\processhacker-appx.cer" + ); + + if (!string.IsNullOrEmpty(output) && !output.Contains("command completed successfully")) + { + Program.PrintColorMessage("[Certutil] " + output, ConsoleColor.Red); + return false; + } + + Program.PrintColorMessage("[Pvk2Pfx] " + output, ConsoleColor.Green); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CleanupAppxSignature() + { + try + { + X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine); + + store.Open(OpenFlags.ReadWrite); + + foreach (X509Certificate2 c in store.Certificates) + { + if (c.Subject.Equals("CN=ProcessHacker, O=ProcessHacker, C=AU", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine("Removing: {0}", c.Subject); + store.Remove(c); + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + } +} diff --git a/tools/CustomBuildTool/Source Files/Build.cs b/tools/CustomBuildTool/Source Files/Build.cs new file mode 100644 index 000000000000..b1d5e83cdea2 --- /dev/null +++ b/tools/CustomBuildTool/Source Files/Build.cs @@ -0,0 +1,1415 @@ +/* + * Process Hacker Toolchain - + * Build script + * + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Security; +using System.Text; + +namespace CustomBuildTool +{ + [SuppressUnmanagedCodeSecurity] + public static class Build + { + private static DateTime TimeStart; + private static bool BuildNightly; + private static bool GitExportBuild; + private static string GitExePath; + private static string MSBuildExePath; + private static string CustomSignToolPath; + + private static string BuildBranch; + private static string BuildOutputFolder; + private static string BuildCommit; + private static string BuildVersion; + private static string BuildLongVersion; + private static string BuildCount; + private static string BuildRevision; + //private static string BuildMessage; + + private static long BuildBinFileLength; + private static string BuildBinHash; + private static string BuildBinSig; + + private static long BuildSetupFileLength; + private static string BuildSetupHash; + private static string BuildSetupSig; + +#region Build Config + private static readonly string[] Build_Release_Files = + { + "\\processhacker-build-checksums.txt", + "\\processhacker-build-setup.exe", + "\\processhacker-build-bin.zip", + "\\processhacker-build-src.zip", + "\\processhacker-build-sdk.zip", + "\\processhacker-build-pdb.zip" + }; + private static readonly string[] Build_Nightly_Files = + { + "\\processhacker-build-checksums.txt", + "\\processhacker-build-setup.exe", + "\\processhacker-build-bin.zip", + //"\\processhacker-build-src.zip", + //"\\processhacker-build-sdk.zip" + }; + + private static readonly string[] sdk_directories = + { + "sdk", + "sdk\\include", + "sdk\\dbg\\amd64", + "sdk\\dbg\\i386", + "sdk\\lib\\amd64", + "sdk\\lib\\i386", + "sdk\\samples\\SamplePlugin", + "sdk\\samples\\SamplePlugin\\bin\\Release32" + }; + + private static readonly string[] phnt_headers = + { + "ntdbg.h", + "ntexapi.h", + "ntgdi.h", + "ntioapi.h", + "ntkeapi.h", + "ntldr.h", + "ntlpcapi.h", + "ntmisc.h", + "ntmmapi.h", + "ntnls.h", + "ntobapi.h", + "ntpebteb.h", + "ntpfapi.h", + "ntpnpapi.h", + "ntpoapi.h", + "ntpsapi.h", + "ntregapi.h", + "ntrtl.h", + "ntsam.h", + "ntseapi.h", + "nttmapi.h", + "nttp.h", + "ntwow64.h", + "ntxcapi.h", + "ntzwapi.h", + "phnt.h", + "phnt_ntdef.h", + "phnt_windows.h", + "subprocesstag.h", + "winsta.h" + }; + + private static readonly string[] phlib_headers = + { + "appresolver.h", + "circbuf.h", + "circbuf_h.h", + "cpysave.h", + "dltmgr.h", + "dspick.h", + "emenu.h", + "exlf.h", + "fastlock.h", + "filestream.h", + "graph.h", + "guisup.h", + "hexedit.h", + "hndlinfo.h", + "json.h", + "kphapi.h", + "kphuser.h", + "lsasup.h", + "mapimg.h", + "ph.h", + "phbase.h", + "phbasesup.h", + "phconfig.h", + "phdata.h", + "phnative.h", + "phnativeinl.h", + "phnet.h", + "phsup.h", + "phutil.h", + "provider.h", + "queuedlock.h", + "ref.h", + "secedit.h", + "settings.h", + "svcsup.h", + "symprv.h", + "templ.h", + "treenew.h", + "verify.h", + "workqueue.h" + }; +#endregion + + public static bool InitializeBuildEnvironment(bool CheckDependencies) + { + TimeStart = DateTime.Now; + BuildOutputFolder = "build\\output"; + MSBuildExePath = VisualStudio.GetMsbuildFilePath(); + GitExePath = Win32.SearchFile("git.exe"); + CustomSignToolPath = "tools\\CustomSignTool\\bin\\Release32\\CustomSignTool.exe"; + BuildNightly = !string.Equals(Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%"), "%APPVEYOR_BUILD_API%", StringComparison.OrdinalIgnoreCase); + + try + { + DirectoryInfo info = new DirectoryInfo("."); + + while (info.Parent != null && info.Parent.Parent != null) + { + info = info.Parent; + + if (File.Exists(info.FullName + "\\ProcessHacker.sln")) + { + // Set the root directory. + Directory.SetCurrentDirectory(info.FullName); + break; + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("Error while setting the root directory: " + ex, ConsoleColor.Red); + return false; + } + + if (!File.Exists("ProcessHacker.sln")) + { + Program.PrintColorMessage("Unable to find project root directory... Exiting.", ConsoleColor.Red); + return false; + } + + if (!File.Exists(MSBuildExePath)) + { + Program.PrintColorMessage("MsBuild not installed. Exiting.", ConsoleColor.Red); + return false; + } + + if (File.Exists(GitExePath)) + { + GitExportBuild = string.Equals(Win32.ShellExecute(GitExePath, "rev-parse --is-inside-work-tree"), string.Empty, StringComparison.OrdinalIgnoreCase); + } + else + { + if (CheckDependencies) + { + Program.PrintColorMessage("[Warning] Git not installed.", ConsoleColor.Yellow); + } + } + + if (!Directory.Exists(BuildOutputFolder)) + { + try + { + Directory.CreateDirectory(BuildOutputFolder); + } + catch (Exception ex) + { + Program.PrintColorMessage("Error creating output directory. " + ex, ConsoleColor.Red); + return false; + } + } + + return true; + } + + public static void CleanupBuildEnvironment() + { + try + { + foreach (string file in Build_Release_Files) + { + string sourceFile = BuildOutputFolder + file; + + if (File.Exists(sourceFile)) + File.Delete(sourceFile); + } + + if (Directory.Exists("sdk")) + { + Directory.Delete("sdk", true); + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[Cleanup] " + ex, ConsoleColor.Red); + } + } + + public static void ShowBuildEnvironment(string Platform, bool ShowBuildInfo, bool ShowLogInfo) + { + if (!GitExportBuild && File.Exists(GitExePath)) + { + BuildBranch = Win32.ShellExecute(GitExePath, "rev-parse --abbrev-ref HEAD").Trim(); + BuildCommit = Win32.ShellExecute(GitExePath, "rev-parse HEAD").Trim(); + + string currentGitTag = Win32.ShellExecute(GitExePath, "describe --abbrev=0 --tags --always").Trim(); + BuildRevision = Win32.ShellExecute(GitExePath, "rev-list --count \"" + currentGitTag + ".." + BuildBranch + "\"").Trim(); + BuildCount = Win32.ShellExecute(GitExePath, "rev-list --count " + BuildBranch).Trim(); + } + + if (string.IsNullOrEmpty(BuildRevision)) + BuildRevision = "0"; + if (string.IsNullOrEmpty(BuildCount)) + BuildCount = "0"; + + BuildVersion = "3.0." + BuildRevision; + BuildLongVersion = "3.0." + BuildCount + "." + BuildRevision; + + if (ShowBuildInfo && !GitExportBuild) + { + Program.PrintColorMessage("Version: ", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildVersion, ConsoleColor.Green, false); + + if (!string.IsNullOrEmpty(BuildCommit)) + { + Program.PrintColorMessage(" (", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildCommit.Substring(0, 8), ConsoleColor.DarkYellow, false); + Program.PrintColorMessage(")", ConsoleColor.DarkGray, false); + } + + if (!string.IsNullOrEmpty(BuildBranch)) + { + Program.PrintColorMessage(" [", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(BuildBranch, ConsoleColor.DarkBlue, false); + Program.PrintColorMessage("]", ConsoleColor.DarkGray, false); + } + + Program.PrintColorMessage(Environment.NewLine, ConsoleColor.DarkGray, true); + + // TODO: The Win10 RS4 release has issues with the git pretty format. + //if (!BuildNightly && ShowLogInfo && File.Exists(GitExePath)) + //{ + // Win32.GetConsoleMode(Win32.GetStdHandle(Win32.STD_OUTPUT_HANDLE), out ConsoleMode mode); + // Win32.SetConsoleMode(Win32.GetStdHandle(Win32.STD_OUTPUT_HANDLE), mode | ConsoleMode.ENABLE_VIRTUAL_TERMINAL_PROCESSING); + // + // BuildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"%C(green)[%cd]%Creset %C(bold blue)%an%Creset %<(65,trunc)%s%Creset %C(#696969)(%Creset%C(yellow)%h%Creset%C(#696969))%Creset\" --abbrev-commit"); + // Console.WriteLine(BuildMessage + Environment.NewLine); + // + // //BuildMessage = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an %s\" --abbrev-commit"); + // //BuildMessage = Win32.ShellExecute(GitExePath, "log -n 1 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %an: %<(65,trunc)%s (%h)\" --abbrev-commit"); + // //Console.WriteLine(BuildMessage + Environment.NewLine); + //} + } + } + + public static string BuildTimeStamp() + { + return "[" + (DateTime.Now - TimeStart).ToString("mm\\:ss") + "] "; + } + + public static void ShowBuildStats() + { + TimeSpan buildTime = DateTime.Now - TimeStart; + + Program.PrintColorMessage(Environment.NewLine + "Build Time: ", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(buildTime.Minutes.ToString(), ConsoleColor.Green, false); + Program.PrintColorMessage(" minute(s), ", ConsoleColor.DarkGray, false); + Program.PrintColorMessage(buildTime.Seconds.ToString(), ConsoleColor.Green, false); + Program.PrintColorMessage(" second(s) " + Environment.NewLine, ConsoleColor.DarkGray, true); + } + + public static bool CopyTextFiles() + { + try + { + Win32.CopyIfNewer("README.txt", "bin\\README.txt"); + //Win32.CopyIfNewer("CHANGELOG.txt", "bin\\CHANGELOG.txt"); // TODO: Git log + Win32.CopyIfNewer("COPYRIGHT.txt", "bin\\COPYRIGHT.txt"); + Win32.CopyIfNewer("LICENSE.txt", "bin\\LICENSE.txt"); + } + catch (Exception ex) + { + Program.PrintColorMessage("[CopyTextFiles] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopyLibFiles(BuildFlags Flags) + { + try + { + if (Flags.HasFlag(BuildFlags.BuildDebug)) + { + if (Flags.HasFlag(BuildFlags.Build32bit)) + { + Win32.CopyIfNewer( + "bin\\Debug32\\ProcessHacker.lib", + "sdk\\lib\\i386\\ProcessHacker.lib" + ); + } + + if (Flags.HasFlag(BuildFlags.Build64bit)) + { + Win32.CopyIfNewer( + "bin\\Debug64\\ProcessHacker.lib", + "sdk\\lib\\amd64\\ProcessHacker.lib" + ); + } + } + else + { + if (Flags.HasFlag(BuildFlags.Build32bit)) + { + Win32.CopyIfNewer( + "bin\\Release32\\ProcessHacker.lib", + "sdk\\lib\\i386\\ProcessHacker.lib" + ); + } + + if (Flags.HasFlag(BuildFlags.Build64bit)) + { + Win32.CopyIfNewer( + "bin\\Release64\\ProcessHacker.lib", + "sdk\\lib\\amd64\\ProcessHacker.lib" + ); + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopyWow64Files(BuildFlags Flags) + { + //Program.PrintColorMessage("Copying Wow64 support files...", ConsoleColor.Cyan); + + try + { + if (Flags.HasFlag(BuildFlags.BuildDebug)) + { + if (!Directory.Exists("bin\\Debug64\\x86")) + Directory.CreateDirectory("bin\\Debug64\\x86"); + if (!Directory.Exists("bin\\Debug64\\x86\\plugins")) + Directory.CreateDirectory("bin\\Debug64\\x86\\plugins"); + + Win32.CopyIfNewer("bin\\Debug32\\ProcessHacker.exe", "bin\\Debug64\\x86\\ProcessHacker.exe"); + Win32.CopyIfNewer("bin\\Debug32\\ProcessHacker.pdb", "bin\\Debug64\\x86\\ProcessHacker.pdb"); + Win32.CopyIfNewer("bin\\Debug32\\plugins\\DotNetTools.dll", "bin\\Debug64\\x86\\plugins\\DotNetTools.dll"); + Win32.CopyIfNewer("bin\\Debug32\\plugins\\DotNetTools.pdb", "bin\\Debug64\\x86\\plugins\\DotNetTools.pdb"); + Win32.CopyIfNewer("bin\\Debug32\\plugins\\ExtendedTools.dll", "bin\\Debug64\\x86\\plugins\\ExtendedTools.dll"); + Win32.CopyIfNewer("bin\\Debug32\\plugins\\ExtendedTools.pdb", "bin\\Debug64\\x86\\plugins\\ExtendedTools.pdb"); + } + else + { + if (!Directory.Exists("bin\\Release64\\x86")) + Directory.CreateDirectory("bin\\Release64\\x86"); + if (!Directory.Exists("bin\\Release64\\x86\\plugins")) + Directory.CreateDirectory("bin\\Release64\\x86\\plugins"); + + Win32.CopyIfNewer( + "bin\\Release32\\ProcessHacker.exe", + "bin\\Release64\\x86\\ProcessHacker.exe" + ); + Win32.CopyIfNewer( + "bin\\Release32\\ProcessHacker.pdb", + "bin\\Release64\\x86\\ProcessHacker.pdb" + ); + Win32.CopyIfNewer( + "bin\\Release32\\plugins\\DotNetTools.dll", + "bin\\Release64\\x86\\plugins\\DotNetTools.dll" + ); + Win32.CopyIfNewer( + "bin\\Release32\\plugins\\DotNetTools.pdb", + "bin\\Release64\\x86\\plugins\\DotNetTools.pdb" + ); + Win32.CopyIfNewer( + "bin\\Release32\\plugins\\ExtendedTools.dll", + "bin\\Release64\\x86\\plugins\\ExtendedTools.dll" + ); + Win32.CopyIfNewer( + "bin\\Release32\\plugins\\ExtendedTools.pdb", + "bin\\Release64\\x86\\plugins\\ExtendedTools.pdb" + ); + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopyPluginSdkHeaders() + { + try + { + foreach (string folder in sdk_directories) + { + // Remove the existing SDK directory + //if (Directory.Exists(folder)) + //{ + // Directory.Delete(folder, true); + //} + + // Create the SDK directories + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + } + + // Copy the plugin SDK headers + foreach (string file in phnt_headers) + Win32.CopyIfNewer("phnt\\include\\" + file, "sdk\\include\\" + file); + foreach (string file in phlib_headers) + Win32.CopyIfNewer("phlib\\include\\" + file, "sdk\\include\\" + file); + + Win32.CopyIfNewer("phlib\\mxml\\mxml.h", "sdk\\include\\mxml.h"); + + // Copy readme + Win32.CopyIfNewer("ProcessHacker\\sdk\\readme.txt", "sdk\\readme.txt"); + // Copy symbols + Win32.CopyIfNewer("bin\\Release32\\ProcessHacker.pdb", "sdk\\dbg\\i386\\ProcessHacker.pdb"); + Win32.CopyIfNewer("bin\\Release64\\ProcessHacker.pdb", "sdk\\dbg\\amd64\\ProcessHacker.pdb"); + Win32.CopyIfNewer("KProcessHacker\\bin\\i386\\kprocesshacker.pdb", "sdk\\dbg\\i386\\kprocesshacker.pdb"); + Win32.CopyIfNewer("KProcessHacker\\bin\\amd64\\kprocesshacker.pdb", "sdk\\dbg\\amd64\\kprocesshacker.pdb"); + // Copy libs + Win32.CopyIfNewer("bin\\Release32\\ProcessHacker.lib", "sdk\\lib\\i386\\ProcessHacker.lib"); + Win32.CopyIfNewer("bin\\Release64\\ProcessHacker.lib", "sdk\\lib\\amd64\\ProcessHacker.lib"); + // Copy sample plugin + Win32.CopyIfNewer("plugins\\SamplePlugin\\main.c", "sdk\\samples\\SamplePlugin\\main.c"); + Win32.CopyIfNewer("plugins\\SamplePlugin\\SamplePlugin.sln", "sdk\\samples\\SamplePlugin\\SamplePlugin.sln"); + Win32.CopyIfNewer("plugins\\SamplePlugin\\SamplePlugin.vcxproj", "sdk\\samples\\SamplePlugin\\SamplePlugin.vcxproj"); + Win32.CopyIfNewer("plugins\\SamplePlugin\\SamplePlugin.vcxproj.filters", "sdk\\samples\\SamplePlugin\\SamplePlugin.vcxproj.filters"); + Win32.CopyIfNewer("plugins\\SamplePlugin\\bin\\Release32\\SamplePlugin.dll", "sdk\\samples\\SamplePlugin\\bin\\Release32\\SamplePlugin.dll"); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopyVersionHeader() + { + try + { + HeaderGen gen = new HeaderGen(); + gen.Execute(); + + Win32.CopyIfNewer("ProcessHacker\\sdk\\phapppub.h", "sdk\\include\\phapppub.h"); + Win32.CopyIfNewer("ProcessHacker\\sdk\\phdk.h", "sdk\\include\\phdk.h"); + Win32.CopyIfNewer("ProcessHacker\\resource.h", "sdk\\include\\phappresource.h"); + + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopySidCapsFile(BuildFlags Flags) + { + //Program.PrintColorMessage("Copying capability sid support file...", ConsoleColor.Cyan); + + try + { + if (Flags.HasFlag(BuildFlags.BuildDebug)) + { + if (Flags.HasFlag(BuildFlags.Build32bit)) + { + Win32.CopyIfNewer( + "ProcessHacker\\resources\\capslist.txt", + "bin\\Debug32\\capslist.txt" + ); + } + + if (Flags.HasFlag(BuildFlags.Build64bit)) + { + Win32.CopyIfNewer( + "ProcessHacker\\resources\\capslist.txt", + "bin\\Debug64\\capslist.txt" + ); + } + } + else + { + if (Flags.HasFlag(BuildFlags.Build32bit)) + { + Win32.CopyIfNewer( + "ProcessHacker\\resources\\capslist.txt", + "bin\\Release32\\capslist.txt" + ); + } + + if (Flags.HasFlag(BuildFlags.Build64bit)) + { + Win32.CopyIfNewer( + "ProcessHacker\\resources\\capslist.txt", + "bin\\Release64\\capslist.txt" + ); + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool FixupResourceHeader() + { + try + { + string phappContent = File.ReadAllText("sdk\\include\\phappresource.h"); + + if (!string.IsNullOrWhiteSpace(phappContent)) + { + phappContent = phappContent.Replace("#define ID", "#define PHAPP_ID"); + + File.WriteAllText("sdk\\include\\phappresource.h", phappContent); + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildPublicHeaderFiles() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building public SDK headers...", ConsoleColor.Cyan); + + try + { + HeaderGen gen = new HeaderGen(); + gen.Execute(); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopyKProcessHacker(BuildFlags Flags) + { + if (!File.Exists(CustomSignToolPath)) + return true; + if (!File.Exists("build\\kph.key")) + return true; + + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) + { + if (File.Exists("bin\\Debug32\\ProcessHacker.exe")) + { + File.WriteAllText("bin\\Debug32\\ProcessHacker.sig", string.Empty); + + string output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug32\\ProcessHacker.exe -s bin\\Debug32\\ProcessHacker.sig"); + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[WARN] (Debug32) " + output, ConsoleColor.Yellow, true, Flags); + } + } + + if (File.Exists("bin\\Debug64\\ProcessHacker.exe")) + { + File.WriteAllText("bin\\Debug64\\ProcessHacker.sig", string.Empty); + + string output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Debug64\\ProcessHacker.exe -s bin\\Debug64\\ProcessHacker.sig"); + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[WARN] (Debug64) " + output, ConsoleColor.Yellow, true, Flags); + } + } + } + + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) + { + if (File.Exists("bin\\Release32\\ProcessHacker.exe")) + { + File.WriteAllText("bin\\Release32\\ProcessHacker.sig", string.Empty); + + string output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release32\\ProcessHacker.exe -s bin\\Release32\\ProcessHacker.sig"); + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[WARN] (Release32) " + output, ConsoleColor.Yellow, true, Flags); + } + } + + if (File.Exists("bin\\Release64\\ProcessHacker.exe")) + { + File.WriteAllText("bin\\Release64\\ProcessHacker.sig", string.Empty); + + string output = Win32.ShellExecute(CustomSignToolPath, "sign -k build\\kph.key bin\\Release64\\ProcessHacker.exe -s bin\\Release64\\ProcessHacker.sig"); + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[WARN] (Release64) " + output, ConsoleColor.Yellow, true, Flags); + } + } + } + + try + { + if (Flags.HasFlag(BuildFlags.BuildDebug)) + { + if (Directory.Exists("bin\\Debug32")) + { + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", + "bin\\Debug32\\kprocesshacker.sys" + ); + } + + if (Directory.Exists("bin\\Debug64")) + { + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", + "bin\\Debug64\\kprocesshacker.sys" + ); + } + } + else + { + if (Directory.Exists("bin\\Release32")) + { + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\i386\\kprocesshacker.sys", + "bin\\Release32\\kprocesshacker.sys" + ); + } + + if (Directory.Exists("bin\\Release64")) + { + Win32.CopyIfNewer( + "KProcessHacker\\bin-signed\\amd64\\kprocesshacker.sys", + "bin\\Release64\\kprocesshacker.sys" + ); + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] (kprocesshacker.sys)" + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool CopyKeyFiles() + { + string buildKey = Environment.ExpandEnvironmentVariables("%NIGHTLY_BUILD_KEY%").Replace("%NIGHTLY_BUILD_KEY%", string.Empty); + string kphKey = Environment.ExpandEnvironmentVariables("%KPH_BUILD_KEY%").Replace("%KPH_BUILD_KEY%", string.Empty); + string vtBuildKey = Environment.ExpandEnvironmentVariables("%VIRUSTOTAL_BUILD_KEY%").Replace("%VIRUSTOTAL_BUILD_KEY%", string.Empty); + + if (!BuildNightly) + return true; + + if (string.IsNullOrEmpty(buildKey)) + { + Program.PrintColorMessage("[Build] (missing build key).", ConsoleColor.Yellow); + return false; + } + if (string.IsNullOrEmpty(kphKey)) + { + Program.PrintColorMessage("[Build] (missing kph key).", ConsoleColor.Yellow); + return false; + } + if (string.IsNullOrEmpty(vtBuildKey)) + { + Program.PrintColorMessage("[Build] (missing VT key).", ConsoleColor.Yellow); + return false; + } + + try + { + Verify.Decrypt("build\\kph.s", "build\\kph.key", kphKey); + Verify.Decrypt("build\\nightly.s", "build\\nightly.key", buildKey); + Verify.Decrypt("build\\virustotal.s", "build\\virustotal.h", vtBuildKey); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] (Verify) " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildWebSetupExe() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-websetup.exe...", ConsoleColor.Cyan); + + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit)) + return false; + + try + { + if (File.Exists(BuildOutputFolder + "\\processhacker-build-websetup.exe")) + File.Delete(BuildOutputFolder + "\\processhacker-build-websetup.exe"); + + File.Move( + "tools\\CustomSetupTool\\CustomSetupTool\\bin\\Release32\\CustomSetupTool.exe", + BuildOutputFolder + "\\processhacker-build-websetup.exe" + ); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + try + { + //var webSetupVersion = System.Diagnostics.FileVersionInfo.GetVersionInfo(BuildOutputFolder + "\\processhacker-build-websetup.exe"); + //BuildWebSetupVersion = webSetupVersion.FileVersion.Replace(",", "."); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildSetupExe() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-setup.exe... ", ConsoleColor.Cyan, false); + + if (!BuildSolution("tools\\CustomSetupTool\\CustomSetupTool.sln", BuildFlags.Build32bit | BuildFlags.BuildApi)) + return false; + + try + { + if (File.Exists(BuildOutputFolder + "\\processhacker-build-setup.exe")) + File.Delete(BuildOutputFolder + "\\processhacker-build-setup.exe"); + + File.Move( + "tools\\CustomSetupTool\\CustomSetupTool\\bin\\Release32\\CustomSetupTool.exe", + BuildOutputFolder + "\\processhacker-build-setup.exe" + ); + + Program.PrintColorMessage(new FileInfo(BuildOutputFolder + "\\processhacker-build-setup.exe").Length.ToPrettySize(), ConsoleColor.Green); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildSdkZip() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-sdk.zip... ", ConsoleColor.Cyan, false); + + try + { + Zip.CreateCompressedSdkFromFolder("sdk", BuildOutputFolder + "\\processhacker-build-sdk.zip"); + + Program.PrintColorMessage(new FileInfo(BuildOutputFolder + "\\processhacker-build-sdk.zip").Length.ToPrettySize(), ConsoleColor.Green); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildBinZip() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-bin.zip... ", ConsoleColor.Cyan, false); + + try + { + if (File.Exists("bin\\Release32\\ProcessHacker.exe.settings.xml")) + File.Delete("bin\\Release32\\ProcessHacker.exe.settings.xml"); + if (File.Exists("bin\\Release64\\ProcessHacker.exe.settings.xml")) + File.Delete("bin\\Release64\\ProcessHacker.exe.settings.xml"); + + File.Create("bin\\Release32\\ProcessHacker.exe.settings.xml").Dispose(); + File.Create("bin\\Release64\\ProcessHacker.exe.settings.xml").Dispose(); + } + catch { } + + try + { + if (File.Exists("bin\\Release32\\usernotesdb.xml")) + File.Delete("bin\\Release32\\usernotesdb.xml"); + if (File.Exists("bin\\Release64\\usernotesdb.xml")) + File.Delete("bin\\Release64\\usernotesdb.xml"); + + File.Create("bin\\Release32\\usernotesdb.xml").Dispose(); + File.Create("bin\\Release64\\usernotesdb.xml").Dispose(); + } + catch { } + + try + { + if (Directory.Exists("bin\\x32")) + Directory.Delete("bin\\x32", true); + if (Directory.Exists("bin\\x64")) + Directory.Delete("bin\\x64", true); + + Directory.Move("bin\\Release32", "bin\\x32"); + Directory.Move("bin\\Release64", "bin\\x64"); + + if (File.Exists(BuildOutputFolder + "\\processhacker-build-bin.zip")) + File.Delete(BuildOutputFolder + "\\processhacker-build-bin.zip"); + Zip.CreateCompressedFolder("bin", BuildOutputFolder + "\\processhacker-build-bin.zip"); + + if (File.Exists(BuildOutputFolder + "\\processhacker-build-bin.64")) + File.Delete(BuildOutputFolder + "\\processhacker-build-bin.64"); + File.WriteAllBytes( + BuildOutputFolder + "\\processhacker-build-bin.64", + Encoding.UTF8.GetBytes(Convert.ToBase64String(File.ReadAllBytes(BuildOutputFolder + "\\processhacker-build-bin.zip"))) + ); + + Directory.Move("bin\\x32", "bin\\Release32"); + Directory.Move("bin\\x64", "bin\\Release64"); + + Program.PrintColorMessage(new FileInfo(BuildOutputFolder + "\\processhacker-build-bin.zip").Length.ToPrettySize(), ConsoleColor.Green); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildSrcZip() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-src.zip... ", ConsoleColor.Cyan, false); + + if (!File.Exists(GitExePath)) + { + Program.PrintColorMessage("[SKIPPED] Git not installed.", ConsoleColor.Yellow); + return false; + } + + try + { + if (File.Exists(BuildOutputFolder + "\\processhacker-build-src.zip")) + File.Delete(BuildOutputFolder + "\\processhacker-build-src.zip"); + + string output = Win32.ShellExecute( + GitExePath, + "--git-dir=.git " + + "--work-tree=.\\ archive " + + "--format zip " + + "--output " + BuildOutputFolder + "\\processhacker-build-src.zip " + + BuildBranch + ); + + if (!string.IsNullOrEmpty(output)) + { + Program.PrintColorMessage("[ERROR] " + output, ConsoleColor.Red); + return false; + } + + Program.PrintColorMessage(new FileInfo(BuildOutputFolder + "\\processhacker-build-src.zip").Length.ToPrettySize(), ConsoleColor.Green); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildPdbZip() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-pdb.zip... ", ConsoleColor.Cyan, false); + + try + { + Zip.CreateCompressedPdbFromFolder(".\\", BuildOutputFolder + "\\processhacker-build-pdb.zip"); + + Program.PrintColorMessage(new FileInfo(BuildOutputFolder + "\\processhacker-build-pdb.zip").Length.ToPrettySize(), ConsoleColor.Green); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildChecksumsFile() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building build-checksums.txt...", ConsoleColor.Cyan); + + try + { + if (File.Exists(BuildOutputFolder + "\\processhacker-build-checksums.txt")) + File.Delete(BuildOutputFolder + "\\processhacker-build-checksums.txt"); + + //if (File.Exists(BuildOutputFolder + "\\processhacker-build-websetup.exe")) + // BuildWebSetupFileLength = new FileInfo(BuildOutputFolder + "\\processhacker-build-websetup.exe").Length; + if (File.Exists(BuildOutputFolder + "\\processhacker-build-setup.exe")) + BuildSetupFileLength = new FileInfo(BuildOutputFolder + "\\processhacker-build-setup.exe").Length; + if (File.Exists(BuildOutputFolder + "\\processhacker-build-bin.zip")) + BuildBinFileLength = new FileInfo(BuildOutputFolder + "\\processhacker-build-bin.zip").Length; + //if (File.Exists(BuildOutputFolder + "\\processhacker-build-websetup.exe")) + // BuildWebSetupHash = Verify.HashFile(BuildOutputFolder + "\\processhacker-build-websetup.exe"); + if (File.Exists(BuildOutputFolder + "\\processhacker-build-setup.exe")) + BuildSetupHash = Verify.HashFile(BuildOutputFolder + "\\processhacker-build-setup.exe"); + if (File.Exists(BuildOutputFolder + "\\processhacker-build-bin.zip")) + BuildBinHash = Verify.HashFile(BuildOutputFolder + "\\processhacker-build-bin.zip"); + + StringBuilder sb = new StringBuilder(); + //sb.AppendLine("processhacker-build-websetup.exe"); + //sb.AppendLine("SHA256: " + BuildWebSetupHash + Environment.NewLine); + sb.AppendLine("processhacker-build-setup.exe"); + sb.AppendLine("SHA256: " + BuildSetupHash + Environment.NewLine); + sb.AppendLine("processhacker-build-bin.zip"); + sb.AppendLine("SHA256: " + BuildBinHash + Environment.NewLine); + + File.WriteAllText(BuildOutputFolder + "\\processhacker-build-checksums.txt", sb.ToString()); + } + catch (Exception ex) + { + Program.PrintColorMessage("[ERROR] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + public static bool BuildUpdateSignature() + { + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false); + Program.PrintColorMessage("Building release signatures...", ConsoleColor.Cyan); + + if (!File.Exists(CustomSignToolPath)) + { + Program.PrintColorMessage("[SKIPPED] CustomSignTool not found.", ConsoleColor.Yellow); + return true; + } + + if (!File.Exists("build\\nightly.key")) + { + Program.PrintColorMessage("[SKIPPED] nightly.key not found.", ConsoleColor.Yellow); + return true; + } + + if (!File.Exists(BuildOutputFolder + "\\processhacker-build-bin.zip")) + { + Program.PrintColorMessage("[SKIPPED] build-bin.zip not found.", ConsoleColor.Yellow); + return false; + } + if (!File.Exists(BuildOutputFolder + "\\processhacker-build-setup.exe")) + { + Program.PrintColorMessage("[SKIPPED] build-setup.exe not found.", ConsoleColor.Yellow); + return false; + } + + BuildBinSig = Win32.ShellExecute( + CustomSignToolPath, + "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-bin.zip -h" + ); + BuildSetupSig = Win32.ShellExecute( + CustomSignToolPath, + "sign -k build\\nightly.key " + BuildOutputFolder + "\\processhacker-build-setup.exe -h" + ); + + return true; + } + + //public static bool BuildRenameReleaseFiles() + //{ + // try + // { + // foreach (string file in Build_Release_Files) + // { + // string sourceFile = BuildOutputFolder + file; + // string destinationFile = BuildOutputFolder + file.Replace("-build-", $"-{BuildVersion}-"); + // + // if (File.Exists(destinationFile)) + // File.Delete(destinationFile); + // + // if (File.Exists(sourceFile)) + // File.Move(sourceFile, destinationFile); + // } + // } + // catch (Exception ex) + // { + // Program.PrintColorMessage("[WebServiceUploadBuild] " + ex, ConsoleColor.Red); + // return false; + // } + // + // return true; + //} + + public static string GetBuildLogString() + { + return Win32.ShellExecute(GitExePath, "log -n 1800 --graph --pretty=format:\"%C(yellow)%h%Creset %C(bold blue)%an%Creset %s %C(dim green)(%cr)\" --abbrev-commit ").Trim(); + } + + public static bool BuildSolution(string Solution, BuildFlags Flags) + { + if ((Flags & BuildFlags.Build32bit) == BuildFlags.Build32bit) + { + StringBuilder compilerOptions = new StringBuilder(); + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false, Flags); + Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); + Program.PrintColorMessage("x32", ConsoleColor.Green, false, Flags); + Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); + + if (Flags.HasFlag(BuildFlags.BuildApi)) + compilerOptions.Append("PH_BUILD_API;"); + if (!string.IsNullOrEmpty(BuildCommit)) + compilerOptions.Append($"PHAPP_VERSION_COMMITHASH=\"{BuildCommit.Substring(0, 8)}\";"); + compilerOptions.Append($"PHAPP_VERSION_REVISION=\"{BuildRevision}\";"); + compilerOptions.Append($"PHAPP_VERSION_BUILD=\"{BuildCount}\""); + + string error32 = Win32.ShellExecute( + MSBuildExePath, + "/m /nologo /verbosity:quiet " + + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug " : "Release ") + + "/p:Platform=Win32 " + + "/p:ExternalCompilerOptions=\"" + compilerOptions.ToString() + "\" " + + Solution + ); + + if (!string.IsNullOrEmpty(error32)) + { + Program.PrintColorMessage("[ERROR] " + error32, ConsoleColor.Red, true, Flags | BuildFlags.BuildVerbose); + return false; + } + } + + if ((Flags & BuildFlags.Build64bit) == BuildFlags.Build64bit) + { + StringBuilder compilerOptions = new StringBuilder(); + Program.PrintColorMessage(BuildTimeStamp(), ConsoleColor.DarkGray, false, Flags); + Program.PrintColorMessage("Building " + Path.GetFileNameWithoutExtension(Solution) + " (", ConsoleColor.Cyan, false, Flags); + Program.PrintColorMessage("x64", ConsoleColor.Green, false, Flags); + Program.PrintColorMessage(")...", ConsoleColor.Cyan, true, Flags); + + if (Flags.HasFlag(BuildFlags.BuildApi)) + compilerOptions.Append("PH_BUILD_API;"); + if (!string.IsNullOrEmpty(BuildCommit)) + compilerOptions.Append($"PHAPP_VERSION_COMMITHASH=\"{BuildCommit.Substring(0, 8)}\";"); + compilerOptions.Append($"PHAPP_VERSION_REVISION=\"{BuildRevision}\";"); + compilerOptions.Append($"PHAPP_VERSION_BUILD=\"{BuildCount}\""); + + string error64 = Win32.ShellExecute( + MSBuildExePath, + "/m /nologo /verbosity:quiet " + + "/p:Configuration=" + (Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug " : "Release ") + + "/p:Platform=x64 " + + "/p:ExternalCompilerOptions=\"" + compilerOptions.ToString() + "\" " + + Solution + ); + + if (!string.IsNullOrEmpty(error64)) + { + Program.PrintColorMessage("[ERROR] " + error64, ConsoleColor.Red, true, Flags | BuildFlags.BuildVerbose); + return false; + } + } + + return true; + } + + + public static bool BuildDeployUpdateConfig() + { + string buildJobId; + string buildPostUrl; + string buildPostApiKey; + string buildChangelog; + string buildSummary; + string buildMessage; + string buildPostString; + + buildJobId = Environment.ExpandEnvironmentVariables("%APPVEYOR_JOB_ID%").Replace("%APPVEYOR_JOB_ID%", string.Empty); + buildPostUrl = Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_API%").Replace("%APPVEYOR_BUILD_API%", string.Empty); + buildPostApiKey = Environment.ExpandEnvironmentVariables("%APPVEYOR_BUILD_KEY%").Replace("%APPVEYOR_BUILD_KEY%", string.Empty); + + if (string.IsNullOrEmpty(buildJobId)) + return true; + if (string.IsNullOrEmpty(buildPostUrl)) + return true; + if (string.IsNullOrEmpty(buildPostApiKey)) + return true; + + if (string.IsNullOrEmpty(BuildVersion)) + return true; + if (string.IsNullOrEmpty(BuildSetupHash)) + return true; + if (string.IsNullOrEmpty(BuildSetupSig)) + return true; + if (string.IsNullOrEmpty(BuildBinHash)) + return true; + if (string.IsNullOrEmpty(BuildBinSig)) + return true; + + buildChangelog = Win32.ShellExecute(GitExePath, "log -n 30 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\""); + buildSummary = Win32.ShellExecute(GitExePath, "log -n 5 --date=format:%Y-%m-%d --pretty=format:\"[%cd] %s (%an)\" --abbrev-commit"); + buildMessage = Win32.ShellExecute(GitExePath, "log -1 --pretty=%B"); + + buildPostString = Json.Serialize(new BuildUpdateRequest + { + BuildUpdated = TimeStart.ToString("o"), + BuildVersion = BuildVersion, + BuildCommit = BuildCommit, + BuildMessage = buildMessage, + + BinUrl = $"https://ci.appveyor.com/api/buildjobs/%7BbuildJobId%7D/artifacts/processhacker-build-bin.zip", + BinLength = BuildBinFileLength.ToString(), + BinHash = BuildBinHash, + BinSig = BuildBinSig, + + SetupUrl = $"https://ci.appveyor.com/api/buildjobs/%7BbuildJobId%7D/artifacts/processhacker-build-setup.exe", + SetupLength = BuildSetupFileLength.ToString(), + SetupHash = BuildSetupHash, + SetupSig = BuildSetupSig, + + Message = buildSummary, + Changelog = buildChangelog + }); + + if (string.IsNullOrEmpty(buildPostString)) + return false; + + Console.Write(Environment.NewLine); + Program.PrintColorMessage("Updating Build WebService... " + BuildVersion, ConsoleColor.Cyan); + + try + { + using (HttpClient client = new HttpClient()) + { + client.DefaultRequestHeaders.Add("X-ApiKey", buildPostApiKey); + + var httpTask = client.PostAsync(buildPostUrl, new StringContent(buildPostString, Encoding.UTF8, "application/json")); + httpTask.Wait(); + + if (!httpTask.Result.IsSuccessStatusCode) + { + Program.PrintColorMessage("[UpdateBuildWebService] " + httpTask.Result, ConsoleColor.Red); + return false; + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[UpdateBuildWebService] " + ex, ConsoleColor.Red); + return false; + } + + if (!AppVeyor.UpdateBuildVersion(BuildVersion)) // HACK: Update Appveyor build version string. + { + return false; + } + + return true; + } + + public static bool BuildDeployUploadArtifacts() + { + string buildPostUrl = Environment.ExpandEnvironmentVariables("%APPVEYOR_NIGHTLY_URL%").Replace("%APPVEYOR_NIGHTLY_URL%", string.Empty); + string buildPostKey = Environment.ExpandEnvironmentVariables("%APPVEYOR_NIGHTLY_KEY%").Replace("%APPVEYOR_NIGHTLY_KEY%", string.Empty); + string buildPostName = Environment.ExpandEnvironmentVariables("%APPVEYOR_NIGHTLY_NAME%").Replace("%APPVEYOR_NIGHTLY_NAME%", string.Empty); + + if (string.IsNullOrEmpty(buildPostUrl)) + return false; + if (string.IsNullOrEmpty(buildPostKey)) + return false; + if (string.IsNullOrEmpty(buildPostName)) + return false; + + Console.Write(Environment.NewLine); + + try + { + foreach (string file in Build_Release_Files) + { + string sourceFile = BuildOutputFolder + file; + string filename = Path.GetFileName(sourceFile); + + if (File.Exists(sourceFile)) + { + FtpWebRequest request = (FtpWebRequest)WebRequest.Create(buildPostUrl + filename); + request.Credentials = new NetworkCredential(buildPostKey, buildPostName); + request.Method = WebRequestMethods.Ftp.UploadFile; + request.Timeout = System.Threading.Timeout.Infinite; + request.EnableSsl = true; + request.UsePassive = true; + request.UseBinary = true; + + Program.PrintColorMessage($"Uploading {filename}...", ConsoleColor.Cyan, true); + + using (BufferedStream localStream = new BufferedStream(File.OpenRead(sourceFile))) + using (BufferedStream remoteStream = new BufferedStream(request.GetRequestStream())) + { + localStream.CopyTo(remoteStream, 4096); + } + + using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) + { + if (response.StatusCode != FtpStatusCode.CommandOK && response.StatusCode != FtpStatusCode.ClosingData) + { + Program.PrintColorMessage($"[HttpWebResponse] {response.StatusDescription}", ConsoleColor.Red); + return false; + } + } + + //string boundary = "---------------------------" + Guid.NewGuid().ToString(); + //byte[] boundarybytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); + //byte[] headerbytes = Encoding.UTF8.GetBytes($"Content-Disposition: form-data; name=\"file\"; filename=\"{filename}\"\r\n\r\n"); + // + //HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(buildPostUrl)); + //request.KeepAlive = true; + //request.SendChunked = true; + //request.AllowWriteStreamBuffering = true; + //request.ServicePoint.Expect100Continue = false; + //request.ServicePoint.ReceiveBufferSize = 4096; + //request.ServicePoint.ConnectionLeaseTimeout = System.Threading.Timeout.Infinite; + //request.ReadWriteTimeout = System.Threading.Timeout.Infinite; + //request.Timeout = System.Threading.Timeout.Infinite; + //request.Method = WebRequestMethods.Http.Post; + //request.ContentType = "multipart/form-data; boundary=" + boundary; + //request.Headers.Add("X-ApiKey", buildPostKey); + // + //Program.PrintColorMessage($"Uploading {filename}...", ConsoleColor.Cyan, true); + // + //using (FileStream fileStream = File.OpenRead(sourceFile)) + //using (BufferedStream localStream = new BufferedStream(fileStream)) + //using (BufferedStream remoteStream = new BufferedStream(request.GetRequestStream())) + //{ + // int bytesRead = 0; + // var totalRead = 0; + // byte[] buffer = new byte[4096]; + // + // remoteStream.Write(boundarybytes, 0, boundarybytes.Length); + // remoteStream.Write(headerbytes, 0, headerbytes.Length); + // + // while ((bytesRead = localStream.Read(buffer, 0, buffer.Length)) != 0) + // { + // totalRead += bytesRead; + // remoteStream.Write(buffer, 0, bytesRead); + // } + // + // remoteStream.Write(boundarybytes, 0, boundarybytes.Length); + //} + // + //using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + //{ + // if (response.StatusCode != HttpStatusCode.OK) + // { + // Program.PrintColorMessage("[HttpWebResponse]" + response.StatusDescription, ConsoleColor.Red); + // return false; + // } + //} + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[UploadBuildWebServiceAsync-Exception]" + ex, ConsoleColor.Red); + return false; + } + + if (!AppVeyor.AppVeyorNightlyBuild()) + { + Program.PrintColorMessage("[SKIPPED] (Appveyor missing)", ConsoleColor.Yellow); + return true; + } + + try + { + foreach (string file in Build_Nightly_Files) + { + string sourceFile = BuildOutputFolder + file; + + if (File.Exists(sourceFile)) + { + if (!AppVeyor.UploadFile(sourceFile)) + { + Program.PrintColorMessage("[WebServiceAppveyorUploadFile]", ConsoleColor.Red); + return false; + } + } + else + { + Program.PrintColorMessage("[SKIPPED] missing file: " + sourceFile, ConsoleColor.Yellow); + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[WebServiceAppveyorPushArtifact] " + ex, ConsoleColor.Red); + return false; + } + + return true; + } + + + public static void BuildAppxPackage(BuildFlags Flags) + { + AppxBuild.BuildAppxPackage(BuildOutputFolder, BuildLongVersion, BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); + } + + public static bool BuildAppxSignature() + { + return AppxBuild.BuildAppxSignature(BuildOutputFolder); + } + } +} diff --git a/tools/CustomBuildTool/Source Files/HeaderGen.cs b/tools/CustomBuildTool/Source Files/HeaderGen.cs new file mode 100644 index 000000000000..dd4f9b9e5b53 --- /dev/null +++ b/tools/CustomBuildTool/Source Files/HeaderGen.cs @@ -0,0 +1,232 @@ +/* + * Process Hacker Toolchain - + * Build script + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.IO; +using System.Linq; +using System.Collections.Generic; + +namespace CustomBuildTool +{ + public class HeaderFile + { + public string Name; + public List Lines; + public List Dependencies; + } + + public class HeaderGen + { + private string BaseDirectory; + private string[] Modes; + private string[] Files; + private string OutputFile; + private string Header; + private string Footer; + + public HeaderGen() + { + this.OutputFile = "..\\sdk\\phapppub.h"; + this.BaseDirectory = "ProcessHacker\\include"; + this.Modes = new[] { "phapppub" }; + this.Files = new[] + { + "phapp.h", + "appsup.h", + "phfwddef.h", + "procprv.h", + "srvprv.h", + "netprv.h", + "modprv.h", + "thrdprv.h", + "hndlprv.h", + "memprv.h", + "phuisup.h", + "colmgr.h", + "proctree.h", + "srvlist.h", + "netlist.h", + "thrdlist.h", + "modlist.h", + "hndllist.h", + "memlist.h", + "extmgr.h", + "mainwnd.h", + "notifico.h", + "phplug.h", + "actions.h", + "procprp.h", + "procprpp.h", + "phsvccl.h", + "sysinfo.h", + "procgrp.h", + "miniinfo.h" + }; + this.Header = "#ifndef _PH_PHAPPPUB_H\r\n#define _PH_PHAPPPUB_H\r\n\r\n// This file was automatically generated. Do not edit.\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n"; + this.Footer = "\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"; + } + + private List OrderHeaderFiles(List headerFiles) + { + var result = new List(); + var done = new HashSet(); + + foreach (var h in headerFiles) + OrderHeaderFiles(result, done, h); + + return result; + } + + private void OrderHeaderFiles(List result, HashSet done, HeaderFile headerFile) + { + if (done.Contains(headerFile)) + return; + + done.Add(headerFile); + + foreach (var h in headerFile.Dependencies) + OrderHeaderFiles(result, done, h); + + result.Add(headerFile); + } + + private List ProcessHeaderLines(IEnumerable lines) + { + var result = new List(); + var modes = new HashSet(); + var blankLine = false; + + foreach (var line in lines) + { + var s = line.Trim(); + + if (s.StartsWith("// begin_")) + { + modes.Add(s.Remove(0, "// begin_".Length)); + } + else if (s.StartsWith("// end_")) + { + modes.Remove(s.Remove(0, "// end_".Length)); + } + else + { + bool blockMode = this.Modes.Any(modes.Contains); + bool lineMode = this.Modes.Any(mode => + { + int indexOfMarker = s.LastIndexOf("// " + mode); + if (indexOfMarker == -1) + return false; + + return s.Substring(indexOfMarker).Trim().All(c => char.IsLetterOrDigit(c) || c == ' ' || c == '/'); + }); + + if (blockMode || lineMode) + { + if (blankLine && result.Count != 0) + result.Add(string.Empty); + + result.Add(line); + blankLine = false; + } + else if (s.Length == 0) + { + blankLine = true; + } + } + } + + return result; + } + + public void Execute() + { + // Read in all header files. + + var headerFiles = this.Files.Select(fileName => + { + var fullFileName = this.BaseDirectory + "\\" + fileName; + var lines = File.ReadAllLines(fullFileName).ToList(); + + return new HeaderFile { Name = Path.GetFileName(fullFileName).ToLowerInvariant(), Lines = lines }; + }) + .ToDictionary(h => h.Name); + + foreach (HeaderFile h in headerFiles.Values) + { + var partitions = h.Lines.Select(s => + { + var trimmed = s.Trim().ToLowerInvariant(); + + if (trimmed.StartsWith("#include <", StringComparison.OrdinalIgnoreCase) && trimmed.EndsWith(">", StringComparison.OrdinalIgnoreCase)) + { + if (headerFiles.TryGetValue(trimmed.Remove(trimmed.Length - 1).Remove(0, "#include <".Length), out HeaderFile d)) + return Tuple.Create(s, d); + else + return Tuple.Create(s, null); + } + return Tuple.Create(s, null); + }) + .ToLookup(p => p.Item2 != null); + + h.Lines = partitions[false].Select(p => p.Item1).ToList(); + h.Dependencies = partitions[true].Select(p => p.Item2).Distinct().ToList(); + + //foreach (var d in h.Dependencies) + //Console.WriteLine("Dependency: " + h.Name + " -> " + d.Name); + } + + // Generate the ordering. + + var orderedHeaderFiles = OrderHeaderFiles(this.Files.Select(s => headerFiles[Path.GetFileName(s).ToLower()]).ToList()); + + // Process each header file and remove irrelevant content. + foreach (HeaderFile h in orderedHeaderFiles) + h.Lines = ProcessHeaderLines(h.Lines); + + // Write out the result. + StreamWriter sw = new StreamWriter(this.BaseDirectory + "\\" + this.OutputFile); + + // Header + sw.Write(this.Header); + + // Header files + foreach (HeaderFile h in orderedHeaderFiles) + { + //Console.WriteLine("Header file: " + h.Name); + sw.WriteLine(); + sw.WriteLine("//"); + sw.WriteLine("// " + Path.GetFileNameWithoutExtension(h.Name)); + sw.WriteLine("//"); + sw.WriteLine(); + + foreach (string line in h.Lines) + sw.WriteLine(line); + } + + // Footer + + sw.Write(this.Footer); + + sw.Close(); + } + } +} diff --git a/tools/CustomBuildTool/CustomBuildTool/Source Files/NativeMethods.cs b/tools/CustomBuildTool/Source Files/NativeMethods.cs similarity index 72% rename from tools/CustomBuildTool/CustomBuildTool/Source Files/NativeMethods.cs rename to tools/CustomBuildTool/Source Files/NativeMethods.cs index 0115f4209f27..30770f760157 100644 --- a/tools/CustomBuildTool/CustomBuildTool/Source Files/NativeMethods.cs +++ b/tools/CustomBuildTool/Source Files/NativeMethods.cs @@ -1,80 +1,32 @@ -using System; +/* + * Process Hacker Toolchain - + * Build script + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; namespace CustomBuildTool { - [System.Security.SuppressUnmanagedCodeSecurity] - public static class Win32 - { - public static string ExecCommand(string FileName, string args) - { - string output = string.Empty; - using (Process process = Process.Start(new ProcessStartInfo - { - UseShellExecute = false, - RedirectStandardOutput = true, - FileName = FileName, - CreateNoWindow = true - })) - { - process.StartInfo.Arguments = args; - process.Start(); - - output = process.StandardOutput.ReadToEnd(); - output = output.Replace("\n\n", "\r\n").Trim(); - - process.WaitForExit(); - } - - return output; - } - - public const int STD_OUTPUT_HANDLE = -11; - public const int STD_INPUT_HANDLE = -10; - public const int STD_ERROR_HANDLE = -12; - - static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - - [DllImport("kernel32.dll")] - public static extern IntPtr GetStdHandle(int nStdHandle); - [DllImport("kernel32.dll")] - public static extern bool GetConsoleMode(IntPtr ConsoleHandle, out ConsoleMode Mode); - [DllImport("kernel32.dll")] - public static extern bool SetConsoleMode(IntPtr ConsoleHandle, ConsoleMode Mode); - } - - [Flags] - public enum ConsoleMode : uint - { - DEFAULT, - ENABLE_PROCESSED_INPUT = 0x0001, - ENABLE_LINE_INPUT = 0x0002, - ENABLE_ECHO_INPUT = 0x0004, - ENABLE_WINDOW_INPUT = 0x0008, - ENABLE_MOUSE_INPUT = 0x0010, - ENABLE_INSERT_MODE = 0x0020, - ENABLE_QUICK_EDIT_MODE = 0x0040, - ENABLE_EXTENDED_FLAGS = 0x0080, - ENABLE_AUTO_POSITION = 0x0100, - ENABLE_PROCESSED_OUTPUT = 0x0001, - ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004, - DISABLE_NEWLINE_AUTO_RETURN = 0x0008, - ENABLE_LVB_GRID_WORLDWIDE = 0x0010, - } - - [Flags] - public enum InstanceState : uint - { - None = 0u, - Local = 1u, - Registered = 2u, - NoRebootRequired = 4u, - NoErrors = 8u, - Complete = 4294967295u - } - [ComImport, ClassInterface(ClassInterfaceType.AutoDispatch), Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")] public class SetupConfigurationClass { diff --git a/tools/CustomBuildTool/Source Files/Program.cs b/tools/CustomBuildTool/Source Files/Program.cs new file mode 100644 index 000000000000..82cd1a9a00ef --- /dev/null +++ b/tools/CustomBuildTool/Source Files/Program.cs @@ -0,0 +1,425 @@ +/* + * Process Hacker Toolchain - + * Build script + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Security.Principal; + +namespace CustomBuildTool +{ + public static class Program + { + public static Dictionary ProgramArgs; + + private static bool BuildSdk(BuildFlags Flags) + { + //PrintColorMessage("Copying Plugin SDK...", ConsoleColor.Cyan); + + if (!Build.CopyTextFiles()) + return false; + if (!Build.CopyPluginSdkHeaders()) + return false; + if (!Build.CopyVersionHeader()) + return false; + if (!Build.FixupResourceHeader()) + return false; + if (!Build.CopyLibFiles(Flags)) + return false; + + return true; + } + + public static void Main(string[] args) + { + ProgramArgs = ParseArgs(args); + + if (ProgramArgs.ContainsKey("-cleanup")) + { + if (Restart("-cleanup")) + return; + + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.CleanupBuildEnvironment(); + Build.ShowBuildStats(); + } + else if (ProgramArgs.ContainsKey("-phapppub_gen")) + { + if (!Build.InitializeBuildEnvironment(false)) + return; + + Build.BuildPublicHeaderFiles(); + } + else if (ProgramArgs.ContainsKey("-graph")) + { + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment("changelog", true, false); + + if (Win32.GetConsoleMode(Win32.GetStdHandle(Win32.STD_OUTPUT_HANDLE), out ConsoleMode mode)) + Win32.SetConsoleMode(Win32.GetStdHandle(Win32.STD_OUTPUT_HANDLE), mode | ConsoleMode.ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + Console.WriteLine(Build.GetBuildLogString()); + + Build.ShowBuildStats(); + } + else if (ProgramArgs.ContainsKey("-sdk")) + { + if (!Build.InitializeBuildEnvironment(false)) + return; + + Build.CopyKProcessHacker( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug + ); + + BuildSdk( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + ); + } + else if (ProgramArgs.ContainsKey("-cleansdk")) + { + if (!Build.InitializeBuildEnvironment(false)) + return; + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + { + return; + } + + BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); + + Build.ShowBuildStats(); + } + else if (ProgramArgs.ContainsKey("-bin")) + { + if (!Build.InitializeBuildEnvironment(false)) + return; + + Build.ShowBuildEnvironment("bin", false, true); + Build.CopyKeyFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + return; + + if (!Build.CopyKProcessHacker( + BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + return; + + if (!Build.CopyWow64Files(BuildFlags.None)) + return; + if (!Build.CopySidCapsFile(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildBinZip()) + return; + + Build.ShowBuildStats(); + } + else if (ProgramArgs.ContainsKey("-debug")) + { + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment("debug", true, true); + Build.CopyKeyFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose | + BuildFlags.BuildApi + )) + return; + + if (!Build.CopyKProcessHacker( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) + return; + + if (!BuildSdk( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose + )) + { + return; + } + + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose | + BuildFlags.BuildApi + )) + { + return; + } + + if (!Build.CopyWow64Files(BuildFlags.BuildDebug)) + return; + if (!Build.CopySidCapsFile( + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildDebug | BuildFlags.BuildVerbose)) + return; + + Build.ShowBuildStats(); + } + else if (ProgramArgs.ContainsKey("-appveyor")) + { + if (!Build.InitializeBuildEnvironment(true)) + Environment.Exit(1); + + Build.ShowBuildEnvironment("nightly", true, true); + Build.CopyKeyFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + Environment.Exit(1); + + if (!Build.CopyKProcessHacker(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + Environment.Exit(1); + + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + Environment.Exit(1); + + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + Environment.Exit(1); + + if (!Build.CopyWow64Files(BuildFlags.None)) + Environment.Exit(1); + if (!Build.CopySidCapsFile(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + Environment.Exit(1); + + if (!Build.BuildBinZip()) + Environment.Exit(1); + //if (!Build.BuildWebSetupExe()) + // Environment.Exit(1); + if (!Build.BuildSetupExe()) + Environment.Exit(1); + if (!Build.BuildPdbZip()) + Environment.Exit(1); + if (!Build.BuildSdkZip()) + Environment.Exit(1); + if (!Build.BuildSrcZip()) + Environment.Exit(1); + + if (!Build.BuildChecksumsFile()) + Environment.Exit(1); + if (!Build.BuildUpdateSignature()) + Environment.Exit(1); + + if (!Build.BuildDeployUploadArtifacts()) + Environment.Exit(1); + if (!Build.BuildDeployUpdateConfig()) + Environment.Exit(1); + } + else if (ProgramArgs.ContainsKey("-appxbuild")) + { + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment("appx", true, true); + Build.CopyKeyFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + return; + + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + return; + + if (!Build.CopyWow64Files(BuildFlags.None)) + return; + if (!Build.CopySidCapsFile(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + Build.BuildAppxPackage(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose); + + Build.ShowBuildStats(); + } + else if (ProgramArgs.ContainsKey("-appxmakecert")) + { + if (Restart("-appxmakecert")) + return; + + if (!Build.InitializeBuildEnvironment(false)) + return; + + Build.ShowBuildEnvironment("appxcert", true, true); + + Build.BuildAppxSignature(); + + Build.ShowBuildStats(); + } + else + { + if (!Build.InitializeBuildEnvironment(true)) + return; + + Build.ShowBuildEnvironment("release", true, true); + Build.CopyKeyFiles(); + + if (!Build.BuildSolution("ProcessHacker.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + return; + + if (!Build.CopyKProcessHacker(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!BuildSdk(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildSolution("plugins\\Plugins.sln", + BuildFlags.Build32bit | BuildFlags.Build64bit | + BuildFlags.BuildVerbose | BuildFlags.BuildApi + )) + return; + + if (!Build.CopyWow64Files(BuildFlags.None)) + return; + if (!Build.CopySidCapsFile(BuildFlags.Build32bit | BuildFlags.Build64bit | BuildFlags.BuildVerbose)) + return; + + if (!Build.BuildBinZip()) + return; + if (!Build.BuildSetupExe()) + return; + Build.BuildPdbZip(); + Build.BuildSdkZip(); + Build.BuildSrcZip(); + + Build.ShowBuildStats(); + } + } + + private static Dictionary ParseArgs(string[] args) + { + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + string argPending = null; + + foreach (string s in args) + { + if (s.StartsWith("-", StringComparison.OrdinalIgnoreCase)) + { + if (!dict.ContainsKey(s)) + dict.Add(s, string.Empty); + + argPending = s; + } + else + { + if (argPending != null) + { + dict[argPending] = s; + argPending = null; + } + else + { + if (!dict.ContainsKey(string.Empty)) + dict.Add(string.Empty, s); + } + } + } + + return dict; + } + + public static void PrintColorMessage(string Message, ConsoleColor Color, bool Newline = true, BuildFlags Flags = BuildFlags.BuildVerbose) + { + if ((Flags & BuildFlags.BuildVerbose) != BuildFlags.BuildVerbose) + return; + + Console.ForegroundColor = Color; + if (Newline) + Console.WriteLine(Message); + else + Console.Write(Message); + Console.ResetColor(); + } + + private static bool Restart(string Arguments) + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + + if (principal.IsInRole(WindowsBuiltInRole.Administrator)) + return false; + + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName, + Arguments = Arguments, + Verb = "runas" + }); + } + catch (Exception) { } + + Environment.Exit(Environment.ExitCode); + + return true; + } + } + + [Flags] + public enum BuildFlags + { + None, + Build32bit = 1, + Build64bit = 2, + BuildDebug = 4, + BuildVerbose = 8, + BuildApi = 16, + } +} diff --git a/tools/CustomBuildTool/Source Files/Utils.cs b/tools/CustomBuildTool/Source Files/Utils.cs new file mode 100644 index 000000000000..b8cb380143c8 --- /dev/null +++ b/tools/CustomBuildTool/Source Files/Utils.cs @@ -0,0 +1,580 @@ +/* + * Process Hacker Toolchain - + * Build script + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Security.Cryptography; +using System.Text; + +namespace CustomBuildTool +{ + [System.Security.SuppressUnmanagedCodeSecurity] + public static class Win32 + { + public static int CreateProcess(string FileName, string args) + { + using (Process process = Process.Start(new ProcessStartInfo + { + UseShellExecute = false, + FileName = FileName, + CreateNoWindow = true + })) + { + process.StartInfo.Arguments = args; + process.Start(); + + process.WaitForExit(); + + return process.ExitCode; + } + } + + public static string ShellExecute(string FileName, string args) + { + string output = string.Empty; + using (Process process = Process.Start(new ProcessStartInfo + { + UseShellExecute = false, + RedirectStandardOutput = true, + FileName = FileName, + CreateNoWindow = true + })) + { + process.StartInfo.Arguments = args; + process.Start(); + + output = process.StandardOutput.ReadToEnd(); + output = output.Replace("\n\n", "\r\n").Trim(); + + process.WaitForExit(); + } + + return output; + } + + public static string SearchFile(string FileName) + { + try + { + if (File.Exists(FileName)) + return Path.GetFullPath(FileName); + + string values = Environment.GetEnvironmentVariable("PATH"); + + foreach (string path in values.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + string whereResult = Path.Combine(path, FileName); + + if (File.Exists(whereResult)) + return whereResult; + } + } + catch (Exception) { } + + return null; + } + + //public static string SearchFile(string FileName) + //{ + // string where = Environment.ExpandEnvironmentVariables("%SystemRoot%\\System32\\where.exe"); + // + // if (File.Exists(where)) + // { + // string whereResult = ShellExecute(where, FileName); + // + // if (!string.IsNullOrEmpty(whereResult)) + // return whereResult; + // } + // + // return null; + //} + + public static void ImageResizeFile(int size, string FileName, string OutName) + { + using (var src = System.Drawing.Image.FromFile(FileName)) + using (var dst = new System.Drawing.Bitmap(size, size)) + using (var g = System.Drawing.Graphics.FromImage(dst)) + { + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + + g.DrawImage(src, 0, 0, dst.Width, dst.Height); + + dst.Save(OutName, System.Drawing.Imaging.ImageFormat.Png); + } + } + + public static void CopyIfNewer(string CurrentFile, string NewFile) + { + if (!File.Exists(CurrentFile)) + return; + + if (CurrentFile.EndsWith(".sys", StringComparison.OrdinalIgnoreCase)) + { + if (!File.Exists(NewFile)) + { + File.Copy(CurrentFile, NewFile, true); + } + else + { + FileVersionInfo currentInfo = FileVersionInfo.GetVersionInfo(CurrentFile); + FileVersionInfo newInfo = FileVersionInfo.GetVersionInfo(NewFile); + var currentInfoVersion = new Version(currentInfo.FileVersion); + var newInfoVersion = new Version(newInfo.FileVersion); + + if ( + currentInfoVersion > newInfoVersion || + File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile) + ) + { + File.Copy(CurrentFile, NewFile, true); + } + } + } + else + { + if (File.GetLastWriteTime(CurrentFile) > File.GetLastWriteTime(NewFile)) + File.Copy(CurrentFile, NewFile, true); + } + } + + public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + public const int STD_OUTPUT_HANDLE = -11; + public const int STD_INPUT_HANDLE = -10; + public const int STD_ERROR_HANDLE = -12; + + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern IntPtr GetStdHandle(int StdHandle); + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern bool GetConsoleMode(IntPtr ConsoleHandle, out ConsoleMode Mode); + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern bool SetConsoleMode(IntPtr ConsoleHandle, ConsoleMode Mode); + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern IntPtr GetConsoleWindow(); + } + + public static class Json where T : class + { + public static string Serialize(T instance) + { + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); + + using (MemoryStream stream = new MemoryStream()) + { + serializer.WriteObject(stream, instance); + + return Encoding.Default.GetString(stream.ToArray()); + } + } + + public static T DeSerialize(string json) + { + using (MemoryStream stream = new MemoryStream(Encoding.Default.GetBytes(json))) + { + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); + + return (T)serializer.ReadObject(stream); + } + } + } + + public static class Verify + { + private static Rijndael GetRijndael(string secret) + { + Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(secret, Convert.FromBase64String("e0U0RTY2RjU5LUNBRjItNEMzOS1BN0Y4LTQ2MDk3QjFDNDYxQn0="), 10000); + Rijndael rijndael = Rijndael.Create(); + rijndael.Key = rfc2898DeriveBytes.GetBytes(32); + rijndael.IV = rfc2898DeriveBytes.GetBytes(16); + return rijndael; + } + + public static void Encrypt(string fileName, string outFileName, string secret) + { + using (FileStream fileOutStream = File.Create(outFileName)) + using (Rijndael rijndael = GetRijndael(secret)) + using (FileStream fileStream = File.OpenRead(fileName)) + using (CryptoStream cryptoStream = new CryptoStream(fileOutStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write, true)) + { + fileStream.CopyTo(cryptoStream); + } + } + + public static void Decrypt(string FileName, string outFileName, string secret) + { + using (FileStream fileOutStream = File.Create(outFileName)) + using (Rijndael rijndael = GetRijndael(secret)) + using (FileStream fileStream = File.OpenRead(FileName)) + using (CryptoStream cryptoStream = new CryptoStream(fileOutStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write, true)) + { + fileStream.CopyTo(cryptoStream); + } + } + + public static string HashFile(string FileName) + { + //using (HashAlgorithm algorithm = new SHA256CryptoServiceProvider()) + //{ + // byte[] inputBytes = Encoding.UTF8.GetBytes(input); + // byte[] hashBytes = algorithm.ComputeHash(inputBytes); + // return BitConverter.ToString(hashBytes).Replace("-", String.Empty); + //} + + using (FileStream fileInStream = File.OpenRead(FileName)) + using (BufferedStream bufferedStream = new BufferedStream(fileInStream, 0x1000)) + { + SHA256Managed sha = new SHA256Managed(); + byte[] checksum = sha.ComputeHash(bufferedStream); + + return BitConverter.ToString(checksum).Replace("-", string.Empty); + } + } + } + + public static class VisualStudio + { + public static string GetMsbuildFilePath() + { + string[] MsBuildPathArray = + { + "\\MSBuild\\Current\\Bin\\MSBuild.exe", + "\\MSBuild\\15.0\\Bin\\MSBuild.exe" + }; + string vswhere = string.Empty; + + if (Environment.Is64BitOperatingSystem) + vswhere = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe"); + else + vswhere = Environment.ExpandEnvironmentVariables("%ProgramFiles%\\Microsoft Visual Studio\\Installer\\vswhere.exe"); + + // Note: vswere.exe was only released with build 15.0.26418.1 + if (File.Exists(vswhere)) + { + string vswhereResult = Win32.ShellExecute(vswhere, + "-latest " + + "-prerelease " + + "-products * " + + "-requires Microsoft.Component.MSBuild " + + "-property installationPath " + ); + + if (string.IsNullOrEmpty(vswhereResult)) + return null; + + foreach (string path in MsBuildPathArray) + { + if (File.Exists(vswhereResult + path)) + return vswhereResult + path; + } + + return null; + } + else + { + try + { + VisualStudioInstance instance = FindVisualStudioInstance(); + + if (instance != null) + { + foreach (string path in MsBuildPathArray) + { + if (File.Exists(instance.Path + path)) + return instance.Path + path; + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[VisualStudioInstance] " + ex, ConsoleColor.Red, true); + } + + return null; + } + } + + private static VisualStudioInstance FindVisualStudioInstance() + { + var setupConfiguration = new SetupConfiguration() as ISetupConfiguration2; + var instanceEnumerator = setupConfiguration.EnumAllInstances(); + var instances = new ISetupInstance2[3]; + + instanceEnumerator.Next(instances.Length, instances, out var instancesFetched); + + if (instancesFetched == 0) + return null; + + do + { + for (int i = 0; i < instancesFetched; i++) + { + var instance = new VisualStudioInstance(instances[i]); + var state = instances[i].GetState(); + var packages = instances[i].GetPackages().Where(package => package.GetId().Contains("Microsoft.Component.MSBuild")); + + if ( + state.HasFlag(InstanceState.Local | InstanceState.Registered | InstanceState.Complete) && + packages.Count() > 0 && + instance.Version.StartsWith("15.0", StringComparison.OrdinalIgnoreCase) + ) + { + return instance; + } + } + + instanceEnumerator.Next(instances.Length, instances, out instancesFetched); + } + while (instancesFetched != 0); + + return null; + } + } + + public static class AppVeyor + { + public static readonly string AppVeyorPath = string.Empty; + + static AppVeyor() + { + AppVeyorPath = Win32.SearchFile("appveyor.exe"); + } + + public static bool AppVeyorNightlyBuild() + { + return !string.IsNullOrWhiteSpace(AppVeyorPath); + } + + public static bool UpdateBuildVersion(string BuildVersion) + { + try + { + if (!string.IsNullOrWhiteSpace(AppVeyorPath)) + { + Win32.ShellExecute("appveyor", $"UpdateBuild -Version \"{BuildVersion}\" "); + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[VisualStudioInstance] " + ex, ConsoleColor.Red, true); + return false; + } + + return true; + } + + public static bool UploadFile(string FileName) + { + try + { + if (!string.IsNullOrWhiteSpace(AppVeyorPath)) + { + Win32.ShellExecute("appveyor", $"PushArtifact \"{FileName}\" "); + } + } + catch (Exception ex) + { + Program.PrintColorMessage("[UploadFile] " + ex, ConsoleColor.Red, true); + return false; + } + + return true; + } + + private static VisualStudioInstance FindVisualStudioInstance() + { + var setupConfiguration = new SetupConfiguration() as ISetupConfiguration2; + var instanceEnumerator = setupConfiguration.EnumAllInstances(); + var instances = new ISetupInstance2[3]; + + instanceEnumerator.Next(instances.Length, instances, out var instancesFetched); + + if (instancesFetched == 0) + return null; + + do + { + for (int i = 0; i < instancesFetched; i++) + { + var instance = new VisualStudioInstance(instances[i]); + var state = instances[i].GetState(); + var packages = instances[i].GetPackages().Where(package => package.GetId().Contains("Microsoft.Component.MSBuild")); + + if ( + state.HasFlag(InstanceState.Local | InstanceState.Registered | InstanceState.Complete) && + packages.Count() > 0 && + instance.Version.StartsWith("15.0", StringComparison.OrdinalIgnoreCase) + ) + { + return instance; + } + } + + instanceEnumerator.Next(instances.Length, instances, out instancesFetched); + } + while (instancesFetched != 0); + + return null; + } + } + + public class VisualStudioInstance + { + public bool IsLaunchable { get; } + public bool IsComplete { get; } + public string Name { get; } + public string Path { get; } + public string Version { get; } + public string DisplayName { get; } + public string Description { get; } + public string ResolvePath { get; } + public string EnginePath { get; } + public string ProductPath { get; } + public string InstanceId { get; } + public DateTime InstallDate { get; } + + public VisualStudioInstance(ISetupInstance2 FromInstance) + { + this.IsLaunchable = FromInstance.IsLaunchable(); + this.IsComplete = FromInstance.IsComplete(); + this.Name = FromInstance.GetInstallationName(); + this.Path = FromInstance.GetInstallationPath(); + this.Version = FromInstance.GetInstallationVersion(); + this.DisplayName = FromInstance.GetDisplayName(); + this.Description = FromInstance.GetDescription(); + this.ResolvePath = FromInstance.ResolvePath(); + this.EnginePath = FromInstance.GetEnginePath(); + this.InstanceId = FromInstance.GetInstanceId(); + this.ProductPath = FromInstance.GetProductPath(); + + try + { + var time = FromInstance.GetInstallDate(); + ulong high = (ulong)time.dwHighDateTime; + uint low = (uint)time.dwLowDateTime; + long fileTime = (long)((high << 32) + low); + + this.InstallDate = DateTime.FromFileTimeUtc(fileTime); + } + catch + { + this.InstallDate = DateTime.UtcNow; + } + + // FromInstance.GetState(); + // FromInstance.GetPackages(); + // FromInstance.GetProduct(); + // FromInstance.GetProperties(); + // FromInstance.GetErrors(); + } + } + + [DataContract] + public class BuildUpdateRequest + { + [DataMember(Name = "build_version")] public string BuildVersion { get; set; } + [DataMember(Name = "build_commit")] public string BuildCommit { get; set; } + [DataMember(Name = "build_updated")] public string BuildUpdated { get; set; } + [DataMember(Name = "build_message")] public string BuildMessage { get; set; } + + [DataMember(Name = "bin_url")] public string BinUrl { get; set; } + [DataMember(Name = "bin_length")] public string BinLength { get; set; } + [DataMember(Name = "bin_hash")] public string BinHash { get; set; } + [DataMember(Name = "bin_sig")] public string BinSig { get; set; } + + [DataMember(Name = "setup_url")] public string SetupUrl { get; set; } + [DataMember(Name = "setup_length")] public string SetupLength { get; set; } + [DataMember(Name = "setup_hash")] public string SetupHash { get; set; } + [DataMember(Name = "setup_sig")] public string SetupSig { get; set; } + + [DataMember(Name = "websetup_url")] public string WebSetupUrl { get; set; } + [DataMember(Name = "websetup_version")] public string WebSetupVersion { get; set; } + [DataMember(Name = "websetup_length")] public string WebSetupLength { get; set; } + [DataMember(Name = "websetup_hash")] public string WebSetupHash { get; set; } + [DataMember(Name = "websetup_sig")] public string WebSetupSig { get; set; } + + [DataMember(Name = "message")] public string Message { get; set; } + [DataMember(Name = "changelog")] public string Changelog { get; set; } + } + + public static class Extextensions + { + private const long OneKb = 1024; + private const long OneMb = OneKb * 1024; + private const long OneGb = OneMb * 1024; + private const long OneTb = OneGb * 1024; + + public static string ToPrettySize(this int value, int decimalPlaces = 0) + { + return ((long)value).ToPrettySize(decimalPlaces); + } + + public static string ToPrettySize(this long value, int decimalPlaces = 0) + { + double asTb = Math.Round((double)value / OneTb, decimalPlaces); + double asGb = Math.Round((double)value / OneGb, decimalPlaces); + double asMb = Math.Round((double)value / OneMb, decimalPlaces); + double asKb = Math.Round((double)value / OneKb, decimalPlaces); + string chosenValue = asTb > 1 ? string.Format("{0}Tb", asTb) + : asGb > 1 ? string.Format("{0}Gb", asGb) + : asMb > 1 ? string.Format("{0}Mb", asMb) + : asKb > 1 ? string.Format("{0}Kb", asKb) + : string.Format("{0}B", Math.Round((double)value, decimalPlaces)); + return chosenValue; + } + } + + [Flags] + public enum ConsoleMode : uint + { + DEFAULT, + ENABLE_PROCESSED_INPUT = 0x0001, + ENABLE_LINE_INPUT = 0x0002, + ENABLE_ECHO_INPUT = 0x0004, + ENABLE_WINDOW_INPUT = 0x0008, + ENABLE_MOUSE_INPUT = 0x0010, + ENABLE_INSERT_MODE = 0x0020, + ENABLE_QUICK_EDIT_MODE = 0x0040, + ENABLE_EXTENDED_FLAGS = 0x0080, + ENABLE_AUTO_POSITION = 0x0100, + ENABLE_PROCESSED_OUTPUT = 0x0001, + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004, + DISABLE_NEWLINE_AUTO_RETURN = 0x0008, + ENABLE_LVB_GRID_WORLDWIDE = 0x0010, + } + + [Flags] + public enum InstanceState : uint + { + None = 0u, + Local = 1u, + Registered = 2u, + NoRebootRequired = 4u, + NoErrors = 8u, + Complete = 4294967295u + } +} diff --git a/tools/CustomBuildTool/Source Files/Zip.cs b/tools/CustomBuildTool/Source Files/Zip.cs new file mode 100644 index 000000000000..f239475ac93e --- /dev/null +++ b/tools/CustomBuildTool/Source Files/Zip.cs @@ -0,0 +1,133 @@ +/* + * Process Hacker Toolchain - + * Build script + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +using System; +using System.IO; +using System.IO.Compression; + +namespace CustomBuildTool +{ + public static class Zip + { + // https://github.com/phofman/zip/blob/master/src/ZipFile.cs + private static string[] GetEntryNames(string[] names, string sourceFolder, bool includeBaseName) + { + if (names == null || names.Length == 0) + return new string[0]; + + if (includeBaseName) + sourceFolder = Path.GetDirectoryName(sourceFolder); + + int length = string.IsNullOrEmpty(sourceFolder) ? 0 : sourceFolder.Length; + if (length > 0 && sourceFolder != null && sourceFolder[length - 1] != Path.DirectorySeparatorChar && sourceFolder[length - 1] != Path.AltDirectorySeparatorChar) + length++; + + var result = new string[names.Length]; + for (int i = 0; i < names.Length; i++) + { + result[i] = names[i].Substring(length); + } + + return result; + } + + public static void CreateCompressedFolder(string sourceDirectoryName, string destinationArchiveFileName) + { + string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); + string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + + if (File.Exists(destinationArchiveFileName)) + File.Delete(destinationArchiveFileName); + + using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true)) + { + for (int i = 0; i < filesToAdd.Length; i++) + { + // Ignore junk files + if (filesToAdd[i].EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || + filesToAdd[i].EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || + filesToAdd[i].EndsWith(".ipdb", StringComparison.OrdinalIgnoreCase) || + filesToAdd[i].EndsWith(".exp", StringComparison.OrdinalIgnoreCase) || + filesToAdd[i].EndsWith(".lib", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + // Ignore junk directories + if (filesToAdd[i].Contains("bin\\Debug")) // Debug32 Debug64 + { + continue; + } + + archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], CompressionLevel.Optimal); + } + } + } + + public static void CreateCompressedSdkFromFolder(string sourceDirectoryName, string destinationArchiveFileName) + { + string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); + string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + + if (File.Exists(destinationArchiveFileName)) + File.Delete(destinationArchiveFileName); + + using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true)) + { + for (int i = 0; i < filesToAdd.Length; i++) + { + archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], CompressionLevel.Optimal); + } + } + } + + public static void CreateCompressedPdbFromFolder(string sourceDirectoryName, string destinationArchiveFileName) + { + string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); + string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + + if (File.Exists(destinationArchiveFileName)) + File.Delete(destinationArchiveFileName); + + using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true)) + { + for (int i = 0; i < filesToAdd.Length; i++) + { + // Ignore junk files + if (!filesToAdd[i].EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) + continue; + + // Ignore junk directories + if (filesToAdd[i].Contains("bin\\Debug") || + filesToAdd[i].Contains("obj\\") || + filesToAdd[i].Contains("tests\\")) + continue; + + archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], CompressionLevel.Optimal); + } + } + } + } +} \ No newline at end of file diff --git a/tools/CustomBuildTool/app.config b/tools/CustomBuildTool/app.config new file mode 100644 index 000000000000..9f787f35ef79 --- /dev/null +++ b/tools/CustomBuildTool/app.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/build/CustomBuildTool.exe b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe similarity index 60% rename from build/CustomBuildTool.exe rename to tools/CustomBuildTool/bin/Release/CustomBuildTool.exe index 1acc5834cacd..b5f35893caf6 100644 Binary files a/build/CustomBuildTool.exe and b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe differ diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe.config b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe.config new file mode 100644 index 000000000000..9f787f35ef79 --- /dev/null +++ b/tools/CustomBuildTool/bin/Release/CustomBuildTool.exe.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb new file mode 100644 index 000000000000..7440abc8fe27 Binary files /dev/null and b/tools/CustomBuildTool/bin/Release/CustomBuildTool.pdb differ diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj index 6dd368455d93..c6b76fd0d6b4 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj @@ -13,22 +13,23 @@ {5C00734F-F50A-49FC-9D2A-F6EE51ECB00F} CustomSetupTool - 10.0.15063.0 + 10.0 CustomSetupTool Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode + Spectre @@ -53,27 +54,27 @@ Level3 Disabled true - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) false StdCall MultiThreadedDebug - _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) true ProgramDatabase true true - uxtheme.lib;winhttp.lib;phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + comctl32.lib;crypt32.lib;uxtheme.lib;winhttp.lib;ntdll.lib;phlib.lib;shlwapi.lib;userenv.lib;windowscodecs.lib;winsta.lib;%(AdditionalDependencies) + $(SolutionDir)..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) Windows - RequireAdministrator + 6.01 + advapi32.dll;comctl32.dll;crypt32.dll;gdi32.dll;ole32.dll;oleaut32.dll;shell32.dll;user32.dll;uxtheme.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) + @@ -82,52 +83,58 @@ true true true - ..\..\..\phnt\include;..\..\..\phlib\include;include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) StdCall - _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) + _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) MultiThreaded true AnySuitable true StreamingSIMDExtensions + Guard true true true - uxtheme.lib;winhttp.lib;phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + comctl32.lib;crypt32.lib;uxtheme.lib;winhttp.lib;ntdll.lib;phlib.lib;shlwapi.lib;userenv.lib;windowscodecs.lib;winsta.lib;%(AdditionalDependencies) + $(SolutionDir)..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) Windows - 5.01 + 6.01 true - RequireAdministrator + advapi32.dll;comctl32.dll;crypt32.dll;gdi32.dll;ole32.dll;oleaut32.dll;shell32.dll;user32.dll;uxtheme.dll;winhttp.dll;%(DelayLoadDLLs) - - - - - + + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + $(SolutionDir)..\..\phnt\include;$(SolutionDir)..\..\phlib\include;include;%(AdditionalIncludeDirectories) + + + - - - - + + + + + + + - + + @@ -139,10 +146,7 @@ - - - - + diff --git a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters index 1731321acc26..9a3eebbb5c71 100644 --- a/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters +++ b/tools/CustomSetupTool/CustomSetupTool/CustomSetupTool.vcxproj.filters @@ -13,41 +13,29 @@ {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 - - {3f81aff6-dff1-4573-bb20-ac132fa9297b} - - - {de42287f-b6af-40c6-b76a-f56ee7dcf507} - {c394360d-d423-4249-a23e-70558948e7f2} - - {fcf7dac9-ef61-47e6-8aae-818354f6b4df} - {5c3f4462-038f-468f-8ab2-7e153d301c33} {7a7dfb06-20d6-4ea2-8992-26ea6e6cf1d9} + + {0e40a0b6-7e1f-4ef2-9c13-14d083de096e} + + + {8d69960b-e11c-4029-b39e-dee698bb871b} + + + {3f81aff6-dff1-4573-bb20-ac132fa9297b} + Source Files - - Source Files\pages - - - Source Files\pages - - - Source Files\pages - - - Source Files\pages - Source Files @@ -63,6 +51,33 @@ Source Files + + Source Files + + + Source Files + + + Source Files\pages + + + Source Files\pages + + + Source Files\pages + + + Source Files\pages + + + Source Files\pages + + + Source Files + + + Source Files\pages + @@ -71,39 +86,37 @@ Header Files - - Header Files - Header Files\zip + + Header Files + - Resource Files\Images + Resource Files\Content - Resource Files\Images + Resource Files\Content Resource Files + + Resource Files + - - Resource Files\Text + + Resource Files\Content - Resource Files + Resource Files\Content - - - Resource Files\Binary - - \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/appsup.c b/tools/CustomSetupTool/CustomSetupTool/appsup.c index 0de46b959b1b..7f3b3eeaba2a 100644 --- a/tools/CustomSetupTool/CustomSetupTool/appsup.c +++ b/tools/CustomSetupTool/CustomSetupTool/appsup.c @@ -19,8 +19,11 @@ */ #include +#include #include #include +#include +#include VOID ExtractResourceToFile( _In_ PWSTR Resource, @@ -28,22 +31,20 @@ VOID ExtractResourceToFile( ) { HANDLE fileHandle = NULL; - ULONG resourceLength; - HRSRC resourceHandle = NULL; - HGLOBAL resourceData; PVOID resourceBuffer; + ULONG resourceLength; IO_STATUS_BLOCK isb; - if (!(resourceHandle = FindResource(PhLibImageBase, Resource, RT_RCDATA))) - goto CleanupExit; - - resourceLength = SizeofResource(PhLibImageBase, resourceHandle); - - if (!(resourceData = LoadResource(PhLibImageBase, resourceHandle))) - goto CleanupExit; - - if (!(resourceBuffer = LockResource(resourceData))) + if (!PhLoadResource( + PhInstanceHandle, + Resource, + RT_RCDATA, + &resourceLength, + &resourceBuffer + )) + { goto CleanupExit; + } if (!NT_SUCCESS(PhCreateFileWin32( &fileHandle, @@ -80,43 +81,6 @@ VOID ExtractResourceToFile( if (fileHandle) NtClose(fileHandle); - - if (resourceHandle) - FreeResource(resourceHandle); -} - -PVOID ExtractResourceToBuffer( - _In_ PWSTR Resource - ) -{ - ULONG resourceLength; - HRSRC resourceHandle = NULL; - HGLOBAL resourceData; - PVOID resourceBuffer; - PVOID buffer = NULL; - - if (!(resourceHandle = FindResource(PhLibImageBase, Resource, RT_RCDATA))) - goto CleanupExit; - - resourceLength = SizeofResource(PhLibImageBase, resourceHandle); - - if (!(resourceData = LoadResource(PhLibImageBase, resourceHandle))) - goto CleanupExit; - - if (!(resourceBuffer = LockResource(resourceData))) - goto CleanupExit; - - if (!(buffer = PhAllocate(resourceLength))) - goto CleanupExit; - - memcpy(buffer, resourceBuffer, resourceLength); - -CleanupExit: - - if (resourceHandle) - FreeResource(resourceHandle); - - return buffer; } HBITMAP LoadPngImageFromResources( @@ -125,10 +89,8 @@ HBITMAP LoadPngImageFromResources( { BOOLEAN success = FALSE; UINT frameCount = 0; - ULONG resourceLength = 0; - HGLOBAL resourceHandle = NULL; - HRSRC resourceHandleSource = NULL; - WICInProcPointer resourceBuffer = NULL; + ULONG resourceLength; + PVOID resourceBuffer = NULL; HDC screenHdc = NULL; HDC bufferDc = NULL; BITMAPINFO bitmapInfo = { 0 }; @@ -147,18 +109,8 @@ HBITMAP LoadPngImageFromResources( if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, &wicFactory))) goto CleanupExit; - // Find the resource - if ((resourceHandleSource = FindResource(PhLibImageBase, Name, L"PNG")) == NULL) - goto CleanupExit; - - // Get the resource length - resourceLength = SizeofResource(PhLibImageBase, resourceHandleSource); - // Load the resource - if ((resourceHandle = LoadResource(PhLibImageBase, resourceHandleSource)) == NULL) - goto CleanupExit; - - if ((resourceBuffer = (WICInProcPointer)LockResource(resourceHandle)) == NULL) + if (!PhLoadResource(PhInstanceHandle, Name, L"PNG", &resourceLength, &resourceBuffer)) goto CleanupExit; // Create the Stream @@ -268,8 +220,8 @@ HBITMAP LoadPngImageFromResources( if (wicFactory) IWICImagingFactory_Release(wicFactory); - if (resourceHandle) - FreeResource(resourceHandle); + if (resourceBuffer) + PhFree(resourceBuffer); if (success) { @@ -349,23 +301,44 @@ BOOLEAN ConnectionAvailable(VOID) } VOID SetupCreateLink( - _In_ PWSTR LinkFilePath, + _In_ PWSTR AppUserModelId, + _In_ PWSTR LinkFilePath, _In_ PWSTR FilePath, _In_ PWSTR FileParentDir ) { IShellLink* shellLinkPtr = NULL; IPersistFile* persistFilePtr = NULL; + IPropertyStore* propertyStorePtr; - if (FAILED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, &shellLinkPtr))) + if (FAILED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &shellLinkPtr))) goto CleanupExit; if (FAILED(IShellLinkW_QueryInterface(shellLinkPtr, &IID_IPersistFile, &persistFilePtr))) goto CleanupExit; + if (SUCCEEDED(IShellLinkW_QueryInterface(shellLinkPtr, &IID_IPropertyStore, &propertyStorePtr))) + { + PROPVARIANT appIdPropVar; + + PropVariantInit(&appIdPropVar); + + appIdPropVar.vt = VT_BSTR; + appIdPropVar.bstrVal = SysAllocString(AppUserModelId); + + if (SUCCEEDED(IPropertyStore_SetValue(propertyStorePtr, &PKEY_AppUserModel_ID, &appIdPropVar))) + { + IPropertyStore_Commit(propertyStorePtr); + } + + PropVariantClear(&appIdPropVar); + IPropertyStore_Release(propertyStorePtr); + } + // Load existing shell item if it exists... //IPersistFile_Load(persistFilePtr, LinkFilePath, STGM_READ) //IShellLinkW_SetDescription(shellLinkPtr, FileComment); + //IShellLinkW_SetHotkey(shellLinkPtr, MAKEWORD(VK_END, HOTKEYF_CONTROL | HOTKEYF_ALT)); IShellLinkW_SetWorkingDirectory(shellLinkPtr, FileParentDir); IShellLinkW_SetIconLocation(shellLinkPtr, FilePath, 0); @@ -376,6 +349,8 @@ VOID SetupCreateLink( // Save the shortcut to the file system... IPersistFile_Save(persistFilePtr, LinkFilePath, TRUE); + SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, LinkFilePath, NULL); + CleanupExit: if (persistFilePtr) IPersistFile_Release(persistFilePtr); @@ -390,7 +365,7 @@ BOOLEAN DialogPromptExit( INT buttonPressed = 0; TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) }; config.hwndParent = hwndDlg; - config.hInstance = PhLibImageBase; + config.hInstance = PhInstanceHandle; config.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW; config.nDefaultButton = IDNO; config.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; @@ -404,53 +379,35 @@ BOOLEAN DialogPromptExit( return buttonPressed == IDNO; } -BOOLEAN CheckProcessHackerRunning(VOID) -{ - HANDLE mutantHandle; - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - - RtlInitUnicodeString(&mutantName, L"\\BaseNamedObjects\\ProcessHackerMutant"); - InitializeObjectAttributes( - &oa, - &mutantName, - 0, - NULL, - NULL - ); - - if (NT_SUCCESS(NtOpenMutant( - &mutantHandle, - MUTANT_QUERY_STATE, - &oa - ))) - { - NtClose(mutantHandle); - return TRUE; - } - - return FALSE; -} - -BOOLEAN CheckProcessHackerInstalled(VOID) +BOOLEAN CheckProcessHackerInstalled( + VOID + ) { BOOLEAN installed = FALSE; PPH_STRING installPath; - + PPH_STRING exePath; + installPath = GetProcessHackerInstallPath(); - if (!PhIsNullOrEmptyString(installPath) && PhEndsWithString2(installPath, L"ProcessHacker.exe", TRUE)) + if (!PhIsNullOrEmptyString(installPath)) { + exePath = SetupCreateFullPath(installPath, L"\\ProcessHacker.exe"); + // Check if the value has a valid file path. - installed = GetFileAttributes(installPath->Buffer) != INVALID_FILE_ATTRIBUTES; + installed = GetFileAttributes(PhGetString(exePath)) != INVALID_FILE_ATTRIBUTES; + + PhDereferenceObject(exePath); } - PhClearReference(&installPath); + if (installPath) + PhDereferenceObject(installPath); return installed; } -PPH_STRING GetProcessHackerInstallPath(VOID) +PPH_STRING GetProcessHackerInstallPath( + VOID + ) { HANDLE keyHandle; PPH_STRING installPath = NULL; @@ -470,196 +427,191 @@ PPH_STRING GetProcessHackerInstallPath(VOID) return installPath; } -BOOLEAN ShutdownProcessHacker(VOID) +static BOOLEAN NTAPI PhpPreviousInstancesCallback( + _In_ PPH_STRINGREF Name, + _In_ PPH_STRINGREF TypeName, + _In_opt_ PVOID Context + ) { - HWND windowHandle; - HANDLE processHandle; - ULONG processID = 0; - - windowHandle = FindWindow(L"ProcessHacker", NULL); - if (!windowHandle) + HANDLE objectHandle; + UNICODE_STRING objectNameUs; + OBJECT_ATTRIBUTES objectAttributes; + MUTANT_OWNER_INFORMATION objectInfo; + + if (!PhStartsWithStringRef2(Name, L"PhMutant_", TRUE) && + !PhStartsWithStringRef2(Name, L"PhSetupMutant_", TRUE) && + !PhStartsWithStringRef2(Name, L"PeViewerMutant_", TRUE)) + { return TRUE; + } - GetWindowThreadProcessId(windowHandle, &processID); + if (!PhStringRefToUnicodeString(Name, &objectNameUs)) + return TRUE; - SendMessageTimeout( - windowHandle, - WM_QUIT, - 0, - 0, - SMTO_ABORTIFHUNG | SMTO_BLOCK, - 5000, + InitializeObjectAttributes( + &objectAttributes, + &objectNameUs, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), NULL ); - // Check the window handle again - windowHandle = FindWindow(L"ProcessHacker", NULL); - if (!windowHandle) + if (!NT_SUCCESS(NtOpenMutant( + &objectHandle, + MUTANT_QUERY_STATE, + &objectAttributes + ))) + { return TRUE; + } - GetWindowThreadProcessId(windowHandle, &processID); - - if (NT_SUCCESS(PhOpenProcess( - &processHandle, - SYNCHRONIZE | PROCESS_TERMINATE, - ULongToHandle(processID) + if (NT_SUCCESS(PhGetMutantOwnerInformation( + objectHandle, + &objectInfo ))) { - PostMessage(windowHandle, WM_QUIT, 0, 0); + HWND hwnd; + HANDLE processHandle = NULL; + + if (objectInfo.ClientId.UniqueProcess == NtCurrentProcessId()) + goto CleanupExit; - if (WaitForSingleObject(processHandle, 10 * 1000) != WAIT_OBJECT_0) + PhOpenProcess( + &processHandle, + ProcessQueryAccess | PROCESS_TERMINATE, + objectInfo.ClientId.UniqueProcess + ); + + hwnd = PhGetProcessMainWindowEx( + objectInfo.ClientId.UniqueProcess, + processHandle, + FALSE + ); + + if (hwnd) { - if (!NT_SUCCESS(NtTerminateProcess(processHandle, 1))) - { - NtClose(processHandle); - return FALSE; - } + SendMessageTimeout(hwnd, WM_QUIT, 0, 0, SMTO_BLOCK, 5000, NULL); } - NtClose(processHandle); + if (processHandle) + { + NtTerminateProcess(processHandle, 1); + } + + CleanupExit: + if (processHandle) NtClose(processHandle); } - return FALSE; + NtClose(objectHandle); + + return TRUE; } -BOOLEAN CreateDirectoryPath(_In_ PWSTR DirPath) +BOOLEAN ShutdownProcessHacker(VOID) { - BOOLEAN success = FALSE; - PPH_STRING dirPathString = NULL; - PWSTR dirPathDup = NULL; + PhEnumDirectoryObjects(PhGetNamespaceHandle(), PhpPreviousInstancesCallback, NULL); + return TRUE; +} - if (RtlDoesFileExists_U(DirPath)) - return TRUE; +NTSTATUS QueryProcessesUsingVolumeOrFile( + _In_ HANDLE VolumeOrFileHandle, + _Out_ PFILE_PROCESS_IDS_USING_FILE_INFORMATION *Information + ) +{ + static ULONG initialBufferSize = 0x4000; + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + IO_STATUS_BLOCK isb; - if ((dirPathDup = PhDuplicateStringZ(DirPath)) == NULL) - goto CleanupExit; + bufferSize = initialBufferSize; + buffer = malloc(bufferSize); - for (PWSTR path = _wcstok(dirPathDup, L"\\"); path; path = _wcstok(NULL, L"\\")) + while ((status = NtQueryInformationFile( + VolumeOrFileHandle, + &isb, + buffer, + bufferSize, + FileProcessIdsUsingFileInformation + )) == STATUS_INFO_LENGTH_MISMATCH) { - if (!dirPathString) - dirPathString = PhCreateString(path); - else - { - PPH_STRING tempPathString; - - tempPathString = PhConcatStrings( - 3, - dirPathString->Buffer, - L"\\", - path - ); + free(buffer); + bufferSize *= 2; - if (!RtlDoesFileExists_U(PhGetString(tempPathString))) - { - if (!CreateDirectory(PhGetString(tempPathString), NULL)) - { - PhDereferenceObject(tempPathString); - goto CleanupExit; - } - } + // Fail if we're resizing the buffer to something very large. + if (bufferSize > SIZE_MAX) + return STATUS_INSUFFICIENT_RESOURCES; - PhSwapReference(&dirPathString, tempPathString); - PhDereferenceObject(tempPathString); - } + buffer = malloc(bufferSize); } - success = TRUE; - -CleanupExit: - - if (dirPathString) + if (!NT_SUCCESS(status)) { - PhDereferenceObject(dirPathString); + free(buffer); + return status; } - if (dirPathDup) - { - PhFree(dirPathDup); - } + if (bufferSize <= 0x100000) initialBufferSize = bufferSize; + *Information = (PFILE_PROCESS_IDS_USING_FILE_INFORMATION)buffer; - return success; + return status; } -BOOLEAN RemoveDirectoryPath(_In_ PWSTR DirPath) +PPH_STRING SetupCreateFullPath( + _In_ PPH_STRING Path, + _In_ PWSTR FileName + ) { - HANDLE findHandle; - PPH_STRING findPath; - WIN32_FIND_DATA data = { 0 }; + PPH_STRING pathString; + PPH_STRING tempString; - findPath = PhConcatStrings2(DirPath, L"\\*"); + pathString = PhConcatStrings2(PhGetString(Path), FileName); - if ((findHandle = FindFirstFile(findPath->Buffer, &data)) == INVALID_HANDLE_VALUE) + if (NT_SUCCESS(PhGetFullPathEx(pathString->Buffer, NULL, &tempString))) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - { - PhDereferenceObject(findPath); - return TRUE; - } - - PhDereferenceObject(findPath); - return FALSE; + PhMoveReference(&pathString, tempString); + return pathString; } - do - { - if (PhEqualStringZ(data.cFileName, L".", TRUE) || PhEqualStringZ(data.cFileName, L"..", TRUE)) - continue; + return pathString; +} - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - PPH_STRING dirPath = PhConcatStrings(3, DirPath, L"\\", data.cFileName); +BOOLEAN SetupBase64StringToBufferEx( + _In_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_opt_ PVOID* OutputBuffer, + _Out_opt_ ULONG* OutputBufferLength + ) +{ + PVOID buffer = NULL; + ULONG bufferLength = 0; + ULONG bufferSkip = 0; + ULONG bufferFlags = 0; - RemoveDirectoryPath(dirPath->Buffer); - PhDereferenceObject(dirPath); - } - else + if (CryptStringToBinary(InputBuffer, InputBufferLength, CRYPT_STRING_BASE64, NULL, &bufferLength, &bufferSkip, &bufferFlags)) + { + if (buffer = PhAllocateSafe(bufferLength)) { - PPH_STRING filePath = PhConcatStrings(3, DirPath, L"\\", data.cFileName); - - if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + if (!CryptStringToBinary(InputBuffer, InputBufferLength, CRYPT_STRING_BASE64, buffer, &bufferLength, &bufferSkip, &bufferFlags)) { - _wchmod(filePath->Buffer, _S_IWRITE); + PhFree(buffer); + buffer = NULL; } - - SetupDeleteDirectoryFile(filePath->Buffer); - PhDereferenceObject(filePath); } + } - } while (FindNextFile(findHandle, &data)); + if (buffer) + { + if (OutputBuffer) + *OutputBuffer = buffer; + else + PhFree(buffer); - FindClose(findHandle); + if (OutputBufferLength) + *OutputBufferLength = bufferLength; - // Delete the parent directory - SetupDeleteDirectoryFile(DirPath); + return TRUE; + } - PhDereferenceObject(findPath); - return TRUE; + return FALSE; } - -VOID SetupDeleteDirectoryFile(_In_ PWSTR FileName) -{ - HANDLE tempHandle; - FILE_DISPOSITION_INFORMATION dispositionInfo; - IO_STATUS_BLOCK isb; - - if (NT_SUCCESS(PhCreateFileWin32( - &tempHandle, - FileName, - FILE_GENERIC_WRITE | DELETE, - 0, - 0, - FILE_OVERWRITE_IF, - FILE_SYNCHRONOUS_IO_NONALERT - ))) - { - dispositionInfo.DeleteFile = TRUE; - NtSetInformationFile( - tempHandle, - &isb, - &dispositionInfo, - sizeof(FILE_DISPOSITION_INFORMATION), - FileDispositionInformation - ); - NtClose(tempHandle); - } -} \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/page3.c b/tools/CustomSetupTool/CustomSetupTool/configpage.c similarity index 68% rename from tools/CustomSetupTool/CustomSetupTool/page3.c rename to tools/CustomSetupTool/CustomSetupTool/configpage.c index aa1344a04c70..0bfcb04bcea8 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page3.c +++ b/tools/CustomSetupTool/CustomSetupTool/configpage.c @@ -22,18 +22,6 @@ #include -PPH_STRING SetupInstallPath = NULL; -BOOLEAN SetupCreateDesktopShortcut = FALSE; -BOOLEAN SetupCreateDesktopShortcutAllUsers = FALSE; -BOOLEAN SetupCreateDefaultTaskManager = FALSE; -BOOLEAN SetupCreateSystemStartup = FALSE; -BOOLEAN SetupCreateMinimizedSystemStartup = FALSE; -BOOLEAN SetupInstallDebuggingTools = FALSE; -BOOLEAN SetupInstallPeViewAssociations = FALSE; -BOOLEAN SetupInstallKphService = FALSE; -BOOLEAN SetupResetSettings = FALSE; -BOOLEAN SetupStartAppAfterExit = FALSE; - INT_PTR CALLBACK SetupPropPage3_WndProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -41,12 +29,25 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: { - SetupFindInstallDirectory(); - SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -17, FW_SEMIBOLD); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), -12, FW_NORMAL); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_STATIC1), -12, FW_NORMAL); @@ -64,6 +65,9 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( SetupInitializeFont(GetDlgItem(hwndDlg, IDC_DBGTOOLS_CHECK), -12, FW_NORMAL); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_RESET_CHECK), -12, FW_NORMAL); + if (PhIsNullOrEmptyString(SetupInstallPath)) + SetupInstallPath = SetupFindInstallDirectory(); + SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetString(SetupInstallPath)); Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_CHECK), TRUE); Button_SetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_ALL_CHECK), TRUE); @@ -91,12 +95,15 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( fileDialogFolderPath = PH_AUTO(PhGetFileDialogFileName(fileDialog)); PhTrimToNullTerminatorString(fileDialogFolderPath); + PhSwapReference(&SetupInstallPath, fileDialogFolderPath); PhFreeFileDialog(fileDialog); - - PhSwapReference(&SetupInstallPath, fileDialogFolderPath); - SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetStringOrEmpty(SetupInstallPath)); } + + if (PhIsNullOrEmptyString(SetupInstallPath)) + SetupInstallPath = SetupFindInstallDirectory(); + + SetDlgItemText(hwndDlg, IDC_INSTALL_DIRECTORY, PhGetStringOrEmpty(SetupInstallPath)); } break; case IDC_STARTUP_CHECK: @@ -123,19 +130,27 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( switch (pageNotify->hdr.code) { - case PSN_KILLACTIVE: + case PSN_WIZNEXT: { + context->SetupCreateDesktopShortcut = Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_CHECK)) == BST_CHECKED; + context->SetupCreateDesktopShortcutAllUsers = Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_ALL_CHECK)) == BST_CHECKED; + context->SetupCreateDefaultTaskManager = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TASKMANAGER_CHECK)) == BST_CHECKED; + context->SetupCreateSystemStartup = Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTUP_CHECK)) == BST_CHECKED; + context->SetupCreateMinimizedSystemStartup = Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTMIN_CHECK)) == BST_CHECKED; + context->SetupInstallDebuggingTools = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DBGTOOLS_CHECK)) == BST_CHECKED; + context->SetupInstallPeViewAssociations = Button_GetCheck(GetDlgItem(hwndDlg, IDC_PEVIEW_CHECK)) == BST_CHECKED; + context->SetupInstallKphService = Button_GetCheck(GetDlgItem(hwndDlg, IDC_KPH_CHECK)) == BST_CHECKED; + context->SetupResetSettings = Button_GetCheck(GetDlgItem(hwndDlg, IDC_RESET_CHECK)) == BST_CHECKED; + context->SetupStartAppAfterExit = Button_GetCheck(GetDlgItem(hwndDlg, IDC_PHSTART_CHECK)) == BST_CHECKED; + SetupInstallPath = PhGetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_DIRECTORY)); - SetupCreateDesktopShortcut = Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_CHECK)) == BST_CHECKED; - SetupCreateDesktopShortcutAllUsers = Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHORTCUT_ALL_CHECK)) == BST_CHECKED; - SetupCreateDefaultTaskManager = Button_GetCheck(GetDlgItem(hwndDlg, IDC_TASKMANAGER_CHECK)) == BST_CHECKED; - SetupCreateSystemStartup = Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTUP_CHECK)) == BST_CHECKED; - SetupCreateMinimizedSystemStartup = Button_GetCheck(GetDlgItem(hwndDlg, IDC_STARTMIN_CHECK)) == BST_CHECKED; - SetupInstallDebuggingTools = Button_GetCheck(GetDlgItem(hwndDlg, IDC_DBGTOOLS_CHECK)) == BST_CHECKED; - SetupInstallPeViewAssociations = Button_GetCheck(GetDlgItem(hwndDlg, IDC_PEVIEW_CHECK)) == BST_CHECKED; - SetupInstallKphService = Button_GetCheck(GetDlgItem(hwndDlg, IDC_KPH_CHECK)) == BST_CHECKED; - SetupResetSettings = Button_GetCheck(GetDlgItem(hwndDlg, IDC_RESET_CHECK)) == BST_CHECKED; - SetupStartAppAfterExit = Button_GetCheck(GetDlgItem(hwndDlg, IDC_PHSTART_CHECK)) == BST_CHECKED; + + if (PhIsNullOrEmptyString(SetupInstallPath)) + SetupInstallPath = SetupFindInstallDirectory(); +#ifdef PH_BUILD_API + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)IDD_DIALOG4); + return TRUE; +#endif } break; case PSN_QUERYINITIALFOCUS: diff --git a/tools/CustomSetupTool/CustomSetupTool/download.c b/tools/CustomSetupTool/CustomSetupTool/download.c new file mode 100644 index 000000000000..a41abb3b351e --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/download.c @@ -0,0 +1,610 @@ +#include +#include +#include +#include + +PPH_STRING SetupGetVersion( + VOID +) +{ + PH_FORMAT format[7]; + + PhInitFormatU(&format[0], PHAPP_VERSION_MAJOR); + PhInitFormatC(&format[1], '.'); + PhInitFormatU(&format[2], PHAPP_VERSION_MINOR); + PhInitFormatC(&format[3], '.'); + PhInitFormatU(&format[4], PHAPP_VERSION_REVISION); + PhInitFormatC(&format[5], '.'); + PhInitFormatU(&format[6], PHAPP_VERSION_BUILD); + + return PhFormat(format, 7, 16); +} + +PPH_STRING UpdateVersionString( + VOID + ) +{ + PPH_STRING currentVersion = 0; + PPH_STRING versionHeader = NULL; + + if (currentVersion = SetupGetVersion()) + { + versionHeader = PhConcatStrings2(L"PH-SETUP-BUILD: ", currentVersion->Buffer); + PhDereferenceObject(currentVersion); + } + + return versionHeader; +} + +PPH_STRING UpdateWindowsString( + VOID + ) +{ + static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion"); + + HANDLE keyHandle; + PPH_STRING buildLabHeader = NULL; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &keyName, + 0 + ))) + { + PPH_STRING buildLabString; + + if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLabEx")) + { + buildLabHeader = PhConcatStrings2(L"PH-OsBuild: ", buildLabString->Buffer); + PhDereferenceObject(buildLabString); + } + else if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLab")) + { + buildLabHeader = PhConcatStrings2(L"PH-OsBuild: ", buildLabString->Buffer); + PhDereferenceObject(buildLabString); + } + + NtClose(keyHandle); + } + + return buildLabHeader; +} + +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString + ) +{ + PH_STRINGREF remaining, majorPart, minorPart, revisionPart; + ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0; + + PhInitializeStringRef(&remaining, PhGetStringOrEmpty(VersionString)); + PhSplitStringRefAtChar(&remaining, '.', &majorPart, &remaining); + PhSplitStringRefAtChar(&remaining, '.', &minorPart, &remaining); + PhSplitStringRefAtChar(&remaining, '.', &revisionPart, &remaining); + + PhStringToInteger64(&majorPart, 10, &majorInteger); + PhStringToInteger64(&minorPart, 10, &minorInteger); + PhStringToInteger64(&revisionPart, 10, &revisionInteger); + + return MAKE_VERSION_ULONGLONG( + (ULONG)majorInteger, + (ULONG)minorInteger, + (ULONG)revisionInteger, + 0 + ); +} + +BOOLEAN ReadRequestString( + _In_ HINTERNET Handle, + _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, + _Out_ ULONG *DataLength +) +{ + PSTR data; + ULONG allocatedLength; + ULONG dataLength; + ULONG returnLength; + BYTE buffer[PAGE_SIZE]; + + allocatedLength = sizeof(buffer); + data = (PSTR)PhAllocate(allocatedLength); + dataLength = 0; + + memset(buffer, 0, PAGE_SIZE); + memset(data, 0, allocatedLength); + + while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength)) + { + if (returnLength == 0) + break; + + if (allocatedLength < dataLength + returnLength) + { + allocatedLength *= 2; + data = (PSTR)PhReAllocate(data, allocatedLength); + } + + memcpy(data + dataLength, buffer, returnLength); + + dataLength += returnLength; + } + + if (allocatedLength < dataLength + 1) + { + allocatedLength++; + data = (PSTR)PhReAllocate(data, allocatedLength); + } + + data[dataLength] = 0; + + *DataLength = dataLength; + *Data = data; + + return TRUE; +} + +BOOLEAN SetupQueryUpdateData( + _Inout_ PPH_SETUP_CONTEXT Context + ) +{ + BOOLEAN success = FALSE; + HINTERNET httpSessionHandle = NULL; + HINTERNET httpConnectionHandle = NULL; + HINTERNET httpRequestHandle = NULL; + ULONG stringBufferLength = 0; + PSTR stringBuffer = NULL; + PVOID jsonObject = NULL; + PPH_STRING versionHeader = UpdateVersionString(); + PPH_STRING windowsHeader = UpdateWindowsString(); + PSTR value = NULL; + + if (!(httpSessionHandle = WinHttpOpen( + NULL, + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0 + ))) + { + goto CleanupExit; + } + + if (WindowsVersion >= WINDOWS_8_1) + { + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG) { WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); + } + + if (!(httpConnectionHandle = WinHttpConnect( + httpSessionHandle, + L"wj32.org", + INTERNET_DEFAULT_HTTPS_PORT, + 0 + ))) + { + //Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!(httpRequestHandle = WinHttpOpenRequest( + httpConnectionHandle, + NULL, + L"/processhacker/nightly.php?phsetup", + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE + ))) + { + //Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (WindowsVersion >= WINDOWS_7) + { + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG) { WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); + } + + if (versionHeader) + { + WinHttpAddRequestHeaders( + httpRequestHandle, + versionHeader->Buffer, + (ULONG)versionHeader->Length / sizeof(WCHAR), + WINHTTP_ADDREQ_FLAG_ADD + ); + } + + if (windowsHeader) + { + WinHttpAddRequestHeaders( + httpRequestHandle, + windowsHeader->Buffer, + (ULONG)windowsHeader->Length / sizeof(WCHAR), + WINHTTP_ADDREQ_FLAG_ADD + ); + } + + if (!WinHttpSendRequest( + httpRequestHandle, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + WINHTTP_NO_REQUEST_DATA, + 0, + WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, + 0 + )) + { + //Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) + { + //Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!ReadRequestString(httpRequestHandle, &stringBuffer, &stringBufferLength)) + goto CleanupExit; + + // Check the buffer for valid data + if (stringBuffer == NULL || stringBuffer[0] == '\0') + goto CleanupExit; + + if (!(jsonObject = PhCreateJsonParser(stringBuffer))) + goto CleanupExit; + + Context->RelVersion = PhGetJsonValueAsString(jsonObject, "version"); + Context->RelDate = PhGetJsonValueAsString(jsonObject, "updated"); + + Context->BinFileDownloadUrl = PhGetJsonValueAsString(jsonObject, "bin_url"); + Context->BinFileLength = PhGetJsonValueAsString(jsonObject, "bin_length"); + Context->BinFileHash = PhGetJsonValueAsString(jsonObject, "bin_hash"); + Context->BinFileSignature = PhGetJsonValueAsString(jsonObject, "bin_sig"); + + Context->SetupFileDownloadUrl = PhGetJsonValueAsString(jsonObject, "setup_url"); + Context->SetupFileLength = PhGetJsonValueAsString(jsonObject, "setup_length"); + Context->SetupFileHash = PhGetJsonValueAsString(jsonObject, "setup_hash"); + Context->SetupFileSignature = PhGetJsonValueAsString(jsonObject, "setup_sig"); + + Context->WebSetupFileDownloadUrl = PhGetJsonValueAsString(jsonObject, "websetup_url"); + Context->WebSetupFileVersion = PhGetJsonValueAsString(jsonObject, "websetup_version"); + Context->WebSetupFileLength = PhGetJsonValueAsString(jsonObject, "websetup_length"); + Context->WebSetupFileHash = PhGetJsonValueAsString(jsonObject, "websetup_hash"); + Context->WebSetupFileSignature = PhGetJsonValueAsString(jsonObject, "websetup_sig"); + + if (PhIsNullOrEmptyString(Context->RelVersion)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->RelDate)) + goto CleanupExit; + + if (PhIsNullOrEmptyString(Context->BinFileDownloadUrl)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->BinFileSignature)) + goto CleanupExit; + + if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl)) + goto CleanupExit; + if (PhIsNullOrEmptyString(Context->SetupFileSignature)) + goto CleanupExit; + + //if (PhIsNullOrEmptyString(Context->WebSetupFileDownloadUrl)) + // goto CleanupExit; + //if (PhIsNullOrEmptyString(Context->WebSetupFileSignature)) + // goto CleanupExit; + //if (PhIsNullOrEmptyString(Context->WebSetupFileVersion)) + // goto CleanupExit; + + success = TRUE; + +CleanupExit: + + if (httpRequestHandle) + WinHttpCloseHandle(httpRequestHandle); + + if (httpConnectionHandle) + WinHttpCloseHandle(httpConnectionHandle); + + if (httpSessionHandle) + WinHttpCloseHandle(httpSessionHandle); + + if (jsonObject) + PhFreeJsonParser(jsonObject); + + if (stringBuffer) + PhFree(stringBuffer); + + PhClearReference(&versionHeader); + PhClearReference(&windowsHeader); + + return success; +} + +BOOLEAN UpdateDownloadUpdateData( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + BOOLEAN downloadSuccess = FALSE; + HANDLE tempFileHandle = NULL; + HINTERNET httpSessionHandle = NULL; + HINTERNET httpConnectionHandle = NULL; + HINTERNET httpRequestHandle = NULL; + PPH_STRING downloadFileName = NULL; + PPH_STRING downloadHostPath = NULL; + PPH_STRING downloadUrlPath = NULL; + PPH_STRING userAgentString = NULL; + ULONG indexOfFileName = -1; + URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) }; + LARGE_INTEGER timeNow; + LARGE_INTEGER timeStart; + ULONG64 timeTicks = 0; + ULONG64 timeBitsPerSecond = 0; + + SetWindowText(Context->MainHeaderHandle, L"Initializing download request..."); + + userAgentString = PhFormatString( + L"PH_%lu.%lu_%lu", + Context->CurrentMajorVersion, + Context->CurrentMinorVersion, + Context->CurrentRevisionVersion + ); + + httpUrlComponents.dwSchemeLength = (ULONG)-1; + httpUrlComponents.dwHostNameLength = (ULONG)-1; + httpUrlComponents.dwUrlPathLength = (ULONG)-1; + + if (!WinHttpCrackUrl( + PhGetString(Context->BinFileDownloadUrl), + 0, + 0, + &httpUrlComponents + )) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + downloadHostPath = PhCreateStringEx( + httpUrlComponents.lpszHostName, + httpUrlComponents.dwHostNameLength * sizeof(WCHAR) + ); + downloadUrlPath = PhCreateStringEx( + httpUrlComponents.lpszUrlPath, + httpUrlComponents.dwUrlPathLength * sizeof(WCHAR) + ); + + { + PH_STRINGREF pathPart; + PH_STRINGREF baseNamePart; + + if (!PhSplitStringRefAtLastChar(&downloadUrlPath->sr, '/', &pathPart, &baseNamePart)) + goto CleanupExit; + + downloadFileName = PhCreateString2(&baseNamePart); + } + + Context->FilePath = PhCreateCacheFile(downloadFileName); + + if (PhIsNullOrEmptyString(Context->FilePath)) + goto CleanupExit; + + if (!NT_SUCCESS(PhCreateFileWin32( + &tempFileHandle, + PhGetString(Context->FilePath), + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + goto CleanupExit; + } + + SetWindowText(Context->MainHeaderHandle, PhFormatString( + L"Downloading Process Hacker %s...", + PhGetString(Context->RelVersion) + )->Buffer); + + //SetWindowText(Context->SubHeaderHandle, L"Progress: ~ of ~ (0.0%)"); + //SendMessage(Context->ProgressHandle, PBM_SETRANGE32, 0, (LPARAM)ExtractTotalLength); + + if (!(httpSessionHandle = WinHttpOpen( + PhGetString(userAgentString), + WindowsVersion >= WINDOWS_8_1 ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0 + ))) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (WindowsVersion >= WINDOWS_8_1) + { + WinHttpSetOption( + httpSessionHandle, + WINHTTP_OPTION_DECOMPRESSION, + &(ULONG) { WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE }, + sizeof(ULONG) + ); + } + + if (!(httpConnectionHandle = WinHttpConnect( + httpSessionHandle, + PhGetString(downloadHostPath), + httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT, + 0 + ))) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!(httpRequestHandle = WinHttpOpenRequest( + httpConnectionHandle, + NULL, + PhGetString(downloadUrlPath), + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) + ))) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (WindowsVersion >= WINDOWS_7) + { + WinHttpSetOption( + httpRequestHandle, + WINHTTP_OPTION_DISABLE_FEATURE, + &(ULONG) { WINHTTP_DISABLE_KEEP_ALIVE }, + sizeof(ULONG) + ); + } + + if (!WinHttpSendRequest( + httpRequestHandle, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + WINHTTP_NO_REQUEST_DATA, + 0, + WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, + 0 + )) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + if (!WinHttpReceiveResponse(httpRequestHandle, NULL)) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + else + { + ULONG bytesDownloaded = 0; + ULONG downloadedBytes = 0; + ULONG contentLengthSize = sizeof(ULONG); + ULONG contentLength = 0; + //PPH_STRING status; + IO_STATUS_BLOCK isb; + BYTE buffer[PAGE_SIZE]; + + //SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0); + + PhQuerySystemTime(&timeStart); + + if (!WinHttpQueryHeaders( + httpRequestHandle, + WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + &contentLength, + &contentLengthSize, + 0 + )) + { + Context->ErrorCode = GetLastError(); + goto CleanupExit; + } + + memset(buffer, 0, PAGE_SIZE); + + while (WinHttpReadData(httpRequestHandle, buffer, PAGE_SIZE, &bytesDownloaded)) + { + if (bytesDownloaded == 0) + break; + + if (!NT_SUCCESS(NtWriteFile( + tempFileHandle, + NULL, + NULL, + NULL, + &isb, + buffer, + bytesDownloaded, + NULL, + NULL + ))) + { + goto CleanupExit; + } + + downloadedBytes += (ULONG)isb.Information; + + if (bytesDownloaded != isb.Information) + goto CleanupExit; + + PhQuerySystemTime(&timeNow); + + timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; + timeBitsPerSecond = downloadedBytes / __max(timeTicks, 1); + + { + FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100); + PPH_STRING totalLength = PhFormatSize(contentLength, -1); + PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, -1); + PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1); + PPH_STRING statusMessage = PhFormatString( + L"Downloaded: %s of %s (%.0f%%)", + PhGetStringOrEmpty(totalDownloaded), + PhGetStringOrEmpty(totalLength), + percent + ); + PPH_STRING subMessage = PhFormatString( + L"Speed: %s/s", + PhGetStringOrEmpty(totalSpeed) + ); + + SetWindowText(Context->StatusHandle, statusMessage->Buffer); + SetWindowText(Context->SubStatusHandle, subMessage->Buffer); + SendMessage(Context->ProgressHandle, PBM_SETPOS, (WPARAM)percent, 0); + + PhDereferenceObject(subMessage); + PhDereferenceObject(statusMessage); + PhDereferenceObject(totalSpeed); + PhDereferenceObject(totalDownloaded); + PhDereferenceObject(totalLength); + } + } + + downloadSuccess = TRUE; + } + +CleanupExit: + + if (tempFileHandle) + NtClose(tempFileHandle); + + if (httpRequestHandle) + WinHttpCloseHandle(httpRequestHandle); + + if (httpConnectionHandle) + WinHttpCloseHandle(httpConnectionHandle); + + if (httpSessionHandle) + WinHttpCloseHandle(httpSessionHandle); + + PhClearReference(&downloadHostPath); + PhClearReference(&downloadUrlPath); + PhClearReference(&userAgentString); + PhClearReference(&downloadFileName); + + return downloadSuccess; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/downloadpage.c b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c new file mode 100644 index 000000000000..76bb0e156d2a --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/downloadpage.c @@ -0,0 +1,109 @@ +/* + * Process Hacker Toolchain - + * project setup + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +NTSTATUS SetupDownloadProgressThread( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + if (!SetupQueryUpdateData(Context)) + goto CleanupExit; + + if (!UpdateDownloadUpdateData(Context)) + goto CleanupExit; + + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG4); + return STATUS_SUCCESS; + +CleanupExit: + + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_ERROR); + return STATUS_FAIL_CHECK; +} + +INT_PTR CALLBACK SetupPropPage5_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ) +{ + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SetupLoadImage(GetDlgItem(hwndDlg, IDC_PROJECT_ICON), MAKEINTRESOURCE(IDB_PNG1)); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -17, FW_SEMIBOLD); + //SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), -12, FW_NORMAL); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), -12, FW_SEMIBOLD); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), -12, FW_NORMAL); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + LPPSHNOTIFY pageNotify = (LPPSHNOTIFY)header; + + switch (pageNotify->hdr.code) + { + case PSN_SETACTIVE: + { + context->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); + context->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); + context->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); + context->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); + + SetWindowText(context->MainHeaderHandle, L"Starting download..."); + SetWindowText(context->StatusHandle, L"Starting download..."); + SetWindowText(context->SubStatusHandle, L""); + + // Disable Next/Back buttons + PropSheet_SetWizButtons(context->DialogHandle, 0); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupDownloadProgressThread, context); + } + break; + } + } + break; + } + + return FALSE; +} \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/errorpage.c b/tools/CustomSetupTool/CustomSetupTool/errorpage.c new file mode 100644 index 000000000000..f1a364360a39 --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/errorpage.c @@ -0,0 +1,61 @@ +/* + * Process Hacker Toolchain - + * project setup + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +INT_PTR CALLBACK SetupErrorPage_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ) +{ + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SetupLoadImage(GetDlgItem(hwndDlg, IDC_PROJECT_ICON), MAKEINTRESOURCE(IDB_PNG1)); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -18, FW_SEMIBOLD); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), 0, FW_NORMAL); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + } + + return FALSE; +} \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/extract.c b/tools/CustomSetupTool/CustomSetupTool/extract.c index 7a19147bac2d..f654fe4c6fee 100644 --- a/tools/CustomSetupTool/CustomSetupTool/extract.c +++ b/tools/CustomSetupTool/CustomSetupTool/extract.c @@ -19,88 +19,112 @@ */ #include -#include +#include #include "miniz\miniz.h" -PVOID GetZipResourceData( - _In_ PULONG resourceLength +BOOLEAN SetupExtractBuild( + _In_ PPH_SETUP_CONTEXT Context ) { - HRSRC resourceHandle = NULL; - HGLOBAL resourceData; - PVOID resourceBuffer = NULL; + mz_bool status = MZ_FALSE; + ULONG64 totalLength = 0; + ULONG64 currentLength = 0; + mz_zip_archive zip_archive = { 0 }; + PPH_STRING extractPath = NULL; + SYSTEM_INFO info; - if (!(resourceHandle = FindResource(PhLibImageBase, MAKEINTRESOURCE(IDR_BIN_DATA), RT_RCDATA))) - goto CleanupExit; + GetNativeSystemInfo(&info); + +#ifdef PH_BUILD_API + ULONG resourceLength; + PVOID resourceBuffer = NULL; + PVOID zipBuffer = NULL; + ULONG zipBufferLength = 0; + PPH_STRING bufferString; - *resourceLength = SizeofResource(PhLibImageBase, resourceHandle); + if (!PhLoadResource(PhInstanceHandle, MAKEINTRESOURCE(IDR_BIN_DATA), RT_RCDATA, &resourceLength, &resourceBuffer)) + return FALSE; - if (!(resourceData = LoadResource(PhLibImageBase, resourceHandle))) + if (!(bufferString = PhZeroExtendToUtf16Ex(resourceBuffer, resourceLength))) + { + Context->ErrorCode = ERROR_PATH_NOT_FOUND; goto CleanupExit; + } - if (!(resourceBuffer = LockResource(resourceData))) + if (!SetupBase64StringToBufferEx( + bufferString->Buffer, + bufferString->Length / sizeof(WCHAR), + &zipBuffer, + &zipBufferLength + )) + { + Context->ErrorCode = ERROR_PATH_NOT_FOUND; goto CleanupExit; + } -CleanupExit: - - if (resourceHandle) - FreeResource(resourceHandle); + if (!(status = mz_zip_reader_init_mem(&zip_archive, zipBuffer, zipBufferLength, 0))) + { + Context->ErrorCode = ERROR_PATH_NOT_FOUND; + goto CleanupExit; + } - return resourceBuffer; -} +#else + PPH_BYTES zipPathUtf8; -BOOLEAN SetupExtractBuild( - _In_ PVOID Arguments - ) -{ - mz_bool status = MZ_FALSE; - ULONG64 totalLength = 0; - ULONG64 currentLength = 0; - mz_zip_archive zip_archive = { 0 }; - PVOID resourceBuffer; - ULONG resourceLength; - SYSTEM_INFO info; + if (PhIsNullOrEmptyString(Context->FilePath)) + { + Context->ErrorCode = ERROR_PATH_NOT_FOUND; + goto CleanupExit; + } - GetNativeSystemInfo(&info); + zipPathUtf8 = PhConvertUtf16ToUtf8(PhGetString(Context->FilePath)); - if (!(resourceBuffer = GetZipResourceData(&resourceLength))) + if (!(status = mz_zip_reader_init_file(&zip_archive, zipPathUtf8->Buffer, 0))) + { + Context->ErrorCode = ERROR_FILE_CORRUPT; goto CleanupExit; + } - if (!(status = mz_zip_reader_init_mem(&zip_archive, resourceBuffer, resourceLength, 0))) - goto CleanupExit; + PhDereferenceObject(zipPathUtf8); +#endif // Remove outdated files //for (ULONG i = 0; i < ARRAYSIZE(SetupRemoveFiles); i++) // SetupDeleteDirectoryFile(SetupRemoveFiles[i].FileName); - if (!CreateDirectoryPath(PhGetString(SetupInstallPath))) - goto CleanupExit; - for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) { mz_zip_archive_file_stat zipFileStat; + PPH_STRING fileName; if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; + fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (strstr(zipFileStat.m_filename, "x32\\")) + if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; } else { - if (strstr(zipFileStat.m_filename, "x64\\")) + if (PhStartsWithString2(fileName, L"x64\\", TRUE)) continue; - if (strstr(zipFileStat.m_filename, "x86\\")) + if (PhStartsWithString2(fileName, L"x86\\", TRUE)) continue; } + if (PhFindStringInString(fileName, 0, L"ProcessHacker.exe.settings.xml") != -1) + continue; + if (PhFindStringInString(fileName, 0, L"usernotesdb.xml") != -1) + continue; + totalLength += zipFileStat.m_uncomp_size; - InterlockedExchange64(&ExtractTotalLength, totalLength); } - SendMessage(Arguments, WM_START_SETUP, 0, 0); + InterlockedExchange64(&ExtractTotalLength, totalLength); + SendMessage(Context->ExtractPageHandle, WM_START_SETUP, 0, 0); for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) { @@ -109,7 +133,6 @@ BOOLEAN SetupExtractBuild( ULONG indexOfFileName = -1; PPH_STRING fileName; PPH_STRING fullSetupPath; - PPH_STRING extractPath; PVOID buffer; mz_ulong zipFileCrc32 = 0; ULONG bufferLength = 0; @@ -118,15 +141,30 @@ BOOLEAN SetupExtractBuild( if (!mz_zip_reader_file_stat(&zip_archive, i, &zipFileStat)) continue; + fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); + + if (PhFindStringInString(fileName, 0, L"ProcessHacker.exe.settings.xml") != -1) + continue; + if (PhFindStringInString(fileName, 0, L"usernotesdb.xml") != -1) + continue; + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (strstr(zipFileStat.m_filename, "x32\\")) + if (PhStartsWithString2(fileName, L"x32\\", TRUE)) continue; + + if (PhFindStringInString(fileName, 0, L"x64\\") != -1) + PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); } else { - if (strstr(zipFileStat.m_filename, "x64\\")) + if (PhStartsWithString2(fileName, L"x64\\", TRUE)) + continue; + if (PhStartsWithString2(fileName, L"x86\\", TRUE)) continue; + + if (PhFindStringInString(fileName, 0, L"x32\\") != -1) + PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); } if (!(buffer = mz_zip_reader_extract_to_heap( @@ -136,41 +174,38 @@ BOOLEAN SetupExtractBuild( 0 ))) { + Context->ErrorCode = ERROR_FILE_INVALID; goto CleanupExit; } if ((zipFileCrc32 = mz_crc32(zipFileCrc32, buffer, bufferLength)) != zipFileStat.m_crc32) - goto CleanupExit; - - if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - { - fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); - - if (PhFindStringInString(fileName, 0, L"x64\\") != -1) - PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); - } - else { - fileName = PhConvertUtf8ToUtf16(zipFileStat.m_filename); - - if (PhFindStringInString(fileName, 0, L"x32\\") != -1) - PhMoveReference(&fileName, PhSubstring(fileName, 4, (fileName->Length / 2) - 4)); + Context->ErrorCode = ERROR_CRC; + goto CleanupExit; } - extractPath = PhConcatStrings(3, PhGetString(SetupInstallPath), L"\\", PhGetString(fileName)); - //OutputDebugString(PhFormatString(L"%s\r\n", extractPath->Buffer)->Buffer); + extractPath = PhConcatStrings( + 3, + PhGetString(SetupInstallPath), + L"\\", + PhGetString(fileName) + ); if (fullSetupPath = PhGetFullPath(extractPath->Buffer, &indexOfFileName)) { PPH_STRING directoryPath; if (indexOfFileName == -1) + { + Context->ErrorCode = ERROR_FILE_CORRUPT; goto CleanupExit; + } if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName)) { - if (!CreateDirectoryPath(PhGetString(directoryPath))) + if (!NT_SUCCESS(PhCreateDirectory(directoryPath))) { + Context->ErrorCode = ERROR_DIRECTORY_NOT_SUPPORTED; PhDereferenceObject(directoryPath); PhDereferenceObject(fullSetupPath); goto CleanupExit; @@ -182,7 +217,22 @@ BOOLEAN SetupExtractBuild( PhDereferenceObject(fullSetupPath); } - // TODO: Rename existing folder items and restore if the setup fails. + // TODO: Backup file and restore if the setup fails. + //{ + // PPH_STRING backupFilePath; + // + // backupFilePath = PhConcatStrings( + // 4, + // PhGetString(Context->SetupInstallPath), + // L"\\", + // PhGetString(fileName), + // L".bak" + // ); + // + // MoveFile(extractPath->Buffer, backupFilePath->Buffer); + // + // PhDereferenceObject(backupFilePath); + //} if (!NT_SUCCESS(PhCreateFileWin32( &fileHandle, @@ -194,6 +244,7 @@ BOOLEAN SetupExtractBuild( FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) { + Context->ErrorCode = ERROR_FILE_CORRUPT; goto CleanupExit; } @@ -209,26 +260,55 @@ BOOLEAN SetupExtractBuild( NULL ))) { + Context->ErrorCode = ERROR_FILE_CORRUPT; goto CleanupExit; } if (isb.Information != bufferLength) + { + Context->ErrorCode = ERROR_FILE_CORRUPT; goto CleanupExit; + } currentLength += bufferLength; InterlockedExchange64(&ExtractCurrentLength, currentLength); - SendMessage(Arguments, WM_UPDATE_SETUP, 0, (LPARAM)extractPath); + + SendMessage(Context->ExtractPageHandle, WM_UPDATE_SETUP, 0, (LPARAM)PhGetBaseName(extractPath)); NtClose(fileHandle); mz_free(buffer); } mz_zip_reader_end(&zip_archive); + +#ifdef PH_BUILD_API + if (zipBuffer) + PhFree(zipBuffer); + if (resourceBuffer) + PhFree(resourceBuffer); + if (bufferString) + PhDereferenceObject(bufferString); +#endif + if (extractPath) + PhDereferenceObject(extractPath); + return TRUE; CleanupExit: + mz_zip_reader_end(&zip_archive); + +#ifdef PH_BUILD_API + if (zipBuffer) + PhFree(zipBuffer); + if (resourceBuffer) + PhFree(resourceBuffer); + if (bufferString) + PhDereferenceObject(bufferString); +#endif + if (extractPath) + PhDereferenceObject(extractPath); + return FALSE; } - diff --git a/tools/CustomSetupTool/CustomSetupTool/include/setup.h b/tools/CustomSetupTool/CustomSetupTool/include/setup.h index 2b4a6ecec879..fc6734629457 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/setup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setup.h @@ -23,28 +23,25 @@ #ifndef _SETUP_H #define _SETUP_H -#pragma comment(lib, "Comctl32.lib") -#pragma comment(lib, "Shlwapi.lib") -#pragma comment(lib, "WindowsCodecs.lib") - -#define CINTERFACE -#define COBJMACROS #include #include #include +#include +#include +#include + #include -#include -#include +#include +#include #include -#include +#include #include #include #include "resource.h" -#include -#include "..\..\..\ProcessHacker\include\phappres.h" -#include "..\..\..\ProcessHacker\include\phapprev.h" +// Version Information +#include "..\..\ProcessHacker\include\phappres.h" // Win32 PropertySheet Control IDs #define IDD_PROPSHEET_ID 1006 // ID of the propsheet dialog template in comctl32.dll @@ -62,32 +59,88 @@ #define WM_UPDATE_SETUP (WM_APP + 2) #define WM_END_SETUP (WM_APP + 3) -extern HANDLE MutantHandle; -extern PPH_STRING SetupInstallPath; -extern BOOLEAN SetupCreateDesktopShortcut; -extern BOOLEAN SetupCreateDesktopShortcutAllUsers; -extern BOOLEAN SetupCreateDefaultTaskManager; -extern BOOLEAN SetupCreateSystemStartup; -extern BOOLEAN SetupCreateMinimizedSystemStartup; -extern BOOLEAN SetupInstallDebuggingTools; -extern BOOLEAN SetupInstallPeViewAssociations; -extern BOOLEAN SetupInstallKphService; -extern BOOLEAN SetupResetSettings; -extern BOOLEAN SetupStartAppAfterExit; - typedef enum _SETUP_COMMAND_TYPE { SETUP_COMMAND_INSTALL, SETUP_COMMAND_UNINSTALL, SETUP_COMMAND_UPDATE, SETUP_COMMAND_REPAIR, + SETUP_COMMAND_SILENTINSTALL, } SETUP_COMMAND_TYPE; -typedef struct _SETUP_INSTANCE +typedef struct _PH_SETUP_CONTEXT { - ULONG Version; - HWND WindowHandle; -} SETUP_INSTANCE, *PSETUP_INSTANCE; + HWND DialogHandle; + HWND PropSheetBackHandle; + HWND PropSheetForwardHandle; + HWND PropSheetCancelHandle; + + HWND WelcomePageHandle; + HWND EulaPageHandle; + HWND ConfigPageHandle; + HWND DownloadPageHandle; + HWND ExtractPageHandle; + HWND FinalPageHandle; + HWND ErrorPageHandle; + + HICON IconSmallHandle; + HICON IconLargeHandle; + + union + { + ULONG Flags; + struct + { + ULONG SetupCreateDesktopShortcut : 1; + ULONG SetupCreateDesktopShortcutAllUsers : 1; + ULONG SetupCreateDefaultTaskManager : 1; + ULONG SetupCreateSystemStartup : 1; + ULONG SetupCreateMinimizedSystemStartup : 1; + ULONG SetupInstallDebuggingTools : 1; + ULONG SetupInstallPeViewAssociations : 1; + ULONG SetupInstallKphService : 1; + ULONG SetupResetSettings : 1; + ULONG SetupStartAppAfterExit : 1; + ULONG SetupKphInstallRequired : 1; + ULONG Spare : 21; + }; + }; + + ULONG ErrorCode; + PPH_STRING FilePath; + + PPH_STRING RelDate; + PPH_STRING RelVersion; + + PPH_STRING BinFileDownloadUrl; + PPH_STRING BinFileLength; + PPH_STRING BinFileHash; + PPH_STRING BinFileSignature; + PPH_STRING SetupFileDownloadUrl; + PPH_STRING SetupFileLength; + PPH_STRING SetupFileHash; + PPH_STRING SetupFileSignature; + + PPH_STRING WebSetupFileDownloadUrl; + PPH_STRING WebSetupFileVersion; + PPH_STRING WebSetupFileLength; + PPH_STRING WebSetupFileHash; + PPH_STRING WebSetupFileSignature; + + HWND MainHeaderHandle; + HWND StatusHandle; + HWND SubStatusHandle; + HWND ProgressHandle; + + BOOLEAN SetupRunning; + ULONG CurrentMajorVersion; + ULONG CurrentMinorVersion; + ULONG CurrentRevisionVersion; + WNDPROC TaskDialogWndProc; +} PH_SETUP_CONTEXT, *PPH_SETUP_CONTEXT; + +extern SETUP_COMMAND_TYPE SetupMode; +extern PPH_STRING SetupInstallPath; VOID SetupLoadImage( _In_ HWND WindowHandle, @@ -115,55 +168,103 @@ INT_PTR CALLBACK SetupPropPage3_WndProc( _Inout_ LPARAM lParam ); -INT_PTR CALLBACK SetupPropPage4_WndProc( +INT_PTR CALLBACK SetupInstallPropPage_WndProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _Inout_ WPARAM wParam, _Inout_ LPARAM lParam ); -// page4.c +INT_PTR CALLBACK SetupPropPage5_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ); -typedef struct _SETUP_PROGRESS_THREAD -{ - HWND DialogHandle; - HWND PropSheetHandle; -} SETUP_PROGRESS_THREAD, *PSETUP_PROGRESS_THREAD; +INT_PTR CALLBACK SetupErrorPage_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ); + +// page4.c -extern BOOLEAN SetupRunning; extern ULONG64 ExtractCurrentLength; extern ULONG64 ExtractTotalLength; // setup.c -VOID SetupInstallKph( - VOID +VOID SetupStartKph( + _In_ PPH_SETUP_CONTEXT Context, + _In_ BOOLEAN ForceInstall ); -ULONG SetupUninstallKph( - VOID +BOOLEAN SetupUninstallKph( + _In_ PPH_SETUP_CONTEXT Context ); NTSTATUS SetupCreateUninstallKey( + _In_ PPH_SETUP_CONTEXT Context + ); + +NTSTATUS SetupDeleteUninstallKey( VOID ); VOID SetupSetWindowsOptions( + _In_ PPH_SETUP_CONTEXT Context + ); + +VOID SetupDeleteWindowsOptions( + _In_ PPH_SETUP_CONTEXT Context + ); + +BOOLEAN SetupCreateUninstallFile( + _In_ PPH_SETUP_CONTEXT Context + ); + +VOID SetupDeleteUninstallFile( + _In_ PPH_SETUP_CONTEXT Context + ); + +BOOLEAN SetupExecuteProcessHacker( + _In_ PPH_SETUP_CONTEXT Context + ); + +VOID SetupUpgradeSettingsFile( VOID ); -VOID SetupCreateUninstallFile( +VOID SetupCreateImageFileExecutionOptions( VOID ); -BOOLEAN SetupExecuteProcessHacker( - _In_ HWND Parent +// download.c + +#define MAKE_VERSION_ULONGLONG(major, minor, build, revision) \ + (((ULONGLONG)(major) << 48) | \ + ((ULONGLONG)(minor) << 32) | \ + ((ULONGLONG)(build) << 16) | \ + ((ULONGLONG)(revision) << 0)) + +ULONG64 ParseVersionString( + _Inout_ PPH_STRING VersionString + ); + +BOOLEAN SetupQueryUpdateData( + _In_ PPH_SETUP_CONTEXT Context + ); + +BOOLEAN UpdateDownloadUpdateData( + _In_ PPH_SETUP_CONTEXT Context ); // extract.c BOOLEAN SetupExtractBuild( - _In_ PVOID Arguments + _In_ PPH_SETUP_CONTEXT Context ); // update.c @@ -172,4 +273,16 @@ VOID SetupShowUpdateDialog( VOID ); -#endif \ No newline at end of file +// updatesetup.c + +NTSTATUS SetupUpdateWebSetupBuild( + _In_ PPH_SETUP_CONTEXT Context + ); + +// uninstall.c + +VOID SetupShowUninstallDialog( + VOID + ); + +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h b/tools/CustomSetupTool/CustomSetupTool/include/setupsup.h similarity index 75% rename from tools/CustomSetupTool/CustomSetupTool/include/appsup.h rename to tools/CustomSetupTool/CustomSetupTool/include/setupsup.h index e74fa065ef9f..afe7c130dc0f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/include/appsup.h +++ b/tools/CustomSetupTool/CustomSetupTool/include/setupsup.h @@ -36,18 +36,10 @@ typedef struct _SETUP_REMOVE_FILE PWSTR FileName; } SETUP_REMOVE_FILE, *PSETUP_REMOVE_FILE; -VOID SetupFindInstallDirectory( +PPH_STRING SetupFindInstallDirectory( VOID ); -PVOID ExtractResourceToBuffer( - _In_ PWSTR Resource - ); - -VOID SetupDeleteDirectoryFile( - _In_ PWSTR FileName - ); - VOID SetupInitializeFont( _In_ HWND ControlHandle, _In_ LONG Height, @@ -55,7 +47,8 @@ VOID SetupInitializeFont( ); VOID SetupCreateLink( - _In_ PWSTR DestFilePath, + _In_ PWSTR AppUserModelId, + _In_ PWSTR LinkFilePath, _In_ PWSTR FilePath, _In_ PWSTR FileParentDir ); @@ -68,6 +61,10 @@ HBITMAP LoadPngImageFromResources( _In_ PCWSTR Name ); +BOOLEAN CheckProcessHackerInstalled( + VOID + ); + PPH_STRING GetProcessHackerInstallPath( VOID ); @@ -76,13 +73,21 @@ BOOLEAN ShutdownProcessHacker( VOID ); -BOOLEAN CreateDirectoryPath( - _In_ PWSTR DirectoryPath +NTSTATUS QueryProcessesUsingVolumeOrFile( + _In_ HANDLE VolumeOrFileHandle, + _Out_ PFILE_PROCESS_IDS_USING_FILE_INFORMATION *Information ); -BOOLEAN RemoveDirectoryPath( - _In_ PWSTR DirPath +PPH_STRING SetupCreateFullPath( + _In_ PPH_STRING Path, + _In_ PWSTR FileName ); +BOOLEAN SetupBase64StringToBufferEx( + _In_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_opt_ PVOID* OutputBuffer, + _Out_opt_ ULONG* OutputBufferLength + ); -#endif \ No newline at end of file +#endif diff --git a/tools/CustomSetupTool/CustomSetupTool/page4.c b/tools/CustomSetupTool/CustomSetupTool/install.c similarity index 55% rename from tools/CustomSetupTool/CustomSetupTool/page4.c rename to tools/CustomSetupTool/CustomSetupTool/install.c index 485fa6370bd3..53d183529d6f 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page4.c +++ b/tools/CustomSetupTool/CustomSetupTool/install.c @@ -27,66 +27,85 @@ ULONG64 ExtractCurrentLength = 0; ULONG64 ExtractTotalLength = 0; NTSTATUS SetupProgressThread( - _In_ PSETUP_PROGRESS_THREAD Context + _In_ PPH_SETUP_CONTEXT Context ) { - //if (SetupInstallDebuggingTools) - // SetupDownloadThread(Arguments) - // Stop Process Hacker. if (!ShutdownProcessHacker()) goto CleanupExit; // Stop the kernel driver(s). - if (!SetupUninstallKph()) + if (!SetupUninstallKph(Context)) goto CleanupExit; + // Upgrade the 2.x settings file. + SetupUpgradeSettingsFile(); + // Remove the previous installation. - if (SetupResetSettings) - RemoveDirectoryPath(PhGetString(SetupInstallPath)); + if (Context->SetupResetSettings) + PhDeleteDirectory(SetupInstallPath); // Create the install folder path. - if (!CreateDirectoryPath(PhGetString(SetupInstallPath))) + if (!NT_SUCCESS(PhCreateDirectory(SetupInstallPath))) goto CleanupExit; - // Create the ARP uninstall entries. - SetupCreateUninstallKey(); // Create the uninstaller. - SetupCreateUninstallFile(); + if (!SetupCreateUninstallFile(Context)) + goto CleanupExit; + + // Create the ARP uninstall entries. + SetupCreateUninstallKey(Context); - // - SetupSetWindowsOptions(); + // Create autorun and shortcuts. + SetupSetWindowsOptions(Context); + + // Set the default image execution options. + SetupCreateImageFileExecutionOptions(); // Setup new installation. - if (!SetupExtractBuild(Context->DialogHandle)) + if (!SetupExtractBuild(Context)) goto CleanupExit; - // Install updated kernel driver - if (SetupInstallKphService) - SetupInstallKph(); + // Setup kernel driver. + if (Context->SetupInstallKphService) + SetupStartKph(Context, TRUE); - PostMessage(Context->DialogHandle, WM_END_SETUP, 0, 0); - PhDereferenceObject(Context); + PostMessage(Context->ExtractPageHandle, WM_END_SETUP, 0, 0); return STATUS_SUCCESS; CleanupExit: - - PostMessage(Context->PropSheetHandle, PSM_SETCURSELID, 0, IDD_ERROR); - PhDereferenceObject(Context); + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_ERROR); return STATUS_FAIL_CHECK; } -INT_PTR CALLBACK SetupPropPage4_WndProc( +INT_PTR CALLBACK SetupInstallPropPage_WndProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _Inout_ WPARAM wParam, _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: { + context->ExtractPageHandle = hwndDlg; + SetupLoadImage(GetDlgItem(hwndDlg, IDC_PROJECT_ICON), MAKEINTRESOURCE(IDB_PNG1)); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -17, FW_SEMIBOLD); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), -12, FW_NORMAL); @@ -117,26 +136,23 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( break; case PSN_SETACTIVE: { - HWND hwPropSheet; - HANDLE threadHandle; - PSETUP_PROGRESS_THREAD progress; + context->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); + context->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); + context->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); + context->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); - hwPropSheet = pageNotify->hdr.hwndFrom; + SetWindowText(context->MainHeaderHandle, L"Installing..."); + SetWindowText(context->StatusHandle, L""); + SetWindowText(context->SubStatusHandle, L"Progress: ~ of ~ (0.0%)"); // Disable Next/Back buttons - PropSheet_SetWizButtons(hwPropSheet, 0); + PropSheet_SetWizButtons(context->DialogHandle, 0); if (!SetupRunning) { SetupRunning = TRUE; - // Setup the progress thread - progress = PhCreateAlloc(sizeof(SETUP_PROGRESS_THREAD)); - progress->DialogHandle = hwndDlg; - progress->PropSheetHandle = hwPropSheet; - - if (threadHandle = PhCreateThread(0, SetupProgressThread, progress)) - NtClose(threadHandle); + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupProgressThread, context); } } break; @@ -145,26 +161,36 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( break; case WM_START_SETUP: { - SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), - PhaFormatString(L"Installing Process Hacker %lu.%lu.%lu", PHAPP_VERSION_MAJOR, PHAPP_VERSION_MINOR, PHAPP_VERSION_REVISION)->Buffer); - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), L"Progress: ~ of ~ (0.0%)"); - SendMessage(GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)ExtractTotalLength); +#ifdef PH_BUILD_API + SetWindowText(context->MainHeaderHandle, PhaFormatString( + L"Installing Process Hacker %lu.%lu.%lu", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR, + PHAPP_VERSION_REVISION + )->Buffer); +#else + SetWindowText(context->MainHeaderHandle, PhaFormatString( + L"Installing Process Hacker %s", + PhGetString(context->RelVersion) + )->Buffer); +#endif + SendMessage(context->ProgressHandle, PBM_SETRANGE32, 0, (LPARAM)ExtractTotalLength); } break; case WM_UPDATE_SETUP: { PPH_STRING currentFile = (PPH_STRING)lParam; - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), + SetWindowText(context->StatusHandle, PhaConcatStrings2(L"Extracting: ", currentFile->Buffer)->Buffer ); - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), PhaFormatString( + SetWindowText(context->SubStatusHandle, PhaFormatString( L"Progress: %s of %s (%.2f%%)", PhaFormatSize(ExtractCurrentLength, -1)->Buffer, PhaFormatSize(ExtractTotalLength, -1)->Buffer, (FLOAT)((double)ExtractCurrentLength / (double)ExtractTotalLength) * 100 )->Buffer); - SendMessage(GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS), PBM_SETPOS, (WPARAM)ExtractCurrentLength, 0); + SendMessage(context->ProgressHandle, PBM_SETPOS, (WPARAM)ExtractCurrentLength, 0); PhDereferenceObject(currentFile); } @@ -173,13 +199,14 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( { SetupRunning = FALSE; - SetWindowText(GetDlgItem(hwndDlg, IDC_MAINHEADER), L"Setup Complete"); - SetWindowText(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), L"Extract complete"); - Button_SetText(GetDlgItem(GetParent(hwndDlg), IDC_PROPSHEET_CANCEL), L"Close"); + SetWindowText(context->MainHeaderHandle, L"Setup Complete"); + SetWindowText(context->StatusHandle, L"Extract complete"); + Button_SetText(context->PropSheetCancelHandle, L"Close"); - if (SetupStartAppAfterExit) + if (context->SetupStartAppAfterExit) { - SetupExecuteProcessHacker(GetParent(hwndDlg)); + SetupExecuteProcessHacker(context); + PropSheet_PressButton(GetParent(hwndDlg), PSBTN_FINISH); } } @@ -187,4 +214,4 @@ INT_PTR CALLBACK SetupPropPage4_WndProc( } return FALSE; -} \ No newline at end of file +} diff --git a/tools/CustomSetupTool/CustomSetupTool/page2.c b/tools/CustomSetupTool/CustomSetupTool/licencepage.c similarity index 56% rename from tools/CustomSetupTool/CustomSetupTool/page2.c rename to tools/CustomSetupTool/CustomSetupTool/licencepage.c index 3d04a4a180d6..5a30d86c1596 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page2.c +++ b/tools/CustomSetupTool/CustomSetupTool/licencepage.c @@ -27,35 +27,38 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: - { - PSTR resourceBuffer; + { + ULONG resourceLength; + PVOID resourceBuffer; PPH_STRING eulaTextString; SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -17, FW_SEMIBOLD); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), -12, FW_NORMAL); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_EDIT1), -12, FW_NORMAL); - if (resourceBuffer = ExtractResourceToBuffer(MAKEINTRESOURCE(IDR_LICENCE_DATA))) + if (PhLoadResource(PhInstanceHandle, MAKEINTRESOURCE(IDR_LICENCE_DATA), RT_RCDATA, &resourceLength, &resourceBuffer)) { - if (eulaTextString = PhConvertMultiByteToUtf16(resourceBuffer)) + if (eulaTextString = PhConvertUtf8ToUtf16Ex(resourceBuffer, resourceLength)) { - ULONG startPos, endPos; - - SendMessage(GetDlgItem(hwndDlg, IDC_EDIT1), EM_GETSEL, (WPARAM)&startPos, (WPARAM)&endPos); - - // move the caret to the end of the text - int outLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_EDIT1)); - SendMessage(GetDlgItem(hwndDlg, IDC_EDIT1), EM_SETSEL, outLength, outLength); - - // insert the text at the new caret position - SendMessage(GetDlgItem(hwndDlg, IDC_EDIT1), EM_REPLACESEL, TRUE, (WPARAM)eulaTextString->Buffer); - - // restore the previous selection - SendMessage(GetDlgItem(hwndDlg, IDC_EDIT1), EM_SETSEL, startPos, endPos); - + SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT1), eulaTextString->Buffer); PhDereferenceObject(eulaTextString); } @@ -72,24 +75,26 @@ INT_PTR CALLBACK SetupPropPage2_WndProc( switch (pageNotify->hdr.code) { - case PSN_SETACTIVE: - { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwPropSheet, IDC_PROPSHEET_NEXT), TRUE); - - // Disable the Next button - //PropSheet_SetWizButtons(hwPropSheet, PSWIZB_BACK); - } - break; case PSN_QUERYINITIALFOCUS: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)context->PropSheetCancelHandle); + return TRUE; + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_SETFOCUS: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - HWND hwnd = GetDlgItem(hwPropSheet, IDC_PROPSHEET_CANCEL); + HWND handle = (HWND)lParam; + + // Prevent the edit control text from being selected. + SendMessage(handle, EM_SETSEL, -1, 0); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)hwnd); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)TRUE); + return TRUE; } - return TRUE; } } break; diff --git a/tools/CustomSetupTool/CustomSetupTool/main.c b/tools/CustomSetupTool/CustomSetupTool/main.c index 694a1809cb9c..4200a23d58f3 100644 --- a/tools/CustomSetupTool/CustomSetupTool/main.c +++ b/tools/CustomSetupTool/CustomSetupTool/main.c @@ -20,58 +20,21 @@ #include -HANDLE MutantHandle = NULL; SETUP_COMMAND_TYPE SetupMode = SETUP_COMMAND_INSTALL; -PH_COMMAND_LINE_OPTION options[] = -{ - { SETUP_COMMAND_INSTALL, L"install", NoArgumentType }, - { SETUP_COMMAND_UNINSTALL, L"uninstall", NoArgumentType }, - { SETUP_COMMAND_UPDATE, L"update", NoArgumentType }, - { SETUP_COMMAND_REPAIR, L"repair", NoArgumentType }, -}; +PPH_STRING SetupInstallPath = NULL; -static NTSTATUS CreateSetupMutant( +VOID PhAddDefaultSettings( VOID ) { - OBJECT_ATTRIBUTES oa; - UNICODE_STRING mutantName; - - RtlInitUnicodeString(&mutantName, L"\\BaseNamedObjects\\ProcessHackerSetup"); - InitializeObjectAttributes( - &oa, - &mutantName, - 0, - NULL, - NULL - ); - - return NtCreateMutant(&MutantHandle, MUTANT_ALL_ACCESS, &oa, FALSE); + NOTHING; } -static VOID SetupInitializeDpi( +VOID PhUpdateCachedSettings( VOID ) { - HDC hdc; - - if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) - { - PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - } -} - -static BOOLEAN NTAPI MainPropSheetCommandLineCallback( - _In_opt_ PPH_COMMAND_LINE_OPTION Option, - _In_opt_ PPH_STRING Value, - _In_opt_ PVOID Context - ) -{ - if (Option) - SetupMode = Option->Id; - - return TRUE; + NOTHING; } INT CALLBACK MainPropSheet_Callback( @@ -82,113 +45,279 @@ INT CALLBACK MainPropSheet_Callback( { switch (uMsg) { - case PSCB_INITIALIZED: - break; case PSCB_PRECREATE: { if (lParam) { - ((LPDLGTEMPLATE)lParam)->style |= WS_MINIMIZEBOX; + if (((DLGTEMPLATEEX *)lParam)->signature == USHORT_MAX) + ((DLGTEMPLATEEX *)lParam)->style |= WS_MINIMIZEBOX; + else + ((DLGTEMPLATE *)lParam)->style |= WS_MINIMIZEBOX; } } break; + case PSCB_INITIALIZED: + { + PPH_SETUP_CONTEXT context; + + context = PhCreateAlloc(sizeof(PH_SETUP_CONTEXT)); + memset(context, 0, sizeof(PH_SETUP_CONTEXT)); + + context->CurrentMajorVersion = PHAPP_VERSION_MAJOR; + context->CurrentMinorVersion = PHAPP_VERSION_MINOR; + context->CurrentRevisionVersion = PHAPP_VERSION_REVISION; + + context->DialogHandle = hwndDlg; + context->PropSheetBackHandle = GetDlgItem(hwndDlg, IDC_PROPSHEET_BACK); + context->PropSheetForwardHandle = GetDlgItem(hwndDlg, IDC_PROPSHEET_NEXT); + context->PropSheetCancelHandle = GetDlgItem(hwndDlg, IDC_PROPSHEET_CANCEL); + + SetupInitializeFont(context->PropSheetBackHandle, -12, FW_NORMAL); + SetupInitializeFont(context->PropSheetForwardHandle, -12, FW_NORMAL); + SetupInitializeFont(context->PropSheetCancelHandle, -12, FW_NORMAL); + + PhSetWindowContext(hwndDlg, ULONG_MAX, context); + } + break; } return FALSE; } -INT WINAPI wWinMain( - _In_ HINSTANCE hInstance, - _In_opt_ HINSTANCE hPrevInstance, - _In_ PWSTR lpCmdLine, - _In_ INT nCmdShow +VOID SetupShowInstallDialog( + VOID ) { - if (!NT_SUCCESS(CreateSetupMutant())) - return 1; + PROPSHEETPAGE propSheetPage = { sizeof(PROPSHEETPAGE) }; + PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) }; + HPROPSHEETPAGE pages[6]; - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + propSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_USECALLBACK | PSH_WIZARD_LITE; + propSheetHeader.pszIcon = MAKEINTRESOURCE(IDI_ICON1); + propSheetHeader.pfnCallback = MainPropSheet_Callback; + propSheetHeader.hInstance = PhInstanceHandle; + propSheetHeader.phpage = pages; - if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0))) - return 1; + // welcome page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG1); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = SetupPropPage1_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - PhApplicationName = L"Process Hacker - Setup"; - PhGuiSupportInitialization(); - SetupInitializeDpi(); + // eula page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG2); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = SetupPropPage2_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + // config page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG3); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = SetupPropPage3_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + // download page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG5); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = SetupPropPage5_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + // extract page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = SetupInstallPropPage_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + // error page + memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); + propSheetPage.dwSize = sizeof(PROPSHEETPAGE); + propSheetPage.dwFlags = PSP_USETITLE; + propSheetPage.pszTitle = PhApplicationName; + propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_ERROR); + propSheetPage.hInstance = PhInstanceHandle; + propSheetPage.pfnDlgProc = SetupErrorPage_WndProc; + pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); + + PhModalPropertySheet(&propSheetHeader); +} + +BOOLEAN NTAPI MainPropSheetCommandLineCallback( + _In_opt_ PPH_COMMAND_LINE_OPTION Option, + _In_opt_ PPH_STRING Value, + _In_opt_ PVOID Context + ) +{ + if (Option) { - PH_STRINGREF commandLine; + SetupMode = Option->Id; + + if (SetupMode == SETUP_COMMAND_UPDATE) + { + PPH_STRING directory; + PPH_STRING string; + + if (PhIsNullOrEmptyString(Value)) + { + SetupInstallPath = SetupFindInstallDirectory(); + return TRUE; + } + + if (string = PhHexStringToBufferEx(&Value->sr)) + { + if (directory = PhGetFullPath(string->Buffer, NULL)) + { + PhSwapReference(&SetupInstallPath, directory); - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - if (!PhParseCommandLine( - &commandLine, + if (!PhEndsWithString2(directory, L"\\", FALSE)) // HACK + { + PhMoveReference(&SetupInstallPath, PhConcatStringRefZ(&directory->sr, L"\\")); + } + + PhDereferenceObject(directory); + } + + PhDereferenceObject(string); + } + } + + if (PhIsNullOrEmptyString(SetupInstallPath)) + { + SetupInstallPath = SetupFindInstallDirectory(); + return TRUE; + } + } + else + { + // HACK: PhParseCommandLine requires the - symbol for commandline parameters + // and we already support the -silent parameter however we need to maintain + // compatibility with the legacy Inno Setup. + if (!PhIsNullOrEmptyString(Value)) + { + if (PhEqualString2(Value, L"/silent", TRUE)) + { + SetupMode = SETUP_COMMAND_SILENTINSTALL; + } + } + } + + return TRUE; +} + +VOID SetupParseCommandLine( + VOID + ) +{ + static PH_COMMAND_LINE_OPTION options[] = + { + { SETUP_COMMAND_INSTALL, L"install", NoArgumentType }, + { SETUP_COMMAND_UNINSTALL, L"uninstall", NoArgumentType }, + { SETUP_COMMAND_UPDATE, L"update", OptionalArgumentType }, + { SETUP_COMMAND_REPAIR, L"repair", NoArgumentType }, + { SETUP_COMMAND_SILENTINSTALL, L"silent", NoArgumentType }, + }; + PPH_STRING commandLine; + + if (NT_SUCCESS(PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) + { + PhParseCommandLine( + &commandLine->sr, options, ARRAYSIZE(options), - PH_COMMAND_LINE_IGNORE_FIRST_PART, + PH_COMMAND_LINE_IGNORE_UNKNOWN_OPTIONS | PH_COMMAND_LINE_IGNORE_FIRST_PART, MainPropSheetCommandLineCallback, NULL - )) - { - return 1; - } + ); + + PhDereferenceObject(commandLine); } +} + +VOID SetupInitializeMutant( + VOID + ) +{ + HANDLE mutantHandle; + PPH_STRING objectName; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING objectNameUs; + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"PhSetupMutant_"); + PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + + objectName = PhFormat(format, 2, 16); + PhStringRefToUnicodeString(&objectName->sr, &objectNameUs); + + InitializeObjectAttributes( + &objectAttributes, + &objectNameUs, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), + NULL + ); + + NtCreateMutant( + &mutantHandle, + MUTANT_QUERY_STATE, + &objectAttributes, + TRUE + ); + + PhDereferenceObject(objectName); +} + +INT WINAPI wWinMain( + _In_ HINSTANCE Instance, + _In_opt_ HINSTANCE PrevInstance, + _In_ PWSTR CmdLine, + _In_ INT CmdShow + ) +{ + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + + if (!NT_SUCCESS(PhInitializePhLibEx(L"Process Hacker - Setup", ULONG_MAX, Instance, 0, 0))) + return EXIT_FAILURE; + + SetupInitializeMutant(); + PhGuiSupportInitialization(); + SetupParseCommandLine(); switch (SetupMode) { case SETUP_COMMAND_INSTALL: default: - { - PROPSHEETPAGE propSheetPage = { sizeof(PROPSHEETPAGE) }; - PROPSHEETHEADER propSheetHeader = { sizeof(PROPSHEETHEADER) }; - HPROPSHEETPAGE pages[4]; - - propSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_USECALLBACK | PSH_WIZARD_LITE; - propSheetHeader.hInstance = PhLibImageBase; - propSheetHeader.pszIcon = MAKEINTRESOURCE(IDI_ICON1); - propSheetHeader.pfnCallback = MainPropSheet_Callback; - propSheetHeader.phpage = pages; - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.pszTitle = PhApplicationName; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG1); - propSheetPage.pfnDlgProc = SetupPropPage1_WndProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.pszTitle = PhApplicationName; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG2); - propSheetPage.pfnDlgProc = SetupPropPage2_WndProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG3); - propSheetPage.pfnDlgProc = SetupPropPage3_WndProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.dwFlags = PSP_USETITLE; - propSheetPage.pszTitle = PhApplicationName; - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG4); - propSheetPage.pfnDlgProc = SetupPropPage4_WndProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - PhModalPropertySheet(&propSheetHeader); - } + SetupShowInstallDialog(); break; case SETUP_COMMAND_UNINSTALL: + SetupShowUninstallDialog(); break; case SETUP_COMMAND_UPDATE: + case SETUP_COMMAND_SILENTINSTALL: SetupShowUpdateDialog(); break; case SETUP_COMMAND_REPAIR: break; } - return 0; -} \ No newline at end of file + return EXIT_SUCCESS; +} diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.h b/tools/CustomSetupTool/CustomSetupTool/resource.h index a16b725cff7e..b27ddfceb9ec 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.h +++ b/tools/CustomSetupTool/CustomSetupTool/resource.h @@ -3,14 +3,15 @@ // Used by resource.rc // #define IDD_DIALOG1 101 -#define IDD_DIALOG2 103 -#define IDD_DIALOG4 105 -#define IDD_DIALOG3 108 -#define IDD_ERROR 109 -#define IDI_ICON1 143 -#define IDB_PNG1 144 -#define IDR_BIN_DATA 150 -#define IDR_LICENCE_DATA 155 +#define IDD_DIALOG2 102 +#define IDD_DIALOG4 103 +#define IDD_DIALOG3 104 +#define IDD_ERROR 105 +#define IDI_ICON1 106 +#define IDD_DIALOG5 106 +#define IDB_PNG1 107 +#define IDR_LICENCE_DATA 108 +#define IDR_BIN_DATA 111 #define IDC_MAINHEADER 1001 #define IDC_INSTALL_PROGRESS 1005 #define IDC_SUBHEADER 1047 @@ -28,25 +29,23 @@ #define IDC_INSTALL_STATUS 1064 #define IDC_INSTALL_DIRECTORY 1065 #define IDC_INSTALL_SUBSTATUS 1065 -#define IDC_RESET_CHECK 1066 -#define IDC_DEVBUILDS_CHECK 1066 -#define IDC_RESET_CHECK2 1067 +#define IDC_LATEST_NIGHTLY_CHECK 1066 +#define IDC_RESET_CHECK 1067 #define IDC_SHORTCUT_CHECK 1068 #define IDC_STATIC1 1069 #define IDC_STATIC2 1070 #define IDC_STATIC3 1071 #define IDC_SYSLINK1 1076 #define IDC_SYSLINK2 1077 -#define IDC_BUTTON1 1079 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 156 +#define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40002 -#define _APS_NEXT_CONTROL_VALUE 1080 -#define _APS_NEXT_SYMED_VALUE 101 +#define _APS_NEXT_CONTROL_VALUE 1078 +#define _APS_NEXT_SYMED_VALUE 112 #endif #endif diff --git a/tools/CustomSetupTool/CustomSetupTool/resource.rc b/tools/CustomSetupTool/CustomSetupTool/resource.rc index 3a349180c3a3..0989de87920b 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resource.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resource.rc @@ -8,15 +8,53 @@ // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" +#include "../../../../ProcessHacker/include/phappres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// English resources +// English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "#include ""../../../../ProcessHacker/include/phappres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS #pragma code_page(1252) ///////////////////////////////////////////////////////////////////////////// @@ -32,8 +70,8 @@ BEGIN CONTROL "Process Hacker - Setup",IDC_MAINHEADER,"Static",SS_SIMPLE | WS_GROUP,15,15,209,18 LTEXT "A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware.",IDC_SUBHEADER,15,46,211,36 CONTROL "",IDC_PROJECT_ICON,"Static",SS_BLACKFRAME,271,15,92,75 - CONTROL "View Changelog",IDC_SYSLINK1,"SysLink",WS_TABSTOP,15,120,110,14 - CONTROL "Project Website",IDC_SYSLINK2,"SysLink",WS_TABSTOP,15,102,110,14 + CONTROL "View Changelog",IDC_SYSLINK1,"SysLink",NOT WS_VISIBLE | WS_TABSTOP,15,120,110,14 + CONTROL "Project Website",IDC_SYSLINK2,"SysLink",NOT WS_VISIBLE | WS_TABSTOP,15,102,110,14 END IDD_DIALOG2 DIALOGEX 0, 0, 400, 225 @@ -41,7 +79,8 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_NOFAILCREATE | WS_POPUP | WS_CAPTION | WS_ EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg 2", 400, 0, 0x1 BEGIN - EDITTEXT IDC_EDIT1,0,0,400,225,ES_MULTILINE | ES_READONLY | NOT WS_BORDER | WS_VSCROLL + EDITTEXT IDC_EDIT1,13,39,387,186,ES_MULTILINE | ES_READONLY | NOT WS_BORDER | WS_VSCROLL + CONTROL "License Agreement",IDC_MAINHEADER,"Static",SS_SIMPLE | WS_GROUP,15,15,209,18 END IDD_DIALOG3 DIALOGEX 0, 0, 400, 225 @@ -49,31 +88,31 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_NOFAILCREATE | WS_POPUP | WS_CAPTION | WS_ EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg 2", 400, 0, 0x1 BEGIN - CONTROL "Create a desktop shortcut",IDC_SHORTCUT_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,90,168,10 - CONTROL "Launch on system startup",IDC_STARTUP_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,200,90,173,10 + CONTROL "Create a desktop shortcut",IDC_SHORTCUT_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,101,168,10 + CONTROL "Launch on system startup",IDC_STARTUP_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,200,101,173,10 CONTROL "Set as the default Windows Task Manager",IDC_TASKMANAGER_CHECK, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,120,345,10 - EDITTEXT IDC_INSTALL_DIRECTORY,20,48,307,14,ES_AUTOHSCROLL | ES_READONLY - PUSHBUTTON "Browse",IDC_FOLDER_BROWSE,330,48,50,14 - CONTROL "Setup Options",IDC_MAINHEADER,"Static",SS_SIMPLE | WS_GROUP,10,7,142,24 - GROUPBOX "Options",IDC_STATIC2,10,74,380,62 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,131,345,10 + EDITTEXT IDC_INSTALL_DIRECTORY,20,59,307,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Browse",IDC_FOLDER_BROWSE,330,59,50,14 + GROUPBOX "Options",IDC_STATIC2,10,85,380,62 CONTROL "Associate file extensions with PE Viewer",IDC_PEVIEW_CHECK, - "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,20,150,168,10 + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,20,161,168,10 CONTROL "Create a desktop shortcut for all users",IDC_SHORTCUT_ALL_CHECK, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,168,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,116,168,10 CONTROL "Launch minimized on system tray",IDC_STARTMIN_CHECK, - "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,200,105,173,10 + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,200,116,173,10 CONTROL "Install Debugging Tools for Windows",IDC_DBGTOOLS_CHECK, - "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,20,165,168,10 + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,20,176,168,10 CONTROL "Install KProcessHacker as a service",IDC_KPH_CHECK, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,179,168,10 - CONTROL "Install latest nightly build (not recommended)",IDC_RESET_CHECK, - "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,201,150,173,10 - GROUPBOX "Installation Folder",IDC_STATIC1,10,32,380,40 - CONTROL "Reset Process Hacker settings",IDC_RESET_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,165,173,10 - GROUPBOX "Additional Options",IDC_STATIC3,10,137,380,59 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,190,168,10 + CONTROL "Install latest nightly build (not recommended)",IDC_LATEST_NIGHTLY_CHECK, + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,201,161,173,10 + GROUPBOX "Installation Folder",IDC_STATIC1,10,43,380,40 + CONTROL "Reset Process Hacker settings",IDC_RESET_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,176,173,10 + GROUPBOX "Additional Options",IDC_STATIC3,10,148,380,59 CONTROL "Start Process Hacker after setup exits",IDC_PHSTART_CHECK, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,179,138,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,190,138,10 + CONTROL "Setup Options",IDC_MAINHEADER,"Static",SS_SIMPLE | WS_GROUP,15,15,209,18 END IDD_DIALOG4 DIALOGEX 0, 0, 400, 225 @@ -81,7 +120,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_NOFAILCREATE | WS_POPUP | WS_CAPTION | WS_ EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg 2", 400, 0, 0x1 BEGIN - LTEXT "Preparing installation...",IDC_INSTALL_STATUS,15,147,243,13 + LTEXT "Preparing installation...",IDC_INSTALL_STATUS,15,147,380,13 CONTROL "",IDC_INSTALL_PROGRESS,"msctls_progress32",PBS_SMOOTH,15,164,370,11,WS_EX_CLIENTEDGE CONTROL "",IDC_PROJECT_ICON,"Static",SS_BLACKFRAME,271,15,92,75 LTEXT "Preparing installation...",IDC_MAINHEADER,15,26,259,24 @@ -94,9 +133,21 @@ EXSTYLE WS_EX_CONTROLPARENT FONT 8, "MS Shell Dlg 2", 400, 0, 0x1 BEGIN CONTROL "",IDC_PROJECT_ICON,"Static",SS_BLACKFRAME,271,15,92,75 - CONTROL "Setup failed to install Process Hacker.",IDC_MAINHEADER, - "Static",SS_SIMPLE | WS_GROUP,22,26,267,24 - LTEXT "Click Retry to try installing Process Hacker again or click Close to exit.",IDC_SUBHEADER,22,51,245,21 + CONTROL "Setup failed to install Process Hacker :(",IDC_MAINHEADER, + "Static",SS_SIMPLE | WS_GROUP,15,26,267,24 + LTEXT "Select Retry to try installing Process Hacker again or Cancel to exit.",IDC_SUBHEADER,15,51,245,34 +END + +IDD_DIALOG5 DIALOGEX 0, 0, 400, 225 +STYLE DS_SETFONT | DS_MODALFRAME | DS_NOFAILCREATE | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg 2", 400, 0, 0x1 +BEGIN + LTEXT "Preparing installation...",IDC_INSTALL_STATUS,15,147,380,13 + CONTROL "",IDC_INSTALL_PROGRESS,"msctls_progress32",PBS_SMOOTH,15,164,370,11,WS_EX_CLIENTEDGE + CONTROL "",IDC_PROJECT_ICON,"Static",SS_BLACKFRAME,271,15,92,75 + LTEXT "Preparing installation...",IDC_MAINHEADER,15,26,259,24 + LTEXT "Preparing installation...",IDC_INSTALL_SUBSTATUS,15,184,370,32 END @@ -138,78 +189,21 @@ BEGIN IDD_ERROR, DIALOG BEGIN - LEFTMARGIN, 22 - RIGHTMARGIN, 378 - TOPMARGIN, 10 + LEFTMARGIN, 15 + RIGHTMARGIN, 385 + TOPMARGIN, 7 BOTTOMMARGIN, 216 END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_DIALOG1 AFX_DIALOG_LAYOUT -BEGIN - 0 -END -IDD_DIALOG2 AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DIALOG4 AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DIALOG3 AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000904b0" - BEGIN - VALUE "CompanyName", "Process Hacker" - VALUE "FileDescription", "Process Hacker Setup" - VALUE "FileVersion", "1.0" - VALUE "InternalName", "Process Hacker Setup" - VALUE "LegalCopyright", "(C) Process Hacker Team. Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "ProcessHackerSetup.exe" - VALUE "ProductName", "Process Hacker Setup" - VALUE "ProductVersion", "1.0" - END - END - BLOCK "VarFileInfo" + IDD_DIALOG5, DIALOG BEGIN - VALUE "Translation", 0x9, 1200 + LEFTMARGIN, 15 + RIGHTMARGIN, 385 + TOPMARGIN, 7 + BOTTOMMARGIN, 216 END END +#endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -229,61 +223,13 @@ IDI_ICON1 ICON "resources\\ProcessHacker.ico" IDB_PNG1 PNG "resources\\ProcessHacker.png" -#endif // English resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#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 - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) ///////////////////////////////////////////////////////////////////////////// // // RCDATA // -IDR_BIN_DATA RCDATA "..\\..\\..\\ClientBin\\processhacker-build-bin.zip" - -IDR_LICENCE_DATA RCDATA "resources\\Licence.txt" +IDR_LICENCE_DATA RCDATA "resources\\LICENSE.txt" #endif // English (Australia) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/tools/CustomSetupTool/CustomSetupTool/resources/Licence.txt b/tools/CustomSetupTool/CustomSetupTool/resources/LICENSE.txt similarity index 67% rename from tools/CustomSetupTool/CustomSetupTool/resources/Licence.txt rename to tools/CustomSetupTool/CustomSetupTool/resources/LICENSE.txt index 4a64d83271e1..af63d5321326 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resources/Licence.txt +++ b/tools/CustomSetupTool/CustomSetupTool/resources/LICENSE.txt @@ -2,7 +2,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 -Copyright � 2007 Free Software Foundation, Inc. +Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -31,36 +31,39 @@ The precise terms and conditions for copying, distribution and modification foll TERMS AND CONDITIONS 0. Definitions. -�This License� refers to version 3 of the GNU General Public License. -�Copyright� also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. +“This License” refers to version 3 of the GNU General Public License. -�The Program� refers to any copyrightable work licensed under this License. Each licensee is addressed as �you�. �Licensees� and �recipients� may be individuals or organizations. +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. -To �modify� a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a �modified version� of the earlier work or a work �based on� the earlier work. +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. -A �covered work� means either the unmodified Program or a work based on the Program. +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. -To �propagate� a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. +A “covered work” means either the unmodified Program or a work based on the Program. -To �convey� a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. -An interactive user interface displays �Appropriate Legal Notices� to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. -The �source code� for a work means the preferred form of the work for making modifications to it. �Object code� means any non-source form of a work. -A �Standard Interface� means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. -The �System Libraries� of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A �Major Component�, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. -The �Corresponding Source� for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. + All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. @@ -68,25 +71,29 @@ You may make, run and propagate covered works that you do not convey, without co Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. + You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. + You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. -b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to �keep intact all notices�. +b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. -A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an �aggregate� if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. + You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. @@ -96,9 +103,9 @@ d) Convey the object code by offering access from a designated place (gratis or e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. -A �User Product� is either (1) a �consumer product�, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, �normally used� refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. -�Installation Information� for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). @@ -107,7 +114,8 @@ The requirement to provide Installation Information does not include a requireme Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. -�Additional permissions� are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. @@ -119,13 +127,14 @@ c) Prohibiting misrepresentation of the origin of that material, or requiring th d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. -All other non-permissive additional terms are considered �further restrictions� within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. + You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. @@ -135,54 +144,63 @@ Moreover, your license from a particular copyright holder is reinstated permanen Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. + You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. + Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. -An �entity transaction� is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. -A �contributor� is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's �contributor version�. -A contributor's �essential patent claims� are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, �control� includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. -In the following three paragraphs, a �patent license� is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To �grant� such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. -If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. �Knowingly relying� means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. -A patent license is �discriminatory� if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. + If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. + Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. + The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License �or any later version� applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM �AS IS� WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. + If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS @@ -191,7 +209,7 @@ How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the �copyright� line and a pointer to where the full notice is found. +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) @@ -216,19 +234,20 @@ If the program does terminal interaction, make it output a short notice like thi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. -The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an �about box�. +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. -You should also get your employer (if you work as a programmer) or school, if any, to sign a �copyright disclaimer� for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . -Process Hacker is distributed under the GNU GPL version 3, -with the following exception: +Process Hacker is distributed under the GNU GPL version 3, with the +following exception: + + Permission is granted to dynamically (but not statically) link this + program with independent modules, regardless of the license terms of + these independent modules, provided that this program is not modified + in any way. An independent module is a module which is not derived + from or based on this program. If you modify this program, this + additional permission no longer applies unless authorized by the + copyright holders. -Permission is granted to dynamically (but not statically) link this -program with independent modules, regardless of the license terms of -these independent modules, provided that this program is not modified -in any way. An independent module is a module which is not derived -from or based on this program. If you modify this program, this -additional permission no longer applies unless authorized by the -copyright holders. \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/resources/app.manifest b/tools/CustomSetupTool/CustomSetupTool/resources/app.manifest index 29e66e8a95a7..42ab6aaaa71e 100644 --- a/tools/CustomSetupTool/CustomSetupTool/resources/app.manifest +++ b/tools/CustomSetupTool/CustomSetupTool/resources/app.manifest @@ -10,7 +10,7 @@ - + diff --git a/plugins/SbieSupport/SbieSupport.rc b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc similarity index 55% rename from plugins/SbieSupport/SbieSupport.rc rename to tools/CustomSetupTool/CustomSetupTool/resources/version.rc index e2d61f7fcf25..73ed715bc03e 100644 --- a/plugins/SbieSupport/SbieSupport.rc +++ b/tools/CustomSetupTool/CustomSetupTool/resources/version.rc @@ -1,146 +1,131 @@ -// 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 - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "Sandboxie Support for Process Hacker" - VALUE "FileVersion", "1.0" - VALUE "InternalName", "ProcessHacker.SbieSupport" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "SbieSupport.dll" - VALUE "ProductName", "Sandboxie Support for Process Hacker" - VALUE "ProductVersion", "1.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_OPTIONS DIALOGEX 0, 0, 268, 50 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "SbieDll.dll path:",IDC_STATIC,7,9,50,8 - EDITTEXT IDC_SBIEDLLPATH,63,8,143,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",IDC_BROWSE,211,7,50,14 - PUSHBUTTON "OK",IDOK,158,29,50,14 - PUSHBUTTON "Cancel",IDCANCEL,211,29,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_OPTIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 261 - TOPMARGIN, 7 - BOTTOMMARGIN, 43 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_OPTIONS AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "../resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +#if defined(PH_BUILD_API) +#include "../../../../ProcessHacker/include/phappres.h" +#else +#define PHAPP_VERSION_NUMBER 1,0,0,0 +#define PHAPP_VERSION_STRING "1,0,0,0" +#endif + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "../resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" +#if defined(PH_BUILD_API) + "#include ""../../../../ProcessHacker/include/phappres.h""\r\n" +#endif + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PHAPP_VERSION_NUMBER + PRODUCTVERSION PHAPP_VERSION_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000904b0" + BEGIN + VALUE "CompanyName", "Process Hacker" + VALUE "FileDescription", "Process Hacker Setup" + VALUE "FileVersion", PHAPP_VERSION_STRING + VALUE "InternalName", "Process Hacker Setup" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3. Copyright (C) 2017" + VALUE "OriginalFilename", "ProcessHackerSetup.exe" + VALUE "ProductName", "Process Hacker Setup" + VALUE "ProductVersion", PHAPP_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// RCDATA +// +#if defined(PH_BUILD_API) + +IDR_BIN_DATA RCDATA "../../../../build/output/processhacker-build-bin.64" + +#endif + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/tools/CustomSetupTool/CustomSetupTool/setup.c b/tools/CustomSetupTool/CustomSetupTool/setup.c index f73deb19f54e..a1ba529b34d1 100644 --- a/tools/CustomSetupTool/CustomSetupTool/setup.c +++ b/tools/CustomSetupTool/CustomSetupTool/setup.c @@ -2,7 +2,7 @@ * Process Hacker Toolchain - * project setup * - * Copyright (C) 2017 dmex + * Copyright (C) 2017-2018 dmex * * This file is part of Process Hacker. * @@ -21,13 +21,17 @@ */ #include -#include +#include #include PH_STRINGREF UninstallKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ProcessHacker"); +PH_STRINGREF CurrentUserRunKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"); +PH_STRINGREF PhImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); +PH_STRINGREF PhImageOptionsWow64KeyName = PH_STRINGREF_INIT(L"Software\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\ProcessHacker.exe"); +PH_STRINGREF TmImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\taskmgr.exe"); NTSTATUS SetupCreateUninstallKey( - VOID + _In_ PPH_SETUP_CONTEXT Context ) { NTSTATUS status; @@ -38,7 +42,7 @@ NTSTATUS SetupCreateUninstallKey( status = PhCreateKey( &keyHandle, - KEY_ALL_ACCESS, + KEY_ALL_ACCESS | KEY_WOW64_64KEY, PH_KEY_LOCAL_MACHINE, &UninstallKeyName, 0, @@ -50,7 +54,7 @@ NTSTATUS SetupCreateUninstallKey( { PPH_STRING tempString; - tempString = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker.exe,0"); + tempString = SetupCreateFullPath(SetupInstallPath, L"\\processhacker.exe,0"); PhStringRefToUnicodeString(&tempString->sr, &value); RtlInitUnicodeString(&name, L"DisplayIcon"); PhStringRefToUnicodeString(&tempString->sr, &value); @@ -97,159 +101,420 @@ NTSTATUS SetupCreateUninstallKey( return status; } -VOID SetupFindInstallDirectory(VOID) +NTSTATUS SetupDeleteUninstallKey( + VOID + ) +{ + NTSTATUS status; + HANDLE keyHandle; + + status = PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE | KEY_WOW64_64KEY, + PH_KEY_LOCAL_MACHINE, + &UninstallKeyName, + 0 + ); + + if (NT_SUCCESS(status)) + { + status = NtDeleteKey(keyHandle); + NtClose(keyHandle); + } + + return status; +} + +PPH_STRING SetupFindInstallDirectory( + VOID + ) { // Find the current installation path. - SetupInstallPath = GetProcessHackerInstallPath(); + PPH_STRING setupInstallPath = GetProcessHackerInstallPath(); // Check if the string is valid. - if (PhIsNullOrEmptyString(SetupInstallPath)) + if (PhIsNullOrEmptyString(setupInstallPath)) { - PH_STRINGREF programW6432 = PH_STRINGREF_INIT(L"%ProgramW6432%"); - PH_STRINGREF programFiles = PH_STRINGREF_INIT(L"%ProgramFiles%"); - PH_STRINGREF defaultDirectoryName = PH_STRINGREF_INIT(L"\\Process Hacker\\"); + static PH_STRINGREF programW6432 = PH_STRINGREF_INIT(L"%ProgramW6432%\\Process Hacker\\"); + static PH_STRINGREF programFiles = PH_STRINGREF_INIT(L"%ProgramFiles%\\Process Hacker\\"); SYSTEM_INFO info; - PPH_STRING expandedString; GetNativeSystemInfo(&info); if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - { - if (expandedString = PH_AUTO(PhExpandEnvironmentStrings(&programW6432))) - { - SetupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); - } - } + setupInstallPath = PhExpandEnvironmentStrings(&programW6432); else - { - if (expandedString = PH_AUTO(PhExpandEnvironmentStrings(&programFiles))) - { - SetupInstallPath = PhConcatStringRef2(&expandedString->sr, &defaultDirectoryName); - } - } + setupInstallPath = PhExpandEnvironmentStrings(&programFiles); } - if (PhIsNullOrEmptyString(SetupInstallPath)) + if (PhIsNullOrEmptyString(setupInstallPath)) { - SetupInstallPath = PhCreateString(L"C:\\Program Files\\Process Hacker\\"); + setupInstallPath = PhCreateString(L"C:\\Program Files\\Process Hacker\\"); } - // Remove extra backslashes - PathRemoveBackslash(PhGetString(SetupInstallPath)); + return setupInstallPath; } -VOID SetupCreateUninstallFile( - VOID +BOOLEAN SetupCreateUninstallFile( + _In_ PPH_SETUP_CONTEXT Context ) { + NTSTATUS status; + PPH_STRING currentFilePath; PPH_STRING backupFilePath; PPH_STRING uninstallFilePath; + if (!NT_SUCCESS(status = PhGetProcessImageFileNameWin32(NtCurrentProcess(), ¤tFilePath))) + { + Context->ErrorCode = WIN32_FROM_NTSTATUS(status); + return FALSE; + } + + // Check if the user has started the setup from the installation folder. + if (PhStartsWithStringRef2(¤tFilePath->sr, PhGetString(SetupInstallPath), TRUE)) + { + PhDereferenceObject(currentFilePath); + return TRUE; + } + backupFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.bak"); uninstallFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.exe"); if (RtlDoesFileExists_U(backupFilePath->Buffer)) + { + PPH_STRING tempFileName; + PPH_STRING tempFilePath; + + tempFileName = PhCreateString(L"processhacker-setup.bak"); + tempFilePath = PhCreateCacheFile(tempFileName); + + //if (!NT_SUCCESS(PhDeleteFileWin32(backupFilePath->Buffer))) + if (!MoveFile(backupFilePath->Buffer, tempFilePath->Buffer)) + { + Context->ErrorCode = GetLastError(); + return FALSE; + } + + PhDereferenceObject(tempFilePath); + PhDereferenceObject(tempFileName); + } + + if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) { - // Move to temp directory - //MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer); + PPH_STRING tempFileName; + PPH_STRING tempFilePath; + + //if (!NT_SUCCESS(PhDeleteFileWin32(uninstallFilePath->Buffer))) + tempFileName = PhCreateString(L"processhacker-setup.exe"); + tempFilePath = PhCreateCacheFile(tempFileName); + + if (!MoveFile(uninstallFilePath->Buffer, tempFilePath->Buffer)) + { + Context->ErrorCode = GetLastError(); + return FALSE; + } + + PhDereferenceObject(tempFilePath); + PhDereferenceObject(tempFileName); } + if (!CopyFile(currentFilePath->Buffer, uninstallFilePath->Buffer, TRUE)) + { + Context->ErrorCode = GetLastError(); + return FALSE; + } + + PhDereferenceObject(uninstallFilePath); + PhDereferenceObject(currentFilePath); + + return TRUE; +} + +VOID SetupDeleteUninstallFile( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + PPH_STRING uninstallFilePath; + + uninstallFilePath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\processhacker-setup.exe"); + if (RtlDoesFileExists_U(uninstallFilePath->Buffer)) { - MoveFile(uninstallFilePath->Buffer, backupFilePath->Buffer); + PPH_STRING tempFileName; + PPH_STRING tempFilePath; + + tempFileName = PhCreateString(L"processhacker-setup.exe"); + tempFilePath = PhCreateCacheFile(tempFileName); + + if (PhIsNullOrEmptyString(tempFilePath)) + { + PhDereferenceObject(tempFileName); + goto CleanupExit; + } + + MoveFile(uninstallFilePath->Buffer, tempFilePath->Buffer); + + PhDereferenceObject(tempFilePath); + PhDereferenceObject(tempFileName); } - CopyFile(NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer, uninstallFilePath->Buffer, FALSE); +CleanupExit: PhDereferenceObject(uninstallFilePath); } -VOID SetupInstallKph( - VOID +VOID SetupStartService( + _In_ PWSTR ServiceName ) { - PPH_STRING clientPath = PhFormatString(L"%s\\ProcessHacker.exe", PhGetString(SetupInstallPath)); + SC_HANDLE serviceHandle; - if (RtlDoesFileExists_U(PhGetString(clientPath))) + serviceHandle = PhOpenService( + ServiceName, + SERVICE_QUERY_STATUS | SERVICE_START + ); + + if (serviceHandle) { - HANDLE processHandle; - LARGE_INTEGER timeout; + ULONG statusLength = 0; + SERVICE_STATUS_PROCESS status; - timeout.QuadPart = -10 * PH_TIMEOUT_SEC; + memset(&status, 0, sizeof(SERVICE_STATUS_PROCESS)); - if (PhShellExecuteEx( - NULL, - PhGetString(clientPath), - L"-installkph", - SW_NORMAL, - 0, - 0, - &processHandle + if (QueryServiceStatusEx( + serviceHandle, + SC_STATUS_PROCESS_INFO, + (PBYTE)&status, + sizeof(SERVICE_STATUS_PROCESS), + &statusLength )) { - NtWaitForSingleObject(processHandle, FALSE, &timeout); + if (status.dwCurrentState != SERVICE_RUNNING) + { + ULONG attempts = 10; + + do + { + StartService(serviceHandle, 0, NULL); + + if (QueryServiceStatusEx( + serviceHandle, + SC_STATUS_PROCESS_INFO, + (PBYTE)&status, + sizeof(SERVICE_STATUS_PROCESS), + &statusLength + )) + { + if (status.dwCurrentState == SERVICE_RUNNING) + { + break; + } + } + + PhDelayExecution(1000); + + } while (--attempts != 0); + } } - SC_HANDLE serviceHandle; - BOOLEAN success = FALSE; + CloseServiceHandle(serviceHandle); + } +} - serviceHandle = PhOpenService(L"KProcessHacker3", SERVICE_START); +VOID SetupStopService( + _In_ PWSTR ServiceName, + _In_ BOOLEAN RemoveService + ) +{ + SC_HANDLE serviceHandle; - if (serviceHandle) + if (serviceHandle = PhOpenService( + ServiceName, + SERVICE_QUERY_STATUS | SERVICE_STOP + )) + { + ULONG statusLength = 0; + SERVICE_STATUS_PROCESS status; + + memset(&status, 0, sizeof(SERVICE_STATUS_PROCESS)); + + if (QueryServiceStatusEx( + serviceHandle, + SC_STATUS_PROCESS_INFO, + (PBYTE)&status, + sizeof(SERVICE_STATUS_PROCESS), + &statusLength + )) { - if (StartService(serviceHandle, 0, NULL)) - success = TRUE; + if (status.dwCurrentState != SERVICE_STOPPED) + { + ULONG attempts = 60; - CloseServiceHandle(serviceHandle); + do + { + SERVICE_STATUS serviceStatus; + + memset(&serviceStatus, 0, sizeof(SERVICE_STATUS)); + + ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + + if (QueryServiceStatusEx( + serviceHandle, + SC_STATUS_PROCESS_INFO, + (PBYTE)&status, + sizeof(SERVICE_STATUS_PROCESS), + &statusLength + )) + { + if (status.dwCurrentState == SERVICE_STOPPED) + { + break; + } + } + + PhDelayExecution(1000); + + } while (--attempts != 0); + } } + + CloseServiceHandle(serviceHandle); } - PhDereferenceObject(clientPath); + if (RemoveService) + { + if (serviceHandle = PhOpenService( + ServiceName, + DELETE + )) + { + DeleteService(serviceHandle); + CloseServiceHandle(serviceHandle); + } + } } -ULONG SetupUninstallKph(VOID) +BOOLEAN SetupKphCheckInstallState( + VOID + ) { - ULONG status = ERROR_SUCCESS; - SC_HANDLE scmHandle; - SC_HANDLE serviceHandle; - SERVICE_STATUS serviceStatus; + static PH_STRINGREF kph3ServiceKeyName = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Services\\KProcessHacker3"); + BOOLEAN kphInstallRequired = FALSE; + HANDLE runKeyHandle; - if (!(scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT))) - return GetLastError(); + if (NT_SUCCESS(PhOpenKey( + &runKeyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &kph3ServiceKeyName, + 0 + ))) + { + // Make sure we re-install the driver when KPH was installed as a service. + if (PhQueryRegistryUlong(runKeyHandle, L"Start") == SERVICE_SYSTEM_START) + { + kphInstallRequired = TRUE; + } + + NtClose(runKeyHandle); + } + + return kphInstallRequired; +} - if (serviceHandle = OpenService(scmHandle, L"KProcessHacker3", SERVICE_STOP | DELETE)) +VOID SetupStartKph( + _In_ PPH_SETUP_CONTEXT Context, + _In_ BOOLEAN ForceInstall + ) +{ + if (ForceInstall || Context->SetupKphInstallRequired) { - ControlService(serviceHandle, SERVICE_CONTROL_STOP, &serviceStatus); + PPH_STRING clientPath; - if (!DeleteService(serviceHandle)) - status = GetLastError(); + clientPath = SetupCreateFullPath(SetupInstallPath, L"\\ProcessHacker.exe"); - CloseServiceHandle(serviceHandle); + if (RtlDoesFileExists_U(PhGetString(clientPath))) + { + HANDLE processHandle; + + if (PhShellExecuteEx( + NULL, + PhGetString(clientPath), + L"-installkph -s", + SW_NORMAL, + PhGetOwnTokenAttributes().Elevated ? 0 : PH_SHELL_EXECUTE_ADMIN, + 0, + &processHandle + )) + { + NtWaitForSingleObject(processHandle, FALSE, NULL); + NtClose(processHandle); + } + } + + PhDereferenceObject(clientPath); + } + + SetupStartService(L"KProcessHacker3"); +} + +BOOLEAN SetupUninstallKph( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + PPH_STRING clientPath; + + // Query the current KPH installation state. + Context->SetupKphInstallRequired = SetupKphCheckInstallState(); + + // Stop and uninstall the current installation. + clientPath = SetupCreateFullPath(SetupInstallPath, L"\\ProcessHacker.exe"); + + if (RtlDoesFileExists_U(PhGetString(clientPath))) + { + HANDLE processHandle; + + if (PhShellExecuteEx( + NULL, + PhGetString(clientPath), + L"-uninstallkph -s", + SW_NORMAL, + 0, + 0, + &processHandle + )) + { + NtWaitForSingleObject(processHandle, FALSE, NULL); + NtClose(processHandle); + } } else { - status = GetLastError(); + SetupStopService(L"KProcessHacker3", TRUE); } - CloseServiceHandle(scmHandle); - - return status; + return TRUE; } VOID SetupSetWindowsOptions( - VOID + _In_ PPH_SETUP_CONTEXT Context ) { - static PH_STRINGREF TaskMgrImageOptionsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\taskmgr.exe"); - static PH_STRINGREF CurrentUserRunKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"); + static PH_STRINGREF desktopStartmenuPathSr = PH_STRINGREF_INIT(L"%ALLUSERSPROFILE%\\Microsoft\\Windows\\Start Menu\\Programs\\Process Hacker.lnk"); + static PH_STRINGREF peviewerShortcutPathSr = PH_STRINGREF_INIT(L"%ALLUSERSPROFILE%\\Microsoft\\Windows\\Start Menu\\Programs\\PE Viewer.lnk"); + static PH_STRINGREF userShortcutPathSr = PH_STRINGREF_INIT(L"%USERPROFILE%\\Desktop\\Process Hacker.lnk"); + static PH_STRINGREF desktopAllusersPathSr = PH_STRINGREF_INIT(L"%PUBLIC%\\Desktop\\Process Hacker.lnk"); PPH_STRING clientPathString; PPH_STRING startmenuFolderString; - clientPathString = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\ProcessHacker.exe"); + clientPathString = SetupCreateFullPath(SetupInstallPath, L"\\ProcessHacker.exe"); - if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) + // Create the startmenu shortcut. + // PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk")) + if (startmenuFolderString = PhExpandEnvironmentStrings(&desktopStartmenuPathSr)) { SetupCreateLink( + L"ProcessHacker.Desktop.App", PhGetString(startmenuFolderString), PhGetString(clientPathString), PhGetString(SetupInstallPath) @@ -257,11 +522,48 @@ VOID SetupSetWindowsOptions( PhDereferenceObject(startmenuFolderString); } - if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk")) + // Create the all users shortcut. + if (Context->SetupCreateDesktopShortcutAllUsers) + { + // PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&desktopAllusersPathSr)) + { + SetupCreateLink( + L"ProcessHacker.Desktop.App", + PhGetString(startmenuFolderString), + PhGetString(clientPathString), + PhGetString(SetupInstallPath) + ); + PhDereferenceObject(startmenuFolderString); + } + } + else + { + // Create the desktop shortcut. + if (Context->SetupCreateDesktopShortcut) + { + // PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&userShortcutPathSr)) + { + SetupCreateLink( + L"ProcessHacker.Desktop.App", + PhGetString(startmenuFolderString), + PhGetString(clientPathString), + PhGetString(SetupInstallPath) + ); + PhDereferenceObject(startmenuFolderString); + } + } + } + + // Create the PE Viewer startmenu shortcut. + // PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&peviewerShortcutPathSr)) { PPH_STRING peviewPathString = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\peview.exe"); SetupCreateLink( + L"PeViewer.Desktop.App", PhGetString(startmenuFolderString), PhGetString(peviewPathString), PhGetString(SetupInstallPath) @@ -271,43 +573,74 @@ VOID SetupSetWindowsOptions( PhDereferenceObject(startmenuFolderString); } - if (SetupResetSettings) + // Reset the settings file. + if (Context->SetupResetSettings) { - PPH_STRING settingsFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker 2\\settings.xml"); + static PH_STRINGREF settingsPath = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker\\settings.xml"); + PPH_STRING settingsFilePath; + + if (settingsFilePath = PhExpandEnvironmentStrings(&settingsPath)) + { + if (RtlDoesFileExists_U(settingsFilePath->Buffer)) + { + PhDeleteFileWin32(settingsFilePath->Buffer); + } - SetupDeleteDirectoryFile(settingsFileName->Buffer); - PhDereferenceObject(settingsFileName); + PhDereferenceObject(settingsFilePath); + } } - if (SetupCreateDefaultTaskManager) + // Set the Windows default Task Manager. + if (Context->SetupCreateDefaultTaskManager) { - HANDLE keyHandle; - - if (NT_SUCCESS(PhOpenKey( - &keyHandle, - KEY_WRITE, + NTSTATUS status; + HANDLE taskmgrKeyHandle; + + status = PhCreateKey( + &taskmgrKeyHandle, + KEY_READ | KEY_WRITE, PH_KEY_LOCAL_MACHINE, - &TaskMgrImageOptionsKeyName, - 0 - ))) + &TmImageOptionsKeyName, + OBJ_OPENIF, + 0, + NULL + ); + + if (NT_SUCCESS(status)) { PPH_STRING value; UNICODE_STRING valueName; - - value = PhConcatStrings(3, L"\"", PhGetString(clientPathString), L"\""); RtlInitUnicodeString(&valueName, L"Debugger"); - NtSetValueKey(keyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); - NtClose(keyHandle); + + value = PhConcatStrings(3, L"\"", PhGetString(clientPathString), L"\""); + status = NtSetValueKey( + taskmgrKeyHandle, + &valueName, + 0, + REG_SZ, + value->Buffer, + (ULONG)value->Length + sizeof(UNICODE_NULL) + ); + + PhDereferenceObject(value); + NtClose(taskmgrKeyHandle); + } + + if (!NT_SUCCESS(status)) + { + PhShowStatus(NULL, L"Unable to set the Windows default Task Manager.", status, 0); } } - if (SetupCreateSystemStartup) - { - HANDLE keyHandle; + // Create the run startup key. + if (Context->SetupCreateSystemStartup) + { + NTSTATUS status; + HANDLE runKeyHandle; - if (NT_SUCCESS(PhOpenKey( - &keyHandle, + if (NT_SUCCESS(status = PhOpenKey( + &runKeyHandle, KEY_WRITE, PH_KEY_CURRENT_USER, &CurrentUserRunKeyName, @@ -319,59 +652,122 @@ VOID SetupSetWindowsOptions( RtlInitUnicodeString(&valueName, L"Process Hacker"); - if (SetupCreateMinimizedSystemStartup) + if (Context->SetupCreateMinimizedSystemStartup) value = PhConcatStrings(3, L"\"", PhGetString(clientPathString), L"\" -hide"); else value = PhConcatStrings(3, L"\"", PhGetString(clientPathString), L"\""); - - NtSetValueKey(keyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); - NtClose(keyHandle); + + NtSetValueKey(runKeyHandle, &valueName, 0, REG_SZ, value->Buffer, (ULONG)value->Length + 2); + NtClose(runKeyHandle); + } + else + { + PhShowStatus(NULL, L"Unable to create the startup entry.", status, 0); } } +} + +VOID SetupDeleteWindowsOptions( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + static PH_STRINGREF desktopShortcutPathSr = PH_STRINGREF_INIT(L"%ALLUSERSPROFILE%\\Microsoft\\Windows\\Start Menu\\Programs\\Process Hacker.lnk"); + static PH_STRINGREF peviewerShortcutPathSr = PH_STRINGREF_INIT(L"%ALLUSERSPROFILE%\\Microsoft\\Windows\\Start Menu\\Programs\\PE Viewer.lnk"); + static PH_STRINGREF userShortcutPathSr = PH_STRINGREF_INIT(L"%USERPROFILE%\\Desktop\\Process Hacker.lnk"); + static PH_STRINGREF desktopAllusersPathSr = PH_STRINGREF_INIT(L"%PUBLIC%\\Desktop\\Process Hacker.lnk"); + PPH_STRING startmenuFolderString; + HANDLE keyHandle; - if (SetupCreateDesktopShortcut) + // PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\Process Hacker.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&desktopShortcutPathSr)) { - PPH_STRING desktopFolderString; + PhDeleteFileWin32(PhGetString(startmenuFolderString)); + PhDereferenceObject(startmenuFolderString); + } - if (desktopFolderString = PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")); - { - SetupCreateLink( - PhGetString(desktopFolderString), - PhGetString(clientPathString), - PhGetString(SetupInstallPath) - ); - PhDereferenceObject(desktopFolderString); - } + // PhGetKnownLocation(CSIDL_COMMON_PROGRAMS, L"\\PE Viewer.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&peviewerShortcutPathSr)) + { + PhDeleteFileWin32(PhGetString(startmenuFolderString)); + PhDereferenceObject(startmenuFolderString); } - else if (SetupCreateDesktopShortcutAllUsers) + + // PhGetKnownLocation(CSIDL_DESKTOPDIRECTORY, L"\\Process Hacker.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&userShortcutPathSr)) { - PPH_STRING startmenuFolderString; + PhDeleteFileWin32(startmenuFolderString->Buffer); + PhDereferenceObject(startmenuFolderString); + } - if (startmenuFolderString = PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk")) - { - SetupCreateLink( - PhGetString(startmenuFolderString), - PhGetString(clientPathString), - PhGetString(SetupInstallPath) - ); - PhDereferenceObject(startmenuFolderString); - } + // PhGetKnownLocation(CSIDL_COMMON_DESKTOPDIRECTORY, L"\\Process Hacker.lnk") + if (startmenuFolderString = PhExpandEnvironmentStrings(&desktopAllusersPathSr)) + { + PhDeleteFileWin32(startmenuFolderString->Buffer); + PhDereferenceObject(startmenuFolderString); + } + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &PhImageOptionsKeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); + } + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &PhImageOptionsWow64KeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); + } + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_LOCAL_MACHINE, + &TmImageOptionsKeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); + } + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_WRITE | DELETE, + PH_KEY_CURRENT_USER, + &CurrentUserRunKeyName, + 0 + ))) + { + NtDeleteKey(keyHandle); + NtClose(keyHandle); } } BOOLEAN SetupExecuteProcessHacker( - _In_ HWND Parent + _In_ PPH_SETUP_CONTEXT Context ) { BOOLEAN success = FALSE; PPH_STRING clientPath; - clientPath = PhConcatStrings2(PhGetString(SetupInstallPath), L"\\ProcessHacker.exe"); + clientPath = SetupCreateFullPath(SetupInstallPath, L"\\ProcessHacker.exe"); if (RtlDoesFileExists_U(clientPath->Buffer)) { success = PhShellExecuteEx( - Parent, + Context->DialogHandle, clientPath->Buffer, NULL, SW_SHOWDEFAULT, @@ -383,4 +779,98 @@ BOOLEAN SetupExecuteProcessHacker( PhDereferenceObject(clientPath); return success; -} \ No newline at end of file +} + +VOID SetupUpgradeSettingsFile( + VOID + ) +{ + static PH_STRINGREF settingsPath = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker\\settings.xml"); + static PH_STRINGREF settingsLegacyPath = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker 2\\settings.xml"); + PPH_STRING settingsFilePath; + PPH_STRING oldSettingsFileName; + + settingsFilePath = PhExpandEnvironmentStrings(&settingsPath); + oldSettingsFileName = PhExpandEnvironmentStrings(&settingsLegacyPath); + + if (settingsFilePath && oldSettingsFileName) + { + if (!RtlDoesFileExists_U(settingsFilePath->Buffer)) + { + CopyFile(oldSettingsFileName->Buffer, settingsFilePath->Buffer, FALSE); + } + } + + if (oldSettingsFileName) PhDereferenceObject(oldSettingsFileName); + if (settingsFilePath) PhDereferenceObject(settingsFilePath); +} + +VOID SetupCreateImageFileExecutionOptions( + VOID + ) +{ + HANDLE keyHandle; + SYSTEM_INFO info; + + if (WindowsVersion < WINDOWS_10) + return; + + GetNativeSystemInfo(&info); + + if (NT_SUCCESS(PhCreateKey( + &keyHandle, + KEY_WRITE, + PH_KEY_LOCAL_MACHINE, + &PhImageOptionsKeyName, + OBJ_OPENIF, + 0, + NULL + ))) + { + static UNICODE_STRING valueName = RTL_CONSTANT_STRING(L"MitigationOptions"); + + NtSetValueKey(keyHandle, &valueName, 0, REG_QWORD, &(ULONG64) + { + PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON + }, sizeof(ULONG64)); + + NtClose(keyHandle); + } + + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + { + if (NT_SUCCESS(PhCreateKey( + &keyHandle, + KEY_WRITE, + PH_KEY_LOCAL_MACHINE, + &PhImageOptionsWow64KeyName, + OBJ_OPENIF, + 0, + NULL + ))) + { + static UNICODE_STRING valueName = RTL_CONSTANT_STRING(L"MitigationOptions"); + + NtSetValueKey(keyHandle, &valueName, 0, REG_QWORD, &(ULONG64) + { + PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON + }, sizeof(ULONG64)); + + NtClose(keyHandle); + } + } +} diff --git a/tools/CustomSetupTool/CustomSetupTool/page1.c b/tools/CustomSetupTool/CustomSetupTool/startpage.c similarity index 53% rename from tools/CustomSetupTool/CustomSetupTool/page1.c rename to tools/CustomSetupTool/CustomSetupTool/startpage.c index 4ce65ad5c163..b767b75a384d 100644 --- a/tools/CustomSetupTool/CustomSetupTool/page1.c +++ b/tools/CustomSetupTool/CustomSetupTool/startpage.c @@ -2,8 +2,6 @@ * Process Hacker Toolchain - * project setup * - * Copyright (C) 2017 dmex - * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify @@ -21,29 +19,24 @@ */ #include +#include +#include -VOID SetupPropSheetParent( - _In_ HWND hwndDlg +VOID SetupPropSheetCenterWindow( + _In_ PPH_SETUP_CONTEXT Context ) { - HWND parent = GetParent(hwndDlg); - - // Center the property sheet on the desktop - PhCenterWindow(parent, NULL); - SetForegroundWindow(parent); - - // Set the fonts for the Property Sheet buttons - SetupInitializeFont(GetDlgItem(parent, IDC_PROPSHEET_BACK), -12, FW_NORMAL); - SetupInitializeFont(GetDlgItem(parent, IDC_PROPSHEET_NEXT), -12, FW_NORMAL); - SetupInitializeFont(GetDlgItem(parent, IDC_PROPSHEET_CANCEL), -12, FW_NORMAL); + // Center the PropertySheet on the desktop. + PhCenterWindow(Context->DialogHandle, NULL); + SetForegroundWindow(Context->DialogHandle); } -static VOID LoadSetupIcons( +static VOID SetupLoadIcons( _In_ HWND hwndDlg ) { HBITMAP smallIconHandle = (HBITMAP)LoadImage( - PhLibImageBase, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), @@ -51,7 +44,7 @@ static VOID LoadSetupIcons( LR_DEFAULTCOLOR ); HBITMAP largeIconHandle = (HBITMAP)LoadImage( - PhLibImageBase, + PhInstanceHandle, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), @@ -73,13 +66,30 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( _Inout_ LPARAM lParam ) { + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + switch (uMsg) { case WM_INITDIALOG: { - SetupPropSheetParent(hwndDlg); + context->WelcomePageHandle = hwndDlg; + + SetupPropSheetCenterWindow(context); - LoadSetupIcons(hwndDlg); + SetupLoadIcons(hwndDlg); SetupLoadImage(GetDlgItem(hwndDlg, IDC_PROJECT_ICON), MAKEINTRESOURCE(IDB_PNG1)); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -18, FW_SEMIBOLD); SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), 0, FW_NORMAL); @@ -96,29 +106,48 @@ INT_PTR CALLBACK SetupPropPage1_WndProc( switch (pageNotify->hdr.code) { + case PSN_WIZNEXT: + { + if (!PhGetOwnTokenAttributes().Elevated) + { + SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) }; + info.lpFile = NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer; + info.lpParameters = NtCurrentPeb()->ProcessParameters->CommandLine.Buffer; + info.lpVerb = L"runas"; + info.nShow = SW_SHOW; + info.hwnd = hwndDlg; + info.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI; + + if (ShellExecuteEx(&info)) + { + NtTerminateProcess(NtCurrentProcess(), STATUS_SUCCESS); + } + else + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)TRUE); + return TRUE; + } + } + } + break; case PSN_SETACTIVE: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - +//#ifdef _DEBUG +// PostMessage(context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG3); +//#endif // Reset the button state. - PropSheet_SetWizButtons(hwPropSheet, PSWIZB_NEXT); - - // Hide the back button. - //ShowWindow(GetDlgItem(hwPropSheet, IDC_PROPSHEET_BACK), SW_HIDE); + PropSheet_SetWizButtons(context->DialogHandle, PSWIZB_NEXT); - // HACK: Focus the next button (reset after changing button state). - PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwPropSheet, IDC_PROPSHEET_NEXT), TRUE); + if (!PhGetOwnTokenAttributes().Elevated) + { + Button_SetElevationRequiredState(GetDlgItem(context->DialogHandle, IDC_PROPSHEET_NEXT), TRUE); + } } break; case PSN_KILLACTIVE: { - HWND hwPropSheet = pageNotify->hdr.hwndFrom; - // Enable the back button. - PropSheet_SetWizButtons(hwPropSheet, PSWIZB_NEXT | PSWIZB_BACK); - - // Show the back button. - //ShowWindow(GetDlgItem(hwPropSheet, IDC_PROPSHEET_BACK), SW_SHOW); + PropSheet_SetWizButtons(context->DialogHandle, PSWIZB_NEXT | PSWIZB_BACK); } break; } diff --git a/tools/CustomSetupTool/CustomSetupTool/uninstall.c b/tools/CustomSetupTool/CustomSetupTool/uninstall.c new file mode 100644 index 000000000000..e2c672713fef --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/uninstall.c @@ -0,0 +1,320 @@ +/* + * Process Hacker Toolchain - + * project setup + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +#define WM_TASKDIALOGINIT (WM_APP + 550) +HWND UninstallDialogHandle = NULL; +HANDLE UninstallDialogThreadHandle = NULL; +PH_EVENT UninstallInitializedEvent = PH_EVENT_INIT; + +VOID ShowUninstallConfirmDialog( + _In_ PPH_SETUP_CONTEXT Context + ); +VOID ShowUninstallDialog( + _In_ PPH_SETUP_CONTEXT Context + ); +VOID ShowUninstallCompleteDialog( + _In_ PPH_SETUP_CONTEXT Context + ); +VOID ShowUninstallErrorDialog( + _In_ PPH_SETUP_CONTEXT Context + ); + +NTSTATUS SetupUninstallBuild( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + SetupInstallPath = SetupFindInstallDirectory(); + + // Stop Process Hacker. + if (!ShutdownProcessHacker()) + goto CleanupExit; + + // Stop the kernel driver(s). + if (!SetupUninstallKph(Context)) + goto CleanupExit; + + // Remove autorun and shortcuts. + SetupDeleteWindowsOptions(Context); + + // Remove the uninstaller. + SetupDeleteUninstallFile(Context); + + // Remove the ARP uninstall entry. + SetupDeleteUninstallKey(); + + // Remove the previous installation. + PhDeleteDirectory(SetupInstallPath); + + ShowUninstallCompleteDialog(Context); + return STATUS_SUCCESS; + +CleanupExit: + ShowUninstallErrorDialog(Context); + return STATUS_SUCCESS; +} + +static VOID TaskDialogCreateIcons( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + HICON largeIcon; + HICON smallIcon; + + largeIcon = PhLoadIcon( + PhInstanceHandle, + MAKEINTRESOURCE(IDI_ICON1), + PH_LOAD_ICON_SIZE_LARGE, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON) + ); + smallIcon = PhLoadIcon( + PhInstanceHandle, + MAKEINTRESOURCE(IDI_ICON1), + PH_LOAD_ICON_SIZE_LARGE, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON) + ); + + Context->IconLargeHandle = largeIcon; + Context->IconSmallHandle = smallIcon; + + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); +} + +HRESULT CALLBACK TaskDialogUninstallConfirmCallbackProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_BUTTON_CLICKED: + { + if ((INT)wParam == IDYES) + { + ShowUninstallDialog(context); + return S_FALSE; + } + else if ((INT)wParam == IDRETRY) + { + ShowUninstallCompleteDialog(context); + return S_FALSE; + } + } + break; + } + + return S_OK; +} + +HRESULT CALLBACK TaskDialogUninstallCallbackProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_NAVIGATED: + { + SendMessage(hwndDlg, TDM_SET_MARQUEE_PROGRESS_BAR, TRUE, 0); + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_MARQUEE, TRUE, 1); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), SetupUninstallBuild, context); + } + break; + } + + return S_OK; +} + +HRESULT CALLBACK TaskDialogUninstallCompleteCallbackProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_NAVIGATED: + SendMessage(hwndDlg, TDM_SET_PROGRESS_BAR_POS, 100, 0); + break; + } + + return S_OK; +} + +VOID ShowUninstallCompleteDialog( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_SHOW_PROGRESS_BAR; + config.dwCommonButtons = TDCBF_CLOSE_BUTTON; + config.hMainIcon = Context->IconLargeHandle; + config.pfCallback = TaskDialogUninstallCompleteCallbackProc; + config.lpCallbackData = (LONG_PTR)Context; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Process Hacker has been uninstalled."; + config.pszContent = L"Click close to exit setup."; + config.cxWidth = 200; + + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} + +VOID ShowUninstallConfirmDialog( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; + config.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; + config.nDefaultButton = IDNO; + config.hMainIcon = Context->IconLargeHandle; + config.pfCallback = TaskDialogUninstallConfirmCallbackProc; + config.lpCallbackData = (LONG_PTR)Context; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Process Hacker - Setup"; + config.pszContent = L"Are you sure you want to uninstall Process Hacker?"; + config.cxWidth = 200; + + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} + +VOID ShowUninstallDialog( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_SHOW_MARQUEE_PROGRESS_BAR; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.hMainIcon = Context->IconLargeHandle; + config.pfCallback = TaskDialogUninstallCallbackProc; + config.lpCallbackData = (LONG_PTR)Context; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Uninstalling Process Hacker..."; + config.cxWidth = 200; + + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} + +VOID ShowUninstallErrorDialog( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; + config.dwCommonButtons = TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + config.hMainIcon = Context->IconLargeHandle; + config.pfCallback = TaskDialogUninstallConfirmCallbackProc; + config.lpCallbackData = (LONG_PTR)Context; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Process Hacker could not be uninstalled."; + config.pszContent = L"Click retry to try again or close to exit setup."; + config.cxWidth = 200; + + SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); +} +HRESULT CALLBACK TaskDialogUninstallBootstrapCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData + ) +{ + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_CREATED: + { + context->DialogHandle = hwndDlg; + + // Center the window on the desktop + PhCenterWindow(hwndDlg, NULL); + // Create the Taskdialog icons + TaskDialogCreateIcons(context); + // Navigate to the first page + ShowUninstallConfirmDialog(context); + } + break; + } + + return S_OK; +} + +VOID SetupShowUninstallDialog( + VOID + ) +{ + PVOID context; + TASKDIALOGCONFIG config; + PH_AUTO_POOL autoPool; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + + PhInitializeAutoPool(&autoPool); + + context = (PPH_SETUP_CONTEXT)PhCreateAlloc(sizeof(PH_SETUP_CONTEXT)); + memset(context, 0, sizeof(PH_SETUP_CONTEXT)); + + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW; + config.dwCommonButtons = TDCBF_CANCEL_BUTTON; + config.pszWindowTitle = PhApplicationName; + config.pfCallback = TaskDialogUninstallBootstrapCallback; + config.lpCallbackData = (LONG_PTR)context; + + TaskDialogIndirect(&config, NULL, NULL, NULL); + + PhDereferenceObject(context); + PhDeleteAutoPool(&autoPool); +} \ No newline at end of file diff --git a/tools/CustomSetupTool/CustomSetupTool/update.c b/tools/CustomSetupTool/CustomSetupTool/update.c index 4de7ddc61e9d..0766d1c9fef6 100644 --- a/tools/CustomSetupTool/CustomSetupTool/update.c +++ b/tools/CustomSetupTool/CustomSetupTool/update.c @@ -19,155 +19,159 @@ */ #include -#include -#include +#include -typedef struct _PH_SETUP_UPDATE_CONTEXT -{ - BOOLEAN FixedWindowStyles; - HWND DialogHandle; - HICON IconSmallHandle; - HICON IconLargeHandle; - - PPH_STRING FileDownloadUrl; - PPH_STRING RevVersion; - PPH_STRING Size; - PPH_STRING SetupFilePath; -} PH_SETUP_UPDATE_CONTEXT, *PPH_SETUP_UPDATE_CONTEXT; - -#define WM_SHOWDIALOG (WM_APP + 550) +#define WM_TASKDIALOGINIT (WM_APP + 550) HWND UpdateDialogHandle = NULL; HANDLE UpdateDialogThreadHandle = NULL; PH_EVENT InitializedEvent = PH_EVENT_INIT; NTSTATUS SetupUpdateBuild( - _In_ PPH_SETUP_UPDATE_CONTEXT Context + _In_ PPH_SETUP_CONTEXT Context ) { - SetupFindInstallDirectory(); - if (!ShutdownProcessHacker()) + { + Context->ErrorCode = ERROR_INVALID_FUNCTION; goto CleanupExit; + } - if (!SetupUninstallKph()) + if (!SetupCreateUninstallFile(Context)) goto CleanupExit; - SetupCreateUninstallKey(); - SetupCreateUninstallFile(); - //SetupSetWindowsOptions(); + if (!SetupUninstallKph(Context)) + goto CleanupExit; - if (!SetupExtractBuild(Context->DialogHandle)) + if (!SetupExtractBuild(Context)) goto CleanupExit; - if (SetupInstallKphService) - SetupInstallKph(); + // Set the default image execution options. + SetupCreateImageFileExecutionOptions(); - if (SetupExecuteProcessHacker(Context->DialogHandle)) - { - PostMessage(Context->DialogHandle, WM_QUIT, 0, 0); - } + // Set the default KPH configuration. + SetupStartKph(Context, FALSE); + if (!SetupExecuteProcessHacker(Context)) + goto CleanupExit; + + PostMessage(Context->DialogHandle, WM_QUIT, 0, 0); PhDereferenceObject(Context); return STATUS_SUCCESS; CleanupExit: - //PostMessage(Context->DialogHandle, IDD_ERROR, 0, 0); + PostMessage(Context->DialogHandle, WM_APP + IDD_ERROR, 0, 0); PhDereferenceObject(Context); return STATUS_FAIL_CHECK; } - -PPH_SETUP_UPDATE_CONTEXT CreateUpdateContext( +PPH_SETUP_CONTEXT CreateUpdateContext( VOID ) { - PPH_SETUP_UPDATE_CONTEXT context; + PPH_SETUP_CONTEXT context; - context = (PPH_SETUP_UPDATE_CONTEXT)PhCreateAlloc(sizeof(PH_SETUP_UPDATE_CONTEXT)); - memset(context, 0, sizeof(PH_SETUP_UPDATE_CONTEXT)); + context = (PPH_SETUP_CONTEXT)PhCreateAlloc(sizeof(PH_SETUP_CONTEXT)); + memset(context, 0, sizeof(PH_SETUP_CONTEXT)); return context; } -VOID FreeUpdateContext( - _In_ _Post_invalid_ PPH_SETUP_UPDATE_CONTEXT Context +VOID TaskDialogCreateIcons( + _In_ PPH_SETUP_CONTEXT Context ) { - //PhClearReference(&Context->Version); - // PhClearReference(&Context->RevVersion); - //PhClearReference(&Context->RelDate); - // PhClearReference(&Context->Size); - //PhClearReference(&Context->Hash); - // PhClearReference(&Context->Signature); - // PhClearReference(&Context->ReleaseNotesUrl); - //PhClearReference(&Context->SetupFilePath); - // PhClearReference(&Context->SetupFileDownloadUrl); - - if (Context->IconLargeHandle) - DestroyIcon(Context->IconLargeHandle); - - if (Context->IconSmallHandle) - DestroyIcon(Context->IconSmallHandle); - - //PhClearReference(&Context); + HICON largeIcon; + HICON smallIcon; + + largeIcon = PhLoadIcon( + PhInstanceHandle, + MAKEINTRESOURCE(IDI_ICON1), + PH_LOAD_ICON_SIZE_LARGE, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON) + ); + smallIcon = PhLoadIcon( + PhInstanceHandle, + MAKEINTRESOURCE(IDI_ICON1), + PH_LOAD_ICON_SIZE_LARGE, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON) + ); + + Context->IconLargeHandle = largeIcon; + Context->IconSmallHandle = smallIcon; + + SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); + SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); } -VOID TaskDialogCreateIcons( - _In_ PPH_SETUP_UPDATE_CONTEXT Context +HRESULT CALLBACK SetupErrorTaskDialogCallbackProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam, + _In_ LONG_PTR dwRefData ) { - //HICON largeIcon; - //HICON smallIcon; - - //largeIcon = PhLoadIcon( - // NtCurrentPeb()->ImageBaseAddress, - // MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - // PH_LOAD_ICON_SIZE_LARGE, - // GetSystemMetrics(SM_CXICON), - // GetSystemMetrics(SM_CYICON) - // ); - //smallIcon = PhLoadIcon( - // NtCurrentPeb()->ImageBaseAddress, - // MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER), - // PH_LOAD_ICON_SIZE_LARGE, - // GetSystemMetrics(SM_CXSMICON), - // GetSystemMetrics(SM_CYSMICON) - // ); - - //Context->IconLargeHandle = largeIcon; - //Context->IconSmallHandle = smallIcon; - - //SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)largeIcon); - //SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)smallIcon); + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; + + switch (uMsg) + { + case TDN_NAVIGATED: + break; + } + + return S_OK; } -VOID TaskDialogLinkClicked( - _In_ PPH_SETUP_UPDATE_CONTEXT Context +VOID SetupShowUpdatingErrorDialog( + _In_ HWND hwndDlg, + _In_ PPH_SETUP_CONTEXT Context ) { - PhShellExecute(Context->DialogHandle, L"https://www.maxmind.com/", NULL); + TASKDIALOGCONFIG config; + + memset(&config, 0, sizeof(TASKDIALOGCONFIG)); + config.cbSize = sizeof(TASKDIALOGCONFIG); + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED; + config.cxWidth = 200; + config.dwCommonButtons = TDCBF_CLOSE_BUTTON; + config.hMainIcon = Context->IconLargeHandle; + config.pfCallback = SetupErrorTaskDialogCallbackProc; + config.lpCallbackData = (LONG_PTR)Context; + config.pszWindowTitle = PhApplicationName; + config.pszMainInstruction = L"Error updating to the latest version."; + + if (Context->ErrorCode) + { + PPH_STRING errorString; + + if (errorString = PhGetStatusMessage(0, Context->ErrorCode)) + config.pszContent = PhGetString(errorString); + } + + SendMessage(hwndDlg, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } LRESULT CALLBACK TaskDialogSubclassProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { - PPH_SETUP_UPDATE_CONTEXT context = (PPH_SETUP_UPDATE_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hwndDlg, UCHAR_MAX))) + return 0; + + oldWndProc = context->TaskDialogWndProc; switch (uMsg) { - case WM_NCDESTROY: - { - RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass); - } - break; - case WM_SHOWDIALOG: + case WM_TASKDIALOGINIT: { if (IsMinimized(hwndDlg)) ShowWindow(hwndDlg, SW_RESTORE); @@ -177,32 +181,23 @@ LRESULT CALLBACK TaskDialogSubclassProc( SetForegroundWindow(hwndDlg); } break; - //case PH_UPDATESUCCESS: - // { - // ShowInstallRestartDialog(context); - // } - // break; - //case PH_UPDATEFAILURE: - // { - // // if ((BOOLEAN)wParam) - // // ShowUpdateFailedDialog(context, TRUE, FALSE); - // // else if ((BOOLEAN)lParam) - // // ShowUpdateFailedDialog(context, FALSE, TRUE); - // //else - // //ShowUpdateFailedDialog(context, FALSE, FALSE); - // } - // break; - //case PH_UPDATEISERRORED: - // { - // //ShowUpdateFailedDialog(context, FALSE, FALSE); - // } - // break; + case WM_DESTROY: + { + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hwndDlg, UCHAR_MAX); + } + break; + case WM_APP + IDD_ERROR: + { + SetupShowUpdatingErrorDialog(hwndDlg, context); + } + break; } - return DefSubclassProc(hwndDlg, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hwndDlg, uMsg, wParam, lParam); } -HRESULT CALLBACK CheckingForUpdatesCallbackProc( +HRESULT CALLBACK SetupUpdatingTaskDialogCallbackProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, @@ -210,7 +205,7 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( _In_ LONG_PTR dwRefData ) { - PPH_SETUP_UPDATE_CONTEXT context = (PPH_SETUP_UPDATE_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -227,8 +222,8 @@ HRESULT CALLBACK CheckingForUpdatesCallbackProc( return S_OK; } -VOID ShowCheckForUpdatesDialog( - _In_ PPH_SETUP_UPDATE_CONTEXT Context +VOID SetupShowUpdatingDialog( + _In_ PPH_SETUP_CONTEXT Context ) { TASKDIALOGCONFIG config; @@ -236,14 +231,31 @@ VOID ShowCheckForUpdatesDialog( memset(&config, 0, sizeof(TASKDIALOGCONFIG)); config.cbSize = sizeof(TASKDIALOGCONFIG); config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_SHOW_MARQUEE_PROGRESS_BAR | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS; + config.cxWidth = 200; config.dwCommonButtons = TDCBF_CANCEL_BUTTON; config.hMainIcon = Context->IconLargeHandle; - config.pfCallback = CheckingForUpdatesCallbackProc; + config.pfCallback = SetupUpdatingTaskDialogCallbackProc; config.lpCallbackData = (LONG_PTR)Context; config.pszWindowTitle = PhApplicationName; - //config.pszMainInstruction = L"Updating Process Hacker..."; - config.pszContent = PhaFormatString(L"Updating to version %lu.%lu.%lu...", PHAPP_VERSION_MAJOR, PHAPP_VERSION_MINOR, PHAPP_VERSION_REVISION)->Buffer; - config.cxWidth = 200; + + if (SetupMode == SETUP_COMMAND_SILENTINSTALL) + { + config.pszMainInstruction = PhaFormatString( + L"Installing Process Hacker %lu.%lu.%lu...", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR, + PHAPP_VERSION_REVISION + )->Buffer; + } + else + { + config.pszMainInstruction = PhaFormatString( + L"Updating to version %lu.%lu.%lu...", + PHAPP_VERSION_MAJOR, + PHAPP_VERSION_MINOR, + PHAPP_VERSION_REVISION + )->Buffer; + } SendMessage(Context->DialogHandle, TDM_NAVIGATE_PAGE, 0, (LPARAM)&config); } @@ -256,7 +268,7 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( _In_ LONG_PTR dwRefData ) { - PPH_SETUP_UPDATE_CONTEXT context = (PPH_SETUP_UPDATE_CONTEXT)dwRefData; + PPH_SETUP_CONTEXT context = (PPH_SETUP_CONTEXT)dwRefData; switch (uMsg) { @@ -264,16 +276,20 @@ HRESULT CALLBACK TaskDialogBootstrapCallback( { context->DialogHandle = hwndDlg; - // Center the window on the desktop + // Center the window on the desktop. PhCenterWindow(hwndDlg, NULL); - // Create the Taskdialog icons + + // Create the Taskdialog icons. TaskDialogCreateIcons(context); - // Subclass the Taskdialog - SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context); - // Navigate to the first page - ShowCheckForUpdatesDialog(context); - PostMessage(hwndDlg, WM_SHOWDIALOG, 0, 0); + // Subclass the Taskdialog. + context->TaskDialogWndProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, UCHAR_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)TaskDialogSubclassProc); + + // Navigate to the first page. + SetupShowUpdatingDialog(context); + SendMessage(hwndDlg, WM_TASKDIALOGINIT, 0, 0); } break; } @@ -285,7 +301,6 @@ VOID SetupShowUpdateDialog( VOID ) { - PVOID context; TASKDIALOGCONFIG config; PH_AUTO_POOL autoPool; @@ -293,16 +308,14 @@ VOID SetupShowUpdateDialog( PhInitializeAutoPool(&autoPool); - context = CreateUpdateContext(); config.cbSize = sizeof(TASKDIALOGCONFIG); - config.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW; + config.dwFlags = TDF_USE_HICON_MAIN | TDF_ALLOW_DIALOG_CANCELLATION | TDF_SHOW_MARQUEE_PROGRESS_BAR | TDF_CAN_BE_MINIMIZED | TDF_ENABLE_HYPERLINKS | TDF_POSITION_RELATIVE_TO_WINDOW; config.dwCommonButtons = TDCBF_CANCEL_BUTTON; config.pszWindowTitle = PhApplicationName; config.pfCallback = TaskDialogBootstrapCallback; - config.lpCallbackData = (LONG_PTR)context; + config.lpCallbackData = (LONG_PTR)CreateUpdateContext(); TaskDialogIndirect(&config, NULL, NULL, NULL); - FreeUpdateContext(context); PhDeleteAutoPool(&autoPool); -} \ No newline at end of file +} diff --git a/tools/CustomSetupTool/CustomSetupTool/websetuppage.c b/tools/CustomSetupTool/CustomSetupTool/websetuppage.c new file mode 100644 index 000000000000..2e48dece3f1b --- /dev/null +++ b/tools/CustomSetupTool/CustomSetupTool/websetuppage.c @@ -0,0 +1,136 @@ +/* + * Process Hacker Toolchain - + * project setup + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +NTSTATUS SetupDownloadWebSetupThread( + _In_ PPH_SETUP_CONTEXT Context + ) +{ + ULONGLONG currentVersion = 0; + ULONGLONG latestVersion = 0; + PPH_STRING setupFileName; + PH_IMAGE_VERSION_INFO versionInfo; + + if (!SetupQueryUpdateData(Context)) + goto CleanupExit; + + setupFileName = PhGetApplicationFileName(); + + if (!PhInitializeImageVersionInfo(&versionInfo, PhGetString(setupFileName))) + goto CleanupExit; + + currentVersion = ParseVersionString(versionInfo.FileVersion); + +#ifdef FORCE_UPDATE_CHECK + latestVersion = MAKE_VERSION_ULONGLONG( + 9999, + 9999, + 9999, + 0 + ); +#else + latestVersion = ParseVersionString(Context->WebSetupFileVersion); +#endif + + // Compare the current version against the latest available version + if (currentVersion < latestVersion) + { + if (!UpdateDownloadUpdateData(Context)) + goto CleanupExit; + } + + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_DIALOG5); + return STATUS_SUCCESS; + +CleanupExit: + + PostMessage(Context->DialogHandle, PSM_SETCURSELID, 0, IDD_ERROR); + return STATUS_FAIL_CHECK; +} + +INT_PTR CALLBACK SetupWebSetup_WndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _Inout_ WPARAM wParam, + _Inout_ LPARAM lParam + ) +{ + PPH_SETUP_CONTEXT context = NULL; + + if (uMsg == WM_INITDIALOG) + { + context = PhGetWindowContext(GetParent(hwndDlg), ULONG_MAX); + PhSetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT, context); + } + else + { + context = PhGetWindowContext(hwndDlg, PH_WINDOW_CONTEXT_DEFAULT); + } + + if (context == NULL) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + SetupLoadImage(GetDlgItem(hwndDlg, IDC_PROJECT_ICON), MAKEINTRESOURCE(IDB_PNG1)); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_MAINHEADER), -17, FW_SEMIBOLD); + //SetupInitializeFont(GetDlgItem(hwndDlg, IDC_SUBHEADER), -12, FW_NORMAL); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_INSTALL_STATUS), -12, FW_SEMIBOLD); + SetupInitializeFont(GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS), -12, FW_NORMAL); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + LPPSHNOTIFY pageNotify = (LPPSHNOTIFY)header; + + switch (pageNotify->hdr.code) + { + case PSN_SETACTIVE: + { + context->MainHeaderHandle = GetDlgItem(hwndDlg, IDC_MAINHEADER); + context->StatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_STATUS); + context->SubStatusHandle = GetDlgItem(hwndDlg, IDC_INSTALL_SUBSTATUS); + context->ProgressHandle = GetDlgItem(hwndDlg, IDC_INSTALL_PROGRESS); + + SetWindowText(context->MainHeaderHandle, L"Checking for newer websetup version..."); + SetWindowText(context->StatusHandle, L"Requesting latest version..."); + SetWindowText(context->SubStatusHandle, L""); + + // Disable Next/Back buttons + PropSheet_SetWizButtons(context->DialogHandle, 0); + + PhCreateThread2(SetupDownloadWebSetupThread, context); + } + break; + } + } + break; + } + + return FALSE; +} \ No newline at end of file diff --git a/tools/CustomSignTool/CustomSignTool.sln b/tools/CustomSignTool/CustomSignTool.sln new file mode 100644 index 000000000000..89743a203c9d --- /dev/null +++ b/tools/CustomSignTool/CustomSignTool.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2006 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomSignTool", "CustomSignTool.vcxproj", "{E8CD0A41-1537-4EA6-98AC-E80CD59C478E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|x64.ActiveCfg = Debug|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|x64.Build.0 = Debug|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|x86.ActiveCfg = Debug|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|x86.Build.0 = Debug|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|x64.ActiveCfg = Release|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|x64.Build.0 = Release|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|x86.ActiveCfg = Release|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {67C12566-2845-498A-B9B9-C4472F50BD19} + EndGlobalSection +EndGlobal diff --git a/tools/CustomSignTool/CustomSignTool.vcxproj b/tools/CustomSignTool/CustomSignTool.vcxproj index 9e345451a89b..24f83c05f5de 100644 --- a/tools/CustomSignTool/CustomSignTool.vcxproj +++ b/tools/CustomSignTool/CustomSignTool.vcxproj @@ -1,4 +1,4 @@ - + @@ -22,7 +22,7 @@ {E8CD0A41-1537-4EA6-98AC-E80CD59C478E} CustomSignTool Win32Proj - 10.0.15063.0 + 10.0.16299.0
diff --git a/tools/CustomSignTool/bin/Release32/CustomSignTool.exe b/tools/CustomSignTool/bin/Release32/CustomSignTool.exe index 7af31bf7ba69..d78fbf12720d 100644 Binary files a/tools/CustomSignTool/bin/Release32/CustomSignTool.exe and b/tools/CustomSignTool/bin/Release32/CustomSignTool.exe differ diff --git a/tools/CustomSignTool/bin/Release64/CustomSignTool.exe b/tools/CustomSignTool/bin/Release64/CustomSignTool.exe index ccbfcc0c2775..89759b686d41 100644 Binary files a/tools/CustomSignTool/bin/Release64/CustomSignTool.exe and b/tools/CustomSignTool/bin/Release64/CustomSignTool.exe differ diff --git a/tools/CustomSignTool/main.c b/tools/CustomSignTool/main.c index 32b8a4051a31..c860769fb5f5 100644 --- a/tools/CustomSignTool/main.c +++ b/tools/CustomSignTool/main.c @@ -234,7 +234,7 @@ int __cdecl wmain(int argc, wchar_t *argv[]) NTSTATUS status; PH_STRINGREF commandLine; - if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0))) + if (!NT_SUCCESS(PhInitializePhLib())) return 1; PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); diff --git a/tools/CustomStartTool/CustomStartTool.manifest b/tools/CustomStartTool/CustomStartTool.manifest new file mode 100644 index 000000000000..49febae436ae --- /dev/null +++ b/tools/CustomStartTool/CustomStartTool.manifest @@ -0,0 +1,46 @@ + + + + CustomStartTool + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + \ No newline at end of file diff --git a/tools/CustomStartTool/CustomStartTool.sln b/tools/CustomStartTool/CustomStartTool.sln new file mode 100644 index 000000000000..ed99573d0794 --- /dev/null +++ b/tools/CustomStartTool/CustomStartTool.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.4 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomStartTool", "CustomStartTool.vcxproj", "{E8CD0A41-1537-4EA6-98AC-E80CD59C478E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|32bit = Debug|32bit + Debug|64bit = Debug|64bit + Release|32bit = Release|32bit + Release|64bit = Release|64bit + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|32bit.ActiveCfg = Debug|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|32bit.Build.0 = Debug|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|64bit.ActiveCfg = Debug|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Debug|64bit.Build.0 = Debug|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|32bit.ActiveCfg = Release|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|32bit.Build.0 = Release|Win32 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|64bit.ActiveCfg = Release|x64 + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E}.Release|64bit.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/CustomStartTool/CustomStartTool.vcxproj b/tools/CustomStartTool/CustomStartTool.vcxproj new file mode 100644 index 000000000000..202dc4bc4471 --- /dev/null +++ b/tools/CustomStartTool/CustomStartTool.vcxproj @@ -0,0 +1,212 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {E8CD0A41-1537-4EA6-98AC-E80CD59C478E} + CustomStartTool + Win32Proj + 10.0.16299.0 + + + + Application + Unicode + true + v141 + + + Application + Unicode + v141 + + + Application + Unicode + true + v141 + + + Application + Unicode + v141 + + + + + + + + + + + + + + + + + + + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + true + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + true + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(ProjectDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + + + bootstrap + + + bootstrap + + + bootstrap + + + bootstrap + + + + Disabled + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + + + noenv.obj;phlib.lib;ntdll.lib;bcrypt.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + MachineX86 + 6.01 + + + + + Disabled + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + + + noenv.obj;phlib.lib;ntdll.lib;bcrypt.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + MachineX64 + 6.01 + + + + + MaxSpeed + true + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + StreamingSIMDExtensions + true + + + noenv.obj;phlib.lib;ntdll.lib;bcrypt.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + true + true + MachineX86 + true + 6.01 + + + + + MaxSpeed + true + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + true + + + noenv.obj;phlib.lib;ntdll.lib;bcrypt.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + true + true + MachineX64 + true + 6.01 + + + + + + + + {477d0215-f252-41a1-874b-f27e3ea1ed17} + false + + + + + + + + + \ No newline at end of file diff --git a/plugins/SbieSupport/SbieSupport.vcxproj.filters b/tools/CustomStartTool/CustomStartTool.vcxproj.filters similarity index 75% rename from plugins/SbieSupport/SbieSupport.vcxproj.filters rename to tools/CustomStartTool/CustomStartTool.vcxproj.filters index 1c000f9ebf8b..948f2cbc045f 100644 --- a/plugins/SbieSupport/SbieSupport.vcxproj.filters +++ b/tools/CustomStartTool/CustomStartTool.vcxproj.filters @@ -1,35 +1,27 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;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 - - - - - Source Files - - - - - Header Files - - - Header Files - - - - - Resource Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;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 + + + + + Source Files + + + + + Resource Files + + \ No newline at end of file diff --git a/tools/CustomStartTool/main.c b/tools/CustomStartTool/main.c new file mode 100644 index 000000000000..a85e586a20f5 --- /dev/null +++ b/tools/CustomStartTool/main.c @@ -0,0 +1,167 @@ +/* + * Process Hacker - + * Custom Bootstrap Tool + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +static ULONG64 PhMitigationPolicy = + //PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON | + //PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON | + PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON; + +PPH_STRING GetProcessHackerInstallPath(VOID) +{ + static PH_STRINGREF UninstallKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ProcessHacker"); + HANDLE keyHandle; + PPH_STRING installPath = NULL; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ | KEY_WOW64_64KEY, + PH_KEY_LOCAL_MACHINE, + &UninstallKeyName, + 0 + ))) + { + installPath = PhQueryRegistryString(keyHandle, L"InstallLocation"); + NtClose(keyHandle); + } + + return installPath; +} + +PPH_STRING GetProcessHackerPath(VOID) +{ + PPH_STRING installPath; + PPH_STRING path = NULL; + + installPath = GetProcessHackerInstallPath(); + + if (!PhIsNullOrEmptyString(installPath)) + { + path = PhConcatStrings2(PhGetString(installPath), L"\\ProcessHacker.exe"); + } + + PhClearReference(&installPath); + + return path; +} + +BOOLEAN CheckProcessHackerInstalled(VOID) +{ + BOOLEAN installed = FALSE; + PPH_STRING installPath; + PPH_STRING path; + + installPath = GetProcessHackerInstallPath(); + + if (!PhIsNullOrEmptyString(installPath)) + { + path = PhConcatStrings2(PhGetString(installPath), L"\\ProcessHacker.exe"); + installed = GetFileAttributes(path->Buffer) != INVALID_FILE_ATTRIBUTES; + PhClearReference(&path); + } + + PhClearReference(&installPath); + + return installed; +} + +INT WINAPI wWinMain( + _In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ PWSTR lpCmdLine, + _In_ INT nCmdShow + ) +{ + SIZE_T attributeListLength = 0; + PROCESS_INFORMATION processInfo = { 0 }; + STARTUPINFOEX info = { sizeof(STARTUPINFOEX) }; + PPH_STRING fileName; + PPH_STRING currentDirectory; + + if (!NT_SUCCESS(PhInitializePhLib())) + return 1; + + if (!InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + PhShowStatus(NULL, L"InitializeProcThreadAttributeList Error", PhGetLastWin32ErrorAsNtStatus(), 0); + return 1; + } + + info.lpAttributeList = PhAllocate(attributeListLength); + + if (!InitializeProcThreadAttributeList(info.lpAttributeList, 1, 0, &attributeListLength)) + { + PhShowStatus(NULL, L"InitializeProcThreadAttributeList Error", PhGetLastWin32ErrorAsNtStatus(), 0); + return 1; + } + + if (!UpdateProcThreadAttribute(info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &PhMitigationPolicy, sizeof(ULONG64), NULL, NULL)) + { + PhShowStatus(NULL, L"UpdateProcThreadAttribute Error", PhGetLastWin32ErrorAsNtStatus(), 0); + return 1; + } + + currentDirectory = PhGetApplicationDirectory(); + fileName = PhConcatStrings2(currentDirectory->Buffer, L"\\ProcessHacker.exe"); + PhMoveReference(&fileName, PhGetFullPath(fileName->Buffer, NULL)); + + if (!RtlDoesFileExists_U(fileName->Buffer) && CheckProcessHackerInstalled()) + PhMoveReference(&fileName, GetProcessHackerPath()); + + if (!CreateProcess( + NULL, + fileName->Buffer, + NULL, + NULL, + FALSE, + EXTENDED_STARTUPINFO_PRESENT, + NULL, + NULL, + &info.StartupInfo, + &processInfo + )) + { + PhShowStatus(NULL, L"CreateProcess Error", PhGetLastWin32ErrorAsNtStatus(), 0); + } + + PhDereferenceObject(fileName); + + if (processInfo.hProcess) + NtClose(processInfo.hProcess); + + if (processInfo.hThread) + NtClose(processInfo.hThread); + + DeleteProcThreadAttributeList(info.lpAttributeList); + + return 0; +} diff --git a/tools/GenerateZw/GenerateZw.sln b/tools/GenerateZw/GenerateZw.sln index e41ba905db6c..95020ef1863e 100644 --- a/tools/GenerateZw/GenerateZw.sln +++ b/tools/GenerateZw/GenerateZw.sln @@ -1,22 +1,22 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 -MinimumVisualStudioVersion = 15.0.26228.4 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateZw", "GenerateZw\GenerateZw.csproj", "{10589240-84D9-4935-9868-7FFADB6545F9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {10589240-84D9-4935-9868-7FFADB6545F9}.Debug|x86.ActiveCfg = Debug|x86 - {10589240-84D9-4935-9868-7FFADB6545F9}.Debug|x86.Build.0 = Debug|x86 - {10589240-84D9-4935-9868-7FFADB6545F9}.Release|x86.ActiveCfg = Release|x86 - {10589240-84D9-4935-9868-7FFADB6545F9}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.4 +MinimumVisualStudioVersion = 15.0.26228.4 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateZw", "GenerateZw\GenerateZw.csproj", "{10589240-84D9-4935-9868-7FFADB6545F9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {10589240-84D9-4935-9868-7FFADB6545F9}.Debug|x86.ActiveCfg = Debug|x86 + {10589240-84D9-4935-9868-7FFADB6545F9}.Debug|x86.Build.0 = Debug|x86 + {10589240-84D9-4935-9868-7FFADB6545F9}.Release|x86.ActiveCfg = Release|x86 + {10589240-84D9-4935-9868-7FFADB6545F9}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/GenerateZw/GenerateZw/GenerateZw.csproj b/tools/GenerateZw/GenerateZw/GenerateZw.csproj index 43a71f805235..ee20581f9977 100644 --- a/tools/GenerateZw/GenerateZw/GenerateZw.csproj +++ b/tools/GenerateZw/GenerateZw/GenerateZw.csproj @@ -1,61 +1,61 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {10589240-84D9-4935-9868-7FFADB6545F9} - Exe - Properties - GenerateZw - GenerateZw - v4.6.1 - 512 - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x86 - pdbonly - true - bin\Release\ - prompt - 4 - false - - - - - - - - - - - - - - - - - - - - + + + + Debug + x86 + 8.0.30703 + 2.0 + {10589240-84D9-4935-9868-7FFADB6545F9} + Exe + Properties + GenerateZw + GenerateZw + v4.6.1 + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Release\ + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/GenerateZw/GenerateZw/Program.cs b/tools/GenerateZw/GenerateZw/Program.cs index 4962274d9ee0..1f857a046f9c 100644 --- a/tools/GenerateZw/GenerateZw/Program.cs +++ b/tools/GenerateZw/GenerateZw/Program.cs @@ -1,26 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace GenerateZw -{ - class Program - { - static void Main(string[] args) - { - ZwGen gen = new ZwGen(); - string configFile = args.Length > 0 ? args[0] : "options.txt"; - - try - { - gen.LoadConfig(configFile); - gen.Execute(); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GenerateZw +{ + class Program + { + static void Main(string[] args) + { + ZwGen gen = new ZwGen(); + string configFile = args.Length > 0 ? args[0] : "options.txt"; + + try + { + gen.LoadConfig(configFile); + gen.Execute(); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + } +} diff --git a/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs b/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs index e45ace10f142..418173296108 100644 --- a/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs +++ b/tools/GenerateZw/GenerateZw/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("GenerateZw")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("GenerateZw")] -[assembly: AssemblyCopyright("Copyright © 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("db557e10-0bf4-4a74-b8b4-ade1faa91177")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GenerateZw")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GenerateZw")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("db557e10-0bf4-4a74-b8b4-ade1faa91177")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tools/GenerateZw/GenerateZw/ZwGen.cs b/tools/GenerateZw/GenerateZw/ZwGen.cs index 5cb58d9cfa16..de079cd659c5 100644 --- a/tools/GenerateZw/GenerateZw/ZwGen.cs +++ b/tools/GenerateZw/GenerateZw/ZwGen.cs @@ -1,123 +1,123 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using System.Text.RegularExpressions; - -namespace GenerateZw -{ - class ServiceDefinition - { - public string Name; - public string Text; - public int NameIndex; - } - - class ServiceDefinitionComparer : IEqualityComparer - { - public bool Equals(ServiceDefinition x, ServiceDefinition y) - { - return string.Equals(x.Name, y.Name); - } - - public int GetHashCode(ServiceDefinition obj) - { - return obj.Name.GetHashCode(); - } - } - - class ZwGen - { - private string _baseDirectory; - private string[] _files; - private string _outputFile; - private string _header = ""; - private string _footer = ""; - - private List _defs; - - private string UnEscape(string text) - { - return text.Replace("\\r", "\r").Replace("\\n", "\n").Replace("\\\\", "\\"); - } - - public void LoadConfig(string fileName) - { - string[] lines = File.ReadAllLines(fileName); - - foreach (string line in lines) - { - string[] split = line.Split(new char[] { '=' }, 2); - - switch (split[0]) - { - case "base": - _baseDirectory = split[1]; - break; - case "in": - _files = split[1].Split(';'); - break; - case "out": - _outputFile = split[1]; - break; - case "header": - _header = UnEscape(split[1]); - break; - case "footer": - _footer = UnEscape(split[1]); - break; - } - } - } - - private void Parse(string text) - { - Regex regex = new Regex(@"NTSYSCALLAPI[\w\s_]*NTAPI\s*(Nt(\w)*)\(.*?\);", RegexOptions.Compiled | RegexOptions.Singleline); - MatchCollection matches; - - matches = regex.Matches(text); - - foreach (Match match in matches) - { - _defs.Add(new ServiceDefinition() { Name = match.Groups[1].Value, Text = match.Value, NameIndex = match.Groups[1].Index - match.Index }); - } - } - - public void Execute() - { - // Build up a list of definitions. - - _defs = new List(); - - foreach (string fileName in _files) - Parse(File.ReadAllText(_baseDirectory + "\\" + fileName)); - - StreamWriter sw = new StreamWriter(_baseDirectory + "\\" + _outputFile); - - // Remove duplicates and sort. - _defs = new List(_defs.Distinct(new ServiceDefinitionComparer())); - _defs.Sort((x, y) => string.CompareOrdinal(x.Name, y.Name)); - - // Header - - sw.Write(_header); - - // Definitions - - foreach (var d in _defs) - { - Console.WriteLine("System service: " + d.Name); - - // Write the original definition, replacing "Nt" with "Zw". - sw.Write(d.Text.Substring(0, d.NameIndex) + "Zw" + d.Text.Substring(d.NameIndex + 2) + "\r\n\r\n"); - } - - // Footer - - sw.Write(_footer); - - sw.Close(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Text.RegularExpressions; + +namespace GenerateZw +{ + class ServiceDefinition + { + public string Name; + public string Text; + public int NameIndex; + } + + class ServiceDefinitionComparer : IEqualityComparer + { + public bool Equals(ServiceDefinition x, ServiceDefinition y) + { + return string.Equals(x.Name, y.Name); + } + + public int GetHashCode(ServiceDefinition obj) + { + return obj.Name.GetHashCode(); + } + } + + class ZwGen + { + private string _baseDirectory; + private string[] _files; + private string _outputFile; + private string _header = ""; + private string _footer = ""; + + private List _defs; + + private string UnEscape(string text) + { + return text.Replace("\\r", "\r").Replace("\\n", "\n").Replace("\\\\", "\\"); + } + + public void LoadConfig(string fileName) + { + string[] lines = File.ReadAllLines(fileName); + + foreach (string line in lines) + { + string[] split = line.Split(new char[] { '=' }, 2); + + switch (split[0]) + { + case "base": + _baseDirectory = split[1]; + break; + case "in": + _files = split[1].Split(';'); + break; + case "out": + _outputFile = split[1]; + break; + case "header": + _header = UnEscape(split[1]); + break; + case "footer": + _footer = UnEscape(split[1]); + break; + } + } + } + + private void Parse(string text) + { + Regex regex = new Regex(@"NTSYSCALLAPI[\w\s_]*NTAPI\s*(Nt(\w)*)\(.*?\);", RegexOptions.Compiled | RegexOptions.Singleline); + MatchCollection matches; + + matches = regex.Matches(text); + + foreach (Match match in matches) + { + _defs.Add(new ServiceDefinition() { Name = match.Groups[1].Value, Text = match.Value, NameIndex = match.Groups[1].Index - match.Index }); + } + } + + public void Execute() + { + // Build up a list of definitions. + + _defs = new List(); + + foreach (string fileName in _files) + Parse(File.ReadAllText(_baseDirectory + "\\" + fileName)); + + StreamWriter sw = new StreamWriter(_baseDirectory + "\\" + _outputFile); + + // Remove duplicates and sort. + _defs = new List(_defs.Distinct(new ServiceDefinitionComparer())); + _defs.Sort((x, y) => string.CompareOrdinal(x.Name, y.Name)); + + // Header + + sw.Write(_header); + + // Definitions + + foreach (var d in _defs) + { + Console.WriteLine("System service: " + d.Name); + + // Write the original definition, replacing "Nt" with "Zw". + sw.Write(d.Text.Substring(0, d.NameIndex) + "Zw" + d.Text.Substring(d.NameIndex + 2) + "\r\n\r\n"); + } + + // Footer + + sw.Write(_footer); + + sw.Close(); + } + } +} diff --git a/tools/GenerateZw/GenerateZw/zw_options.txt b/tools/GenerateZw/GenerateZw/zw_options.txt new file mode 100644 index 000000000000..9aa13c1ce994 --- /dev/null +++ b/tools/GenerateZw/GenerateZw/zw_options.txt @@ -0,0 +1,5 @@ +base=include +in=ntdbg.h;ntexapi.h;ntgdi.h;ntioapi.h;ntkeapi.h;ntldr.h;ntlpcapi.h;ntmisc.h;ntmmapi.h;ntnls.h;ntobapi.h;ntpebteb.h;ntpfapi.h;ntpnpapi.h;ntpoapi.h;ntpsapi.h;ntregapi.h;ntrtl.h;ntsam.h;ntseapi.h;nttmapi.h;nttp.h;ntwow64.h;ntxcapi.h +out=ntzwapi.h +header=#ifndef _NTZWAPI_H\r\n#define _NTZWAPI_H\r\n\r\n// This file was automatically generated. Do not edit.\r\n\r\n +footer=#endif\r\n diff --git a/tools/fixlib/fixlib.c b/tools/fixlib/fixlib.c index 3109b12dac49..abb2688f9513 100644 --- a/tools/fixlib/fixlib.c +++ b/tools/fixlib/fixlib.c @@ -1,113 +1,113 @@ -#include -#include - -#define ARG_OUTFILE 1 - -PPH_STRING inFile = NULL; -PPH_STRING outFile = NULL; - -BOOLEAN NTAPI CommandLineCallback( - _In_opt_ PPH_COMMAND_LINE_OPTION Option, - _In_opt_ PPH_STRING Value, - _In_opt_ PVOID Context - ) -{ - if (Option) - { - switch (Option->Id) - { - case ARG_OUTFILE: - { - PhSwapReference(&outFile, Value); - } - break; - } - } - else - { - PhSwapReference(&inFile, Value); - } - - return TRUE; -} - -int __cdecl main(int argc, char *argv[]) -{ - static PH_COMMAND_LINE_OPTION options[] = - { - { ARG_OUTFILE, L"o", MandatoryArgumentType } - }; - NTSTATUS status; - PH_STRINGREF commandLine; - PH_MAPPED_ARCHIVE mappedArchive; - PH_MAPPED_ARCHIVE_MEMBER member; - PH_MAPPED_ARCHIVE_IMPORT_ENTRY entry; - - if (!NT_SUCCESS(PhInitializePhLib())) - return 1; - - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - - if (!PhParseCommandLine( - &commandLine, - options, - sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), - PH_COMMAND_LINE_IGNORE_FIRST_PART, - CommandLineCallback, - NULL - ) || !inFile) - { - wprintf(L"Usage: fixlib [-o outfile] infile\n"); - return 1; - } - - if (!outFile) - outFile = inFile; - - CopyFile(inFile->Buffer, outFile->Buffer, FALSE); - - status = PhLoadMappedArchive(outFile->Buffer, NULL, FALSE, &mappedArchive); - - if (!NT_SUCCESS(status)) - { - wprintf(L"Error: %s\n", PhGetStringOrDefault(PhGetNtMessage(status), L"unknown")); - return status; - } - - member = *(mappedArchive.LastStandardMember); - - while (NT_SUCCESS(PhGetNextMappedArchiveMember(&member, &member))) - { - if (NT_SUCCESS(PhGetMappedArchiveImportEntry(&member, &entry))) - { - IMPORT_OBJECT_HEADER *header; - PWSTR type = L"unknown"; - - switch (entry.NameType) - { - case IMPORT_OBJECT_ORDINAL: - type = L"ordinal"; - break; - case IMPORT_OBJECT_NAME: - type = L"name"; - break; - case IMPORT_OBJECT_NAME_NO_PREFIX: - type = L"name-noprefix"; - break; - case IMPORT_OBJECT_NAME_UNDECORATE: - type = L"name-undecorate"; - break; - } - - wprintf(L"%S: %S (%s)\n", entry.DllName, entry.Name, type); - - header = (IMPORT_OBJECT_HEADER *)member.Data; - - // Changes - - header->NameType = IMPORT_OBJECT_NAME_UNDECORATE; - } - } - - PhUnloadMappedArchive(&mappedArchive); -} +#include +#include + +#define ARG_OUTFILE 1 + +PPH_STRING inFile = NULL; +PPH_STRING outFile = NULL; + +BOOLEAN NTAPI CommandLineCallback( + _In_opt_ PPH_COMMAND_LINE_OPTION Option, + _In_opt_ PPH_STRING Value, + _In_opt_ PVOID Context + ) +{ + if (Option) + { + switch (Option->Id) + { + case ARG_OUTFILE: + { + PhSwapReference(&outFile, Value); + } + break; + } + } + else + { + PhSwapReference(&inFile, Value); + } + + return TRUE; +} + +int __cdecl main(int argc, char *argv[]) +{ + static PH_COMMAND_LINE_OPTION options[] = + { + { ARG_OUTFILE, L"o", MandatoryArgumentType } + }; + NTSTATUS status; + PH_STRINGREF commandLine; + PH_MAPPED_ARCHIVE mappedArchive; + PH_MAPPED_ARCHIVE_MEMBER member; + PH_MAPPED_ARCHIVE_IMPORT_ENTRY entry; + + if (!NT_SUCCESS(PhInitializePhLib())) + return 1; + + PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); + + if (!PhParseCommandLine( + &commandLine, + options, + sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), + PH_COMMAND_LINE_IGNORE_FIRST_PART, + CommandLineCallback, + NULL + ) || !inFile) + { + wprintf(L"Usage: fixlib [-o outfile] infile\n"); + return 1; + } + + if (!outFile) + outFile = inFile; + + CopyFile(inFile->Buffer, outFile->Buffer, FALSE); + + status = PhLoadMappedArchive(outFile->Buffer, NULL, FALSE, &mappedArchive); + + if (!NT_SUCCESS(status)) + { + wprintf(L"Error: %s\n", PhGetStringOrDefault(PhGetNtMessage(status), L"unknown")); + return status; + } + + member = *(mappedArchive.LastStandardMember); + + while (NT_SUCCESS(PhGetNextMappedArchiveMember(&member, &member))) + { + if (NT_SUCCESS(PhGetMappedArchiveImportEntry(&member, &entry))) + { + IMPORT_OBJECT_HEADER *header; + PWSTR type = L"unknown"; + + switch (entry.NameType) + { + case IMPORT_OBJECT_ORDINAL: + type = L"ordinal"; + break; + case IMPORT_OBJECT_NAME: + type = L"name"; + break; + case IMPORT_OBJECT_NAME_NO_PREFIX: + type = L"name-noprefix"; + break; + case IMPORT_OBJECT_NAME_UNDECORATE: + type = L"name-undecorate"; + break; + } + + wprintf(L"%S: %S (%s)\n", entry.DllName, entry.Name, type); + + header = (IMPORT_OBJECT_HEADER *)member.Data; + + // Changes + + header->NameType = IMPORT_OBJECT_NAME_UNDECORATE; + } + } + + PhUnloadMappedArchive(&mappedArchive); +} diff --git a/tools/fixlib/fixlib.vcxproj b/tools/fixlib/fixlib.vcxproj index 39611dda7afb..1d3d7d12e3c7 100644 --- a/tools/fixlib/fixlib.vcxproj +++ b/tools/fixlib/fixlib.vcxproj @@ -1,109 +1,109 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {31F4AA06-7ED5-4A6D-B901-19AD4BD16175} - fixlib - Win32Proj - 10.0.15063.0 - - - - Application - Unicode - true - v141 - - - Application - Unicode - v141 - - - - - - - - - - - - - $(ProjectDir)bin\$(Configuration)\ - $(ProjectDir)obj\$(Configuration)\ - false - $(ProjectDir)bin\$(Configuration)\ - $(ProjectDir)obj\$(Configuration)\ - false - - - - Disabled - ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) - _PHLIB_;_CONSOLE;WIN32;DEBUG;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - - - noenv.obj;phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - true - Console - MachineX86 - 5.01 - - - - - MaxSpeed - true - ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) - _PHLIB_;_CONSOLE;WIN32;NDEBUG;%(PreprocessorDefinitions) - MultiThreaded - true - Level3 - ProgramDatabase - StdCall - true - true - - - noenv.obj;phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - true - Console - true - true - MachineX86 - true - 5.01 - - - - - - - - {477d0215-f252-41a1-874b-f27e3ea1ed17} - false - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {31F4AA06-7ED5-4A6D-B901-19AD4BD16175} + fixlib + Win32Proj + 10.0.15063.0 + + + + Application + Unicode + true + v141 + + + Application + Unicode + v141 + + + + + + + + + + + + + $(ProjectDir)bin\$(Configuration)\ + $(ProjectDir)obj\$(Configuration)\ + false + $(ProjectDir)bin\$(Configuration)\ + $(ProjectDir)obj\$(Configuration)\ + false + + + + Disabled + ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) + _PHLIB_;_CONSOLE;WIN32;DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + + + noenv.obj;phlib.lib;ntdll.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + true + Console + MachineX86 + 5.01 + + + + + MaxSpeed + true + ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) + _PHLIB_;_CONSOLE;WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + true + + + noenv.obj;phlib.lib;ntdll.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + true + 5.01 + + + + + + + + {477d0215-f252-41a1-874b-f27e3ea1ed17} + false + + + + + \ No newline at end of file diff --git a/tools/peview/attributes.c b/tools/peview/attributes.c new file mode 100644 index 000000000000..31c663c76e17 --- /dev/null +++ b/tools/peview/attributes.c @@ -0,0 +1,151 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +typedef struct _PV_EA_CALLBACK +{ + HWND ListViewHandle; + ULONG Count; +} PV_EA_CALLBACK, *PPV_EA_CALLBACK; + +BOOLEAN NTAPI PvpEnumFileAttributesCallback( + _In_ PFILE_FULL_EA_INFORMATION Information, + _In_opt_ PVOID Context + ) +{ + PPV_EA_CALLBACK context = Context; + PPH_STRING attributeName; + INT lvItemIndex; + WCHAR number[PH_INT32_STR_LEN_1]; + + if (Information->EaNameLength == 0) + return TRUE; + + PhPrintUInt32(number, ++context->Count); + lvItemIndex = PhAddListViewItem(context->ListViewHandle, MAXINT, number, NULL); + + attributeName = PhZeroExtendToUtf16Ex(Information->EaName, Information->EaNameLength); + PhSetListViewSubItem( + context->ListViewHandle, + lvItemIndex, + 1, + attributeName->Buffer + ); + PhDereferenceObject(attributeName); + + PhSetListViewSubItem( + context->ListViewHandle, + lvItemIndex, + 2, + PhaFormatSize(Information->EaValueLength, ULONG_MAX)->Buffer + ); + + return TRUE; +} + +INT_PTR CALLBACK PvpPeExtendedAttributesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE fileHandle; + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 200, L"Value"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageAttributesListViewColumns", lvHandle); + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(PvFileName), + FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + PV_EA_CALLBACK context; + + context.ListViewHandle = lvHandle; + context.Count = 0; + + PhEnumFileExtendedAttributes(fileHandle, PvpEnumFileAttributesCallback, &context); + + NtClose(fileHandle); + } + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageAttributesListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/cfgprp.c b/tools/peview/cfgprp.c new file mode 100644 index 000000000000..7ad1d3eb66ff --- /dev/null +++ b/tools/peview/cfgprp.c @@ -0,0 +1,196 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +INT_PTR CALLBACK PvpPeCgfDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + PH_MAPPED_IMAGE_CFG cfgConfig = { 0 }; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_RIGHT, 80, L"VA"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 250, L"Name"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Flags"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageCfgListViewColumns", lvHandle); + + // Retrieve Cfg Table entry and characteristics + if (NT_SUCCESS(PhGetMappedImageCfg(&cfgConfig, &PvMappedImage))) + { + for (ULONGLONG i = 0; i < cfgConfig.NumberOfGuardFunctionEntries; i++) + { + INT lvItemIndex; + ULONG64 displacement; + PPH_STRING symbol; + PPH_STRING symbolName = NULL; + PH_SYMBOL_RESOLVE_LEVEL symbolResolveLevel = PhsrlInvalid; + IMAGE_CFG_ENTRY cfgFunctionEntry = { 0 }; + WCHAR number[PH_INT64_STR_LEN_1]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + // Parse cfg entry : if it fails, just skip it ? + if (!NT_SUCCESS(PhGetMappedImageCfgEntry(&cfgConfig, i, ControlFlowGuardFunction, &cfgFunctionEntry))) + continue; + + PhPrintUInt64(number, i + 1); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); + + PhPrintPointer(pointer, UlongToPtr(cfgFunctionEntry.Rva)); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); + + // Resolve name based on public symbols + + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + if (!(symbol = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)PTR_ADD_OFFSET(PvMappedImage.NtHeaders32->OptionalHeader.ImageBase, cfgFunctionEntry.Rva), + &symbolResolveLevel, + NULL, + &symbolName, + &displacement + ))) + { + continue; + } + } + else + { + if (!(symbol = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)PTR_ADD_OFFSET(PvMappedImage.NtHeaders->OptionalHeader.ImageBase, cfgFunctionEntry.Rva), + &symbolResolveLevel, + NULL, + &symbolName, + &displacement + ))) + { + continue; + } + } + + switch (symbolResolveLevel) + { + case PhsrlFunction: + { + if (displacement) + { + PhSetListViewSubItem( + lvHandle, + lvItemIndex, + 2, + PhaFormatString(L"%s+0x%x", symbolName->Buffer, displacement)->Buffer + ); + } + else + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, symbolName->Buffer); + } + } + break; + case PhsrlModule: + case PhsrlAddress: + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, symbol->Buffer); + } + break; + default: + case PhsrlInvalid: + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + } + break; + } + + // Add additional flags + if (cfgFunctionEntry.SuppressedCall) + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, L"SuppressedCall"); + else + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, L""); + + if (symbolName) + PhDereferenceObject(symbolName); + PhDereferenceObject(symbol); + } + } + + ExtendedListView_SortItems(lvHandle); + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageCfgListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, + PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), + dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/clrprp.c b/tools/peview/clrprp.c new file mode 100644 index 000000000000..4da668763c63 --- /dev/null +++ b/tools/peview/clrprp.c @@ -0,0 +1,287 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +// CLR structure reference: +// https://github.com/dotnet/coreclr/blob/master/src/md/inc/mdfileformat.h +// https://github.com/dotnet/coreclr/blob/master/src/utilcode/pedecoder.cpp +// https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/nidump.cpp + +#define STORAGE_MAGIC_SIG 0x424A5342 // BSJB + +#include +typedef struct _STORAGESIGNATURE +{ + ULONG Signature; + USHORT MajorVersion; + USHORT MinorVersion; + ULONG ExtraData; // Offset to next structure of information + ULONG VersionLength; // Length of version string + //UCHAR VersionString[1]; +} STORAGESIGNATURE, *PSTORAGESIGNATURE; + +typedef struct _STORAGEHEADER +{ + BYTE Flags; // STGHDR_xxx flags. + BYTE Reserved; + USHORT Streams; // How many streams are there. +} STORAGEHEADER, *PSTORAGEHEADER; + +typedef struct _STORAGESTREAM +{ + ULONG Offset; // Offset in file for this stream. + ULONG Size; // Size of the file. + CHAR Name[32]; // Start of name, null terminated. +} STORAGESTREAM, *PSTORAGESTREAM; +#include + +PSTORAGESIGNATURE PvpPeGetClrMetaDataHeader( + VOID + ) +{ + PSTORAGESIGNATURE metaData; + + metaData = PhMappedImageRvaToVa(&PvMappedImage, PvImageCor20Header->MetaData.VirtualAddress, NULL); + + if (metaData) + { + __try + { + PhProbeAddress(metaData, PvImageCor20Header->MetaData.Size, PvMappedImage.ViewBase, PvMappedImage.Size, 4); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + metaData = NULL; + } + } + + return metaData; +} + +PPH_STRING PvpPeGetClrFlagsText( + VOID + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 256); + + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_ILONLY) + PhAppendStringBuilder2(&stringBuilder, L"IL only, "); + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_32BITREQUIRED) + PhAppendStringBuilder2(&stringBuilder, L"32-bit only, "); + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_32BITPREFERRED) + PhAppendStringBuilder2(&stringBuilder, L"32-bit preferred, "); + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_IL_LIBRARY) + PhAppendStringBuilder2(&stringBuilder, L"IL library, "); + + if (PvImageCor20Header->StrongNameSignature.VirtualAddress != 0 && PvImageCor20Header->StrongNameSignature.Size != 0) + { + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_STRONGNAMESIGNED) + PhAppendStringBuilder2(&stringBuilder, L"Strong-name signed, "); + else + PhAppendStringBuilder2(&stringBuilder, L"Strong-name delay signed, "); + } + + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) + PhAppendStringBuilder2(&stringBuilder, L"Native entry-point, "); + if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_TRACKDEBUGDATA) + PhAppendStringBuilder2(&stringBuilder, L"Track debug data, "); + + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_STRING PvpPeGetClrVersionText( + VOID + ) +{ + return PhFormatString( + L"%hu.%hu", + PvImageCor20Header->MajorRuntimeVersion, + PvImageCor20Header->MinorRuntimeVersion + ); +} + +PPH_STRING PvpPeGetClrStorageVersionText( + _In_ PSTORAGESIGNATURE ClrMetaData + ) +{ + if (ClrMetaData && ClrMetaData->VersionLength != 0) + { + return PhZeroExtendToUtf16Ex( + PTR_ADD_OFFSET(ClrMetaData, RTL_SIZEOF_THROUGH_FIELD(STORAGESIGNATURE, VersionLength)), + ClrMetaData->VersionLength + ); + } + + return PhCreateString(L"N/A"); +} + +PPH_STRING PvpPeClrGetMvid( + _In_ PSTORAGESIGNATURE ClrMetaData + ) +{ + PPH_STRING guidMvidString = NULL; + PSTORAGEHEADER storageHeader; + PSTORAGESTREAM streamHeader; + USHORT i; + + storageHeader = PTR_ADD_OFFSET(ClrMetaData, sizeof(STORAGESIGNATURE) + ClrMetaData->VersionLength); + streamHeader = PTR_ADD_OFFSET(storageHeader, sizeof(STORAGEHEADER)); + + for (i = 0; i < storageHeader->Streams; i++) + { + if (PhEqualBytesZ(streamHeader->Name, "#GUID", TRUE)) + { + guidMvidString = PhFormatGuid(PTR_ADD_OFFSET(ClrMetaData, streamHeader->Offset)); + break; + } + + streamHeader = PTR_ADD_OFFSET(streamHeader, ALIGN_UP(UFIELD_OFFSET(STORAGESTREAM, Name) + strlen(streamHeader->Name) + 1, ULONG)); + } + + return guidMvidString; +} + +VOID PvpPeClrEnumSections( + _In_ PSTORAGESIGNATURE ClrMetaData, + _In_ HWND ListViewHandle + ) +{ + PSTORAGEHEADER storageHeader; + PSTORAGESTREAM streamHeader; + USHORT i; + + storageHeader = PTR_ADD_OFFSET(ClrMetaData, sizeof(STORAGESIGNATURE) + ClrMetaData->VersionLength); + streamHeader = PTR_ADD_OFFSET(storageHeader, sizeof(STORAGEHEADER)); + + for (i = 0; i < storageHeader->Streams; i++) + { + INT lvItemIndex; + WCHAR sectionName[65]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + if (PhCopyStringZFromBytes( + streamHeader->Name, + sizeof(streamHeader->Name), + sectionName, + ARRAYSIZE(sectionName), + NULL + )) + { + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, sectionName, NULL); + PhPrintPointer(pointer, UlongToPtr(streamHeader->Offset)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, pointer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, PhaFormatSize(streamHeader->Size, ULONG_MAX)->Buffer); + } + + // Stream headers don't have fixed sizes... + // The size is aligned up based on a variable length string at the end. + streamHeader = PTR_ADD_OFFSET(streamHeader, ALIGN_UP(UFIELD_OFFSET(STORAGESTREAM, Name) + strlen(streamHeader->Name) + 1, ULONG)); + } +} + + +INT_PTR CALLBACK PvpPeClrDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + PSTORAGESIGNATURE clrMetaData; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 80, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"VA"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Size"); + + PhSetDialogItemText(hwndDlg, IDC_RUNTIMEVERSION, PH_AUTO_T(PH_STRING, PvpPeGetClrVersionText())->Buffer); + PhSetDialogItemText(hwndDlg, IDC_FLAGS, PH_AUTO_T(PH_STRING, PvpPeGetClrFlagsText())->Buffer); + + if (clrMetaData = PvpPeGetClrMetaDataHeader()) + { + PhSetDialogItemText(hwndDlg, IDC_VERSIONSTRING, PH_AUTO_T(PH_STRING, PvpPeGetClrStorageVersionText(clrMetaData))->Buffer); + PhSetDialogItemText(hwndDlg, IDC_MVIDSTRING, PH_AUTO_T(PH_STRING, PvpPeClrGetMvid(clrMetaData))->Buffer); + + PvpPeClrEnumSections(clrMetaData, lvHandle); + } + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case PSN_QUERYINITIALFOCUS: + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_RUNTIMEVERSION)); + } + return TRUE; + } + + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/colmgr.c b/tools/peview/colmgr.c new file mode 100644 index 000000000000..a220a0fa3874 --- /dev/null +++ b/tools/peview/colmgr.c @@ -0,0 +1,600 @@ +/* + * Process Hacker - + * tree new column manager + * + * Copyright (C) 2011-2016 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include "colmgr.h" +//#include +//#include + +typedef struct _PH_CM_SORT_CONTEXT +{ + //PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction; + ULONG SubId; + PVOID Context; + PPH_CM_POST_SORT_FUNCTION PostSortFunction; + PH_SORT_ORDER SortOrder; +} PH_CM_SORT_CONTEXT, *PPH_CM_SORT_CONTEXT; + +/** + * Parses an application name and sub-ID pair. + * + * \param CompoundId The compound identifier. + * \param AppName A variable which receives the application name. + * \param SubId A variable which receives the sub-ID. + */ +BOOLEAN PhEmParseCompoundId( + _In_ PPH_STRINGREF CompoundId, + _Out_ PPH_STRINGREF AppName, + _Out_ PULONG SubId + ) +{ + PH_STRINGREF firstPart; + PH_STRINGREF secondPart; + ULONG64 integer; + + firstPart = *CompoundId; + + if (firstPart.Length == 0) + return FALSE; + if (firstPart.Buffer[0] != '+') + return FALSE; + + PhSkipStringRef(&firstPart, sizeof(WCHAR)); + PhSplitStringRefAtChar(&firstPart, '+', &firstPart, &secondPart); + + if (firstPart.Length == 0 || secondPart.Length == 0) + return FALSE; + + if (!PhStringToInteger64(&secondPart, 10, &integer)) + return FALSE; + + *AppName = firstPart; + *SubId = (ULONG)integer; + + return TRUE; +} + +VOID PhCmInitializeManager( + _Out_ PPH_CM_MANAGER Manager, + _In_ HWND Handle, + _In_ ULONG MinId, + _In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction + ) +{ + Manager->Handle = Handle; + Manager->MinId = MinId; + Manager->NextId = MinId; + Manager->PostSortFunction = PostSortFunction; + InitializeListHead(&Manager->ColumnListHead); + Manager->NotifyList = NULL; +} + +VOID PhCmDeleteManager( + _In_ PPH_CM_MANAGER Manager + ) +{ + PLIST_ENTRY listEntry; + PPH_CM_COLUMN column; + + listEntry = Manager->ColumnListHead.Flink; + + while (listEntry != &Manager->ColumnListHead) + { + column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry); + listEntry = listEntry->Flink; + + PhFree(column); + } + + if (Manager->NotifyList) + PhDereferenceObject(Manager->NotifyList); +} + +PPH_CM_COLUMN PhCmCreateColumn( + _Inout_ PPH_CM_MANAGER Manager, + _In_ PPH_TREENEW_COLUMN Column, + _In_ struct _PV_SYMBOL_NODE *Symbol, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_ PVOID SortFunction + ) +{ + PPH_CM_COLUMN column; + PH_TREENEW_COLUMN tnColumn; + + column = PhAllocate(sizeof(PH_CM_COLUMN)); + memset(column, 0, sizeof(PH_CM_COLUMN)); + column->Id = Manager->NextId++; + //column->Symbol = Symbol; + column->SubId = SubId; + column->Context = Context; + column->SortFunction = SortFunction; + InsertTailList(&Manager->ColumnListHead, &column->ListEntry); + + memset(&tnColumn, 0, sizeof(PH_TREENEW_COLUMN)); + tnColumn.Id = column->Id; + tnColumn.Context = column; + tnColumn.Visible = Column->Visible; + tnColumn.CustomDraw = Column->CustomDraw; + tnColumn.SortDescending = Column->SortDescending; + tnColumn.Text = Column->Text; + tnColumn.Width = Column->Width; + tnColumn.Alignment = Column->Alignment; + tnColumn.DisplayIndex = Column->Visible ? Column->DisplayIndex : ULONG_MAX; + tnColumn.TextFlags = Column->TextFlags; + TreeNew_AddColumn(Manager->Handle, &tnColumn); + + return column; +} + +VOID PhCmSetNotifyPlugin( + _In_ PPH_CM_MANAGER Manager, + _In_ struct _PH_PLUGIN *Plugin + ) +{ + if (!Manager->NotifyList) + { + Manager->NotifyList = PhCreateList(8); + } + else + { + if (PhFindItemList(Manager->NotifyList, Plugin) != ULONG_MAX) + return; + } + + PhAddItemList(Manager->NotifyList, Plugin); +} + +PPH_CM_COLUMN PhCmFindColumn( + _In_ PPH_CM_MANAGER Manager, + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ) +{ + PLIST_ENTRY listEntry; + PPH_CM_COLUMN column; + + listEntry = Manager->ColumnListHead.Flink; + + while (listEntry != &Manager->ColumnListHead) + { + column = CONTAINING_RECORD(listEntry, PH_CM_COLUMN, ListEntry); + + if (column->SubId == SubId && PhEqualStringRef(PluginName, &column->Symbol->Name->sr, FALSE)) + return column; + + listEntry = listEntry->Flink; + } + + return NULL; +} + +BOOLEAN PhCmLoadSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _In_ PPH_STRINGREF Settings, + _In_opt_ PPH_STRINGREF SortSettings + ) +{ + BOOLEAN result = FALSE; + PH_STRINGREF scalePart; + PH_STRINGREF columnPart; + PH_STRINGREF remainingColumnPart; + PH_STRINGREF valuePart; + PH_STRINGREF subPart; + ULONG64 integer; + ULONG scale; + ULONG total; + BOOLEAN hasFixedColumn; + ULONG count; + ULONG i; + PPH_HASHTABLE columnHashtable; + PH_HASHTABLE_ENUM_CONTEXT enumContext; + PPH_KEY_VALUE_PAIR pair; + LONG orderArray[PH_CM_ORDER_LIMIT]; + LONG maxOrder; + + if (Settings->Length != 0) + { + columnHashtable = PhCreateSimpleHashtable(20); + + remainingColumnPart = *Settings; + + if (remainingColumnPart.Length != 0 && remainingColumnPart.Buffer[0] == '@') + { + PhSkipStringRef(&remainingColumnPart, sizeof(WCHAR)); + PhSplitStringRefAtChar(&remainingColumnPart, '|', &scalePart, &remainingColumnPart); + + if (scalePart.Length == 0 || !PhStringToInteger64(&scalePart, 10, &integer)) + goto CleanupExit; + + scale = (ULONG)integer; + } + else + { + scale = PhGlobalDpi; + } + + while (remainingColumnPart.Length != 0) + { + PPH_TREENEW_COLUMN column; + ULONG id; + ULONG displayIndex; + ULONG width; + + PhSplitStringRefAtChar(&remainingColumnPart, '|', &columnPart, &remainingColumnPart); + + if (columnPart.Length != 0) + { + // Id + + PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); + + if (valuePart.Length == 0) + goto CleanupExit; + + if (valuePart.Buffer[0] == '+') + { + PH_STRINGREF pluginName; + ULONG subId; + PPH_CM_COLUMN cmColumn; + + // This is a plugin-owned column. + + if (!Manager) + continue; + if (!PhEmParseCompoundId(&valuePart, &pluginName, &subId)) + continue; + + cmColumn = PhCmFindColumn(Manager, &pluginName, subId); + + if (!cmColumn) + continue; // can't find the column, skip this part + + id = cmColumn->Id; + } + else + { + if (!PhStringToInteger64(&valuePart, 10, &integer)) + goto CleanupExit; + + id = (ULONG)integer; + } + + // Display Index + + PhSplitStringRefAtChar(&columnPart, ',', &valuePart, &columnPart); + + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + if (valuePart.Length == 0 || !PhStringToInteger64(&valuePart, 10, &integer)) + goto CleanupExit; + + displayIndex = (ULONG)integer; + } + else + { + if (valuePart.Length != 0) + goto CleanupExit; + + displayIndex = ULONG_MAX; + } + + // Width + + if (columnPart.Length == 0 || !PhStringToInteger64(&columnPart, 10, &integer)) + goto CleanupExit; + + width = (ULONG)integer; + + if (scale != PhGlobalDpi && scale != 0) + width = PhMultiplyDivide(width, PhGlobalDpi, scale); + + column = PhAllocate(sizeof(PH_TREENEW_COLUMN)); + column->Id = id; + column->DisplayIndex = displayIndex; + column->Width = width; + PhAddItemSimpleHashtable(columnHashtable, UlongToPtr(column->Id), column); + } + } + + TreeNew_SetRedraw(TreeNewHandle, FALSE); + + // Set visibility and width. + + i = 0; + count = 0; + total = TreeNew_GetColumnCount(TreeNewHandle); + hasFixedColumn = !!TreeNew_GetFixedColumn(TreeNewHandle); + memset(orderArray, 0, sizeof(orderArray)); + maxOrder = 0; + + while (count < total) + { + PH_TREENEW_COLUMN setColumn; + PPH_TREENEW_COLUMN *columnPtr; + + if (TreeNew_GetColumn(TreeNewHandle, i, &setColumn)) + { + columnPtr = (PPH_TREENEW_COLUMN *)PhFindItemSimpleHashtable(columnHashtable, UlongToPtr(i)); + + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + if (columnPtr) + { + setColumn.Visible = TRUE; + setColumn.Width = (*columnPtr)->Width; + TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE | TN_COLUMN_WIDTH, &setColumn); + + if (!setColumn.Fixed) + { + // For compatibility reasons, normal columns have their display indicies stored + // one higher than usual (so they start from 1, not 0). Fix that here. + if (hasFixedColumn && (*columnPtr)->DisplayIndex != 0) + (*columnPtr)->DisplayIndex--; + + if ((*columnPtr)->DisplayIndex < PH_CM_ORDER_LIMIT) + { + orderArray[(*columnPtr)->DisplayIndex] = i; + + if ((ULONG)maxOrder < (*columnPtr)->DisplayIndex + 1) + maxOrder = (*columnPtr)->DisplayIndex + 1; + } + } + } + else if (!setColumn.Fixed) // never hide the fixed column + { + setColumn.Visible = FALSE; + TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &setColumn); + } + } + else + { + if (columnPtr) + { + setColumn.Width = (*columnPtr)->Width; + TreeNew_SetColumn(TreeNewHandle, TN_COLUMN_WIDTH, &setColumn); + } + } + + count++; + } + + i++; + } + + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + // Set the order array. + TreeNew_SetColumnOrderArray(TreeNewHandle, maxOrder, orderArray); + } + + TreeNew_SetRedraw(TreeNewHandle, TRUE); + + result = TRUE; + +CleanupExit: + PhBeginEnumHashtable(columnHashtable, &enumContext); + + while (pair = PhNextEnumHashtable(&enumContext)) + PhFree(pair->Value); + + PhDereferenceObject(columnHashtable); + } + + // Load sort settings. + + if (SortSettings && SortSettings->Length != 0) + { + PhSplitStringRefAtChar(SortSettings, ',', &valuePart, &subPart); + + if (valuePart.Length != 0 && subPart.Length != 0) + { + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + sortColumn = ULONG_MAX; + + if (valuePart.Buffer[0] == '+') + { + PH_STRINGREF pluginName; + ULONG subId; + PPH_CM_COLUMN cmColumn; + + if ( + Manager && + PhEmParseCompoundId(&valuePart, &pluginName, &subId) && + (cmColumn = PhCmFindColumn(Manager, &pluginName, subId)) + ) + { + sortColumn = cmColumn->Id; + } + } + else + { + PhStringToInteger64(&valuePart, 10, &integer); + sortColumn = (ULONG)integer; + } + + PhStringToInteger64(&subPart, 10, &integer); + sortOrder = (PH_SORT_ORDER)integer; + + if (sortColumn != ULONG_MAX && sortOrder <= DescendingSortOrder) + { + TreeNew_SetSort(TreeNewHandle, sortColumn, sortOrder); + } + } + } + + return result; +} + +BOOLEAN PhCmLoadSettings( + _In_ HWND TreeNewHandle, + _In_ PPH_STRINGREF Settings + ) +{ + return PhCmLoadSettingsEx(TreeNewHandle, NULL, 0, Settings, NULL); +} + +PPH_STRING PhCmSaveSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _Out_opt_ PPH_STRING *SortSettings + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i = 0; + ULONG count = 0; + ULONG total; + ULONG increment; + PH_TREENEW_COLUMN column; + + total = TreeNew_GetColumnCount(TreeNewHandle); + + if (TreeNew_GetFixedColumn(TreeNewHandle)) + increment = 1; // the first normal column should have a display index that starts with 1, for compatibility + else + increment = 0; + + PhInitializeStringBuilder(&stringBuilder, 100); + + PhAppendFormatStringBuilder(&stringBuilder, L"@%u|", PhGlobalDpi); + + while (count < total) + { + if (TreeNew_GetColumn(TreeNewHandle, i, &column)) + { + if (!(Flags & PH_CM_COLUMN_WIDTHS_ONLY)) + { + if (column.Visible) + { + if (!Manager || i < Manager->MinId) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu,%lu,%lu|", + i, + column.Fixed ? 0 : column.DisplayIndex + increment, + column.Width + ); + } + else + { + PPH_CM_COLUMN cmColumn; + + cmColumn = column.Context; + PhAppendFormatStringBuilder( + &stringBuilder, + L"+%lu,%lu,%ld|", + cmColumn->SubId, + column.DisplayIndex + increment, + column.Width + ); + } + } + } + else + { + if (!Manager || i < Manager->MinId) + { + PhAppendFormatStringBuilder( + &stringBuilder, + L"%lu,,%ld|", + i, + column.Width + ); + } + else + { + PPH_CM_COLUMN cmColumn; + + cmColumn = column.Context; + PhAppendFormatStringBuilder( + &stringBuilder, + L"+%lu,,%ld|", + cmColumn->SubId, + column.Width + ); + } + } + + count++; + } + + i++; + } + + if (stringBuilder.String->Length != 0) + PhRemoveEndStringBuilder(&stringBuilder, 1); + + if (SortSettings) + { + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + if (TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder)) + { + if (sortOrder != NoSortOrder) + { + if (!Manager || sortColumn < Manager->MinId) + { + *SortSettings = PhFormatString(L"%lu,%lu", sortColumn, sortOrder); + } + else + { + PH_TREENEW_COLUMN column; + PPH_CM_COLUMN cmColumn; + + if (TreeNew_GetColumn(TreeNewHandle, sortColumn, &column)) + { + cmColumn = column.Context; + *SortSettings = PhFormatString(L"+%lu,%lu", cmColumn->SubId, sortOrder); + } + else + { + *SortSettings = PhReferenceEmptyString(); + } + } + } + else + { + *SortSettings = PhCreateString(L"0,0"); + } + } + else + { + *SortSettings = PhReferenceEmptyString(); + } + } + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_STRING PhCmSaveSettings( + _In_ HWND TreeNewHandle + ) +{ + return PhCmSaveSettingsEx(TreeNewHandle, NULL, 0, NULL); +} diff --git a/tools/peview/colmgr.h b/tools/peview/colmgr.h new file mode 100644 index 000000000000..e6c54cc03b22 --- /dev/null +++ b/tools/peview/colmgr.h @@ -0,0 +1,87 @@ +#ifndef PH_COLMGR_H +#define PH_COLMGR_H + +#define PH_CM_ORDER_LIMIT 160 + +// begin_phapppub +typedef LONG (NTAPI *PPH_CM_POST_SORT_FUNCTION)( + _In_ LONG Result, + _In_ PVOID Node1, + _In_ PVOID Node2, + _In_ PH_SORT_ORDER SortOrder + ); +// end_phapppub + +typedef struct _PH_CM_MANAGER +{ + HWND Handle; + ULONG MinId; + ULONG NextId; + PPH_CM_POST_SORT_FUNCTION PostSortFunction; + LIST_ENTRY ColumnListHead; + PPH_LIST NotifyList; +} PH_CM_MANAGER, *PPH_CM_MANAGER; + +typedef struct _PH_CM_COLUMN +{ + LIST_ENTRY ListEntry; + ULONG Id; + struct _PV_SYMBOL_NODE *Symbol; + ULONG SubId; + PVOID Context; + PVOID SortFunction; +} PH_CM_COLUMN, *PPH_CM_COLUMN; + +VOID PhCmInitializeManager( + _Out_ PPH_CM_MANAGER Manager, + _In_ HWND Handle, + _In_ ULONG MinId, + _In_ PPH_CM_POST_SORT_FUNCTION PostSortFunction + ); + +VOID PhCmDeleteManager( + _In_ PPH_CM_MANAGER Manager + ); + +PPH_CM_COLUMN PhCmCreateColumn( + _Inout_ PPH_CM_MANAGER Manager, + _In_ PPH_TREENEW_COLUMN Column, + _In_ struct _PV_SYMBOL_NODE *Plugin, + _In_ ULONG SubId, + _In_opt_ PVOID Context, + _In_ PVOID SortFunction + ); + +PPH_CM_COLUMN PhCmFindColumn( + _In_ PPH_CM_MANAGER Manager, + _In_ PPH_STRINGREF PluginName, + _In_ ULONG SubId + ); + +BOOLEAN PhCmLoadSettings( + _In_ HWND TreeNewHandle, + _In_ PPH_STRINGREF Settings + ); + +#define PH_CM_COLUMN_WIDTHS_ONLY 0x1 + +BOOLEAN PhCmLoadSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _In_ PPH_STRINGREF Settings, + _In_opt_ PPH_STRINGREF SortSettings + ); + +PPH_STRING PhCmSaveSettings( + _In_ HWND TreeNewHandle + ); + +PPH_STRING PhCmSaveSettingsEx( + _In_ HWND TreeNewHandle, + _In_opt_ PPH_CM_MANAGER Manager, + _In_ ULONG Flags, + _Out_opt_ PPH_STRING *SortSettings + ); + +#endif diff --git a/tools/peview/exlfdynamic.c b/tools/peview/exlfdynamic.c new file mode 100644 index 000000000000..995c2821685c --- /dev/null +++ b/tools/peview/exlfdynamic.c @@ -0,0 +1,233 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +PWSTR PvpGetDynamicTagName( + _In_ LONGLONG Tag + ) +{ + switch (Tag) + { + case DT_NULL: + return L"NULL"; + case DT_NEEDED: + return L"NEEDED"; + case DT_PLTRELSZ: + return L"PLTRELSZ"; + case DT_PLTGOT: + return L"PLTGOT"; + case DT_HASH: + return L"HASH"; + case DT_STRTAB: + return L"STRTAB"; + case DT_SYMTAB: + return L"SYMTAB"; + case DT_RELA: + return L"RELA"; + case DT_RELASZ: + return L"RELASZ"; + case DT_RELAENT: + return L"RELAENT"; + case DT_STRSZ: + return L"STRSZ"; + case DT_SYMENT: + return L"SYMENT"; + case DT_INIT: + return L"INIT"; + case DT_FINI: + return L"FINI"; + case DT_SONAME: + return L"SONAME"; + case DT_RPATH: + return L"RPATH"; + case DT_SYMBOLIC: + return L"SYMBOLIC"; + case DT_REL: + return L"REL"; + case DT_RELSZ: + return L"RELSZ"; + case DT_RELENT: + return L"RELENT"; + case DT_PLTREL: + return L"PLTREL"; + case DT_DEBUG: + return L"DEBUG"; + case DT_TEXTREL: + return L"TEXTREL"; + case DT_JMPREL: + return L"JMPREL"; + case DT_INIT_ARRAY: + return L"INIT_ARRAY"; + case DT_FINI_ARRAY: + return L"FINI_ARRAY"; + case DT_INIT_ARRAYSZ: + return L"INIT_ARRAYSZ"; + case DT_FINI_ARRAYSZ: + return L"FINI_ARRAYSZ"; + case DT_RUNPATH: + return L"RUNPATH"; + case DT_FLAGS: + return L"FLAGS"; + case DT_PREINIT_ARRAY: + return L"PREINIT_ARRAY"; + case DT_PREINIT_ARRAYSZ: + return L"PREINIT_ARRAYSZ"; + case OLD_DT_LOOS: + return L"OLD_DT_LOOS"; + case DT_LOOS: + return L"LOOS"; + case DT_HIOS: + return L"HIOS"; + case DT_VALRNGLO: + return L"VALRNGLO"; + case DT_VALRNGHI: + return L"VALRNGHI"; + case DT_ADDRRNGLO: + return L"ADDRRNGLO"; + case DT_ADDRRNGHI: + return L"ADDRRNGHI"; + case DT_GNU_HASH: + return L"GNU_HASH"; + case DT_VERSYM: + return L"VERSYM"; + case DT_RELACOUNT: + return L"RELACOUNT"; + case DT_RELCOUNT: + return L"RELCOUNT"; + case DT_FLAGS_1: + return L"FLAGS_1"; + case DT_VERDEF: + return L"VERDEF"; + case DT_VERDEFNUM: + return L"VERDEFNUM"; + case DT_VERNEED: + return L"VERNEED"; + case DT_VERNEEDNUM: + return L"VERNEEDNUM"; + case DT_LOPROC: + return L"LOPROC"; + case DT_HIPROC: + return L"HIPROC"; + } + + return L"***ERROR***"; +} + +VOID PvpProcessElfDynamic( + _In_ HWND ListViewHandle + ) +{ + PPH_LIST dynamics; + ULONG count = 0; + + PhGetMappedWslImageDynamic(&PvMappedImage, &dynamics); + + for (ULONG i = 0; i < dynamics->Count; i++) + { + PPH_ELF_IMAGE_DYNAMIC_ENTRY dynamic = dynamics->Items[i]; + INT lvItemIndex; + WCHAR number[PH_INT32_STR_LEN_1]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhPrintPointer(pointer, (PVOID)dynamic->Tag); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, pointer); + //PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, PhaFormatString(L"0x%016llx", (PVOID)dynamic->Tag)->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, PvpGetDynamicTagName(dynamic->Tag)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, PhGetStringOrEmpty(dynamic->Value)); + } + + PhFreeMappedWslImageDynamic(dynamics); +} + +INT_PTR CALLBACK PvpExlfDynamicDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_RIGHT, 80, L"Tag"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 150, L"Type"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 250, L"Value"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"DynamicWslListViewColumns", lvHandle); + + PvpProcessElfDynamic(lvHandle); + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"DynamicWslListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/exlfexports.c b/tools/peview/exlfexports.c new file mode 100644 index 000000000000..dc89c3b3c629 --- /dev/null +++ b/tools/peview/exlfexports.c @@ -0,0 +1,135 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpProcessElfExports( + _In_ HWND ListViewHandle + ) +{ + PPH_LIST exports; + ULONG count = 0; + + PhGetMappedWslImageSymbols(&PvMappedImage, &exports); + + for (ULONG i = 0; i < exports->Count; i++) + { + PPH_ELF_IMAGE_SYMBOL_ENTRY export = exports->Items[i]; + INT lvItemIndex; + WCHAR number[PH_INT32_STR_LEN_1]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + if (!export->ExportSymbol) + continue; + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhPrintPointer(pointer, (PVOID)export->Address); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, pointer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, export->Name); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, PhaFormatSize(export->Size, ULONG_MAX)->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, PvpGetSymbolTypeName(export->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 5, PvpGetSymbolBindingName(export->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 6, PvpGetSymbolVisibility(export->OtherInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 7, PvpGetSymbolSectionName(export->SectionIndex)->Buffer); + } + + PhFreeMappedWslImageSymbols(exports); +} + +INT_PTR CALLBACK PvpExlfExportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_RIGHT, 80, L"RVA"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 250, L"Name"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Size"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Type"); + PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Binding"); + PhAddListViewColumn(lvHandle, 6, 6, 6, LVCFMT_LEFT, 80, L"Visibility"); + PhAddListViewColumn(lvHandle, 7, 7, 7, LVCFMT_LEFT, 80, L"Section"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ExportsWslListViewColumns", lvHandle); + + PvpProcessElfExports(lvHandle); + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ExportsWslListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, + PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), + dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/exlfimports.c b/tools/peview/exlfimports.c new file mode 100644 index 000000000000..0c01daf154c9 --- /dev/null +++ b/tools/peview/exlfimports.c @@ -0,0 +1,128 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpProcessElfImports( + _In_ HWND ListViewHandle + ) +{ + PPH_LIST imports; + ULONG count = 0; + + PhGetMappedWslImageSymbols(&PvMappedImage, &imports); + + for (ULONG i = 0; i < imports->Count; i++) + { + PPH_ELF_IMAGE_SYMBOL_ENTRY import = imports->Items[i]; + INT lvItemIndex; + WCHAR number[PH_INT32_STR_LEN_1]; + + if (!import->ImportSymbol) + continue; + + PhPrintUInt64(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, import->Module); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, import->Name); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, PvpGetSymbolTypeName(import->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, PvpGetSymbolBindingName(import->TypeInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 5, PvpGetSymbolVisibility(import->OtherInfo)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 6, PvpGetSymbolSectionName(import->SectionIndex)->Buffer); + } + + PhFreeMappedWslImageSymbols(imports); +} + +INT_PTR CALLBACK PvpExlfImportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 130, L"Module"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 210, L"Name"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Type"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Binding"); + PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Visibility"); + PhAddListViewColumn(lvHandle, 6, 6, 6, LVCFMT_LEFT, 80, L"Section"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImportsWslListViewColumns", lvHandle); + + PvpProcessElfImports(lvHandle); + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImportsWslListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/exlfprp.c b/tools/peview/exlfprp.c new file mode 100644 index 000000000000..7fb7456a1418 --- /dev/null +++ b/tools/peview/exlfprp.c @@ -0,0 +1,505 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +PWSTR PvpGetSymbolTypeName( + _In_ UCHAR TypeInfo + ) +{ + switch (ELF_ST_TYPE(TypeInfo)) + { + case STT_NOTYPE: + return L"No type"; + case STT_OBJECT: + return L"Object"; + case STT_FUNC: + return L"Function"; + case STT_SECTION: + return L"Section"; + case STT_FILE: + return L"File"; + case STT_COMMON: + return L"Common"; + case STT_TLS: + return L"TLS"; + case STT_GNU_IFUNC: + return L"IFUNC"; + } + + return L"***ERROR***"; +} + +PWSTR PvpGetSymbolBindingName( + _In_ UCHAR TypeInfo + ) +{ + switch (ELF_ST_BIND(TypeInfo)) + { + case STB_LOCAL: + return L"Local"; + case STB_GLOBAL: + return L"Global"; + case STB_WEAK: + return L"Weak"; + case STB_GNU_UNIQUE: + return L"Unique"; + } + + return L"***ERROR***"; +} + +PWSTR PvpGetSymbolVisibility( + _In_ UCHAR OtherInfo + ) +{ + switch (ELF_ST_VISIBILITY(OtherInfo)) + { + case STV_DEFAULT: + return L"Default"; + case STV_INTERNAL: + return L"Internal"; + case STV_HIDDEN: + return L"Hidden"; + case STV_PROTECTED: + return L"Protected"; + } + + return L"***ERROR***"; +} + +PPH_STRING PvpGetSymbolSectionName( + _In_ ULONG Index + ) +{ + switch (Index) + { + case SHN_UNDEF: + return PhCreateString(L"UND"); + case SHN_ABS: + return PhCreateString(L"ABS"); + case SHN_COMMON: + return PhCreateString(L"Common"); + } + + return PhaFormatUInt64(Index, TRUE); +} + +VOID PvExlfProperties( + VOID + ) +{ + PPV_PROPCONTEXT propContext; + + if (!PhExtractIcon(PvFileName->Buffer, &PvImageLargeIcon, &PvImageSmallIcon)) + { + PhGetStockApplicationIcon(&PvImageSmallIcon, &PvImageLargeIcon); + } + + if (propContext = PvCreatePropContext(PvFileName)) + { + PPV_PROPPAGECONTEXT newPage; + + // General + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_ELFGENERAL), + PvpExlfGeneralDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Dynamic + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_ELFDYNAMIC), + PvpExlfDynamicDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Imports + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEIMPORTS), + PvpExlfImportsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Exports + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEEXPORTS), + PvpExlfExportsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Properties page + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEPROPSTORAGE), + PvpPePropStoreDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Extended attributes page + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEATTR), + PvpPeExtendedAttributesDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Streams page + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PESTREAMS), + PvpPeStreamsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + PhModalPropertySheet(&propContext->PropSheetHeader); + + PhDereferenceObject(propContext); + } +} + +static NTSTATUS PvpQueryWslImageThreadStart( + _In_ PVOID Parameter + ) +{ + HWND windowHandle = Parameter; + + PhInitializeLxssImageVersionInfo(&PvImageVersionInfo, PvFileName); + + PhSetDialogItemText(windowHandle, IDC_NAME, PvpGetStringOrNa(PvImageVersionInfo.FileDescription)); + PhSetDialogItemText(windowHandle, IDC_COMPANYNAME, PvpGetStringOrNa(PvImageVersionInfo.CompanyName)); + PhSetDialogItemText(windowHandle, IDC_VERSION, PvpGetStringOrNa(PvImageVersionInfo.FileVersion)); + + return STATUS_SUCCESS; +} + +VOID PvpSetWslmageVersionInfo( + _In_ HWND WindowHandle + ) +{ + PhSetDialogItemText(WindowHandle, IDC_NAME, L"Loading..."); + PhSetDialogItemText(WindowHandle, IDC_COMPANYNAME, L"Loading..."); + PhSetDialogItemText(WindowHandle, IDC_VERSION, L"Loading..."); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PvpQueryWslImageThreadStart, WindowHandle); + + Static_SetIcon(GetDlgItem(WindowHandle, IDC_FILEICON), PvImageLargeIcon); +} + +VOID PvpSetWslImageType( + _In_ HWND hwndDlg + ) +{ + PWSTR type = L"N/A"; + + switch (PvMappedImage.Header->e_type) + { + case ET_REL: + type = L"Relocatable"; + break; + case ET_DYN: + type = L"Dynamic"; + break; + case ET_EXEC: + type = L"Executable"; + break; + default: + type = L"ERROR"; + break; + } + + PhSetDialogItemText(hwndDlg, IDC_IMAGETYPE, type); +} + +VOID PvpSetWslImageMachineType( + _In_ HWND hwndDlg + ) +{ + PWSTR type = L"N/A"; + + switch (PvMappedImage.Header->e_machine) + { + case EM_386: + type = L"i386"; + break; + case EM_X86_64: + type = L"AMD64"; + break; + default: + type = L"ERROR"; + break; + } + + PhSetDialogItemText(hwndDlg, IDC_TARGETMACHINE, type); +} + +VOID PvpSetWslImageBase( + _In_ HWND hwndDlg + ) +{ + PPH_STRING string; + + if (PvMappedImage.Header->e_ident[EI_CLASS] == ELFCLASS32) + { + string = PhFormatString(L"0x%I32x", PhGetMappedWslImageBaseAddress(&PvMappedImage)); + PhSetDialogItemText(hwndDlg, IDC_IMAGEBASE, string->Buffer); + PhDereferenceObject(string); + } + else if (PvMappedImage.Header->e_ident[EI_CLASS] == ELFCLASS64) + { + string = PhFormatString(L"0x%I64x", PhGetMappedWslImageBaseAddress(&PvMappedImage)); + PhSetDialogItemText(hwndDlg, IDC_IMAGEBASE, string->Buffer); + PhDereferenceObject(string); + } +} + +VOID PvpSetWslEntrypoint( + _In_ HWND hwndDlg + ) +{ + PPH_STRING string; + + if (PvMappedImage.Header->e_ident[EI_CLASS] == ELFCLASS32) + { + string = PhFormatString(L"0x%I32x", PvMappedImage.Headers32->e_entry); + PhSetDialogItemText(hwndDlg, IDC_ENTRYPOINT, string->Buffer); + PhDereferenceObject(string); + } + else if (PvMappedImage.Header->e_ident[EI_CLASS] == ELFCLASS64) + { + string = PhFormatString(L"0x%I64x", PvMappedImage.Headers64->e_entry); + PhSetDialogItemText(hwndDlg, IDC_ENTRYPOINT, string->Buffer); + PhDereferenceObject(string); + } +} + +PWSTR PvpGetWslImageSectionTypeName( + _In_ UINT32 Type + ) +{ + switch (Type) + { + case SHT_NULL: + return L"NULL"; + case SHT_PROGBITS: + return L"PROGBITS"; + case SHT_SYMTAB: + return L"SYMTAB"; + case SHT_STRTAB: + return L"STRTAB"; + case SHT_RELA: + return L"RELA"; + case SHT_HASH: + return L"HASH"; + case SHT_DYNAMIC: + return L"DYNAMIC"; + case SHT_NOTE: + return L"NOTE"; + case SHT_NOBITS: + return L"NOBITS"; + case SHT_REL: + return L"REL"; + case SHT_SHLIB: + return L"SHLIB"; + case SHT_DYNSYM: + return L"DYNSYM"; + case SHT_NUM: + return L"NUM"; + case SHT_INIT_ARRAY: + return L"INIT_ARRAY"; + case SHT_FINI_ARRAY: + return L"FINI_ARRAY"; + case SHT_PREINIT_ARRAY: + return L"PREINIT_ARRAY"; + case SHT_GROUP: + return L"GROUP"; + case SHT_SYMTAB_SHNDX: + return L"SYMTAB_SHNDX"; + case SHT_GNU_INCREMENTAL_INPUTS: + return L"GNU_INCREMENTAL_INPUTS"; + case SHT_GNU_ATTRIBUTES: + return L"GNU_ATTRIBUTES"; + case SHT_GNU_HASH: + return L"GNU_HASH"; + case SHT_GNU_LIBLIST: + return L"GNU_LIBLIST"; + case SHT_SUNW_verdef: + return L"VERDEF"; + case SHT_SUNW_verneed: + return L"VERNEED"; + case SHT_SUNW_versym: + return L"VERSYM"; + } + + return L"***ERROR***"; +} + +PPH_STRING PvpGetWslImageSectionFlagsString( + _In_ ULONGLONG Flags + ) +{ + PH_STRING_BUILDER sb; + + PhInitializeStringBuilder(&sb, 100); + + if (Flags & SHF_ALLOC) + PhAppendStringBuilder2(&sb, L"Allocated, "); + + if (!(Flags & SHF_WRITE)) + PhAppendStringBuilder2(&sb, L"Read-only, "); + + if (Flags & SHF_EXECINSTR) + PhAppendStringBuilder2(&sb, L"Code, "); + else + PhAppendStringBuilder2(&sb, L"Data, "); + + if (sb.String->Length != 0) + PhRemoveEndStringBuilder(&sb, 2); + else + PhAppendStringBuilder2(&sb, L"(None)"); + + // TODO: The "objdump -h /bin/su --wide" command shows section flags + // such as CONTENT which appears to be based on ElfSectionType != SHT_NOBITS + // but I can't find the relevant source-code and check. + + return PhFinalStringBuilderString(&sb); +} + +VOID PvpLoadWslSections( + _In_ HWND LvHandle + ) +{ + USHORT sectionCount; + PPH_ELF_IMAGE_SECTION imageSections; + + if (PhGetMappedWslImageSections(&PvMappedImage, §ionCount, &imageSections)) + { + for (USHORT i = 0; i < sectionCount; i++) + { + INT lvItemIndex; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + if (!imageSections[i].Address && !imageSections[i].Size) + continue; + + lvItemIndex = PhAddListViewItem(LvHandle, MAXINT, imageSections[i].Name, NULL); + PhSetListViewSubItem(LvHandle, lvItemIndex, 1, PvpGetWslImageSectionTypeName(imageSections[i].Type)); + + PhPrintPointer(pointer, (PVOID)imageSections[i].Address); + PhSetListViewSubItem(LvHandle, lvItemIndex, 2, pointer); + + PhPrintPointer(pointer, (PVOID)imageSections[i].Offset); + PhSetListViewSubItem(LvHandle, lvItemIndex, 3, pointer); + + PhSetListViewSubItem(LvHandle, lvItemIndex, 4, PhaFormatSize(imageSections[i].Size, ULONG_MAX)->Buffer); + PhSetListViewSubItem(LvHandle, lvItemIndex, 5, PH_AUTO_T(PH_STRING, PvpGetWslImageSectionFlagsString(imageSections[i].Flags))->Buffer); + } + + PhFree(imageSections); + } +} + +INT_PTR CALLBACK PvpExlfGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 80, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Type"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"VA"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 80, L"Offset"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Size"); + PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 80, L"Flags"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"GeneralWslTreeListColumns", lvHandle); + + PvpSetWslmageVersionInfo(hwndDlg); + PvpSetWslImageType(hwndDlg); + PvpSetWslImageMachineType(hwndDlg); + PvpSetWslImageBase(hwndDlg); + PvpSetWslEntrypoint(hwndDlg); + + PvpLoadWslSections(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"GeneralWslTreeListColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILE), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_NAME), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMPANYNAME), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/expprp.c b/tools/peview/expprp.c new file mode 100644 index 000000000000..3354edb20e2b --- /dev/null +++ b/tools/peview/expprp.c @@ -0,0 +1,216 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +INT_PTR CALLBACK PvpPeExportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + PH_MAPPED_IMAGE_EXPORTS exports; + PH_MAPPED_IMAGE_EXPORT_ENTRY exportEntry; + PH_MAPPED_IMAGE_EXPORT_FUNCTION exportFunction; + ULONG i; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_RIGHT, 80, L"RVA"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 250, L"Name"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 50, L"Ordinal"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 50, L"Hint"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageExportsListViewColumns", lvHandle); + + if (NT_SUCCESS(PhGetMappedImageExports(&exports, &PvMappedImage))) + { + for (i = 0; i < exports.NumberOfEntries; i++) + { + if ( + NT_SUCCESS(PhGetMappedImageExportEntry(&exports, i, &exportEntry)) && + NT_SUCCESS(PhGetMappedImageExportFunction(&exports, NULL, exportEntry.Ordinal, &exportFunction)) + ) + { + INT lvItemIndex; + WCHAR number[PH_INT32_STR_LEN_1]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + PhPrintUInt32(number, i + 1); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); + + if (exportFunction.ForwardedName) + { + PPH_STRING forwardName; + + forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); + + if (forwardName->Buffer[0] == '?') + { + PPH_STRING undecoratedName; + + if (undecoratedName = PhUndecorateSymbolName(PvSymbolProvider, forwardName->Buffer)) + PhMoveReference(&forwardName, undecoratedName); + } + + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, forwardName->Buffer); + PhDereferenceObject(forwardName); + } + else + { + PhPrintPointer(pointer, exportFunction.Function); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); + } + + if (exportEntry.Name) + { + PPH_STRING exportName; + + exportName = PhZeroExtendToUtf16(exportEntry.Name); + + if (exportName->Buffer[0] == '?') + { + PPH_STRING undecoratedName; + + if (undecoratedName = PhUndecorateSymbolName(PvSymbolProvider, exportName->Buffer)) + PhMoveReference(&exportName, undecoratedName); + } + + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, exportName->Buffer); + PhDereferenceObject(exportName); + } + else + { + if (exportFunction.Function) + { + PPH_STRING exportSymbol = NULL; + PPH_STRING exportSymbolName = NULL; + + // Try find the export name using symbols. + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + exportSymbol = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)PTR_ADD_OFFSET(PvMappedImage.NtHeaders32->OptionalHeader.ImageBase, exportFunction.Function), + NULL, + NULL, + &exportSymbolName, + NULL + ); + } + else + { + exportSymbol = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)PTR_ADD_OFFSET(PvMappedImage.NtHeaders->OptionalHeader.ImageBase, exportFunction.Function), + NULL, + NULL, + &exportSymbolName, + NULL + ); + } + + if (exportSymbolName) + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, PH_AUTO_T(PH_STRING, PhConcatStringRefZ(&exportSymbolName->sr, L" (unnamed)"))->Buffer); + PhDereferenceObject(exportSymbolName); + } + else + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + } + + if (exportSymbol) + PhDereferenceObject(exportSymbol); + } + else + { + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); + } + } + + PhPrintUInt32(number, exportEntry.Ordinal); + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, number); + + if (exportEntry.Name) // Note: The 'Hint' is only valid for named exports. (dmex) + { + PhPrintUInt32(number, exportEntry.Hint); + PhSetListViewSubItem(lvHandle, lvItemIndex, 4, number); + } + } + } + } + + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageExportsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/impprp.c b/tools/peview/impprp.c new file mode 100644 index 000000000000..46c872aeee3a --- /dev/null +++ b/tools/peview/impprp.c @@ -0,0 +1,486 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +PPH_STRING PvpQueryModuleOrdinalName( + _In_ PPH_STRING FileName, + _In_ USHORT Ordinal + ) +{ + PPH_STRING exportName = NULL; + PH_MAPPED_IMAGE mappedImage; + + if (NT_SUCCESS(PhLoadMappedImage(FileName->Buffer, NULL, TRUE, &mappedImage))) + { + PH_MAPPED_IMAGE_EXPORTS exports; + PH_MAPPED_IMAGE_EXPORT_ENTRY exportEntry; + PH_MAPPED_IMAGE_EXPORT_FUNCTION exportFunction; + ULONG i; + + if (NT_SUCCESS(PhGetMappedImageExports(&exports, &mappedImage))) + { + for (i = 0; i < exports.NumberOfEntries; i++) + { + if ( + NT_SUCCESS(PhGetMappedImageExportEntry(&exports, i, &exportEntry)) && + NT_SUCCESS(PhGetMappedImageExportFunction(&exports, NULL, exportEntry.Ordinal, &exportFunction)) + ) + { + if (exportEntry.Ordinal == Ordinal) + { + if (exportEntry.Name) + { + exportName = PhZeroExtendToUtf16(exportEntry.Name); + + if (exportName->Buffer[0] == '?') + { + PPH_STRING undecoratedName; + + if (undecoratedName = PhUndecorateSymbolName(PvSymbolProvider, exportName->Buffer)) + PhMoveReference(&exportName, undecoratedName); + } + } + else + { + if (exportFunction.ForwardedName) + { + PPH_STRING forwardName; + + forwardName = PhZeroExtendToUtf16(exportFunction.ForwardedName); + + if (forwardName->Buffer[0] == '?') + { + PPH_STRING undecoratedName; + + if (undecoratedName = PhUndecorateSymbolName(PvSymbolProvider, forwardName->Buffer)) + PhMoveReference(&forwardName, undecoratedName); + } + + PhMoveReference(&exportName, PhFormatString(L"%s (Forwarded)", forwardName->Buffer)); + PhDereferenceObject(forwardName); + } + else if (exportFunction.Function) + { + PPH_STRING exportSymbol = NULL; + PPH_STRING exportSymbolName = NULL; + + if (PhLoadModuleSymbolProvider( + PvSymbolProvider, + FileName->Buffer, + (ULONG64)mappedImage.ViewBase, + (ULONG)mappedImage.Size + )) + { + // Try find the export name using symbols. + exportSymbol = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)PTR_ADD_OFFSET(mappedImage.ViewBase, exportFunction.Function), + NULL, + NULL, + &exportSymbolName, + NULL + ); + } + + if (exportSymbolName) + { + PhSetReference(&exportName, exportSymbolName); + PhDereferenceObject(exportSymbolName); + } + + if (exportSymbol) + PhDereferenceObject(exportSymbol); + } + } + + break; + } + } + } + } + + PhUnloadMappedImage(&mappedImage); + } + + return exportName; +} + +INT PvpAddListViewGroup( + _In_ HWND ListViewHandle, + _In_ PWSTR Text + ) +{ + LVGROUP group; + + memset(&group, 0, sizeof(LVGROUP)); + group.cbSize = sizeof(LVGROUP); + group.mask = LVGF_HEADER | LVGF_ALIGN | LVGF_STATE | LVGF_GROUPID | LVGF_TASK; + group.uAlign = LVGA_HEADER_LEFT; + group.state = LVGS_COLLAPSIBLE; + group.iGroupId = (INT)ListView_GetGroupCount(ListViewHandle); + group.pszHeader = Text; + group.pszTask = L"Properties"; + + return (INT)ListView_InsertGroup(ListViewHandle, group.iGroupId, &group); +} + +BOOLEAN PvpCheckGroupExists( + _In_ HWND ListViewHandle, + _In_ PWSTR GroupText + ) +{ + BOOLEAN exists = FALSE; + INT groupCount; + + groupCount = (INT)ListView_GetGroupCount(ListViewHandle); + + for (INT i = 0; i < groupCount; i++) + { + LVGROUP group; + WCHAR headerText[MAX_PATH] = L""; + + memset(&group, 0, sizeof(LVGROUP)); + group.cbSize = sizeof(LVGROUP); + group.mask = LVGF_HEADER; + group.cchHeader = RTL_NUMBER_OF(headerText); + group.pszHeader = headerText; + + if (ListView_GetGroupInfoByIndex(ListViewHandle, i, &group)) + { + if (PhEqualStringZ(headerText, GroupText, TRUE)) + { + exists = TRUE; + break; + } + } + } + + return exists; +} + +INT PvpGroupIndex( + _In_ HWND ListViewHandle, + _In_ PWSTR GroupText + ) +{ + INT groupIndex = MAXINT; + INT groupCount; + + groupCount = (INT)ListView_GetGroupCount(ListViewHandle); + + for (INT i = 0; i < groupCount; i++) + { + LVGROUP group; + WCHAR headerText[MAX_PATH] = L""; + + memset(&group, 0, sizeof(LVGROUP)); + group.cbSize = sizeof(LVGROUP); + group.mask = LVGF_HEADER | LVGF_GROUPID; + group.cchHeader = RTL_NUMBER_OF(headerText); + group.pszHeader = headerText; + + if (ListView_GetGroupInfoByIndex(ListViewHandle, i, &group)) + { + if (PhEqualStringZ(headerText, GroupText, TRUE)) + { + groupIndex = group.iGroupId; + break; + } + } + } + + return groupIndex; +} + +VOID PvpProcessImports( + _In_ HWND ListViewHandle, + _In_ PPH_MAPPED_IMAGE_IMPORTS Imports, + _In_ BOOLEAN DelayImports, + _Inout_ ULONG *Count + ) +{ + PH_MAPPED_IMAGE_IMPORT_DLL importDll; + PH_MAPPED_IMAGE_IMPORT_ENTRY importEntry; + ULONG i; + ULONG j; + INT groupCount = 0; + + for (i = 0; i < Imports->NumberOfDlls; i++) + { + if (NT_SUCCESS(PhGetMappedImageImportDll(Imports, i, &importDll))) + { + for (j = 0; j < importDll.NumberOfEntries; j++) + { + if (NT_SUCCESS(PhGetMappedImageImportEntry(&importDll, j, &importEntry))) + { + INT groupId = INT_MAX; + INT lvItemIndex = INT_MAX; + PPH_STRING name = NULL; + WCHAR number[PH_INT32_STR_LEN_1]; + + if (DelayImports) + name = PhFormatString(L"%S (Delay)", importDll.Name); + else + name = PhZeroExtendToUtf16(importDll.Name); + + if (PvpCheckGroupExists(ListViewHandle, name->Buffer)) + { + groupId = PvpGroupIndex(ListViewHandle, name->Buffer); + } + else + { + groupId = PvpAddListViewGroup(ListViewHandle, name->Buffer); + } + + PhPrintUInt32(number, ++(*Count)); // HACK + lvItemIndex = PhAddListViewGroupItem(ListViewHandle, groupId, MAXINT, number, NULL); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); + PhDereferenceObject(name); + + if (importEntry.Name) + { + PPH_STRING importName; + + importName = PhZeroExtendToUtf16(importEntry.Name); + + if (importName->Buffer[0] == '?') + { + PPH_STRING undecoratedName; + + if (undecoratedName = PhUndecorateSymbolName(PvSymbolProvider, importName->Buffer)) + PhMoveReference(&importName, undecoratedName); + } + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, importName->Buffer); + PhDereferenceObject(importName); + + PhPrintUInt32(number, importEntry.NameHint); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, number); + } + else + { + PPH_STRING exportDllName; + PPH_STRING exportOrdinalName = NULL; + + if (exportDllName = PhConvertUtf8ToUtf16(importDll.Name)) + { + PPH_STRING filePath; + + // TODO: Implement ApiSet mappings for exportDllName. (dmex) + // TODO: Add DLL directory to PhSearchFilePath for locating non-system images. (dmex) + + if (filePath = PhSearchFilePath(exportDllName->Buffer, L".dll")) + { + PhMoveReference(&exportDllName, filePath); + } + + exportOrdinalName = PvpQueryModuleOrdinalName(exportDllName, importEntry.Ordinal); + PhDereferenceObject(exportDllName); + } + + if (exportOrdinalName) + { + name = PhaFormatString(L"%s (Ordinal %u)", PhGetStringOrEmpty(exportOrdinalName), importEntry.Ordinal); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, PhGetString(name)); + PhDereferenceObject(exportOrdinalName); + } + else + { + name = PhaFormatString(L"(Ordinal %u)", importEntry.Ordinal); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, name->Buffer); + } + } + } + } + } + } +} + +INT_PTR CALLBACK PvpPeImportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG count = 0; + ULONG fallbackColumns[] = { 0, 1, 2 }; + HWND lvHandle; + PH_MAPPED_IMAGE_IMPORTS imports; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 130, L"DLL"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 210, L"Name"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 50, L"Hint"); + PhSetExtendedListView(lvHandle); + ExtendedListView_AddFallbackColumns(lvHandle, 3, fallbackColumns); + PhLoadListViewColumnsFromSetting(L"ImageImportsListViewColumns", lvHandle); + + ExtendedListView_SetRedraw(lvHandle, FALSE); + ListView_EnableGroupView(lvHandle, TRUE); + + if (NT_SUCCESS(PhGetMappedImageImports(&imports, &PvMappedImage))) + { + PvpProcessImports(lvHandle, &imports, FALSE, &count); + } + + if (NT_SUCCESS(PhGetMappedImageDelayImports(&imports, &PvMappedImage))) + { + PvpProcessImports(lvHandle, &imports, TRUE, &count); + } + + ExtendedListView_SortItems(lvHandle); + ExtendedListView_SetRedraw(lvHandle, TRUE); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageImportsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case LVN_LINKCLICK: + { + PNMLVLINK lvLinkInfo = (PNMLVLINK)lParam; + PPH_STRING applicationFileName; + LVGROUP groupInfo; + WCHAR headerText[MAX_PATH] = L""; + + memset(&groupInfo, 0, sizeof(LVGROUP)); + groupInfo.cbSize = sizeof(LVGROUP); + groupInfo.mask = LVGF_HEADER | LVGF_TASK; + groupInfo.cchHeader = RTL_NUMBER_OF(headerText); + groupInfo.pszHeader = headerText; + + ListView_GetGroupInfoByIndex(lvLinkInfo->hdr.hwndFrom, lvLinkInfo->iSubItem, &groupInfo); + + memset(&groupInfo, 0, sizeof(LVGROUP)); + groupInfo.cbSize = sizeof(LVGROUP); + groupInfo.mask = LVGF_TASK; + + if (ListView_GetGroupState(lvLinkInfo->hdr.hwndFrom, lvLinkInfo->iSubItem, LVGS_COLLAPSED)) + { + //ListView_SetGroupState(lvLinkInfo->hdr.hwndFrom, lvLinkInfo->iSubItem, LVGS_COLLAPSED, 0); + groupInfo.pszTask = L"Properties"; + } + else + { + //ListView_SetGroupState(lvLinkInfo->hdr.hwndFrom, lvLinkInfo->iSubItem, LVGS_COLLAPSED, LVGS_COLLAPSED); + groupInfo.pszTask = L"Properties"; + } + + ListView_SetGroupInfo(lvLinkInfo->hdr.hwndFrom, lvLinkInfo->iSubItem, &groupInfo); + + if (applicationFileName = PhGetApplicationFileName()) + { + PH_STRINGREF delayTextSr = PH_STRINGREF_INIT(L" (Delay)"); + PH_STRINGREF headerTextSr; + PPH_STRING fileName; + PPH_STRING filePath; + + PhInitializeStringRefLongHint(&headerTextSr, headerText); + + if (PhEndsWithStringRef(&headerTextSr, &delayTextSr, TRUE)) + { + headerTextSr.Length -= delayTextSr.Length; + } + + fileName = PhCreateString2(&headerTextSr); + + if (filePath = PhSearchFilePath(fileName->Buffer, NULL)) + { + PhMoveReference(&filePath, PhConcatStrings(3, L"\"", filePath->Buffer, L"\"")); + + PhShellExecuteEx( + hwndDlg, + PhGetString(applicationFileName), + PhGetString(filePath), + SW_SHOWNORMAL, + 0, + 0, + NULL + ); + + PhDereferenceObject(filePath); + } + else + { + PhShowStatus(hwndDlg, L"Unable to locate the DLL", 0, ERROR_FILE_NOT_FOUND); + } + + PhDereferenceObject(fileName); + PhDereferenceObject(applicationFileName); + } + break; + } + } + + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/include/pdb.h b/tools/peview/include/pdb.h new file mode 100644 index 000000000000..0ecc68302afa --- /dev/null +++ b/tools/peview/include/pdb.h @@ -0,0 +1,568 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef PV_PDB_H +#define PV_PDB_H + +// Originally based on dbghelptypeinfo by Oleg Starodumov: +// http://www.debuginfo.com/articles/dbghelptypeinfo.html + +#define _NO_CVCONST_H +#define DBGHELP_TRANSLATE_TCHAR +#include + +// Types from Cvconst.h (DIA SDK) +#ifdef _NO_CVCONST_H + +// BasicType, originally from CVCONST.H in DIA SDK +typedef enum _BasicType +{ + btNoType = 0, + btVoid = 1, + btChar = 2, + btWChar = 3, + btInt = 6, + btUInt = 7, + btFloat = 8, + btBCD = 9, + btBool = 10, + btLong = 13, + btULong = 14, + btCurrency = 25, + btDate = 26, + btVariant = 27, + btComplex = 28, + btBit = 29, + btBSTR = 30, + btHresult = 31 +} BasicType; + +// UDtKind, originally from CVCONST.H in DIA SDK +typedef enum _UdtKind +{ + UdtStruct, + UdtClass, + UdtUnion +} UdtKind; + +// CV_call_e, originally from CVCONST.H in DIA SDK +typedef enum _CV_call_e +{ + CV_CALL_NEAR_C = 0x00, // near right to left push, caller pops stack + CV_CALL_FAR_C = 0x01, // far right to left push, caller pops stack + CV_CALL_NEAR_PASCAL = 0x02, // near left to right push, callee pops stack + CV_CALL_FAR_PASCAL = 0x03, // far left to right push, callee pops stack + CV_CALL_NEAR_FAST = 0x04, // near left to right push with regs, callee pops stack + CV_CALL_FAR_FAST = 0x05, // far left to right push with regs, callee pops stack + CV_CALL_SKIPPED = 0x06, // skipped (unused) call index + CV_CALL_NEAR_STD = 0x07, // near standard call + CV_CALL_FAR_STD = 0x08, // far standard call + CV_CALL_NEAR_SYS = 0x09, // near sys call + CV_CALL_FAR_SYS = 0x0a, // far sys call + CV_CALL_THISCALL = 0x0b, // this call (this passed in register) + CV_CALL_MIPSCALL = 0x0c, // Mips call + CV_CALL_GENERIC = 0x0d, // Generic call sequence + CV_CALL_ALPHACALL = 0x0e, // Alpha call + CV_CALL_PPCCALL = 0x0f, // PPC call + CV_CALL_SHCALL = 0x10, // Hitachi SuperH call + CV_CALL_ARMCALL = 0x11, // ARM call + CV_CALL_AM33CALL = 0x12, // AM33 call + CV_CALL_TRICALL = 0x13, // TriCore Call + CV_CALL_SH5CALL = 0x14, // Hitachi SuperH-5 call + CV_CALL_M32RCALL = 0x15, // M32R Call + CV_CALL_RESERVED = 0x16 // first unused call enumeration +} CV_call_e; + +// DataKind, originally from CVCONST.H in DIA SDK +typedef enum _DataKind +{ + DataIsUnknown, + DataIsLocal, + DataIsStaticLocal, + DataIsParam, + DataIsObjectPtr, + DataIsFileStatic, + DataIsGlobal, + DataIsMember, + DataIsStaticMember, + DataIsConstant +} DataKind; + +// CV_HREG_e, originally from CVCONST.H in DIA SDK +typedef enum _CV_HREG_e +{ + // Only a limited number of registers included here + + CV_REG_EAX = 17, + CV_REG_ECX = 18, + CV_REG_EDX = 19, + CV_REG_EBX = 20, + CV_REG_ESP = 21, + CV_REG_EBP = 22, + CV_REG_ESI = 23, + CV_REG_EDI = 24, +} CV_HREG_e; + +// LocatonType, originally from CVCONST.H in DIA SDK +typedef enum _LocationType +{ + LocIsNull, + LocIsStatic, + LocIsTLS, + LocIsRegRel, + LocIsThisRel, + LocIsEnregistered, + LocIsBitField, + LocIsSlot, + LocIsIlRel, + LocInMetaData, + LocIsConstant, + LocTypeMax +} LocationType; + +#endif // _NO_CVCONST_H + +// Maximal length of name buffers (in characters) +#define TIS_MAXNAMELEN 256 +// Maximal number of children (member variables, member functions, base classes, etc.) +#define TIS_MAXNUMCHILDREN 64 +// Maximal number of dimensions of an array +#define TIS_MAXARRAYDIMS 64 + +// SymTagBaseType symbol +typedef struct _BaseTypeInfo +{ + // Basic type (DIA: baseType) + BasicType BaseType; + + // Length (in bytes) (DIA: length) + ULONG64 Length; +} BaseTypeInfo; + +// SymTagTypedef symbol +typedef struct _TypedefInfo +{ + // Name (DIA: name) + WCHAR Name[TIS_MAXNAMELEN]; + + // Index of the underlying type (DIA: typeId) + ULONG TypeIndex; +} TypedefInfo; + +// SymTagPointerType symbol +typedef struct _PointerTypeInfo +{ + // Length (in bytes) (DIA: length) + ULONG64 Length; + + // Index of the type the pointer points to (DIA: typeId) + ULONG TypeIndex; + + // Reference ("reference" in DIA) + BOOL TypeReference; +} PointerTypeInfo; + +// SymTagUDT symbol (Class or structure) +typedef struct _UdtClassInfo +{ + // Name (DIA: name) + WCHAR Name[TIS_MAXNAMELEN]; + + // Length (DIA: length) + ULONG64 Length; + + // UDT kind (class, structure or union) (DIA: udtKind) + UdtKind UDTKind; + + // Nested ("true" if the declaration is nested in another UDT) (DIA: nested) + BOOL Nested; + + // Number of member variables + ULONG NumVariables; + + // Member variables + ULONG Variables[TIS_MAXNUMCHILDREN]; + + // Number of member functions + ULONG NumFunctions; + + // Member functions + ULONG Functions[TIS_MAXNUMCHILDREN]; + + // Number of base classes + ULONG NumBaseClasses; + + // Base classes + ULONG BaseClasses[TIS_MAXNUMCHILDREN]; +} UdtClassInfo; + +// SymTagUDT symbol (Union) +typedef struct _UdtUnionInfo +{ + // Name (DIA: name) + WCHAR Name[TIS_MAXNAMELEN]; + + // Length (in bytes) (DIA: length) + ULONG64 Length; + + // UDT kind (class, structure or union) (DIA: udtKind) + UdtKind UDTKind; + + // Nested ("true" if the declaration is nested in another UDT) (DIA: nested) + BOOL Nested; + + // Number of members + ULONG NumMembers; + + // Members + ULONG Members[TIS_MAXNUMCHILDREN]; +} UdtUnionInfo; + +// SymTagBaseClass symbol +typedef struct _BaseClassInfo +{ + // Index of the UDT symbol that represents the base class (DIA: type) + ULONG TypeIndex; + + // Virtual ("true" if the base class is a virtual base class) (DIA: virtualBaseClass) + BOOL Virtual; + + // Offset of the base class within the class/structure (DIA: offset) + // (defined only if Virtual is "false") + LONG Offset; + + // Virtual base pointer offset (DIA: virtualBasePointerOffset) + // (defined only if Virtual is "true") + LONG VirtualBasePointerOffset; +} BaseClassInfo; + +// SymTagEnum symbol +typedef struct _EnumInfo +{ + // Name (DIA: name) + WCHAR Name[TIS_MAXNAMELEN]; + + // Index of the symbol that represent the type of the enumerators (DIA: typeId) + ULONG TypeIndex; + + // Nested ("true" if the declaration is nested in a UDT) (DIA: nested) + BOOL Nested; + + // Number of enumerators + ULONG NumEnums; + + // Enumerators (their type indices) + ULONG Enums[TIS_MAXNUMCHILDREN]; +} EnumInfo; + +// SymTagArrayType symbol +typedef struct _ArrayTypeInfo +{ + // Index of the symbol that represents the type of the array element + ULONG ElementTypeIndex; + + // Index of the symbol that represents the type of the array index (DIA: arrayIndexTypeId) + ULONG IndexTypeIndex; + + // Size of the array (in bytes) (DIA: length) + ULONG64 Length; + + // Number of dimensions + ULONG NumDimensions; + + // Dimensions + ULONG64 Dimensions[TIS_MAXARRAYDIMS]; +} ArrayTypeInfo; + +// SymTagFunctionType +typedef struct FunctionTypeInfo +{ + // Index of the return value type symbol (DIA: objectPointerType) + ULONG RetTypeIndex; + + // Number of arguments (DIA: count) + ULONG NumArgs; + + // Function arguments + ULONG Args[TIS_MAXNUMCHILDREN]; + + // Calling convention (DIA: callingConvention) + CV_call_e CallConv; + + // "Is member function" flag (member function, if "true") + BOOL MemberFunction; + + // Class symbol index (DIA: classParent) + // (defined only if MemberFunction is "true") + ULONG ClassIndex; + + // "this" adjustment (DIA: thisAdjust) + // (defined only if MemberFunction is "true") + LONG ThisAdjust; + + // "Is static function" flag (static, if "true") + // (defined only if MemberFunction is "true") + BOOL StaticFunction; +} FunctionTypeInfo; + +// SymTagFunctionArgType +typedef struct FunctionArgTypeInfo +{ + // Index of the symbol that represents the type of the argument (DIA: typeId) + ULONG TypeIndex; +} FunctionArgTypeInfo; + +// SymTagData +typedef struct DataInfo +{ + // Name (DIA: name) + WCHAR Name[TIS_MAXNAMELEN]; + + // Index of the symbol that represents the type of the variable (DIA: type) + ULONG TypeIndex; + + // Data kind (local, global, member, etc.) (DIA: dataKind) + DataKind dataKind; + + // Address (defined if dataKind is: DataIsGlobal, DataIsStaticLocal, + // DataIsFileStatic, DataIsStaticMember) (DIA: address) + ULONG64 Address; + + // Offset (defined if dataKind is: DataIsLocal, DataIsParam, + // DataIsObjectPtr, DataIsMember) (DIA: offset) + ULONG Offset; // Verify it for all listed data kinds + + // Note: Length is not available - use the type symbol to obtain it +} DataInfo; + +typedef struct _TypeInfo +{ + // Symbol tag + enum SymTagEnum Tag; + + // UDT kind (defined only if "Tag" is SymTagUDT: "true" if the symbol is + // a class or a structure, "false" if the symbol is a union) + BOOL UdtKind; + + union + { + BaseTypeInfo BaseTypeInfo; // If Tag == SymTagBaseType + TypedefInfo TypedefInfo; // If Tag == SymTagTypedef + PointerTypeInfo PointerTypeInfo; // If Tag == SymTagPointerType + UdtClassInfo UdtClassInfo; // If Tag == SymTagUDT and UdtKind is "true" + UdtUnionInfo UdtUnionInfo; // If Tag == SymTagUDT and UdtKind is "false" + BaseClassInfo BaseClassInfo; // If Tag == SymTagBaseClass + EnumInfo EnumInfo; // If Tag == SymTagEnum + ArrayTypeInfo ArrayTypeInfo; // If Tag == SymTagArrayType + FunctionTypeInfo FunctionTypeInfo; // If Tag == SymTagFunctionType + FunctionArgTypeInfo FunctionArgTypeInfo; // If Tag == SymTagFunctionArgType + DataInfo DataInfo; // If Tag == SymTagData + }; +} TypeInfo; + +BOOLEAN PdbGetSymbolBasicType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ BaseTypeInfo* Info + ); + +BOOLEAN PdbGetSymbolPointerType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ PointerTypeInfo* Info + ); + +BOOLEAN PdbGetSymbolTypedefType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ TypedefInfo* Info + ); + +BOOLEAN PdbGetSymbolEnumType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ EnumInfo* Info + ); + +BOOLEAN PdbGetSymbolArrayType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ArrayTypeInfo* Info + ); + +BOOLEAN SymbolInfo_DumpUDT(_In_ ULONG64 BaseAddress, _In_ ULONG Index, TypeInfo* Info); + +BOOLEAN PdbGetSymbolUDTClass( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ UdtClassInfo* Info + ); + +BOOLEAN PdbGetSymbolUDTUnion( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ UdtUnionInfo *Info + ); + +BOOLEAN PdbGetSymbolFunctionType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ FunctionTypeInfo *Info + ); + +BOOLEAN PdbGetSymbolFunctionArgType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ FunctionArgTypeInfo *Info + ); + +BOOLEAN PdbGetSymbolBaseClass( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ BaseClassInfo *Info + ); + +BOOLEAN PdbGetSymbolData( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ DataInfo *Info + ); + +BOOLEAN SymbolInfo_DumpType(_In_ ULONG64 BaseAddress, _In_ ULONG Index, TypeInfo* Info); +BOOLEAN SymbolInfo_DumpSymbolType(_Inout_ PPDB_SYMBOL_CONTEXT Context, _In_ ULONG Index, TypeInfo* Info, ULONG* TypeIndex); + +BOOLEAN PdbCheckTagType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _In_ ULONG Tag + ); + +BOOLEAN PdbGetSymbolSize( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG64* Size + ); + +BOOLEAN PdbGetSymbolArrayElementTypeIndex( + _In_ ULONG64 BaseAddress, + _In_ ULONG ArrayIndex, + _Inout_ ULONG* ElementTypeIndex + ); + +BOOLEAN PdbGetSymbolArrayDimensions( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG64* pDims, + _Inout_ ULONG* Dims, + _In_ ULONG MaxDims + ); + +BOOLEAN PdbGetSymbolUdtVariables( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pVars, + _Inout_ ULONG* Vars, + _In_ ULONG MaxVars + ); + +BOOLEAN PdbGetSymbolUdtFunctions( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pFuncs, + _Inout_ ULONG* Funcs, + _In_ ULONG MaxFuncs + ); + +BOOLEAN PdbGetSymbolUdtBaseClasses( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pBases, + _Inout_ ULONG* Bases, + _In_ ULONG MaxBases); + +BOOLEAN PdbGetSymbolUdtUnionMembers( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pMembers, + _Inout_ ULONG* Members, + _In_ ULONG MaxMembers + ); + +BOOLEAN PdbGetSymbolFunctionArguments( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pArgs, + _Inout_ ULONG* Args, + _In_ ULONG MaxArgs + ); + +BOOLEAN PdbGetSymbolEnumerations( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG *pEnums, + _Inout_ ULONG *Enums, + _In_ ULONG MaxEnums + ); + +BOOLEAN PdbGetSymbolTypeDefTypeIndex( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG *UndTypeIndex + ); + +BOOLEAN PdbGetSymbolPointers( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG *UndTypeIndex, + _Inout_ ULONG *NumPointers + ); + +BOOLEAN SymbolInfo_GetTypeNameHelper( + _In_ ULONG Index, + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _Out_ PWSTR *VarName, + _Inout_ PPH_STRING_BUILDER TypeName + ); +PPH_STRING SymbolInfo_GetTypeName( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ PWSTR VarName + ); +PWSTR SymbolInfo_TagStr(_In_ enum SymTagEnum Tag); +PWSTR SymbolInfo_BaseTypeStr(_In_ BasicType Type, _In_ ULONG64 Length); +PWSTR SymbolInfo_CallConvStr(_In_ CV_call_e CallConv); +PWSTR SymbolInfo_DataKindFromSymbolInfo(_In_ PSYMBOL_INFOW rSymbol); +PWSTR SymbolInfo_DataKindStr(DataKind dataKind); +VOID SymbolInfo_SymbolLocationStr(PSYMBOL_INFOW rSymbol, PWSTR pBuffer); +PWSTR SymbolInfo_RegisterStr(CV_HREG_e RegCode); +PWSTR SymbolInfo_UdtKindStr(UdtKind KindType); +PWSTR SymbolInfo_LocationTypeStr(LocationType LocType); + +VOID PrintClassInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ UdtClassInfo* Info + ); + +VOID PrintUserDefinedTypes(PPDB_SYMBOL_CONTEXT Context); + +#endif diff --git a/tools/peview/include/peview.h b/tools/peview/include/peview.h index 2ac8644b65e4..67035a1b5985 100644 --- a/tools/peview/include/peview.h +++ b/tools/peview/include/peview.h @@ -1,39 +1,442 @@ -#ifndef PEVIEW_H -#define PEVIEW_H - -#include -#include -#include -#include -#include "resource.h" - -extern PPH_STRING PvFileName; - -// peprp - -VOID PvPeProperties( - VOID - ); - -// libprp - -VOID PvLibProperties( - VOID - ); - -// misc - -PPH_STRING PvResolveShortcutTarget( - _In_ PPH_STRING ShortcutFileName - ); - -VOID PvCopyListView( - _In_ HWND ListViewHandle - ); - -VOID PvHandleListViewNotifyForCopy( - _In_ LPARAM lParam, - _In_ HWND ListViewHandle - ); - -#endif +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#ifndef PEVIEW_H +#define PEVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "resource.h" + +extern PPH_STRING PvFileName; +extern PH_MAPPED_IMAGE PvMappedImage; +extern PIMAGE_COR20_HEADER PvImageCor20Header; +extern PPH_SYMBOL_PROVIDER PvSymbolProvider; +extern HICON PvImageSmallIcon; +extern HICON PvImageLargeIcon; +extern PH_IMAGE_VERSION_INFO PvImageVersionInfo; + +FORCEINLINE PWSTR PvpGetStringOrNa( + _In_ PPH_STRING String + ) +{ + if (!PhIsNullOrEmptyString(String)) + return String->Buffer; + else + return L"N/A"; +} + +// peprp + +VOID PvPeProperties( + VOID + ); + +NTSTATUS PhpOpenFileSecurity( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ); + +// libprp + +VOID PvLibProperties( + VOID + ); + +// exlfprp + +VOID PvExlfProperties( + VOID + ); + +// misc + +PPH_STRING PvResolveShortcutTarget( + _In_ PPH_STRING ShortcutFileName + ); + +VOID PvCopyListView( + _In_ HWND ListViewHandle + ); + +VOID PvHandleListViewNotifyForCopy( + _In_ LPARAM lParam, + _In_ HWND ListViewHandle + ); + +VOID PvHandleListViewCommandCopy( + _In_ HWND WindowHandle, + _In_ LPARAM lParam, + _In_ WPARAM wParam, + _In_ HWND ListViewHandle + ); + +// settings + +VOID PeInitializeSettings( + VOID + ); + +VOID PeSaveSettings( + VOID + ); + +// symbols + +#define WM_PV_SEARCH_FINISHED (WM_APP + 701) +#define WM_PV_SEARCH_SHOWMENU (WM_APP + 702) + +extern ULONG SearchResultsAddIndex; +extern PPH_LIST SearchResults; +extern PH_QUEUED_LOCK SearchResultsLock; + +VOID PvCreateSearchControl( + _In_ HWND WindowHandle, + _In_opt_ PWSTR BannerText + ); + +typedef enum _WCT_TREE_COLUMN_ITEM_NAME +{ + TREE_COLUMN_ITEM_TYPE, + TREE_COLUMN_ITEM_VA, + TREE_COLUMN_ITEM_NAME, + TREE_COLUMN_ITEM_SYMBOL, + TREE_COLUMN_ITEM_SIZE, + TREE_COLUMN_ITEM_MAXIMUM +} WCT_TREE_COLUMN_ITEM_NAME; + +typedef enum _PV_SYMBOL_TYPE +{ + PV_SYMBOL_TYPE_UNKNOWN, + PV_SYMBOL_TYPE_FUNCTION, + PV_SYMBOL_TYPE_SYMBOL, + PV_SYMBOL_TYPE_LOCAL_VAR, + PV_SYMBOL_TYPE_STATIC_LOCAL_VAR, + PV_SYMBOL_TYPE_PARAMETER, + PV_SYMBOL_TYPE_OBJECT_PTR, + PV_SYMBOL_TYPE_STATIC_VAR, + PV_SYMBOL_TYPE_GLOBAL_VAR, + PV_SYMBOL_TYPE_STRUCT, + PV_SYMBOL_TYPE_STATIC_MEMBER, + PV_SYMBOL_TYPE_CONSTANT, + PV_SYMBOL_TYPE_MEMBER, + PV_SYMBOL_TYPE_CLASS, +} PV_SYMBOL_TYPE; + +typedef struct _PV_SYMBOL_NODE +{ + PH_TREENEW_NODE Node; + + PV_SYMBOL_TYPE Type; + ULONG Size; + ULONG64 Address; + PPH_STRING Name; + PPH_STRING Data; + PPH_STRING SizeText; + WCHAR Pointer[PH_PTR_STR_LEN_1]; + + PH_STRINGREF TextCache[TREE_COLUMN_ITEM_MAXIMUM]; +} PV_SYMBOL_NODE, *PPV_SYMBOL_NODE; + +typedef struct _PH_TN_COLUMN_MENU_DATA +{ + HWND TreeNewHandle; + PPH_TREENEW_HEADER_MOUSE_EVENT MouseEvent; + ULONG DefaultSortColumn; + PH_SORT_ORDER DefaultSortOrder; + + struct _PH_EMENU_ITEM *Menu; + struct _PH_EMENU_ITEM *Selection; + ULONG ProcessedId; +} PH_TN_COLUMN_MENU_DATA, *PPH_TN_COLUMN_MENU_DATA; + +#define PH_TN_COLUMN_MENU_HIDE_COLUMN_ID ((ULONG)-1) +#define PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID ((ULONG)-2) +#define PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID ((ULONG)-3) +#define PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID ((ULONG)-4) +#define PH_TN_COLUMN_MENU_RESET_SORT_ID ((ULONG)-5) + +VOID PhInitializeTreeNewColumnMenu( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data + ); + +#define PH_TN_COLUMN_MENU_NO_VISIBILITY 0x1 +#define PH_TN_COLUMN_MENU_SHOW_RESET_SORT 0x2 + +VOID PhInitializeTreeNewColumnMenuEx( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data, + _In_ ULONG Flags + ); + +BOOLEAN PhHandleTreeNewColumnMenu( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data + ); + +VOID PhDeleteTreeNewColumnMenu( + _In_ PPH_TN_COLUMN_MENU_DATA Data + ); + +typedef struct _PH_TN_FILTER_SUPPORT +{ + PPH_LIST FilterList; + HWND TreeNewHandle; + PPH_LIST NodeList; +} PH_TN_FILTER_SUPPORT, *PPH_TN_FILTER_SUPPORT; + +typedef BOOLEAN (NTAPI *PPH_TN_FILTER_FUNCTION)( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ); + +typedef struct _PH_TN_FILTER_ENTRY +{ + PPH_TN_FILTER_FUNCTION Filter; + PVOID Context; +} PH_TN_FILTER_ENTRY, *PPH_TN_FILTER_ENTRY; + +VOID PhInitializeTreeNewFilterSupport( + _Out_ PPH_TN_FILTER_SUPPORT Support, + _In_ HWND TreeNewHandle, + _In_ PPH_LIST NodeList + ); + +VOID PhDeleteTreeNewFilterSupport( + _In_ PPH_TN_FILTER_SUPPORT Support + ); + +PPH_TN_FILTER_ENTRY PhAddTreeNewFilter( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TN_FILTER_FUNCTION Filter, + _In_opt_ PVOID Context + ); + +VOID PhRemoveTreeNewFilter( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TN_FILTER_ENTRY Entry + ); + +BOOLEAN PhApplyTreeNewFiltersToNode( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TREENEW_NODE Node + ); + +VOID PhApplyTreeNewFilters( + _In_ PPH_TN_FILTER_SUPPORT Support + ); + +typedef struct _PDB_SYMBOL_CONTEXT +{ + HWND DialogHandle; + HWND SearchHandle; + HWND TreeNewHandle; + HWND ParentWindowHandle; + HANDLE UpdateTimerHandle; + + ULONG64 BaseAddress; + PPH_STRING FileName; + PPH_STRING SearchboxText; + PPH_STRING TreeText; + + PPH_LIST SymbolList; + PPH_LIST UdtList; + + PH_LAYOUT_MANAGER LayoutManager; + + ULONG TreeNewSortColumn; + PH_SORT_ORDER TreeNewSortOrder; + PH_TN_FILTER_SUPPORT FilterSupport; + PPH_HASHTABLE NodeHashtable; + PPH_LIST NodeList; +} PDB_SYMBOL_CONTEXT, *PPDB_SYMBOL_CONTEXT; + +INT_PTR CALLBACK PvpSymbolsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +NTSTATUS PeDumpFileSymbols( + _In_ PPDB_SYMBOL_CONTEXT Context + ); + +VOID PdbDumpAddress( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG64 Address + ); + +VOID PvPdbProperties( + VOID + ); + +VOID PvSymbolAddTreeNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPV_SYMBOL_NODE Entry + ); + +// + +INT_PTR CALLBACK PvpPeImportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeExportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeLoadConfigDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeClrDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeCgfDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeResourcesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPePropStoreDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeExtendedAttributesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeStreamsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeLinksDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeProcessesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpPeTlsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +// ELF + +PWSTR PvpGetSymbolTypeName( + _In_ UCHAR TypeInfo + ); + +PWSTR PvpGetSymbolBindingName( + _In_ UCHAR TypeInfo + ); + +PWSTR PvpGetSymbolVisibility( + _In_ UCHAR OtherInfo + ); + +PPH_STRING PvpGetSymbolSectionName( + _In_ ULONG Index + ); + +INT_PTR CALLBACK PvpExlfGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpExlfImportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpExlfExportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR CALLBACK PvpExlfDynamicDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +#endif diff --git a/tools/peview/include/prpsh.h b/tools/peview/include/prpsh.h index 4f57f384f7d2..14d8801f10a9 100644 --- a/tools/peview/include/prpsh.h +++ b/tools/peview/include/prpsh.h @@ -29,14 +29,16 @@ typedef struct _PV_PROPSHEETCONTEXT { + BOOLEAN LayoutInitialized; + WNDPROC DefaultWindowProc; PH_LAYOUT_MANAGER LayoutManager; PPH_LAYOUT_ITEM TabPageItem; - BOOLEAN LayoutInitialized; } PV_PROPSHEETCONTEXT, *PPV_PROPSHEETCONTEXT; typedef struct _PV_PROPCONTEXT { PPH_STRING Title; + PPH_STRING StartPage; PROPSHEETHEADER PropSheetHeader; HPROPSHEETPAGE *PropSheetPages; } PV_PROPCONTEXT, *PPV_PROPCONTEXT; @@ -115,10 +117,10 @@ FORCEINLINE BOOLEAN PvPropPageDlgProcHeader( if (uMsg == WM_INITDIALOG) { // Save the context. - SetProp(hwndDlg, L"PvContext", (HANDLE)lParam); + PhSetWindowContext(hwndDlg, ULONG_MAX, (PVOID)lParam); } - propSheetPage = (LPPROPSHEETPAGE)GetProp(hwndDlg, L"PvContext"); + propSheetPage = PhGetWindowContext(hwndDlg, ULONG_MAX); if (!propSheetPage) return FALSE; diff --git a/tools/peview/ldprp.c b/tools/peview/ldprp.c new file mode 100644 index 000000000000..f0757cf1a7f8 --- /dev/null +++ b/tools/peview/ldprp.c @@ -0,0 +1,365 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +#define ADD_VALUE(Name, Value) \ +{ \ + INT lvItemIndex; \ + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, Name, NULL); \ + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, Value); \ +} + +PPH_STRING PvpGetPeGuardFlagsText( + _In_ ULONG GuardFlags + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 10); + + if (GuardFlags & IMAGE_GUARD_CF_INSTRUMENTED) + PhAppendStringBuilder2(&stringBuilder, L"Instrumented, "); + if (GuardFlags & IMAGE_GUARD_CFW_INSTRUMENTED) + PhAppendStringBuilder2(&stringBuilder, L"Instrumented (Write), "); + if (GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT) + PhAppendStringBuilder2(&stringBuilder, L"Function table, "); + if (GuardFlags & IMAGE_GUARD_SECURITY_COOKIE_UNUSED) + PhAppendStringBuilder2(&stringBuilder, L"Unused security cookie, "); + if (GuardFlags & IMAGE_GUARD_PROTECT_DELAYLOAD_IAT) + PhAppendStringBuilder2(&stringBuilder, L"Delay-load IAT protected, "); + if (GuardFlags & IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION) + PhAppendStringBuilder2(&stringBuilder, L"Delay-load private section, "); + if (GuardFlags & IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION) + PhAppendStringBuilder2(&stringBuilder, L"Export supression, "); + if (GuardFlags & IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT) + PhAppendStringBuilder2(&stringBuilder, L"Export information supression, "); + if (GuardFlags & IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT) + PhAppendStringBuilder2(&stringBuilder, L"Longjump table, "); + if (GuardFlags & IMAGE_GUARD_RETPOLINE_PRESENT) + PhAppendStringBuilder2(&stringBuilder, L"Retpoline present, "); + + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + return PhFinalStringBuilderString(&stringBuilder); +} + +PPH_STRING PvpGetPeEnclaveImportsText( + _In_ PVOID EnclaveConfig + ) +{ + PH_STRING_BUILDER stringBuilder; + ULONG i; + + PhInitializeStringBuilder(&stringBuilder, 10); + + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PIMAGE_ENCLAVE_CONFIG32 enclaveConfig32 = EnclaveConfig; + PIMAGE_ENCLAVE_IMPORT enclaveImports; + + enclaveImports = PhMappedImageRvaToVa( + &PvMappedImage, + enclaveConfig32->ImportList, + NULL + ); + + for (i = 0; i < enclaveConfig32->NumberOfImports; i++) + { + PSTR importName; + + if (enclaveImports->ImportName == USHRT_MAX) + break; + + if (importName = PhMappedImageRvaToVa( + &PvMappedImage, + enclaveImports->ImportName, + NULL + )) + { + PhAppendFormatStringBuilder(&stringBuilder, L"%hs, ", importName); + } + + enclaveImports++; + } + } + else + { + PIMAGE_ENCLAVE_CONFIG64 enclaveConfig64 = EnclaveConfig; + PIMAGE_ENCLAVE_IMPORT enclaveImports; + + enclaveImports = PhMappedImageRvaToVa( + &PvMappedImage, + enclaveConfig64->ImportList, + NULL + ); + + for (i = 0; i < enclaveConfig64->NumberOfImports; i++) + { + PSTR importName; + + if (enclaveImports->ImportName == USHRT_MAX) + break; + + if (importName = PhMappedImageRvaToVa( + &PvMappedImage, + enclaveImports->ImportName, + NULL + )) + { + PhAppendFormatStringBuilder(&stringBuilder, L"%hs, ", importName); + } + + enclaveImports++; + } + } + + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + return PhFinalStringBuilderString(&stringBuilder); +} + +VOID PvpAddPeEnclaveConfig( + _In_ PVOID ImageConfig, + _In_ HWND lvHandle + ) +{ + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PIMAGE_LOAD_CONFIG_DIRECTORY32 imageConfig32 = ImageConfig; + PIMAGE_ENCLAVE_CONFIG32 enclaveConfig; + + if (!RTL_CONTAINS_FIELD(imageConfig32, imageConfig32->Size, EnclaveConfigurationPointer)) + return; + + enclaveConfig = PhMappedImageVaToVa( + &PvMappedImage, + (ULONG)imageConfig32->EnclaveConfigurationPointer, + NULL + ); + + if (enclaveConfig) + { + ADD_VALUE(L"Enclave PolicyFlags", PhaFormatUInt64(enclaveConfig->PolicyFlags, TRUE)->Buffer); + ADD_VALUE(L"Enclave FamilyID", PH_AUTO_T(PH_STRING, PhFormatGuid((PGUID)enclaveConfig->FamilyID))->Buffer); + ADD_VALUE(L"Enclave ImageID", PH_AUTO_T(PH_STRING, PhFormatGuid((PGUID)enclaveConfig->ImageID))->Buffer); + ADD_VALUE(L"Enclave ImageVersion", PhaFormatUInt64(enclaveConfig->ImageVersion, TRUE)->Buffer); + ADD_VALUE(L"Enclave SecurityVersion", PhaFormatUInt64(enclaveConfig->SecurityVersion, TRUE)->Buffer); + ADD_VALUE(L"Enclave EnclaveSize", PhaFormatUInt64(enclaveConfig->EnclaveSize, TRUE)->Buffer); + ADD_VALUE(L"Enclave NumberOfThreads", PhaFormatUInt64(enclaveConfig->NumberOfThreads, TRUE)->Buffer); + ADD_VALUE(L"Enclave EnclaveFlags", PhaFormatUInt64(enclaveConfig->EnclaveFlags, TRUE)->Buffer); + ADD_VALUE(L"Enclave NumberOfImports", PhaFormatUInt64(enclaveConfig->NumberOfImports, TRUE)->Buffer); + ADD_VALUE(L"Enclave Imports", PH_AUTO_T(PH_STRING, PvpGetPeEnclaveImportsText(enclaveConfig))->Buffer); + } + } + else + { + PIMAGE_LOAD_CONFIG_DIRECTORY64 imageConfig64 = ImageConfig; + PIMAGE_ENCLAVE_CONFIG64 enclaveConfig; + + if (!RTL_CONTAINS_FIELD(imageConfig64, imageConfig64->Size, EnclaveConfigurationPointer)) + return; + + enclaveConfig = PhMappedImageVaToVa( + &PvMappedImage, + (ULONG)imageConfig64->EnclaveConfigurationPointer, + NULL + ); + + if (enclaveConfig) + { + ADD_VALUE(L"Enclave PolicyFlags", PhaFormatUInt64(enclaveConfig->PolicyFlags, TRUE)->Buffer); + ADD_VALUE(L"Enclave FamilyID", PH_AUTO_T(PH_STRING, PhFormatGuid((PGUID)enclaveConfig->FamilyID))->Buffer); + ADD_VALUE(L"Enclave ImageID", PH_AUTO_T(PH_STRING, PhFormatGuid((PGUID)enclaveConfig->ImageID))->Buffer); + ADD_VALUE(L"Enclave ImageVersion", PhaFormatUInt64(enclaveConfig->ImageVersion, TRUE)->Buffer); + ADD_VALUE(L"Enclave SecurityVersion", PhaFormatUInt64(enclaveConfig->SecurityVersion, TRUE)->Buffer); + ADD_VALUE(L"Enclave EnclaveSize", PhaFormatUInt64(enclaveConfig->EnclaveSize, TRUE)->Buffer); + ADD_VALUE(L"Enclave NumberOfThreads", PhaFormatUInt64(enclaveConfig->NumberOfThreads, TRUE)->Buffer); + ADD_VALUE(L"Enclave EnclaveFlags", PhaFormatUInt64(enclaveConfig->EnclaveFlags, TRUE)->Buffer); + ADD_VALUE(L"Enclave NumberOfImports", PhaFormatUInt64(enclaveConfig->NumberOfImports, TRUE)->Buffer); + ADD_VALUE(L"Enclave Imports", PH_AUTO_T(PH_STRING, PvpGetPeEnclaveImportsText(enclaveConfig))->Buffer); + } + } +} + +INT_PTR CALLBACK PvpPeLoadConfigDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + PIMAGE_LOAD_CONFIG_DIRECTORY32 config32; + PIMAGE_LOAD_CONFIG_DIRECTORY64 config64; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 220, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 170, L"Value"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageLoadCfgListViewColumns", lvHandle); + + #define ADD_VALUES(Type, Config) \ + { \ + LARGE_INTEGER time; \ + SYSTEMTIME systemTime; \ + \ + RtlSecondsSince1970ToTime((Config)->TimeDateStamp, &time); \ + PhLargeIntegerToLocalSystemTime(&systemTime, &time); \ + \ + ADD_VALUE(L"Time stamp", (Config)->TimeDateStamp ? PhaFormatDateTime(&systemTime)->Buffer : L"0"); \ + ADD_VALUE(L"Version", PhaFormatString(L"%u.%u", (Config)->MajorVersion, (Config)->MinorVersion)->Buffer); \ + ADD_VALUE(L"Global flags to clear", PhaFormatString(L"0x%x", (Config)->GlobalFlagsClear)->Buffer); \ + ADD_VALUE(L"Global flags to set", PhaFormatString(L"0x%x", (Config)->GlobalFlagsSet)->Buffer); \ + ADD_VALUE(L"Critical section default timeout", PhaFormatUInt64((Config)->CriticalSectionDefaultTimeout, TRUE)->Buffer); \ + ADD_VALUE(L"De-commit free block threshold", PhaFormatUInt64((Config)->DeCommitFreeBlockThreshold, TRUE)->Buffer); \ + ADD_VALUE(L"De-commit total free threshold", PhaFormatUInt64((Config)->DeCommitTotalFreeThreshold, TRUE)->Buffer); \ + ADD_VALUE(L"LOCK prefix table", PhaFormatString(L"0x%Ix", (Config)->LockPrefixTable)->Buffer); \ + ADD_VALUE(L"Maximum allocation size", PhaFormatString(L"0x%Ix", (Config)->MaximumAllocationSize)->Buffer); \ + ADD_VALUE(L"Virtual memory threshold", PhaFormatString(L"0x%Ix", (Config)->VirtualMemoryThreshold)->Buffer); \ + ADD_VALUE(L"Process affinity mask", PhaFormatString(L"0x%Ix", (Config)->ProcessAffinityMask)->Buffer); \ + ADD_VALUE(L"Process heap flags", PhaFormatString(L"0x%Ix", (Config)->ProcessHeapFlags)->Buffer); \ + ADD_VALUE(L"CSD version", PhaFormatString(L"%u", (Config)->CSDVersion)->Buffer); \ + ADD_VALUE(L"Dependent load flags", PhaFormatString(L"0x%x", (Config)->DependentLoadFlags)->Buffer); \ + ADD_VALUE(L"Edit list", PhaFormatString(L"0x%Ix", (Config)->EditList)->Buffer); \ + ADD_VALUE(L"Security cookie", PhaFormatString(L"0x%Ix", (Config)->SecurityCookie)->Buffer); \ + ADD_VALUE(L"SEH handler table", PhaFormatString(L"0x%Ix", (Config)->SEHandlerTable)->Buffer); \ + ADD_VALUE(L"SEH handler count", PhaFormatUInt64((Config)->SEHandlerCount, TRUE)->Buffer); \ + \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, GuardCFCheckFunctionPointer)) \ + { \ + ADD_VALUE(L"CFG GuardFlags", PhaFormatString(L"%s (0x%x)", PH_AUTO_T(PH_STRING, PvpGetPeGuardFlagsText((Config)->GuardFlags))->Buffer, (Config)->GuardFlags)->Buffer); \ + ADD_VALUE(L"CFG Check Function pointer", PhaFormatString(L"0x%Ix", (Config)->GuardCFCheckFunctionPointer)->Buffer); \ + ADD_VALUE(L"CFG Check Dispatch pointer", PhaFormatString(L"0x%Ix", (Config)->GuardCFDispatchFunctionPointer)->Buffer); \ + ADD_VALUE(L"CFG Function table", PhaFormatString(L"0x%Ix", (Config)->GuardCFFunctionTable)->Buffer); \ + ADD_VALUE(L"CFG Function table entry count", PhaFormatUInt64((Config)->GuardCFFunctionCount, TRUE)->Buffer); \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, GuardAddressTakenIatEntryTable)) \ + { \ + ADD_VALUE(L"CFG IatEntry table", PhaFormatString(L"0x%Ix", (Config)->GuardAddressTakenIatEntryTable)->Buffer); \ + ADD_VALUE(L"CFG IatEntry table entry count", PhaFormatUInt64((Config)->GuardAddressTakenIatEntryCount, TRUE)->Buffer); \ + } \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, GuardLongJumpTargetTable)) \ + { \ + ADD_VALUE(L"CFG LongJump table", PhaFormatString(L"0x%Ix", (Config)->GuardLongJumpTargetTable)->Buffer); \ + ADD_VALUE(L"CFG LongJump table entry count", PhaFormatUInt64((Config)->GuardLongJumpTargetCount, TRUE)->Buffer); \ + } \ + } \ + \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, CodeIntegrity)) \ + { \ + ADD_VALUE(L"CI flags", PhaFormatString(L"0x%x", (Config)->CodeIntegrity.Flags)->Buffer); \ + ADD_VALUE(L"CI catalog", PhaFormatString(L"0x%Ix", (Config)->CodeIntegrity.Catalog)->Buffer); \ + ADD_VALUE(L"CI catalog offset", PhaFormatString(L"0x%Ix", (Config)->CodeIntegrity.CatalogOffset)->Buffer); \ + } \ + \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, DynamicValueRelocTable)) \ + { \ + ADD_VALUE(L"DynamicValueRelocTable", PhaFormatString(L"0x%Ix", (Config)->DynamicValueRelocTable)->Buffer); \ + ADD_VALUE(L"CHPEMetadataPointer", PhaFormatString(L"0x%Ix", (Config)->CHPEMetadataPointer)->Buffer); \ + ADD_VALUE(L"GuardRFFailureRoutine", PhaFormatString(L"0x%Ix", (Config)->GuardRFFailureRoutine)->Buffer); \ + ADD_VALUE(L"GuardRFFailureRoutineFunctionPointer", PhaFormatString(L"0x%Ix", (Config)->GuardRFFailureRoutineFunctionPointer)->Buffer); \ + } \ + \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, DynamicValueRelocTableOffset)) \ + { \ + ADD_VALUE(L"DynamicValueRelocTableOffset", PhaFormatString(L"0x%Ix", (Config)->DynamicValueRelocTableOffset)->Buffer); \ + ADD_VALUE(L"DynamicValueRelocTableSection", PhaFormatString(L"0x%Ix", (Config)->DynamicValueRelocTableSection)->Buffer); \ + ADD_VALUE(L"GuardRFVerifyStackPointerFunctionPointer", PhaFormatString(L"0x%Ix", (Config)->GuardRFVerifyStackPointerFunctionPointer)->Buffer); \ + ADD_VALUE(L"HotPatchTableOffset", PhaFormatString(L"0x%Ix", (Config)->HotPatchTableOffset)->Buffer); \ + ADD_VALUE(L"EnclaveConfigurationPointer", PhaFormatString(L"0x%Ix", (Config)->EnclaveConfigurationPointer)->Buffer); \ + } \ + \ + if (RTL_CONTAINS_FIELD((Config), (Config)->Size, VolatileMetadataPointer)) \ + { \ + ADD_VALUE(L"VolatileMetadataPointer", PhaFormatString(L"0x%Ix", (Config)->VolatileMetadataPointer)->Buffer); \ + } \ + } + + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + if (NT_SUCCESS(PhGetMappedImageLoadConfig32(&PvMappedImage, &config32))) + { + ADD_VALUES(IMAGE_LOAD_CONFIG_DIRECTORY32, config32); + PvpAddPeEnclaveConfig(config32, lvHandle); + } + } + else + { + if (NT_SUCCESS(PhGetMappedImageLoadConfig64(&PvMappedImage, &config64))) + { + ADD_VALUES(IMAGE_LOAD_CONFIG_DIRECTORY64, config64); + PvpAddPeEnclaveConfig(config64, lvHandle); + } + } + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageLoadCfgListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/libprp.c b/tools/peview/libprp.c index 89d473b9103e..e75e0acb58f5 100644 --- a/tools/peview/libprp.c +++ b/tools/peview/libprp.c @@ -1,181 +1,209 @@ -/* - * Process Hacker - - * PE viewer - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include - -INT_PTR CALLBACK PvpLibExportsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PH_MAPPED_ARCHIVE PvMappedArchive; - -VOID PvLibProperties( - VOID - ) -{ - NTSTATUS status; - PROPSHEETHEADER propSheetHeader = { sizeof(propSheetHeader) }; - PROPSHEETPAGE propSheetPage; - HPROPSHEETPAGE pages[1]; - - status = PhLoadMappedArchive(PvFileName->Buffer, NULL, TRUE, &PvMappedArchive); - - if (!NT_SUCCESS(status)) - { - PhShowStatus(NULL, L"Unable to load the archive file", status, 0); - return; - } - - propSheetHeader.dwFlags = - PSH_NOAPPLYNOW | - PSH_NOCONTEXTHELP | - PSH_PROPTITLE; - propSheetHeader.hwndParent = NULL; - propSheetHeader.pszCaption = PvFileName->Buffer; - propSheetHeader.nPages = 0; - propSheetHeader.nStartPage = 0; - propSheetHeader.phpage = pages; - - // Exports page - memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); - propSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_LIBEXPORTS); - propSheetPage.pfnDlgProc = PvpLibExportsDlgProc; - pages[propSheetHeader.nPages++] = CreatePropertySheetPage(&propSheetPage); - - PhModalPropertySheet(&propSheetHeader); - - PhUnloadMappedArchive(&PvMappedArchive); -} - -INT_PTR CALLBACK PvpLibExportsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG fallbackColumns[] = { 0, 1, 2, 3 }; - HWND lvHandle; - PH_MAPPED_ARCHIVE_MEMBER member; - PH_MAPPED_ARCHIVE_IMPORT_ENTRY importEntry; - - PhCenterWindow(GetParent(hwndDlg), NULL); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 60, L"DLL"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 200, L"Name"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 40, L"Ordinal/Hint"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 40, L"Type"); - PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 60, L"Name type"); - PhSetExtendedListView(lvHandle); - ExtendedListView_AddFallbackColumns(lvHandle, 4, fallbackColumns); - - member = *PvMappedArchive.LastStandardMember; - - while (NT_SUCCESS(PhGetNextMappedArchiveMember(&member, &member))) - { - if (NT_SUCCESS(PhGetMappedArchiveImportEntry(&member, &importEntry))) - { - INT lvItemIndex; - PPH_STRING name; - WCHAR number[PH_INT32_STR_LEN_1]; - PWSTR type; - - name = PhZeroExtendToUtf16(importEntry.DllName); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); - PhDereferenceObject(name); - - name = PhZeroExtendToUtf16(importEntry.Name); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, name->Buffer); - PhDereferenceObject(name); - - // Ordinal is unioned with NameHint, so this works both ways. - PhPrintUInt32(number, importEntry.Ordinal); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, number); - - switch (importEntry.Type) - { - case IMPORT_OBJECT_CODE: - type = L"Code"; - break; - case IMPORT_OBJECT_DATA: - type = L"Data"; - break; - case IMPORT_OBJECT_CONST: - type = L"Const"; - break; - default: - type = L"Unknown"; - break; - } - - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, type); - - switch (importEntry.NameType) - { - case IMPORT_OBJECT_ORDINAL: - type = L"Ordinal"; - break; - case IMPORT_OBJECT_NAME: - type = L"Name"; - break; - case IMPORT_OBJECT_NAME_NO_PREFIX: - type = L"Name, no prefix"; - break; - case IMPORT_OBJECT_NAME_UNDECORATE: - type = L"Name, undecorate"; - break; - default: - type = L"Unknown"; - break; - } - - PhSetListViewSubItem(lvHandle, lvItemIndex, 4, type); - } - } - - ExtendedListView_SortItems(lvHandle); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_NOTIFY: - { - PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include + +INT_PTR CALLBACK PvpLibExportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +PH_MAPPED_ARCHIVE PvMappedArchive; + +VOID PvLibProperties( + VOID + ) +{ + NTSTATUS status; + PPV_PROPCONTEXT propContext; + + status = PhLoadMappedArchive(PvFileName->Buffer, NULL, TRUE, &PvMappedArchive); + + if (!NT_SUCCESS(status)) + { + PhShowStatus(NULL, L"Unable to load the archive file", status, 0); + return; + } + + if (propContext = PvCreatePropContext(PvFileName)) + { + PPV_PROPPAGECONTEXT newPage; + + // Lib page + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_LIBEXPORTS), + PvpLibExportsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + PhModalPropertySheet(&propContext->PropSheetHeader); + + PhDereferenceObject(propContext); + } + + PhUnloadMappedArchive(&PvMappedArchive); +} + +INT_PTR CALLBACK PvpLibExportsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + ULONG fallbackColumns[] = { 0, 1, 2, 3 }; + HWND lvHandle; + PH_MAPPED_ARCHIVE_MEMBER member; + PH_MAPPED_ARCHIVE_IMPORT_ENTRY importEntry; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, FALSE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 60, L"DLL"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 200, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 40, L"Ordinal/Hint"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 40, L"Type"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 60, L"Name type"); + PhSetExtendedListView(lvHandle); + ExtendedListView_AddFallbackColumns(lvHandle, 4, fallbackColumns); + PhLoadListViewColumnsFromSetting(L"LibListViewColumns", lvHandle); + + member = *PvMappedArchive.LastStandardMember; + + while (NT_SUCCESS(PhGetNextMappedArchiveMember(&member, &member))) + { + if (NT_SUCCESS(PhGetMappedArchiveImportEntry(&member, &importEntry))) + { + INT lvItemIndex; + PPH_STRING name; + WCHAR number[PH_INT32_STR_LEN_1]; + PWSTR type; + + name = PhZeroExtendToUtf16(importEntry.DllName); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); + PhDereferenceObject(name); + + name = PhZeroExtendToUtf16(importEntry.Name); + PhSetListViewSubItem(lvHandle, lvItemIndex, 1, name->Buffer); + PhDereferenceObject(name); + + // Ordinal is unioned with NameHint, so this works both ways. + PhPrintUInt32(number, importEntry.Ordinal); + PhSetListViewSubItem(lvHandle, lvItemIndex, 2, number); + + switch (importEntry.Type) + { + case IMPORT_OBJECT_CODE: + type = L"Code"; + break; + case IMPORT_OBJECT_DATA: + type = L"Data"; + break; + case IMPORT_OBJECT_CONST: + type = L"Const"; + break; + default: + type = L"Unknown"; + break; + } + + PhSetListViewSubItem(lvHandle, lvItemIndex, 3, type); + + switch (importEntry.NameType) + { + case IMPORT_OBJECT_ORDINAL: + type = L"Ordinal"; + break; + case IMPORT_OBJECT_NAME: + type = L"Name"; + break; + case IMPORT_OBJECT_NAME_NO_PREFIX: + type = L"Name, no prefix"; + break; + case IMPORT_OBJECT_NAME_UNDECORATE: + type = L"Name, undecorate"; + break; + default: + type = L"Unknown"; + break; + } + + PhSetListViewSubItem(lvHandle, lvItemIndex, 4, type); + } + } + + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"LibListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, + PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), + dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/links.c b/tools/peview/links.c new file mode 100644 index 000000000000..9892cbafdf85 --- /dev/null +++ b/tools/peview/links.c @@ -0,0 +1,231 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpPeEnumerateFileLinks( + _In_ HWND ListViewHandle + ) +{ + HANDLE fileHandle; + ULONG count = 0; + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(PvFileName), + FILE_READ_ATTRIBUTES | FILE_READ_DATA | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT + ))) + { + PFILE_LINKS_INFORMATION hardlinks; + PFILE_LINK_ENTRY_INFORMATION i; + + if (NT_SUCCESS(PhEnumFileHardLinks( + fileHandle, + &hardlinks + ))) + { + for (i = PH_FIRST_LINK(&hardlinks->Entry); i; i = PH_NEXT_LINK(i)) + { + NTSTATUS status; + HANDLE linkHandle; + UNICODE_STRING fileNameUs; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + + fileNameUs.Length = sizeof(LONGLONG); + fileNameUs.MaximumLength = sizeof(LONGLONG); + fileNameUs.Buffer = (PWSTR)&i->ParentFileId; + + InitializeObjectAttributes( + &oa, + &fileNameUs, + OBJ_CASE_INSENSITIVE, + fileHandle, + NULL + ); + + status = NtCreateFile( + &linkHandle, + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + &oa, + &isb, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_BY_FILE_ID, + NULL, + 0 + ); + + if (NT_SUCCESS(status)) + { + INT lvItemIndex; + PPH_STRING parentName; + WCHAR number[PH_PTR_STR_LEN_1]; + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + if (NT_SUCCESS(PhGetFileHandleName(linkHandle, &parentName))) + { + PPH_STRING fileName = NULL; + PPH_STRING baseName; + PH_STRINGREF pathPart; + PH_STRINGREF baseNamePart; + + baseName = PhGetBaseName(PvFileName); + + if (PhSplitStringRefAtChar(&PvFileName->sr, OBJ_NAME_PATH_SEPARATOR, &pathPart, &baseNamePart)) + { + PhMoveReference(&fileName, PhCreateString2(&pathPart)); + PhMoveReference(&fileName, PhConcatStringRef2(&fileName->sr, &parentName->sr)); + PhMoveReference(&fileName, PhConcatStrings(3, fileName->Buffer, L"\\", baseName->Buffer)); + } + + //if (!PhIsNullOrEmptyString(fileName)) + //{ + // HANDLE filePathHandle; + // + // if (NT_SUCCESS(PhCreateFileWin32( + // &filePathHandle, + // PhGetString(fileName), + // FILE_READ_ATTRIBUTES | SYNCHRONIZE, + // FILE_ATTRIBUTE_NORMAL, + // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + // FILE_OPEN, + // FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + // ))) + // { + // PFILE_ALL_INFORMATION fileAllInformation; + // //PFILE_ID_INFORMATION fileIdInformation; + // + // if (NT_SUCCESS(PhGetFileAllInformation(filePathHandle, &fileAllInformation))) + // { + // PhPrintPointer(number, (PVOID)fileAllInformation->InternalInformation.IndexNumber.QuadPart); + // PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, number); + // } + // + // //if (NT_SUCCESS(PhGetFileId(filePathHandle, &fileIdInformation))) + // //{ + // // PPH_STRING fileIdString = PhBufferToHexString( + // // fileIdInformation->FileId.Identifier, + // // sizeof(fileIdInformation->FileId.Identifier) + // // ); + // // + // // PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, fileIdString->Buffer); + // // PhDereferenceObject(fileIdString); + // //} + // + // NtClose(filePathHandle); + // } + //} + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, PhGetStringOrEmpty(fileName)); + + if (fileName) PhDereferenceObject(fileName); + PhDereferenceObject(baseName); + PhDereferenceObject(parentName); + } + + NtClose(linkHandle); + } + } + + PhFree(hardlinks); + } + + NtClose(fileHandle); + } +} + +INT_PTR CALLBACK PvpPeLinksDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 250, L"Name"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageHardLinksListViewColumns", lvHandle); + + PvpPeEnumerateFileLinks(lvHandle); + //ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageHardLinksListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/main.c b/tools/peview/main.c index 811f604359e2..1db30524b2f3 100644 --- a/tools/peview/main.c +++ b/tools/peview/main.c @@ -1,128 +1,235 @@ -/* - * Process Hacker - - * PE viewer - * - * Copyright (C) 2010 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include - -PPH_STRING PvFileName = NULL; - -static BOOLEAN NTAPI PvCommandLineCallback( - _In_opt_ PPH_COMMAND_LINE_OPTION Option, - _In_opt_ PPH_STRING Value, - _In_opt_ PVOID Context - ) -{ - if (!Option) - PhSwapReference(&PvFileName, Value); - - return TRUE; -} - -static VOID PvpInitializeDpi( - VOID - ) -{ - HDC hdc; - - if (hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL)) - { - PhGlobalDpi = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - } -} - -INT WINAPI wWinMain( - _In_ HINSTANCE hInstance, - _In_opt_ HINSTANCE hPrevInstance, - _In_ PWSTR lpCmdLine, - _In_ INT nCmdShow - ) -{ - static PH_COMMAND_LINE_OPTION options[] = - { - { 0, L"h", NoArgumentType } - }; - PH_STRINGREF commandLine; - - if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0))) - return 1; - - PhGuiSupportInitialization(); - PvpInitializeDpi(); - PvPropInitialization(); - - PhApplicationName = L"PE Viewer"; - - PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine); - - PhParseCommandLine( - &commandLine, - options, - sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION), - PH_COMMAND_LINE_IGNORE_FIRST_PART, - PvCommandLineCallback, - NULL - ); - - if (!PvFileName) - { - static PH_FILETYPE_FILTER filters[] = - { - { L"Supported files (*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi)", L"*.exe;*.dll;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi" }, - { L"All files (*.*)", L"*.*" } - }; - PVOID fileDialog; - - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - - fileDialog = PhCreateOpenFileDialog(); - PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); - - if (PhShowFileDialog(NULL, fileDialog)) - { - PvFileName = PhGetFileDialogFileName(fileDialog); - } - - PhFreeFileDialog(fileDialog); - } - - if (!PvFileName) - return 1; - - if (PhEndsWithString2(PvFileName, L".lnk", TRUE)) - { - PPH_STRING targetFileName; - - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - targetFileName = PvResolveShortcutTarget(PvFileName); - - if (targetFileName) - PhMoveReference(&PvFileName, targetFileName); - } - - if (!PhEndsWithString2(PvFileName, L".lib", TRUE)) - PvPeProperties(); - else - PvLibProperties(); - - return 0; -} +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +PPH_STRING PvFileName = NULL; + +BOOLEAN NTAPI PvpCommandLineCallback( + _In_opt_ PPH_COMMAND_LINE_OPTION Option, + _In_opt_ PPH_STRING Value, + _In_opt_ PVOID Context + ) +{ + if (!Option) + PhSwapReference(&PvFileName, Value); + + return TRUE; +} + +INT WINAPI wWinMain( + _In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ PWSTR lpCmdLine, + _In_ INT nCmdShow + ) +{ + static PH_COMMAND_LINE_OPTION options[] = + { + { 0, L"h", NoArgumentType } + }; + PPH_STRING commandLine; + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + + if (!NT_SUCCESS(PhInitializePhLibEx(L"PE Viewer", ULONG_MAX, hInstance, 0, 0))) + return 1; + + // Create a mutant for the installer. + { + HANDLE mutantHandle; + PPH_STRING objectName; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING objectNameUs; + PH_FORMAT format[2]; + + PhInitFormatS(&format[0], L"PeViewerMutant_"); + PhInitFormatU(&format[1], HandleToUlong(NtCurrentProcessId())); + + objectName = PhFormat(format, 2, 16); + PhStringRefToUnicodeString(&objectName->sr, &objectNameUs); + + InitializeObjectAttributes( + &objectAttributes, + &objectNameUs, + OBJ_CASE_INSENSITIVE, + PhGetNamespaceHandle(), + NULL + ); + + NtCreateMutant( + &mutantHandle, + MUTANT_QUERY_STATE, + &objectAttributes, + TRUE + ); + + PhDereferenceObject(objectName); + } + +#ifndef DEBUG + if (PhIsExecutingInWow64()) + { + PhShowWarning( + NULL, + L"You are attempting to run the 32-bit version of PE Viewer on 64-bit Windows. " + L"Most features will not work correctly.\n\n" + L"Please run the 64-bit version of PE Viewer instead." + ); + RtlExitUserProcess(STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT); + } +#endif + + PhGuiSupportInitialization(); + PhSettingsInitialization(); + PeInitializeSettings(); + PvPropInitialization(); + PhTreeNewInitialization(); + + if (!NT_SUCCESS(PhGetProcessCommandLine(NtCurrentProcess(), &commandLine))) + return 1; + + PhParseCommandLine( + &commandLine->sr, + options, + RTL_NUMBER_OF(options), + PH_COMMAND_LINE_IGNORE_FIRST_PART, + PvpCommandLineCallback, + NULL + ); + PhDereferenceObject(commandLine); + + if (!PvFileName) + { + static PH_FILETYPE_FILTER filters[] = + { + { L"Supported files (*.exe;*.dll;*.com;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi;*.pdb)", L"*.exe;*.dll;*.com;*.ocx;*.sys;*.scr;*.cpl;*.ax;*.acm;*.lib;*.winmd;*.efi;*.pdb" }, + { L"All files (*.*)", L"*.*" } + }; + PVOID fileDialog; + + fileDialog = PhCreateOpenFileDialog(); + PhSetFileDialogOptions(fileDialog, PH_FILEDIALOG_NOPATHVALIDATE); + PhSetFileDialogFilter(fileDialog, filters, RTL_NUMBER_OF(filters)); + + if (PhShowFileDialog(NULL, fileDialog)) + { + if (PvFileName = PhGetFileDialogFileName(fileDialog)) + { +#ifndef DEBUG + PPH_STRING applicationFileName; + + if (applicationFileName = PhGetApplicationFileName()) + { + PhMoveReference(&PvFileName, PhConcatStrings(3, L"\"", PvFileName->Buffer, L"\"")); + + if (PhShellExecuteEx( + NULL, + PhGetString(applicationFileName), + PvFileName->Buffer, + SW_SHOWDEFAULT, + 0, + ULONG_MAX, + NULL + )) + { + RtlExitUserProcess(STATUS_SUCCESS); + } + } +#endif + } + } + + PhFreeFileDialog(fileDialog); + } + + if (PhIsNullOrEmptyString(PvFileName)) + return 1; + + if (PhEndsWithString2(PvFileName, L".lnk", TRUE)) + { + PPH_STRING targetFileName; + + targetFileName = PvResolveShortcutTarget(PvFileName); + + if (targetFileName) + PhMoveReference(&PvFileName, targetFileName); + } + + if (PhEndsWithString2(PvFileName, L".lib", TRUE)) + PvLibProperties(); + else if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) + PvPdbProperties(); + else + { + NTSTATUS status; + HANDLE fileHandle; + + status = PhCreateFileWin32( + &fileHandle, + PvFileName->Buffer, + FILE_READ_ATTRIBUTES | FILE_READ_DATA | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (NT_SUCCESS(status)) + { + status = PhLoadMappedImageEx( + PvFileName->Buffer, + fileHandle, + TRUE, + &PvMappedImage + ); + NtClose(fileHandle); + + if (NT_SUCCESS(status)) + { + switch (PvMappedImage.Signature) + { + case IMAGE_DOS_SIGNATURE: + PvPeProperties(); + break; + case IMAGE_ELF_SIGNATURE: + PvExlfProperties(); + break; + default: + status = STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT; + break; + } + } + + if (NT_SUCCESS(status)) + PhUnloadMappedImage(&PvMappedImage); + } + + if (!NT_SUCCESS(status)) + PhShowStatus(NULL, L"Unable to load the file.", status, 0); + } + + PeSaveSettings(); + + return 0; +} diff --git a/tools/peview/misc.c b/tools/peview/misc.c index c1e3acb2fbea..bbf37fd8e56e 100644 --- a/tools/peview/misc.c +++ b/tools/peview/misc.c @@ -1,102 +1,333 @@ -/* - * Process Hacker - - * PE viewer - * - * Copyright (C) 2011 wj32 - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#define CINTERFACE -#define COBJMACROS -#include -#include -#undef CINTERFACE -#undef COBJMACROS -#include - -static GUID CLSID_ShellLink_I = { 0x00021401, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; -static GUID IID_IShellLinkW_I = { 0x000214f9, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; -static GUID IID_IPersistFile_I = { 0x0000010b, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; - -PPH_STRING PvResolveShortcutTarget( - _In_ PPH_STRING ShortcutFileName - ) -{ - PPH_STRING targetFileName; - IShellLinkW *shellLink; - IPersistFile *persistFile; - WCHAR path[MAX_PATH]; - - targetFileName = NULL; - - if (SUCCEEDED(CoCreateInstance(&CLSID_ShellLink_I, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW_I, &shellLink))) - { - if (SUCCEEDED(IShellLinkW_QueryInterface(shellLink, &IID_IPersistFile_I, &persistFile))) - { - if (SUCCEEDED(IPersistFile_Load(persistFile, ShortcutFileName->Buffer, STGM_READ)) && - SUCCEEDED(IShellLinkW_Resolve(shellLink, NULL, SLR_NO_UI))) - { - if (SUCCEEDED(IShellLinkW_GetPath(shellLink, path, MAX_PATH, NULL, 0))) - { - targetFileName = PhCreateString(path); - } - } - - IPersistFile_Release(persistFile); - } - - IShellLinkW_Release(shellLink); - } - - return targetFileName; -} - -// Copied from appsup.c - -VOID PvCopyListView( - _In_ HWND ListViewHandle - ) -{ - PPH_STRING text; - - text = PhGetListViewText(ListViewHandle); - PhSetClipboardString(ListViewHandle, &text->sr); - PhDereferenceObject(text); -} - -VOID PvHandleListViewNotifyForCopy( - _In_ LPARAM lParam, - _In_ HWND ListViewHandle - ) -{ - if (((LPNMHDR)lParam)->hwndFrom == ListViewHandle && ((LPNMHDR)lParam)->code == LVN_KEYDOWN) - { - LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)lParam; - - switch (keyDown->wVKey) - { - case 'C': - if (GetKeyState(VK_CONTROL) < 0) - PvCopyListView(ListViewHandle); - break; - case 'A': - if (GetKeyState(VK_CONTROL) < 0) - PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED); - break; - } - } -} +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2011 wj32 + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +#include + +#include + +static GUID CLSID_ShellLink_I = { 0x00021401, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; +static GUID IID_IShellLinkW_I = { 0x000214f9, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; +static GUID IID_IPersistFile_I = { 0x0000010b, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; + +PPH_STRING PvResolveShortcutTarget( + _In_ PPH_STRING ShortcutFileName + ) +{ + PPH_STRING targetFileName; + IShellLinkW *shellLink; + IPersistFile *persistFile; + WCHAR path[MAX_PATH]; + + targetFileName = NULL; + + if (SUCCEEDED(CoCreateInstance(&CLSID_ShellLink_I, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW_I, &shellLink))) + { + if (SUCCEEDED(IShellLinkW_QueryInterface(shellLink, &IID_IPersistFile_I, &persistFile))) + { + if (SUCCEEDED(IPersistFile_Load(persistFile, ShortcutFileName->Buffer, STGM_READ)) && + SUCCEEDED(IShellLinkW_Resolve(shellLink, NULL, SLR_NO_UI))) + { + if (SUCCEEDED(IShellLinkW_GetPath(shellLink, path, MAX_PATH, NULL, 0))) + { + targetFileName = PhCreateString(path); + } + } + + IPersistFile_Release(persistFile); + } + + IShellLinkW_Release(shellLink); + } + + return targetFileName; +} + +// Copied from appsup.c + +VOID PvCopyListView( + _In_ HWND ListViewHandle + ) +{ + PPH_STRING text; + + text = PhGetListViewText(ListViewHandle); + PhSetClipboardString(ListViewHandle, &text->sr); + PhDereferenceObject(text); +} + +VOID PvHandleListViewNotifyForCopy( + _In_ LPARAM lParam, + _In_ HWND ListViewHandle + ) +{ + if (((LPNMHDR)lParam)->hwndFrom == ListViewHandle && ((LPNMHDR)lParam)->code == LVN_KEYDOWN) + { + LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)lParam; + + switch (keyDown->wVKey) + { + case 'C': + if (GetKeyState(VK_CONTROL) < 0) + PvCopyListView(ListViewHandle); + break; + case 'A': + if (GetKeyState(VK_CONTROL) < 0) + PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED); + break; + } + } +} + +typedef struct _PH_COPY_ITEM_CONTEXT +{ + HWND ListViewHandle; + ULONG Id; + ULONG SubId; + PPH_STRING MenuItemText; +} PH_COPY_ITEM_CONTEXT, *PPH_COPY_ITEM_CONTEXT; + +VOID NTAPI PhpCopyListViewEMenuItemDeleteFunction( + _In_ struct _PH_EMENU_ITEM *Item + ) +{ + PPH_COPY_ITEM_CONTEXT context; + + context = Item->Context; + PhDereferenceObject(context->MenuItemText); + PhFree(context); +} + +BOOLEAN PvInsertCopyListViewEMenuItem( + _In_ struct _PH_EMENU_ITEM *Menu, + _In_ ULONG InsertAfterId, + _In_ HWND ListViewHandle + ) +{ + PPH_EMENU_ITEM parentItem; + ULONG indexInParent; + PPH_COPY_ITEM_CONTEXT context; + PPH_STRING columnText = NULL; + PPH_STRING escapedText; + PPH_STRING menuItemText; + PPH_EMENU_ITEM copyMenuItem; + POINT location; + LVHITTESTINFO lvHitInfo; + HDITEM headerItem; + WCHAR headerText[MAX_PATH]; + + if (!GetCursorPos(&location)) + return FALSE; + if (!ScreenToClient(ListViewHandle, &location)) + return FALSE; + + memset(&lvHitInfo, 0, sizeof(LVHITTESTINFO)); + lvHitInfo.pt = location; + + if (ListView_SubItemHitTest(ListViewHandle, &lvHitInfo) == -1) + return FALSE; + + memset(headerText, 0, sizeof(headerText)); + memset(&headerItem, 0, sizeof(HDITEM)); + headerItem.mask = HDI_TEXT; + headerItem.cchTextMax = RTL_NUMBER_OF(headerText); + headerItem.pszText = headerText; + + if (!Header_GetItem(ListView_GetHeader(ListViewHandle), lvHitInfo.iSubItem, &headerItem)) + return FALSE; + + columnText = PhaCreateString(headerText); + + if (PhIsNullOrEmptyString(columnText)) + return FALSE; + + if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent)) + return FALSE; + + indexInParent++; + + context = PhAllocate(sizeof(PH_COPY_ITEM_CONTEXT)); + context->ListViewHandle = ListViewHandle; + context->Id = lvHitInfo.iItem; + context->SubId = lvHitInfo.iSubItem; + + escapedText = PhEscapeStringForMenuPrefix(&columnText->sr); + menuItemText = PhFormatString(L"Copy \"%s\"", escapedText->Buffer); + PhDereferenceObject(escapedText); + + copyMenuItem = PhCreateEMenuItem(0, INT_MAX, menuItemText->Buffer, NULL, context); + copyMenuItem->DeleteFunction = PhpCopyListViewEMenuItemDeleteFunction; + context->MenuItemText = menuItemText; + + PhInsertEMenuItem(parentItem, copyMenuItem, indexInParent); + + return TRUE; +} + +BOOLEAN PvHandleCopyListViewEMenuItem( + _In_ struct _PH_EMENU_ITEM *SelectedItem + ) +{ + PPH_COPY_ITEM_CONTEXT context; + PH_STRING_BUILDER stringBuilder; + ULONG count; + ULONG selectedCount; + ULONG i; + PPH_STRING getItemText; + + if (!SelectedItem) + return FALSE; + if (SelectedItem->Id != INT_MAX) + return FALSE; + + context = SelectedItem->Context; + + PhInitializeStringBuilder(&stringBuilder, 0x100); + count = ListView_GetItemCount(context->ListViewHandle); + selectedCount = 0; + + for (i = 0; i < count; i++) + { + if (!(ListView_GetItemState(context->ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED)) + continue; + + getItemText = PhaGetListViewItemText(context->ListViewHandle, i, context->SubId); + + PhAppendStringBuilder(&stringBuilder, &getItemText->sr); + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + + selectedCount++; + } + + if (stringBuilder.String->Length != 0 && selectedCount == 1) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhSetClipboardString(context->ListViewHandle, &stringBuilder.String->sr); + PhDeleteStringBuilder(&stringBuilder); + + return TRUE; +} + +BOOLEAN PvGetListViewContextMenuPoint( + _In_ HWND ListViewHandle, + _Out_ PPOINT Point + ) +{ + INT selectedIndex; + RECT bounds; + RECT clientRect; + + // The user pressed a key to display the context menu. + // Suggest where the context menu should display. + + if ((selectedIndex = ListView_GetNextItem(ListViewHandle, -1, LVNI_SELECTED)) != -1) + { + if (ListView_GetItemRect(ListViewHandle, selectedIndex, &bounds, LVIR_BOUNDS)) + { + Point->x = bounds.left + PhSmallIconSize.X / 2; + Point->y = bounds.top + PhSmallIconSize.Y / 2; + + GetClientRect(ListViewHandle, &clientRect); + + if (Point->x < 0 || Point->y < 0 || Point->x >= clientRect.right || Point->y >= clientRect.bottom) + { + // The menu is going to be outside of the control. Just put it at the top-left. + Point->x = 0; + Point->y = 0; + } + + ClientToScreen(ListViewHandle, Point); + + return TRUE; + } + } + + Point->x = 0; + Point->y = 0; + ClientToScreen(ListViewHandle, Point); + + return FALSE; +} + +VOID PvHandleListViewCommandCopy( + _In_ HWND WindowHandle, + _In_ LPARAM lParam, + _In_ WPARAM wParam, + _In_ HWND ListViewHandle + ) +{ + if ((HWND)wParam == ListViewHandle) + { + POINT point; + PPH_EMENU menu; + PPH_EMENU item; + PVOID *listviewItems; + ULONG numberOfItems; + + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + + if (point.x == -1 && point.y == -1) + PvGetListViewContextMenuPoint((HWND)wParam, &point); + + PhGetSelectedListViewItemParams(ListViewHandle, &listviewItems, &numberOfItems); + + if (numberOfItems != 0) + { + menu = PhCreateEMenu(); + + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, USHRT_MAX, L"&Copy", NULL, NULL), ULONG_MAX); + PvInsertCopyListViewEMenuItem(menu, USHRT_MAX, ListViewHandle); + + item = PhShowEMenu( + menu, + WindowHandle, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + point.x, + point.y + ); + + if (item) + { + if (!PvHandleCopyListViewEMenuItem(item)) + { + switch (item->Id) + { + case USHRT_MAX: + { + PvCopyListView(ListViewHandle); + } + break; + } + } + } + + PhDestroyEMenu(menu); + } + + PhFree(listviewItems); + } +} diff --git a/tools/peview/pdb.c b/tools/peview/pdb.c new file mode 100644 index 000000000000..5485c2372e33 --- /dev/null +++ b/tools/peview/pdb.c @@ -0,0 +1,2512 @@ +/* + * PE viewer - + * pdb support + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include + +typedef BOOL (WINAPI *_SymInitializeW)( + _In_ HANDLE hProcess, + _In_opt_ PCWSTR UserSearchPath, + _In_ BOOL fInvadeProcess + ); + +typedef BOOL (WINAPI *_SymCleanup)( + _In_ HANDLE hProcess + ); + +typedef BOOL (CALLBACK *_SymGetTypeInfo)( + _In_ HANDLE hProcess, + _In_ ULONG64 ModBase, + _In_ ULONG TypeId, + _In_ IMAGEHLP_SYMBOL_TYPE_INFO GetType, + _Out_ PVOID pInfo + ); + +typedef BOOL (WINAPI *_SymEnumSymbolsW)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_opt_ PCWSTR Mask, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, + _In_opt_ const PVOID UserContext + ); + +typedef BOOL (WINAPI *_SymEnumTypesW)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, + _In_opt_ PVOID UserContext + ); + +typedef BOOL (WINAPI *_SymSearchW)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_opt_ DWORD Index, + _In_opt_ DWORD SymTag, + _In_opt_ PCWSTR Mask, + _In_opt_ DWORD64 Address, + _In_ PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback, + _In_opt_ PVOID UserContext, + _In_ DWORD Options + ); + +typedef BOOL (WINAPI *_SymSetSearchPathW)( + _In_ HANDLE hProcess, + _In_opt_ PCWSTR SearchPath + ); + +typedef ULONG (WINAPI *_SymGetOptions)(); + +typedef ULONG (WINAPI *_SymSetOptions)( + _In_ ULONG SymOptions + ); + +typedef ULONG64 (WINAPI *_SymLoadModuleExW)( + _In_ HANDLE hProcess, + _In_ HANDLE hFile, + _In_ PCWSTR ImageName, + _In_ PCWSTR ModuleName, + _In_ ULONG64 BaseOfDll, + _In_ ULONG DllSize, + _In_ PMODLOAD_DATA Data, + _In_ ULONG Flags + ); + +typedef BOOL (WINAPI *_SymGetModuleInfoW64)( + _In_ HANDLE hProcess, + _In_ ULONG64 qwAddr, + _Out_ PIMAGEHLP_MODULEW64 ModuleInfo + ); + +typedef BOOL (WINAPI *_SymGetTypeFromNameW)( + _In_ HANDLE hProcess, + _In_ ULONG64 BaseOfDll, + _In_ PCTSTR Name, + _Inout_ PSYMBOL_INFO Symbol + ); + +typedef BOOL (WINAPI *_SymSetContext)( + _In_ HANDLE hProcess, + _In_ PIMAGEHLP_STACK_FRAME StackFrame, + _In_opt_ PIMAGEHLP_CONTEXT Context + ); + +static _SymInitializeW SymInitializeW_I = NULL; +static _SymCleanup SymCleanup_I = NULL; +static _SymEnumSymbolsW SymEnumSymbolsW_I = NULL; +static _SymEnumTypesW SymEnumTypesW_I = NULL; +static _SymSetSearchPathW SymSetSearchPathW_I = NULL; +static _SymGetOptions SymGetOptions_I = NULL; +static _SymSetOptions SymSetOptions_I = NULL; +static _SymLoadModuleExW SymLoadModuleExW_I = NULL; +static _SymGetModuleInfoW64 SymGetModuleInfoW64_I = NULL; +static _SymGetTypeFromNameW SymGetTypeFromNameW_I = NULL; +static _SymGetTypeInfo SymGetTypeInfo_I = NULL; +static _SymSetContext SymSetContext_I = NULL; +static _SymSearchW SymSearchW_I = NULL; + +ULONG SearchResultsAddIndex = 0; +PPH_LIST SearchResults = NULL; +PH_QUEUED_LOCK SearchResultsLock = PH_QUEUED_LOCK_INIT; + +BOOLEAN PdbGetSymbolBasicType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ BaseTypeInfo *Info + ) +{ + ULONG baseType = btNoType; + ULONG64 length = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagBaseType)) + return FALSE; + + // Basic type ("basicType" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_BASETYPE, &baseType)) + return FALSE; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) + return FALSE; + + Info->BaseType = (BasicType)baseType; + Info->Length = length; + + return TRUE; +} + +BOOLEAN PdbGetSymbolPointerType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ PointerTypeInfo* Info + ) +{ + ULONG typeIndex = 0; + ULONG64 length = 0; + BOOL reference = FALSE; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagPointerType)) + return FALSE; + + // Type index ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + return FALSE; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) + return FALSE; + + // Reference ("reference" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_IS_REFERENCE, &reference)) + return FALSE; + + Info->TypeIndex = typeIndex; + Info->Length = length; + Info->TypeReference = reference; + + return TRUE; +} + +BOOLEAN PdbGetSymbolTypedefType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ TypedefInfo* Info + ) +{ + ULONG typeIndex = 0; + PWSTR symbolName = NULL; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagTypedef)) + return FALSE; + + // Type index ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + Info->TypeIndex = typeIndex; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = UNICODE_NULL; + LocalFree(symbolName); + + return TRUE; +} + +BOOLEAN PdbGetSymbolEnumType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ EnumInfo* Info + ) +{ + ULONG TypeIndex = 0; + ULONG Nested = 0; + PWSTR symbolName = NULL; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagEnum)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = UNICODE_NULL; + LocalFree(symbolName); + + // Type index ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + return FALSE; + + // Nested ("nested" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_NESTED, &Nested)) + return FALSE; + + // Enumerators + if (!PdbGetSymbolEnumerations(BaseAddress, Index, Info->Enums, &Info->NumEnums, ARRAYSIZE(Info->Enums))) + return FALSE; + + Info->TypeIndex = TypeIndex; + Info->Nested = (Nested != 0); + + return TRUE; +} + +BOOLEAN PdbGetSymbolArrayType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ArrayTypeInfo* Info + ) +{ + ULONG elementTypeIndex = 0; + ULONG64 length = 0; + ULONG indexTypeIndex = 0; + + // Check if it is really SymTagArrayType + if (!PdbCheckTagType(BaseAddress, Index, SymTagArrayType)) + return FALSE; + + // Element type index + if (!PdbGetSymbolArrayElementTypeIndex(BaseAddress, Index, &elementTypeIndex)) + return FALSE; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) + return FALSE; + + // Type index of the array index element + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_ARRAYINDEXTYPEID, &indexTypeIndex)) + return FALSE; + + Info->ElementTypeIndex = elementTypeIndex; + Info->Length = length; + Info->IndexTypeIndex = indexTypeIndex; + + if (length > 0) + { + if (!PdbGetSymbolArrayDimensions( + BaseAddress, + Index, + Info->Dimensions, + &Info->NumDimensions, + ARRAYSIZE(Info->Dimensions) + ) || (Info->NumDimensions == 0)) + { + return FALSE; + } + } + else + { + Info->NumDimensions = 0; // No dimensions + } + + return TRUE; +} + +BOOLEAN SymbolInfo_DumpUDT( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ TypeInfo* Info + ) +{ + ULONG UDTKind = 0; + BOOLEAN result = FALSE; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + return FALSE; + + switch (UDTKind) + { + case UdtStruct: + Info->UdtKind = TRUE; + result = PdbGetSymbolUDTClass(BaseAddress, Index, &Info->UdtClassInfo); + break; + case UdtClass: + Info->UdtKind = TRUE; + result = PdbGetSymbolUDTClass(BaseAddress, Index, &Info->UdtClassInfo); + break; + case UdtUnion: + Info->UdtKind = FALSE; + result = PdbGetSymbolUDTUnion(BaseAddress, Index, &Info->UdtUnionInfo); + break; + } + + return result; +} + +BOOLEAN PdbGetSymbolUDTClass( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ UdtClassInfo* Info + ) +{ + ULONG UDTKind = 0; + PWSTR symbolName = NULL; + ULONG64 Length = 0; + ULONG Nested = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + // Check if it is really a class or structure UDT ? + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + return FALSE; + + if ((UDTKind != UdtStruct) && (UDTKind != UdtClass)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = UNICODE_NULL; + LocalFree(symbolName); + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &Length)) + return FALSE; + + // Nested ("nested" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_NESTED, &Nested)) + return FALSE; + + // Member variables + if (!PdbGetSymbolUdtVariables(BaseAddress, Index, Info->Variables, &Info->NumVariables, ARRAYSIZE(Info->Variables))) + return FALSE; + + // Member functions + if (!PdbGetSymbolUdtFunctions(BaseAddress, Index, Info->Functions, &Info->NumFunctions, ARRAYSIZE(Info->Functions))) + return FALSE; + + // Base classes + if (!PdbGetSymbolUdtBaseClasses(BaseAddress, Index, Info->BaseClasses, &Info->NumBaseClasses, ARRAYSIZE(Info->BaseClasses))) + return FALSE; + + Info->UDTKind = (UdtKind)UDTKind; + Info->Length = Length; + Info->Nested = (Nested != 0); + + return TRUE; +} + +BOOLEAN PdbGetSymbolUDTUnion( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ UdtUnionInfo *Info + ) +{ + ULONG UDTKind = 0; + PWSTR symbolName = 0; + ULONG64 Length = 0; + ULONG Nested = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + // Check if it is really a union UDT ? + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_UDTKIND, &UDTKind)) + return FALSE; + + if (UDTKind != UdtUnion) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = UNICODE_NULL; + LocalFree(symbolName); + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &Length)) + return FALSE; + + // Nested ("nested" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_NESTED, &Nested)) + return FALSE; + + // Union members + if (!PdbGetSymbolUdtUnionMembers(BaseAddress, Index, Info->Members, &Info->NumMembers, ARRAYSIZE(Info->Members))) + return FALSE; + + Info->UDTKind = (UdtKind)UDTKind; + Info->Length = Length; + Info->Nested = (Nested != 0); + + return TRUE; +} + +BOOLEAN PdbGetSymbolFunctionType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ FunctionTypeInfo *Info + ) +{ + ULONG RetTypeIndex = 0; + ULONG NumArgs = 0; + ULONG CallConv = 0; + ULONG ClassIndex = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagFunctionType)) + return FALSE; + + // Index of the return type symbol ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &RetTypeIndex)) + return FALSE; + + // Number of arguments ("count" in DIA) + // Note: For non-static member functions, it includes "this" + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_COUNT, &NumArgs)) + return FALSE; + + // Do not save it in the data structure, since we obtain the number of arguments + // again using FunctionArguments() (see below). + // But later we will use this value to determine whether the function + // is static or not (if it is a member function) + + // Calling convention ("callingConvention" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_CALLING_CONVENTION, &CallConv)) + return FALSE; + + // Parent class type index ("classParent" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_CLASSPARENTID, &ClassIndex)) + { + // The function is not a member function + Info->ClassIndex = 0; + Info->MemberFunction = FALSE; + } + else + { + // This is a member function of a class + Info->ClassIndex = ClassIndex; + Info->MemberFunction = TRUE; + } + + // If this is a member function, obtain additional data + if (Info->MemberFunction) + { + LONG ThisAdjust = 0; + + // "this" adjustment ("thisAdjust" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_THISADJUST, &ThisAdjust)) + return FALSE; + + Info->ThisAdjust = ThisAdjust; + } + + // Test for GetChildren() + /* + ULONG cMaxChildren = 4; + ULONG Children[cMaxChildren]; + ULONG NumChildren = 0; + + GetChildren( m_hProcess, BaseAddress, Index, Children, NumChildren, cMaxChildren) + */ + + // Dump function arguments + if (!PdbGetSymbolFunctionArguments(BaseAddress, Index, Info->Args, &Info->NumArgs, ARRAYSIZE(Info->Args))) + return FALSE; + + // Is the function static ? (If it is a member function) + if (Info->MemberFunction) + { + // The function is static if the value of Count property + // it the same as the number of child FunctionArgType symbols + Info->StaticFunction = (NumArgs == Info->NumArgs); + } + + Info->RetTypeIndex = RetTypeIndex; + Info->CallConv = (CV_call_e)CallConv; + + return TRUE; +} + +BOOLEAN PdbGetSymbolFunctionArgType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ FunctionArgTypeInfo *Info + ) +{ + ULONG typeIndex = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagFunctionArgType)) + return FALSE; + + // Index of the argument type ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &typeIndex)) + return FALSE; + + Info->TypeIndex = typeIndex; + + return TRUE; +} + +BOOLEAN PdbGetSymbolBaseClass( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ BaseClassInfo *Info + ) +{ + ULONG typeIndex = 0; + ULONG virtualBase = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagBaseClass)) + return FALSE; + + // Base class UDT + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + BaseAddress, + Index, + TI_GET_TYPEID, + &typeIndex + )) + { + return FALSE; + } + + // Is this base class virtual ? + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + BaseAddress, + Index, + TI_GET_VIRTUALBASECLASS, + &virtualBase + )) + { + return FALSE; + } + + Info->TypeIndex = typeIndex; + Info->Virtual = (virtualBase != 0); + + if (virtualBase) + { + ULONG virtualBasePtrOffset = 0; + + // Virtual base pointer offset + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + BaseAddress, + Index, + TI_GET_VIRTUALBASEPOINTEROFFSET, + &virtualBasePtrOffset + )) + { + return FALSE; + } + + Info->Offset = 0; + Info->VirtualBasePointerOffset = virtualBasePtrOffset; + } + else + { + ULONG offset = 0; + + // Offset in the parent UDT + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + BaseAddress, + Index, + TI_GET_OFFSET, + &offset + )) + { + return FALSE; + } + + Info->Offset = offset; + Info->VirtualBasePointerOffset = 0; + } + + return TRUE; +} + +BOOLEAN PdbGetSymbolData( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ DataInfo *Info + ) +{ + PWSTR symbolName = NULL; + ULONG TypeIndex = 0; + ULONG dataKind = 0; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagData)) + return FALSE; + + // Name ("name" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMNAME, &symbolName) || !symbolName) + return FALSE; + + wcsncpy(Info->Name, symbolName, ARRAYSIZE(Info->Name)); + Info->Name[ARRAYSIZE(Info->Name) - 1] = UNICODE_NULL; + LocalFree(symbolName); + + // Index of type symbol ("typeId" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + return FALSE; + + // Data kind ("dataKind" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_DATAKIND, &dataKind)) + return FALSE; + + Info->TypeIndex = TypeIndex; + Info->dataKind = (DataKind)dataKind; + + switch (dataKind) + { + case DataIsGlobal: + case DataIsStaticLocal: + case DataIsFileStatic: + case DataIsStaticMember: + { + ULONG64 address = 0; + + // Use Address; Offset is not defined + // Note: If it is DataIsStaticMember, then this is a static member of a class defined in another module + // (it does not have an address in this module) + + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_ADDRESS, &address)) + return FALSE; + + Info->Address = address; + Info->Offset = 0; + } + break; + + case DataIsLocal: + case DataIsParam: + case DataIsObjectPtr: + case DataIsMember: + { + ULONG offset = 0; + + // Use Offset; Address is not defined + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_OFFSET, &offset)) + return FALSE; + + Info->Offset = offset; + Info->Address = 0; + } + break; + + default: + Info->Address = 0; + Info->Offset = 0; + break; + } + + return TRUE; +} + +BOOLEAN SymbolInfo_DumpType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ TypeInfo *Info + ) +{ + ULONG tag = SymTagNull; + + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + BaseAddress, + Index, + TI_GET_SYMTAG, + &tag + )) + { + return FALSE; + } + + Info->Tag = (enum SymTagEnum)tag; + + switch (tag) + { + case SymTagBaseType: + return PdbGetSymbolBasicType(BaseAddress, Index, &Info->BaseTypeInfo); + case SymTagPointerType: + return PdbGetSymbolPointerType(BaseAddress, Index, &Info->PointerTypeInfo); + case SymTagTypedef: + return PdbGetSymbolTypedefType(BaseAddress, Index, &Info->TypedefInfo); + case SymTagEnum: + return PdbGetSymbolEnumType(BaseAddress, Index, &Info->EnumInfo); + case SymTagArrayType: + return PdbGetSymbolArrayType(BaseAddress, Index, &Info->ArrayTypeInfo); + case SymTagUDT: + return SymbolInfo_DumpUDT(BaseAddress, Index, Info); + case SymTagFunctionType: + return PdbGetSymbolFunctionType(BaseAddress, Index, &Info->FunctionTypeInfo); + case SymTagFunctionArgType: + return PdbGetSymbolFunctionArgType(BaseAddress, Index, &Info->FunctionArgTypeInfo); + case SymTagBaseClass: + return PdbGetSymbolBaseClass(BaseAddress, Index, &Info->BaseClassInfo); + case SymTagData: + return PdbGetSymbolData(BaseAddress, Index, &Info->DataInfo); + } + + return FALSE; +} + +BOOLEAN SymbolInfo_DumpSymbolType( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _Inout_ TypeInfo *Info, + _Inout_ ULONG *TypeIndex + ) +{ + // Obtain the index of the symbol's type symbol + if (!SymGetTypeInfo_I(NtCurrentProcess(), Context->BaseAddress, Index, TI_GET_TYPEID, &TypeIndex)) + return FALSE; + + // Dump the type symbol + return SymbolInfo_DumpType(Context->BaseAddress, *TypeIndex, Info); +} + +BOOLEAN PdbCheckTagType( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _In_ ULONG Tag + ) +{ + ULONG symTag = SymTagNull; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_SYMTAG, &symTag)) + return FALSE; + + return symTag == Tag; +} + +BOOLEAN PdbGetSymbolSize( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG64* Size + ) +{ + ULONG index; + ULONG64 length = 0; + + // Does the symbol support "length" property ? + if (SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_GET_LENGTH, &length)) + { + *Size = length; + return TRUE; + } + else + { + // No, it does not - it can be SymTagTypedef + if (!PdbCheckTagType(BaseAddress, Index, SymTagTypedef)) + { + // No, this symbol does not have length + return FALSE; + } + else + { + // Yes, it is a SymTagTypedef - skip to its underlying type + index = Index; + + do + { + ULONG tempIndex = 0; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, &tempIndex)) + return FALSE; + + index = tempIndex; + + } while (PdbCheckTagType(BaseAddress, index, SymTagTypedef)); + + // And get the length + if (SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_LENGTH, &length)) + { + *Size = length; + return TRUE; + } + } + } + + return FALSE; +} + +BOOLEAN PdbGetSymbolArrayElementTypeIndex( + _In_ ULONG64 BaseAddress, + _In_ ULONG ArrayIndex, + _Inout_ ULONG* ElementTypeIndex + ) +{ + ULONG index; + ULONG elementIndex = 0; + + if (!PdbCheckTagType(BaseAddress, ArrayIndex, SymTagArrayType)) + return FALSE; + + // Get the array element type + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, ArrayIndex, TI_GET_TYPEID, &elementIndex)) + return FALSE; + + // If the array element type is SymTagArrayType, skip to its type + index = elementIndex; + + while (PdbCheckTagType(BaseAddress, elementIndex, SymTagArrayType)) + { + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, &elementIndex)) + return FALSE; + + index = elementIndex; + } + + // We have found the ultimate type of the array element + *ElementTypeIndex = elementIndex; + + return TRUE; +} + +BOOLEAN PdbGetSymbolArrayDimensions( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG64* pDims, + _Inout_ ULONG* Dims, + _In_ ULONG MaxDims + ) +{ + ULONG index; + ULONG dimCount; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagArrayType)) + return FALSE; + + if (MaxDims <= 0) + return FALSE; + + index = Index; + dimCount = 0; + + for (ULONG i = 0; i < MaxDims; i++) + { + ULONG typeIndex = 0; + ULONG64 length = 0; + ULONG64 typeSize = 0; + + // Length ("length" in DIA) + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_LENGTH, &length) || (length == 0)) + return FALSE; + + // Size of the current dimension + // (it is the size of its SymTagArrayType symbol divided by the size of its + // type symbol, which can be another SymTagArrayType symbol or the array element symbol) + + // Its type + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, &typeIndex)) + { + // No type - we are done + break; + } + + // Size of its type + if (!PdbGetSymbolSize(BaseAddress, typeIndex, &typeSize) || (typeSize == 0)) + return FALSE; + + // Size of the dimension + pDims[i] = length / typeSize; + dimCount++; + + /* + ULONG ElemCount = 0; + if( !SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, CurIndex, TI_GET_COUNT, &ElemCount)) + { + // and continue ... + } + else if( ElemCount != pDims[i] ) + { + _ASSERTE( !_T("TI_GET_COUNT does not match.") ); + }*/ + + // If the type symbol is not SymTagArrayType, we are done + if (!PdbCheckTagType(BaseAddress, typeIndex, SymTagArrayType)) + break; + + index = typeIndex; + } + + if (dimCount == 0) + return FALSE; + + *Dims = dimCount; + + return TRUE; +} + +BOOLEAN PdbGetSymbolChildren( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* Count, + _Inout_ TI_FINDCHILDREN_PARAMS **Params + ) +{ + ULONG length; + ULONG symbolCount = 0; + TI_FINDCHILDREN_PARAMS* symbols; + + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + BaseAddress, + Index, + TI_GET_CHILDRENCOUNT, + &symbolCount + )) + { + return FALSE; + } + + if (symbolCount == 0) + return TRUE; + + length = sizeof(TI_FINDCHILDREN_PARAMS) + symbolCount * sizeof(ULONG); + symbols = _malloca(length); + memset(symbols, 0, length); + + symbols->Count = symbolCount; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, Index, TI_FINDCHILDREN, symbols)) + return FALSE; + + *Count = symbolCount; + *Params = symbols; + + return TRUE; +} + +BOOLEAN PdbGetSymbolUdtVariables( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pVars, + _Inout_ ULONG* Vars, + _In_ ULONG MaxVars + ) +{ + ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + if (MaxVars <= 0) + return FALSE; + + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) + return FALSE; + + *Vars = 0; + + for (ULONG i = 0; i < childrenLength; i++) + { + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagData)) + { + pVars[*Vars] = symbolParams->ChildId[i]; + + (*Vars)++; + + if (*Vars == MaxVars) + break; + } + } + + _freea(symbolParams); + + return TRUE; +} + +BOOLEAN PdbGetSymbolUdtFunctions( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pFuncs, + _Inout_ ULONG* Funcs, + _In_ ULONG MaxFuncs + ) +{ + ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + if (MaxFuncs <= 0) + return FALSE; + + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) + return FALSE; + + *Funcs = 0; + + for (ULONG i = 0; i < childrenLength; i++) + { + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagFunction)) + { + pFuncs[*Funcs] = symbolParams->ChildId[i]; + + (*Funcs)++; + + if (*Funcs == MaxFuncs) + break; + } + } + + return TRUE; +} + +BOOLEAN PdbGetSymbolUdtBaseClasses( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pBases, + _Inout_ ULONG* Bases, + _In_ ULONG MaxBases + ) +{ + ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + if (MaxBases <= 0) + return FALSE; + + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) + return FALSE; + + *Bases = 0; + + for (ULONG i = 0; i < childrenLength; i++) + { + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagBaseClass)) + { + pBases[*Bases] = symbolParams->ChildId[i]; + + (*Bases)++; + + if (*Bases == MaxBases) + break; + } + } + + _freea(symbolParams); + + return TRUE; +} + +BOOLEAN PdbGetSymbolUdtUnionMembers( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pMembers, + _Inout_ ULONG* Members, + _In_ ULONG MaxMembers + ) +{ + ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagUDT)) + return FALSE; + + if (MaxMembers <= 0) + return FALSE; + + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) + return FALSE; + + *Members = 0; + + for (ULONG i = 0; i < childrenLength; i++) + { + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagData)) + { + pMembers[*Members] = symbolParams->ChildId[i]; + + (*Members)++; + + if (*Members == MaxMembers) + break; + } + } + + _freea(symbolParams); + + return TRUE; +} + +BOOLEAN PdbGetSymbolFunctionArguments( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG* pArgs, + _Inout_ ULONG* Args, + _In_ ULONG MaxArgs + ) +{ + ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagFunctionType)) + return FALSE; + + if (MaxArgs <= 0) + return FALSE; + + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) + return FALSE; + + *Args = 0; + + for (ULONG i = 0; i < childrenLength; i++) + { + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagFunctionArgType)) + { + pArgs[*Args] = symbolParams->ChildId[i]; + + (*Args)++; + + if (*Args == MaxArgs) + break; + } + } + + _freea(symbolParams); + + return TRUE; +} + +BOOLEAN PdbGetSymbolEnumerations( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG *pEnums, + _Inout_ ULONG *Enums, + _In_ ULONG MaxEnums + ) +{ + ULONG childrenLength = 0; + TI_FINDCHILDREN_PARAMS* symbolParams; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagEnum)) + return FALSE; + + if (MaxEnums <= 0) + return FALSE; + + if (!PdbGetSymbolChildren(BaseAddress, Index, &childrenLength, &symbolParams)) + return FALSE; + + *Enums = 0; + + for (ULONG i = 0; i < childrenLength; i++) + { + if (PdbCheckTagType(BaseAddress, symbolParams->ChildId[i], SymTagData)) + { + pEnums[*Enums] = symbolParams->ChildId[i]; + + (*Enums)++; + + if (*Enums == MaxEnums) + break; + } + } + + _freea(symbolParams); + + return TRUE; +} + +BOOLEAN PdbGetSymbolTypeDefTypeIndex( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG *UndTypeIndex + ) +{ + ULONG index; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagTypedef)) + return FALSE; + + // Skip to the type behind the type definition + index = Index; + + while (PdbCheckTagType(BaseAddress, index, SymTagTypedef)) + { + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) + return FALSE; + + index = *UndTypeIndex; + } + + return TRUE; +} + +BOOLEAN PdbGetSymbolPointers( + _In_ ULONG64 BaseAddress, + _In_ ULONG Index, + _Inout_ ULONG *UndTypeIndex, + _Inout_ ULONG *NumPointers + ) +{ + ULONG index; + + if (!PdbCheckTagType(BaseAddress, Index, SymTagPointerType)) + return FALSE; + + // Skip to the type pointer points to + *NumPointers = 0; + index = Index; + + while (PdbCheckTagType(BaseAddress, index, SymTagPointerType)) + { + if (!SymGetTypeInfo_I(NtCurrentProcess(), BaseAddress, index, TI_GET_TYPEID, UndTypeIndex)) + return FALSE; + + (*NumPointers)++; + + index = *UndTypeIndex; + } + + return TRUE; +} + +BOOLEAN SymbolInfo_GetTypeNameHelper( + _In_ ULONG Index, + _Inout_ PPDB_SYMBOL_CONTEXT Obj, + _Out_ PWSTR *VarName, + _Inout_ PPH_STRING_BUILDER TypeName + ) +{ + TypeInfo Info; + + if (!SymbolInfo_DumpType(Obj->BaseAddress, Index, &Info)) + { + return FALSE; + } + else + { + ULONG numPointers = 0; + + if (Info.Tag == SymTagPointerType) + { + ULONG typeIndex = 0; + + // Yes, get the number of * to show + if (!PdbGetSymbolPointers(Obj->BaseAddress, Index, &typeIndex, &numPointers)) + return FALSE; + + // Get more information about the type the pointer points to + if (!SymbolInfo_DumpType(Obj->BaseAddress, typeIndex, &Info)) + return FALSE; + + // Save the index of the type the pointer points to + Index = typeIndex; + + // ... and proceed with that type + } + + switch (Info.Tag) + { + case SymTagBaseType: + { + if (Info.BaseTypeInfo.Length == 0) + break; + + PhAppendStringBuilder2(TypeName, SymbolInfo_BaseTypeStr(Info.BaseTypeInfo.BaseType, Info.BaseTypeInfo.Length)); + PhAppendStringBuilder2(TypeName, L" "); + } + break; + case SymTagTypedef: + PhAppendStringBuilder2(TypeName, Info.TypedefInfo.Name); + PhAppendStringBuilder2(TypeName, L" "); + break; + case SymTagUDT: + { + if (Info.UdtKind) + { + // A class/structure + PhAppendStringBuilder2(TypeName, Info.UdtClassInfo.Name); + PhAppendStringBuilder2(TypeName, L" "); + + // Add the UDT and its base classes to the collection of UDT indexes + PhAddItemList(Obj->UdtList, UlongToPtr(Index)); + } + else + { + // A union + PhAppendStringBuilder2(TypeName, Info.UdtUnionInfo.Name); + PhAppendStringBuilder2(TypeName, L" "); + } + } + break; + case SymTagEnum: + PhAppendStringBuilder2(TypeName, Info.EnumInfo.Name); + break; + case SymTagFunctionType: + { + if (Info.FunctionTypeInfo.MemberFunction && Info.FunctionTypeInfo.StaticFunction) + PhAppendStringBuilder2(TypeName, L"static "); + + // return value + if (!SymbolInfo_GetTypeNameHelper(Info.FunctionTypeInfo.RetTypeIndex, Obj, VarName, TypeName)) + return FALSE; + + // calling convention + PhAppendStringBuilder2(TypeName, SymbolInfo_CallConvStr(Info.FunctionTypeInfo.CallConv)); + PhAppendStringBuilder2(TypeName, L" "); + + // If member function, save the class type index + if (Info.FunctionTypeInfo.MemberFunction) + { + PhAddItemList(Obj->UdtList, UlongToPtr(Info.FunctionTypeInfo.ClassIndex)); + + /* + // It is not needed to print the class name here, because it is contained in the function name + if (!SymbolInfo_GetTypeNameHelper(Info.sFunctionTypeInfo.ClassIndex, Obj, VarName, TypeName)) + return false; + TypeName += "::"); + */ + } + + // Print name + PhAppendStringBuilder2(TypeName, *VarName); + + // Print parameters + PhAppendStringBuilder2(TypeName, L"("); + + for (ULONG i = 0; i < Info.FunctionTypeInfo.NumArgs; i++) + { + if (!SymbolInfo_GetTypeNameHelper(Info.FunctionTypeInfo.Args[i], Obj, VarName, TypeName)) + return FALSE; + + if (i < (Info.FunctionTypeInfo.NumArgs - 1)) + PhAppendStringBuilder2(TypeName, L", "); + } + + PhAppendStringBuilder2(TypeName, L") "); + + // Print "this" adjustment value + if (Info.FunctionTypeInfo.MemberFunction && Info.FunctionTypeInfo.ThisAdjust != 0) + PhAppendFormatStringBuilder(TypeName, L": this+%u ", Info.FunctionTypeInfo.ThisAdjust); + } + break; + + case SymTagFunctionArgType: + { + if (!SymbolInfo_GetTypeNameHelper(Info.FunctionArgTypeInfo.TypeIndex, Obj, VarName, TypeName)) + return FALSE; + } + break; + case SymTagArrayType: + { + // Print element type name + if (!SymbolInfo_GetTypeNameHelper(Info.ArrayTypeInfo.ElementTypeIndex, Obj, VarName, TypeName)) + return FALSE; + + PhAppendStringBuilder2(TypeName, L" "); + + // Print dimensions + for (ULONG i = 0; i < Info.ArrayTypeInfo.NumDimensions; i++) + PhAppendFormatStringBuilder(TypeName, L"[%I64u]", Info.ArrayTypeInfo.Dimensions[i]); + } + break; + default: + { + PhAppendFormatStringBuilder(TypeName, L"Unknown+%lu", Info.Tag); + } + break; + } + + // If it is a pointer, display * characters + if (numPointers != 0) + { + for (ULONG i = 0; i < numPointers; i++) + PhAppendStringBuilder2(TypeName, L"*"); + } + } + + return TRUE; +} + +PPH_STRING SymbolInfo_GetTypeName( + _Inout_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ PWSTR VarName + ) +{ + PWSTR typeVarName = NULL; + PH_STRING_BUILDER typeNamesb; + + PhInitializeStringBuilder(&typeNamesb, 0x100); + + if (VarName) + typeVarName = VarName; + + if (!SymbolInfo_GetTypeNameHelper(Index, Context, &typeVarName, &typeNamesb)) + { + PhDeleteStringBuilder(&typeNamesb); + return NULL; + } + + return PhFinalStringBuilderString(&typeNamesb); +} + +PWSTR SymbolInfo_TagStr( + _In_ enum SymTagEnum Tag + ) +{ + switch (Tag) + { + case SymTagNull: + return L"Null"; + case SymTagExe: + return L"Exe"; + case SymTagCompiland: + return L"Compiland"; + case SymTagCompilandDetails: + return L"CompilandDetails"; + case SymTagCompilandEnv: + return L"CompilandEnv"; + case SymTagFunction: + return L"Function"; + case SymTagBlock: + return L"Block"; + case SymTagData: + return L"Data"; + case SymTagAnnotation: + return L"Annotation"; + case SymTagLabel: + return L"Label"; + case SymTagPublicSymbol: + return L"PublicSymbol"; + case SymTagUDT: + return L"UDT"; + case SymTagEnum: + return L"Enum"; + case SymTagFunctionType: + return L"FunctionType"; + case SymTagPointerType: + return L"PointerType"; + case SymTagArrayType: + return L"ArrayType"; + case SymTagBaseType: + return L"BaseType"; + case SymTagTypedef: + return L"Typedef"; + case SymTagBaseClass: + return L"BaseClass"; + case SymTagFriend: + return L"Friend"; + case SymTagFunctionArgType: + return L"FunctionArgType"; + case SymTagFuncDebugStart: + return L"FuncDebugStart"; + case SymTagFuncDebugEnd: + return L"FuncDebugEnd"; + case SymTagUsingNamespace: + return L"UsingNamespace"; + case SymTagVTableShape: + return L"VTableShape"; + case SymTagVTable: + return L"VTable"; + case SymTagCustom: + return L"Custom"; + case SymTagThunk: + return L"Thunk"; + case SymTagCustomType: + return L"CustomType"; + case SymTagManagedType: + return L"ManagedType"; + case SymTagDimension: + return L"Dimension"; + } + + return L"UNKNOWN"; + +} + +PWSTR SymbolInfo_BaseTypeStr( + _In_ BasicType Type, + _In_ ULONG64 Length + ) +{ + switch (Type) + { + case btNoType: + return L""; + case btVoid: + return L"void"; + case btChar: + return L"char"; + case btWChar: + return L"wchar_t"; + case btInt: + { + if (Length == 0) + { + return L"int"; + } + else + { + if (Length == 1) + return L"char"; + else if (Length == 2) + return L"short"; + else + return L"int"; + } + } + case btUInt: + { + if (Length == 0) + { + return L"unsigned int"; + } + else + { + if (Length == 1) + return L"unsigned char"; + else if (Length == 2) + return L"unsigned short"; + else + return L"unsigned int"; + } + } + break; + case btFloat: + { + if (Length == 0) + { + return L"float"; + } + else + { + if (Length == 4) + return L"float"; + else + return L"double"; + } + } + break; + case btBCD: + return L"BCD"; + case btBool: + return L"bool"; + case btLong: + return L"long"; + case btULong: + return L"unsigned long"; + case btCurrency: + return L"Currency"; + case btDate: + return L"Date"; + case btVariant: + return L"Variant"; + case btComplex: + return L"Complex"; + case btBit: + return L"Bit"; + case btBSTR: + return L"BSTR"; + case btHresult: + return L"HRESULT"; + } + + return L"UNKNOWN"; + +} + +PWSTR SymbolInfo_CallConvStr( + _In_ CV_call_e CallConv + ) +{ + switch (CallConv) + { + case CV_CALL_NEAR_C: + return L"__cdecl"; + case CV_CALL_FAR_C: + return L"FAR_C"; + case CV_CALL_NEAR_PASCAL: + return L"NEAR_PASCAL"; + case CV_CALL_FAR_PASCAL: + return L"FAR_PASCAL"; + case CV_CALL_NEAR_FAST: + return L"__fastcall"; + case CV_CALL_FAR_FAST: + return L"FAR_FAST"; + case CV_CALL_SKIPPED: + return L"SKIPPED"; + case CV_CALL_NEAR_STD: + return L"__stdcall"; + case CV_CALL_FAR_STD: + return L"FAR_STD"; + case CV_CALL_NEAR_SYS: + return L"__syscall"; + case CV_CALL_FAR_SYS: + return L"FAR_SYS"; + case CV_CALL_THISCALL: + return L"__thiscall"; + case CV_CALL_MIPSCALL: + return L"MIPSCALL"; + case CV_CALL_GENERIC: + return L"GENERIC"; + case CV_CALL_ALPHACALL: + return L"ALPHACALL"; + case CV_CALL_PPCCALL: + return L"PPCCALL"; + case CV_CALL_SHCALL: + return L"SHCALL"; + case CV_CALL_ARMCALL: + return L"ARMCALL"; + case CV_CALL_AM33CALL: + return L"AM33CALL"; + case CV_CALL_TRICALL: + return L"TRICALL"; + case CV_CALL_SH5CALL: + return L"SH5CALL"; + case CV_CALL_M32RCALL: + return L"M32RCALL"; + case CV_CALL_RESERVED: + return L"RESERVED"; + } + + return L"UNKNOWN"; + +} + +PWSTR SymbolInfo_DataKindFromSymbolInfo( + _In_ PSYMBOL_INFOW SymbolInfo + ) +{ + ULONG dataKindType = 0; + + if (!SymGetTypeInfo_I(NtCurrentProcess(), SymbolInfo->ModBase, SymbolInfo->Index, TI_GET_DATAKIND, &dataKindType)) + return L"UNKNOWN"; + + return SymbolInfo_DataKindStr(dataKindType); +} + +PWSTR SymbolInfo_DataKindStr( + _In_ DataKind SymDataKind + ) +{ + switch (SymDataKind) + { + case DataIsLocal: + return L"LOCAL_VAR"; + case DataIsStaticLocal: + return L"STATIC_LOCAL_VAR"; + case DataIsParam: + return L"PARAMETER"; + case DataIsObjectPtr: + return L"OBJECT_PTR"; + case DataIsFileStatic: + return L"STATIC_VAR"; + case DataIsGlobal: + return L"GLOBAL_VAR"; + case DataIsMember: + return L"MEMBER"; + case DataIsStaticMember: + return L"STATIC_MEMBER"; + case DataIsConstant: + return L"CONSTANT"; + } + + return L"UNKNOWN"; +} + +VOID SymbolInfo_SymbolLocationStr( + _In_ PSYMBOL_INFOW SymbolInfo, + _In_ PWSTR Buffer + ) +{ + if (SymbolInfo->Flags & SYMFLAG_REGISTER) + { + PWSTR regString = SymbolInfo_RegisterStr(SymbolInfo->Register); + + if (regString) + wcscpy(Buffer, regString); + else + _swprintf(Buffer, L"Reg+%u", SymbolInfo->Register); + } + else if (SymbolInfo->Flags & SYMFLAG_REGREL) + { + WCHAR szReg[32]; + PWSTR regString = SymbolInfo_RegisterStr(SymbolInfo->Register); + + if (regString) + wcscpy(szReg, regString); + else + _swprintf(szReg, L"Reg+%u", SymbolInfo->Register); + + _swprintf(Buffer, L"%s+%I64u", szReg, SymbolInfo->Address); + //PhPrintPointer(Buffer, PTR_ADD_OFFSET(SymbolInfo->ModBase, SymbolInfo->Address)); + } + else if (SymbolInfo->Flags & SYMFLAG_FRAMEREL) + { + wcscpy(Buffer, L"N/A"); + } + else + { + if (SymbolInfo->Address) + PhPrintPointer(Buffer, PTR_SUB_OFFSET(SymbolInfo->Address, SymbolInfo->ModBase)); + } +} + +PWSTR SymbolInfo_RegisterStr( + _In_ CV_HREG_e RegCode + ) +{ + switch (RegCode) + { + case CV_REG_EAX: + return L"EAX"; + case CV_REG_ECX: + return L"ECX"; + case CV_REG_EDX: + return L"EDX"; + case CV_REG_EBX: + return L"EBX"; + case CV_REG_ESP: + return L"ESP"; + case CV_REG_EBP: + return L"EBP"; + case CV_REG_ESI: + return L"ESI"; + case CV_REG_EDI: + return L"EDI"; + } + + return L"Unknown"; +} + +PWSTR SymbolInfo_UdtKindStr( + _In_ UdtKind KindType + ) +{ + switch (KindType) + { + case UdtStruct: + return L"STRUCT"; + case UdtClass: + return L"CLASS"; + case UdtUnion: + return L"UNION"; + } + + return L"UNKNOWN"; +} + +PWSTR SymbolInfo_LocationTypeStr( + _In_ LocationType LocType + ) +{ + switch (LocType) + { + case LocIsNull: + return L"Null"; + case LocIsStatic: + return L"Static"; + case LocIsTLS: + return L"TLS"; + case LocIsRegRel: + return L"RegRel"; + case LocIsThisRel: + return L"ThisRel"; + case LocIsEnregistered: + return L"Enregistered"; + case LocIsBitField: + return L"BitField"; + case LocIsSlot: + return L"Slot"; + case LocIsIlRel: + return L"IlRel"; + case LocInMetaData: + return L"MetaData"; + case LocIsConstant: + return L"Constant"; + } + + return L"Unknown"; +} + +/////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK EnumCallbackProc( + _In_ PSYMBOL_INFOW SymbolInfo, + _In_ ULONG SymbolSize, + _In_ PVOID Context + ) +{ + if (SymbolInfo) + { + switch (SymbolInfo->Tag) + { + case SymTagFunction: + { + PPDB_SYMBOL_CONTEXT context = Context; + PPV_SYMBOL_NODE symbol; + + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + + symbol->Type = PV_SYMBOL_TYPE_FUNCTION; + symbol->Address = SymbolInfo->Address; + symbol->Size = SymbolInfo->Size; + symbol->Name = PhCreateStringEx(SymbolInfo->Name, SymbolInfo->NameLen * sizeof(WCHAR)); + symbol->Data = SymbolInfo_GetTypeName( + context, + SymbolInfo->TypeIndex, + SymbolInfo->Name + ); + SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); + + // Enumerate parameters and variables... + PdbDumpAddress(context, SymbolInfo->Address); + } + break; + case SymTagData: + { + PPDB_SYMBOL_CONTEXT context = Context; + PPV_SYMBOL_NODE symbol; + PWSTR symDataKind; + ULONG dataKindType = 0; + + if (SymbolInfo->Address == 0) + break; + + if (!SymGetTypeInfo_I( + NtCurrentProcess(), + SymbolInfo->ModBase, + SymbolInfo->Index, + TI_GET_DATAKIND, + &dataKindType + )) + { + break; + } + + symDataKind = SymbolInfo_DataKindStr(dataKindType); + + if ( + dataKindType == DataIsLocal || + dataKindType == DataIsParam || + dataKindType == DataIsObjectPtr + ) + { + break; + } + + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + + switch (dataKindType) + { + case DataIsLocal: + { + // TODO: The address variable is FUNCTION+OFFSET + //SymbolInfo->Address = SymbolInfo->Address; + symbol->Type = PV_SYMBOL_TYPE_LOCAL_VAR; + } + break; + case DataIsStaticLocal: + symbol->Type = PV_SYMBOL_TYPE_STATIC_LOCAL_VAR; + break; + case DataIsParam: + symbol->Type = PV_SYMBOL_TYPE_PARAMETER; + break; + case DataIsObjectPtr: + symbol->Type = PV_SYMBOL_TYPE_OBJECT_PTR; + break; + case DataIsFileStatic: + symbol->Type = PV_SYMBOL_TYPE_STATIC_VAR; + break; + case DataIsGlobal: + symbol->Type = PV_SYMBOL_TYPE_GLOBAL_VAR; + break; + case DataIsMember: + symbol->Type = PV_SYMBOL_TYPE_STRUCT; + break; + case DataIsStaticMember: + symbol->Type = PV_SYMBOL_TYPE_STATIC_MEMBER; + break; + case DataIsConstant: + symbol->Type = PV_SYMBOL_TYPE_CONSTANT; + break; + default: + symbol->Type = PV_SYMBOL_TYPE_UNKNOWN; + break; + } + + symbol->Address = SymbolInfo->Address; + symbol->Size = SymbolInfo->Size; + symbol->Name = PhCreateStringEx(SymbolInfo->Name, SymbolInfo->NameLen * sizeof(WCHAR)); + symbol->Data = SymbolInfo_GetTypeName(context, SymbolInfo->TypeIndex, SymbolInfo->Name); + SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); + } + break; + default: + { + PPDB_SYMBOL_CONTEXT context = Context; + PPV_SYMBOL_NODE symbol; + + if (SymbolInfo->Address == 0) + break; + + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + + symbol->Type = PV_SYMBOL_TYPE_SYMBOL; + symbol->Address = SymbolInfo->Address; + symbol->Size = SymbolInfo->Size; + symbol->Data = SymbolInfo_GetTypeName(context, SymbolInfo->TypeIndex, SymbolInfo->Name); + SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + + if (SymbolInfo->Name[0]) // HACK + { + if (SymbolInfo->NameLen) + symbol->Name = PhCreateStringEx(SymbolInfo->Name, SymbolInfo->NameLen * sizeof(WCHAR)); + else + symbol->Name = PhCreateString(SymbolInfo->Name); + } + + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); + } + break; + } + } + + return TRUE; +} + +VOID PrintClassInfo( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG Index, + _In_ UdtClassInfo* Info + ) +{ + //PPV_SYMBOL_NODE symbol; + //symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + //memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + //symbol->Type = PV_SYMBOL_TYPE_CLASS; + //symbol->Address = VarInfo.sDataInfo.Address; + //symbol->Size = (ULONG)Info->Offset; + //symbol->Name = SymbolInfo_GetTypeName(Context, Index, Info->Name);// PhCreateString(SymbolInfo_UdtKindStr(Info->UDTKind)); + //symbol->Data = SymbolInfo_GetTypeName(Context, Index, Info->Name); + //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + //Info.Nested + //if (PhEqualString2(symbol->Name, L"STRUCT", TRUE)) + // symbol->Type = PV_SYMBOL_TYPE_STRUCT; + //PhAcquireQueuedLockExclusive(&SearchResultsLock); + //PhAddItemList(SearchResults, symbol); + //PhReleaseQueuedLockExclusive(&SearchResultsLock); + + for (ULONG i = 0; i < Info->NumVariables; i++) + { + TypeInfo VarInfo; + + if (!SymbolInfo_DumpType(Context->BaseAddress, Info->Variables[i], &VarInfo)) + { + // Continue + } + else if (VarInfo.Tag != SymTagData) + { + // Unexpected symbol tag. + } + else + { + //PPV_SYMBOL_NODE symbol; + // + //symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + //memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + // + //symbol->Type = PV_SYMBOL_TYPE_MEMBER; + // + // Location + switch (VarInfo.DataInfo.dataKind) + { + case DataIsGlobal: + case DataIsStaticLocal: + case DataIsFileStatic: + case DataIsStaticMember: + { + //symbol->Address = VarInfo.sDataInfo.Address; + } + break; + case DataIsLocal: + case DataIsParam: + case DataIsObjectPtr: + case DataIsMember: + { + //symbol->Address = VarInfo.sDataInfo.Offset; + } + break; + default: + break; // TODO Add support for constants + } + + //symbol->Size = (ULONG)VarInfo.sDataInfo.Offset; + //symbol->Name = PhCreateString(VarInfo.sDataInfo.Name); + //symbol->Data = SymbolInfo_GetTypeName(Context, VarInfo.sDataInfo.TypeIndex, VarInfo.sDataInfo.Name); + //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + //Info.Info.sUdtClassInfo.Nested + //Info.Info.sUdtClassInfo.NumVariables + //PhAcquireQueuedLockExclusive(&SearchResultsLock); + //PhAddItemList(SearchResults, symbol); + //PhReleaseQueuedLockExclusive(&SearchResultsLock); + } + } + + for (ULONG i = 0; i < Info->NumFunctions; i++) + { + TypeInfo VarInfo; + + if (!SymbolInfo_DumpType(Context->BaseAddress, Info->Variables[i], &VarInfo)) + { + // Continue + } + else if (VarInfo.Tag != SymTagFunction) + { + // Unexpected symbol tag. + } + else + { + PPV_SYMBOL_NODE symbol; + + symbol = PhAllocate(sizeof(PV_SYMBOL_NODE)); + memset(symbol, 0, sizeof(PV_SYMBOL_NODE)); + + symbol->Type = PV_SYMBOL_TYPE_FUNCTION; + symbol->Address = VarInfo.DataInfo.Address; + symbol->Size = (ULONG)VarInfo.DataInfo.Offset; + symbol->Name = PhCreateString(VarInfo.DataInfo.Name); + symbol->Data = SymbolInfo_GetTypeName(Context, VarInfo.DataInfo.TypeIndex, VarInfo.DataInfo.Name); + //SymbolInfo_SymbolLocationStr(SymbolInfo, symbol->Pointer); + // Info.Info.sUdtClassInfo.Nested + // Info.Info.sUdtClassInfo.NumVariables + + PhAcquireQueuedLockExclusive(&SearchResultsLock); + PhAddItemList(SearchResults, symbol); + PhReleaseQueuedLockExclusive(&SearchResultsLock); + + // Enumerate function parameters and local variables... + PdbDumpAddress(Context, VarInfo.DataInfo.Address); + } + } + + for (ULONG i = 0; i < Info->NumBaseClasses; i++) + { + TypeInfo baseInfo; + + if (!SymbolInfo_DumpType(Context->BaseAddress, Info->BaseClasses[i], &baseInfo)) + { + // Continue + } + else if (baseInfo.Tag != SymTagBaseClass) + { + // Unexpected symbol tag + } + else + { + TypeInfo BaseUdtInfo; + + // Obtain the next base class + if (!SymbolInfo_DumpType(Context->BaseAddress, baseInfo.BaseClassInfo.TypeIndex, &BaseUdtInfo)) + { + // Continue + } + else if (BaseUdtInfo.Tag != SymTagUDT) + { + // Unexpected symbol tag + } + else + { + if (baseInfo.BaseClassInfo.Virtual) + { + //PhAddItemList(L"VIRTUAL_BASE_CLASS", BaseUdtInfo.sUdtClassInfo.Name) + } + else + { + //PhAddItemList(L"BASE_CLASS", BaseUdtInfo.sUdtClassInfo.Name) + } + } + } + } +} + +VOID PrintUserDefinedTypes( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + ULONG i; + ULONG index; + TypeInfo info; + + for (i = 0; i < Context->UdtList->Count; i++) + { + index = PtrToUlong(Context->UdtList->Items[i]); + + if (SymbolInfo_DumpType(Context->BaseAddress, index, &info)) + { + if (info.Tag == SymTagUDT) + { + if (info.UdtKind) + { + // Print information about the class + PrintClassInfo(Context, index, &info.UdtClassInfo); + + // Print information about its base classes + for (ULONG i = 0; i < info.UdtClassInfo.NumBaseClasses; i++) + { + TypeInfo baseInfo; + + if (!SymbolInfo_DumpType(Context->BaseAddress, info.UdtClassInfo.BaseClasses[i], &baseInfo)) + { + // Continue + } + else if (baseInfo.Tag != SymTagBaseClass) + { + // Continue + } + else + { + // Obtain information about the base class + TypeInfo baseUdtInfo; + + if (!SymbolInfo_DumpType(Context->BaseAddress, baseInfo.BaseClassInfo.TypeIndex, &baseUdtInfo)) + { + // Continue + } + else if (baseUdtInfo.Tag != SymTagUDT) + { + // Continue + } + else + { + // Print information about the base class + PrintClassInfo(Context, baseInfo.BaseClassInfo.TypeIndex, &baseUdtInfo.UdtClassInfo); + } + } + } + } + else + { + //info.sUdtUnionInfo.Name + //PhCreateString(SymbolInfo_UdtKindStr(Info->UDTKind)); + //SymbolInfo_GetTypeName(Context, index, info.sUdtUnionInfo.Name); + } + } + } + } +} + +VOID ShowModuleSymbolInfo( + _In_ ULONG64 BaseAddress + ) +{ + IMAGEHLP_MODULE64 info = { sizeof(IMAGEHLP_MODULE64) }; + + if (!SymGetModuleInfoW64_I(NtCurrentProcess(), BaseAddress, &info)) + return; + + switch (info.SymType) + { + case SymNone: + //PhCreateString(L"No symbols available for the module."); + break; + case SymExport: + //PhFormatString(L"Loaded Exports symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymCoff: + //PhFormatString(L"Loaded COFF symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymCv: + //PhFormatString(L"Loaded CodeView symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymSym: + //PhFormatString(L"Loaded SYM symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymVirtual: + //PhFormatString(L"Loaded Virtual symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymPdb: + //PhFormatString(L"Loaded PDB symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymDia: + //PhFormatString(L"Loaded DIA symbols, Type information: %s", info.TypeInfo ? L"Available" : L"Not available"); + break; + case SymDeferred: + //PhCreateString(L"Loaded Deferred symbols"); // not actually loaded + break; + default: + //PhCreateString(L"Error: Unknown symbol format."); + break; + } + + //if (wcslen(info.ImageName) > 0) + //L"Image name: %s\n" info.ImageName + //if (wcslen(info.LoadedImageName) > 0) + //L"Loaded image name: %s\n" info.LoadedImageName + //if (wcslen(info.LoadedPdbName) > 0) + //L"PDB file name: %s\n" info.LoadedPdbName + // (It can only happen if the debug information is contained in a separate file (.DBG or .PDB) + //if (info.PdbUnmatched || info.DbgUnmatched) + // L"Warning: Unmatched symbols. + + //PhaFormatString(L"Line numbers: %s\n", info.LineNumbers ? L"Available" : L"Not available")->Buffer + //PhaFormatString(L"Global symbols: %s\n", info.GlobalSymbols ? L"Available" : L"Not available")->Buffer + //PhaFormatString(L"Type information: %s\n", info.TypeInfo ? L"Available" : L"Not available")->Buffer + //PhaFormatString(L"Source indexing: %s\n", info.SourceIndexed ? L"Yes" : L"No")->Buffer + //PhaFormatString(L"Public symbols: %s\n", info.Publics ? L"Available" : L"Not available")->Buffer +} + +BOOLEAN PepSymbolProviderInitialization( + VOID + ) +{ +#ifdef _WIN64 + static PH_STRINGREF windowsKitsRootKeyName = PH_STRINGREF_INIT(L"Software\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots"); +#else + static PH_STRINGREF windowsKitsRootKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows Kits\\Installed Roots"); +#endif + static PH_STRINGREF dbghelpFileName = PH_STRINGREF_INIT(L"dbghelp.dll"); + static PH_STRINGREF symsrvFileName = PH_STRINGREF_INIT(L"symsrv.dll"); + PVOID dbghelpHandle; + PVOID symsrvHandle; + HANDLE keyHandle; + + if (PhFindLoaderEntry(NULL, NULL, &dbghelpFileName) && + PhFindLoaderEntry(NULL, NULL, &symsrvFileName)) + { + dbghelpHandle = PhGetLoaderEntryDllBase(L"dbghelp.dll"); + SymInitializeW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymInitializeW", 0); + SymCleanup_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymCleanup", 0); + SymEnumSymbolsW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0); + SymEnumTypesW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymEnumTypesW", 0); + SymSetSearchPathW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetSearchPathW", 0); + SymGetOptions_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetOptions", 0); + SymSetOptions_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetOptions", 0); + SymLoadModuleExW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0); + SymGetModuleInfoW64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetModuleInfoW64", 0); + SymGetTypeFromNameW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetTypeFromNameW", 0); + SymGetTypeInfo_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); + SymSetContext_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetContext", 0); + SymSearchW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSearchW", 0); + + if (SymInitializeW_I && SymCleanup_I && SymEnumSymbolsW_I && SymEnumTypesW_I && SymSetSearchPathW_I && + SymGetOptions_I && SymSetOptions_I && SymLoadModuleExW_I && SymGetModuleInfoW64_I && + SymGetTypeFromNameW_I && SymGetTypeInfo_I && SymSetContext_I && SymSearchW_I) + { + return TRUE; + } + } + + dbghelpHandle = NULL; + symsrvHandle = NULL; + + if (NT_SUCCESS(PhOpenKey( + &keyHandle, + KEY_READ, + PH_KEY_LOCAL_MACHINE, + &windowsKitsRootKeyName, + 0 + ))) + { + PPH_STRING winsdkPath; + PPH_STRING dbghelpName; + PPH_STRING symsrvName; + + winsdkPath = PhQueryRegistryString(keyHandle, L"KitsRoot10"); // Windows 10 SDK + + if (PhIsNullOrEmptyString(winsdkPath)) + PhMoveReference(&winsdkPath, PhQueryRegistryString(keyHandle, L"KitsRoot81")); // Windows 8.1 SDK + + if (PhIsNullOrEmptyString(winsdkPath)) + PhMoveReference(&winsdkPath, PhQueryRegistryString(keyHandle, L"KitsRoot")); // Windows 8 SDK + + if (!PhIsNullOrEmptyString(winsdkPath)) + { +#ifdef _WIN64 + PhMoveReference(&winsdkPath, PhConcatStringRefZ(&winsdkPath->sr, L"\\Debuggers\\x64\\")); +#else + PhMoveReference(&winsdkPath, PhConcatStringRefZ(&winsdkPath->sr, L"\\Debuggers\\x86\\")); +#endif + } + + if (winsdkPath) + { + if (dbghelpName = PhConcatStringRef2(&winsdkPath->sr, &dbghelpFileName)) + { + dbghelpHandle = LoadLibrary(dbghelpName->Buffer); + PhDereferenceObject(dbghelpName); + } + + if (symsrvName = PhConcatStringRef2(&winsdkPath->sr, &symsrvFileName)) + { + symsrvHandle = LoadLibrary(symsrvName->Buffer); + PhDereferenceObject(symsrvName); + } + + PhDereferenceObject(winsdkPath); + } + + NtClose(keyHandle); + } + + if (!dbghelpHandle) + dbghelpHandle = LoadLibrary(L"dbghelp.dll"); + + if (!symsrvHandle) + symsrvHandle = LoadLibrary(L"symsrv.dll"); + + if (dbghelpHandle) + { + SymInitializeW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymInitializeW", 0); + SymCleanup_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymCleanup", 0); + SymEnumSymbolsW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymEnumSymbolsW", 0); + SymEnumTypesW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymEnumTypesW", 0); + SymSetSearchPathW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetSearchPathW", 0); + SymGetOptions_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetOptions", 0); + SymSetOptions_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetOptions", 0); + SymLoadModuleExW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymLoadModuleExW", 0); + SymGetModuleInfoW64_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetModuleInfoW64", 0); + SymGetTypeFromNameW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetTypeFromNameW", 0); + SymGetTypeInfo_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymGetTypeInfo", 0); + SymSetContext_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSetContext", 0); + SymSearchW_I = PhGetDllBaseProcedureAddress(dbghelpHandle, "SymSearchW", 0); + } + + if (SymInitializeW_I && SymCleanup_I && SymEnumSymbolsW_I && SymEnumTypesW_I && SymSetSearchPathW_I && + SymGetOptions_I && SymSetOptions_I && SymLoadModuleExW_I && SymGetModuleInfoW64_I && + SymGetTypeFromNameW_I && SymGetTypeInfo_I && SymSetContext_I && SymSearchW_I) + { + return TRUE; + } + + return FALSE; +} + +NTSTATUS PeDumpFileSymbols( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + NTSTATUS status; + HANDLE fileHandle = NULL; + ULONG64 baseAddress = 0; + LARGE_INTEGER fileSize; + + if (!PepSymbolProviderInitialization()) + return STATUS_FAIL_CHECK; + + SymSetOptions_I( + SymGetOptions_I() | + SYMOPT_AUTO_PUBLICS | SYMOPT_CASE_INSENSITIVE | + SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_INCLUDE_32BIT_MODULES | + SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_UNDNAME // SYMOPT_DEBUG + ); + + if (!SymInitializeW_I(NtCurrentProcess(), NULL, FALSE)) + return 1; + + if (!SymSetSearchPathW_I(NtCurrentProcess(), L"SRV*C:\\symbols*https://msdl.microsoft.com/download/symbols")) + goto CleanupExit; + + if (!NT_SUCCESS(status = PhCreateFileWin32( + &fileHandle, + PhGetString(PvFileName), + FILE_GENERIC_READ, + 0, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + goto CleanupExit; + + if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0) + goto CleanupExit; + + if (PhEndsWithString2(PvFileName, L".pdb", TRUE)) + baseAddress = 0x10000000; + + if (Context->BaseAddress = SymLoadModuleExW_I( + NtCurrentProcess(), + fileHandle, + PhGetString(PvFileName), + NULL, + baseAddress, + (ULONG)fileSize.QuadPart, + NULL, + 0 + )) + { + //ShowModuleSymbolInfo(symbolBaseAddress); + SymEnumSymbolsW_I(NtCurrentProcess(), Context->BaseAddress, NULL, EnumCallbackProc, Context); + + // Enumerate user defined types. + SymEnumTypesW_I(NtCurrentProcess(), Context->BaseAddress, EnumCallbackProc, Context); + //PrintUserDefinedTypes(Context); // Commented out due to verbosity. + + if ( + PhFindStringInString(PvFileName, 0, L"ntkrnlmp.pdb") != -1 || + PhFindStringInString(PvFileName, 0, L"ntoskrnl.exe") != -1 // HACK: SymSearchW crashes for ole32.dll + ) + { + // NOTE: The SymEnumSymbolsW and SymEnumTypesW functions don't enumerate exported DATA symbols such as + // as PsActiveProcessHead and PsLoadedModuleList from ntoskrnl.exe?? + // TODO: SymSearchW does enumerate those symbols but we'll need to filter out duplicate symbol entries properly or + // find out why the SymEnumSymbolsW and SymEnumTypesW functions can't enumerate those symbols. + SymSearchW_I( + NtCurrentProcess(), + Context->BaseAddress, + 0, + 0, + NULL, + 0, + EnumCallbackProc, + Context, + SYMSEARCH_RECURSE | SYMSEARCH_ALLITEMS + ); + } + } + +CleanupExit: + + SymCleanup_I(NtCurrentProcess()); + + if (fileHandle) + NtClose(fileHandle); + + PostMessage(Context->DialogHandle, WM_PV_SEARCH_FINISHED, 0, 0); + + return 0; +} + +VOID PdbDumpAddress( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ ULONG64 Address + ) +{ + IMAGEHLP_STACK_FRAME sf; + + sf.InstructionOffset = Address; + + SymSetContext_I(NtCurrentProcess(), &sf, 0); + SymEnumSymbolsW_I(NtCurrentProcess(), 0, NULL, EnumCallbackProc, Context); +} diff --git a/tools/peview/pdbprp.c b/tools/peview/pdbprp.c new file mode 100644 index 000000000000..328514e32675 --- /dev/null +++ b/tools/peview/pdbprp.c @@ -0,0 +1,1269 @@ +/* + * PE viewer - + * pdb support + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include "colmgr.h" + +#pragma region copied from appsup.c + +VOID PhInitializeTreeNewColumnMenu( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data + ) +{ + PhInitializeTreeNewColumnMenuEx(Data, 0); +} + +VOID PhInitializeTreeNewColumnMenuEx( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data, + _In_ ULONG Flags + ) +{ + PPH_EMENU_ITEM resetSortMenuItem = NULL; + PPH_EMENU_ITEM sizeColumnToFitMenuItem; + PPH_EMENU_ITEM sizeAllColumnsToFitMenuItem; + PPH_EMENU_ITEM hideColumnMenuItem = NULL; + PPH_EMENU_ITEM chooseColumnsMenuItem = NULL; + ULONG minimumNumberOfColumns; + + Data->Menu = PhCreateEMenu(); + Data->Selection = NULL; + Data->ProcessedId = 0; + + sizeColumnToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID, L"Size column to fit", NULL, NULL); + sizeAllColumnsToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID, L"Size all columns to fit", NULL, NULL); + + if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY)) + { + hideColumnMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_HIDE_COLUMN_ID, L"Hide column", NULL, NULL); + chooseColumnsMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID, L"Choose columns...", NULL, NULL); + } + + if (Flags & PH_TN_COLUMN_MENU_SHOW_RESET_SORT) + { + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + TreeNew_GetSort(Data->TreeNewHandle, &sortColumn, &sortOrder); + + if (sortOrder != Data->DefaultSortOrder || (Data->DefaultSortOrder != NoSortOrder && sortColumn != Data->DefaultSortColumn)) + resetSortMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_RESET_SORT_ID, L"Reset sort", NULL, NULL); + } + + PhInsertEMenuItem(Data->Menu, sizeColumnToFitMenuItem, ULONG_MAX); + PhInsertEMenuItem(Data->Menu, sizeAllColumnsToFitMenuItem, ULONG_MAX); + + if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY)) + { + PhInsertEMenuItem(Data->Menu, hideColumnMenuItem, ULONG_MAX); + + if (resetSortMenuItem) + PhInsertEMenuItem(Data->Menu, resetSortMenuItem, ULONG_MAX); + + PhInsertEMenuItem(Data->Menu, PhCreateEMenuSeparator(), ULONG_MAX); + PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, ULONG_MAX); + + if (TreeNew_GetFixedColumn(Data->TreeNewHandle)) + minimumNumberOfColumns = 2; // don't allow user to remove all normal columns (the fixed column can never be removed) + else + minimumNumberOfColumns = 1; + + if (!Data->MouseEvent || !Data->MouseEvent->Column || + Data->MouseEvent->Column->Fixed || // don't allow the fixed column to be hidden + TreeNew_GetVisibleColumnCount(Data->TreeNewHandle) < minimumNumberOfColumns + 1 + ) + { + hideColumnMenuItem->Flags |= PH_EMENU_DISABLED; + } + } + else + { + if (resetSortMenuItem) + PhInsertEMenuItem(Data->Menu, resetSortMenuItem, ULONG_MAX); + } + + if (!Data->MouseEvent || !Data->MouseEvent->Column) + { + sizeColumnToFitMenuItem->Flags |= PH_EMENU_DISABLED; + } +} + +VOID PhpEnsureValidSortColumnTreeNew( + _Inout_ HWND TreeNewHandle, + _In_ ULONG DefaultSortColumn, + _In_ PH_SORT_ORDER DefaultSortOrder + ) +{ + ULONG sortColumn; + PH_SORT_ORDER sortOrder; + + // Make sure the column we're sorting by is actually visible, and if not, don't sort anymore. + + TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder); + + if (sortOrder != NoSortOrder) + { + PH_TREENEW_COLUMN column; + + TreeNew_GetColumn(TreeNewHandle, sortColumn, &column); + + if (!column.Visible) + { + if (DefaultSortOrder != NoSortOrder) + { + // Make sure the default sort column is visible. + TreeNew_GetColumn(TreeNewHandle, DefaultSortColumn, &column); + + if (!column.Visible) + { + ULONG maxId; + ULONG id; + BOOLEAN found; + + // Use the first visible column. + maxId = TreeNew_GetMaxId(TreeNewHandle); + id = 0; + found = FALSE; + + while (id <= maxId) + { + if (TreeNew_GetColumn(TreeNewHandle, id, &column)) + { + if (column.Visible) + { + DefaultSortColumn = id; + found = TRUE; + break; + } + } + + id++; + } + + if (!found) + { + DefaultSortColumn = 0; + DefaultSortOrder = NoSortOrder; + } + } + } + + TreeNew_SetSort(TreeNewHandle, DefaultSortColumn, DefaultSortOrder); + } + } +} + +BOOLEAN PhHandleTreeNewColumnMenu( + _Inout_ PPH_TN_COLUMN_MENU_DATA Data + ) +{ + if (!Data->Selection) + return FALSE; + + switch (Data->Selection->Id) + { + case PH_TN_COLUMN_MENU_RESET_SORT_ID: + { + TreeNew_SetSort(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); + } + break; + case PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID: + { + if (Data->MouseEvent && Data->MouseEvent->Column) + { + TreeNew_AutoSizeColumn(Data->TreeNewHandle, Data->MouseEvent->Column->Id, 0); + } + } + break; + case PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID: + { + ULONG maxId; + ULONG id; + + maxId = TreeNew_GetMaxId(Data->TreeNewHandle); + id = 0; + + while (id <= maxId) + { + TreeNew_AutoSizeColumn(Data->TreeNewHandle, id, 0); + id++; + } + } + break; + case PH_TN_COLUMN_MENU_HIDE_COLUMN_ID: + { + PH_TREENEW_COLUMN column; + + if (Data->MouseEvent && Data->MouseEvent->Column && !Data->MouseEvent->Column->Fixed) + { + column.Id = Data->MouseEvent->Column->Id; + column.Visible = FALSE; + TreeNew_SetColumn(Data->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column); + PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); + InvalidateRect(Data->TreeNewHandle, NULL, FALSE); + } + } + break; + case PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID: + { + //PhShowChooseColumnsDialog(Data->TreeNewHandle, Data->TreeNewHandle, PH_CONTROL_TYPE_TREE_NEW); + PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder); + } + break; + default: + return FALSE; + } + + Data->ProcessedId = Data->Selection->Id; + + return TRUE; +} + +VOID PhDeleteTreeNewColumnMenu( + _In_ PPH_TN_COLUMN_MENU_DATA Data + ) +{ + if (Data->Menu) + { + PhDestroyEMenu(Data->Menu); + Data->Menu = NULL; + } +} + +VOID PhInitializeTreeNewFilterSupport( + _Out_ PPH_TN_FILTER_SUPPORT Support, + _In_ HWND TreeNewHandle, + _In_ PPH_LIST NodeList + ) +{ + Support->FilterList = NULL; + Support->TreeNewHandle = TreeNewHandle; + Support->NodeList = NodeList; +} + +VOID PhDeleteTreeNewFilterSupport( + _In_ PPH_TN_FILTER_SUPPORT Support + ) +{ + PhDereferenceObject(Support->FilterList); +} + +PPH_TN_FILTER_ENTRY PhAddTreeNewFilter( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TN_FILTER_FUNCTION Filter, + _In_opt_ PVOID Context + ) +{ + PPH_TN_FILTER_ENTRY entry; + + entry = PhAllocate(sizeof(PH_TN_FILTER_ENTRY)); + entry->Filter = Filter; + entry->Context = Context; + + if (!Support->FilterList) + Support->FilterList = PhCreateList(2); + + PhAddItemList(Support->FilterList, entry); + + return entry; +} + +VOID PhRemoveTreeNewFilter( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TN_FILTER_ENTRY Entry + ) +{ + ULONG index; + + if (!Support->FilterList) + return; + + index = PhFindItemList(Support->FilterList, Entry); + + if (index != ULONG_MAX) + { + PhRemoveItemList(Support->FilterList, index); + PhFree(Entry); + } +} + +BOOLEAN PhApplyTreeNewFiltersToNode( + _In_ PPH_TN_FILTER_SUPPORT Support, + _In_ PPH_TREENEW_NODE Node + ) +{ + BOOLEAN show; + ULONG i; + + show = TRUE; + + if (Support->FilterList) + { + for (i = 0; i < Support->FilterList->Count; i++) + { + PPH_TN_FILTER_ENTRY entry; + + entry = Support->FilterList->Items[i]; + + if (!entry->Filter(Node, entry->Context)) + { + show = FALSE; + break; + } + } + } + + return show; +} + +VOID PhApplyTreeNewFilters( + _In_ PPH_TN_FILTER_SUPPORT Support + ) +{ + ULONG i; + + for (i = 0; i < Support->NodeList->Count; i++) + { + PPH_TREENEW_NODE node; + + node = Support->NodeList->Items[i]; + node->Visible = PhApplyTreeNewFiltersToNode(Support, node); + + if (!node->Visible && node->Selected) + { + node->Selected = FALSE; + } + } + + TreeNew_NodesStructured(Support->TreeNewHandle); +} + +#define ID_COPY_CELL 136 +#define ID_SYMBOL_COPY 40201 + +typedef struct _PH_COPY_CELL_CONTEXT +{ + HWND TreeNewHandle; + ULONG Id; // column ID + PPH_STRING MenuItemText; +} PH_COPY_CELL_CONTEXT, *PPH_COPY_CELL_CONTEXT; + +VOID NTAPI PhpCopyCellEMenuItemDeleteFunction( + _In_ struct _PH_EMENU_ITEM *Item + ) +{ + PPH_COPY_CELL_CONTEXT context; + + context = Item->Context; + PhDereferenceObject(context->MenuItemText); + PhFree(context); +} + +BOOLEAN PhInsertCopyCellEMenuItem( + _In_ struct _PH_EMENU_ITEM *Menu, + _In_ ULONG InsertAfterId, + _In_ HWND TreeNewHandle, + _In_ PPH_TREENEW_COLUMN Column + ) +{ + PPH_EMENU_ITEM parentItem; + ULONG indexInParent; + PPH_COPY_CELL_CONTEXT context; + PH_STRINGREF columnText; + PPH_STRING escapedText; + PPH_STRING menuItemText; + PPH_EMENU_ITEM copyCellItem; + + if (!Column) + return FALSE; + + if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent)) + return FALSE; + + indexInParent++; + + context = PhAllocate(sizeof(PH_COPY_CELL_CONTEXT)); + context->TreeNewHandle = TreeNewHandle; + context->Id = Column->Id; + + PhInitializeStringRef(&columnText, Column->Text); + escapedText = PhEscapeStringForMenuPrefix(&columnText); + menuItemText = PhFormatString(L"Copy \"%s\"", escapedText->Buffer); + PhDereferenceObject(escapedText); + copyCellItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context); + copyCellItem->DeleteFunction = PhpCopyCellEMenuItemDeleteFunction; + context->MenuItemText = menuItemText; + + if (Column->CustomDraw) + copyCellItem->Flags |= PH_EMENU_DISABLED; + + PhInsertEMenuItem(parentItem, copyCellItem, indexInParent); + + return TRUE; +} + +BOOLEAN PhHandleCopyCellEMenuItem( + _In_ struct _PH_EMENU_ITEM *SelectedItem + ) +{ + PPH_COPY_CELL_CONTEXT context; + PH_STRING_BUILDER stringBuilder; + ULONG count; + ULONG selectedCount; + ULONG i; + PPH_TREENEW_NODE node; + PH_TREENEW_GET_CELL_TEXT getCellText; + + if (!SelectedItem) + return FALSE; + if (SelectedItem->Id != ID_COPY_CELL) + return FALSE; + + context = SelectedItem->Context; + + PhInitializeStringBuilder(&stringBuilder, 0x100); + count = TreeNew_GetFlatNodeCount(context->TreeNewHandle); + selectedCount = 0; + + for (i = 0; i < count; i++) + { + node = TreeNew_GetFlatNode(context->TreeNewHandle, i); + + if (node && node->Selected) + { + selectedCount++; + + getCellText.Flags = 0; + getCellText.Node = node; + getCellText.Id = context->Id; + PhInitializeEmptyStringRef(&getCellText.Text); + TreeNew_GetCellText(context->TreeNewHandle, &getCellText); + + PhAppendStringBuilder(&stringBuilder, &getCellText.Text); + PhAppendStringBuilder2(&stringBuilder, L"\r\n"); + } + } + + if (stringBuilder.String->Length != 0 && selectedCount == 1) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhSetClipboardString(context->TreeNewHandle, &stringBuilder.String->sr); + PhDeleteStringBuilder(&stringBuilder); + + return TRUE; +} + +#pragma endregion + +BOOLEAN SymbolNodeHashtableCompareFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ); +ULONG SymbolNodeHashtableHashFunction( + _In_ PVOID Entry + ); +VOID PvDestroySymbolNode( + _In_ PPV_SYMBOL_NODE Node + ); + +VOID PvDeleteSymbolTree( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + PPH_STRING settings = PhCmSaveSettings(Context->TreeNewHandle); + PhSetStringSetting2(L"PdbTreeListColumns", &settings->sr); + PhDereferenceObject(settings); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PvDestroySymbolNode(Context->NodeList->Items[i]); + } + + PhDereferenceObject(Context->NodeHashtable); + PhDereferenceObject(Context->NodeList); +} + +struct _PH_TN_FILTER_SUPPORT* GetSymbolListFilterSupport( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + return &Context->FilterSupport; +} + +BOOLEAN SymbolNodeHashtableCompareFunction( + _In_ PVOID Entry1, + _In_ PVOID Entry2 + ) +{ + PPV_SYMBOL_NODE windowNode1 = *(PPV_SYMBOL_NODE *)Entry1; + PPV_SYMBOL_NODE windowNode2 = *(PPV_SYMBOL_NODE *)Entry2; + + return PhEqualString(windowNode1->Name, windowNode2->Name, TRUE); +} + +ULONG SymbolNodeHashtableHashFunction( + _In_ PVOID Entry + ) +{ + return PhHashStringRef(&(*(PPV_SYMBOL_NODE*)Entry)->Name->sr, TRUE); +} + +VOID PvSymbolAddTreeNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPV_SYMBOL_NODE Entry + ) +{ + PhInitializeTreeNewNode(&Entry->Node); + + memset(Entry->TextCache, 0, sizeof(PH_STRINGREF) * TREE_COLUMN_ITEM_MAXIMUM); + Entry->Node.TextCache = Entry->TextCache; + Entry->Node.TextCacheSize = TREE_COLUMN_ITEM_MAXIMUM; + + if (PhAddEntryHashtable(Context->NodeHashtable, &Entry)) // HACK + { + PhAddItemList(Context->NodeList, Entry); + + if (Context->FilterSupport.NodeList) + { + Entry->Node.Visible = PhApplyTreeNewFiltersToNode(&Context->FilterSupport, &Entry->Node); + } + } +} + +PPV_SYMBOL_NODE PvFindSymbolNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPH_STRING Name + ) +{ + PV_SYMBOL_NODE lookupSymbolNode; + PPV_SYMBOL_NODE lookupSymbolNodePtr = &lookupSymbolNode; + PPV_SYMBOL_NODE *threadNode; + + lookupSymbolNode.Name = Name; + + threadNode = (PPV_SYMBOL_NODE *)PhFindEntryHashtable( + Context->NodeHashtable, + &lookupSymbolNodePtr + ); + + if (threadNode) + return *threadNode; + else + return NULL; +} + +VOID PvRemoveSymbolNode( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPV_SYMBOL_NODE Node + ) +{ + ULONG index = 0; + + PhRemoveEntryHashtable(Context->NodeHashtable, &Node); + + if ((index = PhFindItemList(Context->NodeList, Node)) != ULONG_MAX) + PhRemoveItemList(Context->NodeList, index); + + PvDestroySymbolNode(Node); +} + +VOID PvDestroySymbolNode( + _In_ PPV_SYMBOL_NODE Node + ) +{ + PhFree(Node); +} + +#define SORT_FUNCTION(Column) PmPoolTreeNewCompare##Column +#define BEGIN_SORT_FUNCTION(Column) static int __cdecl PmPoolTreeNewCompare##Column( \ + _In_ void *_context, \ + _In_ const void *_elem1, \ + _In_ const void *_elem2 \ + ) \ +{ \ + PPV_SYMBOL_NODE node1 = *(PPV_SYMBOL_NODE *)_elem1; \ + PPV_SYMBOL_NODE node2 = *(PPV_SYMBOL_NODE *)_elem2; \ + int sortResult = 0; + +#define END_SORT_FUNCTION \ + /*if (sortResult == 0) \ + // sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); \ + */\ + return PhModifySort(sortResult, ((PPDB_SYMBOL_CONTEXT)_context)->TreeNewSortOrder); \ +} + +BEGIN_SORT_FUNCTION(Type) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->Type, (ULONG_PTR)node2->Type); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(VA) +{ + sortResult = uintptrcmp((ULONG_PTR)node1->Address, (ULONG_PTR)node2->Address); + + if (sortResult == 0) + sortResult = uintptrcmp((ULONG_PTR)node1->Node.Index, (ULONG_PTR)node2->Node.Index); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Symbol) +{ + sortResult = PhCompareStringWithNull(node1->Name, node2->Name, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Data) +{ + sortResult = PhCompareStringWithNull(node1->Data, node2->Data, FALSE); +} +END_SORT_FUNCTION + +BEGIN_SORT_FUNCTION(Size) +{ + sortResult = uintcmp(node1->Size, node2->Size); +} +END_SORT_FUNCTION + +BOOLEAN NTAPI PvSymbolTreeNewCallback( + _In_ HWND hwnd, + _In_ PH_TREENEW_MESSAGE Message, + _In_opt_ PVOID Parameter1, + _In_opt_ PVOID Parameter2, + _In_opt_ PVOID Context + ) +{ + PPDB_SYMBOL_CONTEXT context; + PPV_SYMBOL_NODE node; + + context = Context; + + switch (Message) + { + case TreeNewGetChildren: + { + PPH_TREENEW_GET_CHILDREN getChildren = Parameter1; + node = (PPV_SYMBOL_NODE)getChildren->Node; + + if (!getChildren->Node) + { + static PVOID sortFunctions[] = + { + SORT_FUNCTION(Type), + SORT_FUNCTION(VA), + SORT_FUNCTION(Symbol), + SORT_FUNCTION(Data), + SORT_FUNCTION(Size) + }; + int (__cdecl *sortFunction)(void *, const void *, const void *); + + if (context->TreeNewSortColumn < TREE_COLUMN_ITEM_MAXIMUM) + sortFunction = sortFunctions[context->TreeNewSortColumn]; + else + sortFunction = NULL; + + if (sortFunction) + { + qsort_s(context->NodeList->Items, context->NodeList->Count, sizeof(PVOID), sortFunction, context); + } + + getChildren->Children = (PPH_TREENEW_NODE *)context->NodeList->Items; + getChildren->NumberOfChildren = context->NodeList->Count; + } + } + return TRUE; + case TreeNewIsLeaf: + { + PPH_TREENEW_IS_LEAF isLeaf = (PPH_TREENEW_IS_LEAF)Parameter1; + node = (PPV_SYMBOL_NODE)isLeaf->Node; + + isLeaf->IsLeaf = TRUE; + } + return TRUE; + case TreeNewGetCellText: + { + PPH_TREENEW_GET_CELL_TEXT getCellText = (PPH_TREENEW_GET_CELL_TEXT)Parameter1; + node = (PPV_SYMBOL_NODE)getCellText->Node; + + switch (getCellText->Id) + { + case TREE_COLUMN_ITEM_TYPE: + { + switch (node->Type) + { + case PV_SYMBOL_TYPE_FUNCTION: + PhInitializeStringRef(&getCellText->Text, L"FUNCTION"); + break; + case PV_SYMBOL_TYPE_SYMBOL: + PhInitializeStringRef(&getCellText->Text, L"SYMBOL"); + break; + case PV_SYMBOL_TYPE_LOCAL_VAR: + PhInitializeStringRef(&getCellText->Text, L"LOCAL_VAR"); + break; + case PV_SYMBOL_TYPE_STATIC_LOCAL_VAR: + PhInitializeStringRef(&getCellText->Text, L"STATIC_LOCAL_VAR"); + break; + case PV_SYMBOL_TYPE_PARAMETER: + PhInitializeStringRef(&getCellText->Text, L"PARAMETER"); + break; + case PV_SYMBOL_TYPE_OBJECT_PTR: + PhInitializeStringRef(&getCellText->Text, L"OBJECT_PTR"); + break; + case PV_SYMBOL_TYPE_STATIC_VAR: + PhInitializeStringRef(&getCellText->Text, L"STATIC_VAR"); + break; + case PV_SYMBOL_TYPE_GLOBAL_VAR: + PhInitializeStringRef(&getCellText->Text, L"GLOBAL_VAR"); + break; + case PV_SYMBOL_TYPE_STRUCT: + PhInitializeStringRef(&getCellText->Text, L"STRUCT"); + break; + case PV_SYMBOL_TYPE_STATIC_MEMBER: + PhInitializeStringRef(&getCellText->Text, L"STATIC_MEMBER"); + break; + case PV_SYMBOL_TYPE_CONSTANT: + PhInitializeStringRef(&getCellText->Text, L"CONSTANT"); + break; + case PV_SYMBOL_TYPE_CLASS: + PhInitializeStringRef(&getCellText->Text, L"CLASS"); + break; + case PV_SYMBOL_TYPE_MEMBER: + PhInitializeStringRef(&getCellText->Text, L"MEMBER"); + break; + } + } + break; + case TREE_COLUMN_ITEM_VA: + PhInitializeStringRefLongHint(&getCellText->Text, node->Pointer); + break; + case TREE_COLUMN_ITEM_NAME: + getCellText->Text = PhGetStringRef(node->Name); + break; + case TREE_COLUMN_ITEM_SYMBOL: + getCellText->Text = PhGetStringRef(node->Data); + break; + case TREE_COLUMN_ITEM_SIZE: + { + if (node->Size != 0) + { + PH_FORMAT format[1]; + + PhInitFormatSize(&format[0], node->Size); + + PhMoveReference(&node->SizeText, PhFormat(format, 1, 0)); + getCellText->Text = node->SizeText->sr; + } + } + break; + default: + return FALSE; + } + + getCellText->Flags = TN_CACHE; + } + return TRUE; + case TreeNewGetNodeColor: + { + PPH_TREENEW_GET_NODE_COLOR getNodeColor = (PPH_TREENEW_GET_NODE_COLOR)Parameter1; + node = (PPV_SYMBOL_NODE)getNodeColor->Node; + + getNodeColor->Flags = TN_CACHE | TN_AUTO_FORECOLOR; + } + return TRUE; + case TreeNewSortChanged: + { + TreeNew_GetSort(hwnd, &context->TreeNewSortColumn, &context->TreeNewSortOrder); + TreeNew_NodesStructured(hwnd); + } + return TRUE; + case TreeNewKeyDown: + case TreeNewNodeExpanding: + return TRUE; + case TreeNewLeftDoubleClick: + { + // SendMessage(context->ParentWindowHandle, WM_COMMAND, WM_ACTION, (LPARAM)context); + } + return TRUE; + case TreeNewContextMenu: + { + PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1; + + SendMessage(context->ParentWindowHandle, WM_PV_SEARCH_SHOWMENU, 0, (LPARAM)contextMenu); + } + return TRUE; + case TreeNewHeaderRightClick: + { + PH_TN_COLUMN_MENU_DATA data; + + data.TreeNewHandle = hwnd; + data.MouseEvent = Parameter1; + data.DefaultSortColumn = 0; + data.DefaultSortOrder = AscendingSortOrder; + PhInitializeTreeNewColumnMenu(&data); + + data.Selection = PhShowEMenu(data.Menu, hwnd, PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, data.MouseEvent->ScreenLocation.x, data.MouseEvent->ScreenLocation.y); + PhHandleTreeNewColumnMenu(&data); + PhDeleteTreeNewColumnMenu(&data); + } + return TRUE; + } + + return FALSE; +} + +VOID PvSymbolClearTree( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + PvDestroySymbolNode(Context->NodeList->Items[i]); + + PhClearHashtable(Context->NodeHashtable); + PhClearList(Context->NodeList); +} + +PPV_SYMBOL_NODE PvGetSelectedSymbolNode( + _In_ PPDB_SYMBOL_CONTEXT Context + ) +{ + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PPV_SYMBOL_NODE windowNode = Context->NodeList->Items[i]; + + if (windowNode->Node.Selected) + return windowNode; + } + + return NULL; +} + +VOID PvGetSelectedSymbolNodes( + _In_ PPDB_SYMBOL_CONTEXT Context, + _Out_ PPV_SYMBOL_NODE **Windows, + _Out_ PULONG NumberOfWindows + ) +{ + PPH_LIST list = PhCreateList(2); + + for (ULONG i = 0; i < Context->NodeList->Count; i++) + { + PPV_SYMBOL_NODE node = (PPV_SYMBOL_NODE)Context->NodeList->Items[i]; + + if (node->Node.Selected) + PhAddItemList(list, node); + } + + *Windows = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count); + *NumberOfWindows = list->Count; + + PhDereferenceObject(list); +} + +VOID PvInitializeSymbolTree( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ HWND ParentWindowHandle, + _In_ HWND TreeNewHandle + ) +{ + Context->NodeHashtable = PhCreateHashtable( + sizeof(PPV_SYMBOL_NODE), + SymbolNodeHashtableCompareFunction, + SymbolNodeHashtableHashFunction, + 100 + ); + Context->NodeList = PhCreateList(100); + + Context->ParentWindowHandle = ParentWindowHandle; + Context->TreeNewHandle = TreeNewHandle; + PhSetControlTheme(TreeNewHandle, L"explorer"); + + TreeNew_SetCallback(TreeNewHandle, PvSymbolTreeNewCallback, Context); + + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_TYPE, TRUE, L"Type", 80, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_TYPE, 0, 0); + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_VA, TRUE, L"VA", 80, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_VA, 0, 0); + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_NAME, TRUE, L"Symbol", 150, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_NAME, 0, 0); + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_SYMBOL, TRUE, L"Data", 150, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_SYMBOL, 0, 0); + PhAddTreeNewColumnEx2(TreeNewHandle, TREE_COLUMN_ITEM_SIZE, TRUE, L"Size", 40, PH_ALIGN_LEFT, TREE_COLUMN_ITEM_SIZE, 0, 0); + + TreeNew_SetSort(TreeNewHandle, TREE_COLUMN_ITEM_VA, AscendingSortOrder); + + PPH_STRING settings = PhGetStringSetting(L"PdbTreeListColumns"); + PhCmLoadSettings(TreeNewHandle, &settings->sr); + PhDereferenceObject(settings); + + PhInitializeTreeNewFilterSupport(&Context->FilterSupport, TreeNewHandle, Context->NodeList); +} + +BOOLEAN WordMatchStringRef( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PPH_STRINGREF Text + ) +{ + PH_STRINGREF part; + PH_STRINGREF remainingPart; + + remainingPart = Context->SearchboxText->sr; + + while (remainingPart.Length) + { + PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart); + + if (part.Length) + { + if (PhFindStringInStringRef(Text, &part, TRUE) != -1) + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN WordMatchStringZ( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ PWSTR Text + ) +{ + PH_STRINGREF text; + + PhInitializeStringRef(&text, Text); + return WordMatchStringRef(Context, &text); +} + +BOOLEAN PvSymbolTreeFilterCallback( + _In_ PPH_TREENEW_NODE Node, + _In_opt_ PVOID Context + ) +{ + PPDB_SYMBOL_CONTEXT context = Context; + PPV_SYMBOL_NODE node = (PPV_SYMBOL_NODE)Node; + + //if (node->Address == 0) + // return TRUE; + + if (PhIsNullOrEmptyString(context->SearchboxText)) + return TRUE; + + switch (node->Type) + { + case PV_SYMBOL_TYPE_FUNCTION: + if (WordMatchStringZ(context, L"FUNCTION")) + return TRUE; + break; + case PV_SYMBOL_TYPE_SYMBOL: + if (WordMatchStringZ(context, L"SYMBOL")) + return TRUE; + break; + case PV_SYMBOL_TYPE_LOCAL_VAR: + if (WordMatchStringZ(context, L"LOCAL_VAR")) + return TRUE; + break; + case PV_SYMBOL_TYPE_STATIC_LOCAL_VAR: + if (WordMatchStringZ(context, L"STATIC_LOCAL_VAR")) + return TRUE; + break; + case PV_SYMBOL_TYPE_PARAMETER: + if (WordMatchStringZ(context, L"PARAMETER")) + return TRUE; + break; + case PV_SYMBOL_TYPE_OBJECT_PTR: + if (WordMatchStringZ(context, L"OBJECT_PTR")) + return TRUE; + break; + case PV_SYMBOL_TYPE_STATIC_VAR: + if (WordMatchStringZ(context, L"STATIC_VAR")) + return TRUE; + break; + case PV_SYMBOL_TYPE_GLOBAL_VAR: + if (WordMatchStringZ(context, L"GLOBAL_VAR")) + return TRUE; + break; + case PV_SYMBOL_TYPE_STRUCT: + if (WordMatchStringZ(context, L"MEMBER")) + return TRUE; + break; + case PV_SYMBOL_TYPE_STATIC_MEMBER: + if (WordMatchStringZ(context, L"STATIC_MEMBER")) + return TRUE; + break; + case PV_SYMBOL_TYPE_CONSTANT: + if (WordMatchStringZ(context, L"CONSTANT")) + return TRUE; + break; + } + + if (!PhIsNullOrEmptyString(node->Name)) + { + if (WordMatchStringRef(context, &node->Name->sr)) + return TRUE; + } + + if (!PhIsNullOrEmptyString(node->Data)) + { + if (WordMatchStringRef(context, &node->Data->sr)) + return TRUE; + } + + if (node->Pointer[0]) + { + if (WordMatchStringZ(context, node->Pointer)) + return TRUE; + } + + return FALSE; +} + +VOID CALLBACK PvSymbolTreeUpdateCallback( + _In_ PPDB_SYMBOL_CONTEXT Context, + _In_ BOOLEAN TimerOrWaitFired + ) +{ + ULONG i; + + if (!Context->UpdateTimerHandle) + return; + + TreeNew_SetRedraw(Context->TreeNewHandle, FALSE); + + PhAcquireQueuedLockExclusive(&SearchResultsLock); + + for (i = SearchResultsAddIndex; i < SearchResults->Count; i++) + { + PvSymbolAddTreeNode(Context, SearchResults->Items[i]); + } + SearchResultsAddIndex = i; + + PhReleaseQueuedLockExclusive(&SearchResultsLock); + + TreeNew_NodesStructured(Context->TreeNewHandle); + TreeNew_SetRedraw(Context->TreeNewHandle, TRUE); + + RtlUpdateTimer(PhGetGlobalTimerQueue(), Context->UpdateTimerHandle, 1000, INFINITE); +} + +INT_PTR CALLBACK PvpSymbolsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + PPDB_SYMBOL_CONTEXT context; + + if (PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + { + context = (PPDB_SYMBOL_CONTEXT)propPageContext->Context; + } + else + { + return FALSE; + } + + switch (uMsg) + { + case WM_INITDIALOG: + { + HANDLE treeNewTimer = NULL; + + context = propPageContext->Context = PhAllocate(sizeof(PDB_SYMBOL_CONTEXT)); + memset(context, 0, sizeof(PDB_SYMBOL_CONTEXT)); + + context->DialogHandle = hwndDlg; + context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_SYMBOLTREE); + context->SearchHandle = GetDlgItem(hwndDlg, IDC_SYMSEARCH); + context->SearchboxText = PhReferenceEmptyString(); + + PvCreateSearchControl(context->SearchHandle, L"Search Symbols (Ctrl+K)"); + + PvInitializeSymbolTree(context, hwndDlg, context->TreeNewHandle); + PhAddTreeNewFilter(GetSymbolListFilterSupport(context), PvSymbolTreeFilterCallback, context); + + SearchResults = PhCreateList(0x1000); + context->UdtList = PhCreateList(0x100); + + PhCreateThread2(PeDumpFileSymbols, context); + + RtlCreateTimer( + PhGetGlobalTimerQueue(), + &context->UpdateTimerHandle, + PvSymbolTreeUpdateCallback, + context, + 0, + 1000, + 0 + ); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + if (context->UpdateTimerHandle) + { + RtlDeleteTimer(PhGetGlobalTimerQueue(), context->UpdateTimerHandle, NULL); + context->UpdateTimerHandle = NULL; + } + + PvDeleteSymbolTree(context); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, + PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, context->SearchHandle, + dialogItem, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, context->TreeNewHandle, dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_CMD(wParam, lParam)) + { + case EN_CHANGE: + { + PPH_STRING newSearchboxText; + + newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchHandle)); + + if (!PhEqualString(context->SearchboxText, newSearchboxText, FALSE)) + { + PhSwapReference(&context->SearchboxText, newSearchboxText); + + if (!PhIsNullOrEmptyString(context->SearchboxText)) + { + //PhExpandAllProcessNodes(TRUE); + //PhDeselectAllProcessNodes(); + } + + PhApplyTreeNewFilters(GetSymbolListFilterSupport(context)); + } + } + break; + } + } + break; + case WM_PV_SEARCH_FINISHED: + { + // Add any un-added items. + //SendMessage(hwndDlg, WM_PV_SEARCH_UPDATE, 0, 0); + + //NtWaitForSingleObject(context->SearchThreadHandle, FALSE, NULL); + //SearchStop = FALSE; + + //if (context->UpdateTimerHandle) + //{ + // RtlDeleteTimer(PhGetGlobalTimerQueue(), context->UpdateTimerHandle, NULL); + // context->UpdateTimerHandle = NULL; + //} + } + break; + case WM_PV_SEARCH_SHOWMENU: + { + PPH_TREENEW_CONTEXT_MENU contextMenuEvent = (PPH_TREENEW_CONTEXT_MENU)lParam; + PPH_EMENU menu; + PPH_EMENU_ITEM selectedItem; + PPV_SYMBOL_NODE *symbolNodes = NULL; + ULONG numberOfSymbolNodes = 0; + + PvGetSelectedSymbolNodes(context, &symbolNodes, &numberOfSymbolNodes); + + if (numberOfSymbolNodes != 0) + { + menu = PhCreateEMenu(); + PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_SYMBOL_COPY, L"Copy", NULL, NULL), ULONG_MAX); + PhInsertCopyCellEMenuItem(menu, ID_SYMBOL_COPY, context->TreeNewHandle, contextMenuEvent->Column); + + selectedItem = PhShowEMenu( + menu, + hwndDlg, + PH_EMENU_SHOW_SEND_COMMAND | PH_EMENU_SHOW_LEFTRIGHT, + PH_ALIGN_LEFT | PH_ALIGN_TOP, + contextMenuEvent->Location.x, + contextMenuEvent->Location.y + ); + + if (selectedItem && selectedItem->Id != ULONG_MAX) + { + BOOLEAN handled = FALSE; + + handled = PhHandleCopyCellEMenuItem(selectedItem); + + if (!handled && selectedItem->Id == ID_SYMBOL_COPY) + { + PPH_STRING text; + + text = PhGetTreeNewText(context->TreeNewHandle, 0); + PhSetClipboardString(context->TreeNewHandle, &text->sr); + PhDereferenceObject(text); + } + } + + PhDestroyEMenu(menu); + } + } + break; + } + + return FALSE; +} + +VOID PvPdbProperties( + VOID + ) +{ + PPV_PROPCONTEXT propContext; + + if (!RtlDoesFileExists_U(PvFileName->Buffer)) + { + PhShowStatus(NULL, L"Unable to load the pdb file", STATUS_FILE_NOT_AVAILABLE, 0); + return; + } + + if (propContext = PvCreatePropContext(PvFileName)) + { + PPV_PROPPAGECONTEXT newPage; + + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PESYMBOLS), + PvpSymbolsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + PhModalPropertySheet(&propContext->PropSheetHeader); + + PhDereferenceObject(propContext); + } +} diff --git a/tools/peview/peprp.c b/tools/peview/peprp.c index 7228b6a4ed84..d4dc6c1b2f3a 100644 --- a/tools/peview/peprp.c +++ b/tools/peview/peprp.c @@ -1,1406 +1,985 @@ -/* - * Process Hacker - - * PE viewer - * - * Copyright (C) 2010-2011 wj32 - * Copyright (C) 2017 dmex - * - * This file is part of Process Hacker. - * - * Process Hacker is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Process Hacker is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Process Hacker. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PVM_CHECKSUM_DONE (WM_APP + 1) -#define PVM_VERIFY_DONE (WM_APP + 2) - -INT_PTR CALLBACK PvpPeGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PvpPeImportsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PvpPeExportsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PvpPeLoadConfigDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PvpPeClrDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -INT_PTR CALLBACK PvpPeCgfDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ); - -PH_MAPPED_IMAGE PvMappedImage; -PIMAGE_COR20_HEADER PvImageCor20Header; - -HICON PvImageLargeIcon; -PH_IMAGE_VERSION_INFO PvImageVersionInfo; -VERIFY_RESULT PvImageVerifyResult; -PPH_STRING PvImageSignerName; - -VOID PvPeProperties( - VOID - ) -{ - NTSTATUS status; - PPV_PROPCONTEXT propContext; - PH_MAPPED_IMAGE_IMPORTS imports; - PH_MAPPED_IMAGE_EXPORTS exports; - PIMAGE_DATA_DIRECTORY entry; - - if (!NT_SUCCESS(status = PhLoadMappedImage( - PvFileName->Buffer, - NULL, - TRUE, - &PvMappedImage - ))) - { - PhShowStatus(NULL, L"Unable to load the PE file", status, 0); - return; - } - - if (propContext = PvCreatePropContext(PvFileName)) - { - PPV_PROPPAGECONTEXT newPage; - - // General page - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PEGENERAL), - PvpPeGeneralDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - - // Imports page - if ((NT_SUCCESS(PhGetMappedImageImports(&imports, &PvMappedImage)) && imports.NumberOfDlls != 0) || - (NT_SUCCESS(PhGetMappedImageDelayImports(&imports, &PvMappedImage)) && imports.NumberOfDlls != 0)) - { - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PEIMPORTS), - PvpPeImportsDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - } - - // Exports page - if (NT_SUCCESS(PhGetMappedImageExports(&exports, &PvMappedImage)) && exports.NumberOfEntries != 0) - { - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PEEXPORTS), - PvpPeExportsDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - } - - // Load Config page - if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &entry)) && entry->VirtualAddress) - { - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PELOADCONFIG), - PvpPeLoadConfigDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - } - - // CLR page - if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &entry)) && - entry->VirtualAddress && - (PvImageCor20Header = PhMappedImageRvaToVa(&PvMappedImage, entry->VirtualAddress, NULL))) - { - status = STATUS_SUCCESS; - - __try - { - PhProbeAddress( - PvImageCor20Header, - sizeof(IMAGE_COR20_HEADER), - PvMappedImage.ViewBase, - PvMappedImage.Size, - 4 - ); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - status = GetExceptionCode(); - } - - if (NT_SUCCESS(status)) - { - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PECLR), - PvpPeClrDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - } - } - - // CFG page - if ((PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && - (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF)) - { - newPage = PvCreatePropPageContext( - MAKEINTRESOURCE(IDD_PECFG), - PvpPeCgfDlgProc, - NULL - ); - PvAddPropPage(propContext, newPage); - } - - PhModalPropertySheet(&propContext->PropSheetHeader); - - PhDereferenceObject(propContext); - } - - PhUnloadMappedImage(&PvMappedImage); -} - -static NTSTATUS CheckSumImageThreadStart( - _In_ PVOID Parameter - ) -{ - HWND windowHandle; - ULONG checkSum; - - windowHandle = Parameter; - checkSum = PhCheckSumMappedImage(&PvMappedImage); - - PostMessage( - windowHandle, - PVM_CHECKSUM_DONE, - checkSum, - 0 - ); - - return STATUS_SUCCESS; -} - -VERIFY_RESULT PvpVerifyFileWithAdditionalCatalog( - _In_ PPH_STRING FileName, - _In_ ULONG Flags, - _In_opt_ HWND hWnd, - _Out_opt_ PPH_STRING *SignerName - ) -{ - static PH_STRINGREF codeIntegrityFileName = PH_STRINGREF_INIT(L"\\AppxMetadata\\CodeIntegrity.cat"); - - VERIFY_RESULT result; - PH_VERIFY_FILE_INFO info; - PPH_STRING windowsAppsPath; - PPH_STRING additionalCatalogFileName = NULL; - PCERT_CONTEXT *signatures; - ULONG numberOfSignatures; - - memset(&info, 0, sizeof(PH_VERIFY_FILE_INFO)); - info.FileName = FileName->Buffer; - info.Flags = Flags; - info.hWnd = hWnd; - - windowsAppsPath = PhGetKnownLocation(CSIDL_PROGRAM_FILES, L"\\WindowsApps\\"); - - if (windowsAppsPath) - { - if (PhStartsWithStringRef(&FileName->sr, &windowsAppsPath->sr, TRUE)) - { - PH_STRINGREF remainingFileName; - ULONG_PTR indexOfBackslash; - PH_STRINGREF baseFileName; - - remainingFileName = FileName->sr; - PhSkipStringRef(&remainingFileName, windowsAppsPath->Length); - indexOfBackslash = PhFindCharInStringRef(&remainingFileName, '\\', FALSE); - - if (indexOfBackslash != -1) - { - baseFileName.Buffer = FileName->Buffer; - baseFileName.Length = windowsAppsPath->Length + indexOfBackslash * sizeof(WCHAR); - additionalCatalogFileName = PhConcatStringRef2(&baseFileName, &codeIntegrityFileName); - } - } - - PhDereferenceObject(windowsAppsPath); - } - - if (additionalCatalogFileName) - { - info.NumberOfCatalogFileNames = 1; - info.CatalogFileNames = &additionalCatalogFileName->Buffer; - } - - if (!NT_SUCCESS(PhVerifyFileEx(&info, &result, &signatures, &numberOfSignatures))) - { - result = VrNoSignature; - signatures = NULL; - numberOfSignatures = 0; - } - - if (additionalCatalogFileName) - PhDereferenceObject(additionalCatalogFileName); - - if (SignerName) - { - if (numberOfSignatures != 0) - *SignerName = PhGetSignerNameFromCertificate(signatures[0]); - else - *SignerName = NULL; - } - - PhFreeVerifySignatures(signatures, numberOfSignatures); - - return result; -} - -static NTSTATUS VerifyImageThreadStart( - _In_ PVOID Parameter - ) -{ - HWND windowHandle; - - windowHandle = Parameter; - PvImageVerifyResult = PvpVerifyFileWithAdditionalCatalog(PvFileName, PH_VERIFY_PREVENT_NETWORK_ACCESS, NULL, &PvImageSignerName); - PostMessage(windowHandle, PVM_VERIFY_DONE, 0, 0); - - return STATUS_SUCCESS; -} - -FORCEINLINE PWSTR PvpGetStringOrNa( - _In_ PPH_STRING String - ) -{ - if (String) - return String->Buffer; - else - return L"N/A"; -} - -INT_PTR CALLBACK PvpPeGeneralDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPV_PROPPAGECONTEXT propPageContext; - - if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - ULONG i; - PPH_STRING string; - PWSTR type; - PH_STRING_BUILDER stringBuilder; - - PhCenterWindow(GetParent(hwndDlg), NULL); - - // File version information - - { - PhInitializeImageVersionInfo(&PvImageVersionInfo, PvFileName->Buffer); - - if (ExtractIconEx( - PvFileName->Buffer, - 0, - &PvImageLargeIcon, - NULL, - 1 - ) == 0) - { - PvImageLargeIcon = PhGetFileShellIcon(PvFileName->Buffer, NULL, TRUE); - } - - SendMessage(GetDlgItem(hwndDlg, IDC_FILEICON), STM_SETICON, (WPARAM)PvImageLargeIcon, 0); - - SetDlgItemText(hwndDlg, IDC_NAME, PvpGetStringOrNa(PvImageVersionInfo.FileDescription)); - string = PhConcatStrings2(L"(Verifying...) ", PvpGetStringOrNa(PvImageVersionInfo.CompanyName)); - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, string->Buffer); - PhDereferenceObject(string); - SetDlgItemText(hwndDlg, IDC_VERSION, PvpGetStringOrNa(PvImageVersionInfo.FileVersion)); - - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), VerifyImageThreadStart, hwndDlg); - } - - // PE properties - - switch (PvMappedImage.NtHeaders->FileHeader.Machine) - { - case IMAGE_FILE_MACHINE_I386: - type = L"i386"; - break; - case IMAGE_FILE_MACHINE_AMD64: - type = L"AMD64"; - break; - case IMAGE_FILE_MACHINE_IA64: - type = L"IA64"; - break; - case IMAGE_FILE_MACHINE_ARMNT: - type = L"ARM Thumb-2"; - break; - default: - type = L"Unknown"; - break; - } - - SetDlgItemText(hwndDlg, IDC_TARGETMACHINE, type); - - { - LARGE_INTEGER time; - SYSTEMTIME systemTime; - - RtlSecondsSince1970ToTime(PvMappedImage.NtHeaders->FileHeader.TimeDateStamp, &time); - PhLargeIntegerToLocalSystemTime(&systemTime, &time); - - string = PhFormatDateTime(&systemTime); - SetDlgItemText(hwndDlg, IDC_TIMESTAMP, string->Buffer); - PhDereferenceObject(string); - } - - if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - string = PhFormatString(L"0x%I32x", ((PIMAGE_OPTIONAL_HEADER32)&PvMappedImage.NtHeaders->OptionalHeader)->ImageBase); - } - else - { - string = PhFormatString(L"0x%I64x", ((PIMAGE_OPTIONAL_HEADER64)&PvMappedImage.NtHeaders->OptionalHeader)->ImageBase); - } - - SetDlgItemText(hwndDlg, IDC_IMAGEBASE, string->Buffer); - PhDereferenceObject(string); - - if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - string = PhFormatString(L"0x%I32x", ((PIMAGE_OPTIONAL_HEADER32)&PvMappedImage.NtHeaders->OptionalHeader)->AddressOfEntryPoint); - } - else - { - string = PhFormatString(L"0x%I64x", ((PIMAGE_OPTIONAL_HEADER64)&PvMappedImage.NtHeaders->OptionalHeader)->AddressOfEntryPoint); - } - - SetDlgItemText(hwndDlg, IDC_ENTRYPOINT, string->Buffer); - PhDereferenceObject(string); - - string = PhFormatString(L"0x%Ix (verifying...)", PvMappedImage.NtHeaders->OptionalHeader.CheckSum); // same for 32-bit and 64-bit images - SetDlgItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); - PhDereferenceObject(string); - - PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), CheckSumImageThreadStart, hwndDlg); - - switch (PvMappedImage.NtHeaders->OptionalHeader.Subsystem) - { - case IMAGE_SUBSYSTEM_NATIVE: - type = L"Native"; - break; - case IMAGE_SUBSYSTEM_WINDOWS_GUI: - type = L"Windows GUI"; - break; - case IMAGE_SUBSYSTEM_WINDOWS_CUI: - type = L"Windows CUI"; - break; - case IMAGE_SUBSYSTEM_OS2_CUI: - type = L"OS/2 CUI"; - break; - case IMAGE_SUBSYSTEM_POSIX_CUI: - type = L"POSIX CUI"; - break; - case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: - type = L"Windows CE CUI"; - break; - case IMAGE_SUBSYSTEM_EFI_APPLICATION: - type = L"EFI Application"; - break; - case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: - type = L"EFI Boot Service Driver"; - break; - case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: - type = L"EFI Runtime Driver"; - break; - case IMAGE_SUBSYSTEM_EFI_ROM: - type = L"EFI ROM"; - break; - case IMAGE_SUBSYSTEM_XBOX: - type = L"Xbox"; - break; - case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: - type = L"Windows Boot Application"; - break; - default: - type = L"Unknown"; - break; - } - - SetDlgItemText(hwndDlg, IDC_SUBSYSTEM, type); - SetDlgItemText(hwndDlg, IDC_SUBSYSTEMVERSION, PhaFormatString( - L"%u.%u", - PvMappedImage.NtHeaders->OptionalHeader.MajorSubsystemVersion, // same for 32-bit and 64-bit images - PvMappedImage.NtHeaders->OptionalHeader.MinorSubsystemVersion - )->Buffer); - - PhInitializeStringBuilder(&stringBuilder, 10); - - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) - PhAppendStringBuilder2(&stringBuilder, L"Executable, "); - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) - PhAppendStringBuilder2(&stringBuilder, L"DLL, "); - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) - PhAppendStringBuilder2(&stringBuilder, L"Large address aware, "); - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) - PhAppendStringBuilder2(&stringBuilder, L"Removable run from swap, "); - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_NET_RUN_FROM_SWAP) - PhAppendStringBuilder2(&stringBuilder, L"Net run from swap, "); - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_SYSTEM) - PhAppendStringBuilder2(&stringBuilder, L"System, "); - if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) - PhAppendStringBuilder2(&stringBuilder, L"Uni-processor only, "); - - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) - PhAppendStringBuilder2(&stringBuilder, L"High entropy VA, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) - PhAppendStringBuilder2(&stringBuilder, L"Dynamic base, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY) - PhAppendStringBuilder2(&stringBuilder, L"Force integrity check, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT) - PhAppendStringBuilder2(&stringBuilder, L"NX compatible, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION) - PhAppendStringBuilder2(&stringBuilder, L"No isolation, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) - PhAppendStringBuilder2(&stringBuilder, L"No SEH, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_BIND) - PhAppendStringBuilder2(&stringBuilder, L"Do not bind, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER) - PhAppendStringBuilder2(&stringBuilder, L"AppContainer, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER) - PhAppendStringBuilder2(&stringBuilder, L"WDM driver, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) - PhAppendStringBuilder2(&stringBuilder, L"Control Flow Guard, "); - if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) - PhAppendStringBuilder2(&stringBuilder, L"Terminal server aware, "); - - if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) - PhRemoveEndStringBuilder(&stringBuilder, 2); - - SetDlgItemText(hwndDlg, IDC_CHARACTERISTICS, stringBuilder.String->Buffer); - PhDeleteStringBuilder(&stringBuilder); - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 80, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"VA"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Size"); - - for (i = 0; i < PvMappedImage.NumberOfSections; i++) - { - INT lvItemIndex; - WCHAR sectionName[9]; - WCHAR pointer[PH_PTR_STR_LEN_1]; - - if (PhCopyStringZFromBytes(PvMappedImage.Sections[i].Name, - IMAGE_SIZEOF_SHORT_NAME, sectionName, 9, NULL)) - { - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, sectionName, NULL); - - PhPrintPointer(pointer, UlongToPtr(PvMappedImage.Sections[i].VirtualAddress)); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, pointer); - - PhPrintPointer(pointer, UlongToPtr(PvMappedImage.Sections[i].SizeOfRawData)); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); - } - } - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILE), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_CHARACTERISTICS), - dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - - PvDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case PVM_CHECKSUM_DONE: - { - PPH_STRING string; - ULONG headerCheckSum; - ULONG realCheckSum; - - headerCheckSum = PvMappedImage.NtHeaders->OptionalHeader.CheckSum; // same for 32-bit and 64-bit images - realCheckSum = (ULONG)wParam; - - if (headerCheckSum == 0) - { - // Some executables, like .NET ones, don't have a check sum. - string = PhFormatString(L"0x0 (real 0x%Ix)", realCheckSum); - SetDlgItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); - PhDereferenceObject(string); - } - else if (headerCheckSum == realCheckSum) - { - string = PhFormatString(L"0x%Ix (correct)", headerCheckSum); - SetDlgItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); - PhDereferenceObject(string); - } - else - { - string = PhFormatString(L"0x%Ix (incorrect, real 0x%Ix)", headerCheckSum, realCheckSum); - SetDlgItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); - PhDereferenceObject(string); - } - } - break; - case PVM_VERIFY_DONE: - { - PPH_STRING string; - - if (PvImageVerifyResult == VrTrusted) - { - if (PvImageSignerName) - { - string = PhFormatString(L"(Verified) %s", PvImageSignerName->Buffer); - SetDlgItemText(hwndDlg, IDC_COMPANYNAME_LINK, string->Buffer); - PhDereferenceObject(string); - ShowWindow(GetDlgItem(hwndDlg, IDC_COMPANYNAME), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_COMPANYNAME_LINK), SW_SHOW); - } - else - { - string = PhConcatStrings2(L"(Verified) ", PhGetStringOrEmpty(PvImageVersionInfo.CompanyName)); - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, string->Buffer); - PhDereferenceObject(string); - } - } - else if (PvImageVerifyResult != VrUnknown) - { - string = PhConcatStrings2(L"(UNVERIFIED) ", PhGetStringOrEmpty(PvImageVersionInfo.CompanyName)); - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, string->Buffer); - PhDereferenceObject(string); - } - else - { - SetDlgItemText(hwndDlg, IDC_COMPANYNAME, PvpGetStringOrNa(PvImageVersionInfo.CompanyName)); - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case NM_CLICK: - { - switch (header->idFrom) - { - case IDC_COMPANYNAME_LINK: - { - PvpVerifyFileWithAdditionalCatalog(PvFileName, PH_VERIFY_VIEW_PROPERTIES, hwndDlg, NULL); - } - break; - } - } - break; - } - - PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} - -VOID PvpProcessImports( - _In_ HWND ListViewHandle, - _In_ PPH_MAPPED_IMAGE_IMPORTS Imports, - _In_ BOOLEAN DelayImports - ) -{ - PH_MAPPED_IMAGE_IMPORT_DLL importDll; - PH_MAPPED_IMAGE_IMPORT_ENTRY importEntry; - ULONG i; - ULONG j; - - for (i = 0; i < Imports->NumberOfDlls; i++) - { - if (NT_SUCCESS(PhGetMappedImageImportDll(Imports, i, &importDll))) - { - for (j = 0; j < importDll.NumberOfEntries; j++) - { - if (NT_SUCCESS(PhGetMappedImageImportEntry(&importDll, j, &importEntry))) - { - INT lvItemIndex; - PPH_STRING name; - WCHAR number[PH_INT32_STR_LEN_1]; - - if (!DelayImports) - name = PhZeroExtendToUtf16(importDll.Name); - else - name = PhFormatString(L"%S (Delay)", importDll.Name); - - lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, name->Buffer, NULL); - PhDereferenceObject(name); - - if (importEntry.Name) - { - name = PhZeroExtendToUtf16(importEntry.Name); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); - PhDereferenceObject(name); - - PhPrintUInt32(number, importEntry.NameHint); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, number); - } - else - { - name = PhFormatString(L"(Ordinal %u)", importEntry.Ordinal); - PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, name->Buffer); - PhDereferenceObject(name); - } - } - } - } - } -} - -INT_PTR CALLBACK PvpPeImportsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPV_PROPPAGECONTEXT propPageContext; - - if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - ULONG fallbackColumns[] = { 0, 1, 2 }; - HWND lvHandle; - PH_MAPPED_IMAGE_IMPORTS imports; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 130, L"DLL"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 210, L"Name"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 50, L"Hint"); - PhSetExtendedListView(lvHandle); - ExtendedListView_AddFallbackColumns(lvHandle, 3, fallbackColumns); - - if (NT_SUCCESS(PhGetMappedImageImports(&imports, &PvMappedImage))) - { - PvpProcessImports(lvHandle, &imports, FALSE); - } - - if (NT_SUCCESS(PhGetMappedImageDelayImports(&imports, &PvMappedImage))) - { - PvpProcessImports(lvHandle, &imports, TRUE); - } - - ExtendedListView_SortItems(lvHandle); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - - PvDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_NOTIFY: - { - PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK PvpPeExportsDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPV_PROPPAGECONTEXT propPageContext; - - if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - PH_MAPPED_IMAGE_EXPORTS exports; - PH_MAPPED_IMAGE_EXPORT_ENTRY exportEntry; - PH_MAPPED_IMAGE_EXPORT_FUNCTION exportFunction; - ULONG i; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 220, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 50, L"Ordinal"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 120, L"VA"); - PhSetExtendedListView(lvHandle); - - if (NT_SUCCESS(PhGetMappedImageExports(&exports, &PvMappedImage))) - { - for (i = 0; i < exports.NumberOfEntries; i++) - { - if ( - NT_SUCCESS(PhGetMappedImageExportEntry(&exports, i, &exportEntry)) && - NT_SUCCESS(PhGetMappedImageExportFunction(&exports, NULL, exportEntry.Ordinal, &exportFunction)) - ) - { - INT lvItemIndex; - PPH_STRING name; - WCHAR number[PH_INT32_STR_LEN_1]; - WCHAR pointer[PH_PTR_STR_LEN_1]; - - if (exportEntry.Name) - { - name = PhZeroExtendToUtf16(exportEntry.Name); - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, name->Buffer, NULL); - PhDereferenceObject(name); - } - else - { - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, L"(unnamed)", NULL); - } - - PhPrintUInt32(number, exportEntry.Ordinal); - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, number); - - if (!exportFunction.ForwardedName) - { - if ((ULONG_PTR)exportFunction.Function >= (ULONG_PTR)PvMappedImage.ViewBase) - PhPrintPointer(pointer, PTR_SUB_OFFSET(exportFunction.Function, PvMappedImage.ViewBase)); - else - PhPrintPointer(pointer, exportFunction.Function); - - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, pointer); - } - else - { - name = PhZeroExtendToUtf16(exportFunction.ForwardedName); - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, name->Buffer); - PhDereferenceObject(name); - } - } - } - } - - ExtendedListView_SortItems(lvHandle); - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - - PvDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_NOTIFY: - { - PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK PvpPeLoadConfigDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPV_PROPPAGECONTEXT propPageContext; - - if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - PIMAGE_LOAD_CONFIG_DIRECTORY32 config32; - PIMAGE_LOAD_CONFIG_DIRECTORY64 config64; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 220, L"Name"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 170, L"Value"); - - #define ADD_VALUE(Name, Value) \ - { \ - INT lvItemIndex; \ - lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, Name, NULL); \ - PhSetListViewSubItem(lvHandle, lvItemIndex, 1, Value); \ - } - - #define ADD_VALUES(Type, Config) \ - { \ - LARGE_INTEGER time; \ - SYSTEMTIME systemTime; \ - \ - RtlSecondsSince1970ToTime((Config)->TimeDateStamp, &time); \ - PhLargeIntegerToLocalSystemTime(&systemTime, &time); \ - \ - ADD_VALUE(L"Time stamp", PhaFormatDateTime(&systemTime)->Buffer); \ - ADD_VALUE(L"Version", PhaFormatString(L"%u.%u", (Config)->MajorVersion, (Config)->MinorVersion)->Buffer); \ - ADD_VALUE(L"Global flags to clear", PhaFormatString(L"0x%x", (Config)->GlobalFlagsClear)->Buffer); \ - ADD_VALUE(L"Global flags to set", PhaFormatString(L"0x%x", (Config)->GlobalFlagsSet)->Buffer); \ - ADD_VALUE(L"Critical section default timeout", PhaFormatUInt64((Config)->CriticalSectionDefaultTimeout, TRUE)->Buffer); \ - ADD_VALUE(L"De-commit free block threshold", PhaFormatUInt64((Config)->DeCommitFreeBlockThreshold, TRUE)->Buffer); \ - ADD_VALUE(L"De-commit total free threshold", PhaFormatUInt64((Config)->DeCommitTotalFreeThreshold, TRUE)->Buffer); \ - ADD_VALUE(L"LOCK prefix table", PhaFormatString(L"0x%Ix", (Config)->LockPrefixTable)->Buffer); \ - ADD_VALUE(L"Maximum allocation size", PhaFormatString(L"0x%Ix", (Config)->MaximumAllocationSize)->Buffer); \ - ADD_VALUE(L"Virtual memory threshold", PhaFormatString(L"0x%Ix", (Config)->VirtualMemoryThreshold)->Buffer); \ - ADD_VALUE(L"Process affinity mask", PhaFormatString(L"0x%Ix", (Config)->ProcessAffinityMask)->Buffer); \ - ADD_VALUE(L"Process heap flags", PhaFormatString(L"0x%Ix", (Config)->ProcessHeapFlags)->Buffer); \ - ADD_VALUE(L"CSD version", PhaFormatString(L"%u", (Config)->CSDVersion)->Buffer); \ - ADD_VALUE(L"Edit list", PhaFormatString(L"0x%Ix", (Config)->EditList)->Buffer); \ - ADD_VALUE(L"Security cookie", PhaFormatString(L"0x%Ix", (Config)->SecurityCookie)->Buffer); \ - ADD_VALUE(L"SEH handler table", PhaFormatString(L"0x%Ix", (Config)->SEHandlerTable)->Buffer); \ - ADD_VALUE(L"SEH handler count", PhaFormatUInt64((Config)->SEHandlerCount, TRUE)->Buffer); \ - \ - if ((Config)->Size >= (ULONG)FIELD_OFFSET(Type, GuardAddressTakenIatEntryTable)) \ - { \ - ADD_VALUE(L"CFG GuardFlags", PhaFormatString(L"0x%Ix", (Config)->GuardFlags)->Buffer); \ - ADD_VALUE(L"CFG Check Function pointer", PhaFormatString(L"0x%Ix", (Config)->GuardCFCheckFunctionPointer)->Buffer); \ - ADD_VALUE(L"CFG Check Dispatch pointer", PhaFormatString(L"0x%Ix", (Config)->GuardCFDispatchFunctionPointer)->Buffer); \ - ADD_VALUE(L"CFG Function table", PhaFormatString(L"0x%Ix", (Config)->GuardCFFunctionTable)->Buffer); \ - ADD_VALUE(L"CFG Function table entry count", PhaFormatUInt64((Config)->GuardCFFunctionCount, TRUE)->Buffer); \ - if ((Config)->Size >= (ULONG)FIELD_OFFSET(Type, GuardAddressTakenIatEntryTable) \ - + sizeof((Config)->GuardAddressTakenIatEntryTable) \ - + sizeof((Config)->GuardAddressTakenIatEntryCount)) \ - { \ - ADD_VALUE(L"CFG IatEntry table", PhaFormatString(L"0x%Ix", (Config)->GuardAddressTakenIatEntryTable)->Buffer); \ - ADD_VALUE(L"CFG IatEntry table entry count", PhaFormatUInt64((Config)->GuardAddressTakenIatEntryCount, TRUE)->Buffer); \ - } \ - if ((Config)->Size >= (ULONG)FIELD_OFFSET(Type, GuardLongJumpTargetTable) \ - + sizeof((Config)->GuardLongJumpTargetTable) \ - + sizeof((Config)->GuardLongJumpTargetCount))\ - { \ - ADD_VALUE(L"CFG LongJump table", PhaFormatString(L"0x%Ix", (Config)->GuardLongJumpTargetTable)->Buffer); \ - ADD_VALUE(L"CFG LongJump table entry count", PhaFormatUInt64((Config)->GuardLongJumpTargetCount, TRUE)->Buffer); \ - } \ - } \ - } - - if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) - { - if (NT_SUCCESS(PhGetMappedImageLoadConfig32(&PvMappedImage, &config32))) - { - ADD_VALUES(IMAGE_LOAD_CONFIG_DIRECTORY32, config32); - } - } - else - { - if (NT_SUCCESS(PhGetMappedImageLoadConfig64(&PvMappedImage, &config64))) - { - ADD_VALUES(IMAGE_LOAD_CONFIG_DIRECTORY64, config64); - } - } - - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - - PvDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_NOTIFY: - { - PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} - -VOID PvpLoadDbgHelpFromPath( - _In_ PWSTR DbgHelpPath - ) -{ - HMODULE dbghelpModule; - - if (dbghelpModule = LoadLibrary(DbgHelpPath)) - { - PPH_STRING fullDbghelpPath; - ULONG indexOfFileName; - PH_STRINGREF dbghelpFolder; - PPH_STRING symsrvPath; - - fullDbghelpPath = PhGetDllFileName(dbghelpModule, &indexOfFileName); - - if (fullDbghelpPath) - { - if (indexOfFileName != 0) - { - static PH_STRINGREF symsrvString = PH_STRINGREF_INIT(L"\\symsrv.dll"); - - dbghelpFolder.Buffer = fullDbghelpPath->Buffer; - dbghelpFolder.Length = indexOfFileName * sizeof(WCHAR); - - symsrvPath = PhConcatStringRef2(&dbghelpFolder, &symsrvString); - - LoadLibrary(symsrvPath->Buffer); - - PhDereferenceObject(symsrvPath); - } - - PhDereferenceObject(fullDbghelpPath); - } - } - else - { - dbghelpModule = LoadLibrary(L"dbghelp.dll"); - } - - PhSymbolProviderCompleteInitialization(dbghelpModule); -} - -BOOLEAN PvpLoadDbgHelp( - _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider - ) -{ - static UNICODE_STRING symbolPathVarName = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH"); - PPH_STRING symbolSearchPath; - PPH_SYMBOL_PROVIDER symbolProvider; - WCHAR buffer[512] = L""; - UNICODE_STRING symbolPathUs = - { - .Buffer = buffer, - .MaximumLength = sizeof(buffer) - }; - - if (!PhSymbolProviderInitialization()) - return FALSE; - - PvpLoadDbgHelpFromPath(L"C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\dbghelp.dll"); - symbolProvider = PhCreateSymbolProvider(NULL); - - // Load symbol path from _NT_SYMBOL_PATH if configured by the user. - if (NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &symbolPathVarName, &symbolPathUs))) - { - symbolSearchPath = PhFormatString(L"SRV*%s*http://msdl.microsoft.com/download/symbols", symbolPathUs.Buffer); - } - else - { - symbolSearchPath = PhCreateString(L"SRV**http://msdl.microsoft.com/download/symbols"); - } - - PhSetSearchPathSymbolProvider(symbolProvider, symbolSearchPath->Buffer); - PhDereferenceObject(symbolSearchPath); - - *SymbolProvider = symbolProvider; - return TRUE; -} - -INT_PTR CALLBACK PvpPeCgfDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPV_PROPPAGECONTEXT propPageContext; - - if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND lvHandle; - PPH_SYMBOL_PROVIDER symbolProvider = NULL; - PH_MAPPED_IMAGE_CFG cfgConfig = { 0 }; - - lvHandle = GetDlgItem(hwndDlg, IDC_LIST); - PhSetListViewStyle(lvHandle, FALSE, TRUE); - PhSetControlTheme(lvHandle, L"explorer"); - - PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT , 40, L"#"); - PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_RIGHT, 100, L"RVA"); - PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT , 250, L"Name"); - PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT , 100, L"Flags"); - PhSetExtendedListView(lvHandle); - - // Init symbol resolver - if (PvpLoadDbgHelp(&symbolProvider)) - { - // Load current PE's pdb - PhLoadModuleSymbolProvider( - symbolProvider, - PvFileName->Buffer, - (ULONG64)PvMappedImage.NtHeaders->OptionalHeader.ImageBase, - PvMappedImage.NtHeaders->OptionalHeader.SizeOfImage - ); - } - - // Retrieve Cfg Table entry and characteristics - if (NT_SUCCESS(PhGetMappedImageCfg(&cfgConfig, &PvMappedImage))) - { - for (ULONGLONG i = 0; i < cfgConfig.NumberOfGuardFunctionEntries; i++) - { - INT lvItemIndex; - ULONG64 displacement; - PPH_STRING symbol; - PPH_STRING symbolName = NULL; - PH_SYMBOL_RESOLVE_LEVEL symbolResolveLevel = PhsrlInvalid; - IMAGE_CFG_ENTRY cfgFunctionEntry = { 0 }; - - // Parse cfg entry : if it fails, just skip it ? - if (!NT_SUCCESS(PhGetMappedImageCfgEntry(&cfgConfig, i, ControlFlowGuardFunction, &cfgFunctionEntry))) - continue; - - lvItemIndex = PhAddListViewItem( - lvHandle, - MAXINT, - PhaFormatString(L"%I64u", i)->Buffer, - NULL - ); - PhSetListViewSubItem( - lvHandle, - lvItemIndex, - 1, - PhaFormatString(L"0x%08x", cfgFunctionEntry.Rva)->Buffer - ); - - // Resolve name based on public symbols - if (!(symbol = PhGetSymbolFromAddress( - symbolProvider, - (ULONG64)PTR_ADD_OFFSET(PvMappedImage.NtHeaders->OptionalHeader.ImageBase, cfgFunctionEntry.Rva), - &symbolResolveLevel, - NULL, - &symbolName, - &displacement - ))) - { - continue; - } - - switch (symbolResolveLevel) - { - case PhsrlFunction: - { - if (displacement) - { - PhSetListViewSubItem( - lvHandle, - lvItemIndex, - 2, - PhaFormatString(L"%s+0x%x", symbolName->Buffer, displacement)->Buffer - ); - } - else - { - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, symbolName->Buffer); - } - } - break; - case PhsrlModule: - case PhsrlAddress: - { - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, symbol->Buffer); - } - break; - default: - case PhsrlInvalid: - { - PhSetListViewSubItem(lvHandle, lvItemIndex, 2, L"(unnamed)"); - } - break; - } - - // Add additional flags - if (cfgFunctionEntry.SuppressedCall) - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, L"SuppressedCall"); - else - PhSetListViewSubItem(lvHandle, lvItemIndex, 3, L""); - - if (symbolName) - PhDereferenceObject(symbolName); - PhDereferenceObject(symbol); - } - } - - ExtendedListView_SortItems(lvHandle); - EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PPH_LAYOUT_ITEM dialogItem; - - dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), - dialogItem, PH_ANCHOR_ALL); - - PvDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_NOTIFY: - { - PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK PvpPeClrDlgProc( - _In_ HWND hwndDlg, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam - ) -{ - LPPROPSHEETPAGE propSheetPage; - PPV_PROPPAGECONTEXT propPageContext; - - if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) - return FALSE; - - switch (uMsg) - { - case WM_INITDIALOG: - { - PH_STRING_BUILDER stringBuilder; - PPH_STRING string; - PVOID metaData; - ULONG versionStringLength; - - string = PhaFormatString(L"%u.%u", PvImageCor20Header->MajorRuntimeVersion, - PvImageCor20Header->MinorRuntimeVersion); - SetDlgItemText(hwndDlg, IDC_RUNTIMEVERSION, string->Buffer); - - PhInitializeStringBuilder(&stringBuilder, 256); - - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_ILONLY) - PhAppendStringBuilder2(&stringBuilder, L"IL only, "); - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_32BITREQUIRED) - PhAppendStringBuilder2(&stringBuilder, L"32-bit only, "); - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_32BITPREFERRED) - PhAppendStringBuilder2(&stringBuilder, L"32-bit preferred, "); - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_IL_LIBRARY) - PhAppendStringBuilder2(&stringBuilder, L"IL library, "); - - if (PvImageCor20Header->StrongNameSignature.VirtualAddress != 0 && PvImageCor20Header->StrongNameSignature.Size != 0) - { - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_STRONGNAMESIGNED) - PhAppendStringBuilder2(&stringBuilder, L"Strong-name signed, "); - else - PhAppendStringBuilder2(&stringBuilder, L"Strong-name delay signed, "); - } - - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) - PhAppendStringBuilder2(&stringBuilder, L"Native entry-point, "); - if (PvImageCor20Header->Flags & COMIMAGE_FLAGS_TRACKDEBUGDATA) - PhAppendStringBuilder2(&stringBuilder, L"Track debug data, "); - - if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) - PhRemoveEndStringBuilder(&stringBuilder, 2); - - SetDlgItemText(hwndDlg, IDC_FLAGS, stringBuilder.String->Buffer); - PhDeleteStringBuilder(&stringBuilder); - - metaData = PhMappedImageRvaToVa(&PvMappedImage, PvImageCor20Header->MetaData.VirtualAddress, NULL); - - if (metaData) - { - __try - { - PhProbeAddress(metaData, PvImageCor20Header->MetaData.Size, PvMappedImage.ViewBase, PvMappedImage.Size, 4); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - metaData = NULL; - } - } - - versionStringLength = 0; - - if (metaData) - { - // Skip 12 bytes. - // First 4 bytes contains the length of the version string. - // The version string follows. - versionStringLength = *(PULONG)((PCHAR)metaData + 12); - - // Make sure the length is valid. - if (versionStringLength >= 0x100) - versionStringLength = 0; - } - - if (versionStringLength != 0) - { - string = PhZeroExtendToUtf16Ex((PCHAR)metaData + 12 + 4, versionStringLength); - SetDlgItemText(hwndDlg, IDC_VERSIONSTRING, string->Buffer); - PhDereferenceObject(string); - } - else - { - SetDlgItemText(hwndDlg, IDC_VERSIONSTRING, L"N/A"); - } - } - break; - case WM_SHOWWINDOW: - { - if (!propPageContext->LayoutInitialized) - { - PvAddPropPageLayoutItem(hwndDlg, hwndDlg, - PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); - - PvDoPropPageLayout(hwndDlg); - - propPageContext->LayoutInitialized = TRUE; - } - } - break; - case WM_NOTIFY: - { - LPNMHDR header = (LPNMHDR)lParam; - - switch (header->code) - { - case PSN_QUERYINITIALFOCUS: - { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_RUNTIMEVERSION)); - } - return TRUE; - } - } - break; - } - - return FALSE; -} +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2010-2011 wj32 + * Copyright (C) 2017-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define PVM_CHECKSUM_DONE (WM_APP + 1) +#define PVM_VERIFY_DONE (WM_APP + 2) + +BOOLEAN PvpLoadDbgHelp( + _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider + ); + +INT_PTR CALLBACK PvpPeGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +PH_MAPPED_IMAGE PvMappedImage; +PIMAGE_COR20_HEADER PvImageCor20Header = NULL; +PPH_SYMBOL_PROVIDER PvSymbolProvider = NULL; +HICON PvImageSmallIcon = NULL; +HICON PvImageLargeIcon = NULL; +PH_IMAGE_VERSION_INFO PvImageVersionInfo; +static VERIFY_RESULT PvImageVerifyResult; +static PPH_STRING PvImageSignerName; + +VOID PvPeProperties( + VOID + ) +{ + PPV_PROPCONTEXT propContext; + PH_MAPPED_IMAGE_IMPORTS imports; + PH_MAPPED_IMAGE_EXPORTS exports; + PIMAGE_DATA_DIRECTORY entry; + + if (!PhExtractIcon(PvFileName->Buffer, &PvImageLargeIcon, &PvImageSmallIcon)) + { + PhGetStockApplicationIcon(&PvImageSmallIcon, &PvImageLargeIcon); + } + + if (PvpLoadDbgHelp(&PvSymbolProvider)) + { + // Load current PE pdb + // TODO: Move into seperate thread. + + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + PhLoadModuleSymbolProvider( + PvSymbolProvider, + PvFileName->Buffer, + (ULONG64)PvMappedImage.NtHeaders32->OptionalHeader.ImageBase, + PvMappedImage.NtHeaders32->OptionalHeader.SizeOfImage + ); + } + else + { + PhLoadModuleSymbolProvider( + PvSymbolProvider, + PvFileName->Buffer, + (ULONG64)PvMappedImage.NtHeaders->OptionalHeader.ImageBase, + PvMappedImage.NtHeaders->OptionalHeader.SizeOfImage + ); + } + } + + if (propContext = PvCreatePropContext(PvFileName)) + { + PPV_PROPPAGECONTEXT newPage; + + // General page + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEGENERAL), + PvpPeGeneralDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + + // Load Config page + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &entry)) && entry->VirtualAddress) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PELOADCONFIG), + PvpPeLoadConfigDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Imports page + if ((NT_SUCCESS(PhGetMappedImageImports(&imports, &PvMappedImage)) && imports.NumberOfDlls != 0) || + (NT_SUCCESS(PhGetMappedImageDelayImports(&imports, &PvMappedImage)) && imports.NumberOfDlls != 0)) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEIMPORTS), + PvpPeImportsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Exports page + if (NT_SUCCESS(PhGetMappedImageExports(&exports, &PvMappedImage)) && exports.NumberOfEntries != 0) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEEXPORTS), + PvpPeExportsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Resources page + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_RESOURCE, &entry)) && entry->VirtualAddress) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PERESOURCES), + PvpPeResourcesDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // CLR page + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &entry)) && + entry->VirtualAddress && + (PvImageCor20Header = PhMappedImageRvaToVa(&PvMappedImage, entry->VirtualAddress, NULL))) + { + NTSTATUS status = STATUS_SUCCESS; + + __try + { + PhProbeAddress( + PvImageCor20Header, + sizeof(IMAGE_COR20_HEADER), + PvMappedImage.ViewBase, + PvMappedImage.Size, + 4 + ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + if (NT_SUCCESS(status)) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PECLR), + PvpPeClrDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + } + + // CFG page + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PECFG), + PvpPeCgfDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Properties page + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEPROPSTORAGE), + PvpPePropStoreDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Extended attributes page + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PEATTR), + PvpPeExtendedAttributesDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Streams page + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PESTREAMS), + PvpPeStreamsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Links page + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PELINKS), + PvpPeLinksDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Processes page + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PIDS), + PvpPeProcessesDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // TLS page + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_TLS, &entry)) && entry->VirtualAddress) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_TLS), + PvpPeTlsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + // Symbols page + if (NT_SUCCESS(PhGetMappedImageDataEntry(&PvMappedImage, IMAGE_DIRECTORY_ENTRY_DEBUG, &entry)) && entry->VirtualAddress) + { + newPage = PvCreatePropPageContext( + MAKEINTRESOURCE(IDD_PESYMBOLS), + PvpSymbolsDlgProc, + NULL + ); + PvAddPropPage(propContext, newPage); + } + + PhModalPropertySheet(&propContext->PropSheetHeader); + + PhDereferenceObject(propContext); + } +} + +static NTSTATUS CheckSumImageThreadStart( + _In_ PVOID Parameter + ) +{ + HWND windowHandle = Parameter; + PPH_STRING importHash = NULL; + ULONG checkSum; + HANDLE fileHandle; + + checkSum = PhCheckSumMappedImage(&PvMappedImage); + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(PvFileName), + FILE_READ_ACCESS, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + 0 + ))) + { + BYTE importTableMd5Hash[16]; + + if (NT_SUCCESS(RtlComputeImportTableHash(fileHandle, importTableMd5Hash, 1))) + { + importHash = PhBufferToHexString(importTableMd5Hash, 16); + } + + NtClose(fileHandle); + } + + PostMessage( + windowHandle, + PVM_CHECKSUM_DONE, + checkSum, + (LPARAM)importHash + ); + + return STATUS_SUCCESS; +} + +VERIFY_RESULT PvpVerifyFileWithAdditionalCatalog( + _In_ PPH_STRING FileName, + _In_ ULONG Flags, + _In_opt_ HWND hWnd, + _Out_opt_ PPH_STRING *SignerName + ) +{ + static PH_STRINGREF codeIntegrityFileName = PH_STRINGREF_INIT(L"\\AppxMetadata\\CodeIntegrity.cat"); + static PH_STRINGREF windowsAppsPathSr = PH_STRINGREF_INIT(L"%ProgramFiles%\\WindowsApps\\"); + VERIFY_RESULT result; + PH_VERIFY_FILE_INFO info; + PPH_STRING windowsAppsPath; + PPH_STRING additionalCatalogFileName = NULL; + PCERT_CONTEXT *signatures; + ULONG numberOfSignatures; + + memset(&info, 0, sizeof(PH_VERIFY_FILE_INFO)); + info.FileName = FileName->Buffer; + info.Flags = Flags; + info.hWnd = hWnd; + + if (windowsAppsPath = PhExpandEnvironmentStrings(&windowsAppsPathSr)) + { + if (PhStartsWithStringRef(&FileName->sr, &windowsAppsPath->sr, TRUE)) + { + PH_STRINGREF remainingFileName; + ULONG_PTR indexOfBackslash; + PH_STRINGREF baseFileName; + + remainingFileName = FileName->sr; + PhSkipStringRef(&remainingFileName, windowsAppsPath->Length); + indexOfBackslash = PhFindCharInStringRef(&remainingFileName, OBJ_NAME_PATH_SEPARATOR, FALSE); + + if (indexOfBackslash != -1) + { + baseFileName.Buffer = FileName->Buffer; + baseFileName.Length = windowsAppsPath->Length + indexOfBackslash * sizeof(WCHAR); + additionalCatalogFileName = PhConcatStringRef2(&baseFileName, &codeIntegrityFileName); + } + } + + PhDereferenceObject(windowsAppsPath); + } + + if (additionalCatalogFileName) + { + info.NumberOfCatalogFileNames = 1; + info.CatalogFileNames = &additionalCatalogFileName->Buffer; + } + + if (!NT_SUCCESS(PhVerifyFileEx(&info, &result, &signatures, &numberOfSignatures))) + { + result = VrNoSignature; + signatures = NULL; + numberOfSignatures = 0; + } + + if (additionalCatalogFileName) + PhDereferenceObject(additionalCatalogFileName); + + if (SignerName) + { + if (numberOfSignatures != 0) + *SignerName = PhGetSignerNameFromCertificate(signatures[0]); + else + *SignerName = NULL; + } + + PhFreeVerifySignatures(signatures, numberOfSignatures); + + return result; +} + +static NTSTATUS VerifyImageThreadStart( + _In_ PVOID Parameter + ) +{ + HWND windowHandle; + + windowHandle = Parameter; + PvImageVerifyResult = PvpVerifyFileWithAdditionalCatalog(PvFileName, PH_VERIFY_PREVENT_NETWORK_ACCESS, NULL, &PvImageSignerName); + PostMessage(windowHandle, PVM_VERIFY_DONE, 0, 0); + + return STATUS_SUCCESS; +} + +FORCEINLINE PPH_STRING PvpGetSectionCharacteristics( + _In_ ULONG Characteristics + ) +{ + PH_STRING_BUILDER stringBuilder; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + PhInitializeStringBuilder(&stringBuilder, 10); + + if (Characteristics & IMAGE_SCN_TYPE_NO_PAD) + PhAppendStringBuilder2(&stringBuilder, L"No Padding, "); + if (Characteristics & IMAGE_SCN_CNT_CODE) + PhAppendStringBuilder2(&stringBuilder, L"Code, "); + if (Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + PhAppendStringBuilder2(&stringBuilder, L"Initialized data, "); + if (Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) + PhAppendStringBuilder2(&stringBuilder, L"Uninitialized data, "); + if (Characteristics & IMAGE_SCN_LNK_INFO) + PhAppendStringBuilder2(&stringBuilder, L"Comments, "); + if (Characteristics & IMAGE_SCN_LNK_REMOVE) + PhAppendStringBuilder2(&stringBuilder, L"Excluded, "); + if (Characteristics & IMAGE_SCN_LNK_COMDAT) + PhAppendStringBuilder2(&stringBuilder, L"COMDAT, "); + if (Characteristics & IMAGE_SCN_NO_DEFER_SPEC_EXC) + PhAppendStringBuilder2(&stringBuilder, L"Speculative exceptions, "); + if (Characteristics & IMAGE_SCN_GPREL) + PhAppendStringBuilder2(&stringBuilder, L"GP relative, "); + if (Characteristics & IMAGE_SCN_MEM_PURGEABLE) + PhAppendStringBuilder2(&stringBuilder, L"Purgeable, "); + if (Characteristics & IMAGE_SCN_MEM_LOCKED) + PhAppendStringBuilder2(&stringBuilder, L"Locked, "); + if (Characteristics & IMAGE_SCN_MEM_PRELOAD) + PhAppendStringBuilder2(&stringBuilder, L"Preload, "); + if (Characteristics & IMAGE_SCN_LNK_NRELOC_OVFL) + PhAppendStringBuilder2(&stringBuilder, L"Extended relocations, "); + if (Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + PhAppendStringBuilder2(&stringBuilder, L"Discardable, "); + if (Characteristics & IMAGE_SCN_MEM_NOT_CACHED) + PhAppendStringBuilder2(&stringBuilder, L"Not cachable, "); + if (Characteristics & IMAGE_SCN_MEM_NOT_PAGED) + PhAppendStringBuilder2(&stringBuilder, L"Not pageable, "); + if (Characteristics & IMAGE_SCN_MEM_SHARED) + PhAppendStringBuilder2(&stringBuilder, L"Shareable, "); + if (Characteristics & IMAGE_SCN_MEM_EXECUTE) + PhAppendStringBuilder2(&stringBuilder, L"Executable, "); + if (Characteristics & IMAGE_SCN_MEM_READ) + PhAppendStringBuilder2(&stringBuilder, L"Readable, "); + if (Characteristics & IMAGE_SCN_MEM_WRITE) + PhAppendStringBuilder2(&stringBuilder, L"Writeable, "); + + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhPrintPointer(pointer, UlongToPtr(Characteristics)); + PhAppendFormatStringBuilder(&stringBuilder, L" (%s)", pointer); + + return PhFinalStringBuilderString(&stringBuilder); +} + +VOID PvpSetPeImageVersionInfo( + _In_ HWND WindowHandle + ) +{ + PPH_STRING string; + + PhInitializeImageVersionInfo(&PvImageVersionInfo, PvFileName->Buffer); + + Static_SetIcon(GetDlgItem(WindowHandle, IDC_FILEICON), PvImageLargeIcon); + + string = PhConcatStrings2(L"(Verifying...) ", PvpGetStringOrNa(PvImageVersionInfo.CompanyName)); + + PhSetDialogItemText(WindowHandle, IDC_NAME, PvpGetStringOrNa(PvImageVersionInfo.FileDescription)); + PhSetDialogItemText(WindowHandle, IDC_COMPANYNAME, string->Buffer); + PhSetDialogItemText(WindowHandle, IDC_VERSION, PvpGetStringOrNa(PvImageVersionInfo.FileVersion)); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), VerifyImageThreadStart, WindowHandle); + + PhDereferenceObject(string); +} + +#ifndef IMAGE_FILE_MACHINE_CHPE_X86 +#define IMAGE_FILE_MACHINE_CHPE_X86 0x3A64 // defined in ntimage.h +#endif + +VOID PvpSetPeImageMachineType( + _In_ HWND WindowHandle + ) +{ + PWSTR type; + + switch (PvMappedImage.NtHeaders->FileHeader.Machine) + { + case IMAGE_FILE_MACHINE_I386: + type = L"i386"; + break; + case IMAGE_FILE_MACHINE_AMD64: + type = L"AMD64"; + break; + case IMAGE_FILE_MACHINE_IA64: + type = L"IA64"; + break; + case IMAGE_FILE_MACHINE_ARMNT: + type = L"ARM Thumb-2"; + break; + case IMAGE_FILE_MACHINE_ARM64: + type = L"ARM64"; + break; + case IMAGE_FILE_MACHINE_CHPE_X86: + type = L"Hybrid PE"; + break; + default: + type = L"Unknown"; + break; + } + + PhSetDialogItemText(WindowHandle, IDC_TARGETMACHINE, type); +} + +VOID PvpSetPeImageTimeStamp( + _In_ HWND WindowHandle + ) +{ + LARGE_INTEGER time; + SYSTEMTIME systemTime; + PPH_STRING string; + + RtlSecondsSince1970ToTime(PvMappedImage.NtHeaders->FileHeader.TimeDateStamp, &time); + PhLargeIntegerToLocalSystemTime(&systemTime, &time); + + string = PhFormatDateTime(&systemTime); + PhSetDialogItemText(WindowHandle, IDC_TIMESTAMP, string->Buffer); + PhDereferenceObject(string); +} + +VOID PvpSetPeImageBaseAddress( + _In_ HWND WindowHandle + ) +{ + PPH_STRING string; + + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + string = PhFormatString(L"0x%I32x", ((PIMAGE_OPTIONAL_HEADER32)&PvMappedImage.NtHeaders->OptionalHeader)->ImageBase); + else + string = PhFormatString(L"0x%I64x", ((PIMAGE_OPTIONAL_HEADER64)&PvMappedImage.NtHeaders->OptionalHeader)->ImageBase); + + PhSetDialogItemText(WindowHandle, IDC_IMAGEBASE, string->Buffer); + PhDereferenceObject(string); +} + +VOID PvpSetPeImageEntryPoint( + _In_ HWND WindowHandle + ) +{ + PPH_STRING string; + + if (PvMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + string = PhFormatString(L"0x%I32x", ((PIMAGE_OPTIONAL_HEADER32)&PvMappedImage.NtHeaders->OptionalHeader)->AddressOfEntryPoint); + else + string = PhFormatString(L"0x%I32x", ((PIMAGE_OPTIONAL_HEADER64)&PvMappedImage.NtHeaders->OptionalHeader)->AddressOfEntryPoint); + + PhSetDialogItemText(WindowHandle, IDC_ENTRYPOINT, string->Buffer); + PhDereferenceObject(string); +} + +VOID PvpSetPeImageCheckSum( + _In_ HWND WindowHandle + ) +{ + PPH_STRING string; + + string = PhFormatString(L"0x%Ix (verifying...)", PvMappedImage.NtHeaders->OptionalHeader.CheckSum); // same for 32-bit and 64-bit images + + PhSetDialogItemText(WindowHandle, IDC_CHECKSUM, string->Buffer); + + PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), CheckSumImageThreadStart, WindowHandle); + + PhDereferenceObject(string); +} + +VOID PvpSetPeImageSubsystem( + _In_ HWND WindowHandle + ) +{ + PWSTR type; + + switch (PvMappedImage.NtHeaders->OptionalHeader.Subsystem) + { + case IMAGE_SUBSYSTEM_NATIVE: + type = L"Native"; + break; + case IMAGE_SUBSYSTEM_WINDOWS_GUI: + type = L"Windows GUI"; + break; + case IMAGE_SUBSYSTEM_WINDOWS_CUI: + type = L"Windows CUI"; + break; + case IMAGE_SUBSYSTEM_OS2_CUI: + type = L"OS/2 CUI"; + break; + case IMAGE_SUBSYSTEM_POSIX_CUI: + type = L"POSIX CUI"; + break; + case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: + type = L"Windows CE CUI"; + break; + case IMAGE_SUBSYSTEM_EFI_APPLICATION: + type = L"EFI Application"; + break; + case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: + type = L"EFI Boot Service Driver"; + break; + case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: + type = L"EFI Runtime Driver"; + break; + case IMAGE_SUBSYSTEM_EFI_ROM: + type = L"EFI ROM"; + break; + case IMAGE_SUBSYSTEM_XBOX: + type = L"Xbox"; + break; + case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: + type = L"Windows Boot Application"; + break; + default: + type = L"Unknown"; + break; + } + + PhSetDialogItemText(WindowHandle, IDC_SUBSYSTEM, type); + PhSetDialogItemText(WindowHandle, IDC_SUBSYSTEMVERSION, PhaFormatString( + L"%u.%u", + PvMappedImage.NtHeaders->OptionalHeader.MajorSubsystemVersion, // same for 32-bit and 64-bit images + PvMappedImage.NtHeaders->OptionalHeader.MinorSubsystemVersion + )->Buffer); +} + +VOID PvpSetPeImageCharacteristics( + _In_ HWND WindowHandle + ) +{ + PH_STRING_BUILDER stringBuilder; + + PhInitializeStringBuilder(&stringBuilder, 10); + + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) + PhAppendStringBuilder2(&stringBuilder, L"Executable, "); + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) + PhAppendStringBuilder2(&stringBuilder, L"DLL, "); + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) + PhAppendStringBuilder2(&stringBuilder, L"Large address aware, "); + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) + PhAppendStringBuilder2(&stringBuilder, L"Removable run from swap, "); + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_NET_RUN_FROM_SWAP) + PhAppendStringBuilder2(&stringBuilder, L"Net run from swap, "); + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_SYSTEM) + PhAppendStringBuilder2(&stringBuilder, L"System, "); + if (PvMappedImage.NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) + PhAppendStringBuilder2(&stringBuilder, L"Uni-processor only, "); + + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) + PhAppendStringBuilder2(&stringBuilder, L"High entropy VA, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) + PhAppendStringBuilder2(&stringBuilder, L"Dynamic base, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY) + PhAppendStringBuilder2(&stringBuilder, L"Force integrity check, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT) + PhAppendStringBuilder2(&stringBuilder, L"NX compatible, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION) + PhAppendStringBuilder2(&stringBuilder, L"No isolation, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) + PhAppendStringBuilder2(&stringBuilder, L"No SEH, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_BIND) + PhAppendStringBuilder2(&stringBuilder, L"Do not bind, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_APPCONTAINER) + PhAppendStringBuilder2(&stringBuilder, L"AppContainer, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER) + PhAppendStringBuilder2(&stringBuilder, L"WDM driver, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF) + PhAppendStringBuilder2(&stringBuilder, L"Control Flow Guard, "); + if (PvMappedImage.NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) + PhAppendStringBuilder2(&stringBuilder, L"Terminal server aware, "); + + if (PhEndsWithString2(stringBuilder.String, L", ", FALSE)) + PhRemoveEndStringBuilder(&stringBuilder, 2); + + PhSetDialogItemText(WindowHandle, IDC_CHARACTERISTICS, stringBuilder.String->Buffer); + PhDeleteStringBuilder(&stringBuilder); +} + +VOID PvpSetPeImageSections( + _In_ HWND ListViewHandle + ) +{ + for (ULONG i = 0; i < PvMappedImage.NumberOfSections; i++) + { + INT lvItemIndex; + WCHAR sectionName[IMAGE_SIZEOF_SHORT_NAME + 1]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + if (PhGetMappedImageSectionName(&PvMappedImage.Sections[i], sectionName, ARRAYSIZE(sectionName), NULL)) + { + PhPrintPointer(pointer, UlongToPtr(PvMappedImage.Sections[i].VirtualAddress)); + + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, sectionName, NULL); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, pointer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, PhaFormatSize(PvMappedImage.Sections[i].SizeOfRawData, ULONG_MAX)->Buffer); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, PH_AUTO_T(PH_STRING, PvpGetSectionCharacteristics(PvMappedImage.Sections[i].Characteristics))->Buffer); + + //if (PvMappedImage.Sections[i].VirtualAddress && PvMappedImage.Sections[i].SizeOfRawData) + //{ + // PVOID imageSectionData; + // PH_HASH_CONTEXT hashContext; + // PPH_STRING hashString; + // UCHAR hash[32]; + // + // if (imageSectionData = PhMappedImageRvaToVa(&PvMappedImage, PvMappedImage.Sections[i].VirtualAddress, NULL)) + // { + // PhInitializeHash(&hashContext, Md5HashAlgorithm); // PhGetIntegerSetting(L"HashAlgorithm") + // PhUpdateHash(&hashContext, imageSectionData, PvMappedImage.Sections[i].SizeOfRawData); + // + // if (PhFinalHash(&hashContext, hash, 16, NULL)) + // { + // hashString = PhBufferToHexString(hash, 16); + // PhSetListViewSubItem(ListViewHandle, lvItemIndex, 4, hashString->Buffer); + // PhDereferenceObject(hashString); + // } + // } + //} + } + } +} + +NTSTATUS PhpOpenFileSecurity( + _Out_ PHANDLE Handle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ PVOID Context + ) +{ + NTSTATUS status; + FILE_NETWORK_OPEN_INFORMATION networkOpenInfo; + + status = PhQueryFullAttributesFileWin32(PhGetString(PvFileName), &networkOpenInfo); + + if (!NT_SUCCESS(status)) + return status; + + if (networkOpenInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + status = PhCreateFileWin32( + Handle, + PhGetString(PvFileName), + DesiredAccess| READ_CONTROL | WRITE_DAC, + FILE_ATTRIBUTE_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + 0 + ); + } + else + { + status = PhCreateFileWin32( + Handle, + PhGetString(PvFileName), + DesiredAccess | READ_CONTROL | WRITE_DAC, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + 0 + ); + + if (!NT_SUCCESS(status)) + { + status = PhCreateFileWin32( + Handle, + PhGetString(PvFileName), + DesiredAccess | READ_CONTROL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + 0 + ); + } + } + + return status; +} + + +INT_PTR CALLBACK PvpPeGeneralDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 80, L"Name"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"VA"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Size"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 250, L"Characteristics"); + //PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 80, L"Hash"); + PhLoadListViewColumnsFromSetting(L"ImageGeneralListViewColumns", lvHandle); + + // File version information + PvpSetPeImageVersionInfo(hwndDlg); + + // PE properties + PvpSetPeImageMachineType(hwndDlg); + PvpSetPeImageTimeStamp(hwndDlg); + PvpSetPeImageBaseAddress(hwndDlg); + PvpSetPeImageEntryPoint(hwndDlg); + PvpSetPeImageCheckSum(hwndDlg); + PvpSetPeImageSubsystem(hwndDlg); + PvpSetPeImageCharacteristics(hwndDlg); + + PvpSetPeImageSections(lvHandle); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageGeneralListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_FILE), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_NAME), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_CHARACTERISTICS), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_TOP | PH_ANCHOR_RIGHT); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case PVM_CHECKSUM_DONE: + { + PPH_STRING string; + PPH_STRING importTableHash; + ULONG headerCheckSum; + ULONG realCheckSum; + + headerCheckSum = PvMappedImage.NtHeaders->OptionalHeader.CheckSum; // same for 32-bit and 64-bit images + realCheckSum = (ULONG)wParam; + importTableHash = (PPH_STRING)lParam; + + if (headerCheckSum == 0) + { + // Some executables, like .NET ones, don't have a check sum. + string = PhFormatString(L"0x0 (real 0x%Ix) (%s)", realCheckSum, PhGetStringOrDefault(importTableHash, L"N/A")); + PhSetDialogItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); + PhDereferenceObject(string); + } + else if (headerCheckSum == realCheckSum) + { + string = PhFormatString(L"0x%Ix (correct) (%s)", headerCheckSum, PhGetStringOrDefault(importTableHash, L"N/A")); + PhSetDialogItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); + PhDereferenceObject(string); + } + else + { + string = PhFormatString(L"0x%Ix (incorrect, real 0x%Ix) (%s)", headerCheckSum, realCheckSum, PhGetStringOrDefault(importTableHash, L"N/A")); + PhSetDialogItemText(hwndDlg, IDC_CHECKSUM, string->Buffer); + PhDereferenceObject(string); + } + + PhClearReference(&importTableHash); + } + break; + case PVM_VERIFY_DONE: + { + PPH_STRING string; + + if (PvImageVerifyResult == VrTrusted) + { + if (PvImageSignerName) + { + string = PhFormatString(L"(Verified) %s", PvImageSignerName->Buffer); + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME_LINK, string->Buffer); + PhDereferenceObject(string); + ShowWindow(GetDlgItem(hwndDlg, IDC_COMPANYNAME), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_COMPANYNAME_LINK), SW_SHOW); + } + else + { + string = PhConcatStrings2(L"(Verified) ", PhGetStringOrEmpty(PvImageVersionInfo.CompanyName)); + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, string->Buffer); + PhDereferenceObject(string); + } + } + else if (PvImageVerifyResult != VrUnknown) + { + string = PhConcatStrings2(L"(UNVERIFIED) ", PhGetStringOrEmpty(PvImageVersionInfo.CompanyName)); + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, string->Buffer); + PhDereferenceObject(string); + } + else + { + PhSetDialogItemText(hwndDlg, IDC_COMPANYNAME, PvpGetStringOrNa(PvImageVersionInfo.CompanyName)); + } + } + break; + case WM_NOTIFY: + { + LPNMHDR header = (LPNMHDR)lParam; + + switch (header->code) + { + case NM_CLICK: + { + switch (header->idFrom) + { + case IDC_COMPANYNAME_LINK: + { + PvpVerifyFileWithAdditionalCatalog(PvFileName, PH_VERIFY_VIEW_PROPERTIES, hwndDlg, NULL); + } + break; + } + } + break; + } + + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} + +BOOLEAN PvpLoadDbgHelp( + _Inout_ PPH_SYMBOL_PROVIDER *SymbolProvider + ) +{ + static UNICODE_STRING symbolPathVarName = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH"); + PPH_STRING symbolSearchPath; + PPH_SYMBOL_PROVIDER symbolProvider; + UNICODE_STRING symbolPathUs; + WCHAR buffer[512]; + + // Load symbol path from _NT_SYMBOL_PATH if configured by the user. + RtlInitEmptyUnicodeString(&symbolPathUs, buffer, sizeof(buffer)); + + if (NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &symbolPathVarName, &symbolPathUs))) + { + symbolSearchPath = PhCreateStringFromUnicodeString(&symbolPathUs); + } + else + { + // Set the default path (C:\\Symbols is the default hard-coded path for livekd). + symbolSearchPath = PhCreateString(L"SRV*C:\\Symbols*https://msdl.microsoft.com/download/symbols"); + } + + symbolProvider = PhCreateSymbolProvider(NULL); + PhSetSearchPathSymbolProvider(symbolProvider, symbolSearchPath->Buffer); + PhDereferenceObject(symbolSearchPath); + + *SymbolProvider = symbolProvider; + return TRUE; +} diff --git a/tools/peview/peview.manifest b/tools/peview/peview.manifest index a40beb0cc0b2..ba407fa53b04 100644 --- a/tools/peview/peview.manifest +++ b/tools/peview/peview.manifest @@ -1,46 +1,46 @@ - - - - PE Viewer - - - - - - - - - - - - - - - - - - - - - - - - true - - - true - - + + + + PE Viewer + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + \ No newline at end of file diff --git a/tools/peview/peview.rc b/tools/peview/peview.rc index 6f48fb7e7d01..21b312458d8d 100644 --- a/tools/peview/peview.rc +++ b/tools/peview/peview.rc @@ -1,240 +1,492 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" -#include "../../ProcessHacker/include/phappres.h" -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "#include ""../../ProcessHacker/include/phappres.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_PEGENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END - - IDD_PEIMPORTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END - - IDD_PEEXPORTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END - - IDD_LIBEXPORTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END - - IDD_PECLR, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END - - IDD_PELOADCONFIG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END - - IDD_PECFG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 292 - TOPMARGIN, 7 - BOTTOMMARGIN, 273 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_PEGENERAL DIALOGEX 0, 0, 300, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "General" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Target machine:",IDC_STATIC,7,57,53,8 - LTEXT "Static",IDC_TARGETMACHINE,78,57,215,8 - LTEXT "Checksum:",IDC_STATIC,7,100,36,8 - LTEXT "Static",IDC_CHECKSUM,78,100,215,8 - LTEXT "Subsystem:",IDC_STATIC,7,111,38,8 - LTEXT "Subsystem version:",IDC_STATIC,7,122,64,8 - LTEXT "Characteristics:",IDC_STATIC,7,133,51,8 - LTEXT "Static",IDC_SUBSYSTEM,78,111,215,8 - LTEXT "Static",IDC_SUBSYSTEMVERSION,78,122,215,8 - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,156,286,116 - LTEXT "Sections:",IDC_STATIC,7,146,30,8 - EDITTEXT IDC_CHARACTERISTICS,76,133,217,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Time stamp:",IDC_STATIC,7,68,40,8 - LTEXT "Static",IDC_TIMESTAMP,78,68,215,8 - LTEXT "Entry point:",IDC_STATIC,7,90,39,8 - LTEXT "Static",IDC_ENTRYPOINT,78,90,215,8 - LTEXT "Image base:",IDC_STATIC,7,79,41,8 - LTEXT "Static",IDC_IMAGEBASE,78,79,215,8 - ICON "",IDC_FILEICON,14,18,20,20 - GROUPBOX "File",IDC_FILE,7,7,286,48 - EDITTEXT IDC_NAME,44,17,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - EDITTEXT IDC_COMPANYNAME,44,29,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Version:",IDC_STATIC,15,41,27,8 - EDITTEXT IDC_VERSION,44,41,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - CONTROL "Company Name Link",IDC_COMPANYNAME_LINK,"SysLink",LWS_NOPREFIX | NOT WS_VISIBLE | WS_TABSTOP,46,29,240,9 -END - -IDD_PEIMPORTS DIALOGEX 0, 0, 300, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Imports" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 -END - -IDD_PEEXPORTS DIALOGEX 0, 0, 300, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Exports" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 -END - -IDD_LIBEXPORTS DIALOGEX 0, 0, 300, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Exports" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,6,287,267 -END - -IDD_PECLR DIALOGEX 0, 0, 300, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "CLR" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Runtime Version:",IDC_STATIC,7,7,55,8 - LTEXT "Static",IDC_RUNTIMEVERSION,78,7,215,8 - LTEXT "Flags:",IDC_STATIC,7,19,20,8 - EDITTEXT IDC_FLAGS,76,19,217,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - LTEXT "Version String:",IDC_STATIC,7,31,48,8 - LTEXT "Static",IDC_VERSIONSTRING,78,31,215,8 -END - -IDD_PELOADCONFIG DIALOGEX 0, 0, 300, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Load config" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 -END - -IDD_PECFG DIALOGEX 0, 0, 299, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "CFG" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,286,266 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_PELOADCONFIG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PEGENERAL AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PECFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" +#include "../../ProcessHacker/include/phappres.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "#include ""../../ProcessHacker/include/phappres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PEGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 273 + END + + IDD_PEIMPORTS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PEEXPORTS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_LIBEXPORTS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PECLR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 273 + END + + IDD_PELOADCONFIG, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PECFG, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 296 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PESYMBOLS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 298 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PERESOURCES, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_ELFGENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 273 + END + + IDD_PEPROPSTORAGE, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 298 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PEATTR, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PESTREAMS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PELINKS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_PIDS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_ELFDYNAMIC, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_TLS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 297 + TOPMARGIN, 3 + BOTTOMMARGIN, 277 + END + + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 302 + TOPMARGIN, 7 + BOTTOMMARGIN, 169 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PEGENERAL DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Target machine:",IDC_STATIC,7,57,53,8 + LTEXT "Static",IDC_TARGETMACHINE,78,57,215,8 + LTEXT "Checksum:",IDC_STATIC,7,100,36,8 + LTEXT "Subsystem:",IDC_STATIC,7,111,38,8 + LTEXT "Subsystem version:",IDC_STATIC,7,122,64,8 + LTEXT "Characteristics:",IDC_STATIC,7,133,51,8 + LTEXT "Static",IDC_SUBSYSTEM,78,111,215,8 + LTEXT "Static",IDC_SUBSYSTEMVERSION,78,122,215,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,156,286,116 + LTEXT "Sections:",IDC_STATIC,7,146,30,8 + EDITTEXT IDC_CHARACTERISTICS,76,133,217,23,ES_MULTILINE | ES_READONLY | NOT WS_BORDER + LTEXT "Time stamp:",IDC_STATIC,7,68,40,8 + LTEXT "Static",IDC_TIMESTAMP,78,68,215,8 + LTEXT "Entry point:",IDC_STATIC,7,90,39,8 + LTEXT "Static",IDC_ENTRYPOINT,78,90,215,8 + LTEXT "Image base:",IDC_STATIC,7,79,41,8 + LTEXT "Static",IDC_IMAGEBASE,78,79,215,8 + ICON "",IDC_FILEICON,14,18,21,20 + GROUPBOX "File",IDC_FILE,7,7,286,48 + EDITTEXT IDC_NAME,44,17,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + EDITTEXT IDC_COMPANYNAME,44,29,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Version:",IDC_STATIC,15,41,27,8 + EDITTEXT IDC_VERSION,44,41,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + CONTROL "Company Name Link",IDC_COMPANYNAME_LINK,"SysLink",LWS_NOPREFIX | NOT WS_VISIBLE | WS_TABSTOP,46,29,240,9 + EDITTEXT IDC_CHECKSUM,76,100,217,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER +END + +IDD_PEIMPORTS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Imports" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_PEEXPORTS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Exports" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_LIBEXPORTS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Exports" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_PECLR DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "CLR" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Runtime Version:",IDC_STATIC,7,7,55,8 + LTEXT "Static",IDC_RUNTIMEVERSION,78,7,215,8 + LTEXT "Flags:",IDC_STATIC,7,19,20,8 + EDITTEXT IDC_FLAGS,76,19,217,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Version String:",IDC_STATIC,7,32,48,8 + LTEXT "Static",IDC_VERSIONSTRING,78,32,215,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,69,286,204 + LTEXT "Sections:",IDC_STATIC,7,58,30,8 + LTEXT "MVID:",IDC_STATIC,7,44,21,8 + EDITTEXT IDC_MVIDSTRING,76,45,217,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER +END + +IDD_PELOADCONFIG DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Load config" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_PECFG DIALOGEX 0, 0, 299, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "CFG" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,293,274 +END + +IDD_PESYMBOLS DIALOGEX 0, 0, 301, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Symbols" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_SYMBOLTREE,"PhTreeNew",WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0x2,3,21,295,256,WS_EX_CLIENTEDGE + EDITTEXT IDC_SYMSEARCH,157,3,141,14,ES_AUTOHSCROLL +END + +IDD_PERESOURCES DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Resources" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_ELFGENERAL DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Target machine:",IDC_STATIC,7,60,53,8 + LTEXT "Static",IDC_TARGETMACHINE,78,60,215,8 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,128,286,144 + LTEXT "Sections:",IDC_STATIC,7,115,30,8 + LTEXT "Entry point:",IDC_STATIC,7,100,39,8 + LTEXT "Static",IDC_ENTRYPOINT,78,100,215,8 + LTEXT "Image base:",IDC_STATIC,7,87,41,8 + LTEXT "Static",IDC_IMAGEBASE,78,87,215,8 + LTEXT "Image type:",IDC_STATIC,7,73,40,8 + LTEXT "Static",IDC_IMAGETYPE,78,73,215,8 + ICON "",IDC_FILEICON,14,18,20,20 + GROUPBOX "File",IDC_FILE,7,7,286,48 + EDITTEXT IDC_NAME,44,17,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "Version:",IDC_STATIC,15,41,27,8 + EDITTEXT IDC_VERSION,44,41,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + EDITTEXT IDC_COMPANYNAME,44,29,242,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER +END + +IDD_PEPROPSTORAGE DIALOGEX 0, 0, 301, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Properties" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,295,274 +END + +IDD_PEATTR DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Attributes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_PESTREAMS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Streams" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_PELINKS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Links" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_PIDS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Pids" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_ELFDYNAMIC DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dynamic" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_TLS DIALOGEX 0, 0, 300, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Tls" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,3,3,294,274 +END + +IDD_OPTIONS DIALOGEX 0, 0, 309, 176 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,198,155,50,14 + PUSHBUTTON "Cancel",IDCANCEL,252,155,50,14 + LTEXT "Max. size unit:",IDC_STATIC,7,24,49,8 + COMBOBOX IDC_MAXSIZEUNIT,61,23,39,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Font...",IDC_FONT,179,23,49,14 + CONTROL "",IDC_SETTINGS,"SysListView32",LVS_LIST | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,39,295,113 + LTEXT "Application font:",IDC_STATIC,121,25,54,8 + LTEXT "Symbol path:",IDC_STATIC,7,8,43,8 + EDITTEXT IDC_DBGHELPSEARCHPATH,61,7,241,12,ES_AUTOHSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_SEARCH_ACTIVE_BMP BITMAP "resources\\active_search.bmp" + +IDB_SEARCH_INACTIVE_BMP BITMAP "resources\\inactive_search.bmp" + + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_SEARCH_ACTIVE PNG "resources\\active_search.png" + +IDB_SEARCH_INACTIVE PNG "resources\\inactive_search.png" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PHAPP_VERSION_NUMBER + PRODUCTVERSION PHAPP_VERSION_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0904b0" + BEGIN + VALUE "CompanyName", "Process Hacker" + VALUE "FileDescription", "PE Viewer" + VALUE "FileVersion", PHAPP_VERSION_STRING + VALUE "InternalName", "peview.exe" + VALUE "LegalCopyright", "Licensed under the GNU GPL, v3. Copyright (C) 2017" + VALUE "OriginalFilename", "peview.exe" + VALUE "ProductName", "PE Viewer" + VALUE "ProductVersion", PHAPP_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc09, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "resources\\application.ico" + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/tools/peview/peview.vcxproj b/tools/peview/peview.vcxproj index d9119eb82275..f2a45df1243d 100644 --- a/tools/peview/peview.vcxproj +++ b/tools/peview/peview.vcxproj @@ -1,221 +1,279 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {72C124A2-3C80-41C6-ABA1-C4948B713204} - peview - Win32Proj - 10.0.15063.0 - - - - Application - Unicode - true - v141 - - - Application - Unicode - v141 - - - Application - Unicode - true - v141 - - - Application - Unicode - v141 - - - - - - - - - - - - - - - - - - - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ - $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ - false - - - - - - - - Disabled - ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN32;DEBUG;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - true - - - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - - - true - Windows - MachineX86 - 6.01 - - - - - Disabled - ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - true - - - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - - - true - Windows - MachineX64 - 6.01 - - - - - MaxSpeed - true - ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions) - MultiThreaded - true - Level3 - ProgramDatabase - StdCall - true - StreamingSIMDExtensions - true - true - - - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - - - true - Windows - true - true - MachineX86 - true - 6.01 - - - - - MaxSpeed - true - ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) - _PHLIB_;_WINDOWS;WIN64;DEBUG;%(PreprocessorDefinitions) - MultiThreaded - true - Level3 - ProgramDatabase - StdCall - true - true - true - - - noarg.obj;noenv.obj;phlib.lib;ntdll.lib;uxtheme.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - - - true - Windows - true - true - MachineX64 - true - 6.01 - - - - - - - - - - - - - - - - - - - - - {477d0215-f252-41a1-874b-f27e3ea1ed17} - false - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {72C124A2-3C80-41C6-ABA1-C4948B713204} + peview + Win32Proj + 10.0 + + + + Application + Unicode + true + v142 + Spectre + + + Application + Unicode + v142 + + + Application + Unicode + true + v142 + Spectre + + + Application + Unicode + v142 + + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ + $(ProjectDir)obj\$(Configuration)$(PlatformArchitecture)\ + false + + + + + + + + Disabled + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN32;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + true + true + + + aclui.lib;propsys.lib;phlib.lib;ntdll.lib;uxtheme.lib;userenv.lib;windowscodecs.lib;winsta.lib;noarg.obj;noenv.obj;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + MachineX86 + 6.01 + aclui.dll;advapi32.dll;comctl32.dll;comdlg32.dll;gdi32.dll;ole32.dll;propsys.dll;shell32.dll;user32.dll;userenv.dll;uxtheme.dll;version.dll;winsta.dll;%(DelayLoadDLLs) + + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + + + + + Disabled + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN64;_DEBUG;DEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + true + true + + + aclui.lib;propsys.lib;phlib.lib;ntdll.lib;uxtheme.lib;userenv.lib;windowscodecs.lib;winsta.lib;noarg.obj;noenv.obj;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + MachineX64 + 6.01 + aclui.dll;advapi32.dll;comctl32.dll;comdlg32.dll;gdi32.dll;ole32.dll;propsys.dll;shell32.dll;user32.dll;userenv.dll;uxtheme.dll;version.dll;winsta.dll;%(DelayLoadDLLs) + + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + + + + + MaxSpeed + true + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN32;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + StreamingSIMDExtensions + true + true + true + Guard + + + aclui.lib;propsys.lib;phlib.lib;ntdll.lib;uxtheme.lib;userenv.lib;windowscodecs.lib;winsta.lib;noarg.obj;noenv.obj;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + true + true + MachineX86 + true + 6.01 + aclui.dll;advapi32.dll;comctl32.dll;comdlg32.dll;gdi32.dll;ole32.dll;propsys.dll;shell32.dll;user32.dll;userenv.dll;uxtheme.dll;version.dll;winsta.dll;%(DelayLoadDLLs) + + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + + + + + MaxSpeed + true + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _PHLIB_;_WINDOWS;WIN64;NDEBUG;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + true + true + true + Guard + + + aclui.lib;propsys.lib;phlib.lib;ntdll.lib;uxtheme.lib;userenv.lib;windowscodecs.lib;winsta.lib;noarg.obj;noenv.obj;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + + + true + Windows + true + true + MachineX64 + true + 6.01 + aclui.dll;advapi32.dll;comctl32.dll;comdlg32.dll;gdi32.dll;ole32.dll;propsys.dll;shell32.dll;user32.dll;userenv.dll;uxtheme.dll;version.dll;winsta.dll;%(DelayLoadDLLs) + + + ..\..\phnt\include;..\..\phlib\include;include;%(AdditionalIncludeDirectories) + _UNICODE;UNICODE;%(PreprocessorDefinitions);$(ExternalCompilerOptions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {477d0215-f252-41a1-874b-f27e3ea1ed17} + false + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/peview/peview.vcxproj.filters b/tools/peview/peview.vcxproj.filters index ff1f55b4ac2f..c7bc25dca9b7 100644 --- a/tools/peview/peview.vcxproj.filters +++ b/tools/peview/peview.vcxproj.filters @@ -1,58 +1,153 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;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 - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - Resource Files - - - - - Resource Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;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 + + + {227fbc2c-1da0-44f4-95c8-47aca5abb52a} + + + {d170a140-b947-49a4-87e8-e1d35b1024c1} + + + {3213da7e-9e01-48a4-8895-c4dd1499b2fa} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Windows + + + Source Files\Pages\Linux + + + Source Files\Pages\Linux + + + Source Files\Pages\Linux + + + Source Files\Pages + + + Source Files\Pages + + + Source Files\Pages + + + Source Files\Pages\Windows + + + Source Files + + + Source Files\Pages\Linux + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + \ No newline at end of file diff --git a/tools/peview/processes.c b/tools/peview/processes.c new file mode 100644 index 000000000000..6cf6c10e439f --- /dev/null +++ b/tools/peview/processes.c @@ -0,0 +1,146 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpPeEnumerateProcessIds( + _In_ HWND ListViewHandle + ) +{ + HANDLE fileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(PvFileName), + FILE_READ_ATTRIBUTES | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + PFILE_PROCESS_IDS_USING_FILE_INFORMATION processIds; + ULONG count = 0; + ULONG i; + + if (NT_SUCCESS(PhGetProcessIdsUsingFile( + fileHandle, + &processIds + ))) + { + for (i = 0; i < processIds->NumberOfProcessIdsInList; i++) + { + INT lvItemIndex; + HANDLE processId; + PPH_STRING fileName = NULL; + WCHAR number[PH_INT32_STR_LEN_1]; + + processId = (HANDLE)processIds->ProcessIdList[i]; + + if (processId == SYSTEM_PROCESS_ID) + fileName = PhGetKernelFileName(); + else + PhGetProcessImageFileNameByProcessId(processId, &fileName); + + if (fileName) + PhMoveReference(&fileName, PhGetFileName(fileName)); + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhPrintUInt32(number, HandleToUlong(processId)); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, number); + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, fileName->Buffer); + PhDereferenceObject(fileName); + } + + PhFree(processIds); + } + + NtClose(fileHandle); + } +} + +INT_PTR CALLBACK PvpPeProcessesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"PID"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 150, L"Name"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImagePidsListViewColumns", lvHandle); + + PvpPeEnumerateProcessIds(lvHandle); + //ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImagePidsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/propstore.c b/tools/peview/propstore.c new file mode 100644 index 000000000000..f2b5a34152f4 --- /dev/null +++ b/tools/peview/propstore.c @@ -0,0 +1,199 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2018-2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpPeEnumerateFilePropStore( + _In_ HWND ListViewHandle + ) +{ + HRESULT status; + IPropertyStore *propstore; + ULONG count; + ULONG i; + + status = SHGetPropertyStoreFromParsingName( + PvFileName->Buffer, + NULL, + GPS_DEFAULT | GPS_EXTRINSICPROPERTIES | GPS_VOLATILEPROPERTIES, + &IID_IPropertyStore, + &propstore + ); + + if (status == HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND)) + { + status = SHGetPropertyStoreFromParsingName( + PvFileName->Buffer, + NULL, + GPS_FASTPROPERTIESONLY, + &IID_IPropertyStore, + &propstore + ); + } + + if (FAILED(status)) + { + status = SHGetPropertyStoreFromParsingName( + PvFileName->Buffer, + NULL, + GPS_DEFAULT, // required for Windows 7 (dmex) + &IID_IPropertyStore, + &propstore + ); + } + + if (SUCCEEDED(status)) + { + if (SUCCEEDED(IPropertyStore_GetCount(propstore, &count))) + { + for (i = 0; i < count; i++) + { + PROPERTYKEY propkey; + + if (SUCCEEDED(IPropertyStore_GetAt(propstore, i, &propkey))) + { + INT lvItemIndex; + PROPVARIANT propKeyVariant = { 0 }; + PWSTR propKeyName; + WCHAR number[PH_INT32_STR_LEN_1]; + + PhPrintUInt32(number, i + 1); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + if (SUCCEEDED(PSGetNameFromPropertyKey(&propkey, &propKeyName))) + { + IPropertyDescription* propertyDescriptionPtr = NULL; + + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, propKeyName); + + if (SUCCEEDED(PSGetPropertyDescriptionByName(propKeyName, &IID_IPropertyDescription, &propertyDescriptionPtr))) + { + PWSTR propertyLabel = NULL; + + if (SUCCEEDED(IPropertyDescription_GetDisplayName(propertyDescriptionPtr, &propertyLabel))) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 3, propertyLabel); + CoTaskMemFree(propertyLabel); + } + + IPropertyDescription_Release(propertyDescriptionPtr); + } + + CoTaskMemFree(propKeyName); + } + else + { + WCHAR propKeyString[PKEYSTR_MAX]; + + if (SUCCEEDED(PSStringFromPropertyKey(&propkey, propKeyString, RTL_NUMBER_OF(propKeyString)))) + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, propKeyString); + else + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, L"Unknown"); + } + + if (SUCCEEDED(IPropertyStore_GetValue(propstore, &propkey, &propKeyVariant))) + { + if (SUCCEEDED(PSFormatForDisplayAlloc(&propkey, &propKeyVariant, PDFF_DEFAULT, &propKeyName))) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, propKeyName); + CoTaskMemFree(propKeyName); + } + + PropVariantClear(&propKeyVariant); + } + } + } + } + + IPropertyStore_Release(propstore); + } +} + +INT_PTR CALLBACK PvpPePropStoreDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 250, L"Value"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 150, L"Description"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImagePropertiesListViewColumns", lvHandle); + + PvpPeEnumerateFilePropStore(lvHandle); + //ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImagePropertiesListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/prpsh.c b/tools/peview/prpsh.c index 70f9d0ed53da..3378ed102e88 100644 --- a/tools/peview/prpsh.c +++ b/tools/peview/prpsh.c @@ -23,6 +23,8 @@ // NOTE: Copied from processhacker2\ProcessHacker\procprp.c #include +#include +#include PPH_OBJECT_TYPE PvpPropContextType; PPH_OBJECT_TYPE PvpPropPageContextType; @@ -48,9 +50,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ); INT CALLBACK PvpStandardPropPageProc( @@ -80,6 +80,7 @@ PPV_PROPCONTEXT PvCreatePropContext( memset(propContext, 0, sizeof(PV_PROPCONTEXT)); propContext->Title = Caption; + propContext->StartPage = PhGetStringSetting(L"MainWindowPage"); propContext->PropSheetPages = PhAllocate(sizeof(HPROPSHEETPAGE) * PV_PROPCONTEXT_MAXPAGES); memset(&propSheetHeader, 0, sizeof(PROPSHEETHEADER)); @@ -90,6 +91,7 @@ PPV_PROPCONTEXT PvCreatePropContext( PSH_NOCONTEXTHELP | PSH_PROPTITLE | PSH_USECALLBACK; + propSheetHeader.hInstance = PhInstanceHandle; propSheetHeader.hwndParent = NULL; propSheetHeader.pszCaption = propContext->Title->Buffer; propSheetHeader.pfnCallback = PvpPropSheetProc; @@ -97,6 +99,9 @@ PPV_PROPCONTEXT PvCreatePropContext( propSheetHeader.nStartPage = 0; propSheetHeader.phpage = propContext->PropSheetPages; + propSheetHeader.dwFlags |= PSH_USEPSTARTPAGE; + propSheetHeader.pStartPage = propContext->StartPage->Buffer; + memcpy(&propContext->PropSheetHeader, &propSheetHeader, sizeof(PROPSHEETHEADER)); return propContext; @@ -110,9 +115,181 @@ VOID NTAPI PvpPropContextDeleteProcedure( PPV_PROPCONTEXT propContext = (PPV_PROPCONTEXT)Object; PhFree(propContext->PropSheetPages); + PhDereferenceObject(propContext->Title); + PhDereferenceObject(propContext->StartPage); +} + +static HWND OptionsButton = NULL; +static HWND SecurityButton = NULL; +static WNDPROC OldOptionsWndProc = NULL; +static WNDPROC OldSecurityWndProc = NULL; + +INT_PTR CALLBACK PvpOptionsWndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND comboBoxHandle; + + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PvImageSmallIcon); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PvImageLargeIcon); + + comboBoxHandle = GetDlgItem(hwndDlg, IDC_MAXSIZEUNIT); + + PhCenterWindow(hwndDlg, GetParent(hwndDlg)); + + //PhSetDialogItemText(hwndDlg, IDC_DBGHELPSEARCHPATH, PhaGetStringSetting(L"DbgHelpSearchPath")->Buffer); + + for (ULONG i = 0; i < RTL_NUMBER_OF(PhSizeUnitNames); i++) + ComboBox_AddString(comboBoxHandle, PhSizeUnitNames[i]); + + if (PhMaxSizeUnit != ULONG_MAX) + ComboBox_SetCurSel(comboBoxHandle, PhMaxSizeUnit); + else + ComboBox_SetCurSel(comboBoxHandle, RTL_NUMBER_OF(PhSizeUnitNames) - 1); + + //PhInitializeWindowThemeEx(hwndDlg); + } + break; + case WM_COMMAND: + { + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + } + } + break; + } + + return FALSE; +} +LRESULT CALLBACK PvpButtonWndProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_COMMAND: + { + if (GET_WM_COMMAND_HWND(wParam, lParam) == OptionsButton) + { + DialogBox( + PhInstanceHandle, + MAKEINTRESOURCE(IDD_OPTIONS), + hwndDlg, + PvpOptionsWndProc + ); + } + else if (GET_WM_COMMAND_HWND(wParam, lParam) == SecurityButton) + { + PhEditSecurity( + hwndDlg, + PhGetString(PvFileName), + L"FileObject", + PhpOpenFileSecurity, + NULL, + NULL + ); + } + } + break; + } + + return CallWindowProc(OldOptionsWndProc, hwndDlg, uMsg, wParam, lParam); +} + +static HWND PvpCreateOptionsButton( + _In_ HWND hwndDlg +) +{ + if (!OptionsButton) + { + HWND optionsWindow; + RECT clientRect; + RECT rect; + + optionsWindow = hwndDlg; + OldOptionsWndProc = (WNDPROC)GetWindowLongPtr(optionsWindow, GWLP_WNDPROC); + SetWindowLongPtr(optionsWindow, GWLP_WNDPROC, (LONG_PTR)PvpButtonWndProc); + + // Create the Reset button. + GetClientRect(optionsWindow, &clientRect); + GetWindowRect(GetDlgItem(optionsWindow, IDCANCEL), &rect); + MapWindowPoints(NULL, optionsWindow, (POINT*)& rect, 2); + OptionsButton = CreateWindowEx( + WS_EX_NOPARENTNOTIFY, + WC_BUTTON, + L"Options", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_DISABLED, // TODO: Remove disabled flag + clientRect.right - rect.right, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + optionsWindow, + NULL, + PhInstanceHandle, + NULL + ); + SetWindowFont(OptionsButton, GetWindowFont(GetDlgItem(optionsWindow, IDCANCEL)), TRUE); + } + + return OptionsButton; +} + +static HWND PvpCreateSecurityButton( + _In_ HWND hwndDlg +) +{ + if (!SecurityButton) + { + HWND optionsWindow; + RECT clientRect; + RECT rect; + + optionsWindow = hwndDlg; + OldSecurityWndProc = (WNDPROC)GetWindowLongPtr(optionsWindow, GWLP_WNDPROC); + SetWindowLongPtr(optionsWindow, GWLP_WNDPROC, (LONG_PTR)PvpButtonWndProc); + + // Create the Reset button. + GetClientRect(optionsWindow, &clientRect); + GetWindowRect(OptionsButton, &rect); + MapWindowPoints(NULL, optionsWindow, (POINT*)& rect, 2); + + SecurityButton = CreateWindowEx( + WS_EX_NOPARENTNOTIFY, + WC_BUTTON, + L"Security", + WS_CHILD | WS_VISIBLE | WS_TABSTOP, + rect.right, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + optionsWindow, + NULL, + PhInstanceHandle, + NULL + ); + + SetWindowFont(SecurityButton, GetWindowFont(GetDlgItem(optionsWindow, IDCANCEL)), TRUE); + } + + return SecurityButton; } + + INT CALLBACK PvpPropSheetProc( _In_ HWND hwndDlg, _In_ UINT uMsg, @@ -127,7 +304,7 @@ INT CALLBACK PvpPropSheetProc( { if (lParam) { - if (((DLGTEMPLATEEX *)lParam)->signature == 0xffff) + if (((DLGTEMPLATEEX *)lParam)->signature == USHRT_MAX) { ((DLGTEMPLATEEX *)lParam)->style |= PROPSHEET_ADD_STYLE; } @@ -140,15 +317,21 @@ INT CALLBACK PvpPropSheetProc( break; case PSCB_INITIALIZED: { - PPV_PROPSHEETCONTEXT propSheetContext; + PPV_PROPSHEETCONTEXT context; + HICON smallIcon; + HICON largeIcon; - propSheetContext = PhAllocate(sizeof(PV_PROPSHEETCONTEXT)); - memset(propSheetContext, 0, sizeof(PV_PROPSHEETCONTEXT)); + PhGetStockApplicationIcon(&smallIcon, &largeIcon); - PhInitializeLayoutManager(&propSheetContext->LayoutManager, hwndDlg); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)smallIcon); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)largeIcon); - SetProp(hwndDlg, L"PvContext", (HANDLE)propSheetContext); - SetWindowSubclass(hwndDlg, PvpPropSheetWndProc, 0, (ULONG_PTR)propSheetContext); + context = PhAllocateZero(sizeof(PV_PROPSHEETCONTEXT)); + PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); + + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(hwndDlg, GWLP_WNDPROC); + PhSetWindowContext(hwndDlg, UCHAR_MAX, context); + SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)PvpPropSheetWndProc); if (MinimumSize.left == -1) { @@ -173,25 +356,54 @@ PPV_PROPSHEETCONTEXT PvpGetPropSheetContext( _In_ HWND hwnd ) { - return (PPV_PROPSHEETCONTEXT)GetProp(hwnd, L"PvContext"); + return PhGetWindowContext(hwnd, UCHAR_MAX); } LRESULT CALLBACK PvpPropSheetWndProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData + _In_ LPARAM lParam ) { - PPV_PROPSHEETCONTEXT propSheetContext = (PPV_PROPSHEETCONTEXT)dwRefData; + PPV_PROPSHEETCONTEXT propSheetContext; + WNDPROC oldWndProc; + + if (!(propSheetContext = PhGetWindowContext(hWnd, UCHAR_MAX))) + return 0; + + oldWndProc = propSheetContext->DefaultWindowProc; switch (uMsg) { + case WM_DESTROY: + { + HWND tabControl; + TCITEM tabItem; + WCHAR text[128]; + + // Save the window position and size. + + PhSaveWindowPlacementToSetting(L"MainWindowPosition", L"MainWindowSize", hWnd); + + // Save the selected tab. + + tabControl = PropSheet_GetTabControl(hWnd); + + tabItem.mask = TCIF_TEXT; + tabItem.pszText = text; + tabItem.cchTextMax = RTL_NUMBER_OF(text) - 1; + + if (TabCtrl_GetItem(tabControl, TabCtrl_GetCurSel(tabControl), &tabItem)) + { + PhSetStringSetting(L"MainWindowPage", text); + } + } + break; case WM_NCDESTROY: { - RemoveWindowSubclass(hWnd, PvpPropSheetWndProc, uIdSubclass); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, UCHAR_MAX); PhDeleteLayoutManager(&propSheetContext->LayoutManager); PhFree(propSheetContext); @@ -211,7 +423,7 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; case WM_SIZE: { - if (!IsIconic(hWnd)) + if (!IsMinimized(hWnd)) { PhLayoutManagerLayout(&propSheetContext->LayoutManager); } @@ -224,38 +436,36 @@ LRESULT CALLBACK PvpPropSheetWndProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } BOOLEAN PhpInitializePropSheetLayoutStage1( + _In_ PPV_PROPSHEETCONTEXT PropSheetContext, _In_ HWND hwnd ) { - PPV_PROPSHEETCONTEXT propSheetContext = PvpGetPropSheetContext(hwnd); - - if (!propSheetContext->LayoutInitialized) + if (!PropSheetContext->LayoutInitialized) { HWND tabControlHandle; PPH_LAYOUT_ITEM tabControlItem; PPH_LAYOUT_ITEM tabPageItem; tabControlHandle = PropSheet_GetTabControl(hwnd); - tabControlItem = PhAddLayoutItem(&propSheetContext->LayoutManager, tabControlHandle, - NULL, PH_ANCHOR_ALL | PH_LAYOUT_IMMEDIATE_RESIZE); - tabPageItem = PhAddLayoutItem(&propSheetContext->LayoutManager, tabControlHandle, - NULL, PH_LAYOUT_TAB_CONTROL); // dummy item to fix multiline tab control + tabControlItem = PhAddLayoutItem(&PropSheetContext->LayoutManager, tabControlHandle, NULL, PH_ANCHOR_ALL | PH_LAYOUT_IMMEDIATE_RESIZE); + tabPageItem = PhAddLayoutItem(&PropSheetContext->LayoutManager, tabControlHandle, NULL, PH_LAYOUT_TAB_CONTROL); // dummy item to fix multiline tab control - propSheetContext->TabPageItem = tabPageItem; + PropSheetContext->TabPageItem = tabPageItem; - PhAddLayoutItem(&propSheetContext->LayoutManager, GetDlgItem(hwnd, IDCANCEL), - NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&PropSheetContext->LayoutManager, GetDlgItem(hwnd, IDCANCEL), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&PropSheetContext->LayoutManager, PvpCreateOptionsButton(hwnd), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); + PhAddLayoutItem(&PropSheetContext->LayoutManager, PvpCreateSecurityButton(hwnd), NULL, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); // Hide the OK button. ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE); // Set the Cancel button's text to "Close". - SetDlgItemText(hwnd, IDCANCEL, L"Close"); + PhSetDialogItemText(hwnd, IDCANCEL, L"Close"); - propSheetContext->LayoutInitialized = TRUE; + PropSheetContext->LayoutInitialized = TRUE; return TRUE; } @@ -263,6 +473,32 @@ BOOLEAN PhpInitializePropSheetLayoutStage1( return FALSE; } +VOID PhpInitializePropSheetLayoutStage2( + _In_ HWND hwnd + ) +{ + PH_RECTANGLE windowRectangle; + + windowRectangle.Position = PhGetIntegerPairSetting(L"MainWindowPosition"); + windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MainWindowSize", TRUE).Pair; + + if (windowRectangle.Size.X < MinimumSize.right) + windowRectangle.Size.X = MinimumSize.right; + if (windowRectangle.Size.Y < MinimumSize.bottom) + windowRectangle.Size.Y = MinimumSize.bottom; + + PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); + + MoveWindow(hwnd, windowRectangle.Left, windowRectangle.Top, + windowRectangle.Width, windowRectangle.Height, FALSE); + + // Implement cascading by saving an offsetted rectangle. + windowRectangle.Left += 20; + windowRectangle.Top += 20; + + PhSetIntegerPairSetting(L"MainWindowPosition", windowRectangle.Position); +} + BOOLEAN PvAddPropPage( _Inout_ PPV_PROPCONTEXT PropContext, _In_ _Assume_refs_(1) PPV_PROPPAGECONTEXT PropPageContext @@ -283,8 +519,7 @@ BOOLEAN PvAddPropPage( PropPageContext->PropContext = PropContext; PhReferenceObject(PropContext); - PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = - propSheetPageHandle; + PropContext->PropSheetPages[PropContext->PropSheetHeader.nPages] = propSheetPageHandle; PropContext->PropSheetHeader.nPages++; return TRUE; @@ -326,8 +561,7 @@ PPV_PROPPAGECONTEXT PvCreatePropPageContextEx( memset(propPageContext, 0, sizeof(PV_PROPPAGECONTEXT)); propPageContext->PropSheetPage.dwSize = sizeof(PROPSHEETPAGE); - propPageContext->PropSheetPage.dwFlags = - PSP_USECALLBACK; + propPageContext->PropSheetPage.dwFlags = PSP_USECALLBACK; propPageContext->PropSheetPage.hInstance = InstanceHandle; propPageContext->PropSheetPage.pszTemplate = Template; propPageContext->PropSheetPage.pfnDlgProc = DlgProc; @@ -379,13 +613,14 @@ PPH_LAYOUT_ITEM PvAddPropPageLayoutItem( PPV_PROPSHEETCONTEXT propSheetContext; PPH_LAYOUT_MANAGER layoutManager; PPH_LAYOUT_ITEM realParentItem; + BOOLEAN doLayoutStage2; PPH_LAYOUT_ITEM item; parent = GetParent(hwnd); propSheetContext = PvpGetPropSheetContext(parent); layoutManager = &propSheetContext->LayoutManager; - PhpInitializePropSheetLayoutStage1(parent); + doLayoutStage2 = PhpInitializePropSheetLayoutStage1(propSheetContext, parent); if (ParentItem != PH_PROP_PAGE_TAB_CONTROL_PARENT) realParentItem = ParentItem; @@ -425,6 +660,9 @@ PPH_LAYOUT_ITEM PvAddPropPageLayoutItem( item = PhAddLayoutItem(layoutManager, Handle, realParentItem, Anchor); } + if (doLayoutStage2) + PhpInitializePropSheetLayoutStage2(parent); + return item; } @@ -438,4 +676,4 @@ VOID PvDoPropPageLayout( parent = GetParent(hwnd); propSheetContext = PvpGetPropSheetContext(parent); PhLayoutManagerLayout(&propSheetContext->LayoutManager); -} \ No newline at end of file +} diff --git a/tools/peview/resource.h b/tools/peview/resource.h index b300f449de11..fb023fa24282 100644 --- a/tools/peview/resource.h +++ b/tools/peview/resource.h @@ -1,40 +1,65 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by peview.rc -// -#define IDD_PEGENERAL 102 -#define IDD_PEIMPORTS 103 -#define IDD_PEEXPORTS 104 -#define IDD_LIBEXPORTS 105 -#define IDD_PECLR 106 -#define IDD_PELOADCONFIG 107 -#define IDD_PECFG 108 -#define IDC_TARGETMACHINE 1003 -#define IDC_CHECKSUM 1004 -#define IDC_SUBSYSTEM 1005 -#define IDC_SUBSYSTEMVERSION 1006 -#define IDC_CHARACTERISTICS 1007 -#define IDC_LIST 1008 -#define IDC_FILEICON 1009 -#define IDC_TIMESTAMP 1010 -#define IDC_RUNTIMEVERSION 1011 -#define IDC_FILE 1011 -#define IDC_FLAGS 1012 -#define IDC_COMPANYNAME 1012 -#define IDC_VERSION 1013 -#define IDC_VERSIONSTRING 1014 -#define IDC_IMAGEBASE 1015 -#define IDC_ENTRYPOINT 1016 -#define IDC_NAME 1044 -#define IDC_COMPANYNAME_LINK 1279 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 110 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1017 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by peview.rc +// +#define IDD_PEGENERAL 101 +#define IDD_PEIMPORTS 102 +#define IDD_PEEXPORTS 103 +#define IDD_LIBEXPORTS 104 +#define IDD_PECLR 105 +#define IDD_PELOADCONFIG 106 +#define IDD_PECFG 107 +#define IDD_PESYMBOLS 108 +#define IDD_PERESOURCES 109 +#define IDB_SEARCH_ACTIVE 110 +#define IDD_ELFGENERAL 110 +#define IDB_SEARCH_INACTIVE 111 +#define IDD_PEPROPSTORAGE 111 +#define IDB_SEARCH_ACTIVE_BMP 112 +#define IDD_PEATTR 112 +#define IDB_SEARCH_INACTIVE_BMP 113 +#define IDD_PESTREAMS 113 +#define IDD_PELINKS 114 +#define IDD_PIDS 115 +#define IDD_ELFDYNAMIC 116 +#define IDD_TLS 117 +#define IDC_SYMBOLTREE 119 +#define IDI_APPICON 122 +#define IDD_OPTIONS 126 +#define IDC_TARGETMACHINE 1003 +#define IDC_CHECKSUM 1004 +#define IDC_SUBSYSTEM 1005 +#define IDC_SUBSYSTEMVERSION 1006 +#define IDC_CHARACTERISTICS 1007 +#define IDC_LIST 1008 +#define IDC_FILEICON 1009 +#define IDC_TIMESTAMP 1010 +#define IDC_RUNTIMEVERSION 1011 +#define IDC_FILE 1011 +#define IDC_FLAGS 1012 +#define IDC_COMPANYNAME 1012 +#define IDC_VERSION 1013 +#define IDC_VERSIONSTRING 1014 +#define IDC_IMAGEBASE 1015 +#define IDC_MVIDSTRING 1015 +#define IDC_ENTRYPOINT 1016 +#define IDC_SYMSEARCH 1017 +#define IDC_IMAGETYPE 1017 +#define IDC_NAME 1019 +#define IDC_COMPANYNAME_LINK 1020 +#define IDC_FONT 1079 +#define IDC_SEARCHENGINE 1143 +#define IDC_MAXSIZEUNIT 1144 +#define IDC_DBGHELPSEARCHPATH 1218 +#define IDC_SETTINGS 1399 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 123 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1023 +#define _APS_NEXT_SYMED_VALUE 115 +#endif +#endif diff --git a/tools/CustomBuildTool/CustomBuildTool/resources/ProcessHacker.ico b/tools/peview/resources/ProcessHacker.ico similarity index 100% rename from tools/CustomBuildTool/CustomBuildTool/resources/ProcessHacker.ico rename to tools/peview/resources/ProcessHacker.ico diff --git a/tools/peview/resources/active_search.bmp b/tools/peview/resources/active_search.bmp new file mode 100644 index 000000000000..61b13de9a115 Binary files /dev/null and b/tools/peview/resources/active_search.bmp differ diff --git a/tools/peview/resources/active_search.png b/tools/peview/resources/active_search.png new file mode 100644 index 000000000000..1cf29576b0b5 Binary files /dev/null and b/tools/peview/resources/active_search.png differ diff --git a/tools/peview/resources/application.ico b/tools/peview/resources/application.ico new file mode 100644 index 000000000000..c31fc6534c64 Binary files /dev/null and b/tools/peview/resources/application.ico differ diff --git a/tools/peview/resources/inactive_search.bmp b/tools/peview/resources/inactive_search.bmp new file mode 100644 index 000000000000..cf19cb8ca3f0 Binary files /dev/null and b/tools/peview/resources/inactive_search.bmp differ diff --git a/tools/peview/resources/inactive_search.png b/tools/peview/resources/inactive_search.png new file mode 100644 index 000000000000..29eaa91305b3 Binary files /dev/null and b/tools/peview/resources/inactive_search.png differ diff --git a/tools/peview/resprp.c b/tools/peview/resprp.c new file mode 100644 index 000000000000..e82d22a8b6d3 --- /dev/null +++ b/tools/peview/resprp.c @@ -0,0 +1,251 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2017-2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +static PWSTR PvpGetResourceTypeString( + _In_ ULONG_PTR Type + ) +{ + switch (Type) + { + case RT_CURSOR: + return L"RT_CURSOR"; + case RT_BITMAP: + return L"RT_BITMAP"; + case RT_ICON: + return L"RT_ICON"; + case RT_MENU: + return L"RT_MENU"; + case RT_DIALOG: + return L"RT_DIALOG"; + case RT_STRING: + return L"RT_STRING"; + case RT_FONTDIR: + return L"RT_FONTDIR"; + case RT_FONT: + return L"RT_FONT"; + case RT_ACCELERATOR: + return L"RT_ACCELERATOR"; + case RT_RCDATA: + return L"RT_RCDATA"; + case RT_MESSAGETABLE: + return L"RT_MESSAGETABLE"; + case RT_GROUP_CURSOR: + return L"RT_GROUP_CURSOR"; + case RT_GROUP_ICON: + return L"RT_GROUP_ICON"; + case RT_VERSION: + return L"RT_VERSION"; + case RT_ANICURSOR: + return L"RT_ANICURSOR"; + case RT_MANIFEST: + return L"RT_MANIFEST"; + } + + // TODO: Some binaries include undocumented resource types. + return PhaFormatString(L"%lu", Type)->Buffer; +} + +typedef enum _PVE_RESOURCES_COLUMN_INDEX +{ + PVE_RESOURCES_COLUMN_INDEX_COUNT, + PVE_RESOURCES_COLUMN_INDEX_TYPE, + PVE_RESOURCES_COLUMN_INDEX_NAME, + PVE_RESOURCES_COLUMN_INDEX_SIZE, + PVE_RESOURCES_COLUMN_INDEX_LCID, + PVE_RESOURCES_COLUMN_INDEX_HASH +} PVE_RESOURCES_COLUMN_INDEX; + +INT_PTR CALLBACK PvpPeResourcesDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + PH_MAPPED_IMAGE_RESOURCES resources; + PH_IMAGE_RESOURCE_ENTRY entry; + ULONG count = 0; + ULONG i; + INT lvItemIndex; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"Type"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Name"); + PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 100, L"Size"); + PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 100, L"Language"); + //PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 100, L"Hash"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageResourcesListViewColumns", lvHandle); + + if (NT_SUCCESS(PhGetMappedImageResources(&resources, &PvMappedImage))) + { + for (i = 0; i < resources.NumberOfEntries; i++) + { + PPH_STRING string; + WCHAR number[PH_INT32_STR_LEN_1]; + + entry = resources.ResourceEntries[i]; + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, number, NULL); + + if (IS_INTRESOURCE(entry.Type)) + { + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_TYPE, PvpGetResourceTypeString(entry.Type)); + } + else + { + PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Type; + + string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_TYPE, string->Buffer); + PhDereferenceObject(string); + } + + if (IS_INTRESOURCE(entry.Name)) + { + PhPrintUInt32(number, (ULONG)entry.Name); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_NAME, number); + } + else + { + PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Name; + + string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_NAME, string->Buffer); + PhDereferenceObject(string); + } + + if (IS_INTRESOURCE(entry.Language)) + { + NTSTATUS status; + UNICODE_STRING localeNameUs; + WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; + + RtlInitEmptyUnicodeString(&localeNameUs, localeName, sizeof(localeName)); + + // Note: Win32 defaults to the current user locale when zero is specified (e.g. LCIDToLocaleName). + if ((ULONG)entry.Language) + status = RtlLcidToLocaleName((ULONG)entry.Language, &localeNameUs, 0, FALSE); + else + status = RtlLcidToLocaleName(PhGetUserDefaultLCID(), &localeNameUs, 0, FALSE); + + if (NT_SUCCESS(status)) + { + PhPrintUInt32(number, (ULONG)entry.Language); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, PhaFormatString(L"%s (%s)", number, localeName)->Buffer); + } + else + { + PhPrintUInt32(number, (ULONG)entry.Language); + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, number); + } + } + else + { + PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U)entry.Language; + + string = PhCreateStringEx(resourceString->NameString, resourceString->Length * sizeof(WCHAR)); + + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_LCID, string->Buffer); + PhDereferenceObject(string); + } + + PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_SIZE, PhaFormatSize(entry.Size, ULONG_MAX)->Buffer); + + // dmex: This crashes when enumerating resources of VM protected binaries. + //if (entry.Data && entry.Size) + //{ + // PH_HASH_CONTEXT hashContext; + // PPH_STRING hashString; + // UCHAR hash[32]; + // + // PhInitializeHash(&hashContext, PhGetIntegerSetting(L"HashAlgorithm")); + // PhUpdateHash(&hashContext, entry.Data, entry.Size); + // PhFinalHash(&hashContext, hash, 16, NULL); + // + // hashString = PhBufferToHexString(hash, 16); + // PhSetListViewSubItem(lvHandle, lvItemIndex, PVE_RESOURCES_COLUMN_INDEX_HASH, hashString->Buffer); + // PhDereferenceObject(hashString); + //} + } + + PhFree(resources.ResourceEntries); + } + + ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageResourcesListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/plugins/CommonUtil/searchbox.c b/tools/peview/searchbox.c similarity index 53% rename from plugins/CommonUtil/searchbox.c rename to tools/peview/searchbox.c index b79ea2096480..87dbabafc6d3 100644 --- a/plugins/CommonUtil/searchbox.c +++ b/tools/peview/searchbox.c @@ -1,8 +1,8 @@ /* - * Process Hacker CommonUtil Plugin - * Subclassed Edit control + * Process Hacker - + * Searchbox control * - * Copyright (C) 2012-2016 dmex + * Copyright (C) 2012-2017 dmex * * This file is part of Process Hacker. * @@ -21,38 +21,96 @@ * */ -#include "main.h" +#include +#include #include #include -#include +#include -VOID NcAreaFreeTheme( +typedef struct _EDIT_CONTEXT +{ + union + { + ULONG Flags; + struct + { + ULONG Hot : 1; + ULONG Pushed : 1; + ULONG Spare : 30; + }; + }; + + LONG CXWidth; + INT CXBorder; + INT ImageWidth; + INT ImageHeight; + HWND WindowHandle; + HFONT WindowFont; + HICON BitmapActive; + HICON BitmapInactive; + HBRUSH BrushNormal; + HBRUSH BrushPushed; + HBRUSH BrushHot; + WNDPROC DefaultWindowProc; +} EDIT_CONTEXT, *PEDIT_CONTEXT; + +HICON PhpSearchBitmapToIcon( + _In_ HBITMAP BitmapHandle, + _In_ INT Width, + _In_ INT Height + ); + +VOID PhpSearchFreeTheme( _Inout_ PEDIT_CONTEXT Context ) { if (Context->BrushNormal) - DeleteObject(Context->BrushNormal); + DeleteBrush(Context->BrushNormal); if (Context->BrushHot) - DeleteObject(Context->BrushHot); + DeleteBrush(Context->BrushHot); if (Context->BrushPushed) - DeleteObject(Context->BrushPushed); + DeleteBrush(Context->BrushPushed); } -VOID NcAreaInitializeFont( +VOID PhpSearchInitializeFont( _Inout_ PEDIT_CONTEXT Context ) { - if (Context->WindowFont) DeleteObject(Context->WindowFont); - Context->WindowFont = CommonCreateFont(10, FW_MEDIUM, Context->WindowHandle); + LOGFONT logFont; + + if (Context->WindowFont) + DeleteFont(Context->WindowFont); + + if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) + return; + + Context->WindowFont = CreateFont( + -PhMultiplyDivideSigned(10, PhGlobalDpi, 72), + 0, + 0, + 0, + FW_MEDIUM, + FALSE, + FALSE, + FALSE, + ANSI_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY | ANTIALIASED_QUALITY, + DEFAULT_PITCH, + logFont.lfFaceName + ); + + SetWindowFont(Context->WindowHandle, Context->WindowFont, TRUE); } -VOID NcAreaInitializeTheme( +VOID PhpSearchInitializeTheme( _Inout_ PEDIT_CONTEXT Context -) + ) { - Context->CXWidth = PhMultiplyDivide(20, PhGlobalDpi, 96); + Context->CXWidth = PhMultiplyDivideSigned(20, PhGlobalDpi, 96);// PH_SCALE_DPI(20); Context->BrushNormal = GetSysColorBrush(COLOR_WINDOW); Context->BrushHot = CreateSolidBrush(RGB(205, 232, 255)); Context->BrushPushed = CreateSolidBrush(RGB(153, 209, 255)); @@ -89,42 +147,195 @@ VOID NcAreaInitializeTheme( } } -VOID NcAreaInitializeImages( +HBITMAP PhLoadPngImageFromResource( + _In_ PVOID DllBase, + _In_ UINT Width, + _In_ UINT Height, + _In_ PCWSTR Name, + _In_ BOOLEAN RGBAImage + ) +{ + BOOLEAN success = FALSE; + UINT frameCount = 0; + ULONG resourceLength = 0; + WICInProcPointer resourceBuffer = NULL; + HDC screenHdc = NULL; + HDC bufferDc = NULL; + BITMAPINFO bitmapInfo = { 0 }; + HBITMAP bitmapHandle = NULL; + PVOID bitmapBuffer = NULL; + IWICStream* wicStream = NULL; + IWICBitmapSource* wicBitmapSource = NULL; + IWICBitmapDecoder* wicDecoder = NULL; + IWICBitmapFrameDecode* wicFrame = NULL; + IWICImagingFactory* wicFactory = NULL; + IWICBitmapScaler* wicScaler = NULL; + WICPixelFormatGUID pixelFormat; + WICRect rect = { 0, 0, Width, Height }; + + // Create the ImagingFactory + if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, &wicFactory))) + goto CleanupExit; + + // Load the resource + if (!PhLoadResource(DllBase, Name, L"PNG", &resourceLength, &resourceBuffer)) + goto CleanupExit; + + // Create the Stream + if (FAILED(IWICImagingFactory_CreateStream(wicFactory, &wicStream))) + goto CleanupExit; + + // Initialize the Stream from Memory + if (FAILED(IWICStream_InitializeFromMemory(wicStream, resourceBuffer, resourceLength))) + goto CleanupExit; + + if (FAILED(IWICImagingFactory_CreateDecoder(wicFactory, &GUID_ContainerFormatPng, NULL, &wicDecoder))) + goto CleanupExit; + + if (FAILED(IWICBitmapDecoder_Initialize(wicDecoder, (IStream*)wicStream, WICDecodeMetadataCacheOnLoad))) + goto CleanupExit; + + // Get the Frame count + if (FAILED(IWICBitmapDecoder_GetFrameCount(wicDecoder, &frameCount)) || frameCount < 1) + goto CleanupExit; + + // Get the Frame + if (FAILED(IWICBitmapDecoder_GetFrame(wicDecoder, 0, &wicFrame))) + goto CleanupExit; + + // Get the WicFrame image format + if (FAILED(IWICBitmapFrameDecode_GetPixelFormat(wicFrame, &pixelFormat))) + goto CleanupExit; + + // Check if the image format is supported: + if (IsEqualGUID(&pixelFormat, RGBAImage ? &GUID_WICPixelFormat32bppPRGBA : &GUID_WICPixelFormat32bppPBGRA)) + { + wicBitmapSource = (IWICBitmapSource*)wicFrame; + } + else + { + IWICFormatConverter* wicFormatConverter = NULL; + + if (FAILED(IWICImagingFactory_CreateFormatConverter(wicFactory, &wicFormatConverter))) + goto CleanupExit; + + if (FAILED(IWICFormatConverter_Initialize( + wicFormatConverter, + (IWICBitmapSource*)wicFrame, + RGBAImage ? &GUID_WICPixelFormat32bppPRGBA : &GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, + NULL, + 0.0, + WICBitmapPaletteTypeCustom + ))) + { + IWICFormatConverter_Release(wicFormatConverter); + goto CleanupExit; + } + + // Convert the image to the correct format: + IWICFormatConverter_QueryInterface(wicFormatConverter, &IID_IWICBitmapSource, &wicBitmapSource); + + // Cleanup the converter. + IWICFormatConverter_Release(wicFormatConverter); + + // Dispose the old frame now that the converted frame is in wicBitmapSource. + IWICBitmapFrameDecode_Release(wicFrame); + } + + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = rect.Width; + bitmapInfo.bmiHeader.biHeight = -((LONG)rect.Height); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + screenHdc = GetDC(NULL); + bufferDc = CreateCompatibleDC(screenHdc); + bitmapHandle = CreateDIBSection(screenHdc, &bitmapInfo, DIB_RGB_COLORS, &bitmapBuffer, NULL, 0); + + // Check if it's the same rect as the requested size. + //if (width != rect.Width || height != rect.Height) + if (FAILED(IWICImagingFactory_CreateBitmapScaler(wicFactory, &wicScaler))) + goto CleanupExit; + if (FAILED(IWICBitmapScaler_Initialize(wicScaler, wicBitmapSource, rect.Width, rect.Height, WICBitmapInterpolationModeFant))) + goto CleanupExit; + if (FAILED(IWICBitmapScaler_CopyPixels(wicScaler, &rect, rect.Width * 4, rect.Width * rect.Height * 4, (PBYTE)bitmapBuffer))) + goto CleanupExit; + + success = TRUE; + +CleanupExit: + + if (wicScaler) + IWICBitmapScaler_Release(wicScaler); + + if (bufferDc) + DeleteDC(bufferDc); + + if (screenHdc) + ReleaseDC(NULL, screenHdc); + + if (wicBitmapSource) + IWICBitmapSource_Release(wicBitmapSource); + + if (wicStream) + IWICStream_Release(wicStream); + + if (wicDecoder) + IWICBitmapDecoder_Release(wicDecoder); + + if (wicFactory) + IWICImagingFactory_Release(wicFactory); + + if (resourceBuffer) + PhFree(resourceBuffer); + + if (success) + { + return bitmapHandle; + } + + DeleteBitmap(bitmapHandle); + return NULL; +} + +VOID PhpSearchInitializeImages( _Inout_ PEDIT_CONTEXT Context -) + ) { HBITMAP bitmap; Context->ImageWidth = GetSystemMetrics(SM_CXSMICON) + 4; Context->ImageHeight = GetSystemMetrics(SM_CYSMICON) + 4; - if (bitmap = LoadImageFromResources(PluginInstance->DllBase, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE), TRUE)) + if (bitmap = PhLoadPngImageFromResource(PhInstanceHandle, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE), TRUE)) { - Context->BitmapActive = BitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); - DeleteObject(bitmap); + Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteBitmap(bitmap); } - else if (bitmap = LoadImage(PluginInstance->DllBase, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE_BMP), IMAGE_BITMAP, 0, 0, 0)) + else if (bitmap = LoadImage(PhInstanceHandle, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE_BMP), IMAGE_BITMAP, 0, 0, 0)) { - Context->BitmapActive = BitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); - DeleteObject(bitmap); + Context->BitmapActive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteBitmap(bitmap); } - if (bitmap = LoadImageFromResources(PluginInstance->DllBase, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE), TRUE)) + if (bitmap = PhLoadPngImageFromResource(PhInstanceHandle, Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE), TRUE)) { - Context->BitmapInactive = BitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); - DeleteObject(bitmap); + Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteBitmap(bitmap); } - else if (bitmap = LoadImage(PluginInstance->DllBase, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE_BMP), IMAGE_BITMAP, 0, 0, 0)) + else if (bitmap = LoadImage(PhInstanceHandle, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE_BMP), IMAGE_BITMAP, 0, 0, 0)) { - Context->BitmapInactive = BitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); - DeleteObject(bitmap); + Context->BitmapInactive = PhpSearchBitmapToIcon(bitmap, Context->ImageWidth, Context->ImageHeight); + DeleteBitmap(bitmap); } } -VOID NcAreaGetButtonRect( +VOID PhpSearchGetButtonRect( _Inout_ PEDIT_CONTEXT Context, _Inout_ PRECT ButtonRect -) + ) { ButtonRect->left = (ButtonRect->right - Context->CXWidth) - Context->CXBorder - 1; // offset left border by 1 ButtonRect->bottom -= Context->CXBorder; @@ -132,7 +343,7 @@ VOID NcAreaGetButtonRect( ButtonRect->top += Context->CXBorder; } -VOID NcAreaDrawButton( +VOID PhpSearchDrawButton( _Inout_ PEDIT_CONTEXT Context, _In_ RECT ButtonRect ) @@ -153,7 +364,7 @@ VOID NcAreaDrawButton( bufferDc = CreateCompatibleDC(hdc); bufferBitmap = CreateCompatibleBitmap(hdc, bufferRect.right, bufferRect.bottom); - oldBufferBitmap = SelectObject(bufferDc, bufferBitmap); + oldBufferBitmap = SelectBitmap(bufferDc, bufferBitmap); if (Context->Pushed) { @@ -200,37 +411,39 @@ VOID NcAreaDrawButton( } BitBlt(hdc, ButtonRect.left, ButtonRect.top, ButtonRect.right, ButtonRect.bottom, bufferDc, 0, 0, SRCCOPY); - SelectObject(bufferDc, oldBufferBitmap); - DeleteObject(bufferBitmap); + SelectBitmap(bufferDc, oldBufferBitmap); + DeleteBitmap(bufferBitmap); DeleteDC(bufferDc); ReleaseDC(Context->WindowHandle, hdc); } -LRESULT CALLBACK NcAreaWndSubclassProc( +LRESULT CALLBACK PhpSearchWndSubclassProc( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, - _In_ LPARAM lParam, - _In_ UINT_PTR uIdSubclass, - _In_ ULONG_PTR dwRefData -) + _In_ LPARAM lParam + ) { PEDIT_CONTEXT context; + WNDPROC oldWndProc; + + if (!(context = PhGetWindowContext(hWnd, SHRT_MAX))) + return 0; - context = (PEDIT_CONTEXT)GetProp(hWnd, L"SearchBoxContext"); + oldWndProc = context->DefaultWindowProc; switch (uMsg) { case WM_NCDESTROY: { - NcAreaFreeTheme(context); + PhpSearchFreeTheme(context); if (context->WindowFont) - DeleteObject(context->WindowFont); + DeleteFont(context->WindowFont); - RemoveWindowSubclass(hWnd, NcAreaWndSubclassProc, uIdSubclass); - RemoveProp(hWnd, L"SearchBoxContext"); + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + PhRemoveWindowContext(hWnd, SHRT_MAX); PhFree(context); } break; @@ -261,10 +474,10 @@ LRESULT CALLBACK NcAreaWndSubclassProc( OffsetRect(&windowRect, -windowRect.left, -windowRect.top); // Get the position of the inserted button. - NcAreaGetButtonRect(context, &windowRect); + PhpSearchGetButtonRect(context, &windowRect); // Draw the button. - NcAreaDrawButton(context, windowRect); + PhpSearchDrawButton(context, windowRect); } return 0; case WM_NCHITTEST: @@ -280,7 +493,7 @@ LRESULT CALLBACK NcAreaWndSubclassProc( GetWindowRect(hWnd, &windowRect); // Get the position of the inserted button. - NcAreaGetButtonRect(context, &windowRect); + PhpSearchGetButtonRect(context, &windowRect); // Check that the mouse is within the inserted button. if (PtInRect(&windowRect, windowPoint)) @@ -302,7 +515,7 @@ LRESULT CALLBACK NcAreaWndSubclassProc( GetWindowRect(hWnd, &windowRect); // Get the position of the inserted button. - NcAreaGetButtonRect(context, &windowRect); + PhpSearchGetButtonRect(context, &windowRect); // Check that the mouse is within the inserted button. if (PtInRect(&windowRect, windowPoint)) @@ -328,7 +541,7 @@ LRESULT CALLBACK NcAreaWndSubclassProc( GetWindowRect(hWnd, &windowRect); // Get the position of the inserted button. - NcAreaGetButtonRect(context, &windowRect); + PhpSearchGetButtonRect(context, &windowRect); // Check that the mouse is within the inserted button. if (PtInRect(&windowRect, windowPoint)) @@ -362,9 +575,9 @@ LRESULT CALLBACK NcAreaWndSubclassProc( case WM_SYSCOLORCHANGE: case WM_THEMECHANGED: { - NcAreaFreeTheme(context); - NcAreaInitializeTheme(context); - NcAreaInitializeFont(context); + PhpSearchFreeTheme(context); + PhpSearchInitializeTheme(context); + PhpSearchInitializeFont(context); // Reset the client area margins. SendMessage(hWnd, EM_SETMARGINS, EC_LEFTMARGIN, MAKELPARAM(0, 0)); @@ -389,7 +602,7 @@ LRESULT CALLBACK NcAreaWndSubclassProc( GetWindowRect(hWnd, &windowRect); // Get the position of the inserted button. - NcAreaGetButtonRect(context, &windowRect); + PhpSearchGetButtonRect(context, &windowRect); // Check that the mouse is within the inserted button. if (PtInRect(&windowRect, windowPoint) && !context->Hot) @@ -433,7 +646,7 @@ LRESULT CALLBACK NcAreaWndSubclassProc( GetWindowRect(hWnd, &windowRect); // Get the position of the inserted button. - NcAreaGetButtonRect(context, &windowRect); + PhpSearchGetButtonRect(context, &windowRect); // Check that the mouse is within the inserted button. context->Pushed = PtInRect(&windowRect, windowPoint); @@ -444,10 +657,10 @@ LRESULT CALLBACK NcAreaWndSubclassProc( break; } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } -HICON BitmapToIcon( +HICON PhpSearchBitmapToIcon( _In_ HBITMAP BitmapHandle, _In_ INT Width, _In_ INT Height @@ -458,7 +671,7 @@ HICON BitmapToIcon( HBITMAP screenBitmap; ICONINFO iconInfo = { 0 }; - screenDc = CreateIC(L"DISPLAY", NULL, NULL, NULL); + screenDc = GetDC(NULL); screenBitmap = CreateCompatibleBitmap(screenDc, Width, Height); iconInfo.fIcon = TRUE; @@ -467,37 +680,34 @@ HICON BitmapToIcon( icon = CreateIconIndirect(&iconInfo); - DeleteObject(screenBitmap); - DeleteDC(screenDc); + DeleteBitmap(screenBitmap); + ReleaseDC(NULL, screenDc); return icon; } -VOID UtilCreateSearchControl( - _In_ HWND Parent, +VOID PvCreateSearchControl( _In_ HWND WindowHandle, - _In_ PWSTR BannerText + _In_opt_ PWSTR BannerText ) { PEDIT_CONTEXT context; - context = (PEDIT_CONTEXT)PhAllocate(sizeof(EDIT_CONTEXT)); - memset(context, 0, sizeof(EDIT_CONTEXT)); - + context = PhAllocateZero(sizeof(EDIT_CONTEXT)); context->WindowHandle = WindowHandle; - //NcAreaInitializeTheme(context); - NcAreaInitializeImages(context); + //PhpSearchInitializeTheme(context); + PhpSearchInitializeImages(context); // Set initial text - Edit_SetCueBannerText(context->WindowHandle, BannerText); - - // Set our window context data. - SetProp(context->WindowHandle, L"SearchBoxContext", (HANDLE)context); + if (BannerText) + Edit_SetCueBannerText(context->WindowHandle, BannerText); // Subclass the Edit control window procedure. - SetWindowSubclass(context->WindowHandle, NcAreaWndSubclassProc, 0, (ULONG_PTR)context); + context->DefaultWindowProc = (WNDPROC)GetWindowLongPtr(WindowHandle, GWLP_WNDPROC); + PhSetWindowContext(WindowHandle, SHRT_MAX, context); + SetWindowLongPtr(WindowHandle, GWLP_WNDPROC, (LONG_PTR)PhpSearchWndSubclassProc); // Initialize the theme parameters. SendMessage(context->WindowHandle, WM_THEMECHANGED, 0, 0); -} \ No newline at end of file +} diff --git a/tools/peview/settings.c b/tools/peview/settings.c new file mode 100644 index 000000000000..0033333ceb14 --- /dev/null +++ b/tools/peview/settings.c @@ -0,0 +1,166 @@ +/* + * PE viewer - + * program settings + * + * Copyright (C) 2017 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include +#include + +static PPH_STRING PeSettingsFileName = NULL; + +VOID PhAddDefaultSettings( + VOID + ) +{ + PhpAddIntegerSetting(L"FirstRun", L"1"); + PhpAddStringSetting(L"Font", L""); // null + PhpAddStringSetting(L"DbgHelpSearchPath", L"SRV*C:\\Symbols*https://msdl.microsoft.com/download/symbols"); + PhpAddIntegerSetting(L"EnableSecurityAdvancedDialog", L"1"); + PhpAddIntegerSetting(L"EnableThemeSupport", L"0"); + PhpAddIntegerSetting(L"GraphColorMode", L"1"); + PhpAddIntegerSetting(L"HashAlgorithm", L"0"); + PhpAddIntegerSetting(L"MaxSizeUnit", L"6"); + PhpAddStringSetting(L"MainWindowPage", L"General"); + PhpAddIntegerPairSetting(L"MainWindowPosition", L"150,150"); + PhpAddScalableIntegerPairSetting(L"MainWindowSize", L"@96|550,580"); + PhpAddStringSetting(L"ImageGeneralListViewColumns", L""); + PhpAddStringSetting(L"ImageLoadCfgListViewColumns", L""); + PhpAddStringSetting(L"ImageExportsListViewColumns", L""); + PhpAddStringSetting(L"ImageImportsListViewColumns", L""); + PhpAddStringSetting(L"ImageCfgListViewColumns", L""); + PhpAddStringSetting(L"ImageResourcesListViewColumns", L""); + PhpAddStringSetting(L"ImageAttributesListViewColumns", L""); + PhpAddStringSetting(L"ImagePropertiesListViewColumns", L""); + PhpAddStringSetting(L"ImageStreamsListViewColumns", L""); + PhpAddStringSetting(L"ImageHardLinksListViewColumns", L""); + PhpAddStringSetting(L"ImagePidsListViewColumns", L""); + PhpAddStringSetting(L"ImageTlsListViewColumns", L""); + PhpAddStringSetting(L"LibListViewColumns", L""); + PhpAddStringSetting(L"PdbTreeListColumns", L""); + + PhpAddIntegerSetting(L"TreeListBorderEnable", L"0"); + + // Wsl properties + PhpAddStringSetting(L"GeneralWslTreeListColumns", L""); + PhpAddStringSetting(L"DynamicWslListViewColumns", L""); + PhpAddStringSetting(L"ImportsWslListViewColumns", L""); + PhpAddStringSetting(L"ExportsWslListViewColumns", L""); +} + +VOID PhUpdateCachedSettings( + VOID + ) +{ + NOTHING; +} + +VOID PeInitializeSettings( + VOID + ) +{ + static PH_STRINGREF settingsPath = PH_STRINGREF_INIT(L"%APPDATA%\\Process Hacker\\peview.xml"); + static PH_STRINGREF settingsSuffix = PH_STRINGREF_INIT(L".peview.xml"); + NTSTATUS status; + PPH_STRING appFileName; + PPH_STRING tempFileName; + + // There are three possible locations for the settings file: + // 1. A file named peview.exe.peview.xml in the program directory. (This changes + // based on the executable file name.) + // 2. The default location. + + // 1. File in program directory + + appFileName = PhGetApplicationFileName(); + tempFileName = PhConcatStringRef2(&appFileName->sr, &settingsSuffix); + PhDereferenceObject(appFileName); + + if (RtlDoesFileExists_U(tempFileName->Buffer)) + { + PeSettingsFileName = tempFileName; + } + else + { + PhDereferenceObject(tempFileName); + } + + // 2. Default location + if (!PeSettingsFileName) + { + PeSettingsFileName = PhExpandEnvironmentStrings(&settingsPath); + } + + if (PeSettingsFileName) + { + status = PhLoadSettings(PeSettingsFileName->Buffer); + + // If we didn't find the file, it will be created. Otherwise, + // there was probably a parsing error and we don't want to + // change anything. + if (status == STATUS_FILE_CORRUPT_ERROR) + { + if (PhShowMessage2( + NULL, + TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, + TD_WARNING_ICON, + L"PE View's settings file is corrupt. Do you want to reset it?", + L"If you select No, the settings system will not function properly." + ) == IDYES) + { + HANDLE fileHandle; + IO_STATUS_BLOCK isb; + CHAR data[] = ""; + + // This used to delete the file. But it's better to keep the file there + // and overwrite it with some valid XML, especially with case (2) above. + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PeSettingsFileName->Buffer, + FILE_GENERIC_WRITE, + 0, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_OVERWRITE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + NtWriteFile(fileHandle, NULL, NULL, NULL, &isb, data, sizeof(data) - 1, NULL, NULL); + NtClose(fileHandle); + } + } + else + { + // Pretend we don't have a settings store so bad things don't happen. + PhDereferenceObject(PeSettingsFileName); + PeSettingsFileName = NULL; + } + } + } + + // Apply basic global settings. + PhMaxSizeUnit = PhGetIntegerSetting(L"MaxSizeUnit"); +} + +VOID PeSaveSettings( + VOID + ) +{ + if (PeSettingsFileName) + PhSaveSettings(PeSettingsFileName->Buffer); +} diff --git a/tools/peview/streams.c b/tools/peview/streams.c new file mode 100644 index 000000000000..7d25426e4c6b --- /dev/null +++ b/tools/peview/streams.c @@ -0,0 +1,142 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2018 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpPeEnumerateFileStreams( + _In_ HWND ListViewHandle + ) +{ + HANDLE fileHandle; + + if (NT_SUCCESS(PhCreateFileWin32( + &fileHandle, + PhGetString(PvFileName), + FILE_READ_ATTRIBUTES | FILE_READ_DATA | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ))) + { + ULONG count = 0; + PVOID buffer; + PFILE_STREAM_INFORMATION i; + + if (NT_SUCCESS(PhEnumFileStreams(fileHandle, &buffer))) + { + for (i = PH_FIRST_STREAM(buffer); i; i = PH_NEXT_STREAM(i)) + { + INT lvItemIndex; + PPH_STRING attributeName; + WCHAR number[PH_INT32_STR_LEN_1]; + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + attributeName = PhCreateStringEx(i->StreamName, i->StreamNameLength); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, attributeName->Buffer); + PhDereferenceObject(attributeName); + + PhSetListViewSubItem( + ListViewHandle, + lvItemIndex, + 2, + PhaFormatSize(i->StreamSize.QuadPart, ULONG_MAX)->Buffer + ); + } + + PhFree(buffer); + } + + NtClose(fileHandle); + } +} + +INT_PTR CALLBACK PvpPeStreamsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"Name"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 150, L"Size"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageStreamsListViewColumns", lvHandle); + + PvpPeEnumerateFileStreams(lvHandle); + //ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageStreamsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/tlsprp.c b/tools/peview/tlsprp.c new file mode 100644 index 000000000000..deadbe1e36ca --- /dev/null +++ b/tools/peview/tlsprp.c @@ -0,0 +1,141 @@ +/* + * Process Hacker - + * PE viewer + * + * Copyright (C) 2019 dmex + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#include + +VOID PvpPeEnumerateTlsCallbacks( + _In_ HWND ListViewHandle + ) +{ + PH_MAPPED_IMAGE_TLS_CALLBACKS callbacks; + PH_IMAGE_TLS_CALLBACK_ENTRY entry; + ULONG count = 0; + ULONG i; + + if (NT_SUCCESS(PhGetMappedImageTlsCallbacks(&callbacks, &PvMappedImage))) + { + for (i = 0; i < callbacks.NumberOfEntries; i++) + { + INT lvItemIndex; + PPH_STRING symbol; + PPH_STRING symbolName = NULL; + WCHAR number[PH_INT32_STR_LEN_1]; + WCHAR pointer[PH_PTR_STR_LEN_1]; + + entry = callbacks.Entries[i]; + + PhPrintUInt32(number, ++count); + lvItemIndex = PhAddListViewItem(ListViewHandle, MAXINT, number, NULL); + + PhPrintPointer(pointer, (PVOID)(ULONG_PTR)entry.Address); + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 1, pointer); + + symbol = PhGetSymbolFromAddress( + PvSymbolProvider, + (ULONG64)entry.Address, + NULL, + NULL, + &symbolName, + NULL + ); + + if (symbolName) + { + PhSetListViewSubItem(ListViewHandle, lvItemIndex, 2, symbolName->Buffer); + PhDereferenceObject(symbolName); + } + + if (symbol) PhDereferenceObject(symbol); + } + + PhFree(callbacks.Entries); + } +} + +INT_PTR CALLBACK PvpPeTlsDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + LPPROPSHEETPAGE propSheetPage; + PPV_PROPPAGECONTEXT propPageContext; + + if (!PvPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext)) + return FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lvHandle; + + lvHandle = GetDlgItem(hwndDlg, IDC_LIST); + PhSetListViewStyle(lvHandle, TRUE, TRUE); + PhSetControlTheme(lvHandle, L"explorer"); + PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"#"); + PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 150, L"RVA"); + PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 200, L"Symbol"); + PhSetExtendedListView(lvHandle); + PhLoadListViewColumnsFromSetting(L"ImageTlsListViewColumns", lvHandle); + + PvpPeEnumerateTlsCallbacks(lvHandle); + //ExtendedListView_SortItems(lvHandle); + + EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); + } + break; + case WM_DESTROY: + { + PhSaveListViewColumnsToSetting(L"ImageTlsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_SHOWWINDOW: + { + if (!propPageContext->LayoutInitialized) + { + PPH_LAYOUT_ITEM dialogItem; + + dialogItem = PvAddPropPageLayoutItem(hwndDlg, hwndDlg, PH_PROP_PAGE_TAB_CONTROL_PARENT, PH_ANCHOR_ALL); + PvAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_LIST), dialogItem, PH_ANCHOR_ALL); + + PvDoPropPageLayout(hwndDlg); + + propPageContext->LayoutInitialized = TRUE; + } + } + break; + case WM_NOTIFY: + { + PvHandleListViewNotifyForCopy(lParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + case WM_CONTEXTMENU: + { + PvHandleListViewCommandCopy(hwndDlg, lParam, wParam, GetDlgItem(hwndDlg, IDC_LIST)); + } + break; + } + + return FALSE; +} diff --git a/tools/peview/version.rc b/tools/peview/version.rc deleted file mode 100644 index 3e12095c909a..000000000000 --- a/tools/peview/version.rc +++ /dev/null @@ -1,35 +0,0 @@ -#include "winres.h" -#include "../../ProcessHacker/include/phappres.h" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION PHAPP_VERSION_NUMBER - PRODUCTVERSION PHAPP_VERSION_NUMBER - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "wj32" - VALUE "FileDescription", "PE Viewer" - VALUE "FileVersion", PHAPP_VERSION_STRING - VALUE "InternalName", "peview" - VALUE "LegalCopyright", "Licensed under the GNU GPL, v3." - VALUE "OriginalFilename", "peview.exe" - VALUE "ProductName", "Process Hacker" - VALUE "ProductVersion", PHAPP_VERSION_STRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END diff --git a/tests/phlib-test/main.c b/tools/tests/phlib-test/main.c similarity index 93% rename from tests/phlib-test/main.c rename to tools/tests/phlib-test/main.c index 867a5b376909..c6799fbfafeb 100644 --- a/tests/phlib-test/main.c +++ b/tools/tests/phlib-test/main.c @@ -1,16 +1,16 @@ -#include "tests.h" - -int __cdecl wmain(int argc, wchar_t *argv[]) -{ - NTSTATUS status; - - status = PhInitializePhLib(); - assert(NT_SUCCESS(status)); - - Test_basesup(); - Test_avltree(); - Test_format(); - Test_util(); - - return 0; -} +#include "tests.h" + +int __cdecl wmain(int argc, wchar_t *argv[]) +{ + NTSTATUS status; + + status = PhInitializePhLib(); + assert(NT_SUCCESS(status)); + + Test_basesup(); + Test_avltree(); + Test_format(); + Test_util(); + + return 0; +} diff --git a/tests/phlib-test/phlib-test.vcxproj b/tools/tests/phlib-test/phlib-test.vcxproj similarity index 98% rename from tests/phlib-test/phlib-test.vcxproj rename to tools/tests/phlib-test/phlib-test.vcxproj index 50fa90fcf171..aa1ab1641568 100644 --- a/tests/phlib-test/phlib-test.vcxproj +++ b/tools/tests/phlib-test/phlib-test.vcxproj @@ -1,116 +1,116 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {0C21014E-BC90-4AE5-AA32-398445C13B28} - phlib-test - Win32Proj - 10.0.15063.0 - - - - Application - Unicode - true - v141 - - - Application - Unicode - v141 - - - - - - - - - - - - - $(ProjectDir)bin\$(Configuration)\ - $(ProjectDir)obj\$(Configuration)\ - false - $(ProjectDir)bin\$(Configuration)\ - $(ProjectDir)obj\$(Configuration)\ - false - - - - Disabled - ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) - _PHLIB_;_CONSOLE;WIN32;DEBUG;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - Level3 - ProgramDatabase - StdCall - true - true - - - phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - true - Console - MachineX86 - 5.01 - - - - - MaxSpeed - true - ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) - _PHLIB_;_CONSOLE;WIN32;NDEBUG;%(PreprocessorDefinitions) - MultiThreaded - true - Level3 - ProgramDatabase - StdCall - true - true - - - phlib.lib;ntdll.lib;%(AdditionalDependencies) - ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) - true - Console - true - true - MachineX86 - true - 5.01 - - - - - - - - - - - - {477d0215-f252-41a1-874b-f27e3ea1ed17} - false - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {0C21014E-BC90-4AE5-AA32-398445C13B28} + phlib-test + Win32Proj + 10.0.15063.0 + + + + Application + Unicode + true + v141 + + + Application + Unicode + v141 + + + + + + + + + + + + + $(ProjectDir)bin\$(Configuration)\ + $(ProjectDir)obj\$(Configuration)\ + false + $(ProjectDir)bin\$(Configuration)\ + $(ProjectDir)obj\$(Configuration)\ + false + + + + Disabled + ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) + _PHLIB_;_CONSOLE;WIN32;DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + StdCall + true + true + + + phlib.lib;ntdll.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + true + Console + MachineX86 + 5.01 + + + + + MaxSpeed + true + ..\..\phnt\include;..\..\phlib\include;%(AdditionalIncludeDirectories) + _PHLIB_;_CONSOLE;WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + true + Level3 + ProgramDatabase + StdCall + true + true + + + phlib.lib;ntdll.lib;%(AdditionalDependencies) + ..\..\phlib\bin\$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + true + 5.01 + + + + + + + + + + + + {477d0215-f252-41a1-874b-f27e3ea1ed17} + false + + + + + + + + \ No newline at end of file diff --git a/tests/phlib-test/phlib-test.vcxproj.filters b/tools/tests/phlib-test/phlib-test.vcxproj.filters similarity index 97% rename from tests/phlib-test/phlib-test.vcxproj.filters rename to tools/tests/phlib-test/phlib-test.vcxproj.filters index bcef133e24dd..4e5709ff82b8 100644 --- a/tests/phlib-test/phlib-test.vcxproj.filters +++ b/tools/tests/phlib-test/phlib-test.vcxproj.filters @@ -1,35 +1,35 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + \ No newline at end of file diff --git a/tests/phlib-test/t_avltree.c b/tools/tests/phlib-test/t_avltree.c similarity index 100% rename from tests/phlib-test/t_avltree.c rename to tools/tests/phlib-test/t_avltree.c diff --git a/tests/phlib-test/t_basesup.c b/tools/tests/phlib-test/t_basesup.c similarity index 97% rename from tests/phlib-test/t_basesup.c rename to tools/tests/phlib-test/t_basesup.c index 2bd2fe06c060..f32d49d9ecd4 100644 --- a/tests/phlib-test/t_basesup.c +++ b/tools/tests/phlib-test/t_basesup.c @@ -1,352 +1,352 @@ -#include "tests.h" - -static VOID Test_time( - VOID - ) -{ - LARGE_INTEGER time; - FILETIME fileTime; - LARGE_INTEGER localTime; - FILETIME localFileTime; - LARGE_INTEGER systemTime; - FILETIME systemFileTime; - - PhQuerySystemTime(&time); - fileTime.dwLowDateTime = time.LowPart; - fileTime.dwHighDateTime = time.HighPart; - - PhSystemTimeToLocalTime(&time, &localTime); - FileTimeToLocalFileTime(&fileTime, &localFileTime); - - assert(localTime.LowPart == localFileTime.dwLowDateTime); - assert(localTime.HighPart == localFileTime.dwHighDateTime); - - PhLocalTimeToSystemTime(&localTime, &systemTime); - LocalFileTimeToFileTime(&localFileTime, &systemFileTime); - - assert(systemTime.LowPart == systemFileTime.dwLowDateTime); - assert(systemTime.HighPart == systemFileTime.dwHighDateTime); -} - -static VOID Test_stringz( - VOID - ) -{ - BOOLEAN result; - CHAR inputA[16] = "test"; - CHAR outputA[16]; - WCHAR inputW[16] = L"test"; - WCHAR outputW[16]; - ULONG returnCount; - PWSTR zero = L"\0\0\0\0\0\0\0\0"; - PWSTR asdf = L"asdfasdfasdfasdf"; - ULONG i; - - for (i = 0; i < 8; i++) - assert(PhCountStringZ(zero + i) == 0); - for (i = 0; i < 16; i++) - assert(PhCountStringZ(asdf + i) == 16 - i); - - result = PhCopyBytesZ(inputA, 4, outputA, 4, &returnCount); - assert(!result && returnCount == 5); - result = PhCopyBytesZ(inputA, 100, outputA, 4, &returnCount); - assert(!result && returnCount == 5); - result = PhCopyBytesZ(inputA, 3, outputA, 4, &returnCount); - assert(result && returnCount == 4); - result = PhCopyBytesZ(inputA, 4, outputA, 5, &returnCount); - assert(result && returnCount == 5); - result = PhCopyBytesZ(inputA, 100, outputA, 5, &returnCount); - assert(result && returnCount == 5); - - result = PhCopyStringZ(inputW, 100, outputW, 4, &returnCount); - assert(!result && returnCount == 5); - result = PhCopyStringZ(inputW, 4, outputW, 5, &returnCount); - assert(result && returnCount == 5); - result = PhCopyStringZ(inputW, 100, outputW, 5, &returnCount); - assert(result && returnCount == 5); - - result = PhCopyStringZFromMultiByte(inputA, 4, outputW, 4, &returnCount); - assert(!result && returnCount == 5); - result = PhCopyStringZFromMultiByte(inputA, 100, outputW, 4, &returnCount); - assert(!result && returnCount == 5); - result = PhCopyStringZFromMultiByte(inputA, 3, outputW, 4, &returnCount); - assert(result && returnCount == 4); - result = PhCopyStringZFromMultiByte(inputA, 4, outputW, 5, &returnCount); - assert(result && returnCount == 5); - result = PhCopyStringZFromMultiByte(inputA, 100, outputW, 5, &returnCount); - assert(result && returnCount == 5); - - assert(PhCompareStringZNatural(L"abc", L"abc", FALSE) == 0); - assert(PhCompareStringZNatural(L"abc", L"abc", TRUE) == 0); - assert(PhCompareStringZNatural(L"abc", L"ABC", FALSE) != 0); - assert(PhCompareStringZNatural(L"abc", L"ABC", TRUE) == 0); - assert(PhCompareStringZNatural(L"abc", L"abd", FALSE) < 0); - assert(PhCompareStringZNatural(L"abe", L"abd", FALSE) > 0); - assert(PhCompareStringZNatural(L"1", L"2", FALSE) < 0); - assert(PhCompareStringZNatural(L"12", L"9", FALSE) > 0); - assert(PhCompareStringZNatural(L"file-1", L"file-9", FALSE) < 0); - assert(PhCompareStringZNatural(L"file-12", L"file-9", FALSE) > 0); - assert(PhCompareStringZNatural(L"file-12", L"file-90", FALSE) < 0); -} - -VOID Test_stringref( - VOID - ) -{ - ULONG i; - ULONG j; - PH_STRINGREF s1; - PH_STRINGREF s2; - PH_STRINGREF s3; - WCHAR buffer[26 * 2]; - - // PhEqualStringRef, PhFindCharInStringRef, PhFindLastCharInStringRef - - // Alignment tests - - s1.Buffer = buffer; - s1.Length = sizeof(buffer); - - for (i = 0; i < 26; i++) - s1.Buffer[i] = (WCHAR)i; - - for (i = 0; i < 26; i++) - assert(PhFindCharInStringRef(&s1, (WCHAR)i, FALSE) == i); - - memset(buffer, 0, sizeof(buffer)); - s1.Length = 0; - - for (i = 0; i < 26; i++) - assert(PhFindCharInStringRef(&s1, 0, FALSE) == -1); - - buffer[26] = 1; - - for (i = 0; i < 26; i++) - { - s1.Buffer = buffer + 26 - i; - s1.Length = i * sizeof(WCHAR); - assert(PhFindCharInStringRef(&s1, 1, FALSE) == -1); - } - - for (i = 1; i < 26; i++) - { - s1.Buffer = buffer; - s1.Length = i * 2 * sizeof(WCHAR); - - for (j = 0; j < i; j++) - { - buffer[j] = (WCHAR)('a' + j); - buffer[i + j] = (WCHAR)('A' + j); - } - - s2.Buffer = buffer; - s2.Length = i * sizeof(WCHAR); - s3.Buffer = buffer + i; - s3.Length = i * sizeof(WCHAR); - assert(!PhEqualStringRef(&s2, &s3, FALSE)); - assert(PhEqualStringRef(&s2, &s3, TRUE)); - - for (j = 0; j < i; j++) - { - buffer[j] = 'z'; - assert(!PhEqualStringRef(&s2, &s3, FALSE)); - assert(!PhEqualStringRef(&s2, &s3, TRUE)); - buffer[j] = (WCHAR)('a' + j); - } - - s3 = s2; - assert(PhEqualStringRef(&s2, &s3, FALSE)); - assert(PhEqualStringRef(&s2, &s3, TRUE)); - - for (j = 0; j < i; j++) - { - assert(PhFindCharInStringRef(&s1, s1.Buffer[j], FALSE) == j); - assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j], FALSE) == j); - assert(PhFindCharInStringRef(&s1, s1.Buffer[j], TRUE) == j % i); - assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j], TRUE) == i + j % i); - } - - s1.Length = i * sizeof(WCHAR); - - for (j = 0; j < i; j++) - { - assert(PhFindCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), FALSE) == -1); - assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), FALSE) == -1); - assert(PhFindCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), TRUE) == j); - assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), TRUE) == j); - } - - s1.Buffer += i; - - for (j = 0; j < i; j++) - { - assert(PhFindCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), FALSE) == -1); - assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), FALSE) == -1); - assert(PhFindCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), TRUE) == j); - assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), TRUE) == j); - } - } - - // PhFindStringInStringRef - -#define DO_STRSTR_TEST(func, s1, s2, expected, ...) \ - do { \ - PH_STRINGREF ___t1; \ - PH_STRINGREF ___t2; \ - PhInitializeStringRef(&___t1, s1); \ - PhInitializeStringRef(&___t2, s2); \ - assert(func(&___t1, &___t2, __VA_ARGS__) == expected); \ - } while (0) - - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdf", L"f", 3, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdf", L"g", -1, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"g", 7, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdg", 4, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdgh", -1, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdfasdg", 0, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"sdfasdg", 1, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdfasdgg", -1, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdfasdgggggg", -1, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"", L"asdfasdgggggg", -1, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"", 0, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"", L"", 0, FALSE); - // Test roll-over - DO_STRSTR_TEST(PhFindStringInStringRef, L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", 0, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", L"asdg4sdfg", 28, FALSE); - DO_STRSTR_TEST(PhFindStringInStringRef, L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", L"asdg4Gdfg", -1, FALSE); -} - -VOID Test_hexstring( - VOID - ) -{ - BOOLEAN result; - PH_STRINGREF sr; - PPH_STRING string; - UCHAR buffer[16]; - - PhInitializeStringRef(&sr, L"0011223344"); - result = PhHexStringToBuffer(&sr, buffer); - assert(result && buffer[0] == 0 && buffer[1] == 0x11 && buffer[2] == 0x22 && buffer[3] == 0x33 && buffer[4] == 0x44); - - PhInitializeStringRef(&sr, L"00111"); - result = PhHexStringToBuffer(&sr, buffer); - assert(!result); - - buffer[0] = 0; - buffer[1] = 0x99; - buffer[2] = 0xff; - - string = PhBufferToHexString(buffer, 3); - assert(wcscmp(string->Buffer, L"0099ff") == 0); -} - -VOID Test_strint( - VOID - ) -{ - PH_STRINGREF sr; - LONG64 integer; - PPH_STRING string; - - PhInitializeStringRef(&sr, L"123"); - PhStringToInteger64(&sr, 0, &integer); - assert(integer == 123); - PhStringToInteger64(&sr, 10, &integer); - assert(integer == 123); - PhStringToInteger64(&sr, 8, &integer); - assert(integer == 0123); - PhStringToInteger64(&sr, 16, &integer); - assert(integer == 0x123); - - PhInitializeStringRef(&sr, L"0o123"); - PhStringToInteger64(&sr, 0, &integer); - assert(integer == 0123); - PhInitializeStringRef(&sr, L"0x123"); - PhStringToInteger64(&sr, 0, &integer); - assert(integer == 0x123); - - string = PhIntegerToString64(123, 0, FALSE); - assert(wcscmp(string->Buffer, L"123") == 0); - string = PhIntegerToString64(123, 10, FALSE); - assert(wcscmp(string->Buffer, L"123") == 0); - string = PhIntegerToString64(123, 8, FALSE); - assert(wcscmp(string->Buffer, L"173") == 0); - string = PhIntegerToString64(123, 16, FALSE); - assert(wcscmp(string->Buffer, L"7b") == 0); - - string = PhIntegerToString64(-123, 0, TRUE); - assert(wcscmp(string->Buffer, L"-123") == 0); - string = PhIntegerToString64(-123, 10, TRUE); - assert(wcscmp(string->Buffer, L"-123") == 0); - string = PhIntegerToString64(-123, 8, TRUE); - assert(wcscmp(string->Buffer, L"-173") == 0); - string = PhIntegerToString64(-123, 16, TRUE); - assert(wcscmp(string->Buffer, L"-7b") == 0); - - string = PhIntegerToString64(-123, 0, FALSE); - assert(wcscmp(string->Buffer, L"18446744073709551493") == 0); -} - -VOID Test_unicode( - VOID - ) -{ - BOOLEAN result; - ULONG codePoints[6]; - SIZE_T i; - WCHAR utf16[sizeof(codePoints) / sizeof(WCHAR)]; - CHAR utf8[sizeof(codePoints) / sizeof(CHAR)]; - ULONG numberOfCodePoints; - SIZE_T utf16Position = 0; - SIZE_T utf8Position = 0; - PPH_STRING utf16_1, utf16_2, utf16_3; - PPH_BYTES utf8_1, utf8_2, utf8_3; - - codePoints[0] = 0; - codePoints[1] = 0x50; - codePoints[2] = 0x312; - codePoints[3] = 0x3121; - codePoints[4] = 0x31212; - codePoints[5] = PH_UNICODE_MAX_CODE_POINT; - - for (i = 0; i < sizeof(codePoints) / sizeof(ULONG); i++) - { - result = PhEncodeUnicode(PH_UNICODE_UTF16, codePoints[i], utf16 + utf16Position, &numberOfCodePoints); - assert(result); - utf16Position += numberOfCodePoints; - - result = PhEncodeUnicode(PH_UNICODE_UTF8, codePoints[i], utf8 + utf8Position, &numberOfCodePoints); - assert(result); - utf8Position += numberOfCodePoints; - } - - utf16_1 = PhCreateStringEx(utf16, utf16Position * sizeof(WCHAR)); - utf8_1 = PhCreateBytesEx(utf8, utf8Position); - utf16_2 = PhConvertUtf8ToUtf16Ex(utf8_1->Buffer, utf8_1->Length); - utf8_2 = PhConvertUtf16ToUtf8Ex(utf16_1->Buffer, utf16_1->Length); - utf16_3 = PhConvertUtf8ToUtf16Ex(utf8_2->Buffer, utf8_2->Length); - utf8_3 = PhConvertUtf16ToUtf8Ex(utf16_2->Buffer, utf16_2->Length); - - assert(utf16_1->Length == utf16_2->Length); - assert(memcmp(utf16_1->Buffer, utf16_2->Buffer, utf16_1->Length) == 0); - assert(utf16_2->Length == utf16_3->Length); - assert(memcmp(utf16_2->Buffer, utf16_3->Buffer, utf16_2->Length) == 0); - - assert(utf8_1->Length == utf8_2->Length); - assert(memcmp(utf8_1->Buffer, utf8_2->Buffer, utf8_1->Length) == 0); - assert(utf8_2->Length == utf8_3->Length); - assert(memcmp(utf8_2->Buffer, utf8_3->Buffer, utf8_2->Length) == 0); -} - -VOID Test_basesup( - VOID - ) -{ - Test_time(); - Test_stringz(); - Test_stringref(); - Test_hexstring(); - Test_strint(); - Test_unicode(); -} +#include "tests.h" + +static VOID Test_time( + VOID + ) +{ + LARGE_INTEGER time; + FILETIME fileTime; + LARGE_INTEGER localTime; + FILETIME localFileTime; + LARGE_INTEGER systemTime; + FILETIME systemFileTime; + + PhQuerySystemTime(&time); + fileTime.dwLowDateTime = time.LowPart; + fileTime.dwHighDateTime = time.HighPart; + + PhSystemTimeToLocalTime(&time, &localTime); + FileTimeToLocalFileTime(&fileTime, &localFileTime); + + assert(localTime.LowPart == localFileTime.dwLowDateTime); + assert(localTime.HighPart == localFileTime.dwHighDateTime); + + PhLocalTimeToSystemTime(&localTime, &systemTime); + LocalFileTimeToFileTime(&localFileTime, &systemFileTime); + + assert(systemTime.LowPart == systemFileTime.dwLowDateTime); + assert(systemTime.HighPart == systemFileTime.dwHighDateTime); +} + +static VOID Test_stringz( + VOID + ) +{ + BOOLEAN result; + CHAR inputA[16] = "test"; + CHAR outputA[16]; + WCHAR inputW[16] = L"test"; + WCHAR outputW[16]; + ULONG returnCount; + PWSTR zero = L"\0\0\0\0\0\0\0\0"; + PWSTR asdf = L"asdfasdfasdfasdf"; + ULONG i; + + for (i = 0; i < 8; i++) + assert(PhCountStringZ(zero + i) == 0); + for (i = 0; i < 16; i++) + assert(PhCountStringZ(asdf + i) == 16 - i); + + result = PhCopyBytesZ(inputA, 4, outputA, 4, &returnCount); + assert(!result && returnCount == 5); + result = PhCopyBytesZ(inputA, 100, outputA, 4, &returnCount); + assert(!result && returnCount == 5); + result = PhCopyBytesZ(inputA, 3, outputA, 4, &returnCount); + assert(result && returnCount == 4); + result = PhCopyBytesZ(inputA, 4, outputA, 5, &returnCount); + assert(result && returnCount == 5); + result = PhCopyBytesZ(inputA, 100, outputA, 5, &returnCount); + assert(result && returnCount == 5); + + result = PhCopyStringZ(inputW, 100, outputW, 4, &returnCount); + assert(!result && returnCount == 5); + result = PhCopyStringZ(inputW, 4, outputW, 5, &returnCount); + assert(result && returnCount == 5); + result = PhCopyStringZ(inputW, 100, outputW, 5, &returnCount); + assert(result && returnCount == 5); + + result = PhCopyStringZFromMultiByte(inputA, 4, outputW, 4, &returnCount); + assert(!result && returnCount == 5); + result = PhCopyStringZFromMultiByte(inputA, 100, outputW, 4, &returnCount); + assert(!result && returnCount == 5); + result = PhCopyStringZFromMultiByte(inputA, 3, outputW, 4, &returnCount); + assert(result && returnCount == 4); + result = PhCopyStringZFromMultiByte(inputA, 4, outputW, 5, &returnCount); + assert(result && returnCount == 5); + result = PhCopyStringZFromMultiByte(inputA, 100, outputW, 5, &returnCount); + assert(result && returnCount == 5); + + assert(PhCompareStringZNatural(L"abc", L"abc", FALSE) == 0); + assert(PhCompareStringZNatural(L"abc", L"abc", TRUE) == 0); + assert(PhCompareStringZNatural(L"abc", L"ABC", FALSE) != 0); + assert(PhCompareStringZNatural(L"abc", L"ABC", TRUE) == 0); + assert(PhCompareStringZNatural(L"abc", L"abd", FALSE) < 0); + assert(PhCompareStringZNatural(L"abe", L"abd", FALSE) > 0); + assert(PhCompareStringZNatural(L"1", L"2", FALSE) < 0); + assert(PhCompareStringZNatural(L"12", L"9", FALSE) > 0); + assert(PhCompareStringZNatural(L"file-1", L"file-9", FALSE) < 0); + assert(PhCompareStringZNatural(L"file-12", L"file-9", FALSE) > 0); + assert(PhCompareStringZNatural(L"file-12", L"file-90", FALSE) < 0); +} + +VOID Test_stringref( + VOID + ) +{ + ULONG i; + ULONG j; + PH_STRINGREF s1; + PH_STRINGREF s2; + PH_STRINGREF s3; + WCHAR buffer[26 * 2]; + + // PhEqualStringRef, PhFindCharInStringRef, PhFindLastCharInStringRef + + // Alignment tests + + s1.Buffer = buffer; + s1.Length = sizeof(buffer); + + for (i = 0; i < 26; i++) + s1.Buffer[i] = (WCHAR)i; + + for (i = 0; i < 26; i++) + assert(PhFindCharInStringRef(&s1, (WCHAR)i, FALSE) == i); + + memset(buffer, 0, sizeof(buffer)); + s1.Length = 0; + + for (i = 0; i < 26; i++) + assert(PhFindCharInStringRef(&s1, 0, FALSE) == -1); + + buffer[26] = 1; + + for (i = 0; i < 26; i++) + { + s1.Buffer = buffer + 26 - i; + s1.Length = i * sizeof(WCHAR); + assert(PhFindCharInStringRef(&s1, 1, FALSE) == -1); + } + + for (i = 1; i < 26; i++) + { + s1.Buffer = buffer; + s1.Length = i * 2 * sizeof(WCHAR); + + for (j = 0; j < i; j++) + { + buffer[j] = (WCHAR)('a' + j); + buffer[i + j] = (WCHAR)('A' + j); + } + + s2.Buffer = buffer; + s2.Length = i * sizeof(WCHAR); + s3.Buffer = buffer + i; + s3.Length = i * sizeof(WCHAR); + assert(!PhEqualStringRef(&s2, &s3, FALSE)); + assert(PhEqualStringRef(&s2, &s3, TRUE)); + + for (j = 0; j < i; j++) + { + buffer[j] = 'z'; + assert(!PhEqualStringRef(&s2, &s3, FALSE)); + assert(!PhEqualStringRef(&s2, &s3, TRUE)); + buffer[j] = (WCHAR)('a' + j); + } + + s3 = s2; + assert(PhEqualStringRef(&s2, &s3, FALSE)); + assert(PhEqualStringRef(&s2, &s3, TRUE)); + + for (j = 0; j < i; j++) + { + assert(PhFindCharInStringRef(&s1, s1.Buffer[j], FALSE) == j); + assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j], FALSE) == j); + assert(PhFindCharInStringRef(&s1, s1.Buffer[j], TRUE) == j % i); + assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j], TRUE) == i + j % i); + } + + s1.Length = i * sizeof(WCHAR); + + for (j = 0; j < i; j++) + { + assert(PhFindCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), FALSE) == -1); + assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), FALSE) == -1); + assert(PhFindCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), TRUE) == j); + assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] - ('a' - 'A'), TRUE) == j); + } + + s1.Buffer += i; + + for (j = 0; j < i; j++) + { + assert(PhFindCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), FALSE) == -1); + assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), FALSE) == -1); + assert(PhFindCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), TRUE) == j); + assert(PhFindLastCharInStringRef(&s1, s1.Buffer[j] + ('a' - 'A'), TRUE) == j); + } + } + + // PhFindStringInStringRef + +#define DO_STRSTR_TEST(func, s1, s2, expected, ...) \ + do { \ + PH_STRINGREF ___t1; \ + PH_STRINGREF ___t2; \ + PhInitializeStringRef(&___t1, s1); \ + PhInitializeStringRef(&___t2, s2); \ + assert(func(&___t1, &___t2, __VA_ARGS__) == expected); \ + } while (0) + + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdf", L"f", 3, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdf", L"g", -1, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"g", 7, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdg", 4, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdgh", -1, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdfasdg", 0, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"sdfasdg", 1, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdfasdgg", -1, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"asdfasdgggggg", -1, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"", L"asdfasdgggggg", -1, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"asdfasdg", L"", 0, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"", L"", 0, FALSE); + // Test roll-over + DO_STRSTR_TEST(PhFindStringInStringRef, L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", 0, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", L"asdg4sdfg", 28, FALSE); + DO_STRSTR_TEST(PhFindStringInStringRef, L"0sdfasdf1sdfasdf2sdfasdf3sdfasdg4sdfg", L"asdg4Gdfg", -1, FALSE); +} + +VOID Test_hexstring( + VOID + ) +{ + BOOLEAN result; + PH_STRINGREF sr; + PPH_STRING string; + UCHAR buffer[16]; + + PhInitializeStringRef(&sr, L"0011223344"); + result = PhHexStringToBuffer(&sr, buffer); + assert(result && buffer[0] == 0 && buffer[1] == 0x11 && buffer[2] == 0x22 && buffer[3] == 0x33 && buffer[4] == 0x44); + + PhInitializeStringRef(&sr, L"00111"); + result = PhHexStringToBuffer(&sr, buffer); + assert(!result); + + buffer[0] = 0; + buffer[1] = 0x99; + buffer[2] = 0xff; + + string = PhBufferToHexString(buffer, 3); + assert(wcscmp(string->Buffer, L"0099ff") == 0); +} + +VOID Test_strint( + VOID + ) +{ + PH_STRINGREF sr; + LONG64 integer; + PPH_STRING string; + + PhInitializeStringRef(&sr, L"123"); + PhStringToInteger64(&sr, 0, &integer); + assert(integer == 123); + PhStringToInteger64(&sr, 10, &integer); + assert(integer == 123); + PhStringToInteger64(&sr, 8, &integer); + assert(integer == 0123); + PhStringToInteger64(&sr, 16, &integer); + assert(integer == 0x123); + + PhInitializeStringRef(&sr, L"0o123"); + PhStringToInteger64(&sr, 0, &integer); + assert(integer == 0123); + PhInitializeStringRef(&sr, L"0x123"); + PhStringToInteger64(&sr, 0, &integer); + assert(integer == 0x123); + + string = PhIntegerToString64(123, 0, FALSE); + assert(wcscmp(string->Buffer, L"123") == 0); + string = PhIntegerToString64(123, 10, FALSE); + assert(wcscmp(string->Buffer, L"123") == 0); + string = PhIntegerToString64(123, 8, FALSE); + assert(wcscmp(string->Buffer, L"173") == 0); + string = PhIntegerToString64(123, 16, FALSE); + assert(wcscmp(string->Buffer, L"7b") == 0); + + string = PhIntegerToString64(-123, 0, TRUE); + assert(wcscmp(string->Buffer, L"-123") == 0); + string = PhIntegerToString64(-123, 10, TRUE); + assert(wcscmp(string->Buffer, L"-123") == 0); + string = PhIntegerToString64(-123, 8, TRUE); + assert(wcscmp(string->Buffer, L"-173") == 0); + string = PhIntegerToString64(-123, 16, TRUE); + assert(wcscmp(string->Buffer, L"-7b") == 0); + + string = PhIntegerToString64(-123, 0, FALSE); + assert(wcscmp(string->Buffer, L"18446744073709551493") == 0); +} + +VOID Test_unicode( + VOID + ) +{ + BOOLEAN result; + ULONG codePoints[6]; + SIZE_T i; + WCHAR utf16[sizeof(codePoints) / sizeof(WCHAR)]; + CHAR utf8[sizeof(codePoints) / sizeof(CHAR)]; + ULONG numberOfCodePoints; + SIZE_T utf16Position = 0; + SIZE_T utf8Position = 0; + PPH_STRING utf16_1, utf16_2, utf16_3; + PPH_BYTES utf8_1, utf8_2, utf8_3; + + codePoints[0] = 0; + codePoints[1] = 0x50; + codePoints[2] = 0x312; + codePoints[3] = 0x3121; + codePoints[4] = 0x31212; + codePoints[5] = PH_UNICODE_MAX_CODE_POINT; + + for (i = 0; i < sizeof(codePoints) / sizeof(ULONG); i++) + { + result = PhEncodeUnicode(PH_UNICODE_UTF16, codePoints[i], utf16 + utf16Position, &numberOfCodePoints); + assert(result); + utf16Position += numberOfCodePoints; + + result = PhEncodeUnicode(PH_UNICODE_UTF8, codePoints[i], utf8 + utf8Position, &numberOfCodePoints); + assert(result); + utf8Position += numberOfCodePoints; + } + + utf16_1 = PhCreateStringEx(utf16, utf16Position * sizeof(WCHAR)); + utf8_1 = PhCreateBytesEx(utf8, utf8Position); + utf16_2 = PhConvertUtf8ToUtf16Ex(utf8_1->Buffer, utf8_1->Length); + utf8_2 = PhConvertUtf16ToUtf8Ex(utf16_1->Buffer, utf16_1->Length); + utf16_3 = PhConvertUtf8ToUtf16Ex(utf8_2->Buffer, utf8_2->Length); + utf8_3 = PhConvertUtf16ToUtf8Ex(utf16_2->Buffer, utf16_2->Length); + + assert(utf16_1->Length == utf16_2->Length); + assert(memcmp(utf16_1->Buffer, utf16_2->Buffer, utf16_1->Length) == 0); + assert(utf16_2->Length == utf16_3->Length); + assert(memcmp(utf16_2->Buffer, utf16_3->Buffer, utf16_2->Length) == 0); + + assert(utf8_1->Length == utf8_2->Length); + assert(memcmp(utf8_1->Buffer, utf8_2->Buffer, utf8_1->Length) == 0); + assert(utf8_2->Length == utf8_3->Length); + assert(memcmp(utf8_2->Buffer, utf8_3->Buffer, utf8_2->Length) == 0); +} + +VOID Test_basesup( + VOID + ) +{ + Test_time(); + Test_stringz(); + Test_stringref(); + Test_hexstring(); + Test_strint(); + Test_unicode(); +} diff --git a/tests/phlib-test/t_format.c b/tools/tests/phlib-test/t_format.c similarity index 97% rename from tests/phlib-test/t_format.c rename to tools/tests/phlib-test/t_format.c index 1e9302e2a46e..825f7f609b8e 100644 --- a/tests/phlib-test/t_format.c +++ b/tools/tests/phlib-test/t_format.c @@ -1,511 +1,511 @@ -#include "tests.h" - -static VOID Test_buffer( - VOID - ) -{ -#define OUTPUT_COUNT 10 - - BOOLEAN result; - PH_FORMAT format[1]; - WCHAR buffer[16]; - SIZE_T returnLength; - - format[0].Type = Int32FormatType; - format[0].u.Int32 = 1234567890; - - result = PhFormatToBuffer(format, 1, buffer, (OUTPUT_COUNT + 1) * sizeof(WCHAR), &returnLength); - assert(result && wcscmp(buffer, L"1234567890") == 0 && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); - result = PhFormatToBuffer(format, 1, buffer, 16 * sizeof(WCHAR), &returnLength); - assert(result && wcscmp(buffer, L"1234567890") == 0 && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); - result = PhFormatToBuffer(format, 1, buffer, OUTPUT_COUNT * sizeof(WCHAR), &returnLength); - assert(!result && buffer[0] == 0 && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); - result = PhFormatToBuffer(format, 1, buffer, 0, &returnLength); - assert(!result && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); - result = PhFormatToBuffer(format, 1, NULL, 9999, &returnLength); - assert(!result && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); -} - -static VOID Test_char( - VOID - ) -{ - BOOLEAN result; - PH_FORMAT format[2]; - WCHAR buffer[1024]; - - format[0].Type = CharFormatType; - format[0].u.Char = 'H'; - format[1].Type = CharFormatType; - format[1].u.Char = 'i'; - result = PhFormatToBuffer(format, 2, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"Hi") == 0); -} - -static VOID Test_string( - VOID - ) -{ - BOOLEAN result; - PH_FORMAT format[4]; - WCHAR buffer[1024]; - - format[0].Type = StringFormatType; - PhInitializeStringRef(&format[0].u.String, L"This "); - format[1].Type = StringZFormatType; - format[1].u.StringZ = L"is "; - format[2].Type = MultiByteStringFormatType; - PhInitializeBytesRef(&format[2].u.MultiByteString, "a "); - format[3].Type = MultiByteStringZFormatType; - format[3].u.MultiByteStringZ = "string."; - result = PhFormatToBuffer(format, 4, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"This is a string.") == 0); -} - -static BOOLEAN IsThousandSepComma( - VOID - ) -{ - WCHAR thousandSep[4]; - - if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousandSep, 4)) - return FALSE; - if (thousandSep[0] != ',' || thousandSep[1] != 0) - return FALSE; - - return TRUE; -} - -static VOID Test_integer( - VOID - ) -{ - BOOLEAN result; - PH_FORMAT format[1]; - WCHAR buffer[1024]; - - // Basic - - format[0].Type = Int32FormatType; - format[0].u.Int32 = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"0") == 0); - - format[0].Type = Int32FormatType; - format[0].u.Int32 = 123; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123") == 0); - - format[0].Type = Int32FormatType; - format[0].u.Int32 = -123; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-123") == 0); - - format[0].Type = UInt32FormatType; - format[0].u.UInt32 = -123; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"4294967173") == 0); - - format[0].Type = Int64FormatType; - format[0].u.Int64 = -1234567890; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1234567890") == 0); - - format[0].Type = UInt64FormatType; - format[0].u.UInt64 = 12345678901234567890; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"12345678901234567890") == 0); - - // Bases - - format[0].Type = UInt64FormatType | FormatUseRadix; - format[0].u.UInt64 = 12345678901234567890; - format[0].Radix = 16; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"ab54a98ceb1f0ad2") == 0); - - format[0].Type = UInt64FormatType | FormatUseRadix | FormatUpperCase; - format[0].u.UInt64 = 12345678901234567890; - format[0].Radix = 16; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"AB54A98CEB1F0AD2") == 0); - - format[0].Type = Int32FormatType | FormatUseRadix; - format[0].u.Int32 = -1234; - format[0].Radix = 8; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-2322") == 0); - - // Prefix sign - - format[0].Type = Int32FormatType | FormatPrefixSign; - format[0].u.Int32 = 1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"+1234") == 0); - - format[0].Type = Int32FormatType | FormatPrefixSign; - format[0].u.Int32 = -1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1234") == 0); - - // Zero pad - - format[0].Type = Int32FormatType | FormatPadZeros; - format[0].Width = 6; - - format[0].u.Int32 = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"000000") == 0); - format[0].u.Int32 = 1; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"000001") == 0); - format[0].u.Int32 = 12345; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"012345") == 0); - format[0].u.Int32 = 123456; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123456") == 0); - format[0].u.Int32 = 1234567890; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1234567890") == 0); - format[0].u.Int32 = -1; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-00001") == 0); - format[0].u.Int32 = -1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-01234") == 0); - format[0].u.Int32 = -12345; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-12345") == 0); - format[0].u.Int32 = -1234567890; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1234567890") == 0); - - // Digit grouping - - if (!IsThousandSepComma()) - return; - - format[0].Type = Int32FormatType | FormatGroupDigits; - - format[0].u.Int32 = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"0") == 0); - format[0].u.Int32 = 1; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1") == 0); - format[0].u.Int32 = 12; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"12") == 0); - format[0].u.Int32 = 123; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123") == 0); - format[0].u.Int32 = 1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1,234") == 0); - format[0].u.Int32 = 12345; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"12,345") == 0); - format[0].u.Int32 = 123456; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123,456") == 0); - format[0].u.Int32 = -123; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-123") == 0); - format[0].u.Int32 = -1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1,234") == 0); - format[0].u.Int32 = -12345; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-12,345") == 0); -} - -static VOID Test_float( - VOID - ) -{ - BOOLEAN result; - PH_FORMAT format[1]; - WCHAR buffer[1024]; - - // TODO: Standard and hexadecimal form - - // Basic - - format[0].Type = DoubleFormatType; - format[0].u.Double = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"0.000000") == 0); - - format[0].Type = DoubleFormatType; - format[0].u.Double = 1; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1.000000") == 0); - - format[0].Type = DoubleFormatType; - format[0].u.Double = 123456789; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123456789.000000") == 0); - - format[0].Type = DoubleFormatType; - format[0].u.Double = 3.14159265358979; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"3.141593") == 0); - - // Precision - - format[0].Type = DoubleFormatType | FormatUsePrecision; - format[0].u.Double = 0; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"0") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision; - format[0].u.Double = 3.14159265358979; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"3") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision; - format[0].u.Double = 3.14159265358979; - format[0].Precision = 1; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"3.1") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision; - format[0].u.Double = 3.14159265358979; - format[0].Precision = 4; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"3.1416") == 0); - - // Crop zeros - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; - format[0].u.Double = 0; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"0") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; - format[0].u.Double = 1.2; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1.2") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; - format[0].u.Double = 1.21; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1.21") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; - format[0].u.Double = 1.216; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1.216") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; - format[0].u.Double = 1.2159; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1.216") == 0); - - // Prefix sign - - format[0].Type = DoubleFormatType | FormatPrefixSign; - format[0].u.Double = 1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"+1234.000000") == 0); - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatPrefixSign; - format[0].u.Double = 1234; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"+1234") == 0); - - format[0].Type = DoubleFormatType | FormatPrefixSign; - format[0].u.Double = -1234; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1234.000000") == 0); - - format[0].Type = DoubleFormatType | FormatPrefixSign; - format[0].u.Double = -1234.12; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1234.120000") == 0); - - // Zero pad - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatPadZeros; - format[0].Width = 6; - - format[0].u.Double = 0; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"00.000") == 0); - format[0].u.Double = 1.23; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"01.230") == 0); - format[0].u.Double = 123456; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123456.000") == 0); - format[0].u.Double = -1.23; - format[0].Precision = 2; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-01.23") == 0); - format[0].u.Double = -1.23; - format[0].Precision = 3; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1.230") == 0); - - // Digit grouping - - if (!IsThousandSepComma()) - return; - - format[0].Type = DoubleFormatType | FormatUsePrecision | FormatGroupDigits; - - format[0].u.Double = 0; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"0") == 0); - format[0].u.Double = 1; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1") == 0); - format[0].u.Double = 12; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"12") == 0); - format[0].u.Double = 123; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123") == 0); - format[0].u.Double = 1234; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"1,234") == 0); - format[0].u.Double = 12345; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"12,345") == 0); - format[0].u.Double = 123456; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"123,456") == 0); - format[0].u.Double = -123; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-123") == 0); - format[0].u.Double = -1234; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-1,234") == 0); - format[0].u.Double = -12345; - format[0].Precision = 0; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-12,345") == 0); - format[0].u.Double = -12345; - format[0].Precision = 2; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-12,345.00") == 0); - format[0].u.Double = -9876543.21; - format[0].Precision = 5; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"-9,876,543.21000") == 0); -} - -static VOID Test_width( - VOID - ) -{ - BOOLEAN result; - PH_FORMAT format[2]; - WCHAR buffer[1024]; - - PhInitializeStringRef(&format[0].u.String, L"asdf"); - - format[0].Type = StringFormatType; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf") == 0); - - // Left align - - format[0].Type = StringFormatType | FormatLeftAlign; - format[0].Width = 4; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf") == 0); - - format[0].Type = StringFormatType | FormatLeftAlign; - format[0].Width = 2; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf") == 0); - - format[0].Type = StringFormatType | FormatLeftAlign; - format[0].Width = 5; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf ") == 0); - - format[0].Type = StringFormatType | FormatLeftAlign; - format[0].Width = 10; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf ") == 0); - - format[0].Type = StringFormatType | FormatLeftAlign | FormatUsePad; - format[0].Width = 6; - format[0].Pad = '!'; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf!!") == 0); - - // Right align - - format[0].Type = StringFormatType | FormatRightAlign; - format[0].Width = 4; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf") == 0); - - format[0].Type = StringFormatType | FormatRightAlign; - format[0].Width = 2; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"asdf") == 0); - - format[0].Type = StringFormatType | FormatRightAlign; - format[0].Width = 5; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L" asdf") == 0); - - format[0].Type = StringFormatType | FormatRightAlign; - format[0].Width = 10; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L" asdf") == 0); - - format[0].Type = StringFormatType | FormatRightAlign | FormatUsePad; - format[0].Width = 6; - format[0].Pad = '!'; - result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L"!!asdf") == 0); - - // Multiple - - format[0].Type = Int32FormatType | FormatRightAlign; - format[0].u.Int32 = 1234; - format[0].Width = 7; - format[1].Type = StringZFormatType | FormatLeftAlign; - format[1].u.StringZ = L"asdf"; - format[1].Width = 10; - result = PhFormatToBuffer(format, 2, buffer, sizeof(buffer), NULL); - assert(result && wcscmp(buffer, L" 1234asdf ") == 0); -} - -VOID Test_format( - VOID - ) -{ - Test_buffer(); - Test_char(); - Test_string(); - Test_integer(); - Test_float(); - Test_width(); -} +#include "tests.h" + +static VOID Test_buffer( + VOID + ) +{ +#define OUTPUT_COUNT 10 + + BOOLEAN result; + PH_FORMAT format[1]; + WCHAR buffer[16]; + SIZE_T returnLength; + + format[0].Type = Int32FormatType; + format[0].u.Int32 = 1234567890; + + result = PhFormatToBuffer(format, 1, buffer, (OUTPUT_COUNT + 1) * sizeof(WCHAR), &returnLength); + assert(result && wcscmp(buffer, L"1234567890") == 0 && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); + result = PhFormatToBuffer(format, 1, buffer, 16 * sizeof(WCHAR), &returnLength); + assert(result && wcscmp(buffer, L"1234567890") == 0 && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); + result = PhFormatToBuffer(format, 1, buffer, OUTPUT_COUNT * sizeof(WCHAR), &returnLength); + assert(!result && buffer[0] == 0 && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); + result = PhFormatToBuffer(format, 1, buffer, 0, &returnLength); + assert(!result && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); + result = PhFormatToBuffer(format, 1, NULL, 9999, &returnLength); + assert(!result && returnLength == (OUTPUT_COUNT + 1) * sizeof(WCHAR)); +} + +static VOID Test_char( + VOID + ) +{ + BOOLEAN result; + PH_FORMAT format[2]; + WCHAR buffer[1024]; + + format[0].Type = CharFormatType; + format[0].u.Char = 'H'; + format[1].Type = CharFormatType; + format[1].u.Char = 'i'; + result = PhFormatToBuffer(format, 2, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"Hi") == 0); +} + +static VOID Test_string( + VOID + ) +{ + BOOLEAN result; + PH_FORMAT format[4]; + WCHAR buffer[1024]; + + format[0].Type = StringFormatType; + PhInitializeStringRef(&format[0].u.String, L"This "); + format[1].Type = StringZFormatType; + format[1].u.StringZ = L"is "; + format[2].Type = MultiByteStringFormatType; + PhInitializeBytesRef(&format[2].u.MultiByteString, "a "); + format[3].Type = MultiByteStringZFormatType; + format[3].u.MultiByteStringZ = "string."; + result = PhFormatToBuffer(format, 4, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"This is a string.") == 0); +} + +static BOOLEAN IsThousandSepComma( + VOID + ) +{ + WCHAR thousandSep[4]; + + if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousandSep, 4)) + return FALSE; + if (thousandSep[0] != ',' || thousandSep[1] != 0) + return FALSE; + + return TRUE; +} + +static VOID Test_integer( + VOID + ) +{ + BOOLEAN result; + PH_FORMAT format[1]; + WCHAR buffer[1024]; + + // Basic + + format[0].Type = Int32FormatType; + format[0].u.Int32 = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"0") == 0); + + format[0].Type = Int32FormatType; + format[0].u.Int32 = 123; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123") == 0); + + format[0].Type = Int32FormatType; + format[0].u.Int32 = -123; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-123") == 0); + + format[0].Type = UInt32FormatType; + format[0].u.UInt32 = -123; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"4294967173") == 0); + + format[0].Type = Int64FormatType; + format[0].u.Int64 = -1234567890; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1234567890") == 0); + + format[0].Type = UInt64FormatType; + format[0].u.UInt64 = 12345678901234567890; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"12345678901234567890") == 0); + + // Bases + + format[0].Type = UInt64FormatType | FormatUseRadix; + format[0].u.UInt64 = 12345678901234567890; + format[0].Radix = 16; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"ab54a98ceb1f0ad2") == 0); + + format[0].Type = UInt64FormatType | FormatUseRadix | FormatUpperCase; + format[0].u.UInt64 = 12345678901234567890; + format[0].Radix = 16; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"AB54A98CEB1F0AD2") == 0); + + format[0].Type = Int32FormatType | FormatUseRadix; + format[0].u.Int32 = -1234; + format[0].Radix = 8; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-2322") == 0); + + // Prefix sign + + format[0].Type = Int32FormatType | FormatPrefixSign; + format[0].u.Int32 = 1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"+1234") == 0); + + format[0].Type = Int32FormatType | FormatPrefixSign; + format[0].u.Int32 = -1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1234") == 0); + + // Zero pad + + format[0].Type = Int32FormatType | FormatPadZeros; + format[0].Width = 6; + + format[0].u.Int32 = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"000000") == 0); + format[0].u.Int32 = 1; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"000001") == 0); + format[0].u.Int32 = 12345; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"012345") == 0); + format[0].u.Int32 = 123456; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123456") == 0); + format[0].u.Int32 = 1234567890; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1234567890") == 0); + format[0].u.Int32 = -1; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-00001") == 0); + format[0].u.Int32 = -1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-01234") == 0); + format[0].u.Int32 = -12345; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-12345") == 0); + format[0].u.Int32 = -1234567890; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1234567890") == 0); + + // Digit grouping + + if (!IsThousandSepComma()) + return; + + format[0].Type = Int32FormatType | FormatGroupDigits; + + format[0].u.Int32 = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"0") == 0); + format[0].u.Int32 = 1; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1") == 0); + format[0].u.Int32 = 12; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"12") == 0); + format[0].u.Int32 = 123; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123") == 0); + format[0].u.Int32 = 1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1,234") == 0); + format[0].u.Int32 = 12345; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"12,345") == 0); + format[0].u.Int32 = 123456; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123,456") == 0); + format[0].u.Int32 = -123; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-123") == 0); + format[0].u.Int32 = -1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1,234") == 0); + format[0].u.Int32 = -12345; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-12,345") == 0); +} + +static VOID Test_float( + VOID + ) +{ + BOOLEAN result; + PH_FORMAT format[1]; + WCHAR buffer[1024]; + + // TODO: Standard and hexadecimal form + + // Basic + + format[0].Type = DoubleFormatType; + format[0].u.Double = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"0.000000") == 0); + + format[0].Type = DoubleFormatType; + format[0].u.Double = 1; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1.000000") == 0); + + format[0].Type = DoubleFormatType; + format[0].u.Double = 123456789; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123456789.000000") == 0); + + format[0].Type = DoubleFormatType; + format[0].u.Double = 3.14159265358979; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"3.141593") == 0); + + // Precision + + format[0].Type = DoubleFormatType | FormatUsePrecision; + format[0].u.Double = 0; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"0") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision; + format[0].u.Double = 3.14159265358979; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"3") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision; + format[0].u.Double = 3.14159265358979; + format[0].Precision = 1; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"3.1") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision; + format[0].u.Double = 3.14159265358979; + format[0].Precision = 4; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"3.1416") == 0); + + // Crop zeros + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; + format[0].u.Double = 0; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"0") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; + format[0].u.Double = 1.2; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1.2") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; + format[0].u.Double = 1.21; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1.21") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; + format[0].u.Double = 1.216; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1.216") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatCropZeros; + format[0].u.Double = 1.2159; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1.216") == 0); + + // Prefix sign + + format[0].Type = DoubleFormatType | FormatPrefixSign; + format[0].u.Double = 1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"+1234.000000") == 0); + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatPrefixSign; + format[0].u.Double = 1234; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"+1234") == 0); + + format[0].Type = DoubleFormatType | FormatPrefixSign; + format[0].u.Double = -1234; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1234.000000") == 0); + + format[0].Type = DoubleFormatType | FormatPrefixSign; + format[0].u.Double = -1234.12; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1234.120000") == 0); + + // Zero pad + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatPadZeros; + format[0].Width = 6; + + format[0].u.Double = 0; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"00.000") == 0); + format[0].u.Double = 1.23; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"01.230") == 0); + format[0].u.Double = 123456; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123456.000") == 0); + format[0].u.Double = -1.23; + format[0].Precision = 2; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-01.23") == 0); + format[0].u.Double = -1.23; + format[0].Precision = 3; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1.230") == 0); + + // Digit grouping + + if (!IsThousandSepComma()) + return; + + format[0].Type = DoubleFormatType | FormatUsePrecision | FormatGroupDigits; + + format[0].u.Double = 0; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"0") == 0); + format[0].u.Double = 1; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1") == 0); + format[0].u.Double = 12; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"12") == 0); + format[0].u.Double = 123; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123") == 0); + format[0].u.Double = 1234; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"1,234") == 0); + format[0].u.Double = 12345; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"12,345") == 0); + format[0].u.Double = 123456; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"123,456") == 0); + format[0].u.Double = -123; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-123") == 0); + format[0].u.Double = -1234; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-1,234") == 0); + format[0].u.Double = -12345; + format[0].Precision = 0; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-12,345") == 0); + format[0].u.Double = -12345; + format[0].Precision = 2; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-12,345.00") == 0); + format[0].u.Double = -9876543.21; + format[0].Precision = 5; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"-9,876,543.21000") == 0); +} + +static VOID Test_width( + VOID + ) +{ + BOOLEAN result; + PH_FORMAT format[2]; + WCHAR buffer[1024]; + + PhInitializeStringRef(&format[0].u.String, L"asdf"); + + format[0].Type = StringFormatType; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf") == 0); + + // Left align + + format[0].Type = StringFormatType | FormatLeftAlign; + format[0].Width = 4; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf") == 0); + + format[0].Type = StringFormatType | FormatLeftAlign; + format[0].Width = 2; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf") == 0); + + format[0].Type = StringFormatType | FormatLeftAlign; + format[0].Width = 5; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf ") == 0); + + format[0].Type = StringFormatType | FormatLeftAlign; + format[0].Width = 10; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf ") == 0); + + format[0].Type = StringFormatType | FormatLeftAlign | FormatUsePad; + format[0].Width = 6; + format[0].Pad = '!'; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf!!") == 0); + + // Right align + + format[0].Type = StringFormatType | FormatRightAlign; + format[0].Width = 4; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf") == 0); + + format[0].Type = StringFormatType | FormatRightAlign; + format[0].Width = 2; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"asdf") == 0); + + format[0].Type = StringFormatType | FormatRightAlign; + format[0].Width = 5; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L" asdf") == 0); + + format[0].Type = StringFormatType | FormatRightAlign; + format[0].Width = 10; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L" asdf") == 0); + + format[0].Type = StringFormatType | FormatRightAlign | FormatUsePad; + format[0].Width = 6; + format[0].Pad = '!'; + result = PhFormatToBuffer(format, 1, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L"!!asdf") == 0); + + // Multiple + + format[0].Type = Int32FormatType | FormatRightAlign; + format[0].u.Int32 = 1234; + format[0].Width = 7; + format[1].Type = StringZFormatType | FormatLeftAlign; + format[1].u.StringZ = L"asdf"; + format[1].Width = 10; + result = PhFormatToBuffer(format, 2, buffer, sizeof(buffer), NULL); + assert(result && wcscmp(buffer, L" 1234asdf ") == 0); +} + +VOID Test_format( + VOID + ) +{ + Test_buffer(); + Test_char(); + Test_string(); + Test_integer(); + Test_float(); + Test_width(); +} diff --git a/tests/phlib-test/t_util.c b/tools/tests/phlib-test/t_util.c similarity index 100% rename from tests/phlib-test/t_util.c rename to tools/tests/phlib-test/t_util.c diff --git a/tests/phlib-test/tests.h b/tools/tests/phlib-test/tests.h similarity index 89% rename from tests/phlib-test/tests.h rename to tools/tests/phlib-test/tests.h index 8d1fd9b5b872..9e65f73be328 100644 --- a/tests/phlib-test/tests.h +++ b/tools/tests/phlib-test/tests.h @@ -1,22 +1,22 @@ -#ifndef TESTS_H -#define TESTS_H - -#include - -VOID Test_basesup( - VOID - ); - -VOID Test_avltree( - VOID - ); - -VOID Test_format( - VOID - ); - -VOID Test_util( - VOID - ); - -#endif +#ifndef TESTS_H +#define TESTS_H + +#include + +VOID Test_basesup( + VOID + ); + +VOID Test_avltree( + VOID + ); + +VOID Test_format( + VOID + ); + +VOID Test_util( + VOID + ); + +#endif